connectome-workbench-1.2.3+git41-gc4c6c90/000077500000000000000000000000001300200146000177555ustar00rootroot00000000000000connectome-workbench-1.2.3+git41-gc4c6c90/.gitignore000066400000000000000000000001111300200146000217360ustar00rootroot00000000000000*.kdev4 CMakeLists.txt.user* build release debug *.kate-swp .DS_Store *~ connectome-workbench-1.2.3+git41-gc4c6c90/.travis.yml000066400000000000000000000007721300200146000220740ustar00rootroot00000000000000language: cpp sudo: false notifications: email: false cache: - apt - ccache addons: apt: packages: - libqt4-dev - zlib1g-dev - libosmesa6-dev - libssl-dev - libqwt-dev - libfreetype6-dev - libftgl-dev env: global: - OSMESA_DIR=/usr - OMP_NUM_THREADS=4 before_install: - mkdir ../build - cd ../build script: - cmake ../workbench/src - make -j 4 - ctest connectome-workbench-1.2.3+git41-gc4c6c90/Doxyfile000066400000000000000000000016451300200146000214710ustar00rootroot00000000000000PROJECT_NAME = Caret7 OUTPUT_DIRECTORY = ../documentation OUTPUT_LANGUAGE = English EXTRACT_ALL = YES EXTRACT_PRIVATE = YES HAVE_DOT = YES DOT_PATH = /usr/local/graphviz-2.14/bin CLASS_GRAPH = YES CALL_GRAPH = NO CALLER_GRAPH = NO COLLABORATION_GRAPH = NO DIRECTORY_GRAPH = YES SOURCE_BROWSER = YES ALPHABETICAL_INDEX = YES JAVADOC_AUTOBRIEF = YES GENERATE_HTML = YES GENERATE_LATEX = NO GENERATE_MAN = NO GENERATE_RTF = NO CREATE_SUBDIRS = YES SUBGROUPING = YES INPUT = \ src/Algorithms \ src/Annotations \ src/Brain \ src/Charting \ src/Cifti \ src/CommandLine \ src/Commands \ src/Common \ src/Desktop \ src/Files \ src/FilesBase \ src/FtglFont \ src/Gifti \ src/GuiQt \ src/Nifti \ src/OSMesaDummy \ src/Operations \ src/OperationsBase \ src/Palette \ src/Quazip \ src/Qwt \ src/Scenes \ src/Tests \ src/Xml FILE_PATTERNS = *.cxx *.h connectome-workbench-1.2.3+git41-gc4c6c90/LICENSE000066400000000000000000000432541300200146000207720ustar00rootroot00000000000000 GNU GENERAL PUBLIC LICENSE Version 2, June 1991 Copyright (C) 1989, 1991 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This General Public License applies to most of the Free Software Foundation's software and to any other program whose authors commit to using it. (Some other Free Software Foundation software is covered by the GNU Lesser General Public License instead.) You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things. To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the software, or if you modify it. For example, if you distribute copies of such a program, whether gratis or for a fee, you must give the recipients all the rights that you have. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. We protect your rights with two steps: (1) copyright the software, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the software. Also, for each author's protection and ours, we want to make certain that everyone understands that there is no warranty for this free software. If the software is modified by someone else and passed on, we want its recipients to know that what they have is not the original, so that any problems introduced by others will not reflect on the original authors' reputations. Finally, any free program is threatened constantly by software patents. We wish to avoid the danger that redistributors of a free program will individually obtain patent licenses, in effect making the program proprietary. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all. The precise terms and conditions for copying, distribution and modification follow. GNU GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License applies to any program or other work which contains a notice placed by the copyright holder saying it may be distributed under the terms of this General Public License. The "Program", below, refers to any such program or work, and a "work based on the Program" means either the Program or any derivative work under copyright law: that is to say, a work containing the Program or a portion of it, either verbatim or with modifications and/or translated into another language. (Hereinafter, translation is included without limitation in the term "modification".) Each licensee is addressed as "you". Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running the Program is not restricted, and the output from the Program is covered only if its contents constitute a work based on the Program (independent of having been made by running the Program). Whether that is true depends on what the Program does. 1. You may copy and distribute verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and give any other recipients of the Program a copy of this License along with the Program. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Program or any portion of it, thus forming a work based on the Program, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) You must cause the modified files to carry prominent notices stating that you changed the files and the date of any change. b) You must cause any work that you distribute or publish, that in whole or in part contains or is derived from the Program or any part thereof, to be licensed as a whole at no charge to all third parties under the terms of this License. c) If the modified program normally reads commands interactively when run, you must cause it, when started running for such interactive use in the most ordinary way, to print or display an announcement including an appropriate copyright notice and a notice that there is no warranty (or else, saying that you provide a warranty) and that users may redistribute the program under these conditions, and telling the user how to view a copy of this License. (Exception: if the Program itself is interactive but does not normally print such an announcement, your work based on the Program is not required to print an announcement.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Program, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Program, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Program. In addition, mere aggregation of another work not based on the Program with the Program (or with a work based on the Program) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may copy and distribute the Program (or a work based on it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you also do one of the following: a) Accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, b) Accompany it with a written offer, valid for at least three years, to give any third party, for a charge no more than your cost of physically performing source distribution, a complete machine-readable copy of the corresponding source code, to be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, c) Accompany it with the information you received as to the offer to distribute corresponding source code. (This alternative is allowed only for noncommercial distribution and only if you received the program in object code or executable form with such an offer, in accord with Subsection b above.) The source code for a work means the preferred form of the work for making modifications to it. For an executable work, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the executable. However, as a special exception, the source code distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. If distribution of executable or object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place counts as distribution of the source code, even though third parties are not compelled to copy the source along with the object code. 4. You may not copy, modify, sublicense, or distribute the Program except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense or distribute the Program is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 5. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Program or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Program (or any work based on the Program), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Program or works based on it. 6. Each time you redistribute the Program (or any work based on the Program), the recipient automatically receives a license from the original licensor to copy, distribute or modify the Program subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties to this License. 7. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Program at all. For example, if a patent license would not permit royalty-free redistribution of the Program by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Program. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system, which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 8. If the distribution and/or use of the Program is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Program under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 9. The Free Software Foundation may publish revised and/or new versions of the General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of this License, you may choose any version ever published by the Free Software Foundation. 10. If you wish to incorporate parts of the Program into other free programs whose distribution conditions are different, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) This 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. Also add information on how to contact you by electronic and paper mail. If the program is interactive, make it output a short notice like this when it starts in an interactive mode: Gnomovision version 69, Copyright (C) year name of author Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, the commands you use may be called something other than `show w' and `show c'; they could even be mouse-clicks or menu items--whatever suits your program. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the program, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the program `Gnomovision' (which makes passes at compilers) written by James Hacker. , 1 April 1989 Ty Coon, President of Vice This General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Lesser General Public License instead of this License. connectome-workbench-1.2.3+git41-gc4c6c90/README000066400000000000000000000077011300200146000206420ustar00rootroot00000000000000Connectome Workbench requires QT4 to compile. Recommended is the latest 4.8.x release. The following configure options were used on linux for our builds: -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 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: mkdir build cd build cmake ../workbench/src -D CMAKE_BUILD_TYPE=Release make For other OSes, see http://www.cmake.org/cmake/help/runningcmake.html 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: ctest #OR make test To install wb_view, wb_command, wb_shortcuts, and the bash completion script to the default locations: 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 Some source files are licensed under an MIT license (Expat), for easier code reuse in non-GPL projects: Files/SurfaceResamplingHelper.cxx Cifti/examples/* CommandLine/wb_shortcuts CommandLine/wb_command_completion Copyright (C) 2014-2016 Washington University School of Medicine Some included code/files are from third party sources, with the following licenses: kloewe/*: Copyright (c) 2012-2016 Kristian Loewe, Christian Borgelt licensed under MIT (Expat), see kloewe/dot/LICENSE and kloewe/cpuinfo/LICENSE Quazip/*: QuaZIP 0.6, http://quazip.sourceforge.net/ Copyright (C) 2005-2012 Sergey A. Tachenov Copyright (C) 1998-2010 Gilles Vollant Copyright (C) 2009-2010 Mathias Svensson Copyright (C) 2007-2008 Even Rouault Copyright (c) 1990-2000 Info-ZIP licensed under LGPLv2 and zlib, see Quazip/COPYING, Quazip/quazip.h, Quazip/zip.h and Quazip/unzip.c Qwt/*: Copyright (C) 1997 Josef Wilgen Copyright (C) 2002 Uwe Rathmann Qwt 6.0.1, http://qwt.sourceforge.net/ licensed under Qwt license v1.0 (LGPLv2.1, with exceptions), see src/Qwt/COPYING some unneeded files removed FtglFont/*: FTGL library Copyright (C) 2001-2004 Henry Maddocks Copyright (C) 2008 Daniel Remenak Copyright (C) 2008 Éric Beets Copyright (C) 2008 Sam Hocevar Copyright (C) 2008 Sean Morrison licensed under Expat, see FtglFont/COPYING Common/Base64.*, Common/DataCompressZLib.*, Common/MathFunctions.*, Nifti/Matrix4x4.cxx: use code from VTK, http://www.kitware.com/opensource/vtk.html or http://www.vtk.org/ Copyright (c) Ken Martin, Will Schroeder, Bill Lorensen originally licensed under BSD 3-clause, see http://www.kitware.com/Copyright.htm or http://www.vtk.org/VTK/project/license.html GuiQt/WuQDialog.cxx, Brain/FtglFontTextRenderer.cxx: copied some code from from QT4, https://qt-project.org/ Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). originally licensed LGPLv2.1 (or GPLv3, or a commercial license) modified to change some UI behaviors otherwise hardcoded into QT classes Files/SignedDistanceHelper.cxx, Files/RibbonMappingHelper.cxx: make use of PNPOLY, http://www.ecse.rpi.edu/~wrf/Research/Short_Notes/pnpoly.html Copyright (c) 1970-2003, Wm. Randolph Franklin originally licensed with 3-clause BSD/MIT license, see files in question 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 connectome-workbench-1.2.3+git41-gc4c6c90/build_scripts/000077500000000000000000000000001300200146000226235ustar00rootroot00000000000000connectome-workbench-1.2.3+git41-gc4c6c90/build_scripts/commandhtml.sh000077500000000000000000000112631300200146000254700ustar00rootroot00000000000000#!/bin/bash exe=wb_command outDir='.' function command_to_page_name () { echo "command""$1"".html" } #make a page containing just the text output, no links, substituting special characters as needed function make_basic_command_page () { local commandName="$1" local outPage="$outDir/`command_to_page_name $commandName`" echo '' > "$outPage" echo '' >> "$outPage" echo "wb_command $commandName help information" >> "$outPage" echo '
' >> "$outPage"
    #body
    echo "`$exe $commandName`" | sed 's//\>/g' >> "$outPage"
    #end page
    echo '
' >> "$outPage" echo '' >> "$outPage" } #start main page - note that this assumes a particular order of the listed info commands, change and add as needed initialText=`$exe` startPage="wb_command_help.html" #page header echo '' > "$outDir/$startPage" echo '' >> "$outDir/$startPage" echo 'wb_command help information' >> "$outDir/$startPage" echo '
' >> "$outDir/$startPage"
#body
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
echo -n '' >> "$outDir/$startPage"
echo "$initialText" | grep -- -arguments-help >> "$outDir/$startPage"
echo -n '' >> "$outDir/$startPage"
#-cifti-help
echo -n '' >> "$outDir/$startPage"
echo "$initialText" | grep -- -cifti-help >> "$outDir/$startPage"
echo -n '' >> "$outDir/$startPage"
#-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
echo -n '' >> "$outDir/$startPage"
echo "$initialText" | grep -- -list-commands >> "$outDir/$startPage"
echo -n '' >> "$outDir/$startPage"
#-list-deprecated-commands
echo -n '' >> "$outDir/$startPage"
echo "$initialText" | grep -- -list-deprecated-commands >> "$outDir/$startPage"
echo -n '' >> "$outDir/$startPage"
#-all-commands-help - takes 2 lines!
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
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 echo '' > "$outPage" echo '' >> "$outPage" echo 'wb_command -list-commands help information' >> "$outPage" echo '
' >> "$outPage"
#body
readarray -t lines < <($exe -list-commands)
for ((i = 0; i < ${#lines[@]}; ++i))
do
    thisCommand=`echo ${lines[$i]} | cut -f1 -d' '`
    make_basic_command_page "$thisCommand"
    echo ''"${lines[$i]}"'' >> "$outPage"
done

#-list-deprecated-commands page, and its subpages
outPage="$outDir/`command_to_page_name -list-deprecated-commands`"
#header
echo '' > "$outPage"
echo '' >> "$outPage"
echo 'wb_command -list-deprecated-commands help information' >> "$outPage"
echo '
' >> "$outPage"
#body
readarray -t lines < <($exe -list-deprecated-commands)
for ((i = 0; i < ${#lines[@]}; ++i))
do
    thisCommand=`echo ${lines[$i]} | cut -f1 -d' '`
    make_basic_command_page "$thisCommand"
    echo ''"${lines[$i]}"'' >> "$outPage"
done

#end main page
echo '
' >> "$outPage" echo '' >> "$outPage" #-all-commands-help page make_basic_command_page "-all-commands-help" connectome-workbench-1.2.3+git41-gc4c6c90/build_scripts/doxygen.sh000077500000000000000000000012741300200146000246430ustar00rootroot00000000000000#!/bin/sh # # This file is executed by (via "launchd") # /System/Library/LaunchDaemons/edu.wustl.caret7.doxygen.plist # # # File for capturing standard error # BUILD_ROOT_DIR=/mnt/myelin/caret7_documentation ERROR_FILE=${BUILD_ROOT_DIR}/result_doxygen.txt rm -f ${ERROR_FILE} # # If something fails, keep going # set noon # # Run the build script # echo "Starting doxygen generation" >> ${ERROR_FILE} 2>&1 ${BUILD_ROOT_DIR}/caret7_source/build_scripts/doxygen_aux.sh >> ${ERROR_FILE} 2>&1 echo "Finished doxygen generation" >> ${ERROR_FILE} 2>&1 # # Send output as email # cat ${ERROR_FILE} | mail -v -s 'Caret7 Doxygen Build Result' john@brainvis.wustl.edu tsc5yc@mst.edu echo "Sent mail" connectome-workbench-1.2.3+git41-gc4c6c90/build_scripts/doxygen_aux.sh000077500000000000000000000015301300200146000255130ustar00rootroot00000000000000#!/bin/sh # # This file is executed by (via "launchd") # /System/Library/LaunchDaemons/edu.wustl.caret7.doxygen.plist # # (1) Update source code from repository # (2) Build doxygen documentation # # # Go to correct directory # MAIN_DIR=/mnt/myelin/caret7_documentation SOURCE_DIR=${MAIN_DIR}/caret7_source DOCUMENTATION_DIR=${MAIN_DIR}/documentation BUILD_DIR=${MAIN_DIR}/build # # Remove existing documentation directory # rm -rf ${DOCUMENTATION_DIR} # # Go to the source directory which contains Doxyfile # Doxyfile is read by doxygen and contains configuration information # cd ${SOURCE_DIR} # # Update source from repository # echo "UPDATING SOURCE FROM GIT REPOSITORY" git pull -u # # Generate doxygen documentation # echo "Generating Documentation" /Applications/Doxygen.app/Contents/Resources/doxygen echo "SCRIPT COMPLETED SUCCESSFULLY" connectome-workbench-1.2.3+git41-gc4c6c90/build_scripts/linux32_remote.sh000077500000000000000000000005121300200146000260370ustar00rootroot00000000000000#!/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 connectome-workbench-1.2.3+git41-gc4c6c90/build_scripts/linux64_remote.sh000077500000000000000000000005131300200146000260450ustar00rootroot00000000000000#!/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 connectome-workbench-1.2.3+git41-gc4c6c90/build_scripts/linux64_rh.sh000077500000000000000000000015671300200146000251750ustar00rootroot00000000000000#!/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" connectome-workbench-1.2.3+git41-gc4c6c90/build_scripts/linux64_rh_aux.sh000077500000000000000000000025611300200146000260450ustar00rootroot00000000000000#!/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" connectome-workbench-1.2.3+git41-gc4c6c90/build_scripts/linux64_rh_remote.sh000077500000000000000000000004071300200146000265400ustar00rootroot00000000000000#!/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 connectome-workbench-1.2.3+git41-gc4c6c90/build_scripts/mac32.sh000077500000000000000000000016461300200146000240760ustar00rootroot00000000000000#!/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" connectome-workbench-1.2.3+git41-gc4c6c90/build_scripts/mac32_aux.sh000077500000000000000000000033221300200146000247440ustar00rootroot00000000000000#!/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" connectome-workbench-1.2.3+git41-gc4c6c90/build_scripts/mac32_remote.sh000077500000000000000000000003511300200146000254410ustar00rootroot00000000000000#!/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 connectome-workbench-1.2.3+git41-gc4c6c90/build_scripts/mac64.sh000077500000000000000000000016461300200146000241030ustar00rootroot00000000000000#!/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" connectome-workbench-1.2.3+git41-gc4c6c90/build_scripts/mac64_aux.sh000077500000000000000000000054651300200146000247630ustar00rootroot00000000000000#!/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" connectome-workbench-1.2.3+git41-gc4c6c90/build_scripts/mac64_copy_library_to_workbench.sh000077500000000000000000000056551300200146000314310ustar00rootroot00000000000000#!/bin/sh # # Usage: # # This script copies a library to a Workbench Mac App's # directory for extra libraries needed by the App's executable. # The path to the library then needs to be changed # in the executable from its original path to a path # relative to the executable. # # # Uncomment to print debug information # ##debug_flag=1 # # Input library that is copied for executable # #executable_input=/Users/caret/caret7_development/mac64/build/Desktop/wb_view.app/Contents/MacOS/wb_view #library_input=/opt/local/lib/libomp/libomp.dylib library_input=$1 executable_input=$2 # # Extract need paths # exe_path_name=${executable_input} exe_name=`basename ${exe_path_name}` macos_dir_path=`dirname ${exe_path_name}` contents_dir_path=`dirname ${macos_dir_path}` app_name=${exe_name}.app if [ $debug_flag ]; then echo "Executable name: ${exe_name}" echo "MacOS directory: ${macos_dir_path}" echo "Contents directory: ${contents_dir_path}" fi echo "INSTALLING LIBRARY: ${library_input}" echo " INTO: ${executable_input}" # # Name and path to directory in the App to # which the library is inserted. # workbench_lib_dir_name=WorkbenchLibs workbench_lib_dir_path_name=${contents_dir_path}/${workbench_lib_dir_name} # # Full path and just name of library # lib_path_name=${library_input} lib_name=`basename ${library_input}` if [ $debug_flag ]; then echo "library full path: ${lib_path_name}" echo "library name (no path): ${lib_name}" fi #exit # # Does app's "Content" directory exist? # if [ -d "${contents_dir_path}" ]; then if [ $debug_flag ]; then echo "App Contents found" fi # # Does the Workbench library directory exist in the app's content # If not create it # if [ ! -d "${workbench_lib_dir_path_name}" ]; then if [ $debug_flag ]; then echo "Making directory ${workbench_lib_dir_path_name}" fi mkdir ${workbench_lib_dir_path_name} fi # # Verify Workbench library directory exists # if [ -d "${workbench_lib_dir_path_name}" ]; then if [ $debug_flag ]; then echo "lib dir exists" fi # # Copy the library to the App's Workbench library directory # if [ $debug_flag ]; then cp -v ${lib_path_name} ${workbench_lib_dir_path_name} else cp ${lib_path_name} ${workbench_lib_dir_path_name} fi # # Change the path of the library inside of executable. # old_name=${lib_path_name} new_name="@executable_path/../${workbench_lib_dir_name}/${lib_name}" if [ $debug_flag ]; then echo "old name: ${old_name}" echo "new name: ${new_name}" fi install_name_tool -change ${old_name} ${new_name} ${exe_path_name} else echo "ERROR: App Lib Directory with name \""${wb_view_lib_dir}"\" not found" fi else echo "ERROR: App Contents Directory with name \""${contents_dir_path}"\" not found" fi connectome-workbench-1.2.3+git41-gc4c6c90/build_scripts/mac64_noodle.sh000077500000000000000000000015361300200146000254410ustar00rootroot00000000000000#!/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" connectome-workbench-1.2.3+git41-gc4c6c90/build_scripts/mac64_noodle_aux.sh000077500000000000000000000055061300200146000263170ustar00rootroot00000000000000#!/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" connectome-workbench-1.2.3+git41-gc4c6c90/build_scripts/mac64_noodle_remote.sh000077500000000000000000000003401300200146000270040ustar00rootroot00000000000000#!/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 connectome-workbench-1.2.3+git41-gc4c6c90/build_scripts/mac64_remote.sh000077500000000000000000000003511300200146000254460ustar00rootroot00000000000000#!/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 connectome-workbench-1.2.3+git41-gc4c6c90/build_scripts/windows32.bat000077500000000000000000000015311300200146000251550ustar00rootroot00000000000000@echo=off set CMD=c:\Windows\system32\cmd.exe REM REM Go to correct directory REM set BUILD_DIR=c:\dev7\windows32 cd %BUILD_DIR% REM REM Go into source directory REM cd caret7_source echo "Caret7 Windows32 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/windows32 %CMD% /c build.bat cd .. cd .. set DIST_DIR=caret@myelin1:/mainpool/storage/distribution/caret7_distribution/workbench/bin_windows32 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" connectome-workbench-1.2.3+git41-gc4c6c90/build_scripts/windows32/000077500000000000000000000000001300200146000244625ustar00rootroot00000000000000connectome-workbench-1.2.3+git41-gc4c6c90/build_scripts/windows32/README.txt000077500000000000000000000002461300200146000261650ustar00rootroot00000000000000setup_env.bat -sets up the build environment build.bat -sets up environment, runs cmake, and builds the release version of caret c7env32.bat - sets up environment connectome-workbench-1.2.3+git41-gc4c6c90/build_scripts/windows32/build.bat000077500000000000000000000010741300200146000262560ustar00rootroot00000000000000@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 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 jom -j8 cd /d %OLD_DIR% set OLD_DIR= connectome-workbench-1.2.3+git41-gc4c6c90/build_scripts/windows32/c7env32.bat000077500000000000000000000003421300200146000263430ustar00rootroot00000000000000set PATH=C:\dev32\install\Qt\bin;C:\QtSDK\QtCreator\bin;C:\Program Files (x86)\CMake 2.8\bin;%PATH% set QTDIR=C:\dev32\install\Qt set ZLIB_LIB_DIR=C:\dev32\install\zlib\lib set ZLIB_INCLUDE_DIR=C:\dev32\install\zlib\includeconnectome-workbench-1.2.3+git41-gc4c6c90/build_scripts/windows32/c7env32mgw.bat000077500000000000000000000004151300200146000270570ustar00rootroot00000000000000set PATH=C:\QtSDK\Desktop\Qt\4.7.4\mingw\bin;C:\QtSDK\QtCreator\bin;C:\QtSDK\mingw\bin;C:\Program Files (x86)\CMake 2.8\bin;%PATH% set QTDIR=C:\QtSDK\Desktop\Qt\4.7.4\mingw set ZLIB_LIB_DIR=C:\dev32\install\zlib\lib set ZLIB_INCLUDE_DIR=C:\dev32\install\zlib\includeconnectome-workbench-1.2.3+git41-gc4c6c90/build_scripts/windows32/env.bat000077500000000000000000000006141300200146000257460ustar00rootroot00000000000000@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% )connectome-workbench-1.2.3+git41-gc4c6c90/build_scripts/windows32/launchcmdmgw.bat000077500000000000000000000000361300200146000276250ustar00rootroot00000000000000call c7env32mgw.bat cmd.exe connectome-workbench-1.2.3+git41-gc4c6c90/build_scripts/windows32/launchcmdvs.bat000077500000000000000000000001011300200146000274540ustar00rootroot00000000000000call c7env32.bat call env.bat "c:\dev7\windows32" x86 cmd.exe connectome-workbench-1.2.3+git41-gc4c6c90/build_scripts/windows32/launchqtcreatorvs.bat000077500000000000000000000001071300200146000307230ustar00rootroot00000000000000call c7env32.bat call env.bat "c:\dev7\windows32" x86 qtcreator.exe connectome-workbench-1.2.3+git41-gc4c6c90/build_scripts/windows32/launchvs.bat000077500000000000000000000001041300200146000267730ustar00rootroot00000000000000call c7env32.bat call env.bat "c:\dev7\windows32" x86 devenv.exe connectome-workbench-1.2.3+git41-gc4c6c90/build_scripts/windows32/luanchqtcreatormgw.bat000077500000000000000000000000421300200146000310630ustar00rootroot00000000000000call c7env32mgw.bat qtcreator.execonnectome-workbench-1.2.3+git41-gc4c6c90/build_scripts/windows32/setup_env.bat000077500000000000000000000000661300200146000271670ustar00rootroot00000000000000call c7env32.bat call env.bat "c:\dev7\windows32" x86connectome-workbench-1.2.3+git41-gc4c6c90/build_scripts/windows32_remote.sh000077500000000000000000000004031300200146000263710ustar00rootroot00000000000000#!/bin/sh ssh -v caret@windowsbuild '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 connectome-workbench-1.2.3+git41-gc4c6c90/build_scripts/windows64.bat000077500000000000000000000015311300200146000251620ustar00rootroot00000000000000@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" connectome-workbench-1.2.3+git41-gc4c6c90/build_scripts/windows64/000077500000000000000000000000001300200146000244675ustar00rootroot00000000000000connectome-workbench-1.2.3+git41-gc4c6c90/build_scripts/windows64/README.txt000066400000000000000000000002461300200146000261670ustar00rootroot00000000000000setup_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 connectome-workbench-1.2.3+git41-gc4c6c90/build_scripts/windows64/build.bat000077500000000000000000000007641300200146000262700ustar00rootroot00000000000000@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= connectome-workbench-1.2.3+git41-gc4c6c90/build_scripts/windows64/build_debug.bat000077500000000000000000000004461300200146000274330ustar00rootroot00000000000000@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= connectome-workbench-1.2.3+git41-gc4c6c90/build_scripts/windows64/c7env64.bat000077500000000000000000000005321300200146000263560ustar00rootroot00000000000000REM 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 connectome-workbench-1.2.3+git41-gc4c6c90/build_scripts/windows64/c7env64.vbs000077500000000000000000000011731300200146000264040ustar00rootroot00000000000000Set 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"connectome-workbench-1.2.3+git41-gc4c6c90/build_scripts/windows64/env.bat000077500000000000000000000006321300200146000257530ustar00rootroot00000000000000@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% )connectome-workbench-1.2.3+git41-gc4c6c90/build_scripts/windows64/launchcmdvs.bat000077500000000000000000000001101300200146000274610ustar00rootroot00000000000000call c7env64.bat call env.bat "c:\dev\git\caret7_source" x64 cmd.exe connectome-workbench-1.2.3+git41-gc4c6c90/build_scripts/windows64/launchqtcreatorvs.bat000077500000000000000000000001501300200146000307260ustar00rootroot00000000000000call c7env64.bat call env.bat "c:\dev\git\caret7_source" x64 C:\Qt\qtcreator-2.7.1\bin\qtcreator.exe connectome-workbench-1.2.3+git41-gc4c6c90/build_scripts/windows64/launchvs.bat000077500000000000000000000001041300200146000270000ustar00rootroot00000000000000call c7env64.bat call env.bat "c:\dev7\windows64" x64 devenv.exe connectome-workbench-1.2.3+git41-gc4c6c90/build_scripts/windows64/setup_env.bat000077500000000000000000000000701300200146000271670ustar00rootroot00000000000000call c7env64.bat call env.bat "c:\dev7\windows64" AMD64connectome-workbench-1.2.3+git41-gc4c6c90/build_scripts/windows6411/000077500000000000000000000000001300200146000246315ustar00rootroot00000000000000connectome-workbench-1.2.3+git41-gc4c6c90/build_scripts/windows6411/README.txt000066400000000000000000000002461300200146000263310ustar00rootroot00000000000000setup_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 connectome-workbench-1.2.3+git41-gc4c6c90/build_scripts/windows6411/build.bat000077500000000000000000000004241300200146000264230ustar00rootroot00000000000000@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=connectome-workbench-1.2.3+git41-gc4c6c90/build_scripts/windows6411/c7env64.bat000077500000000000000000000003121300200146000265140ustar00rootroot00000000000000set 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\includeconnectome-workbench-1.2.3+git41-gc4c6c90/build_scripts/windows6411/c7env64.vbs000077500000000000000000000012031300200146000265400ustar00rootroot00000000000000Set 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"connectome-workbench-1.2.3+git41-gc4c6c90/build_scripts/windows6411/ctp.bat000077500000000000000000000002661300200146000261160ustar00rootroot00000000000000set 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%connectome-workbench-1.2.3+git41-gc4c6c90/build_scripts/windows6411/env.bat000077500000000000000000000006141300200146000261150ustar00rootroot00000000000000@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% )connectome-workbench-1.2.3+git41-gc4c6c90/build_scripts/windows6411/launchcmdvs.bat000077500000000000000000000001101300200146000276230ustar00rootroot00000000000000call c7env64.bat call env.bat "c:\dev\git\caret7_source" x64 cmd.exe connectome-workbench-1.2.3+git41-gc4c6c90/build_scripts/windows6411/launchqtcreatorvs.bat000077500000000000000000000001071300200146000310720ustar00rootroot00000000000000call c7env32.bat call env.bat "c:\dev7\windows32" x64 qtcreator.exe connectome-workbench-1.2.3+git41-gc4c6c90/build_scripts/windows6411/launchvs.bat000077500000000000000000000001041300200146000271420ustar00rootroot00000000000000call c7env64.bat call env.bat "c:\dev7\windows64" x64 devenv.exe connectome-workbench-1.2.3+git41-gc4c6c90/build_scripts/windows6411/setup_env.bat000077500000000000000000000000701300200146000273310ustar00rootroot00000000000000call c7env64.bat call env.bat "c:\dev7\windows64" AMD64connectome-workbench-1.2.3+git41-gc4c6c90/build_scripts/windows6412/000077500000000000000000000000001300200146000246325ustar00rootroot00000000000000connectome-workbench-1.2.3+git41-gc4c6c90/build_scripts/windows6412/README.txt000077500000000000000000000002461300200146000263350ustar00rootroot00000000000000setup_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 connectome-workbench-1.2.3+git41-gc4c6c90/build_scripts/windows6412/build.bat000077500000000000000000000004241300200146000264240ustar00rootroot00000000000000@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=connectome-workbench-1.2.3+git41-gc4c6c90/build_scripts/windows6412/c7env64.bat000077500000000000000000000003121300200146000265150ustar00rootroot00000000000000set 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\includeconnectome-workbench-1.2.3+git41-gc4c6c90/build_scripts/windows6412/c7env64.vbs000077500000000000000000000012031300200146000265410ustar00rootroot00000000000000Set 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"connectome-workbench-1.2.3+git41-gc4c6c90/build_scripts/windows6412/ctp.bat000077500000000000000000000002661300200146000261170ustar00rootroot00000000000000set 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%connectome-workbench-1.2.3+git41-gc4c6c90/build_scripts/windows6412/env.bat000077500000000000000000000006141300200146000261160ustar00rootroot00000000000000@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% )connectome-workbench-1.2.3+git41-gc4c6c90/build_scripts/windows6412/launchcmdvs.bat000077500000000000000000000001101300200146000276240ustar00rootroot00000000000000call c7env64.bat call env.bat "c:\dev\git\caret7_source" x64 cmd.exe connectome-workbench-1.2.3+git41-gc4c6c90/build_scripts/windows6412/launchqtcreatorvs.bat000077500000000000000000000001071300200146000310730ustar00rootroot00000000000000call c7env32.bat call env.bat "c:\dev7\windows32" x64 qtcreator.exe connectome-workbench-1.2.3+git41-gc4c6c90/build_scripts/windows6412/launchvs.bat000077500000000000000000000001041300200146000271430ustar00rootroot00000000000000call c7env64.bat call env.bat "c:\dev7\windows64" x64 devenv.exe connectome-workbench-1.2.3+git41-gc4c6c90/build_scripts/windows6412/setup_env.bat000077500000000000000000000000701300200146000273320ustar00rootroot00000000000000call c7env64.bat call env.bat "c:\dev7\windows64" AMD64connectome-workbench-1.2.3+git41-gc4c6c90/build_scripts/windows64_debug.bat000077500000000000000000000013701300200146000263310ustar00rootroot00000000000000@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" connectome-workbench-1.2.3+git41-gc4c6c90/build_scripts/windows64_remote.sh000077500000000000000000000003711300200146000264020ustar00rootroot00000000000000#!/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 connectome-workbench-1.2.3+git41-gc4c6c90/build_scripts/windows64_remote_debug.sh000077500000000000000000000004211300200146000275440ustar00rootroot00000000000000#!/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 connectome-workbench-1.2.3+git41-gc4c6c90/icons/000077500000000000000000000000001300200146000210705ustar00rootroot00000000000000connectome-workbench-1.2.3+git41-gc4c6c90/icons/linux/000077500000000000000000000000001300200146000222275ustar00rootroot00000000000000connectome-workbench-1.2.3+git41-gc4c6c90/icons/linux/workbench_128x128x32.png000066400000000000000000000614301300200146000262750ustar00rootroot00000000000000PNG  IHDR>a IDATxXUɂ.̊ QDs9$ 9IR QD%'IbE9mZK;owϝ=}<-R_?O,??O,??O,??O,?7 R}GƟ;c ڎ}c3٘)ǙL3Ik1Ul{J*>߾}؇<,䵓/*,g6@*9taRۿ$Rة}#cQ>0l ~qwB/g,^n:Ɗ+WfZO),Vd_v5WZJcfͻeu$MOJoعo5lYfʕ+Y.]ؼy˒E5#-V._7|׭~ikVr +u$"K˲z} r\ƍ܌GFF_nL4޲0Ϗ>e%e?/fW  XQfdVVXL}Wn޼9GGWwcs{h:ACK&07C@ʺ?\qqMLԩSmnn.]?Tc42eʔllt=MbcNfca-Zgf`;O1Hbdfvu\;++ $\;;;u|_hHhxA -eLIE}ʏÌV hhx3 CI3dR1 SUc1w-6lI q(*)~ցNA=Pr,#1C.ʌa=zPZR(,(=9-#k <-۶yN:m#Lf`P,%.SQQWJKy-D߸+xc~)QTd#9u)Q`,2eyqBa@Ξ)GV❆& !y ؘHJeF c0}|ёJ<5 0h}j T~46Sv}K,?M;P ,৫_&֋`G{[,x{1[ 2Br3` #i!%-+ &!U%(ShiEα 83%EPJ 13(AJ=|Y\VƬoW[kqb﷒ʑ}PTPh9kwJ2?0 =ӧW tW0&΢׭ͲwsGc\ _T恢 L kvvI@T\ *jcV HmGMU)bc㙲mBL M-281KOkEKm1 NBfZĉ,l7Tm'N ]|r,a6o˘S>^z\heM bnQ><}D0XئP0uT''|ߗtzVLp|njkv&ŰPl߶*P5 , Wp*X VdUJ74 `iəA_KJ*Ӄ/RQӥhi>N\EUMR`ai+3x␗\AwbLbn=.]<tƷy&cq:IXW m8rrrHƇh | dbea>u ׂĶʒC.'" }+~.bC^NPKcbICP `]fywhk{<=?&'3QRr Ms wjw y}< P(R3}HEѱ||[ f0`ώcַXq;]} #ǘj_e}ٔ tfOFL! 6_ gggfb=Bjב%7.,8 WqU>/9iȇi-}+Q Yy,lxظlaS[axbkkKcxxe z<23Pt4.՝è 6BuS }"A3tahKviRV=zqUwMq֭[,02::wI7( w(6"Fj& Y%yˉ"ӝSSҠ#N Jr]ئPՎ=Ζ*u^8dOu@m>fkq"Gvu`C{^;yHVg{) 0G2k`ka_`aINc#6ou aᡣv 2ecwu m`x,^c (~}[Ev|nIؙA] "y1LQ|C'y'uq#y/d`mcMAEEҲ PP)3;PQTUW L/BH]tr*E቏&磷i>ث f|wZ42\3ᡱ!R㢐9i? S`Xm$Dރ)n 1v$ήcW;QxvNxx*M%5V)c-~?/~G;i;d7MX\q\N|~3N-}2̇ dʏ>tgspfj062$@LcZ'=g+SXlLPX3ڏkCEYBE  ާˏ,^4M5׈#傩: PQUHK)(AFF 48\]I$ ٳX aXNn @zV;j=uRHVY/[>-B(-Ab8?Jy\=&sqOı *N>Ut*/ENx 89ԂA<1wU"^7#]X[[? 'g?T{g5Uh#<$E!Əy0\ƅ>5(LNFqal eEA \e6\lZv 1f/TL;cLFW&uOss<[1 RXh $g}K=˒_t7&nSOfco@z׫^ܫ#8nYҲZTמG]k/zky0-<ɍ^\$ҌkhN UW2 pE܁8UT/ц(.v9hұȶ 2=Ÿ4xOݴ}6zxQcCi0bh؋x9.4wti*VV#1*]3HVF>̢(8^0;۶n +ei'͘_ c'و4Tl+=fbܺ`111KHBIF ڢ@__/:ۚނF4661nbཽ:;TWKhm?j' o@kz"iܴ؍*?$ed`db+K 0`Y &+]FMe 1 *ƿ)OacL#bnr~ ]쨷 MFəӤG|,ϐ3;UE n߁mWCsىf*Ź Tt`~، 'Q^߈bJJQSY=$Pp.$I(CNg'_E[^m-?`gwdJ ~0Y%Ogy $;NH턞-+a$F%DY+j"EB/7B9xĉ%[1Aa:9;8 [p[ԪIBH;DG8Q%@b ! "T#S6 4gҝ 2Ig~Ű/=]pbz?K];ZÑu{ ~j8+;'x [ uw0ੀÁ>hF3?AN!Zr75;pkxpJhI&2Jy60ȃ PPֆ fp! ANae HMz*puNH\cEhpKLLBzZ:>|}#ܼyD7 'hi/$Z gსOE\L/ aeOt)Wk^󥒓iifjXEAۥ5ϻNGt;܀SCj5D?#@jP`Dž+pYgm17+BGe ܹ÷#2yԿ,g{,NGU2T~02X8wbF}pXA‰o ]`U™3'B32VQS}C+H ".lƊ́h$ Q#Tx}?~{w t06G@= ` H[ܳ |xe+GyѸ)c`=I@ k 3ʚH5\DF ^@W[+ЕK6r8{ e8]}Q nՁku჉c\ܻ'~_+{ ;dLsc/#Â!60{ ߏ0^-fj߂kP.%Q˪ lP߽ mR5~eÇL$o|%ɍ0p}ׯx-.T@hMh$'>GוABU],EtX(RRq)7Ỹ\.0. `dj z...$f*{(0P 1ݗ_~zԍs$FcI î8nҘ]1xk.|Lcd=?fΘ/GߴpϜ4*ë¯#fׅS-d> <BT\_;q~ \spp8oω׹qxx}o`l#nfCm4wŋx<= Gб%=B@|f$zT{)(AYN`/y('OGOB;"=!%"-Ғ`nnKKK4B ,09 O2 7`1qa~:FQ-8a*1oQT\Yit4;8 S+7X@Oz";X|\.1?R|ȉƝG0RA`L7|R0RHxyA@c7BF' b5;)\3lW2q1H }( CƽHŎ吓<4 I$l`bbj pvv1y&Btyxʅ0R͋z[%'hp!)^[*-'ߒ'&?`l{7gfib悰3mII*Bh-jkΈGL,K^c}}X[!8^8@ߧTa IDATد xDMܬ 7ޚmlo5(*Ex̍KGqsx ;1i{I@bln— iE=t[aaK%2:q.5S܎;`,,$$ -UUUhjةO=c9,4ހKD>ŋ f\`x.7fb+ssfAj"Zok;w} J@GrllqJnXc/3,КS$aGn!Nς_`bcIY#/Rt/ڣH~ `ō3$&yB= G9 'ģ\ [K{\Sě(Bafع4jxt4##)F/  8u ǡ,Mb Rqq8x$Ez`mT|؈% q11hiiS3}t#%Atj=5>Ko0BJM\/- /=\xv>b(ӓ/!;DΥhkmQRRJ4!'P OiCw{3Fmz أ[c\;oāw1h6ih،s$p>:]<ƒ/qzĴ[~$ Fs1Ń]s>Ƌ8ކmظ8dJV Ư'pR , 7Ѧز~ 6m!AAP$vp_ 1Wx_M7] 1_dp w (GdW_X,VEltXzɓSRIxxfb 0P[T"M.4x/tسn \7z!a44 (T;@kǣ8e֛_PEpnr|)PÃ`~WpKɆasNδ n)3.O\t|*>[&p\ٙj>|[R-aم8Ń%ܱ"EFw#RaJwÅxn5Z^kHsůȚQ]t[p-z(jkn+י7neվ$r:qLݧV.\G碵hI; apsfXL94Vի8x Ciap&4Dz@(<뾃h .^3P22p_f*>3ùE@ZpC4^Sڼo'ɓC"T>|5{L%g6< qO LWagKʰH5‚hk -y @u*)!UrTHHÐ T>B2YQƅ3pd,db+&[<ōW qcb ZBqDϻwnk 3@G { Gv@n`zijX&$`2~ q;~r9k>?F m Ma4[wQZ|⪮p?BpV:\7`x~2HrEҕ rS%M'vdkttuBB;GD޼yC7Q^Vb<#$&Ŏ:Mk6^[ō; 6/k\|2i ˰B@" N?y뿗/b#юڪ2,ABK;o3w$=| rr ^ZpB$I0&Ԕ#'Ht^›ׯ믿bp`uu1z[ 7xTčx|۞ /3"pMT9E(=Uk!B1 n7Q% '(̏қMuGȷ@4Js!MRDLr@\2‰ œc" 9=O%ܻ{]]052b_\mHgNgr,fƫ켇;gl|l 1fX<_tLD 2uy}ZD)i@JJ&\5CM  ݻwx5>x8VZ Zj1 N2ڬv}x^qW҄h;߂ޫqRvt(5 r 8#r!lnJx Lb.=gEQ'=2妥 ٪,.&Ld3 C؈ + M7SHCCCЈZn7mڬ_{, ##H|<䧆C?^:465SeeD1zX~v*3a=Ð? c.JcO?gOxM%ډaıcHIWkG\n7M18/'K]$|e8))HNL@Ol12#\Im8f '8kWrpm A/0xpc؇wDG᪬@cC=\ļo#PBWp .P حs0[ 0Ӏ*nU_7-I\^q~*.'F^nx1s{qG,)A.6aXr=V\Ea J렌ߓcb`2 >SK%wx>|{|pb7.twrȶ+% g`(+h%?{n" ހ4x]-S;!m3|I0OP~_o/ vՐcUSE'O|7E͛Wx5>{ H/}yvbHz.>EO=‚(ЇQ0߃~Œ]ތ}a .p$=|pn T [QRZC7 ɋ|r ͮK89s>TM^@A?5p3 ;ElHċ @6555tϿцL" >r0j oلF1 ˔R`g =##ثɢO }Ds%Xtt!K73y \n>FUŢX,m۱s=6/M]«p}\F_Z.+,Bjhyr%2gqg**۞B2=h%U#x`% #-G5n+ "y׮S5u=h gb20ذyfYU XS+`%/yd六q_WuLS{٤M\A*,:9F7$v fjEbr8`llu]d/hU_O#]@Wp9|%B= p~6nm00uIG.&Fq:#!= e0m!"[Kd7kL$!,&Sh 061=By.j5%BTGx%<{Dcx>֦ė<`VĨVwG\wMk1&w7<}*/B.i@Zxq" ={I| ]>a :D~~.֢p j -9`׮Rmu0JO$I(<B&H 2/Mw@D7 ;V˘#<8Qpv-`BPOqGane 9ⅭBd\s"( "f2郩7 ОW7|.vX,aǰq lL(ISEԳRR۱az[7m05M7JTTo'D<~=W zĜ^"~ȔL _ 0dWH6T@uC|1탷q!WfIs1 `?(<4v?7HyENn&,L-iܔ6Wu& GF3\ >4HK <p}e(ػh+#)-K:0O@\dʣht 8XkN87ژ6 ~iY{\=69=nbGg>!vP^ \r"\l}4hwM/^J5+wY-شa%v***`4DDEcl۶ܹƍf^ׯ~FFm>zϟއa(UXbbT9g3AH~wׅ8CǺ[+޿%ꨇXп{&J7ݓz?.C{8qDܺ7-cIևR7˘gOCe}hFv6֡s`qLaȩjx(Jmh,T]4 .$F!F zmw=]3*IWK6 򩰰q0.Ɩ~<lamm]#"{ zQh6€tZ'@|$Z{56ԡ=+fHM?I)$P?7O a !{#Z4fvf#IW{zF9M#yT淯 9=(\+Ή̃Ȓ4H/ƣ`3\}:ZQU]I|1WC u𽇅 YP ޻*"Bh D5eeWt6 P7A^|7MMyy$ D~ O]#44FF4(K ; F%%%A\͛7 lY0؏5pť8c i FZ°Wޅu$A5XT"P#f9#q"625M璒ĸOj\x1M֗/_(233:B\R‚s9%YT$ A%wdehI+qů*|@||ME9xlkm8֯[!c7nŭ\9O޿@as8~8aJ/e'KKpH+Us C I==K$0D? L&22znķxKo3>4ϔW vjE+L ݳ L qCQp􇥭]ijgi:DLL_p$BETrO.1NmRV|LLLct? S~O!*"!$$A dHNoQǦ<9 )>p^RH1_47b# I-)TBb0Px>^Ԝg[vyԐA !17(zH]UXv3J1+*Oǩ}5?I?<GG26HBRp׈*j2&*II8wFt27"}aMl Da{$!,4Spt1Z ^&. 7ŋ T6њ/Bhz׌ e*s@;sBzY+?8=D}Uܾ=L ^G0\XFthӯҢ:9:m uh_[ JpV ΏBK~Ċ^F0-i!r#% 9i"+ Pm :{/mm/UAP GƑLClE܈ƽG]E(A IDAT ,x3<|sx o$z՛[Cz/oo=,++ÙӧL=tTY@}yaDw‹虨fh&`RX'bO(,G}]9&tp{/]kvzX#O <{JƠ |/WT v@@܀OO/buugq0`~ #R"ЕoddL/WOZohlB`Jy:</x /qFW!WUTكkP,IRr&o$>4$Yc;(c=yQO30p KFޅ֖o[ah`?1;OPUeѦ [lmC;0D%sDPPH0 A(mٶ plgf0Zν9U_ծi4e$߰~42ޢp{`ƈCw[(cy1blHx3-`vqEPr" oc{u\PEf"Wxm$\E}[degsլ ,7^}BNi&lԹ˙+V!riBGxzf.僿N33Œ?gD#]g Xw 445`|(D$ yÅTy {yuy6żqj5/B4&샫P\\7?ěG())sY͸w4c1/ǘ1OÆ SFʨjB6=( ;Tx*yͼ6,ZTg /vv)4ZK C@ 6{b"9ԹkJڵkx9_ \eF0[}KewpE.'w,.n ~WζO kz ZXm>]>?vTG=ؼe r B&u\?l=v2=m(rc3?b<&TX;?=ntth|wJ*>e@Fhn}4y֬{"ù~UGp\6ϵXcciֲ7D 0Zچ̽kƍX,oVVV2WAOO:nfJH;'NJ'\1޺u# V/L-5u!qW4@E\'![^mS׊$n\ǯBKqFQ-Iiq *ZLLߦ l7É9b4CEn362"Xmi"EȻ"CfY5 Pr!QY%p3R3"^z }x 0*_m`!͡} rZ䓎H$'DCgW-hxx߽4v>q3<]{XzlfS@*eʴ_pp0N Cud tfz YYYs0iS_ q!$RQl`|`.Z$y!$x=KNݏOQqlV/fZR+]+Kq[RЊL0H ;G7l3>p"xēבu9B_ % ӹK=1D"Gݞ <1ikKOjjAP^O ִpɡ_mȀV˚+//;sw x%CuM->xKpH.RR!(ht = =/6w ^]ǘ-Llb!ͺWog30$ lBk;&jjf1ؼjo݁w o#1wPm]n e7bc) V]ا K`k>U܁)+AEMmeו3[xt2)m@;Zb&(Η@@ @P|*#BS<UNjؗ@eu`Z,zj(Hx-HR=yJXMͧM Q~E]:@Ieo'#*2B}BdT@7gq]\'RٹHbLf2wCobU075$]35J͋$-u"=e-syJ;X0s1 &[c[6dCy;F;w|3fX ! ,.|T; =ؗ LEv?WvC83/Tέ<' =RA=]I4 1-MUXy`c7036~3!)qBD |f &&iRp $Ξ)y# c0oV;'Mhk!S*$C _vck  e=:X|e\.y+g:PWGBbq(ATbn^.=~za ܀oC2 0g*%_k#NʷC2/mG;k%442j)20j;#_5Ġ ?4T2 Qx Ʀ]냍ۢn5lM_B͕pP7J9_{ϲH`dI >PWbfL : If" VS'i>|4BYi )=[L5[L&t'/\^|ߠ.8t=ջJJcz1?>ǛjEFF5g/ZBN^vHݽd $$ǚ `@_,ff3$׿TuH2GL X$A-r+(+ڇ4t6y )GTA"۷dY!lcIQHKX0X%C"0^g+yFPwu?u8ʪֵ{x(%[HO@pqqf==bP6[Ƹ3 //ـJK˘säD!4d9ӺUQ)ʙ-cZ7\+qP^&!-%9Dko|lf[.$$&gΪ+,,lU8 qVTC~^-2F`̙4GG{,Ƭ٪P6öZȯ |_SSK';ZA1>s^_ ҷ #{?FdctfU "zGux)JObwNxvrC&akcŻ3gY0荗L&Ap7٣^~(CT?~" 9^zm݀`y}{2 q(r}vI p W{[X/Р:gkk!h]BBDF]º~O}E&FutJoktttEUis%Sh/Sb٘:C3bJfT#=yό^# Wbk,*mM{yS6֌YsdfJ15x54]  ߄?5w< )}|xb*2:8:1`xccJ'F/\1pdR<}e,r 't1# `db Kkx9#=3X]k0ZbH53586An\4MƪIڨ%0Vq@B[!h=E~v' ÙM2PTT7njZv#G-N|+WbQ۶ 7nTM#1b-&ͫ7!.6Xn*D]Vg{ &72f!55=9nCH;foq#x?Oo^;Οp_3g@v Ձ5DnnFxSSy(xP`ee%RRLiHu*.j]E\h3MV!-G"na$&bXE,3t1j+f,4i\q-Ǝlj}yv}[[Qoa䶒e>}a 8h`HD))9YHMWnd5Hܛ60ja!1_\sxΞ=\N&^~!f^%x5o/zEG!us1!)W1_ieJދHf>.lDBL[ǺV8BbRS_Nkܓ\nS}Ρ >.&ee(*,r3L`$E/&/rleڄs5=`h!?!4ǘ&V+7gqW`IDweUbރ.ˇQ@ H*i5÷#$4)['))O' G颯ӞWjjhbʈ0\N' 7Pn\MVCJw|Gjkֿ\5!C8' PRv pz0)Y{+>iϚ"߀W`yy(6noٰ#&%,4[mg`!,qW ~Bxx+1AHHGC8@ Vd+9q;{XƋ d(E7Ly9#fq~^UhM<-b 3yi7}`GQq H?Bs}~g@p sOsз}bth- Qu46z`Ҥi j`FJ>\m? aÆ͛7?->2;)9:׿m߾ۣoA?w뺍5O41B=7X FfU-PlF>¢Bދ U  G`Ӭ0ݶܮ)$"B`ukxܝf1S4Z$&&@ UV"˞s)fҕXR|۷4 (QxDT:P= [4`).VüY͛>} 8eW޽W*(|Bjku "8.A$N M)6+N n\1;!-!VG;gF"3555-7@iisr?v̙%gjkxlc\MA h`|%b5`_jPN(K :Tys0! RBt7`|C!3=Œ{3PfN37g3}CV䪙 W-6}昿D0@4p@> Qݺutdh1ND{7iku2c:u& /fۺN=>ố)j_Z A> dHmˢ f =讓0ؽՑPQVi" GhX3;f/B6Vwa:c([]ܺyJEIk`&s ܴ]n]&ƿyFiHĿОLh/)n/0)/֗ONF]ػqcYߌ1z\hL51bq 'kw\ |bg](?#z1覙 U˝BrU ƍ9#gP &5dW_Msޕ߉]d}}=IdIqLM5- . !'۫JY|9h`qVLIct&չS, g {qG u_ c[どM)@afWw_x @);X2%ȴw^ZoQGsEG:Ww?+>h]%mQ0@ɟ˴WZV^ax>K}Saڲ|,LNm Os: coc܇$=>y#,ajb*̜1qQaq$7G:wF8䔔T޵F I7\XeGr""¹H_TTā@3y ?&'' a]]m!mN[ٷrKxpEK3K ào0\ɬi>a"tiv=v( L\(MF݈ FhP@f u-- pt~]>o1Y\PLlPl VVS(-QHwp/P?^Q`iܸqm5mw#nh߀!F5T0'5:L]N+/] oQ ߇'󠯯_Ѝ~\ݏ/(JII1iv_]ـ=UЀHIDAT3>1oԑlVϔ ŧJqPP)dgghcc-[lʔ)222ee[wtk?|t:u$jD2uȤdd~=l}C##Dnڹ$'!5=r/OOX[YASK40WH()) >md5vwYZc}5_s܈:+ppvlps}@cc*@mQRV?~|3jqEK-`na#X0lP U* 4)_~鳔+LS]5~|~A1GA~TӷeoGi&牉 Rq;cuCirTٳwoטXQlL,{O ;oGLCkȯk? xa8?ҿxﶘ 4+޽{Iĭ[ϭ^A9tm,{}3" ɯ?!tH:@tC:!tH:@| ĪIENDB`connectome-workbench-1.2.3+git41-gc4c6c90/icons/linux/workbench_32x32x32.png000066400000000000000000000050471300200146000261230ustar00rootroot00000000000000PNG  IHDR szz IDATXW TSW~/l*Gt,QD;jE:H."[Hb@YB$@A@FQ*E=:vvlvdI'xVy)t(z!'djW?rB5j(V!#Bi4y\z!ݒ]<0UMyg4e2~ t:۞;w":'꘶)DC[ןڏuknicsҒRRA4׫- +/_fMXN?688`ccc!i{΀coen.fjbnggb'"þ m>s+= 'f=v-:Z d0"<<0}tO?m¥/͙"7z%BtٔCl͎ONJUc;Q.) 9dFH$bhɐ</$yf[)Esu9kFZ[]7T1#F/p7g͑@lS@Q^Z+xR^LNKx@HH0g26#@,[l!َuĘ⋣_OYd`H+Ts" Rƚj *tg1{mPA^הlm 46a-G'`5_N, ,T|vzl~҂g=؄x@[8iJ9nBRJʓp4o@\kA-[\Hŧ =mp$]u4 y&^Yd}g֑m9 ;@QJw;b'p?7rPI~ )v'rlA l7gQAneKg(q@ ݼBQ?;N>$re3KaVMۄUF:+(ڝHcV"j\TFz`"\p<]2RY _}U #VCPwZӐy0t$TW1XVe!ݰqAnی93-AJ17{B([e L\jY1Jk)8 *0zak! @36V4I:UXCq:K![Ġ[aEHv+yh| I?٧4J3b=v{)ϔ@5fںҢ2kt FUƺ~xپ^>XcDǵ8}?t5\a%ކ!\D.=&EڎjO%5bO(+p6[%Yc PdƒۅX+[ CmjU8/lޅPGVЌmejuj+3q"pR1㑟~`yP nz¦ljFyDyd,Rh7ֿ`dGQU"<:\=(w/+}S+a1֞ IqEz͏LNfs@ r`\)%"x ? BT4PNVw'60e>boTG?W _r*SB]Npqp6< ]?gKӳ.^C+1jrstSPx&,"ha=ҿATI.\ Yf[~Y[Pm[V 1{`IlsxK,ul!4L 6-_LɑTp?{T͸3jF{'gɧԑeTf\>8{ |aD?//9Gh/xЍs(ꗌSSScġ8YVVA9gߑz粌ԸIEhiPQ;;`~R[)/WNH^on=Ƃ=wrpqW)7#ky*p8yM3ΘقE^dU)aD\rhvԸ{A܂/ssLCC^f+f-3;w[م9^|=F4U w7mTŊ&|r~PvoaG,STX8Y}JQ25AأY[!TX,lBnnF 0z8GR ^O,LOO'% M+!9B,E"J(dRRY@C$&$p /oALlM3q6|}e$~t{hhcܒ >sSnFO_oo/f?zIENDB`connectome-workbench-1.2.3+git41-gc4c6c90/icons/mac/000077500000000000000000000000001300200146000216305ustar00rootroot00000000000000connectome-workbench-1.2.3+git41-gc4c6c90/icons/mac/data_file.icns000066400000000000000000006737521300200146000244420ustar00rootroot00000000000000icnswTOC His32s8mkil32ul8mkit32ct8mk@ic08Jic09xis32Ɓ+:_No{5 sYfvY@vxxulnze!jo}u_uIp]bfUu炀ijvCu^ysy{O[wY0\~X0 ,+̀rM+=_No{5 sYfvY@vxxulnze!jo}u_uIp]bfUv炀ijwCu^ysy{O[w[0\~X0 ,+̀rM+:_No{5 sYfvY@vxxulnze!jo}u_uIp]bfUu炀ijvCu^ysy{O[wY0\~X0 ,+̀rMs8mkSš͛0i*\J6!1dER Sil32uů +G& J@ldukTpMvly9*6bn\{G)Lbe7lL= VI{bʺua{Rb`dž`Byv~{^Qk~;j_tfuHLft9_]y[a]zӃFzZo= rohMZ4݌cnGRr`Z]7)ZܤُsʄoySbjI[JXK9/0q~pgW]SKAH:b_4r^inyPBhqq^MWVyfx}l~p^UWjWl`dovNqЯwhwfn~iqڏZ]{P.5wƄz`֤zl`k]mB}~j_\T!&PW\u`<жÊ EV5Uů +G( J@ldukTpMvly9*6bo\{G)Lbe7lL= VI{bʻva{Rbadž`Byv~{^Qk~;j_ufuHLft9_]y[b]zӃFzZo= rohMZ4ݍcoGRraZ]7)ZܤُsʄoySbjI[JXK9/0q~pgW]SKAI:c_4r^inyPChqq^MWVyfxl~q^UWjWl`eovNqѰwhwfn~iqڏZ]{P.5xƄz`פzl`k]mB}~j_\T!&PW]u`<жÊ EV5Uů +G& J@ldukTpMvly9*6bn\{G)Lbe7lL= VI{bʺua{Rb`dž`Byv~{^Qk~;j_tfuHLft9_]y[a]zӃFzZo= rohMZ4݌cnGRr`Z]7)ZܤُsʄoySbjI[JXK9/0q~pgW]SKAH:b_4r^inyPBhqq^MWVyfx}l~p^UWjWl`dovNqЯwhwfn~iqڏZ]{P.5wƄz`֤zl`k]mB}~j_\T!&PW\u`<жÊ EV5Ul8mk*;BKlU 7Gfcj@# " +x]Z x  \u hJ  bʞv56N  :BUKit32c        f8 f9 # mS\\by `y^W[SkF3 $s< } \Fw+N*+oOn^_oM)`HVfammogYaL)'';) >bYZ`fL+ ( HhXH3=I9$ ?cclwYOg{n  (  ?cs[9Jg]@$>QX\rVCfy5 ,PWH;;[tyI9gaM;#-@TUCQnjNCVpųp3 .>^jkWcwyAEXON?.5APYDGyoIDGNhse: 23fder]qb4ENXU@14DRQ:YD12>\vkZF=VkY^\QgytI6I_dfzR!8NFB\iboL:[e]D:JX]D.ICDkQ1FDUP>A @%Ymx}6D[_M;HJVqOIMKkx|`[s}ƴiN% 3iK E"6Nd}ˁ=JbSAJEIHuPDMUi̹\cdir~rjk7*j<H/9>=7HTsw6ZVMKFB@^pbOZkyĭVLae›yiRJJfbmHH00.1CKFIKeQKcVLHFHPas][XbvoXW@StpºTniehta(P >[6u?DrzTNWjVL^slQM}°zvЮp>S@¯tsfuM9JNpĴʽȍCHQ^oPTb~cIۦy}ː{ewIT snbeN;81Ľȵ`>KW`uJ]f^wku˺ȨpVmɳVYZǐyy[C*Hɺƣ]CQYpzyQ\[xvQ[m}ѿcLùYZAx[Ck̯ӆCLSazwYV]czɢb:]s~}uɦUXʖeVX(tlþΡgu]̢~yqܫ\IRVqu~Ga^g}LHkqpitֻy[^~uV׶K\ bȚb{{mϜs~wlޣRNW`z|iOianoHWghhjqzήccaZ`ݗ,_;qWPVk~ӗusi]NWYs}ORkkv͜_H_abfhov|żu~wɚ> [ s^Kj|tvY[xҏonVyࣃgP_e{eKstˀER[\acfmrttyæoӍ*_7r|Rfvxlyq`ڍhil趉nKeewԾ_Fs}ƾ~GOY[`adjnnq{|eo_IY~yjcWowvrrpRo`i绔vEiowSBv۳U=PU[^^`eikt}‡fȩZaR^UzJ{^[idi^ahĀjatj~߶}{;et}8Vt]@FGQUYYZ`flwƌsiw#a%ginz`uUqPPi{d|ln|Ɓq7\t~ϬXAbxTJTDADJS\^cjov{xq˜cMb SttrxvRuξ{|VA]qtj~jo¸xBTo}ĮP\sFIhbE117I[dilsxttжѳlg_5lbmleH?|ؐn}}a:[`pmlnxjltuhWdv˟isEJieF0=TK@UvzxxvطĂNwRiýfblVE_fv}e;Rbztafjhza`wpĒpisą†}vFNcMioyri}nN{e\TGJdڿ~}ƴɶb>YhVUxyvhcn{Sg֣Lb\HU2$tT m{rmsrIla;DєlfĨg9GKA@Moģ{ݯ[C^i`hvmm4TwuZhY< RWv B "8ôddQia@9}opl?:G?:8KrѶuԳ\Jkfhwpq˷rLYy]mwpT&>/x^fkfgĽZmvYQf`G8\snN4E@921Go׮n̼fKqgoΫα~kWhjsT>` x al]W~rɅN`cQb`IH7g؜go9?>6.&(@]nPxqxħǪ~tȺ{c}{ru&=y`5Wyrg~ĿsS^aR[aNQI5FkЇuÙTHO>-+%&0@S~→uX͖j|qwõwkrS?Ew7u3 ddPxȿvUXfh[YM\bR]fU>Z|UTO37DKGB97K`vԎL˖›߾pknjsыV`A1dl]jy x li]asf^WfͭaJSdS^mGI\dVZlrlbM>DZgx˃AK60Qdw۔WԙuӨwknke{w\>?a|mgs !zWf"X{ghZXspb\VgnFG[bZZeqtnbUIHNnk<3+2?KMF>91.--,+1>aܝ]ܯmt׷mdl_cҾu8Glo~'?z[n._s|jhZ]zƭ^kslidLC\d[Xajoqja]QGKfi<+%1FJD@815:;71*'"Bުs¾~yTŶw{hPo];TyŰmzYk(c_eegZci̻YarkV@XaVPRTU]hmscI9?`uO1/IJA826<>=842/#<۸©xyprVN{ǹPYW:\ٻm| zYl&VYcgmZior׼]Inh\\[XIGQLAIepXNAATqฒ{`84CC:28ADC>:525.GÒͣ{tlb8TνdKq؝dBZr~z\g;LOaijVixyۺd6OY|y}gUWEXz~eA5\td\UFAKaɽ|X>745?FIHD@<9<:Ko]A/,ɻN\Μs]c˿yx`;`UDPcmiqߣp9Psj{]]di:Fed\VJCCOgКH+9?FKMKGCBDGDIZhŃyx[?1:6\Ż]Oǭu[z)NJy bKjg?@a||BNykypzT<[bXQJFBDPfqM43IOTQNLKLNQOU^tjWWD>A@ABopLnZ{p" R rOho;HN>m~mʥׅHQ]RLJIFECHd•d=;OW[YTTWXY`t|eTQLG"EFHAMӺQc{yWƉ?X-z \Ghd6zY@a{uս˵͟[aTLZLKLLF>8@RcqphΗPRRZ]a_`dghorbMJOQRQLJMNLCHm[\xpilygg|4Fz`;`M@|a@Ynij}xacf`KQKTJNOLGDHU]dr֘{tqdZbhkqsqkc[TTVVUPMPTUQJ=lʛf]bnprtU`}DŽy z\iCCFjBWkmdX\aadPktWigTX6PNOSTX\^dhosͮ`fqv~}{re[XZZXSQV[]UUCGТqfYsmtxWhz`ǀy xRl'8U³wosKXtxr`eknm\]dQ[mbE=>QNUW_ggn|~~uΥҏ[nx~sicb_[XX_cb[XCLѣzXLvhuiJgl)y bv1[οvds|W[|~c\gpul_TPZl\`D:QPVZ^gb]{`aaw~޳ŐXs~rkgc_^cijdZ_A`ɢRJsmnugbe-Aw6D+SЮrϱg^py{ucg]OtQ@LXZX\lhenimjݦSƅ֊bwtke`bkol^aXMſ^Rd^rb[| r+hֳݼoUata`Ylj\JUTVewl{wgde~p}ޕi{}ofabjnc[Z\Ԧ`qZGVMgI{L &uQ6BkeA˷sKYhssp|zy`\d~MycGLMPUOI]iaU_jTlޠn~pf`_cbXS[ogQYyw˹h5rc1Z.w tok^;I̷UVwbmdjw{`VijsrrZ]eZOVXC7HXTMONSMlxzqf^\[WMRopguc\ɼU4eLv s3dWFH? FfMĞob[^u}oY\iZ͟rd`_wʹaakSHEMQh}轀yypf\ZTQLjjvvUôxK;L?,jAtҿrta?gwtv_^gpcؼpj_jo{ӬhTTftz|~Ɇkxna]XNQSvy]fX}ɲcG>/o7[ujZPA<2Rq`fn{awugwpģjiϪ{\`ny|wƇfc_ZSKVbp|TOƵc`~cK<+`>;9?KPQWXZ^futaBYŗudl~qr٨vafx{{՛^XUNLbu]´Z>PXC2"]+B_yMdθɃsx]qx]ٮgmpqߒLPJQuvIXu\GA@HL?.Y#O_~xo[PYO\f[Tرfdlsa8Ifpqj̤p#5DNUftswpsxXjs{UQZghUMR¾gE,B;Y9D1w؁ .<ay  (0Rd_UPeĽ%T@b (9b{n=wĻwWa|"-UkudȹVrh' 4S\a|̽{y||}xlK$+3>Uz|cP?7:6+  DWirkZIIJ?'   ) >bYZ`fL+ ( HhXH3=I9$ >cclwYOg{n  (  ?cs\9Jg]@$>QX]sVCfy5 ,PWH;;[tyI9gaM;#.@TUCQnjNCVqųq4 .=^jkWcwzAEXON?.5APZEGzoIDGNise: 23fder]qc5ENXV@14DSQ:YD12>\vkZF=VkY]\QgytI6I_df{R!8NEB\jbpL:[e]D:IX]D.IDDkQ2FDUP>A @%Ynx}6D[_N;IJVqOIMKkx|`[s}ǵiN% 3iK E"6Od}ˁ=JbSAKEJGvPDNVi͸\cdir~qjk7*j<H/9>=7HTrw6ZWMJGB@^pbPZkyŭVLae›ziRJJfbmHH00.1CKFIKeQKcVLHFHPas][XbvoXW@TtpúTnjehta(P =[6u?Dr{UNWjVL^slQM}°{wЮp?S@°tsfuM9JNpĴʽɍCHR^oPTb}cIܦy~ː|ewIT snbeN;81Ľȵ`>LW`uJ]f^wju̺ɨpVmȳVYZǐyy[C*Hɺƣ]CQYpzyQ\[x³vQ[m}ҿcLùZZAx[Ck̯ӆCLSazwYV]c{ɢb:]s~}uɦUXʖeVX(tlþ΢gu]̢~yqܫ\ISVqv~Ga^g}LHkqpiuֻy[^~uV׷L\ cțc{{mϜs~wlޣRNWaz|iOianoHWghgkqzήccaZ`ݘ+_;rWQVk~җusi]NWZt}ORlkv͜_H_abehov|żv~wɚ> \ s^Kk}tvY\xҏonVyᢃgP_f|fKst̀ER[\acfmrttyçoӍ*_7r|Rfvxlyq`ڍhil鷉nKefwԾ_Fs}Ǿ~GOY[`adjnnq{|en _IYyjcWowvrrpRo`i輔wEiowSBv۳U=PU[^^`eikt}‡fȪZaR^UzJ{^[idi^ahŀjatj~ස}z:et}8Vt]@FGQUYYZ`fkwnjtiv#a%ginzauUqPPhzc|mn|Ɓq7\t~ЬXAcxTJTDADJS\^cjov{xq̜dMb TttrxvRuξ||VA]quj~jo¸xBTo}ŮQ[sFIhbE117I[dilsxttжѳlg^6lcnleH?|ؐn}}a:[`pllnxkltuhWev˟irEJifF0=TK@UvzxxwطłNxRiľfclVE_fv}e;Rbztafkiza`wpĒpisą†}vFMcMhozri}nN{eYhVTxyvhcn{Sg֣Lb\HU2$tT m{rmtrIla/x^fkfgĽZmvYQf`G8\smN4E@921Go׮nͽfKqgoΫα~kWijrT>` x al\W~rɅM`cQb`IH7fלgo9?>6.&(@]nPxqxħǪ~tȺ{c}{ru&=y`5Wyrg~ĿtS_bR[aNQI5FkЇuÙTGO>-+%&0@S~→uY͖j|qwõvkrS?Ew7u4 ddPxȿvVXgi[YM\bR]fU>QdwܔWԚuӨwkoke{w\>?a|mgs !zWg&Y{ghZXspb]VfnFG[bZZeqtnbUIHNnk<3+2?KMF>91.--,+1>aܜ]ܰmtطmdl_cҾu8Glo~'?z[n-_s|jhZ]zƭ]ktlidMC\d[Xbjoqka]QGLfi<+%1FJD@815:;71*'"Bߪsþ~yTŶw{hPo];TzưmzYk(c`degZciͻYarlW@XaVPRTU^imscI9?`uO2/IJA826<>=842/#<ܹéxyqrVN{ǹPYW:]ڻm| zYl&VYchnZior׼]Inh\\[XJGQLAIepXN@ASqฒ{`84CC:28ADC>:526.GÒΣ{tlb8TνdKq؝eBZr~z\g;LOaijVixyܻd6OZ|y|gUWEXz~eA5\td\UFAKaɽ|X>745?EIHD@<9<:Kp]A0,ɻN\Νt]c̿xx`;aVDPcmiq~ߤp9Psjz]]ci:Fed\VJCCOgКG+9?FKMKGCBDGDIZhŃyx[?1:6\Ż]Oǭu[y)NJy bKkg?@a||CNykyqzT=[aXQJFBDPfqL43IOTQNLKLNQOU^tjWWD>A@BCoqLnY{p" R rOhn;HN>mnʥׅHQ]RLJIFECHd–d=;OW[YTTWXY`t|eTQLG"EFHBNӺQc{yWƉ?X-z \Gge7zZ@a{tս˵͟[aTLZLKLLF>8@RcqpiΗPRRZ]a_`dghorbMJOQRQLJMNLCHm[]woilygg–|4Fz`;`MA|a@Ynik}xacf`KQKTJNOLGDHU]dr֘{tqdZbhkqsrlc[TTUVUPMPTUQJ=lʛe]bnprtU`}DŽy z\iCCFjCWkmdX\`bdPktWifTX6PNOSTX\^dhosͮ`fqv~}{re[XZZXSQV[]UUCGТpfZsmtxWhz`ǀy xRk+9T³xpsKYtxr`ekom\]dQ[mbE=>QNUW_ggn|~~uΦҏ[nx~sicb_[XX_cb[XCLѣzXLviuiJgl)y bv2[οvds|W[|~c\gpul_TPZl\`D:QPVZ^gb]{`abx~ݳƐXs}rkgc_^cijdZ_A`ɢRJsmnvhbe-Aw6D+SЮrϲg^qy{ucg]OtQ@LXZX\lhenimjݦSƅ֊bwtke`bkol^bXMſ^Rd^rc[| r+hִ޽oUatb`Ylj\JUTVewl{wgdfq~Œޕi{}ofabjnc[Z\Ԧ`qZGVMgJ{M &uQ6BjeA˷sKZhssp|zy`\d~MzcGLMPUOI]iaU_jUlޠn~pf`_cbXS[ogRYyw˹h6sc1Z.w tok^;I̷VVwbmdjw{`VijsrrZ]eZOVXC7HXTMONTNlxzqf^\[WMRopgud\ɼU4eLv s3dWFH? GgMŞob[_v~pY\iZ͟rda_xʹaakSHEMRi}轀yypf\ZTQLjjvvUôxL;L?,j@tҿrta?gwtv`^gpcػpj_jo{ӬhTUgt{|}Ɇkxna]XNQSwy]fX}ɲdH>/o7[vjZPA<1Rq`fn{avugwpţjiϪ|\`ny}xƇfc_ZSKVbq}TOƵc`dK<,`>;9?KQRWXZ^futaBYŗvdl~qqبvafx{{՛^XUNLbu\´Z>PXC2#]+B_yMdθɃsx]qx]ٯgmorߒLPJQuvIXu\GA@HL?.Y#O_~xoZPYO\f[Tرfdlsa8Ifpqj̤p"6ENVfttwpsxXjs{UQ[ghUMR¾gE,B;Y9D1w؁ .<ay  (0Rd_UPeĽ%T@b (:b{n=wĻwXa|"-UkudȹVuh' 4S\a|̽{z|xlK$+3>Uz|cP?7:6+ !EWirlZIIJ?'  ;I7BH-SBSer+c5UW@x}bLqg-B#ǶeYz L/75yޔ;`!  ^6/; B$          f8 f9 # mS\\by `y^W[SkF3 $s< } \Fw+N*+oOn^_oM)`HVfammogYaL)'';) >bYZ`fL+ ( HhXH3=I9$ ?cclwYOg{n  (  ?cs[9Jg]@$>QX\rVCfy5 ,PWH;;[tyI9gaM;#-@TUCQnjNCVpųp3 .>^jkWcwyAEXON?.5APYDGyoIDGNhse: 23fder]qb4ENXU@14DRQ:YD12>\vkZF=VkY]\QgytI6I_dfzR!8NFB\iboL:[e]D:JX]D.ICDkQ1FDUP>A @%Ymx}6D[_M;HJVqOIMKkx|`[s}ƴiN% 3iK E"6Nd}ˁ=JbSAJEIHuPDMUi̹\cdir~rjk7*j<H/9>=7HTsw6ZVMKFB@^pbOZkyĭVLae›yiRJJfbmHH00.1CKFIKeQKcVLHFHPas][XbvoXW@StpºTniehta(P >[6u?DrzTNWjVL^slQM}°zvЮp>S@¯tsfuM9JNpĴʽȍCHR^oPTb~cIۦy}ː{ewIT snbeN;81Ľȵ`>KW`uJ]f^wku˺ȨpVmɳVYZǐyy[C*Hɺƣ]CQYpzyQ\[xvQ[m}ѿcLùYZAx[Ck̯ӆCLSazwYV]czɢb:]s~}uɦUXʖeVX(tlþΡgu]̢~yqܫ\IRVqu~Ga^g}LHkqpitֻy[^~uV׶K\ bȚb{{mϜs~wlޣRNW`z|iOianoHWghhjqzήccaZ`ݗ,_;qWPVk~җusi]NWYs}ORkkv͜_H_abfhov|żu~wɚ> [ s^Kj|tvY[xҏonVyࣃgP_e{eKstˀER[\acfmrttyæoӍ*_7r|Rfvxlyq`ڍhil趉nKeewԾ_Fs}ƾ~GOY[`adjnnq{|eo_IY~yjcWowvrrpRo`i缔vEiowSBv۳U=PU[^^`eikt}‡fȩZaR^UzJ{^[idi^ahĀjatj~߶}{;et}8Vt]@FGQUYYZ`flwƌsiw#a%ginz`uUqPPi{c|ln|Ɓq7\t~ϬXAbxTJTDADJS\^cjov{xq˜cMb SttrxvRuξ{|VA]qtj~jo¸xBTo}ĮP\sFIhbE117I[dilsxttжѳlg_5lbmleH?|ؐn}}a:[`pmlnxjltuhWdv˟isEJieF0=TK@UvzxxvطĂNwRiýfblVE_fv}e;Rbztafjhza`wpĒpisą†}vFNcMioyri}nN{e\TGJdڿ~}ƴɶb>YhVUxyvhcn{Sg֣Lb\HU2$tT m{rmsrIla;DєlfĨg9GKA@Moģ{ݯ[C^i`hvmm4TwuZhY< RWv B "8ôddQia@9}opl?:G?:8KrѶuԳ\Jkfhwpq˷rLYy]mwpT&>/x^fkfgĽZmvYQf`G8\snN4E@921Go׮n̼fKqgoΫα~kWhjsT>` x al]W~rɅN`cQb`IH7g؜go9?>6.&(@]nPxqxħǪ~tȺ{c}{ru&=y`5Wyrg~ĿsS^aR[aNQI5FkЇuÙTHO>-+%&0@S~→uX͖j|qwõwkrS?Ew7u3 ddPxȿvUXfh[YM\bR]fU>Z|UTO37DKGB97K`vԎL˖›߾pknjsыV`A1dl]jy x li]asf^WfͭaJSdS^mGI\dVZlrlbM>DZgx˃AK60Qdw۔WԙuӨwknke{w\>?a|mgs !zWf"X{ghZXspb\VgnFG[bZZeqtnbUIHNnk<3+2?KMF>91.--,+1>aܝ]ܯmt׷mdl_cҾu8Glo~'?z[n._s|jhZ]zƭ^kslidLC\d[Xajoqja]QGKfi<+%1FJD@815:;71*'"Bުs¾~yTŶw{hPo];TyŰmzYk(c_eegZci̻YarkV@XaVPRTU]hmscI9?`uO1/IJA826<>=842/#<۸©xyprVN{ǹPYW:\ٻm| zYl&VYcgmZior׼]Inh\\[XIGQLAIepXNABTqฒ{`84CC:28ADC>:525.GÒͣ{tlb8TνdKq؝dBZr~z\g;LOaijVixyۺd6OY|y}gUWEXz~eA5\td\UFAKaɽ|X>745?FIHD@<9<:Ko]A/,ɻN\Μs]c˿xx`;`UDPcmiqߣp9Psj{]]di:Fed\VJCCOgКH+9?FKMKGCBDGDIZhŃyx[?1:6\Ż]Oǭu[z)NJy bKjg?@a||BNykypzT<[bXQJFBDPfqM43IOTQNLKLNQOU^tjWWD>A@ABopLnZ{p" R rOho;HN>m~mɥׅHQ]RLJIFECHd•d=;OW[YTTWXY`t|eTQLG"EFHAMӺQc{yWƉ?X-z \Ghd6zY@a{uս˵͟[aTLZLKLLF>8@RcqphΗPRRZ]a_`dghorbMJOQRQLJMNLCHm[\xpilygg|4Fz`;`M@|a@Ynij}xacf`KQKTJNOLGDHU]dr֘{tqdZbhkqsqkc[TTUVUPMPTUQJ=lʛf]bnprtU`}DŽy z\iCCFjBWkmdX\aadPktWigTX6PNOSTX\^dhosͮ`fqv~}{re[XZZXSQV[]UUCGТqfYsmtxWhz`ǀy xRl'8U³wosKXtxr`eknm\]dQ[mbE=>QNUW_ggn|~~uΥҏ[nx~sicb_[XX_cb[XCLѣzXLviuiJgl)y bv1[οvds|W[|~c\gpul_TPZl\`D:QPVZ^gb]{`abw~޲ŐXs~rkgc_^cijdZ_A`ɢRJsmnugbe-Aw6D+SЮrϱg^py{ucg]OtQ@LXZX\lhenimjݦSƅ֊bwtke`bkol^aXMſ^Rd^rb[| r+hֳݼoUata`Ylj\JUTVewl{wgde~p}ޕi{}ofabjnc[Z\Ԧ`qZGVMgI{L &uQ6BkeA˷sKYhssp|zy`\d~MycGLMPUOI]iaU_jTlޠn~pf`_cbXS[ogQYyw˹h5rc1Z.w tok^;I̷UVwbmdjw{`VijsrrZ]eZOVXC7HXTMONSMlxzqf^\[WMRopguc\ɼU4eLv s3dWFH? FfMĞob[^u}oY\iZ͟rd`_wʹaakSHEMQh}轀yypf\ZTQLjjvvUôxK;L?,jAtҿrta?gwtv_^gpcؼpi_jo{ӬhTTftz|~Ɇkxna]XNQSvy]fX}ɲcG>/o7[ujZPA<2Rq`fn{awugwpģjiϪ{\`ny|wƇfc_ZSKVbp|TOƵc`~cK<+`>;9?KPQWXZ^futaBYŗudl~qr٨vafx{{՛^XUNLbu]´Z>PXC2"]+B_yMdθɃsx]qx]ٮgmpqߒLPJQuvIXu\GA@HL?.Y#O_~xo[PYO\f[Tرfdlsa8Ifpqj̤p#5DNUftswpsxXjs{UQZghUMR¾gE,B;Y9D1w؁ .<ay  (0Rd_UPeĽ%T@b (:b{n=wĻwWa|"-UkudȹVrh' 4S\a|̽{y||}xlK$+3>Uz|cP?7:6+  DWirkZIIJ?'   Xf-!JL;1(7ӸRsQ |䶆V#,Q6àq2]ϞO[ lDݒ/ Hz|oV=(lקq!:{ Iyn dg-lFQZu x(d6! \::5}=_)7yJ*^2uQRE%9Yq3D+F:D:B?CHA`SofnVlDZ2F(i@{\LK:_%l6kc  &HyB'_lNb# Auc 8[߳yK3MvΨC""2W{2LA4(U|.>BoR? XGůUS[IJ*$:7鶪O{7@Hkk?<kktq݋m6nƶد-mR;`zv x#=\% oYRڱ#&?>ҹЪn_;j;$}*}+(}'}/LtY"$].9⦅%{_a݊]hk5'SN{<_ t jM{-4%TńtY۟R6#v\喊x:'HO3^&0::m,L%3:qVE t]~Iv6Wٯ) |ʸ2]G4(6w‹$"AEv m[D;Vh[}چN|3HS:KtxU'D;77;_"e?Yqxl+@IDATx ]W}&sߠ'˖yb-π0CB* N;Uݽ:ԪUITHXI36b01`#yy7~(lٱ,ѹ {}asnR@!P@!P@!P@!P@!P@!P@!P@!P@!P@!P@!P@!P@!P@!P@!P@!P@!P@!P@!P@!P@!P@!P@!P@!P@!P@!P@!P@!P@!P@!P@!P@!P@!P@!P@!P@!P@!P@!P@!P@!P@!P@!P@!P@!P@!P@!P@!P@!P@!P@!P@!P@!P@!P@!P@!P@!P@!P@!P@!P@!P@!P@!P@!P@!P@!P@!P@!P@!P@!P@!P@!P@!P@!P@!P@!P@!P@!P@!P@!P@!3gn p8p0 NjfkeBAl[km9gkW[b$-|>dzdݰ 9>sl[5.j@!"}gϺ\ͮ牭]l}2__3zc7>{ 6Z~ .w.??O>ڷVsY/{m_aֲ",fυ|g+VXcw?SO?wsr%;^{ͬ_~{YK0.>+!V<"Y|ի^??222z]wuv.%Kt !fF vagc83F w~O_K7<|&1E!jh;{;?#\n݂u?<{/ˮBauwuWaÆ._666nҥ>{S>x>rs{衇 <|.uĂ\`02{ǺGwÍ7[x㍟emVm,}*!c;ukד{=sOp!O:3`Ń[ouvmAA`fff7phDX]pvk֬!=wy`vvvs#%sFƉL{oJv ]oibφdKb,0`ٲecI CK+l7:2:3Iƒƒ%4  !~x纔-\h^kA lbx=wwwG1s#3 ~~wޞ{|386!aHd?HO.3+:u3: Ve]eARD;Ymc׼5]Db~w-][7n1kXxgE`n{[n@5ݢ]u 7N?[xp+Wx ʘ9F>S6Iǯ}oGG/3&Ab'YJvbZKm?Yd滑ёފ8w}|x߹xtIvwK/x&1܇B.܀j$~xko2߭_ [Ff"ÓN:3<ʋ/c^{E C-Qψ9 ڐ3l<6/7܈?>.?jْ?]|n.u4Vxj*qzքgd3ccݺ5O|&dxMPM8Ѽ24%D/[If@|VD!QnੵO 7!HQG5aWjʕʹ+ܔã9.4 t /.M ew-; L/OroUGbYX8ah<_k_.d2 h~uSSmRr'n5'ky vO=T;N(> wBv(;~ %FDpAF?i'%?0w+X}._@r>>\X?٪Y{ˮ׉bMD{ssSFG`Y.x__2KYdH]w}vI57>"B9q[(!qxW6]<ēL )\|.00#YooeC?4<}i;҆57|ww\>ѫza xw.6\C99V}< 2IhpUWRNKR&8taCB",JyfN$ ($0@ Ǹu}_S6ld !  زv$DF ' ^q A=+GVhvrN̟P%:ɾ&By-٘\)ȭ|be@,F?qcI6n5|X"agMiÏGl-%8,,0N~?)1Cs"Yr I!q6\b#eKŻ-nonDGd8#joո`|&@P2?%A#]]K*  ]+qHhc#o10'!od|'H<{0^{.N~}uI4}p1lh ,hPlJ?:3!o{. QBX6#暛ౚ-@et?6&c@T֟PX zKB}^Ww.Na>s3-${}n U6!0@PM|A?A)r!_nr ΋(4K^!C%a{/%gX .+ynm.v4$#ykxX[O6^1'y`\w:Szsù6V [P!4!vmg'ȜuQ{i6Ǘ7XY R!'U%װO^E|9aSXB`{!;oam6^xW3K )ɺN9.sﺻ+~94#W;s;c-y'k׽Yl׍ ~/\t9Gpq2Y{cou] ;{ԑGr>{e ģ=چwf h /GQ9SޮN^`&W҆He;!PݬXnq'C<8K?u}?fy4R"lY} q-mP6h>~}iC0]BRxA02"=sWR`-/1%/1)- o=bק_s5C1?wguD>df! $ "|FRZ,N4zrJ"&k+h(x"?>qY,Q2kibf!"|}B%\2}vںrP`xO+'-%/co~> )qg";V376 Xa]uU+Wd͢c;ِS`Sz cr ZI@gJ7ƒ2o}[[ALdFEha2Q H,s;r 9Ϲ!߀껕'"ы O|ι‚;<|ZDjY :?c ?vOV7Cua^ޙYtDcm+m{ug%g[#'4H%1N6~D;=DDn$FZeu]Ku =ķ_Fo %EFܓpۏu`3`Y%Dk9Vp(x=tw NI3{o,C[G~yrr{ի_\>6lq4DKMHGwd.xÍMD H!KoAlDt-r!-BD;:K&E E PC=up?ȣMشEChޏ0xje,B>q}^b_O.ܧZ^KL̙`!.BV]mz{ّEE*5܇:?k|G;@>#Kɜ{3qv",gFZ kD57\ ;p,]X|SP[  wË@rh}'fFzG$,pW=0s2O^PT/h)xAp=>=I0@(Ƣ=b#L?w^'ؿYf @@d)XOV7\ s<q7QOג,2ʱ V8('Kyg+_۽:^r.LG.XGb N+^WCx!a~ތaAA%Qo~̧{"ZGeR9߂`齲os"Pl`tabl}ƬG1!G{'+0腞,:U'5KśȎ(hYw"2!xV@͖Ev "%'F$4hBvJ.*Pe_0ٕcIH^j/a⼈&^^_8OY61Ɉς x~FGZ៧Zx~m^JM'nOʞs\q7j8ӳSAO1YX|dԾ+6#+BdΕۻeY k\lx7+i}Fzow_ju:=k^O{"v-!#d&fa|V Dm6̨N!@K&b;}A:h萉[,.vmHҊ}H8ʖLPHhf8r]CH2|1u]m^,9n:nM|&8oÅ#ܿ{“q0=> QVOHz KC6 PD!F"繇EBg!εl#5`rfc'8Xe!H_#,W}Zg=S@~$ @$! ЬC#@==OtXIeCԹ9sݖX򈁺x$^B6JnPFq\FTy ֜7Ax7= _rC) fgc`2"03ߪi[F`˸tA6$$>kS!xsï}kaYYq!.B! ȂEnYf!k+';bq-c&>(I艽Jy,*1ǟx;Sۆ_կ~ooM }5/$ä# h"#6}E5i9`~71u%?#O2pvlB4Z^74$P]fX5VZgF(d@t~״WǸ&yG6 !2ŋ4Xqh<99 |Uk,_uҪJ=G`r8sQgm[eZ b X f~˚<ԓlJdM֢NE.x;Zˆ 1G={Ai BWqR_^ c)xpCxK~ghLQ!›27|߾ e wY:̥֑o&@dnDf)/fI{D!h6! ^-^8R*T#;DxDC.˵sm7<H[u;} Oh1m쓟9 E;YxOщCg"R #D!D $ۉ wN}&%k6$~Kx:pa:0m$LVqunR9nc,x#MDYKXHG>yđ-g`ODXr wcrr qAV^zu $yԑ]U y+\}r˭/ FmB(ˌ>aby%CD1ӅrLvjgDW @hRWyѫK-j&;u N5:fou\ulnmbm #i$tVַSb:\ $ k~ͪBDa8 7O!QPG_. l^'|&^LZud$ V7Ɉp-L2wg "b+"|7 s瀘-R{ lc3a,YI,~'a,Jȳ-o$9":=b~YguH *6VމMJ`Xb> 7~maCH+i?q/v$!In tzS! o~ \;GiE~E" d<$zkef',mltO<b~a?(dv ا]zxw/r/F?l{@Y'lo ![m.ZhcweQŷ~G#NqN'g\TH8cғf!͐(}׼l rzQQ'ru9泲wrLێ+/()4vh{:_}yrb6$"W@(tDBfD77[{xV G;|O;QfdnicnDCoK6G?˹uJ9D\'.2y]ez~,eGq5!LH䘉7|VRrWBa]kp|B~Qf8|2F&$FP|rdK"#_F rzrICp3-B) 2.`</{SQς8&7Uۭ"PUh| ۯvuH {Pߗn%Q"=HNf ukŹ}ADCڠ ?*Epv-hUmc#!".*ӂ;cY !/:-<좃˚+'BϜgɮy`M +$鈍i+s&LNN6˾#^{܃X'@^=# =- 'F+ OQؐiì='n!B > oyx`|-v©\Vj5JOoXe,h",:87i\?%Ugh3OWU'G49YL5hd5:"J B!F OϫXfU&2/ķyH"@l_,OOtG= oy>k4qh '"\@RD"> p0pC/rظ?gB%kL#3}zqdͦJVv5<:N3HE cy2Om=+g鑝ֹ2K` ^FKB>w_w}s{"B|D@9Uuų17@۴lWu>[lU[{Wg>K<mYmwb=S[qE3!Cݏ"}Qcla woZl6|N"9R':Yz!`Dm^疨B,qeuv^+ЁC.φոo{Z1/!m/>׼ר#׹ xBB":$#@<p"! Mn!׶>p-pr%jDCPKdݿ| 2!$KO_`Q#aR'hDfS/F q";1}*D ު YyN yI>l" "Ba |%ؐgSӞTYa@Y< tIywC#-r@Swuhķ2B:D+͜[6ן5w#H~?6w" kK:$(4Gе2:bno<+j1O@羉Lڣ.;^cVj;apos\Ir@~u-Ɍ_j4&Ϲ'n=:ίۖdYufmF! :2#gw,}!| _hx־dRWO@\gq/H9{vK@S,s}'Ѳ{Cb;c&`靀ic VL{3Ѧܞ&'hC3hMґ:9R Թۈ&isviur0l h숡 $ԁ1<SeќkYCt "_+'[=ܫϠφZ\iA&}pb{g1V^=0{U^mlL͝L=BB96dtPޭ5)<~V1wyWX:̒*qpR|梯cu#wŵK9bwQr==ܣŋE:6e$KmCt!Z#{Ͽ[y#Շ<[Ohv,"b{'Zk?Plx]J0@"Op3mkdy=D`E wR,8Y-ګPd#Q2IK>^r9 _#`8f"T@)-%χ3RN\~{>tHn#$b\`4"8;e4CqY559Ӯs@>YkP'"xHmUz 9%:F2l^x%'+IP1 Un1!Rbz$w 1^1qۯ0|O( o(ґ[9[y< d#c'~NǝH'w0`u7Z!<z-8f"q>D{ s/oeAnfX=$6Vnt=][WgBuW/NL=dxP}##ܹK1WyxU,\DžYN{ >#Cx?ljD!5#)cm|8ӕYaӶ:e`$Vt|%b\ꉸ g: LL,HV,n!ɀ5FBMRK.& }-F6!\@DD:?!)ZP$gu?N>FD9eMfC~eXKA=HNE.sɅ[~\̈́˽ÆG`8?-[ڟ WIeu*zO DyJXi|fq]Cc*%ڎ֑$ wfnֱĪZpĕ! kǺy #"N?I|6)|1 d5M{Wr+cH&WDq:%=Ȅ=,BTX3Gv}_8ϓ9>f@^N\ U`!Ey'gyF<'KF7+;=+=0!\qQ3.FNn7  ӀYLq}/G^VxfK`awHkkfg'(ȇu%$݋VOndW0Cݼ 2ܣEC~O# 6 Z8fhBJ, Nrt$M_e7mECzklkBc /!ٷbdxLzY+WN7IHe_`,.[3Jb-^~=T$c\3?tL;FT^M?lC~;V`eІPFAFN=1 <ȣ+GS\O5 "δNd%ȿ#_ky>Jv^aKJǛjֆKKroSS7L-I8'aр!lkBq* ի_$GPY|9m@ qR^> 7 ЁC :aAzޅ߽]/ϧ6I>lBAvm˿s rR<3=GpFƫs*V_onJ^(b~~/:y!n~!pXw_]^quUWX)##}%Љm7,'ɶW/_y-gb[q@||^{v$04@hPB7(S_9r)zn4xƒp<~,:D>0 \Q9MMq "@,}ǔQn k]ƺ7re~MwzbWR !L\‚CpH(f}MC  <a=۴ySCv%pHp%×ނlvi2"@Oy~o\Yzn^Xh)-챜S[ˀ@Yu\;ү9n,;}hM,2Y'>:uI"xEaH+O0͆lA,4-_pyu_M<§dFr$L4+Q(kcj|ӟnp\9anaE&PǪL.1Y{'\g$e" ILP ~Yss@/⺺%@*g@1m2h Jݱoɺ{\}yF>򈭧#z"t,\<4j=V&!:lE`kP.wA4!Bw;n> .@ĸOEG{>Ofxs2_LH<#:PytXwpl\]6! !j.IqO N0e2pdU醉rvigB!}8B@ނ\ϼA0g$ E@2){6^ɢ}Hd೹7/`lm v f״;W`r?B}њX"dO!lb QW~K>.By XtPO iČ?!iC28@S\Do>xT=;\X͖;Ȗ4?[7]/ᡬWrʕNŏOL?aFzA^#6ӄ&4OΝGn$g3I n qsy~s -96Dy;hn}2;f)1Z7{ㅽ ٖD[?GFδscᇙ3DC 6iYyf>[aH/ 40y1EIDAT 8'^Issz";v)ر7^ ]V)8Lf7g&龫2f6naqӋ3w4On]oD Cmgr [E\v %%y#Hs꿝 <wxswYNE~?O.ɾhck"s;m.@8}! o8'pu}vG` g zq腠϶UQ~[$,H~.CvW]uhG0ʅ+fr2@48H@ p{u^u@lcٞ8T[A[gtw& >l&''ߓ/ayX 桞6d#' hxyy yڰ$ IĄy-yqxWT]ˎD; Uˏ@wGD^b'"y/"?n$"E nOr$a'iN5eF߿2<ޗ!şM`xB/yy)9mj4~,΋3/6Cgו  ۆ l}_/<]pT&ѧ[fRҥ !~?!r^_^/,U΄@=35vl[z! 9!X+917th,H0P2pl "$S0H2ugJ"r˛?i(i;WC6x#^tE>G^X_q>yʏࡠFͯBx"}g0+ߖ|65+gMDy[f[C?Fu[@!P@!P@!P@!P@!P@!P@!P@!P@!P@!P@!P@!P@!P@!P@!P@!P@!P@!P@!P@!P@!P@!P@!P@!P@!P@!P@!P@!P@!P@!P@!P@!P@!P@!P@!P@!P@!P@!P@!P@!P@!P@!P@!P@!P@!P@!P@!P@!P@!P@!P@!P@!P@!P@!P@!P@!P@!P@!P@!P@!P@!P@!P@!P@!P@!P@!P@!P@!P@!P@!P@!P@!P@!P@!P@!P@!P@!P@!P?k @>jIIENDB`ic09xډPNG  IHDRx$iCCPICC Profile8UoT>oR? XGůUS[IJ*$:7鶪O{7@Hkk?<kktq݋m6nƶد-mR;`zv x#=\% oYRڱ#&?>ҹЪn_;j;$}*}+(}'}/LtY"$].9⦅%{_a݊]hk5'SN{<_ t jM{-4%TńtY۟R6#v\喊x:'HO3^&0::m,L%3:qVE t]~Iv6Wٯ) |ʸ2]G4(6w‹$"AEv m[D;Vh[}چN|3HS:KtxU'D;77;_"e?Yqxl+@IDATxْG ¾7fL V&;bz^GsWhL ]F#8dwsþH|,ib"2GDFf0 @` 0 @` 0 @` 0 @` 0 @` 0 @` 0 @` 0 @` 0 @` 0 @` 0 @` 0 @` 0 @` 0 @` 0 @` 0 @` 0 @` 0 @` 0 @` 0 @` 0 @` 0 @` 0 @` 0 @` 0 @` 0 @` 0 @` 0 @` 0 @` 0 @` 0 @` 0 @` 0 @` 0 @` 0 @` 0 @` 0 @` 0 @` 0 @` 0 @` 0 @` 0 @` 0 @` 0 @` 0 @` 0 @` 0 @` 0 @` 0 @` 0 @` 0 @` 0 @` 0 @` 0 @` 0 @` 0 @` 0 @` 0 @` 0 @` 0 @` 0 @` 0 @` 0 @` 0 @` 0 @` 0 @` 0 @` 0 #8;rO諦Os : /b^Rβs^5}Ǒrp @EG`Ep7B򏙱1?VN?i qpyI;4"NmU9?>O秐eF`x6>v B!4gS ,yUS; #0nz^߇v?88 ~w99QS};yڎ zIiU.҃>G9$GWehLXy^9=~t=qpX_pGɷt.]~TR6HN\?ydn5:<~ݻw߿ƍoRUR G<#jrv<iq :Х/^ٵï/2(~!6Fk3lzFnx'mZe*ˊ)]*8 #pԏq ^Q!5=9vVE98 l'q;r7N~'N}{ooo+[ۋ )n$lnnd'{zׯ w濻yyBɟ|>Vvconoo9^8qwA}$f ޽{?~(=VI-K)Z[@EtV鞖jDhl` 0x:C|z]qded=|3(www;v=i3~ƶot^Yc})c641#]\&:l>};9ybT8JD88q3S>k5 f_'}B7ް N\GTǚO ;SOG29}E3[dzֶ6%os/iŬz΀_mz俞zZ0^ pqun00 at:kAS^@;#a DknVuϒS;3 3MKzZ9 |w/Xp<᩸S&D8)r)[[9xUsyؾέϟY :h#ЭcOYV qEs6;ﮯ;amcs`kcӽq7z_Oz=]͌kCsW~ww/nwH;&[W 2:XY>i4_4@E`1#pԵe(!S<̕pSۛg66NIŅۋC?Ԍfq~S@H ̠,Nwqd8ߧIO̞;8̖.*O&N'?gϞ"3S\>=*Lv8nhh :Xxoͤ׃x;:4x'^D'7'EOiS3jRmp;n2~,tuOvzv!}zFF6;~x؃{y)75Mo\6𝥷Kztl~GׂGjڇy'8qDsyd<@x23*_o ^pqg}VeZ._s8S>v*M3o7@qK#zr*}mi@?Lċqboljfx9tS~*}kY_:` 4ۙrA JA{8˭4^b![TW$;wΝ:v}ۉ|:x29{zxtP|-ұUҕY:S6uOg#=gD쥸[)jk׮~ۿn^ 6sNF}I;?|s1xmO]qa\mW#\i>{vֻqP9gvٹx8P!ojf)vi{[1~ì}ʼ <6kqqt^>l^o;Yߎ,#ǙtJ<lKs>fyҠ[Ro;\^hԃʇg؅a$[I7ʾ7&Y]'nͤ7<;B{D<+9tNU &BwqT_c^ 8M<~^ 7ݕ̂q.ge{÷lxaWqg~ b9ôd 0<5imTPwmm&Q~09p{;?L"h#dF6|) OTy-ހ=v?vu#zĽzٿ̓AnJt qX:\aV9,f?ܴ#vʕ<>3f,ގ@JO%1}>Fk}vG$qx:6 hr@6 `2g@vg%(rfoJb lo7݌w2@^󸡁üHa^H_gՠnN(vx "0}!k)oU@d3@Q^YtofI|X>噼̈́q((&+y);q8:[_` ?efUΞՁ֭fiAOHqKG="G=y%^.r~e;K~)]+la Xe)\H'4t-Yᢼ}P@Bv3=RJ-aN&)ۏܛ9[k@V?nLz#ݽu7f|S{Cn@jȱlGזs%e@kL:@G` ^S+Fm}P 6'ȩ/f1 V%rhlS|+6C2-G89ɚg)A@Uyɼ'o^u?N%A@9vI7̶2GXB`@wۓ Wr/_'ZBˉEFw/m#_rfr 'Ǔ|ưX(9 j&ocYL9¿zOy |he2?8+4n <# ưErh9g]rk_:{v^N}B_QE}ߦ9z6(|>z`v]o7s~7881'yfٿgyÌY;Y,D\ӗ >{\38⑴{ONï{dl^rIml >y#//7}9q~DmKx+.wڨlT*%ܱ'WF;Mܵ,Yz#C,p¦eK-Oek"ļ4)|b BXq> Ǎ\rW\9d"}y:õs#=ȣ;q&(̾x }vHcZ?sc:]-?{̙yVB 111ۜKӘݹ'lh8Gfw8̷tϰAMxt$fb^ToNy}c[y;;kXg4('c]-gyʑql\+MoޓBhcܙ`7fޅnYrrL29 ]rVᡍ|+ikFtU:w7 ? 0΍G(&&NDC.,_{pzV7v=`sdog3{nkO%!"u<d2@zmv v/rͪFv #0?9x$X5Nw?GqfZ]GCQci<_L>vwfeӾr6FC_H#_9C\}ˠf [f}Q>s-rٰF?/~*OtM~'Q^1{:-4|lԳa).Ӿ6Ł.oOзԇ[;ü)gVhţ+a3D镆d x- E 9Vx=>Os{ɓ!zL >WDE;|eiY04Y^9/$s'_gݼe<8+gi^`9YNd9)u֦8Ksd^z# 8܌J9?93 }9(hnrJ%ny 961eI֏%9^fwKzr|,dd-矼/)| zjBCGi[mzQ0g8sG)?imf[˺ Dn_,d`>8yn3_ʭQu>Ux+oTVsݛ=8u"{{7`tVJjf q?' ~'f.0t@Xl.F~ #0/)zdoɨ7w}o\t8f/8U+3+#1^9 @|_z<-7Fuxp=.ǡ xjl;rx4@SmӆZ-BﴘE졋aUat6@튷O`b-Lq/LХ}96;M5{n9tӥ1&\vNeu@?p[ӓ>zp!Gy|).̀`\wNk0%:!OhԼa܇Ɠ'ASNI_{V̤szMdJ20o3E;k61x1Ë,ܘQ\t_?ۣyggϓ"J8c1ޡo^96 TCaE4f'Z!Wr޴{5s4~p9-39(QRj,WRNd̉ÛD&sfh_:T%~A/`H 9Úg]9F޹{gA ίH7L}+m]`m׮UW.|s6   eo_2ҋ  8?V [ν > 9 4j88ZNR\W/"*G#z%0O.'S9n#j]v]>ҁ1Jvx1lm WQ8/y#=Q  Xt-[#rqhN+Mݫ~~Yp84̈́rqO~.N7#~q5\_:T|RxVREy%p=ZԥYMs`gڮ&8zLdHZBCY"d$p;:F[jgsLք@|ҮU+YmJ#gOrN6? R/>r 2o'Mdl~S##S?܂:vԷAGP(u}lPJYmW@"0W`eؖ7+\=[ou%gqn򘕚ՙ  ~Z9ԛ'߁Yp,iwsXb ML*EN::ƫSt<L" 4Uo zHS;MG+~XKfA4񚩫s;#38SthS1n/Owڼã[VkMb=,r褉o {]nREKw3 8,NXlJ}Tw3(xgB˲|Q]0GѮÁOtؿ,=ύQ2q}J?mzP`/2q+(Gb9i8,ł65i>7_[2^g%`FVֿ-O ɊQ{Rub)36Mg^mcp4聀G >{wbqvtpҾǗ%堬gu>+!x6yCsxDIŬ?9,r_$$h$%8ޮ η:ߋi?yoYWy"9n T#+%wR1ü|?1 zvʌ/l /]Sz/L[Y{Ћ-©cSY;.r23vnpvJG훏2܎9<8n*o_-ǡ_λmK('a#w?S>y)tJMqTMvпg;r Wie0_OE?\ ^EѿC: }l&`TH>m8KbEށqEMO#O[Nb伜ϲݴ)+O~)޷_NP/E8?(dOKš/ ۫~ay#y;޵,'fq]*FnMq9U$X93yTVm3IN^)*?Gpr-0>Қwn׾lu/\Tfl[T T&LmLeXzQ*}h\ kߘ(+bҡS>V@c~ mٸ5}o |e-bȒo*@ Ȅz"b:5\MJvt߳)KrfV|b7vi  g3X:Wz!u_B_4/ENǔ#7 KOA*' p>6]m2E kkGrPQMs9?<9ZNi^Ur Exp^‰OνUfD;/cS dE!,x'RO[u,ܵ8i93}"AιF7eaЇ\-t~}{# Ylv+L8*'LOۅ|s9^zUeFdӭbi* Wh;An,|]xu9#_2L; S͛^7_򫯾 b|g;]2+aR]:.@#0җa#$_5FM ?Kbl/@N{-ir>'wX35d9m&rscD}ho\ 쏮fS-DH~.M3SW[}%_:ʼn&Xryn%,[bd{9p~(-T@GcyWڃkwnvܺyk[kqtkk\Ouέ22y㟴'WT+m¢Y=#)<Ԭ_}Fc² u]}>S@9Ӻ֢KHqM/&Z:<|w^_'g0mOj鞖.F( cg4&ϠQtҎo,/V1Lc&xn/Ks 1uOZ"K2ɕ1JrӠq4K:2ZPN6J#B f0Çztw1Ý1$>ɤ :V/o+Bhr>ћgY_e'1qiԏURߡL>nwٝ;o3|׮]c3{sՀ6 _INzW}kr `?1x6>I3}q\V(N7r qmP΃ъa$_0\m+␕azPof)U.t&uQVCVA:M'+x-s"98mk!rԫ~>aONq/V$:NNbд ?۝!W%xi A^m2\nu {&UF8L-tl;O XhrE H zyaazO2lΞ;wlw`5÷Bn/h:& iy}]#<7A2U㳪{+lAz:x2F|쬷1f≒d[n܉!/_j/^s~kS\`k 8!ǜ\})X^j!}Z@}׬t>7W2+.r>Z|"$}B@VcyEN@g<►OS%~މ^;n/K=SA@gi/04 ,sе!\ucS*R^xh_(頮Mה@.+~D?<3x&ofUp/'|^o%𴕁>.Ē,KE7+U; ~cz HOwS(gf.g:Yh F?B%2d6oKY}{CKڗq)iEޒy zQ3o~h:O6fd/~-'Lrٌh# eȤ=XiOCtKYq- EI\ 3ˣD!,VEȜsZO`*]&oꆚqd{V[$#k|/Uocd?c`/5V=i]N-kWw;arec8Gp hˡű3:4"a[+ r29~VЗQnsZ[v90#m6 6ă8{8S>{k׿@`ňND#K} G0FxeІ6W^=K}6c<{XbV.5+Lg{˿(g3fvN;.cmMvL]g!uDFYG}v{ev9t4w}um鰔uF OC$3R7Na>DW}$ lLulUtETC9."$f;L^T6 L 2>O \PNw$H;kAZv@` :`,`t 6(AaXX K8Bϻ/h^,t Dsfy/ Y-Cce1auQgk0YCjvKYT9 %]^ֆļ|pN++q|v'zi2g&Y#کAma_<l:o;`8]9I7|ѫ.sNˍf\j?P;:ttyG::v j!(f޴10 xM#Ô뚊U\nIlek5uڃMv'<Ξ=UН ߷/??{Z;>{\x{J^ 躾8گۿۦq?`䰍  ƨKyŴ8yȣn1c͊=#hY }*2F,-Fv3x~xOarJ8^>|l=ԣWzGF^̮(9awcnWSǏ>:۪ZN>;gU ^bAocز9m=g9w<'~rrjumtt𴁕u9{#]ϗg> p RT8tXt%?޹mr`ʢIj_0@M;sӰ=Φ{/SfRVٷ*R@W7/^<x<5D'px*xWK?5bOYo#zò̆0K<|6dcG ;Xs]8qh`ѕry,9NkppPihʼnS;A62#{as"|txbf/3oAY>u9:-Ԇ,2ѻOKiw6#r 2|i]] +@B_˟5N9ǵ|*/!c3u "|UԾ[qq}@#]-[XQ}w;C\ٱUJt˧{P#J-WВ>"n{w>38{ }*迸ȞJ1*^'u:ۏ{־ ci1rW\O\?Y>Еwb8H jxȰ3\r>xS^!mAq"f ZeewY j9t6oiu|ؖ< T&-vTD Tk9Xr<=v9_tW.o~4Oio*=~lħeP> .U橼"cul diɜK!ɰsmi/kwѪjK|b1uR.<s9cDϩF[0=HgA6HKY:_.uK`=DV0k>@;mC[c{**ݎ?sg~?!Y1p'B2 A%籀7oxsHc8/|Δ$U~T @Ym\G,*‚xy*Ckg?gwv?o^8|ERnc̕I} `Yxvs;{eE'5nU7>{l]bFL(QkS_N篜2SU8x-xlM_ u3b^NLj\y'Wb?L|/S 3֓qU01:v#<]y+R#Ѷz} dbώ[rXjUnC7L]߱[xn64;"7hqy}:3)Y Tweڠ/?>괇Wpq2omq!ؓr9Or-r-`&}6&tIN1ZkIox4]ܒyTr~+XpM᳑kg/K.qis#oZ `): l+E0fGN9JKW>}_^FE` ^Sbm9:xPKo@k@IDAT ο`29ldD>\硇WАyj'4[N9S}9%sGeG(^: =>^9xUyfFo7 r> O62^l &'" NG(+zor}y…?şRכfEi 𬍃^}H:K` q G-+RHe?\f6L{1NeɈyV V V w{nRNpp?x;D>k )G O6Ȱ3TH]]Y>:{%txlU7N|l9^Z8L6D=@Z ;5 J>8V81A?-gjۘJ瘖RFMV7VvM"k&:8G69<8FKs.3uMMty:IGwҖ#2ۨZ(wME+Y,ͦG' ^U"K9kߙL\2 Gh WӵkO?G,Yx׏~(8?V8~;5 X!:c8-/ىIQ/㿝(=.q7n?~.x1ѦzO7Rye+X٦u9 dimJ\XrG~2%9 i#Eǜv%'YM˵S{:Of3v@J֦7Q.̪(Ӧe *9m6v<=pn@+7AOlդ|X[Y8XpN+ B l`B']h³ }׳]}eV<~퍜?=,29m NZ΋ڊn~W>KVk)x=r^o'`w pSHNſ_*u +WV?8cj|Kqgw~g-[8t2dOJq,HeEbɑ!&.׶ڤrRNCSedk: 62蓓덈x,Jw2,Y\~Nh^nЁNf{fxMn: {^te&`^Osh+p6hhsqKd7 U HkX\}mA|iyzzP\NǺi)NR8z+r4BpBWe"k_?}뛌i_# @dGZj0RZ2ce3.&7#۝\賓cNVnH yM `naPW6wR/0,2e }xV-~xgIt--ʩꋕV,f8Q3ϲƓK_"4ɼ[}~kYX^X:ās0w)A s=g4RrLuClW̌@MیnړWW=YŰ#^^-CcF>A'ֵ_L.:jWOؠ>y*G+WelYzqh'עlMVY7{'O][*7 >]e ,Ƌ6ӆvK);>#&qI;|sg}7_{'p>íüaod )_쿎j_` cjqw:7oycoyw%96?N; ?Vl|hˠ&*gXJit6gE@v`6̙}{8 |Ihsis C؁\);ȷsgcȳ<\pWG$/7(շx5m [ip1xK*I@q~y~}渂09zyq9Mꋳ^z~X }pkOO1*O^RA|F:+z'ˏ~Э{o{l {{ݤoߋ!4K_EYߙa*#Ű l=:L;4 KxK;2ě^ TN%Ϩa9/c~<3h@ޖ?.<٣%Y;6~Q5v7_r)sr 5-=ToӗՉy ѣ1An׼.oͣ.VZA ]Go[F/8lE))Ќ6ósϹU$zOznp5H.+gG2Ec8f}22xp+1J:yrz;/cn\Ϯ?mo׿ŒҗebT?W 1xNhhIZ4)3d'cPɏ?Lf7S}7csv!y}-c8X#2V^X:3>N/mcij/@z1qtiYdmw[pk,j``o~?jAxvrщ\yt7gen^un~A`D~~/Y8HeE͙~l}~?=NIx9ѲG}>;e˴c-Gޏ^urjoj䋙TPXލ&U&2bm˲YGY^?t uʥffLΊf*B-ͣy6.h'-9k7 MM~z4v bw0h~ t8}DT z-sc6wܡIkFl{C*vh2h)ʷGYغ4oœ2l;Uֳq|s#`n\{n{YbR{:a^sMG^Jk3 ~[qwԭ3}nكPZ7 X:~&/Mdrwo7.Y@h%8'SE2'ґWW^'(bԿP u:~00~gLC~ G1N?mx28c59#Nս|Ƭ>ێ }# / *6ehjPj$;s%,sgV}dVksx.rn޸vZЖLz]qKխy|2Ulՙ{?ݗ)/SN߇m8$n]&E/yAZ:4z:ExBTx4MAwiwhy[ǹuMC^])vݧG7?fnI_ 3@-6Agkċ=dr|9%Dtdr!uL+ i0hON\5³gCTrkk'O| 74((<Wc/G3+'t?Zѿ!#b[1^J<ót2D5g6X _r)aOM;~ORKl,1Gm`2 '+'a?d\]Q.ɛҒ[_дOKђV4-iLV+5p 017>} #_'!gkYm_rE䁻(r ώrt2D7_;͊9ι㖃ds} A~9 M|8}!{`q,:yA;kBӀ+K^z|ك`uΩ"K2H8싱^˥]?vA'ȏuqr_\ԋA@_ Tppy9<-,2rM7!ky6!s1F}l7rڑzm/2NeF1LFkRACK8' 6?XF zFC{i͍|;vL~:3֖V^ճkU.Hɀ4dt.J未C;uYe=984|+/}2ㅮχi]룼nAO&H2>nZrM:E}]89ђk쭛-TٽNXsN $u~&?<&~52J^xN,62!]ӹ7n7LTۺt]`p/sqy2qۿ˭OD'W?ʳxxզ X)G7 'hi?XXE#uўaL>"#}F(4FJ-؀k˜ŐT6!/שDaQ)&,/,<,c{h2p9KF-2l-oGK[oƾuWCغp c='#R݇hsS|COjg<ѳeЦ:#mlWcXYbA8wdny.y~:GZi]˪+γt=Z:d]F>7R})/} ]-b idi|] /B`Xպ. r^^oxKvcbOb&}`Ty?/ϣI1@v%}%汋]x1E0y!t;)Z2[~ȡLzRx6/xtҺ5Rtxh#AoyV^ Do [{BI6-my^];e y;mK}}v׆ J=W{r[ft-u #G!p&S '`_o®V7D/y!~Cl;%gJ#ASe=*Jakgh1F!ƳeӞ%Kf,7V}6ѭBA= H ,"̟U]?1xANL̊*G^?NSTosNO%^̒;,b0|觌bFl:h cԴ1Z5Z!CZe⯜a|SZ#AcEb ,iļ<d+ՆhcPgr*w^.坢)KdϏF: Fzp0!(-1?ndd*B;XQ]re7D~#=o3GAYXoF Uڕa ڨ1Sз7KD0G>ȡ-^'R6]YC6v);me̦8^:hGY.Z9.#'YqM,R|K[y?O "C{gc}<58cF}*z]q_b=o ow;_ ܌L_f_N2y]Gx,` kS],F2|޿Wb~o@\I/y 'a2e,cpdhmcШcV&3N!azuͻ T@q%:DȉۆvRǫW}G4fΗ軼䟖0弄ykh,ZƣŞ|W:%O' sqxN]*Ҧ->$ӎ0K91:?Kt$CחkBtL\! 896xiK2_|BaKlKoOǮoz}:ɯ/{r `T'X-#^;xJP{ mސχֳyqQ>mdIxx7 dڞU`5S7,~ۧy`ԕV#qzGu=Kt+|)1,q8?K/81I0){bI2hW(qSuc14a͌v-y˰MQ^b}޿rri,@vͧP2(8ySVYj^/{O4m]=LNZ Gϒ}|hRvrODc~1`F;VX3hloE-śc,wuwNΟ fΥ/@M =4:CAxHɂO|rVub_q]u]Z`0a_x49:2=R2€!"eDv{ w=֝ޓdx/LA9;H_^v.t'Zv& .D;*g> B_HFS 0?O9qz<*4t^<<dgw}vw[[21굟Nk'q=\zxeH7I;GN>7[v9R'[~)~xmYՠ!&fu7KtvYaNl>+)j&CtKW_7LJvL}ڭ {@zs }xepp{I؊gdSY pKqOWGAIeIG<㞈Gb!0/x;AL;sO?{w>~#qc?C"2> 6H < ȨzC]G6P3Pݖ?P3nh]:!xW |:K?CȌ-d7P?Q4rd*(9rʴyemN菵t^:oxJTL |c!}#z} tikG<a'&81ǹQF߮CR599{}_^)FKX8vY`7^h*uMLӮcu/Sl%FQ|H[o@w5>9Neu?jaw)3DӶl$E\yP6މ;9jV Ŏl[W_}[EW]RaH/)EC` ~3ChsQ8}<ҏ~16*#P qh6J1:¨m۫{ˌ}9m\b105r@]fc2!Xffd3@tf`/z ߦ78xW>w8tN+֒>oaE T,9 r >Y;I</vޓ&X8^Ń^GXL-_NYE bɖsB=@{k+8*sG_[UU9>Oƭ:bt?> .|AqT5>\0ГnҾ4࡟ 2 熎%CRpչ2A=xWk <-~f+A磌G 9t'6xwr*7_|gW+.L OT]Іt 1iF`A y~f8l&+ǽ7b < g\81<ۀ0k cŠ3F0Ӫ]O'1bLgl=Z#Ά@F}drA\PmPr匘i;+].m]Lɣ.:vppPN/rɹ9xBm\LKŋ'eZ:.Tq~):Y%uǻ;wF?sө<C׎n_4GbЂ^}ֳp5qɈtN[_@1Y02V Z_z JjZ}sT1߇(|A]׮^+~r ċx}>wpތ n P ŖxE\dUL]J/n?$y?F0+R",@Ym~&?+u/T~+qs |.tw~jӏc(ψ.ݝzyIxTq$dXp C`J!SÃlHvD//]FS}i+xO$G)6RUjp1÷drԟRܥď~P΍-M c Kp#m:iT$ѯ|NƤS5;RD#vf0-Y{Ϳe2 &ǔHT98#^9ѕWjg07v-g ?wxwݗ2dk-W%#&/ pUY킯AZ;.tShϢ /}Gf8uo^iNWqW!#&=9CzG7 :X%Gh/\ȽN _Aw Zӌ߾Z2Q:םk9qW92^ ܻF9G428/ïVBoGap96`Swg%Ws*JWj\3qa ,.J֖`_X 6xMٮ>>c[>11"f?oo tZQ4# hɃVOv$2.5vUGgper34K| EO?DoWsr_ۊ(d?:Q1|a{';]TЏ.< Q8smdrUnmt;9rkK:uH N4bq~#6J/d,K? Jv]gԟnM'n5ge!~br` TpƇU^]OEw fg~E+?-Xq3ck[/_át: \ruTo*&,xb p88LwG4);(e;¡& ~41Pvt`wᄅſOHw)Z48`2C7xt h S7wxfa {CZ95p城}6ܧǓzA^{* R|׏k4o>Q??}pN@PЀ.ypƶ)q=nͼBl!链 +Uf#{suUV&Y鯛W/m,d\ldN|IE k梍Cn=\$\%>s0?!}<:3d? ?+8,j=c1N c7lNc2 |RF#= hlNaڿկpRB1/|ɇr`,Z63.sT^é'B]-̢pW<ow  ˢ)4ѭFmH90ySQ77emozv}8kLmÇW|9g+xi,/Qソ} MۧO}t {ڦO Y*Dn[,"L N]m-(p`DKi@ הwZh)gOlbqGrm[^3xp2kCOTV4/{[^rbG1d!`T^n;{ʥ;OBk zq,yye0x򐱺\^Ǐ]La`у8 >T |[V_ \*¯_g_?j 7|fKq꿓(}>vQrC OAky]_F/r쭛|^W8_w &Ԝ8{9 8:$Nfqu xҦ|z?_y=pZp+}8 X89M}65mW_#vVT8ṚOW|䘫,*…Gl,=uxwgbF[ w|>_u[Phtݺ!ED}K:'@׹0Lho6n*3}WSmNq|%×LWBYi=z޶I70{⚼1ކwN7#ɃC<2?Y鍞9 )y˟:z235 M0i[k@'}1|~ۅ۵</y&WTD\7dBӽx:4pk4_|ś1׳8%kYoݼqFlc71D_+{si3!~7/޽{/_=G)ZL8:]NA8"'k)NNE?|G^PpW</ dȀ/>"aea}lGxOKGw}gvwdN+wVƫ2CtR8 K_=om'l+?|D.u}WnӷcYr? ztM?h~*^G,?JwEn_6`8blo>Df@? \HoN x&z2V ݌ M)L n%F~D6!OֳSSSQ'xAz&$ߺ}94@٩wxN~.mbq1r/Y {& z\?xp(m->:fu:~bW=U4f1 K""G mwʹ`[]Suv9-<=ux=}6Wߟ'ǜ q G6T`YWNtNjnQn7S-+z{zN&~5w:p12 T^YnN 'թ'Ǥf1ʂ<1cStN3zx0VūN 64mƭ kolP;q-}aqSGl%`C(y cṀ[q'y ;,ѿ$N;~Sr2gׯ _( Zw>u/Ņ'EkW-C#H2|vcΓ8. =WuE^qT8}th8&;x縻 O:?BfWMp_,-t':/м6yr~ZPzɉŷ(wmD0?״K tYdy־_aMKt| ڌkP><<\]X>dY O ^\x־3=8s1'3uζmzr`NWOqyC!?wyE> 1H^lYlmWh7}Q}KNuBEmקDw y; 09Uz=/+L!~A>vN,%MI~cz˧x~8:|BDoTL|X8>c bT/9_z>'WH7 f:#i 7;T8f_m +.W`u0~|;D@guvN_ MsZCӨSûջ<{oWV./0!W"+H?xS咮uGVGO]G:lt F}\aӆK!82EWWxpx1,͉nt!6/mՅzrbn<⵼x;=0~|!z n:NK`ϧ<ʋ` J0C9q3[3GK~XWS'#\pbpg2wḴ+;?t{~UЭo 8@"a*PT_6fa{zk['{w]oogBy-Z_T 9C:9t}cO3ȁU^1W^9-8YW~bVC8%W1"P]|x.}ׅqqt'z6 &`6@w6u=xwl¸VWf܍s;<]Zs%j CҎΏSn|x[#|bCr]D{JG y`ʫh«]0Nxi(_Mvf>d+m RD#}:}uX޻Ilx pnCxT=X|v;h>C[fOŒa@g՝$G@IDATtZw@x,@9$ܞ[ _+!'WH[7߼+_z{oqW }[ de+u67vT ^ENmc@8#E-{J:q!:Wk: >BS{hqw(V[և+c }%COEk< udh$fJ q*=K hd\l!jo^tۼ> Gqv_>ТFr 蔷ªe mlR[eJ۶U귋 g$YzmFcUz/lrays =P躲&><omF1x8Egsu>s?}/]tGi73&)FDBm$_6R;y% &:ݓb|/?BF ӯsbGwr&̆ZP--hsF,}W&ǀX`Vޕ x_gLӢ~h4:Bt!ZESg9W)cPaޯ 'YX#EiTO|sOs%NM;mC9m tE'9y2|>`<)R|[8vM츐oz;~mz-?3Z -A_~lT=SVWw*YXcaW)|Ui0anEF7~ge|4ȉwYS5|CȖT@ .s5y͘xS̅՟?Сڹpʓ~mKuwMxL݉Ǽeid84sග9 F[dڵܕWw#*8b=@ef~N_hWvfqmJxG< ah f M+|R?h*ryoyʰx&OCk#_xiϦr`d·fn:+wdN:>.\tIQQ;WC1FN8}N 8;S}>M m>Mm>:Ib,gBh܍nja9c Ճ8n8VfԢcfsCc1/6jЮYh+xRrtѺ: ԢOx.s5^Iq@OkTWh}iћ]Wh/nx,6ů^ԧcP~{5T{.H,[I8!U6E8WqUNfr_x:1ƫj]3ްUJݰ# d OduBPݫFlj[姌w ӆ/xVlfn9eKG9 i"ݡ[6V|ZgѠG7vocA^az&U@N? 'q}&`lOQgOQOAeUK8ޗb6~i|쇱 }O ϼ'j1Yx -o{E319X7Bw,΢pifw8"o1Sǟ.< 6) =- Q ;"8aN  ݭtJwK3m{bk#O:a= 9 0+C}syѣ Qt-nh+ʟ=b'L`'x~?ܧ./NKoq<\Iz +iǩia\=W{>PO@[hY k*`3A?y}&ux-t? Wk7uƼ2ҫ<8Ӟ} ^tG+o%W|)_Nekoh <홀cg\O`r׳/^!=p#ENN:q2y/ v9boQ=8ΒCr4u*[85' Oݨ1pQv#8exDi«gSfqw>v磭Jk*gd;+/^?,= @Kz| ` O|W芞byȿ߆IbQ߉v߷dyd0>*sgXm=4ۮYg&{yd@m%?8WR p,1 m>O¤l@%X?j{/dav8Hvpֈ9aGϼ{đt!Wh 6,0vGAA[Db.4Е kxʅ|[š]Y>Bĩ>f$3474oi/5ƶ'@E/t+_5.=S_}> |q(4~vStVFG-9{@(<1:Ȏ}M>Gx\ce ~SkF@ yn %ŒO?0//y5mdtNۂV9A]=raTb߼cɜ>ƃ}pgwXPuݸ⟫lbѣzq1_d<8_ ϒ[#IdOHRY5pU1'jB$螋 _~Em0ɉI}d'e蜓~^a*05X)|m3Ї3P9mʜ)q~ \97N=yMh+r.f>E=Bv^Bi#0ꄡ O âV_ӏ;ȅ7D{7 mD:k7ϓmt]ݢ!] 'ֲ|6WӿxOzq+~"2|u:ECfA~xO0gmΓI@*|T@r~`O>* U!G stSY/gϟ?s -/\Flr%VNqvGgDY4p3xu7j{կ~7 8osql_b~^| ZAg[lc,ٚg;-0\*b8*nOr +rphznNAPS8rԡ8Z>b#%tZmSFOouɃՆ!GW\3ϐpToCeT,<mСsم yl6%/x @Nws/ 'omr*Š2ThI_*β'8͙h&{*R+^|\ ,^peHP(?#ItgPT`E_ W@Ѩ_Ț`V'O^%s+(oC~t:4DW~ w:_\hIE8VNbnQ2+qHWiJǟ)@e..,@/=gM{x_ÅV /0n'1^̓i)m 3+73WȧJmՏ 4sI~#\TXئxm)|t( ^O()\ F:xH?FOOt&I7Q@p/C[ tScu>lR: x !`u9Ib7ɞ;gm)6՜[gO\'^' xoo۾;r7<0uv{4uuWf a'WUS \ ZVÁQ >.ur8EW6b7)! .<t2/~|N}Хc8|}9o߁׿1u+N /<ocDFCO \ ^a:V6e8)Girxwθ8|eES?>"|pՏ,Yԭr |ڔ©'7^V}JӱɎoŤ3ע>Q]ѷ+{7] 鋏G66#]tx)|0m녀 S% 9|M| 3NIt ??9qdl_Kn leOa Op6Vâ$މ9~o߉^dbџY/3Nq5(Xz![śa*3:JYk㐪asW_RbB#:`uR?'-Z导|."7}=,EF}lmn/NU~ Dzz-nK?Nq)'DB8/ˇj/(ORM&੼KW}wJWAj>Y~72wyOg#`g~ *HN':Ɨ0c6RcOa0< EkRNLlpSy/IKgaS8y¤rOmڷ2mmdahS聿,.SE 6H :ŋ^jںAGN&Ջ>zl31oA^?:z.ء*mW6#xēW?~Fc~2Lj}y_E8-B:G25[e9lYK) r㭼oY{#Ct=ߥȸ1WoPN!X|2z3:*۠ͅˇ)@ʎ6 p>g0'yF<|w]po-Itd$7G5l<=|:M>_D3+1DEt0*#'cl邜u >wqU;J1rm<|5:1y')gYۨ\=^=ٺiS=`ӎMΧ0mClչviU]iSޥ{CS8|(kKNn:.edvkN>i#0Nz_tc ݂< ІG2 _و / 뭑ey_[ ڎ7edOva~hȱl64+SʇT/ gF>: +ntd3׺^aյ){tgSQ^;}+smR8H裫lX=noc?21'Cl~/E#_6ʫ|λ~`rpY 08: o8ϗLߊt' EΖ0c ;g>uTπc1ELH^QBc?خ8o|;}}WP9|K:Wm.xM dVȨ-M|@+:4oM [>7';G3zY+B}Ճ# E)ڻܕJ|BK >ӯW̮~ͽ`uGS2@FwV|Ճ6 Bi:߈.V:%/a Ń^iT?|K?5GPބXS8VwkT~Ɍuƶm'j]|m˗=0sxsB7o^uGYLJiO`M_ ؙ6֗Eݿvڽ~ό'n&)ɿ 5܆ 7:gg˳f2[5Nva3dϷÊ{a;F++8i}9:A^P\| DAkȬy1OS1<9m6En88z4 xD.MHh}8dFH:u">o0"E4N]|GG.|P'*/8lcdB=8pLqx y?~G>pn~U5->:M<=ʂ9FŁ־(U6m |qN9] fľ{t~[q~?tMՉġzow6#G6oz8J(n)#\m"ݾpO\y`fɐKQpQp>//ˡ9_Ey{ݺ~&oh3(pɞ8_RYT>C///fB873ľCT'~Pjcl aKpo:Q0kpqCe Y٩gӡNP9`;UR02\ߺ']X},Vf8e.<>V'h~5}O,FE q`~@} NWWW*o6_Y/[_ȴk(lex*GૼN!|"ofTp\o.n_ʋ;:j;BVǫ[Dxmmkk]SQ,S\2xW@|n0I0=A~ӺO)<< ~)WLvuꤼKkXю8e :%8ʦN`ݹu8T|oo{&vs /jҳ m~iN<6LZֻ[qoV^+>;73/0q|Az/͕f!za:cL5*8:8r9^p0q\YdG}##\: <8Ҕ_ⅳK˅{?WGOkt~/zt>ρ ud#lF\큯|M]%=:]n*Se'^NiWn2!Ah#`/}9ljYTOuYՃ/13o6pس>tlА7"J-|f6z`Uʩ˭<|;D9??!L]f)kZ-6e#_ضhO~QZY^՟v|ڷб'ytg|[զ`w|?ϝahyy&^>~ C6}؍@f7NsПLDK/'~)[of9nuƕ1MQ(2Ocz Sc&F.RF({?O_c֟2C}a8aU#A\>ʭSW^xGSX5{ 2ωju::Íbbr#߾kB[>W 3f U+|ONg'9 RtGpws3-nϼ4T״cX=8GGyt5ɑ1YR*G]2f|'cT<УrhOFr*7x7YͶϿWG}'Yo{Jg_xPY Ӿ7|`*|qÁVul?se{WMM>aX|3CPE> N~] 3wn߹y[1/?uGW <9h u7|lZ^7{ыķ\u0Lv m LRx Ă(ku[p}KWxŊcPXeF1sfP>GQgP6>x_@_C i 28~ I^<\A!>k`ҋДn%=G۱0Wݸ!u'CP|Fu>O ~|fu#x ̏?8w~qKY|s"(DA]:`V_sUqpe .{+4FY2^o|O@[{ܼ68Ut}\ =qi-$Fu]w}GOMH0 +NՇyȏHA+Qx};:bl`|Oz08.ܽ{-/{|^~/>$tJ) >̝ M7w5V|&|,u_rʛW\R H@vsuCG1#3xWZx SWCa5"u5Z91q=QTkw0 Ő[MҩS'9[/#^/Wiho*#i([д[x}|A`8$muNhU{s7o<m?t\>8[ݥ 'e?̥O@ \=\ʳx&`s/}Bm嵮 :K-Běٜd>CՇqK^DܢC5O+Cy߱I{1Fstuqnso2ֆ:6 OȤ=KȬ>)Lej3 k ˟_4ha ڇMxܔ/UtBCģohcW7rb eGWVO!2&ƻ$='}s{80Y9W}8n*/hGS /^Dt6Z€;QI7hHu)yb?qtӱNOymNϱ{LM^ǃ1FqCM@ݚE}X EnMlrnWث :odEI`։KW_vK׮_{!9EUL9$, a4u;سp/0|R8t ѾvTGPccCe}9WW^@5z1lKd}i,ҫi/L~1u~>t.O|lpA_c[{1r;<񽡍50K&sH^lF—WlRS)=?ERa^u1(>ǂl#z?7H~hX |nx= Wxsnn-NB95#e4x.5vKt=0|OC_^յ,mY>rV8@˟Q8o7-߅S_ZtuaX\\~io@9iϳN!SCuTzp|ƀy'I6}<8M@R UW~Wn\-fN-.b ʈ|S)߭:Up|mD":0wUbm?NVZ'&}|Oc3ugc/a^]~lpm†ظ1*d˷\_ڀ'#U'o+0hqKw[6)W/ v ?G6 ; y(X<8fE +|>~_qƽ}k/Lan t`Rsb3¦߽lG51iOjb!cV*I1&澬8[0^GXQP'ŏU' #mڷ8 iœWk<o}ly\+#=w-|s\J_`WZ:ѡ آhƩp{IS_z)V?ߧnߍGk|\7 PgnW>sChd~lbvn{>\b͹]asz֩楋 ~_AvN _4}3|h/|amDE:SWx<0g,ct%?82G$Q4>Eͣ߀wm%~t}h6t: ,~n y ' S6 N6<TݹL@^Qd`*lO]jRA28h4 PnTǠ⯎n05(m舥Xe[׾oMO> ڧ}΍`*kucs=a%cÁRG? :`Fs(q<e܍/D¦By]A.XHWCy_?_[yŸh~u6 t\% '6u~hl& RC>5zs=pjܒ/iۦ<^{ŞL+-/dXխ46uq൭Ks E<,ͪBo 8=/קGǵi?u0<3^JZ&12I%Yվ4+Zze񷐨curu&p MuO_1P\W7 p[tHCQCq vcK2ֈ `KW]*}MҠkX°Fiyxw> h:jcR^],ByV5z$4fF+4W&mn> 㦷6GsOal7\eQ`ps}˳rȻ6c+|86{B\ѯQt_h^XjSh5,DoÇҕ >nUko"\~OK+OS0I~+ EdMs:b^85q˒營@IDATϰ7y4R Lь\έ˙wׂ~G{ `-86ͷ|1DPۼ ȕ\|.;◲IlvW3+di$9#X#g_ݛ')m?\fQZOgÓt5b'~Gi/>rq凃4*'W.[L{,x'ޔ] u%x^}''ʥT7mP#Ìat /e _x;n YCN+ (4]4_/֩+.ci(O]3ޡ'XS_71{E_@z5 xeE%hcT:4ӑ[cplD3Us y+G6kl ^7um__'$Iv5fx>y:z0bU&i||giw[cKW<&7L~ƀ 5j;gIc[蔋5bh v;sE'9XCx #ʫo4Oi+EyuV =W{Nu{5kgOw{/+W|ȴLg6-xzn LL̓-ݻȼ-sm7L(jOΜ>gâ! N'ν䜰[YЛ`a:-Ϧ䱴)w2i*2 ;#zc~暷:S%e;O>+0 `|:Nmק|˅}Z/,\9= O)[m"N 9L; % OƳ }m?|akQxeţ~ a>䳐k7A1M<ܳ> of{R~>JEzPqЁ>=][vɠ:濕|' q>Flyϙ} 6e§H0xA%h .c-ŒD^(Ed_p+ߠmg8h?&rS`S~plu _U]T ߓ+,Fނ\ l~V<(/F<~*4<3nS"r:-NiI_qBQ~^ i+ܠGiLi<;.N`a4F>{Q?d \{gx6-_6tnH9^c;%㞷k)W'Wha>`N!V[/@ʩX xz~ڪ lDm.顱|E8sySy0? Q5g=P qov_Tizyoo>?W˿tKa??qFDYnpnyn&NL;Ϭ3y?V;nsaT /2c6yMxʃ1[W€:jyW >iTɢ}9&˫|Q:Ys_W/Շr X= VڀhkbD}j)7ё~Ԍ~uE4s\]/^\= zeo?0Ս|iFeڿ`OpgukN:Kf=|sz@v~[P'/5LeVGOB.{OmZzr Y~ayouZYko&LM*=c[:rmM7\.6m~zU/_^4OIBq;Uyto9Ut\~̇ұ9a1."Go{ut<&R7A_xwB߯щ8O> 2OdA'x/L<̤L`FW |"$T )AYp2q4:&?`˛vah򍎮Jy3,eQPͶ}KA]ofH1̾ U*+Kʃ~dm嵿|:'7pQa2Rg _~k᥏oЈ)CoGS7g / F[_2*W}Н62?NF[[@4#a ` Ret?ce2*Xu.RJ:iO.mephd&OG%),j3kgy_4k+ce4SϮ}*x~~/Ek|> p ϥ;}/zW/hiBnoKOg+|7Gs&T* *^${)n&ߕLw Iy%,ڳ=ebU'm)ͤfL5N0klq3: ?tF>hh,]2u` X,NFxC"@ц>2fџ2 E૬x/W,|W':*CG1 = 媐›E7cN:Vgp]xmkrNyz=x,TrWgs.WGO޹o:xՕ؁h٠5'Ӯ <+pa/~m)>iW.i? *ʍתU?mM6L欹f|~Â_WoqU~A-XB' cQ@_V/s qC̹ 2Q4*մj OF2?u-?ݏm8xyOrە5[< _A2w2۰pWAliT;<<ӑ ѵCS|Mjd z6`y*K).Gޝ,[v]uBYWJI-tm=^</ !Z pږRy73s;es|Δg<Xkvm`wjwLDVq%!{wWӺJ:vl/KK߁s%_eSvL-1a }t N]R\)4OLسKl逧MD^k'B̉ ޲v#F6iw\).5??t _)y;{ %@;C_ og71 Dov ܙHhwiB}PڒdrX)eg!H&Է@{?c m _]NwoKFkvA]T2 .!NpYpqG$믮~&A%/㇔W*3k;WDc>j)$DRS =f'l/G$>`:]mf pܙ19Y6WmX[-mv}ƪw9߸Z>sN`ߎ[?IH,1|Ny1c5p8cL^ڇnRxo B& s|$L' Akb]0M2ĐָS&hDx')ilTY0lVsKvз36r\ܝ8=l͂tlIƽQNTVukٱoUp&qlٝ6Y~w@o "泯_綿dR`59<rulqA/u"f5W܂nb|%}GmhJ:D<N6\}X;hg> JgW_x7-2v](Єgc|g[XldjڼpGށ:d+Md>@JSh+6޲&qy6]{?&[7yS,:O25Vs+U>0͵Γ'#⩍VYY=nRelWyђ 3pmw(}W[-c:9z:sHe]1X9ۏΕy3/%2_>;| C KmmL3 k^4o^~ȺQx|'O3߃I%j%G }.Gy҈ G ?LpL04-iBliѦl/L7~b9wS#UK]2a; ` FerkYtMP0t784$}2|/YLNx,?6\eo*ʡkbхBxtK-=k?6>-VIff2]q\+Xc&ѡ}J=<)Gةo#@SK}i}syy^!.>YBC1+sU髐Ihd@{xSߡ-&І<"_?FϸGV'4WC-pad > naj7x;zw:8SSۧ9ss)+vlmM0b/ڏqp$wepКS/3-jwm d~uyS d.? Fp|L,-kNO :eG6L/4''W&,6GH!'3 .\EݿwW7]FI&&6N[HY `@&)Z4AـĶ}ԻHY" .Oku46LS/U0xI~PxޕCgi|a9dXn|g3Ǔ_)&p-h٦ fd\:Wim!y,vG!?RG QtR`,6`~O>ߜp t$.٫w&/![N __ q=~oq tcI%8f/u~mw9d!eޣ'Y邉UL'^߇ES[ Ӎ9Bzh%覆[t&J2IKMLcG0Tl_,R7gБJ4G'U?AX/^_ml.|sU nox3gWY)G诎/:+]أ'^д˵>plF w y.w/w6WXdNN69$Mfq+]~%?wi_#KJk{:}ƿ) eJ~ 0r&; Il¦2;Xy$6 $c܋C9YI3?؟ǟsH uNvt~9*M#3NT6sU݇w?HIOKI{>):?] Xu4jlvYvBO8~yPJLyZ\8uqi;u-^4g6E~u~0]מ&_ ^Cp<0z''N_[FC?Z˹>Ckʆ0C6y8/AOO|.?:s>g+Meq˯4}/kK ;n^d׾o\FВw:iOv,{Nu*RHd2*Æ:<+qN*O.]d \ %yVn땣 mɆ?mnna{_i*ksTm0zqy6c<29Tg~iF.d7=w7|̃ľu5x󅋭yQUua}n>Nl~~U| uޱ׾c;pb3g!o[{'Gw*'M&hH^&{ `d >czJtdl&lAqK$2YMtp }M*1+~e3) _obxW΋TʍʡF`#DyU>pQKdV\k NY>G_ົ3](m",/jii|^QՎgAwƖhU s[ hko3SN^&_~vP1#׌k}=bTVGWi~_e_.,/T\ޞӷ+>>1G렃+ Щ 7 bw[ڬ<h `$upr킎|H"#6Ӗ" 'ާYC? _<0aģY6Ν{Ƙ(Pr S;ҽ&?)]?Ɇ9>]m<6t]{ЭF  .S'݉Qx wBPp?$Yl>2 S΢hV87'lWSV^>g eajR?lacߌo؜YxF[fՖW_ݖ.p,vU%./ўm妝ڬu!Wʆ:hG=o.X?7C7}D3$Sp+Glܿ5<;b`JyV_^.pk2WtwÔR N5> Li[el8r>Oer鬃֖_M;g~fwJu}$:ӏ M+7865n} qw鬃 ݹ+ȗÀ>~GYwIV F|y_q_x}H{Hx?xsXo |ʽOy/vabK7&~rZEY[Rv$m,Jt p:2޿~{Dք3dfmcK)37kxcO9צ F6ym.0hGwxSr}Г_%J &%8tJ I V@€+ldyapKΙTuhBF'ѫ6&u ޞKkµ_&/:h'mЬ\e(Y4bʋ+IS:Jmr!NzZ4[^t)d{.xxX;D^ &Z@dG%8hOFO%GiVf9FuS_ǷG>:8|^vE lOSҭ1CW8]h&үƗrМclm_uriӹî`C]ܥ]|19#~d~kX};hyWo'Bnd+BCMk0}}o`y0ݽ#"dݔџ߫." h9Kpz/Q͏DA"7 {@`r&-K{nPv"vDbRt"u)Ip.nm27g:neܕ0PbJ>: M8+oH6o3L/Qi !îGۥ'W?k7X~_Gةm273~<$ÞsZќ( wރo´ډP?}ʙO6n[>插[ݱ!GmSƛʈ7:9c訃i-esi7L,Vq|:,ҞA=_|- _k&+ӝ gy/<[3Ǔ 䞻StE/7<$oyId1zFpQ2ҕxҷP:%)śOrn> ^ w9n{4XL%(wOOϜ+uLhMy_ :P4l6*Ƶ1n31L Sq?6`͡WIh@돱”Lc+=:Oc{rׯ2lKh3ZfaJ Zrvu.yfe13n wʥwɏ)cc@ W g k >>+(/ě2al@J'JONF}ڧf 5J+w#EGstx_a^zJ nK}xQx.]mmywʝFy.tԶ1[94mW:'Q[[T)[]M%BoP3ݤʬl,]G'2Qwpw_\,'e:͂WD'!|Ll?jц_vb_?ۺysxj_l{tW`FhғlN*%J>l6.{>7|h:.|ӱ^VpFԄ|]WyW8=@ڀlTցZFļ;EV+p.lx@,Y;wtL?ņ`[oa-9LGccWYA-@(Nvky$%;k j+֎l` r?GmའCE'/qa#dk^?xճus_h/ ,x5Wқuljg1;߼1 2_?4/ G's׿Cp|fJ5PIm>'GٟO~?H0f+y \{$ft06.KnE yL OѷnV d1ydvRv"xU}~Sʩ,Ne"s'i` SjoNFnsWxxbիr GT=|~NFG!])@ΧtqNK4TlF*:[ŭv8fw㜭%`Е(t_їʏhhWٔ105-: ?J\ͅZږ9&&̀|ه~xӟ4I/LcYNO`zIxϽW0+Ӧ/КY@CD+,LNA~`+uE W`w ><49LW6ڥkݳm$5/2y zHEkK`CUӽ|,O02<8[h5iY/9Ap. ?Ix*<:IdWG gr1^9g;6dK4ʬo6Iެ7UZm섳pcwc.*;|xH;}0=jUx`x|VX9젙x;vB+;7)Z[t8"my0XWѐksj[t ukz9L kvi&_qЮ<1Ӄu#Sօ&{ٻEd޻vYfšV}`P_? :xw޹Ay{PWFs=d/տ=#KGNdYƧ{FOTWͼUJǻ9dyWxK ~)\foE\8KqHGJ+Ki8%‘:, $K7"w[⩣%莆10]>4dß[~Ęo\j˻5f/+vSVv83r̉]\e>m 0,ȿӹ:SO[ۓ~7 2䫍mh0#68Ɇvmӻx]P.:+H-ysfP_ '<ql^d Oߨ__sQ,baA t_]~Y Lmebj|.>Q;yF \>`@]m׬ՉrG^M:+l _g.mߤwcRa\I͋?tG՝LD_֖tY?XIB8q;=k(]wIM0)=[!tY, 3*2JwX^v2TkVca6yˉ]~/+WZ|n-ʚ)ݺ6&|P= 'ѯQ.V>4ڙ-:IZyy[`Nygn~gw_H9㭧˜՘CjF~P@zqO}3*ڥ,?I<0N^2I%Hpe:@@@g&Բ/>+'#Lf勞TM|e> (;@_Yg LWtE ah?c_?<]^;ŝ.,0lǦ=!HxHpJ߸1=xѧYv:{}rN|ۼa Ӽw"<3g?+mTLi69՝c O*#p{ 郜h4޲yc4LZ']W v@IDATJ0ʿsQ=^S/ OsF-_ᯍV)柸feK = X= u%?~c||NBkzbN]e4//<{>ҋ~lr+JR6E ^jy% J:w~Po'gd6|u&1.=%z]-Ȝ4 pA cBi ~f2 vԫUANT: o^ . =2@Ahv st Ƞއf![xO—r,JNh3V oyW矹b8~*L, i7~*.k?/},AMl[`L;{|2!+Mteo2dsgS <bĢ+7>J&y|nԕ6~xxqrW{[:E6ܣY`OOl[ ;SE&<˗-Г%8s.6d7rCmr)7zᓙtS98h߃&u|jH/6 >KJ7edwY>?덡XoeO;p JW\^>pKkl8i.ԯI4'3LL]K+gB lA<}3:l yqɼU] > L&m}ܶv_RJ5qvuR臎N?8Ȗvۛ~;tz{zd/qгIЛ+8]|١gCRdy+>h ݦϪ}|:w*O~A[2|6ޛ[_Mzn]Ά憘n[YCVlSEGs%#Kڕs|RǮ6>\s%/R+.?qAgc >=,Y\Nd1NYz+Ƌ }/[ÊgN+#d26Zb0*ou!]Q: RfRöWc@u݅ c$nx|WBA{B\,GpC5JEw_NN8Ruw{,+`lNinD) >sȮ0,:&0s%ł GOE&5U&z蒕1\8#FrDXzNwedsbY9|q-aujwPGL-2MVcc2~?z/7N{h]=L?C16Zxe=_iol8K|sXyi]IcogCjʭdڲ2gHcv&3MAss`!:ƀy&=/y2_6s_EiUf.>sl^>tKËC~g/iӥm}S0< mOt-N GxWJXs]Ax2+˗Q呂u V2 -cKcoͼ6g8ʰe~/Y{pxೋg>]1~G?OQ*:ix`yoK|7W% 3~EP Z3k$k`вb2xicM¯='c'[gk.RgʓOAM=cѝndVG2IJtȴ?Ol-=rظsu³~~6 o *=}x -~dhY;pK>}m֞ݒCy@+ePx kwS㓗RGmHވ~ѧS;,Pf& ,''ICP`(7>z8Osu}R`Dmԓ"ut~W MoS`ʓn6 L rwX8{c{j8haG8y8tGl8ld!mم|>Xv=ӿ M_2_2MC7p@Ck~)f<7rLrQR],>z.L77_YՋ#㊅o恬>6sWhooj&hF0GFn SߒA9فu03^m™6xSGʯ.lJi/KՇ =z.xEzG@ï/:]rhdܽ!4G/}+!?¡.ZpM:S4&O)DdEK{?dkzBۑY^ Yh2κ~l )BNդ>O7/W^Ck f6ko( G [K}JH3m/- 5u%`mns@[Pۀg1q ^I,@s#tQ2:rzYtd|襾6kCReXii2OLF4Ygg҇ޒ\6<#:6.[p"`ي}I 4v}E/Óit]AC]"ԱG;ذ<=^-p~vuhr[ѵea+W˓| < |U'bޢ\K9t%ؖaOgie]&̎?2?+9w*&W9s<-Nr;pLXGߚxFSJ$u*}2{󉃷n ZC,dZotx֚#>pEÁ7r:\7kUӱ'5֔El9c/#+K? f V^k^4yH9 Τ4N/^ڀh-hBnm[mv|p^'Mu0ּx1e&L #!U3бpzfk̤&+*6oE³ƒm~74_„J kq0+*)FȌ.xH{7A ;1|Y-Gc䳩=v,d[Z>pxwӕ|$`qc<:晇7XV3t?m6ДF{Ͷr''NkBݺ[]NmV]* ޶/E:Z*cn|47g|.6U=Ay+s]/|ןgAΫ\׃S4|++7jQF.^qT` |4]1үP@G;32~rɥ-|ۉn7]84]7ҁO+o.+[ҫ|޽wŏp')}́s >0^O9\8s֝_wL]C7l |T p%{]]-<zy&ƅ!(w+c2uIEqȂnpW<]Łmpw<͝P3g&a@2&!o>4!|DAaۇ} V6ۋ\]]IuhI`k!o\σx^Goѿ~ `+ՙK#ܣJ}-q8ܐ/~]˦qOO >ǙO護ߺwͼy_gό39 /.&]_\f3#(0_ ΤԶ2d˝kSpDֵס—hՅmyW>{]J'ei\~%tYLM7F߼!)jFٝUkQ&ɧܦc\Oǂ^Yᗷ>&;>_y,(ثQ'Gzi]Y:J =fᳵp gIVemEߑ+tb|*+:n֟3"6;{vi[6L~teyf+vXI< hMg}X[ڥ 9FApZ/0X5ӡK$`~hzO暸mFVxr7yV颇婼nWg2y6]䙇9+hR-|\z~Jp,yz BK^}_ :zF&[pF緒^ 7z0s9Vd:+؍uf1? mƮA9|N&ғs yG*Hsu|ί IY; n6id{FEhY:wl=lѕL*0ҩ}"ޝG-6_ɍ{x٬k^614ᱹqûzp5{:;ks'k{~j>z袯<1`KyXhc|H l: ]'^yLu+]9~ rA印}{(w~ӗP&@jzp! #R2 \S};|7N&_H,b,@e ,fY:AOJ&EL r^#OAL~x;-cPJ lqu%`Kxӹw,w9=wx>z3cg[$/fV.նd|(8AL89KM0|$;E/Kxavx0tͫƦpvyʭk'ӓg^9GTy6`Ӫ_݅/̭]5|vm8 yw\ |(:X_{*qγq84; bHLQt5꠮oi+C]#ǎmp9 dDnx7^v}#._!B}hCN.?a>9s? :\: ~7*>y^^EwLcWW|/7Nx}䗟 ӭL>8{p6 &Ca|Ra;L@P"69:L ʴ`z/[f=- n,di&OIhYH}8cC)A.4`717u6{Bl&dM=RѓXu,TY$~2?㤃>#`cٻ,2gə1<346GG=laIgl ^,˻rV94蟗kiX'NDmj ~My2mk3:Kz({8D8-`o|؁32-=wtҖmTS  )1߾@>LX*~mVYx8wKY. cxbR}>AO_䘯H~Z鸛7~!+nAQ+lUye/ov恿˿Eqw$Łq`j&A,<'yYX/ϋ}CZDBCONd^WK 'b|QH#).szm*66NݜԞdQN8I `HoCǹܶrY$ *btH5h,)KS+\hGـLJR)Mn 0;aw9.»G6}. gldY#y8ʜۘ9+ JkY38',Ix*r,LM#hGv~^rUՍUۯ+)e}xO6} ?klh̦ ŢOľ1U`5WN|Owz4Y4\q߫nKW94f]io2Wl$C|;;K}{@ǿb͓lF>}±͸8}ye|bL6jY}ry%M`cste ϗN+O$ϦTͯpheԫkԛ#~%Y')\ d OrV6}i0t,cMikx8! WCx|?׽CoaɬT_7<8E ']Wfyx? 5EiLgD^7CVp߉eɿ)^f|n;ZبT?1 $;}Ivfys5zM,P|( )X,qŵ7_<6HC&Gq94aw| _WXD;Dcx&glTmGG1 說X+gLp~I.l3;| > >AC٠\WU<m\ÉV-viAZW‡Q>GHMT\hSl[>X&Ћ}e )S="SLW An낌^nyLCGyqLW4#/@LŃu望8Fw"oa@oʌi:7̂}7Zf҇C6bIa[~lven)ɥ u=kA.{Q‡lWE]=g,:~}G3=[sA~|&6ukQ } /8FOEM_ ^ -=1%lk^ԩwUS_vbf2RW6??s9DV^XW;rhy\];*N‚Cei<8'7 og)<&>;':hd`pq@V`@49g򞚿ۿ¤Z0_?`7Ʈ7'lcu`XN8Xa K|  <`H $n72֕{ gp57\>o_<4G-'|;՚>лlsq`SLn,ٛW2w{UNpΣvVOe58yI<~U #~tZxa?~vo׏C3:rKRX}[Y/>ˆcc$uqVcNr˫6oWFtwS)ʓ,~{ǯroldט;Ew2~͵uc-:vǟ|S^^SpJOO>3)9x7m͡fКy(#;9~|.^4)2G7~̫G$9>/q?N@zK 79#QgnZn=_gh6(-iJ=7=ME>8-KG\MppEf=S9sl]0(Φ-}39o>fWG4N]:|~.npgV[eee6NE_ÎGjU[5ɊzմMe#3V_%xx|{³H~{|4WM,ZÅ;/'>M^W;۹/OtK߸Z|k>Jór-I{xtW.wV4wqPYM֬.\~S§:*7ed֠ʫ WyW_Q/>Nu/r|~2~//Pyk᫠-|^'n5 8q{s+-[Nhyh&`捀P <@|0Ƶ3=`IЉ`xPJbMTi?;ëiSë]ζi3KsNa[^_ll#2m]yu j|=F..ngCQhW:' ^M}VyjɾduJ{d\L:nI6mwӷUeeCl&=9d}~x~u`Jcdy2pק'eB% su>TG$Q9cSA4jl JƴON֍z M}/VcG^h4z8<ش{y]uvY7W7Cľ^JG[*^ŸxZ6NFvVG|k'>9bf7r ǟd<~q.a?3sɷ|490˥ ~nE[gy'xփ)P;Fx0< كO8OjފSi=: W,`@ڲܱKcP`J\igK򙀮TM|Ξل,K)1JW+ 1IZ"!Ϸ2k6)1ɕ$>4F7 =~`aC.wkS ?7:D=ޱR,Wt #6w[S{MS'}Vl@o:K}<蠔K67=|#CObF?R|CoduIdg[%MS[o/ʿt+}u^á{m^;M̿fi+іɯb}<;l3&O(V΍=w{7Ɇ(K$zkW奬VF.URȻ왘N 6bH6/yO~2~/s18|.=u<0«g[|ϛ~;N_u$oA?Aͩr$^7uBvݾ@K^58$}J:xN3c /tGlY~C/WlsG)|KײA'+>[ }?)m0]^^yEjm9ʛLih>dvu!X|eWRq?p'ڹ꯬dTFm݀=IeV;/{ ćCjY}I;&C7ptT'/[̎QJ`ƿ7T7쎷Ӂd3aXi/Eu'GԘP6VWwo@aRCK]4uCW3vΫyw/ݽ9_zf BIƭ!pQY+o3~/rCKyC,oNPn_߾(#R6Tw*0:ьq_7ox1Ó?#oq"w:ീ &4fL0זZNc¶I[n 24_&dWY.ENz? ɢv} We3ODf>Sy,>rl_M~ux\?#+ϥNeDϰ';gi|%Y߅-wKr3~=ݢx%VgGTl ǎٌ9#Rq#|`$pѲtgcv%K\W5&! yI^GTSCeUb=v6Cvw%p<ӷO6XEp~6p/S\*Œ^t+#y/3gN NشvI\>t38Kh/%kn-n@O:X24躭><){s5meK0}H.Տ>bO*xEݳNW.j/?\^1_ܓmҠx~b}N2Zf8%x.|.|N^!Hu`[¿_|Mz^rI2ftN0*4@ gv 9rlo㛌`+RֽUV>0]9[\Z/p~eMƴe7v#'yCwo/E-Ɇ|mu86igS%W|b8L `Jm.ą:zۻ-᝭Eq'Ks֦ԯLp.Q }Vy3_&5{6pr>6MSMWrm9rXϋƒLᑻqiI}+XuGf(ieQ$~V/{T>/6Ƶה{Pb.츴ז1$˛[nM_W9?+wqVB_"<>䗍5-%X|m1uQ_9x{xzI[η>tU3W/{ }ݟ}}'nMJύi+?uzNtmq#oQI~}BY6~ޱ/yА^6a`F﵍mϑ&w4K|[8ngK䒒 1ptN IK}tI]d];(a/|/ގC~ Iǒߘ~9U/Yv3>2`Kϸٜ٠7t ߙВ3{-:Ob-~J8+N'9^J}vΎo6nupM0g=['<ٔ@{-S୧)}訫h꿯Bu5ȼqjv0<Toc'w 6H =\8؞AG`IwH\e[d,+ >s2O%S, 0=/#kr$S=˰%G~rҋm_ܡ.upM{!۝&;mY3#^;os[)nϬkh9l+e}Ɵ2+C]]C:ܿ$.>ԃ{G+?46vcıGXf4`_Mpr3_N~=y_\`7q7w!``ȥɸ,P2jS)"/ Z-}\dJ)^#o/ #\̙o! &&>c t\WxBM8\ݗvaNW0/|K7DXOY獯o1\B^ǝ~h6)3`"xmp"E"jCɎ@.qWsPZ-m> VF?{TfneYk=sċ4Л$3M=3RVЋ:{zX ) /5^|1tƽ$ b;kN~N_%/(]FOɴ|./>e3W;c?YE LP/y_~;NuqiN/&t霪$?Pl^yOGqb@IDAT.lA 8khH>:r-ɥJ?0%8ˮ__>sz wi+!F o>ۯKxgW[Yi[ԤL/_vK6 Jmp,zJ髝ȯ䕒RƓ.g>Hlmp(>l=tUʮqK^Ͼ~h'3~|";|DֻG?zDnLEL]E+ vcn5NVog|\x 6\3Bs=&g";Mh&3t_ׁ<|`lQiaWS"|LF}Go ^Y_&3 zJh$9_O~zͫݷ]wum^ Ve;%ͦ&`WFw>D kE?W4Nv8D_Ύ`EFyd69 dž`x[|4wqK曝{M_˨ _ҮSir'.1hrtYt3-oL LЯfr|q)hZ ݽŋyջ )X{{zf3^_]_(@e~>xۂ-h-BMx{ʤ^v}erC>p&pv-}bM9$lbj^v/[pvȆ5dWfs d_DdY1ꯏپ.p>G^ 0]%i=⃓2I.)=ƿ//Gc,)w_sr1xɒqBIݒ>,n~!Y_^H`]![C@&,pLXtK=3@m8&}ɍVvO0M. %}EIYV֣^.:ݫE{~7q(W7^INBʃ-m,;zeG|Ğ6!2g|!MIJ?T“յ᠇יn2)L#{U=4|Nn,:/k]لMfwah÷t7'01 }t[1s0x{weqqfQF)v{qqʝ+}È^v?&8tuDnZ?'W;ٌ?;cdKV1a.i&9>%YҎStw/>?vz4'ݘ҈d/=_G6GXKDϙ~TchgS?82O~r GL\;i3Ms9vI^Vw远S 6.hW{L6:@juE(u__#3ɣ_Ϣ7y+ nwl2:utT6I[_٢z,K|A&0Ÿ6~N`xJ.WglD#^hJ ?]z*<'66+p2}iV&1JX8G &smxYSQ7}; 7DזDҽ-Ğwxm#t{MJ}ѭIk+`bٝy=֐Tt>ϾxbFl"U~8}}x#ꋯyvꋎ˻t]?{}ԥ_0y1~?'\0o 5˥m{wȭ$WK>ƣ৯K;dv|~7MqDn)/+οѱSܿG~4^Y7^{bg\S6.`ts2}7)XƇn0,( J *4vBl&eK$hz<{w񜫓^>Nt-%<9=-^tBq% 2H/ 8|MHJki-h#ɬ+~lp>=Hd-:/7=uJ`e&xXлka-}[FGaxmp,w րO>GƦm*W[ݸ_QdD Qg1<}Vݮl.,C ;b*z쌮D/ӯam:;{Ҡ:{d36|/%WZ[Ncf8WLƣY[3[#lY0tX"=Zmݯbn'中3yѸ2O2MC8Gsڣx7^#?3+?0򳗿_;?חB6˩{}:wf+xߏ 娟$~wpLȵ<_2' FBNjHrhRxD2Kgxh+]2dS lLOv5!GY fW<|:C٬ ?؜%rVWv%;^-Lq6[ dW2+/'%KJ`޽_>pó{𳹹R7f[\uK']ugbc|d\&? }҇ qֺ `yҩ|̟:F|TAs.i pve+G;ctKvLvcW4S<]6$[QP2;29'd9gȮp⫣S#9}<`쭾px&Y15"Yًy7o}Ye9쵥d4γL}|qL -,Ywgnx.R2`2J"xAv^eڴ// XO]k1$7u `4VnL)-N2f=tv// /|ޢ}sC>o-~Z+`h%'=O] 7m981u96ȍ釟}*?K\kWg2[#g_}+;(ҝ ?>*֘`FGRkJQ((fI x $>J8[ KySjnq QO(xԧLn:Ѐ/,S"ӓWy)l^qwgCvzq{LNvX`f`O'K`{9mO_X`c+\ow#zv.HԸ __.]aF+$ >ulp%kc|n@YFGVgfkTwa\(;J#c_}}^J~d$1z4gĎ~03JuznWMk]);%}9*֚/YȆ?IZG:骤+u^ /)+dKxdEGQOszɞpЊ~k[24Yz:!m<~zmcdŇS8ܢcz'Hy_O+'1XyY@pJi`pt6EJڙgVa/ R UG[H6`\=xED~f}^4 w I<ػnSƠ>-铢/Lp}$2}6[p|Bj܂jسoqW{iiaQ2+Y, PB2q5jn17N:gl|}ڏ~ W_N7r=eG=d28 8_؊ݲ񯾥=r9O>>'d7k-?~9lgL62zo`sR"}49q8kKӦ ?rŧrl+M%e8,9r蠇6y[.z/7sט|:w ɀg:'6o 9ׇt5Gn6G(?c_E8͸ )v~n5 pҀK3!Q 6и ^6&`dIXn .֗EM"ɈQ]͋ۏv W+L;f+e ai"[Kt"S6Zƃ|>K`7tO~?pDkҮqc>}pdDӸ\ҏ1MT?>"ď;HxW o&u 0]&2qԕxm Hן4̩L 0:WMk߈-g٩V 2xC[gA/^N1pn\1ps,4=,ϡ8`^ƞK.Wu4K|wR_fsɮ%`-&>`œO%#?ƕCaOzW)a|OƳc9IS>J6G1|&?b'6n>coQh<0l.3_o]]%WZ?,w.Y#K/f(X1s\Iv9nɇN98ɀ>ze\)Z^'?hd{ӜQt;mV\%cq'ګ !7>3cؼ?F{/&iPb>4gKcW3/Gl0M3\@wRTF\[zo\J-WLݞCG09%$g mtK`dJ({3:$\FIF-:^a_]{_qgNoned;=<03=*scJd¡4V3l,)odgLBx٢G |ia_{UlG-y֋1t8W5h5f~:Kw8GW[oĜǩ3$N6&n{_"s+Y/kSt+0O\ӳy>bMc͝8/>r5}q 89^A]pl^xh@NFi$Xݪ|6 &\O.}Z?eVZ$x19 vrtK?^[yOxJkGcy_U!E'[Ƴqm (y೹1 Mn`d{Ƌ>9/Op,\Rϖ/[3.Stȕ ƪ?W/eg W<}v~OLShu0섎yxkW ^Zyޜ[^Íg//1[/;oίNC7^V,s&C"7~XzkNA>3Q\><'ya􄫝Ɍ6ɸ^" 7]Oh|F'w5%9@n:^^LKw.:@>xA:$@=24ޏ?Dmy]9NxA;0_c`2~AsfphLdPoYږ@$ EH' dNk'3<᠋g v7y{-` ^+k? _?p*í4xEcCfq }h'8!O#.wm-7ņR/#9_<8m_K=+ų'Gzǟ͜j?~d?0Rd8b/2'?7>&"D>+a#s=ً%`ѐߜ)[v|F=k T~)_?q79s7 }mN6ce^u)W3?Y[6SƷNҁaqF_c'W%N*߮_{͚_7)Cof-^_#_^??_{_k;IۄTLJrAc,JBX=<{ OAn4dxN^Wn < r0N>kuL6dϕ [͘x~<]N[&a8c]E\2'~6L&=G*>oah򤉞~]łɧ>0OnQlVjIg<ꋮvO;\anc) etϱ餿2 Ǹl9RHLRw;yW}cgW<~l`%ragO6pmnL _wuel]Ʒ6x Џ/ F#0R<-08Kl8I ]ԅ,Ʌn8хyn6Xeu$̟S_gFg\&3kоp$J?cI^ɂJ}3w}3JRz Qpk$<6/՗0-H$pِwx& pϿ.酪Ol94i尵6^eCvS;6 [‰&)Y0>2V2~.pm6-'^SFKFƦ3xgFs|,YCGW{]c% W^|ks)h~alam\6+^nqxbļG |8 kbsFU8r/5pV+}ߣSNbds/͠Or!Ժ<.}7%؁Ś6Oli ~pp%l%oWk2ċx([[ ֆ~[Y>`Ó)O GG;uu3qp uI&Z+Щo3KYp PY6V"(GrgQLl N/3NtQCE]mem~w}=J2IykW±`/C^K1dx' O[`ϕAne{K64֜;m_ۇ@pdclh@o}c 9RbSx9>˼fC -ק|\^XN鋦>4"9%2 E8>N>@\I2%q\-xDga>G+a{e'.91Yz~{E~s{󟌷-v#};(=ϔD~o!`ϼƮ+qndxl{b# yzg37;ۯe_ O }6ZI}$J?쫯o>:px:7v ׄ04ڴČX4J_L&+t*/OphQf*3>z٧`̷#`.-Cx{֐;l|;/MޙCe 8؏ O}%*Csc]O0N0qP9N2?.&=%e`α?C5`<4Z /Jb~絩d'Yg??\Cy5(bǬE^7]3V)z_yͦo& ?2;S6`'YX'g''=-:~3'Z##83? 6}wXfo^Yseu O@G"{v`r)9Atos ʑ)τxK=3_ؼjR.hv͏K;Ѧg"Nm~Aӣ$'/z m4 2/=8Wpc߂~NƤ9L2 $gNDKf"EvLR&rǙ2[]8h{2c N_%9[GS?If̸,=sK ^[Ѫǧ>S=XW_?C!~ x+(6Y8gf=B,b60GT;3 .pt&>pJOWj=im%r-߾}t~6޽+\' 9ߢ"$eѠ>67?]{6|Ћ6My;(pVY`4IoPOOd4Ž>՗)h~$7:JIN;l ]vOŢd1ތǧ !@7d/o߬Cc$+RHɉ6L3rEIJقƨ6D0b|o`n}&c}9Y[M o{&2lE_'L-+/ЍSd۝CD+؛,D 9R}ɐK&;Azʎ`/%Ke 2?{u唁,&M|ȟ~NO"bzm-~ʅ~ELd#\o'`oۿ,dYT(Vwgpɓ|d~me8 9#6NgKvlPƔȥ}w.xk[Xqus9[0xƣЙ J[fu0ĔoܲeF^p[Ϧ#fh&K:S6o2}}`W ^_@C۸he9KbMV"1Sʆg';s9516k\vz̿oܱ;Μ3ia_9[+Yi`$}CcOYF?>Y?H/~_s/'1}8m\p:'oR]'nL>6&O^&\)VrÍfY#RRNll&\}=N^h$<Y|vD9{E\D-Zyil7307YWF4-rbnrg;v%O~m ΄iWӋ|:97z2զ;mOo` l~x"|*1+\ȖU̒a9^#_IgS}*?|ၟqYK pݍ`o~\9Бo٭p:t0O؏(}JWWq֛qƛ)_M0#qY0MFq;q׷a.HqƑ#`|Edma,ƃqm҂[C|w'gY&EK6 |jI.xy?;B}!&-zo> ]m`-ف,ɓgwanz'VV'񃲶 |qğLЖM|xÃml(6;-GSY}?'$mO_I;>q%Ә]~ep{+f{1Z|h5f#sEOl/=؋[,|z8d0爵F Ot޼QwepK-V?ٯXYR_bgHgC)G}cӖ~F-\8 DW~w:kICjmNӎƏT;uƸ)4Aūqq388`ڜ1I_@VJnT` Fxc_Zٕ-&'È,C_rMЂerwr:`yG)d[.Z찓"8xW?bM˧,r-jp,Yg_֟g<=/M:ܗn#[X6iwPC+9؇x>cLwǖCf#MRȞ쪭g#[ƯMj@eXg?g{ןt[:;;u bWͣ)hI Ƴo%{EF}`ЀBgR: 18ƔbS)C)wַXj//]w Vɗ|Sx=rtW?W9\}m+Q?&Psׯ޼wq|빅?mAW`4hXp \xe}dl{x[19 ;oS3&@M_r}+R}1 ze0&DBxo냇Ot}.@IDATQ>zgF^g'&W>цZZ-Sl3#1SG)_| //F/"D'ʡѱw~?0l 13c]_! ߾~7߽`?ׁ^FPguޖYC+a.t~oEŸ@ESJ&e>3'r~bC?&Kw9ql<k"S63;U&8x6Af]IKOCTnA?{&-#ݷld-;Qk:]d|S2dU]0,w"NRB6X&_ҵsgF}6?r+7^`ҋEݟ|~S<n\aOR| _ܒ?.+;#u7+Ϋv]mhH^Ռ~9>ni7ܿqP7?O7sKo_f9N\| 9WJe4xy̵㶪i[IW0m&im%hKɛNɥȀ| '}ƾS|E.ec0Mwn=]?xetH`p!n{џ=2~_\y s`(eiO/b~{f+Ƅ2+mOғp':(>ɰX wSw+'8x2|eVCa$c|6UG}҆~E?}N_VG[^Z/ׇƳ2Wt[;*೓zP>sm;.Xj>&V6?91 vRJMƥ;|̡a"]^..S~o&3@??`H}`0/1U6az5?70 K)S98_m@ϩbW^>y|`Hھ< h6)q06{~VvϰJGlJ"'_Rc:\9-Hݨ^LBAǹ'i;dn._LS#*KT斲q-dV‹V|2/Ny껗HCH>vIleM#]K}}1P$/KTC[LdH'g][޼T_:tk/.]bpd!'X;>fq†$-㫼ÙL v]'O[694~hD <~[m dA BS`̋עߕ}|w\v7w#wӜqr\mVm$n?\2|YJl̉g/!Kk鐽bL;d/kPwS<yK;<ݢkMW}4!Ң/!_oȜ-ټz6:/'R'X| d.dG2d4N&)_mcRJN;05 7RֽƆ7 lW=؆]ٍm C>(=l02f?!a6vWlk}u2+I69J7+r6>|=66&i;->W~n=\F9EIes,;zP@Y.I']uJ+>Rnӟoo6+$qoËז択O\~5tՃ3K,h1٢kL@/-ld(6v6dI:&!m#%]QMɘLcdk~e%:ltiޗ &7|ѧ]; &:Z͘Gy7k>R}JYlL~ze6aqel[d&"w%3;UF?GWT,'wp$m,[ G=ѭ~]Y̐]Ch _e `d;pZh>fcɨLN'vi~u8!9  sA> \hlZ.6T/{k[o%KlʶxJIJrhG~s0.v:Ll Щw)QgQ~wCҒ9J&RS,,^+Fes L "?eAtߎA<anL_&(=6O@/ %YϬ/yQ;y&?6%3woow/Z[eJ0Mk~reӖ.頄MSziz__d*ݺ HLxG%%x`|&v@l>|vژC)U/(!iyݚ3̍wC'ח'} _p3Ә6_w^qqy弤&0NQE~ "Ɣpd0ĦQظldA Wr]sfe7G2I %^Aout >%|7?W6)U66Wf`B ]ʖ ^c g{: i 2[v&?[k1Yel㳴^|?4FH`̏7~?L=J3_}`'}`uwOg2. qxm?|3dPkD9^:ӏΜo8j ߅ML0:jo,~o=~:Xωf2GDhi,#OɫW%Њvv6W}F{{dšqWA;<|l\Ru&I}k+WԮ\ 2 dHl[`.[\ u`gŏh)W<>S?9;uV.Ͻ vd>m.^/K6`$)Z-9u8<RDT&_7)Q ysv5mC6o4 ;$YW6c3wS]ņϣ>5KN9={upͫ|>ÌR9twwCw35?O]X呂?q3Dd2޳Mzj_lA]E 6CkstM}U%Ad!,xI]9'JhHƥ1x?ã>rtV؀ks1rӦnWl6/C t맛03y=/xI_٘z9MXyx=rxhbl 4__퓏:>| \cm2ڿ|n\֦KOw.^AEWcX 7_v^P^2NRm0x38.l͉߾+$:CC!z7woT?M?OO@qnS}ycnWr 8fu՟D7ɇ~A& =, lIv2X4?O\W${GbINgަ=soGs'ln ?ꏞ. ~/%M*uթɆ%kjxC_>u` ;(Z`#YQG]\pljz?WNw?ȇl/߽`\4e+I֧\{<<#oʘ.e2g|;ŝ?$񅋟33ؓ}=ZlgH@X KN|ɀl< lQ+/w ( ?N+ &{5).o,̉Jd̲7OprƦNvyackષK\տ }9rD7 #y.xwcZ/oÀ[[q(’=382~ P`t~&K Ab䎹 X}MB#tҷ[~^ɗEàxYE=ӡ^NO:M`]آ8%ig|'xRFɈ 2zְ<:)<2Uo8h Q_s?~m"Bq2<{4Ϝ]uaQi[/1_dy=џ䡥YN9t5oxuLPbV:.XѹUS~uDaDmݴ ', G_įuN n|90ڕLcdtV =]>|xw&` BfƣG FTBeG7mgYYj`]<fy{)W |WC;_6,T=e~4շp~6I}@/6p^uﶙf {eو`ٱzT x|p6Yhy)m ҳ(ۄyxs&e4\ɇhNcmX }&wLCq:{u!'M׸&%P^+Nv//>+ ]U'N]5Z`dR}1*Y$0"I& DM/Xq9rR2gY) ʯ3^P s}hw8] ~Si#FJZ*Fh e7I!cKg0.dž&nllLtU8'GS vU' „a5P k 2ɫ7or@ x |y'@f<{6 Zil^[aršӡ+u&8 '-_SᤅkAWse?UH?Wm~H׆UoU_-ǣ:TҔjk|M>9Uy9V>E/%_~k]Ҷ+↽aSlOZjKkQ߯2WYxhkHW&Ń]/)m[YZ R\y_6 ld^Ж { r̟ ʹ~B+lS+FB,ka>$8#Wכt39t2>64XtVԷ/+|9#~Mu+Th}S=d=,0@ʗA 60 +NkQ @4lD  6:he58a,+ y;^;Dn&E|jt*8nW2^T] ۺ֣E}ȳ&^5+}~g7O`an7!Vu@xthvg_+OUi|_+lXcuoyvd)z)N\q܈\aEN8ի8֦l,w]~7n*O娼-7纸^}Η}ʟQAtG7F*uJgs-#'n+pB"Si˅x'~Iap)}ϥt^|Ey':Rp:sup:Ο\%OلOJhvgW>`ʯW֑dkSVvcye4[~7|++X#:91D=:-ZAȏx"mKM.I=:腷: ų[& ,~V.ֆ҆TY[Bu+٥}6BntP'_~׾cyߢ>W9pK۸V]7IsyѠt|W;[67Xx`l畵G=Cf̓Ym+4V0ԋXPK^Kc.KU&t/Jq/|]5}GO^ye>SloGC+ ᥝW8/ l i(E]4 1^۸7/ U_);?m5_8u̓k}yim_a+ᱟv#?q׳rO[*cn/ ].'oO5Ǐ}W|r/z e'C[RdN+=etأӃA:q,ҙ#9!uY|u ᚖNhc&/ <ڕi8#Q{߅I9>Gz ZPn(m2'Ke\6@<~+M407}9~@CSlKKjgf32CG@@sm?x 偞j:Wjkl}ᚖ>8/y kz+9v\_ K:io,)?OƳM;ZYm-?iy>NXq#~ 6&V|vJ:a2} ߯qrev9=u(W3fGv:L}~avoBL򇘲vt֖I(R' g'ۄ.?p20^:4FmCv?:D}CtG]N -LSKW{WFB`6G."e9WqGAo;T^R n k!?GRm Mm9r2-WkAnodrߋxm e' ez&ԧ_0ƪ@}ҝ2: Z6tUzRt`Q: gR{{aZ?tVzCh,By⨓/Lտ)+p+1]V¶̭;:)Z]:gJ/Y߰>4uO _?nx p>_;|7Sy=||y[h;pYW9ú)yyv-ʯj1FvWQ ~~:ӳ9MGf~I9FJQ@'8pp}p=:T8N?KpK戥KX헃^+U>֚2;ʗ4IE`-Q6A~>U&J ^ELh6MX;o΍lo]S,bv?Դx&g dZm3Sk5mT>OiKu/lxТ7<6_!ݝN NHl&}=ݍzx MzPWL0?nL邠.ExEykyUj3$sn_Xao}6m[SA7M⸍BeZIBU ʫoXнE=rf!,#lG@W:iuvVOrp6>=پ?zpಽ<* V8 xct@UNO)ҒA\],Le]Sm|H_:j?SRepGů6a}\͚|y`Λ ].$O8yN|^$z@Uޜe/E/_[+qn"05=_gx>@3d1eP3ZCwe'fS0Ҽq2v1 3zW%hq :|V^,|6FeS0ɭEKOZ+-4/.=!VʥC6:yYR7ooH_YvQٛjnU%BӶ +"Ǝd"ga2X # Zmht5Y&XQ0m\M 62<O?_0rWNiݦ[|mxE,z" >]=é+4XˢNE8YixMFuG6anڧ Ԝ2ZYYoq^ZLJ|UnisįFS~=rVw᫋nYNMSܢ8K>.|nϢǽJ_Bm!˵X8.i:,657{)XX>Lh>߮d8GM~-/J_ddÒe7htpIW5iw`Th ep^ n`S DvWyۆ| WmSrgQtR=ѤeHu al(*mM[_-ko:^fI?`V[$?'[Y'?o61/Z,heh/^z>)j{W;t 6J—zn=^诲/jN*{LVʛY`Pk4<6jo7=7{\EW6f:bYVN[\k O,Usś=xYߜ#}u{nl~0t-N7ar"\zNŏ~gty 3ݍ&}e"W^ .^zũ1OE?;:gȠ[q/gpu>g}d)yeAycDZ8YuXJS=vy8BFЃ~\V30hT6YmSXy.ʁ1ـu:VnW4;AG;vGWJ^ŧrm{iCqVmS'^xmlD6vi[>4 d&3l-JKtՒE_۷O8Jo'&.OplV_iB3ƘI}:vV'y/M˃kTP'|`]ZH˫[66iִ4ִW[whQu_m|}6}NFRݥ>?6ǖ nշ4?HFrˣ!T>>!I߉oJ(Ǖiԅ^\yi4q|mi 2y睛oۿn˟2סK|65o,wg#A&`:^vΗtn>s&qMM@̄{i{ۍenxO_$<hfDƐr7/5{o~ynx#q  ,:Xut8T#w =:tuZz5ZGëدGbw]sV[*kyW_AꀆWJoM|钉>՗F/tlo~|VjsydQk.yXݵɰKmӺ"qrhUǕnWZL/^'+}RڷvB_諯`(?6Tg/HzVW|L-ߛ]]}a-?E nħ'zuMk['k8]Zݠ,)T+ ſ4v'--4++6Iux;[Ȯ6l-g\zG^ģ|7T/oT(⢏^®4*SBh3,WuK8"_i% xGGy R&o:U*7Ɲ7]Agoy> 8G^ 1q꽳:?11W5[Ÿ׷.K 'mlM9B/mYT.bRoԗv<~mM`J-h嫬^2Wy$*dͷsh%華]>_'t/l嗘~|ŧN;s?8tl˜0_Oө-wfaK_p}Br:vqkx Jt'u "R[Ydʓo@xGcFff! :틗.mGVWRul|jd rI"q$ouW/ܱ.iqxE2=(O.rrl~bmª_ջǣXXmd*plFn`mE@IDATՍ pOY~yE[o57^ے<{W~^~|㳻yZ?>PYC' ,1Aڠ~O_.gԲڶu_ih%oeԞG幦)~iHE|XsG}%%3R86E?Xg`ΪO]LJ>W6i8Y\#6V? ,:kzx?,0|_e Wl lCв[G/'9GGo&`}E^ r$=ٟt#^M@:qt*gҡ"GqT;iꄻ#fAvetqxNd 4-{9+I# ]J&o^]gD2)Gg9+;z>٢Y =]0;DNmYaxt@l(-0v::)Ekw{Ȳ?(\>&.](J˄fa1]yk4#GS87<0ՇNk?#7=~3MS=kgUG)`.+o}ϖ'.8;6sNs<|\m>\e:IV[q'oaZH~6xBg|2 O̓UhϏD'!:'`uä@NG;x78u t8Ǹ5O L<:y\qơ;rNl0)s"yf܏ 96$!&WT'ty !J{h7⥭Aَ]yLy{Yy:մ͝Ϫ{}P? [=Yq˯.-z6Y]MnMVi E>U9+KA曂Փԑ:C|M{1C? O}sQ$ 320P:r\,gHe[:,y[?u?qW7Ӂ7MPKu>g9К Bdh>S?mũ:.}xlu0g,=,6sB >=g.ꔷM6xwEpJ uM+_nafX2ErW)r)D`ќ{~U.]݉:m:hZ 蕦MџEro\GhS N,;IZ,E[$m6wLʷK#ͪEdGHZ]>6 |8=0ɺJ]Cg 4lL؂\ƜXe~w>C[zӾDvnJW~[X]`_[^iy+LK W;CnIҷw~p~`3cB\7hw T5ײY -_8=O_v_j𡏍p0~&7^"m'jpdn$f mNײ7_\x7m-NC IvOaOґԷ?HU֩um8y @#|B s8$-3: j1g@xx*Geh`O[=nv_C\Ţ} E~tOfvmWw]u+YI?m ^c_eXۘuO_lc`;δ+=I)Ao&L*T&G[*K:N ˓}YiMՉd+hcLjW/?1.7ຸ:goo?|P/ʅȘM+.&l|RxFhK;[_u^?W_L#䞿hӤ N7􍋼~#^X1fc;pЍ?lbj?b+P.-0x4{+Oj3v~3i\_ xq8!1Iv1>Wvy2,9Cm-6ڜkUC&f+^vr dpJ*[m(oǽ{mM ReL6*OsT6'_Ջ].S.],l}6 ~l >_}+ T~+_qSXb ."]<>=;G<&yYt,8߫P.Zbn]hg*㠌o闙&ٛI~u:6y1?̉Kf7nIt\5KH9@`⪟>?˫7;*-͓I-.M=ŕD=m+G=96&fà^ }g8…+vW~|P5?|ce|}tL)P;4͆5V78yKmD\7cxc9*U՞䫿ˣ yWvCO/lTױ[6[O^nN b֘3>0a29E':mFm:`n ^^]쾎Iƛ:W0$N9r[G_ɯ]8I&GƯ liN0[77'և⽿io]8_` ]:B|6b:mp;O㜛O?9ϓ^u4 qlNB7l.xuRAGD ^Ʃٴ:vށgptp|Ve't0Aw.{ ~W :`+}]񩟔1]!|<¯^hpL`,$f5 x&cNOA^H($Դ顎- Hʸ ,JmuR#_ee;zt =[ Av 󂅮:<ʁ<tlSf-"9V}.([B(nt,ƩoƹpBSuʷ0iA:r6V^QZ_`=21"@n.f_J ^|y䷱gpyt}xٲB3"^^.M_Z5a<٤O2~+[otLL'^pp8L`yYC>rr5BCk 0 px'Z88uV@#Lt=78xl|wH`ώ_@cR{6`l +=votyRjp\OoF} y_pzGm.l!ʫ u( ?W27_ڳ( ێ >P9vևsWv ֤8P'z&^2 K.xuC#ܫFVN`gdE|{KCOd'ڬ{c,S٤hgw}u3?H^\gB5%(m"Ǯ=V9>"_V|zcy[v, ~YW9ʻzM Ypr0hL' >kW ޕ|Fsiԡ13'IKFu@*[ \Q.oOge]' Oiufa%rF@C,S?dwI'.bSVuފO?_]h*?fV{(O^Y&s75 Khg7r2'@}Lc}Ѯ..YRxgn5v<&]zK FK=Ջx7?G^M0fG&7UWi,/Š[k:+ʟ◧tԭ0|_{;}9I6sJd1_>I=s9%|oy\6D!YQ]ї_iI_gt0jl#b8ȷ,4:I5܃iN,42dT'g`h/͵yBDI O~$70l֕# ~%:37rUP1Ƣx/pWhz2 RQ~6峳Ї-]ߑ+:7o̤F-h#9? ^eܞivR,6 }A Fp\Dt7;ˁDgZFD>LO 芫/"y}&TBLw 2_'_:`Lyg ʫ](uD8uYOQ{y/]W\6ѽ<̙} cA__yWIˣu)z+K?o\Uk.ӟ/Ky{?΀q_BgO}gx}硶f(W~m6qnZxS?NK=Iyױ:\LxOv~A~»Iwt'>v Ѩ&~0d>?Y؁m_=}D=.t3Yj^/ |] Y E⫟n?ScK)v<$;ern&&Se1QMEx=&ΛiHgS}aL J80^64vS}lEN\ Y&[~kZ| ۞_:i^3g#>(FuGOL:rKk+zpэmm@Ξl !&A_@~ Ly& n4 N x#}ѮSe.&Ƀ&2p3d!0&=J[:4w[bI~tta#VRtE\L۞Ng?lؔ,B`+Ȅ%a0ɣPٳVXA̟؈ڊF࣎`M?yC`1̀4B[5Pmk G;9Ы-.K8ut!/~WDNڦ6mVԹZNT>_jcbmZrqԣ=rZCb6's+8 (ٮ {uDqr3:S;~y6Wmkqbi:t⿦y?Nވÿ:Ouޏ#xϲ{ggyPN辜8YtSoPDq̴Ҁp6![`WJ9&'Nvڶyh+q/&ka&p/9"WD%M[PMḼwUr7>N'(/8I4| w_ Ϳ0mYN{)~& on&_'_>@-F޾[`/d6;Ƴ3#DV>M=çW8E%{&}ФySF@3g ˀ("z_qg#gn:5W/'"/oI>YM,N1 br("ī}0'N|#7zcGP'/ʋ8l"㭇/> ,Rݾ!-\S}h doy /ӏĴq胢oKt/~6dX Vd/ Cj۶p +6-%?Ћ>/_KS}jkOF6;ȳsRxkC1@6rym[*;c2ɭoq񥠏ៅ 8,@~~44վ G '$l*&݊WW ]<ҡ?NDn^AgE{98ʕ}R">Z;̖ݜkV0IpgpqzkuKȄjg9|o1 ;9qrnxY ]F'z9w~@x 8!8p'Ky2GШ.M.Ա3MH넘ÜوXC'|23 W"}kb;|Ϸlxߕ 7'

l.Vfm]ա=',4ǫ|e B rCut`O:Sl|w| *r]ΉЦ]Y&!X_#,?˓]/X|Zև(ANE? {Ì.$Ojvd_d]˽7#q'0t0 ę_:, ;}uV8;gNV&8Ee6dc=O_㠛m !G[g4 :p@A`vAi8K-"6qwH; nYlrUO\dT.GU0tD}[gTDFwBBx,_Gq0 ymH׷mdm{>WGbo79v4qMC{ mrQ߹ãWvך~tx:AM]jKiM6yqt>c&'NCcѫ z-Km/Ly6m=xrEE6uBP}Gխ-pৎ݄[U=v?g`;4smIk+]R~ֹA=T;}m6|v* cp[-l\dtimu`i_aj#XݤՍ̚]@)>ln"OL}dɂ3n"O/}`{Xȭyᯇ{!j/26\k_3N>ev;Kp{Ngq|*6BO8~&oe 'q?@K%i>q Df32D _  *ITk&ǀQ fP{;uIMr/UnDBvζ&&ocaQucˆs[.?rCND KLRz§5NW"hMsLQRc,#gŗ ô}H?)k=Sv/.ۄliEӯW_\j? O77Ȃd+[ L U'-.&Mnni4G&Q؎ :ѧ&ud)W>gBOG]!Gl!ZFd/LxM:}@5GMT%O2[F?ȃ[Cje)vmQB茚9"d_խA5M *o~؀r1LF&/}R=m mKL<+/<XP^_4O[8x2nlŸ];C*W󵧲x2<ؔ`l%V)|m69xMENB>U{v/:gpٻiX>я~t1 So:w6騟"Zg6ݞ>J1gD_%42^qMY㤳) l΀ߟhhsnmpVtC0}5X:yg׀w3045L0iz)D"g7.7.pzV5!2C#ڀ=6<o.1a.6GxDr Weo֜>xpШMՑ1[ 9j9U [=2:NxʍY"{kJ7qzm!pH}|]E[6npsc3^OA8ґ'u˿hX{_ug6cџLn _#;gLW~N\ZUɆ h,W76p~,mp01E_$a|*xbv⧷0פ<{o߇F$Iï3&=?{Ԯ#`a\~-NrldžWX2[{S,4q/9# %j&i`tpp:P8 к|kFZtAk"b0Kcg>a0KNfeWyйnj6,8g $nh'ַ M<=$\N`-z) LL&=&rH,HW6p7pڿhnώI&0h٤y`0 .n=0# VO!Щ<rlHɭa(7Ym:2 `5Tx ) fhXtx` 'e jpԟybC -}ö[6]4j<K#qkWtuƶ-j,[&^z#c kubѡ>͏S7tv]c?r;#[3g1|}\\{tNLCR?g/Mtxv,t$"r^>B-lʴt.yY"~7#tPp]]G*Z9N n~q&2O|#yMp' K't>)d2&8 \ok3/NMB`0& 9CCL#.vy`7XC ,:=*@)O@+)_ml*j{'ZHMx&lrYJ&߈ouXaɳ}vﰸ'LU~6膞ڤ~vZ ]dj~ZoRm3ˌGGоF:0+7IC7FZ G[-چ|A~>]z@z5r|QM[쑼>|i*h~@󹴟,Iq#is+߾(WwW 9yx["k^@ ]-B`D!wboЉR]1CޤNFe|: 5\,`2V_m=h۸; LE ]ܢjn6y6;$?zɆ@]y -ˣ 9ő ={PIA6 h򱽠\ S۴i_B^dʫ9"x)QFi֖@,\]0xvKyKHwZ+z |YMO37=I4GiJ1lA'OOl:RmC?< Ѧ{giC8>oy7o%#Py- rmN8 z'?Of`q% ^ͤ|𥔽jRonyoˉ/u2IzK^e@xL̇A ÄN : L.WてYĉq>S&a^- &nRC:l&U>b&5)k,SY(}Z6rty:G+|- &J}кbf` x x5X;Y8b?~dWO.xw_"Xv_7r2i+~iT5?=;gpE6+]c oe *) r!>6Nԡ| z+tsTy +ox_,ltLs6վg|"kbf3Af&.]}cC 3>K!zr}Q>D,⾈3_sTpHR:s tI.8nM:utEg?wzۅSἐm3 )Zs7m2-!_Nf3> E[q7%}=0o^ӽ&M>7此m MK@^`|k$0)fίf6+`CuMv<~k,߽ntˠf ܄PϠ4՛d֏`DX"Du娛.NB33PsUe2%a|.Eik2^.[&j @sȯSB"cpsc8qhv} P?9|Myv~'XmP. ҅CwNxqa;0:iWךּ;._Ai?t;x?@|0f==2g01<OqߝE:hoޕ9en+? :/A&xH{Bz0|M].alm>)߆O]A$>'&/w ~Y=~P֟X>¬L](Qf[f;yeS ɻB^Ksu/f]=A^r]p}%,у@3k&08˄`I n exӐ,_~ir].c3͊UD%{kuÑvTa h2mVF𩣜@FЗx+:)lNNo`8!>8jGʵ>G ExWn`Lczr[n <3 d[@IDATsՇl!^s~ m6!NuyQ\>\ `m%]۫#3.~a?#[cNu ( \ݷ`ן~Vz>tt>itj#:cٿgj:i׮4_<6t`?#J) .qɡ8 ^mA2{uSdRyK)!DHJ]ϟ%Q$uh!ӳhPv~Ӣ{֩DhMמjV"w5gjiClZԝ|g 2ƫa@)g\g"g98=cxoS~N[[//+g^tGa>g?KuK_~dG rg~zYl_zY槲~CT/|zg&^-Qy,O 4z)r7eA}0J.yZKVB"uiN02/Q"p|T~x<I0!%f B#\H%,#ަ : /B o~x t}G'Ol`jv pʃ-^;Չ$V%zf/tP;w2|hwQ?gY}NkF#o5wݭ`PVKU'a\եy7PXxgsqmYo.U w?Q (FF0g|v,vURGypm1G;0n@S}*lW;oãG/I4܌Ǹ=wW^I J4{?;4yy){?Ɵ0eҟ|C#Ohwa{NJv( &H =@uٺ_2ŏFD ';|%Vy~eY_v T?fdN7@a^v PdROq DHX17D) 2v[Vn\)n=jptr2 AUTaB8Ou( +PC[h-^ͧ|t-k{fM8˧ѹwﭛh:I~,HK`Zvv[u(S^fY1Cpw})ʺo鋏e=t7}^XqvJKxs|{F6O-On>'ӻ4~Ø$C=9wnS?Ki8җz~dёO-\p o=`[cy>|mh<]i4>qFAqf߂FR~VAHO?wh͏|Ni%Gf3+W |}~拤ж|a L/28e \IJ^kI;+p.0B|\.`_0'E'!=. [A<)T}B #XZ0Eo:-D|beʺ~#m^-t18] BNiOWŽSv 6s]# V\xV&>)uZ m2*|E >Nw^|+G+|é^(]47r/|BW@)u-x p_Y~F_E8e }Op,u_9]2m& Yo5wͰy݇FZ/ƾ  :?1ReŷrO34?k27=xe~N?I_|>wȏwf'ѩ;!}2H/EtYNov~FymK,.%C3kyv ]ɀrtEN f_k`=2 fr3ش0h3MW>j otx/= 1 gC'ݴ~AKc\pg|t3y-ډsVb/*|n(YKC~O z)oUy 'JxۊEj FzƗ}?OhoV]a}URq(oDp^?{~ 2J f: >bu`U~$EZˀ nºC~ l h^8Fa6WqAAxiГVfRPxh(4՗tK@:DCM[k4@גk]]x/t =Γ=_!g.e/`K@p҄`J&8tKPNηI9ycǗQ `(ćx)duF.~sfdBg c8^P'=>khuؼu:3]>[~Xx0_kw(g8/\s¦+mɋMF> NI=j A`3#/oہ%,uHH$qSUֳ~On8Ӌn*ea ?LA'ɻs9 2븖zF;QNofyBFKXYٷ->JfᩕЩt Щ% :aRgA@ԫx3ko)/g{V L5"P)V4#X ,/R.{|f2‰_+% 'ƫOVG=8[x PVÕ5mGc?oQpxj_yt nsGnM2g+o4\<:Lv[4 JpO͢ XiryZ'w)~]8zp'MxzE{s7[Kh!HuAW3~IoB,}yshțy4N}Xb{t |`\K._bm'-y^yvz +z[xN )eW\Lyǒ&ߏ钤lKq+py⪕+nݿ]~Ru5O'ԆVƙ>:|<~n髂ǰ 0w0GGvz׸IV~3wo,00. $7\2Ɉ|PafK7m 0(e~k7iN9\^)W|zס~i/(+l?xQܶXY-x/</F d|Z 8K'5_nɧxf,LMoY8,ȼdNIrb PA: t !»gvM:7RG (x)4g.&SJD) yߴ-@[!!eD@lޮz+X'a< )Y8` "m^)}8mVmiz8+^<qXBk;ƻDo[wۘ^9zwq 3ZY'Wvxw xNFms{ȩn.kzp`/Cu3>xGdȬKeWS^S?ŗXukE dڤgec!77^s~|dӻ8xRNJغ~OߊML7)2{~߷Q5M^ȫz/kzepc/-?/vy (o6j* Xm7[D=p!rN_@J^FxMp5Q sgxߝ>B4E(i@~p!Dp$X\xYLBjB?}ǭ6 {ʧfW#᥷Ho/M5Xy"kfW[3ߚQ:(, CG 7^i:1 nmi9 OVB^0+f:hXuxᄣw}ĕ{̄K ~5_ͣPj x{hk׺o\`t;y<]>#n+N=ԣ}}s3RRi5pgi[-iގj 'Bh{)t:ӻW+Xf n[L>ͳV۷no`#۹~Rap9d]"2bf3x3l4S7!BRӆQf0$+[h%$KE@Oxg*NsAs8x |ڭXP>fRJp3uv@xo,|+| xlOV2U-ڝxP?08B^ihۭ>Oa;a+xv{y/O]!P$0N+k|xv px@<}< HH<ΏWu6tQp?F~{QNN{?S~e{暈Rz*̃龐Ż_y_. ooq[2>K<2ڋվOO މg߈P!3/ٳ}p.rp!Wd0?`'{gRtۜ#P9L\{ww:KSz 8 Zؤp5S #aC9&w*W&o݃Q|wCdziӶ&T+8Ʋ[7ӯe'y7~зO8@S:}>`rq>zM~$?(6e5zs^hVDr(giz g ?y~/E3fky +\?;/y F `C8x;Qg#n.泸ssYb%p)ܥC#dG1rUݻpR9\}h Soh+E+ܔ 0%BpIQ #(k[Ċ2 NCo}%-$mrH| b y+} jlG|h_q_pm68n}_z!+!|X;MQ_Fղsm3j(Ex͓Kvf G'54/=z=*M߻8jk"1Djw;7\G]FSw7}q+MpHvnψo|+ۃ[g }Eq~ċ ̽s?8w#3oZ:䭶5V3uC\F( Q4ݢASH3BUCxY>Ok m;vQ{y')uUpI;ION^r1:;*oU|q!_}~0~W[VM= ~AoMP >ͤG(i3~hGX|U ?rNޘ1TWI`-*WxjyH_nc}ahB?sͼ_g ۗw?}?nLJ2*{dEhi߽U4w]yKnF dK_a~෿-^s\^ .U_T[끥s:䧖| ՃDև5|"ճpI8?S6ilM)xRc`\ʬ|{E_V+𶺇 (e='G*/ ]JYPQU)М]F0#`m y ~KFȺh6M\uY%m3]X~TT#}qYiSCJ p)?f%de]Bi~%©%\駟n|gRQg6pl;_è:5,<݆vuY17ߖg؇#}Ln4 ۩o``&_$o?+,DŽA@[^}Sc:c\x M޷SNs(0L֙!Yp~f}wzۻ2wep9/ElѬw `a C-%Ll?L9*JٯDhm /%Ҥ]ס%tmK0(u)(Cs2\:moRtK!3^JRW>KѢd/]Mċ֜v oeaou؆SO[Gx74pݳ GG gc_3m}{!({ߧ{1/xx .otTO31*+FA>< oKsiжv&Os$1r~$axy 8YxQw_(kw3yMtgxd)σ5ygxMI֑ MF| g ~Ҿ&W `1^:U a\ ! 끸}˃C4 Vf67xD3|;TSF>.'F]LT.yW"^Kx5w൰(0{ d8N L'T͂0RS O}>-VN2Ń0VR".c5E CQQȄ;x݅_~xhh^3P`hB q4 |p@8!Npq e|=0Rwz+xe8-[EW~P_<t~-_˔J̓08'8b}!,ߙKڗDWlZfaW.E^2kAX †Cgvf#kT2Nvodg .vMLmeSަLYimUe.oy\PkK\'NSD]Y0G iV eQ< :MO/xSozMqw[X0c8u'!'DoV57x| U1Ks`y8'v}<<| |11+݀/=&ٔA/b0~z_3vmy#g^ü l9Azn}wb?0zƞu't=j<ňH{qԴJ{9=/Ű,qG`d} EOd.X߇QO"e!AߋneF03|WaQP+I[a؉/"j>>k |+ gR1W"\;/> A~8(`=V*:}pY ᥔ_ep9j `O|;BCaf_M) KQ 'h'}SV┏( '9될|gfhH8's%euWgF)_pvs.BQfFJ hCy;ݾzÔ3\[[F H[wͬ}:d\am+pͦct^`?nsQGQZG2\`_0g;`3#LHW/Ji7WJDW EL3.J[fgB'({!FT8Wy¹\]FS-eW{ڌT[ф_Wn+~GXu^=]_zp{,rvuoBgR9X?Tnc0wN OwÿN1o31.KxIs 1⹣$M$9w —2'JL]LsgzQ~ Ԭvbpv."J{@dbT8ĭ৔*Ax Q_"H3 |2(#~QzfH~"W:Bw`|߫p(*X[ ]ʈRo:U|:O~F^{yx Nmlty+fJR'/nV8ŹcxxHO]zp%O_f~]HXe'gB'+Hcz`>?5bܼʿ醓eOx,ϺOoa5t4؏ h;a z"uN?@ "FA+xY ?!NQxCgp Il?RJ^I|B;Jb'<8mK piqkVt2I>fJPHgeWt?%T=<%#_!e&%UR\F%vJmN\]<'e*|AShZB]e=w_*Gm/+` `Pa<8>yWhk+\<}`s~t*/l8͊cW~holȪhlO ޯ@NKN:wBtO|}%GQtW\' VP>Ia=Aquhyxޯ6sff2f;ɻ/E>9IÃ2p+g>݉?uQ 71ʧn6vY95ݾ_L OKaN)Ju6 <%IjߴOKr |]jN)G Qdd,u,dL&B|* +6)b$PWCuRߘSP[6G(ryxjʻQmDsMhi c䷍xdKG{OW\G>e׊R}y V^>Nm<.vuۄ<<1F _J:0+ x|hr7pt=FS>} lŽo7m$ ͠9zRamx3IìGL43Ga]ݮ3IDAT`Ͼg_n OE# Q'Sy|J*{ \W[k !yt<Ѻ .̮b|WrHϏ!Y 6I~%m[3Y%-˪_ȵ~ 2v5Jj7ogfiBfQ|%Y08"KE@-FEeG צO}؇(5u(dx =Ց7ߊ YMV^Ό|ek@ +LVۚ&`;荼;ITOƖ+ Z꣫NCHB.gܔ|;&) Uf_ ì]:v(y,+2% ' 'M;|.eҭŻq [Aw4oMؾj[~ `3KN#%Qu9>+[ع(k.2p/;}|˽~mZAQFy%D@<72GsR߹x(73Y9(eXoJPVQjG^9ytw)G9pGZy$tÂxvS9E,Oo>4l5/hvmlt ozxO5p˥l3R|8A^V$lK!9Da>竭{ ?O)vwW~I4ʏ#uwԀZPu8TT~vYGڵKp?9hx-ފbyC`}s܊w6H!ۿD+*e$sh&uG䛩RT`98)7ʙRb\p)Pyy'>·Rn߰`Цl;q+^샀 𓒤GQ u߼_rw\}i0.HaIh J=RCW ,KS2=ä}Un^&Px 7a+n SH礴<ǓFS>tM`u^P~~*fVlˁS}Pg~ \_|7+^\Cr{`1~`e jO57Ʈ|Vz~CYdhM+QToĿ%bFZ(L̈́|_)ۢh(e G,_ֽǏ j}n[R2Q<) npڷxޒ7%u@}Xi$RK^q7 Mt[A 7癞vd>/W^m+O ꛨH~̿#47RuCBw9/?X {%_ uL'k“Xq#̓ˏ<"˟؉3߈U<@1$pT(V6O|p P@A.xNSjPxzhIѴ4qN Ro8F^;`:NհUmSBv9RWmZ/ۡ2i+xM2nkhZc,Qb@$Y_o3lw9 ੼?gFƲzC.K<_N;%Fa^i'!?6[֯u&y|sa;os/l))}!n5Lx:B+ʫ0+R?E ;4Ji'=:ԓ/M2ѧyՓ+Z֘UO#M]wY/VnG! װ$W1W|מei${'ȅl;:pfg xX 蟱V"]SHc1Wc9)=a=η:ApK^}x+ }F7^RU( Rv> lKYdն@)3 MJ?o Yr=ۭk [Z u~)YiIi2 R}`?O獡wӡ_[,xr7bK< VNTѪ@e_34fD=uL{톙mƳul ; se[:~w.=7yz`1^4Lgz yMo,?3'uݡ.֪]Ht0((SE)N[; s+LfgMPz5o?`7GRa@sn:@n,%;)xRt +9]ʖ.@:=x;MRK[(!XF:-nh14:Q2³65a+5q.ӷ;>[o%B\WF|puǏwTr%brp|B_>3{?!HL]z给߉oSN۹Bٿ+x=⿉\|:ie%V_"[Ig B*稔_[ a:VeXAnJ_Z|FXJ?z@蔫NjqVG^T6u~?æ xyi<¡<ӡHqy.}u>Ә0.J}0!qtPz]z`t釥,~ɝkQhynj}We{drws~ߏҿV)s+RMV)س;W`k`7QXu3୬͹J#s G*w`nP`^2^y`s5]!n,Γzp/ N_ѰAVfNd'}#~l?.Z sue;=I1Ǐ7?M[SQ?'V1|#7,eZWgB磜gwe@Y?d(sma){NB,ۅn)8ixx,<)e)@uqyiŌ^>hr{< ll|y +;n,k#s (t &fS^s\¥FsWYi^M{u|4,g!NVGߌr,񏲧Zހ~e^k{(' M7{vo޼Y?*DG@AM+c ]"Gɢ%sSfgUGZw 6:^Gea;6< .ƷP9LYbH U;j-9;j{e{`ce4>z#yya'zBK`{ [IU٨h MlV]'}Zey#ckئ%5N\xnʰג~Tm|`w"Q([?RV]SzNw탍>+'g0n[]z;l i=J]l87݇OAx:[9JVգG6,:-5<<{{8y#]GtfI7? װ/+KGl):F,~`,ϾNR/Q&y>l`y¬&뙮ة(|/ʇayfoD_Rz-JyQ.2?eMy}(.-ud[yj^09y,+#:-2uwO׽VV.8!xG6_'z;W>d$3we;/e3zݖn.=Cz`1~H-u_ d F~F@٤MրVnd&.&(r>ߊz7I^wc\) ,uU&8'˷ipol)*P/ +neoVh7f`OeŁo忻=ݷn4-܆|7lu(hP>iѫd$N4 +v:KKzxZ=p&T-;ӬW>rҏ[կ\(עάοLar smEa氙3A_Chi53 }v'q/1,m(LF. o(/8(85y¦'V>/u\w2wuXta'LVKuh~^}댉نZ¥g,츥?R#B~ٟ~5m8Ūt k? hPkUV@{$0(Ѡ8' [V & `%`FpLBZ Fӓϩ~ `Mg n^~ ԉgP3LU{?~?m>Lek*;]KX gROZiJp:s1gcs(K7uzWѡK><')X؈B:BЁA>@ X 0;fbN(“[SJf&Ʃ.lz`11K?R/H%0דh 弟Y(ϢT>soR_okLENYwyPlQf8 0 6ܹSwΊAvQINʸnLSY^qn;@9ɧOW%|!%?dw_S<4a1?#_MaL֡_{R1ο/6] <\K<],=Gr"; uqL')Ol &nI3\}ʛ_DiߌMcl&b 3JZoX{V(l ԗ1z 8U~ q58_٬뇁*?2*lBqt|T?'a}p}Ye#;FlwwߍM֊woKbHo-K4z+4kU& LgQQފ2t%KIV_d0%XtR77(qyWH6_yvhЯ[x )}ax^3> e+l✰pt\5\KF*| wV]s)F`WDz%z`1;K?4q;OQ*ɸߔ$-?BvfEFd%V8J"=E_AZ.%hu* ׿V8SpA+2(\3R ;5xoZѽLl4Ȃt(c3ui`b_Apf~R҇c68F óك6r#߭KXv pIbz`1~1ziϤGJt曖2yssQG(/_ gj )LÀɧ(=Fi&; 4{AR ս):**ggTfYKKyK  )x ?!O}A*c6=ҳk1t!Es^rq?v:y3'}=I>ﻙ.G}7oAe({Q/F9?x&S̻s& y5qvaD[!"o"N3R~+6,f@2,Ggf$񧾨h%۩s3<{m?&c`?t j%\z{`1^@Z?O(9xZ}߮wW_GAe?^ ?LS~5qP&_Z54S^3 V[,__Ƴ鄿- eD̳R @ a<#`S<nQ8Ȍ}7?uF(ݷ%%,%jN:տ:$~"⌅RE8GƒxCa:' W[ U0>;-wQgɗǯ-Zq>h?R%xZx{`nǛH|R(Wq O>ߝYwNe(_Gގ܋#@ wRv[BwṚ3 bn*u 1< +Q=_@ڷ!||Ǔ>M[>(GaJo_o_wݟDxɳNdT]zg)6:lc ,K޷guΣЃ~/ηSQc 8H?̅QȵwE[fjUa.~qTS7k 7uʁ=;{L駟~XLU9K=?j.ȗ 0~o-s{_W223O5Z?L~c%`uJ^)|a|45]-2L򞃇uQlGHoo%&g_ \' Î?ʒx9`Yzϫp t/p.QTolڕofU _(f[.OQ'¥Vj2(zWq*?u`Pq3wÏO }oR5eq3nJ-~xk-'Gzy;j-x=/CtKb=ЊfaNowZyS߼dZey :!FҦy^y1d)Aemn 4ao#<'໛)>_z0kcGR/ޒX.>l[KpMRIn dXpK+uԫԥxcBAEО&-&EYph2@@Upr$v~}1 cׯ_#gu{=}ۓX-u_orG v ڪQ{i}Z=tA+^<ht2*C*O{u.hK_#{NPt4qF(X*Ջt9zw}]O LhLv;# -`UTvIuoUP-UNhVż:Cc"r>.Qy1 @ѝX  @;d坺ԣsUuE_zo:epIsڃÄr :XM~[(p.[խ{4..?=V> ڣ4]v4<"n]Q#BHV@% 0y;h0iw6F~;:u|`K ue,S.+[izhs}k/+?HY TAٳ̷ܻwy~0gcWǺ*G/ Qީ"ݼy߽W_[lʾ~A .u)|.jS{>*WV'}*9~w4ܶr|:{2y{߿~®YubL"p?VKm  57z}p˗i"8>} 죗)z;w:_р)ݛO+;ZBKwl-//o((7ěy֭\-Zkwm,کiY?'!Pf #e`:ޓk*=rgcbqqqJA}Jf:寥wlhzj{?n>y䌂}_+N on<Ew)۵M^~ڀT|S Um( z- ; 8]3==$}uD }4_,RYT/aov 0yuAtuZ;Cr~&8cu~5dmד&kR~tZڲkd g}4X VV^9oš{;ҡ{ϻvZGi i+TD(J+*F"#JX)@P0Uց{c вN<0TT}TZJAIk j@X#aW˺imNL,@ ~?~mB`Z<{["~2y^*uWeHHb: "0U$MT :ey`~s{SPCE{QlVS}a P%PtWeWꊂ5uFRZ u7# l]iy>PמyB@U@ dGﵘҺԕ9|Xk-BJ#.HlLi]QAAn x0t?-  H u$=t_)}9T v!P Uͧ3PBި pn@HxO`-B@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@|?#VIIENDB`icnV Cconnectome-workbench-1.2.3+git41-gc4c6c90/icons/mac/spec_file.icns000066400000000000000000003275751300200146000244620ustar00rootroot00000000000000icns}TOC His32s8mkil32 l8mkit32et8mk@ic08pic09@is32 '#@MHi~:' PZ4Y|}Xvj·`!ԃ]աR(\pﺐ柜5ok2y)d@SReh/*WwvyY|Y31!zzJ (!;`No{4( p\fmx\9tvrx!iitfgpoq}SjFk_Z|gNpzwmhxFoV{zy}}P6]yV)\}U/ ,- q~N ( 9|Uw}2' "SKk ^uxXdϜ2nǂeIhai#)y0lt|s;DqFbä85y:ZZhrm~XR4ty=}kѠdU+]qh* "?nxP s8mkSš͛0i*\J6!1dER Sil32  ˠŬh 61 I/ng l l/MNptcѡ-h #KxgʪȴHi4?}v>oe]j!e4q^U=̠tL'f~Ha^Y͋zn$rh jy]۝QqvĴ$eeR]tyyaFe޵TVRta^}iΓȱqT"i4Bj+עᢙO~U:_-zбⵈfA[YnxƯH701qԮ~i90ZD~.Q2aDΰpJTX/V2ilcMUT`8y}p7RYD tȀjeUTRTtϨc4 Jg/r6qIҖQe9GŬ\(h 5y18 մsya ˣe8yo/Is~dXI7EWrS =͎ĊILW6|x ˠĭh 'I( IAld l rkWtLvmo:h '6epRdwGi (M\sc5mJy?!fTzGveƷ 'g ecjNtdc|ƅ#k6t{r{z]Qjt#fk`eratBMgg1[V_yZY~Yps9pHdy7)pkiOW5tވKxiGOudXMz8(Rݢׅōcs8dmD[>OL:/0qa}mZR`|RMBG_ss,fʔh  dD C>{w(&{g!h #KK~?w/|ڑ 'g>ꋝHK5]e`sƒ !h'xŌ)B[efpTfPv#e0i͕^o7IX3o{'ScZ9i dSXJXOxl9]&T8./S[LCB-u$s:S?aĹ^1Ik&M=I|ib60MU0HI  &=-g|HI]U=LE2P?R{>?4RLsǿʳ4  &A ;]4OM?M#.z>WJVxmƵ`dt| &A 1;Qy8 GM4 |5/S^rͬsWp͖Ϻ &C(+8*:Kmq7ROIE"de>Ucp{l|ᶹp ֒&C4+1GEgG]cSIJD2ðtj}}΃rtpoY(e5s9&I67$ȥf&GE:FS7ΗnK[IJGF!Dplyo{ʰmwTW|̲*&Gje76 RL[ռ]WL=>BDE[m)Lqjz*slBI˭]_I/4U=&Nem+~\@ڱCPqjMQXm4^Xy%ֿ̯/%2&R d>?JSþĽƶOGS^wyKZb{)แw追ů~5X>&VY#FM=2QŻƱؠAOUj}\WZk8칀hp˞w!)׺;a& 򄧹+evtۍŞx\S>e&dtƴ*'bqоԛӿGV\lHiZl{BSmetײwdSd@Af&=ʷPS[ Wv}ξoLYd~|LmbpE[ckrkpyƲbMwϿ/&fһZ .}y›OZ`pZPqmzԂJc\adgnuyz}Ǻʭ|dٛd&3ۏ^ mlwsX[_nVMtv[O[Z`belqqryȴڣw&& LmWyr{sro剁<׸iTjk~JKtτBRX[_abhllrǼSp&2gyT3Lgq]D˙wwlMrr(Nu֘`FGWZ]\\chku섒äp&g}:K&$:t_Wc}CWntCovf1`rܤ`|]CMQWX[agnubop&uZ]>ÀxkDUrTJlHfxUd_dB?DR__eiryʉhֻ"p&HɺI~[Gt~xHErCpᆴųVZwxZOiwXZ0.8F\h}notϤZjp'ػ0> ruqxR6wry~ϕߢj_g~ӓozL~A;\nK3uvߴpNp' t"tn/ h_~xW5k3 mVm于uoԭML;YQ;tʵ΀LzFp& WztJPׯ?kzU6gЩ"NjUlױ~῝wekPTwܲ}gPDPrkؚNX^o$Ff$W`nwyxøL~R7d\}s~FS]ONuնƲl`{ޢ|zo񟐩]Pe>g2p%|opt~S 0qS4bʮG xBG[KCIlǡýiGǬpEl͑Cw^HPp)*}~z$bsR5YtzK6HC@-sR=D-EA>86Kqʸ\«Ͷych~uyKpClĵ&pQFCiQ6J<7*&FkȲYʽ|Ͱ¿u)p 1aȿKaXDIMn/-44+"&7eڼƳ`رǿwsv˛Tp,GW{µzY) OZfjWbc#/-))-RE¼f˵Ͼȩul{nоzp)j7d}Ơ "fVg͟[U>37DC62LuI}mɱλkmܳAJ`b*p,Ea\s \S +k[OqMUw+49GMMH:17F[wuϿכʯtfv+6|U_ȭAo>\w΂;`,h\Y[uk_`lew -0517;;9410&6ՓdῼþZRw߬X:ќ5p@pzʵzPss3jjmbMNUxaOG[Z: 3MH>63:@A=73/1-<٢ޖvY˾uK`dEܧ3pDTnͶI8hZm}wc\P=A?HuueD31AĴsHiTz̯@qt5Vtdt]#Dkajվx@Leb[UKEBI\w8=CHMOMIEFGKKByъ`F68B8z;PY݂\ôOAzBAJzg. [;]^UNJHCDL[zi ?KTVRPO,SPyĭgEACABF=Ơ`P}i^Y(t\Css= !ו AW[QKCGB53XqDk9EPZ_[WZYaU?NLMMLGHJJEB}ӫrKvr:}Ի(~Yopa^觀Jmĝ`ի(WETUKKLL ?W^n dYY[_cadkdbRRSUSQMKOPLKBb۴SiZ/m\Cq^KᚈP2"wI n;TOMNSH#0I^dktwўiZgiquztjdXSTVXUOOTWVNP:w་\gh"KEvޕ]%VrW g5RLRRXbglmmxw˴ƎVoq|sg^\\[WSU\^^VS )OLRU[_iTnR]Tuwsiec_\Z^eeaXZ9t߿j̎څ/FϺ̟pMRJ 2IRVW\]n%q' ^Zvskgb_bjmi^][<ߵqV{M+(BǦɫxi:lPkVBSYAcs {P@7ڋb~sjc`eone[fLaz j"&4etȧԸX TddW"KQORYo`9vgxld`cjh[\TxFt#'{Nb 4!%~;/˵N&g2<\q\'h=\G9EJ>/ *V9|Ķp֢  n"  Ơ0KҴZfo|bB|oCGdijcd[=&N#%Gæ#=eix{` 60Jia*рxǦS>ftwIKiEG*$'&'T&%%'*&%!P-^nQlZNrs_jj~ߋ[Ǚ{]FqwdWhY<" ')'&%(%L  Lnahowům@ADqnϹVs}̑lLIF& L3ESatZ 57wFg|{ !'<`|jSF?>;)  SioxucN;7<=,   CUfg]WUR;   QK< ۗ  G*$'&'&)'!-^P9$RklIYxY- &%+\um=8beM,,SUKVwlUðɲH &=*n[q:8__WA?NZhE8Q?S{gS?OSyWw~; &A 7ef2LYgH@KH_bFRLVwͷabxs|EvXTP9є\X*&A 5;X^݆4S`MFLCICe>GP_q˷ƮrUYCBTmf}a¬Og3 &A'3?G=:PLY?[WOHGACoo^ap{Y}{=CdWUgbTwLq\=֒&C38-3FSMJFo֦>]aTIIF9T~³sj}|qN^EUmRpOkwj8AKeg|t\jd9&G;KH0<\O;/\rDdULEHB6a{l~wuuJ^jYYePOtYeg`fȦg&G /?Q:V3D[K/]ӺYNZHHED:OmÑOtq|̼WFlYX/Dl[dx̮~)&L VA::1]K8l3UҟPTMADHDHZmoybnk}ĚBLeV;>\xh`~n^b;&N g>12D`&Ckv;J867BGN]sX[\dmTWI>1Pf`mqUHs}]vP{M &M muA:Vc-qpf_phKT:?IQbvysVXczMaMM~~NfIнva\wc_&OccCPd7EnBi~·ǘ@D=ÿ^Rgùj:OPcp`D\a^J[uݩhijq_o@&T,ugxv^I4-Hż`˘6DLZcsiSYXlipyYeq~ȤmNsӷ?&V qttv`71qűϝHIR[uyDbMZ[^yOʣfBYm~}Ѵ^F˜=&XSHe}TX˿eS[DDPUdLpq@bYh|eW~ǽV6jq}SEŝzYPXTƽ>&YPdoyĵxkfoѿ`q{dk^[ڌ8MUXy^k]Kh\hmMppCRipXPv}ײzhYjjab$d&!c]blY~v}پ|fyTdiMn>XUfgɷDYjcpnfElbE\cecjpyǰ|M|eceze&MM_>kcZUn~߾~shl:hݢ{K\[zqyzmSPrmweψAO`^bdgnuyz}ȗd}ӑe&4[pDw{wb^S|wvs~xdF=QG_]lTOuwھTBRY]`belqqryȦxʻjmYvp& PhR_kZs^xx~{tHoi{lsg޴^:`doyNFuq;TU\_abhllrD_zZgoDǾRp&4P;vRo[Zxe}hobctWa[{b|`]GTosOcx4Mupޗ5CNUY]\]cgkvyaճPh˿#f&nQcDeO`wP_WaҋupRqbl^hYoKLKXApuwȌi:\syaA>:GMQWX[`gkwo[ֆJg_աWp&;dFak_{kDSxϗvGbyhecڅDrP\7lsyՍHKh}jy2MI=;>DR__flowsSNĿQV`-p&.^`}X}h4kuxHDuÅ]rEmvkZҝPg{[folB_qyZ߫Tiwa/Gm^.+/6F[ekpx{^M{_jݟcEo`Vp'ԬL}THIuq~xR6wakrQbVŗ`M_Gu}Ek]jypcҐpz`R8ERG5<[uI:MYfizv[֙q۴oSn`o"p'PBzoSDw_}xW4ld_slDDă_RQL[Sf亏up|]ۚ]̮|kg/HA>@WwD):NW\`e\Е`|STvV0p&ş=e~zqYb٬MrxV4hϼqgs_JذiRٙ_ƙHs)DC>OsxQn@++BIEJ_f]kOUaoU p%*SЍ3X`oyvwĵ?bxU4fχeezhBlBWdPJFԹefū^gת̚SwR5EB:Cr8dVC@BpE`~ZN_Aj1p'L}d`|ooui@]nU3cȼ{`gUzCEXJDNl¯q`bŒھzBFTKDHnUlqloCPsx5Z͑@y[EQp*.Ox~{WS]kV3ZQQij\/LA@9MpYyRth|?G[ISSuĐhQRe.KwfbjZ&p*bkõsSqTZmU2*%%98G\wcUɲgħncرiQOrfwl[i^/9YbVPDo@XzmAI4c̘PXUe>n_KNc`V`jmmbL987;|VSl@C-1Yɼ~ndeS\waCO_`X\jttmcKEKAlf90,1@LJC=52574-('+J΀H݅^VfmH|ij}527;;9410&6ӘpnNLRVM9zWwʻt\QwkJ53;@A=73/1-<ԬYpHv^XEGn|ȪuK`oH8Nyۨ2oGFQFi\:xZcǽJILhnADH3K`O=A@Ew~LۆWucB+55mȳsHhboY}˯@qpK?Cnquy۽KaY;qihqeiտ}6=ha\UKEBI\x36:CHMOMIEFGKKD@H֙CmgE8X[PYw]aôOqyU6Jz[YKn6]w\]9^^UNJHCDKZzmX(=KTVRPONQPUSJM`a=DF@DABCE~Ơ`P{HY*wW,tLetU|=QvҩaAW[QKCIE>?Xqo9EPZ_[WY[\bTKRGDFKIMLGHJKCFӫqMrqepXTXor>BMen_HJpkƬƅүiSRETUKKML?;8HU_n]tbYX[^eafgnaXZYSRTUSQMKOPNIAbܴTfqhvkSX[Eo_/ZWdm[ODlpd\ldWjUFkNzSD;TPLOQPKKR^ckuОjXfjpvuvreYSUVXUOOTXVOP:wཊ]eRbo{D[N2oN-^{iatjZDkxxb\nttnXygYsbWHf6QMRSW`egloww˶œXnr|{rg_\][WSU\^^US=ckbV]WYdDUo;'{í~k@hU{hHotXjl|r[XWAi{WQ)OLRU\aidu[]ɇNJWuwsiec_\Z^eeaXZ9so?Qp[bmr>p5%sлnFVoWi`as}~mR[ehQ2IQVX[_jWT^drKIұu[vskgb_bjmi^][=ݶuJ^yYmp{g"p.#jȤ{tѨxiY{blNZsUBSZYXdthwkvZztZϊUމd}sjc`eoneZfL~ˏ{MRUX]@hup'$n}ɦѸ`Ikzv{k\de߀Q}POORYon]eEOyVђlfxld`cjhZ[TwòO:mzETZSϠ7RXp$qG8ʷaI\jlkthxX\mh?gvFNOOLH6BI^`L[]LgZsm{lc]_`ZPSmqbdkQj2Ev:p%`W=8˱zN_uddZthW]nRєWYhm^a}q8DXYLDGLNP}~xeq~ymb\ZWQJey]Hvȼd8=cp';7չYcgPU[u\Xeg^ckGgOʫx_MGPXp|oQQ|y~tl`[XORRTYböS@;>p&/o̹xpXE1_tfXhpw`VuX۶kuơz^^krwvTRM{kwd_ZSLWZiSHWºnL<1 p&IbdeglpzzeQD>DB0 *V9}·q׬k|auLBGPT\QOďpz_nѷA-OT,Okwb@HckceUOA&L%?cǥzȿSLGMfjw{nA8C?Wsȇ\d|3M{R+CL:!H_W,G*$'&'V&%%(*'#)T\fm]Xrjg}[McƞJMo^A89:Ob\;""&)'&'&%(%L29Zkbhpwȴilvqʽ\l[spdXWS]NDB!% L3ES^uֈlsmt]Pwg|&.CZSA1#!%$ 4 3PliMecXogwȼu;LPeio~ 7'(')+7:.8[NbŶ{lZShLz  *!;::AHfmJǻY>Kmw}ν. +5RH=EMPijOW|x||ʴ? *6icB.YͽyvK\v}S #VvE,jʶ?qY %)hr[;ST #UsJ5 !aw[Ƿ~[   0[Rqó|somkh\> !(;aG}{iTF?>;)   5RrvvbO<7<=,   9[gf_RRS;   *5+ ۗ G*$'&'%& )'/EZ][7%(&&(%!0HOQI3#&*)($%&'(&'#-& A~}WDX2 /UgjUX`N% ***lgFaR9$Sjm{|avT$ &%*bo}}>]aM+.RVIYricg9 '/(`' KhwSOF#,NoIMDbNHLpu[r{$Zw-K;&!{ǐ4֒& 2VGSSG=30\]VJFP~Ĺtj{|O)*4g}zt:&EJxEAXJ-FcVKHE^oúw{xxQq&-Jmpȧj&G %Ilx^0&SYIGCGmdg֓ntzA",  Em}̫w&&L %>AgyG*VMBGJIQYoxknzդog|8&P +2S]%#K#-CFM^pZfjD%#(]~Jøb|Q &N 7Pa, =jM!7'?JRbujUb~uLO|q(¶vӒ_&O'Vd:!&OeiսȌ$@IWe|˝RZkړp~6| ǿ⧙t &Q*uԻe@1H̴n`RӾ~&JK[mYdP^tȰRAB6̾+8ĸL,SNdmQOrlx:gS^_ccgnuyz}͇FҎe&53`ٟ|q{2 !@b^m\UOwwݹ1:UX]`belqqryʙVȺ}JEDS__flmur( N`;2p&y}ۯtxHCxg"oՕ/D\:aol jdyB!(06F[dft{}C  Df%"Rp& vsrxP9o{%" sǖZ!3k\kxL  ;˒oz6!#1;[vH;B-R~s/ +ٴpS$`+p&}w`{xV5l@ " ^~c*HE幏up{F&˰R&$>CVu@$# .DG1zVCY56p& {yyqzݧmuY1kmB04~ObAU8װTƘ95LsX+i3$211.]? IOUceTp& "MUboynuðm[0kֆg AXfQH6ԹP!ש ,+"/~GPC.s'uYN[Am2p' 7)W|nozpV1e堎d=DFWJDOlR 7ܽ¿T H;J. V_Zy4WΏ?zZDQ p*/N{ϧmV4YXs"[].M??9Mqņ5[3H# ^ 3Pc#@wf`k[+p+fuIij֩kV)ĸiD/b¼qZE"2"y]s48DB>B=CuNVرڃ'hnlˬc  @3o7`__ADG͔{b\U? *UL#/ }S0׫; )sfwןW)-GoBV{a!Qˏqy`bS`ghndF|Wfd6/=JQJB:4+*.:FSfj RHqXjaݘa,*sbPoLKO$GʺҜl_ra`XZkuulf=e’w_bP%4?LJC=52574-('+J́6}h ,rUgӁW'"8.<]{~Z: 3MI>33;?A=73/2-<ӯv#hN:4F&>ɪuK`PA+Xۨ2oH?'bG |C=ƿNtd(DaN<<"P_PE>Qo༡vD<@A;1;BEEA=9484HĬ@konI('ŲXN{ʧH8[ի7oWL$u>({joӇPnzH:9*ywD"?d\VH@EXwοiC,3;@HJIDA>=A@Dzm'ubA*1<$ɳsHi&1`{˯@omn40slq{߰4X]΅yfl18ia\UKEBI]xÄ06DGMOMIEFGLJE,biD9>>@IPYI>s´OHv{9H{dwmM͗]9^^UNKHDCJYz?:LSVRPONQOWJ#8F@D@CAGtƠ`Pyt UXqxc#tv{HAW[QKKILLTXXs;DP[^[WY\Ze?3HFIHMLGHJLCGӬoQe ǾXqt,7#$IETUKKMO^Q_p`ZV[_eaferQ>NUSQTUSQMKOONI@bܴ}X[- l߼JqbG6-PzٽHbmaeMKF &M+tͥqǿpfnuzxfcs溨љy'nS#3=" GOP$G*$'&'V&%%(*'%:`}ίogvy̙fb|Ц{Ġ+ [_?$Ka]:"#&)'&'&%(%L"3fvhegpw˼‡v}iY(>VPDA#% L3ER]uٶe}(LT@1"!%% 4 ,Jjo~ 7'(')3AHRdpĵxz  *5-6;bŲDz}zyʴ? ),hdH-EʽX%Ew|S (UBeƷqZY &%r`eT # On\5 !$dtZǶ~[   1[Rpų{somkh\> !(;a.t{iSF?>;)  "+JpuwbP<7==,   5gkd`POS:    */% ۗ t8mk@   U+k`% %o^l6 F  Fn` \q La0 >T& XJ5v) 5"1A Ts @ u 4<* aD^BN3# W{ (io9 y /Fg q r | v>D. U &L62  Q )]zF8!SpI7|@Et|sG  6r¿S Q  J|?  k[  dy =Ch"jYu!l!A-7]/5Gic08pPNG  IHDR\rf$iCCPICC Profile8UoT>oR? XGůUS[IJ*$:7鶪O{7@Hkk?<kktq݋m6nƶد-mR;`zv x#=\% oYRڱ#&?>ҹЪn_;j;$}*}+(}'}/LtY"$].9⦅%{_a݊]hk5'SN{<_ t jM{-4%TńtY۟R6#v\喊x:'HO3^&0::m,L%3:qVE t]~Iv6Wٯ) |ʸ2]G4(6w‹$"AEv m[D;Vh[}چN|3HS:KtxU'D;77;_"e?Yqxl+@IDATxWu&~ss,MPI#!$$]wm؋5xm^d, ! %$i&L]9530dltOuW{Ϲ'=))ꔀ:o[޵4rH Tŗ.% RU,ixK H @K@*^|yR= %P_޺4rH Tŗ.% RU,ixK H @K@*^|yR= %P_޺4rH Tŗ.% RU,ixK H @K@*^|yR= %P_޺4rH Tŗ.% RU,ixK H @K@*^|yR= %P_޺4rH Tŗ.% RU,ixK H @K@*^|yR= %P_޺4rH Tŗ.% RU,ixK H @K@*^|yR= %P_޺4rH Tŗ.% RU,ixK H @K@*^|yR= %P_޺4rH Tŗ.% RU,ixK H @K@*^|yR= %P_޺4rH Tŗ.% RU,ixK H @K@*^|yR= %P_޺4rH Tŗ.% RU,ixK H @K@*^|yR= %PUܺ*7_o~J@+ws E۫Oo .8PTUYrz^fZhEiԲZ %k߼XbkEQşW^%~UKē \o&Q|ǐ41>EE/ZJfղua (T~ϥ^.(SZPLqMo~Ƨ7\)i.Sdܵkaِ+s`+]˨\8+'򥿤T|NVvŠTBtqS곖_PKRX,d`0vzL6 GщBP2L0t¸KeP* Oh* qY-N{', ~?oG.h[#eu|rr333t-6ei|ҩ-NeN9@`hм}:E5gF`VsרjFFFB/zghhzz{4Z#z%fd2b RYi^b ]lb:I@pj Z 'D)罍kls=Xe{<4q^1t5_۹OZ uNze_#WXK(Z7y_̡ǟ?=%)`03(9޷*_?/׼!~O"'?xk!Y߅EM t[<.@<)(xO_Yc->m!]afP[6 AE)U* {JM}U&VKgK);N&\j!h ٠+V~Wګ G|詳,EcPw*wP0]1G׹6:/j/Oh4:Ot/v.h.SE]2 yS Z=u =nhľF.)WՑ}P՚3ʆZސD"z*ulḮi"\ÊG-KϦRbZCfMo~pzr'ۻo_AXJQu"Ԡb CP.\9 &(L HpK˿_k~x"|Ɔ߰5ż!Qԕ,IC;2+z`7qz:ڕ({PMaҶ隬j],ֲ^^4ƢupZu]h8q[?z%j(SlR,f3rTΩzWϓ3߉S'œLZ+B*\qWl;xdu-俿E]@h 7 57UOVNPE)j-m\'|2dJ"Tr"jV@g)5H4 zX,z شƀ3Y/"=% WkG`R$UN#!r:vZM8|:_q? k`t٦yqukTgC#º>,ba1X{#^ܾ:O 5֥Jǟ%Ow7q)x#x-i~*NbP7Ԙ"6Fmi.B2(G )/@q,23ȕ-pcPt4ՂN_vݨSOD8Tl\XKOuUܵ#vlZ¿>b,Z!4֠ۇ&l5JӹgAq9,++tYi.LBݰq5ۮЃ>G\:g1  BQ64>ً/2tռqoMfڍ^M[󊉖G0"( qEPyF/!7OoaJ܂}>)NCՋlt*s#pHk\Z@ZvZuߤHﻚOh+W }7-CId|%=oI9$#Z#ih>~iV`Q-xЩB0ıy ڦ$vbO!In',7ERCk|TP?q3 b YIaUOHzK5W*7kj1`^GnY@󮻾wEY xJ@ EjS$^AS HSQ?p Ln]Sm6Wg*-A_4L i.#aУU`7m>t#VA&^B]s3=,XCG,!!Єs5.l^xދHGsߨmpAjoVMz:=+8=Esa:kWTzmۛ=ZL&}OOw{~GsSAl9+"•PK]ߜKﳖ/gdw]f8s|=E͐,{07~Rm㖺r!0> C:ȓLZVR1xvl$e*O2ܚ%xuNxmYfᵰU} j6gۦQK .bAnԄh 3Ǒ..c-js,!&? nyb,:l4 SvҶs5b9p̸Jk47}=yIk?v/*0y P^e@ﳦ4|ht?SsFuΛ]?jQ*m"e؄:|2WSd`LgSbCn8Y_nHãF1_VC"W@FeN.\P  3n ވSa 4%#I KK!AO`!AR6oϣeSwՠ%{5c/Ar2IP%\BomصYyiR|?Pe5Jc'#SrCW۷nhE6 Ǜ,iTW6Md.uݳBd(%3Ih֣k4+p0[FkO-8}TyVK9=Yxj44Zl? O i52l<j d(Kyfg]d'V!Jo'x L`9C-ks6`Mpy ɂCp߯}^w(?#;Ckֽln'N@7^HxS態2O0nb(: x$U;"AB.E6méګW )4"sHt'@yt܏dĮf @(⪡$O0؈"rg3 $2Bhjn5zw-ce AyTW[J"KK})ur[wXr2;3ҴMg*yLBzN;GKYAnZ7B`ع烩׮kf⮅To=fzOCHt4j߽8c ph!/Zѣ+܎3%Y=pBs4d΁4Wd:42igb!Fd!1c:-Ύeڻ;X(ݚq}gjz~29X/|7 xi׭sGbMR_JKQmM}u·v7ce.33<.]nMGZ֣=BPui٬*GKq*_HiPxvfg Nl<%n6z/޳4c<s!4&RlhmGc_Edl30-0| Kz.k:͡7Z`b5u] ! |mp,GDaiTM:ZzJ5c/3#24o~97Xrӊ٬2_g37A QcN.aGqmȔo<Յh+Y X[arv`W_-X߄ŪKJoqKPl/}j[ώx6PcJ4T3"QtVYQ:܂,)z~:]do> .]c5BTH}Bwڌۋȧb$*/MMbqq 50?m(B;}wKA4ۗsXFW>Dw> ckn]>2D+ta"L..N0̮CGch@1ɯ|/PR{ݼ}z[\@} P<*Wsܯz{&+H`)ժK?m-n }㶂+?bLx 6g  Di;t*H n܎U}UiF*hQYhW fBX.?h8r< $0K_Z oSc^5)&6bO=N'J8kd/l!Bp` _XiuE dF%8 Ňb"$JH &\y-C E=}#Ε Tyce#cI4 G^k8H<^V'?-IVWt6|m}jljÇTCVObo*Fzto:46D#A,,cϞa". ZV'h}JJ--1ZfM/WlƻlbBIEᲧ#̲?kj)= |/PD8`/B~?{d߲9nC]| a/ٽ}F1Lb"8DYGpLXK/kkbc'hS7?bn'vkEVkh䂉\7lABA|i7괪VQ7 Meg6ޫwzܻYY53濡a m^jP9Tq3sOT [ 6 ?0:0JV?ԇ;xaۋ]$Aza^~2=q]o6~DwEh) BO"ü@i$Jiv$ (kJ!\9=?x?"wqڀP% b$=xH:*<x\𒝨p׷`ϡcO :Ma-[4Z퓟5 _"2GOB,J#n_\Q 6PR ;7C'oLl~97֓˯{_ߊ:nO͡2ʭ-hjjF'Tۑ # 9MXX^`*VmB}k<  g<ݴ 9=eg#<9G h.74nF0ueg΁)sPUf61gf^2+4z+Ƀ9@$T xĶ܈Ʒ!tZ)GaQ[+/._|/t*U Ԁ\0oN/ôaM =3);}L]+uM?rY *r# en/!׫|xq{cdepCQ~2Z3Ng2PocrބgDǸ9./c]wF|+&eB̸4XQk͍-h> 7;?Onw#8I\d@29Wg1muFt,e|IDY˷r4;p\`\u2i(mדVܝXD0aJ$8ė8_xI0F'&0=ډ#`+ro(ݏUcwg#1(ܠ}r)4RYtY L>~ HKeRSGǓ-k[ޣ>z3 &힯cT,i#rNX$J^ğ_> E?4icEF8&T}Y3{>,ԣ#2,˵gF`*H DQ'J-맲!Gġ'6 |Ɇi[=O} /~C|o'V9,z ZG͓F_H, 2@ <A%^(0bg!"!!a?r`񯢿m+60ϱ SRnjz-7#Lv ;my?JpzWV?&dMSs$X.ѡ2LTp@Aqqt|#r [+rl(l؉OlCvԫ` Z lfe,A 둀4GZ?ZT-,O5;7^w|J)rJ&{9JO4\3,X3 $FqE{fq}< A 7 Ksdxbyu"D`>X" G3F_n$t COL= v-(\ewjŽc6TF`q`~zcboM?Bmd/" 6H c8"X~r ҃ W(xǨhebNtD3p$NN>Q[|EצkPӷo 3& BF?G_^ƞՂ݃_ݙ+CqԬ#Lcsq- haL4А#R*r"yzn[ 4 xmLH,(ʰdÍ~σAhC;RXrJ`ҊHkQdai*3qS{o˧$8[-M.L?UL;ލ@gR H QpnAL~PcI*V8БBe0M'p \ :_4<EjN P)e|2iyamP}^՜9b^ZyMS@\xH/UB<04&h0[Uwźe6'Vpt_$c$$YvʥI?ڞuC +sTP.v-v.:I G-l) Z\F"ӽ0Z&q6Ha fT"JJڣO?,nW V+Y9Xf;Q(aCC\k oc"vGP{u#v'?B0lWu¼D={b49hdan?Z*izI&&43ϡy ?q+Ć8>"v J@X^uKW'F \>tK>o&ZWVŤ 1Н `o"v*qwOGe_c4|3831~XlBȮ!ǥ"OIYt&?aAA`Ίv&튈בhD_ށl** +c}[ϥǜUz!Ѯ(vn p͋&2jjeL +Y?O/bIEc-Z1MW]]I~ON11τ]a_YG7 ӰғPZ[j;naҒ.D" d.@\~EDUNߺ_,2\DY<ݹi)* sTk.\ & 누knE8,K>*^5m"sn@C %faDyR%~S͏pN_ЂeD:6"ÿm`!R`r(]ksZM(:QJ攂9SdMܚ,Ie[ݒ_@'|0a<=:_w&hc{%H;dl HNNn1ؓAI%^/aA>)e$I Z;3.$/N떦$d?>-g6o'[i3alq# TFې\@+ l+O4x:ԓЗ@Ó:ZC@6iDD,x?{0As{}H=ۚxt~vxk,7qEŘJ2unX_ ?:"pzӏ亻jآ^ ,S,1YƬ>q,/-,TвiMp_OLH2Xr4afO-lDIcyd{,= 䭘\\WR`_^޲HvaA߈.)!м P׿ \L Hsוu4MLy}7dַݎuX;(!&iɚdEGW"{E(Y~e-RJMMT.RBaR'W#}jo14HaQֳ/b`+j)d.NGŧ1NPOS/*fnRxv6=?#g IAIF rİ{k ޏcc{id؂Y*l g:r52}OrI`,?dC<+'f )'CmN>S&ʣ(nCm54E?@?E>`C;PY"mDyr\4Ш0.WɧׯCcbDV@,BAơKz^mW級b![\{m*USI/qi~p~Wrhh_m&>![uѢd:ԐU<xS@`sָȊRU+&ش`eY]G|!aЌo>pY^-~ʊ=awD5f&q1_)81[fĆP$~j*-?z˖d90d G)_ۘu[WAFL. j)J[&UF ?Gk5pD@u(u4׾5=h#:{6ȹNBF5462'I{(0b2НȜ*:d)m>;-W|X3 mo'F _?Y~d5ڹ6;8Yfr/+2D"̒&yq2BK/+w!NuI=68X3_Oȯr[Khily|e&1 q ;˰7s(=*˸9 ^79Tglf Nϓ)#+E#SlbJd T(>s}y,UIN"C.+n`@#-FgXN0|iJ2,m}.ō6/NzhY(3,!U i^{k4߿]]-<:JYg;Uo#F+ꗙOsZF;66#muz+tsEkF 6aK -#عK_f&!֐V\!-9xғTY,DYOF@$a.}酧p¥^"ܹs9 q0$g.CXLNZfr,A2q-'EY\;rC/.,k=1qg`rP-@&u}n4x5>8 2rmFx] %Cw~wYA5 ^C@~-J&A󹜒>ؾE0n:8/n1p gL\÷^Th,47!7Jl`a#q],~ ~ GNcܘFvz~0]) }5T87Y{u6Hȱ)nx=H5O4\ˤ$Ic֯[X蹎.zILOOرclݍŦ"3^V9@T%ۗEOd/xBŗ0FxZg'p z60u J1xLЈCފ9(I le' l&]"JdKK ۥg+m R +5w UF2Ábs׿}- e JE^=Qڦyνodly kK 7ab ,xIDATLyOfME#<Ś&ںls1ϱFVU9y3s8Q|nd,YB/v~A82G4zp" ژʾvs@V5ǩ - eH{.`V\O(5)eRDe'',-[ t=wH1=#6EJ c =?&k;k+!3ym ^\z+H]7g?G0ĸcb|`'`]&xS0>1A\ 74Oen~!|i7wcPw?ː 'u:f _D"M|Eqa芋x1.8FK60#xaK: = =!͖ɕX`3OI(.FǨ^bl]ỵ`:SHx?Ng?3IA%Zq3IWtpn zi-GmC?'Ɓ'6gf\.304X z^lsd&(bMTCQH892]0D+^k89,F Qr$5Cf!5Js}qْ,b:_2ūģNJғO=sgadw*93rThXɒ:Vh`F.!Tl׵IA,ep,V$jG=ڕ<4Ls*[u.s0a쇓6sal5p֘L٨ ;g j; {m%b@*16`O&(% i5^\7Q.S7p3=Ċl K4鿏=V/BV*0' 󵊨 P_JJ y/Ax0n !t10aH%*lǛ0Z ؂7CuB'bJJy n߳Fwȁ|(xHx)|/|xSԩȶeTl\c2z@gR Q.D6 L Co]\ K]h5?r>p݁wv (3gK#`cy-nl|g 5-? 5`⽈ENdL VX agyOĕx4:ߵTL ٫6lan^0\vC3>8N<;p}4r$lnjOIxTK&BB//[  huF F}n?03%,j*hӸ[!@kŽ\ixIdST1>+-mU?' k/@T\vɹR&W&KK怶Waלi+wBnlUڈu'6L{fMf;= ҀF̴m@ bThr4Q-gmXk`0mG dL`0<' NS erدPqgm)(tM z$b-Bx0t~p̈́dן? [v#G?0<:f eRIE~)'Z2@T.|煬Qk֛ =~O=Y&)7,cJh dr`&τ?>H?g!N*Dr,ZW_AK>.Gܸ%f"GutSHF"oF:J7}FFbI)I)IY` IFHnǖm)f4kqx33Fo{_kE /bF@Pᕹcw>v `lG@c2c t1X' =1BЮj 'lk)-_KaF* C 饁H L4,ں~H,$ td>X~:;$ t%4֢h ϑr--_x}q Ꭓ3O$qmMPad\Լlno9|ͅ`8.XqLcI߸b|,G@-(QF[ўAut|$6гr[*V†t]!8I%Aor AH77bQlA7݀Ⱥ$Jv'_@S*/g!y\t>BNDbxϪ>%"[V-.璟W 5s " #!+<P-"[>\S W7VGSV%otf҅ib+] _A@k9i2Gc*,*}?mGoۭI6pu*clg[Qq*;*ugV5+nE.vb;F)Puy4A 0)QTʩ_Gw'AS| uj aOa"ZQ j2RsrH3>&i\~ipC'N@L> #:w':8u#%3܀]w=\x @ rn?@JcTY fgMLA`/B9IvbEt 6EJbJ">:s7d?|"No˜YoD<;:Nq7]n4Ljl6G|8-#_D1z+dRheHEYl, P{Ԇ=]% maN)v080Z $=y)NViutE_QI Mg.CC4kaM"Pmk(% >K@ d_!:=8,@G^uh۶m@1fDy>"̙gn+T6Yabd68t\rWfPl[mIX09,)~Qp-"--Vm^-ΧozA~U}U SYKX˰ [$ԐXTt p(M=lṾzQA6PA?Ґ 1L|>@=ل G w J rΎ^E.^uʗ_mBaR!?ܯn{Qݗ d?1j|$XP9Ci^+޼7S \(Zim|c>#K̈́p+& e:l琋}B@k]H[A^>w._4򐚓6%lwZӾWTЍRb՘hS8< h F17\vxR! Ĩ~0L[#^MQ(; DY{AŤ:OhUmꑒ&m8с/^vwNz:\e1x+]{{{.e9@K H^y]G8_Gߨ\+w $LJrO |t ^U*J ŢLb@ t8u_`LpeO`!$! p:ů,x |z'r wlbKo665fnƦ]W?KE<5(4('حf&Ix,FtJݽ!0vF1 S>rX9IzZPf=Y̾;xNeYTHзBe;ZJv"Y*#9e;*^ͽ|ŷme0تiuӰ "sM5>12rݕTVSgZG8ڑyX˾N9ڄqv8;N~An˫)@; ܕ8xÛ,Goa؎eNݰbqp/}uyT[zhutaai q4tFLG=w+6[ ɋ # f;\n…5XYQi])T QkVVEZnL<'|Tw ,6+UVjAe+'ƃ=DkEXmU5#{eZC=)kS5ټ 9ZQ:5g uOhl8hLyXfT5&'f3c ]ry1:W M@GMa9f(G` O;DEMm҉TeVEW5Cj8QP(C;)ݞ]\W!P*NŒVȕޱI73 h&8؇JL"_-0h!A\ca;v\-*!e"  l`J.7Dbg&ǟȧjd-c|Njm Ӝ:㎏͛B`F`Êm G3` Z}S|y|b?oCePragLmR4Ir_77ݹ͜ 7146J`' td =8܆K. [~A#J&`MTrlpTfv?J8]%2vT\S-"%CY}= gՀ4R8Qs9 2WD;HBC@6"zrZg59?}5Whɲ<#ٽǞHYb^1wfž -DuTrNnbF=C 9'~3䠊ɀ4fGI3]Xmg;{= Bmpg#mW]{;*j"|2d%* F1- /Bgo&:3nslZZZ:QAAͷ;ҿ1gۋܤVa2u&{;|.^Ӌ~ڼdi&Ꚕ2j֨J۟W| )(pIrLzdy[!mN`Vq  +jI8 i^aǦ҇cdʡ}i窍*_:CAg7 (`PQ8N H&Cn\م9OvG~" "_N+>5[p:޳ fj7v_>\I' AO`'ҩPp <i8Q}-|=U>uc $ܳQm9~smM+Cyh={x0qÇfMlĵkJ\;Q8د˒=@ƓWBsLggV`Y`>OWrifVgm'0Z99Mh-s-WD4 3isp_rx;O^UxWSAs}apȻ;<5ijJ.{^KV«k*".UjfV ʰ> sZK2@IcMt^q Ϋ= #Dž c)Wq6fYsH_x 𑻸S'}.9.DpBD8=fM9 ! @i  tSC@*&h =TM0" 2MA@zH `Dd"P6A 0@K7=D"mA`n zCE  - A@AZ)! @i  tSC@*&h =TM0" 2MA@zH `Dd"P6A 0@K7=D"mA`n zCE  - A@AZ)! @i  tSC@*&h =TM0" 2MA@zH `Dd"P6A 0@K7=D"mA`n zCE  - A@AZ)! @i  tSC@*&h =TM0" 2MA@zH `Dd"P6A 0@K7=D"mA`n zCE  - A@AZ)! @i  tSC@*&h =TM0" 2MA@zH `Dd"P6A 0@K7=D"mA`n zCE _h8IENDB`ic09@PNG  IHDRx$iCCPICC Profile8UoT>oR? XGůUS[IJ*$:7鶪O{7@Hkk?<kktq݋m6nƶد-mR;`zv x#=\% oYRڱ#&?>ҹЪn_;j;$}*}+(}'}/LtY"$].9⦅%{_a݊]hk5'SN{<_ t jM{-4%TńtY۟R6#v\喊x:'HO3^&0::m,L%3:qVE t]~Iv6Wٯ) |ʸ2]G4(6w‹$"AEv m[D;Vh[}چN|3HS:KtxU'D;77;_"e?Yqxl+@IDATx\yo\9w&0gQH[lix[=4h %Rf&A htU]9ٷHxZLJdZѩ=90Mz؋^hE@D@D@,Z" " "`C6t YD@D@$D@D@D$l8Hh  Hp5dk" " " 5 " " 6$ `IאE@D@D@@k@D@D@lH@!րؐ ']C ! N," " Z" " "`C6t YD@D@$D@D@D$l8Hh  Hp5dk" " " 5 " " 6$ `IאE@D@D@@k@D@D@lH@!րؐ ']C ! N," " Z" " "`C6t YD@D@$D@D@D$l8Hh  Hp5dk" " " 5 " " 6$ `IאE@D@D@@k@D@D@lH@!րؐ ']C ! N," " Z" " "`C6t YD@D@$D@D@D$l8Hh  Hp5dk" " " 5 " " 6$ `IאE@D@D@@k@D@D@lH@!րؐ ']C ! N," " Z" " "`C6t YD@D@$D@D@D$l8Hh  Hp5dk" " " 5 " " 6$ `IאE@D@D@@k@D@D@lH@!րؐ ']C ! N," " Z" " "`C6t YD@D@$D@D@D$l8Hh  Hp5dk" " " 5 " " 6$ `IאE@D@D@@k@D@D@lH@!րؐ ']C ! N," " Z" " "`C6t YD@D@$D@D@D$l8Hh  Hp5dk" " " 5 " " 6$ `IאE@D@D@@k@D@D@lH@!րؐ ']C ! N," " Z" " "`C6t YD@D@$D@D@D$l8Hh  Hp5dk" " " 5 " " 6$ `IאE@D@D@@k@D@D@lH@!րؐ ']C ! N," " Z" " "`C6t YD@D@$D@D@D$l8Hh  Hp5dk" " " 5 " " 6$ `IאE@D@D@@k@D@D@lH@!րؐ ']C ! N," " Z" " "`C6t YD@D@$D@D@D$l8Hh  Hp5dk" " " 5 " " 6$ `IאE@D@D@@k@D@D@lH@!րؐ ']C ! N," " Z" " "`C6t YD@D@$D@D@D$l8Hh  Hp5dk" " " 5 " " 6$ `IאE@D@D@@k@D@D@lH@!րؐ ']C ! N," " Z" " "`C6t YD@D@$D@D@D$l8Hh  Hp5dk" " " 5 " " 6$ `IאE@D@D@@k@D@D@lH@!րؐ ']C ! N," " Z" " "`C6t YD@D@$D@D@D$l8Hh  Hp5dk" " " 5 " " 6$ `IאE@D@D@@k@D@D@lH@!րؐ ']C ! Ǭ!}c~[?a^._\$.Y=,C>}?ϸ.=Ո| wt?nZ4k0a8|ìUeYu^h4טϲ~U*SZo^$ OO~t"Wtk'7 fͲt.ׯs5[V5Qyw:aGͿ)XװxrWD'E@'E^{YvovO{AX[\Z}+Xq.w6x*K~inʭSkoK힞 ve^' 3gξ^KyC֭wx|ɉZq-QO8ysB3\zO^|?,%/{䏾ߏ|>|OE&$l2C2l1u ]{\U.} aJ5?m^L}0ԸJ!2]xڶ~eMg)V7|EWwmqۓ;/;ٕo.,,(F=ix~s||XWECD 󠻸LXla}e`nIXpMjiLpFV-WRw\/b|\>Z l\:X|y5իt8/+t{aIhhJ&~^r;{L?tGO<0$_uOPXq;Pփ17A@4&. w~3򪑑_+d`9X͟M͸ʵ')y<>Qv٣X;`VkxmΙ:_bW57{ܞҒ7B'ԁİKf]5 w!5PTrz*^MuFGS{gNBRҾvKԽ % ^v]FI@Q.K^zY< +uuܶ?֔)EӕW5!fCh}KacX.Ϝ; omy kn^wey\qv"O_Dy`2Fk:m&KtJJ,3 BXM5?cG1(9u ?xXWDG" #ӋEu#`YH d6߆Z[:1pZ0*΀ۙ2g-K:_j?%44Č2ۼ7%vh/ı B(6Z n9~_drKQcP˥jƌ7D{v_zZ?tߚŋie(Vg%&|FM HBx]Xzԃ,W:=]񳅬+XԜUG@xi<2h|-qϝڈ[G1Ԙ3iϙ"1|dڝ )@2=>x&_±r pv>dq畭hsJqƌ?^I͔gVk\yg7өz.eXc^\l|\:rx=kmF5ˍFf/-f1\a<gc?B?i1Q@K<9kD++$'Qta'ո6|Ha,4V|ܴ1-a*;$Hbfz t57a4vj2:O=B*/'`zwe^in߱k兯|OGrSP%#^mjm~,$,3q\vhHz͒ey =pm9̻aoсΫKeI,f+paTӌC=ӄ߿Ӆ[6˵n<75tlZHS-7!6bt?C'sYw_K^h(y棥jaR3r4$ T{^lz_V7 7tj0Mz͂a8^wc,^һeeҩ겧,j`ӻًYzht7W~YqT.v5x0MƧ? ?P^X*dpEFT7ڊ|7f.brbI4@+ |1q{jM 3պ +YP[̤W77Wd)w{]Wv,4]j9p͞nU$!\^hx}vZn b})_|DT:3<2+0 yU8:.ftPeRD++p 0ྈtxlpj =.\Hh, w:}y|jI݂WKgkW~3/@^ w/P"yx,ݢ[ 0`=D@@-ϬgwZ_#"r\;b;K5ۉVt&cxo11];s,؎m=8J ۅt.'`XHɩ$f1306M2p)\"^-|nObU^"McwzW t%cQ)X jhl!Ԉލe46>/ 2=6xMqݕi8h@f07=D@@o \]F*[;3W\YnU=L><[h4Mk5`*dz*fh M>E77KrqP 8q,7{~ S\@V"`i| LLZ}+n2湃_dҘOZD HOpbև`.+X=eke^GlFx͋-CyWчLcϣ3[qԪVc۵g 7^{|[epƷb$$t#ؔw4b0\Xn ]uK!ҰQ; 9W溮0ض {\p`5</ȣX2^`yڍgpln[wl[0|Ax&!T%چ +,@s~ H`fY4V1XJ$\b"C?Ʒ!ubЉ,:} c܆Z18+p/2#`{wp$&[=[؁ ?g D&rb/^(^}k1zٹ'gb$e>Ҿ'S(58Sjg}-ƭh ya44#22J6 3ֆ~?=.a~~N4Q;#hzo7sy&H-0J\eT5WFq&3q`b9^V,Qc Ҡa .?nĉs /=sUp3x *ޕog?wx'>:Ƴ&g茍 hȈ=]mߠf0D1-p0n]B|f܎:YxGe/r/}ftưkMj/ǿ?//^Kim)" (/EVVw%4iSUgȱc:ۢW#xv y`0CTsiz73dg&rMH,f>GC>ȍ|85a/.fj@{i 0YW wNpNYppݝH9٭B`aGN0Bowc?مv|cӈyf]nmo{[9 2})" H@<6z 5C׶ͅ؛2+[n@w]7ob ~D,dsٹ)ԂQttu@cc>.WA!AL KM8q]J3|X`.SB)J<L7O⧺XE0au3s მ&">,#.\Eber KI,׋ hd%NO=j\8 C ?fƍ3fP5{Ͽ{׆֯LYѳE@G>sLD;0(^Ix3ߙvvmz˪uw\Sxe|9vchZDٙ:iBw% N,"lvnlܺkbee^.˪ˋXucb:$]7~Y "wv 0J: ^̞:)Ǧ@(`[>٧LWq˶%&{Yε@On] f]<0wz=q ZPI/!CQQa l{syپ Tōʳpń2rŚngq3gbfeN|XshjjWY؜<6_Ge?M^hJފDG1~ o7z{B|V4utGg^j `KLyoCs455hYC w]Q6 b@ -}ni ,^58*|/\Ari|K(f>ͻY7D_GׄOcEXJ8ǣ%^3~bV=Y,\K;(W]$ò?Ow@b.p4@OBqOo-p5}Yu>x ?"c cȱ m,DIxA8R'хoc54IeUvWej Tjv*@E^u HW{rfr1wTͿMtS [U:؁Nmv+ -gyr%\6 /'bMЂ΢%5{;܋> Ui a֎[?{W;Eu[-Y|  f?g,DV<}旧`\9e;(ī8̪>_m$F|xSxnUpp]r VcGxgj! *c<<ھP#7܆ނ/)_K#ÀbW:0k7P Ϋѳ0Se}WDDGN\~ ':vaA{E{qKSS g3~FAVƁe|[_gi-@1 g/bzlEkF(|g,<ݏ?@bp><o?s#]-" kw0jֻ-C]v|}g> h]g1 4xsįlwe  W*02Y$=?ucùy}1[<<&V!oJ_oazEV r^eD~|!}y >6q~M ]toӛZY}\y1pך~z< ߹OQ$|"ֶp"ʼnGN#<{ BW2R{V̌[Q;J ~ ߂S [Aٽ0[0e^k%_?Al/_ Ozqq dYfuf'@Ex^{ 0`Sa2,` ,P@UVKP!sm};؍7x[h3{ǟyt\^Q+s߻\~(; zZ'elL1=!zvBQF[ދSF>g<f缮\՘7yoy,0=ăv 4g˸o~y0ޅ g"I jC"?AQ. ?\L$jX$e ؛;ا8E,N8 4ko=EˋAQPF>Y@P(f@?\ArK689z~j-~j3`WFGr;$~ R/ٵ]ssnX=)Ui)C?<~iw &<]oV!+_rswPi9< X!=oq"^!Ȃ>l~+, ̎(M|0劉6WhYw*S{?T!H>C\oẮ"?U/K~>c )Ll ?K +&G'џ,,hdǪcbq")>fPDa,T,0@)vQzXDc5Ƴ{2ǟwό p CX凼w"/ HKN^ G,ZL8&ߴ\YlapWyX{Ye+.g_*ơqu{ +-ʝr8;t/3wꔗ̰P'@+?U|XF],`ö8ZE8NmZM,8|ai k5! "ø@*АCtl&3 iZLJqW_Nt#P{І-77߳gPzdb*+4/0pr^~ݿq9z2 4L;8cTx|sW"z'ܻD'"pokqcuaլ`=GH@@@^n^s)~:*wiFW ob&d@@Ģx^O'UC;kemxįrpB1O@e Q̂?1'yz w>ҝN7uLX b*Hz9. ܽZlaS2xr)lZ}-W]!Bu &b}s2nf/s|ox9܅c,(2(fsnxlh'G/2>&~'O}$C oی*DϧQeR= F17KU7bbͭl0l4w VJWG6Ati'>x kZO/ νF#`ſNgn[KFkf+r$4Qܹy,M`rrmՃd2/o`t雵 ks`s9A9ւH''OW1OP2]H/GhTHL{>QRz7ӋUVł|4%BdSD,0gpӃv<[ ZteWb3ENy\ ``#zX` صqzn|5˘Ggg5~z:6\ܷ e`{ g) !ut f ef\>0Kr9s["XYZ~eڌ" e-/]:J,:Tqz-Bb[zfҹ a=?EWyF_~,PQOgg{e_;76a0~ER I8 R?W`}j0z &qmV 13v#sۏu/`]vVL+?EcBsK,Ȳ ,7LU^ECtבo#fcp G3\xi6i@&D^0C!dGP0 kjc8xHzVYN}i-1t!kߋ4x~jAx{g12ZFճ lJf<أ>kG k֬l}ˣw?e"`+n G$oxGәI*z\k6)lD1zC4鋬{v4fKab܀/SUܻ|oTF1_yUDxLCw&z'Xbq,N>?Xu;; a4>$Xb?\ȻZ^=ƌ(j|~) q} 7p3aMZ{=?Q]nLG,|!\shۏ;9 a#ጹ\yB mσK㈻V`LFBzY`x+_5V[c䞾_>qǙn89v>S=Go\$ӫ,o'nV\3z"MwEB+ /HԄ(kmHnùxN,?kG (g6!k`glx|9ޅ7cVw(S4,T7e1s@%c(Ow܎3F=9c`"ë27mnݲ8sa3sgWVfP8O9{׀.%o*o`8/wWm=и}Ʀo=n!6a$?װE k32nvCLKSa*X6= I(:ܷ4> ssLLc$">R&P ;;ބM?+̫ySrY1XQc~nrZ. #؉X,ʠ@h`=jr?Y)fyR# {ٓFVݜUڗ^2smث1l Ѐ0+<˻Al?øiN5J 'KөP?V߀({Na%= >H Q1b陯c$qb}{ 'd?0:z/B` /ؼqBW˚e=}zZhWP.G|ȨfynnKkikn[i,ǯr цf]9Ÿ<6ba,S,8+OWMcW ;P$Jh_M?ۆډ=X!^<ׁ)?= UX[`|%N*M[ݛ+{Z+EYN `ʠkJlm~=cWB56)ZQe`OqQ>f} ΢s XA*Ҍ#H!,AaQ@m䇢D4ccZ;kV, 0kXiT)e"(h8(e#2IR.T8p;yP=5\H6 ot?2jJiBbY4:FBXi:V"6nCCЍ+G1 : ˵c< siۻ0yd>w֙ 6qlc s~=5"2/|/"} ~ஃDe@$ fTXЖAÓfKbՏs }?ue=f"q ejk }x.lΝG@gbfxTXJ`GkF+ÍdpG@X,ҽnD]L yx&YXclp{A}lSv"Yjc_ ̡@IDATsN/Ƴ~ҧY`'@;jweᘤ*ᖮ4p* G[?%.(HRt??fKpn$?6| V-0Ujr<`8`Z s.昻n!?kmpetGoFzn:yc W`A*Ī7^{;S7EK8%f]8܃+жƖ++J&ȃ wເ[hF (U@$ڲnm|]V! "]63rլ4ar&f4Gs ,CxE=h@gͳ^,cMԃ[81L-u#WqX[CP1TŸ ť q^9~8Ņmy߈y?Ǡ$м81 ̱|U*xX#7ps͌8w/4= s3= U'zgx%OVHM@^c.NoB>9Zwȏrd8h8Q:(5o`]eoUg(śQ mk TXo07jYO /4m1 0%8s<? quNs}\ss( w qאK(< 9Е3ϳt {l9 ~qtuvL(S8 hE7EqEcȇ7“E{5xvۑhAQ^Z4DsXN2+ (1pHHH ^=M21``~$\Ɔ`kk0ΡJ[zz 4t줟}v[h@82Q҇bSw4ϠHpsv ' ,(`q!0u.9h#xh܅mc@ w ߆I};0>%ncO3]O֛It2?]4s''q*k^lٲLыc>`VD/MZ9g~!.ojr2k>eFlH@!&Ҩ ~ʷ ./@&l[c$Le"_(c}<#sK H0/]hqgۮ,\ cajFXx٤(Z @ Hpcj>߻NG}#JAnjls5yX9V b+fJ3-`{K\p[W :u1n0!BS,$|Q8ٮPh"Sx?#2j[a$`})z>l]݀_8r5"Iav!>y^8=|iv##͸C ޣ*F&]<ۯb& ,cKTC'̓D]w VS,-iܫhl>"[NOOt0e_;-o-P@0>Aq?0<|;x(hΆFOPgmtS> <ֽKtӐtX4(ǣj~d ݿeK(xYǪOppUנ/yTbxa[1#L}l|N)d~!8;fae,dnKINRhaQezVXppmʬ#N&˪yrcZ"Lk>Yk![Bv0b ssgCQ[{Xߖz ˃P{7l@[s+xq#+~'yI?} "ܟ%Y! k^b<`jj/bڵذ~=3`|^l޺?>Zv1hK^+0WelLH@s&'Vh+gPbzދ\8; [ЙBȸfKP0"z ,:py,,+)Yh^EzB̓X6&G1ل#n*/rht59LX'8z|~}+ENs0R=_HH5ڻ踏N $;E -*V;'|i󒻔%K=;vΎH-5˔e lA^%#ʲ~@` v377ZfyjT`ʿ gd9qorh{:Ɲqr Ԧ<. J{{tv-0 i;[뮔wxXN,{AQZW{ ؜J%dG1>66.4.QǕ\@ݎ2]#ڗÅD8CuWڍ`p&oQ$جy{!G"Ndn4,TfD(ߏ >(nEK$ ՍjA5P'X֨E%MXTؓBlDC҄z;Ϊ"6 :e@}Oߓ\+6'_#PhL sV'ߊe\NNsr gF_Z[I8܈KweIGZ$_8Y#+XKO~FǠ2.6߯Rۘs'_o4I n5,W$a%@DC2⨒lF@_`35bv *}~$=In?sH]併巏kw54YL١Yl.\ x0YPC]X,SwzY-pUc#[šl6[k]8B^trZv$ D'%Rndy2=S%at7 Q/zi F*]t-)y_>*g.yȩl[nAӕizγn;Ȥw"s/B-f&x&%k|> ;؅>){i/ka6"PfŇDG*b4Wȸ~oGKn_},,YS3&?ZI4сWAG4F@dGt$VZ/e!\4ʝvwYds27F>jV׷noM؞\gq**>@>"P?^@"X| \am3fcz| m$A0+%s>WK^\CZf&ĕɷdwODPjponMȋ^S_ pzAi 1PO}݇xu lL#X_w\ ף%DB8'G|&ֆeEwX.TVni4WRiw?[9:8!a5R _8cerZp01+LT:|5+'T j~5LTn&YvmeoZfa YP>݃m"Y9]%g%~VRk|HUde 6w{-(r ^NS`^`~ _KR7qlSS+@,}tBҸM/Z!l8kBW)Lx`FI1uXYZ K!: EKp 5Y]ur|';GG_ERR/Z׌v:^ns%S qoE㴔s2;Kpak[/lĥ<ȶUq:Pݻ;Pw/]s"pa:>,S'FBLs 0s*3'NY8<nb?GfRRՅa)GaV[{QwAd߅ )kO P% ^B'(RUeչEbո ձ;,$@'wz;#էH~UP܃S\VF\V6izWXA\72HWU^-/,,An Kٸr՚Yta\{:{_ڽR}nw%k`1ӎ|A>vDa9ImISO])G/D~457_8V#V%dS+K:q_@ #|bNU¦Dya Ty 5QW)19,A`IE@vBrkii #`Z);|RMǢ,_MA h 4)p9 bj}._re+;Lc[g#9-9 A.^ƻ% [ƯK7Y[+lǍ<(c=;d;GFrk r)cm8V~uKSKp՚ڬvЅBGl6\d>K%GfVB/Sg?囻7+V˯1'=,)w祐ƦӸ5pQ^5|j6BON^CWUnP#Pji%ĴhZ86,W9**DwrZNoS"HӉA`;꺩νz9OYT^E@ o^(pA%P wXV@.;ySL.I&+,ܚgG]N+B ػEJCCG௒TpJtK8_9I1-3}r)yh:jtm|I>;|^T}tLMpY;Ђ$?-E)rr<$ŀ7J*6{a4ゞaCbF%9 ͸7{P~_o?Jf@':i^O|S 0G?X!M;w~|uxo8ptlڼuV GF _={KU86[Z̓M򾿨/>/| 6b!oq 8=Sь=o;?*g3.kdj<35=6<O䟭`QG'^" D*s\՛}.77wlaYxw-2fmہk^*'g@tfd9DfSv,ʁj9JK9ˎ7Idz1rOJNJ5:t@~ί$bSgD]1m<k۱9q 7N,[{7I+" 7pnFE{RB,0S,M"v92P.'r*|)<2t7kj/}+/^w̃㨛mcs[ on#6|b۔ܺU c HvEbwJ qՇE&< JKjY)|xKV=3'oOô,µĭf&"7@͏U66@g;+WKӳcrs $0jiDo^ž#gl .,rw܉#|Ͳk׮_X8Tǯ>qD0}, \v907@4'x'); Kω&)3?m{2V.+b97p,S ^jg( ʣD8bO܂|w3Ƚ}\oK{o.w+ѿ cQWgm^>-k/!K2,F;&p_N6Kٍ+K.LA$JJ9CyyB rqyb`ΗξC~+mM;+oV~YcV~a<&e0^+|fGc2c>R@Ngz4%SJ8!ٶm>Ca5:|jc I-]"睵"}8+DddM[(~o%Ct*b,B~I;25+s#ORB)x'"/fȫx0R .')pytfز/lYFqO}b.;7 *~s EǮ) vIdt,z"|t8 ݵI"!/v\-XF @^R Hu>zt!O[s|d㻲(z%|h!@}M6 A1Zz+I'$ٺ\%Ab @(b_]?_|<*A[:"r%}#fFA9^Zj\wjd!@"$Jedg䱃b{5c?#R1K'^<7-jNPBR`p>EVʖ&7dcǎ؁nZcϠG+mnO;$ðԊF$OaG q/Fe\|Lz.#*_RwI3n t(FXW|Y2,BiE }.l!qPQ_+)0p#^K6u'H:ٸjgg>Lg {8w~tEOц%SQ\C;A*y{#O=T*_u:> PIjHb|B2w˃/(:j%t N%O(#oVo9|&nL-r<8?)زP^ߡTVhn~~oF+bz6|tcr2n7vTW*CKr:{u!]2J_DӃ2Iʶ ~XLv@>xXBת~Zb~%sq`AFj[/.ӋZz9Ӗ|q)K.t,Ux'-G/!\㫒 ᒞBm0 pC" u{_ 1D"$?&32 UW"UYC#'R6,p ` nȴ2鏦dddv)lܢ 6ZbE%PԞ>(@ X+ %BoE['*'kwÚ5k5o[[mbI܅.j h}Շ%_Ekq;nK[Ce. {YgM=- ixYZٹ⼬'S pt7#{J\&?[ekV\UN8.'M',_Nyiܙ:}X'(WO媎tJuR" ߰A1Ǧƒȓk/(};2Oˎ=X~>-:$wP /]eWᶿIC4lS&VaGD$"'1}mѹl57s˚de+8w"G 8Oya_u=T( ^و?A g*P:} ]Դ={h={=~ki꥿ֿjyvUm!zEi3Xbnn[L|yfvS m–9-Dgщp-GJ_^v|dg'e}p䑊-\+q<0.]W >/꒎%Ke ۧ/e >jJb=Tmh ˊkwHˣ@v\{l-ÉՃ=3>ؾ ;nNwr=ylW=}z?=] Qj>(@W`F \XdIewVKf~ـOWCT8.%tK\&񷞑owMϮyp~C'7n^-X[esŚ 8;L:y46-Y{FېdABˤC#֔2 Dܲ$ȸhIi^V"}_*$)?8q`CZYc21yR" 5Q'!w?\fE0(Һtx[v9KPzoLs:Ӷ;f}u܉;l{|h띞-U?1(@/M?އߥk%,VTΠ|6|s{hs{ҍ3kmWyܸ$diݜȔ,a{}-~oSc܈[qr|~Lk#۞%Ml;gA7Fd{MK=ܭmջUYy},uMAïA ЈS 2Y[/tI wS:[ Py.C\|^[`wa9?}Iu{adO߿<1KRybj՚jJ+.. .'^y0YW!*:@jVT%d~PKS;' T;^=v]}} 褯-a.)>FǝoYO?YHP.q`r9_`wmETWcZ^U#h\z$d I9;RiL%>D#yy-S~Cc -pjǿ'Qz,w}e/nR:uJ33|>PXryV JK 3pf$e5_I 0? ArZpN]\S.ՂË L<7/輵fn|Ou<#\6v:v֧B$͞vȲZUĥA{ĥ^fcMjBo~iVGK/T" uMɖiɞ,_)o/IQ%A`#.I8I3ȇ?T Ψq C-+ʲ 49ş$gb_k"Й- __尒t&3GƧ﩯 [OS5|Onv͊_cbH ބ22?\$~_:;:'=,by^ZifN%^ ;w@A*YBU7YSFepŎ#_\}~M J|wCa;Fʘ!U UKmS+EC4<rwK׵ͅt{zPozкUvWGH8ϒTc|^鍲s[PC찷3I-RDZ~ޛʯX^*gըCc\,TWYyψg<._oO P`F(5(ЗHY<+ j+fz0'yzh_NNߖ-muW\ws&_F]=_;{>ap8E>ܱ&)mMin⌴<$` Xl]B+oAGaI#J@:Vظz}ѿz H* P `Q( \q.䗣/ W. q붭qG?Sw5kE~UH]5#3V2>yV+永2n!6Qůr#-pⲣy,Po7 (. _|ʒTgy1.ONMjnUa'};'k5r`7N)3fi/%pK"Sٌ5C_D|Po%o{k+`@%FX9U`Y'N}vx֛>aӶ;僧{`-ks)A?zv?fk!MsC ߤϋʱAuO*oBj桒T|ajxmF $ ~% wDĞD}'?U+{O-;ۼy˒:gF>iD 61c :~֯R";|K=^c~v޽gϜ95E~jzMwOI:(T.#g"ٯ}K^ao[94k7l̷܁nk1l;q aX85q?+Z:<>ߝL&CCC!ƛTi>(@@#-P@ }(@b=)fѺ[U^d7]tj˾w5Wl/o ]. Xx/H3f6k0(-Xxsv Do[A 58h P*l3~C(@ P@`L P0@*Rt(@Ȭ"(@ P@`L P0@*Rt(@Ȭ"(@ P@`L P0@*Rt(@Ȭ"(@ P@`L P0@*Rt(@Ȭ"(@ P@`L P0@*Rt(@Ȭ"(@ P@`L P0@*Rt(@Ȭ"(@ P@`L P0@*Rt(@Ȭ"(@ P@`L P0@*Rt(@Ȭ"(@ P@`L P0@*Rt(@Ȭ"(@ P@`L P0@*Rt(@Ȭ"(@ P@`L P0@*Rt(@Ȭ"(@ P@`L P0@*Rt(@Ȭ"(@ P@`L P0@*Rt(@Ȭ"(@ P@`L P0@*Rt(@Ȭ"(@ P@`L P0@*Rt(@Ȭ"(@ P@`L P0@*Rt(@Ȭ"(@ P@`L P0@*Rt(@Ȭ"(@ P@`L P0@*Rt(@Ȭ"(@ P@`L P0@*Rt(@Ȭ"(@ P@`L P0@*Rt(@Ȭ"(@ P@`L P0@*Rt(@Ȭ"(@ P@`L P0@*Rt(@Ȭ"(@ P@`L P0@*Rt(@Ȭ"(@ P@`L P0@*Rt(@Ȭ"(@ P@`L P0@*Rt(@Ȭ"(@ P@`L P0@*Rt(@Ȭ"(@ P@`L P0@*Rt(@Ȭ"(@ P@`L P0@*Rt(@Ȭ"(@ P@`L P0@*Rt(@Ȭ"(@ P@`L P0@*Rt(@Ȭ"(@ P@`L P0@*Rt(@Ȭ"(@ P@`L P0@*Rt(@Ȭ"(@ P@`L P0@*Rt(@Ȭ"(@ P@`L P0@*Rt(@Ȭ"(@ P@`L P0@*Rt(@Ȭ"(@ P@`L P0@!&IDAT*Rt(@Ȭ"(@ P@`L P0@*Rt(@Ȭ"(@ P@`L P0@*Rt(@Ȭ"(@ P@`L P0@*Rt(@Ȭ"(@ P@`L P0@*Rt(@Ȭ"(@ P@`L P0@*Rt(@Ȭ"(@ P@`L P0@*Rt(@Ȭ"(@ P@`L P0@*Rt(@Ȭ"(@ P@`L P0@*Rt(@Ȭ"(@ P@`L P0@*Rt(@Ȭ"(@ P@`L P0@*Rt(@Ȭ"(@ P@`L P0@*Rt(@Ȭ"(@ P@`L P0@*Rt(@Ȭ"(@ P@`L P0@*Rt(@Ȭ"(@ P@`L P0@*Rt(@Ȭ"(@ P@`L P0@*Rt(@Ȭ"(@ P@`L P0@~h׽IENDB`icnV Cconnectome-workbench-1.2.3+git41-gc4c6c90/icons/mac/wb_view.icns000066400000000000000000001311551300200146000241560ustar00rootroot00000000000000icnsmTOC (il32 l8mkit32et8mk@il32  ˠŬh 61 I/ng l l/MNptcѡ-h #KxgʪȴHi4?}v>oe]j!e4q^U=̠tL'f~Ha^Y͋zn$rh jy]۝QqvĴ$eeR]tyyaFe޵TVRta^}iΓȱqT"i4Bj+עᢙO~U:_-zбⵈfA[YnxƯH701qԮ~i90ZD~.Q2aDΰpJTX/V2ilcMUT`8y}p7RYD tȀjeUTRTtϨc4 Jg/r6qIҖQe9GŬ\(h 5y18 մsya ˣe8yo/Is~dXI7EWrS =͎ĊILW6|x ˠĭh 'I( IAld l rkWtLvmo:h '6epRdwGi (M\sc5mJy?!fTzGveƷ 'g ecjNtdc|ƅ#k6t{r{z]Qjt#fk`eratBMgg1[V_yZY~Yps9pHdy7)pkiOW5tވKxiGOudXMz8(Rݢׅōcs8dmD[>OL:/0qa}mZR`|RMBG_ss,fʔh  dD C>{w(&{g!h #KK~?w/|ڑ 'g>ꋝHK5]e`sƒ !h'xŌ)B[efpTfPv#e0i͕^o7IX3o{'ScZ9i dSXJXOxl9]&T8./S[LCB-u$s:S?aĹ^1Ik&M=I|ib60MU0HI  &=-g|HI]U=LE2P?R{>?4RLsǿʳ4  &A ;]4OM?M#.z>WJVxmƵ`dt| &A 1;Qy8 GM4 |5/S^rͬsWp͖Ϻ &C(+8*:Kmq7ROIE"de>Ucp{l|ᶹp ֒&C4+1GEgG]cSIJD2ðtj}}΃rtpoY(e5s9&I67$ȥf&GE:FS7ΗnK[IJGF!Dplyo{ʰmwTW|̲*&Gje76 RL[ռ]WL=>BDE[m)Lqjz*slBI˭]_I/4U=&Nem+~\@ڱCPqjMQXm4^Xy%ֿ̯/%2&R d>?JSþĽƶOGS^wyKZb{)แw追ů~5X>&VY#FM=2QŻƱؠAOUj}\WZk8칀hp˞w!)׺;a& 򄧹+evtۍŞx\S>e&dtƴ*'bqоԛӿGV\lHiZl{BSmetײwdSd@Af&=ʷPS[ Wv}ξoLYd~|LmbpE[ckrkpyƲbMwϿ/&fһZ .}y›OZ`pZPqmzԂJc\adgnuyz}Ǻʭ|dٛd&3ۏ^ mlwsX[_nVMtv[O[Z`belqqryȴڣw&& LmWyr{sro剁<׸iTjk~JKtτBRX[_abhllrǼSp&2gyT3Lgq]D˙wwlMrr(Nu֘`FGWZ]\\chku섒äp&g}:K&$:t_Wc}CWntCovf1`rܤ`|]CMQWX[agnubop&uZ]>ÀxkDUrTJlHfxUd_dB?DR__eiryʉhֻ"p&HɺI~[Gt~xHErCpᆴųVZwxZOiwXZ0.8F\h}notϤZjp'ػ0> ruqxR6wry~ϕߢj_g~ӓozL~A;\nK3uvߴpNp' t"tn/ h_~xW5k3 mVm于uoԭML;YQ;tʵ΀LzFp& WztJPׯ?kzU6gЩ"NjUlױ~῝wekPTwܲ}gPDPrkؚNX^o$Ff$W`nwyxøL~R7d\}s~FS]ONuնƲl`{ޢ|zo񟐩]Pe>g2p%|opt~S 0qS4bʮG xBG[KCIlǡýiGǬpEl͑Cw^HPp)*}~z$bsR5YtzK6HC@-sR=D-EA>86Kqʸ\«Ͷych~uyKpClĵ&pQFCiQ6J<7*&FkȲYʽ|Ͱ¿u)p 1aȿKaXDIMn/-44+"&7eڼƳ`رǿwsv˛Tp,GW{µzY) OZfjWbc#/-))-RE¼f˵Ͼȩul{nоzp)j7d}Ơ "fVg͟[U>37DC62LuI}mɱλkmܳAJ`b*p,Ea\s \S +k[OqMUw+49GMMH:17F[wuϿכʯtfv+6|U_ȭAo>\w΂;`,h\Y[uk_`lew -0517;;9410&6ՓdῼþZRw߬X:ќ5p@pzʵzPss3jjmbMNUxaOG[Z: 3MH>63:@A=73/1-<٢ޖvY˾uK`dEܧ3pDTnͶI8hZm}wc\P=A?HuueD31AĴsHiTz̯@qt5Vtdt]#Dkajվx@Leb[UKEBI\w8=CHMOMIEFGKKByъ`F68B8z;PY݂\ôOAzBAJzg. [;]^UNJHCDL[zi ?KTVRPO,SPyĭgEACABF=Ơ`P}i^Y(t\Css= !ו AW[QKCGB53XqDk9EPZ_[WZYaU?NLMMLGHJJEB}ӫrKvr:}Ի(~Yopa^觀Jmĝ`ի(WETUKKLL ?W^n dYY[_cadkdbRRSUSQMKOPLKBb۴SiZ/m\Cq^KᚈP2"wI n;TOMNSH#0I^dktwўiZgiquztjdXSTVXUOOTWVNP:w་\gh"KEvޕ]%VrW g5RLRRXbglmmxw˴ƎVoq|sg^\\[WSU\^^VS )OLRU[_iTnR]Tuwsiec_\Z^eeaXZ9t߿j̎څ/FϺ̟pMRJ 2IRVW\]n%q' ^Zvskgb_bjmi^][<ߵqV{M+(BǦɫxi:lPkVBSYAcs {P@7ڋb~sjc`eone[fLaz j"&4etȧԸX TddW"KQORYo`9vgxld`cjh[\TxFt#'{Nb 4!%~;/˵N&g2<\q\'h=\G9EJ>/ *V9|Ķp֢  n"  Ơ0KҴZfo|bB|oCGdijcd[=&N#%Gæ#=eix{` 60Jia*рxǦS>ftwIKiEG*$'&'T&%%'*&%!P-^nQlZNrs_jj~ߋ[Ǚ{]FqwdWhY<" ')'&%(%L  Lnahowům@ADqnϹVs}̑lLIF& L3ESatZ 57wFg|{ !'<`|jSF?>;)  SioxucN;7<=,   CUfg]WUR;   QK< ۗ  G*$'&'&)'!-^P9$RklIYxY- &%+\um=8beM,,SUKVwlUðɲH &=*n[q:8__WA?NZhE8Q?S{gS?OSyWw~; &A 7ef2LYgH@KH_bFRLVwͷabxs|EvXTP9є\X*&A 5;X^݆4S`MFLCICe>GP_q˷ƮrUYCBTmf}a¬Og3 &A'3?G=:PLY?[WOHGACoo^ap{Y}{=CdWUgbTwLq\=֒&C38-3FSMJFo֦>]aTIIF9T~³sj}|qN^EUmRpOkwj8AKeg|t\jd9&G;KH0<\O;/\rDdULEHB6a{l~wuuJ^jYYePOtYeg`fȦg&G /?Q:V3D[K/]ӺYNZHHED:OmÑOtq|̼WFlYX/Dl[dx̮~)&L VA::1]K8l3UҟPTMADHDHZmoybnk}ĚBLeV;>\xh`~n^b;&N g>12D`&Ckv;J867BGN]sX[\dmTWI>1Pf`mqUHs}]vP{M &M muA:Vc-qpf_phKT:?IQbvysVXczMaMM~~NfIнva\wc_&OccCPd7EnBi~·ǘ@D=ÿ^Rgùj:OPcp`D\a^J[uݩhijq_o@&T,ugxv^I4-Hż`˘6DLZcsiSYXlipyYeq~ȤmNsӷ?&V qttv`71qűϝHIR[uyDbMZ[^yOʣfBYm~}Ѵ^F˜=&XSHe}TX˿eS[DDPUdLpq@bYh|eW~ǽV6jq}SEŝzYPXTƽ>&YPdoyĵxkfoѿ`q{dk^[ڌ8MUXy^k]Kh\hmMppCRipXPv}ײzhYjjab$d&!c]blY~v}پ|fyTdiMn>XUfgɷDYjcpnfElbE\cecjpyǰ|M|eceze&MM_>kcZUn~߾~shl:hݢ{K\[zqyzmSPrmweψAO`^bdgnuyz}ȗd}ӑe&4[pDw{wb^S|wvs~xdF=QG_]lTOuwھTBRY]`belqqryȦxʻjmYvp& PhR_kZs^xx~{tHoi{lsg޴^:`doyNFuq;TU\_abhllrD_zZgoDǾRp&4P;vRo[Zxe}hobctWa[{b|`]GTosOcx4Mupޗ5CNUY]\]cgkvyaճPh˿#f&nQcDeO`wP_WaҋupRqbl^hYoKLKXApuwȌi:\syaA>:GMQWX[`gkwo[ֆJg_աWp&;dFak_{kDSxϗvGbyhecڅDrP\7lsyՍHKh}jy2MI=;>DR__flowsSNĿQV`-p&.^`}X}h4kuxHDuÅ]rEmvkZҝPg{[folB_qyZ߫Tiwa/Gm^.+/6F[ekpx{^M{_jݟcEo`Vp'ԬL}THIuq~xR6wakrQbVŗ`M_Gu}Ek]jypcҐpz`R8ERG5<[uI:MYfizv[֙q۴oSn`o"p'PBzoSDw_}xW4ld_slDDă_RQL[Sf亏up|]ۚ]̮|kg/HA>@WwD):NW\`e\Е`|STvV0p&ş=e~zqYb٬MrxV4hϼqgs_JذiRٙ_ƙHs)DC>OsxQn@++BIEJ_f]kOUaoU p%*SЍ3X`oyvwĵ?bxU4fχeezhBlBWdPJFԹefū^gת̚SwR5EB:Cr8dVC@BpE`~ZN_Aj1p'L}d`|ooui@]nU3cȼ{`gUzCEXJDNl¯q`bŒھzBFTKDHnUlqloCPsx5Z͑@y[EQp*.Ox~{WS]kV3ZQQij\/LA@9MpYyRth|?G[ISSuĐhQRe.KwfbjZ&p*bkõsSqTZmU2*%%98G\wcUɲgħncرiQOrfwl[i^/9YbVPDo@XzmAI4c̘PXUe>n_KNc`V`jmmbL987;|VSl@C-1Yɼ~ndeS\waCO_`X\jttmcKEKAlf90,1@LJC=52574-('+J΀H݅^VfmH|ij}527;;9410&6ӘpnNLRVM9zWwʻt\QwkJ53;@A=73/1-<ԬYpHv^XEGn|ȪuK`oH8Nyۨ2oGFQFi\:xZcǽJILhnADH3K`O=A@Ew~LۆWucB+55mȳsHhboY}˯@qpK?Cnquy۽KaY;qihqeiտ}6=ha\UKEBI\x36:CHMOMIEFGKKD@H֙CmgE8X[PYw]aôOqyU6Jz[YKn6]w\]9^^UNJHCDKZzmX(=KTVRPONQPUSJM`a=DF@DABCE~Ơ`P{HY*wW,tLetU|=QvҩaAW[QKCIE>?Xqo9EPZ_[WY[\bTKRGDFKIMLGHJKCFӫqMrqepXTXor>BMen_HJpkƬƅүiSRETUKKML?;8HU_n]tbYX[^eafgnaXZYSRTUSQMKOPNIAbܴTfqhvkSX[Eo_/ZWdm[ODlpd\ldWjUFkNzSD;TPLOQPKKR^ckuОjXfjpvuvreYSUVXUOOTXVOP:wཊ]eRbo{D[N2oN-^{iatjZDkxxb\nttnXygYsbWHf6QMRSW`egloww˶œXnr|{rg_\][WSU\^^US=ckbV]WYdDUo;'{í~k@hU{hHotXjl|r[XWAi{WQ)OLRU\aidu[]ɇNJWuwsiec_\Z^eeaXZ9so?Qp[bmr>p5%sлnFVoWi`as}~mR[ehQ2IQVX[_jWT^drKIұu[vskgb_bjmi^][=ݶuJ^yYmp{g"p.#jȤ{tѨxiY{blNZsUBSZYXdthwkvZztZϊUމd}sjc`eoneZfL~ˏ{MRUX]@hup'$n}ɦѸ`Ikzv{k\de߀Q}POORYon]eEOyVђlfxld`cjhZ[TwòO:mzETZSϠ7RXp$qG8ʷaI\jlkthxX\mh?gvFNOOLH6BI^`L[]LgZsm{lc]_`ZPSmqbdkQj2Ev:p%`W=8˱zN_uddZthW]nRєWYhm^a}q8DXYLDGLNP}~xeq~ymb\ZWQJey]Hvȼd8=cp';7չYcgPU[u\Xeg^ckGgOʫx_MGPXp|oQQ|y~tl`[XORRTYböS@;>p&/o̹xpXE1_tfXhpw`VuX۶kuơz^^krwvTRM{kwd_ZSLWZiSHWºnL<1 p&IbdeglpzzeQD>DB0 *V9}·q׬k|auLBGPT\QOďpz_nѷA-OT,Okwb@HckceUOA&L%?cǥzȿSLGMfjw{nA8C?Wsȇ\d|3M{R+CL:!H_W,G*$'&'V&%%(*'#)T\fm]Xrjg}[McƞJMo^A89:Ob\;""&)'&'&%(%L29Zkbhpwȴilvqʽ\l[spdXWS]NDB!% L3ES^uֈlsmt]Pwg|&.CZSA1#!%$ 4 3PliMecXogwȼu;LPeio~ 7'(')+7:.8[NbŶ{lZShLz  *!;::AHfmJǻY>Kmw}ν. +5RH=EMPijOW|x||ʴ? *6icB.YͽyvK\v}S #VvE,jʶ?qY %)hr[;ST #UsJ5 !aw[Ƿ~[   0[Rqó|somkh\> !(;aG}{iTF?>;)   5RrvvbO<7<=,   9[gf_RRS;   *5+ ۗ G*$'&'%& )'/EZ][7%(&&(%!0HOQI3#&*)($%&'(&'#-& A~}WDX2 /UgjUX`N% ***lgFaR9$Sjm{|avT$ &%*bo}}>]aM+.RVIYricg9 '/(`' KhwSOF#,NoIMDbNHLpu[r{$Zw-K;&!{ǐ4֒& 2VGSSG=30\]VJFP~Ĺtj{|O)*4g}zt:&EJxEAXJ-FcVKHE^oúw{xxQq&-Jmpȧj&G %Ilx^0&SYIGCGmdg֓ntzA",  Em}̫w&&L %>AgyG*VMBGJIQYoxknzդog|8&P +2S]%#K#-CFM^pZfjD%#(]~Jøb|Q &N 7Pa, =jM!7'?JRbujUb~uLO|q(¶vӒ_&O'Vd:!&OeiսȌ$@IWe|˝RZkړp~6| ǿ⧙t &Q*uԻe@1H̴n`RӾ~&JK[mYdP^tȰRAB6̾+8ĸL,SNdmQOrlx:gS^_ccgnuyz}͇FҎe&53`ٟ|q{2 !@b^m\UOwwݹ1:UX]`belqqryʙVȺ}JEDS__flmur( N`;2p&y}ۯtxHCxg"oՕ/D\:aol jdyB!(06F[dft{}C  Df%"Rp& vsrxP9o{%" sǖZ!3k\kxL  ;˒oz6!#1;[vH;B-R~s/ +ٴpS$`+p&}w`{xV5l@ " ^~c*HE幏up{F&˰R&$>CVu@$# .DG1zVCY56p& {yyqzݧmuY1kmB04~ObAU8װTƘ95LsX+i3$211.]? IOUceTp& "MUboynuðm[0kֆg AXfQH6ԹP!ש ,+"/~GPC.s'uYN[Am2p' 7)W|nozpV1e堎d=DFWJDOlR 7ܽ¿T H;J. V_Zy4WΏ?zZDQ p*/N{ϧmV4YXs"[].M??9Mqņ5[3H# ^ 3Pc#@wf`k[+p+fuIij֩kV)ĸiD/b¼qZE"2"y]s48DB>B=CuNVرڃ'hnlˬc  @3o7`__ADG͔{b\U? *UL#/ }S0׫; )sfwןW)-GoBV{a!Qˏqy`bS`ghndF|Wfd6/=JQJB:4+*.:FSfj RHqXjaݘa,*sbPoLKO$GʺҜl_ra`XZkuulf=e’w_bP%4?LJC=52574-('+J́6}h ,rUgӁW'"8.<]{~Z: 3MI>33;?A=73/2-<ӯv#hN:4F&>ɪuK`PA+Xۨ2oH?'bG |C=ƿNtd(DaN<<"P_PE>Qo༡vD<@A;1;BEEA=9484HĬ@konI('ŲXN{ʧH8[ի7oWL$u>({joӇPnzH:9*ywD"?d\VH@EXwοiC,3;@HJIDA>=A@Dzm'ubA*1<$ɳsHi&1`{˯@omn40slq{߰4X]΅yfl18ia\UKEBI]xÄ06DGMOMIEFGLJE,biD9>>@IPYI>s´OHv{9H{dwmM͗]9^^UNKHDCJYz?:LSVRPONQOWJ#8F@D@CAGtƠ`Pyt UXqxc#tv{HAW[QKKILLTXXs;DP[^[WY\Ze?3HFIHMLGHJLCGӬoQe ǾXqt,7#$IETUKKMO^Q_p`ZV[_eaferQ>NUSQTUSQMKOONI@bܴ}X[- l߼JqbG6-PzٽHbmaeMKF &M+tͥqǿpfnuzxfcs溨љy'nS#3=" GOP$G*$'&'V&%%(*'%:`}ίogvy̙fb|Ц{Ġ+ [_?$Ka]:"#&)'&'&%(%L"3fvhegpw˼‡v}iY(>VPDA#% L3ER]uٶe}(LT@1"!%% 4 ,Jjo~ 7'(')3AHRdpĵxz  *5-6;bŲDz}zyʴ? ),hdH-EʽX%Ew|S (UBeƷqZY &%r`eT # On\5 !$dtZǶ~[   1[Rpų{somkh\> !(;a.t{iSF?>;)  "+JpuwbP<7==,   5gkd`POS:    */% ۗ t8mk@   U+k`% %o^l6 F  Fn` \q La0 >T& XJ5v) 5"1A Ts @ u 4<* aD^BN3# W{ (io9 y /Fg q r | v>D. U &L62  Q )]zF8!SpI7|@Et|sG  6r¿S Q  J|?  k[  dy =Ch"jYu!l!A-7]/5GicnV Cconnectome-workbench-1.2.3+git41-gc4c6c90/icons/mac/workbench.icns000066400000000000000000001311551300200146000244760ustar00rootroot00000000000000icnsmTOC (il32 l8mkit32et8mk@il32  ˠŬh 61 I/ng l l/MNptcѡ-h #KxgʪȴHi4?}v>oe]j!e4q^U=̠tL'f~Ha^Y͋zn$rh jy]۝QqvĴ$eeR]tyyaFe޵TVRta^}iΓȱqT"i4Bj+עᢙO~U:_-zбⵈfA[YnxƯH701qԮ~i90ZD~.Q2aDΰpJTX/V2ilcMUT`8y}p7RYD tȀjeUTRTtϨc4 Jg/r6qIҖQe9GŬ\(h 5y18 մsya ˣe8yo/Is~dXI7EWrS =͎ĊILW6|x ˠĭh 'I( IAld l rkWtLvmo:h '6epRdwGi (M\sc5mJy?!fTzGveƷ 'g ecjNtdc|ƅ#k6t{r{z]Qjt#fk`eratBMgg1[V_yZY~Yps9pHdy7)pkiOW5tވKxiGOudXMz8(Rݢׅōcs8dmD[>OL:/0qa}mZR`|RMBG_ss,fʔh  dD C>{w(&{g!h #KK~?w/|ڑ 'g>ꋝHK5]e`sƒ !h'xŌ)B[efpTfPv#e0i͕^o7IX3o{'ScZ9i dSXJXOxl9]&T8./S[LCB-u$s:S?aĹ^1Ik&M=I|ib60MU0HI  &=-g|HI]U=LE2P?R{>?4RLsǿʳ4  &A ;]4OM?M#.z>WJVxmƵ`dt| &A 1;Qy8 GM4 |5/S^rͬsWp͖Ϻ &C(+8*:Kmq7ROIE"de>Ucp{l|ᶹp ֒&C4+1GEgG]cSIJD2ðtj}}΃rtpoY(e5s9&I67$ȥf&GE:FS7ΗnK[IJGF!Dplyo{ʰmwTW|̲*&Gje76 RL[ռ]WL=>BDE[m)Lqjz*slBI˭]_I/4U=&Nem+~\@ڱCPqjMQXm4^Xy%ֿ̯/%2&R d>?JSþĽƶOGS^wyKZb{)แw追ů~5X>&VY#FM=2QŻƱؠAOUj}\WZk8칀hp˞w!)׺;a& 򄧹+evtۍŞx\S>e&dtƴ*'bqоԛӿGV\lHiZl{BSmetײwdSd@Af&=ʷPS[ Wv}ξoLYd~|LmbpE[ckrkpyƲbMwϿ/&fһZ .}y›OZ`pZPqmzԂJc\adgnuyz}Ǻʭ|dٛd&3ۏ^ mlwsX[_nVMtv[O[Z`belqqryȴڣw&& LmWyr{sro剁<׸iTjk~JKtτBRX[_abhllrǼSp&2gyT3Lgq]D˙wwlMrr(Nu֘`FGWZ]\\chku섒äp&g}:K&$:t_Wc}CWntCovf1`rܤ`|]CMQWX[agnubop&uZ]>ÀxkDUrTJlHfxUd_dB?DR__eiryʉhֻ"p&HɺI~[Gt~xHErCpᆴųVZwxZOiwXZ0.8F\h}notϤZjp'ػ0> ruqxR6wry~ϕߢj_g~ӓozL~A;\nK3uvߴpNp' t"tn/ h_~xW5k3 mVm于uoԭML;YQ;tʵ΀LzFp& WztJPׯ?kzU6gЩ"NjUlױ~῝wekPTwܲ}gPDPrkؚNX^o$Ff$W`nwyxøL~R7d\}s~FS]ONuնƲl`{ޢ|zo񟐩]Pe>g2p%|opt~S 0qS4bʮG xBG[KCIlǡýiGǬpEl͑Cw^HPp)*}~z$bsR5YtzK6HC@-sR=D-EA>86Kqʸ\«Ͷych~uyKpClĵ&pQFCiQ6J<7*&FkȲYʽ|Ͱ¿u)p 1aȿKaXDIMn/-44+"&7eڼƳ`رǿwsv˛Tp,GW{µzY) OZfjWbc#/-))-RE¼f˵Ͼȩul{nоzp)j7d}Ơ "fVg͟[U>37DC62LuI}mɱλkmܳAJ`b*p,Ea\s \S +k[OqMUw+49GMMH:17F[wuϿכʯtfv+6|U_ȭAo>\w΂;`,h\Y[uk_`lew -0517;;9410&6ՓdῼþZRw߬X:ќ5p@pzʵzPss3jjmbMNUxaOG[Z: 3MH>63:@A=73/1-<٢ޖvY˾uK`dEܧ3pDTnͶI8hZm}wc\P=A?HuueD31AĴsHiTz̯@qt5Vtdt]#Dkajվx@Leb[UKEBI\w8=CHMOMIEFGKKByъ`F68B8z;PY݂\ôOAzBAJzg. [;]^UNJHCDL[zi ?KTVRPO,SPyĭgEACABF=Ơ`P}i^Y(t\Css= !ו AW[QKCGB53XqDk9EPZ_[WZYaU?NLMMLGHJJEB}ӫrKvr:}Ի(~Yopa^觀Jmĝ`ի(WETUKKLL ?W^n dYY[_cadkdbRRSUSQMKOPLKBb۴SiZ/m\Cq^KᚈP2"wI n;TOMNSH#0I^dktwўiZgiquztjdXSTVXUOOTWVNP:w་\gh"KEvޕ]%VrW g5RLRRXbglmmxw˴ƎVoq|sg^\\[WSU\^^VS )OLRU[_iTnR]Tuwsiec_\Z^eeaXZ9t߿j̎څ/FϺ̟pMRJ 2IRVW\]n%q' ^Zvskgb_bjmi^][<ߵqV{M+(BǦɫxi:lPkVBSYAcs {P@7ڋb~sjc`eone[fLaz j"&4etȧԸX TddW"KQORYo`9vgxld`cjh[\TxFt#'{Nb 4!%~;/˵N&g2<\q\'h=\G9EJ>/ *V9|Ķp֢  n"  Ơ0KҴZfo|bB|oCGdijcd[=&N#%Gæ#=eix{` 60Jia*рxǦS>ftwIKiEG*$'&'T&%%'*&%!P-^nQlZNrs_jj~ߋ[Ǚ{]FqwdWhY<" ')'&%(%L  Lnahowům@ADqnϹVs}̑lLIF& L3ESatZ 57wFg|{ !'<`|jSF?>;)  SioxucN;7<=,   CUfg]WUR;   QK< ۗ  G*$'&'&)'!-^P9$RklIYxY- &%+\um=8beM,,SUKVwlUðɲH &=*n[q:8__WA?NZhE8Q?S{gS?OSyWw~; &A 7ef2LYgH@KH_bFRLVwͷabxs|EvXTP9є\X*&A 5;X^݆4S`MFLCICe>GP_q˷ƮrUYCBTmf}a¬Og3 &A'3?G=:PLY?[WOHGACoo^ap{Y}{=CdWUgbTwLq\=֒&C38-3FSMJFo֦>]aTIIF9T~³sj}|qN^EUmRpOkwj8AKeg|t\jd9&G;KH0<\O;/\rDdULEHB6a{l~wuuJ^jYYePOtYeg`fȦg&G /?Q:V3D[K/]ӺYNZHHED:OmÑOtq|̼WFlYX/Dl[dx̮~)&L VA::1]K8l3UҟPTMADHDHZmoybnk}ĚBLeV;>\xh`~n^b;&N g>12D`&Ckv;J867BGN]sX[\dmTWI>1Pf`mqUHs}]vP{M &M muA:Vc-qpf_phKT:?IQbvysVXczMaMM~~NfIнva\wc_&OccCPd7EnBi~·ǘ@D=ÿ^Rgùj:OPcp`D\a^J[uݩhijq_o@&T,ugxv^I4-Hż`˘6DLZcsiSYXlipyYeq~ȤmNsӷ?&V qttv`71qűϝHIR[uyDbMZ[^yOʣfBYm~}Ѵ^F˜=&XSHe}TX˿eS[DDPUdLpq@bYh|eW~ǽV6jq}SEŝzYPXTƽ>&YPdoyĵxkfoѿ`q{dk^[ڌ8MUXy^k]Kh\hmMppCRipXPv}ײzhYjjab$d&!c]blY~v}پ|fyTdiMn>XUfgɷDYjcpnfElbE\cecjpyǰ|M|eceze&MM_>kcZUn~߾~shl:hݢ{K\[zqyzmSPrmweψAO`^bdgnuyz}ȗd}ӑe&4[pDw{wb^S|wvs~xdF=QG_]lTOuwھTBRY]`belqqryȦxʻjmYvp& PhR_kZs^xx~{tHoi{lsg޴^:`doyNFuq;TU\_abhllrD_zZgoDǾRp&4P;vRo[Zxe}hobctWa[{b|`]GTosOcx4Mupޗ5CNUY]\]cgkvyaճPh˿#f&nQcDeO`wP_WaҋupRqbl^hYoKLKXApuwȌi:\syaA>:GMQWX[`gkwo[ֆJg_աWp&;dFak_{kDSxϗvGbyhecڅDrP\7lsyՍHKh}jy2MI=;>DR__flowsSNĿQV`-p&.^`}X}h4kuxHDuÅ]rEmvkZҝPg{[folB_qyZ߫Tiwa/Gm^.+/6F[ekpx{^M{_jݟcEo`Vp'ԬL}THIuq~xR6wakrQbVŗ`M_Gu}Ek]jypcҐpz`R8ERG5<[uI:MYfizv[֙q۴oSn`o"p'PBzoSDw_}xW4ld_slDDă_RQL[Sf亏up|]ۚ]̮|kg/HA>@WwD):NW\`e\Е`|STvV0p&ş=e~zqYb٬MrxV4hϼqgs_JذiRٙ_ƙHs)DC>OsxQn@++BIEJ_f]kOUaoU p%*SЍ3X`oyvwĵ?bxU4fχeezhBlBWdPJFԹefū^gת̚SwR5EB:Cr8dVC@BpE`~ZN_Aj1p'L}d`|ooui@]nU3cȼ{`gUzCEXJDNl¯q`bŒھzBFTKDHnUlqloCPsx5Z͑@y[EQp*.Ox~{WS]kV3ZQQij\/LA@9MpYyRth|?G[ISSuĐhQRe.KwfbjZ&p*bkõsSqTZmU2*%%98G\wcUɲgħncرiQOrfwl[i^/9YbVPDo@XzmAI4c̘PXUe>n_KNc`V`jmmbL987;|VSl@C-1Yɼ~ndeS\waCO_`X\jttmcKEKAlf90,1@LJC=52574-('+J΀H݅^VfmH|ij}527;;9410&6ӘpnNLRVM9zWwʻt\QwkJ53;@A=73/1-<ԬYpHv^XEGn|ȪuK`oH8Nyۨ2oGFQFi\:xZcǽJILhnADH3K`O=A@Ew~LۆWucB+55mȳsHhboY}˯@qpK?Cnquy۽KaY;qihqeiտ}6=ha\UKEBI\x36:CHMOMIEFGKKD@H֙CmgE8X[PYw]aôOqyU6Jz[YKn6]w\]9^^UNJHCDKZzmX(=KTVRPONQPUSJM`a=DF@DABCE~Ơ`P{HY*wW,tLetU|=QvҩaAW[QKCIE>?Xqo9EPZ_[WY[\bTKRGDFKIMLGHJKCFӫqMrqepXTXor>BMen_HJpkƬƅүiSRETUKKML?;8HU_n]tbYX[^eafgnaXZYSRTUSQMKOPNIAbܴTfqhvkSX[Eo_/ZWdm[ODlpd\ldWjUFkNzSD;TPLOQPKKR^ckuОjXfjpvuvreYSUVXUOOTXVOP:wཊ]eRbo{D[N2oN-^{iatjZDkxxb\nttnXygYsbWHf6QMRSW`egloww˶œXnr|{rg_\][WSU\^^US=ckbV]WYdDUo;'{í~k@hU{hHotXjl|r[XWAi{WQ)OLRU\aidu[]ɇNJWuwsiec_\Z^eeaXZ9so?Qp[bmr>p5%sлnFVoWi`as}~mR[ehQ2IQVX[_jWT^drKIұu[vskgb_bjmi^][=ݶuJ^yYmp{g"p.#jȤ{tѨxiY{blNZsUBSZYXdthwkvZztZϊUމd}sjc`eoneZfL~ˏ{MRUX]@hup'$n}ɦѸ`Ikzv{k\de߀Q}POORYon]eEOyVђlfxld`cjhZ[TwòO:mzETZSϠ7RXp$qG8ʷaI\jlkthxX\mh?gvFNOOLH6BI^`L[]LgZsm{lc]_`ZPSmqbdkQj2Ev:p%`W=8˱zN_uddZthW]nRєWYhm^a}q8DXYLDGLNP}~xeq~ymb\ZWQJey]Hvȼd8=cp';7չYcgPU[u\Xeg^ckGgOʫx_MGPXp|oQQ|y~tl`[XORRTYböS@;>p&/o̹xpXE1_tfXhpw`VuX۶kuơz^^krwvTRM{kwd_ZSLWZiSHWºnL<1 p&IbdeglpzzeQD>DB0 *V9}·q׬k|auLBGPT\QOďpz_nѷA-OT,Okwb@HckceUOA&L%?cǥzȿSLGMfjw{nA8C?Wsȇ\d|3M{R+CL:!H_W,G*$'&'V&%%(*'#)T\fm]Xrjg}[McƞJMo^A89:Ob\;""&)'&'&%(%L29Zkbhpwȴilvqʽ\l[spdXWS]NDB!% L3ES^uֈlsmt]Pwg|&.CZSA1#!%$ 4 3PliMecXogwȼu;LPeio~ 7'(')+7:.8[NbŶ{lZShLz  *!;::AHfmJǻY>Kmw}ν. +5RH=EMPijOW|x||ʴ? *6icB.YͽyvK\v}S #VvE,jʶ?qY %)hr[;ST #UsJ5 !aw[Ƿ~[   0[Rqó|somkh\> !(;aG}{iTF?>;)   5RrvvbO<7<=,   9[gf_RRS;   *5+ ۗ G*$'&'%& )'/EZ][7%(&&(%!0HOQI3#&*)($%&'(&'#-& A~}WDX2 /UgjUX`N% ***lgFaR9$Sjm{|avT$ &%*bo}}>]aM+.RVIYricg9 '/(`' KhwSOF#,NoIMDbNHLpu[r{$Zw-K;&!{ǐ4֒& 2VGSSG=30\]VJFP~Ĺtj{|O)*4g}zt:&EJxEAXJ-FcVKHE^oúw{xxQq&-Jmpȧj&G %Ilx^0&SYIGCGmdg֓ntzA",  Em}̫w&&L %>AgyG*VMBGJIQYoxknzդog|8&P +2S]%#K#-CFM^pZfjD%#(]~Jøb|Q &N 7Pa, =jM!7'?JRbujUb~uLO|q(¶vӒ_&O'Vd:!&OeiսȌ$@IWe|˝RZkړp~6| ǿ⧙t &Q*uԻe@1H̴n`RӾ~&JK[mYdP^tȰRAB6̾+8ĸL,SNdmQOrlx:gS^_ccgnuyz}͇FҎe&53`ٟ|q{2 !@b^m\UOwwݹ1:UX]`belqqryʙVȺ}JEDS__flmur( N`;2p&y}ۯtxHCxg"oՕ/D\:aol jdyB!(06F[dft{}C  Df%"Rp& vsrxP9o{%" sǖZ!3k\kxL  ;˒oz6!#1;[vH;B-R~s/ +ٴpS$`+p&}w`{xV5l@ " ^~c*HE幏up{F&˰R&$>CVu@$# .DG1zVCY56p& {yyqzݧmuY1kmB04~ObAU8װTƘ95LsX+i3$211.]? IOUceTp& "MUboynuðm[0kֆg AXfQH6ԹP!ש ,+"/~GPC.s'uYN[Am2p' 7)W|nozpV1e堎d=DFWJDOlR 7ܽ¿T H;J. V_Zy4WΏ?zZDQ p*/N{ϧmV4YXs"[].M??9Mqņ5[3H# ^ 3Pc#@wf`k[+p+fuIij֩kV)ĸiD/b¼qZE"2"y]s48DB>B=CuNVرڃ'hnlˬc  @3o7`__ADG͔{b\U? *UL#/ }S0׫; )sfwןW)-GoBV{a!Qˏqy`bS`ghndF|Wfd6/=JQJB:4+*.:FSfj RHqXjaݘa,*sbPoLKO$GʺҜl_ra`XZkuulf=e’w_bP%4?LJC=52574-('+J́6}h ,rUgӁW'"8.<]{~Z: 3MI>33;?A=73/2-<ӯv#hN:4F&>ɪuK`PA+Xۨ2oH?'bG |C=ƿNtd(DaN<<"P_PE>Qo༡vD<@A;1;BEEA=9484HĬ@konI('ŲXN{ʧH8[ի7oWL$u>({joӇPnzH:9*ywD"?d\VH@EXwοiC,3;@HJIDA>=A@Dzm'ubA*1<$ɳsHi&1`{˯@omn40slq{߰4X]΅yfl18ia\UKEBI]xÄ06DGMOMIEFGLJE,biD9>>@IPYI>s´OHv{9H{dwmM͗]9^^UNKHDCJYz?:LSVRPONQOWJ#8F@D@CAGtƠ`Pyt UXqxc#tv{HAW[QKKILLTXXs;DP[^[WY\Ze?3HFIHMLGHJLCGӬoQe ǾXqt,7#$IETUKKMO^Q_p`ZV[_eaferQ>NUSQTUSQMKOONI@bܴ}X[- l߼JqbG6-PzٽHbmaeMKF &M+tͥqǿpfnuzxfcs溨љy'nS#3=" GOP$G*$'&'V&%%(*'%:`}ίogvy̙fb|Ц{Ġ+ [_?$Ka]:"#&)'&'&%(%L"3fvhegpw˼‡v}iY(>VPDA#% L3ER]uٶe}(LT@1"!%% 4 ,Jjo~ 7'(')3AHRdpĵxz  *5-6;bŲDz}zyʴ? ),hdH-EʽX%Ew|S (UBeƷqZY &%r`eT # On\5 !$dtZǶ~[   1[Rpų{somkh\> !(;a.t{iSF?>;)  "+JpuwbP<7==,   5gkd`POS:    */% ۗ t8mk@   U+k`% %o^l6 F  Fn` \q La0 >T& XJ5v) 5"1A Ts @ u 4<* aD^BN3# W{ (io9 y /Fg q r | v>D. U &L62  Q )]zF8!SpI7|@Et|sG  6r¿S Q  J|?  k[  dy =Ch"jYu!l!A-7]/5GicnV Cconnectome-workbench-1.2.3+git41-gc4c6c90/icons/windows/000077500000000000000000000000001300200146000225625ustar00rootroot00000000000000connectome-workbench-1.2.3+git41-gc4c6c90/icons/windows/workbench.ico000066400000000000000000002143661300200146000252540ustar00rootroot00000000000000  & (( @   ggg777BBB<<;===<=>=?@??@<<<333LNVUMNQK=<<//0: OLI B<<< hVM\oJIE@@AIIJ 6N / P@ev95dgglkk.-+DCC ]]] hhg``_URR2'!b -38?61`dlzG)&UC9VTTw||DW7,emkAHg5 vZ[`5aeh_\Zsts###J ,01\B=sg@6 gF>RXtG-9k[M%%,1!9BBE .--h>;7џodW0 z$ 3P#O#(c>+7 C*L&#N@@DN !4 <wpi 03>\TQROTdOgO9)(9.I dUs%\-z@XNE76:=(&%!4"cGR*/4|u ?DQWXU>D[;(,E:3oc[##$Q:mJCCG2+&&; W3z# /vJ-1 IHP$MPg IB <#3_`\Z?GiC?>w|kH5  ;.Plo-+)8O&8Vp[ JQax R/>|sWFN '  ( 0mtyrm ***pv|L4lLy$'5OHA]PWMW[lUB5Zvz}ZM- | G>z2<%  2BDOC\p>BO1 ]$? ?1-_NCL45$)[06JR&Pvlp , *)WH O 1>N}_[W !"!+;z$%!7MQV@-!o)O +=IFJ1)-1IA=3H #!#Wi PJLvx#%+ #0-B39%C?9pk+p9O[:312T!:w"Nh)7V"%*<7.ep-t.LzQOV ##" !m9Xl5B VVX_c%w}}Ta - !;; ,3UUUR:ZZZggg``accc`aaacc_]Z[VR}pd*|yv;ZZZmlkBkkiQPQVW\`ckolkVWWg%o P  /3H;(   OOO\\\VVVXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXZZZZZZXXXXXXZ[[XXZTVWZZZghp5opwGccaUUTUTU[[[\\\XXXXXXXXXZZZXXXWWW\\\OOOXXX]  /XXX]]]- #%#" "# 7]]]XXX $)+)-.0/.-  AXXXZZZl L-4Vd`V_`dkigOOO333$$% !ZZZXXXu)*+XXVHR|vzsssUUUBBB333,,,)))'''%%%### !XXXXXXY -/0::9stswyy]]]EEE888555;;;EEELLLIIIGGGJJJXXXXXX"=<=(,.A;8vwyvvwgggPPP>>>777>>>HHHWWWcccddd```VVVLLL j]]]XXXh+#!G+! e}|||FW`cl]\XLMOBAA999;;;III[[[lllmmmiiihhhZZZ[[[]]]XXXC6.+8!%-sA.toywworphmvvo`*< *FC:9:>>=;@@@JJJ\\\sss}}}sss```dddVVVXXX]]]XXX= #$' GwyzgddV\]1dE3.zd/.-345?>=JLL\\\tttcccVVV___XXX\\\]]]XXX  d   \yrstZWVEDEr73340-231135898GHHZZ[ttttttFFFZZZ]]]______ y]]] XXXk     "#N'FdegIFE08:P ]<'!9/->40EIJWVUsss>>>III___ccceddQRR[\\\XXX J|   )TwZQR5vwwa``NNN<;<0338&#TgV#m[E>_]\wvt222???[[[aaakiihhhFEE?XZ\  XXX Q %&'@BCMMMHHHGFFEEEL$U[!a r)#/<N2}cehONNJJJGGF=??9646@EI-#v w!$ )))555[[[___llkoooprp?@A\ZU  [[[ 6r---JIHkhhzww?&E ,>A'- wkgeGHI>>>EEEMMMHHHCDDECCMPQdG?wB/N>/}!##433XXXZZZeedpppttt;83 211 SRQQE"A .#%(!###)))///799MNOeikopp}yytmhep<'|$ & .A*>FL.*(467GFFVVVUUULLLPPP[[[hiiy||&y,$),>;:WZZQQQ]]]gghvvtvto 4} H& t|sG 7|489L>97m:(F"JJJZ[\ikipstprmdmmC,6Q[`7CH"%%#%345LJJ___ddcXXX\\\lll}}¼]Ba3 HLMQOOMMNXXX___oop}|zZWQZ)kC>3  * / OMJNNO?>=# @p- V k_[``a*26dpwrvw`[Z_N9) ""!(%$-/023307 QJJEFEHHIVVV`__kkk|||1370(3a@@= " - /?@Ba`X>EaF$I! 343rlk}ywi]+*)|wi\%A3\X_OVWBCC>-(MMC>PdvkdBFZ|)2(:3@BJ 8 %9-/3)  '%$ %!!   SF()(CCC``_eggehkiggokhhca%sQUX__]iggwwwRTT--1!P 81=m|hotzs]Wp`[||}tvv`a`CCI!'6%-   z Q !!"##"'&')))222221 !!   , kUmO%h0,/2=EI3hI %#)< haooo* 'WBW887*))023E?0  JTw|zr@@@ %%#(*3mki}vv6"yvoRUUM#P 5 $%)    ,)'BBBQRQZ[Ztstv|}TNMH0(?BC())3eD:gM-"*# $)/z/΅lȅpO6;%-tL?my|wN%;-O(V[]/23$%&++*.//686#.c@P333%%%/// =A@ze]$ka|r}r=,UCBGF>)} 6ir333111555---&&&3>AA%<8zro|||zwv[__IEF 8   &". HGGNOOHHGPPQttthprN2+t[O- -:9L#P  <("X*L59$O#%') 445P*O /errzC40?B@859OM?26d@0!vvv***555CCC@@@111'''!#18N9.dloga`hiiosv|}}ki_ H  L %*5  LNOyww___IJJOONoop}yvLUZDr%D#&%-#0>L[7L &'*:#E# # v./  3}t}g#aC9\deZW\NXN<>v,lLVXZZ''':::EEELLLIII333&&& 0'%*  %%R[_LHFDIJTA9XTTdehvmIL] /./  16<:(!L4-aacJIIGHLgegyzzedd=BB0|%2.3@PPQX[T%/ "! T[caD7w)4:$))O(<!18eOImlmrmI.|gc7&-EĪNMI!"#999AAALLLOOOBBB000&&&  %%%###./0rO 'ZekCAA>2-|}miNZ 3@@A %H\TItty\_aNME-3F-rcdhZZX/00%$$3Zc[B8;RWO8\ #&&_3;CE>,% 4-,#/%F3.E92 wo- !Am}@@B 445<<-(1=BA:7@@?T>8=e<>PA;---0//>??BBA@@?667,,,$$$!!! !!!!!! *,-¼IJL1)%M B)E6UW'EoI+ <vFwsm}}|acdHHD137B>.$w- %URMBBC.r%|00(-}-v(mE1+L#v,eZ "# !##&%&',)'-/00//9AE]WVdTwwth]MJE'(),+*333997778236++,### GFG#%%(GA ;V9 >7spoz||}}gggJLI=<=>>@  'UTR>>?2g&) & %D/%2C(opp X?% %%%/--CDEtB/mVWWWDCDC?=;;:%%$!"#%%$))*..---2..--+%!!  ///@AB!#:9-)}3`#*I$@ -+)  lllrpoTUUFFFGJP!(FMc;:78v)7%J'iV<iQmh$9vvtF R @  )((@@VQPkw|ZH)ACm6% ! ZD 874kllihh)*+!+.i:+* !_)48UN5ϭ.123 322NMMLLI=<>GFFUZe 9/;v'(# X?/\FOUX979667IGHieezw 222]\\Ծ]$'  1kX8\a A #    -5QlzZZZ134]UOZNPaA-=H7$}:rc`}}쵳')-( -,( &*8*-;31-ABCrpgo PNI v*>P$#PBNDDD(#\Cp-!2<:4 - # 2113MMMJLJ//-  g t  ///QRROPP$#"    2/-'5pM=?AE--- !   B'sst]]],,,$#$LLLNNNlmi)|342ypmmmm  | Tp$<E Z30(d ľwkha:::- :#;@V# (' Q_4/  8 )(%GGGrrr[ZZMMM...     ??>ipz $a%2d<92))((% O.ҺrstIII334MMJMNOc|B ']wzyhhi   r)p z eH Q#&24ywv[]`112/,,yL,%/Q7%% ' : 0 @   338ZZ`pml566     ?@Ami\.8`@)#.NJ r .(N 3gge---@AAGGGadc·)   1ddc q3dsa MF N_rvvVVV*('2/-B*1w#7J% ! !%%%)('!#) l 7 +,IIHwtsr!%'    ;;;¾QPJ )P(tX %2i/cw|CQ-=?E///JIIOQQ}|%i  UUU g&PMF#ac ORMVv}5UD=&() !=!/_+D %%$--*---&')"  2. 8!&:WVRIa/J"$     MOO87?mi< "'`431rs|#4 l+*)##"A@@TVWh`]֜lX8F[ Q[`k}}sRRO  F32/'F oc T WvP9F *!d (IUB18 ##%-#'7('%  & ! P t3>i3e}"&/ [     """<<:84PQRyyycZiF;GJOp:*?EI*#"#  X  QPrvB=*1:v mw,      --3AQQUhhi8[4'OD3pppidXZ[[U]|:M#)A('%&&'EDCgik}Ȋs5 )iso %C ;wVB Wy }(.4://3;:;QRRtttmtv[VTrB25"Zp#FsJ " 8 % wi W m!    G)0 %tw4mNy ",NH &zzv\aso$<0ؼ e>:-##&543\\]}dPi_imlEFL(,FP`'%%29o$B<=$#?BCRQOrrs}vyze& W o3# (31&ZW V 3    ! s# Gr-G}Īlms R>Qpy|*&%4|%.M43/)*,JIHtttytpr|twycch\[T#. yF-$-T(`#_Q#H5*&NTZihd|zzzzzyvODA#iO&&) $mw &&3h#hMTy0    ('%IN\2# [_rX} Z,agl-/BO_\heeeFED543:9:```mmoXWTIIHDEGPNM`\[iko]ZO 8 [84-i" O3-6%9DJ?:ckrtyzz||svv_[[:EIQ,XB* eM%', he_7"#t #; p$:  ***__`AQ+ )ļALz Qh[/BļQkcHgohhhEEH<==MMNwwwtvw431  #765JLMddassv@@= -zX-/1 WQ[zt0LL<4i]\|ywpooPPP755332CBC=c_lJc'%,  z||>-d,>m=72Z@$%}   *))``a|=N;1- #3 ")MZ9BR-G#FUydEFGJIEZZZ !# !//0MMMggezzyprp""!#CCG%%%{ C4 BD79=EIIABB333()))))2--865V$ye\mdcC \)(* ! l3Z=0EH? #=Pcz 27:I&&&TTTsm[*iG CVr2$ Tp>z(&*&)d|,hkiIMXVX\iii10) &997LLIkllFHH 21/]JDM))(11/(.1-/0;ADovz8 H'05 %" >!!=24=@6##,A@>EA5 "  -;=E||wQRXQOB!}"hT#:Oegrih`adm6R/& vc 3@53GB2GC8ABC,))[-5XPNG_`e! '%#3BXk6 3!158122**-2>OMMcim' 8+%-02 %##րe*#',GN ; %pt557!% N & RV[ooopLLI89=A>5$ikQN}PQVOOJPRX}mBXc,/!z % ,,/p0r$(4 #+>U% "iG%d_RmotWWW !)A)#-|hA<2-478/)(AUM/#rz}HHH345000 &&%7 H9' ,&_89t5:B 'Hd"|}}FFE---)))336@A8WRolk==?>>>NMM]_a3%#Fh J  ONHU\w-/9   -}v!dM6F}t2372.  BFgii;=<=.46C Z_d9+,--+**756000 )//_V%3++V=T1,F}NOP5M-_4vʼRU\%%%%%#015GC9)I] IGJ)))222DDBPRXv\o ] T`5 .-( -"$y52.,.:JQ*`hwttt)))()\N4k]}1?@B445;#_ m%mll---6656660000-+<#+9'.X%))>MVl#3@s")̾&V )*/=:0&1hQhBcA% $$1//BBAJLQk=T`zgG'D8  !#"%4-)(30)43-yw3%ĸVX_" U)% - hdFT+D><7DJF%l wU7iMQ]d530>AB;;;%%%50+O=#.<h1g#&! %<D_7+C\C (&"-,0==8:;R1D`<`]&# 346CBBRRTehv%h}#1g<@  ""!&&$')+-/1+,7EO>VQa?@@#]g: ^D%(#4 ew% R!EAAa/ G-CORGA>LLM876 @:4Bz-):?*Fy(&e#)CUƼ- ))).-.:<=;ABJvlR1 ķAH_  3 ###%%(0/-666EB;)oZ=#EJ_O[) # `.1Q)]z0!>wihMQTWXZNRU346%##%)*(V D-Q 6#/;h/3J=W|34F@G> 0ti /--=>=FEDVTR_ai)gtĺNOP  ###%%%..-=<;FHMa]U196w}s#;PNIid_a*#V)%'lw,8J0pd\iXR\;1>0+1584330-,_rgt}$)++$)3p&@,8#BX_t9PzJg $! %*)%015QPC-E#J_yy.--987AAAILPdc_ah}4L%*; ###&&&&&&+++567FECORWwk$> 2#R)WQE> DHQ8V)FC/3G/?X3'FLND><3:>O/&sky*./3,,5v0_ <P[eg B]hr '&(=<8DGl9/--//.67=JIFddc}z| !!!&&&******+++111:::RQOstyQa0}t4A%' ]M@:-./4VAQ-M %D `D(dio`ggGHI554?CCtwy3512,:7h#&T G#Vos<\@aļ(3Z22)$*\12 '++*&''0/2JG;JOe !=Zı#? 9   ###(((---111222444:::FFF}>O okkomaWae<93P4 EHT<  cly<_&yaZC;TBpd5LLL/..544hhh@?:23J"1 $mm([vsgֺ!: ) " =:5#y 3$%' *))<<:("&_ =P  !% +%%%)))111778>>>AAAEEEDDDppomorXXT+3U\.38+!4 !W/2u   8I)21-ty||vt|`U0%EOV%"&)*WVWGGLONL*-3T % VVU`dl(C d01&m%r #$##&>;3'w))>XD`!$',)! cp..-346?>=FEENMNTTT\\\ZZZrrrIJL62/%# .% % kOhiz z+LVc!_QIByy|z}||zeE4 16?DB@ZZZ`ad>IyRRWWUJOQQ! FIZda[tts " 366)* ##!336NNH !5\v #%!)*.74-?@DGHHLMMVVVa``lklvvvVVW320FFHzyw @*5D7R(-;ehcst|-Iz. " **.opptttrmgaac[\c_`gdgmhhdDLhGLZ[[WlloFV '/-()1UT31/CCFl_QN\L]"F '''357HE<'4o,:yke\acica]kkivvwttyEC@ 8gRHt||yvU0lP|  -6 #}GA7egl|zw0.L  tst||zwwrrtr(@R`}piiimlhrsy:P Z  %GC4, $ap)&%DLOTDC$ }v )1mI| 7#!#)*)556CDCPQVec\trhgishoe_W'/N,ghlwtrmy} sC);RQHkmtrh,#  OOR[e #z|}%w  ')/e`N TscM1::;_>3yyEE|+-888/99?GFFIMLJLNU5emz)@y}|mevy|wsm3> =th : elrzehk 3a($W5B!BP$JW6@FZRO|sloyCP 2 !!"333X\]V!%! >IMy7%J)56:|iGRO) ONUURJ3@s?UdQ;)hki9@GQ$ WO)cZ_QTQDBC---T1'@rk- ) M p=+VW[  *N)"|W}Hdyl8_ &%)-TRQ# s - -..GDCtvw1, % 521O]cG1-358*5h]o"GGa\[M7Bp:Qw. .>E=/[R I?A98]psJ6a" %')HDApy|aQ3" G"vA]'skdO5Am*zvNAF,34I.c336")% ! #1 Z&LMQaNwX  )''::;WXWzkk)10%(*(%%247WQNzR2_G!,&  8E\ms)0kccTOP[dadzywz}}popoig_TN9##|zz5(s 3 5 &0  9O:FD\R}('  #'):::UWZwtpH&J(-1-*)233ADGpoko kB&'7/&I} lZURPONWZZ_ltiehmihiedXVV9DI'034@Ehlr/58v X 0 9 "# !2F[ic+G  : )789VOLvwv}D3-/&%3530/3JG=0 2-!!l# 3-% @,687   )B  '7_*A \ ) C>) QakJ8E!) - +))333C?=QPPeeht|4w/38 &3 Np># /w|37Z]\Uc|G)6}I*G  ILM&>  1Fo=; a4 B[`<3s! )**+=>?P\`gllhymli++,)a'XWN\[h"4BR}i[Ivw##TXXX "!"@:v6; 2r#% /`zsii}e?2 //0IFE}:'meyvIDB /0-#,L)3g.3E #:L3\WWW)(#;*[83  8  E#XImvyCAA  333QNL# M8izd`_MMP:  !?1,_``ypZ-@R[ 7/5,HityysD><  XXXa%% 01 #&$A(#  #g&|8)?JM  433dilzr[WU2$%    +@EIUTVooi}Zozsrla]M:960XXX+,3;,37/94,3>GI607  , Ug I  321_gkg:)O   11/===AEF79:21/0/-003;=?HE<332)),$%'LXXX\7 V#I =024A;9=CF-  JPA!    #!AGL:  /& ')+><;UVVdaamlmikl__a\[XPOM?@D557q XXXn'F)#$"/347)#>%P8  %%%))''>1#243POOgegvvs|psvGIN###` ZZZF )-/4'#4  !!# +/0?&C $ "%653OPRvtry|}_dr#'5 FXXX^&J-d#  l%%%'&&369=863.00JIHZ[_OOI#6 [[[ 54 %#%oXXX U   + k `% lll   ?g3@3Ȁ"? /@@~P^wpq"0Ӏ/P@ PPPPPPPPPPPPPPP ++W@?PP +W_@ PKO(L3O@1 ;connectome-workbench-1.2.3+git41-gc4c6c90/icons/windows/workbench.rc000066400000000000000000000000601300200146000250660ustar00rootroot00000000000000IDI_ICON1 ICON DISCARDABLE "workbench.ico" connectome-workbench-1.2.3+git41-gc4c6c90/mac_plist/000077500000000000000000000000001300200146000217305ustar00rootroot00000000000000connectome-workbench-1.2.3+git41-gc4c6c90/mac_plist/MacOSXBundleInfo.plist.in000066400000000000000000000041571300200146000264610ustar00rootroot00000000000000 CFBundleDocumentTypes CFBundleTypeExtensions scene spec CFBundleTypeIconFile spec_file.icns CFBundleTypeName Workbench Spec File CFBundleTypeRole Viewer CFBundleTypeRole Viewer CFBundleTypeExtensions border foci gii nii palette CFBundleTypeIconFile data_file.icns CFBundleTypeName Workbench Data File CFBundleTypeRole Viewer CFBundleTypeRole Viewer CFBundleDevelopmentRegion English CFBundleExecutable ${MACOSX_BUNDLE_EXECUTABLE_NAME} CFBundleGetInfoString ${MACOSX_BUNDLE_INFO_STRING} CFBundleIconFile ${MACOSX_BUNDLE_ICON_FILE} CFBundleIdentifier ${MACOSX_BUNDLE_GUI_IDENTIFIER} CFBundleInfoDictionaryVersion 6.0 CFBundleLongVersionString ${MACOSX_BUNDLE_LONG_VERSION_STRING} CFBundleName ${MACOSX_BUNDLE_BUNDLE_NAME} CFBundlePackageType APPL CFBundleShortVersionString ${MACOSX_BUNDLE_SHORT_VERSION_STRING} CFBundleSignature ???? CFBundleVersion ${MACOSX_BUNDLE_BUNDLE_VERSION} CSResourcesFileMapped LSRequiresCarbon NSHumanReadableCopyright ${MACOSX_BUNDLE_COPYRIGHT} connectome-workbench-1.2.3+git41-gc4c6c90/notes/000077500000000000000000000000001300200146000211055ustar00rootroot00000000000000connectome-workbench-1.2.3+git41-gc4c6c90/notes/images.txt000066400000000000000000000007551300200146000231220ustar00rootroot00000000000000 Use a PNG image and place the image in the directory src/Desktop/images directory. In the same directory, edit the file resources.qrc and add the image. In the code, to load the image, use one of the "load" methods in WuQtUtilities. In the load methods, prefix the file name with ":/" which tells Qt that the image should be loaded from resources. Delete Desktop from your build directory, rerun cmake, and then build. This step must be done after images.qrc has been edited or touched. connectome-workbench-1.2.3+git41-gc4c6c90/notes/mac_app_icon.txt000066400000000000000000000004151300200146000242560ustar00rootroot00000000000000 * Create an image file in photoshop or other program with a transparent background that is 128x128. * Save as TIFF. * Start Icon Composer (/Developer/Utilities/Icon Composer). * Using Finder, drag the TIFF image and drop it into the 128x128 box. * Save the ICNS file. connectome-workbench-1.2.3+git41-gc4c6c90/notes/qwt_notes.txt000066400000000000000000000027701300200146000236770ustar00rootroot00000000000000 Setting up QWT for use with workbench In QWT's 'src' directory: Examine src.pro A configuration item (QWT_CONFIG) allow selective building of items. There are some HEADERS and SOURCES that we need. These are the HEADERS and SOURCES not controlled with QWT_CONFIG, and QwtPlot. QwtSvg and QwtWidgets are NOT needed to copy part of the src.pro file to create a command for deleting these unneeded files. Something like this: #!/bin/sh rm \ qwt_plot_svgitem.h \ qwt_plot_svgitem.cpp rm \ qwt_abstract_slider.h \ qwt_abstract_scale.h \ qwt_arrow_button.h \ qwt_analog_clock.h \ qwt_compass.h \ qwt_compass_rose.h \ qwt_counter.h \ qwt_dial.h \ qwt_dial_needle.h \ qwt_double_range.h \ qwt_knob.h \ qwt_slider.h \ qwt_thermo.h \ qwt_wheel.h rm \ qwt_abstract_slider.cpp \ qwt_abstract_scale.cpp \ qwt_arrow_button.cpp \ qwt_analog_clock.cpp \ qwt_compass.cpp \ qwt_compass_rose.cpp \ qwt_counter.cpp \ qwt_dial.cpp \ qwt_dial_needle.cpp \ qwt_double_range.cpp \ qwt_knob.cpp \ qwt_slider.cpp \ qwt_thermo.cpp \ qwt_wheel.cpp Next, copy the src.pro to CMakeLists.txt. Run "grep --files-with-matches Q_OBJECT *.h" to find the files that need to go in the MOC_INPUT_HEADER_FILES section of the CMakeLists.txt file. Next place all headers and CPP files into the SOURCE_FILES section. connectome-workbench-1.2.3+git41-gc4c6c90/src/000077500000000000000000000000001300200146000205445ustar00rootroot00000000000000connectome-workbench-1.2.3+git41-gc4c6c90/src/Algorithms/000077500000000000000000000000001300200146000226555ustar00rootroot00000000000000connectome-workbench-1.2.3+git41-gc4c6c90/src/Algorithms/AbstractAlgorithm.cxx000066400000000000000000000033041300200146000270130ustar00rootroot00000000000000/*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 "AbstractAlgorithm.h" using namespace std; using namespace caret; AbstractAlgorithm::AbstractAlgorithm(ProgressObject* myProgressObject) { m_progObj = myProgressObject; m_finish = true; if (m_progObj == NULL) { m_finish = false; return; } myProgressObject->algorithmStartSentinel(); if (myProgressObject->isDisabled()) { m_finish = false;//don't let a subalgorithm finish the progress bar if the main algorithm ignores it } } float AbstractAlgorithm::getAlgorithmInternalWeight() { return 1.0f; } float AbstractAlgorithm::getSubAlgorithmWeight() { return 0.0f; } float AbstractAlgorithm::getAlgorithmWeight() { return getAlgorithmInternalWeight() + getSubAlgorithmWeight(); } AbstractAlgorithm::~AbstractAlgorithm() { if ((m_progObj != NULL) && m_finish) { m_progObj->forceFinish(); } } connectome-workbench-1.2.3+git41-gc4c6c90/src/Algorithms/AbstractAlgorithm.h000066400000000000000000000041701300200146000264420ustar00rootroot00000000000000#ifndef __ABSTRACT_ALGORITHM_H__ #define __ABSTRACT_ALGORITHM_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*/ //make it easy to use these in an algorithm class, don't just forward declare them #include "ProgressObject.h" #include "CaretAssert.h" #include "OperationParameters.h" #include "AbstractOperation.h" namespace caret { ///the constructor for algorithms does the processing, because constructor/execute cycles don't make sense for something this simple class AbstractAlgorithm : public AbstractOperation { ProgressObject* m_progObj;//so that the destructor can make sure the bar finishes bool m_finish; AbstractAlgorithm();//prevent default construction protected: ///override this with the weights of the algorithms this algorithm will call static float getSubAlgorithmWeight();//protected so that people don't try to use them to set algorithm weights in progress objects ///override this with the amount of work the algorithm does internally, outside of calls to other algorithms static float getAlgorithmInternalWeight(); AbstractAlgorithm(ProgressObject* myProgressObject); virtual ~AbstractAlgorithm(); public: ///use this to set the weight parameter of a ProgressObject static float getAlgorithmWeight(); }; } #endif //__ABSTRACT_ALGORITHM_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Algorithms/AlgorithmBorderResample.cxx000066400000000000000000000171001300200146000301550ustar00rootroot00000000000000/*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 "AlgorithmBorderResample.h" #include "AlgorithmException.h" #include "Border.h" #include "BorderFile.h" #include "GiftiLabelTable.h" #include "GiftiMetaData.h" #include "SurfaceFile.h" #include "SurfaceProjectedItem.h" #include "SurfaceProjectionBarycentric.h" #include "SignedDistanceHelper.h" using namespace caret; using namespace std; AString AlgorithmBorderResample::getCommandSwitch() { return "-border-resample"; } AString AlgorithmBorderResample::getShortDescription() { return "RESAMPLE A BORDER FILE TO A DIFFERENT MESH"; } OperationParameters* AlgorithmBorderResample::getParameters() { OperationParameters* ret = new OperationParameters(); ret->addBorderParameter(1, "border-in", "the border file to resample"); ret->addSurfaceParameter(2, "current-sphere", "a sphere surface with the mesh that the metric is currently on"); ret->addSurfaceParameter(3, "new-sphere", "a sphere surface that is in register with and has the desired output mesh"); ret->addBorderOutputParameter(4, "border-out", "the output border file"); ret->setHelpText( AString("Resamples a border file, given two spherical surfaces that are in register. ") + "Only borders that have the same structure as current-sphere will be resampled." ); return ret; } void AlgorithmBorderResample::useParameters(OperationParameters* myParams, ProgressObject* myProgObj) { BorderFile* borderIn = myParams->getBorder(1); SurfaceFile* curSphere = myParams->getSurface(2); SurfaceFile* newSphere = myParams->getSurface(3); BorderFile* borderOut = myParams->getOutputBorder(4); AlgorithmBorderResample(myProgObj, borderIn, curSphere, newSphere, borderOut); } AlgorithmBorderResample::AlgorithmBorderResample(ProgressObject* myProgObj, const BorderFile* borderIn, const SurfaceFile* curSphere, const SurfaceFile* newSphere, BorderFile* borderOut) : AbstractAlgorithm(myProgObj) { LevelProgress myProgress(myProgObj); SurfaceFile curAdjust, newAdjust; adjustRadius(curSphere, curAdjust); adjustRadius(newSphere, newAdjust); borderOut->clear(); *(borderOut->getClassColorTable()) = *(borderIn->getClassColorTable()); *(borderOut->getNameColorTable()) = *(borderIn->getNameColorTable()); *(borderOut->getFileMetaData()) = *(borderIn->getFileMetaData()); borderOut->setStructure(curSphere->getStructure()); borderOut->setNumberOfNodes(newSphere->getNumberOfNodes()); int numBorderMDKeys = borderIn->getNumberOfBorderMetadataKeys(); for (int m = 0; m < numBorderMDKeys; ++m) { borderOut->addBorderMetadataKey(borderIn->getBorderMetadataKey(m));//rely on the keys being in order added } int numBorders = borderIn->getNumberOfBorders(); CaretPointer myHelp = newAdjust.getSignedDistanceHelper(); for (int i = 0; i < numBorders; ++i) { const Border* inputBorder = borderIn->getBorder(i); if (inputBorder->getStructure() != curSphere->getStructure()) continue; CaretPointer outputBorder(new Border());//in case something throws outputBorder->setName(inputBorder->getName()); outputBorder->setClassName(inputBorder->getClassName()); outputBorder->setClosed(inputBorder->isClosed()); int numPoints = inputBorder->getNumberOfPoints(); for (int j = 0; j < numPoints; ++j) { CaretPointer outPoint(new SurfaceProjectedItem());//ditto float coord[3]; const SurfaceProjectedItem* myItem = inputBorder->getPoint(j); if (!myItem->getBarycentricProjection()->isValid()) throw AlgorithmException("input file has a border point without barycentric projection");//because we never want to use van essen projection or straight coords bool valid = myItem->getBarycentricProjection()->unprojectToSurface(curAdjust, coord, 0.0f, true);//should really be "from" surface - "true" makes it not use the signed distance above surface, if present if (!valid) throw AlgorithmException("input file has a border point that is invalid for the current sphere"); BarycentricInfo myBaryInfo; myHelp->barycentricWeights(coord, myBaryInfo); outPoint->setStructure(inputBorder->getStructure()); outPoint->getBarycentricProjection()->setTriangleNodes(myBaryInfo.nodes); outPoint->getBarycentricProjection()->setTriangleAreas(myBaryInfo.baryWeights); outPoint->getBarycentricProjection()->setProjectionSurfaceNumberOfNodes(newSphere->getNumberOfNodes()); outPoint->getBarycentricProjection()->setValid(true); outputBorder->addPoint(outPoint.releasePointer());//NOTE: addPoint currently takes ownership of a RAW POINTER - shared_ptr won't release the pointer, so this function would need to be deprecated } borderOut->addBorder(outputBorder.releasePointer());//NOTE: again, ownership of RAW POINTER for (int m = 0; m < numBorderMDKeys; ++m)//HACK: will do this repeatedly for multi-part borders, but oh well { AString value = borderIn->getBorderMetadataValue(inputBorder->getName(), inputBorder->getClassName(), m); if (value != "") borderOut->setBorderMetadataValue(inputBorder->getName(), inputBorder->getClassName(), m, value); } } } void AlgorithmBorderResample::adjustRadius(const SurfaceFile* in, SurfaceFile& out) { int numNodes = in->getNumberOfNodes(); out = *in;//easiest way to set topology, etc CaretAssert(numNodes > 1); int numNodes3 = numNodes * 3; const float* coordData = in->getCoordinateData(); Vector3D tempData = coordData;//gets the first 3 floats float mindist = tempData.length(); if (mindist != mindist) throw CaretException("found NaN coordinate in an input sphere"); float maxdist = mindist; out.setCoordinate(0, tempData / mindist * 100.0f); const float TOLERANCE = 1.001f; for (int i = 3; i < numNodes3; i += 3) { tempData = coordData + i; float tempf = tempData.length(); if (tempf != tempf) throw CaretException("found NaN coordinate in an input sphere"); if (tempf < mindist) { mindist = tempf; } if (tempf > maxdist) { maxdist = tempf; } out.setCoordinate(i / 3, tempData / tempf * 100.0f); } if (mindist * TOLERANCE <= maxdist) { throw AlgorithmException("input surfaces must be spheres"); } } float AlgorithmBorderResample::getAlgorithmInternalWeight() { return 1.0f;//override this if needed, if the progress bar isn't smooth } float AlgorithmBorderResample::getSubAlgorithmWeight() { //return AlgorithmInsertNameHere::getAlgorithmWeight();//if you use a subalgorithm return 0.0f; } connectome-workbench-1.2.3+git41-gc4c6c90/src/Algorithms/AlgorithmBorderResample.h000066400000000000000000000034271300200146000276110ustar00rootroot00000000000000#ifndef __ALGORITHM_BORDER_RESAMPLE_H__ #define __ALGORITHM_BORDER_RESAMPLE_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 "AbstractAlgorithm.h" namespace caret { class AlgorithmBorderResample : public AbstractAlgorithm { AlgorithmBorderResample(); void adjustRadius(const SurfaceFile* in, SurfaceFile& out); protected: static float getSubAlgorithmWeight(); static float getAlgorithmInternalWeight(); public: AlgorithmBorderResample(ProgressObject* myProgObj, const BorderFile* borderIn, const SurfaceFile* curSphere, const SurfaceFile* newSphere, BorderFile* borderOut); static OperationParameters* getParameters(); static void useParameters(OperationParameters* myParams, ProgressObject* myProgObj); static AString getCommandSwitch(); static AString getShortDescription(); }; typedef TemplateAutoOperation AutoAlgorithmBorderResample; } #endif //__ALGORITHM_BORDER_RESAMPLE_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Algorithms/AlgorithmBorderToVertices.cxx000066400000000000000000000320031300200146000304730ustar00rootroot00000000000000/*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 "AlgorithmBorderToVertices.h" #include "AlgorithmException.h" #include "Border.h" #include "BorderFile.h" #include "CaretAssert.h" #include "GeodesicHelper.h" #include "MetricFile.h" #include "SurfaceFile.h" #include "SurfaceProjectedItem.h" #include "SurfaceProjectionBarycentric.h" #include "Vector3D.h" #include #include using namespace caret; using namespace std; AString AlgorithmBorderToVertices::getCommandSwitch() { return "-border-to-vertices"; } AString AlgorithmBorderToVertices::getShortDescription() { return "DRAW BORDERS AS VERTICES IN A METRIC FILE"; } OperationParameters* AlgorithmBorderToVertices::getParameters() { OperationParameters* ret = new OperationParameters(); ret->addSurfaceParameter(1, "surface", "the surface the borders are drawn on"); ret->addBorderParameter(2, "border-file", "the border file"); ret->addMetricOutputParameter(3, "metric-out", "the output metric file"); OptionalParameter* borderOpt = ret->createOptionalParameter(4, "-border", "create ROI for only one border"); borderOpt->addStringParameter(1, "name", "the name of the border"); ret->setHelpText( AString("Outputs a metric with 1s on vertices that follow a border, and 0s elsewhere. ") + "By default, a separate metric column is created for each border." ); return ret; } void AlgorithmBorderToVertices::useParameters(OperationParameters* myParams, ProgressObject* myProgObj) { SurfaceFile* mySurf = myParams->getSurface(1); BorderFile* myBorderFile = myParams->getBorder(2); MetricFile* myMetricOut = myParams->getOutputMetric(3); bool borderSelectMode = false; AString borderName; OptionalParameter* borderOpt = myParams->getOptionalParameter(4); if (borderOpt->m_present) { borderName = borderOpt->getString(1); borderSelectMode = true; } if (borderSelectMode) { AlgorithmBorderToVertices(myProgObj, mySurf, myBorderFile, myMetricOut, borderName); } else { AlgorithmBorderToVertices(myProgObj, mySurf, myBorderFile, myMetricOut); } } AlgorithmBorderToVertices::AlgorithmBorderToVertices(ProgressObject* myProgObj, const SurfaceFile* mySurf, const BorderFile* myBorderFile, MetricFile* myMetricOut) : AbstractAlgorithm(myProgObj) { LevelProgress myProgress(myProgObj); //TODO: check structure against surface set > borderCounter;//"multi-part border" behavior int numBorderParts = myBorderFile->getNumberOfBorders();//total number of parts for (int i = 0; i < numBorderParts; ++i) { const Border* thisBorderPart = myBorderFile->getBorder(i); borderCounter.insert(make_pair(thisBorderPart->getName(), thisBorderPart->getClassName())); } int numBorders = (int)borderCounter.size(); myMetricOut->setNumberOfNodesAndColumns(mySurf->getNumberOfNodes(), numBorders); myMetricOut->setStructure(mySurf->getStructure()); CaretPointer myGeoHelp = mySurf->getGeodesicHelper(); vector used(numBorderParts, false);//tracking for "multi-part border" behavior int curBorder = 0; for (int i = 0; i < numBorderParts; ++i)//visit borders in file order (of first piece), not set sorted order { if (used[i]) continue;//skip previously used parts vector colScratch(mySurf->getNumberOfNodes(), 0.0f); const Border* refBorderPart = myBorderFile->getBorder(i); AString borderName = refBorderPart->getName(), className = refBorderPart->getClassName(); myMetricOut->setColumnName(curBorder, borderName); for (int j = i; j < numBorderParts; ++j)//we could store the matching info in the counting set instead { const Border* thisBorderPart = myBorderFile->getBorder(j); if (thisBorderPart->getName() == borderName && thisBorderPart->getClassName() == className) { used[j] = true;//now we can actually do stuff with the border part int numBorderPoints = thisBorderPart->getNumberOfPoints(); if (numBorderPoints < 1) continue; const SurfaceProjectionBarycentric* thisBary = thisBorderPart->getPoint(0)->getBarycentricProjection(); CaretAssert(thisBary->isValid()); const float* thisWeights = thisBary->getTriangleAreas(); const int32_t* thisNodes = thisBary->getTriangleNodes(); int useNode = 0; Vector3D curPos;//initializes to 0 float weightSum = 0.0f; for (int n = 0; n < 3; ++n) { if (thisWeights[n] > 0.0f) { curPos += thisWeights[n] * Vector3D(mySurf->getCoordinate(thisNodes[n])); weightSum += thisWeights[n]; } if (thisWeights[n] > thisWeights[useNode]) useNode = n;//get closest node to point } curPos /= weightSum; int curNode = thisBary->getTriangleNodes()[useNode]; colScratch[curNode] = 1.0f;//if there is only one border point, make sure it leaves a mark for (int k = 1; k < numBorderPoints; ++k) { thisBary = thisBorderPart->getPoint(k)->getBarycentricProjection(); CaretAssert(thisBary->isValid()); thisWeights = thisBary->getTriangleAreas(); thisNodes = thisBary->getTriangleNodes(); useNode = 0; weightSum = 0.0f; Vector3D nextPos;//initializes to 0 for (int n = 0; n < 3; ++n) { if (thisWeights[n] > 0.0f) { nextPos += thisWeights[n] * Vector3D(mySurf->getCoordinate(thisNodes[n])); weightSum += thisWeights[n]; } if (thisWeights[n] > thisWeights[useNode]) useNode = n;//get closest node to point } nextPos /= weightSum; int nextNode = thisBary->getTriangleNodes()[useNode]; vector pathNodes; vector pathDists;//not used, but mandatory output myGeoHelp->getPathAlongLineSegment(curNode, nextNode, curPos, nextPos, pathNodes, pathDists); int pathLength = (int)pathNodes.size(); for (int m = 0; m < pathLength; ++m) { colScratch[pathNodes[m]] = 1.0f; } curNode = nextNode; curPos = nextPos; } if (thisBorderPart->isClosed()) { thisBary = thisBorderPart->getPoint(0)->getBarycentricProjection(); CaretAssert(thisBary->isValid()); thisWeights = thisBary->getTriangleAreas(); thisNodes = thisBary->getTriangleNodes(); useNode = 0; weightSum = 0.0f; Vector3D nextPos;//initializes to 0 for (int n = 0; n < 3; ++n) { if (thisWeights[n] > 0.0f) { nextPos += thisWeights[n] * Vector3D(mySurf->getCoordinate(thisNodes[n])); weightSum += thisWeights[n]; } if (thisWeights[n] > thisWeights[useNode]) useNode = n;//get closest node to point } nextPos /= weightSum; int nextNode = thisBary->getTriangleNodes()[useNode]; vector pathNodes; vector pathDists;//not used, but mandatory output myGeoHelp->getPathAlongLineSegment(curNode, nextNode, curPos, nextPos, pathNodes, pathDists); int pathLength = (int)pathNodes.size(); for (int m = 0; m < pathLength; ++m) { colScratch[pathNodes[m]] = 1.0f; } } } } myMetricOut->setValuesForColumn(curBorder, colScratch.data()); ++curBorder; } } AlgorithmBorderToVertices::AlgorithmBorderToVertices(ProgressObject* myProgObj, const SurfaceFile* mySurf, const BorderFile* myBorderFile, MetricFile* myMetricOut, const AString& borderName) : AbstractAlgorithm(myProgObj) { LevelProgress myProgress(myProgObj); //TODO: check structure against surface bool first = true; AString className; int numBorderParts = myBorderFile->getNumberOfBorders();//total number of parts myMetricOut->setNumberOfNodesAndColumns(mySurf->getNumberOfNodes(), 1); myMetricOut->setStructure(mySurf->getStructure()); myMetricOut->setColumnName(0, borderName); CaretPointer myGeoHelp = mySurf->getGeodesicHelper(); vector colScratch(mySurf->getNumberOfNodes(), 0.0f); for (int i = 0; i < numBorderParts; ++i) { const Border* thisBorderPart = myBorderFile->getBorder(i); if (thisBorderPart->getName() == borderName) { if (first) { className = thisBorderPart->getClassName(); } else { if (thisBorderPart->getClassName() != className) throw AlgorithmException("border name matches borders with different classes"); } } int numBorderPoints = thisBorderPart->getNumberOfPoints(); if (numBorderPoints < 1) continue; const SurfaceProjectionBarycentric* thisBary = thisBorderPart->getPoint(0)->getBarycentricProjection(); CaretAssert(thisBary->isValid()); const float* thisWeights = thisBary->getTriangleAreas(); const int32_t* thisNodes = thisBary->getTriangleNodes(); int useNode = 0; Vector3D curPos;//initializes to 0 float weightSum = 0.0f; for (int n = 0; n < 3; ++n) { if (thisWeights[n] > 0.0f) { curPos += thisWeights[n] * Vector3D(mySurf->getCoordinate(thisNodes[n])); weightSum += thisWeights[n]; } if (thisWeights[n] > thisWeights[useNode]) useNode = n;//get closest node to point } curPos /= weightSum; int curNode = thisBary->getTriangleNodes()[useNode]; colScratch[curNode] = 1.0f;//if there is only one border point, make sure it leaves a mark for (int k = 1; k < numBorderPoints; ++k) { thisBary = thisBorderPart->getPoint(k)->getBarycentricProjection(); CaretAssert(thisBary->isValid()); thisWeights = thisBary->getTriangleAreas(); thisNodes = thisBary->getTriangleNodes(); useNode = 0; weightSum = 0.0f; Vector3D nextPos;//initializes to 0 for (int n = 0; n < 3; ++n) { if (thisWeights[n] > 0.0f) { nextPos += thisWeights[n] * Vector3D(mySurf->getCoordinate(thisNodes[n])); weightSum += thisWeights[n]; } if (thisWeights[n] > thisWeights[useNode]) useNode = n;//get closest node to point } nextPos /= weightSum; int nextNode = thisBary->getTriangleNodes()[useNode]; vector pathNodes; vector pathDists;//not used, but mandatory output myGeoHelp->getPathAlongLineSegment(curNode, nextNode, curPos, nextPos, pathNodes, pathDists); int pathLength = (int)pathNodes.size(); for (int m = 0; m < pathLength; ++m) { colScratch[pathNodes[m]] = 1.0f; } curNode = nextNode; curPos = nextPos; } } myMetricOut->setValuesForColumn(0, colScratch.data()); } float AlgorithmBorderToVertices::getAlgorithmInternalWeight() { return 1.0f;//override this if needed, if the progress bar isn't smooth } float AlgorithmBorderToVertices::getSubAlgorithmWeight() { //return AlgorithmInsertNameHere::getAlgorithmWeight();//if you use a subalgorithm return 0.0f; } connectome-workbench-1.2.3+git41-gc4c6c90/src/Algorithms/AlgorithmBorderToVertices.h000066400000000000000000000036011300200146000301220ustar00rootroot00000000000000#ifndef __ALGORITHM_BORDER_TO_VERTEX_PATH_H__ #define __ALGORITHM_BORDER_TO_VERTEX_PATH_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 "AbstractAlgorithm.h" namespace caret { class AlgorithmBorderToVertices : public AbstractAlgorithm { AlgorithmBorderToVertices(); protected: static float getSubAlgorithmWeight(); static float getAlgorithmInternalWeight(); public: AlgorithmBorderToVertices(ProgressObject* myProgObj, const SurfaceFile* mySurf, const BorderFile* myBorderFile, MetricFile* myMetricOut); AlgorithmBorderToVertices(ProgressObject* myProgObj, const SurfaceFile* mySurf, const BorderFile* myBorderFile, MetricFile* myMetricOut, const AString& borderName); static OperationParameters* getParameters(); static void useParameters(OperationParameters* myParams, ProgressObject* myProgObj); static AString getCommandSwitch(); static AString getShortDescription(); }; typedef TemplateAutoOperation AutoAlgorithmBorderToVertices; } #endif //__ALGORITHM_BORDER_TO_VERTEX_PATH_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Algorithms/AlgorithmCiftiAllLabelsToROIs.cxx000066400000000000000000000125701300200146000311270ustar00rootroot00000000000000/*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 "AlgorithmCiftiAllLabelsToROIs.h" #include "AlgorithmException.h" #include "CiftiFile.h" #include "GiftiLabelTable.h" #include #include #include using namespace caret; using namespace std; AString AlgorithmCiftiAllLabelsToROIs::getCommandSwitch() { return "-cifti-all-labels-to-rois"; } AString AlgorithmCiftiAllLabelsToROIs::getShortDescription() { return "MAKE ROIS FROM ALL LABELS IN A CIFTI LABEL MAP"; } OperationParameters* AlgorithmCiftiAllLabelsToROIs::getParameters() { OperationParameters* ret = new OperationParameters(); ret->addCiftiParameter(1, "label-in", "the input cifti label file"); ret->addStringParameter(2, "map", "the number or name of the label map to use"); ret->addCiftiOutputParameter(3, "cifti-out", "the output cifti file"); ret->setHelpText( AString("The output cifti file is a dscalar file with a column (map) for each label in the specified input map, other than the ??? label, ") + "each of which contains a binary ROI of all brainordinates that are set to the corresponding label.\n\n" + "Most of the time, specifying '1' for the argument will do what is desired." ); return ret; } void AlgorithmCiftiAllLabelsToROIs::useParameters(OperationParameters* myParams, ProgressObject* myProgObj) { CiftiFile* myLabel = myParams->getCifti(1); AString mapID = myParams->getString(2); if (myLabel->getCiftiXMLOld().getMappingType(CiftiXMLOld::ALONG_ROW) != CIFTI_INDEX_TYPE_LABELS) { throw AlgorithmException("mapping type along row must be labels");//because we need to translate map ID to index before algorithm, but want a more useful error if the reason it fails is mapping type } int whichMap = myLabel->getCiftiXMLOld().getMapIndexFromNameOrNumber(CiftiXMLOld::ALONG_ROW, mapID); if (whichMap == -1) { throw AlgorithmException("invalid map number or name specified"); } CiftiFile* myCiftiOut = myParams->getOutputCifti(3); AlgorithmCiftiAllLabelsToROIs(myProgObj, myLabel, whichMap, myCiftiOut); } AlgorithmCiftiAllLabelsToROIs::AlgorithmCiftiAllLabelsToROIs(ProgressObject* myProgObj, const CiftiFile* myLabel, const int& whichMap, CiftiFile* myCiftiOut) : AbstractAlgorithm(myProgObj) { LevelProgress myProgress(myProgObj); const CiftiXMLOld& myXML = myLabel->getCiftiXMLOld(); if (myXML.getMappingType(CiftiXMLOld::ALONG_ROW) != CIFTI_INDEX_TYPE_LABELS) { throw AlgorithmException("mapping type along row must be labels");//check again, in case it is used from somewhere other than useParameters() } const GiftiLabelTable* myTable = myXML.getLabelTableForRowIndex(whichMap); int32_t unusedKey = myTable->getUnassignedLabelKey();//WARNING: this actually MODIFIES the label table if the ??? key doesn't exist set myKeys = myTable->getKeys(); int numKeys = (int)myKeys.size(); if (numKeys < 2) { throw AlgorithmException("label table doesn't contain any keys besides the ??? key"); } map keyToMap;//lookup from keys to column CiftiXMLOld outXML = myXML; outXML.resetDirectionToScalars(CiftiXMLOld::ALONG_ROW, numKeys - 1); int counter = 0; for (set::iterator iter = myKeys.begin(); iter != myKeys.end(); ++iter) { if (*iter == unusedKey) continue;//skip the ??? key keyToMap[*iter] = counter; outXML.setMapNameForIndex(CiftiXMLOld::ALONG_ROW, counter, myTable->getLabelName(*iter)); ++counter; } myCiftiOut->setCiftiXML(outXML); vector outRowScratch(numKeys - 1, 0.0f), inRowScratch(myXML.getNumberOfColumns()); int64_t numRows = myXML.getNumberOfRows(); for (int64_t i = 0; i < numRows; ++i) { myLabel->getRow(inRowScratch.data(), i); int32_t thisKey = (int32_t)floor(inRowScratch[whichMap] + 0.5f); map::iterator iter = keyToMap.find(thisKey); if (iter != keyToMap.end()) { outRowScratch[iter->second] = 1.0f;//set the single element for the correct map } myCiftiOut->setRow(outRowScratch.data(), i); if (iter != keyToMap.end()) { outRowScratch[iter->second] = 0.0f;//and rezero it to get ready for the next row } } } float AlgorithmCiftiAllLabelsToROIs::getAlgorithmInternalWeight() { return 1.0f;//override this if needed, if the progress bar isn't smooth } float AlgorithmCiftiAllLabelsToROIs::getSubAlgorithmWeight() { //return AlgorithmInsertNameHere::getAlgorithmWeight();//if you use a subalgorithm return 0.0f; } connectome-workbench-1.2.3+git41-gc4c6c90/src/Algorithms/AlgorithmCiftiAllLabelsToROIs.h000066400000000000000000000033431300200146000305520ustar00rootroot00000000000000#ifndef __ALGORITHM_CIFTI_ALL_LABELS_TO_ROIS_H__ #define __ALGORITHM_CIFTI_ALL_LABELS_TO_ROIS_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 "AbstractAlgorithm.h" namespace caret { class AlgorithmCiftiAllLabelsToROIs : public AbstractAlgorithm { AlgorithmCiftiAllLabelsToROIs(); protected: static float getSubAlgorithmWeight(); static float getAlgorithmInternalWeight(); public: AlgorithmCiftiAllLabelsToROIs(ProgressObject* myProgObj, const CiftiFile* myLabel, const int& whichMap, CiftiFile* myCiftiOut); static OperationParameters* getParameters(); static void useParameters(OperationParameters* myParams, ProgressObject* myProgObj); static AString getCommandSwitch(); static AString getShortDescription(); }; typedef TemplateAutoOperation AutoAlgorithmCiftiAllLabelsToROIs; } #endif //__ALGORITHM_CIFTI_ALL_LABELS_TO_ROIS_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Algorithms/AlgorithmCiftiAverage.cxx000066400000000000000000000247121300200146000276070ustar00rootroot00000000000000/*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 "AlgorithmCiftiAverage.h" #include "AlgorithmException.h" #include "CaretAssert.h" #include "CaretLogger.h" #include "CaretPointer.h" #include "CiftiFile.h" #include "MathFunctions.h" #include using namespace caret; using namespace std; AString AlgorithmCiftiAverage::getCommandSwitch() { return "-cifti-average"; } AString AlgorithmCiftiAverage::getShortDescription() { return "AVERAGE CIFTI FILES"; } OperationParameters* AlgorithmCiftiAverage::getParameters() { OperationParameters* ret = new OperationParameters(); ret->addCiftiOutputParameter(1, "cifti-out", "output cifti file"); OptionalParameter* excludeOpt = ret->createOptionalParameter(2, "-exclude-outliers", "exclude outliers by standard deviation of each element across files"); excludeOpt->addDoubleParameter(1, "sigma-below", "number of standard deviations below the mean to include"); excludeOpt->addDoubleParameter(2, "sigma-above", "number of standard deviations above the mean to include"); ParameterComponent* ciftiOpt = ret->createRepeatableParameter(3, "-cifti", "specify an input file"); ciftiOpt->addCiftiParameter(1, "cifti-in", "the input cifti file"); OptionalParameter* weightOpt = ciftiOpt->createOptionalParameter(1, "-weight", "give a weight for this file"); weightOpt->addDoubleParameter(1, "weight", "the weight to use"); ret->setHelpText( AString("Averages cifti files together. ") + "Files without -weight specified are given a weight of 1. " + "If -exclude-outliers is specified, at each element, the data across all files is taken as a set, its unweighted mean and sample standard deviation are found, " + "and values outside the specified number of standard deviations are excluded from the (potentially weighted) average at that element." ); return ret; } void AlgorithmCiftiAverage::useParameters(OperationParameters* myParams, ProgressObject* myProgObj) { CiftiFile* ciftiOut = myParams->getOutputCifti(1); vector ciftiList;//this is just so that it can pass them to the algorithm vector weights; const vector& myInstances = *(myParams->getRepeatableParameterInstances(3)); for (int i = 0; i < (int)myInstances.size(); ++i) { ciftiList.push_back(myInstances[i]->getCifti(1)); OptionalParameter* weightOpt = myInstances[i]->getOptionalParameter(1); if (weightOpt->m_present) { weights.push_back((float)weightOpt->getDouble(1)); } else { weights.push_back(1.0f); } } OptionalParameter* excludeOpt = myParams->getOptionalParameter(2); if (excludeOpt->m_present) { AlgorithmCiftiAverage(myProgObj, ciftiList, excludeOpt->getDouble(1), excludeOpt->getDouble(2), ciftiOut, &weights); } else { AlgorithmCiftiAverage(myProgObj, ciftiList, ciftiOut, &weights); } } AlgorithmCiftiAverage::AlgorithmCiftiAverage(ProgressObject* myProgObj, const vector& ciftiList, CiftiFile* ciftiOut, const vector* weightsPtr) : AbstractAlgorithm(myProgObj) { LevelProgress myProgress(myProgObj); if (ciftiList.size() == 0) { throw AlgorithmException("no files specified"); } if (weightsPtr != NULL && ciftiList.size() != weightsPtr->size()) { throw AlgorithmException("number of weights doesn't match number of input cifti files"); } CaretAssert(ciftiList[0] != NULL); CiftiXML baseXML = ciftiList[0]->getCiftiXML(); if (baseXML.getNumberOfDimensions() != 2) throw AlgorithmException("cifti average currently only supports 2D files"); int numRows = baseXML.getDimensionLength(CiftiXML::ALONG_COLUMN), rowSize = baseXML.getDimensionLength(CiftiXML::ALONG_ROW), numFiles = (int)ciftiList.size(); for (int i = 1; i < numFiles; ++i) { CaretAssert(ciftiList[i] != NULL); if (!baseXML.approximateMatch(ciftiList[i]->getCiftiXML()))//requires at least length to match, often more restrictive { throw AlgorithmException("cifti file '" + ciftiList[i]->getFileName() + "' does not match earlier inputs"); } } ciftiOut->setCiftiXML(baseXML); vector accum(rowSize); vector myrow(rowSize); for (int i = 0; i < numRows; ++i) { vector weightaccum(rowSize, 0.0); for (int j = 0; j < rowSize; ++j) { accum[j] = 0.0; } for (int j = 0; j < numFiles; ++j) { ciftiList[j]->getRow(myrow.data(), i); if (weightsPtr == NULL) { for (int k = 0; k < rowSize; ++k) { if (MathFunctions::isNumeric(myrow[k])) { weightaccum[k] += 1.0; accum[k] += myrow[k]; } } } else { float weight = (*weightsPtr)[j]; for (int k = 0; k < rowSize; ++k) { if (MathFunctions::isNumeric(myrow[k])) { weightaccum[k] += weight; accum[k] += myrow[k] * weight; } } } } for (int k = 0; k < rowSize; ++k) { if (weightaccum[k] != 0.0) { myrow[k] = accum[k] / weightaccum[k]; } else { myrow[k] = 0.0f; } } ciftiOut->setRow(myrow.data(), i); } } AlgorithmCiftiAverage::AlgorithmCiftiAverage(ProgressObject* myProgObj, const vector& ciftiList, const float& sigmaBelow, const float& sigmaAbove, CiftiFile* ciftiOut, const std::vector* weightsPtr): AbstractAlgorithm(myProgObj) { LevelProgress myProgress(myProgObj); if (ciftiList.size() < 2) { throw AlgorithmException("fewer than 2 files specified with outlier exclusion"); } if (weightsPtr != NULL && ciftiList.size() != weightsPtr->size()) { throw AlgorithmException("number of weights doesn't match number of input cifti files"); } CaretAssert(ciftiList[0] != NULL); CiftiXML baseXML = ciftiList[0]->getCiftiXML(); if (baseXML.getNumberOfDimensions() != 2) throw AlgorithmException("cifti average currently only supports 2D files"); int numRows = baseXML.getDimensionLength(CiftiXML::ALONG_COLUMN), rowSize = baseXML.getDimensionLength(CiftiXML::ALONG_ROW), numFiles = (int)ciftiList.size(); for (int i = 1; i < numFiles; ++i) { CaretAssert(ciftiList[i] != NULL); if (!baseXML.approximateMatch(ciftiList[i]->getCiftiXML())) { throw AlgorithmException("cifti files do not match"); } } bool haveWarned = false; ciftiOut->setCiftiXML(baseXML); vector > myrows(numFiles, vector(rowSize)); for (int i = 0; i < numRows; ++i) { for (int j = 0; j < numFiles; ++j) { ciftiList[j]->getRow(myrows[j].data(), i); } vector outrow(rowSize); for (int k = 0; k < rowSize; ++k) { double accum = 0.0; double weightaccum = 0.0; int nonnumeric = 0; for (int j = 0; j < numFiles; ++j) { if (MathFunctions::isNumeric(myrows[j][k])) { accum += myrows[j][k]; } else { ++nonnumeric; } } if (nonnumeric >= numFiles - 1) { if (!haveWarned) { CaretLogWarning("found element where less than 2 files have numeric values"); haveWarned = true; } outrow[k] = 0.0f; } else { float mean = accum / (numFiles - nonnumeric); accum = 0.0; for (int j = 0; j < numFiles; ++j) { if (MathFunctions::isNumeric(myrows[j][k])) { float temp = myrows[j][k] - mean; accum += temp * temp; } } float stdev = sqrt(accum / (numFiles - 1 - nonnumeric)); float cutoffLow = mean - sigmaBelow * stdev; float cutoffHigh = mean + sigmaAbove * stdev; accum = 0.0; for (int j = 0; j < numFiles; ++j) { if (myrows[j][k] > cutoffLow && myrows[j][k] < cutoffHigh)//implicitly excludes NaN and inf { if (weightsPtr != NULL) { float weight = (*weightsPtr)[j]; accum += myrows[j][k] * weight; weightaccum += weight; } else { accum += myrows[j][k]; weightaccum += 1.0; } } } if (weightaccum != 0.0) { outrow[k] = accum / weightaccum; } else { outrow[k] = 0.0f; } } } ciftiOut->setRow(outrow.data(), i); } } float AlgorithmCiftiAverage::getAlgorithmInternalWeight() { return 1.0f;//override this if needed, if the progress bar isn't smooth } float AlgorithmCiftiAverage::getSubAlgorithmWeight() { //return AlgorithmInsertNameHere::getAlgorithmWeight();//if you use a subalgorithm return 0.0f; } connectome-workbench-1.2.3+git41-gc4c6c90/src/Algorithms/AlgorithmCiftiAverage.h000066400000000000000000000036661300200146000272410ustar00rootroot00000000000000#ifndef __ALGORITHM_CIFTI_AVERAGE_H__ #define __ALGORITHM_CIFTI_AVERAGE_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 "AbstractAlgorithm.h" #include namespace caret { class AlgorithmCiftiAverage : public AbstractAlgorithm { AlgorithmCiftiAverage(); protected: static float getSubAlgorithmWeight(); static float getAlgorithmInternalWeight(); public: AlgorithmCiftiAverage(ProgressObject* myProgObj, const std::vector& ciftiList, CiftiFile* ciftiOut, const std::vector* weightsPtr = NULL); AlgorithmCiftiAverage(ProgressObject* myProgObj, const std::vector& ciftiList, const float& sigmaBelow, const float& sigmaAbove, CiftiFile* ciftiOut, const std::vector* weightsPtr = NULL); static OperationParameters* getParameters(); static void useParameters(OperationParameters* myParams, ProgressObject* myProgObj); static AString getCommandSwitch(); static AString getShortDescription(); }; typedef TemplateAutoOperation AutoAlgorithmCiftiAverage; } #endif //__ALGORITHM_CIFTI_AVERAGE_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Algorithms/AlgorithmCiftiAverageDenseROI.cxx000066400000000000000000000662731300200146000311500ustar00rootroot00000000000000/*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 "AlgorithmCiftiAverageDenseROI.h" #include "AlgorithmException.h" #include "CaretLogger.h" #include "CiftiFile.h" #include "FileInformation.h" #include "SurfaceFile.h" #include "MetricFile.h" #include "VolumeFile.h" #include #include #include using namespace caret; using namespace std; AString AlgorithmCiftiAverageDenseROI::getCommandSwitch() { return "-cifti-average-dense-roi"; } AString AlgorithmCiftiAverageDenseROI::getShortDescription() { return "AVERAGE CIFTI ROWS ACROSS SUBJECTS BY ROI"; } OperationParameters* AlgorithmCiftiAverageDenseROI::getParameters() { OperationParameters* ret = new OperationParameters(); ret->addCiftiOutputParameter(1, "cifti-out", "output cifti dscalar file"); OptionalParameter* ciftiRoiOpt = ret->createOptionalParameter(2, "-cifti-roi", "cifti file containing combined weights"); ciftiRoiOpt->addCiftiParameter(1, "roi-cifti", "the roi cifti file"); ciftiRoiOpt->createOptionalParameter(2, "-in-memory", "cache the roi in memory so that it isn't re-read for each input cifti"); OptionalParameter* leftRoiOpt = ret->createOptionalParameter(3, "-left-roi", "weights to use for left hempsphere"); leftRoiOpt->addMetricParameter(1, "roi-metric", "the left roi as a metric file"); OptionalParameter* rightRoiOpt = ret->createOptionalParameter(4, "-right-roi", "weights to use for right hempsphere"); rightRoiOpt->addMetricParameter(1, "roi-metric", "the right roi as a metric file"); OptionalParameter* cerebRoiOpt = ret->createOptionalParameter(5, "-cerebellum-roi", "weights to use for cerebellum surface"); cerebRoiOpt->addMetricParameter(1, "roi-metric", "the cerebellum roi as a metric file"); OptionalParameter* volRoiOpt = ret->createOptionalParameter(6, "-vol-roi", "voxel weights to use"); volRoiOpt->addVolumeParameter(1, "roi-vol", "the roi volume file"); OptionalParameter* leftAreaSurfOpt = ret->createOptionalParameter(7, "-left-area-surf", "specify the left surface for vertex area correction"); leftAreaSurfOpt->addSurfaceParameter(1, "left-surf", "the left surface file"); OptionalParameter* rightAreaSurfOpt = ret->createOptionalParameter(8, "-right-area-surf", "specify the right surface for vertex area correction"); rightAreaSurfOpt->addSurfaceParameter(1, "right-surf", "the right surface file"); OptionalParameter* cerebAreaSurfOpt = ret->createOptionalParameter(9, "-cerebellum-area-surf", "specify the cerebellum surface for vertex area correction"); cerebAreaSurfOpt->addSurfaceParameter(1, "cerebellum-surf", "the cerebellum surface file"); ParameterComponent* ciftiOpt = ret->createRepeatableParameter(10, "-cifti", "specify an input cifti file"); ciftiOpt->addCiftiParameter(1, "cifti-in", "a cifti file to average across"); ret->setHelpText( AString("Averages rows for each map of the ROI(s), across all files. ") + "ROI maps are treated as weighting functions, including negative values. " + "For efficiency, ensure that everything that is not intended to be used is zero in the ROI map. " + "If -cifti-roi is specified, -left-roi, -right-roi, -cerebellum-roi, and -vol-roi must not be specified. " + "If multiple non-cifti ROI files are specified, they must have the same number of columns." ); return ret; } void AlgorithmCiftiAverageDenseROI::useParameters(OperationParameters* myParams, ProgressObject* myProgObj) { CiftiFile* ciftiOut = myParams->getOutputCifti(1); CiftiFile* ciftiROI = NULL; OptionalParameter* ciftiRoiOpt = myParams->getOptionalParameter(2); if (ciftiRoiOpt->m_present) { ciftiROI = ciftiRoiOpt->getCifti(1); if (ciftiRoiOpt->getOptionalParameter(2)->m_present) ciftiROI->convertToInMemory(); } MetricFile* leftROI = NULL; OptionalParameter* leftRoiOpt = myParams->getOptionalParameter(3); if (leftRoiOpt->m_present) { if (ciftiROI != NULL) throw AlgorithmException("-cifti-roi cannot be used with any other ROI option"); leftROI = leftRoiOpt->getMetric(1); } MetricFile* rightROI = NULL; OptionalParameter* rightRoiOpt = myParams->getOptionalParameter(4); if (rightRoiOpt->m_present) { if (ciftiROI != NULL) throw AlgorithmException("-cifti-roi cannot be used with any other ROI option"); rightROI = rightRoiOpt->getMetric(1); } MetricFile* cerebROI = NULL; OptionalParameter* cerebRoiOpt = myParams->getOptionalParameter(5); if (cerebRoiOpt->m_present) { if (ciftiROI != NULL) throw AlgorithmException("-cifti-roi cannot be used with any other ROI option"); cerebROI = cerebRoiOpt->getMetric(1); } VolumeFile* volROI = NULL; OptionalParameter* volRoiOpt = myParams->getOptionalParameter(6); if (volRoiOpt->m_present) { if (ciftiROI != NULL) throw AlgorithmException("-cifti-roi cannot be used with any other ROI option"); volROI = volRoiOpt->getVolume(1); } SurfaceFile* leftAreaSurf = NULL; OptionalParameter* leftAreaSurfOpt = myParams->getOptionalParameter(7); if (leftAreaSurfOpt->m_present) { leftAreaSurf = leftAreaSurfOpt->getSurface(1); } SurfaceFile* rightAreaSurf = NULL; OptionalParameter* rightAreaSurfOpt = myParams->getOptionalParameter(8); if (rightAreaSurfOpt->m_present) { rightAreaSurf = rightAreaSurfOpt->getSurface(1); } SurfaceFile* cerebAreaSurf = NULL; OptionalParameter* cerebAreaSurfOpt = myParams->getOptionalParameter(9); if (cerebAreaSurfOpt->m_present) { cerebAreaSurf = cerebAreaSurfOpt->getSurface(1); } vector ciftiList; const vector& ciftiInstances = *(myParams->getRepeatableParameterInstances(10)); for (int i = 0; i < (int)ciftiInstances.size(); ++i) { ciftiList.push_back(ciftiInstances[i]->getCifti(1)); } if (ciftiROI != NULL) { AlgorithmCiftiAverageDenseROI(myProgObj, ciftiList, ciftiOut, ciftiROI, leftAreaSurf, rightAreaSurf, cerebAreaSurf); } else { AlgorithmCiftiAverageDenseROI(myProgObj, ciftiList, ciftiOut, leftROI, rightROI, cerebROI, volROI, leftAreaSurf, rightAreaSurf, cerebAreaSurf); } } AlgorithmCiftiAverageDenseROI::AlgorithmCiftiAverageDenseROI(ProgressObject* myProgObj, const vector& ciftiList, CiftiFile* ciftiOut, const MetricFile* leftROI, const MetricFile* rightROI, const MetricFile* cerebROI, const VolumeFile* volROI, const SurfaceFile* leftAreaSurf, const SurfaceFile* rightAreaSurf, const SurfaceFile* cerebAreaSurf) : AbstractAlgorithm(myProgObj) { CaretAssert(ciftiOut != NULL); LevelProgress myProgress(myProgObj); int numCifti = (int)ciftiList.size(); if (numCifti < 1) throw AlgorithmException("no cifti files specified to average"); const CiftiXML& baseXML = ciftiList[0]->getCiftiXML(); if (baseXML.getNumberOfDimensions() != 2) throw AlgorithmException("this operation currently only supports 2D cifti"); if (baseXML.getMappingType(CiftiXML::ALONG_COLUMN) != CiftiMappingType::BRAIN_MODELS) throw AlgorithmException("inputs must have brain models along columns"); int rowSize = baseXML.getDimensionLength(CiftiXML::ALONG_ROW); bool first = true; const CaretMappableDataFile* nameFile = NULL; int numMaps = -1; vector leftAreaData, rightAreaData, cerebAreaData; float* leftAreaPointer = NULL, *rightAreaPointer = NULL, *cerebAreaPointer = NULL; if (leftROI != NULL) { if (leftAreaSurf != NULL) { if (leftROI->getNumberOfNodes() != leftAreaSurf->getNumberOfNodes()) throw AlgorithmException("left area surface and left roi have different number of nodes"); leftAreaSurf->computeNodeAreas(leftAreaData); leftAreaPointer = leftAreaData.data(); } first = false; numMaps = leftROI->getNumberOfMaps(); nameFile = leftROI; } if (rightROI != NULL) { if (rightAreaSurf != NULL) { if (rightROI->getNumberOfNodes() != rightAreaSurf->getNumberOfNodes()) throw AlgorithmException("right area surface and right roi have different number of nodes"); rightAreaSurf->computeNodeAreas(rightAreaData); rightAreaPointer = rightAreaData.data(); } if (first) { first = false; numMaps = rightROI->getNumberOfMaps(); nameFile = rightROI; } else { if (rightROI->getNumberOfMaps() != numMaps) throw AlgorithmException("right roi has a different number of maps"); } } if (cerebROI != NULL) { if (cerebAreaSurf != NULL) { if (cerebROI->getNumberOfNodes() != cerebAreaSurf->getNumberOfNodes()) throw AlgorithmException("cerebellum area surface and cerebellum roi have different number of nodes"); cerebAreaSurf->computeNodeAreas(cerebAreaData); cerebAreaPointer = cerebAreaData.data(); } if (first) { first = false; numMaps = cerebROI->getNumberOfMaps(); nameFile = cerebROI; } else { if (cerebROI->getNumberOfMaps() != numMaps) throw AlgorithmException("cerebellum roi has a different number of maps"); } } if (volROI != NULL) { if (first) { first = false; numMaps = volROI->getNumberOfMaps(); nameFile = volROI; } else { if (volROI->getNumberOfMaps() != numMaps) throw AlgorithmException("volume roi has a different number of maps"); } } if (first) throw AlgorithmException("no roi files provided"); for (int i = 0; i < numCifti; ++i) { const CiftiXML& thisXML = ciftiList[i]->getCiftiXML(); if (!thisXML.approximateMatch(baseXML)) throw AlgorithmException("cifti files do not match between #1 and #" + AString::number(i + 1)); } if (leftROI != NULL) { verifySurfaceComponent(ciftiList[0], StructureEnum::CORTEX_LEFT, leftROI); } if (rightROI != NULL) { verifySurfaceComponent(ciftiList[0], StructureEnum::CORTEX_RIGHT, rightROI); } if (cerebROI != NULL) { verifySurfaceComponent(ciftiList[0], StructureEnum::CEREBELLUM, cerebROI); } if (volROI != NULL) { verifyVolumeComponent(ciftiList[0], volROI); } vector > accum(numMaps, vector(rowSize, 0.0)); vector denom(numMaps, 0.0); for (int i = 0; i < numCifti; ++i) { if (leftROI != NULL) { processSurfaceComponent(accum, denom, ciftiList[i], StructureEnum::CORTEX_LEFT, leftROI, leftAreaPointer); } if (rightROI != NULL) { processSurfaceComponent(accum, denom, ciftiList[i], StructureEnum::CORTEX_RIGHT, rightROI, rightAreaPointer); } if (cerebROI != NULL) { processSurfaceComponent(accum, denom, ciftiList[i], StructureEnum::CEREBELLUM, cerebROI, cerebAreaPointer); } if (volROI != NULL) { processVolumeComponent(accum, denom, ciftiList[i], volROI); } } CiftiXML newXml; newXml.setNumberOfDimensions(2); newXml.setMap(CiftiXML::ALONG_COLUMN, *(baseXML.getMap(CiftiXML::ALONG_ROW))); CiftiScalarsMap rowMap; rowMap.setLength(numMaps); for (int i = 0; i < numMaps; ++i) { rowMap.setMapName(i, nameFile->getMapName(i)); } newXml.setMap(CiftiXML::ALONG_ROW, rowMap); ciftiOut->setCiftiXML(newXml); vector outRow(numMaps); for (int j = 0; j < numMaps; ++j) { if (denom[j] == 0.0) throw AlgorithmException("no data matched one of the ROI(s)"); } for (int i = 0; i < rowSize; ++i) { for (int j = 0; j < numMaps; ++j) { outRow[j] = accum[j][i] / denom[j]; } ciftiOut->setRow(outRow.data(), i); } } AlgorithmCiftiAverageDenseROI::AlgorithmCiftiAverageDenseROI(ProgressObject* myProgObj, const vector& ciftiList, CiftiFile* ciftiOut, const CiftiFile* ciftiROI, const SurfaceFile* leftAreaSurf, const SurfaceFile* rightAreaSurf, const SurfaceFile* cerebAreaSurf): AbstractAlgorithm(myProgObj) { CaretAssert(ciftiOut != NULL); LevelProgress myProgress(myProgObj); int numCifti = (int)ciftiList.size(); if (numCifti < 1) throw AlgorithmException("no cifti files specified to average"); const CiftiXML& baseXML = ciftiList[0]->getCiftiXML(), &roiXML = ciftiROI->getCiftiXML(); if (baseXML.getNumberOfDimensions() != 2 || roiXML.getNumberOfDimensions() != 2) throw AlgorithmException("this operation currently only supports 2D cifti"); if (baseXML.getMappingType(CiftiXML::ALONG_COLUMN) != CiftiMappingType::BRAIN_MODELS) throw AlgorithmException("inputs must have brain models along columns"); const CiftiBrainModelsMap& baseBrainModels = baseXML.getBrainModelsMap(CiftiXML::ALONG_COLUMN); if (baseBrainModels != *(roiXML.getMap(CiftiXML::ALONG_COLUMN))) throw AlgorithmException("cifti roi mapping along columns doesn't match input cifti"); int rowSize = baseXML.getDimensionLength(CiftiXML::ALONG_ROW); vector leftAreaData, rightAreaData, cerebAreaData; float* leftAreaPointer = NULL, *rightAreaPointer = NULL, *cerebAreaPointer = NULL; if (leftAreaSurf != NULL) { if (baseBrainModels.getSurfaceNumberOfNodes(StructureEnum::CORTEX_LEFT) != leftAreaSurf->getNumberOfNodes()) { throw AlgorithmException("left area surface and left cortex cifti structure have different number of nodes"); } leftAreaSurf->computeNodeAreas(leftAreaData); leftAreaPointer = leftAreaData.data(); } if (rightAreaSurf != NULL) { if (baseBrainModels.getSurfaceNumberOfNodes(StructureEnum::CORTEX_RIGHT) != rightAreaSurf->getNumberOfNodes()) { throw AlgorithmException("right area surface and right cortex cifti structure have different number of nodes"); } rightAreaSurf->computeNodeAreas(rightAreaData); rightAreaPointer = rightAreaData.data(); } if (cerebAreaSurf != NULL) { if (baseBrainModels.getSurfaceNumberOfNodes(StructureEnum::CEREBELLUM) != cerebAreaSurf->getNumberOfNodes()) { throw AlgorithmException("cerebellum area surface and cerebellum cortex cifti structure have different number of nodes"); } cerebAreaSurf->computeNodeAreas(cerebAreaData); cerebAreaPointer = cerebAreaData.data(); } for (int i = 1; i < numCifti; ++i) { const CiftiXML& thisXML = ciftiList[i]->getCiftiXML(); if (!thisXML.approximateMatch(baseXML)) throw AlgorithmException("cifti files do not match between #1 and #" + AString::number(i + 1)); } int numMaps = roiXML.getDimensionLength(CiftiXML::ALONG_ROW); vector > accum(numMaps, vector(rowSize, 0.0)); vector denom(numMaps, 0.0); for (int i = 0; i < numCifti; ++i) { processCifti(accum, denom, ciftiList[i], ciftiROI, leftAreaPointer, rightAreaPointer, cerebAreaPointer); } CiftiXML newXml; newXml.setNumberOfDimensions(2); newXml.setMap(CiftiXML::ALONG_COLUMN, *(baseXML.getMap(CiftiXML::ALONG_ROW))); CiftiScalarsMap rowMap; rowMap.setLength(numMaps); const CiftiMappingType& nameMap = *(roiXML.getMap(CiftiXML::ALONG_ROW)); for (int i = 0; i < numMaps; ++i) { rowMap.setMapName(i, nameMap.getIndexName(i)); } newXml.setMap(CiftiXML::ALONG_ROW, rowMap); ciftiOut->setCiftiXML(newXml); vector outRow(numMaps); for (int j = 0; j < numMaps; ++j) { if (denom[j] == 0.0) throw AlgorithmException("no data matched one of the ROI(s)"); } for (int i = 0; i < rowSize; ++i) { for (int j = 0; j < numMaps; ++j) { outRow[j] = accum[j][i] / denom[j]; } ciftiOut->setRow(outRow.data(), i); } } void AlgorithmCiftiAverageDenseROI::verifySurfaceComponent(const CiftiFile* myCifti, const StructureEnum::Enum& myStruct, const MetricFile* myRoi) { const CiftiXML& myXml = myCifti->getCiftiXML(); CaretAssert(myXml.getMappingType(CiftiXML::ALONG_COLUMN) == CiftiMappingType::BRAIN_MODELS);//should be checked in the algorithm constructor const CiftiBrainModelsMap& brainModelsMap = myXml.getBrainModelsMap(CiftiXML::ALONG_COLUMN); if (!brainModelsMap.hasSurfaceData(myStruct)) { CaretLogWarning("cifti files are missing structure " + StructureEnum::toName(myStruct)); return; } if (myRoi->getNumberOfNodes() != brainModelsMap.getSurfaceNumberOfNodes(myStruct)) { throw AlgorithmException("cifti number of vertices does not match roi for structure " + StructureEnum::toName(myStruct)); } } void AlgorithmCiftiAverageDenseROI::processSurfaceComponent(vector >& accum, vector& denom, const CiftiFile* myCifti, const StructureEnum::Enum& myStruct, const MetricFile* myRoi, const float* myAreas) { const CiftiXML& myXml = myCifti->getCiftiXML(); CaretAssert(myXml.getMappingType(CiftiXML::ALONG_COLUMN) == CiftiMappingType::BRAIN_MODELS);//should be checked in the algorithm constructor const CiftiBrainModelsMap& brainModelsMap = myXml.getBrainModelsMap(CiftiXML::ALONG_COLUMN); if (!brainModelsMap.hasSurfaceData(myStruct)) { return; } if (myRoi->getNumberOfNodes() != brainModelsMap.getSurfaceNumberOfNodes(myStruct)) throw AlgorithmException("cifti number of vertices does not match roi"); int rowSize = myXml.getDimensionLength(CiftiXML::ALONG_ROW); vector rowScratch(rowSize); CaretAssert(rowScratch.size() == accum[0].size()); vector myMap = brainModelsMap.getSurfaceMap(myStruct); int mapSize = (int)myMap.size(); int numMaps = myRoi->getNumberOfMaps(); if (myAreas == NULL) { for (int i = 0; i < mapSize; ++i) { const int& myNode = myMap[i].m_surfaceNode; bool dataLoaded = false; for (int m = 0; m < numMaps; ++m) { const float roiVal = myRoi->getValue(myNode, m); if (roiVal != 0.0f) { if (!dataLoaded) { myCifti->getRow(rowScratch.data(), myMap[i].m_ciftiIndex); dataLoaded = true; } for (int j = 0; j < rowSize; ++j) { accum[m][j] += rowScratch[j] * roiVal; } denom[m] += roiVal; } } } } else { for (int i = 0; i < mapSize; ++i) { const int& myNode = myMap[i].m_surfaceNode; bool dataLoaded = false; for (int m = 0; m < numMaps; ++m) { const float& roiVal = myRoi->getValue(myNode, m); if (roiVal != 0.0f) { const float weight = roiVal * myAreas[myMap[i].m_surfaceNode]; if (!dataLoaded) { myCifti->getRow(rowScratch.data(), myMap[i].m_ciftiIndex); dataLoaded = true; } for (int j = 0; j < rowSize; ++j) { accum[m][j] += rowScratch[j] * weight; } denom[m] += weight; } } } } } void AlgorithmCiftiAverageDenseROI::verifyVolumeComponent(const CiftiFile* myCifti, const VolumeFile* volROI) { const CiftiXML& myXml = myCifti->getCiftiXML(); CaretAssert(myXml.getMappingType(CiftiXML::ALONG_COLUMN) == CiftiMappingType::BRAIN_MODELS);//should be checked in the algorithm constructor const CiftiBrainModelsMap& brainModelsMap = myXml.getBrainModelsMap(CiftiXML::ALONG_COLUMN); if (!volROI->matchesVolumeSpace(brainModelsMap.getVolumeSpace())) throw AlgorithmException("cifti files don't match the ROI volume's space"); } void AlgorithmCiftiAverageDenseROI::processVolumeComponent(vector >& accum, vector& denom, const CiftiFile* myCifti, const VolumeFile* volROI) { const CiftiXML& myXml = myCifti->getCiftiXML(); CaretAssert(myXml.getMappingType(CiftiXML::ALONG_COLUMN) == CiftiMappingType::BRAIN_MODELS);//should be checked in the algorithm constructor const CiftiBrainModelsMap& brainModelsMap = myXml.getBrainModelsMap(CiftiXML::ALONG_COLUMN); if (!volROI->matchesVolumeSpace(brainModelsMap.getVolumeSpace())) throw AlgorithmException("cifti files don't match the ROI volume's space"); int rowSize = myXml.getDimensionLength(CiftiXML::ALONG_ROW); vector rowScratch(rowSize); CaretAssert(rowScratch.size() == accum[0].size()); vector myMap = brainModelsMap.getFullVolumeMap(); int mapSize = (int)myMap.size(); int numMaps = volROI->getNumberOfMaps(); for (int i = 0; i < mapSize; ++i) { bool dataLoaded = false; if (!volROI->indexValid(myMap[i].m_ijk)) throw AlgorithmException("cifti file lists invalid voxels"); for (int m = 0; m < numMaps; ++m) { const float& roiVal = volROI->getValue(myMap[i].m_ijk, m); if (roiVal != 0.0f) { if (!dataLoaded) { myCifti->getRow(rowScratch.data(), myMap[i].m_ciftiIndex); dataLoaded = true; } for (int j = 0; j < rowSize; ++j) { accum[m][j] += rowScratch[j] * roiVal; } denom[m] += roiVal; } } } } void AlgorithmCiftiAverageDenseROI::processCifti(vector >& accum, vector& denom, const CiftiFile* myCifti, const CiftiFile* ciftiROI, const float* leftAreas, const float* rightAreas, const float* cerebAreas) { const CiftiXML& myXml = myCifti->getCiftiXML();//same along columns for data and roi, we already checked CaretAssert(myXml.getMappingType(CiftiXML::ALONG_COLUMN) == CiftiMappingType::BRAIN_MODELS);//should be checked in the algorithm constructor const CiftiBrainModelsMap& brainModelsMap = myXml.getBrainModelsMap(CiftiXML::ALONG_COLUMN); int rowSize = myXml.getDimensionLength(CiftiXML::ALONG_ROW); vector surfList = brainModelsMap.getSurfaceStructureList(); int numMaps = ciftiROI->getNumberOfColumns(); vector dataScratch(rowSize), roiScratch(numMaps); for (int s = 0; s < (int)surfList.size(); ++s) { const float* myAreas = NULL; switch (surfList[s]) { case StructureEnum::CORTEX_LEFT: myAreas = leftAreas; break; case StructureEnum::CORTEX_RIGHT: myAreas = rightAreas; break; case StructureEnum::CEREBELLUM: myAreas = cerebAreas; break; default: throw AlgorithmException("found unexpected surface structure in cifti files: " + StructureEnum::toName(surfList[s])); } vector myMap = brainModelsMap.getSurfaceMap(surfList[s]); int mapSize = (int)myMap.size(); if (myAreas != NULL) { for (int i = 0; i < mapSize; ++i) { ciftiROI->getRow(roiScratch.data(), myMap[i].m_ciftiIndex); const float& thisArea = myAreas[myMap[i].m_surfaceNode]; bool dataLoaded = false; for (int m = 0; m < numMaps; ++m)//ROI maps, not cifti mapping { const float& roiValue = roiScratch[m]; if (roiValue != 0.0f) { if (!dataLoaded) { myCifti->getRow(dataScratch.data(), i); dataLoaded = true; } const float weight = roiValue * thisArea; for (int j = 0; j < rowSize; ++j) { accum[m][j] += dataScratch[j] * weight; } denom[m] += weight; } } } } else { for (int i = 0; i < mapSize; ++i) { ciftiROI->getRow(roiScratch.data(), myMap[i].m_ciftiIndex); bool dataLoaded = false; for (int m = 0; m < numMaps; ++m)//ROI maps, not cifti mapping { const float& roiValue = roiScratch[m]; if (roiValue != 0.0f) { if (!dataLoaded) { myCifti->getRow(dataScratch.data(), i); dataLoaded = true; } for (int j = 0; j < rowSize; ++j) { accum[m][j] += dataScratch[j] * roiValue; } denom[m] += roiValue; } } } } } vector myMap = brainModelsMap.getFullVolumeMap();//again, we know columns match between ROI and data int mapSize = (int)myMap.size(); for (int i = 0; i < mapSize; ++i) { ciftiROI->getRow(roiScratch.data(), myMap[i].m_ciftiIndex); bool dataLoaded = false; for (int m = 0; m < numMaps; ++m)//ROI maps, not cifti mapping { const float& roiValue = roiScratch[m]; if (roiValue != 0.0f) { if (!dataLoaded) { myCifti->getRow(dataScratch.data(), i); dataLoaded = true; } for (int j = 0; j < rowSize; ++j) { accum[m][j] += dataScratch[j] * roiValue; } denom[m] += roiValue; } } } } float AlgorithmCiftiAverageDenseROI::getAlgorithmInternalWeight() { return 1.0f;//override this if needed, if the progress bar isn't smooth } float AlgorithmCiftiAverageDenseROI::getSubAlgorithmWeight() { return 0.0f; } connectome-workbench-1.2.3+git41-gc4c6c90/src/Algorithms/AlgorithmCiftiAverageDenseROI.h000066400000000000000000000064251300200146000305660ustar00rootroot00000000000000#ifndef __ALGORITHM_CIFTI_AVERAGE_DENSE_ROI_H__ #define __ALGORITHM_CIFTI_AVERAGE_DENSE_ROI_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 "AbstractAlgorithm.h" #include "StructureEnum.h" #include namespace caret { class AlgorithmCiftiAverageDenseROI : public AbstractAlgorithm { AlgorithmCiftiAverageDenseROI(); void verifySurfaceComponent(const CiftiFile* myCifti, const StructureEnum::Enum& myStruct, const MetricFile* myRoi); void processSurfaceComponent(std::vector >& accum, std::vector& denom, const CiftiFile* myCifti, const StructureEnum::Enum& myStruct, const MetricFile* myRoi, const float* myAreas); void verifyVolumeComponent(const CiftiFile* myCifti, const VolumeFile* volROI); void processVolumeComponent(std::vector >& accum, std::vector& denom, const CiftiFile* myCifti, const VolumeFile* volROI); void processCifti(std::vector >& accum, std::vector& denom, const CiftiFile* myCifti, const CiftiFile* ciftiROI, const float* leftAreas, const float* rightAreas, const float* cerebAreas); protected: static float getSubAlgorithmWeight(); static float getAlgorithmInternalWeight(); public: AlgorithmCiftiAverageDenseROI(ProgressObject* myProgObj, const std::vector& ciftiList, CiftiFile* ciftiOut, const CiftiFile* ciftiROI, const SurfaceFile* leftAreaSurf = NULL, const SurfaceFile* rightAreaSurf = NULL, const SurfaceFile* cerebAreaSurf = NULL); AlgorithmCiftiAverageDenseROI(ProgressObject* myProgObj, const std::vector& ciftiList, CiftiFile* ciftiOut, const MetricFile* leftROI = NULL, const MetricFile* rightROI = NULL, const MetricFile* cerebROI = NULL, const VolumeFile* volROI = NULL, const SurfaceFile* leftAreaSurf = NULL, const SurfaceFile* rightAreaSurf = NULL, const SurfaceFile* cerebAreaSurf = NULL); static OperationParameters* getParameters(); static void useParameters(OperationParameters* myParams, ProgressObject* myProgObj); static AString getCommandSwitch(); static AString getShortDescription(); }; typedef TemplateAutoOperation AutoAlgorithmCiftiAverageDenseROI; } #endif //__ALGORITHM_CIFTI_AVERAGE_DENSE_ROI_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Algorithms/AlgorithmCiftiAverageROICorrelation.cxx000066400000000000000000000740111300200146000323600ustar00rootroot00000000000000/*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 "AlgorithmCiftiAverageROICorrelation.h" #include "AlgorithmException.h" #include "CaretLogger.h" #include "CaretOMP.h" #include "CiftiFile.h" #include "FileInformation.h" #include "MetricFile.h" #include "SurfaceFile.h" #include "VolumeFile.h" #include #include #include #include using namespace caret; using namespace std; AString AlgorithmCiftiAverageROICorrelation::getCommandSwitch() { return "-cifti-average-roi-correlation"; } AString AlgorithmCiftiAverageROICorrelation::getShortDescription() { return "CORRELATE ROI AVERAGE WITH ALL ROWS THEN AVERAGE ACROSS SUBJECTS"; } OperationParameters* AlgorithmCiftiAverageROICorrelation::getParameters() { OperationParameters* ret = new OperationParameters(); ret->addCiftiOutputParameter(1, "cifti-out", "output cifti file"); OptionalParameter* ciftiRoiOpt = ret->createOptionalParameter(2, "-cifti-roi", "cifti file containing combined weights"); ciftiRoiOpt->addCiftiParameter(1, "roi-cifti", "the roi cifti file"); ciftiRoiOpt->createOptionalParameter(2, "-in-memory", "cache the roi in memory so that it isn't re-read for each input cifti"); OptionalParameter* leftRoiOpt = ret->createOptionalParameter(3, "-left-roi", "weights to use for left hempsphere"); leftRoiOpt->addMetricParameter(1, "roi-metric", "the left roi as a metric file"); OptionalParameter* rightRoiOpt = ret->createOptionalParameter(4, "-right-roi", "weights to use for right hempsphere"); rightRoiOpt->addMetricParameter(1, "roi-metric", "the right roi as a metric file"); OptionalParameter* cerebRoiOpt = ret->createOptionalParameter(5, "-cerebellum-roi", "weights to use for cerebellum surface"); cerebRoiOpt->addMetricParameter(1, "roi-metric", "the cerebellum roi as a metric file"); OptionalParameter* volRoiOpt = ret->createOptionalParameter(6, "-vol-roi", "voxel weights to use"); volRoiOpt->addVolumeParameter(1, "roi-vol", "the roi volume file"); OptionalParameter* leftAreaSurfOpt = ret->createOptionalParameter(7, "-left-area-surf", "specify the left surface for vertex area correction"); leftAreaSurfOpt->addSurfaceParameter(1, "left-surf", "the left surface file"); OptionalParameter* rightAreaSurfOpt = ret->createOptionalParameter(8, "-right-area-surf", "specify the right surface for vertex area correction"); rightAreaSurfOpt->addSurfaceParameter(1, "right-surf", "the right surface file"); OptionalParameter* cerebAreaSurfOpt = ret->createOptionalParameter(9, "-cerebellum-area-surf", "specify the cerebellum surface for vertex area correction"); cerebAreaSurfOpt->addSurfaceParameter(1, "cerebellum-surf", "the cerebellum surface file"); ParameterComponent* ciftiOpt = ret->createRepeatableParameter(10, "-cifti", "specify an input cifti file"); ciftiOpt->addCiftiParameter(1, "cifti-in", "a cifti file to average across"); ret->setHelpText( AString("Averages rows for each map of the ROI(s), takes the correlation of each ROI average to the rest of the rows in the same file, then averages the results across all files. ") + "ROIs are always treated as weighting functions, including negative values. " + "For efficiency, ensure that everything that is not intended to be used is zero in the ROI map. " + "If -cifti-roi is specified, -left-roi, -right-roi, -cerebellum-roi, and -vol-roi must not be specified. " + "If multiple non-cifti ROI files are specified, they must have the same number of columns." ); return ret; } void AlgorithmCiftiAverageROICorrelation::useParameters(OperationParameters* myParams, ProgressObject* myProgObj) { CiftiFile* ciftiOut = myParams->getOutputCifti(1); CiftiFile* ciftiROI = NULL; OptionalParameter* ciftiRoiOpt = myParams->getOptionalParameter(2); if (ciftiRoiOpt->m_present) { ciftiROI = ciftiRoiOpt->getCifti(1); if (ciftiRoiOpt->getOptionalParameter(2)->m_present) ciftiROI->convertToInMemory(); } MetricFile* leftROI = NULL; OptionalParameter* leftRoiOpt = myParams->getOptionalParameter(3); if (leftRoiOpt->m_present) { if (ciftiROI != NULL) throw AlgorithmException("-cifti-roi cannot be used with any other ROI option"); leftROI = leftRoiOpt->getMetric(1); } MetricFile* rightROI = NULL; OptionalParameter* rightRoiOpt = myParams->getOptionalParameter(4); if (rightRoiOpt->m_present) { if (ciftiROI != NULL) throw AlgorithmException("-cifti-roi cannot be used with any other ROI option"); rightROI = rightRoiOpt->getMetric(1); } MetricFile* cerebROI = NULL; OptionalParameter* cerebRoiOpt = myParams->getOptionalParameter(5); if (cerebRoiOpt->m_present) { if (ciftiROI != NULL) throw AlgorithmException("-cifti-roi cannot be used with any other ROI option"); cerebROI = cerebRoiOpt->getMetric(1); } VolumeFile* volROI = NULL; OptionalParameter* volRoiOpt = myParams->getOptionalParameter(6); if (volRoiOpt->m_present) { if (ciftiROI != NULL) throw AlgorithmException("-cifti-roi cannot be used with any other ROI option"); volROI = volRoiOpt->getVolume(1); } SurfaceFile* leftAreaSurf = NULL; OptionalParameter* leftAreaSurfOpt = myParams->getOptionalParameter(7); if (leftAreaSurfOpt->m_present) { leftAreaSurf = leftAreaSurfOpt->getSurface(1); } SurfaceFile* rightAreaSurf = NULL; OptionalParameter* rightAreaSurfOpt = myParams->getOptionalParameter(8); if (rightAreaSurfOpt->m_present) { rightAreaSurf = rightAreaSurfOpt->getSurface(1); } SurfaceFile* cerebAreaSurf = NULL; OptionalParameter* cerebAreaSurfOpt = myParams->getOptionalParameter(9); if (cerebAreaSurfOpt->m_present) { cerebAreaSurf = cerebAreaSurfOpt->getSurface(1); } vector ciftiList; const vector& ciftiInputs = *(myParams->getRepeatableParameterInstances(10)); if (ciftiInputs.size() == 0) throw AlgorithmException("at least one -cifti input is required"); for (int i = 0; i < (int)ciftiInputs.size(); ++i) { ciftiList.push_back(ciftiInputs[i]->getCifti(1)); } if (ciftiROI != NULL) { AlgorithmCiftiAverageROICorrelation(myProgObj, ciftiList, ciftiOut, ciftiROI, leftAreaSurf, rightAreaSurf, cerebAreaSurf); } else { AlgorithmCiftiAverageROICorrelation(myProgObj, ciftiList, ciftiOut, leftROI, rightROI, cerebROI, volROI, leftAreaSurf, rightAreaSurf, cerebAreaSurf); } } AlgorithmCiftiAverageROICorrelation::AlgorithmCiftiAverageROICorrelation(ProgressObject* myProgObj, const vector& ciftiList, CiftiFile* ciftiOut, const MetricFile* leftROI, const MetricFile* rightROI, const MetricFile* cerebROI, const VolumeFile* volROI, const SurfaceFile* leftAreaSurf, const SurfaceFile* rightAreaSurf, const SurfaceFile* cerebAreaSurf) : AbstractAlgorithm(myProgObj) { CaretAssert(ciftiOut != NULL); LevelProgress myProgress(myProgObj); int numCifti = (int)ciftiList.size(); if (numCifti < 1) throw AlgorithmException("no cifti files specified to average"); const CiftiXMLOld& baseXML = ciftiList[0]->getCiftiXMLOld(); int rowSize = baseXML.getNumberOfColumns(); int colSize = baseXML.getNumberOfRows(); bool first = true; const CaretMappableDataFile* nameFile = NULL; int numMaps = -1; vector leftAreaData, rightAreaData, cerebAreaData; float* leftAreaPointer = NULL, *rightAreaPointer = NULL, *cerebAreaPointer = NULL; if (leftROI != NULL) { if (leftAreaSurf != NULL) { if (leftROI->getNumberOfNodes() != leftAreaSurf->getNumberOfNodes()) throw AlgorithmException("left area surface and left roi have different number of nodes"); leftAreaSurf->computeNodeAreas(leftAreaData); leftAreaPointer = leftAreaData.data(); } first = false; numMaps = leftROI->getNumberOfMaps(); nameFile = leftROI; } if (rightROI != NULL) { if (rightAreaSurf != NULL) { if (rightROI->getNumberOfNodes() != rightAreaSurf->getNumberOfNodes()) throw AlgorithmException("right area surface and right roi have different number of nodes"); rightAreaSurf->computeNodeAreas(rightAreaData); rightAreaPointer = rightAreaData.data(); } if (first) { first = false; numMaps = rightROI->getNumberOfMaps(); nameFile = rightROI; } else { if (rightROI->getNumberOfMaps() != numMaps) throw AlgorithmException("right roi has a different number of maps"); } } if (cerebROI != NULL) { if (cerebAreaSurf != NULL) { if (cerebROI->getNumberOfNodes() != cerebAreaSurf->getNumberOfNodes()) throw AlgorithmException("cerebellum area surface and cerebellum roi have different number of nodes"); cerebAreaSurf->computeNodeAreas(cerebAreaData); cerebAreaPointer = cerebAreaData.data(); } if (first) { first = false; numMaps = cerebROI->getNumberOfMaps(); nameFile = cerebROI; } else { if (cerebROI->getNumberOfMaps() != numMaps) throw AlgorithmException("cerebellum roi has a different number of maps"); } } if (volROI != NULL) { if (first) { first = false; numMaps = volROI->getNumberOfMaps(); nameFile = volROI; } else { if (volROI->getNumberOfMaps() != numMaps) throw AlgorithmException("volume roi has a different number of maps"); } } if (first) throw AlgorithmException("no roi files provided"); for (int i = 0; i < numCifti; ++i) { const CiftiXMLOld& thisXML = ciftiList[i]->getCiftiXMLOld(); if (!thisXML.mappingMatches(CiftiXMLOld::ALONG_COLUMN, baseXML, CiftiXMLOld::ALONG_COLUMN)) throw AlgorithmException("cifti space does not match between cifti #1 and #" + AString::number(i + 1)); if (thisXML.getNumberOfColumns() != rowSize) throw AlgorithmException("row length doesn't match between cifti #1 and #" + AString::number(i + 1)); if (leftROI != NULL) { verifySurfaceComponent(i, ciftiList[i], StructureEnum::CORTEX_LEFT, leftROI); } if (rightROI != NULL) { verifySurfaceComponent(i, ciftiList[i], StructureEnum::CORTEX_RIGHT, rightROI); } if (cerebROI != NULL) { verifySurfaceComponent(i, ciftiList[i], StructureEnum::CEREBELLUM, cerebROI); } if (volROI != NULL) { verifyVolumeComponent(i, ciftiList[i], volROI); } } vector > tempresult(colSize, vector(numMaps)); CiftiXMLOld newXml = baseXML; newXml.resetRowsToScalars(numMaps); for (int i = 0; i < numMaps; ++i) { newXml.setMapNameForIndex(CiftiXMLOld::ALONG_ROW, i, nameFile->getMapName(i)); } ciftiOut->setCiftiXML(newXml); if (numCifti > 1)//skip averaging in single subject case { vector > accum(colSize, vector(numMaps, 0.0)); for (int i = 0; i < numCifti; ++i) { processCifti(ciftiList[i], tempresult, leftROI, rightROI, cerebROI, volROI, numMaps, leftAreaPointer, rightAreaPointer, cerebAreaPointer); for (int j = 0; j < colSize; ++j) { for (int myMap = 0; myMap < numMaps; ++myMap) { accum[j][myMap] += tempresult[j][myMap]; } } } for (int i = 0; i < colSize; ++i) { for (int myMap = 0; myMap < numMaps; ++myMap) { tempresult[i][myMap] = accum[i][myMap] / numCifti; } ciftiOut->setRow(tempresult[i].data(), i); } } else { processCifti(ciftiList[0], tempresult, leftROI, rightROI, cerebROI, volROI, numMaps, leftAreaPointer, rightAreaPointer, cerebAreaPointer); for (int i = 0; i < colSize; ++i) { ciftiOut->setRow(tempresult[i].data(), i); } } } AlgorithmCiftiAverageROICorrelation::AlgorithmCiftiAverageROICorrelation(ProgressObject* myProgObj, const vector& ciftiList, CiftiFile* ciftiOut, const CiftiFile* ciftiROI, const SurfaceFile* leftAreaSurf, const SurfaceFile* rightAreaSurf, const SurfaceFile* cerebAreaSurf): AbstractAlgorithm(myProgObj) { CaretAssert(ciftiOut != NULL); LevelProgress myProgress(myProgObj); int numCifti = (int)ciftiList.size(); if (numCifti < 1) throw AlgorithmException("no cifti files specified to average"); const CiftiXMLOld baseXML = ciftiList[0]->getCiftiXMLOld(), roiXML = ciftiROI->getCiftiXMLOld(); int rowSize = baseXML.getNumberOfColumns(); int colSize = baseXML.getNumberOfRows(); int numMaps = ciftiROI->getNumberOfColumns(); if (!baseXML.mappingMatches(CiftiXMLOld::ALONG_COLUMN, roiXML, CiftiXMLOld::ALONG_COLUMN)) throw AlgorithmException("cifti roi doesn't match cifti space of data"); for (int i = 1; i < numCifti; ++i) { if (!baseXML.mappingMatches(CiftiXMLOld::ALONG_COLUMN, ciftiList[i]->getCiftiXMLOld(), CiftiXMLOld::ALONG_COLUMN)) throw AlgorithmException("cifti space does not match between cifti #1 and #" + AString::number(i + 1)); if (ciftiList[i]->getNumberOfColumns() != rowSize) throw AlgorithmException("row length doesn't match between cifti #1 and #" + AString::number(i + 1)); } vector leftAreaData, rightAreaData, cerebAreaData; float* leftAreaPointer = NULL, *rightAreaPointer = NULL, *cerebAreaPointer = NULL; if (leftAreaSurf != NULL) { if (baseXML.getSurfaceNumberOfNodes(CiftiXMLOld::ALONG_COLUMN, StructureEnum::CORTEX_LEFT) != leftAreaSurf->getNumberOfNodes()) { throw AlgorithmException("left area surface and left cortex cifti structure have different number of nodes"); } leftAreaSurf->computeNodeAreas(leftAreaData); leftAreaPointer = leftAreaData.data(); } if (rightAreaSurf != NULL) { if (baseXML.getSurfaceNumberOfNodes(CiftiXMLOld::ALONG_COLUMN, StructureEnum::CORTEX_RIGHT) != rightAreaSurf->getNumberOfNodes()) { throw AlgorithmException("right area surface and right cortex cifti structure have different number of nodes"); } rightAreaSurf->computeNodeAreas(rightAreaData); rightAreaPointer = rightAreaData.data(); } if (cerebAreaSurf != NULL) { if (baseXML.getSurfaceNumberOfNodes(CiftiXMLOld::ALONG_COLUMN, StructureEnum::CEREBELLUM) != cerebAreaSurf->getNumberOfNodes()) { throw AlgorithmException("cerebellum area surface and cerebellum cortex cifti structure have different number of nodes"); } cerebAreaSurf->computeNodeAreas(cerebAreaData); cerebAreaPointer = cerebAreaData.data(); } vector > tempresult(colSize, vector(numMaps)); CiftiXMLOld newXml = baseXML; newXml.resetRowsToScalars(numMaps); for (int i = 0; i < numMaps; ++i) { newXml.setMapNameForIndex(CiftiXMLOld::ALONG_ROW, i, roiXML.getMapNameForRowIndex(i)); } ciftiOut->setCiftiXML(newXml); if (numCifti > 1)//skip averaging in single subject case { vector > accum(colSize, vector(numMaps, 0.0)); for (int i = 0; i < numCifti; ++i) { processCifti(ciftiList[i], tempresult, ciftiROI, numMaps, leftAreaPointer, rightAreaPointer, cerebAreaPointer); for (int j = 0; j < colSize; ++j) { for (int myMap = 0; myMap < numMaps; ++myMap) { accum[j][myMap] += tempresult[j][myMap]; } } } for (int i = 0; i < colSize; ++i) { for (int myMap = 0; myMap < numMaps; ++myMap) { tempresult[i][myMap] = accum[i][myMap] / numCifti; } ciftiOut->setRow(tempresult[i].data(), i); } } else { processCifti(ciftiList[0], tempresult, ciftiROI, numMaps, leftAreaPointer, rightAreaPointer, cerebAreaPointer); for (int i = 0; i < colSize; ++i) { ciftiOut->setRow(tempresult[i].data(), i); } } } void AlgorithmCiftiAverageROICorrelation::verifySurfaceComponent(const int& index, const CiftiFile* myCifti, const StructureEnum::Enum& myStruct, const MetricFile* myRoi) { const CiftiXMLOld& myXml = myCifti->getCiftiXMLOld(); if (!myXml.hasColumnSurfaceData(myStruct)) { CaretLogWarning("cifti file #" + AString::number(index + 1) + " missing structure " + StructureEnum::toName(myStruct)); return; } if (myRoi->getNumberOfNodes() != myXml.getColumnSurfaceNumberOfNodes(myStruct)) throw AlgorithmException("cifti #" + AString::number(index + 1) + " number of vertices does not match roi"); } void AlgorithmCiftiAverageROICorrelation::verifyVolumeComponent(const int& index, const CiftiFile* myCifti, const VolumeFile* volROI) { const CiftiXMLOld& myXml = myCifti->getCiftiXMLOld(); int64_t dims[3]; vector > sform; myXml.getVolumeDimsAndSForm(dims, sform); if (!volROI->matchesVolumeSpace(dims, sform)) throw AlgorithmException("cifti file #" + AString::number(index + 1) + " doesn't match the ROI volume's space"); vector myMap; myXml.getVolumeMapForColumns(myMap); int mapSize = (int)myMap.size(); for (int i = 0; i < mapSize; ++i) { if (!volROI->indexValid(myMap[i].m_ijk)) throw AlgorithmException("cifti file #" + AString::number(index + 1) + " lists invalid voxels"); } } void AlgorithmCiftiAverageROICorrelation::processCifti(const CiftiFile* myCifti, vector >& output, const MetricFile* leftROI, const MetricFile* rightROI,const MetricFile* cerebROI, const VolumeFile* volROI, const int& numMaps, const float* leftAreas, const float* rightAreas, const float* cerebAreas) { int rowSize = myCifti->getNumberOfColumns(); int colSize = myCifti->getNumberOfRows(); vector > average(numMaps, vector(rowSize)); vector rrs(numMaps); for (int myMap = 0; myMap < numMaps; ++myMap) { vector accumarray(rowSize, 0.0); addSurface(myCifti, StructureEnum::CORTEX_LEFT, accumarray, leftROI, myMap, leftAreas);//we don't need to keep track of the kernel sums because we are correlating addSurface(myCifti, StructureEnum::CORTEX_RIGHT, accumarray, rightROI, myMap, rightAreas); addSurface(myCifti, StructureEnum::CEREBELLUM, accumarray, cerebROI, myMap, cerebAreas); addVolume(myCifti, accumarray, volROI, myMap); double accum = 0.0; for (int i = 0; i < rowSize; ++i) { accum += accumarray[i]; } double mean = accum / rowSize; accum = 0.0; for (int i = 0; i < rowSize; ++i) { average[myMap][i] = accumarray[i] - mean;//remove the mean from the average timeseries to optimize the correlation, and change back to float for possible speed improvement accum += average[myMap][i] * average[myMap][i]; } rrs[myMap] = sqrt(accum);//compute this only once } int curRow = 0; #pragma omp CARET_PAR { vector rowscratch(rowSize); #pragma omp CARET_FOR schedule(dynamic) for (int i = 0; i < colSize; ++i) { int myRow; #pragma omp critical { myRow = curRow;//force sequential reading ++curRow; myCifti->getRow(rowscratch.data(), myRow);//and never read multiple rows at once from the same file } double tempaccum = 0.0;//compute mean of new row for (int j = 0; j < rowSize; ++j) { tempaccum += rowscratch[j]; } float thismean = tempaccum / rowSize; tempaccum = 0.0; for (int j = 0; j < rowSize; ++j) { rowscratch[j] -= thismean;//demean tempaccum += rowscratch[j] * rowscratch[j];//precompute rrs } float thisrrs = sqrt(tempaccum); for (int myMap = 0; myMap < numMaps; ++myMap) { double corraccum = 0.0;//correlate for (int j = 0; j < rowSize; ++j) { corraccum += rowscratch[j] * average[myMap][j];//gather the correlation } corraccum /= rrs[myMap] * thisrrs; if (corraccum > 0.999999) corraccum = 0.999999; if (corraccum < -0.999999) corraccum = -0.999999; output[myRow][myMap] = 0.5 * log((1 + corraccum) / (1 - corraccum));//fisher z transform, needed for averaging } } } } void AlgorithmCiftiAverageROICorrelation::processCifti(const CiftiFile* myCifti, vector >& output, const CiftiFile* ciftiROI, const int& numMaps, const float* leftAreas, const float* rightAreas, const float* cerebAreas) { int rowSize = myCifti->getNumberOfColumns(); int colSize = myCifti->getNumberOfRows(); vector > average(numMaps, vector(rowSize)); vector rrs(numMaps); const CiftiXMLOld& roiXML = ciftiROI->getCiftiXMLOld(); vector surfStructures, ignored; roiXML.getStructureLists(CiftiXMLOld::ALONG_COLUMN, surfStructures, ignored); vector roiScratch(numMaps), dataScratch(rowSize); { vector > accumarray(numMaps, vector(rowSize, 0.0)); for (int whichStruct = 0; whichStruct < (int)surfStructures.size(); ++whichStruct) { const float* areaPtr = NULL; switch (surfStructures[whichStruct]) { case StructureEnum::CORTEX_LEFT: areaPtr = leftAreas; break; case StructureEnum::CORTEX_RIGHT: areaPtr = rightAreas; break; case StructureEnum::CEREBELLUM: areaPtr = cerebAreas; break; default: break; } vector myMap; roiXML.getSurfaceMap(CiftiXMLOld::ALONG_COLUMN, myMap, surfStructures[whichStruct]); for (int i = 0; i < (int)myMap.size(); ++i) { bool dataLoaded = false; ciftiROI->getRow(roiScratch.data(), myMap[i].m_ciftiIndex); for (int j = 0; j < numMaps; ++j) { if (roiScratch[j] != 0.0f) { if (!dataLoaded) { myCifti->getRow(dataScratch.data(), myMap[i].m_ciftiIndex); dataLoaded = true; } if (areaPtr != NULL) { for (int k = 0; k < rowSize; ++k) { accumarray[j][k] += dataScratch[k] * roiScratch[j] * areaPtr[myMap[i].m_surfaceNode]; } } else { for (int k = 0; k < rowSize; ++k) { accumarray[j][k] += dataScratch[k] * roiScratch[j]; } } } } } } vector myMap; roiXML.getVolumeMap(CiftiXMLOld::ALONG_COLUMN, myMap); for (int i = 0; i < (int)myMap.size(); ++i) { bool dataLoaded = false; ciftiROI->getRow(roiScratch.data(), myMap[i].m_ciftiIndex); for (int j = 0; j < numMaps; ++j) { if (roiScratch[j] != 0.0f) { if (!dataLoaded) { myCifti->getRow(dataScratch.data(), myMap[i].m_ciftiIndex); dataLoaded = true; } for (int k = 0; k < rowSize; ++k) { accumarray[j][k] += dataScratch[k] * roiScratch[j]; } } } } for (int i = 0; i < numMaps; ++i) { double accum = 0.0; for (int j = 0; j < rowSize; ++j) { accum += accumarray[i][j]; } float mean = accum / rowSize; accum = 0.0; for (int j = 0; j < rowSize; ++j) { average[i][j] = accumarray[i][j] - mean; accum += average[i][j] * average[i][j]; } vector().swap(accumarray[i]);//hack to free memory before it goes out of scope rrs[i] = sqrt(accum); } } int curRow = 0; #pragma omp CARET_PAR { vector rowscratch(rowSize); #pragma omp CARET_FOR schedule(dynamic) for (int i = 0; i < colSize; ++i) { int myRow; #pragma omp critical { myRow = curRow;//force sequential reading ++curRow; myCifti->getRow(rowscratch.data(), myRow);//and never read multiple rows at once from the same file } double tempaccum = 0.0;//compute mean of new row for (int j = 0; j < rowSize; ++j) { tempaccum += rowscratch[j]; } float thismean = tempaccum / rowSize; tempaccum = 0.0; for (int j = 0; j < rowSize; ++j) { rowscratch[j] -= thismean;//demean tempaccum += rowscratch[j] * rowscratch[j];//precompute rrs } float thisrrs = sqrt(tempaccum); for (int myMap = 0; myMap < numMaps; ++myMap) { double corraccum = 0.0;//correlate for (int j = 0; j < rowSize; ++j) { corraccum += rowscratch[j] * average[myMap][j];//gather the correlation } corraccum /= rrs[myMap] * thisrrs; if (corraccum > 0.999999) corraccum = 0.999999; if (corraccum < -0.999999) corraccum = -0.999999; output[myRow][myMap] = 0.5 * log((1 + corraccum) / (1 - corraccum));//fisher z transform, needed for averaging } } } } void AlgorithmCiftiAverageROICorrelation::addSurface(const CiftiFile* myCifti, StructureEnum::Enum myStruct, vector& accum, const MetricFile* myRoi, const int& myMap, const float* myAreas) { if (myRoi == NULL) return; vector surfaceMap = myCifti->getCiftiXML().getBrainModelsMap(CiftiXML::ALONG_COLUMN).getSurfaceMap(myStruct); int mapSize = (int)surfaceMap.size(); int rowSize = myCifti->getNumberOfColumns(); vector rowscratch(rowSize); if (myAreas != NULL) { for (int i = 0; i < mapSize; ++i) { float value = myRoi->getValue(surfaceMap[i].m_surfaceNode, myMap); if (value != 0.0f) { float thisArea = myAreas[surfaceMap[i].m_surfaceNode]; myCifti->getRow(rowscratch.data(), surfaceMap[i].m_ciftiIndex); for (int j = 0; j < rowSize; ++j) { accum[j] += rowscratch[j] * value * thisArea; } } } } else { for (int i = 0; i < mapSize; ++i) { float value = myRoi->getValue(surfaceMap[i].m_surfaceNode, myMap); if (value != 0.0f) { myCifti->getRow(rowscratch.data(), surfaceMap[i].m_ciftiIndex); for (int j = 0; j < rowSize; ++j) { accum[j] += rowscratch[j] * value; } } } } } void AlgorithmCiftiAverageROICorrelation::addVolume(const CiftiFile* myCifti, vector& accum, const VolumeFile* myRoi, const int& myMap) { if (myRoi == NULL) return; vector volMap = myCifti->getCiftiXML().getBrainModelsMap(CiftiXML::ALONG_COLUMN).getFullVolumeMap(); int mapSize = (int)volMap.size(); int rowSize = myCifti->getNumberOfColumns(); vector rowscratch(rowSize); for (int i = 0; i < mapSize; ++i) { if (myRoi->getValue(volMap[i].m_ijk, myMap) > 0.0f) { myCifti->getRow(rowscratch.data(), volMap[i].m_ciftiIndex); for (int j = 0; j < rowSize; ++j) { accum[j] += rowscratch[j]; } } } } float AlgorithmCiftiAverageROICorrelation::getAlgorithmInternalWeight() { return 1.0f;//override this if needed, if the progress bar isn't smooth } float AlgorithmCiftiAverageROICorrelation::getSubAlgorithmWeight() { //return AlgorithmInsertNameHere::getAlgorithmWeight();//if you use a subalgorithm return 0.0f; } connectome-workbench-1.2.3+git41-gc4c6c90/src/Algorithms/AlgorithmCiftiAverageROICorrelation.h000066400000000000000000000070631300200146000320100ustar00rootroot00000000000000#ifndef __ALGORITHM_CIFTI_AVERAGE_ROI_CORRELATION_H__ #define __ALGORITHM_CIFTI_AVERAGE_ROI_CORRELATION_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 "AbstractAlgorithm.h" #include "StructureEnum.h" #include namespace caret { class AlgorithmCiftiAverageROICorrelation : public AbstractAlgorithm { AlgorithmCiftiAverageROICorrelation(); void verifySurfaceComponent(const int& index, const CiftiFile* myCifti, const StructureEnum::Enum& myStruct, const MetricFile* myRoi); void verifyVolumeComponent(const int& index, const CiftiFile* myCifti, const VolumeFile* volROI); void processCifti(const CiftiFile* myCifti, std::vector >& output, const MetricFile* leftROI, const MetricFile* rightROI, const MetricFile* cerebROI, const VolumeFile* volROI, const int& numMaps, const float* leftAreas, const float* rightAreas, const float* cerebAreas); void processCifti(const CiftiFile* myCifti, std::vector >& output, const CiftiFile* ciftiROI, const int& numMaps, const float* leftAreas, const float* rightAreas, const float* cerebAreas); void addSurface(const CiftiFile* myCifti, StructureEnum::Enum myStruct, std::vector& accum, const MetricFile* myRoi, const int& myMap, const float* myAreas); void addVolume(const CiftiFile* myCifti, std::vector& accum, const VolumeFile* myRoi, const int& myMap); protected: static float getSubAlgorithmWeight(); static float getAlgorithmInternalWeight(); public: AlgorithmCiftiAverageROICorrelation(ProgressObject* myProgObj, const std::vector& ciftiList, CiftiFile* ciftiOut, const MetricFile* leftROI = NULL, const MetricFile* rightROI = NULL, const MetricFile* cerebROI = NULL, const VolumeFile* volROI = NULL, const SurfaceFile* leftAreaSurf = NULL, const SurfaceFile* rightAreaSurf = NULL, const SurfaceFile* cerebAreaSurf = NULL); AlgorithmCiftiAverageROICorrelation(ProgressObject* myProgObj, const std::vector& ciftiList, CiftiFile* ciftiOut, const CiftiFile* ciftiROI, const SurfaceFile* leftAreaSurf = NULL, const SurfaceFile* rightAreaSurf = NULL, const SurfaceFile* cerebAreaSurf = NULL); static OperationParameters* getParameters(); static void useParameters(OperationParameters* myParams, ProgressObject* myProgObj); static AString getCommandSwitch(); static AString getShortDescription(); }; typedef TemplateAutoOperation AutoAlgorithmCiftiAverageROICorrelation; } #endif //__ALGORITHM_CIFTI_AVERAGE_ROI_CORRELATION_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Algorithms/AlgorithmCiftiCorrelation.cxx000066400000000000000000001024571300200146000305210ustar00rootroot00000000000000/*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 "AlgorithmCiftiCorrelation.h" #include "AlgorithmException.h" #include "AlgorithmCiftiSeparate.h" #include "CiftiFile.h" #include "MetricFile.h" #include "VolumeFile.h" #include "CaretLogger.h" #include "MathFunctions.h" #include "CaretOMP.h" #include "FileInformation.h" #include "CaretPointer.h" #include "dot_wrapper.h" #include #include #include using namespace caret; using namespace std; AString AlgorithmCiftiCorrelation::getCommandSwitch() { return "-cifti-correlation"; } AString AlgorithmCiftiCorrelation::getShortDescription() { return "GENERATE CORRELATION OF ROWS IN A CIFTI FILE"; } OperationParameters* AlgorithmCiftiCorrelation::getParameters() { OperationParameters* ret = new OperationParameters(); ret->addCiftiParameter(1, "cifti", "input cifti file"); ret->addCiftiOutputParameter(2, "cifti-out", "output cifti file"); OptionalParameter* roiOverrideOpt = ret->createOptionalParameter(3, "-roi-override", "perform correlation from a subset of rows to all rows"); OptionalParameter* leftRoiOpt = roiOverrideOpt->createOptionalParameter(1, "-left-roi", "use an roi for left hempsphere"); leftRoiOpt->addMetricParameter(1, "roi-metric", "the left roi as a metric file"); OptionalParameter* rightRoiOpt = roiOverrideOpt->createOptionalParameter(2, "-right-roi", "use an roi for right hempsphere"); rightRoiOpt->addMetricParameter(1, "roi-metric", "the right roi as a metric file"); OptionalParameter* cerebRoiOpt = roiOverrideOpt->createOptionalParameter(3, "-cerebellum-roi", "use an roi for cerebellum"); cerebRoiOpt->addMetricParameter(1, "roi-metric", "the cerebellum roi as a metric file"); OptionalParameter* volRoiOpt = roiOverrideOpt->createOptionalParameter(4, "-vol-roi", "use an roi for volume"); volRoiOpt->addVolumeParameter(1, "roi-vol", "the volume roi file"); OptionalParameter* ciftiRoiOpt = roiOverrideOpt->createOptionalParameter(5, "-cifti-roi", "use a cifti file for combined rois"); ciftiRoiOpt->addCiftiParameter(1, "roi-cifti", "the cifti roi file"); OptionalParameter* weightsOpt = ret->createOptionalParameter(4, "-weights", "specify column weights"); weightsOpt->addStringParameter(1, "weight-file", "text file containing one weight per column"); ret->createOptionalParameter(5, "-fisher-z", "apply fisher small z transform (ie, artanh) to correlation"); ret->createOptionalParameter(7, "-no-demean", "instead of correlation, do dot product of rows, then normalize by diagonal"); ret->createOptionalParameter(8, "-covariance", "compute covariance instead of correlation"); OptionalParameter* memLimitOpt = ret->createOptionalParameter(6, "-mem-limit", "restrict memory usage"); memLimitOpt->addDoubleParameter(1, "limit-GB", "memory limit in gigabytes"); ret->setHelpText( AString("For each row (or each row inside an roi if -roi-override is specified), correlate to all other rows. ") + "The -cifti-roi suboption to -roi-override may not be specified with any other -*-roi suboption, but you may specify the other -*-roi suboptions together.\n\n" + "When using the -fisher-z option, the output is NOT a Z-score, it is artanh(r), to do further math on this output, consider using -cifti-math.\n\n" + "Restricting the memory usage will make it calculate the output in chunks, and if the input file size is more than 70% of the memory limit, " + "it will also read through the input file as rows are required, resulting in several passes through the input file (once per chunk). " + "Memory limit does not need to be an integer, you may also specify 0 to calculate a single output row at a time (this may be very slow)." ); return ret; } void AlgorithmCiftiCorrelation::useParameters(OperationParameters* myParams, ProgressObject* myProgObj) { CiftiFile* myCifti = myParams->getCifti(1); CiftiFile* myCiftiOut = myParams->getOutputCifti(2); OptionalParameter* roiOverrideOpt = myParams->getOptionalParameter(3); bool roiOverrideMode = roiOverrideOpt->m_present; MetricFile* leftRoi = NULL, *rightRoi = NULL, *cerebRoi = NULL; VolumeFile* volRoi = NULL; CiftiFile* ciftiRoi = NULL; bool ciftiRoiMode = true; if (roiOverrideMode) { OptionalParameter* leftRoiOpt = roiOverrideOpt->getOptionalParameter(1); if (leftRoiOpt->m_present) { leftRoi = leftRoiOpt->getMetric(1); ciftiRoiMode = false; } OptionalParameter* rightRoiOpt = roiOverrideOpt->getOptionalParameter(2); if (rightRoiOpt->m_present) { rightRoi = rightRoiOpt->getMetric(1); ciftiRoiMode = false; } OptionalParameter* cerebRoiOpt = roiOverrideOpt->getOptionalParameter(3); if (cerebRoiOpt->m_present) { cerebRoi = cerebRoiOpt->getMetric(1); ciftiRoiMode = false; } OptionalParameter* volRoiOpt = roiOverrideOpt->getOptionalParameter(4); if (volRoiOpt->m_present) { volRoi = volRoiOpt->getVolume(1); ciftiRoiMode = false; } OptionalParameter* ciftiRoiOpt = roiOverrideOpt->getOptionalParameter(5); if (ciftiRoiOpt->m_present) { if (!ciftiRoiMode) throw AlgorithmException("-cifti-roi cannot be specified with any other -*-roi option"); ciftiRoi = ciftiRoiOpt->getCifti(1); } else { if (ciftiRoiMode) throw AlgorithmException("-roi-override requires a -*-roi suboption"); } } OptionalParameter* weightsOpt = myParams->getOptionalParameter(4); vector* weights = NULL, realweights;//NOTE: realweights is NOT a pointer if (weightsOpt->m_present) { weights = &realweights;//point it to the actual vector to signify the option is present AString weightFileName = weightsOpt->getString(1); FileInformation textFileInfo(weightFileName); if (!textFileInfo.exists()) { throw AlgorithmException("weight list file doesn't exist"); } fstream weightListFile(weightFileName.toLocal8Bit().constData(), fstream::in); if (!weightListFile.good()) { throw AlgorithmException("error reading weight list file"); } while (weightListFile.good()) { float weight; if (!(weightListFile >> weight))//yes, this is how you check fstream for successfully extracted output. seriously. { break; } realweights.push_back(weight); } } bool fisherZ = myParams->getOptionalParameter(5)->m_present; float memLimitGB = -1.0f; OptionalParameter* memLimitOpt = myParams->getOptionalParameter(6); if (memLimitOpt->m_present) { memLimitGB = (float)memLimitOpt->getDouble(1); if (memLimitGB < 0.0f) { throw AlgorithmException("memory limit cannot be negative"); } } bool noDemean = myParams->getOptionalParameter(7)->m_present; bool covariance = myParams->getOptionalParameter(8)->m_present; if (roiOverrideMode) { if (ciftiRoiMode) { AlgorithmCiftiCorrelation(myProgObj, myCifti, myCiftiOut, ciftiRoi, weights, fisherZ, memLimitGB, noDemean); } else { AlgorithmCiftiCorrelation(myProgObj, myCifti, myCiftiOut, leftRoi, rightRoi, cerebRoi, volRoi, weights, fisherZ, memLimitGB, noDemean); } } else { AlgorithmCiftiCorrelation(myProgObj, myCifti, myCiftiOut, weights, fisherZ, memLimitGB, noDemean, covariance); } } AlgorithmCiftiCorrelation::AlgorithmCiftiCorrelation(ProgressObject* myProgObj, const CiftiFile* myCifti, CiftiFile* myCiftiOut, const vector* weights, const bool& fisherZ, const float& memLimitGB, const bool& noDemean, const bool& covariance) : AbstractAlgorithm(myProgObj) { LevelProgress myProgress(myProgObj); if (covariance) { if (fisherZ) throw AlgorithmException("cannot apply fisher z transformation to covariance"); } init(myCifti, weights, noDemean, covariance); int numRows = myCifti->getNumberOfRows(); CiftiXMLOld newXML = myCifti->getCiftiXMLOld(); newXML.applyColumnMapToRows(); myCiftiOut->setCiftiXML(newXML); int numCacheRows; bool cacheFullInput = true; if (memLimitGB >= 0.0f) { numCacheRows = numRowsForMem(memLimitGB, cacheFullInput); } else { numCacheRows = numRows; } if (numCacheRows > numRows) numCacheRows = numRows; if (cacheFullInput) { if (numCacheRows != numRows) CaretLogInfo("computing " + AString::number(numCacheRows) + " rows at a time"); } else { CaretLogInfo("computing " + AString::number(numCacheRows) + " rows at a time, reading rows as needed during processing"); } vector > outRows; if (cacheFullInput) { for (int i = 0; i < numRows; ++i) { cacheRow(i); } } for (int startrow = 0; startrow < numRows; startrow += numCacheRows) { int endrow = startrow + numCacheRows; if (endrow > numRows) endrow = numRows; outRows.resize(endrow - startrow); for (int i = startrow; i < endrow; ++i) { if (!cacheFullInput) { cacheRow(i);//preload the rows in a range which we will reuse as much as possible during one row by row scan } if (outRows[i - startrow].size() != numRows) { outRows[i - startrow] = CaretArray(numRows); } } int curRow = 0;//because we can't trust the order threads hit the critical section #pragma omp CARET_PARFOR schedule(dynamic) for (int i = 0; i < numRows; ++i) { float movingRrs; int myrow; const float* movingRow; #pragma omp critical {//CiftiFile may explode if we request multiple rows concurrently (needs mutexes), but we should force sequential requests anyway myrow = curRow;//so, manually force it to read sequentially ++curRow; movingRow = getRow(myrow, movingRrs); } for (int j = startrow; j < endrow; ++j) { if (myrow >= startrow && myrow < endrow)//check whether we are in the output memory area { if (j >= myrow)//if so, only compute one half, and store both places { float cacheRrs; const float* cacheRow = getRow(j, cacheRrs, true); outRows[j - startrow][myrow] = correlate(movingRow, movingRrs, cacheRow, cacheRrs, fisherZ); outRows[myrow - startrow][j] = outRows[j - startrow][myrow]; } } else { float cacheRrs; const float* cacheRow = getRow(j, cacheRrs, true); outRows[j - startrow][myrow] = correlate(movingRow, movingRrs, cacheRow, cacheRrs, fisherZ); } } } for (int i = startrow; i < endrow; ++i) { myCiftiOut->setRow(outRows[i - startrow], i); } if (!cacheFullInput) { clearCache();//tell the cache we are going to preload a different set of rows now } } if (cacheFullInput) { clearCache();//don't currently need to do this, its just for completeness } } AlgorithmCiftiCorrelation::AlgorithmCiftiCorrelation(ProgressObject* myProgObj, const CiftiFile* myCifti, CiftiFile* myCiftiOut, const MetricFile* leftRoi, const MetricFile* rightRoi, const MetricFile* cerebRoi, const VolumeFile* volRoi, const vector* weights, const bool& fisherZ, const float& memLimitGB, const bool& noDemean, const bool& covariance) : AbstractAlgorithm(myProgObj) { LevelProgress myProgress(myProgObj); if (covariance) { if (fisherZ) throw AlgorithmException("cannot apply fisher z transformation to covariance"); } init(myCifti, weights, noDemean, covariance); const CiftiXMLOld& origXML = myCifti->getCiftiXMLOld(); if (origXML.getColumnMappingType() != CIFTI_INDEX_TYPE_BRAIN_MODELS) { throw AlgorithmException("cannot use ROIs on this cifti, columns are not brain models"); } CiftiXMLOld newXML = origXML; vector surfList, volList; origXML.getStructureListsForColumns(surfList, volList); newXML.applyColumnMapToRows(); newXML.resetColumnsToBrainModels(); vector > ciftiIndexList; int newCiftiIndex = 0; for (int i = 0; i < (int)surfList.size(); ++i) { const MetricFile* myRoi = NULL; switch (surfList[i]) { case StructureEnum::CORTEX_LEFT: myRoi = leftRoi; break; case StructureEnum::CORTEX_RIGHT: myRoi = rightRoi; break; case StructureEnum::CEREBELLUM: myRoi = cerebRoi; break; default: break; } if (myRoi != NULL) { if (myRoi->getNumberOfNodes() != origXML.getColumnSurfaceNumberOfNodes(surfList[i])) { throw AlgorithmException("surface roi has the wrong number of vertices for structure " + StructureEnum::toName(surfList[i])); } const CiftiBrainModelsMap& myDenseMap = myCifti->getCiftiXML().getBrainModelsMap(CiftiXML::ALONG_COLUMN); vector myMap = myDenseMap.getSurfaceMap(surfList[i]); int numNodes = myDenseMap.getSurfaceNumberOfNodes(surfList[i]); int mapsize = (int)myMap.size(); vector tempNodeList; for (int j = 0; j < mapsize; ++j) { int myNode = myMap[j].m_surfaceNode; if (myRoi->getValue(myNode, 0) > 0.0f) { tempNodeList.push_back(myNode); ciftiIndexList.push_back(std::pair(myMap[j].m_ciftiIndex, newCiftiIndex)); ++newCiftiIndex; } } if (tempNodeList.size() > 0)//don't add it if it is empty { newXML.addSurfaceModelToColumns(numNodes, surfList[i], tempNodeList); } } } if (volRoi != NULL) { int64_t origDims[3]; vector > origSForm; origXML.getVolumeDimsAndSForm(origDims, origSForm); if (!volRoi->matchesVolumeSpace(origDims, origSForm)) { throw AlgorithmException("roi volume space doesn't match cifti volume space"); } for (int i = 0; i < (int)volList.size(); ++i) { vector myMap; origXML.getVolumeStructureMapForColumns(myMap, volList[i]); vector tempVoxList; int64_t numVoxels = (int64_t)myMap.size(); for (int64_t j = 0; j < numVoxels; ++j) { if (volRoi->getValue(myMap[j].m_ijk) > 0.0f) { tempVoxList.push_back(myMap[j].m_ijk[0]); tempVoxList.push_back(myMap[j].m_ijk[1]); tempVoxList.push_back(myMap[j].m_ijk[2]); ciftiIndexList.push_back(std::pair(myMap[j].m_ciftiIndex, newCiftiIndex)); ++newCiftiIndex; } } if (tempVoxList.size() > 0) { newXML.addVolumeModelToColumns(tempVoxList, volList[i]); } } } myCiftiOut->setCiftiXML(newXML); int numSelected = (int)ciftiIndexList.size(), numRows = myCifti->getNumberOfRows(); int numCacheRows; bool cacheFullInput = true; if (memLimitGB >= 0.0f) { numCacheRows = numRowsForMem(memLimitGB, cacheFullInput); } else { numCacheRows = numSelected; } if (numCacheRows > numSelected) numCacheRows = numSelected; if (cacheFullInput) { if (numCacheRows != numSelected) CaretLogInfo("computing " + AString::number(numCacheRows) + " rows at a time"); } else { CaretLogInfo("computing " + AString::number(numCacheRows) + " rows at a time, reading rows as needed during processing"); } vector > outRows; if (cacheFullInput) { for (int i = 0; i < numRows; ++i) { cacheRow(i); } } CaretArray indexReverse(numRows, -1); for (int startrow = 0; startrow < numSelected; startrow += numCacheRows) { int endrow = startrow + numCacheRows; if (endrow > numSelected) endrow = numSelected; outRows.resize(endrow - startrow); int curRow = 0;//because we can't trust the order threads hit the critical section for (int i = startrow; i < endrow; ++i) { if (!cacheFullInput) { cacheRow(ciftiIndexList[i].first);//preload the rows in a range which we will reuse as much as possible during one row by row scan } if (outRows[i - startrow].size() != numRows) { outRows[i - startrow] = CaretArray(numRows); } indexReverse[ciftiIndexList[i].first] = i; } #pragma omp CARET_PARFOR schedule(dynamic) for (int i = 0; i < numRows; ++i) { float movingRrs; int myrow; const float* movingRow; #pragma omp critical {//CiftiFile may explode if we request multiple rows concurrently (needs mutexes), but we should force sequential requests anyway myrow = curRow;//so, manually force it to read sequentially ++curRow; movingRow = getRow(myrow, movingRrs); } for (int j = startrow; j < endrow; ++j) { if (indexReverse[myrow] != -1)//check if we are on a row that is in the output memory range { if (indexReverse[myrow] <= j)//if so, only compute one of the elements, then store it both places { float cacheRrs; const float* cacheRow = getRow(ciftiIndexList[j].first, cacheRrs, true); outRows[j - startrow][myrow] = correlate(movingRow, movingRrs, cacheRow, cacheRrs, fisherZ); outRows[indexReverse[myrow] - startrow][ciftiIndexList[j].first] = outRows[j - startrow][myrow]; } } else { float cacheRrs; const float* cacheRow = getRow(ciftiIndexList[j].first, cacheRrs, true); outRows[j - startrow][myrow] = correlate(movingRow, movingRrs, cacheRow, cacheRrs, fisherZ); } } } for (int i = startrow; i < endrow; ++i) { myCiftiOut->setRow(outRows[i - startrow], ciftiIndexList[i].second); indexReverse[ciftiIndexList[i].first] = -1; } if (!cacheFullInput) { clearCache();//tell the cache we are going to preload a different set of rows now } } if (cacheFullInput) { clearCache();//don't currently need to do this, its just for completeness } } AlgorithmCiftiCorrelation::AlgorithmCiftiCorrelation(ProgressObject* myProgObj, const CiftiFile* myCifti, CiftiFile* myCiftiOut, const CiftiFile* ciftiRoi, const vector* weights, const bool& fisherZ, const float& memLimitGB, const bool& noDemean, const bool& covariance): AbstractAlgorithm(NULL)//HACK: get around the sentinel by passing a null, because this implementation calls another { const CiftiXML& roiXML = ciftiRoi->getCiftiXML();//roi is not optional in this variant if (roiXML.getMappingType(CiftiXML::ALONG_COLUMN) != CiftiMappingType::BRAIN_MODELS) throw AlgorithmException("cifti roi does not have brain models mapping along column"); const CiftiBrainModelsMap myDenseMap = roiXML.getBrainModelsMap(CiftiXML::ALONG_COLUMN); MetricFile leftRoi, rightRoi, cerebRoi; MetricFile* leftRoiPtr = NULL, *rightRoiPtr = NULL, *cerebRoiPtr = NULL; VolumeFile volRoi; VolumeFile* volRoiPtr = NULL; vector surfStructs = myDenseMap.getSurfaceStructureList(); for (int i = 0; i < (int)surfStructs.size(); ++i) { MetricFile* thisRoi = NULL; switch (surfStructs[i]) { case StructureEnum::CORTEX_LEFT: thisRoi = &leftRoi; leftRoiPtr = thisRoi; break; case StructureEnum::CORTEX_RIGHT: thisRoi = &rightRoi; rightRoiPtr = thisRoi; break; case StructureEnum::CEREBELLUM: thisRoi = &cerebRoi; cerebRoiPtr = thisRoi; break; default: throw AlgorithmException("structure not supported for surface type: " + StructureEnum::toName(surfStructs[i])); } AlgorithmCiftiSeparate(NULL, ciftiRoi, CiftiXML::ALONG_COLUMN, surfStructs[i], thisRoi); } if (myDenseMap.hasVolumeData()) { int64_t offsetOut[3]; AlgorithmCiftiSeparate(NULL, ciftiRoi, CiftiXML::ALONG_COLUMN, &volRoi, offsetOut, NULL, false);//don't crop, because it needs to match the original volume space in the input volRoiPtr = &volRoi; } AlgorithmCiftiCorrelation(myProgObj, myCifti, myCiftiOut, leftRoiPtr, rightRoiPtr, cerebRoiPtr, volRoiPtr, weights, fisherZ, memLimitGB, noDemean, covariance);//HACK: pass through our progress object } float AlgorithmCiftiCorrelation::correlate(const float* row1, const float& rrs1, const float* row2, const float& rrs2, const bool& fisherZ) { double r; if (row1 == row2 && !m_covariance) { r = 1.0;//short circuit for same row } else { 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 if (m_covariance) { if (m_binaryWeights) { r = accum / numWeights; } else { r = accum / rrs1;//NOTE: will equal rrs2 as it only depends on weights, and is not square root } } else { r = accum / (rrs1 * rrs2);//as do these } } else { double accum = sddot(row1, row2, m_numCols);//these have already had the row means subtracted out if (m_covariance) { r = accum / m_numCols; } else { r = accum / (rrs1 * rrs2); } } } if (!m_covariance) { if (fisherZ) { if (r > 0.999999) r = 0.999999;//prevent inf if (r < -0.999999) r = -0.999999;//prevent -inf r = 0.5 * log((1 + r) / (1 - r)); } else { if (r > 1.0) r = 1.0;//don't output anything silly if (r < -1.0) r = -1.0; } } return r; } void AlgorithmCiftiCorrelation::init(const CiftiFile* input, const vector* weights, const bool& noDemean, const bool& covariance) { m_noDemean = noDemean; m_covariance = covariance; m_inputCifti = input; m_rowInfo.resize(m_inputCifti->getNumberOfRows()); m_cacheUsed = 0; m_numCols = m_inputCifti->getNumberOfColumns(); if (weights != NULL) { m_weightedMode = true; int numWeights = (int)weights->size(); if (numWeights != m_numCols) { throw AlgorithmException("number of weights doesn't match length of a row, number of weights given: " + AString::number(weights->size())); } m_binaryWeights = true; for (int i = 0; i < numWeights; ++i) { float val = (*weights)[i]; if (val != 0.0f) { if (val < 0.0f) { throw AlgorithmException("weights cannot be negative"); } m_weights.push_back(val); m_weightIndexes.push_back(i); if (val != 1.0f) { m_binaryWeights = false; } } } if (m_binaryWeights && m_weights.size() == weights->size()) { m_weightedMode = false;//all weights were 1, so switch back to normal mode } } else { m_weightedMode = false; } } void AlgorithmCiftiCorrelation::cacheRow(const int& ciftiIndex) { CaretAssertVectorIndex(m_rowInfo, ciftiIndex); if (m_rowInfo[ciftiIndex].m_cacheIndex != -1) return;//shouldn't happen, but hey if (m_cacheUsed >= (int)m_rowCache.size()) { m_rowCache.push_back(CacheRow()); m_rowCache[m_cacheUsed].m_row.resize(m_numCols); } m_rowCache[m_cacheUsed].m_ciftiIndex = ciftiIndex; float* myPtr = m_rowCache[m_cacheUsed].m_row.data(); m_inputCifti->getRow(myPtr, ciftiIndex); if (!m_rowInfo[ciftiIndex].m_haveCalculated) { computeRowStats(myPtr, m_rowInfo[ciftiIndex].m_mean, m_rowInfo[ciftiIndex].m_rootResidSqr); m_rowInfo[ciftiIndex].m_haveCalculated = true; } doSubtract(myPtr, m_rowInfo[ciftiIndex].m_mean); m_rowInfo[ciftiIndex].m_cacheIndex = m_cacheUsed; ++m_cacheUsed; } void AlgorithmCiftiCorrelation::clearCache() { for (int i = 0; i < m_cacheUsed; ++i) { m_rowInfo[m_rowCache[i].m_ciftiIndex].m_cacheIndex = -1; } m_cacheUsed = 0; } const float* AlgorithmCiftiCorrelation::getRow(const int& ciftiIndex, float& rootResidSqr, const bool& mustBeCached) { float* ret; CaretAssertVectorIndex(m_rowInfo, ciftiIndex); if (m_rowInfo[ciftiIndex].m_cacheIndex != -1) { ret = m_rowCache[m_rowInfo[ciftiIndex].m_cacheIndex].m_row.data(); } else { CaretAssert(!mustBeCached); if (mustBeCached)//largely so it doesn't give warning about unused when compiled in release { throw AlgorithmException("something very bad happened, notify the developers"); } ret = getTempRow(); m_inputCifti->getRow(ret, ciftiIndex); if (!m_rowInfo[ciftiIndex].m_haveCalculated) { computeRowStats(ret, m_rowInfo[ciftiIndex].m_mean, m_rowInfo[ciftiIndex].m_rootResidSqr); m_rowInfo[ciftiIndex].m_haveCalculated = true; } doSubtract(ret, m_rowInfo[ciftiIndex].m_mean); } rootResidSqr = m_rowInfo[ciftiIndex].m_rootResidSqr; return ret; } void AlgorithmCiftiCorrelation::computeRowStats(const float* row, float& mean, float& rootResidSqr) { double accum = 0.0;//double, for numerical stability if (m_noDemean) { mean = 0.0f; } else { if (m_weightedMode) { int weightsize = (int)m_weightIndexes.size(); if (m_binaryWeights)//because should be a little faster without multiplies or a second sum { for (int i = 0; i < weightsize; ++i) { accum += row[m_weightIndexes[i]]; } mean = accum / weightsize; } else { double accum2 = 0.0; for (int i = 0; i < weightsize; ++i) { float weight = m_weights[i]; accum += row[m_weightIndexes[i]] * weight; accum2 += weight; } mean = accum / accum2; } } else { for (int i = 0; i < m_numCols; ++i)//two pass, for numerical stability { accum += row[i]; } mean = accum / m_numCols; } } accum = 0.0; if (m_covariance) { int weightsize = (int)m_weightIndexes.size(); rootResidSqr = 0.0f; if (m_weightedMode && !m_binaryWeights) { for (int i = 0; i < weightsize; ++i) { accum += m_weights[i]; } rootResidSqr = accum;//repurpose this variable to store the weight sum - NOTE: don't take sqrt in case negative sum (whatever that means), so must not divide by both in correlate() in covariance mode } } else { if (m_weightedMode) { int weightsize = (int)m_weightIndexes.size(); if (m_binaryWeights) { for (int i = 0; i < weightsize; ++i) { float tempf = row[m_weightIndexes[i]] - mean; accum += tempf * tempf; } rootResidSqr = sqrt(accum); } else { for (int i = 0; i < weightsize; ++i) { float tempf = row[m_weightIndexes[i]] - mean; accum += tempf * tempf * m_weights[i]; } rootResidSqr = sqrt(accum); } } else { for (int i = 0; i < m_numCols; ++i) { float tempf = row[i] - mean; accum += tempf * tempf; } rootResidSqr = sqrt(accum); } } } void AlgorithmCiftiCorrelation::doSubtract(float* row, const float& mean) { if (m_noDemean) return;//skip subtracting zero from everything if (m_weightedMode) { int weightsize = (int)m_weightIndexes.size(); if (m_binaryWeights) { for (int i = 0; i < weightsize; ++i) { row[i] = row[m_weightIndexes[i]] - mean; } } else { for (int i = 0; i < weightsize; ++i) { row[i] = sqrt(m_weights[i]) * (row[m_weightIndexes[i]] - mean);//multiply by square root of weight, so that the numerator of correlation doesn't get the square of the weight } } } else { for (int i = 0; i < m_numCols; ++i) { row[i] -= mean; } } } float* AlgorithmCiftiCorrelation::getTempRow() { #ifdef CARET_OMP int oldsize = (int)m_tempRows.size(); int threadNum = omp_get_thread_num(); if (threadNum >= oldsize) { m_tempRows.resize(threadNum + 1); for (int i = oldsize; i <= threadNum; ++i) { m_tempRows[i] = CaretArray(m_numCols); } } return m_tempRows[threadNum].getArray(); #else if (m_tempRows.size() == 0) { m_tempRows.resize(1); m_tempRows[0] = CaretArray(m_numCols); } return m_tempRows[0].getArray(); #endif } int AlgorithmCiftiCorrelation::numRowsForMem(const float& memLimitGB, bool& cacheFullInput) { int numRows = m_inputCifti->getNumberOfRows(); int inrowBytes = m_numCols * sizeof(float), outrowBytes = numRows * sizeof(float); int64_t targetBytes = (int64_t)(memLimitGB * 1024 * 1024 * 1024); if (m_inputCifti->isInMemory()) targetBytes -= numRows * m_numCols * 4;//count in-memory input against the total too #ifdef CARET_OMP targetBytes -= inrowBytes * omp_get_max_threads(); #else targetBytes -= inrowBytes;//1 row in memory that isn't a reference to cache #endif targetBytes -= numRows * sizeof(RowInfo);//storage for mean, stdev, and info about caching int64_t perRowBytes = inrowBytes + outrowBytes;//cache and memory collation for output rows if (numRows * m_numCols * 4 < targetBytes * 0.7f)//if caching the entire input file would take less than 70% of remaining allotted memory, do it to reduce IO { cacheFullInput = true;//precache the entire input file, rather than caching it synchronously with the in-memory output rows targetBytes -= numRows * m_numCols * 4;//reduce the remaining total by the memory used perRowBytes = outrowBytes;//don't need to count input rows against the remaining memory total } else { cacheFullInput = false; } if (perRowBytes == 0) return 1;//protect against integer div by zero int ret = targetBytes / perRowBytes;//integer divide rounds down if (ret < 1) return 1;//always return at least one return ret; } float AlgorithmCiftiCorrelation::getAlgorithmInternalWeight() { return 1.0f;//override this if needed, if the progress bar isn't smooth } float AlgorithmCiftiCorrelation::getSubAlgorithmWeight() { return 0.0f; } connectome-workbench-1.2.3+git41-gc4c6c90/src/Algorithms/AlgorithmCiftiCorrelation.h000066400000000000000000000104751300200146000301440ustar00rootroot00000000000000#ifndef __ALGORITHM_CIFTI_CORRELATION_H__ #define __ALGORITHM_CIFTI_CORRELATION_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 "AbstractAlgorithm.h" #include "CaretPointer.h" namespace caret { class AlgorithmCiftiCorrelation : public AbstractAlgorithm { AlgorithmCiftiCorrelation(); struct CacheRow { int m_ciftiIndex; std::vector m_row; }; struct RowInfo { bool m_haveCalculated; float m_mean, m_rootResidSqr; int m_cacheIndex; RowInfo() { m_haveCalculated = false; m_cacheIndex = -1; } }; std::vector m_rowCache; std::vector m_rowInfo; std::vector > m_tempRows;//reuse return values in getRow instead of reallocating std::vector m_weights; std::vector m_weightIndexes; bool m_binaryWeights, m_weightedMode, m_noDemean, m_covariance; int m_cacheUsed;//reuse cache entries instead of reallocating them int m_numCols; const CiftiFile* m_inputCifti;//so that accesses work through the cache functions void cacheRow(const int& ciftiIndex); void computeRowStats(const float* row, float& mean, float& rootResidSqr); void doSubtract(float* row, const float& mean); void clearCache(); const float* getRow(const int& ciftiIndex, float& rootResidSqr, const bool& mustBeCached = false); float* getTempRow(); float correlate(const float* row1, const float& rrs1, const float* row2, const float& rrs2, const bool& fisherZ); void init(const CiftiFile* input, const std::vector* weights, const bool& noDemean, const bool& covariance); int numRowsForMem(const float& memLimitGB, bool& cacheFullInput); protected: static float getSubAlgorithmWeight(); static float getAlgorithmInternalWeight(); public: AlgorithmCiftiCorrelation(ProgressObject* myProgObj, const CiftiFile* myCifti, CiftiFile* myCiftiOut, const std::vector* weights = NULL, const bool& fisherZ = false, const float& memLimitGB = -1.0f, const bool& noDemean = false, const bool& covariance = false); AlgorithmCiftiCorrelation(ProgressObject* myProgObj, const CiftiFile* myCifti, CiftiFile* myCiftiOut, const MetricFile* leftRoi, const MetricFile* rightRoi = NULL, const MetricFile* cerebRoi = NULL, const VolumeFile* volRoi = NULL, const std::vector* weights = NULL, const bool& fisherZ = false, const float& memLimitGB = -1.0f, const bool& noDemean = false, const bool& covariance = false); AlgorithmCiftiCorrelation(ProgressObject* myProgObj, const CiftiFile* myCifti, CiftiFile* myCiftiOut, const CiftiFile* ciftiRoi, const std::vector* weights = NULL, const bool& fisherZ = false, const float& memLimitGB = -1.0f, const bool& noDemean = false, const bool& covariance = false); static OperationParameters* getParameters(); static void useParameters(OperationParameters* myParams, ProgressObject* myProgObj); static AString getCommandSwitch(); static AString getShortDescription(); }; typedef TemplateAutoOperation AutoAlgorithmCiftiCorrelation; } #endif //__ALGORITHM_CIFTI_CORRELATION_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Algorithms/AlgorithmCiftiCorrelationGradient.cxx000066400000000000000000001406141300200146000321740ustar00rootroot00000000000000/*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 "AlgorithmCiftiCorrelationGradient.h" #include "AlgorithmException.h" #include "AlgorithmMetricGradient.h" #include "MetricSmoothingObject.h" #include "AlgorithmVolumeGradient.h" #include "CaretLogger.h" #include "CaretOMP.h" #include "CiftiFile.h" #include "GeodesicHelper.h" #include "MetricFile.h" #include "SurfaceFile.h" #include "Vector3D.h" #include "VolumeFile.h" #include "dot_wrapper.h" #include using namespace caret; using namespace std; AString AlgorithmCiftiCorrelationGradient::getCommandSwitch() { return "-cifti-correlation-gradient"; } AString AlgorithmCiftiCorrelationGradient::getShortDescription() { return "CORRELATE CIFTI ROWS AND TAKE GRADIENT"; } OperationParameters* AlgorithmCiftiCorrelationGradient::getParameters() { OperationParameters* ret = new OperationParameters(); ret->addCiftiParameter(1, "cifti", "the input cifti"); ret->addCiftiOutputParameter(2, "cifti-out", "the output cifti"); OptionalParameter* leftSurfOpt = ret->createOptionalParameter(3, "-left-surface", "specify the left surface to use"); leftSurfOpt->addSurfaceParameter(1, "surface", "the left surface file"); OptionalParameter* leftCorrAreasOpt = leftSurfOpt->createOptionalParameter(2, "-left-corrected-areas", "vertex areas to use instead of computing them from the left surface"); leftCorrAreasOpt->addMetricParameter(1, "area-metric", "the corrected vertex areas, as a metric"); OptionalParameter* rightSurfOpt = ret->createOptionalParameter(4, "-right-surface", "specify the right surface to use"); rightSurfOpt->addSurfaceParameter(1, "surface", "the right surface file"); OptionalParameter* rightCorrAreasOpt = rightSurfOpt->createOptionalParameter(2, "-right-corrected-areas", "vertex areas to use instead of computing them from the right surface"); rightCorrAreasOpt->addMetricParameter(1, "area-metric", "the corrected vertex areas, as a metric"); OptionalParameter* cerebSurfaceOpt = ret->createOptionalParameter(5, "-cerebellum-surface", "specify the cerebellum surface to use"); cerebSurfaceOpt->addSurfaceParameter(1, "surface", "the cerebellum surface file"); OptionalParameter* cerebCorrAreasOpt = cerebSurfaceOpt->createOptionalParameter(2, "-cerebellum-corrected-areas", "vertex areas to use instead of computing them from the cerebellum surface"); cerebCorrAreasOpt->addMetricParameter(1, "area-metric", "the corrected vertex areas, as a metric"); OptionalParameter* presmoothSurfOpt = ret->createOptionalParameter(6, "-surface-presmooth", "smooth on the surface before computing the gradient"); presmoothSurfOpt->addDoubleParameter(1, "surface-kernel", "the sigma for the gaussian surface smoothing kernel, in mm"); OptionalParameter* presmoothVolOpt = ret->createOptionalParameter(7, "-volume-presmooth", "smooth the volume before computing the gradient"); presmoothVolOpt->addDoubleParameter(1, "volume-kernel", "the sigma for the gaussian volume smoothing kernel, in mm"); ret->createOptionalParameter(8, "-undo-fisher-z", "apply the inverse fisher small z transform to the input"); ret->createOptionalParameter(12, "-fisher-z", "apply the fisher small z transform to the correlations before taking the gradient"); OptionalParameter* surfaceExcludeOpt = ret->createOptionalParameter(9, "-surface-exclude", "exclude vertices near each seed vertex from computation"); surfaceExcludeOpt->addDoubleParameter(1, "distance", "geodesic distance from seed vertex for the exclusion zone, in mm"); OptionalParameter* volumeExcludeOpt = ret->createOptionalParameter(10, "-volume-exclude", "exclude voxels near each seed voxel from computation"); volumeExcludeOpt->addDoubleParameter(1, "distance", "distance from seed voxel for the exclusion zone, in mm"); ret->createOptionalParameter(13, "-covariance", "compute covariance instead of correlation"); OptionalParameter* memLimitOpt = ret->createOptionalParameter(11, "-mem-limit", "restrict memory usage"); memLimitOpt->addDoubleParameter(1, "limit-GB", "memory limit in gigabytes"); ret->setHelpText( AString("For each structure, compute the correlation of the rows in the structure, and take the gradients of ") + "the resulting rows, then average them. " + "Memory limit does not need to be an integer, you may also specify 0 to use as little memory as possible (this may be very slow)." ); return ret; } void AlgorithmCiftiCorrelationGradient::useParameters(OperationParameters* myParams, ProgressObject* myProgObj) { CiftiFile* myCifti = myParams->getCifti(1); CiftiFile* myCiftiOut = myParams->getOutputCifti(2); SurfaceFile* myLeftSurf = NULL, *myRightSurf = NULL, *myCerebSurf = NULL; MetricFile* myLeftAreas = NULL, *myRightAreas = NULL, *myCerebAreas = NULL; OptionalParameter* leftSurfOpt = myParams->getOptionalParameter(3); if (leftSurfOpt->m_present) { myLeftSurf = leftSurfOpt->getSurface(1); OptionalParameter* leftCorrAreasOpt = leftSurfOpt->getOptionalParameter(2); if (leftCorrAreasOpt->m_present) { myLeftAreas = leftCorrAreasOpt->getMetric(1); } } OptionalParameter* rightSurfOpt = myParams->getOptionalParameter(4); if (rightSurfOpt->m_present) { myRightSurf = rightSurfOpt->getSurface(1); OptionalParameter* rightCorrAreasOpt = rightSurfOpt->getOptionalParameter(2); if (rightCorrAreasOpt->m_present) { myRightAreas = rightCorrAreasOpt->getMetric(1); } } OptionalParameter* cerebSurfOpt = myParams->getOptionalParameter(5); if (cerebSurfOpt->m_present) { myCerebSurf = cerebSurfOpt->getSurface(1); OptionalParameter* cerebCorrAreasOpt = cerebSurfOpt->getOptionalParameter(2); if (cerebCorrAreasOpt->m_present) { myCerebAreas = cerebCorrAreasOpt->getMetric(1); } } float surfKern = -1.0f; OptionalParameter* presmoothSurfOpt = myParams->getOptionalParameter(6); if (presmoothSurfOpt->m_present) { surfKern = (float)presmoothSurfOpt->getDouble(1); } float volKern = -1.0f; OptionalParameter* presmoothVolOpt = myParams->getOptionalParameter(7); if (presmoothVolOpt->m_present) { volKern = (float)presmoothVolOpt->getDouble(1); } bool undoFisherInput = myParams->getOptionalParameter(8)->m_present; bool applyFisher = myParams->getOptionalParameter(12)->m_present; float surfaceExclude = -1.0f; OptionalParameter* surfaceExcludeOpt = myParams->getOptionalParameter(9); if (surfaceExcludeOpt->m_present) { surfaceExclude = (float)surfaceExcludeOpt->getDouble(1); if (surfaceExclude < 0.0f) { throw AlgorithmException("surface exclude distance cannot be negative"); } } float volumeExclude = -1.0f; OptionalParameter* volumeExcludeOpt = myParams->getOptionalParameter(10); if (volumeExcludeOpt->m_present) { volumeExclude = (float)volumeExcludeOpt->getDouble(1); if (volumeExclude < 0.0f) { throw AlgorithmException("volume exclude distance cannot be negative"); } } float memLimitGB = -1.0f; OptionalParameter* memLimitOpt = myParams->getOptionalParameter(11); if (memLimitOpt->m_present) { memLimitGB = (float)memLimitOpt->getDouble(1); if (memLimitGB < 0.0f) { throw AlgorithmException("memory limit cannot be negative"); } } bool covariance = myParams->getOptionalParameter(13)->m_present; AlgorithmCiftiCorrelationGradient(myProgObj, myCifti, myCiftiOut, myLeftSurf, myRightSurf, myCerebSurf, myLeftAreas, myRightAreas, myCerebAreas, surfKern, volKern, undoFisherInput, applyFisher, surfaceExclude, volumeExclude, covariance, memLimitGB); } AlgorithmCiftiCorrelationGradient::AlgorithmCiftiCorrelationGradient(ProgressObject* myProgObj, const CiftiFile* myCifti, CiftiFile* myCiftiOut, SurfaceFile* myLeftSurf, SurfaceFile* myRightSurf, SurfaceFile* myCerebSurf, const MetricFile* myLeftAreas, const MetricFile* myRightAreas, const MetricFile* myCerebAreas, const float& surfKern, const float& volKern, const bool& undoFisherInput, const bool& applyFisher, const float& surfaceExclude, const float& volumeExclude, const bool& covariance, const float& memLimitGB) : AbstractAlgorithm(myProgObj) { LevelProgress myProgress(myProgObj); init(myCifti, undoFisherInput, applyFisher, covariance); const CiftiXMLOld& myXML = myCifti->getCiftiXMLOld(); CiftiXMLOld myNewXML = myXML; myNewXML.resetDirectionToScalars(CiftiXMLOld::ALONG_ROW, 1); myNewXML.setMapNameForIndex(CiftiXMLOld::ALONG_ROW, 0, "gradient"); myCiftiOut->setCiftiXML(myNewXML); vector surfaceList, volumeList; myXML.getStructureListsForColumns(surfaceList, volumeList); for (int whichStruct = 0; whichStruct < (int)surfaceList.size(); ++whichStruct) {//sanity check surfaces SurfaceFile* mySurf = NULL; const MetricFile* myAreas = NULL; AString surfType; switch (surfaceList[whichStruct]) { case StructureEnum::CORTEX_LEFT: mySurf = myLeftSurf; myAreas = myLeftAreas; surfType = "left"; break; case StructureEnum::CORTEX_RIGHT: mySurf = myRightSurf; myAreas = myRightAreas; surfType = "right"; break; case StructureEnum::CEREBELLUM: mySurf = myCerebSurf; myAreas = myCerebAreas; surfType = "cerebellum"; break; default: throw AlgorithmException("found surface model with incorrect type: " + StructureEnum::toName(surfaceList[whichStruct])); break; } if (mySurf == NULL) { throw AlgorithmException(surfType + " surface required but not provided"); } if (mySurf->getNumberOfNodes() != myCifti->getCiftiXML().getBrainModelsMap(CiftiXML::ALONG_COLUMN).getSurfaceNumberOfNodes(surfaceList[whichStruct])) { throw AlgorithmException(surfType + " surface has the wrong number of vertices"); } if (myAreas != NULL && myAreas->getNumberOfNodes() != mySurf->getNumberOfNodes()) { throw AlgorithmException(surfType + " corrected vertex areas metric has the wrong number of vertices"); } } for (int whichStruct = 0; whichStruct < (int)surfaceList.size(); ++whichStruct) { SurfaceFile* mySurf = NULL; const MetricFile* myAreas = NULL; switch (surfaceList[whichStruct]) { case StructureEnum::CORTEX_LEFT: mySurf = myLeftSurf; myAreas = myLeftAreas; break; case StructureEnum::CORTEX_RIGHT: mySurf = myRightSurf; myAreas = myRightAreas; break; case StructureEnum::CEREBELLUM: mySurf = myCerebSurf; myAreas = myCerebAreas; break; default: break; } if (surfaceExclude > 0.0f) { processSurfaceComponent(surfaceList[whichStruct], surfKern, surfaceExclude, memLimitGB, mySurf, myAreas); } else { processSurfaceComponent(surfaceList[whichStruct], surfKern, memLimitGB, mySurf, myAreas); } } for (int whichStruct = 0; whichStruct < (int)volumeList.size(); ++whichStruct) { if (volumeExclude > 0.0f) { processVolumeComponent(volumeList[whichStruct], volKern, volumeExclude, memLimitGB); } else { processVolumeComponent(volumeList[whichStruct], volKern, memLimitGB); } } myCiftiOut->setColumn(m_outColumn.data(), 0); } void AlgorithmCiftiCorrelationGradient::processSurfaceComponent(StructureEnum::Enum& myStructure, const float& surfKern, const float& memLimitGB, SurfaceFile* mySurf, const MetricFile* myAreas) { const CiftiXMLOld& myXML = m_inputCifti->getCiftiXMLOld(); vector myMap; myXML.getSurfaceMapForColumns(myMap, myStructure); int mapSize = (int)myMap.size(); vector accum(mapSize, 0.0); int numCacheRows = mapSize; bool cacheFullInput = true; if (memLimitGB >= 0.0f) { numCacheRows = numRowsForMem(memLimitGB, m_numCols * sizeof(float), (mySurf->getNumberOfNodes() * (sizeof(float) * 8 + 1)) / 8, mapSize, cacheFullInput); } if (numCacheRows > mapSize) { cacheFullInput = true; numCacheRows = mapSize; } if (cacheFullInput) { if (numCacheRows != mapSize) CaretLogInfo("computing " + AString::number(numCacheRows) + " rows at a time"); } else { CaretLogInfo("computing " + AString::number(numCacheRows) + " rows at a time, reading rows as needed during processing"); } const float* areaData = NULL; if (myAreas != NULL) { areaData = myAreas->getValuePointerForColumn(0); } MetricFile myRoi; myRoi.setNumberOfNodesAndColumns(mySurf->getNumberOfNodes(), 1); myRoi.initializeColumn(0); vector rowsToCache; for (int i = 0; i < mapSize; ++i) { myRoi.setValue(myMap[i].m_surfaceNode, 0, 1.0f); if (cacheFullInput) { rowsToCache.push_back(myMap[i].m_ciftiIndex); } } if (cacheFullInput) { cacheRows(rowsToCache); } CaretPointer mySmooth; if (surfKern > 0.0f) { mySmooth.grabNew(new MetricSmoothingObject(mySurf, surfKern, &myRoi, MetricSmoothingObject::GEO_GAUSS_AREA, areaData));//computes the smoothing weights only once per surface } for (int startpos = 0; startpos < mapSize; startpos += numCacheRows) { int endpos = startpos + numCacheRows; if (endpos > mapSize) endpos = mapSize; if (!cacheFullInput) { rowsToCache.clear(); for (int i = startpos; i < endpos; ++i) { rowsToCache.push_back(myMap[i].m_ciftiIndex); } cacheRows(rowsToCache); } int curRow = 0;//because we can't trust the order threads hit the critical section MetricFile computeMetric; computeMetric.setNumberOfNodesAndColumns(mySurf->getNumberOfNodes(), endpos - startpos); #pragma omp CARET_PARFOR schedule(dynamic) for (int i = 0; i < mapSize; ++i) { float movingRrs; const float* movingRow; int myrow; #pragma omp critical {//CiftiFile may explode if we request multiple rows concurrently (needs mutexes), but we should force sequential requests anyway myrow = curRow;//so, manually force it to read sequentially ++curRow; movingRow = getRow(myMap[myrow].m_ciftiIndex, movingRrs); } for (int j = startpos; j < endpos; ++j) { if (myrow >= startpos && myrow < endpos) { if (j >= myrow) { float cacheRrs; const float* cacheRow = getRow(myMap[j].m_ciftiIndex, cacheRrs, true); float result = correlate(movingRow, movingRrs, cacheRow, cacheRrs); computeMetric.setValue(myMap[myrow].m_surfaceNode, j - startpos, result); computeMetric.setValue(myMap[j].m_surfaceNode, myrow - startpos, result); } } else { float cacheRrs; const float* cacheRow = getRow(myMap[j].m_ciftiIndex, cacheRrs, true); float result = correlate(movingRow, movingRrs, cacheRow, cacheRrs); computeMetric.setValue(myMap[myrow].m_surfaceNode, j - startpos, result); } } } int numMetricCols = endpos - startpos; MetricFile outputMetric, outputMetric2; for (int j = 0; j < numMetricCols; ++j) { const float* myCol; if (surfKern > 0.0f) { mySmooth->smoothColumn(&computeMetric, j, &outputMetric); AlgorithmMetricGradient(NULL, mySurf, &outputMetric, &outputMetric2, NULL, -1.0f, &myRoi, false, -1, myAreas); myCol = outputMetric2.getValuePointerForColumn(0); } else { AlgorithmMetricGradient(NULL, mySurf, &computeMetric, &outputMetric, NULL, -1.0f, &myRoi, false, j, myAreas); myCol = outputMetric.getValuePointerForColumn(0); } for (int i = 0; i < mapSize; ++i) { const float* roiColumn = myRoi.getValuePointerForColumn(0); if (roiColumn[myMap[i].m_surfaceNode] > 0.0f) { accum[i] += myCol[myMap[i].m_surfaceNode]; } } } } for (int i = 0; i < mapSize; ++i) { m_outColumn[myMap[i].m_ciftiIndex] = accum[i] / mapSize; } } void AlgorithmCiftiCorrelationGradient::processSurfaceComponent(StructureEnum::Enum& myStructure, const float& surfKern, const float& surfExclude, const float& memLimitGB, SurfaceFile* mySurf, const MetricFile* myAreas) { const CiftiXMLOld& myXML = m_inputCifti->getCiftiXMLOld(); vector myMap; myXML.getSurfaceMapForColumns(myMap, myStructure); int mapSize = (int)myMap.size(); vector accum(mapSize, 0.0); vector accumCount(mapSize, 0); int numCacheRows = mapSize; bool cacheFullInput = true; if (memLimitGB >= 0.0f) { numCacheRows = numRowsForMem(memLimitGB, m_numCols * sizeof(float), (mySurf->getNumberOfNodes() * (sizeof(float) * 8 + 1)) / 8, mapSize, cacheFullInput); } if (numCacheRows > mapSize) { cacheFullInput = true; numCacheRows = mapSize; } if (cacheFullInput) { if (numCacheRows != mapSize) CaretLogInfo("computing " + AString::number(numCacheRows) + " rows at a time"); } else { CaretLogInfo("computing " + AString::number(numCacheRows) + " rows at a time, reading rows as needed during processing"); } const float* areaData = NULL; if (myAreas != NULL) { areaData = myAreas->getValuePointerForColumn(0); } CaretPointer myGeoBase(new GeodesicHelperBase(mySurf, areaData));//can't really have SurfaceFile cache ones with corrected areas MetricFile myRoi; myRoi.setNumberOfNodesAndColumns(mySurf->getNumberOfNodes(), 1); myRoi.initializeColumn(0); vector > roiLookup(numCacheRows);//this gets bit compressed vector origRoi(mySurf->getNumberOfNodes()); vector > excludeNodes(numCacheRows); vector rowsToCache; for (int i = 0; i < mapSize; ++i) { myRoi.setValue(myMap[i].m_surfaceNode, 0, 1.0f); if (cacheFullInput) { rowsToCache.push_back(myMap[i].m_ciftiIndex); } } if (cacheFullInput) { cacheRows(rowsToCache); } CaretPointer mySmooth; if (surfKern > 0.0f) { mySmooth.grabNew(new MetricSmoothingObject(mySurf, surfKern, &myRoi, MetricSmoothingObject::GEO_GAUSS_AREA, areaData));//computes the smoothing weights only once per surface } for (int startpos = 0; startpos < mapSize; startpos += numCacheRows) { int endpos = startpos + numCacheRows; if (endpos > mapSize) endpos = mapSize; if (!cacheFullInput) { rowsToCache.clear(); for (int i = startpos; i < endpos; ++i) { rowsToCache.push_back(myMap[i].m_ciftiIndex); } cacheRows(rowsToCache); } int numSurfNodes = mySurf->getNumberOfNodes(); #pragma omp CARET_PAR { vector distances; CaretPointer myGeoHelp(new GeodesicHelper(myGeoBase)); #pragma omp CARET_FOR for (int i = startpos; i < endpos; ++i) { vector& excludeRef = excludeNodes[i - startpos]; myGeoHelp->getNodesToGeoDist(myMap[i].m_surfaceNode, surfExclude, excludeRef, distances); vector& lookupRef = roiLookup[i - startpos]; lookupRef.resize(numSurfNodes); for (int j = 0; j < numSurfNodes; ++j) { lookupRef[j] = (myRoi.getValue(j, 0) > 0.0f); } int numExclude = excludeRef.size(); for (int j = 0; j < numExclude; ++j) { lookupRef[excludeRef[j]] = false; } } } int curRow = 0;//because we can't trust the order threads hit the critical section MetricFile computeMetric; computeMetric.setNumberOfNodesAndColumns(mySurf->getNumberOfNodes(), endpos - startpos); #pragma omp CARET_PARFOR schedule(dynamic) for (int i = 0; i < mapSize; ++i) { float movingRrs; const float* movingRow; int myrow; #pragma omp critical {//CiftiFile may explode if we request multiple rows concurrently (needs mutexes), but we should force sequential requests anyway myrow = curRow;//so, manually force it to read sequentially ++curRow; movingRow = getRow(myMap[myrow].m_ciftiIndex, movingRrs); } for (int j = startpos; j < endpos; ++j) { if (roiLookup[j - startpos][myMap[myrow].m_surfaceNode]) { if (myrow >= startpos && myrow < endpos) { if (j >= myrow) { float cacheRrs; const float* cacheRow = getRow(myMap[j].m_ciftiIndex, cacheRrs, true); float result = correlate(movingRow, movingRrs, cacheRow, cacheRrs); computeMetric.setValue(myMap[myrow].m_surfaceNode, j - startpos, result); computeMetric.setValue(myMap[j].m_surfaceNode, myrow - startpos, result); } } else { float cacheRrs; const float* cacheRow = getRow(myMap[j].m_ciftiIndex, cacheRrs, true); float result = correlate(movingRow, movingRrs, cacheRow, cacheRrs); computeMetric.setValue(myMap[myrow].m_surfaceNode, j - startpos, result); } } } } int numMetricCols = endpos - startpos; MetricFile outputMetric, outputMetric2; MetricFile excludeRoi = myRoi; for (int j = 0; j < numMetricCols; ++j) { int numExclude = (int)excludeNodes[j].size(); const float* myCol; for (int k = 0; k < numExclude; ++k) { excludeRoi.setValue(excludeNodes[j][k], 0, 0.0f);//exclude the nodes near the seed node } if (surfKern > 0.0f) { mySmooth->smoothColumn(&computeMetric, j, &outputMetric, &excludeRoi); AlgorithmMetricGradient(NULL, mySurf, &outputMetric, &outputMetric2, NULL, -1.0f, &excludeRoi, false, -1, myAreas); myCol = outputMetric2.getValuePointerForColumn(0); } else { AlgorithmMetricGradient(NULL, mySurf, &computeMetric, &outputMetric, NULL, -1.0f, &excludeRoi, false, j, myAreas); myCol = outputMetric.getValuePointerForColumn(0); } for (int i = 0; i < mapSize; ++i) { const float* roiColumn = excludeRoi.getValuePointerForColumn(0); if (roiColumn[myMap[i].m_surfaceNode] > 0.0f) { accum[i] += myCol[myMap[i].m_surfaceNode]; accumCount[i] += 1;//less dubious looking than ++accumCount[i] } } for (int k = 0; k < numExclude; ++k) { excludeRoi.setValue(excludeNodes[j][k], 0, myRoi.getValue(excludeNodes[j][k], 0));//and set them back to original roi afterwards, instead of a full reinitialize } } } for (int i = 0; i < mapSize; ++i) { if (accumCount[i] != 0) { m_outColumn[myMap[i].m_ciftiIndex] = accum[i] / accumCount[i]; } else { m_outColumn[myMap[i].m_ciftiIndex] = 0.0f; } } } void AlgorithmCiftiCorrelationGradient::processVolumeComponent(StructureEnum::Enum& myStructure, const float& volKern, const float& memLimitGB) { const CiftiXMLOld& myXML = m_inputCifti->getCiftiXMLOld(); vector myMap; myXML.getVolumeStructureMapForColumns(myMap, myStructure); int mapSize = (int)myMap.size(); vector accum(mapSize, 0.0); int numCacheRows = mapSize; bool cacheFullInput = true; vector newdims; int64_t offset[3]; if (mapSize > 0) {//make a voxel bounding box to minimize memory usage int extrema[6] = { myMap[0].m_ijk[0], myMap[0].m_ijk[0], myMap[0].m_ijk[1], myMap[0].m_ijk[1], myMap[0].m_ijk[2], myMap[0].m_ijk[2] }; for (int64_t i = 1; i < mapSize; ++i) { if (myMap[i].m_ijk[0] < extrema[0]) extrema[0] = myMap[i].m_ijk[0]; if (myMap[i].m_ijk[0] > extrema[1]) extrema[1] = myMap[i].m_ijk[0]; if (myMap[i].m_ijk[1] < extrema[2]) extrema[2] = myMap[i].m_ijk[1]; if (myMap[i].m_ijk[1] > extrema[3]) extrema[3] = myMap[i].m_ijk[1]; if (myMap[i].m_ijk[2] < extrema[4]) extrema[4] = myMap[i].m_ijk[2]; if (myMap[i].m_ijk[2] > extrema[5]) extrema[5] = myMap[i].m_ijk[2]; } newdims.push_back(extrema[1] - extrema[0] + 1); newdims.push_back(extrema[3] - extrema[2] + 1); newdims.push_back(extrema[5] - extrema[4] + 1); offset[0] = extrema[0]; offset[1] = extrema[2]; offset[2] = extrema[4]; } else { return; } if (memLimitGB >= 0.0f) { numCacheRows = numRowsForMem(memLimitGB, m_numCols * sizeof(float), newdims[0] * newdims[1] * newdims[2] * sizeof(float), mapSize, cacheFullInput); } if (numCacheRows > mapSize) { cacheFullInput = true; numCacheRows = mapSize; } if (cacheFullInput) { if (numCacheRows != mapSize) CaretLogInfo("computing " + AString::number(numCacheRows) + " rows at a time"); } else { CaretLogInfo("computing " + AString::number(numCacheRows) + " rows at a time, reading rows as needed during processing"); } int64_t ciftiDims[3]; vector > ciftiSform; myXML.getVolumeDimsAndSForm(ciftiDims, ciftiSform); VolumeFile volRoi(newdims, ciftiSform); volRoi.setValueAllVoxels(0.0f); vector rowsToCache; for (int i = 0; i < mapSize; ++i) { volRoi.setValue(1.0f, myMap[i].m_ijk[0] - offset[0], myMap[i].m_ijk[1] - offset[1], myMap[i].m_ijk[2] - offset[2]); if (cacheFullInput) { rowsToCache.push_back(myMap[i].m_ciftiIndex); } } if (cacheFullInput) { cacheRows(rowsToCache); } for (int startpos = 0; startpos < mapSize; startpos += numCacheRows) { int endpos = startpos + numCacheRows; if (endpos > mapSize) endpos = mapSize; if (!cacheFullInput) { rowsToCache.clear(); for (int i = startpos; i < endpos; ++i) { rowsToCache.push_back(myMap[i].m_ciftiIndex); } cacheRows(rowsToCache); } int curRow = 0;//because we can't trust the order threads hit the critical section vector computeDims = newdims; computeDims.push_back(endpos - startpos); VolumeFile computeVol(computeDims, ciftiSform); #pragma omp CARET_PARFOR schedule(dynamic) for (int i = 0; i < mapSize; ++i) { float movingRrs; const float* movingRow; int myrow; #pragma omp critical {//CiftiFile may explode if we request multiple rows concurrently (needs mutexes), but we should force sequential requests anyway myrow = curRow;//so, manually force it to read sequentially ++curRow; movingRow = getRow(myMap[myrow].m_ciftiIndex, movingRrs); } for (int j = startpos; j < endpos; ++j) { if (myrow >= startpos && myrow < endpos) { if (j >= myrow) { float cacheRrs; const float* cacheRow = getRow(myMap[j].m_ciftiIndex, cacheRrs, true); float result = correlate(movingRow, movingRrs, cacheRow, cacheRrs); computeVol.setValue(result, myMap[myrow].m_ijk[0] - offset[0], myMap[myrow].m_ijk[1] - offset[1], myMap[myrow].m_ijk[2] - offset[2], j - startpos); computeVol.setValue(result, myMap[j].m_ijk[0] - offset[0], myMap[j].m_ijk[1] - offset[1], myMap[j].m_ijk[2] - offset[2], myrow - startpos); } } else { float cacheRrs; const float* cacheRow = getRow(myMap[j].m_ciftiIndex, cacheRrs, true); float result = correlate(movingRow, movingRrs, cacheRow, cacheRrs); computeVol.setValue(result, myMap[myrow].m_ijk[0] - offset[0], myMap[myrow].m_ijk[1] - offset[1], myMap[myrow].m_ijk[2] - offset[2], j - startpos); } } } VolumeFile outputVol; int numSubvols = endpos - startpos; for (int j = 0; j < numSubvols; ++j) { AlgorithmVolumeGradient(NULL, &computeVol, &outputVol, volKern, &volRoi, NULL, j); for (int i = 0; i < mapSize; ++i) { accum[i] += outputVol.getValue(myMap[i].m_ijk[0] - offset[0], myMap[i].m_ijk[1] - offset[1], myMap[i].m_ijk[2] - offset[2]); } } } for (int i = 0; i < mapSize; ++i) { m_outColumn[myMap[i].m_ciftiIndex] = accum[i] / mapSize; } } void AlgorithmCiftiCorrelationGradient::processVolumeComponent(StructureEnum::Enum& myStructure, const float& volKern, const float& volExclude, const float& memLimitGB) { const CiftiXMLOld& myXML = m_inputCifti->getCiftiXMLOld(); vector myMap; myXML.getVolumeStructureMapForColumns(myMap, myStructure); int mapSize = (int)myMap.size(); vector accum(mapSize, 0.0); vector accumCount(mapSize, 0); int numCacheRows = mapSize; bool cacheFullInput = true; vector newdims; int64_t offset[3]; if (mapSize > 0) {//make a voxel bounding box to minimize memory usage int extrema[6] = { myMap[0].m_ijk[0], myMap[0].m_ijk[0], myMap[0].m_ijk[1], myMap[0].m_ijk[1], myMap[0].m_ijk[2], myMap[0].m_ijk[2] }; for (int64_t i = 1; i < mapSize; ++i) { if (myMap[i].m_ijk[0] < extrema[0]) extrema[0] = myMap[i].m_ijk[0]; if (myMap[i].m_ijk[0] > extrema[1]) extrema[1] = myMap[i].m_ijk[0]; if (myMap[i].m_ijk[1] < extrema[2]) extrema[2] = myMap[i].m_ijk[1]; if (myMap[i].m_ijk[1] > extrema[3]) extrema[3] = myMap[i].m_ijk[1]; if (myMap[i].m_ijk[2] < extrema[4]) extrema[4] = myMap[i].m_ijk[2]; if (myMap[i].m_ijk[2] > extrema[5]) extrema[5] = myMap[i].m_ijk[2]; } newdims.push_back(extrema[1] - extrema[0] + 1); newdims.push_back(extrema[3] - extrema[2] + 1); newdims.push_back(extrema[5] - extrema[4] + 1); offset[0] = extrema[0]; offset[1] = extrema[2]; offset[2] = extrema[4]; } else { return; } if (memLimitGB >= 0.0f) { numCacheRows = numRowsForMem(memLimitGB, m_numCols * sizeof(float), newdims[0] * newdims[1] * newdims[2] * sizeof(float), mapSize, cacheFullInput); } if (numCacheRows > mapSize) { cacheFullInput = true; numCacheRows = mapSize; } if (cacheFullInput) { if (numCacheRows != mapSize) CaretLogInfo("computing " + AString::number(numCacheRows) + " rows at a time"); } else { CaretLogInfo("computing " + AString::number(numCacheRows) + " rows at a time, reading rows as needed during processing"); } int64_t ciftiDims[3]; vector > ciftiSform; myXML.getVolumeDimsAndSForm(ciftiDims, ciftiSform); VolumeFile volRoi(newdims, ciftiSform); volRoi.setValueAllVoxels(0.0f); vector rowsToCache; for (int i = 0; i < mapSize; ++i) { volRoi.setValue(1.0f, myMap[i].m_ijk[0] - offset[0], myMap[i].m_ijk[1] - offset[1], myMap[i].m_ijk[2] - offset[2]); if (cacheFullInput) { rowsToCache.push_back(myMap[i].m_ciftiIndex); } } if (cacheFullInput) { cacheRows(rowsToCache); } for (int startpos = 0; startpos < mapSize; startpos += numCacheRows) { int endpos = startpos + numCacheRows; if (endpos > mapSize) endpos = mapSize; if (!cacheFullInput) { rowsToCache.clear(); for (int i = startpos; i < endpos; ++i) { rowsToCache.push_back(myMap[i].m_ciftiIndex); } cacheRows(rowsToCache); } int curRow = 0;//because we can't trust the order threads hit the critical section vector computeDims = newdims; computeDims.push_back(endpos - startpos); VolumeFile computeVol(computeDims, ciftiSform); #pragma omp CARET_PARFOR schedule(dynamic) for (int i = 0; i < mapSize; ++i) { float movingRrs; const float* movingRow; int myrow; #pragma omp critical {//CiftiFile may explode if we request multiple rows concurrently (needs mutexes), but we should force sequential requests anyway myrow = curRow;//so, manually force it to read sequentially ++curRow; movingRow = getRow(myMap[myrow].m_ciftiIndex, movingRrs); } Vector3D movingLoc; volRoi.indexToSpace(myMap[myrow].m_ijk, movingLoc);//NOTE: this is outside the cropped volume, but matches the real location in the full volume, because we didn't fix the center for (int j = startpos; j < endpos; ++j) { Vector3D seedLoc; volRoi.indexToSpace(myMap[j].m_ijk, seedLoc);//ditto if ((movingLoc - seedLoc).length() > volExclude)//don't correlate if closer than the exclude range { if (myrow >= startpos && myrow < endpos) { if (j >= myrow) { float cacheRrs; const float* cacheRow = getRow(myMap[j].m_ciftiIndex, cacheRrs, true); float result = correlate(movingRow, movingRrs, cacheRow, cacheRrs); computeVol.setValue(result, myMap[myrow].m_ijk[0] - offset[0], myMap[myrow].m_ijk[1] - offset[1], myMap[myrow].m_ijk[2] - offset[2], j - startpos); computeVol.setValue(result, myMap[j].m_ijk[0] - offset[0], myMap[j].m_ijk[1] - offset[1], myMap[j].m_ijk[2] - offset[2], myrow - startpos); } } else { float cacheRrs; const float* cacheRow = getRow(myMap[j].m_ciftiIndex, cacheRrs, true); float result = correlate(movingRow, movingRrs, cacheRow, cacheRrs); computeVol.setValue(result, myMap[myrow].m_ijk[0] - offset[0], myMap[myrow].m_ijk[1] - offset[1], myMap[myrow].m_ijk[2] - offset[2], j - startpos); } } } } VolumeFile outputVol, excludeRoi(newdims, ciftiSform); excludeRoi.setFrame(volRoi.getFrame()); int numSubvols = endpos - startpos; for (int j = 0; j < numSubvols; ++j) { Vector3D seedLoc; volRoi.indexToSpace(myMap[j + startpos].m_ijk, seedLoc); for (int i = 0; i < mapSize; ++i) { Vector3D otherLoc; volRoi.indexToSpace(myMap[i].m_ijk, otherLoc); if ((otherLoc - seedLoc).length() <= volExclude) { excludeRoi.setValue(0.0f, myMap[i].m_ijk[0] - offset[0], myMap[i].m_ijk[1] - offset[1], myMap[i].m_ijk[2] - offset[2]); } } AlgorithmVolumeGradient(NULL, &computeVol, &outputVol, volKern, &excludeRoi, NULL, j); for (int i = 0; i < mapSize; ++i) { if (excludeRoi.getValue(myMap[i].m_ijk[0] - offset[0], myMap[i].m_ijk[1] - offset[1], myMap[i].m_ijk[2] - offset[2]) > 0.0f) { float val = outputVol.getValue(myMap[i].m_ijk[0] - offset[0], myMap[i].m_ijk[1] - offset[1], myMap[i].m_ijk[2] - offset[2]); accum[i] += val; accumCount[i] += 1; } else { excludeRoi.setValue(1.0f, myMap[i].m_ijk[0] - offset[0], myMap[i].m_ijk[1] - offset[1], myMap[i].m_ijk[2] - offset[2]);//reset the ROI } } } } for (int i = 0; i < mapSize; ++i) { if (accumCount[i] != 0) { m_outColumn[myMap[i].m_ciftiIndex] = accum[i] / accumCount[i]; } else { m_outColumn[myMap[i].m_ciftiIndex] = 0.0f; } } } float AlgorithmCiftiCorrelationGradient::correlate(const float* row1, const float& rrs1, const float* row2, const float& rrs2) { double r; if (row1 == row2 && !m_covariance) { 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 if (m_covariance) { r = accum / m_numCols; } else { r = accum / (rrs1 * rrs2); } } if (!m_covariance) { if (m_applyFisher) { if (r > 0.999999) r = 0.999999;//prevent inf if (r < -0.999999) r = -0.999999;//prevent -inf r = 0.5 * log((1 + r) / (1 - r)); } else { if (r > 1.0) r = 1.0;//don't output anything silly if (r < -1.0) r = -1.0; } } return r; } void AlgorithmCiftiCorrelationGradient::init(const CiftiFile* input, const bool& undoFisherInput, const bool& applyFisher, const bool& covariance) { if (input->getCiftiXML().getMappingType(CiftiXML::ALONG_COLUMN) != CiftiMappingType::BRAIN_MODELS) throw AlgorithmException("input cifti file must have brain models mapping along column"); if (covariance) { if (applyFisher) throw AlgorithmException("cannot apply fisher z transformation to covariance"); } m_undoFisherInput = undoFisherInput; m_applyFisher = applyFisher; m_covariance = covariance; m_inputCifti = input; m_rowInfo.resize(m_inputCifti->getNumberOfRows()); m_cacheUsed = 0; m_numCols = m_inputCifti->getNumberOfColumns(); m_outColumn.resize(m_inputCifti->getNumberOfRows()); } void AlgorithmCiftiCorrelationGradient::cacheRows(const vector& ciftiIndices) { clearCache();//clear first, to be sure we never keep a cache around too long int curIndex = 0, numIndices = (int)ciftiIndices.size();//manually in-order m_rowCache.reserve(m_cacheUsed + numIndices);//so that pointers to members don't change #pragma omp CARET_PAR { int myIndex; float* myPtr; #pragma omp CARET_FOR schedule(dynamic) for (int i = 0; i < numIndices; ++i) { myPtr = NULL; #pragma omp critical { myIndex = curIndex; ++curIndex; CaretAssertVectorIndex(m_rowInfo, ciftiIndices[myIndex]); if (m_rowInfo[ciftiIndices[myIndex]].m_cacheIndex == -1) { if (m_cacheUsed >= (int)m_rowCache.size()) { m_rowCache.push_back(CacheRow()); m_rowCache[m_cacheUsed].m_row.resize(m_numCols); } m_rowCache[m_cacheUsed].m_ciftiIndex = ciftiIndices[myIndex]; myPtr = m_rowCache[m_cacheUsed].m_row.data(); m_inputCifti->getRow(myPtr, ciftiIndices[myIndex]); m_rowInfo[ciftiIndices[myIndex]].m_cacheIndex = m_cacheUsed; ++m_cacheUsed; } }//end critical, now compute while the next thread reads if (myPtr != NULL) { adjustRow(myPtr, ciftiIndices[myIndex]); } } } } void AlgorithmCiftiCorrelationGradient::clearCache() { for (int i = 0; i < m_cacheUsed; ++i) { m_rowInfo[m_rowCache[i].m_ciftiIndex].m_cacheIndex = -1; } m_cacheUsed = 0; } const float* AlgorithmCiftiCorrelationGradient::getRow(const int& ciftiIndex, float& rootResidSqr, const bool& mustBeCached) { float* ret; CaretAssertVectorIndex(m_rowInfo, ciftiIndex); if (m_rowInfo[ciftiIndex].m_cacheIndex != -1) { ret = m_rowCache[m_rowInfo[ciftiIndex].m_cacheIndex].m_row.data(); } else { CaretAssert(!mustBeCached); if (mustBeCached)//largely so it doesn't give warning about unused when compiled in release { throw AlgorithmException("something very bad happened, notify the developers"); } ret = getTempRow(); m_inputCifti->getRow(ret, ciftiIndex); adjustRow(ret, ciftiIndex); } rootResidSqr = m_rowInfo[ciftiIndex].m_rootResidSqr; return ret; } void AlgorithmCiftiCorrelationGradient::adjustRow(float* rowOut, const int& ciftiIndex) { if (m_undoFisherInput) { for (int i = 0; i < m_numCols; ++i) { double temp = exp(2 * rowOut[i]); rowOut[i] = (float)((temp - 1)/(temp + 1)); } } if (!m_rowInfo[ciftiIndex].m_haveCalculated) { double accum = 0.0;//double, for numerical stability for (int i = 0; i < m_numCols; ++i)//two pass, for numerical stability { accum += rowOut[i]; } float mean = accum / m_numCols; float rootResidSqr = 0.0f;//not used in covariance if (!m_covariance) { accum = 0.0; for (int i = 0; i < m_numCols; ++i) { float tempf = rowOut[i] - mean; accum += tempf * tempf; } rootResidSqr = sqrt(accum); } m_rowInfo[ciftiIndex].m_mean = mean; m_rowInfo[ciftiIndex].m_rootResidSqr = rootResidSqr; m_rowInfo[ciftiIndex].m_haveCalculated = true; } float mean = m_rowInfo[ciftiIndex].m_mean; for (int i = 0; i < m_numCols; ++i) { rowOut[i] -= mean; } } float* AlgorithmCiftiCorrelationGradient::getTempRow() { #ifdef CARET_OMP int oldsize = (int)m_tempRows.size(); int threadNum = omp_get_thread_num(); if (threadNum >= oldsize) { m_tempRows.resize(threadNum + 1); for (int i = oldsize; i <= threadNum; ++i) { m_tempRows[i] = CaretArray(m_numCols); } } return m_tempRows[threadNum].getArray(); #else if (m_tempRows.size() == 0) { m_tempRows.resize(1); m_tempRows[0] = CaretArray(m_numCols); } return m_tempRows[0].getArray(); #endif } int AlgorithmCiftiCorrelationGradient::numRowsForMem(const float& memLimitGB, const int64_t& inrowBytes, const int64_t& outrowBytes, const int& numRows, bool& cacheFullInput) { int64_t targetBytes = (int64_t)(memLimitGB * 1024 * 1024 * 1024); if (m_inputCifti->isInMemory()) targetBytes -= numRows * inrowBytes;//count in-memory input against the total too targetBytes -= numRows * sizeof(RowInfo) + 2 * outrowBytes;//storage for mean, stdev, and info about caching, output structures if (targetBytes < 1) { cacheFullInput = false;//the most memory conservation possible, though it will take a LOT of time and do a LOT of IO return 1; } if (inrowBytes * numRows < targetBytes)//if we can cache the full input, compute the number of passes needed and compare { int64_t div = max(outrowBytes, (int64_t)1);//make sure it is never zero or negative int64_t numRowsFull = (targetBytes - inrowBytes * numRows) / div; if (numRowsFull < 1) numRowsFull = 1; int64_t fullPasses = numRows / numRowsFull; int64_t fullCorrSkip = (fullPasses * numRowsFull * (numRowsFull - 1) + (numRows - fullPasses * numRowsFull) * (numRows - fullPasses * numRowsFull - 1)) / 2; #ifdef CARET_OMP targetBytes -= inrowBytes * omp_get_max_threads(); #else targetBytes -= inrowBytes;//1 row in memory that isn't a reference to cache #endif int64_t numPassesPartial = ((outrowBytes + inrowBytes) * numRows + targetBytes - 1) / targetBytes;//break the partial cached passes up equally, to use less memory, and so we don't get an anemic pass at the end if (numPassesPartial < 1) { numPassesPartial = 1; CaretLogWarning("memory usage calculation found zero/negative pass solution, report to developers (it may use a lot of memory this run)"); } int64_t numRowsPartial = (numRows + numPassesPartial - 1) / numPassesPartial; fullPasses = numPassesPartial - 1; int64_t partialCorrSkip = (fullPasses * numRowsPartial * (numRowsPartial - 1) + (numRows - fullPasses * numRowsPartial) * (numRows - fullPasses * numRowsPartial - 1)) / 2; int ret; if (partialCorrSkip > fullCorrSkip * 1.05f)//prefer full caching slightly - include a bias factor in options? {//assume IO and row adjustment (unfisher, subtract mean) won't be the limiting factor, since IO should be balanced during correlation due to evenly sized passes when not fully cached cacheFullInput = false; ret = numRowsPartial; } else { cacheFullInput = true; ret = numRowsFull; } if (ret < 1) ret = 1;//sanitize, just in case if (ret > numRows) ret = numRows; return ret; } else {//if we can't cache the whole thing, split passes evenly cacheFullInput = false; int64_t div = max((int64_t)1, (outrowBytes + inrowBytes) * numRows); #ifdef CARET_OMP targetBytes -= inrowBytes * omp_get_max_threads(); #else targetBytes -= inrowBytes;//1 row in memory that isn't a reference to cache #endif int64_t numPassesPartial = (targetBytes + div - 1) / targetBytes; int ret = (numRows + numPassesPartial - 1) / numPassesPartial; if (ret < 1) ret = 1;//sanitize, just in case if (ret > numRows) ret = numRows; return ret; } } float AlgorithmCiftiCorrelationGradient::getAlgorithmInternalWeight() { return 1.0f;//override this if needed, if the progress bar isn't smooth } float AlgorithmCiftiCorrelationGradient::getSubAlgorithmWeight() { //return AlgorithmInsertNameHere::getAlgorithmWeight();//if you use a subalgorithm return 0.0f; } connectome-workbench-1.2.3+git41-gc4c6c90/src/Algorithms/AlgorithmCiftiCorrelationGradient.h000066400000000000000000000120461300200146000316160ustar00rootroot00000000000000#ifndef __ALGORITHM_CIFTI_CORRELATION_GRADIENT_H__ #define __ALGORITHM_CIFTI_CORRELATION_GRADIENT_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 "AbstractAlgorithm.h" #include "CaretPointer.h" #include "StructureEnum.h" namespace caret { class AlgorithmCiftiCorrelationGradient : public AbstractAlgorithm { AlgorithmCiftiCorrelationGradient(); struct CacheRow { int m_ciftiIndex; std::vector m_row; }; struct RowInfo { bool m_haveCalculated; float m_mean, m_rootResidSqr; int m_cacheIndex; RowInfo() { m_haveCalculated = false; m_cacheIndex = -1; } }; std::vector m_rowCache; std::vector m_rowInfo; std::vector > m_tempRows;//reuse return values in getRow instead of reallocating std::vector m_outColumn; int m_cacheUsed;//reuse cache entries instead of reallocating them int m_numCols; bool m_undoFisherInput, m_applyFisher, m_covariance; const CiftiFile* m_inputCifti;//so that accesses work through the cache functions void cacheRows(const std::vector& ciftiIndices);//grabs the rows and does whatever it needs to, using as much IO bandwidth and CPU resources as available/needed void clearCache(); const float* getRow(const int& ciftiIndex, float& rootResidSqr, const bool& mustBeCached = false); void adjustRow(float* rowOut, const int& ciftiIndex);//does the reverse fisher transform, computes stuff, subtracts mean float* getTempRow(); float correlate(const float* row1, const float& rrs1, const float* row2, const float& rrs2); void init(const CiftiFile* input, const bool& undoFisherInput, const bool& applyFisher, const bool& covariance); int numRowsForMem(const float& memLimitGB, const int64_t& inrowBytes, const int64_t& outrowBytes, const int& numRows, bool& cacheFullInput); //void processSurfaceComponentLocal(StructureEnum::Enum& myStructure, const float& surfKern, const float& memLimitGB, SurfaceFile* mySurf); void processSurfaceComponent(StructureEnum::Enum& myStructure, const float& surfKern, const float& memLimitGB, SurfaceFile* mySurf, const MetricFile* myAreas); void processSurfaceComponent(StructureEnum::Enum& myStructure, const float& surfKern, const float& surfExclude, const float& memLimitGB, SurfaceFile* mySurf, const MetricFile* myAreas); //void processVolumeComponentLocal(StructureEnum::Enum& myStructure, const float& volKern, const float& memLimitGB); void processVolumeComponent(StructureEnum::Enum& myStructure, const float& volKern, const float& memLimitGB); void processVolumeComponent(StructureEnum::Enum& myStructure, const float& volKern, const float& volExclude, const float& memLimitGB); protected: static float getSubAlgorithmWeight(); static float getAlgorithmInternalWeight(); public: AlgorithmCiftiCorrelationGradient(ProgressObject* myProgObj, const CiftiFile* myCifti, CiftiFile* myCiftiOut, SurfaceFile* myLeftSurf = NULL, SurfaceFile* myRightSurf = NULL, SurfaceFile* myCerebSurf = NULL, const MetricFile* myLeftAreas = NULL, const MetricFile* myRightAreas = NULL, const MetricFile* myCerebAreas = NULL, const float& surfKern = -1.0f, const float& volKern = -1.0f, const bool& undoFisherInput = false, const bool& applyFisher = false, const float& surfaceExclude = -1.0f, const float& volumeExclude = -1.0f, const bool& covariance = false, const float& memLimitGB = -1.0f); static OperationParameters* getParameters(); static void useParameters(OperationParameters* myParams, ProgressObject* myProgObj); static AString getCommandSwitch(); static AString getShortDescription(); }; typedef TemplateAutoOperation AutoAlgorithmCiftiCorrelationGradient; } #endif //__ALGORITHM_CIFTI_CORRELATION_GRADIENT_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Algorithms/AlgorithmCiftiCreateDenseScalar.cxx000066400000000000000000000277161300200146000315540ustar00rootroot00000000000000/*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 "AlgorithmCiftiCreateDenseScalar.h" #include "AlgorithmException.h" #include "AlgorithmCiftiCreateDenseTimeseries.h" //for making the dense mapping from metric files #include "CaretAssert.h" #include "CaretLogger.h" #include "CiftiFile.h" #include "CaretAssert.h" #include "GiftiLabelTable.h" #include "MetricFile.h" #include "StructureEnum.h" #include "VolumeFile.h" #include #include #include #include using namespace caret; using namespace std; AString AlgorithmCiftiCreateDenseScalar::getCommandSwitch() { return "-cifti-create-dense-scalar"; } AString AlgorithmCiftiCreateDenseScalar::getShortDescription() { return "CREATE A CIFTI DENSE SCALAR FILE"; } OperationParameters* AlgorithmCiftiCreateDenseScalar::getParameters() { OperationParameters* ret = new OperationParameters(); ret->addCiftiOutputParameter(1, "cifti-out", "the output cifti file"); OptionalParameter* volumeOpt = ret->createOptionalParameter(2, "-volume", "volume component"); volumeOpt->addVolumeParameter(1, "volume-data", "volume file containing all voxel data for all volume structures"); volumeOpt->addVolumeParameter(2, "label-volume", "label volume file containing labels for cifti structures"); OptionalParameter* leftMetricOpt = ret->createOptionalParameter(3, "-left-metric", "metric for left surface"); leftMetricOpt->addMetricParameter(1, "metric", "the metric file"); OptionalParameter* leftRoiOpt = leftMetricOpt->createOptionalParameter(2, "-roi-left", "roi of vertices to use from left surface"); leftRoiOpt->addMetricParameter(1, "roi-metric", "the ROI as a metric file"); OptionalParameter* rightMetricOpt = ret->createOptionalParameter(4, "-right-metric", "metric for left surface"); rightMetricOpt->addMetricParameter(1, "metric", "the metric file"); OptionalParameter* rightRoiOpt = rightMetricOpt->createOptionalParameter(2, "-roi-right", "roi of vertices to use from right surface"); rightRoiOpt->addMetricParameter(1, "roi-metric", "the ROI as a metric file"); OptionalParameter* cerebMetricOpt = ret->createOptionalParameter(5, "-cerebellum-metric", "metric for the cerebellum"); cerebMetricOpt->addMetricParameter(1, "metric", "the metric file"); OptionalParameter* cerebRoiOpt = cerebMetricOpt->createOptionalParameter(2, "-roi-cerebellum", "roi of vertices to use from right surface"); cerebRoiOpt->addMetricParameter(1, "roi-metric", "the ROI as a metric file"); OptionalParameter* nameFileOpt = ret->createOptionalParameter(6, "-name-file", "use a text file to set all map names"); nameFileOpt->addStringParameter(1, "file", "text file containing map names, one per line"); AString myText = AString("All input files must have the same number of columns/subvolumes. Only the specified components will be in the output cifti. ") + "Map names will be taken from one of the input files. " + "At least one component must be specified. The label volume should have some of the label names from this list, all other label names will be ignored:\n"; vector myStructureEnums; StructureEnum::getAllEnums(myStructureEnums); for (int i = 0; i < (int)myStructureEnums.size(); ++i) { myText += "\n" + StructureEnum::toName(myStructureEnums[i]); } ret->setHelpText(myText); return ret; } void AlgorithmCiftiCreateDenseScalar::useParameters(OperationParameters* myParams, ProgressObject* myProgObj) { VolumeFile* myVol = NULL, *myVolLabel = NULL; CiftiFile* myCiftiOut = myParams->getOutputCifti(1); OptionalParameter* volumeOpt = myParams->getOptionalParameter(2); if (volumeOpt->m_present) { myVol = volumeOpt->getVolume(1); myVolLabel = volumeOpt->getVolume(2); } MetricFile* leftData = NULL, *leftRoi = NULL, *rightData = NULL, *rightRoi = NULL, *cerebData = NULL, *cerebRoi = NULL; OptionalParameter* leftMetricOpt = myParams->getOptionalParameter(3); if (leftMetricOpt->m_present) { leftData = leftMetricOpt->getMetric(1); OptionalParameter* leftRoiOpt = leftMetricOpt->getOptionalParameter(2); if (leftRoiOpt->m_present) { leftRoi = leftRoiOpt->getMetric(1); } } OptionalParameter* rightMetricOpt = myParams->getOptionalParameter(4); if (rightMetricOpt->m_present) { rightData = rightMetricOpt->getMetric(1); OptionalParameter* rightRoiOpt = rightMetricOpt->getOptionalParameter(2); if (rightRoiOpt->m_present) { rightRoi = rightRoiOpt->getMetric(1); } } OptionalParameter* cerebMetricOpt = myParams->getOptionalParameter(5); if (cerebMetricOpt->m_present) { cerebData = cerebMetricOpt->getMetric(1); OptionalParameter* cerebRoiOpt = cerebMetricOpt->getOptionalParameter(2); if (cerebRoiOpt->m_present) { cerebRoi = cerebRoiOpt->getMetric(1); } } vector nameStore; vector* namePtr = NULL; OptionalParameter* nameFileOpt = myParams->getOptionalParameter(6); if (nameFileOpt->m_present) { AString listfileName = nameFileOpt->getString(1); ifstream nameListFile(listfileName.toLocal8Bit().constData()); if (!nameListFile.good()) { throw AlgorithmException("error reading name list file"); } string mapName; while (getline(nameListFile, mapName)) { nameStore.push_back(mapName.c_str()); } namePtr = &nameStore; } AlgorithmCiftiCreateDenseScalar(myProgObj, myCiftiOut, myVol, myVolLabel, leftData, leftRoi, rightData, rightRoi, cerebData, cerebRoi, namePtr); } AlgorithmCiftiCreateDenseScalar::AlgorithmCiftiCreateDenseScalar(ProgressObject* myProgObj, CiftiFile* myCiftiOut, const VolumeFile* myVol, const VolumeFile* myVolLabel, const MetricFile* leftData, const MetricFile* leftRoi, const MetricFile* rightData, const MetricFile* rightRoi, const MetricFile* cerebData, const MetricFile* cerebRoi, const vector* namePtr) : AbstractAlgorithm(myProgObj) { CaretAssert(myCiftiOut != NULL); LevelProgress myProgress(myProgObj); CiftiBrainModelsMap denseMap = AlgorithmCiftiCreateDenseTimeseries::makeDenseMapping(myVol, myVolLabel, leftData, leftRoi, rightData, rightRoi, cerebData, cerebRoi); CiftiXML myXML; myXML.setNumberOfDimensions(2); myXML.setMap(CiftiXML::ALONG_COLUMN, denseMap); int numMaps = -1; const CaretMappableDataFile* nameFile = NULL; if (leftData != NULL) { numMaps = leftData->getNumberOfMaps(); nameFile = leftData; } if (rightData != NULL) { if (numMaps == -1) { numMaps = rightData->getNumberOfMaps(); if (nameFile == NULL) nameFile = rightData; } else { if (numMaps != rightData->getNumberOfMaps()) { throw AlgorithmException("right and left surface data have a different number of maps"); } } } if (cerebData != NULL) { if (numMaps == -1) { numMaps = cerebData->getNumberOfMaps(); if (nameFile == NULL) nameFile = cerebData; } else { if (numMaps != cerebData->getNumberOfMaps()) { throw AlgorithmException("cerebellum surface data has a different number of maps"); } } } if (myVol != NULL) { if (numMaps == -1) { numMaps = myVol->getNumberOfMaps(); if (nameFile == NULL) nameFile = myVol; } else { if (numMaps != myVol->getNumberOfMaps()) { throw AlgorithmException("volume data has a different number of maps"); } } } if (numMaps == -1)//doubles as checking nameFile for being NULL { throw AlgorithmException("no models specified"); } if (namePtr != NULL) { if ((int)(namePtr->size()) != numMaps) { if ((int)(namePtr->size()) < numMaps) { throw AlgorithmException("not enough map names provided"); } else { if ((int)(namePtr->size()) > numMaps + 1 || namePtr->back() != "") {//allow an extra newline on the end of the input file - could do strict checking here and fix things up in useParameters, but then it would also need to figure out number of output maps throw AlgorithmException("more map names were provided than output file would use"); } } CaretLogFine("provided map names list has an extra empty string on the end"); } } CiftiScalarsMap scalarMap; scalarMap.setLength(numMaps); for (int i = 0; i < numMaps; ++i) { if (namePtr != NULL) { scalarMap.setMapName(i, namePtr->at(i)); } else { scalarMap.setMapName(i, nameFile->getMapName(i));//copy map names } } myXML.setMap(CiftiXML::ALONG_ROW, scalarMap); myCiftiOut->setCiftiXML(myXML); CaretArray temprow(numMaps); const CiftiBrainModelsMap& myDenseMap = myXML.getBrainModelsMap(CiftiXML::ALONG_COLUMN); vector surfStructs = myDenseMap.getSurfaceStructureList(); for (int whichStruct = 0; whichStruct < (int)surfStructs.size(); ++whichStruct) { vector surfMap = myDenseMap.getSurfaceMap(surfStructs[whichStruct]); const MetricFile* dataMetric = NULL; switch (surfStructs[whichStruct]) { case StructureEnum::CORTEX_LEFT: dataMetric = leftData; break; case StructureEnum::CORTEX_RIGHT: dataMetric = rightData; break; case StructureEnum::CEREBELLUM: dataMetric = cerebData; break; default: CaretAssert(false); } for (int64_t i = 0; i < (int)surfMap.size(); ++i) { for (int t = 0; t < numMaps; ++t) { temprow[t] = dataMetric->getValue(surfMap[i].m_surfaceNode, t); } myCiftiOut->setRow(temprow, surfMap[i].m_ciftiIndex); } } vector volMap = myDenseMap.getFullVolumeMap();//we don't need to know which voxel is from which structure for (int64_t i = 0; i < (int)volMap.size(); ++i) { for (int t = 0; t < numMaps; ++t) { temprow[t] = myVol->getValue(volMap[i].m_ijk, t); } myCiftiOut->setRow(temprow, volMap[i].m_ciftiIndex); } } float AlgorithmCiftiCreateDenseScalar::getAlgorithmInternalWeight() { return 1.0f;//override this if needed, if the progress bar isn't smooth } float AlgorithmCiftiCreateDenseScalar::getSubAlgorithmWeight() { //return AlgorithmInsertNameHere::getAlgorithmWeight();//if you use a subalgorithm return 0.0f; } connectome-workbench-1.2.3+git41-gc4c6c90/src/Algorithms/AlgorithmCiftiCreateDenseScalar.h000066400000000000000000000041751300200146000311730ustar00rootroot00000000000000#ifndef __ALGORITHM_CIFTI_CREATE_DENSE_SCALAR_H__ #define __ALGORITHM_CIFTI_CREATE_DENSE_SCALAR_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 "AbstractAlgorithm.h" namespace caret { class AlgorithmCiftiCreateDenseScalar : public AbstractAlgorithm { AlgorithmCiftiCreateDenseScalar(); protected: static float getSubAlgorithmWeight(); static float getAlgorithmInternalWeight(); public: AlgorithmCiftiCreateDenseScalar(ProgressObject* myProgObj, CiftiFile* myCiftiOut, const VolumeFile* myVol = NULL, const VolumeFile* myVolLabel = NULL, const MetricFile* leftData = NULL, const MetricFile* leftRoi = NULL, const MetricFile* rightData = NULL, const MetricFile* rightRoi = NULL, const MetricFile* cerebData = NULL, const MetricFile* cerebRoi = NULL, const std::vector* namePtr = NULL); static OperationParameters* getParameters(); static void useParameters(OperationParameters* myParams, ProgressObject* myProgObj); static AString getCommandSwitch(); static AString getShortDescription(); }; typedef TemplateAutoOperation AutoAlgorithmCiftiCreateDenseScalar; } #endif //__ALGORITHM_CIFTI_CREATE_DENSE_SCALAR_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Algorithms/AlgorithmCiftiCreateDenseTimeseries.cxx000066400000000000000000000414361300200146000324530ustar00rootroot00000000000000/*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 "AlgorithmCiftiCreateDenseTimeseries.h" #include "AlgorithmException.h" #include "CaretAssert.h" #include "CaretLogger.h" #include "CaretPointer.h" #include "CiftiFile.h" #include "GiftiLabelTable.h" #include "MetricFile.h" #include "StructureEnum.h" #include "VolumeFile.h" #include #include #include using namespace caret; using namespace std; AString AlgorithmCiftiCreateDenseTimeseries::getCommandSwitch() { return "-cifti-create-dense-timeseries"; } AString AlgorithmCiftiCreateDenseTimeseries::getShortDescription() { return "CREATE A CIFTI DENSE TIMESERIES"; } OperationParameters* AlgorithmCiftiCreateDenseTimeseries::getParameters() { OperationParameters* ret = new OperationParameters(); ret->addCiftiOutputParameter(1, "cifti-out", "the output cifti file"); OptionalParameter* volumeOpt = ret->createOptionalParameter(2, "-volume", "volume component"); volumeOpt->addVolumeParameter(1, "volume-data", "volume file containing all voxel data for all volume structures"); volumeOpt->addVolumeParameter(2, "label-volume", "label volume file containing labels for cifti structures"); OptionalParameter* leftMetricOpt = ret->createOptionalParameter(3, "-left-metric", "metric for left surface"); leftMetricOpt->addMetricParameter(1, "metric", "the metric file"); OptionalParameter* leftRoiOpt = leftMetricOpt->createOptionalParameter(2, "-roi-left", "roi of vertices to use from left surface"); leftRoiOpt->addMetricParameter(1, "roi-metric", "the ROI as a metric file"); OptionalParameter* rightMetricOpt = ret->createOptionalParameter(4, "-right-metric", "metric for left surface"); rightMetricOpt->addMetricParameter(1, "metric", "the metric file"); OptionalParameter* rightRoiOpt = rightMetricOpt->createOptionalParameter(2, "-roi-right", "roi of vertices to use from right surface"); rightRoiOpt->addMetricParameter(1, "roi-metric", "the ROI as a metric file"); OptionalParameter* cerebMetricOpt = ret->createOptionalParameter(5, "-cerebellum-metric", "metric for the cerebellum"); cerebMetricOpt->addMetricParameter(1, "metric", "the metric file"); OptionalParameter* cerebRoiOpt = cerebMetricOpt->createOptionalParameter(2, "-roi-cerebellum", "roi of vertices to use from right surface"); cerebRoiOpt->addMetricParameter(1, "roi-metric", "the ROI as a metric file"); OptionalParameter* timestepOpt = ret->createOptionalParameter(6, "-timestep", "set the timestep"); timestepOpt->addDoubleParameter(1, "interval", "the timestep, in seconds (default 1.0)"); OptionalParameter* timestartOpt = ret->createOptionalParameter(7, "-timestart", "set the start time"); timestartOpt->addDoubleParameter(1, "start", "the time at the first frame, in seconds (default 0.0)"); OptionalParameter* unitOpt = ret->createOptionalParameter(8, "-unit", "use a unit other than time"); unitOpt->addStringParameter(1, "unit", "unit identifier (default SECOND)"); AString myText = AString("All input files must have the same number of columns/subvolumes. Only the specified components will be in the output cifti. ") + "At least one component must be specified. The label volume should have some of the label names from this list, all other label names will be ignored:\n"; vector myStructureEnums; StructureEnum::getAllEnums(myStructureEnums); for (int i = 0; i < (int)myStructureEnums.size(); ++i) { myText += "\n" + StructureEnum::toName(myStructureEnums[i]); } myText += "\n\nThe -unit option accepts these values:\n"; vector units = CiftiSeriesMap::getAllUnits(); for (int i = 0; i < (int)units.size(); ++i) { myText += "\n" + CiftiSeriesMap::unitToString(units[i]); } ret->setHelpText(myText); return ret; } void AlgorithmCiftiCreateDenseTimeseries::useParameters(OperationParameters* myParams, ProgressObject* myProgObj) { VolumeFile* myVol = NULL, *myVolLabel = NULL; CiftiFile* myCiftiOut = myParams->getOutputCifti(1); OptionalParameter* volumeOpt = myParams->getOptionalParameter(2); if (volumeOpt->m_present) { myVol = volumeOpt->getVolume(1); myVolLabel = volumeOpt->getVolume(2); } MetricFile* leftData = NULL, *leftRoi = NULL, *rightData = NULL, *rightRoi = NULL, *cerebData = NULL, *cerebRoi = NULL; OptionalParameter* leftMetricOpt = myParams->getOptionalParameter(3); if (leftMetricOpt->m_present) { leftData = leftMetricOpt->getMetric(1); OptionalParameter* leftRoiOpt = leftMetricOpt->getOptionalParameter(2); if (leftRoiOpt->m_present) { leftRoi = leftRoiOpt->getMetric(1); } } OptionalParameter* rightMetricOpt = myParams->getOptionalParameter(4); if (rightMetricOpt->m_present) { rightData = rightMetricOpt->getMetric(1); OptionalParameter* rightRoiOpt = rightMetricOpt->getOptionalParameter(2); if (rightRoiOpt->m_present) { rightRoi = rightRoiOpt->getMetric(1); } } OptionalParameter* cerebMetricOpt = myParams->getOptionalParameter(5); if (cerebMetricOpt->m_present) { cerebData = cerebMetricOpt->getMetric(1); OptionalParameter* cerebRoiOpt = cerebMetricOpt->getOptionalParameter(2); if (cerebRoiOpt->m_present) { cerebRoi = cerebRoiOpt->getMetric(1); } } float timestep = 1.0f; OptionalParameter* timestepOpt = myParams->getOptionalParameter(6); if (timestepOpt->m_present) { timestep = (float)timestepOpt->getDouble(1); } float timestart = 0.0f; OptionalParameter* timestartOpt = myParams->getOptionalParameter(7); if (timestartOpt->m_present) { timestart = (float)timestartOpt->getDouble(1); } CiftiSeriesMap::Unit myUnit = CiftiSeriesMap::SECOND; OptionalParameter* unitOpt = myParams->getOptionalParameter(8); if (unitOpt->m_present) { AString unitName = unitOpt->getString(1); bool ok = false; myUnit = CiftiSeriesMap::stringToUnit(unitName, ok); if (!ok) { throw AlgorithmException("unrecognized unit name: '" + unitName + "'"); } } AlgorithmCiftiCreateDenseTimeseries(myProgObj, myCiftiOut, myVol, myVolLabel, leftData, leftRoi, rightData, rightRoi, cerebData, cerebRoi, timestep, timestart, myUnit); } AlgorithmCiftiCreateDenseTimeseries::AlgorithmCiftiCreateDenseTimeseries(ProgressObject* myProgObj, CiftiFile* myCiftiOut, const VolumeFile* myVol, const VolumeFile* myVolLabel, const MetricFile* leftData, const MetricFile* leftRoi, const MetricFile* rightData, const MetricFile* rightRoi, const MetricFile* cerebData, const MetricFile* cerebRoi, const float& timestep, const float& timestart, const CiftiSeriesMap::Unit& myUnit) : AbstractAlgorithm(myProgObj) { CaretAssert(myCiftiOut != NULL); LevelProgress myProgress(myProgObj); CiftiBrainModelsMap denseMap = makeDenseMapping(myVol, myVolLabel, leftData, leftRoi, rightData, rightRoi, cerebData, cerebRoi); CiftiXML myXML; myXML.setNumberOfDimensions(2); myXML.setMap(CiftiXML::ALONG_COLUMN, denseMap); int numMaps = -1; if (leftData != NULL) { numMaps = leftData->getNumberOfMaps(); } if (rightData != NULL) { if (numMaps == -1) { numMaps = rightData->getNumberOfMaps(); } else { if (numMaps != rightData->getNumberOfMaps()) { throw AlgorithmException("right and left surface data have a different number of maps"); } } } if (cerebData != NULL) { if (numMaps == -1) { numMaps = cerebData->getNumberOfMaps(); } else { if (numMaps != cerebData->getNumberOfMaps()) { throw AlgorithmException("cerebellum surface data has a different number of maps"); } } } if (myVol != NULL) { if (numMaps == -1) { numMaps = myVol->getNumberOfMaps(); } else { if (numMaps != myVol->getNumberOfMaps()) { throw AlgorithmException("volume data has a different number of maps"); } } } if (numMaps == -1) { throw AlgorithmException("no models specified"); } CiftiSeriesMap seriesMap; seriesMap.setUnit(myUnit); seriesMap.setStart(timestart); seriesMap.setStep(timestep); seriesMap.setLength(numMaps); myXML.setMap(CiftiXML::ALONG_ROW, seriesMap); myCiftiOut->setCiftiXML(myXML); CaretArray temprow(numMaps); const CiftiBrainModelsMap& myDenseMap = myXML.getBrainModelsMap(CiftiXML::ALONG_COLUMN); vector surfStructs = myDenseMap.getSurfaceStructureList(); for (int whichStruct = 0; whichStruct < (int)surfStructs.size(); ++whichStruct) { vector surfMap = myDenseMap.getSurfaceMap(surfStructs[whichStruct]); const MetricFile* dataMetric = NULL; switch (surfStructs[whichStruct]) { case StructureEnum::CORTEX_LEFT: dataMetric = leftData; break; case StructureEnum::CORTEX_RIGHT: dataMetric = rightData; break; case StructureEnum::CEREBELLUM: dataMetric = cerebData; break; default: CaretAssert(false); } for (int64_t i = 0; i < (int)surfMap.size(); ++i) { for (int t = 0; t < numMaps; ++t) { temprow[t] = dataMetric->getValue(surfMap[i].m_surfaceNode, t); } myCiftiOut->setRow(temprow, surfMap[i].m_ciftiIndex); } } vector volMap = myDenseMap.getFullVolumeMap();//we don't need to know which voxel is from which structure for (int64_t i = 0; i < (int)volMap.size(); ++i) { for (int t = 0; t < numMaps; ++t) { temprow[t] = myVol->getValue(volMap[i].m_ijk, t); } myCiftiOut->setRow(temprow, volMap[i].m_ciftiIndex); } } CiftiBrainModelsMap AlgorithmCiftiCreateDenseTimeseries::makeDenseMapping(const VolumeFile* myVol, const VolumeFile* myVolLabel, const MetricFile* leftData, const MetricFile* leftRoi, const MetricFile* rightData, const MetricFile* rightRoi, const MetricFile* cerebData, const MetricFile* cerebRoi) { bool noData = true; CiftiBrainModelsMap denseMap; if (leftData != NULL) { noData = false; if (leftRoi == NULL) { denseMap.addSurfaceModel(leftData->getNumberOfNodes(), StructureEnum::CORTEX_LEFT); } else { if (leftRoi->getNumberOfNodes() != leftData->getNumberOfNodes()) { throw AlgorithmException("left surface ROI and data have different vertex counts"); } denseMap.addSurfaceModel(leftData->getNumberOfNodes(), StructureEnum::CORTEX_LEFT, leftRoi->getValuePointerForColumn(0)); } } if (rightData != NULL) { noData = false; if (rightRoi == NULL) { denseMap.addSurfaceModel(rightData->getNumberOfNodes(), StructureEnum::CORTEX_RIGHT); } else { if (rightRoi->getNumberOfNodes() != rightData->getNumberOfNodes()) { throw AlgorithmException("right surface ROI and data have different vertex counts"); } denseMap.addSurfaceModel(rightRoi->getNumberOfNodes(), StructureEnum::CORTEX_RIGHT, rightRoi->getValuePointerForColumn(0)); } } if (cerebData != NULL) { noData = false; if (cerebRoi == NULL) { denseMap.addSurfaceModel(cerebData->getNumberOfNodes(), StructureEnum::CEREBELLUM); } else { if (cerebRoi->getNumberOfNodes() != cerebData->getNumberOfNodes()) { throw AlgorithmException("cerebellum surface ROI and data have different vertex counts"); } denseMap.addSurfaceModel(cerebRoi->getNumberOfNodes(), StructureEnum::CEREBELLUM, cerebRoi->getValuePointerForColumn(0)); } } if (myVol != NULL) { CaretAssert(myVolLabel != NULL); if (myVolLabel == NULL) { throw AlgorithmException("making a dense mapping with voxels requires a label volume"); } if (!myVol->matchesVolumeSpace(myVolLabel)) { throw AlgorithmException("label volume has a different volume space than data volume"); } if (myVolLabel->getType() != SubvolumeAttributes::LABEL) { throw AlgorithmException("parcel volume is not of type label"); } noData = false; map labelMap;//maps label values to structures vector > voxelLists;//voxel lists for each volume component map componentMap;//maps structures to indexes in voxelLists const GiftiLabelTable* myLabelTable = myVolLabel->getMapLabelTable(0); vector labelKeys; myLabelTable->getKeys(labelKeys); int count = 0; for (int i = 0; i < (int)labelKeys.size(); ++i) { bool ok = false; StructureEnum::Enum thisStructure = StructureEnum::fromName(myLabelTable->getLabelName(labelKeys[i]), &ok); if (ok) { labelMap[labelKeys[i]] = thisStructure; if (componentMap.find(thisStructure) == componentMap.end())//make sure we don't already have this structure from another label { componentMap[thisStructure] = count; ++count; } } } voxelLists.resize(count); vector mydims; myVolLabel->getDimensions(mydims); for (int64_t k = 0; k < mydims[2]; ++k) { for (int64_t j = 0; j < mydims[1]; ++j) { for (int64_t i = 0; i < mydims[0]; ++i) { int myval = (int)floor(myVolLabel->getValue(i, j, k) + 0.5f); map::iterator myiter = labelMap.find(myval); if (myiter != labelMap.end()) { int whichList = componentMap.find(myiter->second)->second;//this should always find one, so we don't need to check for being end voxelLists[whichList].push_back(i); voxelLists[whichList].push_back(j); voxelLists[whichList].push_back(k); } } } } denseMap.setVolumeSpace(VolumeSpace(mydims.data(), myVol->getSform())); for (map::iterator myiter = componentMap.begin(); myiter != componentMap.end(); ++myiter) { if (voxelLists[myiter->second].empty()) { CaretLogWarning("volume label file has empty definition of '" + StructureEnum::toName(myiter->first) + "', skipping"); } else { denseMap.addVolumeModel(myiter->first, voxelLists[myiter->second]); } } } if (noData) { throw AlgorithmException("no models specified"); } return denseMap; } float AlgorithmCiftiCreateDenseTimeseries::getAlgorithmInternalWeight() { return 1.0f;//override this if needed, if the progress bar isn't smooth } float AlgorithmCiftiCreateDenseTimeseries::getSubAlgorithmWeight() { return 0.0f; } connectome-workbench-1.2.3+git41-gc4c6c90/src/Algorithms/AlgorithmCiftiCreateDenseTimeseries.h000066400000000000000000000055451300200146000321010ustar00rootroot00000000000000#ifndef __ALGORITHM_CIFTI_CREATE_DENSE_TIMESERIES_H__ #define __ALGORITHM_CIFTI_CREATE_DENSE_TIMESERIES_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 "AbstractAlgorithm.h" #include "CiftiBrainModelsMap.h" #include "CiftiSeriesMap.h" namespace caret { class AlgorithmCiftiCreateDenseTimeseries : public AbstractAlgorithm { AlgorithmCiftiCreateDenseTimeseries(); protected: static float getSubAlgorithmWeight(); static float getAlgorithmInternalWeight(); public: AlgorithmCiftiCreateDenseTimeseries(ProgressObject* myProgObj, CiftiFile* myCiftiOut, const VolumeFile* myVol = NULL, const VolumeFile* myVolLabel = NULL, const MetricFile* leftData = NULL, const MetricFile* leftRoi = NULL, const MetricFile* rightData = NULL, const MetricFile* rightRoi = NULL, const MetricFile* cerebData = NULL, const MetricFile* cerebRoi = NULL, const float& timestep = 1.0f, const float& timestart = 0.0f, const CiftiSeriesMap::Unit& myUnit = CiftiSeriesMap::SECOND); static CiftiBrainModelsMap makeDenseMapping(const VolumeFile* myVol = NULL, const VolumeFile* myVolLabel = NULL, const MetricFile* leftData = NULL, const MetricFile* leftRoi = NULL, const MetricFile* rightData = NULL, const MetricFile* rightRoi = NULL, const MetricFile* cerebData = NULL, const MetricFile* cerebRoi = NULL);//where should this go? should also have version that accepts LabelFile static OperationParameters* getParameters(); static void useParameters(OperationParameters* myParams, ProgressObject* myProgObj); static AString getCommandSwitch(); static AString getShortDescription(); }; typedef TemplateAutoOperation AutoAlgorithmCiftiCreateDenseTimeseries; } #endif //__ALGORITHM_CIFTI_CREATE_DENSE_TIMESERIES_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Algorithms/AlgorithmCiftiCreateLabel.cxx000066400000000000000000000415111300200146000303740ustar00rootroot00000000000000/*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 "AlgorithmCiftiCreateLabel.h" #include "AlgorithmException.h" #include "CaretAssert.h" #include "CaretPointer.h" #include "CiftiFile.h" #include "GiftiLabel.h" #include "GiftiLabelTable.h" #include "LabelFile.h" #include "MetricFile.h" #include "StructureEnum.h" #include "VolumeFile.h" #include #include #include using namespace caret; using namespace std; AString AlgorithmCiftiCreateLabel::getCommandSwitch() { return "-cifti-create-label"; } AString AlgorithmCiftiCreateLabel::getShortDescription() { return "CREATE A CIFTI LABEL FILE"; } OperationParameters* AlgorithmCiftiCreateLabel::getParameters() { OperationParameters* ret = new OperationParameters(); ret->addCiftiOutputParameter(1, "cifti-out", "the output cifti file"); OptionalParameter* volumeOpt = ret->createOptionalParameter(2, "-volume", "volume component"); volumeOpt->addVolumeParameter(1, "label-volume", "volume file containing the label data"); volumeOpt->addVolumeParameter(2, "parcel-volume", "label volume file with cifti structure names to define the volume parcels"); OptionalParameter* leftLabelOpt = ret->createOptionalParameter(3, "-left-label", "label file for left surface"); leftLabelOpt->addLabelParameter(1, "label", "the label file"); OptionalParameter* leftRoiOpt = leftLabelOpt->createOptionalParameter(2, "-roi-left", "roi of vertices to use from left surface"); leftRoiOpt->addMetricParameter(1, "roi-metric", "the ROI as a metric file"); OptionalParameter* rightLabelOpt = ret->createOptionalParameter(4, "-right-label", "label for left surface"); rightLabelOpt->addLabelParameter(1, "label", "the label file"); OptionalParameter* rightRoiOpt = rightLabelOpt->createOptionalParameter(2, "-roi-right", "roi of vertices to use from right surface"); rightRoiOpt->addMetricParameter(1, "roi-metric", "the ROI as a metric file"); OptionalParameter* cerebLabelOpt = ret->createOptionalParameter(5, "-cerebellum-label", "label for the cerebellum"); cerebLabelOpt->addLabelParameter(1, "label", "the label file"); OptionalParameter* cerebRoiOpt = cerebLabelOpt->createOptionalParameter(2, "-roi-cerebellum", "roi of vertices to use from right surface"); cerebRoiOpt->addMetricParameter(1, "roi-metric", "the ROI as a metric file"); AString myText = AString("All input files must have the same number of columns/subvolumes. Only the specified components will be in the output cifti. ") + "At least one component must be specified.\n\n" + "The -volume option of -cifti-create-label requires two volume arguments, " + "the label-volume argument contains all labels you want to display (e.g. nuclei of the thalamus), " + "whereas the parcel-volume argument includes all CIFTI structures you want to include data within (e.g. THALAMUS_LEFT, THALAMUS_RIGHT). " + "If you just want the labels in voxels to be the structure names, you may use the same file for both arguments. " + "The parcel-volume must use some of the label names from this list, all other label names in the parcel-volume will be ignored:\n"; vector myStructureEnums; StructureEnum::getAllEnums(myStructureEnums); for (int i = 0; i < (int)myStructureEnums.size(); ++i) { myText += "\n" + StructureEnum::toName(myStructureEnums[i]); } ret->setHelpText(myText); return ret; } void AlgorithmCiftiCreateLabel::useParameters(OperationParameters* myParams, ProgressObject* myProgObj) { VolumeFile* myVol = NULL, *myVolLabel = NULL; CiftiFile* myCiftiOut = myParams->getOutputCifti(1); OptionalParameter* volumeOpt = myParams->getOptionalParameter(2); if (volumeOpt->m_present) { myVol = volumeOpt->getVolume(1); myVolLabel = volumeOpt->getVolume(2); } LabelFile* leftData = NULL, *rightData = NULL, *cerebData = NULL; MetricFile* leftRoi = NULL, *rightRoi = NULL, *cerebRoi = NULL; OptionalParameter* leftLabelOpt = myParams->getOptionalParameter(3); if (leftLabelOpt->m_present) { leftData = leftLabelOpt->getLabel(1); OptionalParameter* leftRoiOpt = leftLabelOpt->getOptionalParameter(2); if (leftRoiOpt->m_present) { leftRoi = leftRoiOpt->getMetric(1); } } OptionalParameter* rightLabelOpt = myParams->getOptionalParameter(4); if (rightLabelOpt->m_present) { rightData = rightLabelOpt->getLabel(1); OptionalParameter* rightRoiOpt = rightLabelOpt->getOptionalParameter(2); if (rightRoiOpt->m_present) { rightRoi = rightRoiOpt->getMetric(1); } } OptionalParameter* cerebLabelOpt = myParams->getOptionalParameter(5); if (cerebLabelOpt->m_present) { cerebData = cerebLabelOpt->getLabel(1); OptionalParameter* cerebRoiOpt = cerebLabelOpt->getOptionalParameter(2); if (cerebRoiOpt->m_present) { cerebRoi = cerebRoiOpt->getMetric(1); } } AlgorithmCiftiCreateLabel(myProgObj, myCiftiOut, myVol, myVolLabel, leftData, leftRoi, rightData, rightRoi, cerebData, cerebRoi); } AlgorithmCiftiCreateLabel::AlgorithmCiftiCreateLabel(ProgressObject* myProgObj, CiftiFile* myCiftiOut, const VolumeFile* myVol, const VolumeFile* myVolLabel, const LabelFile* leftData, const MetricFile* leftRoi, const LabelFile* rightData, const MetricFile* rightRoi, const LabelFile* cerebData, const MetricFile* cerebRoi) : AbstractAlgorithm(myProgObj) { CaretAssert(myCiftiOut != NULL); LevelProgress myProgress(myProgObj); CiftiXMLOld myXML; myXML.resetColumnsToBrainModels(); int numMaps = -1; if (leftData != NULL) { numMaps = leftData->getNumberOfMaps(); if (leftRoi == NULL) { myXML.addSurfaceModelToColumns(leftData->getNumberOfNodes(), StructureEnum::CORTEX_LEFT); } else { if (leftRoi->getNumberOfNodes() != leftData->getNumberOfNodes()) { throw AlgorithmException("left surface ROI and data have different vertex counts"); } myXML.addSurfaceModelToColumns(leftData->getNumberOfNodes(), StructureEnum::CORTEX_LEFT, leftRoi->getValuePointerForColumn(0)); } } if (rightData != NULL) { if (numMaps == -1) { numMaps = rightData->getNumberOfMaps(); } else { if (numMaps != rightData->getNumberOfMaps()) { throw AlgorithmException("right and left surface data have a different number of maps"); } } if (rightRoi == NULL) { myXML.addSurfaceModelToColumns(rightData->getNumberOfNodes(), StructureEnum::CORTEX_RIGHT); } else { if (rightRoi->getNumberOfNodes() != rightData->getNumberOfNodes()) { throw AlgorithmException("right surface ROI and data have different vertex counts"); } myXML.addSurfaceModelToColumns(rightRoi->getNumberOfNodes(), StructureEnum::CORTEX_RIGHT, rightRoi->getValuePointerForColumn(0)); } } if (cerebData != NULL) { if (numMaps == -1) { numMaps = cerebData->getNumberOfMaps(); } else { if (numMaps != cerebData->getNumberOfMaps()) { throw AlgorithmException("cerebellum surface data has a different number of maps"); } } if (cerebRoi == NULL) { myXML.addSurfaceModelToColumns(cerebData->getNumberOfNodes(), StructureEnum::CEREBELLUM); } else { if (cerebRoi->getNumberOfNodes() != cerebData->getNumberOfNodes()) { throw AlgorithmException("cerebellum surface ROI and data have different vertex counts"); } myXML.addSurfaceModelToColumns(cerebRoi->getNumberOfNodes(), StructureEnum::CEREBELLUM, cerebRoi->getValuePointerForColumn(0)); } } if (myVol != NULL) { CaretAssert(myVolLabel != NULL); if (myVol->getType() != SubvolumeAttributes::LABEL) { throw AlgorithmException("input volume is not a label volume"); } if (!myVol->matchesVolumeSpace(myVolLabel)) { throw AlgorithmException("label volume has a different volume space than data volume"); } if (myVolLabel->getType() != SubvolumeAttributes::LABEL) { throw AlgorithmException("parcel volume is not a label volume"); } if (numMaps == -1) { numMaps = myVol->getNumberOfMaps(); } else { if (numMaps != myVol->getNumberOfMaps()) { throw AlgorithmException("volume data has a different number of maps"); } } map labelMap;//maps label values to structures vector > voxelLists;//voxel lists for each volume component map componentMap;//maps structures to indexes in voxelLists const GiftiLabelTable* myLabelTable = myVolLabel->getMapLabelTable(0); vector labelKeys; myLabelTable->getKeys(labelKeys); int count = 0; for (int i = 0; i < (int)labelKeys.size(); ++i) { bool ok = false; StructureEnum::Enum thisStructure = StructureEnum::fromName(myLabelTable->getLabelName(labelKeys[i]), &ok); if (ok) { if (componentMap.find(thisStructure) == componentMap.end())//make sure we don't already have this structure from another label { labelMap[labelKeys[i]] = thisStructure; componentMap[thisStructure] = count; ++count; } } } voxelLists.resize(count); vector mydims; myVolLabel->getDimensions(mydims); for (int64_t k = 0; k < mydims[2]; ++k) { for (int64_t j = 0; j < mydims[1]; ++j) { for (int64_t i = 0; i < mydims[0]; ++i) { int myval = (int)floor(myVolLabel->getValue(i, j, k) + 0.5f); map::iterator myiter = labelMap.find(myval); if (myiter != labelMap.end()) { int whichList = componentMap.find(myiter->second)->second;//this should always find one, so we don't need to check for being end voxelLists[whichList].push_back(i); voxelLists[whichList].push_back(j); voxelLists[whichList].push_back(k); } } } } int64_t ciftiVolDims[3]; ciftiVolDims[0] = mydims[0]; ciftiVolDims[1] = mydims[1]; ciftiVolDims[2] = mydims[2]; myXML.setVolumeDimsAndSForm(ciftiVolDims, myVol->getSform()); for (map::iterator myiter = componentMap.begin(); myiter != componentMap.end(); ++myiter) { myXML.addVolumeModelToColumns(voxelLists[myiter->second], myiter->first); } } if (numMaps == -1) { throw AlgorithmException("no models specified"); } myXML.resetRowsToLabels(numMaps); vector > surfLeftConvert(numMaps), surfRightConvert(numMaps), surfCerebConvert(numMaps), volConvert(numMaps);//surfLeftConvert could just be a set, but for consistency... for (int i = 0; i < numMaps; ++i) { GiftiLabelTable mapTable;//NOTE: this relies on GiftiLabelTable::append doing the right thing bool first = true; if (leftData != NULL) { surfLeftConvert[i] = mapTable.append(*(leftData->getMapLabelTable(i)));//in case label files ever move to one table per map if (first) { first = false; myXML.setMapNameForRowIndex(i, leftData->getColumnName(i)); } } if (rightData != NULL) { surfRightConvert[i] = mapTable.append(*(rightData->getMapLabelTable(i))); if (first) { first = false; myXML.setMapNameForRowIndex(i, rightData->getColumnName(i)); } } if (cerebData != NULL) { surfCerebConvert[i] = mapTable.append(*(cerebData->getMapLabelTable(i))); if (first) { first = false; myXML.setMapNameForRowIndex(i, cerebData->getColumnName(i)); } } if (myVol != NULL) { volConvert[i] = mapTable.append(*(myVol->getMapLabelTable(i))); if (first) { first = false; myXML.setMapNameForRowIndex(i, myVol->getMapName(i)); } } myXML.setLabelTableForRowIndex(i, mapTable); } myCiftiOut->setCiftiXML(myXML); CaretArray temprow(numMaps); vector surfMap; if (myXML.getSurfaceMapForColumns(surfMap, StructureEnum::CORTEX_LEFT)) { for (int64_t i = 0; i < (int)surfMap.size(); ++i) { for (int t = 0; t < numMaps; ++t) { map::iterator iter = surfLeftConvert[t].find(leftData->getLabelKey(surfMap[i].m_surfaceNode, t)); if (iter == surfLeftConvert[t].end()) { temprow[t] = 0;//NOTE: this relies on 0 being the unused label, from the default constructor of the label table, and not being overwritten by append } else { temprow[t] = iter->second; } } myCiftiOut->setRow(temprow, surfMap[i].m_ciftiIndex); } } if (myXML.getSurfaceMapForColumns(surfMap, StructureEnum::CORTEX_RIGHT)) { for (int64_t i = 0; i < (int)surfMap.size(); ++i) { for (int t = 0; t < numMaps; ++t) { map::iterator iter = surfRightConvert[t].find(rightData->getLabelKey(surfMap[i].m_surfaceNode, t)); if (iter == surfRightConvert[t].end()) { temprow[t] = 0; } else { temprow[t] = iter->second; } } myCiftiOut->setRow(temprow, surfMap[i].m_ciftiIndex); } } if (myXML.getSurfaceMapForColumns(surfMap, StructureEnum::CEREBELLUM)) { for (int64_t i = 0; i < (int)surfMap.size(); ++i) { for (int t = 0; t < numMaps; ++t) { map::iterator iter = surfCerebConvert[t].find(cerebData->getLabelKey(surfMap[i].m_surfaceNode, t)); if (iter == surfCerebConvert[t].end()) { temprow[t] = 0; } else { temprow[t] = iter->second; } } myCiftiOut->setRow(temprow, surfMap[i].m_ciftiIndex); } } vector volMap; if (myXML.getVolumeMapForColumns(volMap))//we don't need to know which voxel is from which parcel { for (int64_t i = 0; i < (int)volMap.size(); ++i) { for (int t = 0; t < numMaps; ++t) { map::iterator iter = volConvert[t].find(myVol->getValue(volMap[i].m_ijk, t)); if (iter == volConvert[t].end()) { temprow[t] = 0; } else { temprow[t] = iter->second; } } myCiftiOut->setRow(temprow, volMap[i].m_ciftiIndex); } } } float AlgorithmCiftiCreateLabel::getAlgorithmInternalWeight() { return 1.0f;//override this if needed, if the progress bar isn't smooth } float AlgorithmCiftiCreateLabel::getSubAlgorithmWeight() { return 0.0f; } connectome-workbench-1.2.3+git41-gc4c6c90/src/Algorithms/AlgorithmCiftiCreateLabel.h000066400000000000000000000037211300200146000300220ustar00rootroot00000000000000#ifndef __ALGORITHM_CIFTI_CREATE_LABEL_H__ #define __ALGORITHM_CIFTI_CREATE_LABEL_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 "AbstractAlgorithm.h" namespace caret { class AlgorithmCiftiCreateLabel : public AbstractAlgorithm { AlgorithmCiftiCreateLabel(); protected: static float getSubAlgorithmWeight(); static float getAlgorithmInternalWeight(); public: AlgorithmCiftiCreateLabel(ProgressObject* myProgObj, CiftiFile* myCiftiOut, const VolumeFile* myVol, const VolumeFile* myVolLabel, const LabelFile* leftData, const MetricFile* leftRoi, const LabelFile* rightData, const MetricFile* rightRoi, const LabelFile* cerebData, const MetricFile* cerebRoi); static OperationParameters* getParameters(); static void useParameters(OperationParameters* myParams, ProgressObject* myProgObj); static AString getCommandSwitch(); static AString getShortDescription(); }; typedef TemplateAutoOperation AutoAlgorithmCiftiCreateLabel; } #endif //__ALGORITHM_CIFTI_CREATE_LABEL_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Algorithms/AlgorithmCiftiCrossCorrelation.cxx000066400000000000000000000404511300200146000315260ustar00rootroot00000000000000/*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 "AlgorithmCiftiCrossCorrelation.h" #include "AlgorithmException.h" #include "CaretAssert.h" #include "CaretLogger.h" #include "CaretOMP.h" #include "CiftiFile.h" #include "dot_wrapper.h" #include "FileInformation.h" #include #include using namespace caret; using namespace std; AString AlgorithmCiftiCrossCorrelation::getCommandSwitch() { return "-cifti-cross-correlation"; } AString AlgorithmCiftiCrossCorrelation::getShortDescription() { return "CORRELATE A CIFTI FILE WITH ANOTHER CIFTI FILE"; } OperationParameters* AlgorithmCiftiCrossCorrelation::getParameters() { OperationParameters* ret = new OperationParameters(); ret->addCiftiParameter(1, "cifti-a", "first input cifti file"); ret->addCiftiParameter(2, "cifti-b", "second input cifti file"); ret->addCiftiOutputParameter(3, "cifti-out", "output cifti file"); OptionalParameter* weightsOpt = ret->createOptionalParameter(4, "-weights", "specify column weights"); weightsOpt->addStringParameter(1, "weight-file", "text file containing one weight per column"); ret->createOptionalParameter(5, "-fisher-z", "apply fisher small z transform (ie, artanh) to correlation"); OptionalParameter* memLimitOpt = ret->createOptionalParameter(6, "-mem-limit", "restrict memory usage"); memLimitOpt->addDoubleParameter(1, "limit-GB", "memory limit in gigabytes"); ret->setHelpText( AString("Correlates every row in with every row in . ") + "The mapping along columns in becomes the mapping along rows in the output.\n\n" + "When using the -fisher-z option, the output is NOT a Z-score, it is artanh(r), to do further math on this output, consider using -cifti-math.\n\n" + "Restricting the memory usage will make it calculate the output in chunks, by reading through multiple times." ); return ret; } void AlgorithmCiftiCrossCorrelation::useParameters(OperationParameters* myParams, ProgressObject* myProgObj) { CiftiFile* myCiftiA = myParams->getCifti(1); CiftiFile* myCiftiB = myParams->getCifti(2); CiftiFile* myCiftiOut = myParams->getOutputCifti(3); OptionalParameter* weightsOpt = myParams->getOptionalParameter(4); vector* weights = NULL, realweights;//NOTE: realweights is not a pointer if (weightsOpt->m_present) { weights = &realweights;//point it to the actual vector to signify the option is present AString weightFileName = weightsOpt->getString(1); FileInformation textFileInfo(weightFileName); if (!textFileInfo.exists()) { throw AlgorithmException("weight list file doesn't exist"); } fstream weightListFile(weightFileName.toLocal8Bit().constData(), fstream::in); if (!weightListFile.good()) { throw AlgorithmException("error reading weight list file"); } while (weightListFile.good()) { float weight; if (!(weightListFile >> weight))//yes, this is how you check fstream for successfully extracted output. seriously. { break; } realweights.push_back(weight); } } bool fisherZ = myParams->getOptionalParameter(5)->m_present; float memLimitGB = -1.0f; OptionalParameter* memLimitOpt = myParams->getOptionalParameter(6); if (memLimitOpt->m_present) { memLimitGB = (float)memLimitOpt->getDouble(1); if (memLimitGB < 0.0f) { throw AlgorithmException("memory limit cannot be negative"); } } AlgorithmCiftiCrossCorrelation(myProgObj, myCiftiA, myCiftiB, myCiftiOut, weights, fisherZ, memLimitGB); } AlgorithmCiftiCrossCorrelation::AlgorithmCiftiCrossCorrelation(ProgressObject* myProgObj, const CiftiFile* myCiftiA, const CiftiFile* myCiftiB, CiftiFile* myCiftiOut, const vector* weights, const bool& fisherZ, const float& memLimitGB) : AbstractAlgorithm(myProgObj) { LevelProgress myProgress(myProgObj); init(myCiftiA, myCiftiB, myCiftiOut, weights); CiftiXMLOld outXML = myCiftiA->getCiftiXMLOld(); outXML.copyMapping(CiftiXMLOld::ALONG_ROW, myCiftiB->getCiftiXMLOld(), CiftiXMLOld::ALONG_COLUMN);//(try to) copy B's along column mapping to output's along row mapping myCiftiOut->setCiftiXML(outXML); int64_t chunkSize = m_numRowsA; if (memLimitGB >= 0.0f) { chunkSize = numRowsForMem(memLimitGB); } vector > outscratch(chunkSize, vector(m_numRowsB));//allocate output rows for (int64_t chunkStart = 0; chunkStart < m_numRowsA; chunkStart += chunkSize) { int64_t chunkEnd = chunkStart + chunkSize; if (chunkEnd > m_numRowsA) chunkEnd = m_numRowsA; cacheRowsA(chunkStart, chunkEnd); int64_t counter = 0; #pragma omp CARET_PARFOR schedule(dynamic) for (int64_t i = 0; i < m_numRowsB; ++i) { float rrsB; int64_t indB; const float* rowB; #pragma omp critical { indB = counter;//manually in-order rows because we need to read them from disk, and can't request more than one at a time ++counter; rowB = getRowB(indB, rrsB); } for (int indA = chunkStart; indA < chunkEnd; ++indA) { float rrsA; const float* rowA = getCachedRowA(indA, rrsA); outscratch[indA - chunkStart][indB] = correlate(rowA, rrsA, rowB, rrsB, fisherZ); } } for (int64_t indA = chunkStart; indA < chunkEnd; ++indA) { myCiftiOut->setRow(outscratch[indA - chunkStart].data(), indA); } } } void AlgorithmCiftiCrossCorrelation::init(const CiftiFile* myCiftiA, const CiftiFile* myCiftiB, const CiftiFile* myCiftiOut, const vector* weights) { m_numCols = myCiftiA->getNumberOfColumns(); if (myCiftiB->getNumberOfColumns() != m_numCols) throw AlgorithmException("input cifti files have different row lengths"); m_numRowsA = myCiftiA->getNumberOfRows(); m_numRowsB = myCiftiB->getNumberOfRows(); m_ciftiA = myCiftiA; m_ciftiB = myCiftiB; m_ciftiOut = myCiftiOut; m_rowInfoA.resize(m_numRowsA);//calls default constructors, setting m_haveCalculated and m_cacheIndex m_rowInfoB.resize(m_numRowsB); if (weights != NULL) { m_weightSum = 0.0; m_weightedMode = true; int64_t numWeights = (int64_t)weights->size(); if (numWeights != m_numCols) throw AlgorithmException("input weights do not match row length"); m_binaryWeights = true; for (int i = 0; i < numWeights; ++i) { float val = (*weights)[i]; if (val != 0.0f) { if (val < 0.0f) { throw AlgorithmException("weights cannot be negative"); } m_weightSum += val; m_weights.push_back(val); m_weightIndexes.push_back(i); if (val != 1.0f) { m_binaryWeights = false; } } } if (m_binaryWeights && m_weights.size() == weights->size()) { m_weightedMode = false;//all weights were 1, so switch back to normal mode } } else { m_weightedMode = false; } } int64_t AlgorithmCiftiCrossCorrelation::numRowsForMem(const float& memLimitGB) { int64_t targetBytes = (int64_t)(memLimitGB * 1024 * 1024 * 1024); if (m_ciftiOut->isInMemory()) targetBytes -= sizeof(float) * m_numRowsA * m_numRowsB;//count only in-memory output against total, the only time inputs might be in memory is in the GUI int64_t bytesPerInputRow = sizeof(float) * m_numCols;//this means we expect the user to give "current free memory" as the limit int64_t bytesPerOutputRow = sizeof(float) * m_numRowsB; #ifdef CARET_OMP targetBytes -= bytesPerInputRow * omp_get_max_threads();//subtract the temporary row memory #else targetBytes -= bytesPerInputRow; #endif int64_t ret = 1; if (targetBytes < 1) { ret = 1;//assume the user knows what they are doing when they request minimum memory usage, even if we do yell at them } else { int64_t numRowsMax = targetBytes / (bytesPerInputRow + bytesPerOutputRow);//calculate max that can fit in given memory if (numRowsMax < 1) numRowsMax = 1; int64_t numPasses = (m_numRowsA - 1) / numRowsMax + 1;//figure the number of passes that makes if (numPasses < 1) { CaretLogWarning("memory usage calculation found zero/negative pass solution, report to developers (it may use a lot of memory this run)"); numPasses = 1; } ret = (m_numRowsA - 1) / numPasses + 1;//and then figure the most even distribution for that number of passes } if (ret == 1) { if (!m_ciftiA->isInMemory() || !m_ciftiB->isInMemory()) { CaretLogWarning("requested memory usage is too low, and at least one input is not in memory, using only 1 row at a time - this may be extremely slow"); } } return ret; } float AlgorithmCiftiCrossCorrelation::correlate(const float* row1, const float& rrs1, const float* row2, const float& rrs2, const bool& fisherZ) { double r; 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 r = accum / (rrs1 * rrs2);//as do these } else { double accum = sddot(row1, row2, m_numCols);//these have already had the row means subtracted out r = accum / (rrs1 * rrs2); } if (fisherZ) { if (r > 0.999999) r = 0.999999;//prevent inf if (r < -0.999999) r = -0.999999;//prevent -inf return 0.5 * log((1 + r) / (1 - r)); } else { if (r > 1.0) r = 1.0;//don't output anything silly if (r < -1.0) r = -1.0; return r; } } const float* AlgorithmCiftiCrossCorrelation::getCachedRowA(const int64_t& ciftiIndex, float& rootResidSqr) { CaretAssertVectorIndex(m_rowInfoA, ciftiIndex); CaretAssert(m_rowInfoA[ciftiIndex].m_cacheIndex != -1); rootResidSqr = m_rowInfoA[ciftiIndex].m_rootResidSqr; return m_rowCacheA[m_rowInfoA[ciftiIndex].m_cacheIndex].m_row.data(); } const float* AlgorithmCiftiCrossCorrelation::getRowB(const int64_t& ciftiIndex, float& rootResidSqr) { CaretAssertVectorIndex(m_rowInfoB, ciftiIndex); float* ret = getTempRowB();//purely allocation, one array per thread m_ciftiB->getRow(ret, ciftiIndex); adjustRow(ret, m_rowInfoB[ciftiIndex]); rootResidSqr = m_rowInfoB[ciftiIndex].m_rootResidSqr;//NOTE: must do this AFTER adjustRow, because it is not computed before it on the first chunk return ret; } float* AlgorithmCiftiCrossCorrelation::getTempRowB() { #ifdef CARET_OMP int oldsize = (int)m_tempRowsB.size(); int threadNum = omp_get_thread_num(); if (threadNum >= oldsize) { m_tempRowsB.resize(threadNum + 1); for (int i = oldsize; i <= threadNum; ++i) { m_tempRowsB[i] = CaretArray(m_numCols); } } return m_tempRowsB[threadNum].getArray(); #else if (m_tempRowsB.size() == 0) { m_tempRowsB.resize(1); m_tempRowsB[0] = CaretArray(m_numCols); } return m_tempRowsB[0].getArray(); #endif } void AlgorithmCiftiCrossCorrelation::cacheRowsA(const int64_t& begin, const int64_t& end) { CaretAssert(begin > -1); CaretAssert(end <= m_numRowsA); CaretAssert(begin < end);//takes care of end <= 0 and being >= numrows int64_t cacheSize = (int64_t)m_rowCacheA.size();//clear the existing cache references for (int64_t i = 0; i < cacheSize; ++i) { m_rowInfoA[m_rowCacheA[i].m_ciftiIndex].m_cacheIndex = -1; } m_rowCacheA.resize(end - begin);//set to exactly the size needed int64_t counter = begin;//force in-order row reading via critical and counter #pragma omp CARET_PARFOR schedule(dynamic) for (int64_t i = begin; i < end; ++i) { int64_t myindex; #pragma omp critical { myindex = counter; ++counter; m_rowCacheA[myindex - begin].m_row.resize(m_numCols); m_ciftiA->getRow(m_rowCacheA[myindex - begin].m_row.data(), myindex); } CacheRow& myRow = m_rowCacheA[myindex - begin]; myRow.m_ciftiIndex = myindex; m_rowInfoA[myindex].m_cacheIndex = myindex - begin; adjustRow(myRow.m_row.data(), m_rowInfoA[myindex]); } } void AlgorithmCiftiCrossCorrelation::adjustRow(float* row, RowInfo& info) { if (!info.m_haveCalculated)//ensure statistics are calculated { info.m_haveCalculated = true; double accum = 0.0; if (m_weightedMode) { int64_t mycount = (int64_t)m_weightIndexes.size(); if (m_binaryWeights) { for (int64_t i = 0; i < mycount; ++i) { accum += row[m_weightIndexes[i]]; } info.m_mean = accum / mycount; accum = 0.0; for (int64_t i = 0; i < mycount; ++i) { float tempf = row[m_weightIndexes[i]] - info.m_mean; accum += tempf * tempf; } info.m_rootResidSqr = sqrt(accum); } else { for (int64_t i = 0; i < mycount; ++i) { accum += m_weights[i] * row[m_weightIndexes[i]]; } info.m_mean = accum / m_weightSum; accum = 0.0; for (int64_t i = 0; i < mycount; ++i) { float tempf = row[m_weightIndexes[i]] - info.m_mean; accum += m_weights[i] * tempf * tempf; } info.m_rootResidSqr = sqrt(accum); } } else { for (int64_t i = 0; i < m_numCols; ++i) { accum += row[i]; } info.m_mean = accum / m_numCols; accum = 0.0; for (int64_t i = 0; i < m_numCols; ++i) { float tempf = row[i] - info.m_mean; accum += tempf * tempf; } info.m_rootResidSqr = sqrt(accum); } } if (m_weightedMode)//COMPACT data, subtract mean, multiply by square root of weights if applicable { int64_t mycount = (int64_t)m_weightIndexes.size(); if (m_binaryWeights) { for (int64_t i = 0; i < mycount; ++i) { row[i] = row[m_weightIndexes[i]] - info.m_mean; } } else { for (int64_t i = 0; i < mycount; ++i) { row[i] = sqrt(m_weights[i]) * (row[m_weightIndexes[i]] - info.m_mean);//this is so the numerator doesn't get squared weights applied, since this happens to both rows } } } else { for (int64_t i = 0; i < m_numCols; ++i) { row[i] -= info.m_mean; } } } float AlgorithmCiftiCrossCorrelation::getAlgorithmInternalWeight() { return 1.0f;//override this if needed, if the progress bar isn't smooth } float AlgorithmCiftiCrossCorrelation::getSubAlgorithmWeight() { //return AlgorithmInsertNameHere::getAlgorithmWeight();//if you use a subalgorithm return 0.0f; } connectome-workbench-1.2.3+git41-gc4c6c90/src/Algorithms/AlgorithmCiftiCrossCorrelation.h000066400000000000000000000072261300200146000311560ustar00rootroot00000000000000#ifndef __ALGORITHM_CIFTI_CROSS_CORRELATION_H__ #define __ALGORITHM_CIFTI_CROSS_CORRELATION_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 "AbstractAlgorithm.h" #include "CaretPointer.h" #include namespace caret { class AlgorithmCiftiCrossCorrelation : public AbstractAlgorithm { struct CacheRow { int m_ciftiIndex; std::vector m_row; }; struct RowInfo { bool m_haveCalculated; float m_mean, m_rootResidSqr; int m_cacheIndex; RowInfo() { m_haveCalculated = false; m_cacheIndex = -1; } }; int64_t m_numCols, m_numRowsA, m_numRowsB; const CiftiFile* m_ciftiA, *m_ciftiB, *m_ciftiOut;//output is really only to check if it is in-memory for numRowsForMem std::vector m_rowCacheA;//we only cache from cifti A std::vector m_rowInfoA, m_rowInfoB; std::vector > m_tempRowsB;//reuse return values in getRowB instead of reallocating std::vector m_weights; std::vector m_weightIndexes; bool m_binaryWeights, m_weightedMode; double m_weightSum; AlgorithmCiftiCrossCorrelation(); void init(const CiftiFile* myCiftiA, const CiftiFile* myCiftiB, const CiftiFile* myCiftiOut, const std::vector* weights); int64_t numRowsForMem(const float& memLimitGB);//call after init() float* getTempRowB();//only used for getRowB, A rows are pulled from cache const float* getCachedRowA(const int64_t& ciftiIndex, float& rootResidSqr);//retrieve already cached rows const float* getRowB(const int64_t& ciftiIndex, float& rootResidSqr); void adjustRow(float* row, RowInfo& info); float correlate(const float* row1, const float& rrs1, const float* row2, const float& rrs2, const bool& fisherZ); void cacheRowsA(const int64_t& begin, const int64_t& end);//grabs the rows and does whatever it needs to, using as much IO bandwidth and CPU resources as available/needed protected: static float getSubAlgorithmWeight(); static float getAlgorithmInternalWeight(); public: AlgorithmCiftiCrossCorrelation(ProgressObject* myProgObj, const CiftiFile* myCiftiA, const CiftiFile* myCiftiB, CiftiFile* myCiftiOut, const std::vector* weights, const bool& fisherZ, const float& memLimitGB); static OperationParameters* getParameters(); static void useParameters(OperationParameters* myParams, ProgressObject* myProgObj); static AString getCommandSwitch(); static AString getShortDescription(); }; typedef TemplateAutoOperation AutoAlgorithmCiftiCrossCorrelation; } #endif //__ALGORITHM_CIFTI_CROSS_CORRELATION_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Algorithms/AlgorithmCiftiDilate.cxx000066400000000000000000000345561300200146000274460ustar00rootroot00000000000000/*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 "AlgorithmCiftiDilate.h" #include "AlgorithmException.h" #include "AlgorithmCiftiReplaceStructure.h" #include "AlgorithmCiftiSeparate.h" #include "AlgorithmLabelDilate.h" #include "AlgorithmMetricDilate.h" #include "AlgorithmVolumeDilate.h" #include "CiftiFile.h" #include "LabelFile.h" #include "MetricFile.h" #include "SurfaceFile.h" #include "VolumeFile.h" using namespace caret; using namespace std; AString AlgorithmCiftiDilate::getCommandSwitch() { return "-cifti-dilate"; } AString AlgorithmCiftiDilate::getShortDescription() { return "DILATE A CIFTI FILE"; } OperationParameters* AlgorithmCiftiDilate::getParameters() { OperationParameters* ret = new OperationParameters(); ret->addCiftiParameter(1, "cifti-in", "the input cifti file"); ret->addStringParameter(2, "direction", "which dimension to dilate along, ROW or COLUMN"); ret->addDoubleParameter(3, "surface-distance", "the distance to dilate on surfaces, in mm"); ret->addDoubleParameter(4, "volume-distance", "the distance to dilate in the volume, in mm"); ret->addCiftiOutputParameter(5, "cifti-out", "the output cifti file"); OptionalParameter* leftSurfOpt = ret->createOptionalParameter(6, "-left-surface", "specify the left surface to use"); leftSurfOpt->addSurfaceParameter(1, "surface", "the left surface file"); OptionalParameter* leftCorrAreasOpt = leftSurfOpt->createOptionalParameter(2, "-left-corrected-areas", "vertex areas to use instead of computing them from the left surface"); leftCorrAreasOpt->addMetricParameter(1, "area-metric", "the corrected vertex areas, as a metric"); OptionalParameter* rightSurfOpt = ret->createOptionalParameter(7, "-right-surface", "specify the right surface to use"); rightSurfOpt->addSurfaceParameter(1, "surface", "the right surface file"); OptionalParameter* rightCorrAreasOpt = rightSurfOpt->createOptionalParameter(2, "-right-corrected-areas", "vertex areas to use instead of computing them from the right surface"); rightCorrAreasOpt->addMetricParameter(1, "area-metric", "the corrected vertex areas, as a metric"); OptionalParameter* cerebSurfOpt = ret->createOptionalParameter(8, "-cerebellum-surface", "specify the cerebellum surface to use"); cerebSurfOpt->addSurfaceParameter(1, "surface", "the cerebellum surface file"); OptionalParameter* cerebCorrAreasOpt = cerebSurfOpt->createOptionalParameter(2, "-cerebellum-corrected-areas", "vertex areas to use instead of computing them from the cerebellum surface"); cerebCorrAreasOpt->addMetricParameter(1, "area-metric", "the corrected vertex areas, as a metric"); OptionalParameter* roiOpt = ret->createOptionalParameter(9, "-bad-brainordinate-roi", "specify an roi of brainordinates to overwrite, rather than zeros"); roiOpt->addCiftiParameter(1, "roi-cifti", "cifti dscalar or dtseries file, positive values denote brainordinates to have their values replaced"); ret->createOptionalParameter(10, "-nearest", "use nearest value"); ret->createOptionalParameter(11, "-merged-volume", "treat volume components as if they were a single component"); ret->setHelpText( AString("For all data values designated as bad, if they neighbor a good value or are within the specified distance of a good value in the same kind of model, ") + "replace the value with a distance weighted average of nearby good values, otherwise set the value to zero. " + "If -nearest is specified, it will use the value from the closest good value within range instead of a weighted average. " + "When the input file contains label data, nearest dilation is used on the surface, and weighted popularity is used in the volume.\n\n" + "The -*-corrected-areas options are intended for dilating on group average surfaces, but it is only an approximate correction " + "for the reduction of structure in a group average surface.\n\n" + "If -bad-brainordinate-roi is specified, all values, including those with value zero, are good, except for locations with a positive value in the ROI. " + "If it is not specified, only values equal to zero are bad." ); return ret; } void AlgorithmCiftiDilate::useParameters(OperationParameters* myParams, ProgressObject* myProgObj) { CiftiFile* myCifti = myParams->getCifti(1); AString directionName = myParams->getString(2); int myDir; if (directionName == "ROW") { myDir = CiftiXMLOld::ALONG_ROW; } else if (directionName == "COLUMN") { myDir = CiftiXMLOld::ALONG_COLUMN; } else { throw AlgorithmException("incorrect string for direction, use ROW or COLUMN"); } float surfDist = (float)myParams->getDouble(3); float volDist = (float)myParams->getDouble(4); CiftiFile* myCiftiOut = myParams->getOutputCifti(5); SurfaceFile* myLeftSurf = NULL, *myRightSurf = NULL, *myCerebSurf = NULL; MetricFile* myLeftAreas = NULL, *myRightAreas = NULL, *myCerebAreas = NULL; OptionalParameter* leftSurfOpt = myParams->getOptionalParameter(6); if (leftSurfOpt->m_present) { myLeftSurf = leftSurfOpt->getSurface(1); OptionalParameter* leftCorrAreasOpt = leftSurfOpt->getOptionalParameter(2); if (leftCorrAreasOpt->m_present) { myLeftAreas = leftCorrAreasOpt->getMetric(1); } } OptionalParameter* rightSurfOpt = myParams->getOptionalParameter(7); if (rightSurfOpt->m_present) { myRightSurf = rightSurfOpt->getSurface(1); OptionalParameter* rightCorrAreasOpt = rightSurfOpt->getOptionalParameter(2); if (rightCorrAreasOpt->m_present) { myRightAreas = rightCorrAreasOpt->getMetric(1); } } OptionalParameter* cerebSurfOpt = myParams->getOptionalParameter(8); if (cerebSurfOpt->m_present) { myCerebSurf = cerebSurfOpt->getSurface(1); OptionalParameter* cerebCorrAreasOpt = cerebSurfOpt->getOptionalParameter(2); if (cerebCorrAreasOpt->m_present) { myCerebAreas = cerebCorrAreasOpt->getMetric(1); } } CiftiFile* myRoi = NULL; OptionalParameter* roiOpt = myParams->getOptionalParameter(9); if (roiOpt->m_present) { myRoi = roiOpt->getCifti(1); } bool nearest = myParams->getOptionalParameter(10)->m_present; bool mergedVolume = myParams->getOptionalParameter(11)->m_present; AlgorithmCiftiDilate(myProgObj, myCifti, myDir, surfDist, volDist, myCiftiOut, myLeftSurf, myRightSurf, myCerebSurf, myLeftAreas, myRightAreas, myCerebAreas, myRoi, nearest, mergedVolume); } AlgorithmCiftiDilate::AlgorithmCiftiDilate(ProgressObject* myProgObj, const CiftiFile* myCifti, const int& myDir, const float& surfDist, const float& volDist, CiftiFile* myCiftiOut, const SurfaceFile* myLeftSurf, const SurfaceFile* myRightSurf, const SurfaceFile* myCerebSurf, const MetricFile* myLeftAreas, const MetricFile* myRightAreas, const MetricFile* myCerebAreas, const CiftiFile* myBadRoi, const bool& nearest, const bool& mergedVolume) : AbstractAlgorithm(myProgObj) { LevelProgress myProgress(myProgObj); CiftiXMLOld myXML = myCifti->getCiftiXMLOld(); vector surfaceList, volumeList; if (myDir != CiftiXMLOld::ALONG_ROW && myDir != CiftiXMLOld::ALONG_COLUMN) throw AlgorithmException("direction not supported by cifti dilate"); if (myBadRoi != NULL && !myXML.mappingMatches(myDir, myBadRoi->getCiftiXMLOld(), CiftiXMLOld::ALONG_COLUMN)) throw AlgorithmException("bad brainordinate roi has different brainordinate space than input"); if (!myXML.getStructureLists(myDir, surfaceList, volumeList)) { throw AlgorithmException("specified direction does not contain brainordinates"); } for (int whichStruct = 0; whichStruct < (int)surfaceList.size(); ++whichStruct) {//sanity check surfaces const SurfaceFile* mySurf = NULL; const MetricFile* myCorrAreas = NULL; AString surfType; switch (surfaceList[whichStruct]) { case StructureEnum::CORTEX_LEFT: mySurf = myLeftSurf; myCorrAreas = myLeftAreas; surfType = "left"; break; case StructureEnum::CORTEX_RIGHT: mySurf = myRightSurf; myCorrAreas = myRightAreas; surfType = "right"; break; case StructureEnum::CEREBELLUM: mySurf = myCerebSurf; myCorrAreas = myCerebAreas; surfType = "cerebellum"; break; default: throw AlgorithmException("found surface model with incorrect type: " + StructureEnum::toName(surfaceList[whichStruct])); break; } if (mySurf == NULL) { throw AlgorithmException(surfType + " surface required but not provided"); } if (mySurf->getNumberOfNodes() != myXML.getSurfaceNumberOfNodes(myDir, surfaceList[whichStruct])) { throw AlgorithmException(surfType + " surface has the wrong number of vertices"); } if (myCorrAreas != NULL && myCorrAreas->getNumberOfNodes() != mySurf->getNumberOfNodes()) { throw AlgorithmException(surfType + " surface and corrected areas metric have different numbers of vertices"); } } myCiftiOut->setCiftiXML(myXML); for (int whichStruct = 0; whichStruct < (int)surfaceList.size(); ++whichStruct) { const SurfaceFile* mySurf = NULL; const MetricFile* myCorrAreas = NULL; switch (surfaceList[whichStruct]) { case StructureEnum::CORTEX_LEFT: mySurf = myLeftSurf; myCorrAreas = myLeftAreas; break; case StructureEnum::CORTEX_RIGHT: mySurf = myRightSurf; myCorrAreas = myRightAreas; break; case StructureEnum::CEREBELLUM: mySurf = myCerebSurf; myCorrAreas = myCerebAreas; break; default: break; } MetricFile badRoiMetric, dataRoiMetric; MetricFile* badRoiPtr = NULL; if (myBadRoi != NULL) { AlgorithmCiftiSeparate(NULL, myBadRoi, CiftiXMLOld::ALONG_COLUMN, surfaceList[whichStruct], &badRoiMetric); badRoiPtr = &badRoiMetric; } if (myXML.getMappingType(1 - myDir) == CIFTI_INDEX_TYPE_LABELS) { LabelFile myLabel, myLabelOut; AlgorithmCiftiSeparate(NULL, myCifti, myDir, surfaceList[whichStruct], &myLabel); AlgorithmLabelDilate(NULL, &myLabel, mySurf, surfDist, &myLabelOut, badRoiPtr, -1, myCorrAreas); AlgorithmCiftiReplaceStructure(NULL, myCiftiOut, myDir, surfaceList[whichStruct], &myLabelOut); } else { MetricFile myMetric, myMetricOut; AlgorithmMetricDilate::Method myMethod = AlgorithmMetricDilate::WEIGHTED; if (nearest) myMethod = AlgorithmMetricDilate::NEAREST; AlgorithmCiftiSeparate(NULL, myCifti, myDir, surfaceList[whichStruct], &myMetric, &dataRoiMetric); AlgorithmMetricDilate(NULL, &myMetric, mySurf, surfDist, &myMetricOut, badRoiPtr, &dataRoiMetric, -1, myMethod, 2.0f, myCorrAreas); AlgorithmCiftiReplaceStructure(NULL, myCiftiOut, myDir, surfaceList[whichStruct], &myMetricOut); } } if (mergedVolume) { if (myXML.hasVolumeData(myDir)) { VolumeFile myVol, roiVol, myVolOut; VolumeFile* roiPtr = NULL; int64_t offset[3]; AlgorithmVolumeDilate::Method myMethod = AlgorithmVolumeDilate::WEIGHTED; if (nearest) { myMethod = AlgorithmVolumeDilate::NEAREST; } if (myBadRoi != NULL) { AlgorithmCiftiSeparate(NULL, myBadRoi, CiftiXMLOld::ALONG_COLUMN, &roiVol, offset, NULL, true); roiPtr = &roiVol; } AlgorithmCiftiSeparate(NULL, myCifti, myDir, &myVol, offset, NULL, true); AlgorithmVolumeDilate(NULL, &myVol, volDist, myMethod, &myVolOut, roiPtr); AlgorithmCiftiReplaceStructure(NULL, myCiftiOut, myDir, &myVolOut, true); } } else { AlgorithmVolumeDilate::Method myMethod = AlgorithmVolumeDilate::WEIGHTED; if (nearest) { myMethod = AlgorithmVolumeDilate::NEAREST; } for (int whichStruct = 0; whichStruct < (int)volumeList.size(); ++whichStruct) { VolumeFile myVol, badRoiVol, myVolOut, dataRoiVol; VolumeFile* roiPtr = NULL; int64_t offset[3]; if (myBadRoi != NULL) { AlgorithmCiftiSeparate(NULL, myBadRoi, CiftiXMLOld::ALONG_COLUMN, volumeList[whichStruct], &badRoiVol, offset, NULL, true); roiPtr = &badRoiVol; } AlgorithmCiftiSeparate(NULL, myCifti, myDir, volumeList[whichStruct], &myVol, offset, &dataRoiVol, true); AlgorithmVolumeDilate(NULL, &myVol, volDist, myMethod, &myVolOut, roiPtr, &dataRoiVol); AlgorithmCiftiReplaceStructure(NULL, myCiftiOut, myDir, volumeList[whichStruct], &myVolOut, true); } } } float AlgorithmCiftiDilate::getAlgorithmInternalWeight() { return 1.0f;//override this if needed, if the progress bar isn't smooth } float AlgorithmCiftiDilate::getSubAlgorithmWeight() { //return AlgorithmInsertNameHere::getAlgorithmWeight();//if you use a subalgorithm return 0.0f; } connectome-workbench-1.2.3+git41-gc4c6c90/src/Algorithms/AlgorithmCiftiDilate.h000066400000000000000000000041311300200146000270550ustar00rootroot00000000000000#ifndef __ALGORITHM_CIFTI_DILATE_H__ #define __ALGORITHM_CIFTI_DILATE_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 "AbstractAlgorithm.h" namespace caret { class AlgorithmCiftiDilate : public AbstractAlgorithm { AlgorithmCiftiDilate(); protected: static float getSubAlgorithmWeight(); static float getAlgorithmInternalWeight(); public: AlgorithmCiftiDilate(ProgressObject* myProgObj, const CiftiFile* myCifti, const int& myDir, const float& surfDist, const float& volDist, CiftiFile* myCiftiOut, const SurfaceFile* myLeftSurf = NULL, const SurfaceFile* myRightSurf = NULL, const SurfaceFile* myCerebSurf = NULL, const MetricFile* myLeftAreas = NULL, const MetricFile* myRightAreas = NULL, const MetricFile* myCerebAreas = NULL, const CiftiFile* myRoi = NULL, const bool& nearest = false, const bool& mergedVolume = false); static OperationParameters* getParameters(); static void useParameters(OperationParameters* myParams, ProgressObject* myProgObj); static AString getCommandSwitch(); static AString getShortDescription(); }; typedef TemplateAutoOperation AutoAlgorithmCiftiDilate; } #endif //__ALGORITHM_CIFTI_DILATE_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Algorithms/AlgorithmCiftiErode.cxx000066400000000000000000000273641300200146000273010ustar00rootroot00000000000000/*LICENSE_START*/ /* * Copyright (C) 2015 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 "AlgorithmCiftiErode.h" #include "AlgorithmException.h" #include "AlgorithmCiftiSeparate.h" #include "AlgorithmCiftiReplaceStructure.h" #include "AlgorithmLabelErode.h" #include "AlgorithmMetricErode.h" #include "AlgorithmVolumeErode.h" #include "CiftiFile.h" #include "LabelFile.h" #include "MetricFile.h" #include "SurfaceFile.h" #include "VolumeFile.h" using namespace caret; using namespace std; AString AlgorithmCiftiErode::getCommandSwitch() { return "-cifti-erode"; } AString AlgorithmCiftiErode::getShortDescription() { return "ERODE A CIFTI FILE"; } OperationParameters* AlgorithmCiftiErode::getParameters() { OperationParameters* ret = new OperationParameters(); ret->addCiftiParameter(1, "cifti-in", "the input cifti file"); ret->addStringParameter(2, "direction", "which dimension to dilate along, ROW or COLUMN"); ret->addDoubleParameter(3, "surface-distance", "the distance to dilate on surfaces, in mm"); ret->addDoubleParameter(4, "volume-distance", "the distance to dilate in the volume, in mm"); ret->addCiftiOutputParameter(5, "cifti-out", "the output cifti file"); OptionalParameter* leftSurfOpt = ret->createOptionalParameter(6, "-left-surface", "specify the left surface to use"); leftSurfOpt->addSurfaceParameter(1, "surface", "the left surface file"); OptionalParameter* leftCorrAreasOpt = leftSurfOpt->createOptionalParameter(2, "-left-corrected-areas", "vertex areas to use instead of computing them from the left surface"); leftCorrAreasOpt->addMetricParameter(1, "area-metric", "the corrected vertex areas, as a metric"); OptionalParameter* rightSurfOpt = ret->createOptionalParameter(7, "-right-surface", "specify the right surface to use"); rightSurfOpt->addSurfaceParameter(1, "surface", "the right surface file"); OptionalParameter* rightCorrAreasOpt = rightSurfOpt->createOptionalParameter(2, "-right-corrected-areas", "vertex areas to use instead of computing them from the right surface"); rightCorrAreasOpt->addMetricParameter(1, "area-metric", "the corrected vertex areas, as a metric"); OptionalParameter* cerebSurfOpt = ret->createOptionalParameter(8, "-cerebellum-surface", "specify the cerebellum surface to use"); cerebSurfOpt->addSurfaceParameter(1, "surface", "the cerebellum surface file"); OptionalParameter* cerebCorrAreasOpt = cerebSurfOpt->createOptionalParameter(2, "-cerebellum-corrected-areas", "vertex areas to use instead of computing them from the cerebellum surface"); cerebCorrAreasOpt->addMetricParameter(1, "area-metric", "the corrected vertex areas, as a metric"); ret->createOptionalParameter(9, "-merged-volume", "treat volume components as if they were a single component"); ret->setHelpText( AString("For all data values that are empty (for label data, unlabeled, for other data, zero), set the surrounding values to empty. ") + "The surrounding values are defined as the immediate neighbors and all values in the same structure within the specified distance " + "(-merged-volume treats all voxels as one structure).\n\n" + "The -*-corrected-areas options are intended for eroding on group average surfaces, but it is only an approximate correction." ); return ret; } void AlgorithmCiftiErode::useParameters(OperationParameters* myParams, ProgressObject* myProgObj) { CiftiFile* myCifti = myParams->getCifti(1); AString directionName = myParams->getString(2); int myDir; if (directionName == "ROW") { myDir = CiftiXML::ALONG_ROW; } else if (directionName == "COLUMN") { myDir = CiftiXML::ALONG_COLUMN; } else { throw AlgorithmException("incorrect string for direction, use ROW or COLUMN"); } float surfDist = (float)myParams->getDouble(3); float volDist = (float)myParams->getDouble(4); CiftiFile* myCiftiOut = myParams->getOutputCifti(5); SurfaceFile* myLeftSurf = NULL, *myRightSurf = NULL, *myCerebSurf = NULL; MetricFile* myLeftAreas = NULL, *myRightAreas = NULL, *myCerebAreas = NULL; OptionalParameter* leftSurfOpt = myParams->getOptionalParameter(6); if (leftSurfOpt->m_present) { myLeftSurf = leftSurfOpt->getSurface(1); OptionalParameter* leftCorrAreasOpt = leftSurfOpt->getOptionalParameter(2); if (leftCorrAreasOpt->m_present) { myLeftAreas = leftCorrAreasOpt->getMetric(1); } } OptionalParameter* rightSurfOpt = myParams->getOptionalParameter(7); if (rightSurfOpt->m_present) { myRightSurf = rightSurfOpt->getSurface(1); OptionalParameter* rightCorrAreasOpt = rightSurfOpt->getOptionalParameter(2); if (rightCorrAreasOpt->m_present) { myRightAreas = rightCorrAreasOpt->getMetric(1); } } OptionalParameter* cerebSurfOpt = myParams->getOptionalParameter(8); if (cerebSurfOpt->m_present) { myCerebSurf = cerebSurfOpt->getSurface(1); OptionalParameter* cerebCorrAreasOpt = cerebSurfOpt->getOptionalParameter(2); if (cerebCorrAreasOpt->m_present) { myCerebAreas = cerebCorrAreasOpt->getMetric(1); } } bool mergedVolume = myParams->getOptionalParameter(9)->m_present; AlgorithmCiftiErode(myProgObj, myCifti, myDir, surfDist, volDist, myCiftiOut, myLeftSurf, myRightSurf, myCerebSurf, myLeftAreas, myRightAreas, myCerebAreas, mergedVolume); } AlgorithmCiftiErode::AlgorithmCiftiErode(ProgressObject* myProgObj, const CiftiFile* myCifti, const int& myDir, const float& surfDist, const float& volDist, CiftiFile* myCiftiOut, const SurfaceFile* myLeftSurf, const SurfaceFile* myRightSurf, const SurfaceFile* myCerebSurf, const MetricFile* myLeftAreas, const MetricFile* myRightAreas, const MetricFile* myCerebAreas, const bool& mergedVolume) : AbstractAlgorithm(myProgObj) { LevelProgress myProgress(myProgObj); const CiftiXML& myXML = myCifti->getCiftiXML(); if (myXML.getNumberOfDimensions() != 2) { throw AlgorithmException("cifti erode only supports 2D cifti"); } if (myXML.getMappingType(myDir) != CiftiMappingType::BRAIN_MODELS) { throw AlgorithmException("specified direction does not contain brainordinates"); } const CiftiBrainModelsMap& myDenseMap = myXML.getBrainModelsMap(myDir); vector surfaceList = myDenseMap.getSurfaceStructureList(); for (int whichStruct = 0; whichStruct < (int)surfaceList.size(); ++whichStruct) {//sanity check surfaces const SurfaceFile* mySurf = NULL; const MetricFile* myCorrAreas = NULL; AString surfType; switch (surfaceList[whichStruct]) { case StructureEnum::CORTEX_LEFT: mySurf = myLeftSurf; myCorrAreas = myLeftAreas; surfType = "left"; break; case StructureEnum::CORTEX_RIGHT: mySurf = myRightSurf; myCorrAreas = myRightAreas; surfType = "right"; break; case StructureEnum::CEREBELLUM: mySurf = myCerebSurf; myCorrAreas = myCerebAreas; surfType = "cerebellum"; break; default: throw AlgorithmException("found surface model with incorrect type: " + StructureEnum::toName(surfaceList[whichStruct])); break; } if (mySurf == NULL) { throw AlgorithmException(surfType + " surface required but not provided"); } if (mySurf->getNumberOfNodes() != myDenseMap.getSurfaceNumberOfNodes(surfaceList[whichStruct])) { throw AlgorithmException(surfType + " surface has the wrong number of vertices"); } if (myCorrAreas != NULL && myCorrAreas->getNumberOfNodes() != mySurf->getNumberOfNodes()) { throw AlgorithmException(surfType + " surface and corrected areas metric have different numbers of vertices"); } } myCiftiOut->setCiftiXML(myXML); for (int whichStruct = 0; whichStruct < (int)surfaceList.size(); ++whichStruct) { const SurfaceFile* mySurf = NULL; const MetricFile* myCorrAreas = NULL; switch (surfaceList[whichStruct]) { case StructureEnum::CORTEX_LEFT: mySurf = myLeftSurf; myCorrAreas = myLeftAreas; break; case StructureEnum::CORTEX_RIGHT: mySurf = myRightSurf; myCorrAreas = myRightAreas; break; case StructureEnum::CEREBELLUM: mySurf = myCerebSurf; myCorrAreas = myCerebAreas; break; default: break; } MetricFile dataRoiMetric; if (myXML.getMappingType(1 - myDir) == CiftiMappingType::LABELS) { LabelFile myLabel, myLabelOut; AlgorithmCiftiSeparate(NULL, myCifti, myDir, surfaceList[whichStruct], &myLabel, &dataRoiMetric); AlgorithmLabelErode(NULL, &myLabel, mySurf, surfDist, &myLabelOut, &dataRoiMetric, -1, myCorrAreas); AlgorithmCiftiReplaceStructure(NULL, myCiftiOut, myDir, surfaceList[whichStruct], &myLabelOut); } else { MetricFile myMetric, myMetricOut; AlgorithmCiftiSeparate(NULL, myCifti, myDir, surfaceList[whichStruct], &myMetric, &dataRoiMetric); AlgorithmMetricErode(NULL, &myMetric, mySurf, surfDist, &myMetricOut, &dataRoiMetric, -1, myCorrAreas); AlgorithmCiftiReplaceStructure(NULL, myCiftiOut, myDir, surfaceList[whichStruct], &myMetricOut); } } if (mergedVolume) { if (myDenseMap.hasVolumeData()) { VolumeFile myVol, roiVol, myVolOut; int64_t offset[3]; AlgorithmCiftiSeparate(NULL, myCifti, myDir, &myVol, offset, &roiVol, true); AlgorithmVolumeErode(NULL, &myVol, volDist, &myVolOut, &roiVol); AlgorithmCiftiReplaceStructure(NULL, myCiftiOut, myDir, &myVolOut, true); } } else { vector volumeList = myDenseMap.getVolumeStructureList(); for (int whichStruct = 0; whichStruct < (int)volumeList.size(); ++whichStruct) { VolumeFile myVol, myVolOut, dataRoiVol; int64_t offset[3]; AlgorithmCiftiSeparate(NULL, myCifti, myDir, volumeList[whichStruct], &myVol, offset, &dataRoiVol, true); AlgorithmVolumeErode(NULL, &myVol, volDist, &myVolOut, &dataRoiVol); AlgorithmCiftiReplaceStructure(NULL, myCiftiOut, myDir, volumeList[whichStruct], &myVolOut, true); } } } float AlgorithmCiftiErode::getAlgorithmInternalWeight() { return 1.0f;//override this if needed, if the progress bar isn't smooth } float AlgorithmCiftiErode::getSubAlgorithmWeight() { //return AlgorithmInsertNameHere::getAlgorithmWeight();//if you use a subalgorithm return 0.0f; } connectome-workbench-1.2.3+git41-gc4c6c90/src/Algorithms/AlgorithmCiftiErode.h000066400000000000000000000037661300200146000267260ustar00rootroot00000000000000#ifndef __ALGORITHM_CIFTI_ERODE_H__ #define __ALGORITHM_CIFTI_ERODE_H__ /*LICENSE_START*/ /* * Copyright (C) 2015 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 AlgorithmCiftiErode : public AbstractAlgorithm { AlgorithmCiftiErode(); protected: static float getSubAlgorithmWeight(); static float getAlgorithmInternalWeight(); public: AlgorithmCiftiErode(ProgressObject* myProgObj, const CiftiFile* myCifti, const int& myDir, const float& surfDist, const float& volDist, CiftiFile* myCiftiOut, const SurfaceFile* myLeftSurf = NULL, const SurfaceFile* myRightSurf = NULL, const SurfaceFile* myCerebSurf = NULL, const MetricFile* myLeftAreas = NULL, const MetricFile* myRightAreas = NULL, const MetricFile* myCerebAreas = NULL, const bool& mergedVolume = false); static OperationParameters* getParameters(); static void useParameters(OperationParameters* myParams, ProgressObject* myProgObj); static AString getCommandSwitch(); static AString getShortDescription(); }; typedef TemplateAutoOperation AutoAlgorithmCiftiErode; } #endif //__ALGORITHM_CIFTI_ERODE_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Algorithms/AlgorithmCiftiExtrema.cxx000066400000000000000000000332411300200146000276370ustar00rootroot00000000000000/*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 "AlgorithmCiftiExtrema.h" #include "AlgorithmException.h" #include "AlgorithmCiftiSeparate.h" #include "AlgorithmCiftiReplaceStructure.h" #include "AlgorithmMetricExtrema.h" #include "AlgorithmVolumeExtrema.h" #include "CiftiFile.h" #include "MetricFile.h" #include "SurfaceFile.h" #include "VolumeFile.h" using namespace caret; using namespace std; AString AlgorithmCiftiExtrema::getCommandSwitch() { return "-cifti-extrema"; } AString AlgorithmCiftiExtrema::getShortDescription() { return "FIND EXTREMA IN A CIFTI FILE"; } OperationParameters* AlgorithmCiftiExtrema::getParameters() { OperationParameters* ret = new OperationParameters(); ret->addCiftiParameter(1, "cifti", "the input cifti"); ret->addDoubleParameter(2, "surface-distance", "the minimum distance between extrema of the same type, for surface components"); ret->addDoubleParameter(3, "volume-distance", "the minimum distance between extrema of the same type, for volume components"); ret->addStringParameter(4, "direction", "which dimension to find extrema along, ROW or COLUMN"); ret->addCiftiOutputParameter(5, "cifti-out", "the output cifti"); OptionalParameter* leftSurfOpt = ret->createOptionalParameter(6, "-left-surface", "specify the left surface to use"); leftSurfOpt->addSurfaceParameter(1, "surface", "the left surface file"); OptionalParameter* rightSurfOpt = ret->createOptionalParameter(7, "-right-surface", "specify the right surface to use"); rightSurfOpt->addSurfaceParameter(1, "surface", "the right surface file"); OptionalParameter* cerebSurfaceOpt = ret->createOptionalParameter(8, "-cerebellum-surface", "specify the cerebellum surface to use"); cerebSurfaceOpt->addSurfaceParameter(1, "surface", "the cerebellum surface file"); OptionalParameter* presmoothSurfOpt = ret->createOptionalParameter(9, "-surface-presmooth", "smooth on the surface before finding extrema"); presmoothSurfOpt->addDoubleParameter(1, "surface-kernel", "the sigma for the gaussian surface smoothing kernel, in mm"); OptionalParameter* presmoothVolOpt = ret->createOptionalParameter(10, "-volume-presmooth", "smooth volume components before finding extrema"); presmoothVolOpt->addDoubleParameter(1, "volume-kernel", "the sigma for the gaussian volume smoothing kernel, in mm"); OptionalParameter* thresholdOpt = ret->createOptionalParameter(11, "-threshold", "ignore small extrema"); thresholdOpt->addDoubleParameter(1, "low", "the largest value to consider for being a minimum"); thresholdOpt->addDoubleParameter(2, "high", "the smallest value to consider for being a maximum"); ret->createOptionalParameter(12, "-merged-volume", "treat volume components as if they were a single component"); ret->createOptionalParameter(13, "-sum-maps", "output the sum of the extrema maps instead of each map separately"); ret->createOptionalParameter(14, "-consolidate-mode", "use consolidation of local minima instead of a large neighborhood"); ret->createOptionalParameter(15, "-only-maxima", "only find the maxima"); ret->createOptionalParameter(16, "-only-minima", "only find the minima"); ret->setHelpText( AString("Finds spatial locations in a cifti file that have more extreme values than all nearby locations in the same component (surface or volume structure). ") + "The input cifti file must have a brain models mapping along the specified direction. " + "COLUMN is the direction that works on dtseries and dscalar. For dconn, if it is symmetric use COLUMN, otherwise use ROW." ); return ret; } void AlgorithmCiftiExtrema::useParameters(OperationParameters* myParams, ProgressObject* myProgObj) { CiftiFile* myCifti = myParams->getCifti(1); float surfDist = (float)myParams->getDouble(2); float volDist = (float)myParams->getDouble(3); AString directionName = myParams->getString(4); int myDir; if (directionName == "ROW") { myDir = CiftiXMLOld::ALONG_ROW; } else if (directionName == "COLUMN") { myDir = CiftiXMLOld::ALONG_COLUMN; } else { throw AlgorithmException("incorrect string for direction, use ROW or COLUMN"); } CiftiFile* myCiftiOut = myParams->getOutputCifti(5); SurfaceFile* myLeftSurf = NULL, *myRightSurf = NULL, *myCerebSurf = NULL; OptionalParameter* leftSurfOpt = myParams->getOptionalParameter(6); if (leftSurfOpt->m_present) { myLeftSurf = leftSurfOpt->getSurface(1); } OptionalParameter* rightSurfOpt = myParams->getOptionalParameter(7); if (rightSurfOpt->m_present) { myRightSurf = rightSurfOpt->getSurface(1); } OptionalParameter* cerebSurfOpt = myParams->getOptionalParameter(8); if (cerebSurfOpt->m_present) { myCerebSurf = cerebSurfOpt->getSurface(1); } float surfPresmooth = -1.0f; OptionalParameter* presmoothSurfOpt = myParams->getOptionalParameter(9); if (presmoothSurfOpt->m_present) { surfPresmooth = (float)presmoothSurfOpt->getDouble(1); } float volPresmooth = -1.0f; OptionalParameter* presmoothVolOpt = myParams->getOptionalParameter(10); if (presmoothVolOpt->m_present) { volPresmooth = (float)presmoothVolOpt->getDouble(1); } OptionalParameter* thresholdOpt = myParams->getOptionalParameter(11); bool thresholdMode = false; float lowThresh = 0.0f, highThresh = 0.0f; if (thresholdOpt->m_present) { thresholdMode = true; lowThresh = (float)thresholdOpt->getDouble(1); highThresh = (float)thresholdOpt->getDouble(2); } bool mergedVolume = myParams->getOptionalParameter(12)->m_present; bool sumMaps = myParams->getOptionalParameter(13)->m_present; bool consolidateMode = myParams->getOptionalParameter(14)->m_present; bool ignoreMinima = myParams->getOptionalParameter(15)->m_present; bool ignoreMaxima = myParams->getOptionalParameter(16)->m_present; if (ignoreMinima && ignoreMaxima) throw AlgorithmException("you may not specify both -only-maxima and -only-minima"); AlgorithmCiftiExtrema(myProgObj, myCifti, surfDist, volDist, myDir, myCiftiOut, myLeftSurf, myRightSurf, myCerebSurf, surfPresmooth, volPresmooth, thresholdMode, lowThresh, highThresh, mergedVolume, sumMaps, consolidateMode, ignoreMinima, ignoreMaxima); } AlgorithmCiftiExtrema::AlgorithmCiftiExtrema(ProgressObject* myProgObj, const CiftiFile* myCifti, const float& surfDist, const float& volDist, const int& myDir, CiftiFile* myCiftiOut, const SurfaceFile* myLeftSurf, const SurfaceFile* myRightSurf, const SurfaceFile* myCerebSurf, const float& surfPresmooth, const float& volPresmooth, const bool& thresholdMode, const float& lowThresh, const float& highThresh, const bool& mergedVolume, const bool& sumMaps, const bool& consolidateMode, const bool& ignoreMinima, const bool& ignoreMaxima) : AbstractAlgorithm(myProgObj) { LevelProgress myProgress(myProgObj); CiftiXMLOld myXML = myCifti->getCiftiXMLOld(), myOutXML; myOutXML = myXML; vector surfaceList, volumeList; if (myDir == CiftiXMLOld::ALONG_COLUMN) { if (!myXML.getStructureListsForColumns(surfaceList, volumeList)) { throw AlgorithmException("specified direction does not contain brainordinates"); } } else { if (!myXML.getStructureListsForRows(surfaceList, volumeList)) { throw AlgorithmException("specified direction does not contain brainordinates"); } } for (int whichStruct = 0; whichStruct < (int)surfaceList.size(); ++whichStruct) {//sanity check surfaces const SurfaceFile* mySurf = NULL; AString surfType; switch (surfaceList[whichStruct]) { case StructureEnum::CORTEX_LEFT: mySurf = myLeftSurf; surfType = "left"; break; case StructureEnum::CORTEX_RIGHT: mySurf = myRightSurf; surfType = "right"; break; case StructureEnum::CEREBELLUM: mySurf = myCerebSurf; surfType = "cerebellum"; break; default: throw AlgorithmException("found surface model with incorrect type: " + StructureEnum::toName(surfaceList[whichStruct])); break; } if (mySurf == NULL) { throw AlgorithmException(surfType + " surface required but not provided"); } if (myDir == CiftiXMLOld::ALONG_COLUMN) { if (mySurf->getNumberOfNodes() != myXML.getColumnSurfaceNumberOfNodes(surfaceList[whichStruct])) { throw AlgorithmException(surfType + " surface has the wrong number of vertices"); } } else { if (mySurf->getNumberOfNodes() != myXML.getRowSurfaceNumberOfNodes(surfaceList[whichStruct])) { throw AlgorithmException(surfType + " surface has the wrong number of vertices"); } } } int outDir = myDir; if (sumMaps) { if (myDir == CiftiXMLOld::ALONG_ROW)//NOTE: when using along row and -sum-maps, make a dscalar, not a scalard { myOutXML.copyMapping(CiftiXMLOld::ALONG_COLUMN, myXML, CiftiXMLOld::ALONG_ROW);//so, transpose the maps if we are using dense along row myOutXML.copyMapping(CiftiXMLOld::ALONG_ROW, myXML, CiftiXMLOld::ALONG_COLUMN); } outDir = CiftiXMLOld::ALONG_COLUMN; myOutXML.resetDirectionToScalars(CiftiXMLOld::ALONG_ROW, 1); myOutXML.setMapNameForIndex(CiftiXMLOld::ALONG_ROW, 0, "sum of extrema"); } myCiftiOut->setCiftiXML(myOutXML); for (int whichStruct = 0; whichStruct < (int)surfaceList.size(); ++whichStruct) { const SurfaceFile* mySurf = NULL; switch (surfaceList[whichStruct]) { case StructureEnum::CORTEX_LEFT: mySurf = myLeftSurf; break; case StructureEnum::CORTEX_RIGHT: mySurf = myRightSurf; break; case StructureEnum::CEREBELLUM: mySurf = myCerebSurf; break; default: break; } MetricFile myMetric, myRoi, myMetricOut; AlgorithmCiftiSeparate(NULL, myCifti, myDir, surfaceList[whichStruct], &myMetric, &myRoi); if (thresholdMode) { AlgorithmMetricExtrema(NULL, mySurf, &myMetric, surfDist, &myMetricOut, lowThresh, highThresh, &myRoi, surfPresmooth, sumMaps, consolidateMode, ignoreMinima, ignoreMaxima); } else { AlgorithmMetricExtrema(NULL, mySurf, &myMetric, surfDist, &myMetricOut, &myRoi, surfPresmooth, sumMaps, consolidateMode, ignoreMinima, ignoreMaxima); } AlgorithmCiftiReplaceStructure(NULL, myCiftiOut, outDir, surfaceList[whichStruct], &myMetricOut); } if (mergedVolume) { if (myCifti->getCiftiXMLOld().hasVolumeData(myDir)) { VolumeFile myVol, myRoi, myVolOut; int64_t offset[3]; AlgorithmCiftiSeparate(NULL, myCifti, myDir, &myVol, offset, &myRoi, true); if (thresholdMode) { AlgorithmVolumeExtrema(NULL, &myVol, volDist, &myVolOut, lowThresh, highThresh, &myRoi, volPresmooth, sumMaps, consolidateMode, ignoreMinima, ignoreMaxima); } else { AlgorithmVolumeExtrema(NULL, &myVol, volDist, &myVolOut, &myRoi, volPresmooth, sumMaps, consolidateMode, ignoreMinima, ignoreMaxima); } AlgorithmCiftiReplaceStructure(NULL, myCiftiOut, outDir, &myVolOut, true); } } else { for (int whichStruct = 0; whichStruct < (int)volumeList.size(); ++whichStruct) { VolumeFile myVol, myRoi, myVolOut; int64_t offset[3]; AlgorithmCiftiSeparate(NULL, myCifti, myDir, volumeList[whichStruct], &myVol, offset, &myRoi, true); if (thresholdMode) { AlgorithmVolumeExtrema(NULL, &myVol, volDist, &myVolOut, lowThresh, highThresh, &myRoi, volPresmooth, sumMaps, consolidateMode, ignoreMinima, ignoreMaxima); } else { AlgorithmVolumeExtrema(NULL, &myVol, volDist, &myVolOut, &myRoi, volPresmooth, sumMaps, consolidateMode, ignoreMinima, ignoreMaxima); } AlgorithmCiftiReplaceStructure(NULL, myCiftiOut, outDir, volumeList[whichStruct], &myVolOut, true); } } } float AlgorithmCiftiExtrema::getAlgorithmInternalWeight() { return 1.0f;//override this if needed, if the progress bar isn't smooth } float AlgorithmCiftiExtrema::getSubAlgorithmWeight() { //return AlgorithmInsertNameHere::getAlgorithmWeight();//if you use a subalgorithm return 0.0f; } connectome-workbench-1.2.3+git41-gc4c6c90/src/Algorithms/AlgorithmCiftiExtrema.h000066400000000000000000000044371300200146000272710ustar00rootroot00000000000000#ifndef __ALGORITHM_CIFTI_EXTREMA_H__ #define __ALGORITHM_CIFTI_EXTREMA_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 "AbstractAlgorithm.h" namespace caret { class AlgorithmCiftiExtrema : public AbstractAlgorithm { AlgorithmCiftiExtrema(); protected: static float getSubAlgorithmWeight(); static float getAlgorithmInternalWeight(); public: AlgorithmCiftiExtrema(ProgressObject* myProgObj, const CiftiFile* myCifti, const float& surfDist, const float& volDist, const int& myDir, CiftiFile* myCiftiOut, const SurfaceFile* myLeftSurf = NULL, const SurfaceFile* myRightSurf = NULL, const SurfaceFile* myCerebSurf = NULL, const float& surfPresmooth = -1.0f, const float& volPresmooth = -1.0f, const bool& thresholdMode = false, const float& lowThresh = 0.0f, const float& highThresh = 0.0f, const bool& mergedVolume = false, const bool& sumMaps = false, const bool& consolidateMode = false, const bool& ignoreMinima = false, const bool& ignoreMaxima = false); static OperationParameters* getParameters(); static void useParameters(OperationParameters* myParams, ProgressObject* myProgObj); static AString getCommandSwitch(); static AString getShortDescription(); }; typedef TemplateAutoOperation AutoAlgorithmCiftiExtrema; } #endif //__ALGORITHM_CIFTI_EXTREMA_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Algorithms/AlgorithmCiftiFalseCorrelation.cxx000066400000000000000000000236411300200146000314710ustar00rootroot00000000000000/*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 "AlgorithmCiftiFalseCorrelation.h" #include "AlgorithmException.h" #include "AlgorithmCiftiReplaceStructure.h" #include "AlgorithmCiftiSeparate.h" #include "AlgorithmMetricFalseCorrelation.h" #include "CiftiFile.h" #include "SurfaceFile.h" #include "MetricFile.h" #include "VolumeFile.h" using namespace caret; using namespace std; AString AlgorithmCiftiFalseCorrelation::getCommandSwitch() { return "-cifti-false-correlation"; } AString AlgorithmCiftiFalseCorrelation::getShortDescription() { return "COMPARE CORRELATION LOCALLY AND ACROSS/THROUGH SULCI/GYRI"; } OperationParameters* AlgorithmCiftiFalseCorrelation::getParameters() { OperationParameters* ret = new OperationParameters(); ret->addCiftiParameter(1, "cifti-in", "the cifti file to use for correlation"); ret->addDoubleParameter(2, "3D-dist", "maximum 3D distance to check around each vertex"); ret->addDoubleParameter(3, "geo-outer", "maximum geodesic distance to use for neighboring correlation"); ret->addDoubleParameter(4, "geo-inner", "minimum geodesic distance to use for neighboring correlation"); ret->addCiftiOutputParameter(5, "cifti-out", "the output cifti dscalar file"); OptionalParameter* leftSurfOpt = ret->createOptionalParameter(6, "-left-surface", "specify the left surface to use"); leftSurfOpt->addSurfaceParameter(1, "surface", "the left surface file"); OptionalParameter* dumpLeftOpt = leftSurfOpt->createOptionalParameter(2, "-dump-text", "dump the raw measures used to a text file"); dumpLeftOpt->addStringParameter(1, "text-out", "the output text file"); OptionalParameter* rightSurfOpt = ret->createOptionalParameter(7, "-right-surface", "specify the right surface to use"); rightSurfOpt->addSurfaceParameter(1, "surface", "the right surface file"); OptionalParameter* dumpRightOpt = rightSurfOpt->createOptionalParameter(2, "-dump-text", "dump the raw measures used to a text file"); dumpRightOpt->addStringParameter(1, "text-out", "the output text file"); OptionalParameter* cerebSurfOpt = ret->createOptionalParameter(8, "-cerebellum-surface", "specify the cerebellum surface to use"); cerebSurfOpt->addSurfaceParameter(1, "surface", "the cerebellum surface file"); OptionalParameter* dumpCerebOpt = cerebSurfOpt->createOptionalParameter(2, "-dump-text", "dump the raw measures used to a text file"); dumpCerebOpt->addStringParameter(1, "text-out", "the output text file"); ret->setHelpText( AString("For each vertex, compute the average correlation within a range of geodesic distances that don't cross a sulcus/gyrus, and the correlation to the closest vertex crossing a sulcus/gyrus. ") + "A vertex is considered to cross a sulcus/gyrus if the 3D distance is less than a third of the geodesic distance. " + "The output file contains the ratio between these correlations, and some additional maps to help explain the ratio." ); return ret; } void AlgorithmCiftiFalseCorrelation::useParameters(OperationParameters* myParams, ProgressObject* myProgObj) { CiftiFile* myCiftiIn = myParams->getCifti(1); float max3D = (float)myParams->getDouble(2); float maxgeo = (float)myParams->getDouble(3); float mingeo = (float)myParams->getDouble(4); CiftiFile* myCiftiOut = myParams->getOutputCifti(5); SurfaceFile* myLeftSurf = NULL, *myRightSurf = NULL, *myCerebSurf = NULL; AString leftDumpName, rightDumpName, cerebDumpName; OptionalParameter* leftSurfOpt = myParams->getOptionalParameter(6); if (leftSurfOpt->m_present) { myLeftSurf = leftSurfOpt->getSurface(1); OptionalParameter* dumpLeftOpt = leftSurfOpt->getOptionalParameter(2); if (dumpLeftOpt->m_present) { leftDumpName = dumpLeftOpt->getString(1); } } OptionalParameter* rightSurfOpt = myParams->getOptionalParameter(7); if (rightSurfOpt->m_present) { myRightSurf = rightSurfOpt->getSurface(1); OptionalParameter* dumpRightOpt = rightSurfOpt->getOptionalParameter(2); if (dumpRightOpt->m_present) { rightDumpName = dumpRightOpt->getString(1); } } OptionalParameter* cerebSurfOpt = myParams->getOptionalParameter(8); if (cerebSurfOpt->m_present) { myCerebSurf = cerebSurfOpt->getSurface(1); OptionalParameter* dumpCerebOpt = cerebSurfOpt->getOptionalParameter(2); if (dumpCerebOpt->m_present) { cerebDumpName = dumpCerebOpt->getString(1); } } AlgorithmCiftiFalseCorrelation(myProgObj, myCiftiIn, max3D, maxgeo, mingeo, myCiftiOut, myLeftSurf, leftDumpName, myRightSurf, rightDumpName, myCerebSurf, cerebDumpName); } AlgorithmCiftiFalseCorrelation::AlgorithmCiftiFalseCorrelation(ProgressObject* myProgObj, const CiftiFile* myCiftiIn, const float& max3D, const float& maxgeo, const float& mingeo, CiftiFile* myCiftiOut, const SurfaceFile* myLeftSurf, const AString& leftDumpName, const SurfaceFile* myRightSurf, const AString& rightDumpName, const SurfaceFile* myCerebSurf, const AString& cerebDumpName) : AbstractAlgorithm(myProgObj) { LevelProgress myProgress(myProgObj); const CiftiXMLOld& myXML = myCiftiIn->getCiftiXMLOld(); CiftiXMLOld myNewXML = myXML; vector surfaceList, volumeList; if (!myXML.getStructureLists(CiftiXMLOld::ALONG_COLUMN, surfaceList, volumeList)) { throw AlgorithmException("input cifti does not contain brainordinates along columns"); } for (int whichStruct = 0; whichStruct < (int)surfaceList.size(); ++whichStruct) {//sanity check surfaces const SurfaceFile* mySurf = NULL; AString surfType; switch (surfaceList[whichStruct]) { case StructureEnum::CORTEX_LEFT: mySurf = myLeftSurf; surfType = "left"; break; case StructureEnum::CORTEX_RIGHT: mySurf = myRightSurf; surfType = "right"; break; case StructureEnum::CEREBELLUM: mySurf = myCerebSurf; surfType = "cerebellum"; break; default: throw AlgorithmException("found surface model with incorrect type: " + StructureEnum::toName(surfaceList[whichStruct])); break; } if (mySurf == NULL) { throw AlgorithmException(surfType + " surface required but not provided"); } if (mySurf->getNumberOfNodes() != myXML.getSurfaceNumberOfNodes(CiftiXMLOld::ALONG_COLUMN, surfaceList[whichStruct])) { throw AlgorithmException(surfType + " surface has the wrong number of vertices"); } } myNewXML.resetRowsToScalars(5); myNewXML.setMapNameForIndex(CiftiXMLOld::ALONG_ROW, 0, "correlation ratio"); myNewXML.setMapNameForIndex(CiftiXMLOld::ALONG_ROW, 1, "non-neighborhood correlation"); myNewXML.setMapNameForIndex(CiftiXMLOld::ALONG_ROW, 2, "average neighborhood correlation"); myNewXML.setMapNameForIndex(CiftiXMLOld::ALONG_ROW, 3, "3D distance to non-neighborhood vertex"); myNewXML.setMapNameForIndex(CiftiXMLOld::ALONG_ROW, 4, "non-neighborhood vertex number"); myCiftiOut->setCiftiXML(myNewXML); for (int whichStruct = 0; whichStruct < (int)surfaceList.size(); ++whichStruct) { const SurfaceFile* mySurf = NULL; AString dumpName; switch (surfaceList[whichStruct]) { case StructureEnum::CORTEX_LEFT: mySurf = myLeftSurf; dumpName = leftDumpName; break; case StructureEnum::CORTEX_RIGHT: mySurf = myRightSurf; dumpName = rightDumpName; break; case StructureEnum::CEREBELLUM: mySurf = myCerebSurf; dumpName = cerebDumpName; break; default: break; } MetricFile myMetric, myRoi, myMetricOut; AlgorithmCiftiSeparate(NULL, myCiftiIn, CiftiXML::ALONG_COLUMN, surfaceList[whichStruct], &myMetric, &myRoi); AlgorithmMetricFalseCorrelation(NULL, mySurf, &myMetric, &myMetricOut, max3D, maxgeo, mingeo, &myRoi, dumpName); AlgorithmCiftiReplaceStructure(NULL, myCiftiOut, CiftiXML::ALONG_COLUMN, surfaceList[whichStruct], &myMetricOut); } int64_t offset[3]; vector dims(3); vector > sform; AlgorithmCiftiSeparate::getCroppedVolSpaceAll(myCiftiIn, CiftiXML::ALONG_COLUMN, dims.data(), sform, offset); dims.push_back(5); VolumeFile zeros(dims, sform); zeros.setValueAllVoxels(0.0f); AlgorithmCiftiReplaceStructure(NULL, myCiftiOut, CiftiXML::ALONG_COLUMN, &zeros, true); } float AlgorithmCiftiFalseCorrelation::getAlgorithmInternalWeight() { return 1.0f;//override this if needed, if the progress bar isn't smooth } float AlgorithmCiftiFalseCorrelation::getSubAlgorithmWeight() { //return AlgorithmInsertNameHere::getAlgorithmWeight();//if you use a subalgorithm return 0.0f; } connectome-workbench-1.2.3+git41-gc4c6c90/src/Algorithms/AlgorithmCiftiFalseCorrelation.h000066400000000000000000000040261300200146000311120ustar00rootroot00000000000000#ifndef __ALGORITHM_CIFTI_FALSE_CORRELATION_H__ #define __ALGORITHM_CIFTI_FALSE_CORRELATION_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 "AbstractAlgorithm.h" namespace caret { class AlgorithmCiftiFalseCorrelation : public AbstractAlgorithm { AlgorithmCiftiFalseCorrelation(); protected: static float getSubAlgorithmWeight(); static float getAlgorithmInternalWeight(); public: AlgorithmCiftiFalseCorrelation(ProgressObject* myProgObj, const CiftiFile* myCiftiIn, const float& max3D, const float& maxgeo, const float& mingeo, CiftiFile* myCiftiOut, const SurfaceFile* myLeftSurf, const AString& leftDumpName, const SurfaceFile* myRightSurf, const AString& rightDumpName, const SurfaceFile* myCerebSurf, const AString& cerebDumpName); static OperationParameters* getParameters(); static void useParameters(OperationParameters* myParams, ProgressObject* myProgObj); static AString getCommandSwitch(); static AString getShortDescription(); }; typedef TemplateAutoOperation AutoAlgorithmCiftiFalseCorrelation; } #endif //__ALGORITHM_CIFTI_FALSE_CORRELATION_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Algorithms/AlgorithmCiftiFindClusters.cxx000066400000000000000000000407541300200146000306460ustar00rootroot00000000000000/*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 "AlgorithmCiftiFindClusters.h" #include "AlgorithmException.h" #include "AlgorithmCiftiSeparate.h" #include "AlgorithmCiftiReplaceStructure.h" #include "AlgorithmMetricFindClusters.h" #include "AlgorithmVolumeFindClusters.h" #include "CiftiFile.h" #include "MetricFile.h" #include "SurfaceFile.h" #include "VolumeFile.h" using namespace caret; using namespace std; AString AlgorithmCiftiFindClusters::getCommandSwitch() { return "-cifti-find-clusters"; } AString AlgorithmCiftiFindClusters::getShortDescription() { return "FILTER CLUSTERS BY AREA/VOLUME"; } OperationParameters* AlgorithmCiftiFindClusters::getParameters() { OperationParameters* ret = new OperationParameters(); ret->addCiftiParameter(1, "cifti", "the input cifti"); ret->addDoubleParameter(2, "surface-value-threshold", "threshold for surface data values"); ret->addDoubleParameter(3, "surface-minimum-area", "threshold for surface cluster area, in mm^2"); ret->addDoubleParameter(4, "volume-value-threshold", "threshold for volume data values"); ret->addDoubleParameter(5, "volume-minimum-size", "threshold for volume cluster size, in mm^3"); ret->addStringParameter(6, "direction", "which dimension to use for spatial information, ROW or COLUMN"); ret->addCiftiOutputParameter(7, "cifti-out", "the output cifti"); ret->createOptionalParameter(8, "-less-than", "find values less than , rather than greater"); OptionalParameter* leftSurfOpt = ret->createOptionalParameter(9, "-left-surface", "specify the left surface to use"); leftSurfOpt->addSurfaceParameter(1, "surface", "the left surface file"); OptionalParameter* leftCorrAreasOpt = leftSurfOpt->createOptionalParameter(2, "-corrected-areas", "vertex areas to use instead of computing them from the surface"); leftCorrAreasOpt->addMetricParameter(1, "area-metric", "the corrected vertex areas, as a metric"); OptionalParameter* rightSurfOpt = ret->createOptionalParameter(10, "-right-surface", "specify the right surface to use"); rightSurfOpt->addSurfaceParameter(1, "surface", "the right surface file"); OptionalParameter* rightCorrAreasOpt = rightSurfOpt->createOptionalParameter(2, "-corrected-areas", "vertex areas to use instead of computing them from the surface"); rightCorrAreasOpt->addMetricParameter(1, "area-metric", "the corrected vertex areas, as a metric"); OptionalParameter* cerebSurfaceOpt = ret->createOptionalParameter(11, "-cerebellum-surface", "specify the cerebellum surface to use"); cerebSurfaceOpt->addSurfaceParameter(1, "surface", "the cerebellum surface file"); OptionalParameter* cerebCorrAreasOpt = cerebSurfaceOpt->createOptionalParameter(2, "-corrected-areas", "vertex areas to use instead of computing them from the surface"); cerebCorrAreasOpt->addMetricParameter(1, "area-metric", "the corrected vertex areas, as a metric"); OptionalParameter* roiOpt = ret->createOptionalParameter(12, "-cifti-roi", "search only within regions of interest"); roiOpt->addCiftiParameter(1, "roi-cifti", "the regions to search within, as a cifti file"); ret->createOptionalParameter(13, "-merged-volume", "treat volume components as if they were a single component"); OptionalParameter* sizeRatioOpt = ret->createOptionalParameter(15, "-size-ratio", "ignore clusters smaller than a given fraction of the largest cluster in the structure"); sizeRatioOpt->addDoubleParameter(1, "surface-ratio", "fraction of the structure's largest cluster area"); sizeRatioOpt->addDoubleParameter(2, "volume-ratio", "fraction of the structure's largest cluster volume"); OptionalParameter* distanceOpt = ret->createOptionalParameter(16, "-distance", "ignore clusters further than a given distance from the largest cluster in the structure"); distanceOpt->addDoubleParameter(1, "surface-distance", "how far from the largest cluster a cluster can be, edge to edge, in mm"); distanceOpt->addDoubleParameter(2, "volume-distance", "how far from the largest cluster a cluster can be, edge to edge, in mm"); OptionalParameter* startOpt = ret->createOptionalParameter(14, "-start", "start labeling clusters from a value other than 1"); startOpt->addIntegerParameter(1, "startval", "the value to give the first cluster found"); ret->setHelpText( AString("Outputs a cifti file with nonzero integers for all brainordinates within a large enough cluster, and zeros elsewhere. ") + "The integers denote cluster membership (by default, first cluster found will use value 1, second cluster 2, etc). " + "The input cifti file must have a brain models mapping on the chosen dimension, columns for .dtseries, and either for .dconn. " + "The ROI should have a brain models mapping along columns, exactly matching the mapping of the chosen direction in the input file. " + "Data outside the ROI is ignored." ); return ret; } void AlgorithmCiftiFindClusters::useParameters(OperationParameters* myParams, ProgressObject* myProgObj) { CiftiFile* myCifti = myParams->getCifti(1); float surfThresh = (float)myParams->getDouble(2); float surfSize = (float)myParams->getDouble(3); float volThresh = (float)myParams->getDouble(4); float volSize = (float)myParams->getDouble(5); AString directionName = myParams->getString(6); int myDir; if (directionName == "ROW") { myDir = CiftiXML::ALONG_ROW; } else if (directionName == "COLUMN") { myDir = CiftiXML::ALONG_COLUMN; } else { throw AlgorithmException("incorrect string for direction, use ROW or COLUMN"); } CiftiFile* myCiftiOut = myParams->getOutputCifti(7); bool lessThan = myParams->getOptionalParameter(8)->m_present; SurfaceFile* myLeftSurf = NULL, *myRightSurf = NULL, *myCerebSurf = NULL; MetricFile* myLeftAreas = NULL, *myRightAreas = NULL, *myCerebAreas = NULL; OptionalParameter* leftSurfOpt = myParams->getOptionalParameter(9); if (leftSurfOpt->m_present) { myLeftSurf = leftSurfOpt->getSurface(1); OptionalParameter* leftCorrAreasOpt = leftSurfOpt->getOptionalParameter(2); if (leftCorrAreasOpt->m_present) { myLeftAreas = leftCorrAreasOpt->getMetric(1); } } OptionalParameter* rightSurfOpt = myParams->getOptionalParameter(10); if (rightSurfOpt->m_present) { myRightSurf = rightSurfOpt->getSurface(1); OptionalParameter* rightCorrAreasOpt = rightSurfOpt->getOptionalParameter(2); if (rightCorrAreasOpt->m_present) { myRightAreas = rightCorrAreasOpt->getMetric(1); } } OptionalParameter* cerebSurfOpt = myParams->getOptionalParameter(11); if (cerebSurfOpt->m_present) { myCerebSurf = cerebSurfOpt->getSurface(1); OptionalParameter* cerebCorrAreasOpt = cerebSurfOpt->getOptionalParameter(2); if (cerebCorrAreasOpt->m_present) { myCerebAreas = cerebCorrAreasOpt->getMetric(1); } } CiftiFile* roiCifti = NULL; OptionalParameter* roiOpt = myParams->getOptionalParameter(12); if (roiOpt->m_present) { roiCifti = roiOpt->getCifti(1); } bool mergedVol = myParams->getOptionalParameter(13)->m_present; OptionalParameter* startOpt = myParams->getOptionalParameter(14); int startVal = 1; if (startOpt->m_present) { startVal = (int)startOpt->getInteger(1); } OptionalParameter* sizeRatioOpt = myParams->getOptionalParameter(15); float surfSizeRatio = -1.0f, volSizeRatio = -1.0f; if (sizeRatioOpt->m_present) { surfSizeRatio = sizeRatioOpt->getDouble(1); volSizeRatio = sizeRatioOpt->getDouble(2); if (surfSizeRatio <= 0.0f && volSizeRatio <= 0.0f) { throw AlgorithmException("at least one size ratio must be positive"); } } OptionalParameter* distanceOpt = myParams->getOptionalParameter(16); float surfDistCutoff = -1.0f, volDistCutoff = -1.0f; if (distanceOpt->m_present) { surfDistCutoff = distanceOpt->getDouble(1); volDistCutoff = distanceOpt->getDouble(2); if (surfDistCutoff <= 0.0f && volDistCutoff <= 0.0f) { throw AlgorithmException("at least one distance cutoff must be positive"); } } AlgorithmCiftiFindClusters(myProgObj, myCifti, surfThresh, surfSize, volThresh, volSize, myDir, myCiftiOut, lessThan, myLeftSurf, myLeftAreas, myRightSurf, myRightAreas, myCerebSurf, myCerebAreas, roiCifti, mergedVol, startVal, NULL, surfSizeRatio, volSizeRatio, surfDistCutoff, volDistCutoff); } AlgorithmCiftiFindClusters::AlgorithmCiftiFindClusters(ProgressObject* myProgObj, const CiftiFile* myCifti, const float& surfThresh, const float& surfSize, const float& volThresh, const float& volSize, const int& myDir, CiftiFile* myCiftiOut, const bool& lessThan, const SurfaceFile* myLeftSurf, const MetricFile* myLeftAreas, const SurfaceFile* myRightSurf, const MetricFile* myRightAreas, const SurfaceFile* myCerebSurf, const MetricFile* myCerebAreas, const CiftiFile* roiCifti, const bool& mergedVol, const int& startVal, int* endVal, const float& surfSizeRatio, const float& volSizeRatio, const float& surfDistCutoff, const float& volDistCutoff) : AbstractAlgorithm(myProgObj) { LevelProgress myProgress(myProgObj); if (startVal == 0) { throw AlgorithmException("0 is not a valid cluster marking start value"); } const CiftiXML& myXML = myCifti->getCiftiXML(); if (myXML.getNumberOfDimensions() != 2) throw AlgorithmException("cifti separate only supported on 2D cifti"); if (myDir >= myXML.getNumberOfDimensions() || myDir < 0) throw AlgorithmException("direction invalid for input cifti"); if (myXML.getMappingType(myDir) != CiftiMappingType::BRAIN_MODELS) { throw AlgorithmException("specified direction does not contain brainordinates"); } const CiftiBrainModelsMap& myBrainMap = myXML.getBrainModelsMap(myDir); if (roiCifti != NULL && myBrainMap != *(roiCifti->getCiftiXML().getMap(CiftiXML::ALONG_COLUMN))) { throw AlgorithmException("along-column mapping of roi cifti does not match the smoothing direction of the input cifti"); } vector surfaceList = myBrainMap.getSurfaceStructureList(); int markVal = startVal; for (int whichStruct = 0; whichStruct < (int)surfaceList.size(); ++whichStruct) {//sanity check surfaces const SurfaceFile* mySurf = NULL; const MetricFile* myAreas = NULL; AString surfType; switch (surfaceList[whichStruct]) { case StructureEnum::CORTEX_LEFT: mySurf = myLeftSurf; myAreas = myLeftAreas; surfType = "left"; break; case StructureEnum::CORTEX_RIGHT: mySurf = myRightSurf; myAreas = myRightAreas; surfType = "right"; break; case StructureEnum::CEREBELLUM: mySurf = myCerebSurf; myAreas = myCerebAreas; surfType = "cerebellum"; break; default: throw AlgorithmException("found surface model with incorrect type: " + StructureEnum::toName(surfaceList[whichStruct])); break; } if (mySurf == NULL) { throw AlgorithmException(surfType + " surface required but not provided"); } if (mySurf->getNumberOfNodes() != myBrainMap.getSurfaceNumberOfNodes(surfaceList[whichStruct])) { throw AlgorithmException(surfType + " surface has the wrong number of vertices"); } if (myAreas != NULL && myAreas->getNumberOfNodes() != mySurf->getNumberOfNodes()) { throw AlgorithmException(surfType + " corrected areas metric has the wrong number of vertices"); } } myCiftiOut->setCiftiXML(myXML); for (int whichStruct = 0; whichStruct < (int)surfaceList.size(); ++whichStruct) { const SurfaceFile* mySurf = NULL; const MetricFile* myAreas = NULL; switch (surfaceList[whichStruct]) { case StructureEnum::CORTEX_LEFT: mySurf = myLeftSurf; myAreas = myLeftAreas; break; case StructureEnum::CORTEX_RIGHT: mySurf = myRightSurf; myAreas = myRightAreas; break; case StructureEnum::CEREBELLUM: mySurf = myCerebSurf; myAreas = myCerebAreas; break; default: break; } MetricFile myMetric, myRoi, myMetricOut; AlgorithmCiftiSeparate(NULL, myCifti, myDir, surfaceList[whichStruct], &myMetric, &myRoi); if (roiCifti != NULL) {//due to above testing, we know the structure mask is the same, so just overwrite the ROI from the mask AlgorithmCiftiSeparate(NULL, roiCifti, CiftiXML::ALONG_COLUMN, surfaceList[whichStruct], &myRoi); } AlgorithmMetricFindClusters(NULL, mySurf, &myMetric, surfThresh, surfSize, &myMetricOut, lessThan, &myRoi, myAreas, -1, markVal, &markVal, surfSizeRatio, surfDistCutoff); AlgorithmCiftiReplaceStructure(NULL, myCiftiOut, myDir, surfaceList[whichStruct], &myMetricOut); } if (mergedVol) { if (myBrainMap.hasVolumeData()) { VolumeFile myVol, myRoi, myVolOut; int64_t offset[3]; AlgorithmCiftiSeparate(NULL, myCifti, myDir, &myVol, offset, &myRoi, true); if (roiCifti != NULL) {//due to above testing, we know the structure mask is the same, so just overwrite the ROI from the mask AlgorithmCiftiSeparate(NULL, roiCifti, CiftiXMLOld::ALONG_COLUMN, &myRoi, offset, NULL, true); } AlgorithmVolumeFindClusters(NULL, &myVol, volThresh, volSize, &myVolOut, lessThan, &myRoi, -1, markVal, &markVal, volSizeRatio, volDistCutoff); AlgorithmCiftiReplaceStructure(NULL, myCiftiOut, myDir, &myVolOut, true); } } else { vector volumeList = myBrainMap.getVolumeStructureList(); for (int whichStruct = 0; whichStruct < (int)volumeList.size(); ++whichStruct) { VolumeFile myVol, myRoi, myVolOut; int64_t offset[3]; AlgorithmCiftiSeparate(NULL, myCifti, myDir, volumeList[whichStruct], &myVol, offset, &myRoi, true); if (roiCifti != NULL) {//due to above testing, we know the structure mask is the same, so just overwrite the ROI from the mask AlgorithmCiftiSeparate(NULL, roiCifti, CiftiXML::ALONG_COLUMN, volumeList[whichStruct], &myRoi, offset, NULL, true); } AlgorithmVolumeFindClusters(NULL, &myVol, volThresh, volSize, &myVolOut, lessThan, &myRoi, -1, markVal, &markVal, volSizeRatio, volDistCutoff); AlgorithmCiftiReplaceStructure(NULL, myCiftiOut, myDir, volumeList[whichStruct], &myVolOut, true); } } if (endVal != NULL) *endVal = markVal; } float AlgorithmCiftiFindClusters::getAlgorithmInternalWeight() { return 1.0f;//override this if needed, if the progress bar isn't smooth } float AlgorithmCiftiFindClusters::getSubAlgorithmWeight() { //return AlgorithmInsertNameHere::getAlgorithmWeight();//if you use a subalgorithm return 0.0f; } connectome-workbench-1.2.3+git41-gc4c6c90/src/Algorithms/AlgorithmCiftiFindClusters.h000066400000000000000000000050311300200146000302600ustar00rootroot00000000000000#ifndef __ALGORITHM_CIFTI_FIND_CLUSTERS_H__ #define __ALGORITHM_CIFTI_FIND_CLUSTERS_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 "AbstractAlgorithm.h" namespace caret { class AlgorithmCiftiFindClusters : public AbstractAlgorithm { AlgorithmCiftiFindClusters(); protected: static float getSubAlgorithmWeight(); static float getAlgorithmInternalWeight(); public: AlgorithmCiftiFindClusters(ProgressObject* myProgObj, const CiftiFile* myCifti, const float& surfThresh, const float& surfSize, const float& volThresh, const float& volSize, const int& myDir, CiftiFile* myCiftiOut, const bool& lessThan = false, const SurfaceFile* myLeftSurf = NULL, const MetricFile* myLeftAreas = NULL, const SurfaceFile* myRightSurf = NULL, const MetricFile* myRightAreas = NULL, const SurfaceFile* myCerebSurf = NULL, const MetricFile* myCerebAreas = NULL, const CiftiFile* roiCifti = NULL, const bool& mergedVol = false, const int& startVal = 1, int* endVal = NULL, const float& surfSizeRatio = -1.0f, const float& volSizeRatio = -1.0f, const float& surfDistCutoff = -1.0f, const float& volDistCutoff = -1.0f); static OperationParameters* getParameters(); static void useParameters(OperationParameters* myParams, ProgressObject* myProgObj); static AString getCommandSwitch(); static AString getShortDescription(); }; typedef TemplateAutoOperation AutoAlgorithmCiftiFindClusters; } #endif //__ALGORITHM_CIFTI_FIND_CLUSTERS_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Algorithms/AlgorithmCiftiGradient.cxx000066400000000000000000000401541300200146000277700ustar00rootroot00000000000000/*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 "AlgorithmCiftiGradient.h" #include "AlgorithmException.h" #include "AlgorithmMetricGradient.h" #include "AlgorithmVolumeGradient.h" #include "CiftiFile.h" #include "MetricFile.h" #include "VolumeFile.h" #include "SurfaceFile.h" #include "AlgorithmCiftiSeparate.h" #include "AlgorithmCiftiReplaceStructure.h" #include using namespace caret; using namespace std; AString AlgorithmCiftiGradient::getCommandSwitch() { return "-cifti-gradient"; } AString AlgorithmCiftiGradient::getShortDescription() { return "TAKE GRADIENT OF A CIFTI FILE"; } OperationParameters* AlgorithmCiftiGradient::getParameters() { OperationParameters* ret = new OperationParameters(); ret->addCiftiParameter(1, "cifti", "the input cifti"); ret->addStringParameter(2, "direction", "which dimension to take the gradient along, ROW or COLUMN"); ret->addCiftiOutputParameter(3, "cifti-out", "the output cifti"); OptionalParameter* leftSurfOpt = ret->createOptionalParameter(4, "-left-surface", "specify the left surface to use"); leftSurfOpt->addSurfaceParameter(1, "surface", "the left surface file"); OptionalParameter* leftCorrAreasOpt = leftSurfOpt->createOptionalParameter(2, "-left-corrected-areas", "vertex areas to use instead of computing them from the left surface"); leftCorrAreasOpt->addMetricParameter(1, "area-metric", "the corrected vertex areas, as a metric"); OptionalParameter* rightSurfOpt = ret->createOptionalParameter(5, "-right-surface", "specify the right surface to use"); rightSurfOpt->addSurfaceParameter(1, "surface", "the right surface file"); OptionalParameter* rightCorrAreasOpt = rightSurfOpt->createOptionalParameter(2, "-right-corrected-areas", "vertex areas to use instead of computing them from the right surface"); rightCorrAreasOpt->addMetricParameter(1, "area-metric", "the corrected vertex areas, as a metric"); OptionalParameter* cerebSurfOpt = ret->createOptionalParameter(6, "-cerebellum-surface", "specify the cerebellum surface to use"); cerebSurfOpt->addSurfaceParameter(1, "surface", "the cerebellum surface file"); OptionalParameter* cerebCorrAreasOpt = cerebSurfOpt->createOptionalParameter(2, "-cerebellum-corrected-areas", "vertex areas to use instead of computing them from the cerebellum surface"); cerebCorrAreasOpt->addMetricParameter(1, "area-metric", "the corrected vertex areas, as a metric"); OptionalParameter* presmoothSurfOpt = ret->createOptionalParameter(7, "-surface-presmooth", "smooth on the surface before computing the gradient"); presmoothSurfOpt->addDoubleParameter(1, "surface-kernel", "the sigma for the gaussian surface smoothing kernel, in mm"); OptionalParameter* presmoothVolOpt = ret->createOptionalParameter(8, "-volume-presmooth", "smooth on the surface before computing the gradient"); presmoothVolOpt->addDoubleParameter(1, "volume-kernel", "the sigma for the gaussian volume smoothing kernel, in mm"); ret->createOptionalParameter(9, "-average-output", "output the average of the gradient magnitude maps instead of each gradient map separately"); OptionalParameter* vectorOpt = ret->createOptionalParameter(10, "-vectors", "output gradient vectors"); vectorOpt->addCiftiOutputParameter(1, "vectors-out", "the vectors, as a dscalar file"); ret->setHelpText( AString("Performs gradient calculation on each component of the cifti file, and optionally averages the resulting gradients. ") + "The -vectors and -average-output options may not be used together. " + "You must specify a surface for each surface structure in the cifti file. The COLUMN direction should be faster, and is the " + "direction that works on dtseries. For dconn, you probably want ROW, unless you are using -average-output." ); return ret; } void AlgorithmCiftiGradient::useParameters(OperationParameters* myParams, ProgressObject* myProgObj) { CiftiFile* myCifti = myParams->getCifti(1); AString directionName = myParams->getString(2); int myDir; if (directionName == "ROW") { myDir = CiftiXMLOld::ALONG_ROW; } else if (directionName == "COLUMN") { myDir = CiftiXMLOld::ALONG_COLUMN; } else { throw AlgorithmException("incorrect string for direction, use ROW or COLUMN"); } CiftiFile* myCiftiOut = myParams->getOutputCifti(3); SurfaceFile* myLeftSurf = NULL, *myRightSurf = NULL, *myCerebSurf = NULL; MetricFile* myLeftAreas = NULL, *myRightAreas = NULL, *myCerebAreas = NULL; OptionalParameter* leftSurfOpt = myParams->getOptionalParameter(4); if (leftSurfOpt->m_present) { myLeftSurf = leftSurfOpt->getSurface(1); OptionalParameter* leftCorrAreasOpt = leftSurfOpt->getOptionalParameter(2); if (leftCorrAreasOpt->m_present) { myLeftAreas = leftCorrAreasOpt->getMetric(1); } } OptionalParameter* rightSurfOpt = myParams->getOptionalParameter(5); if (rightSurfOpt->m_present) { myRightSurf = rightSurfOpt->getSurface(1); OptionalParameter* rightCorrAreasOpt = rightSurfOpt->getOptionalParameter(2); if (rightCorrAreasOpt->m_present) { myRightAreas = rightCorrAreasOpt->getMetric(1); } } OptionalParameter* cerebSurfOpt = myParams->getOptionalParameter(6); if (cerebSurfOpt->m_present) { myCerebSurf = cerebSurfOpt->getSurface(1); OptionalParameter* cerebCorrAreasOpt = cerebSurfOpt->getOptionalParameter(2); if (cerebCorrAreasOpt->m_present) { myCerebAreas = cerebCorrAreasOpt->getMetric(1); } } float surfKern = -1.0f; OptionalParameter* presmoothSurfOpt = myParams->getOptionalParameter(7); if (presmoothSurfOpt->m_present) { surfKern = (float)presmoothSurfOpt->getDouble(1); } float volKern = -1.0f; OptionalParameter* presmoothVolOpt = myParams->getOptionalParameter(8); if (presmoothVolOpt->m_present) { volKern = (float)presmoothVolOpt->getDouble(1); } bool outputAverage = myParams->getOptionalParameter(9)->m_present; CiftiFile* ciftiVectorsOut = NULL; OptionalParameter* vectorOpt = myParams->getOptionalParameter(10); if (vectorOpt->m_present) { ciftiVectorsOut = vectorOpt->getOutputCifti(1); } AlgorithmCiftiGradient(myProgObj, myCifti, myDir, myCiftiOut, surfKern, volKern, myLeftSurf, myRightSurf, myCerebSurf, outputAverage, myLeftAreas, myRightAreas, myCerebAreas, ciftiVectorsOut); } AlgorithmCiftiGradient::AlgorithmCiftiGradient(ProgressObject* myProgObj, const CiftiFile* myCifti, const int& myDir, CiftiFile* myCiftiOut, const float& surfKern, const float& volKern, SurfaceFile* myLeftSurf, SurfaceFile* myRightSurf, SurfaceFile* myCerebSurf, bool outputAverage, const MetricFile* myLeftAreas, const MetricFile* myRightAreas, const MetricFile* myCerebAreas, CiftiFile* ciftiVectorsOut) : AbstractAlgorithm(myProgObj) { LevelProgress myProgress(myProgObj); const CiftiXML& myXML = myCifti->getCiftiXML(); CiftiXML myNewXML = myXML, myVecXML = myXML; if (myXML.getNumberOfDimensions() != 2) { throw AlgorithmException("cifti gradient only supports 2D cifti"); } if (myXML.getMappingType(myDir) != CiftiMappingType::BRAIN_MODELS) { throw AlgorithmException("specified direction does not contain brainordinates"); } if (outputAverage && ciftiVectorsOut != NULL) { throw AlgorithmException("outputting gradient vectors while averaging gradient magnitude is not supported"); } const CiftiBrainModelsMap& myDenseMap = myXML.getBrainModelsMap(myDir); vector surfaceList = myDenseMap.getSurfaceStructureList(), volumeList = myDenseMap.getVolumeStructureList(); if (outputAverage) { if (myDir == CiftiXML::ALONG_ROW) {//dscalar always has brainordinates on columns, so flip the mappings myNewXML.setMap(CiftiXML::ALONG_COLUMN, *(myNewXML.getMap(CiftiXML::ALONG_ROW))); } CiftiScalarsMap magMap; magMap.setLength(1); magMap.setMapName(0, "gradient average"); myNewXML.setMap(CiftiXML::ALONG_ROW, magMap); } else { if (myDir == CiftiXML::ALONG_ROW) {//dscalar always has brainordinates on columns, so flip the mappings myVecXML.setMap(CiftiXML::ALONG_COLUMN, *(myVecXML.getMap(CiftiXML::ALONG_ROW))); } CiftiScalarsMap vecMap; vecMap.setLength(3 * myXML.getDimensionLength(1 - myDir)); myVecXML.setMap(CiftiXML::ALONG_ROW, vecMap); } for (int whichStruct = 0; whichStruct < (int)surfaceList.size(); ++whichStruct) {//sanity check surfaces SurfaceFile* mySurf = NULL; const MetricFile* myAreas = NULL; AString surfType; switch (surfaceList[whichStruct]) { case StructureEnum::CORTEX_LEFT: mySurf = myLeftSurf; myAreas = myLeftAreas; surfType = "left"; break; case StructureEnum::CORTEX_RIGHT: mySurf = myRightSurf; myAreas = myRightAreas; surfType = "right"; break; case StructureEnum::CEREBELLUM: mySurf = myCerebSurf; myAreas = myCerebAreas; surfType = "cerebellum"; break; default: throw AlgorithmException("found surface model with incorrect type: " + StructureEnum::toName(surfaceList[whichStruct])); break; } if (mySurf == NULL) { throw AlgorithmException(surfType + " surface required but not provided"); } if (mySurf->getNumberOfNodes() != myDenseMap.getSurfaceNumberOfNodes(surfaceList[whichStruct])) { throw AlgorithmException(surfType + " surface has the wrong number of vertices"); } checkStructureMatch(mySurf, surfaceList[whichStruct], "surface file", "the argument expects"); if (myAreas != NULL) { if (myAreas->getNumberOfNodes() != mySurf->getNumberOfNodes()) { throw AlgorithmException(surfType + " surface and vertex area metric have different number of vertices"); } checkStructureMatch(myAreas, surfaceList[whichStruct], "vertex area metric", "the argument expects"); } } myCiftiOut->setCiftiXML(myNewXML); if (ciftiVectorsOut != NULL) { ciftiVectorsOut->setCiftiXML(myVecXML); } for (int whichStruct = 0; whichStruct < (int)surfaceList.size(); ++whichStruct) { SurfaceFile* mySurf = NULL; const MetricFile* myAreas = NULL; switch (surfaceList[whichStruct]) { case StructureEnum::CORTEX_LEFT: mySurf = myLeftSurf; myAreas = myLeftAreas; break; case StructureEnum::CORTEX_RIGHT: mySurf = myRightSurf; myAreas = myRightAreas; break; case StructureEnum::CEREBELLUM: mySurf = myCerebSurf; myAreas = myCerebAreas; break; default: break; } MetricFile myMetric, myRoi, myMetricOut, vectorsOut, *vectorPtr = NULL; if (ciftiVectorsOut != NULL) vectorPtr = &vectorsOut; AlgorithmCiftiSeparate(NULL, myCifti, myDir, surfaceList[whichStruct], &myMetric, &myRoi); AlgorithmMetricGradient(NULL, mySurf, &myMetric, &myMetricOut, vectorPtr, surfKern, &myRoi, false, -1, myAreas); if (outputAverage) { int numNodes = myMetricOut.getNumberOfNodes(), numCols = myMetricOut.getNumberOfColumns(); vector accum(numNodes, 0.0);//use double for numerical stability for (int i = 0; i < numCols; ++i) { const float* column = myMetricOut.getValuePointerForColumn(i); for (int j = 0; j < numNodes; ++j) { accum[j] += column[j]; } } vector temparray(numNodes);//copy result into float array so it can be put into a metric, and then into cifti (yes, really) for (int i = 0; i < numNodes; ++i) { temparray[i] = (float)(accum[i] / numCols); } myMetricOut.setNumberOfNodesAndColumns(numNodes, 1); myMetricOut.setValuesForColumn(0, temparray.data()); AlgorithmCiftiReplaceStructure(NULL, myCiftiOut, CiftiXML::ALONG_COLUMN, surfaceList[whichStruct], &myMetricOut);//average always outputs a dscalar, so always along column } else { AlgorithmCiftiReplaceStructure(NULL, myCiftiOut, myDir, surfaceList[whichStruct], &myMetricOut); if (ciftiVectorsOut != NULL) {//is always a dscalar, so always use column AlgorithmCiftiReplaceStructure(NULL, ciftiVectorsOut, CiftiXML::ALONG_COLUMN, surfaceList[whichStruct], &vectorsOut); } } } for (int whichStruct = 0; whichStruct < (int)volumeList.size(); ++whichStruct) { VolumeFile myVol, myRoi, myVolOut, vecVolOut, *vecVolPtr = NULL; if (ciftiVectorsOut != NULL) vecVolPtr = &vecVolOut; int64_t offset[3]; AlgorithmCiftiSeparate(NULL, myCifti, myDir, volumeList[whichStruct], &myVol, offset, &myRoi, true); AlgorithmVolumeGradient(NULL, &myVol, &myVolOut, volKern, &myRoi, vecVolPtr); if (outputAverage) { vector myDims; myVolOut.getDimensions(myDims); int64_t frameSize = myDims[0] * myDims[1] * myDims[2]; vector accum(frameSize, 0.0); for (int64_t i = 0; i < myDims[3]; ++i) { const float* myFrame = myVolOut.getFrame(i); for (int64_t j = 0; j < frameSize; ++j) { accum[j] += myFrame[j]; } } vector temparray(frameSize); for (int64_t i = 0; i < frameSize; ++i) { temparray[i] = (float)(accum[i] / myDims[3]); } vector newDims = myDims; newDims.resize(3); myVolOut.reinitialize(newDims, myVol.getSform()); myVolOut.setFrame(temparray.data()); AlgorithmCiftiReplaceStructure(NULL, myCiftiOut, CiftiXML::ALONG_COLUMN, volumeList[whichStruct], &myVolOut, true); } else { AlgorithmCiftiReplaceStructure(NULL, myCiftiOut, myDir, volumeList[whichStruct], &myVolOut, true); if (ciftiVectorsOut != NULL) { AlgorithmCiftiReplaceStructure(NULL, ciftiVectorsOut, CiftiXML::ALONG_COLUMN, volumeList[whichStruct], &vecVolOut, true); } } } } float AlgorithmCiftiGradient::getAlgorithmInternalWeight() { return 1.0f;//override this if needed, if the progress bar isn't smooth } float AlgorithmCiftiGradient::getSubAlgorithmWeight() { //return AlgorithmInsertNameHere::getAlgorithmWeight();//if you use a subalgorithm return 0.0f; } connectome-workbench-1.2.3+git41-gc4c6c90/src/Algorithms/AlgorithmCiftiGradient.h000066400000000000000000000042141300200146000274120ustar00rootroot00000000000000#ifndef __ALGORITHM_CIFTI_GRADIENT_H__ #define __ALGORITHM_CIFTI_GRADIENT_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 "AbstractAlgorithm.h" namespace caret { class AlgorithmCiftiGradient : public AbstractAlgorithm { AlgorithmCiftiGradient(); protected: static float getSubAlgorithmWeight(); static float getAlgorithmInternalWeight(); public: AlgorithmCiftiGradient(ProgressObject* myProgObj, const CiftiFile* myCifti, const int& myDir, CiftiFile* myCiftiOut, const float& surfKern = -1.0f, const float& volKern = -1.0f, SurfaceFile* myLeftSurf = NULL, SurfaceFile* myRightSurf = NULL, SurfaceFile* myCerebSurf = NULL, bool outputAverage = false, const MetricFile* myLeftAreas = NULL, const MetricFile* myRightAreas = NULL, const MetricFile* myCerebAreas = NULL, CiftiFile* ciftiVectorsOut = NULL); static OperationParameters* getParameters(); static void useParameters(OperationParameters* myParams, ProgressObject* myProgObj); static AString getCommandSwitch(); static AString getShortDescription(); }; typedef TemplateAutoOperation AutoAlgorithmCiftiGradient; } #endif //__ALGORITHM_CIFTI_GRADIENT_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Algorithms/AlgorithmCiftiLabelAdjacency.cxx000066400000000000000000000251041300200146000310520ustar00rootroot00000000000000/*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 "AlgorithmCiftiLabelAdjacency.h" #include "AlgorithmException.h" #include "AlgorithmCiftiParcellate.h" #include "AlgorithmCiftiSeparate.h" //for cropped volume space #include "CiftiFile.h" #include "SurfaceFile.h" #include "TopologyHelper.h" using namespace caret; using namespace std; AString AlgorithmCiftiLabelAdjacency::getCommandSwitch() { return "-cifti-label-adjacency"; } AString AlgorithmCiftiLabelAdjacency::getShortDescription() { return "MAKE ADJACENCY MATRIX OF A CIFTI LABEL FILE"; } OperationParameters* AlgorithmCiftiLabelAdjacency::getParameters() { OperationParameters* ret = new OperationParameters(); ret->addCiftiParameter(1, "label-in", "the input cifti label file"); ret->addCiftiOutputParameter(2, "adjacency-out", "the output cifti pconn adjacency matrix"); OptionalParameter* leftSurfOpt = ret->createOptionalParameter(3, "-left-surface", "specify the left surface to use"); leftSurfOpt->addSurfaceParameter(1, "surface", "the left surface file"); OptionalParameter* rightSurfOpt = ret->createOptionalParameter(4, "-right-surface", "specify the right surface to use"); rightSurfOpt->addSurfaceParameter(1, "surface", "the right surface file"); OptionalParameter* cerebSurfaceOpt = ret->createOptionalParameter(5, "-cerebellum-surface", "specify the cerebellum surface to use"); cerebSurfaceOpt->addSurfaceParameter(1, "surface", "the cerebellum surface file"); ret->setHelpText( AString("Find face-adjacent voxels and connected vertices that have different label values, and count them for each pair. ") + "Put the resulting counts into a parcellated connectivity file, with the diagonal being zero. " + "This gives a rough estimate of how long or expansive the border between two labels is." ); return ret; } void AlgorithmCiftiLabelAdjacency::useParameters(OperationParameters* myParams, ProgressObject* myProgObj) { CiftiFile* myLabelIn = myParams->getCifti(1); CiftiFile* myAdjOut = myParams->getOutputCifti(2); SurfaceFile* myLeftSurf = NULL; OptionalParameter* leftSurfOpt = myParams->getOptionalParameter(3); if (leftSurfOpt->m_present) { myLeftSurf = leftSurfOpt->getSurface(1); } SurfaceFile* myRightSurf = NULL; OptionalParameter* rightSurfOpt = myParams->getOptionalParameter(4); if (rightSurfOpt->m_present) { myRightSurf = rightSurfOpt->getSurface(1); } SurfaceFile* myCerebSurf = NULL; OptionalParameter* cerebSurfOpt = myParams->getOptionalParameter(5); if (cerebSurfOpt->m_present) { myCerebSurf = cerebSurfOpt->getSurface(1); } AlgorithmCiftiLabelAdjacency(myProgObj, myLabelIn, myAdjOut, myLeftSurf, myRightSurf, myCerebSurf); } AlgorithmCiftiLabelAdjacency::AlgorithmCiftiLabelAdjacency(ProgressObject* myProgObj, const CiftiFile* myLabelIn, CiftiFile* myAdjOut, const SurfaceFile* myLeftSurf, const SurfaceFile* myRightSurf, const SurfaceFile* myCerebSurf) : AbstractAlgorithm(myProgObj) { LevelProgress myProgress(myProgObj); const CiftiXML& myLabelXML = myLabelIn->getCiftiXML(); if (myLabelXML.getNumberOfDimensions() != 2 || myLabelXML.getMappingType(CiftiXML::ALONG_ROW) != CiftiMappingType::LABELS || myLabelXML.getMappingType(CiftiXML::ALONG_COLUMN) != CiftiMappingType::BRAIN_MODELS) { throw AlgorithmException("input cifti label file has the wrong mapping types"); } const CiftiBrainModelsMap& myDenseMap = myLabelXML.getBrainModelsMap(CiftiXML::ALONG_COLUMN); vector surfaceList = myDenseMap.getSurfaceStructureList(); for (int whichStruct = 0; whichStruct < (int)surfaceList.size(); ++whichStruct) {//sanity check surfaces const SurfaceFile* mySurf = NULL; AString surfType; switch (surfaceList[whichStruct]) { case StructureEnum::CORTEX_LEFT: mySurf = myLeftSurf; surfType = "left"; break; case StructureEnum::CORTEX_RIGHT: mySurf = myRightSurf; surfType = "right"; break; case StructureEnum::CEREBELLUM: mySurf = myCerebSurf; surfType = "cerebellum"; break; default: throw AlgorithmException("found surface model with unexpected type: " + StructureEnum::toName(surfaceList[whichStruct])); break; } if (mySurf == NULL) { throw AlgorithmException(surfType + " surface required but not provided"); } if (mySurf->getNumberOfNodes() != myDenseMap.getSurfaceNumberOfNodes(surfaceList[whichStruct])) { throw AlgorithmException(surfType + " surface has the wrong number of vertices"); } } vector indexToParcel; CiftiParcelsMap myParcelMap = AlgorithmCiftiParcellate::parcellateMapping(myLabelIn, myDenseMap, indexToParcel); int numParcels = myParcelMap.getLength(); if (numParcels == 0) throw AlgorithmException("no labels found, output would be empty"); CiftiXML outXML; outXML.setNumberOfDimensions(2); outXML.setMap(CiftiXML::ALONG_ROW, myParcelMap); outXML.setMap(CiftiXML::ALONG_COLUMN, myParcelMap); myAdjOut->setCiftiXML(outXML); vector > adjCount(numParcels, vector(numParcels, 0));//count as int, translate back to float when writing for (int whichStruct = 0; whichStruct < (int)surfaceList.size(); ++whichStruct) { const SurfaceFile* mySurf = NULL; switch (surfaceList[whichStruct]) { case StructureEnum::CORTEX_LEFT: mySurf = myLeftSurf; break; case StructureEnum::CORTEX_RIGHT: mySurf = myRightSurf; break; case StructureEnum::CEREBELLUM: mySurf = myCerebSurf; break; default: throw AlgorithmException("found surface model with unexpected type: " + StructureEnum::toName(surfaceList[whichStruct])); break; } CaretPointer myHelp = mySurf->getTopologyHelper(); int numNodes = mySurf->getNumberOfNodes(); for (int i = 0; i < numNodes - 1; ++i)//to avoid double counting, only count pairs that are in ascending order - this means the last node won't have any valid edges { int64_t baseIndex = myDenseMap.getIndexForNode(i, surfaceList[whichStruct]); if (baseIndex < 0) continue; int baseLabel = indexToParcel[baseIndex];//translate on the fly, to do separate we would need to put indexToParcel into a temporary CiftiFile if (baseLabel < 0) continue; const vector& neighbors = myHelp->getNodeNeighbors(i); int numNeighbors = (int)neighbors.size(); for (int j = 0; j < numNeighbors; ++j) { if (neighbors[j] > i) { int64_t neighIndex = myDenseMap.getIndexForNode(neighbors[j], surfaceList[whichStruct]); if (neighIndex < 0) continue; int neighLabel = indexToParcel[neighIndex]; if (neighLabel < 0) continue; if (baseLabel != neighLabel) { ++adjCount[baseLabel][neighLabel]; ++adjCount[neighLabel][baseLabel]; } } } } } if (myDenseMap.hasVolumeData()) { int64_t dims[3], offset[3];//all we really want is offset and dims, to avoid scanning the original FOV vector > sform; AlgorithmCiftiSeparate::getCroppedVolSpaceAll(myLabelIn, CiftiXML::ALONG_COLUMN, dims, sform, offset); int64_t stencil[9] = {1, 0, 0, 0, 1, 0, 0, 0, 1};//only forward differences, to avoid double counting int64_t ijk[3]; for (ijk[2] = offset[2]; ijk[2] < offset[2] + dims[2]; ++ijk[2]) { for (ijk[1] = offset[1]; ijk[1] < offset[1] + dims[1]; ++ijk[1]) { for (ijk[0] = offset[0]; ijk[0] < offset[0] + dims[0]; ++ijk[0]) { int64_t baseIndex = myDenseMap.getIndexForVoxel(ijk); if (baseIndex < 0) continue; int baseLabel = indexToParcel[baseIndex]; if (baseLabel < 0) continue; for (int neighbor = 0; neighbor < 9; neighbor += 3) { int64_t neighIndex = myDenseMap.getIndexForVoxel(ijk[0] + stencil[neighbor], ijk[1] + stencil[neighbor + 1], ijk[2] + stencil[neighbor + 2]); if (neighIndex < 0) continue; int neighLabel = indexToParcel[neighIndex]; if (neighLabel < 0) continue; if (baseLabel != neighLabel) { ++adjCount[baseLabel][neighLabel]; ++adjCount[neighLabel][baseLabel]; } } } } } } vector tempRow(numParcels); for (int i = 0; i < numParcels; ++i) { for (int j = 0; j < numParcels; ++j) { tempRow[j] = adjCount[i][j]; } myAdjOut->setRow(tempRow.data(), i); } } float AlgorithmCiftiLabelAdjacency::getAlgorithmInternalWeight() { return 1.0f;//override this if needed, if the progress bar isn't smooth } float AlgorithmCiftiLabelAdjacency::getSubAlgorithmWeight() { //return AlgorithmInsertNameHere::getAlgorithmWeight();//if you use a subalgorithm return 0.0f; } connectome-workbench-1.2.3+git41-gc4c6c90/src/Algorithms/AlgorithmCiftiLabelAdjacency.h000066400000000000000000000035311300200146000304770ustar00rootroot00000000000000#ifndef __ALGORITHM_CIFTI_LABEL_ADJACENCY_H__ #define __ALGORITHM_CIFTI_LABEL_ADJACENCY_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 "AbstractAlgorithm.h" namespace caret { class AlgorithmCiftiLabelAdjacency : public AbstractAlgorithm { AlgorithmCiftiLabelAdjacency(); protected: static float getSubAlgorithmWeight(); static float getAlgorithmInternalWeight(); public: AlgorithmCiftiLabelAdjacency(ProgressObject* myProgObj, const CiftiFile* myLabelIn, CiftiFile* myAdjOut, const SurfaceFile* myLeftSurf = NULL, const SurfaceFile* myRightSurf = NULL, const SurfaceFile* myCerebSurf = NULL); static OperationParameters* getParameters(); static void useParameters(OperationParameters* myParams, ProgressObject* myProgObj); static AString getCommandSwitch(); static AString getShortDescription(); }; typedef TemplateAutoOperation AutoAlgorithmCiftiLabelAdjacency; } #endif //__ALGORITHM_CIFTI_LABEL_ADJACENCY_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Algorithms/AlgorithmCiftiLabelToBorder.cxx000066400000000000000000000123351300200146000307130ustar00rootroot00000000000000/*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 "AlgorithmCiftiLabelToBorder.h" #include "AlgorithmException.h" #include "AlgorithmCiftiSeparate.h" #include "AlgorithmLabelToBorder.h" #include "CaretLogger.h" #include "BorderFile.h" #include "CiftiFile.h" #include "LabelFile.h" #include "SurfaceFile.h" using namespace caret; using namespace std; AString AlgorithmCiftiLabelToBorder::getCommandSwitch() { return "-cifti-label-to-border"; } AString AlgorithmCiftiLabelToBorder::getShortDescription() { return "DRAW BORDERS AROUND CIFTI LABELS"; } OperationParameters* AlgorithmCiftiLabelToBorder::getParameters() { OperationParameters* ret = new OperationParameters(); ret->addCiftiParameter(1, "cifti-in", "the input cifti dlabel file"); OptionalParameter* placeOpt = ret->createOptionalParameter(2, "-placement", "set how far along the edge border points are drawn"); placeOpt->addDoubleParameter(1, "fraction", "fraction along edge from inside vertex (default 0.33)"); OptionalParameter* columnOpt = ret->createOptionalParameter(3, "-column", "select a single column"); columnOpt->addStringParameter(1, "column", "the column number or name"); ParameterComponent* borderOpt = ret->createRepeatableParameter(4, "-border", "specify output file for a surface structure"); borderOpt->addSurfaceParameter(1, "surface", "the surface to use for neighbor and structure information"); borderOpt->addBorderOutputParameter(2, "border-out", "the output border file"); ret->setHelpText( AString("For each surface, takes the labels on the matching structure and draws borders around the labels. ") + "Use -column to only draw borders around one label map." ); return ret; } void AlgorithmCiftiLabelToBorder::useParameters(OperationParameters* myParams, ProgressObject* /*myProgObj*/) { CiftiFile* myCifti = myParams->getCifti(1); float placement = 0.33f; OptionalParameter* placeOpt = myParams->getOptionalParameter(2); if (placeOpt->m_present) { placement = (float)placeOpt->getDouble(1); } int64_t column = -1; OptionalParameter* columnOpt = myParams->getOptionalParameter(3); if (columnOpt->m_present) { //row is first dimension, 0D cifti won't load, so don't need a test column = myCifti->getCiftiXML().getMap(CiftiXML::ALONG_ROW)->getIndexFromNumberOrName(columnOpt->getString(2)); if (column < 0) { throw AlgorithmException("invalid column specified"); } } const vector& borderOpts = *(myParams->getRepeatableParameterInstances(4)); if (borderOpts.empty()) CaretLogWarning("no output requested from -cifti-label-to-border, command will do nothing"); for (int i = 0; i < (int)borderOpts.size(); ++i) { ParameterComponent& thisBorderOpt = *(borderOpts[i]); AlgorithmCiftiLabelToBorder(NULL, myCifti, thisBorderOpt.getSurface(1), thisBorderOpt.getOutputBorder(2), placement, column); } } AlgorithmCiftiLabelToBorder::AlgorithmCiftiLabelToBorder(ProgressObject* myProgObj, const CiftiFile* myCifti, const SurfaceFile* mySurf, BorderFile* borderOut, const float& placement, const int& column) : AbstractAlgorithm(myProgObj) { LevelProgress myProgress(myProgObj); const CiftiXML& myXML = myCifti->getCiftiXML(); if (myXML.getNumberOfDimensions() != 2) throw AlgorithmException("input cifti file should have 2 dimensions, has " + AString::number(myXML.getNumberOfDimensions())); if (myXML.getMappingType(CiftiXML::ALONG_COLUMN) != CiftiMappingType::BRAIN_MODELS) throw AlgorithmException("input cifti file does not have brain models mapping along column"); if (myXML.getMappingType(CiftiXML::ALONG_ROW) != CiftiMappingType::LABELS) throw AlgorithmException("input cifti file does not have labels mapping along row"); LabelFile tempLabel; AlgorithmCiftiSeparate(NULL, myCifti, CiftiXML::ALONG_COLUMN, mySurf->getStructure(), &tempLabel);//NOTE: bad surface structure errors are printed from this algorithm AlgorithmLabelToBorder(NULL, mySurf, &tempLabel, borderOut, placement, column); } float AlgorithmCiftiLabelToBorder::getAlgorithmInternalWeight() { return 1.0f;//override this if needed, if the progress bar isn't smooth } float AlgorithmCiftiLabelToBorder::getSubAlgorithmWeight() { //return AlgorithmInsertNameHere::getAlgorithmWeight();//if you use a subalgorithm return 0.0f; } connectome-workbench-1.2.3+git41-gc4c6c90/src/Algorithms/AlgorithmCiftiLabelToBorder.h000066400000000000000000000035071300200146000303410ustar00rootroot00000000000000#ifndef __ALGORITHM_CIFTI_LABEL_TO_BORDER_H__ #define __ALGORITHM_CIFTI_LABEL_TO_BORDER_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 AlgorithmCiftiLabelToBorder : public AbstractAlgorithm { AlgorithmCiftiLabelToBorder(); protected: static float getSubAlgorithmWeight(); static float getAlgorithmInternalWeight(); public: AlgorithmCiftiLabelToBorder(ProgressObject* myProgObj, const CiftiFile* myCifti, const SurfaceFile* mySurf, BorderFile* borderOut, const float& placement = 0.33f, const int& column = -1); static OperationParameters* getParameters(); static void useParameters(OperationParameters* myParams, ProgressObject* myProgObj); static AString getCommandSwitch(); static AString getShortDescription(); }; typedef TemplateAutoOperation AutoAlgorithmCiftiLabelToBorder; } #endif //__ALGORITHM_CIFTI_LABEL_TO_BORDER_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Algorithms/AlgorithmCiftiLabelToROI.cxx000066400000000000000000000265451300200146000301370ustar00rootroot00000000000000/*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 "AlgorithmCiftiLabelToROI.h" #include "AlgorithmException.h" #include "CaretLogger.h" #include "CiftiFile.h" #include "GiftiLabel.h" #include "GiftiLabelTable.h" #include using namespace caret; using namespace std; AString AlgorithmCiftiLabelToROI::getCommandSwitch() { return "-cifti-label-to-roi"; } AString AlgorithmCiftiLabelToROI::getShortDescription() { return "MAKE A CIFTI LABEL INTO AN ROI"; } OperationParameters* AlgorithmCiftiLabelToROI::getParameters() { OperationParameters* ret = new OperationParameters(); ret->addCiftiParameter(1, "label-in", "the input cifti label file"); ret->addCiftiOutputParameter(2, "scalar-out", "the output cifti scalar file"); OptionalParameter* nameOpt = ret->createOptionalParameter(3, "-name", "select label by name"); nameOpt->addStringParameter(1, "label-name", "the label name that you want an roi of"); OptionalParameter* keyOpt = ret->createOptionalParameter(4, "-key", "select label by key"); keyOpt->addIntegerParameter(1, "label-key", "the label key that you want an roi of"); OptionalParameter* mapOpt = ret->createOptionalParameter(5, "-map", "select a single label map to use"); mapOpt->addStringParameter(1, "map", "the map number or name"); ret->setHelpText( AString("For each map in , a map is created in where all locations labeled with or with a key of are given a value of 1, and all other locations are given 0. ") + "Exactly one of -name and -key must be specified. " + "Specify -map to use only one map from ." ); return ret; } void AlgorithmCiftiLabelToROI::useParameters(OperationParameters* myParams, ProgressObject* myProgObj) { CiftiFile* myCifti = myParams->getCifti(1); CiftiFile* myCiftiOut = myParams->getOutputCifti(2); bool nameMode = false; AString labelName; OptionalParameter* nameOpt = myParams->getOptionalParameter(3); if (nameOpt->m_present) { nameMode = true; labelName = nameOpt->getString(1); } int32_t labelKey; OptionalParameter* keyOpt = myParams->getOptionalParameter(4); if (keyOpt->m_present) { if (nameMode) throw AlgorithmException("-name and -key cannot be specified together"); labelKey = (int32_t)keyOpt->getInteger(1); } else { if (!nameMode) throw AlgorithmException("you must specify one of -name or -key"); } int64_t whichMap = -1; OptionalParameter* mapOpt = myParams->getOptionalParameter(5); if (mapOpt->m_present) { AString mapID = mapOpt->getString(1); whichMap = myCifti->getCiftiXMLOld().getMapIndexFromNameOrNumber(CiftiXMLOld::ALONG_ROW, mapID); if (whichMap == -1) { throw AlgorithmException("invalid map number or name specified"); } } if (nameMode) { AlgorithmCiftiLabelToROI(myProgObj, myCifti, labelName, myCiftiOut, whichMap); } else { AlgorithmCiftiLabelToROI(myProgObj, myCifti, labelKey, myCiftiOut, whichMap); } } AlgorithmCiftiLabelToROI::AlgorithmCiftiLabelToROI(ProgressObject* myProgObj, const CiftiFile* myCifti, const AString& labelName, CiftiFile* myCiftiOut, const int64_t& whichMap) : AbstractAlgorithm(myProgObj) { LevelProgress myProgress(myProgObj); const CiftiXMLOld& myXml = myCifti->getCiftiXMLOld(); CiftiXMLOld outXml = myXml; int64_t numRows = myXml.getNumberOfRows(); int64_t numMaps = myXml.getDimensionLength(CiftiXMLOld::ALONG_ROW); if (whichMap < -1 || whichMap >= numMaps) { throw AlgorithmException("invalid map index specified"); } if (myXml.getMappingType(CiftiXMLOld::ALONG_ROW) != CIFTI_INDEX_TYPE_LABELS) { throw AlgorithmException("input cifti must have labels along rows"); } if (whichMap == -1) { outXml.resetDirectionToScalars(CiftiXMLOld::ALONG_ROW, numMaps); vector matchKey(numMaps, -1); vector haveKey(numMaps, false);//-1 is actually a valid key, track with a second variable bool shouldThrow = true; for (int64_t i = 0; i < numMaps; ++i) { outXml.setMapNameForIndex(CiftiXMLOld::ALONG_ROW, i, myXml.getMapName(CiftiXMLOld::ALONG_ROW, i)); const GiftiLabelTable* myTable = myXml.getLabelTableForRowIndex(i); int thisKey = myTable->getLabelKeyFromName(labelName); if (thisKey != GiftiLabel::getInvalidLabelKey()) { matchKey[i] = thisKey; haveKey[i] = true; shouldThrow = false; } else { CaretLogWarning("label name '" + labelName + "' not found in map #" + AString::number(i + 1)); } } if (shouldThrow) { throw AlgorithmException("label name was not found in any map"); } shouldThrow = true; myCiftiOut->setCiftiXML(outXml); vector rowScratch(numMaps); for (int64_t row = 0; row < numRows; ++row) { myCifti->getRow(rowScratch.data(), row); for (int64_t i = 0; i < numMaps; ++i) { if (haveKey[i]) { int input = (int)floor(rowScratch[i] + 0.5f); if (input == matchKey[i]) { rowScratch[i] = 1.0f; shouldThrow = false; } else { rowScratch[i] = 0.0f; } } else { rowScratch[i] = 0.0f; } } myCiftiOut->setRow(rowScratch.data(), row); } if (shouldThrow) { throw AlgorithmException("no data matched the specified label name"); } } else { float outScratch; outXml.resetDirectionToScalars(CiftiXMLOld::ALONG_ROW, 1); outXml.setMapNameForIndex(CiftiXMLOld::ALONG_ROW, 0, myXml.getMapName(CiftiXMLOld::ALONG_ROW, whichMap)); myCiftiOut->setCiftiXML(outXml); const GiftiLabelTable* myTable = myXml.getLabelTableForRowIndex(whichMap); int matchKey = myTable->getLabelKeyFromName(labelName); if (matchKey == GiftiLabel::getInvalidLabelKey()) { throw AlgorithmException("label name '" + labelName + "' not found in specified map"); } bool shouldThrow = true; vector rowScratch(numMaps); for (int64_t row = 0; row < numRows; ++row) { myCifti->getRow(rowScratch.data(), row); int input = (int)floor(rowScratch[whichMap] + 0.5f); if (input == matchKey) { outScratch = 1.0f; shouldThrow = false; } else { outScratch = 0.0f; } myCiftiOut->setRow(&outScratch, row); } if (shouldThrow) { throw AlgorithmException("no data matched the specified label name in the specified map"); } } } AlgorithmCiftiLabelToROI::AlgorithmCiftiLabelToROI(ProgressObject* myProgObj, const CiftiFile* myCifti, const int32_t& labelKey, CiftiFile* myCiftiOut, const int64_t& whichMap) : AbstractAlgorithm(myProgObj) { LevelProgress myProgress(myProgObj); const CiftiXMLOld& myXml = myCifti->getCiftiXMLOld(); CiftiXMLOld outXml = myXml; int64_t numRows = myXml.getNumberOfRows(); int64_t numMaps = myXml.getDimensionLength(CiftiXMLOld::ALONG_ROW); if (whichMap < -1 || whichMap >= numMaps) { throw AlgorithmException("invalid map index specified"); } if (myXml.getMappingType(CiftiXMLOld::ALONG_ROW) != CIFTI_INDEX_TYPE_LABELS) { throw AlgorithmException("input cifti must have labels along rows"); } if (whichMap == -1) { outXml.resetDirectionToScalars(CiftiXMLOld::ALONG_ROW, numMaps); for (int64_t i = 0; i < numMaps; ++i) { outXml.setMapNameForIndex(CiftiXMLOld::ALONG_ROW, i, myXml.getMapName(CiftiXMLOld::ALONG_ROW, i)); const GiftiLabelTable* myTable = myXml.getLabelTableForRowIndex(i); if (myTable->getLabel(labelKey) == NULL) { CaretLogWarning("label key " + AString::number(labelKey) + " not found in map #" + AString::number(i + 1)); } } bool shouldThrow = true; myCiftiOut->setCiftiXML(outXml); vector rowScratch(numMaps); for (int64_t row = 0; row < numRows; ++row) { myCifti->getRow(rowScratch.data(), row); for (int64_t i = 0; i < numMaps; ++i) { int input = (int)floor(rowScratch[i] + 0.5f); if (input == labelKey) { rowScratch[i] = 1.0f; shouldThrow = false; } else { rowScratch[i] = 0.0f; } } myCiftiOut->setRow(rowScratch.data(), row); } if (shouldThrow) { throw AlgorithmException("no data matched the specified label key"); } } else { float outScratch; outXml.resetDirectionToScalars(CiftiXMLOld::ALONG_ROW, 1); outXml.setMapNameForIndex(CiftiXMLOld::ALONG_ROW, 0, myXml.getMapName(CiftiXMLOld::ALONG_ROW, whichMap)); myCiftiOut->setCiftiXML(outXml); const GiftiLabelTable* myTable = myXml.getLabelTableForRowIndex(whichMap); if (myTable->getLabel(labelKey) == NULL) { CaretLogWarning("label key " + AString::number(labelKey) + " not found in specified map"); } bool shouldThrow = true; vector rowScratch(numMaps); for (int64_t row = 0; row < numRows; ++row)//try anyway, in case label table is incomplete { myCifti->getRow(rowScratch.data(), row); int input = (int)floor(rowScratch[whichMap] + 0.5f); if (input == labelKey) { outScratch = 1.0f; shouldThrow = false; } else { outScratch = 0.0f; } myCiftiOut->setRow(&outScratch, row); } if (shouldThrow) { throw AlgorithmException("no data matched the specified label key in the specified map"); } } } float AlgorithmCiftiLabelToROI::getAlgorithmInternalWeight() { return 1.0f;//override this if needed, if the progress bar isn't smooth } float AlgorithmCiftiLabelToROI::getSubAlgorithmWeight() { //return AlgorithmInsertNameHere::getAlgorithmWeight();//if you use a subalgorithm return 0.0f; } connectome-workbench-1.2.3+git41-gc4c6c90/src/Algorithms/AlgorithmCiftiLabelToROI.h000066400000000000000000000036001300200146000275470ustar00rootroot00000000000000#ifndef __ALGORITHM_CIFTI_LABEL_TO_ROI_H__ #define __ALGORITHM_CIFTI_LABEL_TO_ROI_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 "AbstractAlgorithm.h" namespace caret { class AlgorithmCiftiLabelToROI : public AbstractAlgorithm { AlgorithmCiftiLabelToROI(); protected: static float getSubAlgorithmWeight(); static float getAlgorithmInternalWeight(); public: AlgorithmCiftiLabelToROI(ProgressObject* myProgObj, const CiftiFile* myCifti, const AString& labelName, CiftiFile* myCiftiOut, const int64_t& whichMap = -1); AlgorithmCiftiLabelToROI(ProgressObject* myProgObj, const CiftiFile* myCifti, const int32_t& labelKey, CiftiFile* myCiftiOut, const int64_t& whichMap = -1); static OperationParameters* getParameters(); static void useParameters(OperationParameters* myParams, ProgressObject* myProgObj); static AString getCommandSwitch(); static AString getShortDescription(); }; typedef TemplateAutoOperation AutoAlgorithmCiftiLabelToROI; } #endif //__ALGORITHM_CIFTI_LABEL_TO_ROI_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Algorithms/AlgorithmCiftiMergeDense.cxx000066400000000000000000000322161300200146000302510ustar00rootroot00000000000000/*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 "AlgorithmCiftiMergeDense.h" #include "AlgorithmException.h" #include "AlgorithmCiftiReplaceStructure.h" #include "AlgorithmCiftiSeparate.h" #include "CiftiFile.h" #include "LabelFile.h" #include "MetricFile.h" #include "VolumeFile.h" using namespace caret; using namespace std; AString AlgorithmCiftiMergeDense::getCommandSwitch() { return "-cifti-merge-dense"; } AString AlgorithmCiftiMergeDense::getShortDescription() { return "MERGE CIFTI FILES ALONG DENSE DIMENSION"; } OperationParameters* AlgorithmCiftiMergeDense::getParameters() { OperationParameters* ret = new OperationParameters(); ret->addStringParameter(1, "direction", "which dimension to merge along, ROW or COLUMN"); ret->addCiftiOutputParameter(2, "cifti-out", "the output cifti file"); ParameterComponent* ciftiOpt = ret->createRepeatableParameter(3, "-cifti", "specify an input cifti file"); ciftiOpt->addCiftiParameter(1, "cifti-in", "a cifti file to merge"); ret->setHelpText( AString("The input cifti files must have matching mappings along the direction not specified, and the mapping along the specified direction must be brain models.") ); return ret; } void AlgorithmCiftiMergeDense::useParameters(OperationParameters* myParams, ProgressObject* myProgObj) { AString directionName = myParams->getString(1); int myDir; if (directionName == "ROW") { myDir = CiftiXMLOld::ALONG_ROW; } else if (directionName == "COLUMN") { myDir = CiftiXMLOld::ALONG_COLUMN; } else { throw AlgorithmException("incorrect string for direction, use ROW or COLUMN"); } CiftiFile* myCiftiOut = myParams->getOutputCifti(2); const vector& myInstances = *(myParams->getRepeatableParameterInstances(3)); vector ciftiList; int numCifti = (int)myInstances.size(); for (int i = 0; i < numCifti; ++i) { ciftiList.push_back(myInstances[i]->getCifti(1)); } AlgorithmCiftiMergeDense(myProgObj, myDir, ciftiList, myCiftiOut); } AlgorithmCiftiMergeDense::AlgorithmCiftiMergeDense(ProgressObject* myProgObj, const int& myDir, const vector& ciftiList, CiftiFile* myCiftiOut) : AbstractAlgorithm(myProgObj) { LevelProgress myProgress(myProgObj); if (ciftiList.size() == 0) throw AlgorithmException("no input files specified"); CaretAssert(ciftiList[0] != NULL); if (myDir != CiftiXMLOld::ALONG_ROW && myDir != CiftiXMLOld::ALONG_COLUMN) throw AlgorithmException("direction not supported by cifti merge dense"); int otherDir = 1 - myDir;//find the other direction const CiftiXMLOld& baseXML = ciftiList[0]->getCiftiXMLOld(); if (baseXML.getMappingType(myDir) != CIFTI_INDEX_TYPE_BRAIN_MODELS) throw AlgorithmException("mapping type along specified dimension is not brain models"); bool isLabel = (baseXML.getMappingType(otherDir) == CIFTI_INDEX_TYPE_LABELS); CiftiXMLOld outXML = baseXML; VolumeSpace baseSpace; bool haveVolSpace = false; if (baseXML.hasVolumeData(myDir)) { haveVolSpace = true; baseXML.getVolumeSpace(baseSpace); } vector sourceCifti(baseXML.getNumberOfBrainModels(myDir), 0); for (int i = 1; i < (int)ciftiList.size(); ++i) { CaretAssert(ciftiList[i] != NULL); const CiftiXMLOld& otherXML = ciftiList[i]->getCiftiXMLOld(); if (!ciftiList[0]->getCiftiXML().getMap(otherDir)->approximateMatch(*(ciftiList[i]->getCiftiXML().getMap(otherDir)))) { throw AlgorithmException("mappings along other dimension do not match"); } if (otherXML.hasVolumeData(myDir)) { if (haveVolSpace) { VolumeSpace otherSpace; otherXML.getVolumeSpace(otherSpace); if (!baseSpace.matches(otherSpace)) throw AlgorithmException("input cifti files have non-matching volume spaces"); } else { haveVolSpace = true; otherXML.getVolumeSpace(baseSpace); outXML.setVolumeDimsAndSForm(baseSpace.getDims(), baseSpace.getSform());//need to set the output vol space if the first input didn't have volume data } } if (otherXML.getMappingType(myDir) != CIFTI_INDEX_TYPE_BRAIN_MODELS) throw AlgorithmException("mapping type along specified dimension is not brain models"); if ((otherXML.getMappingType(otherDir) == CIFTI_INDEX_TYPE_LABELS) != isLabel) throw AlgorithmException("input files to cifti merge dense mix label and non-label data"); int numModels = otherXML.getNumberOfBrainModels(myDir); for (int j = 0; j < numModels; ++j) { sourceCifti.push_back(i); CiftiBrainModelInfo myInfo = otherXML.getBrainModelInfo(myDir, j); switch (myInfo.m_type) { case CIFTI_MODEL_TYPE_SURFACE: { vector myMap; otherXML.getSurfaceMap(myDir, myMap, myInfo.m_structure); vector nodeList(myMap.size()); for (int64_t k = 0; k < (int64_t)myMap.size(); ++k) { nodeList[k] = myMap[k].m_surfaceNode; } if (!outXML.addSurfaceModel(myDir, otherXML.getSurfaceNumberOfNodes(myDir, myInfo.m_structure), myInfo.m_structure, nodeList)) { throw AlgorithmException("duplicate surface structure " + StructureEnum::toName(myInfo.m_structure) + " found in input to cifti merge dense"); } break; } case CIFTI_MODEL_TYPE_VOXELS: { vector myMap; otherXML.getVolumeStructureMap(myDir, myMap, myInfo.m_structure); vector voxelList(myMap.size() * 3); for (int64_t k = 0; k < (int64_t)myMap.size(); ++k) { int64_t k3 = k * 3; voxelList[k3] = myMap[k].m_ijk[0]; voxelList[k3 + 1] = myMap[k].m_ijk[1]; voxelList[k3 + 2] = myMap[k].m_ijk[2]; } if (!outXML.addVolumeModel(myDir, voxelList, myInfo.m_structure)) { throw AlgorithmException("duplicate volume structure " + StructureEnum::toName(myInfo.m_structure) + " found in input to cifti merge dense"); } break; } default: throw AlgorithmException("encountered unknown model type in cifti merge dense"); } } } CaretAssert((int)sourceCifti.size() == outXML.getNumberOfBrainModels(myDir)); myCiftiOut->setCiftiXML(outXML); for (int i = 0; i < (int)sourceCifti.size(); ++i) { CiftiBrainModelInfo myInfo = outXML.getBrainModelInfo(myDir, i); switch (myInfo.m_type) { case CIFTI_MODEL_TYPE_SURFACE: { if (isLabel) { LabelFile tempFile; AlgorithmCiftiSeparate(NULL, ciftiList[sourceCifti[i]], myDir, myInfo.m_structure, &tempFile);//using this because dealing with label tables is nasty, but doesn't happen on large files AlgorithmCiftiReplaceStructure(NULL, myCiftiOut, myDir, myInfo.m_structure, &tempFile); } else {//for everything else, just use rows directly, because making large metric files in-memory is problematic vector inMap, outMap; const CiftiXMLOld& otherXML = ciftiList[sourceCifti[i]]->getCiftiXMLOld(); outXML.getSurfaceMap(myDir, outMap, myInfo.m_structure); otherXML.getSurfaceMap(myDir, inMap, myInfo.m_structure); CaretAssert(inMap.size() == outMap.size()); vector rowscratch(outXML.getNumberOfColumns()), otherscratch(otherXML.getNumberOfColumns()); if (myDir == CiftiXMLOld::ALONG_ROW) { for (int j = 0; j < outXML.getNumberOfRows(); ++j) { myCiftiOut->getRow(rowscratch.data(), j, true); ciftiList[sourceCifti[i]]->getRow(otherscratch.data(), j); for (int k = 0; k < (int)inMap.size(); ++k) { CaretAssert(inMap[k].m_surfaceNode == outMap[k].m_surfaceNode); rowscratch[outMap[k].m_ciftiIndex] = otherscratch[inMap[k].m_ciftiIndex]; } myCiftiOut->setRow(rowscratch.data(), j); } } else { for (int k = 0; k < (int)inMap.size(); ++k) { CaretAssert(inMap[k].m_surfaceNode == outMap[k].m_surfaceNode); ciftiList[sourceCifti[i]]->getRow(otherscratch.data(), inMap[k].m_ciftiIndex); myCiftiOut->setRow(otherscratch.data(), outMap[k].m_ciftiIndex); } } } break; } case CIFTI_MODEL_TYPE_VOXELS: { if (isLabel) {//cropped volume should be okay on memory for label files - let replace structure handle the label table VolumeFile tempFile; int64_t junk[3]; AlgorithmCiftiSeparate(NULL, ciftiList[sourceCifti[i]], myDir, myInfo.m_structure, &tempFile, junk, NULL, true); AlgorithmCiftiReplaceStructure(NULL, myCiftiOut, myDir, myInfo.m_structure, &tempFile, true); } else { vector inMap, outMap; const CiftiXMLOld& otherXML = ciftiList[sourceCifti[i]]->getCiftiXMLOld(); outXML.getVolumeStructureMap(myDir, outMap, myInfo.m_structure); otherXML.getVolumeStructureMap(myDir, inMap, myInfo.m_structure); CaretAssert(inMap.size() == outMap.size()); vector rowscratch(outXML.getNumberOfColumns()), otherscratch(otherXML.getNumberOfColumns()); if (myDir == CiftiXMLOld::ALONG_ROW) { for (int j = 0; j < outXML.getNumberOfRows(); ++j) { myCiftiOut->getRow(rowscratch.data(), j, true); ciftiList[sourceCifti[i]]->getRow(otherscratch.data(), j); for (int k = 0; k < (int)inMap.size(); ++k) { CaretAssert(inMap[k].m_ijk[0] == outMap[k].m_ijk[0]); CaretAssert(inMap[k].m_ijk[1] == outMap[k].m_ijk[1]); CaretAssert(inMap[k].m_ijk[2] == outMap[k].m_ijk[2]); rowscratch[outMap[k].m_ciftiIndex] = otherscratch[inMap[k].m_ciftiIndex]; } myCiftiOut->setRow(rowscratch.data(), j); } } else { for (int k = 0; k < (int)inMap.size(); ++k) { CaretAssert(inMap[k].m_ijk[0] == outMap[k].m_ijk[0]); CaretAssert(inMap[k].m_ijk[1] == outMap[k].m_ijk[1]); CaretAssert(inMap[k].m_ijk[2] == outMap[k].m_ijk[2]); ciftiList[sourceCifti[i]]->getRow(otherscratch.data(), inMap[k].m_ciftiIndex); myCiftiOut->setRow(otherscratch.data(), outMap[k].m_ciftiIndex); } } } break; } default: throw AlgorithmException("encountered unknown model type in cifti merge dense"); } } } float AlgorithmCiftiMergeDense::getAlgorithmInternalWeight() { return 1.0f;//override this if needed, if the progress bar isn't smooth } float AlgorithmCiftiMergeDense::getSubAlgorithmWeight() { //return AlgorithmInsertNameHere::getAlgorithmWeight();//if you use a subalgorithm return 0.0f; } connectome-workbench-1.2.3+git41-gc4c6c90/src/Algorithms/AlgorithmCiftiMergeDense.h000066400000000000000000000033561300200146000277010ustar00rootroot00000000000000#ifndef __ALGORITHM_CIFTI_MERGE_DENSE_H__ #define __ALGORITHM_CIFTI_MERGE_DENSE_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 "AbstractAlgorithm.h" #include #include namespace caret { class AlgorithmCiftiMergeDense : public AbstractAlgorithm { AlgorithmCiftiMergeDense(); protected: static float getSubAlgorithmWeight(); static float getAlgorithmInternalWeight(); public: AlgorithmCiftiMergeDense(ProgressObject* myProgObj, const int& myDir, const std::vector& ciftiList, CiftiFile* myCiftiOut); static OperationParameters* getParameters(); static void useParameters(OperationParameters* myParams, ProgressObject* myProgObj); static AString getCommandSwitch(); static AString getShortDescription(); }; typedef TemplateAutoOperation AutoAlgorithmCiftiMergeDense; } #endif //__ALGORITHM_CIFTI_MERGE_DENSE_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Algorithms/AlgorithmCiftiMergeParcels.cxx000066400000000000000000000365221300200146000306100ustar00rootroot00000000000000/*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 "AlgorithmCiftiMergeParcels.h" #include "AlgorithmException.h" #include "CiftiFile.h" #include "GiftiLabelTable.h" #include "MultiDimIterator.h" #include using namespace caret; using namespace std; AString AlgorithmCiftiMergeParcels::getCommandSwitch() { return "-cifti-merge-parcels"; } AString AlgorithmCiftiMergeParcels::getShortDescription() { return "MERGE CIFTI FILES ALONG PARCELS DIMENSION"; } OperationParameters* AlgorithmCiftiMergeParcels::getParameters() { OperationParameters* ret = new OperationParameters(); ret->addStringParameter(1, "direction", "which dimension to merge along (integer, 'ROW', or 'COLUMN')"); ret->addCiftiOutputParameter(2, "cifti-out", "the output cifti file"); ParameterComponent* ciftiOpt = ret->createRepeatableParameter(3, "-cifti", "specify an input cifti file"); ciftiOpt->addCiftiParameter(1, "cifti-in", "a cifti file to merge"); ret->setHelpText( AString("The input cifti files must have matching mappings along the direction not specified, and the mapping along the specified direction must be parcels. ") + CiftiXML::directionFromStringExplanation() ); return ret; } void AlgorithmCiftiMergeParcels::useParameters(OperationParameters* myParams, ProgressObject* myProgObj) { AString directionName = myParams->getString(1); int myDir = CiftiXML::directionFromString(directionName); CiftiFile* myCiftiOut = myParams->getOutputCifti(2); const vector& myInstances = *(myParams->getRepeatableParameterInstances(3)); vector ciftiList; int numCifti = (int)myInstances.size(); for (int i = 0; i < numCifti; ++i) { ciftiList.push_back(myInstances[i]->getCifti(1)); } AlgorithmCiftiMergeParcels(myProgObj, myDir, ciftiList, myCiftiOut); } AlgorithmCiftiMergeParcels::AlgorithmCiftiMergeParcels(ProgressObject* myProgObj, const int& myDir, const vector& ciftiList, CiftiFile* myCiftiOut) : AbstractAlgorithm(myProgObj) { LevelProgress myProgress(myProgObj); CaretAssert(myDir >= 0); int listSize = (int)ciftiList.size(); if (listSize < 1) throw AlgorithmException("no input files specified"); CaretAssert(ciftiList[0] != NULL); const CiftiXML& firstXML = ciftiList[0]->getCiftiXML(); CiftiXML outXML = firstXML; int numDims = -1; int labelDim = -1; vector > > remaps(listSize); for (int i = 0; i < listSize; ++i) { CaretAssert(ciftiList[i] != NULL); const CiftiXML& thisXML = ciftiList[i]->getCiftiXML(); if (myDir >= thisXML.getNumberOfDimensions()) { throw AlgorithmException("input cifti file '" + ciftiList[i]->getFileName() + "' does not have the specified dimension"); } if (thisXML.getMappingType(myDir) != CiftiMappingType::PARCELS) { throw AlgorithmException("input cifti file '" + ciftiList[i]->getFileName() + "' does not have a parcels mapping on the specified dimension"); } if (i != 0) { CiftiParcelsMap& outParcelMap = outXML.getParcelsMap(myDir); const CiftiParcelsMap& thisParcelMap = thisXML.getParcelsMap(myDir); if (thisParcelMap.hasVolumeData()) { if (outParcelMap.hasVolumeData())//if there is no volume data yet, we don't care about the volume space, if it exists { if (!outParcelMap.getVolumeSpace().matches(thisParcelMap.getVolumeSpace())) { throw AlgorithmException("input cifti file '" + ciftiList[i]->getFileName() + "' does not match the volume space of earlier input files"); } } else { outParcelMap.setVolumeSpace(thisParcelMap.getVolumeSpace()); } } vector thisSurfs = thisParcelMap.getParcelSurfaceStructures(); for (int i = 0; i < (int)thisSurfs.size(); ++i) { if (outParcelMap.hasSurface(thisSurfs[i])) { if (thisParcelMap.getSurfaceNumberOfNodes(thisSurfs[i]) != outParcelMap.getSurfaceNumberOfNodes(thisSurfs[i])) { throw AlgorithmException("input cifti file '" + ciftiList[i]->getFileName() + "' has different number of vertices than earlier input files for surface " + StructureEnum::toName(thisSurfs[i])); } } else { outParcelMap.addSurface(thisParcelMap.getSurfaceNumberOfNodes(thisSurfs[i]), thisSurfs[i]); } } const vector& thisParcels = thisParcelMap.getParcels(); for (int64_t p = 0; p < (int64_t)thisParcels.size(); ++p) { outParcelMap.addParcel(thisParcels[p]);//does the error checking for overlap, unique name } } int thisNumDims = thisXML.getNumberOfDimensions(); if (i == 0) { numDims = thisNumDims; } else { if (thisNumDims != numDims) { throw AlgorithmException("input cifti file '" + ciftiList[i]->getFileName() + "' has " + AString::number(thisNumDims) + " dimensions, while file '" + ciftiList[0]->getFileName() + "' has " + AString::number(numDims) + " dimensions"); } AString explanation; for (int d = 0; d < thisNumDims; ++d) { if (d != myDir) { if (!(firstXML.getMap(d)->approximateMatch(*(thisXML.getMap(d)), &explanation)))//expect transitive property in approximateMatch, so only check against first file { throw AlgorithmException("input cifti file '" + ciftiList[i]->getFileName() + "' is incompatible with file '" + ciftiList[0]->getFileName() + "' on dimension " + AString::number(d + 1) + ": " + explanation); } if (thisXML.getMappingType(d) == CiftiMappingType::LABELS || firstXML.getMappingType(d) == CiftiMappingType::LABELS) {//NOTE: relies on CiftiXML not allowing more than one label dimension if (firstXML.getMappingType(d) != CiftiMappingType::LABELS) { throw AlgorithmException("input cifti file '" + ciftiList[i]->getFileName() + "' has a labels mapping on dimension " + AString::number(d + 1) + ", but file '" + ciftiList[0]->getFileName() + "' does not"); } if (thisXML.getMappingType(d) != CiftiMappingType::LABELS) { throw AlgorithmException("input cifti file '" + ciftiList[0]->getFileName() + "' has a labels mapping on dimension " + AString::number(d + 1) + ", but file '" + ciftiList[i]->getFileName() + "' does not"); } labelDim = d; if (i != 0)//because we used the first file's XML as a template { CiftiLabelsMap& outLabelsMap = outXML.getLabelsMap(d); int64_t numMaps = outLabelsMap.getLength(); remaps[d].resize(numMaps); for (int64_t m = 0; m < numMaps; ++m) { remaps[d][m] = outLabelsMap.getMapLabelTable(m)->append(*(thisXML.getLabelsMap(d).getMapLabelTable(m))); } } } } } } } myCiftiOut->setCiftiXML(outXML); vector outDims = outXML.getDimensions(); vector unlabeledKeys; if (labelDim != -1) { unlabeledKeys.resize(outDims[labelDim]); const CiftiLabelsMap& outLabelsMap = outXML.getLabelsMap(labelDim); for (int64_t i = 0; i < (int64_t)unlabeledKeys.size(); ++i) { unlabeledKeys[i] = outLabelsMap.getMapLabelTable(i)->getUnassignedLabelKey(); } } if (myDir == CiftiXML::ALONG_ROW) {//because unit of input/output is a row vector rowOut(outXML.getDimensionLength(CiftiXML::ALONG_ROW)), rowIn;//adapt input row size on use, in case we add options to select parcels from files for (MultiDimIterator iter(vector(outDims.begin() + 1, outDims.end())); !iter.atEnd(); ++iter) {//the + 1 is to exclude the row dimension int64_t startIndex = 0; for (int i = 0; i < listSize; ++i) { int64_t inRowLength = ciftiList[i]->getCiftiXML().getDimensionLength(CiftiXML::ALONG_ROW); if (inRowLength > (int64_t)rowIn.size()) { rowIn.resize(inRowLength); } ciftiList[i]->getRow(rowIn.data(), *iter); if (labelDim != -1) { int64_t labelMap = (*iter)[labelDim - 1]; for (int64_t j = 0; j < inRowLength; ++j) { int32_t key = (int32_t)floor(0.5f + rowIn[j]); if (i == 0)//because we used the first XML as a template { rowOut[startIndex + j] = key; } else { map::iterator iter = remaps[i][labelMap].find(key); if (iter == remaps[i][labelMap].end())//NOTE: GiftiLabelTable::append creates an entry for every key in the original table, even if it maps to the same value { rowOut[startIndex + j] = unlabeledKeys[labelMap]; } else { rowOut[startIndex + j] = iter->second; } } } } else { for (int64_t j = 0; j < inRowLength; ++j) { rowOut[startIndex + j] = rowIn[j]; } } startIndex += inRowLength; } myCiftiOut->setRow(rowOut.data(), *iter); } } else {//now the fun begins... int64_t rowLength = outXML.getDimensionLength(CiftiXML::ALONG_ROW); vector rowOut(rowLength), rowIn(rowLength);//input rows must be the same length if not row merging vector indexvec(outDims.size() - 1, -1); for (MultiDimIterator outeriter(vector(outDims.begin() + myDir + 1, outDims.end())); !outeriter.atEnd(); ++outeriter) {//split matching cifti dimensions by greater or less than myDir, aim for sequential output vector outerindexvec = *outeriter; for (int i = myDir + 1; i < (int)outDims.size(); ++i) { indexvec[i - 1] = outerindexvec[i - myDir - 1];//indexvec is missing the row dimension, outerindexvec starts one after myDir } int64_t startIndex = 0; for (int i = 0; i < listSize; ++i) {//for mydir, loop over files, and then parcels within file int64_t thisNumParcels = ciftiList[i]->getCiftiXML().getDimensionLength(myDir); for (int64_t j = 0; j < thisNumParcels; ++j) { for (MultiDimIterator inneriter(vector(outDims.begin() + 1, outDims.begin() + myDir)); !inneriter.atEnd(); ++inneriter) {//then loop over dimensions less than mydir, excluding row vector innerindices = *inneriter; for (int k = 1; k < myDir; ++k) { indexvec[k - 1] = innerindices[k - 1]; } indexvec[myDir - 1] = j;//for input ciftiList[i]->getRow(rowIn.data(), indexvec); if (labelDim != -1) {//we COULD check if labelDim is before or after myDim, and move some of the following to an outer loop, but it really doesn't seem worth it for (int k = 0; k < rowLength; ++k) { int32_t key = (int32_t)floor(0.5f + rowIn[k]); if (i == 0)//we used the first XML as a template, there is no remap { rowOut[k] = key; } else { int64_t labelMap = -1; if (labelDim == 0)//note that labelDim will never be myDir, so indexvec doesn't need to be adjusted { labelMap = k; } else { labelMap = indexvec[labelDim - 1]; } map::iterator iter = remaps[i][labelMap].find(key); if (iter == remaps[i][labelMap].end())//NOTE: GiftiLabelTable::append creates an entry for every key in the original table, even if it maps to the same value { rowOut[k] = unlabeledKeys[labelMap]; } else { rowOut[k] = iter->second; } } } indexvec[myDir - 1] = startIndex + j;//for output myCiftiOut->setRow(rowOut.data(), indexvec); } else { indexvec[myDir - 1] = startIndex + j;//for output myCiftiOut->setRow(rowIn.data(), indexvec); } } } startIndex += thisNumParcels; } } } } float AlgorithmCiftiMergeParcels::getAlgorithmInternalWeight() { return 1.0f;//override this if needed, if the progress bar isn't smooth } float AlgorithmCiftiMergeParcels::getSubAlgorithmWeight() { //return AlgorithmInsertNameHere::getAlgorithmWeight();//if you use a subalgorithm return 0.0f; } connectome-workbench-1.2.3+git41-gc4c6c90/src/Algorithms/AlgorithmCiftiMergeParcels.h000066400000000000000000000033301300200146000302240ustar00rootroot00000000000000#ifndef __ALGORITHM_CIFTI_MERGE_PARCELS_H__ #define __ALGORITHM_CIFTI_MERGE_PARCELS_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 AlgorithmCiftiMergeParcels : public AbstractAlgorithm { AlgorithmCiftiMergeParcels(); protected: static float getSubAlgorithmWeight(); static float getAlgorithmInternalWeight(); public: AlgorithmCiftiMergeParcels(ProgressObject* myProgObj, const int& myDir, const std::vector& ciftiList, CiftiFile* myCiftiOut); static OperationParameters* getParameters(); static void useParameters(OperationParameters* myParams, ProgressObject* myProgObj); static AString getCommandSwitch(); static AString getShortDescription(); }; typedef TemplateAutoOperation AutoAlgorithmCiftiMergeParcels; } #endif //__ALGORITHM_CIFTI_MERGE_PARCELS_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Algorithms/AlgorithmCiftiPairwiseCorrelation.cxx000066400000000000000000000127621300200146000322240ustar00rootroot00000000000000/*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 "AlgorithmCiftiPairwiseCorrelation.h" #include "AlgorithmException.h" #include "CaretAssert.h" #include "CaretLogger.h" #include "CaretOMP.h" #include "CiftiFile.h" #include "FileInformation.h" #include using namespace caret; using namespace std; AString AlgorithmCiftiPairwiseCorrelation::getCommandSwitch() { return "-cifti-pairwise-correlation"; } AString AlgorithmCiftiPairwiseCorrelation::getShortDescription() { return "CORRELATE PAIRED ROWS BETWEEN TWO CIFTI FILES"; } OperationParameters* AlgorithmCiftiPairwiseCorrelation::getParameters() { OperationParameters* ret = new OperationParameters(); ret->addCiftiParameter(1, "cifti-a", "first input cifti file"); ret->addCiftiParameter(2, "cifti-b", "second input cifti file"); ret->addCiftiOutputParameter(3, "cifti-out", "output cifti file"); ret->createOptionalParameter(4, "-fisher-z", "apply fisher small z transform (ie, artanh) to correlation"); ret->createOptionalParameter(5, "-override-mapping-check", "don't check the mappings for compatibility, only check length"); ret->setHelpText( AString("For each row in , correlate it with the same row in , and put the result in the same row of , which has only one column.") ); return ret; } void AlgorithmCiftiPairwiseCorrelation::useParameters(OperationParameters* myParams, ProgressObject* myProgObj) { CiftiFile* myCiftiA = myParams->getCifti(1); CiftiFile* myCiftiB = myParams->getCifti(2); CiftiFile* myCiftiOut = myParams->getOutputCifti(3); bool fisherZ = myParams->getOptionalParameter(4)->m_present; bool overrideMappingCheck = myParams->getOptionalParameter(5)->m_present; AlgorithmCiftiPairwiseCorrelation(myProgObj, myCiftiA, myCiftiB, myCiftiOut, fisherZ, overrideMappingCheck); } AlgorithmCiftiPairwiseCorrelation::AlgorithmCiftiPairwiseCorrelation(ProgressObject* myProgObj, const CiftiFile* myCiftiA, const CiftiFile* myCiftiB, CiftiFile* myCiftiOut, const bool& fisherZ, const bool& overrideMappingCheck) : AbstractAlgorithm(myProgObj) { LevelProgress myProgress(myProgObj); CiftiXMLOld outXML = myCiftiA->getCiftiXMLOld(); int64_t numRows = myCiftiA->getNumberOfRows(), rowLength = myCiftiA->getNumberOfColumns(); if (overrideMappingCheck) { if (numRows != myCiftiB->getNumberOfRows()) throw AlgorithmException("column length must match between the input files"); } else { if (!outXML.matchesForColumns(myCiftiB->getCiftiXMLOld())) throw AlgorithmException("mapping along columns must match between the input files"); } if (rowLength != myCiftiB->getNumberOfColumns()) throw AlgorithmException("row length must match between the input files"); outXML.resetRowsToScalars(1); outXML.setMapNameForRowIndex(0, "pairwise correlation"); myCiftiOut->setCiftiXML(outXML); vector rowA(rowLength), rowB(rowLength), columnOut(numRows); for (int64_t i = 0; i < numRows; ++i) { float rrsA, rrsB; myCiftiA->getRow(rowA.data(), i); myCiftiB->getRow(rowB.data(), i); preprocess(rowA.data(), rowLength, rrsA); preprocess(rowB.data(), rowLength, rrsB); columnOut[i] = correlate(rowA.data(), rrsA, rowB.data(), rrsB, rowLength, fisherZ); } myCiftiOut->setColumn(columnOut.data(), 0); } void AlgorithmCiftiPairwiseCorrelation::preprocess(float* row, const int64_t length, float& rrsOut) { double accum = 0.0; for (int64_t i = 0; i < length; ++i) { accum += row[i]; } float mean = accum / length; accum = 0.0; for (int64_t i = 0; i < length; ++i) { row[i] -= mean; accum += row[i] * row[i]; } rrsOut = sqrt(accum); } float AlgorithmCiftiPairwiseCorrelation::correlate(const float* rowA, const float& rrsA, const float* rowB, const float& rrsB, const int64_t& length, const bool& fisherZ) { double r = 0.0; for (int64_t i = 0; i < length; ++i) { r += rowA[i] * rowB[i]; } r /= rrsA * rrsB; if (fisherZ) { if (r > 0.999999) r = 0.999999;//prevent inf if (r < -0.999999) r = -0.999999;//prevent -inf return 0.5 * log((1 + r) / (1 - r)); } else { if (r > 1.0) r = 1.0;//don't output anything silly if (r < -1.0) r = -1.0; return r; } } float AlgorithmCiftiPairwiseCorrelation::getAlgorithmInternalWeight() { return 1.0f;//override this if needed, if the progress bar isn't smooth } float AlgorithmCiftiPairwiseCorrelation::getSubAlgorithmWeight() { //return AlgorithmInsertNameHere::getAlgorithmWeight();//if you use a subalgorithm return 0.0f; } connectome-workbench-1.2.3+git41-gc4c6c90/src/Algorithms/AlgorithmCiftiPairwiseCorrelation.h000066400000000000000000000041201300200146000316360ustar00rootroot00000000000000#ifndef __ALGORITHM_CIFTI_PAIRWISE_CORRELATION_H__ #define __ALGORITHM_CIFTI_PAIRWISE_CORRELATION_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 "AbstractAlgorithm.h" namespace caret { class AlgorithmCiftiPairwiseCorrelation : public AbstractAlgorithm { AlgorithmCiftiPairwiseCorrelation(); void preprocess(float* row, const int64_t length, float& rrsOut); float correlate(const float* rowA, const float& rrsA, const float* rowB, const float& rrsB, const int64_t& length, const bool& fisherZ); protected: static float getSubAlgorithmWeight(); static float getAlgorithmInternalWeight(); public: AlgorithmCiftiPairwiseCorrelation(ProgressObject* myProgObj, const CiftiFile* myCiftiA, const CiftiFile* myCiftiB, CiftiFile* myCiftiOut, const bool& fisherZ = false, const bool& overrideMappingCheck = false); static OperationParameters* getParameters(); static void useParameters(OperationParameters* myParams, ProgressObject* myProgObj); static AString getCommandSwitch(); static AString getShortDescription(); }; typedef TemplateAutoOperation AutoAlgorithmCiftiPairwiseCorrelation; } #endif //__ALGORITHM_CIFTI_PAIRWISE_CORRELATION_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Algorithms/AlgorithmCiftiParcelMappingToLabel.cxx000066400000000000000000000135201300200146000322150ustar00rootroot00000000000000/*LICENSE_START*/ /* * Copyright (C) 2015 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 "AlgorithmCiftiParcelMappingToLabel.h" #include "AlgorithmException.h" #include "CiftiFile.h" #include using namespace caret; using namespace std; AString AlgorithmCiftiParcelMappingToLabel::getCommandSwitch() { return "-cifti-parcel-mapping-to-label"; } AString AlgorithmCiftiParcelMappingToLabel::getShortDescription() { return "CREATE DLABEL FROM PARCELLATED FILE"; } OperationParameters* AlgorithmCiftiParcelMappingToLabel::getParameters() { OperationParameters* ret = new OperationParameters(); ret->addCiftiParameter(1, "cifti-in", "the input parcellated file"); ret->addStringParameter(2, "direction", "which dimension to take the parcel map from, ROW or COLUMN"); ret->addCiftiParameter(3, "template-cifti", "a cifti file with the desired dense mapping along column"); ret->addCiftiOutputParameter(4, "dlabel-out", "the output dense label file"); ret->setHelpText( AString("This command will output a dlabel file, useful for doing the same parcellation to another dense file.\n\n") + "For ptseries, pscalar, plabel, pconn, and pdconn, using COLUMN for will work." ); return ret; } void AlgorithmCiftiParcelMappingToLabel::useParameters(OperationParameters* myParams, ProgressObject* myProgObj) { CiftiFile* ciftiIn = myParams->getCifti(1); AString dirString = myParams->getString(2); int direction = -1; if (dirString == "ROW") { direction = CiftiXML::ALONG_ROW; } else if (dirString == "COLUMN") { direction = CiftiXML::ALONG_COLUMN; } else { throw AlgorithmException("unrecognized direction string, use ROW or COLUMN"); } CiftiFile* templateCifti = myParams->getCifti(3); CiftiFile* ciftiOut = myParams->getOutputCifti(4); const CiftiXML& parcelXML = ciftiIn->getCiftiXML(); if (direction >= parcelXML.getNumberOfDimensions()) { throw AlgorithmException("specified direction does not exist in input cifti file"); } if (parcelXML.getMappingType(direction) != CiftiMappingType::PARCELS) { throw AlgorithmException("input cifti file does not have parcels mapping on specified direction"); } const CiftiXML& denseXML = templateCifti->getCiftiXML(); if (denseXML.getNumberOfDimensions() < 2) { throw AlgorithmException("template cifti file does not have an along column dimension"); } if (denseXML.getMappingType(CiftiXML::ALONG_COLUMN) != CiftiMappingType::BRAIN_MODELS) { throw AlgorithmException("template cifti file does not have a dense mapping along column, try using a dscalar, dtseries, dlabel, or dconn"); } AlgorithmCiftiParcelMappingToLabel(myProgObj, parcelXML.getParcelsMap(direction), denseXML.getBrainModelsMap(CiftiXML::ALONG_COLUMN), ciftiOut); } AlgorithmCiftiParcelMappingToLabel::AlgorithmCiftiParcelMappingToLabel(ProgressObject* myProgObj, const CiftiParcelsMap& parcelMap, const CiftiBrainModelsMap& denseMap, CiftiFile* ciftiOut) : AbstractAlgorithm(myProgObj) { LevelProgress myProgress(myProgObj); CiftiXML outXML; outXML.setNumberOfDimensions(2); outXML.setMap(CiftiXML::ALONG_COLUMN, denseMap); vector surfStructures = parcelMap.getParcelSurfaceStructures(); CiftiLabelsMap outLabelMap; outLabelMap.setLength(1); GiftiLabelTable* outTable = outLabelMap.getMapLabelTable(0); vector parcelToKey(parcelMap.getLength()); for (int i = 0; i < (int)parcelMap.getLength(); ++i) { parcelToKey[i] = outTable->addLabel(parcelMap.getIndexName(i), rand() & 255, rand() & 255, rand() & 255, 255); } outXML.setMap(CiftiXML::ALONG_ROW, outLabelMap); vector scratchCol(denseMap.getLength(), outTable->getUnassignedLabelKey()); vector surfStructs = denseMap.getSurfaceStructureList(); for (int i = 0; i < (int)surfStructs.size(); ++i) { vector surfMap = denseMap.getSurfaceMap(surfStructs[i]); for (int j = 0; j < (int)surfMap.size(); ++j) { int whichParcel = parcelMap.getIndexForNode(surfMap[j].m_surfaceNode, surfStructs[i]); if (whichParcel >= 0) { scratchCol[surfMap[j].m_ciftiIndex] = parcelToKey[whichParcel]; } } } vector volMap = denseMap.getFullVolumeMap(); for (int i = 0; i < (int)volMap.size(); ++i) { int whichParcel = parcelMap.getIndexForVoxel(volMap[i].m_ijk); if (whichParcel >= 0) { scratchCol[volMap[i].m_ciftiIndex] = parcelToKey[whichParcel]; } } ciftiOut->setCiftiXML(outXML); ciftiOut->setColumn(scratchCol.data(), 0); } float AlgorithmCiftiParcelMappingToLabel::getAlgorithmInternalWeight() { return 1.0f;//override this if needed, if the progress bar isn't smooth } float AlgorithmCiftiParcelMappingToLabel::getSubAlgorithmWeight() { //return AlgorithmInsertNameHere::getAlgorithmWeight();//if you use a subalgorithm return 0.0f; } connectome-workbench-1.2.3+git41-gc4c6c90/src/Algorithms/AlgorithmCiftiParcelMappingToLabel.h000066400000000000000000000035401300200146000316430ustar00rootroot00000000000000#ifndef __ALGORITHM_CIFTI_PARCEL_MAPPING_TO_LABEL_H__ #define __ALGORITHM_CIFTI_PARCEL_MAPPING_TO_LABEL_H__ /*LICENSE_START*/ /* * Copyright (C) 2015 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 CiftiParcelsMap; class CiftiBrainModelsMap; class AlgorithmCiftiParcelMappingToLabel : public AbstractAlgorithm { AlgorithmCiftiParcelMappingToLabel(); protected: static float getSubAlgorithmWeight(); static float getAlgorithmInternalWeight(); public: AlgorithmCiftiParcelMappingToLabel(ProgressObject* myProgObj, const CiftiParcelsMap& parcelMap, const CiftiBrainModelsMap& denseMap, CiftiFile* ciftiOut); static OperationParameters* getParameters(); static void useParameters(OperationParameters* myParams, ProgressObject* myProgObj); static AString getCommandSwitch(); static AString getShortDescription(); }; typedef TemplateAutoOperation AutoAlgorithmCiftiParcelMappingToLabel; } #endif //__ALGORITHM_CIFTI_PARCEL_MAPPING_TO_LABEL_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Algorithms/AlgorithmCiftiParcellate.cxx000066400000000000000000001316131300200146000303100ustar00rootroot00000000000000/*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 "AlgorithmCiftiParcellate.h" #include "AlgorithmException.h" #include "CaretLogger.h" #include "CiftiFile.h" #include "GiftiLabel.h" #include "GiftiLabelTable.h" #include "MetricFile.h" #include "MultiDimIterator.h" #include "ReductionOperation.h" #include "SurfaceFile.h" #include #include using namespace caret; using namespace std; AString AlgorithmCiftiParcellate::getCommandSwitch() { return "-cifti-parcellate"; } AString AlgorithmCiftiParcellate::getShortDescription() { return "PARCELLATE A CIFTI FILE"; } OperationParameters* AlgorithmCiftiParcellate::getParameters() { OperationParameters* ret = new OperationParameters(); ret->addCiftiParameter(1, "cifti-in", "the cifti file to parcellate"); ret->addCiftiParameter(2, "cifti-label", "a cifti label file to use for the parcellation"); ret->addStringParameter(3, "direction", "which mapping to parcellate (integer, ROW, or COLUMN)"); ret->addCiftiOutputParameter(4, "cifti-out", "output cifti file"); OptionalParameter* spatialWeightOpt = ret->createOptionalParameter(5, "-spatial-weights", "use voxel volume and either vertex areas or metric files as weights"); OptionalParameter* leftAreaSurfOpt = spatialWeightOpt->createOptionalParameter(1, "-left-area-surf", "use a surface for left vertex areas"); leftAreaSurfOpt->addSurfaceParameter(1, "left-surf", "the left surface to use, areas are in mm^2"); OptionalParameter* rightAreaSurfOpt = spatialWeightOpt->createOptionalParameter(2, "-right-area-surf", "use a surface for right vertex areas"); rightAreaSurfOpt->addSurfaceParameter(1, "right-surf", "the right surface to use, areas are in mm^2"); OptionalParameter* cerebAreaSurfOpt = spatialWeightOpt->createOptionalParameter(3, "-cerebellum-area-surf", "use a surface for cerebellum vertex areas"); cerebAreaSurfOpt->addSurfaceParameter(1, "cerebellum-surf", "the cerebellum surface to use, areas are in mm^2"); OptionalParameter* leftAreaMetricOpt = spatialWeightOpt->createOptionalParameter(4, "-left-area-metric", "use a metric file for left vertex weights"); leftAreaMetricOpt->addMetricParameter(1, "left-metric", "metric file containing left vertex weights"); OptionalParameter* rightAreaMetricOpt = spatialWeightOpt->createOptionalParameter(5, "-right-area-metric", "use a metric file for right vertex weights"); rightAreaMetricOpt->addMetricParameter(1, "right-metric", "metric file containing right vertex weights"); OptionalParameter* cerebAreaMetricOpt = spatialWeightOpt->createOptionalParameter(6, "-cerebellum-area-metric", "use a metric file for cerebellum vertex weights"); cerebAreaMetricOpt->addMetricParameter(1, "cerebellum-metric", "metric file containing cerebellum vertex weights"); OptionalParameter* ciftiWeightOpt = ret->createOptionalParameter(6, "-cifti-weights", "use a cifti file containing weights"); ciftiWeightOpt->addCiftiParameter(1, "weight-cifti", "the weights to use, as a cifti file"); OptionalParameter* methodOpt = ret->createOptionalParameter(7, "-method", "specify method of parcellation (default MEAN, or MODE if label data)"); methodOpt->addStringParameter(1, "method", "the method to use to assign parcel values from the values of member brainordinates"); OptionalParameter* excludeOpt = ret->createOptionalParameter(8, "-exclude-outliers", "exclude non-numeric values and outliers from each parcel by standard deviation"); excludeOpt->addDoubleParameter(1, "sigma-below", "number of standard deviations below the mean to include"); excludeOpt->addDoubleParameter(2, "sigma-above", "number of standard deviations above the mean to include"); ret->createOptionalParameter(9, "-only-numeric", "exclude non-numeric values"); 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 ") + "row or column. " + 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" + "The parameter to the -method option must be one of the following:\n\n" + ReductionOperation::getHelpInfo() + "\nThe -*-weights options are mutually exclusive and may only be used with MEAN, SUM, STDEV, SAMPSTDEV, VARIANCE, MEDIAN, or MODE." ); return ret; } void AlgorithmCiftiParcellate::useParameters(OperationParameters* myParams, ProgressObject* myProgObj) { CiftiFile* myCiftiIn = myParams->getCifti(1); CiftiFile* myCiftiLabel = myParams->getCifti(2); int direction = CiftiXML::directionFromString(myParams->getString(3)); CiftiFile* myCiftiOut = myParams->getOutputCifti(4); const CiftiXML& myXML = myCiftiIn->getCiftiXML(); vector dims = myXML.getDimensions(); ReductionEnum::Enum method = ReductionEnum::MEAN; for (int i = 0; i < (int)dims.size(); ++i) { if (myXML.getMappingType(i) == CiftiMappingType::LABELS) { method = ReductionEnum::MODE; break; } } OptionalParameter* methodOpt = myParams->getOptionalParameter(7); if (methodOpt->m_present) { bool ok = false; method = ReductionEnum::fromName(methodOpt->getString(1), &ok); if (!ok) { throw AlgorithmException("unrecognized method string '" + methodOpt->getString(1) + "'"); } } bool onlyNumeric = myParams->getOptionalParameter(9)->m_present; float excludeLow = -1.0f, excludeHigh = -1.0f; OptionalParameter* excludeOpt = myParams->getOptionalParameter(8); if (excludeOpt->m_present) { if (onlyNumeric) CaretLogWarning("-only-numeric is redundant when -exclude-outliers is specified"); excludeLow = (float)excludeOpt->getDouble(1); excludeHigh = (float)excludeOpt->getDouble(2); if (!(excludeLow > 0.0f && excludeHigh > 0.0f)) throw AlgorithmException("exclusion sigmas must be positive"); } OptionalParameter* spatialWeightOpt = myParams->getOptionalParameter(5); OptionalParameter* ciftiWeightOpt = myParams->getOptionalParameter(6); if (spatialWeightOpt->m_present && ciftiWeightOpt->m_present) { throw AlgorithmException("only one of -spatial-weights and -cifti-weights may be specified"); } if (spatialWeightOpt->m_present) { if (direction >= myXML.getNumberOfDimensions()) throw AlgorithmException("input cifti file does not have the specified dimension"); if (myXML.getMappingType(direction) != CiftiMappingType::BRAIN_MODELS) throw AlgorithmException("input cifti file does not have brain models mapping type in specified direction"); CiftiBrainModelsMap myDenseMap = myXML.getBrainModelsMap(CiftiXML::ALONG_COLUMN); OptionalParameter* leftSurfOpt = spatialWeightOpt->getOptionalParameter(1); OptionalParameter* rightSurfOpt = spatialWeightOpt->getOptionalParameter(2); OptionalParameter* cerebSurfOpt = spatialWeightOpt->getOptionalParameter(3); OptionalParameter* leftMetricOpt = spatialWeightOpt->getOptionalParameter(4); OptionalParameter* rightMetricOpt = spatialWeightOpt->getOptionalParameter(5); OptionalParameter* cerebMetricOpt = spatialWeightOpt->getOptionalParameter(6); if (leftSurfOpt->m_present && leftMetricOpt->m_present) throw AlgorithmException("only one of -left-area-surf and -left-area-metric may be specified"); if (rightSurfOpt->m_present && rightMetricOpt->m_present) throw AlgorithmException("only one of -right-area-surf and -right-area-metric may be specified"); if (cerebSurfOpt->m_present && cerebMetricOpt->m_present) throw AlgorithmException("only one of -cerebellum-area-surf and -cerebellum-area-metric may be specified"); MetricFile leftStore, rightStore, cerebStore; const MetricFile* leftWeights = NULL, *rightWeights = NULL, *cerebWeights = NULL; for (int i = 0; i < 3; ++i) { MetricFile* thisStore = NULL; const MetricFile** thisWeights = NULL; OptionalParameter* thisSurfOpt = NULL, *thisMetricOpt = NULL; switch (i) { case 0: thisStore = &leftStore; thisWeights = &leftWeights; thisSurfOpt = leftSurfOpt; thisMetricOpt = leftMetricOpt; break; case 1: thisStore = &rightStore; thisWeights = &rightWeights; thisSurfOpt = rightSurfOpt; thisMetricOpt = cerebMetricOpt; break; case 2: thisStore = &cerebStore; thisWeights = &cerebWeights; thisSurfOpt = cerebSurfOpt; thisMetricOpt = cerebMetricOpt; break; } if (thisMetricOpt->m_present) { *thisWeights = leftMetricOpt->getMetric(1); } if (thisSurfOpt->m_present) { SurfaceFile* thisSurf = thisSurfOpt->getSurface(1); thisStore->setNumberOfNodesAndColumns(thisSurf->getNumberOfNodes(), 1); thisStore->setStructure(thisSurf->getStructure()); vector vertAreas; thisSurf->computeNodeAreas(vertAreas); thisStore->setValuesForColumn(0, vertAreas.data()); *thisWeights = thisStore; } } AlgorithmCiftiParcellate(myProgObj, myCiftiIn, myCiftiLabel, direction, myCiftiOut, leftWeights, rightWeights, cerebWeights, method, excludeLow, excludeHigh, onlyNumeric); return; } if (ciftiWeightOpt->m_present) { AlgorithmCiftiParcellate(myProgObj, myCiftiIn, myCiftiLabel, direction, myCiftiOut, ciftiWeightOpt->getCifti(1), method, excludeLow, excludeHigh, onlyNumeric); return; } AlgorithmCiftiParcellate(myProgObj, myCiftiIn, myCiftiLabel, direction, myCiftiOut, method, excludeLow, excludeHigh, onlyNumeric); } 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) { LevelProgress myProgress(myProgObj); CaretAssert(direction >= 0); const CiftiXML& myInputXML = myCiftiIn->getCiftiXML(); const CiftiXML& myLabelXML = myCiftiLabel->getCiftiXML(); vector dims = myInputXML.getDimensions(); if (direction >= (int)dims.size()) throw AlgorithmException("specified direction doesn't exist in input file"); if (myInputXML.getMappingType(direction) != CiftiMappingType::BRAIN_MODELS) { throw AlgorithmException("input cifti file does not have brain models mapping type in specified direction"); } if (myLabelXML.getNumberOfDimensions() != 2 || myLabelXML.getMappingType(CiftiXML::ALONG_ROW) != CiftiMappingType::LABELS || myLabelXML.getMappingType(CiftiXML::ALONG_COLUMN) != CiftiMappingType::BRAIN_MODELS) { throw AlgorithmException("input cifti label file has the wrong mapping types"); } const CiftiBrainModelsMap& inputDense = myInputXML.getBrainModelsMap(direction); const CiftiBrainModelsMap& labelDense = myLabelXML.getBrainModelsMap(CiftiXML::ALONG_COLUMN); if (inputDense.hasVolumeData()) {//don't check volume space if direction doesn't have volume data if (labelDense.hasVolumeData() && !inputDense.getVolumeSpace().matches(labelDense.getVolumeSpace())) { throw AlgorithmException("input cifti files must have the same volume space"); } } vector indexToParcel; CiftiXML myOutXML = myInputXML; CiftiParcelsMap outParcelMap = parcellateMapping(myCiftiLabel, inputDense, indexToParcel); int numParcels = outParcelMap.getLength(); if (numParcels < 1) { throw AlgorithmException("no parcels found, output file would be empty, aborting"); } myOutXML.setMap(direction, outParcelMap); myCiftiOut->setCiftiXML(myOutXML); int64_t numCols = myInputXML.getDimensionLength(CiftiXML::ALONG_ROW); vector scratchRow(numCols); vector parcelCounts(numParcels, 0); for (int64_t j = 0; j < (int64_t)indexToParcel.size(); ++j) { int parcel = indexToParcel[j]; CaretAssert(parcel > -2 && parcel < numParcels); if (parcel != -1) { ++parcelCounts[parcel]; } } bool isLabel = false; int labelDir = -1; for (int i = 0; i < (int)dims.size(); ++i) { if (myInputXML.getMappingType(i) == CiftiMappingType::LABELS) { isLabel = true; labelDir = i; break;//there should never be more than one dimension with LABEL type, and if there is, just use the first one, i guess... } } if (isLabel && method != ReductionEnum::MODE) { CaretLogWarning(ReductionEnum::toName(method) + " reduction requested while parcellating label data"); } if (direction == CiftiXML::ALONG_ROW) { vector scratchOutRow(numParcels); vector > parcelData(numParcels);//float so we can use ReductionOperation for (int j = 0; j < numParcels; ++j) { parcelData[j].reserve(parcelCounts[j]); } for (MultiDimIterator iter(vector(dims.begin() + 1, dims.end())); !iter.atEnd(); ++iter) { for (int j = 0; j < numParcels; ++j) { parcelData[j].clear();//doesn't change allocation } myCiftiIn->getRow(scratchRow.data(), *iter); for (int64_t j = 0; j < numCols; ++j) { int parcel = indexToParcel[j]; if (parcel != -1) { if (isLabel) { parcelData[parcel].push_back(floor(scratchRow[j] + 0.5f));//round to nearest integer to be safe } else { parcelData[parcel].push_back(scratchRow[j]); } } } for (int j = 0; j < numParcels; ++j) { CaretAssert(parcelCounts[j] == (int64_t)parcelData[j].size()); if (parcelCounts[j] > 0 && (method != ReductionEnum::SAMPSTDEV || parcelCounts[j] > 1)) { if (excludeLow > 0.0f && excludeHigh > 0.0f) { scratchOutRow[j] = ReductionOperation::reduceExcludeDev(parcelData[j].data(), parcelData[j].size(), method, excludeLow, excludeHigh); } else { if (onlyNumeric) { scratchOutRow[j] = ReductionOperation::reduceOnlyNumeric(parcelData[j].data(), parcelData[j].size(), method); } else { scratchOutRow[j] = ReductionOperation::reduce(parcelData[j].data(), parcelData[j].size(), method); } } } else {//labelDir can't be 0 (row) because we are parcellating along row, so row must be dense if (isLabel) { scratchOutRow[j] = myOutXML.getLabelsMap(labelDir).getMapLabelTable((*iter)[labelDir - 1])->getUnassignedLabelKey(); } else { scratchOutRow[j] = 0.0f; } } } myCiftiOut->setRow(scratchOutRow.data(), *iter); } } else { vector scratchOutRow(numCols); vector otherDims = dims; otherDims.erase(otherDims.begin() + direction);//direction being parcellated otherDims.erase(otherDims.begin());//row vector > > parcelData(numParcels, vector >(numCols));//float so we can use ReductionOperation for (int i = 0; i < numParcels; ++i) { for (int j = 0; j < numCols; ++j) { parcelData[i][j].reserve(parcelCounts[i]); } } for (MultiDimIterator iter(otherDims); !iter.atEnd(); ++iter) { vector indices(dims.size() - 1);//we need to add the parcellated direction index back into the index list to use it in getRow/setRow for (int i = 0; i < (int)otherDims.size(); ++i) { if (i < direction - 1) { indices[i] = (*iter)[i]; } else { indices[i + 1] = (*iter)[i]; } }//indices[direction - 1] is uninitialized, as it is the dimension to be parcellated for (int i = 0; i < numParcels; ++i) { for (int j = 0; j < numCols; ++j) { parcelData[i][j].clear();//doesn't change allocation } } for (int64_t i = 0; i < dims[direction]; ++i) { int parcel = indexToParcel[i]; if (parcel != -1) { indices[direction - 1] = i; myCiftiIn->getRow(scratchRow.data(), indices); vector >& parcelRef = parcelData[parcel]; for (int j = 0; j < numCols; ++j) { if (isLabel) { parcelRef[j].push_back(floor(scratchRow[j] + 0.5f)); } else { parcelRef[j].push_back(scratchRow[j]); } } } } for (int i = 0; i < numParcels; ++i) { indices[direction - 1] = i; int64_t count = parcelCounts[i]; vector >& parcelRef = parcelData[i]; if (count > 0 && (method != ReductionEnum::SAMPSTDEV || count > 1)) { for (int j = 0; j < numCols; ++j) { CaretAssert((int64_t)parcelRef[j].size() == count); if (excludeLow > 0.0f && excludeHigh > 0.0f) { scratchOutRow[j] = ReductionOperation::reduceExcludeDev(parcelRef[j].data(), parcelRef[j].size(), method, excludeLow, excludeHigh); } else { if (onlyNumeric) { scratchOutRow[j] = ReductionOperation::reduceOnlyNumeric(parcelRef[j].data(), parcelRef[j].size(), method); } else { scratchOutRow[j] = ReductionOperation::reduce(parcelRef[j].data(), parcelRef[j].size(), method); } } } } else { for (int j = 0; j < numCols; ++j) { CaretAssert((int64_t)parcelRef[j].size() == count); if (isLabel) { if (labelDir == CiftiXML::ALONG_ROW) { scratchOutRow[j] = myOutXML.getLabelsMap(CiftiXML::ALONG_ROW).getMapLabelTable(j)->getUnassignedLabelKey(); } else { scratchOutRow[j] = myOutXML.getLabelsMap(labelDir).getMapLabelTable(indices[labelDir - 1])->getUnassignedLabelKey(); } } else { scratchOutRow[j] = 0.0f; } } } myCiftiOut->setRow(scratchOutRow.data(), indices); } } } } 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 CiftiXML& myInputXML = myCiftiIn->getCiftiXML(); const CiftiXML& myOutXML = myCiftiOut->getCiftiXML(); vector dims = myInputXML.getDimensions(); CaretAssert(direction < (int)dims.size()); bool isLabel = false; int labelDir = -1; for (int i = 0; i < (int)dims.size(); ++i) { if (myInputXML.getMappingType(i) == CiftiMappingType::LABELS) { isLabel = true; labelDir = i; break;//there should never be more than one dimension with LABEL type, and if there is, just use the first one, i guess... } } if (isLabel && method != ReductionEnum::MODE) { CaretLogWarning(ReductionEnum::toName(method) + " reduction requested while parcellating label data"); } int numParcels = myCiftiOut->getCiftiXML().getDimensionLength(direction); int64_t numCols = myInputXML.getDimensionLength(CiftiXML::ALONG_ROW); vector scratchRow(numCols); if (direction == CiftiXML::ALONG_ROW) { vector scratchOutRow(numParcels); vector > parcelData(numParcels);//float so we can use ReductionOperation for (int j = 0; j < numParcels; ++j) { parcelData[j].reserve(parcelWeights[j].size()); } for (MultiDimIterator iter(vector(dims.begin() + 1, dims.end())); !iter.atEnd(); ++iter) { for (int j = 0; j < numParcels; ++j) { parcelData[j].clear();//doesn't change allocation } myCiftiIn->getRow(scratchRow.data(), *iter); for (int64_t j = 0; j < numCols; ++j) { int parcel = indexToParcel[j]; if (parcel != -1) { if (isLabel) { parcelData[parcel].push_back(floor(scratchRow[j] + 0.5f));//round to nearest integer to be safe } else { parcelData[parcel].push_back(scratchRow[j]); } } } for (int j = 0; j < numParcels; ++j) { CaretAssert(parcelWeights[j].size() == parcelData[j].size()); if (parcelData[j].size() > 0 && (method != ReductionEnum::SAMPSTDEV || parcelData[j].size() > 1)) { if (excludeLow > 0.0f && excludeHigh > 0.0f) { scratchOutRow[j] = ReductionOperation::reduceWeightedExcludeDev(parcelData[j].data(), parcelWeights[j].data(), parcelData[j].size(), method, excludeLow, excludeHigh); } else { if (onlyNumeric) { scratchOutRow[j] = ReductionOperation::reduceWeightedOnlyNumeric(parcelData[j].data(), parcelWeights[j].data(), parcelData[j].size(), method); } else { scratchOutRow[j] = ReductionOperation::reduceWeighted(parcelData[j].data(), parcelWeights[j].data(), parcelData[j].size(), method); } } } else {//labelDir can't be 0 (row) because we are parcellating along row, so row must be dense if (isLabel) { scratchOutRow[j] = myOutXML.getLabelsMap(labelDir).getMapLabelTable((*iter)[labelDir - 1])->getUnassignedLabelKey(); } else { scratchOutRow[j] = 0.0f; } } } myCiftiOut->setRow(scratchOutRow.data(), *iter); } } else { vector scratchOutRow(numCols); vector otherDims = dims; otherDims.erase(otherDims.begin() + direction);//direction being parcellated otherDims.erase(otherDims.begin());//row vector > > parcelData(numParcels, vector >(numCols));//float so we can use ReductionOperation for (int i = 0; i < numParcels; ++i) { for (int j = 0; j < numCols; ++j) { parcelData[i][j].reserve(parcelWeights[i].size()); } } for (MultiDimIterator iter(otherDims); !iter.atEnd(); ++iter) { vector indices(dims.size() - 1);//we need to add the parcellated direction index back into the index list to use it in getRow/setRow for (int i = 0; i < (int)otherDims.size(); ++i) { if (i < direction - 1) { indices[i] = (*iter)[i]; } else { indices[i + 1] = (*iter)[i]; } }//indices[direction - 1] is uninitialized, as it is the dimension to be parcellated for (int i = 0; i < numParcels; ++i) { for (int j = 0; j < numCols; ++j) { parcelData[i][j].clear();//doesn't change allocation } } for (int64_t i = 0; i < dims[direction]; ++i) { int parcel = indexToParcel[i]; if (parcel != -1) { indices[direction - 1] = i; myCiftiIn->getRow(scratchRow.data(), indices); vector >& parcelRef = parcelData[parcel]; for (int j = 0; j < numCols; ++j) { if (isLabel) { parcelRef[j].push_back(floor(scratchRow[j] + 0.5f)); } else { parcelRef[j].push_back(scratchRow[j]); } } } } for (int i = 0; i < numParcels; ++i) { indices[direction - 1] = i; int64_t count = (int64_t)parcelWeights[i].size(); vector >& parcelRef = parcelData[i]; if (count > 0 && (method != ReductionEnum::SAMPSTDEV || count > 1)) { for (int j = 0; j < numCols; ++j) { CaretAssert((int64_t)parcelRef[j].size() == count); if (excludeLow > 0.0f && excludeHigh > 0.0f) { scratchOutRow[j] = ReductionOperation::reduceWeightedExcludeDev(parcelRef[j].data(), parcelWeights[i].data(), parcelRef[j].size(), method, excludeLow, excludeHigh); } else { if (onlyNumeric) { scratchOutRow[j] = ReductionOperation::reduceWeightedOnlyNumeric(parcelRef[j].data(), parcelWeights[i].data(), parcelRef[j].size(), method); } else { scratchOutRow[j] = ReductionOperation::reduceWeighted(parcelRef[j].data(), parcelWeights[i].data(), parcelRef[j].size(), method); } } } } else { for (int j = 0; j < numCols; ++j) { CaretAssert((int64_t)parcelRef[j].size() == count); if (isLabel) { if (labelDir == CiftiXML::ALONG_ROW) { scratchOutRow[j] = myOutXML.getLabelsMap(CiftiXML::ALONG_ROW).getMapLabelTable(j)->getUnassignedLabelKey(); } else { scratchOutRow[j] = myOutXML.getLabelsMap(labelDir).getMapLabelTable(indices[labelDir - 1])->getUnassignedLabelKey(); } } else { scratchOutRow[j] = 0.0f; } } } myCiftiOut->setRow(scratchOutRow.data(), indices); } } } } } 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) { LevelProgress myProgress(myProgObj); CaretAssert(direction >= 0); const CiftiXML& myInputXML = myCiftiIn->getCiftiXML(); const CiftiXML& myLabelXML = myCiftiLabel->getCiftiXML(); vector dims = myInputXML.getDimensions(); if (direction >= (int)dims.size()) throw AlgorithmException("specified direction doesn't exist in input file"); if (myInputXML.getMappingType(direction) != CiftiMappingType::BRAIN_MODELS) { throw AlgorithmException("input cifti file does not have brain models mapping type in specified direction"); } if (myLabelXML.getNumberOfDimensions() != 2 || myLabelXML.getMappingType(CiftiXML::ALONG_ROW) != CiftiMappingType::LABELS || myLabelXML.getMappingType(CiftiXML::ALONG_COLUMN) != CiftiMappingType::BRAIN_MODELS) { throw AlgorithmException("input cifti label file has the wrong mapping types"); } const CiftiBrainModelsMap& inputDense = myInputXML.getBrainModelsMap(direction); const CiftiBrainModelsMap& labelDense = myLabelXML.getBrainModelsMap(CiftiXML::ALONG_COLUMN); float voxelVolume = 1.0f; if (inputDense.hasVolumeData()) {//don't check volume space if direction doesn't have volume data if (labelDense.hasVolumeData() && !inputDense.getVolumeSpace().matches(labelDense.getVolumeSpace())) { throw AlgorithmException("input cifti files must have the same volume space"); } Vector3D ivec, jvec, kvec, origin;//compute the volume of a voxel in case a parcel spans both surface and volume inputDense.getVolumeSpace().getSpacingVectors(ivec, jvec, kvec, origin); voxelVolume = abs(ivec.dot(jvec.cross(kvec))); } vector surfStructs = inputDense.getSurfaceStructureList(); for (int i = 0; i < (int)surfStructs.size(); ++i) { const MetricFile* toCheck = NULL; switch (surfStructs[i]) { case StructureEnum::CORTEX_LEFT: toCheck = leftWeights; break; case StructureEnum::CORTEX_RIGHT: toCheck = rightWeights; break; case StructureEnum::CEREBELLUM: toCheck = cerebWeights; break; default: throw AlgorithmException("unsupported surface structure: " + StructureEnum::toName(surfStructs[i])); } if (toCheck == NULL) throw AlgorithmException("weight metric required but not provided for structure " + StructureEnum::toName(surfStructs[i])); if (toCheck->getNumberOfNodes() != inputDense.getSurfaceNumberOfNodes(surfStructs[i])) { throw AlgorithmException("weight metric has incorrect number of vertices for structure " + StructureEnum::toName(surfStructs[i])); } checkStructureMatch(toCheck, surfStructs[i], "weight metric", "it is provided as the argument for"); } vector indexToParcel; CiftiXML myOutXML = myInputXML; CiftiParcelsMap outParcelMap = parcellateMapping(myCiftiLabel, inputDense, indexToParcel); int numParcels = outParcelMap.getLength(); if (numParcels < 1) { throw AlgorithmException("no parcels found, output file would be empty, aborting"); } myOutXML.setMap(direction, outParcelMap); myCiftiOut->setCiftiXML(myOutXML); vector > parcelWeights(numParcels); for (int64_t j = 0; j < (int64_t)indexToParcel.size(); ++j) { int parcel = indexToParcel[j]; if (parcel != -1) { const CiftiBrainModelsMap::IndexInfo myDenseInfo = inputDense.getInfoForIndex(j); if (myDenseInfo.m_type == CiftiBrainModelsMap::VOXELS) { parcelWeights[parcel].push_back(voxelVolume); } else { const MetricFile* toUse = NULL; switch (myDenseInfo.m_structure) { case StructureEnum::CORTEX_LEFT: toUse = leftWeights; break; case StructureEnum::CORTEX_RIGHT: toUse = rightWeights; break; case StructureEnum::CEREBELLUM: toUse = cerebWeights; break; default: CaretAssert(0); } parcelWeights[parcel].push_back(toUse->getValue(myDenseInfo.m_surfaceNode, 0)); } } } doWeightedParcellation(myCiftiIn, direction, myCiftiOut, indexToParcel, parcelWeights, method, excludeLow, excludeHigh, onlyNumeric); } 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) { LevelProgress myProgress(myProgObj); CaretAssert(direction >= 0); const CiftiXML& myInputXML = myCiftiIn->getCiftiXML(); const CiftiXML& myLabelXML = myCiftiLabel->getCiftiXML(); const CiftiXML& weightsXML = ciftiWeights->getCiftiXML(); vector dims = myInputXML.getDimensions(); if (direction >= (int)dims.size()) throw AlgorithmException("specified direction doesn't exist in input file"); if (myInputXML.getMappingType(direction) != CiftiMappingType::BRAIN_MODELS) { throw AlgorithmException("input cifti file does not have brain models mapping type in specified direction"); } if (weightsXML.getMappingType(CiftiXML::ALONG_COLUMN) != CiftiMappingType::BRAIN_MODELS) { throw AlgorithmException("cifti weight file does not have brain models along column"); } if (myLabelXML.getNumberOfDimensions() != 2 || myLabelXML.getMappingType(CiftiXML::ALONG_ROW) != CiftiMappingType::LABELS || myLabelXML.getMappingType(CiftiXML::ALONG_COLUMN) != CiftiMappingType::BRAIN_MODELS) { throw AlgorithmException("input cifti label file has the wrong mapping types"); } const CiftiBrainModelsMap& inputDense = myInputXML.getBrainModelsMap(direction); const CiftiBrainModelsMap& labelDense = myLabelXML.getBrainModelsMap(CiftiXML::ALONG_COLUMN); if (!weightsXML.getMap(CiftiXML::ALONG_COLUMN)->approximateMatch(inputDense)) { throw AlgorithmException("cifti weight file does not match brain models mapping of input file"); } if (inputDense.hasVolumeData()) {//don't check volume space if direction doesn't have volume data if (labelDense.hasVolumeData() && !inputDense.getVolumeSpace().matches(labelDense.getVolumeSpace())) { throw AlgorithmException("input cifti files must have the same volume space"); } } vector indexToParcel; CiftiXML myOutXML = myInputXML; CiftiParcelsMap outParcelMap = parcellateMapping(myCiftiLabel, inputDense, indexToParcel); int numParcels = outParcelMap.getLength(); if (numParcels < 1) { throw AlgorithmException("no parcels found, output file would be empty, aborting"); } myOutXML.setMap(direction, outParcelMap); myCiftiOut->setCiftiXML(myOutXML); vector weightCol(weightsXML.getDimensionLength(CiftiXML::ALONG_COLUMN)); ciftiWeights->getColumn(weightCol.data(), 0); vector > parcelWeights(numParcels); for (int64_t j = 0; j < (int64_t)indexToParcel.size(); ++j) { int parcel = indexToParcel[j]; if (parcel != -1) { parcelWeights[parcel].push_back(weightCol[j]);//we already tested that the dense mappings matched } } doWeightedParcellation(myCiftiIn, direction, myCiftiOut, indexToParcel, parcelWeights, method, excludeLow, excludeHigh, onlyNumeric); } CiftiParcelsMap AlgorithmCiftiParcellate::parcellateMapping(const CiftiFile* myCiftiLabel, const CiftiBrainModelsMap& toParcellate, vector& indexToParcelOut) { const CiftiXML& myLabelXML = myCiftiLabel->getCiftiXML(); if (myLabelXML.getNumberOfDimensions() != 2 || myLabelXML.getMappingType(CiftiXML::ALONG_ROW) != CiftiMappingType::LABELS || myLabelXML.getMappingType(CiftiXML::ALONG_COLUMN) != CiftiMappingType::BRAIN_MODELS) { 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); CiftiParcelsMap ret; if (toParcellate.hasVolumeData() && myDenseMap.hasVolumeData()) { if(!toParcellate.getVolumeSpace().matches(myDenseMap.getVolumeSpace())) { throw AlgorithmException("AlgorithmCiftiParcellate::parcellateMapping requires matching volume space between dlabel and dense mapping to parcellate"); } ret.setVolumeSpace(toParcellate.getVolumeSpace()); } const GiftiLabelTable* myLabelTable = myLabelsMap.getMapLabelTable(0); 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 (myDenseMap.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 = myDenseMap.getIndexForNode(surfMap[j].m_surfaceNode, myStruct); if (labelIndex != -1) { int labelKey = (int)floor(labelData[labelIndex] + 0.5f); if (labelKey != unusedKey) { int tempVal = -1; map >::iterator iter = usedKeys.find(labelKey); if (iter == usedKeys.end()) { 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); } 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) { int labelKey = (int)floor(labelData[labelIndex] + 0.5f); if (labelKey != unusedKey) { int tempVal = -1; map >::iterator iter = usedKeys.find(labelKey); if (iter == usedKeys.end()) { 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)); } 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) { indexToParcelOut[i] = valRemap[indexToParcelOut[i]]; } } return ret; } float AlgorithmCiftiParcellate::getAlgorithmInternalWeight() { return 1.0f;//override this if needed, if the progress bar isn't smooth } float AlgorithmCiftiParcellate::getSubAlgorithmWeight() { //return AlgorithmInsertNameHere::getAlgorithmWeight();//if you use a subalgorithm return 0.0f; } connectome-workbench-1.2.3+git41-gc4c6c90/src/Algorithms/AlgorithmCiftiParcellate.h000066400000000000000000000061611300200146000277340ustar00rootroot00000000000000#ifndef __ALGORITHM_CIFTI_PARCELLATE_H__ #define __ALGORITHM_CIFTI_PARCELLATE_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 "AbstractAlgorithm.h" #include "CiftiBrainModelsMap.h" #include "CiftiParcelsMap.h" #include "ReductionEnum.h" #include namespace caret { class AlgorithmCiftiParcellate : public AbstractAlgorithm { AlgorithmCiftiParcellate(); protected: static float getSubAlgorithmWeight(); static float getAlgorithmInternalWeight(); 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); 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); 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); static OperationParameters* getParameters(); static void useParameters(OperationParameters* myParams, ProgressObject* myProgObj); static AString getCommandSwitch(); static AString getShortDescription(); }; typedef TemplateAutoOperation AutoAlgorithmCiftiParcellate; } #endif //__ALGORITHM_CIFTI_PARCELLATE_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Algorithms/AlgorithmCiftiROIsFromExtrema.cxx000066400000000000000000000465451300200146000312330ustar00rootroot00000000000000/*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 "AlgorithmCiftiROIsFromExtrema.h" #include "AlgorithmException.h" #include "AlgorithmCiftiSeparate.h" #include "AlgorithmMetricROIsFromExtrema.h" #include "AlgorithmVolumeROIsFromExtrema.h" #include "CaretAssert.h" #include "CaretPointer.h" #include "CiftiFile.h" #include "MetricFile.h" #include "VolumeFile.h" #include "OverlapLogicEnum.h" #include "SurfaceFile.h" #include #include using namespace caret; using namespace std; AString AlgorithmCiftiROIsFromExtrema::getCommandSwitch() { return "-cifti-rois-from-extrema"; } AString AlgorithmCiftiROIsFromExtrema::getShortDescription() { return "CREATE CIFTI ROI MAPS FROM EXTREMA MAPS"; } OperationParameters* AlgorithmCiftiROIsFromExtrema::getParameters() { OperationParameters* ret = new OperationParameters(); ret->addCiftiParameter(1, "cifti", "the input cifti"); ret->addDoubleParameter(2, "surf-limit", "geodesic distance limit from vertex, in mm"); ret->addDoubleParameter(3, "vol-limit", "euclidean distance limit from voxel center, in mm"); ret->addStringParameter(4, "direction", "which dimension an extrema map is along, ROW or COLUMN"); ret->addCiftiOutputParameter(5, "cifti-out", "the output cifti"); OptionalParameter* leftSurfOpt = ret->createOptionalParameter(6, "-left-surface", "specify the left surface to use"); leftSurfOpt->addSurfaceParameter(1, "surface", "the left surface file"); OptionalParameter* rightSurfOpt = ret->createOptionalParameter(7, "-right-surface", "specify the right surface to use"); rightSurfOpt->addSurfaceParameter(1, "surface", "the right surface file"); OptionalParameter* cerebSurfaceOpt = ret->createOptionalParameter(8, "-cerebellum-surface", "specify the cerebellum surface to use"); cerebSurfaceOpt->addSurfaceParameter(1, "surface", "the cerebellum surface file"); OptionalParameter* gaussOpt = ret->createOptionalParameter(9, "-gaussian", "generate gaussian kernels instead of flat ROIs"); gaussOpt->addDoubleParameter(1, "surf-sigma", "the sigma for the surface gaussian kernel, in mm"); gaussOpt->addDoubleParameter(2, "vol-sigma", "the sigma for the volume gaussian kernel, in mm"); OptionalParameter* overlapOpt = ret->createOptionalParameter(10, "-overlap-logic", "how to handle overlapping ROIs, default ALLOW"); overlapOpt->addStringParameter(1, "method", "the method of resolving overlaps"); ret->createOptionalParameter(11, "-merged-volume", "treat volume components as if they were a single component"); ret->setHelpText( AString("For each nonzero value in each map, make a map with an ROI around that location. ") + "If the -gaussian option is specified, then normalized gaussian kernels are output instead of ROIs. " + "The argument to -overlap-logic must be one of ALLOW, CLOSEST, or EXCLUDE. " + "ALLOW is the default, and means that ROIs are treated independently and may overlap. " + "CLOSEST means that ROIs may not overlap, and that no ROI contains vertices that are closer to a different seed vertex. " + "EXCLUDE means that ROIs may not overlap, and that any vertex within range of more than one ROI does not belong to any ROI." ); return ret; } void AlgorithmCiftiROIsFromExtrema::useParameters(OperationParameters* myParams, ProgressObject* myProgObj) { CiftiFile* myCifti = myParams->getCifti(1); float surfLimit = (float)myParams->getDouble(2); float volLimit = (float)myParams->getDouble(3); AString directionName = myParams->getString(4); int myDir; if (directionName == "ROW") { myDir = CiftiXMLOld::ALONG_ROW; } else if (directionName == "COLUMN") { myDir = CiftiXMLOld::ALONG_COLUMN; } else { throw AlgorithmException("incorrect string for direction, use ROW or COLUMN"); } CiftiFile* myCiftiOut = myParams->getOutputCifti(5); SurfaceFile* myLeftSurf = NULL, *myRightSurf = NULL, *myCerebSurf = NULL; OptionalParameter* leftSurfOpt = myParams->getOptionalParameter(6); if (leftSurfOpt->m_present) { myLeftSurf = leftSurfOpt->getSurface(1); } OptionalParameter* rightSurfOpt = myParams->getOptionalParameter(7); if (rightSurfOpt->m_present) { myRightSurf = rightSurfOpt->getSurface(1); } OptionalParameter* cerebSurfOpt = myParams->getOptionalParameter(8); if (cerebSurfOpt->m_present) { myCerebSurf = cerebSurfOpt->getSurface(1); } float surfSigma = -1.0f, volSigma = -1.0f; OptionalParameter* gaussOpt = myParams->getOptionalParameter(9); if (gaussOpt->m_present) { surfSigma = (float)gaussOpt->getDouble(1); volSigma = (float)gaussOpt->getDouble(2); if (surfSigma <= 0.0f || volSigma <= 0.0f) { throw AlgorithmException("sigma values must be positive"); } } OverlapLogicEnum::Enum myLogic = OverlapLogicEnum::ALLOW; OptionalParameter* overlapOpt = myParams->getOptionalParameter(10); if (overlapOpt->m_present) { bool ok = false; myLogic = OverlapLogicEnum::fromName(overlapOpt->getString(1), &ok); if (!ok) { throw AlgorithmException("unrecognized overlap method"); } } bool mergedVolume = myParams->getOptionalParameter(11)->m_present; AlgorithmCiftiROIsFromExtrema(myProgObj, myCifti, surfLimit, volLimit, myDir, myCiftiOut, myLeftSurf, myRightSurf, myCerebSurf, surfSigma, volSigma, myLogic, mergedVolume); } AlgorithmCiftiROIsFromExtrema::AlgorithmCiftiROIsFromExtrema(ProgressObject* myProgObj, const CiftiFile* myCifti, const float& surfLimit, const float& volLimit, const int& myDir, CiftiFile* myCiftiOut, const SurfaceFile* myLeftSurf, const SurfaceFile* myRightSurf, const SurfaceFile* myCerebSurf, const float& surfSigma, const float& volSigma, const OverlapLogicEnum::Enum& myLogic, const bool& mergedVolume) : AbstractAlgorithm(myProgObj) { LevelProgress myProgress(myProgObj); CiftiXMLOld myXML = myCifti->getCiftiXMLOld(); vector surfaceList, volumeList; if (myDir != CiftiXMLOld::ALONG_ROW && myDir != CiftiXMLOld::ALONG_COLUMN) throw AlgorithmException("direction not supported by cifti rois from extrema"); if (!myXML.getStructureLists(myDir, surfaceList, volumeList)) { throw AlgorithmException("columns do not contain brainordinates"); } for (int whichStruct = 0; whichStruct < (int)surfaceList.size(); ++whichStruct) {//sanity check surfaces const SurfaceFile* mySurf = NULL; AString surfType; switch (surfaceList[whichStruct]) { case StructureEnum::CORTEX_LEFT: mySurf = myLeftSurf; surfType = "left"; break; case StructureEnum::CORTEX_RIGHT: mySurf = myRightSurf; surfType = "right"; break; case StructureEnum::CEREBELLUM: mySurf = myCerebSurf; surfType = "cerebellum"; break; default: throw AlgorithmException("found surface model with incorrect type: " + StructureEnum::toName(surfaceList[whichStruct])); break; } if (mySurf == NULL) { throw AlgorithmException(surfType + " surface required but not provided"); } if (mySurf->getNumberOfNodes() != myXML.getColumnSurfaceNumberOfNodes(surfaceList[whichStruct])) { throw AlgorithmException(surfType + " surface has the wrong number of vertices"); } } vector > surfROIs; vector > volROIs; vector volOffsets; int64_t mapCount = 0; for (int whichStruct = 0; whichStruct < (int)surfaceList.size(); ++whichStruct) { const SurfaceFile* mySurf = NULL; switch (surfaceList[whichStruct]) { case StructureEnum::CORTEX_LEFT: mySurf = myLeftSurf; break; case StructureEnum::CORTEX_RIGHT: mySurf = myRightSurf; break; case StructureEnum::CEREBELLUM: mySurf = myCerebSurf; break; default: break; } MetricFile myMetric, myRoi, myMetricOut; AlgorithmCiftiSeparate(NULL, myCifti, myDir, surfaceList[whichStruct], &myMetric, &myRoi); surfROIs.push_back(CaretPointer(new MetricFile())); AlgorithmMetricROIsFromExtrema(NULL, mySurf, &myMetric, surfLimit, surfROIs.back(), surfSigma, &myRoi, myLogic); mapCount += surfROIs.back()->getNumberOfMaps(); } if (mergedVolume) { if (myCifti->getCiftiXMLOld().hasVolumeData(myDir)) { VolumeFile myVol, myRoi, myVolOut; volOffsets.resize(3); AlgorithmCiftiSeparate(NULL, myCifti, myDir, &myVol, volOffsets.data(), &myRoi, true);//without structure, it returns a combined volume volROIs.push_back(CaretPointer(new VolumeFile())); AlgorithmVolumeROIsFromExtrema(NULL, &myVol, volLimit, volROIs.back(), volSigma, &myRoi, myLogic); vector tempDims; volROIs.back()->getDimensions(tempDims); mapCount += tempDims[3];//because getNumberOfMaps only returns int32 } } else { volOffsets.resize(volumeList.size() * 3); for (int whichStruct = 0; whichStruct < (int)volumeList.size(); ++whichStruct) { VolumeFile myVol, myRoi, myVolOut; AlgorithmCiftiSeparate(NULL, myCifti, myDir, volumeList[whichStruct], &myVol, volOffsets.data() + 3 * whichStruct, &myRoi, true); volROIs.push_back(CaretPointer(new VolumeFile())); AlgorithmVolumeROIsFromExtrema(NULL, &myVol, volLimit, volROIs.back(), volSigma, &myRoi, myLogic); vector tempDims; volROIs.back()->getDimensions(tempDims); mapCount += tempDims[3];//because getNumberOfMaps only returns int32 } } if (mapCount > numeric_limits::max()) throw AlgorithmException("result has too many ROIs"); myXML.resetDirectionToScalars(1 - myDir, mapCount); myCiftiOut->setCiftiXML(myXML); if (myDir == CiftiXMLOld::ALONG_ROW) { int64_t curRow = 0; for (int whichStruct = 0; whichStruct < (int)surfaceList.size(); ++whichStruct) { vector scratchrow(myXML.getNumberOfColumns(), 0.0f);//so that it is initially zeroed for each structure vector myMap; myXML.getSurfaceMap(myDir, myMap, surfaceList[whichStruct]); for (int i = 0; i < surfROIs[whichStruct]->getNumberOfColumns(); ++i) { const float* data = surfROIs[whichStruct]->getValuePointerForColumn(i); for (int j = 0; j < (int)myMap.size(); ++j) { scratchrow[myMap[j].m_ciftiIndex] = data[myMap[j].m_surfaceNode]; } myCiftiOut->setRow(scratchrow.data(), curRow); ++curRow; } } if (mergedVolume) { if (myXML.hasVolumeData(myDir)) { vector scratchrow(myXML.getNumberOfColumns(), 0.0f); vector myMap; myXML.getVolumeMap(myDir, myMap); vector tempDims; volROIs[0]->getDimensions(tempDims); for (int64_t b = 0; b < tempDims[3]; ++b) { for (int64_t j = 0; j < (int64_t)myMap.size(); ++j) { int64_t myijk[3] = { myMap[j].m_ijk[0] - volOffsets[0], myMap[j].m_ijk[1] - volOffsets[1], myMap[j].m_ijk[2] - volOffsets[2] }; scratchrow[myMap[j].m_ciftiIndex] = volROIs[0]->getValue(myijk, b); } myCiftiOut->setRow(scratchrow.data(), curRow); ++curRow; } } } else { for (int whichStruct = 0; whichStruct < (int)volumeList.size(); ++whichStruct) { vector scratchrow(myXML.getNumberOfColumns(), 0.0f); vector myMap; myXML.getVolumeStructureMapForRows(myMap, volumeList[whichStruct]); vector tempDims; volROIs[whichStruct]->getDimensions(tempDims); for (int64_t b = 0; b < tempDims[3]; ++b) { for (int64_t j = 0; j < (int64_t)myMap.size(); ++j) { int64_t myijk[3] = { myMap[j].m_ijk[0] - volOffsets[whichStruct * 3], myMap[j].m_ijk[1] - volOffsets[whichStruct * 3 + 1], myMap[j].m_ijk[2] - volOffsets[whichStruct * 3 + 2] }; scratchrow[myMap[j].m_ciftiIndex] = volROIs[whichStruct]->getValue(myijk, b); } myCiftiOut->setRow(scratchrow.data(), curRow); ++curRow; } } } } else { vector surfaceStart, volumeStart; int64_t mystart = 0; for (int whichStruct = 0; whichStruct < (int)surfaceList.size(); ++whichStruct) { surfaceStart.push_back(mystart); mystart += surfROIs[whichStruct]->getNumberOfMaps(); } if (mergedVolume) { volumeStart.push_back(mystart); } else { for (int whichStruct = 0; whichStruct < (int)volumeList.size(); ++whichStruct) { volumeStart.push_back(mystart); vector tempDims; volROIs[whichStruct]->getDimensions(tempDims); mystart += tempDims[3]; } } int64_t curRow = 0; while (curRow < myXML.getNumberOfRows()) { bool found = false; for (int whichStruct = 0; whichStruct < (int)surfaceList.size(); ++whichStruct) { vector myMap; myXML.getSurfaceMap(myDir, myMap, surfaceList[whichStruct]); if (myMap.size() > 0 && myMap[0].m_ciftiIndex == curRow)//NOTE: cifti indexes in structure maps are always linear ascending { vector scratchrow(myXML.getNumberOfColumns(), 0.0f); for (int64_t j = 0; j < (int64_t)myMap.size(); ++j) { CaretAssert(curRow == myMap[j].m_ciftiIndex); for (int k = 0; k < surfROIs[whichStruct]->getNumberOfColumns(); ++k) { scratchrow[surfaceStart[whichStruct] + k] = surfROIs[whichStruct]->getValue(myMap[j].m_surfaceNode, k); } myCiftiOut->setRow(scratchrow.data(), curRow); ++curRow; } found = true; break; } } if (!found) { if (mergedVolume) { for (int whichStruct = 0; whichStruct < (int)volumeList.size(); ++whichStruct)//NOTE: merged map may not be in ascending order, or contiguous, so don't use it { vector myMap; myXML.getVolumeStructureMapForColumns(myMap, volumeList[whichStruct]); if (myMap.size() > 0 && myMap[0].m_ciftiIndex == curRow) { vector tempDims; volROIs[0]->getDimensions(tempDims); vector scratchrow(myXML.getNumberOfColumns(), 0.0f); for (int64_t j = 0; j < (int64_t)myMap.size(); ++j) { CaretAssert(curRow == myMap[j].m_ciftiIndex); int64_t myijk[3] = { myMap[j].m_ijk[0] - volOffsets[0], myMap[j].m_ijk[1] - volOffsets[1], myMap[j].m_ijk[2] - volOffsets[2] }; for (int64_t b = 0; b < tempDims[3]; ++b) { scratchrow[volumeStart[0] + b] = volROIs[0]->getValue(myijk, b); } myCiftiOut->setRow(scratchrow.data(), curRow); ++curRow; } found = true; break; } } } else { for (int whichStruct = 0; whichStruct < (int)volumeList.size(); ++whichStruct) { vector myMap; myXML.getVolumeStructureMapForColumns(myMap, volumeList[whichStruct]); vector tempDims; volROIs[whichStruct]->getDimensions(tempDims); if (myMap.size() > 0 && myMap[0].m_ciftiIndex == curRow) { vector scratchrow(myXML.getNumberOfColumns(), 0.0f); for (int64_t j = 0; j < (int64_t)myMap.size(); ++j) { CaretAssert(curRow == myMap[j].m_ciftiIndex); int64_t myijk[3] = { myMap[j].m_ijk[0] - volOffsets[whichStruct * 3], myMap[j].m_ijk[1] - volOffsets[whichStruct * 3 + 1], myMap[j].m_ijk[2] - volOffsets[whichStruct * 3 + 2] }; for (int64_t b = 0; b < tempDims[3]; ++b) { scratchrow[volumeStart[whichStruct] + b] = volROIs[whichStruct]->getValue(myijk, b); } myCiftiOut->setRow(scratchrow.data(), curRow); ++curRow; } found = true; break; } } } } CaretAssert(found); } } } float AlgorithmCiftiROIsFromExtrema::getAlgorithmInternalWeight() { return 1.0f;//override this if needed, if the progress bar isn't smooth } float AlgorithmCiftiROIsFromExtrema::getSubAlgorithmWeight() { //return AlgorithmInsertNameHere::getAlgorithmWeight();//if you use a subalgorithm return 0.0f; } connectome-workbench-1.2.3+git41-gc4c6c90/src/Algorithms/AlgorithmCiftiROIsFromExtrema.h000066400000000000000000000042151300200146000306440ustar00rootroot00000000000000#ifndef __ALGORITHM_CIFTI_ROIS_FROM_EXTREMA_H__ #define __ALGORITHM_CIFTI_ROIS_FROM_EXTREMA_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 "AbstractAlgorithm.h" #include "OverlapLogicEnum.h" namespace caret { class AlgorithmCiftiROIsFromExtrema : public AbstractAlgorithm { AlgorithmCiftiROIsFromExtrema(); protected: static float getSubAlgorithmWeight(); static float getAlgorithmInternalWeight(); public: AlgorithmCiftiROIsFromExtrema(ProgressObject* myProgObj, const CiftiFile* myCifti, const float& surfLimit, const float& volLimit, const int& myDir, CiftiFile* myCiftiOut, const SurfaceFile* myLeftSurf = NULL, const SurfaceFile* myRightSurf = NULL, const SurfaceFile* myCerebSurf = NULL, const float& surfSigma = -1.0f, const float& volSigma = -1.0f, const OverlapLogicEnum::Enum& myLogic = OverlapLogicEnum::ALLOW, const bool& mergedVolume = false); static OperationParameters* getParameters(); static void useParameters(OperationParameters* myParams, ProgressObject* myProgObj); static AString getCommandSwitch(); static AString getShortDescription(); }; typedef TemplateAutoOperation AutoAlgorithmCiftiROIsFromExtrema; } #endif //__ALGORITHM_CIFTI_ROIS_FROM_EXTREMA_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Algorithms/AlgorithmCiftiReduce.cxx000066400000000000000000000242401300200146000274400ustar00rootroot00000000000000/*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 "AlgorithmCiftiReduce.h" #include "AlgorithmException.h" #include "CaretAssert.h" #include "CaretLogger.h" #include "CiftiFile.h" #include "MultiDimIterator.h" #include "ReductionOperation.h" #include using namespace caret; using namespace std; AString AlgorithmCiftiReduce::getCommandSwitch() { return "-cifti-reduce"; } AString AlgorithmCiftiReduce::getShortDescription() { return "PERFORM REDUCTION OPERATION ON A CIFTI FILE"; } OperationParameters* AlgorithmCiftiReduce::getParameters() { OperationParameters* ret = new OperationParameters(); ret->addCiftiParameter(1, "cifti-in", "the cifti file to reduce"); ret->addStringParameter(2, "operation", "the reduction operator to use"); ret->addCiftiOutputParameter(3, "cifti-out", "the output cifti file"); OptionalParameter* directionOpt = ret->createOptionalParameter(6, "-direction", "specify what direction to reduce along"); directionOpt->addStringParameter(1, "direction", "the direction (default ROW)"); OptionalParameter* excludeOpt = ret->createOptionalParameter(4, "-exclude-outliers", "exclude non-numeric values and outliers by standard deviation"); excludeOpt->addDoubleParameter(1, "sigma-below", "number of standard deviations below the mean to include"); excludeOpt->addDoubleParameter(2, "sigma-above", "number of standard deviations above the mean to include"); ret->createOptionalParameter(5, "-only-numeric", "exclude non-numeric values"); ret->setHelpText( AString("For the specified direction (default ROW), perform a reduction operation along that direction. ") + CiftiXML::directionFromStringExplanation() + " " + "The reduction operators are as follows:\n\n" + ReductionOperation::getHelpInfo() ); return ret; } void AlgorithmCiftiReduce::useParameters(OperationParameters* myParams, ProgressObject* myProgObj) { CiftiFile* ciftiIn = myParams->getCifti(1); AString opString = myParams->getString(2); CiftiFile* ciftiOut = myParams->getOutputCifti(3); int direction = CiftiXML::ALONG_ROW; OptionalParameter* directionOpt = myParams->getOptionalParameter(6); if (directionOpt->m_present) { direction = CiftiXML::directionFromString(directionOpt->getString(1));//does error checking, throws with message on error } OptionalParameter* excludeOpt = myParams->getOptionalParameter(4); bool onlyNumeric = myParams->getOptionalParameter(5)->m_present; bool ok = false; ReductionEnum::Enum myReduce = ReductionEnum::fromName(opString, &ok); if (!ok) throw AlgorithmException("unrecognized operation string '" + opString + "'"); if (excludeOpt->m_present) { if (onlyNumeric) CaretLogWarning("-only-numeric is redundant when -exclude-outliers is specified"); AlgorithmCiftiReduce(myProgObj, ciftiIn, myReduce, ciftiOut, excludeOpt->getDouble(1), excludeOpt->getDouble(2), direction); } else { AlgorithmCiftiReduce(myProgObj, ciftiIn, myReduce, ciftiOut, onlyNumeric, direction); } } AlgorithmCiftiReduce::AlgorithmCiftiReduce(ProgressObject* myProgObj, const CiftiFile* ciftiIn, const ReductionEnum::Enum& myReduce, CiftiFile* ciftiOut, const bool& onlyNumeric, const int& direction) : AbstractAlgorithm(myProgObj) { LevelProgress myProgress(myProgObj); CaretAssert(direction >= 0); const CiftiXML& inputXML = ciftiIn->getCiftiXML(); CiftiXML myOutXML = inputXML; if (direction >= myOutXML.getNumberOfDimensions()) throw AlgorithmException("specified reduction direction doesn't exist in input cifti file"); CiftiScalarsMap newMap; newMap.setLength(1); newMap.setMapName(0, ReductionEnum::toName(myReduce)); myOutXML.setMap(direction, newMap); ciftiOut->setCiftiXML(myOutXML); vector inDims = inputXML.getDimensions(); if (direction == CiftiXML::ALONG_ROW) { vector scratchInRow(inDims[0]); for (MultiDimIterator iter(vector(inDims.begin() + 1, inDims.end())); !iter.atEnd(); ++iter) {// + 1 to exclude row dimension, because getRow/setRow ciftiIn->getRow(scratchInRow.data(), *iter); float result = -1; if (onlyNumeric) { result = ReductionOperation::reduceOnlyNumeric(scratchInRow.data(), inDims[0], myReduce); } else { result = ReductionOperation::reduce(scratchInRow.data(), inDims[0], myReduce); } ciftiOut->setRow(&result, *iter);//if reducing along row, length of output row is 1 } } else { vector > scratchInRows(inDims[direction], vector(inDims[0])); vector outRow(inDims[0]), reduceScratch(inDims[direction]);//reduction isn't along row, so out rows will be same length as in rows vector otherDims = inDims; otherDims.erase(otherDims.begin() + direction);//direction isn't 0 otherDims.erase(otherDims.begin());//remove row direction because getRow/setRow for (MultiDimIterator iter(otherDims); !iter.atEnd(); ++iter) { vector indexvec = *iter; indexvec.insert(indexvec.begin() + direction - 1, -1);//dummy value in place of reduce direction for (int64_t i = 0; i < inDims[direction]; ++i) { indexvec[direction - 1] = i; ciftiIn->getRow(scratchInRows[i].data(), indexvec); } for (int64_t i = 0; i < inDims[0]; ++i) { for (int64_t j = 0; j < inDims[direction]; ++j) {//need reduction input in contiguous array reduceScratch[j] = scratchInRows[j][i]; } if (onlyNumeric) { outRow[i] = ReductionOperation::reduceOnlyNumeric(reduceScratch.data(), inDims[direction], myReduce); } else { outRow[i] = ReductionOperation::reduce(reduceScratch.data(), inDims[direction], myReduce); } } indexvec[direction - 1] = 0;//only one element along reduce output direction ciftiOut->setRow(outRow.data(), indexvec); } } } AlgorithmCiftiReduce::AlgorithmCiftiReduce(ProgressObject* myProgObj, const CiftiFile* ciftiIn, const ReductionEnum::Enum& myReduce, CiftiFile* ciftiOut, const float& sigmaBelow, const float& sigmaAbove, const int& direction) : AbstractAlgorithm(myProgObj) { LevelProgress myProgress(myProgObj); CaretAssert(direction >= 0); const CiftiXML& inputXML = ciftiIn->getCiftiXML(); CiftiXML myOutXML = inputXML; if (direction >= myOutXML.getNumberOfDimensions()) throw AlgorithmException("specified reduction direction doesn't exist in input cifti file"); CiftiScalarsMap newMap; newMap.setLength(1); newMap.setMapName(0, ReductionEnum::toName(myReduce)); myOutXML.setMap(direction, newMap); ciftiOut->setCiftiXML(myOutXML); vector inDims = inputXML.getDimensions(); if (direction == CiftiXML::ALONG_ROW) { vector scratchInRow(inDims[0]); for (MultiDimIterator iter(vector(inDims.begin() + 1, inDims.end())); !iter.atEnd(); ++iter) {// + 1 to exclude row dimension, because getRow/setRow ciftiIn->getRow(scratchInRow.data(), *iter); float result = ReductionOperation::reduceExcludeDev(scratchInRow.data(), inDims[0], myReduce, sigmaBelow, sigmaAbove); ciftiOut->setRow(&result, *iter);//if reducing along row, length of output row is 1 } } else { vector > scratchInRows(inDims[direction], vector(inDims[0])); vector outRow(inDims[0]), reduceScratch(inDims[direction]);//reduction isn't along row, so out rows will be same length as in rows vector otherDims = inDims; otherDims.erase(otherDims.begin() + direction);//direction isn't 0 otherDims.erase(otherDims.begin());//remove row direction because getRow/setRow for (MultiDimIterator iter(otherDims); !iter.atEnd(); ++iter) { vector indexvec = *iter; indexvec.insert(indexvec.begin() + direction - 1, -1);//dummy value in place of reduce direction for (int64_t i = 0; i < inDims[direction]; ++i) { indexvec[direction - 1] = i; ciftiIn->getRow(scratchInRows[i].data(), indexvec); } for (int64_t i = 0; i < inDims[0]; ++i) { for (int64_t j = 0; j < inDims[direction]; ++j) {//need reduction input in contiguous array reduceScratch[j] = scratchInRows[j][i]; } outRow[i] = ReductionOperation::reduceExcludeDev(reduceScratch.data(), inDims[direction], myReduce, sigmaBelow, sigmaAbove); } indexvec[direction - 1] = 0;//only one element along reduce output direction ciftiOut->setRow(outRow.data(), indexvec); } } } float AlgorithmCiftiReduce::getAlgorithmInternalWeight() { return 1.0f;//override this if needed, if the progress bar isn't smooth } float AlgorithmCiftiReduce::getSubAlgorithmWeight() { //return AlgorithmInsertNameHere::getAlgorithmWeight();//if you use a subalgorithm return 0.0f; } connectome-workbench-1.2.3+git41-gc4c6c90/src/Algorithms/AlgorithmCiftiReduce.h000066400000000000000000000041031300200146000270610ustar00rootroot00000000000000#ifndef __ALGORITHM_CIFTI_REDUCE_H__ #define __ALGORITHM_CIFTI_REDUCE_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 "AbstractAlgorithm.h" #include "CiftiXML.h" #include "ReductionEnum.h" namespace caret { class AlgorithmCiftiReduce : public AbstractAlgorithm { AlgorithmCiftiReduce(); protected: static float getSubAlgorithmWeight(); static float getAlgorithmInternalWeight(); public: AlgorithmCiftiReduce(ProgressObject* myProgObj, const CiftiFile* ciftiIn, const ReductionEnum::Enum& myReduce, CiftiFile* ciftiOut, const bool& onlyNumeric = false, const int& direction = CiftiXML::ALONG_ROW); AlgorithmCiftiReduce(ProgressObject* myProgObj, const CiftiFile* ciftiIn, const ReductionEnum::Enum& myReduce, CiftiFile* ciftiOut, const float& sigmaBelow, const float& sigmaAbove, const int& direction = CiftiXML::ALONG_ROW); static OperationParameters* getParameters(); static void useParameters(OperationParameters* myParams, ProgressObject* myProgObj); static AString getCommandSwitch(); static AString getShortDescription(); }; typedef TemplateAutoOperation AutoAlgorithmCiftiReduce; } #endif //__ALGORITHM_CIFTI_REDUCE_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Algorithms/AlgorithmCiftiReorder.cxx000066400000000000000000000172451300200146000276420ustar00rootroot00000000000000/*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 "AlgorithmCiftiReorder.h" #include "AlgorithmException.h" #include "CiftiFile.h" #include "FileInformation.h" #include "GiftiLabelTable.h" #include "PaletteColorMapping.h" #include using namespace caret; using namespace std; AString AlgorithmCiftiReorder::getCommandSwitch() { return "-cifti-reorder"; } AString AlgorithmCiftiReorder::getShortDescription() { return "REORDER THE PARCELS OR SCALAR/LABEL MAPS IN A CIFTI FILE"; } OperationParameters* AlgorithmCiftiReorder::getParameters() { OperationParameters* ret = new OperationParameters(); ret->addCiftiParameter(1, "cifti-in", "input cifti file"); ret->addStringParameter(2, "direction", "which dimension to reorder along, ROW or COLUMN"); ret->addStringParameter(3, "reorder-list", "a text file containing the desired order transformation"); ret->addCiftiOutputParameter(4, "cifti-out", "the reordered cifti file"); ret->setHelpText( AString("The mapping along the specified direction must be parcels, scalars, or labels. ") + "For pscalar or ptseries, use COLUMN to reorder the parcels. " + "For dlabel, use ROW. " + "The file must contain 1-based indices separated by whitespace (spaces, newlines, tabs, etc), with as many indices as has along the specified dimension. " + "These indices specify which current index should end up in that position, for instance, if the current order is 'A B C D', and the desired order is 'D A B C', the text file " + "should contain '4 1 2 3'." ); return ret; } void AlgorithmCiftiReorder::useParameters(OperationParameters* myParams, ProgressObject* myProgObj) { CiftiFile* myCifti = myParams->getCifti(1); AString directionName = myParams->getString(2); int myDir; if (directionName == "ROW") { myDir = CiftiXMLOld::ALONG_ROW; } else if (directionName == "COLUMN") { myDir = CiftiXMLOld::ALONG_COLUMN; } else { throw AlgorithmException("incorrect string for direction, use ROW or COLUMN"); } AString listFileName = myParams->getString(3); CiftiFile* myCiftiOut = myParams->getOutputCifti(4); FileInformation textFileInfo(listFileName); if (!textFileInfo.exists()) { throw AlgorithmException("name list file doesn't exist"); } fstream listFile(listFileName.toLocal8Bit().constData(), fstream::in); if (!listFile.good()) { throw AlgorithmException("error reading name list file"); } vector reorder; int64_t temp; while (listFile >> temp) { reorder.push_back(temp - 1); } AlgorithmCiftiReorder(myProgObj, myCifti, myDir, reorder, myCiftiOut); } AlgorithmCiftiReorder::AlgorithmCiftiReorder(ProgressObject* myProgObj, const CiftiFile* myCifti, const int& myDir, const vector& reorder, CiftiFile* myCiftiOut) : AbstractAlgorithm(myProgObj) { LevelProgress myProgress(myProgObj); const CiftiXMLOld& myXML = myCifti->getCiftiXMLOld(); CiftiXMLOld myOutXML = myXML; int64_t rowSize = myXML.getNumberOfColumns(), colSize = myXML.getNumberOfRows(), myDirSize; if (myDir == CiftiXMLOld::ALONG_ROW) { myDirSize = rowSize; } else if (myDir == CiftiXMLOld::ALONG_COLUMN) { myDirSize = colSize; } else { throw AlgorithmException("direction not supported by cifti reorder"); } if ((int64_t)reorder.size() != myDirSize) throw AlgorithmException("reorder list has incorrect size, expected " + AString::number(myDirSize) + ", got " + AString::number(reorder.size())); vector used(myDirSize, false); for (int64_t i = 0; i < myDirSize; ++i) { int64_t val = reorder[i]; if (val < 0 || val >= myDirSize) throw AlgorithmException("invalid index in reorder list"); if (used[val]) throw AlgorithmException("index specified more than once in reorder list"); used[val] = true; } IndicesMapToDataType myType = myXML.getMappingType(myDir); switch (myType) { case CIFTI_INDEX_TYPE_SCALARS: { for (int64_t i = 0; i < myDirSize; ++i) { myOutXML.setMapNameForIndex(myDir, i, myXML.getMapName(myDir, reorder[i])); *(myOutXML.getMapPalette(myDir, i)) = *(myXML.getMapPalette(myDir, reorder[i])); *(myOutXML.getMapMetadata(myDir, i)) = *(myXML.getMapMetadata(myDir, reorder[i])); } break; } case CIFTI_INDEX_TYPE_LABELS: { for (int64_t i = 0; i < myDirSize; ++i) { myOutXML.setMapNameForIndex(myDir, i, myXML.getMapName(myDir, reorder[i])); *(myOutXML.getMapLabelTable(myDir, i)) = *(myXML.getMapLabelTable(myDir, reorder[i])); *(myOutXML.getMapMetadata(myDir, i)) = *(myXML.getMapMetadata(myDir, reorder[i])); } break; } case CIFTI_INDEX_TYPE_PARCELS: { myOutXML.resetDirectionToParcels(myDir); vector mySurfs; myXML.getParcelSurfaceStructures(myDir, mySurfs); for (int i = 0; i < (int)mySurfs.size(); ++i) { myOutXML.addParcelSurface(myDir, myXML.getSurfaceNumberOfNodes(myDir, mySurfs[i]), mySurfs[i]); } vector origParcels; myXML.getParcels(myDir, origParcels); for (int64_t i = 0; i < myDirSize; ++i) { myOutXML.addParcel(myDir, origParcels[reorder[i]]); } break; } default: throw AlgorithmException("mapping along specified direction must be scalars, labels, or parcels"); } myCiftiOut->setCiftiXML(myOutXML); vector scratchrow(rowSize); switch (myDir) { case CiftiXMLOld::ALONG_ROW: { vector scratchrow2(rowSize); for (int64_t i = 0; i < colSize; ++i) { myCifti->getRow(scratchrow.data(), i); for (int64_t j = 0; j < rowSize; ++j) { scratchrow2[j] = scratchrow[reorder[j]]; } myCiftiOut->setRow(scratchrow2.data(), i); } break; } case CiftiXMLOld::ALONG_COLUMN: { for (int64_t i = 0; i < colSize; ++i) { myCifti->getRow(scratchrow.data(), reorder[i]); myCiftiOut->setRow(scratchrow.data(), i); } break; } default: CaretAssert(false); } } float AlgorithmCiftiReorder::getAlgorithmInternalWeight() { return 1.0f;//override this if needed, if the progress bar isn't smooth } float AlgorithmCiftiReorder::getSubAlgorithmWeight() { //return AlgorithmInsertNameHere::getAlgorithmWeight();//if you use a subalgorithm return 0.0f; } connectome-workbench-1.2.3+git41-gc4c6c90/src/Algorithms/AlgorithmCiftiReorder.h000066400000000000000000000033171300200146000272620ustar00rootroot00000000000000#ifndef __ALGORITHM_CIFTI_REORDER_H__ #define __ALGORITHM_CIFTI_REORDER_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 "AbstractAlgorithm.h" #include namespace caret { class AlgorithmCiftiReorder : public AbstractAlgorithm { AlgorithmCiftiReorder(); protected: static float getSubAlgorithmWeight(); static float getAlgorithmInternalWeight(); public: AlgorithmCiftiReorder(ProgressObject* myProgObj, const CiftiFile* myCifti, const int& myDir, const std::vector& reorder, CiftiFile* myCiftiOut); static OperationParameters* getParameters(); static void useParameters(OperationParameters* myParams, ProgressObject* myProgObj); static AString getCommandSwitch(); static AString getShortDescription(); }; typedef TemplateAutoOperation AutoAlgorithmCiftiReorder; } #endif //__ALGORITHM_CIFTI_REORDER_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Algorithms/AlgorithmCiftiReplaceStructure.cxx000066400000000000000000001066221300200146000315320ustar00rootroot00000000000000/*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 "AlgorithmCiftiReplaceStructure.h" //for computing the cropped volume space #include "AlgorithmCiftiSeparate.h" #include "AlgorithmException.h" #include "CaretPointer.h" #include "CiftiFile.h" #include "GiftiLabelTable.h" #include "LabelFile.h" #include "MetricFile.h" #include "Vector3D.h" #include "VolumeFile.h" #include #include using namespace caret; using namespace std; AString AlgorithmCiftiReplaceStructure::getCommandSwitch() { return "-cifti-replace-structure"; } AString AlgorithmCiftiReplaceStructure::getShortDescription() { return "REPLACE DATA IN A STRUCTURE IN A CIFTI FILE"; } OperationParameters* AlgorithmCiftiReplaceStructure::getParameters() { OperationParameters* ret = new OperationParameters(); ret->addStringParameter(1, "cifti", "the cifti to modify");//see useParameters for why this is a string ret->addStringParameter(2, "direction", "which dimension to interpret as a single map, ROW or COLUMN"); ParameterComponent* labelOpt = ret->createRepeatableParameter(3, "-label", "replace the data in a surface label component"); labelOpt->addStringParameter(1, "structure", "the structure to replace the data of"); labelOpt->addLabelParameter(2, "label", "the input label file"); ParameterComponent* metricOpt = ret->createRepeatableParameter(4, "-metric", "replace the data in a surface component"); metricOpt->addStringParameter(1, "structure", "the structure to replace the data of"); metricOpt->addMetricParameter(2, "metric", "the input metric"); ParameterComponent* volumeOpt = ret->createRepeatableParameter(5, "-volume", "replace the data in a volume component"); volumeOpt->addStringParameter(1, "structure", "the structure to replace the data of"); volumeOpt->addVolumeParameter(2, "volume", "the input volume"); volumeOpt->createOptionalParameter(3, "-from-cropped", "the input is cropped to the size of the component"); OptionalParameter* volumeAllOpt = ret->createOptionalParameter(6, "-volume-all", "replace the data in all volume components"); volumeAllOpt->addVolumeParameter(1, "volume", "the input volume"); volumeAllOpt->createOptionalParameter(2, "-from-cropped", "the input is cropped to the size of the data"); ret->createOptionalParameter(7, "-discard-unused-labels", "when operating on a dlabel file, drop any unused label keys from the label table"); AString helpText = AString("You must specify at least one of -metric, -label, -volume, or -volume-all for this command to do anything. ") + "Input volumes must line up with the output of -cifti-separate. " + "For dtseries/dscalar, use COLUMN, and if your matrix will be fully symmetric, COLUMN is more efficient. " + "The structure argument must be one of the following:\n"; vector myStructureEnums; StructureEnum::getAllEnums(myStructureEnums); for (int i = 0; i < (int)myStructureEnums.size(); ++i) { helpText += "\n" + StructureEnum::toName(myStructureEnums[i]); } ret->setHelpText(helpText); return ret; } void AlgorithmCiftiReplaceStructure::useParameters(OperationParameters* myParams, ProgressObject* /*myProgObj*/) { AString ciftiName = myParams->getString(1); CiftiFile myCifti(ciftiName);//CiftiFile currently doesn't expose the filename it is using, and we need to call writeFile manually (since the parsing framework doesn't expect an input to be modified) AString dirName = myParams->getString(2);//as for why it needs to modify an input, that is the only way it is useful for things like -cifti-smoothing int myDir; if (dirName == "ROW") { myDir = CiftiXML::ALONG_ROW; } else if (dirName == "COLUMN") { myDir = CiftiXML::ALONG_COLUMN; } else { throw AlgorithmException("incorrect string for direction, use ROW or COLUMN"); } bool discardUnusedLabels = myParams->getOptionalParameter(7)->m_present; const vector& labelInst = *(myParams->getRepeatableParameterInstances(3)); for (int i = 0; i < (int)labelInst.size(); ++i) { AString structName = labelInst[i]->getString(1); bool ok = false; StructureEnum::Enum myStruct = StructureEnum::fromName(structName, &ok); if (!ok) { throw AlgorithmException("unrecognized structure name"); } LabelFile* labelIn = labelInst[i]->getLabel(2); AlgorithmCiftiReplaceStructure(NULL, &myCifti, myDir, myStruct, labelIn, discardUnusedLabels); } const vector& metricInst = *(myParams->getRepeatableParameterInstances(4)); for (int i = 0; i < (int)metricInst.size(); ++i) { AString structName = metricInst[i]->getString(1); bool ok = false; StructureEnum::Enum myStruct = StructureEnum::fromName(structName, &ok); if (!ok) { throw AlgorithmException("unrecognized structure name"); } MetricFile* metricIn = metricInst[i]->getMetric(2); AlgorithmCiftiReplaceStructure(NULL, &myCifti, myDir, myStruct, metricIn); } const vector& volumeInst = *(myParams->getRepeatableParameterInstances(5)); for (int i = 0; i < (int)volumeInst.size(); ++i) { AString structName = volumeInst[i]->getString(1); bool ok = false; StructureEnum::Enum myStruct = StructureEnum::fromName(structName, &ok); if (!ok) { throw AlgorithmException("unrecognized structure name"); } VolumeFile* volIn = volumeInst[i]->getVolume(2); bool fromCropVol = volumeInst[i]->getOptionalParameter(3)->m_present; AlgorithmCiftiReplaceStructure(NULL, &myCifti, myDir, myStruct, volIn, fromCropVol, discardUnusedLabels); } OptionalParameter* volumeAllOpt = myParams->getOptionalParameter(6); if (volumeAllOpt->m_present) { VolumeFile* volIn = volumeAllOpt->getVolume(1); bool fromCropVol = volumeAllOpt->getOptionalParameter(2)->m_present; AlgorithmCiftiReplaceStructure(NULL, &myCifti, myDir, volIn, fromCropVol, discardUnusedLabels); } myCifti.writeFile(ciftiName);//and write the modified file } AlgorithmCiftiReplaceStructure::AlgorithmCiftiReplaceStructure(ProgressObject* myProgObj, CiftiFile* ciftiInOut, const int& myDir, const StructureEnum::Enum& myStruct, const MetricFile* metricIn) : AbstractAlgorithm(myProgObj) { LevelProgress myProgress(myProgObj); const CiftiXML& myXML = ciftiInOut->getCiftiXML(); if (myXML.getNumberOfDimensions() != 2) throw AlgorithmException("replace structure only supported on 2D cifti"); if (myXML.getMappingType(myDir) != CiftiMappingType::BRAIN_MODELS) throw AlgorithmException("specified dimension must contain brain models"); const CiftiBrainModelsMap& myDenseMap = myXML.getBrainModelsMap(myDir); vector myMap; int rowSize = ciftiInOut->getNumberOfColumns(), colSize = ciftiInOut->getNumberOfRows(); if (myDir == CiftiXML::ALONG_COLUMN) { myMap = myDenseMap.getSurfaceMap(myStruct); int64_t numNodes = myDenseMap.getSurfaceNumberOfNodes(myStruct); if (metricIn->getNumberOfNodes() != numNodes) { throw AlgorithmException("input metric has the wrong number of vertices"); } if (metricIn->getNumberOfColumns() != rowSize) { throw AlgorithmException("input metric has the wrong number of columns"); } int mapSize = (int)myMap.size(); CaretArray rowScratch(rowSize); for (int i = 0; i < mapSize; ++i) { for (int j = 0; j < rowSize; ++j) { rowScratch[j] = metricIn->getValue(myMap[i].m_surfaceNode, j); } ciftiInOut->setRow(rowScratch, myMap[i].m_ciftiIndex); } } else { if (myDir != CiftiXML::ALONG_ROW) throw AlgorithmException("unsupported cifti direction"); myMap = myDenseMap.getSurfaceMap(myStruct); int64_t numNodes = myDenseMap.getSurfaceNumberOfNodes(myStruct); if (metricIn->getNumberOfNodes() != numNodes) { throw AlgorithmException("input metric has the wrong number of vertices"); } if (metricIn->getNumberOfColumns() != colSize) { throw AlgorithmException("input metric has the wrong number of columns"); } int mapSize = (int)myMap.size(); CaretArray rowScratch(rowSize); for (int i = 0; i < colSize; ++i) { ciftiInOut->getRow(rowScratch, i, true); for (int j = 0; j < mapSize; ++j) { rowScratch[myMap[j].m_ciftiIndex] = metricIn->getValue(myMap[j].m_surfaceNode, i); } ciftiInOut->setRow(rowScratch, i); } } } AlgorithmCiftiReplaceStructure::AlgorithmCiftiReplaceStructure(ProgressObject* myProgObj, CiftiFile* ciftiInOut, const int& myDir, const StructureEnum::Enum& myStruct, const LabelFile* labelIn, const bool& discardUnusedLabels) : AbstractAlgorithm(myProgObj) { LevelProgress myProgress(myProgObj); const CiftiXML& myXML = ciftiInOut->getCiftiXML(); if (myXML.getNumberOfDimensions() != 2) throw AlgorithmException("replace structure only supported on 2D cifti"); if (myXML.getMappingType(myDir) != CiftiMappingType::BRAIN_MODELS) throw AlgorithmException("specified dimension must contain brain models"); const CiftiBrainModelsMap& myDenseMap = myXML.getBrainModelsMap(myDir); ciftiInOut->convertToInMemory();//so that writing it can change the header vector myMap; int64_t rowSize = ciftiInOut->getNumberOfColumns(), colSize = ciftiInOut->getNumberOfRows(); if (myDir == CiftiXML::ALONG_COLUMN) { if (myXML.getMappingType(CiftiXML::ALONG_ROW) != CiftiMappingType::LABELS) throw AlgorithmException("label separate requested on non-label cifti"); const CiftiLabelsMap& myLabelsMap = myXML.getLabelsMap(CiftiXML::ALONG_ROW); myMap = myDenseMap.getSurfaceMap(myStruct); int64_t numNodes = myDenseMap.getSurfaceNumberOfNodes(myStruct); if (labelIn->getNumberOfNodes() != numNodes) { throw AlgorithmException("input label file has the wrong number of vertices"); } if (labelIn->getNumberOfColumns() != rowSize) { throw AlgorithmException("input label file has the wrong number of columns"); } int64_t mapSize = (int64_t)myMap.size(); CaretArray rowScratch(rowSize, 0.0f);//initialize to the default "unused" value in case we get short reads due to unallocated on-disk cifti vector > usedArray(rowSize); vector > remapArray(rowSize); for (int64_t j = 0; j < rowSize; ++j) { GiftiLabelTable myTable = *(labelIn->getLabelTable());//we remap the old label table values so that the new label table keys are unmolested remapArray[j] = myTable.append(*(myLabelsMap.getMapLabelTable(j))); *(myLabelsMap.getMapLabelTable(j)) = myTable; } set writeRows; for (int64_t i = 0; i < mapSize; ++i) { writeRows.insert(myMap[i].m_ciftiIndex); } for (int64_t i = 0; i < colSize; ++i)//we have to remap all old data since we are changing he keys of the old label table { if (writeRows.find(i) == writeRows.end()) { ciftiInOut->getRow(rowScratch, i, true); bool rowChanged = false; for (int64_t j = 0; j < rowSize; ++j) { int32_t tempKey = (int32_t)floor(rowScratch[j] + 0.5f); map::iterator iter = remapArray[j].find(tempKey); if (iter != remapArray[j].end()) { if (iter->first != iter->second) { rowChanged = true; rowScratch[j] = iter->second;//remap the key if its value changes usedArray[j].insert(iter->second); } else { usedArray[j].insert(tempKey); } } else { rowChanged = true; int32_t unusedLabel = myLabelsMap.getMapLabelTable(j)->getUnassignedLabelKey(); rowScratch[j] = unusedLabel; usedArray[j].insert(unusedLabel); } } if (rowChanged) ciftiInOut->setRow(rowScratch, i); } } for (int64_t i = 0; i < mapSize; ++i)//set the new rows { for (int64_t j = 0; j < rowSize; ++j) { int32_t tempKey = labelIn->getLabelKey(myMap[i].m_surfaceNode, j); usedArray[j].insert(tempKey); rowScratch[j] = tempKey; } ciftiInOut->setRow(rowScratch, myMap[i].m_ciftiIndex); } if (discardUnusedLabels) { for (int64_t i = 0; i < rowSize; ++i)//delete unused labels { myLabelsMap.getMapLabelTable(i)->deleteUnusedLabels(usedArray[i]); } } } else { if (myDir != CiftiXML::ALONG_ROW) throw AlgorithmException("unsupported cifti direction"); if (myXML.getMappingType(CiftiXML::ALONG_COLUMN) != CiftiMappingType::LABELS) throw AlgorithmException("label separate requested on non-label cifti"); const CiftiLabelsMap& myLabelsMap = myXML.getLabelsMap(CiftiXML::ALONG_COLUMN); myMap = myDenseMap.getSurfaceMap(myStruct); { throw AlgorithmException("structure not found in specified dimension"); } int64_t numNodes = myDenseMap.getSurfaceNumberOfNodes(myStruct); if (labelIn->getNumberOfNodes() != numNodes) { throw AlgorithmException("input label file has the wrong number of vertices"); } if (labelIn->getNumberOfColumns() != colSize) { throw AlgorithmException("input label file has the wrong number of columns"); } int64_t mapSize = (int64_t)myMap.size(); CaretArray rowScratch(rowSize, 0.0f); set writeCols; for (int64_t i = 0; i < mapSize; ++i) { writeCols.insert(myMap[i].m_ciftiIndex); } for (int64_t i = 0; i < colSize; ++i) { GiftiLabelTable myTable = *(labelIn->getLabelTable());//we remap the old label table values so that the new label table keys are unmolested map remap = myTable.append(*(myLabelsMap.getMapLabelTable(i))); *(myLabelsMap.getMapLabelTable(i)) = myTable; ciftiInOut->getRow(rowScratch, i, true); set used; int32_t unusedKey = myTable.getUnassignedLabelKey(); for(int64_t j = 0; j < rowSize; ++j) { if (writeCols.find(j) == writeCols.end()) { int32_t tempKey = (int32_t)floor(rowScratch[j] + 0.5f); map::iterator iter = remap.find(tempKey); if (iter != remap.end()) { rowScratch[j] = iter->second; used.insert(iter->second); } else { rowScratch[j] = unusedKey; used.insert(unusedKey); } } } for (int64_t j = 0; j < mapSize; ++j) { int32_t tempKey = labelIn->getLabelKey(myMap[j].m_surfaceNode, i); rowScratch[myMap[j].m_ciftiIndex] = tempKey; used.insert(tempKey); } if (discardUnusedLabels) { myLabelsMap.getMapLabelTable(i)->deleteUnusedLabels(used); } ciftiInOut->setRow(rowScratch, i); } } } AlgorithmCiftiReplaceStructure::AlgorithmCiftiReplaceStructure(ProgressObject* myProgObj, CiftiFile* ciftiInOut, const int& myDir, const StructureEnum::Enum& myStruct, const VolumeFile* volIn, const bool& fromCropped, const bool& discardUnusedLabels) : AbstractAlgorithm(myProgObj) { const CiftiXML& myXML = ciftiInOut->getCiftiXML(); if (myDir != CiftiXML::ALONG_ROW && myDir != CiftiXML::ALONG_COLUMN) throw AlgorithmException("direction not supported in cifti replace structure"); if (myXML.getNumberOfDimensions() != 2) throw AlgorithmException("replace structure only supported on 2D cifti"); LevelProgress myProgress(myProgObj); int64_t rowSize = ciftiInOut->getNumberOfColumns(), colSize = ciftiInOut->getNumberOfRows(); if (myXML.getMappingType(myDir) != CiftiMappingType::BRAIN_MODELS) throw AlgorithmException("specified direction does not contain brain models"); const CiftiBrainModelsMap& myBrainMap = myXML.getBrainModelsMap(myDir); const int64_t* myDims = myBrainMap.getVolumeSpace().getDims(); vector > mySform = myBrainMap.getVolumeSpace().getSform(); vector myMap = myBrainMap.getVolumeStructureMap(myStruct); int64_t numVoxels = (int64_t)myMap.size(); int64_t offset[3]; vector newdims; if (fromCropped) { newdims.resize(3); AlgorithmCiftiSeparate::getCroppedVolSpace(ciftiInOut, myDir, myStruct, newdims.data(), mySform, offset); } else { newdims.push_back(myDims[0]); newdims.push_back(myDims[1]); newdims.push_back(myDims[2]); offset[0] = 0; offset[1] = 0; offset[2] = 0; } if (!volIn->matchesVolumeSpace(newdims.data(), mySform)) { throw AlgorithmException("input volume doesn't match volume space and dimensions in CIFTI"); } CaretArray rowScratch(rowSize, 0.0f);//initialize with default unused label in case dlabel and short reads due to uninitialized on-disk cifti vector volDims; volIn->getDimensions(volDims); if (myDir == CiftiXML::ALONG_COLUMN) { if (volDims[3] != rowSize) { throw AlgorithmException("volume has the wrong number of subvolumes"); } if (myXML.getMappingType(CiftiXML::ALONG_ROW) == CiftiMappingType::LABELS) { if (volIn->getType() != SubvolumeAttributes::LABEL) throw AlgorithmException("replace structure called on cifti label file with non-label volume"); ciftiInOut->convertToInMemory();//so that writing can change the XML const CiftiLabelsMap& myLabelsMap = myXML.getLabelsMap(CiftiXML::ALONG_ROW); vector > remapArray(rowSize); vector > usedArray(rowSize); for (int64_t i = 0; i < rowSize; ++i) { GiftiLabelTable myTable = *(volIn->getMapLabelTable(i));//we remap the old label table values so that the new label table keys are unmolested remapArray[i] = myTable.append(*(myLabelsMap.getMapLabelTable(i))); *(myLabelsMap.getMapLabelTable(i)) = myTable; } set writeRows; for (int64_t i = 0; i < numVoxels; ++i) { writeRows.insert(myMap[i].m_ciftiIndex); } for (int64_t i = 0; i < colSize; ++i) { if (writeRows.find(i) == writeRows.end()) { ciftiInOut->getRow(rowScratch, i, true); bool rowChanged = false; for (int64_t j = 0; j < rowSize; ++j) { int32_t tempKey = (int32_t)floor(rowScratch[j] + 0.5f); map::iterator iter = remapArray[j].find(tempKey); if (iter != remapArray[j].end()) { if (iter->first != iter->second) { rowChanged = true; rowScratch[j] = iter->second;//remap the key if its value changes usedArray[j].insert(iter->second); } else { usedArray[j].insert(tempKey); } } else { rowChanged = true; int32_t unusedLabel = myLabelsMap.getMapLabelTable(j)->getUnassignedLabelKey(); rowScratch[j] = unusedLabel; usedArray[j].insert(unusedLabel); } } if (rowChanged) ciftiInOut->setRow(rowScratch, i); } } for (int64_t i = 0; i < numVoxels; ++i) { int64_t thisvoxel[3] = { myMap[i].m_ijk[0] - offset[0], myMap[i].m_ijk[1] - offset[1], myMap[i].m_ijk[2] - offset[2] }; for (int j = 0; j < rowSize; ++j) { int32_t tempKey = (int32_t)floor(volIn->getValue(thisvoxel, j) + 0.5f); usedArray[j].insert(tempKey); rowScratch[j] = tempKey; } ciftiInOut->setRow(rowScratch, myMap[i].m_ciftiIndex); } if (discardUnusedLabels) { for (int64_t i = 0; i < rowSize; ++i)//delete unused labels { myLabelsMap.getMapLabelTable(i)->deleteUnusedLabels(usedArray[i]); } } } else { for (int64_t i = 0; i < numVoxels; ++i) { int64_t thisvoxel[3] = { myMap[i].m_ijk[0] - offset[0], myMap[i].m_ijk[1] - offset[1], myMap[i].m_ijk[2] - offset[2] }; for (int j = 0; j < rowSize; ++j) { rowScratch[j] = volIn->getValue(thisvoxel, j); } ciftiInOut->setRow(rowScratch, myMap[i].m_ciftiIndex); } } } else { if (volDims[3] != colSize) { throw AlgorithmException("volume has the wrong number of subvolumes"); } if (myXML.getMappingType(CiftiXML::ALONG_COLUMN) == CiftiMappingType::LABELS) { if (volIn->getType() != SubvolumeAttributes::LABEL) throw AlgorithmException("replace structure called on cifti label file with non-label volume"); ciftiInOut->convertToInMemory(); const CiftiLabelsMap& myLabelsMap = myXML.getLabelsMap(CiftiXML::ALONG_COLUMN); set writeCols; for (int64_t i = 0; i < numVoxels; ++i) { writeCols.insert(myMap[i].m_ciftiIndex); } for (int64_t i = 0; i < colSize; ++i) { GiftiLabelTable myTable = *(volIn->getMapLabelTable(i));//we remap the old label table values so that the new label table keys are unmolested map remap = myTable.append(*(myLabelsMap.getMapLabelTable(i))); *(myLabelsMap.getMapLabelTable(i)) = myTable; ciftiInOut->getRow(rowScratch, i, true); set used; int32_t unusedKey = myTable.getUnassignedLabelKey(); for(int64_t j = 0; j < rowSize; ++j) { if (writeCols.find(j) == writeCols.end()) { int32_t tempKey = (int32_t)floor(rowScratch[j] + 0.5f); map::iterator iter = remap.find(tempKey); if (iter != remap.end()) { rowScratch[j] = iter->second; used.insert(iter->second); } else { rowScratch[j] = unusedKey; used.insert(unusedKey); } } } for (int64_t j = 0; j < numVoxels; ++j) { int64_t thisvoxel[3] = { myMap[j].m_ijk[0] - offset[0], myMap[j].m_ijk[1] - offset[1], myMap[j].m_ijk[2] - offset[2] }; int32_t tempKey = (int32_t)floor(volIn->getValue(thisvoxel, i) + 0.5f); rowScratch[myMap[j].m_ciftiIndex] = tempKey; used.insert(tempKey); } if (discardUnusedLabels) { myLabelsMap.getMapLabelTable(i)->deleteUnusedLabels(used); } ciftiInOut->setRow(rowScratch, i); } } else { for (int64_t i = 0; i < colSize; ++i) { ciftiInOut->getRow(rowScratch, i, true);//the on-disk cifti file may not have been allocated yet, so short reads are okay for (int64_t j = 0; j < numVoxels; ++j) { int64_t thisvoxel[3] = { myMap[j].m_ijk[0] - offset[0], myMap[j].m_ijk[1] - offset[1], myMap[j].m_ijk[2] - offset[2] }; rowScratch[myMap[j].m_ciftiIndex] = volIn->getValue(thisvoxel, i); } ciftiInOut->setRow(rowScratch, i); } } } } AlgorithmCiftiReplaceStructure::AlgorithmCiftiReplaceStructure(ProgressObject* myProgObj, CiftiFile* ciftiInOut, const int& myDir, const VolumeFile* volIn, const bool& fromCropped, const bool& discardUnusedLabels): AbstractAlgorithm(myProgObj) { const CiftiXML& myXML = ciftiInOut->getCiftiXML(); if (myXML.getNumberOfDimensions() != 2) throw AlgorithmException("replace structure only supported on 2D cifti"); LevelProgress myProgress(myProgObj); if (myDir != CiftiXML::ALONG_ROW && myDir != CiftiXML::ALONG_COLUMN) throw AlgorithmException("direction not supported in cifti replace structure"); if (myXML.getMappingType(myDir) != CiftiMappingType::BRAIN_MODELS) throw AlgorithmException("specified direction does not contain brain models"); const CiftiBrainModelsMap& myBrainMap = myXML.getBrainModelsMap(myDir); const int64_t* myDims = myBrainMap.getVolumeSpace().getDims(); vector > mySform = myBrainMap.getVolumeSpace().getSform(); vector myMap = myBrainMap.getFullVolumeMap(); int64_t numVoxels = (int64_t)myMap.size(); int64_t rowSize = ciftiInOut->getNumberOfColumns(), colSize = ciftiInOut->getNumberOfRows(); vector newdims; int64_t offset[3]; if (fromCropped) { newdims.resize(3); AlgorithmCiftiSeparate::getCroppedVolSpaceAll(ciftiInOut, myDir, newdims.data(), mySform, offset); } else { newdims.push_back(myDims[0]); newdims.push_back(myDims[1]); newdims.push_back(myDims[2]); offset[0] = 0; offset[1] = 0; offset[2] = 0; } if (!volIn->matchesVolumeSpace(newdims.data(), mySform)) { throw AlgorithmException("input volume doesn't match volume space and dimensions in CIFTI"); } CaretArray rowScratch(rowSize, 0.0f);//initialize with default unused label in case dlabel and short reads due to uninitialized on-disk cifti vector volDims; volIn->getDimensions(volDims); if (myDir == CiftiXML::ALONG_COLUMN) { if (volDims[3] != rowSize) { throw AlgorithmException("volume has the wrong number of subvolumes"); } if (myXML.getMappingType(CiftiXML::ALONG_ROW) == CiftiMappingType::LABELS) { const CiftiLabelsMap& myLabelsMap = myXML.getLabelsMap(CiftiXML::ALONG_ROW); if (volIn->getType() != SubvolumeAttributes::LABEL) throw AlgorithmException("replace structure called on cifti label file with non-label volume"); ciftiInOut->convertToInMemory(); vector > remapArray(rowSize); vector > usedArray(rowSize); for (int64_t i = 0; i < rowSize; ++i) { GiftiLabelTable myTable = *(volIn->getMapLabelTable(i));//we remap the old label table values so that the new label table keys are unmolested remapArray[i] = myTable.append(*(myLabelsMap.getMapLabelTable(i))); *(myLabelsMap.getMapLabelTable(i)) = myTable; } set writeRows; for (int64_t i = 0; i < numVoxels; ++i) { writeRows.insert(myMap[i].m_ciftiIndex); } for (int64_t i = 0; i < colSize; ++i) { if (writeRows.find(i) == writeRows.end()) { ciftiInOut->getRow(rowScratch, i, true); bool rowChanged = false; for (int64_t j = 0; j < rowSize; ++j) { int32_t tempKey = (int32_t)floor(rowScratch[j] + 0.5f); map::iterator iter = remapArray[j].find(tempKey); if (iter != remapArray[j].end()) { if (iter->first != iter->second) { rowChanged = true; rowScratch[j] = iter->second;//remap the key if its value changes usedArray[j].insert(iter->second); } else { usedArray[j].insert(tempKey); } } else { rowChanged = true; int32_t unusedLabel = myLabelsMap.getMapLabelTable(j)->getUnassignedLabelKey(); rowScratch[j] = unusedLabel; usedArray[j].insert(unusedLabel); } } if (rowChanged) ciftiInOut->setRow(rowScratch, i); } } for (int64_t i = 0; i < numVoxels; ++i) { int64_t thisvoxel[3] = { myMap[i].m_ijk[0] - offset[0], myMap[i].m_ijk[1] - offset[1], myMap[i].m_ijk[2] - offset[2] }; for (int j = 0; j < rowSize; ++j) { int32_t tempKey = (int32_t)floor(volIn->getValue(thisvoxel, j) + 0.5f); usedArray[j].insert(tempKey); rowScratch[j] = tempKey; } ciftiInOut->setRow(rowScratch, myMap[i].m_ciftiIndex); } if (discardUnusedLabels) { for (int64_t i = 0; i < rowSize; ++i)//delete unused labels { myLabelsMap.getMapLabelTable(i)->deleteUnusedLabels(usedArray[i]); } } } else { for (int64_t i = 0; i < numVoxels; ++i) { int64_t thisvoxel[3] = { myMap[i].m_ijk[0] - offset[0], myMap[i].m_ijk[1] - offset[1], myMap[i].m_ijk[2] - offset[2] }; for (int j = 0; j < rowSize; ++j) { rowScratch[j] = volIn->getValue(thisvoxel, j); } ciftiInOut->setRow(rowScratch, myMap[i].m_ciftiIndex); } } } else { if (volDims[3] != colSize) { throw AlgorithmException("volume has the wrong number of subvolumes"); } if (myXML.getMappingType(CiftiXML::ALONG_COLUMN) == CiftiMappingType::LABELS) { const CiftiLabelsMap& myLabelsMap = myXML.getLabelsMap(CiftiXML::ALONG_COLUMN); if (volIn->getType() != SubvolumeAttributes::LABEL) throw AlgorithmException("replace structure called on cifti label file with non-label volume"); ciftiInOut->convertToInMemory(); set writeCols; for (int64_t i = 0; i < numVoxels; ++i) { writeCols.insert(myMap[i].m_ciftiIndex); } for (int64_t i = 0; i < colSize; ++i) { GiftiLabelTable myTable = *(volIn->getMapLabelTable(i));//we remap the old label table values so that the new label table keys are unmolested map remap = myTable.append(*(myLabelsMap.getMapLabelTable(i))); *(myLabelsMap.getMapLabelTable(i)) = myTable; ciftiInOut->getRow(rowScratch, i, true); set used; int32_t unusedKey = myTable.getUnassignedLabelKey(); for(int64_t j = 0; j < rowSize; ++j) { if (writeCols.find(j) == writeCols.end()) { int32_t tempKey = (int32_t)floor(rowScratch[j] + 0.5f); map::iterator iter = remap.find(tempKey); if (iter != remap.end()) { rowScratch[j] = iter->second; used.insert(iter->second); } else { rowScratch[j] = unusedKey; used.insert(unusedKey); } } } for (int64_t j = 0; j < numVoxels; ++j) { int64_t thisvoxel[3] = { myMap[j].m_ijk[0] - offset[0], myMap[j].m_ijk[1] - offset[1], myMap[j].m_ijk[2] - offset[2] }; int32_t tempKey = (int32_t)floor(volIn->getValue(thisvoxel, i) + 0.5f); rowScratch[myMap[j].m_ciftiIndex] = tempKey; used.insert(tempKey); } if (discardUnusedLabels) { myLabelsMap.getMapLabelTable(i)->deleteUnusedLabels(used); } ciftiInOut->setRow(rowScratch, i); } } else { for (int64_t i = 0; i < colSize; ++i) { ciftiInOut->getRow(rowScratch, i, true);//the on-disk cifti file may not have been allocated yet, so short reads are okay for (int64_t j = 0; j < numVoxels; ++j) { int64_t thisvoxel[3] = { myMap[j].m_ijk[0] - offset[0], myMap[j].m_ijk[1] - offset[1], myMap[j].m_ijk[2] - offset[2] }; rowScratch[myMap[j].m_ciftiIndex] = volIn->getValue(thisvoxel, i); } ciftiInOut->setRow(rowScratch, i); } } } } float AlgorithmCiftiReplaceStructure::getAlgorithmInternalWeight() { return 1.0f;//override this if needed, if the progress bar isn't smooth } float AlgorithmCiftiReplaceStructure::getSubAlgorithmWeight() { return 0.0f; } connectome-workbench-1.2.3+git41-gc4c6c90/src/Algorithms/AlgorithmCiftiReplaceStructure.h000066400000000000000000000051071300200146000311530ustar00rootroot00000000000000#ifndef __ALGORITHM_CIFTI_REPLACE_STRUCTURE_H__ #define __ALGORITHM_CIFTI_REPLACE_STRUCTURE_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 "AbstractAlgorithm.h" #include "StructureEnum.h" namespace caret { class AlgorithmCiftiReplaceStructure : public AbstractAlgorithm { AlgorithmCiftiReplaceStructure(); protected: static float getSubAlgorithmWeight(); static float getAlgorithmInternalWeight(); public: AlgorithmCiftiReplaceStructure(ProgressObject* myProgObj, CiftiFile* ciftiInOut, const int& myDir, const StructureEnum::Enum& myStruct, const MetricFile* metricIn); AlgorithmCiftiReplaceStructure(ProgressObject* myProgObj, CiftiFile* ciftiInOut, const int& myDir, const StructureEnum::Enum& myStruct, const LabelFile* labelIn, const bool& discardUnusedLabels = false); AlgorithmCiftiReplaceStructure(ProgressObject* myProgObj, CiftiFile* ciftiInOut, const int& myDir, const StructureEnum::Enum& myStruct, const VolumeFile* volIn, const bool& fromCropped, const bool& discardUnusedLabels = false); AlgorithmCiftiReplaceStructure(ProgressObject* myProgObj, CiftiFile* ciftiInOut, const int& myDir, const VolumeFile* volIn, const bool& fromCropped, const bool& discardUnusedLabels = false); static OperationParameters* getParameters(); static void useParameters(OperationParameters* myParams, ProgressObject* myProgObj); static AString getCommandSwitch(); static AString getShortDescription(); }; typedef TemplateAutoOperation AutoAlgorithmCiftiReplaceStructure; } #endif //__ALGORITHM_CIFTI_REPLACE_STRUCTURE_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Algorithms/AlgorithmCiftiResample.cxx000066400000000000000000002012511300200146000300000ustar00rootroot00000000000000/*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 "AlgorithmCiftiResample.h" #include "AlgorithmException.h" #include "AffineFile.h" #include "AlgorithmCiftiSeparate.h" #include "AlgorithmCiftiReplaceStructure.h" #include "AlgorithmLabelDilate.h" #include "AlgorithmLabelResample.h" #include "AlgorithmMetricResample.h" #include "AlgorithmVolumeAffineResample.h" #include "AlgorithmVolumeWarpfieldResample.h" #include "CiftiFile.h" #include "LabelFile.h" #include "MetricFile.h" #include "SurfaceFile.h" #include "SurfaceResamplingHelper.h" #include "VolumeFile.h" #include "VolumePaddingHelper.h" #include "WarpfieldFile.h" #include #include using namespace caret; using namespace std; AString AlgorithmCiftiResample::getCommandSwitch() { return "-cifti-resample"; } AString AlgorithmCiftiResample::getShortDescription() { return "RESAMPLE A CIFTI FILE TO A NEW CIFTI SPACE"; } OperationParameters* AlgorithmCiftiResample::getParameters() { OperationParameters* ret = new OperationParameters(); ret->addCiftiParameter(1, "cifti-in", "the cifti file to resample"); ret->addStringParameter(2, "direction", "the direction of the input that should be resampled, ROW or COLUMN"); ret->addCiftiParameter(3, "cifti-template", "a cifti file containing the cifti space to resample to"); ret->addStringParameter(4, "template-direction", "the direction of the template to use as the resampling space, ROW or COLUMN"); ret->addStringParameter(5, "surface-method", "specify a surface resampling method"); ret->addStringParameter(6, "volume-method", "specify a volume interpolation method"); ret->addCiftiOutputParameter(7, "cifti-out", "the output cifti file"); ret->createOptionalParameter(8, "-surface-largest", "use largest weight instead of weighted average or popularity when doing surface resampling"); OptionalParameter* volDilateOpt = ret->createOptionalParameter(9, "-volume-predilate", "dilate the volume components before resampling"); volDilateOpt->addDoubleParameter(1, "dilate-mm", "distance, in mm, to dilate"); volDilateOpt->createOptionalParameter(2, "-nearest", "use nearest value dilation"); OptionalParameter* volDilateWeightedOpt = volDilateOpt->createOptionalParameter(3, "-weighted", "use weighted dilation (default)"); OptionalParameter* volDilateExpOpt = volDilateWeightedOpt->createOptionalParameter(1, "-exponent", "specify exponent in weighting function"); volDilateExpOpt->addDoubleParameter(1, "exponent", "exponent 'n' to use in (1 / (distance ^ n)) as the weighting function (default 2)"); OptionalParameter* surfDilateOpt = ret->createOptionalParameter(10, "-surface-postdilate", "dilate the surface components after resampling"); surfDilateOpt->addDoubleParameter(1, "dilate-mm", "distance, in mm, to dilate"); surfDilateOpt->createOptionalParameter(2, "-nearest", "use nearest value dilation"); surfDilateOpt->createOptionalParameter(3, "-linear", "use linear dilation"); OptionalParameter* surfDilateWeightedOpt = surfDilateOpt->createOptionalParameter(4, "-weighted", "use weighted dilation (default for non-label data)"); OptionalParameter* surfDilateExpOpt = surfDilateWeightedOpt->createOptionalParameter(1, "-exponent", "specify exponent in weighting function"); surfDilateExpOpt->addDoubleParameter(1, "exponent", "exponent 'n' to use in (area / (distance ^ n)) as the weighting function (default 2)"); OptionalParameter* affineOpt = ret->createOptionalParameter(11, "-affine", "use an affine transformation on the volume components"); affineOpt->addStringParameter(1, "affine-file", "the affine file to use"); OptionalParameter* flirtOpt = affineOpt->createOptionalParameter(2, "-flirt", "MUST be used if affine is a flirt affine"); flirtOpt->addStringParameter(1, "source-volume", "the source volume used when generating the affine"); flirtOpt->addStringParameter(2, "target-volume", "the target volume used when generating the affine"); OptionalParameter* warpfieldOpt = ret->createOptionalParameter(12, "-warpfield", "use a warpfield on the volume components"); warpfieldOpt->addStringParameter(1, "warpfield", "the warpfield to use"); OptionalParameter* fnirtOpt = warpfieldOpt->createOptionalParameter(2, "-fnirt", "MUST be used if using a fnirt warpfield"); fnirtOpt->addStringParameter(1, "source-volume", "the source volume used when generating the warpfield"); OptionalParameter* leftSpheresOpt = ret->createOptionalParameter(13, "-left-spheres", "specify spheres for left surface resampling"); leftSpheresOpt->addSurfaceParameter(1, "current-sphere", "a sphere with the same mesh as the current left surface"); leftSpheresOpt->addSurfaceParameter(2, "new-sphere", "a sphere with the new left mesh that is in register with the current sphere"); OptionalParameter* leftAreaSurfsOpt = leftSpheresOpt->createOptionalParameter(3, "-left-area-surfs", "specify left surfaces to do vertex area correction based on"); leftAreaSurfsOpt->addSurfaceParameter(1, "current-area", "a relevant left anatomical surface with current mesh"); leftAreaSurfsOpt->addSurfaceParameter(2, "new-area", "a relevant left anatomical surface with new mesh"); OptionalParameter* leftAreaMetricsOpt = leftSpheresOpt->createOptionalParameter(4, "-left-area-metrics", "specify left vertex area metrics to do area correction based on"); leftAreaMetricsOpt->addMetricParameter(1, "current-area", "a metric file with vertex areas for the current mesh"); leftAreaMetricsOpt->addMetricParameter(2, "new-area", "a metric file with vertex areas for the new mesh"); OptionalParameter* rightSpheresOpt = ret->createOptionalParameter(14, "-right-spheres", "specify spheres for right surface resampling"); rightSpheresOpt->addSurfaceParameter(1, "current-sphere", "a sphere with the same mesh as the current right surface"); rightSpheresOpt->addSurfaceParameter(2, "new-sphere", "a sphere with the new right mesh that is in register with the current sphere"); OptionalParameter* rightAreaSurfsOpt = rightSpheresOpt->createOptionalParameter(3, "-right-area-surfs", "specify right surfaces to do vertex area correction based on"); rightAreaSurfsOpt->addSurfaceParameter(1, "current-area", "a relevant right anatomical surface with current mesh"); rightAreaSurfsOpt->addSurfaceParameter(2, "new-area", "a relevant right anatomical surface with new mesh"); OptionalParameter* rightAreaMetricsOpt = rightSpheresOpt->createOptionalParameter(4, "-right-area-metrics", "specify right vertex area metrics to do area correction based on"); rightAreaMetricsOpt->addMetricParameter(1, "current-area", "a metric file with vertex areas for the current mesh"); rightAreaMetricsOpt->addMetricParameter(2, "new-area", "a metric file with vertex areas for the new mesh"); OptionalParameter* cerebSpheresOpt = ret->createOptionalParameter(15, "-cerebellum-spheres", "specify spheres for cerebellum surface resampling"); cerebSpheresOpt->addSurfaceParameter(1, "current-sphere", "a sphere with the same mesh as the current cerebellum surface"); cerebSpheresOpt->addSurfaceParameter(2, "new-sphere", "a sphere with the new cerebellum mesh that is in register with the current sphere"); OptionalParameter* cerebAreaSurfsOpt = cerebSpheresOpt->createOptionalParameter(3, "-cerebellum-area-surfs", "specify cerebellum surfaces to do vertex area correction based on"); cerebAreaSurfsOpt->addSurfaceParameter(1, "current-area", "a relevant cerebellum anatomical surface with current mesh"); cerebAreaSurfsOpt->addSurfaceParameter(2, "new-area", "a relevant cerebellum anatomical surface with new mesh"); OptionalParameter* cerebAreaMetricsOpt = cerebSpheresOpt->createOptionalParameter(4, "-cerebellum-area-metrics", "specify cerebellum vertex area metrics to do area correction based on"); cerebAreaMetricsOpt->addMetricParameter(1, "current-area", "a metric file with vertex areas for the current mesh"); cerebAreaMetricsOpt->addMetricParameter(2, "new-area", "a metric file with vertex areas for the new mesh"); AString myHelpText = AString("Resample cifti data to a different brainordinate space. Use COLUMN for the direction to resample dscalar, dlabel, or dtseries. ") + "Resampling both dimensions of a dconn requires running this command twice, once with COLUMN and once with ROW. " + "If you are resampling a dconn and your machine has a large amount of memory, you might consider using -cifti-resample-dconn-memory to avoid writing and rereading an intermediate file. " + "The argument should usually be COLUMN, as dtseries, dscalar, and dlabel all have brainordinates on that direction. " + "If spheres are not specified for a surface structure which exists in the cifti files, its data is copied without resampling or dilation. " + "Dilation is done with the 'nearest' method, and is done on for surface data. " + "Volume components are padded before dilation so that dilation doesn't run into the edge of the component bounding box. " + "If neither -affine nor -warpfield are specified, the identity transform is assumed for the volume data.\n\n" + "The recommended resampling methods are ADAP_BARY_AREA and CUBIC (cubic spline), except for label data which should use ADAP_BARY_AREA and ENCLOSING_VOXEL. " + "Using ADAP_BARY_AREA requires specifying an area option to each used -*-spheres option.\n\n" + "The argument must be one of the following:\n\n" + "CUBIC\nENCLOSING_VOXEL\nTRILINEAR\n\n" + "The argument must be one of the following:\n\n"; vector allEnums; SurfaceResamplingMethodEnum::getAllEnums(allEnums); for (int i = 0; i < (int)allEnums.size(); ++i) { myHelpText += SurfaceResamplingMethodEnum::toName(allEnums[i]) + "\n"; } ret->setHelpText(myHelpText); return ret; } void AlgorithmCiftiResample::useParameters(OperationParameters* myParams, ProgressObject* myProgObj) { CiftiFile* myCiftiIn = myParams->getCifti(1); AString myDirString = myParams->getString(2); int direction = -1; if (myDirString == "ROW") { direction = CiftiXML::ALONG_ROW; } else { if (myDirString == "COLUMN") { direction = CiftiXML::ALONG_COLUMN; } else { throw AlgorithmException("unrecognized direction string, use ROW or COLUMN"); } } CiftiFile* myTemplate = myParams->getCifti(3); AString myTemplDirString = myParams->getString(4); int templateDir = -1; if (myTemplDirString == "ROW") { templateDir = CiftiXML::ALONG_ROW; } else { if (myTemplDirString == "COLUMN") { templateDir = CiftiXML::ALONG_COLUMN; } else { throw AlgorithmException("unrecognized template direction string, use ROW or COLUMN"); } } bool ok = false; SurfaceResamplingMethodEnum::Enum mySurfMethod = SurfaceResamplingMethodEnum::fromName(myParams->getString(5), &ok); if (!ok) { throw AlgorithmException("invalid surface resampling method name"); } AString myVolMethodString = myParams->getString(6); VolumeFile::InterpType myVolMethod = VolumeFile::CUBIC; if (myVolMethodString == "CUBIC") { myVolMethod = VolumeFile::CUBIC; } else if (myVolMethodString == "TRILINEAR") { myVolMethod = VolumeFile::TRILINEAR; } else if (myVolMethodString == "ENCLOSING_VOXEL") { myVolMethod = VolumeFile::ENCLOSING_VOXEL; } else { throw AlgorithmException("unrecognized volume interpolation method"); } CiftiFile* myCiftiOut = myParams->getOutputCifti(7); bool surfLargest = myParams->getOptionalParameter(8)->m_present; float voldilatemm = -1.0f, surfdilatemm = -1.0f; bool isLabelData = false;//dilation method arguments checking needs to know if it is label data const CiftiXML& inputXML = myCiftiIn->getCiftiXML(); for (int i = 0; i < inputXML.getNumberOfDimensions(); ++i) { if (inputXML.getMappingType(i) == CiftiMappingType::LABELS) { isLabelData = true; break; } } AlgorithmVolumeDilate::Method volDilateMethod = AlgorithmVolumeDilate::WEIGHTED; float volDilateExponent = 2.0f; AlgorithmMetricDilate::Method surfDilateMethod = AlgorithmMetricDilate::WEIGHTED;//label dilate doesn't support multiple methods - what to do there, share the enum between them? float surfDilateExponent = 2.0f;//label dilate currently only supports nearest, so in order to accept a default in the algorithm, it currently ignores this on label data, no warning OptionalParameter* volDilateOpt = myParams->getOptionalParameter(9);//but it does check the options on the command line for sanity if (volDilateOpt->m_present) { bool methodSpecified = false; voldilatemm = (float)volDilateOpt->getDouble(1); if (voldilatemm <= 0.0f) throw AlgorithmException("dilation amount must be positive"); if (volDilateOpt->getOptionalParameter(2)->m_present) { methodSpecified = true; volDilateMethod = AlgorithmVolumeDilate::NEAREST; } OptionalParameter* volDilateWeightedOpt = volDilateOpt->getOptionalParameter(3); if (volDilateWeightedOpt->m_present) { if (methodSpecified) throw AlgorithmException("cannot specify multiple volume dilation methods"); methodSpecified = true; volDilateMethod = AlgorithmVolumeDilate::WEIGHTED; OptionalParameter* volDilateExpOpt = volDilateWeightedOpt->getOptionalParameter(1); if (volDilateExpOpt->m_present) { volDilateExponent = (float)volDilateExpOpt->getDouble(1); } } } OptionalParameter* surfDilateOpt = myParams->getOptionalParameter(10); if (surfDilateOpt->m_present) { bool methodSpecified = false; surfdilatemm = (float)surfDilateOpt->getDouble(1); if (surfdilatemm <= 0.0f) throw AlgorithmException("dilation amount must be positive"); if (surfDilateOpt->getOptionalParameter(2)->m_present) { methodSpecified = true; surfDilateMethod = AlgorithmMetricDilate::NEAREST; } if (surfDilateOpt->getOptionalParameter(3)->m_present) { if (methodSpecified) throw AlgorithmException("cannot specify multiple surface dilation methods"); methodSpecified = true; if (isLabelData) throw AlgorithmException("cannot do linear surface dilation on label data"); surfDilateMethod = AlgorithmMetricDilate::LINEAR; } OptionalParameter* surfDilateWeightedOpt = surfDilateOpt->getOptionalParameter(4); if (surfDilateWeightedOpt->m_present) { if (methodSpecified) throw AlgorithmException("cannot specify multiple surface dilation methods"); methodSpecified = true; if (isLabelData) throw AlgorithmException("cannot do weighted surface dilation on label data"); surfDilateMethod = AlgorithmMetricDilate::WEIGHTED; OptionalParameter* surfDilateExpOpt = surfDilateWeightedOpt->getOptionalParameter(1); if (surfDilateExpOpt->m_present) { surfDilateExponent = (float)surfDilateExpOpt->getDouble(1); } } } OptionalParameter* affineOpt = myParams->getOptionalParameter(11); OptionalParameter* warpfieldOpt = myParams->getOptionalParameter(12); if (affineOpt->m_present && warpfieldOpt->m_present) throw AlgorithmException("you cannot specify both -affine and -warpfield"); AffineFile myAffine; WarpfieldFile myWarpfield; if (affineOpt->m_present) { OptionalParameter* flirtOpt = affineOpt->getOptionalParameter(2); if (flirtOpt->m_present) { myAffine.readFlirt(affineOpt->getString(1), flirtOpt->getString(1), flirtOpt->getString(2)); } else { myAffine.readWorld(affineOpt->getString(1)); } } if (warpfieldOpt->m_present) { OptionalParameter* fnirtOpt = warpfieldOpt->getOptionalParameter(2); if (fnirtOpt->m_present) { myWarpfield.readFnirt(warpfieldOpt->getString(1), fnirtOpt->getString(1)); } else { myWarpfield.readWorld(warpfieldOpt->getString(1)); } } SurfaceFile* curLeftSphere = NULL, *newLeftSphere = NULL; MetricFile* curLeftAreas = NULL, *newLeftAreas = NULL; MetricFile curLeftAreasTemp, newLeftAreasTemp; OptionalParameter* leftSpheresOpt = myParams->getOptionalParameter(13); if (leftSpheresOpt->m_present) { curLeftSphere = leftSpheresOpt->getSurface(1); newLeftSphere = leftSpheresOpt->getSurface(2); OptionalParameter* leftAreaSurfsOpt = leftSpheresOpt->getOptionalParameter(3); if (leftAreaSurfsOpt->m_present) { SurfaceFile* curAreaSurf = leftAreaSurfsOpt->getSurface(1); SurfaceFile* newAreaSurf = leftAreaSurfsOpt->getSurface(2); vector nodeAreasTemp; curAreaSurf->computeNodeAreas(nodeAreasTemp); curLeftAreasTemp.setNumberOfNodesAndColumns(curAreaSurf->getNumberOfNodes(), 1); curLeftAreasTemp.setValuesForColumn(0, nodeAreasTemp.data()); curLeftAreas = &curLeftAreasTemp; newAreaSurf->computeNodeAreas(nodeAreasTemp); newLeftAreasTemp.setNumberOfNodesAndColumns(newAreaSurf->getNumberOfNodes(), 1); newLeftAreasTemp.setValuesForColumn(0, nodeAreasTemp.data()); newLeftAreas = &newLeftAreasTemp; } OptionalParameter* leftAreaMetricsOpt = leftSpheresOpt->getOptionalParameter(4); if (leftAreaMetricsOpt->m_present) { if (leftAreaSurfsOpt->m_present) { throw AlgorithmException("only one of -left-area-surfs and -left-area-metrics can be specified"); } curLeftAreas = leftAreaMetricsOpt->getMetric(1); newLeftAreas = leftAreaMetricsOpt->getMetric(2); } } SurfaceFile* curRightSphere = NULL, *newRightSphere = NULL; MetricFile* curRightAreas = NULL, *newRightAreas = NULL; MetricFile curRightAreasTemp, newRightAreasTemp; OptionalParameter* rightSpheresOpt = myParams->getOptionalParameter(14); if (rightSpheresOpt->m_present) { curRightSphere = rightSpheresOpt->getSurface(1); newRightSphere = rightSpheresOpt->getSurface(2); OptionalParameter* rightAreaSurfsOpt = rightSpheresOpt->getOptionalParameter(3); if (rightAreaSurfsOpt->m_present) { SurfaceFile* curAreaSurf = rightAreaSurfsOpt->getSurface(1); SurfaceFile* newAreaSurf = rightAreaSurfsOpt->getSurface(2); vector nodeAreasTemp; curAreaSurf->computeNodeAreas(nodeAreasTemp); curRightAreasTemp.setNumberOfNodesAndColumns(curAreaSurf->getNumberOfNodes(), 1); curRightAreasTemp.setValuesForColumn(0, nodeAreasTemp.data()); curRightAreas = &curRightAreasTemp; newAreaSurf->computeNodeAreas(nodeAreasTemp); newRightAreasTemp.setNumberOfNodesAndColumns(newAreaSurf->getNumberOfNodes(), 1); newRightAreasTemp.setValuesForColumn(0, nodeAreasTemp.data()); newRightAreas = &newRightAreasTemp; } OptionalParameter* rightAreaMetricsOpt = rightSpheresOpt->getOptionalParameter(4); if (rightAreaMetricsOpt->m_present) { if (rightAreaSurfsOpt->m_present) { throw AlgorithmException("only one of -right-area-surfs and -right-area-metrics can be specified"); } curRightAreas = rightAreaMetricsOpt->getMetric(1); newRightAreas = rightAreaMetricsOpt->getMetric(2); } } SurfaceFile* curCerebSphere = NULL, *newCerebSphere = NULL; MetricFile* curCerebAreas = NULL, *newCerebAreas = NULL; MetricFile curCerebAreasTemp, newCerebAreasTemp; OptionalParameter* cerebSpheresOpt = myParams->getOptionalParameter(15); if (cerebSpheresOpt->m_present) { curCerebSphere = cerebSpheresOpt->getSurface(1); newCerebSphere = cerebSpheresOpt->getSurface(2); OptionalParameter* cerebAreaSurfsOpt = cerebSpheresOpt->getOptionalParameter(3); if (cerebAreaSurfsOpt->m_present) { SurfaceFile* curAreaSurf = cerebAreaSurfsOpt->getSurface(1); SurfaceFile* newAreaSurf = cerebAreaSurfsOpt->getSurface(2); vector nodeAreasTemp; curAreaSurf->computeNodeAreas(nodeAreasTemp); curCerebAreasTemp.setNumberOfNodesAndColumns(curAreaSurf->getNumberOfNodes(), 1); curCerebAreasTemp.setValuesForColumn(0, nodeAreasTemp.data()); curCerebAreas = &curCerebAreasTemp; newAreaSurf->computeNodeAreas(nodeAreasTemp); newCerebAreasTemp.setNumberOfNodesAndColumns(newAreaSurf->getNumberOfNodes(), 1); newCerebAreasTemp.setValuesForColumn(0, nodeAreasTemp.data()); newCerebAreas = &newCerebAreasTemp; } OptionalParameter* cerebAreaMetricsOpt = cerebSpheresOpt->getOptionalParameter(4); if (cerebAreaMetricsOpt->m_present) { if (cerebAreaSurfsOpt->m_present) { throw AlgorithmException("only one of -cerebellum-area-surfs and -cerebellum-area-metrics can be specified"); } curCerebAreas = cerebAreaMetricsOpt->getMetric(1); newCerebAreas = cerebAreaMetricsOpt->getMetric(2); } } if (warpfieldOpt->m_present) { AlgorithmCiftiResample(myProgObj, myCiftiIn, direction, myTemplate, templateDir, mySurfMethod, myVolMethod, myCiftiOut, surfLargest, voldilatemm, surfdilatemm, myWarpfield.getWarpfield(), curLeftSphere, newLeftSphere, curLeftAreas, newLeftAreas, curRightSphere, newRightSphere, curRightAreas, newRightAreas, curCerebSphere, newCerebSphere, curCerebAreas, newCerebAreas, volDilateMethod, volDilateExponent, surfDilateMethod, surfDilateExponent); } else {//rely on AffineFile() being the identity transform for if neither option is specified AlgorithmCiftiResample(myProgObj, myCiftiIn, direction, myTemplate, templateDir, mySurfMethod, myVolMethod, myCiftiOut, surfLargest, voldilatemm, surfdilatemm, myAffine.getMatrix(), curLeftSphere, newLeftSphere, curLeftAreas, newLeftAreas, curRightSphere, newRightSphere, curRightAreas, newRightAreas, curCerebSphere, newCerebSphere, curCerebAreas, newCerebAreas, volDilateMethod, volDilateExponent, surfDilateMethod, surfDilateExponent); } } pair AlgorithmCiftiResample::checkForErrors(const CiftiFile* myCiftiIn, const int& direction, const CiftiFile* myTemplate, const int& templateDir, const SurfaceResamplingMethodEnum::Enum& mySurfMethod, const SurfaceFile* curLeftSphere, const SurfaceFile* newLeftSphere, const MetricFile* curLeftAreas, const MetricFile* newLeftAreas, const SurfaceFile* curRightSphere, const SurfaceFile* newRightSphere, const MetricFile* curRightAreas, const MetricFile* newRightAreas, const SurfaceFile* curCerebSphere, const SurfaceFile* newCerebSphere, const MetricFile* curCerebAreas, const MetricFile* newCerebAreas) { if (direction > 1) return make_pair(true, AString("unsupported mapping direction for cifti resample")); const CiftiXML& myInputXML = myCiftiIn->getCiftiXML(); if (myInputXML.getNumberOfDimensions() != 2) return make_pair(true, AString("cifti resample only supports 2D cifti")); if (myInputXML.getMappingType(direction) != CiftiMappingType::BRAIN_MODELS) return make_pair(true, AString("direction for input must contain brain models")); const CiftiBrainModelsMap& inModels = myInputXML.getBrainModelsMap(direction); const CiftiXML& myTemplateXML = myTemplate->getCiftiXML(); if (templateDir < 0 || templateDir >= myTemplateXML.getNumberOfDimensions()) return make_pair(true, AString("specified template direction does not exist in template file")); if (myTemplateXML.getMappingType(templateDir) != CiftiMappingType::BRAIN_MODELS) return make_pair(true, AString("direction for template must contain brain models")); const CiftiBrainModelsMap& outModels = myTemplate->getCiftiXML().getBrainModelsMap(templateDir); vector surfList = outModels.getSurfaceStructureList(), volList = outModels.getVolumeStructureList(); for (int i = 0; i < (int)surfList.size(); ++i)//ensure existence of resampling spheres before doing any computation { if (!inModels.hasSurfaceData(surfList[i])) return make_pair(true, AString("input cifti missing surface information for structure: " + StructureEnum::toGuiName(surfList[i]))); const SurfaceFile* curSphere = NULL, *newSphere = NULL; const MetricFile* curAreas = NULL, *newAreas = NULL; AString structName; switch (surfList[i]) { case StructureEnum::CORTEX_LEFT: curSphere = curLeftSphere; newSphere = newLeftSphere; curAreas = curLeftAreas; newAreas = newLeftAreas; structName = "left"; break; case StructureEnum::CORTEX_RIGHT: curSphere = curRightSphere; newSphere = newRightSphere; curAreas = curRightAreas; newAreas = newRightAreas; structName = "right"; break; case StructureEnum::CEREBELLUM: curSphere = curCerebSphere; newSphere = newCerebSphere; curAreas = curCerebAreas; newAreas = newCerebAreas; structName = "cerebellum"; break; default: return make_pair(true, AString("unsupported surface structure: " + StructureEnum::toGuiName(surfList[i]))); break; } if (curSphere != NULL)//resampling { if (newSphere == NULL) return make_pair(true, AString("missing " + structName + " new sphere")); if (curSphere->getNumberOfNodes() != inModels.getSurfaceNumberOfNodes(surfList[i])) return make_pair(true, AString(structName + " current sphere doesn't match input cifti")); if (newSphere->getNumberOfNodes() != outModels.getSurfaceNumberOfNodes(surfList[i])) return make_pair(true, AString(structName + " new sphere doesn't match input cifti")); switch (mySurfMethod) { case SurfaceResamplingMethodEnum::ADAP_BARY_AREA: if (curAreas == NULL || newAreas == NULL) return make_pair(true, AString(structName + " area data is missing")); if (curAreas->getNumberOfNodes() != curSphere->getNumberOfNodes()) return make_pair(true, AString(structName + " current area data has the wrong number of vertices")); if (newAreas->getNumberOfNodes() != newSphere->getNumberOfNodes()) return make_pair(true, AString(structName + " new area data has the wrong number of vertices")); break; default: break; } } else {//copying if (inModels.getSurfaceNumberOfNodes(surfList[i]) != outModels.getSurfaceNumberOfNodes(surfList[i])) return make_pair(true, AString(structName + " structure requires resampling spheres, does not match template")); } } for (int i = 0; i < (int)volList.size(); ++i) { if (!inModels.hasVolumeData(volList[i])) { return make_pair(true, AString(StructureEnum::toGuiName(volList[i]) + " volume model missing from input cifti")); } } return make_pair(false, AString()); } namespace {//so that we don't need these in the header file struct ResampleCache {//a place to stuff anything that can be precomputed or reused for applying to the same structure in multiple maps SurfaceResamplingHelper surfResamp; VolumePaddingHelper volPadding; const SurfaceFile* curSphere, *newSphere; MetricFile tempMetric1, tempMetric2, surfDilateRoi; LabelFile tempLabel1, tempLabel2; CaretPointer tempVol1, tempVol2, tempVol3, volDilateRoi; vector inSurfMap, outSurfMap; vector inVolMap, outVolMap; vector floatScratch1, floatScratch2; vector intScratch1, intScratch2; vector inOffset; int64_t refDims[3], refOffset[3]; vector > refSform; bool copyMode; }; void setupRowResampling(map& surfCache, map& volCache, const CiftiFile* myCiftiIn, CiftiFile* myCiftiOut, const SurfaceResamplingMethodEnum::Enum& mySurfMethod, const float& voldilatemm, const SurfaceFile* curLeftSphere, const SurfaceFile* newLeftSphere, const MetricFile* curLeftAreas, const MetricFile* newLeftAreas, const SurfaceFile* curRightSphere, const SurfaceFile* newRightSphere, const MetricFile* curRightAreas, const MetricFile* newRightAreas, const SurfaceFile* curCerebSphere, const SurfaceFile* newCerebSphere, const MetricFile* curCerebAreas, const MetricFile* newCerebAreas) { const CiftiXML& myInputXML = myCiftiIn->getCiftiXML(), &myOutXML = myCiftiOut->getCiftiXML(); bool labelMode = (myInputXML.getMappingType(CiftiXML::ALONG_COLUMN) == CiftiMappingType::LABELS); const CiftiBrainModelsMap& inModels = myInputXML.getBrainModelsMap(CiftiXML::ALONG_ROW), &outModels = myOutXML.getBrainModelsMap(CiftiXML::ALONG_ROW); vector surfList = outModels.getSurfaceStructureList(), volList = outModels.getVolumeStructureList(); int numSurfStructs = (int)surfList.size(), numVolStructs = (int)volList.size(); for (int i = 0; i < numSurfStructs; ++i)//initialize reusables { const SurfaceFile* curSphere = NULL, *newSphere = NULL; const MetricFile* curAreas = NULL, *newAreas = NULL; switch (surfList[i]) { case StructureEnum::CORTEX_LEFT: curSphere = curLeftSphere; newSphere = newLeftSphere; curAreas = curLeftAreas; newAreas = newLeftAreas; break; case StructureEnum::CORTEX_RIGHT: curSphere = curRightSphere; newSphere = newRightSphere; curAreas = curRightAreas; newAreas = newRightAreas; break; case StructureEnum::CEREBELLUM: curSphere = curCerebSphere; newSphere = newCerebSphere; curAreas = curCerebAreas; newAreas = newCerebAreas; break; default: throw AlgorithmException("unsupported surface structure: " + StructureEnum::toGuiName(surfList[i])); break; } ResampleCache& myCache = surfCache[surfList[i]]; myCache.inSurfMap = inModels.getSurfaceMap(surfList[i]); myCache.outSurfMap = outModels.getSurfaceMap(surfList[i]); if (curSphere == NULL) { myCache.copyMode = true; myCache.floatScratch1.resize(inModels.getSurfaceNumberOfNodes(surfList[i]), 0.0f); continue; } myCache.copyMode = false; myCache.curSphere = curSphere; myCache.newSphere = newSphere; const float* curAreasPtr = NULL, *newAreasPtr = NULL; if (curAreas != NULL && newAreas != NULL) { curAreasPtr = curAreas->getValuePointerForColumn(0); newAreasPtr = newAreas->getValuePointerForColumn(0); } vector tempRoi(curSphere->getNumberOfNodes(), 0.0f); for (int j = 0; j < (int)myCache.inSurfMap.size(); ++j) { tempRoi[myCache.inSurfMap[j].m_surfaceNode] = 1.0f; } myCache.surfResamp = SurfaceResamplingHelper(mySurfMethod, curSphere, newSphere, curAreasPtr, newAreasPtr, tempRoi.data());//resampling is already a helper, so use it as such tempRoi.resize(newSphere->getNumberOfNodes()); myCache.surfResamp.getResampleValidROI(tempRoi.data()); myCache.surfDilateRoi.setNumberOfNodesAndColumns(newSphere->getNumberOfNodes(), 1); for (int j = 0; j < (int)tempRoi.size(); ++j) { myCache.surfDilateRoi.setValue(j, 0, (tempRoi[j] > 0.0f ? 0.0f : 1.0f)); } if (labelMode) { myCache.intScratch1.resize(curSphere->getNumberOfNodes(), 0); myCache.intScratch2.resize(newSphere->getNumberOfNodes(), 0); myCache.tempLabel1.setNumberOfNodesAndColumns(newSphere->getNumberOfNodes(), 1); } else { myCache.floatScratch1.resize(curSphere->getNumberOfNodes(), 0.0f); myCache.floatScratch2.resize(newSphere->getNumberOfNodes(), 0.0f); myCache.tempMetric1.setNumberOfNodesAndColumns(newSphere->getNumberOfNodes(), 1); } } for (int i = 0; i < numVolStructs; ++i) { ResampleCache& myCache = volCache[volList[i]]; myCache.inVolMap = inModels.getVolumeStructureMap(volList[i]); myCache.outVolMap = outModels.getVolumeStructureMap(volList[i]); vector > sform; vector inDims(3); myCache.inOffset.resize(3); myCache.floatScratch1.resize(inDims[0] * inDims[1] * inDims[2]); AlgorithmCiftiSeparate::getCroppedVolSpace(myCiftiIn, CiftiXML::ALONG_ROW, volList[i], inDims.data(), sform, myCache.inOffset.data()); AlgorithmCiftiSeparate::getCroppedVolSpace(myCiftiOut, CiftiXML::ALONG_ROW, volList[i], myCache.refDims, myCache.refSform, myCache.refOffset); if (labelMode) { myCache.tempVol1.grabNew(new VolumeFile(inDims, sform, 1, SubvolumeAttributes::LABEL)); } else { myCache.tempVol1.grabNew(new VolumeFile(inDims, sform)); myCache.tempVol1->setValueAllVoxels(0.0f); } myCache.tempVol2.grabNew(new VolumeFile(inDims, sform));//temporarily use to make the dilation roi, if needed if (voldilatemm > 0.0f) { myCache.volPadding = VolumePaddingHelper::padMM(myCache.tempVol1, voldilatemm); myCache.volDilateRoi.grabNew(new VolumeFile()); myCache.tempVol3.grabNew(new VolumeFile()); myCache.tempVol2->setValueAllVoxels(1.0f); for (int j = 0; j < (int)myCache.inVolMap.size(); ++j) { myCache.tempVol2->setValue(0.0f, myCache.inVolMap[j].m_ijk[0] - myCache.inOffset[0], myCache.inVolMap[j].m_ijk[1] - myCache.inOffset[1], myCache.inVolMap[j].m_ijk[2] - myCache.inOffset[2]); } myCache.volPadding.doPadding(myCache.tempVol2, myCache.volDilateRoi, 1.0f); } } } void processRowSurface(ResampleCache& myCache, const vector& inRow, vector& outRow, const CiftiXML& myInputXML, const float& surfdilatemm, const bool& surfLargest, const int& unassignedLabelKey, const int64_t& row, const AlgorithmMetricDilate::Method& surfDilateMethod, const float& surfDilateExponent) { bool labelMode = (myInputXML.getMappingType(CiftiXML::ALONG_COLUMN) == CiftiMappingType::LABELS); int inMapSize = (int)myCache.inSurfMap.size(), outMapSize = (int)myCache.outSurfMap.size(); if (myCache.copyMode)//copy { for (int j = 0; j < inMapSize; ++j) { myCache.floatScratch1[myCache.inSurfMap[j].m_surfaceNode] = inRow[myCache.inSurfMap[j].m_ciftiIndex]; } for (int j = 0; j < outMapSize; ++j) { outRow[myCache.outSurfMap[j].m_ciftiIndex] = myCache.floatScratch1[myCache.outSurfMap[j].m_surfaceNode]; } } else { if (labelMode) { const CiftiLabelsMap& myLabelMap = myInputXML.getLabelsMap(CiftiXML::ALONG_COLUMN); for (int j = 0; j < inMapSize; ++j) { myCache.intScratch1[myCache.inSurfMap[j].m_surfaceNode] = (int)floor(inRow[myCache.inSurfMap[j].m_ciftiIndex] + 0.5f); } if (surfLargest) { myCache.surfResamp.resampleLargest(myCache.intScratch1.data(), myCache.intScratch2.data(), unassignedLabelKey); } else { myCache.surfResamp.resamplePopular(myCache.intScratch1.data(), myCache.intScratch2.data(), unassignedLabelKey); } *(myCache.tempLabel1.getLabelTable()) = *(myLabelMap.getMapLabelTable(row)); myCache.tempLabel1.setLabelKeysForColumn(0, myCache.intScratch2.data()); LabelFile* toUse = &(myCache.tempLabel1); if (surfdilatemm > 0.0f) { AlgorithmLabelDilate(NULL, toUse, myCache.newSphere, surfdilatemm, &(myCache.tempLabel2), &(myCache.surfDilateRoi), 0); toUse = &(myCache.tempLabel2); } const int32_t* outData = toUse->getLabelKeyPointerForColumn(0); for (int j = 0; j < outMapSize; ++j) { outRow[myCache.outSurfMap[j].m_ciftiIndex] = outData[myCache.outSurfMap[j].m_surfaceNode]; } } else { for (int j = 0; j < inMapSize; ++j) { myCache.floatScratch1[myCache.inSurfMap[j].m_surfaceNode] = inRow[myCache.inSurfMap[j].m_ciftiIndex]; } if (surfLargest) { myCache.surfResamp.resampleLargest(myCache.floatScratch1.data(), myCache.floatScratch2.data()); } else { myCache.surfResamp.resampleNormal(myCache.floatScratch1.data(), myCache.floatScratch2.data()); } myCache.tempMetric1.setValuesForColumn(0, myCache.floatScratch2.data()); MetricFile* toUse = &(myCache.tempMetric1); if (surfdilatemm > 0.0f) { AlgorithmMetricDilate(NULL, toUse, myCache.newSphere, surfdilatemm, &(myCache.tempMetric2), &(myCache.surfDilateRoi), NULL, 0, surfDilateMethod, surfDilateExponent); toUse = &(myCache.tempMetric2); } const float* outData = toUse->getValuePointerForColumn(0); for (int j = 0; j < outMapSize; ++j) { outRow[myCache.outSurfMap[j].m_ciftiIndex] = outData[myCache.outSurfMap[j].m_surfaceNode]; } } } } } AlgorithmCiftiResample::AlgorithmCiftiResample(ProgressObject* myProgObj, const CiftiFile* myCiftiIn, const int& direction, const CiftiFile* myTemplate, const int& templateDir, const SurfaceResamplingMethodEnum::Enum& mySurfMethod, const VolumeFile::InterpType& myVolMethod, CiftiFile* myCiftiOut, const bool& surfLargest, const float& voldilatemm, const float& surfdilatemm, const VolumeFile* warpfield, const SurfaceFile* curLeftSphere, const SurfaceFile* newLeftSphere, const MetricFile* curLeftAreas, const MetricFile* newLeftAreas, const SurfaceFile* curRightSphere, const SurfaceFile* newRightSphere, const MetricFile* curRightAreas, const MetricFile* newRightAreas, const SurfaceFile* curCerebSphere, const SurfaceFile* newCerebSphere, const MetricFile* curCerebAreas, const MetricFile* newCerebAreas, const AlgorithmVolumeDilate::Method& volDilateMethod, const float& volDilateExponent, const AlgorithmMetricDilate::Method& surfDilateMethod, const float& surfDilateExponent) : AbstractAlgorithm(myProgObj) { LevelProgress myProgress(myProgObj); pair myError = checkForErrors(myCiftiIn, direction, myTemplate, templateDir, mySurfMethod, curLeftSphere, newLeftSphere, curLeftAreas, newLeftAreas, curRightSphere, newRightSphere, curRightAreas, newRightAreas, curCerebSphere, newCerebSphere, curCerebAreas, newCerebAreas); if (myError.first) throw AlgorithmException(myError.second); const CiftiXML& myInputXML = myCiftiIn->getCiftiXML(); CiftiXML myOutXML = myInputXML; myOutXML.setMap(direction, *(myTemplate->getCiftiXML().getMap(templateDir))); bool labelMode = (myInputXML.getMappingType(CiftiXML::ALONG_COLUMN) == CiftiMappingType::LABELS); const CiftiBrainModelsMap& outModels = myOutXML.getBrainModelsMap(direction); vector surfList = outModels.getSurfaceStructureList(), volList = outModels.getVolumeStructureList(); myCiftiOut->setCiftiXML(myOutXML); if (direction == CiftiXML::ALONG_COLUMN) { for (int i = 0; i < (int)surfList.size(); ++i)//and now, resampling { const SurfaceFile* curSphere = NULL, *newSphere = NULL; const MetricFile* curAreas = NULL, *newAreas = NULL; switch (surfList[i]) { case StructureEnum::CORTEX_LEFT: curSphere = curLeftSphere; newSphere = newLeftSphere; curAreas = curLeftAreas; newAreas = newLeftAreas; break; case StructureEnum::CORTEX_RIGHT: curSphere = curRightSphere; newSphere = newRightSphere; curAreas = curRightAreas; newAreas = newRightAreas; break; case StructureEnum::CEREBELLUM: curSphere = curCerebSphere; newSphere = newCerebSphere; curAreas = curCerebAreas; newAreas = newCerebAreas; break; default: throw AlgorithmException("unsupported surface structure: " + StructureEnum::toGuiName(surfList[i])); break; } processSurfaceComponent(myCiftiIn, direction, surfList[i], mySurfMethod, myCiftiOut, surfLargest, surfdilatemm, curSphere, newSphere, curAreas, newAreas, surfDilateMethod, surfDilateExponent); } for (int i = 0; i < (int)volList.size(); ++i) { processVolumeWarpfield(myCiftiIn, direction, volList[i], myVolMethod, myCiftiOut, voldilatemm, warpfield, volDilateMethod, volDilateExponent); } } else {//avoid cifti separate/replace with ALONG_ROW vector surfList = outModels.getSurfaceStructureList(), volList = outModels.getVolumeStructureList(); int numSurfStructs = (int)surfList.size(), numVolStructs = (int)volList.size(); vector unassignedLabelKey; if (myInputXML.getMappingType(CiftiXML::ALONG_COLUMN) == CiftiMappingType::LABELS) { const CiftiLabelsMap& myLabelMap = myInputXML.getLabelsMap(CiftiXML::ALONG_COLUMN); unassignedLabelKey.resize(myLabelMap.getLength()); for (int i = 0; i < myLabelMap.getLength(); ++i) { unassignedLabelKey[i] = myLabelMap.getMapLabelTable(i)->getUnassignedLabelKey(); } } map surfCache, volCache;//could make them different types, but whatever - two variables in case of structure overlap in surface and volume, as some members may get used by both setupRowResampling(surfCache, volCache, myCiftiIn, myCiftiOut, mySurfMethod, voldilatemm, curLeftSphere, newLeftSphere, curLeftAreas, newLeftAreas, curRightSphere, newRightSphere, curRightAreas, newRightAreas, curCerebSphere, newCerebSphere, curCerebAreas, newCerebAreas); int64_t numRows = myInputXML.getDimensionLength(CiftiXML::ALONG_COLUMN); vector inRow(myInputXML.getDimensionLength(CiftiXML::ALONG_ROW)), outRow(myOutXML.getDimensionLength(CiftiXML::ALONG_ROW)); for (int64_t row = 0; row < numRows; ++row) { myCiftiIn->getRow(inRow.data(), row); for (int i = 0; i < numSurfStructs; ++i) { map::iterator iter = surfCache.find(surfList[i]); CaretAssert(iter != surfCache.end()); processRowSurface(iter->second, inRow, outRow, myInputXML, surfdilatemm, surfLargest, unassignedLabelKey[row], row, surfDilateMethod, surfDilateExponent); } for (int i = 0; i < numVolStructs; ++i) { map::iterator iter = volCache.find(volList[i]); CaretAssert(iter != volCache.end()); ResampleCache& myCache = iter->second; if (labelMode)//gets initialized to 0 when not using labels { myCache.tempVol1->setValueAllVoxels(unassignedLabelKey[row]); } int inMapSize = (int)myCache.inVolMap.size(), outMapSize = (int)myCache.outVolMap.size(); for (int j = 0; j < inMapSize; ++j) { myCache.tempVol1->setValue(inRow[myCache.inVolMap[j].m_ciftiIndex], myCache.inVolMap[j].m_ijk[0] - myCache.inOffset[0], myCache.inVolMap[j].m_ijk[1] - myCache.inOffset[1], myCache.inVolMap[j].m_ijk[2] - myCache.inOffset[2]); } const VolumeFile* toResample = myCache.tempVol1; if (voldilatemm > 0.0f) { myCache.volPadding.doPadding(myCache.tempVol1, myCache.tempVol2); AlgorithmVolumeDilate(NULL, myCache.tempVol2, voldilatemm, volDilateMethod, myCache.tempVol3, myCache.volDilateRoi, NULL, -1, volDilateExponent); toResample = myCache.tempVol3; } AlgorithmVolumeWarpfieldResample(NULL, toResample, warpfield, myCache.refDims, myCache.refSform, myVolMethod, myCache.tempVol2); for (int j = 0; j < outMapSize; ++j) { outRow[myCache.outVolMap[j].m_ciftiIndex] = myCache.tempVol2->getValue(myCache.outVolMap[j].m_ijk[0] - myCache.refOffset[0], myCache.outVolMap[j].m_ijk[1] - myCache.refOffset[1], myCache.outVolMap[j].m_ijk[2] - myCache.refOffset[2]); } } myCiftiOut->setRow(outRow.data(), row); } } } AlgorithmCiftiResample::AlgorithmCiftiResample(ProgressObject* myProgObj, const CiftiFile* myCiftiIn, const int& direction, const CiftiFile* myTemplate, const int& templateDir, const SurfaceResamplingMethodEnum::Enum& mySurfMethod, const VolumeFile::InterpType& myVolMethod, CiftiFile* myCiftiOut, const bool& surfLargest, const float& voldilatemm, const float& surfdilatemm, const FloatMatrix& affine, const SurfaceFile* curLeftSphere, const SurfaceFile* newLeftSphere, const MetricFile* curLeftAreas, const MetricFile* newLeftAreas, const SurfaceFile* curRightSphere, const SurfaceFile* newRightSphere, const MetricFile* curRightAreas, const MetricFile* newRightAreas, const SurfaceFile* curCerebSphere, const SurfaceFile* newCerebSphere, const MetricFile* curCerebAreas, const MetricFile* newCerebAreas, const AlgorithmVolumeDilate::Method& volDilateMethod, const float& volDilateExponent, const AlgorithmMetricDilate::Method& surfDilateMethod, const float& surfDilateExponent) : AbstractAlgorithm(myProgObj) { LevelProgress myProgress(myProgObj); pair myError = checkForErrors(myCiftiIn, direction, myTemplate, templateDir, mySurfMethod, curLeftSphere, newLeftSphere, curLeftAreas, newLeftAreas, curRightSphere, newRightSphere, curRightAreas, newRightAreas, curCerebSphere, newCerebSphere, curCerebAreas, newCerebAreas); if (myError.first) throw AlgorithmException(myError.second); const CiftiXML& myInputXML = myCiftiIn->getCiftiXML(); CiftiXML myOutXML = myInputXML; myOutXML.setMap(direction, *(myTemplate->getCiftiXML().getMap(templateDir))); const CiftiBrainModelsMap& outModels = myOutXML.getBrainModelsMap(direction); vector surfList = outModels.getSurfaceStructureList(), volList = outModels.getVolumeStructureList(); myCiftiOut->setCiftiXML(myOutXML); if (direction == CiftiXML::ALONG_COLUMN) { for (int i = 0; i < (int)surfList.size(); ++i)//and now, resampling { const SurfaceFile* curSphere = NULL, *newSphere = NULL; const MetricFile* curAreas = NULL, *newAreas = NULL; switch (surfList[i]) { case StructureEnum::CORTEX_LEFT: curSphere = curLeftSphere; newSphere = newLeftSphere; curAreas = curLeftAreas; newAreas = newLeftAreas; break; case StructureEnum::CORTEX_RIGHT: curSphere = curRightSphere; newSphere = newRightSphere; curAreas = curRightAreas; newAreas = newRightAreas; break; case StructureEnum::CEREBELLUM: curSphere = curCerebSphere; newSphere = newCerebSphere; curAreas = curCerebAreas; newAreas = newCerebAreas; break; default: throw AlgorithmException("unsupported surface structure: " + StructureEnum::toGuiName(surfList[i])); break; } processSurfaceComponent(myCiftiIn, direction, surfList[i], mySurfMethod, myCiftiOut, surfLargest, surfdilatemm, curSphere, newSphere, curAreas, newAreas, surfDilateMethod, surfDilateExponent); } for (int i = 0; i < (int)volList.size(); ++i) { processVolumeAffine(myCiftiIn, direction, volList[i], myVolMethod, myCiftiOut, voldilatemm, affine, volDilateMethod, volDilateExponent); } } else {//avoid cifti separate/replace with ALONG_ROW vector surfList = outModels.getSurfaceStructureList(), volList = outModels.getVolumeStructureList(); int numSurfStructs = (int)surfList.size(), numVolStructs = (int)volList.size(); vector unassignedLabelKey; if (myInputXML.getMappingType(CiftiXML::ALONG_COLUMN) == CiftiMappingType::LABELS) { const CiftiLabelsMap& myLabelMap = myInputXML.getLabelsMap(CiftiXML::ALONG_COLUMN); unassignedLabelKey.resize(myLabelMap.getLength()); for (int i = 0; i < myLabelMap.getLength(); ++i) { unassignedLabelKey[i] = myLabelMap.getMapLabelTable(i)->getUnassignedLabelKey(); } } map surfCache, volCache;//could make them different types, but whatever - two variables in case of structure overlap in surface and volume, as some members may get used by both setupRowResampling(surfCache, volCache, myCiftiIn, myCiftiOut, mySurfMethod, voldilatemm, curLeftSphere, newLeftSphere, curLeftAreas, newLeftAreas, curRightSphere, newRightSphere, curRightAreas, newRightAreas, curCerebSphere, newCerebSphere, curCerebAreas, newCerebAreas); int64_t numRows = myInputXML.getDimensionLength(CiftiXML::ALONG_COLUMN); vector inRow(myInputXML.getDimensionLength(CiftiXML::ALONG_ROW)), outRow(myOutXML.getDimensionLength(CiftiXML::ALONG_ROW)); for (int64_t row = 0; row < numRows; ++row) { myCiftiIn->getRow(inRow.data(), row); for (int i = 0; i < numSurfStructs; ++i) { map::iterator iter = surfCache.find(surfList[i]); CaretAssert(iter != surfCache.end()); processRowSurface(iter->second, inRow, outRow, myInputXML, surfdilatemm, surfLargest, unassignedLabelKey[row], row, surfDilateMethod, surfDilateExponent); } for (int i = 0; i < numVolStructs; ++i) { map::iterator iter = volCache.find(volList[i]); CaretAssert(iter != volCache.end()); ResampleCache& myCache = iter->second; int inMapSize = (int)myCache.inVolMap.size(), outMapSize = (int)myCache.outVolMap.size(); for (int j = 0; j < inMapSize; ++j) { myCache.tempVol1->setValue(inRow[myCache.inVolMap[j].m_ciftiIndex], myCache.inVolMap[j].m_ijk[0] - myCache.inOffset[0], myCache.inVolMap[j].m_ijk[1] - myCache.inOffset[1], myCache.inVolMap[j].m_ijk[2] - myCache.inOffset[2]); } const VolumeFile* toResample = myCache.tempVol1; if (voldilatemm > 0.0f) { myCache.volPadding.doPadding(myCache.tempVol1, myCache.tempVol2); AlgorithmVolumeDilate(NULL, myCache.tempVol2, voldilatemm, volDilateMethod, myCache.tempVol3, myCache.volDilateRoi, NULL, -1, volDilateExponent); toResample = myCache.tempVol3; } AlgorithmVolumeAffineResample(NULL, toResample, affine, myCache.refDims, myCache.refSform, myVolMethod, myCache.tempVol2); for (int j = 0; j < outMapSize; ++j) { outRow[myCache.outVolMap[j].m_ciftiIndex] = myCache.tempVol2->getValue(myCache.outVolMap[j].m_ijk[0] - myCache.refOffset[0], myCache.outVolMap[j].m_ijk[1] - myCache.refOffset[1], myCache.outVolMap[j].m_ijk[2] - myCache.refOffset[2]); } } myCiftiOut->setRow(outRow.data(), row); } } } void AlgorithmCiftiResample::processSurfaceComponent(const CiftiFile* myCiftiIn, const int& direction, const StructureEnum::Enum& myStruct, const SurfaceResamplingMethodEnum::Enum& mySurfMethod, CiftiFile* myCiftiOut, const bool& surfLargest, const float& surfdilatemm, const SurfaceFile* curSphere, const SurfaceFile* newSphere, const MetricFile* curAreas, const MetricFile* newAreas, const AlgorithmMetricDilate::Method& surfDilateMethod, const float& surfDilateExponent) { const CiftiXML& myInputXML = myCiftiIn->getCiftiXML(); if (myInputXML.getMappingType(1 - direction) == CiftiMappingType::LABELS) { LabelFile origLabel; MetricFile origRoi, resampleROI; AlgorithmCiftiSeparate(NULL, myCiftiIn, direction, myStruct, &origLabel, &origRoi); LabelFile newLabel, newDilate, *newUse = &newLabel; if (curSphere != NULL) { AlgorithmLabelResample(NULL, &origLabel, curSphere, newSphere, mySurfMethod, &newLabel, curAreas, newAreas, &origRoi, &resampleROI, surfLargest); origLabel.clear();//delete the data we no longer need to keep memory use down if (surfdilatemm > 0.0f) { MetricFile invertResampleROI; invertResampleROI.setNumberOfNodesAndColumns(resampleROI.getNumberOfNodes(), 1); for (int j = 0; j < resampleROI.getNumberOfNodes(); ++j) { float tempf = (resampleROI.getValue(j, 0) > 0.0f) ? 0.0f : 1.0f;//make an inverse ROI invertResampleROI.setValue(j, 0, tempf); } AlgorithmLabelDilate(NULL, &newLabel, newSphere, surfdilatemm, &newDilate, &invertResampleROI); newLabel.clear();//ditto newUse = &newDilate; } } else { newUse = &origLabel; } AlgorithmCiftiReplaceStructure(NULL, myCiftiOut, direction, myStruct, newUse); } else { MetricFile origMetric, origROI; AlgorithmCiftiSeparate(NULL, myCiftiIn, direction, myStruct, &origMetric, &origROI); MetricFile newMetric, newDilate, resampleROI, *newUse = &newMetric; if (curSphere != NULL) { AlgorithmMetricResample(NULL, &origMetric, curSphere, newSphere, mySurfMethod, &newMetric, curAreas, newAreas, &origROI, &resampleROI, surfLargest); origMetric.clear();//ditto if (surfdilatemm > 0.0f) { MetricFile invertResampleROI; invertResampleROI.setNumberOfNodesAndColumns(resampleROI.getNumberOfNodes(), 1); for (int j = 0; j < resampleROI.getNumberOfNodes(); ++j) { float tempf = (resampleROI.getValue(j, 0) > 0.0f) ? 0.0f : 1.0f;//make an inverse ROI invertResampleROI.setValue(j, 0, tempf); } AlgorithmMetricDilate(NULL, &newMetric, newSphere, surfdilatemm, &newDilate, &invertResampleROI, NULL, -1, surfDilateMethod, surfDilateExponent);//we could get the data roi from the template cifti and use it here newMetric.clear(); newUse = &newDilate; } } else { newUse = &origMetric; } AlgorithmCiftiReplaceStructure(NULL, myCiftiOut, direction, myStruct, newUse); } } void AlgorithmCiftiResample::processVolumeWarpfield(const CiftiFile* myCiftiIn, const int& direction, const StructureEnum::Enum& myStruct, const VolumeFile::InterpType& myVolMethod, CiftiFile* myCiftiOut, const float& voldilatemm, const VolumeFile* warpfield, const AlgorithmVolumeDilate::Method& volDilateMethod, const float& volDilateExponent) { VolumeFile origData, origROI, origDilate, *origProcess; origProcess = &origData; int64_t offset[3]; AlgorithmCiftiSeparate(NULL, myCiftiIn, direction, myStruct, &origData, offset, &origROI, true); if (voldilatemm > 0.0f) { VolumeFile invertROI; invertROI.reinitialize(origROI.getOriginalDimensions(), origROI.getSform()); vector dims; origROI.getDimensions(dims); int64_t frameSize = dims[0] * dims[1] * dims[2]; const float* roiFrame = origROI.getFrame(); vector invertFrame(frameSize); for (int64_t j = 0; j < frameSize; ++j) { invertFrame[j] = (roiFrame[j] > 0.0f) ? 0.0f : 1.0f; } invertROI.setFrame(invertFrame.data()); VolumePaddingHelper mypadding = VolumePaddingHelper::padMM(&origData, voldilatemm); VolumeFile origPad, invertROIPad; mypadding.doPadding(&origData, &origPad); origData.clear();//delete data we no longer need to keep memory use down mypadding.doPadding(&invertROI, &invertROIPad, 1.0f);//pad with ones since this is an inverted ROI AlgorithmVolumeDilate(NULL, &origPad, voldilatemm, volDilateMethod, &origDilate, &invertROIPad, NULL, -1, volDilateExponent); origPad.clear();//ditto origProcess = &origDilate; } VolumeFile newVolume; int64_t refdims[3], refoffset[3]; vector > refsform; AlgorithmCiftiSeparate::getCroppedVolSpace(myCiftiOut, direction, myStruct, refdims, refsform, refoffset); AlgorithmVolumeWarpfieldResample(NULL, origProcess, warpfield, refdims, refsform, myVolMethod, &newVolume); origProcess->clear();//ditto AlgorithmCiftiReplaceStructure(NULL, myCiftiOut, direction, myStruct, &newVolume, true); } void AlgorithmCiftiResample::processVolumeAffine(const CiftiFile* myCiftiIn, const int& direction, const StructureEnum::Enum& myStruct, const VolumeFile::InterpType& myVolMethod, CiftiFile* myCiftiOut, const float& voldilatemm, const FloatMatrix& affine, const AlgorithmVolumeDilate::Method& volDilateMethod, const float& volDilateExponent) { VolumeFile origData, origROI, origDilate, *origProcess; origProcess = &origData; int64_t offset[3]; AlgorithmCiftiSeparate(NULL, myCiftiIn, direction, myStruct, &origData, offset, &origROI, true); if (voldilatemm > 0.0f) { VolumeFile invertROI; invertROI.reinitialize(origROI.getOriginalDimensions(), origROI.getSform()); vector dims; origROI.getDimensions(dims); int64_t frameSize = dims[0] * dims[1] * dims[2]; const float* roiFrame = origROI.getFrame(); vector invertFrame(frameSize); for (int64_t j = 0; j < frameSize; ++j) { invertFrame[j] = (roiFrame[j] > 0.0f) ? 0.0f : 1.0f; } invertROI.setFrame(invertFrame.data()); VolumePaddingHelper mypadding = VolumePaddingHelper::padMM(&origData, voldilatemm); VolumeFile origPad, invertROIPad; mypadding.doPadding(&origData, &origPad); origData.clear();//delete data we no longer need to keep memory use down mypadding.doPadding(&invertROI, &invertROIPad, 1.0f);//pad with ones since this is an inverted ROI AlgorithmVolumeDilate(NULL, &origPad, voldilatemm, volDilateMethod, &origDilate, &invertROIPad, NULL, -1, volDilateExponent); origPad.clear();//ditto origProcess = &origDilate; } VolumeFile newVolume; int64_t refdims[3], refoffset[3]; vector > refsform; AlgorithmCiftiSeparate::getCroppedVolSpace(myCiftiOut, direction, myStruct, refdims, refsform, refoffset); AlgorithmVolumeAffineResample(NULL, origProcess, affine, refdims, refsform, myVolMethod, &newVolume); origProcess->clear();//ditto AlgorithmCiftiReplaceStructure(NULL, myCiftiOut, direction, myStruct, &newVolume, true); } float AlgorithmCiftiResample::getAlgorithmInternalWeight() { return 1.0f;//override this if needed, if the progress bar isn't smooth } float AlgorithmCiftiResample::getSubAlgorithmWeight() { //return AlgorithmInsertNameHere::getAlgorithmWeight();//if you use a subalgorithm return 0.0f; } connectome-workbench-1.2.3+git41-gc4c6c90/src/Algorithms/AlgorithmCiftiResample.h000066400000000000000000000150701300200146000274270ustar00rootroot00000000000000#ifndef __ALGORITHM_CIFTI_RESAMPLE_H__ #define __ALGORITHM_CIFTI_RESAMPLE_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 "AbstractAlgorithm.h" #include "AlgorithmMetricDilate.h" //for dilate method enums #include "AlgorithmVolumeDilate.h" #include "FloatMatrix.h" #include "StructureEnum.h" #include "SurfaceResamplingMethodEnum.h" #include "VolumeFile.h" #include //for pair namespace caret { class AlgorithmCiftiResample : public AbstractAlgorithm { AlgorithmCiftiResample(); void processSurfaceComponent(const CiftiFile* myCiftiIn, const int& direction, const StructureEnum::Enum& myStruct, const SurfaceResamplingMethodEnum::Enum& mySurfMethod, CiftiFile* myCiftiOut, const bool& surfLargest, const float& surfdilatemm, const SurfaceFile* curSphere, const SurfaceFile* newSphere, const MetricFile* curAreas, const MetricFile* newAreas, const AlgorithmMetricDilate::Method& surfDilateMethod, const float& surfDilateExponent); void processVolumeWarpfield(const CiftiFile* myCiftiIn, const int& direction, const StructureEnum::Enum& myStruct, const VolumeFile::InterpType& myVolMethod, CiftiFile* myCiftiOut, const float& voldilatemm, const VolumeFile* warpfield, const AlgorithmVolumeDilate::Method& volDilateMethod, const float& volDilateExponent); void processVolumeAffine(const CiftiFile* myCiftiIn, const int& direction, const StructureEnum::Enum& myStruct, const VolumeFile::InterpType& myVolMethod, CiftiFile* myCiftiOut, const float& voldilatemm, const FloatMatrix& affine, const AlgorithmVolumeDilate::Method& volDilateMethod, const float& volDilateExponent); protected: static float getSubAlgorithmWeight(); static float getAlgorithmInternalWeight(); public: //so that other code that uses it more than once can pre-check things - returns true for error present! static std::pair checkForErrors(const CiftiFile* myCiftiIn, const int& direction, const CiftiFile* myTemplate, const int& templateDir, const SurfaceResamplingMethodEnum::Enum& mySurfMethod, const SurfaceFile* curLeftSphere, const SurfaceFile* newLeftSphere, const MetricFile* curLeftAreas, const MetricFile* newLeftAreas, const SurfaceFile* curRightSphere, const SurfaceFile* newRightSphere, const MetricFile* curRightAreas, const MetricFile* newRightAreas, const SurfaceFile* curCerebSphere, const SurfaceFile* newCerebSphere, const MetricFile* curCerebAreas, const MetricFile* newCerebAreas); AlgorithmCiftiResample(ProgressObject* myProgObj, const CiftiFile* myCiftiIn, const int& direction, const CiftiFile* myTemplate, const int& templateDir, const SurfaceResamplingMethodEnum::Enum& mySurfMethod, const VolumeFile::InterpType& myVolMethod, CiftiFile* myCiftiOut, const bool& surfLargest, const float& voldilatemm, const float& surfdilatemm, const VolumeFile* warpfield, const SurfaceFile* curLeftSphere, const SurfaceFile* newLeftSphere, const MetricFile* curLeftAreas, const MetricFile* newLeftAreas, const SurfaceFile* curRightSphere, const SurfaceFile* newRightSphere, const MetricFile* curRightAreas, const MetricFile* newRightAreas, const SurfaceFile* curCerebSphere, const SurfaceFile* newCerebSphere, const MetricFile* curCerebAreas, const MetricFile* newCerebAreas, const AlgorithmVolumeDilate::Method& volDilateMethod = AlgorithmVolumeDilate::WEIGHTED, const float& volDilateExponent = 2.0f, const AlgorithmMetricDilate::Method& surfDilateMethod = AlgorithmMetricDilate::WEIGHTED, const float& surfDilateExponent = 2.0f); AlgorithmCiftiResample(ProgressObject* myProgObj, const CiftiFile* myCiftiIn, const int& direction, const CiftiFile* myTemplate, const int& templateDir, const SurfaceResamplingMethodEnum::Enum& mySurfMethod, const VolumeFile::InterpType& myVolMethod, CiftiFile* myCiftiOut, const bool& surfLargest, const float& voldilatemm, const float& surfdilatemm, const FloatMatrix& affine, const SurfaceFile* curLeftSphere, const SurfaceFile* newLeftSphere, const MetricFile* curLeftAreas, const MetricFile* newLeftAreas, const SurfaceFile* curRightSphere, const SurfaceFile* newRightSphere, const MetricFile* curRightAreas, const MetricFile* newRightAreas, const SurfaceFile* curCerebSphere, const SurfaceFile* newCerebSphere, const MetricFile* curCerebAreas, const MetricFile* newCerebAreas, const AlgorithmVolumeDilate::Method& volDilateMethod = AlgorithmVolumeDilate::WEIGHTED, const float& volDilateExponent = 2.0f, const AlgorithmMetricDilate::Method& surfDilateMethod = AlgorithmMetricDilate::WEIGHTED, const float& surfDilateExponent = 2.0f); static OperationParameters* getParameters(); static void useParameters(OperationParameters* myParams, ProgressObject* myProgObj); static AString getCommandSwitch(); static AString getShortDescription(); }; typedef TemplateAutoOperation AutoAlgorithmCiftiResample; } #endif //__ALGORITHM_CIFTI_RESAMPLE_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Algorithms/AlgorithmCiftiRestrictDenseMap.cxx000066400000000000000000000416071300200146000314530ustar00rootroot00000000000000/*LICENSE_START*/ /* * Copyright (C) 2015 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 "AlgorithmCiftiRestrictDenseMap.h" #include "AlgorithmException.h" #include "CaretLogger.h" #include "CiftiFile.h" #include "MetricFile.h" #include "MultiDimIterator.h" #include "VolumeFile.h" #include using namespace caret; using namespace std; AString AlgorithmCiftiRestrictDenseMap::getCommandSwitch() { return "-cifti-restrict-dense-map"; } AString AlgorithmCiftiRestrictDenseMap::getShortDescription() { return "EXCLUDE BRAINORDINATES FROM A CIFTI FILE"; } OperationParameters* AlgorithmCiftiRestrictDenseMap::getParameters() { OperationParameters* ret = new OperationParameters(); ret->addCiftiParameter(1, "cifti-in", "the input cifti"); ret->addStringParameter(2, "direction", "which dimension to change the mapping on (integer, 'ROW', or 'COLUMN')"); ret->addCiftiOutputParameter(3, "cifti-out", "the output cifti"); OptionalParameter* ciftiRoiOpt = ret->createOptionalParameter(4, "-cifti-roi", "cifti file containing combined rois"); ciftiRoiOpt->addCiftiParameter(1, "roi-cifti", "the rois as a cifti file"); OptionalParameter* leftRoiOpt = ret->createOptionalParameter(5, "-left-roi", "vertices to use from left hemisphere"); leftRoiOpt->addMetricParameter(1, "roi-metric", "the left roi as a metric file"); OptionalParameter* rightRoiOpt = ret->createOptionalParameter(6, "-right-roi", "vertices to use from right hemisphere"); rightRoiOpt->addMetricParameter(1, "roi-metric", "the right roi as a metric file"); OptionalParameter* cerebRoiOpt = ret->createOptionalParameter(7, "-cerebellum-roi", "vertices to use from cerebellum"); cerebRoiOpt->addMetricParameter(1, "roi-metric", "the cerebellum roi as a metric file"); OptionalParameter* volRoiOpt = ret->createOptionalParameter(8, "-vol-roi", "voxels to use"); volRoiOpt->addVolumeParameter(1, "roi-vol", "the roi volume file"); ret->setHelpText( AString("Writes a modified version of , where all brainordinates outside the specified roi(s) are removed from the file. ") + CiftiXML::directionFromStringExplanation() + " " + "If -cifti-roi is specified, no other -*-roi option may be specified. " + "If not using -cifti-roi, any -*-roi options not present will discard the relevant structure, if present in the input file." ); return ret; } void AlgorithmCiftiRestrictDenseMap::useParameters(OperationParameters* myParams, ProgressObject* myProgObj) { const CiftiFile* ciftiIn = myParams->getCifti(1); int direction = CiftiXML::directionFromString(myParams->getString(2)); CiftiFile* ciftiOut = myParams->getOutputCifti(3); OptionalParameter* ciftiRoiOpt = myParams->getOptionalParameter(4); OptionalParameter* leftRoiOpt = myParams->getOptionalParameter(5); OptionalParameter* rightRoiOpt = myParams->getOptionalParameter(6); OptionalParameter* cerebRoiOpt = myParams->getOptionalParameter(7); OptionalParameter* volRoiOpt = myParams->getOptionalParameter(8); if (ciftiRoiOpt->m_present) { if (leftRoiOpt->m_present || rightRoiOpt->m_present || cerebRoiOpt->m_present || volRoiOpt->m_present) { throw AlgorithmException("-cifti-roi cannot be specified with any other roi option"); } const CiftiFile* ciftiRoi = ciftiRoiOpt->getCifti(1); AlgorithmCiftiRestrictDenseMap(myProgObj, ciftiIn, direction, ciftiOut, ciftiRoi); } else { if (!(leftRoiOpt->m_present || rightRoiOpt->m_present || cerebRoiOpt->m_present || volRoiOpt->m_present)) { throw AlgorithmException("you must specify at least one roi option"); } const MetricFile* leftRoi = NULL, *rightRoi = NULL, *cerebRoi = NULL; const VolumeFile* volRoi = NULL; if (leftRoiOpt->m_present) leftRoi = leftRoiOpt->getMetric(1); if (rightRoiOpt->m_present) rightRoi = rightRoiOpt->getMetric(1); if (cerebRoiOpt->m_present) cerebRoi = cerebRoiOpt->getMetric(1); if (volRoiOpt->m_present) volRoi = volRoiOpt->getVolume(1); AlgorithmCiftiRestrictDenseMap(myProgObj, ciftiIn, direction, ciftiOut, leftRoi, rightRoi, cerebRoi, volRoi); } } AlgorithmCiftiRestrictDenseMap::AlgorithmCiftiRestrictDenseMap(ProgressObject* myProgObj, const CiftiFile* ciftiIn, const int& direction, CiftiFile* ciftiOut, const CiftiFile* ciftiRoi) : AbstractAlgorithm(myProgObj) { LevelProgress myProgress(myProgObj); CaretAssert(direction >= 0); const CiftiXML& inXML = ciftiIn->getCiftiXML(), &roiXML = ciftiRoi->getCiftiXML(); if (direction >= inXML.getNumberOfDimensions()) { throw AlgorithmException("invalid direction for input file"); } if (inXML.getMappingType(direction) != CiftiMappingType::BRAIN_MODELS) { throw AlgorithmException("input does not have a brain models mapping along specified direction"); } if (roiXML.getMappingType(CiftiXML::ALONG_COLUMN) != CiftiMappingType::BRAIN_MODELS) { throw AlgorithmException("roi does not have a brain models mapping along column"); } const CiftiBrainModelsMap& inMap = inXML.getBrainModelsMap(direction); if (!inMap.approximateMatch(roiXML.getBrainModelsMap(CiftiXML::ALONG_COLUMN))) {//for now, require that the brain models mappings use the same vertices/voxels, but allow for unusual surface structures not available in the metric/volume version throw AlgorithmException("roi cifti brain models mapping doesn't match the input cifti"); }//alternatively, could separate and call the other version if they don't match, but could be more confusing vector roiData(roiXML.getDimensionLength(CiftiXML::ALONG_COLUMN)); ciftiRoi->getColumn(roiData.data(), 0);//we only need 1 column from the roi CiftiBrainModelsMap outMap; if (inMap.hasVolumeData()) { outMap.setVolumeSpace(inMap.getVolumeSpace()); } vector translate; vector modelInfo = inMap.getModelInfo(); for (int i = 0; i < (int)modelInfo.size(); ++i) { switch (modelInfo[i].m_type) { case CiftiBrainModelsMap::SURFACE: { vector surfMap = inMap.getSurfaceMap(modelInfo[i].m_structure); vector usedNodes; for (size_t j = 0; j < surfMap.size(); ++j) { if (roiData[surfMap[j].m_ciftiIndex] > 0.0f) { usedNodes.push_back(surfMap[j].m_surfaceNode); translate.push_back(surfMap[j].m_ciftiIndex); } } if (!usedNodes.empty()) { outMap.addSurfaceModel(inMap.getSurfaceNumberOfNodes(modelInfo[i].m_structure), modelInfo[i].m_structure, usedNodes); } break; } case CiftiBrainModelsMap::VOXELS: { vector volMap = inMap.getVolumeStructureMap(modelInfo[i].m_structure); vector usedVoxels; for (size_t j = 0; j < volMap.size(); ++j) { if (roiData[volMap[j].m_ciftiIndex] > 0.0f) { usedVoxels.push_back(volMap[j].m_ijk[0]); usedVoxels.push_back(volMap[j].m_ijk[1]); usedVoxels.push_back(volMap[j].m_ijk[2]); translate.push_back(volMap[j].m_ciftiIndex); } } if (!usedVoxels.empty()) { outMap.addVolumeModel(modelInfo[i].m_structure, usedVoxels); } break; } } } if (outMap.getLength() == 0) { throw AlgorithmException("output mapping would contain no brainordinates"); } CiftiXML outXML = inXML; outXML.setMap(direction, outMap); ciftiOut->setCiftiXML(outXML); vector outDims = outXML.getDimensions(); vector scratchRow(inXML.getDimensionLength(CiftiXML::ALONG_ROW)); if (direction == CiftiXML::ALONG_ROW) { vector outRow(outMap.getLength()); for (MultiDimIterator iter(vector(outDims.begin() + 1, outDims.end())); !iter.atEnd(); ++iter) { ciftiIn->getRow(scratchRow.data(), *iter); for (size_t i = 0; i < translate.size(); ++i) { outRow[i] = scratchRow[translate[i]]; } ciftiOut->setRow(outRow.data(), *iter); } } else { for (MultiDimIterator iter(vector(outDims.begin() + 1, outDims.end())); !iter.atEnd(); ++iter) { vector indices = *iter; indices[direction - 1] = translate[(*iter)[direction - 1]]; ciftiIn->getRow(scratchRow.data(), indices); ciftiOut->setRow(scratchRow.data(), *iter); } } } AlgorithmCiftiRestrictDenseMap::AlgorithmCiftiRestrictDenseMap(ProgressObject* myProgObj, const CiftiFile* ciftiIn, const int& direction, CiftiFile* ciftiOut, const MetricFile* leftRoi, const MetricFile* rightRoi, const MetricFile* cerebRoi, const VolumeFile* volRoi) : AbstractAlgorithm(myProgObj) { LevelProgress myProgress(myProgObj); CaretAssert(direction >= 0); const CiftiXML& inXML = ciftiIn->getCiftiXML(); if (direction >= inXML.getNumberOfDimensions()) { throw AlgorithmException("invalid direction for input file"); } if (inXML.getMappingType(direction) != CiftiMappingType::BRAIN_MODELS) { throw AlgorithmException("input does not have a brain models mapping along specified direction"); } const CiftiBrainModelsMap& inMap = inXML.getBrainModelsMap(direction); if (leftRoi != NULL) { if (!inMap.hasSurfaceData(StructureEnum::CORTEX_LEFT)) { throw AlgorithmException("left surface roi provided, but input has no left surface data"); } if (leftRoi->getNumberOfNodes() != inMap.getSurfaceNumberOfNodes(StructureEnum::CORTEX_LEFT)) { throw AlgorithmException("left surface roi has different number of vertices than input cifti"); } } if (rightRoi != NULL) { if (!inMap.hasSurfaceData(StructureEnum::CORTEX_RIGHT)) { throw AlgorithmException("right surface roi provided, but input has no left surface data"); } if (rightRoi->getNumberOfNodes() != inMap.getSurfaceNumberOfNodes(StructureEnum::CORTEX_RIGHT)) { throw AlgorithmException("right surface roi has different number of vertices than input cifti"); } } if (cerebRoi != NULL) { if (!inMap.hasSurfaceData(StructureEnum::CEREBELLUM)) { throw AlgorithmException("cerebellum surface roi provided, but input has no left surface data"); } if (cerebRoi->getNumberOfNodes() != inMap.getSurfaceNumberOfNodes(StructureEnum::CEREBELLUM)) { throw AlgorithmException("cerebellum surface roi has different number of vertices than input cifti"); } } if (volRoi != NULL) { if (!inMap.hasVolumeData()) throw AlgorithmException("volume roi provided, but input has no volume data"); if (!inMap.getVolumeSpace().matches(volRoi->getVolumeSpace())) throw AlgorithmException("roi volume has different volume space than input cifti"); } CiftiBrainModelsMap outMap; if (inMap.hasVolumeData() && volRoi != NULL) { outMap.setVolumeSpace(inMap.getVolumeSpace()); } vector translate; vector modelInfo = inMap.getModelInfo(); for (int i = 0; i < (int)modelInfo.size(); ++i) { switch (modelInfo[i].m_type) { case CiftiBrainModelsMap::SURFACE: { const MetricFile* useRoi = NULL; switch (modelInfo[i].m_structure) { case StructureEnum::CORTEX_LEFT: useRoi = leftRoi; break; case StructureEnum::CORTEX_RIGHT: useRoi = rightRoi; break; case StructureEnum::CEREBELLUM: useRoi = cerebRoi; break; default: CaretLogWarning("removing unsupported surface structure: '" + StructureEnum::toName(modelInfo[i].m_structure) + "'"); break;//remove other surface structures, but warn } if (useRoi != NULL) { const float* roiData = useRoi->getValuePointerForColumn(0); vector surfMap = inMap.getSurfaceMap(modelInfo[i].m_structure); vector usedNodes; for (size_t j = 0; j < surfMap.size(); ++j) { if (roiData[surfMap[j].m_surfaceNode] > 0.0f) { usedNodes.push_back(surfMap[j].m_surfaceNode); translate.push_back(surfMap[j].m_ciftiIndex); } } if (!usedNodes.empty()) { outMap.addSurfaceModel(inMap.getSurfaceNumberOfNodes(modelInfo[i].m_structure), modelInfo[i].m_structure, usedNodes); } } break; } case CiftiBrainModelsMap::VOXELS: { if (volRoi != NULL) { vector volMap = inMap.getVolumeStructureMap(modelInfo[i].m_structure); vector usedVoxels; for (size_t j = 0; j < volMap.size(); ++j) { if (volRoi->getValue(volMap[j].m_ijk) > 0.0f) { usedVoxels.push_back(volMap[j].m_ijk[0]); usedVoxels.push_back(volMap[j].m_ijk[1]); usedVoxels.push_back(volMap[j].m_ijk[2]); translate.push_back(volMap[j].m_ciftiIndex); } } if (!usedVoxels.empty()) { outMap.addVolumeModel(modelInfo[i].m_structure, usedVoxels); } } break; } } } if (outMap.getLength() == 0) { throw AlgorithmException("output mapping would contain no brainordinates"); } CiftiXML outXML = inXML; outXML.setMap(direction, outMap); ciftiOut->setCiftiXML(outXML); vector outDims = outXML.getDimensions(); vector scratchRow(inXML.getDimensionLength(CiftiXML::ALONG_ROW)); if (direction == CiftiXML::ALONG_ROW) { vector outRow(outMap.getLength()); for (MultiDimIterator iter(vector(outDims.begin() + 1, outDims.end())); !iter.atEnd(); ++iter) { ciftiIn->getRow(scratchRow.data(), *iter); for (size_t i = 0; i < translate.size(); ++i) { outRow[i] = scratchRow[translate[i]]; } ciftiOut->setRow(outRow.data(), *iter); } } else { for (MultiDimIterator iter(vector(outDims.begin() + 1, outDims.end())); !iter.atEnd(); ++iter) { vector indices = *iter; indices[direction - 1] = translate[(*iter)[direction - 1]]; ciftiIn->getRow(scratchRow.data(), indices); ciftiOut->setRow(scratchRow.data(), *iter); } } } float AlgorithmCiftiRestrictDenseMap::getAlgorithmInternalWeight() { return 1.0f;//override this if needed, if the progress bar isn't smooth } float AlgorithmCiftiRestrictDenseMap::getSubAlgorithmWeight() { //return AlgorithmInsertNameHere::getAlgorithmWeight();//if you use a subalgorithm return 0.0f; } connectome-workbench-1.2.3+git41-gc4c6c90/src/Algorithms/AlgorithmCiftiRestrictDenseMap.h000066400000000000000000000041131300200146000310670ustar00rootroot00000000000000#ifndef __ALGORITHM_CIFTI_RESTRICT_DENSE_MAP_H__ #define __ALGORITHM_CIFTI_RESTRICT_DENSE_MAP_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 "AbstractAlgorithm.h" namespace caret { class AlgorithmCiftiRestrictDenseMap : public AbstractAlgorithm { AlgorithmCiftiRestrictDenseMap(); protected: static float getSubAlgorithmWeight(); static float getAlgorithmInternalWeight(); public: AlgorithmCiftiRestrictDenseMap(ProgressObject* myProgObj, const CiftiFile* ciftiIn, const int& direction, CiftiFile* ciftiOut, const CiftiFile* ciftiRoi); AlgorithmCiftiRestrictDenseMap(ProgressObject* myProgObj, const CiftiFile* ciftiIn, const int& direction, CiftiFile* ciftiOut, const MetricFile* leftRoi, const MetricFile* rightRoi = NULL, const MetricFile* cerebRoi = NULL, const VolumeFile* volRoi = NULL); static OperationParameters* getParameters(); static void useParameters(OperationParameters* myParams, ProgressObject* myProgObj); static AString getCommandSwitch(); static AString getShortDescription(); }; typedef TemplateAutoOperation AutoAlgorithmCiftiRestrictDenseMap; } #endif //__ALGORITHM_CIFTI_RESTRICT_DENSE_MAP_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Algorithms/AlgorithmCiftiSeparate.cxx000066400000000000000000001041071300200146000277760ustar00rootroot00000000000000/*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 "AlgorithmCiftiSeparate.h" #include "AlgorithmException.h" #include "CaretLogger.h" #include "CaretPointer.h" #include "CiftiFile.h" #include "GiftiLabelTable.h" #include "LabelFile.h" #include "MetricFile.h" #include "Vector3D.h" #include "VolumeFile.h" #include #include using namespace caret; using namespace std; AString AlgorithmCiftiSeparate::getCommandSwitch() { return "-cifti-separate"; } AString AlgorithmCiftiSeparate::getShortDescription() { return "WRITE A CIFTI STRUCTURE AS METRIC, LABEL OR VOLUME"; } OperationParameters* AlgorithmCiftiSeparate::getParameters() { OperationParameters* ret = new OperationParameters(); ret->addCiftiParameter(1, "cifti-in", "the cifti to separate a component of"); ret->addStringParameter(2, "direction", "which direction to separate into components, ROW or COLUMN"); ParameterComponent* labelOpt = ret->createRepeatableParameter(3, "-label", "separate a surface model into a surface label file"); labelOpt->addStringParameter(1, "structure", "the structure to output"); labelOpt->addLabelOutputParameter(2, "label-out", "the output label file"); OptionalParameter* labelRoiOpt = labelOpt->createOptionalParameter(3, "-roi", "also output the roi of which vertices have data"); labelRoiOpt->addMetricOutputParameter(1, "roi-out", "the roi output metric"); ParameterComponent* metricOpt = ret->createRepeatableParameter(4, "-metric", "separate a surface model into a metric file"); metricOpt->addStringParameter(1, "structure", "the structure to output"); metricOpt->addMetricOutputParameter(2, "metric-out", "the output metric"); OptionalParameter* metricRoiOpt = metricOpt->createOptionalParameter(3, "-roi", "also output the roi of which vertices have data"); metricRoiOpt->addMetricOutputParameter(1, "roi-out", "the roi output metric"); ParameterComponent* volumeOpt = ret->createRepeatableParameter(5, "-volume", "separate a volume structure into a volume file"); volumeOpt->addStringParameter(1, "structure", "the structure to output"); volumeOpt->addVolumeOutputParameter(2, "volume-out", "the output volume"); OptionalParameter* volumeRoiOpt = volumeOpt->createOptionalParameter(3, "-roi", "also output the roi of which voxels have data"); volumeRoiOpt->addVolumeOutputParameter(1, "roi-out", "the roi output volume"); volumeOpt->createOptionalParameter(4, "-crop", "crop volume to the size of the component rather than using the original volume size"); OptionalParameter* volumeAllOpt = ret->createOptionalParameter(6, "-volume-all", "separate all volume structures into a volume file"); volumeAllOpt->addVolumeOutputParameter(1, "volume-out", "the output volume"); OptionalParameter* volumeAllRoiOpt = volumeAllOpt->createOptionalParameter(2, "-roi", "also output the roi of which voxels have data"); volumeAllRoiOpt->addVolumeOutputParameter(1, "roi-out", "the roi output volume"); OptionalParameter* volumeAllLabelOpt = volumeAllOpt->createOptionalParameter(4, "-label", "output a volume label file indicating the location of structures"); volumeAllLabelOpt->addVolumeOutputParameter(1, "label-out", "the label output volume"); volumeAllOpt->createOptionalParameter(3, "-crop", "crop volume to the size of the data rather than using the original volume size"); AString helpText = AString("For dtseries, dscalar, and dlabel, use COLUMN for , and if you have a symmetric dconn, COLUMN is more efficient.\n\n") + "You must specify at least one of -metric, -volume-all, -volume, or -label for this command to do anything. " + "Output volumes will spatially line up with their original positions, whether or not they are cropped.\n\n" + "For each argument, use one of the following strings:\n"; vector myStructureEnums; StructureEnum::getAllEnums(myStructureEnums); for (int i = 0; i < (int)myStructureEnums.size(); ++i) { helpText += "\n" + StructureEnum::toName(myStructureEnums[i]); } ret->setHelpText(helpText); return ret; } void AlgorithmCiftiSeparate::useParameters(OperationParameters* myParams, ProgressObject* /*myProgObj*/) {//ignore the progress object for now, and allow specifying multiple options at once CiftiFile* ciftiIn = myParams->getCifti(1); AString dirName = myParams->getString(2); int myDir; if (dirName == "ROW") { myDir = CiftiXML::ALONG_ROW; } else if (dirName == "COLUMN") { myDir = CiftiXML::ALONG_COLUMN; } else { throw AlgorithmException("incorrect string for direction, use ROW or COLUMN"); } bool outputRequested = false; const vector& labelInstances = *(myParams->getRepeatableParameterInstances(3)); for (int i = 0; i < (int)labelInstances.size(); ++i) { outputRequested = true; AString structName = labelInstances[i]->getString(1); bool ok = false; StructureEnum::Enum myStruct = StructureEnum::fromName(structName, &ok); if (!ok) { throw AlgorithmException("unrecognized structure type"); } LabelFile* labelOut = labelInstances[i]->getOutputLabel(2); MetricFile* roiOut = NULL; OptionalParameter* labelRoiOpt = labelInstances[i]->getOptionalParameter(3); if (labelRoiOpt->m_present) { roiOut = labelRoiOpt->getOutputMetric(1); } AlgorithmCiftiSeparate(NULL, ciftiIn, myDir, myStruct, labelOut, roiOut); } const vector& metricInstances = *(myParams->getRepeatableParameterInstances(4)); for (int i = 0; i < (int)metricInstances.size(); ++i) { outputRequested = true; AString structName = metricInstances[i]->getString(1); bool ok = false; StructureEnum::Enum myStruct = StructureEnum::fromName(structName, &ok); if (!ok) { throw AlgorithmException("unrecognized structure type"); } MetricFile* metricOut = metricInstances[i]->getOutputMetric(2); MetricFile* roiOut = NULL; OptionalParameter* metricRoiOpt = metricInstances[i]->getOptionalParameter(3); if (metricRoiOpt->m_present) { roiOut = metricRoiOpt->getOutputMetric(1); } AlgorithmCiftiSeparate(NULL, ciftiIn, myDir, myStruct, metricOut, roiOut); } const vector& volumeInstances = *(myParams->getRepeatableParameterInstances(5)); for (int i = 0; i < (int)volumeInstances.size(); ++i) { outputRequested = true; AString structName = volumeInstances[i]->getString(1); bool ok = false; StructureEnum::Enum myStruct = StructureEnum::fromName(structName, &ok); if (!ok) { throw AlgorithmException("unrecognized structure type"); } VolumeFile* volOut = volumeInstances[i]->getOutputVolume(2); VolumeFile* roiOut = NULL; OptionalParameter* volumeRoiOpt = volumeInstances[i]->getOptionalParameter(3); if (volumeRoiOpt->m_present) { roiOut = volumeRoiOpt->getOutputVolume(1); } bool cropVol = volumeInstances[i]->getOptionalParameter(4)->m_present; int64_t offset[3]; AlgorithmCiftiSeparate(NULL, ciftiIn, myDir, myStruct, volOut, offset, roiOut, cropVol); } OptionalParameter* volumeAllOpt = myParams->getOptionalParameter(6); if (volumeAllOpt->m_present) { outputRequested = true; VolumeFile* volOut = volumeAllOpt->getOutputVolume(1); VolumeFile* roiOut = NULL; OptionalParameter* volumeAllRoiOpt = volumeAllOpt->getOptionalParameter(2); if (volumeAllRoiOpt->m_present) { roiOut = volumeAllRoiOpt->getOutputVolume(1); } bool cropVol = volumeAllOpt->getOptionalParameter(3)->m_present; VolumeFile* labelOut = NULL; OptionalParameter* volumeAllLabelOpt = volumeAllOpt->getOptionalParameter(4); if (volumeAllLabelOpt->m_present) { labelOut = volumeAllLabelOpt->getOutputVolume(1); } int64_t offset[3]; AlgorithmCiftiSeparate(NULL, ciftiIn, myDir, volOut, offset, roiOut, cropVol, labelOut); } if (!outputRequested) { CaretLogWarning("no output requested from -cifti-separate, command will do nothing"); } } AlgorithmCiftiSeparate::AlgorithmCiftiSeparate(ProgressObject* myProgObj, const CiftiFile* ciftiIn, const int& myDir, const StructureEnum::Enum& myStruct, MetricFile* metricOut, MetricFile* roiOut) : AbstractAlgorithm(myProgObj) { LevelProgress myProgress(myProgObj); const CiftiXML& myXML = ciftiIn->getCiftiXML(); if (myXML.getNumberOfDimensions() != 2) throw AlgorithmException("cifti separate only supported on 2D cifti"); if (myDir >= myXML.getNumberOfDimensions() || myDir < 0) throw AlgorithmException("direction invalid for input cifti"); if (myXML.getMappingType(myDir) != CiftiMappingType::BRAIN_MODELS) throw AlgorithmException("specified direction does not contain brain models"); if (myXML.getMappingType(1 - myDir) == CiftiMappingType::LABELS) CaretLogWarning("creating a metric file from cifti label data"); const CiftiBrainModelsMap& myBrainModelsMap = myXML.getBrainModelsMap(myDir); if (!myBrainModelsMap.hasSurfaceData(myStruct)) throw AlgorithmException("specified file and direction does not contain the requested surface structure '" + StructureEnum::toName(myStruct) + "'"); vector myMap = myBrainModelsMap.getSurfaceMap(myStruct); int rowSize = ciftiIn->getNumberOfColumns(), colSize = ciftiIn->getNumberOfRows(); if (myDir == CiftiXML::ALONG_COLUMN) { int64_t numNodes = myBrainModelsMap.getSurfaceNumberOfNodes(myStruct); metricOut->setNumberOfNodesAndColumns(numNodes, rowSize); metricOut->setStructure(myStruct); const CiftiMappingType& myNamesMap = *(myXML.getMap(1 - myDir)); for (int j = 0; j < rowSize; ++j) { metricOut->setMapName(j, myNamesMap.getIndexName(j)); } if (roiOut != NULL) { roiOut->setNumberOfNodesAndColumns(numNodes, 1); roiOut->setStructure(myStruct); } int mapSize = (int)myMap.size(); CaretArray rowScratch(rowSize); CaretArray nodeUsed(numNodes, 0.0f); for (int i = 0; i < mapSize; ++i) { ciftiIn->getRow(rowScratch, myMap[i].m_ciftiIndex); nodeUsed[myMap[i].m_surfaceNode] = 1.0f; for (int j = 0; j < rowSize; ++j) { metricOut->setValue(myMap[i].m_surfaceNode, j, rowScratch[j]); } } if (roiOut != NULL) { roiOut->setValuesForColumn(0, nodeUsed); } for (int i = 0; i < numNodes; ++i)//zero unused columns { if (nodeUsed[i] == 0.0f) { for (int j = 0; j < rowSize; ++j) { metricOut->setValue(i, j, 0.0f); } } } } else { int64_t numNodes = myBrainModelsMap.getSurfaceNumberOfNodes(myStruct); metricOut->setNumberOfNodesAndColumns(numNodes, colSize); metricOut->setStructure(myStruct); const CiftiMappingType& myNamesMap = *(myXML.getMap(1 - myDir)); for (int j = 0; j < rowSize; ++j) { metricOut->setMapName(j, myNamesMap.getIndexName(j)); } if (roiOut != NULL) { roiOut->setNumberOfNodesAndColumns(numNodes, 1); roiOut->setStructure(myStruct); } int mapSize = (int)myMap.size(); CaretArray rowScratch(rowSize), metricScratch(numNodes, 0.0f); if (roiOut != NULL) { CaretArray nodeUsed(numNodes, 0.0f); for (int i = 0; i < mapSize; ++i) { nodeUsed[myMap[i].m_surfaceNode] = 1.0f; } roiOut->setValuesForColumn(0, nodeUsed); } for (int i = 0; i < colSize; ++i) { ciftiIn->getRow(rowScratch, i); for (int j = 0; j < mapSize; ++j) { metricScratch[myMap[j].m_surfaceNode] = rowScratch[myMap[j].m_ciftiIndex]; } metricOut->setValuesForColumn(i, metricScratch); } } } AlgorithmCiftiSeparate::AlgorithmCiftiSeparate(ProgressObject* myProgObj, const CiftiFile* ciftiIn, const int& myDir, const StructureEnum::Enum& myStruct, LabelFile* labelOut, MetricFile* roiOut) : AbstractAlgorithm(myProgObj) { LevelProgress myProgress(myProgObj); const CiftiXML& myXML = ciftiIn->getCiftiXML(); if (myXML.getNumberOfDimensions() != 2) throw AlgorithmException("cifti separate only supported on 2D cifti"); if (myDir >= myXML.getNumberOfDimensions() || myDir < 0) throw AlgorithmException("direction invalid for input cifti"); if (myXML.getMappingType(myDir) != CiftiMappingType::BRAIN_MODELS) throw AlgorithmException("specified direction does not contain brain models"); if (myXML.getMappingType(1 - myDir) != CiftiMappingType::LABELS) throw AlgorithmException("label separate requested on non-label cifti"); const CiftiBrainModelsMap& myBrainModelsMap = myXML.getBrainModelsMap(myDir); const CiftiLabelsMap& myLabelsMap = myXML.getLabelsMap(1 - myDir); if (!myBrainModelsMap.hasSurfaceData(myStruct)) throw AlgorithmException("specified file and direction does not contain the requested surface structure '" + StructureEnum::toName(myStruct) + "'"); vector myMap = myBrainModelsMap.getSurfaceMap(myStruct); int64_t rowSize = ciftiIn->getNumberOfColumns(), colSize = ciftiIn->getNumberOfRows(); if (myDir == CiftiXML::ALONG_COLUMN) { int64_t numNodes = myBrainModelsMap.getSurfaceNumberOfNodes(myStruct); labelOut->setNumberOfNodesAndColumns(numNodes, rowSize); labelOut->setStructure(myStruct); const CiftiMappingType& myNamesMap = *(myXML.getMap(1 - myDir)); for (int j = 0; j < rowSize; ++j) { labelOut->setMapName(j, myNamesMap.getIndexName(j)); } if (roiOut != NULL) { roiOut->setNumberOfNodesAndColumns(numNodes, 1); roiOut->setStructure(myStruct); } int64_t mapSize = (int64_t)myMap.size(); CaretArray rowScratch(rowSize); CaretArray nodeUsed(numNodes, 0.0f); GiftiLabelTable myTable; map cumulativeRemap; for (int64_t i = 0; i < rowSize; ++i) { map thisRemap = myTable.append(*(myLabelsMap.getMapLabelTable(i))); cumulativeRemap.insert(thisRemap.begin(), thisRemap.end()); } for (int64_t i = 0; i < mapSize; ++i) { ciftiIn->getRow(rowScratch, myMap[i].m_ciftiIndex); nodeUsed[myMap[i].m_surfaceNode] = 1.0f; for (int j = 0; j < rowSize; ++j) { int32_t inVal = (int32_t)floor(rowScratch[j] + 0.5f); map::const_iterator iter = cumulativeRemap.find(inVal); if (iter == cumulativeRemap.end()) { labelOut->setLabelKey(myMap[i].m_surfaceNode, j, inVal); } else { labelOut->setLabelKey(myMap[i].m_surfaceNode, j, iter->second); } } } *(labelOut->getLabelTable()) = myTable; int32_t unusedLabel = myTable.getUnassignedLabelKey(); if (roiOut != NULL) { roiOut->setValuesForColumn(0, nodeUsed); } for (int i = 0; i < numNodes; ++i)//set unused columns to unassigned { if (nodeUsed[i] == 0.0f) { for (int j = 0; j < rowSize; ++j) { labelOut->setLabelKey(i, j, unusedLabel); } } } } else { int64_t numNodes = myBrainModelsMap.getSurfaceNumberOfNodes(myStruct); labelOut->setNumberOfNodesAndColumns(numNodes, colSize); labelOut->setStructure(myStruct); const CiftiMappingType& myNamesMap = *(myXML.getMap(1 - myDir)); for (int j = 0; j < rowSize; ++j) { labelOut->setMapName(j, myNamesMap.getIndexName(j)); } if (roiOut != NULL) { roiOut->setNumberOfNodesAndColumns(numNodes, 1); roiOut->setStructure(myStruct); } int64_t mapSize = (int)myMap.size(); if (roiOut != NULL) { CaretArray nodeUsed(numNodes, 0.0f); for (int i = 0; i < mapSize; ++i) { nodeUsed[myMap[i].m_surfaceNode] = 1.0f; } roiOut->setValuesForColumn(0, nodeUsed); } CaretArray rowScratch(rowSize); CaretArray nodeUsed(numNodes, 0); for (int64_t j = 0; j < mapSize; ++j) { nodeUsed[myMap[j].m_surfaceNode] = 1; } GiftiLabelTable myTable; map cumulativeRemap; for (int64_t i = 0; i < colSize; ++i) { map thisRemap = myTable.append(*(myLabelsMap.getMapLabelTable(i))); cumulativeRemap.insert(thisRemap.begin(), thisRemap.end()); } *(labelOut->getLabelTable()) = myTable; int32_t unusedLabel = myTable.getUnassignedLabelKey(); for (int64_t i = 0; i < colSize; ++i) { ciftiIn->getRow(rowScratch, i); for (int64_t j = 0; j < mapSize; ++j) { int32_t inVal = (int32_t)floor(rowScratch[j] + 0.5f); map::const_iterator iter = cumulativeRemap.find(inVal); if (iter == cumulativeRemap.end()) { labelOut->setLabelKey(myMap[j].m_surfaceNode, i, inVal); } else { labelOut->setLabelKey(myMap[j].m_surfaceNode, i, iter->second); } } for (int64_t j = 0; j < numNodes; ++j)//set unused columns to unassigned { if (nodeUsed[j] == 0) { labelOut->setLabelKey(j, i, unusedLabel); } } } } } AlgorithmCiftiSeparate::AlgorithmCiftiSeparate(ProgressObject* myProgObj, const CiftiFile* ciftiIn, const int& myDir, const StructureEnum::Enum& myStruct, VolumeFile* volOut, int64_t offsetOut[3], VolumeFile* roiOut, const bool& cropVol) : AbstractAlgorithm(myProgObj) { LevelProgress myProgress(myProgObj); const CiftiXML& myXML = ciftiIn->getCiftiXML(); if (myXML.getNumberOfDimensions() != 2) throw AlgorithmException("cifti separate only supported on 2D cifti"); if (myDir >= ciftiIn->getCiftiXML().getNumberOfDimensions() || myDir < 0) throw AlgorithmException("direction invalid for input cifti"); if (myXML.getMappingType(myDir) != CiftiMappingType::BRAIN_MODELS) throw AlgorithmException("specified direction does not contain brain models"); int rowSize = ciftiIn->getNumberOfColumns(), colSize = ciftiIn->getNumberOfRows(); const CiftiBrainModelsMap& myBrainMap = myXML.getBrainModelsMap(myDir); const int64_t* myDims = myBrainMap.getVolumeSpace().getDims(); vector > mySform = myBrainMap.getVolumeSpace().getSform(); if (!myBrainMap.hasVolumeData(myStruct)) throw AlgorithmException("specified file and direction does not contain the requested volume structure"); vector myMap = myBrainMap.getVolumeStructureMap(myStruct); vector newdims; int64_t numVoxels = (int64_t)myMap.size(); if (cropVol) { newdims.resize(3); getCroppedVolSpace(ciftiIn, myDir, myStruct, newdims.data(), mySform, offsetOut); } else { newdims.push_back(myDims[0]); newdims.push_back(myDims[1]); newdims.push_back(myDims[2]); offsetOut[0] = 0; offsetOut[1] = 0; offsetOut[2] = 0; } if (roiOut != NULL) { roiOut->reinitialize(newdims, mySform); roiOut->setValueAllVoxels(0.0f); } CaretArray rowScratch(rowSize); if (myDir == CiftiXML::ALONG_COLUMN) { if (rowSize > 1) newdims.push_back(rowSize); volOut->reinitialize(newdims, mySform); volOut->setValueAllVoxels(0.0f); const CiftiMappingType& myNamesMap = *(myXML.getMap(1 - myDir)); for (int j = 0; j < rowSize; ++j) { volOut->setMapName(j, myNamesMap.getIndexName(j)); } if (myXML.getMappingType(CiftiXML::ALONG_ROW) == CiftiMappingType::LABELS) { const CiftiLabelsMap& myLabelsMap = myXML.getLabelsMap(CiftiXML::ALONG_ROW); volOut->setType(SubvolumeAttributes::LABEL); for (int j = 0; j < rowSize; ++j) { *(volOut->getMapLabelTable(j)) = *(myLabelsMap.getMapLabelTable(j)); } } for (int64_t i = 0; i < numVoxels; ++i) { int64_t thisvoxel[3] = { myMap[i].m_ijk[0] - offsetOut[0], myMap[i].m_ijk[1] - offsetOut[1], myMap[i].m_ijk[2] - offsetOut[2] }; if (roiOut != NULL) { roiOut->setValue(1.0f, thisvoxel); } ciftiIn->getRow(rowScratch, myMap[i].m_ciftiIndex); for (int j = 0; j < rowSize; ++j) { volOut->setValue(rowScratch[j], thisvoxel, j); } } } else { if (colSize > 1) newdims.push_back(colSize); volOut->reinitialize(newdims, mySform); volOut->setValueAllVoxels(0.0f); const CiftiMappingType& myNamesMap = *(myXML.getMap(1 - myDir)); for (int j = 0; j < rowSize; ++j) { volOut->setMapName(j, myNamesMap.getIndexName(j)); } if (myXML.getMappingType(CiftiXML::ALONG_COLUMN) == CiftiMappingType::LABELS) { const CiftiLabelsMap& myLabelsMap = myXML.getLabelsMap(CiftiXML::ALONG_COLUMN); volOut->setType(SubvolumeAttributes::LABEL); for (int j = 0; j < colSize; ++j) { *(volOut->getMapLabelTable(j)) = *(myLabelsMap.getMapLabelTable(j)); } } for (int64_t i = 0; i < colSize; ++i) { ciftiIn->getRow(rowScratch, i); for (int64_t j = 0; j < numVoxels; ++j) { int64_t thisvoxel[3] = { myMap[j].m_ijk[0] - offsetOut[0], myMap[j].m_ijk[1] - offsetOut[1], myMap[j].m_ijk[2] - offsetOut[2] }; if (i == 0 && roiOut != NULL) { roiOut->setValue(1.0f, thisvoxel); } volOut->setValue(rowScratch[myMap[j].m_ciftiIndex], thisvoxel, i); } } } } AlgorithmCiftiSeparate::AlgorithmCiftiSeparate(ProgressObject* myProgObj, const CiftiFile* ciftiIn, const int& myDir, VolumeFile* volOut, int64_t offsetOut[3], VolumeFile* roiOut, const bool& cropVol, VolumeFile* labelOut): AbstractAlgorithm(myProgObj) { LevelProgress myProgress(myProgObj); const CiftiXML& myXML = ciftiIn->getCiftiXML(); if (myXML.getNumberOfDimensions() != 2) throw AlgorithmException("cifti separate only supported on 2D cifti"); if (myDir >= ciftiIn->getCiftiXML().getNumberOfDimensions() || myDir < 0) throw AlgorithmException("direction invalid for input cifti"); if (myXML.getMappingType(myDir) != CiftiMappingType::BRAIN_MODELS) throw AlgorithmException("specified direction does not contain brain models"); int rowSize = ciftiIn->getNumberOfColumns(), colSize = ciftiIn->getNumberOfRows(); const CiftiBrainModelsMap& myBrainMap = myXML.getBrainModelsMap(myDir); if (!myBrainMap.hasVolumeData()) throw AlgorithmException("specified file and direction does not contain any volume data"); const int64_t* myDims = myBrainMap.getVolumeSpace().getDims(); vector > mySform = myBrainMap.getVolumeSpace().getSform(); vector newdims; if (cropVol) { newdims.resize(3); getCroppedVolSpaceAll(ciftiIn, myDir, newdims.data(), mySform, offsetOut); } else { newdims.push_back(myDims[0]); newdims.push_back(myDims[1]); newdims.push_back(myDims[2]); offsetOut[0] = 0; offsetOut[1] = 0; offsetOut[2] = 0; } if (labelOut != NULL) { labelOut->reinitialize(newdims, mySform, 1, SubvolumeAttributes::LABEL); labelOut->setValueAllVoxels(0.0f);//unlabeled key defaults to 0 vector volStructs = myBrainMap.getVolumeStructureList(); GiftiLabelTable structureTable; for (int i = 0; i < (int)volStructs.size(); ++i) { const int32_t structKey = structureTable.addLabel(StructureEnum::toName(volStructs[i]), rand() & 255, rand() & 255, rand() & 255, 255); const vector& voxelList = myBrainMap.getVoxelList(volStructs[i]); int64_t structVoxels = (int64_t)voxelList.size(); for (int64_t j = 0; j < structVoxels; j += 3) { labelOut->setValue(structKey, voxelList[j] - offsetOut[0], voxelList[j + 1] - offsetOut[1], voxelList[j + 2] - offsetOut[2]); } } *(labelOut->getMapLabelTable(0)) = structureTable; } if (roiOut != NULL) { roiOut->reinitialize(newdims, mySform); roiOut->setValueAllVoxels(0.0f); } vector myMap = myBrainMap.getFullVolumeMap(); int64_t numVoxels = (int64_t)myMap.size(); CaretArray rowScratch(rowSize); if (myDir == CiftiXML::ALONG_COLUMN) { if (rowSize > 1) newdims.push_back(rowSize); volOut->reinitialize(newdims, mySform); volOut->setValueAllVoxels(0.0f); const CiftiMappingType& myNamesMap = *(myXML.getMap(1 - myDir)); for (int j = 0; j < rowSize; ++j) { volOut->setMapName(j, myNamesMap.getIndexName(j)); } if (myXML.getMappingType(CiftiXML::ALONG_ROW) == CiftiMappingType::LABELS) { const CiftiLabelsMap& myLabelsMap = myXML.getLabelsMap(CiftiXML::ALONG_ROW); volOut->setType(SubvolumeAttributes::LABEL); for (int j = 0; j < rowSize; ++j) { *(volOut->getMapLabelTable(j)) = *(myLabelsMap.getMapLabelTable(j)); } } for (int64_t i = 0; i < numVoxels; ++i) { int64_t thisvoxel[3] = { myMap[i].m_ijk[0] - offsetOut[0], myMap[i].m_ijk[1] - offsetOut[1], myMap[i].m_ijk[2] - offsetOut[2] }; if (roiOut != NULL) { roiOut->setValue(1.0f, thisvoxel); } ciftiIn->getRow(rowScratch, myMap[i].m_ciftiIndex); for (int j = 0; j < rowSize; ++j) { volOut->setValue(rowScratch[j], thisvoxel, j); } } } else { if (colSize > 1) newdims.push_back(colSize); volOut->reinitialize(newdims, mySform); volOut->setValueAllVoxels(0.0f); const CiftiMappingType& myNamesMap = *(myXML.getMap(1 - myDir)); for (int j = 0; j < rowSize; ++j) { volOut->setMapName(j, myNamesMap.getIndexName(j)); } if (myXML.getMappingType(CiftiXML::ALONG_COLUMN) == CiftiMappingType::LABELS) { const CiftiLabelsMap& myLabelsMap = myXML.getLabelsMap(CiftiXML::ALONG_COLUMN); volOut->setType(SubvolumeAttributes::LABEL); for (int j = 0; j < colSize; ++j) { *(volOut->getMapLabelTable(j)) = *(myLabelsMap.getMapLabelTable(j)); } } for (int64_t i = 0; i < colSize; ++i) { ciftiIn->getRow(rowScratch, i); for (int64_t j = 0; j < numVoxels; ++j) { int64_t thisvoxel[3] = { myMap[j].m_ijk[0] - offsetOut[0], myMap[j].m_ijk[1] - offsetOut[1], myMap[j].m_ijk[2] - offsetOut[2] }; if (i == 0 && roiOut != NULL) { roiOut->setValue(1.0f, thisvoxel); } volOut->setValue(rowScratch[myMap[j].m_ciftiIndex], thisvoxel, i); } } } } void AlgorithmCiftiSeparate::getCroppedVolSpace(const CiftiFile* ciftiIn, const int& myDir, const StructureEnum::Enum& myStruct, int64_t dimsOut[3], vector >& sformOut, int64_t offsetOut[3]) { const CiftiXML& myXML = ciftiIn->getCiftiXML(); if (myXML.getMappingType(myDir) != CiftiMappingType::BRAIN_MODELS) throw AlgorithmException("specified direction does not contain brain models"); const CiftiBrainModelsMap& myBrainMap = myXML.getBrainModelsMap(myDir); sformOut = myBrainMap.getVolumeSpace().getSform(); vector myMap = myBrainMap.getVolumeStructureMap(myStruct); int64_t numVoxels = (int64_t)myMap.size(); if (numVoxels > 0) {//make a voxel bounding box to minimize memory usage int extrema[6] = { myMap[0].m_ijk[0], myMap[0].m_ijk[0], myMap[0].m_ijk[1], myMap[0].m_ijk[1], myMap[0].m_ijk[2], myMap[0].m_ijk[2] }; for (int64_t i = 1; i < numVoxels; ++i) { if (myMap[i].m_ijk[0] < extrema[0]) extrema[0] = myMap[i].m_ijk[0]; if (myMap[i].m_ijk[0] > extrema[1]) extrema[1] = myMap[i].m_ijk[0]; if (myMap[i].m_ijk[1] < extrema[2]) extrema[2] = myMap[i].m_ijk[1]; if (myMap[i].m_ijk[1] > extrema[3]) extrema[3] = myMap[i].m_ijk[1]; if (myMap[i].m_ijk[2] < extrema[4]) extrema[4] = myMap[i].m_ijk[2]; if (myMap[i].m_ijk[2] > extrema[5]) extrema[5] = myMap[i].m_ijk[2]; } dimsOut[0] = extrema[1] - extrema[0] + 1; dimsOut[1] = extrema[3] - extrema[2] + 1; dimsOut[2] = extrema[5] - extrema[4] + 1; offsetOut[0] = extrema[0]; offsetOut[1] = extrema[2]; offsetOut[2] = extrema[4]; Vector3D ivec, jvec, kvec, shift; ivec[0] = sformOut[0][0]; ivec[1] = sformOut[1][0]; ivec[2] = sformOut[2][0]; jvec[0] = sformOut[0][1]; jvec[1] = sformOut[1][1]; jvec[2] = sformOut[2][1]; kvec[0] = sformOut[0][2]; kvec[1] = sformOut[1][2]; kvec[2] = sformOut[2][2]; shift = offsetOut[0] * ivec + offsetOut[1] * jvec + offsetOut[2] * kvec; sformOut[0][3] += shift[0];//fix the sform to align to the old position with the new dimensions sformOut[1][3] += shift[1]; sformOut[2][3] += shift[2]; } else { throw AlgorithmException("cropped volume requested, but no voxels exist in this structure"); } } void AlgorithmCiftiSeparate::getCroppedVolSpaceAll(const CiftiFile* ciftiIn, const int& myDir, int64_t dimsOut[3], vector >& sformOut, int64_t offsetOut[3]) { const CiftiXML& myXML = ciftiIn->getCiftiXML(); if (myXML.getMappingType(myDir) != CiftiMappingType::BRAIN_MODELS) throw AlgorithmException("specified direction does not contain brain models"); const CiftiBrainModelsMap& myBrainMap = myXML.getBrainModelsMap(myDir); sformOut = myBrainMap.getVolumeSpace().getSform(); vector myMap = myBrainMap.getFullVolumeMap(); int64_t numVoxels = (int64_t)myMap.size(); if (numVoxels > 0) {//make a voxel bounding box to minimize memory usage int extrema[6] = { myMap[0].m_ijk[0], myMap[0].m_ijk[0], myMap[0].m_ijk[1], myMap[0].m_ijk[1], myMap[0].m_ijk[2], myMap[0].m_ijk[2] }; for (int64_t i = 1; i < numVoxels; ++i) { if (myMap[i].m_ijk[0] < extrema[0]) extrema[0] = myMap[i].m_ijk[0]; if (myMap[i].m_ijk[0] > extrema[1]) extrema[1] = myMap[i].m_ijk[0]; if (myMap[i].m_ijk[1] < extrema[2]) extrema[2] = myMap[i].m_ijk[1]; if (myMap[i].m_ijk[1] > extrema[3]) extrema[3] = myMap[i].m_ijk[1]; if (myMap[i].m_ijk[2] < extrema[4]) extrema[4] = myMap[i].m_ijk[2]; if (myMap[i].m_ijk[2] > extrema[5]) extrema[5] = myMap[i].m_ijk[2]; } dimsOut[0] = extrema[1] - extrema[0] + 1; dimsOut[1] = extrema[3] - extrema[2] + 1; dimsOut[2] = extrema[5] - extrema[4] + 1; offsetOut[0] = extrema[0]; offsetOut[1] = extrema[2]; offsetOut[2] = extrema[4]; Vector3D ivec, jvec, kvec, shift; ivec[0] = sformOut[0][0]; ivec[1] = sformOut[1][0]; ivec[2] = sformOut[2][0]; jvec[0] = sformOut[0][1]; jvec[1] = sformOut[1][1]; jvec[2] = sformOut[2][1]; kvec[0] = sformOut[0][2]; kvec[1] = sformOut[1][2]; kvec[2] = sformOut[2][2]; shift = offsetOut[0] * ivec + offsetOut[1] * jvec + offsetOut[2] * kvec; sformOut[0][3] += shift[0];//fix the sform to align to the old position with the new dimensions sformOut[1][3] += shift[1]; sformOut[2][3] += shift[2]; } else { throw AlgorithmException("cropped volume requested, but no voxels exist in the specified direction"); } } float AlgorithmCiftiSeparate::getAlgorithmInternalWeight() { return 1.0f;//override this if needed, if the progress bar isn't smooth } float AlgorithmCiftiSeparate::getSubAlgorithmWeight() { return 0.0f; } connectome-workbench-1.2.3+git41-gc4c6c90/src/Algorithms/AlgorithmCiftiSeparate.h000066400000000000000000000060421300200146000274220ustar00rootroot00000000000000#ifndef __ALGORITHM_CIFTI_SEPARATE_H__ #define __ALGORITHM_CIFTI_SEPARATE_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 "AbstractAlgorithm.h" #include "StructureEnum.h" #include namespace caret { class AlgorithmCiftiSeparate : public AbstractAlgorithm { AlgorithmCiftiSeparate(); protected: static float getSubAlgorithmWeight(); static float getAlgorithmInternalWeight(); public: AlgorithmCiftiSeparate(ProgressObject* myProgObj, const CiftiFile* ciftiIn, const int& myDir, const StructureEnum::Enum& myStruct, MetricFile* metricOut, MetricFile* roiOut = NULL); AlgorithmCiftiSeparate(ProgressObject* myProgObj, const CiftiFile* ciftiIn, const int& myDir, const StructureEnum::Enum& myStruct, LabelFile* labelOut, MetricFile* roiOut = NULL); AlgorithmCiftiSeparate(ProgressObject* myProgObj, const CiftiFile* ciftiIn, const int& myDir, const StructureEnum::Enum& myStruct, VolumeFile* volOut, int64_t offsetOut[3], VolumeFile* roiOut = NULL, const bool& cropVol = true); AlgorithmCiftiSeparate(ProgressObject* myProgObj, const CiftiFile* ciftiIn, const int& myDir, VolumeFile* volOut, int64_t offsetOut[3], VolumeFile* roiOut = NULL, const bool& cropVol = true, VolumeFile* labelOut = NULL); static void getCroppedVolSpace(const CiftiFile* ciftiIn, const int& myDir, const StructureEnum::Enum& myStruct, int64_t dimsOut[3], std::vector >& sformOut, int64_t offsetOut[3]); static void getCroppedVolSpaceAll(const CiftiFile* ciftiIn, const int& myDir, int64_t dimsOut[3], std::vector >& sformOut, int64_t offsetOut[3]); static OperationParameters* getParameters(); static void useParameters(OperationParameters* myParams, ProgressObject* myProgObj); static AString getCommandSwitch(); static AString getShortDescription(); }; typedef TemplateAutoOperation AutoAlgorithmCiftiSeparate; } #endif //__ALGORITHM_CIFTI_SEPARATE_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Algorithms/AlgorithmCiftiSmoothing.cxx000066400000000000000000000356071300200146000302110ustar00rootroot00000000000000/*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 "AlgorithmCiftiSmoothing.h" #include "AlgorithmException.h" #include "AlgorithmMetricSmoothing.h" #include "AlgorithmVolumeSmoothing.h" #include "CiftiFile.h" #include "MetricFile.h" #include "VolumeFile.h" #include "SurfaceFile.h" #include "AlgorithmCiftiSeparate.h" #include "AlgorithmCiftiReplaceStructure.h" using namespace caret; using namespace std; AString AlgorithmCiftiSmoothing::getCommandSwitch() { return "-cifti-smoothing"; } AString AlgorithmCiftiSmoothing::getShortDescription() { return "SMOOTH A CIFTI FILE"; } OperationParameters* AlgorithmCiftiSmoothing::getParameters() { OperationParameters* ret = new OperationParameters(); ret->addCiftiParameter(1, "cifti", "the input cifti"); ret->addDoubleParameter(2, "surface-kernel", "the sigma for the gaussian surface smoothing kernel, in mm"); ret->addDoubleParameter(3, "volume-kernel", "the sigma for the gaussian volume smoothing kernel, in mm"); ret->addStringParameter(4, "direction", "which dimension to smooth along, ROW or COLUMN"); ret->addCiftiOutputParameter(5, "cifti-out", "the output cifti"); OptionalParameter* leftSurfOpt = ret->createOptionalParameter(6, "-left-surface", "specify the left surface to use"); leftSurfOpt->addSurfaceParameter(1, "surface", "the left surface file"); OptionalParameter* leftCorrAreasOpt = leftSurfOpt->createOptionalParameter(2, "-left-corrected-areas", "vertex areas to use instead of computing them from the left surface"); leftCorrAreasOpt->addMetricParameter(1, "area-metric", "the corrected vertex areas, as a metric"); OptionalParameter* rightSurfOpt = ret->createOptionalParameter(7, "-right-surface", "specify the right surface to use"); rightSurfOpt->addSurfaceParameter(1, "surface", "the right surface file"); OptionalParameter* rightCorrAreasOpt = rightSurfOpt->createOptionalParameter(2, "-right-corrected-areas", "vertex areas to use instead of computing them from the right surface"); rightCorrAreasOpt->addMetricParameter(1, "area-metric", "the corrected vertex areas, as a metric"); OptionalParameter* cerebSurfOpt = ret->createOptionalParameter(8, "-cerebellum-surface", "specify the cerebellum surface to use"); cerebSurfOpt->addSurfaceParameter(1, "surface", "the cerebellum surface file"); OptionalParameter* cerebCorrAreasOpt = cerebSurfOpt->createOptionalParameter(2, "-cerebellum-corrected-areas", "vertex areas to use instead of computing them from the cerebellum surface"); cerebCorrAreasOpt->addMetricParameter(1, "area-metric", "the corrected vertex areas, as a metric"); OptionalParameter* roiOpt = ret->createOptionalParameter(9, "-cifti-roi", "smooth only within regions of interest"); roiOpt->addCiftiParameter(1, "roi-cifti", "the regions to smooth within, as a cifti file"); ret->createOptionalParameter(10, "-fix-zeros-volume", "treat values of zero in the volume as missing data"); ret->createOptionalParameter(11, "-fix-zeros-surface", "treat values of zero on the surface as missing data"); ret->createOptionalParameter(12, "-merged-volume", "smooth across subcortical structure boundaries"); ret->setHelpText( AString("The input cifti file must have a brain models mapping on the chosen dimension, columns for .dtseries, and either for .dconn. ") + "By default, data in different structures is smoothed independently (i.e., \"parcel constrained\" smoothing), so volume structures that touch do not smooth across this boundary. " + "Specify -merged-volume to ignore these boundaries. " + "Surface smoothing uses the GEO_GAUSS_AREA smoothing method.\n\n" + "The -*-corrected-areas options are intended for when it is unavoidable to smooth on group average surfaces, it is only an approximate correction " + "for the reduction of structure in a group average surface. It is better to smooth the data on individuals before averaging, when feasible.\n\n" + "The -fix-zeros-* options will treat values of zero as lack of data, and not use that value when generating the smoothed values, but will fill zeros with extrapolated values. " + "The ROI should have a brain models mapping along columns, exactly matching the mapping of the chosen direction in the input file. " + "Data outside the ROI is ignored." ); return ret; } void AlgorithmCiftiSmoothing::useParameters(OperationParameters* myParams, ProgressObject* myProgObj) { CiftiFile* myCifti = myParams->getCifti(1); float surfKern = (float)myParams->getDouble(2); float volKern = (float)myParams->getDouble(3); AString directionName = myParams->getString(4); int myDir; if (directionName == "ROW") { myDir = CiftiXMLOld::ALONG_ROW; } else if (directionName == "COLUMN") { myDir = CiftiXMLOld::ALONG_COLUMN; } else { throw AlgorithmException("incorrect string for direction, use ROW or COLUMN"); } CiftiFile* myCiftiOut = myParams->getOutputCifti(5); SurfaceFile* myLeftSurf = NULL, *myRightSurf = NULL, *myCerebSurf = NULL; MetricFile* myLeftAreas = NULL, *myRightAreas = NULL, *myCerebAreas = NULL; OptionalParameter* leftSurfOpt = myParams->getOptionalParameter(6); if (leftSurfOpt->m_present) { myLeftSurf = leftSurfOpt->getSurface(1); OptionalParameter* leftCorrAreasOpt = leftSurfOpt->getOptionalParameter(2); if (leftCorrAreasOpt->m_present) { myLeftAreas = leftCorrAreasOpt->getMetric(1); } } OptionalParameter* rightSurfOpt = myParams->getOptionalParameter(7); if (rightSurfOpt->m_present) { myRightSurf = rightSurfOpt->getSurface(1); OptionalParameter* rightCorrAreasOpt = rightSurfOpt->getOptionalParameter(2); if (rightCorrAreasOpt->m_present) { myRightAreas = rightCorrAreasOpt->getMetric(1); } } OptionalParameter* cerebSurfOpt = myParams->getOptionalParameter(8); if (cerebSurfOpt->m_present) { myCerebSurf = cerebSurfOpt->getSurface(1); OptionalParameter* cerebCorrAreasOpt = cerebSurfOpt->getOptionalParameter(2); if (cerebCorrAreasOpt->m_present) { myCerebAreas = cerebCorrAreasOpt->getMetric(1); } } CiftiFile* roiCifti = NULL; OptionalParameter* roiOpt = myParams->getOptionalParameter(9); if (roiOpt->m_present) { roiCifti = roiOpt->getCifti(1); } bool fixZerosVol = myParams->getOptionalParameter(10)->m_present; bool fixZerosSurf = myParams->getOptionalParameter(11)->m_present; bool mergedVolume = myParams->getOptionalParameter(12)->m_present; AlgorithmCiftiSmoothing(myProgObj, myCifti, surfKern, volKern, myDir, myCiftiOut, myLeftSurf, myRightSurf, myCerebSurf, roiCifti, fixZerosVol, fixZerosSurf, myLeftAreas, myRightAreas, myCerebAreas, mergedVolume); } AlgorithmCiftiSmoothing::AlgorithmCiftiSmoothing(ProgressObject* myProgObj, const CiftiFile* myCifti, const float& surfKern, const float& volKern, const int& myDir, CiftiFile* myCiftiOut, const SurfaceFile* myLeftSurf, const SurfaceFile* myRightSurf, const SurfaceFile* myCerebSurf, const CiftiFile* roiCifti, bool fixZerosVol, bool fixZerosSurf, const MetricFile* myLeftAreas, const MetricFile* myRightAreas, const MetricFile* myCerebAreas, const bool& mergedVolume) : AbstractAlgorithm(myProgObj) { LevelProgress myProgress(myProgObj); if (!(surfKern > 0.0f) && !(volKern > 0.0f)) throw AlgorithmException("zero smoothing kernels requested for both volume and surface"); const CiftiXMLOld& myXML = myCifti->getCiftiXMLOld(); vector surfaceList, volumeList; if (myDir == CiftiXMLOld::ALONG_COLUMN) { if (!myXML.getStructureListsForColumns(surfaceList, volumeList)) { throw AlgorithmException("specified direction does not contain brainordinates"); } } else { if (myDir != CiftiXMLOld::ALONG_ROW) throw AlgorithmException("direction not supported in AlgorithmCiftiSmoothing"); if (!myXML.getStructureListsForRows(surfaceList, volumeList)) { throw AlgorithmException("specified direction does not contain brainordinates"); } } if (roiCifti != NULL && !myXML.mappingMatches(myDir, roiCifti->getCiftiXMLOld(), CiftiXMLOld::ALONG_COLUMN)) { throw AlgorithmException("along-column mapping of roi cifti does not match the smoothing direction of the input cifti"); } for (int whichStruct = 0; whichStruct < (int)surfaceList.size(); ++whichStruct) {//sanity check surfaces const SurfaceFile* mySurf = NULL; const MetricFile* myAreas = NULL; AString surfType; switch (surfaceList[whichStruct]) { case StructureEnum::CORTEX_LEFT: mySurf = myLeftSurf; myAreas = myLeftAreas; surfType = "left"; break; case StructureEnum::CORTEX_RIGHT: mySurf = myRightSurf; myAreas = myRightAreas; surfType = "right"; break; case StructureEnum::CEREBELLUM: mySurf = myCerebSurf; myAreas = myCerebAreas; surfType = "cerebellum"; break; default: throw AlgorithmException("found surface model with incorrect type: " + StructureEnum::toName(surfaceList[whichStruct])); break; } if (mySurf == NULL) { throw AlgorithmException(surfType + " surface required but not provided"); } if (myDir == CiftiXMLOld::ALONG_COLUMN) { if (mySurf->getNumberOfNodes() != myXML.getColumnSurfaceNumberOfNodes(surfaceList[whichStruct])) { throw AlgorithmException(surfType + " surface has the wrong number of vertices"); } } else { if (mySurf->getNumberOfNodes() != myXML.getRowSurfaceNumberOfNodes(surfaceList[whichStruct])) { throw AlgorithmException(surfType + " surface has the wrong number of vertices"); } } if (myAreas != NULL && myAreas->getNumberOfNodes() != mySurf->getNumberOfNodes()) { throw AlgorithmException(surfType + " surface and vertex area metric have different number of vertices"); } } myCiftiOut->setCiftiXML(myXML); for (int whichStruct = 0; whichStruct < (int)surfaceList.size(); ++whichStruct) { const SurfaceFile* mySurf = NULL; const MetricFile* myAreas = NULL; switch (surfaceList[whichStruct]) { case StructureEnum::CORTEX_LEFT: mySurf = myLeftSurf; myAreas = myLeftAreas; break; case StructureEnum::CORTEX_RIGHT: mySurf = myRightSurf; myAreas = myRightAreas; break; case StructureEnum::CEREBELLUM: mySurf = myCerebSurf; myAreas = myCerebAreas; break; default: break; } MetricFile myMetric, myRoi, myMetricOut; AlgorithmCiftiSeparate(NULL, myCifti, myDir, surfaceList[whichStruct], &myMetric, &myRoi); if (surfKern > 0.0f) { if (roiCifti != NULL) {//due to above testing, we know the structure mask is the same, so just overwrite the ROI from the mask AlgorithmCiftiSeparate(NULL, roiCifti, CiftiXMLOld::ALONG_COLUMN, surfaceList[whichStruct], &myRoi); } AlgorithmMetricSmoothing(NULL, mySurf, &myMetric, surfKern, &myMetricOut, &myRoi, false, fixZerosSurf, -1, myAreas); AlgorithmCiftiReplaceStructure(NULL, myCiftiOut, myDir, surfaceList[whichStruct], &myMetricOut); } else { AlgorithmCiftiReplaceStructure(NULL, myCiftiOut, myDir, surfaceList[whichStruct], &myMetric); } } if (mergedVolume) { VolumeFile myVol, myRoi, myVolOut; int64_t offset[3]; AlgorithmCiftiSeparate(NULL, myCifti, myDir, &myVol, offset, &myRoi, true); if (volKern > 0.0f) { if (roiCifti != NULL) {//due to above testing, we know the structure mask is the same, so just overwrite the ROI from the mask AlgorithmCiftiSeparate(NULL, roiCifti, CiftiXMLOld::ALONG_COLUMN, &myRoi, offset, NULL, true); } AlgorithmVolumeSmoothing(NULL, &myVol, volKern, &myVolOut, &myRoi, fixZerosVol); AlgorithmCiftiReplaceStructure(NULL, myCiftiOut, myDir, &myVolOut, true); } else { AlgorithmCiftiReplaceStructure(NULL, myCiftiOut, myDir, &myVol, true); } } else { for (int whichStruct = 0; whichStruct < (int)volumeList.size(); ++whichStruct) { VolumeFile myVol, myRoi, myVolOut; int64_t offset[3]; AlgorithmCiftiSeparate(NULL, myCifti, myDir, volumeList[whichStruct], &myVol, offset, &myRoi, true); if (volKern > 0.0f) { if (roiCifti != NULL) {//due to above testing, we know the structure mask is the same, so just overwrite the ROI from the mask AlgorithmCiftiSeparate(NULL, roiCifti, CiftiXMLOld::ALONG_COLUMN, volumeList[whichStruct], &myRoi, offset, NULL, true); } AlgorithmVolumeSmoothing(NULL, &myVol, volKern, &myVolOut, &myRoi, fixZerosVol); AlgorithmCiftiReplaceStructure(NULL, myCiftiOut, myDir, volumeList[whichStruct], &myVolOut, true); } else { AlgorithmCiftiReplaceStructure(NULL, myCiftiOut, myDir, volumeList[whichStruct], &myVol, true); } } } } float AlgorithmCiftiSmoothing::getAlgorithmInternalWeight() { return 1.0f;//override this if needed, if the progress bar isn't smooth } float AlgorithmCiftiSmoothing::getSubAlgorithmWeight() { return 0.0f;//if you use a subalgorithm } connectome-workbench-1.2.3+git41-gc4c6c90/src/Algorithms/AlgorithmCiftiSmoothing.h000066400000000000000000000042211300200146000276220ustar00rootroot00000000000000#ifndef __ALGORITHM_CIFTI_SMOOTHING_H__ #define __ALGORITHM_CIFTI_SMOOTHING_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 "AbstractAlgorithm.h" namespace caret { class AlgorithmCiftiSmoothing : public AbstractAlgorithm { AlgorithmCiftiSmoothing(); protected: static float getSubAlgorithmWeight(); static float getAlgorithmInternalWeight(); public: AlgorithmCiftiSmoothing(ProgressObject* myProgObj, const CiftiFile* myCifti, const float& surfKern, const float& volKern, const int& myDir, CiftiFile* myCiftiOut, const SurfaceFile* myLeftSurf = NULL, const SurfaceFile* myRightSurf = NULL, const SurfaceFile* myCerebSurf = NULL, const CiftiFile* roiCifti = NULL, bool fixZerosVol = false, bool fixZerosSurf = false, const MetricFile* myLeftAreas = NULL, const MetricFile* myRightAreas = NULL, const MetricFile* myCerebAreas = NULL, const bool& mergedVolume = false); static OperationParameters* getParameters(); static void useParameters(OperationParameters* myParams, ProgressObject* myProgObj); static AString getCommandSwitch(); static AString getShortDescription(); }; typedef TemplateAutoOperation AutoAlgorithmCiftiSmoothing; } #endif //__ALGORITHM_CIFTI_SMOOTHING_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Algorithms/AlgorithmCiftiTranspose.cxx000066400000000000000000000105231300200146000302060ustar00rootroot00000000000000/*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 "AlgorithmCiftiTranspose.h" #include "AlgorithmException.h" #include "CiftiFile.h" using namespace caret; using namespace std; AString AlgorithmCiftiTranspose::getCommandSwitch() { return "-cifti-transpose"; } AString AlgorithmCiftiTranspose::getShortDescription() { return "TRANSPOSE A CIFTI FILE"; } OperationParameters* AlgorithmCiftiTranspose::getParameters() { OperationParameters* ret = new OperationParameters(); ret->addCiftiParameter(1, "cifti-in", "the input cifti file"); ret->addCiftiOutputParameter(2, "cifti-out", "the output cifti file"); OptionalParameter* memLimitOpt = ret->createOptionalParameter(3, "-mem-limit", "restrict memory usage"); memLimitOpt->addDoubleParameter(1, "limit-GB", "memory limit in gigabytes"); ret->setHelpText( AString("The input must be a 2-dimensional cifti file. ") + "The output is a cifti file where every row in the input is a column in the output." ); return ret; } void AlgorithmCiftiTranspose::useParameters(OperationParameters* myParams, ProgressObject* myProgObj) { CiftiFile* ciftiIn = myParams->getCifti(1); CiftiFile* ciftiOut = myParams->getOutputCifti(2); OptionalParameter* memLimitOpt = myParams->getOptionalParameter(3); float memLimitGB = -1.0f; if (memLimitOpt->m_present) { memLimitGB = (float)memLimitOpt->getDouble(1); if (memLimitGB < 0.0f) { throw AlgorithmException("memory limit cannot be negative"); } } AlgorithmCiftiTranspose(myProgObj, ciftiIn, ciftiOut, memLimitGB); } AlgorithmCiftiTranspose::AlgorithmCiftiTranspose(ProgressObject* myProgObj, const CiftiFile* ciftiIn, CiftiFile* ciftiOut, const float& memLimitGB) : AbstractAlgorithm(myProgObj) { LevelProgress myProgress(myProgObj); const CiftiXML& inXML = ciftiIn->getCiftiXML(); if (inXML.getNumberOfDimensions() != 2) { throw AlgorithmException("cifti transpose only supports 2D cifti"); } CiftiXML outXML; outXML.setNumberOfDimensions(2); outXML.setMap(0, *(inXML.getMap(1))); outXML.setMap(1, *(inXML.getMap(0))); ciftiOut->setCiftiXML(outXML); int rowSize = outXML.getDimensionLength(CiftiXML::ALONG_ROW), colSize = outXML.getDimensionLength(CiftiXML::ALONG_COLUMN); int64_t outRowBytes = rowSize * sizeof(float); int numCacheRows = colSize; if (memLimitGB >= 0.0f) { numCacheRows = memLimitGB * 1024 * 1024 * 1024 / outRowBytes; if (numCacheRows < 1) numCacheRows = 1; if (numCacheRows > colSize) numCacheRows = colSize; } vector > cacheRows(numCacheRows, vector(rowSize)); vector scratchInRow(colSize); for (int i = 0; i < colSize; i += numCacheRows)//loop through cache chunks { int end = i + numCacheRows; if (end > colSize) end = colSize; for (int j = 0; j < rowSize; ++j)//loop through all input rows { ciftiIn->getRow(scratchInRow.data(), j); for (int k = i; k < end; ++k) { cacheRows[k - i][j] = scratchInRow[k]; } } for (int k = i; k < end; ++k) { ciftiOut->setRow(cacheRows[k - i].data(), k); } } } float AlgorithmCiftiTranspose::getAlgorithmInternalWeight() { return 1.0f;//override this if needed, if the progress bar isn't smooth } float AlgorithmCiftiTranspose::getSubAlgorithmWeight() { //return AlgorithmInsertNameHere::getAlgorithmWeight();//if you use a subalgorithm return 0.0f; } connectome-workbench-1.2.3+git41-gc4c6c90/src/Algorithms/AlgorithmCiftiTranspose.h000066400000000000000000000032641300200146000276370ustar00rootroot00000000000000#ifndef __ALGORITHM_CIFTI_TRANSPOSE_H__ #define __ALGORITHM_CIFTI_TRANSPOSE_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 "AbstractAlgorithm.h" namespace caret { class AlgorithmCiftiTranspose : public AbstractAlgorithm { AlgorithmCiftiTranspose(); protected: static float getSubAlgorithmWeight(); static float getAlgorithmInternalWeight(); public: AlgorithmCiftiTranspose(ProgressObject* myProgObj, const CiftiFile* ciftiIn, CiftiFile* ciftiOut, const float& memLimitGB = -1.0f); static OperationParameters* getParameters(); static void useParameters(OperationParameters* myParams, ProgressObject* myProgObj); static AString getCommandSwitch(); static AString getShortDescription(); }; typedef TemplateAutoOperation AutoAlgorithmCiftiTranspose; } #endif //__ALGORITHM_CIFTI_TRANSPOSE_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Algorithms/AlgorithmCiftiVectorOperation.cxx000066400000000000000000000172171300200146000313620ustar00rootroot00000000000000/*LICENSE_START*/ /* * Copyright (C) 2015 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 "AlgorithmCiftiVectorOperation.h" #include "AlgorithmException.h" #include "CiftiFile.h" using namespace caret; using namespace std; AString AlgorithmCiftiVectorOperation::getCommandSwitch() { return "-cifti-vector-operation"; } AString AlgorithmCiftiVectorOperation::getShortDescription() { return "DO A VECTOR OPERATION ON CIFTI FILES"; } OperationParameters* AlgorithmCiftiVectorOperation::getParameters() { OperationParameters* ret = new OperationParameters(); ret->addCiftiParameter(1, "vectors-a", "first vector input file"); ret->addCiftiParameter(2, "vectors-b", "second vector input file"); ret->addStringParameter(3, "operation", "what vector operation to do"); ret->addCiftiOutputParameter(4, "cifti-out", "the output file"); ret->createOptionalParameter(5, "-normalize-a", "normalize vectors of first input"); ret->createOptionalParameter(6, "-normalize-b", "normalize vectors of second input"); ret->createOptionalParameter(7, "-normalize-output", "normalize output vectors (not valid for dot product)"); ret->createOptionalParameter(8, "-magnitude", "output the magnitude of the result (not valid for dot product)"); AString myText = AString("Does a vector operation on two cifti files (that must have a multiple of 3 columns). ") + "Either of the inputs may have multiple vectors (more than 3 columns), but not both (at least one must have exactly 3 columns). " + "The -magnitude and -normalize-output options may not be specified together, or with an operation that returns a scalar (dot product). " + "The parameter must be one of the following:\n"; vector opList = VectorOperation::getAllOperations(); for (int i = 0; i < (int)opList.size(); ++i) { myText += "\n" + VectorOperation::operationToString(opList[i]); } ret->setHelpText(myText); return ret; } void AlgorithmCiftiVectorOperation::useParameters(OperationParameters* myParams, ProgressObject* myProgObj) { CiftiFile* ciftiA = myParams->getCifti(1); CiftiFile* ciftiB = myParams->getCifti(2); AString operString = myParams->getString(3); bool ok = false; VectorOperation::Operation myOper = VectorOperation::stringToOperation(operString, ok); if (!ok) throw AlgorithmException("unrecognized operation string: " + operString); CiftiFile* myCiftiOut = myParams->getOutputCifti(4); bool normA = myParams->getOptionalParameter(5)->m_present; bool normB = myParams->getOptionalParameter(6)->m_present; bool normOut = myParams->getOptionalParameter(7)->m_present; bool magOut = myParams->getOptionalParameter(8)->m_present; AlgorithmCiftiVectorOperation(myProgObj, ciftiA, ciftiB, myOper, myCiftiOut, normA, normB, normOut, magOut); } AlgorithmCiftiVectorOperation::AlgorithmCiftiVectorOperation(ProgressObject* myProgObj, const CiftiFile* ciftiA, const CiftiFile* ciftiB, const VectorOperation::Operation& myOper, CiftiFile* myCiftiOut, const bool& normA, const bool& normB, const bool& normOut, const bool& magOut) : AbstractAlgorithm(myProgObj) { LevelProgress myProgress(myProgObj); const CiftiXML& xmlA = ciftiA->getCiftiXML(), &xmlB = ciftiB->getCiftiXML(); if (xmlA.getNumberOfDimensions() != 2 || xmlB.getNumberOfDimensions() != 2) { throw AlgorithmException("cifti vector operation only supports 2D cifti"); } if (!xmlA.getMap(CiftiXML::ALONG_COLUMN)->approximateMatch(*xmlB.getMap(CiftiXML::ALONG_COLUMN))) { throw AlgorithmException("input cifti files have non-matching mappings along column"); } int64_t numColA = xmlA.getDimensionLength(CiftiXML::ALONG_ROW), numColB = xmlB.getDimensionLength(CiftiXML::ALONG_ROW); if (numColA % 3 != 0) throw AlgorithmException("number of columns of first input is not a multiple of 3"); if (numColB % 3 != 0) throw AlgorithmException("number of columns of second input is not a multiple of 3"); int numVecA = numColA / 3, numVecB = numColB / 3; if (numVecA > 1 && numVecB > 1) throw AlgorithmException("both inputs have more than 3 columns (more than 1 vector)"); if (normOut && magOut) throw AlgorithmException("normalizing the output and taking the magnitude is meaningless"); bool opScalarResult = VectorOperation::operationReturnsScalar(myOper); if (opScalarResult && (normOut || magOut)) throw AlgorithmException("cannot normalize or take magnitude of a scalar result (such as a dot product)"); bool swapped = false; const CiftiFile* multiVec = ciftiA, *singleVec = ciftiB; int numOutVecs = numVecA; if (numVecB > 1) { multiVec = ciftiB; singleVec = ciftiA; numOutVecs = numVecB; swapped = true; } CiftiXML outXML = multiVec->getCiftiXML(); int numColsOut = numOutVecs * 3; if (opScalarResult || magOut) { numColsOut = numOutVecs; CiftiScalarsMap outRowMap; outRowMap.setLength(numColsOut); outXML.setMap(CiftiXML::ALONG_ROW, outRowMap); } myCiftiOut->setCiftiXML(outXML); vector outRow(numColsOut), multiRow(numOutVecs * 3); int64_t numRows = xmlA.getDimensionLength(CiftiXML::ALONG_COLUMN); for (int64_t row = 0; row < numRows; ++row) { multiVec->getRow(multiRow.data(), row); Vector3D vecSingle; singleVec->getRow(vecSingle, row); for (int64_t v = 0; v < numOutVecs; ++v) { Vector3D vecA, vecB; if (swapped) { vecA = multiRow.data() + v * 3; vecB = vecSingle; } else { vecA = vecSingle; vecB = multiRow.data() + v * 3; } if (normA) vecA = vecA.normal(); if (normB) vecB = vecB.normal(); if (opScalarResult) { outRow[v] = VectorOperation::doScalarOperation(vecA, vecB, myOper); } else { Vector3D tempVec = VectorOperation::doVectorOperation(vecA, vecB, myOper); if (normOut) tempVec = tempVec.normal(); if (magOut) { outRow[v] = tempVec.length(); } else { outRow[v * 3] = tempVec[0]; outRow[v * 3 + 1] = tempVec[1]; outRow[v * 3 + 2] = tempVec[2]; } } } myCiftiOut->setRow(outRow.data(), row); } } float AlgorithmCiftiVectorOperation::getAlgorithmInternalWeight() { return 1.0f;//override this if needed, if the progress bar isn't smooth } float AlgorithmCiftiVectorOperation::getSubAlgorithmWeight() { //return AlgorithmInsertNameHere::getAlgorithmWeight();//if you use a subalgorithm return 0.0f; } connectome-workbench-1.2.3+git41-gc4c6c90/src/Algorithms/AlgorithmCiftiVectorOperation.h000066400000000000000000000037431300200146000310060ustar00rootroot00000000000000#ifndef __ALGORITHM_CIFTI_VECTOR_OPERATION_H__ #define __ALGORITHM_CIFTI_VECTOR_OPERATION_H__ /*LICENSE_START*/ /* * Copyright (C) 2015 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 "VectorOperation.h" namespace caret { class AlgorithmCiftiVectorOperation : public AbstractAlgorithm { AlgorithmCiftiVectorOperation(); protected: static float getSubAlgorithmWeight(); static float getAlgorithmInternalWeight(); public: AlgorithmCiftiVectorOperation(ProgressObject* myProgObj, const CiftiFile* ciftiA, const CiftiFile* ciftiB, const VectorOperation::Operation& myOper, CiftiFile* myCiftiOut, const bool& normA = false, const bool& normB = false, const bool& normOut = false, const bool& magOut = false); static OperationParameters* getParameters(); static void useParameters(OperationParameters* myParams, ProgressObject* myProgObj); static AString getCommandSwitch(); static AString getShortDescription(); }; typedef TemplateAutoOperation AutoAlgorithmCiftiVectorOperation; } #endif //__ALGORITHM_CIFTI_VECTOR_OPERATION_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Algorithms/AlgorithmCreateSignedDistanceVolume.cxx000066400000000000000000000636231300200146000324620ustar00rootroot00000000000000/*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 "AlgorithmCreateSignedDistanceVolume.h" #include "AlgorithmException.h" #include "VolumeFile.h" #include "CaretOMP.h" #include "CaretHeap.h" #include "MathFunctions.h" #include "SurfaceFile.h" #include #include #include using namespace caret; using namespace std; AString AlgorithmCreateSignedDistanceVolume::getCommandSwitch() { return "-create-signed-distance-volume"; } AString AlgorithmCreateSignedDistanceVolume::getShortDescription() { return "CREATE SIGNED DISTANCE VOLUME FROM SURFACE"; } OperationParameters* AlgorithmCreateSignedDistanceVolume::getParameters() { OperationParameters* ret = new OperationParameters(); ret->addSurfaceParameter(1, "surface", "the input surface"); ret->addStringParameter(2, "refspace", "a volume in the desired output space (dims, spacing, origin)"); ret->addVolumeOutputParameter(3, "outvol", "the output volume"); OptionalParameter* roiOutOpt = ret->createOptionalParameter(9, "-roi-out", "output an roi volume of where the output has a computed value"); roiOutOpt->addVolumeOutputParameter(1, "roi-vol", "the output roi volume"); OptionalParameter* fillValOpt = ret->createOptionalParameter(4, "-fill-value", "specify a value to put in all voxels that don't get assigned a distance"); fillValOpt->addDoubleParameter(1, "value", "value to fill with (default 0)"); OptionalParameter* exactDistOpt = ret->createOptionalParameter(5, "-exact-limit", "specify distance for exact output"); exactDistOpt->addDoubleParameter(1, "dist", "distance in mm (default 5)"); OptionalParameter* approxDistOpt = ret->createOptionalParameter(6, "-approx-limit", "specify distance for approximate output"); approxDistOpt->addDoubleParameter(1, "dist", "distance in mm (default 20)"); OptionalParameter* approxNeighborhoodOpt = ret->createOptionalParameter(7, "-approx-neighborhood", "voxel neighborhood for approximate calculation"); approxNeighborhoodOpt->addIntegerParameter(1, "num", "size of neighborhood cube measured from center to face, in voxels (default 2 = 5x5x5)"); OptionalParameter* windingMethodOpt = ret->createOptionalParameter(8, "-winding", "winding method for point inside surface test"); windingMethodOpt->addStringParameter(1, "method", "name of the method (default EVEN_ODD)"); ret->setHelpText( AString("Computes the signed distance function of the surface. Exact distance is calculated by finding the closest point on any surface triangle ") + "to the center of the voxel. Approximate distance is calculated starting with these distances, using dijkstra's method with a neighborhood of voxels. " + "Specifying too small of an exact distance may produce unexpected results. Valid specifiers for winding methods are as follows:\n\n" + "EVEN_ODD (default)\nNEGATIVE\nNONZERO\nNORMALS\n\nThe NORMALS method uses the normals of triangles and edges, or the closest triangle hit by a ray from the point. " + "This method may be slightly faster, but is only reliable for a closed surface that does not cross through itself. All other methods count entry (positive) and " + "exit (negative) crossings of a vertical ray from the point, then counts as inside if the total is odd, negative, or nonzero, respectively." ); return ret; } void AlgorithmCreateSignedDistanceVolume::useParameters(OperationParameters* myParams, ProgressObject* myProgObj) { SurfaceFile* mySurf = myParams->getSurface(1); AString myRefName = myParams->getString(2); vector > volSpace; vector volDims; { VolumeFile myRefSpace; myRefSpace.readFile(myRefName); volSpace = myRefSpace.getSform(); myRefSpace.getDimensions(volDims); } volDims.resize(3); VolumeFile* myVolOut = myParams->getOutputVolume(3); myVolOut->reinitialize(volDims, volSpace); float fillValue = 0.0f; OptionalParameter* fillValOpt = myParams->getOptionalParameter(4); if (fillValOpt->m_present) { fillValue = (float)fillValOpt->getDouble(1); } float exactLim = 5.0f; OptionalParameter* exactDistOpt = myParams->getOptionalParameter(5); if (exactDistOpt->m_present) { exactLim = (float)exactDistOpt->getDouble(1); } float approxLim = 20.0f; OptionalParameter* approxDistOpt = myParams->getOptionalParameter(6); if (approxDistOpt->m_present) { approxLim = (float)approxDistOpt->getDouble(1);//don't sanity check it, less than exact limit simply turns it off, specify extremely large to do entire volume } int approxNeighborhood = 2; OptionalParameter* approxNeighborhoodOpt = myParams->getOptionalParameter(7); if (approxNeighborhoodOpt->m_present) { approxNeighborhood = (int)approxNeighborhoodOpt->getInteger(1); } SignedDistanceHelper::WindingLogic myWinding = SignedDistanceHelper::EVEN_ODD; OptionalParameter* windingMethodOpt = myParams->getOptionalParameter(8); if (windingMethodOpt->m_present) { AString methodName = windingMethodOpt->getString(1); if (methodName == "EVEN_ODD") { myWinding = SignedDistanceHelper::EVEN_ODD; } else if (methodName == "NEGATIVE") { myWinding = SignedDistanceHelper::NEGATIVE; } else if (methodName == "NONZERO") { myWinding = SignedDistanceHelper::NONZERO; } else if (methodName == "NORMALS") { myWinding = SignedDistanceHelper::NORMALS; } else { throw AlgorithmException("unrecognized winding method"); } } VolumeFile* myRoiOut = NULL; OptionalParameter* roiOutOpt = myParams->getOptionalParameter(9); if (roiOutOpt->m_present) { myRoiOut = roiOutOpt->getOutputVolume(1); } AlgorithmCreateSignedDistanceVolume(myProgObj, mySurf, myVolOut, myRoiOut, fillValue, exactLim, approxLim, approxNeighborhood, myWinding); } AlgorithmCreateSignedDistanceVolume::AlgorithmCreateSignedDistanceVolume(ProgressObject* myProgObj, const SurfaceFile* mySurf, VolumeFile* myVolOut, VolumeFile* myRoiOut, const float& fillValue, const float& exactLim, const float& approxLim, const int& approxNeighborhood, const SignedDistanceHelper::WindingLogic& myWinding) : AbstractAlgorithm(myProgObj) { if (exactLim <= 0.0f) { throw AlgorithmException("exact limit must be positive"); } if (approxNeighborhood < 1) { throw AlgorithmException("approximate neighborhood must be at least 1"); } int32_t numNodes = mySurf->getNumberOfNodes(); float markweight = 0.1f, exactweight = 5.0f * exactLim, approxweight = 0.2f * (approxLim - exactLim); if (approxweight < 0.0f) approxweight = 0.0f; LevelProgress myProgress(myProgObj, markweight + exactweight + approxweight); vector > myVolSpace; myVolSpace = myVolOut->getSform(); Vector3D ivec, jvec, kvec; ivec[0] = myVolSpace[0][0]; ivec[1] = myVolSpace[1][0]; ivec[2] = myVolSpace[2][0]; jvec[0] = myVolSpace[0][1]; jvec[1] = myVolSpace[1][1]; jvec[2] = myVolSpace[2][1]; kvec[0] = myVolSpace[0][2]; kvec[1] = myVolSpace[1][2]; kvec[2] = myVolSpace[2][2]; Vector3D iOrthHat = jvec.cross(kvec);//these "orth" vectors are used to find the index extremes of a sphere, adding any amount of the other index vectors increases their length iOrthHat = iOrthHat.normal(); if (iOrthHat.dot(ivec) < 0) iOrthHat = -iOrthHat;//make sure it lies with rather than against the i vector Vector3D jOrthHat = ivec.cross(kvec); jOrthHat = jOrthHat.normal(); if (jOrthHat.dot(jvec) < 0) jOrthHat = -jOrthHat; Vector3D kOrthHat = ivec.cross(jvec); kOrthHat = kOrthHat.normal(); if (kOrthHat.dot(kvec) < 0) kOrthHat = -kOrthHat; vector myDims; myVolOut->getDimensions(myDims); myVolOut->setValueAllVoxels(fillValue); //list all voxels to be exactly computed int64_t frameSize = myDims[0] * myDims[1] * myDims[2]; CaretArray volMarked(frameSize, 0); myProgress.setTask("marking voxel to be calculated exactly"); //compare expected runtimes of kernel based and locator based marking methods if (2.9 * myDims[0] * myDims[1] * myDims[2] < (numNodes * exactLim * exactLim * exactLim / iOrthHat.dot(ivec) / jOrthHat.dot(jvec) / kOrthHat.dot(kvec))) { #pragma omp CARET_PARFOR schedule(dynamic) for (int64_t k = 0; k < myDims[2]; ++k) { for (int64_t j = 0; j < myDims[1]; ++j) { for (int64_t i = 0; i < myDims[0]; ++i) { Vector3D voxCoord; myVolOut->indexToSpace(i, j, k, voxCoord); int32_t ret = mySurf->closestNode(voxCoord, exactLim); if (ret != -1) { volMarked[myVolOut->getIndex(i, j, k)] = 1; } } } } } else { #pragma omp CARET_PARFOR schedule(dynamic) for (int node = 0; node < numNodes; ++node) { int64_t ijk[3]; Vector3D nodeCoord = mySurf->getCoordinate(node), tempvec; float tempf, tempf2, tempf3; tempvec = nodeCoord - iOrthHat * exactLim; myVolOut->spaceToIndex(tempvec, tempf, tempf2, tempf3);//compute bounding box once rather than doing a convoluted sphere loop construct int64_t imin = (int64_t)ceil(tempf); if (imin < 0) imin = 0; tempvec = nodeCoord + iOrthHat * exactLim; myVolOut->spaceToIndex(tempvec, tempf, tempf2, tempf3); int64_t imax = (int64_t)floor(tempf) + 1; if (imax > myDims[0]) imax = myDims[0]; tempvec = nodeCoord - jOrthHat * exactLim; myVolOut->spaceToIndex(tempvec, tempf, tempf2, tempf3); int64_t jmin = (int64_t)ceil(tempf2); if (jmin < 0) jmin = 0; tempvec = nodeCoord + jOrthHat * exactLim; myVolOut->spaceToIndex(tempvec, tempf, tempf2, tempf3); int64_t jmax = (int64_t)floor(tempf2) + 1; if (jmax > myDims[1]) jmax = myDims[1]; tempvec = nodeCoord - kOrthHat * exactLim; myVolOut->spaceToIndex(tempvec, tempf, tempf2, tempf3); int64_t kmin = (int64_t)ceil(tempf3); if (kmin < 0) kmin = 0; tempvec = nodeCoord + kOrthHat * exactLim; myVolOut->spaceToIndex(tempvec, tempf, tempf2, tempf3); int64_t kmax = (int64_t)floor(tempf3) + 1; if (kmax > myDims[2]) kmax = myDims[2]; for (ijk[2] = kmin; ijk[2] < kmax; ++ijk[2]) { for (ijk[1] = jmin; ijk[1] < jmax; ++ijk[1]) { for (ijk[0] = imin; ijk[0] < imax; ++ijk[0]) { myVolOut->indexToSpace(ijk, tempvec); tempvec -= nodeCoord; if (tempvec.length() <= exactLim) { volMarked[myVolOut->getIndex(ijk)] = 1; } } } } } } vector exactVoxelList; int64_t ijk[3]; for (ijk[2] = 0; ijk[2] < myDims[2]; ++ijk[2]) { for (ijk[1] = 0; ijk[1] < myDims[1]; ++ijk[1]) { for (ijk[0] = 0; ijk[0] < myDims[0]; ++ijk[0]) { if (volMarked[myVolOut->getIndex(ijk)] == 1) { exactVoxelList.push_back(ijk[0]); exactVoxelList.push_back(ijk[1]); exactVoxelList.push_back(ijk[2]); } } } } myProgress.reportProgress(markweight); myProgress.setTask("computing exact distances"); #pragma omp CARET_PAR { CaretPointer myDist = mySurf->getSignedDistanceHelper(); int numExact = (int)exactVoxelList.size(); Vector3D thisCoord; #pragma omp CARET_FOR schedule(dynamic) for (int i = 0; i < numExact; i += 3) { myVolOut->indexToSpace(exactVoxelList.data() + i, thisCoord); myVolOut->setValue(myDist->dist(thisCoord, myWinding), exactVoxelList.data() + i); volMarked[myVolOut->getIndex(exactVoxelList.data() + i)] |= 22;//set marked to have valid value (positive and negative), and frozen } } myProgress.reportProgress(markweight + exactweight); if (approxLim > exactLim) { myProgress.setTask("approximating distances in extended region"); int faceNeigh[] = { 1, 0, 0, -1, 0, 0, 0, 1, 0, 0, -1, 0, 0, 0, 1, 0, 0, -1 }; vector neighborhood;//this will contain ONLY the shortest voxel offsets with unique 3d slopes within the neighborhood DistVoxOffset tempIndex; Vector3D tempvec; for (int i = -approxNeighborhood; i <= approxNeighborhood; ++i) { tempIndex.m_offset[0] = i; for (int j = -approxNeighborhood; j <= approxNeighborhood; ++j) { tempIndex.m_offset[1] = j; for (int k = -approxNeighborhood; k <= approxNeighborhood; ++k) { tempIndex.m_offset[2] = k; tempvec = ivec * i + jvec * j + kvec * k; tempIndex.m_dist = tempvec.length(); int low, med, high; low = min(min(abs(i), abs(j)), abs(k));//stupid sort high = max(max(abs(i), abs(j)), abs(k)); if (abs(i) != low && abs(i) != high) { med = abs(i); } else if (abs(j) != low && abs(j) != high) { med = abs(j); } else { med = abs(k); } if (low == 0) { if (med == 0) { if (high == 1) { neighborhood.push_back(tempIndex);//face neighbors } } else { if (MathFunctions::gcd(med, high) == 1) { neighborhood.push_back(tempIndex);//unique in-plane } } } else { if (MathFunctions::gcd(MathFunctions::gcd(low, med), high) == 1) { neighborhood.push_back(tempIndex);//unique out of plane } } } } }//positives float maxFaceDist = max(max(ivec.length(), jvec.length()), kvec.length()) * 1.01f;//add a fudge factor to make sure rounding error doesn't remove a cardinal direction int neighSize = neighborhood.size();//this is provably correct for volumes where there is no diagonal shorter than the longest index vector, so we test this explicitly just in case CaretMinHeap posHeap; CaretArray heapIndexes(frameSize); int numExact = (int)exactVoxelList.size(); for (int i = 0; i < numExact; i += 3) { int64_t* thisVoxel = exactVoxelList.data() + i; float tempf = myVolOut->getValue(thisVoxel); for (int neigh = 0; neigh < neighSize; ++neigh) { int64_t tempijk[3]; tempijk[0] = thisVoxel[0] + neighborhood[neigh].m_offset[0]; tempijk[1] = thisVoxel[1] + neighborhood[neigh].m_offset[1]; tempijk[2] = thisVoxel[2] + neighborhood[neigh].m_offset[2]; if (myVolOut->indexValid(tempijk)) { int64_t tempindex = myVolOut->getIndex(tempijk); float tempf2 = tempf + neighborhood[neigh].m_dist; if (abs(tempf2) <= approxLim && (volMarked[tempindex] & 4) == 0 && ((volMarked[tempindex] & 2) == 0 || tempf2 < myVolOut->getValue(tempijk))) {//within approxlim (so no stragglers outside limit), not frozen, and either no value or worse value volMarked[tempindex] |= 2; myVolOut->setValue(tempf2, tempijk); } } } if (tempf > 0.0f) {//start only from positive values //check face neighbors for being unmarked for (int neigh = 0; neigh < 18; neigh += 3) { int64_t tempijk[3]; tempijk[0] = thisVoxel[0] + faceNeigh[neigh]; tempijk[1] = thisVoxel[1] + faceNeigh[neigh + 1]; tempijk[2] = thisVoxel[2] + faceNeigh[neigh + 2]; if (myVolOut->indexValid(tempijk)) { int64_t tempIndex = myVolOut->getIndex(tempijk); if ((volMarked[tempIndex] & 1) == 0) {//only add this to the heap if it has unmarked face neighbors posHeap.push(VoxelIndex(thisVoxel), tempf);//don't need to store the index to change the key, value is frozen break; } } } } }//initialization done, now (sort of) dijkstras - in order to not go to the "inside" (and to run faster), it only adds things that are FACE neighbors to the heap for cubic voxels while (!posHeap.isEmpty()) { float curDist; VoxelIndex curVoxel = posHeap.pop(&curDist); int64_t curIndex = myVolOut->getIndex(curVoxel.m_ijk); volMarked[curIndex] |= 4;//frozen volMarked[curIndex] &= ~8;//no longer in the heap, don't try to modify by the index it used to have for (int neigh = 0; neigh < neighSize; ++neigh) { int64_t tempijk[3]; tempijk[0] = curVoxel.m_ijk[0] + neighborhood[neigh].m_offset[0]; tempijk[1] = curVoxel.m_ijk[1] + neighborhood[neigh].m_offset[1]; tempijk[2] = curVoxel.m_ijk[2] + neighborhood[neigh].m_offset[2]; if (myVolOut->indexValid(tempijk)) { float tempf = curDist + neighborhood[neigh].m_dist; int tempindex = myVolOut->getIndex(tempijk); int& tempmark = volMarked[tempindex]; if (abs(tempf) <= approxLim && (tempmark & 4) == 0 && ((tempmark & 2) == 0 || myVolOut->getValue(tempijk) > tempf)) {//within range, not frozen, no value or current value is worse tempmark |= 2;//valid value myVolOut->setValue(tempf, tempijk); if ((tempmark & 8) != 0)//if it is already in the heap, we must update its key (log time worst case, but changes should generally be small) { posHeap.changekey(heapIndexes[tempindex], tempf); } } if ((tempmark & 12) == 0 && (tempmark & 2) != 0 && neighborhood[neigh].m_dist <= maxFaceDist) {//this neatly handles both face neighbors and any other needed neighbors to maintain dijkstra correctness under extreme scenarios heapIndexes[tempindex] = posHeap.push(VoxelIndex(tempijk), myVolOut->getValue(tempijk)); tempmark |= 8;//has a heap index } } } }//negatives myProgress.reportProgress(markweight + exactweight + approxweight * 0.5f); CaretMaxHeap negHeap; for (int i = 0; i < numExact; i += 3) { int64_t* thisVoxel = exactVoxelList.data() + i; float tempf = myVolOut->getValue(thisVoxel); for (int neigh = 0; neigh < neighSize; ++neigh) { int64_t tempijk[3]; tempijk[0] = thisVoxel[0] + neighborhood[neigh].m_offset[0]; tempijk[1] = thisVoxel[1] + neighborhood[neigh].m_offset[1]; tempijk[2] = thisVoxel[2] + neighborhood[neigh].m_offset[2]; if (myVolOut->indexValid(tempijk)) { int64_t tempindex = myVolOut->getIndex(tempijk); float tempf2 = tempf - neighborhood[neigh].m_dist; if (abs(tempf2) <= approxLim && (volMarked[tempindex] & 4) == 0 && ((volMarked[tempindex] & 16) == 0 || tempf2 > myVolOut->getValue(tempijk))) {//within approxlim (so no stragglers outside limit), not frozen, and either no value or worse value volMarked[tempindex] |= 16; myVolOut->setValue(tempf2, tempijk); } } } if (tempf < 0.0f) {//start only from negative values //check face neighbors for being unmarked for (int neigh = 0; neigh < 18; neigh += 3) { int64_t tempijk[3]; tempijk[0] = thisVoxel[0] + faceNeigh[neigh]; tempijk[1] = thisVoxel[1] + faceNeigh[neigh + 1]; tempijk[2] = thisVoxel[2] + faceNeigh[neigh + 2]; if (myVolOut->indexValid(tempijk)) { int64_t tempIndex = myVolOut->getIndex(tempijk); if ((volMarked[tempIndex] & 1) == 0) {//only add this to the heap if it has unmarked face neighbors negHeap.push(VoxelIndex(thisVoxel), tempf);//don't need to store the index to change the key, value is frozen break; } } } } } while (!negHeap.isEmpty()) { float curDist; VoxelIndex curVoxel = negHeap.pop(&curDist); int64_t curIndex = myVolOut->getIndex(curVoxel.m_ijk); volMarked[curIndex] |= 4;//frozen volMarked[curIndex] &= ~8;//no longer in the heap, don't try to modify by the index it used to have for (int neigh = 0; neigh < neighSize; ++neigh) { int64_t tempijk[3]; tempijk[0] = curVoxel.m_ijk[0] + neighborhood[neigh].m_offset[0]; tempijk[1] = curVoxel.m_ijk[1] + neighborhood[neigh].m_offset[1]; tempijk[2] = curVoxel.m_ijk[2] + neighborhood[neigh].m_offset[2]; if (myVolOut->indexValid(tempijk)) { float tempf = curDist - neighborhood[neigh].m_dist; int tempindex = myVolOut->getIndex(tempijk); int& tempmark = volMarked[tempindex]; if (abs(tempf) <= approxLim && (tempmark & 4) == 0 && ((tempmark & 16) == 0 || myVolOut->getValue(tempijk) < tempf)) {//within range, not frozen, no value or current value is worse tempmark |= 16;//valid value myVolOut->setValue(tempf, tempijk); if ((tempmark & 8) != 0)//if it is already in the heap, we must update its key (log time worst case, but changes should generally be small) { negHeap.changekey(heapIndexes[tempindex], tempf); } } if ((tempmark & 12) == 0 && (tempmark & 16) != 0 && neighborhood[neigh].m_dist <= maxFaceDist) {//this neatly handles both face neighbors and any other needed neighbors to maintain dijkstra correctness under extreme scenarios heapIndexes[tempindex] = negHeap.push(VoxelIndex(tempijk), myVolOut->getValue(tempijk)); tempmark |= 8;//has a heap index } } } } }//now make the roi volume if (myRoiOut != NULL) { myDims.resize(3); myRoiOut->reinitialize(myDims, myVolOut->getSform()); for (ijk[2] = 0; ijk[2] < myDims[2]; ++ijk[2]) { for (ijk[1] = 0; ijk[1] < myDims[1]; ++ijk[1]) { for (ijk[0] = 0; ijk[0] < myDims[0]; ++ijk[0]) { if ((volMarked[myVolOut->getIndex(ijk)] & 4) == 0)//only mark "frozen" (4) as valid, though "have value" (2) should now be the same { myRoiOut->setValue(0.0f, ijk); } else { myRoiOut->setValue(1.0f, ijk); } } } } } } float AlgorithmCreateSignedDistanceVolume::getAlgorithmInternalWeight() { return 1.0f;//override this if needed, if the progress bar isn't smooth } float AlgorithmCreateSignedDistanceVolume::getSubAlgorithmWeight() { //return AlgorithmInsertNameHere::getAlgorithmWeight();//if you use a subalgorithm return 0.0f; } connectome-workbench-1.2.3+git41-gc4c6c90/src/Algorithms/AlgorithmCreateSignedDistanceVolume.h000066400000000000000000000050331300200146000320760ustar00rootroot00000000000000#ifndef __ALGORITHM_CREATE_SIGNED_DISTANCE_VOLUME_H__ #define __ALGORITHM_CREATE_SIGNED_DISTANCE_VOLUME_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 "AbstractAlgorithm.h" #include "Vector3D.h" #include "CaretMutex.h" #include "OctTree.h" #include "SignedDistanceHelper.h" #include namespace caret { struct DistVoxOffset { float m_dist; int m_offset[3]; }; struct VoxelIndex {//needed in order to contain the data in a heap, operator= doesn't work on a static array VoxelIndex(int64_t ijk[3]) { m_ijk[0] = ijk[0]; m_ijk[1] = ijk[1]; m_ijk[2] = ijk[2]; } int64_t m_ijk[3]; }; class AlgorithmCreateSignedDistanceVolume : public AbstractAlgorithm { AlgorithmCreateSignedDistanceVolume(); protected: static float getSubAlgorithmWeight(); static float getAlgorithmInternalWeight(); public: AlgorithmCreateSignedDistanceVolume(ProgressObject* myProgObj, const SurfaceFile* mySurf, VolumeFile* myVolOut, VolumeFile* myRoiOut = NULL, const float& fillValue = 0.0f, const float& exactLim = 5.0f, const float& approxLim = 20.0f, const int& approxNeighborhood = 2, const SignedDistanceHelper::WindingLogic& myWinding = SignedDistanceHelper::EVEN_ODD); static OperationParameters* getParameters(); static void useParameters(OperationParameters* myParams, ProgressObject* myProgObj); static AString getCommandSwitch(); static AString getShortDescription(); }; typedef TemplateAutoOperation AutoAlgorithmCreateSignedDistanceVolume; } #endif //__ALGORITHM_CREATE_SIGNED_DISTANCE_VOLUME_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Algorithms/AlgorithmException.cxx000066400000000000000000000042431300200146000272110ustar00rootroot00000000000000/*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 "AlgorithmException.h" #include using namespace caret; /** * Constructor. * */ AlgorithmException::AlgorithmException() : CaretException() { this->initializeMembersAlgorithmException(); } /** * Constructor that uses stack trace from the exception * passed in as a parameter. * * @param e Any exception whose stack trace becomes * this exception's stack trace. * */ AlgorithmException::AlgorithmException( const CaretException& e) : CaretException(e) { this->initializeMembersAlgorithmException(); } /** * Constructor. * * @param s Description of the exception. * */ AlgorithmException::AlgorithmException(const AString& s) : CaretException(s) { this->initializeMembersAlgorithmException(); } /** * Copy Constructor. * @param e * Exception that is copied. */ AlgorithmException::AlgorithmException(const AlgorithmException& e) : CaretException(e) { } /** * Assignment operator. * @param e * Exception that is copied. * @return * Copy of the exception. */ AlgorithmException& AlgorithmException::operator=(const AlgorithmException& e) { if (this != &e) { CaretException::operator=(e); } return *this; } /** * Destructor */ AlgorithmException::~AlgorithmException() throw() { } void AlgorithmException::initializeMembersAlgorithmException() { } connectome-workbench-1.2.3+git41-gc4c6c90/src/Algorithms/AlgorithmException.h000066400000000000000000000027421300200146000266400ustar00rootroot00000000000000#ifndef __ALGORITHM_EXCEPTION_H__ #define __ALGORITHM_EXCEPTION_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 "CaretException.h" namespace caret { /// An exception thrown during command processing. class AlgorithmException : public CaretException { public: AlgorithmException(); AlgorithmException(const CaretException& e); AlgorithmException(const AString& s); AlgorithmException(const AlgorithmException& e); AlgorithmException& operator=(const AlgorithmException& e); virtual ~AlgorithmException() throw(); private: void initializeMembersAlgorithmException(); }; } // namespace #endif // __ALGORITHM_EXCEPTION_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Algorithms/AlgorithmFiberDotProducts.cxx000066400000000000000000000171431300200146000305000ustar00rootroot00000000000000/*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 "AlgorithmFiberDotProducts.h" #include "AlgorithmException.h" #include "CiftiFile.h" #include "CaretPointLocator.h" #include "MetricFile.h" #include "SignedDistanceHelper.h" #include "SurfaceFile.h" #include #include using namespace caret; using namespace std; AString AlgorithmFiberDotProducts::getCommandSwitch() { return "-fiber-dot-products"; } AString AlgorithmFiberDotProducts::getShortDescription() { return "COMPUTE DOT PRODUCTS OF FIBER ORIENTATIONS WITH SURFACE NORMALS"; } OperationParameters* AlgorithmFiberDotProducts::getParameters() { OperationParameters* ret = new OperationParameters(); ret->addSurfaceParameter(1, "white-surf", "the white/gray boundary surface"); ret->addCiftiParameter(2, "fiber-file", "the fiber orientation file"); ret->addDoubleParameter(3, "max-dist", "the maximum distance from any surface vertex a fiber population may be, in mm"); ret->addStringParameter(6, "direction", "test against surface for whether a fiber population should be used"); ret->addMetricOutputParameter(4, "dot-metric", "the metric of dot products"); ret->addMetricOutputParameter(5, "f-metric", "a metric of the f values of the fiber distributions"); ret->setHelpText( AString("For each vertex, this command finds the closest fiber population that satisfies the test, ") + "and computes the absolute value of the dot product of the surface normal and the normalized mean direction of each fiber. " + "The test must be one of INSIDE, OUTSIDE, or ANY, which causes the command to only use fiber populations " + "that are inside the surface, outside the surface, or to not care which direction it is from the surface. " + "Each fiber population is output in a separate metric column." ); return ret; } void AlgorithmFiberDotProducts::useParameters(OperationParameters* myParams, ProgressObject* myProgObj) { SurfaceFile* mySurf = myParams->getSurface(1); CiftiFile* myFibers = myParams->getCifti(2); float maxDist = (float)myParams->getDouble(3); MetricFile* myDotProdOut = myParams->getOutputMetric(4); MetricFile* myFSampOut = myParams->getOutputMetric(5); AString direction = myParams->getString(6); Direction myTest = INSIDE; if (direction == "INSIDE") { myTest = INSIDE; } else if (direction == "OUTSIDE") { myTest = OUTSIDE; } else if (direction == "ANY") { myTest = ANY; } else { throw AlgorithmException("unrecognized direction test"); } AlgorithmFiberDotProducts(myProgObj, mySurf, myFibers, maxDist, myTest, myDotProdOut, myFSampOut); } AlgorithmFiberDotProducts::AlgorithmFiberDotProducts(ProgressObject* myProgObj, const SurfaceFile* mySurf, const CiftiFile* myFibers, const float& maxDist, const Direction& myTest, MetricFile* myDotProdOut, MetricFile* myFSampOut) : AbstractAlgorithm(myProgObj) { LevelProgress myProgress(myProgObj); CaretPointer mySignedHelp = mySurf->getSignedDistanceHelper(); int rowSize = myFibers->getNumberOfColumns(); if ((rowSize - 3) % 7 != 0) throw AlgorithmException("input is not a fiber orientation file"); int numFibers = (rowSize - 3) / 7; int64_t numRows = myFibers->getNumberOfRows(); vector coordsInside; vector coordIndices; vector rowScratch(rowSize); for (int64_t i = 0; i < numRows; ++i) { myFibers->getRow(rowScratch.data(), i); int closeNode = mySurf->closestNode(rowScratch.data(), maxDist); if (closeNode == -1) continue;//skip samples that aren't close to a surface node, for speed (signed distance is kinda slow) if (myTest == ANY) { coordsInside.push_back(rowScratch[0]);//add point to vector for locator coordsInside.push_back(rowScratch[1]); coordsInside.push_back(rowScratch[2]); coordIndices.push_back(i);//and save its cifti index } else { float signedDist = mySignedHelp->dist(rowScratch.data(), SignedDistanceHelper::EVEN_ODD);//the first 3 floats are xyz coords if ((myTest == INSIDE) == (signedDist <= 0.0f))//test for inside/outside surface { coordsInside.push_back(rowScratch[0]);//add point to vector for locator coordsInside.push_back(rowScratch[1]); coordsInside.push_back(rowScratch[2]); coordIndices.push_back(i);//and save its cifti index } } } if (coordIndices.size() == 0) throw AlgorithmException("no fiber samples passed the and tests"); CaretPointLocator myLocator(coordsInside.data(), coordIndices.size());//build the locator int numNodes = mySurf->getNumberOfNodes(); myDotProdOut->setNumberOfNodesAndColumns(numNodes, numFibers); myFSampOut->setNumberOfNodesAndColumns(numNodes, numFibers); myDotProdOut->setStructure(mySurf->getStructure()); myFSampOut->setStructure(mySurf->getStructure()); for (int i = 0; i < numFibers; ++i) { myDotProdOut->setColumnName(i, "Fiber " + AString::number(i + 1) + " dot products"); myFSampOut->setColumnName(i, "Fiber " + AString::number(i + 1) + " population mean f"); } const float* coordData = mySurf->getCoordinateData(); for (int i = 0; i < numNodes; ++i) { int closest = myLocator.closestPoint(coordData + i * 3); if (closest != -1) { myFibers->getRow(rowScratch.data(), coordIndices[closest]); Vector3D myNormal = mySurf->getNormalVector(i); for (int j = 0; j < numFibers; ++j) { int base = 3 + j * 7; float fmean = rowScratch[base]; float theta = rowScratch[base + 2]; float phi = rowScratch[base + 3]; Vector3D direction; direction[0] = -sin(theta) * cos(phi);//NOTE: theta, phi are polar coordinates for a RADIOLOGICAL coordinate system, so flip x so that +x = right direction[1] = sin(theta) * sin(phi); direction[2] = cos(theta); float dotProd = abs(myNormal.dot(direction)); myDotProdOut->setValue(i, j, dotProd); myFSampOut->setValue(i, j, fmean); } } else { for (int j = 0; j < numFibers; ++j) { myDotProdOut->setValue(i, j, 0.0f); myFSampOut->setValue(i, j, 0.0f); } } } } float AlgorithmFiberDotProducts::getAlgorithmInternalWeight() { return 1.0f;//override this if needed, if the progress bar isn't smooth } float AlgorithmFiberDotProducts::getSubAlgorithmWeight() { //return AlgorithmInsertNameHere::getAlgorithmWeight();//if you use a subalgorithm return 0.0f; } connectome-workbench-1.2.3+git41-gc4c6c90/src/Algorithms/AlgorithmFiberDotProducts.h000066400000000000000000000035631300200146000301260ustar00rootroot00000000000000#ifndef __ALGORITHM_FIBER_DOT_PRODUCTS_H__ #define __ALGORITHM_FIBER_DOT_PRODUCTS_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 "AbstractAlgorithm.h" namespace caret { class AlgorithmFiberDotProducts : public AbstractAlgorithm { AlgorithmFiberDotProducts(); protected: static float getSubAlgorithmWeight(); static float getAlgorithmInternalWeight(); public: enum Direction { INSIDE, OUTSIDE, ANY }; AlgorithmFiberDotProducts(ProgressObject* myProgObj, const SurfaceFile* mySurf, const CiftiFile* myFibers, const float& maxDist, const Direction& myTest, MetricFile* myDotProdOut, MetricFile* myFSampOut); static OperationParameters* getParameters(); static void useParameters(OperationParameters* myParams, ProgressObject* myProgObj); static AString getCommandSwitch(); static AString getShortDescription(); }; typedef TemplateAutoOperation AutoAlgorithmFiberDotProducts; } #endif //__ALGORITHM_FIBER_DOT_PRODUCTS_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Algorithms/AlgorithmFociResample.cxx000066400000000000000000000213001300200146000276150ustar00rootroot00000000000000/*LICENSE_START*/ /* * Copyright (C) 2015 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 "AlgorithmFociResample.h" #include "AlgorithmException.h" #include "FociFile.h" #include "Focus.h" #include "GiftiLabelTable.h" #include "GiftiMetaData.h" #include "SurfaceFile.h" #include "SurfaceProjector.h" using namespace caret; using namespace std; AString AlgorithmFociResample::getCommandSwitch() { return "-foci-resample"; } AString AlgorithmFociResample::getShortDescription() { return "PROJECT FOCI TO A DIFFERENT SURFACE"; } OperationParameters* AlgorithmFociResample::getParameters() { OperationParameters* ret = new OperationParameters(); ret->addFociParameter(1, "foci-in", "the input foci file"); ret->addFociOutputParameter(2, "foci-out", "the output foci file"); OptionalParameter* leftSurfaceOpt = ret->createOptionalParameter(3, "-left-surfaces", "the left surfaces for resampling"); leftSurfaceOpt->addSurfaceParameter(1, "current-surf", "the surface the foci are currently projected on"); leftSurfaceOpt->addSurfaceParameter(2, "new-surf", "the surface to project the foci onto"); OptionalParameter* rightSurfaceOpt = ret->createOptionalParameter(4, "-right-surfaces", "the right surfaces for resampling"); rightSurfaceOpt->addSurfaceParameter(1, "current-surf", "the surface the foci are currently projected on"); rightSurfaceOpt->addSurfaceParameter(2, "new-surf", "the surface to project the foci onto"); OptionalParameter* cerebSurfaceOpt = ret->createOptionalParameter(5, "-cerebellum-surfaces", "the cerebellum surfaces for resampling"); cerebSurfaceOpt->addSurfaceParameter(1, "current-surf", "the surface the foci are currently projected on"); cerebSurfaceOpt->addSurfaceParameter(2, "new-surf", "the surface to project the foci onto"); ret->createOptionalParameter(6, "-discard-distance-from-surface", "ignore the distance the foci are above or below the current surface"); ret->createOptionalParameter(7, "-restore-xyz", "put the original xyz coordinates into the foci, rather than the coordinates obtained from unprojection"); ret->setHelpText(AString("Unprojects foci from the for the structure, then projects them to . ") + "If the foci have meaningful distances above or below the surface, use anatomical surfaces. " + "If the foci should be on the surface, use registered spheres and the options -discard-distance-from-surface and -restore-xyz."); return ret; } void AlgorithmFociResample::useParameters(OperationParameters* myParams, ProgressObject* myProgObj) { FociFile* fociIn = myParams->getFoci(1); FociFile* fociOut = myParams->getOutputFoci(2); SurfaceFile* leftCurSurf = NULL, *leftNewSurf = NULL; OptionalParameter* leftSurfaceOpt = myParams->getOptionalParameter(3); if (leftSurfaceOpt->m_present) { leftCurSurf = leftSurfaceOpt->getSurface(1); leftNewSurf = leftSurfaceOpt->getSurface(2); } SurfaceFile* rightCurSurf = NULL, *rightNewSurf = NULL; OptionalParameter* rightSurfaceOpt = myParams->getOptionalParameter(4); if (rightSurfaceOpt->m_present) { rightCurSurf = rightSurfaceOpt->getSurface(1); rightNewSurf = rightSurfaceOpt->getSurface(2); } SurfaceFile* cerebCurSurf = NULL, *cerebNewSurf = NULL; OptionalParameter* cerebSurfaceOpt = myParams->getOptionalParameter(5); if (cerebSurfaceOpt->m_present) { cerebCurSurf = cerebSurfaceOpt->getSurface(1); cerebNewSurf = cerebSurfaceOpt->getSurface(2); } bool discardNormDist = myParams->getOptionalParameter(6)->m_present; bool restoryXyz = myParams->getOptionalParameter(7)->m_present; AlgorithmFociResample(myProgObj, fociIn, fociOut, leftCurSurf, leftNewSurf, rightCurSurf, rightNewSurf, cerebCurSurf, cerebNewSurf, discardNormDist, restoryXyz); } AlgorithmFociResample::AlgorithmFociResample(ProgressObject* myProgObj, const FociFile* fociIn, FociFile* fociOut, const SurfaceFile* leftCurSurf, const SurfaceFile* leftNewSurf, const SurfaceFile* rightCurSurf, const SurfaceFile* rightNewSurf, const SurfaceFile* cerebCurSurf, const SurfaceFile* cerebNewSurf, const bool& discardNormDist, const bool& restoryXyz) : AbstractAlgorithm(myProgObj) { LevelProgress myProgress(myProgObj); checkStructureMatch(leftCurSurf, StructureEnum::CORTEX_LEFT, "current left surface", "-left-surfaces option expects"); checkStructureMatch(leftNewSurf, StructureEnum::CORTEX_LEFT, "new left surface", "-left-surfaces option expects"); checkStructureMatch(rightCurSurf, StructureEnum::CORTEX_RIGHT, "current right surface", "-right-surfaces option expects"); checkStructureMatch(rightNewSurf, StructureEnum::CORTEX_RIGHT, "new right surface", "-right-surfaces option expects"); checkStructureMatch(cerebCurSurf, StructureEnum::CEREBELLUM, "current cerebellum surface", "-cerebellum-surfaces option expects"); checkStructureMatch(cerebNewSurf, StructureEnum::CEREBELLUM, "new cerebellum surface", "-cerebellum-surfaces option expects"); CaretPointer leftProj, rightProj, cerebProj; if (leftNewSurf != NULL) leftProj.grabNew(new SurfaceProjector(leftNewSurf)); if (rightNewSurf != NULL) rightProj.grabNew(new SurfaceProjector(rightNewSurf)); if (cerebNewSurf != NULL) cerebProj.grabNew(new SurfaceProjector(cerebNewSurf)); *(fociOut->getClassColorTable()) = *(fociIn->getClassColorTable()); *(fociOut->getNameColorTable()) = *(fociIn->getNameColorTable()); *(fociOut->getFileMetaData()) = *(fociIn->getFileMetaData()); for (int i = 0; i < fociIn->getNumberOfFoci(); ++i) { const Focus* thisFocus = fociIn->getFocus(i); if (thisFocus->getNumberOfProjections() < 1) { throw AlgorithmException("focus '" + thisFocus->getName() + "' has no surface projection"); } SurfaceProjector* myProj = NULL; const SurfaceFile* unprojFrom = NULL; switch (thisFocus->getProjection(0)->getStructure()) { case StructureEnum::CORTEX_LEFT: myProj = leftProj; unprojFrom = leftCurSurf; break; case StructureEnum::CORTEX_RIGHT: myProj = rightProj; unprojFrom = rightCurSurf; break; case StructureEnum::CEREBELLUM: myProj = cerebProj; unprojFrom = cerebCurSurf; break; default: throw AlgorithmException("focus '" + thisFocus->getName() + "' has unsupported structure " + StructureEnum::toName(thisFocus->getProjection(0)->getStructure())); } if (unprojFrom == NULL || myProj == NULL) throw AlgorithmException("focus '" + thisFocus->getName() + "' has structure " + StructureEnum::toName(thisFocus->getProjection(0)->getStructure()) + ", but surfaces for that structure were not specified"); CaretPointer newFocus(new Focus(*thisFocus));//start with a copy float xyz[3]; bool result = thisFocus->getProjection(0)->getProjectedPosition(*unprojFrom, xyz, discardNormDist); if (!result) throw AlgorithmException("failed to unproject focus '" + thisFocus->getName() + "'"); newFocus->getProjection(0)->setStereotaxicXYZ(xyz); myProj->projectFocus(i, newFocus); if (restoryXyz) { newFocus->getProjection(0)->setStereotaxicXYZ(thisFocus->getProjection(0)->getStereotaxicXYZ()); } fociOut->addFocus(newFocus.releasePointer()); } } float AlgorithmFociResample::getAlgorithmInternalWeight() { return 1.0f;//override this if needed, if the progress bar isn't smooth } float AlgorithmFociResample::getSubAlgorithmWeight() { //return AlgorithmInsertNameHere::getAlgorithmWeight();//if you use a subalgorithm return 0.0f; } connectome-workbench-1.2.3+git41-gc4c6c90/src/Algorithms/AlgorithmFociResample.h000066400000000000000000000040341300200146000272470ustar00rootroot00000000000000#ifndef __ALGORITHM_FOCI_RESAMPLE_H__ #define __ALGORITHM_FOCI_RESAMPLE_H__ /*LICENSE_START*/ /* * Copyright (C) 2015 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 AlgorithmFociResample : public AbstractAlgorithm { AlgorithmFociResample(); protected: static float getSubAlgorithmWeight(); static float getAlgorithmInternalWeight(); public: AlgorithmFociResample(ProgressObject* myProgObj, const FociFile* fociIn, FociFile* fociOut, const SurfaceFile* leftCurSurf, const SurfaceFile* leftNewSurf, const SurfaceFile* rightCurSurf = NULL, const SurfaceFile* rightNewSurf = NULL, const SurfaceFile* cerebCurSurf = NULL, const SurfaceFile* cerebNewSurf = NULL, const bool& discardNormDist = false, const bool& restoryXyz = false); static OperationParameters* getParameters(); static void useParameters(OperationParameters* myParams, ProgressObject* myProgObj); static AString getCommandSwitch(); static AString getShortDescription(); }; typedef TemplateAutoOperation AutoAlgorithmFociResample; } #endif //__ALGORITHM_FOCI_RESAMPLE_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Algorithms/AlgorithmGiftiAllLabelsToROIs.cxx000066400000000000000000000106721300200146000311340ustar00rootroot00000000000000/*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 "AlgorithmGiftiAllLabelsToROIs.h" #include "AlgorithmException.h" #include "GiftiLabelTable.h" #include "LabelFile.h" #include "MetricFile.h" #include using namespace caret; using namespace std; AString AlgorithmGiftiAllLabelsToROIs::getCommandSwitch() { return "-gifti-all-labels-to-rois"; } AString AlgorithmGiftiAllLabelsToROIs::getShortDescription() { return "MAKE ROIS FROM ALL LABELS IN A GIFTI COLUMN"; } OperationParameters* AlgorithmGiftiAllLabelsToROIs::getParameters() { OperationParameters* ret = new OperationParameters(); ret->addLabelParameter(1, "label-in", "the input gifti label file"); ret->addStringParameter(2, "map", "the number or name of the label map to use"); ret->addMetricOutputParameter(3, "metric-out", "the output metric file"); ret->setHelpText( AString("The output metric file has a column for each label in the specified input map, other than the ??? label, ") + "each of which contains an ROI of all vertices that are set to the corresponding label." ); return ret; } void AlgorithmGiftiAllLabelsToROIs::useParameters(OperationParameters* myParams, ProgressObject* myProgObj) { LabelFile* myLabel = myParams->getLabel(1); AString mapID = myParams->getString(2); int whichMap = myLabel->getMapIndexFromNameOrNumber(mapID); if (whichMap == -1) { throw AlgorithmException("invalid map number or name specified"); } MetricFile* myMetricOut = myParams->getOutputMetric(3); AlgorithmGiftiAllLabelsToROIs(myProgObj, myLabel, whichMap, myMetricOut); } AlgorithmGiftiAllLabelsToROIs::AlgorithmGiftiAllLabelsToROIs(ProgressObject* myProgObj, const LabelFile* myLabel, const int& whichMap, MetricFile* myMetricOut) : AbstractAlgorithm(myProgObj) { LevelProgress myProgress(myProgObj); if (whichMap < 0 || whichMap >= myLabel->getNumberOfMaps()) { throw AlgorithmException("invalid map index specified"); } const GiftiLabelTable* myTable = myLabel->getMapLabelTable(whichMap); int32_t unusedKey = myTable->getUnassignedLabelKey();//WARNING: this actually MODIFIES the label table if the ??? key doesn't exist set myKeys = myTable->getKeys(); int numKeys = (int)myKeys.size(); if (numKeys < 2) { throw AlgorithmException("label table doesn't contain any keys besides the ??? key"); } int numNodes = myLabel->getNumberOfNodes(); map keyToMap;//lookup from keys to column myMetricOut->setNumberOfNodesAndColumns(numNodes, numKeys - 1);//skip the ??? label myMetricOut->setStructure(myLabel->getStructure()); for (int i = 0; i < numKeys - 1; ++i) { myMetricOut->initializeColumn(i, 0.0f); } int counter = 0; for (set::iterator iter = myKeys.begin(); iter != myKeys.end(); ++iter) { if (*iter == unusedKey) continue;//skip the ??? key keyToMap[*iter] = counter; myMetricOut->setMapName(counter, myTable->getLabelName(*iter)); ++counter; } const int32_t* myColumn = myLabel->getLabelKeyPointerForColumn(whichMap); for (int i = 0; i < numNodes; ++i) { map::iterator iter = keyToMap.find(myColumn[i]); if (iter != keyToMap.end()) { myMetricOut->setValue(i, iter->second, 1.0f); } } } float AlgorithmGiftiAllLabelsToROIs::getAlgorithmInternalWeight() { return 1.0f;//override this if needed, if the progress bar isn't smooth } float AlgorithmGiftiAllLabelsToROIs::getSubAlgorithmWeight() { //return AlgorithmInsertNameHere::getAlgorithmWeight();//if you use a subalgorithm return 0.0f; } connectome-workbench-1.2.3+git41-gc4c6c90/src/Algorithms/AlgorithmGiftiAllLabelsToROIs.h000066400000000000000000000033451300200146000305600ustar00rootroot00000000000000#ifndef __ALGORITHM_GIFTI_ALL_LABELS_TO_ROIS_H__ #define __ALGORITHM_GIFTI_ALL_LABELS_TO_ROIS_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 "AbstractAlgorithm.h" namespace caret { class AlgorithmGiftiAllLabelsToROIs : public AbstractAlgorithm { AlgorithmGiftiAllLabelsToROIs(); protected: static float getSubAlgorithmWeight(); static float getAlgorithmInternalWeight(); public: AlgorithmGiftiAllLabelsToROIs(ProgressObject* myProgObj, const LabelFile* myLabel, const int& whichMap, MetricFile* myMetricOut); static OperationParameters* getParameters(); static void useParameters(OperationParameters* myParams, ProgressObject* myProgObj); static AString getCommandSwitch(); static AString getShortDescription(); }; typedef TemplateAutoOperation AutoAlgorithmGiftiAllLabelsToROIs; } #endif //__ALGORITHM_GIFTI_ALL_LABELS_TO_ROIS_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Algorithms/AlgorithmGiftiLabelAddPrefix.cxx000066400000000000000000000067311300200146000310500ustar00rootroot00000000000000/*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 "AlgorithmGiftiLabelAddPrefix.h" #include "AlgorithmException.h" #include "GiftiLabelTable.h" #include "LabelFile.h" using namespace caret; using namespace std; AString AlgorithmGiftiLabelAddPrefix::getCommandSwitch() { return "-gifti-label-add-prefix"; } AString AlgorithmGiftiLabelAddPrefix::getShortDescription() { return "ADD PREFIX TO ALL LABEL NAMES IN A GIFTI LABEL FILE"; } OperationParameters* AlgorithmGiftiLabelAddPrefix::getParameters() { OperationParameters* ret = new OperationParameters(); ret->addLabelParameter(1, "label-in", "the input label file"); ret->addStringParameter(2, "prefix", "the prefix string to add"); ret->addLabelOutputParameter(3, "label-out", "the output label file"); ret->setHelpText( AString("For each label other than '?\?\?', prepend to the label name."));//bleh, trigraphs return ret; } void AlgorithmGiftiLabelAddPrefix::useParameters(OperationParameters* myParams, ProgressObject* myProgObj) { LabelFile* labelIn = myParams->getLabel(1); AString prefix = myParams->getString(2); LabelFile* labelOut = myParams->getOutputLabel(3); AlgorithmGiftiLabelAddPrefix(myProgObj, labelIn, prefix, labelOut); } AlgorithmGiftiLabelAddPrefix::AlgorithmGiftiLabelAddPrefix(ProgressObject* myProgObj, const LabelFile* labelIn, const AString& prefix, LabelFile* labelOut) : AbstractAlgorithm(myProgObj) { LevelProgress myProgress(myProgObj); int numColumns = labelIn->getNumberOfColumns(); GiftiLabelTable outTable = *(labelIn->getLabelTable()); vector keys; outTable.getKeys(keys); int numKeys = (int)keys.size(); int32_t unassigned = GiftiLabelTable(outTable).getUnassignedLabelKey();//make a copy so getUnassignedLabelKey() doesn't modify the one we are using for (int i = 0; i < numKeys; ++i) { if (keys[i] != unassigned) { outTable.setLabelName(keys[i], prefix + outTable.getLabelName(keys[i])); } } labelOut->setNumberOfNodesAndColumns(labelIn->getNumberOfNodes(), numColumns); labelOut->setStructure(labelIn->getStructure()); *(labelOut->getLabelTable()) = outTable; for (int i = 0; i < numColumns; ++i) { labelOut->setMapName(i, labelIn->getMapName(i)); labelOut->setLabelKeysForColumn(i, labelIn->getLabelKeyPointerForColumn(i)); } } float AlgorithmGiftiLabelAddPrefix::getAlgorithmInternalWeight() { return 1.0f;//override this if needed, if the progress bar isn't smooth } float AlgorithmGiftiLabelAddPrefix::getSubAlgorithmWeight() { //return AlgorithmInsertNameHere::getAlgorithmWeight();//if you use a subalgorithm return 0.0f; } connectome-workbench-1.2.3+git41-gc4c6c90/src/Algorithms/AlgorithmGiftiLabelAddPrefix.h000066400000000000000000000033301300200146000304650ustar00rootroot00000000000000#ifndef __ALGORITHM_GIFTI_LABEL_ADD_PREFIX_H__ #define __ALGORITHM_GIFTI_LABEL_ADD_PREFIX_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 "AbstractAlgorithm.h" namespace caret { class AlgorithmGiftiLabelAddPrefix : public AbstractAlgorithm { AlgorithmGiftiLabelAddPrefix(); protected: static float getSubAlgorithmWeight(); static float getAlgorithmInternalWeight(); public: AlgorithmGiftiLabelAddPrefix(ProgressObject* myProgObj, const LabelFile* labelIn, const AString& prefix, LabelFile* labelOut); static OperationParameters* getParameters(); static void useParameters(OperationParameters* myParams, ProgressObject* myProgObj); static AString getCommandSwitch(); static AString getShortDescription(); }; typedef TemplateAutoOperation AutoAlgorithmGiftiLabelAddPrefix; } #endif //__ALGORITHM_GIFTI_LABEL_ADD_PREFIX_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Algorithms/AlgorithmGiftiLabelToROI.cxx000066400000000000000000000221231300200146000301270ustar00rootroot00000000000000/*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 "AlgorithmGiftiLabelToROI.h" #include "AlgorithmException.h" #include "CaretLogger.h" #include "GiftiLabel.h" #include "GiftiLabelTable.h" #include "LabelFile.h" #include "MetricFile.h" #include #include using namespace caret; using namespace std; AString AlgorithmGiftiLabelToROI::getCommandSwitch() { return "-gifti-label-to-roi"; } AString AlgorithmGiftiLabelToROI::getShortDescription() { return "MAKE A GIFTI LABEL INTO AN ROI METRIC"; } OperationParameters* AlgorithmGiftiLabelToROI::getParameters() { OperationParameters* ret = new OperationParameters(); ret->addLabelParameter(1, "label-in", "the input gifti label file"); ret->addMetricOutputParameter(2, "metric-out", "the output metric file"); OptionalParameter* nameOpt = ret->createOptionalParameter(3, "-name", "select label by name"); nameOpt->addStringParameter(1, "label-name", "the label name that you want an roi of"); OptionalParameter* keyOpt = ret->createOptionalParameter(4, "-key", "select label by key"); keyOpt->addIntegerParameter(1, "label-key", "the label key that you want an roi of"); OptionalParameter* mapOpt = ret->createOptionalParameter(5, "-map", "select a single label map to use"); mapOpt->addStringParameter(1, "map", "the map number or name"); ret->setHelpText( AString("For each map in , a map is created in where all locations labeled with or with a key of are given a value of 1, and all other locations are given 0. ") + "Exactly one of -name and -key must be specified. " + "Specify -map to use only one map from ." ); return ret; } void AlgorithmGiftiLabelToROI::useParameters(OperationParameters* myParams, ProgressObject* myProgObj) { LabelFile* myLabel = myParams->getLabel(1); MetricFile* myMetricOut = myParams->getOutputMetric(2); bool nameMode = false; AString labelName; OptionalParameter* nameOpt = myParams->getOptionalParameter(3); if (nameOpt->m_present) { nameMode = true; labelName = nameOpt->getString(1); } int32_t labelKey; OptionalParameter* keyOpt = myParams->getOptionalParameter(4); if (keyOpt->m_present) { if (nameMode) throw AlgorithmException("-name and -key cannot be specified together"); labelKey = (int32_t)keyOpt->getInteger(1); } else { if (!nameMode) throw AlgorithmException("you must specify one of -name or -key"); } int whichMap = -1; OptionalParameter* mapOpt = myParams->getOptionalParameter(5); if (mapOpt->m_present) { AString mapID = mapOpt->getString(1); whichMap = myLabel->getMapIndexFromNameOrNumber(mapID); if (whichMap == -1) { throw AlgorithmException("invalid map number or name specified"); } } if (nameMode) { AlgorithmGiftiLabelToROI(myProgObj, myLabel, labelName, myMetricOut, whichMap); } else { AlgorithmGiftiLabelToROI(myProgObj, myLabel, labelKey, myMetricOut, whichMap); } } AlgorithmGiftiLabelToROI::AlgorithmGiftiLabelToROI(ProgressObject* myProgObj, const LabelFile* myLabel, const AString& labelName, MetricFile* myMetricOut, const int& whichMap) : AbstractAlgorithm(myProgObj) { LevelProgress myProgress(myProgObj); int64_t numNodes = myLabel->getNumberOfNodes(); int64_t numMaps = myLabel->getNumberOfMaps(); if (whichMap < -1 || whichMap >= numMaps) { throw AlgorithmException("invalid map index specified"); } const GiftiLabelTable* myTable = myLabel->getLabelTable(); int32_t matchKey = myTable->getLabelKeyFromName(labelName); if (matchKey == GiftiLabel::getInvalidLabelKey()) { throw AlgorithmException("label name '" + labelName + "' not found in label file"); } vector scratchCol(numNodes); if (whichMap == -1) { myMetricOut->setNumberOfNodesAndColumns(numNodes, numMaps); myMetricOut->setStructure(myLabel->getStructure()); bool shouldThrow = true; for (int thisMap = 0; thisMap < numMaps; ++thisMap) { myMetricOut->setColumnName(thisMap, myLabel->getColumnName(thisMap)); const int32_t* labelColumn = myLabel->getLabelKeyPointerForColumn(thisMap); for (int i = 0; i < numNodes; ++i) { if (labelColumn[i] == matchKey) { scratchCol[i] = 1.0f; shouldThrow = false; } else { scratchCol[i] = 0.0f; } } myMetricOut->setValuesForColumn(thisMap, scratchCol.data()); } if (shouldThrow) { throw AlgorithmException("no data matched the specified label name"); } } else { myMetricOut->setNumberOfNodesAndColumns(numNodes, 1); myMetricOut->setStructure(myLabel->getStructure()); myMetricOut->setColumnName(0, myLabel->getColumnName(whichMap)); const int32_t* labelColumn = myLabel->getLabelKeyPointerForColumn(whichMap); bool shouldThrow = true; for (int i = 0; i < numNodes; ++i) { if (labelColumn[i] == matchKey) { scratchCol[i] = 1.0f; shouldThrow = false; } else { scratchCol[i] = 0.0f; } } if (shouldThrow) { throw AlgorithmException("no data matched the specified label name in the specified map"); } myMetricOut->setValuesForColumn(0, scratchCol.data()); } } AlgorithmGiftiLabelToROI::AlgorithmGiftiLabelToROI(ProgressObject* myProgObj, const LabelFile* myLabel, const int32_t& labelKey, MetricFile* myMetricOut, const int& whichMap) : AbstractAlgorithm(myProgObj) { LevelProgress myProgress(myProgObj); int64_t numNodes = myLabel->getNumberOfNodes(); int64_t numMaps = myLabel->getNumberOfMaps(); if (whichMap < -1 || whichMap >= numMaps) { throw AlgorithmException("invalid map index specified"); } const GiftiLabelTable* myTable = myLabel->getLabelTable(); if (myTable->getLabel(labelKey) == NULL) { CaretLogWarning("label key " + AString::number(labelKey) + " not found in label file"); } vector scratchCol(numNodes); if (whichMap == -1) { myMetricOut->setNumberOfNodesAndColumns(numNodes, numMaps); myMetricOut->setStructure(myLabel->getStructure()); bool shouldThrow = true; for (int thisMap = 0; thisMap < numMaps; ++thisMap) { myMetricOut->setColumnName(thisMap, myLabel->getColumnName(thisMap)); const int32_t* labelColumn = myLabel->getLabelKeyPointerForColumn(thisMap); for (int i = 0; i < numNodes; ++i) { if (labelColumn[i] == labelKey) { scratchCol[i] = 1.0f; shouldThrow = false; } else { scratchCol[i] = 0.0f; } } myMetricOut->setValuesForColumn(thisMap, scratchCol.data()); } if (shouldThrow) { throw AlgorithmException("no data matched the specified label key"); } } else { myMetricOut->setNumberOfNodesAndColumns(numNodes, 1); myMetricOut->setStructure(myLabel->getStructure()); myMetricOut->setColumnName(0, myLabel->getColumnName(whichMap)); const int32_t* labelColumn = myLabel->getLabelKeyPointerForColumn(whichMap); bool shouldThrow = true; for (int i = 0; i < numNodes; ++i) { if (labelColumn[i] == labelKey) { scratchCol[i] = 1.0f; } else { scratchCol[i] = 0.0f; } } if (shouldThrow) { throw AlgorithmException("no data matched the specified label key in the specified map"); } myMetricOut->setValuesForColumn(0, scratchCol.data()); } } float AlgorithmGiftiLabelToROI::getAlgorithmInternalWeight() { return 1.0f;//override this if needed, if the progress bar isn't smooth } float AlgorithmGiftiLabelToROI::getSubAlgorithmWeight() { //return AlgorithmInsertNameHere::getAlgorithmWeight();//if you use a subalgorithm return 0.0f; } connectome-workbench-1.2.3+git41-gc4c6c90/src/Algorithms/AlgorithmGiftiLabelToROI.h000066400000000000000000000035741300200146000275650ustar00rootroot00000000000000#ifndef __ALGORITHM_GIFTI_LABEL_TO_ROI_H__ #define __ALGORITHM_GIFTI_LABEL_TO_ROI_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 "AbstractAlgorithm.h" namespace caret { class AlgorithmGiftiLabelToROI : public AbstractAlgorithm { AlgorithmGiftiLabelToROI(); protected: static float getSubAlgorithmWeight(); static float getAlgorithmInternalWeight(); public: AlgorithmGiftiLabelToROI(ProgressObject* myProgObj, const LabelFile* myLabel, const AString& labelName, MetricFile* myMetricOut, const int& whichMap = -1); AlgorithmGiftiLabelToROI(ProgressObject* myProgObj, const LabelFile* myLabel, const int32_t& labelKey, MetricFile* myMetricOut, const int& whichMap = -1); static OperationParameters* getParameters(); static void useParameters(OperationParameters* myParams, ProgressObject* myProgObj); static AString getCommandSwitch(); static AString getShortDescription(); }; typedef TemplateAutoOperation AutoAlgorithmGiftiLabelToROI; } #endif //__ALGORITHM_GIFTI_LABEL_TO_ROI_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Algorithms/AlgorithmLabelDilate.cxx000066400000000000000000000324571300200146000274250ustar00rootroot00000000000000/*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 "AlgorithmLabelDilate.h" #include "AlgorithmException.h" #include "CaretOMP.h" #include "GeodesicHelper.h" #include "LabelFile.h" #include "MetricFile.h" #include "GiftiLabelTable.h" #include "SurfaceFile.h" #include "TopologyHelper.h" using namespace caret; using namespace std; AString AlgorithmLabelDilate::getCommandSwitch() { return "-label-dilate"; } AString AlgorithmLabelDilate::getShortDescription() { return "DILATE A LABEL FILE"; } OperationParameters* AlgorithmLabelDilate::getParameters() { OperationParameters* ret = new OperationParameters(); ret->addLabelParameter(1, "label", "the input label"); ret->addSurfaceParameter(2, "surface", "the surface to dilate on"); ret->addDoubleParameter(3, "dilate-dist", "distance in mm to dilate the labels"); ret->addLabelOutputParameter(4, "label-out", "the output label file"); OptionalParameter* roiOpt = ret->createOptionalParameter(5, "-bad-vertex-roi", "specify an roi of vertices to overwrite, rather than vertices with the unlabeled key"); roiOpt->addMetricParameter(1, "roi-metric", "metric file, positive values denote vertices to have their values replaced"); OptionalParameter* columnSelect = ret->createOptionalParameter(6, "-column", "select a single column to dilate"); columnSelect->addStringParameter(1, "column", "the column number or name"); OptionalParameter* corrAreaOpt = ret->createOptionalParameter(7, "-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("Fills in label information for all vertices designated as bad, up to the specified distance away from other labels. ") + "If -bad-vertex-roi is specified, all vertices, including those with the unlabeled key, are good, except for vertices with a positive value in the ROI. " + "If it is not specified, only vertices with the unlabeled key are bad." ); return ret; } void AlgorithmLabelDilate::useParameters(OperationParameters* myParams, ProgressObject* myProgObj) { LabelFile* myLabel = myParams->getLabel(1); SurfaceFile* mySurf = myParams->getSurface(2); float myDist = (float)myParams->getDouble(3); LabelFile* myLabelOut = myParams->getOutputLabel(4); OptionalParameter* roiOpt = myParams->getOptionalParameter(5); MetricFile* badNodeRoi = NULL; if (roiOpt->m_present) { badNodeRoi = roiOpt->getMetric(1); } OptionalParameter* columnSelect = myParams->getOptionalParameter(6); int columnNum = -1; if (columnSelect->m_present) {//set up to use the single column columnNum = (int)myLabel->getMapIndexFromNameOrNumber(columnSelect->getString(1)); if (columnNum < 0) { throw AlgorithmException("invalid column specified"); } } OptionalParameter* corrAreaOpt = myParams->getOptionalParameter(7); MetricFile* corrAreas = NULL; if (corrAreaOpt->m_present) { corrAreas = corrAreaOpt->getMetric(1); } AlgorithmLabelDilate(myProgObj, myLabel, mySurf, myDist, myLabelOut, badNodeRoi, columnNum, corrAreas); } AlgorithmLabelDilate::AlgorithmLabelDilate(ProgressObject* myProgObj, const LabelFile* myLabel, const SurfaceFile* mySurf, float myDist, LabelFile* myLabelOut, const MetricFile* badNodeRoi, int columnNum, const MetricFile* corrAreas) : AbstractAlgorithm(myProgObj) { LevelProgress myProgress(myProgObj); int32_t unusedLabel = myLabel->getLabelTable()->getUnassignedLabelKey(); int numColumns = myLabel->getNumberOfColumns(); if (myDist < 0.0f) { throw AlgorithmException("invalid distance specified"); } if (columnNum < -1 || columnNum >= numColumns) { throw AlgorithmException("invalid column specified"); } int numNodes = myLabel->getNumberOfNodes(); if (mySurf->getNumberOfNodes() != numNodes) { throw AlgorithmException("surface has wrong number of vertices for this label file"); } if (corrAreas != NULL && corrAreas->getNumberOfNodes() != numNodes) { throw AlgorithmException("corrected areas metric number of vertices does not match"); } CaretArray colScratch(numNodes); CaretArray markArray(numNodes); if (badNodeRoi != NULL) { const float* myRoiData = badNodeRoi->getValuePointerForColumn(0); for (int i = 0; i < numNodes; ++i) { if (myRoiData[i] > 0.0f) { markArray[i] = 0; } else { markArray[i] = 1; } } } CaretPointer myCorrBase; if (corrAreas != NULL) { myCorrBase.grabNew(new GeodesicHelperBase(mySurf, corrAreas->getValuePointerForColumn(0))); } if (columnNum == -1) { myLabelOut->setNumberOfNodesAndColumns(numNodes, myLabel->getNumberOfColumns()); *(myLabelOut->getLabelTable()) = *(myLabel->getLabelTable()); myLabelOut->setStructure(mySurf->getStructure()); for (int thisCol = 0; thisCol < myLabel->getNumberOfColumns(); ++thisCol) { const int32_t* myInputData = myLabel->getLabelKeyPointerForColumn(thisCol); myLabelOut->setColumnName(thisCol, myLabel->getColumnName(thisCol) + " dilated"); if (badNodeRoi == NULL) { for (int i = 0; i < numNodes; ++i) { if (myInputData[i] == unusedLabel) { markArray[i] = 0; } else { markArray[i] = 1; } } } #pragma omp CARET_PAR { CaretPointer myTopoHelp = mySurf->getTopologyHelper(); CaretPointer myGeoHelp; if (corrAreas == NULL) { myGeoHelp = mySurf->getGeodesicHelper(); } else { myGeoHelp.grabNew(new GeodesicHelper(myCorrBase)); } #pragma omp CARET_FOR schedule(dynamic) for (int i = 0; i < numNodes; ++i) { if (markArray[i] == 0) { vector nodeList; vector distList; myGeoHelp->getNodesToGeoDist(i, myDist, nodeList, distList); int numInRange = (int)nodeList.size(); bool first = true; float bestDist = -1.0f; int32_t bestLabel = unusedLabel; for (int j = 0; j < numInRange; ++j) { if (markArray[nodeList[j]] == 1) { if (first || distList[j] < bestDist) { first = false; bestDist = distList[j]; bestLabel = myInputData[nodeList[j]]; } } } if (!first) { colScratch[i] = bestLabel; } else { nodeList = myTopoHelp->getNodeNeighbors(i); nodeList.push_back(i); myGeoHelp->getGeoToTheseNodes(i, nodeList, distList);//ok, its a little silly to do this numInRange = (int)nodeList.size(); for (int j = 0; j < numInRange; ++j) { if (markArray[nodeList[j]] == 1) { if (first || distList[j] < bestDist) { first = false; bestDist = distList[j]; bestLabel = myInputData[nodeList[j]]; } } } if (!first) { colScratch[i] = bestLabel; } else { colScratch[i] = unusedLabel; } } } else { colScratch[i] = myInputData[i]; } } } myLabelOut->setLabelKeysForColumn(thisCol, colScratch.getArray()); } } else { myLabelOut->setNumberOfNodesAndColumns(numNodes, 1); *(myLabelOut->getLabelTable()) = *(myLabel->getLabelTable()); myLabelOut->setStructure(mySurf->getStructure()); const int32_t* myInputData = myLabel->getLabelKeyPointerForColumn(columnNum); myLabelOut->setColumnName(0, myLabel->getColumnName(columnNum) + " dilated"); if (badNodeRoi == NULL) { for (int i = 0; i < numNodes; ++i) { if (myInputData[i] == unusedLabel) { markArray[i] = 0; } else { markArray[i] = 1; } } } #pragma omp CARET_PAR { CaretPointer myTopoHelp = mySurf->getTopologyHelper(); CaretPointer myGeoHelp; if (corrAreas == NULL) { myGeoHelp = mySurf->getGeodesicHelper(); } else { myGeoHelp.grabNew(new GeodesicHelper(myCorrBase)); } #pragma omp CARET_FOR schedule(dynamic) for (int i = 0; i < numNodes; ++i) { if (markArray[i] == 0) { vector nodeList; vector distList; myGeoHelp->getNodesToGeoDist(i, myDist, nodeList, distList); int numInRange = (int)nodeList.size(); bool first = true; float bestDist = -1.0f; int32_t bestLabel = unusedLabel; for (int j = 0; j < numInRange; ++j) { if (markArray[nodeList[j]] == 1) { if (first || distList[j] < bestDist) { first = false; bestDist = distList[j]; bestLabel = myInputData[nodeList[j]]; } } } if (!first) { colScratch[i] = bestLabel; } else { nodeList = myTopoHelp->getNodeNeighbors(i); nodeList.push_back(i); myGeoHelp->getGeoToTheseNodes(i, nodeList, distList);//ok, its a little silly to do this numInRange = (int)nodeList.size(); for (int j = 0; j < numInRange; ++j) { if (markArray[nodeList[j]] == 1) { if (first || distList[j] < bestDist) { first = false; bestDist = distList[j]; bestLabel = myInputData[nodeList[j]]; } } } if (!first) { colScratch[i] = bestLabel; } else { colScratch[i] = unusedLabel; } } } else { colScratch[i] = myInputData[i]; } } } myLabelOut->setLabelKeysForColumn(0, colScratch.getArray()); } } float AlgorithmLabelDilate::getAlgorithmInternalWeight() { return 1.0f;//override this if needed, if the progress bar isn't smooth } float AlgorithmLabelDilate::getSubAlgorithmWeight() { //return AlgorithmInsertNameHere::getAlgorithmWeight();//if you use a subalgorithm return 0.0f; } connectome-workbench-1.2.3+git41-gc4c6c90/src/Algorithms/AlgorithmLabelDilate.h000066400000000000000000000034401300200146000270400ustar00rootroot00000000000000#ifndef __ALGORITHM_LABEL_DILATE_H__ #define __ALGORITHM_LABEL_DILATE_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 "AbstractAlgorithm.h" namespace caret { class AlgorithmLabelDilate : public AbstractAlgorithm { AlgorithmLabelDilate(); protected: static float getSubAlgorithmWeight(); static float getAlgorithmInternalWeight(); public: AlgorithmLabelDilate(ProgressObject* myProgObj, const LabelFile* myLabel, const SurfaceFile* mySurf, float myDist, LabelFile* myLabelOut, const MetricFile* badNodeRoi = NULL, int columnNum = -1, const MetricFile* corrAreas = NULL); static OperationParameters* getParameters(); static void useParameters(OperationParameters* myParams, ProgressObject* myProgObj); static AString getCommandSwitch(); static AString getShortDescription(); }; typedef TemplateAutoOperation AutoAlgorithmLabelDilate; } #endif //__ALGORITHM_LABEL_DILATE_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Algorithms/AlgorithmLabelErode.cxx000066400000000000000000000226641300200146000272600ustar00rootroot00000000000000/*LICENSE_START*/ /* * Copyright (C) 2015 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 "AlgorithmLabelErode.h" #include "AlgorithmException.h" #include "GeodesicHelper.h" #include "LabelFile.h" #include "GiftiLabelTable.h" #include "MetricFile.h" #include "SurfaceFile.h" #include "TopologyHelper.h" using namespace caret; using namespace std; AString AlgorithmLabelErode::getCommandSwitch() { return "-label-erode"; } AString AlgorithmLabelErode::getShortDescription() { return "ERODE A LABEL FILE"; } OperationParameters* AlgorithmLabelErode::getParameters() { OperationParameters* ret = new OperationParameters(); ret->addLabelParameter(1, "label", "the input label"); ret->addSurfaceParameter(2, "surface", "the surface to erode on"); ret->addDoubleParameter(3, "erode-dist", "distance in mm to erode the labels"); ret->addLabelOutputParameter(4, "label-out", "the output label file"); OptionalParameter* dataRoiOpt = ret->createOptionalParameter(5, "-roi", "assume values outside this roi are labeled"); dataRoiOpt->addMetricParameter(1, "roi-metric", "metric file, positive values denote vertices that have data"); OptionalParameter* columnSelect = ret->createOptionalParameter(6, "-column", "select a single column to erode"); columnSelect->addStringParameter(1, "column", "the column number or name"); OptionalParameter* corrAreaOpt = ret->createOptionalParameter(7, "-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("Around each vertex that is unlabeled, set surrounding vertices to unlabeled. ") + "The surrounding vertices are all immediate neighbors and all vertices within the specified distance." + "\n\nNote that the -corrected-areas option uses an approximate correction for distance along the surface." ); return ret; } void AlgorithmLabelErode::useParameters(OperationParameters* myParams, ProgressObject* myProgObj) { LabelFile* myLabel = myParams->getLabel(1); SurfaceFile* mySurf = myParams->getSurface(2); float myDist = (float)myParams->getDouble(3); LabelFile* myLabelOut = myParams->getOutputLabel(4); OptionalParameter* roiOpt = myParams->getOptionalParameter(5); MetricFile* myRoi = NULL; if (roiOpt->m_present) { myRoi = roiOpt->getMetric(1); } OptionalParameter* columnSelect = myParams->getOptionalParameter(6); int columnNum = -1; if (columnSelect->m_present) {//set up to use the single column columnNum = (int)myLabel->getMapIndexFromNameOrNumber(columnSelect->getString(1)); if (columnNum < 0) { throw AlgorithmException("invalid column specified"); } } OptionalParameter* corrAreaOpt = myParams->getOptionalParameter(7); MetricFile* corrAreas = NULL; if (corrAreaOpt->m_present) { corrAreas = corrAreaOpt->getMetric(1); } AlgorithmLabelErode(myProgObj, myLabel, mySurf, myDist, myLabelOut, myRoi, columnNum, corrAreas); } namespace { vector > precomputeStencils(const SurfaceFile* mySurf, const float& distance, const MetricFile* corrAreas, const float* roiCol) { int numNodes = mySurf->getNumberOfNodes(); vector > ret(numNodes); CaretPointer myGeoBase; if (corrAreas != NULL) { myGeoBase.grabNew(new GeodesicHelperBase(mySurf, corrAreas->getValuePointerForColumn(0))); } CaretPointer myTopoHelp = mySurf->getTopologyHelper();//we aren't using to depth, so share the topology helper #pragma omp CARET_PAR {//use parallel here so each thread gets its own geodesic helper CaretPointer myGeoHelp; if (corrAreas == NULL) { myGeoHelp = mySurf->getGeodesicHelper(); } else { myGeoHelp.grabNew(new GeodesicHelper(myGeoBase)); } #pragma omp CARET_FOR schedule(dynamic) for (int i = 0; i < numNodes; ++i) { if (roiCol == NULL || roiCol[i] > 0.0f) { vector geoNodes; vector geoDists; myGeoHelp->getNodesToGeoDist(i, distance, geoNodes, geoDists); const vector& topoNodes = myTopoHelp->getNodeNeighbors(i); set mergeSet(geoNodes.begin(), geoNodes.end()); mergeSet.insert(topoNodes.begin(), topoNodes.end()); mergeSet.erase(i);//center of stencil is already 0 if stencil is used, so don't set it again ret[i] = vector(mergeSet.begin(), mergeSet.end()); } } } return ret; } } AlgorithmLabelErode::AlgorithmLabelErode(ProgressObject* myProgObj, const LabelFile* myLabel, const SurfaceFile* mySurf, const float& myDist, LabelFile* myLabelOut, const MetricFile* myRoi, const int& columnNum, const MetricFile* corrAreas) : AbstractAlgorithm(myProgObj) { LevelProgress myProgress(myProgObj); int numNodes = mySurf->getNumberOfNodes(); if (numNodes != myLabel->getNumberOfNodes()) { throw AlgorithmException("surface and metric number of vertices do not match"); } if (myRoi != NULL && myRoi->getNumberOfNodes() != numNodes) { throw AlgorithmException("roi number of vertices does not match"); } if (corrAreas != NULL && corrAreas->getNumberOfNodes() != numNodes) { throw AlgorithmException("corrected areas metric number of vertices does not match"); } int numInColumns = myLabel->getNumberOfColumns(); if (columnNum < -1 || columnNum >= numInColumns) { throw AlgorithmException("invalid column specified"); } if (myDist < 0.0f) { throw AlgorithmException("distance cannot be negative"); } if (columnNum == -1) { myLabelOut->setNumberOfNodesAndColumns(numNodes, numInColumns); } else { myLabelOut->setNumberOfNodesAndColumns(numNodes, 1); } myLabelOut->setStructure(mySurf->getStructure()); const int32_t unlabeledKey = myLabel->getLabelTable()->getUnassignedLabelKey();//will have same key for both *(myLabelOut->getLabelTable()) = *(myLabel->getLabelTable());//file type has only one label table for the whole file const float* roiCol = NULL; if (myRoi != NULL) { roiCol = myRoi->getValuePointerForColumn(0); } vector > stencils = precomputeStencils(mySurf, myDist, corrAreas, roiCol); if (columnNum == -1) { for (int c = 0; c < numInColumns; ++c) { const int32_t* inData = myLabel->getLabelKeyPointerForColumn(c); vector scratchCol(inData, inData + numNodes); for (int i = 0; i < numNodes; ++i) { if (roiCol == NULL || roiCol[i] > 0.0f) { if (inData[i] == unlabeledKey) { for (int n = 0; n < (int)stencils[i].size(); ++n) { scratchCol[stencils[i][n]] = unlabeledKey; } } } else { scratchCol[i] = unlabeledKey;//zero things outside the data roi } } myLabelOut->setMapName(c, myLabel->getMapName(c)); myLabelOut->setLabelKeysForColumn(c, scratchCol.data()); } } else { const int32_t* inData = myLabel->getLabelKeyPointerForColumn(columnNum); vector scratchCol(inData, inData + numNodes); for (int i = 0; i < numNodes; ++i) { if (roiCol == NULL || roiCol[i] > 0.0f) { if (inData[i] == unlabeledKey) { for (int n = 0; n < (int)stencils[i].size(); ++n) { scratchCol[stencils[i][n]] = unlabeledKey; } } } else { scratchCol[i] = unlabeledKey;//zero things outside the data roi } } myLabelOut->setMapName(0, myLabel->getMapName(columnNum)); myLabelOut->setLabelKeysForColumn(0, scratchCol.data()); } } float AlgorithmLabelErode::getAlgorithmInternalWeight() { return 1.0f;//override this if needed, if the progress bar isn't smooth } float AlgorithmLabelErode::getSubAlgorithmWeight() { //return AlgorithmInsertNameHere::getAlgorithmWeight();//if you use a subalgorithm return 0.0f; } connectome-workbench-1.2.3+git41-gc4c6c90/src/Algorithms/AlgorithmLabelErode.h000066400000000000000000000034401300200146000266740ustar00rootroot00000000000000#ifndef __ALGORITHM_LABEL_ERODE_H__ #define __ALGORITHM_LABEL_ERODE_H__ /*LICENSE_START*/ /* * Copyright (C) 2015 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 AlgorithmLabelErode : public AbstractAlgorithm { AlgorithmLabelErode(); protected: static float getSubAlgorithmWeight(); static float getAlgorithmInternalWeight(); public: AlgorithmLabelErode(ProgressObject* myProgObj, const LabelFile* myLabel, const SurfaceFile* mySurf, const float& myDist, LabelFile* myLabelOut, const MetricFile* myRoi = NULL, const int& columnNum = -1, const MetricFile* corrAreas = NULL); static OperationParameters* getParameters(); static void useParameters(OperationParameters* myParams, ProgressObject* myProgObj); static AString getCommandSwitch(); static AString getShortDescription(); }; typedef TemplateAutoOperation AutoAlgorithmLabelErode; } #endif //__ALGORITHM_LABEL_ERODE_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Algorithms/AlgorithmLabelModifyKeys.cxx000066400000000000000000000242051300200146000302760ustar00rootroot00000000000000/*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 "AlgorithmLabelModifyKeys.h" #include "AlgorithmException.h" #include "FileInformation.h" #include "GiftiLabel.h" #include "GiftiLabelTable.h" #include "LabelFile.h" #include #include using namespace caret; using namespace std; AString AlgorithmLabelModifyKeys::getCommandSwitch() { return "-label-modify-keys"; } AString AlgorithmLabelModifyKeys::getShortDescription() { return "CHANGE KEY VALUES IN A LABEL FILE"; } OperationParameters* AlgorithmLabelModifyKeys::getParameters() { OperationParameters* ret = new OperationParameters(); ret->addLabelParameter(1, "label-in", "the input label file"); ret->addStringParameter(2, "remap-file", "text file with old and new key values"); ret->addLabelOutputParameter(3, "label-out", "output label file"); OptionalParameter* columnOpt = ret->createOptionalParameter(4, "-column", "select a single column to use"); columnOpt->addStringParameter(1, "column", "the column number or name"); ret->setHelpText( AString(" should have lines of the form 'oldkey newkey', like so:\n\n") + "3 5\n5 8\n8 2\n\n" + "This would change the current label with key '3' to use the key '5' instead, 5 would use 8, and 8 would use 2. " + "Any collision in key values results in the label that was not specified in the remap file getting remapped to an otherwise unused key. " + "Remapping more than one key to the same new key, or the same key to more than one new key, results in an error. " + "This will not change the appearance of the file when displayed, it will change the keys in the data at the same time." ); return ret; } void AlgorithmLabelModifyKeys::useParameters(OperationParameters* myParams, ProgressObject* myProgObj) { LabelFile* labelIn = myParams->getLabel(1); AString remapName = myParams->getString(2); LabelFile* labelOut = myParams->getOutputLabel(3); int column = -1; OptionalParameter* columnOpt = myParams->getOptionalParameter(4); if (columnOpt->m_present) { column = labelIn->getMapIndexFromNameOrNumber(columnOpt->getString(1)); if (column < 0) throw AlgorithmException("invalid column specified"); } FileInformation textFileInfo(remapName); if (!textFileInfo.exists()) { throw AlgorithmException("label list file doesn't exist"); } fstream remapFile(remapName.toLocal8Bit().constData(), fstream::in); if (!remapFile.good()) { throw AlgorithmException("error reading label list file"); } map remap; int32_t oldkey, newkey; while (remapFile >> oldkey >> newkey) { if (remap.find(oldkey) != remap.end()) throw AlgorithmException("remapping tried to duplicate label " + AString::number(oldkey)); remap[oldkey] = newkey; } AlgorithmLabelModifyKeys(myProgObj, labelIn, remap, labelOut, column); } AlgorithmLabelModifyKeys::AlgorithmLabelModifyKeys(ProgressObject* myProgObj, const LabelFile* labelIn, const map& remap, LabelFile* labelOut, const int& column) : AbstractAlgorithm(myProgObj) { LevelProgress myProgress(myProgObj); const GiftiLabelTable* oldTable = labelIn->getLabelTable(); int32_t oldUnlabeled = oldTable->getUnassignedLabelKey();//because GiftiLabelTable is quirky, we need to check if the unlabeled value ends up as something other than 0 GiftiLabelTable newTable;//beware: key 0 already has the "???" label bool setZero = false;//because of this, we need to track if we overwrote it, so that we can pretend it isn't there for (map::const_iterator iter = remap.begin(); iter != remap.end(); ++iter) { const GiftiLabel* oldLabel = oldTable->getLabel(iter->first); if (oldLabel == NULL) throw AlgorithmException("label " + AString::number(iter->first) + " does not exist in the input label file"); GiftiLabel newLabel(*oldLabel); newLabel.setKey(iter->second); if (iter->first == oldUnlabeled) { if (iter->second != 0)//if it isn't the default unlabeled value, then we have to do something { if (newTable.getLabel(iter->second) != NULL) throw AlgorithmException("remapping tried to set label " + AString::number(iter->second) + " more than once"); newTable.deleteLabel(0);//delete the default, since we don't know what overwrites it, if anything newTable.insertLabel(&newLabel);//insert forces it to use the key in the label, even if it causes a duplicate name (which the original might theoretically have) } else {//otherwise, just error checking if (setZero) throw AlgorithmException("remapping tried to set label " + AString::number(iter->second) + " more than once"); setZero = true;//we do have to track that zero now contains something that can't be overwritten } } else { if (iter->second == 0)//if it remaps to the default unlabeled key, we have to check it differently { if (setZero) throw AlgorithmException("remapping tried to set label " + AString::number(iter->second) + " more than once"); setZero = true; newTable.insertLabel(&newLabel);//insert forces it to use the key in the label, so it will overwrite the existing default 0 key } else {//finally, the simple case if (newTable.getLabel(iter->second) != NULL) throw AlgorithmException("remapping tried to set label " + AString::number(iter->second) + " more than once"); newTable.insertLabel(&newLabel);//insert forces it to use the key in the label, even if it causes a duplicate name (which the original might theoretically have) } } } set keys = oldTable->getKeys(), collisions; for (set::const_iterator iter = keys.begin(); iter != keys.end(); ++iter) { if (remap.find(*iter) == remap.end())//skip if it was remapped { if (*iter == 0)//again with the special default 0 key { if (setZero)//check for collision { collisions.insert(*iter); } else { setZero = true; if (*iter != oldUnlabeled)//if its merely the unassigned label already, we can just keep the existing default { newTable.insertLabel(oldTable->getLabel(*iter)); } } } else { if (newTable.getLabel(*iter) == NULL) { newTable.insertLabel(oldTable->getLabel(*iter)); } else {//collision collisions.insert(*iter); } } } } map valueChanges = remap;//start with the specified changes, then add the collision changes for (set::const_iterator iter = collisions.begin(); iter != collisions.end(); ++iter) {//now deal with collisions int32_t newKey = newTable.generateUnusedKey(); GiftiLabel newLabel(*(oldTable->getLabel(*iter))); newLabel.setKey(newKey); newTable.insertLabel(&newLabel);//insert forces it to use the key in the label, even if it causes a duplicate name (which the original might theoretically have) valueChanges[*iter] = newKey; } int numNodes = labelIn->getNumberOfNodes(), numColumns = labelIn->getNumberOfColumns(); vector scratchCol(numNodes); if (column == -1) { labelOut->setNumberOfNodesAndColumns(numNodes, numColumns); labelOut->setStructure(labelIn->getStructure()); *(labelOut->getLabelTable()) = newTable; for (int i = 0; i < numColumns; ++i) { labelOut->setColumnName(i, labelIn->getColumnName(i)); const int32_t* inCol = labelIn->getLabelKeyPointerForColumn(i); for (int j = 0; j < numNodes; ++j) { map::const_iterator iter = valueChanges.find(inCol[j]); if (iter == valueChanges.end()) { scratchCol[j] = inCol[j]; } else { scratchCol[j] = iter->second; } } labelOut->setLabelKeysForColumn(i, scratchCol.data()); } } else { labelOut->setNumberOfNodesAndColumns(numNodes, 1); labelOut->setStructure(labelIn->getStructure()); *(labelOut->getLabelTable()) = newTable; labelOut->setColumnName(0, labelIn->getColumnName(column)); const int32_t* inCol = labelIn->getLabelKeyPointerForColumn(column); for (int j = 0; j < numNodes; ++j) { map::const_iterator iter = valueChanges.find(inCol[j]); if (iter == valueChanges.end()) { scratchCol[j] = inCol[j]; } else { scratchCol[j] = iter->second; } } labelOut->setLabelKeysForColumn(0, scratchCol.data()); } } float AlgorithmLabelModifyKeys::getAlgorithmInternalWeight() { return 1.0f;//override this if needed, if the progress bar isn't smooth } float AlgorithmLabelModifyKeys::getSubAlgorithmWeight() { //return AlgorithmInsertNameHere::getAlgorithmWeight();//if you use a subalgorithm return 0.0f; } connectome-workbench-1.2.3+git41-gc4c6c90/src/Algorithms/AlgorithmLabelModifyKeys.h000066400000000000000000000033571300200146000277300ustar00rootroot00000000000000#ifndef __ALGORITHM_LABEL_MODIFY_KEYS_H__ #define __ALGORITHM_LABEL_MODIFY_KEYS_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 "AbstractAlgorithm.h" #include namespace caret { class AlgorithmLabelModifyKeys : public AbstractAlgorithm { AlgorithmLabelModifyKeys(); protected: static float getSubAlgorithmWeight(); static float getAlgorithmInternalWeight(); public: AlgorithmLabelModifyKeys(ProgressObject* myProgObj, const LabelFile* labelIn, const std::map& remap, LabelFile* labelOut, const int& column = -1); static OperationParameters* getParameters(); static void useParameters(OperationParameters* myParams, ProgressObject* myProgObj); static AString getCommandSwitch(); static AString getShortDescription(); }; typedef TemplateAutoOperation AutoAlgorithmLabelModifyKeys; } #endif //__ALGORITHM_LABEL_MODIFY_KEYS_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Algorithms/AlgorithmLabelResample.cxx000066400000000000000000000246561300200146000277750ustar00rootroot00000000000000/*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 "AlgorithmLabelResample.h" #include "AlgorithmException.h" #include "CaretLogger.h" #include "GiftiLabelTable.h" #include "LabelFile.h" #include "MetricFile.h" #include "SurfaceFile.h" #include "SurfaceResamplingHelper.h" using namespace caret; using namespace std; AString AlgorithmLabelResample::getCommandSwitch() { return "-label-resample"; } AString AlgorithmLabelResample::getShortDescription() { return "RESAMPLE A LABEL FILE TO A DIFFERENT MESH"; } OperationParameters* AlgorithmLabelResample::getParameters() { OperationParameters* ret = new OperationParameters(); ret->addLabelParameter(1, "label-in", "the label file to resample"); ret->addSurfaceParameter(2, "current-sphere", "a sphere surface with the mesh that the label file is currently on"); ret->addSurfaceParameter(3, "new-sphere", "a sphere surface that is in register with and has the desired output mesh"); ret->addStringParameter(4, "method", "the method name"); ret->addLabelOutputParameter(5, "label-out", "the output label file"); OptionalParameter* areaSurfsOpt = ret->createOptionalParameter(6, "-area-surfs", "specify surfaces to do vertex area correction based on"); areaSurfsOpt->addSurfaceParameter(1, "current-area", "a relevant anatomical surface with mesh"); areaSurfsOpt->addSurfaceParameter(2, "new-area", "a relevant anatomical surface with mesh"); OptionalParameter* areaMetricsOpt = ret->createOptionalParameter(7, "-area-metrics", "specify vertex area metrics to do area correction based on"); areaMetricsOpt->addMetricParameter(1, "current-area", "a metric file with vertex areas for mesh"); areaMetricsOpt->addMetricParameter(2, "new-area", "a metric file with vertex areas for mesh"); OptionalParameter* roiOpt = ret->createOptionalParameter(8, "-current-roi", "use an input roi on the current mesh to exclude non-data vertices"); roiOpt->addMetricParameter(1, "roi-metric", "the roi, as a metric file"); OptionalParameter* validRoiOutOpt = ret->createOptionalParameter(9, "-valid-roi-out", "output the ROI of vertices that got data from valid source vertices"); validRoiOutOpt->addMetricOutputParameter(1, "roi-out", "the output roi as a metric"); ret->createOptionalParameter(10, "-largest", "use only the label of the vertex with the largest weight"); AString myHelpText = AString("Resamples a label file, given two spherical surfaces that are in register. ") + "If ADAP_BARY_AREA is used, exactly one of -area-surfs or -area-metrics must be specified.\n\n" + "The ADAP_BARY_AREA method is recommended for label data, because it should be better at resolving vertices that are near multiple labels, or in case of downsampling. " + "Midthickness surfaces are recommended for the vertex areas for most data.\n\n" + "The -largest option results in nearest vertex behavior when used with BARYCENTRIC, as it uses the value of the source vertex that has the largest weight.\n\n" + "When -largest is not specified, the vertex weights are summed according to which label they correspond to, and the label with the largest sum is used.\n\n" + "The argument must be one of the following:\n\n"; vector allEnums; SurfaceResamplingMethodEnum::getAllEnums(allEnums); for (int i = 0; i < (int)allEnums.size(); ++i) { myHelpText += SurfaceResamplingMethodEnum::toName(allEnums[i]) + "\n"; } ret->setHelpText(myHelpText); return ret; } void AlgorithmLabelResample::useParameters(OperationParameters* myParams, ProgressObject* myProgObj) { LabelFile* labelIn = myParams->getLabel(1); SurfaceFile* curSphere = myParams->getSurface(2); SurfaceFile* newSphere = myParams->getSurface(3); bool ok = false; SurfaceResamplingMethodEnum::Enum myMethod = SurfaceResamplingMethodEnum::fromName(myParams->getString(4), &ok); if (!ok) { throw AlgorithmException("invalid method name"); } LabelFile* labelOut = myParams->getOutputLabel(5); MetricFile* curAreas = NULL, *newAreas = NULL; MetricFile curAreasTemp, newAreasTemp; OptionalParameter* areaSurfsOpt = myParams->getOptionalParameter(6); if (areaSurfsOpt->m_present) { switch(myMethod) { case SurfaceResamplingMethodEnum::BARYCENTRIC: CaretLogInfo("This method does not use area correction, -area-surfs is not needed"); break; default: break; } vector nodeAreasTemp; SurfaceFile* curAreaSurf = areaSurfsOpt->getSurface(1); SurfaceFile* newAreaSurf = areaSurfsOpt->getSurface(2); curAreaSurf->computeNodeAreas(nodeAreasTemp); curAreasTemp.setNumberOfNodesAndColumns(curAreaSurf->getNumberOfNodes(), 1); curAreasTemp.setValuesForColumn(0, nodeAreasTemp.data()); curAreas = &curAreasTemp; newAreaSurf->computeNodeAreas(nodeAreasTemp); newAreasTemp.setNumberOfNodesAndColumns(newAreaSurf->getNumberOfNodes(), 1); newAreasTemp.setValuesForColumn(0, nodeAreasTemp.data()); newAreas = &newAreasTemp; } OptionalParameter* areaMetricsOpt = myParams->getOptionalParameter(7); if (areaMetricsOpt->m_present) { if (areaSurfsOpt->m_present) { throw AlgorithmException("only one of -area-surfs and -area-metrics can be specified"); } switch(myMethod) { case SurfaceResamplingMethodEnum::BARYCENTRIC: CaretLogInfo("This method does not use area correction, -area-metrics is not needed"); break; default: break; } curAreas = areaMetricsOpt->getMetric(1); newAreas = areaMetricsOpt->getMetric(2); } OptionalParameter* roiOpt = myParams->getOptionalParameter(8); MetricFile* currentRoi = NULL; if (roiOpt->m_present) { currentRoi = roiOpt->getMetric(1); } MetricFile* validRoiOut = NULL; OptionalParameter* validRoiOutOpt = myParams->getOptionalParameter(9); if (validRoiOutOpt->m_present) { validRoiOut = validRoiOutOpt->getOutputMetric(1); } bool largest = myParams->getOptionalParameter(10)->m_present; AlgorithmLabelResample(myProgObj, labelIn, curSphere, newSphere, myMethod, labelOut, curAreas, newAreas, currentRoi, validRoiOut, largest); } AlgorithmLabelResample::AlgorithmLabelResample(ProgressObject* myProgObj, const LabelFile* labelIn, const SurfaceFile* curSphere, const SurfaceFile* newSphere, const SurfaceResamplingMethodEnum::Enum& myMethod, LabelFile* labelOut, const MetricFile* curAreas, const MetricFile* newAreas, const MetricFile* currentRoi, MetricFile* validRoiOut, const bool& largest) : AbstractAlgorithm(myProgObj) { LevelProgress myProgress(myProgObj); if (labelIn->getNumberOfNodes() != curSphere->getNumberOfNodes()) throw AlgorithmException("input label file has different number of nodes than input sphere"); const float* curAreaData = NULL, *newAreaData = NULL; switch (myMethod) { case SurfaceResamplingMethodEnum::BARYCENTRIC: break; default: if (curAreas == NULL || newAreas == NULL) throw AlgorithmException("specified method does area correction, but no vertex area data given"); if (curSphere->getNumberOfNodes() != curAreas->getNumberOfNodes()) throw AlgorithmException("current vertex area data has different number of nodes than current sphere"); if (newSphere->getNumberOfNodes() != newAreas->getNumberOfNodes()) throw AlgorithmException("new vertex area data has different number of nodes than new sphere"); curAreaData = curAreas->getValuePointerForColumn(0); newAreaData = newAreas->getValuePointerForColumn(0); } int numColumns = labelIn->getNumberOfColumns(), numNewNodes = newSphere->getNumberOfNodes(); labelOut->setNumberOfNodesAndColumns(numNewNodes, numColumns); labelOut->setStructure(labelIn->getStructure()); *labelOut->getLabelTable() = *labelIn->getLabelTable(); int32_t unusedLabel = labelIn->getLabelTable()->getUnassignedLabelKey(); vector colScratch(numNewNodes, unusedLabel); const float* roiCol = NULL; if (currentRoi != NULL) roiCol = currentRoi->getValuePointerForColumn(0); SurfaceResamplingHelper myHelp(myMethod, curSphere, newSphere, curAreaData, newAreaData, roiCol); if (validRoiOut != NULL) { validRoiOut->setNumberOfNodesAndColumns(numNewNodes, 1); validRoiOut->setStructure(labelIn->getStructure()); vector scratch(numNewNodes); myHelp.getResampleValidROI(scratch.data()); validRoiOut->setValuesForColumn(0, scratch.data()); } for (int i = 0; i < numColumns; ++i) { labelOut->setColumnName(i, labelIn->getColumnName(i)); if (largest) { myHelp.resampleLargest(labelIn->getLabelKeyPointerForColumn(i), colScratch.data(), unusedLabel); } else { myHelp.resamplePopular(labelIn->getLabelKeyPointerForColumn(i), colScratch.data(), unusedLabel); } labelOut->setLabelKeysForColumn(i, colScratch.data()); } } float AlgorithmLabelResample::getAlgorithmInternalWeight() { return 1.0f;//override this if needed, if the progress bar isn't smooth } float AlgorithmLabelResample::getSubAlgorithmWeight() { //return AlgorithmInsertNameHere::getAlgorithmWeight();//if you use a subalgorithm return 0.0f; } connectome-workbench-1.2.3+git41-gc4c6c90/src/Algorithms/AlgorithmLabelResample.h000066400000000000000000000040111300200146000274010ustar00rootroot00000000000000#ifndef __ALGORITHM_LABEL_RESAMPLE_H__ #define __ALGORITHM_LABEL_RESAMPLE_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 "AbstractAlgorithm.h" #include "SurfaceResamplingMethodEnum.h" namespace caret { class AlgorithmLabelResample : public AbstractAlgorithm { AlgorithmLabelResample(); protected: static float getSubAlgorithmWeight(); static float getAlgorithmInternalWeight(); public: AlgorithmLabelResample(ProgressObject* myProgObj, const LabelFile* labelIn, const SurfaceFile* curSphere, const SurfaceFile* newSphere, const SurfaceResamplingMethodEnum::Enum& myMethod, LabelFile* labelOut, const MetricFile* curAreas = NULL, const MetricFile* newAreas = NULL, const MetricFile* currentRoi = NULL, MetricFile* validRoiOut = NULL, const bool& largest = false); static OperationParameters* getParameters(); static void useParameters(OperationParameters* myParams, ProgressObject* myProgObj); static AString getCommandSwitch(); static AString getShortDescription(); }; typedef TemplateAutoOperation AutoAlgorithmLabelResample; } #endif //__ALGORITHM_LABEL_RESAMPLE_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Algorithms/AlgorithmLabelToBorder.cxx000066400000000000000000000150361300200146000277350ustar00rootroot00000000000000/*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 "AlgorithmLabelToBorder.h" #include "AlgorithmException.h" #include "Border.h" #include "BorderFile.h" #include "BorderTracingHelper.h" #include "GiftiLabelTable.h" #include "LabelFile.h" #include "SurfaceFile.h" using namespace caret; using namespace std; AString AlgorithmLabelToBorder::getCommandSwitch() { return "-label-to-border"; } AString AlgorithmLabelToBorder::getShortDescription() { return "DRAW BORDERS AROUND LABELS"; } OperationParameters* AlgorithmLabelToBorder::getParameters() { OperationParameters* ret = new OperationParameters(); ret->addSurfaceParameter(1, "surface", "the surface to use for neighbor information"); ret->addLabelParameter(2, "label-in", "the input label file"); ret->addBorderOutputParameter(3, "border-out", "the output border file"); OptionalParameter* placeOpt = ret->createOptionalParameter(4, "-placement", "set how far along the edge border points are drawn"); placeOpt->addDoubleParameter(1, "fraction", "fraction along edge from inside vertex (default 0.33)"); OptionalParameter* columnSelect = ret->createOptionalParameter(5, "-column", "select a single column"); columnSelect->addStringParameter(1, "column", "the column number or name"); ret->setHelpText( AString("For each label, finds all edges on the mesh that cross the boundary of the label, and draws borders through them. ") + "By default, this is done on all columns in the input file, using the map name as the class name for the border." ); return ret; } void AlgorithmLabelToBorder::useParameters(OperationParameters* myParams, ProgressObject* myProgObj) { SurfaceFile* mySurf = myParams->getSurface(1); LabelFile* myLabel = myParams->getLabel(2); BorderFile* myBorderOut = myParams->getOutputBorder(3); float placement = 0.33f; OptionalParameter* placeOpt = myParams->getOptionalParameter(4); if (placeOpt->m_present) { placement = (float)placeOpt->getDouble(1); } OptionalParameter* columnSelect = myParams->getOptionalParameter(5); int columnNum = -1; if (columnSelect->m_present) {//set up to use the single column columnNum = (int)myLabel->getMapIndexFromNameOrNumber(columnSelect->getString(1)); if (columnNum < 0) { throw AlgorithmException("invalid column specified"); } } AlgorithmLabelToBorder(myProgObj, mySurf, myLabel, myBorderOut, placement, columnNum); } AlgorithmLabelToBorder::AlgorithmLabelToBorder(ProgressObject* myProgObj, const SurfaceFile* mySurf, const LabelFile* myLabel, BorderFile* myBorderOut, const float& placement, const int& columnNum) : AbstractAlgorithm(myProgObj) { LevelProgress myProgress(myProgObj); if (mySurf->getNumberOfNodes() != myLabel->getNumberOfNodes()) throw AlgorithmException("label file does not match surface file number of vertices"); if (placement < 0.0f || placement > 1.0f || placement != placement) throw AlgorithmException("placement must be between 0 and 1"); if (columnNum < -1 || columnNum > myLabel->getNumberOfColumns()) throw AlgorithmException("invalid column specified"); myBorderOut->setStructure(mySurf->getStructure()); myBorderOut->setNumberOfNodes(mySurf->getNumberOfNodes()); BorderTracingHelper myHelper(mySurf); if (columnNum == -1) { for (int col = 0; col < myLabel->getNumberOfColumns(); ++col) { const GiftiLabelTable* myTable = myLabel->getLabelTable(); set myKeys = myTable->getKeys(); int32_t unassignedKey = myTable->getUnassignedLabelKey(); for (set::iterator iter = myKeys.begin(); iter != myKeys.end(); ++iter) { if (*iter == unassignedKey) continue; vector > result = myHelper.traceData(myLabel->getLabelKeyPointerForColumn(col), BorderTracingHelper::LabelSelect(*iter), placement); AString borderName = myTable->getLabelName(*iter); myBorderOut->getNameColorTable()->addLabel(myTable->getLabel(*iter)); for (int i = 0; i < (int)result.size(); ++i) { result[i]->setClassName(myLabel->getMapName(col)); result[i]->setName(borderName); myBorderOut->addBorder(result[i].releasePointer());//NOTE: addBorder takes ownership of a RAW POINTER, shared_ptr won't release the pointer } } } } else { const GiftiLabelTable* myTable = myLabel->getLabelTable(); set myKeys = myTable->getKeys(); int32_t unassignedKey = myTable->getUnassignedLabelKey(); for (set::iterator iter = myKeys.begin(); iter != myKeys.end(); ++iter) { if (*iter == unassignedKey) continue; vector > result = myHelper.traceData(myLabel->getLabelKeyPointerForColumn(columnNum), BorderTracingHelper::LabelSelect(*iter), placement); AString borderName = myTable->getLabelName(*iter); myBorderOut->getNameColorTable()->addLabel(myTable->getLabel(*iter)); for (int i = 0; i < (int)result.size(); ++i) { result[i]->setClassName(myLabel->getMapName(columnNum)); result[i]->setName(borderName); myBorderOut->addBorder(result[i].releasePointer());//ditto } } } } float AlgorithmLabelToBorder::getAlgorithmInternalWeight() { return 1.0f;//override this if needed, if the progress bar isn't smooth } float AlgorithmLabelToBorder::getSubAlgorithmWeight() { //return AlgorithmInsertNameHere::getAlgorithmWeight();//if you use a subalgorithm return 0.0f; } connectome-workbench-1.2.3+git41-gc4c6c90/src/Algorithms/AlgorithmLabelToBorder.h000066400000000000000000000034071300200146000273610ustar00rootroot00000000000000#ifndef __ALGORITHM_LABEL_TO_BORDER_H__ #define __ALGORITHM_LABEL_TO_BORDER_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 "AbstractAlgorithm.h" namespace caret { class AlgorithmLabelToBorder : public AbstractAlgorithm { AlgorithmLabelToBorder(); protected: static float getSubAlgorithmWeight(); static float getAlgorithmInternalWeight(); public: AlgorithmLabelToBorder(ProgressObject* myProgObj, const SurfaceFile* mySurf, const LabelFile* myLabel, BorderFile* myBorderOut, const float& placement = 0.33f, const int& columnNum = -1); static OperationParameters* getParameters(); static void useParameters(OperationParameters* myParams, ProgressObject* myProgObj); static AString getCommandSwitch(); static AString getShortDescription(); }; typedef TemplateAutoOperation AutoAlgorithmLabelToBorder; } #endif //__ALGORITHM_LABEL_TO_BORDER_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Algorithms/AlgorithmLabelToVolumeMapping.cxx000066400000000000000000000264311300200146000313040ustar00rootroot00000000000000/*LICENSE_START*/ /* * Copyright (C) 2015 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 "AlgorithmLabelToVolumeMapping.h" #include "AlgorithmException.h" #include "CaretAssert.h" #include "CaretOMP.h" #include "CaretPointLocator.h" #include "LabelFile.h" #include "GiftiLabelTable.h" #include "RibbonMappingHelper.h" #include "SurfaceFile.h" #include "VolumeFile.h" using namespace caret; using namespace std; AString AlgorithmLabelToVolumeMapping::getCommandSwitch() { return "-label-to-volume-mapping"; } AString AlgorithmLabelToVolumeMapping::getShortDescription() { return "MAP LABEL FILE TO VOLUME"; } OperationParameters* AlgorithmLabelToVolumeMapping::getParameters() { OperationParameters* ret = new OperationParameters(); ret->addLabelParameter(1, "label", "the input label file"); ret->addSurfaceParameter(2, "surface", "the surface to use coordinates from"); ret->addVolumeParameter(3, "volume-space", "a volume file in the desired output volume space"); ret->addVolumeOutputParameter(4, "volume-out", "the output volume file"); OptionalParameter* nearestVertOpt = ret->createOptionalParameter(5, "-nearest-vertex", "use the label from the vertex closest to the voxel center"); nearestVertOpt->addDoubleParameter(1, "distance", "how far from the surface to map labels to voxels, in mm"); OptionalParameter* ribbonOpt = ret->createOptionalParameter(6, "-ribbon-constrained", "use ribbon constrained mapping algorithm"); ribbonOpt->addSurfaceParameter(1, "inner-surf", "the inner surface of the ribbon"); 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"); 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. " ); return ret; } void AlgorithmLabelToVolumeMapping::useParameters(OperationParameters* myParams, ProgressObject* myProgObj) { LabelFile* myLabel = myParams->getLabel(1); SurfaceFile* mySurf = myParams->getSurface(2); VolumeFile* myTemplateVol = myParams->getVolume(3); VolumeFile* myVolOut = myParams->getOutputVolume(4); enum Method { INVALID, NEAREST, RIBBON }; bool haveMethod = false; Method myMethod = INVALID; float nearDist = -1.0f; OptionalParameter* nearestVertOpt = myParams->getOptionalParameter(5); if (nearestVertOpt->m_present) { myMethod = NEAREST; haveMethod = true; nearDist = (float)nearestVertOpt->getDouble(1); if (nearDist < 0.0f) { throw AlgorithmException("invalid distance specified"); } } SurfaceFile* innerSurf = NULL, *outerSurf = NULL; int subDivs = 3; OptionalParameter* ribbonOpt = myParams->getOptionalParameter(6); if (ribbonOpt->m_present) { if (haveMethod) { throw AlgorithmException("more than one mapping method specified"); } myMethod = RIBBON; haveMethod = true; innerSurf = ribbonOpt->getSurface(1); outerSurf = ribbonOpt->getSurface(2); OptionalParameter* ribbonSubdivOpt = ribbonOpt->getOptionalParameter(3); if (ribbonSubdivOpt->m_present) { subDivs = (int)ribbonSubdivOpt->getInteger(1); if (subDivs < 1) { throw AlgorithmException("invalid number of subdivisions specified"); } } } if (!haveMethod) { throw AlgorithmException("no mapping method specified"); } switch (myMethod) { case NEAREST: AlgorithmLabelToVolumeMapping(myProgObj, myLabel, mySurf, myTemplateVol->getVolumeSpace(), myVolOut, nearDist); break; case RIBBON: AlgorithmLabelToVolumeMapping(myProgObj, myLabel, mySurf, myTemplateVol->getVolumeSpace(), myVolOut, innerSurf, outerSurf, subDivs); break; case INVALID: CaretAssert(0); throw AlgorithmException("internal error, tell the developers what you just tried to do"); } } AlgorithmLabelToVolumeMapping::AlgorithmLabelToVolumeMapping(ProgressObject* myProgObj, const LabelFile* myLabel, const SurfaceFile* mySurf, const VolumeSpace& myVolSpace, VolumeFile* myVolOut, const float& nearDist) : AbstractAlgorithm(myProgObj) { LevelProgress myProgress(myProgObj); if (myLabel->getNumberOfNodes() != mySurf->getNumberOfNodes()) { throw AlgorithmException("input surface and label file have different number of vertices"); } if (nearDist < 0.0f) { throw AlgorithmException("invalid distance specified in surface to volume mapping"); } checkStructureMatch(myLabel, mySurf->getStructure(), "input label file", "the surface has"); int numCols = myLabel->getNumberOfColumns(); myVolOut->reinitialize(myVolSpace, numCols, 1, SubvolumeAttributes::LABEL); const int64_t* dims = myVolSpace.getDims(); const int64_t frameSize = dims[0] * dims[1] * dims[2]; vector voxelToVertex(frameSize); CaretPointer myLocator = mySurf->getPointLocator(); #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) { float voxelCoord[3]; myVolSpace.indexToSpace(i, j, k, voxelCoord); voxelToVertex[myVolSpace.getIndex(i, j, k)] = myLocator->closestPointLimited(voxelCoord, nearDist); } } } vector scratchFrame(frameSize, 0.0f); for (int i = 0; i < numCols; ++i) { const int32_t* labelData = myLabel->getLabelKeyPointerForColumn(i); for (int64_t v = 0; v < frameSize; ++v) { if (voxelToVertex[v] >= 0) { scratchFrame[v] = labelData[voxelToVertex[v]]; } } myVolOut->setFrame(scratchFrame.data(), i); *(myVolOut->getMapLabelTable(i)) = *(myLabel->getLabelTable()); myVolOut->setMapName(i, myLabel->getMapName(i)); } } 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) { int numNodes = mySurf->getNumberOfNodes(); if (myLabel->getNumberOfNodes() != numNodes) { throw AlgorithmException("label file and input surfaces have different number of vertices"); } if (!mySurf->hasNodeCorrespondence(*outerSurf) || !mySurf->hasNodeCorrespondence(*innerSurf)) { throw AlgorithmException("all surfaces must have vertex correspondence"); } if (subDivs < 0.0f) { throw AlgorithmException("invalid number of subdivisions specified in surface to volume mapping"); } checkStructureMatch(myLabel, mySurf->getStructure(), "input label file", "the surface has"); checkStructureMatch(innerSurf, myLabel->getStructure(), "inner surface file", "the label file has"); checkStructureMatch(outerSurf, myLabel->getStructure(), "outer surface file", "the label file has"); int numCols = myLabel->getNumberOfColumns(); myVolOut->reinitialize(myVolSpace, numCols, 1, SubvolumeAttributes::LABEL); vector > forwardWeights; RibbonMappingHelper::computeWeightsRibbon(forwardWeights, myVolSpace, innerSurf, outerSurf, NULL, subDivs); map > > reverseWeights; for (int i = 0; i < numNodes; ++i) { for (int v = 0; v < (int)forwardWeights[i].size(); ++v) {//vectors initialize to empty reverseWeights[VoxelIJK(forwardWeights[i][v].ijk)].push_back(pair(i, forwardWeights[i][v].weight)); } } const int64_t* dims = myVolSpace.getDims(); const int64_t frameSize = dims[0] * dims[1] * dims[2]; const int32_t unlabeledVal = myLabel->getLabelTable()->getUnassignedLabelKey(); vector scratchFrame(frameSize, unlabeledVal); for (int m = 0; m < numCols; ++m) { const int32_t* colData = myLabel->getLabelKeyPointerForColumn(m); for (map > >::const_iterator iter = reverseWeights.begin(); iter != reverseWeights.end(); ++iter) { map totals; const vector >& vertWeightsRef = iter->second; for (int i = 0; i < (int)vertWeightsRef.size(); ++i) { map::iterator iter2 = totals.find(colData[vertWeightsRef[i].first]); if (iter2 == totals.end()) {//because floats don't initialize to 0 totals[colData[vertWeightsRef[i].first]] = vertWeightsRef[i].second; } else { iter2->second += vertWeightsRef[i].second; } } float bestWeight = -1.0f; int32_t bestLabel = unlabeledVal; 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; } myVolOut->setFrame(scratchFrame.data(), m); *(myVolOut->getMapLabelTable(m)) = *(myLabel->getLabelTable()); myVolOut->setMapName(m, myLabel->getMapName(m)); } } float AlgorithmLabelToVolumeMapping::getAlgorithmInternalWeight() { return 1.0f;//override this if needed, if the progress bar isn't smooth } float AlgorithmLabelToVolumeMapping::getSubAlgorithmWeight() { //return AlgorithmInsertNameHere::getAlgorithmWeight();//if you use a subalgorithm return 0.0f; } connectome-workbench-1.2.3+git41-gc4c6c90/src/Algorithms/AlgorithmLabelToVolumeMapping.h000066400000000000000000000042031300200146000307220ustar00rootroot00000000000000#ifndef __ALGORITHM_LABEL_TO_VOLUME_MAPPING_H__ #define __ALGORITHM_LABEL_TO_VOLUME_MAPPING_H__ /*LICENSE_START*/ /* * Copyright (C) 2015 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 VolumeSpace; class AlgorithmLabelToVolumeMapping : public AbstractAlgorithm { AlgorithmLabelToVolumeMapping(); protected: static float getSubAlgorithmWeight(); static float getAlgorithmInternalWeight(); public: 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); static OperationParameters* getParameters(); static void useParameters(OperationParameters* myParams, ProgressObject* myProgObj); static AString getCommandSwitch(); static AString getShortDescription(); }; typedef TemplateAutoOperation AutoAlgorithmLabelToVolumeMapping; } #endif //__ALGORITHM_LABEL_TO_VOLUME_MAPPING_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Algorithms/AlgorithmMetricDilate.cxx000066400000000000000000001033411300200146000276200ustar00rootroot00000000000000/*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 "AlgorithmMetricDilate.h" #include "AlgorithmException.h" #include "CaretAssert.h" #include "CaretOMP.h" #include "GeodesicHelper.h" #include "MetricFile.h" #include "PaletteColorMapping.h" #include "SurfaceFile.h" #include "TopologyHelper.h" #include "Vector3D.h" #include #include using namespace caret; using namespace std; AString AlgorithmMetricDilate::getCommandSwitch() { return "-metric-dilate"; } AString AlgorithmMetricDilate::getShortDescription() { return "DILATE A METRIC FILE"; } OperationParameters* AlgorithmMetricDilate::getParameters() { OperationParameters* ret = new OperationParameters(); ret->addMetricParameter(1, "metric", "the metric to dilate"); ret->addSurfaceParameter(2, "surface", "the surface to compute on"); ret->addDoubleParameter(3, "distance", "distance in mm to dilate"); ret->addMetricOutputParameter(4, "metric-out", "the output metric"); OptionalParameter* badRoiOpt = ret->createOptionalParameter(5, "-bad-vertex-roi", "specify an roi of vertices to overwrite, rather than vertices with value zero"); badRoiOpt->addMetricParameter(1, "roi-metric", "metric file, positive values denote vertices to have their values replaced"); OptionalParameter* dataRoiOpt = ret->createOptionalParameter(9, "-data-roi", "specify an roi of where there is data"); dataRoiOpt->addMetricParameter(1, "roi-metric", "metric file, positive values denote vertices that have data"); OptionalParameter* columnSelect = ret->createOptionalParameter(6, "-column", "select a single column to dilate"); columnSelect->addStringParameter(1, "column", "the column number or name"); ret->createOptionalParameter(7, "-nearest", "use the nearest good value instead of a weighted average"); ret->createOptionalParameter(10, "-linear", "fill in values with linear interpolation along strongest gradient"); OptionalParameter* exponentOpt = ret->createOptionalParameter(8, "-exponent", "use a different exponent in the weighting function"); exponentOpt->addDoubleParameter(1, "exponent", "exponent 'n' to use in (area / (distance ^ n)) as the weighting function (default 2)"); OptionalParameter* corrAreaOpt = ret->createOptionalParameter(11, "-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 all metric vertices that are designated as bad, if they neighbor a non-bad vertex with data or are within the specified distance of such a vertex, ") + "replace the value with a distance weighted average of nearby non-bad vertices that have data, otherwise set the value to zero. " + "No matter how small is, dilation will always use at least the immediate neighbor vertices. " + "If -nearest is specified, it will use the value from the closest non-bad vertex with data within range instead of a weighted average.\n\n" + "If -bad-vertex-roi is specified, only vertices with a positive value in the ROI are bad. " + "If it is not specified, only vertices that have data, with a value of zero, are bad. " + "If -data-roi is not specified, all vertices are assumed to have data.\n\n" + "Note that the -corrected-areas option uses an approximate correction for the change in distances along a group average surface." ); return ret; } void AlgorithmMetricDilate::useParameters(OperationParameters* myParams, ProgressObject* myProgObj) { MetricFile* myMetric = myParams->getMetric(1); SurfaceFile* mySurf = myParams->getSurface(2); float distance = (float)myParams->getDouble(3); MetricFile* myMetricOut = myParams->getOutputMetric(4); OptionalParameter* badRoiOpt = myParams->getOptionalParameter(5); MetricFile* badNodeRoi = NULL; if (badRoiOpt->m_present) { badNodeRoi = badRoiOpt->getMetric(1); } OptionalParameter* dataRoiOpt = myParams->getOptionalParameter(9); MetricFile* dataRoi = NULL; if (dataRoiOpt->m_present) { dataRoi = dataRoiOpt->getMetric(1); } OptionalParameter* columnSelect = myParams->getOptionalParameter(6); int columnNum = -1; if (columnSelect->m_present) {//set up to use the single column columnNum = (int)myMetric->getMapIndexFromNameOrNumber(columnSelect->getString(1)); if (columnNum < 0) { throw AlgorithmException("invalid column specified"); } } Method myMethod = WEIGHTED; bool methodSpecified = false; if (myParams->getOptionalParameter(7)->m_present) { methodSpecified = true; myMethod = NEAREST; } if (myParams->getOptionalParameter(10)->m_present) { if (methodSpecified) throw AlgorithmException("-nearest and -linear may not be specified together"); methodSpecified = true; myMethod = LINEAR; } float exponent = 2.0f; OptionalParameter* exponentOpt = myParams->getOptionalParameter(8); if (exponentOpt->m_present) { exponent = (float)exponentOpt->getDouble(1); } OptionalParameter* corrAreaOpt = myParams->getOptionalParameter(11); MetricFile* corrAreas = NULL; if (corrAreaOpt->m_present) { corrAreas = corrAreaOpt->getMetric(1); } AlgorithmMetricDilate(myProgObj, myMetric, mySurf, distance, myMetricOut, badNodeRoi, dataRoi, columnNum, myMethod, exponent, corrAreas); } AlgorithmMetricDilate::AlgorithmMetricDilate(ProgressObject* myProgObj, const MetricFile* myMetric, const SurfaceFile* mySurf, const float& distance, MetricFile* myMetricOut, const MetricFile* badNodeRoi, const MetricFile* dataRoi, const int& columnNum, const Method& myMethod, const float& exponent, const MetricFile* corrAreas) : AbstractAlgorithm(myProgObj) { LevelProgress myProgress(myProgObj); int numNodes = mySurf->getNumberOfNodes(); if (numNodes != myMetric->getNumberOfNodes()) { throw AlgorithmException("surface and metric number of vertices do not match"); } if (badNodeRoi != NULL && badNodeRoi->getNumberOfNodes() != numNodes) { throw AlgorithmException("bad vertex roi number of vertices does not match"); } if (dataRoi != NULL && dataRoi->getNumberOfNodes() != numNodes) { throw AlgorithmException("data roi number of vertices does not match"); } if (corrAreas != NULL && corrAreas->getNumberOfNodes() != numNodes) { throw AlgorithmException("corrected areas metric number of vertices does not match"); } if (columnNum < -1 || columnNum >= myMetric->getNumberOfColumns()) { throw AlgorithmException("invalid column specified"); } if (distance < 0.0f) { throw AlgorithmException("distance cannot be negative"); } myMetricOut->setStructure(mySurf->getStructure()); vector > myStencils;//because we need to iterate over it in parallel vector > myNearest; vector colScratch(numNodes); vector myAreasData; const float* myAreas = NULL; if (corrAreas == NULL) { mySurf->computeNodeAreas(myAreasData); myAreas = myAreasData.data(); } else { myAreas = corrAreas->getValuePointerForColumn(0); } bool linear = (myMethod == LINEAR), nearest = (myMethod == NEAREST); if (!linear && badNodeRoi != NULL)//if we know which nodes need to have their values replaced, then we can do the same thing at each vertex for each column { if (nearest) { precomputeNearest(myNearest, mySurf, badNodeRoi, dataRoi, corrAreas, distance); } else { precomputeStencils(myStencils, mySurf, myAreas, badNodeRoi, dataRoi, corrAreas, distance, exponent); } } if (columnNum == -1) { myMetricOut->setNumberOfNodesAndColumns(numNodes, myMetric->getNumberOfColumns()); for (int thisCol = 0; thisCol < myMetric->getNumberOfColumns(); ++thisCol) { *(myMetricOut->getMapPaletteColorMapping(thisCol)) = *(myMetric->getMapPaletteColorMapping(thisCol)); const float* myInputData = myMetric->getValuePointerForColumn(thisCol); myMetricOut->setColumnName(thisCol, myMetric->getColumnName(thisCol)); if (badNodeRoi == NULL) { processColumn(colScratch.data(), myInputData, mySurf, myAreas, badNodeRoi, dataRoi, corrAreas, distance, nearest, linear, exponent); } else { switch (myMethod) { case NEAREST: processColumn(colScratch.data(), numNodes, myInputData, myNearest); break; case WEIGHTED: processColumn(colScratch.data(), numNodes, myInputData, myStencils); break; case LINEAR: processColumn(colScratch.data(), myInputData, mySurf, myAreas, badNodeRoi, dataRoi, corrAreas, distance, nearest, linear, exponent); break; } } myMetricOut->setValuesForColumn(thisCol, colScratch.data()); } } else { myMetricOut->setNumberOfNodesAndColumns(numNodes, 1); *(myMetricOut->getMapPaletteColorMapping(0)) = *(myMetric->getMapPaletteColorMapping(columnNum)); const float* myInputData = myMetric->getValuePointerForColumn(columnNum); myMetricOut->setColumnName(0, myMetric->getColumnName(columnNum)); if (badNodeRoi == NULL) { processColumn(colScratch.data(), myInputData, mySurf, myAreas, badNodeRoi, dataRoi, corrAreas, distance, nearest, linear, exponent); } else { switch (myMethod) { case NEAREST: processColumn(colScratch.data(), numNodes, myInputData, myNearest); break; case WEIGHTED: processColumn(colScratch.data(), numNodes, myInputData, myStencils); break; case LINEAR: processColumn(colScratch.data(), myInputData, mySurf, myAreas, badNodeRoi, dataRoi, corrAreas, distance, nearest, linear, exponent); break; } } myMetricOut->setValuesForColumn(0, colScratch.data()); } } void AlgorithmMetricDilate::processColumn(float* colScratch, const int& numNodes, const float* myInputData, vector > myNearest) { for (int i = 0; i < numNodes; ++i) { colScratch[i] = myInputData[i];//precopy so that the parallel part doesn't have to worry about vertices that don't get dilated to } int numStencils = (int)myNearest.size(); #pragma omp CARET_PARFOR schedule(dynamic) for (int i = 0; i < numStencils; ++i)//parallel may not matter here, but we do other stuff parallel, so... { const int& node = myNearest[i].first; const int& nearest = myNearest[i].second; if (nearest != -1) { colScratch[node] = myInputData[nearest]; } else { colScratch[node] = 0.0f; } } } void AlgorithmMetricDilate::processColumn(float* colScratch, const int& numNodes, const float* myInputData, vector > myStencils) { for (int i = 0; i < numNodes; ++i) { colScratch[i] = myInputData[i];//precopy so that the parallel part doesn't have to worry about vertices that don't get dilated to } int numStencils = (int)myStencils.size(); #pragma omp CARET_PARFOR schedule(dynamic) for (int i = 0; i < numStencils; ++i)//ditto { const int& node = myStencils[i].first; const StencilElem& stencil = myStencils[i].second; int numWeights = (int)stencil.m_weightlist.size(); if (numWeights > 0) { double accum = 0.0; for (int j = 0; j < numWeights; ++j) { accum += myInputData[stencil.m_weightlist[j].first] * stencil.m_weightlist[j].second; } colScratch[node] = accum / stencil.m_weightsum; } else { colScratch[node] = 0.0f; } } } void AlgorithmMetricDilate::processColumn(float* colScratch, const float* myInputData, const SurfaceFile* mySurf, const float* myAreas, const MetricFile* badNodeRoi, const MetricFile* dataRoi, const MetricFile* corrAreas, const float& distance, const bool& nearest, const bool& linear, const float& exponent) { float cutoffRatio = 1.5f, test = pow(10.0f, 1.0f / exponent);//find what cutoff ratio corresponds to a tenth of weight, but don't use more than a 1.5 * nearest cutoff if (test > 1.0f && test < cutoffRatio)//if it is less than 1, the exponent is weird, so simply ignore it and use default { if (test > 1.1f) { cutoffRatio = test; } else { cutoffRatio = 1.1f; } } int numNodes = mySurf->getNumberOfNodes(); vector charRoi(numNodes); const float* badRoiData = NULL; if (badNodeRoi != NULL) badRoiData = badNodeRoi->getValuePointerForColumn(0); const float* dataRoiVals = NULL; if (dataRoi != NULL) { dataRoiVals = dataRoi->getValuePointerForColumn(0); if (badRoiData != NULL) { for (int i = 0; i < numNodes; ++i) { if (dataRoiVals[i] > 0.0f && !(badRoiData[i] > 0.0f))//"not greater than" to trap NaNs { charRoi[i] = 1; } else { charRoi[i] = 0; } } } else { for (int i = 0; i < numNodes; ++i) { if (dataRoiVals[i] > 0.0f && myInputData[i] != 0.0f) { charRoi[i] = 1; } else { charRoi[i] = 0; } } } } else { if (badRoiData != NULL) { for (int i = 0; i < numNodes; ++i) { if (!(badRoiData[i] > 0.0f))//ditto { charRoi[i] = 1; } else { charRoi[i] = 0; } } } else { for (int i = 0; i < numNodes; ++i) { if (myInputData[i] != 0.0f) { charRoi[i] = 1; } else { charRoi[i] = 0; } } } } CaretPointer correctedBase; if (corrAreas != NULL) { correctedBase.grabNew(new GeodesicHelperBase(mySurf, corrAreas->getValuePointerForColumn(0)));//NOTE: myAreas also points to this when applicable } #pragma omp CARET_PAR { CaretPointer myTopoHelp = mySurf->getTopologyHelper(); CaretPointer myGeoHelp; if (corrAreas == NULL) { myGeoHelp = mySurf->getGeodesicHelper(); } else { myGeoHelp.grabNew(new GeodesicHelper(correctedBase)); } #pragma omp CARET_FOR schedule(dynamic) for (int i = 0; i < numNodes; ++i) { bool badNode; if (badRoiData != NULL) { badNode = (badRoiData[i] > 0.0f); } else { badNode = (myInputData[i] == 0.0f); } if ((dataRoiVals == NULL || dataRoiVals[i] > 0.0f) && badNode) { float closestDist;//NOTE: the only time this function is called with a badRoi is when using linear, which doesn't use the closest distance int closestNode = myGeoHelp->getClosestNodeInRoi(i, charRoi.data(), distance, closestDist); if (closestNode == -1)//check neighbors, to ensure we dilate by at least one node everywhere { const vector& nodeList = myTopoHelp->getNodeNeighbors(i); vector distList; myGeoHelp->getGeoToTheseNodes(i, nodeList, distList);//ok, its a little silly to do this const int numInRange = (int)nodeList.size(); for (int j = 0; j < numInRange; ++j) { if (charRoi[nodeList[j]] != 0 && (closestNode == -1 || distList[j] < closestDist)) { closestNode = nodeList[j]; closestDist = distList[j]; } } } if (closestNode == -1) { colScratch[i] = 0.0f; } else { if (nearest) { colScratch[i] = myInputData[closestNode]; } else { vector nodeList; vector distList; if (linear) { myGeoHelp->getNodesToGeoDist(i, distance, nodeList, distList); int numInRange = (int)nodeList.size(); Vector3D center = mySurf->getCoordinate(i); vector blockDists; vector blockPath; vector usableNodes; vector usableDists; for (int j = 0; j < numInRange; ++j)//prescan what is usable, and also exclude things that are through a valid node { if (badRoiData != NULL) { badNode = (badRoiData[nodeList[j]] > 0.0f); } else { badNode = (myInputData[nodeList[j]] == 0.0f); } if ((dataRoiVals == NULL || dataRoiVals[nodeList[j]] > 0.0f) && !badNode) { myGeoHelp->getPathAlongLineSegment(i, nodeList[j], center, mySurf->getCoordinate(nodeList[j]), blockPath, blockDists); CaretAssert(blockPath.size() > 0 && blockPath[0] == i);//we already know that i is "bad", skip it bool usable = true; for (int k = 1; k < (int)blockPath.size() - 1; ++k)//and don't test the endpoint { if (dataRoiVals == NULL || dataRoiVals[blockPath[k]] > 0.0f) { if (badRoiData != NULL) { if (!(badRoiData[blockPath[k]] > 0.0f))//"not greater than" to trap NaNs { usable = false; break; } } else { if (myInputData[blockPath[k]] != 0.0f) { usable = false; break; } } } } if (usable) { usableNodes.push_back(nodeList[j]); usableDists.push_back(distList[j]); } } } int numUsable = (int)usableNodes.size(); float bestGradient = -1.0f; int bestj = -1, bestk = -1; for (int j = 0; j < numUsable; ++j) { int node1 = usableNodes[j]; for (int k = j + 1; k < numUsable; ++k) { int node2 = usableNodes[k]; float grad = abs(myInputData[node1] - myInputData[node2]) / (usableDists[j] + usableDists[k]); if (grad > bestGradient) { bestGradient = grad; bestj = j; bestk = k; } } } if (bestj == -1) { colScratch[i] = myInputData[closestNode]; } else { int node1 = usableNodes[bestj], node2 = usableNodes[bestk]; colScratch[i] = myInputData[node1] + (myInputData[node2] - myInputData[node1]) * usableDists[bestj] / (usableDists[bestj] + usableDists[bestk]); } } else { myGeoHelp->getNodesToGeoDist(i, closestDist * cutoffRatio, nodeList, distList);//NOTE: guaranteed to find at least the closest node int numInRange = (int)nodeList.size(); float totalWeight = 0.0f, weightedSum = 0.0f; for (int j = 0; j < numInRange; ++j) { if (charRoi[nodeList[j]] != 0) { float weight; const float tolerance = 0.9f;//distances should NEVER be less than closestDist, for obvious reasons float divdist = distList[j] / closestDist; if (divdist > tolerance)//tricky: if closestDist is zero, this filters between NaN and inf, resulting in a straight average between nodes with 0 distance { weight = myAreas[nodeList[j]] / pow(divdist, exponent);//NOTE: myAreas has already been pointed to the right data with -corrected-areas } else { weight = myAreas[nodeList[j]] / pow(tolerance, exponent); } totalWeight += weight; weightedSum += myInputData[nodeList[j]] * weight; } } if (totalWeight != 0.0f) { colScratch[i] = weightedSum / totalWeight; } else { colScratch[i] = 0.0f; } } } } } else { colScratch[i] = myInputData[i]; } } } } void AlgorithmMetricDilate::precomputeStencils(vector >& myStencils, const SurfaceFile* mySurf, const float* myAreas, const MetricFile* badNodeRoi, const MetricFile* dataRoi, const MetricFile* corrAreas, const float& distance, const float& exponent) { CaretAssert(badNodeRoi != NULL);//because it should never be called if we don't know exactly what nodes we are replacing const float* badNodeData = badNodeRoi->getValuePointerForColumn(0); float cutoffRatio = 1.5f, test = pow(10.0f, 1.0f / exponent);//find what cutoff ratio corresponds to a tenth of weight, but don't use more than a 1.5 * nearest cutoff if (test > 1.0f && test < cutoffRatio)//if it is less than 1, the exponent is weird, so simply ignore it and use default { if (test > 1.1f) { cutoffRatio = test; } else { cutoffRatio = 1.1f; } } int numNodes = mySurf->getNumberOfNodes(); vector charRoi(numNodes); const float* dataRoiVals = NULL; int badCount = 0; if (dataRoi != NULL) { dataRoiVals = dataRoi->getValuePointerForColumn(0); for (int i = 0; i < numNodes; ++i) { if (!(badNodeData[i] > 0.0f))//in case some clown uses NaN in the ROI instead of 0 { if (dataRoiVals[i] > 0.0f) { charRoi[i] = 1; } else { charRoi[i] = 0; } } else { if (dataRoiVals[i] > 0.0f) { ++badCount; } charRoi[i] = 0; } } } else { for (int i = 0; i < numNodes; ++i) { if (!(badNodeData[i] > 0.0f)) { charRoi[i] = 1; } else { ++badCount; charRoi[i] = 0; } } } myStencils.resize(badCount);//initializes all stencils to have empty lists badCount = 0; CaretPointer correctedBase; if (corrAreas != NULL) { correctedBase.grabNew(new GeodesicHelperBase(mySurf, corrAreas->getValuePointerForColumn(0)));//NOTE: myAreas also points to this when applicable } #pragma omp CARET_PAR { CaretPointer myTopoHelp = mySurf->getTopologyHelper(); CaretPointer myGeoHelp; if (corrAreas == NULL) { myGeoHelp = mySurf->getGeodesicHelper(); } else { myGeoHelp.grabNew(new GeodesicHelper(correctedBase)); } #pragma omp CARET_FOR schedule(dynamic) for (int i = 0; i < numNodes; ++i) { if (badNodeData[i] > 0.0f && (dataRoiVals == NULL || dataRoiVals[i] > 0.0f)) { int myIndex; #pragma omp critical { myIndex = badCount; ++badCount; } myStencils[myIndex].first = i; StencilElem& myElem = myStencils[myIndex].second; float closestDist; int closestNode = myGeoHelp->getClosestNodeInRoi(i, charRoi.data(), distance, closestDist); if (closestNode == -1)//check neighbors, to ensure we dilate by at least one node everywhere { const vector& nodeList = myTopoHelp->getNodeNeighbors(i); vector distList; myGeoHelp->getGeoToTheseNodes(i, nodeList, distList);//ok, its a little silly to do this const int numInRange = (int)nodeList.size(); for (int j = 0; j < numInRange; ++j) { if (charRoi[nodeList[j]] != 0 && (closestNode == -1 || distList[j] < closestDist)) { closestNode = nodeList[j]; closestDist = distList[j]; } } } if (closestNode != -1) { vector nodeList; vector distList; myGeoHelp->getNodesToGeoDist(i, closestDist * cutoffRatio, nodeList, distList); int numInRange = (int)nodeList.size(); myElem.m_weightsum = 0.0f; for (int j = 0; j < numInRange; ++j) { if (charRoi[nodeList[j]] != 0) { float weight; const float tolerance = 0.9f;//distances should NEVER be less than closestDist, for obvious reasons float divdist = distList[j] / closestDist; if (divdist > tolerance)//tricky: if closestDist is zero, this filters between NaN and inf, resulting in a straight average between nodes with 0 distance { weight = myAreas[nodeList[j]] / pow(divdist, exponent);//NOTE: myAreas has already been pointed to the right data with -corrected-areas } else { weight = myAreas[nodeList[j]] / pow(tolerance, exponent); } myElem.m_weightsum += weight; myElem.m_weightlist.push_back(pair(nodeList[j], weight)); } } if (myElem.m_weightsum == 0.0f)//set list to empty instead of making NaNs { myElem.m_weightlist.clear(); } } } } } } void AlgorithmMetricDilate::precomputeNearest(vector >& myNearest, const SurfaceFile* mySurf, const MetricFile* badNodeRoi, const MetricFile* dataRoi, const MetricFile* corrAreas, const float& distance) { CaretAssert(badNodeRoi != NULL);//because it should never be called if we don't know exactly what nodes we are replacing const float* badNodeData = badNodeRoi->getValuePointerForColumn(0); int numNodes = mySurf->getNumberOfNodes(); vector charRoi(numNodes); const float* dataRoiVals = NULL; int badCount = 0; if (dataRoi != NULL) { dataRoiVals = dataRoi->getValuePointerForColumn(0); for (int i = 0; i < numNodes; ++i) { if (!(badNodeData[i] > 0.0f))//in case some clown uses NaN as "bad" in the ROI { if (dataRoiVals[i] > 0.0f) { charRoi[i] = 1; } else { charRoi[i] = 0; } } else { if (dataRoiVals[i] > 0.0f) { ++badCount; } charRoi[i] = 0; } } } else { for (int i = 0; i < numNodes; ++i) { if (!(badNodeData[i] > 0.0f)) { charRoi[i] = 1; } else { ++badCount; charRoi[i] = 0; } } } myNearest.resize(badCount); badCount = 0; CaretPointer correctedBase; if (corrAreas != NULL) { correctedBase.grabNew(new GeodesicHelperBase(mySurf, corrAreas->getValuePointerForColumn(0)));//NOTE: myAreas also points to this when applicable } #pragma omp CARET_PAR { CaretPointer myTopoHelp = mySurf->getTopologyHelper(); CaretPointer myGeoHelp; if (corrAreas == NULL) { myGeoHelp = mySurf->getGeodesicHelper(); } else { myGeoHelp.grabNew(new GeodesicHelper(correctedBase)); } #pragma omp CARET_FOR schedule(dynamic) for (int i = 0; i < numNodes; ++i) { if (badNodeData[i] > 0.0f && (dataRoiVals == NULL || dataRoiVals[i] > 0.0f)) { int myIndex; #pragma omp critical { myIndex = badCount; ++badCount; } myNearest[myIndex].first = i; float closestDist; int closestNode = myGeoHelp->getClosestNodeInRoi(i, charRoi.data(), distance, closestDist); if (closestNode == -1)//check neighbors, to ensure we dilate by at least one node everywhere { const vector& nodeList = myTopoHelp->getNodeNeighbors(i); vector distList; myGeoHelp->getGeoToTheseNodes(i, nodeList, distList);//ok, its a little silly to do this const int numInRange = (int)nodeList.size(); for (int j = 0; j < numInRange; ++j) { if (charRoi[nodeList[j]] != 0 && (closestNode == -1 || distList[j] < closestDist)) { closestNode = nodeList[j]; closestDist = distList[j]; } } } myNearest[myIndex].second = closestNode; } } } } float AlgorithmMetricDilate::getAlgorithmInternalWeight() { return 1.0f;//override this if needed, if the progress bar isn't smooth } float AlgorithmMetricDilate::getSubAlgorithmWeight() { //return AlgorithmInsertNameHere::getAlgorithmWeight();//if you use a subalgorithm return 0.0f; } connectome-workbench-1.2.3+git41-gc4c6c90/src/Algorithms/AlgorithmMetricDilate.h000066400000000000000000000065601300200146000272520ustar00rootroot00000000000000#ifndef __ALGORITHM_METRIC_DILATE_H__ #define __ALGORITHM_METRIC_DILATE_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 "AbstractAlgorithm.h" namespace caret { class AlgorithmMetricDilate : public AbstractAlgorithm { struct StencilElem { std::vector > m_weightlist; float m_weightsum; }; AlgorithmMetricDilate(); void precomputeStencils(std::vector >& myStencils, const SurfaceFile* mySurf, const float* myAreas, const MetricFile* badNodeRoi, const MetricFile* dataRoi, const MetricFile* corrAreas, const float& distance, const float& exponent); void precomputeNearest(std::vector >& myNearest, const SurfaceFile* mySurf, const MetricFile* badNodeRoi, const MetricFile* dataRoi, const MetricFile* corrAreas, const float& distance); void processColumn(float* colScratch, const int& numNodes, const float* myInputData, std::vector > myNearest); void processColumn(float* colScratch, const int& numNodes, const float* myInputData, std::vector > myStencils); void processColumn(float* colScratch, const float* myInputData, const SurfaceFile* mySurf, const float* myAreas, const MetricFile* badNodeRoi, const MetricFile* dataRoi, const MetricFile* corrAreas, const float& distance, const bool& nearest, const bool& linear, const float& exponent); protected: static float getSubAlgorithmWeight(); static float getAlgorithmInternalWeight(); public: enum Method { NEAREST, WEIGHTED, LINEAR }; AlgorithmMetricDilate(ProgressObject* myProgObj, const MetricFile* myMetric, const SurfaceFile* mySurf, const float& distance, MetricFile* myMetricOut, const MetricFile* badNodeRoi = NULL, const MetricFile* dataRoi = NULL, const int& columnNum = -1, const Method& myMethod = WEIGHTED, const float& exponent = 2.0f, const MetricFile* corrAreas = NULL); static OperationParameters* getParameters(); static void useParameters(OperationParameters* myParams, ProgressObject* myProgObj); static AString getCommandSwitch(); static AString getShortDescription(); }; typedef TemplateAutoOperation AutoAlgorithmMetricDilate; } #endif //__ALGORITHM_METRIC_DILATE_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Algorithms/AlgorithmMetricErode.cxx000066400000000000000000000225611300200146000274600ustar00rootroot00000000000000/*LICENSE_START*/ /* * Copyright (C) 2015 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 "AlgorithmMetricErode.h" #include "AlgorithmException.h" #include "CaretOMP.h" #include "GeodesicHelper.h" #include "MetricFile.h" #include "PaletteColorMapping.h" #include "SurfaceFile.h" #include "TopologyHelper.h" #include #include using namespace caret; using namespace std; AString AlgorithmMetricErode::getCommandSwitch() { return "-metric-erode"; } AString AlgorithmMetricErode::getShortDescription() { return "ERODE A METRIC FILE"; } OperationParameters* AlgorithmMetricErode::getParameters() { OperationParameters* ret = new OperationParameters(); ret->addMetricParameter(1, "metric", "the metric file to erode"); ret->addSurfaceParameter(2, "surface", "the surface to compute on"); ret->addDoubleParameter(3, "distance", "distance in mm to erode"); ret->addMetricOutputParameter(4, "metric-out", "the output metric"); OptionalParameter* dataRoiOpt = ret->createOptionalParameter(5, "-roi", "assume values outside this roi are nonzero"); dataRoiOpt->addMetricParameter(1, "roi-metric", "metric file, positive values denote vertices that have data"); OptionalParameter* columnSelect = ret->createOptionalParameter(6, "-column", "select a single column to erode"); columnSelect->addStringParameter(1, "column", "the column number or name"); OptionalParameter* corrAreaOpt = ret->createOptionalParameter(7, "-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("Around each vertex with a value of zero, set surrounding vertices to zero. ") + "The surrounding vertices are all immediate neighbors and all vertices within the specified distance." + "\n\nNote that the -corrected-areas option uses an approximate correction for distance along the surface." ); return ret; } void AlgorithmMetricErode::useParameters(OperationParameters* myParams, ProgressObject* myProgObj) { MetricFile* myMetric = myParams->getMetric(1); SurfaceFile* mySurf = myParams->getSurface(2); float distance = (float)myParams->getDouble(3); MetricFile* myMetricOut = myParams->getOutputMetric(4); OptionalParameter* roiOpt = myParams->getOptionalParameter(5); MetricFile* myRoi = NULL; if (roiOpt->m_present) { myRoi = roiOpt->getMetric(1); } OptionalParameter* columnOpt = myParams->getOptionalParameter(6); int columnNum = -1; if (columnOpt->m_present) { columnNum = (int)myMetric->getMapIndexFromNameOrNumber(columnOpt->getString(1)); if (columnNum < 0) { throw AlgorithmException("invalid column specified"); } } OptionalParameter* corrAreasOpt = myParams->getOptionalParameter(7); MetricFile* corrAreas = NULL; if (corrAreasOpt->m_present) { corrAreas = corrAreasOpt->getMetric(1); } AlgorithmMetricErode(myProgObj, myMetric, mySurf, distance, myMetricOut, myRoi, columnNum, corrAreas); } namespace { vector > precomputeStencils(const SurfaceFile* mySurf, const float& distance, const MetricFile* corrAreas, const float* roiCol) { int numNodes = mySurf->getNumberOfNodes(); vector > ret(numNodes); CaretPointer myGeoBase; if (corrAreas != NULL) { myGeoBase.grabNew(new GeodesicHelperBase(mySurf, corrAreas->getValuePointerForColumn(0))); } CaretPointer myTopoHelp = mySurf->getTopologyHelper();//we aren't using to depth, so share the topology helper #pragma omp CARET_PAR {//use parallel here so each thread gets its own geodesic helper CaretPointer myGeoHelp; if (corrAreas == NULL) { myGeoHelp = mySurf->getGeodesicHelper(); } else { myGeoHelp.grabNew(new GeodesicHelper(myGeoBase)); } #pragma omp CARET_FOR schedule(dynamic) for (int i = 0; i < numNodes; ++i) { if (roiCol == NULL || roiCol[i] > 0.0f) { vector geoNodes; vector geoDists; myGeoHelp->getNodesToGeoDist(i, distance, geoNodes, geoDists); const vector& topoNodes = myTopoHelp->getNodeNeighbors(i); set mergeSet(geoNodes.begin(), geoNodes.end()); mergeSet.insert(topoNodes.begin(), topoNodes.end()); mergeSet.erase(i);//center of stencil is already 0 if stencil is used, so don't set it again ret[i] = vector(mergeSet.begin(), mergeSet.end()); } } } return ret; } } AlgorithmMetricErode::AlgorithmMetricErode(ProgressObject* myProgObj, const MetricFile* myMetric, const SurfaceFile* mySurf, const float& distance, MetricFile* myMetricOut, const MetricFile* myRoi, const int& columnNum, const MetricFile* corrAreas) : AbstractAlgorithm(myProgObj) { LevelProgress myProgress(myProgObj); int numNodes = mySurf->getNumberOfNodes(); if (numNodes != myMetric->getNumberOfNodes()) { throw AlgorithmException("surface and metric number of vertices do not match"); } if (myRoi != NULL && myRoi->getNumberOfNodes() != numNodes) { throw AlgorithmException("roi number of vertices does not match"); } if (corrAreas != NULL && corrAreas->getNumberOfNodes() != numNodes) { throw AlgorithmException("corrected areas metric number of vertices does not match"); } int numInColumns = myMetric->getNumberOfColumns(); if (columnNum < -1 || columnNum >= numInColumns) { throw AlgorithmException("invalid column specified"); } if (distance < 0.0f) { throw AlgorithmException("distance cannot be negative"); } if (columnNum == -1) { myMetricOut->setNumberOfNodesAndColumns(numNodes, numInColumns); } else { myMetricOut->setNumberOfNodesAndColumns(numNodes, 1); } myMetricOut->setStructure(mySurf->getStructure()); const float* roiCol = NULL; if (myRoi != NULL) { roiCol = myRoi->getValuePointerForColumn(0); } vector > stencils = precomputeStencils(mySurf, distance, corrAreas, roiCol); if (columnNum == -1) { for (int c = 0; c < numInColumns; ++c) { const float* inData = myMetric->getValuePointerForColumn(c); vector scratchCol(inData, inData + numNodes); for (int i = 0; i < numNodes; ++i) { if (roiCol == NULL || roiCol[i] > 0.0f) { if (inData[i] == 0.0f) { for (int n = 0; n < (int)stencils[i].size(); ++n) { scratchCol[stencils[i][n]] = 0.0f; } } } else { scratchCol[i] = 0.0f;//zero things outside the data roi } } *(myMetricOut->getPaletteColorMapping(c)) = *(myMetric->getPaletteColorMapping(c)); myMetricOut->setMapName(c, myMetric->getMapName(c)); myMetricOut->setValuesForColumn(c, scratchCol.data()); } } else { const float* inData = myMetric->getValuePointerForColumn(columnNum); vector scratchCol(inData, inData + numNodes); for (int i = 0; i < numNodes; ++i) { if (roiCol == NULL || roiCol[i] > 0.0f) { if (inData[i] == 0.0f) { for (int n = 0; n < (int)stencils[i].size(); ++n) { scratchCol[stencils[i][n]] = 0.0f; } } } else { scratchCol[i] = 0.0f;//zero things outside the data roi } } *(myMetricOut->getPaletteColorMapping(0)) = *(myMetric->getPaletteColorMapping(columnNum)); myMetricOut->setMapName(0, myMetric->getMapName(columnNum)); myMetricOut->setValuesForColumn(0, scratchCol.data()); } } float AlgorithmMetricErode::getAlgorithmInternalWeight() { return 1.0f;//override this if needed, if the progress bar isn't smooth } float AlgorithmMetricErode::getSubAlgorithmWeight() { //return AlgorithmInsertNameHere::getAlgorithmWeight();//if you use a subalgorithm return 0.0f; } connectome-workbench-1.2.3+git41-gc4c6c90/src/Algorithms/AlgorithmMetricErode.h000066400000000000000000000034571300200146000271100ustar00rootroot00000000000000#ifndef __ALGORITHM_METRIC_ERODE_H__ #define __ALGORITHM_METRIC_ERODE_H__ /*LICENSE_START*/ /* * Copyright (C) 2015 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 AlgorithmMetricErode : public AbstractAlgorithm { AlgorithmMetricErode(); protected: static float getSubAlgorithmWeight(); static float getAlgorithmInternalWeight(); public: AlgorithmMetricErode(ProgressObject* myProgObj, const MetricFile* myMetric, const SurfaceFile* mySurf, const float& distance, MetricFile* myMetricOut, const MetricFile* myRoi = NULL, const int& columnNum = -1, const MetricFile* corrAreas = NULL); static OperationParameters* getParameters(); static void useParameters(OperationParameters* myParams, ProgressObject* myProgObj); static AString getCommandSwitch(); static AString getShortDescription(); }; typedef TemplateAutoOperation AutoAlgorithmMetricErode; } #endif //__ALGORITHM_METRIC_ERODE_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Algorithms/AlgorithmMetricEstimateFWHM.cxx000066400000000000000000000136251300200146000306600ustar00rootroot00000000000000/*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 "AlgorithmMetricEstimateFWHM.h" #include "AlgorithmException.h" #include "DescriptiveStatistics.h" #include "MetricFile.h" #include "SurfaceFile.h" #include "TopologyHelper.h" #include using namespace caret; using namespace std; AString AlgorithmMetricEstimateFWHM::getCommandSwitch() { return "-metric-estimate-fwhm"; } AString AlgorithmMetricEstimateFWHM::getShortDescription() { return "ESTIMATE FWHM SMOOTHNESS OF A METRIC FILE"; } OperationParameters* AlgorithmMetricEstimateFWHM::getParameters() { OperationParameters* ret = new OperationParameters(); ret->addSurfaceParameter(1, "surface", "the surface to use for distance and neighbor information"); ret->addMetricParameter(2, "metric-in", "the input metric"); OptionalParameter* roiOpt = ret->createOptionalParameter(3, "-roi", "use only data within an ROI"); roiOpt->addMetricParameter(1, "roi-metric", "the metric file to use as an ROI"); OptionalParameter* columnSelect = ret->createOptionalParameter(4, "-column", "select a single column to estimate smoothness of"); columnSelect->addStringParameter(1, "column", "the column number or name"); ret->setHelpText( AString("Estimates the smoothness of the metric columns, printing the estimates to standard output. ") + "These estimates ignore variation in vertex spacing." ); return ret; } void AlgorithmMetricEstimateFWHM::useParameters(OperationParameters* myParams, ProgressObject* myProgObj) { LevelProgress myProgress(myProgObj); SurfaceFile* mySurf = myParams->getSurface(1); MetricFile* myMetric = myParams->getMetric(2); OptionalParameter* roiOpt = myParams->getOptionalParameter(3); MetricFile* roi = NULL; if (roiOpt->m_present) { roi = roiOpt->getMetric(1); } OptionalParameter* columnSelect = myParams->getOptionalParameter(4); int columnNum = -1; int numColumns = myMetric->getNumberOfMaps(); if (columnSelect->m_present) { columnNum = (int)myMetric->getMapIndexFromNameOrNumber(columnSelect->getString(1)); if (columnNum < 0 || columnNum >= numColumns) { throw AlgorithmException("invalid column specified"); } } if (columnNum == -1) { for (int i = 0; i < numColumns; ++i) { float result = estimateFWHM(mySurf, myMetric, roi, i); if (numColumns > 1) cout << "column " << i + 1 << " "; cout << "FWHM: " << result << endl; } } else { float result = estimateFWHM(mySurf, myMetric, roi, columnNum); if (numColumns > 1) cout << "column " << columnNum + 1 << " "; cout << "FWHM: " << result << endl; } } float AlgorithmMetricEstimateFWHM::estimateFWHM(const SurfaceFile* mySurf, const MetricFile* input, const MetricFile* roi, const int64_t& column) { CaretAssert(column >= 0 && column < input->getNumberOfColumns()); int numNodes = input->getNumberOfNodes(); const float* inCol = input->getValuePointerForColumn(column); const float* roiCol = NULL; if (roi != NULL) { if (roi->getNumberOfNodes() != numNodes) { throw AlgorithmException("roi metric has a different number of nodes than the input metric"); } roiCol = roi->getValuePointerForColumn(0); } DescriptiveStatistics nodeSpacingStats; mySurf->getNodesSpacingStatistics(nodeSpacingStats);//this will be slow since it recomputes - should change it to returning a const reference, and make it a lazy member double globalAccum = 0.0, localAccum = 0.0; int64_t globalCount = 0, localCount = 0; CaretPointer myHelp = mySurf->getTopologyHelper(); for (int i = 0; i < numNodes; ++i)//the local difference mean will be zero, as we don't have directionality, so don't bother collecting it { if (roi == NULL || roiCol[i] > 0.0f) { float center = inCol[i]; globalAccum += center; ++globalCount; } } float globalMean = globalAccum / globalCount; globalAccum = 0.0; for (int i = 0; i < numNodes; ++i) { if (roi == NULL || roiCol[i] > 0.0f) { float center = inCol[i]; float tempf = center - globalMean; globalAccum += tempf * tempf;//don't need to recalculate count const vector& neighbors = myHelp->getNodeNeighbors(i); for (int j = 0; j < (int)neighbors.size(); ++j) { if (neighbors[j] > i && (roi == NULL || roiCol[neighbors[j]] > 0.0f))//collect lopsided to get correct degrees of freedom (if n-1 denom is desired), mean is assumed zero so it works out { tempf = center - inCol[neighbors[j]]; localAccum += tempf * tempf; ++localCount; } } } } float globalVariance = globalAccum / globalCount; float localVariance = localAccum / localCount; float ret = nodeSpacingStats.getMean() * sqrt(-2.0f * log(2.0f) / log(1.0f - localVariance / (2.0f * globalVariance))); return ret; } connectome-workbench-1.2.3+git41-gc4c6c90/src/Algorithms/AlgorithmMetricEstimateFWHM.h000066400000000000000000000031011300200146000302710ustar00rootroot00000000000000#ifndef __ALGORITHM_METRIC_ESTIMATE_FWHM_H__ #define __ALGORITHM_METRIC_ESTIMATE_FWHM_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 "AbstractAlgorithm.h" namespace caret { class AlgorithmMetricEstimateFWHM : public AbstractOperation { public: static OperationParameters* getParameters(); static void useParameters(OperationParameters* myParams, ProgressObject* myProgObj); static AString getCommandSwitch(); static AString getShortDescription(); static float estimateFWHM(const SurfaceFile* mySurf, const MetricFile* input, const MetricFile* roi = NULL, const int64_t& column = 0); }; typedef TemplateAutoOperation AutoAlgorithmMetricEstimateFWHM; } #endif //__ALGORITHM_METRIC_ESTIMATE_FWHM_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Algorithms/AlgorithmMetricExtrema.cxx000066400000000000000000001062561300200146000300330ustar00rootroot00000000000000/*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 "AlgorithmMetricExtrema.h" #include "AlgorithmException.h" #include "AlgorithmMetricSmoothing.h" #include "CaretHeap.h" #include "CaretOMP.h" #include "GeodesicHelper.h" #include "MetricFile.h" #include "SurfaceFile.h" #include "TopologyHelper.h" #include #include #include using namespace caret; using namespace std; AString AlgorithmMetricExtrema::getCommandSwitch() { return "-metric-extrema"; } AString AlgorithmMetricExtrema::getShortDescription() { return "FIND EXTREMA IN A METRIC FILE"; } OperationParameters* AlgorithmMetricExtrema::getParameters() { OperationParameters* ret = new OperationParameters(); ret->addSurfaceParameter(1, "surface", "the surface to use for distance information"); ret->addMetricParameter(2, "metric-in", "the metric to find the extrema of"); ret->addDoubleParameter(3, "distance", "the minimum distance between identified extrema of the same type"); ret->addMetricOutputParameter(4, "metric-out", "the output extrema metric"); OptionalParameter* presmoothOpt = ret->createOptionalParameter(5, "-presmooth", "smooth the metric before finding extrema"); presmoothOpt->addDoubleParameter(1, "kernel", "the sigma for the gaussian smoothing kernel, in mm"); OptionalParameter* roiOpt = ret->createOptionalParameter(6, "-roi", "ignore values outside the selected area"); roiOpt->addMetricParameter(1, "roi-metric", "the area to find extrema in, as a metric"); OptionalParameter* thresholdOpt = ret->createOptionalParameter(7, "-threshold", "ignore small extrema"); thresholdOpt->addDoubleParameter(1, "low", "the largest value to consider for being a minimum"); thresholdOpt->addDoubleParameter(2, "high", "the smallest value to consider for being a maximum"); ret->createOptionalParameter(8, "-sum-columns", "output the sum of the extrema columns instead of each column separately"); ret->createOptionalParameter(9, "-consolidate-mode", "use consolidation of local minima instead of a large neighborhood"); ret->createOptionalParameter(11, "-only-maxima", "only find the maxima"); ret->createOptionalParameter(12, "-only-minima", "only find the minima"); OptionalParameter* columnSelect = ret->createOptionalParameter(10, "-column", "select a single column to find extrema in"); columnSelect->addStringParameter(1, "column", "the column number or name"); ret->setHelpText( AString("Finds extrema in a metric file, such that no two extrema of the same type are within of each other. ") + "The extrema are labeled as -1 for minima, 1 for maxima, 0 otherwise. " + "If -only-maxima or -only-minima is specified, then it will ignore extrema not of the specified type. These options are mutually exclusive.\n\n" + "If -roi is specified, not only is data outside the roi not used, but any vertex on the edge of the ROI will never be counted as an extrema, " + "in case the ROI cuts across a gradient, which would otherwise generate extrema where there should be none.\n\n" + "If -sum-columns is specified, these extrema columns are summed, and the output has a single column with this result.\n\n" + "By default, a datapoint is an extrema only if it is more extreme than every other datapoint that is within from it. " + "If -consolidate-mode is used, it instead starts by finding all datapoints that are more extreme than their immediate neighbors, " + "then while there are any extrema within of each other, take the two extrema closest to each other and merge them into one by a weighted average " + "based on how many original extrema have been merged into each.\n\n" + "By default, all input columns are used with no smoothing, use -column to specify a single column to use, and -presmooth to smooth the input before " + "finding the extrema." ); return ret; } void AlgorithmMetricExtrema::useParameters(OperationParameters* myParams, ProgressObject* myProgObj) { SurfaceFile* mySurf = myParams->getSurface(1); MetricFile* myMetric = myParams->getMetric(2); float distance = (float)myParams->getDouble(3); MetricFile* myMetricOut = myParams->getOutputMetric(4); OptionalParameter* presmoothOpt = myParams->getOptionalParameter(5); float presmooth = -1.0f; if (presmoothOpt->m_present) { presmooth = (float)presmoothOpt->getDouble(1); if (presmooth <= 0.0f) { throw AlgorithmException("smoothing kernel must be positive"); } } OptionalParameter* roiOpt = myParams->getOptionalParameter(6); MetricFile* myRoi = NULL; if (roiOpt->m_present) { myRoi = roiOpt->getMetric(1); } OptionalParameter* thresholdOpt = myParams->getOptionalParameter(7); bool thresholdMode = false; float lowThresh = 0.0f, highThresh = 0.0f; if (thresholdOpt->m_present) { thresholdMode = true; lowThresh = (float)thresholdOpt->getDouble(1); highThresh = (float)thresholdOpt->getDouble(2); } bool sumColumns = myParams->getOptionalParameter(8)->m_present; bool consolidateMode = myParams->getOptionalParameter(9)->m_present; OptionalParameter* columnSelect = myParams->getOptionalParameter(10); int columnNum = -1; if (columnSelect->m_present) {//set up to use the single column columnNum = (int)myMetric->getMapIndexFromNameOrNumber(columnSelect->getString(1)); if (columnNum < 0) { throw AlgorithmException("invalid column specified"); } } bool ignoreMinima = myParams->getOptionalParameter(11)->m_present; bool ignoreMaxima = myParams->getOptionalParameter(12)->m_present; if (ignoreMinima && ignoreMaxima) throw AlgorithmException("you may not specify both -only-maxima and -only-minima"); if (thresholdMode) { AlgorithmMetricExtrema(myProgObj, mySurf, myMetric, distance, myMetricOut, lowThresh, highThresh, myRoi, presmooth, sumColumns, consolidateMode, ignoreMinima, ignoreMaxima, columnNum); } else { AlgorithmMetricExtrema(myProgObj, mySurf, myMetric, distance, myMetricOut, myRoi, presmooth, sumColumns, consolidateMode, ignoreMinima, ignoreMaxima, columnNum); } } AlgorithmMetricExtrema::AlgorithmMetricExtrema(ProgressObject* myProgObj, const SurfaceFile* mySurf,const MetricFile* myMetric, const float& distance, MetricFile* myMetricOut, const MetricFile* myRoi, const float& presmooth, const bool& sumColumns, const bool& consolidateMode, const bool& ignoreMinima, const bool& ignoreMaxima, const int& columnNum) : AbstractAlgorithm(myProgObj) { LevelProgress myProgress(myProgObj); if (ignoreMinima && ignoreMaxima) throw AlgorithmException("AlgorithmMetricExtrema called with ignoreMinima and ignoreMaxima both true"); int numNodes = mySurf->getNumberOfNodes(); if (myMetric->getNumberOfNodes() != numNodes) { throw AlgorithmException("input metric has different number of vertices than input surface"); } const float* roiColumn = NULL; if (myRoi != NULL) { if (myRoi->getNumberOfNodes() != numNodes) { throw AlgorithmException("roi metric has a different number of nodes than input surface"); } roiColumn = myRoi->getValuePointerForColumn(0); } int numCols = myMetric->getNumberOfColumns(); if (columnNum < -1 || columnNum >= numCols) { throw AlgorithmException("invalid column number"); } int useCol = columnNum; const MetricFile* toProcess = myMetric; MetricFile tempMetric; if (presmooth > 0.0f) { AlgorithmMetricSmoothing(NULL, mySurf, myMetric, presmooth, &tempMetric, myRoi, false, false, columnNum); toProcess = &tempMetric; if (columnNum != -1) { useCol = 0; } } vector > neighborhoods; if (!consolidateMode) { precomputeNeighborhoods(mySurf, roiColumn, distance, neighborhoods); } if (columnNum == -1) { vector scratchcol(numNodes, 0.0f); vector minima, maxima; if (sumColumns) { myMetricOut->setNumberOfNodesAndColumns(numNodes, 1); myMetricOut->setColumnName(0, "sum of extrema"); } else { myMetricOut->setNumberOfNodesAndColumns(numNodes, numCols); } myMetricOut->setStructure(mySurf->getStructure()); for (int i = 0; i < numCols; ++i) { if (consolidateMode) { findExtremaConsolidate(mySurf, toProcess->getValuePointerForColumn(i), roiColumn, distance, false, 0.0f, 0.0f, ignoreMinima, ignoreMaxima, minima, maxima); } else { findExtremaNeighborhoods(toProcess->getValuePointerForColumn(i), neighborhoods, false, 0.0f, 0.0f, ignoreMinima, ignoreMaxima, minima, maxima); } if (sumColumns) { int numelems = (int)minima.size(); for (int j = 0; j < numelems; ++j) { scratchcol[minima[j]] -= 1.0f; } numelems = (int)maxima.size(); for (int j = 0; j < numelems; ++j) { scratchcol[maxima[j]] += 1.0f; } } else { int numelems = (int)minima.size(); for (int j = 0; j < numelems; ++j) { scratchcol[minima[j]] = -1.0f; } numelems = (int)maxima.size(); for (int j = 0; j < numelems; ++j) { scratchcol[maxima[j]] = 1.0f; } myMetricOut->setColumnName(i, "extrema of " + myMetric->getColumnName(i));//don't include the whatever smoothing adds to the names, i guess myMetricOut->setValuesForColumn(i, scratchcol.data()); numelems = (int)minima.size(); for (int j = 0; j < numelems; ++j) { scratchcol[minima[j]] = 0.0f; } numelems = (int)maxima.size(); for (int j = 0; j < numelems; ++j) { scratchcol[maxima[j]] = 0.0f; } } } if (sumColumns) { myMetricOut->setValuesForColumn(0, scratchcol.data()); } } else { vector scratchcol(numNodes, 0.0f); vector minima, maxima; myMetricOut->setNumberOfNodesAndColumns(numNodes, 1); myMetricOut->setStructure(mySurf->getStructure()); if (consolidateMode) { findExtremaConsolidate(mySurf, toProcess->getValuePointerForColumn(useCol), roiColumn, distance, false, 0.0f, 0.0f, ignoreMinima, ignoreMaxima, minima, maxima); } else { findExtremaNeighborhoods(toProcess->getValuePointerForColumn(useCol), neighborhoods, false, 0.0f, 0.0f, ignoreMinima, ignoreMaxima, minima, maxima); } int numelems = (int)minima.size(); for (int j = 0; j < numelems; ++j) { scratchcol[minima[j]] = -1.0f; } numelems = (int)maxima.size(); for (int j = 0; j < numelems; ++j) { scratchcol[maxima[j]] = 1.0f; } myMetricOut->setColumnName(0, "extrema of" + myMetric->getColumnName(columnNum));//ditto myMetricOut->setValuesForColumn(0, scratchcol.data()); } } AlgorithmMetricExtrema::AlgorithmMetricExtrema(ProgressObject* myProgObj, const SurfaceFile* mySurf,const MetricFile* myMetric, const float& distance, MetricFile* myMetricOut, const float& lowThresh, const float& highThresh, const MetricFile* myRoi, const float& presmooth, const bool& sumColumns, const bool& consolidateMode, const bool& ignoreMinima, const bool& ignoreMaxima, const int& columnNum) : AbstractAlgorithm(myProgObj) { LevelProgress myProgress(myProgObj); if (ignoreMinima && ignoreMaxima) throw AlgorithmException("AlgorithmMetricExtrema called with ignoreMinima and ignoreMaxima both true"); int numNodes = mySurf->getNumberOfNodes(); if (myMetric->getNumberOfNodes() != numNodes) { throw AlgorithmException("input metric has different number of vertices than input surface"); } const float* roiColumn = NULL; if (myRoi != NULL) { if (myRoi->getNumberOfNodes() != numNodes) { throw AlgorithmException("roi metric has a different number of nodes than input surface"); } roiColumn = myRoi->getValuePointerForColumn(0); } int numCols = myMetric->getNumberOfColumns(); if (columnNum < -1 || columnNum >= numCols) { throw AlgorithmException("invalid column number"); } int useCol = columnNum; const MetricFile* toProcess = myMetric; MetricFile tempMetric; if (presmooth > 0.0f) { AlgorithmMetricSmoothing(NULL, mySurf, myMetric, presmooth, &tempMetric, myRoi, false, false, columnNum); toProcess = &tempMetric; if (columnNum != -1) { useCol = 0; } } vector > neighborhoods; if (!consolidateMode) { precomputeNeighborhoods(mySurf, roiColumn, distance, neighborhoods); } if (columnNum == -1) { vector scratchcol(numNodes, 0.0f); vector minima, maxima; if (sumColumns) { myMetricOut->setNumberOfNodesAndColumns(numNodes, 1); } else { myMetricOut->setNumberOfNodesAndColumns(numNodes, numCols); } for (int i = 0; i < numCols; ++i) { if (consolidateMode) { findExtremaConsolidate(mySurf, toProcess->getValuePointerForColumn(i), roiColumn, distance, true, lowThresh, highThresh, ignoreMinima, ignoreMaxima, minima, maxima); } else { findExtremaNeighborhoods(toProcess->getValuePointerForColumn(i), neighborhoods, true, lowThresh, highThresh, ignoreMinima, ignoreMaxima, minima, maxima); } if (sumColumns) { int numelems = (int)minima.size(); for (int j = 0; j < numelems; ++j) { scratchcol[minima[j]] -= 1.0f; } numelems = (int)maxima.size(); for (int j = 0; j < numelems; ++j) { scratchcol[maxima[j]] += 1.0f; } } else { int numelems = (int)minima.size(); for (int j = 0; j < numelems; ++j) { scratchcol[minima[j]] = -1.0f; } numelems = (int)maxima.size(); for (int j = 0; j < numelems; ++j) { scratchcol[maxima[j]] = 1.0f; } myMetricOut->setValuesForColumn(i, scratchcol.data()); numelems = (int)minima.size(); for (int j = 0; j < numelems; ++j) { scratchcol[minima[j]] = 0.0f; } numelems = (int)maxima.size(); for (int j = 0; j < numelems; ++j) { scratchcol[maxima[j]] = 0.0f; } } } if (sumColumns) { myMetricOut->setValuesForColumn(0, scratchcol.data()); } } else { vector scratchcol(numNodes, 0.0f); vector minima, maxima; myMetricOut->setNumberOfNodesAndColumns(numNodes, 1); if (consolidateMode) { findExtremaConsolidate(mySurf, toProcess->getValuePointerForColumn(useCol), roiColumn, distance, true, lowThresh, highThresh, ignoreMinima, ignoreMaxima, minima, maxima); } else { findExtremaNeighborhoods(toProcess->getValuePointerForColumn(useCol), neighborhoods, true, lowThresh, highThresh, ignoreMinima, ignoreMaxima, minima, maxima); } int numelems = (int)minima.size(); for (int j = 0; j < numelems; ++j) { scratchcol[minima[j]] = -1.0f; } numelems = (int)maxima.size(); for (int j = 0; j < numelems; ++j) { scratchcol[maxima[j]] = 1.0f; } myMetricOut->setValuesForColumn(0, scratchcol.data()); } } void AlgorithmMetricExtrema::precomputeNeighborhoods(const SurfaceFile* mySurf, const float* roiColumn, const float& distance, vector >& neighborhoods) { int numNodes = mySurf->getNumberOfNodes(); neighborhoods.clear(); neighborhoods.resize(numNodes); CaretPointer myTopoHelp = mySurf->getTopologyHelper();//can share this, we will only use 1-hop neighbors CaretPointer myGeoHelp = mySurf->getGeodesicHelper();//must be thread-private vector junk;//also thread-private for (int i = 0; i < numNodes; ++i) { if (roiColumn == NULL || roiColumn[i] > 0.0f) { if (roiColumn != NULL) { const vector& neighbors = myTopoHelp->getNodeNeighbors(i); int numNeigh = (int)neighbors.size(); bool good = true; for (int j = 0; j < numNeigh; ++j) { if (roiColumn[neighbors[j]] <= 0.0f) { good = false; break; } } if (!good) { neighborhoods[i].push_back(-1);//use a single neighbor of -1 to denote "do not use due to being on the edge of the ROI" - a bit of a hack, but means we don't need a second array, and still separates it from "no neighbors" continue; } } myGeoHelp->getNodesToGeoDist(i, distance, neighborhoods[i], junk); int numelems = (int)neighborhoods[i].size(); if (numelems < 7) { neighborhoods[i] = myTopoHelp->getNodeNeighbors(i); if (roiColumn != NULL) { numelems = (int)neighborhoods[i].size(); for (int j = 0; j < numelems; ++j) { if (roiColumn[neighborhoods[i][j]] <= 0.0f) { neighborhoods[i].erase(neighborhoods[i].begin() + j);//erase it --j;//don't skip any or walk off the vector --numelems; } } } } else { if (roiColumn == NULL) { for (int j = 0; j < numelems; ++j) { if (neighborhoods[i][j] == i) { neighborhoods[i].erase(neighborhoods[i].begin() + j);//we don't want the node itself, so erase it break; } } } else { for (int j = 0; j < numelems; ++j) { if (neighborhoods[i][j] == i || roiColumn[neighborhoods[i][j]] <= 0.0f)//don't want the node itself, or anything outside the roi { neighborhoods[i].erase(neighborhoods[i].begin() + j);//erase it --j;//don't skip any or walk off the vector --numelems; } } } } } } } void AlgorithmMetricExtrema::findExtremaNeighborhoods(const float* data, const vector >& neighborhoods, const bool& threshMode, const float& lowThresh, const float& highThresh, const bool& ignoreMinima, const bool& ignoreMaxima, vector& minima, vector& maxima) { int numNodes = (int)neighborhoods.size(); minima.clear(); maxima.clear(); vector minPos(numNodes, 1), maxPos(numNodes, 1);//mark off things that fail a comparison to reduce the work - these are just used as booleans, but we don't want bitwise packing slowing us down or dropping parallel writes #pragma omp CARET_PARFOR schedule(dynamic) for (int i = 0; i < numNodes; ++i) { bool canBeMin = minPos[i] && !ignoreMinima, canBeMax = maxPos[i] && !ignoreMaxima; if (canBeMin || canBeMax) { const vector& myneighbors = neighborhoods[i]; int numNeigh = (int)myneighbors.size(); if (numNeigh == 0) continue;//don't count isolated nodes as minima or maxima if (numNeigh == 1 && myneighbors[0] == -1) continue;//ignore nodes on the edge of the ROI, because this often generates artificial extrema float myval = data[i]; if (threshMode) { if (myval > lowThresh) canBeMin = false;//check thresholds if (myval < highThresh) canBeMax = false; } int j = 0; if (canBeMin && canBeMax)//avoid the double-test unless both options are on the table {//should be fairly rare, and doesn't need to loop int32_t neighNode = myneighbors[0];//NOTE: the equals case should set one of these to false, so that only one of the two loops needs to execute float otherval = data[neighNode]; if (myval < otherval) { minPos[neighNode] = 0; } else { canBeMin = false;//center being equal or greater means it is not a minimum, so stop testing that } if (myval > otherval) { maxPos[neighNode] = 0; } else { canBeMax = false; } j = 1;//don't test 0 again if we did the double test } if (canBeMax) { for (; j < numNeigh; ++j) { int32_t neighNode = myneighbors[j]; float otherval = data[neighNode]; if (myval > otherval) { maxPos[neighNode] = 0;//TODO: test if performing an intelligent comparison here is faster than doing unneeded stores } else { canBeMax = false; break; } } } if (canBeMin) { for (; j < numNeigh; ++j) { int32_t neighNode = myneighbors[j]; float otherval = data[neighNode]; if (myval < otherval) { minPos[neighNode] = 0;//ditto } else { canBeMin = false; break; } } } if (canBeMax) { #pragma omp critical { maxima.push_back(i); } } if (canBeMin) { #pragma omp critical { minima.push_back(i); } } } } } void AlgorithmMetricExtrema::findExtremaConsolidate(const SurfaceFile* mySurf, const float* data, const float* roiColumn, const float& distance, const bool& threshMode, const float& lowThresh, const float& highThresh, const bool& ignoreMinima, const bool& ignoreMaxima, vector& minima, vector& maxima) { int numNodes = mySurf->getNumberOfNodes(); minima.clear(); maxima.clear(); vector > tempExtrema[2]; vector minPos(numNodes, 1), maxPos(numNodes, 1);//mark off things that fail a comparison to reduce the work - these are just used as booleans, but we don't want bitwise packing slowing us down or dropping writes CaretPointer myTopoHelp = mySurf->getTopologyHelper(); #pragma omp CARET_PARFOR schedule(dynamic) for (int i = 0; i < numNodes; ++i) { bool canBeMin = minPos[i] && !ignoreMinima, canBeMax = maxPos[i] && !ignoreMaxima; if (canBeMin || canBeMax) { const vector& myneighbors = myTopoHelp->getNodeNeighbors(i); int numNeigh = (int)myneighbors.size(); if (numNeigh == 0) continue;//don't count isolated nodes as minima or maxima float myval = data[i]; if (threshMode) { if (myval > lowThresh) canBeMin = false;//check thresholds if (myval < highThresh) canBeMax = false; } if (canBeMin && canBeMax)//avoid the double-test unless both options are on the table {//NOTE: the equals case should set one of these to false, so that only one of the two below loops needs to execute int32_t neighNode = myneighbors[0]; if (roiColumn == NULL || roiColumn[neighNode] > 0.0f) { float otherval = data[neighNode]; if (myval < otherval) { minPos[neighNode] = 0; } else { canBeMin = false;//center being equal or greater means it is not a minimum, so stop testing that } if (myval > otherval) { maxPos[neighNode] = 0; } else { canBeMax = false; } } else { canBeMin = false;//if we neighbor something outside the ROI, do not allow counting as max or min canBeMax = false; } } int j = 1;//don't retest the first neighbor if (canBeMax) { for (; j < numNeigh; ++j) { int32_t neighNode = myneighbors[j]; if (roiColumn == NULL || roiColumn[neighNode] > 0.0f) { float otherval = data[neighNode]; if (myval > otherval) { maxPos[neighNode] = 0;//TODO: test if performing an intelligent comparison here is faster than doing unneeded stores } else { canBeMax = false; break; } } else { canBeMax = false;//if we neighbor something outside the ROI, do not allow counting as max or min break; } } } if (canBeMin) { for (; j < numNeigh; ++j) { int32_t neighNode = myneighbors[j]; if (roiColumn == NULL || roiColumn[neighNode] > 0.0f) { float otherval = data[neighNode]; if (myval < otherval) { minPos[neighNode] = 0;//ditto } else { canBeMin = false; break; } } else { canBeMin = false;//if we neighbor something outside the ROI, do not allow counting as max or min break; } } } if (canBeMax) { #pragma omp critical { tempExtrema[0].push_back(pair(i, 1)); } } if (canBeMin) { #pragma omp critical { tempExtrema[1].push_back(pair(i, 1)); } } } } consolidateStep(mySurf, distance, tempExtrema, minima, maxima); } void AlgorithmMetricExtrema::consolidateStep(const SurfaceFile* mySurf, const float& distance, vector > initExtrema[2], vector& minima, vector& maxima) { int numNodes = mySurf->getNumberOfNodes(); vector scratchDist(numNodes, -1.0f); for (int sign = 0; sign < 2; ++sign) { int numInitExtrema = (int)initExtrema[sign].size(); vector removed(numInitExtrema, false);//track which extrema locations are dropped during consolidation - the one that isn't dropped in a merge has its node number changed vector > heapIDmatrix(numInitExtrema, vector(numInitExtrema, -1)); CaretMinHeap, float> myDistHeap; vector dists; vector neighbors; CaretPointer myGeoHelp = mySurf->getGeodesicHelper(); for (int i = 0; i < numInitExtrema - 1; ++i) { myGeoHelp->getNodesToGeoDist(initExtrema[sign][i].first, distance, neighbors, dists);//use smooth distance to get whether they are close enough int numInDist = (int)dists.size(); for (int j = 0; j < numInDist; ++j) { scratchDist[neighbors[j]] = dists[j]; } for (int j = i + 1; j < numInitExtrema; ++j) { float tempf = scratchDist[initExtrema[sign][j].first]; if (tempf > -0.5f) { int64_t tempID = myDistHeap.push(pair(i, j), tempf); heapIDmatrix[i][j] = tempID; heapIDmatrix[j][i] = tempID; } } for (int j = 0; j < numInDist; ++j) { scratchDist[neighbors[j]] = -1.0f; } }//initial distance matrix computed, now we iterate while (!myDistHeap.isEmpty()) { pair toMerge = myDistHeap.pop();//we don't need to know the key int extr1 = toMerge.first; int extr2 = toMerge.second; heapIDmatrix[extr1][extr2] = -1; heapIDmatrix[extr2][extr1] = -1; int weight1 = initExtrema[sign][extr1].second; int weight2 = initExtrema[sign][extr2].second; if (weight2 > weight1)//swap so weight1 is always bigger { int temp = weight2; weight2 = weight1; weight1 = temp; temp = extr2; extr2 = extr1; extr1 = temp; } int node1 = initExtrema[sign][extr1].first; int node2 = initExtrema[sign][extr2].first; removed[extr2] = true;//drop the one that has less weight, and modify the one that has more weight for (int j = 0; j < numInitExtrema; ++j) { if (!removed[j]) { int64_t tempID = heapIDmatrix[extr2][j]; if (tempID != -1) { myDistHeap.remove(tempID); heapIDmatrix[extr2][j] = -1; heapIDmatrix[j][extr2] = -1; } } } vector pathnodes; vector pathdists; myGeoHelp->getPathToNode(node1, node2, pathnodes, pathdists, false); if (pathdists.size() != 0) { float distToWalk = (pathdists.back() * weight2) / (weight1 + weight2); int walk = 1, maxSteps = (int)pathnodes.size(); float prevdiff = abs(distToWalk - pathdists[0]); while (walk < maxSteps) { float newdiff = abs(distToWalk - pathdists[walk]); if (newdiff > prevdiff) break; ++walk; } int newnode = pathnodes[walk - 1]; initExtrema[sign][extr1].first = newnode; initExtrema[sign][extr1].second += weight2;//add the weights together myGeoHelp->getNodesToGeoDist(newnode, distance, neighbors, dists); int numInDist = (int)dists.size(); for (int j = 0; j < numInDist; ++j) { scratchDist[neighbors[j]] = dists[j]; } for (int j = 0; j < numInitExtrema; ++j) { if (!removed[j]) { float tempf = scratchDist[initExtrema[sign][j].first]; int64_t tempID = heapIDmatrix[extr1][j]; if (tempf > -0.5f) { if (tempID != -1) { myDistHeap.changekey(tempID, tempf); } else { tempID = myDistHeap.push(pair(extr1, j), tempf); heapIDmatrix[extr1][j] = tempID; heapIDmatrix[j][extr1] = tempID; } } else { if (tempID != -1) { myDistHeap.remove(tempID); heapIDmatrix[extr1][j] = -1; heapIDmatrix[j][extr1] = -1; } } } } for (int j = 0; j < numInDist; ++j) { scratchDist[neighbors[j]] = -1.0f; } } } if (sign == 0) { for (int i = 0; i < numInitExtrema; ++i) { if (!removed[i]) { maxima.push_back(initExtrema[sign][i].first); } } } else { for (int i = 0; i < numInitExtrema; ++i) { if (!removed[i]) { minima.push_back(initExtrema[sign][i].first); } } } } } float AlgorithmMetricExtrema::getAlgorithmInternalWeight() { return 1.0f;//override this if needed, if the progress bar isn't smooth } float AlgorithmMetricExtrema::getSubAlgorithmWeight() { //return AlgorithmInsertNameHere::getAlgorithmWeight();//if you use a subalgorithm return 0.0f; } connectome-workbench-1.2.3+git41-gc4c6c90/src/Algorithms/AlgorithmMetricExtrema.h000066400000000000000000000071461300200146000274560ustar00rootroot00000000000000#ifndef __ALGORITHM_METRIC_EXTREMA_H__ #define __ALGORITHM_METRIC_EXTREMA_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 "AbstractAlgorithm.h" #include #include namespace caret { class AlgorithmMetricExtrema : public AbstractAlgorithm { void precomputeNeighborhoods(const SurfaceFile* mySurf, const float* roiColumn, const float& distance, std::vector >& neighborhoods); void findExtremaConsolidate(const SurfaceFile* mySurf, const float* data, const float* roiColumn, const float& distance, const bool& threshMode, const float& lowThresh, const float& highThresh, const bool& ignoreMinima, const bool& ignoreMaxima, std::vector& minima, std::vector& maxima); void findExtremaNeighborhoods(const float* data, const std::vector >& neighborhoods, const bool& threshMode, const float& lowThresh, const float& highThresh, const bool& ignoreMinima, const bool& ignoreMaxima, std::vector& minima, std::vector& maxima); void consolidateStep(const SurfaceFile* mySurf, const float& distance, std::vector > initExtrema[2], std::vector& minima, std::vector& maxima); AlgorithmMetricExtrema(); protected: static float getSubAlgorithmWeight(); static float getAlgorithmInternalWeight(); public: AlgorithmMetricExtrema(ProgressObject* myProgObj, const SurfaceFile* mySurf,const MetricFile* myMetric, const float& distance, MetricFile* myMetricOut, const MetricFile* myRoi = NULL, const float& presmooth = -1.0f, const bool& sumColumns = false, const bool& consolidateMode = false, const bool& ignoreMinima = false, const bool& ignoreMaxima = false, const int& columnNum = -1); AlgorithmMetricExtrema(ProgressObject* myProgObj, const SurfaceFile* mySurf,const MetricFile* myMetric, const float& distance, MetricFile* myMetricOut, const float& lowThresh, const float& highThresh, const MetricFile* myRoi = NULL, const float& presmooth = -1.0f, const bool& sumColumns = false, const bool& consolidateMode = false, const bool& ignoreMinima = false, const bool& ignoreMaxima = false, const int& columnNum = -1); static OperationParameters* getParameters(); static void useParameters(OperationParameters* myParams, ProgressObject* myProgObj); static AString getCommandSwitch(); static AString getShortDescription(); }; typedef TemplateAutoOperation AutoAlgorithmMetricExtrema; } #endif //__ALGORITHM_METRIC_EXTREMA_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Algorithms/AlgorithmMetricFalseCorrelation.cxx000066400000000000000000000340551300200146000316570ustar00rootroot00000000000000/*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 "AlgorithmMetricFalseCorrelation.h" #include "AlgorithmException.h" #include "CaretOMP.h" #include "CaretPointLocator.h" #include "GeodesicHelper.h" #include "SurfaceFile.h" #include "MetricFile.h" #include #include using namespace caret; using namespace std; AString AlgorithmMetricFalseCorrelation::getCommandSwitch() { return "-metric-false-correlation"; } AString AlgorithmMetricFalseCorrelation::getShortDescription() { return "COMPARE CORRELATION LOCALLY AND ACROSS/THROUGH SULCI/GYRI"; } OperationParameters* AlgorithmMetricFalseCorrelation::getParameters() { OperationParameters* ret = new OperationParameters(); ret->addSurfaceParameter(1, "surface", "the surface to compute geodesic and 3D distance with"); ret->addMetricParameter(2, "metric-in", "the metric to correlate"); ret->addDoubleParameter(3, "3D-dist", "maximum 3D distance to check around each vertex"); ret->addDoubleParameter(4, "geo-outer", "maximum geodesic distance to use for neighboring correlation"); ret->addDoubleParameter(5, "geo-inner", "minimum geodesic distance to use for neighboring correlation"); ret->addMetricOutputParameter(6, "metric-out", "the output metric"); OptionalParameter* roiOpt = ret->createOptionalParameter(7, "-roi", "select a region of interest that has data"); roiOpt->addMetricParameter(1, "roi-metric", "the region, as a metric file"); OptionalParameter* dumpTextOpt = ret->createOptionalParameter(8, "-dump-text", "dump the raw measures used to a text file"); dumpTextOpt->addStringParameter(1, "text-out", "the output text file"); ret->setHelpText( AString("For each vertex, compute the average correlation within a range of geodesic distances that don't cross a sulcus/gyrus, and the correlation to the closest vertex crossing a sulcus/gyrus. ") + "A vertex is considered to cross a sulcus/gyrus if the 3D distance is less than a third of the geodesic distance. " + "The output file contains the ratio between these correlations, and some additional maps to help explain the ratio." ); return ret; } void AlgorithmMetricFalseCorrelation::useParameters(OperationParameters* myParams, ProgressObject* myProgObj) { SurfaceFile* mySurf = myParams->getSurface(1); MetricFile* myMetric = myParams->getMetric(2); float max3D = (float)myParams->getDouble(3); float maxgeo = (float)myParams->getDouble(4); float mingeo = (float)myParams->getDouble(5); MetricFile* myMetricOut = myParams->getOutputMetric(6); MetricFile* myRoi = NULL; OptionalParameter* roiOpt = myParams->getOptionalParameter(7); if (roiOpt->m_present) { myRoi = roiOpt->getMetric(1); } AString textName = ""; OptionalParameter* dumpTextOpt = myParams->getOptionalParameter(8); if (dumpTextOpt->m_present) { textName = dumpTextOpt->getString(1); } AlgorithmMetricFalseCorrelation(myProgObj, mySurf, myMetric, myMetricOut, max3D, maxgeo, mingeo, myRoi, textName); } AlgorithmMetricFalseCorrelation::AlgorithmMetricFalseCorrelation(ProgressObject* myProgObj, const SurfaceFile* mySurf, const MetricFile* myMetric, MetricFile* myMetricOut, const float& max3D, const float& maxgeo, const float& mingeo, const MetricFile* myRoi, const AString& textName) : AbstractAlgorithm(myProgObj) { LevelProgress myProgress(myProgObj); if (max3D <= 0.0f || maxgeo <= 0.0f || mingeo < 0.0f) throw AlgorithmException("distance limits must not be negative, and maximums must be positive"); int numNodes = mySurf->getNumberOfNodes(); if (myMetric->getNumberOfNodes() != numNodes) throw AlgorithmException("surface and metric have different number of vertices"); const float* roiCol = NULL; if (myRoi != NULL) { if (myRoi->getNumberOfNodes() != numNodes) throw AlgorithmException("surface and roi metric have different number of vertices"); roiCol = myRoi->getValuePointerForColumn(0); } if (myMetric->getNumberOfColumns() < 3) throw AlgorithmException("input metric must have more than 2 columns for correlation to be meaningful"); bool dumpRaw = false; ofstream rawOut; if (textName != "") { dumpRaw = true; rawOut.open(textName.toLocal8Bit().constData()); if (!rawOut) throw AlgorithmException("failed to open text file for output"); } m_toCorr = myMetric;//so we can do fancy correlation caching without rewriting this part, if we want setupCorr(roiCol); myMetricOut->setNumberOfNodesAndColumns(numNodes, 5); myMetricOut->setStructure(mySurf->getStructure()); myMetricOut->setColumnName(0, "correlation ratio"); myMetricOut->setColumnName(1, "non-neighborhood correlation"); myMetricOut->setColumnName(2, "average neighborhood correlation"); myMetricOut->setColumnName(3, "3D distance to non-neighborhood vertex"); myMetricOut->setColumnName(4, "non-neighborhood vertex number"); const AString sep1 = ",", sep2 = ";\n"; float distRatioCutoff = 3.0f; CaretPointer myLocator = mySurf->getPointLocator(); #pragma omp CARET_PAR { CaretPointer myGeo = mySurf->getGeodesicHelper(); #pragma omp CARET_FOR schedule(dynamic) for (int n = 0; n < numNodes; ++n) { int crossingNode = -1, neighCount = 0; float crossingDist = -1.0f, neighAccum = 0.0f; if (roiCol == NULL || roiCol[n] > 0.0f) { AString rawDumpString;//build the entire string for a single node, then write it in one call within #pragma omp critical Vector3D myCoord = mySurf->getCoordinate(n); set inRange = myLocator->pointsInRange(myCoord, max3D); int numInterested = (int)inRange.size(); vector interested(numInterested); int counter = 0; for (set::iterator iter = inRange.begin(); iter != inRange.end(); ++iter) { interested[counter] = iter->index; ++counter; } vector geoDists; myGeo->getGeoToTheseNodes(n, interested, geoDists); counter = 0; for (set::iterator iter = inRange.begin(); iter != inRange.end(); ++iter) { if (roiCol == NULL || (roiCol[iter->index] > 0.0f)) { float dist3D = (myCoord - iter->coords).length(); if (iter->index == n || geoDists[counter] / dist3D < distRatioCutoff) { if (maxgeo <= max3D)//otherwise, we can't trust the 3D test picking up all the points we want { if (dumpRaw) { float thiscorr = correlate(n, iter->index); rawDumpString += AString::number(n) + sep1 + AString::number(iter->index) + sep1 + AString::number(thiscorr) + sep1 + AString::number(geoDists[counter]) + sep1 + AString::number(dist3D) + sep2; if (geoDists[counter] <= maxgeo && geoDists[counter] >= mingeo) { neighAccum += thiscorr; ++neighCount; } } else { if (geoDists[counter] <= maxgeo && geoDists[counter] >= mingeo) { float thiscorr = correlate(n, iter->index); neighAccum += thiscorr; ++neighCount; } } } } else { if (dist3D < crossingDist || crossingNode == -1) { crossingDist = dist3D; crossingNode = iter->index; } if (dumpRaw) { float thiscorr = correlate(n, iter->index); rawDumpString += AString::number(n) + sep1 + AString::number(iter->index) + sep1 + AString::number(thiscorr) + sep1 + AString::number(geoDists[counter]) + sep1 + AString::number(dist3D) + sep2; } } } ++counter; } if (maxgeo > max3D)//so, we have to run geodesic separately { myGeo->getNodesToGeoDist(n, maxgeo, interested, geoDists);//reuse interested, we don't need its previous contents int numInRange = (int)interested.size(); for (int i = 0; i < numInRange; ++i) { if (roiCol != NULL && !(roiCol[interested[i]] > 0.0f)) continue; float dist3D = (myCoord - Vector3D(mySurf->getCoordinate(interested[i]))).length(); if ((interested[i] == n || geoDists[i] / dist3D < distRatioCutoff)) { if (dumpRaw) { float thiscorr = correlate(n, interested[i]); rawDumpString += AString::number(n) + sep1 + AString::number(interested[i]) + sep1 + AString::number(thiscorr) + sep1 + AString::number(geoDists[i]) + sep1 + AString::number(dist3D) + sep2; if (geoDists[i] >= mingeo)//we already know it is not greater than maxgeo { neighAccum += thiscorr; ++neighCount; } } else { if (geoDists[i] >= mingeo) { float thiscorr = correlate(n, interested[i]); neighAccum += thiscorr; ++neighCount; } } } } } if (dumpRaw) { #pragma omp critical { rawOut << rawDumpString; } } } if (crossingNode != -1 && neighCount != 0) { float longCorr = correlate(n, crossingNode); float closeAvg = neighAccum / neighCount; myMetricOut->setValue(n, 0, longCorr / closeAvg); myMetricOut->setValue(n, 1, longCorr); myMetricOut->setValue(n, 2, closeAvg); myMetricOut->setValue(n, 3, crossingDist); myMetricOut->setValue(n, 4, crossingNode); } else { myMetricOut->setValue(n, 0, 0.0f); myMetricOut->setValue(n, 1, 0.0f); myMetricOut->setValue(n, 2, 0.0f); myMetricOut->setValue(n, 3, 0.0f); myMetricOut->setValue(n, 4, -1); } } } } float AlgorithmMetricFalseCorrelation::correlate(int first, int second) { CaretAssert((int)m_demeanedRows[first].size() == m_rowSize); CaretAssert((int)m_demeanedRows[second].size() == m_rowSize); double accum = 0.0; for (int i = 0; i < m_rowSize; ++i) { accum += m_demeanedRows[first][i] * m_demeanedRows[second][i]; } return accum / (m_rrs[first] * m_rrs[second]); } void AlgorithmMetricFalseCorrelation::setupCorr(const float* roiCol) { m_rowSize = m_toCorr->getNumberOfColumns(); int numRows = m_toCorr->getNumberOfNodes(); m_demeanedRows.resize(numRows); m_rrs.resize(numRows); for (int i = 0; i < numRows; ++i) { if (roiCol != NULL && !(roiCol[i] > 0.0f)) continue; double accum = 0.0f; m_demeanedRows[i].resize(m_rowSize); for (int j = 0; j < m_rowSize; ++j) { float tempf = m_toCorr->getValue(i, j); m_demeanedRows[i][j] = tempf;//first, transpose the indexing - in MetricFile, a column is contiguous in memory accum += tempf; } float mean = accum / m_rowSize; accum = 0.0; for (int j = 0; j < m_rowSize; ++j) { float tempf = m_demeanedRows[i][j] - mean;//remove mean, calculate rrs for correlation m_demeanedRows[i][j] = tempf; accum += tempf * tempf; } m_rrs[i] = sqrt(accum); } } float AlgorithmMetricFalseCorrelation::getAlgorithmInternalWeight() { return 1.0f;//override this if needed, if the progress bar isn't smooth } float AlgorithmMetricFalseCorrelation::getSubAlgorithmWeight() { //return AlgorithmInsertNameHere::getAlgorithmWeight();//if you use a subalgorithm return 0.0f; } connectome-workbench-1.2.3+git41-gc4c6c90/src/Algorithms/AlgorithmMetricFalseCorrelation.h000066400000000000000000000042411300200146000312760ustar00rootroot00000000000000#ifndef __ALGORITHM_METRIC_FALSE_CORRELATION_H__ #define __ALGORITHM_METRIC_FALSE_CORRELATION_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 "AbstractAlgorithm.h" #include namespace caret { class AlgorithmMetricFalseCorrelation : public AbstractAlgorithm { AlgorithmMetricFalseCorrelation(); float correlate(int first, int second); const MetricFile* m_toCorr; int m_rowSize; std::vector > m_demeanedRows; std::vector m_rrs; void setupCorr(const float* roiCol); protected: static float getSubAlgorithmWeight(); static float getAlgorithmInternalWeight(); public: AlgorithmMetricFalseCorrelation(ProgressObject* myProgObj, const SurfaceFile* mySurf, const MetricFile* myMetric, MetricFile* myMetricOut, const float& max3D, const float& maxgeo, const float& mingeo, const MetricFile* myRoi = NULL, const AString& textName = ""); static OperationParameters* getParameters(); static void useParameters(OperationParameters* myParams, ProgressObject* myProgObj); static AString getCommandSwitch(); static AString getShortDescription(); }; typedef TemplateAutoOperation AutoAlgorithmMetricFalseCorrelation; } #endif //__ALGORITHM_METRIC_FALSE_CORRELATION_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Algorithms/AlgorithmMetricFillHoles.cxx000066400000000000000000000150071300200146000303000ustar00rootroot00000000000000/*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 "AlgorithmMetricFillHoles.h" #include "AlgorithmException.h" #include "MetricFile.h" #include "SurfaceFile.h" #include "TopologyHelper.h" #include #include using namespace caret; using namespace std; AString AlgorithmMetricFillHoles::getCommandSwitch() { return "-metric-fill-holes"; } AString AlgorithmMetricFillHoles::getShortDescription() { return "FILL HOLES IN AN ROI METRIC"; } OperationParameters* AlgorithmMetricFillHoles::getParameters() { OperationParameters* ret = new OperationParameters(); ret->addSurfaceParameter(1, "surface", "the surface to use for neighbor information"); ret->addMetricParameter(2, "metric-in", "the input ROI metric"); ret->addMetricOutputParameter(3, "metric-out", "the output ROI metric"); OptionalParameter* corrAreaOpt = ret->createOptionalParameter(4, "-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("Finds all connected areas that are not included in the ROI, and writes ones into all but the largest one, in terms of surface area.") ); return ret; } void AlgorithmMetricFillHoles::useParameters(OperationParameters* myParams, ProgressObject* myProgObj) { SurfaceFile* mySurf = myParams->getSurface(1); MetricFile* myMetric = myParams->getMetric(2); MetricFile* myMetricOut = myParams->getOutputMetric(3); MetricFile* corrAreaMetric = NULL; OptionalParameter* corrAreaOpt = myParams->getOptionalParameter(4); if (corrAreaOpt->m_present) { corrAreaMetric = corrAreaOpt->getMetric(1); } AlgorithmMetricFillHoles(myProgObj, mySurf, myMetric, myMetricOut, corrAreaMetric); } AlgorithmMetricFillHoles::AlgorithmMetricFillHoles(ProgressObject* myProgObj, const SurfaceFile* mySurf, const MetricFile* myMetric, MetricFile* myMetricOut, const MetricFile* corrAreaMetric) : AbstractAlgorithm(myProgObj) { LevelProgress myProgress(myProgObj); int numNodes = mySurf->getNumberOfNodes(); if (myMetric->getNumberOfNodes() != numNodes) throw AlgorithmException("metric file has different number of nodes than the surface"); vector surfAreaData; const float* areaData = NULL; if (corrAreaMetric != NULL) { if (corrAreaMetric->getNumberOfNodes() != numNodes) throw AlgorithmException("corrected vertex area metric file has different number of nodes than the surface"); areaData = corrAreaMetric->getValuePointerForColumn(0); } else { mySurf->computeNodeAreas(surfAreaData); areaData = surfAreaData.data(); } int numCols = myMetric->getNumberOfColumns(); myMetricOut->setNumberOfNodesAndColumns(numNodes, numCols); myMetricOut->setStructure(myMetric->getStructure()); for (int col = 0; col < numCols; ++col) { vector > > areas; vector used(numNodes, 0); CaretPointer myHelp = mySurf->getTopologyHelper(); const float* roiData = myMetric->getValuePointerForColumn(col); myMetricOut->setColumnName(col, myMetric->getColumnName(col)); for (int i = 0; i < numNodes; ++i) { if (used[i] == 0 && !(roiData[i] > 0.0f))//use "not greater than" in case someone uses NaNs in their ROI { areas.push_back(make_pair(0.0f, vector())); vector& thisAreaMembers = areas.back().second; float& thisSurfArea = areas.back().first; thisAreaMembers.push_back(i); thisSurfArea += areaData[i]; used[i] = 1; vector mystack; mystack.push_back(i); while (!mystack.empty()) { int curnode = mystack.back(); mystack.pop_back(); const vector& neighbors = myHelp->getNodeNeighbors(curnode); int numNeigh = (int)neighbors.size(); for (int j = 0; j < numNeigh; ++j) { int thisneigh = neighbors[j]; if (used[thisneigh] == 0 && !(roiData[thisneigh] > 0.0f)) { used[thisneigh] = 1; thisAreaMembers.push_back(thisneigh); thisSurfArea += areaData[thisneigh]; mystack.push_back(thisneigh); } } } } } vector outscratch(numNodes, 1.0f); int numAreas = (int)areas.size(); if (numAreas > 0) { int bestIndex = 0; float bestArea = areas[0].first; for (int i = 1; i < numAreas; ++i) { float thisArea = (int)areas[i].first; if (thisArea > bestArea) { bestIndex = i; bestArea = thisArea; } } const vector& thisArea = areas[bestIndex].second; int numAreaNodes = (int)thisArea.size(); for (int i = 0; i < numAreaNodes; ++i) { outscratch[thisArea[i]] = 0.0f;//make it into a simple 0/1 metric, even if it wasn't before } } myMetricOut->setValuesForColumn(col, outscratch.data()); } } float AlgorithmMetricFillHoles::getAlgorithmInternalWeight() { return 1.0f;//override this if needed, if the progress bar isn't smooth } float AlgorithmMetricFillHoles::getSubAlgorithmWeight() { return 0.0f; } connectome-workbench-1.2.3+git41-gc4c6c90/src/Algorithms/AlgorithmMetricFillHoles.h000066400000000000000000000034111300200146000277210ustar00rootroot00000000000000#ifndef __ALGORITHM_METRIC_FILL_HOLES_H__ #define __ALGORITHM_METRIC_FILL_HOLES_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 "AbstractAlgorithm.h" namespace caret { class AlgorithmMetricFillHoles : public AbstractAlgorithm { AlgorithmMetricFillHoles(); protected: static float getSubAlgorithmWeight(); static float getAlgorithmInternalWeight(); public: AlgorithmMetricFillHoles(ProgressObject* myProgObj, const SurfaceFile* mySurf, const MetricFile* myMetric, MetricFile* myMetricOut, const MetricFile* corrAreaMetric = NULL); static OperationParameters* getParameters(); static void useParameters(OperationParameters* myParams, ProgressObject* myProgObj); static AString getCommandSwitch(); static AString getShortDescription(); }; typedef TemplateAutoOperation AutoAlgorithmMetricFillHoles; } #endif //__ALGORITHM_METRIC_FILL_HOLES_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Algorithms/AlgorithmMetricFindClusters.cxx000066400000000000000000000352021300200146000310230ustar00rootroot00000000000000/*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 "AlgorithmMetricFindClusters.h" #include "AlgorithmException.h" #include "CaretLogger.h" #include "GeodesicHelper.h" #include "MetricFile.h" #include "SurfaceFile.h" #include "TopologyHelper.h" #include using namespace caret; using namespace std; AString AlgorithmMetricFindClusters::getCommandSwitch() { return "-metric-find-clusters"; } AString AlgorithmMetricFindClusters::getShortDescription() { return "FILTER CLUSTERS BY SURFACE AREA"; } OperationParameters* AlgorithmMetricFindClusters::getParameters() { OperationParameters* ret = new OperationParameters(); ret->addSurfaceParameter(1, "surface", "the surface to compute on"); ret->addMetricParameter(2, "metric-in", "the input metric"); ret->addDoubleParameter(3, "value-threshold", "threshold for data values"); ret->addDoubleParameter(4, "minimum-area", "threshold for cluster area, in mm^2"); ret->addMetricOutputParameter(5, "metric-out", "the output metric"); ret->createOptionalParameter(6, "-less-than", "find values less than , rather than greater"); OptionalParameter* roiOption = ret->createOptionalParameter(7, "-roi", "select a region of interest"); roiOption->addMetricParameter(1, "roi-metric", "the roi, as a metric"); OptionalParameter* corrAreasOpt = ret->createOptionalParameter(8, "-corrected-areas", "vertex areas to use instead of computing them from the surface"); corrAreasOpt->addMetricParameter(1, "area-metric", "the corrected vertex areas, as a metric"); OptionalParameter* columnSelect = ret->createOptionalParameter(9, "-column", "select a single column"); columnSelect->addStringParameter(1, "column", "the column number or name"); OptionalParameter* sizeRatioOpt = ret->createOptionalParameter(11, "-size-ratio", "ignore clusters smaller than a given fraction of the largest cluster in map"); sizeRatioOpt->addDoubleParameter(1, "ratio", "fraction of the largest cluster's area"); OptionalParameter* distanceOpt = ret->createOptionalParameter(12, "-distance", "ignore clusters further than a given distance from the largest cluster"); distanceOpt->addDoubleParameter(1, "distance", "how far from the largest cluster a cluster can be, edge to edge, in mm"); OptionalParameter* startOpt = ret->createOptionalParameter(10, "-start", "start labeling clusters from a value other than 1"); startOpt->addIntegerParameter(1, "startval", "the value to give the first cluster found"); ret->setHelpText( AString("Outputs a metric with nonzero integers for all vertices within a large enough cluster, and zeros elsewhere. ") + "The integers denote cluster membership (by default, first cluster found will use value 1, second cluster 2, etc). " + "By default, values greater than are considered to be in a cluster, use -less-than to test for values less than the threshold. " + "To apply this as a mask to the data, or to do more complicated thresholding, see -metric-math." ); return ret; } void AlgorithmMetricFindClusters::useParameters(OperationParameters* myParams, ProgressObject* myProgObj) { SurfaceFile* mySurf = myParams->getSurface(1); MetricFile* myMetric = myParams->getMetric(2); float threshVal = (float)myParams->getDouble(3); float minArea = (float)myParams->getDouble(4); MetricFile* myMetricOut = myParams->getOutputMetric(5); bool lessThan = myParams->getOptionalParameter(6)->m_present; MetricFile* myRoi = NULL; OptionalParameter* roiOption = myParams->getOptionalParameter(7); if (roiOption->m_present) { myRoi = roiOption->getMetric(1); } MetricFile* myAreas = NULL; OptionalParameter* corrAreasOpt = myParams->getOptionalParameter(8); if (corrAreasOpt->m_present) { myAreas = corrAreasOpt->getMetric(1); } OptionalParameter* columnSelect = myParams->getOptionalParameter(9); int columnNum = -1; if (columnSelect->m_present) {//set up to use the single column columnNum = (int)myMetric->getMapIndexFromNameOrNumber(columnSelect->getString(1)); if (columnNum < 0) { throw AlgorithmException("invalid column specified"); } } OptionalParameter* startOpt = myParams->getOptionalParameter(10); int startVal = 1; if (startOpt->m_present) { startVal = (int)startOpt->getInteger(1); } OptionalParameter* sizeRatioOpt = myParams->getOptionalParameter(11); float sizeRatio = -1.0f; if (sizeRatioOpt->m_present) { sizeRatio = sizeRatioOpt->getDouble(1); if (sizeRatio <= 0.0f) { throw AlgorithmException("area ratio must be positive"); } } OptionalParameter* distanceOpt = myParams->getOptionalParameter(12); float distanceCutoff = -1.0f; if (distanceOpt->m_present) { distanceCutoff = distanceOpt->getDouble(1); if (distanceCutoff <= 0.0f) { throw AlgorithmException("distance cutoff must be positive"); } } AlgorithmMetricFindClusters(myProgObj, mySurf, myMetric, threshVal, minArea, myMetricOut, lessThan, myRoi, myAreas, columnNum, startVal, NULL, sizeRatio, distanceCutoff); } namespace { struct Cluster { Cluster() { area = 0.0; } vector members; double area; }; void processColumn(const float* data, const float* roiData, const float* nodeAreas, TopologyHelper* myTopoHelp, GeodesicHelper* myGeoHelp, const float& threshVal, const float& minArea, const bool& lessThan, const float& areaRatio, const float& distanceCutoff, float* outData, int& markVal) { int numNodes = myTopoHelp->getNumberOfNodes(); vector marked(numNodes, 0); if (lessThan) { for (int i = 0; i < numNodes; ++i) { if ((roiData == NULL || roiData[i] > 0.0f) && data[i] < threshVal) { marked[i] = 1; } } } else { for (int i = 0; i < numNodes; ++i) { if ((roiData == NULL || roiData[i] > 0.0f) && data[i] > threshVal) { marked[i] = 1; } } } vector clusters; float biggestSize = 0.0f; int biggestCluster = -1; for (int i = 0; i < numNodes; ++i) { if (marked[i]) { Cluster newCluster; newCluster.members.push_back(i); marked[i] = 0;//unmark it when added to list to prevent multiples for (int index = 0; index < (int)newCluster.members.size(); ++index)//NOTE: vector grows inside loop { int node = newCluster.members[index];//keep list around so we can put it into the output immediately if it is large enough newCluster.area += nodeAreas[node]; const vector& neighbors = myTopoHelp->getNodeNeighbors(node); int numNeigh = (int)neighbors.size(); for (int n = 0; n < numNeigh; ++n) { const int32_t& neighbor = neighbors[n]; if (marked[neighbor]) { newCluster.members.push_back(neighbor); marked[neighbor] = 0; } } } if (newCluster.area > minArea) { if (newCluster.area > biggestSize) { biggestSize = newCluster.area; biggestCluster = (int)clusters.size(); } clusters.push_back(newCluster); } } } vector pathScratch; vector distScratch; if (!clusters.empty() && biggestCluster == -1) CaretLogWarning("clusters found, but none have positive area, check your vertex areas for negatives"); if (biggestCluster != -1 && (distanceCutoff > 0.0f || areaRatio > 0.0f)) { for (size_t i = 0; i < clusters.size(); ++i) { if ((int)i != biggestCluster) { bool erase = false; if (areaRatio > 0.0f) { if ((clusters[i].area / biggestSize) < areaRatio) { erase = true; } } if (!erase && distanceCutoff > 0.0f) { CaretAssert(myGeoHelp != NULL); myGeoHelp->getPathBetweenNodeLists(clusters[i].members, clusters[biggestCluster].members, distanceCutoff, pathScratch, distScratch, true); if (pathScratch.empty())//empty path means no path found { erase = true; } } if (erase) { clusters.erase(clusters.begin() + i);//remove it --i;//don't skip a cluster if (biggestCluster > (int)i) --biggestCluster;//don't lose track of the biggest cluster } } } } for (size_t i = 0; i < clusters.size(); ++i) { if (markVal == 0) { CaretLogInfo("skipping 0 for cluster marking"); ++markVal; } float tempVal = markVal; if ((int)tempVal != markVal) throw AlgorithmException("too many clusters, unable to mark them uniquely"); int numMembers = (int)clusters[i].members.size(); for (int index = 0; index < numMembers; ++index) { outData[clusters[i].members[index]] = tempVal; } ++markVal; } } } AlgorithmMetricFindClusters::AlgorithmMetricFindClusters(ProgressObject* myProgObj, const SurfaceFile* mySurf, const MetricFile* myMetric, const float& threshVal, const float& minArea, MetricFile* myMetricOut, const bool& lessThan, const MetricFile* myRoi, const MetricFile* myAreas, const int& columnNum, const int& startVal, int* endVal, const float& areaRatio, const float& distanceCutoff) : AbstractAlgorithm(myProgObj) { LevelProgress myProgress(myProgObj); if (startVal == 0) { throw AlgorithmException("0 is not a valid cluster marking start value"); } int numNodes = mySurf->getNumberOfNodes(); if (myMetric->getNumberOfNodes() != numNodes) throw AlgorithmException("metric does not match surface in number of vertices"); const float* roiData = NULL; if (myRoi != NULL) { if (myRoi->getNumberOfNodes() != numNodes) throw AlgorithmException("roi metric does not match surface in number of vertices"); roiData = myRoi->getValuePointerForColumn(0); } if (myAreas != NULL && myAreas->getNumberOfNodes() != mySurf->getNumberOfNodes()) { throw AlgorithmException("corrected area metric does not match surface in number of vertices"); } int numCols = myMetric->getNumberOfColumns(); if (columnNum < -1 || columnNum >= numCols) { throw AlgorithmException("invalid column number"); } vector nodeAreasVec; const float* nodeAreas = NULL; if (myAreas == NULL) { mySurf->computeNodeAreas(nodeAreasVec); nodeAreas = nodeAreasVec.data(); } else { nodeAreas = myAreas->getValuePointerForColumn(0); } CaretPointer myTopoHelp = mySurf->getTopologyHelper(); CaretPointer myGeoHelp; CaretPointer myGeoBase; if (distanceCutoff > 0.0f)//geodesic is only needed for distance cutoff { if (myAreas == NULL) { myGeoHelp = mySurf->getGeodesicHelper(); } else { myGeoBase.grabNew(new GeodesicHelperBase(mySurf, myAreas->getValuePointerForColumn(0))); myGeoHelp.grabNew(new GeodesicHelper(myGeoBase)); } } vector toSearch; int markVal = startVal;//give each cluster a different value, including across maps if (columnNum == -1) { myMetricOut->setNumberOfNodesAndColumns(numNodes, numCols); myMetricOut->setStructure(mySurf->getStructure()); for (int c = 0; c < numCols; ++c) { myMetricOut->setColumnName(c, myMetric->getColumnName(c)); vector outData(numNodes, 0.0f); const float* data = myMetric->getValuePointerForColumn(c); processColumn(data, roiData, nodeAreas, myTopoHelp, myGeoHelp, threshVal, minArea, lessThan, areaRatio, distanceCutoff, outData.data(), markVal); myMetricOut->setValuesForColumn(c, outData.data()); } } else { myMetricOut->setNumberOfNodesAndColumns(numNodes, 1); myMetricOut->setStructure(mySurf->getStructure()); myMetricOut->setColumnName(0, myMetric->getColumnName(columnNum)); vector outData(numNodes, 0.0f); const float* data = myMetric->getValuePointerForColumn(columnNum); processColumn(data, roiData, nodeAreas, myTopoHelp, myGeoHelp, threshVal, minArea, lessThan, areaRatio, distanceCutoff, outData.data(), markVal); myMetricOut->setValuesForColumn(0, outData.data()); } if (endVal != NULL) *endVal = markVal; } float AlgorithmMetricFindClusters::getAlgorithmInternalWeight() { return 1.0f;//override this if needed, if the progress bar isn't smooth } float AlgorithmMetricFindClusters::getSubAlgorithmWeight() { return 0.0f; } connectome-workbench-1.2.3+git41-gc4c6c90/src/Algorithms/AlgorithmMetricFindClusters.h000066400000000000000000000040671300200146000304550ustar00rootroot00000000000000#ifndef __ALGORITHM_METRIC_FIND_CLUSTERS_H__ #define __ALGORITHM_METRIC_FIND_CLUSTERS_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 "AbstractAlgorithm.h" namespace caret { class AlgorithmMetricFindClusters : public AbstractAlgorithm { AlgorithmMetricFindClusters(); protected: static float getSubAlgorithmWeight(); static float getAlgorithmInternalWeight(); public: AlgorithmMetricFindClusters(ProgressObject* myProgObj, const SurfaceFile* mySurf, const MetricFile* myMetric, const float& minVal, const float& minArea, MetricFile* myMetricOut, const bool& lessThan = false, const MetricFile* myRoi = NULL, const MetricFile* myAreas = NULL, const int& columnNum = -1, const int& startVal = 1, int* endVal = NULL, const float& areaRatio = -1.0f, const float& distanceCutoff = -1.0f); static OperationParameters* getParameters(); static void useParameters(OperationParameters* myParams, ProgressObject* myProgObj); static AString getCommandSwitch(); static AString getShortDescription(); }; typedef TemplateAutoOperation AutoAlgorithmMetricFindClusters; } #endif //__ALGORITHM_METRIC_FIND_CLUSTERS_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Algorithms/AlgorithmMetricGradient.cxx000066400000000000000000001036061300200146000301570ustar00rootroot00000000000000/*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 "AlgorithmMetricGradient.h" #include "AlgorithmMetricSmoothing.h" #include "AlgorithmException.h" #include "CaretOMP.h" #include "CaretLogger.h" #include "FloatMatrix.h" #include "MathFunctions.h" #include "MetricFile.h" #include "PaletteColorMapping.h" #include "SurfaceFile.h" #include "TopologyHelper.h" #include "Vector3D.h" #include using namespace caret; using namespace std; AString AlgorithmMetricGradient::getCommandSwitch() { return "-metric-gradient"; } AString AlgorithmMetricGradient::getShortDescription() { return "SURFACE GRADIENT OF A METRIC FILE"; } OperationParameters* AlgorithmMetricGradient::getParameters() { OperationParameters* ret = new OperationParameters(); ret->addSurfaceParameter(1, "surface", "the surface to compute the gradient on"); ret->addMetricParameter(2, "metric-in", "the metric to compute the gradient of"); ret->addMetricOutputParameter(3, "metric-out", "the magnitude of the gradient"); OptionalParameter* presmooth = ret->createOptionalParameter(4, "-presmooth", "smooth the metric before computing the gradient"); presmooth->addDoubleParameter(1, "kernel", "the sigma for the gaussian smoothing kernel, in mm"); OptionalParameter* roiOption = ret->createOptionalParameter(5, "-roi", "select a region of interest to take the gradient of"); roiOption->addMetricParameter(1, "roi-metric", "the area to take the gradient within, as a metric"); roiOption->createOptionalParameter(2, "-match-columns", "for each input column, use the corresponding column from the roi"); OptionalParameter* vecOut = ret->createOptionalParameter(6, "-vectors", "output gradient vectors"); vecOut->addMetricOutputParameter(1, "vector-metric-out", "the vectors as a metric file"); OptionalParameter* columnSelect = ret->createOptionalParameter(7, "-column", "select a single column to compute the gradient of"); columnSelect->addStringParameter(1, "column", "the column number or name"); 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->createOptionalParameter(9, "-average-normals", "average the normals of each vertex with its neighbors before using them to compute the gradient"); //that option has no parameters to take, so don't store the return value ret->setHelpText( AString("At each vertex, the immediate neighbors are unfolded onto a plane tangent to the surface at the vertex (specifically, perpendicular to the normal). ") + "The gradient is computed using a regression between the unfolded positions of the vertices and their values. " + "The gradient is then given by the slopes of the regression, and reconstructed as a 3D gradient vector. " + "By default, takes the gradient of all columns, with no presmoothing, across the whole surface, without averaging the normals of the surface among neighbors.\n\n" + "When using -corrected-areas, note that it is an approximate correction. " + "Doing smoothing on individual surfaces before averaging/gradient is preferred, when possible, in order to make use of the original surface structure.\n\n" + "Specifying an ROI will restrict the gradient to only use data from where the ROI metric is positive, and output zeros anywhere the ROI metric is not positive.\n\n" + "By default, the first column of the roi metric is used for all input columns. " + "When -match-columns is specified to the -roi option, the input and roi metrics must have the same number of columns, " + "and for each input column's index, the same column index is used in the roi metric. " + "If the -match-columns option to -roi is used while the -column option is also used, the number of columns of the roi metric must match the input metric, " + "and it will use the roi column with the index of the selected input column.\n\n" + "The vector output metric is organized such that the X, Y, and Z components from a single input column are consecutive columns." ); return ret; } void AlgorithmMetricGradient::useParameters(OperationParameters* myParams, ProgressObject* myProgObj) { SurfaceFile* mySurf = myParams->getSurface(1); MetricFile* myMetricIn = myParams->getMetric(2); MetricFile* myMetricOut = myParams->getOutputMetric(3); float myPresmooth = -1.0f;//negative or zero means no smoothing OptionalParameter* presmooth = myParams->getOptionalParameter(4); if (presmooth->m_present) { myPresmooth = (float)presmooth->getDouble(1); if (myPresmooth <= 0.0f) throw AlgorithmException("presmooth kernel size must be positive"); } MetricFile* myRoi = NULL; bool matchRoiColumns = false; OptionalParameter* roiOption = myParams->getOptionalParameter(5); if (roiOption->m_present) { myRoi = roiOption->getMetric(1); matchRoiColumns = roiOption->getOptionalParameter(2)->m_present; } MetricFile* myVectorsOut = NULL; OptionalParameter* vecOut = myParams->getOptionalParameter(6); if (vecOut->m_present) { myVectorsOut = vecOut->getOutputMetric(1); } int32_t myColumn = -1; OptionalParameter* columnSelect = myParams->getOptionalParameter(7); if (columnSelect->m_present) { myColumn = (int)myMetricIn->getMapIndexFromNameOrNumber(columnSelect->getString(1)); if (myColumn < 0) { throw AlgorithmException("invalid column specified"); } } MetricFile* corrAreaMetric = NULL; OptionalParameter* corrAreaOpt = myParams->getOptionalParameter(8); if (corrAreaOpt->m_present) { corrAreaMetric = corrAreaOpt->getMetric(1); } OptionalParameter* avgNormals = myParams->getOptionalParameter(9); bool myAvgNormals = avgNormals->m_present; AlgorithmMetricGradient(myProgObj, mySurf, myMetricIn, myMetricOut, myVectorsOut, myPresmooth, myRoi, myAvgNormals, myColumn, corrAreaMetric, matchRoiColumns);//executes the algorithm } AlgorithmMetricGradient::AlgorithmMetricGradient(ProgressObject* myProgObj, SurfaceFile* mySurf, const MetricFile* myMetricIn, MetricFile* myMetricOut, MetricFile* myVectorsOut, const float myPresmooth, const MetricFile* myRoi, const bool myAvgNormals, const int32_t myColumn, const MetricFile* corrAreaMetric, const bool matchRoiColumns) : AbstractAlgorithm(myProgObj) { ProgressObject* smoothProgress = NULL; if (myProgObj != NULL && myPresmooth > 0.0f) { smoothProgress = myProgObj->addAlgorithm(AlgorithmMetricSmoothing::getAlgorithmWeight()); } LevelProgress myProgress(myProgObj); int32_t numNodes = mySurf->getNumberOfNodes(); if (myMetricIn->getNumberOfNodes() != numNodes) {//TODO: write these as static functions into an AlgorithmHelper class? throw AlgorithmException("metric does not match surface in number of vertices"); } if (myRoi != NULL && myRoi->getNumberOfNodes() != numNodes) { throw AlgorithmException("roi metric does not match surface in number of vertices"); } if (corrAreaMetric != NULL && corrAreaMetric->getNumberOfNodes() != numNodes) { throw AlgorithmException("corrected areas metric does not match surface in number of vertices"); } int32_t numColumns = myMetricIn->getNumberOfColumns(); if (myColumn < -1 || myColumn >= numColumns) { throw AlgorithmException("invalid column number"); } if (myRoi != NULL && matchRoiColumns && numColumns != myRoi->getNumberOfColumns()) { throw AlgorithmException("match roi columns specified, but roi metric has the wrong number of columns"); } const MetricFile* toProcess = myMetricIn; MetricFile processTemp; int32_t useColumn = myColumn; if (myPresmooth > 0.0f) { AlgorithmMetricSmoothing(smoothProgress, mySurf, myMetricIn, myPresmooth, &processTemp, myRoi, matchRoiColumns, false, myColumn, corrAreaMetric, MetricSmoothingObject::GEO_GAUSS_AREA); toProcess = &processTemp; if (myColumn != -1) { useColumn = 0; } } const float* myNormals = NULL; vector avgNormalStorage; if (myAvgNormals) { avgNormalStorage = mySurf->computeAverageNormals(); myNormals = avgNormalStorage.data(); } else { mySurf->computeNormals(); myNormals = mySurf->getNormalData(); } vector sqrtCorrAreas;//same logic as GeodesicHelper vector sqrtVertAreas; const float* vertAreas = NULL; vector areaData; if (corrAreaMetric != NULL) { sqrtCorrAreas.resize(numNodes); mySurf->computeNodeAreas(sqrtVertAreas); const float* corrAreaData = corrAreaMetric->getValuePointerForColumn(0); for (int i = 0; i < numNodes; ++i) { sqrtCorrAreas[i] = sqrt(corrAreaData[i]); sqrtVertAreas[i] = sqrt(sqrtVertAreas[i]); } vertAreas = corrAreaData; } else { mySurf->computeNodeAreas(areaData); vertAreas = areaData.data(); } const float* myCoords = mySurf->getCoordinateData(); bool haveWarned = false, haveFailed = false;//print warning or failure messages only once if (myColumn == -1) { myMetricOut->setNumberOfNodesAndColumns(numNodes, numColumns); myMetricOut->setStructure(mySurf->getStructure()); float* myVecScratch = NULL; if (myVectorsOut != NULL) { myVectorsOut->setNumberOfNodesAndColumns(numNodes, numColumns * 3); myVectorsOut->setStructure(mySurf->getStructure()); myVecScratch = new float[numNodes * 3]; } float* myScratch = new float[numNodes]; for (int32_t col = 0; col < numColumns; ++col) { const float* myRoiColumn = NULL; if (myRoi != NULL) { if (matchRoiColumns) { myRoiColumn = myRoi->getValuePointerForColumn(col); } else { myRoiColumn = myRoi->getValuePointerForColumn(0); } } const float* myMetricColumn = toProcess->getValuePointerForColumn(col); myMetricOut->setColumnName(col, toProcess->getColumnName(col) + ", gradient"); *(myMetricOut->getPaletteColorMapping(col)) = *(toProcess->getPaletteColorMapping(col));//copy the palette settings if (myVectorsOut != NULL) { myVectorsOut->setColumnName(col * 3, toProcess->getColumnName(col) + ", gradient vector X"); myVectorsOut->setColumnName(col * 3 + 1, toProcess->getColumnName(col) + ", gradient vector Y"); myVectorsOut->setColumnName(col * 3 + 2, toProcess->getColumnName(col) + ", gradient vector Z"); } #pragma omp CARET_PAR { Vector3D somevec, xhat, yhat; float sanity = 0.0f; CaretPointer myTopoHelp = mySurf->getTopologyHelper();//this stores and reuses helpers, so it isn't really a problem to call inside the loop #pragma omp CARET_FOR schedule(dynamic) for (int32_t i = 0; i < numNodes; ++i) { if (myRoiColumn != NULL && myRoiColumn[i] <= 0.0f) { myScratch[i] = 0.0f; if (myVecScratch != NULL) { myVecScratch[i] = 0.0f; myVecScratch[i + numNodes] = 0.0f; myVecScratch[i + numNodes * 2] = 0.0f; } continue; } int32_t numNeigh; int32_t i3 = i * 3; const int32_t* myNeighbors = myTopoHelp->getNodeNeighbors(i, numNeigh);//one function call isn't that bad, most time spent is floating point math anyway Vector3D myNormal = Vector3D(myNormals + i3).normal();//should already be normalized, but just in case Vector3D myCoord = myCoords + i3; float nodeValue = myMetricColumn[i]; somevec[2] = 0.0; if (abs(myNormal[0]) > abs(myNormal[1])) {//generate a vector not parallel to normal somevec[0] = 0.0; somevec[1] = 1.0; } else { somevec[0] = 1.0; somevec[1] = 0.0; } xhat = myNormal.cross(somevec).normal(); yhat = myNormal.cross(xhat).normal();//xhat, yhat are orthogonal unit vectors describing a coord system with k = surface normal int neighCount = 0;//count within-roi neighbors, not simply surface neighbors if (numNeigh >= 2) { FloatMatrix myRegress = FloatMatrix::zeros(3, 4); for (int32_t j = 0; j < numNeigh; ++j) { int32_t whichNode = myNeighbors[j]; int32_t whichNode3 = whichNode * 3; if (myRoiColumn == NULL || myRoiColumn[whichNode] > 0.0f) { ++neighCount; float tempf = myMetricColumn[whichNode] - nodeValue; Vector3D neighCoord = myCoords + whichNode3; somevec = neighCoord - myCoord; float origMag = somevec.length();//save the original length float unrollMag = origMag; float opposite = somevec.dot(myNormal);//check for division by close to zero if (abs(opposite) > 0.035f * origMag)//do not do unrolling on very small angles - this is ~2 degrees { unrollMag = origMag * asin(opposite / origMag) * origMag / opposite; } if (corrAreaMetric != NULL) { unrollMag *= (sqrtCorrAreas[i] + sqrtCorrAreas[whichNode]) / (sqrtVertAreas[i] + sqrtVertAreas[whichNode]); } float xmag = xhat.dot(somevec);//dot product to get the direction in 2d float ymag = yhat.dot(somevec); float mag2d = sqrt(xmag * xmag + ymag * ymag);//get the new magnitude, to divide out xmag *= unrollMag / mag2d;//normalize the 2d vector and multiply by unrolled length ymag *= unrollMag / mag2d; myRegress[0][0] += xmag * xmag * vertAreas[whichNode];//gather A'A and A'b sums for regression, weighted by vertex area myRegress[0][1] += xmag * ymag * vertAreas[whichNode]; myRegress[0][2] += xmag * vertAreas[whichNode]; myRegress[1][1] += ymag * ymag * vertAreas[whichNode]; myRegress[1][2] += ymag * vertAreas[whichNode]; myRegress[2][2] += vertAreas[whichNode]; myRegress[0][3] += xmag * tempf * vertAreas[whichNode]; myRegress[1][3] += ymag * tempf * vertAreas[whichNode]; myRegress[2][3] += tempf * vertAreas[whichNode]; } } if (neighCount >= 2) { myRegress[1][0] = myRegress[0][1];//complete the symmetric elements myRegress[2][0] = myRegress[0][2]; myRegress[2][1] = myRegress[1][2]; myRegress[2][2] += vertAreas[i];//include center (metric and coord differences will be zero, so this is all that is needed) FloatMatrix myRref = myRegress.reducedRowEchelon(); somevec = xhat * myRref[0][3] + yhat * myRref[1][3];//somevec is now our surface gradient sanity = somevec[0] + somevec[1] + somevec[2]; } } if (neighCount > 0 && (neighCount < 2 || sanity != sanity)) { if (!haveWarned && myRoi == NULL) {//don't issue this warning with an ROI, because it is somewhat expected haveWarned = true; CaretLogWarning("WARNING: gradient calculation found a NaN/inf with regression method for at least vertex " + AString::number(i)); } float xgrad = 0.0f, ygrad = 0.0f, totalWeight = 0.0f; for (int32_t j = 0; j < numNeigh; ++j) { int32_t whichNode = myNeighbors[j]; int32_t whichNode3 = whichNode * 3; if (myRoiColumn == NULL || myRoiColumn[whichNode] > 0.0f) { float tempf = myMetricColumn[whichNode] - nodeValue; Vector3D neighCoord = myCoords + whichNode3; somevec = neighCoord - myCoord; float origMag = somevec.length();//save the original length float unrollMag = origMag; float opposite = somevec.dot(myNormal);//check for division by close to zero if (abs(opposite) > 0.035f * origMag)//do not do unrolling on very small angles - this is ~2 degrees { unrollMag = origMag * asin(opposite / origMag) * origMag / opposite; } if (corrAreaMetric != NULL) { unrollMag *= (sqrtCorrAreas[i] + sqrtCorrAreas[whichNode]) / (sqrtVertAreas[i] + sqrtVertAreas[whichNode]); } float xmag = xhat.dot(somevec);//dot product to get the direction in 2d float ymag = yhat.dot(somevec); float mag2d = sqrt(xmag * xmag + ymag * ymag);//get the new magnitude, to divide out tempf /= unrollMag * mag2d;//difference divided by distance gives point estimate of gradient magnitude, also divide by magnitude of 2d vector to normalize the next step at the same time xgrad += xmag * tempf * vertAreas[whichNode];//point estimate of gradient magnitude times normalized projected direction gives 2d estimate of gradient ygrad += ymag * tempf * vertAreas[whichNode];//average point estimates for each neighbor to estimate local gradient totalWeight += vertAreas[whichNode]; } } xgrad /= totalWeight;//weighted average ygrad /= totalWeight; somevec = xhat * xgrad + yhat * ygrad;//unproject back into 3d sanity = somevec[0] + somevec[1] + somevec[2]; } if (neighCount <= 0 || sanity != sanity) { if (!haveFailed && myRoiColumn == NULL) {//don't warn with an roi, they can be strange haveFailed = true; CaretLogWarning("Failed to compute gradient for at least vertex " + AString::number(i) + " with standard and fallback methods, outputting ZERO, check your surface for disconnected vertices or other strangeness"); } somevec[0] = 0.0f; somevec[1] = 0.0f; somevec[2] = 0.0f; } if (myVecScratch != NULL) { myVecScratch[i] = somevec[0];//split them up far, so that they can be set to columns easily myVecScratch[numNodes + i] = somevec[1]; myVecScratch[numNodes * 2 + i] = somevec[2]; } myScratch[i] = MathFunctions::vectorLength(somevec); } } if (myVectorsOut != NULL) { myVectorsOut->setValuesForColumn(col * 3, myVecScratch); myVectorsOut->setValuesForColumn(col * 3 + 1, myVecScratch + numNodes); myVectorsOut->setValuesForColumn(col * 3 + 2, myVecScratch + (numNodes * 2)); } myMetricOut->setValuesForColumn(col, myScratch); myProgress.reportProgress(((float)col + 1) / numColumns); } delete[] myScratch; if (myVecScratch != NULL) { delete[] myVecScratch; } } else { myMetricOut->setNumberOfNodesAndColumns(numNodes, 1); myMetricOut->setStructure(mySurf->getStructure()); float* myVecScratch = NULL; if (myVectorsOut != NULL) { myVectorsOut->setNumberOfNodesAndColumns(numNodes, 3); myVectorsOut->setStructure(mySurf->getStructure()); myVectorsOut->setColumnName(0, toProcess->getColumnName(useColumn) + ", gradient vector X"); myVectorsOut->setColumnName(1, toProcess->getColumnName(useColumn) + ", gradient vector Y"); myVectorsOut->setColumnName(2, toProcess->getColumnName(useColumn) + ", gradient vector Z"); myVecScratch = new float[numNodes * 3]; } float* myScratch = new float[numNodes]; const float* myRoiColumn = NULL; if (myRoi != NULL) { if (matchRoiColumns) { myRoiColumn = myRoi->getValuePointerForColumn(myColumn);//use the ORIGINAL column number, not the one that has been modified due to a presmoothing step that generated a new single column metric } else { myRoiColumn = myRoi->getValuePointerForColumn(0); } } const float* myMetricColumn = toProcess->getValuePointerForColumn(useColumn); myMetricOut->setColumnName(0, toProcess->getColumnName(useColumn) + ", gradient"); *(myMetricOut->getPaletteColorMapping(0)) = *(toProcess->getPaletteColorMapping(useColumn));//copy the palette settings #pragma omp CARET_PAR { Vector3D somevec, xhat, yhat; float sanity = 0.0f; CaretPointer myTopoHelp = mySurf->getTopologyHelper(); #pragma omp CARET_FOR schedule(dynamic) for (int32_t i = 0; i < numNodes; ++i) { if (myRoiColumn != NULL && myRoiColumn[i] <= 0.0f) { myScratch[i] = 0.0f; if (myVecScratch != NULL) { myVecScratch[i] = 0.0f; myVecScratch[i + numNodes] = 0.0f; myVecScratch[i + numNodes * 2] = 0.0f; } continue; } int32_t numNeigh; int32_t i3 = i * 3; const int32_t* myNeighbors = myTopoHelp->getNodeNeighbors(i, numNeigh); Vector3D myNormal = Vector3D(myNormals + i3).normal();//should already be normalized, but just in case Vector3D myCoord = myCoords + i3; float nodeValue = myMetricColumn[i]; somevec[2] = 0.0; if (myNormal[0] > myNormal[1]) {//generate a vector not parallel to normal somevec[0] = 0.0; somevec[1] = 1.0; } else { somevec[0] = 1.0; somevec[1] = 0.0; } xhat = myNormal.cross(somevec).normal(); yhat = myNormal.cross(xhat).normal();//xhat, yhat are orthogonal unit vectors describing a coord system with k = surface normal int neighCount = 0;//count within-roi neighbors, not simply surface neighbors if (numNeigh >= 2) { FloatMatrix myRegress = FloatMatrix::zeros(3, 4); for (int32_t j = 0; j < numNeigh; ++j) { int32_t whichNode = myNeighbors[j]; int32_t whichNode3 = whichNode * 3; if (myRoiColumn == NULL || myRoiColumn[whichNode] > 0.0f) { ++neighCount; float tempf = myMetricColumn[whichNode] - nodeValue; Vector3D neighCoord = myCoords + whichNode3; somevec = neighCoord - myCoord; float origMag = somevec.length();//save the original length float unrollMag = origMag; float opposite = somevec.dot(myNormal);//check for division by close to zero if (abs(opposite) > 0.035f * origMag)//do not do unrolling on very small angles - this is ~2 degrees { unrollMag = origMag * asin(opposite / origMag) * origMag / opposite; } if (corrAreaMetric != NULL) { unrollMag *= (sqrtCorrAreas[i] + sqrtCorrAreas[whichNode]) / (sqrtVertAreas[i] + sqrtVertAreas[whichNode]); } float xmag = xhat.dot(somevec);//dot product to get the direction in 2d float ymag = yhat.dot(somevec); float mag2d = sqrt(xmag * xmag + ymag * ymag);//get the new magnitude, to divide out xmag *= unrollMag / mag2d;//normalize the 2d vector and multiply by unrolled length ymag *= unrollMag / mag2d; myRegress[0][0] += xmag * xmag * vertAreas[whichNode];//gather A'A and A'b sums for regression, weighted by vertex area myRegress[0][1] += xmag * ymag * vertAreas[whichNode]; myRegress[0][2] += xmag * vertAreas[whichNode]; myRegress[1][1] += ymag * ymag * vertAreas[whichNode]; myRegress[1][2] += ymag * vertAreas[whichNode]; myRegress[2][2] += vertAreas[whichNode]; myRegress[0][3] += xmag * tempf * vertAreas[whichNode]; myRegress[1][3] += ymag * tempf * vertAreas[whichNode]; myRegress[2][3] += tempf * vertAreas[whichNode]; } } if (neighCount >= 2) { myRegress[1][0] = myRegress[0][1];//complete the symmetric elements myRegress[2][0] = myRegress[0][2]; myRegress[2][1] = myRegress[1][2]; myRegress[2][2] += vertAreas[i];//include center (metric and coord differences will be zero, so this is all that is needed) FloatMatrix myRref = myRegress.reducedRowEchelon(); somevec = xhat * myRref[0][3] + yhat * myRref[1][3];//somevec is now our surface gradient sanity = somevec[0] + somevec[1] + somevec[2]; } } if (neighCount > 0 && (neighCount < 2 || sanity != sanity)) { if (!haveWarned && myRoi == NULL) {//don't issue this warning with an ROI, because it is somewhat expected haveWarned = true; CaretLogWarning("WARNING: gradient calculation found a NaN/inf with regression method for at least vertex " + AString::number(i)); } float xgrad = 0.0f, ygrad = 0.0f, totalWeight = 0.0f; for (int32_t j = 0; j < numNeigh; ++j) { int32_t whichNode = myNeighbors[j]; int32_t whichNode3 = whichNode * 3; if (myRoiColumn == NULL || myRoiColumn[whichNode] > 0.0f) { float tempf = myMetricColumn[whichNode] - nodeValue; Vector3D neighCoord = myCoords + whichNode3; somevec = neighCoord - myCoord; float origMag = somevec.length();//save the original length float unrollMag = origMag; float opposite = somevec.dot(myNormal);//check for division by close to zero if (abs(opposite) > 0.035f * origMag)//do not do unrolling on very small angles - this is ~2 degrees { unrollMag = origMag * asin(opposite / origMag) * origMag / opposite; } if (corrAreaMetric != NULL) { unrollMag *= (sqrtCorrAreas[i] + sqrtCorrAreas[whichNode]) / (sqrtVertAreas[i] + sqrtVertAreas[whichNode]); } float xmag = xhat.dot(somevec);//dot product to get the direction in 2d float ymag = yhat.dot(somevec); float mag2d = sqrt(xmag * xmag + ymag * ymag);//get the new magnitude, to divide out tempf /= unrollMag * mag2d;//difference divided by distance gives point estimate of gradient magnitude, also divide by magnitude of 2d vector to normalize the next step at the same time xgrad += xmag * tempf * vertAreas[whichNode];//point estimate of gradient magnitude times normalized projected direction gives 2d estimate of gradient ygrad += ymag * tempf * vertAreas[whichNode];//average point estimates for each neighbor to estimate local gradient totalWeight += vertAreas[whichNode]; } } xgrad /= totalWeight;//weighted average ygrad /= totalWeight; somevec = xhat * xgrad + yhat * ygrad;//unproject back into 3d sanity = somevec[0] + somevec[1] + somevec[2]; } if (neighCount <= 0 || sanity != sanity) { if (!haveFailed && myRoiColumn == NULL) {//don't warn with an roi, they can be strange haveFailed = true; CaretLogWarning("Failed to compute gradient for at least vertex " + AString::number(i) + " with standard and fallback methods, outputting ZERO, check your surface for disconnected vertices or other strangeness"); } somevec[0] = 0.0f; somevec[1] = 0.0f; somevec[2] = 0.0f; } if (myVecScratch != NULL) { myVecScratch[i] = somevec[0];//split them up far, so that they can be set to columns easily myVecScratch[numNodes + i] = somevec[1]; myVecScratch[numNodes * 2 + i] = somevec[2]; } myScratch[i] = MathFunctions::vectorLength(somevec); } } if (myVectorsOut != NULL) { myVectorsOut->setValuesForColumn(0, myVecScratch); myVectorsOut->setValuesForColumn(1, myVecScratch + numNodes); myVectorsOut->setValuesForColumn(2, myVecScratch + (numNodes * 2)); } myMetricOut->setValuesForColumn(0, myScratch); delete[] myScratch; if (myVecScratch != NULL) { delete[] myVecScratch; } } } float AlgorithmMetricGradient::getAlgorithmInternalWeight() { return 1.0f;//override this if needed, if the progress bar isn't smooth } float AlgorithmMetricGradient::getSubAlgorithmWeight() { return AlgorithmMetricSmoothing::getAlgorithmWeight();//if you use a subalgorithm } connectome-workbench-1.2.3+git41-gc4c6c90/src/Algorithms/AlgorithmMetricGradient.h000066400000000000000000000043271300200146000276040ustar00rootroot00000000000000#ifndef __ALGORITHM_METRIC_GRADIENT_H__ #define __ALGORITHM_METRIC_GRADIENT_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 "AbstractAlgorithm.h" namespace caret { class AlgorithmMetricGradient : public AbstractAlgorithm { AlgorithmMetricGradient(); protected: static float getSubAlgorithmWeight(); static float getAlgorithmInternalWeight(); public: AlgorithmMetricGradient(ProgressObject* myProgObj, SurfaceFile* mySurf, const MetricFile* myMetricIn, MetricFile* myMetricOut, MetricFile* myVectorsOut = NULL, const float myPresmooth = -1.0f, const MetricFile* myRoi = NULL, const bool myAvgNormals = false, const int32_t myColumn = -1, const MetricFile* corrAreaMetric = NULL, bool matchRoiColumns = false); static OperationParameters* getParameters(); static void useParameters(OperationParameters* myParams, ProgressObject* myProgObj); static AString getCommandSwitch(); static AString getShortDescription(); }; typedef TemplateAutoOperation AutoAlgorithmMetricGradient; } #endif //__ALGORITHM_METRIC_GRADIENT_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Algorithms/AlgorithmMetricROIsFromExtrema.cxx000066400000000000000000000340751300200146000314130ustar00rootroot00000000000000/*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 "AlgorithmMetricROIsFromExtrema.h" #include "AlgorithmException.h" #include "CaretAssert.h" #include "GeodesicHelper.h" #include "MetricFile.h" #include "SurfaceFile.h" #include using namespace caret; using namespace std; AString AlgorithmMetricROIsFromExtrema::getCommandSwitch() { return "-metric-rois-from-extrema"; } AString AlgorithmMetricROIsFromExtrema::getShortDescription() { return "CREATE METRIC ROI MAPS FROM EXTREMA MAPS"; } OperationParameters* AlgorithmMetricROIsFromExtrema::getParameters() { OperationParameters* ret = new OperationParameters(); ret->addSurfaceParameter(1, "surface", "the surface to use for geodesic distance"); ret->addMetricParameter(2, "metric", "the input metric file"); ret->addDoubleParameter(3, "limit", "geodesic distance limit from vertex, in mm"); ret->addMetricOutputParameter(4, "metric-out", "the output metric file"); OptionalParameter* gaussOpt = ret->createOptionalParameter(5, "-gaussian", "generate a gaussian kernel instead of a flat ROI"); gaussOpt->addDoubleParameter(1, "sigma", "the sigma for the gaussian kernel, in mm"); OptionalParameter* roiOption = ret->createOptionalParameter(6, "-roi", "select a region of interest to use"); roiOption->addMetricParameter(1, "roi-metric", "the area to use, as a metric"); OptionalParameter* overlapOpt = ret->createOptionalParameter(7, "-overlap-logic", "how to handle overlapping ROIs, default ALLOW"); overlapOpt->addStringParameter(1, "method", "the method of resolving overlaps"); OptionalParameter* columnSelect = ret->createOptionalParameter(8, "-column", "select a single input column to use"); columnSelect->addStringParameter(1, "column", "the column number or name"); ret->setHelpText( AString("For each nonzero value in each map, make a map with an ROI around that location. ") + "If the -gaussian option is specified, then normalized gaussian kernels are output instead of ROIs. " + "The argument to -overlap-logic must be one of ALLOW, CLOSEST, or EXCLUDE. " + "ALLOW is the default, and means that ROIs are treated independently and may overlap. " + "CLOSEST means that ROIs may not overlap, and that no ROI contains vertices that are closer to a different seed vertex. " + "EXCLUDE means that ROIs may not overlap, and that any vertex within range of more than one ROI does not belong to any ROI." ); return ret; } void AlgorithmMetricROIsFromExtrema::useParameters(OperationParameters* myParams, ProgressObject* myProgObj) { SurfaceFile* mySurf = myParams->getSurface(1); MetricFile* myMetric = myParams->getMetric(2); float limit = (float)myParams->getDouble(3); MetricFile* myMetricOut = myParams->getOutputMetric(4); OptionalParameter* gaussOpt = myParams->getOptionalParameter(5); float sigma = -1.0f; if (gaussOpt->m_present) {//set up to use a gaussian function sigma = (float)gaussOpt->getDouble(1); if (sigma <= 0.0f) { throw AlgorithmException("invalid sigma specified"); } } MetricFile* myRoi = NULL; OptionalParameter* roiOption = myParams->getOptionalParameter(6); if (roiOption->m_present) { myRoi = roiOption->getMetric(1); } OverlapLogicEnum::Enum overlapType = OverlapLogicEnum::ALLOW; OptionalParameter* overlapOpt = myParams->getOptionalParameter(7); if (overlapOpt->m_present) { bool ok = false; overlapType = OverlapLogicEnum::fromName(overlapOpt->getString(1), &ok); if (!ok) throw AlgorithmException("unrecognized overlap method: " + overlapOpt->getString(1)); } int myColumn = -1; OptionalParameter* columnSelect = myParams->getOptionalParameter(8); if (columnSelect->m_present) { myColumn = (int)myMetric->getMapIndexFromNameOrNumber(columnSelect->getString(1)); if (myColumn < 0) { throw AlgorithmException("invalid column specified"); } } AlgorithmMetricROIsFromExtrema(myProgObj, mySurf, myMetric, limit, myMetricOut, sigma, myRoi, overlapType, myColumn); } AlgorithmMetricROIsFromExtrema::AlgorithmMetricROIsFromExtrema(ProgressObject* myProgObj, const SurfaceFile* mySurf, const MetricFile* myMetric, const float& limit, MetricFile* myMetricOut, const float& sigma, const MetricFile* myRoi, const OverlapLogicEnum::Enum& overlapType, const int& myColumn) : AbstractAlgorithm(myProgObj) { LevelProgress myProgress(myProgObj); int numNodes = mySurf->getNumberOfNodes(); if (myMetric->getNumberOfNodes() != numNodes) throw AlgorithmException("surface and metric files have different number of nodes"); const float* roiData = NULL; if (myRoi != NULL) { if (myRoi->getNumberOfNodes() != numNodes) throw AlgorithmException("roi metric has a different number of nodes"); roiData = myRoi->getValuePointerForColumn(0); } int numCols = myMetric->getNumberOfColumns(); if (myColumn < -1 || myColumn >= numCols) { throw AlgorithmException("invalid column number"); } int64_t extremaCount = 0; if (myColumn == -1) { if (roiData == NULL) { for (int i = 0; i < numCols; ++i) { const float* data = myMetric->getValuePointerForColumn(i); for (int j = 0; j < numNodes; ++j) { if (data[j] != 0.0f) ++extremaCount; } } } else { for (int i = 0; i < numCols; ++i) { const float* data = myMetric->getValuePointerForColumn(i); for (int j = 0; j < numNodes; ++j) { if (roiData[j] > 0.0f && data[j] != 0.0f) ++extremaCount; } } } } else { if (roiData == NULL) { const float* data = myMetric->getValuePointerForColumn(0); for (int j = 0; j < numNodes; ++j) { if (data[j] != 0.0f) ++extremaCount; } } else { const float* data = myMetric->getValuePointerForColumn(0); for (int j = 0; j < numNodes; ++j) { if (roiData[j] > 0.0f && data[j] != 0.0f) ++extremaCount; } } } if (extremaCount >= (1LL<<31)) throw AlgorithmException("too many output maps for a metric file");//hopefully this is never needed int32_t mapsOut = (int32_t)extremaCount; vector excludeDists(numNodes, -1.0f); vector excludeSources(numNodes, -1); vector > roiLists(mapsOut); CaretPointer myHelp = mySurf->getGeodesicHelper(); int64_t mapCounter = 0; if (myColumn == -1) { for (int i = 0; i < numCols; ++i) { const float* data = myMetric->getValuePointerForColumn(i); processMap(data, excludeDists, excludeSources, roiLists, mapCounter, myHelp, limit, roiData, overlapType, numNodes); } } else { const float* data = myMetric->getValuePointerForColumn(0); processMap(data, excludeDists, excludeSources, roiLists, mapCounter, myHelp, limit, roiData, overlapType, numNodes); } CaretAssert(mapCounter == extremaCount); myMetricOut->setNumberOfNodesAndColumns(numNodes, mapsOut); myMetricOut->setStructure(mySurf->getStructure()); vector tempCol(numNodes, 0.0f); if (sigma > 0.0f) { float gaussDenom = -0.5f / sigma / sigma; for (int32_t i = 0; i < mapsOut; ++i) { double accum = 0.0; map::iterator myEnd = roiLists[i].end(); for (map::iterator iter = roiLists[i].begin(); iter != myEnd; ++iter) { float gaussVal = exp(iter->second * iter->second * gaussDenom); accum += gaussVal; tempCol[iter->first] = gaussVal;//initial kernel value } for (map::iterator iter = roiLists[i].begin(); iter != myEnd; ++iter) { tempCol[iter->first] /= accum;//normalize } myMetricOut->setValuesForColumn(i, tempCol.data()); for (map::iterator iter = roiLists[i].begin(); iter != myEnd; ++iter) { tempCol[iter->first] = 0.0f;//rezero changed values for next map } } } else { for (int32_t i = 0; i < mapsOut; ++i) { map::iterator myEnd = roiLists[i].end(); for (map::iterator iter = roiLists[i].begin(); iter != myEnd; ++iter) { tempCol[iter->first] = 1.0f;//make roi } myMetricOut->setValuesForColumn(i, tempCol.data()); for (map::iterator iter = roiLists[i].begin(); iter != myEnd; ++iter) { tempCol[iter->first] = 0.0f;//rezero changed values for next map } } } } void AlgorithmMetricROIsFromExtrema::processMap(const float* data, vector& excludeDists, vector excludeSources, vector >& roiLists, int64_t& mapCounter, CaretPointer& myHelp, const float& limit, const float* roiData, const OverlapLogicEnum::Enum& overlapType, const int& numNodes) { for (int j = 0; j < numNodes; ++j) { if ((roiData == NULL || roiData[j] > 0.0f) && data[j] != 0.0f) { vector nodeList; vector distList; myHelp->getNodesToGeoDist(j, limit, nodeList, distList); int listNum = (int)nodeList.size(); switch (overlapType) { case OverlapLogicEnum::ALLOW: if (roiData == NULL) { for (int k = 0; k < listNum; ++k) { const int32_t& thisNode = nodeList[k]; roiLists[mapCounter][thisNode] = distList[k]; } } else { for (int k = 0; k < listNum; ++k) { const int32_t& thisNode = nodeList[k]; if (roiData[thisNode] > 0.0f) { roiLists[mapCounter][thisNode] = distList[k]; } } } break; case OverlapLogicEnum::CLOSEST: for (int k = 0; k < listNum; ++k) { const int32_t& thisNode = nodeList[k]; if (roiData == NULL || roiData[thisNode] > 0.0f) { const float& thisDist = distList[k]; if (excludeDists[thisNode] < 0.0f) { excludeDists[thisNode] = thisDist; excludeSources[thisNode] = mapCounter; roiLists[mapCounter][thisNode] = thisDist; } else { if (excludeDists[thisNode] > thisDist) { roiLists[excludeSources[thisNode]].erase(thisNode); } excludeDists[thisNode] = thisDist; excludeSources[thisNode] = mapCounter; roiLists[mapCounter][thisNode] = thisDist; } } } break; case OverlapLogicEnum::EXCLUDE: for (int k = 0; k < listNum; ++k) { const int32_t& thisNode = nodeList[k]; if (roiData == NULL || roiData[thisNode] > 0.0f) { const float& thisDist = distList[k]; if (excludeDists[thisNode] < 0.0f) { excludeDists[thisNode] = thisDist; excludeSources[thisNode] = mapCounter; roiLists[mapCounter][thisNode] = thisDist; } else { if (excludeSources[thisNode] != -1) { roiLists[excludeSources[thisNode]].erase(thisNode); excludeSources[thisNode] = -1; } } } } break; } ++mapCounter; } } } float AlgorithmMetricROIsFromExtrema::getAlgorithmInternalWeight() { return 1.0f;//override this if needed, if the progress bar isn't smooth } float AlgorithmMetricROIsFromExtrema::getSubAlgorithmWeight() { //return AlgorithmInsertNameHere::getAlgorithmWeight();//if you use a subalgorithm return 0.0f; } connectome-workbench-1.2.3+git41-gc4c6c90/src/Algorithms/AlgorithmMetricROIsFromExtrema.h000066400000000000000000000046111300200146000310310ustar00rootroot00000000000000#ifndef __ALGORITHM_METRIC_ROIS_FROM_EXTREMA_H__ #define __ALGORITHM_METRIC_ROIS_FROM_EXTREMA_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 "AbstractAlgorithm.h" #include "OverlapLogicEnum.h" #include #include namespace caret { class GeodesicHelper; class AlgorithmMetricROIsFromExtrema : public AbstractAlgorithm { AlgorithmMetricROIsFromExtrema(); void processMap(const float* data, std::vector& excludeDists, std::vector excludeSouces, std::vector >& roiLists, int64_t& mapCounter, CaretPointer& myHelp, const float& limit, const float* roiData, const OverlapLogicEnum::Enum& overlapType, const int& numNodes); protected: static float getSubAlgorithmWeight(); static float getAlgorithmInternalWeight(); public: AlgorithmMetricROIsFromExtrema(ProgressObject* myProgObj, const SurfaceFile* mySurf, const MetricFile* myMetric, const float& limit, MetricFile* myMetricOut, const float& sigma = -1.0f, const MetricFile* myRoi = NULL, const OverlapLogicEnum::Enum& overlapType = OverlapLogicEnum::ALLOW, const int& myColumn = -1); static OperationParameters* getParameters(); static void useParameters(OperationParameters* myParams, ProgressObject* myProgObj); static AString getCommandSwitch(); static AString getShortDescription(); }; typedef TemplateAutoOperation AutoAlgorithmMetricROIsFromExtrema; } #endif //__ALGORITHM_METRIC_ROIS_FROM_EXTREMA_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Algorithms/AlgorithmMetricROIsToBorder.cxx000066400000000000000000000141351300200146000306750ustar00rootroot00000000000000/*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 "AlgorithmMetricROIsToBorder.h" #include "AlgorithmException.h" #include "Border.h" #include "BorderFile.h" #include "BorderTracingHelper.h" #include "GiftiLabelTable.h" #include "MetricFile.h" #include "SurfaceFile.h" #include using namespace caret; using namespace std; AString AlgorithmMetricROIsToBorder::getCommandSwitch() { return "-metric-rois-to-border"; } AString AlgorithmMetricROIsToBorder::getShortDescription() { return "DRAW BORDERS AROUND METRIC ROIS"; } OperationParameters* AlgorithmMetricROIsToBorder::getParameters() { OperationParameters* ret = new OperationParameters(); ret->addSurfaceParameter(1, "surface", "the surface to use for neighbor information"); ret->addMetricParameter(2, "metric", "the input metric containing ROIs"); ret->addStringParameter(3, "class-name", "the name to use for the class of the output borders"); ret->addBorderOutputParameter(4, "border-out", "the output border file"); OptionalParameter* placeOpt = ret->createOptionalParameter(5, "-placement", "set how far along the edge border points are drawn"); placeOpt->addDoubleParameter(1, "fraction", "fraction along edge from inside vertex (default 0.33)"); OptionalParameter* columnSelect = ret->createOptionalParameter(6, "-column", "select a single column"); columnSelect->addStringParameter(1, "column", "the column number or name"); ret->setHelpText( AString("For each ROI column, finds all edges on the mesh that cross the boundary of the ROI, and draws borders through them. ") + "By default, this is done on all columns in the input file, using the map name as the name for the border." ); return ret; } void AlgorithmMetricROIsToBorder::useParameters(OperationParameters* myParams, ProgressObject* myProgObj) { SurfaceFile* mySurf = myParams->getSurface(1); MetricFile* myMetric = myParams->getMetric(2); AString className = myParams->getString(3); BorderFile* myBorderOut = myParams->getOutputBorder(4); float placement = 0.33f; OptionalParameter* placeOpt = myParams->getOptionalParameter(5); if (placeOpt->m_present) { placement = (float)placeOpt->getDouble(1); } OptionalParameter* columnSelect = myParams->getOptionalParameter(6); int columnNum = -1; if (columnSelect->m_present) {//set up to use the single column columnNum = (int)myMetric->getMapIndexFromNameOrNumber(columnSelect->getString(1)); if (columnNum < 0) { throw AlgorithmException("invalid column specified"); } } AlgorithmMetricROIsToBorder(myProgObj, mySurf, myMetric, className, myBorderOut, placement, columnNum); } AlgorithmMetricROIsToBorder::AlgorithmMetricROIsToBorder(ProgressObject* myProgObj, const SurfaceFile* mySurf, const MetricFile* myMetric, const AString& className, BorderFile* myBorderOut, const float& placement, const int& columnNum) : AbstractAlgorithm(myProgObj) { LevelProgress myProgress(myProgObj); if (mySurf->getNumberOfNodes() != myMetric->getNumberOfNodes()) throw AlgorithmException("label file does not match surface file number of vertices"); if (placement < 0.0f || placement > 1.0f || placement != placement) throw AlgorithmException("placement must be between 0 and 1"); if (columnNum < -1 || columnNum > myMetric->getNumberOfColumns()) throw AlgorithmException("invalid column specified"); myBorderOut->setStructure(mySurf->getStructure()); myBorderOut->setNumberOfNodes(mySurf->getNumberOfNodes()); BorderTracingHelper myHelper(mySurf); GiftiLabelTable myNameTable; if (columnNum == -1) { for (int col = 0; col < myMetric->getNumberOfColumns(); ++col) { vector > result = myHelper.traceData(myMetric->getValuePointerForColumn(col), BorderTracingHelper::GreaterThan(0.0f, false), placement); myNameTable.addLabel(myMetric->getMapName(col), rand() & 255, rand() & 255, rand() & 255); for (int i = 0; i < (int)result.size(); ++i) { result[i]->setClassName(className); result[i]->setName(myMetric->getMapName(col)); myBorderOut->addBorder(result[i].releasePointer());//NOTE: addBorder takes ownership of a RAW POINTER, shared_ptr won't release the pointer } } } else { myNameTable.addLabel(myMetric->getMapName(columnNum), rand() & 255, rand() & 255, rand() & 255); vector > result = myHelper.traceData(myMetric->getValuePointerForColumn(columnNum), BorderTracingHelper::GreaterThan(0.0f, false), placement); for (int i = 0; i < (int)result.size(); ++i) { result[i]->setClassName(className); result[i]->setName(myMetric->getMapName(columnNum)); myBorderOut->addBorder(result[i].releasePointer()); } } *(myBorderOut->getNameColorTable()) = myNameTable; } float AlgorithmMetricROIsToBorder::getAlgorithmInternalWeight() { return 1.0f;//override this if needed, if the progress bar isn't smooth } float AlgorithmMetricROIsToBorder::getSubAlgorithmWeight() { //return AlgorithmInsertNameHere::getAlgorithmWeight();//if you use a subalgorithm return 0.0f; } connectome-workbench-1.2.3+git41-gc4c6c90/src/Algorithms/AlgorithmMetricROIsToBorder.h000066400000000000000000000035231300200146000303210ustar00rootroot00000000000000#ifndef __ALGORITHM_METRIC_ROIS_TO_BORDER_H__ #define __ALGORITHM_METRIC_ROIS_TO_BORDER_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 "AbstractAlgorithm.h" namespace caret { class AlgorithmMetricROIsToBorder : public AbstractAlgorithm { AlgorithmMetricROIsToBorder(); protected: static float getSubAlgorithmWeight(); static float getAlgorithmInternalWeight(); public: AlgorithmMetricROIsToBorder(ProgressObject* myProgObj, const SurfaceFile* mySurf, const MetricFile* myMetric, const AString& className, BorderFile* myBorderOut, const float& placement = 0.33f, const int& columnNum = -1); static OperationParameters* getParameters(); static void useParameters(OperationParameters* myParams, ProgressObject* myProgObj); static AString getCommandSwitch(); static AString getShortDescription(); }; typedef TemplateAutoOperation AutoAlgorithmMetricROIsToBorder; } #endif //__ALGORITHM_METRIC_ROIS_TO_BORDER_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Algorithms/AlgorithmMetricReduce.cxx000066400000000000000000000133651300200146000276330ustar00rootroot00000000000000/*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 "AlgorithmMetricReduce.h" #include "AlgorithmException.h" #include "CaretLogger.h" #include "MetricFile.h" #include "ReductionOperation.h" #include using namespace caret; using namespace std; AString AlgorithmMetricReduce::getCommandSwitch() { return "-metric-reduce"; } AString AlgorithmMetricReduce::getShortDescription() { return "PERFORM REDUCTION OPERATION ACROSS METRIC COLUMNS"; } OperationParameters* AlgorithmMetricReduce::getParameters() { OperationParameters* ret = new OperationParameters(); ret->addMetricParameter(1, "metric-in", "the metric to reduce"); ret->addStringParameter(2, "operation", "the reduction operator to use"); ret->addMetricOutputParameter(3, "metric-out", "the output metric"); OptionalParameter* excludeOpt = ret->createOptionalParameter(4, "-exclude-outliers", "exclude non-numeric values and outliers by standard deviation"); excludeOpt->addDoubleParameter(1, "sigma-below", "number of standard deviations below the mean to include"); excludeOpt->addDoubleParameter(2, "sigma-above", "number of standard deviations above the mean to include"); ret->createOptionalParameter(5, "-only-numeric", "exclude non-numeric values"); ret->setHelpText( AString("For each surface vertex, takes the data across columns as a vector, and performs the specified reduction on it, putting the result ") + "into the single output column at that vertex. The reduction operators are as follows:\n\n" + ReductionOperation::getHelpInfo() ); return ret; } void AlgorithmMetricReduce::useParameters(OperationParameters* myParams, ProgressObject* myProgObj) { MetricFile* metricIn = myParams->getMetric(1); AString opString = myParams->getString(2); MetricFile* metricOut = myParams->getOutputMetric(3); OptionalParameter* excludeOpt = myParams->getOptionalParameter(4); bool onlyNumeric = myParams->getOptionalParameter(5)->m_present; bool ok = false; ReductionEnum::Enum myReduce = ReductionEnum::fromName(opString, &ok); if (!ok) throw AlgorithmException("unrecognized operation string '" + opString + "'"); if (excludeOpt->m_present) { if (onlyNumeric) CaretLogWarning("-only-numeric is redundant when -exclude-outliers is specified"); AlgorithmMetricReduce(myProgObj, metricIn, myReduce, metricOut, excludeOpt->getDouble(1), excludeOpt->getDouble(2)); } else { AlgorithmMetricReduce(myProgObj, metricIn, myReduce, metricOut, onlyNumeric); } } AlgorithmMetricReduce::AlgorithmMetricReduce(ProgressObject* myProgObj, const MetricFile* metricIn, const ReductionEnum::Enum& myReduce, MetricFile* metricOut, const bool& onlyNumeric) : AbstractAlgorithm(myProgObj) { LevelProgress myProgress(myProgObj); int numNodes = metricIn->getNumberOfNodes(); int numCols = metricIn->getNumberOfColumns(); if (numCols < 1 || numNodes < 1) throw AlgorithmException("input must have at least 1 column and 1 vertex"); metricOut->setNumberOfNodesAndColumns(numNodes, 1); metricOut->setStructure(metricIn->getStructure()); metricOut->setColumnName(0, ReductionEnum::toName(myReduce)); vector scratch(numCols); for (int node = 0; node < numNodes; ++node) { for (int col = 0; col < numCols; ++col) { scratch[col] = metricIn->getValue(node, col); } if (onlyNumeric) { metricOut->setValue(node, 0, ReductionOperation::reduceOnlyNumeric(scratch.data(), numCols, myReduce)); } else { metricOut->setValue(node, 0, ReductionOperation::reduce(scratch.data(), numCols, myReduce)); } } } AlgorithmMetricReduce::AlgorithmMetricReduce(ProgressObject* myProgObj, const MetricFile* metricIn, const ReductionEnum::Enum& myReduce, MetricFile* metricOut, const float& sigmaBelow, const float& sigmaAbove) : AbstractAlgorithm(myProgObj) { LevelProgress myProgress(myProgObj); int numNodes = metricIn->getNumberOfNodes(); int numCols = metricIn->getNumberOfColumns(); if (numCols < 1 || numNodes < 1) throw AlgorithmException("input must have at least 1 column and 1 vertex"); metricOut->setNumberOfNodesAndColumns(numNodes, 1); metricOut->setStructure(metricIn->getStructure()); metricOut->setColumnName(0, ReductionEnum::toName(myReduce)); vector scratch(numCols); for (int node = 0; node < numNodes; ++node) { for (int col = 0; col < numCols; ++col) { scratch[col] = metricIn->getValue(node, col); } metricOut->setValue(node, 0, ReductionOperation::reduceExcludeDev(scratch.data(), numCols, myReduce, sigmaBelow, sigmaAbove)); } } float AlgorithmMetricReduce::getAlgorithmInternalWeight() { return 1.0f;//override this if needed, if the progress bar isn't smooth } float AlgorithmMetricReduce::getSubAlgorithmWeight() { //return AlgorithmInsertNameHere::getAlgorithmWeight();//if you use a subalgorithm return 0.0f; } connectome-workbench-1.2.3+git41-gc4c6c90/src/Algorithms/AlgorithmMetricReduce.h000066400000000000000000000036541300200146000272600ustar00rootroot00000000000000#ifndef __ALGORITHM_METRIC_REDUCE_H__ #define __ALGORITHM_METRIC_REDUCE_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 "AbstractAlgorithm.h" #include "ReductionEnum.h" namespace caret { class AlgorithmMetricReduce : public AbstractAlgorithm { AlgorithmMetricReduce(); protected: static float getSubAlgorithmWeight(); static float getAlgorithmInternalWeight(); public: AlgorithmMetricReduce(ProgressObject* myProgObj, const MetricFile* metricIn, const ReductionEnum::Enum& myReduce, MetricFile* metricOut, const bool& onlyNumeric = false); AlgorithmMetricReduce(ProgressObject* myProgObj, const MetricFile* metricIn, const ReductionEnum::Enum& myReduce, MetricFile* metricOut, const float& sigmaBelow, const float& sigmaAbove); static OperationParameters* getParameters(); static void useParameters(OperationParameters* myParams, ProgressObject* myProgObj); static AString getCommandSwitch(); static AString getShortDescription(); }; typedef TemplateAutoOperation AutoAlgorithmMetricReduce; } #endif //__ALGORITHM_METRIC_REDUCE_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Algorithms/AlgorithmMetricRegression.cxx000066400000000000000000000340571300200146000305450ustar00rootroot00000000000000/*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 "AlgorithmMetricRegression.h" #include "AlgorithmException.h" #include "FloatMatrix.h" #include "MetricFile.h" #include "PaletteColorMapping.h" using namespace caret; using namespace std; AString AlgorithmMetricRegression::getCommandSwitch() { return "-metric-regression"; } AString AlgorithmMetricRegression::getShortDescription() { return "REGRESS METRICS OUT OF A METRIC FILE"; } OperationParameters* AlgorithmMetricRegression::getParameters() { OperationParameters* ret = new OperationParameters(); ret->addMetricParameter(1, "metric-in", "the metric to regress from"); ret->addMetricOutputParameter(2, "metric-out", "the output metric"); OptionalParameter* roiOpt = ret->createOptionalParameter(3, "-roi", "only regress inside an roi"); roiOpt->addMetricParameter(1, "roi-metric", "the area to use for regression, as a metric"); OptionalParameter* columnOpt = ret->createOptionalParameter(4, "-column", "select a single column to regress from"); columnOpt->addStringParameter(1, "column", "the column number or name"); ParameterComponent* removeOpt = ret->createRepeatableParameter(5, "-remove", "specify a metric to regress out"); removeOpt->addMetricParameter(1, "metric", "the metric file to use"); OptionalParameter* removeColOpt = removeOpt->createOptionalParameter(2, "-remove-column", "select a column to use, rather than all"); removeColOpt->addStringParameter(1, "column", "the column number or name"); ParameterComponent* keepOpt = ret->createRepeatableParameter(6, "-keep", "specify a metric to include in regression, but not remove"); keepOpt->addMetricParameter(1, "metric", "the metric file to use"); OptionalParameter* keepColOpt = keepOpt->createOptionalParameter(2, "-keep-column", "select a column to use, rather than all"); keepColOpt->addStringParameter(1, "column", "the column number or name"); ret->setHelpText( AString("For each regressor, its mean across the surface is subtracted from its data. ") + "Each input map is then regressed against these, and a constant term. The resulting regressed slopes of all regressors specified with -remove " + "are multiplied with their respective regressor maps, and these are subtracted from the input map." ); return ret; } void AlgorithmMetricRegression::useParameters(OperationParameters* myParams, ProgressObject* myProgObj) { MetricFile* myMetricIn = myParams->getMetric(1); MetricFile* myMetricOut = myParams->getOutputMetric(2); MetricFile* myRoi = NULL; OptionalParameter* roiOpt = myParams->getOptionalParameter(3); if (roiOpt->m_present) { myRoi = roiOpt->getMetric(1); } int32_t myColumn = -1; OptionalParameter* columnOpt = myParams->getOptionalParameter(4); if (columnOpt->m_present) { myColumn = (int)myMetricIn->getMapIndexFromNameOrNumber(columnOpt->getString(1)); if (myColumn < 0) { throw AlgorithmException("invalid column specified"); } } vector > remove, keep; const vector& removeInstances = *(myParams->getRepeatableParameterInstances(5)); int numRemove = (int)removeInstances.size(); if (numRemove == 0) throw AlgorithmException("you must specify at least one 'remove' metric"); for (int i = 0; i < numRemove; ++i) { MetricFile* toRemove = removeInstances[i]->getMetric(1); int removeCol = -1; OptionalParameter* removeColOpt = removeInstances[i]->getOptionalParameter(2); if (removeColOpt->m_present) { removeCol = toRemove->getMapIndexFromNameOrNumber(removeColOpt->getString(1)); if (removeCol < 0) { throw AlgorithmException("invalid column specified for 'remove' metric '" + toRemove->getFileName() + "'"); } } remove.push_back(pair(toRemove, removeCol)); } const vector& keepInstances = *(myParams->getRepeatableParameterInstances(6)); int numKeep = (int)keepInstances.size(); for (int i = 0; i < numKeep; ++i) { MetricFile* toKeep = keepInstances[i]->getMetric(1); int keepCol = -1; OptionalParameter* keepColOpt = keepInstances[i]->getOptionalParameter(2); if (keepColOpt->m_present) { keepCol = toKeep->getMapIndexFromNameOrNumber(keepColOpt->getString(1)); if (keepCol < 0) { throw AlgorithmException("invalid column specified for 'keep' metric '" + toKeep->getFileName() + "'"); } } keep.push_back(pair(toKeep, keepCol)); } AlgorithmMetricRegression(myProgObj, myMetricIn, myMetricOut, remove, keep, myColumn, myRoi); } AlgorithmMetricRegression::AlgorithmMetricRegression(ProgressObject* myProgObj, const MetricFile* myMetricIn, MetricFile* myMetricOut, const vector >& remove, const vector >& keep, const int& myColumn, const MetricFile* myRoi) : AbstractAlgorithm(myProgObj) { LevelProgress myProgress(myProgObj); vector > regressCols;//because we are going to de-mean the input data int removeCount = 0; int numNodes = myMetricIn->getNumberOfNodes(); int numColumns = myMetricIn->getNumberOfColumns(); if (myColumn < -1 || myColumn >= numColumns) { throw AlgorithmException("invalid column number"); } int numRemove = (int)remove.size(); if (numRemove == 0) throw AlgorithmException("empty remove list in AlgorithmMetricRegression"); const float* roiData = NULL; int numUsedNodes = numNodes; if (myRoi != NULL) { if (myRoi->getNumberOfNodes() != numNodes) throw AlgorithmException("roi metric has different number of nodes"); roiData = myRoi->getValuePointerForColumn(0); numUsedNodes = 0; for (int i = 0; i < numNodes; ++i) { if (roiData[i] > 0.0f) ++numUsedNodes; } } for (int i = 0; i < numRemove; ++i) { const MetricFile* thisMetric = remove[i].first; if (thisMetric->getNumberOfNodes() != numNodes) { throw AlgorithmException("'remove' metric '" + thisMetric->getFileName() + "' has a different number of nodes than the input metric"); } int thisCol = remove[i].second; if (thisCol == -1) { int endCol = thisMetric->getNumberOfColumns(); removeCount += endCol; for (int j = 0; j < endCol; ++j) { regressCols.push_back(vector()); demeanCol(thisMetric->getValuePointerForColumn(j), numNodes, roiData, regressCols.back()); } } else { if (thisCol < 0 || thisCol >= thisMetric->getNumberOfColumns()) throw AlgorithmException("invalid column specified for metric '" + thisMetric->getFileName() + "'"); ++removeCount; regressCols.push_back(vector()); demeanCol(thisMetric->getValuePointerForColumn(thisCol), numNodes, roiData, regressCols.back()); } } int numKeep = (int)keep.size();//repeat, without increasing removeCount - this separates what gets removed after regression for (int i = 0; i < numKeep; ++i) { const MetricFile* thisMetric = keep[i].first; if (thisMetric->getNumberOfNodes() != numNodes) { throw AlgorithmException("'keep' metric '" + thisMetric->getFileName() + "' has a different number of nodes than the input metric"); } int thisCol = keep[i].second; if (thisCol == -1) { int endCol = thisMetric->getNumberOfColumns(); for (int j = 0; j < endCol; ++j) { regressCols.push_back(vector()); demeanCol(thisMetric->getValuePointerForColumn(j), numNodes, roiData, regressCols.back()); } } else { if (thisCol < 0 || thisCol >= thisMetric->getNumberOfColumns()) throw AlgorithmException("invalid column specified for metric '" + thisMetric->getFileName() + "'"); regressCols.push_back(vector()); demeanCol(thisMetric->getValuePointerForColumn(thisCol), numNodes, roiData, regressCols.back()); } } FloatMatrix xtrans(regressCols);//use naive matrix math - not particularly efficient, but it works regressCols.clear();//don't need this any more, should call destructor on each member vector and release the memory xtrans = xtrans.concatVert(FloatMatrix::ones(1, numUsedNodes));//add constant term FloatMatrix toInvert = xtrans * xtrans.transpose(); int invertSize = toInvert.getNumberOfRows(); //a bit of a hack, but it should work, though this causes the work of inversion to be done twice if (toInvert.reducedRowEchelon()[invertSize - 1][invertSize - 1] != 1.0f) throw AlgorithmException("regression encountered a non-invertible matrix, check your inputs for linear independence"); FloatMatrix solver = toInvert.inverse() * xtrans;//do most of the math in temporaries if (myColumn == -1) { myMetricOut->setNumberOfNodesAndColumns(numNodes, numColumns); myMetricOut->setStructure(myMetricIn->getStructure()); for (int i = 0; i < numColumns; ++i) { myMetricOut->setColumnName(i, myMetricIn->getColumnName(i) + " regressed"); *(myMetricOut->getPaletteColorMapping(i)) = *(myMetricIn->getPaletteColorMapping(i)); FloatMatrix y(numUsedNodes, 1);//create column vector for input - note that column vectors are extremely inefficient currently, as is copying the data... const float* data = myMetricIn->getValuePointerForColumn(i); int m = 0; for (int j = 0; j < numNodes; ++j) { if (roiData == NULL || roiData[j] > 0.0f) { y[m][0] = data[j]; ++m; } } FloatMatrix regressed = solver * y; vector outscratch(numNodes); m = 0; for (int j = 0; j < numNodes; ++j) { if (roiData == NULL || roiData[j] > 0.0f) { outscratch[j] = data[j]; for (int k = 0; k < removeCount; ++k) { outscratch[j] -= regressed[k][0] * xtrans[k][m]; } ++m; } else { outscratch[j] = 0.0f; } } myMetricOut->setValuesForColumn(i, outscratch.data()); } } else { myMetricOut->setNumberOfNodesAndColumns(numNodes, 1); myMetricOut->setStructure(myMetricIn->getStructure()); myMetricOut->setColumnName(0, myMetricIn->getColumnName(myColumn) + " regressed"); *(myMetricOut->getPaletteColorMapping(0)) = *(myMetricIn->getPaletteColorMapping(myColumn)); FloatMatrix y(numUsedNodes, 1);//create column vector for input - note that column vectors are extremely inefficient currently, as is copying the data... const float* data = myMetricIn->getValuePointerForColumn(myColumn); int m = 0; for (int j = 0; j < numNodes; ++j) { if (roiData == NULL || roiData[j] > 0.0f) { y[m][0] = data[j]; ++m; } } FloatMatrix regressed = solver * y; vector outscratch(numNodes); m = 0; for (int j = 0; j < numNodes; ++j) { if (roiData == NULL || roiData[j] > 0.0f) { outscratch[j] = data[j]; for (int k = 0; k < removeCount; ++k) { outscratch[j] -= regressed[k][0] * xtrans[k][m]; } ++m; } else { outscratch[j] = 0.0f; } } myMetricOut->setValuesForColumn(0, outscratch.data()); } } void AlgorithmMetricRegression::demeanCol(const float* data, const int& count, const float* roiData, std::vector< float >& out) { if (roiData == NULL) { double accum = 0.0; for (int i = 0; i < count; ++i) { accum += data[i]; } accum /= count; out.resize(count); for (int i = 0; i < count; ++i) { out[i] = data[i] - accum; } } else { int usedCount = 0; double accum = 0.0; for (int i = 0; i < count; ++i) { if (roiData[i] > 0.0f) { accum += data[i]; ++usedCount; } } accum /= usedCount; out.resize(usedCount); int m = 0; for (int i = 0; i < count; ++i) { if (roiData[i] > 0.0f) { out[m] = data[i] - accum; ++m; } } } } float AlgorithmMetricRegression::getAlgorithmInternalWeight() { return 1.0f;//override this if needed, if the progress bar isn't smooth } float AlgorithmMetricRegression::getSubAlgorithmWeight() { //return AlgorithmInsertNameHere::getAlgorithmWeight();//if you use a subalgorithm return 0.0f; } connectome-workbench-1.2.3+git41-gc4c6c90/src/Algorithms/AlgorithmMetricRegression.h000066400000000000000000000040251300200146000301620ustar00rootroot00000000000000#ifndef __ALGORITHM_METRIC_REGRESSION_H__ #define __ALGORITHM_METRIC_REGRESSION_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 "AbstractAlgorithm.h" #include #include namespace caret { class AlgorithmMetricRegression : public AbstractAlgorithm { AlgorithmMetricRegression(); void demeanCol(const float* data, const int& count, const float* roiData, std::vector& out); protected: static float getSubAlgorithmWeight(); static float getAlgorithmInternalWeight(); public: AlgorithmMetricRegression(ProgressObject* myProgObj, const MetricFile* myMetricIn, MetricFile* myMetricOut, const std::vector >& remove, const std::vector >& keep, const int& myColumn = -1, const MetricFile* myRoi = NULL); static OperationParameters* getParameters(); static void useParameters(OperationParameters* myParams, ProgressObject* myProgObj); static AString getCommandSwitch(); static AString getShortDescription(); }; typedef TemplateAutoOperation AutoAlgorithmMetricRegression; } #endif //__ALGORITHM_METRIC_REGRESSION_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Algorithms/AlgorithmMetricRemoveIslands.cxx000066400000000000000000000147431300200146000312000ustar00rootroot00000000000000/*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 "AlgorithmMetricRemoveIslands.h" #include "AlgorithmException.h" #include "MetricFile.h" #include "SurfaceFile.h" #include "TopologyHelper.h" #include #include using namespace caret; using namespace std; AString AlgorithmMetricRemoveIslands::getCommandSwitch() { return "-metric-remove-islands"; } AString AlgorithmMetricRemoveIslands::getShortDescription() { return "REMOVE ISLANDS FROM AN ROI METRIC"; } OperationParameters* AlgorithmMetricRemoveIslands::getParameters() { OperationParameters* ret = new OperationParameters(); ret->addSurfaceParameter(1, "surface", "the surface to use for neighbor information"); ret->addMetricParameter(2, "metric-in", "the input ROI metric"); ret->addMetricOutputParameter(3, "metric-out", "the output ROI metric"); OptionalParameter* corrAreaOpt = ret->createOptionalParameter(4, "-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("Finds all connected areas in the ROI, and zeros out all but the largest one, in terms of surface area.") ); return ret; } void AlgorithmMetricRemoveIslands::useParameters(OperationParameters* myParams, ProgressObject* myProgObj) { SurfaceFile* mySurf = myParams->getSurface(1); MetricFile* myMetric = myParams->getMetric(2); MetricFile* myMetricOut = myParams->getOutputMetric(3); MetricFile* corrAreaMetric = NULL; OptionalParameter* corrAreaOpt = myParams->getOptionalParameter(4); if (corrAreaOpt->m_present) { corrAreaMetric = corrAreaOpt->getMetric(1); } AlgorithmMetricRemoveIslands(myProgObj, mySurf, myMetric, myMetricOut, corrAreaMetric); } AlgorithmMetricRemoveIslands::AlgorithmMetricRemoveIslands(ProgressObject* myProgObj, const SurfaceFile* mySurf, const MetricFile* myMetric, MetricFile* myMetricOut, const MetricFile* corrAreaMetric) : AbstractAlgorithm(myProgObj) { LevelProgress myProgress(myProgObj); int numNodes = mySurf->getNumberOfNodes(); if (myMetric->getNumberOfNodes() != numNodes) throw AlgorithmException("metric file has different number of nodes than the surface"); vector surfAreaData; const float* areaData = NULL; if (corrAreaMetric != NULL) { if (corrAreaMetric->getNumberOfNodes() != numNodes) throw AlgorithmException("corrected vertex area metric file has different number of nodes than the surface"); areaData = corrAreaMetric->getValuePointerForColumn(0); } else { mySurf->computeNodeAreas(surfAreaData); areaData = surfAreaData.data(); } int numCols = myMetric->getNumberOfColumns(); myMetricOut->setNumberOfNodesAndColumns(numNodes, numCols); myMetricOut->setStructure(myMetric->getStructure()); for (int col = 0; col < numCols; ++col) { vector > > areas; vector used(numNodes, 0); CaretPointer myHelp = mySurf->getTopologyHelper(); const float* roiData = myMetric->getValuePointerForColumn(col); myMetricOut->setColumnName(col, myMetric->getColumnName(col)); for (int i = 0; i < numNodes; ++i) { if (used[i] == 0 && roiData[i] > 0.0f) { areas.push_back(make_pair(0.0f, vector())); vector& thisAreaMembers = areas.back().second; float& thisSurfArea = areas.back().first; thisAreaMembers.push_back(i); thisSurfArea += areaData[i]; used[i] = 1; vector mystack; mystack.push_back(i); while (!mystack.empty()) { int curnode = mystack.back(); mystack.pop_back(); const vector& neighbors = myHelp->getNodeNeighbors(curnode); int numNeigh = (int)neighbors.size(); for (int j = 0; j < numNeigh; ++j) { int thisneigh = neighbors[j]; if (used[thisneigh] == 0 && roiData[thisneigh] > 0.0f) { used[thisneigh] = 1; thisAreaMembers.push_back(thisneigh); thisSurfArea += areaData[thisneigh]; mystack.push_back(thisneigh); } } } } } vector outscratch(numNodes, 0.0f); int numAreas = (int)areas.size(); if (numAreas > 0) { int bestIndex = 0; float bestArea = areas[0].first; for (int i = 1; i < numAreas; ++i) { float thisArea = (int)areas[i].first; if (thisArea > bestArea) { bestIndex = i; bestArea = thisArea; } } const vector& thisArea = areas[bestIndex].second; int numAreaNodes = (int)thisArea.size(); for (int i = 0; i < numAreaNodes; ++i) { outscratch[thisArea[i]] = 1.0f;//make it into a simple 0/1 metric, even if it wasn't before } } myMetricOut->setValuesForColumn(col, outscratch.data()); } } float AlgorithmMetricRemoveIslands::getAlgorithmInternalWeight() { return 1.0f;//override this if needed, if the progress bar isn't smooth } float AlgorithmMetricRemoveIslands::getSubAlgorithmWeight() { return 0.0f; } connectome-workbench-1.2.3+git41-gc4c6c90/src/Algorithms/AlgorithmMetricRemoveIslands.h000066400000000000000000000034551300200146000306230ustar00rootroot00000000000000#ifndef __ALGORITHM_METRIC_REMOVE_ISLANDS_H__ #define __ALGORITHM_METRIC_REMOVE_ISLANDS_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 "AbstractAlgorithm.h" namespace caret { class AlgorithmMetricRemoveIslands : public AbstractAlgorithm { AlgorithmMetricRemoveIslands(); protected: static float getSubAlgorithmWeight(); static float getAlgorithmInternalWeight(); public: AlgorithmMetricRemoveIslands(ProgressObject* myProgObj, const SurfaceFile* mySurf, const MetricFile* myMetric, MetricFile* myMetricOut, const MetricFile* corrAreaMetric = NULL); static OperationParameters* getParameters(); static void useParameters(OperationParameters* myParams, ProgressObject* myProgObj); static AString getCommandSwitch(); static AString getShortDescription(); }; typedef TemplateAutoOperation AutoAlgorithmMetricRemoveIslands; } #endif //__ALGORITHM_METRIC_REMOVE_ISLANDS_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Algorithms/AlgorithmMetricResample.cxx000066400000000000000000000252601300200146000301710ustar00rootroot00000000000000/*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 "AlgorithmMetricResample.h" #include "AlgorithmException.h" #include "CaretLogger.h" #include "MetricFile.h" #include "PaletteColorMapping.h" #include "SurfaceFile.h" #include "SurfaceResamplingHelper.h" using namespace caret; using namespace std; AString AlgorithmMetricResample::getCommandSwitch() { return "-metric-resample"; } AString AlgorithmMetricResample::getShortDescription() { return "RESAMPLE A METRIC FILE TO A DIFFERENT MESH"; } OperationParameters* AlgorithmMetricResample::getParameters() { OperationParameters* ret = new OperationParameters(); ret->addMetricParameter(1, "metric-in", "the metric file to resample"); ret->addSurfaceParameter(2, "current-sphere", "a sphere surface with the mesh that the metric is currently on"); ret->addSurfaceParameter(3, "new-sphere", "a sphere surface that is in register with and has the desired output mesh"); ret->addStringParameter(4, "method", "the method name"); ret->addMetricOutputParameter(5, "metric-out", "the output metric"); OptionalParameter* areaSurfsOpt = ret->createOptionalParameter(6, "-area-surfs", "specify surfaces to do vertex area correction based on"); areaSurfsOpt->addSurfaceParameter(1, "current-area", "a relevant anatomical surface with mesh"); areaSurfsOpt->addSurfaceParameter(2, "new-area", "a relevant anatomical surface with mesh"); OptionalParameter* areaMetricsOpt = ret->createOptionalParameter(7, "-area-metrics", "specify vertex area metrics to do area correction based on"); areaMetricsOpt->addMetricParameter(1, "current-area", "a metric file with vertex areas for mesh"); areaMetricsOpt->addMetricParameter(2, "new-area", "a metric file with vertex areas for mesh"); OptionalParameter* roiOpt = ret->createOptionalParameter(8, "-current-roi", "use an input roi on the current mesh to exclude non-data vertices"); roiOpt->addMetricParameter(1, "roi-metric", "the roi, as a metric file"); OptionalParameter* validRoiOutOpt = ret->createOptionalParameter(9, "-valid-roi-out", "output the ROI of vertices that got data from valid source vertices"); validRoiOutOpt->addMetricOutputParameter(1, "roi-out", "the output roi as a metric"); ret->createOptionalParameter(10, "-largest", "use only the value of the vertex with the largest weight"); AString myHelpText = AString("Resamples a metric file, given two spherical surfaces that are in register. ") + "If ADAP_BARY_AREA is used, exactly one of -area-surfs or -area-metrics must be specified.\n\n" + "The ADAP_BARY_AREA method is recommended for ordinary metric data, because it should use all data while downsampling, unlike BARYCENTRIC. " + "The recommended areas option for most data is individual midthicknesses for individual data, and averaged vertex area metrics from individual midthicknesses for group average data.\n\n" "The -current-roi option only masks the input, the output may be slightly dilated in comparison, consider using -metric-mask on the output " + "when using -current-roi.\n\n" + "The -largest option results in nearest vertex behavior when used with BARYCENTRIC. " + "When resampling a binary metric, consider thresholding at 0.5 after resampling rather than using -largest.\n\n" + "The argument must be one of the following:\n\n"; vector allEnums; SurfaceResamplingMethodEnum::getAllEnums(allEnums); for (int i = 0; i < (int)allEnums.size(); ++i) { myHelpText += SurfaceResamplingMethodEnum::toName(allEnums[i]) + "\n"; } ret->setHelpText(myHelpText); return ret; } void AlgorithmMetricResample::useParameters(OperationParameters* myParams, ProgressObject* myProgObj) { MetricFile* metricIn = myParams->getMetric(1); SurfaceFile* curSphere = myParams->getSurface(2); SurfaceFile* newSphere = myParams->getSurface(3); bool ok = false; SurfaceResamplingMethodEnum::Enum myMethod = SurfaceResamplingMethodEnum::fromName(myParams->getString(4), &ok); if (!ok) { throw AlgorithmException("invalid method name"); } MetricFile* metricOut = myParams->getOutputMetric(5); MetricFile* curAreas = NULL, *newAreas = NULL; MetricFile curAreasTemp, newAreasTemp; OptionalParameter* areaSurfsOpt = myParams->getOptionalParameter(6); if (areaSurfsOpt->m_present) { switch(myMethod) { case SurfaceResamplingMethodEnum::BARYCENTRIC: CaretLogInfo("This method does not use area correction, -area-surfs is not needed"); break; default: break; } vector nodeAreasTemp; SurfaceFile* curAreaSurf = areaSurfsOpt->getSurface(1); SurfaceFile* newAreaSurf = areaSurfsOpt->getSurface(2); curAreaSurf->computeNodeAreas(nodeAreasTemp); curAreasTemp.setNumberOfNodesAndColumns(curAreaSurf->getNumberOfNodes(), 1); curAreasTemp.setValuesForColumn(0, nodeAreasTemp.data()); curAreas = &curAreasTemp; newAreaSurf->computeNodeAreas(nodeAreasTemp); newAreasTemp.setNumberOfNodesAndColumns(newAreaSurf->getNumberOfNodes(), 1); newAreasTemp.setValuesForColumn(0, nodeAreasTemp.data()); newAreas = &newAreasTemp; } OptionalParameter* areaMetricsOpt = myParams->getOptionalParameter(7); if (areaMetricsOpt->m_present) { if (areaSurfsOpt->m_present) { throw AlgorithmException("only one of -area-surfs and -area-metrics can be specified"); } switch(myMethod) { case SurfaceResamplingMethodEnum::BARYCENTRIC: CaretLogInfo("This method does not use area correction, -area-metrics is not needed"); break; default: break; } curAreas = areaMetricsOpt->getMetric(1); newAreas = areaMetricsOpt->getMetric(2); } OptionalParameter* roiOpt = myParams->getOptionalParameter(8); MetricFile* currentRoi = NULL; if (roiOpt->m_present) { currentRoi = roiOpt->getMetric(1); } MetricFile* validRoiOut = NULL; OptionalParameter* validRoiOutOpt = myParams->getOptionalParameter(9); if (validRoiOutOpt->m_present) { validRoiOut = validRoiOutOpt->getOutputMetric(1); } bool largest = myParams->getOptionalParameter(10)->m_present; AlgorithmMetricResample(myProgObj, metricIn, curSphere, newSphere, myMethod, metricOut, curAreas, newAreas, currentRoi, validRoiOut, largest); } AlgorithmMetricResample::AlgorithmMetricResample(ProgressObject* myProgObj, const MetricFile* metricIn, const SurfaceFile* curSphere, const SurfaceFile* newSphere, const SurfaceResamplingMethodEnum::Enum& myMethod, MetricFile* metricOut, const MetricFile* curAreas, const MetricFile* newAreas, const MetricFile* currentRoi, MetricFile* validRoiOut, const bool& largest) : AbstractAlgorithm(myProgObj) { LevelProgress myProgress(myProgObj); if (metricIn->getNumberOfNodes() != curSphere->getNumberOfNodes()) throw AlgorithmException("input metric has different number of nodes than input sphere"); if (currentRoi != NULL && currentRoi->getNumberOfNodes() != curSphere->getNumberOfNodes()) throw AlgorithmException("roi metric has different number of nodes than input sphere"); const float* curAreaData = NULL, *newAreaData = NULL; switch (myMethod) { case SurfaceResamplingMethodEnum::BARYCENTRIC: break; default: if (curAreas == NULL || newAreas == NULL) throw AlgorithmException("specified method does area correction, but no vertex area data given"); if (curSphere->getNumberOfNodes() != curAreas->getNumberOfNodes()) throw AlgorithmException("current vertex area data has different number of nodes than current sphere"); if (newSphere->getNumberOfNodes() != newAreas->getNumberOfNodes()) throw AlgorithmException("new vertex area data has different number of nodes than new sphere"); curAreaData = curAreas->getValuePointerForColumn(0); newAreaData = newAreas->getValuePointerForColumn(0); } int numColumns = metricIn->getNumberOfColumns(), numNewNodes = newSphere->getNumberOfNodes(); metricOut->setNumberOfNodesAndColumns(numNewNodes, numColumns); metricOut->setStructure(metricIn->getStructure()); vector colScratch(numNewNodes, 0.0f); const float* roiCol = NULL; if (currentRoi != NULL) roiCol = currentRoi->getValuePointerForColumn(0); SurfaceResamplingHelper myHelp(myMethod, curSphere, newSphere, curAreaData, newAreaData, roiCol); if (validRoiOut != NULL) { validRoiOut->setNumberOfNodesAndColumns(numNewNodes, 1); validRoiOut->setStructure(metricIn->getStructure()); vector scratch(numNewNodes); myHelp.getResampleValidROI(scratch.data()); validRoiOut->setValuesForColumn(0, scratch.data()); } for (int i = 0; i < numColumns; ++i) { metricOut->setColumnName(i, metricIn->getColumnName(i)); *metricOut->getPaletteColorMapping(i) = *metricIn->getPaletteColorMapping(i); if (largest) { myHelp.resampleLargest(metricIn->getValuePointerForColumn(i), colScratch.data()); } else { myHelp.resampleNormal(metricIn->getValuePointerForColumn(i), colScratch.data()); } metricOut->setValuesForColumn(i, colScratch.data()); } } float AlgorithmMetricResample::getAlgorithmInternalWeight() { return 1.0f;//override this if needed, if the progress bar isn't smooth } float AlgorithmMetricResample::getSubAlgorithmWeight() { //return AlgorithmInsertNameHere::getAlgorithmWeight();//if you use a subalgorithm return 0.0f; } connectome-workbench-1.2.3+git41-gc4c6c90/src/Algorithms/AlgorithmMetricResample.h000066400000000000000000000040271300200146000276140ustar00rootroot00000000000000#ifndef __ALGORITHM_METRIC_RESAMPLE_H__ #define __ALGORITHM_METRIC_RESAMPLE_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 "AbstractAlgorithm.h" #include "SurfaceResamplingMethodEnum.h" namespace caret { class AlgorithmMetricResample : public AbstractAlgorithm { AlgorithmMetricResample(); protected: static float getSubAlgorithmWeight(); static float getAlgorithmInternalWeight(); public: AlgorithmMetricResample(ProgressObject* myProgObj, const MetricFile* metricIn, const SurfaceFile* curSphere, const SurfaceFile* newSphere, const SurfaceResamplingMethodEnum::Enum& myMethod, MetricFile* metricOut, const MetricFile* curAreas = NULL, const MetricFile* newAreas = NULL, const MetricFile* currentRoi = NULL, MetricFile* validRoiOut = NULL, const bool& largest = false); static OperationParameters* getParameters(); static void useParameters(OperationParameters* myParams, ProgressObject* myProgObj); static AString getCommandSwitch(); static AString getShortDescription(); }; typedef TemplateAutoOperation AutoAlgorithmMetricResample; } #endif //__ALGORITHM_METRIC_RESAMPLE_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Algorithms/AlgorithmMetricSmoothing.cxx000066400000000000000000000273701300200146000303740ustar00rootroot00000000000000/*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 "AlgorithmMetricSmoothing.h" #include "AlgorithmException.h" #include "CaretOMP.h" #include "CaretPointer.h" #include "GeodesicHelper.h" #include "MetricFile.h" #include "PaletteColorMapping.h" #include "SurfaceFile.h" #include "TopologyHelper.h" #include using namespace caret; using namespace std; AString AlgorithmMetricSmoothing::getCommandSwitch() { return "-metric-smoothing"; } AString AlgorithmMetricSmoothing::getShortDescription() { return "SMOOTH A METRIC FILE"; } OperationParameters* AlgorithmMetricSmoothing::getParameters() { OperationParameters* ret = new OperationParameters(); ret->addSurfaceParameter(1, "surface", "the surface to smooth on"); ret->addMetricParameter(2, "metric-in", "the metric to smooth"); ret->addDoubleParameter(3, "smoothing-kernel", "the sigma for the gaussian kernel function, in mm"); ret->addMetricOutputParameter(4, "metric-out", "the output metric"); OptionalParameter* roiOption = ret->createOptionalParameter(5, "-roi", "select a region of interest to smooth"); roiOption->addMetricParameter(1, "roi-metric", "the roi to smooth within, as a metric"); roiOption->createOptionalParameter(2, "-match-columns", "for each input column, use the corresponding column from the roi"); ret->createOptionalParameter(6, "-fix-zeros", "treat zero values as not being data"); OptionalParameter* columnSelect = ret->createOptionalParameter(7, "-column", "select a single column to smooth"); columnSelect->addStringParameter(1, "column", "the column number or name"); 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"); OptionalParameter* methodSelect = ret->createOptionalParameter(9, "-method", "select smoothing method, default GEO_GAUSS_AREA"); methodSelect->addStringParameter(1, "method", "the name of the smoothing method"); ret->setHelpText( AString("Smooth a metric file on a surface. ") + "By default, smooths all input columns on the entire surface, specify -column to use only one input column, and -roi to smooth only where " + "the roi metric is greater than 0, outputting zeros elsewhere.\n\n" + "When using -roi, input data outside the ROI is not used to compute the smoothed values. " + "By default, the first column of the roi metric is used for all input columns. " + "When -match-columns is specified to the -roi option, the input and roi metrics must have the same number of columns, " + "and for each input column's index, the same column index is used in the roi metric. " + "If the -match-columns option to -roi is used while the -column option is also used, the number of columns must match between the roi and input metric, " + "and it will use the roi column with the index of the selected input column.\n\n" + "The -fix-zeros option causes the smoothing to not use an input value if it is zero, but still write a smoothed value to the vertex. " + "This is useful for zeros that indicate lack of information, preventing them from pulling down the intensity of nearby vertices, while " + "giving the zero an extrapolated value.\n\n" + "The -corrected-areas option is intended for when it is unavoidable to smooth on a group average surface, it is only an approximate correction " + "for the reduction of structure in a group average surface. It is better to smooth the data on individuals before averaging, when feasible.\n\n" + "Valid values for are:\n\n" + "GEO_GAUSS_AREA - uses a geodesic gaussian kernel, and normalizes based on vertex area in order to work more reliably on irregular surfaces\n\n" + "GEO_GAUSS_EQUAL - uses a geodesic gaussian kernel, and normalizes assuming each vertex has equal importance\n\n" + "GEO_GAUSS - matches geodesic gaussian smoothing from caret5, but does not check kernels for having unequal importance\n\n" + "The GEO_GAUSS_AREA method is the default because it is usually the correct choice. " + "GEO_GAUSS_EQUAL may be the correct choice when the sum of vertex values is more meaningful then the surface integral (sum of values .* areas), " + "for instance when smoothing vertex areas (the sum is the total surface area, while the surface integral is the sum of squares of the vertex areas). " + "The GEO_GAUSS method is not recommended, it exists mainly to replicate methods of studies done with caret5's geodesic smoothing." ); return ret; } void AlgorithmMetricSmoothing::useParameters(OperationParameters* myParams, ProgressObject* myProgObj) { SurfaceFile* mySurf = myParams->getSurface(1); MetricFile* myMetric = myParams->getMetric(2); double myKernel = myParams->getDouble(3); MetricFile* myMetricOut = myParams->getOutputMetric(4); MetricFile* myRoi = NULL; bool matchRoiColumns = false; OptionalParameter* roiOption = myParams->getOptionalParameter(5); if (roiOption->m_present) { myRoi = roiOption->getMetric(1); matchRoiColumns = roiOption->getOptionalParameter(2)->m_present; } OptionalParameter* fixZerosOpt = myParams->getOptionalParameter(6); bool fixZeros = fixZerosOpt->m_present; int64_t columnNum = -1; OptionalParameter* columnSelect = myParams->getOptionalParameter(7); if (columnSelect->m_present) { columnNum = (int)myMetric->getMapIndexFromNameOrNumber(columnSelect->getString(1)); if (columnNum < 0) { throw AlgorithmException("invalid column specified"); } } MetricFile* corrAreaMetric = NULL; OptionalParameter* corrAreaOpt = myParams->getOptionalParameter(8); if (corrAreaOpt->m_present) { corrAreaMetric = corrAreaOpt->getMetric(1); } MetricSmoothingObject::Method myMethod = MetricSmoothingObject::GEO_GAUSS_AREA; OptionalParameter* methodSelect = myParams->getOptionalParameter(9); if (methodSelect->m_present) { AString methodName = methodSelect->getString(1); if (methodName == "GEO_GAUSS_AREA") { myMethod = MetricSmoothingObject::GEO_GAUSS_AREA; } else if (methodName == "GEO_GAUSS_EQUAL") { myMethod = MetricSmoothingObject::GEO_GAUSS_EQUAL; } else if (methodName == "GEO_GAUSS") { myMethod = MetricSmoothingObject::GEO_GAUSS; } else { throw AlgorithmException("unknown smoothing method name"); } } AlgorithmMetricSmoothing(myProgObj, mySurf, myMetric, myKernel, myMetricOut, myRoi, matchRoiColumns, fixZeros, columnNum, corrAreaMetric, myMethod); } AlgorithmMetricSmoothing::AlgorithmMetricSmoothing(ProgressObject* myProgObj, const SurfaceFile* mySurf, const MetricFile* myMetric, const double myKernel, MetricFile* myMetricOut, const MetricFile* myRoi, const bool matchRoiColumns, const bool fixZeros, const int64_t columnNum, const MetricFile* corrAreaMetric, const MetricSmoothingObject::Method myMethod) : AbstractAlgorithm(myProgObj) { float precomputeWeightWork = 5.0f;//TODO: adjust this based on number of columns to smooth, if we ever end up using progress indicators LevelProgress myProgress(myProgObj, 1.0f + precomputeWeightWork); int32_t numNodes = mySurf->getNumberOfNodes(); if (numNodes != myMetric->getNumberOfNodes()) { throw AlgorithmException("metric does not match surface in number of vertices"); } if (myRoi != NULL && myRoi->getNumberOfNodes() != numNodes) { throw AlgorithmException("roi metric does not match surface in number of vertices"); } int32_t numCols = myMetric->getNumberOfColumns(); if (columnNum < -1 || columnNum >= numCols) { throw AlgorithmException("invalid column number"); } if (myKernel <= 0.0) { throw AlgorithmException("invalid kernel size"); } if (myRoi != NULL && matchRoiColumns && numCols != myRoi->getNumberOfColumns()) { throw AlgorithmException("match roi columns specified, but roi metric has the wrong number of columns"); } CaretPointer mySmoothObj; const float* areaData = NULL; if (corrAreaMetric != NULL) { if (corrAreaMetric->getNumberOfNodes() != numNodes) { throw AlgorithmException("corrected vertex areas metric does not match surface in number of vertices"); } areaData = corrAreaMetric->getValuePointerForColumn(0); } myProgress.setTask("Precomputing Smoothing Weights"); if (matchRoiColumns) { mySmoothObj.grabNew(new MetricSmoothingObject(mySurf, myKernel, NULL, myMethod, areaData));//don't use an ROI to build weights when the ROI changes each time } else { mySmoothObj.grabNew(new MetricSmoothingObject(mySurf, myKernel, myRoi, myMethod, areaData)); } myProgress.reportProgress(precomputeWeightWork); if (columnNum == -1) { myMetricOut->setNumberOfNodesAndColumns(numNodes, myMetric->getNumberOfColumns()); myMetricOut->setStructure(mySurf->getStructure()); for (int32_t col = 0; col < numCols; ++col) { myProgress.setTask("Smoothing Column " + AString::number(col)); myMetricOut->setColumnName(col, myMetric->getColumnName(col) + ", smooth " + AString::number(myKernel)); *(myMetricOut->getPaletteColorMapping(col)) = *(myMetric->getPaletteColorMapping(col));//copy the palette settings if (myRoi != NULL && matchRoiColumns) { mySmoothObj->smoothColumn(myMetric, col, myMetricOut, col, myRoi, col, fixZeros); } else { mySmoothObj->smoothColumn(myMetric, col, myMetricOut, col, myRoi, 0, fixZeros); } myProgress.reportProgress(precomputeWeightWork + ((float)col + 1) / numCols); } } else { myMetricOut->setNumberOfNodesAndColumns(numNodes, 1); myMetricOut->setStructure(mySurf->getStructure()); myMetricOut->setColumnName(0, myMetric->getColumnName(columnNum) + ", smooth " + AString::number(myKernel)); *(myMetricOut->getPaletteColorMapping(0)) = *(myMetric->getPaletteColorMapping(columnNum));//copy the palette settings myProgress.setTask("Smoothing Column " + AString::number(columnNum)); if (myRoi != NULL && matchRoiColumns) { mySmoothObj->smoothColumn(myMetric, columnNum, myMetricOut, 0, myRoi, columnNum, fixZeros); } else { mySmoothObj->smoothColumn(myMetric, columnNum, myMetricOut, 0, myRoi, 0, fixZeros); } } } float AlgorithmMetricSmoothing::getAlgorithmInternalWeight() { return 1.0f; } float AlgorithmMetricSmoothing::getSubAlgorithmWeight() { return 0.0f; } connectome-workbench-1.2.3+git41-gc4c6c90/src/Algorithms/AlgorithmMetricSmoothing.h000066400000000000000000000041651300200146000300160ustar00rootroot00000000000000#ifndef __ALGORITHM_METRIC_SMOOTHING_H__ #define __ALGORITHM_METRIC_SMOOTHING_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 "AbstractAlgorithm.h" #include "MetricSmoothingObject.h" #include "stdint.h" #include namespace caret { class AlgorithmMetricSmoothing : public AbstractAlgorithm { public: private: AlgorithmMetricSmoothing(); protected: static float getSubAlgorithmWeight(); static float getAlgorithmInternalWeight(); public: AlgorithmMetricSmoothing(ProgressObject* myProgObj, const SurfaceFile* mySurf, const MetricFile* myMetric, const double myKernel, MetricFile* myMetricOut, const MetricFile* myRoi = NULL, const bool matchRoiColumns = false, const bool fixZeros = false, const int64_t columnNum = -1, const MetricFile* corrAreaMetric = NULL, const MetricSmoothingObject::Method myMethod = MetricSmoothingObject::GEO_GAUSS_AREA); static OperationParameters* getParameters(); static void useParameters(OperationParameters* myParams, ProgressObject* myProgObj); static AString getCommandSwitch(); static AString getShortDescription(); }; typedef TemplateAutoOperation AutoAlgorithmMetricSmoothing; } #endif //__ALGORITHM_METRIC_SMOOTHING_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Algorithms/AlgorithmMetricTFCE.cxx000066400000000000000000000440531300200146000271430ustar00rootroot00000000000000/*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 "AlgorithmMetricTFCE.h" #include "AlgorithmException.h" #include "AlgorithmMetricSmoothing.h" #include "CaretAssert.h" #include "CaretHeap.h" #include "CaretOMP.h" #include "MetricFile.h" #include "SurfaceFile.h" #include "TopologyHelper.h" #include #include #include using namespace caret; using namespace std; AString AlgorithmMetricTFCE::getCommandSwitch() { return "-metric-tfce"; } AString AlgorithmMetricTFCE::getShortDescription() { return "DO TFCE ON A METRIC FILE"; } OperationParameters* AlgorithmMetricTFCE::getParameters() { OperationParameters* ret = new OperationParameters(); ret->addSurfaceParameter(1, "surface", "the surface to compute on"); ret->addMetricParameter(2, "metric-in", "the metric to run TFCE on"); ret->addMetricOutputParameter(3, "metric-out", "the output metric"); OptionalParameter* presmoothOpt = ret->createOptionalParameter(4, "-presmooth", "smooth the metric before running TFCE"); presmoothOpt->addDoubleParameter(1, "kernel", "the sigma for the gaussian smoothing kernel, in mm"); OptionalParameter* roiOpt = ret->createOptionalParameter(5, "-roi", "select a region of interest to run TFCE on"); roiOpt->addMetricParameter(1, "roi-metric", "the area to run TFCE on, as a metric"); OptionalParameter* paramsOpt = ret->createOptionalParameter(6, "-parameters", "set parameters for TFCE integral"); paramsOpt->addDoubleParameter(1, "E", "exponent for cluster area (default 1.0)"); paramsOpt->addDoubleParameter(2, "H", "exponent for threshold value (default 2.0)"); OptionalParameter* columnSelect = ret->createOptionalParameter(7, "-column", "select a single column"); columnSelect->addStringParameter(1, "column", "the column number or name"); 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("Threshold-free cluster enhancement is a method to increase the relative value of regions that would form clusters in a standard thresholding test. ") + "This is accomplished by evaluating the integral of:\n\n" + "e(h, p)^E * h^H * dh\n\n" + "at each vertex p, where h ranges from 0 to the maximum value in the data, and e(h, p) is the extent of the cluster containing vertex p at threshold h. " + "Negative values are similarly enhanced by negating the data, running the same process, and negating the result.\n\n" + "When using -presmooth with -corrected-areas, note that it is an approximate correction within the smoothing algorithm (the TFCE correction is exact). " + "Doing smoothing on individual surfaces before averaging/TFCE is preferred, when possible, in order to better tie the smoothing kernel size to the original feature size.\n\n" + "The TFCE method is explained in: Smith SM, Nichols TE., \"Threshold-free cluster enhancement: addressing problems of smoothing, threshold dependence and localisation in cluster inference.\" Neuroimage. 2009 Jan 1;44(1):83-98. PMID: 18501637" ); return ret; } void AlgorithmMetricTFCE::useParameters(OperationParameters* myParams, ProgressObject* myProgObj) { SurfaceFile* mySurf = myParams->getSurface(1); MetricFile* myMetric = myParams->getMetric(2); MetricFile* myMetricOut = myParams->getOutputMetric(3); float presmooth = 0.0f; OptionalParameter* presmoothOpt = myParams->getOptionalParameter(4); if (presmoothOpt->m_present) { presmooth = (float)presmoothOpt->getDouble(1); if (presmooth <= 0.0f) throw AlgorithmException("presmooth kernel size must be positive"); } MetricFile* myRoi = NULL; OptionalParameter* roiOpt = myParams->getOptionalParameter(5); if (roiOpt->m_present) { myRoi = roiOpt->getMetric(1); } float param_e = 1.0f, param_h = 2.0; OptionalParameter* paramsOpt = myParams->getOptionalParameter(6); if (paramsOpt->m_present) { param_e = (float)paramsOpt->getDouble(1); param_h = (float)paramsOpt->getDouble(2); } OptionalParameter* columnSelect = myParams->getOptionalParameter(7); int columnNum = -1; if (columnSelect->m_present) {//set up to use the single column columnNum = (int)myMetric->getMapIndexFromNameOrNumber(columnSelect->getString(1)); if (columnNum < 0) { throw AlgorithmException("invalid column specified"); } } MetricFile* corrAreaMetric = NULL; OptionalParameter* corrAreaOpt = myParams->getOptionalParameter(8); if (corrAreaOpt->m_present) { corrAreaMetric = corrAreaOpt->getMetric(1); } AlgorithmMetricTFCE(myProgObj, mySurf, myMetric, myMetricOut, presmooth, myRoi, param_e, param_h, columnNum, corrAreaMetric); } AlgorithmMetricTFCE::AlgorithmMetricTFCE(ProgressObject* myProgObj, const SurfaceFile* mySurf, const MetricFile* myMetric, MetricFile* myMetricOut, const float& presmooth, const MetricFile* myRoi, const float& param_e, const float& param_h, const int& columnNum, const MetricFile* corrAreaMetric) : AbstractAlgorithm(myProgObj) { LevelProgress myProgress(myProgObj); if (mySurf->getNumberOfNodes() != myMetric->getNumberOfNodes()) throw AlgorithmException("metric and surface have different number of vertices"); if (myRoi != NULL && mySurf->getNumberOfNodes() != myRoi->getNumberOfNodes()) throw AlgorithmException("roi metric and surface have different number of vertices"); if (corrAreaMetric != NULL && mySurf->getNumberOfNodes() != corrAreaMetric->getNumberOfNodes()) throw AlgorithmException("corrected area metric and surface have different number of vertices"); if (columnNum < -1 || columnNum >= myMetric->getNumberOfColumns()) throw AlgorithmException("invalid column specified"); const float* roiData = NULL, *areaData = NULL; vector surfAreaData; if (corrAreaMetric == NULL) { mySurf->computeNodeAreas(surfAreaData); areaData = surfAreaData.data(); } else { areaData = corrAreaMetric->getValuePointerForColumn(0); } if (myRoi != NULL) roiData = myRoi->getValuePointerForColumn(0); if (columnNum == -1) { const MetricFile* toUse = myMetric; MetricFile postSmooth; if (presmooth > 0.0f) { AlgorithmMetricSmoothing(NULL, mySurf, myMetric, presmooth, &postSmooth, myRoi, false, false, -1, corrAreaMetric); toUse = &postSmooth; } int numCols = myMetric->getNumberOfColumns(); myMetricOut->setNumberOfNodesAndColumns(mySurf->getNumberOfNodes(), numCols); myMetricOut->setStructure(mySurf->getStructure()); #pragma omp CARET_PAR { vector outcol(mySurf->getNumberOfNodes(), 0.0f); #pragma omp CARET_FOR for (int col = 0; col < numCols; ++col) { processColumn(mySurf, toUse->getValuePointerForColumn(col), outcol.data(), roiData, param_e, param_h, areaData); myMetricOut->setValuesForColumn(col, outcol.data()); myMetricOut->setMapName(col, myMetric->getMapName(col)); } } } else { const MetricFile* toUse = myMetric; int useCol = columnNum; MetricFile postSmooth; if (presmooth > 0.0f) { AlgorithmMetricSmoothing(NULL, mySurf, myMetric, presmooth, &postSmooth, myRoi, false, false, columnNum, corrAreaMetric); toUse = &postSmooth; useCol = 0; } myMetricOut->setNumberOfNodesAndColumns(mySurf->getNumberOfNodes(), 1); myMetricOut->setStructure(mySurf->getStructure()); vector outcol(mySurf->getNumberOfNodes(), 0.0f); processColumn(mySurf, toUse->getValuePointerForColumn(useCol), outcol.data(), roiData, param_e, param_h, areaData); myMetricOut->setValuesForColumn(0, outcol.data()); myMetricOut->setMapName(0, myMetric->getMapName(columnNum)); } } void AlgorithmMetricTFCE::processColumn(const SurfaceFile* mySurf, const float* colData, float* outData, const float* roiData, const float& param_e, const float& param_h, const float* areaData) { int numNodes = mySurf->getNumberOfNodes(); vector accum(numNodes, 0.0); CaretPointer myHelper = mySurf->getTopologyHelper(); tfce_pos(myHelper, colData, accum.data(), roiData, param_e, param_h, areaData); vector negData(numNodes); for (int i = 0; i < numNodes; ++i) { negData[i] = -colData[i]; } tfce_pos(myHelper, negData.data(), accum.data(), roiData, param_e, param_h, areaData);//negatives and positives don't overlap, so reuse the accum array for (int i = 0; i < numNodes; ++i) { if (roiData == NULL || roiData[i] > 0.0f) { if (colData[i] < 0.0f) { outData[i] = (float)-accum[i]; } else { outData[i] = (float)accum[i]; } } else { outData[i] = 0.0f; } } } namespace {//hidden namespace just to make sure things don't collide struct Cluster { double accumVal, totalArea; vector members; float lastVal; bool first; Cluster() { first = true; accumVal = 0.0; totalArea = 0.0; } void addMember(const int& node, const float& val, const float& area, const float& param_e, const float& param_h) { update(val, param_e, param_h); members.push_back(node); totalArea += area; } void update(const float& bottomVal, const float& param_e, const float& param_h) { if (first) { lastVal = bottomVal; first = false; } else { if (bottomVal != lastVal)//skip computing if there is no difference { CaretAssert(bottomVal < lastVal); double integrated_h = param_h + 1.0f;//integral(x^h) = (x^(h + 1))/(h + 1) + C double newSlice = pow(totalArea, (double)param_e) * (pow((double)lastVal, integrated_h) - pow((double)bottomVal, integrated_h)) / integrated_h; accumVal += newSlice; lastVal = bottomVal;//computing in double precision, with float for inputs, puts the smallest difference between values far greater than the instability of the computation } } } }; int allocCluster(vector& clusterList, set& deadClusters) { if (deadClusters.empty()) { clusterList.push_back(Cluster()); return (int)(clusterList.size() - 1); } else { set::iterator iter = deadClusters.begin(); int ret = *iter; deadClusters.erase(iter); clusterList[ret] = Cluster();//reinitialize return ret; } } } void AlgorithmMetricTFCE::tfce_pos(TopologyHelper* myHelper, const float* colData, double* accumData, const float* roiData, const float& param_e, const float& param_h, const float* areaData) { int numNodes = myHelper->getNumberOfNodes(); vector membership(numNodes, -1);//int is enough as long as numNodes is fine as an int, for obvious reasons vector clusterList; set deadClusters;//to allow reallocation without changing indices CaretSimpleMaxHeap nodeHeap; for (int i = 0; i < numNodes; ++i) { if ((roiData == NULL || roiData[i] > 0.0f) && colData[i] > 0.0f) { nodeHeap.push(i, colData[i]); } } while (!nodeHeap.isEmpty()) { float value; int node = nodeHeap.pop(&value); const vector& neighbors = myHelper->getNodeNeighbors(node); int numNeigh = (int)neighbors.size(); set touchingClusters; for (int i = 0; i < numNeigh; ++i) { if (membership[neighbors[i]] != -1) { touchingClusters.insert(membership[neighbors[i]]); } } int numTouching = (int)touchingClusters.size(); switch (numTouching) { case 0://make new cluster { int newCluster = allocCluster(clusterList, deadClusters); clusterList[newCluster].addMember(node, value, areaData[node], param_e, param_h); membership[node] = newCluster; break; } case 1://add to cluster { int whichCluster = *(touchingClusters.begin()); clusterList[whichCluster].addMember(node, value, areaData[node], param_e, param_h); membership[node] = whichCluster; accumData[node] -= clusterList[whichCluster].accumVal;//the accum value is the current amount less than the peak value that the edge of the cluster has (this node is on the edge) break;//so, when the cluster merges or reaches 0, we add the accum value to every member and zero the accum value of the merged cluster, and we get the correct value in the end (with far fewer flops than integrating everywhere) } default://merge all touching clusters { int mergedIndex = -1, biggestSize = 0;//find the biggest cluster (in number of members) and use as merged cluster, for optimization purposes for (set::iterator iter = touchingClusters.begin(); iter != touchingClusters.end(); ++iter) { if ((int)clusterList[*iter].members.size() > biggestSize) { mergedIndex = *iter; biggestSize = (int)clusterList[*iter].members.size(); } } CaretAssertVectorIndex(clusterList, mergedIndex); Cluster& mergedCluster = clusterList[mergedIndex]; mergedCluster.update(value, param_e, param_h);//recalculate to align cluster bottoms for (set::iterator iter = touchingClusters.begin(); iter != touchingClusters.end(); ++iter) { if (*iter != mergedIndex)//if we are the largest cluster, don't modify the per-vertex accum for members, so merges between small and large clusters are cheap { Cluster& thisCluster = clusterList[*iter]; thisCluster.update(value, param_e, param_h);//recalculate to align cluster bottoms int numMembers = (int)thisCluster.members.size(); double correctionVal = thisCluster.accumVal - mergedCluster.accumVal;//fix the accum values in the side cluster so we can add the merged cluster's accum to everything at the end for (int j = 0; j < numMembers; ++j)//add the correction value to every member so that we have the current integrated values correct { accumData[thisCluster.members[j]] += correctionVal;//apply the correction membership[thisCluster.members[j]] = mergedIndex;//change the membership lookup } mergedCluster.members.insert(mergedCluster.members.end(), thisCluster.members.begin(), thisCluster.members.end());//copy all members mergedCluster.totalArea += thisCluster.totalArea; deadClusters.insert(*iter);//kill it vector().swap(clusterList[*iter].members);//also try to deallocate member list } } mergedCluster.addMember(node, value, areaData[node], param_e, param_h);//will not trigger recomputation, we already recomputed at this value accumData[node] -= mergedCluster.accumVal;//the vertex they merge on must not get the peak value of the cluster, obviously, so again, record its difference from peak membership[node] = mergedIndex; break;//NOTE: do not reset the accum value of the merged cluster, we specifically avoided modifying the per-vertex accum for its members, so the cluster accum is still in play } } } int listSize = (int)clusterList.size();//final cleanup of accum values for (int i = 0; i < listSize; ++i) { if (deadClusters.find(i) != deadClusters.end()) continue;//ignore clusters that don't exist Cluster& thisCluster = clusterList[i]; thisCluster.update(0.0f, param_e, param_h);//update to include the to-zero slice int numMembers = (int)thisCluster.members.size(); for (int j = 0; j < numMembers; ++j) { accumData[thisCluster.members[j]] += thisCluster.accumVal;//add the resulting slice to all members - their stored data contains the offset between the cluster peak and their corect value } } } float AlgorithmMetricTFCE::getAlgorithmInternalWeight() { return 1.0f;//override this if needed, if the progress bar isn't smooth } float AlgorithmMetricTFCE::getSubAlgorithmWeight() { //return AlgorithmInsertNameHere::getAlgorithmWeight();//if you use a subalgorithm return 0.0f; } connectome-workbench-1.2.3+git41-gc4c6c90/src/Algorithms/AlgorithmMetricTFCE.h000066400000000000000000000043651300200146000265720ustar00rootroot00000000000000#ifndef __ALGORITHM_METRIC_TFCE_H__ #define __ALGORITHM_METRIC_TFCE_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 "AbstractAlgorithm.h" namespace caret { class TopologyHelper; class AlgorithmMetricTFCE : public AbstractAlgorithm { AlgorithmMetricTFCE(); void processColumn(const SurfaceFile* mySurf, const float* colData, float* outData, const float* roiData, const float& param_e, const float& param_h, const float* areaData); void tfce_pos(TopologyHelper* myHelper, const float* colData, double* accumData, const float* roiData, const float& param_e, const float& param_h, const float* areaData); protected: static float getSubAlgorithmWeight(); static float getAlgorithmInternalWeight(); public: AlgorithmMetricTFCE(ProgressObject* myProgObj, const SurfaceFile* mySurf, const MetricFile* myMetric, MetricFile* myMetricOut, const float& presmooth = 0.0f, const MetricFile* myRoi = NULL, const float& param_e = 1.0f, const float& param_h = 2.0f, const int& columnNum = -1, const MetricFile* corrAreaMetric = NULL); static OperationParameters* getParameters(); static void useParameters(OperationParameters* myParams, ProgressObject* myProgObj); static AString getCommandSwitch(); static AString getShortDescription(); }; typedef TemplateAutoOperation AutoAlgorithmMetricTFCE; } #endif //__ALGORITHM_METRIC_TFCE_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Algorithms/AlgorithmMetricToVolumeMapping.cxx000066400000000000000000000246331300200146000315120ustar00rootroot00000000000000/*LICENSE_START*/ /* * Copyright (C) 2015 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 "AlgorithmMetricToVolumeMapping.h" #include "AlgorithmException.h" #include "CaretAssert.h" #include "CaretOMP.h" #include "CaretPointLocator.h" #include "MetricFile.h" #include "RibbonMappingHelper.h" #include "SurfaceFile.h" #include "VolumeFile.h" #include "VoxelIJK.h" #include using namespace caret; using namespace std; AString AlgorithmMetricToVolumeMapping::getCommandSwitch() { return "-metric-to-volume-mapping"; } AString AlgorithmMetricToVolumeMapping::getShortDescription() { return "MAP METRIC FILE TO VOLUME"; } OperationParameters* AlgorithmMetricToVolumeMapping::getParameters() { OperationParameters* ret = new OperationParameters(); ret->addMetricParameter(1, "metric", "the input metric file"); ret->addSurfaceParameter(2, "surface", "the surface to use coordinates from"); ret->addVolumeParameter(3, "volume-space", "a volume file in the desired output volume space"); ret->addVolumeOutputParameter(4, "volume-out", "the output volume file"); OptionalParameter* nearestVertOpt = ret->createOptionalParameter(5, "-nearest-vertex", "use the value from the vertex closest to the voxel center"); nearestVertOpt->addDoubleParameter(1, "distance", "how far from the surface to map values to voxels, in mm"); OptionalParameter* ribbonOpt = ret->createOptionalParameter(6, "-ribbon-constrained", "use ribbon constrained mapping algorithm"); ribbonOpt->addSurfaceParameter(1, "inner-surf", "the inner surface of the ribbon"); 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"); 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. " ); return ret; } void AlgorithmMetricToVolumeMapping::useParameters(OperationParameters* myParams, ProgressObject* myProgObj) { MetricFile* myMetric = myParams->getMetric(1); SurfaceFile* mySurf = myParams->getSurface(2); VolumeFile* myTemplateVol = myParams->getVolume(3); VolumeFile* myVolOut = myParams->getOutputVolume(4); enum Method { INVALID, NEAREST, RIBBON }; bool haveMethod = false; Method myMethod = INVALID; float nearDist = -1.0f; OptionalParameter* nearestVertOpt = myParams->getOptionalParameter(5); if (nearestVertOpt->m_present) { myMethod = NEAREST; haveMethod = true; nearDist = (float)nearestVertOpt->getDouble(1); if (nearDist < 0.0f) { throw AlgorithmException("invalid distance specified"); } } SurfaceFile* innerSurf = NULL, *outerSurf = NULL; int subDivs = 3; OptionalParameter* ribbonOpt = myParams->getOptionalParameter(6); if (ribbonOpt->m_present) { if (haveMethod) { throw AlgorithmException("more than one mapping method specified"); } myMethod = RIBBON; haveMethod = true; innerSurf = ribbonOpt->getSurface(1); outerSurf = ribbonOpt->getSurface(2); OptionalParameter* ribbonSubdivOpt = ribbonOpt->getOptionalParameter(3); if (ribbonSubdivOpt->m_present) { subDivs = (int)ribbonSubdivOpt->getInteger(1); if (subDivs < 1) { throw AlgorithmException("invalid number of subdivisions specified"); } } } if (!haveMethod) { throw AlgorithmException("no mapping method specified"); } switch (myMethod) { case NEAREST: AlgorithmMetricToVolumeMapping(myProgObj, myMetric, mySurf, myTemplateVol->getVolumeSpace(), myVolOut, nearDist); break; case RIBBON: AlgorithmMetricToVolumeMapping(myProgObj, myMetric, mySurf, myTemplateVol->getVolumeSpace(), myVolOut, innerSurf, outerSurf, subDivs); break; case INVALID: CaretAssert(0); throw AlgorithmException("internal error, tell the developers what you just tried to do"); } } AlgorithmMetricToVolumeMapping::AlgorithmMetricToVolumeMapping(ProgressObject* myProgObj, const MetricFile* myMetric, const SurfaceFile* mySurf, const VolumeSpace& myVolSpace, VolumeFile* myVolOut, const float& nearDist) : AbstractAlgorithm(myProgObj) { LevelProgress myProgress(myProgObj); if (myMetric->getNumberOfNodes() != mySurf->getNumberOfNodes()) { throw AlgorithmException("input surface and metric have different number of vertices"); } if (nearDist < 0.0f) { throw AlgorithmException("invalid distance specified in surface to volume mapping"); } checkStructureMatch(myMetric, mySurf->getStructure(), "input metric file", "the surface has"); int numCols = myMetric->getNumberOfColumns(); myVolOut->reinitialize(myVolSpace, numCols); const int64_t* dims = myVolSpace.getDims(); const int64_t frameSize = dims[0] * dims[1] * dims[2]; vector voxelToVertex(frameSize); CaretPointer myLocator = mySurf->getPointLocator(); #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) { float voxelCoord[3]; myVolSpace.indexToSpace(i, j, k, voxelCoord); voxelToVertex[myVolSpace.getIndex(i, j, k)] = myLocator->closestPointLimited(voxelCoord, nearDist); } } } vector scratchFrame(frameSize, 0.0f); for (int i = 0; i < numCols; ++i) { const float* metricData = myMetric->getValuePointerForColumn(i); for (int64_t v = 0; v < frameSize; ++v) { if (voxelToVertex[v] >= 0) { scratchFrame[v] = metricData[voxelToVertex[v]]; } } myVolOut->setFrame(scratchFrame.data(), i); myVolOut->setMapName(i, myMetric->getMapName(i)); } } 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) { int numNodes = mySurf->getNumberOfNodes(); if (myMetric->getNumberOfNodes() != numNodes) { throw AlgorithmException("metric and input surfaces have different number of vertices"); } if (!mySurf->hasNodeCorrespondence(*outerSurf) || !mySurf->hasNodeCorrespondence(*innerSurf)) { throw AlgorithmException("all surfaces must have vertex correspondence"); } if (subDivs < 0.0f) { throw AlgorithmException("invalid number of subdivisions specified in surface to volume mapping"); } checkStructureMatch(myMetric, mySurf->getStructure(), "input metric file", "the surface has"); checkStructureMatch(innerSurf, myMetric->getStructure(), "inner surface file", "the metric file has"); checkStructureMatch(outerSurf, myMetric->getStructure(), "outer surface file", "the metric file has"); int numCols = myMetric->getNumberOfColumns(); myVolOut->reinitialize(myVolSpace, numCols); vector > forwardWeights; RibbonMappingHelper::computeWeightsRibbon(forwardWeights, myVolSpace, innerSurf, outerSurf, NULL, subDivs); map > > reverseWeights; for (int i = 0; i < numNodes; ++i) { for (int v = 0; v < (int)forwardWeights[i].size(); ++v) {//vectors initialize to empty reverseWeights[VoxelIJK(forwardWeights[i][v].ijk)].push_back(pair(i, forwardWeights[i][v].weight)); } } const int64_t* dims = myVolSpace.getDims(); const int64_t frameSize = dims[0] * dims[1] * dims[2]; vector scratchFrame(frameSize, 0.0f); for (int m = 0; m < numCols; ++m) { const float* colData = myMetric->getValuePointerForColumn(m); for (map > >::const_iterator iter = reverseWeights.begin(); iter != reverseWeights.end(); ++iter) { double accum = 0.0, totalWeight = 0.0; for (int i = 0; i < (int)iter->second.size(); ++i) { totalWeight += iter->second[i].second; 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; } myVolOut->setFrame(scratchFrame.data(), m); myVolOut->setMapName(m, myMetric->getMapName(m)); } } float AlgorithmMetricToVolumeMapping::getAlgorithmInternalWeight() { return 1.0f;//override this if needed, if the progress bar isn't smooth } float AlgorithmMetricToVolumeMapping::getSubAlgorithmWeight() { //return AlgorithmInsertNameHere::getAlgorithmWeight();//if you use a subalgorithm return 0.0f; } connectome-workbench-1.2.3+git41-gc4c6c90/src/Algorithms/AlgorithmMetricToVolumeMapping.h000066400000000000000000000043121300200146000311270ustar00rootroot00000000000000#ifndef __ALGORITHM_METRIC_TO_VOLUME_MAPPING_H__ #define __ALGORITHM_METRIC_TO_VOLUME_MAPPING_H__ /*LICENSE_START*/ /* * Copyright (C) 2015 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 VolumeSpace; class AlgorithmMetricToVolumeMapping : public AbstractAlgorithm { AlgorithmMetricToVolumeMapping(); protected: static float getSubAlgorithmWeight(); static float getAlgorithmInternalWeight(); public: ///nearest vertex AlgorithmMetricToVolumeMapping(ProgressObject* myProgObj, const MetricFile* myMetric, const SurfaceFile* mySurf, const VolumeSpace& myVolSpace, 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); static OperationParameters* getParameters(); static void useParameters(OperationParameters* myParams, ProgressObject* myProgObj); static AString getCommandSwitch(); static AString getShortDescription(); }; typedef TemplateAutoOperation AutoAlgorithmMetricToVolumeMapping; } #endif //__ALGORITHM_METRIC_TO_VOLUME_MAPPING_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Algorithms/AlgorithmMetricVectorOperation.cxx000066400000000000000000000203041300200146000315360ustar00rootroot00000000000000/*LICENSE_START*/ /* * Copyright (C) 2015 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 "AlgorithmMetricVectorOperation.h" #include "AlgorithmException.h" #include "CaretLogger.h" #include "MetricFile.h" #include "Vector3D.h" using namespace caret; using namespace std; AString AlgorithmMetricVectorOperation::getCommandSwitch() { return "-metric-vector-operation"; } AString AlgorithmMetricVectorOperation::getShortDescription() { return "DO A VECTOR OPERATION ON METRIC FILES"; } OperationParameters* AlgorithmMetricVectorOperation::getParameters() { OperationParameters* ret = new OperationParameters(); ret->addMetricParameter(1, "vectors-a", "first vector input file"); ret->addMetricParameter(2, "vectors-b", "second vector input file"); ret->addStringParameter(3, "operation", "what vector operation to do"); ret->addMetricOutputParameter(4, "metric-out", "the output file"); ret->createOptionalParameter(5, "-normalize-a", "normalize vectors of first input"); ret->createOptionalParameter(6, "-normalize-b", "normalize vectors of second input"); ret->createOptionalParameter(7, "-normalize-output", "normalize output vectors (not valid for dot product)"); ret->createOptionalParameter(8, "-magnitude", "output the magnitude of the result (not valid for dot product)"); AString myText = AString("Does a vector operation on two metric files (that must have a multiple of 3 columns). ") + "Either of the inputs may have multiple vectors (more than 3 columns), but not both (at least one must have exactly 3 columns). " + "The -magnitude and -normalize-output options may not be specified together, or with an operation that returns a scalar (dot product). " + "The parameter must be one of the following:\n"; vector opList = VectorOperation::getAllOperations(); for (int i = 0; i < (int)opList.size(); ++i) { myText += "\n" + VectorOperation::operationToString(opList[i]); } ret->setHelpText(myText); return ret; } void AlgorithmMetricVectorOperation::useParameters(OperationParameters* myParams, ProgressObject* myProgObj) { MetricFile* metricA = myParams->getMetric(1); MetricFile* metricB = myParams->getMetric(2); AString operString = myParams->getString(3); bool ok = false; VectorOperation::Operation myOper = VectorOperation::stringToOperation(operString, ok); if (!ok) throw AlgorithmException("unrecognized operation string: " + operString); MetricFile* myMetricOut = myParams->getOutputMetric(4); bool normA = myParams->getOptionalParameter(5)->m_present; bool normB = myParams->getOptionalParameter(6)->m_present; bool normOut = myParams->getOptionalParameter(7)->m_present; bool magOut = myParams->getOptionalParameter(8)->m_present; AlgorithmMetricVectorOperation(myProgObj, metricA, metricB, myOper, myMetricOut, normA, normB, normOut, magOut); } AlgorithmMetricVectorOperation::AlgorithmMetricVectorOperation(ProgressObject* myProgObj, const MetricFile* metricA, const MetricFile* metricB, const VectorOperation::Operation& myOper, MetricFile* myMetricOut, const bool& normA, const bool& normB, const bool& normOut, const bool& magOut) : AbstractAlgorithm(myProgObj) { LevelProgress myProgress(myProgObj); StructureEnum::Enum checkStruct = metricA->getStructure(); int numNodes = metricA->getNumberOfNodes(); if (numNodes != metricB->getNumberOfNodes()) throw AlgorithmException("inputs have different numbers of nodes"); int numColA = metricA->getNumberOfColumns(), numColB = metricB->getNumberOfColumns(); if (numColA % 3 != 0) throw AlgorithmException("number of columns of first input is not a multiple of 3"); if (numColB % 3 != 0) throw AlgorithmException("number of columns of second input is not a multiple of 3"); int numVecA = numColA / 3, numVecB = numColB / 3; if (numVecA > 1 && numVecB > 1) throw AlgorithmException("both inputs have more than 3 columns (more than 1 vector)"); if (normOut && magOut) throw AlgorithmException("normalizing the output and taking the magnitude is meaningless"); bool opScalarResult = VectorOperation::operationReturnsScalar(myOper); if (opScalarResult && (normOut || magOut)) throw AlgorithmException("cannot normalize or take magnitude of a scalar result (such as a dot product)"); if (checkStruct == StructureEnum::INVALID) { CaretLogWarning("first input vector file has INVALID structure"); } else { checkStructureMatch(metricB, checkStruct, "second input vector file", "the first has"); } bool swapped = false; const MetricFile* multiVec = metricA, *singleVec = metricB; int numOutVecs = numVecA; if (numVecB > 1) { multiVec = metricB; singleVec = metricA; numOutVecs = numVecB; swapped = true; } int numColsOut = numOutVecs * 3; if (opScalarResult || magOut) numColsOut = numOutVecs; myMetricOut->setNumberOfNodesAndColumns(numNodes, numColsOut); myMetricOut->setStructure(checkStruct); vector outCols[3];//let the scalar result case overallocate outCols[0].resize(numNodes); outCols[1].resize(numNodes); outCols[2].resize(numNodes); const float* xColSingle = singleVec->getValuePointerForColumn(0); const float* yColSingle = singleVec->getValuePointerForColumn(1); const float* zColSingle = singleVec->getValuePointerForColumn(2); for (int v = 0; v < numOutVecs; ++v) { const float* xColMulti = multiVec->getValuePointerForColumn(v * 3); const float* yColMulti = multiVec->getValuePointerForColumn(v * 3 + 1); const float* zColMulti = multiVec->getValuePointerForColumn(v * 3 + 2); for (int i = 0; i < numNodes; ++i) { Vector3D vecA(xColMulti[i], yColMulti[i], zColMulti[i]); Vector3D vecB(xColSingle[i], yColSingle[i], zColSingle[i]); if (swapped) { Vector3D tempVec = vecA; vecA = vecB; vecB = tempVec; } if (normA) vecA = vecA.normal(); if (normB) vecB = vecB.normal(); if (opScalarResult) { outCols[0][i] = VectorOperation::doScalarOperation(vecA, vecB, myOper); } else { Vector3D tempVec = VectorOperation::doVectorOperation(vecA, vecB, myOper); if (normOut) tempVec = tempVec.normal(); if (magOut) { outCols[0][i] = tempVec.length(); } else { outCols[0][i] = tempVec[0]; outCols[1][i] = tempVec[1]; outCols[2][i] = tempVec[2]; } } } if (opScalarResult || magOut) { myMetricOut->setValuesForColumn(v, outCols[0].data()); } else { myMetricOut->setValuesForColumn(v * 3, outCols[0].data()); myMetricOut->setValuesForColumn(v * 3 + 1, outCols[1].data()); myMetricOut->setValuesForColumn(v * 3 + 2, outCols[2].data()); } } } float AlgorithmMetricVectorOperation::getAlgorithmInternalWeight() { return 1.0f;//override this if needed, if the progress bar isn't smooth } float AlgorithmMetricVectorOperation::getSubAlgorithmWeight() { //return AlgorithmInsertNameHere::getAlgorithmWeight();//if you use a subalgorithm return 0.0f; } connectome-workbench-1.2.3+git41-gc4c6c90/src/Algorithms/AlgorithmMetricVectorOperation.h000066400000000000000000000037141300200146000311710ustar00rootroot00000000000000#ifndef __ALGORITHM_METRIC_VECTOR_OPERATION_H__ #define __ALGORITHM_METRIC_VECTOR_OPERATION_H__ /*LICENSE_START*/ /* * Copyright (C) 2015 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 "VectorOperation.h" namespace caret { class AlgorithmMetricVectorOperation : public AbstractAlgorithm { AlgorithmMetricVectorOperation(); protected: static float getSubAlgorithmWeight(); static float getAlgorithmInternalWeight(); public: AlgorithmMetricVectorOperation(ProgressObject* myProgObj, const MetricFile* metricA, const MetricFile* metricB, const VectorOperation::Operation& myOper, MetricFile* myMetricOut, const bool& normA = false, const bool& normB = false, const bool& normOut = false, const bool& magOut = false); static OperationParameters* getParameters(); static void useParameters(OperationParameters* myParams, ProgressObject* myProgObj); static AString getCommandSwitch(); static AString getShortDescription(); }; typedef TemplateAutoOperation AutoAlgorithmMetricVectorOperation; } #endif //__ALGORITHM_METRIC_VECTOR_OPERATION_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Algorithms/AlgorithmMetricVectorTowardROI.cxx000066400000000000000000000133731300200146000314200ustar00rootroot00000000000000/*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 "AlgorithmMetricVectorTowardROI.h" #include "AlgorithmException.h" #include "CaretOMP.h" #include "GeodesicHelper.h" #include "MetricFile.h" #include "SurfaceFile.h" #include "Vector3D.h" #include using namespace caret; using namespace std; AString AlgorithmMetricVectorTowardROI::getCommandSwitch() { return "-metric-vector-toward-roi"; } AString AlgorithmMetricVectorTowardROI::getShortDescription() { return "FIND IF VECTORS POINT TOWARD AN ROI"; } OperationParameters* AlgorithmMetricVectorTowardROI::getParameters() { OperationParameters* ret = new OperationParameters(); ret->addSurfaceParameter(1, "surface", "the surface to compute on"); ret->addMetricParameter(2, "target-roi", "the roi to find the shortest path to"); ret->addMetricOutputParameter(3, "metric-out", "the output metric"); OptionalParameter* roiOpt = ret->createOptionalParameter(4, "-roi", "don't compute for vertices outside an roi"); roiOpt->addMetricParameter(1, "roi-metric", "the region to compute inside, as a metric"); ret->setHelpText( AString("At each vertex, compute the vector along the start of the shortest path to the ROI.") ); return ret; } void AlgorithmMetricVectorTowardROI::useParameters(OperationParameters* myParams, ProgressObject* myProgObj) { SurfaceFile* mySurf = myParams->getSurface(1); MetricFile* targetRoi = myParams->getMetric(2); MetricFile* myMetricOut = myParams->getOutputMetric(3); MetricFile* computeRoi = NULL; OptionalParameter* roiOpt = myParams->getOptionalParameter(4); if (roiOpt->m_present) { computeRoi = roiOpt->getMetric(1); } AlgorithmMetricVectorTowardROI(myProgObj, mySurf, targetRoi, myMetricOut, computeRoi); } AlgorithmMetricVectorTowardROI::AlgorithmMetricVectorTowardROI(ProgressObject* myProgObj, SurfaceFile* mySurf, const MetricFile* targetRoi, MetricFile* myMetricOut, const MetricFile* computeRoi) : AbstractAlgorithm(myProgObj) { LevelProgress myProgress(myProgObj); int numNodes = mySurf->getNumberOfNodes(); if (targetRoi->getNumberOfNodes() != numNodes) throw AlgorithmException("target roi and surface have different number of vertices"); const float* computeRoiCol = NULL; if (computeRoi != NULL) { if (computeRoi->getNumberOfNodes() != numNodes) throw AlgorithmException("compute roi and surface have different number of vertices"); computeRoiCol = computeRoi->getValuePointerForColumn(0); } mySurf->computeNormals(); const float* myNormals = mySurf->getNormalData(); myMetricOut->setNumberOfNodesAndColumns(numNodes, 3); myMetricOut->setStructure(mySurf->getStructure()); vector roiConvert(numNodes, 0); const float* coordData = mySurf->getCoordinateData(); const float* targetData = targetRoi->getValuePointerForColumn(0); bool haveTarget = false; vector outCol[3]; outCol[0].resize(numNodes, 0.0f); outCol[1].resize(numNodes, 0.0f); outCol[2].resize(numNodes, 0.0f); for (int i = 0; i < numNodes; ++i) { if (targetData[i] > 0.0f) { roiConvert[i] = 1; haveTarget = true; } } if (!haveTarget) throw AlgorithmException("target roi is empty"); #pragma omp CARET_PAR { CaretPointer myGeoHelp = mySurf->getGeodesicHelper(); #pragma omp CARET_FOR schedule(dynamic) for (int i = 0; i < numNodes; ++i) { if (computeRoiCol == NULL || computeRoiCol[i] > 0.0f) { Vector3D normal = Vector3D(myNormals + i * 3).normal();//ensure unit length, just in case vector pathNodes; vector pathDists; myGeoHelp->getClosestNodeInRoi(i, roiConvert.data(), pathNodes, pathDists, true);//not using smooth will make noisier results, project via normal to avoid curvature problems if (pathNodes.size() < 2)//no path found, or node is inside the target roi { continue;//will already be zero } Vector3D pathVec = Vector3D(coordData + pathNodes[1] * 3) - Vector3D(coordData + i * 3); Vector3D pathProject = (pathVec - pathVec.dot(normal) * normal).normal();//the path vector is a direction only, just normalize after projection outCol[0][i] = pathProject[0]; outCol[1][i] = pathProject[1]; outCol[2][i] = pathProject[2]; } } } myMetricOut->setValuesForColumn(0, outCol[0].data()); myMetricOut->setValuesForColumn(1, outCol[1].data()); myMetricOut->setValuesForColumn(2, outCol[2].data()); } float AlgorithmMetricVectorTowardROI::getAlgorithmInternalWeight() { return 1.0f;//override this if needed, if the progress bar isn't smooth } float AlgorithmMetricVectorTowardROI::getSubAlgorithmWeight() { return 0.0f; } connectome-workbench-1.2.3+git41-gc4c6c90/src/Algorithms/AlgorithmMetricVectorTowardROI.h000066400000000000000000000034711300200146000310430ustar00rootroot00000000000000#ifndef __ALGORITHM_METRIC_VECTOR_TOWARD_ROI_H__ #define __ALGORITHM_METRIC_VECTOR_TOWARD_ROI_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 "AbstractAlgorithm.h" namespace caret { class AlgorithmMetricVectorTowardROI : public AbstractAlgorithm { AlgorithmMetricVectorTowardROI(); protected: static float getSubAlgorithmWeight(); static float getAlgorithmInternalWeight(); public: AlgorithmMetricVectorTowardROI(ProgressObject* myProgObj, SurfaceFile* mySurf, const MetricFile* targetRoi, MetricFile* myMetricOut, const MetricFile* computeRoi = NULL); static OperationParameters* getParameters(); static void useParameters(OperationParameters* myParams, ProgressObject* myProgObj); static AString getCommandSwitch(); static AString getShortDescription(); }; typedef TemplateAutoOperation AutoAlgorithmMetricVectorTowardROI; } #endif //__ALGORITHM_METRIC_VECTOR_TOWARD_ROI_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Algorithms/AlgorithmNodesInsideBorder.cxx000066400000000000000000001020231300200146000306100ustar00rootroot00000000000000 /*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 "AlgorithmNodesInsideBorder.h" #include "AlgorithmException.h" #include "Border.h" #include "BorderFile.h" #include "CaretAssert.h" #include "CaretOMP.h" #include "CiftiBrainordinateLabelFile.h" #include "CiftiBrainordinateScalarFile.h" #include "CiftiFile.h" #include "GeodesicHelper.h" #include "LabelFile.h" #include "MathFunctions.h" #include "MetricFile.h" #include "SurfaceFile.h" #include "SurfaceProjectedItem.h" #include "TopologyHelper.h" using namespace caret; using namespace std; AString AlgorithmNodesInsideBorder::getCommandSwitch() { return "-border-to-rois";//maybe the command should be in a separate file, and the nodes inside border code should be a helper class? } AString AlgorithmNodesInsideBorder::getShortDescription() { return "MAKE METRIC ROIS FROM BORDERS"; } OperationParameters* AlgorithmNodesInsideBorder::getParameters() { OperationParameters* ret = new OperationParameters(); ret->addSurfaceParameter(1, "surface", "the surface the borders are drawn on"); ret->addBorderParameter(2, "border-file", "the border file"); ret->addMetricOutputParameter(3, "metric-out", "the output metric file"); OptionalParameter* borderOpt = ret->createOptionalParameter(4, "-border", "create ROI for only one border"); borderOpt->addStringParameter(1, "name", "the name of the border"); ret->createOptionalParameter(5, "-inverse", "use inverse selection (outside border)"); ret->setHelpText( AString("By default, draws ROIs inside all borders in the border file, as separate metric columns.") ); return ret; } void AlgorithmNodesInsideBorder::useParameters(OperationParameters* myParams, ProgressObject* myProgObj) { LevelProgress myProgress(myProgObj); SurfaceFile* mySurf = myParams->getSurface(1); BorderFile* myBorderFile = myParams->getBorder(2); MetricFile* myMetricOut = myParams->getOutputMetric(3); OptionalParameter* borderOpt = myParams->getOptionalParameter(4); bool inverse = myParams->getOptionalParameter(5)->m_present; int numNodes = mySurf->getNumberOfNodes(); if (mySurf->getStructure() == StructureEnum::INVALID) throw AlgorithmException("surface file needs a valid structure to find the correct borders in the file"); if (myBorderFile->getNumberOfNodes() > 0)//-1 number of nodes is the signal that this is an old-format, potentially multistructure border file { if (myBorderFile->getStructure() != mySurf->getStructure()) throw AlgorithmException("border file and surface file have different structures"); if (myBorderFile->getNumberOfNodes() != numNodes) throw AlgorithmException("border file and surface file have different number of vertices"); } if (borderOpt->m_present) { AString findName = borderOpt->getString(1); int numBorders = myBorderFile->getNumberOfBorders(); vector scratchCol(numNodes, (inverse ? 1.0f : 0.0f)); bool found = false; for (int i = 0; i < numBorders; ++i) { const Border* thisBorder = myBorderFile->getBorder(i); if (thisBorder->getName() == findName && thisBorder->getStructure() == mySurf->getStructure()) { found = true;//don't use inverse in this call, because we need to merge results, and having them inverted first would just make things more confusing vector insideNodes = findNodesInsideBorder(mySurf, thisBorder, false); for (int index = 0; index < (int)insideNodes.size(); ++index) { scratchCol[insideNodes[index]] = (inverse ? 0.0f : 1.0f); } } } if (!found) throw AlgorithmException("border name not found for surface structure"); myMetricOut->setNumberOfNodesAndColumns(mySurf->getNumberOfNodes(), 1); myMetricOut->setStructure(mySurf->getStructure()); myMetricOut->setColumnName(0, findName); myMetricOut->setValuesForColumn(0, scratchCol.data()); } else { int numBorderParts = myBorderFile->getNumberOfBorders(); map, int> borderCounter;//for grouping border parts with this structure into borders, with indexes into borderPartGroups vector > borderPartGroups;//stores the border parts of borders, in order of first part encountered in file for (int i = 0; i < numBorderParts; ++i) { const Border* thisBorder = myBorderFile->getBorder(i); if (thisBorder->getStructure() == mySurf->getStructure()) { map, int>::iterator iter = borderCounter.find(make_pair(thisBorder->getName(), thisBorder->getClassName())); if (iter == borderCounter.end()) { borderCounter[make_pair(thisBorder->getName(), thisBorder->getClassName())] = (int)borderPartGroups.size(); borderPartGroups.push_back(vector(1, i)); } else { borderPartGroups[iter->second].push_back(i); } } } int numBorders = (int)borderPartGroups.size(); if (numBorders == 0) throw AlgorithmException("no borders match the structure of the surface"); myMetricOut->setNumberOfNodesAndColumns(numNodes, numBorders); myMetricOut->setStructure(mySurf->getStructure()); #pragma omp CARET_PARFOR schedule(dynamic) for (int i = 0; i < numBorders; ++i)//parallel mainly because the method used to be inefficient (repeatedly uses geodesic to full surface) { myMetricOut->setColumnName(i, myBorderFile->getBorder(borderPartGroups[i][0])->getName());//should be safe to do in parallel vector scratchCol(numNodes, (inverse ? 1.0f : 0.0f)); int numParts = (int)borderPartGroups[i].size(); for (int j = 0; j < numParts; ++j) { const Border* thisBorder = myBorderFile->getBorder(borderPartGroups[i][j]); vector insideNodes = findNodesInsideBorder(mySurf, thisBorder, false); for (int index = 0; index < (int)insideNodes.size(); ++index) { scratchCol[insideNodes[index]] = (inverse ? 0.0f : 1.0f); } } myMetricOut->setValuesForColumn(i, scratchCol.data());//ditto } } } /** * \class caret::AlgorithmNodesInsideBorder * \brief Assign attribute values to nodes within a closed border. * * Identify nodes within a closed border and assign scalar or * label values to those nodes. * * Identifying nodes within a border can be troublesome for several reasons. * (1) Some borders are very narrow and opposite sides of a border may * be adjacent (like a figure eight) such that there are multiple closed * regions. (2) Borders may self intersect. (3) One end of a border may * overlay the other end of the border. (4) Combinations of 1, 2, and/or 3. * * The original implementation of this algorithm assumed the border was * oriented in a counter-clockwise orientation: * (1) Identified a node to the left (inside) the border and filled * the region using the border as a boundary. * (2) If the number of selected nodes was greater than half then number * of nodes in the surface, it indicated that the border must have been * a clockwise orientation and the node selection was inverted. * * Corrections were made for a couple of the problems listed above but * when combinations of the problems were present the algorithm failed. * * Since finding nodes inside a border was too challenging, it was realized * that finding nodes outside a border was much easier. So, the algorithm * was re-written to identify nodes outside the border and then the selection * is inverted. * (1) Find the center of gravity (average coordinate) of the border. * (2) Find the surface node furthest from the border's center of gravity. * (3) Perform a connected fill operation but stopping anytime a border * node is encountered. * (4) Invert the selection (verifying that the selected number of nodes * is greater than half the number of nodes in the surface). */ /** * Constructor. * * @param myProgObj * * @param surfaceFile * Surface file for nodes inside border. * @param border * Border for which nodes inside are found. * @param isInverseSelection * Invert the selection. * @param assignToMetricMapIndex * Map index in metric file to which assigments are made for * nodes inside the border. * @param assignMetricValue * Metric value assigned to nodes within the border. * @param metricFileInOut * Metric file that has map set with nodes inside border. */ AlgorithmNodesInsideBorder::AlgorithmNodesInsideBorder(ProgressObject* myProgObj, const SurfaceFile* surfaceFile, const Border* border, const bool isInverseSelection, const int32_t assignToMetricMapIndex, const float assignMetricValue, MetricFile* metricFileInOut) : AbstractAlgorithm(myProgObj) { CaretAssert(surfaceFile); CaretAssert(border); CaretAssert(metricFileInOut); if (surfaceFile->getNumberOfNodes() != metricFileInOut->getNumberOfNodes()) throw AlgorithmException("metric file must be initialized to same number of nodes");//a method that requires this really shouldn't be public std::vector nodesInsideBorder = findNodesInsideBorder(surfaceFile, border, isInverseSelection); const int32_t numberOfNodesInsideBorder = static_cast(nodesInsideBorder.size()); for (int32_t i = 0; i < numberOfNodesInsideBorder; i++) { const int32_t nodeNumber = nodesInsideBorder[i]; metricFileInOut->setValue(nodeNumber, assignToMetricMapIndex, assignMetricValue); } } /** * Constructor. * * @param myProgObj * * @param surfaceFile * Surface file for nodes inside border. * @param border * Border for which nodes inside are found. * @param isInverseSelection * Invert the selection. * @param assignToLabelMapIndex * Map index in label file to which assigments are made for * nodes inside the border. * @param assignLabelKey * Label key assigned to nodes within the border. * @param labelFileInOut * Label file that has map set with nodes inside border. */ AlgorithmNodesInsideBorder::AlgorithmNodesInsideBorder(ProgressObject* myProgObj, const SurfaceFile* surfaceFile, const Border* border, const bool isInverseSelection, const int32_t assignToLabelMapIndex, const int32_t assignLabelKey, LabelFile* labelFileInOut) : AbstractAlgorithm(myProgObj) { CaretAssert(surfaceFile); CaretAssert(border); CaretAssert(labelFileInOut); if (surfaceFile->getNumberOfNodes() != labelFileInOut->getNumberOfNodes()) throw AlgorithmException("metric file must be initialized to same number of nodes");//a method that requires this really shouldn't be public std::vector nodesInsideBorder = findNodesInsideBorder(surfaceFile, border, isInverseSelection); const int32_t numberOfNodesInsideBorder = static_cast(nodesInsideBorder.size()); for (int32_t i = 0; i < numberOfNodesInsideBorder; i++) { const int32_t nodeNumber = nodesInsideBorder[i]; labelFileInOut->setLabelKey(nodeNumber, assignToLabelMapIndex, assignLabelKey); } } /** * Constructor. * * @param myProgObj * * @param surfaceFile * Surface file for nodes inside border. * @param border * Border for which nodes inside are found. * @param isInverseSelection * Invert the selection. * @param assignToCiftiScalarMapIndex * Map index in cifti scalar file to which assigments are made for * nodes inside the border. * @param assignScalarValue * Scalar value assigned to nodes within the border. * @param ciftiScalarFileInOut * CIFTI scalar file that has map set with nodes inside border. */ AlgorithmNodesInsideBorder::AlgorithmNodesInsideBorder(ProgressObject* myProgObj, const SurfaceFile* surfaceFile, const Border* border, const bool isInverseSelection, const int32_t assignToCiftiScalarMapIndex, const float assignScalarValue, CiftiBrainordinateScalarFile* ciftiScalarFileInOut) : AbstractAlgorithm(myProgObj) { CaretAssert(surfaceFile); CaretAssert(border); CaretAssert(ciftiScalarFileInOut); std::vector nodesInsideBorder = findNodesInsideBorder(surfaceFile, border, isInverseSelection); std::vector surfaceMapData(surfaceFile->getNumberOfNodes(), 0.0); const int32_t numberOfNodesInsideBorder = static_cast(nodesInsideBorder.size()); for (int32_t i = 0; i < numberOfNodesInsideBorder; i++) { const int32_t nodeIndex = nodesInsideBorder[i]; CaretAssertVectorIndex(surfaceMapData, nodeIndex); surfaceMapData[nodeIndex] = assignScalarValue; } ciftiScalarFileInOut->setMapDataForSurface(assignToCiftiScalarMapIndex, surfaceFile->getStructure(), surfaceMapData); } /** * Constructor. * * @param myProgObj * * @param surfaceFile * Surface file for nodes inside border. * @param border * Border for which nodes inside are found. * @param isInverseSelection * Invert the selection. * @param assignToCiftiScalarMapIndex * Map index in cifti scalar file to which assigments are made for * nodes inside the border. * @param assignLabelKey * Label key assigned to nodes within the border. * @param ciftiLabelFileInOut * CIFTI label file that has map set with nodes inside border. */ AlgorithmNodesInsideBorder::AlgorithmNodesInsideBorder(ProgressObject* myProgObj, const SurfaceFile* surfaceFile, const Border* border, const bool isInverseSelection, const int32_t assignToCiftiLabelMapIndex, const int32_t assignLabelKey, CiftiBrainordinateLabelFile* ciftiLabelFileInOut) : AbstractAlgorithm(myProgObj) { CaretAssert(surfaceFile); CaretAssert(border); CaretAssert(ciftiLabelFileInOut); std::vector nodesInsideBorder = this->findNodesInsideBorder(surfaceFile, border, isInverseSelection); std::vector surfaceMapData(surfaceFile->getNumberOfNodes(), 0.0); const int32_t numberOfNodesInsideBorder = static_cast(nodesInsideBorder.size()); for (int32_t i = 0; i < numberOfNodesInsideBorder; i++) { const int32_t nodeIndex = nodesInsideBorder[i]; CaretAssertVectorIndex(surfaceMapData, nodeIndex); surfaceMapData[nodeIndex] = assignLabelKey; } ciftiLabelFileInOut->setMapDataForSurface(assignToCiftiLabelMapIndex, surfaceFile->getStructure(), surfaceMapData); } /** * Constructor for getting a vector containing indices of nodes inside * the border. * * @param myProgObj * * @param surfaceFile * Surface file for nodes inside border. * @param border * Border for which nodes inside are found. * @param isInverseSelection * Invert the selection. * @param assignToCiftiScalarMapIndex * Map index in cifti scalar file to which assigments are made for * nodes inside the border. * @param assignLabelKey * Label key assigned to nodes within the border. * @param ciftiLabelFileInOut * CIFTI label file that has map set with nodes inside border. */ AlgorithmNodesInsideBorder::AlgorithmNodesInsideBorder(ProgressObject* myProgObj, const SurfaceFile* surfaceFile, const Border* border, const bool isInverseSelection, std::vector& nodesInsideBorderOut) : AbstractAlgorithm(myProgObj) { CaretAssert(surfaceFile); CaretAssert(border); nodesInsideBorderOut = this->findNodesInsideBorder(surfaceFile, border, isInverseSelection); } std::vector AlgorithmNodesInsideBorder::findNodesInsideBorder(const SurfaceFile* mySurf, const Border* myBorder, const bool& inverse) { std::vector nodesInsideBorderOut; if (myBorder->getNumberOfPoints() == 0) return nodesInsideBorderOut;//we could throw, but whatever /* * Move border points to the nearest nodes. */ std::vector unconnectedNodesPath = moveBorderPointsToNearestNodes(mySurf, myBorder); /* * Convert the unconnected nodes path into a connected nodes path */ std::vector connectedNodesPath = createConnectedNodesPath(mySurf, myBorder, unconnectedNodesPath); int32_t numberOfNodesInConnectedPath = static_cast(connectedNodesPath.size()); if (numberOfNodesInConnectedPath < 1) { throw AlgorithmException("Connected path contains no vertices."); } /* * Find nodes that are OUTSIDE the connected path */ std::vector nodesOutsidePath = findNodesOutsideOfConnectedPath(mySurf, connectedNodesPath); /* * Identify nodes INSIDE the connected path */ const int32_t numberOfSurfaceNodes = mySurf->getNumberOfNodes(); std::vector nodeInsideRegionFlags(numberOfSurfaceNodes, true); const int32_t numberOfNodesOutsidePath = static_cast(nodesOutsidePath.size()); for (int32_t i = 0; i < numberOfNodesOutsidePath; i++) { const int32_t nodeIndex = nodesOutsidePath[i]; CaretAssertVectorIndex(nodeInsideRegionFlags, nodeIndex); nodeInsideRegionFlags[nodeIndex] = false; } /* * Handle inverse selection or possibility outside selection was * accidentally inside selection */ bool doInverseFlag = inverse; const int32_t halfNumberOfSurfaceNodes = numberOfSurfaceNodes / 2; if (numberOfNodesOutsidePath < halfNumberOfSurfaceNodes) { doInverseFlag = ( ! doInverseFlag); } const CaretPointer th = mySurf->getTopologyHelper(); /* * Identify nodes */ for (int32_t i = 0; i < numberOfSurfaceNodes; i++) { bool insideFlag = nodeInsideRegionFlags[i]; if (doInverseFlag) { insideFlag = ( ! insideFlag); } if (th->getNodeHasNeighbors(i)) { if (std::find(connectedNodesPath.begin(), connectedNodesPath.end(), i) != connectedNodesPath.end()) { insideFlag = false; } } else { insideFlag = false; } if (insideFlag) { nodesInsideBorderOut.push_back(i); } } return nodesInsideBorderOut; } std::vector AlgorithmNodesInsideBorder::moveBorderPointsToNearestNodes(const SurfaceFile* mySurf, const Border* myBorder) { std::vector nodeIndicesFollowingBorder; /* * Move border points to the nearest nodes. */ const int32_t originalNumberOfBorderPoints = myBorder->getNumberOfPoints(); for (int32_t i = 0; i < originalNumberOfBorderPoints; i++) { const SurfaceProjectedItem* spi = myBorder->getPoint(i); float xyz[3]; if (spi->getProjectedPosition(*mySurf, xyz, true)) { const int32_t nearestNode = mySurf->closestNode(xyz); if (nearestNode >= 0) { nodeIndicesFollowingBorder.push_back(nearestNode); } } } CaretAssert(myBorder->getNumberOfPoints() == (int)nodeIndicesFollowingBorder.size());//required for the connecting code to use following line segment logic return nodeIndicesFollowingBorder; } std::vector AlgorithmNodesInsideBorder::createConnectedNodesPath(const SurfaceFile* mySurf, const Border* myBorder, const std::vector& unconnectedNodesPath) { std::vector connectedNodesPathOut; /* * Geodesic helper for surface */ CaretPointer geodesicHelper = mySurf->getGeodesicHelper(); /* * Get the topology helper for the surface */ CaretPointer th = mySurf->getTopologyHelper(); /* * Find connected path along node neighbors */ const int32_t numberOfNodesInUnconnectedPath = static_cast(unconnectedNodesPath.size()); for (int32_t i = 0; i < numberOfNodesInUnconnectedPath; i++) { const int node = unconnectedNodesPath[i]; connectedNodesPathOut.push_back(node); int nextIndex = -1; const bool lastNodeFlag = (i >= (numberOfNodesInUnconnectedPath - 1)); if (lastNodeFlag) { nextIndex = 0; } else { nextIndex = i + 1; } int nextNode = unconnectedNodesPath[nextIndex]; /*if (node != nextNode) { bool doGeodesicSearch = true; const std::vector neighbors = th->getNodeNeighbors(node); if (std::find(neighbors.begin(), neighbors.end(), nextNode) != neighbors.end()) { connectedNodesPathOut.push_back(nextNode); doGeodesicSearch = false; } if (doGeodesicSearch) { std::vector distances; std::vector parentNodes; geodesicHelper->getGeoFromNode(node, distances, parentNodes, false); bool doneFlag = false; std::vector pathFromNextNodeToNode; int32_t pathNode = nextNode; while (doneFlag == false) { int32_t nextPathNode = parentNodes[pathNode]; if (nextPathNode >= 0) { if (nextPathNode == node) { doneFlag = true; } else { pathFromNextNodeToNode.push_back(nextPathNode); } } else { doneFlag = true; } if (doneFlag == false) { pathNode = nextPathNode; } } const int32_t numberOfPathNodes = static_cast(pathFromNextNodeToNode.size()); for (int32_t i = (numberOfPathNodes - 1); i >= 0; i--) { connectedNodesPathOut.push_back(pathFromNextNodeToNode[i]); } } }//*/ vector path; vector dists; Vector3D nodePos, nextPos; myBorder->getPoint(i)->getProjectedPosition(*mySurf, nodePos, true);//really means unproject myBorder->getPoint(nextIndex)->getProjectedPosition(*mySurf, nextPos, true); geodesicHelper->getPathAlongLineSegment(node, nextNode, nodePos, nextPos, path, dists); connectedNodesPathOut.insert(connectedNodesPathOut.end(), path.begin(), path.end()); } /* * Remove duplicates. */ cleanNodePath(connectedNodesPathOut); /* * Valid that the path nodes are connected. */ validateConnectedNodesPath(mySurf, connectedNodesPathOut); return connectedNodesPathOut; } /** * Find the nodes OUTSIDE the given connected path. * * @param connectedNodesPath * Connected path for which nodes inside are found. * @param nodesOutsidePathOut * Vector into which nodes OUTSIDE connected path are loaded. */ std::vector AlgorithmNodesInsideBorder::findNodesOutsideOfConnectedPath(const SurfaceFile* mySurf, const std::vector& connectedNodesPath) { std::vector nodesOutsidePathOut; /* * Track nodes that are found inside and/or have been visited. */ const int32_t numberOfNodes = mySurf->getNumberOfNodes(); std::vector nodeSearchStatus(numberOfNodes, 0); std::vector insideBorderFlag(numberOfNodes, false); /* * Mark all nodes in connected path as visited. */ const int32_t numberOfNodesInConnectedPath = static_cast(connectedNodesPath.size()); for (int32_t i = 0; i < numberOfNodesInConnectedPath; i++) { CaretAssertVectorIndex(nodeSearchStatus, connectedNodesPath[i]); nodeSearchStatus[connectedNodesPath[i]] = 1; } /* * Get the topology helper for the surface with neighbors sorted. */ CaretPointer th = mySurf->getTopologyHelper(true); /* * Find the node that is furthest from the connected path's * center of gravity (average coordinate) */ int32_t startNode = findNodeFurthestFromConnectedPathCenterOfGravity(mySurf, connectedNodesPath, nodeSearchStatus); if (startNode < 0) { throw AlgorithmException("Failed to find vertex that is not inside of the connected path."); } /* * Mark the starting node as 'inside'. */ CaretAssertVectorIndex(insideBorderFlag, startNode); insideBorderFlag[startNode] = true; /* * Use a stack to help with search. */ std::stack stack; stack.push(startNode); /* * Search until no more to search. */ while (stack.empty() == false) { const int32_t nodeNumber = stack.top(); stack.pop(); /* * Has node been visited? */ CaretAssertVectorIndex(nodeSearchStatus, nodeNumber); if (nodeSearchStatus[nodeNumber] == 0) { nodeSearchStatus[nodeNumber] = 1; /* * Set node as inside */ CaretAssertVectorIndex(insideBorderFlag, nodeNumber); insideBorderFlag[nodeNumber] = true; /* * Get neighbors of this node */ int numNeighbors = 0; const int* neighbors = th->getNodeNeighbors(nodeNumber, numNeighbors); /* * add neighbors to search */ for (int i = 0; i < numNeighbors; i++) { const int neighborNode = neighbors[i]; CaretAssertVectorIndex(nodeSearchStatus, neighborNode); if (nodeSearchStatus[neighborNode] != 1) { stack.push(neighborNode); } } } } /* * Return nodes inside the path */ int32_t insideCount = 0; for (int32_t i = 0; i < numberOfNodes; i++) { CaretAssertVectorIndex(insideBorderFlag, i); if (insideBorderFlag[i]) { nodesOutsidePathOut.push_back(i); insideCount++; } } return nodesOutsidePathOut; } /** * Find an unvisited node that is FURTHEST from the center-of-gravity of * the the nodes in the connected path. * * @param connectedNodesPath * Path of connected nodes. * @param nodeSearchStatus * Search status of the nodes. */ int32_t AlgorithmNodesInsideBorder::findNodeFurthestFromConnectedPathCenterOfGravity(const SurfaceFile* mySurf, const std::vector& connectedNodesPath, std::vector& nodeSearchStatus) { double sumX = 0.0; double sumY = 0.0; double sumZ = 0.0; double sumCount = 0.0; const int32_t numberOfNodesInConnectedPath = static_cast(connectedNodesPath.size()); for (int32_t i = 1; i < (numberOfNodesInConnectedPath - 1); i++) { const float* xyz = mySurf->getCoordinate(connectedNodesPath[i]); sumX += xyz[0]; sumY += xyz[1]; sumZ += xyz[2]; sumCount++; } if (sumCount >= 1.0) { const float cog[3] = { sumX / sumCount, sumY / sumCount, sumZ / sumCount }; /* * Get the topology helper for the surface with neighbors sorted. */ CaretPointer topologyHelper = mySurf->getTopologyHelper(true); float maxDist = -1.0; int32_t maxDistNodeIndex = -1; const int32_t numberOfNodes = mySurf->getNumberOfNodes(); for (int32_t i = 0; i < numberOfNodes; i++) { if (nodeSearchStatus[i] == 0) { if (topologyHelper->getNodeHasNeighbors(i)) { const float* coordXYZ = mySurf->getCoordinate(i); const float distSQ = MathFunctions::distanceSquared3D(cog, coordXYZ); if (distSQ > maxDist) { maxDist = distSQ; maxDistNodeIndex = i; } } } } return maxDistNodeIndex; } return -1; } /** * Clean the path by removing any consecutive nodes that are identical. * * @param nodePath * Path that is cleaned. */ void AlgorithmNodesInsideBorder::cleanNodePath(std::vector& nodePath) { std::vector path = nodePath; nodePath.clear(); /* * Unique copy will remove consecutive identical elements */ std::unique_copy(path.begin(), path.end(), back_inserter(nodePath)); } /** * Verify that the connect nodes path is fully connected. * * @param connectedNodesPath * Path that is validated. */ void AlgorithmNodesInsideBorder::validateConnectedNodesPath(const SurfaceFile* mySurf, const std::vector& connectedNodesPath) { /* * Get the topology helper for the surface with neighbors sorted. */ CaretPointer th = mySurf->getTopologyHelper(false); /* * Check the path to see that each pair of nodes are connected. */ const int32_t numberOfNodesInPath = static_cast(connectedNodesPath.size()); for (int32_t i = 0; i < numberOfNodesInPath; i++) { int numNeighbors; const int node = connectedNodesPath[i]; const int* neighbors = th->getNodeNeighbors(node, numNeighbors); int32_t nextNode = -1; if (i >= (numberOfNodesInPath - 1)) { nextNode = connectedNodesPath[0]; } else { nextNode = connectedNodesPath[i + 1]; } if (node != nextNode) { bool foundIt = false; for (int j = 0; j < numNeighbors; j++) { if (neighbors[j] == nextNode) { foundIt = true; break; } } if (foundIt == false) { throw AlgorithmException("Validation of vertex path along border failed. Vertex " + AString::number(node) + " should be connected to " + AString::number(nextNode) + " but it is not."); } } } } connectome-workbench-1.2.3+git41-gc4c6c90/src/Algorithms/AlgorithmNodesInsideBorder.h000066400000000000000000000120311300200146000302340ustar00rootroot00000000000000#ifndef __ALGORITHM_NODES_INSIDE_BORDER__H_ #define __ALGORITHM_NODES_INSIDE_BORDER__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 "AbstractAlgorithm.h" namespace caret { class Border; class BorderFile; class CiftiBrainordinateLabelFile; class CiftiBrainordinateScalarFile; class MetricFile; class LabelFile; class SurfaceFile; class TopologyHelper; class AlgorithmNodesInsideBorder : public AbstractAlgorithm { public: AlgorithmNodesInsideBorder(ProgressObject* myProgObj, const SurfaceFile* surfaceFile, const Border* border, const bool isInverseSelection, const int32_t assignToMetricMapIndex, const float assignMetricValue, MetricFile* metricFileInOut); AlgorithmNodesInsideBorder(ProgressObject* myProgObj, const SurfaceFile* surfaceFile, const Border* border, const bool isInverseSelection, const int32_t assignToCiftiScalarMapIndex, const float assignScalarValue, CiftiBrainordinateScalarFile* ciftiScalarFileInOut); AlgorithmNodesInsideBorder(ProgressObject* myProgObj, const SurfaceFile* surfaceFile, const Border* border, const bool isInverseSelection, const int32_t assignToCiftiLabelMapIndex, const int32_t assignLabelKey, CiftiBrainordinateLabelFile* ciftiLabelFileInOut); AlgorithmNodesInsideBorder(ProgressObject* myProgObj, const SurfaceFile* surfaceFile, const Border* border, const bool isInverseSelection, const int32_t assignToLabelMapIndex, const int32_t assignLabelKey, LabelFile* labelFileInOut); AlgorithmNodesInsideBorder(ProgressObject* myProgObj, const SurfaceFile* surfaceFile, const Border* border, const bool isInverseSelection, std::vector& nodesInsideBorderOut); static OperationParameters* getParameters(); static void useParameters(OperationParameters* myParams, ProgressObject* myProgObj); static AString getCommandSwitch(); static AString getShortDescription(); static const BorderFile* getDebugBorderFile() { return NULL; } private: static std::vector findNodesInsideBorder(const SurfaceFile* mySurf, const Border* myBorder, const bool& inverse); static std::vector findNodesOutsideOfConnectedPath(const SurfaceFile* mySurf, const std::vector& connectedNodesPath); static int32_t findNodeFurthestFromConnectedPathCenterOfGravity(const SurfaceFile* mySurf, const std::vector& connectedNodesPath, std::vector& nodeSearchStatus); static std::vector createConnectedNodesPath(const SurfaceFile* mySurf, const Border* myBorder, const std::vector& unconnectedNodesPath); static void cleanNodePath(std::vector& nodePath); static std::vector moveBorderPointsToNearestNodes(const SurfaceFile* mySurf, const Border* myBorder); static void validateConnectedNodesPath(const SurfaceFile* mySurf, const std::vector& connectedNodesPath); }; typedef TemplateAutoOperation AutoAlgorithmNodesInsideBorder; } // namespace #endif //__ALGORITHM_NODES_INSIDE_BORDER__H_ connectome-workbench-1.2.3+git41-gc4c6c90/src/Algorithms/AlgorithmSignedDistanceToSurface.cxx000066400000000000000000000121071300200146000317510ustar00rootroot00000000000000/*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 "AlgorithmSignedDistanceToSurface.h" #include "AlgorithmException.h" #include "CaretOMP.h" #include "MetricFile.h" #include "SurfaceFile.h" using namespace caret; using namespace std; AString AlgorithmSignedDistanceToSurface::getCommandSwitch() { return "-signed-distance-to-surface"; } AString AlgorithmSignedDistanceToSurface::getShortDescription() { return "COMPUTE SIGNED DISTANCE FROM ONE SURFACE TO ANOTHER"; } OperationParameters* AlgorithmSignedDistanceToSurface::getParameters() { OperationParameters* ret = new OperationParameters(); ret->addSurfaceParameter(1, "surface-comp", "the comparison surface to measure the signed distance on"); ret->addSurfaceParameter(2, "surface-ref", "the reference surface that defines the signed distance function"); ret->addMetricOutputParameter(3, "metric", "the output metric"); OptionalParameter* windingMethodOpt = ret->createOptionalParameter(4, "-winding", "winding method for point inside surface test"); windingMethodOpt->addStringParameter(1, "method", "name of the method (default EVEN_ODD)"); ret->setHelpText( AString("Compute the signed distance function of the reference surface at every vertex on the comparison surface. ") + "NOTE: this relation is NOT symmetric, the line from a vertex to the closest point on the 'ref' surface " + "(the one that defines the signed distance function) will only align with the normal of the 'ref' surface. Valid specifiers for winding methods are as follows:\n\n" + "EVEN_ODD (default)\nNEGATIVE\nNONZERO\nNORMALS\n\nThe NORMALS method uses the normals of triangles and edges, or the closest triangle hit by a ray from the point. " + "This method may be slightly faster, but is only reliable for a closed surface that does not cross through itself. All other methods count entry (positive) and " + "exit (negative) crossings of a vertical ray from the point, then counts as inside if the total is odd, negative, or nonzero, respectively." ); return ret; } void AlgorithmSignedDistanceToSurface::useParameters(OperationParameters* myParams, ProgressObject* myProgObj) { SurfaceFile* testSurf = myParams->getSurface(1); SurfaceFile* levelSetSurf = myParams->getSurface(2); MetricFile* myMetricOut = myParams->getOutputMetric(3); SignedDistanceHelper::WindingLogic myWinding = SignedDistanceHelper::EVEN_ODD; OptionalParameter* windingMethodOpt = myParams->getOptionalParameter(4); if (windingMethodOpt->m_present) { AString methodName = windingMethodOpt->getString(1); if (methodName == "EVEN_ODD") { myWinding = SignedDistanceHelper::EVEN_ODD; } else if (methodName == "NEGATIVE") { myWinding = SignedDistanceHelper::NEGATIVE; } else if (methodName == "NONZERO") { myWinding = SignedDistanceHelper::NONZERO; } else if (methodName == "NORMALS") { myWinding = SignedDistanceHelper::NORMALS; } else { throw AlgorithmException("unrecognized winding method"); } } AlgorithmSignedDistanceToSurface(myProgObj, testSurf, levelSetSurf, myMetricOut, myWinding); } AlgorithmSignedDistanceToSurface::AlgorithmSignedDistanceToSurface(ProgressObject* myProgObj, const SurfaceFile* testSurf, const SurfaceFile* levelSetSurf, MetricFile* myMetricOut, SignedDistanceHelper::WindingLogic myWinding) : AbstractAlgorithm(myProgObj) { LevelProgress myProgress(myProgObj); int numNodes = testSurf->getNumberOfNodes(); myMetricOut->setNumberOfNodesAndColumns(numNodes, 1); myMetricOut->setStructure(testSurf->getStructure()); #pragma omp CARET_PAR { CaretPointer myHelp = levelSetSurf->getSignedDistanceHelper(); #pragma omp CARET_FOR schedule(dynamic) for (int i = 0; i < numNodes; ++i) { myMetricOut->setValue(i, 0, myHelp->dist(testSurf->getCoordinate(i), myWinding)); } } } float AlgorithmSignedDistanceToSurface::getAlgorithmInternalWeight() { return 1.0f;//override this if needed, if the progress bar isn't smooth } float AlgorithmSignedDistanceToSurface::getSubAlgorithmWeight() { //return AlgorithmInsertNameHere::getAlgorithmWeight();//if you use a subalgorithm return 0.0f; } connectome-workbench-1.2.3+git41-gc4c6c90/src/Algorithms/AlgorithmSignedDistanceToSurface.h000066400000000000000000000035721300200146000314040ustar00rootroot00000000000000#ifndef __ALGORITHM_SIGNED_DISTANCE_TO_SURFACE_H__ #define __ALGORITHM_SIGNED_DISTANCE_TO_SURFACE_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 "AbstractAlgorithm.h" #include "SignedDistanceHelper.h" namespace caret { class AlgorithmSignedDistanceToSurface : public AbstractAlgorithm { AlgorithmSignedDistanceToSurface(); protected: static float getSubAlgorithmWeight(); static float getAlgorithmInternalWeight(); public: AlgorithmSignedDistanceToSurface(ProgressObject* myProgObj, const SurfaceFile* testSurf, const SurfaceFile* levelSetSurf, MetricFile* myMetricOut, SignedDistanceHelper::WindingLogic myWinding = SignedDistanceHelper::EVEN_ODD); static OperationParameters* getParameters(); static void useParameters(OperationParameters* myParams, ProgressObject* myProgObj); static AString getCommandSwitch(); static AString getShortDescription(); }; typedef TemplateAutoOperation AutoAlgorithmSignedDistanceToSurface; } #endif //__ALGORITHM_SIGNED_DISTANCE_TO_SURFACE_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Algorithms/AlgorithmSurfaceAffineRegression.cxx000066400000000000000000000107411300200146000320150ustar00rootroot00000000000000/*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 "AlgorithmSurfaceAffineRegression.h" #include "AlgorithmException.h" #include "AffineFile.h" #include "SurfaceFile.h" //in order to do rref on double #include "MatrixFunctions.h" using namespace caret; using namespace std; AString AlgorithmSurfaceAffineRegression::getCommandSwitch() { return "-surface-affine-regression"; } AString AlgorithmSurfaceAffineRegression::getShortDescription() { return "REGRESS THE AFFINE TRANSFORM BETWEEN SURFACES ON THE SAME MESH"; } OperationParameters* AlgorithmSurfaceAffineRegression::getParameters() { OperationParameters* ret = new OperationParameters(); ret->addSurfaceParameter(1, "source", "the surface to warp"); ret->addSurfaceParameter(2, "target", "the surface to match the coordinates of"); ret->addStringParameter(3, "affine-out", "output - the output affine file"); ret->setHelpText( AString("Use linear regression to compute an affine that minimizes the sum of squares of the coordinate differences between the target surface and the warped source surface. ") + "Note that this has a bias to shrink the surface that is being warped. " + "The output is written as a NIFTI 'world' matrix, see -convert-affine to convert it for use in other software." ); return ret; } void AlgorithmSurfaceAffineRegression::useParameters(OperationParameters* myParams, ProgressObject* myProgObj) { SurfaceFile* sourceSurf = myParams->getSurface(1); SurfaceFile* targetSurf = myParams->getSurface(2); AString affineOutName = myParams->getString(3); FloatMatrix affineMatOut; AlgorithmSurfaceAffineRegression(myProgObj, sourceSurf, targetSurf, affineMatOut); AffineFile affineOut; affineOut.setMatrix(affineMatOut); affineOut.writeWorld(affineOutName); } AlgorithmSurfaceAffineRegression::AlgorithmSurfaceAffineRegression(ProgressObject* myProgObj, const SurfaceFile* sourceSurf, const SurfaceFile* targetSurf, FloatMatrix& affineMatOut) : AbstractAlgorithm(myProgObj) { LevelProgress myProgress(myProgObj); if (!targetSurf->hasNodeCorrespondence(*sourceSurf)) throw AlgorithmException("input surfaces must have vertex correspondence"); affineMatOut = FloatMatrix::identity(4); vector > indep(4, vector(4, 0.0)), dep(4, vector(3, 0.0)); int numNodes = targetSurf->getNumberOfNodes(); int coordSize = 3 * numNodes; const float* targetData = targetSurf->getCoordinateData(); const float* sourceData = sourceSurf->getCoordinateData(); for (int i = 0; i < coordSize; i += 3) { const float* targetCoord = targetData + i; const float* sourceCoord = sourceData + i; for (int j = 0; j < 3; ++j)//compute X' * X and X' * Y { for (int k = 0; k < 3; ++k) { indep[j][k] += sourceCoord[j] * sourceCoord[k]; dep[k][j] += targetCoord[j] * sourceCoord[k]; } indep[j][3] += sourceCoord[j]; indep[3][j] += sourceCoord[j]; dep[3][j] += targetCoord[j]; } indep[3][3] += 1.0; } vector > rrefMat; MatrixFunctions::horizCat(indep, dep, rrefMat); MatrixFunctions::rref(rrefMat); for (int i = 0; i < 3; ++i) { for (int j = 0; j < 4; ++j) { affineMatOut[i][j] = rrefMat[j][4 + i]; } } } float AlgorithmSurfaceAffineRegression::getAlgorithmInternalWeight() { return 1.0f;//override this if needed, if the progress bar isn't smooth } float AlgorithmSurfaceAffineRegression::getSubAlgorithmWeight() { //return AlgorithmInsertNameHere::getAlgorithmWeight();//if you use a subalgorithm return 0.0f; } connectome-workbench-1.2.3+git41-gc4c6c90/src/Algorithms/AlgorithmSurfaceAffineRegression.h000066400000000000000000000034411300200146000314410ustar00rootroot00000000000000#ifndef __ALGORITHM_SURFACE_AFFINE_REGRESSION_H__ #define __ALGORITHM_SURFACE_AFFINE_REGRESSION_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 "AbstractAlgorithm.h" #include "FloatMatrix.h" namespace caret { class AlgorithmSurfaceAffineRegression : public AbstractAlgorithm { AlgorithmSurfaceAffineRegression(); protected: static float getSubAlgorithmWeight(); static float getAlgorithmInternalWeight(); public: AlgorithmSurfaceAffineRegression(ProgressObject* myProgObj, const SurfaceFile* sourceSurf, const SurfaceFile* targetSurf, FloatMatrix& affineMatOut); static OperationParameters* getParameters(); static void useParameters(OperationParameters* myParams, ProgressObject* myProgObj); static AString getCommandSwitch(); static AString getShortDescription(); }; typedef TemplateAutoOperation AutoAlgorithmSurfaceAffineRegression; } #endif //__ALGORITHM_SURFACE_AFFINE_REGRESSION_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Algorithms/AlgorithmSurfaceApplyAffine.cxx000066400000000000000000000102451300200146000307610ustar00rootroot00000000000000/*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 "AlgorithmSurfaceApplyAffine.h" #include "AlgorithmException.h" #include "AffineFile.h" #include "SurfaceFile.h" #include "Vector3D.h" using namespace caret; using namespace std; AString AlgorithmSurfaceApplyAffine::getCommandSwitch() { return "-surface-apply-affine"; } AString AlgorithmSurfaceApplyAffine::getShortDescription() { return "APPLY AFFINE TRANSFORM TO SURFACE FILE"; } OperationParameters* AlgorithmSurfaceApplyAffine::getParameters() { OperationParameters* ret = new OperationParameters(); ret->addSurfaceParameter(1, "in-surf", "the surface to transform"); ret->addStringParameter(2, "affine", "the affine file"); ret->addSurfaceOutputParameter(3, "out-surf", "the output transformed surface"); OptionalParameter* flirtOpt = ret->createOptionalParameter(4, "-flirt", "MUST be used if affine is a flirt affine"); flirtOpt->addStringParameter(1, "source-volume", "the source volume used when generating the affine"); flirtOpt->addStringParameter(2, "target-volume", "the target volume used when generating the affine"); ret->setHelpText( AString("For flirt matrices, you must use the -flirt option, because flirt matrices are not a complete description of the coordinate transform they represent. ") + "If the -flirt option is not present, the affine must be a nifti 'world' affine, which can be obtained with the -convert-affine command, or aff_conv from the 4dfp suite." ); return ret; } void AlgorithmSurfaceApplyAffine::useParameters(OperationParameters* myParams, ProgressObject* myProgObj) { SurfaceFile* mySurf = myParams->getSurface(1); AString affineName = myParams->getString(2); SurfaceFile* mySurfOut = myParams->getOutputSurface(3); OptionalParameter* flirtOpt = myParams->getOptionalParameter(4); AffineFile myAffine; if (flirtOpt->m_present) { myAffine.readFlirt(affineName, flirtOpt->getString(1), flirtOpt->getString(2)); } else { myAffine.readWorld(affineName); } AlgorithmSurfaceApplyAffine(myProgObj, mySurf, myAffine.getMatrix(), mySurfOut); } AlgorithmSurfaceApplyAffine::AlgorithmSurfaceApplyAffine(ProgressObject* myProgObj, const SurfaceFile* mySurf, const FloatMatrix& myMatrix, SurfaceFile* mySurfOut) : AbstractAlgorithm(myProgObj) { LevelProgress myProgress(myProgObj); *mySurfOut = *mySurf;//copy rather than initialize, don't currently have much in the way of modification functions Vector3D xvec, yvec, zvec, translate; myMatrix.getAffineVectors(xvec, yvec, zvec, translate); int numNodes = mySurf->getNumberOfNodes(); const float* coordData = mySurf->getCoordinateData(); vector coordsOut(numNodes * 3); for (int i = 0; i < numNodes; ++i) { int base = i * 3; Vector3D transformed = coordData[base] * xvec + coordData[base + 1] * yvec + coordData[base + 2] * zvec + translate; coordsOut[base] = transformed[0]; coordsOut[base + 1] = transformed[1]; coordsOut[base + 2] = transformed[2]; } mySurfOut->setCoordinates(coordsOut.data()); } float AlgorithmSurfaceApplyAffine::getAlgorithmInternalWeight() { return 1.0f;//override this if needed, if the progress bar isn't smooth } float AlgorithmSurfaceApplyAffine::getSubAlgorithmWeight() { //return AlgorithmInsertNameHere::getAlgorithmWeight();//if you use a subalgorithm return 0.0f; } connectome-workbench-1.2.3+git41-gc4c6c90/src/Algorithms/AlgorithmSurfaceApplyAffine.h000066400000000000000000000033601300200146000304060ustar00rootroot00000000000000#ifndef __ALGORITHM_SURFACE_APPLY_AFFINE_H__ #define __ALGORITHM_SURFACE_APPLY_AFFINE_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 "AbstractAlgorithm.h" #include "FloatMatrix.h" namespace caret { class AlgorithmSurfaceApplyAffine : public AbstractAlgorithm { AlgorithmSurfaceApplyAffine(); protected: static float getSubAlgorithmWeight(); static float getAlgorithmInternalWeight(); public: AlgorithmSurfaceApplyAffine(ProgressObject* myProgObj, const SurfaceFile* mySurf, const FloatMatrix& myMatrix, SurfaceFile* mySurfOut); static OperationParameters* getParameters(); static void useParameters(OperationParameters* myParams, ProgressObject* myProgObj); static AString getCommandSwitch(); static AString getShortDescription(); }; typedef TemplateAutoOperation AutoAlgorithmSurfaceApplyAffine; } #endif //__ALGORITHM_SURFACE_APPLY_AFFINE_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Algorithms/AlgorithmSurfaceApplyWarpfield.cxx000066400000000000000000000111741300200146000315100ustar00rootroot00000000000000/*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 "AlgorithmSurfaceApplyWarpfield.h" #include "AlgorithmException.h" #include "SurfaceFile.h" #include "Vector3D.h" #include "WarpfieldFile.h" using namespace caret; using namespace std; AString AlgorithmSurfaceApplyWarpfield::getCommandSwitch() { return "-surface-apply-warpfield"; } AString AlgorithmSurfaceApplyWarpfield::getShortDescription() { return "APPLY WARPFIELD TO SURFACE FILE"; } OperationParameters* AlgorithmSurfaceApplyWarpfield::getParameters() { OperationParameters* ret = new OperationParameters(); ret->addSurfaceParameter(1, "in-surf", "the surface to transform"); ret->addStringParameter(2, "warpfield", "the INVERSE warpfield"); ret->addSurfaceOutputParameter(3, "out-surf", "the output transformed surface"); OptionalParameter* fnirtOpt = ret->createOptionalParameter(4, "-fnirt", "MUST be used if using a fnirt warpfield"); fnirtOpt->addStringParameter(1, "forward-warp", "the forward warpfield"); ret->setHelpText( AString("NOTE: warping a surface requires the INVERSE of the warpfield used to warp the volume it lines up with. ") + "The header of the forward warp is needed by the -fnirt option in order to correctly interpret the displacements in the fnirt warpfield.\n\n" + "If the -fnirt option is not present, the warpfield must be a nifti 'world' warpfield, which can be obtained with the -convert-warpfield command." ); return ret; } void AlgorithmSurfaceApplyWarpfield::useParameters(OperationParameters* myParams, ProgressObject* myProgObj) { SurfaceFile* mySurf = myParams->getSurface(1); AString warpName = myParams->getString(2); SurfaceFile* mySurfOut = myParams->getOutputSurface(3); OptionalParameter* flirtOpt = myParams->getOptionalParameter(4); WarpfieldFile myWarp; if (flirtOpt->m_present) { myWarp.readFnirt(warpName, flirtOpt->getString(1)); } else { myWarp.readWorld(warpName); } AlgorithmSurfaceApplyWarpfield(myProgObj, mySurf, myWarp.getWarpfield(), mySurfOut); } AlgorithmSurfaceApplyWarpfield::AlgorithmSurfaceApplyWarpfield(ProgressObject* myProgObj, const SurfaceFile* mySurf, const VolumeFile* warpfield, SurfaceFile* mySurfOut) : AbstractAlgorithm(myProgObj) { LevelProgress myProgress(myProgObj); vector warpDims; warpfield->getDimensions(warpDims); if (warpDims[3] != 3 || warpDims[4] != 1) throw AlgorithmException("provided warpfield volume has wrong number of subvolumes or components"); *mySurfOut = *mySurf;//copy rather than initialize, don't currently have much in the way of modification functions int numNodes = mySurf->getNumberOfNodes(); const float* coordData = mySurf->getCoordinateData(); vector coordsOut(numNodes * 3); for (int i = 0; i < numNodes; ++i) { int base = i * 3; Vector3D sample = coordData + base; Vector3D displacement; bool valid = false; displacement[0] = warpfield->interpolateValue(sample, VolumeFile::TRILINEAR, &valid, 0); if (!valid) throw AlgorithmException("surface exceeds the bounding box of the warpfield"); displacement[1] = warpfield->interpolateValue(sample, VolumeFile::TRILINEAR, NULL, 1); displacement[2] = warpfield->interpolateValue(sample, VolumeFile::TRILINEAR, NULL, 2); Vector3D newCoord = sample + displacement; coordsOut[base] = newCoord[0]; coordsOut[base + 1] = newCoord[1]; coordsOut[base + 2] = newCoord[2]; } mySurfOut->setCoordinates(coordsOut.data()); } float AlgorithmSurfaceApplyWarpfield::getAlgorithmInternalWeight() { return 1.0f;//override this if needed, if the progress bar isn't smooth } float AlgorithmSurfaceApplyWarpfield::getSubAlgorithmWeight() { //return AlgorithmInsertNameHere::getAlgorithmWeight();//if you use a subalgorithm return 0.0f; } connectome-workbench-1.2.3+git41-gc4c6c90/src/Algorithms/AlgorithmSurfaceApplyWarpfield.h000066400000000000000000000033571300200146000311410ustar00rootroot00000000000000#ifndef __ALGORITHM_SURFACE_APPLY_WARPFIELD_H__ #define __ALGORITHM_SURFACE_APPLY_WARPFIELD_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 "AbstractAlgorithm.h" namespace caret { class AlgorithmSurfaceApplyWarpfield : public AbstractAlgorithm { AlgorithmSurfaceApplyWarpfield(); protected: static float getSubAlgorithmWeight(); static float getAlgorithmInternalWeight(); public: AlgorithmSurfaceApplyWarpfield(ProgressObject* myProgObj, const SurfaceFile* mySurf, const VolumeFile* warpfield, SurfaceFile* mySurfOut); static OperationParameters* getParameters(); static void useParameters(OperationParameters* myParams, ProgressObject* myProgObj); static AString getCommandSwitch(); static AString getShortDescription(); }; typedef TemplateAutoOperation AutoAlgorithmSurfaceApplyWarpfield; } #endif //__ALGORITHM_SURFACE_APPLY_WARPFIELD_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Algorithms/AlgorithmSurfaceAverage.cxx000066400000000000000000000231761300200146000301440ustar00rootroot00000000000000/*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 "AlgorithmSurfaceAverage.h" #include "AlgorithmException.h" #include "CaretAssert.h" #include "MetricFile.h" #include "SurfaceFile.h" #include "Vector3D.h" #include using namespace caret; using namespace std; AString AlgorithmSurfaceAverage::getCommandSwitch() { return "-surface-average"; } AString AlgorithmSurfaceAverage::getShortDescription() { return "AVERAGE SURFACE FILES TOGETHER"; } OperationParameters* AlgorithmSurfaceAverage::getParameters() { OperationParameters* ret = new OperationParameters(); ret->addSurfaceOutputParameter(1, "surface-out", "the output averaged surface"); ParameterComponent* surfOpt = ret->createRepeatableParameter(2, "-surf", "specify a surface to include in the average"); surfOpt->addSurfaceParameter(1, "surface", "a surface file to average"); OptionalParameter* surfWeightOpt = surfOpt->createOptionalParameter(2, "-weight", "specify a weighted average"); surfWeightOpt->addDoubleParameter(1, "weight", "the weight to use (default 1)"); OptionalParameter* stdevOpt = ret->createOptionalParameter(3, "-stddev", "compute 3D sample standard deviation"); stdevOpt->addMetricOutputParameter(1, "stddev-metric-out", "the output metric for 3D sample standard deviation"); OptionalParameter* uncertaintyOpt = ret->createOptionalParameter(4, "-uncertainty", "compute caret5 'uncertainty'"); uncertaintyOpt->addMetricOutputParameter(1, "uncert-metric-out", "the output metric for uncertainty"); ret->setHelpText( AString("The 3D sample standard deviation is computed as 'sqrt(sum(squaredlength(xyz - mean(xyz)))/(n - 1))'.\n\n") + "Uncertainty is a legacy measure used in caret5, and is computed as 'sum(length(xyz - mean(xyz)))/n'.\n\n" + "When weights are used, the 3D sample standard deviation treats them as reliability weights." ); return ret; } void AlgorithmSurfaceAverage::useParameters(OperationParameters* myParams, ProgressObject* myProgObj) { SurfaceFile* myAvgOut = myParams->getOutputSurface(1); vector inputSurfs; const vector& surfOpts = *(myParams->getRepeatableParameterInstances(2)); int numSurfs = (int)surfOpts.size(); vector surfWeights, *surfWeightPtr = NULL; for (int i = 0; i < numSurfs; ++i) { inputSurfs.push_back(surfOpts[i]->getSurface(1)); OptionalParameter* surfWeightOpt = surfOpts[i]->getOptionalParameter(2); if (surfWeightOpt->m_present) { if (surfWeightPtr == NULL) { surfWeights.resize(i, 1.0f); surfWeightPtr = &surfWeights; } surfWeights.push_back((float)surfWeightOpt->getDouble(1)); } else { if (surfWeightPtr != NULL) { surfWeights.push_back(1.0f); } } } MetricFile* stdevOut = NULL; OptionalParameter* stdevOpt = myParams->getOptionalParameter(3); if (stdevOpt->m_present) { stdevOut = stdevOpt->getOutputMetric(1); } MetricFile* uncertOut = NULL; OptionalParameter* uncertaintyOpt = myParams->getOptionalParameter(4); if (uncertaintyOpt->m_present) { uncertOut = uncertaintyOpt->getOutputMetric(1); } AlgorithmSurfaceAverage(myProgObj, myAvgOut, inputSurfs, stdevOut, uncertOut, surfWeightPtr); } AlgorithmSurfaceAverage::AlgorithmSurfaceAverage(ProgressObject* myProgObj, SurfaceFile* myAvgOut, const vector& inputSurfs, MetricFile* stdevOut, MetricFile* uncertOut, const vector* surfWeightPtr) : AbstractAlgorithm(myProgObj) { LevelProgress myProgress(myProgObj); int numSurfs = (int)inputSurfs.size(); if (numSurfs == 0) throw AlgorithmException("no input surfaces specified in AlgorithmSurfaceAverage"); if (surfWeightPtr != NULL) { CaretAssert((int)surfWeightPtr->size() == numSurfs); } int numNodes = inputSurfs[0]->getNumberOfNodes(); for (int i = 1; i < numSurfs; ++i) { if (!inputSurfs[i]->hasNodeCorrespondence(*(inputSurfs[0]))) throw AlgorithmException("surface '" + inputSurfs[i]->getFileName() + "' does not have node correspondence to surface '" + inputSurfs[0]->getFileName() + "'"); inputSurfs[i]->clearCachedHelpers(); } *myAvgOut = *(inputSurfs[0]); if (uncertOut != NULL) { uncertOut->setNumberOfNodesAndColumns(numNodes, 1); uncertOut->setStructure(inputSurfs[0]->getStructure()); uncertOut->setColumnName(0, "SHAPE_STANDARD_UNCERTAINTY"); } if (stdevOut != NULL) { stdevOut->setNumberOfNodesAndColumns(numNodes, 1); stdevOut->setStructure(inputSurfs[0]->getStructure()); stdevOut->setColumnName(0, "3D sample standard deviation"); } //loop across nodes first so that we can use doubles for accumulation easily for (int i = 0; i < numNodes; ++i) { double accumpoint[3] = {0.0, 0.0, 0.0}; Vector3D avgpoint; if (surfWeightPtr == NULL) { for (int j = 0; j < numSurfs; ++j) { Vector3D inpoint = inputSurfs[j]->getCoordinate(i); accumpoint[0] += inpoint[0]; accumpoint[1] += inpoint[1]; accumpoint[2] += inpoint[2]; } avgpoint[0] = accumpoint[0] / numSurfs; avgpoint[1] = accumpoint[1] / numSurfs; avgpoint[2] = accumpoint[2] / numSurfs; } else { double weightsum = 0.0; for (int j = 0; j < numSurfs; ++j) { Vector3D inpoint = inputSurfs[j]->getCoordinate(i); float thisweight = (*surfWeightPtr)[j]; weightsum += thisweight; accumpoint[0] += inpoint[0] * thisweight; accumpoint[1] += inpoint[1] * thisweight; accumpoint[2] += inpoint[2] * thisweight; } avgpoint[0] = accumpoint[0] / weightsum; avgpoint[1] = accumpoint[1] / weightsum; avgpoint[2] = accumpoint[2] / weightsum; } myAvgOut->setCoordinate(i, avgpoint); if (uncertOut != NULL || stdevOut != NULL) { if (numSurfs == 1) { if (uncertOut != NULL) { uncertOut->setValue(i, 0, 0.0f); } if (stdevOut != NULL) { stdevOut->setValue(i, 0, 0.0f); } } else { double distAccum = 0.0;//for caret5 uncertainty double dist2Accum = 0.0;//for sample stdev if (surfWeightPtr == NULL) { for (int j = 0; j < numSurfs; ++j) { Vector3D inpoint = inputSurfs[j]->getCoordinate(i); float dist2 = (avgpoint - inpoint).lengthsquared(); dist2Accum += dist2; distAccum += sqrt(dist2); } if (uncertOut != NULL) { uncertOut->setValue(i, 0, distAccum / numSurfs); } if (stdevOut != NULL) { stdevOut->setValue(i, 0, sqrt(dist2Accum / (numSurfs - 1))); } } else { double weightsum = 0.0, weight2sum = 0.0; for (int j = 0; j < numSurfs; ++j) { Vector3D inpoint = inputSurfs[j]->getCoordinate(i); float thisweight = (*surfWeightPtr)[j]; weightsum += thisweight; weight2sum += thisweight * thisweight; float dist2 = (avgpoint - inpoint).lengthsquared(); dist2Accum += dist2 * thisweight; distAccum += sqrt(dist2) * thisweight; } if (uncertOut != NULL) { uncertOut->setValue(i, 0, distAccum / weightsum); } if (stdevOut != NULL) { stdevOut->setValue(i, 0, sqrt(dist2Accum / (weightsum - weight2sum / weightsum)));//https://en.wikipedia.org/wiki/Weighted_arithmetic_mean#Weighted_sample_variance } } } } } } float AlgorithmSurfaceAverage::getAlgorithmInternalWeight() { return 1.0f;//override this if needed, if the progress bar isn't smooth } float AlgorithmSurfaceAverage::getSubAlgorithmWeight() { //return AlgorithmInsertNameHere::getAlgorithmWeight();//if you use a subalgorithm return 0.0f; } connectome-workbench-1.2.3+git41-gc4c6c90/src/Algorithms/AlgorithmSurfaceAverage.h000066400000000000000000000035141300200146000275630ustar00rootroot00000000000000#ifndef __ALGORITHM_SURFACE_AVERAGE_H__ #define __ALGORITHM_SURFACE_AVERAGE_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 "AbstractAlgorithm.h" #include namespace caret { class AlgorithmSurfaceAverage : public AbstractAlgorithm { AlgorithmSurfaceAverage(); protected: static float getSubAlgorithmWeight(); static float getAlgorithmInternalWeight(); public: AlgorithmSurfaceAverage(ProgressObject* myProgObj, SurfaceFile* myAvgOut, const std::vector& inputSurfs, MetricFile* stdevOut = NULL, MetricFile* uncertOut = NULL, const std::vector* surfWeightPtr = NULL); static OperationParameters* getParameters(); static void useParameters(OperationParameters* myParams, ProgressObject* myProgObj); static AString getCommandSwitch(); static AString getShortDescription(); }; typedef TemplateAutoOperation AutoAlgorithmSurfaceAverage; } #endif //__ALGORITHM_SURFACE_AVERAGE_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Algorithms/AlgorithmSurfaceCortexLayer.cxx000066400000000000000000000403361300200146000310300ustar00rootroot00000000000000/*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 "AlgorithmSurfaceCortexLayer.h" #include "AlgorithmException.h" #include "MathFunctions.h" #include "MetricFile.h" #include "SurfaceFile.h" #include "TopologyHelper.h" #include "Vector3D.h" #include using namespace caret; using namespace std; AString AlgorithmSurfaceCortexLayer::getCommandSwitch() { return "-surface-cortex-layer"; } AString AlgorithmSurfaceCortexLayer::getShortDescription() { return "CREATE SURFACE APPROXIMATING A CORTICAL LAYER"; } OperationParameters* AlgorithmSurfaceCortexLayer::getParameters() { OperationParameters* ret = new OperationParameters(); ret->addSurfaceParameter(1, "white-surface", "the white matter surface"); ret->addSurfaceParameter(2, "pial-surface", "the pial surface"); ret->addDoubleParameter(3, "location", "what volume fraction to place the layer at"); ret->addSurfaceOutputParameter(4, "out-surface", "the output surface"); 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->setHelpText( AString("The input surfaces must have vertex correspondence. ") + "The output surface is generated by placing vertices between the two surfaces such that the enclosed volume within any small patch of the new and white surfaces " + "is the given fraction of the volume of the same patch between the pial and white surfaces " + "(i.e., specifying 0 would give the white surface, 1 would give the pial surface). " ); return ret; } void AlgorithmSurfaceCortexLayer::useParameters(OperationParameters* myParams, ProgressObject* myProgObj) { SurfaceFile* myWhiteSurf = myParams->getSurface(1); SurfaceFile* myPialSurf = myParams->getSurface(2); float myVolFrac = myParams->getDouble(3); SurfaceFile* myOutSurf = myParams->getOutputSurface(4); MetricFile* myMetricOut = NULL; OptionalParameter* metricOpt = myParams->getOptionalParameter(5); if (metricOpt->m_present) { myMetricOut = metricOpt->getOutputMetric(1); } bool untwistMode = myParams->getOptionalParameter(6)->m_present; AlgorithmSurfaceCortexLayer(myProgObj, myWhiteSurf, myPialSurf, myVolFrac, myOutSurf, myMetricOut, untwistMode); } AlgorithmSurfaceCortexLayer::AlgorithmSurfaceCortexLayer(ProgressObject* myProgObj, const SurfaceFile* myWhiteSurf, const SurfaceFile* myPialSurf, const float& myVolFrac, SurfaceFile* myOutSurf, MetricFile* myMetricOut, const bool& untwistMode) : AbstractAlgorithm(myProgObj) { LevelProgress myProgress(myProgObj); int numNodes = myWhiteSurf->getNumberOfNodes(); if (!myWhiteSurf->hasNodeCorrespondence(*myPialSurf)) { throw AlgorithmException("input surfaces don't have node correspondence"); } *myOutSurf = *myWhiteSurf;//copy topology myOutSurf->setSecondaryType(SecondarySurfaceTypeEnum::MIDTHICKNESS);//??? if (myMetricOut != NULL) { myMetricOut->setNumberOfNodesAndColumns(numNodes, 1); myMetricOut->setStructure(myWhiteSurf->getStructure()); myMetricOut->setColumnName(0, "distance fraction"); } const float* whiteCoords = myWhiteSurf->getCoordinateData(); const float* pialCoords = myPialSurf->getCoordinateData(); bool outside = false; bool above = false; if (myVolFrac < 0 || myVolFrac > 1) { outside = true; if (myVolFrac > 1) above = true; } CaretPointer myTopoHelp = myWhiteSurf->getTopologyHelper();//we checked that the input surfaces have the same effective topology for (int i = 0; i < numNodes; ++i) { Vector3D whiteCenter = whiteCoords + i * 3; Vector3D pialCenter = pialCoords + i * 3; float distFrac = 0.0f; if (untwistMode) { float d1; Vector3D axisHat = (pialCenter - whiteCenter).normal(&d1); const vector& neighbors = myTopoHelp->getNodeNeighbors(i); int numNeigh = (int)neighbors.size(); for (int j = 0; j < numNeigh; ++j) { Vector3D whiteNeighbor = whiteCoords + neighbors[j] * 3; Vector3D pialNeighbor = pialCoords + neighbors[j] * 3; float R0 = axisHat.cross(whiteCenter - whiteNeighbor).length(); float RF = axisHat.cross(pialCenter - pialNeighbor).length(); float Rd = RF - R0; float h0 = axisHat.dot(whiteCenter - whiteNeighbor); float hF = axisHat.dot(pialCenter - pialNeighbor); float hd = hF - h0; float a = d1 * Rd * Rd;//coefficient for t^3 float b = Rd * (3 * d1 * R0 + h0 * Rd - hd * R0);//t^2 float c = R0 * (3 * d1 * R0 + 2 * h0 * Rd - 2 * hd * R0);//t float fullVol = c + b + a;//trivial to evaluate for t = 1, and t = 0 gives 0 float target = fullVol * myVolFrac; float lowt = 0, lowval = 0, hight = 1, highval = fullVol; if (outside) { if (above) { if (Rd / R0 < 0) { hight = min(-R0 / Rd, 1 + (myVolFrac - 1) * 3);//because conical volumes are 1/3 the equivalent cylinder } else { hight = myVolFrac;//because the longest result here is cylinder } highval = hight * (c + hight * (b + hight * a)); } else { if (Rd / R0 > 0) { lowt = max(-R0 / Rd, myVolFrac * 3);//ditto } else { lowt = myVolFrac; } lowval = lowt * (c + lowt * (b + lowt * a)); } }//these ranges SHOULD make the function monotonic const float TOLER = 0.0001f;//stop when high and low bounds are this close const int MAX_ITERS = 100;//but don't take too long trying to get there int iter = 0; while (abs(hight - lowt) > TOLER && iter < MAX_ITERS) { ++iter; float highdiff = (highval - target); float lowdiff = (target - lowval); float guess = (lowt * highdiff + hight * lowdiff) / (highdiff + lowdiff);//weighted average to get guess float highcap = (lowt + 9 * hight) / 10.0f;//make guess lie in the middle 80%, to make sure it always moves nontrivially float lowcap = (9 * lowt + hight) / 10.0f; if (guess < lowcap) guess = lowcap; if (guess > highcap) guess = highcap; float guessval = guess * (c + guess * (b + guess * a)); if ((guessval < target) == (lowval < highval))//in case positive t produces negative volume { lowt = guess; lowval = guessval; } else { hight = guess; highval = guessval; } } float highdiff = (highval - target);//one more iteration to get the value to add to accum float lowdiff = (target - lowval); float guess = (lowt * highdiff + hight * lowdiff) / (highdiff + lowdiff); float highcap = (lowt + 9 * hight) / 10.0f; float lowcap = (9 * lowt + hight) / 10.0f; if (guess < lowcap) guess = lowcap; if (guess > highcap) guess = highcap; distFrac += guess; } distFrac /= numNeigh; } else { float a = 0.0f, b = 0.0f, c = 0.0f;//constants for the cubic function that will give the volume const vector& myTiles = myTopoHelp->getNodeTiles(i); int numTiles = (int)myTiles.size(); for (int j = 0; j < numTiles; ++j) { const int32_t* whiteTile = myWhiteSurf->getTriangle(myTiles[j]); Vector3D whitePoints[3], pialPoints[3], layerDelta[3]; for (int k = 0; k < 3; ++k) { whitePoints[k] = whiteCoords + whiteTile[k] * 3; pialPoints[k] = pialCoords + whiteTile[k] * 3; layerDelta[k] = pialPoints[k] - whitePoints[k]; }//calculate volume with vector field trick: http://en.wikipedia.org/wiki/Polyhedron#Volume Vector3D refPoint = whitePoints[0];//use an "origin" for the vector field that is near the polyhedron for numerical stability - also, this makes several sides have zero contribution //NOTE: the white surface triangle has zero contribution due to choice of reference point, would otherwise contribute to both volumes //layerVol += (layerPoints[0] - refPoint).dot((layerPoints[1] - layerPoints[0]).cross(layerPoints[2] - layerPoints[1])); // (t * (pialPoints[0] - whitePoints[0])).dot((t * (pialPoints[1] - pialPoints[0] - (whitePoints[1] - whitePoints[0])) + whitePoints[1] - whitePoints[0]).cross(...) Vector3D edge10delta = pialPoints[1] - pialPoints[0] - whitePoints[1] + whitePoints[0]; Vector3D edge21delta = pialPoints[2] - pialPoints[1] - whitePoints[2] + whitePoints[1]; // (t * layerDelta[0]).dot((t * edge10delta + whitePoints[1] - whitePoints[0]).cross(t * edge21delta + whitePoints[2] - whitepoints[1])); a += layerDelta[0].dot(edge10delta.cross(edge21delta)); b += layerDelta[0].dot((whitePoints[1] - whitePoints[0]).cross(edge21delta) + edge10delta.cross(whitePoints[2] - whitePoints[1])); c += layerDelta[0].dot((whitePoints[1] - whitePoints[0]).cross(whitePoints[2] - whitePoints[1])); int m = 2; for (int k = 0; k < 3; ++k)//walk the side quadrilaterals as 2->0, 0->1, 1->2 {//add the average of the two triangulations of the quadrilateral Vector3D mref = whitePoints[m] - refPoint, kref = whitePoints[k] - refPoint;//precalculate reference points on the white surface for use in both volumes Vector3D whiteEdge = whitePoints[k] - whitePoints[m];//precalculate this frequently used edge also if (k != 0 && m != 0) //layerVol += 0.5f * mref.dot(whiteEdge.cross(layerPoints[k] - whitePoints[k])); { c += 0.5f * mref.dot(whiteEdge.cross(layerDelta[k])); } if (m != 0) //layerVol += 0.5f * mref.dot((layerPoints[m] - whitePoints[m]).cross(whitePoints[m] - layerPoints[k])); { // 0.5f * mref.dot((t * layerDelta[m]).cross(whitePoints[m] - (whitePoints[k] + t * layerDelta[k]))); b += 0.5f * mref.dot(layerDelta[m].cross(-layerDelta[k])); c += 0.5f * mref.dot(layerDelta[m].cross(-whiteEdge)); } if (k != 0 && m != 0) //layerVol += 0.5f * kref.dot(whiteEdge.cross(layerPoints[m] - whitePoints[k])); { // 0.5f * kref.dot(whiteEdge.cross(layerPoints[m] - whitePoints[m]));//use a formula where t = 0 gives a zero length edge c += 0.5f * kref.dot(whiteEdge.cross(layerDelta[m])); } if (k != 0) //layerVol += 0.5f * kref.dot((layerPoints[k] - whitePoints[k]).cross(layerPoints[m] - layerPoints[k])); { // 0.5f * kref.dot((t * layerDelta[k]).cross((whitePoints[m] + t * layerDelta[m]) - (whitePoints[k] + t * layerDelta[k]))); b += 0.5f * kref.dot(layerDelta[k].cross(layerDelta[m] - layerDelta[k])); c += 0.5f * kref.dot(layerDelta[k].cross(-whiteEdge)); } m = k; } } float fullVol = a + b + c;//trivial to evaluate for t = 1, and t = 0 gives 0 float target = fullVol * myVolFrac; float lowt = 0, lowval = 0, hight = 1, highval = fullVol; if (outside) { if (above) { hight = 1 + (myVolFrac - 1) * 3;//set a limit at 3 times further out than the requested fraction, and just hope that it is monotonic highval = hight * (c + hight * (b + hight * a)); } else { lowt = myVolFrac * 3;//ditto lowval = lowt * (c + lowt * (b + lowt * a)); } }//these ranges SHOULD make the function monotonic const float TOLER = 0.0001f;//stop when high and low bounds are this close const int MAX_ITERS = 100;//but don't take too long trying to get there int iter = 0; while (abs(hight - lowt) > TOLER && iter < MAX_ITERS) { ++iter; float highdiff = (highval - target); float lowdiff = (target - lowval); float guess = (lowt * highdiff + hight * lowdiff) / (highdiff + lowdiff);//weighted average to get guess float highcap = (lowt + 9 * hight) / 10.0f;//make guess lie in the middle 80%, to make sure it always moves nontrivially float lowcap = (9 * lowt + hight) / 10.0f; if (guess < lowcap) guess = lowcap; if (guess > highcap) guess = highcap; float guessval = guess * (c + guess * (b + guess * a)); if ((guessval < target) == (lowval < highval))//in case positive t produces negative volume { lowt = guess; lowval = guessval; } else { hight = guess; highval = guessval; } } float highdiff = (highval - target);//one more iteration to get the value to add to accum float lowdiff = (target - lowval); float guess = (lowt * highdiff + hight * lowdiff) / (highdiff + lowdiff); float highcap = (lowt + 9 * hight) / 10.0f; float lowcap = (9 * lowt + hight) / 10.0f; if (guess < lowcap) guess = lowcap; if (guess > highcap) guess = highcap; distFrac = guess; } if (!MathFunctions::isNumeric(distFrac)) { distFrac = 0.5f;//non-numeric should only happen when pial and white coords are identical, so average should be fine } myOutSurf->setCoordinate(i, distFrac * (pialCenter - whiteCenter) + whiteCenter); if (myMetricOut != NULL) { myMetricOut->setValue(i, 0, distFrac); } } } float AlgorithmSurfaceCortexLayer::getAlgorithmInternalWeight() { return 1.0f;//override this if needed, if the progress bar isn't smooth } float AlgorithmSurfaceCortexLayer::getSubAlgorithmWeight() { //return AlgorithmInsertNameHere::getAlgorithmWeight();//if you use a subalgorithm return 0.0f; } connectome-workbench-1.2.3+git41-gc4c6c90/src/Algorithms/AlgorithmSurfaceCortexLayer.h000066400000000000000000000035331300200146000304530ustar00rootroot00000000000000#ifndef __ALGORITHM_SURFACE_CORTEX_LAYER_H__ #define __ALGORITHM_SURFACE_CORTEX_LAYER_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 "AbstractAlgorithm.h" namespace caret { class AlgorithmSurfaceCortexLayer : public AbstractAlgorithm { AlgorithmSurfaceCortexLayer(); protected: static float getSubAlgorithmWeight(); static float getAlgorithmInternalWeight(); public: AlgorithmSurfaceCortexLayer(ProgressObject* myProgObj, const SurfaceFile* myWhiteSurf, const SurfaceFile* myPialSurf, const float& myVolFrac, SurfaceFile* myOutSurf, MetricFile* myMetricOut = NULL, const bool& untwistMode = false); static OperationParameters* getParameters(); static void useParameters(OperationParameters* myParams, ProgressObject* myProgObj); static AString getCommandSwitch(); static AString getShortDescription(); }; typedef TemplateAutoOperation AutoAlgorithmSurfaceCortexLayer; } #endif //__ALGORITHM_SURFACE_CORTEX_LAYER_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Algorithms/AlgorithmSurfaceCreateSphere.cxx000066400000000000000000000301421300200146000311330ustar00rootroot00000000000000/*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 "AlgorithmSurfaceCreateSphere.h" #include "AlgorithmException.h" #include "CaretLogger.h" #include "SurfaceFile.h" #include "Vector3D.h" #include using namespace caret; using namespace std; AString AlgorithmSurfaceCreateSphere::getCommandSwitch() { return "-surface-create-sphere"; } AString AlgorithmSurfaceCreateSphere::getShortDescription() { return "GENERATE A SPHERE WITH CONSISTENT VERTEX AREAS"; } OperationParameters* AlgorithmSurfaceCreateSphere::getParameters() { OperationParameters* ret = new OperationParameters(); ret->addIntegerParameter(1, "num-vertices", "desired number of vertices"); ret->addSurfaceOutputParameter(2, "sphere-out", "the output sphere"); ret->setHelpText( AString("Generates a sphere by regularly dividing the triangles of an icosahedron, to come as close to the desired number of vertices as possible, ") + "and modifying it to have very similar vertex areas for all vertices. " + "To generate a pair of vertex-matched left and right spheres, use this command, then -surface-flip-lr to generate the other sphere, then -set-structure on each. " + "For example:\n\n" + "$ wb_command -surface-create-sphere 6000 Sphere.6k.R.surf.gii\n" + "$ wb_command -surface-flip-lr Sphere.6k.R.surf.gii Sphere.6k.L.surf.gii\n" + "$ wb_command -set-structure Sphere.6k.R.surf.gii CORTEX_RIGHT\n" + "$ wb_command -set-structure Sphere.6k.L.surf.gii CORTEX_LEFT" ); return ret; } void AlgorithmSurfaceCreateSphere::useParameters(OperationParameters* myParams, ProgressObject* myProgObj) { int numVertices = (int)myParams->getInteger(1); SurfaceFile* mySurfOut = myParams->getOutputSurface(2); AlgorithmSurfaceCreateSphere(myProgObj, numVertices, mySurfOut); } AlgorithmSurfaceCreateSphere::AlgorithmSurfaceCreateSphere(ProgressObject* myProgObj, const int& numVertices, SurfaceFile* mySurfOut) : AbstractAlgorithm(myProgObj) { LevelProgress myProgress(myProgObj); if (numVertices < 1) throw AlgorithmException("desired number of vertices must be positive"); int lastnodes, mynodes, mytris; m_numDivisions = -1; getNumberOfNodesAndTrianglesFromDivisions(0, lastnodes, mytris); for (int i = 1; i < 10000; ++i)//10363 divisions overflows an int32 in number of triangles, tighten this sanity check? { getNumberOfNodesAndTrianglesFromDivisions(i, mynodes, mytris); if (abs(numVertices - lastnodes) < abs(numVertices - mynodes)) { m_numDivisions = i - 1; CaretLogFine("Closest divided icosahedron has " + AString::number(lastnodes) + " nodes."); break; } lastnodes = mynodes; } if (m_numDivisions == -1) throw AlgorithmException("too many vertices specified"); m_surface = mySurfOut; getNumberOfNodesAndTrianglesFromDivisions(m_numDivisions, mynodes, mytris); m_surface->setNumberOfNodesAndTriangles(mynodes, mytris); m_surface->setSurfaceType(SurfaceTypeEnum::SPHERICAL); m_surface->setSecondaryType(SecondarySurfaceTypeEnum::INVALID); SurfaceFile initSurf; initSurf.setNumberOfNodesAndTriangles(12, 20); const double phi = (1.0 + sqrt(5.0)) / 2.0; initSurf.setCoordinate(0, -phi, 0.0, 1.0);//initial icosahedron initSurf.setCoordinate(1, 0, -1.0, phi); initSurf.setCoordinate(2, phi, 0.0, 1.0); initSurf.setCoordinate(3, 0, 1.0, phi); initSurf.setCoordinate(4, -1.0, -phi, 0.0); initSurf.setCoordinate(5, 1.0, -phi, 0.0); initSurf.setCoordinate(6, 1.0, phi, 0.0); initSurf.setCoordinate(7, -1.0, phi, 0.0); initSurf.setCoordinate(8, -phi, 0.0, -1.0); initSurf.setCoordinate(9, 0, -1.0, -phi); initSurf.setCoordinate(10, phi, 0.0, -1.0); initSurf.setCoordinate(11, 0, 1.0, -phi); initSurf.setTriangle(0, 0, 7, 8); initSurf.setTriangle(1, 0, 3, 7); initSurf.setTriangle(2, 0, 1, 3); initSurf.setTriangle(3, 1, 2, 3); initSurf.setTriangle(4, 1, 5, 2); initSurf.setTriangle(5, 5, 9, 10); initSurf.setTriangle(6, 10, 11, 6); initSurf.setTriangle(7, 4, 1, 0); initSurf.setTriangle(8, 4, 5, 1); initSurf.setTriangle(9, 4, 9, 5); initSurf.setTriangle(10, 5, 10, 2); initSurf.setTriangle(11, 10, 6, 2); initSurf.setTriangle(12, 6, 11, 7); initSurf.setTriangle(13, 11, 9, 8); initSurf.setTriangle(14, 9, 4, 8); initSurf.setTriangle(15, 8, 4, 0); initSurf.setTriangle(16, 11, 8, 7); initSurf.setTriangle(17, 6, 7, 3); initSurf.setTriangle(18, 6, 3, 2); initSurf.setTriangle(19, 11, 10, 9); Vector3D tempcoord; for (int i = 0; i < 12; ++i) { tempcoord = initSurf.getCoordinate(i); tempcoord *= 100.0f / tempcoord.length();//make it directly into a radius 100 sphere rather than taking a second pass m_surface->setCoordinate(i, tempcoord); } m_curTiles = 0; m_curNodes = 12; m_edgenodes.resize(11);//stores by low node # vector edge1(m_numDivisions + 1), edge2(m_numDivisions + 1), edge3(m_numDivisions + 1); vector > facenodes(m_numDivisions + 1); for (int i = 0; i <= m_numDivisions; ++i) {// first index is row from bottom, second index is column from left facenodes[i].resize(m_numDivisions - i + 1); } for (int i = 0; i < 20; ++i) { const int32_t* nodes = initSurf.getTriangle(i); //convention for visualization: nodes[0] is bottom left, nodes[1] is bottom right, nodes[2] is top //tile generation also follows this convention, with the consequence that tile orientation is preserved getEdge(nodes[0], nodes[1], edge1.data());//bottom edge, left to right getEdge(nodes[0], nodes[2], edge2.data());//left edge, bottom to top getEdge(nodes[1], nodes[2], edge3.data());//right edge, bottom to top const float* coord1 = initSurf.getCoordinate(nodes[0]); const float* coord2 = initSurf.getCoordinate(nodes[1]); const float* coord3 = initSurf.getCoordinate(nodes[2]); for (int j = 0; j <= m_numDivisions; ++j) {//copy edge nodes into the face array facenodes[0][j] = edge1[j]; facenodes[j][0] = edge2[j]; facenodes[j][m_numDivisions - j] = edge3[j]; } for (int j = 1; j < m_numDivisions - 1; ++j) {//generate interior coordinates int intCols = m_numDivisions - j; for (int k = 1; k < intCols; ++k) { interp3(coord1, coord2, coord3, j, k, tempcoord); tempcoord *= 100.0f / tempcoord.length();//make it directly into a radius 100 sphere rather than taking a second pass m_surface->setCoordinate(m_curNodes, tempcoord); facenodes[j][k] = m_curNodes; ++m_curNodes; } } for (int j = 0; j < m_numDivisions; ++j) {//generate tiles for (int k = 0; k < m_numDivisions - j - 1; ++k) {//pairs for trapezoidal pieces m_surface->setTriangle(m_curTiles, facenodes[j][k], facenodes[j][k + 1], facenodes[j + 1][k]); m_surface->setTriangle(m_curTiles + 1, facenodes[j + 1][k], facenodes[j][k + 1], facenodes[j + 1][k + 1]); m_curTiles += 2; }//and one more m_surface->setTriangle(m_curTiles, facenodes[j][m_numDivisions - j - 1], facenodes[j][m_numDivisions - j], facenodes[j + 1][m_numDivisions - j - 1]); ++m_curTiles; } } } void AlgorithmSurfaceCreateSphere::getNumberOfNodesAndTrianglesFromDivisions(const int& divisions, int& numNodesOut, int& numTrianglesOut) { int div2 = divisions * divisions; numNodesOut = 2 + 10 * div2; numTrianglesOut = 20 * div2;//yes, its really that simple, the sum of the two triangulars is square } void AlgorithmSurfaceCreateSphere::interp3(const float coord1[3], const float coord2[3], const float coord3[3], const int& row, const int& col, float out[3]) {//this is the function to change if you want different spacing float weight2 = ((float)col) / m_numDivisions;//start with flat interpolation weights float weight3 = ((float)row) / m_numDivisions; float weight1 = 1.0f - weight2 - weight3; //polynomial for each weight - should map 0 to 0 and 1 to 1 const float quintweight = 0.0537206f;//WEIGHTS TUNED FOR ICOSAHEDRON VIA GENETIC ALGORITHM, ADJUST FOR OTHER INITIAL POLYGONS const float quartweight = -0.174466f;//this polynomial should be highly dependent on size of the triangle being interpolated const float cubeweight = 0.292547f; const float quadweight = -0.456351f; const float linweight = 1.0f - quintweight - quartweight - cubeweight - quadweight;//make sure it maps 0 to 0 and 1 to 1 weight1 = ((((quintweight * weight1 + quartweight) * weight1 + cubeweight) * weight1 + quadweight) * weight1 + linweight) * weight1;//quintic approximation of great arc equal area weight transformation function weight2 = ((((quintweight * weight2 + quartweight) * weight2 + cubeweight) * weight2 + quadweight) * weight2 + linweight) * weight2; weight3 = ((((quintweight * weight3 + quartweight) * weight3 + cubeweight) * weight3 + quadweight) * weight3 + linweight) * weight3;//three weights no longer sum to 1, but thats ok out[0] = coord1[0] * weight1 + coord2[0] * weight2 + coord3[0] * weight3; out[1] = coord1[1] * weight1 + coord2[1] * weight2 + coord3[1] * weight3; out[2] = coord1[2] * weight1 + coord2[2] * weight2 + coord3[2] * weight3; } void AlgorithmSurfaceCreateSphere::getEdge(int node1, int node2, int* out) { bool reverse = false; int i, index, edgesize = m_numDivisions + 1; if (node1 > node2) { reverse = true; i = node1; node1 = node2; node2 = i; } bool found = false; for (i = 0; i < (int)m_edgenodes[node1].size(); ++i) { if (m_edgenodes[node1][i][m_numDivisions] == node2) { found = true; index = i; break; } } if (!found) { float coord3[3] = {0.0f, 0.0f, 0.0f}; Vector3D tempcoord; const float* coord1, *coord2; coord1 = m_surface->getCoordinate(node1); coord2 = m_surface->getCoordinate(node2); std::vector tempvec; tempvec.resize(edgesize); tempvec[0] = node1; tempvec[m_numDivisions] = node2; for (i = 1; i < m_numDivisions; ++i) { interp3(coord1, coord2, coord3, 0, i, tempcoord);//use 0 as dummy node, with row 0 it is unused tempvec[i] = m_curNodes; tempcoord *= 100.0f / tempcoord.length(); m_surface->setCoordinate(m_curNodes, tempcoord); ++m_curNodes; } index = m_edgenodes[node1].size(); m_edgenodes[node1].push_back(tempvec); } if (reverse) { for (i = 0; i < edgesize; ++i) { out[i] = m_edgenodes[node1][index][edgesize - i - 1]; } } else { for (i = 0; i < edgesize; ++i) { out[i] = m_edgenodes[node1][index][i]; } } } float AlgorithmSurfaceCreateSphere::getAlgorithmInternalWeight() { return 1.0f;//override this if needed, if the progress bar isn't smooth } float AlgorithmSurfaceCreateSphere::getSubAlgorithmWeight() { //return AlgorithmInsertNameHere::getAlgorithmWeight();//if you use a subalgorithm return 0.0f; } connectome-workbench-1.2.3+git41-gc4c6c90/src/Algorithms/AlgorithmSurfaceCreateSphere.h000066400000000000000000000042441300200146000305640ustar00rootroot00000000000000#ifndef __ALGORITHM_SURFACE_CREATE_SPHERE_H__ #define __ALGORITHM_SURFACE_CREATE_SPHERE_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 "AbstractAlgorithm.h" #include namespace caret { class AlgorithmSurfaceCreateSphere : public AbstractAlgorithm { AlgorithmSurfaceCreateSphere(); void interp3(const float coord1[3], const float coord2[3], const float coord3[3], const int& row, const int& col, float out[3]); void getEdge(int node1, int node2, int* out); int m_numDivisions, m_curNodes, m_curTiles; SurfaceFile* m_surface; std::vector > > m_edgenodes; static void getNumberOfNodesAndTrianglesFromDivisions(const int& divisions, int& numNodesOut, int& numTrianglesOut); protected: static float getSubAlgorithmWeight(); static float getAlgorithmInternalWeight(); public: AlgorithmSurfaceCreateSphere(ProgressObject* myProgObj, const int& numVertices, SurfaceFile* mySurfOut); static OperationParameters* getParameters(); static void useParameters(OperationParameters* myParams, ProgressObject* myProgObj); static AString getCommandSwitch(); static AString getShortDescription(); }; typedef TemplateAutoOperation AutoAlgorithmSurfaceCreateSphere; } #endif //__ALGORITHM_SURFACE_CREATE_SPHERE_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Algorithms/AlgorithmSurfaceCurvature.cxx000066400000000000000000000151641300200146000305500ustar00rootroot00000000000000/*LICENSE_START*/ /* * Copyright (C) 2015 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 "AlgorithmSurfaceCurvature.h" #include "AlgorithmException.h" #include "MetricFile.h" #include "SurfaceFile.h" #include "TopologyHelper.h" #include "Vector3D.h" #include using namespace caret; using namespace std; AString AlgorithmSurfaceCurvature::getCommandSwitch() { return "-surface-curvature"; } AString AlgorithmSurfaceCurvature::getShortDescription() { return "CALCULATE CURVATURE OF SURFACE"; } OperationParameters* AlgorithmSurfaceCurvature::getParameters() { OperationParameters* ret = new OperationParameters(); ret->addSurfaceParameter(1, "surface", "the surface to compute the curvature of"); OptionalParameter* meanOpt = ret->createOptionalParameter(2, "-mean", "output mean curvature"); meanOpt->addMetricOutputParameter(1, "mean-out", "mean curvature metric"); OptionalParameter* gaussOpt = ret->createOptionalParameter(3, "-gauss", "output gaussian curvature"); gaussOpt->addMetricOutputParameter(1, "gauss-out", "gaussian curvature metric"); ret->setHelpText( AString("Compute the curvature of the surface, using the method from:\n") + "Interactive Texture Mapping by J. Maillot, Yahia, and Verroust, 1993.\n" + "ACM-0-98791-601-8/93/008" ); return ret; } void AlgorithmSurfaceCurvature::useParameters(OperationParameters* myParams, ProgressObject* myProgObj) { SurfaceFile* mySurf = myParams->getSurface(1); MetricFile* meanOut = NULL, *gaussOut = NULL; OptionalParameter* meanOpt = myParams->getOptionalParameter(2); if (meanOpt->m_present) { meanOut = meanOpt->getOutputMetric(1); } OptionalParameter* gaussOpt = myParams->getOptionalParameter(3); if (gaussOpt->m_present) { gaussOut = gaussOpt->getOutputMetric(1); } AlgorithmSurfaceCurvature(myProgObj, mySurf, meanOut, gaussOut); } AlgorithmSurfaceCurvature::AlgorithmSurfaceCurvature(ProgressObject* myProgObj, const SurfaceFile* mySurf, MetricFile* meanOut, MetricFile* gaussOut) : AbstractAlgorithm(myProgObj) { LevelProgress myProgress(myProgObj); int numNodes = mySurf->getNumberOfNodes(); if (meanOut != NULL) { meanOut->setNumberOfNodesAndColumns(numNodes, 1); meanOut->setStructure(mySurf->getStructure()); meanOut->setColumnName(0, "mean curvature"); } if (gaussOut != NULL) { gaussOut->setNumberOfNodesAndColumns(numNodes, 1); gaussOut->setStructure(mySurf->getStructure()); gaussOut->setColumnName(0, "gaussian curvature"); } CaretPointer myTopoHelp = mySurf->getTopologyHelper(); const float* normalData = mySurf->getNormalData(); for (int i = 0; i < numNodes; ++i) { const vector& neighbors = myTopoHelp->getNodeNeighbors(i); int numNeigh = (int)neighbors.size(); float k1 = 0.0f, k2 = 0.0f; if (numNeigh > 0) { Vector3D center = mySurf->getCoordinate(i); Vector3D normal = normalData + i * 3; Vector3D basisStart;//default constructor is 0 vector if (abs(normal[0]) > abs(normal[1])) {//a vector not parallel to the normal basisStart[1] = 1.0f; } else { basisStart[0] = 1.0f; } Vector3D ihat = normal.cross(basisStart).normal(); Vector3D jhat = normal.cross(ihat); float sig_x = 0.0f, sig_xy = 0.0f, sig_y = 0.0f; float norm_x = 0.0f, norm_xy = 0.0f, norm_y = 0.0f; for (int j = 0; j < numNeigh; ++j) {//center node contributes 0 to each sum, so skip it Vector3D neighNormal = normalData + neighbors[j] * 3; Vector3D neighDiff = Vector3D(mySurf->getCoordinate(neighbors[j])) - center; float normProj[2] = { neighNormal.dot(ihat), neighNormal.dot(jhat) }; float diffProj[2] = { neighDiff.dot(ihat), neighDiff.dot(jhat) }; sig_x += diffProj[0] * diffProj[0]; sig_xy += diffProj[0] * diffProj[1]; sig_y += diffProj[1] * diffProj[1]; norm_x += normProj[0] * diffProj[0]; norm_xy += normProj[0] * diffProj[1] + normProj[1] * diffProj[0]; norm_y += normProj[1] * diffProj[1]; } float sig_xy2 = sig_xy * sig_xy; float denom = (sig_x + sig_y) * (-sig_xy2 + sig_x * sig_y); if (denom != 0.0f) { float a = (norm_x * (-sig_xy2 + sig_x * sig_y + sig_y * sig_y) - norm_xy * sig_xy * sig_y + norm_y * sig_xy2) / denom; float b = (-norm_x * sig_xy * sig_y + norm_xy * sig_x * sig_y - norm_y * sig_x * sig_xy) / denom; float c = (norm_x * sig_xy2 - norm_xy * sig_x * sig_xy + norm_y * (sig_x * sig_x - sig_xy2 + sig_x * sig_y)) / denom; float trC = a + c; float detC = a * c - b * b; float temp = trC * trC - 4 * detC; if (temp >= 0.0f) { float delta = sqrt(temp); k1 = (trC + delta) / 2; k2 = (trC - delta) / 2; } } } if (meanOut != NULL) { meanOut->setValue(i, 0, (k1 + k2) / 2); } if (gaussOut != NULL) { gaussOut->setValue(i, 0, k1 * k2); } } } float AlgorithmSurfaceCurvature::getAlgorithmInternalWeight() { return 1.0f;//override this if needed, if the progress bar isn't smooth } float AlgorithmSurfaceCurvature::getSubAlgorithmWeight() { //return AlgorithmInsertNameHere::getAlgorithmWeight();//if you use a subalgorithm return 0.0f; } connectome-workbench-1.2.3+git41-gc4c6c90/src/Algorithms/AlgorithmSurfaceCurvature.h000066400000000000000000000033101300200146000301630ustar00rootroot00000000000000#ifndef __ALGORITHM_SURFACE_CURVATURE_H__ #define __ALGORITHM_SURFACE_CURVATURE_H__ /*LICENSE_START*/ /* * Copyright (C) 2015 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 AlgorithmSurfaceCurvature : public AbstractAlgorithm { AlgorithmSurfaceCurvature(); protected: static float getSubAlgorithmWeight(); static float getAlgorithmInternalWeight(); public: AlgorithmSurfaceCurvature(ProgressObject* myProgObj, const SurfaceFile* mySurf, MetricFile* meanOut = NULL, MetricFile* gaussOut = NULL); static OperationParameters* getParameters(); static void useParameters(OperationParameters* myParams, ProgressObject* myProgObj); static AString getCommandSwitch(); static AString getShortDescription(); }; typedef TemplateAutoOperation AutoAlgorithmSurfaceCurvature; } #endif //__ALGORITHM_SURFACE_CURVATURE_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Algorithms/AlgorithmSurfaceDistortion.cxx000066400000000000000000000261221300200146000307220ustar00rootroot00000000000000/*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 "AlgorithmSurfaceDistortion.h" #include "AlgorithmException.h" #include "AlgorithmMetricSmoothing.h" #include "MathFunctions.h" #include "MetricFile.h" #include "SurfaceFile.h" #include "TopologyHelper.h" #include "Vector3D.h" #include #include using namespace caret; using namespace std; AString AlgorithmSurfaceDistortion::getCommandSwitch() { return "-surface-distortion"; } AString AlgorithmSurfaceDistortion::getShortDescription() { return "MEASURE DISTORTION BETWEEN SURFACES"; } OperationParameters* AlgorithmSurfaceDistortion::getParameters() { OperationParameters* ret = new OperationParameters(); ret->addSurfaceParameter(1, "surface-reference", "the reference surface"); ret->addSurfaceParameter(2, "surface-distorted", "the distorted surface"); ret->addMetricOutputParameter(3, "metric-out", "the output distortion metric"); OptionalParameter* smoothOpt = ret->createOptionalParameter(4, "-smooth", "smooth the area data"); smoothOpt->addDoubleParameter(1, "sigma", "the smoothing kernel sigma in mm"); ret->createOptionalParameter(5, "-caret5-method", "use the surface distortion method from caret5"); ret->createOptionalParameter(6, "-edge-method", "calculate distortion of edge lengths rather than areas"); ret->setHelpText( AString("This command, when not using -caret5-method or -edge-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." ); return ret; } void AlgorithmSurfaceDistortion::useParameters(OperationParameters* myParams, ProgressObject* myProgObj) { SurfaceFile* referenceSurf = myParams->getSurface(1); SurfaceFile* distortedSurf = myParams->getSurface(2); MetricFile* myMetricOut = myParams->getOutputMetric(3); float smooth = -1.0f; OptionalParameter* smoothOpt = myParams->getOptionalParameter(4); if (smoothOpt->m_present) { smooth = (float)smoothOpt->getDouble(1); if (smooth <= 0.0f) throw AlgorithmException("smoothing kernel must be positive if specified"); } bool caret5method = myParams->getOptionalParameter(5)->m_present; 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); } AlgorithmSurfaceDistortion::AlgorithmSurfaceDistortion(ProgressObject* myProgObj, const SurfaceFile* referenceSurf, const SurfaceFile* distortedSurf, MetricFile* myMetricOut, const float& smooth, const bool& caret5method, const bool& edgeMethod) : 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 if (myProgObj != NULL) { if (smooth > 0.0f && !edgeMethod) { if (caret5method) { caret5Smooth = myProgObj->addAlgorithm(AlgorithmMetricSmoothing::getAlgorithmWeight()); } else { smoothRef = myProgObj->addAlgorithm(AlgorithmMetricSmoothing::getAlgorithmWeight()); smoothDistort = myProgObj->addAlgorithm(AlgorithmMetricSmoothing::getAlgorithmWeight()); } } } LevelProgress myProgress(myProgObj); if (!referenceSurf->hasNodeCorrespondence(*distortedSurf)) throw AlgorithmException("input surfaces must have node correspondence"); int numNodes = referenceSurf->getNumberOfNodes(); myMetricOut->setNumberOfNodesAndColumns(numNodes, 1); myMetricOut->setStructure(referenceSurf->getStructure()); if (caret5method) { myMetricOut->setColumnName(0, "area distortion (caret5)"); int numTiles = referenceSurf->getNumberOfTriangles(); vector tilescratch(numTiles), nodescratch(numNodes); const float* refCoords = referenceSurf->getCoordinateData(), *distortCoords = distortedSurf->getCoordinateData(); for (int i = 0; i < numTiles; ++i) { const int32_t* myTile = referenceSurf->getTriangle(i); int32_t offset[3] = { myTile[0] * 3, myTile[1] * 3, myTile[2] * 3 }; float refarea = MathFunctions::triangleArea(refCoords + offset[0], refCoords + offset[1], refCoords + offset[2]); float distortarea = MathFunctions::triangleArea(distortCoords + offset[0], distortCoords + offset[1], distortCoords + offset[2]); if (refarea == 0.0f) { if (distortarea == 0.0f) { tilescratch[i] = 0.0f; } else { tilescratch[i] = log(10000.0) / log(2.0);//thats what it says } } else { if (distortarea / refarea < 0.00000001) { tilescratch[i] = log(0.00000001) / log(2.0); } else { tilescratch[i] = log(distortarea / refarea) / log(2.0); } } } CaretPointer myhelp = referenceSurf->getTopologyHelper(); for (int i = 0; i < numNodes; ++i) { const vector& myTiles = myhelp->getNodeTiles(i); int tileCount = (int)myTiles.size(); double accum = 0.0; for (int j = 0; j < tileCount; ++j) { accum += tilescratch[myTiles[j]]; } if (tileCount == 0) { nodescratch[i] = 0.0f;//actually, caret5 uses 1 here, but that is wrong, so fix it } else { nodescratch[i] = accum / tileCount; } } if (smooth > 0.0f) { MetricFile tempResult; tempResult.setNumberOfNodesAndColumns(numNodes, 1); tempResult.setValuesForColumn(0, nodescratch.data());//TSC: not sure what the "best" smoothing method here is, but there isn't a "correct" one because this method has flaws AlgorithmMetricSmoothing(caret5Smooth, referenceSurf, &tempResult, smooth, myMetricOut, NULL, false, false, -1, NULL, MetricSmoothingObject::GEO_GAUSS_AREA); myMetricOut->setStructure(referenceSurf->getStructure());//just in case we change where metric smoothing gets structure from myMetricOut->setColumnName(0, "area distortion (caret5)"); } else { myMetricOut->setValuesForColumn(0, nodescratch.data()); } } else if (edgeMethod) { myMetricOut->setColumnName(0, "edge distortion"); CaretPointer myhelp = referenceSurf->getTopologyHelper(); const float* refCoords = referenceSurf->getCoordinateData(), *distortCoords = distortedSurf->getCoordinateData(); for (int i = 0; i < numNodes; ++i) { Vector3D refCenter = refCoords + i * 3; Vector3D distortCenter = distortCoords + i * 3; const vector& neighbors = myhelp->getNodeNeighbors(i); int numNeigh = (int)neighbors.size(); float accum = 0.0f; for (int j = 0; j < numNeigh; ++j) { Vector3D refNeigh = refCoords + neighbors[j] * 3; Vector3D distortNeigh = distortCoords + neighbors[j] * 3; float refLength = (refNeigh - refCenter).length(); float distortLength = (distortNeigh - distortCenter).length(); float ratio; if (refLength < distortLength) { ratio = distortLength / refLength; } else { ratio = refLength / distortLength; } accum += log(ratio) / log(2.0f); } myMetricOut->setValue(i, 0, accum / numNeigh); } } else { myMetricOut->setColumnName(0, "area distortion"); MetricFile refAreas, distortAreas; refAreas.setNumberOfNodesAndColumns(numNodes, 1); distortAreas.setNumberOfNodesAndColumns(numNodes, 1); vector scratch; referenceSurf->computeNodeAreas(scratch); refAreas.setValuesForColumn(0, scratch.data()); distortedSurf->computeNodeAreas(scratch); distortAreas.setValuesForColumn(0, scratch.data()); MetricFile refSmoothed, distortSmoothed; const float* refData = refAreas.getValuePointerForColumn(0), *distortData = distortAreas.getValuePointerForColumn(0); if (smooth > 0.0f) { AlgorithmMetricSmoothing(smoothRef, referenceSurf, &refAreas, smooth, &refSmoothed, NULL, false, false, -1, NULL, MetricSmoothingObject::GEO_GAUSS_EQUAL); AlgorithmMetricSmoothing(smoothDistort, distortedSurf, &distortAreas, smooth, &distortSmoothed, NULL, false, false, -1, NULL, MetricSmoothingObject::GEO_GAUSS_EQUAL); refData = refSmoothed.getValuePointerForColumn(0); distortData = distortSmoothed.getValuePointerForColumn(0); } for (int i = 0; i < numNodes; ++i) { scratch[i] = log(distortData[i] / refData[i]) / log(2.0f);//should we sanity check these? } myMetricOut->setValuesForColumn(0, scratch.data()); } } float AlgorithmSurfaceDistortion::getAlgorithmInternalWeight() { return 1.0f;//override this if needed, if the progress bar isn't smooth } float AlgorithmSurfaceDistortion::getSubAlgorithmWeight() { //return AlgorithmInsertNameHere::getAlgorithmWeight();//if you use a subalgorithm return 0.0f; } connectome-workbench-1.2.3+git41-gc4c6c90/src/Algorithms/AlgorithmSurfaceDistortion.h000066400000000000000000000035331300200146000303500ustar00rootroot00000000000000#ifndef __ALGORITHM_SURFACE_DISTORTION_H__ #define __ALGORITHM_SURFACE_DISTORTION_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 "AbstractAlgorithm.h" namespace caret { class AlgorithmSurfaceDistortion : public AbstractAlgorithm { AlgorithmSurfaceDistortion(); protected: static float getSubAlgorithmWeight(); 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); static OperationParameters* getParameters(); static void useParameters(OperationParameters* myParams, ProgressObject* myProgObj); static AString getCommandSwitch(); static AString getShortDescription(); }; typedef TemplateAutoOperation AutoAlgorithmSurfaceDistortion; } #endif //__ALGORITHM_SURFACE_DISTORTION_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Algorithms/AlgorithmSurfaceFlipLR.cxx000066400000000000000000000065011300200146000277130ustar00rootroot00000000000000/*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 "AlgorithmSurfaceFlipLR.h" #include "AlgorithmException.h" #include "SurfaceFile.h" using namespace caret; using namespace std; AString AlgorithmSurfaceFlipLR::getCommandSwitch() { return "-surface-flip-lr"; } AString AlgorithmSurfaceFlipLR::getShortDescription() { return "MIRROR A SURFACE THROUGH THE YZ PLANE"; } OperationParameters* AlgorithmSurfaceFlipLR::getParameters() { OperationParameters* ret = new OperationParameters(); ret->addSurfaceParameter(1, "surface", "the surface to flip"); ret->addSurfaceOutputParameter(2, "surface-out", "the output flipped surface"); ret->setHelpText( AString("This command negates the x coordinate of each vertex, and flips the surface normals, so that you have a surface of ") + "opposite handedness with the same features and vertex correspondence, with normals consistent with the original surface. " + "That is, if the input surface has normals facing outward, the output surface will also have normals facing outward." ); return ret; } void AlgorithmSurfaceFlipLR::useParameters(OperationParameters* myParams, ProgressObject* myProgObj) { SurfaceFile* mySurf = myParams->getSurface(1); SurfaceFile* mySurfOut = myParams->getOutputSurface(2); AlgorithmSurfaceFlipLR(myProgObj, mySurf, mySurfOut); } AlgorithmSurfaceFlipLR::AlgorithmSurfaceFlipLR(ProgressObject* myProgObj, const SurfaceFile* mySurf, SurfaceFile* mySurfOut) : AbstractAlgorithm(myProgObj) { LevelProgress myProgress(myProgObj); mySurfOut->setNumberOfNodesAndTriangles(mySurf->getNumberOfNodes(), mySurf->getNumberOfTriangles()); mySurfOut->setSurfaceType(mySurf->getSurfaceType()); mySurfOut->setSecondaryType(mySurf->getSecondaryType()); int numNodes = mySurf->getNumberOfNodes(); float tempcoord[3]; for (int i = 0; i < numNodes; ++i) { mySurf->getCoordinate(i, tempcoord); tempcoord[0] = -tempcoord[0]; mySurfOut->setCoordinate(i, tempcoord); } int numTiles = mySurf->getNumberOfTriangles(); for (int i = 0; i < numTiles; ++i) { const int32_t* temptile = mySurf->getTriangle(i); mySurfOut->setTriangle(i, temptile[1], temptile[0], temptile[2]); } } float AlgorithmSurfaceFlipLR::getAlgorithmInternalWeight() { return 1.0f;//override this if needed, if the progress bar isn't smooth } float AlgorithmSurfaceFlipLR::getSubAlgorithmWeight() { //return AlgorithmInsertNameHere::getAlgorithmWeight();//if you use a subalgorithm return 0.0f; } connectome-workbench-1.2.3+git41-gc4c6c90/src/Algorithms/AlgorithmSurfaceFlipLR.h000066400000000000000000000032221300200146000273350ustar00rootroot00000000000000#ifndef __ALGORITHM_SURFACE_FLIP_LR_H__ #define __ALGORITHM_SURFACE_FLIP_LR_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 "AbstractAlgorithm.h" namespace caret { class AlgorithmSurfaceFlipLR : public AbstractAlgorithm { AlgorithmSurfaceFlipLR(); protected: static float getSubAlgorithmWeight(); static float getAlgorithmInternalWeight(); public: AlgorithmSurfaceFlipLR(ProgressObject* myProgObj, const SurfaceFile* mySurf, SurfaceFile* mySurfOut); static OperationParameters* getParameters(); static void useParameters(OperationParameters* myParams, ProgressObject* myProgObj); static AString getCommandSwitch(); static AString getShortDescription(); }; typedef TemplateAutoOperation AutoAlgorithmSurfaceFlipLR; } #endif //__ALGORITHM_SURFACE_FLIP_LR_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Algorithms/AlgorithmSurfaceGenerateInflated.cxx000066400000000000000000000223251300200146000317660ustar00rootroot00000000000000 /*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 "CaretAssert.h" #include "CaretLogger.h" #include "AlgorithmSurfaceGenerateInflated.h" #include "AlgorithmSurfaceInflation.h" #include "AlgorithmException.h" #include "SurfaceFile.h" using namespace caret; /** * \class caret::AlgorithmSurfaceGenerateInflated * \brief SURFACE GENERATE INFLATED * * */ /** * @return Command line switch */ AString AlgorithmSurfaceGenerateInflated::getCommandSwitch() { return "-surface-generate-inflated"; } /** * @return Short description of algorithm */ AString AlgorithmSurfaceGenerateInflated::getShortDescription() { return "SURFACE GENERATE INFLATED"; } /** * @return Parameters for algorithm */ OperationParameters* AlgorithmSurfaceGenerateInflated::getParameters() { OperationParameters* ret = new OperationParameters(); ret->addSurfaceParameter(1, "anatomical-surface-in", "the anatomical surface"); ret->addSurfaceOutputParameter(2, "inflated-surface-out", "the output inflated surface"); ret->addSurfaceOutputParameter(3, "very-inflated-surface-out", "the output very inflated surface"); OptionalParameter* weightOpt = ret->createOptionalParameter(4, "-iterations-scale", "optional iterations scaling"); weightOpt->addDoubleParameter(1, "iterations-scale-value", "iterations-scale value"); AString helpText = ("Generate inflated and very inflated surfaces. The output surfaces are " "\'matched\' (have same XYZ range) to the anatomical surface. " "In most cases, an iterations-scale of 1.0 (default) is sufficient. However, if " "the surface contains a large number of vertices (150,000), try an " "iterations-scale of 2.5."); ret->setHelpText(helpText); return ret; } /** * Use Parameters and perform algorithm * @param myParams * Parameters for algorithm * @param myProgObj * The progress object * @throws * AlgorithmException if errors */ void AlgorithmSurfaceGenerateInflated::useParameters(OperationParameters* myParams, ProgressObject* myProgObj) { const SurfaceFile* anatomicalSurfaceIn = myParams->getSurface(1); SurfaceFile* inflatedSurfaceFile = myParams->getOutputSurface(2); SurfaceFile* veryInflatedSurfaceFile = myParams->getOutputSurface(3); float iterationsScale = 1.0; OptionalParameter* iterationsScaleOpt = myParams->getOptionalParameter(4); if (iterationsScaleOpt->m_present) { iterationsScale = iterationsScaleOpt->getDouble(1); } /* * Constructs and executes the algorithm */ AlgorithmSurfaceGenerateInflated(myProgObj, anatomicalSurfaceIn, inflatedSurfaceFile, veryInflatedSurfaceFile, iterationsScale); } /** * Constructor * * Calling the constructor will execute the algorithm * * @param myProgObj * Parameters for algorithm * @param anatomicalSurfaceFile * The anatomical surface. * @param inflatedSurfaceFileOut * Inflated surface that is generated. * @param veryInflatedSurfaceFileOut * Very inflated surface that is generated. * @param iterationsScaleIn * Iterations scale used for surface with large numbers of nodes. */ AlgorithmSurfaceGenerateInflated::AlgorithmSurfaceGenerateInflated(ProgressObject* myProgObj, const SurfaceFile* anatomicalSurfaceFile, SurfaceFile* inflatedSurfaceFileOut, SurfaceFile* veryInflatedSurfaceFileOut, const float iterationsScaleIn) : AbstractAlgorithm(myProgObj) { ProgressObject* lowProgress = NULL, *inflatedProgress = NULL, *veryInfProgress = NULL; if (myProgObj != NULL) { lowProgress = myProgObj->addAlgorithm(AlgorithmSurfaceInflation::getAlgorithmWeight()); inflatedProgress = myProgObj->addAlgorithm(AlgorithmSurfaceInflation::getAlgorithmWeight() * 2); veryInfProgress = myProgObj->addAlgorithm(AlgorithmSurfaceInflation::getAlgorithmWeight() * 4); } /* * Sets the algorithm up to use the progress object, and will * finish the progress object automatically when the algorithm terminates */ LevelProgress myProgress(myProgObj, 1.0f, 0.1f);//low internal weight because this function doesn't do much non-subalgorithm stuff const float iterationsScale = ((iterationsScaleIn > 0.0) ? iterationsScaleIn : 1.0); /* * Generate low-smooth surface which is an intermediate surface * between anatomical and inflated. */ SurfaceFile lowSmoothSurface(*anatomicalSurfaceFile); const int32_t lowSmoothCycles = 1; const float lowSmoothStrength = 0.2; const int32_t lowSmoothIterations = static_cast(50 * iterationsScale); const float lowSmoothInflationFactor = 1.0; myProgress.setTask("Generating Low-smooth Surface"); AlgorithmSurfaceInflation(lowProgress, anatomicalSurfaceFile, &lowSmoothSurface, &lowSmoothSurface, lowSmoothCycles, lowSmoothStrength, lowSmoothIterations, lowSmoothInflationFactor); /* * Generation the inflated surface */ *inflatedSurfaceFileOut = lowSmoothSurface; const int32_t inflatedSmoothCycles = 2; const float inflatedSmoothStrength = 1.0; const int32_t inflatedSmoothIterations = static_cast(30 * iterationsScale); const float inflatedSmoothInflationFactor = 1.4; myProgress.setTask("Generating Inflated Surface"); AlgorithmSurfaceInflation(inflatedProgress, anatomicalSurfaceFile, inflatedSurfaceFileOut, inflatedSurfaceFileOut, inflatedSmoothCycles, inflatedSmoothStrength, inflatedSmoothIterations, inflatedSmoothInflationFactor); inflatedSurfaceFileOut->setSurfaceType(SurfaceTypeEnum::INFLATED); /* * Generation the inflated surface */ *veryInflatedSurfaceFileOut = *inflatedSurfaceFileOut; const int32_t veryInflatedSmoothCycles = 4; const float veryInflatedSmoothStrength = 1.0; const int32_t veryInflatedSmoothIterations = static_cast(30 * iterationsScale); const float veryInflatedSmoothInflationFactor = 1.1; myProgress.setTask("Generating Very Inflated Surface"); AlgorithmSurfaceInflation(veryInfProgress, anatomicalSurfaceFile, veryInflatedSurfaceFileOut, veryInflatedSurfaceFileOut, veryInflatedSmoothCycles, veryInflatedSmoothStrength, veryInflatedSmoothIterations, veryInflatedSmoothInflationFactor); veryInflatedSurfaceFileOut->setSurfaceType(SurfaceTypeEnum::VERY_INFLATED); myProgress.setTask("Matching Bounding Boxes"); inflatedSurfaceFileOut->matchSurfaceBoundingBox(anatomicalSurfaceFile); myProgress.reportProgress(0.5);//report progress of non-subalgorithm computation only veryInflatedSurfaceFileOut->matchSurfaceBoundingBox(anatomicalSurfaceFile); myProgress.reportProgress(1.0);//this isn't really needed, happens automatically when algorithm ends, but for clarity } /** * @return Algorithm internal weight */ float AlgorithmSurfaceGenerateInflated::getAlgorithmInternalWeight() { /* * override this if needed, if the progress bar isn't smooth */ return 0.1f;//we very little inside this algorithm except call other algorithms } /** * @return Algorithm sub-algorithm weight */ float AlgorithmSurfaceGenerateInflated::getSubAlgorithmWeight() { /* * If you use a subalgorithm */ return AlgorithmSurfaceInflation::getAlgorithmWeight() * 7;//7 cycles in total - may deserve factoring in default number of iterations } connectome-workbench-1.2.3+git41-gc4c6c90/src/Algorithms/AlgorithmSurfaceGenerateInflated.h000066400000000000000000000041061300200146000314100ustar00rootroot00000000000000#ifndef __ALGORITHM_SURFACE_GENERATE_INFLATED_H__ #define __ALGORITHM_SURFACE_GENERATE_INFLATED_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 "AbstractAlgorithm.h" namespace caret { class SurfaceFile; class AlgorithmSurfaceGenerateInflated : public AbstractAlgorithm { private: AlgorithmSurfaceGenerateInflated(); protected: static float getSubAlgorithmWeight(); static float getAlgorithmInternalWeight(); public: AlgorithmSurfaceGenerateInflated(ProgressObject* myProgObj, const SurfaceFile* anatomicalSurfaceFile, SurfaceFile* inflatedSurfaceFileOut, SurfaceFile* veryInflatedSurfaceFileOut, const float iterationsScale); static OperationParameters* getParameters(); static void useParameters(OperationParameters* myParams, ProgressObject* myProgObj); static AString getCommandSwitch(); static AString getShortDescription(); }; typedef TemplateAutoOperation AutoAlgorithmSurfaceGenerateInflated; } // namespace #endif //__ALGORITHM_SURFACE_GENERATE_INFLATED_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Algorithms/AlgorithmSurfaceInflation.cxx000066400000000000000000000166351300200146000305170ustar00rootroot00000000000000 /*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 "AlgorithmSurfaceInflation.h" #include "AlgorithmSurfaceSmoothing.h" #include "AlgorithmException.h" #include "BoundingBox.h" #include "CaretAssert.h" #include "CaretLogger.h" #include "SurfaceFile.h" using namespace caret; /** * \class caret::AlgorithmSurfaceInflation * \brief SURFACE INFLATION * * Perfom surface inflation. */ /** * @return Command line switch */ AString AlgorithmSurfaceInflation::getCommandSwitch() { return "-surface-inflation"; } /** * @return Short description of algorithm */ AString AlgorithmSurfaceInflation::getShortDescription() { return "SURFACE INFLATION"; } /** * @return Parameters for algorithm */ OperationParameters* AlgorithmSurfaceInflation::getParameters() { OperationParameters* ret = new OperationParameters(); ret->addSurfaceParameter(1, "anatomical-surface-in", "the anatomical surface"); ret->addSurfaceParameter(2, "surface-in", "the surface file to inflate"); ret->addIntegerParameter(3, "number-of-smoothing-cycles", "number of smoothing cycles"); ret->addDoubleParameter(4, "smoothing-strength", "smoothing strength (ranges [0.0 - 1.0])"); ret->addIntegerParameter(5, "smoothing-iterations", "smoothing iterations"); ret->addDoubleParameter(6, "inflation-factor", "inflation factor"); ret->addSurfaceOutputParameter(7, "surface-out", "output surface file"); AString helpText = ("Inflate a surface by performing cycles that consist of smoothing " " followed by inflation (to correct shrinkage caused by smoothing)."); ret->setHelpText(helpText); return ret; } /** * Use Parameters and perform algorithm * @param myParams * Parameters for algorithm * @param myProgObj * The progress object * @throws * AlgorithmException if errors */ void AlgorithmSurfaceInflation::useParameters(OperationParameters* myParams, ProgressObject* myProgObj) { SurfaceFile* anatomicalSurfaceIn = myParams->getSurface(1); SurfaceFile* surfaceIn = myParams->getSurface(2); const int32_t cycles = myParams->getInteger(3); const float strength = myParams->getDouble(4); const int32_t iterations = myParams->getInteger(5); const float inflationFactor = myParams->getDouble(6); SurfaceFile* surfaceOut = myParams->getOutputSurface(7); /* * Constructs and executes the algorithm */ AlgorithmSurfaceInflation(myProgObj, anatomicalSurfaceIn, surfaceIn, surfaceOut, cycles, strength, iterations, inflationFactor); } /** * Constructor * * Calling the constructor will execute the algorithm * * @param myProgObj * Parameters for algorithm */ AlgorithmSurfaceInflation::AlgorithmSurfaceInflation(ProgressObject* myProgObj, const SurfaceFile* anatomicalSurfaceFile, const SurfaceFile* inputSurfaceFile, SurfaceFile* outputSurfaceFile, const int32_t cycles, const float strength, const int32_t iterations, const float inflationFactorIn) : AbstractAlgorithm(myProgObj) { std::vector subAlgProgress; if (myProgObj != NULL) { subAlgProgress.resize(cycles); for (int32_t i = 0; i < cycles; ++i) { subAlgProgress[i] = myProgObj->addAlgorithm(AlgorithmSurfaceSmoothing::getAlgorithmWeight()); } } /* * Sets the algorithm up to use the progress object, and will * finish the progress object automatically when the algorithm terminates */ LevelProgress myProgress(myProgObj, 1.0f, 0.1f);//lower the internal weight const float inflationFactor = inflationFactorIn - 1.0; *outputSurfaceFile = *inputSurfaceFile; outputSurfaceFile->translateToCenterOfMass(); const BoundingBox* anatomicalBoundingBox = anatomicalSurfaceFile->getBoundingBox(); const float anatomicalRangeX = anatomicalBoundingBox->getDifferenceX(); const float anatomicalRangeY = anatomicalBoundingBox->getDifferenceY(); const float anatomicalRangeZ = anatomicalBoundingBox->getDifferenceZ(); const int32_t numberOfNodes = outputSurfaceFile->getNumberOfNodes(); for (int iCycle = 0; iCycle < cycles; iCycle++) { /* * Smooth */ ProgressObject* subProgress = NULL; if (myProgObj != NULL) { subProgress = subAlgProgress[iCycle]; } AlgorithmSurfaceSmoothing(subProgress, outputSurfaceFile, outputSurfaceFile, strength, iterations); /* * Inflate */ float xyz[3]; for (int32_t iNode = 0; iNode < numberOfNodes; iNode++) { outputSurfaceFile->getCoordinate(iNode, xyz); const float x = xyz[0] / anatomicalRangeX; const float y = xyz[1] / anatomicalRangeY; const float z = xyz[2] / anatomicalRangeZ; const float radius = std::sqrt(x*x + y*y + z*z); const float scale = 1.0 + inflationFactor * (1.0 - radius); xyz[0] *= scale; xyz[1] *= scale; xyz[2] *= scale; outputSurfaceFile->setCoordinate(iNode, xyz); } myProgress.reportProgress(static_cast(iCycle +1) / static_cast(cycles)); } outputSurfaceFile->computeNormals(); } /** * @return Algorithm internal weight */ float AlgorithmSurfaceInflation::getAlgorithmInternalWeight() { /* * override this if needed, if the progress bar isn't smooth */ return 0.1f;//all this does internally is rescale the coordinates, lower the internal weight } /** * @return Algorithm sub-algorithm weight */ float AlgorithmSurfaceInflation::getSubAlgorithmWeight() { /* * If you use a subalgorithm */ return AlgorithmSurfaceSmoothing::getAlgorithmWeight(); } connectome-workbench-1.2.3+git41-gc4c6c90/src/Algorithms/AlgorithmSurfaceInflation.h000066400000000000000000000041671300200146000301410ustar00rootroot00000000000000#ifndef __ALGORITHM_SURFACE_INFLATION_H__ #define __ALGORITHM_SURFACE_INFLATION_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 "AbstractAlgorithm.h" namespace caret { class AlgorithmSurfaceInflation : public AbstractAlgorithm { private: AlgorithmSurfaceInflation(); protected: static float getSubAlgorithmWeight(); static float getAlgorithmInternalWeight(); public: AlgorithmSurfaceInflation(ProgressObject* myProgObj, const SurfaceFile* anatomicalSurfaceFile, const SurfaceFile* inputSurfaceFile, SurfaceFile* outputSurfaceFile, const int32_t cycles, const float strength, const int32_t iterations, const float inflationFactorIn); static OperationParameters* getParameters(); static void useParameters(OperationParameters* myParams, ProgressObject* myProgObj); static AString getCommandSwitch(); static AString getShortDescription(); }; typedef TemplateAutoOperation AutoAlgorithmSurfaceInflation; } // namespace #endif //__ALGORITHM_SURFACE_INFLATION_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Algorithms/AlgorithmSurfaceMatch.cxx000066400000000000000000000124731300200146000276240ustar00rootroot00000000000000 /*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 "CaretAssert.h" #include "CaretLogger.h" #include "AlgorithmSurfaceMatch.h" #include "AlgorithmException.h" #include "DataFileException.h" #include "SurfaceFile.h" using namespace caret; /** * \class caret::AlgorithmSurfaceMatch * \brief surface match * * */ /** * @return Command line switch */ AString AlgorithmSurfaceMatch::getCommandSwitch() { return "-surface-match"; } /** * @return Short description of algorithm */ AString AlgorithmSurfaceMatch::getShortDescription() { return "SURFACE MATCH"; } /** * @return Parameters for algorithm */ OperationParameters* AlgorithmSurfaceMatch::getParameters() { OperationParameters* ret = new OperationParameters(); ret->addSurfaceParameter(1, "Match Surface File", "Match (Reference) Surface"); ret->addSurfaceParameter(2, "Input Surface File", "File containing surface that will be transformed"); ret->addStringParameter(3, "Output Surface Name", "Surface File after transformation"); AString helpText = ("The Input Surface File will be transformed so that its coordinate " "ranges (bounding box) match that of the Match Surface File"); ret->setHelpText(helpText); return ret; } /** * Use Parameters and perform algorithm * @param myParams * Parameters for algorithm * @param myProgObj * The progress object * @throws * AlgorithmException if errors */ void AlgorithmSurfaceMatch::useParameters(OperationParameters* myParams, ProgressObject* myProgObj) { try { SurfaceFile* matchSurfaceFile = myParams->getSurface(1); SurfaceFile* surfaceFile = myParams->getSurface(2); const AString outputSurfaceFileName = myParams->getString(3); /* * Example parameter processing: * * Gets the surface with key 1 * SurfaceFile* mySurf = myParams->getSurface(1); * * Gets the output metric with key 2 * MetricFile* myMetricOut = myParams->getOutputMetric(2); * * Gets optional parameter with key 3 * OptionalParameter* columnSelect = myParams->getOptionalParameter(3); * int columnNum = -1; * if (columnSelect->m_present) { * columnNum = (int)myMetric->getMapIndexFromNameOrNumber(columnSelect->getString(1)); * if (columnNum < 0) { * throw AlgorithmException("invalid column specified"); * } * } */ /* * Constructs and executes the algorithm */ AlgorithmSurfaceMatch(myProgObj, matchSurfaceFile, surfaceFile); surfaceFile->writeFile(outputSurfaceFileName); } catch (const DataFileException& dfe) { throw AlgorithmException(dfe); } } /** * Constructor * * Calling the constructor will execute the algorithm * * @param myProgObj * Parameters for algorithm */ AlgorithmSurfaceMatch::AlgorithmSurfaceMatch(ProgressObject* myProgObj, const SurfaceFile* matchSurfaceFile, SurfaceFile* surfaceFile) : AbstractAlgorithm(myProgObj) { /* * Uncomment these if you use another algorithm inside here * * ProgressObject* subAlgProgress1 = NULL; * if (myProgObj != NULL) { * subAlgProgress1 = myProgObj->addAlgorithm(AlgorithmInsertNameHere::getAlgorithmWeight()); * } */ /* * Sets the algorithm up to use the progress object, and will * finish the progress object automatically when the algorithm terminates */ LevelProgress myProgress(myProgObj); surfaceFile->matchSurfaceBoundingBox(matchSurfaceFile); /* * How you say you are halfway done with the INTERNAL work of the algorithm * will report finished automatically when this function ends (myProgress goes out of scope, destructor triggers finish */ //myProgress.reportProgress(0.5f); } /** * @return Algorithm internal weight */ float AlgorithmSurfaceMatch::getAlgorithmInternalWeight() { /* * override this if needed, if the progress bar isn't smooth */ return 1.0f; } /** * @return Algorithm sub-algorithm weight */ float AlgorithmSurfaceMatch::getSubAlgorithmWeight() { /* * If you use a subalgorithm */ //return AlgorithmInsertNameHere::getAlgorithmWeight() return 0.0f; } connectome-workbench-1.2.3+git41-gc4c6c90/src/Algorithms/AlgorithmSurfaceMatch.h000066400000000000000000000034211300200146000272420ustar00rootroot00000000000000#ifndef __ALGORITHM_SURFACE_MATCH_H__ #define __ALGORITHM_SURFACE_MATCH_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 "AbstractAlgorithm.h" namespace caret { class AlgorithmSurfaceMatch : public AbstractAlgorithm { private: AlgorithmSurfaceMatch(); protected: static float getSubAlgorithmWeight(); static float getAlgorithmInternalWeight(); public: AlgorithmSurfaceMatch(ProgressObject* myProgObj, const SurfaceFile* matchSurfaceFile, SurfaceFile* surfaceFile); static OperationParameters* getParameters(); static void useParameters(OperationParameters* myParams, ProgressObject* myProgObj); static AString getCommandSwitch(); static AString getShortDescription(); }; typedef TemplateAutoOperation AutoAlgorithmSurfaceMatch; } // namespace #endif //__ALGORITHM_SURFACE_MATCH_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Algorithms/AlgorithmSurfaceModifySphere.cxx000066400000000000000000000135541300200146000311670ustar00rootroot00000000000000/*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 "AlgorithmSurfaceModifySphere.h" #include "AlgorithmException.h" #include "BoundingBox.h" #include "CaretLogger.h" #include "SurfaceFile.h" #include "Vector3D.h" using namespace caret; using namespace std; AString AlgorithmSurfaceModifySphere::getCommandSwitch() { return "-surface-modify-sphere"; } AString AlgorithmSurfaceModifySphere::getShortDescription() { return "CHANGE RADIUS AND OPTIONALLY RECENTER A SPHERE"; } OperationParameters* AlgorithmSurfaceModifySphere::getParameters() { OperationParameters* ret = new OperationParameters(); ret->addSurfaceParameter(1, "sphere-in", "the sphere to modify"); ret->addDoubleParameter(2, "radius", "the radius the output sphere should have"); ret->addSurfaceOutputParameter(3, "sphere-out", "the output sphere"); ret->createOptionalParameter(4, "-recenter", "recenter the sphere by means of the bounding box"); //TODO: add option to supress warnings? ret->setHelpText( AString("This command may be useful if you have used -surface-resample to resample a sphere, which can suffer from problems generally not present in ") + "-surface-sphere-project-unproject. If the sphere should already be centered around the origin, using -recenter may still shift it slightly before changing the radius, " + "which is likely to be undesireable.\n\n" + "If is not close to spherical, or not centered around the origin and -recenter is not used, a warning is printed." ); return ret; } void AlgorithmSurfaceModifySphere::useParameters(OperationParameters* myParams, ProgressObject* myProgObj) { SurfaceFile* mySphere = myParams->getSurface(1); float newRadius = (float)myParams->getDouble(2); SurfaceFile* outSphere = myParams->getOutputSurface(3); bool recenter = myParams->getOptionalParameter(4)->m_present; AlgorithmSurfaceModifySphere(myProgObj, mySphere, newRadius, outSphere, recenter); } AlgorithmSurfaceModifySphere::AlgorithmSurfaceModifySphere(ProgressObject* myProgObj, const SurfaceFile* mySphere, const float& newRadius, SurfaceFile* outSphere, const bool& recenter) : AbstractAlgorithm(myProgObj) { LevelProgress myProgress(myProgObj); bool originVertexWarned = false, nanWarned = false; Vector3D center;//initializes to the origin if (recenter) { const BoundingBox* mybb = mySphere->getBoundingBox(); mybb->getCenter(center); } int numNodes = mySphere->getNumberOfNodes(); CaretAssert(numNodes > 1); int numNodes3 = numNodes * 3; const float* coordData = mySphere->getCoordinateData(); float mindist = 0.0f, maxdist = 0.0f; bool first = true; const float TOLERANCE = 1.05f;//5% is a very loose tolerance, we expect some odd spheres *outSphere = *mySphere; vector outCoords(numNodes3); for (int i = 0; i < numNodes3; i += 3) { Vector3D recenterCoord = Vector3D(coordData + i) - center; float tempf = recenterCoord.length(); if (tempf == 0.0f) { outCoords[i] = newRadius; outCoords[i + 1] = 0.0f; outCoords[i + 2] = 0.0f; if (!originVertexWarned) { CaretLogWarning("found at least one vertex at origin, moved to +x position"); originVertexWarned = true; } } else if (tempf != tempf) { outCoords[i] = newRadius; outCoords[i + 1] = 0.0f; outCoords[i + 2] = 0.0f; if (!nanWarned) { CaretLogWarning("found at least one NaN vertex, moved to +x position"); nanWarned = true; } } else { Vector3D outCoord = recenterCoord * (newRadius / tempf); outCoords[i] = outCoord[0]; outCoords[i + 1] = outCoord[1]; outCoords[i + 2] = outCoord[2]; if (first) { first = false; mindist = tempf; maxdist = tempf; } else { if (tempf < mindist) { mindist = tempf; } if (tempf > maxdist) { maxdist = tempf; } } } } if (first) { throw AlgorithmException("all vertices were located at the origin, aborting"); } else { if (mindist * TOLERANCE < maxdist) { if (recenter) { CaretLogWarning("input sphere is unusually irregular, inspect the input"); } else { CaretLogWarning("input sphere is either unusually irregular or not centered, inspect the input"); } } } outSphere->setCoordinates(outCoords.data()); } float AlgorithmSurfaceModifySphere::getAlgorithmInternalWeight() { return 1.0f;//override this if needed, if the progress bar isn't smooth } float AlgorithmSurfaceModifySphere::getSubAlgorithmWeight() { //return AlgorithmInsertNameHere::getAlgorithmWeight();//if you use a subalgorithm return 0.0f; } connectome-workbench-1.2.3+git41-gc4c6c90/src/Algorithms/AlgorithmSurfaceModifySphere.h000066400000000000000000000033721300200146000306110ustar00rootroot00000000000000#ifndef __ALGORITHM_SURFACE_MODIFY_SPHERE_H__ #define __ALGORITHM_SURFACE_MODIFY_SPHERE_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 "AbstractAlgorithm.h" namespace caret { class AlgorithmSurfaceModifySphere : public AbstractAlgorithm { AlgorithmSurfaceModifySphere(); protected: static float getSubAlgorithmWeight(); static float getAlgorithmInternalWeight(); public: AlgorithmSurfaceModifySphere(ProgressObject* myProgObj, const SurfaceFile* mySphere, const float& newRadius, SurfaceFile* outSphere, const bool& recenter = false); static OperationParameters* getParameters(); static void useParameters(OperationParameters* myParams, ProgressObject* myProgObj); static AString getCommandSwitch(); static AString getShortDescription(); }; typedef TemplateAutoOperation AutoAlgorithmSurfaceModifySphere; } #endif //__ALGORITHM_SURFACE_MODIFY_SPHERE_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Algorithms/AlgorithmSurfaceResample.cxx000066400000000000000000000214221300200146000303320ustar00rootroot00000000000000/*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 "AlgorithmSurfaceResample.h" #include "AlgorithmException.h" #include "CaretLogger.h" #include "GiftiMetaData.h" #include "MetricFile.h" #include "SurfaceFile.h" #include "SurfaceResamplingHelper.h" using namespace caret; using namespace std; AString AlgorithmSurfaceResample::getCommandSwitch() { return "-surface-resample"; } AString AlgorithmSurfaceResample::getShortDescription() { return "RESAMPLE A SURFACE TO A DIFFERENT MESH"; } OperationParameters* AlgorithmSurfaceResample::getParameters() { OperationParameters* ret = new OperationParameters(); ret->addSurfaceParameter(1, "surface-in", "the surface file to resample"); ret->addSurfaceParameter(2, "current-sphere", "a sphere surface with the mesh that the input surface is currently on"); ret->addSurfaceParameter(3, "new-sphere", "a sphere surface that is in register with and has the desired output mesh"); ret->addStringParameter(4, "method", "the method name"); ret->addSurfaceOutputParameter(5, "surface-out", "the output surface file"); OptionalParameter* areaSurfsOpt = ret->createOptionalParameter(6, "-area-surfs", "specify surfaces to do vertex area correction based on"); areaSurfsOpt->addSurfaceParameter(1, "current-area", "a relevant surface with mesh"); areaSurfsOpt->addSurfaceParameter(2, "new-area", "a relevant surface with mesh"); OptionalParameter* areaMetricsOpt = ret->createOptionalParameter(7, "-area-metrics", "specify vertex area metrics to do area correction based on"); areaMetricsOpt->addMetricParameter(1, "current-area", "a metric file with vertex areas for mesh"); areaMetricsOpt->addMetricParameter(2, "new-area", "a metric file with vertex areas for mesh"); AString myHelpText = AString("Resamples a surface file, given two spherical surfaces that are in register. ") + "If ADAP_BARY_AREA is used, exactly one of -area-surfs or -area-metrics must be specified. " + "This method is not generally recommended for surface resampling, but is provided for completeness.\n\n" + "The BARYCENTRIC method is generally recommended for anatomical surfaces, in order to minimize smoothing.\n\n" + "For cut surfaces (including flatmaps), use -surface-cut-resample.\n\n" + "Instead of resampling a spherical surface, the -surface-sphere-project-unproject command is recommended.\n\n" + "The argument must be one of the following:\n\n"; vector allEnums; SurfaceResamplingMethodEnum::getAllEnums(allEnums); for (int i = 0; i < (int)allEnums.size(); ++i) { myHelpText += SurfaceResamplingMethodEnum::toName(allEnums[i]) + "\n"; } ret->setHelpText(myHelpText); return ret; } void AlgorithmSurfaceResample::useParameters(OperationParameters* myParams, ProgressObject* myProgObj) { SurfaceFile* surfaceIn = myParams->getSurface(1); SurfaceFile* curSphere = myParams->getSurface(2); SurfaceFile* newSphere = myParams->getSurface(3); bool ok = false; SurfaceResamplingMethodEnum::Enum myMethod = SurfaceResamplingMethodEnum::fromName(myParams->getString(4), &ok); if (!ok) { throw AlgorithmException("invalid method name"); } SurfaceFile* surfaceOut = myParams->getOutputSurface(5); MetricFile* curAreas = NULL, *newAreas = NULL; MetricFile curAreasTemp, newAreasTemp; OptionalParameter* areaSurfsOpt = myParams->getOptionalParameter(6); if (areaSurfsOpt->m_present) { switch(myMethod) { case SurfaceResamplingMethodEnum::BARYCENTRIC: CaretLogInfo("This method does not use area correction, -area-surfs is not needed"); break; default: break; } vector nodeAreasTemp; SurfaceFile* curAreaSurf = areaSurfsOpt->getSurface(1); SurfaceFile* newAreaSurf = areaSurfsOpt->getSurface(2); curAreaSurf->computeNodeAreas(nodeAreasTemp); curAreasTemp.setNumberOfNodesAndColumns(curAreaSurf->getNumberOfNodes(), 1); curAreasTemp.setValuesForColumn(0, nodeAreasTemp.data()); curAreas = &curAreasTemp; newAreaSurf->computeNodeAreas(nodeAreasTemp); newAreasTemp.setNumberOfNodesAndColumns(newAreaSurf->getNumberOfNodes(), 1); newAreasTemp.setValuesForColumn(0, nodeAreasTemp.data()); newAreas = &newAreasTemp; } OptionalParameter* areaMetricsOpt = myParams->getOptionalParameter(7); if (areaMetricsOpt->m_present) { if (areaSurfsOpt->m_present) { throw AlgorithmException("only one of -area-surfs and -area-metrics can be specified"); } switch(myMethod) { case SurfaceResamplingMethodEnum::BARYCENTRIC: CaretLogInfo("This method does not use area correction, -area-metrics is not needed"); break; default: break; } curAreas = areaMetricsOpt->getMetric(1); newAreas = areaMetricsOpt->getMetric(2); } AlgorithmSurfaceResample(myProgObj, surfaceIn, curSphere, newSphere, myMethod, surfaceOut, curAreas, newAreas); } AlgorithmSurfaceResample::AlgorithmSurfaceResample(ProgressObject* myProgObj, const SurfaceFile* surfaceIn, const SurfaceFile* curSphere, const SurfaceFile* newSphere, const SurfaceResamplingMethodEnum::Enum& myMethod, SurfaceFile* surfaceOut, const MetricFile* curAreas, const MetricFile* newAreas) : AbstractAlgorithm(myProgObj) { LevelProgress myProgress(myProgObj); if (surfaceIn->getNumberOfNodes() != curSphere->getNumberOfNodes()) throw AlgorithmException("input surface has different number of nodes than input sphere"); const float* curAreaData = NULL, *newAreaData = NULL; switch (myMethod) { case SurfaceResamplingMethodEnum::BARYCENTRIC: break; default: if (curAreas == NULL || newAreas == NULL) throw AlgorithmException("specified method does area correction, but no vertex area data given"); if (curSphere->getNumberOfNodes() != curAreas->getNumberOfNodes()) throw AlgorithmException("current vertex area data has different number of nodes than current sphere"); if (newSphere->getNumberOfNodes() != newAreas->getNumberOfNodes()) throw AlgorithmException("new vertex area data has different number of nodes than new sphere"); curAreaData = curAreas->getValuePointerForColumn(0); newAreaData = newAreas->getValuePointerForColumn(0); } int numNewNodes = newSphere->getNumberOfNodes(); GiftiMetaData savedMD; GiftiMetaData* provenanceMD = surfaceOut->getFileMetaData();//save provenance, since we are using operator= on surface as a hack around setting the topology if (provenanceMD != NULL) { savedMD = *provenanceMD;//put it into a local instance, because operator= will probably change the one the pointer refers to } *surfaceOut = *newSphere; provenanceMD = surfaceOut->getFileMetaData();//in case the pointer changes anyway if (provenanceMD != NULL) { *provenanceMD = savedMD;//put it back } surfaceOut->setStructure(surfaceIn->getStructure()); surfaceOut->setSecondaryType(surfaceIn->getSecondaryType()); surfaceOut->setSurfaceType(surfaceIn->getSurfaceType()); vector coordScratch(numNewNodes * 3, 0.0f); SurfaceResamplingHelper myHelp(myMethod, curSphere, newSphere, curAreaData, newAreaData); myHelp.resample3DCoord(surfaceIn->getCoordinateData(), coordScratch.data()); surfaceOut->setCoordinates(coordScratch.data()); } float AlgorithmSurfaceResample::getAlgorithmInternalWeight() { return 1.0f;//override this if needed, if the progress bar isn't smooth } float AlgorithmSurfaceResample::getSubAlgorithmWeight() { //return AlgorithmInsertNameHere::getAlgorithmWeight();//if you use a subalgorithm return 0.0f; } connectome-workbench-1.2.3+git41-gc4c6c90/src/Algorithms/AlgorithmSurfaceResample.h000066400000000000000000000036501300200146000277620ustar00rootroot00000000000000#ifndef __ALGORITHM_SURFACE_RESAMPLE_H__ #define __ALGORITHM_SURFACE_RESAMPLE_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 "AbstractAlgorithm.h" #include "SurfaceResamplingMethodEnum.h" namespace caret { class AlgorithmSurfaceResample : public AbstractAlgorithm { AlgorithmSurfaceResample(); protected: static float getSubAlgorithmWeight(); static float getAlgorithmInternalWeight(); public: AlgorithmSurfaceResample(ProgressObject* myProgObj, const SurfaceFile* surfaceIn, const SurfaceFile* curSphere, const SurfaceFile* newSphere, const SurfaceResamplingMethodEnum::Enum& myMethod, SurfaceFile* surfaceOut, const MetricFile* curAreaSurf = NULL, const MetricFile* newAreaSurf = NULL); static OperationParameters* getParameters(); static void useParameters(OperationParameters* myParams, ProgressObject* myProgObj); static AString getCommandSwitch(); static AString getShortDescription(); }; typedef TemplateAutoOperation AutoAlgorithmSurfaceResample; } #endif //__ALGORITHM_SURFACE_RESAMPLE_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Algorithms/AlgorithmSurfaceSmoothing.cxx000066400000000000000000000240051300200146000305310ustar00rootroot00000000000000 /*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 "CaretAssert.h" #include "CaretLogger.h" #include "AlgorithmSurfaceSmoothing.h" #include "AlgorithmException.h" #include "MathFunctions.h" #include "SurfaceFile.h" #include "TopologyHelper.h" using namespace caret; /** * \class caret::AlgorithmSurfaceSmoothing * \brief SURFACE SMOOTHING * * Smooths a surface. */ /** * @return Command line switch */ AString AlgorithmSurfaceSmoothing::getCommandSwitch() { return "-surface-smoothing"; } /** * @return Short description of algorithm */ AString AlgorithmSurfaceSmoothing::getShortDescription() { return "SURFACE SMOOTHING"; } /** * @return Parameters for algorithm */ OperationParameters* AlgorithmSurfaceSmoothing::getParameters() { OperationParameters* ret = new OperationParameters(); ret->addSurfaceParameter(1, "surface-in", "the surface file to smooth"); ret->addDoubleParameter(2, "smoothing-strength", "smoothing strength (ranges [0.0 - 1.0])"); ret->addIntegerParameter(3, "smoothing-iterations", "smoothing iterations"); ret->addSurfaceOutputParameter(4, "surface-out", "output surface file"); AString helpText = ("Smooths a surface by averaging vertex coordinates with those of the neighboring vertices."); ret->setHelpText(helpText); return ret; } /** * Use Parameters and perform algorithm * @param myParams * Parameters for algorithm * @param myProgObj * The progress object * @throws * AlgorithmException if errors */ void AlgorithmSurfaceSmoothing::useParameters(OperationParameters* myParams, ProgressObject* myProgObj) { SurfaceFile* surfaceIn = myParams->getSurface(1); const float strength = myParams->getDouble(2); const int32_t iterations = myParams->getInteger(3); SurfaceFile* surfaceOut = myParams->getOutputSurface(4); /* * Constructs and executes the algorithm */ AlgorithmSurfaceSmoothing(myProgObj, surfaceIn, surfaceOut, strength, iterations); } /** * Constructor * * Calling the constructor will execute the algorithm * * @param myProgObj * Parameters for algorithm */ AlgorithmSurfaceSmoothing::AlgorithmSurfaceSmoothing(ProgressObject* myProgObj, const SurfaceFile* inputSurfaceFile, SurfaceFile* outputSurfaceFile, const float strength, const int32_t iterations) : AbstractAlgorithm(myProgObj) { if ((strength < 0.0) || (strength > 1.0)) { throw AlgorithmException("Invalid smoothing strength outside [0.0, 1.0]: " + QString::number(strength, 'f', 5)); } if (iterations <= 0) { throw AlgorithmException("Invalid iterations value [1, infinity]: " + QString::number(iterations)); } /* * Sets the algorithm up to use the progress object, and will * finish the progress object automatically when the algorithm terminates */ LevelProgress myProgress(myProgObj); *outputSurfaceFile = *inputSurfaceFile; CaretPointer myTopoHelp = outputSurfaceFile->getTopologyHelper(true); const int32_t numNodes = outputSurfaceFile->getNumberOfNodes(); if (numNodes <= 0) { return; } /* * Storage for coordinates, input and output of each iteration */ std::vector coordsIn(numNodes * 3); std::vector coordsOut(numNodes * 3); /* * Copy coordinates from surface */ for (int32_t i = 0; i < numNodes; i++) { const float* xyz = outputSurfaceFile->getCoordinate(i); const int32_t i3 = i * 3; coordsIn[i3] = xyz[0]; coordsIn[i3+1] = xyz[1]; coordsIn[i3+2] = xyz[2]; coordsOut[i3] = xyz[0]; coordsOut[i3+1] = xyz[1]; coordsOut[i3+2] = xyz[2]; } std::vector triangleAreas(100); std::vector triangleCenters(100*3); const float inverseStrength = 1.0 - strength; /* * Perform the requested number of iterations */ for (int32_t iter = 1; iter <= iterations; iter++) { /* * Copy coordinates from previous iteration */ if (iter > 1) { coordsIn = coordsOut; } /* * Process each node */ for (int32_t iNode = 0; iNode < numNodes; iNode++) { /* * Get node's neighbors */ int32_t numNeighbors = 0; const int32_t* neighbors = myTopoHelp->getNodeNeighbors(iNode, numNeighbors); if (numNeighbors < 2) { coordsOut[iNode*3] = coordsIn[iNode*3]; coordsOut[iNode*3+1] = coordsIn[iNode*3+1]; coordsOut[iNode*3+2] = coordsIn[iNode*3+2]; } else { /* * Ensure adequate space for triangle areas and center coordinate */ if (numNeighbors > static_cast(triangleAreas.size())) { triangleAreas.resize(numNeighbors); triangleCenters.resize(numNeighbors * 3); } double totalArea = 0.0; /* * Average node with its neighbors */ for (int jn = 0; jn < numNeighbors; jn++) { /* * Get two consecutive neighbors */ const int32_t n1 = neighbors[jn]; int nextNeighborIndex = jn + 1; if (nextNeighborIndex >= numNeighbors) { nextNeighborIndex = 0; } const int32_t n2 = neighbors[nextNeighborIndex]; /* * Coordinates of nodes and neighbors */ const float* c1 = &coordsIn[iNode*3]; const float* c2 = &coordsIn[n1*3]; const float* c3 = &coordsIn[n2*3]; const float area = MathFunctions::triangleArea(c1, c2, c3); /* * Area of triangle formed by node and neighbors */ triangleAreas[jn] = area; totalArea += area; /* * Average of nodes that form triangle */ for (int32_t k = 0; k < 3; k++) { triangleCenters[jn*3+k] = (c1[k] + c2[k] + c3[k]) / 3.0; } } /* * Influence of neighbors */ float neighborAverageX = 0.0; float neighborAverageY = 0.0; float neighborAverageZ = 0.0; for (int j = 0; j < numNeighbors; j++) { if (triangleAreas[j] > 0.0) { const float weight = triangleAreas[j] / totalArea; neighborAverageX += (weight * triangleCenters[j*3]); neighborAverageY += (weight * triangleCenters[j*3+1]); neighborAverageZ += (weight * triangleCenters[j*3+2]); } } /* * Update coordinates */ coordsOut[iNode*3] = ((coordsIn[iNode*3] * inverseStrength) + (neighborAverageX * strength)); coordsOut[iNode*3+1] = ((coordsIn[iNode*3+1] * inverseStrength) + (neighborAverageY * strength)); coordsOut[iNode*3+2] = ((coordsIn[iNode*3+2] * inverseStrength) + (neighborAverageZ * strength)); } } /* * Update progress */ const float percentDone = (static_cast(iter) / static_cast(iterations)); myProgress.reportProgress(percentDone);//give continuous updates, if it slows things down we can reduce the resolution in the progress framework } /* * Copy coordinates into surface */ outputSurfaceFile->setCoordinates(&coordsOut[0]); myProgress.reportProgress(1.0f); } /** * @return Algorithm internal weight */ float AlgorithmSurfaceSmoothing::getAlgorithmInternalWeight() { /* * override this if needed, if the progress bar isn't smooth */ return 1.0f; } /** * @return Algorithm sub-algorithm weight */ float AlgorithmSurfaceSmoothing::getSubAlgorithmWeight() { /* * If you use a subalgorithm */ //return AlgorithmInsertNameHere::getAlgorithmWeight() return 0.0f; } connectome-workbench-1.2.3+git41-gc4c6c90/src/Algorithms/AlgorithmSurfaceSmoothing.h000066400000000000000000000036631300200146000301650ustar00rootroot00000000000000#ifndef __ALGORITHM_SURFACE_SMOOTHING_H__ #define __ALGORITHM_SURFACE_SMOOTHING_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 "AbstractAlgorithm.h" namespace caret { class AlgorithmSurfaceSmoothing : public AbstractAlgorithm { private: AlgorithmSurfaceSmoothing(); protected: static float getSubAlgorithmWeight(); static float getAlgorithmInternalWeight(); public: AlgorithmSurfaceSmoothing(ProgressObject* myProgObj, const SurfaceFile* inputSurfaceFile, SurfaceFile* outputSurfaceFile, const float strength, const int32_t iterations); static OperationParameters* getParameters(); static void useParameters(OperationParameters* myParams, ProgressObject* myProgObj); static AString getCommandSwitch(); static AString getShortDescription(); }; typedef TemplateAutoOperation AutoAlgorithmSurfaceSmoothing; } // namespace #endif //__ALGORITHM_SURFACE_SMOOTHING_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Algorithms/AlgorithmSurfaceSphereProjectUnproject.cxx000066400000000000000000000151531300200146000332350ustar00rootroot00000000000000/*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 "AlgorithmSurfaceSphereProjectUnproject.h" #include "AlgorithmException.h" #include "CaretOMP.h" #include "SignedDistanceHelper.h" #include "SurfaceFile.h" #include "Vector3D.h" #include using namespace caret; using namespace std; AString AlgorithmSurfaceSphereProjectUnproject::getCommandSwitch() { return "-surface-sphere-project-unproject"; } AString AlgorithmSurfaceSphereProjectUnproject::getShortDescription() { return "DEFORM A SPHERE ACCORDING TO A REGISTRATION"; } OperationParameters* AlgorithmSurfaceSphereProjectUnproject::getParameters() { OperationParameters* ret = new OperationParameters(); ret->addSurfaceParameter(1, "sphere-in", "the sphere with the desired output mesh"); ret->addSurfaceParameter(2, "sphere-project-to", "a sphere that aligns with sphere-in"); ret->addSurfaceParameter(3, "sphere-unproject-from", "sphere-project-to deformed to the output space"); ret->addSurfaceOutputParameter(4, "sphere-out", "the output sphere"); ret->setHelpText( AString("Each vertex of is projected to to obtain barycentric weights, which are then used to unproject ") + "from . This results in a sphere with the topology of , but coordinates shifted by the deformation between " + " and . and must have the same topology as each other, " + "but may have different topology." ); return ret; } void AlgorithmSurfaceSphereProjectUnproject::useParameters(OperationParameters* myParams, ProgressObject* myProgObj) { SurfaceFile* sphereIn = myParams->getSurface(1); SurfaceFile* projectSphere = myParams->getSurface(2); SurfaceFile* unprojectSphere = myParams->getSurface(3); SurfaceFile* sphereOut = myParams->getOutputSurface(4); AlgorithmSurfaceSphereProjectUnproject(myProgObj, sphereIn, projectSphere, unprojectSphere, sphereOut);//executes the algorithm } AlgorithmSurfaceSphereProjectUnproject::AlgorithmSurfaceSphereProjectUnproject(ProgressObject* myProgObj, const SurfaceFile* sphereIn, const SurfaceFile* projectSphere, const SurfaceFile* unprojectSphere, SurfaceFile* sphereOut) : AbstractAlgorithm(myProgObj) { LevelProgress myProgress(myProgObj); if (!projectSphere->hasNodeCorrespondence(*unprojectSphere)) throw AlgorithmException("projection sphere and unprojection sphere do not have vertex correspondence"); if (!checkSphere(sphereIn) || !checkSphere(projectSphere) || !checkSphere(unprojectSphere)) throw AlgorithmException("all inputs must be spheres centered around the origin"); SurfaceFile inMod = *sphereIn, projectMod = *projectSphere, unprojectMod = *unprojectSphere; changeRadius(100.0f, &inMod); changeRadius(100.0f, &projectMod); changeRadius(100.0f, &unprojectMod); const float* unprojectCoords = unprojectMod.getCoordinateData(); const float* inCoords = inMod.getCoordinateData(); int numNodes = sphereIn->getNumberOfNodes(); vector outCoords(numNodes * 3); *sphereOut = *sphereIn; sphereOut->setStructure(unprojectSphere->getStructure()); CaretPointer myHelper = projectMod.getSignedDistanceHelper(); for (int i = 0; i < numNodes; ++i) { int i3 = i * 3; BarycentricInfo myInfo; myHelper->barycentricWeights(inCoords + i3, myInfo); Vector3D outCoord = myInfo.baryWeights[0] * Vector3D(unprojectCoords + myInfo.nodes[0] * 3) + myInfo.baryWeights[1] * Vector3D(unprojectCoords + myInfo.nodes[1] * 3) + myInfo.baryWeights[2] * Vector3D(unprojectCoords + myInfo.nodes[2] * 3); outCoords[i3] = outCoord[0]; outCoords[i3 + 1] = outCoord[1]; outCoords[i3 + 2] = outCoord[2]; } sphereOut->setCoordinates(outCoords.data()); changeRadius(100.0f, sphereOut); } bool AlgorithmSurfaceSphereProjectUnproject::checkSphere(const SurfaceFile* surface) { int numNodes = surface->getNumberOfNodes(); CaretAssert(numNodes > 1); int numNodes3 = numNodes * 3; const float* coordData = surface->getCoordinateData(); float mindist = Vector3D(coordData).length(); if (mindist != mindist) throw CaretException("found NaN coordinate in an input sphere"); float maxdist = mindist; const float TOLERANCE = 1.001f; for (int i = 3; i < numNodes3; i += 3) { float tempf = Vector3D(coordData + i).length(); if (tempf != tempf) throw CaretException("found NaN coordinate in an input sphere"); if (tempf < mindist) { mindist = tempf; } if (tempf > maxdist) { maxdist = tempf; } } return (mindist * TOLERANCE > maxdist); } void AlgorithmSurfaceSphereProjectUnproject::changeRadius(const float& radius, SurfaceFile* inout) { int numNodes = inout->getNumberOfNodes(); int numNodes3 = numNodes * 3; vector newCoordData(numNodes3); const float* oldCoordData = inout->getCoordinateData(); #pragma omp CARET_PARFOR schedule(dynamic) for (int i = 0; i < numNodes3; i += 3) { Vector3D tempvec = Vector3D(oldCoordData + i).normal() * radius; newCoordData[i] = tempvec[0]; newCoordData[i + 1] = tempvec[1]; newCoordData[i + 2] = tempvec[2]; } inout->setCoordinates(newCoordData.data()); } float AlgorithmSurfaceSphereProjectUnproject::getAlgorithmInternalWeight() { return 1.0f;//override this if needed, if the progress bar isn't smooth } float AlgorithmSurfaceSphereProjectUnproject::getSubAlgorithmWeight() { //return AlgorithmInsertNameHere::getAlgorithmWeight();//if you use a subalgorithm return 0.0f; } connectome-workbench-1.2.3+git41-gc4c6c90/src/Algorithms/AlgorithmSurfaceSphereProjectUnproject.h000066400000000000000000000040061300200146000326550ustar00rootroot00000000000000#ifndef __ALGORITHM_SURFACE_SPHERE_PROJECT_UNPROJECT_H__ #define __ALGORITHM_SURFACE_SPHERE_PROJECT_UNPROJECT_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 "AbstractAlgorithm.h" namespace caret { class AlgorithmSurfaceSphereProjectUnproject : public AbstractAlgorithm { AlgorithmSurfaceSphereProjectUnproject(); bool checkSphere(const SurfaceFile* surface); void changeRadius(const float& radius, SurfaceFile* inout); protected: static float getSubAlgorithmWeight(); static float getAlgorithmInternalWeight(); public: AlgorithmSurfaceSphereProjectUnproject(ProgressObject* myProgObj, const SurfaceFile* sphereIn, const SurfaceFile* projectSphere, const SurfaceFile* unprojectSphere, SurfaceFile* sphereOut); static OperationParameters* getParameters(); static void useParameters(OperationParameters* myParams, ProgressObject* myProgObj); static AString getCommandSwitch(); static AString getShortDescription(); }; typedef TemplateAutoOperation AutoAlgorithmSurfaceSphereProjectUnproject; } #endif //__ALGORITHM_SURFACE_SPHERE_PROJECT_UNPROJECT_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Algorithms/AlgorithmSurfaceToSurface3dDistance.cxx000066400000000000000000000105451300200146000323630ustar00rootroot00000000000000/*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 "AlgorithmSurfaceToSurface3dDistance.h" #include "AlgorithmException.h" #include "MetricFile.h" #include "SurfaceFile.h" #include "Vector3D.h" using namespace caret; using namespace std; AString AlgorithmSurfaceToSurface3dDistance::getCommandSwitch() { return "-surface-to-surface-3d-distance"; } AString AlgorithmSurfaceToSurface3dDistance::getShortDescription() { return "COMPUTE DISTANCE BETWEEN CORRESPONDING VERTICES"; } OperationParameters* AlgorithmSurfaceToSurface3dDistance::getParameters() { OperationParameters* ret = new OperationParameters(); ret->addSurfaceParameter(1, "surface-comp", "the surface to compare to the reference"); ret->addSurfaceParameter(2, "surface-ref", "the surface to use as the reference"); ret->addMetricOutputParameter(3, "dists-out", "the output distances"); OptionalParameter* vectorOpt = ret->createOptionalParameter(4, "-vectors", "output the displacement vectors"); vectorOpt->addMetricOutputParameter(1, "vectors-out", "the output vectors"); ret->setHelpText( AString("Computes the vector difference between the vertices of each surface with the same index, as (comp - ref), ") + "and output the magnitudes, and optionally the displacement vectors." ); return ret; } void AlgorithmSurfaceToSurface3dDistance::useParameters(OperationParameters* myParams, ProgressObject* myProgObj) { SurfaceFile* myCompSurf = myParams->getSurface(1); SurfaceFile* myRefSurf = myParams->getSurface(2); MetricFile* distsOut = myParams->getOutputMetric(3); MetricFile* vectorsOut = NULL; OptionalParameter* vectorOpt = myParams->getOptionalParameter(4); if (vectorOpt->m_present) { vectorsOut = vectorOpt->getOutputMetric(1); } AlgorithmSurfaceToSurface3dDistance(myProgObj, myCompSurf, myRefSurf, distsOut, vectorsOut); } AlgorithmSurfaceToSurface3dDistance::AlgorithmSurfaceToSurface3dDistance(ProgressObject* myProgObj, const SurfaceFile* myCompSurf, const SurfaceFile* myRefSurf, MetricFile* distsOut, MetricFile* vectorsOut) : AbstractAlgorithm(myProgObj) { LevelProgress myProgress(myProgObj); if (!myCompSurf->hasNodeCorrespondence(*myRefSurf)) { throw AlgorithmException("input surfaces must have node correspondence"); } int numNodes = myCompSurf->getNumberOfNodes(); distsOut->setNumberOfNodesAndColumns(numNodes, 1); distsOut->setStructure(myCompSurf->getStructure()); distsOut->setMapName(0, "distance"); if (vectorsOut != NULL) { vectorsOut->setNumberOfNodesAndColumns(numNodes, 3); vectorsOut->setStructure(myCompSurf->getStructure()); vectorsOut->setMapName(0, "x displacement"); vectorsOut->setMapName(1, "y displacement"); vectorsOut->setMapName(2, "z displacement"); } for (int i = 0; i < numNodes; ++i) { Vector3D ref = myRefSurf->getCoordinate(i); Vector3D comp = myCompSurf->getCoordinate(i); Vector3D diff = comp - ref; distsOut->setValue(i, 0, diff.length()); if (vectorsOut != NULL) { vectorsOut->setValue(i, 0, diff[0]); vectorsOut->setValue(i, 1, diff[1]); vectorsOut->setValue(i, 2, diff[2]); } } } float AlgorithmSurfaceToSurface3dDistance::getAlgorithmInternalWeight() { return 1.0f;//override this if needed, if the progress bar isn't smooth } float AlgorithmSurfaceToSurface3dDistance::getSubAlgorithmWeight() { //return AlgorithmInsertNameHere::getAlgorithmWeight();//if you use a subalgorithm return 0.0f; } connectome-workbench-1.2.3+git41-gc4c6c90/src/Algorithms/AlgorithmSurfaceToSurface3dDistance.h000066400000000000000000000034771300200146000320160ustar00rootroot00000000000000#ifndef __ALGORITHM_SURFACE_TO_SURFACE_3D_DISTANCE_H__ #define __ALGORITHM_SURFACE_TO_SURFACE_3D_DISTANCE_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 "AbstractAlgorithm.h" namespace caret { class AlgorithmSurfaceToSurface3dDistance : public AbstractAlgorithm { AlgorithmSurfaceToSurface3dDistance(); protected: static float getSubAlgorithmWeight(); static float getAlgorithmInternalWeight(); public: AlgorithmSurfaceToSurface3dDistance(ProgressObject* myProgObj, const SurfaceFile* myCompSurf, const SurfaceFile* myRefSurf, MetricFile* distsOut, MetricFile* vectorsOut = NULL); static OperationParameters* getParameters(); static void useParameters(OperationParameters* myParams, ProgressObject* myProgObj); static AString getCommandSwitch(); static AString getShortDescription(); }; typedef TemplateAutoOperation AutoAlgorithmSurfaceToSurface3dDistance; } #endif //__ALGORITHM_SURFACE_TO_SURFACE_3D_DISTANCE_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Algorithms/AlgorithmSurfaceWedgeVolume.cxx000066400000000000000000000127751300200146000310200ustar00rootroot00000000000000/*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 "AlgorithmSurfaceWedgeVolume.h" #include "AlgorithmException.h" #include "MetricFile.h" #include "SurfaceFile.h" #include "Vector3D.h" #include using namespace caret; using namespace std; AString AlgorithmSurfaceWedgeVolume::getCommandSwitch() { return "-surface-wedge-volume"; } AString AlgorithmSurfaceWedgeVolume::getShortDescription() { return "MEASURE PER-VERTEX VOLUME BETWEEN SURFACES"; } OperationParameters* AlgorithmSurfaceWedgeVolume::getParameters() { OperationParameters* ret = new OperationParameters(); ret->addSurfaceParameter(1, "inner-surface", "the inner surface"); ret->addSurfaceParameter(2, "outer-surface", "the outer surface"); ret->addMetricOutputParameter(3, "metric", "the output metric"); ret->setHelpText( AString("Compute the volume of each vertex's area from one surface to another. ") + "The surfaces must have vertex correspondence." ); return ret; } void AlgorithmSurfaceWedgeVolume::useParameters(OperationParameters* myParams, ProgressObject* myProgObj) { SurfaceFile* innerSurf = myParams->getSurface(1); SurfaceFile* outerSurf = myParams->getSurface(2); MetricFile* myMetricOut = myParams->getOutputMetric(3); AlgorithmSurfaceWedgeVolume(myProgObj, innerSurf, outerSurf, myMetricOut); } AlgorithmSurfaceWedgeVolume::AlgorithmSurfaceWedgeVolume(ProgressObject* myProgObj, const SurfaceFile* innerSurf, const SurfaceFile* outerSurf, MetricFile* myMetricOut) : AbstractAlgorithm(myProgObj) { LevelProgress myProgress(myProgObj); int numNodes = innerSurf->getNumberOfNodes(); if (numNodes != outerSurf->getNumberOfNodes()) { throw AlgorithmException("input surfaces have different number of nodes"); } myMetricOut->setNumberOfNodesAndColumns(numNodes, 1); myMetricOut->setStructure(innerSurf->getStructure()); myMetricOut->setColumnName(0, "per-vertex volume between surfaces"); vector tempCol(numNodes, 0.0f); const float* innerCoords = innerSurf->getCoordinateData(); const float* outerCoords = outerSurf->getCoordinateData(); int numTiles = innerSurf->getNumberOfTriangles(); for (int i = 0; i < numTiles; ++i) { const int32_t* innerTile = innerSurf->getTriangle(i); Vector3D innerPoints[3], outerPoints[3]; for (int j = 0; j < 3; ++j) { innerPoints[j] = innerCoords + innerTile[j] * 3; outerPoints[j] = outerCoords + innerTile[j] * 3; } Vector3D refPoint = innerPoints[0];//use an "origin" for the vector field that is near the polyhedron for numerical stability - also, this makes several sides have zero contribution float volume = 0.0f;//I am ignoring the 1/3 in the vector field and the 1/2 for area of triangle for now, because they apply to everything so we can multiple later //NOTE: the inner surface triangle has zero contribution due to choice of reference point volume += (outerPoints[0] - refPoint).dot((outerPoints[1] - outerPoints[0]).cross(outerPoints[2] - outerPoints[1]));//we must be VERY careful with orientations int m = 2; for (int k = 0; k < 3; ++k)//walk the side quadrilaterals as 2->0, 0->1, 1->2 {//add the average of the two triangulations of the quadrilateral Vector3D mref = innerPoints[m] - refPoint, kref = innerPoints[k] - refPoint;//precalculate reference points on the white surface for use in both volumes Vector3D whiteEdge = innerPoints[k] - innerPoints[m];//precalculate this frequently used edge also if (k != 0 && m != 0) volume += 0.5f * mref.dot(whiteEdge.cross(outerPoints[k] - innerPoints[k]));//don't calculate them when their contribution is 0, due to containing the reference point if (m != 0) volume += 0.5f * mref.dot((outerPoints[m] - innerPoints[m]).cross(innerPoints[m] - outerPoints[k])); if (k != 0 && m != 0) volume += 0.5f * kref.dot(whiteEdge.cross(outerPoints[m] - innerPoints[k])); if (k != 0) volume += 0.5f * kref.dot((outerPoints[k] - innerPoints[k]).cross(outerPoints[m] - outerPoints[k])); m = k; } volume /= 18.0f;//1/3 for vector field, 1/2 for triangle, and 1/3 to split the tile volume among its 3 vertices for (int j = 0; j < 3; ++j) { tempCol[innerTile[j]] += volume; } } myMetricOut->setValuesForColumn(0, tempCol.data()); } float AlgorithmSurfaceWedgeVolume::getAlgorithmInternalWeight() { return 1.0f;//override this if needed, if the progress bar isn't smooth } float AlgorithmSurfaceWedgeVolume::getSubAlgorithmWeight() { //return AlgorithmInsertNameHere::getAlgorithmWeight();//if you use a subalgorithm return 0.0f; } connectome-workbench-1.2.3+git41-gc4c6c90/src/Algorithms/AlgorithmSurfaceWedgeVolume.h000066400000000000000000000033341300200146000304340ustar00rootroot00000000000000#ifndef __ALGORITHM_SURFACE_WEDGE_VOLUME_H__ #define __ALGORITHM_SURFACE_WEDGE_VOLUME_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 "AbstractAlgorithm.h" namespace caret { class AlgorithmSurfaceWedgeVolume : public AbstractAlgorithm { AlgorithmSurfaceWedgeVolume(); protected: static float getSubAlgorithmWeight(); static float getAlgorithmInternalWeight(); public: AlgorithmSurfaceWedgeVolume(ProgressObject* myProgObj, const SurfaceFile* innerSurf, const SurfaceFile* outerSurf, MetricFile* myMetricOut); static OperationParameters* getParameters(); static void useParameters(OperationParameters* myParams, ProgressObject* myProgObj); static AString getCommandSwitch(); static AString getShortDescription(); }; typedef TemplateAutoOperation AutoAlgorithmSurfaceWedgeVolume; } #endif //__ALGORITHM_SURFACE_WEDGE_VOLUME_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Algorithms/AlgorithmTemplate.cxx.txt000066400000000000000000000075001300200146000276430ustar00rootroot00000000000000/*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 "AlgorithmName.h" #include "AlgorithmException.h" using namespace caret; using namespace std; AString AlgorithmName::getCommandSwitch() { return "-command-switch"; } AString AlgorithmName::getShortDescription() { return "SHORT DESCRIPTION"; } OperationParameters* AlgorithmName::getParameters() { OperationParameters* ret = new OperationParameters(); //ret->addSurfaceParameter(1, "surface", "the surface to compute on"); //ret->addMetricOutputParameter(2, "metric-out", "the output metric"); //OptionalParameter* columnSelect = ret->createOptionalParameter(3, "-column", "select a single column"); //columnSelect->addStringParameter(1, "column", "the column number or name"); ret->setHelpText( AString("This is where you set the help text. ") + "DO NOT add the info about what the command line format is, and do not give the command switch, " + "short description, or the short descriptions of parameters. " + "Do not indent, manually break long lines, or format the text in any way " + "other than to separate paragraphs within the help text prose, usually with two newlines." ); return ret; } void AlgorithmName::useParameters(OperationParameters* myParams, ProgressObject* myProgObj) { //SurfaceFile* mySurf = myParams->getSurface(1);//gets the surface with key 1 //MetricFile* myMetricOut = myParams->getOutputMetric(2);//gets the output metric with key 2 /*OptionalParameter* columnSelect = myParams->getOptionalParameter(3);//gets optional parameter with key 3 int columnNum = -1; if (columnSelect->m_present) {//set up to use the single column columnNum = (int)myMetric->getMapIndexFromNameOrNumber(columnSelect->getString(1)); if (columnNum < 0) { throw AlgorithmException("invalid column specified"); } }//*/ AlgorithmName(myProgObj /*INSERT PARAMETERS HERE*/);//executes the algorithm } AlgorithmName::AlgorithmName(ProgressObject* myProgObj /*INSERT PARAMETERS HERE*/) : AbstractAlgorithm(myProgObj) { /*ProgressObject* subAlgProgress1 = NULL;//uncomment these if you use another algorithm inside here if (myProgObj != NULL) { subAlgProgress1 = myProgObj->addAlgorithm(AlgorithmInsertNameHere::getAlgorithmWeight()); }//*/ LevelProgress myProgress(myProgObj);//this line sets the algorithm up to use the progress object, and will finish the progress object automatically when the algorithm terminates //myProgress.reportProgress(0.5f);//this is how you say you are halfway done with the INTERNAL work of the algorithm //will report finished automatically when this function ends (myProgress goes out of scope, destructor triggers finish) } float AlgorithmName::getAlgorithmInternalWeight() { return 1.0f;//override this if needed, if the progress bar isn't smooth } float AlgorithmName::getSubAlgorithmWeight() { //return AlgorithmInsertNameHere::getAlgorithmWeight();//if you use a subalgorithm return 0.0f; } connectome-workbench-1.2.3+git41-gc4c6c90/src/Algorithms/AlgorithmTemplate.h.txt000066400000000000000000000054501300200146000272720ustar00rootroot00000000000000#ifndef __ALGORITHM_NAME_H__ #define __ALGORITHM_NAME_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*/ /* file->save as... and enter what you will name the class, plus .h find and replace these strings in plain text mode (not "whole word only"): AlgorithmName : algorithm name, in CamelCase, with initial capital, same as what you saved the header file to ALGORITHM_NAME : uppercase of algorithm name, with underscore between words, used in #ifdef guards next, make AlgorithmName.cxx from AlgorithmTemplate.cxx.txt via one of the following (depending on working directory): cat AlgorithmTemplate.cxx.txt | sed 's/[A]lgorithmName/AlgorithmName/g' > AlgorithmName.cxx cat Algorithms/AlgorithmTemplate.cxx.txt | sed 's/[A]lgorithmName/AlgorithmName/g' > Algorithms/AlgorithmName.cxx cat src/Algorithms/AlgorithmTemplate.cxx.txt | sed 's/[A]lgorithmName/AlgorithmName/g' > src/Algorithms/AlgorithmName.cxx or manually copy and replace next, implement its functions - the algorithm work goes in the CONSTRUCTOR add these into Algorithms/CMakeLists.txt: AlgorithmName.h AlgorithmName.cxx place the following lines into Commands/CommandOperationManager.cxx: #include "AlgorithmName.h" //near the top this->commandOperations.push_back(new CommandParser(new AutoAlgorithmName())); //in CommandOperationManager() finally, remove this block comment */ #include "AbstractAlgorithm.h" namespace caret { class AlgorithmName : public AbstractAlgorithm { AlgorithmName(); protected: static float getSubAlgorithmWeight(); static float getAlgorithmInternalWeight(); public: AlgorithmName(ProgressObject* myProgObj /*INSERT PARAMETERS HERE//*/); static OperationParameters* getParameters(); static void useParameters(OperationParameters* myParams, ProgressObject* myProgObj); static AString getCommandSwitch(); static AString getShortDescription(); }; typedef TemplateAutoOperation AutoAlgorithmName; } #endif //__ALGORITHM_NAME_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Algorithms/AlgorithmVolumeAffineResample.cxx000066400000000000000000000164251300200146000313310ustar00rootroot00000000000000/*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 "AlgorithmVolumeAffineResample.h" #include "AffineFile.h" #include "AlgorithmException.h" #include "CaretLogger.h" #include "CaretOMP.h" #include "NiftiIO.h" #include "Vector3D.h" using namespace caret; using namespace std; AString AlgorithmVolumeAffineResample::getCommandSwitch() { return "-volume-affine-resample"; } AString AlgorithmVolumeAffineResample::getShortDescription() { return "RESAMPLE VOLUME USING AFFINE TRANSFORM"; } OperationParameters* AlgorithmVolumeAffineResample::getParameters() { OperationParameters* ret = new OperationParameters(); ret->addVolumeParameter(1, "volume-in", "volume to resample"); ret->addStringParameter(2, "affine", "the affine file to apply"); ret->addStringParameter(3, "volume-space", "a volume file in the volume space you want for the output"); ret->addStringParameter(4, "method", "the resampling method"); ret->addVolumeOutputParameter(5, "volume-out", "the output volume"); OptionalParameter* flirtOpt = ret->createOptionalParameter(6, "-flirt", "MUST be used if affine is a flirt affine"); flirtOpt->addStringParameter(1, "source-volume", "the source volume used when generating the affine"); flirtOpt->addStringParameter(2, "target-volume", "the target volume used when generating the affine"); ret->setHelpText( AString("Resample a volume file with an affine transformation. ") + "The recommended methods are CUBIC (cubic spline) for most data, and ENCLOSING_VOXEL for label data. " "The parameter must be one of:\n\n" + "CUBIC\nENCLOSING_VOXEL\nTRILINEAR" ); return ret; } void AlgorithmVolumeAffineResample::useParameters(OperationParameters* myParams, ProgressObject* myProgObj) { VolumeFile* inVol = myParams->getVolume(1); AString affName = myParams->getString(2); AString refSpaceName = myParams->getString(3); AString method = myParams->getString(4); VolumeFile* outVol = myParams->getOutputVolume(5); OptionalParameter* flirtOpt = myParams->getOptionalParameter(6); AffineFile myAffine; if (flirtOpt->m_present) { myAffine.readFlirt(affName, flirtOpt->getString(1), flirtOpt->getString(2)); } else { myAffine.readWorld(affName); } VolumeFile::InterpType myMethod = VolumeFile::CUBIC; if (method == "CUBIC") { myMethod = VolumeFile::CUBIC; } else if (method == "TRILINEAR") { myMethod = VolumeFile::TRILINEAR; } else if (method == "ENCLOSING_VOXEL") { myMethod = VolumeFile::ENCLOSING_VOXEL; } else { throw AlgorithmException("unrecognized interpolation method"); } FloatMatrix affMat = FloatMatrix(myAffine.getMatrix()); NiftiIO refSpaceIO; refSpaceIO.openRead(refSpaceName); AlgorithmVolumeAffineResample(myProgObj, inVol, affMat, refSpaceIO.getDimensions().data(), refSpaceIO.getHeader().getSForm(), myMethod, outVol); } AlgorithmVolumeAffineResample::AlgorithmVolumeAffineResample(ProgressObject* myProgObj, const VolumeFile* inVol, const FloatMatrix& myAffine, const int64_t refDims[3], const vector >& refSform, const VolumeFile::InterpType& myMethod, VolumeFile* outVol) : AbstractAlgorithm(myProgObj) { LevelProgress myProgress(myProgObj); int64_t affRows, affColumns; myAffine.getDimensions(affRows, affColumns); if (affRows < 3 || affRows > 4 || affColumns != 4) throw AlgorithmException("input matrix is not an affine matrix"); vector outDims = inVol->getOriginalDimensions(); if (outDims.size() < 3) throw AlgorithmException("input must have 3 spatial dimensions"); outDims[0] = refDims[0]; outDims[1] = refDims[1]; outDims[2] = refDims[2]; int64_t numMaps = inVol->getNumberOfMaps(), numComponents = inVol->getNumberOfComponents(); outVol->reinitialize(outDims, refSform, numComponents, inVol->getType()); FloatMatrix targetToSource = myAffine; targetToSource.resize(4, 4); targetToSource[3][0] = 0.0f; targetToSource[3][1] = 0.0f; targetToSource[3][2] = 0.0f; targetToSource[3][3] = 1.0f; targetToSource = targetToSource.inverse(); Vector3D xvec, yvec, zvec, offset; xvec[0] = targetToSource[0][0]; xvec[1] = targetToSource[1][0]; xvec[2] = targetToSource[2][0]; yvec[0] = targetToSource[0][1]; yvec[1] = targetToSource[1][1]; yvec[2] = targetToSource[2][1]; zvec[0] = targetToSource[0][2]; zvec[1] = targetToSource[1][2]; zvec[2] = targetToSource[2][2]; offset[0] = targetToSource[0][3]; offset[1] = targetToSource[1][3]; offset[2] = targetToSource[2][3]; if (inVol->isMappedWithLabelTable()) { if (myMethod != VolumeFile::ENCLOSING_VOXEL) { CaretLogWarning("using interpolation type other than ENCLOSING_VOXEL on a label volume"); } for (int64_t i = 0; i < numMaps; ++i) { *(outVol->getMapLabelTable(i)) = *(inVol->getMapLabelTable(i)); } } for (int64_t c = 0; c < numComponents; ++c) { for (int64_t b = 0; b < numMaps; ++b) { if (myMethod == VolumeFile::CUBIC) { inVol->validateSpline(b, c);//because deconvolve is parallel, but won't execute parallel if we are already in a parallel section } #pragma omp CARET_PARFOR schedule(dynamic) for (int64_t k = 0; k < outDims[2]; ++k) { for (int64_t j = 0; j < outDims[1]; ++j) { for (int64_t i = 0; i < outDims[0]; ++i) { Vector3D outCoord, inCoord; outVol->indexToSpace(i, j, k, outCoord); inCoord = xvec * outCoord[0] + yvec * outCoord[1] + zvec * outCoord[2] + offset; float interpVal = inVol->interpolateValue(inCoord, myMethod, NULL, b, c); outVol->setValue(interpVal, i, j, k, b, c); } } } if (myMethod == VolumeFile::CUBIC) { inVol->freeSpline(b, c);//release memory we no longer need, if we allocated it } } } } float AlgorithmVolumeAffineResample::getAlgorithmInternalWeight() { return 1.0f;//override this if needed, if the progress bar isn't smooth } float AlgorithmVolumeAffineResample::getSubAlgorithmWeight() { //return AlgorithmInsertNameHere::getAlgorithmWeight();//if you use a subalgorithm return 0.0f; } connectome-workbench-1.2.3+git41-gc4c6c90/src/Algorithms/AlgorithmVolumeAffineResample.h000066400000000000000000000036541300200146000307560ustar00rootroot00000000000000#ifndef __ALGORITHM_VOLUME_AFFINE_RESAMPLE_H__ #define __ALGORITHM_VOLUME_AFFINE_RESAMPLE_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 "AbstractAlgorithm.h" #include "FloatMatrix.h" #include "VolumeFile.h" namespace caret { class AlgorithmVolumeAffineResample : public AbstractAlgorithm { AlgorithmVolumeAffineResample(); protected: static float getSubAlgorithmWeight(); static float getAlgorithmInternalWeight(); public: AlgorithmVolumeAffineResample(ProgressObject* myProgObj, const VolumeFile* inVol, const FloatMatrix& myAffine, const int64_t refDims[3], const std::vector >& refSform, const VolumeFile::InterpType& myMethod, VolumeFile* outVol); static OperationParameters* getParameters(); static void useParameters(OperationParameters* myParams, ProgressObject* myProgObj); static AString getCommandSwitch(); static AString getShortDescription(); }; typedef TemplateAutoOperation AutoAlgorithmVolumeAffineResample; } #endif //__ALGORITHM_VOLUME_AFFINE_RESAMPLE_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Algorithms/AlgorithmVolumeAllLabelsToROIs.cxx000066400000000000000000000115601300200146000313360ustar00rootroot00000000000000/*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 "AlgorithmVolumeAllLabelsToROIs.h" #include "AlgorithmException.h" #include "GiftiLabelTable.h" #include "VolumeFile.h" #include #include #include using namespace caret; using namespace std; AString AlgorithmVolumeAllLabelsToROIs::getCommandSwitch() { return "-volume-all-labels-to-rois"; } AString AlgorithmVolumeAllLabelsToROIs::getShortDescription() { return "MAKE ROIS FROM ALL LABELS IN A VOLUME FRAME"; } OperationParameters* AlgorithmVolumeAllLabelsToROIs::getParameters() { OperationParameters* ret = new OperationParameters(); ret->addVolumeParameter(1, "label-in", "the input volume label file"); ret->addStringParameter(2, "map", "the number or name of the label map to use"); ret->addVolumeOutputParameter(3, "volume-out", "the output volume file"); ret->setHelpText( AString("The output volume has a frame for each label in the specified input frame, other than the ??? label, ") + "each of which contains an ROI of all voxels that are set to the corresponding label." ); return ret; } void AlgorithmVolumeAllLabelsToROIs::useParameters(OperationParameters* myParams, ProgressObject* myProgObj) { VolumeFile* myLabel = myParams->getVolume(1); AString mapID = myParams->getString(2); int whichMap = myLabel->getMapIndexFromNameOrNumber(mapID); if (whichMap == -1) { throw AlgorithmException("invalid map number or name specified"); } VolumeFile* myVolOut = myParams->getOutputVolume(3); AlgorithmVolumeAllLabelsToROIs(myProgObj, myLabel, whichMap, myVolOut); } AlgorithmVolumeAllLabelsToROIs::AlgorithmVolumeAllLabelsToROIs(ProgressObject* myProgObj, const VolumeFile* myLabel, const int& whichMap, VolumeFile* myVolOut) : AbstractAlgorithm(myProgObj) { LevelProgress myProgress(myProgObj); if (myLabel->getType() != SubvolumeAttributes::LABEL) { throw AlgorithmException("input volume must be a label volume"); } if (whichMap < 0 || whichMap >= myLabel->getNumberOfMaps()) { throw AlgorithmException("invalid map index specified"); } const GiftiLabelTable* myTable = myLabel->getMapLabelTable(whichMap); int32_t unusedKey = myTable->getUnassignedLabelKey();//WARNING: this actually MODIFIES the label table if the ??? key doesn't exist set myKeys = myTable->getKeys(); int numKeys = (int)myKeys.size(); if (numKeys < 2) { throw AlgorithmException("label table doesn't contain any keys besides the ??? key"); } map keyToMap;//lookup from keys to subvolume vector outDims = myLabel->getOriginalDimensions(); outDims.resize(4); outDims[3] = numKeys - 1;//don't include the ??? key myVolOut->reinitialize(outDims, myLabel->getSform()); myVolOut->setValueAllVoxels(0.0f); int counter = 0; for (set::iterator iter = myKeys.begin(); iter != myKeys.end(); ++iter) { if (*iter == unusedKey) continue;//skip the ??? key keyToMap[*iter] = counter; myVolOut->setMapName(counter, myTable->getLabelName(*iter)); ++counter; } for (int64_t k = 0; k < outDims[2]; ++k)//because we need to set voxels in the output, rather than in a temporary frame, for single pass without duplicating the memory { for (int64_t j = 0; j < outDims[1]; ++j) { for (int64_t i = 0; i < outDims[0]; ++i) { int32_t thisKey = (int32_t)floor(myLabel->getValue(i, j, k, whichMap) + 0.5f); map::iterator iter = keyToMap.find(thisKey); if (iter != keyToMap.end()) { myVolOut->setValue(1.0f, i, j, k, iter->second); } } } } } float AlgorithmVolumeAllLabelsToROIs::getAlgorithmInternalWeight() { return 1.0f;//override this if needed, if the progress bar isn't smooth } float AlgorithmVolumeAllLabelsToROIs::getSubAlgorithmWeight() { //return AlgorithmInsertNameHere::getAlgorithmWeight();//if you use a subalgorithm return 0.0f; } connectome-workbench-1.2.3+git41-gc4c6c90/src/Algorithms/AlgorithmVolumeAllLabelsToROIs.h000066400000000000000000000033531300200146000307640ustar00rootroot00000000000000#ifndef __ALGORITHM_VOLUME_ALL_LABELS_TO_ROIS_H__ #define __ALGORITHM_VOLUME_ALL_LABELS_TO_ROIS_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 "AbstractAlgorithm.h" namespace caret { class AlgorithmVolumeAllLabelsToROIs : public AbstractAlgorithm { AlgorithmVolumeAllLabelsToROIs(); protected: static float getSubAlgorithmWeight(); static float getAlgorithmInternalWeight(); public: AlgorithmVolumeAllLabelsToROIs(ProgressObject* myProgObj, const VolumeFile* myLabel, const int& whichMap, VolumeFile* myVolOut); static OperationParameters* getParameters(); static void useParameters(OperationParameters* myParams, ProgressObject* myProgObj); static AString getCommandSwitch(); static AString getShortDescription(); }; typedef TemplateAutoOperation AutoAlgorithmVolumeAllLabelsToROIs; } #endif //__ALGORITHM_VOLUME_ALL_LABELS_TO_ROIS_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Algorithms/AlgorithmVolumeDilate.cxx000066400000000000000000000657431300200146000276610ustar00rootroot00000000000000/*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 "AlgorithmVolumeDilate.h" #include "AlgorithmException.h" #include "CaretHeap.h" #include "CaretLogger.h" #include "CaretOMP.h" #include "FloatMatrix.h" #include "Vector3D.h" #include "VolumeFile.h" #include "VoxelIJK.h" #include #include using namespace caret; using namespace std; AString AlgorithmVolumeDilate::getCommandSwitch() { return "-volume-dilate"; } AString AlgorithmVolumeDilate::getShortDescription() { return "DILATE A VOLUME FILE"; } OperationParameters* AlgorithmVolumeDilate::getParameters() { OperationParameters* ret = new OperationParameters(); ret->addVolumeParameter(1, "volume", "the volume to dilate"); ret->addDoubleParameter(2, "distance", "distance in mm to dilate"); ret->addStringParameter(3, "method", "dilation method to use"); ret->addVolumeOutputParameter(4, "volume-out", "the output volume"); OptionalParameter* exponentOpt = ret->createOptionalParameter(8, "-exponent", "use a different exponent in the weighting function"); exponentOpt->addDoubleParameter(1, "exponent", "exponent 'n' to use in (1 / (distance ^ n)) as the weighting function (default 2)"); OptionalParameter* badRoiOpt = ret->createOptionalParameter(5, "-bad-voxel-roi", "specify an roi of voxels to overwrite, rather than voxels with value zero"); badRoiOpt->addVolumeParameter(1, "roi-volume", "volume file, positive values denote voxels to have their values replaced"); OptionalParameter* dataRoiOpt = ret->createOptionalParameter(7, "-data-roi", "specify an roi of where there is data"); dataRoiOpt->addVolumeParameter(1, "roi-volume", "volume file, positive values denote voxels that have data"); OptionalParameter* subvolSelect = ret->createOptionalParameter(6, "-subvolume", "select a single subvolume to dilate"); subvolSelect->addStringParameter(1, "subvol", "the subvolume number or name"); ret->setHelpText( AString("For all voxels that are designated as bad, if they neighbor a non-bad voxel with data or are within the specified distance of such a voxel, ") + "replace the value in the bad voxel with a value calculated from nearby non-bad voxels that have data, otherwise set the value to zero. " + "No matter how small is, dilation will always use at least the face neighbor voxels.\n\n" + "By default, voxels that have data with the value 0 are bad, specify -bad-voxel-roi to only count voxels as bad if they are selected by the roi. " + "If -data-roi is not specified, all voxels are assumed to have data.\n\n" + "Valid values for are:\n\n" + "NEAREST - use the value from the nearest good voxel\n" + "WEIGHTED - use a weighted average based on distance" ); return ret; } void AlgorithmVolumeDilate::useParameters(OperationParameters* myParams, ProgressObject* myProgObj) { VolumeFile* volIn = myParams->getVolume(1); float distance = (float)myParams->getDouble(2); AString methodName = myParams->getString(3); Method myMethod = NEAREST; if (methodName == "NEAREST") { myMethod = NEAREST; } else if (methodName == "WEIGHTED") { myMethod = WEIGHTED; } else { throw AlgorithmException("invalid method specified, use NEAREST or WEIGHTED"); } VolumeFile* volOut = myParams->getOutputVolume(4); OptionalParameter* exponentOpt = myParams->getOptionalParameter(8); float exponent = 2.0f; if (exponentOpt->m_present) { exponent = (float)exponentOpt->getDouble(1); } OptionalParameter* badRoiOpt = myParams->getOptionalParameter(5); VolumeFile* badRoi = NULL; if (badRoiOpt->m_present) { badRoi = badRoiOpt->getVolume(1); } OptionalParameter* dataRoiOpt = myParams->getOptionalParameter(7); VolumeFile* dataRoi = NULL; if (dataRoiOpt->m_present) { dataRoi = dataRoiOpt->getVolume(1); } OptionalParameter* subvolSelect = myParams->getOptionalParameter(6); int subvol = -1; if (subvolSelect->m_present) { subvol = volIn->getMapIndexFromNameOrNumber(subvolSelect->getString(1)); if (subvol < 0) throw AlgorithmException("invalid subvolume specified"); } AlgorithmVolumeDilate(myProgObj, volIn, distance, myMethod, volOut, badRoi, dataRoi, subvol, exponent); } AlgorithmVolumeDilate::AlgorithmVolumeDilate(ProgressObject* myProgObj, const VolumeFile* volIn, const float& distance, const Method& myMethod, VolumeFile* volOut, const VolumeFile* badRoi, const VolumeFile* dataRoi, const int& subvol, const float& exponent) : AbstractAlgorithm(myProgObj) { LevelProgress myProgress(myProgObj); vector myDims; volIn->getDimensions(myDims); if (subvol < -1 || subvol >= myDims[3]) { throw AlgorithmException("invalid subvolume specified"); } if (distance < 0.0f) { throw AlgorithmException("distance cannot be negative"); } if (badRoi != NULL && !volIn->matchesVolumeSpace(badRoi)) { throw AlgorithmException("bad voxel roi volume space does not match input volume"); } if (dataRoi != NULL && !volIn->matchesVolumeSpace(dataRoi)) { throw AlgorithmException("data roi volume space does not match input volume"); } bool isLabelData = false; if (volIn->getType() == SubvolumeAttributes::LABEL) { isLabelData = true; } vector > volSpace = volIn->getSform(); Vector3D ivec, jvec, kvec, origin, ijorth, jkorth, kiorth; FloatMatrix(volSpace).getAffineVectors(ivec, jvec, kvec, origin); ijorth = ivec.cross(jvec).normal();//find the bounding box that encloses a sphere of radius kernBox jkorth = jvec.cross(kvec).normal(); kiorth = kvec.cross(ivec).normal(); int irange = (int)floor(abs(distance / ivec.dot(jkorth))); int jrange = (int)floor(abs(distance / jvec.dot(kiorth))); int krange = (int)floor(abs(distance / kvec.dot(ijorth))); if (irange < 1) irange = 1;//don't underflow if (jrange < 1) jrange = 1; if (krange < 1) krange = 1; Vector3D kscratch, jscratch, iscratch; vector stencil; vector stenWeights; for (int k = -krange; k <= krange; ++k) { kscratch = kvec * k; for (int j = -jrange; j <= jrange; ++j) { jscratch = kscratch + jvec * j; for (int i = -irange; i <= irange; ++i) { if (k == 0 && j == 0 && i == 0) continue; iscratch = jscratch + ivec * i; float tempf = iscratch.length(); if (tempf <= distance || abs(i) + abs(j) + abs(k) == 1) { stencil.push_back(i); stencil.push_back(j); stencil.push_back(k); switch (myMethod) { case NEAREST: stenWeights.push_back(tempf); break; case WEIGHTED: if (tempf == 0.0f) throw AlgorithmException("volume space is degenerate, aborting"); stenWeights.push_back(1.0f / pow(tempf, exponent)); break; } } } } } if (myMethod == NEAREST) {//sort the stencil by distance, so we can stop early CaretSimpleMinHeap myHeap; int stencilSize = (int)stenWeights.size(); myHeap.reserve(stencilSize); for (int i = 0; i < stencilSize; ++i) { myHeap.push(VoxelIJK(stencil.data() + i * 3), stenWeights[i]); } stencil.clear(); stenWeights.clear(); while (!myHeap.isEmpty()) { float tempf; VoxelIJK myTriple = myHeap.pop(&tempf); stenWeights.push_back(tempf); stencil.push_back(myTriple.m_ijk[0]); stencil.push_back(myTriple.m_ijk[1]); stencil.push_back(myTriple.m_ijk[2]); } } if (subvol == -1) { volOut->reinitialize(volIn->getOriginalDimensions(), volIn->getSform(), volIn->getNumberOfComponents(), volIn->getType()); for (int i = 0; i < myDims[3]; ++i) { if (volIn->getType() == SubvolumeAttributes::LABEL) { *(volOut->getMapLabelTable(i)) = *(volIn->getMapLabelTable(i)); } else { *(volOut->getMapPaletteColorMapping(i)) = *(volIn->getMapPaletteColorMapping(i)); } volOut->setMapName(i, volIn->getMapName(i) + " dilate " + AString::number(distance)); } } else { vector outDims = myDims; outDims.resize(3); volOut->reinitialize(outDims, volIn->getSform(), volIn->getNumberOfComponents(), volIn->getType()); if (volIn->getType() == SubvolumeAttributes::LABEL) { *(volOut->getMapLabelTable(0)) = *(volIn->getMapLabelTable(subvol)); } else { *(volOut->getMapPaletteColorMapping(0)) = *(volIn->getMapPaletteColorMapping(subvol)); } volOut->setMapName(0, volIn->getMapName(subvol) + " dilate " + AString::number(distance)); } if (subvol == -1) { for (int s = 0; s < myDims[3]; ++s) { for (int c = 0; c < myDims[4]; ++c) { if (isLabelData) { dilateFrameLabel(volIn, s, c, volOut, s, badRoi, dataRoi, myMethod, stencil, stenWeights); } else { dilateFrame(volIn, s, c, volOut, s, badRoi, dataRoi, myMethod, stencil, stenWeights); } } } } else { for (int c = 0; c < myDims[4]; ++c) { if (isLabelData) { dilateFrameLabel(volIn, subvol, c, volOut, 0, badRoi, dataRoi, myMethod, stencil, stenWeights); } else { dilateFrame(volIn, subvol, c, volOut, 0, badRoi, dataRoi, myMethod, stencil, stenWeights); } } } } void AlgorithmVolumeDilate::dilateFrame(const VolumeFile* volIn, const int& insubvol, const int& component, VolumeFile* volOut, const int& outsubvol, const VolumeFile* badRoi, const VolumeFile* dataRoi, const Method& myMethod, const vector& stencil, const vector& stenWeights) { vector myDims; volIn->getDimensions(myDims); int stensize = (int)stenWeights.size(); #pragma omp CARET_PARFOR schedule(dynamic) for (int k = 0; k < myDims[2]; ++k) { for (int j = 0; j < myDims[1]; ++j) { for (int i = 0; i < myDims[0]; ++i) { bool copy = true; if (badRoi == NULL) { copy = volIn->getValue(i, j, k, insubvol, component) != 0.0f || (dataRoi != NULL && !(dataRoi->getValue(i, j, k) > 0.0f)); } else { copy = !(badRoi->getValue(i, j, k) > 0.0f);//in case some clown uses NaNs as bad in an roi } if (copy) { volOut->setValue(volIn->getValue(i, j, k, insubvol, component), i, j, k, outsubvol, component); } else { Vector3D voxcoord; volIn->indexToSpace(i, j, k, voxcoord); switch (myMethod) { case NEAREST: { int best = -1; if (badRoi == NULL) { for (int stenind = 0; stenind < stensize; ++stenind) { int base = stenind * 3; int64_t tempindex[3]; tempindex[0] = stencil[base] + i; tempindex[1] = stencil[base + 1] + j; tempindex[2] = stencil[base + 2] + k; if (volIn->indexValid(tempindex)) { if ((dataRoi == NULL || dataRoi->getValue(tempindex) > 0.0f) && volIn->getValue(tempindex, insubvol, component) != 0.0f) { best = stenind; break; } } } } else { for (int stenind = 0; stenind < stensize; ++stenind) { int base = stenind * 3; int64_t tempindex[3]; tempindex[0] = stencil[base] + i; tempindex[1] = stencil[base + 1] + j; tempindex[2] = stencil[base + 2] + k; if (volIn->indexValid(tempindex)) { if ((dataRoi == NULL || dataRoi->getValue(tempindex) > 0.0f) && !(badRoi->getValue(tempindex) > 0.0f)) { best = stenind; break; } } } } if (best == -1) { volOut->setValue(0.0f, i, j, k, outsubvol, component); } else { int base = best * 3; int64_t tempindex[3]; tempindex[0] = stencil[base] + i; tempindex[1] = stencil[base + 1] + j; tempindex[2] = stencil[base + 2] + k; volOut->setValue(volIn->getValue(tempindex, insubvol, component), i, j, k, outsubvol, component); } break; } case WEIGHTED: { double sum = 0.0, weightsum = 0.0; if (badRoi == NULL) { for (int stenind = 0; stenind < stensize; ++stenind) { int base = stenind * 3; int64_t tempindex[3]; tempindex[0] = stencil[base] + i; tempindex[1] = stencil[base + 1] + j; tempindex[2] = stencil[base + 2] + k; if (volIn->indexValid(tempindex)) { float tempf = volIn->getValue(tempindex, insubvol, component); if ((dataRoi == NULL || dataRoi->getValue(tempindex) > 0.0f) && tempf != 0.0f) { float weight = stenWeights[stenind]; sum += weight * tempf; weightsum += weight; } } } } else { for (int stenind = 0; stenind < stensize; ++stenind) { int base = stenind * 3; int64_t tempindex[3]; tempindex[0] = stencil[base] + i; tempindex[1] = stencil[base + 1] + j; tempindex[2] = stencil[base + 2] + k; if (volIn->indexValid(tempindex)) { if ((dataRoi == NULL || dataRoi->getValue(tempindex) > 0.0f) && !(badRoi->getValue(tempindex) > 0.0f)) { float tempf = volIn->getValue(tempindex, insubvol, component); float weight = stenWeights[stenind]; sum += weight * tempf; weightsum += weight; } } } } if (weightsum != 0.0) { volOut->setValue(sum / weightsum, i, j, k, outsubvol, component); } else { volOut->setValue(0.0f, i, j, k, outsubvol, component); } break; } } } } } } } void AlgorithmVolumeDilate::dilateFrameLabel(const VolumeFile* volIn, const int& insubvol, const int& component, VolumeFile* volOut, const int& outsubvol, const VolumeFile* badRoi, const VolumeFile* dataRoi, const Method& myMethod, const vector& stencil, const vector& stenWeights) { vector myDims; volIn->getDimensions(myDims); int stensize = (int)stenWeights.size(); int32_t unlabeledKey = volIn->getMapLabelTable(insubvol)->getUnassignedLabelKey(); #pragma omp CARET_PARFOR schedule(dynamic) for (int k = 0; k < myDims[2]; ++k) { for (int j = 0; j < myDims[1]; ++j) { for (int i = 0; i < myDims[0]; ++i) { bool copy = true; int32_t keyIn = floor(volIn->getValue(i, j, k, insubvol, component) + 0.5f);//fix non-integers if (badRoi == NULL) { copy = keyIn != unlabeledKey || (dataRoi != NULL && !(dataRoi->getValue(i, j, k) > 0.0f)); } else { copy = !(badRoi->getValue(i, j, k) > 0.0f);//in case some clown uses NaNs as bad in an roi } if (copy) { volOut->setValue(keyIn, i, j, k, outsubvol, component); } else { Vector3D voxcoord; volIn->indexToSpace(i, j, k, voxcoord); switch (myMethod) { case NEAREST: { int best = -1; if (badRoi == NULL) { for (int stenind = 0; stenind < stensize; ++stenind) { int base = stenind * 3; int64_t tempindex[3]; tempindex[0] = stencil[base] + i; tempindex[1] = stencil[base + 1] + j; tempindex[2] = stencil[base + 2] + k; if (volIn->indexValid(tempindex)) { if ((dataRoi == NULL || dataRoi->getValue(tempindex) > 0.0f) && volIn->getValue(tempindex, insubvol, component) != 0.0f) { best = stenind; break; } } } } else { for (int stenind = 0; stenind < stensize; ++stenind) { int base = stenind * 3; int64_t tempindex[3]; tempindex[0] = stencil[base] + i; tempindex[1] = stencil[base + 1] + j; tempindex[2] = stencil[base + 2] + k; if (volIn->indexValid(tempindex)) { if ((dataRoi == NULL || dataRoi->getValue(tempindex) > 0.0f) && !(badRoi->getValue(tempindex) > 0.0f)) { best = stenind; break; } } } } if (best == -1) { volOut->setValue(unlabeledKey, i, j, k, outsubvol, component); } else { int base = best * 3; int64_t tempindex[3]; tempindex[0] = stencil[base] + i; tempindex[1] = stencil[base + 1] + j; tempindex[2] = stencil[base + 2] + k; volOut->setValue((int32_t)floor(volIn->getValue(tempindex, insubvol, component) + 0.5f), i, j, k, outsubvol, component);//fix non-integers }//yes, the cast is undefined for silly things like NaN that shouldn't be in a label file, but so are the other paths - we just want consistency between behavior of old and new values break; } case WEIGHTED: { map labelSums; if (badRoi == NULL) { for (int stenind = 0; stenind < stensize; ++stenind) { int base = stenind * 3; int64_t tempindex[3]; tempindex[0] = stencil[base] + i; tempindex[1] = stencil[base + 1] + j; tempindex[2] = stencil[base + 2] + k; if (volIn->indexValid(tempindex)) { int32_t tempKey = floor(volIn->getValue(tempindex, insubvol, component) + 0.5f);//fix non-integers if ((dataRoi == NULL || dataRoi->getValue(tempindex) > 0.0f) && tempKey != unlabeledKey) { float weight = stenWeights[stenind]; map::iterator iter = labelSums.find(tempKey); if (iter == labelSums.end()) { labelSums[tempKey] = weight; } else { iter->second += weight; } } } } } else { for (int stenind = 0; stenind < stensize; ++stenind) { int base = stenind * 3; int64_t tempindex[3]; tempindex[0] = stencil[base] + i; tempindex[1] = stencil[base + 1] + j; tempindex[2] = stencil[base + 2] + k; if (volIn->indexValid(tempindex)) { if ((dataRoi == NULL || dataRoi->getValue(tempindex) > 0.0f) && !(badRoi->getValue(tempindex) > 0.0f)) { int32_t tempKey = floor(volIn->getValue(tempindex, insubvol, component) + 0.5f);//fix non-integers float weight = stenWeights[stenind]; map::iterator iter = labelSums.find(tempKey); if (iter == labelSums.end()) { labelSums[tempKey] = weight; } else { iter->second += weight; } } } } } int32_t outVal = unlabeledKey; float bestSum = -1.0f;//weights should all be positive, so should the sums for (map::iterator iter = labelSums.begin(); iter != labelSums.end(); ++iter) { if (iter->second > bestSum) { outVal = iter->first; bestSum = iter->second; } } volOut->setValue(outVal, i, j, k, outsubvol, component); break; } } } } } } } float AlgorithmVolumeDilate::getAlgorithmInternalWeight() { return 1.0f;//override this if needed, if the progress bar isn't smooth } float AlgorithmVolumeDilate::getSubAlgorithmWeight() { //return AlgorithmInsertNameHere::getAlgorithmWeight();//if you use a subalgorithm return 0.0f; } connectome-workbench-1.2.3+git41-gc4c6c90/src/Algorithms/AlgorithmVolumeDilate.h000066400000000000000000000050351300200146000272720ustar00rootroot00000000000000#ifndef __ALGORITHM_VOLUME_DILATE_H__ #define __ALGORITHM_VOLUME_DILATE_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 "AbstractAlgorithm.h" namespace caret { class AlgorithmVolumeDilate : public AbstractAlgorithm { AlgorithmVolumeDilate(); protected: static float getSubAlgorithmWeight(); static float getAlgorithmInternalWeight(); public: enum Method { NEAREST, WEIGHTED }; AlgorithmVolumeDilate(ProgressObject* myProgObj, const VolumeFile* volIn, const float& distance, const Method& myMethod, VolumeFile* volOut, const VolumeFile* badRoi = NULL, const VolumeFile* dataRoi = NULL, const int& subvol = -1, const float& exponent = 2.0f); static OperationParameters* getParameters(); static void useParameters(OperationParameters* myParams, ProgressObject* myProgObj); static AString getCommandSwitch(); static AString getShortDescription(); private: void dilateFrame(const VolumeFile* volIn, const int& insubvol, const int& component, VolumeFile* volOut, const int& outsubvol, const VolumeFile* badRoi, const VolumeFile* dataRoi, const Method& myMethod, const std::vector& stencil, const std::vector& stenWeights); void dilateFrameLabel(const VolumeFile* volIn, const int& insubvol, const int& component, VolumeFile* volOut, const int& outsubvol, const VolumeFile* badRoi, const VolumeFile* dataRoi, const Method& myMethod, const std::vector& stencil, const std::vector& stenWeights); }; typedef TemplateAutoOperation AutoAlgorithmVolumeDilate; } #endif //__ALGORITHM_VOLUME_DILATE_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Algorithms/AlgorithmVolumeErode.cxx000066400000000000000000000237061300200146000275060ustar00rootroot00000000000000/*LICENSE_START*/ /* * Copyright (C) 2015 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 "AlgorithmVolumeErode.h" #include "AlgorithmException.h" #include "CaretPointLocator.h" #include "VolumeFile.h" #include #include using namespace caret; using namespace std; AString AlgorithmVolumeErode::getCommandSwitch() { return "-volume-erode"; } AString AlgorithmVolumeErode::getShortDescription() { return "ERODE A VOLUME FILE"; } OperationParameters* AlgorithmVolumeErode::getParameters() { OperationParameters* ret = new OperationParameters(); ret->addVolumeParameter(1, "volume", "the volume to erode"); ret->addDoubleParameter(2, "distance", "distance in mm to erode"); ret->addVolumeOutputParameter(3, "volume-out", "the output volume"); OptionalParameter* roiOpt = ret->createOptionalParameter(4, "-roi", "assume voxels outside this roi are nonzero"); roiOpt->addVolumeParameter(1, "roi-volume", "volume file, positive values denote voxels that have data"); OptionalParameter* subvolOpt = ret->createOptionalParameter(5, "-subvolume", "select a single subvolume to dilate"); subvolOpt->addStringParameter(1, "subvol", "the subvolume number or name"); ret->setHelpText( AString("Around each voxel with a value of zero, set surrounding voxels to zero. ") + "The surrounding voxels are all face neighbors and all voxels within the specified distance (center to center)." ); return ret; } void AlgorithmVolumeErode::useParameters(OperationParameters* myParams, ProgressObject* myProgObj) { VolumeFile* volIn = myParams->getVolume(1); float distance = (float)myParams->getDouble(2); VolumeFile* volOut = myParams->getOutputVolume(3); VolumeFile* roiVol = NULL; OptionalParameter* roiOpt = myParams->getOptionalParameter(4); if (roiOpt->m_present) { roiVol = roiOpt->getVolume(1); } int subvol = -1; OptionalParameter* subvolOpt = myParams->getOptionalParameter(5); if (subvolOpt->m_present) { subvol = volIn->getMapIndexFromNameOrNumber(subvolOpt->getString(1)); if (subvol < 0) throw AlgorithmException("invalid subvolume specified"); } AlgorithmVolumeErode(myProgObj, volIn, distance, volOut, roiVol, subvol); } namespace { void erodeFrame(const VolumeFile* volIn, const int& inFrame, const int& component, const float& distance, VolumeFile* volOut, const int& outFrame, const VolumeFile* roiVol) { int neighbors[] = { 0, 0, -1, 0, -1, 0, -1, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1 }; Vector3D ivec, jvec, kvec, origin; volIn->getVolumeSpace().getSpacingVectors(ivec, jvec, kvec, origin); float minSpacing = min(min(ivec.length(), jvec.length()), kvec.length()); bool checkNeighbors = (minSpacing > distance); vector myDims; volIn->getDimensions(myDims); const float* inData = volIn->getFrame(inFrame, component), *roiData = NULL; float emptyVal = 0.0f; bool labelData = false; if (volIn->getType() == SubvolumeAttributes::LABEL) { emptyVal = volIn->getMapLabelTable(inFrame)->getUnassignedLabelKey(); labelData = true; } if (roiVol != NULL) roiData = roiVol->getFrame(); vector scratchFrame(inData, inData + myDims[0] * myDims[1] * myDims[2]);//start with a copy, then zero what we don't need vector coordList; for (int64_t k = 0; k < myDims[2]; ++k) { for (int64_t j = 0; j < myDims[1]; ++j) { for (int64_t i = 0; i < myDims[0]; ++i) { int64_t flatIndex = volIn->getIndex(i, j, k); if (roiData == NULL || roiData[flatIndex] > 0.0f) { if (labelData) { if (floor(inData[flatIndex] + 0.5f) == emptyVal) { float coord[3]; volIn->indexToSpace(i, j, k, coord); coordList.insert(coordList.end(), coord, coord + 3); } } else { if (inData[flatIndex] == 0.0f) { float coord[3]; volIn->indexToSpace(i, j, k, coord); coordList.insert(coordList.end(), coord, coord + 3); } } } } } } CaretPointLocator myLocator(coordList.data(), coordList.size() / 3); for (int64_t k = 0; k < myDims[2]; ++k) { for (int64_t j = 0; j < myDims[1]; ++j) { for (int64_t i = 0; i < myDims[0]; ++i) { float coord[3]; volIn->indexToSpace(i, j, k, coord); if (myLocator.anyInRange(coord, distance)) { scratchFrame[volIn->getIndex(i, j, k)] = emptyVal;//this is used for both label and normal data, use the variable } else if (checkNeighbors) { for (int neigh = 0; neigh < 18; neigh += 3) { if (volIn->indexValid(i + neighbors[neigh], j + neighbors[neigh + 1], k + neighbors[neigh + 2])) { int64_t flatIndex = volIn->getIndex(i + neighbors[neigh], j + neighbors[neigh + 1], k + neighbors[neigh + 2]); if (roiData == NULL || roiData[flatIndex] > 0.0f) { if (labelData) { if (floor(inData[flatIndex] + 0.5f) == emptyVal) { scratchFrame[volIn->getIndex(i, j, k)] = emptyVal; } } else { if (inData[flatIndex] == 0.0f) { scratchFrame[volIn->getIndex(i, j, k)] = 0.0f; } } } } } } } } } volOut->setFrame(scratchFrame.data(), outFrame, component); } } AlgorithmVolumeErode::AlgorithmVolumeErode(ProgressObject* myProgObj, const VolumeFile* volIn, const float& distance, VolumeFile* volOut, VolumeFile* roiVol, const int& subvol) : AbstractAlgorithm(myProgObj) { LevelProgress myProgress(myProgObj); vector myDims; volIn->getDimensions(myDims); if (subvol < -1 || subvol >= myDims[3]) { throw AlgorithmException("invalid subvolume specified"); } if (distance < 0.0f) { throw AlgorithmException("distance cannot be negative"); } if (roiVol != NULL && !volIn->matchesVolumeSpace(roiVol)) { throw AlgorithmException("roi volume space does not match input volume"); } if (subvol == -1) { volOut->reinitialize(volIn->getOriginalDimensions(), volIn->getSform(), volIn->getNumberOfComponents(), volIn->getType()); for (int i = 0; i < myDims[3]; ++i) { if (volIn->getType() == SubvolumeAttributes::LABEL) { *(volOut->getMapLabelTable(i)) = *(volIn->getMapLabelTable(i)); } else { *(volOut->getMapPaletteColorMapping(i)) = *(volIn->getMapPaletteColorMapping(i)); } volOut->setMapName(i, volIn->getMapName(i) + " dilate " + AString::number(distance)); } for (int s = 0; s < myDims[3]; ++s) { for (int c = 0; c < myDims[4]; ++c) { erodeFrame(volIn, s, c, distance, volOut, s, roiVol); } } } else { vector outDims = myDims; outDims.resize(3); volOut->reinitialize(outDims, volIn->getSform(), volIn->getNumberOfComponents(), volIn->getType()); if (volIn->getType() == SubvolumeAttributes::LABEL) { *(volOut->getMapLabelTable(0)) = *(volIn->getMapLabelTable(subvol)); } else { *(volOut->getMapPaletteColorMapping(0)) = *(volIn->getMapPaletteColorMapping(subvol)); } volOut->setMapName(0, volIn->getMapName(subvol) + " dilate " + AString::number(distance)); for (int c = 0; c < myDims[4]; ++c) { erodeFrame(volIn, subvol, c, distance, volOut, 0, roiVol); } } } float AlgorithmVolumeErode::getAlgorithmInternalWeight() { return 1.0f;//override this if needed, if the progress bar isn't smooth } float AlgorithmVolumeErode::getSubAlgorithmWeight() { //return AlgorithmInsertNameHere::getAlgorithmWeight();//if you use a subalgorithm return 0.0f; } connectome-workbench-1.2.3+git41-gc4c6c90/src/Algorithms/AlgorithmVolumeErode.h000066400000000000000000000033031300200146000271220ustar00rootroot00000000000000#ifndef __ALGORITHM_VOLUME_ERODE_H__ #define __ALGORITHM_VOLUME_ERODE_H__ /*LICENSE_START*/ /* * Copyright (C) 2015 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 AlgorithmVolumeErode : public AbstractAlgorithm { AlgorithmVolumeErode(); protected: static float getSubAlgorithmWeight(); static float getAlgorithmInternalWeight(); public: AlgorithmVolumeErode(ProgressObject* myProgObj, const VolumeFile* volIn, const float& distance, VolumeFile* volOut, VolumeFile* roiVol = NULL, const int& subvol = -1); static OperationParameters* getParameters(); static void useParameters(OperationParameters* myParams, ProgressObject* myProgObj); static AString getCommandSwitch(); static AString getShortDescription(); }; typedef TemplateAutoOperation AutoAlgorithmVolumeErode; } #endif //__ALGORITHM_VOLUME_ERODE_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Algorithms/AlgorithmVolumeEstimateFWHM.cxx000066400000000000000000000230671300200146000307050ustar00rootroot00000000000000/*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 "AlgorithmVolumeEstimateFWHM.h" #include "AlgorithmException.h" #include #include using namespace caret; using namespace std; AString AlgorithmVolumeEstimateFWHM::getCommandSwitch() { return "-volume-estimate-fwhm"; } AString AlgorithmVolumeEstimateFWHM::getShortDescription() { return "ESTIMATE FWHM SMOOTHNESS OF A VOLUME"; } OperationParameters* AlgorithmVolumeEstimateFWHM::getParameters() { OperationParameters* ret = new OperationParameters(); ret->addVolumeParameter(1, "volume", "the input volume"); OptionalParameter* roiVolOpt = ret->createOptionalParameter(2, "-roi", "use only data within an ROI"); roiVolOpt->addVolumeParameter(1, "roivol", "the volume to use as an ROI"); OptionalParameter* subvolSelect = ret->createOptionalParameter(3, "-subvolume", "select a single subvolume to estimate smoothness of"); subvolSelect->addStringParameter(1, "subvol", "the subvolume number or name"); ret->setHelpText( AString("Estimates the smoothness of the input volume in X, Y, and Z directions separately, printing the estimates to standard output. ") + "If -subvolume is not specified, each subvolume is estimated and displayed separately." ); return ret; } void AlgorithmVolumeEstimateFWHM::useParameters(OperationParameters* myParams, ProgressObject* myProgObj) { LevelProgress myProgress(myProgObj); VolumeFile* myVol = myParams->getVolume(1); OptionalParameter* roiVolOpt = myParams->getOptionalParameter(2); VolumeFile* roiVol = NULL; if (roiVolOpt->m_present) { roiVol = roiVolOpt->getVolume(1); if (!roiVol->matchesVolumeSpace(myVol)) { throw AlgorithmException("roi volume does not match the space of the input volume"); } } OptionalParameter* subvolSelect = myParams->getOptionalParameter(3); int subvolNum = -1; if (subvolSelect->m_present) { subvolNum = (int)myVol->getMapIndexFromNameOrNumber(subvolSelect->getString(1)); if (subvolNum < 0) { throw AlgorithmException("invalid subvolume specified"); } } vector dims; myVol->getDimensions(dims); if (subvolNum == -1) { for (int64_t s = 0; s < dims[3]; ++s) { for (int64_t c = 0; c < dims[4]; ++c) { Vector3D result = estimateFWHM(myVol, roiVol, s, c); if (dims[3] != 1) cout << "subvol " << s + 1 << " "; if (dims[4] != 1) cout << "component " << c + 1 << " "; cout << "FWHM: " << result[0] << ", " << result[1] << ", " << result[2] << endl; } } } else { for (int64_t c = 0; c < dims[4]; ++c) { Vector3D result = estimateFWHM(myVol, roiVol, subvolNum, c); if (dims[3] != 1) cout << "subvol " << subvolNum + 1 << " "; if (dims[4] != 1) cout << "component " << c + 1 << " "; cout << "FWHM: " << result[0] << ", " << result[1] << ", " << result[2] << endl; } } } Vector3D AlgorithmVolumeEstimateFWHM::estimateFWHM(const VolumeFile* input, const VolumeFile* roi, const int64_t& brickIndex, const int64_t& component) { if (roi != NULL && !roi->matchesVolumeSpace(input)) { throw AlgorithmException("roi volume does not match the space of the input volume"); } vector dims; input->getDimensions(dims); double globalaccum = 0.0; int64_t globalCount = 0; double diraccum[3] = {0.0, 0.0, 0.0}; int64_t dirCount[3] = {0, 0, 0}; for (int64_t k = 0; k < dims[2]; ++k)//2 pass method, first find means of values and of FORWARD differences only - this removes global gradient effects {//for derivation, see Forman, S.D., Cohen, J.D., Fitzgerald, M., Eddy, W.F., Mintun, M.A., Noll, D.C., 1995. for (int64_t j = 0; j < dims[1]; ++j)//Improved assessment of significant activation in functional magnetic resonance imaging (fMRI): use of a cluster-size threshold. {//Magn. Reson. Med. 33, 636–647. for (int64_t i = 0; i < dims[0]; ++i) { if (roi == NULL || roi->getValue(i, j, k) > 0.0f) { float center = input->getValue(i, j, k, brickIndex, component); globalaccum += center; ++globalCount; if (i + 1 < dims[0] && (roi == NULL || roi->getValue(i + 1, j, k) > 0.0f))//use ONLY forward differences, to avoid double counting { float diff = center - input->getValue(i + 1, j, k, brickIndex, component); diraccum[0] += diff; ++dirCount[0]; } if (j + 1 < dims[1] && (roi == NULL || roi->getValue(i, j + 1, k) > 0.0f))//use ONLY forward differences, to avoid double counting { float diff = center - input->getValue(i, j + 1, k, brickIndex, component); diraccum[1] += diff; ++dirCount[1]; } if (k + 1 < dims[2] && (roi == NULL || roi->getValue(i, j, k + 1) > 0.0f))//use ONLY forward differences, to avoid double counting { float diff = center - input->getValue(i, j, k + 1, brickIndex, component); diraccum[2] += diff; ++dirCount[2]; } } } } } if (globalCount == 0) throw AlgorithmException("ROI is empty or volume file has no voxels"); float dirmean[3]; for (int i = 0; i < 3; ++i) { dirmean[i] = diraccum[i] / dirCount[i];//we will fix NaNs later diraccum[i] = 0.0; } float globalmean = globalaccum / globalCount; globalaccum = 0; for (int64_t k = 0; k < dims[2]; ++k)//now, find the variances { for (int64_t j = 0; j < dims[1]; ++j) { for (int64_t i = 0; i < dims[0]; ++i) { if (roi == NULL || roi->getValue(i, j, k) > 0.0f) { float center = input->getValue(i, j, k, brickIndex, component); float tempf = center - globalmean; globalaccum += tempf * tempf; if (i + 1 < dims[0] && (roi == NULL || roi->getValue(i + 1, j, k) > 0.0f))//use ONLY forward differences, to avoid double counting { float diff = center - input->getValue(i + 1, j, k, brickIndex, component); tempf = diff - dirmean[0]; diraccum[0] += tempf * tempf; } if (j + 1 < dims[1] && (roi == NULL || roi->getValue(i, j + 1, k) > 0.0f))//use ONLY forward differences, to avoid double counting { float diff = center - input->getValue(i, j + 1, k, brickIndex, component); tempf = diff - dirmean[1]; diraccum[1] += tempf * tempf; } if (k + 1 < dims[2] && (roi == NULL || roi->getValue(i, j, k + 1) > 0.0f))//use ONLY forward differences, to avoid double counting { float diff = center - input->getValue(i, j, k + 1, brickIndex, component); tempf = diff - dirmean[2]; diraccum[2] += tempf * tempf; } } } } } float dirvariance[3]; float fwhms[3]; float globalvariance = globalaccum / globalCount; const VolumeSpace& volSpace = input->getVolumeSpace(); Vector3D spacingVecs[4]; volSpace.getSpacingVectors(spacingVecs[0], spacingVecs[1], spacingVecs[2], spacingVecs[3]); for (int i = 0; i < 3; ++i) { if (dirCount[i] > 0) { dirvariance[i] = diraccum[i] / dirCount[i]; } else { dirvariance[i] = 0.0;//avoid NaNs } fwhms[i] = spacingVecs[i].length() * sqrt(-2.0f * log(2.0f) / log(1.0f - dirvariance[i] / (2.0f * globalvariance))); } Vector3D ret; VolumeSpace::OrientTypes myorient[3]; volSpace.getOrientation(myorient); for (int i = 0; i < 3; ++i) { switch (myorient[i]) { case VolumeSpace::LEFT_TO_RIGHT: case VolumeSpace::RIGHT_TO_LEFT: ret[0] = fwhms[i]; break; case VolumeSpace::POSTERIOR_TO_ANTERIOR: case VolumeSpace::ANTERIOR_TO_POSTERIOR: ret[1] = fwhms[i]; break; case VolumeSpace::INFERIOR_TO_SUPERIOR: case VolumeSpace::SUPERIOR_TO_INFERIOR: ret[2] = fwhms[i]; break; } } return ret; } connectome-workbench-1.2.3+git41-gc4c6c90/src/Algorithms/AlgorithmVolumeEstimateFWHM.h000066400000000000000000000032021300200146000303170ustar00rootroot00000000000000#ifndef __ALGORITHM_VOLUME_ESTIMATE_FWHM_H__ #define __ALGORITHM_VOLUME_ESTIMATE_FWHM_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 "AbstractAlgorithm.h" #include "Vector3D.h" #include "VolumeFile.h" namespace caret { class AlgorithmVolumeEstimateFWHM : public AbstractAlgorithm { public: static OperationParameters* getParameters(); static void useParameters(OperationParameters* myParams, ProgressObject* myProgObj); static AString getCommandSwitch(); static AString getShortDescription(); static Vector3D estimateFWHM(const VolumeFile* input, const VolumeFile* roi = NULL, const int64_t& brickIndex = 0, const int64_t& component = 0); }; typedef TemplateAutoOperation AutoAlgorithmVolumeEstimateFWHM; } #endif //__ALGORITHM_VOLUME_ESTIMATE_FWHM_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Algorithms/AlgorithmVolumeExtrema.cxx000066400000000000000000001514441300200146000300560ustar00rootroot00000000000000/*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 "AlgorithmVolumeExtrema.h" #include "AlgorithmException.h" #include "AlgorithmVolumeSmoothing.h" #include "CaretHeap.h" #include "CaretLogger.h" #include "CaretOMP.h" #include "VolumeFile.h" #include #include #include using namespace caret; using namespace std; AString AlgorithmVolumeExtrema::getCommandSwitch() { return "-volume-extrema"; } AString AlgorithmVolumeExtrema::getShortDescription() { return "FIND EXTREMA IN A VOLUME FILE"; } OperationParameters* AlgorithmVolumeExtrema::getParameters() { OperationParameters* ret = new OperationParameters(); ret->addVolumeParameter(1, "volume-in", "volume file to find the extrema of"); ret->addDoubleParameter(2, "distance", "the minimum distance between identified extrema of the same type"); ret->addVolumeOutputParameter(3, "volume-out", "the output extrema volume"); OptionalParameter* presmoothOpt = ret->createOptionalParameter(4, "-presmooth", "smooth the volume before finding extrema"); presmoothOpt->addDoubleParameter(1, "kernel", "the sigma for the gaussian smoothing kernel, in mm"); OptionalParameter* roiOpt = ret->createOptionalParameter(5, "-roi", "ignore values outside the selected area"); roiOpt->addVolumeParameter(1, "roi-volume", "the area to find extrema in"); OptionalParameter* thresholdOpt = ret->createOptionalParameter(6, "-threshold", "ignore small extrema"); thresholdOpt->addDoubleParameter(1, "low", "the largest value to consider for being a minimum"); thresholdOpt->addDoubleParameter(2, "high", "the smallest value to consider for being a maximum"); ret->createOptionalParameter(7, "-sum-subvols", "output the sum of the extrema subvolumes instead of each subvolume separately"); ret->createOptionalParameter(8, "-consolidate-mode", "use consolidation of local minima instead of a large neighborhood"); ret->createOptionalParameter(10, "-only-maxima", "only find the maxima"); ret->createOptionalParameter(11, "-only-minima", "only find the minima"); OptionalParameter* subvolOpt = ret->createOptionalParameter(9, "-subvolume", "select a single subvolume to find extrema in"); subvolOpt->addStringParameter(1, "subvolume", "the subvolume number or name"); ret->setHelpText( AString("Finds extrema in a volume file, such that no two extrema of the same type are within of each other. ") + "The extrema are labeled as -1 for minima, 1 for maxima, 0 otherwise. " + "If -only-maxima or -only-minima is specified, then it will ignore extrema not of the specified type. These options are mutually exclusive.\n\n" + "If -sum-subvols is specified, these extrema subvolumes are summed, and the output has a single subvolume with this result.\n\n" + "By default, a datapoint is an extrema only if it is more extreme than every other datapoint that is within from it. " + "If -consolidate-mode is used, it instead starts by finding all datapoints that are more extreme than their immediate neighbors, " + "then while there are any extrema within of each other, take the two extrema closest to each other and merge them into one by a weighted average " + "based on how many original extrema have been merged into each.\n\n" + "By default, all input subvolumes are used with no smoothing, use -subvolume to specify a single subvolume to use, and -presmooth to smooth the input before " + "finding the extrema." ); return ret; } void AlgorithmVolumeExtrema::useParameters(OperationParameters* myParams, ProgressObject* myProgObj) { VolumeFile* myVolIn = myParams->getVolume(1); float distance = (float)myParams->getDouble(2); VolumeFile* myVolOut = myParams->getOutputVolume(3); OptionalParameter* presmoothOpt = myParams->getOptionalParameter(4); float presmooth = -1.0f; if (presmoothOpt->m_present) { presmooth = presmoothOpt->getDouble(1); if (presmooth <= 0.0f) { throw AlgorithmException("smoothing kernel must be positive"); } } OptionalParameter* roiOpt = myParams->getOptionalParameter(5); VolumeFile* myRoi = NULL; if (roiOpt->m_present) { myRoi = roiOpt->getVolume(1); } OptionalParameter* thresholdOpt = myParams->getOptionalParameter(6); bool thresholdMode = false; float lowThresh = 0.0f, highThresh = 0.0f; if (thresholdOpt->m_present) { thresholdMode = true; lowThresh = (float)thresholdOpt->getDouble(1); highThresh = (float)thresholdOpt->getDouble(2); } bool sumSubvols = myParams->getOptionalParameter(7)->m_present; bool consolidateMode = myParams->getOptionalParameter(8)->m_present; OptionalParameter* subvolOpt = myParams->getOptionalParameter(9); int subvol = -1; if (subvolOpt->m_present) {//set up to use the single column subvol = (int)myVolIn->getMapIndexFromNameOrNumber(subvolOpt->getString(1)); if (subvol < 0) { throw AlgorithmException("invalid subvolume specified"); } } bool ignoreMinima = myParams->getOptionalParameter(10)->m_present; bool ignoreMaxima = myParams->getOptionalParameter(11)->m_present; if (ignoreMinima && ignoreMaxima) throw AlgorithmException("you may not specify both -only-maxima and -only-minima"); if (thresholdMode) { AlgorithmVolumeExtrema(myProgObj, myVolIn, distance, myVolOut, lowThresh, highThresh, myRoi, presmooth, sumSubvols, consolidateMode, ignoreMinima, ignoreMaxima, subvol); } else { AlgorithmVolumeExtrema(myProgObj, myVolIn, distance, myVolOut, myRoi, presmooth, sumSubvols, consolidateMode, ignoreMinima, ignoreMaxima, subvol); } } AlgorithmVolumeExtrema::AlgorithmVolumeExtrema(ProgressObject* myProgObj, const VolumeFile* myVolIn, const float& distance, VolumeFile* myVolOut, const VolumeFile* myRoi, const float& presmooth, const bool& sumSubvols, const bool& consolidateMode, bool ignoreMinima, bool ignoreMaxima, const int& subvol) : AbstractAlgorithm(myProgObj) { LevelProgress myProgress(myProgObj); if (ignoreMinima && ignoreMaxima) throw AlgorithmException("AlgorithmVolumeExtrema called with ignoreMinima and ignoreMaxima both true"); if (myRoi != NULL && !myVolIn->matchesVolumeSpace(myRoi)) { throw AlgorithmException("roi doesn't match input volume space"); } vector myDims; myVolIn->getDimensions(myDims); if (subvol < -1 || subvol >= myDims[3]) { throw AlgorithmException("invalid subvolume specified"); } int useSubvol = subvol; const VolumeFile* toProcess = myVolIn; VolumeFile tempVol; if (presmooth > 0.0f) { AlgorithmVolumeSmoothing(NULL, myVolIn, presmooth, &tempVol, myRoi, false, subvol); toProcess = &tempVol; if (subvol != -1) { useSubvol = 0; } } if (!consolidateMode) { precomputeStencil(myVolIn, distance); } if (subvol == -1) { vector minima, maxima; vector outDims = myDims; outDims.resize(4); if (sumSubvols) { outDims[3] = 1; } myVolOut->reinitialize(outDims, myVolIn->getSform(), myDims[4]); if (sumSubvols) { myVolOut->setMapName(0, "sum of extrema"); } myVolOut->setValueAllVoxels(0.0f); for (int s = 0; s < myDims[3]; ++s) { for (int c = 0; c < myDims[4]; ++c) { if (consolidateMode) { findExtremaConsolidate(toProcess, s, c, myRoi, distance, false, 0.0f, 0.0f, ignoreMinima, ignoreMaxima, minima, maxima); } else { findExtremaStencils(toProcess, s, c, myRoi, false, 0.0f, 0.0f, ignoreMinima, ignoreMaxima, minima, maxima); } if (sumSubvols) { int64_t numElems = (int64_t)minima.size(); for (int64_t i = 0; i < numElems; ++i) { myVolOut->setValue(myVolOut->getValue(minima[i].m_ijk, 0, c) - 1.0f, minima[i].m_ijk, 0, c); } numElems = (int64_t)maxima.size(); for (int64_t i = 0; i < numElems; ++i) { myVolOut->setValue(myVolOut->getValue(maxima[i].m_ijk, 0, c) + 1.0f, maxima[i].m_ijk, 0, c); } } else { if (c == 0) myVolOut->setMapName(s, "extrema of " + myVolIn->getMapName(s)); int64_t numElems = (int64_t)minima.size(); for (int64_t i = 0; i < numElems; ++i) { myVolOut->setValue(-1.0f, minima[i].m_ijk, s, c); } numElems = (int64_t)maxima.size(); for (int64_t i = 0; i < numElems; ++i) { myVolOut->setValue(1.0f, maxima[i].m_ijk, s, c); } } } } } else { vector minima, maxima; vector outDims = myDims; outDims.resize(3); myVolOut->reinitialize(outDims, myVolIn->getSform(), myDims[4]); myVolOut->setMapName(0, "extrema of " + myVolIn->getMapName(subvol)); myVolOut->setValueAllVoxels(0.0f); for (int c = 0; c < myDims[4]; ++c) { if (consolidateMode) { findExtremaConsolidate(toProcess, useSubvol, c, myRoi, distance, false, 0.0f, 0.0f, ignoreMinima, ignoreMaxima, minima, maxima); } else { findExtremaStencils(toProcess, useSubvol, c, myRoi, false, 0.0f, 0.0f, ignoreMinima, ignoreMaxima, minima, maxima); } int64_t numElems = (int64_t)minima.size(); for (int64_t i = 0; i < numElems; ++i) { myVolOut->setValue(-1.0f, minima[i].m_ijk, 0, c); } numElems = (int64_t)maxima.size(); for (int64_t i = 0; i < numElems; ++i) { myVolOut->setValue(1.0f, maxima[i].m_ijk, 0, c); } } } } AlgorithmVolumeExtrema::AlgorithmVolumeExtrema(ProgressObject* myProgObj, const VolumeFile* myVolIn, const float& distance, VolumeFile* myVolOut, const float& lowThresh, const float& highThresh, const VolumeFile* myRoi, const float& presmooth, const bool& sumSubvols, const bool& consolidateMode, bool ignoreMinima, bool ignoreMaxima, const int& subvol) : AbstractAlgorithm(myProgObj) { LevelProgress myProgress(myProgObj); if (ignoreMinima && ignoreMaxima) throw AlgorithmException("AlgorithmVolumeExtrema called with ignoreMinima and ignoreMaxima both true"); if (myRoi != NULL && !myVolIn->matchesVolumeSpace(myRoi)) { throw AlgorithmException("roi doesn't match input volume space"); } vector myDims; myVolIn->getDimensions(myDims); if (subvol < -1 || subvol >= myDims[3]) { throw AlgorithmException("invalid subvolume specified"); } int useSubvol = subvol; const VolumeFile* toProcess = myVolIn; VolumeFile tempVol; if (presmooth > 0.0f) { AlgorithmVolumeSmoothing(NULL, myVolIn, presmooth, &tempVol, myRoi, false, subvol); toProcess = &tempVol; if (subvol != -1) { useSubvol = 0; } } if (!consolidateMode) { precomputeStencil(myVolIn, distance); } if (subvol == -1) { vector minima, maxima; vector outDims = myDims; outDims.resize(4); if (sumSubvols) { outDims[3] = 1; } myVolOut->reinitialize(outDims, myVolIn->getSform(), myDims[4]); if (sumSubvols) { myVolOut->setMapName(0, "sum of extrema"); } myVolOut->setValueAllVoxels(0.0f); for (int s = 0; s < myDims[3]; ++s) { for (int c = 0; c < myDims[4]; ++c) { if (consolidateMode) { findExtremaConsolidate(toProcess, s, c, myRoi, distance, true, lowThresh, highThresh, ignoreMinima, ignoreMaxima, minima, maxima); } else { findExtremaStencils(toProcess, s, c, myRoi, true, lowThresh, highThresh, ignoreMinima, ignoreMaxima, minima, maxima); } if (sumSubvols) { int64_t numElems = (int64_t)minima.size(); for (int64_t i = 0; i < numElems; ++i) { myVolOut->setValue(myVolOut->getValue(minima[i].m_ijk, 0, c) - 1.0f, minima[i].m_ijk, 0, c); } numElems = (int64_t)maxima.size(); for (int64_t i = 0; i < numElems; ++i) { myVolOut->setValue(myVolOut->getValue(maxima[i].m_ijk, 0, c) + 1.0f, maxima[i].m_ijk, 0, c); } } else { if (c == 0) myVolOut->setMapName(s, "extrema of " + myVolIn->getMapName(s)); int64_t numElems = (int64_t)minima.size(); for (int64_t i = 0; i < numElems; ++i) { myVolOut->setValue(-1.0f, minima[i].m_ijk, s, c); } numElems = (int64_t)maxima.size(); for (int64_t i = 0; i < numElems; ++i) { myVolOut->setValue(1.0f, maxima[i].m_ijk, s, c); } } } } } else { vector minima, maxima; vector outDims = myDims; outDims.resize(3); myVolOut->reinitialize(outDims, myVolIn->getSform(), myDims[4]); myVolOut->setMapName(0, "extrema of " + myVolIn->getMapName(subvol)); myVolOut->setValueAllVoxels(0.0f); for (int c = 0; c < myDims[4]; ++c) { if (consolidateMode) { findExtremaConsolidate(toProcess, useSubvol, c, myRoi, distance, true, lowThresh, highThresh, ignoreMinima, ignoreMaxima, minima, maxima); } else { findExtremaStencils(toProcess, useSubvol, c, myRoi, true, lowThresh, highThresh, ignoreMinima, ignoreMaxima, minima, maxima); } int64_t numElems = (int64_t)minima.size(); for (int64_t i = 0; i < numElems; ++i) { myVolOut->setValue(-1.0f, minima[i].m_ijk, 0, c); } numElems = (int64_t)maxima.size(); for (int64_t i = 0; i < numElems; ++i) { myVolOut->setValue(1.0f, maxima[i].m_ijk, 0, c); } } } } void AlgorithmVolumeExtrema::precomputeStencil(const VolumeFile* myVolIn, const float& distance) { m_stencil.clear(); const vector >& volSpace = myVolIn->getSform(); Vector3D ivec, jvec, kvec, origin; ivec[0] = volSpace[0][0]; jvec[0] = volSpace[0][1]; kvec[0] = volSpace[0][2]; origin[0] = volSpace[0][3]; ivec[1] = volSpace[1][0]; jvec[1] = volSpace[1][1]; kvec[1] = volSpace[1][2]; origin[1] = volSpace[1][3]; ivec[2] = volSpace[2][0]; jvec[2] = volSpace[2][1]; kvec[2] = volSpace[2][2]; origin[2] = volSpace[2][3]; Vector3D ijorth = ivec.cross(jvec).normal();//find the bounding box that encloses a sphere of radius kernBox Vector3D jkorth = jvec.cross(kvec).normal(); Vector3D kiorth = kvec.cross(ivec).normal(); m_irange = (int)floor(abs(distance / ivec.dot(jkorth)));//these are member variables so we can avoid testing for things outside the volume bounding box when the stencil is m_jrange = (int)floor(abs(distance / jvec.dot(kiorth)));// guaranteed within the bounding box m_krange = (int)floor(abs(distance / kvec.dot(ijorth))); if (m_irange < 1) m_irange = 1;//don't underflow if (m_jrange < 1) m_jrange = 1; if (m_krange < 1) m_krange = 1; float dist2 = distance * distance;//don't require square root calls VoxelIJK tempVox; bool ichange = false, jchange = false, kchange = false;//ensure that stencil is 3D, not degenerate for (int k = -m_krange; k <= m_krange; ++k)//generate the stencil list in the same index order as the volume file { Vector3D kpart = k * kvec; tempVox.m_ijk[2] = k; for (int j = -m_jrange; j <= m_jrange; ++j) { Vector3D jpart = (j * jvec) + kpart;//I could do an extra test here, or recompute the range, but we only do this once, so whatever tempVox.m_ijk[1] = j; for (int i = -m_irange; i <= m_irange; ++i) { if (k == 0 && j == 0 && i == 0) continue;//skip the center voxel if ((jpart + (i * ivec)).lengthsquared() <= dist2) { tempVox.m_ijk[0] = i; m_stencil.push_back(tempVox); if (k != 0) kchange = true; if (j != 0) jchange = true; if (i != 0) ichange = true; } } } } if (!ichange || !jchange || !kchange) { CaretLogWarning("distance too small, stencil did not use all 3 dimensions, substituting in 6-neighbor stencil"); m_irange = 1; m_jrange = 1; m_krange = 1; m_stencil.clear(); m_stencil.push_back(VoxelIJK(-1, 0, 0)); m_stencil.push_back(VoxelIJK(0, -1, 0)); m_stencil.push_back(VoxelIJK(0, 0, -1)); m_stencil.push_back(VoxelIJK(0, 0, 1)); m_stencil.push_back(VoxelIJK(0, 1, 0)); m_stencil.push_back(VoxelIJK(1, 0, 0)); } } void AlgorithmVolumeExtrema::findExtremaStencils(const VolumeFile* toProcess, const int& s, const int& c, const VolumeFile* myRoi, const bool& threshMode, const float& lowThresh, const float& highThresh, bool ignoreMinima, bool ignoreMaxima, vector& minima, vector& maxima) { minima.clear(); maxima.clear(); vector myDims; toProcess->getDimensions(myDims); int64_t frameSize = myDims[0] * myDims[1] * myDims[2]; int stencilSize = (int)m_stencil.size(); vector minPos(frameSize, 1), maxPos(frameSize, 1);//mark things off that fail a comparison, to reduce redundant comparisons const float* dataFrame = toProcess->getFrame(s, c); const float* roiFrame = NULL; if (myRoi != NULL) { roiFrame = myRoi->getFrame(); } #pragma omp CARET_PARFOR schedule(dynamic) for (int64_t k = 0; k < myDims[2]; ++k) { bool ksafe = (k >= m_krange && k < myDims[2] - m_krange);//so we can avoid performing certain bounds checks in the inner loop for (int64_t j = 0; j < myDims[1]; ++j) { bool jsafe = (j >= m_jrange && j < myDims[1] - m_jrange); for (int64_t i = 0; i < myDims[0]; ++i) { int64_t myindex = toProcess->getIndex(i, j, k); if (roiFrame == NULL || roiFrame[myindex] > 0.0f) { bool canBeMin = minPos[myindex] && !ignoreMinima; bool canBeMax = maxPos[myindex] && !ignoreMaxima; float myval = dataFrame[myindex]; if (threshMode) { if (myval > lowThresh) canBeMin = false;//check thresholds if (myval < highThresh) canBeMax = false; } if (canBeMin || canBeMax) { bool isafe = (i >= m_jrange && i < myDims[0] - m_irange); if (isafe && jsafe && ksafe)//special case out the inner loops to avoid branching when possible { if (roiFrame == NULL) { int v = 0; if (canBeMin && canBeMax)//no ROI and safe means we can just use the first element in stencil to break the double test { VoxelIJK& offset = m_stencil[0]; int64_t testVox[3] = { i + offset.m_ijk[0], j + offset.m_ijk[1], k + offset.m_ijk[2] }; int64_t otherindex = toProcess->getIndex(testVox); float otherval = dataFrame[otherindex]; if (myval < otherval) { minPos[otherindex] = 0; } else { canBeMin = false; } if (myval > otherval) { maxPos[otherindex] = 0; } else { canBeMax = false; } v = 1; } if (canBeMin) { for (; v < stencilSize; ++v) { VoxelIJK& offset = m_stencil[v]; int64_t testVox[3] = { i + offset.m_ijk[0], j + offset.m_ijk[1], k + offset.m_ijk[2] }; int64_t otherindex = toProcess->getIndex(testVox); float otherval = dataFrame[otherindex]; if (myval < otherval) { minPos[otherindex] = 0; } else { canBeMin = false; break; } } } if (canBeMax) { for (; v < stencilSize; ++v) { VoxelIJK& offset = m_stencil[v]; int64_t testVox[3] = { i + offset.m_ijk[0], j + offset.m_ijk[1], k + offset.m_ijk[2] }; int64_t otherindex = toProcess->getIndex(testVox); float otherval = dataFrame[otherindex]; if (myval > otherval) { maxPos[otherindex] = 0; } else { canBeMax = false; break; } } } } else { int v = 0; if (canBeMin && canBeMax)//we have an roi, so we may need to loop before the double test hits a valid neighbor { for (; v < stencilSize; ++v) { VoxelIJK& offset = m_stencil[v]; int64_t testVox[3] = { i + offset.m_ijk[0], j + offset.m_ijk[1], k + offset.m_ijk[2] }; int64_t otherindex = toProcess->getIndex(testVox); if (roiFrame[otherindex] > 0.0f) { float otherval = dataFrame[otherindex]; if (myval < otherval) { minPos[otherindex] = 0; } else { canBeMin = false; } if (myval > otherval) { maxPos[otherindex] = 0; } else { canBeMax = false; } ++v;//don't test the same voxel again break;//we have eliminated one possibility, so we can move to a loop with fewer tests } else { if (abs(offset.m_ijk[0]) + abs(offset.m_ijk[1]) + abs(offset.m_ijk[2]) == 1) { canBeMax = false;//if we find a face neighbor outside the roi, don't count this as an extrema canBeMin = false; break; } } } } if (canBeMin) { for (; v < stencilSize; ++v) { VoxelIJK& offset = m_stencil[v]; int64_t testVox[3] = { i + offset.m_ijk[0], j + offset.m_ijk[1], k + offset.m_ijk[2] }; int64_t otherindex = toProcess->getIndex(testVox); if (roiFrame[otherindex] > 0.0f) { float otherval = dataFrame[otherindex]; if (myval < otherval) { minPos[otherindex] = 0; } else { canBeMin = false; break; } } else { if (abs(offset.m_ijk[0]) + abs(offset.m_ijk[1]) + abs(offset.m_ijk[2])) { canBeMin = false;//if we find a face neighbor outside the roi, don't count this as an extrema break; } } } } if (canBeMax) { for (; v < stencilSize; ++v) { VoxelIJK& offset = m_stencil[v]; int64_t testVox[3] = { i + offset.m_ijk[0], j + offset.m_ijk[1], k + offset.m_ijk[2] }; int64_t otherindex = toProcess->getIndex(testVox); if (roiFrame[otherindex] > 0.0f) { float otherval = dataFrame[otherindex]; if (myval > otherval) { maxPos[otherindex] = 0; } else { canBeMax = false; break; } } else { if (abs(offset.m_ijk[0]) + abs(offset.m_ijk[1]) + abs(offset.m_ijk[2]) == 1) { canBeMax = false;//if we find a face neighbor outside the roi, don't count this as an extrema break; } } } } if (canBeMin && canBeMax)//only way for this to happen is if there are no neighbors in the roi { canBeMax = false;//don't count isolated voxels as extrema canBeMin = false; } } } else {//if we are near an edge, we have to check all the neighbor indexes if (i < 1 || i >= myDims[0] - 1 || j < 1 || j >= myDims[1] - 1 || k < 1 || k >= myDims[2] - 1) { canBeMax = false;//but if we are on the edge of the volume, that is effectively the same as on the edge of the roi canBeMin = false;//so, don't count any extrema here } else { for (int v = 0; v < stencilSize; ++v) { VoxelIJK& offset = m_stencil[v]; int64_t testVox[3] = { i + offset.m_ijk[0], j + offset.m_ijk[1], k + offset.m_ijk[2] }; if (!ksafe && (testVox[2] < 0 || testVox[2] >= myDims[2])) continue; if (!jsafe && (testVox[1] < 0 || testVox[1] >= myDims[1])) continue; if (!isafe && (testVox[0] < 0 || testVox[0] >= myDims[0])) continue; int64_t otherindex = toProcess->getIndex(testVox); if (roiFrame != NULL && roiFrame[otherindex] <= 0.0f) { if (abs(offset.m_ijk[0]) + abs(offset.m_ijk[1]) + abs(offset.m_ijk[2]) == 1) { canBeMax = false; canBeMin = false; break; } else { continue; } } float otherval = dataFrame[otherindex]; if (myval < otherval)//since we are checking index bounds anyway, just do the double test to make the code simpler { minPos[otherindex] = 0; } else { canBeMin = false; if (!canBeMax) break; } if (myval > otherval) { maxPos[otherindex] = 0; } else { canBeMax = false; if (!canBeMin) break; } } } /*if (canBeMin && canBeMax)//this can't happen anymore { canBeMax = false;//don't count isolated voxels as extrema canBeMin = false; }//*/ } if (canBeMin) { #pragma omp critical { minima.push_back(VoxelIJK(i, j, k)); } } if (canBeMax) { #pragma omp critical { maxima.push_back(VoxelIJK(i, j, k)); } } } } } } } } void AlgorithmVolumeExtrema::findExtremaConsolidate(const VolumeFile* toProcess, const int& s, const int& c, const VolumeFile* myRoi, const float& distance, const bool& threshMode, const float& lowThresh, const float& highThresh, bool ignoreMinima, bool ignoreMaxima, vector& minima, vector& maxima) { const int stencil[18] = {-1, 0, 0, 0, -1, 0, 0, 0, -1, 0, 0, 1, 0, 1, 0, 1, 0, 0}; const int STENCIL_SIZE = 18; minima.clear(); maxima.clear(); vector myDims; toProcess->getDimensions(myDims); int64_t frameSize = myDims[0] * myDims[1] * myDims[2]; vector minPos(frameSize, 1), maxPos(frameSize, 1);//mark things off that fail a comparison, to reduce redundant comparisons const float* dataFrame = toProcess->getFrame(s, c); const float* roiFrame = NULL; vector > tempExtrema[2]; if (myRoi != NULL) { roiFrame = myRoi->getFrame(); } #pragma omp CARET_PARFOR schedule(dynamic) for (int64_t k = 0; k < myDims[2]; ++k) { bool ksafe = (k > 0 && k < myDims[2] - 1);//so we can avoid performing certain bounds checks in the inner loop for (int64_t j = 0; j < myDims[1]; ++j) { bool jsafe = (j > 0 && j < myDims[1] - 1); for (int64_t i = 0; i < myDims[0]; ++i) { int64_t myindex = toProcess->getIndex(i, j, k); if (roiFrame == NULL || roiFrame[myindex] > 0.0f) { bool canBeMin = minPos[myindex] && !ignoreMinima; bool canBeMax = maxPos[myindex] && !ignoreMaxima; float myval = dataFrame[myindex]; if (threshMode) { if (myval > lowThresh) canBeMin = false;//check thresholds if (myval < highThresh) canBeMax = false; } if (canBeMin || canBeMax) { bool isafe = (i > 0 && i < myDims[0] - 1); if (isafe && jsafe && ksafe)//special case out the inner loops to avoid branching when possible { if (roiFrame == NULL) { int v = 0; if (canBeMin && canBeMax)//no ROI and safe means we can just use the first element in stencil to break the double test { int64_t testVox[3] = { i + stencil[0], j + stencil[1], k + stencil[2] }; int64_t otherindex = toProcess->getIndex(testVox); float otherval = dataFrame[otherindex]; if (myval < otherval) { minPos[otherindex] = 0; } else { canBeMin = false; } if (myval > otherval) { maxPos[otherindex] = 0; } else { canBeMax = false; } v = 3; } if (canBeMin) { for (; v < STENCIL_SIZE; v += 3) { int64_t testVox[3] = { i + stencil[v], j + stencil[v + 1], k + stencil[v + 2] }; int64_t otherindex = toProcess->getIndex(testVox); float otherval = dataFrame[otherindex]; if (myval < otherval) { minPos[otherindex] = 0; } else { canBeMin = false; break; } } } if (canBeMax) { for (; v < STENCIL_SIZE; v += 3) { int64_t testVox[3] = { i + stencil[v], j + stencil[v + 1], k + stencil[v + 2] }; int64_t otherindex = toProcess->getIndex(testVox); float otherval = dataFrame[otherindex]; if (myval > otherval) { maxPos[otherindex] = 0; } else { canBeMax = false; break; } } } } else { if (canBeMin && canBeMax)//we have an roi, but if a neighbor falls outside it, we are done anyway, we don't count extrema on the edge { int64_t testVox[3] = { i + stencil[0], j + stencil[1], k + stencil[2] }; int64_t otherindex = toProcess->getIndex(testVox); if (roiFrame[otherindex] > 0.0f) { float otherval = dataFrame[otherindex]; if (myval < otherval) { minPos[otherindex] = 0; } else { canBeMin = false; } if (myval > otherval) { maxPos[otherindex] = 0; } else { canBeMax = false; } } else { canBeMax = false;//we are next to an roi edge, do not count this as an extrema canBeMin = false; } } int v = 3; if (canBeMin) { for (; v < STENCIL_SIZE; v += 3) { int64_t testVox[3] = { i + stencil[v], j + stencil[v + 1], k + stencil[v + 2] }; int64_t otherindex = toProcess->getIndex(testVox); if (roiFrame[otherindex] > 0.0f) { float otherval = dataFrame[otherindex]; if (myval < otherval) { minPos[otherindex] = 0; } else { canBeMin = false; break; } } else { canBeMin = false;//we are next to an roi edge, do not count this as an extrema break; } } } if (canBeMax) { for (; v < STENCIL_SIZE; v += 3) { int64_t testVox[3] = { i + stencil[v], j + stencil[v + 1], k + stencil[v + 2] }; int64_t otherindex = toProcess->getIndex(testVox); if (roiFrame[otherindex] > 0.0f) { float otherval = dataFrame[otherindex]; if (myval > otherval) { maxPos[otherindex] = 0; } else { canBeMax = false; break; } } else { canBeMax = false;//we are next to an roi edge, do not count this as an extrema } } } /*if (canBeMin && canBeMax)//this can't happen anymore { canBeMax = false;//don't count isolated voxels as extrema canBeMin = false; }//*/ } } else {//if we are near an edge, we have to check all the neighbor indexes for (int v = 0; v < STENCIL_SIZE; v += 3) { int64_t testVox[3] = { i + stencil[v], j + stencil[v + 1], k + stencil[v + 2] }; if (!ksafe && (testVox[2] < 0 || testVox[2] >= myDims[2])) continue; if (!jsafe && (testVox[1] < 0 || testVox[1] >= myDims[1])) continue; if (!isafe && (testVox[0] < 0 || testVox[0] >= myDims[0])) continue; int64_t otherindex = toProcess->getIndex(testVox); if (roiFrame != NULL && roiFrame[otherindex] <= 0.0f) { canBeMax = false;//neighbor outside the roi means on the roi edge, don't count as extrema canBeMin = false; break; } float otherval = dataFrame[otherindex]; if (myval < otherval)//since we are checking index bounds anyway, just do the double test to make the code simpler { minPos[otherindex] = 0; } else { canBeMin = false; if (!canBeMax) break; } if (myval > otherval) { maxPos[otherindex] = 0; } else { canBeMax = false; if (!canBeMin) break; } } /*if (canBeMin && canBeMax)//this can't happen anymore { canBeMax = false;//don't count isolated voxels as extrema canBeMin = false; }//*/ } if (canBeMax) { Vector3D tempvec; toProcess->indexToSpace(i, j, k, tempvec); #pragma omp critical { tempExtrema[0].push_back(pair(tempvec, 1)); } } if (canBeMin) { Vector3D tempvec; toProcess->indexToSpace(i, j, k, tempvec); #pragma omp critical { tempExtrema[1].push_back(pair(tempvec, 1)); } } } } } } } consolidateStep(toProcess, distance, tempExtrema, minima, maxima); } void AlgorithmVolumeExtrema::consolidateStep(const VolumeFile* toProcess, const float& distance, vector > initExtrema[2], vector& minima, vector& maxima) { for (int sign = 0; sign < 2; ++sign) { int numInitExtrema = (int)initExtrema[sign].size(); vector removed(numInitExtrema, false);//track which extrema locations are dropped during consolidation - the one that isn't dropped in a merge has its node number changed vector > heapIDmatrix(numInitExtrema, vector(numInitExtrema, -1)); CaretMinHeap, float> myDistHeap; for (int i = 0; i < numInitExtrema - 1; ++i) { for (int j = i + 1; j < numInitExtrema; ++j) { float tempf = (initExtrema[sign][i].first - initExtrema[sign][j].first).length(); if (tempf < distance) { int64_t tempID = myDistHeap.push(pair(i, j), tempf); heapIDmatrix[i][j] = tempID; heapIDmatrix[j][i] = tempID; } } }//initial distance matrix computed, now we iterate while (!myDistHeap.isEmpty()) { pair toMerge = myDistHeap.pop();//we don't need to know the key int extr1 = toMerge.first; int extr2 = toMerge.second; heapIDmatrix[extr1][extr2] = -1; heapIDmatrix[extr2][extr1] = -1; int weight1 = initExtrema[sign][extr1].second; int weight2 = initExtrema[sign][extr2].second; if (weight2 > weight1)//swap so weight1 is always bigger { int temp = weight2; weight2 = weight1; weight1 = temp; temp = extr2; extr2 = extr1; extr1 = temp; } Vector3D point1 = initExtrema[sign][extr1].first; Vector3D point2 = initExtrema[sign][extr2].first; removed[extr2] = true;//drop the one that has less weight, and modify the one that has more weight for (int j = 0; j < numInitExtrema; ++j) { if (!removed[j]) { int64_t tempID = heapIDmatrix[extr2][j]; if (tempID != -1) { myDistHeap.remove(tempID); heapIDmatrix[extr2][j] = -1; heapIDmatrix[j][extr2] = -1; } } } Vector3D newPoint = (point1 * weight1 + point2 * weight2) / (weight1 + weight2); initExtrema[sign][extr1].first = newPoint; for (int j = 0; j < numInitExtrema; ++j) { if (!removed[j]) { float tempf = (newPoint - initExtrema[sign][j].first).length(); int64_t tempID = heapIDmatrix[extr1][j]; if (tempf < distance) { if (tempID != -1) { myDistHeap.changekey(tempID, tempf); } else { tempID = myDistHeap.push(pair(extr1, j), tempf); heapIDmatrix[extr1][j] = tempID; heapIDmatrix[j][extr1] = tempID; } } else { if (tempID != -1) { myDistHeap.remove(tempID); heapIDmatrix[extr1][j] = -1; heapIDmatrix[j][extr1] = -1; } } } } } if (sign == 0) { for (int i = 0; i < numInitExtrema; ++i) { if (!removed[i]) { VoxelIJK tempijk; toProcess->enclosingVoxel(initExtrema[sign][i].first, tempijk.m_ijk); maxima.push_back(tempijk); } } } else { for (int i = 0; i < numInitExtrema; ++i) { if (!removed[i]) { VoxelIJK tempijk; toProcess->enclosingVoxel(initExtrema[sign][i].first, tempijk.m_ijk); minima.push_back(tempijk); } } } } } float AlgorithmVolumeExtrema::getAlgorithmInternalWeight() { return 1.0f;//override this if needed, if the progress bar isn't smooth } float AlgorithmVolumeExtrema::getSubAlgorithmWeight() { //return AlgorithmInsertNameHere::getAlgorithmWeight();//if you use a subalgorithm return 0.0f; } connectome-workbench-1.2.3+git41-gc4c6c90/src/Algorithms/AlgorithmVolumeExtrema.h000066400000000000000000000066371300200146000275060ustar00rootroot00000000000000#ifndef __ALGORITHM_VOLUME_EXTREMA_H__ #define __ALGORITHM_VOLUME_EXTREMA_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 "AbstractAlgorithm.h" #include "Vector3D.h" #include "VoxelIJK.h" #include namespace caret { class VolumeFile; class AlgorithmVolumeExtrema : public AbstractAlgorithm { std::vector m_stencil; int64_t m_irange, m_jrange, m_krange; AlgorithmVolumeExtrema(); void precomputeStencil(const VolumeFile* myVolIn, const float& distance); void findExtremaConsolidate(const VolumeFile* toProcess, const int& s, const int& c, const VolumeFile* myRoi, const float& distance, const bool& threshMode, const float& lowThresh, const float& highThresh, bool ignoreMinima, bool ignoreMaxima, std::vector& minima, std::vector& maxima); void findExtremaStencils(const VolumeFile* toProcess, const int& s, const int& c, const VolumeFile* myRoi, const bool& threshMode, const float& lowThresh, const float& highThresh, bool ignoreMinima, bool ignoreMaxima, std::vector& minima, std::vector& maxima); void consolidateStep(const VolumeFile* toProcess, const float& distance, std::vector > tempExtrema[2], std::vector& minima, std::vector& maxima); protected: static float getSubAlgorithmWeight(); static float getAlgorithmInternalWeight(); public: AlgorithmVolumeExtrema(ProgressObject* myProgObj, const VolumeFile* myVolIn, const float& distance, VolumeFile* myVolOut, const float& lowThresh, const float& highThresh, const VolumeFile* myRoi = NULL, const float& presmooth = -1.0f, const bool& sumSubvols = false, const bool& consolidateMode = false, bool ignoreMinima = false, bool ignoreMaxima = false, const int& subvol = -1); AlgorithmVolumeExtrema(ProgressObject* myProgObj, const VolumeFile* myVolIn, const float& distance, VolumeFile* myVolOut, const VolumeFile* myRoi = NULL, const float& presmooth = -1.0f, const bool& sumSubvols = false, const bool& consolidateMode = false, bool ignoreMinima = false, bool ignoreMaxima = false, const int& subvol = -1); static OperationParameters* getParameters(); static void useParameters(OperationParameters* myParams, ProgressObject* myProgObj); static AString getCommandSwitch(); static AString getShortDescription(); }; typedef TemplateAutoOperation AutoAlgorithmVolumeExtrema; } #endif //__ALGORITHM_VOLUME_EXTREMA_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Algorithms/AlgorithmVolumeFillHoles.cxx000066400000000000000000000155151300200146000303300ustar00rootroot00000000000000/*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 "AlgorithmVolumeFillHoles.h" #include "AlgorithmException.h" #include "VolumeFile.h" #include using namespace caret; using namespace std; AString AlgorithmVolumeFillHoles::getCommandSwitch() { return "-volume-fill-holes"; } AString AlgorithmVolumeFillHoles::getShortDescription() { return "FILL HOLES IN AN ROI VOLUME"; } OperationParameters* AlgorithmVolumeFillHoles::getParameters() { OperationParameters* ret = new OperationParameters(); ret->addVolumeParameter(1, "volume-in", "the input ROI volume"); ret->addVolumeOutputParameter(2, "volume-out", "the output ROI volume"); ret->setHelpText( AString("Finds all face-connected parts that are not included in the ROI, and fills all but the largest one with ones.") ); return ret; } void AlgorithmVolumeFillHoles::useParameters(OperationParameters* myParams, ProgressObject* myProgObj) { VolumeFile* myVolIn = myParams->getVolume(1); VolumeFile* myVolOut = myParams->getOutputVolume(2); AlgorithmVolumeFillHoles(myProgObj, myVolIn, myVolOut); } AlgorithmVolumeFillHoles::AlgorithmVolumeFillHoles(ProgressObject* myProgObj, const VolumeFile* myVolIn, VolumeFile* myVolOut) : AbstractAlgorithm(myProgObj) { LevelProgress myProgress(myProgObj); const int STENCIL_SIZE = 18;//the easy way, and prepare for different stencils if we ever need them const int stencil[STENCIL_SIZE] = { 0, 0, -1, 0, -1, 0, -1, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1 }; vector dims; myVolIn->getDimensions(dims); myVolOut->reinitialize(myVolIn->getOriginalDimensions(), myVolIn->getSform(), myVolIn->getNumberOfComponents(), myVolIn->getType()); for (int s = 0; s < dims[3]; ++s) { myVolOut->setMapName(s, myVolIn->getMapName(s)); for (int c = 0; c < dims[4]; ++c) { int64_t ijk[3]; vector > parts; vector used(dims[0] * dims[1] * dims[2], 0); const float* frame = myVolIn->getFrame(s, c); for (ijk[2] = 0; ijk[2] < dims[2]; ++ijk[2]) { for (ijk[1] = 0; ijk[1] < dims[1]; ++ijk[1]) { for (ijk[0] = 0; ijk[0] < dims[0]; ++ijk[0]) { int64_t index = myVolIn->getIndex(ijk); if (used[index] == 0 && !(frame[index] > 0.0f))//use "not greater than" in case someone uses NaNs in their ROI { parts.push_back(vector()); vector& thispart = parts.back(); thispart.push_back(ijk[0]); thispart.push_back(ijk[1]); thispart.push_back(ijk[2]); used[index] = 1; vector mystack; mystack.push_back(ijk[0]); mystack.push_back(ijk[1]); mystack.push_back(ijk[2]); while (!mystack.empty()) { int64_t curSize = (int64_t)mystack.size(); int64_t curvox[3] = { mystack[curSize - 3], mystack[curSize - 2], mystack[curSize - 1] }; mystack.resize(curSize - 3);//basically pop_back for (int i = 0; i < STENCIL_SIZE; i += 3) { int64_t neighbor[3] = { curvox[0] + stencil[i], curvox[1] + stencil[i + 1], curvox[2] + stencil[i + 2] }; if (myVolIn->indexValid(neighbor)) { int64_t neighindex = myVolIn->getIndex(neighbor); if (used[neighindex] == 0 && !(frame[neighindex] > 0.0f)) { thispart.push_back(neighbor[0]); thispart.push_back(neighbor[1]); thispart.push_back(neighbor[2]); used[neighindex] = 1; mystack.push_back(neighbor[0]); mystack.push_back(neighbor[1]); mystack.push_back(neighbor[2]); } } } } } } } } int64_t bestCount = -1, bestPart = -1, numParts = (int64_t)parts.size(); for (int64_t i = 0; i < numParts; ++i) { int64_t thisCount = (int64_t)parts[i].size(); if (thisCount > bestCount) { bestCount = thisCount; bestPart = i; } } vector outFrame(dims[0] * dims[1] * dims[2], 1.0f); if (bestPart != -1) { vector& myPart = parts[bestPart]; for (int64_t i = 0; i < bestCount; i += 3) { int64_t myIndex = myVolIn->getIndex(myPart.data() + i); outFrame[myIndex] = 0.0f;//make it a simple 0/1 volume, even if it wasn't before } } myVolOut->setFrame(outFrame.data(), s, c); } } } float AlgorithmVolumeFillHoles::getAlgorithmInternalWeight() { return 1.0f;//override this if needed, if the progress bar isn't smooth } float AlgorithmVolumeFillHoles::getSubAlgorithmWeight() { //return AlgorithmInsertNameHere::getAlgorithmWeight();//if you use a subalgorithm return 0.0f; } connectome-workbench-1.2.3+git41-gc4c6c90/src/Algorithms/AlgorithmVolumeFillHoles.h000066400000000000000000000032401300200146000277450ustar00rootroot00000000000000#ifndef __ALGORITHM_VOLUME_FILL_HOLES_H__ #define __ALGORITHM_VOLUME_FILL_HOLES_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 "AbstractAlgorithm.h" namespace caret { class AlgorithmVolumeFillHoles : public AbstractAlgorithm { AlgorithmVolumeFillHoles(); protected: static float getSubAlgorithmWeight(); static float getAlgorithmInternalWeight(); public: AlgorithmVolumeFillHoles(ProgressObject* myProgObj, const VolumeFile* myVolIn, VolumeFile* myVolOut); static OperationParameters* getParameters(); static void useParameters(OperationParameters* myParams, ProgressObject* myProgObj); static AString getCommandSwitch(); static AString getShortDescription(); }; typedef TemplateAutoOperation AutoAlgorithmVolumeFillHoles; } #endif //__ALGORITHM_VOLUME_FILL_HOLES_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Algorithms/AlgorithmVolumeFindClusters.cxx000066400000000000000000000347351300200146000310610ustar00rootroot00000000000000/*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 "AlgorithmVolumeFindClusters.h" #include "AlgorithmException.h" #include "CaretAssert.h" #include "CaretLogger.h" #include "CaretPointer.h" #include "CaretPointLocator.h" #include "VolumeFile.h" #include "VoxelIJK.h" #include #include using namespace caret; using namespace std; AString AlgorithmVolumeFindClusters::getCommandSwitch() { return "-volume-find-clusters"; } AString AlgorithmVolumeFindClusters::getShortDescription() { return "FILTER CLUSTERS BY VOLUME"; } OperationParameters* AlgorithmVolumeFindClusters::getParameters() { OperationParameters* ret = new OperationParameters(); ret->addVolumeParameter(1, "volume-in", "the input volume"); ret->addDoubleParameter(2, "value-threshold", "threshold for data values"); ret->addDoubleParameter(3, "minimum-volume", "threshold for cluster volume, in mm^3"); ret->addVolumeOutputParameter(4, "volume-out", "the output volume"); ret->createOptionalParameter(5, "-less-than", "find values less than , rather than greater"); OptionalParameter* roiOption = ret->createOptionalParameter(6, "-roi", "select a region of interest"); roiOption->addMetricParameter(1, "roi-volume", "the roi, as a volume file"); OptionalParameter* subvolSelect = ret->createOptionalParameter(7, "-subvolume", "select a single subvolume"); subvolSelect->addStringParameter(1, "subvol", "the subvolume number or name"); OptionalParameter* sizeRatioOpt = ret->createOptionalParameter(9, "-size-ratio", "ignore clusters smaller than a given fraction of the largest cluster in map"); sizeRatioOpt->addDoubleParameter(1, "ratio", "fraction of the largest cluster's volume"); OptionalParameter* distanceOpt = ret->createOptionalParameter(10, "-distance", "ignore clusters further than a given distance from the largest cluster"); distanceOpt->addDoubleParameter(1, "distance", "how far from the largest cluster a cluster can be, edge to edge, in mm"); OptionalParameter* startOpt = ret->createOptionalParameter(8, "-start", "start labeling clusters from a value other than 1"); startOpt->addIntegerParameter(1, "startval", "the value to give the first cluster found"); ret->setHelpText( AString("Outputs a volume with nonzero integers for all voxels within a large enough cluster, and zeros elsewhere. ") + "The integers denote cluster membership (by default, first cluster found will use value 1, second cluster 2, etc). " + "By default, values greater than are considered to be in a cluster, use -less-than to test for values less than the threshold. " + "To apply this as a mask to the data, or to do more complicated thresholding, see -volume-math." ); return ret; } void AlgorithmVolumeFindClusters::useParameters(OperationParameters* myParams, ProgressObject* myProgObj) { VolumeFile* volIn = myParams->getVolume(1); float threshValue = (float)myParams->getDouble(2); float minVolume = (float)myParams->getDouble(3); VolumeFile* volOut = myParams->getOutputVolume(4); bool lessThan = myParams->getOptionalParameter(5)->m_present; VolumeFile* myRoi = NULL; OptionalParameter* roiOption = myParams->getOptionalParameter(6); if (roiOption->m_present) { myRoi = roiOption->getVolume(1); } OptionalParameter* subvolSelect = myParams->getOptionalParameter(7); int subvolNum = -1; if (subvolSelect->m_present) { subvolNum = (int)volIn->getMapIndexFromNameOrNumber(subvolSelect->getString(1)); if (subvolNum < 0) { throw AlgorithmException("invalid subvolume specified"); } } OptionalParameter* startOpt = myParams->getOptionalParameter(8); int startVal = 1; if (startOpt->m_present) { startVal = (int)startOpt->getInteger(1); } OptionalParameter* sizeRatioOpt = myParams->getOptionalParameter(9); float sizeRatio = -1.0f; if (sizeRatioOpt->m_present) { sizeRatio = sizeRatioOpt->getDouble(1); if (sizeRatio <= 0.0f) { throw AlgorithmException("volume ratio must be positive"); } } OptionalParameter* distanceOpt = myParams->getOptionalParameter(10); float distaceCutoff = -1.0f; if (distanceOpt->m_present) { distaceCutoff = distanceOpt->getDouble(1); if (distaceCutoff <= 0.0f) { throw AlgorithmException("distance cutoff must be positive"); } } AlgorithmVolumeFindClusters(myProgObj, volIn, threshValue, minVolume, volOut, lessThan, myRoi, subvolNum, startVal, NULL, sizeRatio, distaceCutoff); } namespace { void processSubvol(const float* inFrame, VolumeFile* volOut, const int64_t& outSubvol, const int64_t& outComponent, const float& threshValue, const float& minVolume, const bool& lessThan, const float* roiFrame, const float& sizeRatio, const float& distanceCutoff, int& markVal) { vector dims = volOut->getDimensions(); int64_t frameSize = dims[0] * dims[1] * dims[2]; const VolumeSpace& mySpace = volOut->getVolumeSpace(); Vector3D ivec, jvec, kvec, origin; mySpace.getSpacingVectors(ivec, jvec, kvec, origin); float voxelVolume = abs(ivec.dot(jvec.cross(kvec))); int64_t minVoxels = (int64_t)ceil(minVolume / voxelVolume); vector neighbors; neighbors.push_back(VoxelIJK(1, 0, 0)); neighbors.push_back(VoxelIJK(-1, 0, 0)); neighbors.push_back(VoxelIJK(0, 1, 0)); neighbors.push_back(VoxelIJK(0, -1, 0)); neighbors.push_back(VoxelIJK(0, 0, 1)); neighbors.push_back(VoxelIJK(0, 0, -1)); vector marked(frameSize, 0); if (lessThan) { for (int64_t i = 0; i < frameSize; ++i) { if ((roiFrame == NULL || roiFrame[i] > 0.0f) && inFrame[i] < threshValue) { marked[i] = 1; } } } else { for (int64_t i = 0; i < frameSize; ++i) { if ((roiFrame == NULL || roiFrame[i] > 0.0f) && inFrame[i] > threshValue) { marked[i] = 1; } } } vector > clusters; size_t biggestCount = 0; int64_t biggestCluster = -1; 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) { int64_t myIndex = mySpace.getIndex(i, j, k); if (marked[myIndex]) { vector voxelList; voxelList.push_back(VoxelIJK(i, j, k)); marked[myIndex] = 0;//don't let voxels get duplicated in the list for (int64_t index = 0; index < (int64_t)voxelList.size(); ++index)//NOTE: vector grows inside loop { const VoxelIJK& thisVoxel = voxelList[index]; for (int n = 0; n < (int)neighbors.size(); ++n) { VoxelIJK neighVox(thisVoxel.m_ijk[0] + neighbors[n].m_ijk[0], thisVoxel.m_ijk[1] + neighbors[n].m_ijk[1], thisVoxel.m_ijk[2] + neighbors[n].m_ijk[2]); if (mySpace.indexValid(neighVox.m_ijk)) { int64_t neighIndex = mySpace.getIndex(neighVox.m_ijk); if (marked[neighIndex]) { voxelList.push_back(neighVox); marked[neighIndex] = 0; } } } } if ((int64_t)voxelList.size() >= minVoxels) { if (voxelList.size() > biggestCount) { biggestCount = voxelList.size(); biggestCluster = (int64_t)clusters.size(); } clusters.push_back(voxelList); } } } } } if (!clusters.empty()) CaretAssert(biggestCluster != -1); if (biggestCluster != -1 && (distanceCutoff > 0.0f || sizeRatio > 0.0f)) { CaretPointer myLocator; if (distanceCutoff > 0.0f) { vector biggestCoords;//gather coordinates of biggest cluster voxels biggestCoords.reserve(biggestCount * 3); for (size_t i = 0; i < clusters[biggestCluster].size(); ++i) { float thisCoord[3]; mySpace.indexToSpace(clusters[biggestCluster][i].m_ijk, thisCoord); biggestCoords.push_back(thisCoord[0]); biggestCoords.push_back(thisCoord[1]); biggestCoords.push_back(thisCoord[2]); } myLocator.grabNew(new CaretPointLocator(biggestCoords.data(), biggestCoords.size())); } for (size_t i = 0; i < clusters.size(); ++i) { if ((int64_t)i != biggestCluster) { bool erase = false; if (sizeRatio > 0.0f && ((float)clusters[i].size()) / biggestCount < sizeRatio) { erase = true; } if (!erase && distanceCutoff > 0.0f) { erase = true;//erase unless we find a point close enough to the biggest cluster for (size_t j = 0; j < clusters[i].size(); ++j) { float thisCoord[3]; mySpace.indexToSpace(clusters[i][j].m_ijk, thisCoord); int32_t ret = myLocator->closestPointLimited(thisCoord, distanceCutoff); if (ret == -1) { erase = false; break; } } } if (erase) { clusters.erase(clusters.begin() + i);//remove it --i;//don't skip a cluster if (biggestCluster > (int64_t)i) --biggestCluster;//don't lose track of the biggest cluster } } } } for (size_t i = 0; i < clusters.size(); ++i) { if (markVal == 0) { CaretLogInfo("skipping 0 for cluster marking"); ++markVal; } float tempVal = markVal; if ((int)tempVal != markVal) throw AlgorithmException("too many clusters, unable to mark them uniquely"); for (size_t index = 0; index < clusters[i].size(); ++index) { volOut->setValue(tempVal, clusters[i][index].m_ijk, outSubvol, outComponent); } ++markVal; } } } AlgorithmVolumeFindClusters::AlgorithmVolumeFindClusters(ProgressObject* myProgObj, const VolumeFile* volIn, const float& threshValue, const float& minVolume, VolumeFile* volOut, const bool& lessThan, const VolumeFile* myRoi, const int& subvolNum, const int& startVal, int* endVal, const float& sizeRatio, const float& distanceCutoff) : AbstractAlgorithm(myProgObj) { LevelProgress myProgress(myProgObj); if (startVal == 0) { throw AlgorithmException("0 is not a valid cluster marking start value"); } const VolumeSpace& mySpace = volIn->getVolumeSpace(); const float* roiFrame = NULL; if (myRoi != NULL) { if (!mySpace.matches(myRoi->getVolumeSpace())) throw AlgorithmException("roi volume space does not match input"); roiFrame = myRoi->getFrame(); } vector dims = volIn->getDimensions(); int markVal = startVal; if (subvolNum == -1) { volOut->reinitialize(volIn->getOriginalDimensions(), volIn->getSform(), dims[4]); volOut->setValueAllVoxels(0.0f); for (int64_t c = 0; c < dims[4]; ++c) { for (int64_t s = 0; s < dims[3]; ++s) { const float* inFrame = volIn->getFrame(s, c); processSubvol(inFrame, volOut, s, c, threshValue, minVolume, lessThan, roiFrame, sizeRatio, distanceCutoff, markVal); } } } else { vector outDims = volIn->getOriginalDimensions(); outDims.resize(3); volOut->reinitialize(outDims, volIn->getSform(), dims[4]); volOut->setValueAllVoxels(0.0f); for (int64_t c = 0; c < dims[4]; ++c) { const float* inFrame = volIn->getFrame(subvolNum, c); processSubvol(inFrame, volOut, 0, c, threshValue, minVolume, lessThan, roiFrame, sizeRatio, distanceCutoff, markVal); } } if (endVal != NULL) *endVal = markVal; } float AlgorithmVolumeFindClusters::getAlgorithmInternalWeight() { return 1.0f;//override this if needed, if the progress bar isn't smooth } float AlgorithmVolumeFindClusters::getSubAlgorithmWeight() { return 0.0f; } connectome-workbench-1.2.3+git41-gc4c6c90/src/Algorithms/AlgorithmVolumeFindClusters.h000066400000000000000000000037711300200146000305020ustar00rootroot00000000000000#ifndef __ALGORITHM_VOLUME_FIND_CLUSTERS_H__ #define __ALGORITHM_VOLUME_FIND_CLUSTERS_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 "AbstractAlgorithm.h" namespace caret { class AlgorithmVolumeFindClusters : public AbstractAlgorithm { AlgorithmVolumeFindClusters(); protected: static float getSubAlgorithmWeight(); static float getAlgorithmInternalWeight(); public: AlgorithmVolumeFindClusters(ProgressObject* myProgObj, const VolumeFile* volIn, const float& threshValue, const float& minVolume, VolumeFile* volOut, const bool& lessThan = false, const VolumeFile* myRoi = NULL, const int& subvolNum = -1, const int& startVal = 1, int* endVal = NULL, const float& sizeRatio = -1.0f, const float& distanceCutoff = -1.0f); static OperationParameters* getParameters(); static void useParameters(OperationParameters* myParams, ProgressObject* myProgObj); static AString getCommandSwitch(); static AString getShortDescription(); }; typedef TemplateAutoOperation AutoAlgorithmVolumeFindClusters; } #endif //__ALGORITHM_VOLUME_FIND_CLUSTERS_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Algorithms/AlgorithmVolumeGradient.cxx000066400000000000000000001166201300200146000302030ustar00rootroot00000000000000/*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 "AlgorithmException.h" #include "AlgorithmVolumeGradient.h" #include "AlgorithmVolumeSmoothing.h" #include "CaretOMP.h" #include "FloatMatrix.h" #include "MathFunctions.h" #include "Vector3D.h" #include "VolumeFile.h" #include #include using namespace caret; using namespace std; AString AlgorithmVolumeGradient::getCommandSwitch() { return "-volume-gradient"; } AString AlgorithmVolumeGradient::getShortDescription() { return "GRADIENT OF A VOLUME FILE"; } OperationParameters* AlgorithmVolumeGradient::getParameters() { OperationParameters* ret = new OperationParameters(); ret->addVolumeParameter(1, "volume-in", "the input volume"); ret->addVolumeOutputParameter(2, "volume-out", "the output gradient magnitude volume"); OptionalParameter* presmoothOpt = ret->createOptionalParameter(3, "-presmooth", "smooth the volume before computing the gradient"); presmoothOpt->addDoubleParameter(1, "kernel", "sigma for gaussian weighting function, in mm"); OptionalParameter* roiOption = ret->createOptionalParameter(4, "-roi", "select a region of interest to take the gradient of"); roiOption->addVolumeParameter(1, "roi-volume", "the region to take the gradient within"); OptionalParameter* vecOption = ret->createOptionalParameter(5, "-vectors", "output vectors"); vecOption->addVolumeOutputParameter(1, "vector-volume-out", "the vectors as a volume file"); OptionalParameter* subvolSelect = ret->createOptionalParameter(6, "-subvolume", "select a single subvolume to take the gradient of"); subvolSelect->addStringParameter(1, "subvol", "the subvolume number or name"); ret->setHelpText( AString("Computes the gradient of the volume by doing linear regressions for each voxel, considering only its face neighbors unless too few face neighbors exist. ") + "The gradient vector is constructed from the partial derivatives of the resulting linear function, and the magnitude of this vector is the output. " + "If specified, the volume vector output is arranged with the x, y, and z components from a subvolume as consecutive subvolumes." ); return ret; } void AlgorithmVolumeGradient::useParameters(OperationParameters* myParams, ProgressObject* myProgObj) { VolumeFile* volIn = myParams->getVolume(1); VolumeFile* volOut = myParams->getOutputVolume(2); float presmooth = -1.0f; OptionalParameter* presmoothOpt = myParams->getOptionalParameter(3); if (presmoothOpt->m_present) { presmooth = (float)presmoothOpt->getDouble(1); } VolumeFile* myRoi = NULL; OptionalParameter* roiOption = myParams->getOptionalParameter(4); if (roiOption->m_present) { myRoi = roiOption->getVolume(1); } VolumeFile* vectorsOut = NULL; OptionalParameter* vecOption = myParams->getOptionalParameter(5); if (vecOption->m_present) { vectorsOut = vecOption->getOutputVolume(1); } OptionalParameter* subvolSelect = myParams->getOptionalParameter(6); int subvolNum = -1; if (subvolSelect->m_present) { subvolNum = (int)volIn->getMapIndexFromNameOrNumber(subvolSelect->getString(1)); if (subvolNum < 0) { throw AlgorithmException("invalid subvolume specified"); } } AlgorithmVolumeGradient(myProgObj, volIn, volOut, presmooth, myRoi, vectorsOut, subvolNum); } AlgorithmVolumeGradient::AlgorithmVolumeGradient(ProgressObject* myProgObj, const VolumeFile* volIn, VolumeFile* volOut, const float& presmooth, const VolumeFile* myRoi, VolumeFile* vectorsOut, const int& subvolNum) : AbstractAlgorithm(myProgObj) { ProgressObject* smoothProgress = NULL; if (myProgObj != NULL && presmooth > 0.0f) { smoothProgress = myProgObj->addAlgorithm(AlgorithmVolumeSmoothing::getAlgorithmWeight()); } LevelProgress myProgress(myProgObj); if (myRoi != NULL && !volIn->matchesVolumeSpace(myRoi)) { throw AlgorithmException("roi volume space does not match input"); } if (subvolNum < -1 || subvolNum > volIn->getNumberOfMaps()) { throw AlgorithmException("invalid subvolume specified"); } VolumeFile smoothVol; const VolumeFile* processVol = volIn; int useSubvol = subvolNum; if (presmooth > 0.0f) { AlgorithmVolumeSmoothing(smoothProgress, volIn, presmooth, &smoothVol, myRoi, false, subvolNum); processVol = &smoothVol; if (subvolNum != -1) { useSubvol = 0; } } vector origDims = volIn->getOriginalDimensions(), myDims; volIn->getDimensions(myDims); int stencil[] = { 0, 0, 1, 0, 0, -1, 0, 1, 0, 0, -1, 0, 1, 0, 0, -1, 0, 0 }; vector > volSpace = volIn->getSform(); Vector3D ivec, jvec, kvec, origin; ivec[0] = volSpace[0][0]; jvec[0] = volSpace[0][1]; kvec[0] = volSpace[0][2]; origin[0] = volSpace[0][3]; ivec[1] = volSpace[1][0]; jvec[1] = volSpace[1][1]; kvec[1] = volSpace[1][2]; origin[1] = volSpace[1][3]; ivec[2] = volSpace[2][0]; jvec[2] = volSpace[2][1]; kvec[2] = volSpace[2][2]; origin[2] = volSpace[2][3];//TODO: special case orthogonal volumes (central difference)? const float* roiFrame = NULL; if (myRoi != NULL) { roiFrame = myRoi->getFrame(); } if (subvolNum == -1) { volOut->reinitialize(origDims, volIn->getSform(), myDims[4], volIn->getType()); if (vectorsOut != NULL) { while (origDims.size() < 4) { origDims.push_back(1); } origDims[3] *= 3; vectorsOut->reinitialize(origDims, volIn->getSform(), myDims[4], volIn->getType()); } for (int c = 0; c < myDims[4]; ++c) { for (int s = 0; s < myDims[3]; ++s) { const float* inFrame = processVol->getFrame(s, c); #pragma omp CARET_PARFOR schedule(dynamic) for (int k = 0; k < myDims[2]; ++k) { for (int j = 0; j < myDims[1]; ++j) { for (int i = 0; i < myDims[0]; ++i) { float magnitude; Vector3D gradient; if (myRoi == NULL || myRoi->getValue(i, j, k) > 0.0f) { float curval = processVol->getValue(i, j, k, s, c); FloatMatrix regress = FloatMatrix::zeros(4, 5); regress[3][3] = 1;//count the center voxel in case neighbors are missing (displacement and valdiff are zero, cancelling all other terms) int dircheck = 0; for (int neighbase = 0; neighbase < 18; neighbase += 3) { int ikern = i + stencil[neighbase]; int jkern = j + stencil[neighbase + 1]; int kkern = k + stencil[neighbase + 2]; if (volIn->indexValid(ikern, jkern, kkern)) { int64_t kernIndex = volIn->getIndex(ikern, jkern, kkern); if (roiFrame == NULL || roiFrame[kernIndex] > 0.0f) { dircheck |= 1<<(neighbase / 6);//uses the ordering of the neighbors to map neighbor to direction float valdiff = inFrame[kernIndex] - curval; Vector3D displacement = ivec * stencil[neighbase] + jvec * stencil[neighbase + 1] + kvec * stencil[neighbase + 2]; regress[0][0] += displacement[0] * displacement[0];//note: this is the generic code, built to handle strange volumes, to get more speed, test regress[0][1] += displacement[0] * displacement[1];// for orthogonal volume, and use central differences (if no ROI, could special case for even more speed) regress[0][2] += displacement[0] * displacement[2];//but, seems reasonably fast anyway regress[0][3] += displacement[0]; regress[0][4] += displacement[0] * valdiff; regress[1][1] += displacement[1] * displacement[1]; regress[1][2] += displacement[1] * displacement[2]; regress[1][3] += displacement[1]; regress[1][4] += displacement[1] * valdiff; regress[2][2] += displacement[2] * displacement[2]; regress[2][3] += displacement[2]; regress[2][4] += displacement[2] * valdiff; regress[3][3] += 1; regress[3][4] += valdiff; } } } if (dircheck == 7)//have at least one neighbor in every index axis, continue { regress[1][0] = regress[0][1];//finish the symmetric part of the matrix regress[2][0] = regress[0][2]; regress[2][1] = regress[1][2]; regress[3][0] = regress[0][3]; regress[3][1] = regress[1][3]; regress[3][2] = regress[2][3]; FloatMatrix result = regress.reducedRowEchelon(); gradient[0] = result[0][4]; gradient[1] = result[1][4]; gradient[2] = result[2][4];//[3][4] is the constant part of the regression magnitude = gradient.length(); if (!MathFunctions::isNumeric(magnitude)) { magnitude = 0.0f; gradient[0] = 0.0f; gradient[1] = 0.0f; gradient[2] = 0.0f; } } else {//fallback 1: regression with 26-neighbors Vector3D directions[3];//track the displacements in index space for simplicity int dirUsed = 0; if (dircheck & 1) { directions[dirUsed][0] = 1; ++dirUsed; } if (dircheck & 2) { directions[dirUsed][1] = 1; ++dirUsed; } if (dircheck & 4) { directions[dirUsed][2] = 1; ++dirUsed; } int imin = max(i - 1, 0), jmin = max(j - 1, 0), kmin = max(k - 1, 0); int imax = min(i + 2, (int)myDims[0]), jmax = min(j + 2, (int)myDims[1]), kmax = min(k + 2, (int)myDims[2]); Vector3D voxelDir; for (int kkern = kmin; kkern < kmax; ++kkern) { voxelDir[2] = kkern - k; int kabs = abs(kkern - k); int64_t kindpart = kkern * myDims[1]; for (int jkern = jmin; jkern < jmax; ++jkern) { int jabs = abs(jkern - j) + kabs; if (jabs > 0)//skip inner loop if it won't get any new neighbors { int64_t jindpart = (kindpart + jkern) * myDims[0]; voxelDir[1] = jkern - j; for (int ikern = imin; ikern < imax; ++ikern) { int64_t kernIndex = jindpart + ikern; if (jabs + abs(ikern - i) > 1 && (roiFrame == NULL || roiFrame[kernIndex] > 0.0f))//only add non-face neighbors { if (dirUsed < 3)//check for singularity via base vectors being dependent { bool newDir = true; switch (dirUsed) { case 0: default: break; case 1: if (voxelDir.cross(directions[0]).length() < 0.01f) newDir = false; break; case 2: if (voxelDir.cross(directions[0]).cross(voxelDir.cross(directions[1])).length() < 0.01f) newDir = false; break; } if (newDir) { directions[dirUsed] = voxelDir; ++dirUsed; } } voxelDir[0] = ikern - i; Vector3D displacement = ivec * voxelDir[0] + jvec * voxelDir[1] + kvec * voxelDir[2]; float valdiff = inFrame[kernIndex] - curval; regress[0][0] += displacement[0] * displacement[0]; regress[0][1] += displacement[0] * displacement[1]; regress[0][2] += displacement[0] * displacement[2]; regress[0][3] += displacement[0]; regress[0][4] += displacement[0] * valdiff; regress[1][1] += displacement[1] * displacement[1]; regress[1][2] += displacement[1] * displacement[2]; regress[1][3] += displacement[1]; regress[1][4] += displacement[1] * valdiff; regress[2][2] += displacement[2] * displacement[2]; regress[2][3] += displacement[2]; regress[2][4] += displacement[2] * valdiff; regress[3][3] += 1; regress[3][4] += valdiff; } } } } } if (dirUsed == 3) { regress[1][0] = regress[0][1];//finish the symmetric part of the matrix regress[2][0] = regress[0][2]; regress[2][1] = regress[1][2]; regress[3][0] = regress[0][3]; regress[3][1] = regress[1][3]; regress[3][2] = regress[2][3]; FloatMatrix result = regress.reducedRowEchelon(); gradient[0] = result[0][4]; gradient[1] = result[1][4]; gradient[2] = result[2][4];//[3][4] is the constant part of the regression magnitude = gradient.length(); if (!MathFunctions::isNumeric(magnitude)) { magnitude = 0.0f; gradient[0] = 0.0f; gradient[1] = 0.0f; gradient[2] = 0.0f; } } else {//fallback 2: average forward differences in 26-neighborhood Vector3D accum; int accumCount = 0; for (int kkern = kmin; kkern < kmax; ++kkern) { voxelDir[2] = kkern - k; int64_t kindpart = kkern * myDims[1]; for (int jkern = jmin; jkern < jmax; ++jkern) { int64_t jindpart = (kindpart + jkern) * myDims[0]; voxelDir[1] = jkern - j; for (int ikern = imin; ikern < imax; ++ikern) { int64_t kernIndex = jindpart + ikern; if (roiFrame == NULL || roiFrame[kernIndex] > 0.0f) { float valdiff = inFrame[kernIndex] - curval; voxelDir[0] = ikern - i; Vector3D displacement = ivec * voxelDir[0] + jvec * voxelDir[1] + kvec * voxelDir[2]; float length = displacement.length(); if (length > 0.0f) { accum += displacement * (valdiff / (length * length));//once to normalize vector, and once to find gradient magnitude ++accumCount; } } } } } if (accumCount > 0) { gradient = accum / accumCount; magnitude = gradient.length(); if (!MathFunctions::isNumeric(magnitude)) { magnitude = 0.0f; gradient[0] = 0.0f; gradient[1] = 0.0f; gradient[2] = 0.0f; } } else { magnitude = 0.0f; gradient[0] = 0.0f; gradient[1] = 0.0f; gradient[2] = 0.0f; } } } } else { magnitude = 0.0f; gradient[0] = 0.0f; gradient[1] = 0.0f; gradient[2] = 0.0f; } volOut->setValue(magnitude, i, j, k, s, c); if (vectorsOut != NULL) { int subvolbase = s * 3; vectorsOut->setValue(gradient[0], i, j, k, subvolbase, c); vectorsOut->setValue(gradient[1], i, j, k, subvolbase + 1, c); vectorsOut->setValue(gradient[2], i, j, k, subvolbase + 2, c); } } } } } } } else { origDims.resize(3); volOut->reinitialize(origDims, volIn->getSform(), myDims[4], volIn->getType()); if (vectorsOut != NULL) { origDims.push_back(3); vectorsOut->reinitialize(origDims, volIn->getSform(), myDims[4], volIn->getType()); } for (int c = 0; c < myDims[4]; ++c) { const float* inFrame = processVol->getFrame(useSubvol, c); #pragma omp CARET_PARFOR schedule(dynamic) for (int k = 0; k < myDims[2]; ++k) { for (int j = 0; j < myDims[1]; ++j) { for (int i = 0; i < myDims[0]; ++i) { float magnitude; Vector3D gradient; if (myRoi == NULL || myRoi->getValue(i, j, k) > 0.0f) { float curval = processVol->getValue(i, j, k, useSubvol, c); FloatMatrix regress = FloatMatrix::zeros(4, 5); regress[3][3] = 1;//count the center voxel in case neighbors are missing (displacement and valdiff are zero, cancelling all other terms) int dircheck = 0; for (int neighbase = 0; neighbase < 18; neighbase += 3) { int ikern = i + stencil[neighbase]; int jkern = j + stencil[neighbase + 1]; int kkern = k + stencil[neighbase + 2]; if (volIn->indexValid(ikern, jkern, kkern)) { int64_t kernIndex = volIn->getIndex(ikern, jkern, kkern); if (roiFrame == NULL || roiFrame[kernIndex] > 0.0f) { dircheck |= 1<<(neighbase / 6);//uses the ordering of the neighbors to map neighbor to direction float valdiff = inFrame[kernIndex] - curval; Vector3D displacement = ivec * stencil[neighbase] + jvec * stencil[neighbase + 1] + kvec * stencil[neighbase + 2]; regress[0][0] += displacement[0] * displacement[0];//note: this is the generic code, built to handle strange volumes, to get more speed, test regress[0][1] += displacement[0] * displacement[1];// for orthogonal volume, and use central differences (if no ROI, could special case for even more speed) regress[0][2] += displacement[0] * displacement[2]; regress[0][3] += displacement[0]; regress[0][4] += displacement[0] * valdiff; regress[1][1] += displacement[1] * displacement[1]; regress[1][2] += displacement[1] * displacement[2]; regress[1][3] += displacement[1]; regress[1][4] += displacement[1] * valdiff; regress[2][2] += displacement[2] * displacement[2]; regress[2][3] += displacement[2]; regress[2][4] += displacement[2] * valdiff; regress[3][3] += 1; regress[3][4] += valdiff; } } } if (dircheck == 7)//have at least one neighbor in every index axis, continue { regress[1][0] = regress[0][1];//finish the symmetric part of the matrix regress[2][0] = regress[0][2]; regress[2][1] = regress[1][2]; regress[3][0] = regress[0][3]; regress[3][1] = regress[1][3]; regress[3][2] = regress[2][3]; FloatMatrix result = regress.reducedRowEchelon(); gradient[0] = result[0][4]; gradient[1] = result[1][4]; gradient[2] = result[2][4];//[3][4] is the constant part of the regression magnitude = gradient.length(); if (!MathFunctions::isNumeric(magnitude)) { magnitude = 0.0f; gradient[0] = 0.0f; gradient[1] = 0.0f; gradient[2] = 0.0f; } } else {//fallback 1: regression with 26-neighbors Vector3D directions[3];//track the displacements in index space for simplicity int dirUsed = 0; if (dircheck & 1) { directions[dirUsed][0] = 1; ++dirUsed; } if (dircheck & 2) { directions[dirUsed][1] = 1; ++dirUsed; } if (dircheck & 4) { directions[dirUsed][2] = 1; ++dirUsed; } int imin = max(i - 1, 0), jmin = max(j - 1, 0), kmin = max(k - 1, 0); int imax = min(i + 2, (int)myDims[0]), jmax = min(j + 2, (int)myDims[1]), kmax = min(k + 2, (int)myDims[2]); Vector3D voxelDir; for (int kkern = kmin; kkern < kmax; ++kkern) { voxelDir[2] = kkern - k; int kabs = abs(kkern - k); int64_t kindpart = kkern * myDims[1]; for (int jkern = jmin; jkern < jmax; ++jkern) { int jabs = abs(jkern - j) + kabs; if (jabs > 0)//skip inner loop if it won't get any new neighbors { int64_t jindpart = (kindpart + jkern) * myDims[0]; voxelDir[1] = jkern - j; for (int ikern = imin; ikern < imax; ++ikern) { int64_t kernIndex = jindpart + ikern; if (jabs + abs(ikern - i) > 1 && (roiFrame == NULL || roiFrame[kernIndex] > 0.0f))//only add non-face neighbors { if (dirUsed < 3)//check for singularity via base vectors being dependent { bool newDir = true; switch (dirUsed) { case 0: default: break; case 1: if (voxelDir.cross(directions[0]).length() < 0.01f) newDir = false; break; case 2: if (voxelDir.cross(directions[0]).cross(voxelDir.cross(directions[1])).length() < 0.01f) newDir = false; break; } if (newDir) { directions[dirUsed] = voxelDir; ++dirUsed; } } voxelDir[0] = ikern - i; Vector3D displacement = ivec * voxelDir[0] + jvec * voxelDir[1] + kvec * voxelDir[2]; float valdiff = inFrame[kernIndex] - curval; regress[0][0] += displacement[0] * displacement[0]; regress[0][1] += displacement[0] * displacement[1]; regress[0][2] += displacement[0] * displacement[2]; regress[0][3] += displacement[0]; regress[0][4] += displacement[0] * valdiff; regress[1][1] += displacement[1] * displacement[1]; regress[1][2] += displacement[1] * displacement[2]; regress[1][3] += displacement[1]; regress[1][4] += displacement[1] * valdiff; regress[2][2] += displacement[2] * displacement[2]; regress[2][3] += displacement[2]; regress[2][4] += displacement[2] * valdiff; regress[3][3] += 1; regress[3][4] += valdiff; } } } } } if (dirUsed == 3) { regress[1][0] = regress[0][1];//finish the symmetric part of the matrix regress[2][0] = regress[0][2]; regress[2][1] = regress[1][2]; regress[3][0] = regress[0][3]; regress[3][1] = regress[1][3]; regress[3][2] = regress[2][3]; FloatMatrix result = regress.reducedRowEchelon(); gradient[0] = result[0][4]; gradient[1] = result[1][4]; gradient[2] = result[2][4];//[3][4] is the constant part of the regression magnitude = gradient.length(); if (!MathFunctions::isNumeric(magnitude)) { magnitude = 0.0f; gradient[0] = 0.0f; gradient[1] = 0.0f; gradient[2] = 0.0f; } } else {//fallback 2: average forward differences in 26-neighborhood Vector3D accum; int accumCount = 0; for (int kkern = kmin; kkern < kmax; ++kkern) { voxelDir[2] = kkern - k; int64_t kindpart = kkern * myDims[1]; for (int jkern = jmin; jkern < jmax; ++jkern) { int64_t jindpart = (kindpart + jkern) * myDims[0]; voxelDir[1] = jkern - j; for (int ikern = imin; ikern < imax; ++ikern) { int64_t kernIndex = jindpart + ikern; if (roiFrame == NULL || roiFrame[kernIndex] > 0.0f) { float valdiff = inFrame[kernIndex] - curval; voxelDir[0] = ikern - i; Vector3D displacement = ivec * voxelDir[0] + jvec * voxelDir[1] + kvec * voxelDir[2]; float length = displacement.length(); if (length > 0.0f) { accum += displacement * (valdiff / (length * length));//once to normalize vector, and once to find gradient magnitude ++accumCount; } } } } } if (accumCount > 0) { gradient = accum / accumCount; magnitude = gradient.length(); if (!MathFunctions::isNumeric(magnitude)) { magnitude = 0.0f; gradient[0] = 0.0f; gradient[1] = 0.0f; gradient[2] = 0.0f; } } else { magnitude = 0.0f; gradient[0] = 0.0f; gradient[1] = 0.0f; gradient[2] = 0.0f; } } } } else { magnitude = 0.0f; gradient[0] = 0.0f; gradient[1] = 0.0f; gradient[2] = 0.0f; } volOut->setValue(magnitude, i, j, k, 0, c); if (vectorsOut != NULL) { vectorsOut->setValue(gradient[0], i, j, k, 0, c); vectorsOut->setValue(gradient[1], i, j, k, 1, c); vectorsOut->setValue(gradient[2], i, j, k, 2, c); } } } } } } } float AlgorithmVolumeGradient::getAlgorithmInternalWeight() { return 1.0f;//override this if needed, if the progress bar isn't smooth } float AlgorithmVolumeGradient::getSubAlgorithmWeight() { //return AlgorithmInsertNameHere::getAlgorithmWeight();//if you use a subalgorithm return 0.0f; } connectome-workbench-1.2.3+git41-gc4c6c90/src/Algorithms/AlgorithmVolumeGradient.h000066400000000000000000000034571300200146000276330ustar00rootroot00000000000000#ifndef __ALGORITHM_VOLUME_GRADIENT_H__ #define __ALGORITHM_VOLUME_GRADIENT_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 "AbstractAlgorithm.h" namespace caret { class AlgorithmVolumeGradient : public AbstractAlgorithm { AlgorithmVolumeGradient(); protected: static float getSubAlgorithmWeight(); static float getAlgorithmInternalWeight(); public: AlgorithmVolumeGradient(ProgressObject* myProgObj, const VolumeFile* volIn, VolumeFile* volOut, const float& presmooth = -1.0f, const VolumeFile* myRoi = NULL, VolumeFile* vectorsOut = NULL, const int& subvolNum = -1); static OperationParameters* getParameters(); static void useParameters(OperationParameters* myParams, ProgressObject* myProgObj); static AString getCommandSwitch(); static AString getShortDescription(); }; typedef TemplateAutoOperation AutoAlgorithmVolumeGradient; } #endif //__ALGORITHM_VOLUME_GRADIENT_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Algorithms/AlgorithmVolumeLabelProbability.cxx000066400000000000000000000145051300200146000316650ustar00rootroot00000000000000/*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 "AlgorithmVolumeLabelProbability.h" #include "AlgorithmException.h" #include "GiftiLabelTable.h" #include "VolumeFile.h" #include #include #include using namespace caret; using namespace std; AString AlgorithmVolumeLabelProbability::getCommandSwitch() { return "-volume-label-probability"; } AString AlgorithmVolumeLabelProbability::getShortDescription() { return "FIND FREQUENCY OF LABELS"; } OperationParameters* AlgorithmVolumeLabelProbability::getParameters() { OperationParameters* ret = new OperationParameters(); ret->addVolumeParameter(1, "label-maps", "volume label file containing individual label maps from many subjects"); ret->addVolumeOutputParameter(2, "probability-out", "the relative frequencies of each label at each 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 voxel, divided by the number of input maps." ); return ret; } void AlgorithmVolumeLabelProbability::useParameters(OperationParameters* myParams, ProgressObject* myProgObj) { const VolumeFile* inputVol = myParams->getVolume(1); VolumeFile* outputVol = myParams->getOutputVolume(2); bool excludeUnlabeled = myParams->getOptionalParameter(3)->m_present; AlgorithmVolumeLabelProbability(myProgObj, inputVol, outputVol, excludeUnlabeled); } AlgorithmVolumeLabelProbability::AlgorithmVolumeLabelProbability(ProgressObject* myProgObj, const VolumeFile* inputVol, VolumeFile* outputVol, const bool& excludeUnlabeled) : AbstractAlgorithm(myProgObj) { LevelProgress myProgress(myProgObj); if (inputVol->getType() != SubvolumeAttributes::LABEL) throw AlgorithmException("input volume must be a label volume"); if (inputVol->getNumberOfComponents() != 1) throw AlgorithmException("label volumes must not have multiple components per map"); int numInMaps = inputVol->getNumberOfMaps(); vector > outMapToKeyLookup(numInMaps);//we match labels by name, not by key - the lookup is this direction so we can compute one output frame at a time map nameToOutMap;//yes, that involves a lot of rescanning memory, but it takes half the additional memory as one pass (because VolumeFile has internal memory) for (int i = 0; i < numInMaps; ++i)//testing shows it doesn't take that much longer anyway, for smallish frame sizes at least { const GiftiLabelTable* thisTable = inputVol->getMapLabelTable(i); set thisKeys = thisTable->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 = thisTable->getUnassignedLabelKey(); } for (set::iterator iter = thisKeys.begin(); iter != thisKeys.end(); ++iter)//order by key value { if (excludeUnlabeled && *iter == unlabeledKey) continue;//skip to next key const AString thisName = thisTable->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; } outMapToKeyLookup[i][outMap] = *iter; } } const vector inDims = inputVol->getDimensions(); vector outDims = inDims; outDims.resize(4); int64_t numOutMaps = (int64_t)nameToOutMap.size(); outDims[3] = numOutMaps; outputVol->reinitialize(outDims, inputVol->getSform()); for (map::iterator iter = nameToOutMap.begin(); iter != nameToOutMap.end(); ++iter) { outputVol->setMapName(iter->second, iter->first); } int64_t frameSize = inDims[0] * inDims[1] * inDims[2]; for (int outMap = 0; outMap < numOutMaps; ++outMap) { vector scratchCount(frameSize, 0); for (int inMap = 0; inMap < numInMaps; ++inMap) { map::iterator search = outMapToKeyLookup[inMap].find(outMap); if (search == outMapToKeyLookup[inMap].end()) { continue;//this map doesn't contain a label name that matches, skip to next input map } int matchKey = search->second; const float* inFrame = inputVol->getFrame(inMap); for (int64_t i = 0; i < frameSize; ++i) { int thisKey = (int)floor(inFrame[i] + 0.5f); if (thisKey == matchKey) { ++scratchCount[i]; } } } vector scratchFrameOut(frameSize); for (int64_t i = 0; i < frameSize; ++i) { scratchFrameOut[i] = ((float)scratchCount[i]) / numInMaps; } outputVol->setFrame(scratchFrameOut.data(), outMap); } } float AlgorithmVolumeLabelProbability::getAlgorithmInternalWeight() { return 1.0f;//override this if needed, if the progress bar isn't smooth } float AlgorithmVolumeLabelProbability::getSubAlgorithmWeight() { //return AlgorithmInsertNameHere::getAlgorithmWeight();//if you use a subalgorithm return 0.0f; } connectome-workbench-1.2.3+git41-gc4c6c90/src/Algorithms/AlgorithmVolumeLabelProbability.h000066400000000000000000000034001300200146000313020ustar00rootroot00000000000000#ifndef __ALGORITHM_VOLUME_LABEL_PROBABILITY_H__ #define __ALGORITHM_VOLUME_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 AlgorithmVolumeLabelProbability : public AbstractAlgorithm { AlgorithmVolumeLabelProbability(); protected: static float getSubAlgorithmWeight(); static float getAlgorithmInternalWeight(); public: AlgorithmVolumeLabelProbability(ProgressObject* myProgObj, const VolumeFile* inputVol, VolumeFile* outputVol, const bool& excludeUnlabeled = false); static OperationParameters* getParameters(); static void useParameters(OperationParameters* myParams, ProgressObject* myProgObj); static AString getCommandSwitch(); static AString getShortDescription(); }; typedef TemplateAutoOperation AutoAlgorithmVolumeLabelProbability; } #endif //__ALGORITHM_VOLUME_LABEL_PROBABILITY_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Algorithms/AlgorithmVolumeLabelToROI.cxx000066400000000000000000000246601300200146000303440ustar00rootroot00000000000000/*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 "AlgorithmVolumeLabelToROI.h" #include "AlgorithmException.h" #include "CaretLogger.h" #include "GiftiLabel.h" #include "GiftiLabelTable.h" #include "VolumeFile.h" #include #include using namespace caret; using namespace std; AString AlgorithmVolumeLabelToROI::getCommandSwitch() { return "-volume-label-to-roi"; } AString AlgorithmVolumeLabelToROI::getShortDescription() { return "MAKE A VOLUME LABEL INTO AN ROI VOLUME"; } OperationParameters* AlgorithmVolumeLabelToROI::getParameters() { OperationParameters* ret = new OperationParameters(); ret->addVolumeParameter(1, "label-in", "the input volume label file"); ret->addVolumeOutputParameter(2, "volume-out", "the output volume file"); OptionalParameter* nameOpt = ret->createOptionalParameter(3, "-name", "select label by name"); nameOpt->addStringParameter(1, "label-name", "the label name that you want an roi of"); OptionalParameter* keyOpt = ret->createOptionalParameter(4, "-key", "select label by key"); keyOpt->addIntegerParameter(1, "label-key", "the label key that you want an roi of"); OptionalParameter* mapOpt = ret->createOptionalParameter(5, "-map", "select a single label map to use"); mapOpt->addStringParameter(1, "map", "the map number or name"); ret->setHelpText( AString("For each map in , a map is created in where all locations labeled with or with a key of are given a value of 1, and all other locations are given 0. ") + "Exactly one of -name and -key must be specified. " + "Specify -map to use only one map from ." ); return ret; } void AlgorithmVolumeLabelToROI::useParameters(OperationParameters* myParams, ProgressObject* myProgObj) { VolumeFile* myLabel = myParams->getVolume(1); VolumeFile* myVolumeOut = myParams->getOutputVolume(2); bool nameMode = false; AString labelName; OptionalParameter* nameOpt = myParams->getOptionalParameter(3); if (nameOpt->m_present) { nameMode = true; labelName = nameOpt->getString(1); } int32_t labelKey; OptionalParameter* keyOpt = myParams->getOptionalParameter(4); if (keyOpt->m_present) { if (nameMode) throw AlgorithmException("-name and -key cannot be specified together"); labelKey = (int32_t)keyOpt->getInteger(1); } else { if (!nameMode) throw AlgorithmException("you must specify one of -name or -key"); } int whichMap = -1; OptionalParameter* mapOpt = myParams->getOptionalParameter(5); if (mapOpt->m_present) { AString mapID = mapOpt->getString(1); whichMap = myLabel->getMapIndexFromNameOrNumber(mapID); if (whichMap == -1) { throw AlgorithmException("invalid map number or name specified"); } } if (nameMode) { AlgorithmVolumeLabelToROI(myProgObj, myLabel, labelName, myVolumeOut, whichMap); } else { AlgorithmVolumeLabelToROI(myProgObj, myLabel, labelKey, myVolumeOut, whichMap); } } AlgorithmVolumeLabelToROI::AlgorithmVolumeLabelToROI(ProgressObject* myProgObj, const VolumeFile* myLabel, const AString& labelName, VolumeFile* myVolumeOut, const int& whichMap) : AbstractAlgorithm(myProgObj) { LevelProgress myProgress(myProgObj); int numMaps = myLabel->getNumberOfMaps(); if (whichMap < -1 || whichMap >= numMaps) { throw AlgorithmException("invalid map index specified"); } if (myLabel->getType() != SubvolumeAttributes::LABEL) { throw AlgorithmException("input volume is not a label volume"); } vector dims, origDims = myLabel->getOriginalDimensions(); myLabel->getDimensions(dims); if (dims[4] != 1) { throw AlgorithmException("input label volume has multi-component type"); } int64_t frameSize = dims[0] * dims[1] * dims[2]; vector scratchFrame(frameSize); if (whichMap == -1) { myVolumeOut->reinitialize(origDims, myLabel->getSform()); bool shouldThrow = true; for (int thisMap = 0; thisMap < numMaps; ++thisMap) { const GiftiLabelTable* myTable = myLabel->getMapLabelTable(thisMap); int matchKey = myTable->getLabelKeyFromName(labelName); if (matchKey == GiftiLabel::getInvalidLabelKey()) { CaretLogWarning("label name '" + labelName + "' not found in map #" + AString::number(thisMap + 1)); for (int64_t i = 0; i < frameSize; ++i) { scratchFrame[i] = 0.0f; } } else { const float* labelFrame = myLabel->getFrame(thisMap); for (int64_t i = 0; i < frameSize; ++i) { int thisKey = (int)floor(labelFrame[i] + 0.5f); if (thisKey == matchKey) { scratchFrame[i] = 1.0f; shouldThrow = false; } else { scratchFrame[i] = 0.0f; } } } myVolumeOut->setFrame(scratchFrame.data(), thisMap); } if (shouldThrow) { throw AlgorithmException("no data matched the specified label name"); } } else { origDims.resize(3); myVolumeOut->reinitialize(origDims, myLabel->getSform()); const GiftiLabelTable* myTable = myLabel->getMapLabelTable(whichMap); int matchKey = myTable->getLabelKeyFromName(labelName); if (matchKey == GiftiLabel::getInvalidLabelKey()) { throw AlgorithmException("label name '" + labelName + "' not found in specified map"); } const float* labelFrame = myLabel->getFrame(whichMap); bool shouldThrow = true; for (int64_t i = 0; i < frameSize; ++i) { int thisKey = (int)floor(labelFrame[i] + 0.5f); if (thisKey == matchKey) { scratchFrame[i] = 1.0f; shouldThrow = false; } else { scratchFrame[i] = 0.0f; } } if (shouldThrow) { throw AlgorithmException("no data matched the specified label name in the specified map"); } myVolumeOut->setFrame(scratchFrame.data(), 0); } } AlgorithmVolumeLabelToROI::AlgorithmVolumeLabelToROI(ProgressObject* myProgObj, const VolumeFile* myLabel, const int32_t& labelKey, VolumeFile* myVolumeOut, const int& whichMap) : AbstractAlgorithm(myProgObj) { LevelProgress myProgress(myProgObj); int numMaps = myLabel->getNumberOfMaps(); if (whichMap < -1 || whichMap >= numMaps) { throw AlgorithmException("invalid map index specified"); } if (myLabel->getType() != SubvolumeAttributes::LABEL) { throw AlgorithmException("input volume is not a label volume"); } vector dims, origDims = myLabel->getOriginalDimensions(); myLabel->getDimensions(dims); if (dims[4] != 1) { throw AlgorithmException("input label volume has multi-component type"); } int64_t frameSize = dims[0] * dims[1] * dims[2]; vector scratchFrame(frameSize); if (whichMap == -1) { myVolumeOut->reinitialize(origDims, myLabel->getSform()); bool shouldThrow = true; for (int thisMap = 0; thisMap < numMaps; ++thisMap) { const GiftiLabelTable* myTable = myLabel->getMapLabelTable(thisMap); if (myTable->getLabel(labelKey) == NULL) { CaretLogWarning("label key " + AString::number(labelKey) + " not found in map #" + AString::number(thisMap + 1)); } const float* labelFrame = myLabel->getFrame(thisMap);//try anyway, in case label table is incomplete for (int64_t i = 0; i < frameSize; ++i) { int thisKey = (int)floor(labelFrame[i] + 0.5f); if (thisKey == labelKey) { scratchFrame[i] = 1.0f; shouldThrow = false; } else { scratchFrame[i] = 0.0f; } } myVolumeOut->setFrame(scratchFrame.data(), thisMap); } if (shouldThrow) { throw AlgorithmException("no data matched the specified label key"); } } else { origDims.resize(3); myVolumeOut->reinitialize(origDims, myLabel->getSform()); const GiftiLabelTable* myTable = myLabel->getMapLabelTable(whichMap); if (myTable->getLabel(labelKey) == NULL) { CaretLogWarning("label key " + AString::number(labelKey) + " not found in specified map"); } const float* labelFrame = myLabel->getFrame(whichMap); bool shouldThrow = true; for (int64_t i = 0; i < frameSize; ++i) { int thisKey = (int)floor(labelFrame[i] + 0.5f); if (thisKey == labelKey) { scratchFrame[i] = 1.0f; shouldThrow = false; } else { scratchFrame[i] = 0.0f; } } if (shouldThrow) { throw AlgorithmException("no data matched the specified label key in the specified map"); } myVolumeOut->setFrame(scratchFrame.data(), 0); } } float AlgorithmVolumeLabelToROI::getAlgorithmInternalWeight() { return 1.0f;//override this if needed, if the progress bar isn't smooth } float AlgorithmVolumeLabelToROI::getSubAlgorithmWeight() { //return AlgorithmInsertNameHere::getAlgorithmWeight();//if you use a subalgorithm return 0.0f; } connectome-workbench-1.2.3+git41-gc4c6c90/src/Algorithms/AlgorithmVolumeLabelToROI.h000066400000000000000000000036071300200146000277670ustar00rootroot00000000000000#ifndef __ALGORITHM_VOLUME_LABEL_TO_ROI_H__ #define __ALGORITHM_VOLUME_LABEL_TO_ROI_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 "AbstractAlgorithm.h" namespace caret { class AlgorithmVolumeLabelToROI : public AbstractAlgorithm { AlgorithmVolumeLabelToROI(); protected: static float getSubAlgorithmWeight(); static float getAlgorithmInternalWeight(); public: AlgorithmVolumeLabelToROI(ProgressObject* myProgObj, const VolumeFile* myLabel, const AString& labelName, VolumeFile* myVolumeOut, const int& whichMap = -1); AlgorithmVolumeLabelToROI(ProgressObject* myProgObj, const VolumeFile* myLabel, const int32_t& labelKey, VolumeFile* myVolumeOut, const int& whichMap = -1); static OperationParameters* getParameters(); static void useParameters(OperationParameters* myParams, ProgressObject* myProgObj); static AString getCommandSwitch(); static AString getShortDescription(); }; typedef TemplateAutoOperation AutoAlgorithmVolumeLabelToROI; } #endif //__ALGORITHM_VOLUME_LABEL_TO_ROI_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Algorithms/AlgorithmVolumeLabelToSurfaceMapping.cxx000066400000000000000000000342701300200146000326150ustar00rootroot00000000000000/*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 "AlgorithmVolumeLabelToSurfaceMapping.h" #include "AlgorithmException.h" #include "GiftiLabelTable.h" #include "LabelFile.h" #include "RibbonMappingHelper.h" #include "SurfaceFile.h" #include "VolumeFile.h" #include #include using namespace caret; using namespace std; AString AlgorithmVolumeLabelToSurfaceMapping::getCommandSwitch() { return "-volume-label-to-surface-mapping"; } AString AlgorithmVolumeLabelToSurfaceMapping::getShortDescription() { return "MAP A LABEL VOLUME TO A SURFACE LABEL FILE"; } OperationParameters* AlgorithmVolumeLabelToSurfaceMapping::getParameters() { OperationParameters* ret = new OperationParameters(); ret->addVolumeParameter(1, "volume", "the volume to map data from"); ret->addSurfaceParameter(2, "surface", "the surface to map the data onto"); ret->addLabelOutputParameter(3, "label-out", "the output gifti label file"); OptionalParameter* ribbonOpt = ret->createOptionalParameter(4, "-ribbon-constrained", "use ribbon constrained mapping algorithm"); ribbonOpt->addSurfaceParameter(1, "inner-surf", "the inner surface of the ribbon"); ribbonOpt->addSurfaceParameter(2, "outer-surf", "the outer surface of the ribbon"); OptionalParameter* roiVol = ribbonOpt->createOptionalParameter(3, "-volume-roi", "use a volume roi"); 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"); 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.") ); return ret; } void AlgorithmVolumeLabelToSurfaceMapping::useParameters(OperationParameters* myParams, ProgressObject* myProgObj) { VolumeFile* myVolume = myParams->getVolume(1); SurfaceFile* mySurface = myParams->getSurface(2); LabelFile* myLabelOut = myParams->getOutputLabel(3); int64_t mySubVol = -1; OptionalParameter* subvolumeSelect = myParams->getOptionalParameter(5); if (subvolumeSelect->m_present) { mySubVol = myVolume->getMapIndexFromNameOrNumber(subvolumeSelect->getString(1)); if (mySubVol < 0) { throw AlgorithmException("invalid column specified"); } } OptionalParameter* ribbonOpt = myParams->getOptionalParameter(4);//method options last for now, for simplicity if (ribbonOpt->m_present) { SurfaceFile* innerSurf = ribbonOpt->getSurface(1); SurfaceFile* outerSurf = ribbonOpt->getSurface(2); OptionalParameter* roiVol = ribbonOpt->getOptionalParameter(3); VolumeFile* myRoiVol = NULL; if (roiVol->m_present) { myRoiVol = roiVol->getVolume(1); } int32_t subdivisions = 3; OptionalParameter* ribbonSubdiv = ribbonOpt->getOptionalParameter(4); if (ribbonSubdiv->m_present) { subdivisions = ribbonSubdiv->getInteger(1); if (subdivisions < 1) { throw AlgorithmException("invalid number of subdivisions specified"); } } AlgorithmVolumeLabelToSurfaceMapping(myProgObj, myVolume, mySurface, myLabelOut, innerSurf, outerSurf, myRoiVol, subdivisions, mySubVol); } else { AlgorithmVolumeLabelToSurfaceMapping(myProgObj, myVolume, mySurface, myLabelOut, mySubVol); } } AlgorithmVolumeLabelToSurfaceMapping::AlgorithmVolumeLabelToSurfaceMapping(ProgressObject* myProgObj, const VolumeFile* myVolume, const SurfaceFile* mySurface, LabelFile* myLabelOut, const int64_t& mySubVol) : AbstractAlgorithm(myProgObj) { LevelProgress myProgress(myProgObj); if (myVolume->getType() != SubvolumeAttributes::LABEL) { throw AlgorithmException("input volume must be a label volume"); } vector myVolDims; myVolume->getDimensions(myVolDims); if (mySubVol >= myVolDims[3] || mySubVol < -1) { throw AlgorithmException("invalid subvolume specified"); } int64_t numColumns; if (mySubVol == -1) { numColumns = myVolDims[3] * myVolDims[4]; } else { numColumns = myVolDims[4]; } int64_t numNodes = mySurface->getNumberOfNodes(); myLabelOut->setNumberOfNodesAndColumns(numNodes, numColumns); myLabelOut->setStructure(mySurface->getStructure()); vector myArray(numNodes); 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 for (int64_t i = 0; i < myVolDims[3]; ++i) { map frameRemap; if (i > 0) { const GiftiLabelTable* tempTable = myVolume->getMapLabelTable(i); if (tempTable == NULL) { throw AlgorithmException("a subvolume is missing a label table"); } frameRemap = cumulativeTable.append(*tempTable); } for (int64_t j = 0; j < myVolDims[4]; ++j) { AString mapName = myVolume->getMapName(i); if (myVolDims[4] != 1) { mapName += " component " + AString::number(j); } 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->getLabelTable()) = cumulativeTable; } else { const GiftiLabelTable* tempTable = myVolume->getMapLabelTable(mySubVol); 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) { mapName += " component " + AString::number(j); } 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] = (int32_t)floor(myVolume->interpolateValue(mySurface->getCoordinate(node), VolumeFile::ENCLOSING_VOXEL, NULL, mySubVol, j) + 0.5f); } myLabelOut->setLabelKeysForColumn(thisCol, 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 int64_t& mySubVol): AbstractAlgorithm(myProgObj) { LevelProgress myProgress(myProgObj); if (myVolume->getType() != SubvolumeAttributes::LABEL) { throw AlgorithmException("input volume must be a label volume"); } vector myVolDims; myVolume->getDimensions(myVolDims); if (mySubVol >= myVolDims[3] || mySubVol < -1) { throw AlgorithmException("invalid subvolume specified"); } int64_t numColumns; if (mySubVol == -1) { numColumns = myVolDims[3] * myVolDims[4]; } else { numColumns = myVolDims[4]; } int64_t numNodes = mySurface->getNumberOfNodes(); myLabelOut->setNumberOfNodesAndColumns(numNodes, numColumns); myLabelOut->setStructure(mySurface->getStructure()); vector myArray(numNodes); vector > myWeights; const float* roiFrame = NULL; if (myRoiVol != NULL) roiFrame = myRoiVol->getFrame(); RibbonMappingHelper::computeWeightsRibbon(myWeights, myVolume->getVolumeSpace(), innerSurf, outerSurf, roiFrame, subdivisions); 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; if (i > 0) { const GiftiLabelTable* tempTable = myVolume->getMapLabelTable(i); if (tempTable == NULL) { throw AlgorithmException("a subvolume is missing a label table"); } frameRemap = cumulativeTable.append(*tempTable); } for (int64_t j = 0; j < myVolDims[4]; ++j) { AString mapName = myVolume->getMapName(i); if (myVolDims[4] != 1) { mapName += " component " + AString::number(j); } int64_t thisCol = i * myVolDims[4] + j; myLabelOut->setColumnName(thisCol, mapName); #pragma omp CARET_PARFOR for (int64_t node = 0; node < numNodes; ++node) { 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()) { myArray[node] = iter->second; } else { myArray[node] = tempKey;//for simplicity, assume all values in the frame are in the label table } } myLabelOut->setLabelKeysForColumn(thisCol, myArray.data()); } } *(myLabelOut->getLabelTable()) = cumulativeTable; } else { const GiftiLabelTable* tempTable = myVolume->getMapLabelTable(mySubVol); 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) { mapName += " component " + AString::number(j); } 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 } myLabelOut->setLabelKeysForColumn(thisCol, myArray.data()); } } } float AlgorithmVolumeLabelToSurfaceMapping::getAlgorithmInternalWeight() { return 1.0f;//override this if needed, if the progress bar isn't smooth } float AlgorithmVolumeLabelToSurfaceMapping::getSubAlgorithmWeight() { //return AlgorithmInsertNameHere::getAlgorithmWeight();//if you use a subalgorithm return 0.0f; } connectome-workbench-1.2.3+git41-gc4c6c90/src/Algorithms/AlgorithmVolumeLabelToSurfaceMapping.h000066400000000000000000000044031300200146000322350ustar00rootroot00000000000000#ifndef __ALGORITHM_VOLUME_LABEL_TO_SURFACE_MAPPING_H__ #define __ALGORITHM_VOLUME_LABEL_TO_SURFACE_MAPPING_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 "AbstractAlgorithm.h" namespace caret { class AlgorithmVolumeLabelToSurfaceMapping : public AbstractAlgorithm { AlgorithmVolumeLabelToSurfaceMapping(); protected: static float getSubAlgorithmWeight(); static float getAlgorithmInternalWeight(); public: 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 int64_t& mySubVol = -1); static OperationParameters* getParameters(); static void useParameters(OperationParameters* myParams, ProgressObject* myProgObj); static AString getCommandSwitch(); static AString getShortDescription(); }; typedef TemplateAutoOperation AutoAlgorithmVolumeLabelToSurfaceMapping; } #endif //__ALGORITHM_VOLUME_LABEL_TO_SURFACE_MAPPING_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Algorithms/AlgorithmVolumeParcelResampling.cxx000066400000000000000000001276761300200146000317130ustar00rootroot00000000000000/*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 "AlgorithmVolumeParcelResampling.h" #include "AlgorithmException.h" #include "VolumeFile.h" #include "AlgorithmVolumeSmoothing.h" #include "CaretAssert.h" #include "CaretLogger.h" #include "Vector3D.h" #include #include #include #include using namespace caret; using namespace std; const int FIX_ZEROS_POST_ITERATIONS = 10;//number of times to do the "find remaining zeros and try to fill" code before giving up when -fix-zeros is specified AString AlgorithmVolumeParcelResampling::getCommandSwitch() { return "-volume-parcel-resampling"; } AString AlgorithmVolumeParcelResampling::getShortDescription() { return "SMOOTH AND RESAMPLE VOLUME PARCELS"; } OperationParameters* AlgorithmVolumeParcelResampling::getParameters() { OperationParameters* ret = new OperationParameters(); ret->addVolumeParameter(1, "volume-in", "the input data volume"); ret->addVolumeParameter(2, "cur-parcels", "label volume of where the parcels currently are"); ret->addVolumeParameter(3, "new-parcels", "label volume of where the parcels should be"); ret->addDoubleParameter(4, "kernel", "gaussian kernel sigma to smooth by during resampling"); ret->addVolumeOutputParameter(5, "volume-out", "output volume"); ret->createOptionalParameter(6, "-fix-zeros", "treat zero values as not being data"); OptionalParameter* subvolSelect = ret->createOptionalParameter(7, "-subvolume", "select a single subvolume as input"); subvolSelect->addStringParameter(1, "subvol", "the subvolume number or name"); ret->setHelpText( AString("Smooths and resamples the region inside each label in cur-parcels to the region of the same label name in new-parcels. ") + "Any voxels in the output label region but outside the input label region will be extrapolated from nearby data. " + "The -fix-zeros option causes the smoothing to not use an input value if it is zero, but still write a smoothed value to the voxel, and after smoothing " + "is complete, it will check for any remaining values of zero, and fill them in with extrapolated values.\n\nNote: all volumes must have " + "the same dimensions and spacing. To use a different output space, see -volume-parcel-resampling-generic." ); return ret; } void AlgorithmVolumeParcelResampling::useParameters(OperationParameters* myParams, ProgressObject* myProgObj) { VolumeFile* inVol = myParams->getVolume(1); VolumeFile* curLabel = myParams->getVolume(2); VolumeFile* newLabel = myParams->getVolume(3); float kernel = (float)myParams->getDouble(4); VolumeFile* outVol = myParams->getOutputVolume(5); bool fixZeros = false; if (myParams->getOptionalParameter(6)->m_present) { fixZeros = true; } OptionalParameter* subvolSelect = myParams->getOptionalParameter(7); int subvolNum = -1; if (subvolSelect->m_present) { subvolNum = (int)inVol->getMapIndexFromNameOrNumber(subvolSelect->getString(1)); if (subvolNum < 0) { throw AlgorithmException("invalid subvolume specified"); } } AlgorithmVolumeParcelResampling(myProgObj, inVol, curLabel, newLabel, kernel, outVol, fixZeros, subvolNum); } AlgorithmVolumeParcelResampling::AlgorithmVolumeParcelResampling(ProgressObject* myProgObj, const VolumeFile* inVol, const VolumeFile* curLabel, const VolumeFile* newLabel, const float& kernel, VolumeFile* outVol, const bool& fixZeros, const int& subvolNum) : AbstractAlgorithm(myProgObj) { CaretAssert(inVol != NULL); CaretAssert(curLabel != NULL); CaretAssert(newLabel != NULL); CaretAssert(outVol != NULL); if (!inVol->matchesVolumeSpace(curLabel) || !inVol->matchesVolumeSpace(newLabel)) { throw AlgorithmException("volume spacing or dimension mismatch"); } if (curLabel->getType() != SubvolumeAttributes::LABEL || newLabel->getType() != SubvolumeAttributes::LABEL) { throw AlgorithmException("parcel volumes are not of type label"); } if (subvolNum < -1 || subvolNum >= inVol->getNumberOfMaps()) { throw AlgorithmException("invalid subvolume specified"); } vector > matchedLabels; matchLabels(curLabel, newLabel, matchedLabels); if (matchedLabels.size() == 0) { throw AlgorithmException("no matching labels"); } vector > voxelLists; generateVoxelLists(matchedLabels, curLabel, newLabel, voxelLists); /*ProgressObject* subAlgProgress = NULL; if (myProgObj != NULL)//TODO: create a vector of progress objects based on number of matched labels, and possibly counts of voxels { subAlgProgress = myProgObj->addAlgorithm(AlgorithmVolumeParcelSmoothing::getAlgorithmWeight()); }//*/ LevelProgress myProgress(myProgObj); if (fixZeros) { resampleFixZeros(myProgress, matchedLabels, voxelLists, inVol, curLabel, newLabel, kernel, outVol, subvolNum); } else { resample(myProgress, matchedLabels, voxelLists, inVol, curLabel, newLabel, kernel, outVol, subvolNum); } } void AlgorithmVolumeParcelResampling::matchLabels(const VolumeFile* curLabel, const VolumeFile* newLabel, vector >& matchedLabels) { const GiftiLabelTable* curTable = curLabel->getMapLabelTable(0), *newTable = newLabel->getMapLabelTable(0); vector curKeys; curTable->getKeys(curKeys); int32_t curUnused = curTable->getUnassignedLabelKey(); for (int i = 0; i < (int)curKeys.size(); ++i) { if (curKeys[i] == curUnused) continue;//always skip the unassigned label if (newTable->getLabel(curKeys[i]) != NULL && newTable->getLabelName(curKeys[i]) == curTable->getLabelName(curKeys[i])) {//do the obvious check first matchedLabels.push_back(make_pair(curKeys[i], curKeys[i])); } else { int32_t newKey = newTable->getLabelKeyFromName(curTable->getLabelName(curKeys[i])); if (newKey != -1) { matchedLabels.push_back(make_pair(curKeys[i], newKey)); } } } } void AlgorithmVolumeParcelResampling::generateVoxelLists(const vector >& matchedLabels, const VolumeFile* curLabel, const VolumeFile* newLabel, vector >& voxelLists) { map curLabelReverse, newLabelReverse; for (int i = 0; i < (int)matchedLabels.size(); ++i) { curLabelReverse[matchedLabels[i].first] = i; newLabelReverse[matchedLabels[i].second] = i; } voxelLists.resize(matchedLabels.size()); vector myDims; curLabel->getDimensions(myDims); for (int64_t k = 0; k < myDims[2]; ++k) { for (int64_t j = 0; j < myDims[1]; ++j) { for (int64_t i = 0; i < myDims[0]; ++i) { int curValue = (int)floor(curLabel->getValue(i, j, k) + 0.5f); int newValue = (int)floor(newLabel->getValue(i, j, k) + 0.5f); map::iterator curiter = curLabelReverse.find(curValue), newiter = newLabelReverse.find(newValue); if (curiter != curLabelReverse.end()) { voxelLists[curiter->second].push_back(i); voxelLists[curiter->second].push_back(j); voxelLists[curiter->second].push_back(k); } if (newiter != newLabelReverse.end() && (curiter == curLabelReverse.end() || newiter->second != curiter->second)) { voxelLists[newiter->second].push_back(i); voxelLists[newiter->second].push_back(j); voxelLists[newiter->second].push_back(k); } } } } } void AlgorithmVolumeParcelResampling::resample(LevelProgress& myProgress, const vector >& matchedLabels, const vector >& voxelLists, const VolumeFile* inVol, const VolumeFile* curLabel, const VolumeFile* newLabel, const float& kernel, VolumeFile* outVol, const int& subvolNum) { float kernBox = kernel * 3.0f; vector > volSpace = inVol->getSform();//copied from volume smoothing, perhaps this should be in a convenience method in VolumeFile Vector3D ivec, jvec, kvec, origin, ijorth, jkorth, kiorth; ivec[0] = volSpace[0][0]; jvec[0] = volSpace[0][1]; kvec[0] = volSpace[0][2]; origin[0] = volSpace[0][3];//needs to be this verbose because the axis and origin vectors are column vectors ivec[1] = volSpace[1][0]; jvec[1] = volSpace[1][1]; kvec[1] = volSpace[1][2]; origin[1] = volSpace[1][3];//while vector > is a column of row vectors ivec[2] = volSpace[2][0]; jvec[2] = volSpace[2][1]; kvec[2] = volSpace[2][2]; origin[2] = volSpace[2][3]; ijorth = ivec.cross(jvec).normal();//find the bounding box that encloses a sphere of radius kernBox jkorth = jvec.cross(kvec).normal(); kiorth = kvec.cross(ivec).normal(); int irange = (int)floor(abs(kernBox / ivec.dot(jkorth))); int jrange = (int)floor(abs(kernBox / jvec.dot(kiorth))); int krange = (int)floor(abs(kernBox / kvec.dot(ijorth))); if (irange < 1) irange = 1;//don't underflow if (jrange < 1) jrange = 1; if (krange < 1) krange = 1; map curLabelReverse, newLabelReverse; int numLabels = (int)matchedLabels.size(); for (int i = 0; i < numLabels; ++i) { curLabelReverse[matchedLabels[i].first] = i; newLabelReverse[matchedLabels[i].second] = i; } vector myDims; inVol->getDimensions(myDims); if (subvolNum == -1) { outVol->reinitialize(inVol->getOriginalDimensions(), inVol->getSform(), myDims[4], inVol->getType()); } else { vector newDims = inVol->getOriginalDimensions(); newDims.resize(3);//discard nonspatial dimentions outVol->reinitialize(newDims, inVol->getSform(), myDims[4], inVol->getType()); } outVol->setValueAllVoxels(0.0f); const GiftiLabelTable* curTable = curLabel->getMapLabelTable(0); for (int whichList = 0; whichList < numLabels; ++whichList) { int curLabelValue = matchedLabels[whichList].first; int newLabelValue = matchedLabels[whichList].second; myProgress.reportProgress(((float)whichList) / numLabels); myProgress.setTask("Processing parcel " + curTable->getLabelName(curLabelValue)); const vector& thisList = voxelLists[whichList]; int64_t listSize = (int64_t)thisList.size(); if (listSize > 2)//NOTE: this should NEVER be something other than a multiple of 3, but check against what we will actually access anyway { int extrema[6] = { thisList[0], thisList[0], thisList[1], thisList[1], thisList[2], thisList[2] }; for (int64_t base = 3; base < listSize; base += 3) { if (thisList[base] < extrema[0]) extrema[0] = thisList[base]; if (thisList[base] > extrema[1]) extrema[1] = thisList[base]; if (thisList[base + 1] < extrema[2]) extrema[2] = thisList[base + 1]; if (thisList[base + 1] > extrema[3]) extrema[3] = thisList[base + 1]; if (thisList[base + 2] < extrema[4]) extrema[4] = thisList[base + 2]; if (thisList[base + 2] > extrema[5]) extrema[5] = thisList[base + 2]; } vector boxdims; boxdims.push_back(extrema[1] - extrema[0] + 1); boxdims.push_back(extrema[3] - extrema[2] + 1); boxdims.push_back(extrema[5] - extrema[4] + 1); VolumeFile roibox(boxdims, inVol->getSform()); roibox.setValueAllVoxels(0.0f); vector curList, newList;//make the 2 separate lists, so we don't need to test the label volume for (int64_t base = 0; base < listSize; base += 3)//could do this when we make the merged list...maybe use member variables to cut the argument clutter { int curvalue = (int)floor(curLabel->getValue(thisList[base], thisList[base + 1], thisList[base + 2]) + 0.5f); int newvalue = (int)floor(newLabel->getValue(thisList[base], thisList[base + 1], thisList[base + 2]) + 0.5f); if (curvalue == curLabelValue) { curList.push_back(thisList[base]); curList.push_back(thisList[base + 1]); curList.push_back(thisList[base + 2]); } if (newvalue == newLabelValue) { newList.push_back(thisList[base]); newList.push_back(thisList[base + 1]); newList.push_back(thisList[base + 2]); } } int64_t curListSize = (int64_t)curList.size(); int64_t newListSize = (int64_t)newList.size(); if (subvolNum == -1) { boxdims.push_back(myDims[3]); VolumeFile inbox(boxdims, inVol->getSform(), myDims[4]), outbox; for (int c = 0; c < myDims[4]; ++c) { for (int s = 0; s < myDims[3]; ++s) { for (int64_t base = 0; base < curListSize; base += 3) { if (s == 0 && c == 0)//first, smooth within only the current label { roibox.setValue(1.0f, curList[base] - extrema[0], curList[base + 1] - extrema[2], curList[base + 2] - extrema[4]); } inbox.setValue(inVol->getValue(curList[base], curList[base + 1], curList[base + 2], s, c), curList[base] - extrema[0], curList[base + 1] - extrema[2], curList[base + 2] - extrema[4], s, c); } } } AlgorithmVolumeSmoothing(NULL, &inbox, kernel, &outbox, &roibox, false); float kernelMult = -1.0f / kernel / kernel / 2.0f;//precompute the part of the kernel function that doesn't change for (int c = 0; c < myDims[4]; ++c) { for (int s = 0; s < myDims[3]; ++s) { const float* inFrame = inVol->getFrame(s, c); const float* labelFrame = curLabel->getFrame(); for (int64_t base = 0; base < newListSize; base += 3) { int myCurVal = (int)floor(curLabel->getValue(newList[base], newList[base + 1], newList[base + 2]) + 0.5f); if (myCurVal == curLabelValue) { outVol->setValue(outbox.getValue(newList[base] - extrema[0], newList[base + 1] - extrema[2], newList[base + 2] - extrema[4], s, c), newList[base], newList[base + 1], newList[base + 2], s, c); } else { float sum = 0.0f, weightsum = 0.0f;//coded in-place for now, its a special restricted case of volume dilate, copied out of nonorth volume smoothing int i = newList[base], j = newList[base + 1], k = newList[base + 2];//special casing orthogonal would be faster, but harder to follow/debug, and more code int imin = i - irange, imax = i + irange + 1;//one-after array size convention if (imin < 0) imin = 0; if (imax > myDims[0]) imax = myDims[0]; int jmin = j - jrange, jmax = j + jrange + 1; if (jmin < 0) jmin = 0; if (jmax > myDims[1]) jmax = myDims[1]; int kmin = k - krange, kmax = k + krange + 1; if (kmin < 0) kmin = 0; if (kmax > myDims[2]) kmax = myDims[2]; Vector3D kscratch, jscratch, iscratch; for (int kkern = kmin; kkern < kmax; ++kkern) { kscratch = kvec * (kkern - k); int64_t kindpart = kkern * myDims[1]; for (int jkern = jmin; jkern < jmax; ++jkern) { jscratch = kscratch + jvec * (jkern - j); int64_t jindpart = (kindpart + jkern) * myDims[0]; for (int ikern = imin; ikern < imax; ++ikern) { int64_t thisIndex = jindpart + ikern;//somewhat optimized index computation, could remove some integer multiplies, but there aren't that many int curVal = (int)floor(labelFrame[thisIndex] + 0.5f); if (curVal == curLabelValue) { iscratch = jscratch + ivec * (ikern - i); float tempf = iscratch.length(); float weight = exp(tempf * tempf * kernelMult); sum += weight * inFrame[thisIndex]; weightsum += weight; } } } } if (weightsum != 0.0f) { outVol->setValue(sum / weightsum, newList[base], newList[base + 1], newList[base + 2], s, c); }//should already be 0, so don't need to handle the else } } } } } else { VolumeFile inbox(boxdims, inVol->getSform(), myDims[4]), outbox; for (int c = 0; c < myDims[4]; ++c) { for (int64_t base = 0; base < curListSize; base += 3) { int myvalue = (int)floor(curLabel->getValue(curList[base], curList[base + 1], curList[base + 2]) + 0.5f); if (myvalue == matchedLabels[whichList].first) {//first, only smooth within the ROI of the current label, otherwise we would smooth in some zeros that aren't data if (c == 0) { roibox.setValue(1.0f, curList[base] - extrema[0], curList[base + 1] - extrema[2], curList[base + 2] - extrema[4]); } inbox.setValue(inVol->getValue(curList[base], curList[base + 1], curList[base + 2], subvolNum, c), curList[base] - extrema[0], curList[base + 1] - extrema[2], curList[base + 2] - extrema[4], 0, c); } } } AlgorithmVolumeSmoothing(NULL, &inbox, kernel, &outbox, &roibox, false); float kernelMult = -1.0f / kernel / kernel / 2.0f;//precompute the part of the kernel function that doesn't change for (int c = 0; c < myDims[4]; ++c) { const float* inFrame = inVol->getFrame(subvolNum, c); const float* labelFrame = curLabel->getFrame(); for (int64_t base = 0; base < newListSize; base += 3) { int myCurVal = (int)floor(curLabel->getValue(newList[base], newList[base + 1], newList[base + 2]) + 0.5f); if (myCurVal == curLabelValue) { outVol->setValue(outbox.getValue(newList[base] - extrema[0], newList[base + 1] - extrema[2], newList[base + 2] - extrema[4], 0, c), newList[base], newList[base + 1], newList[base + 2], 0, c); } else { float sum = 0.0f, weightsum = 0.0f;//coded in-place for now, its a special restricted case of volume dilate, copied out of nonorth volume smoothing int i = newList[base], j = newList[base + 1], k = newList[base + 2];//special casing orthogonal would be faster, but harder to follow/debug, and more code int imin = i - irange, imax = i + irange + 1;//one-after array size convention if (imin < 0) imin = 0; if (imax > myDims[0]) imax = myDims[0]; int jmin = j - jrange, jmax = j + jrange + 1; if (jmin < 0) jmin = 0; if (jmax > myDims[1]) jmax = myDims[1]; int kmin = k - krange, kmax = k + krange + 1; if (kmin < 0) kmin = 0; if (kmax > myDims[2]) kmax = myDims[2]; Vector3D kscratch, jscratch, iscratch; for (int kkern = kmin; kkern < kmax; ++kkern) { kscratch = kvec * (kkern - k); int64_t kindpart = kkern * myDims[1]; for (int jkern = jmin; jkern < jmax; ++jkern) { jscratch = kscratch + jvec * (jkern - j); int64_t jindpart = (kindpart + jkern) * myDims[0]; for (int ikern = imin; ikern < imax; ++ikern) { int64_t thisIndex = jindpart + ikern;//somewhat optimized index computation, could remove some integer multiplies, but there aren't that many int curVal = (int)floor(labelFrame[thisIndex] + 0.5f); if (curVal == curLabelValue) { iscratch = jscratch + ivec * (ikern - i); float tempf = iscratch.length(); float weight = exp(tempf * tempf * kernelMult); sum += weight * inFrame[thisIndex]; weightsum += weight; } } } } if (weightsum != 0.0f) { outVol->setValue(sum / weightsum, newList[base], newList[base + 1], newList[base + 2], 0, c); }//should already be 0, so don't need to handle the else } } } } } } } void AlgorithmVolumeParcelResampling::resampleFixZeros(LevelProgress& myProgress, const vector >& matchedLabels, const vector >& voxelLists, const VolumeFile* inVol, const VolumeFile* curLabel, const VolumeFile* newLabel, const float& kernel, VolumeFile* outVol, const int& subvolNum) { float kernBox = kernel * 3.0f; vector > volSpace = inVol->getSform();//copied from volume smoothing, perhaps this should be in a convenience method in VolumeFile Vector3D ivec, jvec, kvec, origin, ijorth, jkorth, kiorth; ivec[0] = volSpace[0][0]; jvec[0] = volSpace[0][1]; kvec[0] = volSpace[0][2]; origin[0] = volSpace[0][3];//needs to be this verbose because the axis and origin vectors are column vectors ivec[1] = volSpace[1][0]; jvec[1] = volSpace[1][1]; kvec[1] = volSpace[1][2]; origin[1] = volSpace[1][3];//while vector > is a column of row vectors ivec[2] = volSpace[2][0]; jvec[2] = volSpace[2][1]; kvec[2] = volSpace[2][2]; origin[2] = volSpace[2][3]; ijorth = ivec.cross(jvec).normal();//find the bounding box that encloses a sphere of radius kernBox jkorth = jvec.cross(kvec).normal(); kiorth = kvec.cross(ivec).normal(); int irange = (int)floor(abs(kernBox / ivec.dot(jkorth))); int jrange = (int)floor(abs(kernBox / jvec.dot(kiorth))); int krange = (int)floor(abs(kernBox / kvec.dot(ijorth))); if (irange < 1) irange = 1;//don't underflow if (jrange < 1) jrange = 1; if (krange < 1) krange = 1; map curLabelReverse, newLabelReverse; int numLabels = (int)matchedLabels.size(); for (int i = 0; i < numLabels; ++i) { curLabelReverse[matchedLabels[i].first] = i; newLabelReverse[matchedLabels[i].second] = i; } vector myDims; inVol->getDimensions(myDims); if (subvolNum == -1) { outVol->reinitialize(inVol->getOriginalDimensions(), inVol->getSform(), myDims[4], inVol->getType()); } else { vector newDims = inVol->getOriginalDimensions(); newDims.resize(3);//discard nonspatial dimentions outVol->reinitialize(newDims, inVol->getSform(), myDims[4], inVol->getType()); } outVol->setValueAllVoxels(0.0f); const GiftiLabelTable* curTable = curLabel->getMapLabelTable(0); for (int whichList = 0; whichList < numLabels; ++whichList) { int curLabelValue = matchedLabels[whichList].first; int newLabelValue = matchedLabels[whichList].second; myProgress.reportProgress(((float)whichList) / numLabels); myProgress.setTask("Processing parcel " + curTable->getLabelName(curLabelValue)); const vector& thisList = voxelLists[whichList]; int64_t listSize = (int64_t)thisList.size(); if (listSize > 2)//NOTE: this should NEVER be something other than a multiple of 3, but check against what we will actually access anyway { int extrema[6] = { thisList[0], thisList[0], thisList[1], thisList[1], thisList[2], thisList[2] }; for (int64_t base = 3; base < listSize; base += 3) { if (thisList[base] < extrema[0]) extrema[0] = thisList[base]; if (thisList[base] > extrema[1]) extrema[1] = thisList[base]; if (thisList[base + 1] < extrema[2]) extrema[2] = thisList[base + 1]; if (thisList[base + 1] > extrema[3]) extrema[3] = thisList[base + 1]; if (thisList[base + 2] < extrema[4]) extrema[4] = thisList[base + 2]; if (thisList[base + 2] > extrema[5]) extrema[5] = thisList[base + 2]; } vector boxdims; boxdims.push_back(extrema[1] - extrema[0] + 1); boxdims.push_back(extrema[3] - extrema[2] + 1); boxdims.push_back(extrema[5] - extrema[4] + 1); VolumeFile roibox(boxdims, inVol->getSform()); roibox.setValueAllVoxels(0.0f); vector newList;//make the separate new list, so we don't need to test the label volume for (int64_t base = 0; base < listSize; base += 3)//could do this when we make the merged list...maybe use member variables to cut the argument clutter { int newvalue = (int)floor(newLabel->getValue(thisList[base], thisList[base + 1], thisList[base + 2]) + 0.5f); if (newvalue == newLabelValue) { newList.push_back(thisList[base]); newList.push_back(thisList[base + 1]); newList.push_back(thisList[base + 2]); } } int64_t newListSize = (int64_t)newList.size(); if (subvolNum == -1) { boxdims.push_back(myDims[3]); VolumeFile inbox(boxdims, inVol->getSform(), myDims[4]), outbox; for (int c = 0; c < myDims[4]; ++c) { for (int s = 0; s < myDims[3]; ++s) { for (int64_t base = 0; base < listSize; base += 3) { if (s == 0 && c == 0)//smooth within BOTH labels, but only copy in from current label { roibox.setValue(1.0f, thisList[base] - extrema[0], thisList[base + 1] - extrema[2], thisList[base + 2] - extrema[4]); } int curValue = (int)floor(curLabel->getValue(thisList[base], thisList[base + 1], thisList[base + 2]) + 0.5f); if (curValue == curLabelValue) { inbox.setValue(inVol->getValue(thisList[base], thisList[base + 1], thisList[base + 2], s, c), thisList[base] - extrema[0], thisList[base + 1] - extrema[2], thisList[base + 2] - extrema[4], s, c); } else { inbox.setValue(0.0f, thisList[base] - extrema[0], thisList[base + 1] - extrema[2], thisList[base + 2] - extrema[4], s, c); } } } } AlgorithmVolumeSmoothing(NULL, &inbox, kernel, &outbox, &roibox, true); float kernelMult = -1.0f / kernel / kernel / 2.0f;//precompute the part of the kernel function that doesn't change VolumeFile* current = &outbox, *next = &inbox, *tempvol;//reuse inbox as scratch space for iterated dilation const float* labelFrame = newLabel->getFrame(); int fixIter; for (fixIter = 0; fixIter < FIX_ZEROS_POST_ITERATIONS; ++fixIter) { bool again = false; for (int c = 0; c < myDims[4]; ++c)//since fix zeros smoothing doesn't consider components as multidimensional datatypes, neither should this { for (int s = 0; s < myDims[3]; ++s) { for (int64_t base = 0; base < newListSize; base += 3) { float curVal = current->getValue(newList[base] - extrema[0], newList[base + 1] - extrema[2], newList[base + 2] - extrema[4], s, c); if (curVal == 0.0f) { float sum = 0.0f, weightsum = 0.0f;//coded in-place for now, its a special restricted case of volume dilate, copied out of nonorth volume smoothing int i = newList[base], j = newList[base + 1], k = newList[base + 2];//special casing orthogonal would be faster, but harder to follow/debug, and more code int imin = i - irange, imax = i + irange + 1;//one-after array size convention if (imin < 0) imin = 0; if (imax > myDims[0]) imax = myDims[0]; int jmin = j - jrange, jmax = j + jrange + 1; if (jmin < 0) jmin = 0; if (jmax > myDims[1]) jmax = myDims[1]; int kmin = k - krange, kmax = k + krange + 1; if (kmin < 0) kmin = 0; if (kmax > myDims[2]) kmax = myDims[2]; Vector3D kscratch, jscratch, iscratch; for (int kkern = kmin; kkern < kmax; ++kkern) { kscratch = kvec * (kkern - k); int64_t kindpart = kkern * myDims[1]; for (int jkern = jmin; jkern < jmax; ++jkern) { jscratch = kscratch + jvec * (jkern - j); int64_t jindpart = (kindpart + jkern) * myDims[0]; for (int ikern = imin; ikern < imax; ++ikern) { int64_t thisIndex = jindpart + ikern;//somewhat optimized index computation, could remove some integer multiplies, but there aren't that many int tempi = (int)floor(labelFrame[thisIndex] + 0.5f); if (tempi == curLabelValue) { float dataVal = current->getValue(ikern - extrema[0], jkern - extrema[2], kkern - extrema[4], s, c); if (dataVal != 0.0f) { iscratch = jscratch + ivec * (ikern - i); float tempf = iscratch.length(); float weight = exp(tempf * tempf * kernelMult); sum += weight * dataVal; weightsum += weight; } } } } } if (weightsum != 0.0f) { next->setValue(sum / weightsum, newList[base] - extrema[0], newList[base + 1] - extrema[2], newList[base + 2] - extrema[4], s, c); } else { again = true; next->setValue(0.0f, newList[base] - extrema[0], newList[base + 1] - extrema[2], newList[base + 2] - extrema[4], s, c); } } else { next->setValue(curVal, newList[base] - extrema[0], newList[base + 1] - extrema[2], newList[base + 2] - extrema[4], s, c); } } } } tempvol = current; current = next; next = tempvol; if (!again) break; } if (fixIter == FIX_ZEROS_POST_ITERATIONS) { const GiftiLabelTable* curLabelTable = curLabel->getMapLabelTable(0); CaretLogWarning("unable to fix all zeros in parcel " + curLabelTable->getLabelName(curLabelValue)); } for (int c = 0; c < myDims[4]; ++c)//copy the final result into the output valume { for (int s = 0; s < myDims[3]; ++s) { for (int64_t base = 0; base < newListSize; base += 3) { outVol->setValue(current->getValue(newList[base] - extrema[0], newList[base + 1] - extrema[2], newList[base + 2] - extrema[4], s, c), newList[base], newList[base + 1], newList[base + 2], s, c); } } } } else { VolumeFile inbox(boxdims, inVol->getSform(), myDims[4]), outbox; for (int c = 0; c < myDims[4]; ++c) { for (int64_t base = 0; base < listSize; base += 3) { if (c == 0)//smooth within BOTH labels, but only copy in from current label { roibox.setValue(1.0f, thisList[base] - extrema[0], thisList[base + 1] - extrema[2], thisList[base + 2] - extrema[4]); } int curValue = (int)floor(curLabel->getValue(thisList[base], thisList[base + 1], thisList[base + 2]) + 0.5f); if (curValue == curLabelValue) { inbox.setValue(inVol->getValue(thisList[base], thisList[base + 1], thisList[base + 2], subvolNum, c), thisList[base] - extrema[0], thisList[base + 1] - extrema[2], thisList[base + 2] - extrema[4], 0, c); } } } AlgorithmVolumeSmoothing(NULL, &inbox, kernel, &outbox, &roibox, true); float kernelMult = -1.0f / kernel / kernel / 2.0f;//precompute the part of the kernel function that doesn't change int fixIter; VolumeFile* current = &outbox, *next = &inbox, *tempvol;//reuse inbox as scratch space for iterated dilation const float* labelFrame = newLabel->getFrame(); for (fixIter = 0; fixIter < FIX_ZEROS_POST_ITERATIONS; ++fixIter) { bool again = false; for (int c = 0; c < myDims[4]; ++c)//since fix zeros smoothing doesn't consider components as multidimensional datatypes, neither should this { for (int64_t base = 0; base < newListSize; base += 3) { float curVal = current->getValue(newList[base] - extrema[0], newList[base + 1] - extrema[2], newList[base + 2] - extrema[4], 0, c); if (curVal == 0.0f) { float sum = 0.0f, weightsum = 0.0f;//coded in-place for now, its a special restricted case of volume dilate, copied out of nonorth volume smoothing int i = newList[base], j = newList[base + 1], k = newList[base + 2];//special casing orthogonal would be faster, but harder to follow/debug, and more code int imin = i - irange, imax = i + irange + 1;//one-after array size convention if (imin < 0) imin = 0; if (imax > myDims[0]) imax = myDims[0]; int jmin = j - jrange, jmax = j + jrange + 1; if (jmin < 0) jmin = 0; if (jmax > myDims[1]) jmax = myDims[1]; int kmin = k - krange, kmax = k + krange + 1; if (kmin < 0) kmin = 0; if (kmax > myDims[2]) kmax = myDims[2]; Vector3D kscratch, jscratch, iscratch; for (int kkern = kmin; kkern < kmax; ++kkern) { kscratch = kvec * (kkern - k); int64_t kindpart = kkern * myDims[1]; for (int jkern = jmin; jkern < jmax; ++jkern) { jscratch = kscratch + jvec * (jkern - j); int64_t jindpart = (kindpart + jkern) * myDims[0]; for (int ikern = imin; ikern < imax; ++ikern) { int64_t thisIndex = jindpart + ikern;//somewhat optimized index computation, could remove some integer multiplies, but there aren't that many int tempi = (int)floor(labelFrame[thisIndex] + 0.5f); if (tempi == curLabelValue) { float dataVal = current->getValue(ikern - extrema[0], jkern - extrema[2], kkern - extrema[4], 0, c); if (dataVal != 0.0f) { iscratch = jscratch + ivec * (ikern - i); float tempf = iscratch.length(); float weight = exp(tempf * tempf * kernelMult); sum += weight * dataVal; weightsum += weight; } } } } } if (weightsum != 0.0f) { next->setValue(sum / weightsum, newList[base] - extrema[0], newList[base + 1] - extrema[2], newList[base + 2] - extrema[4], 0, c); } else { again = true; next->setValue(0.0f, newList[base] - extrema[0], newList[base + 1] - extrema[2], newList[base + 2] - extrema[4], 0, c); } } else { next->setValue(curVal, newList[base] - extrema[0], newList[base + 1] - extrema[2], newList[base + 2] - extrema[4], 0, c); } } } tempvol = current; current = next; next = tempvol; if (!again) break; } if (fixIter == FIX_ZEROS_POST_ITERATIONS) { const GiftiLabelTable* curLabelTable = curLabel->getMapLabelTable(0); CaretLogWarning("unable to fix all zeros in parcel " + curLabelTable->getLabelName(curLabelValue)); } for (int c = 0; c < myDims[4]; ++c)//copy the final result into the output valume { for (int64_t base = 0; base < newListSize; base += 3) { outVol->setValue(current->getValue(newList[base] - extrema[0], newList[base + 1] - extrema[2], newList[base + 2] - extrema[4], 0, c), newList[base], newList[base + 1], newList[base + 2], 0, c); } } } } } } float AlgorithmVolumeParcelResampling::getAlgorithmInternalWeight() { return 1.0f;//override this if needed, if the progress bar isn't smooth } float AlgorithmVolumeParcelResampling::getSubAlgorithmWeight() { //return AlgorithmInsertNameHere::getAlgorithmWeight();//if you use a subalgorithm return 0.0f; } connectome-workbench-1.2.3+git41-gc4c6c90/src/Algorithms/AlgorithmVolumeParcelResampling.h000066400000000000000000000054431300200146000313230ustar00rootroot00000000000000#ifndef __ALGORITHM_VOLUME_PARCEL_RESAMPLING_H__ #define __ALGORITHM_VOLUME_PARCEL_RESAMPLING_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 "AbstractAlgorithm.h" namespace caret { class AlgorithmVolumeParcelResampling : public AbstractAlgorithm { AlgorithmVolumeParcelResampling(); void matchLabels(const caret::VolumeFile* curLabel, const caret::VolumeFile* newLabel, std::vector >& matchedLabels); void generateVoxelLists(const std::vector >& matchedLabels, const VolumeFile* curLabel, const VolumeFile* newLabel, std::vector >& voxelLists); void resampleFixZeros(LevelProgress& myProgress, const std::vector >& matchedLabels, const std::vector >& voxelLists, const VolumeFile* inVol, const VolumeFile* curLabel, const caret::VolumeFile* newLabel, const float& kernel, VolumeFile* outVol, const int& subvolNum); void resample(LevelProgress& myProgress, const std::vector >& matchedLabels, const std::vector >& voxelLists, const VolumeFile* inVol, const VolumeFile* curLabel, const caret::VolumeFile* newLabel, const float& kernel, VolumeFile* outVol, const int& subvolNum); protected: static float getSubAlgorithmWeight(); static float getAlgorithmInternalWeight(); public: AlgorithmVolumeParcelResampling(ProgressObject* myProgObj, const VolumeFile* inVol, const VolumeFile* curLabel, const VolumeFile* newLabel, const float& kernel, VolumeFile* outVol, const bool& fixZeros = false, const int& subvolNum = -1); static OperationParameters* getParameters(); static void useParameters(OperationParameters* myParams, ProgressObject* myProgObj); static AString getCommandSwitch(); static AString getShortDescription(); }; typedef TemplateAutoOperation AutoAlgorithmVolumeParcelResampling; } #endif //__ALGORITHM_VOLUME_PARCEL_RESAMPLING_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Algorithms/AlgorithmVolumeParcelResamplingGeneric.cxx000066400000000000000000000664521300200146000332020ustar00rootroot00000000000000/*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 "AlgorithmVolumeParcelResamplingGeneric.h" #include "AlgorithmException.h" #include "VolumeFile.h" #include "CaretAssert.h" #include "CaretLogger.h" #include "Vector3D.h" #include "CaretOMP.h" #include #include #include #include using namespace caret; using namespace std; const int FIX_ZEROS_POST_ITERATIONS = 10;//number of times to do the "find remaining zeros and try to fill" code before giving up when -fix-zeros is specified AString AlgorithmVolumeParcelResamplingGeneric::getCommandSwitch() { return "-volume-parcel-resampling-generic"; } AString AlgorithmVolumeParcelResamplingGeneric::getShortDescription() { return "SMOOTH AND RESAMPLE VOLUME PARCELS FROM DIFFERENT VOLUME SPACE"; } OperationParameters* AlgorithmVolumeParcelResamplingGeneric::getParameters() { OperationParameters* ret = new OperationParameters(); ret->addVolumeParameter(1, "volume-in", "the input data volume"); ret->addVolumeParameter(2, "cur-parcels", "label volume of where the parcels currently are"); ret->addVolumeParameter(3, "new-parcels", "label volume of where the parcels should be"); ret->addDoubleParameter(4, "kernel", "gaussian kernel sigma to smooth by during resampling"); ret->addVolumeOutputParameter(5, "volume-out", "output volume"); ret->createOptionalParameter(6, "-fix-zeros", "treat zero values as not being data"); OptionalParameter* subvolSelect = ret->createOptionalParameter(7, "-subvolume", "select a single subvolume as input"); subvolSelect->addStringParameter(1, "subvol", "the subvolume number or name"); ret->setHelpText( AString("Smooths and resamples the region inside each label in cur-parcels to the region of the same label name in new-parcels. ") + "Any voxels in the output label region but outside the input label region will be extrapolated from nearby data. " + "The -fix-zeros option causes the smoothing to not use an input value if it is zero, but still write a smoothed value to the voxel, and after smoothing " + "is complete, it will check for any remaining values of zero, and fill them in with extrapolated values. " + "The output volume will use the volume space of new-parcels, which does not need to be in the same volume space as the input." ); return ret; } void AlgorithmVolumeParcelResamplingGeneric::useParameters(OperationParameters* myParams, ProgressObject* myProgObj) { VolumeFile* inVol = myParams->getVolume(1); VolumeFile* curLabel = myParams->getVolume(2); VolumeFile* newLabel = myParams->getVolume(3); float kernel = (float)myParams->getDouble(4); VolumeFile* outVol = myParams->getOutputVolume(5); bool fixZeros = false; if (myParams->getOptionalParameter(6)->m_present) { fixZeros = true; } OptionalParameter* subvolSelect = myParams->getOptionalParameter(7); int subvolNum = -1; if (subvolSelect->m_present) { subvolNum = (int)inVol->getMapIndexFromNameOrNumber(subvolSelect->getString(1)); if (subvolNum < 0) { throw AlgorithmException("invalid subvolume specified"); } } AlgorithmVolumeParcelResamplingGeneric(myProgObj, inVol, curLabel, newLabel, kernel, outVol, fixZeros, subvolNum); } AlgorithmVolumeParcelResamplingGeneric::AlgorithmVolumeParcelResamplingGeneric(ProgressObject* myProgObj, const VolumeFile* inVol, const VolumeFile* curLabel, const VolumeFile* newLabel, const float& kernel, VolumeFile* outVol, const bool& fixZeros, const int& subvolNum) : AbstractAlgorithm(myProgObj) { CaretAssert(inVol != NULL); CaretAssert(curLabel != NULL); CaretAssert(newLabel != NULL); CaretAssert(outVol != NULL); if (!inVol->matchesVolumeSpace(curLabel)) { throw AlgorithmException("input label volume must be in the same space as input data volume"); } if (curLabel->getType() != SubvolumeAttributes::LABEL || newLabel->getType() != SubvolumeAttributes::LABEL) { throw AlgorithmException("parcel volumes are not of type label"); } if (subvolNum < -1 || subvolNum >= inVol->getNumberOfMaps()) { throw AlgorithmException("invalid subvolume specified"); } vector > matchedLabels; matchLabels(curLabel, newLabel, matchedLabels); if (matchedLabels.size() == 0) { throw AlgorithmException("no matching labels"); } vector > voxelLists; generateVoxelLists(matchedLabels, newLabel, voxelLists); int voxelListsSize = (int)voxelLists.size(); LevelProgress myProgress(myProgObj); float kernBox = kernel * 3.0f; float kernelMult = -1.0f / kernel / kernel / 2.0f;//precompute the part of the kernel function that doesn't change vector > newVolSpace = newLabel->getSform();//copied from volume smoothing, perhaps this should be in a convenience method in VolumeFile Vector3D ivecnew, jvecnew, kvecnew, ijorthnew, jkorthnew, kiorthnew; ivecnew[0] = newVolSpace[0][0]; jvecnew[0] = newVolSpace[0][1]; kvecnew[0] = newVolSpace[0][2]; ivecnew[1] = newVolSpace[1][0]; jvecnew[1] = newVolSpace[1][1]; kvecnew[1] = newVolSpace[1][2]; ivecnew[2] = newVolSpace[2][0]; jvecnew[2] = newVolSpace[2][1]; kvecnew[2] = newVolSpace[2][2]; ijorthnew = ivecnew.cross(jvecnew).normal();//find the bounding box that encloses a sphere of radius kernBox jkorthnew = jvecnew.cross(kvecnew).normal(); kiorthnew = kvecnew.cross(ivecnew).normal(); float irangenew = abs(kernBox / ivecnew.dot(jkorthnew));//note, these are in index space float jrangenew = abs(kernBox / jvecnew.dot(kiorthnew)); float krangenew = abs(kernBox / kvecnew.dot(ijorthnew)); if (irangenew < 1.0f) irangenew = 1.0f;//don't underflow, always use at least a 3x3x3 box if (jrangenew < 1.0f) jrangenew = 1.0f; if (krangenew < 1.0f) krangenew = 1.0f; vector > volSpace = inVol->getSform();//copied from volume smoothing, perhaps this should be in a convenience method in VolumeFile Vector3D ivec, jvec, kvec, ijorth, jkorth, kiorth; ivec[0] = volSpace[0][0]; jvec[0] = volSpace[0][1]; kvec[0] = volSpace[0][2];//needs to be this verbose because the axis and origin vectors are column vectors ivec[1] = volSpace[1][0]; jvec[1] = volSpace[1][1]; kvec[1] = volSpace[1][2];//while vector > is a column of row vectors ivec[2] = volSpace[2][0]; jvec[2] = volSpace[2][1]; kvec[2] = volSpace[2][2]; ijorth = ivec.cross(jvec).normal();//find the bounding box that encloses a sphere of radius kernBox jkorth = jvec.cross(kvec).normal(); kiorth = kvec.cross(ivec).normal(); float irange = abs(kernBox / ivec.dot(jkorth));//note, these are in index space float jrange = abs(kernBox / jvec.dot(kiorth)); float krange = abs(kernBox / kvec.dot(ijorth)); if (irange < 1.0f) irange = 1.0f;//don't underflow, always use at least a 3x3x3 box if (jrange < 1.0f) jrange = 1.0f; if (krange < 1.0f) krange = 1.0f; vector myDims; inVol->getDimensions(myDims); vector newDims; newLabel->getDimensions(newDims); if (subvolNum == -1) { vector outDims = newLabel->getOriginalDimensions(), inDims = inVol->getOriginalDimensions(); outDims.resize(3); for (int i = 3; i < (int)inDims.size(); ++i) { outDims.push_back(inDims[i]); } outVol->reinitialize(outDims, newLabel->getSform(), myDims[4], inVol->getType()); } else { vector outDims = newLabel->getOriginalDimensions(); outDims.resize(3);//discard nonspatial dimentions outVol->reinitialize(outDims, newLabel->getSform(), myDims[4], inVol->getType()); } outVol->setValueAllVoxels(0.0f); const float* labelFrame = curLabel->getFrame(); const float* newLabelFrame = newLabel->getFrame(); CaretArray scratchFrame(newDims[0] * newDims[1] * newDims[2]), scratchFrame2(newDims[0] * newDims[1] * newDims[2]), tempFrame; for (int whichList = 0; whichList < voxelListsSize; ++whichList) { int curLabelValue = matchedLabels[whichList].first; int newLabelValue = matchedLabels[whichList].second; vector& thisList = voxelLists[whichList]; int64_t listSize = (int64_t)thisList.size(); if (subvolNum == -1) { for (int c = 0; c < myDims[4]; ++c) { for (int s = 0; s < myDims[3]; ++s) { const float* inFrame = inVol->getFrame(s, c); //#pragma omp CARET_PARFOR schedule(dynamic) for (int64_t base = 0; base < listSize; base += 3) { float sum = 0.0f, weightsum = 0.0f; int i = thisList[base], j = thisList[base + 1], k = thisList[base + 2]; float xyz[3], curijk[3]; newLabel->indexToSpace(i, j, k, xyz); inVol->spaceToIndex(xyz, curijk); int imin = (int)ceil(curijk[0] - irange), imax = (int)floor(curijk[0] + irange) + 1; if (imin < 0) imin = 0; if (imax > myDims[0]) imax = myDims[0]; int jmin = (int)ceil(curijk[1] - jrange), jmax = (int)floor(curijk[1] + jrange) + 1; if (jmin < 0) jmin = 0; if (jmax > myDims[1]) jmax = myDims[1]; int kmin = (int)ceil(curijk[2] - krange), kmax = (int)floor(curijk[2] + krange) + 1; if (kmin < 0) kmin = 0; if (kmax > myDims[2]) kmax = myDims[2]; for (int kkern = kmin; kkern < kmax; ++kkern) { Vector3D kscratch = kvec * (kkern - curijk[2]); int64_t kindpart = kkern * myDims[1]; for (int jkern = jmin; jkern < jmax; ++jkern) { Vector3D jscratch = kscratch + jvec * (jkern - curijk[1]); int64_t jindpart = (kindpart + jkern) * myDims[0]; for (int ikern = imin; ikern < imax; ++ikern) { int64_t thisIndex = jindpart + ikern;//somewhat optimized index computation, could remove some integer multiplies, but there aren't that many int curVal = (int)floor(labelFrame[thisIndex] + 0.5f); float dataVal = inFrame[thisIndex]; if (curVal == curLabelValue && (!fixZeros || dataVal != 0.0f)) { Vector3D iscratch = jscratch + ivec * (ikern - curijk[0]); float tempf = iscratch.length(); float weight = exp(tempf * tempf * kernelMult); sum += weight * dataVal; weightsum += weight; } } } } if (weightsum != 0.0f) { scratchFrame[outVol->getIndex(i, j, k)] = sum / weightsum; } else { scratchFrame[outVol->getIndex(i, j, k)] = 0.0f; } } if (fixZeros) { int fixIter; for (fixIter = 0; fixIter < FIX_ZEROS_POST_ITERATIONS; ++fixIter) { bool again = false; //#pragma omp CARET_PARFOR schedule(dynamic) for (int64_t base = 0; base < listSize; base += 3) { int i = thisList[base], j = thisList[base + 1], k = thisList[base + 2]; int64_t outIndex = outVol->getIndex(i, j, k); float dataVal = scratchFrame[outIndex]; if (dataVal == 0.0f) { float sum = 0.0f, weightsum = 0.0f; int imin = (int)ceil(i - irangenew), imax = (int)floor(i + irangenew) + 1; if (imin < 0) imin = 0; if (imax > newDims[0]) imax = newDims[0]; int jmin = (int)ceil(j - jrangenew), jmax = (int)floor(j + jrangenew) + 1; if (jmin < 0) jmin = 0; if (jmax > newDims[1]) jmax = newDims[1]; int kmin = (int)ceil(k - krangenew), kmax = (int)floor(k + krangenew) + 1; if (kmin < 0) kmin = 0; if (kmax > newDims[2]) kmax = newDims[2]; for (int kkern = kmin; kkern < kmax; ++kkern) { Vector3D kscratch = kvecnew * (kkern - k); int64_t kindpart = kkern * newDims[1]; for (int jkern = jmin; jkern < jmax; ++jkern) { Vector3D jscratch = kscratch + jvecnew * (jkern - j); int64_t jindpart = (kindpart + jkern) * newDims[0]; for (int ikern = imin; ikern < imax; ++ikern) { int64_t thisIndex = jindpart + ikern;//somewhat optimized index computation, could remove some integer multiplies, but there aren't that many int newVal = (int)floor(newLabelFrame[thisIndex] + 0.5f); float dataVal = scratchFrame[thisIndex]; if (newVal == newLabelValue && (!fixZeros || dataVal != 0.0f)) { Vector3D iscratch = jscratch + ivecnew * (ikern - i); float tempf = iscratch.length(); float weight = exp(tempf * tempf * kernelMult); sum += weight * dataVal; weightsum += weight; } } } } if (weightsum != 0.0f) { scratchFrame2[outIndex] = sum / weightsum; } else { again = true; scratchFrame2[outIndex] = 0.0f; } } else { scratchFrame2[outIndex] = scratchFrame[outIndex]; } } tempFrame = scratchFrame;//this is just pointer swapping, CaretArray is not like vector scratchFrame = scratchFrame2; scratchFrame2 = tempFrame; if (!again) break; } if (fixIter == FIX_ZEROS_POST_ITERATIONS) { const GiftiLabelTable* curLabelTable = curLabel->getMapLabelTable(0); CaretLogWarning("unable to fix all zeros in parcel " + curLabelTable->getLabelName(curLabelValue)); } } for (int64_t base = 0; base < listSize; base += 3) { int i = thisList[base], j = thisList[base + 1], k = thisList[base + 2]; int64_t outIndex = outVol->getIndex(i, j, k); outVol->setValue(scratchFrame[outIndex], i, j, k, s, c); } } } } else { for (int c = 0; c < myDims[4]; ++c) { const float* inFrame = inVol->getFrame(subvolNum, c); #pragma omp CARET_PARFOR schedule(dynamic) for (int64_t base = 0; base < listSize; base += 3) { float sum = 0.0f, weightsum = 0.0f; int i = thisList[base], j = thisList[base + 1], k = thisList[base + 2]; float xyz[3], curijk[3]; newLabel->indexToSpace(i, j, k, xyz); inVol->spaceToIndex(xyz, curijk); int imin = (int)ceil(curijk[0] - irange), imax = (int)floor(curijk[0] + irange) + 1; if (imin < 0) imin = 0; if (imax > myDims[0]) imax = myDims[0]; int jmin = (int)ceil(curijk[1] - jrange), jmax = (int)floor(curijk[1] + jrange) + 1; if (jmin < 0) jmin = 0; if (jmax > myDims[1]) jmax = myDims[1]; int kmin = (int)ceil(curijk[2] - krange), kmax = (int)floor(curijk[2] + krange) + 1; if (kmin < 0) kmin = 0; if (kmax > myDims[2]) kmax = myDims[2]; for (int kkern = kmin; kkern < kmax; ++kkern) { Vector3D kscratch = kvec * (kkern - curijk[2]); int64_t kindpart = kkern * myDims[1]; for (int jkern = jmin; jkern < jmax; ++jkern) { Vector3D jscratch = kscratch + jvec * (jkern - curijk[1]); int64_t jindpart = (kindpart + jkern) * myDims[0]; for (int ikern = imin; ikern < imax; ++ikern) { int64_t thisIndex = jindpart + ikern;//somewhat optimized index computation, could remove some integer multiplies, but there aren't that many int newVal = (int)floor(newLabelFrame[thisIndex] + 0.5f); float dataVal = inFrame[thisIndex]; if (newVal == newLabelValue && (!fixZeros || dataVal != 0.0f)) { Vector3D iscratch = jscratch + ivec * (ikern - curijk[0]); float tempf = iscratch.length(); float weight = exp(tempf * tempf * kernelMult); sum += weight * dataVal; weightsum += weight; } } } } if (weightsum != 0.0f) { scratchFrame[outVol->getIndex(i, j, k)] = sum / weightsum; } else { scratchFrame[outVol->getIndex(i, j, k)] = 0.0f; } } if (fixZeros) { int fixIter; for (fixIter = 0; fixIter < FIX_ZEROS_POST_ITERATIONS; ++fixIter) { bool again = false; #pragma omp CARET_PARFOR schedule(dynamic) for (int64_t base = 0; base < listSize; base += 3) { int i = thisList[base], j = thisList[base + 1], k = thisList[base + 2]; int64_t outIndex = outVol->getIndex(i, j, k); float dataVal = scratchFrame[outIndex]; if (dataVal == 0.0f) { float sum = 0.0f, weightsum = 0.0f; int imin = (int)ceil(i - irangenew), imax = (int)floor(i + irangenew) + 1; if (imin < 0) imin = 0; if (imax > newDims[0]) imax = newDims[0]; int jmin = (int)ceil(j - jrangenew), jmax = (int)floor(j + jrangenew) + 1; if (jmin < 0) jmin = 0; if (jmax > newDims[1]) jmax = newDims[1]; int kmin = (int)ceil(k - krangenew), kmax = (int)floor(k + krangenew) + 1; if (kmin < 0) kmin = 0; if (kmax > newDims[2]) kmax = newDims[2]; for (int kkern = kmin; kkern < kmax; ++kkern) { Vector3D kscratch = kvecnew * (kkern - k); int64_t kindpart = kkern * newDims[1]; for (int jkern = jmin; jkern < jmax; ++jkern) { Vector3D jscratch = kscratch + jvecnew * (jkern - j); int64_t jindpart = (kindpart + jkern) * newDims[0]; for (int ikern = imin; ikern < imax; ++ikern) { int64_t thisIndex = jindpart + ikern;//somewhat optimized index computation, could remove some integer multiplies, but there aren't that many int curVal = (int)floor(labelFrame[thisIndex] + 0.5f); float dataVal = scratchFrame[thisIndex]; if (curVal == curLabelValue && (!fixZeros || dataVal != 0.0f)) { Vector3D iscratch = jscratch + ivecnew * (ikern - i); float tempf = iscratch.length(); float weight = exp(tempf * tempf * kernelMult); sum += weight * dataVal; weightsum += weight; } } } } if (weightsum != 0.0f) { scratchFrame2[outIndex] = sum / weightsum; } else { again = true; scratchFrame2[outIndex] = 0.0f; } } else { scratchFrame2[outIndex] = scratchFrame[outIndex]; } } tempFrame = scratchFrame;//this is just pointer swapping, CaretArray is not like vector scratchFrame = scratchFrame2; scratchFrame2 = tempFrame; if (!again) break; } if (fixIter == FIX_ZEROS_POST_ITERATIONS) { const GiftiLabelTable* curLabelTable = curLabel->getMapLabelTable(0); CaretLogWarning("unable to fix all zeros in parcel " + curLabelTable->getLabelName(curLabelValue)); } } for (int64_t base = 0; base < listSize; base += 3) { int i = thisList[base], j = thisList[base + 1], k = thisList[base + 2]; int64_t outIndex = outVol->getIndex(i, j, k); outVol->setValue(scratchFrame[outIndex], i, j, k, 0, c); } } } } } void AlgorithmVolumeParcelResamplingGeneric::matchLabels(const VolumeFile* curLabel, const VolumeFile* newLabel, vector >& matchedLabels) { const GiftiLabelTable* curTable = curLabel->getMapLabelTable(0), *newTable = newLabel->getMapLabelTable(0); vector curKeys; curTable->getKeys(curKeys); int32_t curUnused = curTable->getUnassignedLabelKey(); for (int i = 0; i < (int)curKeys.size(); ++i) { if (curKeys[i] == curUnused) continue;//always skip the unassigned label if (newTable->getLabel(curKeys[i]) != NULL && newTable->getLabelName(curKeys[i]) == curTable->getLabelName(curKeys[i])) {//do the obvious check first matchedLabels.push_back(make_pair(curKeys[i], curKeys[i])); } else { int32_t newKey = newTable->getLabelKeyFromName(curTable->getLabelName(curKeys[i])); if (newKey != -1) { matchedLabels.push_back(make_pair(curKeys[i], newKey)); } } } } void AlgorithmVolumeParcelResamplingGeneric::generateVoxelLists(const vector >& matchedLabels, const VolumeFile* newLabel, vector >& voxelLists) { map newLabelReverse; for (int i = 0; i < (int)matchedLabels.size(); ++i) { newLabelReverse[matchedLabels[i].second] = i; } voxelLists.resize(matchedLabels.size()); vector myDims; newLabel->getDimensions(myDims); for (int64_t k = 0; k < myDims[2]; ++k) { for (int64_t j = 0; j < myDims[1]; ++j) { for (int64_t i = 0; i < myDims[0]; ++i) { int newValue = (int)floor(newLabel->getValue(i, j, k) + 0.5f); map::iterator newiter = newLabelReverse.find(newValue); if (newiter != newLabelReverse.end()) { voxelLists[newiter->second].push_back(i); voxelLists[newiter->second].push_back(j); voxelLists[newiter->second].push_back(k); } } } } } float AlgorithmVolumeParcelResamplingGeneric::getAlgorithmInternalWeight() { return 1.0f;//override this if needed, if the progress bar isn't smooth } float AlgorithmVolumeParcelResamplingGeneric::getSubAlgorithmWeight() { //return AlgorithmInsertNameHere::getAlgorithmWeight();//if you use a subalgorithm return 0.0f; } connectome-workbench-1.2.3+git41-gc4c6c90/src/Algorithms/AlgorithmVolumeParcelResamplingGeneric.h000066400000000000000000000043141300200146000326140ustar00rootroot00000000000000#ifndef __ALGORITHM_VOLUME_PARCEL_RESAMPLING_GENERIC_H__ #define __ALGORITHM_VOLUME_PARCEL_RESAMPLING_GENERIC_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 "AbstractAlgorithm.h" namespace caret { class AlgorithmVolumeParcelResamplingGeneric : public AbstractAlgorithm { AlgorithmVolumeParcelResamplingGeneric(); protected: static float getSubAlgorithmWeight(); static float getAlgorithmInternalWeight(); void matchLabels(const caret::VolumeFile* curLabel, const caret::VolumeFile* newLabel, std::vector >& matchedLabels); void generateVoxelLists(const std::vector >& matchedLabels, const VolumeFile* newLabel, std::vector >& voxelLists); public: AlgorithmVolumeParcelResamplingGeneric(ProgressObject* myProgObj, const VolumeFile* inVol, const VolumeFile* curLabel, const VolumeFile* newLabel, const float& kernel, VolumeFile* outVol, const bool& fixZeros = false, const int& subvolNum = -1); static OperationParameters* getParameters(); static void useParameters(OperationParameters* myParams, ProgressObject* myProgObj); static AString getCommandSwitch(); static AString getShortDescription(); }; typedef TemplateAutoOperation AutoAlgorithmVolumeParcelResamplingGeneric; } #endif //__ALGORITHM_VOLUME_PARCEL_RESAMPLING_GENERIC_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Algorithms/AlgorithmVolumeParcelSmoothing.cxx000066400000000000000000000315351300200146000315450ustar00rootroot00000000000000/*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 "AlgorithmVolumeParcelSmoothing.h" #include "AlgorithmException.h" #include "VolumeFile.h" #include "AlgorithmVolumeSmoothing.h" #include #include using namespace caret; using namespace std; AString AlgorithmVolumeParcelSmoothing::getCommandSwitch() { return "-volume-parcel-smoothing"; } AString AlgorithmVolumeParcelSmoothing::getShortDescription() { return "SMOOTH PARCELS IN A VOLUME SEPARATELY"; } OperationParameters* AlgorithmVolumeParcelSmoothing::getParameters() { OperationParameters* ret = new OperationParameters(); ret->addVolumeParameter(1, "data-volume", "the volume to smooth"); ret->addVolumeParameter(2, "label-volume", "a label volume containing the parcels to smooth"); ret->addDoubleParameter(3, "kernel", "the gaussian smoothing kernel sigma, in mm"); ret->addVolumeOutputParameter(4, "volume-out", "the output volume"); ret->createOptionalParameter(5, "-fix-zeros", "treat zero values as not being data"); OptionalParameter* subvolSelect = ret->createOptionalParameter(6, "-subvolume", "select a single subvolume to smooth"); subvolSelect->addStringParameter(1, "subvol", "the subvolume number or name"); ret->setHelpText( AString("The volume is smoothed within each label in the label volume using data only from within the label. Equivalent to ") + "running volume smoothing with ROIs matching each label separately, then adding the resulting volumes, but faster." ); return ret; } void AlgorithmVolumeParcelSmoothing::useParameters(OperationParameters* myParams, ProgressObject* myProgObj) { VolumeFile* myVol = myParams->getVolume(1); VolumeFile* myLabelVol = myParams->getVolume(2); float myKernel = (float)myParams->getDouble(3); VolumeFile* myOutVol = myParams->getOutputVolume(4); OptionalParameter* fixZerosOpt = myParams->getOptionalParameter(5); bool fixZeros = fixZerosOpt->m_present; OptionalParameter* subvolSelect = myParams->getOptionalParameter(6); int subvolNum = -1; if (subvolSelect->m_present) { subvolNum = (int)myVol->getMapIndexFromNameOrNumber(subvolSelect->getString(1)); if (subvolNum < 0) { throw AlgorithmException("invalid subvolume specified"); } } AlgorithmVolumeParcelSmoothing(myProgObj, myVol, myLabelVol, myKernel, myOutVol, fixZeros, subvolNum); } AlgorithmVolumeParcelSmoothing::AlgorithmVolumeParcelSmoothing(ProgressObject* myProgObj, const VolumeFile* myVol, const VolumeFile* myLabelVol, const float& myKernel, VolumeFile* myOutVol, const bool& fixZeros, const int& subvolNum) : AbstractAlgorithm(myProgObj) { CaretAssert(myVol != NULL); CaretAssert(myOutVol != NULL); CaretAssert(myLabelVol != NULL); if (myLabelVol->getType() != SubvolumeAttributes::LABEL) { throw AlgorithmException("label volume doesn't contain label data"); } if (!myLabelVol->matchesVolumeSpace(myVol)) { throw AlgorithmException("input volume and label volume have different spacing"); } const GiftiLabelTable* myLabels = myLabelVol->getMapLabelTable(0); vector myKeys; myLabels->getKeys(myKeys); int numLabels = (int)myKeys.size(); map labelLookup;//reverse lookup, used to compact labels into the list of voxel lists for (int i = 0; i < numLabels; ++i) { labelLookup[myKeys[i]] = i; } int32_t unusedLabel = myLabels->getUnassignedLabelKey(); /*ProgressObject* subAlgProgress1 = NULL;//uncomment these if you use another algorithm inside here if (myProgObj != NULL) { subAlgProgress1 = myProgObj->addAlgorithm(AlgorithmInsertNameHere::getAlgorithmWeight());//TODO: set a vector of objects up via number of labels }//*/ LevelProgress myProgress(myProgObj);//this line sets the algorithm up to use the progress object, and will finish the progress object automatically when the algorithm terminates vector myDims; myVol->getDimensions(myDims); if (subvolNum < -1 || subvolNum >= myDims[3]) { throw AlgorithmException("invalid subvolume specified"); } vector > voxelLists;//build all lists in a single pass, allows some short circuits and less conversion to label integers voxelLists.resize(numLabels); for (int k = 0; k < myDims[2]; ++k) { for (int j = 0; j < myDims[1]; ++j) { for (int i = 0; i < myDims[0]; ++i) { int myValue = (int)floor(myLabelVol->getValue(i, j, k) + 0.5f); if (myValue == unusedLabel) continue;//skip the ??? label, whether it is included in the keys or not map::iterator search = labelLookup.find(myValue); if (search != labelLookup.end()) { int keyIndex = search->second; voxelLists[keyIndex].push_back(i); voxelLists[keyIndex].push_back(j); voxelLists[keyIndex].push_back(k); } } } } if (subvolNum == -1) { myOutVol->reinitialize(myVol->getOriginalDimensions(), myVol->getSform(), myDims[4], myVol->getType()); myOutVol->setValueAllVoxels(0.0f); for (int whichList = 0; whichList < numLabels; ++whichList) { const vector& thisList = voxelLists[whichList]; int64_t listSize = (int64_t)thisList.size(); if (listSize > 2)//NOTE: this should NEVER be something other than a multiple of 3, but check against what we will actually access anyway { int extrema[6] = { thisList[0], thisList[0], thisList[1], thisList[1], thisList[2], thisList[2] }; for (int64_t base = 3; base < listSize; base += 3) { if (thisList[base] < extrema[0]) extrema[0] = thisList[base]; if (thisList[base] > extrema[1]) extrema[1] = thisList[base]; if (thisList[base + 1] < extrema[2]) extrema[2] = thisList[base + 1]; if (thisList[base + 1] > extrema[3]) extrema[3] = thisList[base + 1]; if (thisList[base + 2] < extrema[4]) extrema[4] = thisList[base + 2]; if (thisList[base + 2] > extrema[5]) extrema[5] = thisList[base + 2]; } vector boxdims; boxdims.push_back(extrema[1] - extrema[0] + 1); boxdims.push_back(extrema[3] - extrema[2] + 1); boxdims.push_back(extrema[5] - extrema[4] + 1); VolumeFile roibox(boxdims, myVol->getSform()); boxdims.push_back(myDims[3]); VolumeFile inbox(boxdims, myVol->getSform(), myDims[4]), outbox; roibox.setValueAllVoxels(0.0f); for (int c = 0; c < myDims[4]; ++c) { for (int s = 0; s < myDims[3]; ++s) { for (int64_t base = 0; base < listSize; base += 3) { if (s == 0 && c == 0) roibox.setValue(1.0f, thisList[base] - extrema[0], thisList[base + 1] - extrema[2], thisList[base + 2] - extrema[4]); inbox.setValue(myVol->getValue(thisList[base], thisList[base + 1], thisList[base + 2], s, c), thisList[base] - extrema[0], thisList[base + 1] - extrema[2], thisList[base + 2] - extrema[4], s, c); } } } AlgorithmVolumeSmoothing(NULL, &inbox, myKernel, &outbox, &roibox, fixZeros); for (int c = 0; c < myDims[4]; ++c) { for (int s = 0; s < myDims[3]; ++s) { for (int64_t base = 0; base < listSize; base += 3) { myOutVol->setValue(outbox.getValue(thisList[base] - extrema[0], thisList[base + 1] - extrema[2], thisList[base + 2] - extrema[4], s, c), thisList[base], thisList[base + 1], thisList[base + 2], s, c); } } } } } for (int s = 0; s < myDims[3]; ++s) { myOutVol->setMapName(s, myVol->getMapName(s) + ", parcel smoothed " + AString::number(myKernel)); } } else { vector newDims = myVol->getOriginalDimensions(); newDims.resize(3);//discard non-spatial extra dimensions myOutVol->reinitialize(newDims, myVol->getSform(), myDims[4], myVol->getType());//keep components myOutVol->setValueAllVoxels(0.0f); for (int whichList = 0; whichList < numLabels; ++whichList) { const vector& thisList = voxelLists[whichList]; int64_t listSize = (int64_t)thisList.size(); if (listSize > 2)//NOTE: this should NEVER be something other than a multiple of 3, but check against what we will actually access anyway { int extrema[6] = { thisList[0], thisList[0], thisList[1], thisList[1], thisList[2], thisList[2] }; for (int64_t base = 3; base < listSize; base += 3) { if (thisList[base] < extrema[0]) extrema[0] = thisList[base]; if (thisList[base] > extrema[1]) extrema[1] = thisList[base]; if (thisList[base + 1] < extrema[2]) extrema[2] = thisList[base + 1]; if (thisList[base + 1] > extrema[3]) extrema[3] = thisList[base + 1]; if (thisList[base + 2] < extrema[4]) extrema[4] = thisList[base + 2]; if (thisList[base + 2] > extrema[5]) extrema[5] = thisList[base + 2]; } vector boxdims; boxdims.push_back(extrema[1] - extrema[0] + 1); boxdims.push_back(extrema[3] - extrema[2] + 1); boxdims.push_back(extrema[5] - extrema[4] + 1); VolumeFile inbox(boxdims, myVol->getSform(), myDims[4]), roibox(boxdims, myVol->getSform()), outbox; roibox.setValueAllVoxels(0.0f); for (int c = 0; c < myDims[4]; ++c) { for (int64_t base = 0; base < listSize; base += 3) { if (c == 0) roibox.setValue(1.0f, thisList[base] - extrema[0], thisList[base + 1] - extrema[2], thisList[base + 2] - extrema[4]); inbox.setValue(myVol->getValue(thisList[base], thisList[base + 1], thisList[base + 2], subvolNum, c), thisList[base] - extrema[0], thisList[base + 1] - extrema[2], thisList[base + 2] - extrema[4], 0, c); } } AlgorithmVolumeSmoothing(NULL, &inbox, myKernel, &outbox, &roibox, fixZeros); for (int c = 0; c < myDims[4]; ++c) { for (int64_t base = 0; base < listSize; base += 3) { myOutVol->setValue(outbox.getValue(thisList[base] - extrema[0], thisList[base + 1] - extrema[2], thisList[base + 2] - extrema[4], 0, c), thisList[base], thisList[base + 1], thisList[base + 2], 0, c); } } } } myOutVol->setMapName(0, myVol->getMapName(subvolNum) + ", parcel smoothed " + AString::number(myKernel)); } } float AlgorithmVolumeParcelSmoothing::getAlgorithmInternalWeight() { return 1.0f;//override this if needed, if the progress bar isn't smooth } float AlgorithmVolumeParcelSmoothing::getSubAlgorithmWeight() { //return AlgorithmInsertNameHere::getAlgorithmWeight();//if you use a subalgorithm return 0.0f; } connectome-workbench-1.2.3+git41-gc4c6c90/src/Algorithms/AlgorithmVolumeParcelSmoothing.h000066400000000000000000000034741300200146000311730ustar00rootroot00000000000000#ifndef __ALGORITHM_VOLUME_PARCEL_SMOOTHING_H__ #define __ALGORITHM_VOLUME_PARCEL_SMOOTHING_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 "AbstractAlgorithm.h" namespace caret { class AlgorithmVolumeParcelSmoothing : public AbstractAlgorithm { AlgorithmVolumeParcelSmoothing(); protected: static float getSubAlgorithmWeight(); static float getAlgorithmInternalWeight(); public: AlgorithmVolumeParcelSmoothing(ProgressObject* myProgObj, const VolumeFile* myVol, const VolumeFile* myLabelVol, const float& myKernel, VolumeFile* myOutVol, const bool& fixZeros = false, const int& subvolNum = -1); static OperationParameters* getParameters(); static void useParameters(OperationParameters* myParams, ProgressObject* myProgObj); static AString getCommandSwitch(); static AString getShortDescription(); }; typedef TemplateAutoOperation AutoAlgorithmVolumeParcelSmoothing; } #endif //__ALGORITHM_VOLUME_PARCEL_SMOOTHING_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Algorithms/AlgorithmVolumeROIsFromExtrema.cxx000066400000000000000000000420551300200146000314340ustar00rootroot00000000000000/*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 "AlgorithmVolumeROIsFromExtrema.h" #include "AlgorithmException.h" #include "FloatMatrix.h" #include "Vector3D.h" #include "VolumeFile.h" #include using namespace caret; using namespace std; AString AlgorithmVolumeROIsFromExtrema::getCommandSwitch() { return "-volume-rois-from-extrema"; } AString AlgorithmVolumeROIsFromExtrema::getShortDescription() { return "CREATE VOLUME ROI MAPS FROM EXTREMA MAPS"; } OperationParameters* AlgorithmVolumeROIsFromExtrema::getParameters() { OperationParameters* ret = new OperationParameters(); ret->addVolumeParameter(1, "volume-in", "the input volume"); ret->addDoubleParameter(2, "limit", "distance limit from voxel center, in mm"); ret->addVolumeOutputParameter(3, "volume-out", "the output volume"); OptionalParameter* gaussOpt = ret->createOptionalParameter(4, "-gaussian", "generate a gaussian kernel instead of a flat ROI"); gaussOpt->addDoubleParameter(1, "sigma", "the sigma for the gaussian kernel, in mm"); OptionalParameter* roiOption = ret->createOptionalParameter(5, "-roi", "select a region of interest to use"); roiOption->addVolumeParameter(1, "roi-volume", "the region to use"); OptionalParameter* overlapOpt = ret->createOptionalParameter(6, "-overlap-logic", "how to handle overlapping ROIs, default ALLOW"); overlapOpt->addStringParameter(1, "method", "the method of resolving overlaps"); OptionalParameter* subvolSelect = ret->createOptionalParameter(7, "-subvolume", "select a single subvolume to take the gradient of"); subvolSelect->addStringParameter(1, "subvol", "the subvolume number or name"); ret->setHelpText( AString("For each nonzero value in each map, make a map with an ROI around that location. ") + "If the -gaussian option is specified, then normalized gaussian kernels are output instead of ROIs. " + "The argument to -overlap-logic must be one of ALLOW, CLOSEST, or EXCLUDE. " + "ALLOW is the default, and means that ROIs are treated independently and may overlap. " + "CLOSEST means that ROIs may not overlap, and that no ROI contains vertices that are closer to a different seed vertex. " + "EXCLUDE means that ROIs may not overlap, and that any vertex within range of more than one ROI does not belong to any ROI." ); return ret; } void AlgorithmVolumeROIsFromExtrema::useParameters(OperationParameters* myParams, ProgressObject* myProgObj) { VolumeFile* myVol = myParams->getVolume(1); float limit = (float)myParams->getDouble(2); VolumeFile* myVolOut = myParams->getOutputVolume(3); float sigma = -1.0f; OptionalParameter* gaussOpt = myParams->getOptionalParameter(4); if (gaussOpt->m_present) { sigma = (float)gaussOpt->getDouble(1); if (sigma <= 0.0f) { throw AlgorithmException("invalid sigma specified"); } } VolumeFile* myRoi = NULL; OptionalParameter* roiOption = myParams->getOptionalParameter(5); if (roiOption->m_present) { myRoi = roiOption->getVolume(1); } OverlapLogicEnum::Enum overlapType = OverlapLogicEnum::ALLOW; OptionalParameter* overlapOpt = myParams->getOptionalParameter(6); if (overlapOpt->m_present) { bool ok = false; overlapType = OverlapLogicEnum::fromName(overlapOpt->getString(1), &ok); if (!ok) throw AlgorithmException("unrecognized overlap method: " + overlapOpt->getString(1)); } OptionalParameter* subvolSelect = myParams->getOptionalParameter(7); int subvolNum = -1; if (subvolSelect->m_present) { subvolNum = (int)myVol->getMapIndexFromNameOrNumber(subvolSelect->getString(1)); if (subvolNum < 0) { throw AlgorithmException("invalid subvolume specified"); } } AlgorithmVolumeROIsFromExtrema(myProgObj, myVol, limit, myVolOut, sigma, myRoi, overlapType, subvolNum); } AlgorithmVolumeROIsFromExtrema::AlgorithmVolumeROIsFromExtrema(ProgressObject* myProgObj, const VolumeFile* myVol, const float& limit, VolumeFile* myVolOut, const float& sigma, const VolumeFile* myRoi, const OverlapLogicEnum::Enum& overlapType, const int& subvolNum) : AbstractAlgorithm(myProgObj) { LevelProgress myProgress(myProgObj); const float* roiFrame = NULL; if (myRoi != NULL) { if (!myRoi->matchesVolumeSpace(myVol)) throw AlgorithmException("roi volume has a different volume space"); roiFrame = myRoi->getFrame(); } int64_t extremaCount = 0; vector myDims; myVol->getDimensions(myDims); int64_t frameSize = myDims[0] * myDims[1] * myDims[2]; if (subvolNum == -1) { if (roiFrame == NULL) { for (int c = 0; c < myDims[4]; ++c) { for (int b = 0; b < myDims[3]; ++b) { const float* frame = myVol->getFrame(b, c); for (int64_t index = 0; index < frameSize; ++index) { if (frame[index] != 0.0f) ++extremaCount; } } } } else { for (int c = 0; c < myDims[4]; ++c) { for (int b = 0; b < myDims[3]; ++b) { const float* frame = myVol->getFrame(b, c); for (int64_t index = 0; index < frameSize; ++index) { if (roiFrame[index] > 0.0f && frame[index] != 0.0f) ++extremaCount; } } } } } else { if (roiFrame == NULL) { for (int c = 0; c < myDims[4]; ++c) { const float* frame = myVol->getFrame(subvolNum, c); for (int64_t index = 0; index < frameSize; ++index) { if (frame[index] != 0.0f) ++extremaCount; } } } else { for (int c = 0; c < myDims[4]; ++c) { const float* frame = myVol->getFrame(subvolNum, c); for (int64_t index = 0; index < frameSize; ++index) { if (roiFrame[index] > 0.0f && frame[index] != 0.0f) ++extremaCount; } } } } Vector3D ivec, jvec, kvec, offset, ijorth, jkorth, kiorth; FloatMatrix(myVol->getVolumeSpace().getSform()).getAffineVectors(ivec, jvec, kvec, offset);//this should probably be made more accessible ijorth = ivec.cross(jvec).normal();//find the bounding box that encloses a sphere of radius limit jkorth = jvec.cross(kvec).normal(); kiorth = kvec.cross(ivec).normal(); int irange = (int)floor(abs(limit / ivec.dot(jkorth))); int jrange = (int)floor(abs(limit / jvec.dot(kiorth))); int krange = (int)floor(abs(limit / kvec.dot(ijorth))); Vector3D iscratch, jscratch, kscratch; vector stencil; vector stencildist; for (int k = -krange; k <= krange; ++k) { kscratch = kvec * k; for (int j = -jrange; j <= jrange; ++j) { jscratch = kscratch + jvec * j; for (int i = -irange; i <= irange; ++i) { iscratch = jscratch + ivec * i; float tempf = iscratch.length(); if (tempf <= limit) { stencil.push_back(i); stencil.push_back(j); stencil.push_back(k); stencildist.push_back(tempf); } } } } vector excludeDists(frameSize, -1.0f); vector excludeSources(frameSize, -1); vector > roiLists(extremaCount); int64_t mapCounter = 0; if (subvolNum == -1) { for (int c = 0; c < myDims[4]; ++c) { for (int b = 0; b < myDims[3]; ++b) { const float* data = myVol->getFrame(b, c); processFrame(data, excludeDists, excludeSources, roiLists, mapCounter, stencil, stencildist, roiFrame, overlapType, myVol->getVolumeSpace()); } } } else { for (int c = 0; c < myDims[4]; ++c) { const float* data = myVol->getFrame(subvolNum, c); processFrame(data, excludeDists, excludeSources, roiLists, mapCounter, stencil, stencildist, roiFrame, overlapType, myVol->getVolumeSpace()); } } CaretAssert(mapCounter == extremaCount); vector outDims = myDims; outDims.resize(4); outDims[3] = extremaCount; myVolOut->reinitialize(outDims, myVol->getSform()); vector tempFrame(frameSize, 0.0f); if (sigma > 0.0f) { float gaussDenom = -0.5f / sigma / sigma; for (int64_t i = 0; i < mapCounter; ++i) { double accum = 0.0; map::iterator myEnd = roiLists[i].end(); for (map::iterator iter = roiLists[i].begin(); iter != myEnd; ++iter) { float gaussVal = exp(iter->second * iter->second * gaussDenom); accum += gaussVal; tempFrame[iter->first] = gaussVal;//initial kernel value } for (map::iterator iter = roiLists[i].begin(); iter != myEnd; ++iter) { tempFrame[iter->first] /= accum;//normalize } myVolOut->setFrame(tempFrame.data(), i); for (map::iterator iter = roiLists[i].begin(); iter != myEnd; ++iter) { tempFrame[iter->first] = 0.0f;//rezero changed values for next map } } } else { for (int64_t i = 0; i < mapCounter; ++i) { map::iterator myEnd = roiLists[i].end(); for (map::iterator iter = roiLists[i].begin(); iter != myEnd; ++iter) { tempFrame[iter->first] = 1.0f;//make roi } myVolOut->setFrame(tempFrame.data(), i); for (map::iterator iter = roiLists[i].begin(); iter != myEnd; ++iter) { tempFrame[iter->first] = 0.0f;//rezero changed values for next map } } } } void AlgorithmVolumeROIsFromExtrema::processFrame(const float* data, vector& excludeDists, vector& excludeSources, vector >& roiLists, int64_t& mapCounter, const vector& stencil, const vector& stencildist, const float* roiFrame, const OverlapLogicEnum::Enum& overlapType, const VolumeSpace& mySpace) { const int64_t* myDims = mySpace.getDims(); int stencilSize = (int)stencildist.size(); for (int k = 0; k < myDims[2]; ++k) { for (int j = 0; j < myDims[1]; ++j) { for (int i = 0; i < myDims[0]; ++i) { int64_t myIndex = mySpace.getIndex(i, j, k); if ((roiFrame == NULL || roiFrame[myIndex] > 0.0f) && data[myIndex] != 0.0f) { switch (overlapType) { case OverlapLogicEnum::ALLOW: if (roiFrame == NULL) { for (int s = 0; s < stencilSize; ++s) { int s3 = s * 3; const int thisVoxel[3] = { i + stencil[s3], j + stencil[s3 + 1], k + stencil[s3 + 2] }; if (mySpace.indexValid(thisVoxel)) { roiLists[mapCounter][mySpace.getIndex(thisVoxel)] = stencildist[s]; } } } else { for (int s = 0; s < stencilSize; ++s) { int s3 = s * 3; const int thisVoxel[3] = { i + stencil[s3], j + stencil[s3 + 1], k + stencil[s3 + 2] }; if (mySpace.indexValid(thisVoxel) && roiFrame[mySpace.getIndex(thisVoxel)] > 0.0f) { roiLists[mapCounter][mySpace.getIndex(thisVoxel)] = stencildist[s]; } } } break; case OverlapLogicEnum::CLOSEST: for (int s = 0; s < stencilSize; ++s) { int s3 = s * 3; const int thisVoxel[3] = { i + stencil[s3], j + stencil[s3 + 1], k + stencil[s3 + 2] }; if (mySpace.indexValid(thisVoxel) && (roiFrame == NULL || roiFrame[mySpace.getIndex(thisVoxel)] > 0.0f)) { int64_t thisIndex = mySpace.getIndex(thisVoxel); const float& thisDist = stencildist[s]; roiLists[mapCounter][thisIndex] = thisDist; if (excludeDists[thisIndex] < 0.0f) { excludeDists[thisIndex] = thisDist; excludeSources[thisIndex] = mapCounter; roiLists[mapCounter][thisIndex] = thisDist; } else { if (excludeDists[thisIndex] > thisDist) { roiLists[excludeSources[thisIndex]].erase(thisIndex); } excludeDists[thisIndex] = thisDist; excludeSources[thisIndex] = mapCounter; roiLists[mapCounter][thisIndex] = thisDist; } } } break; case OverlapLogicEnum::EXCLUDE: for (int s = 0; s < stencilSize; ++s) { int s3 = s * 3; const int thisVoxel[3] = { i + stencil[s3], j + stencil[s3 + 1], k + stencil[s3 + 2] }; if (mySpace.indexValid(thisVoxel) && (roiFrame == NULL || roiFrame[mySpace.getIndex(thisVoxel)] > 0.0f)) { int64_t thisIndex = mySpace.getIndex(thisVoxel); const float& thisDist = stencildist[s]; roiLists[mapCounter][thisIndex] = thisDist; if (excludeDists[thisIndex] < 0.0f) { excludeDists[thisIndex] = thisDist; excludeSources[thisIndex] = mapCounter; roiLists[mapCounter][thisIndex] = thisDist; } else { if (excludeSources[thisIndex] != -1) { roiLists[excludeSources[thisIndex]].erase(thisIndex); excludeSources[thisIndex] = -1; } } } } break; } ++mapCounter; } } } } } float AlgorithmVolumeROIsFromExtrema::getAlgorithmInternalWeight() { return 1.0f;//override this if needed, if the progress bar isn't smooth } float AlgorithmVolumeROIsFromExtrema::getSubAlgorithmWeight() { //return AlgorithmInsertNameHere::getAlgorithmWeight();//if you use a subalgorithm return 0.0f; } connectome-workbench-1.2.3+git41-gc4c6c90/src/Algorithms/AlgorithmVolumeROIsFromExtrema.h000066400000000000000000000046271300200146000310640ustar00rootroot00000000000000#ifndef __ALGORITHM_VOLUME_ROIS_FROM_EXTREMA_H__ #define __ALGORITHM_VOLUME_ROIS_FROM_EXTREMA_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 "AbstractAlgorithm.h" #include "OverlapLogicEnum.h" #include "VolumeSpace.h" #include #include namespace caret { class AlgorithmVolumeROIsFromExtrema : public AbstractAlgorithm { AlgorithmVolumeROIsFromExtrema(); void processFrame(const float* data, std::vector& excludeDists, std::vector& excludeSouces, std::vector >& roiLists, int64_t& mapCounter, const std::vector& stencil, const std::vector& stencildist, const float* roiFrame, const OverlapLogicEnum::Enum& overlapType, const VolumeSpace& myDims); protected: static float getSubAlgorithmWeight(); static float getAlgorithmInternalWeight(); public: AlgorithmVolumeROIsFromExtrema(ProgressObject* myProgObj, const VolumeFile* myVol, const float& limit, VolumeFile* myVolOut, const float& sigma = -1.0f, const VolumeFile* myRoi = NULL, const OverlapLogicEnum::Enum& overlapType = OverlapLogicEnum::ALLOW, const int& subvolNum = -1); static OperationParameters* getParameters(); static void useParameters(OperationParameters* myParams, ProgressObject* myProgObj); static AString getCommandSwitch(); static AString getShortDescription(); }; typedef TemplateAutoOperation AutoAlgorithmVolumeROIsFromExtrema; } #endif //__ALGORITHM_VOLUME_ROIS_FROM_EXTREMA_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Algorithms/AlgorithmVolumeReduce.cxx000066400000000000000000000147131300200146000276550ustar00rootroot00000000000000/*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 "AlgorithmVolumeReduce.h" #include "AlgorithmException.h" #include "CaretLogger.h" #include "GiftiLabelTable.h" #include "ReductionOperation.h" #include "VolumeFile.h" #include using namespace caret; using namespace std; AString AlgorithmVolumeReduce::getCommandSwitch() { return "-volume-reduce"; } AString AlgorithmVolumeReduce::getShortDescription() { return "PERFORM REDUCTION OPERATION ACROSS SUBVOLUMES"; } OperationParameters* AlgorithmVolumeReduce::getParameters() { OperationParameters* ret = new OperationParameters(); ret->addVolumeParameter(1, "volume-in", "the volume file to reduce"); ret->addStringParameter(2, "operation", "the reduction operator to use"); ret->addVolumeOutputParameter(3, "volume-out", "the output volume"); OptionalParameter* excludeOpt = ret->createOptionalParameter(4, "-exclude-outliers", "exclude non-numeric values and outliers by standard deviation"); excludeOpt->addDoubleParameter(1, "sigma-below", "number of standard deviations below the mean to include"); excludeOpt->addDoubleParameter(2, "sigma-above", "number of standard deviations above the mean to include"); ret->createOptionalParameter(5, "-only-numeric", "exclude non-numeric values"); ret->setHelpText( AString("For each voxel, takes the data across subvolumes as a vector, and performs the specified reduction on it, putting the result ") + "into the single output volume at that voxel. The reduction operators are as follows:\n\n" + ReductionOperation::getHelpInfo() ); return ret; } void AlgorithmVolumeReduce::useParameters(OperationParameters* myParams, ProgressObject* myProgObj) { VolumeFile* volumeIn = myParams->getVolume(1); AString opString = myParams->getString(2); VolumeFile* volumeOut = myParams->getOutputVolume(3); OptionalParameter* excludeOpt = myParams->getOptionalParameter(4); bool onlyNumeric = myParams->getOptionalParameter(5)->m_present; bool ok = false; ReductionEnum::Enum myReduce = ReductionEnum::fromName(opString, &ok); if (!ok) throw AlgorithmException("unrecognized operation string '" + opString + "'"); if (excludeOpt->m_present) { if (onlyNumeric) CaretLogWarning("-only-numeric is redundant when -exclude-outliers is specified"); AlgorithmVolumeReduce(myProgObj, volumeIn, myReduce, volumeOut, excludeOpt->getDouble(1), excludeOpt->getDouble(2)); } else { AlgorithmVolumeReduce(myProgObj, volumeIn, myReduce, volumeOut, onlyNumeric); } } AlgorithmVolumeReduce::AlgorithmVolumeReduce(ProgressObject* myProgObj, const VolumeFile* volumeIn, const ReductionEnum::Enum& myReduce, VolumeFile* volumeOut, const bool& onlyNumeric) : AbstractAlgorithm(myProgObj) { LevelProgress myProgress(myProgObj); vector myDims, newDims = volumeIn->getOriginalDimensions(); newDims.resize(3, 1);//have only one subvolume volumeIn->getDimensions(myDims); volumeOut->reinitialize(newDims, volumeIn->getSform(), myDims[4], volumeIn->getType()); if (volumeIn->getType() == SubvolumeAttributes::LABEL) { CaretLogWarning("reduction operation performed on label volume"); *(volumeOut->getMapLabelTable(0)) = *(volumeIn->getMapLabelTable(0)); } int64_t frameSize = myDims[0] * myDims[1] * myDims[2]; vector scratchArray(myDims[3]), outFrame(frameSize); for (int c = 0; c < myDims[4]; ++c) { for (int64_t i = 0; i < frameSize; ++i) { for (int b = 0; b < myDims[3]; ++b) { const float* tempFrame = volumeIn->getFrame(b, c); scratchArray[b] = tempFrame[i]; } if (onlyNumeric) { outFrame[i] = ReductionOperation::reduceOnlyNumeric(scratchArray.data(), myDims[3], myReduce); } else { outFrame[i] = ReductionOperation::reduce(scratchArray.data(), myDims[3], myReduce); } } volumeOut->setFrame(outFrame.data(), 0, c); } } AlgorithmVolumeReduce::AlgorithmVolumeReduce(ProgressObject* myProgObj, const VolumeFile* volumeIn, const ReductionEnum::Enum& myReduce, VolumeFile* volumeOut, const float& sigmaBelow, const float& sigmaAbove) : AbstractAlgorithm(myProgObj) { LevelProgress myProgress(myProgObj); vector myDims, newDims = volumeIn->getOriginalDimensions(); newDims.resize(3, 1);//have only one subvolume volumeIn->getDimensions(myDims); volumeOut->reinitialize(newDims, volumeIn->getSform(), myDims[4], volumeIn->getType()); if (volumeIn->getType() == SubvolumeAttributes::LABEL) { CaretLogWarning("reduction operation performed on label volume"); *(volumeOut->getMapLabelTable(0)) = *(volumeIn->getMapLabelTable(0)); } int64_t frameSize = myDims[0] * myDims[1] * myDims[2]; vector scratchArray(myDims[3]), outFrame(frameSize); for (int c = 0; c < myDims[4]; ++c) { for (int64_t i = 0; i < frameSize; ++i) { for (int b = 0; b < myDims[3]; ++b) { const float* tempFrame = volumeIn->getFrame(b, c); scratchArray[b] = tempFrame[i]; } outFrame[i] = ReductionOperation::reduceExcludeDev(scratchArray.data(), myDims[3], myReduce, sigmaBelow, sigmaAbove); } volumeOut->setFrame(outFrame.data(), 0, c); } } float AlgorithmVolumeReduce::getAlgorithmInternalWeight() { return 1.0f;//override this if needed, if the progress bar isn't smooth } float AlgorithmVolumeReduce::getSubAlgorithmWeight() { //return AlgorithmInsertNameHere::getAlgorithmWeight();//if you use a subalgorithm return 0.0f; } connectome-workbench-1.2.3+git41-gc4c6c90/src/Algorithms/AlgorithmVolumeReduce.h000066400000000000000000000036541300200146000273040ustar00rootroot00000000000000#ifndef __ALGORITHM_VOLUME_REDUCE_H__ #define __ALGORITHM_VOLUME_REDUCE_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 "AbstractAlgorithm.h" #include "ReductionEnum.h" namespace caret { class AlgorithmVolumeReduce : public AbstractAlgorithm { AlgorithmVolumeReduce(); protected: static float getSubAlgorithmWeight(); static float getAlgorithmInternalWeight(); public: AlgorithmVolumeReduce(ProgressObject* myProgObj, const VolumeFile* volumeIn, const ReductionEnum::Enum& myReduce, VolumeFile* volumeOut, const bool& onlyNumeric = false); AlgorithmVolumeReduce(ProgressObject* myProgObj, const VolumeFile* volumeIn, const ReductionEnum::Enum& myReduce, VolumeFile* volumeOut, const float& sigmaBelow, const float& sigmaAbove); static OperationParameters* getParameters(); static void useParameters(OperationParameters* myParams, ProgressObject* myProgObj); static AString getCommandSwitch(); static AString getShortDescription(); }; typedef TemplateAutoOperation AutoAlgorithmVolumeReduce; } #endif //__ALGORITHM_VOLUME_REDUCE_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Algorithms/AlgorithmVolumeRemoveIslands.cxx000066400000000000000000000154361300200146000312240ustar00rootroot00000000000000/*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 "AlgorithmVolumeRemoveIslands.h" #include "AlgorithmException.h" #include "VolumeFile.h" #include using namespace caret; using namespace std; AString AlgorithmVolumeRemoveIslands::getCommandSwitch() { return "-volume-remove-islands"; } AString AlgorithmVolumeRemoveIslands::getShortDescription() { return "REMOVE ISLANDS FROM AN ROI VOLUME"; } OperationParameters* AlgorithmVolumeRemoveIslands::getParameters() { OperationParameters* ret = new OperationParameters(); ret->addVolumeParameter(1, "volume-in", "the input ROI volume"); ret->addVolumeOutputParameter(2, "volume-out", "the output ROI volume"); ret->setHelpText( AString("Finds all face-connected parts of the ROI, and zeros out all but the largest one.") ); return ret; } void AlgorithmVolumeRemoveIslands::useParameters(OperationParameters* myParams, ProgressObject* myProgObj) { VolumeFile* myVolIn = myParams->getVolume(1); VolumeFile* myVolOut = myParams->getOutputVolume(2); AlgorithmVolumeRemoveIslands(myProgObj, myVolIn, myVolOut); } AlgorithmVolumeRemoveIslands::AlgorithmVolumeRemoveIslands(ProgressObject* myProgObj, const VolumeFile* myVolIn, VolumeFile* myVolOut) : AbstractAlgorithm(myProgObj) { LevelProgress myProgress(myProgObj); const int STENCIL_SIZE = 18;//the easy way, and prepare for different stencils if we ever need them const int stencil[STENCIL_SIZE] = { 0, 0, -1, 0, -1, 0, -1, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1 }; vector dims; myVolIn->getDimensions(dims); myVolOut->reinitialize(myVolIn->getOriginalDimensions(), myVolIn->getSform(), myVolIn->getNumberOfComponents(), myVolIn->getType()); for (int s = 0; s < dims[3]; ++s) { myVolOut->setMapName(s, myVolIn->getMapName(s)); for (int c = 0; c < dims[4]; ++c) { int64_t ijk[3]; vector > parts; vector used(dims[0] * dims[1] * dims[2], 0); const float* frame = myVolIn->getFrame(s, c); for (ijk[2] = 0; ijk[2] < dims[2]; ++ijk[2]) { for (ijk[1] = 0; ijk[1] < dims[1]; ++ijk[1]) { for (ijk[0] = 0; ijk[0] < dims[0]; ++ijk[0]) { int64_t index = myVolIn->getIndex(ijk); if (used[index] == 0 && frame[index] > 0.0f) { parts.push_back(vector()); vector& thispart = parts.back(); thispart.push_back(ijk[0]); thispart.push_back(ijk[1]); thispart.push_back(ijk[2]); used[index] = 1; vector mystack; mystack.push_back(ijk[0]); mystack.push_back(ijk[1]); mystack.push_back(ijk[2]); while (!mystack.empty()) { int64_t curSize = (int64_t)mystack.size(); int64_t curvox[3] = { mystack[curSize - 3], mystack[curSize - 2], mystack[curSize - 1] }; mystack.resize(curSize - 3);//basically pop_back for (int i = 0; i < STENCIL_SIZE; i += 3) { int64_t neighbor[3] = { curvox[0] + stencil[i], curvox[1] + stencil[i + 1], curvox[2] + stencil[i + 2] }; if (myVolIn->indexValid(neighbor)) { int64_t neighindex = myVolIn->getIndex(neighbor); if (used[neighindex] == 0 && frame[neighindex] > 0.0f) { thispart.push_back(neighbor[0]); thispart.push_back(neighbor[1]); thispart.push_back(neighbor[2]); used[neighindex] = 1; mystack.push_back(neighbor[0]); mystack.push_back(neighbor[1]); mystack.push_back(neighbor[2]); } } } } } } } } int64_t bestCount = -1, bestPart = -1, numParts = (int64_t)parts.size(); for (int64_t i = 0; i < numParts; ++i) { int64_t thisCount = (int64_t)parts[i].size(); if (thisCount > bestCount) { bestCount = thisCount; bestPart = i; } } vector outFrame(dims[0] * dims[1] * dims[2], 0.0f); if (bestPart != -1) { vector& myPart = parts[bestPart]; for (int64_t i = 0; i < bestCount; i += 3) { int64_t myIndex = myVolIn->getIndex(myPart.data() + i); outFrame[myIndex] = 1.0f;//make it a simple 0/1 volume, even if it wasn't before } } myVolOut->setFrame(outFrame.data(), s, c); } } } float AlgorithmVolumeRemoveIslands::getAlgorithmInternalWeight() { return 1.0f;//override this if needed, if the progress bar isn't smooth } float AlgorithmVolumeRemoveIslands::getSubAlgorithmWeight() { //return AlgorithmInsertNameHere::getAlgorithmWeight();//if you use a subalgorithm return 0.0f; } connectome-workbench-1.2.3+git41-gc4c6c90/src/Algorithms/AlgorithmVolumeRemoveIslands.h000066400000000000000000000033001300200146000306340ustar00rootroot00000000000000#ifndef __ALGORITHM_VOLUME_REMOVE_ISLANDS_H__ #define __ALGORITHM_VOLUME_REMOVE_ISLANDS_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 "AbstractAlgorithm.h" namespace caret { class AlgorithmVolumeRemoveIslands : public AbstractAlgorithm { AlgorithmVolumeRemoveIslands(); protected: static float getSubAlgorithmWeight(); static float getAlgorithmInternalWeight(); public: AlgorithmVolumeRemoveIslands(ProgressObject* myProgObj, const VolumeFile* myVolIn, VolumeFile* myVolOut); static OperationParameters* getParameters(); static void useParameters(OperationParameters* myParams, ProgressObject* myProgObj); static AString getCommandSwitch(); static AString getShortDescription(); }; typedef TemplateAutoOperation AutoAlgorithmVolumeRemoveIslands; } #endif //__ALGORITHM_VOLUME_REMOVE_ISLANDS_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Algorithms/AlgorithmVolumeSmoothing.cxx000066400000000000000000000772151300200146000304230ustar00rootroot00000000000000/*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 "AlgorithmVolumeSmoothing.h" #include "AlgorithmException.h" #include "VolumeFile.h" #include "Vector3D.h" #include "CaretLogger.h" #include "CaretOMP.h" #include "CaretAssert.h" #include using namespace caret; using namespace std; //makes the program issue warning only once per launch, prevents repeated calls by other algorithms from spamming bool AlgorithmVolumeSmoothing::haveWarned = false; AString AlgorithmVolumeSmoothing::getCommandSwitch() { return "-volume-smoothing"; } AString AlgorithmVolumeSmoothing::getShortDescription() { return "SMOOTH A VOLUME FILE"; } OperationParameters* AlgorithmVolumeSmoothing::getParameters() { OperationParameters* ret = new OperationParameters(); ret->addVolumeParameter(1, "volume-in", "the volume to smooth"); ret->addDoubleParameter(2, "kernel", "the gaussian smoothing kernel sigma, in mm"); ret->addVolumeOutputParameter(3, "volume-out", "the output volume"); OptionalParameter* roiVolOpt = ret->createOptionalParameter(4, "-roi", "smooth only from data within an ROI"); roiVolOpt->addVolumeParameter(1, "roivol", "the volume to use as an ROI"); ret->createOptionalParameter(5, "-fix-zeros", "treat zero values as not being data"); OptionalParameter* subvolSelect = ret->createOptionalParameter(6, "-subvolume", "select a single subvolume to smooth"); subvolSelect->addStringParameter(1, "subvol", "the subvolume number or name"); ret->setHelpText( AString("Gaussian smoothing for volumes. By default, smooths all subvolumes with no ROI, if ROI is given, only ") + "positive voxels in the ROI volume have their values used, and all other voxels are set to zero. Smoothing a non-orthogonal volume will " + "be significantly slower, because the operation cannot be separated into 1-dimensional smoothings without distorting the kernel shape.\n\n" + "The -fix-zeros option causes the smoothing to not use an input value if it is zero, but still write a smoothed value to the voxel. " + "This is useful for zeros that indicate lack of information, preventing them from pulling down the intensity of nearby voxels, while " + "giving the zero an extrapolated value." ); return ret; } void AlgorithmVolumeSmoothing::useParameters(OperationParameters* myParams, ProgressObject* myProgObj) { VolumeFile* myVol = myParams->getVolume(1); float myKernel = (float)myParams->getDouble(2); VolumeFile* myOutVol = myParams->getOutputVolume(3); OptionalParameter* roiVolOpt = myParams->getOptionalParameter(4); VolumeFile* roiVol = NULL; if (roiVolOpt->m_present) { roiVol = roiVolOpt->getVolume(1); } OptionalParameter* fixZerosOpt = myParams->getOptionalParameter(5); bool fixZeros = fixZerosOpt->m_present; OptionalParameter* subvolSelect = myParams->getOptionalParameter(6); int subvolNum = -1; if (subvolSelect->m_present) { subvolNum = (int)myVol->getMapIndexFromNameOrNumber(subvolSelect->getString(1)); if (subvolNum < 0) { throw AlgorithmException("invalid subvolume specified"); } } AlgorithmVolumeSmoothing(myProgObj, myVol, myKernel, myOutVol, roiVol, fixZeros, subvolNum); } AlgorithmVolumeSmoothing::AlgorithmVolumeSmoothing(ProgressObject* myProgObj, const VolumeFile* inVol, const float& kernel, VolumeFile* outVol, const VolumeFile* roiVol, const bool& fixZeros, const int& subvol) : AbstractAlgorithm(myProgObj) { CaretAssert(inVol != NULL); CaretAssert(outVol != NULL); LevelProgress myProgress(myProgObj); if (roiVol != NULL && !inVol->matchesVolumeSpace(roiVol)) { throw AlgorithmException("volume roi space does not match input volume"); } vector myDims; inVol->getDimensions(myDims); if (subvol < -1 || subvol >= myDims[3]) { throw AlgorithmException("invalid subvolume specified"); } if (kernel <= 0.0f) { throw AlgorithmException("kernel too small"); } CaretArray scratchFrame(myDims[0] * myDims[1] * myDims[2]);//it could be faster to preinitialize with zeros, then generate a usable voxels list if there is a small ROI... float kernBox = kernel * 3.0f; vector > volSpace = inVol->getSform(); Vector3D ivec, jvec, kvec, origin, ijorth, jkorth, kiorth; ivec[0] = volSpace[0][0]; jvec[0] = volSpace[0][1]; kvec[0] = volSpace[0][2]; origin[0] = volSpace[0][3]; ivec[1] = volSpace[1][0]; jvec[1] = volSpace[1][1]; kvec[1] = volSpace[1][2]; origin[1] = volSpace[1][3]; ivec[2] = volSpace[2][0]; jvec[2] = volSpace[2][1]; kvec[2] = volSpace[2][2]; origin[2] = volSpace[2][3]; const float ORTH_TOLERANCE = 0.001f;//tolerate this much deviation from orthogonal (dot product divided by product of lengths) to use orthogonal assumptions to smooth if (abs(ivec.dot(jvec.normal())) / ivec.length() < ORTH_TOLERANCE && abs(jvec.dot(kvec.normal())) / jvec.length() < ORTH_TOLERANCE && abs(kvec.dot(ivec.normal())) / kvec.length() < ORTH_TOLERANCE) {//if our axes are orthogonal, optimize by doing three 1-dimensional smoothings for O(voxels * (ki + kj + kk)) instead of O(voxels * (ki * kj * kk)) CaretArray scratchFrame2(myDims[0] * myDims[1] * myDims[2]), scratchWeights(myDims[0] * myDims[1] * myDims[2]), scratchWeights2(myDims[0] * myDims[1] * myDims[2]), scratchFrame3; if (roiVol != NULL) { scratchFrame3 = CaretArray (myDims[0] * myDims[1] * myDims[2]); } float ispace = ivec.length(), jspace = jvec.length(), kspace = kvec.length(); int irange = (int)floor(kernBox / ispace); int jrange = (int)floor(kernBox / jspace); int krange = (int)floor(kernBox / kspace); if (irange < 1) irange = 1;//don't underflow if (jrange < 1) jrange = 1; if (krange < 1) krange = 1; int isize = irange * 2 + 1;//and construct a precomputed kernel in the box int jsize = jrange * 2 + 1; int ksize = krange * 2 + 1; CaretArray iweights(isize), jweights(jsize), kweights(ksize); for (int i = 0; i < isize; ++i) { float tempf = ispace * (i - irange) / kernel; iweights[i] = exp(-tempf * tempf / 2.0f); } for (int j = 0; j < jsize; ++j) { float tempf = jspace * (j - jrange) / kernel; jweights[j] = exp(-tempf * tempf / 2.0f); } for (int k = 0; k < ksize; ++k) { float tempf = kspace * (k - krange) / kernel; kweights[k] = exp(-tempf * tempf / 2.0f); } if (subvol == -1) { vector origDims = inVol->getOriginalDimensions(); outVol->reinitialize(origDims, volSpace, myDims[4]); vector lists[3]; for (int s = 0; s < myDims[3]; ++s) { outVol->setMapName(s, inVol->getMapName(s) + ", smooth " + AString::number(kernel)); for (int c = 0; c < myDims[4]; ++c) { const float* inFrame = inVol->getFrame(s, c); if (roiVol == NULL) { smoothFrame(inFrame, myDims, scratchFrame, scratchFrame2, scratchWeights, scratchWeights2, inVol, iweights, jweights, kweights, irange, jrange, krange, fixZeros); } else { smoothFrameROI(inFrame, myDims, scratchFrame, scratchFrame2, scratchFrame3, scratchWeights, scratchWeights2, lists, inVol, roiVol, iweights, jweights, kweights, irange, jrange, krange, fixZeros); } outVol->setFrame(scratchFrame, s, c); } } } else { vector origDims = inVol->getOriginalDimensions(), newDims; newDims.resize(3); newDims[0] = origDims[0]; newDims[1] = origDims[1]; newDims[2] = origDims[2]; outVol->reinitialize(newDims, volSpace, myDims[4]); vector lists[3]; outVol->setMapName(0, inVol->getMapName(subvol) + ", smooth " + AString::number(kernel)); for (int c = 0; c < myDims[4]; ++c) { const float* inFrame = inVol->getFrame(subvol, c); if (roiVol == NULL) { smoothFrame(inFrame, myDims, scratchFrame, scratchFrame2, scratchWeights, scratchWeights2, inVol, iweights, jweights, kweights, irange, jrange, krange, fixZeros); } else { smoothFrameROI(inFrame, myDims, scratchFrame, scratchFrame2, scratchFrame3, scratchWeights, scratchWeights2, lists, inVol, roiVol, iweights, jweights, kweights, irange, jrange, krange, fixZeros); } outVol->setFrame(scratchFrame, 0, c); } } } else { if (!haveWarned) { CaretLogWarning("input volume is not orthogonal, smoothing will take longer"); haveWarned = true; } ijorth = ivec.cross(jvec).normal();//find the bounding box that encloses a sphere of radius kernBox jkorth = jvec.cross(kvec).normal(); kiorth = kvec.cross(ivec).normal(); int irange = (int)floor(abs(kernBox / ivec.dot(jkorth))); int jrange = (int)floor(abs(kernBox / jvec.dot(kiorth))); int krange = (int)floor(abs(kernBox / kvec.dot(ijorth))); if (irange < 1) irange = 1;//don't underflow if (jrange < 1) jrange = 1; if (krange < 1) krange = 1; int isize = irange * 2 + 1;//and construct a precomputed kernel in the box int jsize = jrange * 2 + 1; int ksize = krange * 2 + 1; CaretArray weights(ksize);//so I don't need to explicitly delete[] if I throw CaretArray weights2(ksize * jsize);//construct flat arrays and index them into 3D CaretArray weights3(ksize * jsize * isize);//index i comes last because that is linear for volume frames Vector3D kscratch, jscratch, iscratch; for (int k = 0; k < ksize; ++k) { kscratch = kvec * (k - krange); weights[k] = weights2 + k * jsize; for (int j = 0; j < jsize; ++j) { jscratch = kscratch + jvec * (j - jrange); weights[k][j] = weights3 + ((k * jsize) + j) * isize; for (int i = 0; i < isize; ++i) { iscratch = jscratch + ivec * (i - irange); float tempf = iscratch.length(); if (tempf > kernBox) { weights[k][j][i] = 0.0f;//test for zero to avoid some multiplies/adds, cheaper or cleaner than checking bounds on indexes from an index list } else { weights[k][j][i] = exp(-tempf * tempf / kernel / kernel / 2.0f);//optimization here isn't critical } } } } if (subvol == -1) { vector origDims = inVol->getOriginalDimensions(); outVol->reinitialize(origDims, volSpace, myDims[4]); for (int s = 0; s < myDims[3]; ++s) { outVol->setMapName(s, inVol->getMapName(s) + ", smooth " + AString::number(kernel)); for (int c = 0; c < myDims[4]; ++c) { const float* inFrame = inVol->getFrame(s, c); smoothFrameNonOrth(inFrame, myDims, scratchFrame, inVol, roiVol, weights, irange, jrange, krange, fixZeros); outVol->setFrame(scratchFrame, s, c); } } } else { vector origDims = inVol->getOriginalDimensions(), newDims; newDims.resize(3); newDims[0] = origDims[0]; newDims[1] = origDims[1]; newDims[2] = origDims[2]; outVol->reinitialize(newDims, volSpace, myDims[4]); outVol->setMapName(0, inVol->getMapName(subvol) + ", smooth " + AString::number(kernel)); for (int c = 0; c < myDims[4]; ++c) { const float* inFrame = inVol->getFrame(subvol, c); smoothFrameNonOrth(inFrame, myDims, scratchFrame, inVol, roiVol, weights, irange, jrange, krange, fixZeros); outVol->setFrame(scratchFrame, 0, c); } } } } void AlgorithmVolumeSmoothing::smoothFrame(const float* inFrame, vector myDims, CaretArray scratchFrame, CaretArray scratchFrame2, CaretArray scratchWeights, CaretArray scratchWeights2, const VolumeFile* inVol, CaretArray iweights, CaretArray jweights, CaretArray kweights, int irange, int jrange, int krange, const bool& fixZeros) {//this function should ONLY get invoked when the volume is orthogonal (axes are perpendicular, not necessarily aligned with x, y, z, and not necessarily equal spacing) #pragma omp CARET_PARFOR schedule(dynamic) for (int k = 0; k < myDims[2]; ++k)//smooth along i axis { for (int j = 0; j < myDims[1]; ++j) { for (int i = 0; i < myDims[0]; ++i) { int imin = i - irange, imax = i + irange + 1;//one-after array size convention if (imin < 0) imin = 0; if (imax > myDims[0]) imax = myDims[0]; float sum = 0.0f, weightsum = 0.0f; int64_t baseInd = inVol->getIndex(0, j, k, 0);//extra 0 on a default parameter is to prevent int->pointer vs int->int64 conversion ambiguity int64_t curInd = baseInd + i; for (int ikern = imin; ikern < imax; ++ikern) { int64_t thisIndex = baseInd + ikern; if ((!fixZeros || inFrame[thisIndex] != 0.0f)) { float weight = iweights[ikern - i + irange]; weightsum += weight; sum += weight * inFrame[thisIndex]; } } scratchWeights[curInd] = weightsum; scratchFrame[curInd] = sum;//don't divide yet, we will divide later after we gather the weighted sums of the weighted sums of the weight sums (yes, that repetition is right) } } } #pragma omp CARET_PARFOR schedule(dynamic) for (int k = 0; k < myDims[2]; ++k)//now j { for (int i = 0; i < myDims[0]; ++i) { for (int j = 0; j < myDims[1]; ++j)//step along the dimension being smoothed last for best cache coherence { int jmin = j - jrange, jmax = j + jrange + 1;//one-after array size convention if (jmin < 0) jmin = 0; if (jmax > myDims[1]) jmax = myDims[1]; float sum = 0.0f, weightsum = 0.0f; int64_t baseInd = inVol->getIndex(i, 0, k); int64_t curInd = baseInd + j * myDims[0]; for (int jkern = jmin; jkern < jmax; ++jkern) { int64_t thisIndex = baseInd + jkern * myDims[0]; float weight = jweights[jkern - j + jrange]; weightsum += weight * scratchWeights[thisIndex]; sum += weight * scratchFrame[thisIndex]; } scratchWeights2[curInd] = weightsum; scratchFrame2[curInd] = sum;//we now have the weighted sum of the weight sums } } } #pragma omp CARET_PARFOR schedule(dynamic) for (int j = 0; j < myDims[1]; ++j)//and finally k { for (int i = 0; i < myDims[0]; ++i) { for (int k = 0; k < myDims[2]; ++k)//ditto { int64_t baseInd = inVol->getIndex(i, j, 0); int64_t curInd = baseInd + k * myDims[0] * myDims[1]; int kmin = k - krange, kmax = k + krange + 1;//one-after array size convention if (kmin < 0) kmin = 0; if (kmax > myDims[2]) kmax = myDims[2]; float sum = 0.0f, weightsum = 0.0f; for (int kkern = kmin; kkern < kmax; ++kkern) { int64_t thisIndex = baseInd + kkern * myDims[0] * myDims[1]; float weight = kweights[kkern - k + krange]; weightsum += weight * scratchWeights2[thisIndex]; sum += weight * scratchFrame2[thisIndex]; } if (weightsum != 0.0f) { scratchFrame[curInd] = sum / weightsum;//NOW we can divide } else { scratchFrame[curInd] = 0.0f; } } } } } void AlgorithmVolumeSmoothing::smoothFrameROI(const float* inFrame, vector myDims, CaretArray scratchFrame, CaretArray scratchFrame2, CaretArray scratchFrame3, CaretArray scratchWeights, CaretArray scratchWeights2, vector lists[3], const VolumeFile* inVol, const VolumeFile* roiVol, CaretArray iweights, CaretArray jweights, CaretArray kweights, int irange, int jrange, int krange, const bool& fixZeros) {//optimized for orthogonal, plus lists of voxels for ROI smoothing if (lists[0].size() == 0) {//this is our first time into this function, we must populate the lists const float* roiFrame = roiVol->getFrame(); CaretArray markROI(myDims[0] * myDims[1] * myDims[2], 0);//need a temporary array to sort out ROI zeros from -fix-zeros zeros #pragma omp CARET_PARFOR for (int k = 0; k < myDims[2]; ++k)//smooth along i axis { for (int j = 0; j < myDims[1]; ++j) { for (int i = 0; i < myDims[0]; ++i)//don't test whether intermediate voxel is insode ROI, or we lose some data { int imin = i - irange, imax = i + irange + 1;//one-after array size convention if (imin < 0) imin = 0; if (imax > myDims[0]) imax = myDims[0]; float sum = 0.0f, weightsum = 0.0f; int64_t baseInd = inVol->getIndex(0, j, k, 0); int64_t curInd = baseInd + i; bool used = false; for (int ikern = imin; ikern < imax; ++ikern) { int64_t thisIndex = baseInd + ikern; if (roiFrame[thisIndex] > 0.0f)//only test source and final voxels for being in ROI { if (!used)//keep a list of voxels whose i-kernel intersects the ROI { used = true; markROI[curInd] = 1; #pragma omp critical { lists[0].push_back(i); lists[0].push_back(j); lists[0].push_back(k); } } if (!fixZeros || inFrame[thisIndex] != 0.0f) { float weight = iweights[ikern - i + irange]; weightsum += weight; sum += weight * inFrame[thisIndex]; } } } scratchWeights[curInd] = weightsum; scratchFrame3[curInd] = sum;//don't divide yet, we will divide later after we gather the weighted sums of the weighted sums of the weight sums (yes, that repetition is right) } } } #pragma omp CARET_PARFOR for (int k = 0; k < myDims[2]; ++k)//now j { for (int i = 0; i < myDims[0]; ++i) { for (int j = 0; j < myDims[1]; ++j)//step along the dimension being smoothed last for best cache coherence { int jmin = j - jrange, jmax = j + jrange + 1;//one-after array size convention if (jmin < 0) jmin = 0; if (jmax > myDims[1]) jmax = myDims[1]; float sum = 0.0f, weightsum = 0.0f; int64_t baseInd = inVol->getIndex(i, 0, k); int64_t curInd = baseInd + j * myDims[0]; bool used = false; for (int jkern = jmin; jkern < jmax; ++jkern) { int64_t thisIndex = baseInd + jkern * myDims[0];//DO NOT test for this source voxel being outside ROI, or you will skip good data if ((markROI[thisIndex] & 1) == 1) {//skip voxels whose i-kernels don't touch the ROI, they will always have 0/0 if (!used) { used = true; markROI[curInd] |= 2;//bitwise so i can track all 3 lists separately in one array #pragma omp critical { lists[1].push_back(i); lists[1].push_back(j); lists[1].push_back(k); } } float weight = jweights[jkern - j + jrange]; weightsum += weight * scratchWeights[thisIndex]; sum += weight * scratchFrame3[thisIndex]; } } scratchWeights2[curInd] = weightsum; scratchFrame2[curInd] = sum;//we now have the weighted sum of the weight sums } } } #pragma omp CARET_PARFOR for (int j = 0; j < myDims[1]; ++j)//and finally k { for (int i = 0; i < myDims[0]; ++i) { for (int k = 0; k < myDims[2]; ++k)//ditto { int64_t baseInd = inVol->getIndex(i, j, 0); int64_t curInd = baseInd + k * myDims[0] * myDims[1]; if (roiFrame[curInd] > 0.0f) { #pragma omp critical { lists[2].push_back(i);//third list is a little different, since we don't output stuff outside the ROI, we can drop the voxels that "grew" from the ROI lists[2].push_back(j);//we do need to calculate those grown voxels, though, since we use some of them within the k-kernel lists[2].push_back(k); } int kmin = k - krange, kmax = k + krange + 1;//one-after array size convention if (kmin < 0) kmin = 0; if (kmax > myDims[2]) kmax = myDims[2]; float sum = 0.0f, weightsum = 0.0f; for (int kkern = kmin; kkern < kmax; ++kkern) { int64_t thisIndex = baseInd + kkern * myDims[0] * myDims[1];//ditto float weight = kweights[kkern - k + krange]; weightsum += weight * scratchWeights2[thisIndex]; sum += weight * scratchFrame2[thisIndex]; } if (weightsum != 0.0f) { scratchFrame[curInd] = sum / weightsum;//NOW we can divide } else { scratchFrame[curInd] = 0.0f; } } else { scratchFrame[curInd] = 0.0f; } } } } if (lists[0].size() == 0) { lists[0].push_back(-1);//to keep it from scanning the ROI again when the ROI has no voxels, slightly hacky } } else {//lists already made, use them const float* roiFrame = roiVol->getFrame(); int64_t ibasesize = (int64_t)lists[0].size(); if (ibasesize < 3) return;//handle the case of empty ROI here (ibasesize will be 1) int64_t jbasesize = (int64_t)lists[1].size(); int64_t kbasesize = (int64_t)lists[2].size(); #pragma omp CARET_PARFOR schedule(dynamic) for (int ibase = 0; ibase < ibasesize; ibase += 3) { int i = lists[0][ibase]; int j = lists[0][ibase + 1]; int k = lists[0][ibase + 2]; int imin = i - irange, imax = i + irange + 1;//one-after array size convention if (imin < 0) imin = 0; if (imax > myDims[0]) imax = myDims[0]; float sum = 0.0f, weightsum = 0.0f; int64_t baseInd = inVol->getIndex(0, j, k, 0); int64_t curInd = baseInd + i; for (int ikern = imin; ikern < imax; ++ikern) { int64_t thisIndex = baseInd + ikern; if (roiFrame[thisIndex] > 0.0f && (!fixZeros || inFrame[thisIndex] != 0.0f)) { float weight = iweights[ikern - i + irange]; weightsum += weight; sum += weight * inFrame[thisIndex]; } } scratchWeights[curInd] = weightsum; scratchFrame3[curInd] = sum;//don't divide yet, we will divide later after we gather the weighted sums of the weighted sums of the weight sums (yes, that repetition is right) } #pragma omp CARET_PARFOR schedule(dynamic) for (int jbase = 0; jbase < jbasesize; jbase += 3) { int i = lists[1][jbase]; int j = lists[1][jbase + 1]; int k = lists[1][jbase + 2]; int jmin = j - jrange, jmax = j + jrange + 1;//one-after array size convention if (jmin < 0) jmin = 0; if (jmax > myDims[1]) jmax = myDims[1]; float sum = 0.0f, weightsum = 0.0f; int64_t baseInd = inVol->getIndex(i, 0, k); int64_t curInd = baseInd + j * myDims[0]; for (int jkern = jmin; jkern < jmax; ++jkern) { int64_t thisIndex = baseInd + jkern * myDims[0];//DO NOT test for this source voxel having zero data, or it will skip good data float weight = jweights[jkern - j + jrange]; weightsum += weight * scratchWeights[thisIndex]; sum += weight * scratchFrame3[thisIndex]; } scratchWeights2[curInd] = weightsum; scratchFrame2[curInd] = sum;//we now have the weighted sum of the weight sums } #pragma omp CARET_PARFOR schedule(dynamic) for (int kbase = 0; kbase < kbasesize; kbase += 3) { int i = lists[2][kbase]; int j = lists[2][kbase + 1]; int k = lists[2][kbase + 2]; int64_t baseInd = inVol->getIndex(i, j, 0); int64_t curInd = baseInd + k * myDims[0] * myDims[1]; int kmin = k - krange, kmax = k + krange + 1;//one-after array size convention if (kmin < 0) kmin = 0; if (kmax > myDims[2]) kmax = myDims[2]; float sum = 0.0f, weightsum = 0.0f; for (int kkern = kmin; kkern < kmax; ++kkern) { int64_t thisIndex = baseInd + kkern * myDims[0] * myDims[1];//ditto float weight = kweights[kkern - k + krange]; weightsum += weight * scratchWeights2[thisIndex]; sum += weight * scratchFrame2[thisIndex]; } if (weightsum != 0.0f) { scratchFrame[curInd] = sum / weightsum;//NOW we can divide } else { scratchFrame[curInd] = 0.0f; } }//the frame should be zeroed outside the ROI already due to the first time through } } void AlgorithmVolumeSmoothing::smoothFrameNonOrth(const float* inFrame, const vector& myDims, CaretArray& scratchFrame, const VolumeFile* inVol, const VolumeFile* roiVol, const CaretArray& weights, const int& irange, const int& jrange, const int& krange, const bool& fixZeros) { const float* roiFrame = NULL; if (roiVol != NULL) { roiFrame = roiVol->getFrame(); } #pragma omp CARET_PARFOR schedule(dynamic) for (int k = 0; k < myDims[2]; ++k) { for (int j = 0; j < myDims[1]; ++j) { for (int i = 0; i < myDims[0]; ++i) { if (roiVol == NULL || roiVol->getValue(i, j, k) > 0.0f) { int imin = i - irange, imax = i + irange + 1;//one-after array size convention if (imin < 0) imin = 0; if (imax > myDims[0]) imax = myDims[0]; int jmin = j - jrange, jmax = j + jrange + 1; if (jmin < 0) jmin = 0; if (jmax > myDims[1]) jmax = myDims[1]; int kmin = k - krange, kmax = k + krange + 1; if (kmin < 0) kmin = 0; if (kmax > myDims[2]) kmax = myDims[2]; float sum = 0.0f, weightsum = 0.0f; for (int kkern = kmin; kkern < kmax; ++kkern) { int64_t kindpart = kkern * myDims[1]; int kkernpart = kkern - k + krange; for (int jkern = jmin; jkern < jmax; ++jkern) { int64_t jindpart = (kindpart + jkern) * myDims[0]; int jkernpart = jkern - j + jrange; for (int ikern = imin; ikern < imax; ++ikern) { int64_t thisIndex = jindpart + ikern;//somewhat optimized index computation, could remove some integer multiplies, but there aren't that many float weight = weights[kkernpart][jkernpart][ikern - i + irange]; if (weight != 0.0f && (roiVol == NULL || roiFrame[thisIndex] > 0.0f) && (!fixZeros || inFrame[thisIndex] != 0.0f)) { weightsum += weight; sum += weight * inFrame[thisIndex]; } } } } if (weightsum != 0.0f) { scratchFrame[inVol->getIndex(i, j, k)] = sum / weightsum; } else { scratchFrame[inVol->getIndex(i, j, k)] = 0.0f; } } else { scratchFrame[inVol->getIndex(i, j, k)] = 0.0f; } } } } } float AlgorithmVolumeSmoothing::getAlgorithmInternalWeight() { return 1.0f;//override this if needed, if the progress bar isn't smooth } float AlgorithmVolumeSmoothing::getSubAlgorithmWeight() { return 0.0f; } connectome-workbench-1.2.3+git41-gc4c6c90/src/Algorithms/AlgorithmVolumeSmoothing.h000066400000000000000000000061611300200146000300400ustar00rootroot00000000000000#ifndef __ALGORITHM_VOLUME_SMOOTHING_H__ #define __ALGORITHM_VOLUME_SMOOTHING_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 "AbstractAlgorithm.h" namespace caret { class AlgorithmVolumeSmoothing : public AbstractAlgorithm { AlgorithmVolumeSmoothing(); static bool haveWarned; protected: static float getSubAlgorithmWeight(); static float getAlgorithmInternalWeight(); void smoothFrame(const float* inFrame, std::vector myDims, CaretArray scratchFrame, CaretArray scratchFrame2, CaretArray scratchWeights, CaretArray scratchWeights2, const VolumeFile* inVol, CaretArray iweights, CaretArray jweights, CaretArray kweights, int irange, int jrange, int krange, const bool& fixZeros); void smoothFrameROI(const float* inFrame, std::vector myDims, CaretArray scratchFrame, CaretArray scratchFrame2, CaretArray scratchFrame3, CaretArray scratchWeights, CaretArray scratchWeights2, std::vector lists[3], const VolumeFile* inVol, const VolumeFile* roiVol, CaretArray iweights, CaretArray jweights, CaretArray kweights, int irange, int jrange, int krange, const bool& fixZeros); void smoothFrameNonOrth(const float* inFrame, const std::vector& myDims, CaretArray& scratchFrame, const VolumeFile* inVol, const VolumeFile* roiVol, const CaretArray& weights, const int& irange, const int& jrange, const int& krange, const bool& fixZeros); public: AlgorithmVolumeSmoothing(ProgressObject* myProgObj, const VolumeFile* inVol, const float& kernel, VolumeFile* outVol, const VolumeFile* roiVol = NULL, const bool& fixZeros = false, const int& subvol = -1); static OperationParameters* getParameters(); static void useParameters(OperationParameters* myParams, ProgressObject* myProgObj); static AString getCommandSwitch(); static AString getShortDescription(); }; typedef TemplateAutoOperation AutoAlgorithmVolumeSmoothing; } #endif //__ALGORITHM_VOLUME_SMOOTHING_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Algorithms/AlgorithmVolumeTFCE.cxx000066400000000000000000000435351300200146000271730ustar00rootroot00000000000000/*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 "AlgorithmVolumeTFCE.h" #include "AlgorithmException.h" #include "AlgorithmVolumeSmoothing.h" #include "CaretAssert.h" #include "CaretHeap.h" #include "CaretOMP.h" #include "VolumeFile.h" #include "VoxelIJK.h" #include #include #include using namespace caret; using namespace std; AString AlgorithmVolumeTFCE::getCommandSwitch() { return "-volume-tfce"; } AString AlgorithmVolumeTFCE::getShortDescription() { return "DO TFCE ON A VOLUME FILE"; } OperationParameters* AlgorithmVolumeTFCE::getParameters() { OperationParameters* ret = new OperationParameters(); ret->addVolumeParameter(1, "volume-in", "the volume to run TFCE on"); ret->addVolumeOutputParameter(2, "volume-out", "the output volume"); OptionalParameter* presmoothOpt = ret->createOptionalParameter(3, "-presmooth", "smooth the volume before running TFCE"); presmoothOpt->addDoubleParameter(1, "kernel", "the sigma for the gaussian smoothing kernel, in mm"); OptionalParameter* roiOpt = ret->createOptionalParameter(4, "-roi", "select a region of interest to run TFCE on"); roiOpt->addVolumeParameter(1, "roi-volume", "the area to run TFCE on, as a volume"); OptionalParameter* paramsOpt = ret->createOptionalParameter(5, "-parameters", "set parameters for TFCE integral"); paramsOpt->addDoubleParameter(1, "E", "exponent for cluster volume (default 0.5)"); paramsOpt->addDoubleParameter(2, "H", "exponent for threshold value (default 2.0)"); OptionalParameter* subvolSelect = ret->createOptionalParameter(6, "-subvolume", "select a single subvolume"); subvolSelect->addStringParameter(1, "subvolume", "the subvolume number or name"); ret->setHelpText( AString("Threshold-free cluster enhancement is a method to increase the relative value of regions that would form clusters in a standard thresholding test. ") + "This is accomplished by evaluating the integral of:\n\n" + "e(h, p)^E * h^H * dh\n\n" + "at each vertex p, where h ranges from 0 to the maximum value in the data, and e(h, p) is the extent of the cluster containing vertex p at threshold h. " + "Negative values are similarly enhanced by negating the data, running the same process, and negating the result.\n\n" + "This method is explained in: Smith SM, Nichols TE., \"Threshold-free cluster enhancement: addressing problems of smoothing, threshold dependence and localisation in cluster inference.\" Neuroimage. 2009 Jan 1;44(1):83-98. PMID: 18501637" ); return ret; } void AlgorithmVolumeTFCE::useParameters(OperationParameters* myParams, ProgressObject* myProgObj) { VolumeFile* myVol = myParams->getVolume(1); VolumeFile* myVolOut = myParams->getOutputVolume(2); float presmooth = 0.0f; OptionalParameter* presmoothOpt = myParams->getOptionalParameter(3); if (presmoothOpt->m_present) { presmooth = (float)presmoothOpt->getDouble(1); if (presmooth <= 0.0f) throw AlgorithmException("presmooth kernel size must be positive"); } VolumeFile* myRoi = NULL; OptionalParameter* roiOpt = myParams->getOptionalParameter(4); if (roiOpt->m_present) { myRoi = roiOpt->getVolume(1); } float param_e = 0.5f, param_h = 2.0; OptionalParameter* paramsOpt = myParams->getOptionalParameter(5); if (paramsOpt->m_present) { param_e = (float)paramsOpt->getDouble(1); param_h = (float)paramsOpt->getDouble(2); } OptionalParameter* subvolSelect = myParams->getOptionalParameter(6); int64_t subvolNum = -1; if (subvolSelect->m_present) {//set up to use the single column subvolNum = myVol->getMapIndexFromNameOrNumber(subvolSelect->getString(1)); if (subvolNum < 0) { throw AlgorithmException("invalid subvolume specified"); } } AlgorithmVolumeTFCE(myProgObj, myVol, myVolOut, presmooth, myRoi, param_e, param_h, subvolNum); } AlgorithmVolumeTFCE::AlgorithmVolumeTFCE(ProgressObject* myProgObj, const VolumeFile* myVol, VolumeFile* myVolOut, const float& presmooth, const VolumeFile* myRoi, const float& param_e, const float& param_h, const int64_t& subvolNum) : AbstractAlgorithm(myProgObj) { LevelProgress myProgress(myProgObj); if (myRoi != NULL && !myVol->getVolumeSpace().matches(myRoi->getVolumeSpace())) throw AlgorithmException("roi volume has different volume space than input"); if (subvolNum < -1 || subvolNum >= myVol->getNumberOfMaps()) throw AlgorithmException("invalid subvolume specified"); vector dims = myVol->getDimensions(); const float* roiFrame = NULL; if (myRoi != NULL) roiFrame = myRoi->getFrame(); if (subvolNum == -1) { myVolOut->reinitialize(myVol->getOriginalDimensions(), myVol->getSform(), dims[4]); const VolumeFile* toUse = myVol; VolumeFile smoothed; if (presmooth > 0.0f) { AlgorithmVolumeSmoothing(NULL, myVol, presmooth, &smoothed, myRoi); toUse = &smoothed; } #pragma omp CARET_PAR { vector outframe(dims[0] * dims[1] * dims[2]); #pragma omp CARET_FOR for (int64_t b = 0; b < dims[3]; ++b) { for (int64_t c = 0; c < dims[4]; ++c) { processFrame(toUse, b, c, outframe.data(), roiFrame, param_e, param_h); myVolOut->setFrame(outframe.data(), b, c); } } } } else { vector outDims = dims; outDims.resize(3); myVolOut->reinitialize(outDims, myVol->getSform(), dims[4]); const VolumeFile* toUse = myVol; int useFrame = subvolNum; VolumeFile smoothed; if (presmooth > 0.0f) { AlgorithmVolumeSmoothing(NULL, myVol, presmooth, &smoothed, myRoi, false, subvolNum); toUse = &smoothed; useFrame = 0; } vector outframe(dims[0] * dims[1] * dims[2]); for (int64_t c = 0; c < dims[4]; ++c) { processFrame(toUse, useFrame, c, outframe.data(), roiFrame, param_e, param_h); myVolOut->setFrame(outframe.data(), 0, c); } } } void AlgorithmVolumeTFCE::processFrame(const VolumeFile* inVol, const int64_t& b, const int64_t& c, float* outData, const float* roiData, const float& param_e, const float& param_h) { vector dims = inVol->getDimensions(); int64_t frameSize = dims[0] * dims[1] * dims[2]; vector accum(frameSize, 0.0); tfce(inVol, b, c, accum.data(), roiData, param_e, param_h, false);//don't negate - positives tfce(inVol, b, c, accum.data(), roiData, param_e, param_h, true);//negate - negatives - NOTE: output is still positive!!! const float* inData = inVol->getFrame(b, c); for (int64_t i = 0; i < frameSize; ++i) { if (inData[i] > 0.0f)//negate the results from negative inputs { outData[i] = accum[i]; } else {//the areas outside the roi will have zeros, so we don't have to worry about them outData[i] = -accum[i]; } } } namespace {//hidden namespace just to make sure things don't collide struct Cluster { double accumVal, totalVolume; vector members; float lastVal; bool first; Cluster() { first = true; accumVal = 0.0; totalVolume = 0.0; } void addMember(const VoxelIJK& voxel, const float& val, const float& voxel_volume, const float& param_e, const float& param_h) { update(val, param_e, param_h); members.push_back(voxel); totalVolume += voxel_volume; } void update(const float& bottomVal, const float& param_e, const float& param_h) { if (first) { lastVal = bottomVal; first = false; } else { if (bottomVal != lastVal)//skip computing if there is no difference { CaretAssert(bottomVal < lastVal); double integrated_h = param_h + 1.0f;//integral(x^h) = (x^(h + 1))/(h + 1) + C double newSlice = pow(totalVolume, (double)param_e) * (pow((double)lastVal, integrated_h) - pow((double)bottomVal, integrated_h)) / integrated_h; accumVal += newSlice; lastVal = bottomVal;//computing in double precision, with float for inputs, puts the smallest difference between values far greater than the instability of the computation } } } }; int64_t allocCluster(vector& clusterList, set& deadClusters) { if (deadClusters.empty()) { clusterList.push_back(Cluster()); return (int64_t)(clusterList.size() - 1); } else { set::iterator iter = deadClusters.begin(); int64_t ret = *iter; deadClusters.erase(iter); clusterList[ret] = Cluster();//reinitialize return ret; } } } void AlgorithmVolumeTFCE::tfce(const VolumeFile* inVol, const int64_t& b, const int64_t& c, double* accumData, const float* roiData, const float& param_e, const float& param_h, const bool& negate) { vector dims = inVol->getDimensions(); Vector3D ivec, jvec, kvec, origin;//compute the volume of a voxel so different resolutions have comparable values - as if it matters, but hey inVol->getVolumeSpace().getSpacingVectors(ivec, jvec, kvec, origin);//who knows, maybe we'll have distortion correction in volume someday float voxelVolume = abs(ivec.dot(jvec.cross(kvec))); const int64_t frameSize = dims[0] * dims[1] * dims[2]; const float* frameData = inVol->getFrame(b, c); vector membership(frameSize, -1);//use int64_t just in case we get an absurd number of clusters vector clusterList; set deadClusters;//to allow reallocation without changing indices CaretSimpleMaxHeap voxelHeap; for (int64_t i = 0; i < dims[0]; ++i) { for (int64_t j = 0; j < dims[1]; ++j) { for (int64_t k = 0; k < dims[2]; ++k) { int64_t index = inVol->getIndex(i, j, k); if ((roiData == NULL || roiData[index] > 0.0f)) { if (negate) { if (frameData[index] < 0.0f) { voxelHeap.push(VoxelIJK(i, j, k), -frameData[index]); } } else { if (frameData[index] > 0.0f) { voxelHeap.push(VoxelIJK(i, j, k), frameData[index]); } } } } } } const int STENCIL_SIZE = 18; int64_t stencil[STENCIL_SIZE] = { 0, 0, -1, 0, -1, 0, -1, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1 }; while (!voxelHeap.isEmpty()) { float value; VoxelIJK voxel = voxelHeap.pop(&value); int64_t voxelIndex = inVol->getIndex(voxel.m_ijk); set touchingClusters; for (int i = 0; i < STENCIL_SIZE; i += 3) { VoxelIJK neighVoxel(voxel.m_ijk[0] + stencil[i], voxel.m_ijk[1] + stencil[i + 1], voxel.m_ijk[2] + stencil[i + 2]); if (inVol->indexValid(neighVoxel.m_ijk)) { int64_t neighIndex = inVol->getIndex(neighVoxel.m_ijk); if (membership[neighIndex] != -1) { touchingClusters.insert(membership[neighIndex]); } } } int numTouching = (int)touchingClusters.size(); switch (numTouching) { case 0://make new cluster { int64_t newCluster = allocCluster(clusterList, deadClusters); clusterList[newCluster].addMember(voxel, value, voxelVolume, param_e, param_h); membership[voxelIndex] = newCluster; break; } case 1://add to cluster { int64_t whichCluster = *(touchingClusters.begin()); clusterList[whichCluster].addMember(voxel, value, voxelVolume, param_e, param_h); membership[voxelIndex] = whichCluster; accumData[voxelIndex] -= clusterList[whichCluster].accumVal;//the accum value is the current amount less than the peak value that the edge of the cluster has (this node is on the edge) break;//so, when the cluster merges or reaches 0, we add the accum value to every member and zero the accum value of the merged cluster, and we get the correct value in the end (with far fewer flops than integrating everywhere) } default://merge all touching clusters { int64_t mergedIndex = -1, biggestSize = 0;//find the biggest cluster (in number of members) and use as merged cluster, for optimization purposes for (set::iterator iter = touchingClusters.begin(); iter != touchingClusters.end(); ++iter) { if ((int64_t)clusterList[*iter].members.size() > biggestSize) { mergedIndex = *iter; biggestSize = (int64_t)clusterList[*iter].members.size(); } } CaretAssertVectorIndex(clusterList, mergedIndex); Cluster& mergedCluster = clusterList[mergedIndex]; mergedCluster.update(value, param_e, param_h);//recalculate to align cluster bottoms for (set::iterator iter = touchingClusters.begin(); iter != touchingClusters.end(); ++iter) { if (*iter != mergedIndex)//if we are the largest cluster, don't modify the per-voxel accum for members, so merges between small and large clusters are cheap { Cluster& thisCluster = clusterList[*iter]; thisCluster.update(value, param_e, param_h); int64_t numMembers = (int64_t)thisCluster.members.size(); double correctionVal = thisCluster.accumVal - mergedCluster.accumVal;//fix the accum values in the side cluster so we can add the merged cluster's accum to everything at the end for (int64_t j = 0; j < numMembers; ++j)//add the correction value to every member so that we have the current integrated values correct { int64_t memberIndex = inVol->getIndex(thisCluster.members[j].m_ijk); accumData[memberIndex] += correctionVal;//apply the correction membership[memberIndex] = mergedIndex;//and update membership } mergedCluster.members.insert(mergedCluster.members.end(), thisCluster.members.begin(), thisCluster.members.end());//copy all members mergedCluster.totalVolume += thisCluster.totalVolume; deadClusters.insert(*iter);//kill it vector().swap(clusterList[*iter].members);//also try to deallocate member list } } mergedCluster.addMember(voxel, value, voxelVolume, param_e, param_h);//will not trigger recomputation, we already recomputed at this value accumData[voxelIndex] -= mergedCluster.accumVal;//the voxel they merge on must not get the peak value of the cluster, obviously, so again, record its difference from peak membership[voxelIndex] = mergedIndex; break;//NOTE: do not reset the accum value of the merged cluster, we specifically avoided modifying the per-voxel accum for its members, so the cluster accum is still in play } } } int listSize = (int)clusterList.size();//final cleanup of accum values for (int i = 0; i < listSize; ++i) { if (deadClusters.find(i) != deadClusters.end()) continue;//ignore clusters that don't exist Cluster& thisCluster = clusterList[i]; thisCluster.update(0.0f, param_e, param_h);//update to include the to-zero slice int numMembers = (int)thisCluster.members.size(); for (int j = 0; j < numMembers; ++j) { accumData[inVol->getIndex(thisCluster.members[j].m_ijk)] += thisCluster.accumVal;//add the resulting slice to all members - their stored data contains the offset between the cluster peak and their corect value } } } float AlgorithmVolumeTFCE::getAlgorithmInternalWeight() { return 1.0f;//override this if needed, if the progress bar isn't smooth } float AlgorithmVolumeTFCE::getSubAlgorithmWeight() { //return AlgorithmInsertNameHere::getAlgorithmWeight();//if you use a subalgorithm return 0.0f; } connectome-workbench-1.2.3+git41-gc4c6c90/src/Algorithms/AlgorithmVolumeTFCE.h000066400000000000000000000042121300200146000266050ustar00rootroot00000000000000#ifndef __ALGORITHM_VOLUME_TFCE_H__ #define __ALGORITHM_VOLUME_TFCE_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 "AbstractAlgorithm.h" namespace caret { class AlgorithmVolumeTFCE : public AbstractAlgorithm { AlgorithmVolumeTFCE(); void processFrame(const VolumeFile* inVol, const int64_t& b, const int64_t& c, float* outData, const float* roiData, const float& param_e, const float& param_h); void tfce(const VolumeFile* inVol, const int64_t& b, const int64_t& c, double* accumData, const float* roiData, const float& param_e, const float& param_h, const bool& negate); protected: static float getSubAlgorithmWeight(); static float getAlgorithmInternalWeight(); public: AlgorithmVolumeTFCE(ProgressObject* myProgObj, const VolumeFile* myVol, VolumeFile* myVolOut, const float& presmooth = 0.0f, const VolumeFile* myRoi = NULL, const float& param_e = 0.5f, const float& param_h = 2.0f, const int64_t& subvolNum = -1); static OperationParameters* getParameters(); static void useParameters(OperationParameters* myParams, ProgressObject* myProgObj); static AString getCommandSwitch(); static AString getShortDescription(); }; typedef TemplateAutoOperation AutoAlgorithmVolumeTFCE; } #endif //__ALGORITHM_VOLUME_TFCE_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Algorithms/AlgorithmVolumeToSurfaceMapping.cxx000066400000000000000000000661261300200146000316620ustar00rootroot00000000000000/*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 "AlgorithmVolumeToSurfaceMapping.h" #include "AlgorithmException.h" #include "CaretOMP.h" #include "FloatMatrix.h" #include "MathFunctions.h" #include "MetricFile.h" #include "SurfaceFile.h" #include "TopologyHelper.h" #include "Vector3D.h" #include "VolumeFile.h" #include #include using namespace caret; using namespace std; AString AlgorithmVolumeToSurfaceMapping::getCommandSwitch() { return "-volume-to-surface-mapping"; } AString AlgorithmVolumeToSurfaceMapping::getShortDescription() { return "MAP VOLUME TO SURFACE"; } OperationParameters* AlgorithmVolumeToSurfaceMapping::getParameters() { OperationParameters* ret = new OperationParameters(); ret->addVolumeParameter(1, "volume", "the volume to map data from"); ret->addSurfaceParameter(2, "surface", "the surface to map the data onto"); ret->addMetricOutputParameter(3, "metric-out", "the output metric file"); ret->createOptionalParameter(4, "-trilinear", "use trilinear volume interpolation"); ret->createOptionalParameter(5, "-enclosing", "use value of the enclosing voxel"); ret->createOptionalParameter(8, "-cubic", "use cubic splines"); OptionalParameter* ribbonOpt = ret->createOptionalParameter(6, "-ribbon-constrained", "use ribbon constrained mapping algorithm"); ribbonOpt->addSurfaceParameter(1, "inner-surf", "the inner surface of the ribbon"); ribbonOpt->addSurfaceParameter(2, "outer-surf", "the outer surface of the ribbon"); OptionalParameter* roiVol = ribbonOpt->createOptionalParameter(3, "-volume-roi", "use a volume roi"); 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"); 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"); OptionalParameter* ribbonWeightsText = ribbonOpt->createOptionalParameter(6, "-output-weights-text", "write the voxel weights for all vertices to a text file"); ribbonWeightsText->addStringParameter(1, "text-out", "output - the output text filename");//fake the output formatting OptionalParameter* myelinStyleOpt = ret->createOptionalParameter(9, "-myelin-style", "use the method from myelin mapping"); 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"); OptionalParameter* subvolumeSelect = ret->createOptionalParameter(7, "-subvol-select", "select a single subvolume to map"); subvolumeSelect->addStringParameter(1, "subvol", "the subvolume number or name"); ret->setHelpText( AString("You must specify exactly one mapping method. Enclosing voxel uses the value from the voxel the vertex lies inside, while trilinear does a 3D ") + "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. " + "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." ); return ret; } void AlgorithmVolumeToSurfaceMapping::useParameters(OperationParameters* myParams, ProgressObject* myProgObj) { VolumeFile* myVolume = myParams->getVolume(1); SurfaceFile* mySurface = myParams->getSurface(2); MetricFile* myMetricOut = myParams->getOutputMetric(3); OptionalParameter* trilinearOpt = myParams->getOptionalParameter(4); OptionalParameter* enclosingOpt = myParams->getOptionalParameter(5); OptionalParameter* cubicOpt = myParams->getOptionalParameter(8); OptionalParameter* ribbonOpt = myParams->getOptionalParameter(6); OptionalParameter* myelinStyleOpt = myParams->getOptionalParameter(9); int64_t mySubVol = -1; OptionalParameter* subvolumeSelect = myParams->getOptionalParameter(7); if (subvolumeSelect->m_present) { mySubVol = (int)myVolume->getMapIndexFromNameOrNumber(subvolumeSelect->getString(1)); if (mySubVol < 0) { throw AlgorithmException("invalid column specified"); } } bool haveMethod = false; Method myMethod = CUBIC;//this tracks which constructor to call VolumeFile::InterpType volInterpMethod = VolumeFile::CUBIC; if (trilinearOpt->m_present) { haveMethod = true; myMethod = TRILINEAR; volInterpMethod = VolumeFile::TRILINEAR; } if (enclosingOpt->m_present) { if (haveMethod) { throw AlgorithmException("more than one mapping method specified"); } haveMethod = true; myMethod = ENCLOSING_VOXEL; volInterpMethod = VolumeFile::ENCLOSING_VOXEL; } if (ribbonOpt->m_present) { if (haveMethod) { throw AlgorithmException("more than one mapping method specified"); } haveMethod = true; myMethod = RIBBON_CONSTRAINED; } if (cubicOpt->m_present) { if (haveMethod) { throw AlgorithmException("more than one mapping method specified"); } haveMethod = true; myMethod = CUBIC; volInterpMethod = VolumeFile::CUBIC; } if (myelinStyleOpt->m_present) { if (haveMethod) { throw AlgorithmException("more than one mapping method specified"); } haveMethod = true; myMethod = MYELIN_STYLE; } if (!haveMethod) { throw AlgorithmException("no mapping method specified"); } switch (myMethod) { case TRILINEAR: case ENCLOSING_VOXEL: case CUBIC: AlgorithmVolumeToSurfaceMapping(myProgObj, myVolume, mySurface, myMetricOut, volInterpMethod, mySubVol);//because we have separate constructors break; case RIBBON_CONSTRAINED: { SurfaceFile* innerSurf = ribbonOpt->getSurface(1); SurfaceFile* outerSurf = ribbonOpt->getSurface(2); OptionalParameter* roiVol = ribbonOpt->getOptionalParameter(3); VolumeFile* myRoiVol = NULL; if (roiVol->m_present) { myRoiVol = roiVol->getVolume(1); } int32_t subdivisions = 3; OptionalParameter* ribbonSubdiv = ribbonOpt->getOptionalParameter(4); if (ribbonSubdiv->m_present) { subdivisions = ribbonSubdiv->getInteger(1); if (subdivisions < 1) { throw AlgorithmException("invalid number of subdivisions specified"); } } int weightsOutVertex = -1; VolumeFile* weightsOut = NULL; OptionalParameter* ribbonWeights = ribbonOpt->getOptionalParameter(5); if (ribbonWeights->m_present) { weightsOutVertex = (int)ribbonWeights->getInteger(1); weightsOut = ribbonWeights->getOutputVolume(2); } AlgorithmVolumeToSurfaceMapping(myProgObj, myVolume, mySurface, myMetricOut, innerSurf, outerSurf, myRoiVol, subdivisions, mySubVol, weightsOutVertex, weightsOut); OptionalParameter* ribbonWeightsText = ribbonOpt->getOptionalParameter(6); if (ribbonWeightsText->m_present) {//do this after the algorithm, to let it do the error condition checking ofstream outFile(ribbonWeightsText->getString(1).toLocal8Bit().constData()); if (!outFile) throw AlgorithmException("failed to open output textfile '" + ribbonWeightsText->getString(1) + "'"); vector > myWeights; const float* roiFrame = NULL; if (myRoiVol != NULL) roiFrame = myRoiVol->getFrame(); RibbonMappingHelper::computeWeightsRibbon(myWeights, myVolume->getVolumeSpace(), innerSurf, outerSurf, roiFrame, subdivisions); for (int i = 0; i < (int)myWeights.size(); ++i) { outFile << i << ", " << myWeights[i].size(); for (int j = 0; j < (int)myWeights[i].size(); ++j) { for (int v = 0; v < 3; ++v) { outFile << ", " << myWeights[i][j].ijk[v]; } outFile << ", " << myWeights[i][j].weight; } outFile << endl; } } break; } case MYELIN_STYLE: { 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); break; } default: throw AlgorithmException("this method not yet implemented"); } } //interpolation mapping AlgorithmVolumeToSurfaceMapping::AlgorithmVolumeToSurfaceMapping(ProgressObject* myProgObj, const VolumeFile* myVolume, const SurfaceFile* mySurface, MetricFile* myMetricOut, const VolumeFile::InterpType& myMethod, const int64_t& mySubVol) : AbstractAlgorithm(myProgObj) { LevelProgress myProgress(myProgObj); vector myVolDims; myVolume->getDimensions(myVolDims); if (mySubVol >= myVolDims[3] || mySubVol < -1) { throw AlgorithmException("invalid subvolume specified"); } int64_t numColumns; if (mySubVol == -1) { numColumns = myVolDims[3] * myVolDims[4]; } else { numColumns = myVolDims[4]; } int64_t numNodes = mySurface->getNumberOfNodes(); myMetricOut->setNumberOfNodesAndColumns(numNodes, numColumns); myMetricOut->setStructure(mySurface->getStructure()); vector myArray(numNodes); AString methodName; switch (myMethod) { case VolumeFile::CUBIC: methodName = " cubic"; break; case VolumeFile::TRILINEAR: methodName = " trilinear"; break; case VolumeFile::ENCLOSING_VOXEL: methodName = " enclosing voxel"; break; } if (mySubVol == -1) { for (int64_t i = 0; i < myVolDims[3]; ++i) { for (int64_t j = 0; j < myVolDims[4]; ++j) { if (myMethod == VolumeFile::CUBIC) { myVolume->validateSpline(i, j);//to do spline deconvolution in parallel } AString metricLabel = myVolume->getMapName(i); if (myVolDims[4] != 1) { metricLabel += " component " + AString::number(j); } metricLabel += methodName; int64_t thisCol = i * myVolDims[4] + j; myMetricOut->setColumnName(thisCol, metricLabel); #pragma omp CARET_PARFOR for (int64_t node = 0; node < numNodes; ++node) { myArray[node] = myVolume->interpolateValue(mySurface->getCoordinate(node), myMethod, NULL, i, j); } if (myMethod == VolumeFile::CUBIC) { myVolume->freeSpline(i, j);//release memory we no longer need, if we allocated it } myMetricOut->setValuesForColumn(thisCol, myArray.data()); } } } else { for (int64_t j = 0; j < myVolDims[4]; ++j) { if (myMethod == VolumeFile::CUBIC) { myVolume->validateSpline(mySubVol, j);//to do spline deconvolution in parallel } AString metricLabel = myVolume->getMapName(mySubVol); if (myVolDims[4] != 1) { metricLabel += " component " + AString::number(j); } metricLabel += methodName; int64_t thisCol = j; myMetricOut->setColumnName(thisCol, metricLabel); #pragma omp CARET_PARFOR for (int64_t node = 0; node < numNodes; ++node) { myArray[node] = myVolume->interpolateValue(mySurface->getCoordinate(node), myMethod, NULL, mySubVol, j); } if (myMethod == VolumeFile::CUBIC) { myVolume->freeSpline(mySubVol, j);//release memory we no longer need, if we allocated it } myMetricOut->setValuesForColumn(thisCol, myArray.data()); } } } //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) { LevelProgress myProgress(myProgObj); vector myVolDims; myVolume->getDimensions(myVolDims); if (mySubVol >= myVolDims[3] || mySubVol < -1) { throw AlgorithmException("invalid subvolume specified"); } int64_t numColumns; if (mySubVol == -1) { numColumns = myVolDims[3] * myVolDims[4]; } else { numColumns = myVolDims[4]; } int64_t numNodes = mySurface->getNumberOfNodes(); myMetricOut->setNumberOfNodesAndColumns(numNodes, numColumns); myMetricOut->setStructure(mySurface->getStructure()); if (!mySurface->hasNodeCorrespondence(*outerSurf) || !mySurface->hasNodeCorrespondence(*innerSurf)) { throw AlgorithmException("all surfaces must have vertex correspondence"); } if (roiVol != NULL && !roiVol->matchesVolumeSpace(myVolume)) { throw AlgorithmException("roi volume is not in the same volume space as input volume"); } if (weightsOut != NULL) { if (weightsOutVertex < 0 || weightsOutVertex >= numNodes) throw AlgorithmException("invalid vertex for voxel weights output"); vector weightDims = myVolDims; weightDims.resize(3); weightsOut->reinitialize(weightDims, myVolume->getSform()); } vector > myWeights; const float* roiFrame = NULL; if (roiVol != NULL) roiFrame = roiVol->getFrame(); RibbonMappingHelper::computeWeightsRibbon(myWeights, myVolume->getVolumeSpace(), innerSurf, outerSurf, roiFrame, subdivisions); if (weightsOut != NULL) { weightsOut->setValueAllVoxels(0.0f); const vector& vertexWeights = myWeights[weightsOutVertex]; int numWeights = (int)vertexWeights.size(); for (int i = 0; i < numWeights; ++i) { weightsOut->setValue(vertexWeights[i].weight, vertexWeights[i].ijk); } } CaretArray myScratchArray(numNodes); float* myScratch = myScratchArray.getArray(); if (mySubVol == -1) { for (int64_t i = 0; i < myVolDims[3]; ++i) { for (int64_t j = 0; j < myVolDims[4]; ++j) { int64_t thisCol = i * myVolDims[4] + j; AString metricLabel = myVolume->getMapName(i); if (myVolDims[4] != 1) { metricLabel += " component " + AString::number(j); } metricLabel += " ribbon constrained"; myMetricOut->setColumnName(thisCol, metricLabel); #pragma omp CARET_PARFOR schedule(dynamic) for (int64_t node = 0; node < numNodes; ++node) { myScratch[node] = 0.0f; float totalWeight = 0.0f; int numVoxels = (int)myWeights[node].size(); for (int voxel = 0; voxel < numVoxels; ++voxel) { float thisWeight = myWeights[node][voxel].weight; totalWeight += thisWeight; myScratch[node] += thisWeight * myVolume->getValue(myWeights[node][voxel].ijk, i, j); } if (totalWeight != 0.0f) { myScratch[node] /= totalWeight; } else { myScratch[node] = 0.0f; } } myMetricOut->setValuesForColumn(thisCol, myScratch); } } } else { for (int64_t j = 0; j < myVolDims[4]; ++j) { AString metricLabel = myVolume->getMapName(mySubVol); if (myVolDims[4] != 1) { metricLabel += " component " + AString::number(j); } metricLabel += " ribbon constrained"; int64_t thisCol = j; myMetricOut->setColumnName(thisCol, metricLabel); #pragma omp CARET_PARFOR schedule(dynamic) for (int64_t node = 0; node < numNodes; ++node) { myScratch[node] = 0.0f; float totalWeight = 0.0f; int numVoxels = (int)myWeights[node].size(); for (int voxel = 0; voxel < numVoxels; ++voxel) { float thisWeight = myWeights[node][voxel].weight; totalWeight += thisWeight; myScratch[node] += thisWeight * myVolume->getValue(myWeights[node][voxel].ijk, mySubVol, j); } if (totalWeight != 0.0f) { myScratch[node] /= totalWeight; } else { myScratch[node] = 0.0f; } } myMetricOut->setValuesForColumn(thisCol, myScratch); } } } //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) { LevelProgress myProgress(myProgObj); vector myVolDims; myVolume->getDimensions(myVolDims); if (mySubVol >= myVolDims[3] || mySubVol < -1) { throw AlgorithmException("invalid subvolume specified"); } if (!roiVol->matchesVolumeSpace(myVolume)) { throw AlgorithmException("roi volume is not in the same volume space as input volume"); } int64_t numColumns; if (mySubVol == -1) { numColumns = myVolDims[3] * myVolDims[4]; } else { numColumns = myVolDims[4]; } int64_t numNodes = mySurface->getNumberOfNodes(); myMetricOut->setNumberOfNodesAndColumns(numNodes, numColumns); myMetricOut->setStructure(mySurface->getStructure()); vector > myWeights; precomputeWeightsMyelin(myWeights, mySurface, roiVol, thickness, sigma); vector myScratch(numNodes); if (mySubVol == -1) { for (int64_t i = 0; i < myVolDims[3]; ++i) { for (int64_t j = 0; j < myVolDims[4]; ++j) { int64_t thisCol = i * myVolDims[4] + j; AString metricLabel = myVolume->getMapName(i); if (myVolDims[4] != 1) { metricLabel += " component " + AString::number(j); } metricLabel += " ribbon constrained"; myMetricOut->setColumnName(thisCol, metricLabel); #pragma omp CARET_PARFOR schedule(dynamic) for (int64_t node = 0; node < numNodes; ++node) { double accum = 0.0; int numVoxels = (int)myWeights[node].size(); for (int voxel = 0; voxel < numVoxels; ++voxel) { accum += myWeights[node][voxel].weight * myVolume->getValue(myWeights[node][voxel].ijk, i, j);//weights have already been normalized in precompute, for this method } myScratch[node] = accum; } myMetricOut->setValuesForColumn(thisCol, myScratch.data()); } } } else { for (int64_t j = 0; j < myVolDims[4]; ++j) { AString metricLabel = myVolume->getMapName(mySubVol); if (myVolDims[4] != 1) { metricLabel += " component " + AString::number(j); } metricLabel += " ribbon constrained"; int64_t thisCol = j; myMetricOut->setColumnName(thisCol, metricLabel); #pragma omp CARET_PARFOR schedule(dynamic) for (int64_t node = 0; node < numNodes; ++node) { double accum = 0.0; int numVoxels = (int)myWeights[node].size(); for (int voxel = 0; voxel < numVoxels; ++voxel) { accum += myWeights[node][voxel].weight * myVolume->getValue(myWeights[node][voxel].ijk, mySubVol, j);//weights have already been normalized in precompute, for this method } myScratch[node] = accum; } myMetricOut->setValuesForColumn(thisCol, myScratch.data()); } } } void AlgorithmVolumeToSurfaceMapping::precomputeWeightsMyelin(vector >& myWeights, const SurfaceFile* mySurface, const VolumeFile* roiVol, const MetricFile* thickness, const float& sigma) { int64_t numNodes = mySurface->getNumberOfNodes(); myWeights.clear(); myWeights.resize(numNodes); vector myDims; roiVol->getDimensions(myDims); vector > volSpace = roiVol->getSform();//same as input volume Vector3D ivec, jvec, kvec, origin, ijorth, jkorth, kiorth; ivec[0] = volSpace[0][0]; jvec[0] = volSpace[0][1]; kvec[0] = volSpace[0][2]; origin[0] = volSpace[0][3]; ivec[1] = volSpace[1][0]; jvec[1] = volSpace[1][1]; kvec[1] = volSpace[1][2]; origin[1] = volSpace[1][3]; ivec[2] = volSpace[2][0]; jvec[2] = volSpace[2][1]; kvec[2] = volSpace[2][2]; origin[2] = volSpace[2][3]; ijorth = ivec.cross(jvec).normal();//find the box in index space that encloses a sphere of radius 1 jkorth = jvec.cross(kvec).normal(); kiorth = kvec.cross(ivec).normal(); float range[3]; range[0] = abs(1.0f / ivec.dot(jkorth));//we can multiply these by thickness to get the range of voxels to check range[1] = abs(1.0f / jvec.dot(kiorth)); range[2] = abs(1.0f / kvec.dot(ijorth)); const float* thicknessData = thickness->getValuePointerForColumn(0); const float* normals = mySurface->getNormalData(); float invnegsigmasquaredx2 = -1.0f / (2.0f * sigma * sigma); #pragma omp CARET_PARFOR schedule(dynamic) for (int node = 0; node < numNodes; ++node) { Vector3D nodeCoord = mySurface->getCoordinate(node), nodeIndices, nodenormal = normals + (node * 3); roiVol->spaceToIndex(nodeCoord, nodeIndices); float nodeThick = thicknessData[node]; int64_t min[3], max[3]; for (int i = 0; i < 3; ++i) { min[i] = (int)ceil(nodeIndices[i] - range[i] * nodeThick); if (min[i] < 0) min[i] = 0; max[i] = (int)floor(nodeIndices[i] + range[i] * nodeThick) + 1; if (max[i] > myDims[i]) max[i] = myDims[i]; } double weightTotal = 0.0; int64_t ijk[3]; for (ijk[2] = min[2]; ijk[2] < max[2]; ++ijk[2]) { for (ijk[1] = min[1]; ijk[1] < max[1]; ++ijk[1]) { for (ijk[0] = min[0]; ijk[0] < max[0]; ++ijk[0]) { Vector3D voxelCoord; roiVol->indexToSpace(ijk, voxelCoord); Vector3D delta = voxelCoord - nodeCoord; if (abs(nodenormal.dot(delta)) < 0.5f * nodeThick && roiVol->getValue(ijk) > 0.0f) { float weight = exp(delta.lengthsquared() * invnegsigmasquaredx2); myWeights[node].push_back(VoxelWeight(weight, ijk)); weightTotal += weight; } } } } int numWeights = (int)myWeights[node].size(); for (int i = 0; i < numWeights; ++i) { myWeights[node][i].weight /= weightTotal;//normalize weights to eliminate the need to divide } } } float AlgorithmVolumeToSurfaceMapping::getAlgorithmInternalWeight() { return 1.0f;//override this if needed, if the progress bar isn't smooth } float AlgorithmVolumeToSurfaceMapping::getSubAlgorithmWeight() { //return AlgorithmInsertNameHere::getAlgorithmWeight();//if you use a subalgorithm return 0.0f; } connectome-workbench-1.2.3+git41-gc4c6c90/src/Algorithms/AlgorithmVolumeToSurfaceMapping.h000066400000000000000000000060451300200146000313010ustar00rootroot00000000000000#ifndef __ALGORITHM_VOLUME_TO_SURFACE_MAPPING_H__ #define __ALGORITHM_VOLUME_TO_SURFACE_MAPPING_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 "AbstractAlgorithm.h" #include "RibbonMappingHelper.h" #include "Vector3D.h" #include "VolumeFile.h" #include namespace caret { class AlgorithmVolumeToSurfaceMapping : public AbstractAlgorithm { AlgorithmVolumeToSurfaceMapping(); void precomputeWeightsMyelin(std::vector >& myWeights, const SurfaceFile* mySurface, const VolumeFile* roiVol, const MetricFile* thickness, const float& sigma); enum Method { TRILINEAR, ENCLOSING_VOXEL, RIBBON_CONSTRAINED, CUBIC, MYELIN_STYLE }; protected: static float getSubAlgorithmWeight(); static float getAlgorithmInternalWeight(); public: 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); 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); static OperationParameters* getParameters(); static void useParameters(OperationParameters* myParams, ProgressObject* myProgObj); static AString getCommandSwitch(); static AString getShortDescription(); }; typedef TemplateAutoOperation AutoAlgorithmVolumeToSurfaceMapping; } #endif //__ALGORITHM_VOLUME_TO_SURFACE_MAPPING_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Algorithms/AlgorithmVolumeVectorOperation.cxx000066400000000000000000000202241300200146000315630ustar00rootroot00000000000000/*LICENSE_START*/ /* * Copyright (C) 2015 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 "AlgorithmVolumeVectorOperation.h" #include "AlgorithmException.h" #include "VolumeFile.h" using namespace caret; using namespace std; AString AlgorithmVolumeVectorOperation::getCommandSwitch() { return "-volume-vector-operation"; } AString AlgorithmVolumeVectorOperation::getShortDescription() { return "DO A VECTOR OPERATION ON VOLUME FILES"; } OperationParameters* AlgorithmVolumeVectorOperation::getParameters() { OperationParameters* ret = new OperationParameters(); ret->addVolumeParameter(1, "vectors-a", "first vector input file"); ret->addVolumeParameter(2, "vectors-b", "second vector input file"); ret->addStringParameter(3, "operation", "what vector operation to do"); ret->addVolumeOutputParameter(4, "volume-out", "the output file"); ret->createOptionalParameter(5, "-normalize-a", "normalize vectors of first input"); ret->createOptionalParameter(6, "-normalize-b", "normalize vectors of second input"); ret->createOptionalParameter(7, "-normalize-output", "normalize output vectors (not valid for dot product)"); ret->createOptionalParameter(8, "-magnitude", "output the magnitude of the result (not valid for dot product)"); AString myText = AString("Does a vector operation on two volume files (that must have a multiple of 3 subvolumes). ") + "Either of the inputs may have multiple vectors (more than 3 subvolumes), but not both (at least one must have exactly 3 subvolumes). " + "The -magnitude and -normalize-output options may not be specified together, or with the DOT operation. " + "The parameter must be one of the following:\n"; vector opList = VectorOperation::getAllOperations(); for (int i = 0; i < (int)opList.size(); ++i) { myText += "\n" + VectorOperation::operationToString(opList[i]); } ret->setHelpText(myText); return ret; } void AlgorithmVolumeVectorOperation::useParameters(OperationParameters* myParams, ProgressObject* myProgObj) { VolumeFile* volumeA = myParams->getVolume(1); VolumeFile* volumeB = myParams->getVolume(2); AString operString = myParams->getString(3); bool ok = false; VectorOperation::Operation myOper = VectorOperation::stringToOperation(operString, ok); if (!ok) throw AlgorithmException("unrecognized operation string: " + operString); VolumeFile* myVolumeOut = myParams->getOutputVolume(4); bool normA = myParams->getOptionalParameter(5)->m_present; bool normB = myParams->getOptionalParameter(6)->m_present; bool normOut = myParams->getOptionalParameter(7)->m_present; bool magOut = myParams->getOptionalParameter(8)->m_present; AlgorithmVolumeVectorOperation(myProgObj, volumeA, volumeB, myOper, myVolumeOut, normA, normB, normOut, magOut); } AlgorithmVolumeVectorOperation::AlgorithmVolumeVectorOperation(ProgressObject* myProgObj, const VolumeFile* volumeA, const VolumeFile* volumeB, const VectorOperation::Operation& myOper, VolumeFile* myVolumeOut, const bool& normA, const bool& normB, const bool& normOut, const bool& magOut) : AbstractAlgorithm(myProgObj) { LevelProgress myProgress(myProgObj); const VolumeSpace& checkSpace = volumeA->getVolumeSpace(); if (!volumeB->matchesVolumeSpace(checkSpace)) throw AlgorithmException("input files have different volume spaces"); const vector dimA = volumeA->getDimensions(), dimB = volumeB->getDimensions(); if (dimA[4] != 1 || dimB[4] != 1) throw AlgorithmException("volumes with multiple components (complex or rgb datatypes) are not supported in vector operations"); if (dimA[3] % 3 != 0) throw AlgorithmException("number of subvolumes in first input is not a multiple of 3"); if (dimB[3] % 3 != 0) throw AlgorithmException("number of subvolumes in first input is not a multiple of 3"); int64_t numVecA = dimA[3] / 3, numVecB = dimB[3] / 3; if (numVecA > 1 && numVecB > 1) throw AlgorithmException("both inputs have more than 3 subvolumes (more than 1 vector)"); if (normOut && magOut) throw AlgorithmException("normalizing the output and taking the magnitude is meaningless"); bool opScalarResult = VectorOperation::operationReturnsScalar(myOper); if (opScalarResult && (normOut || magOut)) throw AlgorithmException("cannot normalize or take magnitude of a scalar result (such as a dot product)"); bool swapped = false; const VolumeFile* multiVec = volumeA, *singleVec = volumeB; int64_t numOutVecs = numVecA; if (numVecB > 1) { multiVec = volumeB; singleVec = volumeA; numOutVecs = numVecB; swapped = true; } int64_t numSubvolsOut = numOutVecs * 3; if (opScalarResult || magOut) numSubvolsOut = numOutVecs; vector outDims = dimA; outDims.resize(3); if (numSubvolsOut > 1) outDims.push_back(numSubvolsOut); myVolumeOut->reinitialize(outDims, checkSpace.getSform()); int64_t frameSize = dimA[0] * dimA[1] * dimA[2]; vector outFrames[3];//let the scalar result case overallocate outFrames[0].resize(frameSize); outFrames[1].resize(frameSize); outFrames[2].resize(frameSize); const float* xFrameSingle = singleVec->getFrame(0); const float* yFrameSingle = singleVec->getFrame(1); const float* zFrameSingle = singleVec->getFrame(2); for (int64_t v = 0; v < numOutVecs; ++v) { const float* xFrameMulti = multiVec->getFrame(v * 3); const float* yFrameMulti = multiVec->getFrame(v * 3 + 1); const float* zFrameMulti = multiVec->getFrame(v * 3 + 2); for (int64_t i = 0; i < frameSize; ++i) { Vector3D vecA(xFrameMulti[i], yFrameMulti[i], zFrameMulti[i]); Vector3D vecB(xFrameSingle[i], yFrameSingle[i], zFrameSingle[i]); if (swapped) { Vector3D tempVec = vecA; vecA = vecB; vecB = tempVec; } if (normA) vecA = vecA.normal(); if (normB) vecB = vecB.normal(); if (opScalarResult) { outFrames[0][i] = VectorOperation::doScalarOperation(vecA, vecB, myOper); } else { Vector3D tempVec = VectorOperation::doVectorOperation(vecA, vecB, myOper); if (normOut) tempVec = tempVec.normal(); if (magOut) { outFrames[0][i] = tempVec.length(); } else { outFrames[0][i] = tempVec[0]; outFrames[1][i] = tempVec[1]; outFrames[2][i] = tempVec[2]; } } } if (opScalarResult || magOut) { myVolumeOut->setFrame(outFrames[0].data(), v); } else { myVolumeOut->setFrame(outFrames[0].data(), v * 3); myVolumeOut->setFrame(outFrames[1].data(), v * 3 + 1); myVolumeOut->setFrame(outFrames[2].data(), v * 3 + 2); } } } float AlgorithmVolumeVectorOperation::getAlgorithmInternalWeight() { return 1.0f;//override this if needed, if the progress bar isn't smooth } float AlgorithmVolumeVectorOperation::getSubAlgorithmWeight() { //return AlgorithmInsertNameHere::getAlgorithmWeight();//if you use a subalgorithm return 0.0f; } connectome-workbench-1.2.3+git41-gc4c6c90/src/Algorithms/AlgorithmVolumeVectorOperation.h000066400000000000000000000037631300200146000312210ustar00rootroot00000000000000#ifndef __ALGORITHM_VOLUME_VECTOR_OPERATION_H__ #define __ALGORITHM_VOLUME_VECTOR_OPERATION_H__ /*LICENSE_START*/ /* * Copyright (C) 2015 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 "VectorOperation.h" namespace caret { class AlgorithmVolumeVectorOperation : public AbstractAlgorithm { AlgorithmVolumeVectorOperation(); protected: static float getSubAlgorithmWeight(); static float getAlgorithmInternalWeight(); public: AlgorithmVolumeVectorOperation(ProgressObject* myProgObj, const VolumeFile* volumeA, const VolumeFile* volumeB, const VectorOperation::Operation& myOper, VolumeFile* myVolumeOut, const bool& normA = false, const bool& normB = false, const bool& normOut = false, const bool& magOut = false); static OperationParameters* getParameters(); static void useParameters(OperationParameters* myParams, ProgressObject* myProgObj); static AString getCommandSwitch(); static AString getShortDescription(); }; typedef TemplateAutoOperation AutoAlgorithmVolumeVectorOperation; } #endif //__ALGORITHM_VOLUME_VECTOR_OPERATION_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Algorithms/AlgorithmVolumeWarpfieldResample.cxx000066400000000000000000000161171300200146000320540ustar00rootroot00000000000000/*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 "AlgorithmVolumeWarpfieldResample.h" #include "AlgorithmException.h" #include "CaretLogger.h" #include "CaretOMP.h" #include "NiftiIO.h" #include "Vector3D.h" #include "WarpfieldFile.h" using namespace caret; using namespace std; AString AlgorithmVolumeWarpfieldResample::getCommandSwitch() { return "-volume-warpfield-resample"; } AString AlgorithmVolumeWarpfieldResample::getShortDescription() { return "RESAMPLE VOLUME USING WARPFIELD"; } OperationParameters* AlgorithmVolumeWarpfieldResample::getParameters() { OperationParameters* ret = new OperationParameters(); ret->addVolumeParameter(1, "volume-in", "volume to resample"); ret->addStringParameter(2, "warpfield", "the warpfield to apply"); ret->addStringParameter(3, "volume-space", "a volume file in the volume space you want for the output"); ret->addStringParameter(4, "method", "the resampling method"); ret->addVolumeOutputParameter(5, "volume-out", "the output volume"); OptionalParameter* fnirtOpt = ret->createOptionalParameter(6, "-fnirt", "MUST be used if using a fnirt warpfield"); fnirtOpt->addStringParameter(1, "source-volume", "the source volume used when generating the warpfield"); ret->setHelpText( AString("Resample a volume file with a warpfield. ") + "The recommended methods are CUBIC (cubic spline) for most data, and ENCLOSING_VOXEL for label data. " "The parameter must be one of:\n\n" + "CUBIC\nENCLOSING_VOXEL\nTRILINEAR" ); return ret; } void AlgorithmVolumeWarpfieldResample::useParameters(OperationParameters* myParams, ProgressObject* myProgObj) { VolumeFile* inVol = myParams->getVolume(1); AString warpName = myParams->getString(2); AString refSpaceName = myParams->getString(3); AString method = myParams->getString(4); VolumeFile* outVol = myParams->getOutputVolume(5); OptionalParameter* fnirtOpt = myParams->getOptionalParameter(6); WarpfieldFile myWarpfield; if (fnirtOpt->m_present) { myWarpfield.readFnirt(warpName, fnirtOpt->getString(1)); } else { myWarpfield.readWorld(warpName); } VolumeFile::InterpType myMethod = VolumeFile::CUBIC; if (method == "CUBIC") { myMethod = VolumeFile::CUBIC; } else if (method == "TRILINEAR") { myMethod = VolumeFile::TRILINEAR; } else if (method == "ENCLOSING_VOXEL") { myMethod = VolumeFile::ENCLOSING_VOXEL; } else { throw AlgorithmException("unrecognized interpolation method"); } NiftiIO refSpaceIO; refSpaceIO.openRead(refSpaceName); AlgorithmVolumeWarpfieldResample(myProgObj, inVol, myWarpfield.getWarpfield(), refSpaceIO.getDimensions().data(), refSpaceIO.getHeader().getSForm(), myMethod, outVol); } AlgorithmVolumeWarpfieldResample::AlgorithmVolumeWarpfieldResample(ProgressObject* myProgObj, const VolumeFile* inVol, const VolumeFile* warpfield, const int64_t refDims[3], const vector >& refSform, const VolumeFile::InterpType& myMethod, VolumeFile* outVol) : AbstractAlgorithm(myProgObj) { LevelProgress myProgress(myProgObj); vector warpDims; warpfield->getDimensions(warpDims); if (warpDims[3] != 3 || warpDims[4] != 1) throw AlgorithmException("provided warpfield volume has wrong number of subvolumes or components"); vector outDims = inVol->getOriginalDimensions(); if (outDims.size() < 3) throw AlgorithmException("input must have 3 spatial dimensions"); outDims[0] = refDims[0]; outDims[1] = refDims[1]; outDims[2] = refDims[2]; int64_t numMaps = inVol->getNumberOfMaps(), numComponents = inVol->getNumberOfComponents(); outVol->reinitialize(outDims, refSform, numComponents, inVol->getType()); if (inVol->isMappedWithLabelTable()) { if (myMethod != VolumeFile::ENCLOSING_VOXEL) { CaretLogWarning("using interpolation type other than ENCLOSING_VOXEL on a label volume"); } for (int64_t i = 0; i < numMaps; ++i) { *(outVol->getMapLabelTable(i)) = *(inVol->getMapLabelTable(i)); } } for (int64_t c = 0; c < numComponents; ++c) { for (int64_t b = 0; b < numMaps; ++b) { if (myMethod == VolumeFile::CUBIC) { inVol->validateSpline(b, c);//because deconvolve is parallel, but won't execute parallel if we are already in a parallel section } #pragma omp CARET_PARFOR schedule(dynamic) for (int64_t k = 0; k < outDims[2]; ++k) { for (int64_t j = 0; j < outDims[1]; ++j) { for (int64_t i = 0; i < outDims[0]; ++i) { Vector3D outCoord, inCoord, displacement; outVol->indexToSpace(i, j, k, outCoord); bool validDisplacement = false; displacement[0] = warpfield->interpolateValue(outCoord, VolumeFile::TRILINEAR, &validDisplacement, 0); if (validDisplacement) { displacement[1] = warpfield->interpolateValue(outCoord, VolumeFile::TRILINEAR, NULL, 1); displacement[2] = warpfield->interpolateValue(outCoord, VolumeFile::TRILINEAR, NULL, 2); inCoord = outCoord + displacement; float interpVal = inVol->interpolateValue(inCoord, myMethod, NULL, b, c); outVol->setValue(interpVal, i, j, k, b, c); } else { outVol->setValue(VolumeFile::INVALID_INTERP_VALUE, i, j, k, b, c); } } } } if (myMethod == VolumeFile::CUBIC) { inVol->freeSpline(b, c);//release memory we no longer need, if we allocated it } } } } float AlgorithmVolumeWarpfieldResample::getAlgorithmInternalWeight() { return 1.0f;//override this if needed, if the progress bar isn't smooth } float AlgorithmVolumeWarpfieldResample::getSubAlgorithmWeight() { //return AlgorithmInsertNameHere::getAlgorithmWeight();//if you use a subalgorithm return 0.0f; } connectome-workbench-1.2.3+git41-gc4c6c90/src/Algorithms/AlgorithmVolumeWarpfieldResample.h000066400000000000000000000036561300200146000315050ustar00rootroot00000000000000#ifndef __ALGORITHM_VOLUME_WARPFIELD_RESAMPLE_H__ #define __ALGORITHM_VOLUME_WARPFIELD_RESAMPLE_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 "AbstractAlgorithm.h" #include "VolumeFile.h" namespace caret { class AlgorithmVolumeWarpfieldResample : public AbstractAlgorithm { AlgorithmVolumeWarpfieldResample(); protected: static float getSubAlgorithmWeight(); static float getAlgorithmInternalWeight(); public: AlgorithmVolumeWarpfieldResample(ProgressObject* myProgObj, const VolumeFile* inVol, const VolumeFile* warpfield, const int64_t refDims[3], const std::vector >& refSform, const VolumeFile::InterpType& myMethod, VolumeFile* outVol); static OperationParameters* getParameters(); static void useParameters(OperationParameters* myParams, ProgressObject* myProgObj); static AString getCommandSwitch(); static AString getShortDescription(); }; typedef TemplateAutoOperation AutoAlgorithmVolumeWarpfieldResample; } #endif //__ALGORITHM_VOLUME_WARPFIELD_RESAMPLE_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Algorithms/CMakeLists.txt000066400000000000000000000164151300200146000254240ustar00rootroot00000000000000# # Name of project # PROJECT (Algorithms) # # Need XML from Qt # SET(QT_DONT_USE_QTGUI) # # Add QT for includes # INCLUDE (${QT_USE_FILE}) # # Create the helper library # ADD_LIBRARY(Algorithms AbstractAlgorithm.h AlgorithmBorderResample.h AlgorithmBorderToVertices.h AlgorithmCiftiAllLabelsToROIs.h AlgorithmCiftiAverage.h AlgorithmCiftiAverageDenseROI.h AlgorithmCiftiAverageROICorrelation.h AlgorithmCiftiCorrelation.h AlgorithmCiftiCorrelationGradient.h AlgorithmCiftiCreateDenseScalar.h AlgorithmCiftiCreateDenseTimeseries.h AlgorithmCiftiCreateLabel.h AlgorithmCiftiCrossCorrelation.h AlgorithmCiftiDilate.h AlgorithmCiftiErode.h AlgorithmCiftiExtrema.h AlgorithmCiftiFalseCorrelation.h AlgorithmCiftiFindClusters.h AlgorithmCiftiGradient.h AlgorithmCiftiLabelAdjacency.h AlgorithmCiftiLabelToBorder.h AlgorithmCiftiLabelToROI.h AlgorithmCiftiMergeDense.h AlgorithmCiftiMergeParcels.h AlgorithmCiftiPairwiseCorrelation.h AlgorithmCiftiParcellate.h AlgorithmCiftiParcelMappingToLabel.h AlgorithmCiftiReduce.h AlgorithmCiftiReorder.h AlgorithmCiftiReplaceStructure.h AlgorithmCiftiResample.h AlgorithmCiftiRestrictDenseMap.h AlgorithmCiftiROIsFromExtrema.h AlgorithmCiftiSeparate.h AlgorithmCiftiSmoothing.h AlgorithmCiftiTranspose.h AlgorithmCiftiVectorOperation.h AlgorithmCreateSignedDistanceVolume.h AlgorithmException.h AlgorithmFiberDotProducts.h AlgorithmFociResample.h AlgorithmGiftiAllLabelsToROIs.h AlgorithmGiftiLabelAddPrefix.h AlgorithmGiftiLabelToROI.h AlgorithmLabelDilate.h AlgorithmLabelErode.h AlgorithmLabelModifyKeys.h AlgorithmLabelResample.h AlgorithmLabelToBorder.h AlgorithmLabelToVolumeMapping.h AlgorithmMetricDilate.h AlgorithmMetricErode.h AlgorithmMetricEstimateFWHM.h AlgorithmMetricExtrema.h AlgorithmMetricFalseCorrelation.h AlgorithmMetricFillHoles.h AlgorithmMetricFindClusters.h AlgorithmMetricGradient.h AlgorithmMetricReduce.h AlgorithmMetricRegression.h AlgorithmMetricRemoveIslands.h AlgorithmMetricResample.h AlgorithmMetricROIsFromExtrema.h AlgorithmMetricROIsToBorder.h AlgorithmMetricSmoothing.h AlgorithmMetricTFCE.h AlgorithmMetricToVolumeMapping.h AlgorithmMetricVectorOperation.h AlgorithmMetricVectorTowardROI.h AlgorithmNodesInsideBorder.h AlgorithmSignedDistanceToSurface.h AlgorithmSurfaceAffineRegression.h AlgorithmSurfaceApplyAffine.h AlgorithmSurfaceApplyWarpfield.h AlgorithmSurfaceAverage.h AlgorithmSurfaceCortexLayer.h AlgorithmSurfaceCreateSphere.h AlgorithmSurfaceCurvature.h AlgorithmSurfaceDistortion.h AlgorithmSurfaceFlipLR.h AlgorithmSurfaceGenerateInflated.h AlgorithmSurfaceInflation.h AlgorithmSurfaceMatch.h AlgorithmSurfaceModifySphere.h AlgorithmSurfaceResample.h AlgorithmSurfaceSmoothing.h AlgorithmSurfaceSphereProjectUnproject.h AlgorithmSurfaceToSurface3dDistance.h AlgorithmSurfaceWedgeVolume.h AlgorithmVolumeAffineResample.h AlgorithmVolumeAllLabelsToROIs.h AlgorithmVolumeDilate.h AlgorithmVolumeErode.h AlgorithmVolumeEstimateFWHM.h AlgorithmVolumeExtrema.h AlgorithmVolumeFillHoles.h AlgorithmVolumeFindClusters.h AlgorithmVolumeGradient.h AlgorithmVolumeLabelProbability.h AlgorithmVolumeLabelToROI.h AlgorithmVolumeLabelToSurfaceMapping.h AlgorithmVolumeParcelResampling.h AlgorithmVolumeParcelResamplingGeneric.h AlgorithmVolumeParcelSmoothing.h AlgorithmVolumeReduce.h AlgorithmVolumeRemoveIslands.h AlgorithmVolumeROIsFromExtrema.h AlgorithmVolumeSmoothing.h AlgorithmVolumeTFCE.h AlgorithmVolumeToSurfaceMapping.h AlgorithmVolumeVectorOperation.h AlgorithmVolumeWarpfieldResample.h OverlapLogicEnum.h AbstractAlgorithm.cxx AlgorithmBorderResample.cxx AlgorithmBorderToVertices.cxx AlgorithmCiftiAllLabelsToROIs.cxx AlgorithmCiftiAverage.cxx AlgorithmCiftiAverageDenseROI.cxx AlgorithmCiftiAverageROICorrelation.cxx AlgorithmCiftiCorrelation.cxx AlgorithmCiftiCorrelationGradient.cxx AlgorithmCiftiCreateDenseScalar.cxx AlgorithmCiftiCreateDenseTimeseries.cxx AlgorithmCiftiCreateLabel.cxx AlgorithmCiftiCrossCorrelation.cxx AlgorithmCiftiDilate.cxx AlgorithmCiftiErode.cxx AlgorithmCiftiExtrema.cxx AlgorithmCiftiFalseCorrelation.cxx AlgorithmCiftiFindClusters.cxx AlgorithmCiftiGradient.cxx AlgorithmCiftiLabelAdjacency.cxx AlgorithmCiftiLabelToBorder.cxx AlgorithmCiftiLabelToROI.cxx AlgorithmCiftiMergeDense.cxx AlgorithmCiftiMergeParcels.cxx AlgorithmCiftiPairwiseCorrelation.cxx AlgorithmCiftiParcellate.cxx AlgorithmCiftiParcelMappingToLabel.cxx AlgorithmCiftiReduce.cxx AlgorithmCiftiReorder.cxx AlgorithmCiftiReplaceStructure.cxx AlgorithmCiftiResample.cxx AlgorithmCiftiRestrictDenseMap.cxx AlgorithmCiftiROIsFromExtrema.cxx AlgorithmCiftiSeparate.cxx AlgorithmCiftiSmoothing.cxx AlgorithmCiftiTranspose.cxx AlgorithmCiftiVectorOperation.cxx AlgorithmCreateSignedDistanceVolume.cxx AlgorithmException.cxx AlgorithmFiberDotProducts.cxx AlgorithmFociResample.cxx AlgorithmGiftiAllLabelsToROIs.cxx AlgorithmGiftiLabelAddPrefix.cxx AlgorithmGiftiLabelToROI.cxx AlgorithmLabelDilate.cxx AlgorithmLabelErode.cxx AlgorithmLabelModifyKeys.cxx AlgorithmLabelResample.cxx AlgorithmLabelToBorder.cxx AlgorithmLabelToVolumeMapping.cxx AlgorithmMetricDilate.cxx AlgorithmMetricErode.cxx AlgorithmMetricEstimateFWHM.cxx AlgorithmMetricExtrema.cxx AlgorithmMetricFalseCorrelation.cxx AlgorithmMetricFillHoles.cxx AlgorithmMetricFindClusters.cxx AlgorithmMetricGradient.cxx AlgorithmMetricReduce.cxx AlgorithmMetricRegression.cxx AlgorithmMetricRemoveIslands.cxx AlgorithmMetricResample.cxx AlgorithmMetricROIsFromExtrema.cxx AlgorithmMetricROIsToBorder.cxx AlgorithmMetricSmoothing.cxx AlgorithmMetricTFCE.cxx AlgorithmMetricToVolumeMapping.cxx AlgorithmMetricVectorOperation.cxx AlgorithmMetricVectorTowardROI.cxx AlgorithmNodesInsideBorder.cxx AlgorithmSignedDistanceToSurface.cxx AlgorithmSurfaceAffineRegression.cxx AlgorithmSurfaceApplyAffine.cxx AlgorithmSurfaceApplyWarpfield.cxx AlgorithmSurfaceAverage.cxx AlgorithmSurfaceCortexLayer.cxx AlgorithmSurfaceCreateSphere.cxx AlgorithmSurfaceCurvature.cxx AlgorithmSurfaceDistortion.cxx AlgorithmSurfaceFlipLR.cxx AlgorithmSurfaceGenerateInflated.cxx AlgorithmSurfaceInflation.cxx AlgorithmSurfaceMatch.cxx AlgorithmSurfaceModifySphere.cxx AlgorithmSurfaceResample.cxx AlgorithmSurfaceSmoothing.cxx AlgorithmSurfaceSphereProjectUnproject.cxx AlgorithmSurfaceToSurface3dDistance.cxx AlgorithmSurfaceWedgeVolume.cxx AlgorithmVolumeAffineResample.cxx AlgorithmVolumeAllLabelsToROIs.cxx AlgorithmVolumeDilate.cxx AlgorithmVolumeErode.cxx AlgorithmVolumeEstimateFWHM.cxx AlgorithmVolumeExtrema.cxx AlgorithmVolumeFillHoles.cxx AlgorithmVolumeFindClusters.cxx AlgorithmVolumeGradient.cxx AlgorithmVolumeLabelProbability.cxx AlgorithmVolumeLabelToROI.cxx AlgorithmVolumeLabelToSurfaceMapping.cxx AlgorithmVolumeParcelResampling.cxx AlgorithmVolumeParcelResamplingGeneric.cxx AlgorithmVolumeParcelSmoothing.cxx AlgorithmVolumeReduce.cxx AlgorithmVolumeRemoveIslands.cxx AlgorithmVolumeROIsFromExtrema.cxx AlgorithmVolumeSmoothing.cxx AlgorithmVolumeTFCE.cxx AlgorithmVolumeToSurfaceMapping.cxx AlgorithmVolumeVectorOperation.cxx AlgorithmVolumeWarpfieldResample.cxx OverlapLogicEnum.cxx ) # # Find Headers # INCLUDE_DIRECTORIES( ${CMAKE_SOURCE_DIR}/Algorithms ${CMAKE_SOURCE_DIR}/Annotations ${CMAKE_SOURCE_DIR}/OperationsBase ${CMAKE_SOURCE_DIR}/FilesBase ${CMAKE_SOURCE_DIR}/Files ${CMAKE_SOURCE_DIR}/Gifti ${CMAKE_SOURCE_DIR}/Cifti ${CMAKE_SOURCE_DIR}/Palette ${CMAKE_SOURCE_DIR}/Nifti ${CMAKE_SOURCE_DIR}/Scenes ${CMAKE_SOURCE_DIR}/Xml ${CMAKE_SOURCE_DIR}/Charting ${CMAKE_SOURCE_DIR}/Common ) connectome-workbench-1.2.3+git41-gc4c6c90/src/Algorithms/OverlapLogicEnum.cxx000066400000000000000000000223161300200146000266200ustar00rootroot00000000000000 /*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 __OVERLAP_LOGIC_ENUM_DECLARE__ #include "OverlapLogicEnum.h" #undef __OVERLAP_LOGIC_ENUM_DECLARE__ #include "CaretAssert.h" using namespace caret; /** * \class caret::OverlapLogicEnum * \brief Enumeration for how to handle overlapping areas * * Created to be used as a parameter to ROIsFromExtrema algorithms */ /** * Constructor. * * @param enumValue * An enumerated value. * @param integerCode * Integer code for this enumerated value. * * @param name * Name of enumerated value. * * @param guiName * User-friendly name for use in user-interface. */ OverlapLogicEnum::OverlapLogicEnum(const Enum enumValue, const int32_t integerCode, const AString& name, const AString& guiName) { this->enumValue = enumValue; this->integerCode = integerCode; this->name = name; this->guiName = guiName; } /** * Destructor. */ OverlapLogicEnum::~OverlapLogicEnum() { } /** * Initialize the enumerated metadata. */ void OverlapLogicEnum::initialize() { if (initializedFlag) { return; } initializedFlag = true; enumData.push_back(OverlapLogicEnum(ALLOW, 0, "ALLOW", "Allow")); enumData.push_back(OverlapLogicEnum(CLOSEST, 1, "CLOSEST", "Closest")); enumData.push_back(OverlapLogicEnum(EXCLUDE, 2, "EXCLUDE", "Exclude")); } /** * 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 OverlapLogicEnum* OverlapLogicEnum::findData(const Enum enumValue) { if (initializedFlag == false) initialize(); size_t num = enumData.size(); for (size_t i = 0; i < num; i++) { const OverlapLogicEnum* 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 OverlapLogicEnum::toName(Enum enumValue) { if (initializedFlag == false) initialize(); const OverlapLogicEnum* 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. */ OverlapLogicEnum::Enum OverlapLogicEnum::fromName(const AString& name, bool* isValidOut) { if (initializedFlag == false) initialize(); bool validFlag = false; Enum enumValue = ALLOW; for (std::vector::iterator iter = enumData.begin(); iter != enumData.end(); iter++) { const OverlapLogicEnum& 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 OverlapLogicEnum")); } return enumValue; } /** * Get a GUI string representation of the enumerated type. * @param enumValue * Enumerated value. * @return * String representing enumerated value. */ AString OverlapLogicEnum::toGuiName(Enum enumValue) { if (initializedFlag == false) initialize(); const OverlapLogicEnum* 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. */ OverlapLogicEnum::Enum OverlapLogicEnum::fromGuiName(const AString& guiName, bool* isValidOut) { if (initializedFlag == false) initialize(); bool validFlag = false; Enum enumValue = ALLOW; for (std::vector::iterator iter = enumData.begin(); iter != enumData.end(); iter++) { const OverlapLogicEnum& 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 OverlapLogicEnum")); } return enumValue; } /** * Get the integer code for a data type. * * @return * Integer code for data type. */ int32_t OverlapLogicEnum::toIntegerCode(Enum enumValue) { if (initializedFlag == false) initialize(); const OverlapLogicEnum* 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. */ OverlapLogicEnum::Enum OverlapLogicEnum::fromIntegerCode(const int32_t integerCode, bool* isValidOut) { if (initializedFlag == false) initialize(); bool validFlag = false; Enum enumValue = ALLOW; for (std::vector::iterator iter = enumData.begin(); iter != enumData.end(); iter++) { const OverlapLogicEnum& 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 OverlapLogicEnum")); } 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 OverlapLogicEnum::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 OverlapLogicEnum::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(OverlapLogicEnum::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 OverlapLogicEnum::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(OverlapLogicEnum::toGuiName(iter->enumValue)); } if (isSorted) { std::sort(allGuiNames.begin(), allGuiNames.end()); } } connectome-workbench-1.2.3+git41-gc4c6c90/src/Algorithms/OverlapLogicEnum.h000066400000000000000000000057771300200146000262610ustar00rootroot00000000000000#ifndef __OVERLAP_LOGIC_ENUM_H__ #define __OVERLAP_LOGIC_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 OverlapLogicEnum { public: /** * Enumerated values. */ enum Enum { /** Allow overlap */ ALLOW, /** Use closer center to resolve overlaps */ CLOSEST, /** Contested areas are unused */ EXCLUDE }; ~OverlapLogicEnum(); 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: OverlapLogicEnum(const Enum enumValue, const int32_t integerCode, const AString& name, const AString& guiName); static const OverlapLogicEnum* 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; /** 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 __OVERLAP_LOGIC_ENUM_DECLARE__ std::vector OverlapLogicEnum::enumData; bool OverlapLogicEnum::initializedFlag = false; #endif // __OVERLAP_LOGIC_ENUM_DECLARE__ } // namespace #endif //__OVERLAP_LOGIC_ENUM_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Annotations/000077500000000000000000000000001300200146000230415ustar00rootroot00000000000000connectome-workbench-1.2.3+git41-gc4c6c90/src/Annotations/Annotation.cxx000066400000000000000000001437661300200146000257200ustar00rootroot00000000000000 /*LICENSE_START*/ /* * Copyright (C) 2015 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 __ANNOTATION_DECLARE__ #include "Annotation.h" #undef __ANNOTATION_DECLARE__ #include "AnnotationBox.h" #include "AnnotationColorBar.h" #include "AnnotationGroup.h" #include "AnnotationImage.h" #include "AnnotationLine.h" #include "AnnotationOval.h" #include "AnnotationPercentSizeText.h" #include "AnnotationPointSizeText.h" #include "AnnotationText.h" #include "BrainConstants.h" #include "CaretAssert.h" #include "CaretLogger.h" #include "DisplayGroupAndTabItemHelper.h" #include "MathFunctions.h" #include "SceneClass.h" #include "SceneClassAssistant.h" using namespace caret; /** * \class caret::Annotation * \brief Abstract class for annotations. * \ingroup Annotations */ /** * Constructor for an annotation. * * @param drawingType * Type of annotation for drawing. * @param attributeDefaultType * Default type for annotation attributes. */ Annotation::Annotation(const AnnotationTypeEnum::Enum type, const AnnotationAttributesDefaultTypeEnum::Enum attributeDefaultType) : CaretObjectTracksModification(), DisplayGroupAndTabItemInterface(), SceneableInterface(), m_type(type), m_attributeDefaultType(attributeDefaultType) { initializeAnnotationMembers(); } /** * Destructor. */ Annotation::~Annotation() { delete m_displayGroupAndTabItemHelper; delete m_sceneAssistant; } /** * Copy constructor. * @param obj * Object that is copied. */ Annotation::Annotation(const Annotation& obj) : CaretObjectTracksModification(obj), DisplayGroupAndTabItemInterface(obj), SceneableInterface(obj), m_type(obj.m_type), m_attributeDefaultType(obj.m_attributeDefaultType) { initializeAnnotationMembers(); this->copyHelperAnnotation(obj); } /** * Assignment operator. * @param obj * Data copied from obj to this. * @return * Reference to this object. */ Annotation& Annotation::operator=(const Annotation& obj) { if (this != &obj) { CaretObjectTracksModification::operator=(obj); this->copyHelperAnnotation(obj); } return *this; } /** * Helps with copying an object of this type. * @param obj * Object that is copied. */ void Annotation::copyHelperAnnotation(const Annotation& obj) { m_annotationGroupKey.reset(); m_uniqueKey = -1; m_coordinateSpace = obj.m_coordinateSpace; m_tabIndex = obj.m_tabIndex; m_windowIndex = obj.m_windowIndex; m_lineWidth = obj.m_lineWidth; m_colorLine = obj.m_colorLine; m_colorBackground = obj.m_colorBackground; m_customColorBackground[0] = obj.m_customColorBackground[0]; m_customColorBackground[1] = obj.m_customColorBackground[1]; m_customColorBackground[2] = obj.m_customColorBackground[2]; m_customColorBackground[3] = obj.m_customColorBackground[3]; m_customColorLine[0] = obj.m_customColorLine[0]; m_customColorLine[1] = obj.m_customColorLine[1]; m_customColorLine[2] = obj.m_customColorLine[2]; m_customColorLine[3] = obj.m_customColorLine[3]; *m_displayGroupAndTabItemHelper = *obj.m_displayGroupAndTabItemHelper; /* * Initializes unique name */ m_uniqueKey = -1; m_name = ""; //setUniqueKey(m_uniqueKey); calling this will cause a crash since we could be in copy constructor and subclass has not been constructed /* * Selected status is NOT copied. */ m_selectedForEditingInWindowFlag.reset(); } /** * @return An identical copy (clone) of "this" annotation. * The type of the annotation is used to ensure the * correct type of annotation is created and returned. */ Annotation* Annotation::clone() const { Annotation* myClone = NULL; switch (getType()) { case AnnotationTypeEnum::BOX: { const AnnotationBox* box = dynamic_cast(this); CaretAssert(box); myClone = new AnnotationBox(*box); } break; case AnnotationTypeEnum::COLOR_BAR: { const AnnotationColorBar* colorBar = dynamic_cast(this); CaretAssert(colorBar); myClone = new AnnotationColorBar(*colorBar); } break; case AnnotationTypeEnum::IMAGE: { const AnnotationImage* image = dynamic_cast(this); CaretAssert(image); myClone = new AnnotationImage(*image); } break; case AnnotationTypeEnum::LINE: { const AnnotationLine* line = dynamic_cast(this); CaretAssert(line); myClone = new AnnotationLine(*line); } break; case AnnotationTypeEnum::OVAL: { const AnnotationOval* oval = dynamic_cast(this); CaretAssert(oval); myClone = new AnnotationOval(*oval); } break; case AnnotationTypeEnum::TEXT: { const AnnotationText* text = dynamic_cast(this); CaretAssert(text); switch (text->getFontSizeType()) { case AnnotationTextFontSizeTypeEnum::PERCENTAGE_OF_VIEWPORT_HEIGHT: { const AnnotationPercentSizeText* pctText = dynamic_cast(text); CaretAssert(pctText); myClone = new AnnotationPercentSizeText(*pctText); } break; case AnnotationTextFontSizeTypeEnum::POINTS: { const AnnotationPointSizeText* pointText = dynamic_cast(text); CaretAssert(pointText); myClone = new AnnotationPointSizeText(*pointText); } } } break; } CaretAssert(myClone); return myClone; } /** * Replace "this" annotation with content of the given annotation. * The annotation must by the same type and class. * * @param annotation * Annotation whose content is copied to "this" annotation. */ void Annotation::replaceWithCopyOfAnnotation(const Annotation* annotation) { CaretAssert(annotation); const AnnotationTypeEnum::Enum myType = getType(); if (myType != annotation->getType()) { CaretLogSevere("Attempting to replace " + AnnotationTypeEnum::toGuiName(myType) + " with annotation of different type: " + AnnotationTypeEnum::toGuiName(annotation->getType())); return; } *this = *annotation; } /** * @return Is this annotation deletable? This method may be overridden * by annotations (such as colorbars) that cannot be deleted. */ bool Annotation::isDeletable() const { return true; } /** * @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). */ bool Annotation::isFixedAspectRatio() const { return false; } /** * @return The aspect ratio for annotations that have a fixed aspect ratio. * This method may be overridden by annotations that require a fixed aspect ratio * (such as an image annotaiton). * * If the aspect ratio is unknown return 1. Never return zero. */ float Annotation::getFixedAspectRatio() const { return 1.0; } /** * Apply coloring including line width from annother annotation. * * @param otherAnnotation * Other annotation from which coloring is copied. */ void Annotation::applyColoringFromOther(const Annotation* otherAnnotation) { CaretAssert(otherAnnotation); m_colorBackground = otherAnnotation->m_colorBackground; m_colorLine = otherAnnotation->m_colorLine; m_lineWidth = otherAnnotation->m_lineWidth; for (int32_t i = 0; i < 4; i++) { m_customColorBackground[i] = otherAnnotation->m_customColorBackground[i]; m_customColorLine[i] = otherAnnotation->m_customColorLine[i]; } } /** * Factory method for creating an annotation of the given type. * * @param annotationType * Type of annotation that will be created. * @param attributeDefaultType * Default type for annotation attributes. * @return * New annotation of the given type. */ Annotation* Annotation::newAnnotationOfType(const AnnotationTypeEnum::Enum annotationType, const AnnotationAttributesDefaultTypeEnum::Enum attributeDefaultType) { Annotation* annotation = NULL; switch (annotationType) { case AnnotationTypeEnum::BOX: annotation = new AnnotationBox(attributeDefaultType); break; case AnnotationTypeEnum::COLOR_BAR: annotation = new AnnotationColorBar(attributeDefaultType); break; case AnnotationTypeEnum::IMAGE: annotation = new AnnotationImage(attributeDefaultType); break; case AnnotationTypeEnum::LINE: annotation = new AnnotationLine(attributeDefaultType); break; case AnnotationTypeEnum::OVAL: annotation = new AnnotationOval(attributeDefaultType); break; case AnnotationTypeEnum::TEXT: annotation = new AnnotationPercentSizeText(attributeDefaultType); break; } return annotation; } /** * Initialize members of this class. */ void Annotation::initializeAnnotationMembers() { CaretAssertMessage((m_selectedForEditingInWindowFlag.size() == BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_WINDOWS), ("m_selectedInWindowFlag (size=" + QString::number(m_selectedForEditingInWindowFlag.size()) + ") must be the same size as BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_WINDOWS (size=" + QString::number(BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_WINDOWS) + ")")); m_selectedForEditingInWindowFlag.reset(); m_coordinateSpace = AnnotationCoordinateSpaceEnum::TAB; m_tabIndex = -1; m_windowIndex = -1; m_displayGroupAndTabItemHelper = new DisplayGroupAndTabItemHelper(); /* * Default the unique identifier. * * NOTE: do not call 'setUniqueIdentifier()' * from here as the * type of annotation has not been set * and may cause a crash. */ m_uniqueKey = -1; m_annotationGroupKey.reset(); switch (m_attributeDefaultType) { case AnnotationAttributesDefaultTypeEnum::NORMAL: m_lineWidth = 3.0; m_colorBackground = CaretColorEnum::NONE; m_colorLine = CaretColorEnum::WHITE; switch (m_type) { case AnnotationTypeEnum::BOX: break; case AnnotationTypeEnum::COLOR_BAR: m_colorBackground = CaretColorEnum::BLACK; break; case AnnotationTypeEnum::IMAGE: break; case AnnotationTypeEnum::LINE: break; case AnnotationTypeEnum::OVAL: break; case AnnotationTypeEnum::TEXT: m_colorBackground = CaretColorEnum::NONE; m_colorLine = CaretColorEnum::NONE; break; } m_customColorBackground[0] = 0.0; m_customColorBackground[1] = 0.0; m_customColorBackground[2] = 0.0; m_customColorBackground[3] = 1.0; m_customColorLine[0] = 1.0; m_customColorLine[1] = 1.0; m_customColorLine[2] = 1.0; m_customColorLine[3] = 1.0; break; case AnnotationAttributesDefaultTypeEnum::USER: { m_lineWidth = s_userDefaultLineWidth; m_colorBackground = s_userDefaultColorBackground; m_colorLine = s_userDefaultColorLine; m_customColorBackground[0] = s_userDefaultCustomColorBackground[0]; m_customColorBackground[1] = s_userDefaultCustomColorBackground[1]; m_customColorBackground[2] = s_userDefaultCustomColorBackground[2]; m_customColorBackground[3] = s_userDefaultCustomColorBackground[3]; m_customColorLine[0] = s_userDefaultCustomColorLine[0]; m_customColorLine[1] = s_userDefaultCustomColorLine[1]; m_customColorLine[2] = s_userDefaultCustomColorLine[2]; m_customColorLine[3] = s_userDefaultCustomColorLine[3]; const bool lineBackNoneFlag = ((m_colorLine == CaretColorEnum::NONE) && (m_colorBackground == CaretColorEnum::NONE)); const CaretColorEnum::Enum defaultColor = CaretColorEnum::RED; switch (m_type) { case AnnotationTypeEnum::BOX: if (lineBackNoneFlag) { m_colorBackground = defaultColor; } break; case AnnotationTypeEnum::COLOR_BAR: break; case AnnotationTypeEnum::IMAGE: break; case AnnotationTypeEnum::LINE: if (m_colorLine == CaretColorEnum::NONE) { m_colorLine = defaultColor; } break; case AnnotationTypeEnum::OVAL: if (lineBackNoneFlag) { m_colorBackground = defaultColor; } break; case AnnotationTypeEnum::TEXT: m_colorLine = s_userDefaultForTextColorLine; m_customColorLine[0] = s_userDefaultForTextCustomColorLine[0]; m_customColorLine[1] = s_userDefaultForTextCustomColorLine[1]; m_customColorLine[2] = s_userDefaultForTextCustomColorLine[2]; m_customColorLine[3] = s_userDefaultForTextCustomColorLine[3]; break; } } break; } /* * May need to override colors if both are none BUT NOT for text */ if (m_type != AnnotationTypeEnum::TEXT) { if ((m_colorBackground == CaretColorEnum::NONE) && (m_colorLine == CaretColorEnum::NONE)) { m_colorLine = CaretColorEnum::WHITE; } } /* * Don't allow a line color of NONE for text or line */ bool disallowLineColorNoneFlag = false; switch (m_type) { case AnnotationTypeEnum::BOX: break; case AnnotationTypeEnum::COLOR_BAR: disallowLineColorNoneFlag = true; break; case AnnotationTypeEnum::IMAGE: break; case AnnotationTypeEnum::LINE: disallowLineColorNoneFlag = true; break; case AnnotationTypeEnum::OVAL: break; case AnnotationTypeEnum::TEXT: break; } if (disallowLineColorNoneFlag) { if (m_colorLine == CaretColorEnum::NONE) { m_colorLine = CaretColorEnum::WHITE; if (m_colorBackground == CaretColorEnum::WHITE) { m_colorBackground = CaretColorEnum::NONE; } } } /* * Note: The 'const' members are not saved to the scene as they * are set by constructor. * * The 'selected' status is not saved to the scene. * * Currently this is used for saving color bar attributes * to a scene. */ m_sceneAssistant = new SceneClassAssistant(); m_sceneAssistant->add("m_displayGroupAndTabItemHelper", "DisplayGroupAndTabItemHelper", m_displayGroupAndTabItemHelper); if (m_type == AnnotationTypeEnum::COLOR_BAR) { m_sceneAssistant->add("m_coordinateSpace", &m_coordinateSpace); m_sceneAssistant->add("m_colorBackground", &m_colorBackground); m_sceneAssistant->addArray("m_customColorBackground", m_customColorBackground, 4, 0.0); m_sceneAssistant->add("m_colorLine", &m_colorLine); m_sceneAssistant->addArray("m_customColorLine", m_customColorLine, 4, 0.0); } } /** * @return The annotation drawing type. */ AnnotationTypeEnum::Enum Annotation::getType() const { return m_type; } QString Annotation::getShortDescriptiveString() const { QString s = AnnotationTypeEnum::toGuiName(m_type); if (m_type == AnnotationTypeEnum::TEXT) { const AnnotationText* textAnn = dynamic_cast(this); s += (" \"" + textAnn->getText() + "\""); } return s; } /** * Get text displayed in the Paste Menu Items * * @param pasteMenuItemText * Text for paste menu item. * @param pasteSpecialMenuItemText * Text for paste special menu item. */ void Annotation::getTextForPasteMenuItems(AString& pasteMenuItemText, AString& pasteSpecialMenuItemText) const { AString typeName = AnnotationTypeEnum::toGuiName(m_type); switch (m_type) { case AnnotationTypeEnum::BOX: break; case AnnotationTypeEnum::COLOR_BAR: break; case AnnotationTypeEnum::IMAGE: break; case AnnotationTypeEnum::LINE: break; case AnnotationTypeEnum::OVAL: break; case AnnotationTypeEnum::TEXT: { const AnnotationText* textAnn = dynamic_cast(this); CaretAssertMessage(textAnn, "If this fails, it may be due to this method being called from a constructor " "and the subclass constructor has not yet executed."); typeName = ("\"" + textAnn->getText() + "\""); } break; } const AString spaceName = AnnotationCoordinateSpaceEnum::toGuiName(m_coordinateSpace); pasteMenuItemText = ("Paste " + typeName + " in " + spaceName + " Space"); pasteSpecialMenuItemText = ("Paste " + typeName + " and Change Space"); } /** * @return The coordinate space. */ AnnotationCoordinateSpaceEnum::Enum Annotation::getCoordinateSpace() const { return m_coordinateSpace; } /** * Set the coordinate space. * * @param coordinateSpace * New value for coordinate space. */ void Annotation::setCoordinateSpace(const AnnotationCoordinateSpaceEnum::Enum coordinateSpace) { if (m_coordinateSpace != coordinateSpace) { m_coordinateSpace = coordinateSpace; setModified(); } } /** * @return The tab index. Valid only for tab coordinate space annotations. */ int32_t Annotation::getTabIndex() const { return m_tabIndex; } /** * Set tab index. Valid only for tab coordinate space annotations. * * @param tabIndex */ void Annotation::setTabIndex(const int32_t tabIndex) { if (tabIndex != m_tabIndex) { m_tabIndex = tabIndex; setModified(); } } /** * @return The window index. Valid only for window coordinate space annotations. */ int32_t Annotation::getWindowIndex() const { return m_windowIndex; } /** * Set window index. Valid only for window coordinate space annotations. * * @param tabIndex */ void Annotation::setWindowIndex(const int32_t windowIndex) { if (windowIndex != m_windowIndex) { m_windowIndex = windowIndex; setModified(); } } /** * Get a description of this object's content. * @return String describing this object's content. */ AString Annotation::toString() const { AString msg("Annotation type=" + AnnotationTypeEnum::toName(m_type)); msg += (" space=" + AnnotationCoordinateSpaceEnum::toGuiName(getCoordinateSpace())); const AnnotationText* textAnn = dynamic_cast(this); if (textAnn != NULL) { msg += (" text=" + textAnn->getText()); } return msg; } /** * @return The line width. */ float Annotation::getLineWidth() const { return m_lineWidth; } /** * Set the line width. * * @param lineWidth * New value for line width. */ void Annotation::setLineWidth(const float lineWidth) { if (lineWidth != m_lineWidth) { m_lineWidth = 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. */ bool Annotation::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 Annotation::isBackgroundColorSupported() const { return true; } /** * @return The line color. */ CaretColorEnum::Enum Annotation::getLineColor() const { return m_colorLine; } /** * Set the line color. * * @param color * New value for line color. */ void Annotation::setLineColor(const CaretColorEnum::Enum color) { if (m_colorLine != color) { m_colorLine = color; setModified(); } } /** * Get the line color's RGBA components regardless of * coloring (custom color or a CaretColorEnum) selected by the user. * * @param rgbaOut * RGBA components ranging 0.0 to 1.0. */ void Annotation::getLineColorRGBA(float rgbaOut[4]) const { switch (m_colorLine) { case CaretColorEnum::NONE: rgbaOut[0] = 0.0; rgbaOut[1] = 0.0; rgbaOut[2] = 0.0; rgbaOut[3] = 0.0; break; case CaretColorEnum::CUSTOM: getCustomLineColor(rgbaOut); break; case CaretColorEnum::AQUA: case CaretColorEnum::BLACK: case CaretColorEnum::BLUE: case CaretColorEnum::FUCHSIA: case CaretColorEnum::GRAY: case CaretColorEnum::GREEN: case CaretColorEnum::LIME: case CaretColorEnum::MAROON: case CaretColorEnum::NAVY: case CaretColorEnum::OLIVE: case CaretColorEnum::PURPLE: case CaretColorEnum::RED: case CaretColorEnum::SILVER: case CaretColorEnum::TEAL: case CaretColorEnum::WHITE: case CaretColorEnum::YELLOW: CaretColorEnum::toRGBFloat(m_colorLine, rgbaOut); rgbaOut[3] = 1.0; break; } } /** * Get the line color's RGBA components regardless of * coloring (custom color or a CaretColorEnum) selected by the user. * * @param rgbaOut * RGBA components ranging 0 to 255. */ void Annotation::getLineColorRGBA(uint8_t rgbaOut[4]) const { float rgbaFloat[4] = { 0.0, 0.0, 0.0, 0.0 }; getLineColorRGBA(rgbaFloat); 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] = static_cast(rgbaFloat[3] * 255.0); } /** * @return The background color. */ CaretColorEnum::Enum Annotation::getBackgroundColor() const { return m_colorBackground; } /** * Set the background color. * * @param color * New value for background color. */ void Annotation::setBackgroundColor(const CaretColorEnum::Enum color) { if (m_colorBackground != color) { m_colorBackground = color; setModified(); } } /** * Get the background color's RGBA components regardless of * coloring (custom color or a CaretColorEnum) selected by the user. * * @param rgbaOut * RGBA components ranging 0.0 to 1.0. */ void Annotation::getBackgroundColorRGBA(float rgbaOut[4]) const { switch (m_colorBackground) { case CaretColorEnum::NONE: rgbaOut[0] = 0.0; rgbaOut[1] = 0.0; rgbaOut[2] = 0.0; rgbaOut[3] = 0.0; break; case CaretColorEnum::CUSTOM: getCustomBackgroundColor(rgbaOut); break; case CaretColorEnum::AQUA: case CaretColorEnum::BLACK: case CaretColorEnum::BLUE: case CaretColorEnum::FUCHSIA: case CaretColorEnum::GRAY: case CaretColorEnum::GREEN: case CaretColorEnum::LIME: case CaretColorEnum::MAROON: case CaretColorEnum::NAVY: case CaretColorEnum::OLIVE: case CaretColorEnum::PURPLE: case CaretColorEnum::RED: case CaretColorEnum::SILVER: case CaretColorEnum::TEAL: case CaretColorEnum::WHITE: case CaretColorEnum::YELLOW: CaretColorEnum::toRGBFloat(m_colorBackground, rgbaOut); rgbaOut[3] = 1.0; break; } } /** * Get the background color's RGBA components regardless of * coloring (custom color or a CaretColorEnum) selected by the user. * * @param rgbaOut * RGBA components ranging 0 to 255. */ void Annotation::getBackgroundColorRGBA(uint8_t rgbaOut[4]) const { float rgbaFloat[4] = { 0.0, 0.0, 0.0, 0.0 }; getBackgroundColorRGBA(rgbaFloat); 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] = static_cast(rgbaFloat[3] * 255.0); } /** * Get the custom line color. * * @param rgbaOut * RGBA components (red, green, blue, alpha) each of which ranges [0.0, 1.0]. */ void Annotation::getCustomLineColor(float rgbaOut[4]) const { rgbaOut[0] = m_customColorLine[0]; rgbaOut[1] = m_customColorLine[1]; rgbaOut[2] = m_customColorLine[2]; rgbaOut[3] = m_customColorLine[3]; } /** * Get the custom line color. * * @param rgbaOut * RGBA components (red, green, blue, alpha) each of which ranges [0, 255]. */ void Annotation::getCustomLineColor(uint8_t rgbaOut[4]) const { rgbaOut[0] = static_cast(m_customColorLine[0] * 255.0); rgbaOut[1] = static_cast(m_customColorLine[1] * 255.0); rgbaOut[2] = static_cast(m_customColorLine[2] * 255.0); rgbaOut[3] = static_cast(m_customColorLine[3] * 255.0); } /** * Set the custom line color with floats. * * @param rgba * RGBA components (red, green, blue, alpha) each of which ranges [0.0, 1.0]. */ void Annotation::setCustomLineColor(const float rgba[4]) { for (int32_t i = 0; i < 4; i++) { if (rgba[i] != m_customColorLine[i]) { m_customColorLine[i] = rgba[i]; setModified(); } } } /** * Set the custom line color with unsigned bytes. * * @param rgba * RGBA components (red, green, blue, alpha) each of which ranges [0, 255]. */ void Annotation::setCustomLineColor(const uint8_t rgba[4]) { for (int32_t i = 0; i < 4; i++) { const float component = rgba[i] / 255.0; if (component != m_customColorLine[i]) { m_customColorLine[i] = component; setModified(); } } } /** * Get the background color. * * @param rgbaOut * RGBA components (red, green, blue, alpha) each of which ranges [0.0, 1.0]. */ void Annotation::getCustomBackgroundColor(float rgbaOut[4]) const { rgbaOut[0] = m_customColorBackground[0]; rgbaOut[1] = m_customColorBackground[1]; rgbaOut[2] = m_customColorBackground[2]; rgbaOut[3] = m_customColorBackground[3]; } /** * Get the background color. * * @param rgbaOut * RGBA components (red, green, blue, alpha) each of which ranges [0, 255]. */ void Annotation::getCustomBackgroundColor(uint8_t rgbaOut[4]) const { rgbaOut[0] = static_cast(m_customColorBackground[0] * 255.0); rgbaOut[1] = static_cast(m_customColorBackground[1] * 255.0); rgbaOut[2] = static_cast(m_customColorBackground[2] * 255.0); rgbaOut[3] = static_cast(m_customColorBackground[3] * 255.0); } /** * Set the background color with floats. * The background color is applied only when its alpha component is greater than zero. * * @param rgba * RGBA components (red, green, blue, alpha) each of which ranges [0.0, 1.0]. */ void Annotation::setCustomBackgroundColor(const float rgba[4]) { for (int32_t i = 0; i < 4; i++) { if (rgba[i] != m_customColorBackground[i]) { m_customColorBackground[i] = rgba[i]; setModified(); } } } /** * Set the background color with bytes. * The background color is applied only when its alpha component is greater than zero. * * @param rgba * RGBA components (red, green, blue, alpha) each of which ranges [0, 255]. */ void Annotation::setCustomBackgroundColor(const uint8_t rgba[4]) { for (int32_t i = 0; i < 4; i++) { const float component = rgba[i] / 255.0; if (component != m_customColorBackground[i]) { m_customColorBackground[i] = component; setModified(); } } } /** * @return The key to the annotation group that owns this annotation. */ AnnotationGroupKey Annotation::getAnnotationGroupKey() const { return m_annotationGroupKey; } /** * Set the annotation group key. * * @param annotationGroupKey * The key to the annotation group that contains this annotation. */ void Annotation::setAnnotationGroupKey(const AnnotationGroupKey& annotationGroupKey) { AnnotationGroupKey newGroupKeyForAnnotation = annotationGroupKey; switch (newGroupKeyForAnnotation.getGroupType()) { case AnnotationGroupTypeEnum::INVALID: CaretAssertMessage(0, "Do not call this method with invalid key. " "Instead call invalidateAnnotationGroupKey()."); break; case AnnotationGroupTypeEnum::SPACE: CaretAssert(newGroupKeyForAnnotation.getSpaceGroupUniqueKey() > 0); /* * When an annotation is moved to a space group (from a user group), * we want to preserve the user group unique key so that it can * be used to 'regroup' annotations */ if (m_annotationGroupKey.getGroupType() == AnnotationGroupTypeEnum::USER) { newGroupKeyForAnnotation.setUserGroupUniqueKey(m_annotationGroupKey.getUserGroupUniqueKey()); } break; case AnnotationGroupTypeEnum::USER: CaretAssert(newGroupKeyForAnnotation.getUserGroupUniqueKey() > 0); break; } m_annotationGroupKey = newGroupKeyForAnnotation; } /** * Set the annotation group key so that it is invalid. */ void Annotation::invalidateAnnotationGroupKey() { m_annotationGroupKey = AnnotationGroupKey(); } /** * Set the unique key for this annotation. This method is * called by the annotation file when the annotation * is added to the file. * * @param uniqueKey * Unique key displayed in an annotation name. */ void Annotation::setUniqueKey(const int32_t uniqueKey) { m_uniqueKey = uniqueKey; textAnnotationResetName(); } /** * @return Unique key displayed in annotation name. */ int32_t Annotation::getUniqueKey() const { return m_uniqueKey; } /** * @return Name of annotation. */ AString Annotation::getName() const { return m_name; } /** * Called by text annotation to reset the name * displayed in the gui. DO NOT CALL FROM * A CONSTRUCTOR !!! */ void Annotation::textAnnotationResetName() { AString suffixName; switch (m_type) { case AnnotationTypeEnum::BOX: break; case AnnotationTypeEnum::COLOR_BAR: break; case AnnotationTypeEnum::IMAGE: break; case AnnotationTypeEnum::LINE: break; case AnnotationTypeEnum::OVAL: break; case AnnotationTypeEnum::TEXT: { const AnnotationText* textAnn = dynamic_cast(this); CaretAssertMessage(textAnn, "If this fails, it may be due to this method being called from a constructor " "and the subclass constructor has not yet executed."); suffixName = (": " + textAnn->getText()); } break; } m_name = (AnnotationTypeEnum::toGuiName(m_type) + " " + AString::number(m_uniqueKey) + suffixName); } /** * Set the drawn status to true for the given window. * * @param windowIndex * Index of the window. * * This method is called from the graphics * code when an annotation is drawn. * * NOTE: To find the annotations drawn in a window, * the drawn status is cleared, annotations are drawn * in one window, and the graphics system sets the * drawn status for annotations that are drawn. * A query is then made to find all annotations with * the drawn status set. */ void Annotation::setDrawnInWindowStatus(const int32_t windowIndex) { CaretAssertArrayIndex(m_drawnInWindowStatus, BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_WINDOWS, windowIndex); m_drawnInWindowStatus[windowIndex] = true; } /** * Is the drawn status set for the given window. * * @param windowIndex * Index of the window. * * NOTE: To find the annotations drawn in a window, * the drawn status is cleared, annotations are drawn * in one window, and the graphics system sets the * drawn status for annotations that are drawn. * A query is then made to find all annotations with * the drawn status set. */ bool Annotation::isDrawnInWindowStatus(const int32_t windowIndex) { CaretAssertArrayIndex(m_drawnInWindowStatus, BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_WINDOWS, windowIndex); return m_drawnInWindowStatus[windowIndex]; } /** * Clear the drawn status for all windows. * * This method is called by the Annotation File * containing this annotation to clear the drawn status. * * NOTE: To find the annotations drawn in a window, * the drawn status is cleared, annotations are drawn * in one window, and the graphics system sets the * drawn status for annotations that are drawn. * A query is then made to find all annotations with * the drawn status set. */ void Annotation::clearDrawnInWindowStatusForAllWindows() { for (int32_t iWindow = 0; iWindow < BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_WINDOWS; iWindow++) { CaretAssertArrayIndex(m_drawnInWindowStatus, BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_WINDOWS, iWindow); m_drawnInWindowStatus[iWindow] = false; } } /** * @return The annotation's selected for editing status. * * Note: (1) The selection status is never saved to a scene * or file. (2) Changing the selection status DOES NOT * alter the annotation's modified status. * * @param windowIndex * Window for annotation selection status. */ bool Annotation::isSelectedForEditing(const int32_t windowIndex) const { CaretAssert((windowIndex >= 0) && (windowIndex < static_cast(m_selectedForEditingInWindowFlag.size()))); return m_selectedForEditingInWindowFlag.test(windowIndex); } /** * Set the annotation's selected for editing status. * * This method is private - AnnotationManager handles selection and allowing * public access to this method could cause improper selection status. * * Note: (1) The selection status is never saved to a scene * or file. (2) Changing the selection status DOES NOT * alter the annotation's modified status. * * @param windowIndex * Window for annotation selection. * @param selectedStatus * New selection status. */ void Annotation::setSelectedForEditing(const int32_t windowIndex, const bool selectedStatus) const { CaretAssert((windowIndex >= 0) && (windowIndex < static_cast(m_selectedForEditingInWindowFlag.size()))); if (selectedStatus) { m_selectedForEditingInWindowFlag.set(windowIndex); } else { m_selectedForEditingInWindowFlag.reset(windowIndex); } } /** * Set the annotation's selected for editing status to deselected. */ void Annotation::setDeselectedForEditing() { /* * Clear selected status in ALL windows */ m_selectedForEditingInWindowFlag.reset(); } /** * Convert a relative (zero to one) XYZ coordinate to * a viewport coordinate. * * @param relativeXYZ * The relative (zero to one) XYZ. * @param viewportWidth * Width of the viewport. * @param viewportHeight * Height of the viewport. * @param viewportXYZOut * Output viewport coordinate. */ void Annotation::relativeXYZToViewportXYZ(const float relativeXYZ[3], const float viewportWidth, const float viewportHeight, float viewportXYZOut[3]) { viewportXYZOut[0] = (relativeXYZ[0] / 100.0) * viewportWidth; viewportXYZOut[1] = (relativeXYZ[1] / 100.0) * viewportHeight; viewportXYZOut[2] = relativeXYZ[2]; } /** * Convert a viewport XYZ coordinate to * a relative coordinate. The output relative * coordinates are may be outside of the range * zero to one if viewport coordinate is outside * of the viewport bounds. * * @param viewportXYZ * The relative (zero to one) XYZ. * @param viewportWidth * Width of the viewport. * @param viewportHeight * Height of the viewport. * @param relativeXYZOut * Output relative coordinate. */ void Annotation::viewportXYZToRelativeXYZ(const float viewportXYZ[3], const float viewportWidth, const float viewportHeight, float relativeXYZOut[3]) { relativeXYZOut[0] = 100.0 * (viewportXYZ[0] / viewportWidth); relativeXYZOut[1] = 100.0 * (viewportXYZ[1] / viewportHeight); relativeXYZOut[2] = viewportXYZ[2]; } /** * Convert a viewport XYZ coordinate to * a relative coordinate. The output relative coordinate * will be limited to the range zero to one even if the * viewport coordinate is outside of the viewport bounds. * * @param viewportXYZ * The relative (zero to one) XYZ. * @param viewportWidth * Width of the viewport. * @param viewportHeight * Height of the viewport. * @param relativeXYZOut * Output relative coordinate. */ void Annotation::viewportXYZToLimitedRelativeXYZ(const float viewportXYZ[3], const float viewportWidth, const float viewportHeight, float relativeXYZOut[3]) { viewportXYZToRelativeXYZ(viewportXYZ, viewportWidth, viewportHeight, relativeXYZOut); relativeXYZOut[0] = MathFunctions::limitRange(relativeXYZOut[0], 0.0f, 1.0f); relativeXYZOut[1] = MathFunctions::limitRange(relativeXYZOut[1], 0.0f, 1.0f); relativeXYZOut[2] = MathFunctions::limitRange(relativeXYZOut[2], 0.0f, 1.0f); } /** * 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* Annotation::saveToScene(const SceneAttributes* sceneAttributes, const AString& instanceName) { SceneClass* sceneClass = new SceneClass(instanceName, "Annotation", 1); 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 Annotation::restoreFromScene(const SceneAttributes* sceneAttributes, const SceneClass* sceneClass) { if (sceneClass == NULL) { return; } m_sceneAssistant->restoreMembers(sceneAttributes, sceneClass); restoreSubClassDataFromScene(sceneAttributes, sceneClass); } /** * @return Number of children. */ int32_t Annotation::getNumberOfItemChildren() const { /* * Annotation has no children. */ return 0; } /** * Get child at the given index. * * @param index * Index of the child. * @return * Child at the given index. */ DisplayGroupAndTabItemInterface* Annotation::getItemChild(const int32_t /* index */) const { /* * Annotation has no children. */ return NULL; } /** * @return Children of this item. */ std::vector Annotation::getItemChildren() const { /* * Annotation has no children. */ std::vector children; return children; } /** * @return Parent of this item. */ DisplayGroupAndTabItemInterface* Annotation::getItemParent() const { return m_displayGroupAndTabItemHelper->getParent(); } /** * Set the parent of this item. * * @param itemParent * Parent of this item. */ void Annotation::setItemParent(DisplayGroupAndTabItemInterface* itemParent) { m_displayGroupAndTabItemHelper->setParent(itemParent); } /** * @return Name of this item. */ AString Annotation::getItemName() const { return getName(); } /** * Get the icon color for this item. Icon is filled with background * color, outline color is drawn around edges, and text color is small * square in center. For any colors that do not apply, use an alpha * value (last element) of zero. * * @param backgroundRgbaOut * Red, green, blue, alpha components for background ranging [0, 1]. * @param outlineRgbaOut * Red, green, blue, alpha components for outline ranging [0, 1]. * @param textRgbaOut * Red, green, blue, alpha components for text ranging [0, 1]. */ void Annotation::getItemIconColorsRGBA(float backgroundRgbaOut[4], float outlineRgbaOut[4], float textRgbaOut[4]) const { for (int32_t i = 0; i < 4; i++) { backgroundRgbaOut[i] = 0.0; outlineRgbaOut[i] = 0.0; textRgbaOut[i] = 0.0; } getBackgroundColorRGBA(backgroundRgbaOut); getLineColorRGBA(outlineRgbaOut); /* * Note: AnnotationText overrides this method to set text color */ textRgbaOut[0] = 0.0; textRgbaOut[1] = 0.0; textRgbaOut[2] = 0.0; textRgbaOut[3] = 0.0; } /** * @return This item can be expanded. */ bool Annotation::isItemExpandable() const { return false; } /** * @return Is this item expanded in the given display group/tab? * * Note: If this annotation is in window space, the display group * and tab are ignored and window status is used with the window index * of the annotation. * * @param displayGroup * The display group. * @param tabIndex * Index of the tab. */ bool Annotation::isItemExpanded(const DisplayGroupEnum::Enum displayGroup, const int32_t tabIndex) const { if (m_coordinateSpace == AnnotationCoordinateSpaceEnum::WINDOW) { return m_displayGroupAndTabItemHelper->isExpandedInWindow(m_windowIndex); } return m_displayGroupAndTabItemHelper->isExpanded(displayGroup, tabIndex); } /** * Set this item's expanded status in the given display group/tab. * * Note: If this annotation is in window space, the display group * and tab are ignored and window status is used with the window index * of the annotation. * * @param displayGroup * The display group. * @param tabIndex * Index of the tab. * @param status * New expanded status. */ void Annotation::setItemExpanded(const DisplayGroupEnum::Enum displayGroup, const int32_t tabIndex, const bool status) { if (m_coordinateSpace == AnnotationCoordinateSpaceEnum::WINDOW) { m_displayGroupAndTabItemHelper->setExpandedInWindow(m_windowIndex, status); } else { m_displayGroupAndTabItemHelper->setExpanded(displayGroup, tabIndex, status); } } /** * Get display selection status in the given display group/tab? * * Note: If this annotation is in window space, the display group * and tab are ignored and window status is used with the window index * of the annotation. * * @param displayGroup * The display group. * @param tabIndex * Index of the tab. */ TriStateSelectionStatusEnum::Enum Annotation::getItemDisplaySelected(const DisplayGroupEnum::Enum displayGroup, const int32_t tabIndex) const { if (m_coordinateSpace == AnnotationCoordinateSpaceEnum::WINDOW) { return m_displayGroupAndTabItemHelper->getSelectedInWindow(m_windowIndex); } return m_displayGroupAndTabItemHelper->getSelected(displayGroup, tabIndex); } /** * Set display this item selected in the given display group/tab. * * Note: If this annotation is in window space, the display group * and tab are ignored and window status is used with the window index * of the annotation. * * @param displayGroup * The display group. * @param tabIndex * Index of the tab. * @param status * New selection status. */ void Annotation::setItemDisplaySelected(const DisplayGroupEnum::Enum displayGroup, const int32_t tabIndex, const TriStateSelectionStatusEnum::Enum status) { if (m_coordinateSpace == AnnotationCoordinateSpaceEnum::WINDOW) { m_displayGroupAndTabItemHelper->setSelectedInWindow(m_windowIndex, status); } else { m_displayGroupAndTabItemHelper->setSelected(displayGroup, tabIndex, status); } } /** * Is this item selected for editing in the given window? * * @param windowIndex * Index of the window. * @return * Selection status. */ bool Annotation::isItemSelectedForEditingInWindow(const int32_t windowIndex) { return isSelectedForEditing(windowIndex); } /** * Set the default value for line color * * @param color * Default for newly created annotations. */ void Annotation::setUserDefaultLineColor(const CaretColorEnum::Enum color) { s_userDefaultColorLine = color; } /** * Set the default value for custom line color * * @param rgba * Default for newly created annotations. */ void Annotation::setUserDefaultCustomLineColor(const float rgba[4]) { s_userDefaultCustomColorLine[0] = rgba[0]; s_userDefaultCustomColorLine[1] = rgba[1]; s_userDefaultCustomColorLine[2] = rgba[2]; s_userDefaultCustomColorLine[3] = rgba[3]; } /** * Set the default value for line color FOR USE BY TEXT ONLY * * @param color * Default for newly created annotations. */ void Annotation::setUserDefaultForTextLineColor(const CaretColorEnum::Enum color) { s_userDefaultForTextColorLine = color; } /** * Set the default value for custom line color FOR USE BY TEXT ONLY * * @param rgba * Default for newly created annotations. */ void Annotation::setUserDefaultForTextCustomLineColor(const float rgba[4]) { s_userDefaultForTextCustomColorLine[0] = rgba[0]; s_userDefaultForTextCustomColorLine[1] = rgba[1]; s_userDefaultForTextCustomColorLine[2] = rgba[2]; s_userDefaultForTextCustomColorLine[3] = rgba[3]; } /** * Set the default value for background color * * @param color * Default for newly created annotations. */ void Annotation::setUserDefaultBackgroundColor(const CaretColorEnum::Enum color) { s_userDefaultColorBackground = color; } /** * Set the default value for custom background color * * @param rgba * Default for newly created annotations. */ void Annotation::setUserDefaultCustomBackgroundColor(const float rgba[4]) { s_userDefaultCustomColorBackground[0] = rgba[0]; s_userDefaultCustomColorBackground[1] = rgba[1]; s_userDefaultCustomColorBackground[2] = rgba[2]; s_userDefaultCustomColorBackground[3] = rgba[3]; } /** * Set the default value for line width * * @param lineWidth * Default for newly created annotations. */ void Annotation::setUserDefaultLineWidth(const float lineWidth) { s_userDefaultLineWidth = lineWidth; } connectome-workbench-1.2.3+git41-gc4c6c90/src/Annotations/Annotation.h000066400000000000000000000313041300200146000253250ustar00rootroot00000000000000#ifndef __ANNOTATION_H__ #define __ANNOTATION_H__ /*LICENSE_START*/ /* * Copyright (C) 2015 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 "AnnotationAttributesDefaultTypeEnum.h" #include "AnnotationCoordinateSpaceEnum.h" #include "AnnotationGroupKey.h" #include "AnnotationSizingHandleTypeEnum.h" #include "AnnotationTypeEnum.h" #include "CaretColorEnum.h" #include "CaretObjectTracksModification.h" #include "DisplayGroupAndTabItemInterface.h" #include "SceneableInterface.h" namespace caret { class AnnotationSpatialModification; class DisplayGroupAndTabItemHelper; class SceneClassAssistant; class Annotation : public CaretObjectTracksModification, public DisplayGroupAndTabItemInterface, public SceneableInterface { public: Annotation(const AnnotationTypeEnum::Enum type, const AnnotationAttributesDefaultTypeEnum::Enum attributeDefaultType); virtual ~Annotation(); Annotation(const Annotation& obj); Annotation& operator=(const Annotation& obj); static Annotation* newAnnotationOfType(const AnnotationTypeEnum::Enum annotationType, const AnnotationAttributesDefaultTypeEnum::Enum attributeDefaultType); Annotation* clone() const; AnnotationGroupKey getAnnotationGroupKey() const; int32_t getUniqueKey() const; AString getName() const; void replaceWithCopyOfAnnotation(const Annotation* annotation); QString getShortDescriptiveString() const; void getTextForPasteMenuItems(AString& pasteMenuItemText, AString& pasteSpecialMenuItemText) const; AnnotationTypeEnum::Enum getType() const ; AnnotationCoordinateSpaceEnum::Enum getCoordinateSpace() const; void setCoordinateSpace(const AnnotationCoordinateSpaceEnum::Enum coordinateSpace); int32_t getTabIndex() const; void setTabIndex(const int32_t tabIndex); int32_t getWindowIndex() const; void setWindowIndex(const int32_t windowIndex); CaretColorEnum::Enum getLineColor() const; void setLineColor(const CaretColorEnum::Enum color); void getLineColorRGBA(float rgbaOut[4]) const; void getLineColorRGBA(uint8_t rgbaOut[4]) const; CaretColorEnum::Enum getBackgroundColor() const; void setBackgroundColor(const CaretColorEnum::Enum color); void getBackgroundColorRGBA(float rgbaOut[4]) const; void getBackgroundColorRGBA(uint8_t rgbaOut[4]) const; void getCustomLineColor(float rgbaOut[4]) const; void getCustomLineColor(uint8_t rgbaOut[4]) const; void setCustomLineColor(const float rgba[4]); void setCustomLineColor(const uint8_t rgba[4]); void getCustomBackgroundColor(float rgbaOut[4]) const; void getCustomBackgroundColor(uint8_t rgbaOut[4]) const; void setCustomBackgroundColor(const float rgba[4]); void setCustomBackgroundColor(const uint8_t rgba[4]); float getLineWidth() const; void setLineWidth(const float lineWidth); virtual bool isLineWidthSupported() const; virtual bool isBackgroundColorSupported() const; bool isSelectedForEditing(const int32_t windowIndex) const; static void setUserDefaultLineColor(const CaretColorEnum::Enum color); static void setUserDefaultCustomLineColor(const float rgba[4]); static void setUserDefaultForTextLineColor(const CaretColorEnum::Enum color); static void setUserDefaultForTextCustomLineColor(const float rgba[4]); static void setUserDefaultBackgroundColor(const CaretColorEnum::Enum color); static void setUserDefaultCustomBackgroundColor(const float rgba[4]); static void setUserDefaultLineWidth(const float lineWidth); virtual bool isDeletable() const; virtual bool isFixedAspectRatio() const; virtual float getFixedAspectRatio() const; /** * Apply a spatial modification to an annotation. * * @param spatialModification * Contains information about the spatial modification. * @return * True if the annotation was modified, else false. */ virtual bool applySpatialModification(const AnnotationSpatialModification& spatialModification) = 0; /** * Is the given sizing handle valid for this annotation? * * @sizingHandle * The sizing handle. * @return * True if sizing handle valid, else false. */ virtual bool isSizeHandleValid(const AnnotationSizingHandleTypeEnum::Enum sizingHandle) const = 0; virtual void applyColoringFromOther(const Annotation* otherAnnotation); virtual void applyCoordinatesSizeAndRotationFromOther(const Annotation* otherAnnotation) = 0; static void relativeXYZToViewportXYZ(const float relativeXYZ[3], const float viewportWidth, const float viewportHeight, float viewportXYZOut[3]); static void viewportXYZToRelativeXYZ(const float viewportXYZ[3], const float viewportWidth, const float viewportHeight, float relativeXYZOut[3]); static void viewportXYZToLimitedRelativeXYZ(const float viewportXYZ[3], const float viewportWidth, const float viewportHeight, float relativeXYZOut[3]); // 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); virtual int32_t getNumberOfItemChildren() const; virtual DisplayGroupAndTabItemInterface* getItemChild(const int32_t index) const; virtual std::vector getItemChildren() const; virtual DisplayGroupAndTabItemInterface* getItemParent() const; virtual void setItemParent(DisplayGroupAndTabItemInterface* itemParent); virtual AString getItemName() const; virtual void getItemIconColorsRGBA(float backgroundRgbaOut[4], float outlineRgbaOut[4], float textRgbaOut[4]) const; virtual bool isItemExpandable() const; virtual bool isItemExpanded(const DisplayGroupEnum::Enum displayGroup, const int32_t tabIndex) const; virtual void setItemExpanded(const DisplayGroupEnum::Enum displayGroup, const int32_t tabIndex, const bool status); virtual TriStateSelectionStatusEnum::Enum getItemDisplaySelected(const DisplayGroupEnum::Enum displayGroup, const int32_t tabIndex) const; virtual void setItemDisplaySelected(const DisplayGroupEnum::Enum displayGroup, const int32_t tabIndex, const TriStateSelectionStatusEnum::Enum status); virtual bool isItemSelectedForEditingInWindow(const int32_t windowIndex); void setDrawnInWindowStatus(const int32_t windowIndex); protected: virtual void saveSubClassDataToScene(const SceneAttributes* sceneAttributes, SceneClass* sceneClass) = 0; virtual void restoreSubClassDataFromScene(const SceneAttributes* sceneAttributes, const SceneClass* sceneClass) = 0; private: void copyHelperAnnotation(const Annotation& obj); void initializeAnnotationMembers(); // private - AnnotationManager handles selection and allowing // public access to this method could cause improper selection status void setSelectedForEditing(const int32_t windowIndex, const bool selectedStatus) const; const AnnotationTypeEnum::Enum m_type; protected: void setDeselectedForEditing(); const AnnotationAttributesDefaultTypeEnum::Enum m_attributeDefaultType; void textAnnotationResetName(); bool isDrawnInWindowStatus(const int32_t windowIndex); void clearDrawnInWindowStatusForAllWindows(); private: void setAnnotationGroupKey(const AnnotationGroupKey& annotationGroupKey); void invalidateAnnotationGroupKey(); void setUniqueKey(const int32_t uniqueKey); SceneClassAssistant* m_sceneAssistant; DisplayGroupAndTabItemHelper* m_displayGroupAndTabItemHelper; AnnotationCoordinateSpaceEnum::Enum m_coordinateSpace; int32_t m_tabIndex; int32_t m_windowIndex; CaretColorEnum::Enum m_colorLine; CaretColorEnum::Enum m_colorBackground; float m_customColorLine[4]; float m_customColorBackground[4]; float m_lineWidth; AString m_name; int32_t m_uniqueKey; AnnotationGroupKey m_annotationGroupKey; bool m_drawnInWindowStatus[BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_WINDOWS]; /** * 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. */ mutable std::bitset<10> m_selectedForEditingInWindowFlag; // defaults static CaretColorEnum::Enum s_userDefaultColorLine; static float s_userDefaultCustomColorLine[4]; static CaretColorEnum::Enum s_userDefaultForTextColorLine; static float s_userDefaultForTextCustomColorLine[4]; static CaretColorEnum::Enum s_userDefaultColorBackground; static float s_userDefaultCustomColorBackground[4]; static float s_userDefaultLineWidth; // ADD_NEW_MEMBERS_HERE friend class AnnotationFile; friend class AnnotationFileXmlReader; friend class AnnotationGroup; friend class AnnotationManager; }; #ifdef __ANNOTATION_DECLARE__ CaretColorEnum::Enum Annotation::s_userDefaultColorLine = CaretColorEnum::NONE; float Annotation::s_userDefaultCustomColorLine[4] = { 1.0, 1.0, 1.0, 1.0 }; CaretColorEnum::Enum Annotation::s_userDefaultForTextColorLine = CaretColorEnum::NONE; float Annotation::s_userDefaultForTextCustomColorLine[4] = { 1.0, 1.0, 1.0, 1.0 }; CaretColorEnum::Enum Annotation::s_userDefaultColorBackground = CaretColorEnum::NONE; float Annotation::s_userDefaultCustomColorBackground[4] = { 0.0, 0.0, 0.0, 1.0 }; float Annotation::s_userDefaultLineWidth = 3.0; #endif // __ANNOTATION_DECLARE__ } // namespace #endif //__ANNOTATION_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Annotations/AnnotationAlignmentEnum.cxx000066400000000000000000000264021300200146000303670ustar00rootroot00000000000000 /*LICENSE_START*/ /* * Copyright (C) 2015 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 __ANNOTATION_ALIGNMENT_ENUM_DECLARE__ #include "AnnotationAlignmentEnum.h" #undef __ANNOTATION_ALIGNMENT_ENUM_DECLARE__ #include "CaretAssert.h" using namespace caret; /** * \class caret::AnnotationAlignmentEnum * \brief Enumerated type for aligning multiple annotations. * * 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_annotationAlignmentEnumComboBox; * * Declare a slot that is called when user changes selection * private slots: * void annotationAlignmentEnumComboBoxItemActivated(); * * Implementation File (.cxx) * Include the header files * #include "EnumComboBoxTemplate.h" * #include "AnnotationAlignmentEnum.h" * * Instatiate: * m_annotationAlignmentEnumComboBox = new EnumComboBoxTemplate(this); * m_annotationAlignmentEnumComboBox->setup(); * * Get notified when the user changes the selection: * QObject::connect(m_annotationAlignmentEnumComboBox, SIGNAL(itemActivated()), * this, SLOT(annotationAlignmentEnumComboBoxItemActivated())); * * Update the selection: * m_annotationAlignmentEnumComboBox->setSelectedItem(NEW_VALUE); * * Read the selection: * const AnnotationAlignmentEnum::Enum VARIABLE = m_annotationAlignmentEnumComboBox->getSelectedItem(); * */ /** * Constructor. * * @param enumValue * An enumerated value. * @param name * Name of enumerated value. * * @param guiName * User-friendly name for use in user-interface. */ AnnotationAlignmentEnum::AnnotationAlignmentEnum(const Enum enumValue, const AString& name, const AString& guiName) { this->enumValue = enumValue; this->integerCode = integerCodeCounter++; this->name = name; this->guiName = guiName; } /** * Destructor. */ AnnotationAlignmentEnum::~AnnotationAlignmentEnum() { } /** * Initialize the enumerated metadata. */ void AnnotationAlignmentEnum::initialize() { if (initializedFlag) { return; } initializedFlag = true; enumData.push_back(AnnotationAlignmentEnum(ALIGN_LEFT, "ALIGN_LEFT", "Align Left")); enumData.push_back(AnnotationAlignmentEnum(ALIGN_CENTER, "ALIGN_CENTER", "Align Center")); enumData.push_back(AnnotationAlignmentEnum(ALIGN_RIGHT, "ALIGN_RIGHT", "Align Right")); enumData.push_back(AnnotationAlignmentEnum(ALIGN_TOP, "ALIGN_TOP", "Align Top")); enumData.push_back(AnnotationAlignmentEnum(ALIGN_MIDDLE, "ALIGN_MIDDLE", "Align Middle")); enumData.push_back(AnnotationAlignmentEnum(ALIGN_BOTTOM, "ALIGN_BOTTOM", "Align Bottom")); } /** * 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 AnnotationAlignmentEnum* AnnotationAlignmentEnum::findData(const Enum enumValue) { if (initializedFlag == false) initialize(); size_t num = enumData.size(); for (size_t i = 0; i < num; i++) { const AnnotationAlignmentEnum* 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 AnnotationAlignmentEnum::toName(Enum enumValue) { if (initializedFlag == false) initialize(); const AnnotationAlignmentEnum* 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. */ AnnotationAlignmentEnum::Enum AnnotationAlignmentEnum::fromName(const AString& name, bool* isValidOut) { if (initializedFlag == false) initialize(); bool validFlag = false; Enum enumValue = AnnotationAlignmentEnum::enumData[0].enumValue; for (std::vector::iterator iter = enumData.begin(); iter != enumData.end(); iter++) { const AnnotationAlignmentEnum& 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 AnnotationAlignmentEnum")); } return enumValue; } /** * Get a GUI string representation of the enumerated type. * @param enumValue * Enumerated value. * @return * String representing enumerated value. */ AString AnnotationAlignmentEnum::toGuiName(Enum enumValue) { if (initializedFlag == false) initialize(); const AnnotationAlignmentEnum* 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. */ AnnotationAlignmentEnum::Enum AnnotationAlignmentEnum::fromGuiName(const AString& guiName, bool* isValidOut) { if (initializedFlag == false) initialize(); bool validFlag = false; Enum enumValue = AnnotationAlignmentEnum::enumData[0].enumValue; for (std::vector::iterator iter = enumData.begin(); iter != enumData.end(); iter++) { const AnnotationAlignmentEnum& 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 AnnotationAlignmentEnum")); } return enumValue; } /** * Get the integer code for a data type. * * @return * Integer code for data type. */ int32_t AnnotationAlignmentEnum::toIntegerCode(Enum enumValue) { if (initializedFlag == false) initialize(); const AnnotationAlignmentEnum* 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. */ AnnotationAlignmentEnum::Enum AnnotationAlignmentEnum::fromIntegerCode(const int32_t integerCode, bool* isValidOut) { if (initializedFlag == false) initialize(); bool validFlag = false; Enum enumValue = AnnotationAlignmentEnum::enumData[0].enumValue; for (std::vector::iterator iter = enumData.begin(); iter != enumData.end(); iter++) { const AnnotationAlignmentEnum& 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 AnnotationAlignmentEnum")); } 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 AnnotationAlignmentEnum::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 AnnotationAlignmentEnum::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(AnnotationAlignmentEnum::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 AnnotationAlignmentEnum::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(AnnotationAlignmentEnum::toGuiName(iter->enumValue)); } if (isSorted) { std::sort(allGuiNames.begin(), allGuiNames.end()); } } connectome-workbench-1.2.3+git41-gc4c6c90/src/Annotations/AnnotationAlignmentEnum.h000066400000000000000000000064111300200146000300120ustar00rootroot00000000000000#ifndef __ANNOTATION_ALIGNMENT_ENUM_H__ #define __ANNOTATION_ALIGNMENT_ENUM_H__ /*LICENSE_START*/ /* * Copyright (C) 2015 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 AnnotationAlignmentEnum { public: /** * Enumerated values. */ enum Enum { /** Left */ ALIGN_LEFT, /** Center */ ALIGN_CENTER, /** Right */ ALIGN_RIGHT, /** Top */ ALIGN_TOP, /** Middle */ ALIGN_MIDDLE, /** Bottom */ ALIGN_BOTTOM }; ~AnnotationAlignmentEnum(); 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: AnnotationAlignmentEnum(const Enum enumValue, const AString& name, const AString& guiName); static const AnnotationAlignmentEnum* 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 __ANNOTATION_ALIGNMENT_ENUM_DECLARE__ std::vector AnnotationAlignmentEnum::enumData; bool AnnotationAlignmentEnum::initializedFlag = false; int32_t AnnotationAlignmentEnum::integerCodeCounter = 0; #endif // __ANNOTATION_ALIGNMENT_ENUM_DECLARE__ } // namespace #endif //__ANNOTATION_ALIGNMENT_ENUM_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Annotations/AnnotationAttributesDefaultTypeEnum.cxx000066400000000000000000000264731300200146000327560ustar00rootroot00000000000000 /*LICENSE_START*/ /* * Copyright (C) 2015 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 __ANNOTATION_ATTRIBUTES_DEFAULT_TYPE_ENUM_DECLARE__ #include "AnnotationAttributesDefaultTypeEnum.h" #undef __ANNOTATION_ATTRIBUTES_DEFAULT_TYPE_ENUM_DECLARE__ #include "CaretAssert.h" using namespace caret; /** * \class caret::AnnotationAttributesDefaultTypeEnum * \brief * * * * 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_annotationAttributesDefaultTypeEnumComboBox; * * Declare a slot that is called when user changes selection * private slots: * void annotationAttributesDefaultTypeEnumComboBoxItemActivated(); * * Implementation File (.cxx) * Include the header files * #include "EnumComboBoxTemplate.h" * #include "AnnotationAttributesDefaultTypeEnum.h" * * Instatiate: * m_annotationAttributesDefaultTypeEnumComboBox = new EnumComboBoxTemplate(this); * m_annotationAttributesDefaultTypeEnumComboBox->setup(); * * Get notified when the user changes the selection: * QObject::connect(m_annotationAttributesDefaultTypeEnumComboBox, SIGNAL(itemActivated()), * this, SLOT(annotationAttributesDefaultTypeEnumComboBoxItemActivated())); * * Update the selection: * m_annotationAttributesDefaultTypeEnumComboBox->setSelectedItem(NEW_VALUE); * * Read the selection: * const AnnotationAttributesDefaultTypeEnum::Enum VARIABLE = m_annotationAttributesDefaultTypeEnumComboBox->getSelectedItem(); * */ /** * Constructor. * * @param enumValue * An enumerated value. * @param name * Name of enumerated value. * * @param guiName * User-friendly name for use in user-interface. */ AnnotationAttributesDefaultTypeEnum::AnnotationAttributesDefaultTypeEnum(const Enum enumValue, const AString& name, const AString& guiName) { this->enumValue = enumValue; this->integerCode = integerCodeCounter++; this->name = name; this->guiName = guiName; } /** * Destructor. */ AnnotationAttributesDefaultTypeEnum::~AnnotationAttributesDefaultTypeEnum() { } /** * Initialize the enumerated metadata. */ void AnnotationAttributesDefaultTypeEnum::initialize() { if (initializedFlag) { return; } initializedFlag = true; enumData.push_back(AnnotationAttributesDefaultTypeEnum(NORMAL, "NORMAL", "")); enumData.push_back(AnnotationAttributesDefaultTypeEnum(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 AnnotationAttributesDefaultTypeEnum* AnnotationAttributesDefaultTypeEnum::findData(const Enum enumValue) { if (initializedFlag == false) initialize(); size_t num = enumData.size(); for (size_t i = 0; i < num; i++) { const AnnotationAttributesDefaultTypeEnum* 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 AnnotationAttributesDefaultTypeEnum::toName(Enum enumValue) { if (initializedFlag == false) initialize(); const AnnotationAttributesDefaultTypeEnum* 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. */ AnnotationAttributesDefaultTypeEnum::Enum AnnotationAttributesDefaultTypeEnum::fromName(const AString& name, bool* isValidOut) { if (initializedFlag == false) initialize(); bool validFlag = false; Enum enumValue = AnnotationAttributesDefaultTypeEnum::enumData[0].enumValue; for (std::vector::iterator iter = enumData.begin(); iter != enumData.end(); iter++) { const AnnotationAttributesDefaultTypeEnum& 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 AnnotationAttributesDefaultTypeEnum")); } return enumValue; } /** * Get a GUI string representation of the enumerated type. * @param enumValue * Enumerated value. * @return * String representing enumerated value. */ AString AnnotationAttributesDefaultTypeEnum::toGuiName(Enum enumValue) { if (initializedFlag == false) initialize(); const AnnotationAttributesDefaultTypeEnum* 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. */ AnnotationAttributesDefaultTypeEnum::Enum AnnotationAttributesDefaultTypeEnum::fromGuiName(const AString& guiName, bool* isValidOut) { if (initializedFlag == false) initialize(); bool validFlag = false; Enum enumValue = AnnotationAttributesDefaultTypeEnum::enumData[0].enumValue; for (std::vector::iterator iter = enumData.begin(); iter != enumData.end(); iter++) { const AnnotationAttributesDefaultTypeEnum& 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 AnnotationAttributesDefaultTypeEnum")); } return enumValue; } /** * Get the integer code for a data type. * * @return * Integer code for data type. */ int32_t AnnotationAttributesDefaultTypeEnum::toIntegerCode(Enum enumValue) { if (initializedFlag == false) initialize(); const AnnotationAttributesDefaultTypeEnum* 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. */ AnnotationAttributesDefaultTypeEnum::Enum AnnotationAttributesDefaultTypeEnum::fromIntegerCode(const int32_t integerCode, bool* isValidOut) { if (initializedFlag == false) initialize(); bool validFlag = false; Enum enumValue = AnnotationAttributesDefaultTypeEnum::enumData[0].enumValue; for (std::vector::iterator iter = enumData.begin(); iter != enumData.end(); iter++) { const AnnotationAttributesDefaultTypeEnum& 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 AnnotationAttributesDefaultTypeEnum")); } 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 AnnotationAttributesDefaultTypeEnum::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 AnnotationAttributesDefaultTypeEnum::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(AnnotationAttributesDefaultTypeEnum::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 AnnotationAttributesDefaultTypeEnum::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(AnnotationAttributesDefaultTypeEnum::toGuiName(iter->enumValue)); } if (isSorted) { std::sort(allGuiNames.begin(), allGuiNames.end()); } } connectome-workbench-1.2.3+git41-gc4c6c90/src/Annotations/AnnotationAttributesDefaultTypeEnum.h000066400000000000000000000063731300200146000324000ustar00rootroot00000000000000#ifndef __ANNOTATION_ATTRIBUTES_DEFAULT_TYPE_ENUM_H__ #define __ANNOTATION_ATTRIBUTES_DEFAULT_TYPE_ENUM_H__ /*LICENSE_START*/ /* * Copyright (C) 2015 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 AnnotationAttributesDefaultTypeEnum { public: /** * Enumerated values. */ enum Enum { /** */ NORMAL, /** */ USER }; ~AnnotationAttributesDefaultTypeEnum(); 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: AnnotationAttributesDefaultTypeEnum(const Enum enumValue, const AString& name, const AString& guiName); static const AnnotationAttributesDefaultTypeEnum* 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 __ANNOTATION_ATTRIBUTES_DEFAULT_TYPE_ENUM_DECLARE__ std::vector AnnotationAttributesDefaultTypeEnum::enumData; bool AnnotationAttributesDefaultTypeEnum::initializedFlag = false; int32_t AnnotationAttributesDefaultTypeEnum::integerCodeCounter = 0; #endif // __ANNOTATION_ATTRIBUTES_DEFAULT_TYPE_ENUM_DECLARE__ } // namespace #endif //__ANNOTATION_ATTRIBUTES_DEFAULT_TYPE_ENUM_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Annotations/AnnotationBox.cxx000066400000000000000000000077561300200146000263670ustar00rootroot00000000000000 /*LICENSE_START*/ /* * Copyright (C) 2015 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 __ANNOTATION_BOX_DECLARE__ #include "AnnotationBox.h" #undef __ANNOTATION_BOX_DECLARE__ #include "CaretAssert.h" #include "SceneClass.h" #include "SceneClassAssistant.h" using namespace caret; /** * \class caret::AnnotationBox * \brief An annotation box. * \ingroup Annotations */ /** * Constructor. * * @param attributeDefaultType * Type for attribute defaults */ AnnotationBox::AnnotationBox(const AnnotationAttributesDefaultTypeEnum::Enum attributeDefaultType) : AnnotationTwoDimensionalShape(AnnotationTypeEnum::BOX, attributeDefaultType) { initializeMembersAnnotationBox(); } /** * Destructor. */ AnnotationBox::~AnnotationBox() { } /** * Copy constructor. * @param obj * Object that is copied. */ AnnotationBox::AnnotationBox(const AnnotationBox& obj) : AnnotationTwoDimensionalShape(obj) { this->initializeMembersAnnotationBox(); this->copyHelperAnnotationBox(obj); } /** * Assignment operator. * @param obj * Data copied from obj to this. * @return * Reference to this object. */ AnnotationBox& AnnotationBox::operator=(const AnnotationBox& obj) { if (this != &obj) { AnnotationTwoDimensionalShape::operator=(obj); this->copyHelperAnnotationBox(obj); } return *this; } /** * Initialize a new instance of this class. */ void AnnotationBox::initializeMembersAnnotationBox() { m_sceneAssistant.grabNew(new SceneClassAssistant()); } /** * Helps with copying an object of this type. * @param obj * Object that is copied. */ void AnnotationBox::copyHelperAnnotationBox(const AnnotationBox& /*obj*/) { /* nothing to copy here */ } /** * 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 AnnotationBox::saveSubClassDataToScene(const SceneAttributes* sceneAttributes, SceneClass* sceneClass) { AnnotationTwoDimensionalShape::saveSubClassDataToScene(sceneAttributes, 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 AnnotationBox::restoreSubClassDataFromScene(const SceneAttributes* sceneAttributes, const SceneClass* sceneClass) { AnnotationTwoDimensionalShape::restoreSubClassDataFromScene(sceneAttributes, sceneClass); m_sceneAssistant->restoreMembers(sceneAttributes, sceneClass); } connectome-workbench-1.2.3+git41-gc4c6c90/src/Annotations/AnnotationBox.h000066400000000000000000000042231300200146000257760ustar00rootroot00000000000000#ifndef __ANNOTATION_BOX_H__ #define __ANNOTATION_BOX_H__ /*LICENSE_START*/ /* * Copyright (C) 2015 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 "AnnotationTwoDimensionalShape.h" #include "CaretPointer.h" namespace caret { class AnnotationBox : public AnnotationTwoDimensionalShape { public: AnnotationBox(const AnnotationAttributesDefaultTypeEnum::Enum attributeDefaultType); virtual ~AnnotationBox(); AnnotationBox(const AnnotationBox& obj); AnnotationBox& operator=(const AnnotationBox& obj); // ADD_NEW_METHODS_HERE protected: virtual void saveSubClassDataToScene(const SceneAttributes* sceneAttributes, SceneClass* sceneClass); virtual void restoreSubClassDataFromScene(const SceneAttributes* sceneAttributes, const SceneClass* sceneClass); private: void copyHelperAnnotationBox(const AnnotationBox& obj); void initializeMembersAnnotationBox(); CaretPointer m_sceneAssistant; // ADD_NEW_MEMBERS_HERE }; #ifdef __ANNOTATION_BOX_DECLARE__ // #endif // __ANNOTATION_BOX_DECLARE__ } // namespace #endif //__ANNOTATION_BOX_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Annotations/AnnotationColorBar.cxx000066400000000000000000000475541300200146000273420ustar00rootroot00000000000000 /*LICENSE_START*/ /* * Copyright (C) 2015 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 __ANNOTATION_COLOR_BAR_DECLARE__ #include "AnnotationColorBar.h" #undef __ANNOTATION_COLOR_BAR_DECLARE__ #include "AnnotationColorBarNumericText.h" #include "AnnotationColorBarSection.h" #include "CaretAssert.h" #include "SceneClassAssistant.h" using namespace caret; /** * \class caret::AnnotationColorBar * \brief Annotation used for drawing a color bar. * \ingroup Annotations */ /** * Constructor. * * @param attributeDefaultType * Type for attribute defaults */ AnnotationColorBar::AnnotationColorBar(const AnnotationAttributesDefaultTypeEnum::Enum attributeDefaultType) : AnnotationTwoDimensionalShape(AnnotationTypeEnum::COLOR_BAR, attributeDefaultType), 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); } /** * Destructor. */ AnnotationColorBar::~AnnotationColorBar() { clearSections(); clearNumericText(); } /** * Copy constructor. * @param obj * Object that is copied. */ AnnotationColorBar::AnnotationColorBar(const AnnotationColorBar& obj) : AnnotationTwoDimensionalShape(obj), AnnotationFontAttributesInterface() { this->copyHelperAnnotationColorBar(obj); } /** * Assignment operator. * @param obj * Data copied from obj to this. * @return * Reference to this object. */ AnnotationColorBar& AnnotationColorBar::operator=(const AnnotationColorBar& obj) { if (this != &obj) { AnnotationTwoDimensionalShape::operator=(obj); this->copyHelperAnnotationColorBar(obj); } return *this; } /** * Helps with copying an object of this type. * * @param obj * Object that is copied. */ void AnnotationColorBar::copyHelperAnnotationColorBar(const AnnotationColorBar& obj) { /* * NOTE: sections and numeric text are not copied */ clearSections(); clearNumericText(); m_positionMode = obj.m_positionMode; m_fontName = obj.m_fontName; m_fontPercentViewportHeight = obj.m_fontPercentViewportHeight; m_positionMode = obj.m_positionMode; m_displayedFlag = obj.m_displayedFlag; m_showTickMarksSelected = obj.m_showTickMarksSelected; m_colorText = obj.m_colorText; m_customColorText[0] = obj.m_customColorText[0]; m_customColorText[1] = obj.m_customColorText[1]; m_customColorText[2] = obj.m_customColorText[2]; m_customColorText[3] = obj.m_customColorText[3]; } /** * Reset the annotation colorbar. * * DO NOT make this method virtual is it is called from constructor. */ void AnnotationColorBar::reset() { resetSizeAttributes(); m_fontName = AnnotationTextFontNameEnum::getDefaultFontName(); m_positionMode = AnnotationColorBarPositionModeEnum::AUTOMATIC; m_displayedFlag = false; m_showTickMarksSelected = false; m_colorText = CaretColorEnum::WHITE; m_customColorText[0] = 1.0; m_customColorText[1] = 1.0; m_customColorText[2] = 1.0; m_customColorText[3] = 1.0; setLineColor(CaretColorEnum::WHITE); setBackgroundColor(CaretColorEnum::BLACK); clearSections(); clearNumericText(); } /** * Reset the size attributes of the color bar. */ void AnnotationColorBar::resetSizeAttributes() { setWidth(25.0); setHeight(7.0); setRotationAngle(0.0); m_fontPercentViewportHeight = 3.33; } /** * @return The font. */ AnnotationTextFontNameEnum::Enum AnnotationColorBar::getFont() const { return m_fontName; } /** * Set the font. * * @param font * New value for font. */ void AnnotationColorBar::setFont(const AnnotationTextFontNameEnum::Enum font) { if (font != m_fontName) { m_fontName = font; setModified(); } } /** * @return THe percent viewport height for the font. */ float AnnotationColorBar::getFontPercentViewportSize() const { return m_fontPercentViewportHeight; } /** * Set the percent viewport size for the font. * * @param fontPercentViewportHeight * New value for percent viewport height. */ void AnnotationColorBar::setFontPercentViewportSize(const float fontPercentViewportHeight) { if (fontPercentViewportHeight != m_fontPercentViewportHeight) { m_fontPercentViewportHeight = fontPercentViewportHeight; setModified(); } } /** * Are font styles (Bold, Italic, Underline) supported? */ bool AnnotationColorBar::isStylesSupported() const { return false; } /** * @return The foreground color. */ CaretColorEnum::Enum AnnotationColorBar::getTextColor() const { return m_colorText; } /** * Set the foreground color. * * @param color * New value for foreground color. */ void AnnotationColorBar::setTextColor(const CaretColorEnum::Enum color) { if (m_colorText != color) { m_colorText = color; setModified(); } } /** * Get the foreground color's RGBA components regardless of * coloring (custom color or a CaretColorEnum) selected by the user. * * @param rgbaOut * RGBA components ranging 0.0 to 1.0. */ void AnnotationColorBar::getTextColorRGBA(float rgbaOut[4]) const { switch (m_colorText) { case CaretColorEnum::NONE: rgbaOut[0] = 0.0; rgbaOut[1] = 0.0; rgbaOut[2] = 0.0; rgbaOut[3] = 0.0; break; case CaretColorEnum::CUSTOM: getCustomTextColor(rgbaOut); break; case CaretColorEnum::AQUA: case CaretColorEnum::BLACK: case CaretColorEnum::BLUE: case CaretColorEnum::FUCHSIA: case CaretColorEnum::GRAY: case CaretColorEnum::GREEN: case CaretColorEnum::LIME: case CaretColorEnum::MAROON: case CaretColorEnum::NAVY: case CaretColorEnum::OLIVE: case CaretColorEnum::PURPLE: case CaretColorEnum::RED: case CaretColorEnum::SILVER: case CaretColorEnum::TEAL: case CaretColorEnum::WHITE: case CaretColorEnum::YELLOW: CaretColorEnum::toRGBFloat(m_colorText, rgbaOut); rgbaOut[3] = 1.0; break; } } /** * Get the foreground color's RGBA components regardless of * coloring (custom color or a CaretColorEnum) selected by the user. * * @param rgbaOut * RGBA components ranging 0 to 255. */ void AnnotationColorBar::getTextColorRGBA(uint8_t rgbaOut[4]) const { float rgbaFloat[4] = { 0.0, 0.0, 0.0, 0.0 }; getTextColorRGBA(rgbaFloat); 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] = static_cast(rgbaFloat[3] * 255.0); } /** * Get the foreground color. * * @param rgbaOut * RGBA components (red, green, blue, alpha) each of which ranges [0.0, 1.0]. */ void AnnotationColorBar::getCustomTextColor(float rgbaOut[4]) const { rgbaOut[0] = m_customColorText[0]; rgbaOut[1] = m_customColorText[1]; rgbaOut[2] = m_customColorText[2]; rgbaOut[3] = m_customColorText[3]; } /** * Get the foreground color. * * @param rgbaOut * RGBA components (red, green, blue, alpha) each of which ranges [0, 255]. */ void AnnotationColorBar::getCustomTextColor(uint8_t rgbaOut[4]) const { rgbaOut[0] = static_cast(m_customColorText[0] * 255.0); rgbaOut[1] = static_cast(m_customColorText[1] * 255.0); rgbaOut[2] = static_cast(m_customColorText[2] * 255.0); rgbaOut[3] = static_cast(m_customColorText[3] * 255.0); } /** * Set the foreground color with floats. * * @param rgba * RGBA components (red, green, blue, alpha) each of which ranges [0.0, 1.0]. */ void AnnotationColorBar::setCustomTextColor(const float rgba[4]) { for (int32_t i = 0; i < 4; i++) { if (rgba[i] != m_customColorText[i]) { m_customColorText[i] = rgba[i]; setModified(); } } } /** * Set the foreground color with unsigned bytes. * * @param rgba * RGBA components (red, green, blue, alpha) each of which ranges [0, 255]. */ void AnnotationColorBar::setCustomTextColor(const uint8_t rgba[4]) { for (int32_t i = 0; i < 4; i++) { const float component = rgba[i] / 255.0; if (component != m_customColorText[i]) { m_customColorText[i] = component; setModified(); } } } /** * @return * Is bold enabled ? */ bool AnnotationColorBar::isBoldStyleEnabled() const { return false; } /** * Set bold enabled. * * @param enabled * New status for bold enabled. */ void AnnotationColorBar::setBoldStyleEnabled(const bool /*enabled*/) { } /** * @return * Is italic enabled ? */ bool AnnotationColorBar::isItalicStyleEnabled() const { return false; } /** * Set italic enabled. * * @param enabled * New status for italic enabled. */ void AnnotationColorBar::setItalicStyleEnabled(const bool /*enabled*/) { } /** * @return * Is underline enabled ? */ bool AnnotationColorBar::isUnderlineStyleEnabled() const { return false; } /** * Set underline enabled. * * @param enabled * New status for underline enabled. */ void AnnotationColorBar::setUnderlineStyleEnabled(const bool /*enabled*/) { } /** * @return * Is outline enabled ? */ bool AnnotationColorBar::isOutlineStyleEnabled() const { return false; } /** * Set outline enabled. * * @param enabled * New status for outline enabled. */ void 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. */ AnnotationColorBarPositionModeEnum::Enum AnnotationColorBar::getPositionMode() const { return m_positionMode; } /** * Set the position mode for the colorbar. * * @param positionMode * New position mode for the colorbar. */ void AnnotationColorBar::setPositionMode(const AnnotationColorBarPositionModeEnum::Enum positionMode) { if (positionMode != m_positionMode) { m_positionMode = positionMode; setModified(); } } /** * @return Display status of colorbar. */ bool AnnotationColorBar::isDisplayed() const { return m_displayedFlag; } /** * Set the color bar annotation displayed. * * Note that this also sets the annotation's selection * status to off so that if the user turns off display * of the annotation while the annotation is selected * the annotation does not show up as selected when * the color bar is later displayed by the user. * * @param displayed * New status for display of colorbar. */ void AnnotationColorBar::setDisplayed(const bool displayed) { if (displayed != m_displayedFlag) { m_displayedFlag = displayed; setDeselectedForEditing(); setModified(); } } /** * @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. * * @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 AnnotationColorBar::saveSubClassDataToScene(const SceneAttributes* sceneAttributes, SceneClass* sceneClass) { AnnotationTwoDimensionalShape::saveSubClassDataToScene(sceneAttributes, 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 AnnotationColorBar::restoreSubClassDataFromScene(const SceneAttributes* sceneAttributes, const SceneClass* sceneClass) { AnnotationTwoDimensionalShape::restoreSubClassDataFromScene(sceneAttributes, sceneClass); m_sceneAssistant->restoreMembers(sceneAttributes, sceneClass); /* * Prior to WB-617 (28 Apr 2016), scenes may * not have contained the color bar background * color so it may be necessary to ensure * the background is not the same as the text * color. */ if (getTextColor() == getBackgroundColor()) { if (getTextColor() == CaretColorEnum::BLACK) { setBackgroundColor(CaretColorEnum::WHITE); } else { setBackgroundColor(CaretColorEnum::BLACK); } } } /** * Add a section. * * Note: sections are not saved to scene so this method DOES NOT change the modified status. * * @param startScalar * Value of the starting scalar. * @param endScalar * Value of the ending scalar. * @param startRGBA * RGBA coloring at the starting scalar. * @param endRGBA * RGBA coloring at the ending scalar. */ void AnnotationColorBar::addSection(const float startScalar, const float endScalar, const float startRGBA[4], const float endRGBA[4]) { m_sections.push_back(new AnnotationColorBarSection(startScalar, endScalar, startRGBA, endRGBA)); } /** * Clear the sections. * * Note: sections are not saved to scene so this method DOES NOT change the modified status. */ void AnnotationColorBar::clearSections() { for (std::vector::iterator iter = m_sections.begin(); iter != m_sections.end(); iter++) { delete *iter; } m_sections.clear(); } /** * @return Number of sections. */ int32_t AnnotationColorBar::getNumberOfSections() const { return m_sections.size(); } /** * @return Section at the given index. * * @param index * Index of the section. */ const AnnotationColorBarSection* AnnotationColorBar::getSection(const int32_t index) const { CaretAssertVectorIndex(m_sections, index); return m_sections[index]; } /** * @param Is show tick marks selected? */ bool AnnotationColorBar::isShowTickMarksSelected() const { return m_showTickMarksSelected; } /** * Set show tick marks selected. * * @param selected * New selection status. */ void AnnotationColorBar::setShowTickMarksSelected(const bool selected) { m_showTickMarksSelected = selected; } /** * Add numeric text. * * Note: numeric text is not saved to scene so this method DOES NOT change the modified status. * * @param scalar * Scalar value for position of numeric text. * @param numericText * The numeric text. */ void AnnotationColorBar::addNumericText(const float scalar, const AString& numericText, const AnnotationTextAlignHorizontalEnum::Enum horizontalAlignment, const bool drawTickMarkAtScalar) { m_numericText.push_back(new AnnotationColorBarNumericText(scalar, numericText, horizontalAlignment, drawTickMarkAtScalar)); } /** * Clear the numeric text. * * Note: numeric text is not saved to scene so this method DOES NOT change the modified status. */ void AnnotationColorBar::clearNumericText() { for (std::vector::iterator iter = m_numericText.begin(); iter != m_numericText.end(); iter++) { delete *iter; } m_numericText.clear(); } /** * @return Number of numeric text. */ int32_t AnnotationColorBar::getNumberOfNumericText() const { return m_numericText.size(); } /** * @return Numeric text at the given index. * * @param index * Inext of the numeric text. */ const AnnotationColorBarNumericText* AnnotationColorBar::getNumericText(const int32_t index) const { CaretAssertVectorIndex(m_numericText, index); return m_numericText[index]; } /** * Get the minimum and maximum scalar values in the colorbar. * * @param minimumScalarOut * Minimum scalar value upon exit. * @param maximumScalarOut * Maximum scalar value upon exit. */ void AnnotationColorBar::getScalarMinimumAndMaximumValues(float& minimumScalarOut, float& maximumScalarOut) const { minimumScalarOut = std::numeric_limits::max(); maximumScalarOut = -std::numeric_limits::max(); const int32_t numSections = getNumberOfSections(); if (numSections <= 0) { minimumScalarOut = 0.0; maximumScalarOut = 0.0; return; } for (int32_t i = 0; i < numSections; i++) { const AnnotationColorBarSection* section = getSection(i); minimumScalarOut = std::min(minimumScalarOut, section->getStartScalar()); minimumScalarOut = std::min(minimumScalarOut, section->getEndScalar()); maximumScalarOut = std::max(maximumScalarOut, section->getStartScalar()); maximumScalarOut = std::max(maximumScalarOut, section->getEndScalar()); } } connectome-workbench-1.2.3+git41-gc4c6c90/src/Annotations/AnnotationColorBar.h000066400000000000000000000137641300200146000267630ustar00rootroot00000000000000#ifndef __ANNOTATION_COLOR_BAR_H__ #define __ANNOTATION_COLOR_BAR_H__ /*LICENSE_START*/ /* * Copyright (C) 2015 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 "AnnotationColorBarPositionModeEnum.h" #include "AnnotationFontAttributesInterface.h" #include "AnnotationTextAlignHorizontalEnum.h" #include "AnnotationTwoDimensionalShape.h" namespace caret { class AnnotationColorBarSection; class AnnotationColorBarNumericText; class AnnotationColorBar : public AnnotationTwoDimensionalShape, public AnnotationFontAttributesInterface { public: AnnotationColorBar(const AnnotationAttributesDefaultTypeEnum::Enum attributeDefaultType); virtual ~AnnotationColorBar(); AnnotationColorBar(const AnnotationColorBar& obj); AnnotationColorBar& operator=(const AnnotationColorBar& obj); void reset(); void resetSizeAttributes(); virtual AnnotationTextFontNameEnum::Enum getFont() const; virtual void setFont(const AnnotationTextFontNameEnum::Enum font); virtual float getFontPercentViewportSize() const; virtual void setFontPercentViewportSize(const float fontPercentViewportHeight); AnnotationColorBarPositionModeEnum::Enum getPositionMode() const; void setPositionMode(const AnnotationColorBarPositionModeEnum::Enum positionMode); virtual CaretColorEnum::Enum getTextColor() const; virtual void setTextColor(const CaretColorEnum::Enum color); virtual void getTextColorRGBA(float rgbaOut[4]) const; virtual void getTextColorRGBA(uint8_t rgbaOut[4]) const; virtual void getCustomTextColor(float rgbaOut[4]) const; virtual void getCustomTextColor(uint8_t rgbaOut[4]) const; virtual void setCustomTextColor(const float rgba[4]); virtual void setCustomTextColor(const uint8_t rgba[4]); virtual bool isStylesSupported() const; virtual bool isBoldStyleEnabled() const; virtual void setBoldStyleEnabled(const bool enabled); virtual bool isItalicStyleEnabled() const; virtual void setItalicStyleEnabled(const bool enabled); virtual bool isUnderlineStyleEnabled() const; virtual void setUnderlineStyleEnabled(const bool enabled); virtual bool isOutlineStyleEnabled() const; virtual void setOutlineStyleEnabled(const bool enabled); virtual bool isLineWidthSupported() const; bool isDisplayed() const; void setDisplayed(const bool displayed); void addSection(const float startScalar, const float endScalar, const float startRGBA[4], const float endRGBA[4]); void clearSections(); int32_t getNumberOfSections() const; const AnnotationColorBarSection* getSection(const int32_t index) const; void addNumericText(const float scalar, const AString& numericText, const AnnotationTextAlignHorizontalEnum::Enum horizontalAlignment, const bool drawTickMarkAtScalar); void clearNumericText(); int32_t getNumberOfNumericText() const; const AnnotationColorBarNumericText* getNumericText(const int32_t index) const; void getScalarMinimumAndMaximumValues(float& minimumScalarOut, float& maximumScalarOut) const; bool isShowTickMarksSelected() const; void setShowTickMarksSelected(const bool selected); virtual bool isDeletable() const; // ADD_NEW_METHODS_HERE protected: virtual void saveSubClassDataToScene(const SceneAttributes* sceneAttributes, SceneClass* sceneClass); virtual void restoreSubClassDataFromScene(const SceneAttributes* sceneAttributes, const SceneClass* sceneClass); private: void copyHelperAnnotationColorBar(const AnnotationColorBar& obj); CaretPointer m_sceneAssistant; AnnotationTextFontNameEnum::Enum m_fontName; float m_fontPercentViewportHeight; AnnotationColorBarPositionModeEnum::Enum m_positionMode; CaretColorEnum::Enum m_colorText; float m_customColorText[4]; bool m_displayedFlag; /** color bar sections NOT SAVED TO SCENE */ std::vector m_sections; /** color bar numeric text NOT SAVED TO SCENE */ std::vector m_numericText; bool m_showTickMarksSelected; // ADD_NEW_MEMBERS_HERE }; #ifdef __ANNOTATION_COLOR_BAR_DECLARE__ // #endif // __ANNOTATION_COLOR_BAR_DECLARE__ } // namespace #endif //__ANNOTATION_COLOR_BAR_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Annotations/AnnotationColorBarNumericText.cxx000066400000000000000000000063141300200146000315170ustar00rootroot00000000000000 /*LICENSE_START*/ /* * Copyright (C) 2015 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 __ANNOTATION_COLOR_BAR_NUMERIC_TEXT_DECLARE__ #include "AnnotationColorBarNumericText.h" #undef __ANNOTATION_COLOR_BAR_NUMERIC_TEXT_DECLARE__ #include "CaretAssert.h" using namespace caret; /** * \class caret::AnnotationColorBarNumericText * \brief Numeric text that is displayed above the color bar. * \ingroup Annotations */ /** * Constructor. * * @param scalar * Scalar for position of text. * @param numericText * Text that is displayed. * @param horizontalAlignment * Horizontal alignment for the text. * @param drawTickMarkAtScalar * If true, a tick mark is drawn at the scalar */ AnnotationColorBarNumericText::AnnotationColorBarNumericText(const float scalar, const AString& numericText, const AnnotationTextAlignHorizontalEnum::Enum horizontalAlignment, const bool drawTickMarkAtScalar) : CaretObject(), m_scalar(scalar), m_numericText(numericText), m_horizontalAlignment(horizontalAlignment), m_drawTickMarkAtScalar(drawTickMarkAtScalar) { } /** * Destructor. */ AnnotationColorBarNumericText::~AnnotationColorBarNumericText() { } /** * Copy constructor. * @param obj * Object that is copied. */ AnnotationColorBarNumericText::AnnotationColorBarNumericText(const AnnotationColorBarNumericText& obj) : CaretObject(obj) { this->copyHelperAnnotationColorBarNumericText(obj); } /** * Assignment operator. * @param obj * Data copied from obj to this. * @return * Reference to this object. */ AnnotationColorBarNumericText& AnnotationColorBarNumericText::operator=(const AnnotationColorBarNumericText& obj) { if (this != &obj) { CaretObject::operator=(obj); this->copyHelperAnnotationColorBarNumericText(obj); } return *this; } /** * Helps with copying an object of this type. * @param obj * Object that is copied. */ void AnnotationColorBarNumericText::copyHelperAnnotationColorBarNumericText(const AnnotationColorBarNumericText& obj) { m_scalar = obj.m_scalar; m_numericText = obj.m_numericText; } /** * Get a description of this object's content. * @return String describing this object's content. */ AString AnnotationColorBarNumericText::toString() const { return "AnnotationColorBarNumericText"; } connectome-workbench-1.2.3+git41-gc4c6c90/src/Annotations/AnnotationColorBarNumericText.h000066400000000000000000000061101300200146000311360ustar00rootroot00000000000000#ifndef __ANNOTATION_COLOR_BAR_NUMERIC_TEXT_H__ #define __ANNOTATION_COLOR_BAR_NUMERIC_TEXT_H__ /*LICENSE_START*/ /* * Copyright (C) 2015 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 "AnnotationTextAlignHorizontalEnum.h" #include "CaretObject.h" namespace caret { class AnnotationColorBarNumericText : public CaretObject { public: AnnotationColorBarNumericText(const float scalar, const AString& numericText, const AnnotationTextAlignHorizontalEnum::Enum horizontalAlignment, const bool drawTickMarkAtScalar); virtual ~AnnotationColorBarNumericText(); AnnotationColorBarNumericText(const AnnotationColorBarNumericText& obj); AnnotationColorBarNumericText& operator=(const AnnotationColorBarNumericText& obj); /** * @return The scalar value. */ float getScalar() const { return m_scalar; } /** * @return The numeric text. */ AString getNumericText() const { return m_numericText; } /** * Set the numeric text * * @param text * New text. */ void setNumericText(const AString& text) { m_numericText = text; } /** * @return The horizontal alignment for the text */ AnnotationTextAlignHorizontalEnum::Enum getHorizontalAlignment() const { return m_horizontalAlignment; } /** * @return Is a tick mark drawn at the scalar value ? */ bool isDrawTickMarkAtScalar() const { return m_drawTickMarkAtScalar; } // ADD_NEW_METHODS_HERE virtual AString toString() const; private: void copyHelperAnnotationColorBarNumericText(const AnnotationColorBarNumericText& obj); float m_scalar; AString m_numericText; AnnotationTextAlignHorizontalEnum::Enum m_horizontalAlignment; bool m_drawTickMarkAtScalar; // ADD_NEW_MEMBERS_HERE }; #ifdef __ANNOTATION_COLOR_BAR_NUMERIC_TEXT_DECLARE__ // #endif // __ANNOTATION_COLOR_BAR_NUMERIC_TEXT_DECLARE__ } // namespace #endif //__ANNOTATION_COLOR_BAR_NUMERIC_TEXT_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Annotations/AnnotationColorBarPositionModeEnum.cxx000066400000000000000000000270061300200146000325070ustar00rootroot00000000000000 /*LICENSE_START*/ /* * Copyright (C) 2015 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 __ANNOTATION_COLOR_BAR_POSITION_MODE_ENUM_DECLARE__ #include "AnnotationColorBarPositionModeEnum.h" #undef __ANNOTATION_COLOR_BAR_POSITION_MODE_ENUM_DECLARE__ #include "CaretAssert.h" using namespace caret; /** * \class caret::AnnotationColorBarPositionModeEnum * \brief Enumerated type for positioning of color bar * * * * 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_annotationColorBarPositionModeEnumComboBox; * * Declare a slot that is called when user changes selection * private slots: * void annotationColorBarPositionModeEnumComboBoxItemActivated(); * * Implementation File (.cxx) * Include the header files * #include "EnumComboBoxTemplate.h" * #include "AnnotationColorBarPositionModeEnum.h" * * Instatiate: * m_annotationColorBarPositionModeEnumComboBox = new EnumComboBoxTemplate(this); * m_annotationColorBarPositionModeEnumComboBox->setup(); * * Get notified when the user changes the selection: * QObject::connect(m_annotationColorBarPositionModeEnumComboBox, SIGNAL(itemActivated()), * this, SLOT(annotationColorBarPositionModeEnumComboBoxItemActivated())); * * Update the selection: * m_annotationColorBarPositionModeEnumComboBox->setSelectedItem(NEW_VALUE); * * Read the selection: * const AnnotationColorBarPositionModeEnum::Enum VARIABLE = m_annotationColorBarPositionModeEnumComboBox->getSelectedItem(); * */ /** * Constructor. * * @param enumValue * An enumerated value. * @param name * Name of enumerated value. * * @param guiName * User-friendly name for use in user-interface. */ AnnotationColorBarPositionModeEnum::AnnotationColorBarPositionModeEnum(const Enum enumValue, const AString& name, const AString& guiName) { this->enumValue = enumValue; this->integerCode = integerCodeCounter++; this->name = name; this->guiName = guiName; } /** * Destructor. */ AnnotationColorBarPositionModeEnum::~AnnotationColorBarPositionModeEnum() { } /** * Initialize the enumerated metadata. */ void AnnotationColorBarPositionModeEnum::initialize() { if (initializedFlag) { return; } initializedFlag = true; enumData.push_back(AnnotationColorBarPositionModeEnum(AUTOMATIC, "AUTOMATIC", "Automatic")); enumData.push_back(AnnotationColorBarPositionModeEnum(MANUAL, "MANUAL", "Manual")); } /** * 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 AnnotationColorBarPositionModeEnum* AnnotationColorBarPositionModeEnum::findData(const Enum enumValue) { if (initializedFlag == false) initialize(); size_t num = enumData.size(); for (size_t i = 0; i < num; i++) { const AnnotationColorBarPositionModeEnum* 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 AnnotationColorBarPositionModeEnum::toName(Enum enumValue) { if (initializedFlag == false) initialize(); const AnnotationColorBarPositionModeEnum* 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. */ AnnotationColorBarPositionModeEnum::Enum AnnotationColorBarPositionModeEnum::fromName(const AString& nameIn, bool* isValidOut) { if (initializedFlag == false) initialize(); AString name(nameIn); if (name == "USER") { name = AnnotationColorBarPositionModeEnum::toName(MANUAL); } if (name == "AUTO") { name = AnnotationColorBarPositionModeEnum::toName(AUTOMATIC); } bool validFlag = false; Enum enumValue = AnnotationColorBarPositionModeEnum::enumData[0].enumValue; for (std::vector::iterator iter = enumData.begin(); iter != enumData.end(); iter++) { const AnnotationColorBarPositionModeEnum& 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 AnnotationColorBarPositionModeEnum")); } return enumValue; } /** * Get a GUI string representation of the enumerated type. * @param enumValue * Enumerated value. * @return * String representing enumerated value. */ AString AnnotationColorBarPositionModeEnum::toGuiName(Enum enumValue) { if (initializedFlag == false) initialize(); const AnnotationColorBarPositionModeEnum* 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. */ AnnotationColorBarPositionModeEnum::Enum AnnotationColorBarPositionModeEnum::fromGuiName(const AString& guiName, bool* isValidOut) { if (initializedFlag == false) initialize(); bool validFlag = false; Enum enumValue = AnnotationColorBarPositionModeEnum::enumData[0].enumValue; for (std::vector::iterator iter = enumData.begin(); iter != enumData.end(); iter++) { const AnnotationColorBarPositionModeEnum& 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 AnnotationColorBarPositionModeEnum")); } return enumValue; } /** * Get the integer code for a data type. * * @return * Integer code for data type. */ int32_t AnnotationColorBarPositionModeEnum::toIntegerCode(Enum enumValue) { if (initializedFlag == false) initialize(); const AnnotationColorBarPositionModeEnum* 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. */ AnnotationColorBarPositionModeEnum::Enum AnnotationColorBarPositionModeEnum::fromIntegerCode(const int32_t integerCode, bool* isValidOut) { if (initializedFlag == false) initialize(); bool validFlag = false; Enum enumValue = AnnotationColorBarPositionModeEnum::enumData[0].enumValue; for (std::vector::iterator iter = enumData.begin(); iter != enumData.end(); iter++) { const AnnotationColorBarPositionModeEnum& 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 AnnotationColorBarPositionModeEnum")); } 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 AnnotationColorBarPositionModeEnum::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 AnnotationColorBarPositionModeEnum::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(AnnotationColorBarPositionModeEnum::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 AnnotationColorBarPositionModeEnum::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(AnnotationColorBarPositionModeEnum::toGuiName(iter->enumValue)); } if (isSorted) { std::sort(allGuiNames.begin(), allGuiNames.end()); } } connectome-workbench-1.2.3+git41-gc4c6c90/src/Annotations/AnnotationColorBarPositionModeEnum.h000066400000000000000000000064031300200146000321320ustar00rootroot00000000000000#ifndef __ANNOTATION_COLOR_BAR_POSITION_MODE_ENUM_H__ #define __ANNOTATION_COLOR_BAR_POSITION_MODE_ENUM_H__ /*LICENSE_START*/ /* * Copyright (C) 2015 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 AnnotationColorBarPositionModeEnum { public: /** * Enumerated values. */ enum Enum { /** Auto */ AUTOMATIC, /** Manual */ MANUAL }; ~AnnotationColorBarPositionModeEnum(); 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: AnnotationColorBarPositionModeEnum(const Enum enumValue, const AString& name, const AString& guiName); static const AnnotationColorBarPositionModeEnum* 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 __ANNOTATION_COLOR_BAR_POSITION_MODE_ENUM_DECLARE__ std::vector AnnotationColorBarPositionModeEnum::enumData; bool AnnotationColorBarPositionModeEnum::initializedFlag = false; int32_t AnnotationColorBarPositionModeEnum::integerCodeCounter = 0; #endif // __ANNOTATION_COLOR_BAR_POSITION_MODE_ENUM_DECLARE__ } // namespace #endif //__ANNOTATION_COLOR_BAR_POSITION_MODE_ENUM_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Annotations/AnnotationColorBarSection.cxx000066400000000000000000000060021300200146000306460ustar00rootroot00000000000000 /*LICENSE_START*/ /* * Copyright (C) 2015 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 __ANNOTATION_COLOR_BAR_SECTION_DECLARE__ #include "AnnotationColorBarSection.h" #undef __ANNOTATION_COLOR_BAR_SECTION_DECLARE__ #include "CaretAssert.h" using namespace caret; /** * \class caret::AnnotationColorBarSection * \brief Contains scalars and coloring for one section of the colorbar. * \ingroup Annotations */ /** * Constructor. * * @param startScalar * Value of the starting scalar. * @param endScalar * Value of the ending scalar. * @param startRGBA * RGBA coloring at the starting scalar. * @param endRGBA * RGBA coloring at the ending scalar. */ AnnotationColorBarSection::AnnotationColorBarSection(const float startScalar, const float endScalar, const float startRGBA[4], const float endRGBA[4]) : CaretObject(), m_startScalar(startScalar), m_endScalar(endScalar) { for (int32_t i = 0; i < 4; i++) { m_startRGBA[i] = startRGBA[i]; m_endRGBA[i] = endRGBA[i]; } } /** * Destructor. */ AnnotationColorBarSection::~AnnotationColorBarSection() { } /** * Copy constructor. * @param obj * Object that is copied. */ AnnotationColorBarSection::AnnotationColorBarSection(const AnnotationColorBarSection& obj) : CaretObject(obj) { this->copyHelperAnnotationColorBarSection(obj); } /** * Assignment operator. * @param obj * Data copied from obj to this. * @return * Reference to this object. */ AnnotationColorBarSection& AnnotationColorBarSection::operator=(const AnnotationColorBarSection& obj) { if (this != &obj) { CaretObject::operator=(obj); this->copyHelperAnnotationColorBarSection(obj); } return *this; } /** * Helps with copying an object of this type. * @param obj * Object that is copied. */ void AnnotationColorBarSection::copyHelperAnnotationColorBarSection(const AnnotationColorBarSection& obj) { m_startScalar = obj.m_startScalar; m_endScalar = obj.m_endScalar; for (int32_t i = 0; i < 4; i++) { m_startRGBA[i] = obj.m_startRGBA[i]; m_endRGBA[i] = obj.m_endRGBA[i]; } } connectome-workbench-1.2.3+git41-gc4c6c90/src/Annotations/AnnotationColorBarSection.h000066400000000000000000000050111300200146000302720ustar00rootroot00000000000000#ifndef __ANNOTATION_COLOR_BAR_SECTION_H__ #define __ANNOTATION_COLOR_BAR_SECTION_H__ /*LICENSE_START*/ /* * Copyright (C) 2015 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" namespace caret { class AnnotationColorBarSection : public CaretObject { public: AnnotationColorBarSection(const float startScalar, const float endScalar, const float startRGBA[4], const float endRGBA[4]); virtual ~AnnotationColorBarSection(); AnnotationColorBarSection(const AnnotationColorBarSection& obj); AnnotationColorBarSection& operator=(const AnnotationColorBarSection& obj); /** * @return The starting scalar */ float getStartScalar() const { return m_startScalar; } /** * @return The ending scalar */ float getEndScalar() const { return m_endScalar; } /** * @return Pointer to starting RGBA */ const float* getStartRGBA() const { return m_startRGBA; } /** * @return Pointer to ending RGBA */ const float* getEndRGBA() const { return m_endRGBA; } private: void copyHelperAnnotationColorBarSection(const AnnotationColorBarSection& obj); float m_startScalar; float m_endScalar; float m_startRGBA[4]; float m_endRGBA[4]; // ADD_NEW_MEMBERS_HERE }; #ifdef __ANNOTATION_COLOR_BAR_SECTION_DECLARE__ // #endif // __ANNOTATION_COLOR_BAR_SECTION_DECLARE__ } // namespace #endif //__ANNOTATION_COLOR_BAR_SECTION_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Annotations/AnnotationCoordinate.cxx000066400000000000000000000327631300200146000277220ustar00rootroot00000000000000 /*LICENSE_START*/ /* * Copyright (C) 2015 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 __ANNOTATION_COORDINATE_DECLARE__ #include "AnnotationCoordinate.h" #undef __ANNOTATION_COORDINATE_DECLARE__ #include "CaretAssert.h" #include "MathFunctions.h" #include "SceneClass.h" #include "SceneClassAssistant.h" using namespace caret; /** * \class caret::AnnotationCoordinate * \brief Coordinate for an annotation. * \ingroup Annotations */ /** * Constructor. */ AnnotationCoordinate::AnnotationCoordinate() : CaretObjectTracksModification() { initializeAnnotationCoordinateMembers(); } /** * Destructor. */ AnnotationCoordinate::~AnnotationCoordinate() { delete m_sceneAssistant; } /** * Copy constructor. * @param obj * Object that is copied. */ AnnotationCoordinate::AnnotationCoordinate(const AnnotationCoordinate& obj) : CaretObjectTracksModification(obj), SceneableInterface(obj) { initializeAnnotationCoordinateMembers(); this->copyHelperAnnotationCoordinate(obj); } /** * Assignment operator. * @param obj * Data copied from obj to this. * @return * Reference to this object. */ AnnotationCoordinate& AnnotationCoordinate::operator=(const AnnotationCoordinate& obj) { if (this != &obj) { CaretObjectTracksModification::operator=(obj); this->copyHelperAnnotationCoordinate(obj); } return *this; } /** * Helps with copying an object of this type. * @param obj * Object that is copied. */ void AnnotationCoordinate::copyHelperAnnotationCoordinate(const AnnotationCoordinate& obj) { m_xyz[0] = obj.m_xyz[0]; m_xyz[1] = obj.m_xyz[1]; m_xyz[2] = obj.m_xyz[2]; m_surfaceSpaceStructure = obj.m_surfaceSpaceStructure; m_surfaceSpaceNumberOfNodes = obj.m_surfaceSpaceNumberOfNodes; m_surfaceSpaceNodeIndex = obj.m_surfaceSpaceNodeIndex; m_surfaceOffsetLength = obj.m_surfaceOffsetLength; m_surfaceOffsetVectorType = obj.m_surfaceOffsetVectorType; } /** * Initialize members for an instance of this class. */ void AnnotationCoordinate::initializeAnnotationCoordinateMembers() { m_xyz[0] = 0.0; m_xyz[1] = 0.0; m_xyz[2] = 0.0; m_surfaceSpaceStructure = StructureEnum::INVALID; m_surfaceSpaceNumberOfNodes = -1; m_surfaceSpaceNodeIndex = -1; m_surfaceOffsetLength = getDefaultSurfaceOffsetLength(); m_surfaceOffsetVectorType = AnnotationSurfaceOffsetVectorTypeEnum::CENTROID_THRU_VERTEX; m_sceneAssistant = new SceneClassAssistant(); m_sceneAssistant->addArray("m_xyz", m_xyz, 3, 0.0); m_sceneAssistant->add("m_surfaceSpaceStructure", &m_surfaceSpaceStructure); m_sceneAssistant->add("m_surfaceSpaceNumberOfNodes", &m_surfaceSpaceNumberOfNodes); m_sceneAssistant->add("m_surfaceSpaceNodeIndex", &m_surfaceSpaceNodeIndex); m_sceneAssistant->add("m_surfaceOffsetLength", &m_surfaceOffsetLength); m_sceneAssistant->add("m_surfaceOffsetVectorType", &m_surfaceOffsetVectorType); } /** * @return The annotation's coordinate. * * For tab and window spaces, the Z value is a depth for ordering. */ const float* AnnotationCoordinate::getXYZ() const { return m_xyz; } /** * Get the annotation's coordinate. * * For tab and window spaces, the Z value is a depth for ordering. * * @param xyzOut */ void AnnotationCoordinate::getXYZ(float xyzOut[3]) const { xyzOut[0] = m_xyz[0]; xyzOut[1] = m_xyz[1]; xyzOut[2] = m_xyz[2]; } /** * Set the annotation's coordinate. * * For tab and window spaces, the Z value is a depth for ordering. * * @param xyz * New coordinate for the annotation. */ void AnnotationCoordinate::setXYZ(const float xyz[3]) { setXYZ(xyz[0], xyz[1], xyz[2]); } /** * Set the annotation's coordinate. * * For tab and window spaces, the Z value is a depth for ordering. * * @param xyz * New coordinate for the annotation. */ void AnnotationCoordinate::setXYZ(const double xyz[3]) { setXYZ(xyz[0], xyz[1], xyz[2]); } /** * Set the annotation's coordinate. * * For tab and window spaces, the Z value is a depth for ordering. * * @param x * New X-coordinate for the annotation. * @param y * New Y-coordinate for the annotation. * @param z * New Z-coordinate for the annotation. */ void AnnotationCoordinate::setXYZ(const float x, const float y, const float z) { if ((x != m_xyz[0]) || (y != m_xyz[1]) || (z != m_xyz[2])) { m_xyz[0] = x; m_xyz[1] = y; m_xyz[2] = z; setModified(); } } /** * Set the window or tab XYZ from viewport X, Y. * * @param viewportWidth * Width of viewport. * @param viewportHeight * Height of viewport. * @param viewportX * X viewport coordinate. * @param viewportY * Y viewport coordinate. */ void AnnotationCoordinate::setXYZFromViewportXYZ(const float viewportWidth, const float viewportHeight, const float viewportX, const float viewportY) { const float x = MathFunctions::limitRange(((viewportX / viewportWidth) * 100.0), 0.0, 100.0); const float y = MathFunctions::limitRange(((viewportY / viewportHeight) * 100.0), 0.0, 100.0); setXYZ(x, y, m_xyz[2]); } /** * Get the viewport X, Y from window or tab XYZ. * * @param viewportWidth * Width of viewport. * @param viewportHeight * Height of viewport. * @param viewportXOut * X viewport coordinate. * @param viewportYOut * Y viewport coordinate. */ void AnnotationCoordinate::getViewportXY(const float viewportWidth, const float viewportHeight, float& viewportXOut, float& viewportYOut) const { viewportXOut = (m_xyz[0] / 100.0) * viewportWidth; viewportYOut = (m_xyz[1] / 100.0) * viewportHeight; } /** * Get the surface space data. * * @param structureOut * The surface structure. * @param surfaceNumberOfNodesOut * Number of nodes in surface. * @param surfaceNodeIndexOut * Index of surface node. */ void AnnotationCoordinate::getSurfaceSpace(StructureEnum::Enum& structureOut, int32_t& surfaceNumberOfNodesOut, int32_t& surfaceNodeIndexOut) const { structureOut = m_surfaceSpaceStructure; surfaceNumberOfNodesOut = m_surfaceSpaceNumberOfNodes; surfaceNodeIndexOut = m_surfaceSpaceNodeIndex; } /** * Get the surface space data. * * @param structureOut * The surface structure. * @param surfaceNumberOfNodesOut * Number of nodes in surface. * @param surfaceNodeIndexOut * Index of surface node. * @param surfaceOffsetLengthOut * Offset of annotation from surface * @param surfaceOffsetVectorTypeOut * Offset from surface vector type. */ void AnnotationCoordinate::getSurfaceSpace(StructureEnum::Enum& structureOut, int32_t& surfaceNumberOfNodesOut, int32_t& surfaceNodeIndexOut, float& surfaceOffsetLengthOut, AnnotationSurfaceOffsetVectorTypeEnum::Enum& surfaceOffsetVectorTypeOut) const { structureOut = m_surfaceSpaceStructure; surfaceNumberOfNodesOut = m_surfaceSpaceNumberOfNodes; surfaceNodeIndexOut = m_surfaceSpaceNodeIndex; surfaceOffsetLengthOut = m_surfaceOffsetLength; surfaceOffsetVectorTypeOut = m_surfaceOffsetVectorType; } /** * Set the surface space data. * * @param structure * The surface structure. * @param surfaceNumberOfNodes * Number of nodes in surface. * @param surfaceNodeIndex * Index of surface node. */ void AnnotationCoordinate::setSurfaceSpace(const StructureEnum::Enum structure, const int32_t surfaceNumberOfNodes, const int32_t surfaceNodeIndex) { if (structure != m_surfaceSpaceStructure) { m_surfaceSpaceStructure = structure; setModified(); } if (surfaceNumberOfNodes != m_surfaceSpaceNumberOfNodes) { m_surfaceSpaceNumberOfNodes = surfaceNumberOfNodes; setModified(); } if (surfaceNodeIndex != m_surfaceSpaceNodeIndex) { m_surfaceSpaceNodeIndex = surfaceNodeIndex; setModified(); } } /** * Set the surface space data. * * @param structure * The surface structure. * @param surfaceNumberOfNodes * Number of nodes in surface. * @param surfaceNodeIndex * Index of surface node. * @param surfaceOffsetLength * Offset of annotation from surface * @param surfaceOffsetVectorType * Offset from surface vector type. */ void AnnotationCoordinate::setSurfaceSpace(const StructureEnum::Enum structure, const int32_t surfaceNumberOfNodes, const int32_t surfaceNodeIndex, const float surfaceOffsetLength, const AnnotationSurfaceOffsetVectorTypeEnum::Enum surfaceOffsetVectorType) { if (structure != m_surfaceSpaceStructure) { m_surfaceSpaceStructure = structure; setModified(); } if (surfaceNumberOfNodes != m_surfaceSpaceNumberOfNodes) { m_surfaceSpaceNumberOfNodes = surfaceNumberOfNodes; setModified(); } if (surfaceNodeIndex != m_surfaceSpaceNodeIndex) { m_surfaceSpaceNodeIndex = surfaceNodeIndex; setModified(); } if (surfaceOffsetLength != m_surfaceOffsetLength) { m_surfaceOffsetLength = surfaceOffsetLength; setModified(); } if (surfaceOffsetVectorType != m_surfaceOffsetVectorType) { m_surfaceOffsetVectorType = surfaceOffsetVectorType; setModified(); } } /** * @return The default surface offset length. */ float AnnotationCoordinate::getDefaultSurfaceOffsetLength() { return 5.0; } /** * @return The surface offset length. */ float AnnotationCoordinate::getSurfaceOffsetLength() const { return m_surfaceOffsetLength; } /** * @return The surface structure. */ StructureEnum::Enum AnnotationCoordinate::getSurfaceStructure() const { return m_surfaceSpaceStructure; } /** * @return Type of surface offset. */ AnnotationSurfaceOffsetVectorTypeEnum::Enum AnnotationCoordinate::getSurfaceOffsetVectorType() const { return m_surfaceOffsetVectorType; } /** * Get a description of this object's content. * @return String describing this object's content. */ AString AnnotationCoordinate::toString() const { return "AnnotationCoordinate"; } /** * 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* AnnotationCoordinate::saveToScene(const SceneAttributes* sceneAttributes, const AString& instanceName) { SceneClass* sceneClass = new SceneClass(instanceName, "AnnotationCoordinate", 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 AnnotationCoordinate::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); } connectome-workbench-1.2.3+git41-gc4c6c90/src/Annotations/AnnotationCoordinate.h000066400000000000000000000124541300200146000273420ustar00rootroot00000000000000#ifndef __ANNOTATION_COORDINATE_H__ #define __ANNOTATION_COORDINATE_H__ /*LICENSE_START*/ /* * Copyright (C) 2015 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 "AnnotationSurfaceOffsetVectorTypeEnum.h" #include "CaretObjectTracksModification.h" #include "SceneableInterface.h" #include "StructureEnum.h" namespace caret { class SceneClassAssistant; class AnnotationCoordinate : public CaretObjectTracksModification, public SceneableInterface { public: AnnotationCoordinate(); virtual ~AnnotationCoordinate(); AnnotationCoordinate(const AnnotationCoordinate& obj); AnnotationCoordinate& operator=(const AnnotationCoordinate& obj); const float* getXYZ() const; void getXYZ(float xyzOut[3]) const; void setXYZ(const float xyz[3]); void setXYZ(const double xyz[3]); void setXYZ(const float x, const float y, const float z); void setXYZFromViewportXYZ(const float viewportWidth, const float viewportHeight, const float viewportX, const float viewportY); void getViewportXY(const float viewportWidth, const float viewportHeight, float& viewportXOut, float& viewportYOut) const; void getSurfaceSpace(StructureEnum::Enum& structureOut, int32_t& surfaceNumberOfNodesOut, int32_t& surfaceNodeIndexOut) const; void getSurfaceSpace(StructureEnum::Enum& structureOut, int32_t& surfaceNumberOfNodesOut, int32_t& surfaceNodeIndexOut, float& surfaceOffsetLengthOut, AnnotationSurfaceOffsetVectorTypeEnum::Enum& surfaceOffsetVectorTypeOut) const; void setSurfaceSpace(const StructureEnum::Enum structure, const int32_t surfaceNumberOfNodes, const int32_t surfaceNodeIndex); void setSurfaceSpace(const StructureEnum::Enum structure, const int32_t surfaceNumberOfNodes, const int32_t surfaceNodeIndex, const float surfaceOffsetLength, const AnnotationSurfaceOffsetVectorTypeEnum::Enum surfaceOffsetVectorType); float getSurfaceOffsetLength() const; StructureEnum::Enum getSurfaceStructure() const; AnnotationSurfaceOffsetVectorTypeEnum::Enum getSurfaceOffsetVectorType() 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); static float getDefaultSurfaceOffsetLength(); // 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 implemetation 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 copyHelperAnnotationCoordinate(const AnnotationCoordinate& obj); void initializeAnnotationCoordinateMembers(); SceneClassAssistant* m_sceneAssistant; float m_xyz[3]; int32_t m_surfaceSpaceNodeIndex; int32_t m_surfaceSpaceNumberOfNodes; StructureEnum::Enum m_surfaceSpaceStructure; float m_surfaceOffsetLength; AnnotationSurfaceOffsetVectorTypeEnum::Enum m_surfaceOffsetVectorType; // ADD_NEW_MEMBERS_HERE }; #ifdef __ANNOTATION_COORDINATE_DECLARE__ // #endif // __ANNOTATION_COORDINATE_DECLARE__ } // namespace #endif //__ANNOTATION_COORDINATE_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Annotations/AnnotationCoordinateSpaceEnum.cxx000066400000000000000000000330541300200146000315150ustar00rootroot00000000000000 /*LICENSE_START*/ /* * Copyright (C) 2015 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 __ANNOTATION_COORDINATE_SPACE_ENUM_DECLARE__ #include "AnnotationCoordinateSpaceEnum.h" #undef __ANNOTATION_COORDINATE_SPACE_ENUM_DECLARE__ #include "CaretAssert.h" using namespace caret; /** * \class caret::AnnotationCoordinateSpaceEnum * \brief Coordinate space of annotation. * * 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_annotationCoordinateSpaceEnumComboBox; * * Declare a slot that is called when user changes selection * private slots: * void annotationCoordinateSpaceEnumComboBoxItemActivated(); * * Implementation File (.cxx) * Include the header files * #include "EnumComboBoxTemplate.h" * #include "AnnotationCoordinateSpaceEnum.h" * * Instatiate: * m_annotationCoordinateSpaceEnumComboBox = new EnumComboBoxTemplate(this); * m_annotationCoordinateSpaceEnumComboBox->setup(); * * Get notified when the user changes the selection: * QObject::connect(m_annotationCoordinateSpaceEnumComboBox, SIGNAL(itemActivated()), * this, SLOT(annotationCoordinateSpaceEnumComboBoxItemActivated())); * * Update the selection: * m_annotationCoordinateSpaceEnumComboBox->setSelectedItem(NEW_VALUE); * * Read the selection: * const AnnotationCoordinateSpaceEnum::Enum VARIABLE = m_annotationCoordinateSpaceEnumComboBox->getSelectedItem(); * */ /** * Constructor. * * @param enumValue * An enumerated value. * @param name * Name of enumerated value. * * @param guiName * User-friendly name for use in user-interface. * @param guiAbbreviatedName * Abbreviated User-friendly name for use in user-interface. */ AnnotationCoordinateSpaceEnum::AnnotationCoordinateSpaceEnum(const Enum enumValue, const AString& name, const AString& guiName, const AString& guiAbbreviatedName) { this->enumValue = enumValue; this->integerCode = integerCodeCounter++; this->name = name; this->guiName = guiName; this->guiAbbreviatedName = guiAbbreviatedName; } /** * Destructor. */ AnnotationCoordinateSpaceEnum::~AnnotationCoordinateSpaceEnum() { } /** * Initialize the enumerated metadata. */ void AnnotationCoordinateSpaceEnum::initialize() { if (initializedFlag) { return; } initializedFlag = true; enumData.push_back(AnnotationCoordinateSpaceEnum(PIXELS, "PIXELS", "Pixels", "P")); enumData.push_back(AnnotationCoordinateSpaceEnum(STEREOTAXIC, "STEREOTAXIC", "Stereotaxic", "St")); enumData.push_back(AnnotationCoordinateSpaceEnum(SURFACE, "SURFACE", "Surface", "Sf")); enumData.push_back(AnnotationCoordinateSpaceEnum(TAB, "TAB", "Tab", "T")); enumData.push_back(AnnotationCoordinateSpaceEnum(WINDOW, "WINDOW", "Window", "W")); } /** * 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 AnnotationCoordinateSpaceEnum* AnnotationCoordinateSpaceEnum::findData(const Enum enumValue) { if (initializedFlag == false) initialize(); size_t num = enumData.size(); for (size_t i = 0; i < num; i++) { const AnnotationCoordinateSpaceEnum* 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 AnnotationCoordinateSpaceEnum::toName(Enum enumValue) { if (initializedFlag == false) initialize(); const AnnotationCoordinateSpaceEnum* enumInstance = findData(enumValue); return enumInstance->name; } /** * Get an enumerated value corresponding to its name. * @param name In * 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. */ AnnotationCoordinateSpaceEnum::Enum AnnotationCoordinateSpaceEnum::fromName(const AString& nameIn, bool* isValidOut) { if (initializedFlag == false) initialize(); AString name = nameIn; if (name == "MODEL") { name = "STEREOTAXIC"; } bool validFlag = false; Enum enumValue = AnnotationCoordinateSpaceEnum::enumData[0].enumValue; for (std::vector::iterator iter = enumData.begin(); iter != enumData.end(); iter++) { const AnnotationCoordinateSpaceEnum& 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 AnnotationCoordinateSpaceEnum")); } return enumValue; } /** * Get a GUI string representation of the enumerated type. * @param enumValue * Enumerated value. * @return * String representing enumerated value. */ AString AnnotationCoordinateSpaceEnum::toGuiName(Enum enumValue) { if (initializedFlag == false) initialize(); const AnnotationCoordinateSpaceEnum* enumInstance = findData(enumValue); return enumInstance->guiName; } /** * Get an enumerated value corresponding to its GUI name. * @param guiNameIn * 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. */ AnnotationCoordinateSpaceEnum::Enum AnnotationCoordinateSpaceEnum::fromGuiName(const AString& guiNameIn, bool* isValidOut) { if (initializedFlag == false) initialize(); AString guiName = guiNameIn; if (guiName == "Model") { guiName = "Stereotaxic"; } bool validFlag = false; Enum enumValue = AnnotationCoordinateSpaceEnum::enumData[0].enumValue; for (std::vector::iterator iter = enumData.begin(); iter != enumData.end(); iter++) { const AnnotationCoordinateSpaceEnum& 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 AnnotationCoordinateSpaceEnum")); } return enumValue; } /** * Get a GUI abbreviated string representation of the enumerated type. * @param enumValue * Enumerated value. * @return * String representing enumerated value. */ AString AnnotationCoordinateSpaceEnum::toGuiAbbreviatedName(Enum enumValue) { if (initializedFlag == false) initialize(); const AnnotationCoordinateSpaceEnum* enumInstance = findData(enumValue); return enumInstance->guiAbbreviatedName; } /** * @return Text that is displayed for a space's tooltip in the GUI. */ AString AnnotationCoordinateSpaceEnum::toToolTip(Enum enumValue) { AString text; switch (enumValue) { case PIXELS: text = "New annotation is drawn at an XY pixel coordinate"; break; case STEREOTAXIC: text = "New annotation is drawn at a surface/volume XYZ coordinate"; break; case SURFACE: text = "New annotation is drawn at a surface vertex"; break; case TAB: text = "New annotation is drawn at an XY coordinate in the tab"; break; case WINDOW: text = "New annotation is drawn at an XY coordinate in the window"; break; } return text; } /** * Get the integer code for a data type. * * @return * Integer code for data type. */ int32_t AnnotationCoordinateSpaceEnum::toIntegerCode(Enum enumValue) { if (initializedFlag == false) initialize(); const AnnotationCoordinateSpaceEnum* 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. */ AnnotationCoordinateSpaceEnum::Enum AnnotationCoordinateSpaceEnum::fromIntegerCode(const int32_t integerCode, bool* isValidOut) { if (initializedFlag == false) initialize(); bool validFlag = false; Enum enumValue = AnnotationCoordinateSpaceEnum::enumData[0].enumValue; for (std::vector::iterator iter = enumData.begin(); iter != enumData.end(); iter++) { const AnnotationCoordinateSpaceEnum& 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 AnnotationCoordinateSpaceEnum")); } 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 AnnotationCoordinateSpaceEnum::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 AnnotationCoordinateSpaceEnum::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(AnnotationCoordinateSpaceEnum::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 AnnotationCoordinateSpaceEnum::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(AnnotationCoordinateSpaceEnum::toGuiName(iter->enumValue)); } if (isSorted) { std::sort(allGuiNames.begin(), allGuiNames.end()); } } connectome-workbench-1.2.3+git41-gc4c6c90/src/Annotations/AnnotationCoordinateSpaceEnum.h000066400000000000000000000073641300200146000311470ustar00rootroot00000000000000#ifndef __ANNOTATION_COORDINATE_SPACE_ENUM_H__ #define __ANNOTATION_COORDINATE_SPACE_ENUM_H__ /*LICENSE_START*/ /* * Copyright (C) 2015 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 AnnotationCoordinateSpaceEnum { public: /** * Enumerated values. */ enum Enum { /** Annotation in pixels (origin is bottom, left corner). */ PIXELS, /** Annotation in stereotaxic (3D) space */ STEREOTAXIC, /** Annotation on surface node */ SURFACE, /** Annotation in tab space */ TAB, /** Annotation in window space */ WINDOW }; ~AnnotationCoordinateSpaceEnum(); static AString toName(Enum enumValue); static Enum fromName(const AString& name, bool* isValidOut); static AString toGuiName(Enum enumValue); static AString toGuiAbbreviatedName(Enum enumValue); static Enum fromGuiName(const AString& guiName, bool* isValidOut); static AString toToolTip(Enum enumValue); 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: AnnotationCoordinateSpaceEnum(const Enum enumValue, const AString& name, const AString& guiName, const AString& guiAbbreviatedName); static const AnnotationCoordinateSpaceEnum* 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; /** An abbreviated user-friendly name that is displayed in the GUI */ AString guiAbbreviatedName; /** A user-friendly name that is displayed in the GUI */ AString guiName; }; #ifdef __ANNOTATION_COORDINATE_SPACE_ENUM_DECLARE__ std::vector AnnotationCoordinateSpaceEnum::enumData; bool AnnotationCoordinateSpaceEnum::initializedFlag = false; int32_t AnnotationCoordinateSpaceEnum::integerCodeCounter = 0; #endif // __ANNOTATION_COORDINATE_SPACE_ENUM_DECLARE__ } // namespace #endif //__ANNOTATION_COORDINATE_SPACE_ENUM_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Annotations/AnnotationDistributeEnum.cxx000066400000000000000000000252461300200146000305740ustar00rootroot00000000000000 /*LICENSE_START*/ /* * Copyright (C) 2015 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 __ANNOTATION_DISTRIBUTE_ENUM_DECLARE__ #include "AnnotationDistributeEnum.h" #undef __ANNOTATION_DISTRIBUTE_ENUM_DECLARE__ #include "CaretAssert.h" using namespace caret; /** * \class caret::AnnotationDistributeEnum * \brief Enumerated type for distributing annotations. * * 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_annotationDistributeEnumComboBox; * * Declare a slot that is called when user changes selection * private slots: * void annotationDistributeEnumComboBoxItemActivated(); * * Implementation File (.cxx) * Include the header files * #include "EnumComboBoxTemplate.h" * #include "AnnotationDistributeEnum.h" * * Instatiate: * m_annotationDistributeEnumComboBox = new EnumComboBoxTemplate(this); * m_annotationDistributeEnumComboBox->setup(); * * Get notified when the user changes the selection: * QObject::connect(m_annotationDistributeEnumComboBox, SIGNAL(itemActivated()), * this, SLOT(annotationDistributeEnumComboBoxItemActivated())); * * Update the selection: * m_annotationDistributeEnumComboBox->setSelectedItem(NEW_VALUE); * * Read the selection: * const AnnotationDistributeEnum::Enum VARIABLE = m_annotationDistributeEnumComboBox->getSelectedItem(); * */ /** * Constructor. * * @param enumValue * An enumerated value. * @param name * Name of enumerated value. * * @param guiName * User-friendly name for use in user-interface. */ AnnotationDistributeEnum::AnnotationDistributeEnum(const Enum enumValue, const AString& name, const AString& guiName) { this->enumValue = enumValue; this->integerCode = integerCodeCounter++; this->name = name; this->guiName = guiName; } /** * Destructor. */ AnnotationDistributeEnum::~AnnotationDistributeEnum() { } /** * Initialize the enumerated metadata. */ void AnnotationDistributeEnum::initialize() { if (initializedFlag) { return; } initializedFlag = true; enumData.push_back(AnnotationDistributeEnum(HORIZONTALLY, "HORIZONTALLY", "Distribute Horizontally")); enumData.push_back(AnnotationDistributeEnum(VERTICALLY, "VERTICALLY", "Distribute Vertically")); } /** * 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 AnnotationDistributeEnum* AnnotationDistributeEnum::findData(const Enum enumValue) { if (initializedFlag == false) initialize(); size_t num = enumData.size(); for (size_t i = 0; i < num; i++) { const AnnotationDistributeEnum* 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 AnnotationDistributeEnum::toName(Enum enumValue) { if (initializedFlag == false) initialize(); const AnnotationDistributeEnum* 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. */ AnnotationDistributeEnum::Enum AnnotationDistributeEnum::fromName(const AString& name, bool* isValidOut) { if (initializedFlag == false) initialize(); bool validFlag = false; Enum enumValue = AnnotationDistributeEnum::enumData[0].enumValue; for (std::vector::iterator iter = enumData.begin(); iter != enumData.end(); iter++) { const AnnotationDistributeEnum& 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 AnnotationDistributeEnum")); } return enumValue; } /** * Get a GUI string representation of the enumerated type. * @param enumValue * Enumerated value. * @return * String representing enumerated value. */ AString AnnotationDistributeEnum::toGuiName(Enum enumValue) { if (initializedFlag == false) initialize(); const AnnotationDistributeEnum* 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. */ AnnotationDistributeEnum::Enum AnnotationDistributeEnum::fromGuiName(const AString& guiName, bool* isValidOut) { if (initializedFlag == false) initialize(); bool validFlag = false; Enum enumValue = AnnotationDistributeEnum::enumData[0].enumValue; for (std::vector::iterator iter = enumData.begin(); iter != enumData.end(); iter++) { const AnnotationDistributeEnum& 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 AnnotationDistributeEnum")); } return enumValue; } /** * Get the integer code for a data type. * * @return * Integer code for data type. */ int32_t AnnotationDistributeEnum::toIntegerCode(Enum enumValue) { if (initializedFlag == false) initialize(); const AnnotationDistributeEnum* 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. */ AnnotationDistributeEnum::Enum AnnotationDistributeEnum::fromIntegerCode(const int32_t integerCode, bool* isValidOut) { if (initializedFlag == false) initialize(); bool validFlag = false; Enum enumValue = AnnotationDistributeEnum::enumData[0].enumValue; for (std::vector::iterator iter = enumData.begin(); iter != enumData.end(); iter++) { const AnnotationDistributeEnum& 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 AnnotationDistributeEnum")); } 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 AnnotationDistributeEnum::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 AnnotationDistributeEnum::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(AnnotationDistributeEnum::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 AnnotationDistributeEnum::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(AnnotationDistributeEnum::toGuiName(iter->enumValue)); } if (isSorted) { std::sort(allGuiNames.begin(), allGuiNames.end()); } } connectome-workbench-1.2.3+git41-gc4c6c90/src/Annotations/AnnotationDistributeEnum.h000066400000000000000000000061431300200146000302140ustar00rootroot00000000000000#ifndef __ANNOTATION_DISTRIBUTE_ENUM_H__ #define __ANNOTATION_DISTRIBUTE_ENUM_H__ /*LICENSE_START*/ /* * Copyright (C) 2015 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 AnnotationDistributeEnum { public: /** * Enumerated values. */ enum Enum { /** */ HORIZONTALLY, /** */ VERTICALLY }; ~AnnotationDistributeEnum(); 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: AnnotationDistributeEnum(const Enum enumValue, const AString& name, const AString& guiName); static const AnnotationDistributeEnum* 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 __ANNOTATION_DISTRIBUTE_ENUM_DECLARE__ std::vector AnnotationDistributeEnum::enumData; bool AnnotationDistributeEnum::initializedFlag = false; int32_t AnnotationDistributeEnum::integerCodeCounter = 0; #endif // __ANNOTATION_DISTRIBUTE_ENUM_DECLARE__ } // namespace #endif //__ANNOTATION_DISTRIBUTE_ENUM_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Annotations/AnnotationEditingSelectionInformation.cxx000066400000000000000000000205541300200146000332650ustar00rootroot00000000000000 /*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 __ANNOTATION_SELECTION_INFORMATION_DECLARE__ #include "AnnotationEditingSelectionInformation.h" #undef __ANNOTATION_SELECTION_INFORMATION_DECLARE__ #include "Annotation.h" #include "AnnotationGroup.h" #include "CaretAssert.h" #include "CaretLogger.h" using namespace caret; /** * \class caret::AnnotationEditingSelectionInformation * \brief Contains selected annotations and related information. * \ingroup Annotations */ /** * Constructor. * * @param windowIndex * Index of window in which annotations are selected. */ AnnotationEditingSelectionInformation::AnnotationEditingSelectionInformation(const int32_t windowIndex) : CaretObject(), m_windowIndex(windowIndex) { } /** * Destructor. */ AnnotationEditingSelectionInformation::~AnnotationEditingSelectionInformation() { } /** * Copy constructor. * @param obj * Object that is copied. */ AnnotationEditingSelectionInformation::AnnotationEditingSelectionInformation(const AnnotationEditingSelectionInformation& obj) : CaretObject(obj), m_windowIndex(obj.m_windowIndex) { this->copyHelperAnnotationSelectionInformation(obj); } /** * Assignment operator. * @param obj * Data copied from obj to this. * @return * Reference to this object. */ AnnotationEditingSelectionInformation& AnnotationEditingSelectionInformation::operator=(const AnnotationEditingSelectionInformation& obj) { if (this != &obj) { if (m_windowIndex == obj.m_windowIndex) { CaretObject::operator=(obj); this->copyHelperAnnotationSelectionInformation(obj); } else { const QString msg("Cannot copy AnnotationEditingSelectionInformation to different window."); CaretAssertMessage(0, msg); CaretLogSevere(msg); m_annotations.clear(); } } return *this; } /** * Helps with copying an object of this type. * @param obj * Object that is copied. */ void AnnotationEditingSelectionInformation::copyHelperAnnotationSelectionInformation(const AnnotationEditingSelectionInformation& obj) { CaretAssertMessage(0, "Copying not allowed"); m_annotations = obj.m_annotations; } /** * */ void AnnotationEditingSelectionInformation::clear() { m_annotations.clear(); m_annotationGroupKeys.clear(); m_groupingValid = false; m_regroupValid = false; m_ungroupValid = false; } /** * @return Index of window in which annotations are selected. */ int32_t AnnotationEditingSelectionInformation::getWindowIndex() const { return m_windowIndex; } /** * Update the selected annotations. If any of the given annotations * are in a 'user' annotation group, all annotations in the 'user' * annotation group are selected. * * @param annotation. */ void AnnotationEditingSelectionInformation::update(const std::vector& selectedAnnotations) { clear(); m_annotations = selectedAnnotations; std::set groupKeysSet; for (std::vector::iterator annIter = m_annotations.begin(); annIter != m_annotations.end(); annIter++) { groupKeysSet.insert((*annIter)->getAnnotationGroupKey()); } // std::set groupSet; // for (std::vector::iterator annIter = m_annotations.begin(); // annIter != m_annotations.end(); // annIter++) { // const AnnotationGroup* annGroup = (*annIter)->getAnnotationGroup(); // CaretAssert(annGroup); // groupSet.insert(annGroup); // } 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; } } } /* * Is ANY annotation selected */ if (m_annotations.size() > 0) { /* * If all annotations are in a space group and were * in the same previous user group, enable regroup. */ if (numGroups == 1) { CaretAssertVectorIndex(m_annotationGroupKeys, 0); const AnnotationGroupKey& groupKey = m_annotationGroupKeys[0]; switch (groupKey.getGroupType()) { case AnnotationGroupTypeEnum::INVALID: break; case AnnotationGroupTypeEnum::SPACE: /* * The current group type is a 'space' group but * the annotation(s) were previously in a 'user' * group so enable 'regroup'. */ if (groupKey.getUserGroupUniqueKey() > 0) { m_regroupValid = true; } break; case AnnotationGroupTypeEnum::USER: break; } } } } /** * @return Vector containing the selected annotation group keys. */ std::vector AnnotationEditingSelectionInformation::getSelectedAnnotationGroupKeys() const { return m_annotationGroupKeys; } /** * @return Number of annotations selected. */ int32_t AnnotationEditingSelectionInformation::getNumberOfSelectedAnnotations() const { return m_annotations.size(); } /** * @return True if any annotations are selected. */ bool AnnotationEditingSelectionInformation::isAnyAnnotationSelected() const { return ( ! m_annotations.empty()); } /** * @return Vector containing the selected annotations. */ std::vector AnnotationEditingSelectionInformation::getAnnotationsSelectedForEditing() const { return m_annotations; } /** * Get the selected annotations. * * @param annotationsOut * Output containing the selected anntotations. */ void AnnotationEditingSelectionInformation::getAnnotationsSelectedForEditing(std::vector& annotationsOut) const { annotationsOut = m_annotations; } bool AnnotationEditingSelectionInformation::isGroupingModeValid(const AnnotationGroupingModeEnum::Enum groupingMode) const { bool valid = false; switch (groupingMode) { case AnnotationGroupingModeEnum::GROUP: valid = m_groupingValid; break; case AnnotationGroupingModeEnum::REGROUP: valid = m_regroupValid; break; case AnnotationGroupingModeEnum::UNGROUP: valid = m_ungroupValid; break; } return valid; } /** * Get a description of this object's content. * @return String describing this object's content. */ AString AnnotationEditingSelectionInformation::toString() const { return "AnnotationEditingSelectionInformation"; } connectome-workbench-1.2.3+git41-gc4c6c90/src/Annotations/AnnotationEditingSelectionInformation.h000066400000000000000000000056071300200146000327140ustar00rootroot00000000000000#ifndef __ANNOTATION_SELECTION_INFORMATION_H__ #define __ANNOTATION_SELECTION_INFORMATION_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 "AnnotationGroupKey.h" #include "AnnotationGroupingModeEnum.h" #include "CaretObject.h" namespace caret { class Annotation; class AnnotationEditingSelectionInformation : public CaretObject { public: AnnotationEditingSelectionInformation(const int32_t windowIndex); virtual ~AnnotationEditingSelectionInformation(); void clear(); void update(const std::vector& selectedAnnotations); int32_t getWindowIndex() const; std::vector getSelectedAnnotationGroupKeys() const; int32_t getNumberOfSelectedAnnotations() const; bool isAnyAnnotationSelected() const; std::vector getAnnotationsSelectedForEditing() const; void getAnnotationsSelectedForEditing(std::vector& annotationsOut) const; bool isGroupingModeValid(const AnnotationGroupingModeEnum::Enum groupingMode) const; // ADD_NEW_METHODS_HERE virtual AString toString() const; private: AnnotationEditingSelectionInformation(const AnnotationEditingSelectionInformation& obj); AnnotationEditingSelectionInformation& operator=(const AnnotationEditingSelectionInformation& obj); void copyHelperAnnotationSelectionInformation(const AnnotationEditingSelectionInformation& obj); const int32_t m_windowIndex; std::vector m_annotationGroupKeys; std::vector m_annotations; bool m_groupingValid; bool m_ungroupValid; bool m_regroupValid; // ADD_NEW_MEMBERS_HERE }; #ifdef __ANNOTATION_SELECTION_INFORMATION_DECLARE__ // #endif // __ANNOTATION_SELECTION_INFORMATION_DECLARE__ } // namespace #endif //__ANNOTATION_SELECTION_INFORMATION_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Annotations/AnnotationFontAttributesInterface.h000066400000000000000000000061141300200146000320450ustar00rootroot00000000000000#ifndef __ANNOTATION_FONT_STYLE_INTERFACE_H__ #define __ANNOTATION_FONT_STYLE_INTERFACE_H__ /*LICENSE_START*/ /* * Copyright (C) 2015 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 "AnnotationTextFontNameEnum.h" #include "CaretColorEnum.h" namespace caret { class AnnotationFontAttributesInterface { public: AnnotationFontAttributesInterface() { } virtual ~AnnotationFontAttributesInterface() { } virtual AnnotationTextFontNameEnum::Enum getFont() const = 0; virtual void setFont(const AnnotationTextFontNameEnum::Enum font) = 0; virtual float getFontPercentViewportSize() const = 0; virtual void setFontPercentViewportSize(const float fontPercentViewportHeight) = 0; virtual CaretColorEnum::Enum getTextColor() const = 0; virtual void setTextColor(const CaretColorEnum::Enum color) = 0; virtual void getTextColorRGBA(float rgbaOut[4]) const = 0; virtual void getTextColorRGBA(uint8_t rgbaOut[4]) const = 0; virtual void getCustomTextColor(float rgbaOut[4]) const = 0; virtual void getCustomTextColor(uint8_t rgbaOut[4]) const = 0; virtual void setCustomTextColor(const float rgba[4]) = 0; 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; virtual bool isItalicStyleEnabled() const = 0; virtual void setItalicStyleEnabled(const bool enabled) = 0; virtual bool isUnderlineStyleEnabled() const = 0; virtual void setUnderlineStyleEnabled(const bool enabled) = 0; // ADD_NEW_METHODS_HERE private: AnnotationFontAttributesInterface(const AnnotationFontAttributesInterface&); AnnotationFontAttributesInterface& operator=(const AnnotationFontAttributesInterface&); // ADD_NEW_MEMBERS_HERE }; #ifdef __ANNOTATION_FONT_STYLE_INTERFACE_DECLARE__ // #endif // __ANNOTATION_FONT_STYLE_INTERFACE_DECLARE__ } // namespace #endif //__ANNOTATION_FONT_STYLE_INTERFACE_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Annotations/AnnotationGroup.cxx000066400000000000000000000672641300200146000267330ustar00rootroot00000000000000 /*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 __ANNOTATION_GROUP_DECLARE__ #include "AnnotationGroup.h" #undef __ANNOTATION_GROUP_DECLARE__ #include "Annotation.h" #include "AnnotationPointSizeText.h" #include "BrainConstants.h" #include "CaretAssert.h" #include "CaretLogger.h" #include "DisplayGroupAndTabItemHelper.h" #include "SceneClass.h" #include "SceneClassAssistant.h" using namespace caret; /** * \class caret::AnnotationGroup * \brief Contains a group of annotations. * \ingroup Annotations */ /** * Constructor. * * @param annotationFile * File to which this group belongs. * @param groupType * Type of annotation group. * @param uniqueKey * Unique key for this group. * @param coordinateSpace * Annotation coordinate space for the group. * @param tabOrWindowIndex * Index of tab or window for tab or window space. */ AnnotationGroup::AnnotationGroup(AnnotationFile* annotationFile, const AnnotationGroupTypeEnum::Enum groupType, const int32_t uniqueKey, const AnnotationCoordinateSpaceEnum::Enum coordinateSpace, const int32_t tabOrWindowIndex) : CaretObjectTracksModification(), DisplayGroupAndTabItemInterface(), SceneableInterface() { CaretAssert(annotationFile); CaretAssert(groupType != AnnotationGroupTypeEnum::INVALID); CaretAssert(uniqueKey > 0); CaretAssert(coordinateSpace != AnnotationCoordinateSpaceEnum::PIXELS); initializeInstance(); m_groupKey.setAnnotationFile(annotationFile); m_groupKey.setGroupType(groupType); switch (groupType) { case AnnotationGroupTypeEnum::INVALID: CaretAssertMessage(0, "Should never get here"); break; case AnnotationGroupTypeEnum::SPACE: m_groupKey.setSpaceGroupUniqueKey(uniqueKey); break; case AnnotationGroupTypeEnum::USER: m_groupKey.setUserGroupUniqueKey(uniqueKey); break; } m_coordinateSpace = coordinateSpace; m_tabOrWindowIndex = tabOrWindowIndex; switch (m_coordinateSpace) { case AnnotationCoordinateSpaceEnum::PIXELS: CaretAssert(0); break; case AnnotationCoordinateSpaceEnum::STEREOTAXIC: break; case AnnotationCoordinateSpaceEnum::SURFACE: break; case AnnotationCoordinateSpaceEnum::TAB: CaretAssert((tabOrWindowIndex >= 0) && (tabOrWindowIndex < BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS)); break; case AnnotationCoordinateSpaceEnum::WINDOW: CaretAssert((tabOrWindowIndex >= 0) && (tabOrWindowIndex < BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_WINDOWS)); break; } setModified(); } /** * Destructor. */ AnnotationGroup::~AnnotationGroup() { m_annotations.clear(); delete m_displayGroupAndTabItemHelper; delete m_sceneAssistant; } /** * Copy constructor. * @param obj * Object that is copied. */ AnnotationGroup::AnnotationGroup(const AnnotationGroup& obj) : CaretObjectTracksModification(obj), DisplayGroupAndTabItemInterface(obj), SceneableInterface(obj) { initializeInstance(); this->copyHelperAnnotationGroup(obj); } /** * Assignment operator. * @param obj * Data copied from obj to this. * @return * Reference to this object. */ AnnotationGroup& AnnotationGroup::operator=(const AnnotationGroup& obj) { if (this != &obj) { CaretObjectTracksModification::operator=(obj); this->copyHelperAnnotationGroup(obj); } return *this; } /** * Helps with copying an object of this type. * @param obj * Object that is copied. */ void AnnotationGroup::copyHelperAnnotationGroup(const AnnotationGroup& obj) { m_groupKey = obj.m_groupKey; m_coordinateSpace = obj.m_coordinateSpace; m_name = obj.m_name; m_tabOrWindowIndex = obj.m_tabOrWindowIndex; *m_displayGroupAndTabItemHelper = *obj.m_displayGroupAndTabItemHelper; CaretAssertMessage(0, "What to do with annotations remove copy constructor/operator="); } /** * Initialize an instance. */ void AnnotationGroup::initializeInstance() { m_groupKey.reset(); m_coordinateSpace = AnnotationCoordinateSpaceEnum::PIXELS; m_name = ""; m_tabOrWindowIndex = -1; m_displayGroupAndTabItemHelper = new DisplayGroupAndTabItemHelper(); m_sceneAssistant = new SceneClassAssistant(); m_sceneAssistant->add("m_displayGroupAndTabItemHelper", "DisplayGroupAndTabItemHelper", m_displayGroupAndTabItemHelper); } /** * @return Is this group valid? */ bool AnnotationGroup::isEmpty() const { return (m_annotations.empty()); } /** * @return The annotation group key. */ AnnotationGroupKey AnnotationGroup::getAnnotationGroupKey() const { return m_groupKey; } /** * @return Annotation file that contains this group. */ AnnotationFile* AnnotationGroup::getAnnotationFile() const { return m_groupKey.getAnnotationFile(); } /** * @return The group type. */ AnnotationGroupTypeEnum::Enum AnnotationGroup::getGroupType() const { return m_groupKey.getGroupType(); } /** * @return Coordinate space of the annotations. */ AnnotationCoordinateSpaceEnum::Enum AnnotationGroup::getCoordinateSpace() const { return m_coordinateSpace; } /** * Get a description of this object's content. * @return String describing this object's content. */ AString AnnotationGroup::toString() const { return "AnnotationGroup"; } /** * @return Name of the annotation group. */ AString AnnotationGroup::getName() const { if (m_name.isEmpty()) { AString spaceName = AnnotationCoordinateSpaceEnum::toGuiName(m_coordinateSpace); switch (m_coordinateSpace) { case AnnotationCoordinateSpaceEnum::PIXELS: CaretAssertMessage(0, "Should never be pixels"); break; case AnnotationCoordinateSpaceEnum::STEREOTAXIC: break; case AnnotationCoordinateSpaceEnum::SURFACE: break; case AnnotationCoordinateSpaceEnum::TAB: spaceName.append(" " + AString::number(getTabOrWindowIndex() + 1)); break; case AnnotationCoordinateSpaceEnum::WINDOW: spaceName.append(" " + AString::number(getTabOrWindowIndex() + 1)); break; } switch (m_groupKey.getGroupType()) { case AnnotationGroupTypeEnum::INVALID: CaretAssertMessage(0, "Should never get here"); break; case AnnotationGroupTypeEnum::SPACE: // m_name.append("Space Group: " // + spaceName); m_name.append(spaceName); break; case AnnotationGroupTypeEnum::USER: // m_name.append("User Group " // + AString::number(m_groupKey.getUserGroupUniqueKey()) // + ": " // + spaceName); m_name.append("Group " + AString::number(m_groupKey.getUserGroupUniqueKey()) + ": " + spaceName); break; } } return m_name; } /** * @return Index or tab or window for tab or window space. */ int32_t AnnotationGroup::getTabOrWindowIndex() const { return m_tabOrWindowIndex; } /** * @return Unique key displayed in annotation group name. */ int32_t AnnotationGroup::getUniqueKey() const { int32_t uniqueKey = -1; switch (m_groupKey.getGroupType()) { case AnnotationGroupTypeEnum::INVALID: CaretAssertMessage(0, "Should never get here"); break; case AnnotationGroupTypeEnum::SPACE: uniqueKey = m_groupKey.getSpaceGroupUniqueKey(); break; case AnnotationGroupTypeEnum::USER: uniqueKey = m_groupKey.getUserGroupUniqueKey(); break; } return uniqueKey; } /** * Private method for adding annotations to this file. * * In the GUI, annotations are added using the AnnotationRedoUndoCommand * which allows undo/redo operations. * * @param annotation * Annotation that is added. */ void AnnotationGroup::addAnnotationPrivate(Annotation* annotation) { if ( ! validateAddedAnnotation(annotation)) { delete annotation; return; } assignGroupKeyToAnnotation(annotation); m_annotations.push_back(QSharedPointer(annotation)); setModified(); } /** * Private method for adding annotations to this file using shared pointer. * * In the GUI, annotations are added using the AnnotationRedoUndoCommand * which allows undo/redo operations. * * @param annotation * Annotation that is added. */ void AnnotationGroup::addAnnotationPrivateSharedPointer(QSharedPointer& annotation) { if ( ! validateAddedAnnotation(annotation.data())) { return; } assignGroupKeyToAnnotation(annotation.data()); m_annotations.push_back(annotation); setModified(); } /** * Assign group key for the given annotation. * * @param annotation * The annotation. */ void AnnotationGroup::assignGroupKeyToAnnotation(Annotation* annotation) { annotation->setAnnotationGroupKey(m_groupKey); annotation->setItemParent(this); } /** * Validate the annotation that is being added to this group. * * @param annotation * Pointer to annotation. * @return * True if the annotation is compatible with this group. */ bool AnnotationGroup::validateAddedAnnotation(const Annotation* annotation) { if (annotation->getType() == AnnotationTypeEnum::TEXT) { const AnnotationPointSizeText* pointSizeAnnotation = dynamic_cast(annotation); if (pointSizeAnnotation != NULL) { CaretLogWarning("Point size text annotations are not supported in AnnotationGroup. " "The annotation has been discarded."); return false; } } const AnnotationCoordinateSpaceEnum::Enum space = annotation->getCoordinateSpace(); if (space != m_coordinateSpace) { CaretLogSevere("Attempting to add annotation with non-matching coordinate space"); CaretAssert(0); return false; } switch (space) { case AnnotationCoordinateSpaceEnum::PIXELS: break; case AnnotationCoordinateSpaceEnum::STEREOTAXIC: break; case AnnotationCoordinateSpaceEnum::SURFACE: break; case AnnotationCoordinateSpaceEnum::TAB: if (m_tabOrWindowIndex != annotation->getTabIndex()) { CaretLogSevere("Attempting to add anntation with non-matching tab index: "); CaretAssert(0); return false; } break; case AnnotationCoordinateSpaceEnum::WINDOW: if (m_tabOrWindowIndex != annotation->getWindowIndex()) { CaretLogSevere("Attempting to add anntation with non-matching tab index: "); CaretAssert(0); return false; } break; } return true; } /** * @return The maximum unique key found in this group and * its annotations. */ int32_t AnnotationGroup::getMaximumUniqueKey() const { int32_t maxUniqueKey = getUniqueKey(); for (AnnotationConstIterator iter = m_annotations.begin(); iter != m_annotations.end(); iter++) { maxUniqueKey = std::max(maxUniqueKey, (*iter)->getUniqueKey()); } return maxUniqueKey; } /** * Remove all annotations from this group. * * @param allRemovedAnnotationsOut * Output containing all annotations from this group. */ void AnnotationGroup::removeAllAnnotations(std::vector >& allRemovedAnnotationsOut) { allRemovedAnnotationsOut = m_annotations; m_annotations.clear(); setModified(); } /** * Remove the annotation. NOTE: The annotation is NOT deleted * but instead it is returned so that it can be 'undeleted' * or 're-pasted'. * * @param annotation * Annotation that is removed. * @param removedAnnotationOut * Shared pointer for annotation that was removed so * the file can later undelete the annotation. Only * valid when true is returned. * @return * True if the annotation was removed, otherwise false. */ bool AnnotationGroup::removeAnnotation(Annotation* annotation, QSharedPointer& removedAnnotationOut) { removedAnnotationOut.clear(); for (AnnotationIterator iter = m_annotations.begin(); iter != m_annotations.end(); iter++) { QSharedPointer& annotationPointer = *iter; if (annotationPointer == annotation) { annotation->setItemParent(NULL); removedAnnotationOut = annotationPointer; m_annotations.erase(iter); setModified(); /* * Successfully removed */ return true; } } /* * Annotation not in this file */ return false; } /** * @return Number of annotations in this group. */ int32_t AnnotationGroup::getNumberOfAnnotations() const { return m_annotations.size(); } /** * @param index * Get the annotation at the given index. * @return * Annotation at the given index. */ Annotation* AnnotationGroup::getAnnotation(const int32_t index) { CaretAssertVectorIndex(m_annotations, index); return m_annotations[index].data(); } /** * Are all of the given annotations in this group? * * @param annotations * Annotations tested for membership in this group. * @return * True if all of the annotations are in this group. * False if (a) not all annotations are in this group; * or if (b) the annotations are empty * or if (c) this group contains no annotations. */ bool AnnotationGroup::containsAllAnnotation(const std::vector annotations) const { if (annotations.empty()) { return false; } if (m_annotations.empty()) { return false; } for (std::vector::const_iterator annIter = annotations.begin(); annIter != annotations.end(); annIter++) { CaretAssert(*annIter); if ( ! containsAnnotation(*annIter)) { return false; } } return true; } /** * Is the given annotation in this group. * * @param annotation * Annotation tested for membership in this group. * @return * True if annotation is in this group, else false. */ bool AnnotationGroup::containsAnnotation(const Annotation* annotation) const { CaretAssert(annotation); for (AnnotationConstIterator annIter = m_annotations.begin(); annIter != m_annotations.end(); annIter++) { if (annotation == (*annIter).data()) { return true; } } return false; } /** * @param index * Get the annotation at the given index. * @return * Annotation at the given index. */ const Annotation* AnnotationGroup::getAnnotation(const int32_t index) const { CaretAssertVectorIndex(m_annotations, index); return m_annotations[index].data(); } /** * Get all annotations in this group. * * @param annotationsOut * Output containing all annotations. */ void AnnotationGroup::getAllAnnotations(std::vector& annotationsOut) const { annotationsOut.clear(); for (AnnotationConstIterator iter = m_annotations.begin(); iter != m_annotations.end(); iter++) { annotationsOut.push_back((*iter).data()); } } /** * Set the selection for editing status for all annotations in this group * in the given window. * * @param windowIndex * Index of window. * @param selectedStatus * The selection status. */ void AnnotationGroup::setAllAnnotationsSelectedForEditing(const int32_t windowIndex, const bool selectedStatus) { for (AnnotationIterator iter = m_annotations.begin(); iter != m_annotations.end(); iter++) { (*iter)->setSelectedForEditing(windowIndex, selectedStatus); } } /** * @return true if file is modified, else false. */ bool AnnotationGroup::isModified() const { for (AnnotationConstIterator iter = m_annotations.begin(); iter != m_annotations.end(); iter++) { const QSharedPointer& annotationPointer = *iter; if (annotationPointer->isModified()) { return true; } } return false; } /** * Clear the modified status of this file. */ void AnnotationGroup::clearModified() { for (AnnotationIterator iter = m_annotations.begin(); iter != m_annotations.end(); iter++) { (*iter)->clearModified(); } } /** * @return name for an annotation saved to scene. * * @param uniqueKey * Unique key of the annotation in the file. */ AString AnnotationGroup::getSceneClassNameForAnnotationUniqueKey(const int32_t uniqueKey) { return ("Ann-" + AString::number(uniqueKey)); } /** * 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* AnnotationGroup::saveToScene(const SceneAttributes* sceneAttributes, const AString& instanceName) { SceneClass* sceneClass = new SceneClass(instanceName, "AnnotationGroup", 1); m_sceneAssistant->saveMembers(sceneAttributes, sceneClass); const int32_t annCount = getNumberOfAnnotations(); for (int32_t i = 0; i < annCount; i++) { const int32_t uniqueKey = getAnnotation(i)->getUniqueKey(); SceneClass* annClass = getAnnotation(i)->saveToScene(sceneAttributes, getSceneClassNameForAnnotationUniqueKey(uniqueKey)); sceneClass->addClass(annClass); } // 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 AnnotationGroup::restoreFromScene(const SceneAttributes* sceneAttributes, const SceneClass* sceneClass) { if (sceneClass == NULL) { return; } m_sceneAssistant->restoreMembers(sceneAttributes, sceneClass); const int32_t annCount = getNumberOfAnnotations(); for (int32_t i = 0; i < annCount; i++) { const int32_t uniqueKey = getAnnotation(i)->getUniqueKey(); const SceneClass* annClass = sceneClass->getClass(getSceneClassNameForAnnotationUniqueKey(uniqueKey)); if (sceneClass != NULL) { getAnnotation(i)->restoreFromScene(sceneAttributes, annClass); } } //Uncomment if sub-classes must restore from scene //restoreSubClassDataFromScene(sceneAttributes, // sceneClass); } /** * @return Number of children. */ int32_t AnnotationGroup::getNumberOfItemChildren() const { return m_annotations.size(); } /** * Get child at the given index. * * @param index * Index of the child. * @return * Child at the given index. */ DisplayGroupAndTabItemInterface* AnnotationGroup::getItemChild(const int32_t index) const { CaretAssertVectorIndex(m_annotations, index); return m_annotations[index].data(); } /** * @return Children of this item. */ std::vector AnnotationGroup::getItemChildren() const { std::vector children; for (AnnotationConstIterator annIter = m_annotations.begin(); annIter != m_annotations.end(); annIter++) { children.push_back((*annIter).data()); } return children; } /** * @return Parent of this item. */ DisplayGroupAndTabItemInterface* AnnotationGroup::getItemParent() const { return m_displayGroupAndTabItemHelper->getParent(); } /** * Set the parent of this item. * * @param itemParent * Parent of this item. */ void AnnotationGroup::setItemParent(DisplayGroupAndTabItemInterface* itemParent) { m_displayGroupAndTabItemHelper->setParent(itemParent); } /** * @return Name of this item. */ AString AnnotationGroup::getItemName() const { return getName(); } /** * Get the icon color for this item. Icon is filled with background * color, outline color is drawn around edges, and text color is small * square in center. For any colors that do not apply, use an alpha * value (last element) of zero. * * @param backgroundRgbaOut * Red, green, blue, alpha components for background ranging [0, 1]. * @param outlineRgbaOut * Red, green, blue, alpha components for outline ranging [0, 1]. * @param textRgbaOut * Red, green, blue, alpha components for text ranging [0, 1]. */ void AnnotationGroup::getItemIconColorsRGBA(float backgroundRgbaOut[4], float outlineRgbaOut[4], float textRgbaOut[4]) const { backgroundRgbaOut[0] = 0.0; backgroundRgbaOut[1] = 0.0; backgroundRgbaOut[2] = 0.0; backgroundRgbaOut[3] = 0.0; outlineRgbaOut[0] = 0.0; outlineRgbaOut[1] = 0.0; outlineRgbaOut[2] = 0.0; outlineRgbaOut[3] = 0.0; textRgbaOut[0] = 0.0; textRgbaOut[1] = 0.0; textRgbaOut[2] = 0.0; textRgbaOut[3] = 0.0; } /** * @return This item can be expanded. */ bool AnnotationGroup::isItemExpandable() const { return true; } /** * @return Is this item expanded in the given display group/tab? * * @param displayGroup * The display group. * @param tabIndex * Index of the tab. */ bool AnnotationGroup::isItemExpanded(const DisplayGroupEnum::Enum displayGroup, const int32_t tabIndex) const { if (m_coordinateSpace == AnnotationCoordinateSpaceEnum::WINDOW) { return m_displayGroupAndTabItemHelper->isExpandedInWindow(m_tabOrWindowIndex); } return m_displayGroupAndTabItemHelper->isExpanded(displayGroup, tabIndex); } /** * Set this item's expanded status in the given display group/tab. * * @param displayGroup * The display group. * @param tabIndex * Index of the tab. * @param status * New expanded status. */ void AnnotationGroup::setItemExpanded(const DisplayGroupEnum::Enum displayGroup, const int32_t tabIndex, const bool status) { if (m_coordinateSpace == AnnotationCoordinateSpaceEnum::WINDOW) { m_displayGroupAndTabItemHelper->setExpandedInWindow(m_tabOrWindowIndex, status); } else { m_displayGroupAndTabItemHelper->setExpanded(displayGroup, tabIndex, status); } } /** * Get display selection status in the given display group/tab? * * @param displayGroup * The display group. * @param tabIndex * Index of the tab. */ TriStateSelectionStatusEnum::Enum AnnotationGroup::getItemDisplaySelected(const DisplayGroupEnum::Enum displayGroup, const int32_t tabIndex) const { TriStateSelectionStatusEnum::Enum status = TriStateSelectionStatusEnum::UNSELECTED; const int numChildren = getNumberOfAnnotations(); if (numChildren > 0) { int32_t selectedCount = 0; for (int32_t i = 0; i < numChildren; i++) { CaretAssertVectorIndex(m_annotations, i); switch (m_annotations[i]->getItemDisplaySelected(displayGroup, tabIndex)) { case TriStateSelectionStatusEnum::PARTIALLY_SELECTED: CaretAssertMessage(0, "Annotation should never be partially selected."); break; case TriStateSelectionStatusEnum::SELECTED: selectedCount++; break; case TriStateSelectionStatusEnum::UNSELECTED: break; } } if (selectedCount == numChildren) { status = TriStateSelectionStatusEnum::SELECTED; } else if (selectedCount > 0) { status = TriStateSelectionStatusEnum::PARTIALLY_SELECTED; } } return status; } /** * Set display this item selected in the given display group/tab. * * @param displayGroup * The display group. * @param tabIndex * Index of the tab. * @param status * New selection status. */ void AnnotationGroup::setItemDisplaySelected(const DisplayGroupEnum::Enum displayGroup, const int32_t tabIndex, const TriStateSelectionStatusEnum::Enum status) { switch (status) { case TriStateSelectionStatusEnum::PARTIALLY_SELECTED: CaretAssertMessage(0, "Annotation group should never be set to partially selected."); return; break; case TriStateSelectionStatusEnum::SELECTED: break; case TriStateSelectionStatusEnum::UNSELECTED: break; } /* * Note: An annotation group's selection status is based * of the the group's annotations so we do not need to set * an explicit selection status for the group. */ DisplayGroupAndTabItemInterface::setChildrenDisplaySelectedHelper(this, displayGroup, tabIndex, status); } /** * Is this item selected for editing in the given window? * * @param windowIndex * Index of the window. * @return * Selection status. */ bool AnnotationGroup::isItemSelectedForEditingInWindow(const int32_t /*windowIndex*/) { return false; } connectome-workbench-1.2.3+git41-gc4c6c90/src/Annotations/AnnotationGroup.h000066400000000000000000000164471300200146000263550ustar00rootroot00000000000000#ifndef __ANNOTATION_GROUP_H__ #define __ANNOTATION_GROUP_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 "AnnotationCoordinateSpaceEnum.h" #include "AnnotationGroupKey.h" #include "CaretObjectTracksModification.h" #include "DisplayGroupAndTabItemInterface.h" #include "SceneableInterface.h" namespace caret { class Annotation; class DisplayGroupAndTabItemHelper; class SceneClassAssistant; class AnnotationGroup : public CaretObjectTracksModification, public DisplayGroupAndTabItemInterface, public SceneableInterface { public: AnnotationGroup(AnnotationFile* annotationFile, const AnnotationGroupTypeEnum::Enum groupType, const int32_t uniqueKey, const AnnotationCoordinateSpaceEnum::Enum coordinateSpace, const int32_t tabOrWindowIndex); virtual ~AnnotationGroup(); bool isEmpty() const; int32_t getUniqueKey() const; AString getName() const; AnnotationGroupKey getAnnotationGroupKey() const; AnnotationFile* getAnnotationFile() const; AnnotationGroupTypeEnum::Enum getGroupType() const; AnnotationCoordinateSpaceEnum::Enum getCoordinateSpace() const; int32_t getTabOrWindowIndex() const; int32_t getNumberOfAnnotations() const; Annotation* getAnnotation(const int32_t index); const Annotation* getAnnotation(const int32_t index) const; void getAllAnnotations(std::vector& annotationsOut) const; bool containsAnnotation(const Annotation* annotation) const; bool containsAllAnnotation(const std::vector annotations) const; void setAllAnnotationsSelectedForEditing(const int32_t windowIndex, const bool selectedStatus); bool isModified() const; void clearModified(); // 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); virtual int32_t getNumberOfItemChildren() const; virtual DisplayGroupAndTabItemInterface* getItemChild(const int32_t index) const; virtual std::vector getItemChildren() const; virtual DisplayGroupAndTabItemInterface* getItemParent() const; virtual void setItemParent(DisplayGroupAndTabItemInterface* itemParent); virtual AString getItemName() const; virtual void getItemIconColorsRGBA(float backgroundRgbaOut[4], float outlineRgbaOut[4], float textRgbaOut[4]) const; virtual bool isItemExpandable() const; virtual bool isItemExpanded(const DisplayGroupEnum::Enum displayGroup, const int32_t tabIndex) const; virtual void setItemExpanded(const DisplayGroupEnum::Enum displayGroup, const int32_t tabIndex, const bool status); virtual TriStateSelectionStatusEnum::Enum getItemDisplaySelected(const DisplayGroupEnum::Enum displayGroup, const int32_t tabIndex) const; virtual void setItemDisplaySelected(const DisplayGroupEnum::Enum displayGroup, const int32_t tabIndex, const TriStateSelectionStatusEnum::Enum status); virtual bool isItemSelectedForEditingInWindow(const int32_t windowIndex); // 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: AnnotationGroup(const AnnotationGroup& obj); AnnotationGroup& operator=(const AnnotationGroup& obj); void copyHelperAnnotationGroup(const AnnotationGroup& obj); void addAnnotationPrivate(Annotation* annotation); void addAnnotationPrivateSharedPointer(QSharedPointer& annotation); void assignGroupKeyToAnnotation(Annotation* annotation); bool validateAddedAnnotation(const Annotation* annotation); bool removeAnnotation(Annotation* annotation, QSharedPointer& removedAnnotationOut); void removeAllAnnotations(std::vector >& allRemovedAnnotationsOut); int32_t getMaximumUniqueKey() const; void initializeInstance(); SceneClassAssistant* m_sceneAssistant; DisplayGroupAndTabItemHelper* m_displayGroupAndTabItemHelper; AnnotationGroupKey m_groupKey; AnnotationCoordinateSpaceEnum::Enum m_coordinateSpace; int32_t m_tabOrWindowIndex; mutable AString m_name; std::vector > m_annotations; typedef std::vector >::iterator AnnotationIterator; typedef std::vector >::const_iterator AnnotationConstIterator; static AString getSceneClassNameForAnnotationUniqueKey(const int32_t uniqueKey); // ADD_NEW_MEMBERS_HERE friend class AnnotationFile; friend class AnnotationFileXmlReader; friend class AnnotationFileXmlWriter; }; #ifdef __ANNOTATION_GROUP_DECLARE__ // #endif // __ANNOTATION_GROUP_DECLARE__ } // namespace #endif //__ANNOTATION_GROUP_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Annotations/AnnotationGroupKey.cxx000066400000000000000000000175361300200146000274010ustar00rootroot00000000000000 /*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 __ANNOTATION_GROUP_KEY_DECLARE__ #include "AnnotationGroupKey.h" #undef __ANNOTATION_GROUP_KEY_DECLARE__ #include "CaretAssert.h" using namespace caret; /** * \class caret::AnnotationGroupKey * \brief This class is a "key" for an annotation group. * \ingroup Annotations * * This class is a member of each annotation and tracks the * group to which the annotation belongs. * * There are two types of annotation groups: 'Space' and 'User'. * By default, annotations are assigned to a 'Space' group when * added to an annotation file. In an annotation file, there * is one group for each space (stereotaxic, surface, each tab, * and each window). A user may create 'User' groups to group * annotation that are in the same space. * * Creating a user group: * - The group type is set to USER. * - The user group unique key is set to the key in the user group. * - The space key is invalidate. * * Ungrouping a user group: * - The group type is set to SPACE. * - The space group unique key is set to the key in the space group. * - The user group unique is not changed. It is used by a regroup operation. * * Regrouping a user group: * - The group type is changed to USER. * - A user group is created with the user group unique key and all annotations * that have the user group unique key are added to the user group. */ /** * Constructor. */ AnnotationGroupKey::AnnotationGroupKey() : CaretObject(), m_annotationFile(NULL), m_groupType(AnnotationGroupTypeEnum::INVALID), m_spaceGroupUniqueKey(-1), m_userGroupUniqueKey(-1) { reset(); } /** * Destructor. */ AnnotationGroupKey::~AnnotationGroupKey() { } /** * Copy constructor. * @param obj * Object that is copied. */ AnnotationGroupKey::AnnotationGroupKey(const AnnotationGroupKey& obj) : CaretObject(obj) { this->copyHelperAnnotationGroupKey(obj); } /** * Assignment operator. * @param obj * Data copied from obj to this. * @return * Reference to this object. */ AnnotationGroupKey& AnnotationGroupKey::operator=(const AnnotationGroupKey& obj) { if (this != &obj) { CaretObject::operator=(obj); this->copyHelperAnnotationGroupKey(obj); } return *this; } /** * Helps with copying an object of this type. * @param obj * Object that is copied. */ void AnnotationGroupKey::copyHelperAnnotationGroupKey(const AnnotationGroupKey& obj) { m_annotationFile = obj.m_annotationFile; m_groupType = obj.m_groupType; m_spaceGroupUniqueKey = obj.m_spaceGroupUniqueKey; m_userGroupUniqueKey = obj.m_userGroupUniqueKey; } /** * Reset an instance to invalid. */ void AnnotationGroupKey::reset() { m_annotationFile = NULL; m_groupType = AnnotationGroupTypeEnum::INVALID; m_spaceGroupUniqueKey = -1; m_userGroupUniqueKey = -1; } /** * Equality operator. Equal if annotation file is equal AND * either the group type is space and the space unique keys are * equal or the group type is user and the user unique keys are * equal. * * @param groupKey * The group key that is tested for equality. * @return * True if these group keys are equal, else false. */ bool AnnotationGroupKey::operator==(const AnnotationGroupKey& groupKey) const { if (this == &groupKey) { return true; } if (m_annotationFile == groupKey.m_annotationFile) { if (m_groupType == groupKey.m_groupType) { switch (m_groupType) { case AnnotationGroupTypeEnum::INVALID: CaretAssertMessage(0, "Should never get here."); break; case AnnotationGroupTypeEnum::SPACE: if (m_spaceGroupUniqueKey == groupKey.m_spaceGroupUniqueKey) { return true; } break; case AnnotationGroupTypeEnum::USER: if (m_userGroupUniqueKey == groupKey.m_userGroupUniqueKey) { return true; } break; } } } return false; } /** * Less than operator. * * @param groupKey * The group key that is tested for equality. * @return * True if these group keys are equal, else false. */ bool AnnotationGroupKey::operator<(const AnnotationGroupKey& groupKey) const { if (this == &groupKey) { return false; } bool lessThanFlag = false; if (m_annotationFile == groupKey.m_annotationFile) { if (m_groupType == groupKey.m_groupType) { switch (m_groupType) { case AnnotationGroupTypeEnum::INVALID: CaretAssertMessage(0, "Should never get here."); lessThanFlag = false; break; case AnnotationGroupTypeEnum::SPACE: lessThanFlag = (m_spaceGroupUniqueKey < groupKey.m_spaceGroupUniqueKey); break; case AnnotationGroupTypeEnum::USER: lessThanFlag = (m_userGroupUniqueKey < groupKey.m_userGroupUniqueKey); break; } } else { lessThanFlag = (AnnotationGroupTypeEnum::toIntegerCode(m_groupType) < AnnotationGroupTypeEnum::toIntegerCode(groupKey.m_groupType)); } } else { lessThanFlag = (m_annotationFile < groupKey.m_annotationFile); } return lessThanFlag; } /** * @return The annotation file. */ AnnotationFile* AnnotationGroupKey::getAnnotationFile() const { return m_annotationFile; } /** * Set the annotation file. * * @param annotationFile * The annotation file. */ void AnnotationGroupKey::setAnnotationFile(AnnotationFile* annotationFile) { m_annotationFile = annotationFile; } /** * @return The group type. */ AnnotationGroupTypeEnum::Enum AnnotationGroupKey::getGroupType() const { return m_groupType; } /** * Set the group type. * * @param groupType * The group type. */ void AnnotationGroupKey::setGroupType(const AnnotationGroupTypeEnum::Enum groupType) { m_groupType = groupType; } /** * @return The space group unique key. */ int32_t AnnotationGroupKey::getSpaceGroupUniqueKey() const { return m_spaceGroupUniqueKey; } /** * Set the space group unique key. * * @param spaceGroupUniqueKey * The space group unique key. */ void AnnotationGroupKey::setSpaceGroupUniqueKey(const int32_t spaceGroupUniqueKey) { m_spaceGroupUniqueKey = spaceGroupUniqueKey; } /** * @return The user group unique key. */ int32_t AnnotationGroupKey::getUserGroupUniqueKey() const { return m_userGroupUniqueKey; } /** * Set the user group unique key. * * @param userGroupUniqueKey * The user group unique key. */ void AnnotationGroupKey::setUserGroupUniqueKey(const int32_t userGroupUniqueKey) { m_userGroupUniqueKey = userGroupUniqueKey; } /** * Get a description of this object's content. * @return String describing this object's content. */ AString AnnotationGroupKey::toString() const { return "AnnotationGroupKey"; } connectome-workbench-1.2.3+git41-gc4c6c90/src/Annotations/AnnotationGroupKey.h000066400000000000000000000054721300200146000270220ustar00rootroot00000000000000#ifndef __ANNOTATION_GROUP_KEY_H__ #define __ANNOTATION_GROUP_KEY_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 "AnnotationGroupTypeEnum.h" #include "CaretObject.h" namespace caret { class AnnotationFile; class AnnotationGroupKey : public CaretObject { public: AnnotationGroupKey(); virtual ~AnnotationGroupKey(); AnnotationGroupKey(const AnnotationGroupKey& obj); AnnotationGroupKey& operator=(const AnnotationGroupKey& obj); bool operator==(const AnnotationGroupKey& groupKey) const; bool operator<(const AnnotationGroupKey& groupKey) const; void reset(); AnnotationFile* getAnnotationFile() const; AnnotationGroupTypeEnum::Enum getGroupType() const; int32_t getSpaceGroupUniqueKey() const; int32_t getUserGroupUniqueKey() const; // ADD_NEW_METHODS_HERE virtual AString toString() const; private: void copyHelperAnnotationGroupKey(const AnnotationGroupKey& obj); /* set method private so that only friend classes can access them */ void setAnnotationFile(AnnotationFile* annotationFile); void setGroupType(const AnnotationGroupTypeEnum::Enum groupType); void setSpaceGroupUniqueKey(const int32_t spaceGroupUniqueKey); void setUserGroupUniqueKey(const int32_t userGroupUniqueKey); AnnotationFile* m_annotationFile; AnnotationGroupTypeEnum::Enum m_groupType; int32_t m_spaceGroupUniqueKey; int32_t m_userGroupUniqueKey; friend class Annotation; friend class AnnotationFile; friend class AnnotationGroup; // ADD_NEW_MEMBERS_HERE }; #ifdef __ANNOTATION_GROUP_KEY_DECLARE__ // #endif // __ANNOTATION_GROUP_KEY_DECLARE__ } // namespace #endif //__ANNOTATION_GROUP_KEY_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Annotations/AnnotationGroupTypeEnum.cxx000066400000000000000000000257151300200146000304150ustar00rootroot00000000000000 /*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 __ANNOTATION_GROUP_TYPE_ENUM_DECLARE__ #include "AnnotationGroupTypeEnum.h" #undef __ANNOTATION_GROUP_TYPE_ENUM_DECLARE__ #include "CaretAssert.h" using namespace caret; /** * \class caret::AnnotationGroupTypeEnum * \brief Type of annotation group * * In an annotation file, each annotation is assigned to a group. * These groups may be created by the user or be 'space' groups. * For annotations not assigned to a user created annotation group, * the annotation, the file contains one group for each annotation * space. * * 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_annotationGroupTypeEnumComboBox; * * Declare a slot that is called when user changes selection * private slots: * void annotationGroupTypeEnumComboBoxItemActivated(); * * Implementation File (.cxx) * Include the header files * #include "EnumComboBoxTemplate.h" * #include "AnnotationGroupTypeEnum.h" * * Instatiate: * m_annotationGroupTypeEnumComboBox = new EnumComboBoxTemplate(this); * m_annotationGroupTypeEnumComboBox->setup(); * * Get notified when the user changes the selection: * QObject::connect(m_annotationGroupTypeEnumComboBox, SIGNAL(itemActivated()), * this, SLOT(annotationGroupTypeEnumComboBoxItemActivated())); * * Update the selection: * m_annotationGroupTypeEnumComboBox->setSelectedItem(NEW_VALUE); * * Read the selection: * const AnnotationGroupTypeEnum::Enum VARIABLE = m_annotationGroupTypeEnumComboBox->getSelectedItem(); * */ /** * Constructor. * * @param enumValue * An enumerated value. * @param name * Name of enumerated value. * * @param guiName * User-friendly name for use in user-interface. */ AnnotationGroupTypeEnum::AnnotationGroupTypeEnum(const Enum enumValue, const AString& name, const AString& guiName) { this->enumValue = enumValue; this->integerCode = integerCodeCounter++; this->name = name; this->guiName = guiName; } /** * Destructor. */ AnnotationGroupTypeEnum::~AnnotationGroupTypeEnum() { } /** * Initialize the enumerated metadata. */ void AnnotationGroupTypeEnum::initialize() { if (initializedFlag) { return; } initializedFlag = true; enumData.push_back(AnnotationGroupTypeEnum(INVALID, "INVALID", "Invalid")); enumData.push_back(AnnotationGroupTypeEnum(SPACE, "SPACE", "Space")); enumData.push_back(AnnotationGroupTypeEnum(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 AnnotationGroupTypeEnum* AnnotationGroupTypeEnum::findData(const Enum enumValue) { if (initializedFlag == false) initialize(); size_t num = enumData.size(); for (size_t i = 0; i < num; i++) { const AnnotationGroupTypeEnum* 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 AnnotationGroupTypeEnum::toName(Enum enumValue) { if (initializedFlag == false) initialize(); const AnnotationGroupTypeEnum* 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. */ AnnotationGroupTypeEnum::Enum AnnotationGroupTypeEnum::fromName(const AString& name, bool* isValidOut) { if (initializedFlag == false) initialize(); bool validFlag = false; Enum enumValue = AnnotationGroupTypeEnum::enumData[0].enumValue; for (std::vector::iterator iter = enumData.begin(); iter != enumData.end(); iter++) { const AnnotationGroupTypeEnum& 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 AnnotationGroupTypeEnum")); } return enumValue; } /** * Get a GUI string representation of the enumerated type. * @param enumValue * Enumerated value. * @return * String representing enumerated value. */ AString AnnotationGroupTypeEnum::toGuiName(Enum enumValue) { if (initializedFlag == false) initialize(); const AnnotationGroupTypeEnum* 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. */ AnnotationGroupTypeEnum::Enum AnnotationGroupTypeEnum::fromGuiName(const AString& guiName, bool* isValidOut) { if (initializedFlag == false) initialize(); bool validFlag = false; Enum enumValue = AnnotationGroupTypeEnum::enumData[0].enumValue; for (std::vector::iterator iter = enumData.begin(); iter != enumData.end(); iter++) { const AnnotationGroupTypeEnum& 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 AnnotationGroupTypeEnum")); } return enumValue; } /** * Get the integer code for a data type. * * @return * Integer code for data type. */ int32_t AnnotationGroupTypeEnum::toIntegerCode(Enum enumValue) { if (initializedFlag == false) initialize(); const AnnotationGroupTypeEnum* 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. */ AnnotationGroupTypeEnum::Enum AnnotationGroupTypeEnum::fromIntegerCode(const int32_t integerCode, bool* isValidOut) { if (initializedFlag == false) initialize(); bool validFlag = false; Enum enumValue = AnnotationGroupTypeEnum::enumData[0].enumValue; for (std::vector::iterator iter = enumData.begin(); iter != enumData.end(); iter++) { const AnnotationGroupTypeEnum& 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 AnnotationGroupTypeEnum")); } 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 AnnotationGroupTypeEnum::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 AnnotationGroupTypeEnum::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(AnnotationGroupTypeEnum::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 AnnotationGroupTypeEnum::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(AnnotationGroupTypeEnum::toGuiName(iter->enumValue)); } if (isSorted) { std::sort(allGuiNames.begin(), allGuiNames.end()); } } connectome-workbench-1.2.3+git41-gc4c6c90/src/Annotations/AnnotationGroupTypeEnum.h000066400000000000000000000063741300200146000300420ustar00rootroot00000000000000#ifndef __ANNOTATION_GROUP_TYPE_ENUM_H__ #define __ANNOTATION_GROUP_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 AnnotationGroupTypeEnum { public: /** * Enumerated values for type of annotation group */ enum Enum { /** Invalid*/ INVALID, /** Annotations not in a user group are assigned to a file's space groups */ SPACE, /** Annotation groups created by the user */ USER }; ~AnnotationGroupTypeEnum(); 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: AnnotationGroupTypeEnum(const Enum enumValue, const AString& name, const AString& guiName); static const AnnotationGroupTypeEnum* 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 __ANNOTATION_GROUP_TYPE_ENUM_DECLARE__ std::vector AnnotationGroupTypeEnum::enumData; bool AnnotationGroupTypeEnum::initializedFlag = false; int32_t AnnotationGroupTypeEnum::integerCodeCounter = 0; #endif // __ANNOTATION_GROUP_TYPE_ENUM_DECLARE__ } // namespace #endif //__ANNOTATION_GROUP_TYPE_ENUM_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Annotations/AnnotationGroupingModeEnum.cxx000066400000000000000000000256411300200146000310540ustar00rootroot00000000000000 /*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 __ANNOTATION_GROUPING_MODE_ENUM_DECLARE__ #include "AnnotationGroupingModeEnum.h" #undef __ANNOTATION_GROUPING_MODE_ENUM_DECLARE__ #include "CaretAssert.h" using namespace caret; /** * \class caret::AnnotationGroupingModeEnum * \brief Mode for creating, regrouping, and ungrouping annotations. * * 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_annotationGroupingModeEnumComboBox; * * Declare a slot that is called when user changes selection * private slots: * void annotationGroupingModeEnumComboBoxItemActivated(); * * Implementation File (.cxx) * Include the header files * #include "EnumComboBoxTemplate.h" * #include "AnnotationGroupingModeEnum.h" * * Instatiate: * m_annotationGroupingModeEnumComboBox = new EnumComboBoxTemplate(this); * m_annotationGroupingModeEnumComboBox->setup(); * * Get notified when the user changes the selection: * QObject::connect(m_annotationGroupingModeEnumComboBox, SIGNAL(itemActivated()), * this, SLOT(annotationGroupingModeEnumComboBoxItemActivated())); * * Update the selection: * m_annotationGroupingModeEnumComboBox->setSelectedItem(NEW_VALUE); * * Read the selection: * const AnnotationGroupingModeEnum::Enum VARIABLE = m_annotationGroupingModeEnumComboBox->getSelectedItem(); * */ /** * Constructor. * * @param enumValue * An enumerated value. * @param name * Name of enumerated value. * * @param guiName * User-friendly name for use in user-interface. */ AnnotationGroupingModeEnum::AnnotationGroupingModeEnum(const Enum enumValue, const AString& name, const AString& guiName) { this->enumValue = enumValue; this->integerCode = integerCodeCounter++; this->name = name; this->guiName = guiName; } /** * Destructor. */ AnnotationGroupingModeEnum::~AnnotationGroupingModeEnum() { } /** * Initialize the enumerated metadata. */ void AnnotationGroupingModeEnum::initialize() { if (initializedFlag) { return; } initializedFlag = true; enumData.push_back(AnnotationGroupingModeEnum(GROUP, "GROUP", "Group")); enumData.push_back(AnnotationGroupingModeEnum(REGROUP, "REGROUP", "Regroup")); enumData.push_back(AnnotationGroupingModeEnum(UNGROUP, "UNGROUP", "Ungroup")); } /** * 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 AnnotationGroupingModeEnum* AnnotationGroupingModeEnum::findData(const Enum enumValue) { if (initializedFlag == false) initialize(); size_t num = enumData.size(); for (size_t i = 0; i < num; i++) { const AnnotationGroupingModeEnum* 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 AnnotationGroupingModeEnum::toName(Enum enumValue) { if (initializedFlag == false) initialize(); const AnnotationGroupingModeEnum* 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. */ AnnotationGroupingModeEnum::Enum AnnotationGroupingModeEnum::fromName(const AString& name, bool* isValidOut) { if (initializedFlag == false) initialize(); bool validFlag = false; Enum enumValue = AnnotationGroupingModeEnum::enumData[0].enumValue; for (std::vector::iterator iter = enumData.begin(); iter != enumData.end(); iter++) { const AnnotationGroupingModeEnum& 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 AnnotationGroupingModeEnum")); } return enumValue; } /** * Get a GUI string representation of the enumerated type. * @param enumValue * Enumerated value. * @return * String representing enumerated value. */ AString AnnotationGroupingModeEnum::toGuiName(Enum enumValue) { if (initializedFlag == false) initialize(); const AnnotationGroupingModeEnum* 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. */ AnnotationGroupingModeEnum::Enum AnnotationGroupingModeEnum::fromGuiName(const AString& guiName, bool* isValidOut) { if (initializedFlag == false) initialize(); bool validFlag = false; Enum enumValue = AnnotationGroupingModeEnum::enumData[0].enumValue; for (std::vector::iterator iter = enumData.begin(); iter != enumData.end(); iter++) { const AnnotationGroupingModeEnum& 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 AnnotationGroupingModeEnum")); } return enumValue; } /** * Get the integer code for a data type. * * @return * Integer code for data type. */ int32_t AnnotationGroupingModeEnum::toIntegerCode(Enum enumValue) { if (initializedFlag == false) initialize(); const AnnotationGroupingModeEnum* 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. */ AnnotationGroupingModeEnum::Enum AnnotationGroupingModeEnum::fromIntegerCode(const int32_t integerCode, bool* isValidOut) { if (initializedFlag == false) initialize(); bool validFlag = false; Enum enumValue = AnnotationGroupingModeEnum::enumData[0].enumValue; for (std::vector::iterator iter = enumData.begin(); iter != enumData.end(); iter++) { const AnnotationGroupingModeEnum& 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 AnnotationGroupingModeEnum")); } 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 AnnotationGroupingModeEnum::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 AnnotationGroupingModeEnum::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(AnnotationGroupingModeEnum::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 AnnotationGroupingModeEnum::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(AnnotationGroupingModeEnum::toGuiName(iter->enumValue)); } if (isSorted) { std::sort(allGuiNames.begin(), allGuiNames.end()); } } connectome-workbench-1.2.3+git41-gc4c6c90/src/Annotations/AnnotationGroupingModeEnum.h000066400000000000000000000063521300200146000304770ustar00rootroot00000000000000#ifndef __ANNOTATION_GROUPING_MODE_ENUM_H__ #define __ANNOTATION_GROUPING_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 AnnotationGroupingModeEnum { public: /** * Enumerated values. */ enum Enum { /** Create a group */ GROUP, /** Recreate a previously 'ungrouped' group */ REGROUP, /** Ungroup (remove) a group */ UNGROUP }; ~AnnotationGroupingModeEnum(); 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: AnnotationGroupingModeEnum(const Enum enumValue, const AString& name, const AString& guiName); static const AnnotationGroupingModeEnum* 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 __ANNOTATION_GROUPING_MODE_ENUM_DECLARE__ std::vector AnnotationGroupingModeEnum::enumData; bool AnnotationGroupingModeEnum::initializedFlag = false; int32_t AnnotationGroupingModeEnum::integerCodeCounter = 0; #endif // __ANNOTATION_GROUPING_MODE_ENUM_DECLARE__ } // namespace #endif //__ANNOTATION_GROUPING_MODE_ENUM_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Annotations/AnnotationImage.cxx000066400000000000000000000176461300200146000266600ustar00rootroot00000000000000 /*LICENSE_START*/ /* * Copyright (C) 2015 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 __ANNOTATION_IMAGE_DECLARE__ #include "AnnotationImage.h" #undef __ANNOTATION_IMAGE_DECLARE__ #include "CaretAssert.h" #include "DrawnWithOpenGLTextureInfo.h" #include "SceneClass.h" #include "SceneClassAssistant.h" using namespace caret; /** * \class caret::AnnotationImage * \brief An annotation image. * \ingroup Annotations */ /** * Constructor. * * @param attributeDefaultType * Type for attribute defaults */ AnnotationImage::AnnotationImage(const AnnotationAttributesDefaultTypeEnum::Enum attributeDefaultType) : AnnotationTwoDimensionalShape(AnnotationTypeEnum::IMAGE, attributeDefaultType), DrawnWithOpenGLTextureInterface() { initializeMembersAnnotationImage(); } /** * Destructor. */ AnnotationImage::~AnnotationImage() { delete m_sceneAssistant; } /** * Copy constructor. * @param obj * Object that is copied. */ AnnotationImage::AnnotationImage(const AnnotationImage& obj) : AnnotationTwoDimensionalShape(obj), DrawnWithOpenGLTextureInterface() { initializeMembersAnnotationImage(); this->copyHelperAnnotationImage(obj); } /** * Assignment operator. * @param obj * Data copied from obj to this. * @return * Reference to this object. */ AnnotationImage& AnnotationImage::operator=(const AnnotationImage& obj) { if (this != &obj) { AnnotationTwoDimensionalShape::operator=(obj); this->copyHelperAnnotationImage(obj); } return *this; } /** * Helps with copying an object of this type. * @param obj * Object that is copied. */ void 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; } /** * Set the image. * * @param imageBytesRGBA * Bytes containing the image. 4 bytes per pixel with values * ranging 0 to 255. Number of elements MUST BE * (width * height * 4) * @param imageWidth * Width of the image. * @param imageHeight * Height of the image. */ void AnnotationImage::setImageBytesRGBA(const uint8_t* imageBytesRGBA, const int32_t imageWidth, const int32_t imageHeight) { m_imageWidth = imageWidth; m_imageHeight = imageHeight; const int32_t numBytes = (imageWidth * imageHeight * 4); m_imageBytesRGBA.resize(numBytes); for (int32_t i = 0; i < numBytes; i++) { m_imageBytesRGBA[i] = imageBytesRGBA[i]; } setModified(); } /** * @return Width of the image. */ int32_t AnnotationImage::getImageWidth() const { return m_imageWidth; } /** * @return Height of the image. */ int32_t AnnotationImage::getImageHeight() const { return m_imageHeight; } /** * @return The RGBA bytes of the image. * NULL if image is invalid. */ const uint8_t* AnnotationImage::getImageBytesRGBA() const { if (m_imageBytesRGBA.empty()) { return NULL; } return &m_imageBytesRGBA[0]; } /** * Initialize members of a new instance. */ void AnnotationImage::initializeMembersAnnotationImage() { switch (m_attributeDefaultType) { case AnnotationAttributesDefaultTypeEnum::NORMAL: break; case AnnotationAttributesDefaultTypeEnum::USER: break; } m_imageBytesRGBA.clear(); m_imageWidth = 0; 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; } /** * @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). */ bool AnnotationImage::isFixedAspectRatio() const { return true; } /** * @return The aspect ratio for annotations that have a fixed aspect ratio. * This method may be overridden by annotations that require a fixed aspect ratio * (such as an image annotaiton). * * If the aspect ratio is unknown return 1. Never return zero. */ float AnnotationImage::getFixedAspectRatio() const { float aspectRatio = 1.0; if ((m_imageWidth > 0.0) && (m_imageHeight > 0.0)) { aspectRatio = static_cast(m_imageHeight) / static_cast(m_imageWidth); CaretAssert(aspectRatio != 0.0); } return aspectRatio; } /** * @return The OpenGL texture information used for managing * texture resources. */ DrawnWithOpenGLTextureInfo* AnnotationImage::getDrawWithOpenGLTextureInfo() { return m_drawnWithOpenGLTextureInfo; } /** * @return The OpenGL texture information used for managing * texture resources (const method) */ const DrawnWithOpenGLTextureInfo* AnnotationImage::getDrawWithOpenGLTextureInfo() const { return m_drawnWithOpenGLTextureInfo; } /** * 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 AnnotationImage::saveSubClassDataToScene(const SceneAttributes* sceneAttributes, SceneClass* sceneClass) { AnnotationTwoDimensionalShape::saveSubClassDataToScene(sceneAttributes, 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 AnnotationImage::restoreSubClassDataFromScene(const SceneAttributes* sceneAttributes, const SceneClass* sceneClass) { AnnotationTwoDimensionalShape::restoreSubClassDataFromScene(sceneAttributes, sceneClass); m_sceneAssistant->restoreMembers(sceneAttributes, sceneClass); } connectome-workbench-1.2.3+git41-gc4c6c90/src/Annotations/AnnotationImage.h000066400000000000000000000063711300200146000262760ustar00rootroot00000000000000#ifndef __ANNOTATION_IMAGE_H__ #define __ANNOTATION_IMAGE_H__ /*LICENSE_START*/ /* * Copyright (C) 2015 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 "AnnotationTwoDimensionalShape.h" #include "CaretPointer.h" #include "DrawnWithOpenGLTextureInfo.h" #include "DrawnWithOpenGLTextureInterface.h" namespace caret { class AnnotationImage : public AnnotationTwoDimensionalShape, public DrawnWithOpenGLTextureInterface { public: AnnotationImage(const AnnotationAttributesDefaultTypeEnum::Enum attributeDefaultType); virtual ~AnnotationImage(); AnnotationImage(const AnnotationImage& obj); AnnotationImage& operator=(const AnnotationImage& obj); virtual bool isLineWidthSupported() const; virtual bool isBackgroundColorSupported() const; virtual bool isFixedAspectRatio() const; virtual float getFixedAspectRatio() const; void setImageBytesRGBA(const uint8_t* imageBytesRGBA, const int32_t imageWidth, const int32_t imageHeight); int32_t getImageWidth() const; int32_t getImageHeight() const; const uint8_t* getImageBytesRGBA() const; virtual DrawnWithOpenGLTextureInfo* getDrawWithOpenGLTextureInfo(); virtual const DrawnWithOpenGLTextureInfo* getDrawWithOpenGLTextureInfo() const; // ADD_NEW_METHODS_HERE protected: virtual void saveSubClassDataToScene(const SceneAttributes* sceneAttributes, SceneClass* sceneClass); virtual void restoreSubClassDataFromScene(const SceneAttributes* sceneAttributes, const SceneClass* sceneClass); private: void copyHelperAnnotationImage(const AnnotationImage& obj); void initializeMembersAnnotationImage(); SceneClassAssistant* m_sceneAssistant; std::vector m_imageBytesRGBA; int32_t m_imageWidth; int32_t m_imageHeight; CaretPointer m_drawnWithOpenGLTextureInfo; // ADD_NEW_MEMBERS_HERE }; #ifdef __ANNOTATION_IMAGE_DECLARE__ // #endif // __ANNOTATION_IMAGE_DECLARE__ } // namespace #endif //__ANNOTATION_IMAGE_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Annotations/AnnotationLine.cxx000066400000000000000000000140461300200146000265140ustar00rootroot00000000000000 /*LICENSE_START*/ /* * Copyright (C) 2015 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 __ANNOTATION_LINE_DECLARE__ #include "AnnotationLine.h" #undef __ANNOTATION_LINE_DECLARE__ #include "CaretAssert.h" #include "SceneClass.h" #include "SceneClassAssistant.h" using namespace caret; /** * \class caret::AnnotationLine * \brief An annotation line. * \ingroup Annotations */ /** * Constructor. * * @param attributeDefaultType * Type for attribute defaults */ AnnotationLine::AnnotationLine(const AnnotationAttributesDefaultTypeEnum::Enum attributeDefaultType) : AnnotationOneDimensionalShape(AnnotationTypeEnum::LINE, attributeDefaultType) { initializeMembersAnnotationLine(); } /** * Destructor. */ AnnotationLine::~AnnotationLine() { } /** * Copy constructor. * @param obj * Object that is copied. */ AnnotationLine::AnnotationLine(const AnnotationLine& obj) : AnnotationOneDimensionalShape(obj) { this->initializeMembersAnnotationLine(); this->copyHelperAnnotationLine(obj); } /** * Assignment operator. * @param obj * Data copied from obj to this. * @return * Reference to this object. */ AnnotationLine& AnnotationLine::operator=(const AnnotationLine& obj) { if (this != &obj) { AnnotationOneDimensionalShape::operator=(obj); this->copyHelperAnnotationLine(obj); } return *this; } /** * Helps with copying an object of this type. * @param obj * Object that is copied. */ void AnnotationLine::copyHelperAnnotationLine(const AnnotationLine& obj) { m_displayEndArrow = obj.m_displayEndArrow; m_displayStartArrow = obj.m_displayStartArrow; } /** * Initialize a new instance of this class. */ void AnnotationLine::initializeMembersAnnotationLine() { switch (m_attributeDefaultType) { case AnnotationAttributesDefaultTypeEnum::NORMAL: m_displayStartArrow = false; m_displayEndArrow = false; break; case AnnotationAttributesDefaultTypeEnum::USER: m_displayStartArrow = s_userDefaultDisplayStartArrow; m_displayEndArrow = s_userDefaultDisplayEndArrow; break; } m_sceneAssistant.grabNew(new SceneClassAssistant()); } /** * @return Is the arrow at the line's start coordinate displayed? */ bool AnnotationLine::isDisplayStartArrow() const { return m_displayStartArrow; } /** * Set the display status of the arrow at the line's start coordinate. * * @param displayArrow * New status. */ void AnnotationLine::setDisplayStartArrow(const bool displayArrow) { m_displayStartArrow = displayArrow; } /** * @return Is the arrow at the line's start coordinate displayed? */ bool AnnotationLine::isDisplayEndArrow() const { return m_displayEndArrow; } /** * Set the display status of the arrow at the line's start coordinate. * * @param displayArrow * New status. */ void 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. * * @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 AnnotationLine::saveSubClassDataToScene(const SceneAttributes* sceneAttributes, SceneClass* sceneClass) { AnnotationOneDimensionalShape::saveSubClassDataToScene(sceneAttributes, 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 AnnotationLine::restoreSubClassDataFromScene(const SceneAttributes* sceneAttributes, const SceneClass* sceneClass) { AnnotationOneDimensionalShape::restoreSubClassDataFromScene(sceneAttributes, sceneClass); m_sceneAssistant->restoreMembers(sceneAttributes, sceneClass); } /** * Set the default value for start arrow enabled. * * @param displayArrow * Default for newly created line annotations. */ void AnnotationLine::setUserDefaultDisplayStartArrow(const bool displayArrow) { s_userDefaultDisplayStartArrow = displayArrow; } /** * Set the default value for end arrow enabled. * * @param displayArrow * Default for newly created line annotations. */ void AnnotationLine::setUserDefaultDisplayEndArrow(const bool displayArrow) { s_userDefaultDisplayEndArrow = displayArrow; } connectome-workbench-1.2.3+git41-gc4c6c90/src/Annotations/AnnotationLine.h000066400000000000000000000056541300200146000261460ustar00rootroot00000000000000#ifndef __ANNOTATION_LINE_H__ #define __ANNOTATION_LINE_H__ /*LICENSE_START*/ /* * Copyright (C) 2015 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 "AnnotationOneDimensionalShape.h" #include "CaretPointer.h" namespace caret { class AnnotationLine : public AnnotationOneDimensionalShape { public: AnnotationLine(const AnnotationAttributesDefaultTypeEnum::Enum attributeDefaultType); virtual ~AnnotationLine(); AnnotationLine(const AnnotationLine& obj); AnnotationLine& operator=(const AnnotationLine& obj); bool isDisplayStartArrow() const; void setDisplayStartArrow(const bool displayArrow); bool isDisplayEndArrow() const; void setDisplayEndArrow(const bool displayArrow); virtual bool isBackgroundColorSupported() const; static void setUserDefaultDisplayStartArrow(const bool displayArrow); static void setUserDefaultDisplayEndArrow(const bool displayArrow); // ADD_NEW_METHODS_HERE protected: virtual void saveSubClassDataToScene(const SceneAttributes* sceneAttributes, SceneClass* sceneClass); virtual void restoreSubClassDataFromScene(const SceneAttributes* sceneAttributes, const SceneClass* sceneClass); private: void copyHelperAnnotationLine(const AnnotationLine& obj); void initializeMembersAnnotationLine(); CaretPointer m_sceneAssistant; bool m_displayStartArrow; bool m_displayEndArrow; // defaults static bool s_userDefaultDisplayStartArrow; static bool s_userDefaultDisplayEndArrow; // ADD_NEW_MEMBERS_HERE }; #ifdef __ANNOTATION_LINE_DECLARE__ bool AnnotationLine::s_userDefaultDisplayStartArrow = false; bool AnnotationLine::s_userDefaultDisplayEndArrow = false; #endif // __ANNOTATION_LINE_DECLARE__ } // namespace #endif //__ANNOTATION_LINE_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Annotations/AnnotationOneDimensionalShape.cxx000066400000000000000000000616621300200146000315200ustar00rootroot00000000000000 /*LICENSE_START*/ /* * Copyright (C) 2015 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 __ANNOTATION_ONE_DIMENSIONAL_SHAPE_DECLARE__ #include "AnnotationOneDimensionalShape.h" #undef __ANNOTATION_ONE_DIMENSIONAL_SHAPE_DECLARE__ #include #include "AnnotationCoordinate.h" #include "AnnotationSpatialModification.h" #include "CaretAssert.h" #include "MathFunctions.h" #include "SceneClass.h" #include "SceneClassAssistant.h" using namespace caret; /** * \class caret::AnnotationOneDimensionalShape * \brief Class for annotations that are one-dimensional (lines) * \ingroup Annotations */ /** * Constructor. * @param type * Type of annotation. * @param attributeDefaultType * Type for attribute defaults */ AnnotationOneDimensionalShape::AnnotationOneDimensionalShape(const AnnotationTypeEnum::Enum type, const AnnotationAttributesDefaultTypeEnum::Enum attributeDefaultType) : Annotation(type, attributeDefaultType) { initializeMembersAnnotationOneDimensionalShape(); } /** * Destructor. */ AnnotationOneDimensionalShape::~AnnotationOneDimensionalShape() { } /** * Copy constructor. * @param obj * Object that is copied. */ AnnotationOneDimensionalShape::AnnotationOneDimensionalShape(const AnnotationOneDimensionalShape& obj) : Annotation(obj) { initializeMembersAnnotationOneDimensionalShape(); this->copyHelperAnnotationOneDimensionalShape(obj); } /** * Assignment operator. * @param obj * Data copied from obj to this. * @return * Reference to this object. */ AnnotationOneDimensionalShape& AnnotationOneDimensionalShape::operator=(const AnnotationOneDimensionalShape& obj) { if (this != &obj) { Annotation::operator=(obj); this->copyHelperAnnotationOneDimensionalShape(obj); } return *this; } /** * Helps with copying an object of this type. * @param obj * Object that is copied. */ void AnnotationOneDimensionalShape::copyHelperAnnotationOneDimensionalShape(const AnnotationOneDimensionalShape& obj) { *m_startCoordinate = *obj.m_startCoordinate; *m_endCoordinate = *obj.m_endCoordinate; } /** * Initialize members of this class. */ void AnnotationOneDimensionalShape::initializeMembersAnnotationOneDimensionalShape() { m_startCoordinate.grabNew(new AnnotationCoordinate()); m_endCoordinate.grabNew(new AnnotationCoordinate()); m_sceneAssistant.grabNew(new SceneClassAssistant()); } /** * @return The start coordinate for the one dimensional shape. */ AnnotationCoordinate* AnnotationOneDimensionalShape::getStartCoordinate() { return m_startCoordinate; } /** * @return The start coordinate for the one dimensional shape. */ const AnnotationCoordinate* AnnotationOneDimensionalShape::getStartCoordinate() const { return m_startCoordinate; } /** * @return The end coordinate for the one dimensional shape. */ AnnotationCoordinate* AnnotationOneDimensionalShape::getEndCoordinate() { return m_endCoordinate; } /** * @return The end coordinate for the one dimensional shape. */ const AnnotationCoordinate* AnnotationOneDimensionalShape::getEndCoordinate() const { return m_endCoordinate; } /** * Is the object modified? * @return true if modified, else false. */ bool AnnotationOneDimensionalShape::isModified() const { if (Annotation::isModified()) { return true; } if (m_startCoordinate->isModified()) { return true; } if (m_endCoordinate->isModified()) { return true; } return false; } /** * Set the status to unmodified. */ void AnnotationOneDimensionalShape::clearModified() { Annotation::clearModified(); m_startCoordinate->clearModified(); m_endCoordinate->clearModified(); } /** * Apply the coordinates, size, and rotation from the given annotation * to this annotation. * * @param otherAnnotation * The other annotation from which attributes are obtained. */ void AnnotationOneDimensionalShape::applyCoordinatesSizeAndRotationFromOther(const Annotation* otherAnnotation) { CaretAssert(otherAnnotation); const AnnotationOneDimensionalShape* otherOneDim = dynamic_cast(otherAnnotation); CaretAssert(otherOneDim); AnnotationCoordinate* startCoord = getStartCoordinate(); const AnnotationCoordinate* otherStartCoord = otherOneDim->getStartCoordinate(); *startCoord = *otherStartCoord; AnnotationCoordinate* endCoord = getEndCoordinate(); const AnnotationCoordinate* otherEndCoord = otherOneDim->getEndCoordinate(); *endCoord = *otherEndCoord; setCoordinateSpace(otherAnnotation->getCoordinateSpace()); setTabIndex(otherAnnotation->getTabIndex()); setWindowIndex(otherAnnotation->getWindowIndex()); } /** * Get the rotation angle from the one-dimensional annotation. * 0 is horizontal. * * @param viewportWidth * Width of viewport. * @param viewportHeight * Height of viewport. * @return * Rotation angle of the annotation. */ float AnnotationOneDimensionalShape::getRotationAngle(const float viewportWidth, const float viewportHeight) const { if ( ! isSizeHandleValid(AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_ROTATION)) { return 0.0; } float vpOneX = 0.0; float vpOneY = 0.0; float vpTwoX = 0.0; float vpTwoY = 0.0; getStartCoordinate()->getViewportXY(viewportWidth, viewportHeight, vpOneX, vpOneY); getEndCoordinate()->getViewportXY(viewportWidth, viewportHeight, vpTwoX, vpTwoY); const float dx = vpTwoX - vpOneX; const float dy = vpTwoY - vpOneY; float angle = 180.0 - MathFunctions::toDegrees(std::atan2(dy, dx)); if (angle < 0.0) { angle = angle + 360.0; } else if (angle > 360.0) { angle = angle - 360.0; } return angle; } /** * Set the rotation angle from the one-dimensional annotation. * 0 is horizontal. * * @param viewportWidth * Width of viewport. * @param viewportHeight * Height of viewport. * @param rotationAngle * Rotation angle for the annotation. */ void AnnotationOneDimensionalShape::setRotationAngle(const float viewportWidth, const float viewportHeight, const float rotationAngle) { if ( ! isSizeHandleValid(AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_ROTATION)) { return; } float annOneX = 0.0; float annOneY = 0.0; float annTwoX = 0.0; float annTwoY = 0.0; getStartCoordinate()->getViewportXY(viewportWidth, viewportHeight, annOneX, annOneY); getEndCoordinate()->getViewportXY(viewportWidth, viewportHeight, annTwoX, annTwoY); const float midPointXYZ[3] = { (annOneX + annTwoX) / 2.0, (annOneY + annTwoY) / 2.0, 0.0 }; const float vpOneXYZ[3] = { annOneX, annOneY, 0.0 }; const float lengthMidToOne = MathFunctions::distance3D(midPointXYZ, vpOneXYZ); const float newRotationAngle = 180.0 - rotationAngle; 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; getStartCoordinate()->setXYZFromViewportXYZ(viewportWidth, viewportHeight, annOneX, annOneY); getEndCoordinate()->setXYZFromViewportXYZ(viewportWidth, viewportHeight, annTwoX, annTwoY); } /** * Is the given sizing handle valid for this annotation? * * @sizingHandle * The sizing handle. * @return * True if sizing handle valid, else false. */ bool AnnotationOneDimensionalShape::isSizeHandleValid(const AnnotationSizingHandleTypeEnum::Enum sizingHandle) const { bool pixelsFlag = false; bool tabWindowFlag = false; bool stereotaxicFlag = false; bool surfaceFlag = false; switch (getCoordinateSpace()) { case AnnotationCoordinateSpaceEnum::PIXELS: pixelsFlag = true; break; case AnnotationCoordinateSpaceEnum::STEREOTAXIC: stereotaxicFlag = true; break; case AnnotationCoordinateSpaceEnum::SURFACE: surfaceFlag = true; break; case AnnotationCoordinateSpaceEnum::TAB: tabWindowFlag = true; break; case AnnotationCoordinateSpaceEnum::WINDOW: tabWindowFlag = true; break; } 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: validFlag = true; break; case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_LINE_START: 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; } } return validFlag; } /** * Apply a spatial modification to an annotation in surface space. * * @param spatialModification * Contains information about the spatial modification. * @return * True if the annotation was modified, else false. */ bool AnnotationOneDimensionalShape::applySpatialModificationSurfaceSpace(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: { StructureEnum::Enum structure = StructureEnum::INVALID; int32_t surfaceNumberOfNodes = -1; int32_t surfaceNodeIndex = -1; m_endCoordinate->getSurfaceSpace(structure, surfaceNumberOfNodes, surfaceNodeIndex); if (spatialModification.m_surfaceCoordinateAtMouseXY.m_surfaceNodeValid) { if ((spatialModification.m_surfaceCoordinateAtMouseXY.m_surfaceStructure == structure) && (spatialModification.m_surfaceCoordinateAtMouseXY.m_surfaceNumberOfNodes == surfaceNumberOfNodes)) { m_endCoordinate->setSurfaceSpace(spatialModification.m_surfaceCoordinateAtMouseXY.m_surfaceStructure, spatialModification.m_surfaceCoordinateAtMouseXY.m_surfaceNumberOfNodes, spatialModification.m_surfaceCoordinateAtMouseXY.m_surfaceNodeIndex); validFlag = true; } } } break; case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_LINE_START: { StructureEnum::Enum structure = StructureEnum::INVALID; int32_t surfaceNumberOfNodes = -1; int32_t surfaceNodeIndex = -1; m_startCoordinate->getSurfaceSpace(structure, surfaceNumberOfNodes, surfaceNodeIndex); if (spatialModification.m_surfaceCoordinateAtMouseXY.m_surfaceNodeValid) { if ((spatialModification.m_surfaceCoordinateAtMouseXY.m_surfaceStructure == structure) && (spatialModification.m_surfaceCoordinateAtMouseXY.m_surfaceNumberOfNodes == surfaceNumberOfNodes)) { m_startCoordinate->setSurfaceSpace(spatialModification.m_surfaceCoordinateAtMouseXY.m_surfaceStructure, spatialModification.m_surfaceCoordinateAtMouseXY.m_surfaceNumberOfNodes, spatialModification.m_surfaceCoordinateAtMouseXY.m_surfaceNodeIndex); validFlag = true; } } } break; case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_NONE: break; case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_ROTATION: break; } if (validFlag) { setModified(); } return validFlag; } /** * Apply a spatial modification to an annotation in tab or window space. * * @param spatialModification * Contains information about the spatial modification. * @return * True if the annotation was modified, else false. */ bool AnnotationOneDimensionalShape::applySpatialModificationTabOrWindowSpace(const AnnotationSpatialModification& spatialModification) { float xyz1[3]; float xyz2[3]; m_startCoordinate->getXYZ(xyz1); m_endCoordinate->getXYZ(xyz2); float newX1 = xyz1[0]; float newY1 = xyz1[1]; float newX2 = xyz2[0]; float newY2 = xyz2[1]; const float spaceDX = 100.0 * ((spatialModification.m_viewportWidth != 0.0) ? (spatialModification.m_mouseDX / spatialModification.m_viewportWidth) : 0.0); const float spaceDY = 100.0 * ((spatialModification.m_viewportHeight != 0.0) ? (spatialModification.m_mouseDY / spatialModification.m_viewportHeight) : 0.0); 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: newX2 += spaceDX; newY2 += spaceDY; validFlag = true; break; case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_LINE_START: newX1 += spaceDX; newY1 += spaceDY; validFlag = true; break; case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_NONE: newX1 += spaceDX; newY1 += spaceDY; newX2 += spaceDX; newY2 += spaceDY; validFlag = true; break; case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_ROTATION: { float vpOneXYZ[3] = { 0.0, 0.0, 0.0 }; relativeXYZToViewportXYZ(xyz1, spatialModification.m_viewportWidth, spatialModification.m_viewportHeight, vpOneXYZ); float vpTwoXYZ[3] = { 0.0, 0.0, 0.0 }; relativeXYZToViewportXYZ(xyz2, spatialModification.m_viewportWidth, spatialModification.m_viewportHeight, vpTwoXYZ); float vpXYZ[3] = { (vpOneXYZ[0] + vpTwoXYZ[0]) / 2.0, (vpOneXYZ[1] + vpTwoXYZ[1]) / 2.0, (vpOneXYZ[2] + vpTwoXYZ[2]) / 2.0 }; /* * Rotation angle is a formed by the triangle * (Mouse XY, Annotation XY, Positive X-axis). */ const float dy = spatialModification.m_mouseY - vpXYZ[1]; const float dx = spatialModification.m_mouseX - vpXYZ[0]; const float angleRadians = std::atan2(dy, dx); const float angleDegrees = MathFunctions::toDegrees(angleRadians); float rotationAngle = -angleDegrees; if (rotationAngle > 360.0) { rotationAngle -= 360.0; } else if (rotationAngle < 0.0) { rotationAngle += 360.0; } CaretPointer shapeCopy(dynamic_cast(this->clone())); shapeCopy->setRotationAngle(spatialModification.m_viewportWidth, spatialModification.m_viewportHeight, rotationAngle); const float* xyzOne = shapeCopy->getStartCoordinate()->getXYZ(); const float* xyzTwo = shapeCopy->getEndCoordinate()->getXYZ(); newX1 = xyzOne[0]; newY1 = xyzOne[1]; newX2 = xyzTwo[0]; newY2 = xyzTwo[1]; validFlag = true; } break; } if (validFlag) { if ((newX1 >= 0.0) && (newX1 <= 100.0) && (newY1 >= 0.0) && (newY1 <= 100.0) && (newX2 >= 0.0) && (newX2 <= 100.0) && (newY2 >= 0.0) && (newY2 <= 100.0)) { xyz1[0] = newX1; xyz1[1] = newY1; m_startCoordinate->setXYZ(xyz1); xyz2[0] = newX2; xyz2[1] = newY2; m_endCoordinate->setXYZ(xyz2); } else { validFlag = false; } } return validFlag; } /** * Apply a spatial modification to an annotation in stereotaxic space. * * @param spatialModification * Contains information about the spatial modification. * @return * True if the annotation was modified, else false. */ bool AnnotationOneDimensionalShape::applySpatialModificationStereotaxicSpace(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_stereotaxicCoordinateAtMouseXY.m_stereotaxicValid) { m_endCoordinate->setXYZ(spatialModification.m_stereotaxicCoordinateAtMouseXY.m_stereotaxicXYZ); validFlag = true; } break; case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_LINE_START: if (spatialModification.m_stereotaxicCoordinateAtMouseXY.m_stereotaxicValid) { m_startCoordinate->setXYZ(spatialModification.m_stereotaxicCoordinateAtMouseXY.m_stereotaxicXYZ); validFlag = true; } break; case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_NONE: break; case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_ROTATION: break; } if (validFlag) { setModified(); } return validFlag; } /** * Apply a spatial modification to an annotation. * * @param spatialModification * Contains information about the spatial modification. * @return * True if the annotation was modified, else false. */ bool AnnotationOneDimensionalShape::applySpatialModification(const AnnotationSpatialModification& spatialModification) { if ( ! isSizeHandleValid(spatialModification.m_sizingHandleType)) { return false; } switch (getCoordinateSpace()) { case AnnotationCoordinateSpaceEnum::PIXELS: break; case AnnotationCoordinateSpaceEnum::STEREOTAXIC: return applySpatialModificationStereotaxicSpace(spatialModification); break; case AnnotationCoordinateSpaceEnum::SURFACE: return applySpatialModificationSurfaceSpace(spatialModification); break; case AnnotationCoordinateSpaceEnum::TAB: return applySpatialModificationTabOrWindowSpace(spatialModification); break; case AnnotationCoordinateSpaceEnum::WINDOW: return applySpatialModificationTabOrWindowSpace(spatialModification); break; } return false; } /** * 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 AnnotationOneDimensionalShape::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 AnnotationOneDimensionalShape::restoreSubClassDataFromScene(const SceneAttributes* sceneAttributes, const SceneClass* sceneClass) { m_sceneAssistant->restoreMembers(sceneAttributes, sceneClass); } connectome-workbench-1.2.3+git41-gc4c6c90/src/Annotations/AnnotationOneDimensionalShape.h000066400000000000000000000076711300200146000311450ustar00rootroot00000000000000#ifndef __ANNOTATION_ONE_DIMENSIONAL_SHAPE_H__ #define __ANNOTATION_ONE_DIMENSIONAL_SHAPE_H__ /*LICENSE_START*/ /* * Copyright (C) 2015 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 "Annotation.h" #include "CaretPointer.h" namespace caret { class AnnotationCoordinate; class AnnotationOneDimensionalShape : public Annotation { public: AnnotationOneDimensionalShape(const AnnotationTypeEnum::Enum type, const AnnotationAttributesDefaultTypeEnum::Enum attributeDefaultType); virtual ~AnnotationOneDimensionalShape(); AnnotationOneDimensionalShape(const AnnotationOneDimensionalShape& obj); AnnotationOneDimensionalShape& operator=(const AnnotationOneDimensionalShape& obj); AnnotationCoordinate* getStartCoordinate(); const AnnotationCoordinate* getStartCoordinate() const; AnnotationCoordinate* getEndCoordinate(); const AnnotationCoordinate* getEndCoordinate() const; virtual bool isModified() const; virtual void clearModified(); virtual bool isSizeHandleValid(const AnnotationSizingHandleTypeEnum::Enum sizingHandle) const; virtual bool applySpatialModification(const AnnotationSpatialModification& spatialModification); virtual void applyCoordinatesSizeAndRotationFromOther(const Annotation* otherAnnotation); float getRotationAngle(const float viewportWidth, const float viewportHeight) const; void setRotationAngle(const float viewportWidth, const float viewportHeight, const float rotationAngle); // ADD_NEW_METHODS_HERE protected: virtual void saveSubClassDataToScene(const SceneAttributes* sceneAttributes, SceneClass* sceneClass); virtual void restoreSubClassDataFromScene(const SceneAttributes* sceneAttributes, const SceneClass* sceneClass); private: void copyHelperAnnotationOneDimensionalShape(const AnnotationOneDimensionalShape& obj); void initializeMembersAnnotationOneDimensionalShape(); bool applySpatialModificationSurfaceSpace(const AnnotationSpatialModification& spatialModification); bool applySpatialModificationStereotaxicSpace(const AnnotationSpatialModification& spatialModification); bool applySpatialModificationTabOrWindowSpace(const AnnotationSpatialModification& spatialModification); CaretPointer m_sceneAssistant; CaretPointer m_startCoordinate; CaretPointer m_endCoordinate; // ADD_NEW_MEMBERS_HERE }; #ifdef __ANNOTATION_ONE_DIMENSIONAL_SHAPE_DECLARE__ // #endif // __ANNOTATION_ONE_DIMENSIONAL_SHAPE_DECLARE__ } // namespace #endif //__ANNOTATION_ONE_DIMENSIONAL_SHAPE_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Annotations/AnnotationOval.cxx000066400000000000000000000100041300200146000265140ustar00rootroot00000000000000 /*LICENSE_START*/ /* * Copyright (C) 2015 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 __ANNOTATION_OVAL_DECLARE__ #include "AnnotationOval.h" #undef __ANNOTATION_OVAL_DECLARE__ #include "CaretAssert.h" #include "SceneClass.h" #include "SceneClassAssistant.h" using namespace caret; /** * \class caret::AnnotationOval * \brief An annotation oval. * \ingroup Annotations */ /** * Constructor. * * @param attributeDefaultType * Type for attribute defaults */ AnnotationOval::AnnotationOval(const AnnotationAttributesDefaultTypeEnum::Enum attributeDefaultType) : AnnotationTwoDimensionalShape(AnnotationTypeEnum::OVAL, attributeDefaultType) { initializeMembersAnnotationOval(); } /** * Destructor. */ AnnotationOval::~AnnotationOval() { } /** * Copy constructor. * @param obj * Object that is copied. */ AnnotationOval::AnnotationOval(const AnnotationOval& obj) : AnnotationTwoDimensionalShape(obj) { this->initializeMembersAnnotationOval(); this->copyHelperAnnotationOval(obj); } /** * Assignment operator. * @param obj * Data copied from obj to this. * @return * Reference to this object. */ AnnotationOval& AnnotationOval::operator=(const AnnotationOval& obj) { if (this != &obj) { AnnotationTwoDimensionalShape::operator=(obj); this->copyHelperAnnotationOval(obj); } return *this; } /** * Helps with copying an object of this type. * @param obj * Object that is copied. */ void AnnotationOval::copyHelperAnnotationOval(const AnnotationOval& /*obj*/) { /* nothing to copy */ } /** * Initialize a new instance of this class. */ void AnnotationOval::initializeMembersAnnotationOval() { m_sceneAssistant.grabNew(new SceneClassAssistant()); } /** * 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 AnnotationOval::saveSubClassDataToScene(const SceneAttributes* sceneAttributes, SceneClass* sceneClass) { AnnotationTwoDimensionalShape::saveSubClassDataToScene(sceneAttributes, 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 AnnotationOval::restoreSubClassDataFromScene(const SceneAttributes* sceneAttributes, const SceneClass* sceneClass) { AnnotationTwoDimensionalShape::restoreSubClassDataFromScene(sceneAttributes, sceneClass); m_sceneAssistant->restoreMembers(sceneAttributes, sceneClass); } connectome-workbench-1.2.3+git41-gc4c6c90/src/Annotations/AnnotationOval.h000066400000000000000000000042321300200146000261470ustar00rootroot00000000000000#ifndef __ANNOTATION_OVAL_H__ #define __ANNOTATION_OVAL_H__ /*LICENSE_START*/ /* * Copyright (C) 2015 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 "AnnotationTwoDimensionalShape.h" #include "CaretPointer.h" namespace caret { class AnnotationOval : public AnnotationTwoDimensionalShape { public: AnnotationOval(const AnnotationAttributesDefaultTypeEnum::Enum attributeDefaultType); virtual ~AnnotationOval(); AnnotationOval(const AnnotationOval&); AnnotationOval& operator=(const AnnotationOval&); // ADD_NEW_METHODS_HERE protected: virtual void saveSubClassDataToScene(const SceneAttributes* sceneAttributes, SceneClass* sceneClass); virtual void restoreSubClassDataFromScene(const SceneAttributes* sceneAttributes, const SceneClass* sceneClass); private: void copyHelperAnnotationOval(const AnnotationOval& obj); void initializeMembersAnnotationOval(); CaretPointer m_sceneAssistant; // ADD_NEW_MEMBERS_HERE }; #ifdef __ANNOTATION_OVAL_DECLARE__ // #endif // __ANNOTATION_OVAL_DECLARE__ } // namespace #endif //__ANNOTATION_OVAL_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Annotations/AnnotationPercentSizeText.cxx000066400000000000000000000057621300200146000307320ustar00rootroot00000000000000 /*LICENSE_START*/ /* * Copyright (C) 2015 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 __ANNOTATION_PERCENT_SIZE_TEXT_DECLARE__ #include "AnnotationPercentSizeText.h" #undef __ANNOTATION_PERCENT_SIZE_TEXT_DECLARE__ #include "CaretAssert.h" using namespace caret; /** * \class caret::AnnotationPercentSizeText * \brief Text annotation with font height a percentage of viewport height. * \ingroup Annotations */ /** * Constructor. * * @param attributeDefaultType * Type for attribute defaults */ AnnotationPercentSizeText::AnnotationPercentSizeText(const AnnotationAttributesDefaultTypeEnum::Enum attributeDefaultType) : AnnotationText(attributeDefaultType, AnnotationTextFontSizeTypeEnum::PERCENTAGE_OF_VIEWPORT_HEIGHT) { } /** * Destructor. */ AnnotationPercentSizeText::~AnnotationPercentSizeText() { } /** * Copy constructor. * @param obj * Object that is copied. */ AnnotationPercentSizeText::AnnotationPercentSizeText(const AnnotationPercentSizeText& obj) : AnnotationText(obj) { this->copyHelperAnnotationPercentSizeText(obj); } /** * Assignment operator. * @param obj * Data copied from obj to this. * @return * Reference to this object. */ AnnotationPercentSizeText& AnnotationPercentSizeText::operator=(const AnnotationPercentSizeText& obj) { if (this != &obj) { AnnotationText::operator=(obj); this->copyHelperAnnotationPercentSizeText(obj); } return *this; } /** * Helps with copying an object of this type. * @param obj * Object that is copied. */ void AnnotationPercentSizeText::copyHelperAnnotationPercentSizeText(const AnnotationPercentSizeText& /* obj */) { } /** * @return Size of font as a percentage of the viewport height. * * Range is zero to one hundred. */ float AnnotationPercentSizeText::getFontPercentViewportSize() const { return getFontPercentViewportSizeProtected(); } /** * Set the size of the font as a percentage of the viewport height. * * @param fontPercentViewportHeight * New value for percentage of viewport height. * Range is zero to one hundred. */ void AnnotationPercentSizeText::setFontPercentViewportSize(const float fontPercentViewportHeight) { setFontPercentViewportSizeProtected(fontPercentViewportHeight); } connectome-workbench-1.2.3+git41-gc4c6c90/src/Annotations/AnnotationPercentSizeText.h000066400000000000000000000036351300200146000303540ustar00rootroot00000000000000#ifndef __ANNOTATION_PERCENT_SIZE_TEXT_H__ #define __ANNOTATION_PERCENT_SIZE_TEXT_H__ /*LICENSE_START*/ /* * Copyright (C) 2015 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 "AnnotationText.h" namespace caret { class AnnotationPercentSizeText : public AnnotationText { public: AnnotationPercentSizeText(const AnnotationAttributesDefaultTypeEnum::Enum attributeDefaultType); virtual ~AnnotationPercentSizeText(); AnnotationPercentSizeText(const AnnotationPercentSizeText& obj); AnnotationPercentSizeText& operator=(const AnnotationPercentSizeText& obj); virtual float getFontPercentViewportSize() const; virtual void setFontPercentViewportSize(const float fontPercentViewportHeight); // ADD_NEW_METHODS_HERE private: void copyHelperAnnotationPercentSizeText(const AnnotationPercentSizeText& obj); // ADD_NEW_MEMBERS_HERE }; #ifdef __ANNOTATION_PERCENT_SIZE_TEXT_DECLARE__ // #endif // __ANNOTATION_PERCENT_SIZE_TEXT_DECLARE__ } // namespace #endif //__ANNOTATION_PERCENT_SIZE_TEXT_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Annotations/AnnotationPointSizeText.cxx000066400000000000000000000066451300200146000304240ustar00rootroot00000000000000 /*LICENSE_START*/ /* * Copyright (C) 2015 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 __ANNOTATION_POINT_SIZE_TEXT_DECLARE__ #include "AnnotationPointSizeText.h" #undef __ANNOTATION_POINT_SIZE_TEXT_DECLARE__ #include "CaretAssert.h" using namespace caret; /** * \class caret::AnnotationPointSizeText * \brief Text annotation with font height in points (one point is 1/72 of inch). * \ingroup Annotations */ /** * Constructor. * * @param attributeDefaultType * Type for attribute defaults */ AnnotationPointSizeText::AnnotationPointSizeText(const AnnotationAttributesDefaultTypeEnum::Enum attributeDefaultType) : AnnotationText(attributeDefaultType, AnnotationTextFontSizeTypeEnum::POINTS) { } /** * Destructor. */ AnnotationPointSizeText::~AnnotationPointSizeText() { } /** * Copy constructor. * @param obj * Object that is copied. */ AnnotationPointSizeText::AnnotationPointSizeText(const AnnotationPointSizeText& obj) : AnnotationText(obj) { this->copyHelperAnnotationPointSizeText(obj); } /** * Assignment operator. * @param obj * Data copied from obj to this. * @return * Reference to this object. */ AnnotationPointSizeText& AnnotationPointSizeText::operator=(const AnnotationPointSizeText& obj) { if (this != &obj) { AnnotationText::operator=(obj); this->copyHelperAnnotationPointSizeText(obj); } return *this; } /** * Helps with copying an object of this type. * @param obj * Object that is copied. */ void AnnotationPointSizeText::copyHelperAnnotationPointSizeText(const AnnotationPointSizeText& /*obj*/) { } /** * @return The font point size. */ AnnotationTextFontPointSizeEnum::Enum AnnotationPointSizeText::getFontPointSize() const { return getFontPointSizeProtected(); } /** * Set the font point size. * * @param fontPointSize * New font point size. */ void AnnotationPointSizeText::setFontPointSize(const AnnotationTextFontPointSizeEnum::Enum fontPointSize) { setFontPointSizeProtected(fontPointSize); } /** * @return Size of font as a percentage of the viewport height. * * Range is zero to one hundred. */ float AnnotationPointSizeText::getFontPercentViewportSize() const { CaretAssertMessage(0, "This method should never be called for Point Size Text"); return 10.0; } /** * Set the size of the font as a percentage of the viewport height. * * @param fontPercentViewportHeight * New value for percentage of viewport height. * Range is zero to one hundred. */ void AnnotationPointSizeText::setFontPercentViewportSize(const float /*fontPercentViewportHeight*/) { CaretAssertMessage(0, "This method should never be called for Point Size Text"); } connectome-workbench-1.2.3+git41-gc4c6c90/src/Annotations/AnnotationPointSizeText.h000066400000000000000000000040661300200146000300440ustar00rootroot00000000000000#ifndef __ANNOTATION_POINT_SIZE_TEXT_H__ #define __ANNOTATION_POINT_SIZE_TEXT_H__ /*LICENSE_START*/ /* * Copyright (C) 2015 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 "AnnotationText.h" namespace caret { class AnnotationPointSizeText : public AnnotationText { public: AnnotationPointSizeText(const AnnotationAttributesDefaultTypeEnum::Enum attributeDefaultType); virtual ~AnnotationPointSizeText(); AnnotationPointSizeText(const AnnotationPointSizeText& obj); AnnotationPointSizeText& operator=(const AnnotationPointSizeText& obj); AnnotationTextFontPointSizeEnum::Enum getFontPointSize() const; void setFontPointSize(const AnnotationTextFontPointSizeEnum::Enum fontPointSize); virtual float getFontPercentViewportSize() const; virtual void setFontPercentViewportSize(const float fontPercentViewportHeight); // ADD_NEW_METHODS_HERE private: void copyHelperAnnotationPointSizeText(const AnnotationPointSizeText& obj); // ADD_NEW_MEMBERS_HERE }; #ifdef __ANNOTATION_POINT_SIZE_TEXT_DECLARE__ // #endif // __ANNOTATION_POINT_SIZE_TEXT_DECLARE__ } // namespace #endif //__ANNOTATION_POINT_SIZE_TEXT_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Annotations/AnnotationRedoUndoCommand.cxx000066400000000000000000002473301300200146000306470ustar00rootroot00000000000000 /*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 __ANNOTATION_UNDO_COMMAND_DECLARE__ #include "AnnotationRedoUndoCommand.h" #undef __ANNOTATION_UNDO_COMMAND_DECLARE__ #include #include "AnnotationFontAttributesInterface.h" #include "AnnotationLine.h" #include "AnnotationPercentSizeText.h" #include "AnnotationPointSizeText.h" #include "AnnotationTwoDimensionalShape.h" #include "EventAnnotationAddToRemoveFromFile.h" #include "EventAnnotationGrouping.h" #include "EventGetViewportSize.h" #include "EventManager.h" #include "CaretAssert.h" #include "CaretLogger.h" #include "MathFunctions.h" using namespace caret; /** * \class caret::AnnotationRedoUndoCommand * \brief Command pattern for annotation modifications that undo and redo. * \ingroup Annotations */ /** * Constructor. */ AnnotationRedoUndoCommand::AnnotationRedoUndoCommand() : CaretUndoCommand() { m_mode = AnnotationRedoUndoCommandModeEnum::INVALID; m_annotationGroupMemento = NULL; m_sortedFlag = false; } /** * Destructor. */ AnnotationRedoUndoCommand::~AnnotationRedoUndoCommand() { for (std::vector::iterator iter = m_annotationMementos.begin(); iter != m_annotationMementos.end(); iter++) { delete *iter; } if (m_annotationGroupMemento != NULL) { delete m_annotationGroupMemento; } } /** * Apply a redo or undo command. * * @param annotation * The annotation being redone or undone. * @param annotationValue * The redo or undo value for the annotation. */ void AnnotationRedoUndoCommand::applyRedoOrUndo(Annotation* annotation, const Annotation* annotationValue) const { CaretAssert(annotation); const AnnotationTypeEnum::Enum annType = annotation->getType(); switch (m_mode) { case AnnotationRedoUndoCommandModeEnum::INVALID: break; case AnnotationRedoUndoCommandModeEnum::COLOR_BACKGROUND: { CaretAssert(annotationValue); annotation->setBackgroundColor(annotationValue->getBackgroundColor()); float rgba[4]; annotationValue->getCustomBackgroundColor(rgba); annotation->setCustomBackgroundColor(rgba); } break; case AnnotationRedoUndoCommandModeEnum::COLOR_FOREGROUND: { CaretAssert(annotationValue); annotation->setLineColor(annotationValue->getLineColor()); float rgba[4]; annotationValue->getCustomLineColor(rgba); annotation->setCustomLineColor(rgba); } break; case AnnotationRedoUndoCommandModeEnum::COORDINATE_ONE: { AnnotationOneDimensionalShape* oneDimShape = dynamic_cast(annotation); AnnotationTwoDimensionalShape* twoDimShape = dynamic_cast(annotation); const AnnotationOneDimensionalShape* valueOneDimShape = dynamic_cast(annotationValue); const AnnotationTwoDimensionalShape* valueTwoDimShape = dynamic_cast(annotationValue); if ((oneDimShape != NULL) && (valueOneDimShape != NULL)) { AnnotationCoordinate* coordDest = oneDimShape->getStartCoordinate(); const AnnotationCoordinate* coordFrom = valueOneDimShape->getStartCoordinate(); *coordDest = *coordFrom; } else if ((twoDimShape != NULL) && (valueTwoDimShape != NULL)) { AnnotationCoordinate* coordDest = twoDimShape->getCoordinate(); const AnnotationCoordinate* coordFrom = valueTwoDimShape->getCoordinate(); *coordDest = *coordFrom; } else { CaretAssert(0); } } break; case AnnotationRedoUndoCommandModeEnum::COORDINATE_ONE_AND_TWO: { AnnotationOneDimensionalShape* oneDimShape = dynamic_cast(annotation); const AnnotationOneDimensionalShape* valueOneDimShape = dynamic_cast(annotationValue); if ((oneDimShape != NULL) && (valueOneDimShape != NULL)) { AnnotationCoordinate* coordDestStart = oneDimShape->getStartCoordinate(); const AnnotationCoordinate* coordFromStart = valueOneDimShape->getStartCoordinate(); *coordDestStart = *coordFromStart; AnnotationCoordinate* coordDestEnd = oneDimShape->getEndCoordinate(); const AnnotationCoordinate* coordFromEnd = valueOneDimShape->getEndCoordinate(); *coordDestEnd = *coordFromEnd; } else { CaretAssert(0); } } break; case AnnotationRedoUndoCommandModeEnum::COORDINATE_TWO: { AnnotationOneDimensionalShape* oneDimShape = dynamic_cast(annotation); const AnnotationOneDimensionalShape* valueOneDimShape = dynamic_cast(annotationValue); if ((oneDimShape != NULL) && (valueOneDimShape != NULL)) { AnnotationCoordinate* coordDest = oneDimShape->getEndCoordinate(); const AnnotationCoordinate* coordFrom = valueOneDimShape->getEndCoordinate(); *coordDest = *coordFrom; } else { CaretAssert(0); } } break; case AnnotationRedoUndoCommandModeEnum::CREATE_ANNOTATION: CaretAssertMessage(0, ("This mode " + AnnotationRedoUndoCommandModeEnum::toName(m_mode) + " is handle in the redo() and undo() functions.")); break; case AnnotationRedoUndoCommandModeEnum::CUT_ANNOTATION: CaretAssertMessage(0, ("This mode " + AnnotationRedoUndoCommandModeEnum::toName(m_mode) + " is handle in the redo() and undo() functions.")); break; case AnnotationRedoUndoCommandModeEnum::DELETE_ANNOTATIONS: 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: CaretAssert(0); case AnnotationRedoUndoCommandModeEnum::GROUPING_UNGROUP: CaretAssert(0); case AnnotationRedoUndoCommandModeEnum::LINE_ARROW_START: if ((annType == AnnotationTypeEnum::LINE) && (annotationValue->getType() == AnnotationTypeEnum::LINE)) { AnnotationLine* line = dynamic_cast(annotation); CaretAssert(line); const AnnotationLine* lineValue = dynamic_cast(annotationValue); line->setDisplayStartArrow(lineValue->isDisplayStartArrow()); } else { CaretAssert(0); } break; case AnnotationRedoUndoCommandModeEnum::LINE_ARROW_END: if ((annType == AnnotationTypeEnum::LINE) && (annotationValue->getType() == AnnotationTypeEnum::LINE)) { AnnotationLine* line = dynamic_cast(annotation); CaretAssert(line); const AnnotationLine* lineValue = dynamic_cast(annotationValue); line->setDisplayEndArrow(lineValue->isDisplayEndArrow()); } else { CaretAssert(0); } break; case AnnotationRedoUndoCommandModeEnum::LINE_WIDTH_FOREGROUND: if (annotation->isLineWidthSupported()) { //annotation->setLineWidth(value.m_floatValue); annotation->setLineWidth(annotationValue->getLineWidth()); } break; case AnnotationRedoUndoCommandModeEnum::LOCATION_AND_SIZE: annotation->applyCoordinatesSizeAndRotationFromOther(annotationValue); break; case AnnotationRedoUndoCommandModeEnum::PASTE_ANNOTATION: CaretAssertMessage(0, ("This mode " + AnnotationRedoUndoCommandModeEnum::toName(m_mode) + " is handle in the redo() and undo() functions.")); break; case AnnotationRedoUndoCommandModeEnum::ROTATION_ANGLE: { AnnotationTwoDimensionalShape* twoDimAnn = dynamic_cast(annotation); const AnnotationTwoDimensionalShape* twoDimValue = dynamic_cast(annotationValue); if ((twoDimAnn != NULL) && (twoDimValue != NULL)) { twoDimAnn->setRotationAngle(twoDimValue->getRotationAngle()); } else { AnnotationOneDimensionalShape* oneDimAnn = dynamic_cast(annotation); const AnnotationOneDimensionalShape* oneDimValue = dynamic_cast(annotationValue); if ((oneDimAnn != NULL) && (oneDimValue != NULL)) { oneDimAnn->getStartCoordinate()->setXYZ(oneDimValue->getStartCoordinate()->getXYZ()); oneDimAnn->getEndCoordinate()->setXYZ(oneDimValue->getEndCoordinate()->getXYZ()); } } } break; case AnnotationRedoUndoCommandModeEnum::TEXT_ALIGNMENT_HORIZONTAL: { AnnotationText* textAnn = dynamic_cast(annotation); const AnnotationText* textAnnValue = dynamic_cast(annotationValue); if ((textAnn != NULL) && (textAnnValue != NULL)) { textAnn->setHorizontalAlignment(textAnnValue->getHorizontalAlignment()); } else { CaretAssert(0); } } break; case AnnotationRedoUndoCommandModeEnum::TEXT_ALIGNMENT_VERTICAL: { AnnotationText* textAnn = dynamic_cast(annotation); const AnnotationText* textAnnValue = dynamic_cast(annotationValue); if ((textAnn != NULL) && (textAnnValue != NULL)) { textAnn->setVerticalAlignment(textAnnValue->getVerticalAlignment()); } else { CaretAssert(0); } } break; case AnnotationRedoUndoCommandModeEnum::TEXT_CHARACTERS: { AnnotationText* textAnn = dynamic_cast(annotation); const AnnotationText* textAnnValue = dynamic_cast(annotationValue); if ((textAnn != NULL) && (textAnnValue != NULL)) { textAnn->setText(textAnnValue->getText()); } else { CaretAssert(0); } } break; case AnnotationRedoUndoCommandModeEnum::TEXT_COLOR: { AnnotationFontAttributesInterface* textAnn = dynamic_cast(annotation); const AnnotationFontAttributesInterface* textAnnValue = dynamic_cast(annotationValue); if ((textAnn != NULL) && (textAnnValue != NULL)) { textAnn->setTextColor(textAnnValue->getTextColor()); float rgba[4]; textAnnValue->getCustomTextColor(rgba); textAnn->setCustomTextColor(rgba); } else { CaretAssert(0); } CaretAssert(annotationValue); annotation->setLineColor(annotationValue->getLineColor()); float rgba[4]; annotationValue->getCustomLineColor(rgba); annotation->setCustomLineColor(rgba); } break; case AnnotationRedoUndoCommandModeEnum::TEXT_CONNECT_TO_BRAINORDINATE: { AnnotationText* textAnn = dynamic_cast(annotation); const AnnotationText* textAnnValue = dynamic_cast(annotationValue); if ((textAnn != NULL) && (textAnnValue != NULL)) { textAnn->setConnectToBrainordinate(textAnnValue->getConnectToBrainordinate()); } else { CaretAssert(0); } } break; case AnnotationRedoUndoCommandModeEnum::TEXT_FONT_BOLD: { AnnotationFontAttributesInterface* textAnn = dynamic_cast(annotation); const AnnotationFontAttributesInterface* textAnnValue = dynamic_cast(annotationValue); if ((textAnn != NULL) && (textAnnValue != NULL)) { textAnn->setBoldStyleEnabled(textAnnValue->isBoldStyleEnabled()); } else { CaretAssert(0); } } break; case AnnotationRedoUndoCommandModeEnum::TEXT_FONT_ITALIC: { AnnotationFontAttributesInterface* textAnn = dynamic_cast(annotation); const AnnotationFontAttributesInterface* textAnnValue = dynamic_cast(annotationValue); if ((textAnn != NULL) && (textAnnValue != NULL)) { textAnn->setItalicStyleEnabled(textAnnValue->isItalicStyleEnabled()); } else { CaretAssert(0); } } break; case AnnotationRedoUndoCommandModeEnum::TEXT_FONT_NAME: { AnnotationFontAttributesInterface* textAnn = dynamic_cast(annotation); const AnnotationFontAttributesInterface* textAnnValue = dynamic_cast(annotationValue); if ((textAnn != NULL) && (textAnnValue != NULL)) { textAnn->setFont(textAnnValue->getFont()); } else { CaretAssert(0); } } break; case AnnotationRedoUndoCommandModeEnum::TEXT_FONT_PERCENT_SIZE: { AnnotationFontAttributesInterface* textAnn = dynamic_cast(annotation); const AnnotationFontAttributesInterface* textAnnValue = dynamic_cast(annotationValue); if ((textAnn != NULL) && (textAnnValue != NULL)) { textAnn->setFontPercentViewportSize(textAnnValue->getFontPercentViewportSize()); } else { CaretAssert(0); } } break; case AnnotationRedoUndoCommandModeEnum::TEXT_FONT_POINT_SIZE: { AnnotationPointSizeText* textAnn = dynamic_cast(annotation); const AnnotationPointSizeText* textAnnValue = dynamic_cast(annotationValue); if ((textAnn != NULL) && (textAnnValue != NULL)) { textAnn->setFontPointSize(textAnnValue->getFontPointSize()); } else { CaretAssert(0); } } break; case AnnotationRedoUndoCommandModeEnum::TEXT_FONT_UNDERLINE: { AnnotationFontAttributesInterface* textAnn = dynamic_cast(annotation); const AnnotationFontAttributesInterface* textAnnValue = dynamic_cast(annotationValue); if ((textAnn != NULL) && (textAnnValue != NULL)) { textAnn->setUnderlineStyleEnabled(textAnnValue->isUnderlineStyleEnabled()); } else { CaretAssert(0); } } break; case AnnotationRedoUndoCommandModeEnum::TEXT_ORIENTATION: { AnnotationText* textAnn = dynamic_cast(annotation); const AnnotationText* textAnnValue = dynamic_cast(annotationValue); if ((textAnn != NULL) && (textAnnValue != NULL)) { textAnn->setOrientation(textAnnValue->getOrientation()); } else { CaretAssert(0); } } break; case AnnotationRedoUndoCommandModeEnum::TWO_DIM_HEIGHT: { AnnotationTwoDimensionalShape* twoDimAnn = dynamic_cast(annotation); const AnnotationTwoDimensionalShape* twoDimValue = dynamic_cast(annotationValue); if ((twoDimAnn != NULL) && (twoDimValue != NULL)) { twoDimAnn->setHeight(twoDimValue->getHeight()); } else { CaretAssert(0); } } break; case AnnotationRedoUndoCommandModeEnum::TWO_DIM_WIDTH: { AnnotationTwoDimensionalShape* twoDimAnn = dynamic_cast(annotation); const AnnotationTwoDimensionalShape* twoDimValue = dynamic_cast(annotationValue); if ((twoDimAnn != NULL) && (twoDimValue != NULL)) { twoDimAnn->setWidth(twoDimValue->getWidth()); } else { CaretAssert(0); } } break; } } /** * Operation that "redoes" the command. * * @param errorMessageOut * Output containing error message. * @return * True if the command executed successfully, else false. */ bool AnnotationRedoUndoCommand::redo(AString& errorMessageOut) { errorMessageOut.clear(); if (m_mode == AnnotationRedoUndoCommandModeEnum::GROUPING_GROUP) { CaretAssert(m_annotationGroupMemento); EventAnnotationGrouping groupEvent; groupEvent.setModeGroupAnnotations(getWindowIndex(), m_annotationGroupMemento->m_annotationGroupKey, m_annotationGroupMemento->m_annotations); EventManager::get()->sendEvent(groupEvent.getPointer()); m_annotationGroupMemento->setUndoAnnotationGroupKey(groupEvent.getGroupKeyToWhichAnnotationsWereMoved()); if (groupEvent.isError()) { errorMessageOut = groupEvent.getErrorMessage(); return false; } return true; } else if (m_mode == AnnotationRedoUndoCommandModeEnum::GROUPING_UNGROUP) { CaretAssert(m_annotationGroupMemento); EventAnnotationGrouping groupEvent; groupEvent.setModeUngroupAnnotations(getWindowIndex(), m_annotationGroupMemento->m_annotationGroupKey); EventManager::get()->sendEvent(groupEvent.getPointer()); //m_annotationGroupMemento->setUndoAnnotationGroupKey(groupEvent.getGroupKeyToWhichAnnotationsWereMoved()); if (groupEvent.isError()) { errorMessageOut = groupEvent.getErrorMessage(); return false; } return true; } else if (m_mode == AnnotationRedoUndoCommandModeEnum::GROUPING_REGROUP) { CaretAssert(m_annotationGroupMemento); EventAnnotationGrouping groupEvent; groupEvent.setModeRegroupAnnotations(getWindowIndex(), m_annotationGroupMemento->m_annotationGroupKey); EventManager::get()->sendEvent(groupEvent.getPointer()); m_annotationGroupMemento->setUndoAnnotationGroupKey(groupEvent.getGroupKeyToWhichAnnotationsWereMoved()); if (groupEvent.isError()) { errorMessageOut = groupEvent.getErrorMessage(); return false; } return true; } bool validFlag = true; for (std::vector::iterator iter = m_annotationMementos.begin(); iter != m_annotationMementos.end(); iter++) { const AnnotationMemento* annMem = *iter; CaretAssert(annMem); if (m_mode == AnnotationRedoUndoCommandModeEnum::CREATE_ANNOTATION) { EventAnnotationAddToRemoveFromFile event(EventAnnotationAddToRemoveFromFile::MODE_CREATE, annMem->m_annotationFile, annMem->m_annotation); EventManager::get()->sendEvent(event.getPointer()); if (event.isError()) { errorMessageOut.appendWithNewLine(event.getErrorMessage()); validFlag = false; } } else if (m_mode == AnnotationRedoUndoCommandModeEnum::CUT_ANNOTATION) { EventAnnotationAddToRemoveFromFile event(EventAnnotationAddToRemoveFromFile::MODE_CUT, annMem->m_annotationFile, annMem->m_annotation); EventManager::get()->sendEvent(event.getPointer()); if (event.isError()) { errorMessageOut.appendWithNewLine(event.getErrorMessage()); validFlag = false; } } else if (m_mode == AnnotationRedoUndoCommandModeEnum::DELETE_ANNOTATIONS) { EventAnnotationAddToRemoveFromFile event(EventAnnotationAddToRemoveFromFile::MODE_DELETE, annMem->m_annotationFile, annMem->m_annotation); EventManager::get()->sendEvent(event.getPointer()); if (event.isError()) { errorMessageOut.appendWithNewLine(event.getErrorMessage()); validFlag = false; } } else if (m_mode == AnnotationRedoUndoCommandModeEnum::PASTE_ANNOTATION) { EventAnnotationAddToRemoveFromFile pasteEvent(EventAnnotationAddToRemoveFromFile::MODE_PASTE, annMem->m_annotationFile, annMem->m_annotation); EventManager::get()->sendEvent(pasteEvent.getPointer()); if (pasteEvent.isError()) { errorMessageOut.appendWithNewLine(pasteEvent.getErrorMessage()); validFlag = false; } } else { /* * Note: Does not report any errors */ applyRedoOrUndo(annMem->m_annotation, annMem->m_redoAnnotation); } } return validFlag; } /** * Operation that "undoes" the command. * * @param errorMessageOut * Output containing error message. * @return * True if the command executed successfully, else false. */ bool AnnotationRedoUndoCommand::undo(AString& errorMessageOut) { errorMessageOut.clear(); if (m_mode == AnnotationRedoUndoCommandModeEnum::GROUPING_GROUP) { CaretAssert(m_annotationGroupMemento); EventAnnotationGrouping groupEvent; groupEvent.setModeUngroupAnnotations(getWindowIndex(), m_annotationGroupMemento->m_undoAnnotationGroupKey); EventManager::get()->sendEvent(groupEvent.getPointer()); if (groupEvent.isError()) { errorMessageOut = groupEvent.getErrorMessage(); return false; } return true; } else if (m_mode == AnnotationRedoUndoCommandModeEnum::GROUPING_UNGROUP) { CaretAssert(m_annotationGroupMemento); EventAnnotationGrouping groupEvent; groupEvent.setModeRegroupAnnotations(getWindowIndex(), m_annotationGroupMemento->m_annotationGroupKey); EventManager::get()->sendEvent(groupEvent.getPointer()); //m_annotationGroupMemento->setUndoAnnotationGroupKey(groupEvent.getGroupKeyToWhichAnnotationsWereMoved()); if (groupEvent.isError()) { errorMessageOut = groupEvent.getErrorMessage(); return false; } return true; } else if (m_mode == AnnotationRedoUndoCommandModeEnum::GROUPING_REGROUP) { CaretAssert(m_annotationGroupMemento); EventAnnotationGrouping groupEvent; groupEvent.setModeUngroupAnnotations(getWindowIndex(), m_annotationGroupMemento->m_undoAnnotationGroupKey); EventManager::get()->sendEvent(groupEvent.getPointer()); m_annotationGroupMemento->setUndoAnnotationGroupKey(groupEvent.getGroupKeyToWhichAnnotationsWereMoved()); if (groupEvent.isError()) { errorMessageOut = groupEvent.getErrorMessage(); return false; } return true; } bool validFlag = true; for (std::vector::iterator iter = m_annotationMementos.begin(); iter != m_annotationMementos.end(); iter++) { const AnnotationMemento* annMem = *iter; CaretAssert(annMem); if (m_mode == AnnotationRedoUndoCommandModeEnum::CREATE_ANNOTATION) { EventAnnotationAddToRemoveFromFile event(EventAnnotationAddToRemoveFromFile::MODE_UNCREATE, annMem->m_annotationFile, annMem->m_annotation); EventManager::get()->sendEvent(event.getPointer()); if (event.isError()) { errorMessageOut.appendWithNewLine(event.getErrorMessage()); validFlag = false; } } else if (m_mode == AnnotationRedoUndoCommandModeEnum::CUT_ANNOTATION) { EventAnnotationAddToRemoveFromFile event(EventAnnotationAddToRemoveFromFile::MODE_UNCUT, annMem->m_annotationFile, annMem->m_annotation); EventManager::get()->sendEvent(event.getPointer()); if (event.isError()) { errorMessageOut.appendWithNewLine(event.getErrorMessage()); validFlag = false; } } else if (m_mode == AnnotationRedoUndoCommandModeEnum::DELETE_ANNOTATIONS) { EventAnnotationAddToRemoveFromFile event(EventAnnotationAddToRemoveFromFile::MODE_UNDELETE, annMem->m_annotationFile, annMem->m_annotation); EventManager::get()->sendEvent(event.getPointer()); if (event.isError()) { errorMessageOut.appendWithNewLine(event.getErrorMessage()); validFlag = false; } } else if (m_mode == AnnotationRedoUndoCommandModeEnum::PASTE_ANNOTATION) { EventAnnotationAddToRemoveFromFile pasteEvent(EventAnnotationAddToRemoveFromFile::MODE_UNPASTE, annMem->m_annotationFile, annMem->m_annotation); EventManager::get()->sendEvent(pasteEvent.getPointer()); if (pasteEvent.isError()) { errorMessageOut.appendWithNewLine(pasteEvent.getErrorMessage()); validFlag = false; } } else { applyRedoOrUndo(annMem->m_annotation, annMem->m_undoAnnotation); } } return validFlag; } /** * @return Is the command valid? */ bool AnnotationRedoUndoCommand::isValid() const { if (m_annotationMementos.size() > 0) { return true; } else if (m_annotationGroupMemento != NULL) { return true; } return false; // return m_annotationMementos.size(); } /** * Attempts to merge this command with command. Returns true on success; otherwise returns false. * * If this function returns true, calling this command's redo() must have the same effect as * redoing both this command and command. Similarly, calling this command's undo() must have * the same effect as undoing command and this command. * * @return True if the given command was merged with this command, else false. */ bool AnnotationRedoUndoCommand::mergeWith(const CaretUndoCommand* command) { const AnnotationRedoUndoCommand* otherCommand = dynamic_cast(command); CaretAssert(otherCommand); /* * Assume compatibility if same mode and applied to same number of annotations. */ if (m_mode != otherCommand->m_mode) { return false; } if (m_annotationMementos.size() != otherCommand->m_annotationMementos.size()) { return false; } /* * Sort mementos so that the standard library's equal() function * can compare the annotation pointers */ sortAnnotationMementos(); otherCommand->sortAnnotationMementos(); /* * Compare to verify that the corresponding annotation mementos * are the same. */ if ( ! std::equal(m_annotationMementos.begin(), m_annotationMementos.end(), otherCommand->m_annotationMementos.begin(), AnnotationRedoUndoCommand::equalAnnotationMemento)) { return false; } const int32_t numAnnMem = static_cast(m_annotationMementos.size()); for (int32_t i = 0; i < numAnnMem; i++) { CaretAssertVectorIndex(m_annotationMementos, i); CaretAssertVectorIndex(otherCommand->m_annotationMementos, i); delete m_annotationMementos[i]->m_redoAnnotation; m_annotationMementos[i]->m_redoAnnotation = otherCommand->m_annotationMementos[i]->m_redoAnnotation->clone(); } return true; } /** * Set them mode to first coordinate and create the redo/undo instances. * * @param coordinate * New value of the coordinate. * @param annotations * Annotations that receive this new coordinate. */ void AnnotationRedoUndoCommand::setModeCoordinateOne(const AnnotationCoordinate& coordinate, const std::vector& annotations) { m_mode = AnnotationRedoUndoCommandModeEnum::COORDINATE_ONE; setDescription("Coordinate"); for (std::vector::const_iterator iter = annotations.begin(); iter != annotations.end(); iter++) { Annotation* annotation = *iter; CaretAssert(annotation); Annotation* redoAnnotation = annotation->clone(); AnnotationOneDimensionalShape* oneDimShape = dynamic_cast(redoAnnotation); AnnotationTwoDimensionalShape* twoDimShape = dynamic_cast(redoAnnotation); AnnotationCoordinate* redoCoordinate = NULL; if (oneDimShape != NULL) { redoCoordinate = oneDimShape->getStartCoordinate(); } else if (twoDimShape != NULL) { redoCoordinate = twoDimShape->getCoordinate(); } if (redoCoordinate != NULL) { *redoCoordinate = coordinate; Annotation* undoAnnotation = annotation->clone(); AnnotationMemento* am = new AnnotationMemento(annotation, redoAnnotation, undoAnnotation); m_annotationMementos.push_back(am); } else { delete redoAnnotation; } } } /** * Set them mode to second coordinate and create the redo/undo instances. * * @param coordinateOne * New value of the coordinate one. * @param coordinateTwo * New value of the coordinate two. * @param annotations * Annotations that receive this new coordinate. */ void AnnotationRedoUndoCommand::setModeCoordinateOneAndTwo(const AnnotationCoordinate& coordinateOne, const AnnotationCoordinate& coordinateTwo, const std::vector annotations) { m_mode = AnnotationRedoUndoCommandModeEnum::COORDINATE_ONE_AND_TWO; setDescription("Coordinate"); for (std::vector::const_iterator iter = annotations.begin(); iter != annotations.end(); iter++) { Annotation* annotation = *iter; CaretAssert(annotation); Annotation* redoAnnotation = annotation->clone(); AnnotationOneDimensionalShape* oneDimShape = dynamic_cast(redoAnnotation); if (oneDimShape != NULL) { AnnotationCoordinate* redoCoordinateOne = oneDimShape->getStartCoordinate(); CaretAssert(redoCoordinateOne); AnnotationCoordinate* redoCoordinateTwo = oneDimShape->getEndCoordinate(); CaretAssert(redoCoordinateTwo); *redoCoordinateOne = coordinateOne; *redoCoordinateTwo = coordinateTwo; Annotation* undoAnnotation = annotation->clone(); AnnotationMemento* am = new AnnotationMemento(annotation, redoAnnotation, undoAnnotation); m_annotationMementos.push_back(am); } else { delete redoAnnotation; } } } /** * Set them mode to location and size of annotations (coords, size, space, window, tab) * * @param annotationsBeforeMoveAndResize * Annotations before move and resize has been applied. * @param annotationsAfterMoveAndResize * Corresponding annotations after move and resize has been applied. */ void AnnotationRedoUndoCommand::setModeLocationAndSize(const std::vector& annotationsBeforeMoveAndResize, const std::vector& annotationsAfterMoveAndResize) { const int32_t numAnnotations = static_cast(annotationsBeforeMoveAndResize.size()); if (numAnnotations != static_cast(annotationsAfterMoveAndResize.size())) { CaretAssertMessage(0, "Number of annotations must be the same"); CaretLogSevere("Number of annotations must be the same"); return; } m_mode = AnnotationRedoUndoCommandModeEnum::LOCATION_AND_SIZE; if (numAnnotations == 1) { setDescription("Reshape Annotation"); } else { setDescription("Reshape Annotations"); } for (int32_t i = 0; i < numAnnotations; i++) { CaretAssertVectorIndex(annotationsBeforeMoveAndResize, i); CaretAssertVectorIndex(annotationsAfterMoveAndResize, i); Annotation* annBefore = annotationsBeforeMoveAndResize[i]; const Annotation* annAfter = annotationsAfterMoveAndResize[i]; CaretAssert(annBefore); CaretAssert(annAfter); CaretAssert(annBefore->getType() == annAfter->getType()); Annotation* undoAnnotation = annBefore->clone(); Annotation* redoAnnotation = annAfter->clone(); AnnotationMemento* am = new AnnotationMemento(annBefore, redoAnnotation, undoAnnotation); m_annotationMementos.push_back(am); } } /** * Set them mode to second coordinate and create the redo/undo instances. * * @param coordinate * New value of the coordinate. * @param annotations * Annotations that receive this new coordinate. */ void AnnotationRedoUndoCommand::setModeCoordinateTwo(const AnnotationCoordinate& coordinate, const std::vector& annotations) { m_mode = AnnotationRedoUndoCommandModeEnum::COORDINATE_TWO; setDescription("Coordinate"); for (std::vector::const_iterator iter = annotations.begin(); iter != annotations.end(); iter++) { Annotation* annotation = *iter; CaretAssert(annotation); Annotation* redoAnnotation = annotation->clone(); AnnotationOneDimensionalShape* oneDimShape = dynamic_cast(redoAnnotation); AnnotationCoordinate* redoCoordinate = NULL; if (oneDimShape != NULL) { redoCoordinate = oneDimShape->getEndCoordinate(); } if (redoCoordinate != NULL) { *redoCoordinate = coordinate; Annotation* undoAnnotation = annotation->clone(); AnnotationMemento* am = new AnnotationMemento(annotation, redoAnnotation, undoAnnotation); m_annotationMementos.push_back(am); } else { delete redoAnnotation; } } } /** * Set them mode to line arrow start and create the redo/undo instances. * * @param newStatus * New status of line arrow at start of line. * @param annotations * Annotation that receive this new color. */ void AnnotationRedoUndoCommand::setModeLineArrowStart(const bool newStatus, const std::vector& annotations) { m_mode = AnnotationRedoUndoCommandModeEnum::LINE_ARROW_START; setDescription("Line Arrow Start"); for (std::vector::const_iterator iter = annotations.begin(); iter != annotations.end(); iter++) { Annotation* annotation = *iter; CaretAssert(annotation); if (annotation->getType() == AnnotationTypeEnum::LINE) { AnnotationLine* redoAnnotation = dynamic_cast(annotation->clone()); redoAnnotation->setDisplayStartArrow(newStatus); Annotation* undoAnnotation = annotation->clone(); AnnotationMemento* am = new AnnotationMemento(annotation, redoAnnotation, undoAnnotation); m_annotationMementos.push_back(am); } } } /** * Set them mode to line arrow end and create the redo/undo instances. * * @param newStatus * New status of line arrow at end of line. * @param annotations * Annotation that receive this new color. */ void AnnotationRedoUndoCommand::setModeLineArrowEnd(const bool newStatus, const std::vector& annotations) { m_mode = AnnotationRedoUndoCommandModeEnum::LINE_ARROW_END; setDescription("Line Arrow End"); for (std::vector::const_iterator iter = annotations.begin(); iter != annotations.end(); iter++) { Annotation* annotation = *iter; CaretAssert(annotation); if (annotation->getType() == AnnotationTypeEnum::LINE) { AnnotationLine* redoAnnotation = dynamic_cast(annotation->clone()); redoAnnotation->setDisplayEndArrow(newStatus); Annotation* undoAnnotation = annotation->clone(); AnnotationMemento* am = new AnnotationMemento(annotation, redoAnnotation, undoAnnotation); m_annotationMementos.push_back(am); } } } /** * Set them mode to line width and create the redo/undo instances. * * @param newLineWidth * New line width. * @param annotations * Annotation that receive this new line width. */ void AnnotationRedoUndoCommand::setModeLineWidth(const float newLineWidth, const std::vector& annotations) { m_mode = AnnotationRedoUndoCommandModeEnum::LINE_WIDTH_FOREGROUND; setDescription("Line Width"); for (std::vector::const_iterator iter = annotations.begin(); iter != annotations.end(); iter++) { Annotation* annotation = *iter; CaretAssert(annotation); Annotation* redoAnnotation = annotation->clone(); redoAnnotation->setLineWidth(newLineWidth); Annotation* undoAnnotation = annotation->clone(); AnnotationMemento* am = new AnnotationMemento(annotation, redoAnnotation, undoAnnotation); m_annotationMementos.push_back(am); } } /** * Set the mode to color background and create the undo/redo instances. * * @param color * The new background color. * @param customColor * The new custom background color. * @param annotations * Annotation that receive this new color. */ void AnnotationRedoUndoCommand::setModeColorBackground(const CaretColorEnum::Enum color, const float customColor[4], const std::vector& annotations) { m_mode = AnnotationRedoUndoCommandModeEnum::COLOR_BACKGROUND; setDescription("Background Color"); for (std::vector::const_iterator iter = annotations.begin(); iter != annotations.end(); iter++) { Annotation* annotation = *iter; CaretAssert(annotation); Annotation* redoAnnotation = annotation->clone(); redoAnnotation->setBackgroundColor(color); redoAnnotation->setCustomBackgroundColor(customColor); Annotation* undoAnnotation = annotation->clone(); AnnotationMemento* am = new AnnotationMemento(annotation, redoAnnotation, undoAnnotation); m_annotationMementos.push_back(am); } } /** * Set the mode to line color and create the undo/redo instances. * * @param color * The new line color. * @param customColor * The new custom line color. * @param annotations * Annotation that receive this new color. */ void AnnotationRedoUndoCommand::setModeColorLine(const CaretColorEnum::Enum color, const float customColor[4], const std::vector& annotations) { m_mode = AnnotationRedoUndoCommandModeEnum::COLOR_FOREGROUND; setDescription("Line Color"); for (std::vector::const_iterator iter = annotations.begin(); iter != annotations.end(); iter++) { Annotation* annotation = *iter; CaretAssert(annotation); Annotation* redoAnnotation = annotation->clone(); redoAnnotation->setLineColor(color); redoAnnotation->setCustomLineColor(customColor); Annotation* undoAnnotation = annotation->clone(); AnnotationMemento* am = new AnnotationMemento(annotation, redoAnnotation, undoAnnotation); m_annotationMementos.push_back(am); } } /** * Set the mode to add new annotation and create the undo/redo instances. * * @param annotationFile * File to which annotation is pasted. * @param annotations * Annotation that are pasted. */ void AnnotationRedoUndoCommand::setModeCreateAnnotation(AnnotationFile* annotationFile, Annotation* annotation) { m_mode = AnnotationRedoUndoCommandModeEnum::CREATE_ANNOTATION; setDescription("Create 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 cut annotations and create the undo/redo instances. * * @param annotations * Annotation that are cut. */ void AnnotationRedoUndoCommand::setModeCutAnnotations(const std::vector& annotations) { m_mode = AnnotationRedoUndoCommandModeEnum::CUT_ANNOTATION; setDescription("Cut Annotation"); for (std::vector::const_iterator iter = annotations.begin(); iter != annotations.end(); iter++) { Annotation* annotation = *iter; 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(annotation, NULL, NULL); m_annotationMementos.push_back(am); } } /** * Set the mode to delete annotations and create the undo/redo instances. * * @param annotations * Annotation that are deleted. */ void AnnotationRedoUndoCommand::setModeDeleteAnnotations(const std::vector& annotations) { m_mode = AnnotationRedoUndoCommandModeEnum::DELETE_ANNOTATIONS; if (annotations.size() > 1) { setDescription("Delete Annotations"); } else { setDescription("Delete Annotation"); } for (std::vector::const_iterator iter = annotations.begin(); iter != annotations.end(); iter++) { Annotation* annotation = *iter; 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(annotation, NULL, NULL); m_annotationMementos.push_back(am); } } /** * Set the mode to move annotations into a user group. * * @param annotationGroupKey * Group key for source of annotations. * @param annotations * Annotations that will be moved to a user group. */ void AnnotationRedoUndoCommand::setModeGroupingGroupAnnotations(const AnnotationGroupKey& annotationGroupKey, const std::vector& annotations) { m_mode = AnnotationRedoUndoCommandModeEnum::GROUPING_GROUP; setDescription("Group Annotations"); m_annotationGroupMemento = new AnnotationGroupMemento(annotationGroupKey, annotations); } /** * Set the mode to remove annotations from a user group. * * @param annotationGroupKey * Group key of user group containing annotations. */ void AnnotationRedoUndoCommand::setModeGroupingUngroupAnnotations(const AnnotationGroupKey& annotationGroupKey) { m_mode = AnnotationRedoUndoCommandModeEnum::GROUPING_UNGROUP; setDescription("Ungroup Annotations"); std::vector emptyAnnotations; m_annotationGroupMemento = new AnnotationGroupMemento(annotationGroupKey, emptyAnnotations); } /** * Set the mode to move annotations back to a previous user group. * * @param annotationGroupKey * Group key of previous user group. */ void AnnotationRedoUndoCommand::setModeGroupingRegroupAnnotations(const AnnotationGroupKey& annotationGroupKey) { m_mode = AnnotationRedoUndoCommandModeEnum::GROUPING_REGROUP; setDescription("Regroup Annotations"); std::vector emptyAnnotations; m_annotationGroupMemento = new AnnotationGroupMemento(annotationGroupKey, emptyAnnotations); } /** * Set the mode to paste annotations and create the undo/redo instances. * * @param annotationFile * File to which annotation is pasted. * @param annotations * Annotation that are pasted. */ void AnnotationRedoUndoCommand::setModePasteAnnotation(AnnotationFile* annotationFile, Annotation* annotation) { m_mode = AnnotationRedoUndoCommandModeEnum::PASTE_ANNOTATION; setDescription("Paste 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 * * @param newRotationAngle * The new rotation angle * @param annotations * Annotations that receive this new rotation angle */ void AnnotationRedoUndoCommand::setModeRotationAngle(const float newRotationAngle, const std::vector& annotations) { m_mode = AnnotationRedoUndoCommandModeEnum::ROTATION_ANGLE; setDescription("Rotation Angle"); for (std::vector::const_iterator iter = annotations.begin(); iter != annotations.end(); iter++) { Annotation* annotation = *iter; CaretAssert(annotation); AnnotationTwoDimensionalShape* twoDimAnn = dynamic_cast(annotation); AnnotationOneDimensionalShape* oneDimAnn = dynamic_cast(annotation); if (twoDimAnn != NULL) { AnnotationTwoDimensionalShape* redoAnnotation = dynamic_cast(annotation->clone()); CaretAssert(redoAnnotation); redoAnnotation->setRotationAngle(newRotationAngle); Annotation* undoAnnotation = annotation->clone(); AnnotationMemento* am = new AnnotationMemento(annotation, redoAnnotation, undoAnnotation); m_annotationMementos.push_back(am); } else if (oneDimAnn != NULL) { int32_t viewport[4] = { 0, 0, 0, 0 }; bool viewportValid = 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); viewportValid = 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); viewportValid = true; } } break; } if (viewportValid) { const float vpWidth = viewport[2]; const float vpHeight = viewport[3]; float annOneX = 0.0; float annOneY = 0.0; float annTwoX = 0.0; float annTwoY = 0.0; oneDimAnn->getStartCoordinate()->getViewportXY(vpWidth, vpHeight, annOneX, annOneY); oneDimAnn->getEndCoordinate()->getViewportXY(vpWidth, vpHeight, annTwoX, annTwoY); 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, undoAnnotation); m_annotationMementos.push_back(am); } else { 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 angleRadians = MathFunctions::toRadians(-newRotationAngle); const float dy = length * std::sin(angleRadians); const float dx = length * std::cos(angleRadians); annTwoX = annOneX + dx; annTwoY = annOneY + dy; AnnotationOneDimensionalShape* redoAnnotation = dynamic_cast(annotation->clone()); CaretAssert(redoAnnotation); redoAnnotation->getEndCoordinate()->setXYZFromViewportXYZ(vpWidth, vpHeight, annTwoX, annTwoY); Annotation* undoAnnotation = annotation->clone(); AnnotationMemento* am = new AnnotationMemento(annotation, redoAnnotation, undoAnnotation); 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); } } } /** * Set the mode to horizontal text alignment and create the undo/redo instances * * @param newHorizontalAlignment * The new horizontal alignment * @param annotations * Annotation that receive this new text. */ void AnnotationRedoUndoCommand::setModeTextAlignmentHorizontal(const AnnotationTextAlignHorizontalEnum::Enum newHorizontalAlignment, const std::vector& annotations) { m_mode = AnnotationRedoUndoCommandModeEnum::TEXT_ALIGNMENT_HORIZONTAL; setDescription("Text Horizontal Alignment"); for (std::vector::const_iterator iter = annotations.begin(); iter != annotations.end(); iter++) { Annotation* annotation = *iter; CaretAssert(annotation); if (annotation->getType() == AnnotationTypeEnum::TEXT) { AnnotationText* redoAnnotation = dynamic_cast(annotation->clone()); CaretAssert(redoAnnotation); redoAnnotation->setHorizontalAlignment(newHorizontalAlignment); Annotation* undoAnnotation = annotation->clone(); AnnotationMemento* am = new AnnotationMemento(annotation, redoAnnotation, undoAnnotation); m_annotationMementos.push_back(am); } } } /** * Set the mode to vertical text alignment and create the undo/redo instances * * @param newVerticalAlignment * The new vertical alignment * @param annotations * Annotation that receive this new text. */ void AnnotationRedoUndoCommand::setModeTextAlignmentVertical(const AnnotationTextAlignVerticalEnum::Enum newVerticalAlignment, const std::vector& annotations) { m_mode = AnnotationRedoUndoCommandModeEnum::TEXT_ALIGNMENT_VERTICAL; setDescription("Text Vertical Alignment"); for (std::vector::const_iterator iter = annotations.begin(); iter != annotations.end(); iter++) { Annotation* annotation = *iter; CaretAssert(annotation); if (annotation->getType() == AnnotationTypeEnum::TEXT) { AnnotationText* redoAnnotation = dynamic_cast(annotation->clone()); CaretAssert(redoAnnotation); redoAnnotation->setVerticalAlignment(newVerticalAlignment); Annotation* undoAnnotation = annotation->clone(); AnnotationMemento* am = new AnnotationMemento(annotation, redoAnnotation, undoAnnotation); m_annotationMementos.push_back(am); } } } /** * Set the mode to text characters and create the undo/redo instances. * * @param text * The text . * @param annotations * Annotation that receive this new text. */ void AnnotationRedoUndoCommand::setModeTextCharacters(const AString& text, const std::vector& annotations) { m_mode = AnnotationRedoUndoCommandModeEnum::TEXT_CHARACTERS; setDescription("Text Characters"); for (std::vector::const_iterator iter = annotations.begin(); iter != annotations.end(); iter++) { Annotation* annotation = *iter; CaretAssert(annotation); if (annotation->getType() == AnnotationTypeEnum::TEXT) { AnnotationText* redoAnnotation = dynamic_cast(annotation->clone()); CaretAssert(redoAnnotation); redoAnnotation->setText(text); Annotation* undoAnnotation = annotation->clone(); AnnotationMemento* am = new AnnotationMemento(annotation, redoAnnotation, undoAnnotation); m_annotationMementos.push_back(am); } } } /** * Set the mode to text color and create the undo/redo instances. * * @param color * The color enum. * @param customColor * The custom color components. * @param annotations * Annotation that receive this new color. */ void AnnotationRedoUndoCommand::setModeTextColor(const CaretColorEnum::Enum color, const float customColor[4], const std::vector& annotations) { m_mode = AnnotationRedoUndoCommandModeEnum::TEXT_COLOR; setDescription("Text Color"); for (std::vector::const_iterator iter = annotations.begin(); iter != annotations.end(); iter++) { Annotation* annotation = *iter; CaretAssert(annotation); AnnotationFontAttributesInterface* fontStyleAnn = dynamic_cast(annotation); if (fontStyleAnn != NULL) { Annotation* redoAnnotation = annotation->clone(); AnnotationFontAttributesInterface* redoAnnotationFontStyle = dynamic_cast(redoAnnotation); CaretAssert(redoAnnotationFontStyle); redoAnnotationFontStyle->setTextColor(color); redoAnnotationFontStyle->setCustomTextColor(customColor); Annotation* undoAnnotation = annotation->clone(); AnnotationMemento* am = new AnnotationMemento(annotation, redoAnnotation, undoAnnotation); m_annotationMementos.push_back(am); } } } /** * Set the mode to text connect to brainordinate and create the undo/redo instances. * * @param newConnectType * New value for connect to brainordinate. * @param annotations * Annotation that receive this new status. */ void AnnotationRedoUndoCommand::setModeTextConnectToBrainordinate(const AnnotationTextConnectTypeEnum::Enum newConnectType, const std::vector& annotations) { m_mode = AnnotationRedoUndoCommandModeEnum::TEXT_CONNECT_TO_BRAINORDINATE; setDescription("Text Connect to Brainordinate"); for (std::vector::const_iterator iter = annotations.begin(); iter != annotations.end(); iter++) { Annotation* annotation = *iter; CaretAssert(annotation); if (annotation->getType() == AnnotationTypeEnum::TEXT) { AnnotationText* redoAnnotation = dynamic_cast(annotation->clone()); CaretAssert(redoAnnotation); redoAnnotation->setConnectToBrainordinate(newConnectType); Annotation* undoAnnotation = annotation->clone(); AnnotationMemento* am = new AnnotationMemento(annotation, redoAnnotation, undoAnnotation); m_annotationMementos.push_back(am); } } } /** * Set the mode to text font bold and create the undo/redo instances. * * @param newStatus * New status for font bold. * @param annotations * Annotation that receive this new status. */ void AnnotationRedoUndoCommand::setModeTextFontBold(const bool newStatus, const std::vector& annotations) { m_mode = AnnotationRedoUndoCommandModeEnum::TEXT_FONT_BOLD; setDescription("Text Bold"); for (std::vector::const_iterator iter = annotations.begin(); iter != annotations.end(); iter++) { Annotation* annotation = *iter; CaretAssert(annotation); AnnotationFontAttributesInterface* fontStyleAnn = dynamic_cast(annotation); if (fontStyleAnn != NULL) { Annotation* redoAnnotation = annotation->clone(); AnnotationFontAttributesInterface* redoAnnotationFontStyle = dynamic_cast(redoAnnotation); CaretAssert(redoAnnotationFontStyle); redoAnnotationFontStyle->setBoldStyleEnabled(newStatus); Annotation* undoAnnotation = annotation->clone(); AnnotationMemento* am = new AnnotationMemento(annotation, redoAnnotation, undoAnnotation); m_annotationMementos.push_back(am); } } } /** * Set the mode to text font italic and create the undo/redo instances. * * @param newStatus * New status for font italic. * @param annotations * Annotation that receive this new status. */ void AnnotationRedoUndoCommand::setModeTextFontItalic(const bool newStatus, const std::vector& annotations) { m_mode = AnnotationRedoUndoCommandModeEnum::TEXT_FONT_ITALIC; setDescription("Text Italic"); for (std::vector::const_iterator iter = annotations.begin(); iter != annotations.end(); iter++) { Annotation* annotation = *iter; CaretAssert(annotation); AnnotationFontAttributesInterface* fontStyleAnn = dynamic_cast(annotation); if (fontStyleAnn != NULL) { Annotation* redoAnnotation = annotation->clone(); AnnotationFontAttributesInterface* redoAnnotationFontStyle = dynamic_cast(redoAnnotation); CaretAssert(redoAnnotationFontStyle); redoAnnotationFontStyle->setItalicStyleEnabled(newStatus); Annotation* undoAnnotation = annotation->clone(); AnnotationMemento* am = new AnnotationMemento(annotation, redoAnnotation, undoAnnotation); m_annotationMementos.push_back(am); } } } /** * Set the mode to font name and create the undo/redo instances. * * @param newFontName * New font name * @param annotations * Annotation that receive this font name */ void AnnotationRedoUndoCommand::setModeTextFontName(const AnnotationTextFontNameEnum::Enum newFontName, const std::vector& annotations) { m_mode = AnnotationRedoUndoCommandModeEnum::TEXT_FONT_NAME; setDescription("Text Font Name"); for (std::vector::const_iterator iter = annotations.begin(); iter != annotations.end(); iter++) { Annotation* annotation = *iter; CaretAssert(annotation); AnnotationFontAttributesInterface* fontStyleAnn = dynamic_cast(annotation); if (fontStyleAnn != NULL) { Annotation* redoAnnotation = annotation->clone(); AnnotationFontAttributesInterface* redoAnnotationFontStyle = dynamic_cast(redoAnnotation); CaretAssert(redoAnnotationFontStyle); redoAnnotationFontStyle->setFont(newFontName); Annotation* undoAnnotation = annotation->clone(); AnnotationMemento* am = new AnnotationMemento(annotation, redoAnnotation, undoAnnotation); m_annotationMementos.push_back(am); } } } /** * Set the mode to font size and create the undo/redo instances. * * @param newFontPointSize * New font point size * @param annotations * Annotation that receive this font size */ void AnnotationRedoUndoCommand::setModeTextFontPointSize(const AnnotationTextFontPointSizeEnum::Enum newFontPointSize, const std::vector& annotations) { m_mode = AnnotationRedoUndoCommandModeEnum::TEXT_FONT_POINT_SIZE; setDescription("Text Font Point Size"); for (std::vector::const_iterator iter = annotations.begin(); iter != annotations.end(); iter++) { Annotation* annotation = *iter; CaretAssert(annotation); if (annotation->getType() == AnnotationTypeEnum::TEXT) { AnnotationPointSizeText* redoAnnotation = dynamic_cast(annotation->clone()); CaretAssert(redoAnnotation); redoAnnotation->setFontPointSize(newFontPointSize); Annotation* undoAnnotation = annotation->clone(); AnnotationMemento* am = new AnnotationMemento(annotation, redoAnnotation, undoAnnotation); m_annotationMementos.push_back(am); } } } /** * Set the mode to font percent size and create the undo/redo instances. * * @param newFontPercentSize * New font percentage size * @param annotations * Annotation that receive this font size */ void AnnotationRedoUndoCommand::setModeTextFontPercentSize(const float newFontPercentSize, const std::vector& annotations, const float surfaceSpaceRowCount) { 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); AnnotationFontAttributesInterface* fontStyleAnn = dynamic_cast(annotation); if (fontStyleAnn != NULL) { Annotation* redoAnnotation = annotation->clone(); AnnotationFontAttributesInterface* redoAnnotationFontStyle = dynamic_cast(redoAnnotation); CaretAssert(redoAnnotationFontStyle); float percentSize = newFontPercentSize; switch (redoAnnotation->getCoordinateSpace()) { case AnnotationCoordinateSpaceEnum::PIXELS: break; case AnnotationCoordinateSpaceEnum::STEREOTAXIC: percentSize *= surfaceSpaceRowCount; break; case AnnotationCoordinateSpaceEnum::SURFACE: percentSize *= surfaceSpaceRowCount; break; case AnnotationCoordinateSpaceEnum::TAB: break; case AnnotationCoordinateSpaceEnum::WINDOW: break; } redoAnnotationFontStyle->setFontPercentViewportSize(percentSize); Annotation* undoAnnotation = annotation->clone(); AnnotationMemento* am = new AnnotationMemento(annotation, redoAnnotation, undoAnnotation); m_annotationMementos.push_back(am); } } } //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. * * @param newStatus * New status for font undeline. * @param annotations * Annotation that receive this new status. */ void AnnotationRedoUndoCommand::setModeTextFontUnderline(const bool newStatus, const std::vector& annotations) { m_mode = AnnotationRedoUndoCommandModeEnum::TEXT_FONT_UNDERLINE; setDescription("Text Underline"); for (std::vector::const_iterator iter = annotations.begin(); iter != annotations.end(); iter++) { Annotation* annotation = *iter; CaretAssert(annotation); AnnotationFontAttributesInterface* fontStyleAnn = dynamic_cast(annotation); if (fontStyleAnn != NULL) { Annotation* redoAnnotation = annotation->clone(); AnnotationFontAttributesInterface* redoAnnotationFontStyle = dynamic_cast(redoAnnotation); CaretAssert(redoAnnotationFontStyle); redoAnnotationFontStyle->setUnderlineStyleEnabled(newStatus); Annotation* undoAnnotation = annotation->clone(); AnnotationMemento* am = new AnnotationMemento(annotation, redoAnnotation, undoAnnotation); m_annotationMementos.push_back(am); } } } /** * Set the mode to vertical text alignment and create the undo/redo instances * * @param newVerticalAlignment * The new vertical alignment * @param annotations * Annotation that receive this new text. */ void AnnotationRedoUndoCommand::setModeTextOrientation(const AnnotationTextOrientationEnum::Enum newTextOrientation, const std::vector& annotations) { m_mode = AnnotationRedoUndoCommandModeEnum::TEXT_ORIENTATION; setDescription("Text Orientation"); for (std::vector::const_iterator iter = annotations.begin(); iter != annotations.end(); iter++) { Annotation* annotation = *iter; CaretAssert(annotation); if (annotation->getType() == AnnotationTypeEnum::TEXT) { AnnotationText* redoAnnotation = dynamic_cast(annotation->clone()); CaretAssert(redoAnnotation); redoAnnotation->setOrientation(newTextOrientation); Annotation* undoAnnotation = annotation->clone(); AnnotationMemento* am = new AnnotationMemento(annotation, redoAnnotation, undoAnnotation); m_annotationMementos.push_back(am); } } } /** * Set the mode to two dim height and create the undo/redo instances * * @param newHeight * The new height * @param annotations * Annotation that receive this new text. */ void AnnotationRedoUndoCommand::setModeTwoDimHeight(const float newHeight, const std::vector& annotations) { m_mode = AnnotationRedoUndoCommandModeEnum::TWO_DIM_HEIGHT; setDescription("Height"); for (std::vector::const_iterator iter = annotations.begin(); iter != annotations.end(); iter++) { Annotation* annotation = *iter; CaretAssert(annotation); AnnotationTwoDimensionalShape* twoDimAnn = dynamic_cast(annotation); if (twoDimAnn != NULL) { AnnotationTwoDimensionalShape* redoAnnotation = dynamic_cast(annotation->clone()); CaretAssert(redoAnnotation); redoAnnotation->setHeight(newHeight); Annotation* undoAnnotation = annotation->clone(); AnnotationMemento* am = new AnnotationMemento(annotation, redoAnnotation, undoAnnotation); m_annotationMementos.push_back(am); } } } /** * Set the mode to two dim width and create the undo/redo instances * * @param newWidth * The new vertical alignment * @param annotations * Annotation that receive this new text. */ void AnnotationRedoUndoCommand::setModeTwoDimWidth(const float newWidth, const std::vector& annotations) { m_mode = AnnotationRedoUndoCommandModeEnum::TWO_DIM_WIDTH; setDescription("Width"); for (std::vector::const_iterator iter = annotations.begin(); iter != annotations.end(); iter++) { Annotation* annotation = *iter; CaretAssert(annotation); AnnotationTwoDimensionalShape* twoDimAnn = dynamic_cast(annotation); if (twoDimAnn != NULL) { AnnotationTwoDimensionalShape* redoAnnotation = dynamic_cast(annotation->clone()); CaretAssert(redoAnnotation); redoAnnotation->setWidth(newWidth); Annotation* undoAnnotation = annotation->clone(); AnnotationMemento* am = new AnnotationMemento(annotation, redoAnnotation, undoAnnotation); m_annotationMementos.push_back(am); } } } /** * Compare the two annotation mementos using the annotation pointer. * * @param am1 * Left side for comparison. * @param am2 * Right side for comparison. * @return * True if left side's annotation pointer is equal to the right side's annotation pointer. */ bool AnnotationRedoUndoCommand::equalAnnotationMemento(const AnnotationMemento* am1, const AnnotationMemento* am2) { CaretAssert(am1); CaretAssert(am2); return (am1->m_annotation == am2->m_annotation); } /** * Compare the two annotation mementos using the annotation pointer. * * @param am1 * Left side for comparison. * @param am2 * Right side for comparison. * @return * True if left side's annotation pointer is less than right side's annotation pointer. */ bool AnnotationRedoUndoCommand::lessThanAnnotationMemento(const AnnotationMemento* am1, const AnnotationMemento* am2) { CaretAssert(am1); CaretAssert(am2); return (am1->m_annotation < am2->m_annotation); } /** * Sort the annotation mementos in this redo/undo command. * The mergeWith() method needs to compare the mementos * in two command and when the mementos are sorted, * it allows faster comparison. */ void AnnotationRedoUndoCommand::sortAnnotationMementos() const { if (m_sortedFlag) { return; } std::sort(m_annotationMementos.begin(), m_annotationMementos.end(), AnnotationRedoUndoCommand::lessThanAnnotationMemento); m_sortedFlag = true; } connectome-workbench-1.2.3+git41-gc4c6c90/src/Annotations/AnnotationRedoUndoCommand.h000066400000000000000000000254101300200146000302650ustar00rootroot00000000000000#ifndef __ANNOTATION_UNDO_COMMAND_H__ #define __ANNOTATION_UNDO_COMMAND_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 "AnnotationCoordinate.h" #include "AnnotationRedoUndoCommandModeEnum.h" #include "AnnotationText.h" #include "CaretColorEnum.h" #include "CaretUndoCommand.h" namespace caret { class AnnotationFile; class Annotation; class AnnotationRedoUndoCommand : public CaretUndoCommand { public: AnnotationRedoUndoCommand(); virtual ~AnnotationRedoUndoCommand(); virtual bool redo(AString& errorMessageOut); virtual bool undo(AString& errorMessageOut); bool isValid() const; virtual bool mergeWith(const CaretUndoCommand* command); void setModeCoordinateOne(const AnnotationCoordinate& coordinate, const std::vector& annotations); void setModeCoordinateOneAndTwo(const AnnotationCoordinate& coordinateOne, const AnnotationCoordinate& coordinateTwo, const std::vector annotations); void setModeCoordinateTwo(const AnnotationCoordinate& coordinate, const std::vector& annotations); void setModeLineArrowStart(const bool newStatus, const std::vector& annotations); void setModeLineArrowEnd(const bool newStatus, const std::vector& annotations); void setModeLineWidth(const float newLineWidth, const std::vector& annotations); void setModeColorBackground(const CaretColorEnum::Enum color, const float customColor[4], const std::vector& annotations); void setModeColorLine(const CaretColorEnum::Enum color, const float customColor[4], const std::vector& annotations); void setModeCreateAnnotation(AnnotationFile* annotationFile, Annotation* annotation); void setModeCutAnnotations(const std::vector& annotations); void setModeDeleteAnnotations(const std::vector& annotations); void setModeGroupingGroupAnnotations(const AnnotationGroupKey& annotationGroupKey, const std::vector& annotations); void setModeGroupingUngroupAnnotations(const AnnotationGroupKey& annotationGroupKey); void setModeGroupingRegroupAnnotations(const AnnotationGroupKey& annotationGroupKey); void setModeLocationAndSize(const std::vector& annotationsBeforeMoveAndResize, const std::vector& annotationsAfterMoveAndResize); void setModePasteAnnotation(AnnotationFile* annotationFile, Annotation* annotation); void setModeRotationAngle(const float newRotationAngle, const std::vector& annotations); void setModeTextAlignmentHorizontal(const AnnotationTextAlignHorizontalEnum::Enum newHorizontalAlignment, const std::vector& annotations); void setModeTextAlignmentVertical(const AnnotationTextAlignVerticalEnum::Enum newVerticalAlignment, const std::vector& annotations); void setModeTextCharacters(const AString& text, const std::vector& annotations); void setModeTextColor(const CaretColorEnum::Enum color, const float customColor[4], const std::vector& annotations); void setModeTextConnectToBrainordinate(const AnnotationTextConnectTypeEnum::Enum newConnectType, const std::vector& annotations); void setModeTextFontBold(const bool newStatus, const std::vector& annotations); void setModeTextFontItalic(const bool newStatus, const std::vector& annotations); void setModeTextFontName(const AnnotationTextFontNameEnum::Enum newFontName, const std::vector& annotations); void setModeTextFontPointSize(const AnnotationTextFontPointSizeEnum::Enum newFontPointSize, const std::vector& annotations); void setModeTextFontPercentSize(const float newFontPercentSize, const std::vector& annotation, const float surfaceSpaceRowCount); void setModeTextFontUnderline(const bool newStatus, const std::vector& annotations); void setModeTextFontOutline(const bool newStatus, const std::vector& annotations); void setModeTextOrientation(const AnnotationTextOrientationEnum::Enum newTextOrientation, const std::vector& annotations); void setModeTwoDimHeight(const float newHeight, const std::vector& annotations); void setModeTwoDimWidth(const float newWidth, const std::vector& annotations); // ADD_NEW_METHODS_HERE private: /** * The annotation memento contains copies of the * annotation before and after its modification. */ class AnnotationMemento { public: AnnotationMemento(Annotation* annotation, Annotation* redoAnnotation, Annotation* undoAnnotation) { m_annotationFile = NULL; m_annotation = annotation; m_redoAnnotation = redoAnnotation; m_undoAnnotation = undoAnnotation; } AnnotationMemento(AnnotationFile* annotationFile, Annotation* annotation, Annotation* redoAnnotation, Annotation* undoAnnotation) { m_annotationFile = annotationFile; m_annotation = annotation; m_redoAnnotation = redoAnnotation; m_undoAnnotation = undoAnnotation; } ~AnnotationMemento() { if (m_redoAnnotation != NULL) { delete m_redoAnnotation; } if (m_undoAnnotation != NULL) { delete m_undoAnnotation; } } bool operator=(const AnnotationMemento& am) const { return (m_annotation < am.m_annotation); } AnnotationFile* m_annotationFile; Annotation* m_annotation; Annotation* m_undoAnnotation; Annotation* m_redoAnnotation; }; class AnnotationGroupMemento { public: AnnotationGroupMemento(const AnnotationGroupKey& annotationGroupKey) { m_windowIndex = -1; m_annotationGroupKey = annotationGroupKey; } AnnotationGroupMemento(const AnnotationGroupKey& annotationGroupKey, const std::vector& annotations) { m_windowIndex = -1; m_annotationGroupKey = annotationGroupKey; m_annotations = annotations; } void setUndoAnnotationGroupKey(const AnnotationGroupKey& undoAnnotationGroupKey) { m_undoAnnotationGroupKey = undoAnnotationGroupKey; } bool isValid() const { return (m_annotationGroupKey.getGroupType() != AnnotationGroupTypeEnum::INVALID); } AnnotationGroupKey m_annotationGroupKey; std::vector m_annotations; AnnotationGroupKey m_undoAnnotationGroupKey; int32_t m_windowIndex; }; AnnotationRedoUndoCommand(const AnnotationRedoUndoCommand&); AnnotationRedoUndoCommand& operator=(const AnnotationRedoUndoCommand&); void applyRedoOrUndo(Annotation* annotation, const Annotation* annotationValue) const; void sortAnnotationMementos() const; static bool equalAnnotationMemento(const AnnotationMemento* am1, const AnnotationMemento* am2); static bool lessThanAnnotationMemento(const AnnotationMemento* am1, const AnnotationMemento* am2); AnnotationRedoUndoCommandModeEnum::Enum m_mode; mutable std::vector m_annotationMementos; mutable AnnotationGroupMemento* m_annotationGroupMemento; mutable bool m_sortedFlag; // ADD_NEW_MEMBERS_HERE }; #ifdef __ANNOTATION_UNDO_COMMAND_DECLARE__ // #endif // __ANNOTATION_UNDO_COMMAND_DECLARE__ } // namespace #endif //__ANNOTATION_UNDO_COMMAND_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Annotations/AnnotationRedoUndoCommandModeEnum.cxx000066400000000000000000000430701300200146000322740ustar00rootroot00000000000000 /*LICENSE_START*/ /* * Copyright (C) 2015 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 __ANNOTATION_UNDO_COMMAND_MODE_ENUM_DECLARE__ #include "AnnotationRedoUndoCommandModeEnum.h" #undef __ANNOTATION_UNDO_COMMAND_MODE_ENUM_DECLARE__ #include "CaretAssert.h" using namespace caret; /** * \class caret::AnnotationRedoUndoCommandModeEnum * \brief Mode (type of modification) for the annotation undo command * * 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_AnnotationRedoUndoCommandModeEnumComboBox; * * Declare a slot that is called when user changes selection * private slots: * void AnnotationRedoUndoCommandModeEnumComboBoxItemActivated(); * * Implementation File (.cxx) * Include the header files * #include "EnumComboBoxTemplate.h" * #include "AnnotationRedoUndoCommandModeEnum.h" * * Instatiate: * m_AnnotationRedoUndoCommandModeEnumComboBox = new EnumComboBoxTemplate(this); * m_AnnotationRedoUndoCommandModeEnumComboBox->setup(); * * Get notified when the user changes the selection: * QObject::connect(m_AnnotationRedoUndoCommandModeEnumComboBox, SIGNAL(itemActivated()), * this, SLOT(AnnotationRedoUndoCommandModeEnumComboBoxItemActivated())); * * Update the selection: * m_AnnotationRedoUndoCommandModeEnumComboBox->setSelectedItem(NEW_VALUE); * * Read the selection: * const AnnotationRedoUndoCommandModeEnum::Enum VARIABLE = m_AnnotationRedoUndoCommandModeEnumComboBox->getSelectedItem(); * */ /** * Constructor. * * @param enumValue * An enumerated value. * @param name * Name of enumerated value. * * @param guiName * User-friendly name for use in user-interface. */ AnnotationRedoUndoCommandModeEnum::AnnotationRedoUndoCommandModeEnum(const Enum enumValue, const AString& name, const AString& guiName) { this->enumValue = enumValue; this->integerCode = integerCodeCounter++; this->name = name; this->guiName = guiName; } /** * Destructor. */ AnnotationRedoUndoCommandModeEnum::~AnnotationRedoUndoCommandModeEnum() { } /** * Initialize the enumerated metadata. */ void AnnotationRedoUndoCommandModeEnum::initialize() { if (initializedFlag) { return; } initializedFlag = true; enumData.push_back(AnnotationRedoUndoCommandModeEnum(INVALID, "INVALID", "Invalid")); enumData.push_back(AnnotationRedoUndoCommandModeEnum(COLOR_BACKGROUND, "COLOR_BACKGROUND", "Color - Background")); enumData.push_back(AnnotationRedoUndoCommandModeEnum(COLOR_FOREGROUND, "COLOR_FOREGROUND", "Color - Foreground")); enumData.push_back(AnnotationRedoUndoCommandModeEnum(COORDINATE_ONE, "COORDINATE_ONE", "Coordinate One")); enumData.push_back(AnnotationRedoUndoCommandModeEnum(COORDINATE_TWO, "COORDINATE_TWO", "Coordinate Two")); enumData.push_back(AnnotationRedoUndoCommandModeEnum(CREATE_ANNOTATION, "CREATE_ANNOTATION", "Create Annotation")); enumData.push_back(AnnotationRedoUndoCommandModeEnum(CUT_ANNOTATION, "CUT_ANNOTATION", "Cut Annotations")); enumData.push_back(AnnotationRedoUndoCommandModeEnum(DELETE_ANNOTATIONS, "DELETE_ANNOTATIONS", "Delete Annotations")); enumData.push_back(AnnotationRedoUndoCommandModeEnum(GROUPING_GROUP, "GROUPING_GROUP", "Group Annotations")); enumData.push_back(AnnotationRedoUndoCommandModeEnum(GROUPING_REGROUP, "GROUPING_REGROUP", "Regroup Annotations")); enumData.push_back(AnnotationRedoUndoCommandModeEnum(GROUPING_UNGROUP, "GROUPING_UNGROUP", "Ungroup Annotations")); enumData.push_back(AnnotationRedoUndoCommandModeEnum(LINE_ARROW_START, "LINE_ARROW_START", "Line Arrow Start")); enumData.push_back(AnnotationRedoUndoCommandModeEnum(LINE_ARROW_END, "LINE_ARROW_END", "Line Arrow End")); enumData.push_back(AnnotationRedoUndoCommandModeEnum(LINE_WIDTH_FOREGROUND, "LINE_WIDTH_FOREGROUND", "Line Width - Foreground")); enumData.push_back(AnnotationRedoUndoCommandModeEnum(LOCATION_AND_SIZE, "LOCATION_AND_SIZE", "location and size of annotations (coords, size, space, window, tab)")); enumData.push_back(AnnotationRedoUndoCommandModeEnum(PASTE_ANNOTATION, "PASTE_ANNOTATION", "Paste Annotation")); enumData.push_back(AnnotationRedoUndoCommandModeEnum(ROTATION_ANGLE, "ROTATION_ANGLE", "Rotation Angle")); enumData.push_back(AnnotationRedoUndoCommandModeEnum(TEXT_ALIGNMENT_HORIZONTAL, "TEXT_ALIGNMENT_HORIZONTAL", "Text Alignment Horizontal")); enumData.push_back(AnnotationRedoUndoCommandModeEnum(TEXT_ALIGNMENT_VERTICAL, "TEXT_ALIGNMENT_VERTICAL", "Text Alignment Vertical")); enumData.push_back(AnnotationRedoUndoCommandModeEnum(TEXT_CHARACTERS, "TEXT_CHARACTERS", "Text Characters")); enumData.push_back(AnnotationRedoUndoCommandModeEnum(TEXT_COLOR, "TEXT_COLOR", "Text Color")); enumData.push_back(AnnotationRedoUndoCommandModeEnum(TEXT_CONNECT_TO_BRAINORDINATE, "TEXT_CONNECT_TO_BRAINORDINATE", "Text Connect to Brainordinate")); enumData.push_back(AnnotationRedoUndoCommandModeEnum(TEXT_FONT_BOLD, "TEXT_FONT_BOLD", "Text Font Bold")); enumData.push_back(AnnotationRedoUndoCommandModeEnum(TEXT_FONT_ITALIC, "TEXT_FONT_ITALIC", "Text Font Italic")); enumData.push_back(AnnotationRedoUndoCommandModeEnum(TEXT_FONT_NAME, "TEXT_FONT_NAME", "Text Font Name")); enumData.push_back(AnnotationRedoUndoCommandModeEnum(TEXT_FONT_PERCENT_SIZE, "TEXT_FONT_PERCENT_SIZE", "Text Font Percent Size")); enumData.push_back(AnnotationRedoUndoCommandModeEnum(TEXT_FONT_POINT_SIZE, "TEXT_FONT_POINT_SIZE", "Text Font Point Size")); enumData.push_back(AnnotationRedoUndoCommandModeEnum(TEXT_FONT_UNDERLINE, "TEXT_FONT_UNDERLINE", "Text Font Undeline")); enumData.push_back(AnnotationRedoUndoCommandModeEnum(TEXT_ORIENTATION, "TEXT_ORIENTATION", "Text Orientation")); } /** * 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 AnnotationRedoUndoCommandModeEnum* AnnotationRedoUndoCommandModeEnum::findData(const Enum enumValue) { if (initializedFlag == false) initialize(); size_t num = enumData.size(); for (size_t i = 0; i < num; i++) { const AnnotationRedoUndoCommandModeEnum* 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 AnnotationRedoUndoCommandModeEnum::toName(Enum enumValue) { if (initializedFlag == false) initialize(); const AnnotationRedoUndoCommandModeEnum* 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. */ AnnotationRedoUndoCommandModeEnum::Enum AnnotationRedoUndoCommandModeEnum::fromName(const AString& name, bool* isValidOut) { if (initializedFlag == false) initialize(); bool validFlag = false; Enum enumValue = AnnotationRedoUndoCommandModeEnum::enumData[0].enumValue; for (std::vector::iterator iter = enumData.begin(); iter != enumData.end(); iter++) { const AnnotationRedoUndoCommandModeEnum& 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 AnnotationRedoUndoCommandModeEnum")); } return enumValue; } /** * Get a GUI string representation of the enumerated type. * @param enumValue * Enumerated value. * @return * String representing enumerated value. */ AString AnnotationRedoUndoCommandModeEnum::toGuiName(Enum enumValue) { if (initializedFlag == false) initialize(); const AnnotationRedoUndoCommandModeEnum* 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. */ AnnotationRedoUndoCommandModeEnum::Enum AnnotationRedoUndoCommandModeEnum::fromGuiName(const AString& guiName, bool* isValidOut) { if (initializedFlag == false) initialize(); bool validFlag = false; Enum enumValue = AnnotationRedoUndoCommandModeEnum::enumData[0].enumValue; for (std::vector::iterator iter = enumData.begin(); iter != enumData.end(); iter++) { const AnnotationRedoUndoCommandModeEnum& 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 AnnotationRedoUndoCommandModeEnum")); } return enumValue; } /** * Get the integer code for a data type. * * @return * Integer code for data type. */ int32_t AnnotationRedoUndoCommandModeEnum::toIntegerCode(Enum enumValue) { if (initializedFlag == false) initialize(); const AnnotationRedoUndoCommandModeEnum* 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. */ AnnotationRedoUndoCommandModeEnum::Enum AnnotationRedoUndoCommandModeEnum::fromIntegerCode(const int32_t integerCode, bool* isValidOut) { if (initializedFlag == false) initialize(); bool validFlag = false; Enum enumValue = AnnotationRedoUndoCommandModeEnum::enumData[0].enumValue; for (std::vector::iterator iter = enumData.begin(); iter != enumData.end(); iter++) { const AnnotationRedoUndoCommandModeEnum& 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 AnnotationRedoUndoCommandModeEnum")); } 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 AnnotationRedoUndoCommandModeEnum::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 AnnotationRedoUndoCommandModeEnum::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(AnnotationRedoUndoCommandModeEnum::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 AnnotationRedoUndoCommandModeEnum::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(AnnotationRedoUndoCommandModeEnum::toGuiName(iter->enumValue)); } if (isSorted) { std::sort(allGuiNames.begin(), allGuiNames.end()); } } connectome-workbench-1.2.3+git41-gc4c6c90/src/Annotations/AnnotationRedoUndoCommandModeEnum.h000066400000000000000000000121511300200146000317150ustar00rootroot00000000000000#ifndef __ANNOTATION_UNDO_COMMAND_MODE_ENUM_H__ #define __ANNOTATION_UNDO_COMMAND_MODE_ENUM_H__ /*LICENSE_START*/ /* * Copyright (C) 2015 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 AnnotationRedoUndoCommandModeEnum { public: /** * Enumerated values. */ enum Enum { /** Invalid mode */ INVALID, /** Color - Background */ COLOR_BACKGROUND, /** Color - Foreground */ COLOR_FOREGROUND, /** Coordinate One */ COORDINATE_ONE, /** Coordinate One And Two */ COORDINATE_ONE_AND_TWO, /** Coordinate Two */ COORDINATE_TWO, /** Create an annotation */ CREATE_ANNOTATION, /** Cut Annotation */ CUT_ANNOTATION, /** Delete Annotations */ DELETE_ANNOTATIONS, /** Group Annotations */ GROUPING_GROUP, /** Regroup Annotations */ GROUPING_REGROUP, /** Ungroup Annotations */ GROUPING_UNGROUP, /** Line Arrow Start */ LINE_ARROW_START, /** Line Arrow End */ LINE_ARROW_END, /** Line Width of Foreground */ LINE_WIDTH_FOREGROUND, /** Location and size of annotations (coords, size, space, window, tab) */ LOCATION_AND_SIZE, /** Paste Annotation */ PASTE_ANNOTATION, /** Rotation Angle */ ROTATION_ANGLE, /** Text Alignment Horizontal */ TEXT_ALIGNMENT_HORIZONTAL, /** Text Alignment Vertical */ TEXT_ALIGNMENT_VERTICAL, /** Text Characters */ TEXT_CHARACTERS, /** Text Color */ TEXT_COLOR, /** Text Connect to Brainordinate */ TEXT_CONNECT_TO_BRAINORDINATE, /** Text Font Bold */ TEXT_FONT_BOLD, /** Text Font Italic */ TEXT_FONT_ITALIC, /** Text Font Name */ TEXT_FONT_NAME, /** Text Font Percent Size */ TEXT_FONT_PERCENT_SIZE, /** Text Font Point Size */ TEXT_FONT_POINT_SIZE, /** Text Font Underline */ TEXT_FONT_UNDERLINE, /** Text Orientation */ TEXT_ORIENTATION, /** Two dimensional (box, oval) height */ TWO_DIM_HEIGHT, /** Two dimensional (box, oval) width */ TWO_DIM_WIDTH }; ~AnnotationRedoUndoCommandModeEnum(); 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: AnnotationRedoUndoCommandModeEnum(const Enum enumValue, const AString& name, const AString& guiName); static const AnnotationRedoUndoCommandModeEnum* 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 __ANNOTATION_UNDO_COMMAND_MODE_ENUM_DECLARE__ std::vector AnnotationRedoUndoCommandModeEnum::enumData; bool AnnotationRedoUndoCommandModeEnum::initializedFlag = false; int32_t AnnotationRedoUndoCommandModeEnum::integerCodeCounter = 0; #endif // __ANNOTATION_UNDO_COMMAND_MODE_ENUM_DECLARE__ } // namespace #endif //__ANNOTATION_UNDO_COMMAND_MODE_ENUM_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Annotations/AnnotationSizingHandleTypeEnum.cxx000066400000000000000000000333451300200146000316760ustar00rootroot00000000000000 /*LICENSE_START*/ /* * Copyright (C) 2015 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 __ANNOTATION_SIZING_HANDLE_TYPE_ENUM_DECLARE__ #include "AnnotationSizingHandleTypeEnum.h" #undef __ANNOTATION_SIZING_HANDLE_TYPE_ENUM_DECLARE__ #include "CaretAssert.h" using namespace caret; /** * \class caret::AnnotationSizingHandleTypeEnum * \brief Enumerated type for annotation sizing handles * * 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_annotationSizingHandleTypeEnumComboBox; * * Declare a slot that is called when user changes selection * private slots: * void annotationSizingHandleTypeEnumComboBoxItemActivated(); * * Implementation File (.cxx) * Include the header files * #include "EnumComboBoxTemplate.h" * #include "AnnotationSizingHandleTypeEnum.h" * * Instatiate: * m_annotationSizingHandleTypeEnumComboBox = new EnumComboBoxTemplate(this); * m_annotationSizingHandleTypeEnumComboBox->setup(); * * Get notified when the user changes the selection: * QObject::connect(m_annotationSizingHandleTypeEnumComboBox, SIGNAL(itemActivated()), * this, SLOT(annotationSizingHandleTypeEnumComboBoxItemActivated())); * * Update the selection: * m_annotationSizingHandleTypeEnumComboBox->setSelectedItem(NEW_VALUE); * * Read the selection: * const AnnotationSizingHandleTypeEnum::Enum VARIABLE = m_annotationSizingHandleTypeEnumComboBox->getSelectedItem(); * */ /** * Constructor. * * @param enumValue * An enumerated value. * @param name * Name of enumerated value. * * @param guiName * User-friendly name for use in user-interface. */ AnnotationSizingHandleTypeEnum::AnnotationSizingHandleTypeEnum(const Enum enumValue, const AString& name, const AString& guiName) { this->enumValue = enumValue; this->integerCode = integerCodeCounter++; this->name = name; this->guiName = guiName; } /** * Destructor. */ AnnotationSizingHandleTypeEnum::~AnnotationSizingHandleTypeEnum() { } /** * Initialize the enumerated metadata. */ void AnnotationSizingHandleTypeEnum::initialize() { if (initializedFlag) { return; } initializedFlag = true; enumData.push_back(AnnotationSizingHandleTypeEnum(ANNOTATION_SIZING_HANDLE_NONE, "ANNOTATION_SIZING_HANDLE_NONE", "None")); enumData.push_back(AnnotationSizingHandleTypeEnum(ANNOTATION_SIZING_HANDLE_BOX_BOTTOM, "ANNOTATION_SIZING_HANDLE_BOX_BOTTOM", "Bottom")); enumData.push_back(AnnotationSizingHandleTypeEnum(ANNOTATION_SIZING_HANDLE_BOX_BOTTOM_LEFT, "ANNOTATION_SIZING_HANDLE_BOX_BOTTOM_LEFT", "Bottom Left")); enumData.push_back(AnnotationSizingHandleTypeEnum(ANNOTATION_SIZING_HANDLE_BOX_BOTTOM_RIGHT, "ANNOTATION_SIZING_HANDLE_BOX_BOTTOM_RIGHT", "Bottom Right")); enumData.push_back(AnnotationSizingHandleTypeEnum(ANNOTATION_SIZING_HANDLE_BOX_LEFT, "ANNOTATION_SIZING_HANDLE_BOX_LEFT", "Left")); enumData.push_back(AnnotationSizingHandleTypeEnum(ANNOTATION_SIZING_HANDLE_BOX_RIGHT, "ANNOTATION_SIZING_HANDLE_BOX_RIGHT", "Right")); enumData.push_back(AnnotationSizingHandleTypeEnum(ANNOTATION_SIZING_HANDLE_BOX_TOP, "ANNOTATION_SIZING_HANDLE_BOX_TOP", "Top")); enumData.push_back(AnnotationSizingHandleTypeEnum(ANNOTATION_SIZING_HANDLE_BOX_TOP_LEFT, "ANNOTATION_SIZING_HANDLE_BOX_TOP_LEFT", "Top Left")); enumData.push_back(AnnotationSizingHandleTypeEnum(ANNOTATION_SIZING_HANDLE_BOX_TOP_RIGHT, "ANNOTATION_SIZING_HANDLE_BOX_TOP_RIGHT", "Top Right")); enumData.push_back(AnnotationSizingHandleTypeEnum(ANNOTATION_SIZING_HANDLE_ROTATION, "ANNOTATION_SIZING_HANDLE_ROTATION", "Rotation")); enumData.push_back(AnnotationSizingHandleTypeEnum(ANNOTATION_SIZING_HANDLE_LINE_END, "ANNOTATION_SIZING_HANDLE_LINE_END", "Line End")); enumData.push_back(AnnotationSizingHandleTypeEnum(ANNOTATION_SIZING_HANDLE_LINE_START, "ANNOTATION_SIZING_HANDLE_LINE_START", "Line Start")); } /** * 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 AnnotationSizingHandleTypeEnum* AnnotationSizingHandleTypeEnum::findData(const Enum enumValue) { if (initializedFlag == false) initialize(); size_t num = enumData.size(); for (size_t i = 0; i < num; i++) { const AnnotationSizingHandleTypeEnum* 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 AnnotationSizingHandleTypeEnum::toName(Enum enumValue) { if (initializedFlag == false) initialize(); const AnnotationSizingHandleTypeEnum* 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. */ AnnotationSizingHandleTypeEnum::Enum AnnotationSizingHandleTypeEnum::fromName(const AString& name, bool* isValidOut) { if (initializedFlag == false) initialize(); bool validFlag = false; Enum enumValue = AnnotationSizingHandleTypeEnum::enumData[0].enumValue; for (std::vector::iterator iter = enumData.begin(); iter != enumData.end(); iter++) { const AnnotationSizingHandleTypeEnum& 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 AnnotationSizingHandleTypeEnum")); } return enumValue; } /** * Get a GUI string representation of the enumerated type. * @param enumValue * Enumerated value. * @return * String representing enumerated value. */ AString AnnotationSizingHandleTypeEnum::toGuiName(Enum enumValue) { if (initializedFlag == false) initialize(); const AnnotationSizingHandleTypeEnum* 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. */ AnnotationSizingHandleTypeEnum::Enum AnnotationSizingHandleTypeEnum::fromGuiName(const AString& guiName, bool* isValidOut) { if (initializedFlag == false) initialize(); bool validFlag = false; Enum enumValue = AnnotationSizingHandleTypeEnum::enumData[0].enumValue; for (std::vector::iterator iter = enumData.begin(); iter != enumData.end(); iter++) { const AnnotationSizingHandleTypeEnum& 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 AnnotationSizingHandleTypeEnum")); } return enumValue; } /** * Get the integer code for a data type. * * @return * Integer code for data type. */ int32_t AnnotationSizingHandleTypeEnum::toIntegerCode(Enum enumValue) { if (initializedFlag == false) initialize(); const AnnotationSizingHandleTypeEnum* 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. */ AnnotationSizingHandleTypeEnum::Enum AnnotationSizingHandleTypeEnum::fromIntegerCode(const int32_t integerCode, bool* isValidOut) { if (initializedFlag == false) initialize(); bool validFlag = false; Enum enumValue = AnnotationSizingHandleTypeEnum::enumData[0].enumValue; for (std::vector::iterator iter = enumData.begin(); iter != enumData.end(); iter++) { const AnnotationSizingHandleTypeEnum& 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 AnnotationSizingHandleTypeEnum")); } 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 AnnotationSizingHandleTypeEnum::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 AnnotationSizingHandleTypeEnum::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(AnnotationSizingHandleTypeEnum::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 AnnotationSizingHandleTypeEnum::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(AnnotationSizingHandleTypeEnum::toGuiName(iter->enumValue)); } if (isSorted) { std::sort(allGuiNames.begin(), allGuiNames.end()); } } connectome-workbench-1.2.3+git41-gc4c6c90/src/Annotations/AnnotationSizingHandleTypeEnum.h000066400000000000000000000076711300200146000313260ustar00rootroot00000000000000#ifndef __ANNOTATION_SIZING_HANDLE_TYPE_ENUM_H__ #define __ANNOTATION_SIZING_HANDLE_TYPE_ENUM_H__ /*LICENSE_START*/ /* * Copyright (C) 2015 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 AnnotationSizingHandleTypeEnum { public: /** * Enumerated values. */ enum Enum { /** No sizing handle */ ANNOTATION_SIZING_HANDLE_NONE, /** Bottom */ ANNOTATION_SIZING_HANDLE_BOX_BOTTOM, /** Bottom Left */ ANNOTATION_SIZING_HANDLE_BOX_BOTTOM_LEFT, /** Bottom Right */ ANNOTATION_SIZING_HANDLE_BOX_BOTTOM_RIGHT, /** Left */ ANNOTATION_SIZING_HANDLE_BOX_LEFT, /** Right */ ANNOTATION_SIZING_HANDLE_BOX_RIGHT, /** Top */ ANNOTATION_SIZING_HANDLE_BOX_TOP, /** Top Left */ ANNOTATION_SIZING_HANDLE_BOX_TOP_LEFT, /** Top Right */ ANNOTATION_SIZING_HANDLE_BOX_TOP_RIGHT, /** Rotation */ ANNOTATION_SIZING_HANDLE_ROTATION, /** Line End */ ANNOTATION_SIZING_HANDLE_LINE_END, /** Line Start */ ANNOTATION_SIZING_HANDLE_LINE_START }; ~AnnotationSizingHandleTypeEnum(); 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: AnnotationSizingHandleTypeEnum(const Enum enumValue, const AString& name, const AString& guiName); static const AnnotationSizingHandleTypeEnum* 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 __ANNOTATION_SIZING_HANDLE_TYPE_ENUM_DECLARE__ std::vector AnnotationSizingHandleTypeEnum::enumData; bool AnnotationSizingHandleTypeEnum::initializedFlag = false; int32_t AnnotationSizingHandleTypeEnum::integerCodeCounter = 0; #endif // __ANNOTATION_SIZING_HANDLE_TYPE_ENUM_DECLARE__ } // namespace #endif //__ANNOTATION_SIZING_HANDLE_TYPE_ENUM_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Annotations/AnnotationSpatialModification.cxx000066400000000000000000000123451300200146000315500ustar00rootroot00000000000000 /*LICENSE_START*/ /* * Copyright (C) 2015 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 __ANNOTATION_SPATIAL_MODIFICATION_DECLARE__ #include "AnnotationSpatialModification.h" #undef __ANNOTATION_SPATIAL_MODIFICATION_DECLARE__ #include "CaretAssert.h" using namespace caret; /** * \class caret::AnnotationSpatialModification * \brief Contains information for spatially modifying an annotation. * \ingroup Annotations */ /** * Apply a move or resize operation received from the GUI. * * @param handleSelected * Annotatoion handle that is being processed by the user. * @param viewportWidth * Width of viewport * @param viewportHeight * Height of viewport * @param mousePressX * Mouse pressed X-coordinate. * @param mousePressY * Mouse pressed Y-coordinate. * @param mouseX * Mouse X-coordinate. * @param mouseY * Mouse Y-coordinate. * @param mouseDX * Change in mouse X-coordinate. * @param mouseDY * Change in mouse Y-coordinate. * @param startOfDraggingFlag * True when user starts to drag mouse. */ AnnotationSpatialModification::AnnotationSpatialModification(const AnnotationSizingHandleTypeEnum::Enum sizingHandleType, const float viewportWidth, const float viewportHeight, const float mousePressX, const float mousePressY, const float mouseX, const float mouseY, const float mouseDX, const float mouseDY, const bool startOfDraggingFlag) : CaretObject(), m_sizingHandleType(sizingHandleType), m_viewportWidth(viewportWidth), m_viewportHeight(viewportHeight), m_mousePressX(mousePressX), m_mousePressY(mousePressY), m_mouseX(mouseX), m_mouseY(mouseY), m_mouseDX(mouseDX), m_mouseDY(mouseDY), m_startOfDraggingFlag(startOfDraggingFlag) { } /** * Destructor. */ AnnotationSpatialModification::~AnnotationSpatialModification() { } /** * Set the surface coordinate at mouse X/Y. * * @param structure * The surface structure. * @param surfaceNumberOfNodes * Number of nodes in the surface. * @param surfaceNodeIndex * Surface node index. */ void AnnotationSpatialModification::setSurfaceCoordinateAtMouseXY(const StructureEnum::Enum structure, const int32_t surfaceNumberOfNodes, const int32_t surfaceNodeIndex) { m_surfaceCoordinateAtMouseXY.m_surfaceStructure = structure; m_surfaceCoordinateAtMouseXY.m_surfaceNumberOfNodes = surfaceNumberOfNodes; m_surfaceCoordinateAtMouseXY.m_surfaceNodeIndex = surfaceNodeIndex; m_surfaceCoordinateAtMouseXY.m_surfaceNodeValid = false; if ((m_surfaceCoordinateAtMouseXY.m_surfaceStructure != StructureEnum::INVALID) && (m_surfaceCoordinateAtMouseXY.m_surfaceNumberOfNodes > 0) && (m_surfaceCoordinateAtMouseXY.m_surfaceNodeIndex >= 0) && (m_surfaceCoordinateAtMouseXY.m_surfaceNodeIndex < m_surfaceCoordinateAtMouseXY.m_surfaceNumberOfNodes)) { m_surfaceCoordinateAtMouseXY.m_surfaceNodeValid = true; } } /** * Set the stereotaxic coordinate at mouse X/Y * * @param stereotaxicX * stereotaxic X-coordinate. * @param stereotaxicY * stereotaxic Y-coordinate. * @param stereotaxicZ * stereotaxic Z-coordinate. */ void AnnotationSpatialModification::setStereotaxicCoordinateAtMouseXY(const float stereotaxicX, const float stereotaxicY, const float stereotaxicZ) { m_stereotaxicCoordinateAtMouseXY.m_stereotaxicXYZ[0] = stereotaxicX; m_stereotaxicCoordinateAtMouseXY.m_stereotaxicXYZ[1] = stereotaxicY; m_stereotaxicCoordinateAtMouseXY.m_stereotaxicXYZ[2] = stereotaxicZ; m_stereotaxicCoordinateAtMouseXY.m_stereotaxicValid = true; } /** * Get a description of this object's content. * @return String describing this object's content. */ AString AnnotationSpatialModification::toString() const { return "AnnotationSpatialModification"; } connectome-workbench-1.2.3+git41-gc4c6c90/src/Annotations/AnnotationSpatialModification.h000066400000000000000000000106761300200146000312020ustar00rootroot00000000000000#ifndef __ANNOTATION_SPATIAL_MODIFICATION_H__ #define __ANNOTATION_SPATIAL_MODIFICATION_H__ /*LICENSE_START*/ /* * Copyright (C) 2015 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 "AnnotationSizingHandleTypeEnum.h" #include "CaretObject.h" #include "StructureEnum.h" namespace caret { class AnnotationSpatialModification : public CaretObject { public: AnnotationSpatialModification(const AnnotationSizingHandleTypeEnum::Enum sizingHandleType, const float viewportWidth, const float viewportHeight, const float mousePressX, const float mousePressY, const float mouseX, const float mouseY, const float mouseDX, const float mouseDY, const bool startOfDraggingFlag); void setSurfaceCoordinateAtMouseXY(const StructureEnum::Enum structure, const int32_t surfaceNumberOfNodes, const int32_t surfaceNodeIndex); void setStereotaxicCoordinateAtMouseXY(const float stereotaxicX, const float stereotaxicY, const float stereotaxicZ); virtual ~AnnotationSpatialModification(); // ADD_NEW_METHODS_HERE virtual AString toString() const; private: class SurfaceCoord { public: SurfaceCoord() { m_surfaceStructure = StructureEnum::INVALID; m_surfaceNumberOfNodes = -1; m_surfaceNodeIndex = -1; m_surfaceNodeValid = false; } StructureEnum::Enum m_surfaceStructure; int32_t m_surfaceNumberOfNodes; int32_t m_surfaceNodeIndex; bool m_surfaceNodeValid; }; class StereotaxicCoord { public: StereotaxicCoord() { m_stereotaxicXYZ[0] = 0.0; m_stereotaxicXYZ[1] = 0.0; m_stereotaxicXYZ[2] = 0.0; m_stereotaxicValid = false; } float m_stereotaxicXYZ[3]; bool m_stereotaxicValid; }; AnnotationSpatialModification(const AnnotationSpatialModification&); AnnotationSpatialModification& operator=(const AnnotationSpatialModification&); const AnnotationSizingHandleTypeEnum::Enum m_sizingHandleType; const float m_viewportWidth; const float m_viewportHeight; const float m_mousePressX; const float m_mousePressY; const float m_mouseX; const float m_mouseY; const float m_mouseDX; const float m_mouseDY; const bool m_startOfDraggingFlag; SurfaceCoord m_surfaceCoordinateAtMouseXY; StereotaxicCoord m_stereotaxicCoordinateAtMouseXY; // ADD_NEW_MEMBERS_HERE friend class AnnotationOneDimensionalShape; friend class AnnotationText; friend class AnnotationTwoDimensionalShape; }; #ifdef __ANNOTATION_SPATIAL_MODIFICATION_DECLARE__ // #endif // __ANNOTATION_SPATIAL_MODIFICATION_DECLARE__ } // namespace #endif //__ANNOTATION_SPATIAL_MODIFICATION_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Annotations/AnnotationSurfaceOffsetVectorTypeEnum.cxx000066400000000000000000000307151300200146000332370ustar00rootroot00000000000000 /*LICENSE_START*/ /* * Copyright (C) 2015 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 __ANNOTATION_SURFACE_OFFSET_VECTOR_TYPE_ENUM_DECLARE__ #include "AnnotationSurfaceOffsetVectorTypeEnum.h" #undef __ANNOTATION_SURFACE_OFFSET_VECTOR_TYPE_ENUM_DECLARE__ #include "CaretAssert.h" using namespace caret; /** * \class caret::AnnotationSurfaceOffsetVectorTypeEnum * \brief Type of offset vector for surface annotations * * 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_annotationSurfaceOffsetVectorTypeEnumComboBox; * * Declare a slot that is called when user changes selection * private slots: * void annotationSurfaceOffsetVectorTypeEnumComboBoxItemActivated(); * * Implementation File (.cxx) * Include the header files * #include "EnumComboBoxTemplate.h" * #include "AnnotationSurfaceOffsetVectorTypeEnum.h" * * Instatiate: * m_annotationSurfaceOffsetVectorTypeEnumComboBox = new EnumComboBoxTemplate(this); * m_annotationSurfaceOffsetVectorTypeEnumComboBox->setup(); * * Get notified when the user changes the selection: * QObject::connect(m_annotationSurfaceOffsetVectorTypeEnumComboBox, SIGNAL(itemActivated()), * this, SLOT(annotationSurfaceOffsetVectorTypeEnumComboBoxItemActivated())); * * Update the selection: * m_annotationSurfaceOffsetVectorTypeEnumComboBox->setSelectedItem(NEW_VALUE); * * Read the selection: * const AnnotationSurfaceOffsetVectorTypeEnum::Enum VARIABLE = m_annotationSurfaceOffsetVectorTypeEnumComboBox->getSelectedItem(); * */ /** * Constructor. * * @param enumValue * An enumerated value. * @param name * Name of enumerated value. * @param guiName * User-friendly name for use in user-interface. * @param longGuiName * Long User-friendly name for use in user-interface. */ AnnotationSurfaceOffsetVectorTypeEnum::AnnotationSurfaceOffsetVectorTypeEnum(const Enum enumValue, const AString& name, const AString& guiName, const AString& longGuiName) { this->enumValue = enumValue; this->integerCode = integerCodeCounter++; this->name = name; this->guiName = guiName; this->longGuiName = longGuiName; } /** * Destructor. */ AnnotationSurfaceOffsetVectorTypeEnum::~AnnotationSurfaceOffsetVectorTypeEnum() { } /** * Initialize the enumerated metadata. */ void AnnotationSurfaceOffsetVectorTypeEnum::initialize() { if (initializedFlag) { return; } initializedFlag = true; enumData.push_back(AnnotationSurfaceOffsetVectorTypeEnum(CENTROID_THRU_VERTEX, "CENTROID_THRU_VERTEX", "C", "Centroid Thru Vertex")); enumData.push_back(AnnotationSurfaceOffsetVectorTypeEnum(SURACE_NORMAL, "SURACE_NORMAL", "N", "Surace Normal")); } /** * 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 AnnotationSurfaceOffsetVectorTypeEnum* AnnotationSurfaceOffsetVectorTypeEnum::findData(const Enum enumValue) { if (initializedFlag == false) initialize(); size_t num = enumData.size(); for (size_t i = 0; i < num; i++) { const AnnotationSurfaceOffsetVectorTypeEnum* 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 AnnotationSurfaceOffsetVectorTypeEnum::toName(Enum enumValue) { if (initializedFlag == false) initialize(); const AnnotationSurfaceOffsetVectorTypeEnum* 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. */ AnnotationSurfaceOffsetVectorTypeEnum::Enum AnnotationSurfaceOffsetVectorTypeEnum::fromName(const AString& name, bool* isValidOut) { if (initializedFlag == false) initialize(); bool validFlag = false; Enum enumValue = AnnotationSurfaceOffsetVectorTypeEnum::enumData[0].enumValue; for (std::vector::iterator iter = enumData.begin(); iter != enumData.end(); iter++) { const AnnotationSurfaceOffsetVectorTypeEnum& 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 AnnotationSurfaceOffsetVectorTypeEnum")); } return enumValue; } /** * Get a GUI string representation of the enumerated type. * @param enumValue * Enumerated value. * @return * String representing enumerated value. */ AString AnnotationSurfaceOffsetVectorTypeEnum::toGuiName(Enum enumValue) { if (initializedFlag == false) initialize(); const AnnotationSurfaceOffsetVectorTypeEnum* enumInstance = findData(enumValue); return enumInstance->guiName; } /** * Get a long GUI string representation of the enumerated type. * @param enumValue * Enumerated value. * @return * long string representing enumerated value. */ AString AnnotationSurfaceOffsetVectorTypeEnum::toLongGuiName(Enum enumValue) { if (initializedFlag == false) initialize(); const AnnotationSurfaceOffsetVectorTypeEnum* enumInstance = findData(enumValue); return enumInstance->longGuiName; } /** * 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. */ AnnotationSurfaceOffsetVectorTypeEnum::Enum AnnotationSurfaceOffsetVectorTypeEnum::fromGuiName(const AString& guiName, bool* isValidOut) { if (initializedFlag == false) initialize(); bool validFlag = false; Enum enumValue = AnnotationSurfaceOffsetVectorTypeEnum::enumData[0].enumValue; for (std::vector::iterator iter = enumData.begin(); iter != enumData.end(); iter++) { const AnnotationSurfaceOffsetVectorTypeEnum& 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 AnnotationSurfaceOffsetVectorTypeEnum")); } return enumValue; } /** * Get the integer code for a data type. * * @return * Integer code for data type. */ int32_t AnnotationSurfaceOffsetVectorTypeEnum::toIntegerCode(Enum enumValue) { if (initializedFlag == false) initialize(); const AnnotationSurfaceOffsetVectorTypeEnum* 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. */ AnnotationSurfaceOffsetVectorTypeEnum::Enum AnnotationSurfaceOffsetVectorTypeEnum::fromIntegerCode(const int32_t integerCode, bool* isValidOut) { if (initializedFlag == false) initialize(); bool validFlag = false; Enum enumValue = AnnotationSurfaceOffsetVectorTypeEnum::enumData[0].enumValue; for (std::vector::iterator iter = enumData.begin(); iter != enumData.end(); iter++) { const AnnotationSurfaceOffsetVectorTypeEnum& 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 AnnotationSurfaceOffsetVectorTypeEnum")); } 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 AnnotationSurfaceOffsetVectorTypeEnum::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 AnnotationSurfaceOffsetVectorTypeEnum::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(AnnotationSurfaceOffsetVectorTypeEnum::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 AnnotationSurfaceOffsetVectorTypeEnum::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(AnnotationSurfaceOffsetVectorTypeEnum::toGuiName(iter->enumValue)); } if (isSorted) { std::sort(allGuiNames.begin(), allGuiNames.end()); } } connectome-workbench-1.2.3+git41-gc4c6c90/src/Annotations/AnnotationSurfaceOffsetVectorTypeEnum.h000066400000000000000000000072131300200146000326610ustar00rootroot00000000000000#ifndef __ANNOTATION_SURFACE_OFFSET_VECTOR_TYPE_ENUM_H__ #define __ANNOTATION_SURFACE_OFFSET_VECTOR_TYPE_ENUM_H__ /*LICENSE_START*/ /* * Copyright (C) 2015 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 AnnotationSurfaceOffsetVectorTypeEnum { public: /** * Enumerated values. */ enum Enum { /** Vector starts at centroid and extends through vertex */ CENTROID_THRU_VERTEX, /** Vector is surface normal */ SURACE_NORMAL }; ~AnnotationSurfaceOffsetVectorTypeEnum(); 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 AString toLongGuiName(Enum enumValue); 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: AnnotationSurfaceOffsetVectorTypeEnum(const Enum enumValue, const AString& name, const AString& guiName, const AString& longGuiName); static const AnnotationSurfaceOffsetVectorTypeEnum* 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; /** A user-friendly name that is displayed in the GUI */ AString longGuiName; }; #ifdef __ANNOTATION_SURFACE_OFFSET_VECTOR_TYPE_ENUM_DECLARE__ std::vector AnnotationSurfaceOffsetVectorTypeEnum::enumData; bool AnnotationSurfaceOffsetVectorTypeEnum::initializedFlag = false; int32_t AnnotationSurfaceOffsetVectorTypeEnum::integerCodeCounter = 0; #endif // __ANNOTATION_SURFACE_OFFSET_VECTOR_TYPE_ENUM_DECLARE__ } // namespace #endif //__ANNOTATION_SURFACE_OFFSET_VECTOR_TYPE_ENUM_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Annotations/AnnotationText.cxx000066400000000000000000000707251300200146000265570ustar00rootroot00000000000000 /*LICENSE_START*/ /* * Copyright (C) 2015 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 __ANNOTATION_TEXT_DECLARE__ #include "AnnotationText.h" #undef __ANNOTATION_TEXT_DECLARE__ #include "AnnotationPercentSizeText.h" #include "AnnotationSpatialModification.h" #include "CaretAssert.h" #include "CaretLogger.h" #include "MathFunctions.h" #include "SceneClass.h" #include "SceneClassAssistant.h" using namespace caret; /** * \class caret::AnnotationText * \brief Text annotation. * \ingroup Annotations */ ///** // * Constructor for a text annotation. // */ //AnnotationText::AnnotationText() //: AnnotationTwoDimensionalShape(AnnotationTypeEnum::TEXT), //m_fontSizeType(AnnotationTextFontSizeTypeEnum::PERCENTAGE_OF_VIEWPORT_HEIGHT) //{ // initializeAnnotationTextMembers(); //} /** * Constructor for subclass. * * @param attributeDefaultType * Type for attribute defaults * @param fontSizeType * Type of font sizing. */ AnnotationText::AnnotationText(const AnnotationAttributesDefaultTypeEnum::Enum attributeDefaultType, const AnnotationTextFontSizeTypeEnum::Enum fontSizeType) : AnnotationTwoDimensionalShape(AnnotationTypeEnum::TEXT, attributeDefaultType), AnnotationFontAttributesInterface(), m_fontSizeType(fontSizeType) { initializeAnnotationTextMembers(); } /** * Destructor. */ AnnotationText::~AnnotationText() { } /** * Copy constructor. * @param obj * Object that is copied. */ AnnotationText::AnnotationText(const AnnotationText& obj) : AnnotationTwoDimensionalShape(obj), AnnotationFontAttributesInterface(), m_fontSizeType(obj.m_fontSizeType) { initializeAnnotationTextMembers(); this->copyHelperAnnotationText(obj); } /** * Assignment operator. * @param obj * Data copied from obj to this. * @return * Reference to this object. */ AnnotationText& AnnotationText::operator=(const AnnotationText& obj) { if (this != &obj) { Annotation::operator=(obj); this->copyHelperAnnotationText(obj); } return *this; } /** * Initialize members of this class. */ void AnnotationText::initializeAnnotationTextMembers() { switch (m_attributeDefaultType) { case AnnotationAttributesDefaultTypeEnum::NORMAL: m_alignmentHorizontal = AnnotationTextAlignHorizontalEnum::LEFT; m_alignmentVertical = AnnotationTextAlignVerticalEnum::TOP; m_font = AnnotationTextFontNameEnum::VERA; m_fontPointSize = AnnotationTextFontPointSizeEnum::SIZE14; m_orientation = AnnotationTextOrientationEnum::HORIZONTAL; m_colorText = CaretColorEnum::WHITE; m_customColorText[0] = 1.0; m_customColorText[1] = 1.0; m_customColorText[2] = 1.0; m_customColorText[3] = 1.0; m_boldEnabled = false; m_italicEnabled = false; m_underlineEnabled = false; m_connectToBrainordinate = AnnotationTextConnectTypeEnum::ANNOTATION_TEXT_CONNECT_NONE; m_fontPercentViewportSize = 5.0; break; case AnnotationAttributesDefaultTypeEnum::USER: m_alignmentHorizontal = s_userDefaultAlignmentHorizontal; m_alignmentVertical = s_userDefaultAlignmentVertical; m_font = s_userDefaultFont; m_fontPointSize = s_userDefaultPointSize; m_orientation = s_userDefaultOrientation; m_colorText = s_userDefaultColorText; m_customColorText[0] = s_userDefaultCustomColorText[0]; m_customColorText[1] = s_userDefaultCustomColorText[1]; m_customColorText[2] = s_userDefaultCustomColorText[2]; m_customColorText[3] = s_userDefaultCustomColorText[3]; m_boldEnabled = s_userDefaultBoldEnabled; m_italicEnabled = s_userDefaultItalicEnabled; m_underlineEnabled = s_userDefaultUnderlineEnabled; m_connectToBrainordinate = s_userDefaultConnectToBrainordinate; m_fontPercentViewportSize = s_userDefaultFontPercentViewportSize; break; } m_text = ""; m_sceneAssistant.grabNew(new SceneClassAssistant()); } /** * Get an encoded name that contains the * name of the font, the font height, and the * font style used by font rendering to provide * a name for cached fonts. * * The text annotation contains the height of the viewport * when the annotation was created. When it is valid ( * greater than zero), and the drawing viewport height * is also valid (greater than zero), a scaling value * (equal to drawing viewport height divided by creation * viewport height) is applied to the font size so that * the font can scale as the viewport change in size. * Thus, if the viewport becomes larger (smaller), the * text will become larger (smaller). While one could * scale while drawing the text (using glScale()), the * quality is poor. * * @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 { 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); } } } AString encodedName; encodedName.reserve(50); encodedName.append(AnnotationTextFontNameEnum::toName(m_font)); encodedName.append("_" + fontSizeID); if (m_boldEnabled) { encodedName.append("_B"); } 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; } /** * @return Text for the annotation. */ AString AnnotationText::getText() const { return m_text; } /** * Set the text for an annotation. * @param text * Text for the annotation. */ void AnnotationText::setText(const AString& text) { if (text != m_text) { m_text = text; textAnnotationResetName(); setModified(); } } /** * @return The horizontal alignment. */ AnnotationTextAlignHorizontalEnum::Enum AnnotationText::getHorizontalAlignment() const { return m_alignmentHorizontal; } /** * Set the horizontal alignment. * * @param alignment * New value for horizontal alignment. */ void AnnotationText::setHorizontalAlignment(const AnnotationTextAlignHorizontalEnum::Enum alignment) { if (m_alignmentHorizontal != alignment) { m_alignmentHorizontal = alignment; setModified(); } } /** * @return The vertical alignment. */ AnnotationTextAlignVerticalEnum::Enum AnnotationText::getVerticalAlignment() const { return m_alignmentVertical; } /** * Set the vertical alignment. * * @param alignment * New value for vertical alignment. */ void AnnotationText::setVerticalAlignment(const AnnotationTextAlignVerticalEnum::Enum alignment) { if (m_alignmentVertical != alignment) { m_alignmentVertical = alignment; setModified(); } } /** * @param connect to brainordinate status (none, line, arrow). */ AnnotationTextConnectTypeEnum::Enum AnnotationText::getConnectToBrainordinate() const { return m_connectToBrainordinate; } /** * Set the connect to brainordinate status. * * @param connectToBrainordinate * New value for connection to brainordinate (none, line, arrow) */ void AnnotationText::setConnectToBrainordinate(const AnnotationTextConnectTypeEnum::Enum connectToBrainordinate) { if (m_connectToBrainordinate != connectToBrainordinate) { m_connectToBrainordinate = connectToBrainordinate; setModified(); } } /** * @return Is connect to brainordinate valid for this text annotation. */ bool AnnotationText::isConnectToBrainordinateValid() const { if (getCoordinateSpace() == AnnotationCoordinateSpaceEnum::SURFACE) { return true; } return false; } /** * @return The font. */ AnnotationTextFontNameEnum::Enum AnnotationText::getFont() const { return m_font; } /** * Set the font for an annotation. * @param font * Font for the annotation. */ void AnnotationText::setFont(const AnnotationTextFontNameEnum::Enum font) { if (font != m_font) { m_font = font; setModified(); } } /** * Get the font size for drawing. The font size may * be scaled to "best fit" the viewport. * * For a Point Size Text Annotation, the size returned is the text annotation's * point size. * * For a Percentage Size Text Annotation, the size returned will be * "percent size" of the viewport height in a range from zero to * one where one equivalent to the viewport's 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 { float sizeForDrawing = AnnotationTextFontPointSizeEnum::toSizeNumeric(AnnotationTextFontPointSizeEnum::SIZE14); switch (m_fontSizeType) { case AnnotationTextFontSizeTypeEnum::PERCENTAGE_OF_VIEWPORT_HEIGHT: { /* * May need pixel to points conversion if not 72 DPI */ const float pixelSize = drawingViewportHeight * (m_fontPercentViewportSize / 100.0); sizeForDrawing = pixelSize; } break; case AnnotationTextFontSizeTypeEnum::POINTS: sizeForDrawing = AnnotationTextFontPointSizeEnum::toSizeNumeric(m_fontPointSize); break; } if (sizeForDrawing < AnnotationTextFontPointSizeEnum::getMinimumSizeNumeric()) { sizeForDrawing = AnnotationTextFontPointSizeEnum::getMinimumSizeNumeric(); } const int32_t sizeInt = static_cast(MathFunctions::round(sizeForDrawing)); return sizeInt; } /** * @return The font point size. */ AnnotationTextFontPointSizeEnum::Enum AnnotationText::getFontPointSizeProtected() const { return m_fontPointSize; } /** * Set the font point size. * * @param fontPointSize * New font point size. */ void AnnotationText::setFontPointSizeProtected(const AnnotationTextFontPointSizeEnum::Enum fontPointSize) { if (fontPointSize != m_fontPointSize) { m_fontPointSize = fontPointSize; setModified(); } } /** * @param The font size type. */ AnnotationTextFontSizeTypeEnum::Enum AnnotationText::getFontSizeType() const { return m_fontSizeType; } /** * @return The text orientation. */ AnnotationTextOrientationEnum::Enum AnnotationText::getOrientation() const { return m_orientation; } /** * Set the text orientation. * * @param orientation * New value for orientation. */ void AnnotationText::setOrientation(const AnnotationTextOrientationEnum::Enum orientation) { if (orientation != m_orientation) { m_orientation = orientation; setModified(); } } /** * Get the icon color for this item. Icon is filled with background * color, outline color is drawn around edges, and text color is small * square in center. For any colors that do not apply, use an alpha * value (last element) of zero. * * @param backgroundRgbaOut * Red, green, blue, alpha components for background ranging [0, 1]. * @param outlineRgbaOut * Red, green, blue, alpha components for outline ranging [0, 1]. * @param textRgbaOut * Red, green, blue, alpha components for text ranging [0, 1]. */ void AnnotationText::getItemIconColorsRGBA(float backgroundRgbaOut[4], float outlineRgbaOut[4], float textRgbaOut[4]) const { /* * Sets the background and outline colors */ Annotation::getItemIconColorsRGBA(backgroundRgbaOut, outlineRgbaOut, textRgbaOut); getTextColorRGBA(textRgbaOut); } /** * @return The foreground color. */ CaretColorEnum::Enum AnnotationText::getTextColor() const { return m_colorText; } /** * Set the foreground color. * * @param color * New value for foreground color. */ void AnnotationText::setTextColor(const CaretColorEnum::Enum color) { if (m_colorText != color) { m_colorText = color; setModified(); } } /** * Get the foreground color's RGBA components regardless of * coloring (custom color or a CaretColorEnum) selected by the user. * * @param rgbaOut * RGBA components ranging 0.0 to 1.0. */ void AnnotationText::getTextColorRGBA(float rgbaOut[4]) const { switch (m_colorText) { case CaretColorEnum::NONE: rgbaOut[0] = 0.0; rgbaOut[1] = 0.0; rgbaOut[2] = 0.0; rgbaOut[3] = 0.0; break; case CaretColorEnum::CUSTOM: getCustomTextColor(rgbaOut); break; case CaretColorEnum::AQUA: case CaretColorEnum::BLACK: case CaretColorEnum::BLUE: case CaretColorEnum::FUCHSIA: case CaretColorEnum::GRAY: case CaretColorEnum::GREEN: case CaretColorEnum::LIME: case CaretColorEnum::MAROON: case CaretColorEnum::NAVY: case CaretColorEnum::OLIVE: case CaretColorEnum::PURPLE: case CaretColorEnum::RED: case CaretColorEnum::SILVER: case CaretColorEnum::TEAL: case CaretColorEnum::WHITE: case CaretColorEnum::YELLOW: CaretColorEnum::toRGBFloat(m_colorText, rgbaOut); rgbaOut[3] = 1.0; break; } } /** * Get the foreground color's RGBA components regardless of * coloring (custom color or a CaretColorEnum) selected by the user. * * @param rgbaOut * RGBA components ranging 0 to 255. */ void AnnotationText::getTextColorRGBA(uint8_t rgbaOut[4]) const { float rgbaFloat[4] = { 0.0, 0.0, 0.0, 0.0 }; getTextColorRGBA(rgbaFloat); 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] = static_cast(rgbaFloat[3] * 255.0); } /** * Get the foreground color. * * @param rgbaOut * RGBA components (red, green, blue, alpha) each of which ranges [0.0, 1.0]. */ void AnnotationText::getCustomTextColor(float rgbaOut[4]) const { rgbaOut[0] = m_customColorText[0]; rgbaOut[1] = m_customColorText[1]; rgbaOut[2] = m_customColorText[2]; rgbaOut[3] = m_customColorText[3]; } /** * Get the foreground color. * * @param rgbaOut * RGBA components (red, green, blue, alpha) each of which ranges [0, 255]. */ void AnnotationText::getCustomTextColor(uint8_t rgbaOut[4]) const { rgbaOut[0] = static_cast(m_customColorText[0] * 255.0); rgbaOut[1] = static_cast(m_customColorText[1] * 255.0); rgbaOut[2] = static_cast(m_customColorText[2] * 255.0); rgbaOut[3] = static_cast(m_customColorText[3] * 255.0); } /** * Set the foreground color with floats. * * @param rgba * RGBA components (red, green, blue, alpha) each of which ranges [0.0, 1.0]. */ void AnnotationText::setCustomTextColor(const float rgba[4]) { for (int32_t i = 0; i < 4; i++) { if (rgba[i] != m_customColorText[i]) { m_customColorText[i] = rgba[i]; setModified(); } } } /** * Set the foreground color with unsigned bytes. * * @param rgba * RGBA components (red, green, blue, alpha) each of which ranges [0, 255]. */ void AnnotationText::setCustomTextColor(const uint8_t rgba[4]) { for (int32_t i = 0; i < 4; i++) { const float component = rgba[i] / 255.0; if (component != m_customColorText[i]) { m_customColorText[i] = component; setModified(); } } } /** * Are font styles (Bold, Italic, Underline) supported? */ bool AnnotationText::isStylesSupported() const { return true; } /** * @return * Is bold enabled ? */ bool AnnotationText::isBoldStyleEnabled() const { return m_boldEnabled; } /** * Set bold enabled. * * @param enabled * New status for bold enabled. */ void AnnotationText::setBoldStyleEnabled(const bool enabled) { if (enabled != m_boldEnabled) { m_boldEnabled = enabled; setModified(); } } /** * @return * Is italic enabled ? */ bool AnnotationText::isItalicStyleEnabled() const { return m_italicEnabled; } /** * Set italic enabled. * * @param enabled * New status for italic enabled. */ void AnnotationText::setItalicStyleEnabled(const bool enabled) { if (enabled != m_italicEnabled) { m_italicEnabled = enabled; setModified(); } } /** * @return * Is underline enabled ? */ bool 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. * * @param enabled * New status for underline enabled. */ void AnnotationText::setUnderlineStyleEnabled(const bool enabled) { if (enabled != m_underlineEnabled) { m_underlineEnabled = enabled; setModified(); } } /** * Helps with copying an object of this type. * @param obj * Object that is copied. */ void AnnotationText::copyHelperAnnotationText(const AnnotationText& obj) { m_text = obj.m_text; m_alignmentHorizontal = obj.m_alignmentHorizontal; m_alignmentVertical = obj.m_alignmentVertical; m_font = obj.m_font; m_fontPointSize = obj.m_fontPointSize; m_orientation = obj.m_orientation; m_colorText = obj.m_colorText; m_customColorText[0] = obj.m_customColorText[0]; m_customColorText[1] = obj.m_customColorText[1]; m_customColorText[2] = obj.m_customColorText[2]; m_customColorText[3] = obj.m_customColorText[3]; m_boldEnabled = obj.m_boldEnabled; m_italicEnabled = obj.m_italicEnabled; m_underlineEnabled = obj.m_underlineEnabled; m_connectToBrainordinate = obj.m_connectToBrainordinate; m_fontPercentViewportSize = obj.m_fontPercentViewportSize; } /** * Apply the coordinates, size, and rotation from the given annotation * to this annotation. * * @param otherAnnotation * The other annotation from which attributes are obtained. */ void AnnotationText::applyCoordinatesSizeAndRotationFromOther(const Annotation* otherAnnotation) { AnnotationTwoDimensionalShape::applyCoordinatesSizeAndRotationFromOther(otherAnnotation); /* * Text size may change when an annotation is moved to a different * coordinate space (ie: Tab Space with Tile Tabs on to Window Space). */ const AnnotationPercentSizeText* otherTextAnn = dynamic_cast(otherAnnotation); AnnotationPercentSizeText* textAnn = dynamic_cast(this); if ((textAnn != NULL) && (otherTextAnn != NULL)) { textAnn->setFontPercentViewportSize(otherTextAnn->getFontPercentViewportSize()); } } /** * @return Size of font as a percentage of the viewport height. * * Range is zero to one-hundred. */ float AnnotationText::getFontPercentViewportSizeProtected() const { return m_fontPercentViewportSize; } /** * Set the size of the font as a percentage of the viewport height. * * @param fontPercentViewportHeight * New value for percentage of viewport height. * Range is zero to one-hundred. */ void AnnotationText::setFontPercentViewportSizeProtected(const float fontPercentViewportHeight) { bool validPercentSizeFlag = true; if (fontPercentViewportHeight < 0.0) { validPercentSizeFlag = false; } else if (fontPercentViewportHeight > 100.0) { /* * With surface montage tab sizing, percentage may exceed 100.0 * SO NOTHING TO DO */ } if ( ! validPercentSizeFlag) { const QString msg("Percent viewport height should range [0.0, 100.0] but value is " + QString::number(fontPercentViewportHeight)); CaretLogWarning(msg); CaretAssertMessage(0, msg); } if (fontPercentViewportHeight != m_fontPercentViewportSize) { m_fontPercentViewportSize = fontPercentViewportHeight; setModified(); } } /** * Apply a spatial modification to an annotation. * * @param spatialModification * Contains information about the spatial modification. * @return * True if the annotation was modified, else false. */ bool AnnotationText::applySpatialModification(const AnnotationSpatialModification& spatialModification) { /* * Text limits support of resize options */ bool operationSupportedFlag = 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: break; case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_LINE_START: break; case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_NONE: operationSupportedFlag = true; break; case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_ROTATION: operationSupportedFlag = true; break; } bool validFlag = false; if (operationSupportedFlag) { validFlag = AnnotationTwoDimensionalShape::applySpatialModification(spatialModification); } return validFlag; } /** * 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 AnnotationText::saveSubClassDataToScene(const SceneAttributes* sceneAttributes, SceneClass* sceneClass) { AnnotationTwoDimensionalShape::saveSubClassDataToScene(sceneAttributes, 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 AnnotationText::restoreSubClassDataFromScene(const SceneAttributes* sceneAttributes, const SceneClass* sceneClass) { AnnotationTwoDimensionalShape::restoreSubClassDataFromScene(sceneAttributes, sceneClass); m_sceneAssistant->restoreMembers(sceneAttributes, sceneClass); } /** * Set the default value for horizontal alignment. * * @param alignment * Default for newly created text annotations. */ void AnnotationText::setUserDefaultHorizontalAlignment(const AnnotationTextAlignHorizontalEnum::Enum alignment) { s_userDefaultAlignmentHorizontal = alignment; } /** * Set the default value for vertical alignment. * * @param alignment * Default for newly created text annotations. */ void AnnotationText::setUserDefaultVerticalAlignment(const AnnotationTextAlignVerticalEnum::Enum alignment) { s_userDefaultAlignmentVertical = alignment; } /** * Set the default value for the font name. * * @param font * Default for newly created text annotations. */ void AnnotationText::setUserDefaultFont(const AnnotationTextFontNameEnum::Enum font) { s_userDefaultFont = font; } /** * Set the default value for the text orientation. * * @param orientation * Default for newly created text annotations. */ void AnnotationText::setUserDefaultOrientation(const AnnotationTextOrientationEnum::Enum orientation) { s_userDefaultOrientation = orientation; } /** * Set the default value for font point size. * * @param fontPointSize * Default for newly created text annotations. */ void AnnotationText::setUserDefaultFontPointSize(const AnnotationTextFontPointSizeEnum::Enum fontPointSize) { s_userDefaultPointSize = fontPointSize; } /** * Set the default value for font percent viewport height. * * @param fontPercentViewportHeight * Default for newly created text annotations. */ void AnnotationText::setUserDefaultFontPercentViewportSize(const float fontPercentViewportHeight) { s_userDefaultFontPercentViewportSize = fontPercentViewportHeight; } /** * Set the default value for text color * * @param color * Default for newly created annotations. */ void AnnotationText::setUserDefaultTextColor(const CaretColorEnum::Enum color) { s_userDefaultColorText = color; } /** * Set the default value for custom text color * * @param rgba * Default for newly created annotations. */ void AnnotationText::setUserDefaultCustomTextColor(const float rgba[4]) { s_userDefaultCustomColorText[0] = rgba[0]; s_userDefaultCustomColorText[1] = rgba[1]; s_userDefaultCustomColorText[2] = rgba[2]; s_userDefaultCustomColorText[3] = rgba[3]; } /** * Set the default value for bold enabled. * * @param enabled * Default for newly created text annotations. */ void AnnotationText::setUserDefaultBoldEnabled(const bool enabled) { s_userDefaultBoldEnabled = enabled; } /** * Set the default value for italic enabled. * * @param enabled * Default for newly created text annotations. */ void AnnotationText::setUserDefaultItalicEnabled(const bool enabled) { s_userDefaultItalicEnabled = enabled; } /** * Set the default value for underline enabled. * * @param enabled * Default for newly created text annotations. */ void AnnotationText::setUserDefaultUnderlineEnabled(const bool enabled) { s_userDefaultUnderlineEnabled = enabled; } /** * Set the default value for connect to brainordinate. * * @param connectToBrainordinate * Default for newly created text annotations. */ void AnnotationText::setUserDefaultConnectToBrainordinate(const AnnotationTextConnectTypeEnum::Enum connectToBrainordinate) { s_userDefaultConnectToBrainordinate = connectToBrainordinate; } connectome-workbench-1.2.3+git41-gc4c6c90/src/Annotations/AnnotationText.h000066400000000000000000000243721300200146000262010ustar00rootroot00000000000000#ifndef __ANNOTATION_TEXT_H__ #define __ANNOTATION_TEXT_H__ /*LICENSE_START*/ /* * Copyright (C) 2015 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 "AnnotationFontAttributesInterface.h" #include "AnnotationTextFontPointSizeEnum.h" #include "AnnotationTextAlignHorizontalEnum.h" #include "AnnotationTextAlignVerticalEnum.h" #include "AnnotationTextFontSizeTypeEnum.h" #include "AnnotationTextConnectTypeEnum.h" #include "AnnotationTextOrientationEnum.h" #include "AnnotationTwoDimensionalShape.h" #include "CaretPointer.h" namespace caret { class AnnotationText : public AnnotationTwoDimensionalShape, public AnnotationFontAttributesInterface { public: virtual ~AnnotationText(); AnnotationText(const AnnotationText& obj); AnnotationText& operator=(const AnnotationText& obj); AString getFontRenderingEncodedName(const float drawingViewportHeight) const; AString getText() const; void setText(const AString& text); AnnotationTextAlignHorizontalEnum::Enum getHorizontalAlignment() const; void setHorizontalAlignment(const AnnotationTextAlignHorizontalEnum::Enum alignment); AnnotationTextAlignVerticalEnum::Enum getVerticalAlignment() const; void setVerticalAlignment(const AnnotationTextAlignVerticalEnum::Enum alignment); virtual AnnotationTextFontNameEnum::Enum getFont() const; virtual void setFont(const AnnotationTextFontNameEnum::Enum font); virtual float getFontPercentViewportSize() const = 0; virtual void setFontPercentViewportSize(const float fontPercentViewportHeight) = 0; AnnotationTextOrientationEnum::Enum getOrientation() const; void setOrientation(const AnnotationTextOrientationEnum::Enum orientation); int32_t getFontSizeForDrawing(const int32_t drawingViewportHeight) const; AnnotationTextFontSizeTypeEnum::Enum getFontSizeType() const; virtual CaretColorEnum::Enum getTextColor() const; virtual void setTextColor(const CaretColorEnum::Enum color); virtual void getTextColorRGBA(float rgbaOut[4]) const; virtual void getTextColorRGBA(uint8_t rgbaOut[4]) const; virtual void getCustomTextColor(float rgbaOut[4]) const; virtual void getCustomTextColor(uint8_t rgbaOut[4]) const; virtual void setCustomTextColor(const float rgba[4]); virtual void setCustomTextColor(const uint8_t rgba[4]); virtual bool isStylesSupported() const; virtual bool isBoldStyleEnabled() const; virtual void setBoldStyleEnabled(const bool enabled); virtual bool isItalicStyleEnabled() const; virtual void setItalicStyleEnabled(const bool enabled); virtual bool isUnderlineStyleEnabled() const; virtual void setUnderlineStyleEnabled(const bool enabled); AnnotationTextConnectTypeEnum::Enum getConnectToBrainordinate() const; void setConnectToBrainordinate(const AnnotationTextConnectTypeEnum::Enum connectToBrainordinate); bool isConnectToBrainordinateValid() const; virtual bool isLineWidthSupported() const; virtual bool applySpatialModification(const AnnotationSpatialModification& spatialModification); virtual void applyCoordinatesSizeAndRotationFromOther(const Annotation* otherAnnotation); virtual void getItemIconColorsRGBA(float backgroundRgbaOut[4], float outlineRgbaOut[4], float textRgbaOut[4]) const; static void setUserDefaultHorizontalAlignment(const AnnotationTextAlignHorizontalEnum::Enum alignment); static void setUserDefaultVerticalAlignment(const AnnotationTextAlignVerticalEnum::Enum alignment); static void setUserDefaultFont(const AnnotationTextFontNameEnum::Enum font); static void setUserDefaultOrientation(const AnnotationTextOrientationEnum::Enum orientation); static void setUserDefaultFontPointSize(const AnnotationTextFontPointSizeEnum::Enum fontPointSize); static void setUserDefaultFontPercentViewportSize(const float fontPercentViewportHeight); static void setUserDefaultTextColor(const CaretColorEnum::Enum color); static void setUserDefaultCustomTextColor(const float rgba[4]); static void setUserDefaultBoldEnabled(const bool enabled); static void setUserDefaultItalicEnabled(const bool enabled); static void setUserDefaultUnderlineEnabled(const bool enabled); static void setUserDefaultOutlineEnabled(const bool enabled); static void setUserDefaultConnectToBrainordinate(const AnnotationTextConnectTypeEnum::Enum connectToBrainordinate); // ADD_NEW_METHODS_HERE protected: AnnotationText(const AnnotationAttributesDefaultTypeEnum::Enum attributeDefaultType, const AnnotationTextFontSizeTypeEnum::Enum fontSizeType); virtual void saveSubClassDataToScene(const SceneAttributes* sceneAttributes, SceneClass* sceneClass); virtual void restoreSubClassDataFromScene(const SceneAttributes* sceneAttributes, const SceneClass* sceneClass); AnnotationTextFontPointSizeEnum::Enum getFontPointSizeProtected() const; void setFontPointSizeProtected(const AnnotationTextFontPointSizeEnum::Enum fontPointSize); float getFontPercentViewportSizeProtected() const; void setFontPercentViewportSizeProtected(const float fontPercentViewportHeight); private: /* Not implemented */ AnnotationText(); void copyHelperAnnotationText(const AnnotationText& obj); void initializeAnnotationTextMembers(); /* Not saved to scene since it is set by sub-class constructor. */ const AnnotationTextFontSizeTypeEnum::Enum m_fontSizeType; CaretPointer m_sceneAssistant; AString m_text; AnnotationTextAlignHorizontalEnum::Enum m_alignmentHorizontal; AnnotationTextAlignVerticalEnum::Enum m_alignmentVertical; AnnotationTextFontNameEnum::Enum m_font; AnnotationTextOrientationEnum::Enum m_orientation; AnnotationTextFontPointSizeEnum::Enum m_fontPointSize; AnnotationTextConnectTypeEnum::Enum m_connectToBrainordinate; float m_fontPercentViewportSize; CaretColorEnum::Enum m_colorText; float m_customColorText[4]; bool m_boldEnabled; bool m_italicEnabled; bool m_underlineEnabled; // Defaults static AnnotationTextAlignHorizontalEnum::Enum s_userDefaultAlignmentHorizontal; static AnnotationTextAlignVerticalEnum::Enum s_userDefaultAlignmentVertical; static AnnotationTextFontNameEnum::Enum s_userDefaultFont; static AnnotationTextOrientationEnum::Enum s_userDefaultOrientation; static AnnotationTextFontPointSizeEnum::Enum s_userDefaultPointSize; static AnnotationTextConnectTypeEnum::Enum s_userDefaultConnectToBrainordinate; static CaretColorEnum::Enum s_userDefaultColorText; static float s_userDefaultCustomColorText[4]; static float s_userDefaultFontPercentViewportSize; static bool s_userDefaultBoldEnabled; static bool s_userDefaultItalicEnabled; static bool s_userDefaultUnderlineEnabled; // ADD_NEW_MEMBERS_HERE friend class AnnotationFileXmlReader; friend class AnnotationFileXmlWriter; }; #ifdef __ANNOTATION_TEXT_DECLARE__ AnnotationTextAlignHorizontalEnum::Enum AnnotationText::s_userDefaultAlignmentHorizontal = AnnotationTextAlignHorizontalEnum::LEFT; AnnotationTextAlignVerticalEnum::Enum AnnotationText::s_userDefaultAlignmentVertical = AnnotationTextAlignVerticalEnum::TOP; AnnotationTextFontNameEnum::Enum AnnotationText::s_userDefaultFont = AnnotationTextFontNameEnum::VERA; AnnotationTextOrientationEnum::Enum AnnotationText::s_userDefaultOrientation = AnnotationTextOrientationEnum::HORIZONTAL; AnnotationTextFontPointSizeEnum::Enum AnnotationText::s_userDefaultPointSize = AnnotationTextFontPointSizeEnum::SIZE14; AnnotationTextConnectTypeEnum::Enum AnnotationText::s_userDefaultConnectToBrainordinate = AnnotationTextConnectTypeEnum::ANNOTATION_TEXT_CONNECT_NONE; CaretColorEnum::Enum AnnotationText::s_userDefaultColorText = CaretColorEnum::WHITE; float AnnotationText::s_userDefaultCustomColorText[4] = { 1.0, 1.0, 1.0, 1.0 }; float AnnotationText::s_userDefaultFontPercentViewportSize = 5.0; bool AnnotationText::s_userDefaultBoldEnabled = false; bool AnnotationText::s_userDefaultItalicEnabled = false; bool AnnotationText::s_userDefaultUnderlineEnabled = false; #endif // __ANNOTATION_TEXT_DECLARE__ } // namespace #endif //__ANNOTATION_TEXT_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Annotations/AnnotationTextAlignHorizontalEnum.cxx000066400000000000000000000264471300200146000324330ustar00rootroot00000000000000 /*LICENSE_START*/ /* * Copyright (C) 2015 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 __ANNOTATION_TEXT_ALIGN_HORIZONTAL_ENUM_DECLARE__ #include "AnnotationTextAlignHorizontalEnum.h" #undef __ANNOTATION_TEXT_ALIGN_HORIZONTAL_ENUM_DECLARE__ #include "CaretAssert.h" using namespace caret; /** * \class caret::AnnotationTextAlignHorizontalEnum * \brief Horizontal alignment for text annotations. * * 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_annotationAlignHorizontalEnumComboBox; * * Declare a slot that is called when user changes selection * private slots: * void annotationAlignHorizontalEnumComboBoxItemActivated(); * * Implementation File (.cxx) * Include the header files * #include "EnumComboBoxTemplate.h" * #include "AnnotationTextAlignHorizontalEnum.h" * * Instatiate: * m_annotationAlignHorizontalEnumComboBox = new EnumComboBoxTemplate(this); * m_annotationAlignHorizontalEnumComboBox->setup(); * * Get notified when the user changes the selection: * QObject::connect(m_annotationAlignHorizontalEnumComboBox, SIGNAL(itemActivated()), * this, SLOT(annotationAlignHorizontalEnumComboBoxItemActivated())); * * Update the selection: * m_annotationAlignHorizontalEnumComboBox->setSelectedItem(NEW_VALUE); * * Read the selection: * const AnnotationTextAlignHorizontalEnum::Enum VARIABLE = m_annotationAlignHorizontalEnumComboBox->getSelectedItem(); * */ /** * Constructor. * * @param enumValue * An enumerated value. * @param name * Name of enumerated value. * * @param guiName * User-friendly name for use in user-interface. */ AnnotationTextAlignHorizontalEnum::AnnotationTextAlignHorizontalEnum(const Enum enumValue, const AString& name, const AString& guiName) { this->enumValue = enumValue; this->integerCode = integerCodeCounter++; this->name = name; this->guiName = guiName; } /** * Destructor. */ AnnotationTextAlignHorizontalEnum::~AnnotationTextAlignHorizontalEnum() { } /** * Initialize the enumerated metadata. */ void AnnotationTextAlignHorizontalEnum::initialize() { if (initializedFlag) { return; } initializedFlag = true; enumData.push_back(AnnotationTextAlignHorizontalEnum(LEFT, "LEFT", "Left")); enumData.push_back(AnnotationTextAlignHorizontalEnum(CENTER, "CENTER", "Center")); enumData.push_back(AnnotationTextAlignHorizontalEnum(RIGHT, "RIGHT", "Right")); } /** * 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 AnnotationTextAlignHorizontalEnum* AnnotationTextAlignHorizontalEnum::findData(const Enum enumValue) { if (initializedFlag == false) initialize(); size_t num = enumData.size(); for (size_t i = 0; i < num; i++) { const AnnotationTextAlignHorizontalEnum* 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 AnnotationTextAlignHorizontalEnum::toName(Enum enumValue) { if (initializedFlag == false) initialize(); const AnnotationTextAlignHorizontalEnum* 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. */ AnnotationTextAlignHorizontalEnum::Enum AnnotationTextAlignHorizontalEnum::fromName(const AString& name, bool* isValidOut) { if (initializedFlag == false) initialize(); bool validFlag = false; Enum enumValue = AnnotationTextAlignHorizontalEnum::enumData[0].enumValue; for (std::vector::iterator iter = enumData.begin(); iter != enumData.end(); iter++) { const AnnotationTextAlignHorizontalEnum& 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 AnnotationTextAlignHorizontalEnum")); } return enumValue; } /** * Get a GUI string representation of the enumerated type. * @param enumValue * Enumerated value. * @return * String representing enumerated value. */ AString AnnotationTextAlignHorizontalEnum::toGuiName(Enum enumValue) { if (initializedFlag == false) initialize(); const AnnotationTextAlignHorizontalEnum* 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. */ AnnotationTextAlignHorizontalEnum::Enum AnnotationTextAlignHorizontalEnum::fromGuiName(const AString& guiName, bool* isValidOut) { if (initializedFlag == false) initialize(); bool validFlag = false; Enum enumValue = AnnotationTextAlignHorizontalEnum::enumData[0].enumValue; for (std::vector::iterator iter = enumData.begin(); iter != enumData.end(); iter++) { const AnnotationTextAlignHorizontalEnum& 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 AnnotationTextAlignHorizontalEnum")); } return enumValue; } /** * Get the integer code for a data type. * * @return * Integer code for data type. */ int32_t AnnotationTextAlignHorizontalEnum::toIntegerCode(Enum enumValue) { if (initializedFlag == false) initialize(); const AnnotationTextAlignHorizontalEnum* 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. */ AnnotationTextAlignHorizontalEnum::Enum AnnotationTextAlignHorizontalEnum::fromIntegerCode(const int32_t integerCode, bool* isValidOut) { if (initializedFlag == false) initialize(); bool validFlag = false; Enum enumValue = AnnotationTextAlignHorizontalEnum::enumData[0].enumValue; for (std::vector::iterator iter = enumData.begin(); iter != enumData.end(); iter++) { const AnnotationTextAlignHorizontalEnum& 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 AnnotationTextAlignHorizontalEnum")); } 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 AnnotationTextAlignHorizontalEnum::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 AnnotationTextAlignHorizontalEnum::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(AnnotationTextAlignHorizontalEnum::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 AnnotationTextAlignHorizontalEnum::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(AnnotationTextAlignHorizontalEnum::toGuiName(iter->enumValue)); } if (isSorted) { std::sort(allGuiNames.begin(), allGuiNames.end()); } } connectome-workbench-1.2.3+git41-gc4c6c90/src/Annotations/AnnotationTextAlignHorizontalEnum.h000066400000000000000000000064411300200146000320500ustar00rootroot00000000000000#ifndef __ANNOTATION_TEXT_ALIGN_HORIZONTAL_ENUM_H__ #define __ANNOTATION_TEXT_ALIGN_HORIZONTAL_ENUM_H__ /*LICENSE_START*/ /* * Copyright (C) 2015 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 AnnotationTextAlignHorizontalEnum { public: /** * Enumerated values. */ enum Enum { /** Align Left */ LEFT, /** Align Center */ CENTER, /** Align Right */ RIGHT }; ~AnnotationTextAlignHorizontalEnum(); 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: AnnotationTextAlignHorizontalEnum(const Enum enumValue, const AString& name, const AString& guiName); static const AnnotationTextAlignHorizontalEnum* 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 __ANNOTATION_TEXT_ALIGN_HORIZONTAL_ENUM_DECLARE__ std::vector AnnotationTextAlignHorizontalEnum::enumData; bool AnnotationTextAlignHorizontalEnum::initializedFlag = false; int32_t AnnotationTextAlignHorizontalEnum::integerCodeCounter = 0; #endif // __ANNOTATION_TEXT_ALIGN_HORIZONTAL_ENUM_DECLARE__ } // namespace #endif //__ANNOTATION_TEXT_ALIGN_HORIZONTAL_ENUM_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Annotations/AnnotationTextAlignVerticalEnum.cxx000066400000000000000000000262431300200146000320450ustar00rootroot00000000000000 /*LICENSE_START*/ /* * Copyright (C) 2015 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 __ANNOTATION_TEXT_ALIGN_VERTICAL_ENUM_DECLARE__ #include "AnnotationTextAlignVerticalEnum.h" #undef __ANNOTATION_TEXT_ALIGN_VERTICAL_ENUM_DECLARE__ #include "CaretAssert.h" using namespace caret; /** * \class caret::AnnotationTextAlignVerticalEnum * \brief Vertical alignment for text annotations. * * 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_annotationAlignVerticalEnumComboBox; * * Declare a slot that is called when user changes selection * private slots: * void annotationAlignVerticalEnumComboBoxItemActivated(); * * Implementation File (.cxx) * Include the header files * #include "EnumComboBoxTemplate.h" * #include "AnnotationTextAlignVerticalEnum.h" * * Instatiate: * m_annotationAlignVerticalEnumComboBox = new EnumComboBoxTemplate(this); * m_annotationAlignVerticalEnumComboBox->setup(); * * Get notified when the user changes the selection: * QObject::connect(m_annotationAlignVerticalEnumComboBox, SIGNAL(itemActivated()), * this, SLOT(annotationAlignVerticalEnumComboBoxItemActivated())); * * Update the selection: * m_annotationAlignVerticalEnumComboBox->setSelectedItem(NEW_VALUE); * * Read the selection: * const AnnotationTextAlignVerticalEnum::Enum VARIABLE = m_annotationAlignVerticalEnumComboBox->getSelectedItem(); * */ /** * Constructor. * * @param enumValue * An enumerated value. * @param name * Name of enumerated value. * * @param guiName * User-friendly name for use in user-interface. */ AnnotationTextAlignVerticalEnum::AnnotationTextAlignVerticalEnum(const Enum enumValue, const AString& name, const AString& guiName) { this->enumValue = enumValue; this->integerCode = integerCodeCounter++; this->name = name; this->guiName = guiName; } /** * Destructor. */ AnnotationTextAlignVerticalEnum::~AnnotationTextAlignVerticalEnum() { } /** * Initialize the enumerated metadata. */ void AnnotationTextAlignVerticalEnum::initialize() { if (initializedFlag) { return; } initializedFlag = true; enumData.push_back(AnnotationTextAlignVerticalEnum(BOTTOM, "BOTTOM", "Bottom")); enumData.push_back(AnnotationTextAlignVerticalEnum(MIDDLE, "MIDDLE", "Middle")); enumData.push_back(AnnotationTextAlignVerticalEnum(TOP, "TOP", "Top")); } /** * 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 AnnotationTextAlignVerticalEnum* AnnotationTextAlignVerticalEnum::findData(const Enum enumValue) { if (initializedFlag == false) initialize(); size_t num = enumData.size(); for (size_t i = 0; i < num; i++) { const AnnotationTextAlignVerticalEnum* 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 AnnotationTextAlignVerticalEnum::toName(Enum enumValue) { if (initializedFlag == false) initialize(); const AnnotationTextAlignVerticalEnum* 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. */ AnnotationTextAlignVerticalEnum::Enum AnnotationTextAlignVerticalEnum::fromName(const AString& name, bool* isValidOut) { if (initializedFlag == false) initialize(); bool validFlag = false; Enum enumValue = AnnotationTextAlignVerticalEnum::enumData[0].enumValue; for (std::vector::iterator iter = enumData.begin(); iter != enumData.end(); iter++) { const AnnotationTextAlignVerticalEnum& 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 AnnotationTextAlignVerticalEnum")); } return enumValue; } /** * Get a GUI string representation of the enumerated type. * @param enumValue * Enumerated value. * @return * String representing enumerated value. */ AString AnnotationTextAlignVerticalEnum::toGuiName(Enum enumValue) { if (initializedFlag == false) initialize(); const AnnotationTextAlignVerticalEnum* 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. */ AnnotationTextAlignVerticalEnum::Enum AnnotationTextAlignVerticalEnum::fromGuiName(const AString& guiName, bool* isValidOut) { if (initializedFlag == false) initialize(); bool validFlag = false; Enum enumValue = AnnotationTextAlignVerticalEnum::enumData[0].enumValue; for (std::vector::iterator iter = enumData.begin(); iter != enumData.end(); iter++) { const AnnotationTextAlignVerticalEnum& 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 AnnotationTextAlignVerticalEnum")); } return enumValue; } /** * Get the integer code for a data type. * * @return * Integer code for data type. */ int32_t AnnotationTextAlignVerticalEnum::toIntegerCode(Enum enumValue) { if (initializedFlag == false) initialize(); const AnnotationTextAlignVerticalEnum* 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. */ AnnotationTextAlignVerticalEnum::Enum AnnotationTextAlignVerticalEnum::fromIntegerCode(const int32_t integerCode, bool* isValidOut) { if (initializedFlag == false) initialize(); bool validFlag = false; Enum enumValue = AnnotationTextAlignVerticalEnum::enumData[0].enumValue; for (std::vector::iterator iter = enumData.begin(); iter != enumData.end(); iter++) { const AnnotationTextAlignVerticalEnum& 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 AnnotationTextAlignVerticalEnum")); } 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 AnnotationTextAlignVerticalEnum::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 AnnotationTextAlignVerticalEnum::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(AnnotationTextAlignVerticalEnum::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 AnnotationTextAlignVerticalEnum::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(AnnotationTextAlignVerticalEnum::toGuiName(iter->enumValue)); } if (isSorted) { std::sort(allGuiNames.begin(), allGuiNames.end()); } } connectome-workbench-1.2.3+git41-gc4c6c90/src/Annotations/AnnotationTextAlignVerticalEnum.h000066400000000000000000000064051300200146000314700ustar00rootroot00000000000000#ifndef __ANNOTATION_TEXT_ALIGN_VERTICAL_ENUM_H__ #define __ANNOTATION_TEXT_ALIGN_VERTICAL_ENUM_H__ /*LICENSE_START*/ /* * Copyright (C) 2015 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 AnnotationTextAlignVerticalEnum { public: /** * Enumerated values. */ enum Enum { /** Align Bottom */ BOTTOM, /** Align Middle */ MIDDLE, /** Align Top */ TOP }; ~AnnotationTextAlignVerticalEnum(); 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: AnnotationTextAlignVerticalEnum(const Enum enumValue, const AString& name, const AString& guiName); static const AnnotationTextAlignVerticalEnum* 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 __ANNOTATION_TEXT_ALIGN_VERTICAL_ENUM_DECLARE__ std::vector AnnotationTextAlignVerticalEnum::enumData; bool AnnotationTextAlignVerticalEnum::initializedFlag = false; int32_t AnnotationTextAlignVerticalEnum::integerCodeCounter = 0; #endif // __ANNOTATION_TEXT_ALIGN_VERTICAL_ENUM_DECLARE__ } // namespace #endif //__ANNOTATION_TEXT_ALIGN_VERTICAL_ENUM_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Annotations/AnnotationTextConnectTypeEnum.cxx000066400000000000000000000264051300200146000315540ustar00rootroot00000000000000 /*LICENSE_START*/ /* * Copyright (C) 2015 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 __ANNOTATION_TEXT_CONNECT_TYPE_ENUM_DECLARE__ #include "AnnotationTextConnectTypeEnum.h" #undef __ANNOTATION_TEXT_CONNECT_TYPE_ENUM_DECLARE__ #include "CaretAssert.h" using namespace caret; /** * \class caret::AnnotationTextConnectTypeEnum * \brief Enumerated type for connecting a text annotation to a stereotaxic coordinate with arrow or line * * 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_annotationTextConnectTypeEnumComboBox; * * Declare a slot that is called when user changes selection * private slots: * void annotationTextConnectTypeEnumComboBoxItemActivated(); * * Implementation File (.cxx) * Include the header files * #include "EnumComboBoxTemplate.h" * #include "AnnotationTextConnectTypeEnum.h" * * Instatiate: * m_annotationTextConnectTypeEnumComboBox = new EnumComboBoxTemplate(this); * m_annotationTextConnectTypeEnumComboBox->setup(); * * Get notified when the user changes the selection: * QObject::connect(m_annotationTextConnectTypeEnumComboBox, SIGNAL(itemActivated()), * this, SLOT(annotationTextConnectTypeEnumComboBoxItemActivated())); * * Update the selection: * m_annotationTextConnectTypeEnumComboBox->setSelectedItem(NEW_VALUE); * * Read the selection: * const AnnotationTextConnectTypeEnum::Enum VARIABLE = m_annotationTextConnectTypeEnumComboBox->getSelectedItem(); * */ /** * Constructor. * * @param enumValue * An enumerated value. * @param name * Name of enumerated value. * * @param guiName * User-friendly name for use in user-interface. */ AnnotationTextConnectTypeEnum::AnnotationTextConnectTypeEnum(const Enum enumValue, const AString& name, const AString& guiName) { this->enumValue = enumValue; this->integerCode = integerCodeCounter++; this->name = name; this->guiName = guiName; } /** * Destructor. */ AnnotationTextConnectTypeEnum::~AnnotationTextConnectTypeEnum() { } /** * Initialize the enumerated metadata. */ void AnnotationTextConnectTypeEnum::initialize() { if (initializedFlag) { return; } initializedFlag = true; enumData.push_back(AnnotationTextConnectTypeEnum(ANNOTATION_TEXT_CONNECT_NONE, "ANNOTATION_TEXT_CONNECT_NONE", "Off")); enumData.push_back(AnnotationTextConnectTypeEnum(ANNOTATION_TEXT_CONNECT_ARROW, "ANNOTATION_TEXT_CONNECT_ARROW", "Arrow")); enumData.push_back(AnnotationTextConnectTypeEnum(ANNOTATION_TEXT_CONNECT_LINE, "ANNOTATION_TEXT_CONNECT_LINE", "Line")); } /** * 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 AnnotationTextConnectTypeEnum* AnnotationTextConnectTypeEnum::findData(const Enum enumValue) { if (initializedFlag == false) initialize(); size_t num = enumData.size(); for (size_t i = 0; i < num; i++) { const AnnotationTextConnectTypeEnum* 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 AnnotationTextConnectTypeEnum::toName(Enum enumValue) { if (initializedFlag == false) initialize(); const AnnotationTextConnectTypeEnum* 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. */ AnnotationTextConnectTypeEnum::Enum AnnotationTextConnectTypeEnum::fromName(const AString& name, bool* isValidOut) { if (initializedFlag == false) initialize(); bool validFlag = false; Enum enumValue = AnnotationTextConnectTypeEnum::enumData[0].enumValue; for (std::vector::iterator iter = enumData.begin(); iter != enumData.end(); iter++) { const AnnotationTextConnectTypeEnum& 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 AnnotationTextConnectTypeEnum")); } return enumValue; } /** * Get a GUI string representation of the enumerated type. * @param enumValue * Enumerated value. * @return * String representing enumerated value. */ AString AnnotationTextConnectTypeEnum::toGuiName(Enum enumValue) { if (initializedFlag == false) initialize(); const AnnotationTextConnectTypeEnum* 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. */ AnnotationTextConnectTypeEnum::Enum AnnotationTextConnectTypeEnum::fromGuiName(const AString& guiName, bool* isValidOut) { if (initializedFlag == false) initialize(); bool validFlag = false; Enum enumValue = AnnotationTextConnectTypeEnum::enumData[0].enumValue; for (std::vector::iterator iter = enumData.begin(); iter != enumData.end(); iter++) { const AnnotationTextConnectTypeEnum& 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 AnnotationTextConnectTypeEnum")); } return enumValue; } /** * Get the integer code for a data type. * * @return * Integer code for data type. */ int32_t AnnotationTextConnectTypeEnum::toIntegerCode(Enum enumValue) { if (initializedFlag == false) initialize(); const AnnotationTextConnectTypeEnum* 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. */ AnnotationTextConnectTypeEnum::Enum AnnotationTextConnectTypeEnum::fromIntegerCode(const int32_t integerCode, bool* isValidOut) { if (initializedFlag == false) initialize(); bool validFlag = false; Enum enumValue = AnnotationTextConnectTypeEnum::enumData[0].enumValue; for (std::vector::iterator iter = enumData.begin(); iter != enumData.end(); iter++) { const AnnotationTextConnectTypeEnum& 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 AnnotationTextConnectTypeEnum")); } 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 AnnotationTextConnectTypeEnum::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 AnnotationTextConnectTypeEnum::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(AnnotationTextConnectTypeEnum::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 AnnotationTextConnectTypeEnum::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(AnnotationTextConnectTypeEnum::toGuiName(iter->enumValue)); } if (isSorted) { std::sort(allGuiNames.begin(), allGuiNames.end()); } } connectome-workbench-1.2.3+git41-gc4c6c90/src/Annotations/AnnotationTextConnectTypeEnum.h000066400000000000000000000066251300200146000312030ustar00rootroot00000000000000#ifndef __ANNOTATION_TEXT_CONNECT_TYPE_ENUM_H__ #define __ANNOTATION_TEXT_CONNECT_TYPE_ENUM_H__ /*LICENSE_START*/ /* * Copyright (C) 2015 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 AnnotationTextConnectTypeEnum { public: /** * Enumerated values. */ enum Enum { /** No connection of annotation to brainordinate */ ANNOTATION_TEXT_CONNECT_NONE, /** Connect annotation to brainordinate with arrow */ ANNOTATION_TEXT_CONNECT_ARROW, /** Connect annotation to brainordinate with line */ ANNOTATION_TEXT_CONNECT_LINE }; ~AnnotationTextConnectTypeEnum(); 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: AnnotationTextConnectTypeEnum(const Enum enumValue, const AString& name, const AString& guiName); static const AnnotationTextConnectTypeEnum* 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 __ANNOTATION_TEXT_CONNECT_TYPE_ENUM_DECLARE__ std::vector AnnotationTextConnectTypeEnum::enumData; bool AnnotationTextConnectTypeEnum::initializedFlag = false; int32_t AnnotationTextConnectTypeEnum::integerCodeCounter = 0; #endif // __ANNOTATION_TEXT_CONNECT_TYPE_ENUM_DECLARE__ } // namespace #endif //__ANNOTATION_TEXT_CONNECT_TYPE_ENUM_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Annotations/AnnotationTextFontNameEnum.cxx000066400000000000000000000347501300200146000310320ustar00rootroot00000000000000 /*LICENSE_START*/ /* * Copyright (C) 2015 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 __ANNOTATION_TEXT_FONT_NAME_ENUM_DECLARE__ #include "AnnotationTextFontNameEnum.h" #undef __ANNOTATION_TEXT_FONT_NAME_ENUM_DECLARE__ #include "CaretAssert.h" using namespace caret; /** * \class caret::AnnotationTextFontNameEnum * \brief Names of annotation fonts * * 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_annotationFontNameEnumComboBox; * * Declare a slot that is called when user changes selection * private slots: * void annotationFontNameEnumComboBoxItemActivated(); * * Implementation File (.cxx) * Include the header files * #include "EnumComboBoxTemplate.h" * #include "AnnotationTextFontNameEnum.h" * * Instatiate: * m_annotationFontNameEnumComboBox = new EnumComboBoxTemplate(this); * m_annotationFontNameEnumComboBox->setup(); * * Get notified when the user changes the selection: * QObject::connect(m_annotationFontNameEnumComboBox, SIGNAL(itemActivated()), * this, SLOT(annotationFontNameEnumComboBoxItemActivated())); * * Update the selection: * m_annotationFontNameEnumComboBox->setSelectedItem(NEW_VALUE); * * Read the selection: * const AnnotationTextFontNameEnum::Enum VARIABLE = m_annotationFontNameEnumComboBox->getSelectedItem(); * */ /** * Constructor. * * @param enumValue * An enumerated value. * @param name * Name of enumerated value. * * @param guiName * User-friendly name for use in user-interface. */ AnnotationTextFontNameEnum::AnnotationTextFontNameEnum(const Enum enumValue, const AString& name, const AString& guiName, const AString& fontFileName, const AString& boldFontFileName, const AString& boldItalicFontFileName, const AString& italicFontFileName) { this->enumValue = enumValue; this->integerCode = integerCodeCounter++; this->name = name; this->guiName = guiName; this->resourceFontFileName = fontFileName; this->resourceBoldFontFileName = boldFontFileName; this->resourceBoldItalicFontFileName = boldItalicFontFileName; this->resourceItalicFontFileName = italicFontFileName; } /** * Destructor. */ AnnotationTextFontNameEnum::~AnnotationTextFontNameEnum() { } /** * Initialize the enumerated metadata. */ void AnnotationTextFontNameEnum::initialize() { if (initializedFlag) { return; } initializedFlag = true; enumData.push_back(AnnotationTextFontNameEnum(LIBERTINE, "LIBERTINE", "Libertine", ":/Fonts/LinuxLibertine/LinLibertine_Rah.ttf", ":/Fonts/LinuxLibertine/LinLibertine_RBah.ttf", ":/Fonts/LinuxLibertine/LinLibertine_RBIah.ttf", ":/Fonts/LinuxLibertine/LinLibertine_RIah.ttf")); enumData.push_back(AnnotationTextFontNameEnum(VERA, "VERA", "Vera", ":/Fonts/VeraFonts/Vera.ttf", ":/Fonts/VeraFonts/VeraBd.ttf", ":/Fonts/VeraFonts/VeraBI.ttf", ":/Fonts/VeraFonts/VeraIt.ttf")); enumData.push_back(AnnotationTextFontNameEnum(VERA_MONOSPACE, "VERA_MONOSPACE", "Vera Mono", ":/Fonts/VeraFonts/VeraMono.ttf", ":/Fonts/VeraFonts/VeraMoBd.ttf", ":/Fonts/VeraFonts/VeraMoBI.ttf", ":/Fonts/VeraFonts/VeraMoIt.ttf")); } /** * @return The default font name. */ AnnotationTextFontNameEnum::Enum AnnotationTextFontNameEnum::getDefaultFontName() { if (initializedFlag == false) initialize(); CaretAssert( ! enumData.empty()); return AnnotationTextFontNameEnum::VERA; // return enumData[0].enumValue; } /** * 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 AnnotationTextFontNameEnum* AnnotationTextFontNameEnum::findData(const Enum enumValue) { if (initializedFlag == false) initialize(); size_t num = enumData.size(); for (size_t i = 0; i < num; i++) { const AnnotationTextFontNameEnum* 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 AnnotationTextFontNameEnum::toName(Enum enumValue) { if (initializedFlag == false) initialize(); const AnnotationTextFontNameEnum* 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. */ AnnotationTextFontNameEnum::Enum AnnotationTextFontNameEnum::fromName(const AString& name, bool* isValidOut) { if (initializedFlag == false) initialize(); bool validFlag = false; Enum enumValue = getDefaultFontName(); for (std::vector::iterator iter = enumData.begin(); iter != enumData.end(); iter++) { const AnnotationTextFontNameEnum& 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 AnnotationTextFontNameEnum")); } return enumValue; } /** * Get a GUI string representation of the enumerated type. * @param enumValue * Enumerated value. * @return * String representing enumerated value. */ AString AnnotationTextFontNameEnum::toGuiName(Enum enumValue) { if (initializedFlag == false) initialize(); const AnnotationTextFontNameEnum* 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. */ AnnotationTextFontNameEnum::Enum AnnotationTextFontNameEnum::fromGuiName(const AString& guiName, bool* isValidOut) { if (initializedFlag == false) initialize(); bool validFlag = false; Enum enumValue = getDefaultFontName(); for (std::vector::iterator iter = enumData.begin(); iter != enumData.end(); iter++) { const AnnotationTextFontNameEnum& 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 AnnotationTextFontNameEnum")); } return enumValue; } /** * Get the integer code for a data type. * * @return * Integer code for data type. */ int32_t AnnotationTextFontNameEnum::toIntegerCode(Enum enumValue) { if (initializedFlag == false) initialize(); const AnnotationTextFontNameEnum* 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. */ AnnotationTextFontNameEnum::Enum AnnotationTextFontNameEnum::fromIntegerCode(const int32_t integerCode, bool* isValidOut) { if (initializedFlag == false) initialize(); bool validFlag = false; Enum enumValue = getDefaultFontName(); for (std::vector::iterator iter = enumData.begin(); iter != enumData.end(); iter++) { const AnnotationTextFontNameEnum& 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 AnnotationTextFontNameEnum")); } 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 AnnotationTextFontNameEnum::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 AnnotationTextFontNameEnum::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(AnnotationTextFontNameEnum::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 AnnotationTextFontNameEnum::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(AnnotationTextFontNameEnum::toGuiName(iter->enumValue)); } if (isSorted) { std::sort(allGuiNames.begin(), allGuiNames.end()); } } /** * Get the resource file name for the regular font file. * * @param enumValue * Enumerated value. * @return * String containing name of font file in resources. */ AString AnnotationTextFontNameEnum::getResourceFontFileName(Enum enumValue) { if (initializedFlag == false) initialize(); const AnnotationTextFontNameEnum* enumInstance = findData(enumValue); return enumInstance->resourceFontFileName; } /** * Get the resource file name for the bold font file. * * @param enumValue * Enumerated value. * @return * String containing name of font file in resources. */ AString AnnotationTextFontNameEnum::getResourceBoldFontFileName(Enum enumValue) { if (initializedFlag == false) initialize(); const AnnotationTextFontNameEnum* enumInstance = findData(enumValue); return enumInstance->resourceBoldFontFileName; } /** * Get the resource file name for the bold italic font file. * * @param enumValue * Enumerated value. * @return * String containing name of font file in resources. */ AString AnnotationTextFontNameEnum::getResourceBoldItalicFontFileName(Enum enumValue) { if (initializedFlag == false) initialize(); const AnnotationTextFontNameEnum* enumInstance = findData(enumValue); return enumInstance->resourceBoldItalicFontFileName; } /** * Get the resource file name for the italic font file. * * @param enumValue * Enumerated value. * @return * String containing name of font file in resources. */ AString AnnotationTextFontNameEnum::getResourceItalicFontFileName(Enum enumValue) { if (initializedFlag == false) initialize(); const AnnotationTextFontNameEnum* enumInstance = findData(enumValue); return enumInstance->resourceItalicFontFileName; } connectome-workbench-1.2.3+git41-gc4c6c90/src/Annotations/AnnotationTextFontNameEnum.h000066400000000000000000000101111300200146000304400ustar00rootroot00000000000000#ifndef __ANNOTATION_TEXT_FONT_NAME_ENUM_H__ #define __ANNOTATION_TEXT_FONT_NAME_ENUM_H__ /*LICENSE_START*/ /* * Copyright (C) 2015 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 AnnotationTextFontNameEnum { public: /** * Enumerated values. */ enum Enum { /** Libertine */ LIBERTINE, /** Vera Fonts */ VERA, /** Vera Monospaced Fonts */ VERA_MONOSPACE }; ~AnnotationTextFontNameEnum(); static Enum getDefaultFontName(); 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 getResourceFontFileName(Enum enumValue); static AString getResourceBoldFontFileName(Enum enumValue); static AString getResourceBoldItalicFontFileName(Enum enumValue); static AString getResourceItalicFontFileName(Enum enumValue); private: AnnotationTextFontNameEnum(const Enum enumValue, const AString& name, const AString& guiName, const AString& fontFileName, const AString& boldFontFileName, const AString& boldItalicFontFileName, const AString& italicFontFileName); static const AnnotationTextFontNameEnum* 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; /** Name of font file */ AString resourceFontFileName; /** Name of bold font file */ AString resourceBoldFontFileName; /** Name of bold italic font file */ AString resourceBoldItalicFontFileName; /** Name of italic font file */ AString resourceItalicFontFileName; }; #ifdef __ANNOTATION_TEXT_FONT_NAME_ENUM_DECLARE__ std::vector AnnotationTextFontNameEnum::enumData; bool AnnotationTextFontNameEnum::initializedFlag = false; int32_t AnnotationTextFontNameEnum::integerCodeCounter = 0; #endif // __ANNOTATION_TEXT_FONT_NAME_ENUM_DECLARE__ } // namespace #endif //__ANNOTATION_TEXT_FONT_NAME_ENUM_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Annotations/AnnotationTextFontPointSizeEnum.cxx000066400000000000000000000370731300200146000320770ustar00rootroot00000000000000 /*LICENSE_START*/ /* * Copyright (C) 2015 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 __ANNOTATION_TEXT_FONT_POINT_SIZE_ENUM_DECLARE__ #include "AnnotationTextFontPointSizeEnum.h" #undef __ANNOTATION_TEXT_FONT_POINT_SIZE_ENUM_DECLARE__ #include "CaretAssert.h" using namespace caret; /** * \class caret::AnnotationTextFontPointSizeEnum * \brief Enumerated type for font size. * * * * 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_annotationFontSizeEnumComboBox; * * Declare a slot that is called when user changes selection * private slots: * void annotationFontSizeEnumComboBoxItemActivated(); * * Implementation File (.cxx) * Include the header files * #include "EnumComboBoxTemplate.h" * #include "AnnotationTextFontPointSizeEnum.h" * * Instatiate: * m_annotationFontSizeEnumComboBox = new EnumComboBoxTemplate(this); * m_annotationFontSizeEnumComboBox->setup(); * * Get notified when the user changes the selection: * QObject::connect(m_annotationFontSizeEnumComboBox, SIGNAL(itemActivated()), * this, SLOT(annotationFontSizeEnumComboBoxItemActivated())); * * Update the selection: * m_annotationFontSizeEnumComboBox->setSelectedItem(NEW_VALUE); * * Read the selection: * const AnnotationTextFontPointSizeEnum::Enum VARIABLE = m_annotationFontSizeEnumComboBox->getSelectedItem(); * */ /** * Constructor. * * @param enumValue * An enumerated value. * @param name * Name of enumerated value. * * @param guiName * User-friendly name for use in user-interface. * @param sizeNumeric * Numerical size of font. */ AnnotationTextFontPointSizeEnum::AnnotationTextFontPointSizeEnum(const Enum enumValue, const AString& name, const AString& guiName, const int32_t sizeNumeric) { this->enumValue = enumValue; this->integerCode = integerCodeCounter++; this->name = name; this->guiName = guiName; this->sizeNumeric = sizeNumeric; } /** * Destructor. */ AnnotationTextFontPointSizeEnum::~AnnotationTextFontPointSizeEnum() { } /** * Initialize the enumerated metadata. */ void AnnotationTextFontPointSizeEnum::initialize() { if (initializedFlag) { return; } initializedFlag = true; enumData.push_back(AnnotationTextFontPointSizeEnum(SIZE10, "SIZE10", "10", 10)); enumData.push_back(AnnotationTextFontPointSizeEnum(SIZE12, "SIZE12", "12", 12)); enumData.push_back(AnnotationTextFontPointSizeEnum(SIZE14, "SIZE14", "14", 14)); enumData.push_back(AnnotationTextFontPointSizeEnum(SIZE16, "SIZE16", "16", 16)); enumData.push_back(AnnotationTextFontPointSizeEnum(SIZE18, "SIZE18", "18", 18)); enumData.push_back(AnnotationTextFontPointSizeEnum(SIZE20, "SIZE20", "20", 20)); enumData.push_back(AnnotationTextFontPointSizeEnum(SIZE24, "SIZE24", "24", 24)); enumData.push_back(AnnotationTextFontPointSizeEnum(SIZE28, "SIZE28", "28", 28)); enumData.push_back(AnnotationTextFontPointSizeEnum(SIZE32, "SIZE32", "32", 32)); enumData.push_back(AnnotationTextFontPointSizeEnum(SIZE36, "SIZE36", "36", 36)); enumData.push_back(AnnotationTextFontPointSizeEnum(SIZE40, "SIZE40", "40", 40)); enumData.push_back(AnnotationTextFontPointSizeEnum(SIZE44, "SIZE44", "44", 44)); enumData.push_back(AnnotationTextFontPointSizeEnum(SIZE48, "SIZE48", "48", 48)); enumData.push_back(AnnotationTextFontPointSizeEnum(SIZE54, "SIZE54", "54", 54)); enumData.push_back(AnnotationTextFontPointSizeEnum(SIZE60, "SIZE60", "60", 60)); enumData.push_back(AnnotationTextFontPointSizeEnum(SIZE66, "SIZE66", "66", 66)); enumData.push_back(AnnotationTextFontPointSizeEnum(SIZE72, "SIZE72", "72", 72)); for (std::vector::iterator iter = enumData.begin(); iter != enumData.end(); iter++) { if (minimumNumericSize < 0) { minimumNumericSize = iter->sizeNumeric; } else if (iter->sizeNumeric < minimumNumericSize) { minimumNumericSize = iter->sizeNumeric; } } } /** * 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 AnnotationTextFontPointSizeEnum* AnnotationTextFontPointSizeEnum::findData(const Enum enumValue) { if (initializedFlag == false) initialize(); size_t num = enumData.size(); for (size_t i = 0; i < num; i++) { const AnnotationTextFontPointSizeEnum* 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 AnnotationTextFontPointSizeEnum::toName(Enum enumValue) { if (initializedFlag == false) initialize(); const AnnotationTextFontPointSizeEnum* 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. */ AnnotationTextFontPointSizeEnum::Enum AnnotationTextFontPointSizeEnum::fromName(const AString& name, bool* isValidOut) { if (initializedFlag == false) initialize(); bool validFlag = false; Enum enumValue = AnnotationTextFontPointSizeEnum::enumData[0].enumValue; for (std::vector::iterator iter = enumData.begin(); iter != enumData.end(); iter++) { const AnnotationTextFontPointSizeEnum& 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 AnnotationTextFontPointSizeEnum")); } return enumValue; } /** * Get a GUI string representation of the enumerated type. * @param enumValue * Enumerated value. * @return * String representing enumerated value. */ AString AnnotationTextFontPointSizeEnum::toGuiName(Enum enumValue) { if (initializedFlag == false) initialize(); const AnnotationTextFontPointSizeEnum* 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. */ AnnotationTextFontPointSizeEnum::Enum AnnotationTextFontPointSizeEnum::fromGuiName(const AString& guiName, bool* isValidOut) { if (initializedFlag == false) initialize(); bool validFlag = false; Enum enumValue = AnnotationTextFontPointSizeEnum::enumData[0].enumValue; for (std::vector::iterator iter = enumData.begin(); iter != enumData.end(); iter++) { const AnnotationTextFontPointSizeEnum& 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 AnnotationTextFontPointSizeEnum")); } return enumValue; } /** * Get the integer code for a data type. * * @return * Integer code for data type. */ int32_t AnnotationTextFontPointSizeEnum::toIntegerCode(Enum enumValue) { if (initializedFlag == false) initialize(); const AnnotationTextFontPointSizeEnum* 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. */ AnnotationTextFontPointSizeEnum::Enum AnnotationTextFontPointSizeEnum::fromIntegerCode(const int32_t integerCode, bool* isValidOut) { if (initializedFlag == false) initialize(); bool validFlag = false; Enum enumValue = AnnotationTextFontPointSizeEnum::enumData[0].enumValue; for (std::vector::iterator iter = enumData.begin(); iter != enumData.end(); iter++) { const AnnotationTextFontPointSizeEnum& 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 AnnotationTextFontPointSizeEnum")); } return enumValue; } /** * Get the integer size for a data type. * * @return * Integer size for data type. */ int32_t AnnotationTextFontPointSizeEnum::toSizeNumeric(Enum enumValue) { if (initializedFlag == false) initialize(); const AnnotationTextFontPointSizeEnum* enumInstance = findData(enumValue); return enumInstance->sizeNumeric; } /** * @return The minimum font size numeric value. */ int32_t AnnotationTextFontPointSizeEnum::getMinimumSizeNumeric() { if (initializedFlag == false) initialize(); return minimumNumericSize; } /** * 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 AnnotationTextFontPointSizeEnum::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 AnnotationTextFontPointSizeEnum::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(AnnotationTextFontPointSizeEnum::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 AnnotationTextFontPointSizeEnum::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(AnnotationTextFontPointSizeEnum::toGuiName(iter->enumValue)); } if (isSorted) { std::sort(allGuiNames.begin(), allGuiNames.end()); } } connectome-workbench-1.2.3+git41-gc4c6c90/src/Annotations/AnnotationTextFontPointSizeEnum.h000066400000000000000000000100121300200146000315040ustar00rootroot00000000000000#ifndef __ANNOTATION_TEXT_FONT_POINT_SIZE_ENUM_H__ #define __ANNOTATION_TEXT_FONT_POINT_SIZE_ENUM_H__ /*LICENSE_START*/ /* * Copyright (C) 2015 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 AnnotationTextFontPointSizeEnum { public: /** * Enumerated values. */ enum Enum { /** */ SIZE10, /** */ SIZE12, /** */ SIZE14, /** */ SIZE16, /** */ SIZE18, /** */ SIZE20, /** */ SIZE24, /** */ SIZE28, /** */ SIZE32, /** */ SIZE36, /** */ SIZE40, /** */ SIZE44, /** */ SIZE48, /** */ SIZE54, /** */ SIZE60, /** */ SIZE66, /** */ SIZE72 }; ~AnnotationTextFontPointSizeEnum(); 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 int32_t toSizeNumeric(Enum enumValue); static int32_t getMinimumSizeNumeric(); private: AnnotationTextFontPointSizeEnum(const Enum enumValue, const AString& name, const AString& guiName, const int32_t sizeNumeric); static const AnnotationTextFontPointSizeEnum* 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; static int32_t minimumNumericSize; /** 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; /** Numerical size of font */ int32_t sizeNumeric; }; #ifdef __ANNOTATION_TEXT_FONT_POINT_SIZE_ENUM_DECLARE__ std::vector AnnotationTextFontPointSizeEnum::enumData; bool AnnotationTextFontPointSizeEnum::initializedFlag = false; int32_t AnnotationTextFontPointSizeEnum::integerCodeCounter = 0; int32_t AnnotationTextFontPointSizeEnum::minimumNumericSize = -1; #endif // __ANNOTATION_TEXT_FONT_POINT_SIZE_ENUM_DECLARE__ } // namespace #endif //__ANNOTATION_TEXT_FONT_POINT_SIZE_ENUM_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Annotations/AnnotationTextFontSizeTypeEnum.cxx000066400000000000000000000260241300200146000317210ustar00rootroot00000000000000 /*LICENSE_START*/ /* * Copyright (C) 2015 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 __ANNOTATION_TEXT_FONT_SIZE_TYPE_ENUM_DECLARE__ #include "AnnotationTextFontSizeTypeEnum.h" #undef __ANNOTATION_TEXT_FONT_SIZE_TYPE_ENUM_DECLARE__ #include "CaretAssert.h" using namespace caret; /** * \class caret::AnnotationTextFontSizeTypeEnum * \brief * * * * 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_annotationFontSizeTypeEnumComboBox; * * Declare a slot that is called when user changes selection * private slots: * void annotationFontSizeTypeEnumComboBoxItemActivated(); * * Implementation File (.cxx) * Include the header files * #include "EnumComboBoxTemplate.h" * #include "AnnotationTextFontSizeTypeEnum.h" * * Instatiate: * m_annotationFontSizeTypeEnumComboBox = new EnumComboBoxTemplate(this); * m_annotationFontSizeTypeEnumComboBox->setup(); * * Get notified when the user changes the selection: * QObject::connect(m_annotationFontSizeTypeEnumComboBox, SIGNAL(itemActivated()), * this, SLOT(annotationFontSizeTypeEnumComboBoxItemActivated())); * * Update the selection: * m_annotationFontSizeTypeEnumComboBox->setSelectedItem(NEW_VALUE); * * Read the selection: * const AnnotationTextFontSizeTypeEnum::Enum VARIABLE = m_annotationFontSizeTypeEnumComboBox->getSelectedItem(); * */ /** * Constructor. * * @param enumValue * An enumerated value. * @param name * Name of enumerated value. * * @param guiName * User-friendly name for use in user-interface. */ AnnotationTextFontSizeTypeEnum::AnnotationTextFontSizeTypeEnum(const Enum enumValue, const AString& name, const AString& guiName) { this->enumValue = enumValue; this->integerCode = integerCodeCounter++; this->name = name; this->guiName = guiName; } /** * Destructor. */ AnnotationTextFontSizeTypeEnum::~AnnotationTextFontSizeTypeEnum() { } /** * Initialize the enumerated metadata. */ void AnnotationTextFontSizeTypeEnum::initialize() { if (initializedFlag) { return; } initializedFlag = true; enumData.push_back(AnnotationTextFontSizeTypeEnum(POINTS, "POINTS", "")); enumData.push_back(AnnotationTextFontSizeTypeEnum(PERCENTAGE_OF_VIEWPORT_HEIGHT, "PERCENTAGE_OF_VIEWPORT_HEIGHT", "")); } /** * 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 AnnotationTextFontSizeTypeEnum* AnnotationTextFontSizeTypeEnum::findData(const Enum enumValue) { if (initializedFlag == false) initialize(); size_t num = enumData.size(); for (size_t i = 0; i < num; i++) { const AnnotationTextFontSizeTypeEnum* 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 AnnotationTextFontSizeTypeEnum::toName(Enum enumValue) { if (initializedFlag == false) initialize(); const AnnotationTextFontSizeTypeEnum* 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. */ AnnotationTextFontSizeTypeEnum::Enum AnnotationTextFontSizeTypeEnum::fromName(const AString& name, bool* isValidOut) { if (initializedFlag == false) initialize(); bool validFlag = false; Enum enumValue = AnnotationTextFontSizeTypeEnum::enumData[0].enumValue; for (std::vector::iterator iter = enumData.begin(); iter != enumData.end(); iter++) { const AnnotationTextFontSizeTypeEnum& 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 AnnotationTextFontSizeTypeEnum")); } return enumValue; } /** * Get a GUI string representation of the enumerated type. * @param enumValue * Enumerated value. * @return * String representing enumerated value. */ AString AnnotationTextFontSizeTypeEnum::toGuiName(Enum enumValue) { if (initializedFlag == false) initialize(); const AnnotationTextFontSizeTypeEnum* 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. */ AnnotationTextFontSizeTypeEnum::Enum AnnotationTextFontSizeTypeEnum::fromGuiName(const AString& guiName, bool* isValidOut) { if (initializedFlag == false) initialize(); bool validFlag = false; Enum enumValue = AnnotationTextFontSizeTypeEnum::enumData[0].enumValue; for (std::vector::iterator iter = enumData.begin(); iter != enumData.end(); iter++) { const AnnotationTextFontSizeTypeEnum& 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 AnnotationTextFontSizeTypeEnum")); } return enumValue; } /** * Get the integer code for a data type. * * @return * Integer code for data type. */ int32_t AnnotationTextFontSizeTypeEnum::toIntegerCode(Enum enumValue) { if (initializedFlag == false) initialize(); const AnnotationTextFontSizeTypeEnum* 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. */ AnnotationTextFontSizeTypeEnum::Enum AnnotationTextFontSizeTypeEnum::fromIntegerCode(const int32_t integerCode, bool* isValidOut) { if (initializedFlag == false) initialize(); bool validFlag = false; Enum enumValue = AnnotationTextFontSizeTypeEnum::enumData[0].enumValue; for (std::vector::iterator iter = enumData.begin(); iter != enumData.end(); iter++) { const AnnotationTextFontSizeTypeEnum& 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 AnnotationTextFontSizeTypeEnum")); } 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 AnnotationTextFontSizeTypeEnum::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 AnnotationTextFontSizeTypeEnum::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(AnnotationTextFontSizeTypeEnum::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 AnnotationTextFontSizeTypeEnum::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(AnnotationTextFontSizeTypeEnum::toGuiName(iter->enumValue)); } if (isSorted) { std::sort(allGuiNames.begin(), allGuiNames.end()); } } connectome-workbench-1.2.3+git41-gc4c6c90/src/Annotations/AnnotationTextFontSizeTypeEnum.h000066400000000000000000000063231300200146000313460ustar00rootroot00000000000000#ifndef __ANNOTATION_TEXT_FONT_SIZE_TYPE_ENUM_H__ #define __ANNOTATION_TEXT_FONT_SIZE_TYPE_ENUM_H__ /*LICENSE_START*/ /* * Copyright (C) 2015 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 AnnotationTextFontSizeTypeEnum { public: /** * Enumerated values. */ enum Enum { /** */ POINTS, /** */ PERCENTAGE_OF_VIEWPORT_HEIGHT }; ~AnnotationTextFontSizeTypeEnum(); 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: AnnotationTextFontSizeTypeEnum(const Enum enumValue, const AString& name, const AString& guiName); static const AnnotationTextFontSizeTypeEnum* 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 __ANNOTATION_TEXT_FONT_SIZE_TYPE_ENUM_DECLARE__ std::vector AnnotationTextFontSizeTypeEnum::enumData; bool AnnotationTextFontSizeTypeEnum::initializedFlag = false; int32_t AnnotationTextFontSizeTypeEnum::integerCodeCounter = 0; #endif // __ANNOTATION_TEXT_FONT_SIZE_TYPE_ENUM_DECLARE__ } // namespace #endif //__ANNOTATION_TEXT_FONT_SIZE_TYPE_ENUM_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Annotations/AnnotationTextOrientationEnum.cxx000066400000000000000000000257361300200146000316220ustar00rootroot00000000000000 /*LICENSE_START*/ /* * Copyright (C) 2015 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 __ANNOTATION_TEXT_ORIENTATION_ENUM_DECLARE__ #include "AnnotationTextOrientationEnum.h" #undef __ANNOTATION_TEXT_ORIENTATION_ENUM_DECLARE__ #include "CaretAssert.h" using namespace caret; /** * \class caret::AnnotationTextOrientationEnum * \brief * * * * 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_annotationTextOrientationEnumComboBox; * * Declare a slot that is called when user changes selection * private slots: * void annotationTextOrientationEnumComboBoxItemActivated(); * * Implementation File (.cxx) * Include the header files * #include "EnumComboBoxTemplate.h" * #include "AnnotationTextOrientationEnum.h" * * Instatiate: * m_annotationTextOrientationEnumComboBox = new EnumComboBoxTemplate(this); * m_annotationTextOrientationEnumComboBox->setup(); * * Get notified when the user changes the selection: * QObject::connect(m_annotationTextOrientationEnumComboBox, SIGNAL(itemActivated()), * this, SLOT(annotationTextOrientationEnumComboBoxItemActivated())); * * Update the selection: * m_annotationTextOrientationEnumComboBox->setSelectedItem(NEW_VALUE); * * Read the selection: * const AnnotationTextOrientationEnum::Enum VARIABLE = m_annotationTextOrientationEnumComboBox->getSelectedItem(); * */ /** * Constructor. * * @param enumValue * An enumerated value. * @param name * Name of enumerated value. * * @param guiName * User-friendly name for use in user-interface. */ AnnotationTextOrientationEnum::AnnotationTextOrientationEnum(const Enum enumValue, const AString& name, const AString& guiName) { this->enumValue = enumValue; this->integerCode = integerCodeCounter++; this->name = name; this->guiName = guiName; } /** * Destructor. */ AnnotationTextOrientationEnum::~AnnotationTextOrientationEnum() { } /** * Initialize the enumerated metadata. */ void AnnotationTextOrientationEnum::initialize() { if (initializedFlag) { return; } initializedFlag = true; enumData.push_back(AnnotationTextOrientationEnum(HORIZONTAL, "HORIZONTAL", "Horizontal")); enumData.push_back(AnnotationTextOrientationEnum(STACKED, "STACKED", "Stacked")); } /** * 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 AnnotationTextOrientationEnum* AnnotationTextOrientationEnum::findData(const Enum enumValue) { if (initializedFlag == false) initialize(); size_t num = enumData.size(); for (size_t i = 0; i < num; i++) { const AnnotationTextOrientationEnum* 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 AnnotationTextOrientationEnum::toName(Enum enumValue) { if (initializedFlag == false) initialize(); const AnnotationTextOrientationEnum* 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. */ AnnotationTextOrientationEnum::Enum AnnotationTextOrientationEnum::fromName(const AString& name, bool* isValidOut) { if (initializedFlag == false) initialize(); bool validFlag = false; Enum enumValue = AnnotationTextOrientationEnum::enumData[0].enumValue; for (std::vector::iterator iter = enumData.begin(); iter != enumData.end(); iter++) { const AnnotationTextOrientationEnum& 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 AnnotationTextOrientationEnum")); } return enumValue; } /** * Get a GUI string representation of the enumerated type. * @param enumValue * Enumerated value. * @return * String representing enumerated value. */ AString AnnotationTextOrientationEnum::toGuiName(Enum enumValue) { if (initializedFlag == false) initialize(); const AnnotationTextOrientationEnum* 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. */ AnnotationTextOrientationEnum::Enum AnnotationTextOrientationEnum::fromGuiName(const AString& guiName, bool* isValidOut) { if (initializedFlag == false) initialize(); bool validFlag = false; Enum enumValue = AnnotationTextOrientationEnum::enumData[0].enumValue; for (std::vector::iterator iter = enumData.begin(); iter != enumData.end(); iter++) { const AnnotationTextOrientationEnum& 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 AnnotationTextOrientationEnum")); } return enumValue; } /** * Get the integer code for a data type. * * @return * Integer code for data type. */ int32_t AnnotationTextOrientationEnum::toIntegerCode(Enum enumValue) { if (initializedFlag == false) initialize(); const AnnotationTextOrientationEnum* 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. */ AnnotationTextOrientationEnum::Enum AnnotationTextOrientationEnum::fromIntegerCode(const int32_t integerCode, bool* isValidOut) { if (initializedFlag == false) initialize(); bool validFlag = false; Enum enumValue = AnnotationTextOrientationEnum::enumData[0].enumValue; for (std::vector::iterator iter = enumData.begin(); iter != enumData.end(); iter++) { const AnnotationTextOrientationEnum& 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 AnnotationTextOrientationEnum")); } 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 AnnotationTextOrientationEnum::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 AnnotationTextOrientationEnum::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(AnnotationTextOrientationEnum::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 AnnotationTextOrientationEnum::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(AnnotationTextOrientationEnum::toGuiName(iter->enumValue)); } if (isSorted) { std::sort(allGuiNames.begin(), allGuiNames.end()); } } connectome-workbench-1.2.3+git41-gc4c6c90/src/Annotations/AnnotationTextOrientationEnum.h000066400000000000000000000062741300200146000312430ustar00rootroot00000000000000#ifndef __ANNOTATION_TEXT_ORIENTATION_ENUM_H__ #define __ANNOTATION_TEXT_ORIENTATION_ENUM_H__ /*LICENSE_START*/ /* * Copyright (C) 2015 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 AnnotationTextOrientationEnum { public: /** * Enumerated values. */ enum Enum { /** Horizontal */ HORIZONTAL, /** Stacked */ STACKED }; ~AnnotationTextOrientationEnum(); 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: AnnotationTextOrientationEnum(const Enum enumValue, const AString& name, const AString& guiName); static const AnnotationTextOrientationEnum* 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 __ANNOTATION_TEXT_ORIENTATION_ENUM_DECLARE__ std::vector AnnotationTextOrientationEnum::enumData; bool AnnotationTextOrientationEnum::initializedFlag = false; int32_t AnnotationTextOrientationEnum::integerCodeCounter = 0; #endif // __ANNOTATION_TEXT_ORIENTATION_ENUM_DECLARE__ } // namespace #endif //__ANNOTATION_TEXT_ORIENTATION_ENUM_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Annotations/AnnotationTwoDimensionalShape.cxx000066400000000000000000001501111300200146000315340ustar00rootroot00000000000000 /*LICENSE_START*/ /* * Copyright (C) 2015 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 __ANNOTATION_TWO_DIMENSIONAL_SHAPE_DECLARE__ #include "AnnotationTwoDimensionalShape.h" #undef __ANNOTATION_TWO_DIMENSIONAL_SHAPE_DECLARE__ #include "AnnotationColorBar.h" #include "AnnotationCoordinate.h" #include "AnnotationSpatialModification.h" #include "CaretAssert.h" #include "CaretLogger.h" #include "MathFunctions.h" #include "Matrix4x4.h" #include "SceneClass.h" #include "SceneClassAssistant.h" using namespace caret; /** * \class caret::AnnotationTwoDimensionalShape * \brief Class for annotations that are two dimensional (width and height). * \ingroup Annotations */ /** * Constructor. * * @param type * Type of annotation * @param attributeDefaultType * Type for attribute defaults */ AnnotationTwoDimensionalShape::AnnotationTwoDimensionalShape(const AnnotationTypeEnum::Enum type, const AnnotationAttributesDefaultTypeEnum::Enum attributeDefaultType) : Annotation(type, attributeDefaultType) { initializeMembersAnnotationTwoDimensionalShape(); } /** * Destructor. */ AnnotationTwoDimensionalShape::~AnnotationTwoDimensionalShape() { } /** * Copy constructor. * @param obj * Object that is copied. */ AnnotationTwoDimensionalShape::AnnotationTwoDimensionalShape(const AnnotationTwoDimensionalShape& obj) : Annotation(obj) { initializeMembersAnnotationTwoDimensionalShape(); this->copyHelperAnnotationTwoDimensionalShape(obj); } /** * Assignment operator. * @param obj * Data copied from obj to this. * @return * Reference to this object. */ AnnotationTwoDimensionalShape& AnnotationTwoDimensionalShape::operator=(const AnnotationTwoDimensionalShape& obj) { if (this != &obj) { Annotation::operator=(obj); this->copyHelperAnnotationTwoDimensionalShape(obj); } return *this; } /** * Helps with copying an object of this type. * @param obj * Object that is copied. */ void AnnotationTwoDimensionalShape::copyHelperAnnotationTwoDimensionalShape(const AnnotationTwoDimensionalShape& obj) { *m_coordinate = *obj.m_coordinate; m_width = obj.m_width; m_height = obj.m_height; m_rotationAngle = obj.m_rotationAngle; } /** * Initialize members of this class. */ void AnnotationTwoDimensionalShape::initializeMembersAnnotationTwoDimensionalShape() { m_coordinate.grabNew(new AnnotationCoordinate()); switch (m_attributeDefaultType) { case AnnotationAttributesDefaultTypeEnum::NORMAL: m_width = 25.0; m_height = 25.0; m_rotationAngle = 0.0; break; case AnnotationAttributesDefaultTypeEnum::USER: m_width = s_userDefaultWidth; m_height = s_userDefaultHeight; m_rotationAngle = 0.0; break; } m_sceneAssistant.grabNew(new SceneClassAssistant()); if (getType() == AnnotationTypeEnum::COLOR_BAR) { /* * Saves/restores color bar manual position and size mode. */ m_sceneAssistant->add("m_coordinate", "AnnotationCoordinate", m_coordinate); m_sceneAssistant->add("m_width", &m_width); m_sceneAssistant->add("m_height", &m_height); m_sceneAssistant->add("m_rotationAngle", &m_rotationAngle); } } /** * @return The coordinate for the two dimensional shape. */ AnnotationCoordinate* AnnotationTwoDimensionalShape::getCoordinate() { return m_coordinate; } /** * @return The start coordinate for the two dimensional shape. */ const AnnotationCoordinate* AnnotationTwoDimensionalShape::getCoordinate() const { return m_coordinate; } /** * @return Height for "two-dimensional" annotations in percentage zero to one-hundred. */ float AnnotationTwoDimensionalShape::getHeight() const { return m_height; } /** * Set the height for "two-dimensional" annotations in percentage zero to one-hundred. * * @param height * New value for height of the annotation. */ void AnnotationTwoDimensionalShape::setHeight(const float height) { if (height != m_height) { if (isFixedAspectRatio()) { m_height = height; m_width = m_height / getFixedAspectRatio(); } else { m_height = height; } setModified(); } } /** * @return Width for "two-dimensional" annotations in percentage zero to one-hundred. */ float AnnotationTwoDimensionalShape::getWidth() const { return m_width; } /** * Set the width for "two-dimensional" annotations in percentage zero to one-hundred. * * @param width * New value for width of the annotation. */ void AnnotationTwoDimensionalShape::setWidth(const float width) { if (width != m_width) { if (isFixedAspectRatio()) { m_width = width; m_height = m_width * getFixedAspectRatio(); } else { m_width = width; } setModified(); } } /** * @return The rotation angle, in degrees, clockwise, from vertical at the top (12 o'clock). */ float AnnotationTwoDimensionalShape::getRotationAngle() const { return m_rotationAngle; } /** * The rotation angle, in degrees, clockwise, from vertical at the top (12 o'clock). * * @param rotationAngle * New value rotation angle. */ void AnnotationTwoDimensionalShape::setRotationAngle(const float rotationAngle) { if (rotationAngle != m_rotationAngle) { m_rotationAngle = rotationAngle; setModified(); } } /** * Is the object modified? * @return true if modified, else false. */ bool AnnotationTwoDimensionalShape::isModified() const { if (Annotation::isModified()) { return true; } if (m_coordinate->isModified()) { return true; } return false; } /** * Set the status to unmodified. */ void AnnotationTwoDimensionalShape::clearModified() { Annotation::clearModified(); m_coordinate->clearModified(); } /** * Apply the coordinates, size, and rotation from the given annotation * to this annotation. * * @param otherAnnotation * The other annotation from which attributes are obtained. */ void AnnotationTwoDimensionalShape::applyCoordinatesSizeAndRotationFromOther(const Annotation* otherAnnotation) { CaretAssert(otherAnnotation); const AnnotationTwoDimensionalShape* otherTwoDim = dynamic_cast(otherAnnotation); CaretAssert(otherTwoDim); AnnotationCoordinate* coord = getCoordinate(); const AnnotationCoordinate* otherCoord = otherTwoDim->getCoordinate(); *coord = *otherCoord; setWidth(otherTwoDim->getWidth()); setHeight(otherTwoDim->getHeight()); setRotationAngle(otherTwoDim->getRotationAngle()); setCoordinateSpace(otherAnnotation->getCoordinateSpace()); setTabIndex(otherAnnotation->getTabIndex()); setWindowIndex(otherAnnotation->getWindowIndex()); /* * Switch color bar to manual positioning */ AnnotationColorBar* colorBar = dynamic_cast(this); if (colorBar != NULL) { colorBar->setPositionMode(AnnotationColorBarPositionModeEnum::MANUAL); } } /** * Is the given sizing handle valid for this annotation? * * @sizingHandle * The sizing handle. * @return * True if sizing handle valid, else false. */ bool AnnotationTwoDimensionalShape::isSizeHandleValid(const AnnotationSizingHandleTypeEnum::Enum sizingHandle) const { bool pixelsFlag = false; bool tabWindowFlag = false; bool stereotaxicFlag = false; bool surfaceFlag = false; switch (getCoordinateSpace()) { case AnnotationCoordinateSpaceEnum::PIXELS: pixelsFlag = true; break; case AnnotationCoordinateSpaceEnum::STEREOTAXIC: stereotaxicFlag = true; break; case AnnotationCoordinateSpaceEnum::SURFACE: surfaceFlag = true; break; case AnnotationCoordinateSpaceEnum::TAB: tabWindowFlag = true; break; case AnnotationCoordinateSpaceEnum::WINDOW: tabWindowFlag = true; break; } bool allowsMovingFlag = false; bool allowsCornerResizingFlag = false; bool allowsSideResizingFlag = false; bool allowsRotationFlag = false; switch (getType()) { case AnnotationTypeEnum::BOX: allowsMovingFlag = true; allowsCornerResizingFlag = true; allowsSideResizingFlag = true; allowsRotationFlag = true; break; case AnnotationTypeEnum::COLOR_BAR: allowsMovingFlag = true; allowsCornerResizingFlag = true; allowsSideResizingFlag = true; allowsRotationFlag = true; break; case AnnotationTypeEnum::IMAGE: allowsMovingFlag = true; allowsSideResizingFlag = true; allowsRotationFlag = true; break; case AnnotationTypeEnum::LINE: break; case AnnotationTypeEnum::OVAL: allowsMovingFlag = true; allowsCornerResizingFlag = true; allowsSideResizingFlag = true; allowsRotationFlag = true; break; case AnnotationTypeEnum::TEXT: allowsMovingFlag = true; allowsRotationFlag = true; break; } bool validFlag = false; if ( ! pixelsFlag) { switch (sizingHandle) { case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_BOX_BOTTOM: if (allowsSideResizingFlag) { validFlag = true; } break; case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_BOX_BOTTOM_LEFT: if (allowsCornerResizingFlag) { validFlag = true; } break; case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_BOX_BOTTOM_RIGHT: if (allowsCornerResizingFlag) { validFlag = true; } break; case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_BOX_LEFT: if (allowsSideResizingFlag) { validFlag = true; } break; case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_BOX_RIGHT: if (allowsSideResizingFlag) { validFlag = true; } break; case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_BOX_TOP: if (allowsSideResizingFlag) { validFlag = true; } break; case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_BOX_TOP_LEFT: if (allowsCornerResizingFlag) { validFlag = true; } break; case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_BOX_TOP_RIGHT: if (allowsCornerResizingFlag) { validFlag = 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 (allowsMovingFlag) { validFlag = true; } break; case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_ROTATION: if (allowsRotationFlag) { validFlag = true; } break; } } return validFlag; } /** * Apply a spatial modification to an annotation in surface * or stereotaxic space. * * @param spatialModification * Contains information about the spatial modification. * @param coordinateSpace * The coordinate space. * @return * True if the annotation was modified, else false. */ bool AnnotationTwoDimensionalShape::applySpatialModificationSurfaceOrStereotaxicSpace(const AnnotationSpatialModification& spatialModification, const AnnotationCoordinateSpaceEnum::Enum coordinateSpace) { bool badSpaceFlag = false; bool stereoSpaceFlag = false; bool surfaceSpaceFlag = false; switch (coordinateSpace) { case AnnotationCoordinateSpaceEnum::PIXELS: badSpaceFlag = true; break; case AnnotationCoordinateSpaceEnum::STEREOTAXIC: stereoSpaceFlag = true; break; case AnnotationCoordinateSpaceEnum::SURFACE: surfaceSpaceFlag = true; break; case AnnotationCoordinateSpaceEnum::TAB: badSpaceFlag = true; break; case AnnotationCoordinateSpaceEnum::WINDOW: badSpaceFlag = true; break; } if (badSpaceFlag) { CaretAssertMessage(0, ("This method should NEVER be called for " + AnnotationCoordinateSpaceEnum::toName(coordinateSpace) + " space annotations")); return false; } 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 (stereoSpaceFlag) { m_coordinate->setXYZ(spatialModification.m_stereotaxicCoordinateAtMouseXY.m_stereotaxicXYZ); validFlag = true; } else if (surfaceSpaceFlag) { StructureEnum::Enum structure = StructureEnum::INVALID; int32_t surfaceNumberOfNodes = -1; int32_t surfaceNodeIndex = -1; m_coordinate->getSurfaceSpace(structure, surfaceNumberOfNodes, surfaceNodeIndex); if (spatialModification.m_surfaceCoordinateAtMouseXY.m_surfaceNodeValid) { if ((spatialModification.m_surfaceCoordinateAtMouseXY.m_surfaceStructure == structure) && (spatialModification.m_surfaceCoordinateAtMouseXY.m_surfaceNumberOfNodes == surfaceNumberOfNodes)) { m_coordinate->setSurfaceSpace(spatialModification.m_surfaceCoordinateAtMouseXY.m_surfaceStructure, spatialModification.m_surfaceCoordinateAtMouseXY.m_surfaceNumberOfNodes, spatialModification.m_surfaceCoordinateAtMouseXY.m_surfaceNodeIndex); validFlag = true; } } } else { CaretAssertMessage(0, "New space???"); } 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 tab or window space. * * @param spatialModification * Contains information about the spatial modification. * @return * True if the annotation was modified, else false. */ bool AnnotationTwoDimensionalShape::applySpatialModificationTabOrWindowSpace(const AnnotationSpatialModification& spatialModification) { float xyz[3]; m_coordinate->getXYZ(xyz); float viewportXYZ[3] = { 0.0, 0.0, 0.0 }; relativeXYZToViewportXYZ(xyz, spatialModification.m_viewportWidth, spatialModification.m_viewportHeight, viewportXYZ); float bottomLeftXYZ[3]; float bottomRightXYZ[3]; float topLeftXYZ[3]; float topRightXYZ[3]; const bool validBounds = getShapeBounds(spatialModification.m_viewportWidth, spatialModification.m_viewportHeight, viewportXYZ, bottomLeftXYZ, bottomRightXYZ, topRightXYZ, topLeftXYZ); if ( ! validBounds) { return false; } float leftToRightUnitVector[3]; MathFunctions::createUnitVector(bottomLeftXYZ, bottomRightXYZ, leftToRightUnitVector); float bottomToTopUnitVector[3]; MathFunctions::createUnitVector(bottomLeftXYZ, topLeftXYZ, bottomToTopUnitVector); /* * Find size adjustment for side (not corner) sizing handles */ float sideHandleDX = 0.0; float sideHandleDY = 0.0; getSideHandleMouseDelta(spatialModification.m_sizingHandleType, leftToRightUnitVector, bottomToTopUnitVector, spatialModification.m_mouseDX, spatialModification.m_mouseDY, sideHandleDX, sideHandleDY); bool validCoordinatesFlag = false; bool validRotationFlag = false; /* * When a resize handle is moved, update the corners of the shape */ switch (spatialModification.m_sizingHandleType) { case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_BOX_BOTTOM: addToXYZWithXY(bottomLeftXYZ, sideHandleDX, sideHandleDY); addToXYZWithXY(bottomRightXYZ, sideHandleDX, sideHandleDY); validCoordinatesFlag = true; break; case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_BOX_BOTTOM_LEFT: { /* * Bottom left is now at the mouse XY */ bottomLeftXYZ[0] = spatialModification.m_mouseX; bottomLeftXYZ[1] = spatialModification.m_mouseY; /* * Unit vector from bottom left to updated top right */ float bottomLeftToTopRightUnitVector[3]; MathFunctions::createUnitVector(bottomLeftXYZ, topRightXYZ, bottomLeftToTopRightUnitVector); /* * We have a right triangle where: * The hypotnuse is from bottom left corner to new top right corner * A right angle is at top left corner * Want angle at bottom left but vector angle is at top right (all * angles add up to PI=180). */ const float oppositeAngle = MathFunctions::angle(topLeftXYZ, topRightXYZ, bottomLeftXYZ); const float angle = (M_PI / 2.0) - oppositeAngle; const float hypotnuseLength = MathFunctions::distance3D(bottomLeftXYZ, topRightXYZ); const float newWidth = std::sin(angle) * hypotnuseLength; const float newHeight = std::cos(angle) * hypotnuseLength; topLeftXYZ[0] = bottomLeftXYZ[0] + bottomToTopUnitVector[0] * newHeight; topLeftXYZ[1] = bottomLeftXYZ[1] + bottomToTopUnitVector[1] * newHeight; bottomRightXYZ[0] = bottomLeftXYZ[0] + leftToRightUnitVector[0] * newWidth; bottomRightXYZ[1] = bottomLeftXYZ[1] + leftToRightUnitVector[1] * newWidth; validCoordinatesFlag = true; } break; case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_BOX_BOTTOM_RIGHT: { /* * Bottom right is now at the mouse XY */ bottomRightXYZ[0] = spatialModification.m_mouseX; bottomRightXYZ[1] = spatialModification.m_mouseY; /* * Unit vector from top left to updated bottom right */ float topLeftToBottomRightUnitVector[3]; MathFunctions::createUnitVector(topLeftXYZ, bottomRightXYZ, topLeftToBottomRightUnitVector); /* * We have a right triangle where: * The hypotnuse is from top left corner to new bottom right corner * A right angle is at top right corner */ const float angle = MathFunctions::angle(topRightXYZ, topLeftXYZ, bottomRightXYZ); const float hypotnuseLength = MathFunctions::distance3D(topLeftXYZ, bottomRightXYZ); const float newWidth = std::cos(angle) * hypotnuseLength; const float newHeight = std::sin(angle) * hypotnuseLength; topRightXYZ[0] = topLeftXYZ[0] + leftToRightUnitVector[0] * newWidth; topRightXYZ[1] = topLeftXYZ[1] + leftToRightUnitVector[1] * newWidth; bottomLeftXYZ[0] = topLeftXYZ[0] - bottomToTopUnitVector[0] * newHeight; bottomLeftXYZ[1] = topLeftXYZ[1] - bottomToTopUnitVector[1] * newHeight; validCoordinatesFlag = true; } break; case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_BOX_LEFT: addToXYZWithXY(topLeftXYZ, sideHandleDX, sideHandleDY); addToXYZWithXY(bottomLeftXYZ, sideHandleDX, sideHandleDY); validCoordinatesFlag = true; break; case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_BOX_RIGHT: addToXYZWithXY(topRightXYZ, sideHandleDX, sideHandleDY); addToXYZWithXY(bottomRightXYZ, sideHandleDX, sideHandleDY); validCoordinatesFlag = true; break; case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_BOX_TOP: addToXYZWithXY(topLeftXYZ, sideHandleDX, sideHandleDY); addToXYZWithXY(topRightXYZ, sideHandleDX, sideHandleDY); validCoordinatesFlag = true; break; case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_BOX_TOP_LEFT: { /* * Top left is now at the mouse XY */ topLeftXYZ[0] = spatialModification.m_mouseX; topLeftXYZ[1] = spatialModification.m_mouseY; /* * Unit vector from top left to updated bottom right */ float topLeftToBottomRightUnitVector[3]; MathFunctions::createUnitVector(topLeftXYZ, bottomRightXYZ, topLeftToBottomRightUnitVector); /* * We have a right triangle where: * The hypotnuse is from top left corner to new bottom right corner * A right angle is at top right corner */ const float oppositeAngle = MathFunctions::angle(topLeftXYZ, bottomRightXYZ, bottomLeftXYZ); const float angle = (M_PI / 2.0) - oppositeAngle; const float hypotnuseLength = MathFunctions::distance3D(topLeftXYZ, bottomRightXYZ); const float newWidth = std::sin(angle) * hypotnuseLength; const float newHeight = std::cos(angle) * hypotnuseLength; topRightXYZ[0] = topLeftXYZ[0] + leftToRightUnitVector[0] * newWidth; topRightXYZ[1] = topLeftXYZ[1] + leftToRightUnitVector[1] * newWidth; bottomLeftXYZ[0] = topLeftXYZ[0] - bottomToTopUnitVector[0] * newHeight; bottomLeftXYZ[1] = topLeftXYZ[1] - bottomToTopUnitVector[1] * newHeight; validCoordinatesFlag = true; } break; case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_BOX_TOP_RIGHT: { /* * Top right is now at the mouse XY */ topRightXYZ[0] = spatialModification.m_mouseX; topRightXYZ[1] = spatialModification.m_mouseY; /* * Unit vector from updated top right to bottom left */ float topRightToBottomLeftUnitVector[3]; MathFunctions::createUnitVector(topRightXYZ, bottomLeftXYZ, topRightToBottomLeftUnitVector); /* * We have a right triangle where: * The hypotnuse is from bottom left corner to new top right corner * A right angle is at top left corner */ const float oppositeAngle = MathFunctions::angle(topLeftXYZ, bottomLeftXYZ, topRightXYZ); const float angle = (M_PI / 2.0) - oppositeAngle; const float hypotnuseLength = MathFunctions::distance3D(topRightXYZ, bottomLeftXYZ); const float newWidth = std::cos(angle) * hypotnuseLength; const float newHeight = std::sin(angle) * hypotnuseLength; topLeftXYZ[0] = topRightXYZ[0] - leftToRightUnitVector[0] * newWidth; topLeftXYZ[1] = topRightXYZ[1] - leftToRightUnitVector[1] * newWidth; bottomRightXYZ[0] = topRightXYZ[0] - bottomToTopUnitVector[0] * newHeight; bottomRightXYZ[1] = topRightXYZ[1] - bottomToTopUnitVector[1] * newHeight; validCoordinatesFlag = true; } break; case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_LINE_END: CaretAssert(0); break; case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_LINE_START: CaretAssert(0); break; case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_NONE: addToXYZWithXY(bottomLeftXYZ, spatialModification.m_mouseDX, spatialModification.m_mouseDY); addToXYZWithXY(bottomRightXYZ, spatialModification.m_mouseDX, spatialModification.m_mouseDY); addToXYZWithXY(topRightXYZ, spatialModification.m_mouseDX, spatialModification.m_mouseDY); addToXYZWithXY(topLeftXYZ, spatialModification.m_mouseDX, spatialModification.m_mouseDY); validCoordinatesFlag = true; break; case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_ROTATION: { /* * Rotation angle is a formed by the triangle * (Mouse XY, Annotation XY, Positive X-axis). */ const float dy = spatialModification.m_mouseY - viewportXYZ[1]; const float dx = spatialModification.m_mouseX - viewportXYZ[0]; 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; } validRotationFlag = true; } break; } if (validCoordinatesFlag) { /* * Using the updated corners of the annotation, convert back to normalized x, y, width, and aspect ratio */ float newViewportXYZ[3]; MathFunctions::averageOfFourCoordinates(bottomLeftXYZ, bottomRightXYZ, topRightXYZ, topLeftXYZ, newViewportXYZ); const float newX = 100.0 * (newViewportXYZ[0] / spatialModification.m_viewportWidth); const float newY = 100.0 * (newViewportXYZ[1] / spatialModification.m_viewportHeight); const float newShapeViewportWidth = MathFunctions::distance3D(bottomLeftXYZ, bottomRightXYZ); const float newWidth = 100.0 * (newShapeViewportWidth / spatialModification.m_viewportWidth); const float newShapeViewportHeight = MathFunctions::distance3D(bottomLeftXYZ, topLeftXYZ); const float newHeight = 100.0 * (newShapeViewportHeight / spatialModification.m_viewportHeight); /* * Note: * Coordinates are relative (range 0 to 100) * Width is relative (range 0 to 100) * Aspect ratio must only be greater than zero (when < 1, horizontal rectangle, when > 1 vertical rectangle) */ 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)) { if (isFixedAspectRatio()) { xyz[0] = newX; xyz[1] = newY; m_coordinate->setXYZ(xyz); 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(sideHandleDX) > std::fabs(sideHandleDY)) { /* * 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 newViewportHeight = newShapeViewportWidth * aspectRatio; const float newShapeHeight = 100.0 * (newViewportHeight / spatialModification.m_viewportHeight); setHeight(newShapeHeight); } else if (std::fabs(sideHandleDX) < std::fabs(sideHandleDY)) { setHeight(newHeight); } 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 { xyz[0] = newX; xyz[1] = newY; m_coordinate->setXYZ(xyz); m_width = newWidth; m_height = newHeight; } } else { validCoordinatesFlag = false; } } if (validCoordinatesFlag || validRotationFlag) { setModified(); return true; } return false; } /** * Given the previous mouse location, current mouse location, and the shape * location, examine the angle formed to determine if rotation is allowed. * Note that if the mouse is moved purely horizontally or vertically from * the rotation point, the rotation angle may rapidly change causing weird * behavior. * * @param previousMouseXYZ * Previous location of mouse. * @param shapeXYZ * Location of shape. * @param currentMouseXYZ * Current location of mouse. * @return * True if rotation is valid, else false. */ bool AnnotationTwoDimensionalShape::rotationAngleTest(const float previousMouseXYZ[3], const float shapeXYZ[3], const float currentMouseXYZ[3]) const { const float angle = MathFunctions::angle(currentMouseXYZ, shapeXYZ, previousMouseXYZ); static const float MINIMUM_ANGLE = 0.025; //0.01; if (angle > MINIMUM_ANGLE) { return true; } return false; } /** * Apply a spatial modification to an annotation. * * @param spatialModification * Contains information about the spatial modification. * @return * True if the annotation was modified, else false. */ bool AnnotationTwoDimensionalShape::applySpatialModification(const AnnotationSpatialModification& spatialModification) { if ( ! isSizeHandleValid(spatialModification.m_sizingHandleType)) { return false; } const AnnotationCoordinateSpaceEnum::Enum space = getCoordinateSpace(); switch (space) { case AnnotationCoordinateSpaceEnum::PIXELS: break; case AnnotationCoordinateSpaceEnum::STEREOTAXIC: return applySpatialModificationSurfaceOrStereotaxicSpace(spatialModification, space); break; case AnnotationCoordinateSpaceEnum::SURFACE: return applySpatialModificationSurfaceOrStereotaxicSpace(spatialModification, space); break; case AnnotationCoordinateSpaceEnum::TAB: return applySpatialModificationTabOrWindowSpace(spatialModification); break; case AnnotationCoordinateSpaceEnum::WINDOW: return applySpatialModificationTabOrWindowSpace(spatialModification); break; } return false; } /** * When adjusting one of the "side handles" of a selected shape, set and adjust the change in the shape * so that the shape changes in the same direction as the mouse is moved. * * @param sizeHandle Size handle that is being adjusted. * @param leftToRightShapeVector * Vector running from left to right of shape accounting for any rotation. * @param bottomToTopShapeVector * Vector running from bottom to top of shape accounting for any rotation. * @param mouseDX * Mouse movement in X. * @param mouseDY * Mouse movement in Y. * @param shapeDxOut * Suggested change in shape (signed). * @param shapeDyOut * Suggested change in shape (signed). */ void AnnotationTwoDimensionalShape::getSideHandleMouseDelta(const AnnotationSizingHandleTypeEnum::Enum sizeHandle, const float leftToRightShapeVector[3], const float bottomToTopShapeVector[3], const float mouseDX, const float mouseDY, float& shapeDxOut, float& shapeDyOut) { shapeDxOut = 0.0; shapeDyOut = 0.0; bool useLeftRightFlag = false; bool useBottomTopFlag = false; bool posToNegFlag = false; switch (sizeHandle) { case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_BOX_BOTTOM: useBottomTopFlag = true; 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: useLeftRightFlag = true; break; case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_BOX_RIGHT: useLeftRightFlag = true; posToNegFlag = true; break; case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_BOX_TOP: useBottomTopFlag = true; posToNegFlag = true; 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: break; case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_LINE_START: break; case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_NONE: break; case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_ROTATION: break; } float shapeVector[3]; if (useLeftRightFlag) { shapeVector[0] = leftToRightShapeVector[0]; shapeVector[1] = leftToRightShapeVector[1]; shapeVector[2] = 0.0; } else if (useBottomTopFlag) { shapeVector[0] = bottomToTopShapeVector[0]; shapeVector[1] = bottomToTopShapeVector[1]; shapeVector[2] = 0.0; } else { return; } if (posToNegFlag) { shapeVector[0] = -shapeVector[0]; shapeVector[1] = -shapeVector[1]; } MathFunctions::normalizeVector(shapeVector); float mouseVector[3] = { mouseDX, mouseDY, 0.0 }; float mouseDelta = MathFunctions::normalizeVector(mouseVector); const float cosineAngle = MathFunctions::dotProduct(mouseVector, shapeVector); if (cosineAngle < 0.0) { mouseDelta = -mouseDelta; } shapeDxOut = (shapeVector[0] * mouseDelta); shapeDyOut = (shapeVector[1] * mouseDelta); } /** * Add the given X and Y values to the three-dimensional coordinate. * * @param xyz * Coordinate that has values added to it. * @param addX * Value that is added coordinate's X. * @param addY * Value that is added coordinate's Y. */ void AnnotationTwoDimensionalShape::addToXYZWithXY(float xyz[3], const float addX, const float addY) { xyz[0] += addX; xyz[1] += addY; } /** * Get the bounds for a two-dimensional shape annotation. * * @param viewportWidth * Width of the viewport. * @param viewportHeight * Height of the viewport. * @param viewportXYZ * Viewport coordinates of the annotation. * @param bottomLeftOut * The bottom left corner of the annotation absolute bounds. * @param bottomRightOut * The bottom right corner of the annotation absolute bounds. * @param topRightOut * The top right corner of the annotation absolute bounds. * @param topLeftOut * The top left corner of the annotation absolute bounds. */ bool AnnotationTwoDimensionalShape::getShapeBounds(const float viewportWidth, const float viewportHeight, const float viewportXYZ[3], float bottomLeftOut[3], float bottomRightOut[3], float topRightOut[3], float topLeftOut[3]) const { /* * NOTE: Annotation's height and width are 'relative' ([0.0, 100.0] percentage) of window size. * So want HALF of width/height */ float halfWidth = (getWidth() / 200.0) * viewportWidth; const float halfHeight = (getHeight() / 200.0) * viewportHeight; if (isFixedAspectRatio()) { halfWidth = halfHeight / getFixedAspectRatio(); } bottomLeftOut[0] = viewportXYZ[0] - halfWidth; bottomLeftOut[1] = viewportXYZ[1] - halfHeight; bottomLeftOut[2] = viewportXYZ[2]; bottomRightOut[0] = viewportXYZ[0] + halfWidth; bottomRightOut[1] = viewportXYZ[1] - halfHeight; bottomRightOut[2] = viewportXYZ[2]; topRightOut[0] = viewportXYZ[0] + halfWidth; topRightOut[1] = viewportXYZ[1] + halfHeight; topRightOut[2] = viewportXYZ[2]; topLeftOut[0] = viewportXYZ[0] - halfWidth; topLeftOut[1] = viewportXYZ[1] + halfHeight; topLeftOut[2] = viewportXYZ[2]; if (m_rotationAngle != 0) { Matrix4x4 matrix; matrix.translate(-viewportXYZ[0], -viewportXYZ[1], -viewportXYZ[2]); matrix.rotateZ(-m_rotationAngle); matrix.translate(viewportXYZ[0], viewportXYZ[1], viewportXYZ[2]); matrix.multiplyPoint3(bottomLeftOut); matrix.multiplyPoint3(bottomRightOut); matrix.multiplyPoint3(topRightOut); matrix.multiplyPoint3(topLeftOut); } return true; } /** * Set the width and height of the shape from bounding coordinates. * * @param xyzOne * First bounding coordinate in absolute tab coordinates * @param xyzTwo * Second bounding coordinate in absolute tab coordinates * @param spaceWidth * Width of space. * @param spaceHeight * Height of space. */ void AnnotationTwoDimensionalShape::setWidthAndHeightFromBounds(const float xyzOne[3], const float xyzTwo[3], const float spaceWidth, const float spaceHeight) { if ((spaceWidth > 0.0) && (spaceHeight > 0.0)) { const float minX = std::min(xyzOne[0], xyzTwo[0]); const float maxX = std::max(xyzOne[0], xyzTwo[0]); const float minY = std::min(xyzOne[1], xyzTwo[1]); const float maxY = std::max(xyzOne[1], xyzTwo[1]); const float width = ((maxX - minX) / spaceWidth) * 100.0; const float height = ((maxY - minY) / spaceHeight) * 100.0; setWidth(width); setHeight(height); } } /** * 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 AnnotationTwoDimensionalShape::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 AnnotationTwoDimensionalShape::restoreSubClassDataFromScene(const SceneAttributes* sceneAttributes, const SceneClass* sceneClass) { m_sceneAssistant->restoreMembers(sceneAttributes, sceneClass); } /** * Set the default value for height * * @param height * Default for newly created text annotations. */ void AnnotationTwoDimensionalShape::setUserDefaultHeight(const float height) { s_userDefaultHeight = height; } /** * Set the default value for width * * @param width * Default for newly created annotations. */ void AnnotationTwoDimensionalShape::setUserDefaultWidth(const float width) { s_userDefaultWidth = width; } connectome-workbench-1.2.3+git41-gc4c6c90/src/Annotations/AnnotationTwoDimensionalShape.h000066400000000000000000000133561300200146000311720ustar00rootroot00000000000000#ifndef __ANNOTATION_TWO_DIMENSIONAL_SHAPE_H__ #define __ANNOTATION_TWO_DIMENSIONAL_SHAPE_H__ /*LICENSE_START*/ /* * Copyright (C) 2015 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 "Annotation.h" #include "CaretPointer.h" namespace caret { class AnnotationCoordinate; class AnnotationTwoDimensionalShape : public Annotation { public: AnnotationTwoDimensionalShape(const AnnotationTypeEnum::Enum type, const AnnotationAttributesDefaultTypeEnum::Enum attributeDefaultType); virtual ~AnnotationTwoDimensionalShape(); AnnotationTwoDimensionalShape(const AnnotationTwoDimensionalShape& obj); AnnotationTwoDimensionalShape& operator=(const AnnotationTwoDimensionalShape& obj); AnnotationCoordinate* getCoordinate(); const AnnotationCoordinate* getCoordinate() const; float getHeight() const; void setHeight(const float height); float getWidth() const; void setWidth(const float width); float getRotationAngle() const; void setRotationAngle(const float rotationAngle); virtual bool isModified() const; virtual void clearModified(); virtual bool isSizeHandleValid(const AnnotationSizingHandleTypeEnum::Enum sizingHandle) const; virtual bool applySpatialModification(const AnnotationSpatialModification& spatialModification); virtual void applyCoordinatesSizeAndRotationFromOther(const Annotation* otherAnnotation); virtual bool getShapeBounds(const float viewportWidth, const float viewportHeight, const float viewportXYZ[3], float bottomLeftOut[3], float bottomRightOut[3], float topRightOut[3], float topLeftOut[3]) const; void getSideHandleMouseDelta(const AnnotationSizingHandleTypeEnum::Enum sizeHandle, const float leftToRightShapeVector[3], const float bottomToTopShapeVector[3], const float mouseDX, const float mouseDY, float& shapeDxOut, float& shapeDyOut); void setWidthAndHeightFromBounds(const float xyzOne[3], const float xyzTwo[3], const float spaceWidth, const float spaceHeight); static void setUserDefaultHeight(const float height); static void setUserDefaultWidth(const float width); // ADD_NEW_METHODS_HERE protected: virtual void saveSubClassDataToScene(const SceneAttributes* sceneAttributes, SceneClass* sceneClass); virtual void restoreSubClassDataFromScene(const SceneAttributes* sceneAttributes, const SceneClass* sceneClass); private: void copyHelperAnnotationTwoDimensionalShape(const AnnotationTwoDimensionalShape& obj); void initializeMembersAnnotationTwoDimensionalShape(); void addToXYZWithXY(float xyz[3], const float addX, const float addY); bool applySpatialModificationSurfaceOrStereotaxicSpace(const AnnotationSpatialModification& spatialModification, const AnnotationCoordinateSpaceEnum::Enum coordinateSpace); // bool applySpatialModificationStereotaxicSpace(const AnnotationSpatialModification& spatialModification); bool applySpatialModificationTabOrWindowSpace(const AnnotationSpatialModification& spatialModification); bool rotationAngleTest(const float previousMouseXYZ[3], const float shapeXYZ[3], const float currentMouseXYZ[3]) const; CaretPointer m_sceneAssistant; CaretPointer m_coordinate; float m_rotationAngle; float m_width; float m_height; static float s_userDefaultWidth; static float s_userDefaultHeight; // ADD_NEW_MEMBERS_HERE }; #ifdef __ANNOTATION_TWO_DIMENSIONAL_SHAPE_DECLARE__ float AnnotationTwoDimensionalShape::s_userDefaultWidth = 25.0; float AnnotationTwoDimensionalShape::s_userDefaultHeight = 25.0; #endif // __ANNOTATION_TWO_DIMENSIONAL_SHAPE_DECLARE__ } // namespace #endif //__ANNOTATION_TWO_DIMENSIONAL_SHAPE_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Annotations/AnnotationTypeEnum.cxx000066400000000000000000000255321300200146000273750ustar00rootroot00000000000000 /*LICENSE_START*/ /* * Copyright (C) 2015 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 __ANNOTATION_TYPE_ENUM_DECLARE__ #include "AnnotationTypeEnum.h" #undef __ANNOTATION_TYPE_ENUM_DECLARE__ #include "CaretAssert.h" using namespace caret; /** * \class caret::AnnotationTypeEnum * \brief Types of annotations. * * 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_AnnotationTypeEnumComboBox; * * Declare a slot that is called when user changes selection * private slots: * void AnnotationTypeEnumComboBoxItemActivated(); * * Implementation File (.cxx) * Include the header files * #include "EnumComboBoxTemplate.h" * #include "AnnotationTypeEnum.h" * * Instatiate: * m_AnnotationTypeEnumComboBox = new EnumComboBoxTemplate(this); * m_AnnotationTypeEnumComboBox->setup(); * * Get notified when the user changes the selection: * QObject::connect(m_AnnotationTypeEnumComboBox, SIGNAL(itemActivated()), * this, SLOT(AnnotationTypeEnumComboBoxItemActivated())); * * Update the selection: * m_AnnotationTypeEnumComboBox->setSelectedItem(NEW_VALUE); * * Read the selection: * const AnnotationTypeEnum::Enum VARIABLE = m_AnnotationTypeEnumComboBox->getSelectedItem(); * */ /** * Constructor. * * @param enumValue * An enumerated value. * @param name * Name of enumerated value. * * @param guiName * User-friendly name for use in user-interface. */ AnnotationTypeEnum::AnnotationTypeEnum(const Enum enumValue, const AString& name, const AString& guiName) { this->enumValue = enumValue; this->integerCode = integerCodeCounter++; this->name = name; this->guiName = guiName; } /** * Destructor. */ AnnotationTypeEnum::~AnnotationTypeEnum() { } /** * Initialize the enumerated metadata. */ void AnnotationTypeEnum::initialize() { if (initializedFlag) { return; } initializedFlag = true; enumData.push_back(AnnotationTypeEnum(BOX, "BOX", "Box")); enumData.push_back(AnnotationTypeEnum(COLOR_BAR, "COLOR_BAR", "Color Bar")); enumData.push_back(AnnotationTypeEnum(IMAGE, "IMAGE", "Image")); enumData.push_back(AnnotationTypeEnum(LINE, "LINE", "Line")); enumData.push_back(AnnotationTypeEnum(OVAL, "OVAL", "Oval")); enumData.push_back(AnnotationTypeEnum(TEXT, "TEXT", "Text")); } /** * 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 AnnotationTypeEnum* AnnotationTypeEnum::findData(const Enum enumValue) { if (initializedFlag == false) initialize(); size_t num = enumData.size(); for (size_t i = 0; i < num; i++) { const AnnotationTypeEnum* 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 AnnotationTypeEnum::toName(Enum enumValue) { if (initializedFlag == false) initialize(); const AnnotationTypeEnum* 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. */ AnnotationTypeEnum::Enum AnnotationTypeEnum::fromName(const AString& name, bool* isValidOut) { if (initializedFlag == false) initialize(); bool validFlag = false; Enum enumValue = AnnotationTypeEnum::enumData[0].enumValue; for (std::vector::iterator iter = enumData.begin(); iter != enumData.end(); iter++) { const AnnotationTypeEnum& 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 AnnotationTypeEnum")); } return enumValue; } /** * Get a GUI string representation of the enumerated type. * @param enumValue * Enumerated value. * @return * String representing enumerated value. */ AString AnnotationTypeEnum::toGuiName(Enum enumValue) { if (initializedFlag == false) initialize(); const AnnotationTypeEnum* 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. */ AnnotationTypeEnum::Enum AnnotationTypeEnum::fromGuiName(const AString& guiName, bool* isValidOut) { if (initializedFlag == false) initialize(); bool validFlag = false; Enum enumValue = AnnotationTypeEnum::enumData[0].enumValue; for (std::vector::iterator iter = enumData.begin(); iter != enumData.end(); iter++) { const AnnotationTypeEnum& 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 AnnotationTypeEnum")); } return enumValue; } /** * Get the integer code for a data type. * * @return * Integer code for data type. */ int32_t AnnotationTypeEnum::toIntegerCode(Enum enumValue) { if (initializedFlag == false) initialize(); const AnnotationTypeEnum* 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. */ AnnotationTypeEnum::Enum AnnotationTypeEnum::fromIntegerCode(const int32_t integerCode, bool* isValidOut) { if (initializedFlag == false) initialize(); bool validFlag = false; Enum enumValue = AnnotationTypeEnum::enumData[0].enumValue; for (std::vector::iterator iter = enumData.begin(); iter != enumData.end(); iter++) { const AnnotationTypeEnum& 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 AnnotationTypeEnum")); } 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 AnnotationTypeEnum::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 AnnotationTypeEnum::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(AnnotationTypeEnum::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 AnnotationTypeEnum::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(AnnotationTypeEnum::toGuiName(iter->enumValue)); } if (isSorted) { std::sort(allGuiNames.begin(), allGuiNames.end()); } } connectome-workbench-1.2.3+git41-gc4c6c90/src/Annotations/AnnotationTypeEnum.h000066400000000000000000000062341300200146000270200ustar00rootroot00000000000000#ifndef __ANNOTATION_TYPE_ENUM_H__ #define __ANNOTATION_TYPE_ENUM_H__ /*LICENSE_START*/ /* * Copyright (C) 2015 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 AnnotationTypeEnum { public: /** * Enumerated values. */ enum Enum { /** Box */ BOX, /** Colorbar */ COLOR_BAR, /** Image */ IMAGE, /** Line */ LINE, /** Oval */ OVAL, /** Text */ TEXT }; ~AnnotationTypeEnum(); 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: AnnotationTypeEnum(const Enum enumValue, const AString& name, const AString& guiName); static const AnnotationTypeEnum* 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 __ANNOTATION_TYPE_ENUM_DECLARE__ std::vector AnnotationTypeEnum::enumData; bool AnnotationTypeEnum::initializedFlag = false; int32_t AnnotationTypeEnum::integerCodeCounter = 0; #endif // __ANNOTATION_TYPE_ENUM_DECLARE__ } // namespace #endif //__ANNOTATION_TYPE_ENUM_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Annotations/CMakeLists.txt000066400000000000000000000052261300200146000256060ustar00rootroot00000000000000# # Name of Project # PROJECT (Annotations) # # Use XML from Qt but not GUI. # SET(QT_DONT_USE_QTGUI TRUE) # # QT include files # ###INCLUDE(${QT_USE_FILE}) # # Create a Library # ADD_LIBRARY(Annotations Annotation.h AnnotationAlignmentEnum.h AnnotationAttributesDefaultTypeEnum.h AnnotationBox.h AnnotationColorBar.h AnnotationColorBarNumericText.h AnnotationColorBarPositionModeEnum.h AnnotationColorBarSection.h AnnotationCoordinate.h AnnotationCoordinateSpaceEnum.h AnnotationDistributeEnum.h AnnotationFontAttributesInterface.h AnnotationGroup.h AnnotationGroupKey.h AnnotationGroupTypeEnum.h AnnotationGroupingModeEnum.h AnnotationImage.h AnnotationLine.h AnnotationOneDimensionalShape.h AnnotationOval.h AnnotationPercentSizeText.h AnnotationPointSizeText.h AnnotationRedoUndoCommand.h AnnotationRedoUndoCommandModeEnum.h AnnotationEditingSelectionInformation.h AnnotationSizingHandleTypeEnum.h AnnotationSpatialModification.h AnnotationSurfaceOffsetVectorTypeEnum.h AnnotationText.h AnnotationTextAlignHorizontalEnum.h AnnotationTextAlignVerticalEnum.h AnnotationTextConnectTypeEnum.h AnnotationTextFontNameEnum.h AnnotationTextFontPointSizeEnum.h AnnotationTextFontSizeTypeEnum.h AnnotationTextOrientationEnum.h AnnotationTwoDimensionalShape.h AnnotationTypeEnum.h EventAnnotationAddToRemoveFromFile.h EventAnnotationGroupGetWithKey.h EventAnnotationGrouping.h Annotation.cxx AnnotationAlignmentEnum.cxx AnnotationAttributesDefaultTypeEnum.cxx AnnotationBox.cxx AnnotationColorBar.cxx AnnotationColorBarNumericText.cxx AnnotationColorBarPositionModeEnum.cxx AnnotationColorBarSection.cxx AnnotationCoordinate.cxx AnnotationCoordinateSpaceEnum.cxx AnnotationDistributeEnum.cxx AnnotationGroup.cxx AnnotationGroupKey.cxx AnnotationGroupTypeEnum.cxx AnnotationGroupingModeEnum.cxx AnnotationImage.cxx AnnotationLine.cxx AnnotationOneDimensionalShape.cxx AnnotationOval.cxx AnnotationPercentSizeText.cxx AnnotationPointSizeText.cxx AnnotationRedoUndoCommand.cxx AnnotationRedoUndoCommandModeEnum.cxx AnnotationEditingSelectionInformation.cxx AnnotationSizingHandleTypeEnum.cxx AnnotationSpatialModification.cxx AnnotationSurfaceOffsetVectorTypeEnum.cxx AnnotationText.cxx AnnotationTextAlignHorizontalEnum.cxx AnnotationTextAlignVerticalEnum.cxx AnnotationTextConnectTypeEnum.cxx AnnotationTextFontNameEnum.cxx AnnotationTextFontPointSizeEnum.cxx AnnotationTextFontSizeTypeEnum.cxx AnnotationTextOrientationEnum.cxx AnnotationTwoDimensionalShape.cxx AnnotationTypeEnum.cxx EventAnnotationAddToRemoveFromFile.cxx EventAnnotationGroupGetWithKey.cxx EventAnnotationGrouping.cxx ) # # Find headers # INCLUDE_DIRECTORIES( ${CMAKE_SOURCE_DIR}/Common ${CMAKE_SOURCE_DIR}/Scenes ${CMAKE_SOURCE_DIR}/Nifti ) connectome-workbench-1.2.3+git41-gc4c6c90/src/Annotations/EventAnnotationAddToRemoveFromFile.cxx000066400000000000000000000067641300200146000324340ustar00rootroot00000000000000 /*LICENSE_START*/ /* * Copyright (C) 2015 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_ADD_TO_REMOVE_FROM_FILE_H_DECLARE__ #include "EventAnnotationAddToRemoveFromFile.h" #undef __EVENT_ANNOTATION_ADD_TO_REMOVE_FROM_FILE_H_DECLARE__ #include "CaretAssert.h" #include "EventTypeEnum.h" using namespace caret; /** * \class caret::EventAnnotationAddToRemoveFromFile * \brief Request that an annotation be pasted from or unpasted from an annotation file * \ingroup Annotations * * Annotations may be deleted by the user but the user may request to undelete an * annotation. This event is received by an annotation file and the file will * then delete or undelete the annotation. When the file receives a request to * delete the annotation, the file will delete the annotation from its valid * annotations but it does NOT destroy the annotation. Instead, the file will * save the annotation so that the user can request undeletion of the annotation. * With this logic, the pointer can be used to request undeletion. */ /** * Constructor. * * @param mode * Mode (paste/unpaste) * @param annotationFile * AnnotationFile containing annontation that was pasted/unpasted * @param annotation * Annotation that is pasted or unpasted */ EventAnnotationAddToRemoveFromFile::EventAnnotationAddToRemoveFromFile(const Mode mode, AnnotationFile* annotationFile, Annotation* annotation) : Event(EventTypeEnum::EVENT_ANNOTATION_ADD_TO_REMOVE_FROM_FILE), m_mode(mode), m_annotationFile(annotationFile), m_annotation(annotation), m_successFlag(false) { CaretAssert(annotation); } /** * Destructor. */ EventAnnotationAddToRemoveFromFile::~EventAnnotationAddToRemoveFromFile() { } /** * @return the mode (paste/unpaste) */ EventAnnotationAddToRemoveFromFile::Mode EventAnnotationAddToRemoveFromFile::getMode() const { return m_mode; } /** * @return Pointer to annotation file of annotation that is pasted or unpasted. */ AnnotationFile* EventAnnotationAddToRemoveFromFile::getAnnotationFile() const { return m_annotationFile; } /** * @return Pointer to annotation that is pasted or unpasted. */ Annotation* EventAnnotationAddToRemoveFromFile::getAnnotation() const { return m_annotation; } /** * @return True if paste or unpaste of the annotation was successful. */ bool EventAnnotationAddToRemoveFromFile::isSuccessful() const { return m_successFlag; } /** * Set the success status for paste or unpaste of the annotation. * * @param status * New status indicating success. */ void EventAnnotationAddToRemoveFromFile::setSuccessful(const bool status) { m_successFlag = status; } connectome-workbench-1.2.3+git41-gc4c6c90/src/Annotations/EventAnnotationAddToRemoveFromFile.h000066400000000000000000000050361300200146000320500ustar00rootroot00000000000000#ifndef __EVENT_ANNOTATION_ADD_TO_REMOVE_FROM_FILE_H__ #define __EVENT_ANNOTATION_ADD_TO_REMOVE_FROM_FILE_H__ /*LICENSE_START*/ /* * Copyright (C) 2015 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 Annotation; class AnnotationFile; class EventAnnotationAddToRemoveFromFile : public Event { public: enum Mode { MODE_CREATE, MODE_CUT, MODE_DELETE, MODE_PASTE, MODE_UNCREATE, MODE_UNCUT, MODE_UNDELETE, MODE_UNPASTE }; EventAnnotationAddToRemoveFromFile(const Mode mode, AnnotationFile* annotationFile, Annotation* annotation); virtual ~EventAnnotationAddToRemoveFromFile(); Mode getMode() const; AnnotationFile* getAnnotationFile() const; Annotation* getAnnotation() const; bool isSuccessful() const; void setSuccessful(const bool status); // ADD_NEW_METHODS_HERE private: EventAnnotationAddToRemoveFromFile(const EventAnnotationAddToRemoveFromFile&); EventAnnotationAddToRemoveFromFile& operator=(const EventAnnotationAddToRemoveFromFile&); const Mode m_mode; AnnotationFile* m_annotationFile; Annotation* m_annotation; bool m_successFlag; // ADD_NEW_MEMBERS_HERE }; #ifdef __EVENT_ANNOTATION_ADD_TO_REMOVE_FROM_FILE_H_DECLARE__ // #endif // __EVENT_ANNOTATION_ADD_TO_REMOVE_FROM_FILE_H_DECLARE__ } // namespace #endif //__EVENT_ANNOTATION_ADD_TO_REMOVE_FROM_FILE_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Annotations/EventAnnotationGroupGetWithKey.cxx000066400000000000000000000041611300200146000316650ustar00rootroot00000000000000 /*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_ANNOTATION_GROUP_GET_WITH_KEY_DECLARE__ #include "EventAnnotationGroupGetWithKey.h" #undef __EVENT_ANNOTATION_GROUP_GET_WITH_KEY_DECLARE__ #include "CaretAssert.h" #include "EventTypeEnum.h" using namespace caret; /** * \class caret::EventAnnotationGroupGetWithKey * \brief Get an annotation group using its group key. * \ingroup Annotations */ /** * Constructor. * * @param groupKey * The annotation group's key. */ EventAnnotationGroupGetWithKey::EventAnnotationGroupGetWithKey(const AnnotationGroupKey& groupKey) : Event(EventTypeEnum::EVENT_ANNOTATION_GROUP_GET_WITH_KEY), m_groupKey(groupKey), m_annotationGroup(NULL) { } /** * Destructor. */ EventAnnotationGroupGetWithKey::~EventAnnotationGroupGetWithKey() { } /** * @return The annotation group's key. */ const AnnotationGroupKey EventAnnotationGroupGetWithKey::getGroupKey() const { return m_groupKey; } /** * @return The annotation group (my be NULL if not found). */ AnnotationGroup* EventAnnotationGroupGetWithKey::getAnnotationGroup() const { return m_annotationGroup; } /** * Set the annotation group. * * @param annotationGroup * The annotation group. */ void EventAnnotationGroupGetWithKey::setAnnotationGroup(AnnotationGroup* annotationGroup) { m_annotationGroup = annotationGroup; } connectome-workbench-1.2.3+git41-gc4c6c90/src/Annotations/EventAnnotationGroupGetWithKey.h000066400000000000000000000040361300200146000313130ustar00rootroot00000000000000#ifndef __EVENT_ANNOTATION_GROUP_GET_WITH_KEY_H__ #define __EVENT_ANNOTATION_GROUP_GET_WITH_KEY_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 "AnnotationGroupKey.h" #include "Event.h" namespace caret { class Annotation; class AnnotationGroup; class EventAnnotationGroupGetWithKey : public Event { public: EventAnnotationGroupGetWithKey(const AnnotationGroupKey& groupKey); virtual ~EventAnnotationGroupGetWithKey(); const AnnotationGroupKey getGroupKey() const; AnnotationGroup* getAnnotationGroup() const; void setAnnotationGroup(AnnotationGroup* annotationGroup); // ADD_NEW_METHODS_HERE private: EventAnnotationGroupGetWithKey(const EventAnnotationGroupGetWithKey&); EventAnnotationGroupGetWithKey& operator=(const EventAnnotationGroupGetWithKey&); const AnnotationGroupKey m_groupKey; AnnotationGroup* m_annotationGroup; // ADD_NEW_MEMBERS_HERE }; #ifdef __EVENT_ANNOTATION_GROUP_GET_WITH_KEY_DECLARE__ // #endif // __EVENT_ANNOTATION_GROUP_GET_WITH_KEY_DECLARE__ } // namespace #endif //__EVENT_ANNOTATION_GROUP_GET_WITH_KEY_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Annotations/EventAnnotationGrouping.cxx000066400000000000000000000114571300200146000304240ustar00rootroot00000000000000 /*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_ANNOTATION_GROUPING_DECLARE__ #include "EventAnnotationGrouping.h" #undef __EVENT_ANNOTATION_GROUPING_DECLARE__ #include "CaretAssert.h" #include "EventTypeEnum.h" using namespace caret; /** * \class caret::EventAnnotationGrouping * \brief Event for performing an annotation grouping operation. * \ingroup Annotations */ /** * Constructor. */ EventAnnotationGrouping::EventAnnotationGrouping() : Event(EventTypeEnum::EVENT_ANNOTATION_GROUPING), m_mode(MODE_INVALID) { m_annotationGroupKey.reset(); m_groupKeyToWhichAnnotationsWereMoved.reset(); } /** * Destructor. */ EventAnnotationGrouping::~EventAnnotationGrouping() { } /** * Set the mode to group annotations. * * @param windowIndex * Index of window in which operation was requested. * Will be negative if invalid. * @param spaceGroupKey * The annotation space group key whose annotations are moved into a user group. * @param annotations * Annotations move to user group. */ void EventAnnotationGrouping::setModeGroupAnnotations(const int32_t windowIndex, const AnnotationGroupKey spaceGroupKey, std::vector& annotations) { m_mode = MODE_GROUP; m_windowIndex = windowIndex; m_annotationGroupKey = spaceGroupKey; m_annotations = annotations; } /** * Set the mode to ungroup annotations. * * @param windowIndex * Index of window in which operation was requested. * Will be negative if invalid. * @param userGroupKey * The annotation user group key whose annotations are moved out of a user group. */ void EventAnnotationGrouping::setModeUngroupAnnotations(const int32_t windowIndex, const AnnotationGroupKey userGroupKey) { m_mode = MODE_UNGROUP; m_windowIndex = windowIndex; m_annotationGroupKey = userGroupKey; } /** * Set the mode to regroup annotations. * * @param windowIndex * Index of window in which operation was requested. * Will be negative if invalid. * @param userGroupKey * The annotation user group key whose annotations are moved back to a user group. */ void EventAnnotationGrouping::setModeRegroupAnnotations(const int32_t windowIndex, const AnnotationGroupKey userGroupKey) { m_mode = MODE_REGROUP; m_windowIndex = windowIndex; m_annotationGroupKey = userGroupKey; } /* * @return The mode. */ EventAnnotationGrouping::Mode EventAnnotationGrouping::getMode() const { return m_mode; } /* * @return The annotation group Key */ AnnotationGroupKey EventAnnotationGrouping::getAnnotationGroupKey() const { return m_annotationGroupKey; } /** * @return The annotation group key to which the annotations were * moved. This value is set by the receiver of the event and * can be used to assist with 'undo' of this event. This method * must be called after the event has successfully executed. */ AnnotationGroupKey EventAnnotationGrouping::getGroupKeyToWhichAnnotationsWereMoved() const { return m_groupKeyToWhichAnnotationsWereMoved; } /** * Set the annotation group key to which the anntotations were * moved. This method is called by the receiver of the event * so that the caller may be able to 'undo' this event. * * @param movedToKey * Key of group that now contains the annotations after * the event is executed. */ void EventAnnotationGrouping::setGroupKeyToWhichAnnotationsWereMoved(const AnnotationGroupKey& movedToKey) { m_groupKeyToWhichAnnotationsWereMoved = movedToKey; } /* * @return The annotations. */ std::vector EventAnnotationGrouping::getAnnotations() const { return m_annotations; } /** * @return Index of window in which operation was requested. * Will be negative if invalid. */ int32_t EventAnnotationGrouping::getWindowIndex() const { return m_windowIndex; } connectome-workbench-1.2.3+git41-gc4c6c90/src/Annotations/EventAnnotationGrouping.h000066400000000000000000000056361300200146000300530ustar00rootroot00000000000000#ifndef __EVENT_ANNOTATION_GROUPING_H__ #define __EVENT_ANNOTATION_GROUPING_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 "AnnotationGroupKey.h" #include "Event.h" namespace caret { class Annotation; class EventAnnotationGrouping : public Event { public: enum Mode { MODE_INVALID, MODE_GROUP, MODE_REGROUP, MODE_UNGROUP }; EventAnnotationGrouping(); virtual ~EventAnnotationGrouping(); void setModeGroupAnnotations(const int32_t windowIndex, const AnnotationGroupKey spaceGroupKey, std::vector& annotations); void setModeUngroupAnnotations(const int32_t windowIndex, const AnnotationGroupKey userGroupKey); void setModeRegroupAnnotations(const int32_t windowIndex, const AnnotationGroupKey userGroupKey); Mode getMode() const; AnnotationGroupKey getAnnotationGroupKey() const; std::vector getAnnotations() const; AnnotationGroupKey getGroupKeyToWhichAnnotationsWereMoved() const; void setGroupKeyToWhichAnnotationsWereMoved(const AnnotationGroupKey& movedToKey); int32_t getWindowIndex() const; // ADD_NEW_METHODS_HERE private: EventAnnotationGrouping(const EventAnnotationGrouping&); EventAnnotationGrouping& operator=(const EventAnnotationGrouping&); Mode m_mode; int32_t m_windowIndex; AnnotationGroupKey m_annotationGroupKey; std::vector m_annotations; AnnotationGroupKey m_groupKeyToWhichAnnotationsWereMoved; // ADD_NEW_MEMBERS_HERE }; #ifdef __EVENT_ANNOTATION_GROUPING_DECLARE__ // #endif // __EVENT_ANNOTATION_GROUPING_DECLARE__ } // namespace #endif //__EVENT_ANNOTATION_GROUPING_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Annotations/create_enum.sh000077500000000000000000000010511300200146000256640ustar00rootroot00000000000000#!/bin/tcsh wb_cmd_run -class-create-enum AnnotationSizingHandleTypeEnum 11 true \ ANNOTATION_SIZING_HANDLE_BOX_BOTTOM \ ANNOTATION_SIZING_HANDLE_BOX_BOTTOM_LEFT \ ANNOTATION_SIZING_HANDLE_BOX_BOTTOM_RIGHT \ ANNOTATION_SIZING_HANDLE_BOX_LEFT \ ANNOTATION_SIZING_HANDLE_BOX_RIGHT \ ANNOTATION_SIZING_HANDLE_BOX_TOP \ ANNOTATION_SIZING_HANDLE_BOX_TOP_LEFT \ ANNOTATION_SIZING_HANDLE_BOX_TOP_RIGHT \ ANNOTATION_SIZING_HANDLE_ROTATION \ ANNOTATION_SIZING_HANDLE_LINE_END \ ANNOTATION_SIZING_HANDLE_LINE_START connectome-workbench-1.2.3+git41-gc4c6c90/src/Brain/000077500000000000000000000000001300200146000215775ustar00rootroot00000000000000connectome-workbench-1.2.3+git41-gc4c6c90/src/Brain/AnnotationArrangerExecutor.cxx000066400000000000000000001134011300200146000276360ustar00rootroot00000000000000 /*LICENSE_START*/ /* * Copyright (C) 2015 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 #define __ANNOTATION_ARRANGER_EXECUTOR_DECLARE__ #include "AnnotationArrangerExecutor.h" #undef __ANNOTATION_ARRANGER_EXECUTOR_DECLARE__ #include "AnnotationArrangerInputs.h" #include "AnnotationCoordinate.h" #include "AnnotationManager.h" #include "AnnotationOneDimensionalShape.h" #include "AnnotationRedoUndoCommand.h" #include "AnnotationText.h" #include "BrainOpenGLTextRenderInterface.h" #include "CaretAssert.h" #include "CaretException.h" #include "EventGetViewportSize.h" #include "EventManager.h" using namespace caret; /** * \class caret::AnnotationArrangerExecutor * \brief Performs alignment, distribution of annotations similar to PowerPoint. * \ingroup Brain */ /** * Constructor. */ AnnotationArrangerExecutor::AnnotationArrangerExecutor() : CaretObject(), m_mode(MODE_NONE), m_annotationManager(NULL), m_alignment(AnnotationAlignmentEnum::ALIGN_BOTTOM), m_distribute(AnnotationDistributeEnum::HORIZONTALLY), m_debugFlag(false) { } /** * Destructor. */ AnnotationArrangerExecutor::~AnnotationArrangerExecutor() { } /** * Apply alignment modification to selected annotations * * @param annotationManager * The annotation manager. * @param arrangerInputs * Contains information about the alignment. * @param alignment * The alignment selection. * @param errorMessageOut * Contains description of error. * @return * True if the alignment was successful, else false. */ bool AnnotationArrangerExecutor::alignAnnotations(AnnotationManager* annotationManager, const AnnotationArrangerInputs& arrangerInputs, const AnnotationAlignmentEnum::Enum alignment, AString& errorMessageOut) { m_mode = MODE_ALIGN; m_annotationManager = annotationManager; CaretAssert(m_annotationManager); m_alignment = alignment; errorMessageOut.clear(); try { alignAnnotationsPrivate(arrangerInputs); } catch (const CaretException& caretException) { errorMessageOut = caretException.whatString(); return false; } return true; } /** * Apply distribute modification to selected annotations * * @param annotationManager * The annotation manager. * @param arrangerInputs * Contains information about the alignment. * @param distribute * The distribute selection. * @param errorMessageOut * Contains description of error. * @return * True if the alignment was successful, else false. */ bool AnnotationArrangerExecutor::distributeAnnotations(AnnotationManager* annotationManager, const AnnotationArrangerInputs& arrangerInputs, const AnnotationDistributeEnum::Enum distribute, AString& errorMessageOut) { m_mode = MODE_DISTRIBUTE; m_annotationManager = annotationManager; CaretAssert(m_annotationManager); m_distribute = distribute; errorMessageOut.clear(); try { distributeAnnotationsPrivate(arrangerInputs); } catch (const CaretException& caretException) { errorMessageOut = caretException.whatString(); return false; } return true; } /** * \class caret::AnnotationArrangerExecutor::SortForDistributeFunctionObject * \brief Performs alignment, distribution of annotations similar to PowerPoint. * \ingroup Brain */ AnnotationArrangerExecutor::SortForDistributeFunctionObject::SortForDistributeFunctionObject(const AnnotationDistributeEnum::Enum distribute) : m_distribute(distribute) { } /** * Function for sorting by distribution value. * * @param ann1 * First annotation info. * @param ann2 * Second annotation info. * @return * True if ann1 less than ann2, else false. */ bool AnnotationArrangerExecutor::SortForDistributeFunctionObject::operator()(const AnnotationInfo& ann1, const AnnotationInfo& ann2) const { switch (m_distribute) { case AnnotationDistributeEnum::HORIZONTALLY: return (ann1.m_windowMidPointXY[0] < ann2.m_windowMidPointXY[0]); break; case AnnotationDistributeEnum::VERTICALLY: return (ann1.m_windowMidPointXY[1] < ann2.m_windowMidPointXY[1]); break; } return false; } /** * Apply distribute modification to selected annotations * * @param arrangerInputs * Contains information about the distribute. * @throw CaretException * If there is an error. */ void AnnotationArrangerExecutor::distributeAnnotationsPrivate(const AnnotationArrangerInputs& arrangerInputs) { initializeForArranging(arrangerInputs); printAnnotationInfo("BEFORE"); /* * Sort the annotation info by the window mid point value */ std::sort(m_annotationInfo.begin(), m_annotationInfo.end(), SortForDistributeFunctionObject(m_distribute)); printAnnotationInfo("AFTER SORT FOR DISTRIBUTE"); float distMinValue = 0.0; float distMaxValue = 0.0; float distributeSum = 0.0; const int32_t numAnn = static_cast(m_annotationInfo.size()); for (int32_t i = 0; i < numAnn; i++) { CaretAssertVectorIndex(m_annotationInfo, i); AnnotationInfo& annInfo = m_annotationInfo[i]; switch (m_distribute) { case AnnotationDistributeEnum::HORIZONTALLY: if (i == 0) { distMinValue = annInfo.m_windowBoundingBox.getMaxX(); } else if (i == (numAnn - 1)) { distMaxValue = annInfo.m_windowBoundingBox.getMinX(); } else { distributeSum += annInfo.m_windowBoundingBox.getDifferenceX(); } break; case AnnotationDistributeEnum::VERTICALLY: if (i == 0) { distMinValue = annInfo.m_windowBoundingBox.getMaxY(); } else if (i == (numAnn - 1)) { distMaxValue = annInfo.m_windowBoundingBox.getMinY(); } else { distributeSum += annInfo.m_windowBoundingBox.getDifferenceY(); } break; } } /* * Space between max edge of first and min edge of last */ const float availableSpace = distMaxValue - distMinValue; /* * Space to put between annotations. * Will be negative when the distance between the first and last annotations * is less than the total size of the annotations that are distributed. */ const float numberOfSpacesBetweenAnnotations = numAnn - 1; const float spaceBetweenAnnotations = ((availableSpace - distributeSum) / numberOfSpacesBetweenAnnotations); if (m_debugFlag) { std::cout << "Dist min max: (" << distMinValue << ", " << distMaxValue << ") availSpace=" << availableSpace << " delta-space=" << spaceBetweenAnnotations << std::endl; } std::vector beforeMoving; std::vector afterMoving; /* * Initialize to the right/top edge of the first annotation */ float newCoordinate = distMinValue; for (int32_t i = 0; i < numAnn; i++) { CaretAssertVectorIndex(m_annotationInfo, i); AnnotationInfo& annInfo = m_annotationInfo[i]; Annotation* annotationModified = annInfo.m_annotation->clone(); if (i == 0) { /* first annotation does not move */ } else if (i == (numAnn - 1)) { /* last annotation does not move */ } else { /* * newCoordinate is now at the location of the * left/bottom edge of the annotation to be moved. */ newCoordinate += spaceBetweenAnnotations; /* * For most annotations, the annotation's coordinate is at the * center of the annotation. However, an exception is text annotation * due to their alignment properties. So, we calculate how much the * left or bottom edge should move and apply that difference to the annotation's * coordinate. */ float dx = 0.0; float dy = 0.0; float annotationWidthHeight = 0.0; switch (m_distribute) { case AnnotationDistributeEnum::HORIZONTALLY: { dx = newCoordinate - annInfo.m_windowBoundingBox.getMinX(); annotationWidthHeight = annInfo.m_windowBoundingBox.getDifferenceX(); } break; case AnnotationDistributeEnum::VERTICALLY: { dy = newCoordinate - annInfo.m_windowBoundingBox.getMinY(); annotationWidthHeight = annInfo.m_windowBoundingBox.getDifferenceY(); } break; } annInfo.moveAnnotationByXY(annotationModified, dx, dy); newCoordinate += annotationWidthHeight; } beforeMoving.push_back(annInfo.m_annotation); afterMoving.push_back(annotationModified); } bool validFlag = true; AString errorMessage; /* * If any annotations were moved, use a redo command * so that the user may undo the alignment. */ if ( ! beforeMoving.empty()) { CaretAssert(beforeMoving.size() == afterMoving.size()); AnnotationRedoUndoCommand* undoCommand = new AnnotationRedoUndoCommand(); undoCommand->setModeLocationAndSize(beforeMoving, afterMoving); undoCommand->setDescription(AnnotationDistributeEnum::toGuiName(m_distribute)); validFlag = m_annotationManager->applyCommand(undoCommand, errorMessage); } /* * The undo command copies these annotations so we need * to delete them. */ for (std::vector::iterator annIter = afterMoving.begin(); annIter != afterMoving.end(); annIter++) { Annotation* ann = *annIter; CaretAssert(ann); delete ann; } if ( ! validFlag) { throw CaretException(errorMessage); } } /** * Apply alignment modification to selected annotations * * @param arrangerInputs * Contains information about the alignment. * @throw CaretException * If there is an error. */ void AnnotationArrangerExecutor::alignAnnotationsPrivate(const AnnotationArrangerInputs& arrangerInputs) { switch (m_mode) { case MODE_NONE: CaretAssertMessage(0, "Mode has not been set !!!"); throw CaretException("Mode has not been set !!!"); break; case MODE_ALIGN: break; case MODE_DISTRIBUTE: break; } initializeForArranging(arrangerInputs); printAnnotationInfo("BEFORE"); /* * Set the coordinate value to which annotations are aligned * based upon the selected alignment. */ float alignToCoordinateValue = 0.0; switch (m_alignment) { case AnnotationAlignmentEnum::ALIGN_BOTTOM: alignToCoordinateValue = m_allAnnotationsBoundingBox.getMinY(); break; case AnnotationAlignmentEnum::ALIGN_CENTER: alignToCoordinateValue = (m_allAnnotationsBoundingBox.getMinX() + m_allAnnotationsBoundingBox.getMaxX()) / 2.0; break; case AnnotationAlignmentEnum::ALIGN_LEFT: alignToCoordinateValue = m_allAnnotationsBoundingBox.getMinX(); break; case AnnotationAlignmentEnum::ALIGN_MIDDLE: alignToCoordinateValue = (m_allAnnotationsBoundingBox.getMinY() + m_allAnnotationsBoundingBox.getMaxY()) / 2.0; break; case AnnotationAlignmentEnum::ALIGN_RIGHT: alignToCoordinateValue = m_allAnnotationsBoundingBox.getMaxX(); break; case AnnotationAlignmentEnum::ALIGN_TOP: alignToCoordinateValue = m_allAnnotationsBoundingBox.getMaxY(); break; } if (m_debugFlag) { std::cout << "Bounding box for all: " << qPrintable(m_allAnnotationsBoundingBox.toString()) << std::endl; std::cout << "New Align to value: " << alignToCoordinateValue << std::endl; } /* * Move each of the annotations to align them. */ std::vector beforeMoving; std::vector afterMoving; for (std::vector::iterator annInfoIter = m_annotationInfo.begin(); annInfoIter != m_annotationInfo.end(); annInfoIter++) { AnnotationInfo& annInfo = *annInfoIter; alignAnnotationToValue(alignToCoordinateValue, annInfo, beforeMoving, afterMoving); } bool validFlag = true; AString errorMessage; /* * If any annotations were moved, use a redo command * so that the user may undo the alignment. */ if ( ! beforeMoving.empty()) { CaretAssert(beforeMoving.size() == afterMoving.size()); AnnotationRedoUndoCommand* undoCommand = new AnnotationRedoUndoCommand(); undoCommand->setModeLocationAndSize(beforeMoving, afterMoving); undoCommand->setDescription(AnnotationAlignmentEnum::toGuiName(m_alignment)); validFlag = m_annotationManager->applyCommand(undoCommand, errorMessage); } /* * The undo command copies these annotations so we need * to delete them. */ for (std::vector::iterator annIter = afterMoving.begin(); annIter != afterMoving.end(); annIter++) { Annotation* ann = *annIter; CaretAssert(ann); delete ann; } if ( ! validFlag) { throw CaretException(errorMessage); } } /** * Initialize members in preparation for arranging annotations. * * @param arrangerInputs * Inputs for arranging annotations. */ void AnnotationArrangerExecutor::initializeForArranging(const AnnotationArrangerInputs& arrangerInputs) { /* * Get the window's viewport. */ EventGetViewportSize vpEvent(EventGetViewportSize::MODE_WINDOW_INDEX, arrangerInputs.getWindowIndex()); EventManager::get()->sendEvent(vpEvent.getPointer()); if (vpEvent.isViewportSizeValid()) { vpEvent.getViewportSize(m_windowViewport); } else { throw CaretException("Failed to get viewport size for window index " + QString::number(arrangerInputs.getWindowIndex() + 1)); } /* * Verify that selected annotations can be arranged. */ std::vector annotations; getAnnotationsForArranging(arrangerInputs, annotations); /** * Setup information to assist with arranging each annotation. */ setupAnnotationInfo(arrangerInputs, annotations); /** * Setup a bounding box that contains the bounds * of all annotations. */ m_allAnnotationsBoundingBox.resetForUpdate(); for (std::vector::iterator annInfoIter = m_annotationInfo.begin(); annInfoIter != m_annotationInfo.end(); annInfoIter++) { const AnnotationInfo& annInfo = *annInfoIter; m_allAnnotationsBoundingBox.update(annInfo.m_windowBoundingBox.getMinXYZ()); m_allAnnotationsBoundingBox.update(annInfo.m_windowBoundingBox.getMaxXYZ()); } } /** * Get the annotations for arranging. Not all coordinate spaces are supported. * * @param arrangerInputs * Inputs for arranging annotations. * @param annotationsOut * On output contains annotations that will be arranged. * @throw CaretException * If there are input annotations that cannot be aligned. */ void AnnotationArrangerExecutor::getAnnotationsForArranging(const AnnotationArrangerInputs& arrangerInputs, std::vector& annotationsOut) const { annotationsOut.clear(); std::vector spaces; spaces.push_back(AnnotationCoordinateSpaceEnum::TAB); spaces.push_back(AnnotationCoordinateSpaceEnum::WINDOW); const std::vector allSpaceAnnotations = m_annotationManager->getAnnotationsSelectedForEditing(arrangerInputs.getWindowIndex()); annotationsOut = m_annotationManager->getAnnotationsSelectedForEditingInSpaces(arrangerInputs.getWindowIndex(), spaces); if (allSpaceAnnotations.size() != annotationsOut.size()) { QString spaceString; for (std::vector::iterator spaceIter = spaces.begin(); spaceIter != spaces.end(); spaceIter++) { spaceString += ("\n " + AnnotationCoordinateSpaceEnum::toGuiName(*spaceIter) + " "); } throw CaretException("Annotations are selected that cannot be aligned due to their coordinate space. " "Annotations MUST BE in one of these spaces:" + spaceString); } const int32_t numAnnotations = static_cast(annotationsOut.size()); if (numAnnotations < 1) { throw CaretException("No annotations are selected."); } switch (m_mode) { case MODE_NONE: CaretAssert(0); break; case MODE_ALIGN: if (numAnnotations < 2) { throw CaretException("At least two annotations must be selected for alignment."); } break; case MODE_DISTRIBUTE: if (numAnnotations < 3) { throw CaretException("At least three annotations must be selected for distributing."); } break; } } /** * Setup the annotation information that contains the bounds of * the annotation in window coordinates and other information. * * @param arrangerInputs * Inputs for arranging annotations. * @param annotations * Contains annotations that will be arranged. * @throw CaretException * If there are input annotations that cannot be aligned. */ void AnnotationArrangerExecutor::setupAnnotationInfo(const AnnotationArrangerInputs& arrangerInputs, std::vector& annotations) { m_annotationInfo.clear(); for (std::vector::iterator annIter = annotations.begin(); annIter != annotations.end(); annIter++) { Annotation* annotation = *annIter; CaretAssert(annotation); /* * Viewport containing the annotation. */ int32_t annViewport[4] = { 0, 0, 0, 0 }; switch (annotation->getCoordinateSpace()) { case AnnotationCoordinateSpaceEnum::PIXELS: CaretAssert(0); break; case AnnotationCoordinateSpaceEnum::STEREOTAXIC: CaretAssert(0); break; case AnnotationCoordinateSpaceEnum::SURFACE: CaretAssert(0); break; case AnnotationCoordinateSpaceEnum::TAB: getTabViewport(annotation->getTabIndex(), annViewport); break; case AnnotationCoordinateSpaceEnum::WINDOW: annViewport[0] = m_windowViewport[0]; annViewport[1] = m_windowViewport[1]; annViewport[2] = m_windowViewport[2]; annViewport[3] = m_windowViewport[3]; break; } const AnnotationOneDimensionalShape* oneDimAnn = dynamic_cast(annotation); const AnnotationTwoDimensionalShape* twoDimAnn = dynamic_cast(annotation); float viewportPixelOneXYZ[3] = { 0.0, 0.0, 0.0 }; float viewportPixelTwoXYZ[3] = { 0.0, 0.0, 0.0 }; bool viewportPixelTwoXYZValidFlag = false; /* * Bounding box for the annotation. */ BoundingBox windowBoundingBox; windowBoundingBox.resetForUpdate(); if (oneDimAnn != NULL) { float xyzRel1[3]; oneDimAnn->getStartCoordinate()->getXYZ(xyzRel1); float xyzRel2[3]; oneDimAnn->getEndCoordinate()->getXYZ(xyzRel2); /* * Convert range [0.0, 100.0] to viewport XY. */ Annotation::relativeXYZToViewportXYZ(xyzRel1, annViewport[2], annViewport[3], viewportPixelOneXYZ); Annotation::relativeXYZToViewportXYZ(xyzRel2, annViewport[2], annViewport[3], viewportPixelTwoXYZ); windowBoundingBox.update(annViewport[0] + viewportPixelOneXYZ[0], annViewport[1] + viewportPixelOneXYZ[1], viewportPixelOneXYZ[2]); windowBoundingBox.update(annViewport[0] + viewportPixelTwoXYZ[0], annViewport[1] + viewportPixelTwoXYZ[1], viewportPixelTwoXYZ[2]); viewportPixelTwoXYZValidFlag = true; } else if (twoDimAnn != NULL) { float xyz[3]; twoDimAnn->getCoordinate()->getXYZ(xyz); /* * Convert range [0.0, 100.0] to viewport XY. */ Annotation::relativeXYZToViewportXYZ(xyz, annViewport[2], annViewport[3], viewportPixelOneXYZ); float bottomLeft[3]; float bottomRight[3]; float topRight[3]; float topLeft[3]; bool boundsValidFlag = false; if (twoDimAnn->getType() == AnnotationTypeEnum::TEXT) { const AnnotationText* textAnn = dynamic_cast(twoDimAnn); CaretAssert(textAnn); arrangerInputs.getTextRender()->getBoundsForTextAtViewportCoords(*textAnn, viewportPixelOneXYZ[0], viewportPixelOneXYZ[1], viewportPixelOneXYZ[2], annViewport[3], bottomLeft, bottomRight, topRight, topLeft); boundsValidFlag = true; } else { boundsValidFlag = twoDimAnn->getShapeBounds(annViewport[2], annViewport[3], viewportPixelOneXYZ, bottomLeft, bottomRight, topRight, topLeft); } if (boundsValidFlag) { windowBoundingBox.update(annViewport[0] + bottomLeft[0], annViewport[1] + bottomLeft[1], bottomLeft[2]); windowBoundingBox.update(annViewport[0] + bottomRight[0], annViewport[1] + bottomRight[1], bottomRight[2]); windowBoundingBox.update(annViewport[0] + topRight[0], annViewport[1] + topRight[1], topRight[2]); windowBoundingBox.update(annViewport[0] + topLeft[0], annViewport[1] + topLeft[1], topLeft[2]); } else { throw CaretException("Failed to get bounds for an annotation: " + twoDimAnn->toString()); } } AnnotationInfo annInfo(annotation, annViewport, windowBoundingBox, viewportPixelOneXYZ, viewportPixelTwoXYZ, viewportPixelTwoXYZValidFlag); m_annotationInfo.push_back(annInfo); } CaretAssert(annotations.size() == m_annotationInfo.size()); } /** * Print the annotation info. * * @param title * Title that is printed before the annotation * information is printed. */ void AnnotationArrangerExecutor::printAnnotationInfo(const QString& title) { if ( ! m_debugFlag) { return; } std::cout << std::endl; std::cout << qPrintable(title) << std::endl; for (std::vector::iterator annInfoIter = m_annotationInfo.begin(); annInfoIter != m_annotationInfo.end(); annInfoIter++) { const AnnotationInfo& annInfo = *annInfoIter; annInfo.print(); } std::cout << std::endl; } /** * Align an annotation to the given window coordinate value. * * @param alignToWindowCoordinateValue * Align to window coordinate value. * @param annotationInfo * Information for annotation that is aligned. * @param annotationsBeforeMoving * If an annotation is moved, upon exit this will * contain the annotation before it is moved and * is used by the redo/undo command. * @param annotationsAfterMoving * If an annotation is moved, upon exit this will * contain the annotation after it is moved and * is used by the redo/undo command. * @throw CaretException * If there is an error. */ void AnnotationArrangerExecutor::alignAnnotationToValue(const float alignToWindowCoordinateValue, AnnotationInfo& annotationInfo, std::vector& annotationsBeforeMoving, std::vector& annotationsAfterMoving) { /* * Set amount of movement needed to align the annotation. */ float dx = 0.0; float dy = 0.0; switch (m_alignment) { case AnnotationAlignmentEnum::ALIGN_BOTTOM: dy = alignToWindowCoordinateValue - annotationInfo.m_windowBoundingBox.getMinY(); break; case AnnotationAlignmentEnum::ALIGN_CENTER: dx = alignToWindowCoordinateValue - (annotationInfo.m_windowBoundingBox.getMinX() + annotationInfo.m_windowBoundingBox.getMaxX()) / 2.0; break; case AnnotationAlignmentEnum::ALIGN_LEFT: dx = alignToWindowCoordinateValue - annotationInfo.m_windowBoundingBox.getMinX(); break; case AnnotationAlignmentEnum::ALIGN_MIDDLE: dy = alignToWindowCoordinateValue - (annotationInfo.m_windowBoundingBox.getMinY() + annotationInfo.m_windowBoundingBox.getMaxY()) / 2.0; break; case AnnotationAlignmentEnum::ALIGN_RIGHT: dx = alignToWindowCoordinateValue - annotationInfo.m_windowBoundingBox.getMaxX(); break; case AnnotationAlignmentEnum::ALIGN_TOP: dy = alignToWindowCoordinateValue - annotationInfo.m_windowBoundingBox.getMaxY(); break; } if (m_debugFlag) { std::cout << "Moving " << qPrintable(annotationInfo.m_annotation->toString()) << " by dx=" << dx << ", dy=" << dy << std::endl; } Annotation* annotationModified = annotationInfo.m_annotation->clone(); bool modifiedFlag = annotationInfo.moveAnnotationByXY(annotationModified, dx, dy); if (modifiedFlag) { annotationsBeforeMoving.push_back(annotationInfo.m_annotation); annotationsAfterMoving.push_back(annotationModified); } else { delete annotationModified; } } /** * Get the viewport for the given tab. Viewports are cached for efficiency. * * @param tabIndex * Index of the tab. * @param tabViewportOut * Viewport of the tab. * @throw CaretException * If there is an error. */ void AnnotationArrangerExecutor::getTabViewport(const int32_t tabIndex, int32_t tabViewportOut[4]) { std::map::iterator iter = m_tabViewports.find(tabIndex); if (iter != m_tabViewports.end()) { tabViewportOut[0] = iter->second.m_viewport[0]; tabViewportOut[1] = iter->second.m_viewport[1]; tabViewportOut[2] = iter->second.m_viewport[2]; tabViewportOut[3] = iter->second.m_viewport[3]; return; } EventGetViewportSize vpEvent(EventGetViewportSize::MODE_TAB_AFTER_MARGINS_INDEX, tabIndex); EventManager::get()->sendEvent(vpEvent.getPointer()); if (vpEvent.isViewportSizeValid()) { ViewportArray viewportArray; vpEvent.getViewportSize(tabViewportOut); viewportArray.m_viewport[0] = tabViewportOut[0]; viewportArray.m_viewport[1] = tabViewportOut[1]; viewportArray.m_viewport[2] = tabViewportOut[2]; viewportArray.m_viewport[3] = tabViewportOut[3]; m_tabViewports.insert(std::make_pair(tabIndex, viewportArray)); } else { throw CaretException("Failed to get viewport size for tab index " + QString::number(tabIndex + 1)); } } /** * Get a description of this object's content. * @return String describing this object's content. */ AString AnnotationArrangerExecutor::toString() const { return "AnnotationArrangerExecutor"; } /** * \class caret::AnnotationInfo * \brief Contains information used to arrange annotations. * \ingroup Brain */ /** * Constructor. * * @param annotation * The annotation. * @param viewport * Viewport coordinates containing the annotation. * @param windowBoundingBox * Bounding box for the annotation in window coordinates. * @param viewportPixelOneXY * XY coordinate of the annotation in its viewport. * @param viewportPixelTwoXY * optional second XY coordinate of the annotation in its viewport. * @param viewportPixelTwoXYValidValid * True if optional second XY coordinate is valid. */ AnnotationArrangerExecutor::AnnotationInfo::AnnotationInfo(Annotation* annotation, const int32_t viewport[4], const BoundingBox windowBoundingBox, float viewportPixelOneXY[2], float viewportPixelTwoXY[2], const bool viewportPixelTwoXYValidValid) : m_annotation(annotation) { m_viewport[0] = viewport[0]; m_viewport[1] = viewport[1]; m_viewport[2] = viewport[2]; m_viewport[3] = viewport[3]; m_viewportPixelOneXY[0] = viewportPixelOneXY[0]; m_viewportPixelOneXY[1] = viewportPixelOneXY[1]; m_viewportPixelTwoXY[0] = viewportPixelTwoXY[0]; m_viewportPixelTwoXY[1] = viewportPixelTwoXY[1]; m_windowBoundingBox = windowBoundingBox; m_windowPixelOneXY[0] = m_viewport[0] + viewportPixelOneXY[0]; m_windowPixelOneXY[1] = m_viewport[1] + viewportPixelOneXY[1]; m_windowPixelTwoXY[0] = m_viewport[0] + viewportPixelTwoXY[0]; m_windowPixelTwoXY[1] = m_viewport[1] + viewportPixelTwoXY[1]; m_windowPixelTwoXYValidFlag = viewportPixelTwoXYValidValid; if (m_windowPixelTwoXYValidFlag) { m_windowMidPointXY[0] = (m_windowPixelOneXY[0] + m_windowPixelTwoXY[0]) / 2.0; m_windowMidPointXY[1] = (m_windowPixelOneXY[1] + m_windowPixelTwoXY[1]) / 2.0; } else { m_windowMidPointXY[0] = m_windowPixelOneXY[0]; m_windowMidPointXY[1] = m_windowPixelOneXY[1]; } } /** * Is this XY coordinate a valid relative coordinate, * in the range [0.0, 100.0]? * * @return x * The x-coordinate * @param y * The y-coordinate * @return * True if valid relative coordinate, else false. */ bool AnnotationArrangerExecutor::AnnotationInfo::isValidRelativeCoord(const float x, const float y) const { if ((x >= 0.0) && (x <= 100.0) && (y >= 0.0) && (y <= 100.0)) { return true; } return false; } /** * Move the given annotation by the given amounts. * The annotation is moved only if its new coordinates * remain in the viewport. * * @return x * The x-coordinate * @param y * The y-coordinate * @return * True if annotation was moved, else false. */bool AnnotationArrangerExecutor::AnnotationInfo::moveAnnotationByXY(Annotation* annotation, const float dx, const float dy) const { CaretAssert(m_annotation); AnnotationOneDimensionalShape* oneDimAnn = dynamic_cast(annotation); AnnotationTwoDimensionalShape* twoDimAnn = dynamic_cast(annotation); const float newPixelOneX = m_viewportPixelOneXY[0] + dx; const float relativeOneX = (newPixelOneX / m_viewport[2]) * 100.0; const float newPixelOneY = m_viewportPixelOneXY[1] + dy; const float relativeOneY = (newPixelOneY / m_viewport[3]) * 100.0; if (isValidRelativeCoord(relativeOneX, relativeOneY)) { if (twoDimAnn != NULL) { float xyz[3]; twoDimAnn->getCoordinate()->getXYZ(xyz); xyz[0] = relativeOneX; xyz[1] = relativeOneY; twoDimAnn->getCoordinate()->setXYZ(xyz); return true; } else if (oneDimAnn) { const float newPixelTwoX = m_viewportPixelTwoXY[0] + dx; const float relativeTwoX = (newPixelTwoX / m_viewport[2]) * 100.0; const float newPixelTwoY = m_viewportPixelTwoXY[1] + dy; const float relativeTwoY = (newPixelTwoY / m_viewport[3]) * 100.0; if (isValidRelativeCoord(relativeTwoX, relativeTwoY)) { float xyz[3]; oneDimAnn->getStartCoordinate()->getXYZ(xyz); xyz[0] = relativeOneX; xyz[1] = relativeOneY; oneDimAnn->getStartCoordinate()->setXYZ(xyz); oneDimAnn->getEndCoordinate()->getXYZ(xyz); xyz[0] = relativeTwoX; xyz[1] = relativeTwoY; oneDimAnn->getEndCoordinate()->setXYZ(xyz); return true; } } } return false; } /** * Print information used to arrange an annotation. */ void AnnotationArrangerExecutor::AnnotationInfo::print() const { QString msg(m_annotation->toString() + "\n Window One XY: " + AString::fromNumbers(m_windowPixelOneXY, 2, ",") + "\n Window Two XY: " + AString::fromNumbers(m_windowPixelTwoXY, 2, ",") + "\n Window Mid Point XY: " + AString::fromNumbers(m_windowMidPointXY, 2, ",") + "\n Viewport XY: " + AString::fromNumbers(m_viewportPixelOneXY, 2, ",") + "\n Viewport: " + AString::fromNumbers(m_viewport, 4, ",") + "\n Bounds: " + m_windowBoundingBox.toString()); std::cout << qPrintable(msg) << std::endl; } connectome-workbench-1.2.3+git41-gc4c6c90/src/Brain/AnnotationArrangerExecutor.h000066400000000000000000000133561300200146000272730ustar00rootroot00000000000000#ifndef __ANNOTATION_ARRANGER_EXECUTOR_H__ #define __ANNOTATION_ARRANGER_EXECUTOR_H__ /*LICENSE_START*/ /* * Copyright (C) 2015 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 "AnnotationAlignmentEnum.h" #include "AnnotationDistributeEnum.h" #include "BoundingBox.h" #include "CaretObject.h" namespace caret { class Annotation; class AnnotationArrangerInputs; class AnnotationManager; class AnnotationArrangerExecutor : public CaretObject { public: AnnotationArrangerExecutor(); virtual ~AnnotationArrangerExecutor(); bool alignAnnotations(AnnotationManager* annotationManager, const AnnotationArrangerInputs& arrangerInputs, const AnnotationAlignmentEnum::Enum alignment, AString& errorMessageOut); bool distributeAnnotations(AnnotationManager* annotationManager, const AnnotationArrangerInputs& arrangerInputs, const AnnotationDistributeEnum::Enum distribute, AString& errorMessageOut); // ADD_NEW_METHODS_HERE virtual AString toString() const; private: enum Mode { MODE_NONE, MODE_ALIGN, MODE_DISTRIBUTE }; class AnnotationInfo { public: AnnotationInfo(Annotation* annotation, const int32_t viewport[4], const BoundingBox windowBoundingBox, float viewportPixelOneXY[2], float viewportPixelTwoXY[2], const bool viewportPixelTwoXYValidValid); bool isValidRelativeCoord(const float x, const float y) const; bool moveAnnotationByXY(Annotation* annotation, const float dx, const float dy) const; void print() const; Annotation* m_annotation; int32_t m_viewport[4]; float m_viewportPixelOneXY[2]; float m_viewportPixelTwoXY[2]; BoundingBox m_windowBoundingBox; float m_windowPixelOneXY[2]; float m_windowPixelTwoXY[2]; bool m_windowPixelTwoXYValidFlag; float m_windowMidPointXY[2]; }; class SortForDistributeFunctionObject { public: SortForDistributeFunctionObject(const AnnotationDistributeEnum::Enum distribute); bool operator()(const AnnotationInfo& ann1, const AnnotationInfo& ann2) const; private: const AnnotationDistributeEnum::Enum m_distribute; }; AnnotationArrangerExecutor(const AnnotationArrangerExecutor&); AnnotationArrangerExecutor& operator=(const AnnotationArrangerExecutor&); void alignAnnotationsPrivate(const AnnotationArrangerInputs& arrangerInputs); void distributeAnnotationsPrivate(const AnnotationArrangerInputs& arrangerInputs); void alignAnnotationToValue(const float alignToWindowCoordinateValue, AnnotationInfo& annotationInfo, std::vector& annotationsBeforeMoving, std::vector& annotationsAfterMoving); void getAnnotationsForArranging(const AnnotationArrangerInputs& arrangerInputs, std::vector& annotationsOut) const; void getTabViewport(const int32_t tabIndex, int32_t tabViewportOut[4]); void initializeForArranging(const AnnotationArrangerInputs& arrangerInputs); void setupAnnotationInfo(const AnnotationArrangerInputs& arrangerInputs, std::vector& annotations); void printAnnotationInfo(const QString& title); Mode m_mode; AnnotationManager* m_annotationManager; int32_t m_windowViewport[4]; struct ViewportArray { int32_t m_viewport[4]; }; std::map m_tabViewports; BoundingBox m_allAnnotationsBoundingBox; std::vector m_annotationInfo; AnnotationAlignmentEnum::Enum m_alignment; AnnotationDistributeEnum::Enum m_distribute; bool m_debugFlag; // ADD_NEW_MEMBERS_HERE }; #ifdef __ANNOTATION_ARRANGER_EXECUTOR_DECLARE__ // #endif // __ANNOTATION_ARRANGER_EXECUTOR_DECLARE__ } // namespace #endif //__ANNOTATION_ARRANGER_EXECUTOR_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Brain/AnnotationArrangerInputs.cxx000066400000000000000000000044121300200146000273230ustar00rootroot00000000000000 /*LICENSE_START*/ /* * Copyright (C) 2015 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 ____ANNOTATION_ARRANGER_INPUTS__DECLARE__ #include "AnnotationArrangerInputs.h" #undef ____ANNOTATION_ARRANGER_INPUTS__DECLARE__ #include "CaretAssert.h" using namespace caret; /** * \class caret::AnnotationArrangerInputs * \brief Contains information for aligning, distributing annotations * \ingroup Brain */ /** * Constructor. * * @param textRenderInterface * Text render interface needed for bounds of text annotations. * @param alignment * Alignment to direction. * @param windowIndex * Index of window in which annotations are aligned. */ AnnotationArrangerInputs::AnnotationArrangerInputs(BrainOpenGLTextRenderInterface* textRenderInterface, const int32_t windowIndex) : CaretObject(), m_textRenderInterface(textRenderInterface), m_windowIndex(windowIndex) { } /** * Destructor. */ AnnotationArrangerInputs::~AnnotationArrangerInputs() { } /** * @return Text renderer needed for text annotation sizing. */ BrainOpenGLTextRenderInterface* AnnotationArrangerInputs::getTextRender() const { return m_textRenderInterface; } /** * @return The window index. */ int32_t AnnotationArrangerInputs::getWindowIndex() const { return m_windowIndex; } /** * Get a description of this object's content. * @return String describing this object's content. */ AString AnnotationArrangerInputs::toString() const { return "AnnotationArrangerInputs"; } connectome-workbench-1.2.3+git41-gc4c6c90/src/Brain/AnnotationArrangerInputs.h000066400000000000000000000040001300200146000267410ustar00rootroot00000000000000#ifndef __ANNOTATION_ARRANGER_INPUTS_H__ #define __ANNOTATION_ARRANGER_INPUTS_H__ /*LICENSE_START*/ /* * Copyright (C) 2015 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" namespace caret { class BrainOpenGLTextRenderInterface; class AnnotationArrangerInputs : public CaretObject { public: AnnotationArrangerInputs(BrainOpenGLTextRenderInterface* textRenderInterface, const int32_t windowIndex); virtual ~AnnotationArrangerInputs(); BrainOpenGLTextRenderInterface* getTextRender() const; int32_t getWindowIndex() const; // ADD_NEW_METHODS_HERE virtual AString toString() const; private: AnnotationArrangerInputs(const AnnotationArrangerInputs&); AnnotationArrangerInputs& operator=(const AnnotationArrangerInputs&); BrainOpenGLTextRenderInterface* m_textRenderInterface; const int32_t m_windowIndex; // ADD_NEW_MEMBERS_HERE }; #ifdef ____ANNOTATION_ARRANGER_INPUTS__DECLARE__ // #endif // ____ANNOTATION_ARRANGER_INPUTS__DECLARE__ } // namespace #endif //__ANNOTATION_ARRANGER_INPUTS_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Brain/AnnotationManager.cxx000066400000000000000000001025161300200146000257350ustar00rootroot00000000000000 /*LICENSE_START*/ /* * Copyright (C) 2015 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 #define __ANNOTATION_MANAGER_DECLARE__ #include "AnnotationManager.h" #undef __ANNOTATION_MANAGER_DECLARE__ #include "Annotation.h" #include "AnnotationArrangerExecutor.h" #include "AnnotationColorBar.h" #include "AnnotationFile.h" #include "AnnotationGroup.h" #include "AnnotationOneDimensionalShape.h" #include "AnnotationRedoUndoCommand.h" #include "AnnotationEditingSelectionInformation.h" #include "AnnotationTwoDimensionalShape.h" #include "Brain.h" #include "CaretAssert.h" #include "CaretLogger.h" #include "CaretUndoStack.h" #include "DisplayPropertiesAnnotation.h" #include "EventAnnotationColorBarGet.h" #include "EventAnnotationGroupGetWithKey.h" #include "EventGetDisplayedDataFiles.h" #include "EventManager.h" #include "SceneClass.h" #include "SceneClassAssistant.h" using namespace caret; /** * \class caret::AnnotationManager * \brief Manager for annotations. * \ingroup Brain */ /** * Constructor. */ AnnotationManager::AnnotationManager(Brain* brain) : CaretObject(), m_brain(brain) { CaretAssert(m_brain); m_clipboardAnnotationFile = NULL; m_clipboardAnnotation.grabNew(NULL); m_annotationRedoUndoStack.grabNew(new CaretUndoStack()); m_annotationRedoUndoStack->setUndoLimit(500); for (int32_t i = 0; i < BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_WINDOWS; i++) { m_annotationBeingDrawnInWindow[i] = NULL; m_selectionInformation[i] = new AnnotationEditingSelectionInformation(i); } m_sceneAssistant = new SceneClassAssistant(); } /** * Destructor. */ AnnotationManager::~AnnotationManager() { EventManager::get()->removeAllEventsFromListener(this); for (int32_t i = 0; i < BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_WINDOWS; i++) { if (m_annotationBeingDrawnInWindow[i] != NULL) { delete m_annotationBeingDrawnInWindow[i]; } delete m_selectionInformation[i]; } m_annotationRedoUndoStack->clear(); delete m_sceneAssistant; } /** * Reset the annotation manager. */ void AnnotationManager::reset() { m_annotationRedoUndoStack->clear(); } /** * Apply a new command to the selected annotations. After execution of * the command, the command is placed on the undo stack so that the * user can undo or redo the command. * * @param command * Command that will be applied to the selected annotations. * Annotation manager will take ownership of the command and * destroy it at the appropriate time. */ bool AnnotationManager::applyCommand(AnnotationRedoUndoCommand* command, AString& errorMessageOut) { return applyCommandInWindow(command, -1, errorMessageOut); } /** * Apply a new command to the selected annotations. After execution of * the command, the command is placed on the undo stack so that the * user can undo or redo the command. * * @param command * Command that will be applied to the selected annotations. * Annotation manager will take ownership of the command and * destroy it at the appropriate time. * @param windowIndex * Index of window in which command was requested. */ bool AnnotationManager::applyCommandInWindow(AnnotationRedoUndoCommand* command, const int32_t windowIndex, AString& errorMessageOut) { errorMessageOut.clear(); CaretAssert(command); /* * Ignore command if it does not apply to any annotations */ if ( ! command->isValid()) { delete command; errorMessageOut = "Command was not valid."; return false; } /* * "Redo" the command and add it to the undo stack */ const bool result = m_annotationRedoUndoStack->pushAndRedo(command, windowIndex, errorMessageOut); return result; } /** * Deselect all annotations for editing in the given window. * * @param windowIndex * Index of window for deselection of window annotations. */ void AnnotationManager::deselectAllAnnotationsForEditing(const int32_t windowIndex) { std::vector annotationFiles; m_brain->getAllAnnotationFilesIncludingSceneAnnotationFile(annotationFiles); for (std::vector::iterator fileIter = annotationFiles.begin(); fileIter != annotationFiles.end(); fileIter++) { AnnotationFile* file = *fileIter; CaretAssert(file); file->setAllAnnotationsSelectedForEditing(windowIndex, false); } EventAnnotationColorBarGet colorBarEvent; EventManager::get()->sendEvent(colorBarEvent.getPointer()); std::vector colorBars = colorBarEvent.getAnnotationColorBars(); for (std::vector::iterator iter = colorBars.begin(); iter != colorBars.end(); iter++) { AnnotationColorBar* cb = *iter; cb->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. * * @param windowIndex * Index of window for annotation selection. * @param selectionMode * The annotation selection mode. * @param shiftKeyDown * The shift key is down * @param selectedAnnotation * Annotation whose selection is updated. * May be NULL. */ void AnnotationManager::selectAnnotationForEditing(const int32_t windowIndex, const SelectionMode selectionMode, const bool shiftKeyDownFlag, Annotation* selectedAnnotation) { switch (selectionMode) { case SELECTION_MODE_EXTENDED: processExtendedModeSelectionForEditing(windowIndex, shiftKeyDownFlag, selectedAnnotation); break; case SELECTION_MODE_SINGLE: processSingleModeSelectionForEditing(windowIndex, selectedAnnotation); break; } /* * If the annotation is in a user group, select all * annotations in the group. */ if (selectedAnnotation != NULL) { const bool selectedStatus = selectedAnnotation->isSelectedForEditing(windowIndex); const AnnotationGroupKey groupKey = selectedAnnotation->getAnnotationGroupKey(); if (groupKey.getGroupType() == AnnotationGroupTypeEnum::USER) { EventAnnotationGroupGetWithKey getGroupEvent(groupKey); EventManager::get()->sendEvent(getGroupEvent.getPointer()); AnnotationGroup* annotationGroup = getGroupEvent.getAnnotationGroup(); if (annotationGroup != NULL) { annotationGroup->setAllAnnotationsSelectedForEditing(windowIndex, selectedStatus); } } } } /** * Set the annotations for editing to the given annotations in the given window. * * @param windowIndex * Index of window for annotation selection. * @param selectedAnnotations * Annotation that become the selected annotations. */ void AnnotationManager::setAnnotationsForEditing(const int32_t windowIndex, const std::vector& selectedAnnotations) { deselectAllAnnotationsForEditing(windowIndex); std::set uniqueAnnotationsSet; for (std::vector::const_iterator annIter = selectedAnnotations.begin(); annIter != selectedAnnotations.end(); annIter++) { Annotation* ann = *annIter; CaretAssert(ann); const AnnotationGroupKey groupKey = ann->getAnnotationGroupKey(); if (groupKey.getGroupType() == AnnotationGroupTypeEnum::USER) { EventAnnotationGroupGetWithKey getGroupEvent(groupKey); EventManager::get()->sendEvent(getGroupEvent.getPointer()); AnnotationGroup* annotationGroup = getGroupEvent.getAnnotationGroup(); if (annotationGroup != NULL) { std::vector groupAnns; annotationGroup->getAllAnnotations(groupAnns); uniqueAnnotationsSet.insert(groupAnns.begin(), groupAnns.end()); } } else { uniqueAnnotationsSet.insert(ann); } } for (std::set::iterator annIter = uniqueAnnotationsSet.begin(); annIter != uniqueAnnotationsSet.end(); annIter++) { Annotation* ann = *annIter; ann->setSelectedForEditing(windowIndex, true); } } /** * Process extended mode annotation selection for editing. * * @param windowIndex * Index of window for annotation selection. * @param shiftKeyDownFlag * True if the shift key is down. * @param selectedAnnotation * Annotation that was selected (NULL if no annotation selected). */ void AnnotationManager::processExtendedModeSelectionForEditing(const int32_t windowIndex, const bool shiftKeyDownFlag, Annotation* selectedAnnotation) { if (selectedAnnotation != NULL) { if (shiftKeyDownFlag) { /* * When shift key is down, the annotation's selection status * is toggled */ selectedAnnotation->setSelectedForEditing(windowIndex, ! selectedAnnotation->isSelectedForEditing(windowIndex)); } else { if (selectedAnnotation->isSelectedForEditing(windowIndex)) { /* cannot deselect an annotation WITHOUT shift key down */ } else { /* * Clicking an annotation without shift key selects the * annotation but deselects all other annotations. */ deselectAllAnnotationsForEditing(windowIndex); selectedAnnotation->setSelectedForEditing(windowIndex, true); } } } else { if (shiftKeyDownFlag) { /* do nothing with shift key down and no annotation clicked */ } else { deselectAllAnnotationsForEditing(windowIndex); } } } /** * Process single mode annotation selection for editing. * * @param windowIndex * Index of window for annotation selection. * @param selectedAnnotation * Annotation that was selected (NULL if no annotation selected). */ void AnnotationManager::processSingleModeSelectionForEditing(const int32_t windowIndex, Annotation* selectedAnnotation) { deselectAllAnnotationsForEditing(windowIndex); if (selectedAnnotation != NULL) { selectedAnnotation->setSelectedForEditing(windowIndex, true); } } /** * @return True if all of the selected annotations for editing are deletable. * * @param windowIndex * Index of window for annotation selection. */ bool AnnotationManager::isAnnotationSelectedForEditingDeletable(const int32_t windowIndex) const { bool selectedAnnotationsDeletableFlag = false; std::vector selectedAnnotations = getAnnotationsSelectedForEditing(windowIndex); if ( ! selectedAnnotations.empty()) { selectedAnnotationsDeletableFlag = true; for (std::vector::const_iterator iter = selectedAnnotations.begin(); iter != selectedAnnotations.end(); iter++) { const Annotation* ann = *iter; if ( ! ann->isDeletable()) { selectedAnnotationsDeletableFlag = false; break; } } } return selectedAnnotationsDeletableFlag; } /** * @return A vector containing all annotations. */ std::vector AnnotationManager::getAllAnnotations() const { std::vector annotationFiles; m_brain->getAllAnnotationFilesIncludingSceneAnnotationFile(annotationFiles); std::vector allAnnotations; for (std::vector::iterator fileIter = annotationFiles.begin(); fileIter != annotationFiles.end(); fileIter++) { AnnotationFile* file = *fileIter; CaretAssert(file); std::vector annotations; file->getAllAnnotations(annotations); if ( ! annotations.empty()) { allAnnotations.insert(allAnnotations.end(), annotations.begin(), annotations.end()); } } EventAnnotationColorBarGet colorBarEvent; EventManager::get()->sendEvent(colorBarEvent.getPointer()); std::vector colorBars = colorBarEvent.getAnnotationColorBars(); for (std::vector::iterator iter = colorBars.begin(); iter != colorBars.end(); iter++) { allAnnotations.push_back(*iter); } return allAnnotations; } /** * Get the annotation editing selection information for the given window. * * @param windowIndex * Index of window. * @return * Annotation selection information. */ const AnnotationEditingSelectionInformation* AnnotationManager::getAnnotationEditingSelectionInformation(const int32_t windowIndex) const { CaretAssertArrayIndex(m_selectionInformation, BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_WINDOWS, windowIndex); AnnotationEditingSelectionInformation* asi = m_selectionInformation[windowIndex]; std::vector allAnnotations = getAllAnnotations(); std::vector selectedAnnotations; for (std::vector::iterator annIter = allAnnotations.begin(); annIter != allAnnotations.end(); annIter++) { Annotation* annotation = *annIter; if (annotation->isSelectedForEditing(windowIndex)) { selectedAnnotations.push_back(annotation); } } asi->update(selectedAnnotations); return asi; } /** * @return A vector containing all SELECTED annotations for editing * * @param windowIndex * Index of window for annotation selection. */ std::vector AnnotationManager::getAnnotationsSelectedForEditing(const int32_t windowIndex) const { std::vector selectedAnnotations; const AnnotationEditingSelectionInformation* asi = getAnnotationEditingSelectionInformation(windowIndex); asi->getAnnotationsSelectedForEditing(selectedAnnotations); return selectedAnnotations; } /** * @return A vector containing all SELECTED annotations for editing and in the * given spaces. * * @param windowIndex * Index of window for annotation selection. * @param spaces * Limit returned annotations to annotations in these spaces. */ std::vector AnnotationManager::getAnnotationsSelectedForEditingInSpaces(const int32_t windowIndex, const std::vector& spaces) const { std::vector annotations = getAnnotationsSelectedForEditing(windowIndex); std::vector annotationsOut; for (std::vector::iterator iter = annotations.begin(); iter != annotations.end(); iter++) { Annotation* ann = *iter; CaretAssert(ann); const AnnotationCoordinateSpaceEnum::Enum annSpace = ann->getCoordinateSpace(); if (std::find(spaces.begin(), spaces.end(), annSpace) != spaces.end()) { annotationsOut.push_back(ann); } } return annotationsOut; } /** * Get the selected annotations for editing and the files that contain them. * * @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::getAnnotationsSelectedForEditing(const int32_t windowIndex, std::vector >& annotationsAndFileOut) const { annotationsAndFileOut.clear(); std::vector annotationFiles; m_brain->getAllAnnotationFilesIncludingSceneAnnotationFile(annotationFiles); for (std::vector::iterator fileIter = annotationFiles.begin(); fileIter != annotationFiles.end(); fileIter++) { AnnotationFile* file = *fileIter; CaretAssert(file); std::vector annotations; file->getAllAnnotations(annotations); for (std::vector::iterator annIter = annotations.begin(); annIter != annotations.end(); annIter++) { Annotation* ann = *annIter; if (ann->isSelectedForEditing(windowIndex)) { annotationsAndFileOut.push_back(std::make_pair(ann, file)); } } } } /** * Align annotations. * * @param arrangerInputs * Inputs to algorithm that aligns the annotations. * @param errorMessageOut * Contains error message upon exit. * @return * True if successful, false if error. */ bool AnnotationManager::alignAnnotations(const AnnotationArrangerInputs& arrangerInputs, const AnnotationAlignmentEnum::Enum alignment, AString& errorMessageOut) { AnnotationArrangerExecutor arranger; return arranger.alignAnnotations(this, arrangerInputs, alignment, errorMessageOut); } /** * Align annotations. * * @param arrangerInputs * Inputs to algorithm that aligns the annotations. * @param errorMessageOut * Contains error message upon exit. * @return * True if successful, false if error. */ bool AnnotationManager::distributeAnnotations(const AnnotationArrangerInputs& arrangerInputs, const AnnotationDistributeEnum::Enum distribute, AString& errorMessageOut) { AnnotationArrangerExecutor arranger; return arranger.distributeAnnotations(this, arrangerInputs, distribute, errorMessageOut); } /** * Apply given grouping mode valid in the given window. * * @param windowIndex * Index of the window. * @param groupingMode * The grouping mode. * @param errorMessageOut * Contains error message if grouping fails. * @return * True if successful, else false. */ bool AnnotationManager::applyGroupingMode(const int32_t windowIndex, const AnnotationGroupingModeEnum::Enum groupingMode, AString& errorMessageOut) { errorMessageOut.clear(); const AnnotationEditingSelectionInformation* selectionInfo = getAnnotationEditingSelectionInformation(windowIndex); CaretAssert(selectionInfo); if ( ! selectionInfo->isGroupingModeValid(groupingMode)) { const QString msg("PROGRAM ERROR: AnnotationMenuArrange::applyGrouping " "should not have been called. Grouping mode " + AnnotationGroupingModeEnum::toGuiName(groupingMode) + " is invalid for the selected annotations."); CaretAssertMessage(0, msg); errorMessageOut = msg; return false; } std::vector groupKeys = selectionInfo->getSelectedAnnotationGroupKeys(); std::vector annotations = selectionInfo->getAnnotationsSelectedForEditing(); bool validFlag = false; switch (groupingMode) { case AnnotationGroupingModeEnum::GROUP: { if (groupKeys.size() != 1) { const QString msg("PROGRAM ERROR: AnnotationMenuArrange::applyGrouping " "should not have been called. More than one selected group."); CaretAssertMessage(0, msg); errorMessageOut = msg; return false; } CaretAssertVectorIndex(groupKeys, 0); const AnnotationGroupKey annotationGroupKey = groupKeys[0]; AnnotationRedoUndoCommand* command = new AnnotationRedoUndoCommand(); command->setModeGroupingGroupAnnotations(annotationGroupKey, annotations); validFlag = applyCommandInWindow(command, windowIndex, errorMessageOut); } break; case AnnotationGroupingModeEnum::REGROUP: { if (groupKeys.size() != 1) { const QString msg("PROGRAM ERROR: AnnotationMenuArrange::applyGrouping " "should not have been called. More than one selected group."); CaretAssertMessage(0, msg); errorMessageOut = msg; return false; } CaretAssertVectorIndex(groupKeys, 0); const AnnotationGroupKey annotationGroupKey = groupKeys[0]; AnnotationRedoUndoCommand* command = new AnnotationRedoUndoCommand(); command->setModeGroupingRegroupAnnotations(annotationGroupKey); validFlag = applyCommandInWindow(command, windowIndex, errorMessageOut); } break; case AnnotationGroupingModeEnum::UNGROUP: { if (groupKeys.size() != 1) { const QString msg("PROGRAM ERROR: AnnotationMenuArrange::applyGrouping " "should not have been called. More than one selected group."); CaretAssertMessage(0, msg); errorMessageOut = msg; return false; } CaretAssertVectorIndex(groupKeys, 0); const AnnotationGroupKey annotationGroupKey = groupKeys[0]; AnnotationRedoUndoCommand* command = new AnnotationRedoUndoCommand(); command->setModeGroupingUngroupAnnotations(annotationGroupKey); validFlag = applyCommandInWindow(command, windowIndex, errorMessageOut); } break; } return validFlag; } /** * Is the given grouping mode valid in the given window. * * @param windowIndex * Index of the window. * @param groupingMode * The grouping mode. */ bool AnnotationManager::isGroupingModeValid(const int32_t windowIndex, const AnnotationGroupingModeEnum::Enum groupingMode) const { return getAnnotationEditingSelectionInformation(windowIndex)->isGroupingModeValid(groupingMode); } /** * Receive an event. * * @param event * An event for which this instance is listening. */ void AnnotationManager::receiveEvent(Event* /*event*/) { } /** * Get a description of this object's content. * @return String describing this object's content. */ AString AnnotationManager::toString() const { return "AnnotationManager"; } /** * @return True if there is an annotation on the clipboard. */ bool AnnotationManager::isAnnotationOnClipboardValid() const { return (m_clipboardAnnotation != NULL); } /** * @return Pointer to annotation file on clipboard. * If there is not annotation file on the clipboard, * NULL is returned. */ AnnotationFile* AnnotationManager::getAnnotationFileOnClipboard() const { if (m_clipboardAnnotationFile != NULL) { /* * It is possible that the file has been destroyed. * If so, invalidate the file (set it to NULL). */ std::vector allAnnotationFiles; m_brain->getAllAnnotationFilesIncludingSceneAnnotationFile(allAnnotationFiles); if (std::find(allAnnotationFiles.begin(), allAnnotationFiles.end(), m_clipboardAnnotationFile) == allAnnotationFiles.end()) { m_clipboardAnnotationFile = NULL; } } return m_clipboardAnnotationFile; } /** * @return Pointer to annotation on clipboard. * If there is not annotation on the clipboard, * NULL is returned. */ const Annotation* AnnotationManager::getAnnotationOnClipboard() const { return m_clipboardAnnotation; } /** * @return A copy of the annotation on the clipboard. * If there is not annotation on the clipboard, * NULL is returned. */ Annotation* AnnotationManager::getCopyOfAnnotationOnClipboard() const { if (m_clipboardAnnotation != NULL) { return m_clipboardAnnotation->clone(); } return NULL; } void AnnotationManager::copyAnnotationToClipboard(const AnnotationFile* annotationFile, const Annotation* annotation) { m_clipboardAnnotationFile = const_cast(annotationFile); m_clipboardAnnotation.grabNew(annotation->clone()); } /** * Get the annotation being drawn in window with the given window index. * * @param windowIndex * Index of window. * @return * Pointer to the annotation (will be NULL if no annotation is being drawn). */ const Annotation* AnnotationManager::getAnnotationBeingDrawnInWindow(const int32_t windowIndex) const { CaretAssertArrayIndex(m_annotationBeingDrawnInWindow, BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_WINDOWS, windowIndex); return m_annotationBeingDrawnInWindow[windowIndex]; } /** * Set the annotation being drawn in window with the given window index. * * @param windowIndex * Index of window. * @param annotation * Pointer to the annotation (will be NULL if no annotation is being drawn). */ void AnnotationManager::setAnnotationBeingDrawnInWindow(const int32_t windowIndex, const Annotation* annotation) { CaretAssertArrayIndex(m_annotationBeingDrawnInWindow, BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_WINDOWS, windowIndex); if (m_annotationBeingDrawnInWindow[windowIndex] != NULL) { delete m_annotationBeingDrawnInWindow[windowIndex]; m_annotationBeingDrawnInWindow[windowIndex] = NULL; } if (annotation != NULL) { m_annotationBeingDrawnInWindow[windowIndex] = annotation->clone(); } } /** * Find annotation files that are displayed. * * @param displayedFilesEvent * Event that queries for displayed data files. * @param displayedAnnotationFilesOut * Output that contains annotation files that are displayed. */ void AnnotationManager::getDisplayedAnnotationFiles(EventGetDisplayedDataFiles* displayedFilesEvent, std::vector& displayedAnnotationFilesOut) const { displayedAnnotationFilesOut.clear(); const std::vector tabIndices = displayedFilesEvent->getTabIndices(); // const DisplayPropertiesAnnotation* annProps = m_brain->getDisplayPropertiesAnnotation(); std::vector annotationFiles; m_brain->getAllAnnotationFilesIncludingSceneAnnotationFile(annotationFiles); const int32_t numAnnFiles = static_cast(annotationFiles.size()); for (int32_t iFile = 0; iFile < numAnnFiles; iFile++) { CaretAssertVectorIndex(annotationFiles, iFile); const AnnotationFile* annFile = annotationFiles[iFile]; std::vector annotations; annFile->getAllAnnotations(annotations); const int32_t numberOfAnnotations = static_cast(annotations.size()); for (int32_t i = 0; i < numberOfAnnotations; i++) { CaretAssertVectorIndex(annotations, i); const Annotation* ann = annotations[i]; CaretAssert(ann); bool displayedFlag = false; switch (ann->getCoordinateSpace()) { case AnnotationCoordinateSpaceEnum::PIXELS: break; case AnnotationCoordinateSpaceEnum::STEREOTAXIC: displayedFlag = true; break; case AnnotationCoordinateSpaceEnum::SURFACE: displayedFlag = true; break; case AnnotationCoordinateSpaceEnum::TAB: displayedFlag = true; break; case AnnotationCoordinateSpaceEnum::WINDOW: displayedFlag = true; break; } if (displayedFlag) { displayedFilesEvent->addDisplayedDataFile(annFile); break; } } } } /** * @return Pointer to the command redo undo stack */ CaretUndoStack* AnnotationManager::getCommandRedoUndoStack() { return m_annotationRedoUndoStack; } /** * 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* AnnotationManager::saveToScene(const SceneAttributes* sceneAttributes, const AString& instanceName) { SceneClass* sceneClass = new SceneClass(instanceName, "AnnotationManager", 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 AnnotationManager::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); } connectome-workbench-1.2.3+git41-gc4c6c90/src/Brain/AnnotationManager.h000066400000000000000000000215661300200146000253670ustar00rootroot00000000000000#ifndef __ANNOTATION_MANAGER_H__ #define __ANNOTATION_MANAGER_H__ /*LICENSE_START*/ /* * Copyright (C) 2015 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 "AnnotationAlignmentEnum.h" #include "AnnotationCoordinateSpaceEnum.h" #include "AnnotationDistributeEnum.h" #include "AnnotationGroupingModeEnum.h" #include "BrainConstants.h" #include "CaretObject.h" #include "CaretUndoCommand.h" #include "CaretPointer.h" #include "EventListenerInterface.h" #include "SceneableInterface.h" namespace caret { class Annotation; class AnnotationArrangerInputs; class AnnotationFile; class AnnotationGroupKey; class AnnotationRedoUndoCommand; class AnnotationEditingSelectionInformation; class Brain; class CaretUndoStack; class EventGetDisplayedDataFiles; class SceneClassAssistant; class AnnotationManager : public CaretObject, public EventListenerInterface, public SceneableInterface { public: /** * Selection mode with the comments and functionality similar to QAbstractItemView::SelectionMode */ enum SelectionMode { /* * When the user selects an annotation, any already-selected annotation becomes unselected, * and the user cannot unselect the selected annotation by clicking on it. */ SELECTION_MODE_SINGLE, /** * When the user selects an annotation in the usual way, the selection is cleared * and the new annotation selected. However, if the user presses the Ctrl key when * clicking on an annotation, the clicked annotation gets toggled and all other annotation * are left untouched. */ SELECTION_MODE_EXTENDED }; AnnotationManager(Brain* brain); virtual ~AnnotationManager(); bool applyCommand(AnnotationRedoUndoCommand* command, AString& errorMessageOut); bool applyCommandInWindow(AnnotationRedoUndoCommand* command, const int32_t windowIndex, AString& errorMessageOut); void reset(); void deselectAllAnnotationsForEditing(const int32_t windowIndex); void selectAnnotationForEditing(const int32_t windowIndex, const SelectionMode selectionMode, const bool shiftKeyDownFlag, Annotation* selectedAnnotation); void setAnnotationsForEditing(const int32_t windowIndex, const std::vector& selectedAnnotations); bool isAnnotationSelectedForEditingDeletable(const int32_t windowIndex) const; std::vector getAllAnnotations() const; const AnnotationEditingSelectionInformation* getAnnotationEditingSelectionInformation(const int32_t windowIndex) const; std::vector getAnnotationsSelectedForEditing(const int32_t windowIndex) const; std::vector getAnnotationsSelectedForEditingInSpaces(const int32_t windowIndex, const std::vector& spaces) const; void getAnnotationsSelectedForEditing(const int32_t windowIndex, std::vector >& annotationsAndFileOut) const; // std::vector getFilesContainingAnnotations(const std::vector annotations) const; bool isAnnotationOnClipboardValid() const; AnnotationFile* getAnnotationFileOnClipboard() const; const Annotation* getAnnotationOnClipboard() const; Annotation* getCopyOfAnnotationOnClipboard() const; void copyAnnotationToClipboard(const AnnotationFile* annotationFile, const Annotation* annotation); const Annotation* getAnnotationBeingDrawnInWindow(const int32_t windowIndex) const; void setAnnotationBeingDrawnInWindow(const int32_t windowIndex, const Annotation* annotation); CaretUndoStack* getCommandRedoUndoStack(); void getDisplayedAnnotationFiles(EventGetDisplayedDataFiles* displayedFilesEvent, std::vector& displayedAnnotationFilesOut) const; bool alignAnnotations(const AnnotationArrangerInputs& arrangerInputs, const AnnotationAlignmentEnum::Enum alignment, AString& errorMessageOut); bool distributeAnnotations(const AnnotationArrangerInputs& arrangerInputs, const AnnotationDistributeEnum::Enum distribute, AString& errorMessageOut); bool applyGroupingMode(const int32_t windowIndex, const AnnotationGroupingModeEnum::Enum groupingMode, AString& errorMessageOut); bool isGroupingModeValid(const int32_t windowIndex, const AnnotationGroupingModeEnum::Enum groupingMode) const; // 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 implemetation by sub-classes. // protected: // virtual void saveSubClassDataToScene(const SceneAttributes* sceneAttributes, // SceneClass* sceneClass) = 0; // // virtual void restoreSubClassDataFromScene(const SceneAttributes* sceneAttributes, // const SceneClass* sceneClass) = 0; private: AnnotationManager(const AnnotationManager&); AnnotationManager& operator=(const AnnotationManager&); void processExtendedModeSelectionForEditing(const int32_t windowIndex, const bool shiftKeyDownFlag, Annotation* selectedAnnotation); void processSingleModeSelectionForEditing(const int32_t windowIndex, Annotation* selectedAnnotation); SceneClassAssistant* m_sceneAssistant; /** Brain owning this manager */ Brain* m_brain; Annotation* m_annotationBeingDrawnInWindow[BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_WINDOWS]; /* * DO NOT directly reference this. Instead, call this class' * getAnnotationEditingSelectionInformation() method so that the selection * information is updated. */ mutable AnnotationEditingSelectionInformation* m_selectionInformation[BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_WINDOWS]; /** * Do not use a Caret Pointer for this as it points to a file in the brain. * If a pointer was used it may get deleted which will cause deletion of the * file that is owned by the Brain. */ mutable AnnotationFile* m_clipboardAnnotationFile; CaretPointer m_clipboardAnnotation; CaretPointer m_annotationRedoUndoStack; // ADD_NEW_MEMBERS_HERE }; #ifdef __ANNOTATION_MANAGER_DECLARE__ // #endif // __ANNOTATION_MANAGER_DECLARE__ } // namespace #endif //__ANNOTATION_MANAGER_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Brain/BorderDrawingTypeEnum.cxx000066400000000000000000000227341300200146000265530ustar00rootroot00000000000000 /*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 __BORDER_DRAWING_TYPE_ENUM_DECLARE__ #include "BorderDrawingTypeEnum.h" #undef __BORDER_DRAWING_TYPE_ENUM_DECLARE__ #include "CaretAssert.h" using namespace caret; /** * \class caret::BorderDrawingTypeEnum * \brief * * */ /** * Constructor. * * @param enumValue * An enumerated value. * @param name * Name of enumerated value. * * @param guiName * User-friendly name for use in user-interface. */ BorderDrawingTypeEnum::BorderDrawingTypeEnum(const Enum enumValue, const AString& name, const AString& guiName) { this->enumValue = enumValue; this->integerCode = integerCodeCounter++; this->name = name; this->guiName = guiName; } /** * Destructor. */ BorderDrawingTypeEnum::~BorderDrawingTypeEnum() { } /** * Initialize the enumerated metadata. */ void BorderDrawingTypeEnum::initialize() { if (initializedFlag) { return; } initializedFlag = true; enumData.push_back(BorderDrawingTypeEnum(DRAW_AS_LINES, "DRAW_AS_LINES", "Lines")); enumData.push_back(BorderDrawingTypeEnum(DRAW_AS_POINTS_SPHERES, "DRAW_AS_POINTS_SPHERES", "Spheres")); enumData.push_back(BorderDrawingTypeEnum(DRAW_AS_POINTS_SQUARES, "DRAW_AS_POINTS_SQUARES", "Squares")); enumData.push_back(BorderDrawingTypeEnum(DRAW_AS_POINTS_AND_LINES, "DRAW_AS_POINTS_AND_LINES", "Spheres and Lines")); } /** * 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 BorderDrawingTypeEnum* BorderDrawingTypeEnum::findData(const Enum enumValue) { if (initializedFlag == false) initialize(); size_t num = enumData.size(); for (size_t i = 0; i < num; i++) { const BorderDrawingTypeEnum* 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 BorderDrawingTypeEnum::toName(Enum enumValue) { if (initializedFlag == false) initialize(); const BorderDrawingTypeEnum* 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. */ BorderDrawingTypeEnum::Enum BorderDrawingTypeEnum::fromName(const AString& name, bool* isValidOut) { if (initializedFlag == false) initialize(); bool validFlag = false; Enum enumValue = DRAW_AS_LINES; for (std::vector::iterator iter = enumData.begin(); iter != enumData.end(); iter++) { const BorderDrawingTypeEnum& 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 BorderDrawingTypeEnum")); } return enumValue; } /** * Get a GUI string representation of the enumerated type. * @param enumValue * Enumerated value. * @return * String representing enumerated value. */ AString BorderDrawingTypeEnum::toGuiName(Enum enumValue) { if (initializedFlag == false) initialize(); const BorderDrawingTypeEnum* 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. */ BorderDrawingTypeEnum::Enum BorderDrawingTypeEnum::fromGuiName(const AString& guiName, bool* isValidOut) { if (initializedFlag == false) initialize(); bool validFlag = false; Enum enumValue = DRAW_AS_LINES; for (std::vector::iterator iter = enumData.begin(); iter != enumData.end(); iter++) { const BorderDrawingTypeEnum& 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 BorderDrawingTypeEnum")); } return enumValue; } /** * Get the integer code for a data type. * * @return * Integer code for data type. */ int32_t BorderDrawingTypeEnum::toIntegerCode(Enum enumValue) { if (initializedFlag == false) initialize(); const BorderDrawingTypeEnum* 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. */ BorderDrawingTypeEnum::Enum BorderDrawingTypeEnum::fromIntegerCode(const int32_t integerCode, bool* isValidOut) { if (initializedFlag == false) initialize(); bool validFlag = false; Enum enumValue = DRAW_AS_LINES; for (std::vector::iterator iter = enumData.begin(); iter != enumData.end(); iter++) { const BorderDrawingTypeEnum& 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 BorderDrawingTypeEnum")); } 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 BorderDrawingTypeEnum::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 BorderDrawingTypeEnum::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(BorderDrawingTypeEnum::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 BorderDrawingTypeEnum::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(BorderDrawingTypeEnum::toGuiName(iter->enumValue)); } if (isSorted) { std::sort(allGuiNames.begin(), allGuiNames.end()); } } connectome-workbench-1.2.3+git41-gc4c6c90/src/Brain/BorderDrawingTypeEnum.h000066400000000000000000000064331300200146000261760ustar00rootroot00000000000000#ifndef __BORDER_DRAWING_TYPE_ENUM__H_ #define __BORDER_DRAWING_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 BorderDrawingTypeEnum { public: /** * Enumerated values for border drawing types. */ enum Enum { /** draw as lines */ DRAW_AS_LINES, /** draw as spherical points */ DRAW_AS_POINTS_SPHERES, /** draw as square points */ DRAW_AS_POINTS_SQUARES, /** draw as points and lines */ DRAW_AS_POINTS_AND_LINES }; ~BorderDrawingTypeEnum(); 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: BorderDrawingTypeEnum(const Enum enumValue, const AString& name, const AString& guiName); static const BorderDrawingTypeEnum* 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 __BORDER_DRAWING_TYPE_ENUM_DECLARE__ std::vector BorderDrawingTypeEnum::enumData; bool BorderDrawingTypeEnum::initializedFlag = false; int32_t BorderDrawingTypeEnum::integerCodeCounter = 0; #endif // __BORDER_DRAWING_TYPE_ENUM_DECLARE__ } // namespace #endif //__BORDER_DRAWING_TYPE_ENUM__H_ connectome-workbench-1.2.3+git41-gc4c6c90/src/Brain/Brain.cxx000066400000000000000000007257301300200146000233740ustar00rootroot00000000000000/*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 "CaretAssert.h" #include "AnnotationFile.h" #include "AnnotationManager.h" #include "Border.h" #include "BorderFile.h" #include "BorderPointFromSearch.h" #include "Brain.h" #include "BrainordinateRegionOfInterest.h" #include "BrainStructure.h" #include "BrowserTabContent.h" #include "CaretDataFileHelper.h" #include "CaretLogger.h" #include "CaretPreferences.h" #include "ChartingDataManager.h" #include "ChartableLineSeriesBrainordinateInterface.h" #include "CiftiBrainordinateDataSeriesFile.h" #include "CiftiBrainordinateLabelFile.h" #include "CiftiBrainordinateScalarFile.h" #include "CiftiConnectivityMatrixDenseFile.h" #include "CiftiConnectivityMatrixDenseDynamicFile.h" #include "CiftiConnectivityMatrixDenseParcelFile.h" #include "CiftiFiberOrientationFile.h" #include "CiftiFiberTrajectoryFile.h" #include "CiftiConnectivityMatrixParcelFile.h" #include "CiftiConnectivityMatrixParcelDenseFile.h" #include "CiftiParcelLabelFile.h" #include "CiftiParcelSeriesFile.h" #include "CiftiParcelScalarFile.h" #include "CiftiScalarDataSeriesFile.h" #include "DisplayPropertiesAnnotation.h" #include "DisplayPropertiesBorders.h" #include "DisplayPropertiesFiberOrientation.h" #include "DisplayPropertiesFoci.h" #include "DisplayPropertiesImages.h" #include "DisplayPropertiesLabels.h" #include "DisplayPropertiesSurface.h" #include "DisplayPropertiesVolume.h" #include "ElapsedTimer.h" #include "EventBrainReset.h" #include "EventBrowserTabGetAll.h" #include "EventCaretMappableDataFilesGet.h" #include "EventDataFileAdd.h" #include "EventDataFileDelete.h" #include "EventDataFileRead.h" #include "EventDataFileReload.h" #include "EventGetDisplayedDataFiles.h" #include "EventModelAdd.h" #include "EventModelDelete.h" #include "EventModelGetAll.h" #include "EventPaletteGetByName.h" #include "EventProgressUpdate.h" #include "EventSpecFileReadDataFiles.h" #include "EventManager.h" #include "FiberOrientationSamplesLoader.h" #include "FileInformation.h" #include "FociFile.h" #include "GapsAndMargins.h" #include "GroupAndNameHierarchyModel.h" #include "IdentificationManager.h" #include "ImageFile.h" #include "MathFunctions.h" #include "MetricFile.h" #include "ModelChart.h" #include "ModelSurface.h" #include "ModelSurfaceMontage.h" #include "ModelVolume.h" #include "ModelWholeBrain.h" #include "LabelFile.h" #include "Overlay.h" #include "OverlaySet.h" #include "PaletteFile.h" #include "RgbaFile.h" #include "SceneAttributes.h" #include "SceneClass.h" #include "SceneClassArray.h" #include "SceneClassAssistant.h" #include "SceneFile.h" #include "SelectionManager.h" #include "SessionManager.h" #include "SpecFile.h" #include "SpecFileDataFile.h" #include "SpecFileDataFileTypeGroup.h" #include "Surface.h" #include "SurfaceProjectedItem.h" #include "SystemUtilities.h" #include "VolumeFile.h" #include "VolumeSurfaceOutlineSetModel.h" using namespace caret; /** * Constructor. */ Brain::Brain() { m_annotationManager = new AnnotationManager(this); m_chartingDataManager = new ChartingDataManager(this); m_fiberOrientationSamplesLoader = new FiberOrientationSamplesLoader(); m_paletteFile = new PaletteFile(); m_paletteFile->setFileName(convertFilePathNameToAbsolutePathName(m_paletteFile->getFileName())); m_paletteFile->clearModified(); m_specFile = new SpecFile(); m_specFile->setFileName(""); m_specFile->clearModified(); m_sceneAnnotationFile = new AnnotationFile(AnnotationFile::ANNOTATION_FILE_SAVE_TO_SCENE); m_sceneAnnotationFile->setFileName("Scene Annotations"); m_sceneAnnotationFile->clearModified(); m_modelChart = NULL; m_surfaceMontageModel = NULL; m_volumeSliceModel = NULL; m_wholeBrainModel = NULL; m_displayPropertiesAnnotation = new DisplayPropertiesAnnotation(this); m_displayProperties.push_back(m_displayPropertiesAnnotation); m_displayPropertiesBorders = new DisplayPropertiesBorders(); m_displayProperties.push_back(m_displayPropertiesBorders); m_displayPropertiesFiberOrientation = new DisplayPropertiesFiberOrientation(); m_displayProperties.push_back(m_displayPropertiesFiberOrientation); m_displayPropertiesFoci = new DisplayPropertiesFoci(); m_displayProperties.push_back(m_displayPropertiesFoci); m_displayPropertiesImages = new DisplayPropertiesImages(this); m_displayProperties.push_back(m_displayPropertiesImages); m_displayPropertiesLabels = new DisplayPropertiesLabels(); m_displayProperties.push_back(m_displayPropertiesLabels); m_displayPropertiesSurface = new DisplayPropertiesSurface(); m_displayProperties.push_back(m_displayPropertiesSurface); m_displayPropertiesVolume = new DisplayPropertiesVolume(); m_displayProperties.push_back(m_displayPropertiesVolume); EventManager::get()->addEventListener(this, EventTypeEnum::EVENT_DATA_FILE_ADD); EventManager::get()->addEventListener(this, EventTypeEnum::EVENT_DATA_FILE_DELETE); EventManager::get()->addEventListener(this, EventTypeEnum::EVENT_DATA_FILE_READ); EventManager::get()->addEventListener(this, EventTypeEnum::EVENT_DATA_FILE_RELOAD); EventManager::get()->addEventListener(this, EventTypeEnum::EVENT_CARET_MAPPABLE_DATA_FILES_GET); EventManager::get()->addEventListener(this, EventTypeEnum::EVENT_GET_DISPLAYED_DATA_FILES); EventManager::get()->addEventListener(this, EventTypeEnum::EVENT_SPEC_FILE_READ_DATA_FILES); EventManager::get()->addEventListener(this, EventTypeEnum::EVENT_PALETTE_GET_BY_NAME); m_isSpecFileBeingRead = false; m_gapsAndMargins = new GapsAndMargins(); m_sceneAssistant = new SceneClassAssistant(); m_sceneAssistant->add("m_sceneAnnotationFile", "SceneAnnotationFile", m_sceneAnnotationFile); m_sceneAssistant->add("displayPropertiesAnnotation", "DisplayPropertiesAnnotation", m_displayPropertiesAnnotation); m_sceneAssistant->add("displayPropertiesBorders", "DisplayPropertiesBorders", m_displayPropertiesBorders); m_sceneAssistant->add("displayPropertiesFiberOrientation", "DisplayPropertiesFiberOrientation", m_displayPropertiesFiberOrientation); m_sceneAssistant->add("displayPropertiesFoci", "DisplayPropertiesFoci", m_displayPropertiesFoci); m_sceneAssistant->add("m_displayPropertiesImages", "DisplayPropertiesImages", m_displayPropertiesImages); m_sceneAssistant->add("m_displayPropertiesLabels", "DisplayPropertiesLabels", m_displayPropertiesLabels); m_sceneAssistant->add("m_displayPropertiesSurface", "DisplayPropertiesSurface", m_displayPropertiesSurface); m_sceneAssistant->add("displayPropertiesVolume", "DisplayPropertiesVolume", m_displayPropertiesVolume); m_sceneAssistant->add("m_gapsAndMargins", "GapsAndMargins", m_gapsAndMargins); m_selectionManager = new SelectionManager(); m_identificationManager = new IdentificationManager(); m_brainordinateHighlightRegionOfInterest = new BrainordinateRegionOfInterest(); updateChartModel(); } /** * Destructor. */ Brain::~Brain() { EventManager::get()->removeAllEventsFromListener(this); delete m_sceneAssistant; for (std::vector::iterator iter = m_displayProperties.begin(); iter != m_displayProperties.end(); iter++) { delete *iter; } m_displayProperties.clear(); resetBrain(); delete m_sceneAnnotationFile; delete m_specFile; delete m_annotationManager; delete m_chartingDataManager; delete m_fiberOrientationSamplesLoader; delete m_paletteFile; if (m_modelChart != NULL) { delete m_modelChart; } if (m_surfaceMontageModel != NULL) { delete m_surfaceMontageModel; } if (m_volumeSliceModel != NULL) { delete m_volumeSliceModel; } if (m_wholeBrainModel != NULL) { delete m_wholeBrainModel; } delete m_selectionManager; delete m_identificationManager; delete m_brainordinateHighlightRegionOfInterest; delete m_gapsAndMargins; } /** * Get number of brain structures. * * @return * Number of brain structure. */ int Brain::getNumberOfBrainStructures() const { return static_cast(m_brainStructures.size()); } /** * Add a brain structure. * * @param brainStructure * Brain structure to add. */ void Brain::addBrainStructure(BrainStructure* brainStructure) { m_brainStructures.push_back(brainStructure); } /** * Get a brain structure at specified index. * * @param indx * Index of brain structure. * @return * Pointer to brain structure at index. */ BrainStructure* Brain::getBrainStructure(const int32_t indx) { CaretAssertVectorIndex(m_brainStructures, indx); return m_brainStructures[indx]; } /** * Get a brain structure at specified index. * * @param indx * Index of brain structure. * @return * Pointer to brain structure at index. */ const BrainStructure* Brain::getBrainStructure(const int32_t indx) const { CaretAssertVectorIndex(m_brainStructures, indx); return m_brainStructures[indx]; } /** * Find, and possibly create, a brain structure that * models the specified structure. * * @param structure * The desired structure. * @param createIfNotFound * If there is not a matching brain structure, create one. * @return * Pointer to brain structure or NULL if no match. */ BrainStructure* Brain::getBrainStructure(StructureEnum::Enum structure, bool createIfNotFound) { for (std::vector::iterator iter = m_brainStructures.begin(); iter != m_brainStructures.end(); iter++) { BrainStructure* bs = *iter; if (bs->getStructure() == structure) { return bs; } } if (createIfNotFound) { BrainStructure* bs = new BrainStructure(this, structure); m_brainStructures.push_back(bs); return bs; } return NULL; } /** * Increment and return the duplicate counter for the given data file type. * * @param dataFileType * Type of data file. * @return * Next duplicate counter for the file type. */ int32_t Brain::getDuplicateFileNameCounterForFileType(const DataFileTypeEnum::Enum dataFileType) { int32_t counterValue = 0; std::map::iterator duplicateCounterIter = m_duplicateFileNameCounter.find(dataFileType); if (duplicateCounterIter != m_duplicateFileNameCounter.end()) { counterValue = duplicateCounterIter->second; } /* * Extremely unlikely that that this will happen */ if (counterValue == std::numeric_limits::max()) { counterValue = 0; } ++counterValue; m_duplicateFileNameCounter[dataFileType] = counterValue; // m_duplicateFileNameCounter.insert(std::make_pair(dataFileType, // counterValue)); return counterValue; } /** * Reset the duplicate file name counter for all data file types. In some * instances, the scene file counter is not altered and needs to be * preserved. * * @param preserveSceneFileCounter * If true, do not reset the scene file duplicate counter. */ void Brain::resetDuplicateFileNameCounter(const bool preserveSceneFileCounter) { int32_t sceneDuplicateCounter = -1; if (preserveSceneFileCounter) { std::map::iterator sceneDuplicateIter = m_duplicateFileNameCounter.find(DataFileTypeEnum::SCENE); if (sceneDuplicateIter != m_duplicateFileNameCounter.end()) { sceneDuplicateCounter = sceneDuplicateIter->second; } } m_duplicateFileNameCounter.clear(); if (sceneDuplicateCounter > 0) { m_duplicateFileNameCounter.insert(std::make_pair(DataFileTypeEnum::SCENE, sceneDuplicateCounter)); } } /** * Reset the brain structure. * @param keepSceneFiles * Status of keeping scene files. * @param keepSpecFile * Status of keeping spec file. */ void Brain::resetBrain(const ResetBrainKeepSceneFiles keepSceneFiles, const ResetBrainKeepSpecFile keepSpecFile) { m_isSpecFileBeingRead = false; /* * Clear the counters used to prevent duplicate file names. */ resetDuplicateFileNameCounter(keepSceneFiles); int num = getNumberOfBrainStructures(); for (int32_t i = 0; i < num; i++) { delete m_brainStructures[i]; } for (std::vector::iterator vfi = m_volumeFiles.begin(); vfi != m_volumeFiles.end(); vfi++) { VolumeFile* vf = *vfi; delete vf; } m_volumeFiles.clear(); m_brainStructures.clear(); for (std::vector::iterator afi = m_annotationFiles.begin(); afi != m_annotationFiles.end(); afi++) { AnnotationFile* af = *afi; delete af; } m_annotationFiles.clear(); m_sceneAnnotationFile->clear(); //m_sceneAnnotationFile->setFileName("Scene Annotations"); m_sceneAnnotationFile->clearModified(); for (std::vector::iterator bfi = m_borderFiles.begin(); bfi != m_borderFiles.end(); bfi++) { BorderFile* bf = *bfi; delete bf; } m_borderFiles.clear(); for (std::vector::iterator ffi = m_fociFiles.begin(); ffi != m_fociFiles.end(); ffi++) { FociFile* ff = *ffi; delete ff; } m_fociFiles.clear(); for (std::vector::iterator ifi = m_imageFiles.begin(); ifi != m_imageFiles.end(); ifi++) { ImageFile* img = *ifi; delete img; } m_imageFiles.clear(); for (std::vector::iterator cdsfi = m_connectivityDataSeriesFiles.begin(); cdsfi != m_connectivityDataSeriesFiles.end(); cdsfi++) { CiftiBrainordinateDataSeriesFile* cbdsf = *cdsfi; delete cbdsf; } m_connectivityDataSeriesFiles.clear(); for (std::vector::iterator clfi = m_connectivityDenseLabelFiles.begin(); clfi != m_connectivityDenseLabelFiles.end(); clfi++) { CiftiBrainordinateLabelFile* clf = *clfi; delete clf; } m_connectivityDenseLabelFiles.clear(); for (std::vector::iterator clfi = m_connectivityMatrixDenseFiles.begin(); clfi != m_connectivityMatrixDenseFiles.end(); clfi++) { CiftiConnectivityMatrixDenseFile* clf = *clfi; delete clf; } m_connectivityMatrixDenseFiles.clear(); for (std::vector::iterator clfi = m_connectivityMatrixDenseParcelFiles.begin(); clfi != m_connectivityMatrixDenseParcelFiles.end(); clfi++) { CiftiConnectivityMatrixDenseParcelFile* clf = *clfi; delete clf; } m_connectivityMatrixDenseParcelFiles.clear(); for (std::vector::iterator clfi = m_connectivityDenseScalarFiles.begin(); clfi != m_connectivityDenseScalarFiles.end(); clfi++) { CiftiBrainordinateScalarFile* clf = *clfi; delete clf; } m_connectivityDenseScalarFiles.clear(); for (std::vector::iterator clfi = m_connectivityParcelSeriesFiles.begin(); clfi != m_connectivityParcelSeriesFiles.end(); clfi++) { CiftiParcelSeriesFile* pdsf = *clfi; delete pdsf; } m_connectivityParcelSeriesFiles.clear(); for (std::vector::iterator cpfi = m_connectivityParcelLabelFiles.begin(); cpfi != m_connectivityParcelLabelFiles.end(); cpfi++) { CiftiParcelLabelFile* plf = *cpfi; delete plf; } m_connectivityParcelLabelFiles.clear(); for (std::vector::iterator clfi = m_connectivityParcelScalarFiles.begin(); clfi != m_connectivityParcelScalarFiles.end(); clfi++) { CiftiParcelScalarFile* psf = *clfi; delete psf; } m_connectivityParcelScalarFiles.clear(); for (std::vector::iterator clfi = m_connectivityScalarDataSeriesFiles.begin(); clfi != m_connectivityScalarDataSeriesFiles.end(); clfi++) { CiftiScalarDataSeriesFile* psf = *clfi; delete psf; } m_connectivityScalarDataSeriesFiles.clear(); for (std::vector::iterator clfi = m_connectivityFiberOrientationFiles.begin(); clfi != m_connectivityFiberOrientationFiles.end(); clfi++) { CiftiFiberOrientationFile* clf = *clfi; delete clf; } m_connectivityFiberOrientationFiles.clear(); for (std::vector::iterator clfi = m_connectivityFiberTrajectoryFiles.begin(); clfi != m_connectivityFiberTrajectoryFiles.end(); clfi++) { CiftiFiberTrajectoryFile* clf = *clfi; delete clf; } m_connectivityFiberTrajectoryFiles.clear(); for (std::vector::iterator clfi = m_connectivityMatrixParcelFiles.begin(); clfi != m_connectivityMatrixParcelFiles.end(); clfi++) { CiftiConnectivityMatrixParcelFile* clf = *clfi; delete clf; } m_connectivityMatrixParcelFiles.clear(); for (std::vector::iterator clfi = m_connectivityMatrixParcelDenseFiles.begin(); clfi != m_connectivityMatrixParcelDenseFiles.end(); clfi++) { CiftiConnectivityMatrixParcelDenseFile* clf = *clfi; delete clf; } m_connectivityMatrixParcelDenseFiles.clear(); if (m_paletteFile != NULL) { delete m_paletteFile; } m_paletteFile = new PaletteFile(); m_paletteFile->setFileName(convertFilePathNameToAbsolutePathName(m_paletteFile->getFileName())); m_paletteFile->clearModified(); m_fiberOrientationSamplesLoader->reset(); switch (keepSceneFiles) { case RESET_BRAIN_KEEP_SCENE_FILES_NO: for (std::vector::iterator sfi = m_sceneFiles.begin(); sfi != m_sceneFiles.end(); sfi++) { SceneFile* sf = *sfi; delete sf; } m_sceneFiles.clear(); break; case RESET_BRAIN_KEEP_SCENE_FILES_YES: break; } switch (keepSpecFile) { case RESET_BRAIN_KEEP_SPEC_FILE_NO: m_specFile->clear(); m_specFile->setFileName(""); m_specFile->clearModified(); break; case RESET_BRAIN_KEEP_SPEC_FILE_YES: break; } for (std::vector::iterator iter = m_displayProperties.begin(); iter != m_displayProperties.end(); iter++) { (*iter)->reset(); } m_identificationManager->removeAllIdentifiedItems(); m_selectionManager->reset(); m_selectionManager->setLastSelectedItem(NULL); m_annotationManager->reset(); m_brainordinateHighlightRegionOfInterest->clear(); if (m_modelChart != NULL) { m_modelChart->reset(); } m_gapsAndMargins->reset(); updateAfterFilesAddedOrRemoved(); EventManager::get()->sendEvent(EventBrainReset(this).getPointer()); } /** * Reset the brain structure. */ void Brain::resetBrain() { resetBrain(RESET_BRAIN_KEEP_SCENE_FILES_NO, RESET_BRAIN_KEEP_SPEC_FILE_NO); } /** * Reset the brain structure but keep spec and scene files. */ void Brain::resetBrainKeepSceneFiles() { /* * Save all of the non-modified files so that loading * of them can be avoided later */ std::vector allFiles; getAllDataFiles(allFiles); m_nonModifiedFilesForRestoringScene.clear(); for (std::vector::iterator iter = allFiles.begin(); iter != allFiles.end(); iter++) { CaretDataFile* caretDataFile = *iter; if (caretDataFile->isModified()) { continue; } const DataFileTypeEnum::Enum dataFileType = caretDataFile->getDataFileType(); bool keepFileFlag = true; switch (dataFileType) { 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: break; case DataFileTypeEnum::METRIC: break; case DataFileTypeEnum::PALETTE: keepFileFlag = false; break; case DataFileTypeEnum::RGBA: break; case DataFileTypeEnum::SCENE: keepFileFlag = false; break; case DataFileTypeEnum::SPECIFICATION: keepFileFlag = false; break; case DataFileTypeEnum::SURFACE: break; case DataFileTypeEnum::UNKNOWN: keepFileFlag = false; break; case DataFileTypeEnum::VOLUME: break; } if (keepFileFlag) { if (removeWithoutDeleteDataFile(caretDataFile)) { m_nonModifiedFilesForRestoringScene.push_back(caretDataFile); } } } resetBrain(RESET_BRAIN_KEEP_SCENE_FILES_YES, RESET_BRAIN_KEEP_SPEC_FILE_NO); } /** * Copy all display properties from the source tab to the target tab. * @param sourceTabIndex * Index of source tab. * @param targetTabIndex * Index of target tab. */ void Brain::copyDisplayProperties(const int32_t sourceTabIndex, const int32_t targetTabIndex) { for (std::vector::iterator iter = m_displayProperties.begin(); iter != m_displayProperties.end(); iter++) { (*iter)->copyDisplayProperties(sourceTabIndex, targetTabIndex); } } /** * Read a surface file. * * @param fileMode * Mode for file adding, reading, or reloading. * @param caretDataFile * File that is added or reloaded (MUST NOT BE NULL). If NULL, * the mode must be READING. * @param filename * Name of the file. * @param structureIn * Structure of label file. * @throws DataFileException * If reading failed. * @return Pointer to file that was read. */ Surface* Brain::addReadOrReloadSurfaceFile(const FileModeAddReadReload fileMode, CaretDataFile* caretDataFile, const AString& filename, const StructureEnum::Enum structureIn, const bool markDataFileAsModified) { Surface* surface = NULL; StructureEnum::Enum structure = StructureEnum::INVALID; if (caretDataFile != NULL) { surface = dynamic_cast(caretDataFile); CaretAssert(surface); /* * Need structure in case file reloading fails */ structure = surface->getStructure(); } else { surface = new Surface(); } bool addFlag = false; bool readFlag = false; bool reloadFlag = false; switch (fileMode) { case FILE_MODE_ADD: addFlag = true; break; case FILE_MODE_READ: addFlag = true; readFlag = true; break; case FILE_MODE_RELOAD: readFlag = true; reloadFlag = true; break; } if (readFlag) { try { try { surface->readFile(filename); } catch (const std::bad_alloc&) { /* * This DataFileException will be caught * in the outer try/catch and it will * clean up to avoid memory leaks. */ throw DataFileException(filename, CaretDataFileHelper::createBadAllocExceptionMessage(filename)); } } catch (const DataFileException& dfe) { if (reloadFlag) { BrainStructure* bs = getBrainStructure(structure, true); if (bs != NULL) { bs->removeWithoutDeleteDataFile(surface); } } delete surface; throw dfe; } } if (addFlag) { } bool fileIsModified = false; if (structureIn != StructureEnum::INVALID) { if (surface->getStructure() != structureIn) { surface->setStructure(structureIn); fileIsModified = markDataFileAsModified; } } structure = surface->getStructure(); if (structure == StructureEnum::INVALID) { if (caretDataFile == NULL) { delete surface; } DataFileException e(filename, "Structure is not valid."); e.setErrorInvalidStructure(true); CaretLogThrowing(e); throw e; } const bool brainStructureExists = (getBrainStructure(structure, false) != NULL); BrainStructure* bs = getBrainStructure(structure, true); if (bs != NULL) { /* * Initialize the overlays if the brain structure did NOT exist * AND a spec file is NOT being read */ bool initializeOverlaysFlag = false; if (brainStructureExists == false) { if (m_isSpecFileBeingRead == false) { initializeOverlaysFlag = true; } } if (addFlag) { std::vector allSurfaces; bs->getSurfaces(allSurfaces); updateDataFileNameIfDuplicate(allSurfaces, surface); } bs->addSurface(surface, addFlag, initializeOverlaysFlag); } else { if (caretDataFile == NULL) { delete surface; } AString message = "Failed to create a BrainStructure for surface with structure " + StructureEnum::toGuiName(structure) + "."; DataFileException e(filename, message); CaretLogThrowing(e); throw e; } surface->clearModified(); if (fileIsModified) { surface->setModified(); } return surface; } /** * Read a label file. * * @param fileMode * Mode for file adding, reading, or reloading. * @param caretDataFile * File that is added or reloaded (MUST NOT BE NULL). If NULL, * the mode must be READING. * @param filename * Name of the file. * @param structureIn * Structure of label file. * @throws DataFileException * If reading failed. * @return Pointer to file that was read. */ LabelFile* Brain::addReadOrReloadLabelFile(const FileModeAddReadReload fileMode, CaretDataFile* caretDataFile, const AString& filename, const StructureEnum::Enum structureIn, const bool markDataFileAsModified) { LabelFile* labelFile = NULL; StructureEnum::Enum structure = StructureEnum::INVALID; if (caretDataFile != NULL) { labelFile = dynamic_cast(caretDataFile); CaretAssert(labelFile); /* * Need structure in case file reloading fails */ structure = labelFile->getStructure(); } else { labelFile = new LabelFile(); } bool addFlag = false; bool readFlag = false; bool reloadFlag = false; switch (fileMode) { case FILE_MODE_ADD: addFlag = true; break; case FILE_MODE_READ: addFlag = true; readFlag = true; break; case FILE_MODE_RELOAD: readFlag = true; reloadFlag = true; break; } if (readFlag) { try { try { labelFile->readFile(filename); } catch (const std::bad_alloc&) { /* * This DataFileException will be caught * in the outer try/catch and it will * clean up to avoid memory leaks. */ throw DataFileException(filename, CaretDataFileHelper::createBadAllocExceptionMessage(filename)); } } catch (const DataFileException& dfe) { if (reloadFlag) { BrainStructure* bs = getBrainStructure(structure, true); if (bs != NULL) { bs->removeWithoutDeleteDataFile(labelFile); } } delete labelFile; throw dfe; } } if (addFlag) { } bool fileIsModified = false; if (structureIn != StructureEnum::INVALID) { if (labelFile->getStructure() != structureIn) { labelFile->setStructure(structureIn); fileIsModified = markDataFileAsModified; } } structure = labelFile->getStructure(); if (structure == StructureEnum::INVALID) { if (caretDataFile == NULL) { delete labelFile; } DataFileException e(filename, "Structure is not valid."); e.setErrorInvalidStructure(true); CaretLogThrowing(e); throw e; } BrainStructure* bs = getBrainStructure(structure, false); if (bs != NULL) { try { if (addFlag) { std::vector allLabelFiles; bs->getLabelFiles(allLabelFiles); updateDataFileNameIfDuplicate(allLabelFiles, labelFile); } bs->addLabelFile(labelFile, addFlag); } catch (const DataFileException& e) { if (caretDataFile == NULL) { delete labelFile; } throw e; } } else { if (caretDataFile == NULL) { delete labelFile; } AString message = "Must read a surface with structure " + StructureEnum::toGuiName(structure) + " before reading its label files."; DataFileException e(filename, message); CaretLogThrowing(e); throw e; } labelFile->clearModified(); if (fileIsModified) { labelFile->setModified(); } return labelFile; } /** * Read a metric file. * * @param fileMode * Mode for file adding, reading, or reloading. * @param caretDataFile * File that is added or reloaded (MUST NOT BE NULL). If NULL, * the mode must be READING. * @param filename * Name of the file. * @param structureIn * Structure of label file. * @throws DataFileException * If reading failed. * @return Pointer to file that was read. */ MetricFile* Brain::addReadOrReloadMetricFile(const FileModeAddReadReload fileMode, CaretDataFile* caretDataFile, const AString& filename, const StructureEnum::Enum structureIn, const bool markDataFileAsModified) { MetricFile* metricFile = NULL; StructureEnum::Enum structure = StructureEnum::INVALID; if (caretDataFile != NULL) { metricFile = dynamic_cast(caretDataFile); CaretAssert(metricFile); /* * Need structure in case file reloading fails */ structure = metricFile->getStructure(); } else { metricFile = new MetricFile(); } bool addFlag = false; bool readFlag = false; bool reloadFlag = false; switch (fileMode) { case FILE_MODE_ADD: addFlag = true; break; case FILE_MODE_READ: addFlag = true; readFlag = true; break; case FILE_MODE_RELOAD: readFlag = true; reloadFlag = true; break; } if (readFlag) { try { try { metricFile->readFile(filename); } catch (const std::bad_alloc&) { /* * This DataFileException will be caught * in the outer try/catch and it will * clean up to avoid memory leaks. */ throw DataFileException(filename, CaretDataFileHelper::createBadAllocExceptionMessage(filename)); } } catch (const DataFileException& dfe) { if (reloadFlag) { BrainStructure* bs = getBrainStructure(structure, true); if (bs != NULL) { bs->removeWithoutDeleteDataFile(metricFile); } } delete metricFile; throw dfe; } } if (addFlag) { } bool fileIsModified = false; if (structureIn != StructureEnum::INVALID) { if (metricFile->getStructure() != structureIn) { metricFile->setStructure(structureIn); fileIsModified = markDataFileAsModified; } } structure = metricFile->getStructure(); if (structure == StructureEnum::INVALID) { if (caretDataFile == NULL) { delete metricFile; } DataFileException e(filename, "Structure is not valid."); e.setErrorInvalidStructure(true); CaretLogThrowing(e); throw e; } BrainStructure* bs = getBrainStructure(structure, false); if (bs != NULL) { try { if (addFlag) { std::vector allMetricFiles; bs->getMetricFiles(allMetricFiles); updateDataFileNameIfDuplicate(allMetricFiles, metricFile); } bs->addMetricFile(metricFile, addFlag); } catch (const DataFileException& e) { if (caretDataFile == NULL) { delete metricFile; } throw e; } } else { if (caretDataFile == NULL) { delete metricFile; } AString message = "Must read a surface with structure " + StructureEnum::toGuiName(structure) + " before reading its metric files."; DataFileException e(filename, message); CaretLogThrowing(e); throw e; } metricFile->clearModified(); if (fileIsModified) { metricFile->setModified(); } return metricFile; } /** * Read an RGBA file. * * @param fileMode * Mode for file adding, reading, or reloading. * @param caretDataFile * File that is added or reloaded (MUST NOT BE NULL). If NULL, * the mode must be READING. * @param filename * Name of the file. * @param structureIn * Structure of label file. * @throws DataFileException * If reading failed. * @return Pointer to file that was read. */ RgbaFile* Brain::addReadOrReloadRgbaFile(const FileModeAddReadReload fileMode, CaretDataFile* caretDataFile, const AString& filename, const StructureEnum::Enum structureIn, const bool markDataFileAsModified) { RgbaFile* rgbaFile = NULL; StructureEnum::Enum structure = StructureEnum::INVALID; if (caretDataFile != NULL) { rgbaFile = dynamic_cast(caretDataFile); CaretAssert(rgbaFile); /* * Need structure in case file reloading fails */ structure = rgbaFile->getStructure(); } else { rgbaFile = new RgbaFile(); } bool addFlag = false; bool readFlag = false; bool reloadFlag = false; switch (fileMode) { case FILE_MODE_ADD: addFlag = true; break; case FILE_MODE_READ: addFlag = true; readFlag = true; break; case FILE_MODE_RELOAD: readFlag = true; reloadFlag = true; break; } if (readFlag) { try { try { rgbaFile->readFile(filename); } catch (const std::bad_alloc&) { /* * This DataFileException will be caught * in the outer try/catch and it will * clean up to avoid memory leaks. */ throw DataFileException(filename, CaretDataFileHelper::createBadAllocExceptionMessage(filename)); } } catch (const DataFileException& dfe) { if (reloadFlag) { BrainStructure* bs = getBrainStructure(structure, true); if (bs != NULL) { bs->removeWithoutDeleteDataFile(rgbaFile); } } delete rgbaFile; throw dfe; } } if (addFlag) { } bool fileIsModified = false; if (structureIn != StructureEnum::INVALID) { if (rgbaFile->getStructure() != structureIn) { rgbaFile->setStructure(structureIn); fileIsModified = markDataFileAsModified; } } structure = rgbaFile->getStructure(); if (structure == StructureEnum::INVALID) { if (caretDataFile == NULL) { delete rgbaFile; } DataFileException e(filename, "Structure is not valid."); e.setErrorInvalidStructure(true); CaretLogThrowing(e); throw e; } BrainStructure* bs = getBrainStructure(structure, false); if (bs != NULL) { try { if (addFlag) { std::vector allRgbaFiles; bs->getRgbaFiles(allRgbaFiles); updateDataFileNameIfDuplicate(allRgbaFiles, rgbaFile); } bs->addRgbaFile(rgbaFile, addFlag); } catch (const DataFileException& e) { if (caretDataFile == NULL) { delete rgbaFile; } throw e; } } else { if (caretDataFile == NULL) { delete rgbaFile; } AString message = "Must read a surface with structure " + StructureEnum::toGuiName(structure) + " before reading its RGBA files."; DataFileException e(filename, message); CaretLogThrowing(e); throw e; } rgbaFile->clearModified(); if (fileIsModified) { rgbaFile->setModified(); } return rgbaFile; } /** * Read a volume file. * * @param fileMode * Mode for file adding, reading, or reloading. * @param caretDataFile * File that is added or reloaded (MUST NOT BE NULL). If NULL, * the mode must be READING. * @param filename * Name of the file. * @return * File that was read. * @throws DataFileException * If reading failed. */ VolumeFile* Brain::addReadOrReloadVolumeFile(const FileModeAddReadReload fileMode, CaretDataFile* caretDataFile, const AString& filename) { VolumeFile* vf = NULL; if (caretDataFile != NULL) { vf = dynamic_cast(caretDataFile); CaretAssert(vf); } else { vf = new VolumeFile(); } bool addFlag = false; bool readFlag = false; switch (fileMode) { case FILE_MODE_ADD: addFlag = true; break; case FILE_MODE_READ: addFlag = true; readFlag = true; break; case FILE_MODE_RELOAD: readFlag = true; break; } if (readFlag) { try { try { vf->readFile(filename); } catch (const std::bad_alloc&) { /* * This DataFileException will be caught * in the outer try/catch and it will * clean up to avoid memory leaks. */ throw DataFileException(filename, CaretDataFileHelper::createBadAllocExceptionMessage(filename)); } } catch (const DataFileException& e) { if (caretDataFile != NULL) { removeAndDeleteDataFile(caretDataFile); } else { delete vf; } throw e; } } vf->clearModified(); ElapsedTimer timer; timer.start(); vf->updateScalarColoringForAllMaps(m_paletteFile); CaretLogInfo("Time to color volume data is " + AString::number(timer.getElapsedTimeSeconds(), 'f', 3) + " seconds."); if (addFlag) { updateDataFileNameIfDuplicate(m_volumeFiles, vf); m_volumeFiles.push_back(vf); } return vf; } /** * @return Number of volume files. */ int32_t Brain::getNumberOfVolumeFiles() const { return m_volumeFiles.size(); } /** * Get the volume file at the given index. * @param volumeFileIndex * Index of the volume file. * @return * Volume file at the given index. */ VolumeFile* Brain::getVolumeFile(const int32_t volumeFileIndex) { CaretAssertVectorIndex(m_volumeFiles, volumeFileIndex); return m_volumeFiles[volumeFileIndex]; } /** * Get the volume file at the given index. * @param volumeFileIndex * Index of the volume file. * @return * Volume file at the given index. */ const VolumeFile* Brain::getVolumeFile(const int32_t volumeFileIndex) const { CaretAssertVectorIndex(m_volumeFiles, volumeFileIndex); return m_volumeFiles[volumeFileIndex]; } /** * Add, read, or reload an annotation file. * * @param fileMode * Mode for file adding, reading, or reloading. * @param caretDataFile * File that is added or reloaded (MUST NOT BE NULL). If NULL, * the mode must be READING. * @param filename * Name of the file. * @return * File that was read. * @throws DataFileException * If reading failed. */ AnnotationFile* Brain::addReadOrReloadAnnotationFile(const FileModeAddReadReload fileMode, CaretDataFile* caretDataFile, const AString& filename) { AnnotationFile* af = NULL; if (caretDataFile != NULL) { af = dynamic_cast(caretDataFile); CaretAssert(af); } else { af = new AnnotationFile(); } bool addFlag = false; bool readFlag = false; switch (fileMode) { case FILE_MODE_ADD: addFlag = true; break; case FILE_MODE_READ: addFlag = true; readFlag = true; break; case FILE_MODE_RELOAD: readFlag = true; break; } if (readFlag) { try { try { af->readFile(filename); } catch (const std::bad_alloc&) { /* * This DataFileException will be caught * in the outer try/catch and it will * clean up to avoid memory leaks. */ throw DataFileException(filename, CaretDataFileHelper::createBadAllocExceptionMessage(filename)); } } catch (DataFileException& dfe) { if (caretDataFile != NULL) { removeAndDeleteDataFile(caretDataFile); } else { delete af; } throw dfe; } } if (addFlag) { updateDataFileNameIfDuplicate(m_annotationFiles, af); m_annotationFiles.push_back(af); } return af; } /** * Read a border file. * * @param fileMode * Mode for file adding, reading, or reloading. * @param caretDataFile * File that is added or reloaded (MUST NOT BE NULL). If NULL, * the mode must be READING. * @param filename * Name of the file. * @return * File that was read. * @throws DataFileException * If reading failed. */ BorderFile* Brain::addReadOrReloadBorderFile(const FileModeAddReadReload fileMode, CaretDataFile* caretDataFile, const AString& filename) { BorderFile* bf = NULL; if (caretDataFile != NULL) { bf = dynamic_cast(caretDataFile); CaretAssert(bf); } else { bf = new BorderFile(); } bool addFlag = false; bool readFlag = false; switch (fileMode) { case FILE_MODE_ADD: addFlag = true; break; case FILE_MODE_READ: addFlag = true; readFlag = true; break; case FILE_MODE_RELOAD: readFlag = true; break; } if (readFlag) { try { try { bf->readFile(filename); } catch (const std::bad_alloc&) { /* * This DataFileException will be caught * in the outer try/catch and it will * clean up to avoid memory leaks. */ throw DataFileException(filename, CaretDataFileHelper::createBadAllocExceptionMessage(filename)); } /* * Create a map of structure to number of nodes */ std::map structureToNodeCountMap; for (std::vector::iterator bsIter = m_brainStructures.begin(); bsIter != m_brainStructures.end(); bsIter++) { const BrainStructure* bs = *bsIter; CaretAssert(bs); structureToNodeCountMap.insert(std::make_pair(bs->getStructure(), bs->getNumberOfNodes())); } bf->updateNumberOfNodesIfSingleStructure(structureToNodeCountMap); } catch (DataFileException& dfe) { if (caretDataFile != NULL) { removeAndDeleteDataFile(caretDataFile); } else { delete bf; } throw dfe; } } if (addFlag) { updateDataFileNameIfDuplicate(m_borderFiles, bf); m_borderFiles.push_back(bf); } return bf; } /** * Read a foci file. * * @param fileMode * Mode for file adding, reading, or reloading. * @param caretDataFile * File that is added or reloaded (MUST NOT BE NULL). If NULL, * the mode must be READING. * @param filename * Name of the file. * @throws DataFileException * If reading failed. */ FociFile* Brain::addReadOrReloadFociFile(const FileModeAddReadReload fileMode, CaretDataFile* caretDataFile, const AString& filename) { FociFile* ff = NULL; if (caretDataFile != NULL) { ff = dynamic_cast(caretDataFile); CaretAssert(ff); } else { ff = new FociFile(); } bool addFlag = false; bool readFlag = false; switch (fileMode) { case FILE_MODE_ADD: addFlag = true; break; case FILE_MODE_READ: addFlag = true; readFlag = true; break; case FILE_MODE_RELOAD: readFlag = true; break; } if (readFlag) { try { try { ff->readFile(filename); } catch (const std::bad_alloc&) { /* * This DataFileException will be caught * in the outer try/catch and it will * clean up to avoid memory leaks. */ throw DataFileException(filename, CaretDataFileHelper::createBadAllocExceptionMessage(filename)); } } catch (DataFileException& dfe) { if (caretDataFile != NULL) { removeAndDeleteDataFile(caretDataFile); } else { delete ff; } throw dfe; } } if (addFlag) { updateDataFileNameIfDuplicate(m_fociFiles, ff); m_fociFiles.push_back(ff); } return ff; } /** * Read a foci file. * * @param fileMode * Mode for file adding, reading, or reloading. * @param caretDataFile * File that is added or reloaded (MUST NOT BE NULL). If NULL, * the mode must be READING. * @param filename * Name of the file. * @throws DataFileException * If reading failed. */ ImageFile* Brain::addReadOrReloadImageFile(const FileModeAddReadReload fileMode, CaretDataFile* caretDataFile, const AString& filename) { ImageFile* imageFile = NULL; if (caretDataFile != NULL) { imageFile = dynamic_cast(caretDataFile); CaretAssert(imageFile); } else { imageFile = new ImageFile(); } bool addFlag = false; bool readFlag = false; switch (fileMode) { case FILE_MODE_ADD: addFlag = true; break; case FILE_MODE_READ: addFlag = true; readFlag = true; break; case FILE_MODE_RELOAD: readFlag = true; break; } if (readFlag) { try { try { imageFile->readFile(filename); } catch (const std::bad_alloc&) { /* * This DataFileException will be caught * in the outer try/catch and it will * clean up to avoid memory leaks. */ throw DataFileException(filename, CaretDataFileHelper::createBadAllocExceptionMessage(filename)); } } catch (DataFileException& dfe) { if (caretDataFile != NULL) { removeAndDeleteDataFile(caretDataFile); } else { delete imageFile; } throw dfe; } } if (addFlag) { updateDataFileNameIfDuplicate(m_imageFiles, imageFile); m_imageFiles.push_back(imageFile); } return imageFile; } /** * Validate a CIFTI Mappable Data File. * A file is valid if its surface mappings match the loaded surfaces. * * @param ciftiMapFile * File examined for validity. * @throws DataFileException * If the file is found to be incompatible with the loaded surfaces. */ void Brain::validateCiftiMappableDataFile(const CiftiMappableDataFile* ciftiMapFile) const { const int32_t numBrainStructures = getNumberOfBrainStructures(); for (int32_t i = 0; i < numBrainStructures; i++) { const StructureEnum::Enum structure = getBrainStructure(i)->getStructure(); const int numNodes = getBrainStructure(i)->getNumberOfNodes(); const int numConnNodes = ciftiMapFile->getMappingSurfaceNumberOfNodes(structure); if (numConnNodes > 0) { if (numNodes != numConnNodes) { AString msg = ("The CIFTI file contains " + AString::number(numConnNodes) + " nodes for structure " + StructureEnum::toGuiName(structure) + " but the corresponding surface brain structure contains " + AString::number(numNodes) + " nodes."); throw DataFileException(ciftiMapFile->getFileName(), msg); } } } } /** * Read a connectivity matrix dense file. * * @param fileMode * Mode for file adding, reading, or reloading. * @param caretDataFile * File that is added or reloaded (MUST NOT BE NULL). If NULL, * the mode must be READING. * @param filename * Name of the file. * @return * File that was read. * @throws DataFileException * If reading failed. */ CiftiConnectivityMatrixDenseFile* Brain::addReadOrReloadConnectivityDenseFile(const FileModeAddReadReload fileMode, CaretDataFile* caretDataFile, const AString& filename) { CiftiConnectivityMatrixDenseFile* cmdf = NULL; if (caretDataFile != NULL) { cmdf = dynamic_cast(caretDataFile); CaretAssert(cmdf); } else { cmdf = new CiftiConnectivityMatrixDenseFile(); } bool addFlag = false; bool readFlag = false; switch (fileMode) { case FILE_MODE_ADD: addFlag = true; break; case FILE_MODE_READ: addFlag = true; readFlag = true; break; case FILE_MODE_RELOAD: readFlag = true; break; } if (readFlag) { try { try { cmdf->readFile(filename); } catch (const std::bad_alloc&) { /* * This DataFileException will be caught * in the outer try/catch and it will * clean up to avoid memory leaks. */ throw DataFileException(filename, CaretDataFileHelper::createBadAllocExceptionMessage(filename)); } cmdf->clearModified(); validateCiftiMappableDataFile(cmdf); } catch (const DataFileException& dfe) { if (caretDataFile != NULL) { removeAndDeleteDataFile(caretDataFile); } else { delete cmdf; } throw dfe; } } if (addFlag) { updateDataFileNameIfDuplicate(m_connectivityMatrixDenseFiles, cmdf); m_connectivityMatrixDenseFiles.push_back(cmdf); } return cmdf; } /** * Read a connectivity dense label file. * * @param fileMode * Mode for file adding, reading, or reloading. * @param caretDataFile * File that is added or reloaded (MUST NOT BE NULL). If NULL, * the mode must be READING. * @param filename * Name of the file. * @return * File that was read. * @throws DataFileException * If reading failed. */ CiftiBrainordinateLabelFile* Brain::addReadOrReloadConnectivityDenseLabelFile(const FileModeAddReadReload fileMode, CaretDataFile* caretDataFile, const AString& filename) { CiftiBrainordinateLabelFile* file = NULL; if (caretDataFile != NULL) { file = dynamic_cast(caretDataFile); CaretAssert(file); } else { file = new CiftiBrainordinateLabelFile(); } bool addFlag = false; bool readFlag = false; switch (fileMode) { case FILE_MODE_ADD: addFlag = true; break; case FILE_MODE_READ: addFlag = true; readFlag = true; break; case FILE_MODE_RELOAD: readFlag = true; break; } if (readFlag) { try { try { file->readFile(filename); } catch (const std::bad_alloc&) { /* * This DataFileException will be caught * in the outer try/catch and it will * clean up to avoid memory leaks. */ throw DataFileException(filename, CaretDataFileHelper::createBadAllocExceptionMessage(filename)); } validateCiftiMappableDataFile(file); } catch (const DataFileException& dfe) { if (caretDataFile != NULL) { removeAndDeleteDataFile(caretDataFile); } else { delete file; } throw dfe; } } if (addFlag) { updateDataFileNameIfDuplicate(m_connectivityDenseLabelFiles, file); m_connectivityDenseLabelFiles.push_back(file); } return file; } /** * Read a connectivity dense parcel file. * * @param fileMode * Mode for file adding, reading, or reloading. * @param caretDataFile * File that is added or reloaded (MUST NOT BE NULL). If NULL, * the mode must be READING. * @param filename * Name of the file. * @throws DataFileException * If reading failed. */ CiftiConnectivityMatrixDenseParcelFile* Brain::addReadOrReloadConnectivityMatrixDenseParcelFile(const FileModeAddReadReload fileMode, CaretDataFile* caretDataFile, const AString& filename) { CiftiConnectivityMatrixDenseParcelFile* file = NULL; if (caretDataFile != NULL) { file = dynamic_cast(caretDataFile); CaretAssert(file); } else { file = new CiftiConnectivityMatrixDenseParcelFile(); } bool addFlag = false; bool readFlag = false; switch (fileMode) { case FILE_MODE_ADD: addFlag = true; break; case FILE_MODE_READ: addFlag = true; readFlag = true; break; case FILE_MODE_RELOAD: readFlag = true; break; } if (readFlag) { try { try { file->readFile(filename); } catch (const std::bad_alloc&) { /* * This DataFileException will be caught * in the outer try/catch and it will * clean up to avoid memory leaks. */ throw DataFileException(filename, CaretDataFileHelper::createBadAllocExceptionMessage(filename)); } validateCiftiMappableDataFile(file); } catch (const DataFileException& dfe) { if (caretDataFile != NULL) { removeAndDeleteDataFile(caretDataFile); } else { delete file; } throw dfe; } } if (addFlag) { updateDataFileNameIfDuplicate(m_connectivityMatrixDenseParcelFiles, file); m_connectivityMatrixDenseParcelFiles.push_back(file); } return file; } /** * Update the fiber orientation files assigned to matching * fiber trajectory files. This is typically called after * files are added or removed. */ void Brain::updateFiberTrajectoryMatchingFiberOrientationFiles() { for (std::vector::iterator iter = m_connectivityFiberTrajectoryFiles.begin(); iter != m_connectivityFiberTrajectoryFiles.end(); iter++) { CiftiFiberTrajectoryFile* trajFile = *iter; trajFile->updateMatchingFiberOrientationFileFromList(m_connectivityFiberOrientationFiles); } } /** * Read a connectivity dense scalar file. * * @param fileMode * Mode for file adding, reading, or reloading. * @param caretDataFile * File that is added or reloaded (MUST NOT BE NULL). If NULL, * the mode must be READING. * @param filename * Name of the file. * @throws DataFileException * If reading failed. */ CiftiBrainordinateScalarFile* Brain::addReadOrReloadConnectivityDenseScalarFile(const FileModeAddReadReload fileMode, CaretDataFile* caretDataFile, const AString& filename) { CiftiBrainordinateScalarFile* clf = NULL; if (caretDataFile != NULL) { clf = dynamic_cast(caretDataFile); CaretAssert(clf); } else { clf = new CiftiBrainordinateScalarFile(); } bool addFlag = false; bool readFlag = false; switch (fileMode) { case FILE_MODE_ADD: addFlag = true; break; case FILE_MODE_READ: addFlag = true; readFlag = true; break; case FILE_MODE_RELOAD: readFlag = true; break; } if (readFlag) { try { try { clf->readFile(filename); } catch (const std::bad_alloc&) { /* * This DataFileException will be caught * in the outer try/catch and it will * clean up to avoid memory leaks. */ throw DataFileException(filename, CaretDataFileHelper::createBadAllocExceptionMessage(filename)); } validateCiftiMappableDataFile(clf); } catch (const DataFileException& dfe) { if (caretDataFile != NULL) { removeAndDeleteDataFile(caretDataFile); } else { delete clf; } throw dfe; } } if (addFlag) { updateDataFileNameIfDuplicate(m_connectivityDenseScalarFiles, clf); m_connectivityDenseScalarFiles.push_back(clf); } return clf; } /** * Read a connectivity parcel data series file. * * @param fileMode * Mode for file adding, reading, or reloading. * @param caretDataFile * File that is added or reloaded (MUST NOT BE NULL). If NULL, * the mode must be READING. * @param filename * Name of the file. * @throws DataFileException * If reading failed. */ CiftiParcelSeriesFile* Brain::addReadOrReloadConnectivityParcelSeriesFile(const FileModeAddReadReload fileMode, CaretDataFile* caretDataFile, const AString& filename) { CiftiParcelSeriesFile* clf = NULL; if (caretDataFile != NULL) { clf = dynamic_cast(caretDataFile); CaretAssert(clf); } else { clf = new CiftiParcelSeriesFile(); } bool addFlag = false; bool readFlag = false; switch (fileMode) { case FILE_MODE_ADD: addFlag = true; break; case FILE_MODE_READ: addFlag = true; readFlag = true; break; case FILE_MODE_RELOAD: readFlag = true; break; } if (readFlag) { try { try { clf->readFile(filename); } catch (const std::bad_alloc&) { /* * This DataFileException will be caught * in the outer try/catch and it will * clean up to avoid memory leaks. */ throw DataFileException(filename, CaretDataFileHelper::createBadAllocExceptionMessage(filename)); } validateCiftiMappableDataFile(clf); } catch (const DataFileException& dfe) { if (caretDataFile != NULL) { removeAndDeleteDataFile(caretDataFile); } else { delete clf; } throw dfe; } } if (addFlag) { updateDataFileNameIfDuplicate(m_connectivityParcelSeriesFiles, clf); m_connectivityParcelSeriesFiles.push_back(clf); } return clf; } /** * Read a connectivity parcel label file. * * @param fileMode * Mode for file adding, reading, or reloading. * @param caretDataFile * File that is added or reloaded (MUST NOT BE NULL). If NULL, * the mode must be READING. * @param filename * Name of the file. * @throws DataFileException * If reading failed. */ CiftiParcelLabelFile* Brain::addReadOrReloadConnectivityParcelLabelFile(const FileModeAddReadReload fileMode, CaretDataFile* caretDataFile, const AString& filename) { CiftiParcelLabelFile* clf = NULL; if (caretDataFile != NULL) { clf = dynamic_cast(caretDataFile); CaretAssert(clf); } else { clf = new CiftiParcelLabelFile(); } bool addFlag = false; bool readFlag = false; switch (fileMode) { case FILE_MODE_ADD: addFlag = true; break; case FILE_MODE_READ: addFlag = true; readFlag = true; break; case FILE_MODE_RELOAD: readFlag = true; break; } if (readFlag) { try { try { clf->readFile(filename); } catch (const std::bad_alloc&) { /* * This DataFileException will be caught * in the outer try/catch and it will * clean up to avoid memory leaks. */ throw DataFileException(filename, CaretDataFileHelper::createBadAllocExceptionMessage(filename)); } validateCiftiMappableDataFile(clf); } catch (const DataFileException& dfe) { if (caretDataFile != NULL) { removeAndDeleteDataFile(caretDataFile); } else { delete clf; } throw dfe; } } if (addFlag) { updateDataFileNameIfDuplicate(m_connectivityParcelLabelFiles, clf); m_connectivityParcelLabelFiles.push_back(clf); } return clf; } /** * Read a connectivity parcel scalar file. * * @param fileMode * Mode for file adding, reading, or reloading. * @param caretDataFile * File that is added or reloaded (MUST NOT BE NULL). If NULL, * the mode must be READING. * @param filename * Name of the file. * @throws DataFileException * If reading failed. */ CiftiParcelScalarFile* Brain::addReadOrReloadConnectivityParcelScalarFile(const FileModeAddReadReload fileMode, CaretDataFile* caretDataFile, const AString& filename) { CiftiParcelScalarFile* clf = NULL; if (caretDataFile != NULL) { clf = dynamic_cast(caretDataFile); CaretAssert(clf); } else { clf = new CiftiParcelScalarFile(); } bool addFlag = false; bool readFlag = false; switch (fileMode) { case FILE_MODE_ADD: addFlag = true; break; case FILE_MODE_READ: addFlag = true; readFlag = true; break; case FILE_MODE_RELOAD: readFlag = true; break; } if (readFlag) { try { try { clf->readFile(filename); } catch (const std::bad_alloc&) { /* * This DataFileException will be caught * in the outer try/catch and it will * clean up to avoid memory leaks. */ throw DataFileException(filename, CaretDataFileHelper::createBadAllocExceptionMessage(filename)); } validateCiftiMappableDataFile(clf); } catch (const DataFileException& dfe) { if (caretDataFile != NULL) { removeAndDeleteDataFile(caretDataFile); } else { delete clf; } throw dfe; } } if (addFlag) { updateDataFileNameIfDuplicate(m_connectivityParcelScalarFiles, clf); m_connectivityParcelScalarFiles.push_back(clf); } return clf; } /** * Read a connectivity scalar data series file. * * @param fileMode * Mode for file adding, reading, or reloading. * @param caretDataFile * File that is added or reloaded (MUST NOT BE NULL). If NULL, * the mode must be READING. * @param filename * Name of the file. * @throws DataFileException * If reading failed. */ CiftiScalarDataSeriesFile* Brain::addReadOrReloadConnectivityScalarDataSeriesFile(const FileModeAddReadReload fileMode, CaretDataFile* caretDataFile, const AString& filename) { CiftiScalarDataSeriesFile* clf = NULL; if (caretDataFile != NULL) { clf = dynamic_cast(caretDataFile); CaretAssert(clf); } else { clf = new CiftiScalarDataSeriesFile(); } bool addFlag = false; bool readFlag = false; switch (fileMode) { case FILE_MODE_ADD: addFlag = true; break; case FILE_MODE_READ: addFlag = true; readFlag = true; break; case FILE_MODE_RELOAD: readFlag = true; break; } if (readFlag) { try { try { clf->readFile(filename); } catch (const std::bad_alloc&) { /* * This DataFileException will be caught * in the outer try/catch and it will * clean up to avoid memory leaks. */ throw DataFileException(filename, CaretDataFileHelper::createBadAllocExceptionMessage(filename)); } validateCiftiMappableDataFile(clf); } catch (const DataFileException& dfe) { if (caretDataFile != NULL) { removeAndDeleteDataFile(caretDataFile); } else { delete clf; } throw dfe; } } if (addFlag) { updateDataFileNameIfDuplicate(m_connectivityScalarDataSeriesFiles, clf); m_connectivityScalarDataSeriesFiles.push_back(clf); } return clf; } /** * Find a cifti scalar file containing shape information. * * @param ciftiScalarShapeFileOut * Output CIFTI Scalar file that meets requirements (NULL if no matches. * @param shapeMapIndexOut * Output Index of map meeting requirements. */ void Brain::getCiftiShapeMap(CiftiBrainordinateScalarFile* &ciftiScalarShapeFileOut, int32_t& ciftiScalarhapeFileMapIndexOut, std::vector& ciftiScalarNotShapeFilesOut) const { ciftiScalarShapeFileOut = NULL; ciftiScalarhapeFileMapIndexOut = -1; ciftiScalarNotShapeFilesOut.clear(); CiftiBrainordinateScalarFile* depthMetricFile = NULL; int32_t depthMapIndex = -1; CiftiBrainordinateScalarFile* depthNamedMetricFile = NULL; CiftiBrainordinateScalarFile* curvatureMetricFile = NULL; int32_t curvatureMapIndex = -1; CiftiBrainordinateScalarFile* curvatureNamedMetricFile = NULL; CiftiBrainordinateScalarFile* shapeNamedMetricFile = NULL; CiftiBrainordinateScalarFile* sulcMetricFile = NULL; int32_t sulcMapIndex = -1; CiftiBrainordinateScalarFile* sulcNamedMetricFile = NULL; const int numFiles = static_cast(m_connectivityDenseScalarFiles.size()); for (int32_t i = 0; i < numFiles; i++) { CiftiBrainordinateScalarFile* cf = m_connectivityDenseScalarFiles[i]; const AString filename = cf->getFileNameNoPath().toLower(); const int32_t numMaps = cf->getNumberOfMaps(); for (int32_t j = 0; j < numMaps; j++) { const AString mapName = cf->getMapName(j).toLower(); if (mapName.contains("sulc")) { if (sulcMetricFile == NULL) { sulcMetricFile = cf; sulcMapIndex = j; } } else if (mapName.contains("depth")) { if (depthMetricFile == NULL) { depthMetricFile = cf; depthMapIndex = j; } } else if (mapName.contains("curv")) { if (curvatureMetricFile == NULL) { curvatureMetricFile = cf; curvatureMapIndex = j; } } } if (filename.contains("sulc")) { if (numMaps > 0) { sulcNamedMetricFile = cf; } } if (filename.contains("shape")) { if (numMaps > 0) { shapeNamedMetricFile = cf; } } if (filename.contains("curv")) { if (numMaps > 0) { curvatureNamedMetricFile = cf; } } if (filename.contains("depth")) { if (numMaps > 0) { depthNamedMetricFile = cf; } } } if (sulcMetricFile != NULL) { ciftiScalarShapeFileOut = sulcMetricFile; ciftiScalarhapeFileMapIndexOut = sulcMapIndex; } else if (depthMetricFile != NULL) { ciftiScalarShapeFileOut = depthMetricFile; ciftiScalarhapeFileMapIndexOut = depthMapIndex; } else if (curvatureMetricFile != NULL) { ciftiScalarShapeFileOut = curvatureMetricFile; ciftiScalarhapeFileMapIndexOut = curvatureMapIndex; } else if (sulcNamedMetricFile != NULL) { ciftiScalarShapeFileOut = sulcNamedMetricFile; ciftiScalarhapeFileMapIndexOut = 0; } else if (depthNamedMetricFile != NULL) { ciftiScalarShapeFileOut = depthNamedMetricFile; ciftiScalarhapeFileMapIndexOut = 0; } else if (curvatureNamedMetricFile != NULL) { ciftiScalarShapeFileOut = curvatureNamedMetricFile; ciftiScalarhapeFileMapIndexOut = 0; } else if (shapeNamedMetricFile != NULL) { ciftiScalarShapeFileOut = shapeNamedMetricFile; ciftiScalarhapeFileMapIndexOut = 0; } /* * Get all shape type files (NULLs okay) */ std::vector ciftiShapeFiles; ciftiShapeFiles.push_back(sulcMetricFile); ciftiShapeFiles.push_back(depthMetricFile); ciftiShapeFiles.push_back(curvatureMetricFile); ciftiShapeFiles.push_back(sulcNamedMetricFile); ciftiShapeFiles.push_back(depthNamedMetricFile); ciftiShapeFiles.push_back(curvatureNamedMetricFile); ciftiShapeFiles.push_back(shapeNamedMetricFile); /* * Find files that are NOT shape files */ for (int32_t i = 0; i < numFiles; i++) { CiftiBrainordinateScalarFile* cf = m_connectivityDenseScalarFiles[i]; if (std::find(ciftiShapeFiles.begin(), ciftiShapeFiles.end(), cf) == ciftiShapeFiles.end()) { ciftiScalarNotShapeFilesOut.push_back(cf); } } } /** * Read a connectivity fiber orientation file. * * @param fileMode * Mode for file adding, reading, or reloading. * @param caretDataFile * File that is added or reloaded (MUST NOT BE NULL). If NULL, * the mode must be READING. * @param filename * Name of the file. * @throws DataFileException * If reading failed. */ CiftiFiberOrientationFile* Brain::addReadOrReloadConnectivityFiberOrientationFile(const FileModeAddReadReload fileMode, CaretDataFile* caretDataFile, const AString& filename) { CiftiFiberOrientationFile* cfof = NULL; if (caretDataFile != NULL) { cfof = dynamic_cast(caretDataFile); CaretAssert(cfof); } else { cfof = new CiftiFiberOrientationFile(); } bool addFlag = false; bool readFlag = false; switch (fileMode) { case FILE_MODE_ADD: addFlag = true; break; case FILE_MODE_READ: addFlag = true; readFlag = true; break; case FILE_MODE_RELOAD: readFlag = true; break; } if (readFlag) { try { try { cfof->readFile(filename); } catch (const std::bad_alloc&) { /* * This DataFileException will be caught * in the outer try/catch and it will * clean up to avoid memory leaks. */ throw DataFileException(filename, CaretDataFileHelper::createBadAllocExceptionMessage(filename)); } } catch (const DataFileException& dfe) { if (caretDataFile != NULL) { removeAndDeleteDataFile(caretDataFile); } else { delete cfof; } throw dfe; } } /* * If first fiber orientation file, default the above and below limits * to +/- one-half voxel size. */ if (m_connectivityFiberOrientationFiles.empty()) { float voxelSizes[3]; cfof->getVolumeSpacing(voxelSizes); float maxVoxelSize = std::max(std::fabs(voxelSizes[0]), std::max(std::fabs(voxelSizes[1]), std::fabs(voxelSizes[2]))); if (maxVoxelSize <= 0.0) { maxVoxelSize = 1.0; } const float aboveLimit = maxVoxelSize; const float belowLimit = -maxVoxelSize; m_displayPropertiesFiberOrientation->setAboveAndBelowLimitsForAll(aboveLimit, belowLimit); } if (addFlag) { updateDataFileNameIfDuplicate(m_connectivityFiberOrientationFiles, cfof); m_connectivityFiberOrientationFiles.push_back(cfof); } return cfof; } /** * Read a connectivity fiber trajectory file. * * @param fileMode * Mode for file adding, reading, or reloading. * @param caretDataFile * File that is added or reloaded (MUST NOT BE NULL). If NULL, * the mode must be READING. * @param filename * Name of the file. * @throws DataFileException * If reading failed. */ CiftiFiberTrajectoryFile* Brain::addReadOrReloadConnectivityFiberTrajectoryFile(const FileModeAddReadReload fileMode, CaretDataFile* caretDataFile, const AString& filename) { CiftiFiberTrajectoryFile* cftf = NULL; if (caretDataFile != NULL) { cftf = dynamic_cast(caretDataFile); CaretAssert(cftf); } else { cftf = new CiftiFiberTrajectoryFile(); } bool addFlag = false; bool readFlag = false; switch (fileMode) { case FILE_MODE_ADD: addFlag = true; break; case FILE_MODE_READ: addFlag = true; readFlag = true; break; case FILE_MODE_RELOAD: readFlag = true; break; } if (readFlag) { try { try { cftf->readFile(filename); } catch (const std::bad_alloc&) { /* * This DataFileException will be caught * in the outer try/catch and it will * clean up to avoid memory leaks. */ throw DataFileException(filename, CaretDataFileHelper::createBadAllocExceptionMessage(filename)); } } catch (const DataFileException& dfe) { if (caretDataFile != NULL) { removeAndDeleteDataFile(caretDataFile); } else { delete cftf; } throw dfe; } } if (addFlag) { updateDataFileNameIfDuplicate(m_connectivityFiberTrajectoryFiles, cftf); m_connectivityFiberTrajectoryFiles.push_back(cftf); } return cftf; } /** * Read a connectivity parcel file. * * @param fileMode * Mode for file adding, reading, or reloading. * @param caretDataFile * File that is added or reloaded (MUST NOT BE NULL). If NULL, * the mode must be READING. * @param filename * Name of the file. * @throws DataFileException * If reading failed. */ CiftiConnectivityMatrixParcelFile* Brain::addReadOrReloadConnectivityMatrixParcelFile(const FileModeAddReadReload fileMode, CaretDataFile* caretDataFile, const AString& filename) { CiftiConnectivityMatrixParcelFile* file = NULL; if (caretDataFile != NULL) { file = dynamic_cast(caretDataFile); CaretAssert(file); } else { file = new CiftiConnectivityMatrixParcelFile(); } bool addFlag = false; bool readFlag = false; switch (fileMode) { case FILE_MODE_ADD: addFlag = true; break; case FILE_MODE_READ: addFlag = true; readFlag = true; break; case FILE_MODE_RELOAD: readFlag = true; break; } if (readFlag) { try { try { file->readFile(filename); } catch (const std::bad_alloc&) { /* * This DataFileException will be caught * in the outer try/catch and it will * clean up to avoid memory leaks. */ throw DataFileException(filename, CaretDataFileHelper::createBadAllocExceptionMessage(filename)); } validateCiftiMappableDataFile(file); } catch (const DataFileException& dfe) { if (caretDataFile != NULL) { removeAndDeleteDataFile(caretDataFile); } else { delete file; } throw dfe; } } if (addFlag) { updateDataFileNameIfDuplicate(m_connectivityMatrixParcelFiles, file); m_connectivityMatrixParcelFiles.push_back(file); } return file; } /** * Read a connectivity parcel dense file. * * @param fileMode * Mode for file adding, reading, or reloading. * @param caretDataFile * File that is added or reloaded (MUST NOT BE NULL). If NULL, * the mode must be READING. * @param filename * Name of the file. * @throws DataFileException * If reading failed. */ CiftiConnectivityMatrixParcelDenseFile* Brain::addReadOrReloadConnectivityMatrixParcelDenseFile(const FileModeAddReadReload fileMode, CaretDataFile* caretDataFile, const AString& filename) { CiftiConnectivityMatrixParcelDenseFile* file = NULL; if (caretDataFile != NULL) { file = dynamic_cast(caretDataFile); CaretAssert(file); } else { file = new CiftiConnectivityMatrixParcelDenseFile(); } bool addFlag = false; bool readFlag = false; switch (fileMode) { case FILE_MODE_ADD: addFlag = true; break; case FILE_MODE_READ: addFlag = true; readFlag = true; break; case FILE_MODE_RELOAD: readFlag = true; break; } if (readFlag) { try { try { file->readFile(filename); } catch (const std::bad_alloc&) { /* * This DataFileException will be caught * in the outer try/catch and it will * clean up to avoid memory leaks. */ throw DataFileException(filename, CaretDataFileHelper::createBadAllocExceptionMessage(filename)); } validateCiftiMappableDataFile(file); } catch (const DataFileException& dfe) { if (caretDataFile != NULL) { removeAndDeleteDataFile(caretDataFile); } else { delete file; } throw dfe; } } if (addFlag) { updateDataFileNameIfDuplicate(m_connectivityMatrixParcelDenseFiles, file); m_connectivityMatrixParcelDenseFiles.push_back(file); } return file; } /** * Initialize a data series file's dense connectivity */ void Brain::initializeDenseDataSeriesFile(CiftiBrainordinateDataSeriesFile* dataSeriesFile) { /* * Enable dynamic connectivity using preferences */ CaretPreferences* prefs = SessionManager::get()->getCaretPreferences(); CiftiConnectivityMatrixDenseDynamicFile* denseDynFile = dataSeriesFile->getConnectivityMatrixDenseDynamicFile(); denseDynFile->setEnabledAsLayer(prefs->isDynamicConnectivityDefaultedOn()); } /** * Read a connectivity data series file. * * @param fileMode * Mode for file adding, reading, or reloading. * @param caretDataFile * File that is added or reloaded (MUST NOT BE NULL). If NULL, * the mode must be READING. * @param filename * Name of the file. * @throws DataFileException * If reading failed. */ CiftiBrainordinateDataSeriesFile* Brain::addReadOrReloadConnectivityDataSeriesFile(const FileModeAddReadReload fileMode, CaretDataFile* caretDataFile, const AString& filename) { CiftiBrainordinateDataSeriesFile* file = NULL; if (caretDataFile != NULL) { file = dynamic_cast(caretDataFile); CaretAssert(file); } else { file = new CiftiBrainordinateDataSeriesFile(); } bool addFlag = false; bool readFlag = false; switch (fileMode) { case FILE_MODE_ADD: addFlag = true; break; case FILE_MODE_READ: addFlag = true; readFlag = true; break; case FILE_MODE_RELOAD: readFlag = true; break; } if (readFlag) { try { try { file->readFile(filename); } catch (const std::bad_alloc&) { /* * This DataFileException will be caught * in the outer try/catch and it will * clean up to avoid memory leaks. */ throw DataFileException(filename, CaretDataFileHelper::createBadAllocExceptionMessage(filename)); } validateCiftiMappableDataFile(file); } catch (const DataFileException& dfe) { if (caretDataFile != NULL) { removeAndDeleteDataFile(caretDataFile); } else { delete file; } throw dfe; } } if (addFlag) { updateDataFileNameIfDuplicate(m_connectivityDataSeriesFiles, file); m_connectivityDataSeriesFiles.push_back(file); } initializeDenseDataSeriesFile(file); return file; } /** * Read a palette file. * * @param fileMode * Mode for file adding, reading, or reloading. * @param caretDataFile * File that is added or reloaded (MUST NOT BE NULL). If NULL, * the mode must be READING. * @param filename * Name of the file. * @throws DataFileException * If reading failed. */ PaletteFile* Brain::addReadOrReloadPaletteFile(const FileModeAddReadReload fileMode, CaretDataFile* /*caretDataFile*/, const AString& filename) { bool addFlag = false; bool readFlag = false; switch (fileMode) { case FILE_MODE_ADD: addFlag = true; break; case FILE_MODE_READ: addFlag = true; readFlag = true; break; case FILE_MODE_RELOAD: readFlag = true; break; } if (readFlag) { } if (addFlag) { } throw DataFileException(filename, "Reading not implemented for: palette"); return NULL; } /** * Read a scene file. * * @param fileMode * Mode for file adding, reading, or reloading. * @param caretDataFile * File that is added or reloaded (MUST NOT BE NULL). If NULL, * the mode must be READING. * @param filename * Name of the file. * @throws DataFileException * If reading failed. */ SceneFile* Brain::addReadOrReloadSceneFile(const FileModeAddReadReload fileMode, CaretDataFile* caretDataFile, const AString& filename) { SceneFile* sf = NULL; if (caretDataFile != NULL) { sf = dynamic_cast(caretDataFile); CaretAssert(sf); } else { sf = new SceneFile(); } bool addFlag = false; bool readFlag = false; switch (fileMode) { case FILE_MODE_ADD: addFlag = true; break; case FILE_MODE_READ: addFlag = true; readFlag = true; break; case FILE_MODE_RELOAD: readFlag = true; break; } if (readFlag) { try { try { sf->readFile(filename); } catch (const std::bad_alloc&) { /* * This DataFileException will be caught * in the outer try/catch and it will * clean up to avoid memory leaks. */ throw DataFileException(filename, CaretDataFileHelper::createBadAllocExceptionMessage(filename)); } } catch (DataFileException& dfe) { if (caretDataFile != NULL) { removeAndDeleteDataFile(caretDataFile); } else { delete sf; } throw dfe; } } if (addFlag) { updateDataFileNameIfDuplicate(m_sceneFiles, sf); m_sceneFiles.push_back(sf); } // CaretPreferences* prefs = SessionManager::get()->getCaretPreferences(); // prefs->addToPreviousSceneFiles(sf->getFileName()); return sf; } /** * @return Number of connectivity matrix dense files. */ int32_t Brain::getNumberOfConnectivityMatrixDenseFiles() const { return m_connectivityMatrixDenseFiles.size(); } /** * Get the connectivity dense file at the given index. * @param indx * Index of file. * @return Conectivity dense file at index. */ CiftiConnectivityMatrixDenseFile* Brain::getConnectivityMatrixDenseFile(int32_t indx) { CaretAssertVectorIndex(m_connectivityMatrixDenseFiles, indx); return m_connectivityMatrixDenseFiles[indx]; } /** * Get the connectivity dense file at the given index. * @param indx * Index of file. * @return Conectivity dense file at index. */ const CiftiConnectivityMatrixDenseFile* Brain::getConnectivityMatrixDenseFile(int32_t indx) const { CaretAssertVectorIndex(m_connectivityMatrixDenseFiles, indx); return m_connectivityMatrixDenseFiles[indx]; } /** * Get ALL connectivity matrix dense files. * @param connectivityFilesOut * Contains all connectivity dense files on exit. */ void Brain::getConnectivityMatrixDenseFiles(std::vector& connectivityDenseFilesOut) const { connectivityDenseFilesOut = m_connectivityMatrixDenseFiles; } /** * Get ALL connectivity matrix dense dynamic files. * @param connectivityDenseDynamicFilesOut * Contains all connectivity dense dynamic files on exit. */ void Brain::getConnectivityMatrixDenseDynamicFiles(std::vector& connectivityDenseDynamicFilesOut) const { connectivityDenseDynamicFilesOut.clear(); for (std::vector::const_iterator iter = m_connectivityDataSeriesFiles.begin(); iter != m_connectivityDataSeriesFiles.end(); iter++) { CiftiConnectivityMatrixDenseDynamicFile* denseDynFile = (*iter)->getConnectivityMatrixDenseDynamicFile(); CaretAssert(denseDynFile); if (denseDynFile->isDataValid()) { connectivityDenseDynamicFilesOut.push_back(denseDynFile); } } } /** * @return Number of connectivity dense label files. */ int32_t Brain::getNumberOfConnectivityDenseLabelFiles() const { return m_connectivityDenseLabelFiles.size(); } /** * Get the connectivity dense label file at the given index. * @param indx * Index of file. * @return Conectivity dense label file at index. */ CiftiBrainordinateLabelFile* Brain::getConnectivityDenseLabelFile(int32_t indx) { CaretAssertVectorIndex(m_connectivityDenseLabelFiles, indx); return m_connectivityDenseLabelFiles[indx]; } /** * Get the connectivity dense label file at the given index. * @param indx * Index of file. * @return Conectivity dense label file at index. */ const CiftiBrainordinateLabelFile* Brain::getConnectivityDenseLabelFile(int32_t indx) const { CaretAssertVectorIndex(m_connectivityDenseLabelFiles, indx); return m_connectivityDenseLabelFiles[indx]; } /** * Get ALL connectivity dense label files. * @param connectivityDenseLabelFilesOut * Contains all connectivity dense labelfiles on exit. */ void Brain::getConnectivityDenseLabelFiles(std::vector& connectivityDenseLabelFilesOut) const { connectivityDenseLabelFilesOut = m_connectivityDenseLabelFiles; } /** * Get all of the CIFTI Mappable Data Files * @param allCiftiMappableDataFilesOut * Contains all CIFTI Mappable Data files upon exit. */ void Brain::getAllCiftiMappableDataFiles(std::vector& allCiftiMappableDataFilesOut) const { allCiftiMappableDataFilesOut.clear(); std::vector allFiles; getAllDataFiles(allFiles); for (std::vector::iterator iter = allFiles.begin(); iter != allFiles.end(); iter++) { CiftiMappableDataFile* cmdf = dynamic_cast(*iter); if (cmdf != NULL) { allCiftiMappableDataFilesOut.push_back(cmdf); } } } /** * Get all of the Brainordinate Chartable Data Files. Only files that implement the * ChartableLineSeriesBrainordinateInterface AND return true for ChartableLineSeriesBrainordinateInterface::isLineSeriesChartDataTypeSupported() * are included in the returned files. * * @param chartableDataFilesOut * Contains all chartable data files upon exit. */ void Brain::getAllChartableBrainordinateDataFiles(std::vector& chartableDataFilesOut) const { chartableDataFilesOut.clear(); std::vector allFiles; getAllDataFiles(allFiles); for (std::vector::iterator iter = allFiles.begin(); iter != allFiles.end(); iter++) { ChartableLineSeriesBrainordinateInterface* chartFile = dynamic_cast(*iter); if (chartFile != NULL) { if (chartFile->isLineSeriesChartingSupported()) { chartableDataFilesOut.push_back(chartFile); } } } } /** * Get all of the Line Series Chartable Data Files. Only files that implement the * ChartableLineSeriesInterface AND return true for ChartableLineSeriesInterface::isLineSeriesChartDataTypeSupported() * are included in the returned files. * * @param chartableDataFilesOut * Contains all chartable data files upon exit. */ void Brain::getAllChartableLineSeriesDataFiles(std::vector& chartableDataFilesOut) const { chartableDataFilesOut.clear(); std::vector allFiles; getAllDataFiles(allFiles); for (std::vector::iterator iter = allFiles.begin(); iter != allFiles.end(); iter++) { ChartableLineSeriesInterface* chartFile = dynamic_cast(*iter); if (chartFile != NULL) { if (chartFile->isLineSeriesChartingSupported()) { chartableDataFilesOut.push_back(chartFile); } } } } /** * Get all of the Line Series Chartable Data Files. Only files that implement the * ChartableLineSeriesInterface AND return true for ChartableLineSeriesInterface::isLineSeriesChartDataTypeSupported() * and support a chart of the given data type are included in the returned files. * * @param chartDataType * Desired chart data type. * @param chartableDataFilesOut * Contains all chartable data files upon exit. */ void Brain::getAllChartableLineSeriesDataFilesForChartDataType(const ChartDataTypeEnum::Enum chartDataType, std::vector& chartableDataFilesOut) const { chartableDataFilesOut.clear(); std::vector chartFiles; getAllChartableLineSeriesDataFiles(chartFiles); for (std::vector::iterator iter = chartFiles.begin(); iter != chartFiles.end(); iter++) { ChartableLineSeriesInterface* chartFile = *iter; if (chartFile->isLineSeriesChartDataTypeSupported(chartDataType)) { chartableDataFilesOut.push_back(chartFile); } } } /** * Get all of the Brainordinate Chartable Data Files. Only files that implement the * ChartableLineSeriesBrainordinateInterface, return true for ChartableLineSeriesBrainordinateInterface::isChartingSupported(), * AND return true for ChartableLineSeriesBrainordinateInterface::isChartingEnabled() for any tab index * are included in the returned files. * * @param chartableDataFilesOut * Contains all chartable data files upon exit. */ void Brain::getAllChartableBrainordinateDataFilesWithChartingEnabled(std::vector& chartableDataFilesOut) const { chartableDataFilesOut.clear(); std::vector allFiles; getAllDataFiles(allFiles); EventBrowserTabGetAll allTabsEvent; EventManager::get()->sendEvent(allTabsEvent.getPointer()); const std::vector tabIndices = allTabsEvent.getBrowserTabIndices(); const int32_t numTabs = static_cast(tabIndices.size()); for (std::vector::iterator iter = allFiles.begin(); iter != allFiles.end(); iter++) { ChartableLineSeriesBrainordinateInterface* chartFile = dynamic_cast(*iter); if (chartFile != NULL) { if (chartFile->isLineSeriesChartingSupported()) { for (int32_t iTab = 0; iTab < numTabs; iTab++) { const int32_t tabIndex = tabIndices[iTab]; if (chartFile->isLineSeriesChartingEnabled(tabIndex)) { chartableDataFilesOut.push_back(chartFile); break; } } } } } } /** * Get all of the Chartable Matrix Data Files. Only files that implement the * ChartableMatrixInterface AND return true for ChartableMatrixInterface::isChartingSupported() * are included in the returned files. * * @param chartableDataFilesOut * Contains all chartable data files upon exit. */ void Brain::getAllChartableMatrixDataFiles(std::vector& chartableDataFilesOut) const { chartableDataFilesOut.clear(); std::vector allFiles; getAllDataFiles(allFiles); for (std::vector::iterator iter = allFiles.begin(); iter != allFiles.end(); iter++) { ChartableMatrixInterface* chartFile = dynamic_cast(*iter); if (chartFile != NULL) { if (chartFile->isMatrixChartingSupported()) { chartableDataFilesOut.push_back(chartFile); } } } } /** * Get all of the Chartable Matrix Data Files. Only files that implement the * ChartableMatrixInterface AND return true for ChartableMatrixInterface::isChartingSupported() * and support a chart of the given data type are included in the returned files. * * @param chartDataType * Desired chart data type. * @param chartableDataFilesOut * Contains all chartable data files upon exit. */ void Brain::getAllChartableMatrixDataFilesForChartDataType(const ChartDataTypeEnum::Enum chartDataType, std::vector& chartableDataFilesOut) const { chartableDataFilesOut.clear(); std::vector chartFiles; getAllChartableMatrixDataFiles(chartFiles); for (std::vector::iterator iter = chartFiles.begin(); iter != chartFiles.end(); iter++) { ChartableMatrixInterface* chartFile = *iter; if (chartFile->isMatrixChartDataTypeSupported(chartDataType)) { chartableDataFilesOut.push_back(chartFile); } } } /** * @return Number of cifti dense parcel files. */ int32_t Brain::getNumberOfConnectivityMatrixDenseParcelFiles() const { return m_connectivityMatrixDenseParcelFiles.size(); } /** * Get the cifti dense parcel file at the given index. * @param indx * Index of file. * @return cifti dense parcel file at index. */ CiftiConnectivityMatrixDenseParcelFile* Brain::getConnectivityMatrixDenseParcelFile(int32_t indx) { CaretAssertVectorIndex(m_connectivityMatrixDenseParcelFiles, indx); return m_connectivityMatrixDenseParcelFiles[indx]; } /** * Get the connectivity dense parcel file at the given index. * @param indx * Index of file. * @return cifti dense parcel file at index. */ const CiftiConnectivityMatrixDenseParcelFile* Brain::getConnectivityMatrixDenseParcelFile(int32_t indx) const { CaretAssertVectorIndex(m_connectivityMatrixDenseParcelFiles, indx); return m_connectivityMatrixDenseParcelFiles[indx]; } /** * Get ALL cifti dense parcel files. * @param connectivityFilesOut * Contains all cifti dense parcel files on exit. */ void Brain::getConnectivityMatrixDenseParcelFiles(std::vector& connectivityDenseParcelFilesOut) const { connectivityDenseParcelFilesOut = m_connectivityMatrixDenseParcelFiles; } /** * @return Number of connectivity dense scalar files. */ int32_t Brain::getNumberOfConnectivityDenseScalarFiles() const { return m_connectivityDenseScalarFiles.size(); } /** * Get the connectivity dense scalar file at the given index. * @param indx * Index of file. * @return Conectivity dense scalar file at index. */ CiftiBrainordinateScalarFile* Brain::getConnectivityDenseScalarFile(int32_t indx) { CaretAssertVectorIndex(m_connectivityDenseScalarFiles, indx); return m_connectivityDenseScalarFiles[indx]; } /** * Get the connectivity dense scalar file at the given index. * @param indx * Index of file. * @return Conectivity dense scalar file at index. */ const CiftiBrainordinateScalarFile* Brain::getConnectivityDenseScalarFile(int32_t indx) const { CaretAssertVectorIndex(m_connectivityDenseScalarFiles, indx); return m_connectivityDenseScalarFiles[indx]; } /** * Get ALL connectivity dense scalr files. * @param connectivityDenseScalarFilesOut * Contains all connectivity dense files on exit. */ void Brain::getConnectivityDenseScalarFiles(std::vector& connectivityDenseScalarFilesOut) const { connectivityDenseScalarFilesOut = m_connectivityDenseScalarFiles; } /** * @return Number of connectivity parcel label files. */ int32_t Brain::getNumberOfConnectivityParcelLabelFiles() const { return m_connectivityParcelLabelFiles.size(); } /** * Get the connectivity parcel label file at the given index. * @param indx * Index of file. * @return Connectivity parcel label file at index. */ CiftiParcelLabelFile* Brain::getConnectivityParcelLabelFile(int32_t indx) { CaretAssertVectorIndex(m_connectivityParcelLabelFiles, indx); return m_connectivityParcelLabelFiles[indx]; } /** * Get the connectivity parcel label file at the given index. * @param indx * Index of file. * @return Connectivity parcel label file at index. */ const CiftiParcelLabelFile* Brain::getConnectivityParcelLabelFile(int32_t indx) const { CaretAssertVectorIndex(m_connectivityParcelLabelFiles, indx); return m_connectivityParcelLabelFiles[indx]; } /** * Get ALL connectivity parcel label files. * @param connectivityParcelLabelFilesOut * Contains all connectivity parcel label files on exit. */ void Brain::getConnectivityParcelLabelFiles(std::vector& connectivityParcelLabelFilesOut) const { connectivityParcelLabelFilesOut = m_connectivityParcelLabelFiles; } /** * @return Number of connectivity parcel scalar files. */ int32_t Brain::getNumberOfConnectivityParcelScalarFiles() const { return m_connectivityParcelScalarFiles.size(); } /** * Get the connectivity parcel scalar file at the given index. * @param indx * Index of file. * @return Connectivity parcel scalar file at index. */ CiftiParcelScalarFile* Brain::getConnectivityParcelScalarFile(int32_t indx) { CaretAssertVectorIndex(m_connectivityParcelScalarFiles, indx); return m_connectivityParcelScalarFiles[indx]; } /** * Get the connectivity parcel scalar file at the given index. * @param indx * Index of file. * @return Connectivity parcel scalar file at index. */ const CiftiParcelScalarFile* Brain::getConnectivityParcelScalarFile(int32_t indx) const { CaretAssertVectorIndex(m_connectivityParcelScalarFiles, indx); return m_connectivityParcelScalarFiles[indx]; } /** * Get ALL connectivity parcel scalar files. * @param connectivityParcelScalarFilesOut * Contains all connectivity parcel files on exit. */ void Brain::getConnectivityParcelScalarFiles(std::vector& connectivityParcelScalarFilesOut) const { connectivityParcelScalarFilesOut = m_connectivityParcelScalarFiles; } /** * @return Number of connectivity parcel scalar files. */ int32_t Brain::getNumberOfConnectivityScalarDataSeriesFiles() const { return m_connectivityScalarDataSeriesFiles.size(); } /** * Get the connectivity parcel scalar file at the given index. * @param indx * Index of file. * @return Connectivity parcel scalar file at index. */ CiftiScalarDataSeriesFile* Brain::getConnectivityScalarDataSeriesFile(int32_t indx) { CaretAssertVectorIndex(m_connectivityScalarDataSeriesFiles, indx); return m_connectivityScalarDataSeriesFiles[indx]; } /** * Get the connectivity parcel scalar file at the given index. * @param indx * Index of file. * @return Connectivity parcel scalar file at index. */ const CiftiScalarDataSeriesFile* Brain::getConnectivityScalarDataSeriesFile(int32_t indx) const { CaretAssertVectorIndex(m_connectivityScalarDataSeriesFiles, indx); return m_connectivityScalarDataSeriesFiles[indx]; } /** * Get ALL connectivity parcel scalar files. * @param connectivityScalarDataSeriesFilesOut * Contains all connectivity parcel files on exit. */ void Brain::getConnectivityScalarDataSeriesFiles(std::vector& connectivityScalarDataSeriesFilesOut) const { connectivityScalarDataSeriesFilesOut = m_connectivityScalarDataSeriesFiles; } /** * @return Number of connectivity parcel data series files. */ int32_t Brain::getNumberOfConnectivityParcelSeriesFiles() const { return m_connectivityParcelSeriesFiles.size(); } /** * Get the connectivity parcel data series file at the given index. * @param indx * Index of file. * @return Connectivity parcel data series file at index. */ CiftiParcelSeriesFile* Brain::getConnectivityParcelSeriesFile(int32_t indx) { CaretAssertVectorIndex(m_connectivityParcelSeriesFiles, indx); return m_connectivityParcelSeriesFiles[indx]; } /** * Get the connectivity parcel data series file at the given index. * @param indx * Index of file. * @return Connectivity parcel data series file at index. */ const CiftiParcelSeriesFile* Brain::getConnectivityParcelSeriesFile(int32_t indx) const { CaretAssertVectorIndex(m_connectivityParcelSeriesFiles, indx); return m_connectivityParcelSeriesFiles[indx]; } /** * Get ALL connectivity parcel data series files. * @param connectivityParcelSeriesFilesOut * Contains all connectivity parcel files on exit. */ void Brain::getConnectivityParcelSeriesFiles(std::vector& connectivityParcelSeriesFilesOut) const { connectivityParcelSeriesFilesOut = m_connectivityParcelSeriesFiles; } /** * @return Number of connectivity fiber orientation files. */ int32_t Brain::getNumberOfConnectivityFiberOrientationFiles() const { return m_connectivityFiberOrientationFiles.size(); } /** * Get the connectivity fiber orientation file at the given index. * @param indx * Index of file. * @return Conectivity fiber orientation file at index. */ CiftiFiberOrientationFile* Brain::getConnectivityFiberOrientationFile(int32_t indx) { CaretAssertVectorIndex(m_connectivityFiberOrientationFiles, indx); return m_connectivityFiberOrientationFiles[indx]; } /** * Get the connectivity fiber orientation file at the given index. * @param indx * Index of file. * @return Conectivity fiber orientation file at index. */ const CiftiFiberOrientationFile* Brain::getConnectivityFiberOrientationFile(int32_t indx) const { CaretAssertVectorIndex(m_connectivityFiberOrientationFiles, indx); return m_connectivityFiberOrientationFiles[indx]; } /** * Get ALL connectivity fiber orientation files. * @param connectivityFiberOrientationFilesOut * Contains all connectivity fiber orientation files on exit. */ void Brain::getConnectivityFiberOrientationFiles(std::vector& connectivityFiberOrientationFilesOut) const { connectivityFiberOrientationFilesOut = m_connectivityFiberOrientationFiles; } /** * @return Number of connectivity fiber trajectory files. */ int32_t Brain::getNumberOfConnectivityFiberTrajectoryFiles() const { return m_connectivityFiberTrajectoryFiles.size(); } /** * Get the connectivity fiber trajectory file at the given index. * @param indx * Index of file. * @return Conectivity fiber trajectory file at index. */ CiftiFiberTrajectoryFile* Brain::getConnectivityFiberTrajectoryFile(int32_t indx) { CaretAssertVectorIndex(m_connectivityFiberTrajectoryFiles, indx); return m_connectivityFiberTrajectoryFiles[indx]; } /** * Get the connectivity fiber trajectory file at the given index. * @param indx * Index of file. * @return Conectivity fiber trajectory file at index. */ const CiftiFiberTrajectoryFile* Brain::getConnectivityFiberTrajectoryFile(int32_t indx) const { CaretAssertVectorIndex(m_connectivityFiberTrajectoryFiles, indx); return m_connectivityFiberTrajectoryFiles[indx]; } /** * Get ALL connectivity fiber trajectory files. * @param connectivityFiberTrajectoryFilesOut * Contains all connectivity fiber trajectory files on exit. */ void Brain::getConnectivityFiberTrajectoryFiles(std::vector& connectivityFiberTrajectoryFilesOut) const { connectivityFiberTrajectoryFilesOut = m_connectivityFiberTrajectoryFiles; } /** * @return Number of cifti parcel files. */ int32_t Brain::getNumberOfConnectivityMatrixParcelFiles() const { return m_connectivityMatrixParcelFiles.size(); } /** * Get the cifti parcel file at the given index. * @param indx * Index of file. * @return cifti parcel file at index. */ CiftiConnectivityMatrixParcelFile* Brain::getConnectivityMatrixParcelFile(int32_t indx) { CaretAssertVectorIndex(m_connectivityMatrixParcelFiles, indx); return m_connectivityMatrixParcelFiles[indx]; } /** * Get the connectivity parcel file at the given index. * @param indx * Index of file. * @return cifti parcel file at index. */ const CiftiConnectivityMatrixParcelFile* Brain::getConnectivityMatrixParcelFile(int32_t indx) const { CaretAssertVectorIndex(m_connectivityMatrixParcelFiles, indx); return m_connectivityMatrixParcelFiles[indx]; } /** * Get ALL cifti parcel files. * @param connectivityFilesOut * Contains all cifti parcel files on exit. */ void Brain::getConnectivityMatrixParcelFiles(std::vector& connectivityParcelFilesOut) const { connectivityParcelFilesOut = m_connectivityMatrixParcelFiles; } /** * @return Number of cifti parcel dense files. */ int32_t Brain::getNumberOfConnectivityMatrixParcelDenseFiles() const { return m_connectivityMatrixParcelDenseFiles.size(); } /** * Get the cifti parcel dense file at the given index. * @param indx * Index of file. * @return cifti parcel dense file at index. */ CiftiConnectivityMatrixParcelDenseFile* Brain::getConnectivityMatrixParcelDenseFile(int32_t indx) { CaretAssertVectorIndex(m_connectivityMatrixParcelDenseFiles, indx); return m_connectivityMatrixParcelDenseFiles[indx]; } /** * Get the connectivity parcel dense file at the given index. * @param indx * Index of file. * @return cifti parcel dense file at index. */ const CiftiConnectivityMatrixParcelDenseFile* Brain::getConnectivityMatrixParcelDenseFile(int32_t indx) const { CaretAssertVectorIndex(m_connectivityMatrixParcelDenseFiles, indx); return m_connectivityMatrixParcelDenseFiles[indx]; } /** * Get ALL cifti parcel dense files. * @param connectivityFilesOut * Contains all cifti parcel dense files on exit. */ void Brain::getConnectivityMatrixParcelDenseFiles(std::vector& connectivityParcelDenseFilesOut) const { connectivityParcelDenseFilesOut = m_connectivityMatrixParcelDenseFiles; } /** * @return Number of cifti data series files. */ int32_t Brain::getNumberOfConnectivityDataSeriesFiles() const { return m_connectivityDataSeriesFiles.size(); } /** * Get the cifti data series file at the given index. * @param indx * Index of file. * @return cifti data series file at index. */ CiftiBrainordinateDataSeriesFile* Brain::getConnectivityDataSeriesFile(int32_t indx) { CaretAssertVectorIndex(m_connectivityDataSeriesFiles, indx); return m_connectivityDataSeriesFiles[indx]; } /** * Get the connectivity data series file at the given index. * @param indx * Index of file. * @return cifti data series file at index. */ const CiftiBrainordinateDataSeriesFile* Brain::getConnectivityDataSeriesFile(int32_t indx) const { CaretAssertVectorIndex(m_connectivityDataSeriesFiles, indx); return m_connectivityDataSeriesFiles[indx]; } /** * Get ALL cifti data series files. * @param connectivityDataSeriesFilesOut * Contains all cifti data series files on exit. */ void Brain::getConnectivityDataSeriesFiles(std::vector& connectivityDataSeriesFilesOut) const { connectivityDataSeriesFilesOut = m_connectivityDataSeriesFiles; } /** * Get all of the cifti connectivity type data files. * * param allCiftiConnectivityMatrixFiles * Will contain the files upon exit. */ void Brain::getAllCiftiConnectivityMatrixFiles(std::vector& allCiftiConnectivityMatrixFiles) const { allCiftiConnectivityMatrixFiles.clear(); std::vector allFiles; getAllDataFiles(allFiles); for (std::vector::iterator iter = allFiles.begin(); iter != allFiles.end(); iter++) { CiftiMappableConnectivityMatrixDataFile* cmdf = dynamic_cast(*iter); if (cmdf != NULL) { allCiftiConnectivityMatrixFiles.push_back(cmdf); } } } /** * Add a data file to the brain. * * This will add the file to its corresponding data file type and add the * file into the spec file. * * @param caretDataFile * The caret data file. * @throw DataFileException * If there is an error. */ void Brain::addDataFile(CaretDataFile* caretDataFile) { CaretAssert(caretDataFile); caretDataFile->setFileName(convertFilePathNameToAbsolutePathName(caretDataFile->getFileName())); const StructureEnum::Enum structure = caretDataFile->getStructure(); BrainStructure* brainStructure = getBrainStructure(structure, false); const DataFileTypeEnum::Enum dataFileType = caretDataFile->getDataFileType(); switch (dataFileType) { case DataFileTypeEnum::ANNOTATION: { AnnotationFile* file = dynamic_cast(caretDataFile); CaretAssert(file); m_annotationFiles.push_back(file); } break; case DataFileTypeEnum::BORDER: { BorderFile* file = dynamic_cast(caretDataFile); CaretAssert(file); m_borderFiles.push_back(file); } break; case DataFileTypeEnum::CONNECTIVITY_DENSE: { CiftiConnectivityMatrixDenseFile* file = dynamic_cast(caretDataFile); CaretAssert(file); m_connectivityMatrixDenseFiles.push_back(file); } break; case DataFileTypeEnum::CONNECTIVITY_DENSE_DYNAMIC: CaretAssertMessage(0, "Dense Dynamic Files should never be added to Brain."); break; case DataFileTypeEnum::CONNECTIVITY_DENSE_LABEL: { CiftiBrainordinateLabelFile* file = dynamic_cast(caretDataFile); CaretAssert(file); m_connectivityDenseLabelFiles.push_back(file); } break; case DataFileTypeEnum::CONNECTIVITY_DENSE_PARCEL: { CiftiConnectivityMatrixDenseParcelFile* file = dynamic_cast(caretDataFile); CaretAssert(file); m_connectivityMatrixDenseParcelFiles.push_back(file); } break; case DataFileTypeEnum::CONNECTIVITY_DENSE_SCALAR: { CiftiBrainordinateScalarFile* file = dynamic_cast(caretDataFile); CaretAssert(file); m_connectivityDenseScalarFiles.push_back(file); } break; case DataFileTypeEnum::CONNECTIVITY_DENSE_TIME_SERIES: { CiftiBrainordinateDataSeriesFile* file = dynamic_cast(caretDataFile); CaretAssert(file); initializeDenseDataSeriesFile(file); m_connectivityDataSeriesFiles.push_back(file); } break; case DataFileTypeEnum::CONNECTIVITY_FIBER_ORIENTATIONS_TEMPORARY: { CiftiFiberOrientationFile* file = dynamic_cast(caretDataFile); CaretAssert(file); m_connectivityFiberOrientationFiles.push_back(file); } break; case DataFileTypeEnum::CONNECTIVITY_FIBER_TRAJECTORY_TEMPORARY: { CiftiFiberTrajectoryFile* file = dynamic_cast(caretDataFile); CaretAssert(file); m_connectivityFiberTrajectoryFiles.push_back(file); } break; case DataFileTypeEnum::CONNECTIVITY_PARCEL: { CiftiConnectivityMatrixParcelFile* file = dynamic_cast(caretDataFile); CaretAssert(file); m_connectivityMatrixParcelFiles.push_back(file); } break; case DataFileTypeEnum::CONNECTIVITY_PARCEL_DENSE: { CiftiConnectivityMatrixParcelDenseFile* file = dynamic_cast(caretDataFile); CaretAssert(file); m_connectivityMatrixParcelDenseFiles.push_back(file); } break; case DataFileTypeEnum::CONNECTIVITY_PARCEL_LABEL: { CiftiParcelLabelFile* file = dynamic_cast(caretDataFile); CaretAssert(file); m_connectivityParcelLabelFiles.push_back(file); } break; case DataFileTypeEnum::CONNECTIVITY_PARCEL_SCALAR: { CiftiParcelScalarFile* file = dynamic_cast(caretDataFile); CaretAssert(file); m_connectivityParcelScalarFiles.push_back(file); } break; case DataFileTypeEnum::CONNECTIVITY_PARCEL_SERIES: { CiftiParcelSeriesFile* file = dynamic_cast(caretDataFile); CaretAssert(file); m_connectivityParcelSeriesFiles.push_back(file); } break; case DataFileTypeEnum::CONNECTIVITY_SCALAR_DATA_SERIES: { CiftiScalarDataSeriesFile* file = dynamic_cast(caretDataFile); CaretAssert(file); m_connectivityScalarDataSeriesFiles.push_back(file); } break; case DataFileTypeEnum::FOCI: { FociFile* file = dynamic_cast(caretDataFile); CaretAssert(file); m_fociFiles.push_back(file); } break; case DataFileTypeEnum::IMAGE: { ImageFile* file = dynamic_cast(caretDataFile); CaretAssert(file); m_imageFiles.push_back(file); } break; case DataFileTypeEnum::LABEL: { LabelFile* file = dynamic_cast(caretDataFile); CaretAssert(file); if (structure == StructureEnum::INVALID) { throw DataFileException(file->getFileName(), "Structure in label file is INVALID."); } if (brainStructure == NULL) { throw DataFileException(file->getFileName(), "Must load surface(s) with matching structure prior to label files"); } brainStructure->addLabelFile(file, true); } break; case DataFileTypeEnum::METRIC: { MetricFile* file = dynamic_cast(caretDataFile); CaretAssert(file); if (structure == StructureEnum::INVALID) { throw DataFileException(file->getFileName(), "Structure in metric file is INVALID."); } if (brainStructure == NULL) { throw DataFileException(file->getFileName(), "Must load surface(s) with matching structure prior to metric files"); } brainStructure->addMetricFile(file, true); } break; case DataFileTypeEnum::PALETTE: { throw DataFileException(caretDataFile->getFileName(), "Adding palette files not supported at this time."); } break; case DataFileTypeEnum::RGBA: { RgbaFile* file = dynamic_cast(caretDataFile); CaretAssert(file); if (structure == StructureEnum::INVALID) { throw DataFileException(file->getFileName(), "Structure in rgba file is INVALID."); } if (brainStructure == NULL) { throw DataFileException(file->getFileName(), "Must load surface(s) with matching structure prior to label files"); } brainStructure->addRgbaFile(file, true); } break; case DataFileTypeEnum::SCENE: { SceneFile* file = dynamic_cast(caretDataFile); CaretAssert(file); m_sceneFiles.push_back(file); } break; case DataFileTypeEnum::SPECIFICATION: CaretLogSevere("PROGRAM ERROR: Reading spec file should never call Brain::addReadOrReloadDataFile()"); throw DataFileException(caretDataFile->getFileName(), "PROGRAM ERROR: Reading spec file should never call Brain::addReadOrReloadDataFile()"); break; case DataFileTypeEnum::SURFACE: { Surface* file = dynamic_cast(caretDataFile); if (structure == StructureEnum::INVALID) { throw DataFileException(file->getFileName(), "Structure in surface file is INVALID."); } if (file == NULL) { throw DataFileException(file->getFileName(), "Cannot add SurfaceFile but can add a Surface."); } if (brainStructure == NULL) { brainStructure = getBrainStructure(structure, true); } brainStructure->addSurface(file, true, true); } break; case DataFileTypeEnum::UNKNOWN: throw DataFileException(caretDataFile->getFileName(), "Unable to read files of type UNKNOWN. Filename extension may be invalid."); break; case DataFileTypeEnum::VOLUME: { VolumeFile* file = dynamic_cast(caretDataFile); CaretAssert(file); m_volumeFiles.push_back(file); } break; } m_specFile->addCaretDataFile(caretDataFile); } /** * Get all annotation files INCLUDING the scene's annotation file. * * @param allAnnotationFilesOut * Will contain files on exit. */ void Brain::getAllAnnotationFilesIncludingSceneAnnotationFile(std::vector& annotationFilesOut) const { annotationFilesOut = m_annotationFiles; annotationFilesOut.push_back(m_sceneAnnotationFile); } /** * Get all annotation files EXCLUDING the scene's annotation file * * @param allAnnotationFilesOut * Will contain files on exit. */ void Brain::getAllAnnotationFilesExcludingSceneAnnotationFile(std::vector& annotationFilesOut) const { annotationFilesOut = m_annotationFiles; } /** * @return The annotation file associated with the current scene. */ AnnotationFile* Brain::getSceneAnnotationFile() { CaretAssert(m_sceneAnnotationFile); return m_sceneAnnotationFile; } /** * @return The annotation file associated with the current scene. */ const AnnotationFile* Brain::getSceneAnnotationFile() const { CaretAssert(m_sceneAnnotationFile); return m_sceneAnnotationFile; } /** * @return Number of border files. */ int32_t Brain::getNumberOfBorderFiles() const { return m_borderFiles.size(); } /** * @return The border file. * @param indx Index of the border file. */ BorderFile* Brain::getBorderFile(const int32_t indx) { CaretAssertVectorIndex(m_borderFiles, indx); return m_borderFiles[indx]; } /** * @return The border file. * @param indx Index of the border file. */ const BorderFile* Brain::getBorderFile(const int32_t indx) const { CaretAssertVectorIndex(m_borderFiles, indx); return m_borderFiles[indx]; } /** * @return Number of foci files. */ int32_t Brain::getNumberOfFociFiles() const { return m_fociFiles.size(); } /** * @return The foci file. * @param indx Index of the foci file. */ FociFile* Brain::getFociFile(const int32_t indx) { CaretAssertVectorIndex(m_fociFiles, indx); return m_fociFiles[indx]; } /** * @return The foci file. * @param indx Index of the foci file. */ const FociFile* Brain::getFociFile(const int32_t indx) const { CaretAssertVectorIndex(m_fociFiles, indx); return m_fociFiles[indx]; } /** * @return All image files. */ const std::vector Brain::getAllImagesFiles() const { return m_imageFiles; } /** * @return Number of image files. */ int32_t Brain::getNumberOfImageFiles() const { return m_imageFiles.size(); } /** * @return The image file. * @param indx Index of the image file. */ ImageFile* Brain::getImageFile(const int32_t indx) { CaretAssertVectorIndex(m_imageFiles, indx); return m_imageFiles[indx]; } /** * @return The image file. * @param indx Index of the image file. */ const ImageFile* Brain::getImageFile(const int32_t indx) const { CaretAssertVectorIndex(m_imageFiles, indx); return m_imageFiles[indx]; } /** * @return Number of scene files. */ int32_t Brain::getNumberOfSceneFiles() const { return m_sceneFiles.size(); } /** * @return The scene file. * @param indx Index of the scene file. */ SceneFile* Brain::getSceneFile(const int32_t indx) { CaretAssertVectorIndex(m_sceneFiles, indx); return m_sceneFiles[indx]; } /** * @return The scene file. * @param indx Index of the scene file. */ const SceneFile* Brain::getSceneFile(const int32_t indx) const { CaretAssertVectorIndex(m_sceneFiles, indx); return m_sceneFiles[indx]; } /** * @return The Spec File. */ const SpecFile* Brain::getSpecFile() const { return m_specFile; } /** * @return The Spec File. */ SpecFile* Brain::getSpecFile() { return m_specFile; } /* * @return The palette file. */ PaletteFile* Brain::getPaletteFile() { return m_paletteFile; } /* * @return The palette file. */ const PaletteFile* Brain::getPaletteFile() const { return m_paletteFile; } /** * Find the surface with the given name. * @param surfaceFileName * Name of surface. * @param useAbsolutePath * If true the given surfaceFileName is an absolute path. * If false, the given surfaceFileName is just the file * name without any path. */ Surface* Brain::getSurfaceWithName(const AString& surfaceFileName, const bool useAbsolutePath) { for (std::vector::iterator iter = m_brainStructures.begin(); iter != m_brainStructures.end(); iter++) { BrainStructure* bs = *iter; Surface* surface = bs->getSurfaceWithName(surfaceFileName, useAbsolutePath); if (surface != NULL) { return surface; } } return NULL; } /** * @return The primary anatomical surfaces from all brain structures. */ std::vector Brain::getPrimaryAnatomicalSurfaces() const { std::vector surfaces; const int32_t numBrainStructures = getNumberOfBrainStructures(); for (int32_t i = 0; i < numBrainStructures; i++) { surfaces.push_back(m_brainStructures[i]->getPrimaryAnatomicalSurface()); } return surfaces; } /** * @return The primary anatomical surfaces from all brain structures. */ std::vector Brain::getPrimaryAnatomicalSurfaceFiles() const { std::vector surfaces = getPrimaryAnatomicalSurfaces(); std::vector surfaceFiles; surfaceFiles.insert(surfaceFiles.end(), surfaces.begin(), surfaces.end()); return surfaceFiles; } /** * Get the primary anatomical surface for the given structure. * * @param structure * Structure for which a primary anatomical surface is requested. * @return * The primary anatomical surface corresonding to the given structure. * NULL may be returned if a surface is not available. */ const Surface* Brain::getPrimaryAnatomicalSurfaceForStructure(const StructureEnum::Enum structure) const { const int32_t numBrainStructures = getNumberOfBrainStructures(); for (int32_t i = 0; i < numBrainStructures; i++) { if (m_brainStructures[i]->getStructure() == structure) { return m_brainStructures[i]->getPrimaryAnatomicalSurface(); } } return NULL; } /** * Get the primary anatomical surface nearest the given coordinate and * within the given tolerance. * * @param xyz * The coordinate. * @param tolerance * The tolerance (if negative tolerance is ignored). * @return * Nearest surface or NULL if nearest surface not within tolerance. */ Surface* Brain::getPrimaryAnatomicalSurfaceNearestCoordinate(const float xyz[3], const float tolerance) { Surface* nearestSurface = NULL; float nearestDistance = ((tolerance > 0.0) ? tolerance : std::numeric_limits::max()); const int32_t numBrainStructures = getNumberOfBrainStructures(); for (int32_t i = 0; i < numBrainStructures; i++) { Surface* surface = m_brainStructures[i]->getPrimaryAnatomicalSurface(); const int32_t nodeIndex = surface->closestNode(xyz, tolerance); if (nodeIndex >= 0) { const float dist = MathFunctions::distanceSquared3D(xyz, surface->getCoordinate(nodeIndex)); if (dist < nearestDistance) { nearestDistance = dist; nearestSurface = surface; } } } return nearestSurface; } /** * Update the chart model. */ void Brain::updateChartModel() { std::vector chartableBrainordinateFiles; getAllChartableBrainordinateDataFiles(chartableBrainordinateFiles); std::vector chartableMatrixFiles; getAllChartableMatrixDataFiles(chartableMatrixFiles); const int32_t numberOfChartableFiles = (chartableBrainordinateFiles.size() + chartableMatrixFiles.size()); if (numberOfChartableFiles > 0) { if (m_modelChart == NULL) { m_modelChart = new ModelChart(this); EventModelAdd eventAddModel(m_modelChart); EventManager::get()->sendEvent(eventAddModel.getPointer()); if (m_isSpecFileBeingRead == false) { m_modelChart->initializeOverlays(); } } } else { if (m_modelChart != NULL) { EventModelDelete eventDeleteModel(m_modelChart); EventManager::get()->sendEvent(eventDeleteModel.getPointer()); delete m_modelChart; m_modelChart = NULL; } } } /** * Update the volume slice model. */ void Brain::updateVolumeSliceModel() { bool isValid = false; std::vector allCaretMappableDataFiles; getAllMappableDataFiles(allCaretMappableDataFiles); for (std::vector::iterator iter = allCaretMappableDataFiles.begin(); iter != allCaretMappableDataFiles.end(); iter++) { CaretMappableDataFile* cmdf = *iter; if (cmdf->isVolumeMappable()) { if (cmdf->isEmpty() == false) { isValid = true; break; } } } if (isValid) { if (m_volumeSliceModel == NULL) { m_volumeSliceModel = new ModelVolume(this); EventModelAdd eventAddModel(m_volumeSliceModel); EventManager::get()->sendEvent(eventAddModel.getPointer()); if (m_isSpecFileBeingRead == false) { m_volumeSliceModel->initializeOverlays(); } } } else { if (m_volumeSliceModel != NULL) { EventModelDelete eventDeleteModel(m_volumeSliceModel); EventManager::get()->sendEvent(eventDeleteModel.getPointer()); delete m_volumeSliceModel; m_volumeSliceModel = NULL; } } } /** * Update the whole brain model. */ void Brain::updateWholeBrainModel() { bool isValid = false; if ((getNumberOfBrainStructures() > 0) || (getNumberOfVolumeFiles() > 0)) { isValid = true; } if (isValid) { if (m_wholeBrainModel == NULL) { m_wholeBrainModel = new ModelWholeBrain(this); EventModelAdd eventAddModel(m_wholeBrainModel); EventManager::get()->sendEvent(eventAddModel.getPointer()); if (m_isSpecFileBeingRead == false) { m_wholeBrainModel->initializeOverlays(); } } m_wholeBrainModel->updateModel(); } else { if (m_wholeBrainModel != NULL) { EventModelDelete eventDeleteModel(m_wholeBrainModel); EventManager::get()->sendEvent(eventDeleteModel.getPointer()); delete m_wholeBrainModel; m_wholeBrainModel = NULL; } } } /** * Update the surface montage model */ void Brain::updateSurfaceMontageModel() { bool isValid = false; if (getNumberOfBrainStructures() > 0) { isValid = true; } if (isValid) { if (m_surfaceMontageModel == NULL) { m_surfaceMontageModel = new ModelSurfaceMontage(this); EventModelAdd eventAddModel(m_surfaceMontageModel); EventManager::get()->sendEvent(eventAddModel.getPointer()); if (m_isSpecFileBeingRead == false) { m_surfaceMontageModel->initializeOverlays(); } } } else { if (m_surfaceMontageModel != NULL) { EventModelDelete eventDeleteModel(m_surfaceMontageModel); EventManager::get()->sendEvent(eventDeleteModel.getPointer()); delete m_surfaceMontageModel; m_surfaceMontageModel = NULL; } } } /** * Process a reload data file event. * @param reloadDataFileEvent * Event containing file for reloading and my be updated with error messages. */ void Brain::processReloadDataFileEvent(EventDataFileReload* reloadDataFileEvent) { /* * Verify that file is already in memory. */ std::vector allDataFiles; getAllDataFiles(allDataFiles); CaretDataFile* caretDataFile = reloadDataFileEvent->getCaretDataFile(); CaretAssert(caretDataFile); if (std::find(allDataFiles.begin(), allDataFiles.end(), caretDataFile) == allDataFiles.end()) { reloadDataFileEvent->setErrorMessage("ERROR: " + caretDataFile->getFileNameNoPath() + " was not found as a loaded file."); return; } CaretDataFile::setFileReadingUsernameAndPassword(reloadDataFileEvent->getUsername(), reloadDataFileEvent->getPassword()); try { addReadOrReloadDataFile(FILE_MODE_RELOAD, caretDataFile, caretDataFile->getDataFileType(), caretDataFile->getStructure(), caretDataFile->getFileName(), false); } catch (const DataFileException& dfe) { reloadDataFileEvent->setErrorMessage(dfe.whatString()); } updateAfterFilesAddedOrRemoved(); } #include "CaretHttpManager.h" /** * Process a read data file event. * @param readDataFileEvent * Event describing file for reading and may be updated with error messages. */ void Brain::processReadDataFileEvent(EventDataFileRead* readDataFileEvent) { const QString username = readDataFileEvent->getUsername(); const QString password = readDataFileEvent->getPassword(); CaretDataFile::setFileReadingUsernameAndPassword(username, password); const int32_t numberOfFilesToRead = readDataFileEvent->getNumberOfDataFilesToRead(); EventProgressUpdate progressEvent(0, numberOfFilesToRead, 0, "Starting to read data file(s)"); EventManager::get()->sendEvent(progressEvent.getPointer()); AString eventErrorMessage; for (int32_t i = 0; i < numberOfFilesToRead; i++) { const AString filename = readDataFileEvent->getDataFileName(i); const DataFileTypeEnum::Enum dataFileType = readDataFileEvent->getDataFileType(i); const StructureEnum::Enum structure = readDataFileEvent->getStructure(i); const bool setFileModifiedStatus = readDataFileEvent->isFileToBeMarkedModified(i); const AString shortName = FileInformation(filename).getFileName(); progressEvent.setProgress(i, ("Reading " + shortName)); EventManager::get()->sendEvent(progressEvent.getPointer()); if (progressEvent.isCancelled()) { eventErrorMessage.appendWithNewLine("File reading cancelled."); break; } try { if (DataFile::isFileOnNetwork(filename) && ( ! username.isEmpty()) && ( ! password.isEmpty())) { CaretHttpManager::setAuthentication(filename, username, password); } CaretDataFile* fileRead = readDataFile(dataFileType, structure, filename, setFileModifiedStatus); readDataFileEvent->setDataFileRead(i, fileRead); } catch (const DataFileException& e) { if (e.isErrorInvalidStructure()) { readDataFileEvent->setFileErrorInvalidStructure(i, e.isErrorInvalidStructure()); } else { eventErrorMessage.appendWithNewLine(e.whatString()); readDataFileEvent->setFileErrorMessage(i, e.whatString()); } } } readDataFileEvent->setErrorMessage(eventErrorMessage); CaretDataFile::setFileReadingUsernameAndPassword("", ""); } /** * Read, or reload a data file. * * @param fileMode * Mode for file adding, reading, or reloading. * @param caretDataFile * File that is added or reloaded (MUST NOT BE NULL). If NULL, * the mode must be READING. * @param dataFileType * Type of data file to read. * @param structure * Struture of file (used if not invalid) * @param dataFileNameIn * Name of data file to read. * @param markDataFileAsModified * If file has invalid structure and settings structure, mark file modified * @return * In some cases this will return a pointer to the file that was read so * beware that this value may be NULL. * @throws DataFileException * If there is an error reading the file. */ CaretDataFile* Brain::addReadOrReloadDataFile(const FileModeAddReadReload fileMode, CaretDataFile* caretDataFile, const DataFileTypeEnum::Enum dataFileType, const StructureEnum::Enum structure, const AString& dataFileNameIn, const bool markDataFileAsModified) { /* * Need absolute path */ AString dataFileName = convertFilePathNameToAbsolutePathName(dataFileNameIn); CaretDataFile* caretDataFileRead = NULL; switch (fileMode) { case FILE_MODE_ADD: CaretAssert(caretDataFile != NULL); break; case FILE_MODE_READ: CaretAssert(caretDataFile == NULL); break; case FILE_MODE_RELOAD: CaretAssert(caretDataFile != NULL); break; } try { ElapsedTimer et; et.start(); switch (dataFileType) { case DataFileTypeEnum::ANNOTATION: caretDataFileRead = addReadOrReloadAnnotationFile(fileMode, caretDataFile, dataFileName); break; case DataFileTypeEnum::BORDER: caretDataFileRead = addReadOrReloadBorderFile(fileMode, caretDataFile, dataFileName); break; case DataFileTypeEnum::CONNECTIVITY_DENSE: caretDataFileRead = addReadOrReloadConnectivityDenseFile(fileMode, caretDataFile, dataFileName); break; case DataFileTypeEnum::CONNECTIVITY_DENSE_DYNAMIC: CaretAssertMessage(0, "Dense Dynamic files are never read by the Brain."); break; case DataFileTypeEnum::CONNECTIVITY_DENSE_LABEL: caretDataFileRead = addReadOrReloadConnectivityDenseLabelFile(fileMode, caretDataFile, dataFileName); break; case DataFileTypeEnum::CONNECTIVITY_DENSE_PARCEL: caretDataFileRead = addReadOrReloadConnectivityMatrixDenseParcelFile(fileMode, caretDataFile, dataFileName); break; case DataFileTypeEnum::CONNECTIVITY_DENSE_SCALAR: caretDataFileRead = addReadOrReloadConnectivityDenseScalarFile(fileMode, caretDataFile, dataFileName); break; case DataFileTypeEnum::CONNECTIVITY_DENSE_TIME_SERIES: caretDataFileRead = addReadOrReloadConnectivityDataSeriesFile(fileMode, caretDataFile, dataFileName); break; case DataFileTypeEnum::CONNECTIVITY_FIBER_ORIENTATIONS_TEMPORARY: caretDataFileRead = addReadOrReloadConnectivityFiberOrientationFile(fileMode, caretDataFile, dataFileName); break; case DataFileTypeEnum::CONNECTIVITY_FIBER_TRAJECTORY_TEMPORARY: caretDataFileRead = addReadOrReloadConnectivityFiberTrajectoryFile(fileMode, caretDataFile, dataFileName); break; case DataFileTypeEnum::CONNECTIVITY_PARCEL: caretDataFileRead = addReadOrReloadConnectivityMatrixParcelFile(fileMode, caretDataFile, dataFileName); break; case DataFileTypeEnum::CONNECTIVITY_PARCEL_DENSE: caretDataFileRead = addReadOrReloadConnectivityMatrixParcelDenseFile(fileMode, caretDataFile, dataFileName); break; case DataFileTypeEnum::CONNECTIVITY_PARCEL_LABEL: caretDataFileRead = addReadOrReloadConnectivityParcelLabelFile(fileMode, caretDataFile, dataFileName); break; case DataFileTypeEnum::CONNECTIVITY_PARCEL_SCALAR: caretDataFileRead = addReadOrReloadConnectivityParcelScalarFile(fileMode, caretDataFile, dataFileName); break; case DataFileTypeEnum::CONNECTIVITY_PARCEL_SERIES: caretDataFileRead = addReadOrReloadConnectivityParcelSeriesFile(fileMode, caretDataFile, dataFileName); break; case DataFileTypeEnum::CONNECTIVITY_SCALAR_DATA_SERIES: caretDataFileRead = addReadOrReloadConnectivityScalarDataSeriesFile(fileMode, caretDataFile, dataFileName); break; case DataFileTypeEnum::FOCI: caretDataFileRead = addReadOrReloadFociFile(fileMode, caretDataFile, dataFileName); break; case DataFileTypeEnum::IMAGE: caretDataFileRead = addReadOrReloadImageFile(fileMode, caretDataFile, dataFileName); break; case DataFileTypeEnum::LABEL: caretDataFileRead = addReadOrReloadLabelFile(fileMode, caretDataFile, dataFileName, structure, markDataFileAsModified); break; case DataFileTypeEnum::METRIC: caretDataFileRead = addReadOrReloadMetricFile(fileMode, caretDataFile, dataFileName, structure, markDataFileAsModified); break; case DataFileTypeEnum::PALETTE: caretDataFileRead = addReadOrReloadPaletteFile(fileMode, caretDataFile, dataFileName); break; case DataFileTypeEnum::RGBA: caretDataFileRead = addReadOrReloadRgbaFile(fileMode, caretDataFile, dataFileName, structure, markDataFileAsModified); break; case DataFileTypeEnum::SCENE: caretDataFileRead = addReadOrReloadSceneFile(fileMode, caretDataFile, dataFileName); break; case DataFileTypeEnum::SPECIFICATION: CaretLogSevere("PROGRAM ERROR: Reading spec file should never call Brain::addReadOrReloadDataFile()"); throw DataFileException(dataFileName, "PROGRAM ERROR: Reading spec file should never call Brain::addReadOrReloadDataFile()"); break; case DataFileTypeEnum::SURFACE: caretDataFileRead = addReadOrReloadSurfaceFile(fileMode, caretDataFile, dataFileName, structure, markDataFileAsModified); break; case DataFileTypeEnum::UNKNOWN: throw DataFileException(dataFileName, "Unable to read files of type UNKNOWN. May have invalid filename extenson."); break; case DataFileTypeEnum::VOLUME: caretDataFileRead = addReadOrReloadVolumeFile(fileMode, caretDataFile, dataFileName); break; } if (caretDataFileRead != NULL) { /* * NOTE: Name may have changed if it is a duplicate file name * for the data type. */ dataFileName = caretDataFileRead->getFileName(); m_specFile->addCaretDataFile(caretDataFileRead); } m_specFile->addDataFile(dataFileType, structure, dataFileName, true, false, false); AString msg = ("Time to read " + dataFileName + " was " + AString::number(et.getElapsedTimeSeconds()) + " seconds."); CaretLogInfo(msg); } catch (DataFileException& dfe) { /* * If "caretDataFile" is not NULL, then we were trying to * RELOAD a file so remove it from the "loaded files" */ if (caretDataFile != NULL) { m_specFile->removeCaretDataFile(caretDataFile); } else { if (caretDataFileRead != NULL) { delete caretDataFileRead; caretDataFileRead = NULL; } } throw dfe; } updateAfterFilesAddedOrRemoved(); return caretDataFileRead; } /** * Read a data file. * * @param fileMode * Mode for file adding, reading, or reloading. * @param caretDataFile * File that is added or reloaded (MUST NOT BE NULL). If NULL, * the mode must be READING. * @param dataFileType * Type of data file to read. * @param structure * Struture of file (used if not invalid) * @param dataFileNameIn * Name of data file to read. * @param markDataFileAsModified * If file has invalid structure and settings structure, mark file modified * @throws DataFileException * If there is an error reading the file. * @return * Pointer to file that was read, if no errors. */ CaretDataFile* Brain::readDataFile(const DataFileTypeEnum::Enum dataFileType, const StructureEnum::Enum structure, const AString& dataFileNameIn, const bool markDataFileAsModified) { AString dataFileName = dataFileNameIn; /* * If possible, update path so that is absolute */ dataFileName = convertFilePathNameToAbsolutePathName(dataFileName); /* * Since file is being read, it must exist */ if (DataFile::isFileOnNetwork(dataFileName) == false) { FileInformation fileInfoFullPath(dataFileName); if (fileInfoFullPath.exists() == false) { throw DataFileException(dataFileName, "File does not exist!"); } } CaretDataFile* caretDataFileRead = addReadOrReloadDataFile(FILE_MODE_READ, NULL, dataFileType, structure, dataFileName, markDataFileAsModified); return caretDataFileRead; } /** * Processing performed after adding or removing a data file. */ void Brain::updateAfterFilesAddedOrRemoved() { updateChartModel(); updateVolumeSliceModel(); updateWholeBrainModel(); updateSurfaceMontageModel(); updateFiberTrajectoryMatchingFiberOrientationFiles(); } /** * Load the data files selected in a spec file. * @param readSpecFileDataFilesEvent * Event containing the spec file. */ void Brain::loadFilesSelectedInSpecFile(EventSpecFileReadDataFiles* readSpecFileDataFilesEvent) { ElapsedTimer timer; timer.start(); AString errorMessage; const SpecFile* sf = readSpecFileDataFilesEvent->getSpecFile(); CaretAssert(sf); resetBrain(); CaretPreferences* prefs = SessionManager::get()->getCaretPreferences(); prefs->setBackgroundAndForegroundColorsMode(BackgroundAndForegroundColorsModeEnum::USER_PREFERENCES); try { m_specFile->clear(); *m_specFile = *sf; } catch (const DataFileException& e) { CaretLogSevere("SPEC FILE TODO: " + e.whatString()); } m_isSpecFileBeingRead = true; CaretDataFile::setFileReadingUsernameAndPassword(readSpecFileDataFilesEvent->getUsername(), readSpecFileDataFilesEvent->getPassword()); FileInformation fileInfo(sf->getFileName()); setCurrentDirectory(fileInfo.getPathName()); const int32_t numberOfFilesToRead = sf->getNumberOfFilesSelectedForLoading(); int32_t fileReadCounter = 0; EventProgressUpdate progressUpdate(0, numberOfFilesToRead, fileReadCounter, "Starting to read selected files"); EventManager::get()->sendEvent(progressUpdate.getPointer()); /* * Note: Need to read palette first since some of the individual file * reading routines update palette coloring when file is read */ const int32_t numFileGroups = sf->getNumberOfDataFileTypeGroups(); for (int32_t ig = -1; ig < numFileGroups; ig++) { const SpecFileDataFileTypeGroup* group = ((ig == -1) ? sf->getDataFileTypeGroupByType(DataFileTypeEnum::PALETTE) : sf->getDataFileTypeGroupByIndex(ig)); const DataFileTypeEnum::Enum dataFileType = group->getDataFileType(); if (ig >= 0) { if (dataFileType == DataFileTypeEnum::PALETTE) { continue; } } const int32_t numFiles = group->getNumberOfFiles(); for (int32_t iFile = 0; iFile < numFiles; iFile++) { const SpecFileDataFile* dataFileInfo = group->getFileInformation(iFile); if (dataFileInfo->isLoadingSelected()) { const AString filename = dataFileInfo->getFileName(); const StructureEnum::Enum structure = dataFileInfo->getStructure(); /* * Send event indicating progress of file reading */ FileInformation fileInfo(dataFileInfo->getFileName()); progressUpdate.setProgress(fileReadCounter, ("Reading " + fileInfo.getFileName())); EventManager::get()->sendEvent(progressUpdate.getPointer()); /* * If user cancelled, reset brain and get out! */ if (progressUpdate.isCancelled()) { resetBrain(); return; } try { readDataFile(dataFileType, structure, filename, false); } catch (const DataFileException& e) { if (errorMessage.isEmpty() == false) { errorMessage += "\n"; } errorMessage += e.whatString(); } fileReadCounter++; } } } m_specFile->clearModified(); const AString specFileName = sf->getFileName(); if (DataFile::isFileOnNetwork(specFileName)) { CaretPreferences* prefs = SessionManager::get()->getCaretPreferences(); prefs->addToPreviousSpecFiles(specFileName); } else { FileInformation specFileInfo(specFileName); if (specFileInfo.exists() && specFileInfo.isAbsolute()) { CaretPreferences* prefs = SessionManager::get()->getCaretPreferences(); prefs->addToPreviousSpecFiles(specFileName); } } if (errorMessage.isEmpty() == false) { readSpecFileDataFilesEvent->setErrorMessage(errorMessage); } m_paletteFile->setFileName(convertFilePathNameToAbsolutePathName(m_paletteFile->getFileNameNoPath())); m_paletteFile->clearModified(); // CaretLogSevere("Adding an annotation file for testing to the Brain." // "NOTE: THIS WILL CAUSE A PRINTOUT OF UNDELETED OBJECTS since this file is " // "added inside of resetBrain() which does all file deletion."); // AnnotationFile* testingAnnFile = new AnnotationFile(); // testingAnnFile->setFileName("Testing." + DataFileTypeEnum::toFileExtension(DataFileTypeEnum::ANNOTATION)); // addDataFile(testingAnnFile); /* * Reset the primary anatomical surfaces since they can get set * incorrectly when loading files */ for (std::vector::iterator bsi = m_brainStructures.begin(); bsi != m_brainStructures.end(); bsi++) { BrainStructure* bs = *bsi; bs->setPrimaryAnatomicalSurface(NULL); } /* * Initialize the overlay for ALL models */ EventModelGetAll 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; mdc->initializeSelectedSurfaces(); mdc->initializeOverlays(); } /* * Initialize overlays for brain structures */ for (std::vector::iterator iter = m_brainStructures.begin(); iter != m_brainStructures.end(); iter++) { BrainStructure* bs = *iter; bs->initializeOverlays(); } CaretLogInfo("Time to read files from spec file (in Brain) \"" + sf->getFileNameNoPath() + "\" was " + AString::number(timer.getElapsedTimeSeconds()) + " seconds."); m_isSpecFileBeingRead = false; CaretDataFile::setFileReadingUsernameAndPassword("", ""); } /** * Load files from the given spec file. * @param specFileToLoad * Spec file from which selected files are read. * @param keepSceneFiles * Controls clearing of scene files * @param keepSpecFile * Controls clearing of spec file */ void Brain::loadSpecFileFromScene(const SceneAttributes* sceneAttributes, SpecFile* specFileToLoad, const ResetBrainKeepSceneFiles keepSceneFiles, const ResetBrainKeepSpecFile keepSpecFile) { CaretAssert(specFileToLoad); EventProgressUpdate progressEvent(-1, -1, -1, "Resetting brain"); EventManager::get()->sendEvent(progressEvent.getPointer()); resetBrainKeepSceneFiles(); /* * Try to set to current directory */ const AString previousSpecFileName = m_specFile->getFileName(); delete m_specFile; m_specFile = new SpecFile(*specFileToLoad); FileInformation newSpecFileInfo(m_specFile->getFileName()); if (newSpecFileInfo.isAbsolute()) { setCurrentDirectory(newSpecFileInfo.getPathName()); } else { if (previousSpecFileName.endsWith(m_specFile->getFileName()) == false) { FileInformation oldSpecFileInfo(previousSpecFileName); setCurrentDirectory(oldSpecFileInfo.getPathName()); } } /* * Check to see if existing spec file exists */ FileInformation specFileInfo(specFileToLoad->getFileName()); const bool specFileValid = specFileInfo.exists(); /* * Apply spec file pulled from scene */ m_isSpecFileBeingRead = true; /* * Set current directory to directory containing scene file * but only if there is NOT a valid spec file */ const AString sceneFileName = sceneAttributes->getSceneFileName(); const bool sceneFileOnNetwork = DataFile::isFileOnNetwork(sceneFileName); if (specFileValid == false) { FileInformation sceneFileInfo(sceneFileName); if (sceneFileInfo.exists()) { setCurrentDirectory(sceneFileInfo.getPathName()); } } progressEvent.setProgressMessage("Loading data files"); EventManager::get()->sendEvent(progressEvent.getPointer()); if (progressEvent.isCancelled()) { resetBrain(keepSceneFiles, keepSpecFile); return; } /* * To speed file loading, non-modified files that were in memory * prior to restoring the scene are saved. This map matches * an entry of a selected file to one of the non-modified in * memory data files. */ std::map specFilesEntryToNonModifiedFile; /* * Find non-modified files that match, by name, files that are to be * loaded from the spec file and associate them for later use. */ if ( ! m_nonModifiedFilesForRestoringScene.empty()) { const int32_t numFileGroups = specFileToLoad->getNumberOfDataFileTypeGroups(); for (int32_t ig = 0; ig < numFileGroups; ig++) { const SpecFileDataFileTypeGroup* group = specFileToLoad->getDataFileTypeGroupByIndex(ig); const int32_t numFiles = group->getNumberOfFiles(); for (int32_t iFile = 0; iFile < numFiles; iFile++) { const SpecFileDataFile* fileInfo = group->getFileInformation(iFile); if (fileInfo->isLoadingSelected()) { AString filename = fileInfo->getFileName(); for (std::vector::iterator iter = m_nonModifiedFilesForRestoringScene.begin(); iter != m_nonModifiedFilesForRestoringScene.end(); iter++) { CaretDataFile* caretDataFile = *iter; if (caretDataFile != NULL) { const AString nonModifiedFileName = caretDataFile->getFileName(); if (nonModifiedFileName == filename) { specFilesEntryToNonModifiedFile.insert(std::make_pair(fileInfo, caretDataFile)); *iter = NULL; CaretLogFine("Scene loading matched previous file: " + filename); } } } } } } } /* * Delete any of the files that were in memory prior to loading the scene * that are not part of the scene being loaded. */ for (std::vector::iterator iter = m_nonModifiedFilesForRestoringScene.begin(); iter != m_nonModifiedFilesForRestoringScene.end(); iter++) { CaretDataFile* caretDataFile = *iter; if (caretDataFile != NULL) { CaretLogFine("Scene loading removing previous file not needed: " + caretDataFile->getFileName()); delete caretDataFile; } } m_nonModifiedFilesForRestoringScene.clear(); /* * Load new files and add existing files that were previously loaded. */ const int32_t numFileGroups = specFileToLoad->getNumberOfDataFileTypeGroups(); for (int32_t ig = 0; ig < numFileGroups; ig++) { const SpecFileDataFileTypeGroup* group = specFileToLoad->getDataFileTypeGroupByIndex(ig); const DataFileTypeEnum::Enum dataFileType = group->getDataFileType(); const int32_t numFiles = group->getNumberOfFiles(); for (int32_t iFile = 0; iFile < numFiles; iFile++) { const SpecFileDataFile* fileInfo = group->getFileInformation(iFile); if (fileInfo->isLoadingSelected()) { try { AString filename = fileInfo->getFileName(); std::map::iterator specToFileIter = specFilesEntryToNonModifiedFile.find(fileInfo); if (specToFileIter != specFilesEntryToNonModifiedFile.end()) { const QString msg = ("Adding previous file " + FileInformation(filename).getFileName()); progressEvent.setProgressMessage(msg); EventManager::get()->sendEvent(progressEvent.getPointer()); if (progressEvent.isCancelled()) { resetBrain(keepSceneFiles, keepSpecFile); return; } CaretDataFile* caretDataFile = specToFileIter->second; addReadOrReloadDataFile(FILE_MODE_ADD, caretDataFile, caretDataFile->getDataFileType(), caretDataFile->getStructure(), filename, false); } else { const StructureEnum::Enum structure = fileInfo->getStructure(); const QString msg = ("Loading " + FileInformation(filename).getFileName()); progressEvent.setProgressMessage(msg); EventManager::get()->sendEvent(progressEvent.getPointer()); if (progressEvent.isCancelled()) { resetBrain(keepSceneFiles, keepSpecFile); return; } if (sceneFileOnNetwork) { if (DataFile::isFileOnNetwork(filename) == false) { const int32_t lastSlashIndex = sceneFileName.lastIndexOf("/"); if (lastSlashIndex >= 0) { const AString newName = (sceneFileName.left(lastSlashIndex) + "/" + filename); filename = newName; } } } readDataFile(dataFileType, structure, filename, false); } } catch (const DataFileException& e) { sceneAttributes->addToErrorMessage(e.whatString()); } } } } m_isSpecFileBeingRead = false; if (m_paletteFile != NULL) { delete m_paletteFile; } m_paletteFile = new PaletteFile(); m_paletteFile->setFileName(convertFilePathNameToAbsolutePathName(m_paletteFile->getFileNameNoPath())); m_paletteFile->clearModified(); progressEvent.setProgressMessage("Initializing Overlays"); EventManager::get()->sendEvent(progressEvent.getPointer()); if (progressEvent.isCancelled()) { resetBrain(keepSceneFiles, keepSpecFile); return; } /* * Initialize the overlay for ALL models */ EventModelGetAll 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; mdc->initializeOverlays(); } /* * Initialize overlays for brain structures */ for (std::vector::iterator iter = m_brainStructures.begin(); iter != m_brainStructures.end(); iter++) { BrainStructure* bs = *iter; bs->initializeOverlays(); } } /** * If the file is NOT an absolute path, the name of the file path is updated * to include the current directory. * * @param caretDataFile * File that may have its name updated. */ void Brain::convertDataFilePathNameToAbsolutePathName(CaretDataFile* caretDataFile) const { CaretAssert(caretDataFile); const AString newFileName = convertFilePathNameToAbsolutePathName(caretDataFile->getFileName()); caretDataFile->setFileName(newFileName); } /** * Exampine the file path name to determine if it is an * absolute or relative path. If it is a relative * path, convert it to an absolute path. * * If the file is on the network (starts with "http:"), that is considered * an absolute path and the file name is not changed. * * @param filename * Name of file. * @return * If input filename was absolute path, it is returned with * no changes. Otherwise, the name is returned after * updating it to an absolute path. */ AString Brain::convertFilePathNameToAbsolutePathName(const AString& filename) const { /* * If file is on network, is is considered an absolute path */ if (DataFile::isFileOnNetwork(filename)) { return filename; } FileInformation fileInfo(filename); if (fileInfo.isAbsolute()) { return filename; } if (m_currentDirectory.isEmpty()) { AString fullPathName = FileInformation(filename).getAbsoluteFilePath(); return fullPathName; } FileInformation pathFileInfo(m_currentDirectory, filename); AString fullPathName = pathFileInfo.getAbsoluteFilePath(); return fullPathName; } /** * Receive events from the event manager. * * @param event * The event. */ void Brain::receiveEvent(Event* event) { if (event->getEventType() == EventTypeEnum::EVENT_DATA_FILE_ADD) { EventDataFileAdd* addDataFileEvent = dynamic_cast(event); CaretAssert(addDataFileEvent); try { addDataFile(addDataFileEvent->getCaretDataFile()); } catch (const DataFileException& dfe) { addDataFileEvent->setErrorMessage(dfe.whatString()); } addDataFileEvent->setEventProcessed(); } else if (event->getEventType() == EventTypeEnum::EVENT_DATA_FILE_DELETE) { EventDataFileDelete* deleteDataFileEvent = dynamic_cast(event); CaretAssert(deleteDataFileEvent); removeAndDeleteDataFile(deleteDataFileEvent->getCaretDataFile()); deleteDataFileEvent->setEventProcessed(); } else if (event->getEventType() == EventTypeEnum::EVENT_DATA_FILE_READ) { EventDataFileRead* readDataFileEvent = dynamic_cast(event); CaretAssert(readDataFileEvent); /* * Make sure event is for this brain */ if (readDataFileEvent->getLoadIntoBrain() == this) { readDataFileEvent->setEventProcessed(); processReadDataFileEvent(readDataFileEvent); } } else if (event->getEventType() == EventTypeEnum::EVENT_DATA_FILE_RELOAD) { EventDataFileReload* reloadDataFileEvent = dynamic_cast(event); CaretAssert(reloadDataFileEvent); if (reloadDataFileEvent->getBrain() == this) { reloadDataFileEvent->setEventProcessed(); processReloadDataFileEvent(reloadDataFileEvent); } } else if (event->getEventType() == EventTypeEnum::EVENT_CARET_MAPPABLE_DATA_FILES_GET) { EventCaretMappableDataFilesGet* dataFilesEvent = dynamic_cast(event); CaretAssert(dataFilesEvent); std::vector allCaretMappableFiles; getAllMappableDataFiles(allCaretMappableFiles); for (std::vector::iterator iter = allCaretMappableFiles.begin(); iter != allCaretMappableFiles.end(); iter++) { dataFilesEvent->addFile(*iter); } dataFilesEvent->setEventProcessed(); } else if (event->getEventType() == EventTypeEnum::EVENT_SPEC_FILE_READ_DATA_FILES) { EventSpecFileReadDataFiles* readSpecFileDataFilesEvent = dynamic_cast(event); CaretAssert(readSpecFileDataFilesEvent); /* * Make sure event is for this brain */ if (readSpecFileDataFilesEvent->getLoadIntoBrain() == this) { readSpecFileDataFilesEvent->setEventProcessed(); loadFilesSelectedInSpecFile(readSpecFileDataFilesEvent); } } else if (event->getEventType() == EventTypeEnum::EVENT_GET_DISPLAYED_DATA_FILES) { EventGetDisplayedDataFiles* displayedFilesEvent = dynamic_cast(event); CaretAssert(displayedFilesEvent); /* * Get all visible browser tabs. */ EventBrowserTabGetAll getAllTabsEvent; EventManager::get()->sendEvent(getAllTabsEvent.getPointer()); std::set dataFilesDisplayedInTabs; /* * Get files displayed in each tab. */ const int32_t numberOfTabs = getAllTabsEvent.getNumberOfBrowserTabs(); for (int32_t i = 0; i < numberOfTabs; i++) { BrowserTabContent* btc = getAllTabsEvent.getBrowserTab(i); const int32_t tabIndex = btc->getTabNumber(); if (displayedFilesEvent->isTestForDisplayedDataFileInTabIndex(tabIndex)) { std::vector tabDataFiles; btc->getFilesDisplayedInTab(tabDataFiles); dataFilesDisplayedInTabs.insert(tabDataFiles.begin(), tabDataFiles.end()); } } /* * See if any palette mappable files are displayed */ bool havePaletteMappableFiles = false; for (std::set::const_iterator iter = dataFilesDisplayedInTabs.begin(); iter != dataFilesDisplayedInTabs.end(); iter++) { const CaretMappableDataFile* mappableFile = dynamic_cast(*iter); if (mappableFile != NULL) { if (mappableFile->isMappedWithPalette()) { havePaletteMappableFiles = true; break; } } } /* * If there are ANY palette mappable data files, add the * palette file. */ if (havePaletteMappableFiles) { dataFilesDisplayedInTabs.insert(m_paletteFile); } for (std::set::const_iterator iter = dataFilesDisplayedInTabs.begin(); iter != dataFilesDisplayedInTabs.end(); iter++) { displayedFilesEvent->addDisplayedDataFile(*iter); } /* * Annotation files */ std::vector annotationFiles; m_annotationManager->getDisplayedAnnotationFiles(displayedFilesEvent, annotationFiles); if ( ! annotationFiles.empty()) { dataFilesDisplayedInTabs.insert(annotationFiles.begin(), annotationFiles.end()); } } else if (event->getEventType() == EventTypeEnum::EVENT_PALETTE_GET_BY_NAME) { EventPaletteGetByName* paletteGetByName = dynamic_cast(event); CaretAssert(paletteGetByName); if (m_paletteFile != NULL) { Palette* palette = m_paletteFile->getPaletteByName(paletteGetByName->getPaletteName()); if (palette != NULL) { paletteGetByName->setPalette(palette); paletteGetByName->setEventProcessed(); } } } } /** * @return The chart model (warning may be NULL!) */ ModelChart* Brain::getChartModel() { return m_modelChart; } /** * @return The chart model (warning may be NULL!) */ const ModelChart* Brain::getChartModel() const { return m_modelChart; } /** * @return The annotation manager. */ AnnotationManager* Brain::getAnnotationManager() { return m_annotationManager; } /** * @return The annotation manager. */ const AnnotationManager* Brain::getAnnotationManager() const { return m_annotationManager; } /** * @return The charting data manager. */ ChartingDataManager* Brain::getChartingDataManager() { return m_chartingDataManager; } /** * @return The charting data manager. */ const ChartingDataManager* Brain::getChartingDataManager() const { return m_chartingDataManager; } /** * @return The current directory. */ AString Brain::getCurrentDirectory() const { if (m_currentDirectory.isEmpty()) { m_currentDirectory = SystemUtilities::systemCurrentDirectory(); } return m_currentDirectory; } /** * Set the current directory. * @param currentDirectory * New value for current directory. */ void Brain::setCurrentDirectory(const AString& currentDirectory) { m_currentDirectory = currentDirectory; } /** * Get All CaretMappableDataFiles. * * @param allCaretMappableDataFilesOut * Will contain instance of CaretMappableDataFiles upon exit. */ void Brain::getAllMappableDataFiles(std::vector& allCaretMappableDataFilesOut) const { allCaretMappableDataFilesOut.clear(); std::vector allDataFiles; getAllDataFiles(allDataFiles); for (std::vector::iterator iter = allDataFiles.begin(); iter != allDataFiles.end(); iter++) { CaretDataFile* cdf = *iter; CaretMappableDataFile* cmdf = dynamic_cast(cdf); if (cmdf != NULL) { allCaretMappableDataFilesOut.push_back(cmdf); } } } /** * Get All CaretMappableDataFiles of the given data file type. * * @param dataFileType * Type of data file. * @param caretMappableDataFilesOut * Contains CaretMappableDataFiles matching data file type upon exit. */ void Brain::getAllMappableDataFileWithDataFileType(const DataFileTypeEnum::Enum dataFileType, std::vector& caretMappableDataFilesOut) const { caretMappableDataFilesOut.clear(); std::vector allFiles; getAllMappableDataFiles(allFiles); for (std::vector::iterator iter = allFiles.begin(); iter != allFiles.end(); iter++) { CaretMappableDataFile* cmdf = *iter; if (cmdf->getDataFileType() == dataFileType) { caretMappableDataFilesOut.push_back(cmdf); } } } /** * Get All CaretMappableDataFiles of the given data file types. * * @param dataFileType * Type of data file. * @param caretMappableDataFilesOut * Contains CaretMappableDataFiles matching data file type upon exit. */ void Brain::getAllMappableDataFileWithDataFileTypes(const std::vector& dataFileTypes, std::vector& caretMappableDataFilesOut) const { caretMappableDataFilesOut.clear(); std::vector allFiles; getAllMappableDataFiles(allFiles); for (std::vector::iterator iter = allFiles.begin(); iter != allFiles.end(); iter++) { CaretMappableDataFile* cmdf = *iter; if (std::find(dataFileTypes.begin(), dataFileTypes.end(), cmdf->getDataFileType()) != dataFileTypes.end()) { caretMappableDataFilesOut.push_back(cmdf); } } } /** * Get all CaretDataFiles of the given data file types. * * @param dataFileTypes * Types of data files. * @param caretDataFilesOut * Data file of the given data file type that were found. */ void Brain::getAllDataFilesWithDataFileTypes(const std::vector& dataFileTypes, std::vector& caretDataFilesOut) const { caretDataFilesOut.clear(); std::vector allDataFiles; getAllDataFiles(allDataFiles, true); for (std::vector::iterator iter = allDataFiles.begin(); iter != allDataFiles.end(); iter++) { CaretDataFile* cdf = *iter; if (std::find(dataFileTypes.begin(), dataFileTypes.end(), cdf->getDataFileType()) != dataFileTypes.end()) { caretDataFilesOut.push_back(cdf); } } } /** * Get all CaretDataFiles of the given data file type. * * @param dataFileType * Type of data file. * @param caretDataFilesOut * Data file of the given data file type that were found. */ void Brain::getAllDataFilesWithDataFileType(const DataFileTypeEnum::Enum dataFileType, std::vector& caretDataFilesOut) const { std::vector dataFileTypes; dataFileTypes.push_back(dataFileType); getAllDataFilesWithDataFileTypes(dataFileTypes, caretDataFilesOut); // caretDataFilesOut.clear(); // // std::vector allDataFiles; // getAllDataFiles(allDataFiles, // true); // // for (std::vector::iterator iter = allDataFiles.begin(); // iter != allDataFiles.end(); // iter++) { // CaretDataFile* cdf = *iter; // if (cdf->getDataFileType() == dataFileType) { // caretDataFilesOut.push_back(cdf); // } // } } /** * Get all loaded data files. * @param allDataFilesOut * Data files are loaded into this parameter. * @param includeSpecFile * If true, the spec file is included as the first file. */ void Brain::getAllDataFiles(std::vector& allDataFilesOut, const bool includeSpecFile) const { allDataFilesOut.clear(); if (includeSpecFile) { if (m_specFile->isEmpty() == false) { allDataFilesOut.push_back(m_specFile); } } const int32_t numBrainStructures = getNumberOfBrainStructures(); for (int32_t i = 0; i < numBrainStructures; i++) { getBrainStructure(i)->getAllDataFiles(allDataFilesOut); } allDataFilesOut.insert(allDataFilesOut.end(), m_annotationFiles.begin(), m_annotationFiles.end()); allDataFilesOut.insert(allDataFilesOut.end(), m_borderFiles.begin(), m_borderFiles.end()); allDataFilesOut.insert(allDataFilesOut.end(), m_fociFiles.begin(), m_fociFiles.end()); allDataFilesOut.insert(allDataFilesOut.end(), m_imageFiles.begin(), m_imageFiles.end()); allDataFilesOut.insert(allDataFilesOut.end(), m_connectivityDenseScalarFiles.begin(), m_connectivityDenseScalarFiles.end()); allDataFilesOut.insert(allDataFilesOut.end(), m_connectivityMatrixDenseFiles.begin(), m_connectivityMatrixDenseFiles.end()); // allDataFilesOut.insert(allDataFilesOut.end(), // m_connectivityDataSeriesFiles.begin(), // m_connectivityDataSeriesFiles.end()); /* * By placing the dynamic connectivity file immediately after * its parent data-series file, they will appear in this * order in the overlay file selectors. */ for (std::vector::const_iterator dsIter = m_connectivityDataSeriesFiles.begin(); dsIter != m_connectivityDataSeriesFiles.end(); dsIter++) { CiftiBrainordinateDataSeriesFile* seriesFile = *dsIter; CaretAssert(seriesFile); allDataFilesOut.push_back(seriesFile); CiftiConnectivityMatrixDenseDynamicFile* dynFile = seriesFile->getConnectivityMatrixDenseDynamicFile(); CaretAssert(dynFile); if (dynFile->isDataValid()) { allDataFilesOut.push_back(dynFile); } } // std::vector denseDynFiles; // getConnectivityMatrixDenseDynamicFiles(denseDynFiles); // allDataFilesOut.insert(allDataFilesOut.end(), // denseDynFiles.begin(), // denseDynFiles.end()); allDataFilesOut.insert(allDataFilesOut.end(), m_connectivityDenseLabelFiles.begin(), m_connectivityDenseLabelFiles.end()); allDataFilesOut.insert(allDataFilesOut.end(), m_connectivityMatrixDenseParcelFiles.begin(), m_connectivityMatrixDenseParcelFiles.end()); allDataFilesOut.insert(allDataFilesOut.end(), m_connectivityFiberOrientationFiles.begin(), m_connectivityFiberOrientationFiles.end()); allDataFilesOut.insert(allDataFilesOut.end(), m_connectivityFiberTrajectoryFiles.begin(), m_connectivityFiberTrajectoryFiles.end()); allDataFilesOut.insert(allDataFilesOut.end(), m_connectivityMatrixParcelFiles.begin(), m_connectivityMatrixParcelFiles.end()); allDataFilesOut.insert(allDataFilesOut.end(), m_connectivityMatrixParcelDenseFiles.begin(), m_connectivityMatrixParcelDenseFiles.end()); allDataFilesOut.insert(allDataFilesOut.end(), m_connectivityParcelLabelFiles.begin(), m_connectivityParcelLabelFiles.end()); allDataFilesOut.insert(allDataFilesOut.end(), m_connectivityParcelScalarFiles.begin(), m_connectivityParcelScalarFiles.end()); allDataFilesOut.insert(allDataFilesOut.end(), m_connectivityParcelSeriesFiles.begin(), m_connectivityParcelSeriesFiles.end()); allDataFilesOut.insert(allDataFilesOut.end(), m_connectivityScalarDataSeriesFiles.begin(), m_connectivityScalarDataSeriesFiles.end()); allDataFilesOut.push_back(m_paletteFile); allDataFilesOut.insert(allDataFilesOut.end(), m_sceneFiles.begin(), m_sceneFiles.end()); allDataFilesOut.insert(allDataFilesOut.end(), m_volumeFiles.begin(), m_volumeFiles.end()); } /** * Determine if a file is still valid (pointer is for an existing data * of the same DataFileType. */ bool Brain::isFileValid(const CaretDataFile* caretDataFile) const { std::vector allDataFiles; getAllDataFiles(allDataFiles); for (std::vector::iterator iter = allDataFiles.begin(); iter != allDataFiles.end(); iter++) { const CaretDataFile* cdf = *iter; if (caretDataFile == cdf) { if (caretDataFile->getDataFileType() == cdf->getDataFileType()) { return true; } } } return false; } /** * Get all of the modified files excluding the given data file types. * * @param excludeTheseDataTypes * Data types of files that excluded. * @param modifiedDataFilesOut * Output containing the modified files. * */ void Brain::getAllModifiedFiles(const std::vector& excludeTheseDataTypes, std::vector& modifiedDataFilesOut) const { modifiedDataFilesOut.clear(); if (std::find(excludeTheseDataTypes.begin(), excludeTheseDataTypes.end(), DataFileTypeEnum::SPECIFICATION) == excludeTheseDataTypes.end()) { if (m_specFile->isModified()) { modifiedDataFilesOut.push_back(m_specFile); } } std::vector dataFiles; getAllDataFiles(dataFiles); for (std::vector::iterator iter = dataFiles.begin(); iter != dataFiles.end(); iter++) { CaretDataFile* cdf = *iter; /** * Ignore files whose data type is excluded. */ if (std::find(excludeTheseDataTypes.begin(), excludeTheseDataTypes.end(), cdf->getDataFileType()) == excludeTheseDataTypes.end()) { if (cdf->isModified()) { modifiedDataFilesOut.push_back(cdf); } } } } ///** // * Are any data files modified (including spec file)? // * @param excludeTheseDataTypes // * Do not check the modification status of any data files whose // * data type is contained in this parameter. // */ //bool //Brain::areFilesModified(const std::vector& excludeTheseDataTypes) //{ // if (std::find(excludeTheseDataTypes.begin(), // excludeTheseDataTypes.end(), // DataFileTypeEnum::SPECIFICATION) == excludeTheseDataTypes.end()) { // if (m_specFile->isModified()) { // return true; // } // } // // std::vector dataFiles; // getAllDataFiles(dataFiles); // // for (std::vector::iterator iter = dataFiles.begin(); // iter != dataFiles.end(); // iter++) { // CaretDataFile* cdf = *iter; // // /** // * Ignore files whose data type is excluded. // */ // if (std::find(excludeTheseDataTypes.begin(), // excludeTheseDataTypes.end(), // cdf->getDataFileType()) == excludeTheseDataTypes.end()) { // if (cdf->isModified()) { // return true; // } // } // } // // return false; //} /** * Write a data file. * @param caretDataFile * Data file to write. * @return * true if file was written, else false. * @throw * DataFileException if there was an error writing the file. */ void Brain::writeDataFile(CaretDataFile* caretDataFile) { AString dataFileName = caretDataFile->getFileName(); /* * If file is on network, it cannot be written ! */ if (DataFile::isFileOnNetwork(dataFileName)) { throw DataFileException(dataFileName, "Cannot write a file with a network path. " "To write the file, its name must be changed to " "a path on your computer. This can be done using " "the \"More\" icon on the Manage Files Dialog " "(File Menu->Save/Manage Files)."); } /* * If file is relative path, update path using current directory */ dataFileName = convertFilePathNameToAbsolutePathName(dataFileName); caretDataFile->setFileName(dataFileName); /* * Write the data file */ caretDataFile->writeFile(caretDataFile->getFileName()); caretDataFile->clearModified(); /* * File has been successfully written. * Perform any post-write actions. */ const DataFileTypeEnum::Enum dataFileType = caretDataFile->getDataFileType(); switch (dataFileType) { 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: break; case DataFileTypeEnum::METRIC: break; case DataFileTypeEnum::PALETTE: break; case DataFileTypeEnum::RGBA: break; case DataFileTypeEnum::SCENE: { /* * Add to recent scene files */ CaretPreferences* prefs = SessionManager::get()->getCaretPreferences(); prefs->addToPreviousSceneFiles(caretDataFile->getFileName()); } break; case DataFileTypeEnum::SPECIFICATION: break; case DataFileTypeEnum::SURFACE: break; case DataFileTypeEnum::UNKNOWN: break; case DataFileTypeEnum::VOLUME: break; } } /** * Remove the data file from memory but DO NOT delete it. * * @param caretDataFile * Caret file that is removed from the Brain. After calling this method * and the file was removed( true was returned), the caller is responsible * for deleting the file when it is no longer needed. * @return * True if the file was removed, else false. */ bool Brain::removeWithoutDeleteDataFile(const CaretDataFile* caretDataFile) { if (caretDataFile == NULL) { return false; } /* * Dense dynamic files are encapsulated in a dense-series file * so they do not get removed. */ if (caretDataFile->getDataFileType() == DataFileTypeEnum::CONNECTIVITY_DENSE_DYNAMIC) { return false; } const bool wasRemoved = removeWithoutDeleteDataFilePrivate(caretDataFile); if (wasRemoved) { m_specFile->removeCaretDataFile(caretDataFile); updateAfterFilesAddedOrRemoved(); } else { CaretLogSevere("Software bug: failed to remove file type=" + DataFileTypeEnum::toName(caretDataFile->getDataFileType()) + " name=" + caretDataFile->getFileName()); } return wasRemoved; } /** * Remove the data file from memory but DO NOT delete it. * * @param caretDataFile * Caret file that is removed from the Brain. After calling this method * and the file was removed( true was returned), the caller is responsible * for deleting the file when it is no longer needed. * @return * True if the file was removed, else false. */ bool Brain::removeWithoutDeleteDataFilePrivate(const CaretDataFile* caretDataFile) { const int32_t numBrainStructures = getNumberOfBrainStructures(); for (int32_t i = 0; i < numBrainStructures; i++) { if (getBrainStructure(i)->removeWithoutDeleteDataFile(caretDataFile)) { return true; } } std::vector::iterator annotationIterator = std::find(m_annotationFiles.begin(), m_annotationFiles.end(), caretDataFile); if (annotationIterator != m_annotationFiles.end()) { m_annotationFiles.erase(annotationIterator); return true; } std::vector::iterator borderIterator = std::find(m_borderFiles.begin(), m_borderFiles.end(), caretDataFile); if (borderIterator != m_borderFiles.end()) { m_borderFiles.erase(borderIterator); return true; } std::vector::iterator dataSeriesIterator = std::find(m_connectivityDataSeriesFiles.begin(), m_connectivityDataSeriesFiles.end(), caretDataFile); if (dataSeriesIterator != m_connectivityDataSeriesFiles.end()) { m_connectivityDataSeriesFiles.erase(dataSeriesIterator); return true; } std::vector::iterator connLabelIterator = std::find(m_connectivityDenseLabelFiles.begin(), m_connectivityDenseLabelFiles.end(), caretDataFile); if (connLabelIterator != m_connectivityDenseLabelFiles.end()) { m_connectivityDenseLabelFiles.erase(connLabelIterator); return true; } std::vector::iterator connDenseIterator = std::find(m_connectivityMatrixDenseFiles.begin(), m_connectivityMatrixDenseFiles.end(), caretDataFile); if (connDenseIterator != m_connectivityMatrixDenseFiles.end()) { m_connectivityMatrixDenseFiles.erase(connDenseIterator); return true; } /* Note: There is no test for dense dynamic files as they are a child of data sereies file */ std::vector::iterator connDenseParcelIterator = std::find(m_connectivityMatrixDenseParcelFiles.begin(), m_connectivityMatrixDenseParcelFiles.end(), caretDataFile); if (connDenseParcelIterator != m_connectivityMatrixDenseParcelFiles.end()) { m_connectivityMatrixDenseParcelFiles.erase(connDenseParcelIterator); return true; } std::vector::iterator connScalarIterator = std::find(m_connectivityDenseScalarFiles.begin(), m_connectivityDenseScalarFiles.end(), caretDataFile); if (connScalarIterator != m_connectivityDenseScalarFiles.end()) { m_connectivityDenseScalarFiles.erase(connScalarIterator); return true; } std::vector::iterator connParcelSeriesIterator = std::find(m_connectivityParcelSeriesFiles.begin(), m_connectivityParcelSeriesFiles.end(), caretDataFile); if (connParcelSeriesIterator != m_connectivityParcelSeriesFiles.end()) { m_connectivityParcelSeriesFiles.erase(connParcelSeriesIterator); return true; } std::vector::iterator connParcelLabelIterator = std::find(m_connectivityParcelLabelFiles.begin(), m_connectivityParcelLabelFiles.end(), caretDataFile); if (connParcelLabelIterator != m_connectivityParcelLabelFiles.end()) { m_connectivityParcelLabelFiles.erase(connParcelLabelIterator); return true; } std::vector::iterator connParcelScalarIterator = std::find(m_connectivityParcelScalarFiles.begin(), m_connectivityParcelScalarFiles.end(), caretDataFile); if (connParcelScalarIterator != m_connectivityParcelScalarFiles.end()) { m_connectivityParcelScalarFiles.erase(connParcelScalarIterator); return true; } std::vector::iterator connScalarDataSeriesIterator = std::find(m_connectivityScalarDataSeriesFiles.begin(), m_connectivityScalarDataSeriesFiles.end(), caretDataFile); if (connScalarDataSeriesIterator != m_connectivityScalarDataSeriesFiles.end()) { m_connectivityScalarDataSeriesFiles.erase(connScalarDataSeriesIterator); return true; } std::vector::iterator connFiberOrientationIterator = std::find(m_connectivityFiberOrientationFiles.begin(), m_connectivityFiberOrientationFiles.end(), caretDataFile); if (connFiberOrientationIterator != m_connectivityFiberOrientationFiles.end()) { m_connectivityFiberOrientationFiles.erase(connFiberOrientationIterator); return true; } std::vector::iterator connFiberTrajectoryIterator = std::find(m_connectivityFiberTrajectoryFiles.begin(), m_connectivityFiberTrajectoryFiles.end(), caretDataFile); if (connFiberTrajectoryIterator != m_connectivityFiberTrajectoryFiles.end()) { m_connectivityFiberTrajectoryFiles.erase(connFiberTrajectoryIterator); return true; } std::vector::iterator connParcelIterator = std::find(m_connectivityMatrixParcelFiles.begin(), m_connectivityMatrixParcelFiles.end(), caretDataFile); if (connParcelIterator != m_connectivityMatrixParcelFiles.end()) { m_connectivityMatrixParcelFiles.erase(connParcelIterator); return true; } std::vector::iterator connParcelDenseIterator = std::find(m_connectivityMatrixParcelDenseFiles.begin(), m_connectivityMatrixParcelDenseFiles.end(), caretDataFile); if (connParcelDenseIterator != m_connectivityMatrixParcelDenseFiles.end()) { m_connectivityMatrixParcelDenseFiles.erase(connParcelDenseIterator); return true; } std::vector::iterator fociIterator = std::find(m_fociFiles.begin(), m_fociFiles.end(), caretDataFile); if (fociIterator != m_fociFiles.end()) { m_fociFiles.erase(fociIterator); return true; } std::vector::iterator imageIterator = std::find(m_imageFiles.begin(), m_imageFiles.end(), caretDataFile); if (imageIterator != m_imageFiles.end()) { m_imageFiles.erase(imageIterator); return true; } if (m_paletteFile == caretDataFile) { if (m_paletteFile != NULL) { CaretLogSevere("Cannot remove PaletteFile at this time."); } } std::vector::iterator sceneIterator = std::find(m_sceneFiles.begin(), m_sceneFiles.end(), caretDataFile); if (sceneIterator != m_sceneFiles.end()) { m_sceneFiles.erase(sceneIterator); return true; } std::vector::iterator volumeIterator = std::find(m_volumeFiles.begin(), m_volumeFiles.end(), caretDataFile); if (volumeIterator != m_volumeFiles.end()) { m_volumeFiles.erase(volumeIterator); return true; } return false; } /** * Remove AND DELETE a data file from memory (does NOT delete file on disk.) * Searches all of the loaded files for given file, and, when found * deletes the file. * * @param caretDataFile * Data file to remove. After calling this method and the file was * deleted (true was returned) this pointer is no longer valid. * @return * true if file was removed, else false. */ bool Brain::removeAndDeleteDataFile(CaretDataFile* caretDataFile) { if (removeWithoutDeleteDataFile(caretDataFile)) { delete caretDataFile; return true; } return false; } /** * @return The annotation display properties. */ DisplayPropertiesAnnotation* Brain::getDisplayPropertiesAnnotation() { return m_displayPropertiesAnnotation; } /** * @return The annotation display properties. */ const DisplayPropertiesAnnotation* Brain::getDisplayPropertiesAnnotation() const { return m_displayPropertiesAnnotation; } /** * @return The border display properties. */ DisplayPropertiesBorders* Brain::getDisplayPropertiesBorders() { return m_displayPropertiesBorders; } /** * @return The border display properties. */ const DisplayPropertiesBorders* Brain::getDisplayPropertiesBorders() const { return m_displayPropertiesBorders; } /** * @return The fiber orientation display properties. */ DisplayPropertiesFiberOrientation* Brain::getDisplayPropertiesFiberOrientation() { return m_displayPropertiesFiberOrientation; } /** * @return The fiber orientation display properties. */ const DisplayPropertiesFiberOrientation* Brain::getDisplayPropertiesFiberOrientation() const { return m_displayPropertiesFiberOrientation; } /** * @return The foci display properties. */ DisplayPropertiesFoci* Brain::getDisplayPropertiesFoci() { return m_displayPropertiesFoci; } /** * @return The foci display properties. */ const DisplayPropertiesFoci* Brain::getDisplayPropertiesFoci() const { return m_displayPropertiesFoci; } /** * @return The label display properties. */ DisplayPropertiesImages* Brain::getDisplayPropertiesImages() { return m_displayPropertiesImages; } /** * @return The label display properties. */ const DisplayPropertiesImages* Brain::getDisplayPropertiesImages() const { return m_displayPropertiesImages; } /** * @return The label display properties. */ DisplayPropertiesLabels* Brain::getDisplayPropertiesLabels() { return m_displayPropertiesLabels; } /** * @return The label display properties. */ const DisplayPropertiesLabels* Brain::getDisplayPropertiesLabels() const { return m_displayPropertiesLabels; } /** * @return The volume display properties. */ DisplayPropertiesVolume* Brain::getDisplayPropertiesVolume() { return m_displayPropertiesVolume; } /** * @return The volume display properties. */ const DisplayPropertiesVolume* Brain::getDisplayPropertiesVolume() const { return m_displayPropertiesVolume; } /** * @return The surface display properties. */ DisplayPropertiesSurface* Brain::getDisplayPropertiesSurface() { return m_displayPropertiesSurface; } /** * @return The volume display properties. */ const DisplayPropertiesSurface* Brain::getDisplayPropertiesSurface() const { return m_displayPropertiesSurface; } /** * Create a scene for an instance of a class. * * @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. * * @return Pointer to SceneClass object representing the state of * this object. Under some circumstances a NULL pointer may be * returned. Caller will take ownership of returned object. */ SceneClass* Brain::saveToScene(const SceneAttributes* sceneAttributes, const AString& instanceName) { bool isSaveSpecFile = false; switch (sceneAttributes->getSceneType()) { case SceneTypeEnum::SCENE_TYPE_FULL: isSaveSpecFile = true; break; case SceneTypeEnum::SCENE_TYPE_GENERIC: break; } SceneClass* sceneClass = new SceneClass(instanceName, "Brain", 1); /* * Get all data files */ std::vector allCaretDataFiles; getAllDataFiles(allCaretDataFiles); /* * Save data files into an array. * Note that data file's saveToScene returns NULL if no data for saving. */ std::vector allCaretDataFileScenes; for (std::vector::iterator iter = allCaretDataFiles.begin(); iter != allCaretDataFiles.end(); iter++) { CaretDataFile* cdf = *iter; const AString caretDataFileName = cdf->getFileName(); // use full path 7/16/2015 cdf->getFileNameNoPath(); SceneClass* caretDataFileSceneClass = cdf->saveToScene(sceneAttributes, caretDataFileName); if (caretDataFileSceneClass != NULL) { allCaretDataFileScenes.push_back(caretDataFileSceneClass); } } if (allCaretDataFileScenes.empty() == false) { SceneClassArray* caretDataFileSceneArray = new SceneClassArray("allCaretDataFiles", allCaretDataFileScenes); sceneClass->addChild(caretDataFileSceneArray); } if (isSaveSpecFile) { SpecFile sf; sf.setFileName(m_specFile->getFileName()); for (std::vector::iterator iter = allCaretDataFiles.begin(); iter != allCaretDataFiles.end(); iter++) { CaretDataFile* cdf = *iter; sf.addCaretDataFile(cdf); } sceneClass->addClass(sf.saveToScene(sceneAttributes, "specFile")); } m_sceneAssistant->saveMembers(sceneAttributes, sceneClass); /* * Clear modification status of scene annotations */ m_sceneAnnotationFile->clearModified(); /* * Save all models */ std::vector modelClassVector; EventModelGetAll 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; modelClassVector.push_back(mdc->saveToScene(sceneAttributes, "models")); } SceneClassArray* modelsClassArray = new SceneClassArray("models", modelClassVector); sceneClass->addChild(modelsClassArray); /* * Save all brain structures */ const int32_t numBrainStructures = getNumberOfBrainStructures(); SceneClassArray* brainStructureClassArray = new SceneClassArray("m_brainStructures", numBrainStructures); for (int32_t i = 0; i < numBrainStructures; i++) { const AString name = ("m_brainStructures[" + AString::number(i) + "]"); brainStructureClassArray->setClassAtIndex(i, m_brainStructures[i]->saveToScene(sceneAttributes, name)); } sceneClass->addChild(brainStructureClassArray); /* * Save Group/Name Selection Hierarchies */ for (std::vector::iterator borderIter = m_borderFiles.begin(); borderIter != m_borderFiles.end(); borderIter++) { BorderFile* bf = *borderIter; sceneClass->addClass(bf->getGroupAndNameHierarchyModel()->saveToScene(sceneAttributes, bf->getFileNameNoPath())); } for (std::vector::iterator fociIter = m_fociFiles.begin(); fociIter != m_fociFiles.end(); fociIter++) { FociFile* ff = *fociIter; sceneClass->addClass(ff->getGroupAndNameHierarchyModel()->saveToScene(sceneAttributes, ff->getFileNameNoPath())); } sceneClass->addClass(m_identificationManager->saveToScene(sceneAttributes, "m_identificationManager")); sceneClass->addClass(m_brainordinateHighlightRegionOfInterest->saveToScene(sceneAttributes, "m_brainordinateHighlightRegionOfInterest")); return sceneClass; } /** * Restore the state of an instance of a class. * * @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. May be NULL for some types of scenes. */ void Brain::restoreFromScene(const SceneAttributes* sceneAttributes, const SceneClass* sceneClass) { if (sceneClass == NULL) { return; } /* * Prior to restoring the scene, make a copy of the current spec file * so that the "in spec" status for data files within the spec file * can be preserved. */ const SpecFile preSceneLoadSpecFile(*m_specFile); bool isLoadFiles = false; switch (sceneAttributes->getSceneType()) { case SceneTypeEnum::SCENE_TYPE_FULL: isLoadFiles = true; break; case SceneTypeEnum::SCENE_TYPE_GENERIC: break; } if (isLoadFiles) { SpecFile specFile; specFile.restoreFromScene(sceneAttributes, sceneClass->getClass("specFile")); loadSpecFileFromScene(sceneAttributes, &specFile, RESET_BRAIN_KEEP_SCENE_FILES_YES, RESET_BRAIN_KEEP_SPEC_FILE_YES); } /* * Preserve "in spec" status prior to loading of scene. */ m_specFile->transferDataFilesInSpecStatus(preSceneLoadSpecFile); /* * Add all scene files to the spec file (but not a member of * the spec file) since a scene file may not be in the spec file. */ const bool specFileModStatus = m_specFile->isModified(); for (std::vector::iterator sceneIter = m_sceneFiles.begin(); sceneIter != m_sceneFiles.end(); sceneIter++) { m_specFile->addCaretDataFile(*sceneIter); } if (specFileModStatus == false) { m_specFile->clearModified(); } /* * Get all data files */ std::vector allCaretDataFiles; getAllDataFiles(allCaretDataFiles); /* * Restore data files */ const SceneClassArray* caretDataFileSceneArray = sceneClass->getClassArray("allCaretDataFiles"); if (caretDataFileSceneArray != NULL) { for (std::vector::iterator iter = allCaretDataFiles.begin(); iter != allCaretDataFiles.end(); iter++) { CaretDataFile* caretDataFile = *iter; CaretAssert(caretDataFile); const AString caretDataFileNameNoPath = caretDataFile->getFileNameNoPath(); const AString caretDataFileNameFullPath = caretDataFile->getFileName(); SceneClass* bestMatchingSceneClass = NULL; int64_t bestMatchingCount = 0; const int32_t numCaretDataFileScenes = caretDataFileSceneArray->getNumberOfArrayElements(); for (int32_t i = 0; i < numCaretDataFileScenes; i++) { const SceneClass* fileSceneClass = caretDataFileSceneArray->getClassAtIndex(i); const AString fileNameFullPath = fileSceneClass->getName(); const FileInformation fileInfo(fileNameFullPath); const AString fileNameNoPath = fileInfo.getFileName(); if (caretDataFileNameNoPath == fileNameNoPath) { const int64_t matchCount = caretDataFileNameFullPath.countMatchingCharactersFromEnd(fileNameFullPath); if (matchCount > bestMatchingCount) { bestMatchingSceneClass = const_cast(fileSceneClass); bestMatchingCount = matchCount; } // caretDataFile->restoreFromScene(sceneAttributes, // fileSceneClass); } } if (bestMatchingCount > 0) { CaretAssert(bestMatchingSceneClass); caretDataFile->restoreFromScene(sceneAttributes, bestMatchingSceneClass); } } } /* * Fiber trajectory files need special handling after restoring a scene. */ updateFiberTrajectoryMatchingFiberOrientationFiles(); for (std::vector::iterator iter = m_connectivityFiberTrajectoryFiles.begin(); iter != m_connectivityFiberTrajectoryFiles.end(); iter++) { CiftiFiberTrajectoryFile* trajFile = *iter; trajFile->finishRestorationOfScene(); } /* * Restore members */ m_sceneAssistant->restoreMembers(sceneAttributes, sceneClass); /* * Need to color all connectivity matrix files */ std::vector ciftiMatrixFiles; getAllCiftiConnectivityMatrixFiles(ciftiMatrixFiles); for (std::vector::iterator iter = ciftiMatrixFiles.begin(); iter != ciftiMatrixFiles.end(); iter++) { CiftiMappableConnectivityMatrixDataFile* cmf = *iter; if (cmf->isEmpty() == false) { const int32_t mapIndex = 0; cmf->updateScalarColoringForMap(mapIndex, getPaletteFile()); } } /* * Restore all models */ EventModelGetAll getAllModels; EventManager::get()->sendEvent(getAllModels.getPointer()); std::vector allModels = getAllModels.getModels(); const SceneClassArray* modelsClassArray = sceneClass->getClassArray("models"); const int32_t numModelClasses = modelsClassArray->getNumberOfArrayElements(); for (int32_t i = 0; i < numModelClasses; i++) { /* * Apply to all models. Each model will only use the saved * scene information that is applicable. */ for (std::vector::iterator iter = allModels.begin(); iter != allModels.end(); iter++) { Model* mdc = *iter; mdc->restoreFromScene(sceneAttributes, modelsClassArray->getClassAtIndex(i)); } } /* * Apply each of the saved brain structures to each of the loaded brain structures. * The loaded brain structure will examine the structure and number of nodes in each * saved brain structure to determine the proper one to use for restoration */ const SceneClassArray* brainStructureClassArray = sceneClass->getClassArray("m_brainStructures"); const int32_t numSavedBrainStructures = brainStructureClassArray->getNumberOfArrayElements(); const int32_t numValidBrainStructures = getNumberOfBrainStructures(); for (int32_t i = 0; i < numValidBrainStructures; i++) { BrainStructure* bs = m_brainStructures[i]; for (int32_t j = 0; j < numSavedBrainStructures; j++) { bs->restoreFromScene(sceneAttributes, brainStructureClassArray->getClassAtIndex(j)); } } switch (sceneAttributes->getSceneType()) { case SceneTypeEnum::SCENE_TYPE_FULL: break; case SceneTypeEnum::SCENE_TYPE_GENERIC: break; } /* * Restore Group/Name Selection Hierarchies */ for (std::vector::iterator borderIter = m_borderFiles.begin(); borderIter != m_borderFiles.end(); borderIter++) { BorderFile* bf = *borderIter; const SceneClass* borderScene = sceneClass->getClass(bf->getFileNameNoPath()); if (borderScene != NULL) { /* * WB-533 Default State of Borders in Scenes * * When there is scene information for a border file, disable the display * of all classes and names prior to restoring the class/name hierarchy. * The purpose of this is to prevent the display of borders that have been * added to the border file AFTER the scene was created. */ GroupAndNameHierarchyModel* groupAndNameModel = bf->getGroupAndNameHierarchyModel(); groupAndNameModel->setAllSelected(false); bf->getGroupAndNameHierarchyModel()->restoreFromScene(sceneAttributes, borderScene); } } for (std::vector::iterator fociIter = m_fociFiles.begin(); fociIter != m_fociFiles.end(); fociIter++) { FociFile* ff = *fociIter; ff->getGroupAndNameHierarchyModel()->restoreFromScene(sceneAttributes, sceneClass->getClass(ff->getFileNameNoPath())); } m_identificationManager->restoreFromScene(sceneAttributes, sceneClass->getClass("m_identificationManager")); m_brainordinateHighlightRegionOfInterest->restoreFromScene(sceneAttributes, sceneClass->getClass("m_brainordinateHighlightRegionOfInterest")); m_sceneAnnotationFile->clearModified(); } /** * @return The gaps and margins. */ GapsAndMargins* Brain::getGapsAndMargins() { return m_gapsAndMargins; } /** * @return the gaps and margins. */ const GapsAndMargins* Brain::getGapsAndMargins() const { return m_gapsAndMargins; } /** * @return The selection manager. */ SelectionManager* Brain::getSelectionManager() { return m_selectionManager; } /** * @return The identification manager. */ IdentificationManager* Brain::getIdentificationManager() { return m_identificationManager; } /** Region of interest for highlighting brainordinates */ BrainordinateRegionOfInterest* Brain::getBrainordinateHighlightRegionOfInterest() { return m_brainordinateHighlightRegionOfInterest; } const BrainordinateRegionOfInterest* Brain::getBrainordinateHighlightRegionOfInterest() const { return m_brainordinateHighlightRegionOfInterest; } /** * Get the fiber orientation sample vectors for display on a sphere. * * @param xVectors * Vectors for X-orientation. * @param yVectors * Vectors for Y-orientation. * @param zVectors * Vectors for Z-orientation. * @param fiberOrientation * The nearby fiber orientation * @param errorMessageOut * Will contain any error messages. * This error message will only be set in some cases when there is an * error. * @return * True if data is valid, else false. */ bool Brain::getFiberOrientationSphericalSamplesVectors(std::vector& xVectors, std::vector& yVectors, std::vector& zVectors, FiberOrientation* &fiberOrientationOut, AString& errorMessageOut) { CaretAssert(m_fiberOrientationSamplesLoader); return m_fiberOrientationSamplesLoader->getFiberOrientationSphericalSamplesVectors(this, xVectors, yVectors, zVectors, fiberOrientationOut, errorMessageOut); } connectome-workbench-1.2.3+git41-gc4c6c90/src/Brain/Brain.h000066400000000000000000001121261300200146000230060ustar00rootroot00000000000000#ifndef __BRAIN_H__ #define __BRAIN_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 "CaretObject.h" #include "ChartDataTypeEnum.h" #include "DataFileTypeEnum.h" #include "DisplayGroupEnum.h" #include "EventListenerInterface.h" #include "FiberOrientationSamplesLoader.h" #include "FiberOrientationSamplesVector.h" #include "FileInformation.h" #include "SceneableInterface.h" #include "StructureEnum.h" #include "VolumeFile.h" namespace caret { class AnnotationFile; class AnnotationManager; class Border; class BorderFile; class BorderPointFromSearch; class BrainordinateRegionOfInterest; class FociFile; class BrainStructure; class CaretDataFile; class CaretMappableDataFile; class ChartingDataManager; class ChartableLineSeriesBrainordinateInterface; class ChartableMatrixInterface; class CiftiBrainordinateDataSeriesFile; class CiftiBrainordinateLabelFile; class CiftiBrainordinateScalarFile; class CiftiConnectivityMatrixDenseFile; class CiftiConnectivityMatrixDenseDynamicFile; class CiftiConnectivityMatrixDenseParcelFile; class CiftiConnectivityMatrixParcelFile; class CiftiConnectivityMatrixParcelDenseFile; class CiftiFiberOrientationFile; class CiftiFiberTrajectoryFile; class CiftiMappableDataFile; class CiftiMappableConnectivityMatrixDataFile; class CiftiParcelLabelFile; class CiftiParcelSeriesFile; class CiftiParcelScalarFile; class CiftiScalarDataSeriesFile; class DisplayProperties; class DisplayPropertiesAnnotation; class DisplayPropertiesBorders; class DisplayPropertiesFiberOrientation; class DisplayPropertiesFoci; class DisplayPropertiesImages; class DisplayPropertiesLabels; class DisplayPropertiesSurface; class DisplayPropertiesVolume; class EventDataFileRead; class EventDataFileReload; class EventSpecFileReadDataFiles; class GapsAndMargins; class IdentificationManager; class ImageFile; class LabelFile; class MetricFile; class ModelChart; class ModelSurfaceMontage; class ModelVolume; class ModelWholeBrain; class PaletteFile; class RgbaFile; class SceneClassAssistant; class SceneFile; class SelectionManager; class SpecFile; class Surface; class SurfaceFile; class SurfaceProjectedItem; class VolumeFile; class Brain : public CaretObject, public EventListenerInterface, public SceneableInterface { public: Brain(); ~Brain(); private: Brain(const Brain&); Brain& operator=(const Brain&); public: int getNumberOfBrainStructures() const; void addBrainStructure(BrainStructure* brainStructure); BrainStructure* getBrainStructure(const int32_t indx); const BrainStructure* getBrainStructure(const int32_t indx) const; BrainStructure* getBrainStructure(StructureEnum::Enum structure, bool createIfNotFound); int32_t getNumberOfBorderFiles() const; BorderFile* getBorderFile(const int32_t indx); const BorderFile* getBorderFile(const int32_t indx) const; void getAllAnnotationFilesIncludingSceneAnnotationFile(std::vector& annotationFilesOut) const; void getAllAnnotationFilesExcludingSceneAnnotationFile(std::vector& annotationFilesOut) const; AnnotationFile* getSceneAnnotationFile(); const AnnotationFile* getSceneAnnotationFile() const; int32_t getNumberOfFociFiles() const; FociFile* getFociFile(const int32_t indx); const FociFile* getFociFile(const int32_t indx) const; const std::vector getAllImagesFiles() const; int32_t getNumberOfImageFiles() const; ImageFile* getImageFile(const int32_t indx); const ImageFile* getImageFile(const int32_t indx) const; PaletteFile* getPaletteFile(); const PaletteFile* getPaletteFile() const; int32_t getNumberOfSceneFiles() const; SceneFile* getSceneFile(const int32_t indx); const SceneFile* getSceneFile(const int32_t indx) const; const SpecFile* getSpecFile() const; SpecFile* getSpecFile(); Surface* getSurfaceWithName(const AString& surfaceFileName, const bool useAbsolutePath); const Surface* getPrimaryAnatomicalSurfaceForStructure(const StructureEnum::Enum structure) const; Surface* getPrimaryAnatomicalSurfaceNearestCoordinate(const float xyz[3], const float tolerance); std::vector getPrimaryAnatomicalSurfaces() const; std::vector getPrimaryAnatomicalSurfaceFiles() const; int32_t getNumberOfVolumeFiles() const; VolumeFile* getVolumeFile(const int32_t volumeFileIndex); const VolumeFile* getVolumeFile(const int32_t volumeFileIndex) const; void resetBrain(); void resetBrainKeepSceneFiles(); void receiveEvent(Event* event); AnnotationManager* getAnnotationManager(); const AnnotationManager* getAnnotationManager() const; ModelChart* getChartModel(); const ModelChart* getChartModel() const; ChartingDataManager* getChartingDataManager(); const ChartingDataManager* getChartingDataManager() const; void getAllCiftiMappableDataFiles(std::vector& allCiftiMappableDataFilesOut) const; int32_t getNumberOfConnectivityMatrixDenseFiles() const; CiftiConnectivityMatrixDenseFile* getConnectivityMatrixDenseFile(int32_t indx); const CiftiConnectivityMatrixDenseFile* getConnectivityMatrixDenseFile(int32_t indx) const; void getConnectivityMatrixDenseFiles(std::vector& connectivityDenseFilesOut) const; void getConnectivityMatrixDenseDynamicFiles(std::vector& connectivityDenseDynamicFilesOut) const; int32_t getNumberOfConnectivityDenseLabelFiles() const; CiftiBrainordinateLabelFile* getConnectivityDenseLabelFile(int32_t indx); const CiftiBrainordinateLabelFile* getConnectivityDenseLabelFile(int32_t indx) const; void getConnectivityDenseLabelFiles(std::vector& connectivityDenseLabelFilesOut) const; int32_t getNumberOfConnectivityMatrixDenseParcelFiles() const; CiftiConnectivityMatrixDenseParcelFile* getConnectivityMatrixDenseParcelFile(int32_t indx); const CiftiConnectivityMatrixDenseParcelFile* getConnectivityMatrixDenseParcelFile(int32_t indx) const; void getConnectivityMatrixDenseParcelFiles(std::vector& connectivityDenseParcelFilesOut) const; int32_t getNumberOfConnectivityDenseScalarFiles() const; CiftiBrainordinateScalarFile* getConnectivityDenseScalarFile(int32_t indx); const CiftiBrainordinateScalarFile* getConnectivityDenseScalarFile(int32_t indx) const; void getConnectivityDenseScalarFiles(std::vector& connectivityDenseScalarFilesOut) const; int32_t getNumberOfConnectivityParcelLabelFiles() const; CiftiParcelLabelFile* getConnectivityParcelLabelFile(int32_t indx); const CiftiParcelLabelFile* getConnectivityParcelLabelFile(int32_t indx) const; void getConnectivityParcelLabelFiles(std::vector& connectivityParcelLabelFilesOut) const; int32_t getNumberOfConnectivityParcelScalarFiles() const; CiftiParcelScalarFile* getConnectivityParcelScalarFile(int32_t indx); const CiftiParcelScalarFile* getConnectivityParcelScalarFile(int32_t indx) const; void getConnectivityParcelScalarFiles(std::vector& connectivityParcelScalarFilesOut) const; int32_t getNumberOfConnectivityScalarDataSeriesFiles() const; CiftiScalarDataSeriesFile* getConnectivityScalarDataSeriesFile(int32_t indx); const CiftiScalarDataSeriesFile* getConnectivityScalarDataSeriesFile(int32_t indx) const; void getConnectivityScalarDataSeriesFiles(std::vector& connectivityScalarDataSeriesFilesOut) const; int32_t getNumberOfConnectivityParcelSeriesFiles() const; CiftiParcelSeriesFile* getConnectivityParcelSeriesFile(int32_t indx); const CiftiParcelSeriesFile* getConnectivityParcelSeriesFile(int32_t indx) const; void getConnectivityParcelSeriesFiles(std::vector& connectivityParcelSeriesFilesOut) const; int32_t getNumberOfConnectivityFiberOrientationFiles() const; CiftiFiberOrientationFile* getConnectivityFiberOrientationFile(int32_t indx); const CiftiFiberOrientationFile* getConnectivityFiberOrientationFile(int32_t indx) const; void getConnectivityFiberOrientationFiles(std::vector& connectivityFiberOrientationFilesOut) const; bool getFiberOrientationSphericalSamplesVectors(std::vector& xVectors, std::vector& yVectors, std::vector& zVectors, FiberOrientation* &fiberOrientationOut, AString& errorMessageOut); int32_t getNumberOfConnectivityFiberTrajectoryFiles() const; CiftiFiberTrajectoryFile* getConnectivityFiberTrajectoryFile(int32_t indx); const CiftiFiberTrajectoryFile* getConnectivityFiberTrajectoryFile(int32_t indx) const; void getConnectivityFiberTrajectoryFiles(std::vector& ciftiFiberTrajectoryFilesOut) const; int32_t getNumberOfConnectivityMatrixParcelFiles() const; CiftiConnectivityMatrixParcelFile* getConnectivityMatrixParcelFile(int32_t indx); const CiftiConnectivityMatrixParcelFile* getConnectivityMatrixParcelFile(int32_t indx) const; void getConnectivityMatrixParcelFiles(std::vector& connectivityParcelFilesOut) const; int32_t getNumberOfConnectivityMatrixParcelDenseFiles() const; CiftiConnectivityMatrixParcelDenseFile* getConnectivityMatrixParcelDenseFile(int32_t indx); const CiftiConnectivityMatrixParcelDenseFile* getConnectivityMatrixParcelDenseFile(int32_t indx) const; void getConnectivityMatrixParcelDenseFiles(std::vector& connectivityParcelDenseFilesOut) const; int32_t getNumberOfConnectivityTimeSeriesFiles() const; void getAllCiftiConnectivityMatrixFiles(std::vector& allCiftiConnectivityMatrixFiles) const; int32_t getNumberOfConnectivityDataSeriesFiles() const; CiftiBrainordinateDataSeriesFile* getConnectivityDataSeriesFile(int32_t indx); const CiftiBrainordinateDataSeriesFile* getConnectivityDataSeriesFile(int32_t indx) const; void getConnectivityDataSeriesFiles(std::vector& connectivityDataSeriesFilesOut) const; void getAllChartableBrainordinateDataFiles(std::vector& chartableDataFilesOut) const; void getAllChartableLineSeriesDataFiles(std::vector& chartableDataFilesOut) const; void getAllChartableLineSeriesDataFilesForChartDataType(const ChartDataTypeEnum::Enum chartDataType, std::vector& chartableDataFilesOut) const; void getAllChartableBrainordinateDataFilesWithChartingEnabled(std::vector& chartableDataFilesOut) const; void getAllChartableMatrixDataFiles(std::vector& chartableDataFilesOut) const; void getAllChartableMatrixDataFilesForChartDataType(const ChartDataTypeEnum::Enum chartDataType, std::vector& chartableDataFilesOut) const; AString getCurrentDirectory() const; void setCurrentDirectory(const AString& currentDirectory); void convertDataFilePathNameToAbsolutePathName(CaretDataFile* caretDataFile) const; void getAllDataFiles(std::vector& allDataFilesOut, const bool includeSpecFile = false) const; void getAllDataFilesWithDataFileTypes(const std::vector& dataFileTypes, std::vector& caretDataFilesOut) const; void getAllDataFilesWithDataFileType(const DataFileTypeEnum::Enum dataFileType, std::vector& caretDataFilesOut) const; void getAllMappableDataFiles(std::vector& allCaretMappableDataFilesOut) const; void getAllMappableDataFileWithDataFileType(const DataFileTypeEnum::Enum dataFileType, std::vector& caretMappableDataFilesOut) const; void getAllMappableDataFileWithDataFileTypes(const std::vector& dataFileTypes, std::vector& caretMappableDataFilesOut) const; bool isFileValid(const CaretDataFile* caretDataFile) const; void getAllModifiedFiles(const std::vector& excludeTheseDataTypes, std::vector& modifiedDataFilesOut) const; void writeDataFile(CaretDataFile* caretDataFile); DisplayPropertiesAnnotation* getDisplayPropertiesAnnotation(); const DisplayPropertiesAnnotation* getDisplayPropertiesAnnotation() const; DisplayPropertiesBorders* getDisplayPropertiesBorders(); const DisplayPropertiesBorders* getDisplayPropertiesBorders() const; DisplayPropertiesFiberOrientation* getDisplayPropertiesFiberOrientation(); const DisplayPropertiesFiberOrientation* getDisplayPropertiesFiberOrientation() const; DisplayPropertiesFoci* getDisplayPropertiesFoci(); const DisplayPropertiesFoci* getDisplayPropertiesFoci() const; DisplayPropertiesVolume* getDisplayPropertiesVolume(); const DisplayPropertiesVolume* getDisplayPropertiesVolume() const; DisplayPropertiesSurface* getDisplayPropertiesSurface(); const DisplayPropertiesSurface* getDisplayPropertiesSurface() const; DisplayPropertiesImages* getDisplayPropertiesImages(); const DisplayPropertiesImages* getDisplayPropertiesImages() const; DisplayPropertiesLabels* getDisplayPropertiesLabels(); const DisplayPropertiesLabels* getDisplayPropertiesLabels() const; void copyDisplayProperties(const int32_t sourceTabIndex, const int32_t targetTabIndex); virtual SceneClass* saveToScene(const SceneAttributes* sceneAttributes, const AString& instanceName); virtual void restoreFromScene(const SceneAttributes* sceneAttributes, const SceneClass* sceneClass); IdentificationManager* getIdentificationManager(); SelectionManager* getSelectionManager(); BrainordinateRegionOfInterest* getBrainordinateHighlightRegionOfInterest(); const BrainordinateRegionOfInterest* getBrainordinateHighlightRegionOfInterest() const; void getCiftiShapeMap(CiftiBrainordinateScalarFile* &ciftiScalarShapeFileOut, int32_t& ciftiScalarhapeFileMapIndexOut, std::vector& ciftiScalarNotShapeFilesOut) const; GapsAndMargins* getGapsAndMargins(); const GapsAndMargins* getGapsAndMargins() const; private: /** * Reset the brain scene file mode */ enum ResetBrainKeepSceneFiles { /** Do NOT keep scene files when resetting the brain*/ RESET_BRAIN_KEEP_SCENE_FILES_NO, /** Do keep scene files when resetting the brain*/ RESET_BRAIN_KEEP_SCENE_FILES_YES }; /** * Reset the brain spec file mode */ enum ResetBrainKeepSpecFile { /** Do NOT keep spec files when resetting the brain*/ RESET_BRAIN_KEEP_SPEC_FILE_NO, /** Do keep spec files when resetting the brain*/ RESET_BRAIN_KEEP_SPEC_FILE_YES }; /** * Mode of file reading */ enum FileModeAddReadReload { /** Add the file */ FILE_MODE_ADD, /** Read the file */ FILE_MODE_READ, /** Reload the file */ FILE_MODE_RELOAD }; void addDataFile(CaretDataFile* caretDataFile); bool removeWithoutDeleteDataFile(const CaretDataFile* caretDataFile); bool removeWithoutDeleteDataFilePrivate(const CaretDataFile* caretDataFile); bool removeAndDeleteDataFile(CaretDataFile* caretDataFile); void loadFilesSelectedInSpecFile(EventSpecFileReadDataFiles* readSpecFileDataFilesEvent); void loadSpecFileFromScene(const SceneAttributes* sceneAttributes, SpecFile* specFile, const ResetBrainKeepSceneFiles keepSceneFile, const ResetBrainKeepSpecFile keepSpecFile); void resetBrain(const ResetBrainKeepSceneFiles keepSceneFiles, const ResetBrainKeepSpecFile keepSpecFile); void processReadDataFileEvent(EventDataFileRead* readDataFileEvent); void processReloadDataFileEvent(EventDataFileReload* reloadDataFileEvent); CaretDataFile* readDataFile(const DataFileTypeEnum::Enum dataFileType, const StructureEnum::Enum structure, const AString& dataFileName, const bool markDataFileAsModified); /** * Is the data file with the given name already loaded? * * @param loadedDataFiles * All files of a particular data type that are loaded. * @param fileName * File name for matching. */ template static bool dataFileWithNameIsLoaded(const std::vector& loadedDataFiles, const AString& fileName) { typename std::vector::const_iterator iter; for (iter = loadedDataFiles.begin(); iter != loadedDataFiles.end(); iter++) { const DFT* file = *iter; if (file->getFileName() == fileName) { return true; } } return false; } /** * If needed, update the name of a data file so that there are no two files * of the same type with the same name. If a duplicate needs to be created, * an underscore followed by a number is placed in the file name just * before the extension. * * @param loadedDataFiles * All files of a particular data type that are loaded. * @param newDataFile * New data file whose name may get changed if it duplicates * a currently loaded file. */ template void updateDataFileNameIfDuplicate(const std::vector& loadedDataFiles, DFT* newDataFile) { AString newFileName = newDataFile->getFileName(); const DataFileTypeEnum::Enum dataFileType = newDataFile->getDataFileType(); /* * Is there a file of the same name? */ if (dataFileWithNameIsLoaded(loadedDataFiles, newFileName)) { FileInformation fileInfo(newFileName); AString path; AString name; AString extension; fileInfo.getFileComponents(path, name, extension); /* * Modify the filename with a number (duplicate counter) * at the end of the file's name but before the extension's * dot. */ bool done = false; while ( ! done) { const int32_t duplicateCounter = getDuplicateFileNameCounterForFileType(dataFileType); AString versionName = (name + "_" + AString::number(duplicateCounter)); const AString versionFullName = FileInformation::assembleFileComponents(path, versionName, extension); if ( ! dataFileWithNameIsLoaded(loadedDataFiles, versionFullName)) { /* * Update name of file with version number, * Set the file modified (name changed), * and exit loop */ newDataFile->setFileName(versionFullName); newDataFile->setModified(); done = true; } } } } CaretDataFile* addReadOrReloadDataFile(const FileModeAddReadReload fileMode, CaretDataFile* caretDataFile, const DataFileTypeEnum::Enum dataFileType, const StructureEnum::Enum structure, const AString& dataFileName, const bool markDataFileAsModified); void updateAfterFilesAddedOrRemoved(); LabelFile* addReadOrReloadLabelFile(const FileModeAddReadReload fileMode, CaretDataFile* caretDataFile, const AString& filename, const StructureEnum::Enum structure, const bool markDataFileAsModified); MetricFile* addReadOrReloadMetricFile(const FileModeAddReadReload fileMode, CaretDataFile* caretDataFile, const AString& filename, const StructureEnum::Enum structure, const bool markDataFileAsModified); RgbaFile* addReadOrReloadRgbaFile(const FileModeAddReadReload fileMode, CaretDataFile* caretDataFile, const AString& filename, const StructureEnum::Enum structure, const bool markDataFileAsModified); Surface* addReadOrReloadSurfaceFile(const FileModeAddReadReload fileMode, CaretDataFile* caretDataFile, const AString& filename, const StructureEnum::Enum structure, const bool markDataFileAsModified); VolumeFile* addReadOrReloadVolumeFile(const FileModeAddReadReload fileMode, CaretDataFile* caretDataFile, const AString& filename); AnnotationFile* addReadOrReloadAnnotationFile(const FileModeAddReadReload fileMode, CaretDataFile* caretDataFile, const AString& filename); BorderFile* addReadOrReloadBorderFile(const FileModeAddReadReload fileMode, CaretDataFile* caretDataFile, const AString& filename); CiftiConnectivityMatrixDenseFile* addReadOrReloadConnectivityDenseFile(const FileModeAddReadReload fileMode, CaretDataFile* caretDataFile, const AString& filename); CiftiBrainordinateLabelFile* addReadOrReloadConnectivityDenseLabelFile(const FileModeAddReadReload fileMode, CaretDataFile* caretDataFile, const AString& filename); CiftiConnectivityMatrixDenseParcelFile* addReadOrReloadConnectivityMatrixDenseParcelFile(const FileModeAddReadReload fileMode, CaretDataFile* caretDataFile, const AString& filename); CiftiBrainordinateScalarFile* addReadOrReloadConnectivityDenseScalarFile(const FileModeAddReadReload fileMode, CaretDataFile* caretDataFile, const AString& filename); CiftiParcelLabelFile* addReadOrReloadConnectivityParcelLabelFile(const FileModeAddReadReload fileMode, CaretDataFile* caretDataFile, const AString& filename); CiftiParcelScalarFile* addReadOrReloadConnectivityParcelScalarFile(const FileModeAddReadReload fileMode, CaretDataFile* caretDataFile, const AString& filename); CiftiParcelSeriesFile* addReadOrReloadConnectivityParcelSeriesFile(const FileModeAddReadReload fileMode, CaretDataFile* caretDataFile, const AString& filename); CiftiScalarDataSeriesFile* addReadOrReloadConnectivityScalarDataSeriesFile(const FileModeAddReadReload fileMode, CaretDataFile* caretDataFile, const AString& filename); CiftiFiberOrientationFile* addReadOrReloadConnectivityFiberOrientationFile(const FileModeAddReadReload fileMode, CaretDataFile* caretDataFile, const AString& filename); CiftiFiberTrajectoryFile* addReadOrReloadConnectivityFiberTrajectoryFile(const FileModeAddReadReload fileMode, CaretDataFile* caretDataFile, const AString& filename); CiftiConnectivityMatrixParcelFile* addReadOrReloadConnectivityMatrixParcelFile(const FileModeAddReadReload fileMode, CaretDataFile* caretDataFile, const AString& filename); CiftiConnectivityMatrixParcelDenseFile* addReadOrReloadConnectivityMatrixParcelDenseFile(const FileModeAddReadReload fileMode, CaretDataFile* caretDataFile, const AString& filename); CiftiBrainordinateDataSeriesFile* addReadOrReloadConnectivityDataSeriesFile(const FileModeAddReadReload fileMode, CaretDataFile* caretDataFile, const AString& filename); FociFile* addReadOrReloadFociFile(const FileModeAddReadReload fileMode, CaretDataFile* caretDataFile, const AString& filename); ImageFile* addReadOrReloadImageFile(const FileModeAddReadReload fileMode, CaretDataFile* caretDataFile, const AString& filename); PaletteFile* addReadOrReloadPaletteFile(const FileModeAddReadReload fileMode, CaretDataFile* caretDataFile, const AString& filename); SceneFile* addReadOrReloadSceneFile(const FileModeAddReadReload fileMode, CaretDataFile* caretDataFile, const AString& filename); AString convertFilePathNameToAbsolutePathName(const AString& filename) const; void initializeDenseDataSeriesFile(CiftiBrainordinateDataSeriesFile* dataSeriesFile); void updateChartModel(); void updateVolumeSliceModel(); void updateWholeBrainModel(); void updateSurfaceMontageModel(); void updateFiberTrajectoryMatchingFiberOrientationFiles(); void validateCiftiMappableDataFile(const CiftiMappableDataFile* ciftiMapFile) const; int32_t getDuplicateFileNameCounterForFileType(const DataFileTypeEnum::Enum dataFileType); void resetDuplicateFileNameCounter(const bool preserveSceneFileCounter); std::vector m_brainStructures; std::vector m_annotationFiles; AnnotationFile* m_sceneAnnotationFile; std::vector m_borderFiles; std::vector m_fociFiles; std::vector m_imageFiles; std::vector m_sceneFiles; PaletteFile* m_paletteFile; std::vector m_connectivityMatrixDenseFiles; std::vector m_connectivityDenseLabelFiles; std::vector m_connectivityMatrixDenseParcelFiles; std::vector m_connectivityDenseScalarFiles; std::vector m_connectivityParcelLabelFiles; std::vector m_connectivityParcelSeriesFiles; std::vector m_connectivityParcelScalarFiles; std::vector m_connectivityScalarDataSeriesFiles; std::vector m_connectivityFiberOrientationFiles; std::vector m_connectivityFiberTrajectoryFiles; std::vector m_connectivityMatrixParcelFiles; std::vector m_connectivityMatrixParcelDenseFiles; std::vector m_connectivityDataSeriesFiles; std::vector m_nonModifiedFilesForRestoringScene; mutable AString m_currentDirectory; SpecFile* m_specFile; std::vector m_volumeFiles; ModelChart* m_modelChart; ModelVolume* m_volumeSliceModel; ModelWholeBrain* m_wholeBrainModel; ModelSurfaceMontage* m_surfaceMontageModel; ChartingDataManager* m_chartingDataManager; AnnotationManager* m_annotationManager; /** contains all display properties */ std::vector m_displayProperties; /** * Display properties for volume - DO NOT delete since this * is also in the displayProperties std::vector. */ DisplayPropertiesVolume* m_displayPropertiesVolume; /** * Display properties for surface - DO NOT delete since this * is also in the displayProperties std::vector. */ DisplayPropertiesSurface* m_displayPropertiesSurface; /** * Display properties for image - DO NOT delete since this * is also in the displayProperties std::vector. */ DisplayPropertiesImages* m_displayPropertiesImages; /** * Display properties for labels - DO NOT delete since this * is also in the displayProperties std::vector. */ DisplayPropertiesLabels* m_displayPropertiesLabels; /** * Display properties for annotations - DO NOT delete since this * is also in the displayProperties std::vector. */ DisplayPropertiesAnnotation* m_displayPropertiesAnnotation; /** * Display properties for borders - DO NOT delete since this * is also in the displayProperties std::vector. */ DisplayPropertiesBorders* m_displayPropertiesBorders; /** * Display properties for fiber orientation - DO NOT delete since this * is also in the displayProperties std::vector. */ DisplayPropertiesFiberOrientation* m_displayPropertiesFiberOrientation; /** * Display properties for foci - DO NOT delete since this * is also in the displayProperties std::vector. */ DisplayPropertiesFoci* m_displayPropertiesFoci; /** true when a spec file is being read */ bool m_isSpecFileBeingRead; SceneClassAssistant* m_sceneAssistant; /** Selection manager */ SelectionManager* m_selectionManager; /** Identification Manager */ IdentificationManager* m_identificationManager; /** The loader of fiber orientation samples */ FiberOrientationSamplesLoader* m_fiberOrientationSamplesLoader; /** Region of interest for highlighting brainordinates */ BrainordinateRegionOfInterest* m_brainordinateHighlightRegionOfInterest; std::map m_duplicateFileNameCounter; GapsAndMargins* m_gapsAndMargins; }; } // namespace #endif // __BRAIN_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Brain/BrainOpenGL.cxx000066400000000000000000001036311300200146000244270ustar00rootroot00000000000000 /*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 #define __BRAIN_OPENGL_DEFINE_H #include "BrainOpenGL.h" #undef __BRAIN_OPENGL_DEFINE_H #include "CaretAssert.h" #include "CaretLogger.h" #include "CaretPreferences.h" #include "DummyFontTextRenderer.h" #include "Model.h" #include "SessionManager.h" using namespace caret; /** * Constructor. * * @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. This instance * will take ownership of the text renderer and * delete it at the appropriate time. */ BrainOpenGL::BrainOpenGL(BrainOpenGLTextRenderInterface* textRenderer) { m_textRenderer = textRenderer; this->borderBeingDrawn = NULL; m_drawHighlightedEndPoints = false; } /** * Destructor. */ BrainOpenGL::~BrainOpenGL() { if (m_textRenderer != NULL) { delete m_textRenderer; m_textRenderer = NULL; } } /** * @return The active text renderer. */ BrainOpenGLTextRenderInterface* BrainOpenGL::getTextRenderer() { return m_textRenderer; } /** * 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. */ void BrainOpenGL::setTextRenderer(BrainOpenGLTextRenderInterface* textRenderer) { CaretAssert(textRenderer); if (m_textRenderer != NULL) { delete m_textRenderer; m_textRenderer = NULL; } m_textRenderer = textRenderer; } /** * Get the minimum and maximum values for the size of a point. * @param minPointSizeOut * Gets minimum size of point. * @param maxPointSizeOut * Gets maximum size of point. */ void BrainOpenGL::getMinMaxPointSize(float& minPointSizeOut, float& maxPointSizeOut) { minPointSizeOut = BrainOpenGL::s_minPointSize; maxPointSizeOut = BrainOpenGL::s_maxPointSize; } /** * Get the minimum and maximum values for the width of a line. * @param minLineWidthOut * Gets minimum line width. * @param maxLineWidthOut * Gets maximum line width. */ void BrainOpenGL::getMinMaxLineWidth(float& minLineWidthOut, float& maxLineWidthOut) { minLineWidthOut = BrainOpenGL::s_minLineWidth; maxLineWidthOut = BrainOpenGL::s_maxLineWidth; } /** * Set the border being drawn by the user. If not NULL then * subclasses should draw the border. * * @param borderBeingDrawn * Pointer to border that is being drawn. */ void BrainOpenGL::setBorderBeingDrawn(Border* borderBeingDrawn) { this->borderBeingDrawn = borderBeingDrawn; } /** * @return Should border end points be highlighted? */ bool BrainOpenGL::isDrawHighlightedEndPoints() const { return m_drawHighlightedEndPoints; } /** * Set border end points should be highlighted. */ void BrainOpenGL::setDrawHighlightedEndPoints(const bool drawHighlightedEndPoints) { m_drawHighlightedEndPoints = drawHighlightedEndPoints; } /** * Determine if the given version of OpenGL is supported at runtime. * OpenGL is continually updated and this method is used to test for * support of a given runtime version of OpenGL so that OpenGL functions * in the given version may be used. For example, if a function in OpenGL * 2.1 is called on a system that it is OpenGL 1.1, a crash will likely occur. * * The OpenGL runtime version is two or three numbers separated by a period, * possibly followed by a space and then text. * The first number is the major version, the second number is the minor * version, and the optional third number is the release of the major/minor * version. * * A version of of OpenGL is supported when it is less than or equal to * the runtime version of the OpenGL library. However, there may be exceptions as * OpenGL is deprecating functionality from OpenGL 1.x and 2.x in versions * 3.1 and later. At this time, an extension is provided by all vendors so * that OpenGL 1.x and 2.x is available in 3.1 and later. THIS MAY CHANGE. * This method does not check for this potential missing, deprecated capability. * * @param versionOfOpenGL * Version of OpenGL in the form X.Y.Z (eg: 1.1, 2.1, 3.0, 3.0.0, 3.1, etc.) * for which support is tested. * * @return true if the given version of OpenGL is less than the runtime version. */ bool BrainOpenGL::testForVersionOfOpenGLSupported(const AString& versionOfOpenGL) { AString desiredMajorVersion; AString desiredMinorVersion; getOpenGLMajorMinorVersions(versionOfOpenGL, desiredMajorVersion, desiredMinorVersion); /* * If desired MAJOR version is LESS THAN the runtime MAJOR version * then is it supported. */ if (desiredMajorVersion.toInt() < s_runtimeLibraryMajorVersionOfOpenGL.toInt()) { return true; } /* * Is the desired MAJOR version THE SAME as the runtime MAJOR version */ if (s_runtimeLibraryMajorVersionOfOpenGL.toInt() == desiredMajorVersion.toInt()) { /* * If the desired MINOR version is LESS THAN OR EQUAL to the * runtime MINOR version, then it is supported. */ if (desiredMinorVersion.toInt() <= s_runtimeLibraryMinorVersionOfOpenGL.toInt()) { return true; } } return false; } /** * Extract the major and minor versions from an OpenGL version string. * @param versionString * The OpenGL version string which is ".[.optionalVendorInfo]. * Usually strings such as 1.1, 1.2, 2.1, 3.0, etc. * @param majorVersionOut * Output containing the major version. * @param minorVersionOut * Output containing the minor version. */ void BrainOpenGL::getOpenGLMajorMinorVersions(const AString& versionString, AString& majorVersionOut, AString& minorVersionOut) { /* * Major and minor version are separated by a period. * Vendor information may follow and begin with whitespace. */ const QStringList sl = versionString.split(QRegExp("[\\s|\\.]")); if (sl.count() >= 2) { minorVersionOut = sl.at(1); } else { minorVersionOut = "1"; } if (sl.count() >= 1) { majorVersionOut = sl.at(0); } else { majorVersionOut = "1"; } } ///** // * Initialize the drawing mode using the most optimal drawing given // * the compile time and run time constraints. // */ //void //BrainOpenGL::initializeOpenGL() //{ // AString compileVersions = "OpenGL Header File Versions Supported: "; //#ifdef GL_VERSION_1_1 // compileVersions += " 1.1"; //#endif //#ifdef GL_VERSION_1_2 // compileVersions += " 1.2"; //#endif //#ifdef GL_VERSION_1_3 // compileVersions += " 1.3"; //#endif //#ifdef GL_VERSION_1_4 // compileVersions += " 1.4"; //#endif //#ifdef GL_VERSION_1_5 // compileVersions += " 1.5"; //#endif //#ifdef GL_VERSION_2_0 // compileVersions += " 2.0"; //#endif //#ifdef GL_VERSION_2_1 // compileVersions += " 2.1"; //#endif //#ifdef GL_VERSION_3_0 // compileVersions += " 3.0"; //#endif //#ifdef GL_VERSION_3_1 // compileVersions += " 3.1"; //#endif //#ifdef GL_VERSION_3_2 // compileVersions += " 3.2"; //#endif //#ifdef GL_VERSION_3_3 // compileVersions += " 3.3"; //#endif //#ifdef GL_VERSION_4_0 // compileVersions += " 4.0"; //#endif //#ifdef GL_VERSION_4_1 // compileVersions += " 4.1"; //#endif //#ifdef GL_VERSION_4_2 // compileVersions += " 4.2"; //#endif //#ifdef GL_VERSION_4_3 // compileVersions += " 4.3"; //#endif //#ifdef GL_VERSION_4_4 // compileVersions += " 4.4"; //#endif //#ifdef GL_VERSION_4_4 // compileVersions += " 4.4"; //#endif //#ifdef GL_VERSION_4_5 // compileVersions += " 4.5"; //#endif //#ifdef GL_VERSION_5_0 // compileVersions += " 5.0"; //#endif // //#ifdef GL_OES_VERSION_1_0 // compileVersions += " ES_1.0"; //#endif //#ifdef GL_ES_VERSION_2_0 // compileVersions += " ES_2.0"; //#endif //#ifdef GL_ES_VERSION_3_0 // compileVersions += " ES_3.0"; //#endif // // s_runtimeLibraryVersionOfOpenGL = QLatin1String(reinterpret_cast(glGetString(GL_VERSION))); // if (s_runtimeLibraryVersionOfOpenGL.isEmpty()) { // s_runtimeLibraryVersionOfOpenGL = "1.1"; // } // getOpenGLMajorMinorVersions(s_runtimeLibraryVersionOfOpenGL, // s_runtimeLibraryMajorVersionOfOpenGL, // s_runtimeLibraryMinorVersionOfOpenGL); // // // // // Note: The version string might be something like 1.2.4. std::atof() // // will get just the 1.2 which is okay. // // // const char* vendorStr = (char*)(glGetString(GL_VENDOR)); // const char* renderStr = (char*)(glGetString(GL_RENDERER)); // AString lineInfo = (compileVersions // + "\nOpenGL Runtime Version: " + s_runtimeLibraryVersionOfOpenGL // + "\nMajor Runtime Version: " + BrainOpenGL::s_runtimeLibraryMajorVersionOfOpenGL // + "\nMinor Runtime Version: " + BrainOpenGL::s_runtimeLibraryMinorVersionOfOpenGL // + "\nOpenGL Vendor: " + AString(vendorStr) // + "\nOpenGL Renderer: " + AString(renderStr)); // // lineInfo += "\n"; // lineInfo += ("\nFont Renderer: " + m_textRenderer->getName()); // lineInfo += "\n"; // //#ifdef GL_VERSION_2_0 // if (testForVersionOfOpenGLSupported("2.0")) { // GLfloat values[2]; // glGetFloatv (GL_ALIASED_LINE_WIDTH_RANGE, values); // const AString aliasedLineWidthRange = ("GL_ALIASED_LINE_WIDTH_RANGE value is " // + AString::fromNumbers(values, 2, ", ")); // // glGetFloatv (GL_SMOOTH_LINE_WIDTH_RANGE, values); // const AString smoothLineWidthRange = ("GL_SMOOTH_LINE_WIDTH_RANGE value is " // + AString::fromNumbers(values, 2, ", ")); // // glGetFloatv (GL_SMOOTH_LINE_WIDTH_GRANULARITY, values); // const AString smoothLineWidthGranularity = ("GL_SMOOTH_LINE_WIDTH_GRANULARITY value is " // + AString::number(values[0])); // // lineInfo += ("\n" + aliasedLineWidthRange // + "\n" + smoothLineWidthRange // + "\n" + smoothLineWidthGranularity); // } //#endif // GL_VERSION_2_0 ////#else // GL_VERSION_2_0 // GLfloat values[2]; // glGetFloatv (GL_LINE_WIDTH_RANGE, values); // const AString lineWidthRange = ("GL_LINE_WIDTH_RANGE value is " // + AString::fromNumbers(values, 2, ", ")); // // glGetFloatv (GL_LINE_WIDTH_GRANULARITY, values); // const AString lineWidthGranularity = ("GL_LINE_WIDTH_GRANULARITY value is " // + AString::number(values[0])); // lineInfo += ("\n" + lineWidthRange // + "\n" + lineWidthGranularity); ////#endif // GL_VERSION_2_0 // // float sizes[2]; // glGetFloatv(GL_POINT_SIZE_RANGE, sizes); // s_minPointSize = sizes[0]; // s_maxPointSize = sizes[1]; // glGetFloatv(GL_LINE_WIDTH_RANGE, sizes); // s_minLineWidth = sizes[0]; // s_maxLineWidth = sizes[1]; // // s_supportsDisplayLists = false; // s_supportsImmediateMode = false; // s_supportsVertexBuffers = false; // // GLint maximumNumberOfClipPlanes; // glGetIntegerv(GL_MAX_CLIP_PLANES, // & maximumNumberOfClipPlanes); // lineInfo += ("\n\nMaximum number of clipping planes is " // + AString::number(maximumNumberOfClipPlanes)); // // GLint redBits, greenBits, blueBits, alphaBits; // glGetIntegerv(GL_RED_BITS, &redBits); // glGetIntegerv(GL_GREEN_BITS, &greenBits); // glGetIntegerv(GL_BLUE_BITS, &blueBits); // glGetIntegerv(GL_ALPHA_BITS, &alphaBits); // lineInfo += ("\n\nBuffer bits red/green/blue/apha: (" // + AString::number(redBits) + ", " // + AString::number(greenBits) + ", " // + AString::number(blueBits) + ", " // + AString::number(alphaBits) + ")"); // // /* // * Get the OpenGL Extensions. // */ // bool haveARBCompatibility = false; // const QString extensionsString((char*)glGetString(GL_EXTENSIONS)); // const QStringList extensionsList = extensionsString.split(QChar(' ')); // QStringListIterator extensionsIterator(extensionsList); // AString extInfo = ("\n\nOpenGL Extensions:"); // while (extensionsIterator.hasNext()) { // const QString ext = extensionsIterator.next(); // extInfo += ("\n " + ext); // // if (ext == "GL_ARB_compatibility") { // haveARBCompatibility = true; // } // } // // if (testForVersionOfOpenGLSupported("3.1")) { // if (haveARBCompatibility == false) { // CaretLogSevere("OpenGL 3.1 or later and ARB compatibilty extensions not found.\n" // "OpenGL may fail."); // } // } // //#if BRAIN_OPENGL_INFO_SUPPORTS_IMMEDIATE // s_supportsImmediateMode = true; //#endif // BRAIN_OPENGL_INFO_SUPPORTS_IMMEDIATE // //#if BRAIN_OPENGL_INFO_SUPPORTS_DISPLAY_LISTS // s_supportsDisplayLists = true; //#endif // BRAIN_OPENGL_INFO_SUPPORTS_DISPLAY_LISTS // //#ifdef BRAIN_OPENGL_INFO_SUPPORTS_VERTEX_BUFFERS // s_supportsVertexBuffers = true; //#endif // BRAIN_OPENGL_INFO_SUPPORTS_VERTEX_BUFFERS // // lineInfo += ("\n\nBest Drawing Mode: " // + BrainOpenGL::getBestDrawingModeName()); // lineInfo += ("\nDisplay Lists Supported: " // + AString::fromBool(s_supportsDisplayLists)); // lineInfo += ("\nImmediate Mode Supported: " // + AString::fromBool(s_supportsImmediateMode)); // lineInfo += ("\nVertex Buffers Supported: " // + AString::fromBool(s_supportsVertexBuffers)); // // lineInfo += extInfo; // // CaretLogConfig(lineInfo); // // m_openGLInformation = lineInfo; // // /* // * Call to validate the draw mode selection logic. // */ // getBestDrawingMode(); //} /** * Initialize the drawing mode using the most optimal drawing given * the compile time and run time constraints. */ void BrainOpenGL::initializeOpenGL() { s_runtimeLibraryVersionOfOpenGL = QLatin1String(reinterpret_cast(glGetString(GL_VERSION))); if (s_runtimeLibraryVersionOfOpenGL.isEmpty()) { s_runtimeLibraryVersionOfOpenGL = "1.1"; } getOpenGLMajorMinorVersions(s_runtimeLibraryVersionOfOpenGL, s_runtimeLibraryMajorVersionOfOpenGL, s_runtimeLibraryMinorVersionOfOpenGL); float sizes[2]; glGetFloatv(GL_POINT_SIZE_RANGE, sizes); s_minPointSize = sizes[0]; s_maxPointSize = sizes[1]; glGetFloatv(GL_LINE_WIDTH_RANGE, sizes); s_minLineWidth = sizes[0]; s_maxLineWidth = sizes[1]; s_supportsDisplayLists = false; s_supportsImmediateMode = false; s_supportsVertexBuffers = false; /* * Get the OpenGL Extensions. */ bool haveARBCompatibility = false; const QString extensionsString((char*)glGetString(GL_EXTENSIONS)); const QStringList extensionsList = extensionsString.split(QChar(' ')); QStringListIterator extensionsIterator(extensionsList); m_openGLExtensionsInformation = ("\n\nOpenGL Extensions:"); while (extensionsIterator.hasNext()) { const QString ext = extensionsIterator.next(); m_openGLExtensionsInformation += ("\n " + ext); if (ext == "GL_ARB_compatibility") { haveARBCompatibility = true; } } if (testForVersionOfOpenGLSupported("3.1")) { if (haveARBCompatibility == false) { CaretLogSevere("OpenGL 3.1 or later and ARB compatibilty extensions not found.\n" "OpenGL may fail."); } } #if BRAIN_OPENGL_INFO_SUPPORTS_IMMEDIATE s_supportsImmediateMode = true; #endif // BRAIN_OPENGL_INFO_SUPPORTS_IMMEDIATE #if BRAIN_OPENGL_INFO_SUPPORTS_DISPLAY_LISTS s_supportsDisplayLists = true; #endif // BRAIN_OPENGL_INFO_SUPPORTS_DISPLAY_LISTS #ifdef BRAIN_OPENGL_INFO_SUPPORTS_VERTEX_BUFFERS s_supportsVertexBuffers = true; #endif // BRAIN_OPENGL_INFO_SUPPORTS_VERTEX_BUFFERS /* * Call to validate the draw mode selection logic. */ getBestDrawingMode(); } /** * @return String containing information about OpenGL. */ AString BrainOpenGL::getOpenGLInformation() { AString compileVersions = "OpenGL Header File Versions Supported: "; #ifdef GL_VERSION_1_1 compileVersions += " 1.1"; #endif #ifdef GL_VERSION_1_2 compileVersions += " 1.2"; #endif #ifdef GL_VERSION_1_3 compileVersions += " 1.3"; #endif #ifdef GL_VERSION_1_4 compileVersions += " 1.4"; #endif #ifdef GL_VERSION_1_5 compileVersions += " 1.5"; #endif #ifdef GL_VERSION_2_0 compileVersions += " 2.0"; #endif #ifdef GL_VERSION_2_1 compileVersions += " 2.1"; #endif #ifdef GL_VERSION_3_0 compileVersions += " 3.0"; #endif #ifdef GL_VERSION_3_1 compileVersions += " 3.1"; #endif #ifdef GL_VERSION_3_2 compileVersions += " 3.2"; #endif #ifdef GL_VERSION_3_3 compileVersions += " 3.3"; #endif #ifdef GL_VERSION_4_0 compileVersions += " 4.0"; #endif #ifdef GL_VERSION_4_1 compileVersions += " 4.1"; #endif #ifdef GL_VERSION_4_2 compileVersions += " 4.2"; #endif #ifdef GL_VERSION_4_3 compileVersions += " 4.3"; #endif #ifdef GL_VERSION_4_4 compileVersions += " 4.4"; #endif #ifdef GL_VERSION_4_4 compileVersions += " 4.4"; #endif #ifdef GL_VERSION_4_5 compileVersions += " 4.5"; #endif #ifdef GL_VERSION_5_0 compileVersions += " 5.0"; #endif #ifdef GL_OES_VERSION_1_0 compileVersions += " ES_1.0"; #endif #ifdef GL_ES_VERSION_2_0 compileVersions += " ES_2.0"; #endif #ifdef GL_ES_VERSION_3_0 compileVersions += " ES_3.0"; #endif // // Note: The version string might be something like 1.2.4. std::atof() // will get just the 1.2 which is okay. // const char* vendorStr = (char*)(glGetString(GL_VENDOR)); const char* renderStr = (char*)(glGetString(GL_RENDERER)); AString lineInfo = (compileVersions + "\nOpenGL Runtime Version: " + s_runtimeLibraryVersionOfOpenGL + "\nMajor Runtime Version: " + BrainOpenGL::s_runtimeLibraryMajorVersionOfOpenGL + "\nMinor Runtime Version: " + BrainOpenGL::s_runtimeLibraryMinorVersionOfOpenGL + "\nOpenGL Vendor: " + AString(vendorStr) + "\nOpenGL Renderer: " + AString(renderStr)); lineInfo += "\n"; lineInfo += ("\nFont Renderer: " + m_textRenderer->getName()); lineInfo += "\n"; #ifdef GL_VERSION_2_0 if (testForVersionOfOpenGLSupported("2.0")) { GLfloat values[2]; glGetFloatv (GL_ALIASED_LINE_WIDTH_RANGE, values); const AString aliasedLineWidthRange = ("GL_ALIASED_LINE_WIDTH_RANGE value is " + AString::fromNumbers(values, 2, ", ")); glGetFloatv (GL_SMOOTH_LINE_WIDTH_RANGE, values); const AString smoothLineWidthRange = ("GL_SMOOTH_LINE_WIDTH_RANGE value is " + AString::fromNumbers(values, 2, ", ")); glGetFloatv (GL_SMOOTH_LINE_WIDTH_GRANULARITY, values); const AString smoothLineWidthGranularity = ("GL_SMOOTH_LINE_WIDTH_GRANULARITY value is " + AString::number(values[0])); lineInfo += ("\n" + aliasedLineWidthRange + "\n" + smoothLineWidthRange + "\n" + smoothLineWidthGranularity); } #endif // GL_VERSION_2_0 //#else // GL_VERSION_2_0 GLfloat values[2]; glGetFloatv (GL_LINE_WIDTH_RANGE, values); const AString lineWidthRange = ("GL_LINE_WIDTH_RANGE value is " + AString::fromNumbers(values, 2, ", ")); glGetFloatv (GL_LINE_WIDTH_GRANULARITY, values); const AString lineWidthGranularity = ("GL_LINE_WIDTH_GRANULARITY value is " + AString::number(values[0])); lineInfo += ("\n" + lineWidthRange + "\n" + lineWidthGranularity); //#endif // GL_VERSION_2_0 GLint maximumNumberOfClipPlanes; glGetIntegerv(GL_MAX_CLIP_PLANES, & maximumNumberOfClipPlanes); lineInfo += ("\n\nMaximum number of clipping planes is " + AString::number(maximumNumberOfClipPlanes)); 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); lineInfo += ("\n\nMaximum Modelview Matrix Stack Depth " + QString::number(maxModelStackDepth)); lineInfo += ("\nMaximum Name Matrix Stack Depth " + QString::number(maxNameStackDepth)); lineInfo += ("\nMaximum Projection Matrix Stack Depth " + QString::number(maxProjStackDepth)); GLint redBits, greenBits, blueBits, alphaBits; glGetIntegerv(GL_RED_BITS, &redBits); glGetIntegerv(GL_GREEN_BITS, &greenBits); glGetIntegerv(GL_BLUE_BITS, &blueBits); glGetIntegerv(GL_ALPHA_BITS, &alphaBits); lineInfo += ("\n\nBuffer bits red/green/blue/apha: (" + AString::number(redBits) + ", " + AString::number(greenBits) + ", " + AString::number(blueBits) + ", " + AString::number(alphaBits) + ")"); lineInfo += ("\n\nBest Drawing Mode: " + BrainOpenGL::getBestDrawingModeName()); lineInfo += ("\nDisplay Lists Supported: " + AString::fromBool(s_supportsDisplayLists)); lineInfo += ("\nImmediate Mode Supported: " + AString::fromBool(s_supportsImmediateMode)); lineInfo += ("\nVertex Buffers Supported: " + AString::fromBool(s_supportsVertexBuffers)); lineInfo += "\n"; lineInfo += "\n"; lineInfo += "Note that State of OpenGL may be different when drawing objects.\n"; lineInfo += getStateOfOpenGL(); lineInfo += "\n"; lineInfo += m_openGLExtensionsInformation; return lineInfo; } /** * @return The best drawing mode given the limitations of * the compile and run-time systems. */ BrainOpenGL::DrawMode BrainOpenGL::getBestDrawingMode() { BrainOpenGL::DrawMode drawMode = BrainOpenGL::DRAW_MODE_INVALID; const OpenGLDrawingMethodEnum::Enum userPrefDrawMode = SessionManager::get()->getCaretPreferences()->getOpenDrawingMethod(); bool useVertexBuffersFlag = false; switch (userPrefDrawMode) { case OpenGLDrawingMethodEnum::DRAW_WITH_VERTEX_BUFFERS_OFF: break; case OpenGLDrawingMethodEnum::DRAW_WITH_VERTEX_BUFFERS_ON: if (s_supportsVertexBuffers) { useVertexBuffersFlag = true; } break; } if (s_supportsVertexBuffers && useVertexBuffersFlag) { drawMode = DRAW_MODE_VERTEX_BUFFERS; } else if (s_supportsDisplayLists) { drawMode = DRAW_MODE_DISPLAY_LISTS; } else if (s_supportsImmediateMode) { drawMode = DRAW_MODE_IMMEDIATE; } else if (s_supportsVertexBuffers) { drawMode = DRAW_MODE_VERTEX_BUFFERS; } else { CaretAssertMessage(0, "OpenGL does not appear to support any valid drawing modes, should never occur." " Or, OpenGL was not initialized (failed to call BrainOpenGL::initializeOpenGL())."); } return drawMode; } /** * @return Text string describing the drawing mode for shapes. */ QString BrainOpenGL::getBestDrawingModeName() { QString modeName = "Invalid"; switch (getBestDrawingMode()) { case BrainOpenGL::DRAW_MODE_DISPLAY_LISTS: modeName = "Display Lists"; break; case BrainOpenGL::DRAW_MODE_IMMEDIATE: modeName = "Immediate"; break; case BrainOpenGL::DRAW_MODE_INVALID: modeName = "Invalid"; break; case BrainOpenGL::DRAW_MODE_VERTEX_BUFFERS: modeName = "Vertex Buffers"; break; } return modeName; } /** * @return A string containing the state of OpenGL (depth testing, lighting, etc.) */ AString BrainOpenGL::getStateOfOpenGL() const { AString s; return s; } /** * Get the status of an OpenGL enum (enabled/disabled) as text in form * "name=true/false". * * @param enumName * Text name of the enumerated value. * @param enumValue * The OpenGl enum value. * @return * String with "enumName=true/false". */ AString BrainOpenGL::getOpenGLEnabledEnumAsText(const AString& enumName, const GLenum enumValue) const { /* * Reset error status */ glGetError(); GLboolean boolValue= glIsEnabled(enumValue); AString errorText; const GLenum errorCode = glGetError(); if (errorCode != GL_NO_ERROR) { const GLubyte* errorChars = gluErrorString(errorCode); if (errorChars != NULL) { errorText = ("ERROR = " + AString((char*)errorChars)); } } const AString s = (enumName + "=" + ((boolValue == GL_TRUE) ? "true" : "false") + " " + errorText); return s; } /** * Get the status of an OpenGL boolean as text in form * "name=true/false". * * @param enumName * Text name of the enumerated value. * @param enumValue * The OpenGl enum value. * @return * String with "enumName=true/false". */ AString BrainOpenGL::getOpenGLBooleanAsText(const AString& enumName, const GLenum enumValue) const { /* * Reset error status */ glGetError(); GLboolean boolValue = GL_FALSE; glGetBooleanv(enumValue, &boolValue); AString errorText; const GLenum errorCode = glGetError(); if (errorCode != GL_NO_ERROR) { const GLubyte* errorChars = gluErrorString(errorCode); if (errorChars != NULL) { errorText = ("ERROR = " + AString((char*)errorChars)); } } const AString s = (enumName + "=" + ((boolValue == GL_TRUE) ? "true" : "false") + " " + errorText); return s; } /** * Get the status of an OpenGL enum (enabled/disabled) as text in form * "name=true/false". * * @param enumName * Text name of the enumerated value. * @param enumValue * The OpenGl enum value. * @param numberOfValues * Number of floating pointer values associated with enumName. * @return * String with "enumName=(1, 2, 3,..)". */ AString BrainOpenGL::getOpenGLFloatAsText(const AString& enumName, const GLenum enumValue, const int32_t numberOfValues) const { /* * Reset error status */ glGetError(); AString valuesString; if (numberOfValues > 0) { std::vector valuesVector(numberOfValues, 0.0); GLfloat* values = &valuesVector[0]; glGetFloatv(enumValue, values); const GLenum errorCode = glGetError(); if (errorCode != GL_NO_ERROR) { const GLubyte* errorChars = gluErrorString(errorCode); if (errorChars != NULL) { valuesString = ("ERROR = " + AString((char*)errorChars)); } } else { valuesString = ("(" + AString::fromNumbers(valuesVector, ",") + ")"); } } const AString s = (enumName + "=" + valuesString); return s; } /** * Get the status of an OpenGL enum (enabled/disabled) as text in form * "name=true/false". * * @param enumName * Text name of the enumerated value. * @param enumValue * The OpenGl enum value. * @param enumValue * The OpenGl enum value. * @param numberOfValues * Number of floating pointer values associated with enumName. * @return * String with "enumName=(1, 2, 3,..)". */ AString BrainOpenGL::getOpenGLLightAsText(const AString& enumName, const GLenum lightEnum, const GLenum enumValue, const int32_t numberOfValues) const { /* * Reset error status */ glGetError(); AString valuesString; if (numberOfValues > 0) { std::vector valuesVector(numberOfValues, 0.0); GLfloat* values = &valuesVector[0]; glGetLightfv(lightEnum, enumValue, values); const GLenum errorCode = glGetError(); if (errorCode != GL_NO_ERROR) { const GLubyte* errorChars = gluErrorString(errorCode); if (errorChars != NULL) { valuesString = ("ERROR = " + AString((char*)errorChars)); } } else { valuesString = ("(" + AString::fromNumbers(valuesVector, ",") + ")"); } } const AString s = (enumName + "=" + valuesString); return s; } /** * Get the background color. * * @param backgroundColor * Output containing RGB components [0, 255]. */ void BrainOpenGL::getBackgroundColor(uint8_t backgroundColor[3]) const { backgroundColor[0] = m_backgroundColorByte[0]; backgroundColor[1] = m_backgroundColorByte[1]; backgroundColor[2] = m_backgroundColorByte[2]; } /** * Print information if there is an OpenGL error. */ void BrainOpenGL::testForOpenGLError(const AString& message) { testForOpenGLError(message, NULL, -1, -1); } /** * Print information if there is an OpenGL error. * * @param message * Message that is printed at beginning of text. * @param model * Model being drawn. * @param windowIndex * Index of the window. * @param tabIndex * Index of the tab. */ void BrainOpenGL::testForOpenGLError(const AString& message, const Model* model, const int32_t windowIndex, const int32_t tabIndex) { GLenum errorCode = glGetError(); if (errorCode != GL_NO_ERROR) { 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"); } if (windowIndex >= 0) { msg += ("In window number " + AString::number(windowIndex) + "\n"); } if (tabIndex >= 0) { msg += ("In tab number " + AString::number(tabIndex) + "\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); } } connectome-workbench-1.2.3+git41-gc4c6c90/src/Brain/BrainOpenGL.h000066400000000000000000000244071300200146000240570ustar00rootroot00000000000000 #ifndef __BRAIN_OPENGL_H__ #define __BRAIN_OPENGL_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 "CaretOpenGLInclude.h" #include "CaretObject.h" #undef BRAIN_OPENGL_INFO_SUPPORTS_DISPLAY_LISTS #undef BRAIN_OPENGL_INFO_SUPPORTS_IMMEDIATE #undef BRAIN_OPENGL_INFO_SUPPORTS_VERTEX_BUFFERS #define BRAIN_OPENGL_INFO_SUPPORTS_DISPLAY_LISTS 1 #define BRAIN_OPENGL_INFO_SUPPORTS_IMMEDIATE 1 //#define BRAIN_OPENGL_INFO_SUPPORTS_VERTEX_BUFFERS 1 //#ifdef GL_VERSION_1_1 //#define BRAIN_OPENGL_INFO_SUPPORTS_IMMEDIATE 1 //#define BRAIN_OPENGL_INFO_SUPPORTS_DISPLAY_LISTS 1 //#endif // GL_VERSION_1_1 // #ifdef GL_VERSION_2_1 #define BRAIN_OPENGL_INFO_SUPPORTS_VERTEX_BUFFERS 1 #endif // GL_VERSION_2_1 #ifdef GL_VERSION_3_0 #define BRAIN_OPENGL_INFO_SUPPORTS_VERTEX_BUFFERS 1 #endif // GL_VERSION_3_0 #ifdef GL_VERSION_4_0 #define BRAIN_OPENGL_INFO_SUPPORTS_VERTEX_BUFFERS 1 #endif // GL_VERSION_4_0 #include "CaretObject.h" namespace caret { class Border; class Brain; class BrainOpenGLTextRenderInterface; class BrainOpenGLTextureManager; class BrainOpenGLViewportContent; class Model; class SurfaceProjectedItem; /** * Performs drawing of graphics using OpenGL. */ class BrainOpenGL : public CaretObject { protected: BrainOpenGL(BrainOpenGLTextRenderInterface* textRenderer); public: /** Mode for how the shape is drawn, set to most optimal for compilation and runtime systems */ enum DrawMode { /** Draw using display lists */ DRAW_MODE_DISPLAY_LISTS, /** Draw using immediate mode */ DRAW_MODE_IMMEDIATE, /** Invalid */ DRAW_MODE_INVALID, /** Draw using vertex buffers */ DRAW_MODE_VERTEX_BUFFERS }; virtual ~BrainOpenGL(); /** * Initialize the OpenGL system. */ virtual void initializeOpenGL(); 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; /** * @return Half-size of the model window height. */ static float getModelViewingHalfWindowHeight() { return 90.0f; } void setBorderBeingDrawn(Border* borderBeingDrawn); bool isDrawHighlightedEndPoints() const; void setDrawHighlightedEndPoints(const bool drawHighlightedEndPoints); void getBackgroundColor(uint8_t backgroundColor[3]) const; static void getMinMaxPointSize(float& minPointSizeOut, float& maxPointSizeOut); static void getMinMaxLineWidth(float& minLineWidthOut, float& maxLineWidthOut); static bool testForVersionOfOpenGLSupported(const AString& versionOfOpenGL); static void testForOpenGLError(const AString& message); static void testForOpenGLError(const AString& message, const Model* model, const int32_t windowIndex, const int32_t tabIndex); static QString getBestDrawingModeName(); AString getOpenGLInformation(); static DrawMode getBestDrawingMode(); /** * @return True if display list drawing is supported. */ inline static bool isDisplayListsSupported() { return s_supportsDisplayLists; } /** * @return True if immediate mode drawing is supported. */ inline static bool isImmediateSupported() { return s_supportsImmediateMode; } /** * @return True if vertex buffer drawing is supported. */ inline static bool isVertexBuffersSupported() { return s_supportsVertexBuffers; } virtual AString getStateOfOpenGL() const; private: BrainOpenGL(const BrainOpenGL&); BrainOpenGL& operator=(const BrainOpenGL&); protected: AString getOpenGLEnabledEnumAsText(const AString& enumName, const GLenum enumValue) const; AString getOpenGLBooleanAsText(const AString& enumName, const GLenum enumValue) const; AString getOpenGLFloatAsText(const AString& enumName, const GLenum enumValue, const int32_t numberOfValues) const; AString getOpenGLLightAsText(const AString& enumName, const GLenum lightEnum, const GLenum enumValue, const int32_t numberOfValues) const; Border* borderBeingDrawn; bool m_drawHighlightedEndPoints; uint8_t m_foregroundColorByte[4]; float m_foregroundColorFloat[4]; uint8_t m_backgroundColorByte[4]; float m_backgroundColorFloat[4]; /** minimum point size */ static float s_minPointSize; /** maximum point size */ static float s_maxPointSize; /** minimum line width */ static float s_minLineWidth; /** maximum line width */ static float s_maxLineWidth; private: static void getOpenGLMajorMinorVersions(const AString& versionString, AString& majorVersionOut, AString& minorVersionOut); /** runtime library version of OpenGL */ static AString s_runtimeLibraryVersionOfOpenGL; /** runtime library major version number of OpenGL */ static AString s_runtimeLibraryMajorVersionOfOpenGL; /** runtime library minor version number of OpenGL */ static AString s_runtimeLibraryMinorVersionOfOpenGL; static bool s_supportsDisplayLists; static bool s_supportsImmediateMode; static bool s_supportsVertexBuffers; /** Optional text rendering (if not null) */ BrainOpenGLTextRenderInterface* m_textRenderer; AString m_openGLExtensionsInformation; }; #ifdef __BRAIN_OPENGL_DEFINE_H AString BrainOpenGL::s_runtimeLibraryVersionOfOpenGL = ""; AString BrainOpenGL::s_runtimeLibraryMajorVersionOfOpenGL = ""; AString BrainOpenGL::s_runtimeLibraryMinorVersionOfOpenGL = ""; float BrainOpenGL::s_minPointSize = 1.0f; float BrainOpenGL::s_maxPointSize = 10.0f; float BrainOpenGL::s_minLineWidth = 1.0f; float BrainOpenGL::s_maxLineWidth = 10.0f; bool BrainOpenGL::s_supportsDisplayLists = false; bool BrainOpenGL::s_supportsImmediateMode = false; bool BrainOpenGL::s_supportsVertexBuffers = false; #endif //__BRAIN_OPENGL_DEFINE_H } // namespace #endif // __BRAIN_OPENGL_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Brain/BrainOpenGLAnnotationDrawingFixedPipeline.cxx000066400000000000000000004266561300200146000324630ustar00rootroot00000000000000 /*LICENSE_START*/ /* * Copyright (C) 2015 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 #define __BRAIN_OPEN_G_L_ANNOTATION_DRAWING_FIXED_PIPELINE_DECLARE__ #include "BrainOpenGLAnnotationDrawingFixedPipeline.h" #undef __BRAIN_OPEN_G_L_ANNOTATION_DRAWING_FIXED_PIPELINE_DECLARE__ #include "AnnotationBox.h" #include "AnnotationColorBar.h" #include "AnnotationColorBarSection.h" #include "AnnotationColorBarNumericText.h" #include "AnnotationCoordinate.h" #include "AnnotationFile.h" #include "AnnotationImage.h" #include "AnnotationLine.h" #include "AnnotationManager.h" #include "AnnotationOval.h" #include "AnnotationPercentSizeText.h" #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" #include "CaretLogger.h" #include "DeveloperFlagsEnum.h" #include "DisplayPropertiesAnnotation.h" #include "EventBrowserTabGet.h" #include "EventManager.h" #include "IdentificationWithColor.h" #include "MathFunctions.h" #include "Matrix4x4.h" #include "SelectionManager.h" #include "SelectionItemAnnotation.h" #include "Surface.h" using namespace caret; /** * \class caret::BrainOpenGLAnnotationDrawingFixedPipeline * \brief OpenGL Fixed Pipeline drawing of Annotations. * \ingroup Brain */ /** * Constructor. * * @param brainOpenGLFixedPipeline * Fixed pipeline drawing. */ BrainOpenGLAnnotationDrawingFixedPipeline::BrainOpenGLAnnotationDrawingFixedPipeline(BrainOpenGLFixedPipeline* brainOpenGLFixedPipeline) : CaretObject(), m_brainOpenGLFixedPipeline(brainOpenGLFixedPipeline), m_inputs(NULL), m_volumeSpacePlaneValid(false), m_volumeSliceThickness(0.0) { CaretAssert(brainOpenGLFixedPipeline); m_dummyAnnotationFile = new AnnotationFile(); m_dummyAnnotationFile->setFileName("DummyFileForDrawing" + DataFileTypeEnum::toFileExtension(DataFileTypeEnum::ANNOTATION)); m_rotationHandleCircle = new BrainOpenGLShapeRing(20, 0.7, 1.0); } /** * Destructor. */ BrainOpenGLAnnotationDrawingFixedPipeline::~BrainOpenGLAnnotationDrawingFixedPipeline() { delete m_rotationHandleCircle; delete m_dummyAnnotationFile; } /** * Get a description of this object's content. * @return String describing this object's content. */ AString BrainOpenGLAnnotationDrawingFixedPipeline::toString() const { return "BrainOpenGLAnnotationDrawingFixedPipeline"; } /** * Convert a viewport coordinate to an OpenGL window coordinate. * * @param viewportXYZ * Viewport coordinate * @param openGLXYZOut * Output OpenGL coordinate. */ void BrainOpenGLAnnotationDrawingFixedPipeline::viewportToOpenGLWindowCoordinate(const float viewportXYZ[3], float openGLXYZOut[3]) const { GLint viewport[4]; glGetIntegerv(GL_VIEWPORT, viewport); openGLXYZOut[0] = viewport[2] * (viewportXYZ[0] / 100.0); openGLXYZOut[1] = viewport[3] * (viewportXYZ[1] / 100.0); openGLXYZOut[2] = (-viewportXYZ[2] / 100.0); } /** * Get the window coordinate for display of the annotation. * * @param coordinate * The annotation coordinate whose window coordinate is computed. * @param surfaceDisplayed * Surface that is displayed (may be NULL !) * @param windowXYZOut * Output containing the window coordinate. * @return * True if the window coordinate is valid, else false. */ bool BrainOpenGLAnnotationDrawingFixedPipeline::getAnnotationWindowCoordinate(const AnnotationCoordinate* coordinate, const AnnotationCoordinateSpaceEnum::Enum annotationCoordSpace, const Surface* surfaceDisplayed, float windowXYZOut[3]) const { float stereotaxicXYZ[3] = { 0.0, 0.0, 0.0 }; bool stereotaxicXYZValid = false; float windowXYZ[3] = { 0.0, 0.0, 0.0 }; bool windowXYZValid = false; float annotationXYZ[3]; coordinate->getXYZ(annotationXYZ); switch (annotationCoordSpace) { case AnnotationCoordinateSpaceEnum::STEREOTAXIC: stereotaxicXYZ[0] = annotationXYZ[0]; stereotaxicXYZ[1] = annotationXYZ[1]; stereotaxicXYZ[2] = annotationXYZ[2]; stereotaxicXYZValid = true; if (m_volumeSpacePlaneValid) { float xyzFloat[3] = { stereotaxicXYZ[0], stereotaxicXYZ[1], stereotaxicXYZ[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; float projectedPoint[3]; m_volumeSpacePlane.projectPointToPlane(stereotaxicXYZ, projectedPoint); } else { stereotaxicXYZValid = 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; int32_t annotationNumberOfNodes = -1; int32_t annotationNodeIndex = -1; float annotationOffsetLength = AnnotationCoordinate::getDefaultSurfaceOffsetLength(); AnnotationSurfaceOffsetVectorTypeEnum::Enum annotationOffsetVector = AnnotationSurfaceOffsetVectorTypeEnum::CENTROID_THRU_VERTEX; coordinate->getSurfaceSpace(annotationStructure, annotationNumberOfNodes, annotationNodeIndex, annotationOffsetLength, annotationOffsetVector); const StructureEnum::Enum surfaceStructure = surfaceDisplayed->getStructure(); const int32_t surfaceNumberOfNodes = surfaceDisplayed->getNumberOfNodes(); if ((surfaceStructure == annotationStructure) && (surfaceNumberOfNodes == annotationNumberOfNodes)) { if ((annotationNodeIndex >= 0) && (annotationNodeIndex < surfaceNumberOfNodes)) { float nodeXYZ[3]; surfaceDisplayed->getCoordinate(annotationNodeIndex, nodeXYZ); stereotaxicXYZ[0] = nodeXYZ[0]; stereotaxicXYZ[1] = nodeXYZ[1]; stereotaxicXYZ[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. * Using the centroid will not work as there is no * "z depth" so it will incorrectly offset in the XY-plane. * */ if (surfaceDisplayed->getSurfaceType() == SurfaceTypeEnum::FLAT) { annotationOffsetVector = AnnotationSurfaceOffsetVectorTypeEnum::SURACE_NORMAL; } switch (annotationOffsetVector) { case AnnotationSurfaceOffsetVectorTypeEnum::CENTROID_THRU_VERTEX: { 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); } break; case AnnotationSurfaceOffsetVectorTypeEnum::SURACE_NORMAL: const float* normalVector = surfaceDisplayed->getNormalVector(annotationNodeIndex); offsetUnitVector[0] = normalVector[0]; offsetUnitVector[1] = normalVector[1]; offsetUnitVector[2] = normalVector[2]; break; } stereotaxicXYZ[0] += (offsetUnitVector[0] * annotationOffsetLength); stereotaxicXYZ[1] += (offsetUnitVector[1] * annotationOffsetLength); stereotaxicXYZ[2] += (offsetUnitVector[2] * annotationOffsetLength); stereotaxicXYZValid = 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; // } 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; // } break; } if (stereotaxicXYZValid) { /* * Convert model space coordinates to window coordinates * as all annotations are drawn in window coordinates. */ if (convertModelToWindowCoordinate(stereotaxicXYZ, windowXYZ)) { stereotaxicXYZValid = false; windowXYZValid = true; } else { CaretLogSevere("Failed to convert model coordinates to window coordinates for annotation drawing."); } } if (windowXYZValid) { windowXYZOut[0] = windowXYZ[0]; windowXYZOut[1] = windowXYZ[1]; windowXYZOut[2] = windowXYZ[2]; } return windowXYZValid; } /** * Convert the given model coordinate into a window coordinate. * * @param modelXYZ * The model coordinate. * @param windowXYZOut * The window coordinate * @return true if the conversion is valid, else false. */ 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; } /** * Get the bounds for a two-dimensional shape annotation. * * @param annotation * The annotation whose bounds is computed. * @param windowXYZ * Window coordinates of the annotation. * @param bottomLeftOut * The bottom left corner of the annotation bounds. * @param bottomRightOut * The bottom right corner of the annotation bounds. * @param topRightOut * The top right corner of the annotation bounds. * @param topLeftOut * The top left corner of the annotation bounds. */ bool BrainOpenGLAnnotationDrawingFixedPipeline::getAnnotationTwoDimShapeBounds(const AnnotationTwoDimensionalShape* annotation2D, const float windowXYZ[3], float bottomLeftOut[3], float bottomRightOut[3], float topRightOut[3], float topLeftOut[3]) 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 */ const AnnotationText* textAnnotation = dynamic_cast(annotation2D); bool textFlag = false; if (textAnnotation != NULL) { if ( ! textAnnotation->getText().isEmpty()) { textFlag = true; } } bool boundsValid = false; if (textFlag) { m_brainOpenGLFixedPipeline->getTextRenderer()->getBoundsForTextAtViewportCoords(*textAnnotation, windowXYZ[0], windowXYZ[1], windowXYZ[2], viewportHeight, bottomLeftOut, bottomRightOut, topRightOut, topLeftOut); boundsValid = true; } else { boundsValid = annotation2D->getShapeBounds(viewportWidth, viewportHeight, windowXYZ, bottomLeftOut, bottomRightOut, topRightOut, topLeftOut); } return boundsValid; } /** * Apply rotation to the shape's bounding coordinates. * * @param rotationAngle * The rotation angle. * @param bottomLeftOut * The bottom left corner of the annotation bounds. * @param bottomRightOut * The bottom right corner of the annotation bounds. * @param topRightOut * The top right corner of the annotation bounds. * @param topLeftOut * The top left corner of the annotation bounds. */ void BrainOpenGLAnnotationDrawingFixedPipeline::applyRotationToShape(const float rotationAngle, const float rotationPoint[3], float bottomLeftOut[3], float bottomRightOut[3], float topRightOut[3], float topLeftOut[3]) const { if (rotationAngle != 0) { Matrix4x4 matrix; matrix.translate(-rotationPoint[0], -rotationPoint[1], -rotationPoint[2]); matrix.rotateZ(-rotationAngle); matrix.translate(rotationPoint[0], rotationPoint[1], rotationPoint[2]); matrix.multiplyPoint3(bottomLeftOut); matrix.multiplyPoint3(bottomRightOut); matrix.multiplyPoint3(topRightOut); matrix.multiplyPoint3(topLeftOut); } } /** * Draw model space annotations on the volume slice with the given plane. * * @param inputs * Inputs for drawing annotations. * @param plane * The volume slice's plane. * @param sliceThickness * Thickness of volume slice */ void BrainOpenGLAnnotationDrawingFixedPipeline::drawModelSpaceAnnotationsOnVolumeSlice(Inputs* inputs, const Plane& plane, const float sliceThickness) { CaretAssert(inputs); m_inputs = inputs; m_volumeSpacePlaneValid = false; if (plane.isValidPlane()) { m_volumeSpacePlane = plane; m_volumeSpacePlaneValid = true; std::vector colorBars; drawAnnotationsInternal(AnnotationCoordinateSpaceEnum::STEREOTAXIC, colorBars, NULL, sliceThickness); } m_volumeSpacePlaneValid = false; m_inputs = NULL; } /** * Draw the annotations in the given coordinate space. * * @param inputs * Inputs for drawing annotations. * @param drawingCoordinateSpace * Coordinate space of annotation that are drawn. * @param colorbars * Colorbars that will be drawn. * @param surfaceDisplayed * In not NULL, surface no which annotations are drawn. */ void BrainOpenGLAnnotationDrawingFixedPipeline::drawAnnotations(Inputs* inputs, const AnnotationCoordinateSpaceEnum::Enum drawingCoordinateSpace, std::vector& colorBars, const Surface* surfaceDisplayed) { CaretAssert(inputs); m_inputs = inputs; m_volumeSpacePlaneValid = false; const float sliceThickness = 0.0; drawAnnotationsInternal(drawingCoordinateSpace, colorBars, surfaceDisplayed, sliceThickness); m_inputs = NULL; } /** * Draw the annotations in the given coordinate space. * * @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 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, const Surface* surfaceDisplayed, const float sliceThickness) { if (m_inputs->m_brain == NULL) { return; } const DisplayPropertiesAnnotation* dpa = m_inputs->m_brain->getDisplayPropertiesAnnotation(); if ( ! dpa->isDisplayAnnotations()) { return; } m_volumeSliceThickness = sliceThickness; setSelectionBoxColor(); m_brainOpenGLFixedPipeline->checkForOpenGLError(NULL, ("At beginning of annotation drawing in space " + AnnotationCoordinateSpaceEnum::toName(drawingCoordinateSpace))); AnnotationManager* annotationManager = m_inputs->m_brain->getAnnotationManager(); /* * When user is drawing an annotation by dragging the mouse, it is always in window space. */ const Annotation* annotationBeingDrawn = ((drawingCoordinateSpace == AnnotationCoordinateSpaceEnum::WINDOW) ? annotationManager->getAnnotationBeingDrawnInWindow(m_inputs->m_windowIndex) : NULL); bool drawAnnotationsFromFilesFlag = true; switch (drawingCoordinateSpace) { case AnnotationCoordinateSpaceEnum::STEREOTAXIC: break; case AnnotationCoordinateSpaceEnum::PIXELS: CaretAssertMessage(0, "Never draw annotations in pixel space."); return; break; case AnnotationCoordinateSpaceEnum::SURFACE: break; case AnnotationCoordinateSpaceEnum::TAB: break; case AnnotationCoordinateSpaceEnum::WINDOW: switch (m_inputs->m_windowDrawingMode) { case Inputs::WINDOW_DRAWING_NO: drawAnnotationsFromFilesFlag = false; break; case Inputs::WINDOW_DRAWING_YES: break; } // if ( ! m_inputs->m_drawWindowAnnotationsFlag) { // drawAnnotationsFromFilesFlag = false; // } break; } /* * 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) { displayGroup = dpa->getDisplayGroupForTab(m_inputs->m_tabIndex); } SelectionItemAnnotation* annotationID = m_inputs->m_brain->getSelectionManager()->getAnnotationIdentification(); GLint savedShadeModel = 0; GLboolean savedLightingEnabled = GL_FALSE; startOpenGLForDrawing(&savedShadeModel, &savedLightingEnabled); /* * Check for a 'selection' type mode */ bool idReturnFlag = false; m_selectionModeFlag = false; m_selectionInfo.clear(); switch (m_inputs->m_drawingMode) { case BrainOpenGLFixedPipeline::MODE_DRAWING: break; case BrainOpenGLFixedPipeline::MODE_IDENTIFICATION: if (annotationID->isEnabledForSelection()) { m_selectionModeFlag = true; } else { idReturnFlag = true; } /* * Need flag shading for identification */ glShadeModel(GL_FLAT); break; case BrainOpenGLFixedPipeline::MODE_PROJECTION: idReturnFlag = true; break; } if (idReturnFlag) { endOpenGLForDrawing(savedShadeModel, savedLightingEnabled); return; } /* * When selecting, clear out all previous drawing * since we identify via colors in each pixel. */ if (m_selectionModeFlag) { glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); } std::vector allAnnotationFiles; if (drawAnnotationsFromFilesFlag) { m_inputs->m_brain->getAllAnnotationFilesIncludingSceneAnnotationFile(allAnnotationFiles); } m_brainOpenGLFixedPipeline->checkForOpenGLError(NULL, ("Before draw annotations loop in space: " + AnnotationCoordinateSpaceEnum::toName(drawingCoordinateSpace))); /* * Draw annotations from all files. * NOTE: iFile == numAnnFiles, the annotation colorbars are drawn */ const int32_t numAnnFiles = static_cast(allAnnotationFiles.size()); for (int32_t iFile = 0; iFile <= numAnnFiles; iFile++) { AnnotationFile* annotationFile = NULL; std::vector annotationsFromFile; if (iFile == numAnnFiles) { /* * Use the dummy file when drawing annotation color * bars since they do not belong to a file. */ annotationFile = m_dummyAnnotationFile; switch (drawingCoordinateSpace) { case AnnotationCoordinateSpaceEnum::STEREOTAXIC: break; case AnnotationCoordinateSpaceEnum::PIXELS: break; case AnnotationCoordinateSpaceEnum::SURFACE: break; case AnnotationCoordinateSpaceEnum::TAB: case AnnotationCoordinateSpaceEnum::WINDOW: { /* * Note: Positions are in percentages ranging [0.0, 100.0] */ float x = 14.0; float y = 4.0; bool firstColorBarFlag = true; if ( ! colorBars.empty()) { for (std::vector::iterator cbIter = colorBars.begin(); cbIter != colorBars.end(); cbIter++) { AnnotationColorBar* cb = *cbIter; if (cb->getCoordinateSpace() == drawingCoordinateSpace) { switch (cb->getPositionMode()) { case AnnotationColorBarPositionModeEnum::AUTOMATIC: { /* * Note: Y is incremented twice. Once to move colorbar * so that the colorbars bottom is just above the previous * colorbar or bottom of screen. Second time to move the * Y to the top of this annotation. */ const float halfHeight = cb->getHeight() / 2.0; if (firstColorBarFlag) { firstColorBarFlag = false; y = 4; if (halfHeight > y) { y = halfHeight; } } else { y += halfHeight; } float xyz[3]; cb->getCoordinate()->getXYZ(xyz); xyz[0] = x; xyz[1] = y; cb->getCoordinate()->setXYZ(xyz); y += halfHeight; } break; case AnnotationColorBarPositionModeEnum::MANUAL: break; } } } annotationsFromFile.insert(annotationsFromFile.end(), colorBars.begin(), colorBars.end()); } } break; } } else { CaretAssertVectorIndex(allAnnotationFiles, iFile); annotationFile = allAnnotationFiles[iFile]; annotationFile->getAllAnnotations(annotationsFromFile); } const int32_t annotationCount = static_cast(annotationsFromFile.size()); for (int32_t iAnn = 0; iAnn < annotationCount; iAnn++) { CaretAssertVectorIndex(annotationsFromFile, iAnn); Annotation* annotation = annotationsFromFile[iAnn]; 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; } } if ( ! drawItFlag) { continue; } AnnotationOneDimensionalShape* oneDimAnn = dynamic_cast(annotation); AnnotationTwoDimensionalShape* twoDimAnn = dynamic_cast(annotation); /* * Limit drawing of annotations to those in the * selected coordinate space. */ const AnnotationCoordinateSpaceEnum::Enum annotationCoordinateSpace = annotation->getCoordinateSpace(); if (annotationCoordinateSpace != drawingCoordinateSpace) { continue; } if (oneDimAnn != NULL) { } else if (twoDimAnn != NULL) { } else { CaretAssertMessage(0, ("Annotation is not derived from One or Two Dim Annotation classes: " + annotation->toString())); continue; } /* * Skip annotation in a different window */ if (annotationCoordinateSpace == AnnotationCoordinateSpaceEnum::WINDOW) { const int32_t annotationWindowIndex = annotation->getWindowIndex(); if ((annotationWindowIndex < 0) || (annotationWindowIndex >= BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_WINDOWS)) { CaretLogSevere("Annotation has invalid window index=" + AString::number(annotationWindowIndex) + " " + annotation->toString()); } if (m_inputs->m_windowIndex != annotationWindowIndex) { continue; } } /* * Skip annotations in a different tab */ if (annotationCoordinateSpace == AnnotationCoordinateSpaceEnum::TAB) { const int32_t annotationTabIndex = annotation->getTabIndex(); if ((annotationTabIndex < 0) || (annotationTabIndex >= BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS)) { CaretLogSevere("Annotation has invalid tab index=" + AString::number(annotationTabIndex) + " " + annotation->toString()); } if (m_inputs->m_tabIndex != annotationTabIndex) { continue; } } drawAnnotation(annotationFile, annotation, surfaceDisplayed); } } m_brainOpenGLFixedPipeline->checkForOpenGLError(NULL, ("After draw annotations loop in space: " + AnnotationCoordinateSpaceEnum::toName(drawingCoordinateSpace))); if (m_selectionModeFlag) { CaretAssert(annotationID); int32_t annotationIndex = -1; float depth = -1.0; m_brainOpenGLFixedPipeline->getIndexFromColorSelection(SelectionItemDataTypeEnum::ANNOTATION, m_brainOpenGLFixedPipeline->mouseX, m_brainOpenGLFixedPipeline->mouseY, annotationIndex, depth); if (annotationIndex >= 0) { if (annotationID != NULL) { CaretAssertVectorIndex(m_selectionInfo, annotationIndex); const SelectionInfo& selectionInfo = m_selectionInfo[annotationIndex]; if (annotationID->isOtherScreenDepthCloserToViewer(depth)) { annotationID->setAnnotation(selectionInfo.m_annotationFile, selectionInfo.m_annotation, selectionInfo.m_sizingHandle); annotationID->setBrain(m_inputs->m_brain); annotationID->setScreenXYZ(selectionInfo.m_windowXYZ); annotationID->setScreenDepth(depth); CaretLogFine("Selected Annotation: " + annotationID->toString()); } } } } else { /* * Annotation being drawn by the user. */ m_brainOpenGLFixedPipeline->checkForOpenGLError(NULL, "Start of annotation drawn by user model space."); if (annotationBeingDrawn != NULL) { if (annotationBeingDrawn->getType() == AnnotationTypeEnum::TEXT) { const AnnotationText* textAnn = dynamic_cast(annotationBeingDrawn); CaretAssert(textAnn); AnnotationBox box(AnnotationAttributesDefaultTypeEnum::NORMAL); box.applyCoordinatesSizeAndRotationFromOther(textAnn); box.applyColoringFromOther(textAnn); drawAnnotation(m_dummyAnnotationFile, &box, surfaceDisplayed); } else { drawAnnotation(m_dummyAnnotationFile, const_cast(annotationBeingDrawn), surfaceDisplayed); } } m_brainOpenGLFixedPipeline->checkForOpenGLError(NULL, "End of annotation drawn by user model space."); } endOpenGLForDrawing(savedShadeModel, savedLightingEnabled); m_brainOpenGLFixedPipeline->checkForOpenGLError(NULL, ("At end of annotation drawing in space " + AnnotationCoordinateSpaceEnum::toName(drawingCoordinateSpace))); } /** * Sets OpenGL attributes before drawing annotations. * * @param savedShadeModelOut * Current shading model is saved to this. * @param savedLightingEnabledOut * Current lighting enabled status is saved to this. */ void BrainOpenGLAnnotationDrawingFixedPipeline::startOpenGLForDrawing(GLint* savedShadeModelOut, GLboolean* savedLightingEnabledOut) { glGetIntegerv(GL_SHADE_MODEL, savedShadeModelOut); glGetBooleanv(GL_LIGHTING, savedLightingEnabledOut); glDisable(GL_LIGHTING); /* * When selection is performed, annoations in model space need * to be converted to window coordinates. However, when * selecting, all annotations are drawn in WINDOW SPACE * as a rectangle in a solid color so that the color selector * can be used. * * So, when selecting: * (1) Save the matrices and viewewport if drawing in * model space. * (2) Setup matrices for pixel (window) coordinates. * Since we are changing the matrices, they must * be saved as is done in (1). */ glGetDoublev(GL_MODELVIEW_MATRIX, m_modelSpaceModelMatrix); glGetDoublev(GL_PROJECTION_MATRIX, m_modelSpaceProjectionMatrix); glGetIntegerv(GL_VIEWPORT, m_modelSpaceViewport); GLdouble depthRange[2]; glGetDoublev(GL_DEPTH_RANGE, depthRange); /* * All drawing is in window space */ glMatrixMode(GL_PROJECTION); glPushMatrix(); glLoadIdentity(); glOrtho(0.0, m_modelSpaceViewport[2], 0.0, m_modelSpaceViewport[3], depthRange[0], depthRange[1]); glMatrixMode(GL_MODELVIEW); glPushMatrix(); glLoadIdentity(); /* * Enable anti-aliasing for lines and polygons */ m_brainOpenGLFixedPipeline->enableLineAntiAliasing(); } /** * Restores OpenGL attributes after drawing annotations. * * @param savedShadeModel * Saved shading model that is restored * @param savedLightingEnabled * Saved lighting enabled that is restored */ void BrainOpenGLAnnotationDrawingFixedPipeline::endOpenGLForDrawing(GLint savedShadeModel, GLboolean savedLightingEnabled) { /* * Disable anti-aliasing for lines */ m_brainOpenGLFixedPipeline->disableLineAntiAliasing(); if (savedLightingEnabled) { glEnable(GL_LIGHTING); } else { glDisable(GL_LIGHTING); } glShadeModel(savedShadeModel); /* * Restore the matrices since we were drawing in window space */ glMatrixMode(GL_PROJECTION); glPopMatrix(); glMatrixMode(GL_MODELVIEW); glPopMatrix(); } /** * Get color used for identification when drawing an annotation. * * @param identificationColorOut * Color components encoding identification. */ void BrainOpenGLAnnotationDrawingFixedPipeline::getIdentificationColor(uint8_t identificationColorOut[4]) { const int32_t annotationDrawnIndex = static_cast(m_selectionInfo.size()); m_brainOpenGLFixedPipeline->colorIdentification->addItem(identificationColorOut, SelectionItemDataTypeEnum::ANNOTATION, annotationDrawnIndex); } /** * Draw an annotation. * * @param annotationFile * File containing the annotation. * @param annotation * Annotation to draw. * @param surfaceDisplayed * Surface that is displayed (may be NULL). */ void BrainOpenGLAnnotationDrawingFixedPipeline::drawAnnotation(AnnotationFile* annotationFile, Annotation* annotation, const Surface* surfaceDisplayed) { CaretAssert(annotation); bool drawnFlag = false; switch (annotation->getType()) { case AnnotationTypeEnum::BOX: drawnFlag = drawBox(annotationFile, dynamic_cast(annotation), surfaceDisplayed); break; case AnnotationTypeEnum::COLOR_BAR: drawColorBar(annotationFile, dynamic_cast(annotation)); break; case AnnotationTypeEnum::IMAGE: drawnFlag = drawImage(annotationFile, dynamic_cast(annotation), surfaceDisplayed); break; case AnnotationTypeEnum::LINE: drawnFlag = drawLine(annotationFile, dynamic_cast(annotation), surfaceDisplayed); break; case AnnotationTypeEnum::OVAL: drawnFlag = drawOval(annotationFile, dynamic_cast(annotation), surfaceDisplayed); break; case AnnotationTypeEnum::TEXT: drawnFlag = drawText(annotationFile, dynamic_cast(annotation), surfaceDisplayed); break; } if (drawnFlag) { annotation->setDrawnInWindowStatus(m_inputs->m_windowIndex); } } /** * Draw an annotation box. * * @param annotationFile * File containing the annotation. * @param box * box to draw. * @param surfaceDisplayed * Surface that is displayed (may be NULL). * @return * True if the annotation was drawn while NOT selecting annotations. */ bool BrainOpenGLAnnotationDrawingFixedPipeline::drawBox(AnnotationFile* annotationFile, AnnotationBox* box, const Surface* surfaceDisplayed) { CaretAssert(box); CaretAssert(box->getType() == AnnotationTypeEnum::BOX); float windowXYZ[3]; if ( ! getAnnotationWindowCoordinate(box->getCoordinate(), box->getCoordinateSpace(), surfaceDisplayed, windowXYZ)) { return false; } float bottomLeft[3]; float bottomRight[3]; float topRight[3]; float topLeft[3]; if ( ! getAnnotationTwoDimShapeBounds(box, windowXYZ, 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 }; const float outlineWidth = box->getLineWidth(); const bool depthTestFlag = isDrawnWithDepthTesting(box); 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]; box->getBackgroundColorRGBA(backgroundRGBA); float foregroundRGBA[4]; box->getLineColorRGBA(foregroundRGBA); const bool drawBackgroundFlag = (backgroundRGBA[3] > 0.0); const bool drawForegroundFlag = (foregroundRGBA[3] > 0.0); const bool drawAnnotationFlag = (drawBackgroundFlag || drawForegroundFlag); bool drawnFlag = false; if (drawAnnotationFlag) { if (m_selectionModeFlag) { uint8_t selectionColorRGBA[4]; getIdentificationColor(selectionColorRGBA); if (drawBackgroundFlag) { /* * When selecting draw only background if it is enabled * since it is opaque and prevents "behind" annotations * from being selected */ BrainOpenGLPrimitiveDrawing::drawPolygon(coords, dummyNormals, 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); } m_selectionInfo.push_back(SelectionInfo(annotationFile, box, AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_NONE, selectionCenterXYZ)); } else { if (drawBackgroundFlag) { BrainOpenGLPrimitiveDrawing::drawPolygon(coords, dummyNormals, 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); } drawnFlag = true; } } if (box->isSelectedForEditing(m_inputs->m_windowIndex)) { drawAnnotationTwoDimSizingHandles(annotationFile, box, bottomLeft, bottomRight, topRight, topLeft, outlineWidth, box->getRotationAngle()); } } setDepthTestingStatus(savedDepthTestStatus); return drawnFlag; } /** * Draw an annotation box. * * @param annotationFile * File containing the annotation. * @param colorBar * Color bar to draw. */ void BrainOpenGLAnnotationDrawingFixedPipeline::drawColorBar(AnnotationFile* annotationFile, AnnotationColorBar* colorBar) { CaretAssert(colorBar); CaretAssert(colorBar->getType() == AnnotationTypeEnum::COLOR_BAR); float windowXYZ[3]; if ( ! getAnnotationWindowCoordinate(colorBar->getCoordinate(), colorBar->getCoordinateSpace(), NULL, windowXYZ)) { 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, 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 }; const float outlineWidth = colorBar->getLineWidth(); const bool depthTestFlag = isDrawnWithDepthTesting(colorBar); 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); if (m_selectionModeFlag) { uint8_t selectionColorRGBA[4]; getIdentificationColor(selectionColorRGBA); BrainOpenGLPrimitiveDrawing::drawPolygon(coords, dummyNormals, selectionColorRGBA); m_selectionInfo.push_back(SelectionInfo(annotationFile, colorBar, AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_NONE, selectionCenterXYZ)); } else { if (drawBackgroundFlag) { float bgBottomLeft[3]; float bgBottomRight[3]; float bgTopRight[3]; float bgTopLeft[3]; for (int32_t i = 0; i < 3; i++) { bgBottomLeft[i] = bottomLeft[i]; bgBottomRight[i] = bottomRight[i]; bgTopLeft[i] = topLeft[i]; bgTopRight[i] = topRight[i]; } expandBox(bgBottomLeft, bgBottomRight, bgTopRight, bgTopLeft, 2, 0); std::vector bgCoords; bgCoords.insert(bgCoords.end(), bgBottomLeft, bgBottomLeft + 3); bgCoords.insert(bgCoords.end(), bgBottomRight, bgBottomRight + 3); bgCoords.insert(bgCoords.end(), bgTopRight, bgTopRight + 3); bgCoords.insert(bgCoords.end(), bgTopLeft, bgTopLeft + 3); BrainOpenGLPrimitiveDrawing::drawPolygon(bgCoords, dummyNormals, backgroundRGBA); } /* * The user sets the total height of the colorbar and the * height of the text. * * There are three items in the colorbar from top to bottom: * (1) The numeric values * (2) The tick marks * (3) The palettes color sections */ const float totalHeightPercent = colorBar->getHeight(); float textHeightPercent = colorBar->getFontPercentViewportSize(); float ticksMarksHeightPercent = totalHeightPercent * 0.10; float sectionsHeightPercent = totalHeightPercent - (ticksMarksHeightPercent - textHeightPercent); if (sectionsHeightPercent <= 0.0) { sectionsHeightPercent = totalHeightPercent * 0.10; textHeightPercent = totalHeightPercent - sectionsHeightPercent; } const float viewportHeight = m_modelSpaceViewport[3]; /* * Text is aligned at the top of the characters */ const float totalHeightPixels = (viewportHeight * (totalHeightPercent / 100.0)); const float textOffsetFromTopPixels = 2; const float textHeightPixels = (viewportHeight * (textHeightPercent / 100.0)); const float tickMarksHeightPixels = (viewportHeight * (ticksMarksHeightPercent / 100.0)); const float sectionsHeightPixels = (totalHeightPixels - (textHeightPixels + textOffsetFromTopPixels + tickMarksHeightPixels)); const bool debugFlag = false; if (debugFlag) { std::cout << "Color bar heights (pixels) " << std::endl; std::cout << " Total: " << totalHeightPixels << std::endl; std::cout << " Text: " << textHeightPixels << std::endl; std::cout << " Text Offset: " << textOffsetFromTopPixels << std::endl; std::cout << " Ticks: " << tickMarksHeightPixels << std::endl; } drawColorBarSections(colorBar, bottomLeft, bottomRight, topRight, topLeft, sectionsHeightPixels); drawColorBarText(colorBar, bottomLeft, bottomRight, topRight, topLeft, textHeightPixels, textOffsetFromTopPixels); /* * If 'extendTickMarksIntoColorBarPixels' is greater than zero, * the tickmarks will extend down and into the colorbar */ const float extendTickMarksIntoColorBarPixels = tickMarksHeightPixels; const float tickMarksOffsetFromBotom = sectionsHeightPixels - extendTickMarksIntoColorBarPixels; const float totalTickMarksHeightPixels = tickMarksHeightPixels + extendTickMarksIntoColorBarPixels; drawColorBarTickMarks(colorBar, bottomLeft, bottomRight, topRight, topLeft, totalTickMarksHeightPixels, tickMarksOffsetFromBotom); } if (colorBar->isSelectedForEditing(m_inputs->m_windowIndex)) { drawAnnotationTwoDimSizingHandles(annotationFile, colorBar, bottomLeft, bottomRight, topRight, topLeft, outlineWidth, colorBar->getRotationAngle()); } setDepthTestingStatus(savedDepthTestStatus); } /** * Draw the color bar's tick marks * * @param colorBar * Colorbar whose tick marks are drawn. * @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 tickMarksHeightInPixels * Height of the tick marks in pixels. * @param offsetFromBottomInPixels * Offset of tick marks from the bottom */ void BrainOpenGLAnnotationDrawingFixedPipeline::drawColorBarTickMarks(const AnnotationColorBar* colorBar, const float bottomLeftIn[3], const float bottomRightIn[3], const float topRightIn[3], const float topLeftIn[3], const float tickMarksHeightInPixels, const float offsetFromBottomInPixels) { if ( ! colorBar->isShowTickMarksSelected()) { return; } if (tickMarksHeightInPixels < 1.0) { return; } float bottomLeft[3]; float bottomRight[3]; float topRight[3]; float topLeft[3]; for (int32_t i = 0; i < 3; i++) { bottomLeft[i] = bottomLeftIn[i]; bottomRight[i] = bottomRightIn[i]; topRight[i] = topRightIn[i]; topLeft[i] = topLeftIn[i]; } /* * Shrink the box that bounds the color bar in X so that start * and end ticks are at the ends of the color bar. */ const float tickThickness = 2.0; expandBox(bottomLeft, bottomRight, topRight, topLeft, (-tickThickness / 2.0), 0); float bottomToTopUnitVector[3]; MathFunctions::createUnitVector(bottomLeft, topLeft, bottomToTopUnitVector); const float xBottomLeft = (bottomLeft[0] + (bottomToTopUnitVector[0] * offsetFromBottomInPixels)); const float yBottomLeft = (bottomLeft[1] + (bottomToTopUnitVector[1] * offsetFromBottomInPixels)); const float xTopLeft = (xBottomLeft + (bottomToTopUnitVector[0] * tickMarksHeightInPixels)); const float yTopLeft = (yBottomLeft + (bottomToTopUnitVector[1] * tickMarksHeightInPixels)); const float xBottomRight = (bottomRight[0] + (bottomToTopUnitVector[0] * offsetFromBottomInPixels)); const float yBottomRight = (bottomRight[1] + (bottomToTopUnitVector[1] * offsetFromBottomInPixels)); 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; /* * 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)); } float leftToRightVector[3]; MathFunctions::subtractVectors(topRight, topLeft, leftToRightVector); const float dx = leftToRightVector[0]; const float dy = leftToRightVector[1]; /* * Tickmarks for numeric text */ const int32_t numText = colorBar->getNumberOfNumericText(); for (int32_t i = 0; i < numText; i++) { const AnnotationColorBarNumericText* numericText = colorBar->getNumericText(i); const float scalar = numericText->getScalar(); const float tickTopX = xTopLeft + (dx * scalar); const float tickTopY = yTopLeft + (dy * scalar); 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)); } /* * Draw lines and use polygon offset to ensure above color bar */ 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); } glDisable(GL_POLYGON_OFFSET_LINE); } /** * Draw the color bar's sections (the actual color bar) * * @param colorBar * Colorbar whose sections are drawn. * @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 sectionsHeightInPixels * Height for drawing the sections in pixels. */ void BrainOpenGLAnnotationDrawingFixedPipeline::drawColorBarSections(const AnnotationColorBar* colorBar, const float bottomLeft[3], const float bottomRight[3], const float* /* const float topRight[3]*/, 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]; MathFunctions::createUnitVector(bottomLeft, topLeft, bottomToTopUnitVector); const float xTopLeft = (bottomLeft[0] + (bottomToTopUnitVector[0] * sectionsHeightInPixels)); const float yTopLeft = (bottomLeft[1] + (bottomToTopUnitVector[1] * sectionsHeightInPixels)); const float xTopRight = (bottomRight[0] + (bottomToTopUnitVector[0] * sectionsHeightInPixels)); const float yTopRight = (bottomRight[1] + (bottomToTopUnitVector[1] * sectionsHeightInPixels)); const float dx = xTopRight - xTopLeft; const float dy = yTopRight - yTopLeft; const float xBottomLeft = bottomLeft[0]; const float yBottomLeft = bottomLeft[1]; float minScalar = 0.0; float maxScalar = 0.0; colorBar->getScalarMinimumAndMaximumValues(minScalar, maxScalar); const float dScalar = maxScalar - minScalar; const bool printDebugFlag = false; if (printDebugFlag) { std::cout << qPrintable(QString("minScalar %1, maxScalar %2, dScalar %3").arg(minScalar).arg(maxScalar).arg(dScalar)) << std::endl; } const float z = 0.0; const int32_t numSections = colorBar->getNumberOfSections(); for (int32_t iSect = 0; iSect < numSections; iSect++) { const AnnotationColorBarSection* section = colorBar->getSection(iSect); const float startScalar = section->getStartScalar(); const float endScalar = section->getEndScalar(); float startNormalizedScalar = startScalar; float endNormalizedScalar = endScalar; if (dScalar > 0.0) { startNormalizedScalar = (startScalar - minScalar) / dScalar; endNormalizedScalar = (endScalar - minScalar) / dScalar; } const float blX = xBottomLeft + (dx * startNormalizedScalar); const float blY = yBottomLeft + (dy * startNormalizedScalar); const float tlX = xTopLeft + (dx * startNormalizedScalar); 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); colorBarLines.push_back(ColorBarLine(lineCoords, section->getStartRGBA())); } else { const float brX = xBottomLeft + (dx * endNormalizedScalar); const float brY = yBottomLeft + (dy * endNormalizedScalar); 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); if (printDebugFlag) { const AString msg("Section (" + AString::number(startScalar) + ", " + AString::number(endScalar) + ") normalized (" + AString::number(startNormalizedScalar) + ", " + AString::number(endNormalizedScalar) + ") X-range (" + AString::number(blX) + ", " + AString::number(brX) + ") Y-range (" + AString::number(blY) + ", " + AString::number(tlY) + ")"); std::cout << qPrintable(msg) << std::endl; } } } /* * Lines need to be drawn OVER any quads (otherwise the quads * would obscure the lines */ 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); } glDisable(GL_POLYGON_OFFSET_LINE); } /** * Draw the color bar's text * * @param colorBar * Colorbar whose text is drawn. * @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 textHeightInPixels * Height of text in pixels. * @param offsetFromTopInPixels * Offset of text from the top of the colorbar viewport in pixels */ void BrainOpenGLAnnotationDrawingFixedPipeline::drawColorBarText(const AnnotationColorBar* colorBar, const float bottomLeft[3], const float bottomRight[3], const float topRight[3], const float topLeft[3], 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.setTextColor(CaretColorEnum::CUSTOM); float rgba[4]; colorBar->getTextColorRGBA(rgba); annText.setCustomTextColor(rgba); annText.setRotationAngle(colorBar->getRotationAngle()); float bottomToTopUnitVector[3]; MathFunctions::createUnitVector(bottomLeft, topLeft, bottomToTopUnitVector); const float distanceBottomToTop = MathFunctions::distance3D(topLeft, bottomLeft); const float distanceBottomToTopWithOffset = (distanceBottomToTop - offsetFromTopInPixels); const float xTopLeft = (bottomLeft[0] + (bottomToTopUnitVector[0] * distanceBottomToTopWithOffset)); const float yTopLeft = (bottomLeft[1] + (bottomToTopUnitVector[1] * distanceBottomToTopWithOffset)); float leftToRightVector[3]; MathFunctions::subtractVectors(topRight, topLeft, leftToRightVector); const float dx = leftToRightVector[0]; const float dy = leftToRightVector[1]; const float windowZ = (bottomLeft[2] + bottomRight[2] + topRight[2] + topLeft[2]) / 4.0; const int32_t numText = colorBar->getNumberOfNumericText(); for (int32_t i = 0; i < numText; i++) { const AnnotationColorBarNumericText* numericText = colorBar->getNumericText(i); const float scalar = numericText->getScalar(); float scalarOffset = 0.0; annText.setHorizontalAlignment(numericText->getHorizontalAlignment()); const float windowX = xTopLeft + (dx * (scalar + scalarOffset)); const float windowY = yTopLeft + (dy * (scalar + scalarOffset)); annText.setText(numericText->getNumericText()); m_brainOpenGLFixedPipeline->getTextRenderer()->drawTextAtViewportCoords(windowX, windowY, windowZ, annText); } } /** * Draw an annotation oval. * * @param annotationFile * File containing the annotation. * @param oval * Annotation oval to draw. * @param surfaceDisplayed * Surface that is displayed (may be NULL). * @return * True if the annotation was drawn while NOT selecting annotations. */ bool BrainOpenGLAnnotationDrawingFixedPipeline::drawOval(AnnotationFile* annotationFile, AnnotationOval* oval, const Surface* surfaceDisplayed) { CaretAssert(oval); CaretAssert(oval->getType() == AnnotationTypeEnum::OVAL); float windowXYZ[3]; if ( ! getAnnotationWindowCoordinate(oval->getCoordinate(), oval->getCoordinateSpace(), surfaceDisplayed, windowXYZ)) { return false; } float bottomLeft[3]; float bottomRight[3]; float topRight[3]; float topLeft[3]; if ( ! getAnnotationTwoDimShapeBounds(oval, windowXYZ, 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 rotationAngle = oval->getRotationAngle(); const float outlineWidth = oval->getLineWidth(); 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 }; const bool depthTestFlag = isDrawnWithDepthTesting(oval); const bool savedDepthTestStatus = setDepthTestingStatus(depthTestFlag); uint8_t backgroundRGBA[4]; oval->getBackgroundColorRGBA(backgroundRGBA); uint8_t foregroundRGBA[4]; oval->getLineColorRGBA(foregroundRGBA); const bool drawBackgroundFlag = (backgroundRGBA[3] > 0.0); const bool drawForegroundFlag = (foregroundRGBA[3] > 0.0); const bool drawAnnotationFlag = (drawBackgroundFlag || drawForegroundFlag); bool drawnFlag = false; if (drawAnnotationFlag) { glPushMatrix(); glTranslatef(windowXYZ[0], windowXYZ[1], windowXYZ[2]); if (rotationAngle != 0.0) { glRotatef(-rotationAngle, 0.0, 0.0, 1.0); } if (m_selectionModeFlag) { uint8_t selectionColorRGBA[4]; getIdentificationColor(selectionColorRGBA); if (drawBackgroundFlag) { /* * When selecting draw only background if it is enabled * since it is opaque and prevents "behind" annotations * from being selected */ m_brainOpenGLFixedPipeline->drawEllipseFilled(selectionColorRGBA, majorAxis, minorAxis); } 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); } m_selectionInfo.push_back(SelectionInfo(annotationFile, oval, AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_NONE, selectionCenterXYZ)); } else { if (drawBackgroundFlag) { m_brainOpenGLFixedPipeline->drawEllipseFilled(backgroundRGBA, majorAxis, minorAxis); drawnFlag = true; } if (drawForegroundFlag) { m_brainOpenGLFixedPipeline->drawEllipseOutline(foregroundRGBA, majorAxis, minorAxis, outlineWidth); drawnFlag = true; } } glPopMatrix(); if (oval->isSelectedForEditing(m_inputs->m_windowIndex)) { drawAnnotationTwoDimSizingHandles(annotationFile, oval, bottomLeft, bottomRight, topRight, topLeft, outlineWidth, oval->getRotationAngle()); } } setDepthTestingStatus(savedDepthTestStatus); return drawnFlag; } /** * Get coordinate for drawing a line that connects text to brainordinate. * * @param text * The text annotation. * @param surfaceDisplayed * Surface for text * @param lineCoordinatesOut * Output with coordinates for drawing line. Will be EMPTY if line is not drawn. */ void BrainOpenGLAnnotationDrawingFixedPipeline::getTextLineToBrainordinateLineCoordinates(const AnnotationText* text, const Surface* surfaceDisplayed, const float bottomLeft[3], const float bottomRight[3], const float topRight[3], const float topLeft[3], std::vector& lineCoordinatesOut) const { lineCoordinatesOut.clear(); if (text->isConnectToBrainordinateValid()) { bool showLineFlag = false; bool showArrowFlag = false; if (text->getCoordinateSpace() == AnnotationCoordinateSpaceEnum::SURFACE) { if (surfaceDisplayed != NULL) { switch (text->getConnectToBrainordinate()) { case AnnotationTextConnectTypeEnum::ANNOTATION_TEXT_CONNECT_NONE: break; case AnnotationTextConnectTypeEnum::ANNOTATION_TEXT_CONNECT_ARROW: showArrowFlag = true; break; case AnnotationTextConnectTypeEnum::ANNOTATION_TEXT_CONNECT_LINE: showLineFlag = true; break; } } } if (showLineFlag || showArrowFlag) { const AnnotationCoordinate* coord = text->getCoordinate(); StructureEnum::Enum structure = StructureEnum::INVALID; int32_t numNodes = 0; int32_t nodeIndex = 0; float offset = 0.0; AnnotationSurfaceOffsetVectorTypeEnum::Enum offsetVector = AnnotationSurfaceOffsetVectorTypeEnum::CENTROID_THRU_VERTEX; coord->getSurfaceSpace(structure, numNodes, nodeIndex, offset, offsetVector); if (offset > 0.0) { AnnotationCoordinate noOffsetCoord = *coord; noOffsetCoord.setSurfaceSpace(structure, numNodes, nodeIndex, 0.0, AnnotationSurfaceOffsetVectorTypeEnum::CENTROID_THRU_VERTEX); float brainordinateXYZ[3]; if (getAnnotationWindowCoordinate(&noOffsetCoord, text->getCoordinateSpace(), surfaceDisplayed, brainordinateXYZ)) { float windowXYZ[3]; if (getAnnotationWindowCoordinate(coord, text->getCoordinateSpace(), surfaceDisplayed, windowXYZ)) { const bool clipAtBoxFlag = true; if (clipAtBoxFlag) { clipLineAtTextBox(bottomLeft, bottomRight, topRight, topLeft, brainordinateXYZ, windowXYZ); } createLineCoordinates(windowXYZ, brainordinateXYZ, text->getLineWidth(), false, showArrowFlag, lineCoordinatesOut); } } } } } } /* * Clip the line that connects the text to the surface so that the line * does not enter a 'box' that encloses the text. * * @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 startXYZ * XYZ of where line attaches to surface * @param endXYZ * XYZ of where line attaches to text and may be changed to avoid * overlapping the text. */ void BrainOpenGLAnnotationDrawingFixedPipeline::clipLineAtTextBox(const float bottomLeft[3], const float bottomRight[3], const float topRight[3], const float topLeft[3], const float startXYZ[3], float endXYZ[3]) const { const float tol = 0.01; float clippedXYZ[3] = { 0.0, 0.0, 0.0 }; float clippedDistance = std::numeric_limits::max(); bool clippedValid = false; for (int32_t i = 0; i < 4; i++) { float* p1 = NULL; float* p2 = NULL; switch (i) { case 0: p1 = const_cast(bottomLeft); p2 = const_cast(bottomRight); break; case 1: p1 = const_cast(bottomRight); p2 = const_cast(topRight); break; case 2: p1 = const_cast(topRight); p2 = const_cast(topLeft); break; case 3: p1 = const_cast(topLeft); p2 = const_cast(bottomLeft); break; } float intersection[3] = { 0.0, 0.0, 0.0 }; const bool yesFlag = MathFunctions::lineIntersection2D(p1, p2, startXYZ, endXYZ, tol, intersection); if (yesFlag) { const float dist = MathFunctions::distance3D(startXYZ, intersection); if ( ( ! clippedValid) || (dist < clippedDistance)) { clippedDistance = dist; clippedValid = true; clippedXYZ[0] = intersection[0]; clippedXYZ[1] = intersection[1]; clippedXYZ[2] = intersection[2]; } } } if (clippedValid) { endXYZ[0] = clippedXYZ[0]; endXYZ[1] = clippedXYZ[1]; endXYZ[2] = clippedXYZ[2]; } } /** * Draw an annotation text. * * @param annotationFile * File containing the annotation. * @param text * Annotation text to draw. * @param surfaceDisplayed * Surface that is displayed (may be NULL). * @return * True if the annotation was drawn while NOT selecting annotations. */ bool BrainOpenGLAnnotationDrawingFixedPipeline::drawText(AnnotationFile* annotationFile, AnnotationText* text, const Surface* surfaceDisplayed) { DisplayPropertiesAnnotation* dpa = m_inputs->m_brain->getDisplayPropertiesAnnotation(); if ( ! dpa->isDisplayTextAnnotations()) { return false; } CaretAssert(text); CaretAssert(text->getType() == AnnotationTypeEnum::TEXT); float windowXYZ[3]; if ( ! getAnnotationWindowCoordinate(text->getCoordinate(), text->getCoordinateSpace(), surfaceDisplayed, windowXYZ)) { return false; } float bottomLeft[3]; float bottomRight[3]; float topRight[3]; float topLeft[3]; /* * The fonts are sized using either the height * of the OpenGL viewport or the height of the * TAB viewport. During a single surface view, * they will be the same but different in a * surface montage view if there are two or more * rows. */ AnnotationPercentSizeText* percentSizeText = NULL; 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], 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 }; const bool depthTestFlag = isDrawnWithDepthTesting(text); const bool savedDepthTestStatus = setDepthTestingStatus(depthTestFlag); float backgroundRGBA[4]; text->getBackgroundColorRGBA(backgroundRGBA); uint8_t foregroundRGBA[4]; text->getLineColorRGBA(foregroundRGBA); uint8_t textColorRGBA[4]; text->getTextColorRGBA(textColorRGBA); const bool drawTextFlag = (textColorRGBA[3] > 0.0); const bool drawBackgroundFlag = (backgroundRGBA[3] > 0.0); const bool drawAnnotationFlag = (drawBackgroundFlag || drawTextFlag); bool drawnFlag = false; if (drawAnnotationFlag) { if (m_selectionModeFlag) { uint8_t selectionColorRGBA[4]; getIdentificationColor(selectionColorRGBA); BrainOpenGLPrimitiveDrawing::drawPolygon(coords, dummyNormals, selectionColorRGBA); m_selectionInfo.push_back(SelectionInfo(annotationFile, text, AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_NONE, selectionCenterXYZ)); } else { std::vector connectLineCoordinates; getTextLineToBrainordinateLineCoordinates(text, surfaceDisplayed, bottomLeft, bottomRight, topRight, topLeft, connectLineCoordinates); if ( ! connectLineCoordinates.empty()) { BrainOpenGLPrimitiveDrawing::drawLines(connectLineCoordinates, textColorRGBA, text->getLineWidth()); } if (drawBackgroundFlag) { BrainOpenGLPrimitiveDrawing::drawPolygon(coords, dummyNormals, backgroundRGBA); } if (drawTextFlag) { const bool debugFlag = false; if (debugFlag) { if (text->getCoordinateSpace() == AnnotationCoordinateSpaceEnum::STEREOTAXIC) { 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, ",")); std::cout << qPrintable(msg) << std::endl; } } if (depthTestFlag) { m_brainOpenGLFixedPipeline->getTextRenderer()->drawTextAtViewportCoords(windowXYZ[0], windowXYZ[1], windowXYZ[2], *text); drawnFlag = true; } else { if (text->getText().isEmpty()) { /* * Text is empty when user is dragging mouse to create a * text region. In this case, use the bounds of the * two-dim shape. */ float bl[3]; 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); if (boxCoords.size() == 12) { BrainOpenGLPrimitiveDrawing::drawLineLoop(boxCoords, foregroundRGBA, text->getLineWidth()); } } else { m_brainOpenGLFixedPipeline->getTextRenderer()->drawTextAtViewportCoords(windowXYZ[0], windowXYZ[1], *text); drawnFlag = true; } } setDepthTestingStatus(depthTestFlag); } } if (text->isSelectedForEditing(m_inputs->m_windowIndex)) { const float outlineWidth = 2.0; drawAnnotationTwoDimSizingHandles(annotationFile, text, bottomLeft, bottomRight, topRight, topLeft, outlineWidth, text->getRotationAngle()); } } if (percentSizeText != NULL) { if (savedFontPercentViewportHeight > 0.0) { percentSizeText->setFontPercentViewportSize(savedFontPercentViewportHeight); if ( ! modifiedStatus) { percentSizeText->clearModified(); } } } setDepthTestingStatus(savedDepthTestStatus); return drawnFlag; } /** * Draw an annotation image. * * @param annotationFile * File containing the annotation. * @param image * Annotation image to draw. * @param surfaceDisplayed * Surface that is displayed (may be NULL). * @return * True if the annotation was drawn while NOT selecting annotations. */ bool BrainOpenGLAnnotationDrawingFixedPipeline::drawImage(AnnotationFile* annotationFile, AnnotationImage* image, const Surface* surfaceDisplayed) { const uint8_t* imageRgbaBytes = image->getImageBytesRGBA(); const int32_t imageWidth = image->getImageWidth(); const int32_t imageHeight = image->getImageHeight(); if ((imageWidth > 0) && (imageHeight > 0) && (imageRgbaBytes != NULL)) { } else { CaretLogWarning("Attempt to draw invalid image annotation."); } float windowXYZ[3]; if ( ! getAnnotationWindowCoordinate(image->getCoordinate(), image->getCoordinateSpace(), surfaceDisplayed, windowXYZ)) { return false; } float bottomLeft[3]; float bottomRight[3]; float topRight[3]; float topLeft[3]; if ( ! getAnnotationTwoDimShapeBounds(image, windowXYZ, 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 }; const bool depthTestFlag = isDrawnWithDepthTesting(image); const bool savedDepthTestStatus = setDepthTestingStatus(depthTestFlag); const bool drawAnnotationFlag = true; float foregroundRGBA[4]; image->getLineColorRGBA(foregroundRGBA); const bool drawForegroundFlag = (foregroundRGBA[3] > 0.0); bool drawnFlag = false; if (drawAnnotationFlag) { if (m_selectionModeFlag) { uint8_t selectionColorRGBA[4]; getIdentificationColor(selectionColorRGBA); BrainOpenGLPrimitiveDrawing::drawPolygon(coords, dummyNormals, selectionColorRGBA); m_selectionInfo.push_back(SelectionInfo(annotationFile, image, AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_NONE, selectionCenterXYZ)); } else { const bool debugFlag = false; if (debugFlag) { if (image->getCoordinateSpace() == AnnotationCoordinateSpaceEnum::STEREOTAXIC) { 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, ",")); std::cout << qPrintable(msg) << std::endl; } } drawImageBytesWithTexture(image->getDrawWithOpenGLTextureInfo(), bottomLeft, bottomRight, topRight, topLeft, imageRgbaBytes, imageWidth, imageHeight); drawnFlag = true; if (drawForegroundFlag) { BrainOpenGLPrimitiveDrawing::drawLineLoop(coords, foregroundRGBA, image->getLineWidth()); } setDepthTestingStatus(depthTestFlag); } if (image->isSelectedForEditing(m_inputs->m_windowIndex)) { const float outlineWidth = 2.0; drawAnnotationTwoDimSizingHandles(annotationFile, image, bottomLeft, bottomRight, topRight, topLeft, outlineWidth, image->getRotationAngle()); } } setDepthTestingStatus(savedDepthTestStatus); 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. * * @param lineHeadXYZ * Start of the line * @param lineTailXYZ * End of the line * @param lineThickness * Thickness of the line that affects size of the optional arrow heads. * @param validStartArrow * Add an arrow at the start of the line * @param validEndArrow * Add an arrow at the end of the line * @param coordinatesOut * Output containing coordinates for drawing the line */ void BrainOpenGLAnnotationDrawingFixedPipeline::createLineCoordinates(const float lineHeadXYZ[3], const float lineTailXYZ[3], const float lineThickness, const bool validStartArrow, const bool validEndArrow, std::vector& coordinatesOut) const { coordinatesOut.clear(); /* * Length of arrow's tips is function of line thickness */ const float tipScale = 3.0; const float tipLength = lineThickness * tipScale; /* * Point on arrow's line that is between the arrow's left and right arrow tips */ 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) }; /* * 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] }; 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 */ const float headRightTipEnd[3] = { startArrowTipsOnLine[0] - headLeftRightTipOffset[0], startArrowTipsOnLine[1] - headLeftRightTipOffset[1], startArrowTipsOnLine[2] - headLeftRightTipOffset[2] }; /* * Tip of arrow's head pointer on the left */ const float headLeftTipEnd[3] = { startArrowTipsOnLine[0] + headLeftRightTipOffset[0], startArrowTipsOnLine[1] + headLeftRightTipOffset[1], startArrowTipsOnLine[2] + headLeftRightTipOffset[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] }; 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); } } /** * Draw an annotation line. * * @param annotationFile * File containing the annotation. * @param line * Annotation line to draw. * @param surfaceDisplayed * Surface that is displayed (may be NULL). * @return * True if the annotation was drawn while NOT selecting annotations. */ bool BrainOpenGLAnnotationDrawingFixedPipeline::drawLine(AnnotationFile* annotationFile, AnnotationLine* line, const Surface* surfaceDisplayed) { CaretAssert(line); CaretAssert(line->getType() == AnnotationTypeEnum::LINE); float lineHeadXYZ[3]; float lineTailXYZ[3]; if ( ! getAnnotationWindowCoordinate(line->getStartCoordinate(), line->getCoordinateSpace(), surfaceDisplayed, lineHeadXYZ)) { return false; } if ( ! getAnnotationWindowCoordinate(line->getEndCoordinate(), line->getCoordinateSpace(), surfaceDisplayed, lineTailXYZ)) { return false; } const float lineWidth = line->getLineWidth(); const float backgroundLineWidth = lineWidth + 4; const float selectionCenterXYZ[3] = { (lineHeadXYZ[0] + lineTailXYZ[0]) / 2.0, (lineHeadXYZ[1] + lineTailXYZ[1]) / 2.0, (lineHeadXYZ[2] + lineTailXYZ[2]) / 2.0 }; const bool depthTestFlag = isDrawnWithDepthTesting(line); const bool savedDepthTestStatus = setDepthTestingStatus(depthTestFlag); std::vector coords; createLineCoordinates(lineHeadXYZ, lineTailXYZ, line->getLineWidth(), line->isDisplayStartArrow(), line->isDisplayEndArrow(), coords); uint8_t foregroundRGBA[4]; line->getLineColorRGBA(foregroundRGBA); const bool drawForegroundFlag = (foregroundRGBA[3] > 0.0); bool drawnFlag = false; if (drawForegroundFlag) { if (m_selectionModeFlag) { uint8_t selectionColorRGBA[4]; getIdentificationColor(selectionColorRGBA); BrainOpenGLPrimitiveDrawing::drawLines(coords, selectionColorRGBA, backgroundLineWidth); m_selectionInfo.push_back(SelectionInfo(annotationFile, line, AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_NONE, selectionCenterXYZ)); } else { if (drawForegroundFlag) { BrainOpenGLPrimitiveDrawing::drawLines(coords, foregroundRGBA, lineWidth); drawnFlag = true; } } if (line->isSelectedForEditing(m_inputs->m_windowIndex)) { drawAnnotationOneDimSizingHandles(annotationFile, line, lineHeadXYZ, lineTailXYZ, lineWidth); } } setDepthTestingStatus(savedDepthTestStatus); return drawnFlag; } /** * Draw a sizing handle at the given coordinate. * * @param handleType * Type of sizing handle. * @param annotationFile * File containing the annotation. * @param annotation * Annotation to draw. * @param xyz * Center of square. * @param halfWidthHeight * Half Width/height of square. * @param rotationAngle * Rotation angle for the sizing handle. */ void BrainOpenGLAnnotationDrawingFixedPipeline::drawSizingHandle(const AnnotationSizingHandleTypeEnum::Enum handleType, AnnotationFile* annotationFile, Annotation* annotation, const float xyz[3], const float halfWidthHeight, const float rotationAngle) { glPushMatrix(); glTranslatef(xyz[0], xyz[1], xyz[2]); if (rotationAngle != 0.0) { 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; bool drawFilledCircleFlag = false; bool drawOutlineCircleFlag = false; bool drawSquareFlag = false; switch (handleType) { case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_BOX_BOTTOM: drawSquareFlag = true; break; case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_BOX_BOTTOM_LEFT: drawFilledCircleFlag = true; break; case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_BOX_BOTTOM_RIGHT: drawFilledCircleFlag = true; break; case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_BOX_LEFT: drawSquareFlag = true; break; case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_BOX_RIGHT: drawSquareFlag = true; break; case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_BOX_TOP: drawSquareFlag = true; break; case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_BOX_TOP_LEFT: drawFilledCircleFlag = true; break; case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_BOX_TOP_RIGHT: drawFilledCircleFlag = true; break; case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_LINE_END: drawFilledCircleFlag = true; break; case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_LINE_START: drawFilledCircleFlag = true; break; case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_NONE: break; case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_ROTATION: drawOutlineCircleFlag = true; break; } if (m_selectionModeFlag) { uint8_t identificationRGBA[4]; getIdentificationColor(identificationRGBA); BrainOpenGLPrimitiveDrawing::drawPolygon(coords, dummyNormals, identificationRGBA); m_selectionInfo.push_back(SelectionInfo(annotationFile, annotation, handleType, xyz)); } else { if (drawFilledCircleFlag) { m_brainOpenGLFixedPipeline->drawCircleFilled(m_selectionBoxRGBA, halfWidthHeight); } else if (drawSquareFlag) { BrainOpenGLPrimitiveDrawing::drawPolygon(coords, dummyNormals, m_selectionBoxRGBA); } else if (drawOutlineCircleFlag) { const float diameter = halfWidthHeight; glPushMatrix(); glScaled(diameter, diameter, 1.0); m_rotationHandleCircle->draw(m_selectionBoxRGBA); glPopMatrix(); } } glPopMatrix(); } /** * Draw sizing handles around a one-dimensional annotation. * * @param annotationFile * File containing the annotation. * @param annotation * Annotation to draw. * @param firstPoint * Top right corner of annotation. * @param secondPoint * Top left corner of annotation. * @param lineThickness * Thickness of line (when enabled). */ void BrainOpenGLAnnotationDrawingFixedPipeline::drawAnnotationOneDimSizingHandles(AnnotationFile* annotationFile, Annotation* annotation, const float firstPoint[3], const float secondPoint[3], const float lineThickness) { float lengthVector[3]; MathFunctions::subtractVectors(secondPoint, firstPoint, lengthVector); MathFunctions::normalizeVector(lengthVector); const float dx = secondPoint[0] - firstPoint[0]; const float dy = secondPoint[1] - firstPoint[1]; const float cornerSquareSize = 3.0 + lineThickness; const float directionVector[3] = { lengthVector[0] * cornerSquareSize, lengthVector[1] * cornerSquareSize, 0.0 }; const float firstPointSymbolXYZ[3] = { firstPoint[0] - directionVector[0], firstPoint[1] - directionVector[1], firstPoint[2] - directionVector[2] }; const float secondPointSymbolXYZ[3] = { secondPoint[0] + directionVector[0], secondPoint[1] + directionVector[1], secondPoint[2] + directionVector[2] }; float rotationAngle = 0.0; if ((dy != 0.0) && (dx != 0.0)) { const float angleRadians = std::atan2(dx, dy); rotationAngle = MathFunctions::toDegrees(angleRadians); } /* * Symbol for first coordinate is a little bigger */ if (annotation->isSizeHandleValid(AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_LINE_START)) { drawSizingHandle(AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_LINE_START, annotationFile, annotation, firstPointSymbolXYZ, cornerSquareSize + 2.0, rotationAngle); } if (annotation->isSizeHandleValid(AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_LINE_END)) { drawSizingHandle(AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_LINE_END, annotationFile, annotation, secondPointSymbolXYZ, cornerSquareSize, rotationAngle); } 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 }; drawSizingHandle(AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_ROTATION, annotationFile, annotation, midPointXYZ, cornerSquareSize, rotationAngle); } } /** * Expand a box by given amounts in X and Y. * * @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 extraSpaceX * Extra space to add in X. * @param extraSpaceY * Extra space to add in Y. */ void BrainOpenGLAnnotationDrawingFixedPipeline::expandBox(float bottomLeft[3], float bottomRight[3], float topRight[3], float topLeft[3], const float extraSpaceX, const float extraSpaceY) { float widthVector[3]; MathFunctions::subtractVectors(topRight, topLeft, widthVector); MathFunctions::normalizeVector(widthVector); float heightVector[3]; MathFunctions::subtractVectors(topLeft, bottomLeft, heightVector); MathFunctions::normalizeVector(heightVector); const float widthSpacingX = extraSpaceX * widthVector[0]; const float widthSpacingY = extraSpaceY * widthVector[1]; const float heightSpacingX = extraSpaceX * heightVector[0]; const float heightSpacingY = extraSpaceY * heightVector[1]; topLeft[0] += (-widthSpacingX + heightSpacingX); topLeft[1] += (-widthSpacingY + heightSpacingY); topRight[0] += (widthSpacingX + heightSpacingX); topRight[1] += (widthSpacingY + heightSpacingY); bottomLeft[0] += (-widthSpacingX - heightSpacingX); bottomLeft[1] += (-widthSpacingY - heightSpacingY); bottomRight[0] += (widthSpacingX - heightSpacingX); bottomRight[1] += (widthSpacingY - heightSpacingY); } /** * Draw sizing handles around a two-dimensional annotation. * * @param annotationFile * File containing the annotation. * @param annotation * Annotation to draw. * @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 lineThickness * Thickness of line (when enabled). * @param rotationAngle * Rotation of the annotation. */ void BrainOpenGLAnnotationDrawingFixedPipeline::drawAnnotationTwoDimSizingHandles(AnnotationFile* annotationFile, Annotation* annotation, const float bottomLeft[3], const float bottomRight[3], const float topRight[3], const float topLeft[3], const float lineThickness, const float rotationAngle) { float heightVector[3]; MathFunctions::subtractVectors(topLeft, bottomLeft, heightVector); MathFunctions::normalizeVector(heightVector); const float innerSpacing = 2.0 + (lineThickness / 2.0); float handleTopLeft[3]; float handleTopRight[3]; float handleBottomRight[3]; float handleBottomLeft[3]; for (int32_t i = 0; i < 3; i++) { handleTopLeft[i] = topLeft[i]; handleTopRight[i] = topRight[i]; handleBottomRight[i] = bottomRight[i]; handleBottomLeft[i] = bottomLeft[i]; } 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); } const float handleLeft[3] = { (handleBottomLeft[0] + handleTopLeft[0]) / 2.0, (handleBottomLeft[1] + handleTopLeft[1]) / 2.0, (handleBottomLeft[2] + handleTopLeft[2]) / 2.0, }; const float handleRight[3] = { (handleBottomRight[0] + handleTopRight[0]) / 2.0, (handleBottomRight[1] + handleTopRight[1]) / 2.0, (handleBottomRight[2] + handleTopRight[2]) / 2.0, }; const float handleBottom[3] = { (handleBottomLeft[0] + handleBottomRight[0]) / 2.0, (handleBottomLeft[1] + handleBottomRight[1]) / 2.0, (handleBottomLeft[2] + handleBottomRight[2]) / 2.0, }; const float handleTop[3] = { (handleTopLeft[0] + handleTopRight[0]) / 2.0, (handleTopLeft[1] + handleTopRight[1]) / 2.0, (handleTopLeft[2] + handleTopRight[2]) / 2.0, }; const float sizeHandleSize = 5.0; if (annotation->isSizeHandleValid(AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_BOX_BOTTOM_LEFT)) { drawSizingHandle(AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_BOX_BOTTOM_LEFT, annotationFile, annotation, handleBottomLeft, sizeHandleSize, rotationAngle); } if (annotation->isSizeHandleValid(AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_BOX_BOTTOM_RIGHT)) { drawSizingHandle(AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_BOX_BOTTOM_RIGHT, annotationFile, annotation, handleBottomRight, sizeHandleSize, rotationAngle); } if (annotation->isSizeHandleValid(AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_BOX_TOP_RIGHT)) { drawSizingHandle(AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_BOX_TOP_RIGHT, annotationFile, annotation, handleTopRight, sizeHandleSize, rotationAngle); } if (annotation->isSizeHandleValid(AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_BOX_TOP_LEFT)) { drawSizingHandle(AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_BOX_TOP_LEFT, annotationFile, annotation, handleTopLeft, sizeHandleSize, rotationAngle); } if (annotation->isSizeHandleValid(AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_BOX_TOP)) { drawSizingHandle(AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_BOX_TOP, annotationFile, annotation, handleTop, sizeHandleSize, rotationAngle); } if (annotation->isSizeHandleValid(AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_BOX_BOTTOM)) { drawSizingHandle(AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_BOX_BOTTOM, annotationFile, annotation, handleBottom, sizeHandleSize, rotationAngle); } if (annotation->isSizeHandleValid(AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_BOX_RIGHT)) { drawSizingHandle(AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_BOX_RIGHT, annotationFile, annotation, handleRight, sizeHandleSize, rotationAngle); } if (annotation->isSizeHandleValid(AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_BOX_LEFT)) { drawSizingHandle(AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_BOX_LEFT, annotationFile, annotation, handleLeft, sizeHandleSize, rotationAngle); } if (annotation->isSizeHandleValid(AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_ROTATION)) { float handleOffset[3] = { handleTop[0], handleTop[1], handleTop[2] }; if (annotation->getType() == AnnotationTypeEnum::TEXT) { const AnnotationText* textAnn = dynamic_cast(annotation); CaretAssert(textAnn); /* * The rotation point of a text annotation * is adjusted for the horizontal alignment. */ switch (textAnn->getHorizontalAlignment()) { case AnnotationTextAlignHorizontalEnum::CENTER: break; case AnnotationTextAlignHorizontalEnum::LEFT: handleOffset[0] = handleTopLeft[0]; handleOffset[1] = handleTopLeft[1]; handleOffset[2] = handleTopLeft[2]; break; case AnnotationTextAlignHorizontalEnum::RIGHT: handleOffset[0] = handleTopRight[0]; handleOffset[1] = handleTopRight[1]; handleOffset[2] = handleTopRight[2]; break; } } const float rotationOffset = sizeHandleSize * 3.0; const float handleRotation[3] = { handleOffset[0] + (rotationOffset * heightVector[0]), handleOffset[1] + (rotationOffset * heightVector[1]), handleOffset[2] + (rotationOffset * heightVector[2]) }; 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]) }; /* * Rotation handle and line connecting rotation handle to selection box */ std::vector coords; coords.insert(coords.end(), handleRotationLineEnd, handleRotationLineEnd + 3); coords.insert(coords.end(), handleOffset, handleOffset + 3); BrainOpenGLPrimitiveDrawing::drawLines(coords, m_selectionBoxRGBA, 2.0); drawSizingHandle(AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_ROTATION, annotationFile, annotation, handleRotation, sizeHandleSize, rotationAngle); } } /** * Set the color for drawing the selection box and handles. */ void BrainOpenGLAnnotationDrawingFixedPipeline::setSelectionBoxColor() { /* * Use the foreground color but reduce the intensity and saturation. */ m_selectionBoxRGBA[0] = m_brainOpenGLFixedPipeline->m_foregroundColorByte[0]; m_selectionBoxRGBA[1] = m_brainOpenGLFixedPipeline->m_foregroundColorByte[1]; m_selectionBoxRGBA[2] = m_brainOpenGLFixedPipeline->m_foregroundColorByte[2]; m_selectionBoxRGBA[3] = m_brainOpenGLFixedPipeline->m_foregroundColorByte[3]; QColor color(m_selectionBoxRGBA[0], m_selectionBoxRGBA[1], m_selectionBoxRGBA[2], m_selectionBoxRGBA[3]); qreal hue = 0.0; qreal saturation = 0.0; qreal value = 0.0; color.getHsvF(&hue, &saturation, &value); saturation *= 0.8; value *= 0.8; color.setHsvF(hue, saturation, value); m_selectionBoxRGBA[0] = static_cast(color.red()); m_selectionBoxRGBA[1] = static_cast(color.green()); m_selectionBoxRGBA[2] = static_cast(color.blue()); } /** * Is the annotation drawn with depth testing enabled (based upon coordinate space)? * * @param annotation * Annotation that will be drawn. * @return * True if the annotation is drawn with depth testing, else false. */ bool BrainOpenGLAnnotationDrawingFixedPipeline::isDrawnWithDepthTesting(const Annotation* /*annotation*/) { return true; /* bool depthTestFlag = false; switch (annotation->getCoordinateSpace()) { case AnnotationCoordinateSpaceEnum::STEREOTAXIC: depthTestFlag = true; break; case AnnotationCoordinateSpaceEnum::PIXELS: break; case AnnotationCoordinateSpaceEnum::SURFACE: depthTestFlag = true; break; case AnnotationCoordinateSpaceEnum::TAB: break; case AnnotationCoordinateSpaceEnum::WINDOW: break; } return depthTestFlag; */ } /** * Set the depth testing to the given status. * * @param newDepthTestingStatus * New status for depth testing. * @return * Depth testing status PRIOR to applying the new depth testing status. */ bool BrainOpenGLAnnotationDrawingFixedPipeline::setDepthTestingStatus(const bool newDepthTestingStatus) { GLboolean savedStatus = GL_FALSE; glGetBooleanv(GL_DEPTH_TEST, &savedStatus); if (newDepthTestingStatus) glEnable(GL_DEPTH_TEST); else glDisable(GL_DEPTH_TEST); return (savedStatus == GL_TRUE); } connectome-workbench-1.2.3+git41-gc4c6c90/src/Brain/BrainOpenGLAnnotationDrawingFixedPipeline.h000066400000000000000000000406071300200146000320740ustar00rootroot00000000000000#ifndef __BRAIN_OPEN_G_L_ANNOTATION_DRAWING_FIXED_PIPELINE_H__ #define __BRAIN_OPEN_G_L_ANNOTATION_DRAWING_FIXED_PIPELINE_H__ /*LICENSE_START*/ /* * Copyright (C) 2015 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 "AnnotationCoordinateSpaceEnum.h" #include "AnnotationSizingHandleTypeEnum.h" #include "BrainOpenGLFixedPipeline.h" #include "CaretObject.h" #include "CaretOpenGLInclude.h" #include "Plane.h" namespace caret { class Annotation; class AnnotationBox; class AnnotationColorBar; class AnnotationCoordinate; class AnnotationFile; class AnnotationImage; class AnnotationLine; class AnnotationOval; class AnnotationText; class AnnotationTwoDimensionalShape; class Brain; class BrainOpenGLFixedPipeline; class BrainOpenGLShapeRing; class DrawnWithOpenGLTextureInfo; class Surface; class BrainOpenGLAnnotationDrawingFixedPipeline : public CaretObject { 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 }; 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; }; BrainOpenGLAnnotationDrawingFixedPipeline(BrainOpenGLFixedPipeline* brainOpenGLFixedPipeline); virtual ~BrainOpenGLAnnotationDrawingFixedPipeline(); void drawAnnotations(Inputs* inputs, const AnnotationCoordinateSpaceEnum::Enum drawingCoordinateSpace, std::vector& colorBars, const Surface* surfaceDisplayed); void drawModelSpaceAnnotationsOnVolumeSlice(Inputs* inputs, const Plane& plane, const float sliceThickness); // ADD_NEW_METHODS_HERE virtual AString toString() const; private: class SelectionInfo { public: SelectionInfo(AnnotationFile* annotationFile, Annotation* annotation, AnnotationSizingHandleTypeEnum::Enum sizingHandle, const float windowXYZ[3]) { m_annotationFile = annotationFile; m_annotation = annotation; m_sizingHandle = sizingHandle; m_windowXYZ[0] = windowXYZ[0]; m_windowXYZ[1] = windowXYZ[1]; m_windowXYZ[2] = windowXYZ[2]; } AnnotationFile* m_annotationFile; Annotation* m_annotation; AnnotationSizingHandleTypeEnum::Enum m_sizingHandle; double m_windowXYZ[3]; }; class ColorBarLine { public: ColorBarLine(const std::vector& lineCoords, const float rgba[4]) { m_lineCoords = lineCoords; m_rgba[0] = rgba[0]; m_rgba[1] = rgba[1]; m_rgba[2] = rgba[2]; m_rgba[3] = rgba[3]; } std::vector m_lineCoords; float m_rgba[4]; }; BrainOpenGLAnnotationDrawingFixedPipeline(const BrainOpenGLAnnotationDrawingFixedPipeline&); BrainOpenGLAnnotationDrawingFixedPipeline& operator=(const BrainOpenGLAnnotationDrawingFixedPipeline&); void drawAnnotationsInternal(const AnnotationCoordinateSpaceEnum::Enum drawingCoordinateSpace, std::vector& colorBars, const Surface* surfaceDisplayed, const float sliceThickness); bool getAnnotationWindowCoordinate(const AnnotationCoordinate* coordinate, const AnnotationCoordinateSpaceEnum::Enum annotationCoordSpace, const Surface* surfaceDisplayed, float windowXYZOut[3]) const; bool getAnnotationTwoDimShapeBounds(const AnnotationTwoDimensionalShape* annotation2D, const float windowXYZ[3], float bottomLeftOut[3], float bottomRightOut[3], float topRightOut[3], float topLeftOut[3]) const; void applyRotationToShape(const float rotationAngle, const float rotationPoint[3], float bottomLeftOut[3], float bottomRightOut[3], float topRightOut[3], float topLeftOut[3]) const; void drawAnnotation(AnnotationFile* annotationFile, Annotation* annotation, const Surface* surfaceDisplayed); 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); bool drawOval(AnnotationFile* annotationFile, AnnotationOval* oval, const Surface* surfaceDisplayed); bool drawText(AnnotationFile* annotationFile, AnnotationText* text, const Surface* surfaceDisplayed); void drawColorBarSections(const AnnotationColorBar* colorBar, const float bottomLeft[3], const float bottomRight[3], const float topRight[3], const float topLeft[3], const float sectionsHeightInPixels); void drawColorBarText(const AnnotationColorBar* colorBar, const float bottomLeft[3], const float bottomRight[3], const float topRight[3], const float topLeft[3], const float textHeightInPixels, const float offsetFromTopInPixels); void drawColorBarTickMarks(const AnnotationColorBar* colorBar, const float bottomLeft[3], const float bottomRight[3], const float topRight[3], const float topLeft[3], const float tickMarksHeightInPixels, const float offsetFromBottomInPixels); void drawSizingHandle(const AnnotationSizingHandleTypeEnum::Enum handleType, AnnotationFile* annotationFile, Annotation* annotation, const float xyz[3], const float halfWidthHeight, const float rotationAngle); void drawAnnotationTwoDimSizingHandles(AnnotationFile* annotationFile, Annotation* annotation, const float bottomLeft[3], const float bottomRight[3], const float topRight[3], const float topLeft[3], const float lineThickness, const float rotationAngle); void drawAnnotationOneDimSizingHandles(AnnotationFile* annotationFile, Annotation* annotation, const float firstPoint[3], const float secondPoint[3], const float lineThickness); bool isDrawnWithDepthTesting(const Annotation* annotation); bool setDepthTestingStatus(const bool newDepthTestingStatus); void getIdentificationColor(uint8_t identificationColorOut[4]); bool convertModelToWindowCoordinate(const float modelXYZ[3], float windowXYZOut[3]) const; void viewportToOpenGLWindowCoordinate(const float viewportXYZ[3], float openGLXYZOut[3]) const; void createLineCoordinates(const float lineHeadXYZ[3], const float lineTailXYZ[3], const float lineThickness, const bool validStartArrow, const bool validEndArrow, std::vector& coordinatesOut) const; void getTextLineToBrainordinateLineCoordinates(const AnnotationText* text, const Surface* surfaceDisplayed, const float bottomLeft[3], const float bottomRight[3], const float topRight[3], const float topLeft[3], std::vector& lineCoordinatesOut) const; static void expandBox(float bottomLeft[3], float bottomRight[3], float topRight[3], float topLeft[3], const float extraSpaceX, const float extraSpaceY); void setSelectionBoxColor(); void startOpenGLForDrawing(GLint* savedShadeModelOut, GLboolean* savedLightingEnabledOut); void endOpenGLForDrawing(GLint savedShadeModel, GLboolean savedLightingEnabled); void clipLineAtTextBox(const float bottomLeft[3], const float bottomRight[3], const float topRight[3], const float topLeft[3], const float startXYZ[3], float endXYZ[3]) const; BrainOpenGLFixedPipeline* m_brainOpenGLFixedPipeline; Inputs* m_inputs; /** * Dummy annotation file is used for annotations that * do not belong to a file. This includes the * "annotation being drawn" and AnnotationColorBar's. */ AnnotationFile* m_dummyAnnotationFile; /** Tracks items drawn for selection */ std::vector m_selectionInfo; /** In selection mode */ bool m_selectionModeFlag; /** OpenGL Model Matrix */ GLdouble m_modelSpaceModelMatrix[16]; /** OpenGL Projection Matrix */ GLdouble m_modelSpaceProjectionMatrix[16]; /** OpenGL Viewport */ GLint m_modelSpaceViewport[4]; /** Browser tab's viewport */ //GLint m_tabViewport[4]; /** volume space plane */ Plane m_volumeSpacePlane; /** Validity of volume space plane */ bool m_volumeSpacePlaneValid; /** Thickness of volume slice when drawing annotations on volume slices */ float m_volumeSliceThickness; /** Color for selection box and sizing handles */ uint8_t m_selectionBoxRGBA[4]; /** Used for rotation hanlde circle */ BrainOpenGLShapeRing* m_rotationHandleCircle; // ADD_NEW_MEMBERS_HERE }; #ifdef __BRAIN_OPEN_G_L_ANNOTATION_DRAWING_FIXED_PIPELINE_DECLARE__ // #endif // __BRAIN_OPEN_G_L_ANNOTATION_DRAWING_FIXED_PIPELINE_DECLARE__ } // namespace #endif //__BRAIN_OPEN_G_L_ANNOTATION_DRAWING_FIXED_PIPELINE_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Brain/BrainOpenGLChartDrawingFixedPipeline.cxx000066400000000000000000002022651300200146000313760ustar00rootroot00000000000000 /*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_G_L_CHART_DRAWING_FIXED_PIPELINE_DECLARE__ #include "BrainOpenGLChartDrawingFixedPipeline.h" #undef __BRAIN_OPEN_G_L_CHART_DRAWING_FIXED_PIPELINE_DECLARE__ #include "AnnotationColorBar.h" #include "AnnotationPointSizeText.h" #include "CaretOpenGLInclude.h" #include "BrainOpenGLFixedPipeline.h" #include "BrainOpenGLTextRenderInterface.h" #include "CaretAssert.h" #include "ChartAxis.h" #include "ChartAxisCartesian.h" #include "ChartData.h" #include "ChartDataCartesian.h" #include "CaretLogger.h" #include "ChartMatrixDisplayProperties.h" #include "ChartModelDataSeries.h" #include "ChartModelFrequencySeries.h" #include "ChartModelTimeSeries.h" #include "ChartableMatrixInterface.h" #include "CaretPreferences.h" #include "ChartPoint.h" #include "CiftiMappableConnectivityMatrixDataFile.h" #include "CiftiParcelLabelFile.h" #include "CiftiParcelScalarFile.h" #include "CiftiScalarDataSeriesFile.h" #include "Brain.h" #include "ConnectivityDataLoaded.h" #include "EventCaretMappableDataFileMapsViewedInOverlays.h" #include "EventManager.h" #include "IdentificationWithColor.h" #include "SelectionItemChartDataSeries.h" #include "SelectionItemChartFrequencySeries.h" #include "SelectionItemChartMatrix.h" #include "SelectionItemChartTimeSeries.h" #include "SelectionManager.h" #include "SessionManager.h" using namespace caret; /** * \class caret::BrainOpenGLChartDrawingFixedPipeline * \brief Chart drawing using OpenGL's fixed pipeline. * \ingroup Brain */ /** * Constructor. */ BrainOpenGLChartDrawingFixedPipeline::BrainOpenGLChartDrawingFixedPipeline() : BrainOpenGLChartDrawingInterface() { m_brain = NULL; m_fixedPipelineDrawing = NULL; m_identificationModeFlag = false; } /** * Destructor. */ BrainOpenGLChartDrawingFixedPipeline::~BrainOpenGLChartDrawingFixedPipeline() { } /** * Draw a cartesian chart in the given viewport. * * @param brain * Brain. * @param fixedPipelineDrawing * The fixed pipeline OpenGL drawing. * @param viewport * Viewport for the chart. * @param textRenderer * Text rendering. * @param cartesianChart * Cartesian Chart that is drawn. * @param selectionItemDataType * Selected data type. * @param tabIndex * Index of the tab. */ void BrainOpenGLChartDrawingFixedPipeline::drawCartesianChart(Brain* brain, BrainOpenGLFixedPipeline* fixedPipelineDrawing, const int32_t viewport[4], BrainOpenGLTextRenderInterface* textRenderer, ChartModelCartesian* cartesianChart, const SelectionItemDataTypeEnum::Enum selectionItemDataType, const int32_t tabIndex) { m_brain = brain; m_fixedPipelineDrawing = fixedPipelineDrawing; m_tabIndex = tabIndex; m_chartModelDataSeriesBeingDrawnForIdentification = dynamic_cast(cartesianChart); m_chartModelFrequencySeriesBeingDrawnForIdentification = dynamic_cast(cartesianChart); m_chartModelTimeSeriesBeingDrawnForIdentification = dynamic_cast(cartesianChart); m_chartCartesianSelectionTypeForIdentification = selectionItemDataType; m_chartableMatrixInterfaceBeingDrawnForIdentification = NULL; CaretAssert(cartesianChart); if (cartesianChart->isEmpty()) { return; } SelectionItemChartDataSeries* chartDataSeriesID = m_brain->getSelectionManager()->getChartDataSeriesIdentification(); SelectionItemChartFrequencySeries* chartFrequencySeriesID = m_brain->getSelectionManager()->getChartFrequencySeriesIdentification(); SelectionItemChartTimeSeries* chartTimeSeriesID = m_brain->getSelectionManager()->getChartTimeSeriesIdentification(); /* * Check for a 'selection' type mode */ m_identificationModeFlag = false; switch (m_fixedPipelineDrawing->mode) { case BrainOpenGLFixedPipeline::MODE_DRAWING: break; case BrainOpenGLFixedPipeline::MODE_IDENTIFICATION: if (chartDataSeriesID->isEnabledForSelection() || chartFrequencySeriesID->isEnabledForSelection() || chartTimeSeriesID->isEnabledForSelection()) { m_identificationModeFlag = true; glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); } else { return; } break; case BrainOpenGLFixedPipeline::MODE_PROJECTION: return; break; } saveStateOfOpenGL(); resetIdentification(); const int32_t vpX = viewport[0]; const int32_t vpY = viewport[1]; const int32_t vpWidth = viewport[2]; const int32_t vpHeight = viewport[3]; int32_t chartGraphicsDrawingViewport[4] = { vpX, vpY, vpWidth, vpHeight }; /* * Margin is region around the chart in which * the axes legends, values, and ticks are drawn. */ const double marginSize = 30; Margins margins(marginSize); double width, height; estimateCartesianChartAxisLegendsWidthHeight(textRenderer, vpHeight, cartesianChart->getLeftAxis(), width, height); margins.m_left = std::max(margins.m_left, width); estimateCartesianChartAxisLegendsWidthHeight(textRenderer, vpHeight, cartesianChart->getRightAxis(), width, height); margins.m_right = std::max(margins.m_right, width); estimateCartesianChartAxisLegendsWidthHeight(textRenderer, vpHeight, cartesianChart->getTopAxis(), width, height); margins.m_top = std::max(margins.m_top, height); estimateCartesianChartAxisLegendsWidthHeight(textRenderer, vpHeight, cartesianChart->getBottomAxis(), width, height); margins.m_bottom = std::max(margins.m_bottom, height); if (margins.m_left > marginSize) margins.m_left += 10; if (margins.m_right > marginSize) margins.m_right += 10; /* * Ensure that there is sufficient space for the axes data display. */ if ((vpWidth > (marginSize * 3)) && (vpHeight > (marginSize * 3))) { /* Draw legends and grids */ drawChartAxis(vpX, vpY, vpWidth, vpHeight, margins, textRenderer, cartesianChart->getLeftAxis()); drawChartAxis(vpX, vpY, vpWidth, vpHeight, margins, textRenderer, cartesianChart->getRightAxis()); drawChartAxis(vpX, vpY, vpWidth, vpHeight, margins, textRenderer, cartesianChart->getBottomAxis()); drawChartAxis(vpX, vpY, vpWidth, vpHeight, margins, textRenderer, cartesianChart->getTopAxis()); drawChartGraphicsBoxAndSetViewport(vpX, vpY, vpWidth, vpHeight, margins, chartGraphicsDrawingViewport); } glViewport(chartGraphicsDrawingViewport[0], chartGraphicsDrawingViewport[1], chartGraphicsDrawingViewport[2], chartGraphicsDrawingViewport[3]); drawChartGraphicsLineSeries(textRenderer, cartesianChart); /* * Process selection */ if (m_identificationModeFlag) { processIdentification(); } restoreStateOfOpenGL(); } /** * Draw a matrix chart in the given viewport. * * @param brain * Brain. * @param fixedPipelineDrawing * The fixed pipeline OpenGL drawing. * @param viewport * Viewport for the chart. * @param textRenderer * Text rendering. * @param chartMatrixInterface * Chart matrix interface containing matrix data. * @param scalarDataSeriesMapIndex * Selected map in scalar data series file. * @param selectionItemDataType * Selected data type. * @param tabIndex * Index of the tab. */ void BrainOpenGLChartDrawingFixedPipeline::drawMatrixChart(Brain* brain, BrainOpenGLFixedPipeline* fixedPipelineDrawing, const int32_t viewport[4], BrainOpenGLTextRenderInterface* textRenderer, ChartableMatrixInterface* chartMatrixInterface, const int32_t scalarDataSeriesMapIndex, const SelectionItemDataTypeEnum::Enum selectionItemDataType, const int32_t tabIndex) { m_brain = brain; m_fixedPipelineDrawing = fixedPipelineDrawing; m_tabIndex = tabIndex; m_chartModelDataSeriesBeingDrawnForIdentification = NULL; m_chartModelFrequencySeriesBeingDrawnForIdentification = NULL; m_chartModelTimeSeriesBeingDrawnForIdentification = NULL; m_chartableMatrixInterfaceBeingDrawnForIdentification = chartMatrixInterface; m_chartableMatrixSelectionTypeForIdentification = selectionItemDataType; CaretAssert(chartMatrixInterface); SelectionItemChartMatrix* chartMatrixID = m_brain->getSelectionManager()->getChartMatrixIdentification(); /* * Check for a 'selection' type mode */ m_identificationModeFlag = false; switch (m_fixedPipelineDrawing->mode) { case BrainOpenGLFixedPipeline::MODE_DRAWING: break; case BrainOpenGLFixedPipeline::MODE_IDENTIFICATION: if (chartMatrixID->isEnabledForSelection()) { m_identificationModeFlag = true; glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); } else { return; } break; case BrainOpenGLFixedPipeline::MODE_PROJECTION: return; break; } saveStateOfOpenGL(); resetIdentification(); ChartMatrixDisplayProperties* matrixProperties = chartMatrixInterface->getChartMatrixDisplayProperties(m_tabIndex); const int32_t paletteHeight = (matrixProperties->getColorBar()->isDisplayed() ? 40 : 0); /* * Viewport is X, Y, Width, Height */ const int32_t chartGraphicsDrawingViewport[4] = { viewport[0], viewport[1] + paletteHeight, viewport[2], viewport[3] - paletteHeight }; /* * Margin is region around the chart in which * the axes legends, values, and ticks are drawn. */ glViewport(chartGraphicsDrawingViewport[0], chartGraphicsDrawingViewport[1], chartGraphicsDrawingViewport[2], chartGraphicsDrawingViewport[3]); drawChartGraphicsMatrix(chartGraphicsDrawingViewport, textRenderer, chartMatrixInterface, scalarDataSeriesMapIndex); /* * Process selection */ if (m_identificationModeFlag) { processIdentification(); } restoreStateOfOpenGL(); } /** * Draw the chart axes grid/box * * @param vpX * Viewport X for all chart content * @param vpY * Viewport Y for all chart content * @param vpWidth * Viewport width for all chart content * @param vpHeight * Viewport height for all chart content * @param margins * Margin around graphics region. The margin corresponding to the * axis may be changed so that all text in the axis is visible * (and not cut off). * @param textRenderer * Text rendering. * @param axis * Axis that is drawn. */ void BrainOpenGLChartDrawingFixedPipeline::drawChartAxis(const float vpX, const float vpY, const float vpWidth, const float vpHeight, Margins& margins, BrainOpenGLTextRenderInterface* textRenderer, ChartAxis* axis) { if (axis == NULL) { return; } if ( ! axis->isVisible()) { return; } switch (axis->getAxisType()) { case ChartAxisTypeEnum::CHART_AXIS_TYPE_CARTESIAN: drawChartAxisCartesian(vpX, vpY, vpWidth, vpHeight, margins, textRenderer, dynamic_cast(axis)); break; case ChartAxisTypeEnum::CHART_AXIS_TYPE_NONE: break; } } /** * Draw the chart axes grid/box * * @param vpX * Viewport X for all chart content * @param vpY * Viewport Y for all chart content * @param vpWidth * Viewport width for all chart content * @param vpHeight * Viewport height for all chart content * @param margins * Margin around graphics region. The margin corresponding to the * axis may be changed so that all text in the axis is visible * (and not cut off). * @param textRenderer * Text rendering. * @param chartModelCartesian * The chart cartesian model. * @param axis * Axis that is drawn. */ void BrainOpenGLChartDrawingFixedPipeline::drawChartAxisCartesian(const float vpX, const float vpY, const float vpWidth, const float vpHeight, Margins& margins, BrainOpenGLTextRenderInterface* textRenderer, ChartAxisCartesian* axis) { CaretAssert(axis); const float fontSizeInPixels = 14; float axisLength = 0.0; switch (axis->getAxisLocation()) { case ChartAxisLocationEnum::CHART_AXIS_LOCATION_BOTTOM: case ChartAxisLocationEnum::CHART_AXIS_LOCATION_TOP: axisLength = vpWidth - (margins.m_left + margins.m_right); break; case ChartAxisLocationEnum::CHART_AXIS_LOCATION_LEFT: case ChartAxisLocationEnum::CHART_AXIS_LOCATION_RIGHT: axisLength = vpHeight - (margins.m_top + margins.m_bottom); break; } std::vector labelOffsetInPixels; std::vector labelTexts; axis->getLabelsAndPositions(axisLength, fontSizeInPixels, labelOffsetInPixels, labelTexts); const int32_t numLabelsToDraw = static_cast(labelTexts.size()); AnnotationPointSizeText annotationText(AnnotationAttributesDefaultTypeEnum::NORMAL); if (numLabelsToDraw > 0) { float labelX = 0.0; float labelY = 0.0; float labelOffsetMultiplierX = 0.0; float labelOffsetMultiplierY = 0.0; annotationText.setHorizontalAlignment(AnnotationTextAlignHorizontalEnum::CENTER); annotationText.setVerticalAlignment(AnnotationTextAlignVerticalEnum::MIDDLE); /* * Viewport for axis name and numeric values */ int32_t axisVpX = vpX; int32_t axisVpY = vpY; int32_t axisVpWidth = vpWidth; int32_t axisVpHeight = vpHeight; float tickDeltaXY[2] = { 0.0, 0.0 }; const float tickLength = 5.0; switch (axis->getAxisLocation()) { case ChartAxisLocationEnum::CHART_AXIS_LOCATION_BOTTOM: axisVpX = vpX; axisVpY = vpY; axisVpWidth = vpWidth; axisVpHeight = margins.m_bottom; labelX = margins.m_left; labelY = margins.m_bottom; labelOffsetMultiplierX = 1.0; labelOffsetMultiplierY = 0.0; annotationText.setHorizontalAlignment(AnnotationTextAlignHorizontalEnum::CENTER); annotationText.setVerticalAlignment(AnnotationTextAlignVerticalEnum::TOP); tickDeltaXY[0] = 0.0; tickDeltaXY[1] = -tickLength; break; case ChartAxisLocationEnum::CHART_AXIS_LOCATION_TOP: axisVpX = vpX; axisVpY = vpY + vpHeight - margins.m_top; axisVpWidth = vpWidth; axisVpHeight = margins.m_top; labelX = margins.m_left; labelY = 0.0; labelOffsetMultiplierX = 1.0; labelOffsetMultiplierY = 0.0; annotationText.setHorizontalAlignment(AnnotationTextAlignHorizontalEnum::CENTER); annotationText.setVerticalAlignment(AnnotationTextAlignVerticalEnum::BOTTOM); tickDeltaXY[0] = 0.0; tickDeltaXY[1] = tickLength; break; case ChartAxisLocationEnum::CHART_AXIS_LOCATION_LEFT: axisVpX = vpX; axisVpY = vpY; axisVpWidth = margins.m_left; axisVpHeight = vpHeight; labelX = margins.m_left; labelY = margins.m_bottom; labelOffsetMultiplierX = 0.0; labelOffsetMultiplierY = 1.0; annotationText.setHorizontalAlignment(AnnotationTextAlignHorizontalEnum::RIGHT); annotationText.setVerticalAlignment(AnnotationTextAlignVerticalEnum::MIDDLE); tickDeltaXY[0] = -tickLength; tickDeltaXY[1] = 0.0; break; case ChartAxisLocationEnum::CHART_AXIS_LOCATION_RIGHT: axisVpX = vpX + vpWidth - margins.m_right; axisVpY = vpY; axisVpWidth = margins.m_right; axisVpHeight = vpHeight; labelX = 0.0; labelY = margins.m_bottom; labelOffsetMultiplierX = 0.0; labelOffsetMultiplierY = 1.0; annotationText.setHorizontalAlignment(AnnotationTextAlignHorizontalEnum::LEFT); annotationText.setVerticalAlignment(AnnotationTextAlignVerticalEnum::MIDDLE); tickDeltaXY[0] = tickLength; tickDeltaXY[1] = 0.0; break; } /* * Viewport for axis text and numeric values */ const int viewport[4] = { axisVpX, axisVpY, axisVpWidth, axisVpHeight }; glViewport(viewport[0], viewport[1], viewport[2], viewport[3]); glMatrixMode(GL_PROJECTION); glLoadIdentity(); glOrtho(0, axisVpWidth, 0, axisVpHeight, -1.0, 1.0); glMatrixMode(GL_MODELVIEW); glLoadIdentity(); glColor3fv(m_fixedPipelineDrawing->m_foregroundColorFloat); for (int32_t i = 0; i < numLabelsToDraw; i++) { const float tickStartX = labelX + labelOffsetInPixels[i] * labelOffsetMultiplierX; const float tickStartY = labelY + labelOffsetInPixels[i] * labelOffsetMultiplierY; const float tickEndX = tickStartX + tickDeltaXY[0]; const float tickEndY = tickStartY + tickDeltaXY[1]; glBegin(GL_LINES); glVertex2f(tickStartX, tickStartY); glVertex2f(tickEndX, tickEndY); glEnd(); const float textX = tickEndX; const float textY = tickEndY; annotationText.setText(labelTexts[i]); textRenderer->drawTextAtViewportCoords(textX, textY, 0.0, annotationText); } const AString axisText = axis->getText(); if ( ! axisText.isEmpty()) { AnnotationPointSizeText annotationText(AnnotationAttributesDefaultTypeEnum::NORMAL); annotationText.setHorizontalAlignment(AnnotationTextAlignHorizontalEnum::CENTER); annotationText.setVerticalAlignment(AnnotationTextAlignVerticalEnum::MIDDLE); bool drawAxisTextVerticalFlag = false; float axisTextCenterX = axisVpWidth / 2.0; float axisTextCenterY = axisVpHeight / 2.0; const float textMarginOffset = 5.0; switch (axis->getAxisLocation()) { case ChartAxisLocationEnum::CHART_AXIS_LOCATION_BOTTOM: axisTextCenterX = (vpWidth / 2.0); axisTextCenterY = textMarginOffset; annotationText.setVerticalAlignment(AnnotationTextAlignVerticalEnum::BOTTOM); break; case ChartAxisLocationEnum::CHART_AXIS_LOCATION_TOP: axisTextCenterX = (vpWidth / 2.0); axisTextCenterY = margins.m_top - textMarginOffset; annotationText.setVerticalAlignment(AnnotationTextAlignVerticalEnum::TOP); break; case ChartAxisLocationEnum::CHART_AXIS_LOCATION_LEFT: axisTextCenterX = textMarginOffset; axisTextCenterY = (vpHeight / 2.0); annotationText.setHorizontalAlignment(AnnotationTextAlignHorizontalEnum::LEFT); drawAxisTextVerticalFlag = true; break; case ChartAxisLocationEnum::CHART_AXIS_LOCATION_RIGHT: axisTextCenterX = margins.m_right - textMarginOffset; axisTextCenterY = (vpHeight / 2.0); annotationText.setHorizontalAlignment(AnnotationTextAlignHorizontalEnum::RIGHT); drawAxisTextVerticalFlag = true; break; } if (drawAxisTextVerticalFlag) { annotationText.setOrientation(AnnotationTextOrientationEnum::STACKED); annotationText.setText(axisText); textRenderer->drawTextAtViewportCoords(axisTextCenterX, axisTextCenterY, 0.0, annotationText); } else { annotationText.setOrientation(AnnotationTextOrientationEnum::HORIZONTAL); annotationText.setText(axisText); textRenderer->drawTextAtViewportCoords(axisTextCenterX, axisTextCenterY, 0.0, annotationText); } } } } /** * Estimate the size of the axis' text. * * @param textRenderer * Text rendering. * @param viewportHeight * Height of viewport. * @param axis * The axis. * @param widthOut * Width of text out. * @param heightOut * Heigh of text out. */ void BrainOpenGLChartDrawingFixedPipeline::estimateCartesianChartAxisLegendsWidthHeight(BrainOpenGLTextRenderInterface* textRenderer, const float viewportHeight, ChartAxis* axis, double& widthOut, double& heightOut) { widthOut = 0; heightOut = 0; if (axis == NULL) { return; } ChartAxisCartesian* cartesianAxis = dynamic_cast(axis); if ( ! cartesianAxis->isVisible()) { return; } const float fontSizeInPixels = 14; const float axisLength = 1000.0; std::vector labelOffsetInPixels; std::vector labelTexts; cartesianAxis->getLabelsAndPositions(axisLength, fontSizeInPixels, labelOffsetInPixels, labelTexts); for (std::vector::iterator iter = labelTexts.begin(); iter != labelTexts.end(); iter++) { const AString text = *iter; if ( ! text.isEmpty()) { AnnotationPointSizeText annotationText(AnnotationAttributesDefaultTypeEnum::NORMAL); annotationText.setText(text); double textWidth = 0.0; double textHeight = 0.0; textRenderer->getTextWidthHeightInPixels(annotationText, viewportHeight, textWidth, textHeight); widthOut = std::max(widthOut, textWidth); heightOut = std::max(heightOut, textHeight); } } } /** * Draw the chart graphics surrounding box and set the graphics viewport. * drawChartGraphicsBoxAndSetViewport * @param vpX * Viewport X * @param vpY * Viewport Y * @param vpWidth * Viewport width * @param vpHeight * Viewport height * @param marginSize * Margin around grid/box * @param chartGraphicsDrawingViewportOut * Output containing viewport for drawing chart graphics within * the box/grid that is adjusted for the box's line thickness. */ void BrainOpenGLChartDrawingFixedPipeline::drawChartGraphicsBoxAndSetViewport(const float vpX, const float vpY, const float vpWidth, const float vpHeight, const Margins& margins, int32_t chartGraphicsDrawingViewportOut[4]) { const float gridLineWidth = 2; const float halfGridLineWidth = gridLineWidth / 2.0; const float gridLeft = vpX + margins.m_left; const float gridRight = vpX + vpWidth - margins.m_right; const float gridBottom = vpY + margins.m_bottom; const float gridTop = vpY + vpHeight - margins.m_top; glViewport(vpX, vpY, vpWidth, vpHeight); glMatrixMode(GL_PROJECTION); glLoadIdentity(); glOrtho(vpX, (vpX + vpWidth), vpY, (vpY + vpHeight), -1.0, 1.0); glMatrixMode(GL_MODELVIEW); glLoadIdentity(); glLineWidth(gridLineWidth); glColor3fv(m_fixedPipelineDrawing->m_foregroundColorFloat); glBegin(GL_LINES); /* bottom line */ glVertex3f(gridLeft, gridBottom + halfGridLineWidth, 0.0); glVertex3f(gridRight, gridBottom + halfGridLineWidth, 0.0); /* right line */ glVertex3f(gridRight - halfGridLineWidth, gridBottom, 0.0); glVertex3f(gridRight - halfGridLineWidth, gridTop, 0.0); /* top line */ glVertex3f(gridRight, gridTop - halfGridLineWidth, 0.0); glVertex3f(gridLeft, gridTop - halfGridLineWidth, 0.0); /* left line */ glVertex3f(gridLeft + halfGridLineWidth, gridTop, 0.0); glVertex3f(gridLeft + halfGridLineWidth, gridBottom, 0.0); glEnd(); /* * Region inside the grid's box */ const int32_t graphicsLeft = static_cast(gridLeft + std::ceil(gridLineWidth + 1.0)); const int32_t graphicsRight = static_cast(gridRight - std::floor(gridLineWidth + 1.0)); const int32_t graphicsBottom = static_cast(gridBottom + std::ceil(gridLineWidth + 1.0)); const int32_t graphicsTop = static_cast(gridTop - std::floor(gridLineWidth + 1.0)); const int32_t graphicsWidth = graphicsRight - graphicsLeft; const int32_t graphicsHeight = graphicsTop - graphicsBottom; chartGraphicsDrawingViewportOut[0] = graphicsLeft; chartGraphicsDrawingViewportOut[1] = graphicsBottom; chartGraphicsDrawingViewportOut[2] = graphicsWidth; chartGraphicsDrawingViewportOut[3] = graphicsHeight; } /** * Draw graphics for the given line series chart. * * @param textRenderer * Text rendering. * @param chart * Chart that is drawn. */ void BrainOpenGLChartDrawingFixedPipeline::drawChartGraphicsLineSeries(BrainOpenGLTextRenderInterface* /*textRenderer*/, ChartModelCartesian* chart) { CaretAssert(chart); std::vector chartVector = chart->getAllChartDatas(); if (chartVector.empty()) { return; } m_fixedPipelineDrawing->enableLineAntiAliasing(); const ChartAxisCartesian* leftAxis = dynamic_cast(chart->getLeftAxis()); CaretAssert(leftAxis); const ChartAxisCartesian* bottomAxis = dynamic_cast(chart->getBottomAxis()); CaretAssert(bottomAxis); float xMin = bottomAxis->getMinimumValue(); float xMax = bottomAxis->getMaximumValue(); float yMin = leftAxis->getMinimumValue(); float yMax = leftAxis->getMaximumValue(); glMatrixMode(GL_PROJECTION); glLoadIdentity(); glOrtho(xMin, xMax, yMin, yMax, -1.0, 1.0); glMatrixMode(GL_MODELVIEW); glLoadIdentity(); const float lineWidth = chart->getLineWidth(); /* * Start at the oldest chart and end with the newest chart. */ const int32_t numChartData = static_cast(chartVector.size()); for (int32_t chartDataIndex = (numChartData - 1); chartDataIndex >= 0; chartDataIndex--) { const ChartData* chartData = chartVector[chartDataIndex]; if (chartData->isSelected(m_tabIndex)) { const ChartDataCartesian* chartDataCart = dynamic_cast(chartData); CaretAssert(chartDataCart); CaretColorEnum::Enum color = chartDataCart->getColor(); drawChartDataCartesian(chartDataIndex, chartDataCart, lineWidth, CaretColorEnum::toRGB(color)); } } if (chart->isAverageChartDisplaySelected()) { const ChartData* chartData = chart->getAverageChartDataForDisplay(m_tabIndex); if (chartData != NULL) { const ChartDataCartesian* chartDataCart = dynamic_cast(chartData); CaretAssert(chartDataCart); drawChartDataCartesian(-1, chartDataCart, lineWidth, m_fixedPipelineDrawing->m_foregroundColorFloat); } } m_fixedPipelineDrawing->disableLineAntiAliasing(); } /** * Draw the cartesian data with the given color. * * @param chartDataIndex * Index of chart data * @param chartDataCartesian * Cartesian data that is drawn. * @param lineWidth * Width of lines. * @param color * Color for the data. */ void BrainOpenGLChartDrawingFixedPipeline::drawChartDataCartesian(const int32_t chartDataIndex, const ChartDataCartesian* chartDataCartesian, const float lineWidth, const float rgb[3]) { if (lineWidth <= 0.0) { return; } glColor3fv(rgb); glLineWidth(lineWidth); if (m_identificationModeFlag) { glLineWidth(5.0); } glBegin(GL_LINE_STRIP); const int32_t numPoints = chartDataCartesian->getNumberOfPoints(); for (int32_t i = 0; i < numPoints; i++) { const ChartPoint* point = chartDataCartesian->getPointAtIndex(i); if (m_identificationModeFlag) { uint8_t rgbaForID[4]; addToChartLineIdentification(chartDataIndex, i, rgbaForID); glColor4ubv(rgbaForID); } glVertex2fv(point->getXY()); } glEnd(); } /** * Draw graphics for the matrix chart.. * * @param viewport * The viewport. * @param textRenderer * Text rendering. * @param chartMatrixInterface * Chart that is drawn. * @param scalarDataSeriesMapIndex * Selected map for scalar data series file. */ void BrainOpenGLChartDrawingFixedPipeline::drawChartGraphicsMatrix(const int32_t viewport[4], BrainOpenGLTextRenderInterface* /*textRenderer*/, ChartableMatrixInterface* chartMatrixInterface, const int32_t scalarDataSeriesMapIndex) { CaretAssert(chartMatrixInterface); CaretPreferences* prefs = SessionManager::get()->getCaretPreferences(); uint8_t highlightRGBByte[3]; prefs->getBackgroundAndForegroundColors()->getColorForegroundChartView(highlightRGBByte); const float highlightRGB[3] = { highlightRGBByte[0] / 255.0, highlightRGBByte[1] / 255.0, highlightRGBByte[2] / 255.0 }; int32_t numberOfRows = 0; int32_t numberOfColumns = 0; std::vector matrixRGBA; if (chartMatrixInterface->getMatrixDataRGBA(numberOfRows, numberOfColumns, matrixRGBA)) { std::set selectedColumnIndices; std::set selectedRowIndices; CiftiMappableConnectivityMatrixDataFile* connMapFile = dynamic_cast(chartMatrixInterface); if (connMapFile != NULL) { const ConnectivityDataLoaded* connDataLoaded = connMapFile->getConnectivityDataLoaded(); if (connDataLoaded != NULL) { int64_t loadedRowIndex = -1; int64_t loadedColumnIndex = -1; connDataLoaded->getRowColumnLoading(loadedRowIndex, loadedColumnIndex); if (loadedRowIndex >= 0) { selectedRowIndices.insert(loadedRowIndex); } else if (loadedColumnIndex >= 0) { selectedColumnIndices.insert(loadedColumnIndex); } } } CiftiParcelScalarFile* parcelScalarFile = dynamic_cast(chartMatrixInterface); if (parcelScalarFile != NULL) { EventCaretMappableDataFileMapsViewedInOverlays mapOverlayEvent(parcelScalarFile); EventManager::get()->sendEvent(mapOverlayEvent.getPointer()); selectedColumnIndices = mapOverlayEvent.getSelectedMapIndices(); } CiftiParcelLabelFile* parcelLabelFile = dynamic_cast(chartMatrixInterface); if (parcelLabelFile != NULL) { EventCaretMappableDataFileMapsViewedInOverlays mapOverlayEvent(parcelLabelFile); EventManager::get()->sendEvent(mapOverlayEvent.getPointer()); selectedColumnIndices = mapOverlayEvent.getSelectedMapIndices(); } CiftiScalarDataSeriesFile* scalarDataSeriesFile = dynamic_cast(chartMatrixInterface); if (scalarDataSeriesFile != NULL) { if (scalarDataSeriesMapIndex >= 0) { selectedRowIndices.insert(scalarDataSeriesMapIndex); } } bool applyTransformationsFlag = false; float panningXY[2] = { 0.0, 0.0 }; float zooming = 1.0; 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 ((viewport[2] < (margin * 3.0)) || (viewport[3] < (margin * 3.0))) { margin = 0.0; } const float graphicsWidth = viewport[2] - (margin * 2.0); const float graphicsHeight = viewport[3] - (margin * 2.0); /* * Set the width and neight of each matrix cell. */ ChartMatrixDisplayProperties* matrixProperties = chartMatrixInterface->getChartMatrixDisplayProperties(m_tabIndex); CaretAssert(matrixProperties); const ChartMatrixScaleModeEnum::Enum scaleMode = matrixProperties->getScaleMode(); switch (scaleMode) { case ChartMatrixScaleModeEnum::CHART_MATRIX_SCALE_AUTO: /* * Auto scale 'fills' the matrix region * and updates the width and height in the * matrix properties for use in manual mode. * There is NO zooming or panning for Auto scale. */ cellWidth = graphicsWidth / numberOfColumns; cellHeight = graphicsHeight / numberOfRows; matrixProperties->setCellWidth(cellWidth); matrixProperties->setCellHeight(cellHeight); break; case ChartMatrixScaleModeEnum::CHART_MATRIX_SCALE_MANUAL: /* * Use the cell width and height for manual mode * and allow both panning and zooming. */ cellWidth = matrixProperties->getCellWidth(); cellHeight = matrixProperties->getCellHeight(); matrixProperties->getViewPanning(panningXY); zooming = matrixProperties->getViewZooming(); applyTransformationsFlag = true; break; } const bool highlightSelectedRowColumnFlag = ( ( ! m_identificationModeFlag) && matrixProperties->isSelectedRowColumnHighlighted() ); const bool displayGridLinesFlag = ( ( ! m_identificationModeFlag) && matrixProperties->isGridLinesDisplayed() ); /* * Set the coordinates for the area in which the matrix is drawn. */ 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(panningXY[0], panningXY[1], 0.0); const float chartWidth = cellWidth * numberOfColumns; const float chartHeight = cellHeight * numberOfRows; const float halfWidth = chartWidth / 2.0; const float halfHeight = chartHeight / 2.0; glTranslatef(halfWidth, halfHeight, 0.0); glScalef(zooming, zooming, 1.0); glTranslatef(-halfWidth, -halfHeight, 0.0); } int32_t rgbaOffset = 0; std::vector quadVerticesXYZ; quadVerticesXYZ.reserve(numberOfRows * numberOfColumns * 3); std::vector quadVerticesFloatRGBA; quadVerticesFloatRGBA.reserve(numberOfRows * numberOfColumns * 4); std::vector quadVerticesByteRGBA; quadVerticesByteRGBA.reserve(numberOfRows * numberOfColumns * 4); 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; uint8_t idRGBA[4]; if (m_identificationModeFlag) { addToChartMatrixIdentification(rowIndex, columnIndex, idRGBA); } if (m_identificationModeFlag) { quadVerticesByteRGBA.push_back(idRGBA[0]); quadVerticesByteRGBA.push_back(idRGBA[1]); quadVerticesByteRGBA.push_back(idRGBA[2]); quadVerticesByteRGBA.push_back(idRGBA[3]); } else { quadVerticesFloatRGBA.push_back(rgba[0]); quadVerticesFloatRGBA.push_back(rgba[1]); quadVerticesFloatRGBA.push_back(rgba[2]); quadVerticesFloatRGBA.push_back(rgba[3]); } quadVerticesXYZ.push_back(cellX); quadVerticesXYZ.push_back(cellY); quadVerticesXYZ.push_back(0.0); if (m_identificationModeFlag) { quadVerticesByteRGBA.push_back(idRGBA[0]); quadVerticesByteRGBA.push_back(idRGBA[1]); quadVerticesByteRGBA.push_back(idRGBA[2]); quadVerticesByteRGBA.push_back(idRGBA[3]); } else { quadVerticesFloatRGBA.push_back(rgba[0]); quadVerticesFloatRGBA.push_back(rgba[1]); quadVerticesFloatRGBA.push_back(rgba[2]); quadVerticesFloatRGBA.push_back(rgba[3]); } quadVerticesXYZ.push_back(cellX + cellWidth); quadVerticesXYZ.push_back(cellY); quadVerticesXYZ.push_back(0.0); if (m_identificationModeFlag) { quadVerticesByteRGBA.push_back(idRGBA[0]); quadVerticesByteRGBA.push_back(idRGBA[1]); quadVerticesByteRGBA.push_back(idRGBA[2]); quadVerticesByteRGBA.push_back(idRGBA[3]); } else { quadVerticesFloatRGBA.push_back(rgba[0]); quadVerticesFloatRGBA.push_back(rgba[1]); quadVerticesFloatRGBA.push_back(rgba[2]); quadVerticesFloatRGBA.push_back(rgba[3]); } quadVerticesXYZ.push_back(cellX + cellWidth); quadVerticesXYZ.push_back(cellY + cellHeight); quadVerticesXYZ.push_back(0.0); if (m_identificationModeFlag) { quadVerticesByteRGBA.push_back(idRGBA[0]); quadVerticesByteRGBA.push_back(idRGBA[1]); quadVerticesByteRGBA.push_back(idRGBA[2]); quadVerticesByteRGBA.push_back(idRGBA[3]); } else { quadVerticesFloatRGBA.push_back(rgba[0]); quadVerticesFloatRGBA.push_back(rgba[1]); quadVerticesFloatRGBA.push_back(rgba[2]); quadVerticesFloatRGBA.push_back(rgba[3]); } quadVerticesXYZ.push_back(cellX); quadVerticesXYZ.push_back(cellY + cellHeight); quadVerticesXYZ.push_back(0.0); cellX += cellWidth; } cellY -= cellHeight; } /* * Draw the matrix elements. */ if (m_identificationModeFlag) { CaretAssert((quadVerticesXYZ.size() / 3) == (quadVerticesByteRGBA.size() / 4)); const int32_t numberQuadVertices = static_cast(quadVerticesXYZ.size() / 3); glBegin(GL_QUADS); for (int32_t i = 0; i < numberQuadVertices; i++) { CaretAssertVectorIndex(quadVerticesByteRGBA, i*4 + 3); glColor4ubv(&quadVerticesByteRGBA[i*4]); CaretAssertVectorIndex(quadVerticesXYZ, i*3 + 2); glVertex3fv(&quadVerticesXYZ[i*3]); } glEnd(); } else { /* * 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); CaretAssert((quadVerticesXYZ.size() / 3) == (quadVerticesFloatRGBA.size() / 4)); const int32_t numberQuadVertices = static_cast(quadVerticesXYZ.size() / 3); glBegin(GL_QUADS); for (int32_t i = 0; i < numberQuadVertices; i++) { CaretAssertVectorIndex(quadVerticesFloatRGBA, i*4 + 3); glColor4fv(&quadVerticesFloatRGBA[i*4]); CaretAssertVectorIndex(quadVerticesXYZ, i*3 + 2); glVertex3fv(&quadVerticesXYZ[i*3]); } glEnd(); glDisable(GL_BLEND); /* * Drawn an outline around the matrix elements. */ if (displayGridLinesFlag) { uint8_t gridLineColorBytes[3]; prefs->getBackgroundAndForegroundColors()->getColorChartMatrixGridLines(gridLineColorBytes); float gridLineColorFloats[4]; CaretPreferences::byteRgbToFloatRgb(gridLineColorBytes, gridLineColorFloats); gridLineColorFloats[3] = 1.0; std::vector outlineRGBA; outlineRGBA.reserve(numberQuadVertices * 4); for (int32_t i = 0; i < numberQuadVertices; i++) { outlineRGBA.push_back(gridLineColorFloats[0]); outlineRGBA.push_back(gridLineColorFloats[1]); outlineRGBA.push_back(gridLineColorFloats[2]); outlineRGBA.push_back(gridLineColorFloats[3]); } glPolygonMode(GL_FRONT, GL_LINE); glLineWidth(1.0); glBegin(GL_QUADS); for (int32_t i = 0; i < numberQuadVertices; i++) { CaretAssertVectorIndex(outlineRGBA, i*4 + 3); glColor4fv(&outlineRGBA[i*4]); CaretAssertVectorIndex(quadVerticesXYZ, i*3 + 2); glVertex3fv(&quadVerticesXYZ[i*3]); } glEnd(); } if ( (! selectedRowIndices.empty()) && highlightSelectedRowColumnFlag) { std::vector rowXYZ; std::vector rowRGBA; for (std::set::iterator rowIter = selectedRowIndices.begin(); rowIter != selectedRowIndices.end(); rowIter ++) { const float rowIndex = * rowIter; const float rowY = (numberOfRows - rowIndex - 1) * cellHeight; rowXYZ.push_back(0.0); rowXYZ.push_back(rowY); rowXYZ.push_back(0.0); rowRGBA.push_back(highlightRGB[0]); rowRGBA.push_back(highlightRGB[1]); rowRGBA.push_back(highlightRGB[2]); rowRGBA.push_back(1.0); rowXYZ.push_back(0.0); rowXYZ.push_back(rowY + cellHeight); rowXYZ.push_back(0.0); rowRGBA.push_back(highlightRGB[0]); rowRGBA.push_back(highlightRGB[1]); rowRGBA.push_back(highlightRGB[2]); rowRGBA.push_back(1.0); rowXYZ.push_back(numberOfColumns * cellWidth); rowXYZ.push_back(rowY + cellHeight); rowXYZ.push_back(0.0); rowRGBA.push_back(highlightRGB[0]); rowRGBA.push_back(highlightRGB[1]); rowRGBA.push_back(highlightRGB[2]); rowRGBA.push_back(1.0); rowXYZ.push_back(numberOfColumns * cellWidth); rowXYZ.push_back(rowY); rowXYZ.push_back(0.0); rowRGBA.push_back(highlightRGB[0]); rowRGBA.push_back(highlightRGB[1]); rowRGBA.push_back(highlightRGB[2]); rowRGBA.push_back(1.0); } CaretAssert((rowXYZ.size() / 3) == (rowRGBA.size() / 4)); const int32_t numberOfVertices = static_cast(rowXYZ.size() / 3); const int32_t numberOfQuads = numberOfVertices / 4; CaretAssert((numberOfQuads * 4) == numberOfVertices); /* * As cells get larger, increase linewidth for selected row */ const float highlightLineWidth = std::max(((cellHeight * zooming) * 0.20), 3.0); glLineWidth(highlightLineWidth); for (int32_t iQuad = 0; iQuad < numberOfQuads; iQuad++) { glBegin(GL_LINE_LOOP); for (int32_t iVert = 0; iVert < 4; iVert++) { const int32_t rgbaOffset = (iQuad * 16) + (iVert * 4); CaretAssertVectorIndex(rowRGBA, rgbaOffset + 3); glColor4fv(&rowRGBA[rgbaOffset]); const int32_t xyzOffset = (iQuad * 12) + (iVert * 3); CaretAssertVectorIndex(rowXYZ, xyzOffset + 2); glVertex3fv(&rowXYZ[xyzOffset]); } glEnd(); } glLineWidth(1.0); } if ( (! selectedColumnIndices.empty()) && highlightSelectedRowColumnFlag) { std::vector columnXYZ; std::vector columnRGBA; for (std::set::iterator colIter = selectedColumnIndices.begin(); colIter != selectedColumnIndices.end(); colIter++) { const float columnIndex = *colIter; const float colX = columnIndex * cellWidth; columnXYZ.push_back(colX); columnXYZ.push_back(0.0); columnXYZ.push_back(0.0); columnRGBA.push_back(highlightRGB[0]); columnRGBA.push_back(highlightRGB[1]); columnRGBA.push_back(highlightRGB[2]); columnRGBA.push_back(1.0); columnXYZ.push_back(colX + cellWidth); columnXYZ.push_back(0.0); columnXYZ.push_back(0.0); columnRGBA.push_back(highlightRGB[0]); columnRGBA.push_back(highlightRGB[1]); columnRGBA.push_back(highlightRGB[2]); columnRGBA.push_back(1.0); columnXYZ.push_back(colX + cellWidth); columnXYZ.push_back(numberOfRows * cellHeight); columnXYZ.push_back(0.0); columnRGBA.push_back(highlightRGB[0]); columnRGBA.push_back(highlightRGB[1]); columnRGBA.push_back(highlightRGB[2]); columnRGBA.push_back(1.0); columnXYZ.push_back(colX); columnXYZ.push_back(numberOfRows * cellHeight); columnXYZ.push_back(0.0); columnRGBA.push_back(highlightRGB[0]); columnRGBA.push_back(highlightRGB[1]); columnRGBA.push_back(highlightRGB[2]); columnRGBA.push_back(1.0); } CaretAssert((columnXYZ.size() / 3) == (columnRGBA.size() / 4)); const int32_t numberOfVertices = static_cast(columnXYZ.size() / 3); const int32_t numberOfQuads = numberOfVertices / 4; CaretAssert((numberOfQuads * 4) == numberOfVertices); /* * As cells get larger, increase linewidth for selected row */ const float highlightLineWidth = std::max(((cellHeight * zooming) * 0.20), 3.0); glLineWidth(highlightLineWidth); for (int32_t iQuad = 0; iQuad < numberOfQuads; iQuad++) { glBegin(GL_LINE_LOOP); for (int32_t iVert = 0; iVert < 4; iVert++) { const int32_t rgbaOffset = (iQuad * 16) + (iVert * 4); CaretAssertVectorIndex(columnRGBA, rgbaOffset + 3); glColor4fv(&columnRGBA[rgbaOffset]); const int32_t xyzOffset = (iQuad * 12) + (iVert * 3); CaretAssertVectorIndex(columnXYZ, xyzOffset + 2); glVertex3fv(&columnXYZ[xyzOffset]); } glEnd(); } glLineWidth(1.0); } glPolygonMode(GL_FRONT, GL_FILL); } } } /** * Save the state of OpenGL. * Copied from Qt's qgl.cpp, qt_save_gl_state(). */ void BrainOpenGLChartDrawingFixedPipeline::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 BrainOpenGLChartDrawingFixedPipeline::restoreStateOfOpenGL() { glMatrixMode(GL_TEXTURE); glPopMatrix(); glMatrixMode(GL_PROJECTION); glPopMatrix(); glMatrixMode(GL_MODELVIEW); glPopMatrix(); glPopAttrib(); glPopClientAttrib(); } /** * Add an item for line identification. * * @param chartDataIndex * Index of the chart data. * @param lineIndex * Index of the line. * @param rgbaForColorIdentificationOut * Encoded identification in RGBA color OUTPUT */ void BrainOpenGLChartDrawingFixedPipeline::addToChartLineIdentification(const int32_t chartDataIndex, const int32_t chartLineIndex, uint8_t rgbaForColorIdentificationOut[4]) { const int32_t idIndex = m_identificationIndices.size() / IDENTIFICATION_INDICES_PER_CHART_LINE; m_fixedPipelineDrawing->colorIdentification->addItem(rgbaForColorIdentificationOut, m_chartCartesianSelectionTypeForIdentification, idIndex); rgbaForColorIdentificationOut[3] = 255; /* * If these items change, need to update reset and * processing of identification. */ m_identificationIndices.push_back(chartDataIndex); m_identificationIndices.push_back(chartLineIndex); } /** * Add an item for matrix identification. * * @param matrixRowIndex * Index of the row * @param matrixColumnIndex * Index of the column * @param rgbaForColorIdentificationOut * Encoded identification in RGBA color OUTPUT */ void BrainOpenGLChartDrawingFixedPipeline::addToChartMatrixIdentification(const int32_t matrixRowIndex, const int32_t matrixColumnIndex, uint8_t rgbaForColorIdentificationOut[4]) { const int32_t idIndex = m_identificationIndices.size() / IDENTIFICATION_INDICES_PER_MATRIX_ELEMENT; m_fixedPipelineDrawing->colorIdentification->addItem(rgbaForColorIdentificationOut, m_chartableMatrixSelectionTypeForIdentification, idIndex); rgbaForColorIdentificationOut[3] = 255; /* * If these items change, need to update reset and * processing of identification. */ m_identificationIndices.push_back(matrixRowIndex); m_identificationIndices.push_back(matrixColumnIndex); } /** * Reset identification. */ void BrainOpenGLChartDrawingFixedPipeline::resetIdentification() { m_identificationIndices.clear(); if (m_identificationModeFlag) { const int32_t estimatedNumberOfItems = 1000; m_identificationIndices.reserve(estimatedNumberOfItems); } } /** * Process identification. */ void BrainOpenGLChartDrawingFixedPipeline::processIdentification() { int32_t identifiedItemIndex; float depth = -1.0; if (m_chartModelDataSeriesBeingDrawnForIdentification != NULL) { m_fixedPipelineDrawing->getIndexFromColorSelection(m_chartCartesianSelectionTypeForIdentification, m_fixedPipelineDrawing->mouseX, m_fixedPipelineDrawing->mouseY, identifiedItemIndex, depth); if (identifiedItemIndex >= 0) { const int32_t idIndex = identifiedItemIndex * IDENTIFICATION_INDICES_PER_CHART_LINE; const int32_t chartDataIndex = m_identificationIndices[idIndex]; const int32_t chartLineIndex = m_identificationIndices[idIndex + 1]; SelectionItemChartDataSeries* chartDataSeriesID = m_brain->getSelectionManager()->getChartDataSeriesIdentification(); if (chartDataSeriesID->isOtherScreenDepthCloserToViewer(depth)) { ChartDataCartesian* chartDataCartesian = dynamic_cast(m_chartModelDataSeriesBeingDrawnForIdentification->getChartDataAtIndex(chartDataIndex)); CaretAssert(chartDataCartesian); chartDataSeriesID->setChart(m_chartModelDataSeriesBeingDrawnForIdentification, chartDataCartesian, chartLineIndex); const ChartPoint* chartPoint = chartDataCartesian->getPointAtIndex(chartLineIndex); const float lineXYZ[3] = { chartPoint->getX(), chartPoint->getY(), 0.0 }; m_fixedPipelineDrawing->setSelectedItemScreenXYZ(chartDataSeriesID, lineXYZ); } } } else if (m_chartModelFrequencySeriesBeingDrawnForIdentification != NULL) { m_fixedPipelineDrawing->getIndexFromColorSelection(m_chartCartesianSelectionTypeForIdentification, m_fixedPipelineDrawing->mouseX, m_fixedPipelineDrawing->mouseY, identifiedItemIndex, depth); if (identifiedItemIndex >= 0) { const int32_t idIndex = identifiedItemIndex * IDENTIFICATION_INDICES_PER_CHART_LINE; const int32_t chartDataIndex = m_identificationIndices[idIndex]; const int32_t chartLineIndex = m_identificationIndices[idIndex + 1]; SelectionItemChartFrequencySeries* chartFrequencySeriesID = m_brain->getSelectionManager()->getChartFrequencySeriesIdentification(); if (chartFrequencySeriesID->isOtherScreenDepthCloserToViewer(depth)) { ChartDataCartesian* chartDataCartesian = dynamic_cast(m_chartModelFrequencySeriesBeingDrawnForIdentification->getChartDataAtIndex(chartDataIndex)); CaretAssert(chartDataCartesian); chartFrequencySeriesID->setChart(m_chartModelFrequencySeriesBeingDrawnForIdentification, chartDataCartesian, chartLineIndex); const ChartPoint* chartPoint = chartDataCartesian->getPointAtIndex(chartLineIndex); const float lineXYZ[3] = { chartPoint->getX(), chartPoint->getY(), 0.0 }; m_fixedPipelineDrawing->setSelectedItemScreenXYZ(chartFrequencySeriesID, lineXYZ); } } } else if (m_chartModelTimeSeriesBeingDrawnForIdentification != NULL) { m_fixedPipelineDrawing->getIndexFromColorSelection(m_chartCartesianSelectionTypeForIdentification, m_fixedPipelineDrawing->mouseX, m_fixedPipelineDrawing->mouseY, identifiedItemIndex, depth); if (identifiedItemIndex >= 0) { const int32_t idIndex = identifiedItemIndex * IDENTIFICATION_INDICES_PER_CHART_LINE; const int32_t chartDataIndex = m_identificationIndices[idIndex]; const int32_t chartLineIndex = m_identificationIndices[idIndex + 1]; SelectionItemChartTimeSeries* chartTimeSeriesID = m_brain->getSelectionManager()->getChartTimeSeriesIdentification(); if (chartTimeSeriesID->isOtherScreenDepthCloserToViewer(depth)) { ChartDataCartesian* chartDataCartesian = dynamic_cast(m_chartModelTimeSeriesBeingDrawnForIdentification->getChartDataAtIndex(chartDataIndex)); CaretAssert(chartDataCartesian); chartTimeSeriesID->setChart(m_chartModelTimeSeriesBeingDrawnForIdentification, chartDataCartesian, chartLineIndex); const ChartPoint* chartPoint = chartDataCartesian->getPointAtIndex(chartLineIndex); const float lineXYZ[3] = { chartPoint->getX(), chartPoint->getY(), 0.0 }; m_fixedPipelineDrawing->setSelectedItemScreenXYZ(chartTimeSeriesID, lineXYZ); } } } else if (m_chartableMatrixInterfaceBeingDrawnForIdentification != NULL) { m_fixedPipelineDrawing->getIndexFromColorSelection(m_chartableMatrixSelectionTypeForIdentification, m_fixedPipelineDrawing->mouseX, m_fixedPipelineDrawing->mouseY, identifiedItemIndex, depth); if (identifiedItemIndex >= 0) { const int32_t idIndex = identifiedItemIndex * IDENTIFICATION_INDICES_PER_MATRIX_ELEMENT; const int32_t rowIndex = m_identificationIndices[idIndex]; const int32_t columnIndex = m_identificationIndices[idIndex + 1]; SelectionItemChartMatrix* chartMatrixID = m_brain->getSelectionManager()->getChartMatrixIdentification(); if (chartMatrixID->isOtherScreenDepthCloserToViewer(depth)) { chartMatrixID->setChartMatrix(m_chartableMatrixInterfaceBeingDrawnForIdentification, rowIndex, columnIndex); } } } } connectome-workbench-1.2.3+git41-gc4c6c90/src/Brain/BrainOpenGLChartDrawingFixedPipeline.h000066400000000000000000000171731300200146000310250ustar00rootroot00000000000000#ifndef __BRAIN_OPEN_G_L_CHART_DRAWING_FIXED_PIPELINE_H__ #define __BRAIN_OPEN_G_L_CHART_DRAWING_FIXED_PIPELINE_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 "BrainOpenGLChartDrawingInterface.h" #include "CaretColorEnum.h" namespace caret { class Brain; class BrainOpenGLFixedPipeline; class ChartAxis; class ChartAxisCartesian; class ChartDataCartesian; class ChartModelCartesian; class ChartModelDataSeries; class ChartModelFrequencySeries; class ChartModelTimeSeries; class ChartableMatrixInterface; class BrainOpenGLChartDrawingFixedPipeline : public BrainOpenGLChartDrawingInterface { public: BrainOpenGLChartDrawingFixedPipeline(); virtual ~BrainOpenGLChartDrawingFixedPipeline(); virtual void drawCartesianChart(Brain* brain, BrainOpenGLFixedPipeline* fixedPipelineDrawing, const int32_t viewport[4], BrainOpenGLTextRenderInterface* textRenderer, ChartModelCartesian* cartesianChart, const SelectionItemDataTypeEnum::Enum selectionItemDataType, const int32_t tabIndex); virtual void drawMatrixChart(Brain* brain, BrainOpenGLFixedPipeline* fixedPipelineDrawing, const int32_t viewport[4], BrainOpenGLTextRenderInterface* textRenderer, ChartableMatrixInterface* chartMatrixInterface, const int32_t scalarDataSeriesMapIndex, const SelectionItemDataTypeEnum::Enum selectionItemDataType, const int32_t tabIndex); private: class Margins { public: Margins(const double defaultSize) { m_bottom = defaultSize; m_left = defaultSize; m_right = defaultSize; m_top = defaultSize; } double m_bottom; double m_left; double m_right; double m_top; }; BrainOpenGLChartDrawingFixedPipeline(const BrainOpenGLChartDrawingFixedPipeline&); BrainOpenGLChartDrawingFixedPipeline& operator=(const BrainOpenGLChartDrawingFixedPipeline&); void drawChartGraphicsLineSeries(BrainOpenGLTextRenderInterface* textRenderer, ChartModelCartesian* chart); void drawChartGraphicsMatrix(const int32_t viewport[4], BrainOpenGLTextRenderInterface* textRenderer, ChartableMatrixInterface* chartMatrixInterface, const int32_t scalarDataSeriesMapIndex); void drawChartGraphicsBoxAndSetViewport(const float vpX, const float vpY, const float vpWidth, const float vpHeight, const Margins& margins, int32_t chartGraphicsDrawingViewportOut[4]); void drawChartAxis(const float vpX, const float vpY, const float vpWidth, const float vpHeight, Margins& margins, BrainOpenGLTextRenderInterface* textRenderer, ChartAxis* axis); void drawChartAxisCartesian(const float vpX, const float vpY, const float vpWidth, const float vpHeight, Margins& margins, BrainOpenGLTextRenderInterface* textRenderer, ChartAxisCartesian* axis); void drawChartDataCartesian(const int32_t chartDataIndex, const ChartDataCartesian* chartDataCartesian, const float lineWidth, const float rgb[3]); void estimateCartesianChartAxisLegendsWidthHeight(BrainOpenGLTextRenderInterface* textRenderer, const float viewportHeight, ChartAxis* axis, double& widthOut, double& heightOut); void restoreStateOfOpenGL(); void saveStateOfOpenGL(); void addToChartLineIdentification(const int32_t chartDataIndex, const int32_t lineIndex, uint8_t rgbaForColorIdentification[4]); void addToChartMatrixIdentification(const int32_t matrixRowIndex, const int32_t matrixColumnIndex, uint8_t rgbaForColorIdentification[4]); void resetIdentification(); void processIdentification(); public: // ADD_NEW_METHODS_HERE private: Brain* m_brain; BrainOpenGLFixedPipeline* m_fixedPipelineDrawing; ChartModelDataSeries* m_chartModelDataSeriesBeingDrawnForIdentification; ChartModelFrequencySeries* m_chartModelFrequencySeriesBeingDrawnForIdentification; ChartModelTimeSeries* m_chartModelTimeSeriesBeingDrawnForIdentification; SelectionItemDataTypeEnum::Enum m_chartCartesianSelectionTypeForIdentification; ChartableMatrixInterface* m_chartableMatrixInterfaceBeingDrawnForIdentification; SelectionItemDataTypeEnum::Enum m_chartableMatrixSelectionTypeForIdentification; int32_t m_tabIndex; std::vector m_identificationIndices; bool m_identificationModeFlag; // ADD_NEW_MEMBERS_HERE static const int32_t IDENTIFICATION_INDICES_PER_CHART_LINE; static const int32_t IDENTIFICATION_INDICES_PER_MATRIX_ELEMENT; }; #ifdef __BRAIN_OPEN_G_L_CHART_DRAWING_FIXED_PIPELINE_DECLARE__ const int32_t BrainOpenGLChartDrawingFixedPipeline::IDENTIFICATION_INDICES_PER_CHART_LINE = 2; const int32_t BrainOpenGLChartDrawingFixedPipeline::IDENTIFICATION_INDICES_PER_MATRIX_ELEMENT = 2; #endif // __BRAIN_OPEN_G_L_CHART_DRAWING_FIXED_PIPELINE_DECLARE__ } // namespace #endif //__BRAIN_OPEN_G_L_CHART_DRAWING_FIXED_PIPELINE_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Brain/BrainOpenGLChartDrawingInterface.h000066400000000000000000000105741300200146000301760ustar00rootroot00000000000000#ifndef __BRAIN_OPEN_G_L_CHART_DRAWING_INTERFACE_H__ #define __BRAIN_OPEN_G_L_CHART_DRAWING_INTERFACE_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 "AString.h" #include "SelectionItemDataTypeEnum.h" /** * \class caret::BrainOpenGLTextRenderInterface * \brief Interface for drawing charts with OpenGL. * \ingroup Brain */ namespace caret { class Brain; class BrainOpenGLFixedPipeline; class BrainOpenGLTextRenderInterface; class ChartModelCartesian; class ChartableMatrixInterface; class BrainOpenGLChartDrawingInterface { public: BrainOpenGLChartDrawingInterface() { } virtual ~BrainOpenGLChartDrawingInterface() { } /** * Draw a cartesian chart in the given viewport. * * @param brain * Brain. * @param fixedPipelineDrawing * The fixed pipeline OpenGL drawing. * @param viewport * Viewport for the chart. * @param textRenderer * Text rendering. * @param cartesianChart * Cartesian Chart that is drawn. * @param selectionItemDataType * Selected data type. * @param tabIndex * Index of the tab. */ virtual void drawCartesianChart(Brain* brain, BrainOpenGLFixedPipeline* fixedPipelineDrawing, const int32_t viewport[4], BrainOpenGLTextRenderInterface* textRenderer, ChartModelCartesian* cartesianChart, const SelectionItemDataTypeEnum::Enum selectionItemDataType, const int32_t tabIndex) = 0; /** * Draw a matrix chart in the given viewport. * * @param brain * Brain. * @param fixedPipelineDrawing * The fixed pipeline OpenGL drawing. * @param viewport * Viewport for the chart. * @param textRenderer * Text rendering. * @param chartMatrixInterface * Chart matrix interface containing matrix data. * @param scalarDataSeriesMapIndex * Scalar data series selected map index. * @param selectionItemDataType * Selected data type. * @param tabIndex * Index of the tab. */ virtual void drawMatrixChart(Brain* brain, BrainOpenGLFixedPipeline* fixedPipelineDrawing, const int32_t viewport[4], BrainOpenGLTextRenderInterface* textRenderer, ChartableMatrixInterface* chartMatrixInterface, const int32_t scalarDataSeriesMapIndex, const SelectionItemDataTypeEnum::Enum selectionItemDataType, const int32_t tabIndex) = 0; private: BrainOpenGLChartDrawingInterface(const BrainOpenGLChartDrawingInterface&); BrainOpenGLChartDrawingInterface& operator=(const BrainOpenGLChartDrawingInterface&); public: // ADD_NEW_METHODS_HERE private: // ADD_NEW_MEMBERS_HERE }; #ifdef __BRAIN_OPEN_G_L_CHART_DRAWING_INTERFACE_DECLARE__ // #endif // __BRAIN_OPEN_G_L_CHART_DRAWING_INTERFACE_DECLARE__ } // namespace #endif //__BRAIN_OPEN_G_L_CHART_DRAWING_INTERFACE_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Brain/BrainOpenGLFixedPipeline.cxx000066400000000000000000011002671300200146000271000ustar00rootroot00000000000000 /*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 "CaretOpenGLInclude.h" #define __BRAIN_OPENGL_FIXED_PIPELINE_DEFINE_H #include "BrainOpenGLFixedPipeline.h" #undef __BRAIN_OPENGL_FIXED_PIPELINE_DEFINE_H #include #include #include #include #include #include "AnnotationColorBar.h" #include "AnnotationManager.h" #include "AnnotationPointSizeText.h" #include "Border.h" #include "BorderFile.h" #include "Brain.h" #include "BrainOpenGLAnnotationDrawingFixedPipeline.h" #include "BrainOpenGLChartDrawingFixedPipeline.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" #include "BrowserTabContent.h" #include "BoundingBox.h" #include "CaretAssert.h" #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 "CiftiBrainordinateLabelFile.h" #include "CiftiFiberOrientationFile.h" #include "CiftiFiberTrajectoryFile.h" #include "ClippingPlaneGroup.h" #include "ControlPointFile.h" #include "ControlPoint3D.h" #include "DeveloperFlagsEnum.h" #include "DisplayGroupEnum.h" #include "DisplayPropertiesAnnotation.h" #include "DisplayPropertiesBorders.h" #include "DisplayPropertiesFiberOrientation.h" #include "DisplayPropertiesFoci.h" #include "DisplayPropertiesImages.h" #include "DisplayPropertiesLabels.h" #include "DisplayPropertiesSurface.h" #include "DisplayPropertiesVolume.h" #include "ElapsedTimer.h" #include "EventAnnotationColorBarGet.h" #include "EventManager.h" #include "EventModelSurfaceGet.h" #include "EventNodeIdentificationColorsGetFromCharts.h" #include "EventPaletteGetByName.h" #include "FastStatistics.h" #include "Fiber.h" #include "FiberOrientation.h" #include "FiberOrientationTrajectory.h" #include "FiberTrajectoryMapProperties.h" #include "FociFile.h" #include "Focus.h" #include "GapsAndMargins.h" #include "GiftiLabel.h" #include "GiftiLabelTable.h" #include "GroupAndNameHierarchyModel.h" #include "IdentifiedItemNode.h" #include "IdentificationManager.h" #include "ImageFile.h" #include "Matrix4x4.h" #include "SelectionItemBorderSurface.h" #include "SelectionItemFocusSurface.h" #include "SelectionItemFocusVolume.h" #include "SelectionItemImage.h" #include "SelectionItemImageControlPoint.h" #include "SelectionItemSurfaceNode.h" #include "SelectionItemSurfaceNodeIdentificationSymbol.h" #include "SelectionItemSurfaceTriangle.h" #include "SelectionItemVoxel.h" #include "SurfaceMontageConfigurationCerebellar.h" #include "SurfaceMontageConfigurationCerebral.h" #include "SurfaceMontageConfigurationFlatMaps.h" #include "IdentificationWithColor.h" #include "SelectionManager.h" #include "MathFunctions.h" #include "ModelChart.h" #include "ModelSurface.h" #include "ModelSurfaceMontage.h" #include "ModelVolume.h" #include "ModelWholeBrain.h" #include "NodeAndVoxelColoring.h" #include "Overlay.h" #include "OverlaySet.h" #include "Palette.h" #include "PaletteColorMapping.h" #include "PaletteFile.h" #include "PaletteScalarAndColor.h" #include "Plane.h" #include "SessionManager.h" #include "Surface.h" #include "SurfaceMontageViewport.h" #include "SurfaceNodeColoring.h" #include "SurfaceProjectedItem.h" #include "SurfaceProjectionBarycentric.h" #include "SurfaceProjectionVanEssen.h" #include "SurfaceSelectionModel.h" #include "TopologyHelper.h" #include "VolumeFile.h" #include "VolumeMappableInterface.h" #include "VolumeSurfaceOutlineColorOrTabModel.h" #include "VolumeSurfaceOutlineModel.h" #include "VolumeSurfaceOutlineSetModel.h" using namespace caret; /* * When true, */ static bool drawTabAnnotationsAfterTabContentFlag = true; /** * Constructor. * * @param parentGLWidget * 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) { 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; m_tileTabsActiveFlag = false; setTabViewport(NULL); } /** * Destructor. */ BrainOpenGLFixedPipeline::~BrainOpenGLFixedPipeline() { if (m_shapeSphere != NULL) { delete m_shapeSphere; m_shapeSphere = NULL; } if (m_shapeCone != NULL) { delete m_shapeCone; m_shapeCone = NULL; } if (m_shapeCylinder != NULL) { delete m_shapeCylinder; m_shapeCylinder = NULL; } if (m_shapeCube != NULL) { delete m_shapeCube; m_shapeCube = NULL; } if (m_shapeCubeRounded != NULL) { 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; } /** * Selection on a model. * * @param brain * The brain (must be valid!) * @param viewportConent * 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 BrainOpenGLFixedPipeline::selectModel(Brain* brain, BrainOpenGLViewportContent* viewportContent, const int32_t mouseX, const int32_t mouseY, const bool applySelectionBackgroundFiltering) { m_brain = brain; CaretAssert(m_brain); setTabViewport(viewportContent); std::vector viewportContentsVector; viewportContentsVector.push_back(viewportContent); setAnnotationColorBarsForDrawing(viewportContentsVector); m_clippingPlaneGroup = NULL; this->inverseRotationMatrixValid = false; /* * For identification, set the background * to white. */ glClearColor(1.0, 1.0, 1.0, 0.0); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); this->mouseX = mouseX; this->mouseY = mouseY; this->colorIdentification->reset(); 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); } int windowViewport[4]; viewportContent->getWindowViewport(windowViewport); drawWindowAnnotations(windowViewport); m_brain->getSelectionManager()->filterSelections(applySelectionBackgroundFiltering); m_brain = 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 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 * Contains projection result upon exit. */ void BrainOpenGLFixedPipeline::projectToModel(Brain* brain, BrainOpenGLViewportContent* viewportContent, const int32_t mouseX, const int32_t mouseY, SurfaceProjectedItem& projectionOut) { m_brain = brain; CaretAssert(m_brain); setTabViewport(viewportContent); m_clippingPlaneGroup = NULL; m_brain->getSelectionManager()->reset(); this->modeProjectionData = &projectionOut; this->modeProjectionData->reset(); this->modeProjectionScreenDepth = std::numeric_limits::max(); /* * For projection which uses colors for finding triangles, * set the background to white. */ glClearColor(1.0, 1.0, 1.0, 0.0); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); this->mouseX = mouseX; this->mouseY = mouseY; this->colorIdentification->reset(); this->drawModelInternal(MODE_PROJECTION, viewportContent); this->modeProjectionData = NULL; m_brain = NULL; } /** * Update the foreground and background colors using the model in * the given viewport content. */ void BrainOpenGLFixedPipeline::updateForegroundAndBackgroundColors(BrainOpenGLViewportContent* vpContent) { /* * Default to colors for surface */ CaretPreferences* prefs = SessionManager::get()->getCaretPreferences(); prefs->getBackgroundAndForegroundColors()->getColorForegroundSurfaceView(m_foregroundColorByte); m_foregroundColorByte[3] = 255; prefs->getBackgroundAndForegroundColors()->getColorBackgroundSurfaceView(m_backgroundColorByte); m_backgroundColorByte[3] = 255; if (vpContent != NULL) { const BrowserTabContent* btc = vpContent->getBrowserTabContent(); if (btc != NULL) { const Model* model = btc->getModelForDisplay(); if (model != NULL) { switch (model->getModelType()) { case ModelTypeEnum::MODEL_TYPE_CHART: prefs->getBackgroundAndForegroundColors()->getColorForegroundChartView(m_foregroundColorByte); prefs->getBackgroundAndForegroundColors()->getColorBackgroundChartView(m_backgroundColorByte); break; case ModelTypeEnum::MODEL_TYPE_INVALID: break; case ModelTypeEnum::MODEL_TYPE_SURFACE: prefs->getBackgroundAndForegroundColors()->getColorForegroundSurfaceView(m_foregroundColorByte); prefs->getBackgroundAndForegroundColors()->getColorBackgroundSurfaceView(m_backgroundColorByte); break; case ModelTypeEnum::MODEL_TYPE_SURFACE_MONTAGE: prefs->getBackgroundAndForegroundColors()->getColorForegroundSurfaceView(m_foregroundColorByte); prefs->getBackgroundAndForegroundColors()->getColorBackgroundSurfaceView(m_backgroundColorByte); break; case ModelTypeEnum::MODEL_TYPE_VOLUME_SLICES: prefs->getBackgroundAndForegroundColors()->getColorForegroundVolumeView(m_foregroundColorByte); prefs->getBackgroundAndForegroundColors()->getColorBackgroundVolumeView(m_backgroundColorByte); break; case ModelTypeEnum::MODEL_TYPE_WHOLE_BRAIN: prefs->getBackgroundAndForegroundColors()->getColorForegroundAllView(m_foregroundColorByte); prefs->getBackgroundAndForegroundColors()->getColorBackgroundAllView(m_backgroundColorByte); break; } } } m_foregroundColorByte[3] = 255; m_backgroundColorByte[3] = 255; } CaretPreferences::byteRgbToFloatRgb(m_backgroundColorByte, m_backgroundColorFloat); m_backgroundColorFloat[3] = 1.0; CaretPreferences::byteRgbToFloatRgb(m_foregroundColorByte, m_foregroundColorFloat); m_foregroundColorFloat[3] = 1.0; } /** * Set the viewport for the tab content that is being drawn. * * @param vpContent * The viewport content (may be NULL). */ void BrainOpenGLFixedPipeline::setTabViewport(const BrainOpenGLViewportContent* vpContent) { m_tabViewport[0] = 0; m_tabViewport[1] = 0; m_tabViewport[2] = 0; m_tabViewport[3] = 0; if (vpContent != NULL) { vpContent->getTabViewportBeforeApplyingMargins(m_tabViewport); } } /** * Get colorbars in window space. * * @param viewportContents * Contents of the viewports. * @param windowColorBarsOut * Output with window color bars in the viewports. */ void BrainOpenGLFixedPipeline::setAnnotationColorBarsForDrawing(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); EventManager::get()->sendEvent(colorBarEvent.getPointer()); m_annotationColorBarsForDrawing = colorBarEvent.getAnnotationColorBars(); } /** * Draw models in their respective viewports. * * @param brain * The brain (must be valid!) * @param viewportContents * Viewport info for drawing. */ void BrainOpenGLFixedPipeline::drawModels(Brain* brain, std::vector& viewportContents) { m_brain = brain; CaretAssert(m_brain); setTabViewport(NULL); setAnnotationColorBarsForDrawing(viewportContents); m_tileTabsActiveFlag = (viewportContents.size() > 1); this->inverseRotationMatrixValid = false; m_clippingPlaneGroup = NULL; this->checkForOpenGLError(NULL, "At beginning of drawModels()"); /* * Default the background colors to first model * NOTE: If there are no models, the surface background color is used */ if (viewportContents.empty()) { updateForegroundAndBackgroundColors(NULL); } else { updateForegroundAndBackgroundColors(viewportContents[0]); } /* * Use the background color as the clear color. */ uint8_t clearColorByte[3] = { m_backgroundColorByte[0], m_backgroundColorByte[1], m_backgroundColorByte[2], }; float clearColorFloat[3]; CaretPreferences::byteRgbToFloatRgb(clearColorByte, clearColorFloat); glClearColor(clearColorFloat[0], clearColorFloat[1], clearColorFloat[2], 1.0); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); this->checkForOpenGLError(NULL, "At middle of drawModels()"); for (int32_t i = 0; i < static_cast(viewportContents.size()); i++) { /* * Viewport of window. */ BrainOpenGLViewportContent* vpContent = viewportContents[i]; setTabViewport(vpContent); glViewport(m_tabViewport[0], m_tabViewport[1], m_tabViewport[2], m_tabViewport[3]); /* * Update foreground and background colors for model */ updateForegroundAndBackgroundColors(vpContent); /* * If this is NOT the first viewport content, * AND the background color for this viewport content is * different that the clear color, THEN * draw a rectangle with the background color. */ if (i > 0) { if ((m_backgroundColorByte[0] != clearColorByte[0]) || (m_backgroundColorByte[1] != clearColorByte[1]) || (m_backgroundColorByte[2] != clearColorByte[2])) { GLboolean depthEnabledFlag; glGetBooleanv(GL_DEPTH_TEST, &depthEnabledFlag); glDisable(GL_DEPTH_TEST); glMatrixMode(GL_PROJECTION); glLoadIdentity(); glOrtho(0.0, 1.0, 0.0, 1.0, -1.0, 1.0); glMatrixMode(GL_MODELVIEW); glLoadIdentity(); glColor3ubv(m_backgroundColorByte); glBegin(GL_POLYGON); glVertex2f(0.0, 0.0); glVertex2f(1.0, 0.0); glVertex2f(1.0, 1.0); glVertex2f(0.0, 1.0); glEnd(); if (depthEnabledFlag) { glEnable(GL_DEPTH_TEST); } } } this->drawModelInternal(MODE_DRAWING, vpContent); /* * Draw border in foreground color around tab that is highlighted * 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); } } 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++) { /* * Viewport of window. */ BrainOpenGLViewportContent* vpContent = viewportContents[i]; setTabViewport(vpContent); /* * Update foreground and background colors for model */ updateForegroundAndBackgroundColors(vpContent); drawTabAnnotations(vpContent); //m_tabViewport); } } /* * Draw window viewport annotations */ int windowViewport[4]; viewportContents[0]->getWindowViewport(windowViewport); CaretAssert(m_windowIndex == viewportContents[0]->getWindowIndex()); drawWindowAnnotations(windowViewport); } this->checkForOpenGLError(NULL, "At end of drawModels()"); m_brain = NULL; } /** * Draw the tab annotations. * * @param windowViewport * Viewport (x, y, w, h). */ void BrainOpenGLFixedPipeline::drawTabAnnotations(BrainOpenGLViewportContent* tabContent) // const int tabViewport[4]) { if (tabContent->getBrowserTabContent() == NULL) { return; } int tabViewport[4]; tabContent->getModelViewport(tabViewport); CaretAssertMessage(m_brain, "m_brain must NOT be NULL for drawing window annotations."); glViewport(tabViewport[0], tabViewport[1], tabViewport[2], tabViewport[3]); glMatrixMode(GL_PROJECTION); glPushMatrix(); glOrtho(0.0, tabViewport[2], 0.0, tabViewport[3], -1.0, 1.0); glMatrixMode(GL_MODELVIEW); glPushMatrix(); glLoadIdentity(); CaretAssert(m_windowIndex == tabContent->getWindowIndex()); this->browserTabContent = tabContent->getBrowserTabContent(); m_clippingPlaneGroup = const_cast(tabContent->getBrowserTabContent()->getClippingPlaneGroup()); CaretAssert(m_clippingPlaneGroup); this->windowTabIndex = this->browserTabContent->getTabNumber(); 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, NULL); glPopMatrix(); glMatrixMode(GL_PROJECTION); glPopMatrix(); glMatrixMode(GL_MODELVIEW); } /** * Draw the window annotations. * * @param windowViewport * Viewport (x, y, w, h). */ void BrainOpenGLFixedPipeline::drawWindowAnnotations(const int windowViewport[4]) { CaretAssertMessage(m_brain, "m_brain must NOT be NULL for drawing window annotations."); /* * 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; } } glViewport(windowViewport[0], windowViewport[1], windowViewport[2], windowViewport[3]); glMatrixMode(GL_PROJECTION); glPushMatrix(); glOrtho(0.0, windowViewport[2], 0.0, windowViewport[3], -1.0, 1.0); glMatrixMode(GL_MODELVIEW); glPushMatrix(); glLoadIdentity(); for (std::vector::iterator iter = m_annotationColorBarsForDrawing.begin(); iter != m_annotationColorBarsForDrawing.end(); iter++) { AnnotationColorBar* cb = *iter; CaretAssert(cb); cb->setWindowIndex(m_windowIndex); } /* * No valid tab */ this->windowTabIndex = -1; 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, NULL); glPopMatrix(); glMatrixMode(GL_PROJECTION); glPopMatrix(); glMatrixMode(GL_MODELVIEW); } /** * Draw a model. * * @param mode * The mode of operations (draw, select, etc). * @param viewportContent * Viewport contents for drawing. */ void BrainOpenGLFixedPipeline::drawModelInternal(Mode mode, BrainOpenGLViewportContent* viewportContent) { ElapsedTimer et; et.start(); CaretAssert(m_windowIndex == viewportContent->getWindowIndex()); this->windowTabIndex = -1; this->browserTabContent = viewportContent->getBrowserTabContent(); m_mirroredClippingEnabled = false; Model* model = NULL; this->mode = mode; // drawBackgroundImage(viewportContent); if (this->browserTabContent != NULL) { m_clippingPlaneGroup = const_cast(this->browserTabContent->getClippingPlaneGroup()); CaretAssert(m_clippingPlaneGroup); Model* model = this->browserTabContent->getModelForDisplay(); this->windowTabIndex = this->browserTabContent->getTabNumber(); int viewport[4]; viewportContent->getModelViewport(viewport); this->mode = mode; this->checkForOpenGLError(model, "At beginning of drawModelInternal()"); if(model != NULL) { CaretAssert((this->windowTabIndex >= 0) && (this->windowTabIndex < BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS)); bool modelAllowsPalettes = true; ModelChart* modelChart = 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; } else if (surfaceModel != NULL) { m_mirroredClippingEnabled = true; this->drawSurfaceModel(surfaceModel, viewport); } else if (surfaceMontageModel != NULL) { m_mirroredClippingEnabled = true; this->drawSurfaceMontageModel(browserTabContent, surfaceMontageModel, viewport); } else if (volumeModel != NULL) { this->drawVolumeModel(browserTabContent, volumeModel, viewport); } else if (wholeBrainModel != NULL) { this->drawWholeBrainModel(browserTabContent, 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); glFlush(); this->checkForOpenGLError(model, "At end of drawModelInternal()"); if (model != NULL) { CaretLogFine("Time to draw " + model->getNameForGUI(false) + " was " + AString::number(et.getElapsedTimeSeconds()) + " seconds"); } } /** * @return Get the texture manager. */ BrainOpenGLTextureManager* BrainOpenGLFixedPipeline::getTextureManager() { BrainOpenGLTextureManager* tm = m_textureManager.getPointer(); CaretAssert(tm); return tm; } /** * Set the viewport. * * @param viewport * Values for viewport (x, y, x-size, y-size) * @param projectionType * Type of view projection. */ void BrainOpenGLFixedPipeline::setViewportAndOrthographicProjection(const int32_t viewport[4], const ProjectionViewTypeEnum::Enum projectionType) { glViewport(viewport[0], viewport[1], viewport[2], viewport[3]); glMatrixMode(GL_PROJECTION); glLoadIdentity(); this->setOrthographicProjection(viewport, projectionType); glMatrixMode(GL_MODELVIEW); } /** * Set the viewport for a volume. * * @param viewport * Values for viewport (x, y, x-size, y-size) * @param projectionType * Type of view projection. * @param volume * Volume for use in setting orthographic projection. */ void BrainOpenGLFixedPipeline::setViewportAndOrthographicProjectionForWholeBrainVolume(const int32_t viewport[4], const ProjectionViewTypeEnum::Enum projectionType, const VolumeMappableInterface* volume) { CaretAssert(volume); glViewport(viewport[0], viewport[1], viewport[2], viewport[3]); BoundingBox boundingBox; volume->getVoxelSpaceBoundingBox(boundingBox); glMatrixMode(GL_PROJECTION); glLoadIdentity(); setOrthographicProjectionForWithBoundingBox(viewport, projectionType, &boundingBox); glMatrixMode(GL_MODELVIEW); } /** * Set the viewport for a surface file. * * @param viewport * Values for viewport (x, y, x-size, y-size) * @param projectionType * Type of view projection. * @param surfaceFile * Surface file for use in setting orthographic projection. */ void BrainOpenGLFixedPipeline::setViewportAndOrthographicProjectionForSurfaceFile(const int32_t viewport[4], const ProjectionViewTypeEnum::Enum projectionType, const SurfaceFile* surfaceFile) { CaretAssert(surfaceFile); glViewport(viewport[0], viewport[1], viewport[2], viewport[3]); glMatrixMode(GL_PROJECTION); glLoadIdentity(); setOrthographicProjectionForWithBoundingBox(viewport, projectionType, surfaceFile->getBoundingBox()); glMatrixMode(GL_MODELVIEW); } /** * Disable clipping planes. */ void BrainOpenGLFixedPipeline::disableClippingPlanes() { glDisable(GL_CLIP_PLANE0); glDisable(GL_CLIP_PLANE1); glDisable(GL_CLIP_PLANE2); glDisable(GL_CLIP_PLANE3); glDisable(GL_CLIP_PLANE4); glDisable(GL_CLIP_PLANE5); } /** * Apply the clipping planes for the given data type and structure. * * @param clippingDataType * Type of data that is being clipped. * @param structureIn * The structure. */ void BrainOpenGLFixedPipeline::applyClippingPlanes(const ClippingDataType clippingDataType, const StructureEnum::Enum structureIn) { disableClippingPlanes(); StructureEnum::Enum structure = StructureEnum::CORTEX_LEFT; if (m_mirroredClippingEnabled) { structure = structureIn; } if (browserTabContent == NULL) { return; } CaretAssert(m_clippingPlaneGroup); float rotation[3]; m_clippingPlaneGroup->getRotationAngles(rotation); float panning[3]; m_clippingPlaneGroup->getTranslationForStructure(structure, panning); float rotationAngles[3]; m_clippingPlaneGroup->getRotationAngles(rotationAngles); float thickness[3]; m_clippingPlaneGroup->getThickness(thickness); float minX = -thickness[0] / 2.0; float maxX = thickness[0] / 2.0; float minY = -thickness[1] / 2.0; float maxY = thickness[1] / 2.0; float minZ = -thickness[2] / 2.0; float maxZ = thickness[2] / 2.0; GLfloat rotMatrix[16]; m_clippingPlaneGroup->getRotationMatrixForStructure(structure).getMatrixForOpenGL(rotMatrix); if (m_clippingPlaneGroup->isDisplayClippingBoxSelected()) { glColor3f(1.0, 0.0, 0.0); glLineWidth(2.0); glPushMatrix(); glTranslatef(panning[0], panning[1], panning[2]); glMultMatrixf(rotMatrix); glBegin(GL_LINE_LOOP); glVertex3f(minX, minY, minZ); glVertex3f(maxX, minY, minZ); glVertex3f(maxX, maxY, minZ); glVertex3f(minX, maxY, minZ); glEnd(); glBegin(GL_LINE_LOOP); glVertex3f(minX, minY, maxZ); glVertex3f(maxX, minY, maxZ); glVertex3f(maxX, maxY, maxZ); glVertex3f(minX, maxY, maxZ); glEnd(); glBegin(GL_LINES); glVertex3f(minX, minY, minZ); glVertex3f(minX, minY, maxZ); glVertex3f(maxX, minY, minZ); glVertex3f(maxX, minY, maxZ); glVertex3f(maxX, maxY, minZ); glVertex3f(maxX, maxY, maxZ); glVertex3f(minX, maxY, minZ); glVertex3f(minX, maxY, maxZ); glEnd(); glPopMatrix(); } switch (clippingDataType) { case CLIPPING_DATA_TYPE_FEATURES: if (! m_clippingPlaneGroup->isFeaturesSelected()) { return; } break; case CLIPPING_DATA_TYPE_SURFACE: if (! m_clippingPlaneGroup->isSurfaceSelected()) { return; } break; case CLIPPING_DATA_TYPE_VOLUME: if (! m_clippingPlaneGroup->isVolumeSelected()) { return; } break; } std::vector planes = m_clippingPlaneGroup->getActiveClippingPlanesForStructure(structure); const int32_t numPlanes = static_cast(planes.size()); for (int32_t i = 0; i < numPlanes; i++) { const Plane* p = planes[i]; double a, b, c, d; p->getPlane(a, b, c, d); const GLdouble abcd[4] = { a, b, c, d }; glClipPlane(GL_CLIP_PLANE0 + i, abcd); glEnable(GL_CLIP_PLANE0 + i); } } /** * Is the coordinate inside the clipping planes? * * If a clipping plane for an axis is off, the coordinate is considered * to be inside the clipping plane. * * @param structureIn * The structure. Note that right and left hemispheres are mirror flipped. * @param xyz * The coordinate. * * @return * True if inside the clipping planes, else false. */ bool BrainOpenGLFixedPipeline::isCoordinateInsideClippingPlanesForStructure(const StructureEnum::Enum structureIn, const float xyz[3]) const { CaretAssert(m_clippingPlaneGroup); StructureEnum::Enum structure = StructureEnum::CORTEX_LEFT; if (m_mirroredClippingEnabled) { structure = structureIn; } return m_clippingPlaneGroup->isCoordinateInsideClippingPlanesForStructure(structure, xyz); } /** * @return Is clipping of features enabled? */ bool BrainOpenGLFixedPipeline::isFeatureClippingEnabled() const { CaretAssert(m_clippingPlaneGroup); return m_clippingPlaneGroup->isFeaturesAndAnyAxisSelected(); } /** * Apply the viewing transformations for the content of the browser tab. * * @param model * Model that is being drawn. * @param objectCenterXYZ * If not NULL, contains center of object about * which rotation should take place. * @param projectionViewType * Projection view type. */ void BrainOpenGLFixedPipeline::applyViewingTransformations(const Model* model, const float objectCenterXYZ[3], const ProjectionViewTypeEnum::Enum projectionViewType) { CaretAssert(model); glMatrixMode(GL_MODELVIEW); glLoadIdentity(); float eyeX = 0.0; float eyeY = 0.0; float eyeZ = 0.0; float centerX = 0.0; float centerY = 0.0; float centerZ = 0.0; float upX = 0.0; float upY = 0.0; float upZ = 0.0; bool useGluLookAt = false; bool rightCortexFlatFlag = false; switch (projectionViewType) { case ProjectionViewTypeEnum::PROJECTION_VIEW_CEREBELLUM_ANTERIOR: case ProjectionViewTypeEnum::PROJECTION_VIEW_CEREBELLUM_DORSAL: case ProjectionViewTypeEnum::PROJECTION_VIEW_CEREBELLUM_POSTERIOR: case ProjectionViewTypeEnum::PROJECTION_VIEW_CEREBELLUM_VENTRAL: eyeZ = BrainOpenGLFixedPipeline::s_gluLookAtCenterFromEyeOffsetDistance; //5.0; upY = 1.0; useGluLookAt = true; break; case ProjectionViewTypeEnum::PROJECTION_VIEW_CEREBELLUM_FLAT_SURFACE: eyeZ = BrainOpenGLFixedPipeline::s_gluLookAtCenterFromEyeOffsetDistance; //5.0; upY = 1.0; useGluLookAt = true; break; case ProjectionViewTypeEnum::PROJECTION_VIEW_LEFT_LATERAL: break; case ProjectionViewTypeEnum::PROJECTION_VIEW_LEFT_MEDIAL: break; case ProjectionViewTypeEnum::PROJECTION_VIEW_LEFT_FLAT_SURFACE: break; case ProjectionViewTypeEnum::PROJECTION_VIEW_RIGHT_LATERAL: break; case ProjectionViewTypeEnum::PROJECTION_VIEW_RIGHT_MEDIAL: break; case ProjectionViewTypeEnum::PROJECTION_VIEW_RIGHT_FLAT_SURFACE: if (model->getModelType() == ModelTypeEnum::MODEL_TYPE_SURFACE_MONTAGE) { const ModelSurfaceMontage* surfaceMontageModel = dynamic_cast(model); CaretAssert(surfaceMontageModel); if (surfaceMontageModel != NULL) { if (surfaceMontageModel->getSelectedConfigurationType(this->windowTabIndex) == SurfaceMontageConfigurationTypeEnum::FLAT_CONFIGURATION) { rightCortexFlatFlag = true; } } } break; } if (useGluLookAt) { gluLookAt(eyeX, eyeY, eyeZ, centerX, centerY, centerZ, upX, upY, upZ); } float translation[3]; double rotationMatrixElements[16]; float scaling; browserTabContent->getTransformationsForOpenGLDrawing(projectionViewType, translation, rotationMatrixElements, scaling); glTranslatef(translation[0], 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); /* * Save the inverse rotation matrix which may be used * later by some drawing functions. */ Matrix4x4 inverseMatrix; inverseMatrix.setMatrixFromOpenGL(rotationMatrixElements); this->inverseRotationMatrixValid = inverseMatrix.invert(); if (this->inverseRotationMatrixValid) { inverseMatrix.getMatrixForOpenGL(this->inverseRotationMatrix); } glScalef(scaling, scaling, scaling); 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); const float rightFlatMapZoomFactor = browserTabContent->getRightCortexFlatMapZoomFactor(); glScalef(rightFlatMapZoomFactor, rightFlatMapZoomFactor, rightFlatMapZoomFactor); } if (objectCenterXYZ != NULL) { /* * Place center of surface at origin. */ glTranslatef(-objectCenterXYZ[0], -objectCenterXYZ[1], -objectCenterXYZ[2]); } } /** * For a volume, get translation and scaling so that the volume 'fills' * the window. * * @param volume * The volume. * @param sliceViewPlane * The slice viewing plane. * @param orthographicExtent * The orthographic bounds * @param translationOut * Output of translation. * @param scalingOut * Output of scaling. * */ void BrainOpenGLFixedPipeline::getVolumeFitToWindowScalingAndTranslation(const VolumeMappableInterface* volume, const VolumeSliceViewPlaneEnum::Enum sliceViewPlane, const double orthographicExtent[6], float translationOut[3], float& scalingOut) const { /* * Apply some scaling and translation so that the volume slice, by default * is not larger than the window in which it is being viewed. */ scalingOut = 1.0; translationOut[0] = 0.0; translationOut[1] = 0.0; translationOut[2] = 0.0; if (volume != NULL) { BoundingBox boundingBox; volume->getVoxelSpaceBoundingBox(boundingBox); int64_t dimI, dimJ, dimK, numMaps, numComponents; volume->getDimensions(dimI, dimJ, dimK, numMaps, numComponents); if ((dimI > 2) && (dimJ > 2) && (dimK > 2)) { float volumeCenter[3] = { (boundingBox.getMinX() + boundingBox.getMaxX()) / 2, (boundingBox.getMinY() + boundingBox.getMaxY()) / 2, (boundingBox.getMinZ() + boundingBox.getMaxZ()) / 2 }; /* * Translate so that the center voxel (by dimenisons) * is at the center of the screen. */ translationOut[0] = -volumeCenter[0]; translationOut[1] = -volumeCenter[1]; translationOut[2] = -volumeCenter[2]; switch (sliceViewPlane) { case VolumeSliceViewPlaneEnum::ALL: break; case VolumeSliceViewPlaneEnum::AXIAL: translationOut[2] = 0.0; break; case VolumeSliceViewPlaneEnum::CORONAL: translationOut[1] = 0.0; break; case VolumeSliceViewPlaneEnum::PARASAGITTAL: translationOut[0] = 0.0; break; } /* * Scale so volume fills, but does not extend out of window. */ const float xExtent = (boundingBox.getMaxX() - boundingBox.getMinX()) / 2; const float yExtent = (boundingBox.getMaxY() - boundingBox.getMinY()) / 2; const float zExtent = (boundingBox.getMaxZ() - boundingBox.getMinZ()) / 2; const float orthoExtentX = std::min(std::fabs(orthographicExtent[0]), std::fabs(orthographicExtent[1])); const float orthoExtentY = std::min(std::fabs(orthographicExtent[2]), std::fabs(orthographicExtent[3])); float temp; float scaleWindowX = (orthoExtentX / xExtent); 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 if (temp < scaleWindowY) scaleWindowY = temp; scalingOut = std::min(scaleWindowX, scaleWindowY); scalingOut *= 0.98; } } } void BrainOpenGLFixedPipeline::initializeMembersBrainOpenGL() { this->initializedOpenGLFlag = false; this->modeProjectionData = NULL; } /** * Initialize OpenGL. */ void BrainOpenGLFixedPipeline::initializeOpenGL() { if (s_staticInitialized == false) { s_staticInitialized = true; BrainOpenGL::initializeOpenGL(); } glEnable(GL_DEPTH_TEST); glDepthFunc(GL_LEQUAL); glClearDepth(1.0); glFrontFace(GL_CCW); /* * As normal vectors are multiplied by transformation matrices, their * lengths may no longer be one and cause drawing errors. * GL_NORMALIZE will rescale normal vectors to one to prevent this problem. * GL_RESCALE_NORMAL was added in later versions of OpenGL and * is reported to be more efficient. However, GL_RESCALE_NORMAL * does not seem to work with OpenGL 4.2 on Linux, whereas GL_NORMALIZE * seems to work on all operating systems and versions of OpenGL. */ glEnable(GL_NORMALIZE); /* * Avoid drawing backfacing polygons */ glCullFace(GL_BACK); glEnable(GL_CULL_FACE); glShadeModel(GL_SMOOTH); glLightModeli(GL_LIGHT_MODEL_TWO_SIDE, GL_FALSE); glLightModeli(GL_LIGHT_MODEL_LOCAL_VIEWER, GL_FALSE); float lightColor[] = { 0.9f, 0.9f, 0.9f, 1.0f }; glLightfv(GL_LIGHT0, GL_DIFFUSE, lightColor); glLightfv(GL_LIGHT1, GL_DIFFUSE, lightColor); glEnable(GL_LIGHT0); glDisable(GL_LIGHT1); float materialColor[] = { 0.8f, 0.8f, 0.8f, 1.0f }; glMaterialfv(GL_FRONT, GL_DIFFUSE, materialColor); glColorMaterial(GL_FRONT, GL_DIFFUSE); float ambient[] = { 0.8f, 0.8f, 0.8f, 1.0f }; glLightModelfv(GL_LIGHT_MODEL_AMBIENT, ambient); if (m_shapeSphere == NULL) { m_shapeSphere = new BrainOpenGLShapeSphere(5, 0.5); } if (m_shapeCone == NULL) { m_shapeCone = new BrainOpenGLShapeCone(8); } if (m_shapeCylinder == NULL) { m_shapeCylinder = new BrainOpenGLShapeCylinder(8); } if (m_shapeCube == NULL) { m_shapeCube = new BrainOpenGLShapeCube(1.0, BrainOpenGLShapeCube::NORMAL); } if (m_shapeCubeRounded == NULL) { 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; } this->initializedOpenGLFlag = true; /* * Remaining items need to executed only once. */ } /** * Enable lighting based upon the current mode. */ void BrainOpenGLFixedPipeline::enableLighting() { float lightPosition[] = { 0.0f, 0.0f, 1000.0f, 0.0f }; switch (this->mode) { case MODE_DRAWING: glPushMatrix(); glLoadIdentity(); glLightfv(GL_LIGHT0, GL_POSITION, lightPosition); glEnable(GL_LIGHT0); /* * Light 1 position is opposite of light 0 */ lightPosition[0] = -lightPosition[0]; lightPosition[1] = -lightPosition[1]; lightPosition[2] = -lightPosition[2]; glLightfv(GL_LIGHT1, GL_POSITION, lightPosition); glEnable(GL_LIGHT1); glPopMatrix(); glEnable(GL_LIGHTING); glEnable(GL_COLOR_MATERIAL); break; case MODE_IDENTIFICATION: case MODE_PROJECTION: this->disableLighting(); break; } } /** * Disable lighting. */ void BrainOpenGLFixedPipeline::disableLighting() { glDisable(GL_LIGHTING); glDisable(GL_COLOR_MATERIAL); } /** * Enable line anti-aliasing (line smoothing) which also required blending. */ void BrainOpenGLFixedPipeline::enableLineAntiAliasing() { glEnable(GL_LINE_SMOOTH); glEnable(GL_BLEND); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); glHint(GL_LINE_SMOOTH_HINT, GL_NICEST); } /** * Disable line anti-aliasing (line smoothing) which also required blending. */ void BrainOpenGLFixedPipeline::disableLineAntiAliasing() { glDisable(GL_LINE_SMOOTH); glDisable(GL_BLEND); } /** * Draw contents of a surface model. * @param surfaceModel * Model that is drawn. * @param viewport * Viewport for drawing region. */ void BrainOpenGLFixedPipeline::drawSurfaceModel(ModelSurface* surfaceModel, const int32_t viewport[4]) { Surface* surface = surfaceModel->getSurface(); float center[3]; surface->getBoundingBox()->getCenter(center); this->setViewportAndOrthographicProjectionForSurfaceFile(viewport, browserTabContent->getProjectionViewType(), surface); this->applyViewingTransformations(surfaceModel, center, browserTabContent->getProjectionViewType()); const float* nodeColoringRGBA = this->surfaceNodeColoring->colorSurfaceNodes(surfaceModel, surface, this->windowTabIndex); this->drawSurface(surface, nodeColoringRGBA, true); } /** * Draw the surface axes. */ void BrainOpenGLFixedPipeline::drawSurfaceAxes() { const float bigNumber = 1000000.0; glPushMatrix(); glColor3f(1.0, 0.0, 0.0); glBegin(GL_LINES); glVertex3f(-bigNumber, 0.0, 0.0); glVertex3f( bigNumber, 0.0, 0.0); glVertex3f(0.0, -bigNumber, 0.0); glVertex3f(0.0, bigNumber, 0.0); glVertex3f(0.0, 0.0, -bigNumber); glVertex3f(0.0, 0.0, bigNumber); glEnd(); glPopMatrix(); } /** * Draw a surface. * * @param surface * Surface that is drawn. * @param nodeColoringRGBA * RGBA coloring for the nodes. * @param drawAnnotationsInModelSpaceFlag * If true, draw annotations in model space. */ void BrainOpenGLFixedPipeline::drawSurface(Surface* surface, const float* nodeColoringRGBA, const bool drawAnnotationsInModelSpaceFlag) { const DisplayPropertiesSurface* dps = m_brain->getDisplayPropertiesSurface(); glMatrixMode(GL_MODELVIEW); glEnable(GL_DEPTH_TEST); applyClippingPlanes(BrainOpenGLFixedPipeline::CLIPPING_DATA_TYPE_SURFACE, surface->getStructure()); this->enableLighting(); const SurfaceDrawingTypeEnum::Enum drawingType = dps->getSurfaceDrawingType(); switch (this->mode) { case MODE_DRAWING: { switch (drawingType) { case SurfaceDrawingTypeEnum::DRAW_HIDE: break; case SurfaceDrawingTypeEnum::DRAW_AS_LINKS: /* * Draw first as triangles without coloring which uses * the background color. This prevents edges on back * from being seen. */ glPolygonOffset(1.0, 1.0); glEnable(GL_POLYGON_OFFSET_FILL); disableLighting(); this->drawSurfaceTrianglesWithVertexArrays(surface, NULL); glDisable(GL_POLYGON_OFFSET_FILL); /* * Now draw as polygon but outline only, do not fill. */ enableLighting(); setLineWidth(dps->getLinkSize()); glPolygonMode(GL_FRONT, GL_LINE); this->drawSurfaceTrianglesWithVertexArrays(surface, nodeColoringRGBA); glPolygonMode(GL_FRONT, GL_FILL); break; case SurfaceDrawingTypeEnum::DRAW_AS_NODES: this->drawSurfaceNodes(surface, nodeColoringRGBA); break; case SurfaceDrawingTypeEnum::DRAW_AS_TRIANGLES: /* * Enable alpha blending so that surface transparency * (using first overlay opacity) will function. */ GLboolean blendingEnabled = false; glGetBooleanv(GL_BLEND, &blendingEnabled); glEnable(GL_BLEND); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); 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; glEnable(GL_POLYGON_OFFSET_FILL); glPolygonOffset(factor, units); } this->drawSurfaceTrianglesWithVertexArrays(surface, nodeColoringRGBA); if (borderAboveSurfaceOffset != 0.0) { glDisable(GL_POLYGON_OFFSET_FILL); } if (blendingEnabled == false) { glDisable(GL_BLEND); } break; } this->disableClippingPlanes(); if (dps->isDisplayNormalVectors()) { drawSurfaceNormalVectors(surface); } this->drawSurfaceBorders(surface); this->drawSurfaceFoci(surface); this->drawSurfaceNodeAttributes(surface); this->drawSurfaceBorderBeingDrawn(surface); /* * Draw annotations for this surface and maybe draw * the model annotations. */ 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; m_annotationDrawing->drawAnnotations(&inputs, AnnotationCoordinateSpaceEnum::SURFACE, emptyColorBars, surface); if (drawAnnotationsInModelSpaceFlag) { m_annotationDrawing->drawAnnotations(&inputs, AnnotationCoordinateSpaceEnum::STEREOTAXIC, emptyColorBars, NULL); } } break; case MODE_IDENTIFICATION: { /* * Disable shading since ID info is encoded in rgba coloring */ glShadeModel(GL_FLAT); if (drawingType != SurfaceDrawingTypeEnum::DRAW_HIDE) { this->drawSurfaceNodes(surface, nodeColoringRGBA); this->drawSurfaceTriangles(surface, nodeColoringRGBA); } this->disableClippingPlanes(); if (dps->isDisplayNormalVectors()) { drawSurfaceNormalVectors(surface); } this->drawSurfaceBorders(surface); this->drawSurfaceFoci(surface); this->drawSurfaceNodeAttributes(surface); /* * Draw annotations for this surface and maybe draw * the model annotations. */ 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; m_annotationDrawing->drawAnnotations(&inputs, AnnotationCoordinateSpaceEnum::SURFACE, emptyColorBars, surface); if (drawAnnotationsInModelSpaceFlag) { m_annotationDrawing->drawAnnotations(&inputs, AnnotationCoordinateSpaceEnum::STEREOTAXIC, emptyColorBars, NULL); } /* * Re-enable shading since ID info is encoded in rgba coloring */ glShadeModel(GL_SMOOTH); } break; case MODE_PROJECTION: /* * Disable shading since ID info is encoded in rgba coloring */ glShadeModel(GL_FLAT); if (drawingType != SurfaceDrawingTypeEnum::DRAW_HIDE) { this->drawSurfaceTriangles(surface, nodeColoringRGBA); } /* * Re-enable shading since ID info is encoded in rgba coloring */ glShadeModel(GL_SMOOTH); break; } this->disableLighting(); this->disableClippingPlanes(); } /** * Draw a surface as individual triangles. * @param surface * Surface that is drawn. * @param nodeColoringRGBA * RGBA coloring for the nodes. */ void BrainOpenGLFixedPipeline::drawSurfaceTriangles(Surface* surface, const float* nodeColoringRGBA) { const int numTriangles = surface->getNumberOfTriangles(); const int32_t* triangles = surface->getTriangle(0); const float* coordinates = surface->getCoordinate(0); const float* normals = surface->getNormalVector(0); SelectionItemSurfaceTriangle* triangleID = NULL; /* * Check for a 'selection' type mode */ bool isSelect = false; bool isProjection = false; switch (this->mode) { case MODE_DRAWING: break; case MODE_IDENTIFICATION: triangleID = m_brain->getSelectionManager()->getSurfaceTriangleIdentification(); if (triangleID->isEnabledForSelection()) { isSelect = true; } else { return; } break; case MODE_PROJECTION: isSelect = true; isProjection = true; break; } if (isSelect) { glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); } uint8_t rgba[4]; glBegin(GL_TRIANGLES); for (int32_t i = 0; i < numTriangles; i++) { const int32_t i3 = i * 3; const int32_t n1 = triangles[i3]; const int32_t n2 = triangles[i3+1]; const int32_t n3 = triangles[i3+2]; if (isSelect) { this->colorIdentification->addItem(rgba, SelectionItemDataTypeEnum::SURFACE_TRIANGLE, i); glColor3ubv(rgba); glNormal3fv(&normals[n1*3]); glVertex3fv(&coordinates[n1*3]); glNormal3fv(&normals[n2*3]); glVertex3fv(&coordinates[n2*3]); glNormal3fv(&normals[n3*3]); glVertex3fv(&coordinates[n3*3]); } else { glColor4fv(&nodeColoringRGBA[n1*4]); glNormal3fv(&normals[n1*3]); glVertex3fv(&coordinates[n1*3]); glColor4fv(&nodeColoringRGBA[n2*4]); glNormal3fv(&normals[n2*3]); glVertex3fv(&coordinates[n2*3]); glColor4fv(&nodeColoringRGBA[n3*4]); glNormal3fv(&normals[n3*3]); glVertex3fv(&coordinates[n3*3]); } } glEnd(); if (isSelect) { int32_t triangleIndex = -1; float depth = -1.0; this->getIndexFromColorSelection(SelectionItemDataTypeEnum::SURFACE_TRIANGLE, this->mouseX, this->mouseY, triangleIndex, depth); if (triangleIndex >= 0) { bool isTriangleIdAccepted = false; if (triangleID != NULL) { if (triangleID->isOtherScreenDepthCloserToViewer(depth)) { triangleID->setBrain(surface->getBrainStructure()->getBrain()); triangleID->setSurface(surface); triangleID->setTriangleNumber(triangleIndex); const int32_t* triangleNodeIndices = surface->getTriangle(triangleIndex); triangleID->setNearestNode(triangleNodeIndices[0]); triangleID->setScreenDepth(depth); isTriangleIdAccepted = true; CaretLogFine("Selected Triangle: " + triangleID->toString()); } else { CaretLogFine("Rejecting Selected Triangle but still using: " + triangleID->toString()); } } /* * Node indices */ const int32_t n1 = triangles[triangleIndex*3]; const int32_t n2 = triangles[triangleIndex*3 + 1]; const int32_t n3 = triangles[triangleIndex*3 + 2]; /* * Node coordinates */ const float* c1 = &coordinates[n1*3]; const float* c2 = &coordinates[n2*3]; const float* c3 = &coordinates[n3*3]; const float average[3] = { c1[0] + c2[0] + c3[0], c1[1] + c2[1] + c3[1], c1[2] + c2[2] + c3[2] }; if (triangleID != NULL) { if (isTriangleIdAccepted) { this->setSelectedItemScreenXYZ(triangleID, average); } } GLdouble selectionModelviewMatrix[16]; glGetDoublev(GL_MODELVIEW_MATRIX, selectionModelviewMatrix); GLdouble selectionProjectionMatrix[16]; glGetDoublev(GL_PROJECTION_MATRIX, selectionProjectionMatrix); GLint selectionViewport[4]; glGetIntegerv(GL_VIEWPORT, selectionViewport); /* * Window positions of each coordinate */ double dc1[3] = { c1[0], c1[1], c1[2] }; double dc2[3] = { c2[0], c2[1], c2[2] }; double dc3[3] = { c3[0], c3[1], c3[2] }; double wc1[3], wc2[3], wc3[3]; if (gluProject(dc1[0], dc1[1], dc1[2], selectionModelviewMatrix, selectionProjectionMatrix, selectionViewport, &wc1[0], &wc1[1], &wc1[2]) && gluProject(dc2[0], dc2[1], dc2[2], selectionModelviewMatrix, selectionProjectionMatrix, selectionViewport, &wc2[0], &wc2[1], &wc2[2]) && gluProject(dc3[0], dc3[1], dc3[2], selectionModelviewMatrix, selectionProjectionMatrix, selectionViewport, &wc3[0], &wc3[1], &wc3[2])) { const double d1 = MathFunctions::distanceSquared2D(wc1[0], wc1[1], this->mouseX, this->mouseY); const double d2 = MathFunctions::distanceSquared2D(wc2[0], wc2[1], this->mouseX, this->mouseY); const double d3 = MathFunctions::distanceSquared2D(wc3[0], wc3[1], this->mouseX, this->mouseY); if (triangleID != NULL) { if (isTriangleIdAccepted) { triangleID->setNearestNode(n3); triangleID->setNearestNodeScreenXYZ(wc3); triangleID->setNearestNodeModelXYZ(dc3); if ((d1 < d2) && (d1 < d3)) { triangleID->setNearestNode(n1); triangleID->setNearestNodeScreenXYZ(wc1); triangleID->setNearestNodeModelXYZ(dc1); } else if ((d2 < d1) && (d2 < d3)) { triangleID->setNearestNode(n2); triangleID->setNearestNodeScreenXYZ(wc2); triangleID->setNearestNodeModelXYZ(dc2); } } } /* * Getting projected position? */ if (isProjection) { /* * Place window coordinates of triangle's nodes * onto the screen by setting Z-coordinate to zero */ wc1[2] = 0.0; wc2[2] = 0.0; wc3[2] = 0.0; /* * Area of triangle when projected to display */ const double triangleDisplayArea = MathFunctions::triangleArea(wc1, wc2, wc3); /* * If area of triangle on display is small, * use a coordinate from the triangle */ if (triangleDisplayArea < 0.001) { float barycentricAreas[3] = { 1.0, 0.0, 0.0 }; int barycentricNodes[3] = { n1, n1, n1 }; this->setProjectionModeData(depth, c1, surface->getStructure(), barycentricAreas, barycentricNodes, surface->getNumberOfNodes()); } else { /* * Determine position in triangle using barycentric coordinates */ double displayXYZ[3] = { this->mouseX, this->mouseY, 0.0 }; const double areaU = (MathFunctions::triangleArea(displayXYZ, wc2, wc3) / triangleDisplayArea); const double areaV = (MathFunctions::triangleArea(displayXYZ, wc3, wc1) / triangleDisplayArea); const double areaW = (MathFunctions::triangleArea(displayXYZ, wc1, wc2) / triangleDisplayArea); double totalArea = areaU + areaV + areaW; if (totalArea <= 0) { totalArea = 1.0; } if ((areaU < 0.0) || (areaV < 0.0) || (areaW < 0.0)) { CaretLogWarning("Invalid tile area: less than zero when projecting to surface."); } else { /* * 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 }; const float barycentricAreas[3] = { areaU, areaV, areaW }; const int32_t barycentricNodes[3] = { n1, n2, n3 }; this->setProjectionModeData(depth, projectedXYZ, surface->getStructure(), barycentricAreas, barycentricNodes, surface->getNumberOfNodes()); } } } } CaretLogFine("Selected Triangle: " + QString::number(triangleIndex)); } } } /** * During projection mode, set the projected data. If the * projection data is already set, it will be overridden * if the new data is closer, in screen depth, to the user. * * @param screenDepth * Screen depth of data. * @param xyz * Stereotaxic coordinate of projected position. * @param structure * Structure to which data projects. * @param barycentricAreas * Barycentric areas of projection, if to surface with valid structure. * @param barycentricNodes * Barycentric nodes of projection, if to surface with valid structure * @param numberOfNodes * Number of nodes in surface, if to surface with valid structure. */ void BrainOpenGLFixedPipeline::setProjectionModeData(const float screenDepth, const float xyz[3], const StructureEnum::Enum structure, const float barycentricAreas[3], const int barycentricNodes[3], const int numberOfNodes) { CaretAssert(this->modeProjectionData); if (screenDepth < this->modeProjectionScreenDepth) { this->modeProjectionScreenDepth = screenDepth; this->modeProjectionData->setStructure(structure); this->modeProjectionData->setStereotaxicXYZ(xyz); SurfaceProjectionBarycentric* barycentric = this->modeProjectionData->getBarycentricProjection(); barycentric->setProjectionSurfaceNumberOfNodes(numberOfNodes); barycentric->setTriangleAreas(barycentricAreas); barycentric->setTriangleNodes(barycentricNodes); barycentric->setValid(true); CaretLogFiner("Projected to surface " + StructureEnum::toName(structure) + " with depth " + screenDepth); } } /** * Draw a surface as individual nodes. * @param surface * Surface that is drawn. * @param nodeColoringRGBA * RGBA coloring for the nodes. */ void BrainOpenGLFixedPipeline::drawSurfaceNodes(Surface* surface, const float* nodeColoringRGBA) { const DisplayPropertiesSurface* dps = m_brain->getDisplayPropertiesSurface(); const int numNodes = surface->getNumberOfNodes(); const float* coordinates = surface->getCoordinate(0); const float* normals = surface->getNormalVector(0); SelectionItemSurfaceNode* nodeID = m_brain->getSelectionManager()->getSurfaceNodeIdentification(); /* * Check for a 'selection' type mode */ bool isSelect = false; switch (this->mode) { case MODE_DRAWING: break; case MODE_IDENTIFICATION: if (nodeID->isEnabledForSelection()) { isSelect = true; glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); } else { return; } break; case MODE_PROJECTION: break; } uint8_t rgba[4]; float pointSize = dps->getNodeSize(); if (isSelect) { if (pointSize < 2.0) { pointSize = 2.0; } } setPointSize(pointSize); glBegin(GL_POINTS); for (int32_t i = 0; i < numNodes; i++) { const int32_t i3 = i * 3; if (isSelect) { this->colorIdentification->addItem(rgba, SelectionItemDataTypeEnum::SURFACE_NODE, i); glColor3ubv(rgba); glNormal3fv(&normals[i3]); glVertex3fv(&coordinates[i3]); } else { glColor4fv(&nodeColoringRGBA[i*4]); glNormal3fv(&normals[i3]); glVertex3fv(&coordinates[i3]); } } glEnd(); if (isSelect) { int nodeIndex = -1; float depth = -1.0; this->getIndexFromColorSelection(SelectionItemDataTypeEnum::SURFACE_NODE, this->mouseX, this->mouseY, nodeIndex, depth); if (nodeIndex >= 0) { if (nodeID->isOtherScreenDepthCloserToViewer(depth)) { nodeID->setBrain(surface->getBrainStructure()->getBrain()); nodeID->setSurface(surface); nodeID->setNodeNumber(nodeIndex); nodeID->setScreenDepth(depth); this->setSelectedItemScreenXYZ(nodeID, &coordinates[nodeIndex * 3]); CaretLogFine("Selected Vertex: " + nodeID->toString()); } else { CaretLogFine("Rejecting Selected Vertex: " + nodeID->toString()); } } } } /** * Draw a surface triangles with vertex arrays. * @param surface * Surface that is drawn. * @param nodeColoringRGBA * RGBA coloring for the nodes. */ void BrainOpenGLFixedPipeline::drawSurfaceTrianglesWithVertexArrays(const Surface* surface, const float* nodeColoringRGBA) { glEnableClientState(GL_VERTEX_ARRAY); if (nodeColoringRGBA != NULL) { glEnableClientState(GL_COLOR_ARRAY); } glEnableClientState(GL_NORMAL_ARRAY); glVertexPointer(3, GL_FLOAT, 0, reinterpret_cast(surface->getCoordinate(0))); if (nodeColoringRGBA != NULL) { glColorPointer(4, GL_FLOAT, 0, reinterpret_cast(nodeColoringRGBA)); } else { glColor3fv(m_backgroundColorFloat); } glNormalPointer(GL_FLOAT, 0, reinterpret_cast(surface->getNormalVector(0))); const int numTriangles = surface->getNumberOfTriangles(); glDrawElements(GL_TRIANGLES, (3 * numTriangles), GL_UNSIGNED_INT, reinterpret_cast(surface->getTriangle(0))); glDisableClientState(GL_VERTEX_ARRAY); glDisableClientState(GL_COLOR_ARRAY); glDisableClientState(GL_NORMAL_ARRAY); } /** * Draw a surface's normal vectors. * @param surface * Surface on which normal vectors are drawn. */ void BrainOpenGLFixedPipeline::drawSurfaceNormalVectors(const Surface* surface) { disableLighting(); const StructureEnum::Enum structure = surface->getStructure(); const float length = 10.0; CaretPointer topoHelper = surface->getTopologyHelper(); setLineWidth(1.0); glColor3f(1.0, 0.0, 0.0); const int32_t numNodes = surface->getNumberOfNodes(); glBegin(GL_LINES); for (int32_t i = 0; i < numNodes; i++) { if (topoHelper->getNodeHasNeighbors(i)) { const float* xyz = surface->getCoordinate(i); if (m_clippingPlaneGroup->isSurfaceSelected()) { if ( ! isCoordinateInsideClippingPlanesForStructure(structure, xyz)) { continue; } } const float* normal = surface->getNormalVector(i); float vector[3] = { xyz[0] + length * normal[0], xyz[1] + length * normal[1], xyz[2] + length * normal[2] }; glVertex3fv(xyz); glVertex3fv(vector); } } glEnd(); enableLighting(); } /** * Draw attributes for the given surface. * @param surface * Surface for which attributes are drawn. */ void BrainOpenGLFixedPipeline::drawSurfaceNodeAttributes(Surface* surface) { BrainStructure* brainStructure = surface->getBrainStructure(); CaretAssert(brainStructure); Brain* brain = brainStructure->getBrain(); CaretAssert(brain); const StructureEnum::Enum structure = surface->getStructure(); const int numNodes = surface->getNumberOfNodes(); const float* coordinates = surface->getCoordinate(0); IdentificationManager* idManager = brain->getIdentificationManager(); SelectionItemSurfaceNodeIdentificationSymbol* symbolID = m_brain->getSelectionManager()->getSurfaceNodeIdentificationSymbol(); const std::vector identifiedNodes = idManager->getNodeIdentifiedItemsForSurface(structure, numNodes); std::vector identifiedNodeIndices; for (std::vector::const_iterator iter = identifiedNodes.begin(); iter != identifiedNodes.end(); iter++) { const IdentifiedItemNode& nodeID = *iter; identifiedNodeIndices.push_back(nodeID.getNodeIndex()); } EventNodeIdentificationColorsGetFromCharts colorsFromChartsEvent(structure, this->windowTabIndex, identifiedNodeIndices); /* * Check for a 'selection' type mode */ bool isSelect = false; switch (this->mode) { case MODE_DRAWING: EventManager::get()->sendEvent(colorsFromChartsEvent.getPointer()); break; case MODE_IDENTIFICATION: if (symbolID->isEnabledForSelection()) { isSelect = true; glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); } else { return; } break; case MODE_PROJECTION: 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; } } 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); } else { nodeID.getContralateralSymbolRGB(idRGBA); } } idRGBA[3] = 255; glPushMatrix(); glTranslatef(xyz[0], xyz[1], xyz[2]); this->drawSphereWithDiameter(idRGBA, symbolDiameter); glPopMatrix(); } if (isSelect) { int nodeIndex = -1; float depth = -1.0; this->getIndexFromColorSelection(SelectionItemDataTypeEnum::SURFACE_NODE_IDENTIFICATION_SYMBOL, this->mouseX, this->mouseY, nodeIndex, depth); if (nodeIndex >= 0) { if (symbolID->isOtherScreenDepthCloserToViewer(depth)) { symbolID->setBrain(surface->getBrainStructure()->getBrain()); symbolID->setSurface(surface); symbolID->setNodeNumber(nodeIndex); symbolID->setScreenDepth(depth); this->setSelectedItemScreenXYZ(symbolID, &coordinates[nodeIndex * 3]); CaretLogFine("Selected Vertex Identification Symbol: " + QString::number(nodeIndex)); } } } } /** * Draw a border on a surface. The color must be set prior * to calling this method. * * @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 BrainOpenGLFixedPipeline::drawBorder(const BorderDrawInfo& borderDrawInfo) { CaretAssert(borderDrawInfo.surface); CaretAssert(borderDrawInfo.border); const StructureEnum::Enum surfaceStructure = borderDrawInfo.surface->getStructure(); const StructureEnum::Enum contralateralSurfaceStructure = StructureEnum::getContralateralStructure(surfaceStructure); const int32_t numBorderPoints = borderDrawInfo.border->getNumberOfPoints(); const bool isHighlightEndPoints = borderDrawInfo.isHighlightEndPoints; float pointDiameter = 2.0; float lineWidth = 2.0; BorderDrawingTypeEnum::Enum drawType = BorderDrawingTypeEnum::DRAW_AS_POINTS_SPHERES; if (borderDrawInfo.borderFileIndex >= 0) { const BrainStructure* bs = borderDrawInfo.surface->getBrainStructure(); const Brain* brain = bs->getBrain(); const DisplayPropertiesBorders* dpb = brain->getDisplayPropertiesBorders(); const DisplayGroupEnum::Enum displayGroup = dpb->getDisplayGroupForTab(this->windowTabIndex); pointDiameter = dpb->getPointSize(displayGroup, this->windowTabIndex); lineWidth = dpb->getLineWidth(displayGroup, this->windowTabIndex); drawType = dpb->getDrawingType(displayGroup, this->windowTabIndex); } bool drawSphericalPoints = false; bool drawSquarePoints = false; bool drawLines = false; switch (drawType) { case BorderDrawingTypeEnum::DRAW_AS_LINES: drawLines = true; break; case BorderDrawingTypeEnum::DRAW_AS_POINTS_SPHERES: drawSphericalPoints = true; break; case BorderDrawingTypeEnum::DRAW_AS_POINTS_SQUARES: drawSquarePoints = true; break; case BorderDrawingTypeEnum::DRAW_AS_POINTS_AND_LINES: drawLines = true; drawSphericalPoints = true; break; } const bool flatSurfaceFlag = (borderDrawInfo.surface->getSurfaceType() == SurfaceTypeEnum::FLAT); bool flatSurfaceDrawUnstretchedLinesFlag = false; float unstretchedLinesLength = -1.0; if (flatSurfaceFlag) { if ((borderDrawInfo.anatomicalSurface != NULL) && (borderDrawInfo.unstretchedLinesLength > 0.0)) { flatSurfaceDrawUnstretchedLinesFlag = true; unstretchedLinesLength = borderDrawInfo.unstretchedLinesLength; } } 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(); CaretAssert(static_cast(nodesBoundaryEdgeCount.size()) == borderDrawInfo.surface->getNumberOfNodes()); /* * Find points valid for this surface */ for (int32_t i = 0; i < numBorderPoints; i++) { 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. */ const StructureEnum::Enum pointStructure = p->getStructure(); bool structureMatches = true; if (surfaceStructure != pointStructure) { structureMatches = false; if (borderDrawInfo.isContralateralEnabled) { if (contralateralSurfaceStructure == pointStructure) { structureMatches = true; } } } if (structureMatches == false) { continue; } float xyz[3]; bool isXyzValid = p->getProjectedPositionAboveSurface(*borderDrawInfo.surface, xyz, drawAtDistanceAboveSurface); 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; } } } } } 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); } } 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, pointIndex[i]); idRGBA[3] = 255; if (drawSphericalPoints) { this->drawSphereWithDiameter(idRGBA, pointDiameter); } else { this->drawSquare(idRGBA, pointDiameter); } } 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 (drawSphericalPoints) { this->drawSphereWithDiameter(rgba, pointDiameter); } else { this->drawSquare(rgba, pointDiameter); } } 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; } } 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 { drawIt = false; } } if (drawIt) { if (flatSurfaceDrawUnstretchedLinesFlag) { CaretAssertVectorIndex(pointAnatomicalXYZ, i3 + 2); if (unstretchedBorderLineTest(xyz1, xyz2, &pointAnatomicalXYZ[i3], &pointAnatomicalXYZ[i3-3], unstretchedLinesLength)) { drawIt = false; } } } if (drawIt) { glBegin(GL_LINES); glVertex3fv(xyz1); glVertex3fv(xyz2); glEnd(); } } } 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 (drawIt) { glVertex3fv(xyz1); glVertex3fv(xyz2); } } glEnd(); } this->enableLighting(); } } /** * Determine if the ratio if border length over anatomical border length * is greater than the unstretched lines factor. * * @param p1 * Position of border point in surface. * @param p1 * Position of next border point in surface. * @param anat1 * Position of border point in anatomical surface. * @param anat2 * Position of next border point in anatomical surface. * @param unstretchedLinesFactor * The unstretched lines factor. * @return * True if the border is too long and should NOT be drawn, else false. */ bool BrainOpenGLFixedPipeline::unstretchedBorderLineTest(const float p1[3], const float p2[3], const float anat1[3], const float anat2[3], const float unstretchedLinesFactor) const { const float dist = MathFunctions::distance3D(p1, p2); const float anatDist = MathFunctions::distance3D(anat1, anat2); if (anatDist > 0.0) { const float ratio = dist / anatDist; if (ratio > unstretchedLinesFactor) { return true; } } 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 * Surface on which foci are drawn. */ void BrainOpenGLFixedPipeline::drawSurfaceFoci(Surface* surface) { SelectionItemFocusSurface* idFocus = m_brain->getSelectionManager()->getSurfaceFocusIdentification(); /* * Check for a 'selection' type mode */ bool isSelect = false; switch (this->mode) { case MODE_DRAWING: break; case MODE_IDENTIFICATION: if (idFocus->isEnabledForSelection()) { isSelect = true; glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); } else { return; } break; case MODE_PROJECTION: return; break; } Brain* brain = surface->getBrainStructure()->getBrain(); const DisplayPropertiesFoci* fociDisplayProperties = brain->getDisplayPropertiesFoci(); const DisplayGroupEnum::Enum displayGroup = fociDisplayProperties->getDisplayGroupForTab(this->windowTabIndex); if (fociDisplayProperties->isDisplayed(displayGroup, this->windowTabIndex) == false) { return; } const float focusDiameter = fociDisplayProperties->getFociSize(displayGroup, this->windowTabIndex); const FeatureColoringTypeEnum::Enum fociColoringType = fociDisplayProperties->getColoringType(displayGroup, this->windowTabIndex); const StructureEnum::Enum surfaceStructure = surface->getStructure(); const StructureEnum::Enum surfaceContralateralStructure = StructureEnum::getContralateralStructure(surfaceStructure); const bool doClipping = isFeatureClippingEnabled(); bool drawAsSpheres = false; switch (fociDisplayProperties->getDrawingType(displayGroup, this->windowTabIndex)) { case FociDrawingTypeEnum::DRAW_AS_SPHERES: drawAsSpheres = true; break; case FociDrawingTypeEnum::DRAW_AS_SQUARES: break; } const CaretColorEnum::Enum caretColor = fociDisplayProperties->getStandardColorType(displayGroup, this->windowTabIndex); float caretColorRGBA[4]; CaretColorEnum::toRGBFloat(caretColor, caretColorRGBA); const bool isPasteOntoSurface = fociDisplayProperties->isPasteOntoSurface(displayGroup, this->windowTabIndex); const bool isContralateralEnabled = fociDisplayProperties->isContralateralDisplayed(displayGroup, this->windowTabIndex); const int32_t numFociFiles = brain->getNumberOfFociFiles(); for (int32_t i = 0; i < numFociFiles; i++) { FociFile* fociFile = brain->getFociFile(i); const GroupAndNameHierarchyModel* classAndNameSelection = fociFile->getGroupAndNameHierarchyModel(); if (classAndNameSelection->isSelected(displayGroup, this->windowTabIndex) == false) { continue; } const GiftiLabelTable* classColorTable = fociFile->getClassColorTable(); const GiftiLabelTable* nameColorTable = fociFile->getNameColorTable(); const int32_t numFoci = fociFile->getNumberOfFoci(); for (int32_t j = 0; j < numFoci; j++) { Focus* focus = fociFile->getFocus(j); float rgba[4] = { 0.0, 0.0, 0.0, 1.0 }; const GroupAndNameHierarchyItem* nameItem = focus->getGroupNameSelectionItem(); if (nameItem != NULL) { if (nameItem->isSelected(displayGroup, this->windowTabIndex) == false) { continue; } } switch (fociColoringType) { case FeatureColoringTypeEnum::FEATURE_COLORING_TYPE_CLASS: if (focus->isClassRgbaValid() == false) { const GiftiLabel* colorLabel = classColorTable->getLabelBestMatching(focus->getClassName()); if (colorLabel != NULL) { colorLabel->getColor(rgba); focus->setClassRgba(rgba); } else { focus->setClassRgba(rgba); } } focus->getClassRgba(rgba); break; case FeatureColoringTypeEnum::FEATURE_COLORING_TYPE_STANDARD_COLOR: rgba[0] = caretColorRGBA[0]; rgba[1] = caretColorRGBA[1]; rgba[2] = caretColorRGBA[2]; rgba[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); } } focus->getNameRgba(rgba); break; } glColor3fv(rgba); const int32_t numProjections = focus->getNumberOfProjections(); for (int32_t k = 0; k < numProjections; k++) { const SurfaceProjectedItem* spi = focus->getProjection(k); float xyz[3]; if (spi->getProjectedPosition(*surface, xyz, isPasteOntoSurface)) { const StructureEnum::Enum focusStructure = spi->getStructure(); bool drawIt = false; if (focusStructure == surfaceStructure) { drawIt = true; } else if (focusStructure == StructureEnum::INVALID) { drawIt = true; } else if (isContralateralEnabled) { if (focusStructure == surfaceContralateralStructure) { drawIt = true; } } if (doClipping) { if ( ! isCoordinateInsideClippingPlanesForStructure(surfaceStructure, xyz)) { drawIt = false; } } 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 idRGBA[3] = 255; if (drawAsSpheres) { this->drawSphereWithDiameter(idRGBA, focusDiameter); } else { this->drawSquare(idRGBA, focusDiameter); } } else { if (drawAsSpheres) { this->drawSphereWithDiameter(rgba, focusDiameter); } else { this->drawSquare(rgba, focusDiameter); } } glPopMatrix(); } } } } } if (isSelect) { int32_t fociFileIndex = -1; int32_t focusIndex = -1; int32_t focusProjectionIndex = -1; float depth = -1.0; this->getIndexFromColorSelection(SelectionItemDataTypeEnum::FOCUS_SURFACE, this->mouseX, this->mouseY, fociFileIndex, focusIndex, focusProjectionIndex, depth); if (fociFileIndex >= 0) { if (idFocus->isOtherScreenDepthCloserToViewer(depth)) { Focus* focus = brain->getFociFile(fociFileIndex)->getFocus(focusIndex); idFocus->setBrain(brain); idFocus->setFocus(focus); idFocus->setFociFile(brain->getFociFile(fociFileIndex)); idFocus->setFocusIndex(focusIndex); idFocus->setFocusProjectionIndex(focusProjectionIndex); idFocus->setSurface(surface); idFocus->setScreenDepth(depth); float xyz[3]; const SurfaceProjectedItem* spi = focus->getProjection(focusProjectionIndex); spi->getProjectedPosition(*surface, xyz, false); this->setSelectedItemScreenXYZ(idFocus, xyz); CaretLogFine("Selected Focus Identification Symbol: " + QString::number(focusIndex)); } } } } /** * Draw borders on a surface. * @param surface * Surface on which borders are drawn. */ void BrainOpenGLFixedPipeline::drawSurfaceBorders(Surface* surface) { SelectionItemBorderSurface* idBorder = m_brain->getSelectionManager()->getSurfaceBorderIdentification(); /* * Check for a 'selection' type mode */ bool isSelect = false; switch (this->mode) { case MODE_DRAWING: break; case MODE_IDENTIFICATION: if (idBorder->isEnabledForSelection()) { isSelect = true; glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); } else { return; } break; case MODE_PROJECTION: return; break; } Brain* brain = surface->getBrainStructure()->getBrain(); const DisplayPropertiesBorders* borderDisplayProperties = brain->getDisplayPropertiesBorders(); const DisplayGroupEnum::Enum displayGroup = borderDisplayProperties->getDisplayGroupForTab(this->windowTabIndex); if (borderDisplayProperties->isDisplayed(displayGroup, this->windowTabIndex) == false) { return; } float unstretchedLinesLength = -1.0; if (borderDisplayProperties->isUnstretchedLinesEnabled(displayGroup, this->windowTabIndex)) { unstretchedLinesLength = borderDisplayProperties->getUnstretchedLinesLength(displayGroup, this->windowTabIndex); } const FeatureColoringTypeEnum::Enum borderColoringType = borderDisplayProperties->getColoringType(displayGroup, this->windowTabIndex); const CaretColorEnum::Enum caretColor = borderDisplayProperties->getStandardColorType(displayGroup, this->windowTabIndex); float caretColorRGBA[4]; CaretColorEnum::toRGBFloat(caretColor, caretColorRGBA); const bool isContralateralEnabled = borderDisplayProperties->isContralateralDisplayed(displayGroup, this->windowTabIndex); const int32_t numBorderFiles = brain->getNumberOfBorderFiles(); for (int32_t i = 0; i < numBorderFiles; i++) { BorderFile* borderFile = brain->getBorderFile(i); const GroupAndNameHierarchyModel* classAndNameSelection = borderFile->getGroupAndNameHierarchyModel(); if (classAndNameSelection->isSelected(displayGroup, this->windowTabIndex) == false) { continue; } const GiftiLabelTable* classColorTable = borderFile->getClassColorTable(); const GiftiLabelTable* nameColorTable = borderFile->getNameColorTable(); const int32_t numBorders = borderFile->getNumberOfBorders(); for (int32_t j = 0; j < numBorders; j++) { Border* border = borderFile->getBorder(j); if (borderFile->isBorderDisplayed(displayGroup, this->windowTabIndex, border) == false) { continue; } float rgba[4] = { 0.0, 0.0, 0.0, 1.0 }; switch (borderColoringType) { case FeatureColoringTypeEnum::FEATURE_COLORING_TYPE_CLASS: if (border->isClassRgbaValid() == false) { const GiftiLabel* colorLabel = classColorTable->getLabelBestMatching(border->getClassName()); if (colorLabel != NULL) { colorLabel->getColor(rgba); border->setClassRgba(rgba); } else { border->setClassRgba(rgba); } } border->getClassRgba(rgba); break; case FeatureColoringTypeEnum::FEATURE_COLORING_TYPE_STANDARD_COLOR: rgba[0] = caretColorRGBA[0]; rgba[1] = caretColorRGBA[1]; rgba[2] = caretColorRGBA[2]; rgba[3] = caretColorRGBA[3]; break; case FeatureColoringTypeEnum::FEATURE_COLORING_TYPE_NAME: if (border->isNameRgbaValid() == false) { const GiftiLabel* colorLabel = nameColorTable->getLabelBestMatching(border->getName()); if (colorLabel != NULL) { colorLabel->getColor(rgba); border->setNameRgba(rgba); } else { border->setNameRgba(rgba); } } border->getNameRgba(rgba); break; } glColor3fv(rgba); BorderDrawInfo borderDrawInfo; borderDrawInfo.surface = surface; borderDrawInfo.border = border; borderDrawInfo.rgba[0] = rgba[0]; borderDrawInfo.rgba[1] = rgba[1]; borderDrawInfo.rgba[2] = rgba[2]; borderDrawInfo.rgba[3] = rgba[3]; borderDrawInfo.borderFileIndex = i; borderDrawInfo.borderIndex = j; borderDrawInfo.isSelect = isSelect; borderDrawInfo.isContralateralEnabled = isContralateralEnabled; borderDrawInfo.isHighlightEndPoints = m_drawHighlightedEndPoints; borderDrawInfo.anatomicalSurface = NULL; borderDrawInfo.unstretchedLinesLength = unstretchedLinesLength; BrainStructure* bs = brain->getBrainStructure(border->getStructure(), false); if (bs != NULL) { borderDrawInfo.anatomicalSurface = bs->getPrimaryAnatomicalSurface(); } this->drawBorder(borderDrawInfo); } } if (isSelect) { int32_t borderFileIndex = -1; int32_t borderIndex = -1; int32_t borderPointIndex; float depth = -1.0; this->getIndexFromColorSelection(SelectionItemDataTypeEnum::BORDER_SURFACE, this->mouseX, this->mouseY, borderFileIndex, borderIndex, borderPointIndex, depth); if (borderFileIndex >= 0) { if (idBorder->isOtherScreenDepthCloserToViewer(depth)) { Border* border = brain->getBorderFile(borderFileIndex)->getBorder(borderIndex); idBorder->setBrain(brain); idBorder->setBorder(border); idBorder->setBorderFile(brain->getBorderFile(borderFileIndex)); idBorder->setBorderIndex(borderIndex); idBorder->setBorderPointIndex(borderPointIndex); idBorder->setSurface(surface); idBorder->setScreenDepth(depth); float xyz[3]; border->getPoint(borderPointIndex)->getProjectedPosition(*surface, xyz, false); this->setSelectedItemScreenXYZ(idBorder, xyz); CaretLogFine("Selected Border Identification Symbol: " + QString::number(borderIndex)); } } } } /** * Draw the border that is begin drawn. * @param surface * Surface on which border is being drawn. */ void BrainOpenGLFixedPipeline::drawSurfaceBorderBeingDrawn(const Surface* surface) { glColor3f(1.0, 0.0, 0.0); if (this->borderBeingDrawn != NULL) { BorderDrawInfo borderDrawInfo; borderDrawInfo.surface = const_cast(surface); borderDrawInfo.border = this->borderBeingDrawn; borderDrawInfo.rgba[0] = 1.0; borderDrawInfo.rgba[1] = 0.0; borderDrawInfo.rgba[2] = 0.0; borderDrawInfo.rgba[3] = 1.0; borderDrawInfo.borderFileIndex = -1; borderDrawInfo.borderIndex = -1; borderDrawInfo.isSelect = false; borderDrawInfo.isContralateralEnabled = false; borderDrawInfo.isHighlightEndPoints = false; borderDrawInfo.anatomicalSurface = NULL; borderDrawInfo.unstretchedLinesLength = -1.0; this->drawBorder(borderDrawInfo); } } /** * Setup volume drawing information for an overlay set. * * @param browserTabContent * Content in the browser tab. * @param paletteFile * File from which palette is obtained. * @param volumeDrawInfoOut * Output containing information for volume drawing. */ void BrainOpenGLFixedPipeline::setupVolumeDrawInfo(BrowserTabContent* browserTabContent, Brain* brain, std::vector& volumeDrawInfoOut) { 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--) { Overlay* overlay = overlaySet->getOverlay(iOver); if (overlay->isEnabled()) { CaretMappableDataFile* mapFile; int32_t mapIndex; overlay->getSelectionData(mapFile, mapIndex); if (mapFile != NULL) { if (mapFile->isVolumeMappable()) { VolumeMappableInterface* vf = dynamic_cast(mapFile); if (vf != NULL) { float opacity = overlay->getOpacity(); if (volumeDrawInfoOut.empty()) { opacity = 1.0; } WholeBrainVoxelDrawingMode::Enum wholeBrainVoxelDrawingMode = overlay->getWholeBrainVoxelDrawingMode(); if (mapFile->isMappedWithPalette()) { FastStatistics* statistics = NULL; switch (mapFile->getPaletteNormalizationMode()) { case PaletteNormalizationModeEnum::NORMALIZATION_ALL_MAP_DATA: statistics = const_cast(mapFile->getFileFastStatistics()); break; case PaletteNormalizationModeEnum::NORMALIZATION_SELECTED_MAP_DATA: statistics = const_cast(mapFile->getMapFastStatistics(mapIndex)); break; } //CaretAssert(statistics); PaletteColorMapping* paletteColorMapping = mapFile->getMapPaletteColorMapping(mapIndex); Palette* palette = paletteFile->getPaletteByName(paletteColorMapping->getSelectedPaletteName()); if (palette != NULL) { /* * Statistics may be NULL for a dense connectome file * that does not have any data loaded by user * clicking on surface/volume. */ if (statistics != NULL) { bool useIt = true; if (volumeDrawInfoOut.empty() == false) { /* * If previous volume is the same as this * volume, there is no need to draw it twice. */ const VolumeDrawInfo& vdi = volumeDrawInfoOut[volumeDrawInfoOut.size() - 1]; if ((vdi.volumeFile == vf) && (opacity >= 1.0) && (mapIndex == vdi.mapIndex) && (*paletteColorMapping == *vdi.paletteColorMapping)) { useIt = false; } } if (useIt) { VolumeDrawInfo vdi(mapFile, vf, brain, paletteColorMapping, statistics, wholeBrainVoxelDrawingMode, mapIndex, opacity); volumeDrawInfoOut.push_back(vdi); } } } else { CaretLogWarning("No valid palette for drawing volume file: " + mapFile->getFileNameNoPath()); } } else { VolumeDrawInfo vdi(mapFile, vf, brain, NULL, NULL, wholeBrainVoxelDrawingMode, mapIndex, opacity); volumeDrawInfoOut.push_back(vdi); } } } } } } } /** * Draw the volume slices. * @param browserTabContent * Content of the window. * @param volumeModel * Model for slices. * @param viewport * Region of drawing. */ void BrainOpenGLFixedPipeline::drawVolumeModel(BrowserTabContent* browserTabContent, ModelVolume* volumeModel, const int32_t viewport[4]) { /* * Determine volumes that are to be drawn */ const int32_t tabNumber = browserTabContent->getTabNumber(); volumeModel->updateModel(tabNumber); Brain* brain = volumeModel->getBrain(); std::vector volumeDrawInfo; this->setupVolumeDrawInfo(browserTabContent, brain, volumeDrawInfo); VolumeSliceDrawingTypeEnum::Enum sliceDrawingType = browserTabContent->getSliceDrawingType(); VolumeSliceProjectionTypeEnum::Enum sliceProjectionType = browserTabContent->getSliceProjectionType(); /* * There is/was a flaw in volume drawing in that it does not "center" * correctly when the voxel corresponding to the coordinate (0, 0, 0) * is not within the volume. It seems to be fixed for orthogonal * drawing but oblique drawing probably needs a new algorithm to * fix the problem. */ bool useNewDrawingFlag = false; switch (sliceProjectionType) { case VolumeSliceProjectionTypeEnum::VOLUME_SLICE_PROJECTION_OBLIQUE: break; case VolumeSliceProjectionTypeEnum::VOLUME_SLICE_PROJECTION_ORTHOGONAL: useNewDrawingFlag = true; break; } if (useNewDrawingFlag) { BrainOpenGLVolumeSliceDrawing volumeSliceDrawing; volumeSliceDrawing.draw(this, browserTabContent, volumeDrawInfo, sliceDrawingType, sliceProjectionType, viewport); } else { BrainOpenGLVolumeObliqueSliceDrawing obliqueVolumeSliceDrawing; obliqueVolumeSliceDrawing.draw(this, browserTabContent, volumeDrawInfo, sliceDrawingType, sliceProjectionType, viewport); } } /** * Draw volumes a voxel cubes for whole brain view. * * @param volumeDrawInfoIn * Describes volumes that are drawn. */ void BrainOpenGLFixedPipeline::drawVolumeVoxelsAsCubesWholeBrain(std::vector& volumeDrawInfoIn) { /* * Filter volumes for drawing and only draw those volumes that * are to be drawn as 3D Voxel Cubes. */ std::vector volumeDrawInfo; for (std::vector::iterator iter = volumeDrawInfoIn.begin(); iter != volumeDrawInfoIn.end(); iter++) { bool useIt = false; VolumeDrawInfo& vdi = *iter; switch (vdi.wholeBrainVoxelDrawingMode) { case WholeBrainVoxelDrawingMode::DRAW_VOXELS_AS_THREE_D_CUBES: case WholeBrainVoxelDrawingMode::DRAW_VOXELS_AS_ROUNDED_THREE_D_CUBES: useIt = true; break; case WholeBrainVoxelDrawingMode::DRAW_VOXELS_ON_TWO_D_SLICES: break; } if (useIt) { volumeDrawInfo.push_back(vdi); } } const int32_t numberOfVolumesToDraw = static_cast(volumeDrawInfo.size()); if (numberOfVolumesToDraw <= 0) { return; } SelectionItemVoxel* voxelID = m_brain->getSelectionManager()->getVoxelIdentification(); /* * Check for a 'selection' type mode */ bool isSelect = false; switch (this->mode) { case MODE_DRAWING: break; case MODE_IDENTIFICATION: if (voxelID->isEnabledForSelection()) { isSelect = true; glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); } else { return; } break; case MODE_PROJECTION: return; break; } /* * When selecting turn on lighting and shading since * colors are used for identification. */ if (isSelect) { this->disableLighting(); glShadeModel(GL_FLAT); } else { this->enableLighting(); glEnable(GL_CULL_FACE); glShadeModel(GL_SMOOTH); } glEnable(GL_CULL_FACE); const bool doClipping = isFeatureClippingEnabled(); const DisplayPropertiesLabels* dsl = m_brain->getDisplayPropertiesLabels(); const DisplayGroupEnum::Enum displayGroup = dsl->getDisplayGroupForTab(this->windowTabIndex); /* * For identification, five items per voxel * 1) volume index * 2) map index * 3) index I * 4) index J * 5) index K */ const int32_t idPerVoxelCount = 5; std::vector identificationIndices; if (isSelect) { identificationIndices.reserve(10000 * idPerVoxelCount); } PaletteFile* paletteFile = m_brain->getPaletteFile(); for (int32_t iVol = 0; iVol < numberOfVolumesToDraw; iVol++) { VolumeDrawInfo& volInfo = volumeDrawInfo[iVol]; if (volInfo.opacity < 1.0) { glEnable(GL_BLEND); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); } else { glDisable(GL_BLEND); } const VolumeMappableInterface* volumeFile = volInfo.volumeFile; int64_t dimI, dimJ, dimK, numMaps, numComponents; volumeFile->getDimensions(dimI, dimJ, dimK, numMaps, numComponents); float originX, originY, originZ; float x1, y1, z1; volumeFile->indexToSpace(0, 0, 0, originX, originY, originZ); volumeFile->indexToSpace(1, 1, 1, x1, y1, z1); const float dx = x1 - originX; const float dy = y1 - originY; const float dz = z1 - originZ; /* * Cube size for voxel drawing. Some volumes may have a right to left * orientation in which case dx may be negative. * * Scale the cube slightly larger to avoid cracks, particularly if * a single slice is drawn. */ const float cubeScale = 1.10; const float cubeSizeDX = std::fabs(dx) * cubeScale; const float cubeSizeDY = std::fabs(dy) * cubeScale; const float cubeSizeDZ = std::fabs(dz) * cubeScale; std::vector labelMapData; const CiftiBrainordinateLabelFile* ciftiLabelFile = dynamic_cast(volumeFile); if (ciftiLabelFile != NULL) { ciftiLabelFile->getMapData(volInfo.mapIndex, labelMapData); } if ((dimI == 1) || (dimJ == 1) || (dimK == 1)) { glDisable(GL_LIGHTING); } uint8_t rgba[4]; for (int64_t iVoxel = 0; iVoxel < dimI; iVoxel++) { for (int64_t jVoxel = 0; jVoxel < dimJ; jVoxel++) { for (int64_t kVoxel = 0; kVoxel < dimK; kVoxel++) { if (ciftiLabelFile != NULL) { ciftiLabelFile->getVoxelColorInMapForLabelData(paletteFile, labelMapData, iVoxel, jVoxel, kVoxel, volInfo.mapIndex, displayGroup, this->windowTabIndex, rgba); } else { volumeFile->getVoxelColorInMap(paletteFile, iVoxel, jVoxel, kVoxel, volInfo.mapIndex, displayGroup, this->windowTabIndex, rgba); } if (rgba[3] > 0) { if (volInfo.opacity < 1.0) { rgba[3] *= volInfo.opacity; } if (rgba[3] > 0) { if (isSelect) { const int32_t idIndex = identificationIndices.size() / idPerVoxelCount; this->colorIdentification->addItem(rgba, SelectionItemDataTypeEnum::VOXEL, idIndex); identificationIndices.push_back(iVol); identificationIndices.push_back(volInfo.mapIndex); identificationIndices.push_back(iVoxel); identificationIndices.push_back(jVoxel); identificationIndices.push_back(kVoxel); } float x = 0, y = 0.0, z = 0.0; volumeFile->indexToSpace(iVoxel, jVoxel, kVoxel, x, y, z); bool drawIt = true; if (doClipping) { const float xyz[3] = { x, y, z }; if ( ! isCoordinateInsideClippingPlanesForStructure(StructureEnum::ALL, xyz)) { drawIt = false; } } if (drawIt) { glPushMatrix(); glTranslatef(x, y, z); switch (volInfo.wholeBrainVoxelDrawingMode) { case WholeBrainVoxelDrawingMode::DRAW_VOXELS_AS_THREE_D_CUBES: drawCuboid(rgba, cubeSizeDX, cubeSizeDY, cubeSizeDZ); break; case WholeBrainVoxelDrawingMode::DRAW_VOXELS_AS_ROUNDED_THREE_D_CUBES: drawRoundedCuboid(rgba, cubeSizeDX, cubeSizeDY, cubeSizeDZ); break; case WholeBrainVoxelDrawingMode::DRAW_VOXELS_ON_TWO_D_SLICES: break; } glPopMatrix(); } } } } } } } if (isSelect) { int32_t identifiedItemIndex; float depth = -1.0; this->getIndexFromColorSelection(SelectionItemDataTypeEnum::VOXEL, this->mouseX, this->mouseY, identifiedItemIndex, depth); if (identifiedItemIndex >= 0) { const int32_t idIndex = identifiedItemIndex * idPerVoxelCount; const int32_t volDrawInfoIndex = identificationIndices[idIndex]; CaretAssertVectorIndex(volumeDrawInfo, volDrawInfoIndex); VolumeMappableInterface* vf = volumeDrawInfo[volDrawInfoIndex].volumeFile; const int64_t voxelIndices[3] = { identificationIndices[idIndex + 2], identificationIndices[idIndex + 3], identificationIndices[idIndex + 4] }; if (voxelID->isOtherScreenDepthCloserToViewer(depth)) { voxelID->setVoxelIdentification(m_brain, vf, voxelIndices, depth); float voxelCoordinates[3]; vf->indexToSpace(voxelIndices[0], voxelIndices[1], voxelIndices[2], voxelCoordinates[0], voxelCoordinates[1], voxelCoordinates[2]); this->setSelectedItemScreenXYZ(voxelID, voxelCoordinates); CaretLogFine("Selected Voxel (3D): " + AString::fromNumbers(voxelIndices, 3, ",")); } } } this->disableLighting(); glShadeModel(GL_SMOOTH); glDisable(GL_BLEND); } void BrainOpenGLFixedPipeline::setFiberOrientationDisplayInfo(const DisplayPropertiesFiberOrientation* dpfo, const DisplayGroupEnum::Enum displayGroup, const int32_t tabIndex, Plane* plane, const StructureEnum::Enum structure, FiberTrajectoryColorModel::Item* colorSource, FiberOrientationDisplayInfo& dispInfo) { dispInfo.aboveLimit = dpfo->getAboveLimit(displayGroup, tabIndex); dispInfo.belowLimit = dpfo->getBelowLimit(displayGroup, tabIndex); dispInfo.colorSource = colorSource; dispInfo.fiberOrientationColorType = dpfo->getColoringType(displayGroup, tabIndex); dispInfo.fanMultiplier = dpfo->getFanMultiplier(displayGroup, tabIndex); dispInfo.isDrawWithMagnitude = dpfo->isDrawWithMagnitude(displayGroup, tabIndex); dispInfo.minimumMagnitude = dpfo->getMinimumMagnitude(displayGroup, tabIndex); dispInfo.magnitudeMultiplier = dpfo->getLengthMultiplier(displayGroup, tabIndex); dispInfo.plane = plane; dispInfo.structure = structure; dispInfo.symbolType = dpfo->getSymbolType(displayGroup, tabIndex); } /** * Draw fibers for a surface or a volume. * * @param plane * If not NULL, it is the plane of the volume slice being drawn and * only fibers within the above and below limits from the plane will * be drawn. * @param structure * The structure. */ void BrainOpenGLFixedPipeline::drawFiberOrientations(const Plane* plane, const StructureEnum::Enum structure) { const DisplayPropertiesFiberOrientation* dpfo = m_brain->getDisplayPropertiesFiberOrientation(); const DisplayGroupEnum::Enum displayGroup = dpfo->getDisplayGroupForTab(this->windowTabIndex); if (dpfo->isDisplayed(displayGroup, this->windowTabIndex) == false) { return; } const FiberOrientationSymbolTypeEnum::Enum symbolType = dpfo->getSymbolType(displayGroup, this->windowTabIndex); /* * Save status of clipping and disable clipping. * For fibers, the entire fiber symbol is displayed if its * origin is within the clipping planes which is tested below. */ GLboolean clipPlanesEnabled[6] = { glIsEnabled(GL_CLIP_PLANE0), glIsEnabled(GL_CLIP_PLANE1), glIsEnabled(GL_CLIP_PLANE2), glIsEnabled(GL_CLIP_PLANE3), glIsEnabled(GL_CLIP_PLANE4), glIsEnabled(GL_CLIP_PLANE5) }; glDisable(GL_CLIP_PLANE0); glDisable(GL_CLIP_PLANE1); glDisable(GL_CLIP_PLANE2); glDisable(GL_CLIP_PLANE3); glDisable(GL_CLIP_PLANE4); glDisable(GL_CLIP_PLANE5); /* * Fans use lighting but NOT on a volume slice */ disableLighting(); switch (symbolType) { case FiberOrientationSymbolTypeEnum::FIBER_SYMBOL_FANS: if (plane == NULL) { enableLighting(); } break; case FiberOrientationSymbolTypeEnum::FIBER_SYMBOL_LINES: break; } /* * Default constructor is color by fiber orientation settings XYZ/123 as RGB */ FiberTrajectoryColorModel::Item colorUseFiber; FiberOrientationDisplayInfo fiberOrientDispInfo; setFiberOrientationDisplayInfo(dpfo, displayGroup, this->windowTabIndex, const_cast(plane), structure, &colorUseFiber, fiberOrientDispInfo); /* * Draw the vectors from each of the connectivity files */ const int32_t numFiberOrienationFiles = m_brain->getNumberOfConnectivityFiberOrientationFiles(); for (int32_t iFile = 0; iFile < numFiberOrienationFiles; iFile++) { CiftiFiberOrientationFile* cfof = m_brain->getConnectivityFiberOrientationFile(iFile); if (cfof->isDisplayed(displayGroup, this->windowTabIndex)) { /* * Draw each of the fiber orientations which may contain multiple fibers */ const int64_t numberOfFiberOrientations = cfof->getNumberOfFiberOrientations(); for (int64_t i = 0; i < numberOfFiberOrientations; i++) { const FiberOrientation* fiberOrientation = cfof->getFiberOrientations(i); if (fiberOrientation->m_valid == false) { continue; } for (int32_t ifi = 0; ifi < fiberOrientation->m_numberOfFibers; ifi++) { fiberOrientation->m_fibers[ifi]->m_opacityForDrawing = 1.0; } addFiberOrientationForDrawing(&fiberOrientDispInfo, fiberOrientation); } } } drawAllFiberOrientations(&fiberOrientDispInfo, false); /* * Restore status of clipping planes enabled */ if (clipPlanesEnabled[0]) glEnable(GL_CLIP_PLANE0); if (clipPlanesEnabled[1]) glEnable(GL_CLIP_PLANE1); if (clipPlanesEnabled[2]) glEnable(GL_CLIP_PLANE2); if (clipPlanesEnabled[3]) glEnable(GL_CLIP_PLANE3); if (clipPlanesEnabled[4]) glEnable(GL_CLIP_PLANE4); if (clipPlanesEnabled[5]) glEnable(GL_CLIP_PLANE5); } /** * Add fiber orientation for drawing. Note that for alpha blending to * work correctly, the fibers must be sorted by depth and drawn from * furthest to nearest. Some tests will be performed to determine if * the fiber should be drawn prior to adding the fiber to the list * of fibers that will be drawn. * * @param fodi * Parameters controlling the drawing of fiber orientations. * @param fiberOrientation * The fiber orientation that will be drawn. */ void BrainOpenGLFixedPipeline::addFiberOrientationForDrawing(const FiberOrientationDisplayInfo* fodi, const FiberOrientation* fiberOrientation) { /* * Test location of fiber orientation for drawing */ if (fodi->plane != NULL) { const float distToPlane = fodi->plane->signedDistanceToPlane(fiberOrientation->m_xyz); if (distToPlane > fodi->aboveLimit) { return; } if (distToPlane < fodi->belowLimit) { return; } } if (isFeatureClippingEnabled()) { if ( ! isCoordinateInsideClippingPlanesForStructure(fodi->structure, fiberOrientation->m_xyz)) { return; } } m_fiberOrientationsForDrawing.push_back(const_cast(fiberOrientation)); } /* * For comparison when sorting that results in furthest fibers drawn first. */ static bool fiberDepthCompare(FiberOrientation* &f1, FiberOrientation* &f2) { return (f1->m_drawingDepth > f2->m_drawingDepth); } /** * Sort the fiber orientations by depth. */ void BrainOpenGLFixedPipeline::sortFiberOrientationsByDepth() { ElapsedTimer timer; timer.start(); /* * Create transforms model coordinate to a screen coordinate. */ GLdouble modelMatrixOpenGL[16]; glGetDoublev(GL_MODELVIEW_MATRIX, modelMatrixOpenGL); GLdouble projectionMatrixOpenGL[16]; glGetDoublev(GL_PROJECTION_MATRIX, projectionMatrixOpenGL); Matrix4x4 modelMatrix; modelMatrix.setMatrixFromOpenGL(modelMatrixOpenGL); Matrix4x4 projectionMatrix; projectionMatrix.setMatrixFromOpenGL(projectionMatrixOpenGL); Matrix4x4 modelToScreenMatrix; modelToScreenMatrix.setMatrix(projectionMatrix); modelToScreenMatrix.premultiply(modelMatrix); const float m0 = modelToScreenMatrix.getMatrixElement(2, 0); const float m1 = modelToScreenMatrix.getMatrixElement(2, 1); const float m2 = modelToScreenMatrix.getMatrixElement(2, 2); const float m3 = modelToScreenMatrix.getMatrixElement(2, 3); for (std::list::const_iterator iter = m_fiberOrientationsForDrawing.begin(); iter != m_fiberOrientationsForDrawing.end(); iter++) { const FiberOrientation* fiberOrientation = *iter; const float rawDepth =(m0 * fiberOrientation->m_xyz[0] + m1 * fiberOrientation->m_xyz[1] + m2 * fiberOrientation->m_xyz[2] + m3); const float screenDepth = ((rawDepth + 1.0) / 2.0); fiberOrientation->m_drawingDepth = screenDepth; } m_fiberOrientationsForDrawing.sort(fiberDepthCompare); } /** * Draw all of the fiber orienations. * * @param fodi * Parameters controlling the drawing of fiber orientations. */ void BrainOpenGLFixedPipeline::drawAllFiberOrientations(const FiberOrientationDisplayInfo* fodi, const bool isSortFibers) { if (isSortFibers) { sortFiberOrientationsByDepth(); } for (std::list::const_iterator iter = m_fiberOrientationsForDrawing.begin(); iter != m_fiberOrientationsForDrawing.end(); iter++) { const FiberOrientation* fiberOrientation = *iter; /* * Draw each of the fibers */ const int64_t numberOfFibers = fiberOrientation->m_numberOfFibers; for (int64_t j = 0; j < numberOfFibers; j++) { const Fiber* fiber = fiberOrientation->m_fibers[j]; /* * Apply display properties */ bool drawIt = true; if (fiber->m_meanF < fodi->minimumMagnitude) { drawIt = false; } if (drawIt) { float alpha = 1.0; if (j < 3) { alpha = fiber->m_opacityForDrawing; CaretAssertMessage(((alpha >= 0.0) && (alpha <= 1.0)), ("Value=" + AString::number(alpha))); if (alpha <= 0.0) { continue; } } /* * Length of vector */ float vectorLength = fodi->magnitudeMultiplier; if (fodi->isDrawWithMagnitude) { vectorLength *= fiber->m_meanF; } /* * Vector with magnitude */ const float magnitudeVector[3] = { fiber->m_directionUnitVector[0] * vectorLength, fiber->m_directionUnitVector[1] * vectorLength, fiber->m_directionUnitVector[2] * vectorLength }; const float halfMagnitudeVector[3] = { magnitudeVector[0] * 0.5, magnitudeVector[1] * 0.5, magnitudeVector[2] * 0.5, }; /* * Start of vector */ float startXYZ[3] = { fiberOrientation->m_xyz[0], fiberOrientation->m_xyz[1], fiberOrientation->m_xyz[2] }; /* * When drawing lines, start of vector is offset by * have of the vector length since the vector is * bi-directional. */ switch (fodi->symbolType) { case FiberOrientationSymbolTypeEnum::FIBER_SYMBOL_FANS: break; case FiberOrientationSymbolTypeEnum::FIBER_SYMBOL_LINES: startXYZ[0] -= halfMagnitudeVector[0]; startXYZ[1] -= halfMagnitudeVector[1]; startXYZ[2] -= halfMagnitudeVector[2]; break; } /* * End of vector */ float endXYZ[3] = { 0.0, 0.0, 0.0 }; /* * When drawing lines, end point is the start * plus the vector with magnitude. * * When drawing fans, there are two endpoints * with the fans starting in the middle. */ switch (fodi->symbolType) { case FiberOrientationSymbolTypeEnum::FIBER_SYMBOL_FANS: endXYZ[0] = startXYZ[0] + halfMagnitudeVector[0]; endXYZ[1] = startXYZ[1] + halfMagnitudeVector[1]; endXYZ[2] = startXYZ[2] + halfMagnitudeVector[2]; break; case FiberOrientationSymbolTypeEnum::FIBER_SYMBOL_LINES: endXYZ[0] = startXYZ[0] + magnitudeVector[0]; endXYZ[1] = startXYZ[1] + magnitudeVector[1]; endXYZ[2] = startXYZ[2] + magnitudeVector[2]; break; } float fiberRGBA[4] = { 0.0, 0.0, 0.0, 0.0 }; /* * Color of fiber */ switch (fodi->colorSource->getItemType()) { case FiberTrajectoryColorModel::Item::ITEM_TYPE_FIBER_ORIENTATION_COLORING_TYPE: switch (fodi->fiberOrientationColorType) { case FiberOrientationColoringTypeEnum::FIBER_COLORING_FIBER_INDEX_AS_RGB: { const int32_t indx = j % 3; switch (indx) { case 0: // use RED glColor4f(BrainOpenGLFixedPipeline::COLOR_RED[0], BrainOpenGLFixedPipeline::COLOR_RED[1], BrainOpenGLFixedPipeline::COLOR_RED[2], alpha); fiberRGBA[0] = BrainOpenGLFixedPipeline::COLOR_RED[0]; fiberRGBA[1] = BrainOpenGLFixedPipeline::COLOR_RED[1]; fiberRGBA[2] = BrainOpenGLFixedPipeline::COLOR_RED[2]; fiberRGBA[3] = alpha; break; case 1: // use BLUE glColor4f(BrainOpenGLFixedPipeline::COLOR_BLUE[0], BrainOpenGLFixedPipeline::COLOR_BLUE[1], BrainOpenGLFixedPipeline::COLOR_BLUE[2], alpha); fiberRGBA[0] = BrainOpenGLFixedPipeline::COLOR_BLUE[0]; fiberRGBA[1] = BrainOpenGLFixedPipeline::COLOR_BLUE[1]; fiberRGBA[2] = BrainOpenGLFixedPipeline::COLOR_BLUE[2]; fiberRGBA[3] = alpha; break; case 2: // use GREEN glColor4f(BrainOpenGLFixedPipeline::COLOR_GREEN[0], BrainOpenGLFixedPipeline::COLOR_GREEN[1], BrainOpenGLFixedPipeline::COLOR_GREEN[2], alpha); fiberRGBA[0] = BrainOpenGLFixedPipeline::COLOR_GREEN[0]; fiberRGBA[1] = BrainOpenGLFixedPipeline::COLOR_GREEN[1]; fiberRGBA[2] = BrainOpenGLFixedPipeline::COLOR_GREEN[2]; fiberRGBA[3] = alpha; break; } } break; case FiberOrientationColoringTypeEnum::FIBER_COLORING_XYZ_AS_RGB: CaretAssert((fiber->m_directionUnitVectorRGB[0] >= 0.0) && (fiber->m_directionUnitVectorRGB[0] <= 1.0)); CaretAssert((fiber->m_directionUnitVectorRGB[1] >= 0.0) && (fiber->m_directionUnitVectorRGB[1] <= 1.0)); CaretAssert((fiber->m_directionUnitVectorRGB[2] >= 0.0) && (fiber->m_directionUnitVectorRGB[2] <= 1.0)); CaretAssert((alpha >= 0.0) && (alpha <= 1.0)); glColor4f(fiber->m_directionUnitVectorRGB[0], fiber->m_directionUnitVectorRGB[1], fiber->m_directionUnitVectorRGB[2], alpha); fiberRGBA[0] = fiber->m_directionUnitVectorRGB[0]; fiberRGBA[1] = fiber->m_directionUnitVectorRGB[1]; fiberRGBA[2] = fiber->m_directionUnitVectorRGB[2]; fiberRGBA[3] = alpha; break; } break; case FiberTrajectoryColorModel::Item::ITEM_TYPE_CARET_COLOR: { const CaretColorEnum::Enum caretColor = fodi->colorSource->getCaretColor(); const float* rgb = CaretColorEnum::toRGB(caretColor); glColor4f(rgb[0], rgb[1], rgb[2], alpha); fiberRGBA[0] = rgb[0]; fiberRGBA[1] = rgb[1]; fiberRGBA[2] = rgb[2]; fiberRGBA[3] = alpha; } break; } /* * Draw the fiber */ switch (fodi->symbolType) { case FiberOrientationSymbolTypeEnum::FIBER_SYMBOL_FANS: { /* * Draw the cones */ const float radiansToDegrees = 180.0 / M_PI; const float majorAxis = std::min((vectorLength * std::tan(fiber->m_fanningMajorAxisAngle) * fodi->fanMultiplier), vectorLength); const float minorAxis = std::min((vectorLength * std::tan(fiber->m_fanningMinorAxisAngle) * fodi->fanMultiplier), vectorLength); /* * First cone */ glPushMatrix(); glTranslatef(startXYZ[0], startXYZ[1], startXYZ[2]); glRotatef(-fiber->m_phi * radiansToDegrees, 0.0, 0.0, 1.0); glRotatef(-fiber->m_theta * radiansToDegrees, 0.0, 1.0, 0.0); glRotatef(-fiber->m_psi * radiansToDegrees, 0.0, 0.0, 1.0); glScalef(majorAxis * 2.0, minorAxis * 2.0, vectorLength); m_shapeCone->draw(fiberRGBA); glPopMatrix(); /* * Second cone but pointing in opposite direction */ glPushMatrix(); glTranslatef(startXYZ[0], startXYZ[1], startXYZ[2]); glRotatef(-fiber->m_phi * radiansToDegrees, 0.0, 0.0, 1.0); glRotatef(180.0 -fiber->m_theta * radiansToDegrees, 0.0, 1.0, 0.0); glRotatef(fiber->m_psi * radiansToDegrees, 0.0, 0.0, 1.0); glScalef(majorAxis * 2.0, minorAxis * 2.0, vectorLength); m_shapeCone->draw(fiberRGBA); glPopMatrix(); } break; case FiberOrientationSymbolTypeEnum::FIBER_SYMBOL_LINES: { const float radius = 2.0; setLineWidth(radius); glBegin(GL_LINES); glVertex3fv(startXYZ); glVertex3fv(endXYZ); glEnd(); } break; } } } } /* * Now clear the list of fiber orientations for drawing. */ m_fiberOrientationsForDrawing.clear(); } /** * Draw fiber trajectories on a surface. */ void BrainOpenGLFixedPipeline::drawSurfaceFiberTrajectories(const StructureEnum::Enum structure) { drawFiberTrajectories(NULL, structure); } /** * Draw the fiber trajectories. * @param plane * If a volume it is non-NULL and contains the plane of the slice. * @param structure * The structure. */ void BrainOpenGLFixedPipeline::drawFiberTrajectories(const Plane* plane, const StructureEnum::Enum structure) { /* * Save status of clipping and disable clipping. * For fibers, the entire fiber symbol is displayed if its * origin is within the clipping planes which is tested below. */ GLboolean clipPlanesEnabled[6] = { glIsEnabled(GL_CLIP_PLANE0), glIsEnabled(GL_CLIP_PLANE1), glIsEnabled(GL_CLIP_PLANE2), glIsEnabled(GL_CLIP_PLANE3), glIsEnabled(GL_CLIP_PLANE4), glIsEnabled(GL_CLIP_PLANE5) }; glDisable(GL_CLIP_PLANE0); glDisable(GL_CLIP_PLANE1); glDisable(GL_CLIP_PLANE2); glDisable(GL_CLIP_PLANE3); glDisable(GL_CLIP_PLANE4); glDisable(GL_CLIP_PLANE5); glEnable(GL_BLEND); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); CaretAssert(this->browserTabContent); OverlaySet* overlaySet = this->browserTabContent->getOverlaySet(); const int32_t numberOfDisplayedOverlays = overlaySet->getNumberOfDisplayedOverlays(); for (int32_t iOver = 0; iOver < numberOfDisplayedOverlays; iOver++) { Overlay* overlay = overlaySet->getOverlay(iOver); if (overlay->isEnabled() == false) { continue; } CaretMappableDataFile* caretMappableDataFile = NULL; int32_t mapIndex = -1; overlay->getSelectionData(caretMappableDataFile, mapIndex); if (caretMappableDataFile == NULL) { continue; } CiftiFiberTrajectoryFile* trajFile = dynamic_cast(caretMappableDataFile); if (trajFile == NULL) { continue; } FiberTrajectoryMapProperties* ftmp = trajFile->getFiberTrajectoryMapProperties(); const float proportionMinimumOpacity = ftmp->getProportionMinimumOpacity(); const float proportionMaximumOpacity = ftmp->getProportionMaximumOpacity(); const float proportionRangeOpacity = proportionMaximumOpacity - proportionMinimumOpacity; const float countMinimumOpacity = ftmp->getCountMinimumOpacity(); const float countMaximumOpacity = ftmp->getCountMaximumOpacity(); const float countRangeOpacity = countMaximumOpacity - countMinimumOpacity; const float distanceMinimumOpacity = ftmp->getDistanceMinimumOpacity(); const float distanceMaximumOpacity = ftmp->getDistanceMaximumOpacity(); const float distanceRangeOpacity = distanceMaximumOpacity - distanceMinimumOpacity; const FiberTrajectoryDisplayModeEnum::Enum displayMode = ftmp->getDisplayMode(); float streamlineThreshold = std::numeric_limits::max(); switch (displayMode) { case FiberTrajectoryDisplayModeEnum::FIBER_TRAJECTORY_DISPLAY_ABSOLUTE: streamlineThreshold = ftmp->getCountStreamline(); if (countRangeOpacity <= 0.0) { continue; } break; case FiberTrajectoryDisplayModeEnum::FIBER_TRAJECTORY_DISPLAY_DISTANCE_WEIGHTED: case FiberTrajectoryDisplayModeEnum::FIBER_TRAJECTORY_DISPLAY_DISTANCE_WEIGHTED_LOG: streamlineThreshold = ftmp->getDistanceStreamline(); if (distanceRangeOpacity <= 0.0) { continue; } break; case FiberTrajectoryDisplayModeEnum::FIBER_TRAJECTORY_DISPLAY_PROPORTION: streamlineThreshold = ftmp->getProportionStreamline(); if (proportionRangeOpacity <= 0.0) { continue; } break; } DisplayPropertiesFiberOrientation* dpfo = m_brain->getDisplayPropertiesFiberOrientation(); const DisplayGroupEnum::Enum displayGroup = dpfo->getDisplayGroupForTab(this->windowTabIndex); const FiberOrientationSymbolTypeEnum::Enum symbolType = dpfo->getSymbolType(displayGroup, this->windowTabIndex); FiberOrientationDisplayInfo fiberOrientDispInfo; setFiberOrientationDisplayInfo(dpfo, displayGroup, this->windowTabIndex, const_cast(plane), structure, ftmp->getFiberTrajectoryColorModel()->getSelectedItem(), fiberOrientDispInfo); /* * Fans use lighting but NOT on a volume slice */ disableLighting(); switch (symbolType) { case FiberOrientationSymbolTypeEnum::FIBER_SYMBOL_FANS: if (plane == NULL) { enableLighting(); } break; case FiberOrientationSymbolTypeEnum::FIBER_SYMBOL_LINES: break; } const std::vector& trajectories = trajFile->getLoadedFiberOrientationTrajectories(); const int64_t numTraj = static_cast(trajectories.size()); for (int64_t iTraj = 0; iTraj < numTraj; iTraj++) { const FiberOrientationTrajectory* fiberTraj = trajectories[iTraj]; const FiberOrientation* orientation = fiberTraj->getFiberOrientation(); const float fiberFractionTotalCount = fiberTraj->getFiberFractionTotalCount(); const std::vector& fiberFractions = fiberTraj->getFiberFractions(); if (fiberFractions.size() != 3) { CaretLogFinest("Fiber Trajectory index=" + AString::number(iTraj) + " has " + AString::number(fiberFractions.size()) + " fibers != 3 from file " + trajFile->getFileNameNoPath()); continue; } else if (fiberFractionTotalCount < streamlineThreshold) { continue; } float fiberOpacities[3] = { 0.0, 0.0, 0.0 }; const float fiberCounts[3] = { fiberFractions[0] * fiberFractionTotalCount, fiberFractions[1] * fiberFractionTotalCount, fiberFractions[2] * fiberFractionTotalCount }; const float fiberFractionDistance = fiberTraj->getFiberFractionDistance(); /* * Set opacities for each fiber using mapping of minimum and * maximum opacities */ switch (displayMode) { case FiberTrajectoryDisplayModeEnum::FIBER_TRAJECTORY_DISPLAY_ABSOLUTE: fiberOpacities[0] = (fiberCounts[0] - countMinimumOpacity) / countRangeOpacity; fiberOpacities[1] = (fiberCounts[1] - countMinimumOpacity) / countRangeOpacity; fiberOpacities[2] = (fiberCounts[2] - countMinimumOpacity) / countRangeOpacity; break; case FiberTrajectoryDisplayModeEnum::FIBER_TRAJECTORY_DISPLAY_DISTANCE_WEIGHTED: fiberOpacities[0] = ((fiberCounts[0] * fiberFractionDistance) - distanceMinimumOpacity) / distanceRangeOpacity; fiberOpacities[1] = ((fiberCounts[1] * fiberFractionDistance) - distanceMinimumOpacity) / distanceRangeOpacity; fiberOpacities[2] = ((fiberCounts[2] * fiberFractionDistance) - distanceMinimumOpacity) / distanceRangeOpacity; break; case FiberTrajectoryDisplayModeEnum::FIBER_TRAJECTORY_DISPLAY_DISTANCE_WEIGHTED_LOG: { const float distanceLog = std::log(fiberFractionDistance); fiberOpacities[0] = ((fiberCounts[0] * distanceLog) - distanceMinimumOpacity) / distanceRangeOpacity; fiberOpacities[1] = ((fiberCounts[1] * distanceLog) - distanceMinimumOpacity) / distanceRangeOpacity; fiberOpacities[2] = ((fiberCounts[2] * distanceLog) - distanceMinimumOpacity) / distanceRangeOpacity; } break; case FiberTrajectoryDisplayModeEnum::FIBER_TRAJECTORY_DISPLAY_PROPORTION: fiberOpacities[0] = (fiberFractions[0] - proportionMinimumOpacity) /proportionRangeOpacity; fiberOpacities[1] = (fiberFractions[1] - proportionMinimumOpacity) /proportionRangeOpacity; fiberOpacities[2] = (fiberFractions[2] - proportionMinimumOpacity) /proportionRangeOpacity; break; } int32_t drawCount = 3; for (int32_t i = 0; i < 3; i++) { if (fiberOpacities[i] > 1.0) { fiberOpacities[i] = 1.0; } else if (fiberOpacities[i] <= 0.0) { fiberOpacities[i] = 0.0; drawCount--; } } if (drawCount > 0) { orientation->m_fibers[0]->m_opacityForDrawing = fiberOpacities[0]; orientation->m_fibers[1]->m_opacityForDrawing = fiberOpacities[1]; orientation->m_fibers[2]->m_opacityForDrawing = fiberOpacities[2]; addFiberOrientationForDrawing(&fiberOrientDispInfo, orientation); } } drawAllFiberOrientations(&fiberOrientDispInfo, true); } glDisable(GL_BLEND); /* * Restore status of clipping planes enabled */ if (clipPlanesEnabled[0]) glEnable(GL_CLIP_PLANE0); if (clipPlanesEnabled[1]) glEnable(GL_CLIP_PLANE1); if (clipPlanesEnabled[2]) glEnable(GL_CLIP_PLANE2); if (clipPlanesEnabled[3]) glEnable(GL_CLIP_PLANE3); if (clipPlanesEnabled[4]) glEnable(GL_CLIP_PLANE4); if (clipPlanesEnabled[5]) glEnable(GL_CLIP_PLANE5); } /** * Draw a cone with an elliptical shape. * @param rgba * Color of cone. * @param baseXYZ * Location of the base (flat wide) part of the cone * @param apexXYZ * Location of the pointed end of the cone * @param baseRadiusScaling * Scale the base radius by this amount * @param baseMajorAngle * Angle for the major axis of the ellipse (units = Radians) * Valid range is [0, Pi/2] * @param baseMinorAngle * Angle for the minor axis of the ellipse (units = Radians) * Valid range is [0, Pi/2] * @param baseRotationAngle (units = Radians) * Rotation of major axis from 'up' * @param backwardsFlag * If true, draw the cone backwards (rotated 180 degrees). */ void BrainOpenGLFixedPipeline::drawEllipticalCone(const float rgba[4], const float baseXYZ[3], const float apexXYZ[3], const float baseRadiusScaling, const float baseMajorAngleIn, const float baseMinorAngleIn, const float baseRotationAngle, const bool backwardsFlag) { float x1 = apexXYZ[0]; float y1 = apexXYZ[1]; float z1 = apexXYZ[2]; float vx = baseXYZ[0] - x1; float vy = baseXYZ[1] - y1; float vz = baseXYZ[2] - z1; float z = (float)std::sqrt( vx*vx + vy*vy + vz*vz ); double ax = 0.0f; const float maxAngle = M_PI_2 * 0.95; float baseMajorAngle = baseMajorAngleIn; if (baseMajorAngle > maxAngle) { baseMajorAngle = maxAngle; } float baseMinorAngle = baseMinorAngleIn; if (baseMinorAngle > maxAngle) { baseMinorAngle = maxAngle; } const float maxWidth = z; const float majorAxis = std::min(z * std::tan(baseMajorAngle) * baseRadiusScaling, maxWidth); const float minorAxis = std::min(z * std::tan(baseMinorAngle) * baseRadiusScaling, maxWidth); double zero = 1.0e-3; if (std::abs(vz) < zero) { 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 if ( vz <= 0.0f ) ax = -ax; } glPushMatrix(); glTranslatef( x1, y1, z1 ); float rx = -vy*vz; float ry = vx*vz; if ((std::abs(vx) < zero) && (std::fabs(vz) < zero)) { if (vy > 0) { ax = 90; } } 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 } else { glRotated(ax, rx, ry, 0.0); // Rotate about rotation vector } glPushMatrix(); if (backwardsFlag) { glRotatef(180.0, 0.0, 1.0, 0.0); glRotatef(MathFunctions::toDegrees(-baseRotationAngle), 0.0, 0.0, 1.0); } else { /* * Rotate around Z-axis using the base rotation angle */ glRotatef(MathFunctions::toDegrees(baseRotationAngle), 0.0, 0.0, 1.0); } /* * Draw the cone */ glScalef(majorAxis * 2.0, minorAxis * 2.0, z); m_shapeCone->draw(rgba); glPopMatrix(); glPopMatrix(); } /** * Draw a cone with an elliptical shape. * @param rgba * Color of cone. * @param bottomXYZ * Location of the bottom of the cylinder. * @param topXYZ * Location of the top of the cylinder. * @param radius * Radius of the cylinder. */ void BrainOpenGLFixedPipeline::drawCylinder(const float rgba[4], const float bottomXYZ[3], const float topXYZ[3], const float radius) { float x1 = topXYZ[0]; float y1 = topXYZ[1]; float z1 = topXYZ[2]; float vx = bottomXYZ[0] - x1; float vy = bottomXYZ[1] - y1; float vz = bottomXYZ[2] - z1; float z = (float)std::sqrt( vx*vx + vy*vy + vz*vz ); double ax = 0.0f; double zero = 1.0e-3; if (std::abs(vz) < zero) { 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 if ( vz <= 0.0f ) ax = -ax; } glPushMatrix(); glTranslatef( x1, y1, z1 ); float rx = -vy*vz; float ry = vx*vz; if ((std::abs(vx) < zero) && (std::fabs(vz) < zero)) { if (vy > 0) { ax = 90; } } 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 } else { glRotated(ax, rx, ry, 0.0); // Rotate about rotation vector } glPushMatrix(); /* * Draw the cone */ glScalef(radius * 2.0, radius * 2.0, z); m_shapeCylinder->draw(rgba); glPopMatrix(); glPopMatrix(); } /** * Given the full size of a viewport, the gap percentage, and the number * of subviewports, compute the subviewport size and gap in pixels. * * @param viewportSize * Full size of the viewport. * @param gapPercentage * Percentage of viewport used for gap between adjacent subviewports * @param gapOverride * If greater than zero, use this value as the gap and ignore the gap percentage. * @param numberOfSubViewports * Number of subviewports * @param subViewportSizeOut * Output containing the size for a subviewport * @param gapOut * Output for the size of the gap between adjacent subviewports. */ void BrainOpenGLFixedPipeline::createSubViewportSizeAndGaps(const int32_t viewportSize, const float gapPercentage, const int32_t gapOverride, const int32_t numberOfSubViewports, int32_t& subViewportSizeOut, int32_t& gapOut) { subViewportSizeOut = viewportSize; gapOut = 0; if (numberOfSubViewports > 1) { subViewportSizeOut = viewportSize / numberOfSubViewports; gapOut = static_cast(std::floor(static_cast(viewportSize * (gapPercentage / 100.0)))); if (gapOverride > 0) { gapOut = gapOverride; } const double gapSum = gapOut * (numberOfSubViewports - 1); const int32_t subtractFromViewport = std::ceil(gapSum / numberOfSubViewports); subViewportSizeOut -= subtractFromViewport; int32_t checkValue = (subViewportSizeOut * numberOfSubViewports) + (gapOut * (numberOfSubViewports - 1)); if (checkValue < viewportSize) { const bool makeViewportsLargerFlag = true; if (makeViewportsLargerFlag) { /* * Since viewports are integer values, may be able to add more to gaps */ const int32_t subViewportSizePlusOne = subViewportSizeOut + 1; const int32_t checkValueOne = (subViewportSizePlusOne * numberOfSubViewports) + (gapOut * (numberOfSubViewports - 1)); if (checkValueOne <= viewportSize) { subViewportSizeOut = subViewportSizePlusOne; checkValue = checkValueOne; const int32_t subViewportSizePlusTwo = subViewportSizeOut + 1; const int32_t checkValueTwo = (subViewportSizePlusTwo * numberOfSubViewports) + (gapOut * (numberOfSubViewports - 1)); if (checkValueTwo <= viewportSize) { subViewportSizeOut = subViewportSizePlusTwo; checkValue = checkValueTwo; } } } else { /* * Since viewports are integer values, may be able to add more to gaps */ const int32_t gapPlusOne = gapOut + 1; const int32_t checkValueOne = (subViewportSizeOut * numberOfSubViewports) + (gapPlusOne * (numberOfSubViewports - 1)); if (checkValueOne <= viewportSize) { gapOut = gapPlusOne; checkValue = checkValueOne; const int32_t gapPlusTwo = gapOut + 1; const int32_t checkValueTwo = (subViewportSizeOut * numberOfSubViewports) + (gapPlusTwo * (numberOfSubViewports - 1)); if (checkValueTwo <= viewportSize) { gapOut = gapPlusTwo; checkValue = checkValueTwo; } } } } CaretAssert(checkValue <= viewportSize); } } /** * Draw fiber orientations on surface models. * * @param structure * The structure. */ void BrainOpenGLFixedPipeline::drawSurfaceFiberOrientations(const StructureEnum::Enum structure) { drawFiberOrientations(NULL, structure); } /** * Draw the surface montage model. * @param browserTabContent * Content of the window. * @param surfaceMontageModel * The surface montage displayed in the window. * @param viewport * Region for drawing. */ void BrainOpenGLFixedPipeline::drawSurfaceMontageModel(BrowserTabContent* browserTabContent, ModelSurfaceMontage* surfaceMontageModel, const int32_t viewport[4]) { const int32_t tabIndex = browserTabContent->getTabNumber(); std::vector montageViewports; surfaceMontageModel->getSurfaceMontageViewportsForDrawing(tabIndex, montageViewports); if (montageViewports.empty()) { return; } GLint savedVP[4]; glGetIntegerv(GL_VIEWPORT, savedVP); int32_t numberOfRows = 0; int32_t numberOfColumns = 0; SurfaceMontageViewport::getNumberOfRowsAndColumns(montageViewports, numberOfRows, numberOfColumns); const GapsAndMargins* gapsAndMargins = m_brain->getGapsAndMargins(); int32_t subViewportHeight = 0; int32_t verticalGap = 0; createSubViewportSizeAndGaps(viewport[3], gapsAndMargins->getSurfaceMontageVerticalGapForWindow(m_windowIndex), -1, numberOfRows, subViewportHeight, verticalGap); int32_t subViewportWidth = 0; int32_t horizontalGap = 0; createSubViewportSizeAndGaps(viewport[2], gapsAndMargins->getSurfaceMontageHorizontalGapForWindow(m_windowIndex), -1, numberOfColumns, subViewportWidth, horizontalGap); const int32_t numberOfViewports = static_cast(montageViewports.size()); for (int32_t ivp = 0; ivp < numberOfViewports; ivp++) { SurfaceMontageViewport* mvp = montageViewports[ivp]; const float* nodeColoringRGBA = this->surfaceNodeColoring->colorSurfaceNodes(surfaceMontageModel, mvp->getSurface(), this->windowTabIndex); float center[3]; mvp->getSurface()->getBoundingBox()->getCenter(center); const int32_t rowFromTop = mvp->getRow(); const int32_t rowFromBottom = (numberOfRows - rowFromTop - 1); const int32_t column = mvp->getColumn(); const int32_t surfaceViewport[4] = { (viewport[0] + (column * (subViewportWidth + horizontalGap))), (viewport[1] + (rowFromBottom * (subViewportHeight + verticalGap))), subViewportWidth, subViewportHeight }; mvp->setViewport(surfaceViewport); this->setViewportAndOrthographicProjectionForSurfaceFile(surfaceViewport, mvp->getProjectionViewType(), mvp->getSurface()); this->applyViewingTransformations(surfaceMontageModel, center, mvp->getProjectionViewType()); this->drawSurface(mvp->getSurface(), nodeColoringRGBA, true); } glViewport(savedVP[0], savedVP[1], savedVP[2], savedVP[3]); } /** * Draw the whole brain. * @param browserTabContent * Content of the window. * @param wholeBrainModel * Model for whole brain. * @param viewport * Region for drawing. */ void BrainOpenGLFixedPipeline::drawWholeBrainModel(BrowserTabContent* browserTabContent, ModelWholeBrain* wholeBrainModel, const int32_t viewport[4]) { const int32_t tabNumberIndex = browserTabContent->getTabNumber(); Surface* leftSurface = wholeBrainModel->getSelectedSurface(StructureEnum::CORTEX_LEFT, tabNumberIndex); Surface* rightSurface = wholeBrainModel->getSelectedSurface(StructureEnum::CORTEX_RIGHT, tabNumberIndex); /* * Center using volume, if it is available * Otherwise, see if surface is available, but a surface is offset * from center so override the X-coordinate to zero. */ float center[3] = { 0.0, 0.0, 0.0 }; VolumeMappableInterface* underlayVolumeFile = wholeBrainModel->getUnderlayVolumeFile(tabNumberIndex); if (underlayVolumeFile != NULL) { BoundingBox volumeBoundingBox; underlayVolumeFile->getVoxelSpaceBoundingBox(volumeBoundingBox); volumeBoundingBox.getCenter(center); } if (leftSurface != NULL) { leftSurface->getBoundingBox()->getCenter(center); center[0] = 0.0; } else { if (rightSurface != NULL) { rightSurface->getBoundingBox()->getCenter(center); center[0] = 0.0; } } /* * Use a surface (if available) to set the orthographic projection size */ Surface* anySurface = NULL; if (leftSurface != NULL) { anySurface = leftSurface; } else if (rightSurface != NULL) { anySurface = rightSurface; } if (anySurface != NULL) { this->setViewportAndOrthographicProjectionForSurfaceFile(viewport, ProjectionViewTypeEnum::PROJECTION_VIEW_LEFT_LATERAL, anySurface); } else if (underlayVolumeFile != NULL) { this->setViewportAndOrthographicProjectionForWholeBrainVolume(viewport, ProjectionViewTypeEnum::PROJECTION_VIEW_LEFT_LATERAL, underlayVolumeFile); } else { this->setViewportAndOrthographicProjection(viewport, ProjectionViewTypeEnum::PROJECTION_VIEW_LEFT_LATERAL); } this->applyViewingTransformations(wholeBrainModel, center, ProjectionViewTypeEnum::PROJECTION_VIEW_LEFT_LATERAL); const SurfaceTypeEnum::Enum surfaceType = wholeBrainModel->getSelectedSurfaceType(tabNumberIndex); /* * Need depth testing for drawing slices */ glEnable(GL_DEPTH_TEST); /* * Determine volumes that are to be drawn */ if (underlayVolumeFile != NULL) { std::vector volumeDrawInfo; this->setupVolumeDrawInfo(browserTabContent, m_brain, volumeDrawInfo); if (volumeDrawInfo.empty() == false) { /* * Voxels as 3D */ drawVolumeVoxelsAsCubesWholeBrain(volumeDrawInfo); /* * Filter volumes for drawing and only draw those volumes that * are to be drawn as 2D volume slices. */ std::vector twoDimSliceDrawVolumeDrawInfo; for (std::vector::iterator iter = volumeDrawInfo.begin(); iter != volumeDrawInfo.end(); iter++) { bool useIt = false; VolumeDrawInfo& vdi = *iter; switch (vdi.wholeBrainVoxelDrawingMode) { case WholeBrainVoxelDrawingMode::DRAW_VOXELS_AS_THREE_D_CUBES: case WholeBrainVoxelDrawingMode::DRAW_VOXELS_AS_ROUNDED_THREE_D_CUBES: break; case WholeBrainVoxelDrawingMode::DRAW_VOXELS_ON_TWO_D_SLICES: useIt = true; break; } if (useIt) { twoDimSliceDrawVolumeDrawInfo.push_back(vdi); } } if ( ! twoDimSliceDrawVolumeDrawInfo.empty()) { /* * Check for oblique slice drawing */ VolumeSliceDrawingTypeEnum::Enum sliceDrawingType = browserTabContent->getSliceDrawingType(); VolumeSliceProjectionTypeEnum::Enum sliceProjectionType = browserTabContent->getSliceProjectionType(); BrainOpenGLVolumeSliceDrawing volumeSliceDrawing; volumeSliceDrawing.draw(this, browserTabContent, twoDimSliceDrawVolumeDrawInfo, //volumeDrawInfo, sliceDrawingType, sliceProjectionType, viewport); } } } drawSurfaceFiberOrientations(StructureEnum::ALL); drawSurfaceFiberTrajectories(StructureEnum::ALL); /* * Draw surfaces last so that opacity works. */ std::vector surfacesToDraw; const int32_t numberOfBrainStructures = m_brain->getNumberOfBrainStructures(); for (int32_t i = 0; i < numberOfBrainStructures; i++) { BrainStructure* brainStructure = m_brain->getBrainStructure(i); const StructureEnum::Enum structure = brainStructure->getStructure(); Surface* surface = wholeBrainModel->getSelectedSurface(structure, tabNumberIndex); if (surface != NULL) { bool drawIt = false; switch (structure) { case StructureEnum::CORTEX_LEFT: drawIt = browserTabContent->isWholeBrainLeftEnabled(); break; case StructureEnum::CORTEX_RIGHT: drawIt = browserTabContent->isWholeBrainRightEnabled(); break; case StructureEnum::CEREBELLUM: drawIt = browserTabContent->isWholeBrainCerebellumEnabled(); break; default: CaretLogWarning("programmer-issure: Surface type not left/right/cerebellum"); break; } if (drawIt) { surfacesToDraw.push_back(surface); } } } const int32_t numSurfaceToDraw = static_cast(surfacesToDraw.size()); for (int32_t i = 0; i < numSurfaceToDraw; i++) { CaretAssertVectorIndex(surfacesToDraw, i); Surface* surface = surfacesToDraw[i]; CaretAssert(surface); float dx = 0.0; float dy = 0.0; float dz = 0.0; switch (surface->getStructure()) { case StructureEnum::CORTEX_LEFT: dx = -browserTabContent->getWholeBrainLeftRightSeparation(); if ((surfaceType != SurfaceTypeEnum::ANATOMICAL) && (surfaceType != SurfaceTypeEnum::RECONSTRUCTION)) { dx -= surface->getBoundingBox()->getMaxX(); } break; case StructureEnum::CORTEX_RIGHT: dx = browserTabContent->getWholeBrainLeftRightSeparation(); if ((surfaceType != SurfaceTypeEnum::ANATOMICAL) && (surfaceType != SurfaceTypeEnum::RECONSTRUCTION)) { dx -= surface->getBoundingBox()->getMinX(); } break; case StructureEnum::CEREBELLUM: dz = browserTabContent->getWholeBrainCerebellumSeparation(); break; default: CaretLogWarning("programmer-issure: Surface type not left/right/cerebellum"); break; } if (surface != NULL) { /* * Draw the model annotations when the last surface is drawn. */ const bool drawModelSpaceAnnotationsFlag = (i == (numSurfaceToDraw - 1)); const float* nodeColoringRGBA = this->surfaceNodeColoring->colorSurfaceNodes(wholeBrainModel, surface, this->windowTabIndex); glPushMatrix(); glTranslatef(dx, dy, dz); this->drawSurface(surface, nodeColoringRGBA, drawModelSpaceAnnotationsFlag); 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(); // } // } // } } /** * Draw a chart model. * * @param browserTabContent * Content of browser tab. * @param chartModel * The chart model. * @param viewport * The viewport (x, y, width, height) */ void BrainOpenGLFixedPipeline::drawChartData(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); SelectionItemDataTypeEnum::Enum selectionItemDataType = SelectionItemDataTypeEnum::INVALID; int32_t scalarDataSeriesMapIndex = -1; switch (chartDataType) { case ChartDataTypeEnum::CHART_DATA_TYPE_INVALID: break; case ChartDataTypeEnum::CHART_DATA_TYPE_LINE_DATA_SERIES: cartesianChart = chartModel->getSelectedDataSeriesChartModel(tabIndex); selectionItemDataType = SelectionItemDataTypeEnum::CHART_DATA_SERIES; break; case ChartDataTypeEnum::CHART_DATA_TYPE_LINE_FREQUENCY_SERIES: cartesianChart = chartModel->getSelectedFrequencySeriesChartModel(tabIndex); selectionItemDataType = SelectionItemDataTypeEnum::CHART_FREQUENCY_SERIES; break; case ChartDataTypeEnum::CHART_DATA_TYPE_LINE_TIME_SERIES: cartesianChart = chartModel->getSelectedTimeSeriesChartModel(tabIndex); selectionItemDataType = SelectionItemDataTypeEnum::CHART_TIME_SERIES; break; case ChartDataTypeEnum::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: { CaretDataFileSelectionModel* fileModel = chartModel->getChartableMatrixSeriesFileSelectionModel(tabIndex); CaretDataFile* caretFile = fileModel->getSelectedFile(); if (caretFile != NULL) { ChartableMatrixSeriesInterface* matrixSeriesFile = dynamic_cast(caretFile); if (matrixSeriesFile != NULL) { matrixChartFile = matrixSeriesFile; selectionItemDataType = SelectionItemDataTypeEnum::CHART_MATRIX; scalarDataSeriesMapIndex = matrixSeriesFile->getSelectedMapIndex(tabIndex); } } } break; } if (cartesianChart != NULL) { BrainOpenGLChartDrawingFixedPipeline chartDrawing; chartDrawing.drawCartesianChart(m_brain, this, viewport, getTextRenderer(), cartesianChart, selectionItemDataType, this->windowTabIndex); } else if (matrixChartFile != NULL) { BrainOpenGLChartDrawingFixedPipeline chartDrawing; chartDrawing.drawMatrixChart(m_brain, this, viewport, getTextRenderer(), matrixChartFile, scalarDataSeriesMapIndex, selectionItemDataType, this->windowTabIndex); } } /** * 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) * @param projectionType * Type of view projection. * @param boundingBox * The bounding box used for maximum spatial extent. */ void BrainOpenGLFixedPipeline::setOrthographicProjectionForWithBoundingBox(const int32_t viewport[4], const ProjectionViewTypeEnum::Enum projectionType, const BoundingBox* boundingBox) { CaretAssert(boundingBox); /* * For a cortical surface, this largest dimension is the Y-Axis. * This worked correctly when the default view was dorsal with * the anterior pole at the top of the display and the posterior * pole at the bottom of the display. */ float modelHalfHeight = std::max(std::max(boundingBox->getDifferenceX(), boundingBox->getDifferenceY()), boundingBox->getDifferenceZ()) / 2.0; float modelHalfWidth = modelHalfHeight; float windowHorizontalSize = boundingBox->getDifferenceY(); float windowVerticalSize = boundingBox->getDifferenceZ(); switch (projectionType) { case ProjectionViewTypeEnum::PROJECTION_VIEW_CEREBELLUM_ANTERIOR: break; case ProjectionViewTypeEnum::PROJECTION_VIEW_CEREBELLUM_DORSAL: break; case ProjectionViewTypeEnum::PROJECTION_VIEW_CEREBELLUM_POSTERIOR: break; case ProjectionViewTypeEnum::PROJECTION_VIEW_CEREBELLUM_VENTRAL: break; case ProjectionViewTypeEnum::PROJECTION_VIEW_CEREBELLUM_FLAT_SURFACE: case ProjectionViewTypeEnum::PROJECTION_VIEW_LEFT_FLAT_SURFACE: case ProjectionViewTypeEnum::PROJECTION_VIEW_RIGHT_FLAT_SURFACE: windowHorizontalSize = boundingBox->getDifferenceX(); windowVerticalSize = boundingBox->getDifferenceY(); break; case ProjectionViewTypeEnum::PROJECTION_VIEW_LEFT_LATERAL: break; case ProjectionViewTypeEnum::PROJECTION_VIEW_LEFT_MEDIAL: break; case ProjectionViewTypeEnum::PROJECTION_VIEW_RIGHT_LATERAL: break; case ProjectionViewTypeEnum::PROJECTION_VIEW_RIGHT_MEDIAL: break; } /* * The default view was changed to a lateral view and the above * code results in problems during some window resize operations. * But, the Z-difference of a flat surface is zero. * * 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)) { /* * Note Z is vertical, Y is horizontal when viewed */ const float surfaceAspectRatio = windowVerticalSize / windowHorizontalSize; const float viewportAspectRatio = (static_cast(viewport[3]) / static_cast(viewport[2])); if (viewportAspectRatio > surfaceAspectRatio) { modelHalfWidth = windowHorizontalSize / 2.0; modelHalfHeight = modelHalfWidth * viewportAspectRatio; } } } const float orthoHeight = modelHalfHeight * 1.02; const float orthoWidth = modelHalfWidth * 1.02; const bool setWidthFromHeightFlag = true; if (setWidthFromHeightFlag) { setOrthographicProjectionWithHeight(viewport, projectionType, orthoHeight); } else { setOrthographicProjectionWithWidth(viewport, projectionType, orthoWidth); } } /** * Setup the orthographic projection with the given window height. * * @param viewport * The viewport (x, y, width, height) * @param projectionType * Type of view projection. * @param halfWindowHeight * Half of window height for model. */ void BrainOpenGLFixedPipeline::setOrthographicProjectionWithHeight(const int32_t viewport[4], const ProjectionViewTypeEnum::Enum projectionType, const float halfWindowHeight) { double width = viewport[2]; double height = viewport[3]; double aspectRatio = (width / height); this->orthographicRight = halfWindowHeight * aspectRatio; 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; switch (projectionType) { case ProjectionViewTypeEnum::PROJECTION_VIEW_CEREBELLUM_ANTERIOR: case ProjectionViewTypeEnum::PROJECTION_VIEW_CEREBELLUM_DORSAL: case ProjectionViewTypeEnum::PROJECTION_VIEW_CEREBELLUM_POSTERIOR: case ProjectionViewTypeEnum::PROJECTION_VIEW_CEREBELLUM_VENTRAL: glOrtho(this->orthographicLeft, this->orthographicRight, this->orthographicBottom, this->orthographicTop, this->orthographicNear, this->orthographicFar); break; case ProjectionViewTypeEnum::PROJECTION_VIEW_CEREBELLUM_FLAT_SURFACE: glOrtho(this->orthographicLeft, this->orthographicRight, this->orthographicBottom, this->orthographicTop, this->orthographicNear, this->orthographicFar); break; case ProjectionViewTypeEnum::PROJECTION_VIEW_LEFT_LATERAL: case ProjectionViewTypeEnum::PROJECTION_VIEW_LEFT_FLAT_SURFACE: case ProjectionViewTypeEnum::PROJECTION_VIEW_RIGHT_MEDIAL: glOrtho(this->orthographicLeft, this->orthographicRight, this->orthographicBottom, this->orthographicTop, this->orthographicNear, this->orthographicFar); break; case ProjectionViewTypeEnum::PROJECTION_VIEW_RIGHT_LATERAL: case ProjectionViewTypeEnum::PROJECTION_VIEW_LEFT_MEDIAL: case ProjectionViewTypeEnum::PROJECTION_VIEW_RIGHT_FLAT_SURFACE: glOrtho(this->orthographicRight, this->orthographicLeft, this->orthographicBottom, this->orthographicTop, this->orthographicFar, this->orthographicNear); break; } } /** * Setup the orthographic projection with the given window width. * * @param viewport * The viewport (x, y, width, height) * @param projectionType * Type of view projection. * @param halfWindowWidth * Half of window width for model. */ void BrainOpenGLFixedPipeline::setOrthographicProjectionWithWidth(const int32_t viewport[4], const ProjectionViewTypeEnum::Enum projectionType, const float halfWindowWidth) { 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; switch (projectionType) { case ProjectionViewTypeEnum::PROJECTION_VIEW_CEREBELLUM_ANTERIOR: case ProjectionViewTypeEnum::PROJECTION_VIEW_CEREBELLUM_DORSAL: case ProjectionViewTypeEnum::PROJECTION_VIEW_CEREBELLUM_POSTERIOR: case ProjectionViewTypeEnum::PROJECTION_VIEW_CEREBELLUM_VENTRAL: glOrtho(this->orthographicLeft, this->orthographicRight, this->orthographicBottom, this->orthographicTop, this->orthographicNear, this->orthographicFar); break; case ProjectionViewTypeEnum::PROJECTION_VIEW_CEREBELLUM_FLAT_SURFACE: glOrtho(this->orthographicLeft, this->orthographicRight, this->orthographicBottom, this->orthographicTop, this->orthographicNear, this->orthographicFar); break; case ProjectionViewTypeEnum::PROJECTION_VIEW_LEFT_LATERAL: case ProjectionViewTypeEnum::PROJECTION_VIEW_LEFT_FLAT_SURFACE: case ProjectionViewTypeEnum::PROJECTION_VIEW_RIGHT_MEDIAL: glOrtho(this->orthographicLeft, this->orthographicRight, this->orthographicBottom, this->orthographicTop, this->orthographicNear, this->orthographicFar); break; case ProjectionViewTypeEnum::PROJECTION_VIEW_RIGHT_LATERAL: case ProjectionViewTypeEnum::PROJECTION_VIEW_LEFT_MEDIAL: case ProjectionViewTypeEnum::PROJECTION_VIEW_RIGHT_FLAT_SURFACE: glOrtho(this->orthographicRight, this->orthographicLeft, this->orthographicBottom, this->orthographicTop, this->orthographicFar, this->orthographicNear); break; } } /** * check for an OpenGL Error. */ void BrainOpenGLFixedPipeline::checkForOpenGLError(const Model* model, const AString& msgIn) { 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); // } } /** * Analyze color information to extract identification data. * @param dataType * Type of data. * @param x * X-coordinate of identification. * @param y * X-coordinate of identification. * @param indexOut * Index of selected item. * @param depthOut * Depth of selected item. */ void BrainOpenGLFixedPipeline::getIndexFromColorSelection(SelectionItemDataTypeEnum::Enum dataType, const int32_t x, const int32_t y, int32_t& indexOut, float& depthOut) { /* * Saves glPixelStore parameters */ glPushClientAttrib(GL_CLIENT_PIXEL_STORE_BIT); // Figure out item was picked using color in color buffer // glReadBuffer(GL_BACK); glPixelStorei(GL_PACK_SKIP_ROWS, 0); glPixelStorei(GL_PACK_SKIP_PIXELS, 0); glPixelStorei(GL_PACK_ALIGNMENT, 1); uint8_t pixels[3]; glReadPixels((int)x, (int)y, 1, 1, GL_RGB, GL_UNSIGNED_BYTE, pixels); indexOut = -1; depthOut = -1.0; CaretLogFine("ID color is " + QString::number(pixels[0]) + ", " + QString::number(pixels[1]) + ", " + QString::number(pixels[2])); this->colorIdentification->getItem(pixels, dataType, &indexOut); if (indexOut >= 0) { /* * Get depth from depth buffer */ glPixelStorei(GL_PACK_ALIGNMENT, 4); glReadPixels(x, y, 1, 1, GL_DEPTH_COMPONENT, GL_FLOAT, &depthOut); } this->colorIdentification->reset(); glPopClientAttrib(); } /** * Analyze color information to extract identification data. * @param dataType * Type of data. * @param x * X-coordinate of identification. * @param y * X-coordinate of identification. * @param indexOut1 * First index of selected item. * @param indexOut2 * Second index of selected item. * @param depthOut * Depth of selected item. */ void BrainOpenGLFixedPipeline::getIndexFromColorSelection(SelectionItemDataTypeEnum::Enum dataType, const int32_t x, const int32_t y, int32_t& index1Out, int32_t& index2Out, float& depthOut) { /* * Saves glPixelStore parameters */ glPushClientAttrib(GL_CLIENT_PIXEL_STORE_BIT); // Figure out item was picked using color in color buffer // glReadBuffer(GL_BACK); glPixelStorei(GL_PACK_SKIP_ROWS, 0); glPixelStorei(GL_PACK_SKIP_PIXELS, 0); glPixelStorei(GL_PACK_ALIGNMENT, 1); uint8_t pixels[3]; glReadPixels((int)x, (int)y, 1, 1, GL_RGB, GL_UNSIGNED_BYTE, pixels); index1Out = -1; index2Out = -1; depthOut = -1.0; CaretLogFine("ID color is " + QString::number(pixels[0]) + ", " + QString::number(pixels[1]) + ", " + QString::number(pixels[2])); this->colorIdentification->getItem(pixels, dataType, &index1Out, &index2Out); if (index1Out >= 0) { /* * Get depth from depth buffer */ glPixelStorei(GL_PACK_ALIGNMENT, 4); glReadPixels(x, y, 1, 1, GL_DEPTH_COMPONENT, GL_FLOAT, &depthOut); } this->colorIdentification->reset(); glPopClientAttrib(); } /** * Analyze color information to extract identification data. * @param dataType * Type of data. * @param x * X-coordinate of identification. * @param y * X-coordinate of identification. * @param indexOut1 * First index of selected item. * @param indexOut2 * Second index of selected item. * @param indexOut3 * Third index of selected item. * @param depthOut * Depth of selected item. */ void BrainOpenGLFixedPipeline::getIndexFromColorSelection(SelectionItemDataTypeEnum::Enum dataType, const int32_t x, const int32_t y, int32_t& index1Out, int32_t& index2Out, int32_t& index3Out, float& depthOut) { /* * Saves glPixelStore parameters */ glPushClientAttrib(GL_CLIENT_PIXEL_STORE_BIT); // Figure out item was picked using color in color buffer // glReadBuffer(GL_BACK); glPixelStorei(GL_PACK_SKIP_ROWS, 0); glPixelStorei(GL_PACK_SKIP_PIXELS, 0); glPixelStorei(GL_PACK_ALIGNMENT, 1); uint8_t pixels[3]; glReadPixels((int)x, (int)y, 1, 1, GL_RGB, GL_UNSIGNED_BYTE, pixels); index1Out = -1; index2Out = -1; index3Out = -1; depthOut = -1.0; CaretLogFine("ID color is " + QString::number(pixels[0]) + ", " + QString::number(pixels[1]) + ", " + QString::number(pixels[2])); this->colorIdentification->getItem(pixels, dataType, &index1Out, &index2Out, &index3Out); if (index1Out >= 0) { /* * Get depth from depth buffer */ glPixelStorei(GL_PACK_ALIGNMENT, 4); glReadPixels(x, y, 1, 1, GL_DEPTH_COMPONENT, GL_FLOAT, &depthOut); } this->colorIdentification->reset(); glPopClientAttrib(); } /** * Set the selected item's screen coordinates. * @param item * Item that has screen coordinates set. * @param itemXYZ * Model's coordinate. */ void BrainOpenGLFixedPipeline::setSelectedItemScreenXYZ(SelectionItem* item, const float itemXYZ[3]) { GLdouble selectionModelviewMatrix[16]; glGetDoublev(GL_MODELVIEW_MATRIX, selectionModelviewMatrix); GLdouble selectionProjectionMatrix[16]; glGetDoublev(GL_PROJECTION_MATRIX, selectionProjectionMatrix); GLint selectionViewport[4]; glGetIntegerv(GL_VIEWPORT, selectionViewport); const double modelXYZ[3] = { itemXYZ[0], itemXYZ[1], itemXYZ[2] }; double windowPos[3]; if (gluProject(modelXYZ[0], modelXYZ[1], modelXYZ[2], selectionModelviewMatrix, selectionProjectionMatrix, selectionViewport, &windowPos[0], &windowPos[1], &windowPos[2])) { item->setScreenXYZ(windowPos); item->setModelXYZ(modelXYZ); } } /** * Draw sphere. * * @param rgba * Color for drawing. * @param diameter * Diameter of the sphere. */ void BrainOpenGLFixedPipeline::drawSphereWithDiameter(const float rgba[4], const double diameter) { glPushMatrix(); glScaled(diameter, diameter, diameter); m_shapeSphere->draw(rgba); glPopMatrix(); } /** * Draw sphere. * * @param rgba * Color for drawing. * @param diameter * Diameter of the sphere. */ void BrainOpenGLFixedPipeline::drawSphereWithDiameter(const uint8_t rgba[4], const double diameter) { glPushMatrix(); glScaled(diameter, diameter, diameter); m_shapeSphere->draw(rgba); glPopMatrix(); } /** * Draw cube. * * @param rgba * Color for drawing. * @param cubeSize * Size of the cube (distance from one face to its opposite face). */ void BrainOpenGLFixedPipeline::drawCube(const float rgba[4], const double cubeSize) { glPushMatrix(); glScaled(cubeSize, cubeSize, cubeSize); m_shapeCube->draw(rgba); glPopMatrix(); } /** * Draw a cuboid (3D Box) * * @param rgba * Color for drawing. * @param sizeX * X-Size of the cube (distance from -X face to its +X face). * @param sizeY * Y-Size of the cube (distance from -Y face to its +Y face). * @param sizeZ * Z-Size of the cube (distance from -Z face to its +X face). */ void BrainOpenGLFixedPipeline::drawCuboid(const uint8_t rgba[4], const double sizeX, const double sizeY, const double sizeZ) { glPushMatrix(); glScaled(sizeX, sizeY, sizeZ); m_shapeCube->draw(rgba); glPopMatrix(); } /** * Draw cube. * * @param rgba * Color for drawing. * @param cubeSize * Size of the cube (distance from one face to its opposite face). */ void BrainOpenGLFixedPipeline::drawRoundedCube(const float rgba[4], const double cubeSize) { glPushMatrix(); glScaled(cubeSize, cubeSize, cubeSize); m_shapeCubeRounded->draw(rgba); 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) * * @param rgba * Color for drawing. * @param sizeX * X-Size of the cube (distance from -X face to its +X face). * @param sizeY * Y-Size of the cube (distance from -Y face to its +Y face). * @param sizeZ * Z-Size of the cube (distance from -Z face to its +X face). */ void BrainOpenGLFixedPipeline::drawRoundedCuboid(const uint8_t rgba[4], const double sizeX, const double sizeY, const double sizeZ) { glPushMatrix(); glScaled(sizeX, sizeY, sizeZ); m_shapeCubeRounded->draw(rgba); glPopMatrix(); } /** * Draw a one millimeter square facing the user. * NOTE: This method will alter the current * modelviewing matrices so caller may need * to enclose the call to this method within * glPushMatrix() and glPopMatrix(). * * @param rgba * RGBA coloring ranging 0.0 to 1.0. * @param size * Size of square. */ void BrainOpenGLFixedPipeline::drawSquare(const float rgba[4], const float size) { if (this->inverseRotationMatrixValid) { glColor4fv(rgba); /* * Remove any rotation */ glMultMatrixd(this->inverseRotationMatrix); glScalef(size, size, size); /* * Draw both front and back side since in some instances, * such as surface montage, we are viweing from the far * side (from back of monitor) */ glBegin(GL_QUADS); glNormal3f(0.0, 0.0, 1.0); glVertex3f(-0.5, -0.5, 0.0); glVertex3f( 0.5, -0.5, 0.0); glVertex3f( 0.5, 0.5, 0.0); glVertex3f(-0.5, 0.5, 0.0); glNormal3f(0.0, 0.0, -1.0); glVertex3f(-0.5, -0.5, 0.0); glVertex3f(-0.5, 0.5, 0.0); glVertex3f( 0.5, 0.5, 0.0); glVertex3f( 0.5, -0.5, 0.0); glEnd(); } } /** * Draw a one millimeter square facing the user. * NOTE: This method will alter the current * modelviewing matrices so caller may need * to enclose the call to this method within * glPushMatrix() and glPopMatrix(). * * @param rgba * RGBA coloring ranging 0 to 255. * @param size * Size of square. */ void BrainOpenGLFixedPipeline::drawSquare(const uint8_t rgba[4], const float size) { if (this->inverseRotationMatrixValid) { glColor4ubv(rgba); /* * Remove any rotation */ glMultMatrixd(this->inverseRotationMatrix); glScalef(size, size, size); /* * Draw both front and back side since in some instances, * such as surface montage, we are viweing from the far * side (from back of monitor) */ glBegin(GL_QUADS); glNormal3f(0.0, 0.0, 1.0); glVertex3f(-0.5, -0.5, 0.0); glVertex3f( 0.5, -0.5, 0.0); glVertex3f( 0.5, 0.5, 0.0); glVertex3f(-0.5, 0.5, 0.0); glNormal3f(0.0, 0.0, -1.0); glVertex3f(-0.5, -0.5, 0.0); glVertex3f(-0.5, 0.5, 0.0); glVertex3f( 0.5, 0.5, 0.0); glVertex3f( 0.5, -0.5, 0.0); glEnd(); } } /** * Draw the user's selected image over the background * * vpContent * Viewport content which image is displayed. */ void BrainOpenGLFixedPipeline::drawBackgroundImage(BrainOpenGLViewportContent* vpContent) { BrowserTabContent* btc = vpContent->getBrowserTabContent(); if (btc == NULL) { return; } const float backZ = -990.0; const float middleZ = 0.0; const float frontZ = 990.0; DisplayPropertiesImages* dpi = m_brain->getDisplayPropertiesImages(); const int32_t tabIndex = btc->getTabNumber(); const DisplayGroupEnum::Enum displayGroup = dpi->getDisplayGroupForTab(tabIndex); if (dpi->isDisplayed(displayGroup, tabIndex)) { ImageFile* imageFile = dpi->getSelectedImageFile(displayGroup, tabIndex); if (imageFile != NULL) { float windowZ = 990.0; const ImageDepthPositionEnum::Enum depthPos = dpi->getImagePosition(displayGroup, tabIndex); switch (depthPos) { case ImageDepthPositionEnum::BACK: windowZ = backZ; break; case ImageDepthPositionEnum::FRONT: windowZ = frontZ; break; case ImageDepthPositionEnum::MIDDLE: windowZ = middleZ; break; } drawImage(vpContent, imageFile, windowZ, frontZ, dpi->getThresholdMinimum(displayGroup, tabIndex), dpi->getThresholdMaximum(displayGroup, tabIndex), dpi->getOpacity(displayGroup, tabIndex), dpi->isControlPointsDisplayed(displayGroup, tabIndex)); } } } /** * Draw the given image in the given viewport. * * @param viewport * The viewport dimensions. * @param image * The QImage that is drawn. * @param windowZ * Z-position for image. * @param frontZ * Z-position for front (used for control points) * @param minimumThreshold * Minimum threshold value. * @param maximumThreshold * Maximum threshold value. * @param opacity * Opacity. */ void BrainOpenGLFixedPipeline::drawImage(BrainOpenGLViewportContent* vpContent, ImageFile* imageFile, const float windowZ, const float frontZ, const float minimumThreshold, const float maximumThreshold, const float opacity, const bool drawControlPointsFlag) { CaretAssert(vpContent); const int32_t originalImageWidth = imageFile->getWidth(); const int32_t originalImageHeight = imageFile->getHeight(); const int32_t originalNumberOfPixels = originalImageWidth * originalImageHeight; if (originalNumberOfPixels <= 0) { return; } int viewport[4]; vpContent->getModelViewport(viewport); SelectionItemImage* idImage = m_brain->getSelectionManager()->getImageIdentification(); SelectionItemImageControlPoint* idControlPoint = m_brain->getSelectionManager()->getImageControlPointIdentification(); /* * Check for a 'selection' type mode */ bool isSelectImage = false; bool isSelectImageControlPoint = false; switch (this->mode) { case BrainOpenGLFixedPipeline::MODE_DRAWING: break; case BrainOpenGLFixedPipeline::MODE_IDENTIFICATION: if (idImage->isEnabledForSelection()) { isSelectImage = true; } if (idControlPoint->isEnabledForSelection()) { isSelectImageControlPoint = true; } if (isSelectImage || isSelectImageControlPoint) { glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); } else { return; } break; case BrainOpenGLFixedPipeline::MODE_PROJECTION: return; break; } /* * Normalized width/height * * > 1.0 ===> viewport dimension larger than image dimension * < 1.0 ===> viewport dimension smaller than image dimension */ const int32_t viewportWidth = viewport[2]; const int32_t viewportHeight = viewport[3]; const float widthNormalized = static_cast(viewportWidth) / static_cast(originalImageWidth); const float heightNormalized = static_cast(viewportHeight) / static_cast(originalImageHeight); /* * Scale image so that it fills window in one dimension and other * image dimension is less than or equal to the window dimension. */ float imageScale = 0.0; if (widthNormalized < heightNormalized) { imageScale = widthNormalized; } else { imageScale = heightNormalized; } std::vector imageBytesRGBA; int32_t imageWidth = originalImageWidth; int32_t imageHeight = originalImageHeight; if (imageScale > 0.0) { imageWidth = originalImageWidth * imageScale; imageHeight = originalImageHeight * imageScale; imageFile->getImageResizedBytes(ImageFile::IMAGE_DATA_ORIGIN_AT_BOTTOM, imageWidth, imageHeight, imageBytesRGBA); } else { int32_t dummyWidth = 0; int32_t dummyHeight = 0; imageFile->getImageBytesRGBA(ImageFile::IMAGE_DATA_ORIGIN_AT_BOTTOM, imageBytesRGBA, dummyWidth, dummyHeight); } const int32_t numberOfPixels = imageWidth * imageHeight; const int32_t bytesPerPixel = 4; const int32_t correctNumberOfBytes = numberOfPixels * bytesPerPixel; if (static_cast(imageBytesRGBA.size()) != correctNumberOfBytes) { CaretLogSevere("Image size is incorrect. Number of bytes is " + QString::number(imageBytesRGBA.size()) + " but should be " + QString::number(correctNumberOfBytes)); } const bool testThresholdFlag = ((minimumThreshold > 0.0) || (maximumThreshold < 255.0)); const bool testOpacityFlag = (opacity < 1.0); bool useBlendingFlag = false; if (testThresholdFlag || testOpacityFlag) { 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]; if (testThresholdFlag) { if ((imageBytesRGBA[i4] < minimumThreshold) || (imageBytesRGBA[i4] > maximumThreshold) || (imageBytesRGBA[i4+1] < minimumThreshold) || (imageBytesRGBA[i4+1] > maximumThreshold) || (imageBytesRGBA[i4+2] < minimumThreshold) || (imageBytesRGBA[i4+2] > maximumThreshold)) { pixelAlpha = 0; } } if (testOpacityFlag) { pixelAlpha = static_cast(pixelAlpha * opacity); } if (pixelAlpha < 255) { useBlendingFlag = true; } imageBytesRGBA[i4 + 3] = pixelAlpha; } } if (isSelectImage || isSelectImageControlPoint) { useBlendingFlag = false; } /* * Center image in the window */ const int32_t xHalfMargin = (viewportWidth - imageWidth) / 2.0; const int32_t xPos = std::max(xHalfMargin, 0); const int32_t yHalfMargin = (viewportHeight - imageHeight) / 2.0; const int32_t yPos = std::max(yHalfMargin, 0); /* * Reset orthographic projection to viewport size */ glMatrixMode(GL_PROJECTION); glPushMatrix(); glLoadIdentity(); const double maxClip = 1000.0; const double nearClip = -maxClip; const double farClip = maxClip; glOrtho(0, viewportWidth, 0, viewportHeight, nearClip, farClip); glMatrixMode(GL_MODELVIEW); glPushMatrix(); glLoadIdentity(); /* * Saves glPixelStore parameters */ glPushClientAttrib(GL_CLIENT_PIXEL_STORE_BIT); GLboolean blendingEnabled = GL_FALSE; glGetBooleanv(GL_BLEND, &blendingEnabled); if (useBlendingFlag) { glEnable(GL_BLEND); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); } /* * Set the image's Z coordinate where a depth percentage of 100.0 * is at the far clipping plane (away from viewer) and a percentage * of zero is at the near clipping plane (closest to viewer). * * Old way to set Z: const float imageZ = 10.0 - farClip; */ glRasterPos3f(xPos, yPos, windowZ); glDrawPixels(imageWidth, imageHeight, GL_RGBA, GL_UNSIGNED_BYTE, (GLvoid*)&imageBytesRGBA[0]); if (blendingEnabled == GL_FALSE) { glDisable(GL_BLEND); } glPopClientAttrib(); ControlPointFile* controlPointFile = imageFile->getControlPointFile(); if (drawControlPointsFlag) { const int32_t numControlPoints = controlPointFile->getNumberOfControlPoints(); if (numControlPoints > 0) { const uint8_t red[4] = { 255, 0, 0, 255 }; for (int32_t icp = 0; icp < numControlPoints; icp++) { const ControlPoint3D* cp = controlPointFile->getControlPointAtIndex(icp); const float pixelX = cp->getSourceX(); const float pixelY = cp->getSourceY(); const float percentX = pixelX / originalImageWidth; const float percentY = pixelY / originalImageHeight; const float x = xPos + (percentX * imageWidth); const float y = yPos + (percentY * imageHeight); glPushMatrix(); glTranslatef(x, y, frontZ); uint8_t rgba[4] = { red[0], red[1], red[2], red[3] }; if (isSelectImageControlPoint) { this->colorIdentification->addItem(rgba, SelectionItemDataTypeEnum::IMAGE_CONTROL_POINT, 0, // file index icp); // index in file rgba[3] = 255; } drawSphereWithDiameter(rgba, 10); glPopMatrix(); } } } glPopMatrix(); glMatrixMode(GL_PROJECTION); glPopMatrix(); glMatrixMode(GL_MODELVIEW); if (isSelectImage) { const float mx = this->mouseX - viewport[0]; const float my = this->mouseY - viewport[1]; const float imageX = mx - xPos; const float imageY = my - yPos; const float normalizedX = imageX / static_cast(imageWidth); const float normalizedY = imageY / static_cast(imageHeight); const int32_t pixelX = static_cast(normalizedX * static_cast(originalImageWidth)); const int32_t pixelY = static_cast(normalizedY * static_cast(originalImageHeight)); if ((pixelX >= 0) && (pixelX < originalImageWidth) && (pixelY >= 0) && (pixelY < originalImageHeight)) { idImage->setImageFile(imageFile); idImage->setPixelI(pixelX); idImage->setPixelJ(pixelY); uint8_t pixelByteRGBA[4]; if (imageFile->getImagePixelRGBA(ImageFile::IMAGE_DATA_ORIGIN_AT_BOTTOM, pixelX, pixelY, pixelByteRGBA)) { idImage->setPixelRGBA(pixelByteRGBA); } } } if (isSelectImageControlPoint) { int32_t fileIndex = -1; int32_t controlPointIndex = -1; float depth = -1.0; this->getIndexFromColorSelection(SelectionItemDataTypeEnum::IMAGE_CONTROL_POINT, this->mouseX, this->mouseY, fileIndex, controlPointIndex, depth); if ((fileIndex >= 0) && (controlPointIndex >= 0)) { if (idControlPoint->isOtherScreenDepthCloserToViewer(depth)) { ControlPoint3D* controlPoint = controlPointFile->getControlPointAtIndex(controlPointIndex); idControlPoint->setBrain(m_brain); idControlPoint->setImageFile(imageFile); idControlPoint->setControlPointFile(controlPointFile); idControlPoint->setControlPoint(controlPoint); idControlPoint->setControlPointIndexInFile(controlPointIndex); idControlPoint->setScreenDepth(depth); } } } } /** * Draw annnotation text at the given viewport coordinates using * the the annotations attributes for the style of text. * * Depth testing is ENABLED for this method * * @param viewportX * Viewport X-coordinate. * @param viewportY * Viewport Y-coordinate. * @param annotationText * Annotation text and attributes. */ void BrainOpenGLFixedPipeline::drawTextAtViewportCoords(const double viewportX, const double viewportY, const AnnotationText& annotationText) { if (getTextRenderer() != NULL) { getTextRenderer()->drawTextAtViewportCoords(viewportX, viewportY, annotationText); } } /** * Draw annnotation text at the given model coordinates using * the the annotations attributes for the style of text. * * Depth testing is ENABLED for this method * * @param modelX * Model X-coordinate. * @param modelY * Model Y-coordinate. * @param modelZ * Model Z-coordinate. * @param annotationText * Annotation text and attributes. */ void BrainOpenGLFixedPipeline::drawTextAtModelCoords(const double modelX, const double modelY, const double modelZ, const AnnotationText& annotationText) { if (getTextRenderer() != NULL) { getTextRenderer()->drawTextAtModelCoords(modelX, modelY, modelZ, annotationText); } } /** * Draw annnotation text at the given model coordinates using * the the annotations attributes for the style of text. * * @param modelXYZ * Model XYZ coordinates. * @param annotationText * Annotation text and attributes. */ void BrainOpenGLFixedPipeline::drawTextAtModelCoords(const double modelXYZ[3], const AnnotationText& annotationText) { if (getTextRenderer() != NULL) { getTextRenderer()->drawTextAtModelCoords(modelXYZ, annotationText); } } /** * Draw annnotation text at the given model coordinates using * the the annotations attributes for the style of text. * * @param modelXYZ * Model XYZ coordinates. * @param annotationText * Annotation text and attributes. */ void BrainOpenGLFixedPipeline::drawTextAtModelCoords(const float modelXYZ[3], const AnnotationText& annotationText) { if (getTextRenderer() != NULL) { getTextRenderer()->drawTextAtModelCoords(modelXYZ, annotationText); } } /** * 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.) */ AString BrainOpenGLFixedPipeline::getStateOfOpenGL() const { AString s = BrainOpenGL::getStateOfOpenGL(); s.appendWithNewLine("Fixed Pipeline State:"); s.appendWithNewLine(" " + getOpenGLEnabledEnumAsText("GL_BLEND", GL_BLEND)); s.appendWithNewLine(" " + getOpenGLEnabledEnumAsText("GL_CLIP_PLANE0", GL_CLIP_PLANE0)); s.appendWithNewLine(" " + getOpenGLEnabledEnumAsText("GL_CLIP_PLANE1", GL_CLIP_PLANE1)); s.appendWithNewLine(" " + getOpenGLEnabledEnumAsText("GL_CLIP_PLANE2", GL_CLIP_PLANE2)); s.appendWithNewLine(" " + getOpenGLEnabledEnumAsText("GL_CLIP_PLANE3", GL_CLIP_PLANE3)); s.appendWithNewLine(" " + getOpenGLEnabledEnumAsText("GL_CLIP_PLANE4", GL_CLIP_PLANE4)); s.appendWithNewLine(" " + getOpenGLEnabledEnumAsText("GL_CLIP_PLANE5", GL_CLIP_PLANE5)); 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_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)); s.appendWithNewLine(" " + getOpenGLEnabledEnumAsText("GL_LIGHT0", GL_LIGHT0)); s.appendWithNewLine(" " + getOpenGLEnabledEnumAsText("GL_LIGHT1", GL_LIGHT1)); s.appendWithNewLine(" " + getOpenGLEnabledEnumAsText("GL_LIGHT2", GL_LIGHT2)); s.appendWithNewLine(" " + getOpenGLEnabledEnumAsText("GL_LIGHT3", GL_LIGHT3)); s.appendWithNewLine(" " + getOpenGLEnabledEnumAsText("GL_LIGHT4", GL_LIGHT4)); s.appendWithNewLine(" " + getOpenGLEnabledEnumAsText("GL_LIGHT5", GL_LIGHT5)); s.appendWithNewLine(" " + getOpenGLLightAsText("GL_LIGHT0, GL_DIFFUSE", GL_LIGHT0, GL_DIFFUSE, 4)); s.appendWithNewLine(" " + getOpenGLLightAsText("GL_LIGHT0, GL_POSITION", GL_LIGHT0, GL_POSITION, 4)); s.appendWithNewLine(" " + getOpenGLLightAsText("GL_LIGHT1, GL_DIFFUSE", GL_LIGHT1, GL_DIFFUSE, 4)); s.appendWithNewLine(" " + getOpenGLLightAsText("GL_LIGHT1, GL_POSITION", GL_LIGHT1, GL_POSITION, 4)); s.appendWithNewLine(" " + getOpenGLFloatAsText("GL_LIGHT_MODEL_AMBIENT", GL_LIGHT_MODEL_AMBIENT, 4)); s.appendWithNewLine(" " + getOpenGLEnabledEnumAsText("GL_LINE_SMOOTH", GL_LINE_SMOOTH)); s.appendWithNewLine(" " + getOpenGLEnabledEnumAsText("GL_NORMALIZE", GL_NORMALIZE)); s.appendWithNewLine(" " + getOpenGLEnabledEnumAsText("GL_POLYGON_OFFSET_FILL", GL_POLYGON_OFFSET_FILL)); s.appendWithNewLine(" " + getOpenGLEnabledEnumAsText("GL_POLYGON_SMOOTH", GL_POLYGON_SMOOTH)); GLint frontFace; glGetIntegerv(GL_FRONT_FACE, &frontFace); AString frontFaceValue; switch (frontFace) { case GL_CW: frontFaceValue = "GL_CW"; break; case GL_CCW: frontFaceValue = "GL_CCW"; break; default: frontFaceValue = "INVALID"; break; } s.appendWithNewLine(" Front Face " + frontFaceValue); GLint cullFace; glGetIntegerv(GL_FRONT_FACE, &cullFace); AString cullFaceValue; switch (cullFace) { case GL_FRONT: cullFaceValue = "GL_FRONT"; break; case GL_BACK: cullFaceValue = "GL_BACK"; break; case GL_FRONT_AND_BACK: cullFaceValue = "GL_FRONT_AND_BACK"; break; default: cullFaceValue = "INVALID"; break; } s.appendWithNewLine(" Cull Face " + cullFaceValue); return s; } //============================================================================ /** * Constructor. */ BrainOpenGLFixedPipeline::VolumeDrawInfo::VolumeDrawInfo(CaretMappableDataFile* mapFile, VolumeMappableInterface* volumeFile, Brain* brain, PaletteColorMapping* paletteColorMapping, const FastStatistics* statistics, const WholeBrainVoxelDrawingMode::Enum wholeBrainVoxelDrawingMode, const int32_t mapIndex, const float opacity) : statistics(statistics) { this->mapFile = mapFile; this->volumeFile = volumeFile; this->brain = brain; this->paletteColorMapping = paletteColorMapping; this->wholeBrainVoxelDrawingMode = wholeBrainVoxelDrawingMode; this->mapIndex = mapIndex; this->opacity = opacity; } connectome-workbench-1.2.3+git41-gc4c6c90/src/Brain/BrainOpenGLFixedPipeline.h000066400000000000000000000655471300200146000265370ustar00rootroot00000000000000 #ifndef __BRAIN_OPENGL_FIXED_PIPELINE_H__ #define __BRAIN_OPENGL_FIXED_PIPELINE_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 "BrainConstants.h" #include "BrainOpenGL.h" #include "BrainOpenGLTextRenderInterface.h" #include "CaretPointer.h" #include "CaretVolumeExtension.h" #include "DisplayGroupEnum.h" #include "FiberOrientationColoringTypeEnum.h" #include "FiberOrientationSymbolTypeEnum.h" #include "FiberTrajectoryColorModel.h" #include "ProjectionViewTypeEnum.h" #include "SelectionItemDataTypeEnum.h" #include "StructureEnum.h" #include "SurfaceNodeColoring.h" #include "VolumeSliceViewPlaneEnum.h" #include "WholeBrainVoxelDrawingMode.h" class QGLWidget; class QImage; namespace caret { class AnnotationText; class BoundingBox; class Brain; class BrainOpenGLAnnotationDrawingFixedPipeline; class BrainOpenGLShapeCone; class BrainOpenGLShapeCube; class BrainOpenGLShapeCylinder; class BrainOpenGLShapeRing; class BrainOpenGLShapeRingOutline; class BrainOpenGLShapeSphere; class BrainOpenGLTextureManager; class BrainOpenGLViewportContent; class BrowserTabContent; class CaretMappableDataFile; class ClippingPlaneGroup; class FastStatistics; class DisplayPropertiesFiberOrientation; class FiberOrientation; class SelectionItem; class SelectionManager; class IdentificationWithColor; class ImageFile; class Plane; class Surface; class Model; class ModelChart; class ModelSurface; class ModelSurfaceMontage; class ModelVolume; class ModelWholeBrain; class Palette; class PaletteColorMapping; class PaletteFile; class SurfaceFile; class SurfaceMontageConfigurationCerebellar; class SurfaceMontageConfigurationCerebral; class SurfaceMontageConfigurationFlatMaps; class VolumeFile; class VolumeMappableInterface; /** * Performs drawing of graphics using OpenGL. */ class BrainOpenGLFixedPipeline : public BrainOpenGL { private: enum Mode { MODE_DRAWING, MODE_IDENTIFICATION, MODE_PROJECTION }; BrainOpenGLFixedPipeline(const BrainOpenGLFixedPipeline&); BrainOpenGLFixedPipeline& operator=(const BrainOpenGLFixedPipeline&); public: BrainOpenGLFixedPipeline(const int32_t windowIndex, 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(); virtual AString getStateOfOpenGL() const; static void createSubViewportSizeAndGaps(const int32_t viewportSize, const float gapPercentage, const int32_t gapOverride, const int32_t numberOfSubViewports, int32_t& subViewportSizeOut, int32_t& gapOut); virtual BrainOpenGLTextureManager* getTextureManager(); private: class VolumeDrawInfo { public: VolumeDrawInfo(CaretMappableDataFile* mapFile, VolumeMappableInterface* volumeFile, Brain* brain, PaletteColorMapping* paletteColorMapping, const FastStatistics* statistics, const WholeBrainVoxelDrawingMode::Enum wholeBrainVoxelDrawingMode, const int32_t mapIndex, const float opacity); Brain* brain; CaretMappableDataFile* mapFile; VolumeMappableInterface* volumeFile; SubvolumeAttributes::VolumeType volumeType; PaletteColorMapping* paletteColorMapping; WholeBrainVoxelDrawingMode::Enum wholeBrainVoxelDrawingMode; const FastStatistics* statistics; int32_t mapIndex; float opacity; }; struct FiberOrientationDisplayInfo { float aboveLimit; float belowLimit; FiberTrajectoryColorModel::Item* colorSource; FiberOrientationColoringTypeEnum::Enum fiberOrientationColorType; float fanMultiplier; bool isDrawWithMagnitude; float minimumMagnitude; float magnitudeMultiplier; Plane* plane; FiberOrientationSymbolTypeEnum::Enum symbolType; StructureEnum::Enum structure; }; void setFiberOrientationDisplayInfo(const DisplayPropertiesFiberOrientation* dpfo, const DisplayGroupEnum::Enum displayGroup, const int32_t tabIndex, Plane* plane, const StructureEnum::Enum structure, FiberTrajectoryColorModel::Item* colorSource, FiberOrientationDisplayInfo& dispInfo); void drawModelInternal(Mode mode, BrainOpenGLViewportContent* viewportContent); void initializeMembersBrainOpenGL(); void drawChartData(BrowserTabContent* browserTabContent, ModelChart* chartData, const int32_t viewport[4]); void drawSurfaceModel(ModelSurface* surfaceModel, const int32_t viewport[4]); void drawSurface(Surface* surface, const float* nodeColoringRGBA, const bool drawAnnotationsInModelSpaceFlag); void drawSurfaceNodes(Surface* surface, const float* nodeColoringRGBA); void drawSurfaceTrianglesWithVertexArrays(const Surface* surface, const float* nodeColoringRGBA); void drawSurfaceTriangles(Surface* surface, const float* nodeColoringRGBA); void drawSurfaceNodeAttributes(Surface* surface); void drawSurfaceBorderBeingDrawn(const Surface* surface); void drawSurfaceBorders(Surface* surface); struct BorderDrawInfo { Surface* anatomicalSurface; Surface* surface; Border* border; int32_t borderFileIndex; int32_t borderIndex; float rgba[4]; bool isSelect; bool isContralateralEnabled; bool isHighlightEndPoints; float unstretchedLinesLength; }; void drawBorder(const BorderDrawInfo& borderDrawInfo); bool unstretchedBorderLineTest(const float p1[3], const float p2[3], const float anat1[3], const float anat2[3], const float unstretchedLinesFactor) const; void drawSurfaceFoci(Surface* surface); void drawSurfaceNormalVectors(const Surface* surface); void drawSurfaceFiberOrientations(const StructureEnum::Enum structure); void drawFiberOrientations(const Plane* plane, const StructureEnum::Enum structure); void addFiberOrientationForDrawing(const FiberOrientationDisplayInfo* fodi, const FiberOrientation* fiberOrientation); void sortFiberOrientationsByDepth(); void drawAllFiberOrientations(const FiberOrientationDisplayInfo* fodi, const bool isSortFibers); void drawSurfaceFiberTrajectories(const StructureEnum::Enum structure); void drawFiberTrajectories(const Plane* plane, const StructureEnum::Enum structure); void drawVolumeModel(BrowserTabContent* browserTabContent, ModelVolume* volumeModel, const int32_t viewport[4]); void drawVolumeAxesCrosshairs( const VolumeSliceViewPlaneEnum::Enum slicePlane, const float voxelXYZ[3]); void drawVolumeAxesLabels(const VolumeSliceViewPlaneEnum::Enum slicePlane, const int32_t viewport[4]); void drawVolumeVoxelsAsCubesWholeBrain(std::vector& volumeDrawInfoIn); void drawVolumeOrthogonalSliceWholeBrain(const VolumeSliceViewPlaneEnum::Enum slicePlane, const int64_t sliceIndex, std::vector& volumeDrawInfoIn); void drawVolumeOrthogonalSliceVolumeViewer(const VolumeSliceViewPlaneEnum::Enum slicePlane, const int64_t sliceIndex, std::vector& volumeDrawInfo); void drawVolumeSurfaceOutlines(Brain* brain, Model* modelDisplayModel, BrowserTabContent* browserTabContent, const VolumeSliceViewPlaneEnum::Enum slicePlane, const int64_t sliceIndex, VolumeMappableInterface* underlayVolume); void drawVolumeFoci(Brain* brain, ModelVolume* modelVolume, BrowserTabContent* browserTabContent, const VolumeSliceViewPlaneEnum::Enum slicePlane, const int64_t sliceIndex, VolumeMappableInterface* underlayVolume); void drawVolumeFibers(Brain* brain, ModelVolume* modelVolume, BrowserTabContent* browserTabContent, const VolumeSliceViewPlaneEnum::Enum slicePlane, const int64_t sliceIndex, VolumeMappableInterface* underlayVolume); void setupVolumeDrawInfo(BrowserTabContent* browserTabContent, Brain* brain, std::vector& volumeDrawInfoOut); void drawWholeBrainModel(BrowserTabContent* browserTabContent, ModelWholeBrain* wholeBrainModel, const int32_t viewport[4]); void drawSurfaceMontageModel(BrowserTabContent* browserTabContent, ModelSurfaceMontage* surfaceMontageModel, const int32_t viewport[4]); void setOrthographicProjection(const int32_t viewport[4], const ProjectionViewTypeEnum::Enum projectionType); void setOrthographicProjectionForWithBoundingBox(const int32_t viewport[4], const ProjectionViewTypeEnum::Enum projectionType, const BoundingBox* boundingBox); void setOrthographicProjectionWithHeight(const int32_t viewport[4], const ProjectionViewTypeEnum::Enum projectionType, const float halfWindowHeight); void setOrthographicProjectionWithWidth(const int32_t viewport[4], const ProjectionViewTypeEnum::Enum projectionType, const float halfWindowWidgth); void checkForOpenGLError(const Model* modelModel, const AString& msg); void enableLighting(); void disableLighting(); void enableLineAntiAliasing(); void disableLineAntiAliasing(); void getIndexFromColorSelection(const SelectionItemDataTypeEnum::Enum dataType, const int32_t x, const int32_t y, int32_t& indexOut, float& depthOut); void getIndexFromColorSelection(const SelectionItemDataTypeEnum::Enum dataType, const int32_t x, const int32_t y, int32_t& index1Out, int32_t& index2Out, float& depthOut); void getIndexFromColorSelection(const SelectionItemDataTypeEnum::Enum dataType, const int32_t x, const int32_t y, int32_t& index1Out, int32_t& index2Out, int32_t& index3Out, float& depthOut); void setSelectedItemScreenXYZ(SelectionItem* item, const float itemXYZ[3]); void setViewportAndOrthographicProjection(const int32_t viewport[4], const ProjectionViewTypeEnum::Enum projectionType); void setViewportAndOrthographicProjectionForWholeBrainVolume(const int32_t viewport[4], const ProjectionViewTypeEnum::Enum projectionType, const VolumeMappableInterface* volume); void setViewportAndOrthographicProjectionForSurfaceFile(const int32_t viewport[4], const ProjectionViewTypeEnum::Enum projectionType, const SurfaceFile* surfaceFile); void applyViewingTransformations(const Model* model, const float objectCenterXYZ[3], const ProjectionViewTypeEnum::Enum projectionViewType); void applyViewingTransformationsVolumeSlice(const ModelVolume* modelVolume, const int32_t tabIndex, const VolumeSliceViewPlaneEnum::Enum viewPlane); 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); void drawSphereWithDiameter(const float rgba[4], const double diameter); void drawSquare(const float rgba[4], const float size); void drawSquare(const uint8_t rgba[4], const float size); void drawCube(const float rgba[4], const double cubeSize); void drawCuboid(const uint8_t rgba[4], const double sizeX, const double sizeY, const double sizeZ); void drawRoundedCube(const float rgba[4], const double cubeSize); void drawRoundedCuboid(const uint8_t rgba[4], const double sizeX, const double sizeY, const double sizeZ); void drawCylinder(const float rgba[4], const float bottomXYZ[3], const float topXYZ[3], const float radius); void drawEllipticalCone(const float rgba[4], const float baseXYZ[3], const float apexXYZ[3], const float baseRadiusScaling, const float baseMajorAngle, const float baseMinorAngle, const float baseRotationAngle, const bool backwardsFlag); void drawTextAtViewportCoords(const double viewportX, const double viewportY, const AnnotationText& annotationText); void drawTextAtModelCoords(const double modelX, const double modelY, const double modelZ, const AnnotationText& annotationText); void drawTextAtModelCoords(const double modelXYZ[3], const AnnotationText& annotationText); void drawTextAtModelCoords(const float modelXYZ[3], const AnnotationText& annotationText); void drawWindowAnnotations(const int windowViewport[4]); // void drawTabAnnotations(BrainOpenGLViewportContent* tabContent, // const int32_t tabViewport[4]); void drawTabAnnotations(BrainOpenGLViewportContent* tabContent); void drawAllPalettes(Brain* brain); void drawPalette(const Palette* palette, const PaletteColorMapping* paletteColorMapping, const FastStatistics* statistics, const int paletteDrawingIndex); void drawBackgroundImage(BrainOpenGLViewportContent* vpContent); void drawImage(BrainOpenGLViewportContent* vpContent, ImageFile* imageFile, const float windowZ, const float frontZ, const float minimumThreshold, const float maximumThreshold, const float opacity, const bool drawControlPointsFlag); void setProjectionModeData(const float screenDepth, const float xyz[3], const StructureEnum::Enum structure, const float barycentricAreas[3], 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, CLIPPING_DATA_TYPE_VOLUME }; void applyClippingPlanes(const ClippingDataType clippingDataType, const StructureEnum::Enum structureIn); void disableClippingPlanes(); bool isCoordinateInsideClippingPlanesForStructure(const StructureEnum::Enum structureIn, const float xyz[3]) const; bool isFeatureClippingEnabled() const; void getVolumeFitToWindowScalingAndTranslation(const VolumeMappableInterface* volume, const VolumeSliceViewPlaneEnum::Enum sliceViewPlane, const double orthographicExtent[6], float translationOut[3], float& scalingOut) const; Plane getPlaneForVolumeSliceIndex(const VolumeMappableInterface* volumeMappable, const VolumeSliceViewPlaneEnum::Enum slicePlane, const int64_t sliceIndex) const; void updateForegroundAndBackgroundColors(BrainOpenGLViewportContent* vpContent); void setTabViewport(const BrainOpenGLViewportContent* vpContent); void setAnnotationColorBarsForDrawing(std::vector& viewportContents); /** Index of window */ const int32_t m_windowIndex; /** Indicates OpenGL has been initialized */ bool initializedOpenGLFlag; /** Content of browser tab being drawn */ BrowserTabContent* browserTabContent; /** Source brain of content being drawn DOES NOT get deleted! */ Brain* m_brain; /** Index of window tab */ int32_t windowTabIndex; /** mode of operation draw/select/etc*/ Mode mode; /** Identification manager */ SelectionManager* selectionManager; int32_t mouseX; int32_t mouseY; /** Viewport for tab content that is being drawn */ int m_tabViewport[4]; /** Clipping plane group active in browser tab */ ClippingPlaneGroup* m_clippingPlaneGroup; /** * When mirrored clipping is enabled, the clipping region is * 'mirror flipped' for right structures and clipping performed * separately for left and right structures. Otherwise, one * clipping is used for all data. * * Model Enabled * ----- ------- * All NO * Montage YES * Surface YES * Volume NO */ bool m_mirroredClippingEnabled; /** Identify using color */ IdentificationWithColor* colorIdentification; SurfaceProjectedItem* modeProjectionData; /** Screen depth when projecting to surface mode */ double modeProjectionScreenDepth; /** Performs node coloring */ SurfaceNodeColoring* surfaceNodeColoring; /** Tile tabs active */ bool m_tileTabsActiveFlag; /** Sphere symbol */ BrainOpenGLShapeSphere* m_shapeSphere; /** Cone symbol */ BrainOpenGLShapeCone* m_shapeCone; /** 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; /** Cylinder symbol */ BrainOpenGLShapeCylinder* m_shapeCylinder; std::list m_fiberOrientationsForDrawing; double inverseRotationMatrix[16]; bool inverseRotationMatrixValid; double orthographicLeft; double orthographicRight; double orthographicBottom; double orthographicTop; double orthographicFar; double orthographicNear; CaretPointer m_annotationDrawing; std::vector m_annotationColorBarsForDrawing; /** The texture manager. */ CaretPointer m_textureManager; static bool s_staticInitialized; static const float s_gluLookAtCenterFromEyeOffsetDistance; static float COLOR_RED[3]; static float COLOR_GREEN[3]; static float COLOR_BLUE[3]; friend class BrainOpenGLAnnotationDrawingFixedPipeline; friend class BrainOpenGLChartDrawingFixedPipeline; friend class BrainOpenGLVolumeObliqueSliceDrawing; friend class BrainOpenGLVolumeSliceDrawing; friend class OldBrainOpenGLVolumeSliceDrawing; }; #ifdef __BRAIN_OPENGL_FIXED_PIPELINE_DEFINE_H bool BrainOpenGLFixedPipeline::s_staticInitialized = false; float BrainOpenGLFixedPipeline::COLOR_RED[3] = { 1.0, 0.0, 0.0 }; float BrainOpenGLFixedPipeline::COLOR_GREEN[3] = { 0.0, 1.0, 0.0 }; float BrainOpenGLFixedPipeline::COLOR_BLUE[3] = { 0.0, 0.0, 1.0 }; const float BrainOpenGLFixedPipeline::s_gluLookAtCenterFromEyeOffsetDistance = 1.0; #endif //__BRAIN_OPENGL_FIXED_PIPELINE_DEFINE_H } // namespace #endif // __BRAIN_OPENGL_FIXED_PIPELINE_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Brain/BrainOpenGLPrimitiveDrawing.cxx000066400000000000000000001144721300200146000276410ustar00rootroot00000000000000 /*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_OPEN_G_L_PRIMITIVE_DRAWING_DECLARE__ #include "BrainOpenGLPrimitiveDrawing.h" #undef __BRAIN_OPEN_G_L_PRIMITIVE_DRAWING_DECLARE__ #include "AString.h" #include "BrainOpenGL.h" #include "CaretOpenGLInclude.h" #include "CaretAssert.h" #include "CaretLogger.h" #include "MathFunctions.h" using namespace caret; /** * \class caret::BrainOpenGLPrimitiveDrawing * \brief Draws OpenGL Primitives (Triangles, Quads, etc.) * \ingroup Brain * * As OpenGL has evolved, functionality for drawing primitives (triangles, quads, * etc.) has been added. Users of this class only need to provide the * vertices, colors, normal vectors, and type of primitive for drawing. This * class will use the OpenGL primitive drawing most appropriate for the * OpenGL capabilities available at runtime. * * All public methods are static methods. */ /** * Constructor. */ BrainOpenGLPrimitiveDrawing::BrainOpenGLPrimitiveDrawing() { } /** * Destructor. */ BrainOpenGLPrimitiveDrawing::~BrainOpenGLPrimitiveDrawing() { } /** * Draw quads. * * @param coordinates * Coordinates of the quads. * @param normals * Normal vectors for the quads. * @param rgbaColors * RGBA colors for the quads. */ void BrainOpenGLPrimitiveDrawing::drawQuads(const std::vector& coordinates, const std::vector& normals, const std::vector& rgbaColors) { std::vector rgbaBytes; for (std::vector::const_iterator iter = rgbaColors.begin(); iter != rgbaColors.end(); iter++) { rgbaBytes.push_back(static_cast(*iter * 255.0)); } drawQuads(coordinates, normals, rgbaBytes); } /** * Draw quads. * * @param coordinates * Coordinates of the quads. * @param normals * Normal vectors for the quads. * @param rgbaColors * RGBA colors for the quads. */ void BrainOpenGLPrimitiveDrawing::drawQuads(const std::vector& coordinates, const std::vector& normals, const std::vector& rgbaColors) { const uint64_t numCoords = coordinates.size() / 3; const uint64_t numNormals = normals.size() / 3; const uint64_t numColors = rgbaColors.size() / 4; const uint64_t numQuads = numCoords / 4; // 4 three-d coords per quad if (numQuads <= 0) { return; } if (numNormals != numCoords) { const AString message = ("Size of normals must equal size of coordinates. " "Coordinate size: " + AString::number(coordinates.size()) + " Normals size: " + AString::number(normals.size())); CaretLogSevere(message); CaretAssertMessage(0, message); return; } if (numColors != numCoords) { const AString message = ("Size of RGBA colors must be 4/3 size of coordinates. " "Coordinate size: " + AString::number(coordinates.size()) + " RGBA size: " + AString::number(rgbaColors.size())); CaretLogSevere(message); CaretAssertMessage(0, message); return; } /* * Use the number of voxels in a 300 x 300 slice as * as the most maximum number of voxels to draw in * in one call to OpenGL drawing. */ const int64_t maximumCoordinatesToDraw = 300 * 300 * 4; CaretAssert(maximumCoordinatesToDraw == ((maximumCoordinatesToDraw / 4) * 4)); //std::cout << "Max vertices to draw: " << maximumCoordinatesToDraw << std::endl; int64_t coordinateOffset = 0; bool done = false; while ( ! done) { const int64_t coordinateToDrawCount = std::min(int64_t(numCoords - coordinateOffset), maximumCoordinatesToDraw); if (coordinateToDrawCount > 0) { //std::cout << "Drawing offset " << coordinateOffset << " count " << coordinateToDrawCount << std::endl; bool wasDrawnWithVertexBuffers = false; #ifdef BRAIN_OPENGL_INFO_SUPPORTS_VERTEX_BUFFERS if (BrainOpenGL::getBestDrawingMode() == BrainOpenGL::DRAW_MODE_VERTEX_BUFFERS) { CaretAssertMessage(0, "See John Harwell"); drawQuadsVertexBuffers(coordinates, normals, rgbaColors, coordinateOffset, coordinateToDrawCount); wasDrawnWithVertexBuffers = true; } #endif // BRAIN_OPENGL_INFO_SUPPORTS_VERTEX_BUFFERS if ( ! wasDrawnWithVertexBuffers) { drawQuadsVertexArrays(coordinates, normals, rgbaColors, coordinateOffset, coordinateToDrawCount); // drawQuadsImmediateMode(coordinates, // normals, // rgbaColors, // coordinateOffset, // coordinateToDrawCount); } coordinateOffset += maximumCoordinatesToDraw; } else { done = true; } } } /** * 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. * * @param coordinates * Coordinates of the quads. * @param normals * Normal vectors for the quads. * @param rgbaColors * RGBA colors for the quads. */ void BrainOpenGLPrimitiveDrawing::drawQuadsVertexArrays(const std::vector& coordinates, const std::vector& normals, const std::vector& rgbaColors, const int64_t coordinateOffset, const int64_t coordinateCount) { glEnableClientState(GL_VERTEX_ARRAY); glEnableClientState(GL_COLOR_ARRAY); glEnableClientState(GL_NORMAL_ARRAY); glVertexPointer(3, GL_FLOAT, 0, reinterpret_cast(&coordinates[0])); glColorPointer(4, GL_UNSIGNED_BYTE, 0, reinterpret_cast(&rgbaColors[0])); glNormalPointer(GL_FLOAT, 0, reinterpret_cast(&normals[0])); // const int64_t maxVertices = 1000; // const int64_t numQuads = maxVertices / 4; // CaretAssert(maxVertices == (numQuads * 4)); // if (numCoords > maxVertices) { // std::cout << "Max vertices: " << maxVertices << std::endl; // int64_t offset = 0; // bool done = false; // while ( ! done) { // const int64_t count = std::min(int64_t(numCoords - offset), // maxVertices); // if (count > 0) { // std::cout << "Drawing offset " << offset << " count " << count << std::endl; // glDrawArrays(GL_QUADS, // offset, // count); // offset += maxVertices; // } // else { // done = true; // } // } // } // else { // glDrawArrays(GL_QUADS, // 0, // numCoords); // } glDrawArrays(GL_QUADS, coordinateOffset, coordinateCount); glDisableClientState(GL_VERTEX_ARRAY); glDisableClientState(GL_COLOR_ARRAY); glDisableClientState(GL_NORMAL_ARRAY); } /** * Draw quads using vertex buffers. * * @param coordinates * Coordinates of the quads. * @param normals * Normal vectors for the quads. * @param rgbaColors * RGBA colors for the quads. */ void BrainOpenGLPrimitiveDrawing::drawQuadsVertexBuffers(const std::vector& coordinates, const std::vector& normals, const std::vector& rgbaColors, const int64_t coordinateOffset, const int64_t coordinateCount) { #ifdef BRAIN_OPENGL_INFO_SUPPORTS_VERTEX_BUFFERS /* * Put vertices (coordinates) into its buffer. */ GLuint vertexBufferID = -1; glGenBuffers(1, &vertexBufferID); glBindBuffer(GL_ARRAY_BUFFER, vertexBufferID); glBufferData(GL_ARRAY_BUFFER, coordinates.size() * sizeof(GLfloat), &coordinates[0], GL_STREAM_DRAW); /* * Put normals into its buffer. */ GLuint normalBufferID = -1; glGenBuffers(1, &normalBufferID); glBindBuffer(GL_ARRAY_BUFFER, normalBufferID); glBufferData(GL_ARRAY_BUFFER, normals.size() * sizeof(GLfloat), &normals[0], GL_STREAM_DRAW); /* * Put colors into its buffer. */ GLuint colorBufferID = -1; glGenBuffers(1, &colorBufferID); glBindBuffer(GL_ARRAY_BUFFER, colorBufferID); glBufferData(GL_ARRAY_BUFFER, rgbaColors.size() * sizeof(uint8_t), &rgbaColors[0], GL_STREAM_DRAW); // /* // * Put triangle strips into its buffer. // */ // GLuint quadsBufferID = -1; // glGenBuffers(1, &quadsBufferID); // glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, // quadsBufferID); // glBufferData(GL_ELEMENT_ARRAY_BUFFER, // quads.size() * sizeof(GLuint), // &quads[0], // GL_STREAM_DRAW); /* * Deselect active buffer. */ glBindBuffer(GL_ARRAY_BUFFER, 0); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); glEnableClientState(GL_VERTEX_ARRAY); glEnableClientState(GL_COLOR_ARRAY); glEnableClientState(GL_NORMAL_ARRAY); /* * Set the vertices for drawing. */ glBindBuffer(GL_ARRAY_BUFFER, vertexBufferID); glVertexPointer(3, GL_FLOAT, 0, (GLvoid*)0); /* * Set the normal vectors for drawing. */ glBindBuffer(GL_ARRAY_BUFFER, normalBufferID); glNormalPointer(GL_FLOAT, 0, (GLvoid*)0); /* * Set the rgba colors for drawing */ glBindBuffer(GL_ARRAY_BUFFER, colorBufferID); glColorPointer(4, GL_UNSIGNED_BYTE, 0, (GLvoid*)0); /* * Draw the triangle strips. */ glDrawArrays(GL_QUADS, coordinateOffset, coordinateCount); // glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, // quadsBufferID); // glDrawElements(GL_TRIANGLE_STRIP, // quads.size(), // GL_UNSIGNED_INT, // (GLvoid*)0); /* * Deselect active buffer. */ glBindBuffer(GL_ARRAY_BUFFER, 0); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); glDisableClientState(GL_VERTEX_ARRAY); glDisableClientState(GL_COLOR_ARRAY); glDisableClientState(GL_NORMAL_ARRAY); glDeleteBuffers(1, &vertexBufferID); glDeleteBuffers(1, &normalBufferID); glDeleteBuffers(1, &colorBufferID); #endif // BRAIN_OPENGL_INFO_SUPPORTS_VERTEX_BUFFERS } /** * Draw quads strips * * @param coordinates * Coordinates of the quads. * @param normals * Normal vectors for the quads. * @param rgbaColors * RGBA colors for the quads. * @param quadStripIndices * Indices into the quad strip coordinates. */ void BrainOpenGLPrimitiveDrawing::drawQuadStrips(const std::vector& coordinates, const std::vector& normals, const std::vector& rgbaColors, const std::vector& quadStripIndices) { drawQuadStripsVertexArrays(coordinates, normals, rgbaColors, quadStripIndices); } /** * Draw quads strips * * @param coordinates * Coordinates of the quads. * @param normals * Normal vectors for the quads. * @param rgbaColors * RGBA colors for the quads. * @param quadStripIndices * Indices into the quad strip coordinates. */ void BrainOpenGLPrimitiveDrawing::drawQuadStripsVertexArrays(const std::vector& coordinates, const std::vector& normals, const std::vector& rgbaColors, const std::vector& quadStripIndices) { if (quadStripIndices.empty()) { return; } glVertexPointer(3, GL_FLOAT, 0, reinterpret_cast(&coordinates[0])); glColorPointer(4, GL_UNSIGNED_BYTE, 0, reinterpret_cast(&rgbaColors[0])); glNormalPointer(GL_FLOAT, 0, reinterpret_cast(&normals[0])); glEnableClientState(GL_VERTEX_ARRAY); glEnableClientState(GL_COLOR_ARRAY); glEnableClientState(GL_NORMAL_ARRAY); glDrawElements(GL_QUAD_STRIP, quadStripIndices.size(), GL_UNSIGNED_INT, &quadStripIndices[0]); glDisableClientState(GL_VERTEX_ARRAY); glDisableClientState(GL_COLOR_ARRAY); glDisableClientState(GL_NORMAL_ARRAY); } /** * Draw quad with indices * * @param coordinates * Coordinates of the quads. * @param normals * Normal vectors for the quads. * @param rgbaColors * RGBA colors for the quads. * @param quadIndices * Indices of the quads (4 per quad). */ void BrainOpenGLPrimitiveDrawing::drawQuadIndices(const std::vector& coordinates, const std::vector& normals, const std::vector& rgbaColors, const std::vector& quadIndices) { drawQuadIndicesVertexArrays(coordinates, normals, rgbaColors, quadIndices); } /** * Draw quad with indices * * @param coordinates * Coordinates of the quads. * @param normals * Normal vectors for the quads. * @param rgbaColors * RGBA colors for the quads. * @param quadIndices * Indices of the quads (4 per quad). */ void BrainOpenGLPrimitiveDrawing::drawQuadIndicesVertexArrays(const std::vector& coordinates, const std::vector& normals, const std::vector& rgbaColors, const std::vector& quadIndices) { const int64_t numQuadIndices = static_cast(quadIndices.size()); if (numQuadIndices <= 0) { return; } glVertexPointer(3, GL_FLOAT, 0, reinterpret_cast(&coordinates[0])); glColorPointer(4, GL_UNSIGNED_BYTE, 0, reinterpret_cast(&rgbaColors[0])); glNormalPointer(GL_FLOAT, 0, reinterpret_cast(&normals[0])); glEnableClientState(GL_VERTEX_ARRAY); glEnableClientState(GL_COLOR_ARRAY); glEnableClientState(GL_NORMAL_ARRAY); glDrawElements(GL_QUADS, quadIndices.size(), GL_UNSIGNED_INT, &quadIndices[0]); glDisableClientState(GL_VERTEX_ARRAY); glDisableClientState(GL_COLOR_ARRAY); glDisableClientState(GL_NORMAL_ARRAY); } /** * 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::drawLineLoop(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(); } } connectome-workbench-1.2.3+git41-gc4c6c90/src/Brain/BrainOpenGLPrimitiveDrawing.h000066400000000000000000000152161300200146000272620ustar00rootroot00000000000000#ifndef __BRAIN_OPEN_G_L_PRIMITIVE_DRAWING_H__ #define __BRAIN_OPEN_G_L_PRIMITIVE_DRAWING_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 "stdint.h" #include #include "BrainOpenGL.h" namespace caret { class BrainOpenGLPrimitiveDrawing { public: static void drawQuads(const std::vector& coordinates, const std::vector& normals, const std::vector& rgbaColors); static void drawQuads(const std::vector& coordinates, const std::vector& normals, const std::vector& rgbaColors); static void drawQuadIndices(const std::vector& coordinates, const std::vector& normals, const std::vector& rgbaColors, const std::vector& quadIndices); static void drawQuadStrips(const std::vector& coordinates, const std::vector& normals, 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(); ~BrainOpenGLPrimitiveDrawing(); BrainOpenGLPrimitiveDrawing(const BrainOpenGLPrimitiveDrawing&); BrainOpenGLPrimitiveDrawing& operator=(const BrainOpenGLPrimitiveDrawing&); static void drawQuadsImmediateMode(const std::vector& coordinates, const std::vector& normals, const std::vector& rgbaColors, const int64_t coordinateOffset, const int64_t coordinateCount); static void drawQuadsVertexArrays(const std::vector& coordinates, const std::vector& normals, const std::vector& rgbaColors, const int64_t coordinateOffset, const int64_t coordinateCount); static void drawQuadStripsVertexArrays(const std::vector& coordinates, const std::vector& normals, const std::vector& rgbaColors, const std::vector& quadStripIndices); static void drawQuadsVertexBuffers(const std::vector& coordinates, const std::vector& normals, const std::vector& rgbaColors, const int64_t coordinateOffset, const int64_t coordinateCount); static void drawQuadIndicesVertexArrays(const std::vector& coordinates, 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__ // #endif // __BRAIN_OPEN_G_L_PRIMITIVE_DRAWING_DECLARE__ } // namespace #endif //__BRAIN_OPEN_G_L_PRIMITIVE_DRAWING_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Brain/BrainOpenGLShape.cxx000066400000000000000000000416201300200146000254070ustar00rootroot00000000000000 /*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_OPEN_GL_SHAPE_DECLARE__ #include "BrainOpenGLShape.h" #undef __BRAIN_OPEN_GL_SHAPE_DECLARE__ #include "BrainOpenGL.h" #include "CaretAssert.h" #include "CaretLogger.h" using namespace caret; /** * \class caret::BrainOpenGLShape * \brief Abstract class for shapes drawn in OpenGL. * * Subclasses should allocate the */ /** * Constructor. */ BrainOpenGLShape::BrainOpenGLShape() : CaretObject() { m_drawMode = BrainOpenGL::DRAW_MODE_INVALID; m_shapeSetupComplete = false; } /** * Destructor. */ BrainOpenGLShape::~BrainOpenGLShape() { releaseOpenGLAllocations(); } void BrainOpenGLShape::releaseOpenGLAllocations() { /* * Since 'releaseBufferIDInternal()' will alter 'm_bufferIDs' * make a copy of the set. Otherwise, the iterator will get * corrupted and a crash will occur. */ std::set bufferIDCopy = m_bufferIDs; for (std::set::iterator iter = bufferIDCopy.begin(); iter != bufferIDCopy.end(); iter++) { releaseBufferIDInternal(*iter); } m_bufferIDs.clear(); /* * Since 'releaseDisplayListInternal()' will alter 'm_displayLists' * make a copy of the set. Otherwise, the iterator will get * corrupted and a crash will occur. */ std::set displayListsCopy = m_displayLists; for (std::set::iterator iter = displayListsCopy.begin(); iter != displayListsCopy.end(); iter++) { releaseDisplayListInternal(*iter); } m_displayLists.clear(); } /** * Force drawing mode to immediate mode since display lists * do not work during image capture. * @param override * If true, immediate mode is always used until reset by * calling this with false. */ void BrainOpenGLShape::setImmediateModeOverride(const bool override) { s_immediateModeOverride = override; } /** * Draw the shape. * * @param rgba * Color for shape. */ void BrainOpenGLShape::draw(const uint8_t rgba[4]) { createShapeIfNeeded(); if (s_immediateModeOverride) { drawShape(BrainOpenGL::DRAW_MODE_IMMEDIATE, rgba); } else { drawShape(m_drawMode, rgba); } } /** * Create the shape or recreate it if drawing mode changed. */ void BrainOpenGLShape::createShapeIfNeeded() { if (m_drawMode == BrainOpenGL::DRAW_MODE_INVALID) { m_shapeSetupComplete = false; } if (m_drawMode != BrainOpenGL::getBestDrawingMode()) { m_shapeSetupComplete = false; } if (m_shapeSetupComplete == false) { releaseOpenGLAllocations(); m_drawMode = BrainOpenGL::getBestDrawingMode(); setupOpenGLForShape(m_drawMode); m_shapeSetupComplete = true; } } /** * Draw the shape. * * @param rgba * Color for shape. */ void BrainOpenGLShape::draw(const float rgba[4]) { createShapeIfNeeded(); if (s_immediateModeOverride) { drawShape(BrainOpenGL::DRAW_MODE_IMMEDIATE, rgba); } else { drawShape(m_drawMode, rgba); } } /** * @return A new buffer ID for use with OpenGL. * A return value of zero indicates that creation of buffer ID failed. * Values greater than zero are valid buffer IDs. */ GLuint BrainOpenGLShape::createBufferID() { GLuint id = 0; #ifdef BRAIN_OPENGL_INFO_SUPPORTS_VERTEX_BUFFERS glGenBuffers(1, &id); if (id > 0) { m_bufferIDs.insert(id); } else { CaretLogSevere("Failed to create a new OpenGL Vertex Buffer"); } #else // BRAIN_OPENGL_INFO_SUPPORTS_VERTEX_BUFFERS CaretLogSevere("PROGRAM ERROR: Creating OpenGL vertex buffer but vertex buffers not supported."); #endif // BRAIN_OPENGL_INFO_SUPPORTS_VERTEX_BUFFERS return id; } /** * Release an allocated buffer ID. * * @param bufferID * Buffer ID that was previously returned by createBufferID(). */ void BrainOpenGLShape::releaseBufferID(const GLuint bufferID) { releaseBufferIDInternal(bufferID); } /** * Release an allocated buffer ID. * * @param bufferID * Buffer ID that was previously returned by createBufferID(). */ void BrainOpenGLShape::releaseBufferIDInternal(const GLuint bufferID) { #ifdef BRAIN_OPENGL_INFO_SUPPORTS_VERTEX_BUFFERS if (glIsBuffer(bufferID)) { glDeleteBuffers(1, &bufferID); m_bufferIDs.erase(bufferID); } else { CaretLogSevere("PROGRAM ERROR: Attempting to delete invalid OpenGL BufferID=" + AString::number(bufferID)); } #else // BRAIN_OPENGL_INFO_SUPPORTS_VERTEX_BUFFERS CaretLogSevere("PROGRAM ERROR: Releasing OpenGL vertex buffer #" + AString::number(bufferID) + " but vertex buffers not supported."); #endif // BRAIN_OPENGL_INFO_SUPPORTS_VERTEX_BUFFERS } /** * Release an allocated buffer ID. * * @param bufferID * Buffer ID that was previously returned by createBufferID(). * @param isRemoveFromTrackedIDs * If true, remove the ID from the bufferID that are tracked * by this object. */ void BrainOpenGLShape::releaseDisplayListInternal(const GLuint displayList) { #ifdef BRAIN_OPENGL_INFO_SUPPORTS_DISPLAY_LISTS if (BrainOpenGL::isDisplayListsSupported()) { if (glIsList(displayList)) { glDeleteLists(displayList, 1); m_displayLists.erase(displayList); } else { CaretLogSevere("PROGRAM ERROR: Attempting to delete invalid OpenGL DisplayList=" + AString::number(displayList)); } } #else // BRAIN_OPENGL_INFO_SUPPORTS_DISPLAY_LISTS CaretLogSevere("PROGRAM ERROR: Releasing OpenGL display list #" + AString::number(displayList) + " but display lists not supported."); #endif // BRAIN_OPENGL_INFO_SUPPORTS_DISPLAY_LISTS } /** * @return A new display list for use with OpenGL. * A return value of zero indicates that creation of display list failed. * Values greater than zero are valid display list. */ GLuint BrainOpenGLShape::createDisplayList() { GLuint displayList = 0; #ifdef BRAIN_OPENGL_INFO_SUPPORTS_DISPLAY_LISTS if (BrainOpenGL::isDisplayListsSupported()) { displayList = glGenLists(1); if (displayList > 0) { m_displayLists.insert(displayList); } else { CaretLogSevere("Failed to create a new OpenGL Display List"); } } #else // BRAIN_OPENGL_INFO_SUPPORTS_DISPLAY_LISTS CaretLogSevere("PROGRAM ERROR: Creating OpenGL display list but display lists not supported."); #endif // BRAIN_OPENGL_INFO_SUPPORTS_DISPLAY_LISTS return displayList; } /** * Release an allocated display list. * * @param displayList * Display list that was previously returned by createDisplayList(). */ void BrainOpenGLShape::releaseDisplayList(const GLuint displayList) { releaseDisplayListInternal(displayList); } /** * Convert a series of discontinuous triangle strips into one triangle strip. * Between each of the original triangle strips, additional vertices forming * degenerate (arealess) triangles are added. This triangles are not drawn * by OpenGl. * * @see http://www.gamedev.net/page/resources/_/technical/graphics-programming-and-theory/concatenating-triangle-strips-r1871 * @see http://en.wikipedia.org/wiki/Triangle_strip * * @param vertices * The indices to all of the vertices in the input triangle strips. * @param stripStartIndices * A vector containing the starting index of the strip in 'vertices'. * @param stripEndIndices * A vector containing the ending index of the strip in 'vertices'. * @param triangleStripVerticesOut * OUTPUT - A vector containing all of the vertex indices for drawing * all of the input triangles with one triangle strip. */ void BrainOpenGLShape::contatenateTriangleStrips(const std::vector& vertices, const std::vector& stripStartIndices, const std::vector& stripEndIndices, std::vector& triangleStripVerticesOut) const { triangleStripVerticesOut.clear(); CaretAssert(stripStartIndices.size() == stripEndIndices.size()); const int32_t numberOfStrips = static_cast(stripStartIndices.size()); for (int32_t i = 0; i < numberOfStrips; i++) { const int32_t numInStrip = (stripEndIndices[i] - stripStartIndices[i]); if (numInStrip < 2) { continue; } const int32_t startIndex = stripStartIndices[i]; CaretAssertVectorIndex(vertices, startIndex); //const int32_t endIndex = stripEndIndices[i]; //CaretAssertVectorIndex(vertices, endIndex); if (i > 0) { /* * N1 is index of first vertex in the strip * N2 is index of second vertex in the strip */ const GLuint n1 = vertices[startIndex]; CaretAssertVectorIndex(vertices, startIndex+1); const GLuint n2 = vertices[startIndex + 1]; const GLuint prevEndIndex = stripEndIndices[i-1]; const int32_t numInPrevStrip = (prevEndIndex - stripStartIndices[i-1]); if (numInPrevStrip >= 2) { /* * P1 is index of last vertex in previous strip * P2 is index of second to last vertex in previous strip */ CaretAssertVectorIndex(vertices, ((int32_t)prevEndIndex) - 1); const GLuint p1 = vertices[prevEndIndex - 1]; CaretAssertVectorIndex(vertices, ((int32_t)prevEndIndex) - 2); const GLuint p2 = vertices[prevEndIndex - 2]; /* * Add degenerate triangles so that strips can be concatenated. */ triangleStripVerticesOut.push_back(p2); triangleStripVerticesOut.push_back(p1); triangleStripVerticesOut.push_back(p1); triangleStripVerticesOut.push_back(p1); triangleStripVerticesOut.push_back(p1); triangleStripVerticesOut.push_back(n1); triangleStripVerticesOut.push_back(p1); triangleStripVerticesOut.push_back(n1); triangleStripVerticesOut.push_back(n1); triangleStripVerticesOut.push_back(n1); triangleStripVerticesOut.push_back(n1); triangleStripVerticesOut.push_back(n2); triangleStripVerticesOut.push_back(n1); triangleStripVerticesOut.push_back(n2); triangleStripVerticesOut.push_back(n1); triangleStripVerticesOut.push_back(n1); triangleStripVerticesOut.push_back(n1); triangleStripVerticesOut.push_back(n2); } } const int32_t vertexStart = stripStartIndices[i]; const int32_t vertexEnd = stripEndIndices[i]; for (int32_t iVertex = vertexStart; iVertex < vertexEnd; iVertex++) { CaretAssertVectorIndex(vertices, iVertex); const int32_t vertexIndex = vertices[iVertex]; triangleStripVerticesOut.push_back(vertexIndex); } } } /** * Convert a series of discontinuous triangle strips into one triangle strip. * Between each of the original triangle strips, additional vertices forming * degenerate (arealess) triangles are added. This triangles are not drawn * by OpenGl. * * @see http://www.gamedev.net/page/resources/_/technical/graphics-programming-and-theory/concatenating-triangle-strips-r1871 * @see http://en.wikipedia.org/wiki/Triangle_strip * * @param triangleStrips * The triangle strips. Each contains a sequence of vertex indices. * @param triangleStripOut * OUTPUT - A vector containing all of the vertex indices for drawing * all of the input triangles with one triangle strip. */ void BrainOpenGLShape::contatenateTriangleStrips(const std::vector >& triangleStrips, std::vector& triangleStripOut) const { triangleStripOut.clear(); const int32_t numberOfStrips = static_cast(triangleStrips.size()); for (int32_t i = 0; i < numberOfStrips; i++) { const std::vector& strip = triangleStrips[i]; const int32_t numInStrip = static_cast(strip.size()); if (numInStrip < 2) { continue; } if (i > 0) { /* * N1 is index of first vertex in the strip * N2 is index of second vertex in the strip */ CaretAssertVectorIndex(strip, 0); const GLuint n1 = strip[0]; CaretAssertVectorIndex(strip, 1); const GLuint n2 = strip[1]; const std::vector& prevStrip = triangleStrips[i - 1]; const int32_t numInPrevStrip = static_cast(prevStrip.size()); if (numInPrevStrip >= 2) { /* * P1 is index of last vertex in previous strip * P2 is index of second to last vertex in previous strip */ CaretAssertVectorIndex(prevStrip, numInPrevStrip - 1); const GLuint p1 = prevStrip[numInPrevStrip - 1]; CaretAssertVectorIndex(prevStrip, numInPrevStrip - 2); const GLuint p2 = prevStrip[numInPrevStrip - 2]; /* * Add degenerate triangles so that strips can be concatenated. */ triangleStripOut.push_back(p2); triangleStripOut.push_back(p1); triangleStripOut.push_back(p1); triangleStripOut.push_back(p1); triangleStripOut.push_back(p1); triangleStripOut.push_back(n1); triangleStripOut.push_back(p1); triangleStripOut.push_back(n1); triangleStripOut.push_back(n1); triangleStripOut.push_back(n1); triangleStripOut.push_back(n1); triangleStripOut.push_back(n2); triangleStripOut.push_back(n1); triangleStripOut.push_back(n2); triangleStripOut.push_back(n1); triangleStripOut.push_back(n1); triangleStripOut.push_back(n1); triangleStripOut.push_back(n2); } } for (int32_t j = 0; j < numInStrip; j++) { triangleStripOut.push_back(strip[j]); } } } /** * Print the vertices in a triangle strip. Each triplet is contained * within a set of parenthesis. * * @param triangleStrip * Triangle strip that is printed. */ void BrainOpenGLShape::printTriangleStrip(const std::vector& triangleStrip) const { int i3 = 0; for (std::vector::const_iterator iter = triangleStrip.begin(); iter != triangleStrip.end(); iter++) { if (i3 == 0) { std::cout << " ("; } else { std::cout << ","; } std::cout << *iter; if (i3 == 2) { std::cout << ")"; i3 = 0; } else { i3++; } } std::cout << std::endl << std::endl; } /** * Print the vertices in a triangle fan * * @param triangleFan * Triangle fan that is printed. */ void BrainOpenGLShape::printTriangleFan(const std::vector& triangleFan) const { const int32_t numVertices = static_cast(triangleFan.size()); for (int32_t i = 0; i < numVertices; i++) { const GLuint vertexIndex = triangleFan[i]; if (i == 0) { std::cout << "Center ("; } else if (i > 1) { std::cout << ", "; } std::cout << vertexIndex; if (i == 0) { std::cout << ") Perimeter ("; } } if (numVertices > 1) { std::cout << ")"; } std::cout << std::endl << std::endl; } connectome-workbench-1.2.3+git41-gc4c6c90/src/Brain/BrainOpenGLShape.h000066400000000000000000000103701300200146000250320ustar00rootroot00000000000000#ifndef __BRAIN_OPEN_GL_SHAPE__H_ #define __BRAIN_OPEN_GL_SHAPE__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 "BrainOpenGL.h" namespace caret { class BrainOpenGLShape : public CaretObject { public: BrainOpenGLShape(); virtual ~BrainOpenGLShape(); // ADD_NEW_METHODS_HERE void draw(const uint8_t rgba[4]); void draw(const float rgba[4]); static void setImmediateModeOverride(const bool override); private: BrainOpenGLShape(const BrainOpenGLShape&); BrainOpenGLShape& operator=(const BrainOpenGLShape&); void releaseBufferIDInternal(const GLuint bufferID); void releaseDisplayListInternal(const GLuint displayList); protected: /** * Setup for drawing the shape with OpenGL. This may include * creating display lists, allocating buffers, and other calls * to the OpenGL API. The base class handles alllocation and * deallocation of OpenGL display lists and buffers. * * @param drawMode * How the shape will be drawn. */ virtual void setupOpenGLForShape(const BrainOpenGL::DrawMode drawMode) = 0; /** * Draw the shape using the given drawing mode. * * @param drawMode * How the shape will be drawn. * @param rgba * RGBA coloring ranging 0 to 255. */ virtual void drawShape(const BrainOpenGL::DrawMode drawMode, const uint8_t rgba[4]) = 0; /** * Draw the shape using the given drawing mode. * * @param drawMode * How the shape will be drawn. * @param rgba * RGBA coloring ranging 0.0 to 1.0. */ virtual void drawShape(const BrainOpenGL::DrawMode drawMode, const float rgba[4]) = 0; GLuint createBufferID(); void releaseBufferID(const GLuint bufferID); GLuint createDisplayList(); void releaseDisplayList(const GLuint displayList); void contatenateTriangleStrips(const std::vector& vertices, const std::vector& stripStartIndices, const std::vector& stripEndIndices, std::vector& triangleStripVerticesOut) const; void printTriangleStrip(const std::vector& triangleStrip) const; void printTriangleFan(const std::vector& triangleFan) const; void contatenateTriangleStrips(const std::vector >& triangleStrips, std::vector& triangleStripOut) const; private: void createShapeIfNeeded(); void releaseOpenGLAllocations(); // ADD_NEW_MEMBERS_HERE std::set m_bufferIDs; std::set m_displayLists; bool m_shapeSetupComplete; BrainOpenGL::DrawMode m_drawMode; static bool s_immediateModeOverride; }; #ifdef __BRAIN_OPEN_GL_SHAPE_DECLARE__ bool BrainOpenGLShape::s_immediateModeOverride = false; #endif // __BRAIN_OPEN_GL_SHAPE_DECLARE__ } // namespace #endif //__BRAIN_OPEN_GL_SHAPE__H_ connectome-workbench-1.2.3+git41-gc4c6c90/src/Brain/BrainOpenGLShapeCone.cxx000066400000000000000000000441261300200146000262200ustar00rootroot00000000000000 /*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_CONE_DECLARE__ #include "BrainOpenGLShapeCone.h" #undef __BRAIN_OPEN_GL_SHAPE_CONE_DECLARE__ #include "CaretAssert.h" #include "MathFunctions.h" using namespace caret; /** * \class caret::BrainOpenGLShapeCone * \brief Create a conical shape for use with OpenGL. */ /** * Constructor. * @param numberOfSides * Number of sides in the cone. */ BrainOpenGLShapeCone::BrainOpenGLShapeCone(const int32_t numberOfSides) : BrainOpenGLShape(), m_numberOfSides(numberOfSides) { m_isApplyColoring = true; bool debugFlag = false; /* * Add origin (apex) to points */ const int32_t apexIndex = static_cast(m_coordinates.size() / 3); const float origin[3] = { 0.0, 0.0, 0.0 }; m_coordinates.push_back(origin[0]); m_coordinates.push_back(origin[1]); m_coordinates.push_back(origin[2]); m_sideNormals.push_back(0.0); m_sideNormals.push_back(0.0); m_sideNormals.push_back(-1.0); m_capNormals.push_back(0.0); m_capNormals.push_back(0.0); m_capNormals.push_back(1.0); /* * 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 circle */ const float radius = 0.5; const float z = 1.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 = radius * std::cos(t); const float y = radius * std::sin(t); m_coordinates.push_back(x); m_coordinates.push_back(y); m_coordinates.push_back(z); m_sideNormals.push_back(0.0); m_sideNormals.push_back(0.0); m_sideNormals.push_back(1.0); m_capNormals.push_back(0.0); m_capNormals.push_back(0.0); m_capNormals.push_back(1.0); } /* * Add cap center */ const int32_t capCenterIndex = static_cast(m_coordinates.size() / 3); m_coordinates.push_back(origin[0]); m_coordinates.push_back(origin[1]); m_coordinates.push_back(origin[2]); m_sideNormals.push_back(0.0); m_sideNormals.push_back(0.0); m_sideNormals.push_back(1.0); m_capNormals.push_back(0.0); m_capNormals.push_back(0.0); m_capNormals.push_back(1.0); /* * Add normal vectors of triangles into normal vector summations * NOTE: Vertex at index zero is the apex */ for (int32_t i = 1; i <= m_numberOfSides; i++) { int32_t nextIndex = i + 1; if (nextIndex > m_numberOfSides) { nextIndex = 1; } /* * Normal of triangle */ const int32_t i3 = i * 3; const int32_t next3 = nextIndex * 3; float triangleNormal[3]; MathFunctions::normalVector(origin, &m_coordinates[i3], &m_coordinates[next3], triangleNormal); CaretAssertVectorIndex(m_sideNormals, i3+2); m_sideNormals[i3] += triangleNormal[0]; m_sideNormals[i3+1] += triangleNormal[1]; m_sideNormals[i3+2] += triangleNormal[2]; CaretAssertVectorIndex(m_sideNormals, next3+2); m_sideNormals[next3] += triangleNormal[0]; m_sideNormals[next3+1] += triangleNormal[1]; m_sideNormals[next3+2] += triangleNormal[2]; } /* * Finish creation of the normal vectors */ for (int32_t i = 1; i <= m_numberOfSides; i++) { const int32_t i3 = i * 3; CaretAssertVectorIndex(m_sideNormals, i3+2); m_sideNormals[i3] /= 2.0; // vertices are shared by two triangles m_sideNormals[i3+1] /= 2.0; m_sideNormals[i3+2] /= 2.0; MathFunctions::normalizeVector(&m_sideNormals[i3]); } /* * Generate vector list for triangles */ m_sidesTriangleFan.push_back(apexIndex); for (int32_t i = m_numberOfSides; i > 0; i--) { m_sidesTriangleFan.push_back(i); } m_sidesTriangleFan.push_back(m_numberOfSides); // closes m_capTriangleFan.push_back(capCenterIndex); for (int32_t i = 1; i <= m_numberOfSides; i++) { m_capTriangleFan.push_back(i); } m_capTriangleFan.push_back(1); if (debugFlag) { CaretAssert(m_coordinates.size() == m_sideNormals.size()); CaretAssert(m_coordinates.size() == m_capNormals.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; CaretAssertVectorIndex(m_coordinates, i3+2); std::cout << "points[" << i << "]=(" << AString::fromNumbers(&m_coordinates[i3], 3, ",") << ")" << std::endl; CaretAssertVectorIndex(m_sideNormals, i3+2); std::cout << "side normal[" << i << "]=(" << AString::fromNumbers(&m_sideNormals[i3], 3, ",") << ")" << std::endl; CaretAssertVectorIndex(m_capNormals, i3+2); std::cout << "cap normal[" << i << "]=(" << AString::fromNumbers(&m_capNormals[i3], 3, ",") << ")" << std::endl; } std::cout << "Sides: "; printTriangleFan(m_sidesTriangleFan); std::cout << std::endl; std::cout << "Cap: "; printTriangleFan(m_capTriangleFan); std::cout << std::endl; } // /* // * End Cap // */ // glBegin(GL_POLYGON); // glNormal3f(0.0, 0.0, 1.0); // for (int32_t i = 0; i < m_numberOfSides; i++) { // const int32_t i3 = i * 3; // glVertex3fv(&m_coordinates[i3]); // } // glEnd(); /* * 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. */ BrainOpenGLShapeCone::~BrainOpenGLShapeCone() { } void BrainOpenGLShapeCone::setupOpenGLForShape(const BrainOpenGL::DrawMode drawMode) { m_displayList = 0; m_coordinatesBufferID = 0; m_coordinatesRgbaByteBufferID = 0; m_sidesNormalBufferID = 0; m_sidesTriangleFanBufferID = 0; m_capNormalBufferID = 0; m_capTriangleFanBufferID = 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_sidesNormalBufferID = createBufferID(); glBindBuffer(GL_ARRAY_BUFFER, m_sidesNormalBufferID); glBufferData(GL_ARRAY_BUFFER, m_sideNormals.size() * sizeof(GLfloat), &m_sideNormals[0], GL_STATIC_DRAW); /* * Put sides triangle fan for sides into its buffer. */ m_sidesTriangleFanBufferID = createBufferID(); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_sidesTriangleFanBufferID); glBufferData(GL_ELEMENT_ARRAY_BUFFER, m_sidesTriangleFan.size() * sizeof(GLuint), &m_sidesTriangleFan[0], GL_STATIC_DRAW); /* * Put cap normals into its buffer. */ m_capNormalBufferID = createBufferID(); glBindBuffer(GL_ARRAY_BUFFER, m_capNormalBufferID); glBufferData(GL_ARRAY_BUFFER, m_capNormals.size() * sizeof(GLfloat), &m_capNormals[0], GL_STATIC_DRAW); /* * Put cap triangle fan for cap into its buffer. */ m_capTriangleFanBufferID = createBufferID(); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_capTriangleFanBufferID); glBufferData(GL_ELEMENT_ARRAY_BUFFER, m_capTriangleFan.size() * sizeof(GLuint), &m_capTriangleFan[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 BrainOpenGLShapeCone::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 BrainOpenGLShapeCone::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: { if (m_isApplyColoring) { glColor4ubv(rgba); } const int32_t numSideVertices = static_cast(m_sidesTriangleFan.size()); glBegin(GL_TRIANGLE_FAN); for (int32_t j = 0; j < numSideVertices; j++) { const int32_t vertexIndex = m_sidesTriangleFan[j] * 3; CaretAssertVectorIndex(m_sideNormals, vertexIndex+2); CaretAssertVectorIndex(m_coordinates, vertexIndex+2); glNormal3fv(&m_sideNormals[vertexIndex]); glVertex3fv(&m_coordinates[vertexIndex]); } glEnd(); const int32_t numCapVertices = static_cast(m_capTriangleFan.size()); glBegin(GL_TRIANGLE_FAN); for (int32_t j = 0; j < numCapVertices; j++) { const int32_t vertexIndex = m_capTriangleFan[j] * 3; CaretAssertVectorIndex(m_capNormals, vertexIndex+2); CaretAssertVectorIndex(m_coordinates, vertexIndex+2); glNormal3fv(&m_capNormals[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. */ CaretAssert(glIsBuffer(m_coordinatesBufferID) == GL_TRUE); 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; 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. */ CaretAssert(glIsBuffer(m_sidesNormalBufferID) == GL_TRUE); glBindBuffer(GL_ARRAY_BUFFER, m_sidesNormalBufferID); glNormalPointer(GL_FLOAT, 0, (GLvoid*)0); /* * Draw the side triangle fans. */ CaretAssert(glIsBuffer(m_sidesTriangleFanBufferID) == GL_TRUE); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_sidesTriangleFanBufferID); glDrawElements(GL_TRIANGLE_FAN, m_sidesTriangleFan.size(), GL_UNSIGNED_INT, (GLvoid*)0); /* * Set the cap normal vectors for drawing. */ glBindBuffer(GL_ARRAY_BUFFER, m_capNormalBufferID); glNormalPointer(GL_FLOAT, 0, (GLvoid*)0); /* * Draw the cap triangle fans. */ glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_capTriangleFanBufferID); glDrawElements(GL_TRIANGLE_FAN, m_capTriangleFan.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_OPENGL_INFO_SUPPORTS_VERTEX_BUFFERS break; } } connectome-workbench-1.2.3+git41-gc4c6c90/src/Brain/BrainOpenGLShapeCone.h000066400000000000000000000051421300200146000256400ustar00rootroot00000000000000#ifndef __BRAIN_OPEN_GL_SHAPE_CONE_H_ #define __BRAIN_OPEN_GL_SHAPE_CONE_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 BrainOpenGLShapeCone : public BrainOpenGLShape { public: BrainOpenGLShapeCone(const int32_t numberOfSides); virtual ~BrainOpenGLShapeCone(); private: BrainOpenGLShapeCone(const BrainOpenGLShapeCone&); BrainOpenGLShapeCone& operator=(const BrainOpenGLShapeCone&); 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; GLuint m_coordinatesBufferID; GLuint m_coordinatesRgbaByteBufferID; GLuint m_sidesNormalBufferID; GLuint m_sidesTriangleFanBufferID; GLuint m_capNormalBufferID; GLuint m_capTriangleFanBufferID; GLuint m_displayList; std::vector m_coordinates; std::vector m_rgbaByte; std::vector m_sideNormals; std::vector m_sidesTriangleFan; std::vector m_capTriangleFan; std::vector m_capNormals; bool m_isApplyColoring; }; #ifdef __BRAIN_OPEN_GL_SHAPE_CONE_DECLARE__ // #endif // __BRAIN_OPEN_GL_SHAPE_CONE_DECLARE__ } // namespace #endif //__BRAIN_OPEN_GL_SHAPE_CONE_H_ connectome-workbench-1.2.3+git41-gc4c6c90/src/Brain/BrainOpenGLShapeCube.cxx000066400000000000000000000405021300200146000262040ustar00rootroot00000000000000 /*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_G_L_SHAPE_CUBE_DECLARE__ #include "BrainOpenGLShapeCube.h" #undef __BRAIN_OPEN_G_L_SHAPE_CUBE_DECLARE__ #include "AString.h" #include "CaretAssert.h" #include "MathFunctions.h" using namespace caret; /** * \class caret::BrainOpenGLShapeCube * \brief Create a cube shape for use with OpenGL. */ /** * Constructor. * * @param cubeSize * Size of cube from one face to the opposite face. * @param cubeType * Type of cube. */ BrainOpenGLShapeCube::BrainOpenGLShapeCube(const float cubeSize, const CUBE_TYPE cubeType) : BrainOpenGLShape(), m_cubeSize(cubeSize), m_cubeType(cubeType) { m_isApplyColoring = true; GLfloat cubeVertices[8][3] = { { -0.5, -0.5, 0.5 }, // 0: left, near, top { 0.5, -0.5, 0.5 }, // 1: right, near, top { -0.5, 0.5, 0.5 }, // 2: left, far, top { 0.5, 0.5, 0.5 }, // 3: right, far, top { -0.5, -0.5, -0.5 }, // 4: left, near, bottom { 0.5, -0.5, -0.5 }, // 5: right, near, bottom { -0.5, 0.5, -0.5 }, // 6: left, far, bottom { 0.5, 0.5, -0.5 } // 7: right, far, bottom }; const GLint lnt = 0; const GLint rnt = 1; const GLint lft = 2; const GLint rft = 3; const GLint lnb = 4; const GLint rnb = 5; const GLint lfb = 6; const GLint rfb = 7; const GLfloat cubeNormals[6][3] = { { -1.0, 0.0, 0.0 }, { 1.0, 0.0, 0.0 }, { 0.0, -1.0, 0.0 }, { 0.0, 1.0, 0.0 }, { 0.0, 0.0, 1.0 }, { 0.0, 0.0, -1.0 } }; const GLint normalIndexLeftFace = 0; const GLint normalIndexRightFace = 1; const GLint normalIndexNearFace = 2; const GLint normalIndexFarFace = 3; const GLint normalIndexTopFace = 4; const GLint normalIndexBottomFace = 5; /* * Near Face */ addFace(cubeVertices, lnt, lnb, rnb, rnt, cubeNormals[normalIndexNearFace]); /* * Far Face */ addFace(cubeVertices, rfb, lfb, lft, rft, cubeNormals[normalIndexFarFace]); /* * Left Face */ addFace(cubeVertices, lfb, lnb, lnt, lft, cubeNormals[normalIndexLeftFace]); /* * Right Face */ addFace(cubeVertices, rnb, rfb, rft, rnt, cubeNormals[normalIndexRightFace]); /* * Bottom Face */ addFace(cubeVertices, rnb, lnb, lfb, rfb, cubeNormals[normalIndexBottomFace]); /* * Top Face */ addFace(cubeVertices, lnt, rnt, rft, lft, cubeNormals[normalIndexTopFace]); // /* // * Near Face // */ // addFace(cubeVertices, // lnt, lnb, rnb, rnt, // cubeNormals[normalIndexNearFace]); // // /* // * Far Face // */ // addFace(cubeVertices, // rfb, lfb, lft, rft, // cubeNormals[normalIndexFarFace]); // // /* // * Left Face // */ // addFace(cubeVertices, // lfb, lnb, lnt, lft, // cubeNormals[normalIndexLeftFace]); // // /* // * Right Face // */ // addFace(cubeVertices, // rnb, rfb, rft, rnt, // cubeNormals[normalIndexRightFace]); // // /* // * Bottom Face // */ // addFace(cubeVertices, // rnb, lnb, lfb, rfb, // cubeNormals[normalIndexBottomFace]); // // /* // * Top Face // */ // addFace(cubeVertices, // lnt, rnt, rft, lft, // cubeNormals[normalIndexTopFace]); CaretAssert(m_coordinates.size() == m_normals.size()); /* * 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; } switch (m_cubeType) { case NORMAL: break; case ROUNDED: { const unsigned int numPoints = m_coordinates.size() / 3; for (unsigned int i = 0; i < numPoints; i++) { const unsigned int i3 = i * 3; m_normals[i3] = m_coordinates[i3]; m_normals[i3+1] = m_coordinates[i3+1]; m_normals[i3+2] = m_coordinates[i3+2]; MathFunctions::normalizeVector(&m_normals[i3]); } } break; } } /** * Destructor. */ BrainOpenGLShapeCube::~BrainOpenGLShapeCube() { } /** * Setup the shape for drawing. * * @param drawMode * The drawing mode. */ void BrainOpenGLShapeCube::setupOpenGLForShape(const BrainOpenGL::DrawMode drawMode) { m_displayList = 0; m_vertexBufferID = 0; m_coordinatesRgbaByteBufferID = 0; m_normalBufferID = 0; m_trianglesBufferID = 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 vertices (coordinates) into its buffer. */ m_vertexBufferID = createBufferID(); glBindBuffer(GL_ARRAY_BUFFER, m_vertexBufferID); 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 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 triangle strips into its buffer. */ m_trianglesBufferID = createBufferID(); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_trianglesBufferID); glBufferData(GL_ELEMENT_ARRAY_BUFFER, m_triangles.size() * sizeof(GLuint), &m_triangles[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; } } /** * Add a face (two triangles) to vertices and normals for drawing. * Vertex indices must be in counter-clockwise order. * * @param vertices * Array of vertices. * @param v1 * First face coordinate index. * @param v2 * Second face coordinate index. * @param v3 * Third face coordinate index. * @param v4 * Fourth face coordinate index. * @param normalVector * Normal vector for the face. */ void BrainOpenGLShapeCube::addFace(const GLfloat coordinates[][3], const GLint v1, const GLint v2, const GLint v3, const GLint v4, const GLfloat normalVector[3]) { m_coordinates.push_back(coordinates[v1][0]); m_coordinates.push_back(coordinates[v1][1]); m_coordinates.push_back(coordinates[v1][2]); m_normals.push_back(normalVector[0]); m_normals.push_back(normalVector[1]); m_normals.push_back(normalVector[2]); m_triangles.push_back(m_coordinates.size() / 3 - 1); m_coordinates.push_back(coordinates[v2][0]); m_coordinates.push_back(coordinates[v2][1]); m_coordinates.push_back(coordinates[v2][2]); m_normals.push_back(normalVector[0]); m_normals.push_back(normalVector[1]); m_normals.push_back(normalVector[2]); m_triangles.push_back(m_coordinates.size() / 3 - 1); m_coordinates.push_back(coordinates[v3][0]); m_coordinates.push_back(coordinates[v3][1]); m_coordinates.push_back(coordinates[v3][2]); m_normals.push_back(normalVector[0]); m_normals.push_back(normalVector[1]); m_normals.push_back(normalVector[2]); m_triangles.push_back(m_coordinates.size() / 3 - 1); m_coordinates.push_back(coordinates[v3][0]); m_coordinates.push_back(coordinates[v3][1]); m_coordinates.push_back(coordinates[v3][2]); m_normals.push_back(normalVector[0]); m_normals.push_back(normalVector[1]); m_normals.push_back(normalVector[2]); m_triangles.push_back(m_coordinates.size() / 3 - 1); m_coordinates.push_back(coordinates[v4][0]); m_coordinates.push_back(coordinates[v4][1]); m_coordinates.push_back(coordinates[v4][2]); m_normals.push_back(normalVector[0]); m_normals.push_back(normalVector[1]); m_normals.push_back(normalVector[2]); m_triangles.push_back(m_coordinates.size() / 3 - 1); m_coordinates.push_back(coordinates[v1][0]); m_coordinates.push_back(coordinates[v1][1]); m_coordinates.push_back(coordinates[v1][2]); m_normals.push_back(normalVector[0]); m_normals.push_back(normalVector[1]); m_normals.push_back(normalVector[2]); m_triangles.push_back(m_coordinates.size() / 3 - 1); } /** * Draw the shape. * * @param drawMode * How to draw the shape. * @param rgba * RGBA coloring ranging 0.0 to 1.0 */ void BrainOpenGLShapeCube::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 BrainOpenGLShapeCube::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: { if (m_isApplyColoring) { glColor4ubv(rgba); } const int32_t numVertices = static_cast(m_coordinates.size() / 3); glBegin(GL_TRIANGLES); for (int32_t j = 0; j < numVertices; j++) { const int32_t vertexIndex = 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 glEnableClientState(GL_VERTEX_ARRAY); glEnableClientState(GL_NORMAL_ARRAY); glEnableClientState(GL_COLOR_ARRAY); /* * Set the vertices for drawing. */ glBindBuffer(GL_ARRAY_BUFFER, m_vertexBufferID); 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; 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 normal vectors for drawing. */ glBindBuffer(GL_ARRAY_BUFFER, m_normalBufferID); glNormalPointer(GL_FLOAT, 0, (GLvoid*)0); /* * Draw the triangles. */ CaretAssert(m_triangles.size() == (m_coordinates.size() / 3)); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_trianglesBufferID); glDrawElements(GL_TRIANGLES, m_triangles.size(), GL_UNSIGNED_INT, (GLvoid*)0); /* * Deselect active buffer. */ glBindBuffer(GL_ARRAY_BUFFER, 0); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); glDisableClientState(GL_VERTEX_ARRAY); glDisableClientState(GL_NORMAL_ARRAY); glDisableClientState(GL_COLOR_ARRAY); #endif // BRAIN_OPENGL_INFO_SUPPORTS_VERTEX_BUFFERS break; } } connectome-workbench-1.2.3+git41-gc4c6c90/src/Brain/BrainOpenGLShapeCube.h000066400000000000000000000057361300200146000256430ustar00rootroot00000000000000#ifndef __BRAIN_OPEN_G_L_SHAPE_CUBE__H_ #define __BRAIN_OPEN_G_L_SHAPE_CUBE__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 BrainOpenGLShapeCube : public BrainOpenGLShape { public: /** * Type of cube */ enum CUBE_TYPE { /** Normal vectors orthogonal to faces */ NORMAL, /** Normal vectors run though vertices which smooths corners */ ROUNDED }; BrainOpenGLShapeCube(const float cubeSize, const CUBE_TYPE cubeType); virtual ~BrainOpenGLShapeCube(); private: BrainOpenGLShapeCube(const BrainOpenGLShapeCube&); BrainOpenGLShapeCube& operator=(const BrainOpenGLShapeCube&); 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: void addFace(const GLfloat coordinates[][3], const GLint v1, const GLint v2, const GLint v3, const GLint v4, const GLfloat normalVector[3]); // ADD_NEW_MEMBERS_HERE const float m_cubeSize; const CUBE_TYPE m_cubeType; GLuint m_vertexBufferID; GLuint m_coordinatesRgbaByteBufferID; GLuint m_normalBufferID; GLuint m_trianglesBufferID; GLuint m_displayList; std::vector m_coordinates; std::vector m_rgbaByte; std::vector m_normals; std::vector m_triangles; bool m_isApplyColoring; }; #ifdef __BRAIN_OPEN_G_L_SHAPE_CUBE_DECLARE__ // #endif // __BRAIN_OPEN_G_L_SHAPE_CUBE_DECLARE__ } // namespace #endif //__BRAIN_OPEN_G_L_SHAPE_CUBE__H_ connectome-workbench-1.2.3+git41-gc4c6c90/src/Brain/BrainOpenGLShapeCylinder.cxx000066400000000000000000000473441300200146000271120ustar00rootroot00000000000000 /*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_G_L_SHAPE_CYLINDER_DECLARE__ #include "BrainOpenGLShapeCylinder.h" #undef __BRAIN_OPEN_G_L_SHAPE_CYLINDER_DECLARE__ #include "CaretAssert.h" #include "MathFunctions.h" using namespace caret; /** * \class caret::BrainOpenGLShapeCylinder * \brief Creates a cylinder shape for use with OpenGL. * \ingroup Brain */ /** * Constructor. */ BrainOpenGLShapeCylinder::BrainOpenGLShapeCylinder(const int32_t numberOfSides) : BrainOpenGLShape(), m_numberOfSides(numberOfSides) { m_isApplyColoring = true; // bool debugFlag = false; /* * Setup step size based upon number of points around circle */ const float step = (2.0 * M_PI) / m_numberOfSides; /* * Z-coordinates at bottom and top of cylinder */ const float zTop = 1.0; const float zBottom = 0.0; std::vector topTriangleStripVertices; std::vector bottomTriangleStripVertices; std::vector sideTriangleStripVertices; /* * Counts vertices */ GLuint vertexCounter = 0; // const float topCenterNormalX = 0.0; // const float topCenterNormalY = 0.0; // const float topCenterNormalZ = 1.0; // // const float topCenterX = 0.0; // const float topCenterY = 0.0; // const float topCenterZ = 1.0; // // const float bottomCenterNormalX = 0.0; // const float bottomCenterNormalY = 0.0; // const float bottomCenterNormalZ = -1.0; // // const float bottomCenterX = 0.0; // const float bottomCenterY = 0.0; // const float bottomCenterZ = -1.0; // /* // * Center of top // */ // m_coordinates.push_back(0.0); // m_coordinates.push_back(0.0); // m_coordinates.push_back(1.0); // m_normals.push_back(topCenterNormalX); // m_normals.push_back(topCenterNormalY); // m_normals.push_back(topCenterNormalZ); // m_normals.push_back(vertexCounter); // vertexCounter++; // // /* // * Center of bottom // */ // m_coordinates.push_back(0.0); // m_coordinates.push_back(0.0); // m_coordinates.push_back(-1.0); // m_normals.push_back(bottomCenterNormalX); // m_normals.push_back(bottomCenterNormalY); // m_normals.push_back(bottomCenterNormalZ); // m_normals.push_back(vertexCounter); // vertexCounter++; const GLuint firstSideVertex = vertexCounter; /* * Generate points around cylinder */ const float radius = 0.5; for (int32_t i = 0; i < m_numberOfSides; i++) { const float t = step * i; const float x = radius * std::cos(t); const float y = radius * std::sin(t); /* * Top of slice */ m_coordinates.push_back(x); m_coordinates.push_back(y); m_coordinates.push_back(zTop); m_normals.push_back(x); m_normals.push_back(y); m_normals.push_back(0.0); sideTriangleStripVertices.push_back(vertexCounter); vertexCounter++; // m_normals.push_back(topCenterNormalX); // m_normals.push_back(topCenterNormalY); // m_normals.push_back(topCenterNormalZ); // m_topTriangleFan.push_back(vertexCounter); m_coordinates.push_back(x); m_coordinates.push_back(y); m_coordinates.push_back(zBottom); m_normals.push_back(x); m_normals.push_back(y); m_normals.push_back(0.0); sideTriangleStripVertices.push_back(vertexCounter); vertexCounter++; // m_bottomNormals.push_back(bottomCenterNormalX); // m_bottomNormals.push_back(bottomCenterNormalY); // m_bottomNormals.push_back(bottomCenterNormalZ); // m_bottomTriangleFan.push_back(vertexCounter); // vertexCounter++; } /* * Finish cylinder by specifying first two coordinates again */ const GLuint firstVertexIndex = firstSideVertex * 3; m_coordinates.push_back(m_coordinates[firstVertexIndex]); m_coordinates.push_back(m_coordinates[firstVertexIndex+1]); m_coordinates.push_back(m_coordinates[firstVertexIndex+2]); m_normals.push_back(m_normals[firstVertexIndex]); m_normals.push_back(m_normals[firstVertexIndex+1]); m_normals.push_back(m_normals[firstVertexIndex+2]); sideTriangleStripVertices.push_back(vertexCounter); vertexCounter++; m_coordinates.push_back(m_coordinates[firstVertexIndex+3]); m_coordinates.push_back(m_coordinates[firstVertexIndex+4]); m_coordinates.push_back(m_coordinates[firstVertexIndex+5]); m_normals.push_back(m_normals[firstVertexIndex+3]); m_normals.push_back(m_normals[firstVertexIndex+4]); m_normals.push_back(m_normals[firstVertexIndex+5]); sideTriangleStripVertices.push_back(vertexCounter); vertexCounter++; CaretAssert((vertexCounter * 3) == static_cast(m_coordinates.size())); CaretAssert((vertexCounter * 3) == static_cast(m_normals.size())); m_triangleStrip = sideTriangleStripVertices; // /* // * Finish top and bottom // * Note bottom vertices need to be reverse so that // * that the vertices are counter clockwise when pointing down. // */ // m_topNormals.push_back(m_topNormals[0]); // m_topNormals.push_back(m_topNormals[1]); // m_topNormals.push_back(m_topNormals[2]); // m_topTriangleFan.push_back(firstSideVertex); // // m_bottomNormals.push_back(m_bottomNormals[0]); // m_bottomNormals.push_back(m_bottomNormals[1]); // m_bottomNormals.push_back(m_bottomNormals[2]); // m_bottomTriangleFan.push_back(firstSideVertex + 1); // std::reverse(m_bottomTriangleFan.begin(), // m_bottomTriangleFan.end()); // // CaretAssert(m_topNormals.size() == (m_topTriangleFan.size() * 3)); // CaretAssert(m_sidesNormals.size() == ((m_sidesTriangleStrip.size() - 2) * 3)); // CaretAssert(m_topNormals.size() == (m_topTriangleFan.size() * 3)); /* * Create storage for colors */ m_rgbaByte.resize(vertexCounter * 4, 0); m_rgbaFloat.resize(vertexCounter * 4, 0.0); for (GLuint i = 0; i < vertexCounter; i++) { const int32_t i4 = i * 4; m_rgbaFloat[i4] = 0.0; m_rgbaFloat[i4+1] = 0.0; m_rgbaFloat[i4+2] = 0.0; m_rgbaFloat[i4+3] = 1.0; m_rgbaByte[i4] = 0; m_rgbaByte[i4+1] = 0; m_rgbaByte[i4+2] = 0; m_rgbaByte[i4+3] = 255; } } /** * Destructor. */ BrainOpenGLShapeCylinder::~BrainOpenGLShapeCylinder() { } void BrainOpenGLShapeCylinder::setupOpenGLForShape(const BrainOpenGL::DrawMode drawMode) { m_displayList = 0; m_coordinatesBufferID = 0; m_coordinatesRgbaByteBufferID = 0; m_coordinatesRgbaFloatBufferID = 0; m_normalsBufferID = 0; m_triangleStripBufferID = 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); 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 FLOAT colors into its buffer */ m_coordinatesRgbaFloatBufferID = createBufferID(); glBindBuffer(GL_ARRAY_BUFFER, m_coordinatesRgbaFloatBufferID); glBufferData(GL_ARRAY_BUFFER, m_rgbaFloat.size() * sizeof(GLfloat), &m_rgbaFloat[0], GL_DYNAMIC_DRAW); /* * Put side normals into its buffer. */ m_normalsBufferID = createBufferID(); glBindBuffer(GL_ARRAY_BUFFER, m_normalsBufferID); glBufferData(GL_ARRAY_BUFFER, m_normals.size() * sizeof(GLfloat), &m_normals[0], GL_STATIC_DRAW); /* * Put sides triangle strip for sides into its buffer. */ m_triangleStripBufferID = createBufferID(); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_triangleStripBufferID); glBufferData(GL_ELEMENT_ARRAY_BUFFER, m_triangleStrip.size() * sizeof(GLuint), &m_triangleStrip[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 BrainOpenGLShapeCylinder::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. */ /** * Draw the cone. * @param drawMode * How to draw the shape. */ void BrainOpenGLShapeCylinder::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: { if (m_isApplyColoring) { glColor4ubv(rgba); } // /* // * Draw the top // */ // const int32_t numTopVertices = static_cast (m_topTriangleFan.size()); // glBegin(GL_TRIANGLE_FAN); // for (int32_t i = 0; i < numTopVertices; i++) { // CaretAssertVectorIndex(m_topTriangleFan, i); // const int32_t fanIndex = m_topTriangleFan[i]; // const int32_t coordinateOffset = fanIndex * 3; // const int32_t colorOffset = fanIndex * 4; // const int32_t normalOffset = i * 3; // // CaretAssertVectorIndex(m_coordinates, coordinateOffset + 2); // CaretAssertVectorIndex(m_topNormals, normalOffset + 2); // CaretAssertVectorIndex(m_coordinatesRGBA, colorOffset + 3); // // glColor4fv(&m_coordinatesRGBA[colorOffset]); // glNormal3fv(&m_topNormals[normalOffset]); // glVertex3fv(&m_coordinates[coordinateOffset]); // } // glEnd(); // // /* // * Draw the bottom // */ // const int32_t numBottomVertices = static_cast (m_bottomTriangleFan.size()); // glBegin(GL_TRIANGLE_FAN); // for (int32_t i = 0; i < numBottomVertices; i++) { // CaretAssertVectorIndex(m_bottomTriangleFan, i); // const int32_t fanIndex = m_bottomTriangleFan[i]; // const int32_t coordinateOffset = fanIndex * 3; // const int32_t colorOffset = fanIndex * 4; // const int32_t normalOffset = i * 3; // // CaretAssertVectorIndex(m_coordinates, coordinateOffset + 2); // CaretAssertVectorIndex(m_bottomNormals, normalOffset + 2); // CaretAssertVectorIndex(m_coordinatesRGBA, colorOffset + 3); // // glColor4fv(&m_coordinatesRGBA[colorOffset]); // glNormal3fv(&m_bottomNormals[normalOffset]); // glVertex3fv(&m_coordinates[coordinateOffset]); // } // glEnd(); /* * Draw the sides */ const int32_t numSideVertices = static_cast(m_triangleStrip.size()); glBegin(GL_TRIANGLE_STRIP); for (int32_t i = 0; i < numSideVertices; i++) { CaretAssertVectorIndex(m_triangleStrip, i); const int32_t stripIndex = m_triangleStrip[i]; const int32_t coordinateOffset = stripIndex * 3; //const int32_t colorOffset = stripIndex * 4; const int32_t normalOffset = stripIndex * 3; CaretAssertVectorIndex(m_coordinates, coordinateOffset + 2); CaretAssertVectorIndex(m_normals, normalOffset + 2); //CaretAssertVectorIndex(m_rgba, colorOffset + 3); //glColor4ubv(&m_rgba[colorOffset]); glNormal3fv(&m_normals[normalOffset]); glVertex3fv(&m_coordinates[coordinateOffset]); } 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; m_rgbaByte[ir4] = rgba[0]; m_rgbaByte[ir4+1] = rgba[1]; m_rgbaByte[ir4+2] = rgba[2]; m_rgbaByte[ir4+3] = rgba[3]; } 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 FLOAT color components for drawing // */ // glBindBuffer(GL_ARRAY_BUFFER, // m_coordinatesRgbaFloatBufferID); // glColorPointer(4, // GL_FLOAT, // 0, // (GLvoid*)0); /* * Set the side normal vectors for drawing. */ glBindBuffer(GL_ARRAY_BUFFER, m_normalsBufferID); glNormalPointer(GL_FLOAT, 0, (GLvoid*)0); /* * Draw the side triangle strip. */ glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_triangleStripBufferID); glDrawElements(GL_TRIANGLE_STRIP, m_triangleStrip.size(), GL_UNSIGNED_INT, (GLvoid*)0); /* * Draw the bottom cap triangle fans. */ glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_triangleStripBufferID); glDrawElements(GL_TRIANGLE_STRIP, m_triangleStrip.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_OPENGL_INFO_SUPPORTS_VERTEX_BUFFERS break; } } connectome-workbench-1.2.3+git41-gc4c6c90/src/Brain/BrainOpenGLShapeCylinder.h000066400000000000000000000051111300200146000265210ustar00rootroot00000000000000#ifndef __BRAIN_OPEN_G_L_SHAPE_CYLINDER_H__ #define __BRAIN_OPEN_G_L_SHAPE_CYLINDER_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 "BrainOpenGLShape.h" namespace caret { class BrainOpenGLShapeCylinder : public BrainOpenGLShape { public: BrainOpenGLShapeCylinder(const int32_t numberOfSides); virtual ~BrainOpenGLShapeCylinder(); private: BrainOpenGLShapeCylinder(const BrainOpenGLShapeCylinder&); BrainOpenGLShapeCylinder& operator=(const BrainOpenGLShapeCylinder&); 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); public: // ADD_NEW_METHODS_HERE private: // ADD_NEW_MEMBERS_HERE // ADD_NEW_MEMBERS_HERE const int32_t m_numberOfSides; GLuint m_coordinatesBufferID; GLuint m_coordinatesRgbaByteBufferID; GLuint m_coordinatesRgbaFloatBufferID; GLuint m_normalsBufferID; GLuint m_triangleStripBufferID; GLuint m_displayList; std::vector m_coordinates; std::vector m_rgbaByte; std::vector m_rgbaFloat; std::vector m_normals; std::vector m_triangleStrip; bool m_isApplyColoring; }; #ifdef __BRAIN_OPEN_G_L_SHAPE_CYLINDER_DECLARE__ // #endif // __BRAIN_OPEN_G_L_SHAPE_CYLINDER_DECLARE__ } // namespace #endif //__BRAIN_OPEN_G_L_SHAPE_CYLINDER_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Brain/BrainOpenGLShapeRing.cxx000066400000000000000000000313701300200146000262300ustar00rootroot00000000000000 /*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_DECLARE__ #include "BrainOpenGLShapeRing.h" #undef __BRAIN_OPEN_GL_SHAPE_RING_DECLARE__ #include "CaretAssert.h" #include "MathFunctions.h" using namespace caret; /** * \class caret::BrainOpenGLShapeRing * \brief Create a conical shape for use with OpenGL. */ /** * Constructor. * @param numberOfSides * Number of sides in the ring. */ BrainOpenGLShapeRing::BrainOpenGLShapeRing(const int32_t numberOfSides, const float innerRadius, const float outerRadius) : BrainOpenGLShape(), m_numberOfSides(numberOfSides), m_innerRadius(innerRadius), m_outerRadius(outerRadius) { 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 xin = m_innerRadius * std::cos(t); const float yin = m_innerRadius * std::sin(t); const float xout = m_outerRadius * std::cos(t); const float yout = m_outerRadius * std::sin(t); m_triangleStrip.push_back(m_coordinates.size() / 3); m_coordinates.push_back(xin); m_coordinates.push_back(yin); m_coordinates.push_back(z); m_normals.push_back(0.0); m_normals.push_back(0.0); m_normals.push_back(1.0); m_triangleStrip.push_back(m_coordinates.size() / 3); m_coordinates.push_back(xout); m_coordinates.push_back(yout); m_coordinates.push_back(z); m_normals.push_back(0.0); m_normals.push_back(0.0); m_normals.push_back(1.0); } if (m_numberOfSides > 0) { m_triangleStrip.push_back(0); m_triangleStrip.push_back(1); } 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; } // /* // * End Cap // */ // glBegin(GL_POLYGON); // glNormal3f(0.0, 0.0, 1.0); // for (int32_t i = 0; i < m_numberOfSides; i++) { // const int32_t i3 = i * 3; // glVertex3fv(&m_coordinates[i3]); // } // glEnd(); /* * 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. */ BrainOpenGLShapeRing::~BrainOpenGLShapeRing() { } void BrainOpenGLShapeRing::setupOpenGLForShape(const BrainOpenGL::DrawMode drawMode) { m_displayList = 0; m_coordinatesBufferID = 0; m_coordinatesRgbaByteBufferID = 0; m_normalBufferID = 0; m_triangleStripBufferID = 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 sides triangle strip into its buffer. */ m_triangleStripBufferID = createBufferID(); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_triangleStripBufferID); glBufferData(GL_ELEMENT_ARRAY_BUFFER, m_triangleStrip.size() * sizeof(GLuint), &m_triangleStrip[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 BrainOpenGLShapeRing::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 BrainOpenGLShapeRing::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: { if (m_isApplyColoring) { glColor4ubv(rgba); } const int32_t numVertices = static_cast(m_triangleStrip.size()); glBegin(GL_TRIANGLE_STRIP); for (int32_t j = 0; j < numVertices; j++) { const int32_t vertexIndex = m_triangleStrip[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); /* * Draw the side triangle fans. */ glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_triangleStripBufferID); glDrawElements(GL_TRIANGLE_STRIP, m_triangleStrip.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_OPENGL_INFO_SUPPORTS_VERTEX_BUFFERS break; } } connectome-workbench-1.2.3+git41-gc4c6c90/src/Brain/BrainOpenGLShapeRing.h000066400000000000000000000051461300200146000256570ustar00rootroot00000000000000#ifndef __BRAIN_OPEN_GL_SHAPE_RING_H_ #define __BRAIN_OPEN_GL_SHAPE_RING_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 BrainOpenGLShapeRing : public BrainOpenGLShape { public: BrainOpenGLShapeRing(const int32_t numberOfSides, const float innerRadius, const float outerRadius); virtual ~BrainOpenGLShapeRing(); private: BrainOpenGLShapeRing(const BrainOpenGLShapeRing&); BrainOpenGLShapeRing& operator=(const BrainOpenGLShapeRing&); 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_innerRadius; const float m_outerRadius; GLuint m_coordinatesBufferID; GLuint m_coordinatesRgbaByteBufferID; GLuint m_normalBufferID; GLuint m_triangleStripBufferID; GLuint m_displayList; std::vector m_coordinates; std::vector m_rgbaByte; std::vector m_normals; std::vector m_triangleStrip; bool m_isApplyColoring; }; #ifdef __BRAIN_OPEN_GL_SHAPE_RING_DECLARE__ // #endif // __BRAIN_OPEN_GL_SHAPE_RING_DECLARE__ } // namespace #endif //__BRAIN_OPEN_GL_SHAPE_RING_H_ connectome-workbench-1.2.3+git41-gc4c6c90/src/Brain/BrainOpenGLShapeRingOutline.cxx000066400000000000000000000274551300200146000276010ustar00rootroot00000000000000 /*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; } } connectome-workbench-1.2.3+git41-gc4c6c90/src/Brain/BrainOpenGLShapeRingOutline.h000066400000000000000000000053461300200146000272210ustar00rootroot00000000000000#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_ connectome-workbench-1.2.3+git41-gc4c6c90/src/Brain/BrainOpenGLShapeSphere.cxx000066400000000000000000000361661300200146000265670ustar00rootroot00000000000000 /*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_G_L_SHAPE_SPHERE_DECLARE__ #include "BrainOpenGLShapeSphere.h" #undef __BRAIN_OPEN_G_L_SHAPE_SPHERE_DECLARE__ #include "AString.h" #include "CaretAssert.h" #include "MathFunctions.h" using namespace caret; /** * \class caret::BrainOpenGLShapeSphere * \brief Create a spherical shape for use with OpenGL. */ /** * Constructor. */ BrainOpenGLShapeSphere::BrainOpenGLShapeSphere(const int32_t numberOfLatitudeAndLongitude, const float radius) : BrainOpenGLShape(), m_numberOfLatitudeAndLongitude(numberOfLatitudeAndLongitude), m_radius(radius) { m_isApplyColoring = true; bool debugFlag = false; const int numLat = m_numberOfLatitudeAndLongitude; const int numLon = m_numberOfLatitudeAndLongitude; const float degToRad = M_PI / 180.0; const float dLat = 180.0 / numLat; for (int iLat = 0; iLat < numLat; iLat++) { const float latDeg = -90.0 + (iLat * dLat); const float latRad = latDeg * degToRad; float z1 = m_radius * std::sin(latRad); const float latDeg2 = -90.0 + ((iLat + 1) * dLat); const float latRad2 = latDeg2 * degToRad; float z2 = m_radius * std::sin(latRad2); std::vector strip; if (debugFlag) std::cout << "Strip Start: " << std::endl; const float dLon = 360.0 / numLon; for (int iLon = 0; iLon <= numLon; iLon++) { const float lonDeg = iLon * dLon; const float lonRad = lonDeg * degToRad; float x1 = m_radius * std::cos(latRad) * std::cos(lonRad); float y1 = m_radius * std::cos(latRad) * std::sin(lonRad); if (iLat == 0) { x1 = 0.0; y1 = 0.0; z1 = -m_radius; } float x2 = m_radius * std::cos(latRad2) * std::cos(lonRad); float y2 = m_radius * std::cos(latRad2) * std::sin(lonRad); if (iLat == (numLat - 1)) { x2 = 0.0; y2 = 0.0; z2 = m_radius; } strip.push_back(static_cast(m_coordinates.size() / 3)); const int32_t indx1 = static_cast(m_coordinates.size() / 3); m_coordinates.push_back(x2); m_coordinates.push_back(y2); m_coordinates.push_back(z2); const float length2 = std::sqrt(x2*x2 + y2*y2 + z2*z2); m_normals.push_back(x2 / length2); m_normals.push_back(y2 / length2); m_normals.push_back(z2 / length2); strip.push_back(static_cast(m_coordinates.size() / 3)); const int32_t indx2 = static_cast(m_coordinates.size() / 3); m_coordinates.push_back(x1); m_coordinates.push_back(y1); m_coordinates.push_back(z1); const float length1 = std::sqrt(x1*x1 + y1*y1 + z1*z1); m_normals.push_back(x1 / length1); m_normals.push_back(y1 / length1); m_normals.push_back(z1 / length1); if (debugFlag) { std::cout << " " << iLat << ", " << iLon << std::endl; std::cout << " " << indx1 << ":" << latDeg << ", " << lonDeg << ", " << x1 << ", " << y1 << ", " << z1 << std::endl; std::cout << " " << indx2 << ":" << latDeg2 << ", " << lonDeg << ", " << x2 << ", " << y2 << ", " << z2 << std::endl; } } if (strip.empty() == false) { m_triangleStrips.push_back(strip); if (debugFlag) { const float* c1 = &m_coordinates[strip[0]* 3]; const float* c2 = &m_coordinates[strip[1]* 3]; const float* c3 = &m_coordinates[strip[2]* 3]; float normal[3]; MathFunctions::normalVector(c1, c2, c3, normal); std::cout << "Normal Vector" << qPrintable(AString::fromNumbers(normal, 3, ",")) << std::endl; } } if (debugFlag) std::cout << std::endl; } // if (debugFlag) { // const int32_t numTriangleStrips = m_triangleStrips.size(); // for (int32_t i = 0; i < numTriangleStrips; i++) { // std::cout << "Strip " << i << ":"; // printTriangleStrip(m_triangleStrips[i]); // } // } /* * Concatente into a single strip */ contatenateTriangleStrips(m_triangleStrips, m_singleTriangleStrip); if (debugFlag) { std::cout << "NEW STRIPS" << std::endl; const int32_t numTriangleStrips = m_triangleStrips.size(); for (int32_t i = 0; i < numTriangleStrips; i++) { std::cout << "Strip " << i << ":"; printTriangleStrip(m_triangleStrips[i]); } std::cout << "SINGLE STRIP: "; printTriangleStrip(m_singleTriangleStrip); } /* * 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. */ BrainOpenGLShapeSphere::~BrainOpenGLShapeSphere() { } void BrainOpenGLShapeSphere::setupOpenGLForShape(const BrainOpenGL::DrawMode drawMode) { m_displayList = 0; m_vertexBufferID = 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 vertices (coordinates) into its buffer. */ m_vertexBufferID = createBufferID(); glBindBuffer(GL_ARRAY_BUFFER, m_vertexBufferID); glBufferData(GL_ARRAY_BUFFER, m_coordinates.size() * sizeof(GLfloat), &m_coordinates[0], GL_STATIC_DRAW); /* * For RGBA coloring */ m_vertexRgbaByteBufferID = createBufferID(); glBindBuffer(GL_ARRAY_BUFFER, m_vertexRgbaByteBufferID); glBufferData(GL_ARRAY_BUFFER, m_rgbaByte.size() * sizeof(GLubyte), &m_rgbaByte[0], GL_DYNAMIC_DRAW); /* * Put 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 triangle strips into its buffer. */ m_triangleStripBufferID = createBufferID(); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_triangleStripBufferID); glBufferData(GL_ELEMENT_ARRAY_BUFFER, m_singleTriangleStrip.size() * sizeof(GLuint), &m_singleTriangleStrip[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 BrainOpenGLShapeSphere::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 BrainOpenGLShapeSphere::drawShape(const BrainOpenGL::DrawMode drawMode, const uint8_t rgba[4]) { if (m_isApplyColoring) { glColor4ubv(rgba); } bool useOneTriangleStrip = true; switch (drawMode) { case BrainOpenGL::DRAW_MODE_DISPLAY_LISTS: { if (m_displayList > 0) { glCallList(m_displayList); } } break; case BrainOpenGL::DRAW_MODE_IMMEDIATE: if (useOneTriangleStrip) { const int32_t numVertices = static_cast(m_singleTriangleStrip.size()); glBegin(GL_TRIANGLE_STRIP); for (int32_t j = 0; j < numVertices; j++) { const int32_t vertexIndex = m_singleTriangleStrip[j] * 3; CaretAssertVectorIndex(m_normals, vertexIndex); CaretAssertVectorIndex(m_coordinates, vertexIndex); glNormal3fv(&m_normals[vertexIndex]); glVertex3fv(&m_coordinates[vertexIndex]); } glEnd(); } else { const int32_t numTriangleStrips = m_triangleStrips.size(); for (int32_t i = 0; i < numTriangleStrips; i++) { const std::vector& strip = m_triangleStrips[i]; const int32_t numVertices = static_cast(strip.size()); glBegin(GL_TRIANGLE_STRIP); for (int32_t j = 0; j < numVertices; j++) { const int32_t vertexIndex = strip[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 glEnableClientState(GL_VERTEX_ARRAY); glEnableClientState(GL_NORMAL_ARRAY); glEnableClientState(GL_COLOR_ARRAY); /* * Set the vertices for drawing. */ glBindBuffer(GL_ARRAY_BUFFER, m_vertexBufferID); 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; 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_vertexRgbaByteBufferID) == GL_TRUE); glBindBuffer(GL_ARRAY_BUFFER, m_vertexRgbaByteBufferID); 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 normal vectors for drawing. */ glBindBuffer(GL_ARRAY_BUFFER, m_normalBufferID); glNormalPointer(GL_FLOAT, 0, (GLvoid*)0); /* * Draw the triangle strips. */ glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_triangleStripBufferID); glDrawElements(GL_TRIANGLE_STRIP, m_singleTriangleStrip.size(), GL_UNSIGNED_INT, (GLvoid*)0); /* * Deselect active buffer. */ glBindBuffer(GL_ARRAY_BUFFER, 0); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); glDisableClientState(GL_VERTEX_ARRAY); glDisableClientState(GL_NORMAL_ARRAY); glDisableClientState(GL_COLOR_ARRAY); #endif // BRAIN_OPENGL_INFO_SUPPORTS_VERTEX_BUFFERS break; } } connectome-workbench-1.2.3+git41-gc4c6c90/src/Brain/BrainOpenGLShapeSphere.h000066400000000000000000000051331300200146000262020ustar00rootroot00000000000000#ifndef __BRAIN_OPEN_G_L_SHAPE_SPHERE__H_ #define __BRAIN_OPEN_G_L_SHAPE_SPHERE__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 BrainOpenGLShapeSphere : public BrainOpenGLShape { public: BrainOpenGLShapeSphere(const int32_t numberOfLatitudeAndLongitude, const float radius); virtual ~BrainOpenGLShapeSphere(); private: BrainOpenGLShapeSphere(const BrainOpenGLShapeSphere&); BrainOpenGLShapeSphere& operator=(const BrainOpenGLShapeSphere&); 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_numberOfLatitudeAndLongitude; const float m_radius; GLuint m_vertexBufferID; GLuint m_vertexRgbaByteBufferID; GLuint m_normalBufferID; GLuint m_triangleStripBufferID; GLuint m_displayList; std::vector m_coordinates; std::vector m_normals; std::vector m_rgbaByte; std::vector > m_triangleStrips; std::vector m_singleTriangleStrip; bool m_isApplyColoring; }; #ifdef __BRAIN_OPEN_G_L_SHAPE_SPHERE_DECLARE__ // #endif // __BRAIN_OPEN_G_L_SHAPE_SPHERE_DECLARE__ } // namespace #endif //__BRAIN_OPEN_G_L_SHAPE_SPHERE__H_ connectome-workbench-1.2.3+git41-gc4c6c90/src/Brain/BrainOpenGLTextRenderInterface.cxx000066400000000000000000000112251300200146000302520ustar00rootroot00000000000000 /*LICENSE_START*/ /* * Copyright (C) 2015 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_TEXT_RENDER_INTERFACE_DECLARE__ #include "BrainOpenGLTextRenderInterface.h" #undef __BRAIN_OPEN_G_L_TEXT_RENDER_INTERFACE_DECLARE__ #include "CaretAssert.h" using namespace caret; /** * \class caret::BrainOpenGLTextRenderInterface * \brief Interface for rendering of text to OpenGL. * \ingroup test */ /** * Constructor. */ BrainOpenGLTextRenderInterface::BrainOpenGLTextRenderInterface() : CaretObject() { } /** * Destructor. */ BrainOpenGLTextRenderInterface::~BrainOpenGLTextRenderInterface() { } /** * Get the bounds of text (in pixels) using the given text * attributes. * * See http://ftgl.sourceforge.net/docs/html/metrics.png * * @param annotationText * Text that is to be drawn. * @param viewportX * Viewport X-coordinate. * @param viewportY * Viewport Y-coordinate. * @param viewportZ * Viewport Z-coordinate. * @param viewportHeight * Height of the viewport needed for percentage height text. * @param bottomLeftOut * The bottom left corner of the text bounds. * @param bottomRightOut * The bottom right corner of the text bounds. * @param topRightOut * The top right corner of the text bounds. * @param topLeftOut * The top left corner of the text bounds. */ void BrainOpenGLTextRenderInterface::getBoundsForTextAtViewportCoords(const AnnotationText& annotationText, const float viewportX, const float viewportY, const float viewportZ, const float viewportHeight, float bottomLeftOut[3], float bottomRightOut[3], float topRightOut[3], float topLeftOut[3]) { double bottomLeft[3]; double bottomRight[3]; double topRight[3]; double topLeft[3]; getBoundsForTextAtViewportCoords(annotationText, viewportX, viewportY, viewportZ, viewportHeight, bottomLeft, bottomRight, topRight, topLeft); for (int32_t i = 0; i < 3; i++) { bottomLeftOut[i] = bottomLeft[i]; bottomRightOut[i] = bottomRight[i]; topRightOut[i] = topRight[i]; topLeftOut[i] = topLeft[i]; } } /** * Convert point size to pixels. * One point is 1/72 inch. * * @param pointSize * The point size. * @return * The size in pixels. */ float BrainOpenGLTextRenderInterface::pointSizeToPixels(const float pointSize) { const float inches = pointSize / 72.0; float pixels = inches * s_pixelsPerInch; return pixels; } /** * Convert pixels to point size. * One point is 1/72 inch. * * @param pixels * The pixel size. * @return * The point size. */ float BrainOpenGLTextRenderInterface::pixelsToPointSize(const float pixels) { const float inches = pixels / s_pixelsPerInch; const float pointSize = 72.0 * inches; return pointSize; } /** * @return The pixels per inch (PPI). */ float BrainOpenGLTextRenderInterface::getPixelsPerInch() { return s_pixelsPerInch; } /** * Set the pixels per inch. * * @param pixelsPerInch * The new value for pixels per inch. */ void BrainOpenGLTextRenderInterface::setPixelsPerInch(const float pixelsPerInch) { s_pixelsPerInch = pixelsPerInch; } connectome-workbench-1.2.3+git41-gc4c6c90/src/Brain/BrainOpenGLTextRenderInterface.h000066400000000000000000000233201300200146000276760ustar00rootroot00000000000000#ifndef __BRAIN_OPEN_G_L_TEXT_RENDER_INTERFACE__H_ #define __BRAIN_OPEN_G_L_TEXT_RENDER_INTERFACE__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" namespace caret { class AnnotationText; class BrainOpenGLTextRenderInterface : public CaretObject { protected: BrainOpenGLTextRenderInterface(); public: virtual ~BrainOpenGLTextRenderInterface(); /** * Draw annnotation text at the given viewport coordinates using * the the annotations attributes for the style of text. * * Depth testing is DISABLED when drawing text with this method. * * @param viewportX * Viewport X-coordinate. * @param viewportY * Viewport Y-coordinate. * @param annotationText * Annotation text and attributes. */ virtual void drawTextAtViewportCoords(const double viewportX, const double viewportY, const AnnotationText& annotationText) = 0; /** * Draw annnotation text at the given viewport coordinates using * the the annotations attributes for the style of text. * * Depth testing is ENABLED when drawing text with this method. * * @param viewportX * Viewport X-coordinate. * @param viewportY * Viewport Y-coordinate. * @param viewportZ * Viewport Z-coordinate. * @param annotationText * Annotation text and attributes. */ virtual void drawTextAtViewportCoords(const double viewportX, const double viewportY, const double viewportZ, const AnnotationText& annotationText) = 0; /** * Draw annnotation text at the given model coordinates using * the the annotations attributes for the style of text. * * Depth testing is ENABLED when drawing text with this method. * * @param modelX * Model X-coordinate. * @param modelY * Model Y-coordinate. * @param modelZ * Model Z-coordinate. * @param annotationText * Annotation text and attributes. */ virtual void drawTextAtModelCoords(const double modelX, const double modelY, const double modelZ, const AnnotationText& annotationText) = 0; /** * Draw annnotation text at the given model coordinates using * the the annotations attributes for the style of text. * * Depth testing is ENABLED when drawing text with this method. * * @param modelXYZ * Model XYZ coordinate. * @param annotationText * Annotation text and attributes. */ void drawTextAtModelCoords(const double modelXYZ[3], const AnnotationText& annotationText) { drawTextAtModelCoords(modelXYZ[0], modelXYZ[1], modelXYZ[2], annotationText); } /** * Draw annnotation text at the given model coordinates using * the the annotations attributes for the style of text. * * Depth testing is ENABLED when drawing text with this method. * * @param modelXYZ * Model XYZ coordinate. * @param annotationText * Annotation text and attributes. */ void drawTextAtModelCoords(const float modelXYZ[3], const AnnotationText& annotationText) { drawTextAtModelCoords(modelXYZ[0], modelXYZ[1], modelXYZ[2], annotationText); } /** * Get the estimated width and height of text (in pixels) using the given text * attributes. * * See http://ftgl.sourceforge.net/docs/html/metrics.png * * @param annotationText * Text for width and height estimation. * @param viewportHeight * Height of the viewport needed for percentage height text. * @param widthOut * Estimated width of text. * @param heightOut * Estimated height of text. */ virtual void getTextWidthHeightInPixels(const AnnotationText& annotationText, const double viewportHeight, double& widthOut, double& heightOut) = 0; /** * Get the bounds of text (in pixels) using the given text * attributes. * * See http://ftgl.sourceforge.net/docs/html/metrics.png * * @param annotationText * Text that is to be drawn. * @param viewportX * Viewport X-coordinate. * @param viewportY * Viewport Y-coordinate. * @param viewportZ * Viewport Z-coordinate. * @param viewportHeight * Height of the viewport needed for percentage height text. * @param bottomLeftOut * The bottom left corner of the text bounds. * @param bottomRightOut * The bottom right corner of the text bounds. * @param topRightOut * The top right corner of the text bounds. * @param topLeftOut * The top left corner of the text bounds. */ virtual void getBoundsForTextAtViewportCoords(const AnnotationText& annotationText, const double viewportX, const double viewportY, const double viewportZ, const double viewportHeight, double bottomLeftOut[3], double bottomRightOut[3], double topRightOut[3], double topLeftOut[3]) = 0; /** * Get the bounds of text (in pixels) using the given text * attributes. * * See http://ftgl.sourceforge.net/docs/html/metrics.png * * @param annotationText * Text that is to be drawn. * @param viewportX * Viewport X-coordinate. * @param viewportY * Viewport Y-coordinate. * @param viewportZ * Viewport Z-coordinate. * @param viewportHeight * Height of the viewport needed for percentage height text. * @param bottomLeftOut * The bottom left corner of the text bounds. * @param bottomRightOut * The bottom right corner of the text bounds. * @param topRightOut * The top right corner of the text bounds. * @param topLeftOut * The top left corner of the text bounds. */ void getBoundsForTextAtViewportCoords(const AnnotationText& annotationText, const float viewportX, const float viewportY, const float viewportZ, const float viewportHeight, float bottomLeftOut[3], float bottomRightOut[3], float topRightOut[3], float topLeftOut[3]); static float pointSizeToPixels(const float pointSize); static float pixelsToPointSize(const float pixels); static float getPixelsPerInch(); static void setPixelsPerInch(const float pixelsPerInch); /** * @return The font system is valid. */ virtual bool isValid() const = 0; /** * @return Name of the text renderer. */ virtual AString getName() const = 0; private: BrainOpenGLTextRenderInterface(const BrainOpenGLTextRenderInterface&); BrainOpenGLTextRenderInterface& operator=(const BrainOpenGLTextRenderInterface&); private: static float s_pixelsPerInch; }; #ifdef __BRAIN_OPEN_G_L_TEXT_RENDER_INTERFACE_DECLARE__ float BrainOpenGLTextRenderInterface::s_pixelsPerInch = 72.0; #endif // __BRAIN_OPEN_G_L_TEXT_RENDER_INTERFACE_DECLARE__ } // namespace #endif //__BRAIN_OPEN_G_L_TEXT_RENDER_INTERFACE__H_ connectome-workbench-1.2.3+git41-gc4c6c90/src/Brain/BrainOpenGLTextureManager.cxx000066400000000000000000000154131300200146000273030ustar00rootroot00000000000000 /*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"; } connectome-workbench-1.2.3+git41-gc4c6c90/src/Brain/BrainOpenGLTextureManager.h000066400000000000000000000046641300200146000267360ustar00rootroot00000000000000#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__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Brain/BrainOpenGLViewportContent.cxx000066400000000000000000000751051300200146000275260ustar00rootroot00000000000000 /*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_OPEN_G_L_VIEWPORT_CONTENT_DECLARE__ #include "BrainOpenGLViewportContent.h" #undef __BRAIN_OPEN_G_L_VIEWPORT_CONTENT_DECLARE__ #include #include #include #include #include "BrowserTabContent.h" #include "CaretAssert.h" #include "CaretLogger.h" #include "GapsAndMargins.h" #include "MathFunctions.h" #include "ModelSurfaceMontage.h" #include "SurfaceMontageConfigurationAbstract.h" #include "TileTabsConfiguration.h" using namespace caret; /** * \class BrainOpenGLViewportContent * \brief Dimensions and model for a viewport in the graphics window. * * Dimensions and model for a viewport in the graphics window. */ /** * Constructor. * * @param windowViewport * Viewport of WINDOW in which drawing takes place. * @param tabViewport * Viewport for TAB in which drawing takes place. * @param modelViewport * Viewport for MODEL in which drawing takes place. * @param windowIndex * Index of browser window. * @param highlightTabFlag * True indicates that the tab is highlighted (used in * Tile Tabs mode so user knows graphics region corresponding * to the tab that was recently selected). * @param browserTabContent * Tab's content that is being drawn. */ BrainOpenGLViewportContent::BrainOpenGLViewportContent(const int windowViewport[4], const int tabViewport[4], const int modelViewport[4], const int windowIndex, const bool highlightTabFlag, BrowserTabContent* browserTabContent) : CaretObject(), m_windowIndex(windowIndex), m_highlightTab(highlightTabFlag), m_browserTabContent(browserTabContent) { m_windowX = windowViewport[0]; m_windowY = windowViewport[1]; m_windowWidth = windowViewport[2]; m_windowHeight = windowViewport[3]; m_tabX = tabViewport[0]; m_tabY = tabViewport[1]; m_tabWidth = tabViewport[2]; m_tabHeight = tabViewport[3]; m_modelX = modelViewport[0]; m_modelY = modelViewport[1]; m_modelWidth = modelViewport[2]; m_modelHeight = modelViewport[3]; } /** * Destructor. */ BrainOpenGLViewportContent::~BrainOpenGLViewportContent() { } /** * Copy constructor. * @param obj * Object that is copied. */ BrainOpenGLViewportContent::BrainOpenGLViewportContent(const BrainOpenGLViewportContent& obj) : CaretObject(obj), m_windowIndex(obj.m_windowIndex), m_highlightTab(obj.m_highlightTab) { this->initializeMembersBrainOpenGLViewportContent(); this->copyHelperBrainOpenGLViewportContent(obj); } /** * Assignment operator. * @param obj * Data copied from obj to this. * @return * Reference to this object. */ BrainOpenGLViewportContent& BrainOpenGLViewportContent::operator=(const BrainOpenGLViewportContent& obj) { if (this != &obj) { CaretObject::operator=(obj); this->copyHelperBrainOpenGLViewportContent(obj); } return *this; } /** * Initialize members of a new instance. */ void BrainOpenGLViewportContent::initializeMembersBrainOpenGLViewportContent() { m_modelX = 0; m_modelY = 0; m_modelWidth = 0; m_modelHeight = 0; m_tabX = 0; m_tabY = 0; m_tabWidth = 0; m_tabHeight = 0; m_windowX = 0; m_windowY = 0; m_windowWidth = 0; m_windowHeight = 0; } /** * Helps with copying an object of this type. * @param obj * Object that is copied. */ void BrainOpenGLViewportContent::copyHelperBrainOpenGLViewportContent(const BrainOpenGLViewportContent& obj) { m_modelX = obj.m_modelX; m_modelY = obj.m_modelY; m_modelWidth = obj.m_modelWidth; m_modelHeight = obj.m_modelHeight; m_tabX = obj.m_tabX; m_tabY = obj.m_tabY; m_tabWidth = obj.m_tabWidth; m_tabHeight = obj.m_tabHeight; m_windowX = obj.m_windowX; m_windowY = obj.m_windowY; m_windowWidth = obj.m_windowWidth; m_windowHeight = obj.m_windowHeight; m_browserTabContent = obj.m_browserTabContent; } /** * Adjust the width/height using the aspect ratio */ void BrainOpenGLViewportContent::adjustWidthHeightForAspectRatio(const float aspectRatio, int32_t& width, int32_t& height) { if (aspectRatio > 0.0) { int32_t heightInt = height; float widthFloat = width; float heightFloat = height; float preferredHeightFloat = MathFunctions::round(widthFloat * aspectRatio); const int32_t preferredHeightInt = static_cast(preferredHeightFloat); if (heightInt == preferredHeightInt) { /* * Viewport matches aspect ratio so do not need to * adjust the width and height. * * Due to floating point error, when lock is enabled, * the preferred height may be a very small difference * from the current height. So rounding and then * converting to an int prevents the graphics region * from a small resizing. */ } else if (preferredHeightFloat > heightFloat) { /* * Shrink width */ const float percentage = heightFloat / preferredHeightFloat; width = static_cast(widthFloat * percentage); } else { /* * Shrink height */ height = preferredHeightInt; } } } /** * Adjust the given viewport by applying the given aspect ratio. * * Sets the new height to be width * aspect ratio. If this new height * is too tall, the viewport width and height is scaled down so that * the height fits the original viewport size and the viewport is * horizontally centered. If the new height is less than the original * height, the viewport is centered vertically. * * @param viewport * The viewport * @param aspectRatio * The aspect ratio (height ~= width * aspect ratio) */ void BrainOpenGLViewportContent::adjustViewportForAspectRatio(int viewport[4], const float aspectRatio) { int32_t heightInt = viewport[3]; float widthFloat = viewport[2]; float heightFloat = viewport[3]; float preferredHeightFloat = MathFunctions::round(widthFloat * aspectRatio); const int32_t preferredHeightInt = static_cast(preferredHeightFloat); if (heightInt == preferredHeightInt) { /* * Due to floating point error, when lock is enabled, * the preferred height may be a very small difference * from the current height. So rounding and then * converting to an int prevents the graphics region * from a small resizing. */ } else if (preferredHeightFloat > heightFloat) { const float percentage = heightFloat / preferredHeightFloat; widthFloat *= percentage; const float xOffset = (viewport[2] - widthFloat) / 2.0; viewport[0] += xOffset; viewport[2] = widthFloat; } else { const float yOffset = (viewport[3] - preferredHeightFloat) / 2.0; viewport[1] += yOffset; viewport[3] = preferredHeightFloat; } } /** * @return True indicates that the tab is highlighted (used in * Tile Tabs mode so user knows graphics region corresponding * to the tab that was recently selected). */ bool BrainOpenGLViewportContent::isTabHighlighted() const { return m_highlightTab; } /** * Get the viewport for drawing the model (has been reduced * from tab viewport by applying the margin). * * @param modelViewport * Output into which model viewport dimensions are loaded. * (x, y, width, height) */ void BrainOpenGLViewportContent::getModelViewport(int modelViewport[4]) const { modelViewport[0] = m_modelX; modelViewport[1] = m_modelY; modelViewport[2] = m_modelWidth; modelViewport[3] = m_modelHeight; } /** * Get the viewport for drawing the tab (includes margin). * * @param tabViewport * Output into which tab viewport dimensions are loaded. * (x, y, width, height) */ void BrainOpenGLViewportContent::getTabViewportBeforeApplyingMargins(int tabViewportOut[4]) const { tabViewportOut[0] = m_tabX; tabViewportOut[1] = m_tabY; tabViewportOut[2] = m_tabWidth; tabViewportOut[3] = m_tabHeight; } /** * @return Pointer to the viewport for the window. * * @param windowViewportOut * Output into which window viewport dimensions are loaded. * (x, y, width, height) */ void BrainOpenGLViewportContent::getWindowViewport(int windowViewportOut[4]) const { windowViewportOut[0] = m_windowX; windowViewportOut[1] = m_windowY; windowViewportOut[2] = m_windowWidth; windowViewportOut[3] = m_windowHeight; } /** * @return The window index. */ int BrainOpenGLViewportContent::getWindowIndex() const { return m_windowIndex; } /** * @return Pointer to tab content in viewport. */ BrowserTabContent* BrainOpenGLViewportContent::getBrowserTabContent() const { return m_browserTabContent; } /** * @return Index of browser tab or -1 if there is not browser tab for this viewport. */ int32_t BrainOpenGLViewportContent::getTabIndex() const { if (m_browserTabContent != NULL) { return m_browserTabContent->getTabNumber(); } return -1; } /** * Get a description of this object's content. * @return String describing this object's content. */ AString BrainOpenGLViewportContent::toString() const { const QString windowMsg = QString(" Window x=%1 y=%2 w=%3 h=%4").arg(m_windowX).arg(m_windowY).arg(m_windowWidth).arg(m_windowHeight); 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); msgOut.appendWithNewLine(tabMsg); msgOut.appendWithNewLine(modelMsg); return msgOut; } /** * Create viewport contents for a single tab using the given window content and window sizes. * * @param browserTabContent * Tab's content that is being drawn. * @param gapsAndMargins * Gaps and margins * @param windowIndex * Index of browser window. * @param windowViewport * Viewport of WINDOW in which drawing takes place. * @return * Viewport content for the single tab. */ BrainOpenGLViewportContent* BrainOpenGLViewportContent::createViewportForSingleTab(BrowserTabContent* browserTabContent, const GapsAndMargins* gapsAndMargins, const int32_t windowIndex, const int32_t windowViewport[4]) { int tabViewport[4] = { windowViewport[0], windowViewport[1], windowViewport[2], windowViewport[3] }; int modelViewport[4] = { tabViewport[0], tabViewport[1], tabViewport[2], tabViewport[3] }; if (browserTabContent != NULL) { if (browserTabContent->isAspectRatioLocked()) { const float aspectRatio = browserTabContent->getAspectRatio(); BrainOpenGLViewportContent::adjustViewportForAspectRatio(tabViewport, aspectRatio); } const int32_t tabIndex = browserTabContent->getTabNumber(); createModelViewport(tabViewport, tabIndex, gapsAndMargins, modelViewport); } BrainOpenGLViewportContent* vpContent = new BrainOpenGLViewportContent(windowViewport, tabViewport, modelViewport, windowIndex, false, browserTabContent); return vpContent; } /** * Create Viewport Contents for the given tab contents, window sizes, and tile sizes. * * @param tabContents * Content of each tab. * @param tileTabsConfiguration * The tile tabs configuration * @param gapsAndMargins * Contains margins around edges of tabs * @param windowIndex * Index of the window. * @param windowViewport * The window's viewport. * @param hightlightTabIndex * Index of tab that is highlighted when selected by user. * @return * Vector containing data for drawing each model. */ std::vector BrainOpenGLViewportContent::createViewportContentForTileTabs(std::vector& tabContents, TileTabsConfiguration* tileTabsConfiguration, const GapsAndMargins* gapsAndMargins, const int32_t windowIndex, const int32_t windowViewport[4], const int32_t highlightTabIndex) { CaretAssert(tileTabsConfiguration); CaretAssert(gapsAndMargins); std::vector viewportContentsOut; const int32_t numberOfTabs = static_cast(tabContents.size()); if (numberOfTabs <= 0) { return viewportContentsOut; } const int32_t windowWidth = windowViewport[2]; const int32_t windowHeight = windowViewport[3]; /* * The tile tabs configuration provides the sizes of the * rows and columns. The user may "stretch" rows and/or * columns. Thus, the tabs viewports may not be uniformly sized. */ std::vector tabConfigRowHeights; std::vector tabConfigColumnWidths; tileTabsConfiguration->getRowHeightsAndColumnWidthsForWindowSize(windowWidth, windowHeight, numberOfTabs, tabConfigRowHeights, tabConfigColumnWidths); const int32_t numRows = static_cast(tabConfigRowHeights.size()); const int32_t numColumns = static_cast(tabConfigColumnWidths.size()); const int32_t numCells = numRows * numColumns; if (numCells <= 0) { return viewportContentsOut; } CaretAssert(numRows > 0); CaretAssert(numColumns > 0); /* * Due to aspect ratios, the width or height * of tab viewports may shrink so we will * need to recompute the row heights and column * widths. */ std::vector rowHeights(numRows, 0); std::vector columnWidths(numColumns, 0); /* * Create the sizes for the tabs before and after application * of aspect ratio. */ std::vector tabSizeInfoVector; int32_t iTab = 0; for (int32_t iRowFromTop = 0; iRowFromTop < numRows; iRowFromTop++) { const int32_t vpHeight = tabConfigRowHeights[iRowFromTop]; for (int32_t jCol = 0; jCol < numColumns; jCol++) { const int32_t vpWidth = tabConfigColumnWidths[jCol]; if (iTab < numberOfTabs) { CaretAssertVectorIndex(tabContents, iTab); /* * Note: the constructor will adjust the width and * height if lock aspect ratio is enabled. */ TileTabsViewportSizingInfo tsi(tabContents[iTab], iRowFromTop, jCol, vpWidth, vpHeight); rowHeights[iRowFromTop] = std::max(rowHeights[iRowFromTop], tsi.m_height); columnWidths[jCol] = std::max(columnWidths[jCol], tsi.m_width); tabSizeInfoVector.push_back(tsi); iTab++; } else { /* * Get out of loop */ iRowFromTop = numRows; jCol = numColumns; } } } /* * Note: There may be more tabs than there are cells (rows * columns) * so some tabs may not be displayed. */ const int32_t numberOfDisplayedTabs = static_cast(tabSizeInfoVector.size()); /* * Now that we know the height of each row, and width of each column, * we can get the total width and height of ALL tab viewports. */ const int32_t allTabsHeight = std::accumulate(rowHeights.begin(), rowHeights.end(), 0); const int32_t allTabsWidth = std::accumulate(columnWidths.begin(), columnWidths.end(), 0); /* * The total width/height of the tabs may be less than the size * of the window viewport. We want to center the tabs inside of * the window viewport so find any extra space in the window. */ const int32_t windowExtraWidth = windowWidth - allTabsWidth; const int32_t windowExtraHeight = windowHeight - allTabsHeight; CaretAssert(windowExtraWidth >= 0); CaretAssert(windowExtraHeight >= 0); /* * Set the X and Y-coordinates for the tab viewports * We start at the bottom row, left corner. */ int32_t vpY = windowViewport[1] + (windowExtraHeight / 2); for (int32_t iRow = (numRows - 1); iRow >= 0; iRow--) { int32_t vpX = windowViewport[0] + (windowExtraWidth / 2); for (int32_t jCol = 0; jCol < numColumns; jCol++) { TileTabsViewportSizingInfo* tabSizePtr = NULL; for (int32_t iTab = 0; iTab < numberOfDisplayedTabs; iTab++) { CaretAssertVectorIndex(tabSizeInfoVector, iTab); if ((tabSizeInfoVector[iTab].m_rowIndexFromTop == iRow) && (tabSizeInfoVector[iTab].m_columnIndex == jCol)) { tabSizePtr = &tabSizeInfoVector[iTab]; break; } } if (tabSizePtr != NULL) { CaretAssertVectorIndex(columnWidths, jCol); CaretAssertVectorIndex(rowHeights, iRow); /* * Center tab inside of its region */ const int32_t tabExtraWidth = columnWidths[jCol] - tabSizePtr->m_width; const int32_t tabExtraHeight = rowHeights[iRow] - tabSizePtr->m_height; const int32_t tabX = vpX + (tabExtraWidth / 2); const int32_t tabY = vpY + (tabExtraHeight / 2); const int tabViewport[4] = { tabX, tabY, tabSizePtr->m_width, tabSizePtr->m_height }; /* * Model is drawn in the model viewport inside any margins. */ const int32_t tabIndex = tabSizePtr->m_browserTabContent->getTabNumber(); int modelViewport[4] = { 0, 0, 0, 0 }; createModelViewport(tabViewport, tabIndex, gapsAndMargins, modelViewport); //tabSizePtr->print(tabX, tabY); BrainOpenGLViewportContent* vpContent = new BrainOpenGLViewportContent(windowViewport, tabViewport, modelViewport, windowIndex, (highlightTabIndex ==tabIndex), tabSizePtr->m_browserTabContent); viewportContentsOut.push_back(vpContent); } else { /* * If the number of tabs is less than the number of tiles, * empty viewport content is needed so that user can draw * annotations in these regions. */ const int tabViewport[4] = { vpX, vpY, columnWidths[jCol], rowHeights[iRow] }; BrainOpenGLViewportContent* vpContent = new BrainOpenGLViewportContent(windowViewport, tabViewport, tabViewport, windowIndex, false, NULL); viewportContentsOut.push_back(vpContent); } CaretAssertVectorIndex(columnWidths, jCol); vpX += columnWidths[jCol]; } CaretAssertVectorIndex(rowHeights, iRow); vpY += rowHeights[iRow]; } return viewportContentsOut; } /** * Create viewport from the model using the tab's viewport and the tabs margins. * * @paramj tabViewport * Viewport for the tab. * @param tabIndex * Index of the tab. * @param gapsAndMargins * Gaps and margins. * @param modelViewportOut * Output containing viewport for model. */ void BrainOpenGLViewportContent::createModelViewport(const int tabViewport[4], const int32_t tabIndex, const GapsAndMargins* gapsAndMargins, int modelViewportOut[4]) { int32_t leftMargin = 0; int32_t rightMargin = 0; int32_t bottomMargin = 0; int32_t topMargin = 0; modelViewportOut[0] = tabViewport[0]; modelViewportOut[1] = tabViewport[1]; modelViewportOut[2] = tabViewport[2]; modelViewportOut[3] = tabViewport[3]; if (gapsAndMargins != NULL) { gapsAndMargins->getMarginsInPixelsForDrawing(tabIndex, tabViewport[2], tabViewport[3], leftMargin, rightMargin, bottomMargin, topMargin); const int32_t marginHorizSize = (leftMargin + rightMargin); const int32_t marginVertSize = (bottomMargin + topMargin); if ((marginHorizSize < modelViewportOut[2]) && (marginVertSize < modelViewportOut[3])) { modelViewportOut[0] += leftMargin; modelViewportOut[1] += bottomMargin; modelViewportOut[2] -= marginHorizSize; modelViewportOut[3] -= marginVertSize; } else { CaretLogSevere("Margins are too big for tab " + AString::number(tabIndex + 1) + " viewport. Viewport (x,y,w,h)=" + AString::fromNumbers(modelViewportOut, 4, ",") + " margin (l,r,b,t)=" + AString::number(leftMargin) + "," + AString::number(rightMargin) + "," + AString::number(bottomMargin) + "," + AString::number(topMargin)); } } } /** * When viewing a surface montage, "getModelViewport()" returns the viewport * for the entire surface montage (viewport containing all of the surfaces). * However, the are instance where we need the viewport for one of surfaces * in a surface montage. This method will find the viewport for one of the * surfaces within the surface montage using the given X and Y coordinates. * * @param montageX * X-coordinate within the surface montage. * @param montageX * X-coordinate within the surface montage. * @param subViewportOut * Viewport for individual surface in the montage at the given X, Y * coordinates. Note: if a surface montage is not displayed, * this method returns the same viewport as getModelViewport(). */ void BrainOpenGLViewportContent::getSurfaceMontageModelViewport(const int32_t montageX, const int32_t montageY, int subViewportOut[4]) const { getModelViewport(subViewportOut); if (this->m_browserTabContent == NULL) { return; } ModelSurfaceMontage* msm = m_browserTabContent->getDisplayedSurfaceMontageModel(); if (msm != NULL) { std::vector montageViewports; msm->getSurfaceMontageViewportsForTransformation(m_browserTabContent->getTabNumber(), montageViewports); const int x = montageX; // + m_tabX; const int y = montageY; // + m_tabY; for (std::vector::const_iterator iter = montageViewports.begin(); iter != montageViewports.end(); iter++) { const SurfaceMontageViewport* smv = *iter; int vp[4]; smv->getViewport(vp); const int offsetX = x - vp[0]; if ((offsetX >= 0) && (offsetX < vp[2])) { const int offsetY = y - vp[1]; if ((offsetY >= 0) && (offsetY < vp[3])) { smv->getViewport(subViewportOut); return; } } } } } /* =================================================================================================== */ /** * Contructor for tile tabs viewport sizing. * * @param browserTabContent * Content of the browser tab. * @param rowIndexFromTop * Row index starting with top row * @param columnIndex * Column index starting on left. * @param initialWidth * Initial width of the tab prior to application of aspect ratio. * @param initialHeight * Initial height of the tab prior to application of aspect ratio. */ BrainOpenGLViewportContent::TileTabsViewportSizingInfo::TileTabsViewportSizingInfo(BrowserTabContent* browserTabContent, const int32_t rowIndexFromTop, const int32_t columnIndex, const float initialWidth, const float initialHeight) : m_browserTabContent(browserTabContent), m_rowIndexFromTop(rowIndexFromTop), m_columnIndex(columnIndex), m_initialWidth(initialWidth), m_initialHeight(initialHeight), m_width(initialWidth), m_height(initialHeight) { if (browserTabContent->isAspectRatioLocked()) { const float aspectRatio = browserTabContent->getAspectRatio(); if (aspectRatio > 0.0) { BrainOpenGLViewportContent::adjustWidthHeightForAspectRatio(aspectRatio, m_width, m_height); } } } /** * Copy constructor. * @param obj * Object that is copied. */ BrainOpenGLViewportContent::TileTabsViewportSizingInfo& BrainOpenGLViewportContent::TileTabsViewportSizingInfo::operator=(const TileTabsViewportSizingInfo& obj) { if (this != &obj) { m_browserTabContent = obj.m_browserTabContent; m_rowIndexFromTop = obj.m_rowIndexFromTop; m_columnIndex = obj.m_columnIndex; m_initialWidth = obj.m_initialWidth; m_initialHeight = obj.m_initialHeight; m_width = obj.m_initialWidth; m_height = obj.m_initialHeight; } return *this; } /** * Print for debugging. * * @param x * X-coordinate of tab viewport. * @param y * Y-coordinate of tab viewport. */ void BrainOpenGLViewportContent::TileTabsViewportSizingInfo::print(const int32_t x, const int32_t y) { const QString msg("Model: " + m_browserTabContent->getName() + "\n row/col: " + QString::number(m_rowIndexFromTop) + ", " + QString::number(m_columnIndex) + "\n x/y: " + QString::number(x) + ", " + QString::number(y) + "\n width/height: " + QString::number(m_width) + ", " + QString::number(m_height)); std::cout << qPrintable(msg) << std::endl; } connectome-workbench-1.2.3+git41-gc4c6c90/src/Brain/BrainOpenGLViewportContent.h000066400000000000000000000152161300200146000271500ustar00rootroot00000000000000#ifndef __BRAIN_OPEN_G_L_VIEWPORT_CONTENT__H_ #define __BRAIN_OPEN_G_L_VIEWPORT_CONTENT__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" namespace caret { class BrowserTabContent; class GapsAndMargins; class TileTabsConfiguration; class BrainOpenGLViewportContent : public CaretObject { public: ~BrainOpenGLViewportContent(); BrainOpenGLViewportContent(const BrainOpenGLViewportContent& obj); BrainOpenGLViewportContent& operator=(const BrainOpenGLViewportContent& obj); void getModelViewport(int modelViewportOut[4]) const; void getSurfaceMontageModelViewport(const int32_t montageX, const int32_t montageY, int subViewportOut[4]) const; void getTabViewportBeforeApplyingMargins(int tabViewportOut[4]) const; void getWindowViewport(int windowViewportOut[4]) const; int getWindowIndex() const; BrowserTabContent* getBrowserTabContent() const; int32_t getTabIndex() const; bool isTabHighlighted() const; static void adjustViewportForAspectRatio(int viewport[4], const float aspectRatio); static void adjustWidthHeightForAspectRatio(const float aspectRatio, int32_t& width, int32_t& height); static std::vector createViewportContentForTileTabs(std::vector& tabContents, TileTabsConfiguration* tileTabsConfiguration, const GapsAndMargins* gapsAndMargins, const int32_t windowIndex, const int32_t windowViewport[4], const int32_t highlightTabIndex); static BrainOpenGLViewportContent* createViewportForSingleTab(BrowserTabContent* browserTabContent, const GapsAndMargins* gapsAndMargins, const int32_t windowIndex, const int32_t windowViewport[4]); private: /** * Assists with creation of the tile tab viewports */ class TileTabsViewportSizingInfo { public: TileTabsViewportSizingInfo(BrowserTabContent* browserTabContent, const int32_t rowIndexFromTop, const int32_t columnIndex, const float initialWidth, const float initialHeight); TileTabsViewportSizingInfo& operator=(const TileTabsViewportSizingInfo& obj); void print(const int32_t x, const int32_t y); BrowserTabContent* m_browserTabContent; int32_t m_rowIndexFromTop; int32_t m_columnIndex; /** size with application of tile tabs configuration */ float m_initialWidth; float m_initialHeight; /** size after application of lock aspect ratio */ int32_t m_width; int32_t m_height; }; BrainOpenGLViewportContent(const int windowViewport[4], const int tabViewport[4], const int modelViewport[4], const int windowIndex, const bool highlightTabFlag, BrowserTabContent* browserTabContent); void initializeMembersBrainOpenGLViewportContent(); void copyHelperBrainOpenGLViewportContent(const BrainOpenGLViewportContent& obj); static void createModelViewport(const int tabViewport[4], const int32_t tabIndex, const GapsAndMargins* gapsAndMargins, int modelViewportOut[4]); const int m_windowIndex; const bool m_highlightTab; /** Tab viewport's X-coordinate */ int m_tabX; /** Tab viewport's Y-coordinate */ int m_tabY; /** Tab viewport's Width */ int m_tabWidth; /** Tab viewport's Height */ int m_tabHeight; /** Model viewport's X-coordinate */ int m_modelX; /** Model viewport's Y-coordinate */ int m_modelY; /** Model viewport's Width */ int m_modelWidth; /** Model viewport's Height */ int m_modelHeight; /** Window viewport's X-coordinate */ int m_windowX; /** Window viewport's Y-coordinate */ int m_windowY; /** Window viewport's Width */ int m_windowWidth; /** Window viewport's Height */ int m_windowHeight; BrowserTabContent* m_browserTabContent; public: virtual AString toString() const; }; #ifdef __BRAIN_OPEN_G_L_VIEWPORT_CONTENT_DECLARE__ // #endif // __BRAIN_OPEN_G_L_VIEWPORT_CONTENT_DECLARE__ } // namespace #endif //__BRAIN_OPEN_G_L_VIEWPORT_CONTENT__H_ connectome-workbench-1.2.3+git41-gc4c6c90/src/Brain/BrainOpenGLVolumeObliqueSliceDrawing.cxx000066400000000000000000006530201300200146000314360ustar00rootroot00000000000000 /*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_VOLUME_OBLIQUE_SLICE_DRAWING_DECLARE__ #include "BrainOpenGLVolumeObliqueSliceDrawing.h" #undef __BRAIN_OPEN_GL_VOLUME_OBLIQUE_SLICE_DRAWING_DECLARE__ #include "AnnotationCoordinate.h" #include "AnnotationPointSizeText.h" #include "BoundingBox.h" #include "Brain.h" #include "BrainOpenGLAnnotationDrawingFixedPipeline.h" #include "BrainOpenGLPrimitiveDrawing.h" #include "BrowserTabContent.h" #include "CaretAssert.h" #include "CaretLogger.h" #include "CaretOpenGLInclude.h" #include "CaretPreferences.h" #include "CiftiMappableDataFile.h" #include "DeveloperFlagsEnum.h" #include "DisplayPropertiesFoci.h" #include "DisplayPropertiesLabels.h" #include "ElapsedTimer.h" #include "FociFile.h" #include "Focus.h" #include "GapsAndMargins.h" #include "GiftiLabel.h" #include "GiftiLabelTable.h" #include "GroupAndNameHierarchyModel.h" #include "IdentificationWithColor.h" #include "LabelDrawingProperties.h" #include "MathFunctions.h" #include "Matrix4x4.h" #include "ModelVolume.h" #include "ModelWholeBrain.h" #include "NodeAndVoxelColoring.h" #include "SelectionItemFocusVolume.h" #include "SelectionItemVoxel.h" #include "SelectionItemVoxelEditing.h" #include "SelectionManager.h" #include "SessionManager.h" #include "Surface.h" #include "VolumeFile.h" #include "VolumeSurfaceOutlineColorOrTabModel.h" #include "VolumeSurfaceOutlineModel.h" #include "VolumeSurfaceOutlineSetModel.h" using namespace caret; static const bool debugFlag = false; /** * \class caret::BrainOpenGLVolumeObliqueSliceDrawing * \brief Draws volume slices using OpenGL * \ingroup Brain */ /** * Constructor. */ BrainOpenGLVolumeObliqueSliceDrawing::BrainOpenGLVolumeObliqueSliceDrawing() : CaretObject() { } /** * Destructor. */ BrainOpenGLVolumeObliqueSliceDrawing::~BrainOpenGLVolumeObliqueSliceDrawing() { } /** * Draw Volume Slices or slices for ALL Stuctures View. * * @param fixedPipelineDrawing * The OpenGL drawing. * @param browserTabContent * Content of browser tab that is to be drawn. * @param volumeDrawInfo * Info on each volume layers for drawing. * @param sliceDrawingType * Type of slice drawing (montage, single) * @param sliceProjectionType * Type of projection for the slice drawing (oblique, orthogonal) * @param viewport * The viewport (region of graphics area) for drawing slices. */ void BrainOpenGLVolumeObliqueSliceDrawing::draw(BrainOpenGLFixedPipeline* fixedPipelineDrawing, BrowserTabContent* browserTabContent, std::vector& volumeDrawInfo, const VolumeSliceDrawingTypeEnum::Enum sliceDrawingType, const VolumeSliceProjectionTypeEnum::Enum sliceProjectionType, const int32_t viewport[4]) { CaretAssert(sliceProjectionType == VolumeSliceProjectionTypeEnum::VOLUME_SLICE_PROJECTION_OBLIQUE); if (volumeDrawInfo.empty()) { return; } CaretAssert(fixedPipelineDrawing); CaretAssert(browserTabContent); m_browserTabContent = browserTabContent; m_fixedPipelineDrawing = fixedPipelineDrawing; /* * No lighting for drawing slices */ m_fixedPipelineDrawing->disableLighting(); /* * Initialize class members which help reduce the number of * parameters that are passed to methods. */ m_brain = NULL; m_modelVolume = NULL; m_modelWholeBrain = NULL; if (m_browserTabContent->getDisplayedVolumeModel() != NULL) { m_modelVolume = m_browserTabContent->getDisplayedVolumeModel(); m_brain = m_modelVolume->getBrain(); } else if (m_browserTabContent->getDisplayedWholeBrainModel() != NULL) { m_modelWholeBrain = m_browserTabContent->getDisplayedWholeBrainModel(); m_brain = m_modelWholeBrain->getBrain(); } else { CaretAssertMessage(0, "Invalid model for volume slice drawing."); } CaretAssert(m_brain); m_volumeDrawInfo = volumeDrawInfo; if (m_volumeDrawInfo.empty()) { return; } 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); m_tabIndex = m_browserTabContent->getTabNumber(); /* * Cifti files are slow at getting individual voxels since they * provide no access to individual voxels. The reason is that * the data may be on a server (Dense data) and accessing a single * voxel would require requesting the entire map. So, for * each Cifti file, get the enter map. This also, eliminate multiple * requests for the same map when drawing an ALL view. */ const int32_t numVolumes = static_cast(m_volumeDrawInfo.size()); for (int32_t i = 0; i < numVolumes; i++) { std::vector ciftiMapData; m_ciftiMappableFileData.push_back(ciftiMapData); const CiftiMappableDataFile* ciftiMapFile = dynamic_cast(m_volumeDrawInfo[i].volumeFile); if (ciftiMapFile != NULL) { ciftiMapFile->getMapData(m_volumeDrawInfo[i].mapIndex, m_ciftiMappableFileData[i]); } } /** END SETUP OF MEMBERS IN PARENT CLASS BrainOpenGLVolumeObliqueSliceDrawing */ if (browserTabContent->getDisplayedVolumeModel() != NULL) { drawVolumeSliceViewPlane(sliceDrawingType, sliceProjectionType, browserTabContent->getSliceViewPlane(), viewport); } else if (browserTabContent->getDisplayedWholeBrainModel() != NULL) { drawVolumeSlicesForAllStructuresView(sliceProjectionType, viewport); } } /** * Draw volume view slices for the given view plane. * * @param sliceDrawingType * Type of slice drawing (montage, single) * @param sliceProjectionType * Type of projection for the slice drawing (oblique, orthogonal) * @param sliceViewPlane * The plane for slice drawing. * @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]) { switch (sliceViewPlane) { case VolumeSliceViewPlaneEnum::ALL: { const int32_t gap = 2; const int32_t vpHalfX = viewport[2] / 2; const int32_t vpHalfY = viewport[3] / 2; /* * Draw parasagittal slice */ const int32_t paraVP[4] = { viewport[0], viewport[1] + vpHalfY + gap, vpHalfX - gap, vpHalfY - gap }; glPushMatrix(); drawVolumeSliceViewType(sliceDrawingType, sliceProjectionType, VolumeSliceViewPlaneEnum::PARASAGITTAL, paraVP); glPopMatrix(); /* * Draw coronal slice */ const int32_t coronalVP[4] = { viewport[0] + vpHalfX + gap, viewport[1] + vpHalfY + gap, vpHalfX - gap, vpHalfY - gap }; glPushMatrix(); drawVolumeSliceViewType(sliceDrawingType, sliceProjectionType, VolumeSliceViewPlaneEnum::CORONAL, coronalVP); glPopMatrix(); /* * Draw axial slice */ const int32_t axialVP[4] = { viewport[0] + vpHalfX + gap, viewport[1], vpHalfX - gap, vpHalfY - gap }; glPushMatrix(); drawVolumeSliceViewType(sliceDrawingType, sliceProjectionType, VolumeSliceViewPlaneEnum::AXIAL, 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; } } break; case VolumeSliceViewPlaneEnum::AXIAL: case VolumeSliceViewPlaneEnum::CORONAL: case VolumeSliceViewPlaneEnum::PARASAGITTAL: drawVolumeSliceViewType(sliceDrawingType, sliceProjectionType, sliceViewPlane, viewport); break; } } /** * Draw slices for the all structures view. * * @param sliceProjectionType * Type of projection for the slice drawing (oblique, orthogonal) * @param viewport * The viewport. */ void BrainOpenGLVolumeObliqueSliceDrawing::drawVolumeSlicesForAllStructuresView(const VolumeSliceProjectionTypeEnum::Enum sliceProjectionType, const int32_t viewport[4]) { m_orthographicBounds[0] = m_fixedPipelineDrawing->orthographicLeft; m_orthographicBounds[1] = m_fixedPipelineDrawing->orthographicRight; m_orthographicBounds[2] = m_fixedPipelineDrawing->orthographicBottom; m_orthographicBounds[3] = m_fixedPipelineDrawing->orthographicTop; m_orthographicBounds[4] = m_fixedPipelineDrawing->orthographicNear; m_orthographicBounds[5] = m_fixedPipelineDrawing->orthographicFar; /* * Enlarge the region */ { const float left = m_fixedPipelineDrawing->orthographicLeft; const float right = m_fixedPipelineDrawing->orthographicRight; const float bottom = m_fixedPipelineDrawing->orthographicBottom; const float top = m_fixedPipelineDrawing->orthographicTop; const float scale = 2.0; const float centerX = (left + right) / 2.0; const float dx = (right - left) / 2.0; const float newLeft = centerX - (dx * scale); const float newRight = centerX + (dx * scale); const float centerY = (bottom + top) / 2.0; const float dy = (top - bottom) / 2.0; const float newBottom = centerY - (dy * scale); const float newTop = centerY + (dy * scale); m_orthographicBounds[0] = newLeft; m_orthographicBounds[1] = newRight; m_orthographicBounds[2] = newBottom; m_orthographicBounds[3] = newTop; } const float sliceCoordinates[3] = { m_browserTabContent->getSliceCoordinateParasagittal(), m_browserTabContent->getSliceCoordinateCoronal(), m_browserTabContent->getSliceCoordinateAxial() }; if (m_browserTabContent->isSliceAxialEnabled()) { glPushMatrix(); drawVolumeSliceViewProjection(VolumeSliceDrawingTypeEnum::VOLUME_SLICE_DRAW_SINGLE, sliceProjectionType, VolumeSliceViewPlaneEnum::AXIAL, sliceCoordinates, viewport); glPopMatrix(); } if (m_browserTabContent->isSliceCoronalEnabled()) { glPushMatrix(); drawVolumeSliceViewProjection(VolumeSliceDrawingTypeEnum::VOLUME_SLICE_DRAW_SINGLE, sliceProjectionType, VolumeSliceViewPlaneEnum::CORONAL, sliceCoordinates, viewport); glPopMatrix(); } if (m_browserTabContent->isSliceParasagittalEnabled()) { glPushMatrix(); drawVolumeSliceViewProjection(VolumeSliceDrawingTypeEnum::VOLUME_SLICE_DRAW_SINGLE, sliceProjectionType, VolumeSliceViewPlaneEnum::PARASAGITTAL, sliceCoordinates, viewport); glPopMatrix(); } } /** * Draw single or montage volume view slices. * * @param sliceDrawingType * Type of slice drawing (montage, single) * @param sliceProjectionType * Type of projection for the slice drawing (oblique, orthogonal) * @param sliceViewPlane * The plane for slice drawing. * @param viewport * The viewport (region of graphics area) for drawing slices. */ void BrainOpenGLVolumeObliqueSliceDrawing::drawVolumeSliceViewType(const VolumeSliceDrawingTypeEnum::Enum sliceDrawingType, const VolumeSliceProjectionTypeEnum::Enum sliceProjectionType, const VolumeSliceViewPlaneEnum::Enum sliceViewPlane, const int32_t viewport[4]) { switch (sliceDrawingType) { case VolumeSliceDrawingTypeEnum::VOLUME_SLICE_DRAW_MONTAGE: drawVolumeSliceViewTypeMontage(sliceDrawingType, sliceProjectionType, sliceViewPlane, viewport); break; case VolumeSliceDrawingTypeEnum::VOLUME_SLICE_DRAW_SINGLE: { const float sliceCoordinates[3] = { m_browserTabContent->getSliceCoordinateParasagittal(), m_browserTabContent->getSliceCoordinateCoronal(), m_browserTabContent->getSliceCoordinateAxial() }; drawVolumeSliceViewProjection(sliceDrawingType, sliceProjectionType, sliceViewPlane, sliceCoordinates, viewport); } break; } } /** * Draw montage slices. * * @param sliceDrawingType * Type of slice drawing (montage, single) * @param sliceProjectionType * Type of projection for the slice drawing (oblique, orthogonal) * @param sliceViewPlane * The plane for slice drawing. * @param viewport * The viewport (region of graphics area) for drawing slices. */ void BrainOpenGLVolumeObliqueSliceDrawing::drawVolumeSliceViewTypeMontage(const VolumeSliceDrawingTypeEnum::Enum sliceDrawingType, const VolumeSliceProjectionTypeEnum::Enum sliceProjectionType, const VolumeSliceViewPlaneEnum::Enum sliceViewPlane, const int32_t viewport[4]) { const int32_t numRows = m_browserTabContent->getMontageNumberOfRows(); CaretAssert(numRows > 0); const int32_t numCols = m_browserTabContent->getMontageNumberOfColumns(); CaretAssert(numCols > 0); const CaretPreferences* caretPreferences = SessionManager::get()->getCaretPreferences(); const int32_t montageCoordPrecision = caretPreferences->getVolumeMontageCoordinatePrecision(); const GapsAndMargins* gapsAndMargins = m_brain->getGapsAndMargins(); // const int32_t horizontalMargin = static_cast(viewport[2] * gapsAndMargins->getVolumeMontageHorizontalGap()); // const int32_t verticalMargin = static_cast(viewport[3] * gapsAndMargins->getVolumeMontageVerticalGap()); // // const int32_t totalGapX = horizontalMargin * (numCols - 1); // const int32_t vpSizeX = (viewport[2] - totalGapX) / numCols; // const int32_t totalGapY = verticalMargin * (numRows - 1); // const int32_t vpSizeY = (viewport[3] - totalGapY) / numRows; const int32_t windowIndex = m_fixedPipelineDrawing->m_windowIndex; int32_t vpSizeY = 0; int32_t verticalMargin = 0; BrainOpenGLFixedPipeline::createSubViewportSizeAndGaps(viewport[3], gapsAndMargins->getVolumeMontageVerticalGapForWindow(windowIndex), -1, numRows, vpSizeY, verticalMargin); int32_t vpSizeX = 0; int32_t horizontalMargin = 0; BrainOpenGLFixedPipeline::createSubViewportSizeAndGaps(viewport[2], gapsAndMargins->getVolumeMontageHorizontalGapForWindow(windowIndex), -1, numCols, vpSizeX, horizontalMargin); /* * Voxel sizes for underlay volume */ float originX, originY, originZ; float x1, y1, z1; m_underlayVolume->indexToSpace(0, 0, 0, originX, originY, originZ); m_underlayVolume->indexToSpace(1, 1, 1, x1, y1, z1); float sliceThickness = 0.0; float sliceOrigin = 0.0; AString axisLetter = ""; float sliceCoordinates[3] = { m_browserTabContent->getSliceCoordinateParasagittal(), m_browserTabContent->getSliceCoordinateCoronal(), m_browserTabContent->getSliceCoordinateAxial() }; int32_t sliceIndex = -1; int32_t maximumSliceIndex = -1; int64_t dimI, dimJ, dimK, numMaps, numComponents; m_underlayVolume->getDimensions(dimI, dimJ, dimK, numMaps, numComponents); const int32_t sliceStep = m_browserTabContent->getMontageSliceSpacing(); switch (sliceViewPlane) { case VolumeSliceViewPlaneEnum::ALL: sliceIndex = -1; break; case VolumeSliceViewPlaneEnum::AXIAL: sliceIndex = m_browserTabContent->getSliceIndexAxial(m_underlayVolume); maximumSliceIndex = dimK; sliceThickness = z1 - originZ; sliceOrigin = originZ; axisLetter = "Z"; break; case VolumeSliceViewPlaneEnum::CORONAL: sliceIndex = m_browserTabContent->getSliceIndexCoronal(m_underlayVolume); maximumSliceIndex = dimJ; sliceThickness = y1 - originY; sliceOrigin = originY; axisLetter = "Y"; break; case VolumeSliceViewPlaneEnum::PARASAGITTAL: sliceIndex = m_browserTabContent->getSliceIndexParasagittal(m_underlayVolume); maximumSliceIndex = dimI; sliceThickness = x1 - originX; sliceOrigin = originX; axisLetter = "X"; break; } /* * Foreground color for slice coordinate text */ const CaretPreferences* prefs = SessionManager::get()->getCaretPreferences(); uint8_t foregroundRGBA[4]; prefs->getBackgroundAndForegroundColors()->getColorForegroundVolumeView(foregroundRGBA); foregroundRGBA[3] = 255; uint8_t backgroundRGBA[4]; prefs->getBackgroundAndForegroundColors()->getColorBackgroundVolumeView(backgroundRGBA); backgroundRGBA[3] = 255; const bool showCoordinates = prefs->isVolumeMontageAxesCoordinatesDisplayed(); /* * Determine a slice offset to selected slices is in * the center of the montage */ const int32_t numSlicesViewed = (numCols * numRows); const int32_t sliceOffset = ((numSlicesViewed / 2) * sliceStep); sliceIndex += sliceOffset; /* * Find first valid slice for montage */ while (sliceIndex >= 0) { if (sliceIndex < maximumSliceIndex) { break; } sliceIndex -= sliceStep; } if (sliceIndex >= 0) { for (int32_t i = 0; i < numRows; i++) { for (int32_t j = 0; j < numCols; j++) { if ((sliceIndex >= 0) && (sliceIndex < maximumSliceIndex)) { const int32_t vpX = (j * (vpSizeX + horizontalMargin)); const int32_t vpY = ((numRows - i - 1) * (vpSizeY + verticalMargin)); int32_t vp[4] = { viewport[0] + vpX, viewport[1] + vpY, vpSizeX, vpSizeY }; if ((vp[2] <= 0) || (vp[3] <= 0)) { continue; } const float sliceCoord = (sliceOrigin + sliceThickness * sliceIndex); switch (sliceViewPlane) { case VolumeSliceViewPlaneEnum::ALL: break; case VolumeSliceViewPlaneEnum::AXIAL: sliceCoordinates[2] = sliceCoord; break; case VolumeSliceViewPlaneEnum::CORONAL: sliceCoordinates[1] = sliceCoord; break; case VolumeSliceViewPlaneEnum::PARASAGITTAL: sliceCoordinates[0] = sliceCoord; break; } drawVolumeSliceViewProjection(sliceDrawingType, sliceProjectionType, sliceViewPlane, sliceCoordinates, vp); if (showCoordinates) { const AString coordText = (axisLetter + "=" + AString::number(sliceCoord, 'f', montageCoordPrecision) + "mm"); AnnotationPointSizeText annotationText(AnnotationAttributesDefaultTypeEnum::NORMAL); annotationText.setHorizontalAlignment(AnnotationTextAlignHorizontalEnum::RIGHT); annotationText.setVerticalAlignment(AnnotationTextAlignVerticalEnum::BOTTOM); annotationText.setFontPointSize(AnnotationTextFontPointSizeEnum::SIZE12); annotationText.setLineColor(CaretColorEnum::NONE); annotationText.setTextColor(CaretColorEnum::CUSTOM); annotationText.setBackgroundColor(CaretColorEnum::CUSTOM); annotationText.setCustomTextColor(foregroundRGBA); annotationText.setCustomBackgroundColor(backgroundRGBA); annotationText.setText(coordText); m_fixedPipelineDrawing->drawTextAtViewportCoords((vpSizeX - 5), 5.0, annotationText); } } sliceIndex -= sliceStep; } } } /* * Draw the axes labels for the montage view */ glViewport(viewport[0], viewport[1], viewport[2], viewport[3]); if (prefs->isVolumeAxesLabelsDisplayed()) { drawAxesCrosshairsOrthoAndOblique(sliceProjectionType, sliceViewPlane, sliceCoordinates, false, true); } } /** * Draw a slice for either projection mode (oblique, orthogonal) * * @param sliceDrawingType * Type of slice drawing (montage, single) * @param sliceProjectionType * Type of projection for the slice drawing (oblique, orthogonal) * @param sliceViewPlane * The plane for slice drawing. * @param sliceCoordinates * Coordinates of the selected slice. * @param viewport * The viewport (region of graphics area) for drawing slices. */ void BrainOpenGLVolumeObliqueSliceDrawing::drawVolumeSliceViewProjection(const VolumeSliceDrawingTypeEnum::Enum sliceDrawingType, const VolumeSliceProjectionTypeEnum::Enum sliceProjectionType, const VolumeSliceViewPlaneEnum::Enum sliceViewPlane, const float sliceCoordinates[3], const int32_t viewport[4]) { bool twoDimSliceViewFlag = false; if (m_modelVolume != NULL) { twoDimSliceViewFlag = true; } else if (m_modelWholeBrain != NULL) { /* nothing */ } else { CaretAssertMessage(0, "Invalid model type."); } if (twoDimSliceViewFlag) { glMatrixMode(GL_MODELVIEW); glLoadIdentity(); glViewport(viewport[0], viewport[1], viewport[2], viewport[3]); /* * Set the orthographic projection to fit the slice axis */ setOrthographicProjection(sliceViewPlane, viewport); } /* * Create the plane equation for the slice */ Plane slicePlane; createSlicePlaneEquation(sliceProjectionType, sliceViewPlane, sliceCoordinates, slicePlane); CaretAssert(slicePlane.isValidPlane()); if (slicePlane.isValidPlane() == false) { return; } if (twoDimSliceViewFlag) { /* * Set the viewing transformation (camera position) */ setVolumeSliceViewingAndModelingTransformations(sliceProjectionType, sliceViewPlane, slicePlane, sliceCoordinates); } SelectionItemVoxel* voxelID = m_brain->getSelectionManager()->getVoxelIdentification(); SelectionItemVoxelEditing* voxelEditingID = m_brain->getSelectionManager()->getVoxelEditingIdentification(); m_fixedPipelineDrawing->applyClippingPlanes(BrainOpenGLFixedPipeline::CLIPPING_DATA_TYPE_VOLUME, StructureEnum::ALL); /* * Check for a 'selection' type mode */ bool drawVolumeSlicesFlag = true; m_identificationModeFlag = false; switch (m_fixedPipelineDrawing->mode) { case BrainOpenGLFixedPipeline::MODE_DRAWING: break; case BrainOpenGLFixedPipeline::MODE_IDENTIFICATION: if (voxelID->isEnabledForSelection() || voxelEditingID->isEnabledForSelection()) { m_identificationModeFlag = true; glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); } else { /* * Don't return. Allow other items (such as annotations) to be drawn. */ drawVolumeSlicesFlag = false; } break; case BrainOpenGLFixedPipeline::MODE_PROJECTION: return; break; } resetIdentification(); GLboolean cullFaceOn = glIsEnabled(GL_CULL_FACE); if (drawVolumeSlicesFlag) { /* * Disable culling so that both sides of the triangles/quads are drawn. */ glDisable(GL_CULL_FACE); switch (sliceProjectionType) { case VolumeSliceProjectionTypeEnum::VOLUME_SLICE_PROJECTION_ORTHOGONAL: if (m_modelVolume != NULL) { const bool cullingFlag = true; if (cullingFlag) { drawOrthogonalSliceWithCulling(sliceViewPlane, sliceCoordinates, slicePlane); } else { drawOrthogonalSlice(sliceViewPlane, sliceCoordinates, slicePlane); } } else if (m_modelWholeBrain != NULL) { drawOrthogonalSlice(sliceViewPlane, sliceCoordinates, slicePlane); } break; case VolumeSliceProjectionTypeEnum::VOLUME_SLICE_PROJECTION_OBLIQUE: { /* * Create the oblique slice transformation matrix */ Matrix4x4 obliqueTransformationMatrix; createObliqueTransformationMatrix(sliceCoordinates, obliqueTransformationMatrix); drawObliqueSlice(sliceViewPlane, obliqueTransformationMatrix, slicePlane); } break; } /* * Process selection */ if (m_identificationModeFlag) { processIdentification(); } } if ( ! m_identificationModeFlag) { if (slicePlane.isValidPlane()) { drawLayers(sliceDrawingType, sliceProjectionType, sliceViewPlane, slicePlane, sliceCoordinates); } } /* * Draw model space annotaitons on the volume slice */ float sliceThickness = 1.0; if ( ! m_volumeDrawInfo.empty()) { if (m_volumeDrawInfo[0].volumeFile != NULL) { float spaceX = 0.0, spaceY = 0.0, spaceZ = 0.0; m_volumeDrawInfo[0].volumeFile->getVoxelSpacing(spaceX, spaceY, spaceZ); switch (sliceViewPlane) { case VolumeSliceViewPlaneEnum::ALL: CaretAssert(0); break; case VolumeSliceViewPlaneEnum::AXIAL: sliceThickness = spaceZ; break; case VolumeSliceViewPlaneEnum::CORONAL: sliceThickness = spaceY; break; case VolumeSliceViewPlaneEnum::PARASAGITTAL: sliceThickness = spaceX; break; } } } 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, sliceThickness); m_fixedPipelineDrawing->disableClippingPlanes(); if (cullFaceOn) { glEnable(GL_CULL_FACE); } } /** * Draw an oblique slice. * * @param sliceViewPlane * The plane for slice drawing. * @param transformationMatrix * The for oblique viewing. * @param plane * Plane equation for the selected slice. */ void BrainOpenGLVolumeObliqueSliceDrawing::drawObliqueSlice(const VolumeSliceViewPlaneEnum::Enum sliceViewPlane, Matrix4x4& transformationMatrix, const Plane& plane) { /* * When performing voxel identification for editing voxels, * we need to draw EVERY voxel since the user may click * regions where the voxels are "off". */ float voxelEditingValue = 1.0; VolumeFile* voxelEditingVolumeFile = NULL; bool volumeEditingDrawAllVoxelsFlag = false; if (m_identificationModeFlag) { SelectionItemVoxelEditing* voxelEditID = m_brain->getSelectionManager()->getVoxelEditingIdentification(); if (voxelEditID->isEnabledForSelection()) { voxelEditingVolumeFile = voxelEditID->getVolumeFileForEditing(); if (voxelEditingVolumeFile != NULL) { volumeEditingDrawAllVoxelsFlag = true; if (voxelEditingVolumeFile->isMappedWithLabelTable()) { if (voxelEditingVolumeFile->getNumberOfMaps() > 0) { voxelEditingValue = voxelEditingVolumeFile->getMapLabelTable(0)->getUnassignedLabelKey(); } } } } } const bool obliqueSliceModeThreeDimFlag = false; float m[16]; glGetFloatv(GL_MODELVIEW_MATRIX, m); Matrix4x4 tm; tm.setMatrixFromOpenGL(m); const int32_t numVolumes = static_cast(m_volumeDrawInfo.size()); /* * Get the maximum bounds of the voxels from all slices * and the smallest voxel spacing */ float voxelBounds[6]; float voxelSpacing[3]; if (false == getVoxelCoordinateBoundsAndSpacing(voxelBounds, voxelSpacing)) { return; } float voxelSize = std::min(voxelSpacing[0], std::min(voxelSpacing[1], voxelSpacing[2])); /* * Use a larger voxel size for the 3D view in volume slice viewing * since it draws all three slices and this takes time */ if (obliqueSliceModeThreeDimFlag) { voxelSize *= 3.0; } /* * Look at point is in center of volume */ float translation[3]; m_browserTabContent->getTranslation(translation); float viewOffsetX = 0.0; float viewOffsetY = 0.0; switch (sliceViewPlane) { case VolumeSliceViewPlaneEnum::ALL: CaretAssert(0); break; case VolumeSliceViewPlaneEnum::AXIAL: viewOffsetX = (m_lookAtCenter[0] + translation[0]); viewOffsetY = (m_lookAtCenter[1] + translation[1]); break; case VolumeSliceViewPlaneEnum::CORONAL: viewOffsetX = (m_lookAtCenter[0] + translation[0]); viewOffsetY = (m_lookAtCenter[2] + translation[2]); break; case VolumeSliceViewPlaneEnum::PARASAGITTAL: viewOffsetX = (m_lookAtCenter[1] + translation[1]); viewOffsetY = (m_lookAtCenter[2] + translation[2]); break; } float minScreenX = m_orthographicBounds[0] - viewOffsetX; float maxScreenX = m_orthographicBounds[1] - viewOffsetX; float minScreenY = m_orthographicBounds[2] - viewOffsetY; float maxScreenY = m_orthographicBounds[3] - viewOffsetY; /* * Get origin voxel IJK */ const float zeroXYZ[3] = { 0.0, 0.0, 0.0 }; int64_t originIJK[3]; m_volumeDrawInfo[0].volumeFile->enclosingVoxel(zeroXYZ[0], zeroXYZ[1], zeroXYZ[2], originIJK[0], originIJK[1], originIJK[2]); /* * Get XYZ center of origin Voxel */ float originVoxelXYZ[3]; m_volumeDrawInfo[0].volumeFile->indexToSpace(originIJK, originVoxelXYZ); float actualOrigin[3]; m_volumeDrawInfo[0].volumeFile->indexToSpace(originIJK, actualOrigin); float screenOffsetX = 0.0; float screenOffsetY = 0.0; float originOffsetX = 0.0; float originOffsetY = 0.0; switch (sliceViewPlane) { case VolumeSliceViewPlaneEnum::ALL: CaretAssert(0); break; case VolumeSliceViewPlaneEnum::AXIAL: screenOffsetX = m_lookAtCenter[0]; screenOffsetY = m_lookAtCenter[1]; originOffsetX = actualOrigin[0]; originOffsetY = actualOrigin[1]; break; case VolumeSliceViewPlaneEnum::CORONAL: screenOffsetX = m_lookAtCenter[0]; screenOffsetY = m_lookAtCenter[2]; originOffsetX = actualOrigin[0]; originOffsetY = actualOrigin[2]; break; case VolumeSliceViewPlaneEnum::PARASAGITTAL: screenOffsetX = m_lookAtCenter[1]; screenOffsetY = m_lookAtCenter[2]; originOffsetX = actualOrigin[1]; originOffsetY = actualOrigin[2]; break; } const int32_t alignVoxelsFlag = 1; if (alignVoxelsFlag == 1) { /* * Adjust for when selected slices are not at the origin */ const float xOffset = MathFunctions::remainder(screenOffsetX, voxelSize); const float yOffset = MathFunctions::remainder(screenOffsetY, voxelSize); originOffsetX -= xOffset; originOffsetY -= yOffset; const int64_t numVoxelsToLeft = static_cast(MathFunctions::round(minScreenX + originOffsetX) / voxelSize); const int64_t numVoxelsToRight = static_cast(MathFunctions::round(maxScreenX + originOffsetX) / voxelSize); const int64_t numVoxelsToBottom = static_cast(MathFunctions::round(minScreenY + originOffsetY) / voxelSize); const int64_t numVoxelsToTop = static_cast(MathFunctions::round(maxScreenY + originOffsetY)/ voxelSize); const float halfVoxel = voxelSize / 2.0; const float firstVoxelCenterX = (numVoxelsToLeft * voxelSize) + originOffsetX; const float lastVoxelCenterX = (numVoxelsToRight * voxelSize) + originOffsetX; const float firstVoxelCenterY = (numVoxelsToBottom * voxelSize) + originOffsetY; const float lastVoxelCenterY = (numVoxelsToTop * voxelSize) + originOffsetY; float newMinScreenX = firstVoxelCenterX - halfVoxel; float newMaxScreenX = lastVoxelCenterX + halfVoxel; float newMinScreenY = firstVoxelCenterY - halfVoxel; float newMaxScreenY = lastVoxelCenterY + halfVoxel; if (debugFlag) { const AString msg2 = ("Origin Voxel Coordinate: (" + AString::fromNumbers(actualOrigin, 3, ",") + "\n Oblique Screen X: (" + AString::number(minScreenX) + "," + AString::number(maxScreenX) + ") Y: (" + AString::number(minScreenY) + "," + AString::number(maxScreenY) + ")\nNew X: (" + AString::number(newMinScreenX) + "," + AString::number(newMaxScreenX) + ") Y: (" + AString::number(newMinScreenY) + "," + AString::number(newMaxScreenY) + ") Diff: (" + AString::number((newMaxScreenX - newMinScreenX) / voxelSize) + "," + AString::number((newMaxScreenY - newMinScreenY) / voxelSize) + ")"); std::cout << qPrintable(msg2) << std::endl; } minScreenX = newMinScreenX; maxScreenX = newMaxScreenX; minScreenY = newMinScreenY; maxScreenY = newMaxScreenY; } if (alignVoxelsFlag == 2) { const float quarterVoxelSize = voxelSize / 4.0; float newMinScreenX = (static_cast(minScreenX / voxelSize) * voxelSize) + quarterVoxelSize; float newMaxScreenX = (static_cast(maxScreenX / voxelSize) * voxelSize) - quarterVoxelSize; float newMinScreenY = (static_cast(minScreenY / voxelSize) * voxelSize) + quarterVoxelSize; float newMaxScreenY = (static_cast(maxScreenY / voxelSize) * voxelSize) - quarterVoxelSize; minScreenX = newMinScreenX; maxScreenX = newMaxScreenX; minScreenY = newMinScreenY; maxScreenY = newMaxScreenY; } /* * Set the corners of the screen for the respective view */ float bottomLeft[3]; float bottomRight[3]; float topRight[3]; float topLeft[3]; switch (sliceViewPlane) { case VolumeSliceViewPlaneEnum::ALL: CaretAssert(0); break; case VolumeSliceViewPlaneEnum::AXIAL: bottomLeft[0] = minScreenX; bottomLeft[1] = minScreenY; bottomLeft[2] = 0.0; bottomRight[0] = maxScreenX; bottomRight[1] = minScreenY; bottomRight[2] = 0.0; topRight[0] = maxScreenX; topRight[1] = maxScreenY; topRight[2] = 0.0; topLeft[0] = minScreenX; topLeft[1] = maxScreenY; topLeft[2] = 0.0; break; case VolumeSliceViewPlaneEnum::CORONAL: bottomLeft[0] = minScreenX; bottomLeft[1] = 0.0; bottomLeft[2] = minScreenY; bottomRight[0] = maxScreenX; bottomRight[1] = 0.0; bottomRight[2] = minScreenY; topRight[0] = maxScreenX; topRight[1] = 0.0; topRight[2] = maxScreenY; topLeft[0] = minScreenX; topLeft[1] = 0.0; topLeft[2] = maxScreenY; break; case VolumeSliceViewPlaneEnum::PARASAGITTAL: bottomLeft[0] = 0.0; bottomLeft[1] = minScreenX; bottomLeft[2] = minScreenY; bottomRight[0] = 0.0; bottomRight[1] = maxScreenX; bottomRight[2] = minScreenY; topRight[0] = 0.0; topRight[1] = maxScreenX; topRight[2] = maxScreenY; topLeft[0] = 0.0; topLeft[1] = minScreenX; topLeft[2] = maxScreenY; break; } /* * Transform the corners of the screen into model coordinates */ transformationMatrix.multiplyPoint3(bottomLeft); transformationMatrix.multiplyPoint3(bottomRight); transformationMatrix.multiplyPoint3(topRight); transformationMatrix.multiplyPoint3(topLeft); if (debugFlag) { const double bottomDist = MathFunctions::distance3D(bottomLeft, bottomRight); const double topDist = MathFunctions::distance3D(topLeft, topRight); const double bottomVoxels = bottomDist / voxelSize; const double topVoxels = topDist / voxelSize; const AString msg = ("Bottom Dist: " + AString::number(bottomDist) + " voxel size: " + AString::number(bottomVoxels) + " Top Dist: " + AString::number(bottomDist) + " voxel size: " + AString::number(topVoxels)); std::cout << qPrintable(msg) << std::endl; } if (debugFlag) { m_fixedPipelineDrawing->setLineWidth(3.0); glColor3f(1.0, 0.0, 0.0); glBegin(GL_LINE_LOOP); glVertex3fv(bottomLeft); glVertex3fv(bottomRight); glVertex3fv(topRight); glVertex3fv(topLeft); glEnd(); } /* * Unit vector and distance in model coords along left side of screen */ double bottomLeftToTopLeftUnitVector[3] = { topLeft[0] - bottomLeft[0], topLeft[1] - bottomLeft[1], topLeft[2] - bottomLeft[2], }; MathFunctions::normalizeVector(bottomLeftToTopLeftUnitVector); const double bottomLeftToTopLeftDistance = MathFunctions::distance3D(bottomLeft, topLeft); /* * Unit vector and distance in model coords along right side of screen */ double bottomRightToTopRightUnitVector[3] = { topRight[0] - bottomRight[0], topRight[1] - bottomRight[1], topRight[2] - bottomRight[2] }; MathFunctions::normalizeVector(bottomRightToTopRightUnitVector); const double bottomRightToTopRightDistance = MathFunctions::distance3D(bottomRight, topRight); /* * For fastest coloring, need to color data values as a group */ std::vector volumeSlices; for (int32_t i = 0; i < numVolumes; i++) { volumeSlices.push_back(VolumeSlice(m_volumeDrawInfo[i].volumeFile, m_volumeDrawInfo[i].mapIndex)); } bool showFirstVoxelCoordFlag = debugFlag; /* * Track voxels that will be drawn */ std::vector voxelsToDraw; if ((bottomLeftToTopLeftDistance > 0) && (bottomRightToTopRightDistance > 0)) { const double bottomLeftToTopLeftStep = voxelSize; const double numLeftSteps = (bottomLeftToTopLeftDistance / bottomLeftToTopLeftStep); const double bottomRightToTopRightStep = (bottomRightToTopRightDistance / numLeftSteps); const double dtVertical = bottomLeftToTopLeftStep / bottomLeftToTopLeftDistance; /* * Voxels are drawn in rows, left to right, across the screen, * starting at the bottom. */ double leftEdgeBottomCoord[3]; double leftEdgeTopCoord[3]; double rightEdgeBottomCoord[3]; double rightEdgeTopCoord[3]; for (double tVertical = 0.0, dLeft = 0.0, dRight = 0.0; tVertical < 1.0; tVertical += dtVertical, dLeft += bottomLeftToTopLeftStep, dRight += bottomRightToTopRightStep) { /* * Coordinate on left edge at BOTTOM of current row */ leftEdgeBottomCoord[0] = bottomLeft[0] + (dLeft * bottomLeftToTopLeftUnitVector[0]); leftEdgeBottomCoord[1] = bottomLeft[1] + (dLeft * bottomLeftToTopLeftUnitVector[1]); leftEdgeBottomCoord[2] = bottomLeft[2] + (dLeft * bottomLeftToTopLeftUnitVector[2]); /* * Coordinate on right edge at BOTTOM of current row */ rightEdgeBottomCoord[0] = bottomRight[0] + (dRight * bottomRightToTopRightUnitVector[0]); rightEdgeBottomCoord[1] = bottomRight[1] + (dRight * bottomRightToTopRightUnitVector[1]); rightEdgeBottomCoord[2] = bottomRight[2] + (dRight * bottomRightToTopRightUnitVector[2]); /* * Coordinate on left edge at TOP of current row */ leftEdgeTopCoord[0] = bottomLeft[0] + ((dLeft + bottomLeftToTopLeftStep) * bottomLeftToTopLeftUnitVector[0]); leftEdgeTopCoord[1] = bottomLeft[1] + ((dLeft + bottomLeftToTopLeftStep) * bottomLeftToTopLeftUnitVector[1]); leftEdgeTopCoord[2] = bottomLeft[2] + ((dLeft + bottomLeftToTopLeftStep) * bottomLeftToTopLeftUnitVector[2]); /* * Coordinate on right edge at TOP of current row */ rightEdgeTopCoord[0] = bottomRight[0] + ((dRight + bottomRightToTopRightStep) * bottomRightToTopRightUnitVector[0]); rightEdgeTopCoord[1] = bottomRight[1] + ((dRight + bottomRightToTopRightStep) * bottomRightToTopRightUnitVector[1]); rightEdgeTopCoord[2] = bottomRight[2] + ((dRight + bottomRightToTopRightStep) * bottomRightToTopRightUnitVector[2]); /* * Determine change in XYZ per voxel along the bottom of the current row */ const double bottomVoxelEdgeDistance = MathFunctions::distance3D(leftEdgeBottomCoord, rightEdgeBottomCoord); double bottomEdgeUnitVector[3]; MathFunctions::createUnitVector(leftEdgeBottomCoord, rightEdgeBottomCoord, bottomEdgeUnitVector); const double numVoxelsInRowFloat = bottomVoxelEdgeDistance / voxelSize; const int64_t numVoxelsInRow = MathFunctions::round(numVoxelsInRowFloat); const double bottomEdgeVoxelSize = bottomVoxelEdgeDistance / numVoxelsInRow; const double bottomVoxelEdgeDX = bottomEdgeVoxelSize * bottomEdgeUnitVector[0]; const double bottomVoxelEdgeDY = bottomEdgeVoxelSize * bottomEdgeUnitVector[1]; const double bottomVoxelEdgeDZ = bottomEdgeVoxelSize * bottomEdgeUnitVector[2]; /* * Determine change in XYZ per voxel along top of the current row */ const double topVoxelEdgeDistance = MathFunctions::distance3D(leftEdgeTopCoord, rightEdgeTopCoord); double topEdgeUnitVector[3]; MathFunctions::createUnitVector(leftEdgeTopCoord, rightEdgeTopCoord, topEdgeUnitVector); const double topEdgeVoxelSize = topVoxelEdgeDistance / numVoxelsInRow; const double topVoxelEdgeDX = topEdgeVoxelSize * topEdgeUnitVector[0]; const double topVoxelEdgeDY = topEdgeVoxelSize * topEdgeUnitVector[1]; const double topVoxelEdgeDZ = topEdgeVoxelSize * topEdgeUnitVector[2]; /* * Initialize bottom and top left coordinate of first voxel in row */ double bottomLeftVoxelCoord[3] = { leftEdgeBottomCoord[0], leftEdgeBottomCoord[1], leftEdgeBottomCoord[2] }; double topLeftVoxelCoord[3] = { leftEdgeTopCoord[0], leftEdgeTopCoord[1], leftEdgeTopCoord[2] }; const bool useInterpolatedVoxel = true; /* * Draw the voxels in the row */ for (int64_t i = 0; i < numVoxelsInRow; i++) { /* * Top right corner of voxel */ const double topRightVoxelCoord[3] = { topLeftVoxelCoord[0] + topVoxelEdgeDX, topLeftVoxelCoord[1] + topVoxelEdgeDY, topLeftVoxelCoord[2] + topVoxelEdgeDZ }; const float voxelCenter[3] = { (bottomLeftVoxelCoord[0] + topRightVoxelCoord[0]) * 0.5, (bottomLeftVoxelCoord[1] + topRightVoxelCoord[1]) * 0.5, (bottomLeftVoxelCoord[2] + topRightVoxelCoord[2]) * 0.5 }; bool printOriginVoxelInfo = false; if (debugFlag) { switch (sliceViewPlane) { case VolumeSliceViewPlaneEnum::ALL: break; case VolumeSliceViewPlaneEnum::AXIAL: if (showFirstVoxelCoordFlag) { const float dist = voxelCenter[0] - actualOrigin[0]; const AString msg = ("First Voxel Center: " + AString::fromNumbers(voxelCenter, 3, ",") + " Dist from origin voxel in X: " + AString::number(dist) + " Number of voxels between: " + AString::number(dist / voxelSize)); std::cout << qPrintable(msg) << std::endl; showFirstVoxelCoordFlag = false; } if ((bottomLeftVoxelCoord[0] < actualOrigin[0]) && (topRightVoxelCoord[0] > actualOrigin[0])) { if ((bottomLeftVoxelCoord[1] < actualOrigin[1]) && (topRightVoxelCoord[1] > actualOrigin[1])) { printOriginVoxelInfo = true; } } break; case VolumeSliceViewPlaneEnum::CORONAL: if (showFirstVoxelCoordFlag) { const float dist = voxelCenter[0] - actualOrigin[0]; const AString msg = ("First Voxel Center: " + AString::fromNumbers(voxelCenter, 3, ",") + " Dist from origin voxel in X: " + AString::number(dist) + " Number of voxels between: " + AString::number(dist / voxelSize)); std::cout << qPrintable(msg) << std::endl; showFirstVoxelCoordFlag = false; } if ((bottomLeftVoxelCoord[0] < actualOrigin[0]) && (topRightVoxelCoord[0] > actualOrigin[0])) { if ((bottomLeftVoxelCoord[2] < actualOrigin[2]) && (topRightVoxelCoord[2] > actualOrigin[2])) { printOriginVoxelInfo = true; } } break; case VolumeSliceViewPlaneEnum::PARASAGITTAL: if (showFirstVoxelCoordFlag) { const float dist = voxelCenter[1] - actualOrigin[1]; const AString msg = ("First Voxel Center: " + AString::fromNumbers(voxelCenter, 3, ",") + " Dist from origin voxel in Y: " + AString::number(dist) + " Number of voxels between: " + AString::number(dist / voxelSize)); std::cout << qPrintable(msg) << std::endl; showFirstVoxelCoordFlag = false; } if ((bottomLeftVoxelCoord[1] < actualOrigin[1]) && (topRightVoxelCoord[1] > actualOrigin[1])) { if ((bottomLeftVoxelCoord[2] < actualOrigin[2]) && (topRightVoxelCoord[2] > actualOrigin[2])) { printOriginVoxelInfo = true; } } break; } } if (printOriginVoxelInfo) { const AString msg = ("Origin voxel center when drawn is " + AString::fromNumbers(voxelCenter, 3, ",") + " but should be " + AString::fromNumbers(actualOrigin, 3, ",") + " Voxel Corners: (" + AString::fromNumbers(bottomLeftVoxelCoord, 3, ",") + ") (" + AString::fromNumbers(topRightVoxelCoord, 3, ",") + ")"); std::cout << qPrintable(msg) << std::endl; } /* * Loop through the volumes selected as overlays. */ VoxelToDraw* voxelDrawingInfo = NULL; for (int32_t iVol = 0; iVol < numVolumes; iVol++) { const BrainOpenGLFixedPipeline::VolumeDrawInfo& vdi = m_volumeDrawInfo[iVol]; const VolumeMappableInterface* volInter = vdi.volumeFile; const VolumeFile* volumeFile = volumeSlices[iVol].m_volumeFile; float values[4] = { 0.0, 0.0, 0.0, 0.0 }; bool valueValidFlag = false; bool isPaletteMappedVolumeFile = false; bool isRgbVolumeFile = false; bool isRgbaVolumeFile = false; if (volumeFile != NULL) { if (volumeFile->isMappedWithPalette()) { isPaletteMappedVolumeFile = true; } else if (volumeFile->isMappedWithRGBA()) { if (volumeFile->getNumberOfComponents() == 4) { isRgbaVolumeFile = true; } else if (volumeFile->getNumberOfComponents() == 3) { isRgbVolumeFile = true; } } } const CiftiMappableDataFile* ciftiMappableFile = volumeSlices[iVol].m_ciftiMappableDataFile; if (useInterpolatedVoxel && isPaletteMappedVolumeFile) { values[0] = volumeFile->interpolateValue(voxelCenter, VolumeFile::CUBIC, &valueValidFlag, vdi.mapIndex); } else if (ciftiMappableFile != NULL) { const int64_t voxelOffset = ciftiMappableFile->getMapDataOffsetForVoxelAtCoordinate(voxelCenter, vdi.mapIndex); if (voxelOffset >= 0) { CaretAssertVectorIndex(m_ciftiMappableFileData, iVol); const std::vector& data = m_ciftiMappableFileData[iVol]; CaretAssertVectorIndex(data, voxelOffset); values[0] = data[voxelOffset]; valueValidFlag = true; } } else if (isRgbVolumeFile || isRgbaVolumeFile) { values[0] = volInter->getVoxelValue(voxelCenter, &valueValidFlag, vdi.mapIndex, 0); values[1] = volInter->getVoxelValue(voxelCenter, &valueValidFlag, vdi.mapIndex, 1); values[2] = volInter->getVoxelValue(voxelCenter, &valueValidFlag, vdi.mapIndex, 2); if (isRgbaVolumeFile) { values[3] = volInter->getVoxelValue(voxelCenter, &valueValidFlag, vdi.mapIndex, 3); } else { values[3] = 1.0; } } else { values[0] = volInter->getVoxelValue(voxelCenter, &valueValidFlag, vdi.mapIndex); } /* * Need to draw all voxels when editing */ if (volumeEditingDrawAllVoxelsFlag) { if (! valueValidFlag) { if (volumeFile != NULL) { if (volumeFile == voxelEditingVolumeFile) { values[0] = voxelEditingValue; valueValidFlag = true; } } } } if (valueValidFlag) { if (voxelDrawingInfo == NULL) { /* * Bottom right corner of voxel */ const double bottomRightVoxelCoord[3] = { bottomLeftVoxelCoord[0] + bottomVoxelEdgeDX, bottomLeftVoxelCoord[1] + bottomVoxelEdgeDY, bottomLeftVoxelCoord[2] + bottomVoxelEdgeDZ }; /* * Top right corner of voxel */ const double topRightVoxelCoord[3] = { topLeftVoxelCoord[0] + topVoxelEdgeDX, topLeftVoxelCoord[1] + topVoxelEdgeDY, topLeftVoxelCoord[2] + topVoxelEdgeDZ }; voxelDrawingInfo = new VoxelToDraw(voxelCenter, bottomLeftVoxelCoord, bottomRightVoxelCoord, topRightVoxelCoord, topLeftVoxelCoord); voxelsToDraw.push_back(voxelDrawingInfo); } const int64_t offset = ((isRgbVolumeFile|| isRgbaVolumeFile) ? volumeSlices[iVol].addValuesRGBA(values) : volumeSlices[iVol].addValue(values[0])); voxelDrawingInfo->addVolumeValue(iVol, offset); } } /* * Move to the next voxel in the row */ bottomLeftVoxelCoord[0] += bottomVoxelEdgeDX; bottomLeftVoxelCoord[1] += bottomVoxelEdgeDY; bottomLeftVoxelCoord[2] += bottomVoxelEdgeDZ; topLeftVoxelCoord[0] += topVoxelEdgeDX; topLeftVoxelCoord[1] += topVoxelEdgeDY; topLeftVoxelCoord[2] += topVoxelEdgeDZ; } } } const int32_t browserTabIndex = m_browserTabContent->getTabNumber(); const DisplayPropertiesLabels* displayPropertiesLabels = m_brain->getDisplayPropertiesLabels(); const DisplayGroupEnum::Enum displayGroup = displayPropertiesLabels->getDisplayGroupForTab(browserTabIndex); /* * Color voxel values */ for (int32_t i = 0; i < numVolumes; i++) { const int64_t numValues = static_cast(volumeSlices[i].m_values.size()); if (numValues > 0) { volumeSlices[i].allocateColors(); VolumeMappableInterface* volume = volumeSlices[i].m_volumeMappableInterface; CaretMappableDataFile* mappableFile = dynamic_cast(volume); VolumeFile* volumeFile = volumeSlices[i].m_volumeFile; const bool isRgbaFlag = ( ! volumeSlices[i].m_valuesGreen.empty()); CaretAssert(mappableFile); const int32_t mapIndex = volumeSlices[i].m_mapIndex; const float* values = &volumeSlices[i].m_values[0]; uint8_t* rgba = &volumeSlices[i].m_rgba[0]; if (volumeEditingDrawAllVoxelsFlag && (volumeFile == voxelEditingVolumeFile)) { for (int64_t i = 0; i < numValues; i++) { const int64_t i4 = i * 4; rgba[i4] = 255; rgba[i4+1] = 255; rgba[i4+2] = 255; rgba[i4+3] = 255; } } 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); } } else if (mappableFile->isMappedWithLabelTable()) { GiftiLabelTable* labelTable = mappableFile->getMapLabelTable(mapIndex); NodeAndVoxelColoring::colorIndicesWithLabelTableForDisplayGroupTab(labelTable, values, numValues, displayGroup, browserTabIndex, rgba); } else if (isRgbaFlag) { const uint8_t rgbThreshold[3] = { 5, 5, 5 }; CaretAssert(volumeSlices[i].m_values.size() == volumeSlices[i].m_valuesGreen.size()); CaretAssert(volumeSlices[i].m_values.size() == volumeSlices[i].m_valuesBlue.size()); CaretAssert(volumeSlices[i].m_values.size() == volumeSlices[i].m_valuesAlpha.size()); NodeAndVoxelColoring::colorScalarsWithRGBA(&volumeSlices[i].m_values[0], &volumeSlices[i].m_valuesGreen[0], &volumeSlices[i].m_valuesBlue[0], &volumeSlices[i].m_valuesAlpha[0], numValues, rgbThreshold, rgba); } else { CaretAssert(0); } } } const int64_t numVoxelsToDraw = static_cast(voxelsToDraw.size()); /* * quadCoords is the coordinates for all four corners of a 'quad' * that is used to draw a voxel. quadRGBA is the colors for each * voxel drawn as a 'quad'. */ std::vector quadCoordsVector; std::vector quadNormalsVector; std::vector quadRGBAsVector; /* * Reserve space to avoid reallocations */ const int64_t coordinatesPerQuad = 4; const int64_t componentsPerCoordinate = 3; const int64_t colorComponentsPerCoordinate = 4; quadCoordsVector.resize(numVoxelsToDraw * coordinatesPerQuad * componentsPerCoordinate); quadNormalsVector.resize(quadCoordsVector.size()); quadRGBAsVector.resize(numVoxelsToDraw * coordinatesPerQuad * colorComponentsPerCoordinate); int64_t coordOffset = 0; int64_t normalOffset = 0; int64_t rgbaOffset = 0; float* quadCoords = &quadCoordsVector[0]; float* quadNormals = &quadNormalsVector[0]; uint8_t* quadRGBAs = &quadRGBAsVector[0]; for (int64_t iVox = 0; iVox < numVoxelsToDraw; iVox++) { CaretAssertVectorIndex(voxelsToDraw, iVox); VoxelToDraw* vtd = voxelsToDraw[iVox]; CaretAssert(vtd); uint8_t voxelRGBA[4] = { 0, 0, 0, 0 }; const int32_t numSlicesForVoxel = static_cast(vtd->m_sliceIndices.size()); for (int32_t iSlice = 0; iSlice < numSlicesForVoxel; iSlice++) { CaretAssertVectorIndex(vtd->m_sliceIndices, iSlice); CaretAssertVectorIndex(vtd->m_sliceOffsets, iSlice); const int32_t sliceIndex = vtd->m_sliceIndices[iSlice]; const int64_t voxelOffset = vtd->m_sliceOffsets[iSlice]; 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) { 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], voxelI, voxelJ, voxelK); if (volMap->indexValid(voxelI, voxelJ, voxelK)) { float diffXYZ[3]; vtd->getDiffXYZ(diffXYZ); addVoxelToIdentification(sliceIndex, volumeSlices[sliceIndex].m_mapIndex, voxelI, voxelJ, voxelK, diffXYZ, voxelRGBA); } } } } if (voxelRGBA[3] > 0) { float sliceNormalVector[3]; plane.getNormalVector(sliceNormalVector); CaretAssertVectorIndex(quadRGBAsVector, rgbaOffset + 3); quadRGBAs[rgbaOffset] = voxelRGBA[0]; quadRGBAs[rgbaOffset+1] = voxelRGBA[1]; quadRGBAs[rgbaOffset+2] = voxelRGBA[2]; quadRGBAs[rgbaOffset+3] = voxelRGBA[3]; rgbaOffset += 4; CaretAssertVectorIndex(quadNormalsVector, normalOffset + 2); quadNormals[normalOffset] = sliceNormalVector[0]; quadNormals[normalOffset+1] = sliceNormalVector[1]; quadNormals[normalOffset+2] = sliceNormalVector[2]; normalOffset += 3; CaretAssertVectorIndex(quadRGBAsVector, rgbaOffset + 3); quadRGBAs[rgbaOffset] = voxelRGBA[0]; quadRGBAs[rgbaOffset+1] = voxelRGBA[1]; quadRGBAs[rgbaOffset+2] = voxelRGBA[2]; quadRGBAs[rgbaOffset+3] = voxelRGBA[3]; rgbaOffset += 4; CaretAssertVectorIndex(quadNormalsVector, normalOffset + 2); quadNormals[normalOffset] = sliceNormalVector[0]; quadNormals[normalOffset+1] = sliceNormalVector[1]; quadNormals[normalOffset+2] = sliceNormalVector[2]; normalOffset += 3; CaretAssertVectorIndex(quadRGBAsVector, rgbaOffset + 3); quadRGBAs[rgbaOffset] = voxelRGBA[0]; quadRGBAs[rgbaOffset+1] = voxelRGBA[1]; quadRGBAs[rgbaOffset+2] = voxelRGBA[2]; quadRGBAs[rgbaOffset+3] = voxelRGBA[3]; rgbaOffset += 4; CaretAssertVectorIndex(quadNormalsVector, normalOffset + 2); quadNormals[normalOffset] = sliceNormalVector[0]; quadNormals[normalOffset+1] = sliceNormalVector[1]; quadNormals[normalOffset+2] = sliceNormalVector[2]; normalOffset += 3; CaretAssertVectorIndex(quadRGBAsVector, rgbaOffset + 3); quadRGBAs[rgbaOffset] = voxelRGBA[0]; quadRGBAs[rgbaOffset+1] = voxelRGBA[1]; quadRGBAs[rgbaOffset+2] = voxelRGBA[2]; quadRGBAs[rgbaOffset+3] = voxelRGBA[3]; rgbaOffset += 4; CaretAssertVectorIndex(quadNormalsVector, normalOffset + 2); quadNormals[normalOffset] = sliceNormalVector[0]; quadNormals[normalOffset+1] = sliceNormalVector[1]; quadNormals[normalOffset+2] = sliceNormalVector[2]; normalOffset += 3; CaretAssertVectorIndex(quadCoordsVector, coordOffset + 11); for (int32_t iq = 0; iq < 12; iq++) { quadCoords[coordOffset + iq] = vtd->m_coordinates[iq]; } coordOffset += 12; } } quadCoordsVector.resize(coordOffset); quadNormalsVector.resize(normalOffset); quadRGBAsVector.resize(rgbaOffset); for (std::vector::iterator iter = voxelsToDraw.begin(); iter != voxelsToDraw.end(); iter++) { VoxelToDraw* vtd = *iter; delete vtd; } voxelsToDraw.clear(); if ( ! quadCoordsVector.empty()) { glPushMatrix(); BrainOpenGLPrimitiveDrawing::drawQuads(quadCoordsVector, quadNormalsVector, quadRGBAsVector); glPopMatrix(); } } /** * Draw an orthogonal slice. * * @param sliceViewPlane * The plane for slice drawing. * @param sliceCoordinates * Coordinates of the selected slice. * @param plane * Plane equation for the selected slice. */ void BrainOpenGLVolumeObliqueSliceDrawing::drawOrthogonalSlice(const VolumeSliceViewPlaneEnum::Enum sliceViewPlane, const float sliceCoordinates[3], const Plane& plane) { const int32_t browserTabIndex = m_browserTabContent->getTabNumber(); const DisplayPropertiesLabels* displayPropertiesLabels = m_brain->getDisplayPropertiesLabels(); const DisplayGroupEnum::Enum displayGroup = displayPropertiesLabels->getDisplayGroupForTab(browserTabIndex); /* * 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); /* * Flat shading voxels not interpolated */ glShadeModel(GL_FLAT); CaretAssert(plane.isValidPlane()); if (plane.isValidPlane() == false) { return; } /* * Compute coordinate of point in center of first slice */ float selectedSliceCoordinate = 0.0; float sliceNormalVector[3] = { 0.0, 0.0, 0.0 }; plane.getNormalVector(sliceNormalVector); switch (sliceViewPlane) { case VolumeSliceViewPlaneEnum::ALL: CaretAssert(0); break; case VolumeSliceViewPlaneEnum::AXIAL: selectedSliceCoordinate = sliceCoordinates[2]; break; case VolumeSliceViewPlaneEnum::CORONAL: selectedSliceCoordinate = sliceCoordinates[1]; break; case VolumeSliceViewPlaneEnum::PARASAGITTAL: selectedSliceCoordinate = sliceCoordinates[0]; break; } /* * Holds colors for voxels in the slice * Outside of loop to minimize allocations * It is faster to make one call to * NodeAndVoxelColoring::colorScalarsWithPalette() with * all voxels in the slice than it is to call it * separately for each voxel. */ std::vector sliceVoxelsRgbaVector; /* * Draw each of the volumes separately so that each * is drawn with the correct voxel slices. */ const int32_t numberOfVolumesToDraw = static_cast(m_volumeDrawInfo.size()); for (int32_t iVol = 0; iVol < numberOfVolumesToDraw; iVol++) { const BrainOpenGLFixedPipeline::VolumeDrawInfo& volInfo = m_volumeDrawInfo[iVol]; const VolumeMappableInterface* volumeFile = volInfo.volumeFile; int64_t dimI, dimJ, dimK, numMaps, numComponents; volumeFile->getDimensions(dimI, dimJ, dimK, numMaps, numComponents); const int64_t mapIndex = volInfo.mapIndex; float originX, originY, originZ; volumeFile->indexToSpace(0, 0, 0, originX, originY, originZ); float x1, y1, z1; volumeFile->indexToSpace(1, 1, 1, x1, y1, z1); const float voxelStepX = x1 - originX; const float voxelStepY = y1 - originY; const float voxelStepZ = z1 - originZ; /* * Determine index of slice being viewed for the volume */ float coordinateOnSlice[3] = { originX, originY, originZ }; switch (sliceViewPlane) { case VolumeSliceViewPlaneEnum::ALL: CaretAssert(0); break; case VolumeSliceViewPlaneEnum::AXIAL: coordinateOnSlice[2] = selectedSliceCoordinate; break; case VolumeSliceViewPlaneEnum::CORONAL: coordinateOnSlice[1] = selectedSliceCoordinate; break; case VolumeSliceViewPlaneEnum::PARASAGITTAL: coordinateOnSlice[0] = selectedSliceCoordinate; break; } int64_t sliceIndicesForCoordinateOnSlice[3]; volumeFile->enclosingVoxel(coordinateOnSlice[0], coordinateOnSlice[1], coordinateOnSlice[2], sliceIndicesForCoordinateOnSlice[0], sliceIndicesForCoordinateOnSlice[1], sliceIndicesForCoordinateOnSlice[2]); int64_t sliceIndexForDrawing = -1; int64_t numVoxelsInSlice = 0; switch (sliceViewPlane) { case VolumeSliceViewPlaneEnum::ALL: CaretAssert(0); break; case VolumeSliceViewPlaneEnum::AXIAL: sliceIndexForDrawing = sliceIndicesForCoordinateOnSlice[2]; if ((sliceIndexForDrawing < 0) || (sliceIndexForDrawing >= dimK)) { continue; } numVoxelsInSlice = dimI * dimJ; break; case VolumeSliceViewPlaneEnum::CORONAL: sliceIndexForDrawing = sliceIndicesForCoordinateOnSlice[1]; if ((sliceIndexForDrawing < 0) || (sliceIndexForDrawing >= dimJ)) { continue; } numVoxelsInSlice = dimI * dimK; break; case VolumeSliceViewPlaneEnum::PARASAGITTAL: sliceIndexForDrawing = sliceIndicesForCoordinateOnSlice[0]; if ((sliceIndexForDrawing < 0) || (sliceIndexForDrawing >= dimI)) { continue; } numVoxelsInSlice = dimJ * dimK; break; } /* * Stores RGBA values for each voxel. * Use a vector for voxel colors so no worries about memory being freed. */ const int64_t numVoxelsInSliceRGBA = numVoxelsInSlice * 4; if (numVoxelsInSliceRGBA > static_cast(sliceVoxelsRgbaVector.size())) { sliceVoxelsRgbaVector.resize(numVoxelsInSliceRGBA); } uint8_t* sliceVoxelsRGBA = &sliceVoxelsRgbaVector[0]; /* * Get colors for all voxels in the slice. */ const int64_t validVoxelCount = volumeFile->getVoxelColorsForSliceInMap(m_brain->getPaletteFile(), mapIndex, sliceViewPlane, sliceIndexForDrawing, displayGroup, browserTabIndex, sliceVoxelsRGBA); /* * Is label outline mode? */ if (m_volumeDrawInfo[iVol].mapFile->isMappedWithLabelTable()) { int64_t xdim = 0; int64_t ydim = 0; switch (sliceViewPlane) { case VolumeSliceViewPlaneEnum::ALL: CaretAssert(0); break; case VolumeSliceViewPlaneEnum::AXIAL: xdim = dimI; ydim = dimJ; break; case VolumeSliceViewPlaneEnum::CORONAL: xdim = dimI; ydim = dimK; break; case VolumeSliceViewPlaneEnum::PARASAGITTAL: xdim = dimJ; ydim = dimK; break; } LabelDrawingTypeEnum::Enum labelDrawingType = LabelDrawingTypeEnum::DRAW_FILLED; CaretColorEnum::Enum outlineColor = CaretColorEnum::BLACK; const CaretMappableDataFile* mapFile = dynamic_cast(volumeFile); if (mapFile != NULL) { if (mapFile->isMappedWithLabelTable()) { const LabelDrawingProperties* props = mapFile->getLabelDrawingProperties(); labelDrawingType = props->getDrawingType(); outlineColor = props->getOutlineColor(); } } NodeAndVoxelColoring::convertSliceColoringToOutlineMode(sliceVoxelsRGBA, labelDrawingType, outlineColor, xdim, ydim); } int64_t selectedSliceIndices[3]; volumeFile->enclosingVoxel(sliceCoordinates[0], sliceCoordinates[1], sliceCoordinates[2], selectedSliceIndices[0], selectedSliceIndices[1], selectedSliceIndices[2]); const uint8_t volumeDrawingOpacity = static_cast(volInfo.opacity * 255.0); /* * Setup for drawing the voxels in the slice. */ float startCoordinate[3] = { originX - (voxelStepX / 2.0), originY - (voxelStepY / 2.0), originZ - (voxelStepZ / 2.0) }; float rowStep[3] = { 0.0, 0.0, 0.0 }; float columnStep[3] = { 0.0, 0.0, 0.0 }; int64_t numberOfRows = 0; int64_t numberOfColumns = 0; switch (sliceViewPlane) { case VolumeSliceViewPlaneEnum::ALL: CaretAssert(0); break; case VolumeSliceViewPlaneEnum::AXIAL: startCoordinate[2] = selectedSliceCoordinate; rowStep[1] = voxelStepY; columnStep[0] = voxelStepX; numberOfRows = dimJ; numberOfColumns = dimI; break; case VolumeSliceViewPlaneEnum::CORONAL: startCoordinate[1] = selectedSliceCoordinate; rowStep[2] = voxelStepZ; columnStep[0] = voxelStepX; numberOfRows = dimK; numberOfColumns = dimI; break; case VolumeSliceViewPlaneEnum::PARASAGITTAL: startCoordinate[0] = selectedSliceCoordinate; rowStep[2] = voxelStepZ; columnStep[1] = voxelStepY; numberOfRows = dimK; numberOfColumns = dimJ; break; } if (m_modelWholeBrain != NULL) { /* * After the a slice is drawn in ALL view, some layers * (volume surface outline) may be drawn in lines. As the * view is rotated, lines will partially appear and disappear * due to the lines having the same (extremely close) depth * values as the voxel polygons. OpenGL's Polygon Offset * only works with polygons and NOT with lines or points. * So, polygon offset cannot be used to move the depth * values for the lines and points "a little closer" to * the user. Instead, polygon offset is used to push * the underlaying slices "a little bit away" from the * user. * * Resolves WB-414 */ const float inverseSliceIndex = numberOfVolumesToDraw - iVol; const float factor = inverseSliceIndex * 1.0 + 1.0; const float units = inverseSliceIndex * 1.0 + 1.0; glEnable(GL_POLYGON_OFFSET_FILL); glPolygonOffset(factor, units); } /* * Draw the voxels in the slice. */ drawOrthogonalSliceVoxels(sliceNormalVector, startCoordinate, rowStep, columnStep, numberOfColumns, numberOfRows, sliceVoxelsRgbaVector, validVoxelCount, volumeFile, iVol, mapIndex, volumeDrawingOpacity); glDisable(GL_POLYGON_OFFSET_FILL); } glDisable(GL_BLEND); glShadeModel(GL_SMOOTH); } /** * Draw an orthogonal slice with culling to avoid drawing * voxels not visible in the viewport and reduce drawing time. * * @param sliceViewPlane * The plane for slice drawing. * @param sliceCoordinates * Coordinates of the selected slice. * @param plane * Plane equation for the selected slice. */ void BrainOpenGLVolumeObliqueSliceDrawing::drawOrthogonalSliceWithCulling(const VolumeSliceViewPlaneEnum::Enum sliceViewPlane, const float sliceCoordinates[3], const Plane& plane) { const int32_t browserTabIndex = m_browserTabContent->getTabNumber(); const DisplayPropertiesLabels* displayPropertiesLabels = m_brain->getDisplayPropertiesLabels(); const DisplayGroupEnum::Enum displayGroup = displayPropertiesLabels->getDisplayGroupForTab(browserTabIndex); /* * 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); /* * Flat shading voxels not interpolated */ glShadeModel(GL_FLAT); CaretAssert(plane.isValidPlane()); if (plane.isValidPlane() == false) { return; } /* * Compute coordinate of point in center of first slice */ float selectedSliceCoordinate = 0.0; float sliceNormalVector[3] = { 0.0, 0.0, 0.0 }; plane.getNormalVector(sliceNormalVector); switch (sliceViewPlane) { case VolumeSliceViewPlaneEnum::ALL: CaretAssert(0); break; case VolumeSliceViewPlaneEnum::AXIAL: selectedSliceCoordinate = sliceCoordinates[2]; break; case VolumeSliceViewPlaneEnum::CORONAL: selectedSliceCoordinate = sliceCoordinates[1]; break; case VolumeSliceViewPlaneEnum::PARASAGITTAL: selectedSliceCoordinate = sliceCoordinates[0]; break; } /* * Holds colors for voxels in the slice * Outside of loop to minimize allocations * It is faster to make one call to * NodeAndVoxelColoring::colorScalarsWithPalette() with * all voxels in the slice than it is to call it * separately for each voxel. */ std::vector sliceVoxelsRgbaVector; /* * Draw each of the volumes separately so that each * is drawn with the correct voxel slices. */ const int32_t numberOfVolumesToDraw = static_cast(m_volumeDrawInfo.size()); for (int32_t iVol = 0; iVol < numberOfVolumesToDraw; iVol++) { const BrainOpenGLFixedPipeline::VolumeDrawInfo& volInfo = m_volumeDrawInfo[iVol]; const VolumeMappableInterface* volumeFile = volInfo.volumeFile; int64_t culledFirstVoxelIJK[3]; int64_t culledLastVoxelIJK[3]; float voxelDeltaXYZ[3]; if ( ! getVolumeDrawingViewDependentCulling(sliceViewPlane, selectedSliceCoordinate, volumeFile, culledFirstVoxelIJK, culledLastVoxelIJK, voxelDeltaXYZ)) { CaretLogSevere("BrainOpenGLVolumeObliqueSliceDrawing::getVolumeDrawingViewDependentCulling() failed."); continue; } const int64_t numVoxelsI = std::abs(culledLastVoxelIJK[0] - culledFirstVoxelIJK[0]) + 1; const int64_t numVoxelsJ = std::abs(culledLastVoxelIJK[1] - culledFirstVoxelIJK[1]) + 1; const int64_t numVoxelsK = std::abs(culledLastVoxelIJK[2] - culledFirstVoxelIJK[2]) + 1; int64_t dimIJK[3], numMaps, numComponents; volumeFile->getDimensions(dimIJK[0], dimIJK[1], dimIJK[2], numMaps, numComponents); const int64_t mapIndex = volInfo.mapIndex; float firstVoxelXYZ[3]; volumeFile->indexToSpace(culledFirstVoxelIJK[0], culledFirstVoxelIJK[1], culledFirstVoxelIJK[2], firstVoxelXYZ[0], firstVoxelXYZ[1], firstVoxelXYZ[2]); const float voxelStepX = voxelDeltaXYZ[0]; const float voxelStepY = voxelDeltaXYZ[1]; const float voxelStepZ = voxelDeltaXYZ[2]; int64_t sliceIndexForDrawing = -1; int64_t numVoxelsInSlice = 0; switch (sliceViewPlane) { case VolumeSliceViewPlaneEnum::ALL: CaretAssert(0); break; case VolumeSliceViewPlaneEnum::AXIAL: sliceIndexForDrawing = culledFirstVoxelIJK[2]; if ((sliceIndexForDrawing < 0) || (sliceIndexForDrawing >= dimIJK[2])) { continue; } numVoxelsInSlice = numVoxelsI * numVoxelsJ; break; case VolumeSliceViewPlaneEnum::CORONAL: sliceIndexForDrawing = culledFirstVoxelIJK[1]; if ((sliceIndexForDrawing < 0) || (sliceIndexForDrawing >= dimIJK[1])) { continue; } numVoxelsInSlice = numVoxelsI * numVoxelsK; break; case VolumeSliceViewPlaneEnum::PARASAGITTAL: sliceIndexForDrawing = culledFirstVoxelIJK[0]; if ((sliceIndexForDrawing < 0) || (sliceIndexForDrawing >= dimIJK[0])) { continue; } numVoxelsInSlice = numVoxelsJ * numVoxelsK; break; } /* * Stores RGBA values for each voxel. * Use a vector for voxel colors so no worries about memory being freed. */ const int64_t numVoxelsInSliceRGBA = numVoxelsInSlice * 4; if (numVoxelsInSliceRGBA != static_cast(sliceVoxelsRgbaVector.size())) { sliceVoxelsRgbaVector.resize(numVoxelsInSliceRGBA); } uint8_t* sliceVoxelsRGBA = &sliceVoxelsRgbaVector[0]; /* * Get colors for all voxels in the slice. */ const int64_t voxelCountIJK[3] = { numVoxelsI, numVoxelsJ, numVoxelsK }; const int64_t validVoxelCount = volumeFile->getVoxelColorsForSubSliceInMap(m_brain->getPaletteFile(), mapIndex, sliceViewPlane, sliceIndexForDrawing, culledFirstVoxelIJK, culledLastVoxelIJK, voxelCountIJK, displayGroup, browserTabIndex, sliceVoxelsRGBA); /* * Is label outline mode? */ if (m_volumeDrawInfo[iVol].mapFile->isMappedWithLabelTable()) { int64_t xdim = 0; int64_t ydim = 0; switch (sliceViewPlane) { case VolumeSliceViewPlaneEnum::ALL: CaretAssert(0); break; case VolumeSliceViewPlaneEnum::AXIAL: xdim = numVoxelsI; ydim = numVoxelsJ; break; case VolumeSliceViewPlaneEnum::CORONAL: xdim = numVoxelsI; ydim = numVoxelsK; break; case VolumeSliceViewPlaneEnum::PARASAGITTAL: xdim = numVoxelsJ; ydim = numVoxelsK; break; } LabelDrawingTypeEnum::Enum labelDrawingType = LabelDrawingTypeEnum::DRAW_FILLED; CaretColorEnum::Enum outlineColor = CaretColorEnum::BLACK; const CaretMappableDataFile* mapFile = dynamic_cast(volumeFile); if (mapFile != NULL) { if (mapFile->isMappedWithLabelTable()) { const LabelDrawingProperties* props = mapFile->getLabelDrawingProperties(); labelDrawingType = props->getDrawingType(); outlineColor = props->getOutlineColor(); } } NodeAndVoxelColoring::convertSliceColoringToOutlineMode(sliceVoxelsRGBA, labelDrawingType, outlineColor, xdim, ydim); } const uint8_t volumeDrawingOpacity = static_cast(volInfo.opacity * 255.0); /* * 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) }; float rowStep[3] = { 0.0, 0.0, 0.0 }; float columnStep[3] = { 0.0, 0.0, 0.0 }; int64_t numberOfRows = 0; int64_t numberOfColumns = 0; switch (sliceViewPlane) { case VolumeSliceViewPlaneEnum::ALL: CaretAssert(0); break; case VolumeSliceViewPlaneEnum::AXIAL: rowStep[1] = voxelStepY; columnStep[0] = voxelStepX; numberOfRows = numVoxelsJ; numberOfColumns = numVoxelsI; break; case VolumeSliceViewPlaneEnum::CORONAL: rowStep[2] = voxelStepZ; columnStep[0] = voxelStepX; numberOfRows = numVoxelsK; numberOfColumns = numVoxelsI; break; case VolumeSliceViewPlaneEnum::PARASAGITTAL: rowStep[2] = voxelStepZ; columnStep[1] = voxelStepY; numberOfRows = numVoxelsK; numberOfColumns = numVoxelsJ; break; } if (m_modelWholeBrain != NULL) { /* * After the a slice is drawn in ALL view, some layers * (volume surface outline) may be drawn in lines. As the * view is rotated, lines will partially appear and disappear * due to the lines having the same (extremely close) depth * values as the voxel polygons. OpenGL's Polygon Offset * only works with polygons and NOT with lines or points. * So, polygon offset cannot be used to move the depth * values for the lines and points "a little closer" to * the user. Instead, polygon offset is used to push * the underlaying slices "a little bit away" from the * user. * * Resolves WB-414 */ const float inverseSliceIndex = numberOfVolumesToDraw - iVol; const float factor = inverseSliceIndex * 1.0 + 1.0; const float units = inverseSliceIndex * 1.0 + 1.0; glEnable(GL_POLYGON_OFFSET_FILL); glPolygonOffset(factor, units); } /* * Draw the voxels in the slice. */ drawOrthogonalSliceVoxels(sliceNormalVector, startCoordinate, rowStep, columnStep, numberOfColumns, numberOfRows, sliceVoxelsRgbaVector, validVoxelCount, volumeFile, iVol, mapIndex, volumeDrawingOpacity); glDisable(GL_POLYGON_OFFSET_FILL); } glDisable(GL_BLEND); glShadeModel(GL_SMOOTH); } /** * Create the equation for the slice plane * * @param sliceProjectionType * Type of projection for the slice drawing (oblique, orthogonal) * @param sliceViewPlane * View plane that is displayed. * @param montageSliceIndex * Selected montage slice index * @param planeOut * OUTPUT plane of slice after transforms. */ void BrainOpenGLVolumeObliqueSliceDrawing::createSlicePlaneEquation(const VolumeSliceProjectionTypeEnum::Enum sliceProjectionType, const VolumeSliceViewPlaneEnum::Enum sliceViewPlane, const float sliceCoordinates[3], Plane& planeOut) { /* * Default the slice normal vector to an orthogonal view */ float sliceNormalVector[3] = { 0.0, 0.0, 0.0 }; switch (sliceViewPlane) { case VolumeSliceViewPlaneEnum::ALL: case VolumeSliceViewPlaneEnum::AXIAL: sliceNormalVector[2] = 1.0; break; case VolumeSliceViewPlaneEnum::CORONAL: sliceNormalVector[1] = -1.0; break; case VolumeSliceViewPlaneEnum::PARASAGITTAL: sliceNormalVector[0] = -1.0; break; } switch (sliceProjectionType) { break; case VolumeSliceProjectionTypeEnum::VOLUME_SLICE_PROJECTION_OBLIQUE: { /* * Transform the slice normal vector by the oblique rotation * matrix so that the normal vector points out of the slice */ const Matrix4x4 obliqueRotationMatrix = m_browserTabContent->getObliqueVolumeRotationMatrix(); obliqueRotationMatrix.multiplyPoint3(sliceNormalVector); MathFunctions::normalizeVector(sliceNormalVector); } break; case VolumeSliceProjectionTypeEnum::VOLUME_SLICE_PROJECTION_ORTHOGONAL: break; } Plane plane(sliceNormalVector, sliceCoordinates); planeOut = plane; m_lookAtCenter[0] = sliceCoordinates[0]; m_lookAtCenter[1] = sliceCoordinates[1]; m_lookAtCenter[2] = sliceCoordinates[2]; } /** * Set the volume slice viewing transformation. This sets the position and * orientation of the camera. * * @param sliceProjectionType * Type of projection for the slice drawing (oblique, orthogonal) * @param sliceViewPlane * View plane that is displayed. * @param plane * Plane equation of selected slice. * @param sliceCoordinates * Coordinates of the selected slices. */ void BrainOpenGLVolumeObliqueSliceDrawing::setVolumeSliceViewingAndModelingTransformations(const VolumeSliceProjectionTypeEnum::Enum sliceProjectionType, const VolumeSliceViewPlaneEnum::Enum sliceViewPlane, const Plane& plane, const float sliceCoordinates[3]) { /* * Initialize the modelview matrix to the identity matrix * This places the camera at the origin, pointing down the * negative-Z axis with the up vector set to (0,1,0 => * positive-Y is up). */ glMatrixMode(GL_MODELVIEW); glLoadIdentity(); const float* userTranslation = m_browserTabContent->getTranslation(); /* * Move the camera with the user's translation */ float viewTranslationX = 0.0; float viewTranslationY = 0.0; float viewTranslationZ = 0.0; switch (sliceViewPlane) { case VolumeSliceViewPlaneEnum::ALL: case VolumeSliceViewPlaneEnum::AXIAL: viewTranslationX = sliceCoordinates[0] + userTranslation[0]; viewTranslationY = sliceCoordinates[1] + userTranslation[1]; break; case VolumeSliceViewPlaneEnum::CORONAL: viewTranslationX = sliceCoordinates[0] + userTranslation[0]; viewTranslationY = sliceCoordinates[2] + userTranslation[2]; break; case VolumeSliceViewPlaneEnum::PARASAGITTAL: viewTranslationX = -(sliceCoordinates[1] + userTranslation[1]); viewTranslationY = sliceCoordinates[2] + userTranslation[2]; break; } glTranslatef(viewTranslationX, viewTranslationY, viewTranslationZ); glGetDoublev(GL_MODELVIEW_MATRIX, m_viewingMatrix); /* * Since an orthographic projection is used, the camera only needs * to be a little bit from the center along the plane's normal vector. */ double planeNormal[3]; plane.getNormalVector(planeNormal); double cameraXYZ[3] = { m_lookAtCenter[0] + planeNormal[0] * BrainOpenGLFixedPipeline::s_gluLookAtCenterFromEyeOffsetDistance, m_lookAtCenter[1] + planeNormal[1] * BrainOpenGLFixedPipeline::s_gluLookAtCenterFromEyeOffsetDistance, m_lookAtCenter[2] + planeNormal[2] * BrainOpenGLFixedPipeline::s_gluLookAtCenterFromEyeOffsetDistance, }; /* * Set the up vector which indices which way is up (screen Y) */ float up[3] = { 0.0, 0.0, 0.0 }; switch (sliceViewPlane) { case VolumeSliceViewPlaneEnum::ALL: case VolumeSliceViewPlaneEnum::AXIAL: up[1] = 1.0; break; case VolumeSliceViewPlaneEnum::CORONAL: up[2] = 1.0; break; case VolumeSliceViewPlaneEnum::PARASAGITTAL: up[2] = 1.0; break; } /* * For oblique viewing, the up vector needs to be rotated by the * oblique rotation matrix. */ switch (sliceProjectionType) { case VolumeSliceProjectionTypeEnum::VOLUME_SLICE_PROJECTION_OBLIQUE: m_browserTabContent->getObliqueVolumeRotationMatrix().multiplyPoint3(up); break; case VolumeSliceProjectionTypeEnum::VOLUME_SLICE_PROJECTION_ORTHOGONAL: break; } /* * Now set the camera to look at the selected coordinate (center) * with the camera offset a little bit from the center. * This allows the slice's voxels to be drawn in the actual coordinates. */ gluLookAt(cameraXYZ[0], cameraXYZ[1], cameraXYZ[2], m_lookAtCenter[0], m_lookAtCenter[1], m_lookAtCenter[2], up[0], up[1], up[2]); } /** * Draw the layers type data. * * @param sliceDrawingType * Type of slice drawing (montage, single) * @param sliceProjectionType * Type of projection for the slice drawing (oblique, orthogonal) * @param sliceViewPlane * View plane that is displayed. * @param slicePlane * Plane of the slice. * @param sliceCoordinates * Coordinates of the selected slices. */ void BrainOpenGLVolumeObliqueSliceDrawing::drawLayers(const VolumeSliceDrawingTypeEnum::Enum sliceDrawingType, const VolumeSliceProjectionTypeEnum::Enum sliceProjectionType, const VolumeSliceViewPlaneEnum::Enum sliceViewPlane, const Plane& slicePlane, const float sliceCoordinates[3]) { bool drawCrosshairsFlag = true; bool drawFibersFlag = true; bool drawFociFlag = true; bool drawOutlineFlag = true; if (m_modelWholeBrain != NULL) { drawCrosshairsFlag = false; drawFibersFlag = false; drawFociFlag = false; } if ( ! m_identificationModeFlag) { if (slicePlane.isValidPlane()) { /* * Disable culling so that both sides of the triangles/quads are drawn. */ GLboolean cullFaceOn = glIsEnabled(GL_CULL_FACE); glDisable(GL_CULL_FACE); glPushMatrix(); GLboolean depthBufferEnabled = false; glGetBooleanv(GL_DEPTH_TEST, &depthBufferEnabled); /* * Use some polygon offset that will adjust the depth values of the * layers so that the layers depth values place the layers in front of * the volume slice. */ glEnable(GL_POLYGON_OFFSET_FILL); glPolygonOffset(0.0, 1.0); if (drawOutlineFlag) { drawSurfaceOutline(slicePlane); } if (drawFibersFlag) { glDisable(GL_DEPTH_TEST); m_fixedPipelineDrawing->drawFiberOrientations(&slicePlane, StructureEnum::ALL); m_fixedPipelineDrawing->drawFiberTrajectories(&slicePlane, StructureEnum::ALL); if (depthBufferEnabled) { glEnable(GL_DEPTH_TEST); } else { glDisable(GL_DEPTH_TEST); } } if (drawFociFlag) { glDisable(GL_DEPTH_TEST); drawVolumeSliceFoci(slicePlane); if (depthBufferEnabled) { glEnable(GL_DEPTH_TEST); } else { glDisable(GL_DEPTH_TEST); } } glDisable(GL_POLYGON_OFFSET_FILL); if (drawCrosshairsFlag) { glPushMatrix(); drawAxesCrosshairs(sliceProjectionType, sliceDrawingType, sliceViewPlane, sliceCoordinates); glPopMatrix(); if (depthBufferEnabled) { glEnable(GL_DEPTH_TEST); } else { glDisable(GL_DEPTH_TEST); } } glPopMatrix(); if (cullFaceOn) { glEnable(GL_CULL_FACE); } } } } /** * 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. * * @param plane * Plane of the volume slice on which surface outlines are drawn. */ void BrainOpenGLVolumeObliqueSliceDrawing::drawVolumeSliceFoci(const Plane& plane) { SelectionItemFocusVolume* idFocus = m_brain->getSelectionManager()->getVolumeFocusIdentification(); /* * Check for a 'selection' type mode */ bool isSelect = false; switch (m_fixedPipelineDrawing->mode) { case BrainOpenGLFixedPipeline::MODE_DRAWING: break; case BrainOpenGLFixedPipeline::MODE_IDENTIFICATION: if (idFocus->isEnabledForSelection()) { isSelect = true; glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); } else { return; } break; case BrainOpenGLFixedPipeline::MODE_PROJECTION: return; break; } VolumeMappableInterface* underlayVolume = m_volumeDrawInfo[0].volumeFile; float minVoxelSpacing; float maxVoxelSpacing; if ( ! getMinMaxVoxelSpacing(underlayVolume, minVoxelSpacing, maxVoxelSpacing)) { return; } const float sliceThickness = maxVoxelSpacing; const float halfSliceThickness = sliceThickness * 0.5; const DisplayPropertiesFoci* fociDisplayProperties = m_brain->getDisplayPropertiesFoci(); const DisplayGroupEnum::Enum displayGroup = fociDisplayProperties->getDisplayGroupForTab(m_fixedPipelineDrawing->windowTabIndex); if (fociDisplayProperties->isDisplayed(displayGroup, m_fixedPipelineDrawing->windowTabIndex) == false) { return; } const float focusDiameter = fociDisplayProperties->getFociSize(displayGroup, m_fixedPipelineDrawing->windowTabIndex); const FeatureColoringTypeEnum::Enum fociColoringType = fociDisplayProperties->getColoringType(displayGroup, m_fixedPipelineDrawing->windowTabIndex); const CaretColorEnum::Enum caretColor = fociDisplayProperties->getStandardColorType(displayGroup, m_fixedPipelineDrawing->windowTabIndex); float caretColorRGBA[4]; CaretColorEnum::toRGBFloat(caretColor, caretColorRGBA); bool drawAsSpheres = false; switch (fociDisplayProperties->getDrawingType(displayGroup, m_fixedPipelineDrawing->windowTabIndex)) { case FociDrawingTypeEnum::DRAW_AS_SPHERES: drawAsSpheres = true; break; case FociDrawingTypeEnum::DRAW_AS_SQUARES: break; } /* * Process each foci file */ const int32_t numberOfFociFiles = m_brain->getNumberOfFociFiles(); for (int32_t iFile = 0; iFile < numberOfFociFiles; iFile++) { FociFile* fociFile = m_brain->getFociFile(iFile); const GroupAndNameHierarchyModel* classAndNameSelection = fociFile->getGroupAndNameHierarchyModel(); if (classAndNameSelection->isSelected(displayGroup, m_fixedPipelineDrawing->windowTabIndex) == false) { continue; } const GiftiLabelTable* classColorTable = fociFile->getClassColorTable(); const GiftiLabelTable* nameColorTable = fociFile->getNameColorTable(); const int32_t numFoci = fociFile->getNumberOfFoci(); for (int32_t j = 0; j < numFoci; j++) { Focus* focus = fociFile->getFocus(j); const GroupAndNameHierarchyItem* groupNameItem = focus->getGroupNameSelectionItem(); if (groupNameItem != NULL) { if (groupNameItem->isSelected(displayGroup, m_fixedPipelineDrawing->windowTabIndex) == false) { continue; } } float rgba[4] = { 0.0, 0.0, 0.0, 1.0 }; switch (fociColoringType) { case FeatureColoringTypeEnum::FEATURE_COLORING_TYPE_CLASS: if (focus->isClassRgbaValid() == false) { const GiftiLabel* colorLabel = classColorTable->getLabelBestMatching(focus->getClassName()); if (colorLabel != NULL) { colorLabel->getColor(rgba); focus->setClassRgba(rgba); } else { focus->setClassRgba(rgba); } } focus->getClassRgba(rgba); break; case FeatureColoringTypeEnum::FEATURE_COLORING_TYPE_STANDARD_COLOR: rgba[0] = caretColorRGBA[0]; rgba[1] = caretColorRGBA[1]; rgba[2] = caretColorRGBA[2]; rgba[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); } } focus->getNameRgba(rgba); break; } const int32_t numProjections = focus->getNumberOfProjections(); for (int32_t k = 0; k < numProjections; k++) { const SurfaceProjectedItem* spi = focus->getProjection(k); if (spi->isVolumeXYZValid()) { float xyz[3]; spi->getVolumeXYZ(xyz); bool drawIt = false; if (plane.absoluteDistanceToPlane(xyz) < halfSliceThickness) { drawIt = true; } if (drawIt) { glPushMatrix(); glTranslatef(xyz[0], xyz[1], xyz[2]); if (isSelect) { uint8_t idRGBA[4]; m_fixedPipelineDrawing->colorIdentification->addItem(idRGBA, SelectionItemDataTypeEnum::FOCUS_VOLUME, iFile, // file index j, // focus index k);// projection index idRGBA[3] = 255; if (drawAsSpheres) { m_fixedPipelineDrawing->drawSphereWithDiameter(idRGBA, focusDiameter); } else { glColor4ubv(idRGBA); drawSquare(focusDiameter); } } else { if (drawAsSpheres) { m_fixedPipelineDrawing->drawSphereWithDiameter(rgba, focusDiameter); } else { glColor3fv(rgba); drawSquare(focusDiameter); } } glPopMatrix(); } } } } } if (isSelect) { int32_t fociFileIndex = -1; int32_t focusIndex = -1; int32_t focusProjectionIndex = -1; float depth = -1.0; m_fixedPipelineDrawing->getIndexFromColorSelection(SelectionItemDataTypeEnum::FOCUS_VOLUME, m_fixedPipelineDrawing->mouseX, m_fixedPipelineDrawing->mouseY, fociFileIndex, focusIndex, focusProjectionIndex, depth); if (fociFileIndex >= 0) { if (idFocus->isOtherScreenDepthCloserToViewer(depth)) { Focus* focus = m_brain->getFociFile(fociFileIndex)->getFocus(focusIndex); idFocus->setBrain(m_brain); idFocus->setFocus(focus); idFocus->setFociFile(m_brain->getFociFile(fociFileIndex)); idFocus->setFocusIndex(focusIndex); idFocus->setFocusProjectionIndex(focusProjectionIndex); idFocus->setVolumeFile(underlayVolume); idFocus->setScreenDepth(depth); float xyz[3]; const SurfaceProjectedItem* spi = focus->getProjection(focusProjectionIndex); spi->getVolumeXYZ(xyz); m_fixedPipelineDrawing->setSelectedItemScreenXYZ(idFocus, xyz); CaretLogFine("Selected Volume Focus Identification Symbol: " + QString::number(focusIndex)); } } } } /** * Draw the axes crosshairs. * * @param sliceProjectionType * Type of projection for the slice drawing (oblique, orthogonal) * @param sliceDrawingType * Type of slice drawing (montage, single) * @param sliceViewPlane * View plane that is displayed. * @param sliceCoordinates * Coordinates of the selected slices. */ void BrainOpenGLVolumeObliqueSliceDrawing::drawAxesCrosshairs(const VolumeSliceProjectionTypeEnum::Enum sliceProjectionType, const VolumeSliceDrawingTypeEnum::Enum sliceDrawingType, const VolumeSliceViewPlaneEnum::Enum sliceViewPlane, const float sliceCoordinates[3]) { CaretPreferences* prefs = SessionManager::get()->getCaretPreferences(); const bool drawCrosshairsFlag = prefs->isVolumeAxesCrosshairsDisplayed(); bool drawCrosshairLabelsFlag = prefs->isVolumeAxesLabelsDisplayed(); switch (sliceDrawingType) { case VolumeSliceDrawingTypeEnum::VOLUME_SLICE_DRAW_MONTAGE: drawCrosshairLabelsFlag = false; break; case VolumeSliceDrawingTypeEnum::VOLUME_SLICE_DRAW_SINGLE: break; } switch (sliceProjectionType) { case VolumeSliceProjectionTypeEnum::VOLUME_SLICE_PROJECTION_ORTHOGONAL: drawAxesCrosshairsOrthoAndOblique(sliceProjectionType, sliceViewPlane, sliceCoordinates, drawCrosshairsFlag, drawCrosshairLabelsFlag); break; case VolumeSliceProjectionTypeEnum::VOLUME_SLICE_PROJECTION_OBLIQUE: { glPushMatrix(); glLoadIdentity(); // double mv[16]; // glGetDoublev(GL_MODELVIEW_MATRIX, mv); // Matrix4x4 mvm; // mvm.setMatrixFromOpenGL(mv); // float trans[3]; // m_browserTabContent->getTranslation(trans); // glTranslatef(trans[0], trans[1], trans[2]); drawAxesCrosshairsOrthoAndOblique(sliceProjectionType, sliceViewPlane, sliceCoordinates, drawCrosshairsFlag, drawCrosshairLabelsFlag); glPopMatrix(); } break; } } /** * Draw the axes crosshairs for an orthogonal slice. * * @param sliceProjectionType * Type of projection for the slice drawing (oblique, orthogonal) * @param sliceViewPlane * The slice plane view. * @param sliceCoordinates * Coordinates of the selected slices. * @param drawCrosshairsFlag * If true, draw the crosshairs. * @param drawCrosshairLabelsFlag * If true, draw the crosshair labels. */ void BrainOpenGLVolumeObliqueSliceDrawing::drawAxesCrosshairsOrthoAndOblique(const VolumeSliceProjectionTypeEnum::Enum sliceProjectionType, const VolumeSliceViewPlaneEnum::Enum sliceViewPlane, const float sliceCoordinates[3], const bool drawCrosshairsFlag, const bool drawCrosshairLabelsFlag) { bool obliqueModeFlag = false; switch (sliceProjectionType) { case VolumeSliceProjectionTypeEnum::VOLUME_SLICE_PROJECTION_OBLIQUE: obliqueModeFlag = true; break; case VolumeSliceProjectionTypeEnum::VOLUME_SLICE_PROJECTION_ORTHOGONAL: break; } GLboolean depthEnabled = GL_FALSE; glGetBooleanv(GL_DEPTH_TEST, &depthEnabled); glDisable(GL_DEPTH_TEST); const float bigValue = 10000.0; float horizontalAxisStartXYZ[3] = { sliceCoordinates[0], sliceCoordinates[1], sliceCoordinates[2] }; float horizontalAxisEndXYZ[3] = { sliceCoordinates[0], sliceCoordinates[1], sliceCoordinates[2] }; float verticalAxisStartXYZ[3] = { sliceCoordinates[0], sliceCoordinates[1], sliceCoordinates[2] }; float verticalAxisEndXYZ[3] = { sliceCoordinates[0], sliceCoordinates[1], sliceCoordinates[2] }; float trans[3]; m_browserTabContent->getTranslation(trans); float horizTrans[3] = { trans[0], trans[1], trans[2] }; float vertTrans[3] = { trans[0], trans[1], trans[2] }; if (obliqueModeFlag) { switch (sliceViewPlane) { case VolumeSliceViewPlaneEnum::ALL: break; case VolumeSliceViewPlaneEnum::AXIAL: break; case VolumeSliceViewPlaneEnum::CORONAL: horizontalAxisStartXYZ[0] = sliceCoordinates[0]; horizontalAxisStartXYZ[1] = sliceCoordinates[2]; horizontalAxisStartXYZ[2] = sliceCoordinates[1]; horizontalAxisEndXYZ[0] = sliceCoordinates[0]; horizontalAxisEndXYZ[1] = sliceCoordinates[2]; horizontalAxisEndXYZ[2] = sliceCoordinates[1]; horizTrans[0] = trans[0]; horizTrans[1] = trans[2]; horizTrans[2] = trans[1]; verticalAxisStartXYZ[0] = sliceCoordinates[0]; verticalAxisStartXYZ[1] = sliceCoordinates[1]; verticalAxisStartXYZ[2] = sliceCoordinates[2]; verticalAxisEndXYZ[0] = sliceCoordinates[0]; verticalAxisEndXYZ[1] = sliceCoordinates[1]; verticalAxisEndXYZ[2] = sliceCoordinates[2]; vertTrans[0] = trans[0]; vertTrans[1] = trans[1]; vertTrans[2] = trans[2]; break; case VolumeSliceViewPlaneEnum::PARASAGITTAL: horizontalAxisStartXYZ[0] = sliceCoordinates[1]; horizontalAxisStartXYZ[1] = sliceCoordinates[2]; horizontalAxisStartXYZ[2] = sliceCoordinates[0]; horizontalAxisEndXYZ[0] = sliceCoordinates[1]; horizontalAxisEndXYZ[1] = sliceCoordinates[2]; horizontalAxisEndXYZ[2] = sliceCoordinates[0]; horizTrans[0] = trans[1]; horizTrans[1] = trans[2]; horizTrans[2] = trans[0]; verticalAxisStartXYZ[0] = -sliceCoordinates[1]; verticalAxisStartXYZ[1] = sliceCoordinates[0]; verticalAxisStartXYZ[2] = sliceCoordinates[2]; verticalAxisEndXYZ[0] = -sliceCoordinates[1]; verticalAxisEndXYZ[1] = sliceCoordinates[0]; verticalAxisEndXYZ[2] = sliceCoordinates[2]; vertTrans[0] = -trans[1]; vertTrans[1] = trans[0]; vertTrans[2] = trans[2]; break; } } float axialRGBA[4]; getAxesColor(VolumeSliceViewPlaneEnum::AXIAL, axialRGBA); float coronalRGBA[4]; getAxesColor(VolumeSliceViewPlaneEnum::CORONAL, coronalRGBA); float paraRGBA[4]; getAxesColor(VolumeSliceViewPlaneEnum::PARASAGITTAL, paraRGBA); AString horizontalLeftText = ""; AString horizontalRightText = ""; AString verticalBottomText = ""; AString verticalTopText = ""; float* horizontalAxisRGBA = axialRGBA; float* verticalAxisRGBA = axialRGBA; switch (sliceViewPlane) { case VolumeSliceViewPlaneEnum::ALL: break; case VolumeSliceViewPlaneEnum::AXIAL: horizontalLeftText = "L"; horizontalRightText = "R"; horizontalAxisRGBA = coronalRGBA; horizontalAxisStartXYZ[0] -= bigValue; horizontalAxisEndXYZ[0] += bigValue; verticalBottomText = "P"; verticalTopText = "A"; verticalAxisRGBA = paraRGBA; verticalAxisStartXYZ[1] -= bigValue; verticalAxisEndXYZ[1] += bigValue; break; case VolumeSliceViewPlaneEnum::CORONAL: horizontalLeftText = "L"; horizontalRightText = "R"; horizontalAxisRGBA = axialRGBA; if (obliqueModeFlag) { horizontalAxisStartXYZ[0] -= bigValue; horizontalAxisEndXYZ[0] += bigValue; } else { horizontalAxisStartXYZ[0] -= bigValue; horizontalAxisEndXYZ[0] += bigValue; } verticalBottomText = "I"; verticalTopText = "S"; verticalAxisRGBA = paraRGBA; if (obliqueModeFlag) { verticalAxisStartXYZ[1] -= bigValue; verticalAxisEndXYZ[1] += bigValue; } else { verticalAxisStartXYZ[2] -= bigValue; verticalAxisEndXYZ[2] += bigValue; } break; case VolumeSliceViewPlaneEnum::PARASAGITTAL: horizontalLeftText = "A"; horizontalRightText = "P"; horizontalAxisRGBA = axialRGBA; if (obliqueModeFlag) { horizontalAxisStartXYZ[0] -= bigValue; horizontalAxisEndXYZ[0] += bigValue; } else { horizontalAxisStartXYZ[1] -= bigValue; horizontalAxisEndXYZ[1] += bigValue; } verticalBottomText = "I"; verticalTopText = "S"; verticalAxisRGBA = coronalRGBA; if (obliqueModeFlag) { verticalAxisStartXYZ[1] -= bigValue; verticalAxisEndXYZ[1] += bigValue; } else { verticalAxisStartXYZ[2] -= bigValue; verticalAxisEndXYZ[2] += bigValue; } break; } GLint viewport[4]; glGetIntegerv(GL_VIEWPORT, viewport); const int textOffset = 15; const int textLeftWindowXY[2] = { textOffset, (viewport[3] / 2) }; const int textRightWindowXY[2] = { viewport[2] - textOffset, (viewport[3] / 2) }; const int textBottomWindowXY[2] = { viewport[2] / 2, textOffset }; const int textTopWindowXY[2] = { (viewport[2] / 2), viewport[3] - textOffset }; /* * Crosshairs */ if (drawCrosshairsFlag) { glPushMatrix(); glLineWidth(1.0); glTranslatef(horizTrans[0], horizTrans[1], horizTrans[2]); glColor3fv(horizontalAxisRGBA); glBegin(GL_LINES); glVertex3fv(horizontalAxisStartXYZ); glVertex3fv(horizontalAxisEndXYZ); glEnd(); glPopMatrix(); glPushMatrix(); glLineWidth(1.0); glTranslatef(vertTrans[0], vertTrans[1], vertTrans[2]); glColor3fv(verticalAxisRGBA); glBegin(GL_LINES); glVertex3fv(verticalAxisStartXYZ); glVertex3fv(verticalAxisEndXYZ); glEnd(); glPopMatrix(); } if (drawCrosshairLabelsFlag) { const AnnotationTextFontPointSizeEnum::Enum fontSize = AnnotationTextFontPointSizeEnum::SIZE18; const int textCenter[2] = { textLeftWindowXY[0], textLeftWindowXY[1] }; const int halfFontSize = AnnotationTextFontPointSizeEnum::toSizeNumeric(fontSize) / 2; uint8_t backgroundRGBA[4] = { m_fixedPipelineDrawing->m_backgroundColorByte[0], m_fixedPipelineDrawing->m_backgroundColorByte[1], m_fixedPipelineDrawing->m_backgroundColorByte[2], 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); annotationText.setBoldStyleEnabled(true); annotationText.setFontPointSize(fontSize); annotationText.setTextColor(CaretColorEnum::CUSTOM); annotationText.setBackgroundColor(CaretColorEnum::CUSTOM); annotationText.setCustomTextColor(horizontalAxisRGBA); annotationText.setCustomBackgroundColor(backgroundRGBA); annotationText.setText(horizontalLeftText); m_fixedPipelineDrawing->drawTextAtViewportCoords(textLeftWindowXY[0], textLeftWindowXY[1], annotationText); annotationText.setText(horizontalRightText); m_fixedPipelineDrawing->drawTextAtViewportCoords(textRightWindowXY[0], textRightWindowXY[1], annotationText); annotationText.setCustomTextColor(verticalAxisRGBA); annotationText.setText(verticalBottomText); m_fixedPipelineDrawing->drawTextAtViewportCoords(textBottomWindowXY[0], textBottomWindowXY[1], annotationText); annotationText.setText(verticalTopText); annotationText.getCoordinate()->setXYZ(textTopWindowXY[0], textTopWindowXY[1], 0.0); m_fixedPipelineDrawing->drawTextAtViewportCoords(textTopWindowXY[0], textTopWindowXY[1], annotationText); } if (depthEnabled) { glEnable(GL_DEPTH_TEST); } } /** * Get the RGBA coloring for a slice view plane. * * @param sliceViewPlane * The slice view plane. * @param rgbaOut * Output colors ranging 0.0 to 1.0 */ void BrainOpenGLVolumeObliqueSliceDrawing::getAxesColor(const VolumeSliceViewPlaneEnum::Enum sliceViewPlane, float rgbaOut[4]) const { switch (sliceViewPlane) { case VolumeSliceViewPlaneEnum::ALL: CaretAssert(0); break; case VolumeSliceViewPlaneEnum::AXIAL: rgbaOut[0] = 0.0; rgbaOut[1] = 0.0; rgbaOut[2] = 1.0; rgbaOut[3] = 1.0; break; case VolumeSliceViewPlaneEnum::CORONAL: rgbaOut[0] = 0.0; rgbaOut[1] = 1.0; rgbaOut[2] = 0.0; rgbaOut[3] = 1.0; break; case VolumeSliceViewPlaneEnum::PARASAGITTAL: rgbaOut[0] = 1.0; rgbaOut[1] = 0.0; rgbaOut[2] = 0.0; rgbaOut[3] = 1.0; break; } } /** * Draw a one millimeter square facing the user. * NOTE: This method will alter the current * modelviewing matrices so caller may need * to enclose the call to this method within * glPushMatrix() and glPopMatrix(). * * @param size * Size of square. */ void BrainOpenGLVolumeObliqueSliceDrawing::drawSquare(const float size) { const float length = size * 0.5; /* * Draw both front and back side since in some instances, * such as surface montage, we are viweing from the far * side (from back of monitor) */ glBegin(GL_QUADS); glNormal3f(0.0, 0.0, 1.0); glVertex3f(-length, -length, 0.0); glVertex3f( length, -length, 0.0); glVertex3f( length, length, 0.0); glVertex3f(-length, length, 0.0); glNormal3f(0.0, 0.0, -1.0); glVertex3f(-length, -length, 0.0); glVertex3f(-length, length, 0.0); glVertex3f( length, length, 0.0); glVertex3f( length, -length, 0.0); glEnd(); } /** * Get the minimum and maximum distance between adjacent voxels in all * slices planes. Output spacing value are always non-negative even if * a right-to-left orientation. * * @param volume * Volume for which min/max spacing is requested. * @param minSpacingOut * Output minimum spacing. * @param maxSpacingOut * Output maximum spacing. * @return * True if min and max spacing are greater than zero. */ bool BrainOpenGLVolumeObliqueSliceDrawing::getMinMaxVoxelSpacing(const VolumeMappableInterface* volume, float& minSpacingOut, float& maxSpacingOut) const { CaretAssert(volume); float originX, originY, originZ; float x1, y1, z1; volume->indexToSpace(0, 0, 0, originX, originY, originZ); volume->indexToSpace(1, 1, 1, x1, y1, z1); const float dx = std::fabs(x1 - originX); const float dy = std::fabs(y1 - originY); const float dz = std::fabs(z1 - originZ); minSpacingOut = std::min(std::min(dx, dy), dz); maxSpacingOut = std::max(std::max(dx, dy), dz); if ((minSpacingOut > 0.0) && (maxSpacingOut > 0.0)) { return true; } return false; } /** * Draw orientation axes * * @param viewport * The viewport region for the orientation axes. */ void BrainOpenGLVolumeObliqueSliceDrawing::drawOrientationAxes(const int viewport[4]) { CaretPreferences* prefs = SessionManager::get()->getCaretPreferences(); const bool drawCylindersFlag = prefs->isVolumeAxesCrosshairsDisplayed(); const bool drawLabelsFlag = prefs->isVolumeAxesLabelsDisplayed(); /* * Set the viewport */ glViewport(viewport[0], viewport[1], viewport[2], viewport[3]); const double viewportWidth = viewport[2]; const double viewportHeight = viewport[3]; /* * Determine bounds for orthographic projection */ const double maxCoord = 100.0; const double minCoord = -maxCoord; double left = 0.0; double right = 0.0; double top = 0.0; double bottom = 0.0; const double nearDepth = -1000.0; const double farDepth = 1000.0; if (viewportHeight > viewportWidth) { left = minCoord; right = maxCoord; const double aspectRatio = (viewportHeight / viewportWidth); top = maxCoord * aspectRatio; bottom = minCoord * aspectRatio; } else { const double aspectRatio = (viewportWidth / viewportHeight); top = maxCoord; bottom = minCoord; left = minCoord * aspectRatio; right = maxCoord * aspectRatio; } /* * Set the orthographic projection */ glMatrixMode(GL_PROJECTION); glPushMatrix(); glLoadIdentity(); glOrtho(left, right, bottom, top, nearDepth, farDepth); glMatrixMode(GL_MODELVIEW); glPushMatrix(); glLoadIdentity(); { /* * Set the viewing transformation, places 'eye' so that it looks * at the 'model' which is, in this case, the axes */ double eyeX = 0.0; double eyeY = 0.0; double eyeZ = BrainOpenGLFixedPipeline::s_gluLookAtCenterFromEyeOffsetDistance; //100.0; const double centerX = 0; const double centerY = 0; const double centerZ = 0; const double upX = 0; const double upY = 1; const double upZ = 0; gluLookAt(eyeX, eyeY, eyeZ, centerX, centerY, centerZ, upX, upY, upZ); /* * Set the modeling transformation */ const Matrix4x4 obliqueRotationMatrix = m_browserTabContent->getObliqueVolumeRotationMatrix(); double rotationMatrix[16]; obliqueRotationMatrix.getMatrixForOpenGL(rotationMatrix); glMultMatrixd(rotationMatrix); /* * Disable depth buffer. Otherwise, when volume slices are drawn * black regions of the slices may set depth buffer and the occlude * the axes from display. */ GLboolean depthBufferEnabled = false; glGetBooleanv(GL_DEPTH_TEST, &depthBufferEnabled); glDisable(GL_DEPTH_TEST); const float red[4] = { 1.0, 0.0, 0.0, 1.0 }; const float green[4] = { 0.0, 1.0, 0.0, 1.0 }; const float blue[4] = { 0.0, 0.0, 1.0, 1.0 }; const double axisMaxCoord = maxCoord * 0.8; const double axisMinCoord = -axisMaxCoord; const double textMaxCoord = maxCoord * 0.9; 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 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 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 axesCrosshairRadius = 1.0; if (drawCylindersFlag) { m_fixedPipelineDrawing->drawCylinder(blue, axialPlaneMin, axialPlaneMax, axesCrosshairRadius); } AnnotationPointSizeText annotationText(AnnotationAttributesDefaultTypeEnum::NORMAL); annotationText.setHorizontalAlignment(AnnotationTextAlignHorizontalEnum::CENTER); annotationText.setVerticalAlignment(AnnotationTextAlignVerticalEnum::MIDDLE); annotationText.setFontPointSize(AnnotationTextFontPointSizeEnum::SIZE14); annotationText.setCoordinateSpace(AnnotationCoordinateSpaceEnum::STEREOTAXIC); annotationText.setTextColor(CaretColorEnum::CUSTOM); if (drawLabelsFlag) { annotationText.setCustomTextColor(blue); annotationText.setText("I"); m_fixedPipelineDrawing->drawTextAtModelCoords(axialTextMin, annotationText); annotationText.setText("S"); m_fixedPipelineDrawing->drawTextAtModelCoords(axialTextMax, annotationText); } if (drawCylindersFlag) { m_fixedPipelineDrawing->drawCylinder(green, coronalPlaneMin, coronalPlaneMax, axesCrosshairRadius); } if (drawLabelsFlag) { annotationText.setCustomTextColor(green); annotationText.setText("L"); m_fixedPipelineDrawing->drawTextAtModelCoords(coronalTextMin, annotationText); annotationText.setText("R"); m_fixedPipelineDrawing->drawTextAtModelCoords(coronalTextMax, annotationText); } if (drawCylindersFlag) { m_fixedPipelineDrawing->drawCylinder(red, paraPlaneMin, paraPlaneMax, axesCrosshairRadius); } if (drawLabelsFlag) { annotationText.setCustomTextColor(red); annotationText.setText("P"); m_fixedPipelineDrawing->drawTextAtModelCoords(paraTextMin, annotationText); annotationText.setText("A"); m_fixedPipelineDrawing->drawTextAtModelCoords(paraTextMax, annotationText); } } glPopMatrix(); glMatrixMode(GL_PROJECTION); glPopMatrix(); glMatrixMode(GL_MODELVIEW); } /** * Set the orthographic projection. * * @param sliceViewPlane * View plane that is displayed. * @param viewport * The viewport. */ void BrainOpenGLVolumeObliqueSliceDrawing::setOrthographicProjection(const VolumeSliceViewPlaneEnum::Enum sliceViewPlane, const int viewport[4]) { /* * Determine model size in screen Y when viewed */ BoundingBox boundingBox; m_volumeDrawInfo[0].volumeFile->getVoxelSpaceBoundingBox(boundingBox); /* * Set top and bottom to the min/max coordinate * that runs vertically on the screen */ double modelTop = 200.0; double modelBottom = -200.0; switch (sliceViewPlane) { case VolumeSliceViewPlaneEnum::ALL: CaretAssertMessage(0, "Should never get here"); break; case VolumeSliceViewPlaneEnum::AXIAL: modelTop = boundingBox.getMaxY(); modelBottom = boundingBox.getMinY(); break; case VolumeSliceViewPlaneEnum::CORONAL: modelTop = boundingBox.getMaxZ(); modelBottom = boundingBox.getMinZ(); break; case VolumeSliceViewPlaneEnum::PARASAGITTAL: modelTop = boundingBox.getMaxZ(); modelBottom = boundingBox.getMinZ(); break; } /* * Scale ratio makes region slightly larger than model */ const double zoom = m_browserTabContent->getScaling(); double scaleRatio = (1.0 / 0.98); if (zoom > 0.0) { scaleRatio /= zoom; } modelTop *= scaleRatio; modelBottom *= scaleRatio; /* * Determine aspect ratio of viewport */ const double viewportWidth = viewport[2]; const double viewportHeight = viewport[3]; const double aspectRatio = (viewportWidth / viewportHeight); /* * Set bounds of orthographic projection */ const double halfModelY = ((modelTop - modelBottom) / 2.0); const double orthoBottom = modelBottom; const double orthoTop = modelTop; const double orthoRight = halfModelY * aspectRatio; const double orthoLeft = -halfModelY * aspectRatio; const double nearDepth = -1000.0; const double farDepth = 1000.0; m_orthographicBounds[0] = orthoLeft; m_orthographicBounds[1] = orthoRight; m_orthographicBounds[2] = orthoBottom; m_orthographicBounds[3] = orthoTop; m_orthographicBounds[4] = nearDepth; m_orthographicBounds[5] = farDepth; glMatrixMode(GL_PROJECTION); glLoadIdentity(); glOrtho(m_orthographicBounds[0], m_orthographicBounds[1], m_orthographicBounds[2], m_orthographicBounds[3], m_orthographicBounds[4], m_orthographicBounds[5]); glMatrixMode(GL_MODELVIEW); } /** * Draw the voxels in an orthogonal slice. * * @param sliceNormalVector * Normal vector of the slice plane. * @param coordinate * Coordinate of first voxel in the slice (bottom left as begin viewed) * @param rowStep * Three-dimensional step to next row. * @param columnStep * Three-dimensional step to next column. * @param numberOfColumns * Number of columns in the slice. * @param numberOfRows * Number of rows in the slice. * @param sliceRGBA * RGBA coloring for voxels in the slice. * @param validVoxelCount * Number of voxels with valid coloring * @param volumeInterface * Index of the volume being drawn. * @param volumeIndex * Selected map in the volume being drawn. * @param mapIndex * Selected map in the volume being drawn. * @param sliceOpacity * Opacity from the overlay. */ void BrainOpenGLVolumeObliqueSliceDrawing::drawOrthogonalSliceVoxels(const float sliceNormalVector[3], const float coordinate[3], const float rowStep[3], const float columnStep[3], const int64_t numberOfColumns, const int64_t numberOfRows, const std::vector& sliceRGBA, const int64_t validVoxelCount, const VolumeMappableInterface* volumeInterface, const int32_t volumeIndex, const int32_t mapIndex, const uint8_t sliceOpacity) { /* * There are two ways to draw the voxels. * * Quad Indices: This method submits each vertex (coordinate, normal, rgba) * one time BUT it submits EVERY vertex in the slice. Note that the vertex * is shared by four voxels (except along an edge). For each valid voxel, * it submits the indices of the voxel's four vertices. This method is * efficient when many voxels are drawn since each vertex is submitted to * OpenGL one time and is shared by up to four voxels. However, when * only a few voxels are drawn, many unused vertices are submitted. * * Single Quads: For each voxel, this method submits the four vertices * (coordinate, normal, rgba) for each voxel drawn. Even though adjacent * voxels share vertices, the vertices are submitted for each voxel. When * only a small number of voxels in the slice are drawn, this method is * efficient since only the needed vertex data is submitted. However, * when many voxels are drawn, many vertices are duplicated making the * drawing inefficient. * * So, estimate the bytes that are passed to OpenGL for each drawing * mode and choose the drawing mode that requires the smallest number * of bytes. */ /* * Each vertex requires 28 bytes * (3 float xyz, 3 float normal xyz + 4 bytes color). * * Single quads uses four vertices per quad. * * Index quads requires four 4-byte ints for the quad's indices. */ const int64_t bytesPerVertex = 28; const int64_t totalVertexBytes = ((numberOfColumns + 1) * (numberOfRows + 1) * bytesPerVertex); const int64_t singleQuadBytes = (validVoxelCount * bytesPerVertex * 4); const int64_t indexQuadBytes = (totalVertexBytes + (16 * validVoxelCount)); bool drawWithQuadIndicesFlag = false; if (indexQuadBytes < singleQuadBytes) { drawWithQuadIndicesFlag = true; } if (drawWithQuadIndicesFlag) { drawOrthogonalSliceVoxelsQuadIndicesAndStrips(sliceNormalVector, coordinate, rowStep, columnStep, numberOfColumns, numberOfRows, sliceRGBA, volumeInterface, volumeIndex, mapIndex, sliceOpacity); } else { drawOrthogonalSliceVoxelsSingleQuads(sliceNormalVector, coordinate, rowStep, columnStep, numberOfColumns, numberOfRows, sliceRGBA, volumeInterface, volumeIndex, mapIndex, sliceOpacity); } } /** * Draw the voxels in an orthogonal slice with single quads. * * Four coordinates, normals, and colors are sent to OpenGL for each * quad that is drawn. This may be efficent when only a few voxels * are drawn but very inefficient when many voxels are drawn. * * @param sliceNormalVector * Normal vector of the slice plane. * @param coordinate * Coordinate of first voxel in the slice (bottom left as begin viewed) * @param rowStep * Three-dimensional step to next row. * @param columnStep * Three-dimensional step to next column. * @param numberOfColumns * Number of columns in the slice. * @param numberOfRows * Number of rows in the slice. * @param sliceRGBA * RGBA coloring for voxels in the slice. * @param volumeInterface * Index of the volume being drawn. * @param volumeIndex * Selected map in the volume being drawn. * @param mapIndex * Selected map in the volume being drawn. * @param sliceOpacity * Opacity from the overlay. */ void BrainOpenGLVolumeObliqueSliceDrawing::drawOrthogonalSliceVoxelsSingleQuads(const float sliceNormalVector[3], const float coordinate[3], const float rowStep[3], const float columnStep[3], const int64_t numberOfColumns, const int64_t numberOfRows, const std::vector& sliceRGBA, const VolumeMappableInterface* volumeInterface, const int32_t volumeIndex, const int32_t mapIndex, const uint8_t sliceOpacity) { /* * When performing voxel identification for editing voxels, * we need to draw EVERY voxel since the user may click * regions where the voxels are "off". */ bool volumeEditingDrawAllVoxelsFlag = false; if (m_identificationModeFlag) { SelectionItemVoxelEditing* voxelEditID = m_brain->getSelectionManager()->getVoxelEditingIdentification(); if (voxelEditID->isEnabledForSelection()) { const VolumeFile* vf = dynamic_cast(volumeInterface); if (vf == voxelEditID->getVolumeFileForEditing()) { volumeEditingDrawAllVoxelsFlag = true; } } } const int64_t numVoxelsInSlice = numberOfColumns * numberOfRows; /* * Allocate for quadrilateral drawing */ const int64_t numQuadCoords = numVoxelsInSlice * 12; const int64_t numQuadRgba = numVoxelsInSlice * 16; std::vector voxelQuadCoordinates; std::vector voxelQuadNormals; std::vector voxelQuadRgba; voxelQuadCoordinates.reserve(numQuadCoords); voxelQuadNormals.reserve(numQuadCoords); voxelQuadRgba.reserve(numQuadRgba); /* * Step to next row or column voxel */ const float rowStepX = rowStep[0]; const float rowStepY = rowStep[1]; const float rowStepZ = rowStep[2]; const float columnStepX = columnStep[0]; const float columnStepY = columnStep[1]; const float columnStepZ = columnStep[2]; /* * Draw each row */ for (int64_t jRow = 0; jRow < numberOfRows; jRow++) { /* * Coordinates on left side of row */ float rowBottomLeft[3] = { coordinate[0] + (jRow * rowStepX), coordinate[1] + (jRow * rowStepY), coordinate[2] + (jRow * rowStepZ) }; float rowTopLeft[3] = { rowBottomLeft[0] + rowStepX, rowBottomLeft[1] + rowStepY, rowBottomLeft[2] + rowStepZ }; /* * Draw each voxel in its column */ for (int64_t iCol = 0; iCol < numberOfColumns; iCol++) { /* * Offset of voxel in coloring. */ const int64_t sliceRgbaOffset = (4 * (iCol + (numberOfColumns * jRow))); const int64_t alphaOffset = sliceRgbaOffset + 3; uint8_t rgba[4] = { 0, 0, 0, 0 }; /* * Negative alpha means do not display */ CaretAssertVectorIndex(sliceRGBA, alphaOffset); if (sliceRGBA[alphaOffset] <= 0) { if (volumeIndex == 0) { /* * For first drawn volume, use an alpha of 255 so * so that black is drawn */ rgba[3] = 255; /* * OVERRIDES BLACK VOXEL ABOVE (255) * For first drawn volume, use an alpha of zero so * that the background shows through */ rgba[3] = 0; //255; } } else { /* * Use overlay's opacity */ rgba[0] = sliceRGBA[sliceRgbaOffset]; rgba[1] = sliceRGBA[sliceRgbaOffset + 1]; rgba[2] = sliceRGBA[sliceRgbaOffset + 2]; rgba[3] = sliceOpacity; } /* * Set coordinates of voxel corners */ float voxelBottomLeft[3] = { rowBottomLeft[0] + (iCol * columnStepX), rowBottomLeft[1] + (iCol * columnStepY), rowBottomLeft[2] + (iCol * columnStepZ) }; float voxelBottomRight[3] = { voxelBottomLeft[0] + columnStepX, voxelBottomLeft[1] + columnStepY, voxelBottomLeft[2] + columnStepZ }; float voxelTopLeft[3] = { rowTopLeft[0] + (iCol * columnStepX), rowTopLeft[1] + (iCol * columnStepY), rowTopLeft[2] + (iCol * columnStepZ) }; float voxelTopRight[3] = { voxelTopLeft[0] + columnStepX, voxelTopLeft[1] + columnStepY, voxelTopLeft[2] + columnStepZ }; /* * Need to draw ALL voxels if performing * identificaiton for voxel editing. */ if (volumeEditingDrawAllVoxelsFlag) { rgba[3] = 255; } /* * Draw voxel based upon opacity */ if (rgba[3] > 0) { if (m_identificationModeFlag) { /* * Add info about voxel for identication. */ int64_t voxelI = 0; int64_t voxelJ = 0; int64_t voxelK = 0; const float voxelCenterX = (voxelBottomLeft[0] + voxelTopRight[0]) / 2.0; const float voxelCenterY = (voxelBottomLeft[1] + voxelTopRight[1]) / 2.0; const float voxelCenterZ = (voxelBottomLeft[2] + voxelTopRight[2]) / 2.0; volumeInterface->enclosingVoxel(voxelCenterX, voxelCenterY, voxelCenterZ, voxelI, voxelJ, voxelK); const float voxelDiffXYZ[3] = { voxelTopRight[0] - voxelBottomLeft[0], voxelTopRight[1] - voxelBottomLeft[1], voxelTopRight[2] - voxelBottomLeft[2], }; addVoxelToIdentification(volumeIndex, mapIndex, voxelI, voxelJ, voxelK, voxelDiffXYZ, rgba); } /* * Add voxel to quadrilaterals */ voxelQuadCoordinates.push_back(voxelBottomLeft[0]); voxelQuadCoordinates.push_back(voxelBottomLeft[1]); voxelQuadCoordinates.push_back(voxelBottomLeft[2]); voxelQuadCoordinates.push_back(voxelBottomRight[0]); voxelQuadCoordinates.push_back(voxelBottomRight[1]); voxelQuadCoordinates.push_back(voxelBottomRight[2]); voxelQuadCoordinates.push_back(voxelTopRight[0]); voxelQuadCoordinates.push_back(voxelTopRight[1]); voxelQuadCoordinates.push_back(voxelTopRight[2]); voxelQuadCoordinates.push_back(voxelTopLeft[0]); voxelQuadCoordinates.push_back(voxelTopLeft[1]); voxelQuadCoordinates.push_back(voxelTopLeft[2]); for (int32_t iNormalAndColor = 0; iNormalAndColor < 4; iNormalAndColor++) { voxelQuadRgba.push_back(rgba[0]); voxelQuadRgba.push_back(rgba[1]); voxelQuadRgba.push_back(rgba[2]); voxelQuadRgba.push_back(rgba[3]); voxelQuadNormals.push_back(sliceNormalVector[0]); voxelQuadNormals.push_back(sliceNormalVector[1]); voxelQuadNormals.push_back(sliceNormalVector[2]); } } } } /* * Draw the voxels. */ if (voxelQuadCoordinates.empty() == false) { BrainOpenGLPrimitiveDrawing::drawQuads(voxelQuadCoordinates, voxelQuadNormals, voxelQuadRgba); } } /** * Draw the voxels in an orthogonal slice using quad indices or strips. * * Each vertex (coordinate, its normal vector, and its color) is sent to OpenGL * one time. Index arrays are used to specify the vertices when drawing the * quads. * * This is efficient when many voxels are drawn but may be inefficent * when only a few voxels are drawn. * * @param sliceNormalVector * Normal vector of the slice plane. * @param firstVoxelCoordinate * Coordinate of first voxel in the slice (bottom left as begin viewed) * @param rowStep * Three-dimensional step to next row. * @param columnStep * Three-dimensional step to next column. * @param numberOfColumns * Number of columns in the slice. * @param numberOfRows * Number of rows in the slice. * @param sliceRGBA * RGBA coloring for voxels in the slice. * @param volumeInterface * Index of the volume being drawn. * @param volumeIndex * Selected map in the volume being drawn. * @param mapIndex * Selected map in the volume being drawn. * @param sliceOpacity * Opacity from the overlay. */ void BrainOpenGLVolumeObliqueSliceDrawing::drawOrthogonalSliceVoxelsQuadIndicesAndStrips(const float sliceNormalVector[3], const float firstVoxelCoordinate[3], const float rowStep[3], const float columnStep[3], const int64_t numberOfColumns, const int64_t numberOfRows, const std::vector& sliceRGBA, const VolumeMappableInterface* volumeInterface, const int32_t volumeIndex, const int32_t mapIndex, const uint8_t sliceOpacity) { //const bool debugFlag = false; enum DrawType { DRAW_QUADS, DRAW_QUAD_STRIPS }; const DrawType drawType = DRAW_QUADS; /* * When performing voxel identification for editing voxels, * we need to draw EVERY voxel since the user may click * regions where the voxels are "off". */ bool volumeEditingDrawAllVoxelsFlag = false; if (m_identificationModeFlag) { SelectionItemVoxelEditing* voxelEditID = m_brain->getSelectionManager()->getVoxelEditingIdentification(); if (voxelEditID->isEnabledForSelection()) { const VolumeFile* vf = dynamic_cast(volumeInterface); if (vf == voxelEditID->getVolumeFileForEditing()) { volumeEditingDrawAllVoxelsFlag = true; } } } /* * Allocate vectors for quadrilateral drawing */ const int64_t totalCoordElements = (numberOfColumns + 1) * (numberOfRows + 1); const int64_t numQuadStripCoords = totalCoordElements * 3; const int64_t numQuadStripRGBA = totalCoordElements * 4; std::vector voxelQuadCoordinates; std::vector voxelQuadNormals; std::vector voxelQuadRgba; voxelQuadCoordinates.reserve(numQuadStripCoords); voxelQuadNormals.reserve(numQuadStripCoords); voxelQuadRgba.reserve(numQuadStripRGBA); /* * Step to next row or column voxel */ const float rowStepX = rowStep[0]; const float rowStepY = rowStep[1]; const float rowStepZ = rowStep[2]; const float columnStepX = columnStep[0]; const float columnStepY = columnStep[1]; const float columnStepZ = columnStep[2]; const float voxelStepX = rowStepX + columnStepX; const float voxelStepY = rowStepY + columnStepY; const float voxelStepZ = rowStepZ + columnStepZ; const float voxelStepXYZ[3] = { voxelStepX, voxelStepY, voxelStepZ }; const float halfVoxelStepX = (voxelStepX / 2.0); const float halfVoxelStepY = (voxelStepY / 2.0); const float halfVoxelStepZ = (voxelStepZ / 2.0); int64_t numberOfVoxelsToDraw = 0; /* * Loop through column COORDINATES */ float columnBottomCoord[3] = { firstVoxelCoordinate[0], firstVoxelCoordinate[1], firstVoxelCoordinate[2] }; for (int64_t iCol = 0; iCol <= numberOfColumns; iCol++) { if (iCol > 0) { columnBottomCoord[0] += columnStepX; columnBottomCoord[1] += columnStepY; columnBottomCoord[2] += columnStepZ; } /* * Loop through the row COORDINATES */ float rowCoord[3] = { columnBottomCoord[0], columnBottomCoord[1], columnBottomCoord[2] }; for (int64_t jRow = 0; jRow <= numberOfRows; jRow++) { if (jRow > 0) { rowCoord[0] += rowStepX; rowCoord[1] += rowStepY; rowCoord[2] += rowStepZ; } voxelQuadCoordinates.push_back(rowCoord[0]); voxelQuadCoordinates.push_back(rowCoord[1]); voxelQuadCoordinates.push_back(rowCoord[2]); voxelQuadNormals.push_back(sliceNormalVector[0]); voxelQuadNormals.push_back(sliceNormalVector[1]); voxelQuadNormals.push_back(sliceNormalVector[2]); uint8_t rgba[4] = { 0, 0, 0, 0 }; /* * With FLAT shading: * Quads: Uses top left coordinate for quad coloring * Quad Strip: Uses top right coordinate for quad coloring * So, the color is only set for this coordinate */ int64_t iColRGBA = iCol; int64_t jRowRGBA = jRow; switch (drawType) { case DRAW_QUADS: if (iColRGBA >= numberOfColumns) { iColRGBA = numberOfColumns - 1; } jRowRGBA = jRow - 1; break; case DRAW_QUAD_STRIPS: iColRGBA = iCol - 1; jRowRGBA = jRow - 1; break; } if ((iColRGBA >= 0) && (jRowRGBA >= 0)) { const int64_t voxelOffset = (iColRGBA + (numberOfColumns * jRowRGBA)); if (debugFlag) { std::cout << "col=" << iCol << " row=" << jRow << " voxel-offset=" << voxelOffset << std::endl; } /* * Offset of voxel in coloring. * Note that colors are stored in rows */ int64_t sliceRgbaOffset = (4 * voxelOffset); /* * An alpha greater than zero means the voxel is displayed */ const int64_t alphaOffset = sliceRgbaOffset + 3; CaretAssertVectorIndex(sliceRGBA, alphaOffset); if (sliceRGBA[alphaOffset] > 0) { /* * Use overlay's opacity for the voxel */ rgba[0] = sliceRGBA[sliceRgbaOffset]; rgba[1] = sliceRGBA[sliceRgbaOffset + 1]; rgba[2] = sliceRGBA[sliceRgbaOffset + 2]; rgba[3] = sliceOpacity; } } /* * Voxel editing requires drawing of all voxels so that * "off" voxels can be turned "on". */ if (volumeEditingDrawAllVoxelsFlag) { rgba[3] = 255; } /* * Draw voxel if non-zero opacity */ if (rgba[3] > 0) { ++numberOfVoxelsToDraw; if (m_identificationModeFlag) { /* * Identification information is encoded in the * RGBA coloring. */ const float voxelCenterX = rowCoord[0] + halfVoxelStepX; const float voxelCenterY = rowCoord[1] + halfVoxelStepY; const float voxelCenterZ = rowCoord[2] + halfVoxelStepZ; int64_t voxelI = 0; int64_t voxelJ = 0; int64_t voxelK = 0; volumeInterface->enclosingVoxel(voxelCenterX, voxelCenterY, voxelCenterZ, voxelI, voxelJ, voxelK); addVoxelToIdentification(volumeIndex, mapIndex, voxelI, voxelJ, voxelK, voxelStepXYZ, rgba); } } voxelQuadRgba.push_back(rgba[0]); voxelQuadRgba.push_back(rgba[1]); voxelQuadRgba.push_back(rgba[2]); voxelQuadRgba.push_back(rgba[3]); } } const int64_t numberOfCoordinates = voxelQuadCoordinates.size() / 3; if (debugFlag) { std::cout << "Num rows/cols: " << numberOfRows << ", " << numberOfColumns << std::endl; std::cout << "Total, 3, 4 " << totalCoordElements << ", " << numQuadStripCoords << ", " << numQuadStripRGBA << std::endl; std::cout << "Size coords: " << voxelQuadCoordinates.size() << std::endl; std::cout << "Size normals: " << voxelQuadNormals.size() << std::endl; std::cout << "Size rgba: " << voxelQuadRgba.size() << std::endl; std::cout << "Valid voxels: " << numberOfVoxelsToDraw << std::endl; for (int64_t i = 0; i < numberOfCoordinates; i++) { std::cout << i << ": "; CaretAssertVectorIndex(voxelQuadCoordinates, i*3 + 2); std::cout << qPrintable(AString::fromNumbers(&voxelQuadCoordinates[i*3], 3, ",")) << " "; CaretAssertVectorIndex(voxelQuadRgba, i*4 + 3); std::cout << qPrintable(AString::fromNumbers(&voxelQuadRgba[i*4], 4, ",")) << std::endl; } } /* * Setup indices into coordinates/normals/coloring to draw the quads */ switch (drawType) { case DRAW_QUADS: { std::vector quadIndices; quadIndices.reserve(numberOfVoxelsToDraw * 4); for (int64_t iCol = 0; iCol < numberOfColumns; iCol++) { const int64_t columnOffset = (iCol * (numberOfRows + 1)); for (int64_t jRow = 0; jRow < numberOfRows; jRow++) { const int64_t coordBottomLeftIndex = columnOffset + jRow; //(iCol * (numberOfRows + 1) + jRow); const int64_t coordTopLeftIndex = coordBottomLeftIndex + 1; const int64_t rgbaIndex = coordTopLeftIndex * 4; CaretAssert(coordBottomLeftIndex < numberOfCoordinates); CaretAssert(coordTopLeftIndex < numberOfCoordinates); CaretAssertVectorIndex(voxelQuadRgba, rgbaIndex + 3); if (voxelQuadRgba[rgbaIndex + 3] > 0) { /* * For quads: (bottom left, bottom right, top right, top left) * Color with flat shading comes from the top left coordinate */ const int32_t coordBottomRightIndex = coordBottomLeftIndex + (numberOfRows + 1); const int32_t coordTopRightIndex = coordBottomRightIndex + 1; CaretAssert(coordBottomRightIndex < numberOfCoordinates); CaretAssert(coordTopRightIndex < numberOfCoordinates); quadIndices.push_back(coordBottomLeftIndex); quadIndices.push_back(coordBottomRightIndex); quadIndices.push_back(coordTopRightIndex); quadIndices.push_back(coordTopLeftIndex); } } if (debugFlag) { std::cout << "Quad Indices: " << quadIndices.size() << std::endl; for (uint32_t i = 0; i < quadIndices.size(); i++) { std::cout << quadIndices[i] << " ("; const int32_t coordOffset = quadIndices[i] * 3; CaretAssertVectorIndex(voxelQuadCoordinates, coordOffset + 2); std::cout << qPrintable(AString::fromNumbers(&voxelQuadCoordinates[coordOffset], 3, ",")) << ") ("; const int32_t rgbaOffset = quadIndices[i] * 4; CaretAssertVectorIndex(voxelQuadRgba, rgbaOffset + 3); std::cout << qPrintable(AString::fromNumbers(&voxelQuadRgba[rgbaOffset], 4, ",")) << " " << std::endl; } std::cout << std::endl; } } if (debugFlag) { std::cout << "Drawing " << quadIndices.size() / 4 << " quads." << std::endl; } BrainOpenGLPrimitiveDrawing::drawQuadIndices(voxelQuadCoordinates, voxelQuadNormals, voxelQuadRgba, quadIndices); } break; case DRAW_QUAD_STRIPS: { int64_t stripCount = 0; const int64_t maxCoordsPerStrip = numberOfRows * 2 + 2; for (int64_t iCol = 0; iCol < numberOfColumns; iCol++) { std::vector quadIndices; quadIndices.reserve(maxCoordsPerStrip); for (int64_t jRow = 0; jRow < numberOfRows; jRow++) { const int32_t coordBottomLeftIndex = (iCol * (numberOfRows + 1) + jRow); const int32_t coordTopLeftIndex = coordBottomLeftIndex + 1; const int32_t coordBottomRightIndex = coordBottomLeftIndex + (numberOfRows + 1); const int32_t coordTopRightIndex = coordBottomRightIndex + 1; const int64_t rgbaIndex = coordTopRightIndex * 4; CaretAssert(coordBottomLeftIndex < numberOfCoordinates); CaretAssert(coordTopLeftIndex < numberOfCoordinates); CaretAssert(coordBottomRightIndex < numberOfCoordinates); CaretAssert(coordTopRightIndex < numberOfCoordinates); CaretAssertVectorIndex(voxelQuadRgba, rgbaIndex + 3); if (voxelQuadRgba[rgbaIndex + 3] > 0) { /* * For quad strips (bottom left, bottom right, top left, top right) */ if (quadIndices.empty()) { quadIndices.push_back(coordBottomLeftIndex); quadIndices.push_back(coordBottomRightIndex); } quadIndices.push_back(coordTopLeftIndex); quadIndices.push_back(coordTopRightIndex); } else { if ( ! quadIndices.empty()) { if (debugFlag) { std::cout << "Quad Indices: " << quadIndices.size() << std::endl; for (uint32_t i = 0; i < quadIndices.size(); i++) { std::cout << quadIndices[i] << " ("; const int32_t coordOffset = quadIndices[i] * 3; CaretAssertVectorIndex(voxelQuadCoordinates, coordOffset + 2); std::cout << qPrintable(AString::fromNumbers(&voxelQuadCoordinates[coordOffset], 3, ",")) << ") ("; const int32_t rgbaOffset = quadIndices[i] * 4; CaretAssertVectorIndex(voxelQuadRgba, rgbaOffset + 3); std::cout << qPrintable(AString::fromNumbers(&voxelQuadRgba[rgbaOffset], 4, ",")) << " " << std::endl; } std::cout << std::endl; } BrainOpenGLPrimitiveDrawing::drawQuadStrips(voxelQuadCoordinates, voxelQuadNormals, voxelQuadRgba, quadIndices); quadIndices.clear(); stripCount++; } } } if ( ! quadIndices.empty()) { if (debugFlag) { std::cout << "Quad Indices: " << quadIndices.size() << std::endl; for (uint32_t i = 0; i < quadIndices.size(); i++) { std::cout << quadIndices[i] << " ("; const int32_t coordOffset = quadIndices[i] * 3; CaretAssertVectorIndex(voxelQuadCoordinates, coordOffset + 2); std::cout << qPrintable(AString::fromNumbers(&voxelQuadCoordinates[coordOffset], 3, ",")) << ") ("; const int32_t rgbaOffset = quadIndices[i] * 4; CaretAssertVectorIndex(voxelQuadRgba, rgbaOffset + 3); std::cout << qPrintable(AString::fromNumbers(&voxelQuadRgba[rgbaOffset], 4, ",")) << " " << std::endl; } std::cout << std::endl; } BrainOpenGLPrimitiveDrawing::drawQuadStrips(voxelQuadCoordinates, voxelQuadNormals, voxelQuadRgba, quadIndices); quadIndices.clear(); stripCount++; } } if (debugFlag) { std::cout << "Strips drawn: " << stripCount << std::endl; } } break; } } /** * Reset for volume identification. * * Clear identification indices and if identification is enabled, * reserve a reasonable amount of space for the indices. */ void BrainOpenGLVolumeObliqueSliceDrawing::resetIdentification() { m_identificationIndices.clear(); if (m_identificationModeFlag) { int64_t estimatedNumberOfItems = 0; std::vector volumeDims; m_volumeDrawInfo[0].volumeFile->getDimensions(volumeDims); if (volumeDims.size() >= 3) { const int64_t maxDim = std::max(volumeDims[0], std::max(volumeDims[1], volumeDims[2])); estimatedNumberOfItems = maxDim * maxDim * IDENTIFICATION_INDICES_PER_VOXEL; } m_identificationIndices.reserve(estimatedNumberOfItems); } } /** * Add a voxel to the identification indices. * * @param volumeIndex * Index of the volume. * @param mapIndex * Index of the volume map. * @param voxelI * Voxel Index I * @param voxelJ * Voxel Index J * @param voxelK * Voxel Index K * @param voxelDiffXYZ * Change in XYZ from voxel bottom left to top right * @param rgbaForColorIdentificationOut * Encoded identification in RGBA color OUTPUT */ void BrainOpenGLVolumeObliqueSliceDrawing::addVoxelToIdentification(const int32_t volumeIndex, const int32_t mapIndex, const int32_t voxelI, const int32_t voxelJ, const int32_t voxelK, const float voxelDiffXYZ[3], uint8_t rgbaForColorIdentificationOut[4]) { const int32_t idIndex = m_identificationIndices.size() / IDENTIFICATION_INDICES_PER_VOXEL; m_fixedPipelineDrawing->colorIdentification->addItem(rgbaForColorIdentificationOut, SelectionItemDataTypeEnum::VOXEL, idIndex); rgbaForColorIdentificationOut[3] = 255; /* * ID stack requires integers to * use an integer pointer to the float values */ CaretAssert(sizeof(float) == sizeof(int32_t)); const int32_t* intPointerDiffXYZ = (int32_t*)voxelDiffXYZ; /* * If these items change, need to update reset and * processing of identification. */ m_identificationIndices.push_back(volumeIndex); m_identificationIndices.push_back(mapIndex); m_identificationIndices.push_back(voxelI); m_identificationIndices.push_back(voxelJ); m_identificationIndices.push_back(voxelK); m_identificationIndices.push_back(intPointerDiffXYZ[0]); m_identificationIndices.push_back(intPointerDiffXYZ[1]); m_identificationIndices.push_back(intPointerDiffXYZ[2]); } /** * Process voxel identification. */ void BrainOpenGLVolumeObliqueSliceDrawing::processIdentification() { int32_t identifiedItemIndex; float depth = -1.0; m_fixedPipelineDrawing->getIndexFromColorSelection(SelectionItemDataTypeEnum::VOXEL, m_fixedPipelineDrawing->mouseX, m_fixedPipelineDrawing->mouseY, identifiedItemIndex, depth); if (identifiedItemIndex >= 0) { const int32_t idIndex = identifiedItemIndex * IDENTIFICATION_INDICES_PER_VOXEL; const int32_t volDrawInfoIndex = m_identificationIndices[idIndex]; CaretAssertVectorIndex(m_volumeDrawInfo, volDrawInfoIndex); VolumeMappableInterface* vf = m_volumeDrawInfo[volDrawInfoIndex].volumeFile; const int64_t voxelIndices[3] = { m_identificationIndices[idIndex + 2], m_identificationIndices[idIndex + 3], m_identificationIndices[idIndex + 4] }; const float* floatDiffXYZ = (float*)&m_identificationIndices[idIndex + 5]; SelectionItemVoxel* voxelID = m_brain->getSelectionManager()->getVoxelIdentification(); if (voxelID->isEnabledForSelection()) { if (voxelID->isOtherScreenDepthCloserToViewer(depth)) { voxelID->setVoxelIdentification(m_brain, vf, voxelIndices, depth); float voxelCoordinates[3]; vf->indexToSpace(voxelIndices[0], voxelIndices[1], voxelIndices[2], voxelCoordinates[0], voxelCoordinates[1], voxelCoordinates[2]); m_fixedPipelineDrawing->setSelectedItemScreenXYZ(voxelID, voxelCoordinates); CaretLogFinest("Selected Voxel (3D): " + AString::fromNumbers(voxelIndices, 3, ",")); } } SelectionItemVoxelEditing* voxelEditID = m_brain->getSelectionManager()->getVoxelEditingIdentification(); if (voxelEditID->isEnabledForSelection()) { if (voxelEditID->getVolumeFileForEditing() == vf) { if (voxelEditID->isOtherScreenDepthCloserToViewer(depth)) { voxelEditID->setVoxelIdentification(m_brain, vf, voxelIndices, depth); voxelEditID->setVoxelDiffXYZ(floatDiffXYZ); float voxelCoordinates[3]; vf->indexToSpace(voxelIndices[0], voxelIndices[1], voxelIndices[2], voxelCoordinates[0], voxelCoordinates[1], voxelCoordinates[2]); m_fixedPipelineDrawing->setSelectedItemScreenXYZ(voxelEditID, voxelCoordinates); CaretLogFinest("Selected Voxel Editing (3D): Indices (" + AString::fromNumbers(voxelIndices, 3, ",") + ") Diff XYZ (" + AString::fromNumbers(floatDiffXYZ, 3, ",") + ")"); } } } } } /** * Get the maximum bounds that enclose the volumes and the minimum * voxel spacing from the volumes. * * @param boundsOut * Bounds of the volumes. * @param spacingOut * Minimum voxel spacing from the volumes. Always positive values (even if * volumes is oriented right to left). * */ bool BrainOpenGLVolumeObliqueSliceDrawing::getVoxelCoordinateBoundsAndSpacing(float boundsOut[6], float spacingOut[3]) { const int32_t numberOfVolumesToDraw = static_cast(m_volumeDrawInfo.size()); if (numberOfVolumesToDraw <= 0) { return false; } /* * Find maximum extent of all voxels and smallest voxel * size in each dimension. */ float minVoxelX = std::numeric_limits::max(); float maxVoxelX = -std::numeric_limits::max(); float minVoxelY = std::numeric_limits::max(); float maxVoxelY = -std::numeric_limits::max(); float minVoxelZ = std::numeric_limits::max(); float maxVoxelZ = -std::numeric_limits::max(); float voxelStepX = std::numeric_limits::max(); float voxelStepY = std::numeric_limits::max(); float voxelStepZ = std::numeric_limits::max(); for (int32_t i = 0; i < numberOfVolumesToDraw; i++) { const VolumeMappableInterface* volumeFile = m_volumeDrawInfo[i].volumeFile; int64_t dimI, dimJ, dimK, numMaps, numComponents; volumeFile->getDimensions(dimI, dimJ, dimK, numMaps, numComponents); float originX, originY, originZ; float x1, y1, z1; float lastX, lastY, lastZ; volumeFile->indexToSpace(0, 0, 0, originX, originY, originZ); volumeFile->indexToSpace(1, 1, 1, x1, y1, z1); volumeFile->indexToSpace(dimI - 1, dimJ - 1, dimK - 1, lastX, lastY, lastZ); const float dx = x1 - originX; const float dy = y1 - originY; const float dz = z1 - originZ; voxelStepX = std::min(voxelStepX, std::fabs(dx)); voxelStepY = std::min(voxelStepY, std::fabs(dy)); voxelStepZ = std::min(voxelStepZ, std::fabs(dz)); minVoxelX = std::min(minVoxelX, std::min(originX, lastX)); maxVoxelX = std::max(maxVoxelX, std::max(originX, lastX)); minVoxelY = std::min(minVoxelY, std::min(originY, lastY)); maxVoxelY = std::max(maxVoxelY, std::max(originY, lastY)); minVoxelZ = std::min(minVoxelZ, std::min(originZ, lastZ)); maxVoxelZ = std::max(maxVoxelZ, std::max(originZ, lastZ)); } boundsOut[0] = minVoxelX; boundsOut[1] = maxVoxelX; boundsOut[2] = minVoxelY; boundsOut[3] = maxVoxelY; boundsOut[4] = minVoxelZ; boundsOut[5] = maxVoxelZ; spacingOut[0] = voxelStepX; spacingOut[1] = voxelStepY; spacingOut[2] = voxelStepZ; /* * Two dimensions: single slice * Three dimensions: multiple slices */ int32_t validDimCount = 0; if (maxVoxelX > minVoxelX) validDimCount++; if (maxVoxelY > minVoxelY) validDimCount++; if (maxVoxelZ > minVoxelZ) validDimCount++; bool valid = false; if ((validDimCount >= 2) && (voxelStepX > 0.0) && (voxelStepY > 0.0) && (voxelStepZ > 0.0)) { valid = true; } // if ((maxVoxelX > minVoxelX) // && (maxVoxelY > minVoxelY) // && (maxVoxelZ > minVoxelZ) // && (voxelStepX > 0.0) // && (voxelStepY > 0.0) // && (voxelStepZ > 0.0)) { // valid = true; // } return valid; } /** * Create the oblique transformation matrix. * * @param sliceCoordinates * Slice that is being drawn. * @param obliqueTransformationMatrixOut * OUTPUT transformation matrix for oblique viewing. */ void BrainOpenGLVolumeObliqueSliceDrawing::createObliqueTransformationMatrix(const float sliceCoordinates[3], Matrix4x4& obliqueTransformationMatrixOut) { /* * Initialize the oblique transformation matrix */ obliqueTransformationMatrixOut.identity(); /* * Get the oblique rotation matrix */ Matrix4x4 obliqueRotationMatrix = m_browserTabContent->getObliqueVolumeRotationMatrix(); /* * Create the transformation matrix */ obliqueTransformationMatrixOut.postmultiply(obliqueRotationMatrix); /* * Translate to selected coordinate */ obliqueTransformationMatrixOut.translate(sliceCoordinates[0], sliceCoordinates[1], sliceCoordinates[2]); } /** * Find portion (or all) of slice that fits inside the graphics window. * * @param sliceViewPlane * The orthogonal plane being viewed. * @param selectedSliceCoordinate * Coordinate of the slice being drawn. * @param volumeFile * Volume file that is to be drawn. * @param culledFirstVoxelIJKOut * First (bottom left) voxel that will be drawn for the volume file. * @param culledLastVoxelIJKOut * Last (top right) voxel that will be drawn for the volume file. * @param voxelDeltaXYZOut * Voxel sizes for the volume file. The element corresponding to the * slice plane being drawn will be zero (axial => [2]=0) */ bool BrainOpenGLVolumeObliqueSliceDrawing::getVolumeDrawingViewDependentCulling(const VolumeSliceViewPlaneEnum::Enum sliceViewPlane, const float selectedSliceCoordinate, const VolumeMappableInterface* volumeFile, int64_t culledFirstVoxelIJKOut[3], int64_t culledLastVoxelIJKOut[3], float voxelDeltaXYZOut[3]) { GLint viewport[4]; glGetIntegerv(GL_VIEWPORT, viewport); GLdouble projectionMatrix[16]; glGetDoublev(GL_PROJECTION_MATRIX, projectionMatrix); GLdouble modelMatrix[16]; glGetDoublev(GL_MODELVIEW_MATRIX, modelMatrix); const double vpMinX = viewport[0]; const double vpMaxX = viewport[0] + viewport[2]; const double vpMinY = viewport[1]; const double vpMaxY = viewport[1] + viewport[3]; GLdouble bottomLeftWin[3] = { vpMinX, vpMinY, 0.0 }; GLdouble bottomRightWin[3] = { vpMaxX, vpMinY, 0.0 }; GLdouble topRightWin[3] = { vpMaxX, vpMaxY, 0.0 }; GLdouble topLeftWin[3] = { vpMinX, vpMaxY, 0.0 }; GLdouble bottomLeftCoord[3]; int32_t cornersValidCount = 0; if (gluUnProject(bottomLeftWin[0], bottomLeftWin[1], bottomLeftWin[2], modelMatrix, projectionMatrix, viewport, &bottomLeftCoord[0], &bottomLeftCoord[1], &bottomLeftCoord[2])) { cornersValidCount++; } GLdouble bottomRightCoord[3]; if (gluUnProject(bottomRightWin[0], bottomRightWin[1], bottomRightWin[2], modelMatrix, projectionMatrix, viewport, &bottomRightCoord[0], &bottomRightCoord[1], &bottomRightCoord[2])) { cornersValidCount++; } GLdouble topRightCoord[3]; if (gluUnProject(topRightWin[0], topRightWin[1], topRightWin[2], modelMatrix, projectionMatrix, viewport, &topRightCoord[0], &topRightCoord[1], &topRightCoord[2])) { cornersValidCount++; } GLdouble topLeftCoord[3]; if (gluUnProject(topLeftWin[0], topLeftWin[1], topLeftWin[2], modelMatrix, projectionMatrix, viewport, &topLeftCoord[0], &topLeftCoord[1], &topLeftCoord[2])) { cornersValidCount++; } if (cornersValidCount != 4) { return false; } switch (sliceViewPlane) { case VolumeSliceViewPlaneEnum::ALL: CaretAssert(0); break; case VolumeSliceViewPlaneEnum::AXIAL: bottomLeftCoord[2] = selectedSliceCoordinate; bottomRightCoord[2] = selectedSliceCoordinate; topRightCoord[2] = selectedSliceCoordinate; topLeftCoord[2] = selectedSliceCoordinate; break; case VolumeSliceViewPlaneEnum::CORONAL: bottomLeftCoord[1] = selectedSliceCoordinate; bottomRightCoord[1] = selectedSliceCoordinate; topRightCoord[1] = selectedSliceCoordinate; topLeftCoord[1] = selectedSliceCoordinate; break; case VolumeSliceViewPlaneEnum::PARASAGITTAL: bottomLeftCoord[0] = selectedSliceCoordinate; bottomRightCoord[0] = selectedSliceCoordinate; topRightCoord[0] = selectedSliceCoordinate; topLeftCoord[0] = selectedSliceCoordinate; break; } BoundingBox boundingBox; volumeFile->getVoxelSpaceBoundingBox(boundingBox); boundingBox.limitCoordinateToBoundingBox(bottomLeftCoord); boundingBox.limitCoordinateToBoundingBox(bottomRightCoord); boundingBox.limitCoordinateToBoundingBox(topRightCoord); boundingBox.limitCoordinateToBoundingBox(topLeftCoord); /* * Note: Spacing may be negative for some orientations * and positive may be on left or bottom */ float voxelDeltaX, voxelDeltaY, voxelDeltaZ; volumeFile->getVoxelSpacing(voxelDeltaX, voxelDeltaY, voxelDeltaZ); voxelDeltaX = std::fabs(voxelDeltaX); voxelDeltaY = std::fabs(voxelDeltaY); voxelDeltaZ = std::fabs(voxelDeltaZ); if (bottomLeftCoord[0] > topRightCoord[0]) { voxelDeltaX = -voxelDeltaX; } if (bottomLeftCoord[1] > topRightCoord[1]) { voxelDeltaY = -voxelDeltaY; } if (bottomLeftCoord[2] > topRightCoord[2]) { voxelDeltaZ = -voxelDeltaZ; } bool adjustX = false; bool adjustY = false; bool adjustZ = false; switch (sliceViewPlane) { case VolumeSliceViewPlaneEnum::ALL: CaretAssert(0); break; case VolumeSliceViewPlaneEnum::AXIAL: adjustX = true; adjustY = true; voxelDeltaZ = 0.0; break; case VolumeSliceViewPlaneEnum::CORONAL: adjustX = true; adjustZ = true; voxelDeltaY = 0.0; break; case VolumeSliceViewPlaneEnum::PARASAGITTAL: adjustY = true; adjustZ = true; voxelDeltaX = 0.0; break; } /* * Adjust by one voxel to ensure full coverage */ if (adjustX) { bottomLeftCoord[0] -= voxelDeltaX; topRightCoord[0] += voxelDeltaX; } if (adjustY) { bottomLeftCoord[1] -= voxelDeltaY; topRightCoord[1] += voxelDeltaY; } if (adjustZ) { bottomLeftCoord[2] -= voxelDeltaZ; topRightCoord[2] += voxelDeltaZ; } int64_t bottomLeftIJK[3]; volumeFile->enclosingVoxel(bottomLeftCoord[0], bottomLeftCoord[1], bottomLeftCoord[2], bottomLeftIJK[0], bottomLeftIJK[1], bottomLeftIJK[2]); int64_t topRightIJK[3]; volumeFile->enclosingVoxel(topRightCoord[0], topRightCoord[1], topRightCoord[2], topRightIJK[0], topRightIJK[1], topRightIJK[2]); volumeFile->limitIndicesToValidIndices(bottomLeftIJK[0], bottomLeftIJK[1], bottomLeftIJK[2]); volumeFile->limitIndicesToValidIndices(topRightIJK[0], topRightIJK[1], topRightIJK[2]); culledFirstVoxelIJKOut[0] = bottomLeftIJK[0]; culledFirstVoxelIJKOut[1] = bottomLeftIJK[1]; culledFirstVoxelIJKOut[2] = bottomLeftIJK[2]; culledLastVoxelIJKOut[0] = topRightIJK[0]; culledLastVoxelIJKOut[1] = topRightIJK[1]; culledLastVoxelIJKOut[2] = topRightIJK[2]; voxelDeltaXYZOut[0] = voxelDeltaX; voxelDeltaXYZOut[1] = voxelDeltaY; voxelDeltaXYZOut[2] = voxelDeltaZ; return true; } /* ======================================================================= */ /** * Constructor * * @param volumeMappableInterface * Volume that contains the data values. * @param mapIndex * Index of selected map. */ BrainOpenGLVolumeObliqueSliceDrawing::VolumeSlice::VolumeSlice(VolumeMappableInterface* volumeMappableInterface, const int32_t mapIndex) { m_volumeMappableInterface = volumeMappableInterface; m_volumeFile = dynamic_cast(m_volumeMappableInterface); m_ciftiMappableDataFile = dynamic_cast(m_volumeMappableInterface); CaretAssert(m_volumeMappableInterface); m_mapIndex = mapIndex; CaretAssert(m_mapIndex >= 0); const int64_t sliceDim = 300; const int64_t numVoxels = sliceDim * sliceDim; m_values.reserve(numVoxels); m_valuesGreen.reserve(numVoxels); m_valuesBlue.reserve(numVoxels); m_valuesAlpha.reserve(numVoxels); } /** * Add a value and return its index. * * @param value * Value that is added. * @return * The index for the value. */ int64_t BrainOpenGLVolumeObliqueSliceDrawing::VolumeSlice::addValue(const float value) { const int64_t indx = static_cast(m_values.size()); m_values.push_back(value); return indx; } /** * Add values for RGBA and returns its index. * * @param value * Value that is added. * @return * The index for the value. */ int64_t BrainOpenGLVolumeObliqueSliceDrawing::VolumeSlice::addValuesRGBA(const float values[4]) { const int64_t indx = static_cast(m_values.size()); m_values.push_back(values[0]); m_valuesGreen.push_back(values[1]); m_valuesBlue.push_back(values[2]); m_valuesAlpha.push_back(values[3]); return indx; } /** * Return RGBA colors for value using the value's index * returned by addValue(). * * @param indx * Index of the value. * @return * RGBA coloring for value. */ uint8_t* BrainOpenGLVolumeObliqueSliceDrawing::VolumeSlice::getRgbaForValueByIndex(const int64_t indx) { CaretAssertVectorIndex(m_rgba, indx * 4); return &m_rgba[indx*4]; } /** * Allocate colors for the voxel values */ void BrainOpenGLVolumeObliqueSliceDrawing::VolumeSlice::allocateColors() { m_rgba.resize(m_values.size() * 4); } /* ======================================================================= */ /** * Create a voxel for drawing. * * @param center * Center of voxel. * @param leftBottom * Left bottom coordinate of voxel. * @param rightBottom * Right bottom coordinate of voxel. * @param rightTop * Right top coordinate of voxel. * @param leftTop * Left top coordinate of voxel. */ BrainOpenGLVolumeObliqueSliceDrawing::VoxelToDraw::VoxelToDraw(const float center[3], const double leftBottom[3], const double rightBottom[3], const double rightTop[3], const double leftTop[3]) { m_center[0] = center[0]; m_center[1] = center[1]; m_center[2] = center[2]; m_coordinates[0] = leftBottom[0]; m_coordinates[1] = leftBottom[1]; m_coordinates[2] = leftBottom[2]; m_coordinates[3] = rightBottom[0]; m_coordinates[4] = rightBottom[1]; m_coordinates[5] = rightBottom[2]; m_coordinates[6] = rightTop[0]; m_coordinates[7] = rightTop[1]; m_coordinates[8] = rightTop[2]; m_coordinates[9] = leftTop[0]; m_coordinates[10] = leftTop[1]; m_coordinates[11] = leftTop[2]; const int64_t numSlices = 5; m_sliceIndices.reserve(numSlices); m_sliceOffsets.reserve(numSlices); } /** * Get the change in XYZ for the voxel ([top right] minus [bottom left]) * * @param dxyzOut * Change in XYZ from bottom left to top right */ void BrainOpenGLVolumeObliqueSliceDrawing::VoxelToDraw::getDiffXYZ(float dxyzOut[3]) const { dxyzOut[0] = m_coordinates[6] - m_coordinates[0]; dxyzOut[1] = m_coordinates[7] - m_coordinates[1]; dxyzOut[2] = m_coordinates[8] - m_coordinates[2]; } /** * Add a value from a volume slice. * * @param sliceIndex * Index of the slice. * @param sliceOffset * Offset of value in the slice. */ void BrainOpenGLVolumeObliqueSliceDrawing::VoxelToDraw::addVolumeValue(const int64_t sliceIndex, const int64_t sliceOffset) { CaretAssert(sliceIndex >= 0); CaretAssert(sliceOffset >= 0); m_sliceIndices.push_back(sliceIndex); m_sliceOffsets.push_back(sliceOffset); } connectome-workbench-1.2.3+git41-gc4c6c90/src/Brain/BrainOpenGLVolumeObliqueSliceDrawing.h000066400000000000000000000403341300200146000310610ustar00rootroot00000000000000#ifndef __BRAIN_OPEN_GL_VOLUME_OBLIQUE_SLICE_DRAWING_H__ #define __BRAIN_OPEN_GL_VOLUME_OBLIQUE_SLICE_DRAWING_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 "BrainOpenGLFixedPipeline.h" #include "CaretObject.h" #include "DisplayGroupEnum.h" #include "VolumeSliceProjectionTypeEnum.h" #include "VolumeSliceDrawingTypeEnum.h" #include "VolumeSliceViewPlaneEnum.h" namespace caret { class Brain; class BrowserTabContent; class CiftiMappableDataFile; class Matrix4x4; class ModelVolume; class ModelWholeBrain; class PaletteFile; class Plane; class VolumeMappableInterface; class BrainOpenGLVolumeObliqueSliceDrawing : public CaretObject { public: BrainOpenGLVolumeObliqueSliceDrawing(); virtual ~BrainOpenGLVolumeObliqueSliceDrawing(); void draw(BrainOpenGLFixedPipeline* fixedPipelineDrawing, BrowserTabContent* browserTabContent, std::vector& volumeDrawInfo, const VolumeSliceDrawingTypeEnum::Enum sliceDrawingType, const VolumeSliceProjectionTypeEnum::Enum sliceProjectionType, const int32_t viewport[4]); // ADD_NEW_METHODS_HERE private: /** * Holds values in the slice for a volume so that they * can be colored all at once which is more efficient than * colors singles voxels many times */ class VolumeSlice{ public: /** * Constructor * * @param volumeMappableInterface * Volume that contains the data values. */ VolumeSlice(VolumeMappableInterface* volumeMappableInterface, const int32_t mapIndex); /** * Add a value and return its index. * * @param value * Value that is added. * @return * The index for the value. */ int64_t addValue(const float value); /** * Add values for RGBA and returns its index. * * @param value * Value that is added. * @return * The index for the value. */ int64_t addValuesRGBA(const float values[4]); /** * Return RGBA colors for value using the value's index * returned by addValue(). * * @param indx * Index of the value. * @return * RGBA coloring for value. */ uint8_t* getRgbaForValueByIndex(const int64_t indx); /** * Allocate colors for the voxel values */ void allocateColors(); /** * Volume containing the values */ VolumeMappableInterface* m_volumeMappableInterface; /** * If not NULL, it is a VolumeFile */ VolumeFile* m_volumeFile; /** * If not NULL, it is a Cifti Mappable Data File */ CiftiMappableDataFile* m_ciftiMappableDataFile; /** * Map index */ int32_t m_mapIndex; /** * The voxel values for single scalar or red if RGBA volume */ std::vector m_values; /** Voxel values for green in RGBA */ std::vector m_valuesGreen; /** Voxel values for blue in RGBA */ std::vector m_valuesBlue; /** Voxel values for alpha in RGBA */ std::vector m_valuesAlpha; /** * Coloring corresponding to the values (4 components per voxel) */ std::vector m_rgba; }; /** * For each voxel, contains offsets to each layer */ class VoxelToDraw { public: VoxelToDraw(const float center[3], const double leftBottom[3], const double rightBottom[3], const double rightTop[3], const double leftTop[3]); void getDiffXYZ(float dxyzOut[3]) const; void addVolumeValue(const int64_t sliceIndex, const int64_t sliceOffset); /** * Center of voxel. */ float m_center[3]; /** * Corners of voxel */ float m_coordinates[12]; /* * Index of volume in VoxelsInSliceForVolume */ std::vector m_sliceIndices; /** * Offset in values in VoxelsInSliceForVolume */ std::vector m_sliceOffsets; }; BrainOpenGLVolumeObliqueSliceDrawing(const BrainOpenGLVolumeObliqueSliceDrawing&); BrainOpenGLVolumeObliqueSliceDrawing& operator=(const BrainOpenGLVolumeObliqueSliceDrawing&); void drawVolumeSlicesForAllStructuresView(const VolumeSliceProjectionTypeEnum::Enum sliceProjectionType, const int32_t viewport[4]); void drawVolumeSliceViewPlane(const VolumeSliceDrawingTypeEnum::Enum sliceDrawingType, const VolumeSliceProjectionTypeEnum::Enum sliceProjectionType, const VolumeSliceViewPlaneEnum::Enum sliceViewPlane, const int32_t viewport[4]); void drawVolumeSliceViewType(const VolumeSliceDrawingTypeEnum::Enum sliceDrawingType, const VolumeSliceProjectionTypeEnum::Enum sliceProjectionType, const VolumeSliceViewPlaneEnum::Enum sliceViewPlane, const int32_t viewport[4]); void drawVolumeSliceViewTypeMontage(const VolumeSliceDrawingTypeEnum::Enum sliceDrawingType, const VolumeSliceProjectionTypeEnum::Enum sliceProjectionType, const VolumeSliceViewPlaneEnum::Enum sliceViewPlane, const int32_t viewport[4]); void drawVolumeSliceViewProjection(const VolumeSliceDrawingTypeEnum::Enum sliceDrawingType, const VolumeSliceProjectionTypeEnum::Enum sliceProjectionType, const VolumeSliceViewPlaneEnum::Enum sliceViewPlane, const float sliceCoordinates[3], const int32_t viewport[4]); void drawObliqueSlice(const VolumeSliceViewPlaneEnum::Enum sliceViewPlane, Matrix4x4& transformationMatrix, const Plane& plane); void drawOrthogonalSlice(const VolumeSliceViewPlaneEnum::Enum sliceViewPlane, const float sliceCoordinates[3], const Plane& plane); void drawOrthogonalSliceWithCulling(const VolumeSliceViewPlaneEnum::Enum sliceViewPlane, const float sliceCoordinates[3], const Plane& plane); void createSlicePlaneEquation(const VolumeSliceProjectionTypeEnum::Enum sliceProjectionType, const VolumeSliceViewPlaneEnum::Enum sliceViewPlane, const float sliceCoordinates[3], Plane& planeOut); void drawAxesCrosshairsOrthoAndOblique(const VolumeSliceProjectionTypeEnum::Enum sliceProjectionType, const VolumeSliceViewPlaneEnum::Enum sliceViewPlane, const float sliceCoordinates[3], const bool drawCrosshairsFlag, const bool drawCrosshairLabelsFlag); void setVolumeSliceViewingAndModelingTransformations(const VolumeSliceProjectionTypeEnum::Enum sliceProjectionType, const VolumeSliceViewPlaneEnum::Enum sliceViewPlane, const Plane& plane, const float sliceCoordinates[3]); void getAxesColor(const VolumeSliceViewPlaneEnum::Enum sliceViewPlane, float rgbaOut[4]) const; void drawLayers(const VolumeSliceDrawingTypeEnum::Enum sliceDrawingType, const VolumeSliceProjectionTypeEnum::Enum sliceProjectionType, const VolumeSliceViewPlaneEnum::Enum sliceViewPlane, const Plane& slicePlane, const float sliceCoordinates[3]); void drawSurfaceOutline(const Plane& plane); void drawVolumeSliceFoci(const Plane& plane); void drawAxesCrosshairs(const VolumeSliceProjectionTypeEnum::Enum sliceProjectionType, const VolumeSliceDrawingTypeEnum::Enum sliceDrawingType, const VolumeSliceViewPlaneEnum::Enum sliceViewPlane, const float sliceCoordinates[3]); bool getMinMaxVoxelSpacing(const VolumeMappableInterface* volume, float& minSpacingOut, float& maxSpacingOut) const; void drawSquare(const float size); void drawOrientationAxes(const int viewport[4]); void setOrthographicProjection(const VolumeSliceViewPlaneEnum::Enum sliceViewPlane, const int viewport[4]); void drawOrthogonalSliceVoxels(const float sliceNormalVector[3], const float coordinate[3], const float rowStep[3], const float columnStep[3], const int64_t numberOfColumns, const int64_t numberOfRows, const std::vector& sliceRGBA, const int64_t validVoxelCount, const VolumeMappableInterface* volumeInterface, const int32_t volumeIndex, const int32_t mapIndex, const uint8_t sliceOpacity); void drawOrthogonalSliceVoxelsSingleQuads(const float sliceNormalVector[3], const float coordinate[3], const float rowStep[3], const float columnStep[3], const int64_t numberOfColumns, const int64_t numberOfRows, const std::vector& sliceRGBA, const VolumeMappableInterface* volumeInterface, const int32_t volumeIndex, const int32_t mapIndex, const uint8_t sliceOpacity); void drawOrthogonalSliceVoxelsQuadIndicesAndStrips(const float sliceNormalVector[3], const float coordinate[3], const float rowStep[3], const float columnStep[3], const int64_t numberOfColumns, const int64_t numberOfRows, const std::vector& sliceRGBA, const VolumeMappableInterface* volumeInterface, const int32_t volumeIndex, const int32_t mapIndex, const uint8_t sliceOpacity); bool getVoxelCoordinateBoundsAndSpacing(float boundsOut[6], float spacingOut[3]); void createObliqueTransformationMatrix(const float sliceCoordinates[3], Matrix4x4& obliqueTransformationMatrixOut); void addVoxelToIdentification(const int32_t volumeIndex, const int32_t mapIndex, const int32_t voxelI, const int32_t voxelJ, const int32_t voxelK, const float voxelDiffXYZ[3], uint8_t rgbaForColorIdentificationOut[4]); bool getVolumeDrawingViewDependentCulling(const VolumeSliceViewPlaneEnum::Enum sliceViewPlane, const float selectedSliceCoordinate, const VolumeMappableInterface* volumeFile, int64_t culledFirstVoxelIJKOut[3], int64_t culledLastVoxelIJKOut[3], float voxelDeltaXYZOut[3]); void processIdentification(); void resetIdentification(); ModelVolume* m_modelVolume; ModelWholeBrain* m_modelWholeBrain; VolumeMappableInterface* m_underlayVolume; Brain* m_brain; std::vector > m_ciftiMappableFileData; BrainOpenGLFixedPipeline* m_fixedPipelineDrawing; std::vector m_volumeDrawInfo; BrowserTabContent* m_browserTabContent; PaletteFile* m_paletteFile; DisplayGroupEnum::Enum m_displayGroup; int32_t m_tabIndex; double m_lookAtCenter[3]; double m_viewingMatrix[16]; double m_orthographicBounds[6]; std::vector m_identificationIndices; bool m_identificationModeFlag; static const int32_t IDENTIFICATION_INDICES_PER_VOXEL; // ADD_NEW_MEMBERS_HERE }; #ifdef __BRAIN_OPEN_GL_VOLUME_OBLIQUE_SLICE_DRAWING_DECLARE__ const int32_t BrainOpenGLVolumeObliqueSliceDrawing::IDENTIFICATION_INDICES_PER_VOXEL = 8; #endif // __BRAIN_OPEN_GL_VOLUME_OBLIQUE_SLICE_DRAWING_DECLARE__ } // namespace #endif //__BRAIN_OPEN_GL_VOLUME_OBLIQUE_SLICE_DRAWING_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Brain/BrainOpenGLVolumeSliceDrawing.cxx000066400000000000000000007543561300200146000301330ustar00rootroot00000000000000 /*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_VOLUME_SLICE_DRAWING_DECLARE__ #include "BrainOpenGLVolumeSliceDrawing.h" #undef __BRAIN_OPEN_GL_VOLUME_SLICE_DRAWING_DECLARE__ #include "AnnotationCoordinate.h" #include "AnnotationPointSizeText.h" #include "BoundingBox.h" #include "Brain.h" #include "BrainOpenGLAnnotationDrawingFixedPipeline.h" #include "BrainOpenGLPrimitiveDrawing.h" #include "BrainordinateRegionOfInterest.h" #include "BrowserTabContent.h" #include "CaretAssert.h" #include "CaretLogger.h" #include "CaretOpenGLInclude.h" #include "CaretPreferences.h" #include "CiftiMappableDataFile.h" #include "DeveloperFlagsEnum.h" #include "DisplayPropertiesFoci.h" #include "DisplayPropertiesLabels.h" #include "ElapsedTimer.h" #include "FociFile.h" #include "Focus.h" #include "GapsAndMargins.h" #include "GiftiLabel.h" #include "GiftiLabelTable.h" #include "GroupAndNameHierarchyModel.h" #include "IdentificationManager.h" #include "IdentificationWithColor.h" #include "IdentifiedItemVoxel.h" #include "LabelDrawingProperties.h" #include "MathFunctions.h" #include "Matrix4x4.h" #include "ModelVolume.h" #include "ModelWholeBrain.h" #include "NodeAndVoxelColoring.h" #include "SelectionItemFocusVolume.h" #include "SelectionItemVoxel.h" #include "SelectionItemVoxelEditing.h" #include "SelectionItemVoxelIdentificationSymbol.h" #include "SelectionManager.h" #include "SessionManager.h" #include "Surface.h" #include "VolumeFile.h" #include "VolumeSurfaceOutlineColorOrTabModel.h" #include "VolumeSurfaceOutlineModel.h" #include "VolumeSurfaceOutlineSetModel.h" using namespace caret; static const bool debugFlag = false; /** * \class caret::BrainOpenGLVolumeSliceDrawing * \brief Draws volume slices using OpenGL * \ingroup Brain */ /** * Constructor. */ BrainOpenGLVolumeSliceDrawing::BrainOpenGLVolumeSliceDrawing() : CaretObject() { } /** * Destructor. */ BrainOpenGLVolumeSliceDrawing::~BrainOpenGLVolumeSliceDrawing() { } /** * Draw Volume Slices or slices for ALL Stuctures View. * * @param fixedPipelineDrawing * The OpenGL drawing. * @param browserTabContent * Content of browser tab that is to be drawn. * @param volumeDrawInfo * Info on each volume layers for drawing. * @param sliceDrawingType * Type of slice drawing (montage, single) * @param sliceProjectionType * Type of projection for the slice drawing (oblique, orthogonal) * @param viewport * The viewport (region of graphics area) for drawing slices. */ void BrainOpenGLVolumeSliceDrawing::draw(BrainOpenGLFixedPipeline* fixedPipelineDrawing, BrowserTabContent* browserTabContent, std::vector& volumeDrawInfo, const VolumeSliceDrawingTypeEnum::Enum sliceDrawingType, const VolumeSliceProjectionTypeEnum::Enum sliceProjectionType, const int32_t viewport[4]) { if (volumeDrawInfo.empty()) { return; } CaretAssert(fixedPipelineDrawing); CaretAssert(browserTabContent); m_browserTabContent = browserTabContent; m_fixedPipelineDrawing = fixedPipelineDrawing; /* * No lighting for drawing slices */ m_fixedPipelineDrawing->disableLighting(); /* * Initialize class members which help reduce the number of * parameters that are passed to methods. */ m_brain = NULL; m_modelVolume = NULL; m_modelWholeBrain = NULL; if (m_browserTabContent->getDisplayedVolumeModel() != NULL) { m_modelVolume = m_browserTabContent->getDisplayedVolumeModel(); m_brain = m_modelVolume->getBrain(); } else if (m_browserTabContent->getDisplayedWholeBrainModel() != NULL) { m_modelWholeBrain = m_browserTabContent->getDisplayedWholeBrainModel(); m_brain = m_modelWholeBrain->getBrain(); } else { CaretAssertMessage(0, "Invalid model for volume slice drawing."); } CaretAssert(m_brain); m_volumeDrawInfo = volumeDrawInfo; if (m_volumeDrawInfo.empty()) { return; } 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); m_tabIndex = m_browserTabContent->getTabNumber(); /* * Cifti files are slow at getting individual voxels since they * provide no access to individual voxels. The reason is that * the data may be on a server (Dense data) and accessing a single * voxel would require requesting the entire map. So, for * each Cifti file, get the enter map. This also, eliminate multiple * requests for the same map when drawing an ALL view. */ const int32_t numVolumes = static_cast(m_volumeDrawInfo.size()); for (int32_t i = 0; i < numVolumes; i++) { std::vector ciftiMapData; m_ciftiMappableFileData.push_back(ciftiMapData); const CiftiMappableDataFile* ciftiMapFile = dynamic_cast(m_volumeDrawInfo[i].volumeFile); if (ciftiMapFile != NULL) { ciftiMapFile->getMapData(m_volumeDrawInfo[i].mapIndex, m_ciftiMappableFileData[i]); } } /** END SETUP OF MEMBERS IN PARENT CLASS BrainOpenGLVolumeSliceDrawing */ if (browserTabContent->getDisplayedVolumeModel() != NULL) { drawVolumeSliceViewPlane(sliceDrawingType, sliceProjectionType, browserTabContent->getSliceViewPlane(), viewport); } else if (browserTabContent->getDisplayedWholeBrainModel() != NULL) { drawVolumeSlicesForAllStructuresView(sliceProjectionType, viewport); } } /** * Draw volume view slices for the given view plane. * * @param sliceDrawingType * Type of slice drawing (montage, single) * @param sliceProjectionType * Type of projection for the slice drawing (oblique, orthogonal) * @param sliceViewPlane * The plane for slice drawing. * @param viewport * The viewport (region of graphics area) for drawing slices. */ void BrainOpenGLVolumeSliceDrawing::drawVolumeSliceViewPlane(const VolumeSliceDrawingTypeEnum::Enum sliceDrawingType, const VolumeSliceProjectionTypeEnum::Enum sliceProjectionType, const VolumeSliceViewPlaneEnum::Enum sliceViewPlane, const int32_t viewport[4]) { switch (sliceViewPlane) { case VolumeSliceViewPlaneEnum::ALL: { const int32_t gap = 2; const int32_t vpHalfX = viewport[2] / 2; const int32_t vpHalfY = viewport[3] / 2; /* * Draw parasagittal slice */ const int32_t paraVP[4] = { viewport[0], viewport[1] + vpHalfY + gap, vpHalfX - gap, vpHalfY - gap }; glPushMatrix(); drawVolumeSliceViewType(sliceDrawingType, sliceProjectionType, VolumeSliceViewPlaneEnum::PARASAGITTAL, paraVP); glPopMatrix(); /* * Draw coronal slice */ const int32_t coronalVP[4] = { viewport[0] + vpHalfX + gap, viewport[1] + vpHalfY + gap, vpHalfX - gap, vpHalfY - gap }; glPushMatrix(); drawVolumeSliceViewType(sliceDrawingType, sliceProjectionType, VolumeSliceViewPlaneEnum::CORONAL, coronalVP); glPopMatrix(); /* * Draw axial slice */ const int32_t axialVP[4] = { viewport[0] + vpHalfX + gap, viewport[1], vpHalfX - gap, vpHalfY - gap }; glPushMatrix(); drawVolumeSliceViewType(sliceDrawingType, sliceProjectionType, VolumeSliceViewPlaneEnum::AXIAL, 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; } } break; case VolumeSliceViewPlaneEnum::AXIAL: case VolumeSliceViewPlaneEnum::CORONAL: case VolumeSliceViewPlaneEnum::PARASAGITTAL: drawVolumeSliceViewType(sliceDrawingType, sliceProjectionType, sliceViewPlane, viewport); break; } } /** * Draw slices for the all structures view. * * @param sliceProjectionType * Type of projection for the slice drawing (oblique, orthogonal) * @param viewport * The viewport. */ void BrainOpenGLVolumeSliceDrawing::drawVolumeSlicesForAllStructuresView(const VolumeSliceProjectionTypeEnum::Enum sliceProjectionType, const int32_t viewport[4]) { m_orthographicBounds[0] = m_fixedPipelineDrawing->orthographicLeft; m_orthographicBounds[1] = m_fixedPipelineDrawing->orthographicRight; m_orthographicBounds[2] = m_fixedPipelineDrawing->orthographicBottom; m_orthographicBounds[3] = m_fixedPipelineDrawing->orthographicTop; m_orthographicBounds[4] = m_fixedPipelineDrawing->orthographicNear; m_orthographicBounds[5] = m_fixedPipelineDrawing->orthographicFar; /* * Enlarge the region */ { const float left = m_fixedPipelineDrawing->orthographicLeft; const float right = m_fixedPipelineDrawing->orthographicRight; const float bottom = m_fixedPipelineDrawing->orthographicBottom; const float top = m_fixedPipelineDrawing->orthographicTop; const float scale = 2.0; const float centerX = (left + right) / 2.0; const float dx = (right - left) / 2.0; const float newLeft = centerX - (dx * scale); const float newRight = centerX + (dx * scale); const float centerY = (bottom + top) / 2.0; const float dy = (top - bottom) / 2.0; const float newBottom = centerY - (dy * scale); const float newTop = centerY + (dy * scale); m_orthographicBounds[0] = newLeft; m_orthographicBounds[1] = newRight; m_orthographicBounds[2] = newBottom; m_orthographicBounds[3] = newTop; } const float sliceCoordinates[3] = { m_browserTabContent->getSliceCoordinateParasagittal(), m_browserTabContent->getSliceCoordinateCoronal(), m_browserTabContent->getSliceCoordinateAxial() }; if (m_browserTabContent->isSliceAxialEnabled()) { glPushMatrix(); drawVolumeSliceViewProjection(VolumeSliceDrawingTypeEnum::VOLUME_SLICE_DRAW_SINGLE, sliceProjectionType, VolumeSliceViewPlaneEnum::AXIAL, sliceCoordinates, viewport); glPopMatrix(); } if (m_browserTabContent->isSliceCoronalEnabled()) { glPushMatrix(); drawVolumeSliceViewProjection(VolumeSliceDrawingTypeEnum::VOLUME_SLICE_DRAW_SINGLE, sliceProjectionType, VolumeSliceViewPlaneEnum::CORONAL, sliceCoordinates, viewport); glPopMatrix(); } if (m_browserTabContent->isSliceParasagittalEnabled()) { glPushMatrix(); drawVolumeSliceViewProjection(VolumeSliceDrawingTypeEnum::VOLUME_SLICE_DRAW_SINGLE, sliceProjectionType, VolumeSliceViewPlaneEnum::PARASAGITTAL, sliceCoordinates, viewport); glPopMatrix(); } } /** * Draw single or montage volume view slices. * * @param sliceDrawingType * Type of slice drawing (montage, single) * @param sliceProjectionType * Type of projection for the slice drawing (oblique, orthogonal) * @param sliceViewPlane * The plane for slice drawing. * @param viewport * The viewport (region of graphics area) for drawing slices. */ void BrainOpenGLVolumeSliceDrawing::drawVolumeSliceViewType(const VolumeSliceDrawingTypeEnum::Enum sliceDrawingType, const VolumeSliceProjectionTypeEnum::Enum sliceProjectionType, const VolumeSliceViewPlaneEnum::Enum sliceViewPlane, const int32_t viewport[4]) { switch (sliceDrawingType) { case VolumeSliceDrawingTypeEnum::VOLUME_SLICE_DRAW_MONTAGE: drawVolumeSliceViewTypeMontage(sliceDrawingType, sliceProjectionType, sliceViewPlane, viewport); break; case VolumeSliceDrawingTypeEnum::VOLUME_SLICE_DRAW_SINGLE: { const float sliceCoordinates[3] = { m_browserTabContent->getSliceCoordinateParasagittal(), m_browserTabContent->getSliceCoordinateCoronal(), m_browserTabContent->getSliceCoordinateAxial() }; drawVolumeSliceViewProjection(sliceDrawingType, sliceProjectionType, sliceViewPlane, sliceCoordinates, viewport); } break; } } /** * Draw montage slices. * * @param sliceDrawingType * Type of slice drawing (montage, single) * @param sliceProjectionType * Type of projection for the slice drawing (oblique, orthogonal) * @param sliceViewPlane * The plane for slice drawing. * @param viewport * The viewport (region of graphics area) for drawing slices. */ void BrainOpenGLVolumeSliceDrawing::drawVolumeSliceViewTypeMontage(const VolumeSliceDrawingTypeEnum::Enum sliceDrawingType, const VolumeSliceProjectionTypeEnum::Enum sliceProjectionType, const VolumeSliceViewPlaneEnum::Enum sliceViewPlane, const int32_t viewport[4]) { const int32_t numRows = m_browserTabContent->getMontageNumberOfRows(); CaretAssert(numRows > 0); const int32_t numCols = m_browserTabContent->getMontageNumberOfColumns(); CaretAssert(numCols > 0); const CaretPreferences* caretPreferences = SessionManager::get()->getCaretPreferences(); const int32_t montageCoordPrecision = caretPreferences->getVolumeMontageCoordinatePrecision(); const GapsAndMargins* gapsAndMargins = m_brain->getGapsAndMargins(); // const int32_t horizontalMargin = static_cast(viewport[2] * gapsAndMargins->getVolumeMontageHorizontalGap()); // const int32_t verticalMargin = static_cast(viewport[3] * gapsAndMargins->getVolumeMontageVerticalGap()); // // const int32_t totalGapX = horizontalMargin * (numCols - 1); // const int32_t vpSizeX = (viewport[2] - totalGapX) / numCols; // const int32_t totalGapY = verticalMargin * (numRows - 1); // const int32_t vpSizeY = (viewport[3] - totalGapY) / numRows; const int32_t windowIndex = m_fixedPipelineDrawing->m_windowIndex; int32_t vpSizeY = 0; int32_t verticalMargin = 0; BrainOpenGLFixedPipeline::createSubViewportSizeAndGaps(viewport[3], gapsAndMargins->getVolumeMontageVerticalGapForWindow(windowIndex), -1, numRows, vpSizeY, verticalMargin); int32_t vpSizeX = 0; int32_t horizontalMargin = 0; BrainOpenGLFixedPipeline::createSubViewportSizeAndGaps(viewport[2], gapsAndMargins->getVolumeMontageHorizontalGapForWindow(windowIndex), -1, numCols, vpSizeX, horizontalMargin); /* * Voxel sizes for underlay volume */ float originX, originY, originZ; float x1, y1, z1; m_underlayVolume->indexToSpace(0, 0, 0, originX, originY, originZ); m_underlayVolume->indexToSpace(1, 1, 1, x1, y1, z1); float sliceThickness = 0.0; float sliceOrigin = 0.0; AString axisLetter = ""; float sliceCoordinates[3] = { m_browserTabContent->getSliceCoordinateParasagittal(), m_browserTabContent->getSliceCoordinateCoronal(), m_browserTabContent->getSliceCoordinateAxial() }; int32_t sliceIndex = -1; int32_t maximumSliceIndex = -1; int64_t dimI, dimJ, dimK, numMaps, numComponents; m_underlayVolume->getDimensions(dimI, dimJ, dimK, numMaps, numComponents); const int32_t sliceStep = m_browserTabContent->getMontageSliceSpacing(); switch (sliceViewPlane) { case VolumeSliceViewPlaneEnum::ALL: sliceIndex = -1; break; case VolumeSliceViewPlaneEnum::AXIAL: sliceIndex = m_browserTabContent->getSliceIndexAxial(m_underlayVolume); maximumSliceIndex = dimK; sliceThickness = z1 - originZ; sliceOrigin = originZ; axisLetter = "Z"; break; case VolumeSliceViewPlaneEnum::CORONAL: sliceIndex = m_browserTabContent->getSliceIndexCoronal(m_underlayVolume); maximumSliceIndex = dimJ; sliceThickness = y1 - originY; sliceOrigin = originY; axisLetter = "Y"; break; case VolumeSliceViewPlaneEnum::PARASAGITTAL: sliceIndex = m_browserTabContent->getSliceIndexParasagittal(m_underlayVolume); maximumSliceIndex = dimI; sliceThickness = x1 - originX; sliceOrigin = originX; axisLetter = "X"; break; } /* * Foreground color for slice coordinate text */ const CaretPreferences* prefs = SessionManager::get()->getCaretPreferences(); uint8_t foregroundRGBA[4]; prefs->getBackgroundAndForegroundColors()->getColorForegroundVolumeView(foregroundRGBA); foregroundRGBA[3] = 255; const bool showCoordinates = prefs->isVolumeMontageAxesCoordinatesDisplayed(); uint8_t backgroundRGBA[4]; prefs->getBackgroundAndForegroundColors()->getColorBackgroundVolumeView(backgroundRGBA); backgroundRGBA[3] = 255; /* * Determine a slice offset to selected slices is in * the center of the montage */ const int32_t numSlicesViewed = (numCols * numRows); const int32_t sliceOffset = ((numSlicesViewed / 2) * sliceStep); sliceIndex += sliceOffset; /* * Find first valid slice for montage */ while (sliceIndex >= 0) { if (sliceIndex < maximumSliceIndex) { break; } sliceIndex -= sliceStep; } if (sliceIndex >= 0) { for (int32_t i = 0; i < numRows; i++) { for (int32_t j = 0; j < numCols; j++) { if ((sliceIndex >= 0) && (sliceIndex < maximumSliceIndex)) { const int32_t vpX = (j * (vpSizeX + horizontalMargin)); const int32_t vpY = ((numRows - i - 1) * (vpSizeY + verticalMargin)); int32_t vp[4] = { viewport[0] + vpX, viewport[1] + vpY, vpSizeX, vpSizeY }; if ((vp[2] <= 0) || (vp[3] <= 0)) { continue; } const float sliceCoord = (sliceOrigin + sliceThickness * sliceIndex); switch (sliceViewPlane) { case VolumeSliceViewPlaneEnum::ALL: break; case VolumeSliceViewPlaneEnum::AXIAL: sliceCoordinates[2] = sliceCoord; break; case VolumeSliceViewPlaneEnum::CORONAL: sliceCoordinates[1] = sliceCoord; break; case VolumeSliceViewPlaneEnum::PARASAGITTAL: sliceCoordinates[0] = sliceCoord; break; } drawVolumeSliceViewProjection(sliceDrawingType, sliceProjectionType, sliceViewPlane, sliceCoordinates, vp); if (showCoordinates) { const AString coordText = (axisLetter + "=" + AString::number(sliceCoord, 'f', montageCoordPrecision) + "mm"); AnnotationPointSizeText annotationText(AnnotationAttributesDefaultTypeEnum::NORMAL); annotationText.setHorizontalAlignment(AnnotationTextAlignHorizontalEnum::RIGHT); annotationText.setVerticalAlignment(AnnotationTextAlignVerticalEnum::BOTTOM); annotationText.setFontPointSize(AnnotationTextFontPointSizeEnum::SIZE12); annotationText.setTextColor(CaretColorEnum::CUSTOM); annotationText.setCustomTextColor(foregroundRGBA); annotationText.setBackgroundColor(CaretColorEnum::CUSTOM); annotationText.setCustomBackgroundColor(backgroundRGBA); annotationText.setText(coordText); m_fixedPipelineDrawing->drawTextAtViewportCoords((vpSizeX - 5), 5, annotationText); } } sliceIndex -= sliceStep; } } } /* * Draw the axes labels for the montage view */ glViewport(viewport[0], viewport[1], viewport[2], viewport[3]); if (prefs->isVolumeAxesLabelsDisplayed()) { drawAxesCrosshairsOrthoAndOblique(sliceProjectionType, sliceViewPlane, sliceCoordinates, false, true); } } /** * Draw a slice for either projection mode (oblique, orthogonal) * * @param sliceDrawingType * Type of slice drawing (montage, single) * @param sliceProjectionType * Type of projection for the slice drawing (oblique, orthogonal) * @param sliceViewPlane * The plane for slice drawing. * @param sliceCoordinates * Coordinates of the selected slice. * @param viewport * The viewport (region of graphics area) for drawing slices. */ void BrainOpenGLVolumeSliceDrawing::drawVolumeSliceViewProjection(const VolumeSliceDrawingTypeEnum::Enum sliceDrawingType, const VolumeSliceProjectionTypeEnum::Enum sliceProjectionType, const VolumeSliceViewPlaneEnum::Enum sliceViewPlane, const float sliceCoordinates[3], const int32_t viewport[4]) { bool twoDimSliceViewFlag = false; if (m_modelVolume != NULL) { twoDimSliceViewFlag = true; } else if (m_modelWholeBrain != NULL) { /* nothing */ } else { CaretAssertMessage(0, "Invalid model type."); } if (twoDimSliceViewFlag) { glMatrixMode(GL_MODELVIEW); glLoadIdentity(); glViewport(viewport[0], viewport[1], viewport[2], viewport[3]); /* * Set the orthographic projection to fit the slice axis */ setOrthographicProjection(sliceViewPlane, viewport); } /* * Create the plane equation for the slice */ Plane slicePlane; createSlicePlaneEquation(sliceProjectionType, sliceViewPlane, sliceCoordinates, slicePlane); CaretAssert(slicePlane.isValidPlane()); if (slicePlane.isValidPlane() == false) { return; } if (twoDimSliceViewFlag) { /* * Set the viewing transformation (camera position) */ setVolumeSliceViewingAndModelingTransformations(sliceProjectionType, sliceViewPlane, slicePlane); } SelectionItemVoxel* voxelID = m_brain->getSelectionManager()->getVoxelIdentification(); SelectionItemVoxelEditing* voxelEditingID = m_brain->getSelectionManager()->getVoxelEditingIdentification(); m_fixedPipelineDrawing->applyClippingPlanes(BrainOpenGLFixedPipeline::CLIPPING_DATA_TYPE_VOLUME, StructureEnum::ALL); /* * Check for a 'selection' type mode */ bool drawVolumeSlicesFlag = true; m_identificationModeFlag = false; switch (m_fixedPipelineDrawing->mode) { case BrainOpenGLFixedPipeline::MODE_DRAWING: break; case BrainOpenGLFixedPipeline::MODE_IDENTIFICATION: if (voxelID->isEnabledForSelection() || voxelEditingID->isEnabledForSelection()) { m_identificationModeFlag = true; glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); } else { /* * Don't return. Allow other items (such as annotations) to be drawn. */ drawVolumeSlicesFlag = false; } break; case BrainOpenGLFixedPipeline::MODE_PROJECTION: return; break; } resetIdentification(); GLboolean cullFaceOn = glIsEnabled(GL_CULL_FACE); if (drawVolumeSlicesFlag) { /* * Disable culling so that both sides of the triangles/quads are drawn. */ glDisable(GL_CULL_FACE); const bool cullingSliceViewFlag = true; const bool nonCulledLpiSliceViewFlag = false; // culling only works in a view looking along an axis (any rotation and slices disappear) switch (sliceProjectionType) { case VolumeSliceProjectionTypeEnum::VOLUME_SLICE_PROJECTION_ORTHOGONAL: if (m_modelVolume != NULL) { if (cullingSliceViewFlag) { drawOrthogonalSliceWithCulling(sliceViewPlane, sliceCoordinates, slicePlane); } else { if (nonCulledLpiSliceViewFlag) { drawOrthogonalSlice_LPI_ONLY(sliceViewPlane, sliceCoordinates, slicePlane); } else { drawOrthogonalSlice(sliceViewPlane, sliceCoordinates, slicePlane); } } } else if (m_modelWholeBrain != NULL) { if (nonCulledLpiSliceViewFlag) { drawOrthogonalSlice_LPI_ONLY(sliceViewPlane, sliceCoordinates, slicePlane); } else { drawOrthogonalSlice(sliceViewPlane, sliceCoordinates, slicePlane); } } break; case VolumeSliceProjectionTypeEnum::VOLUME_SLICE_PROJECTION_OBLIQUE: { /* * Create the oblique slice transformation matrix */ Matrix4x4 obliqueTransformationMatrix; createObliqueTransformationMatrix(sliceCoordinates, obliqueTransformationMatrix); drawObliqueSlice(sliceViewPlane, obliqueTransformationMatrix, slicePlane); } break; } /* * Process selection */ if (m_identificationModeFlag) { processIdentification(); } } drawIdentificationSymbols(slicePlane); if ( ! m_identificationModeFlag) { if (slicePlane.isValidPlane()) { drawLayers(sliceDrawingType, sliceProjectionType, sliceViewPlane, slicePlane, sliceCoordinates); } } /* * Draw model space annotaitons on the volume slice */ float sliceThickness = 1.0; if ( ! m_volumeDrawInfo.empty()) { if (m_volumeDrawInfo[0].volumeFile != NULL) { float spaceX = 0.0, spaceY = 0.0, spaceZ = 0.0; m_volumeDrawInfo[0].volumeFile->getVoxelSpacing(spaceX, spaceY, spaceZ); switch (sliceViewPlane) { case VolumeSliceViewPlaneEnum::ALL: CaretAssert(0); break; case VolumeSliceViewPlaneEnum::AXIAL: sliceThickness = spaceZ; break; case VolumeSliceViewPlaneEnum::CORONAL: sliceThickness = spaceY; break; case VolumeSliceViewPlaneEnum::PARASAGITTAL: sliceThickness = spaceX; break; } } } 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, sliceThickness); m_fixedPipelineDrawing->disableClippingPlanes(); if (cullFaceOn) { glEnable(GL_CULL_FACE); } } /** * Draw an oblique slice. * * @param sliceViewPlane * The plane for slice drawing. * @param transformationMatrix * The for oblique viewing. * @param plane * Plane equation for the selected slice. */ void BrainOpenGLVolumeSliceDrawing::drawObliqueSlice(const VolumeSliceViewPlaneEnum::Enum sliceViewPlane, Matrix4x4& transformationMatrix, const Plane& plane) { /* * When performing voxel identification for editing voxels, * we need to draw EVERY voxel since the user may click * regions where the voxels are "off". */ float voxelEditingValue = 1.0; VolumeFile* voxelEditingVolumeFile = NULL; bool volumeEditingDrawAllVoxelsFlag = false; if (m_identificationModeFlag) { SelectionItemVoxelEditing* voxelEditID = m_brain->getSelectionManager()->getVoxelEditingIdentification(); if (voxelEditID->isEnabledForSelection()) { voxelEditingVolumeFile = voxelEditID->getVolumeFileForEditing(); if (voxelEditingVolumeFile != NULL) { volumeEditingDrawAllVoxelsFlag = true; if (voxelEditingVolumeFile->isMappedWithLabelTable()) { if (voxelEditingVolumeFile->getNumberOfMaps() > 0) { voxelEditingValue = voxelEditingVolumeFile->getMapLabelTable(0)->getUnassignedLabelKey(); } } } } } const bool obliqueSliceModeThreeDimFlag = false; float m[16]; glGetFloatv(GL_MODELVIEW_MATRIX, m); Matrix4x4 tm; tm.setMatrixFromOpenGL(m); const int32_t numVolumes = static_cast(m_volumeDrawInfo.size()); /* * Get the maximum bounds of the voxels from all slices * and the smallest voxel spacing */ float voxelBounds[6]; float voxelSpacing[3]; if (false == getVoxelCoordinateBoundsAndSpacing(voxelBounds, voxelSpacing)) { return; } float voxelSize = std::min(voxelSpacing[0], std::min(voxelSpacing[1], voxelSpacing[2])); /* * Use a larger voxel size for the 3D view in volume slice viewing * since it draws all three slices and this takes time */ if (obliqueSliceModeThreeDimFlag) { voxelSize *= 3.0; } /* * Look at point is in center of volume */ float translation[3]; m_browserTabContent->getTranslation(translation); float viewOffsetX = 0.0; float viewOffsetY = 0.0; switch (sliceViewPlane) { case VolumeSliceViewPlaneEnum::ALL: CaretAssert(0); break; case VolumeSliceViewPlaneEnum::AXIAL: viewOffsetX = (m_lookAtCenter[0] + translation[0]); viewOffsetY = (m_lookAtCenter[1] + translation[1]); break; case VolumeSliceViewPlaneEnum::CORONAL: viewOffsetX = (m_lookAtCenter[0] + translation[0]); viewOffsetY = (m_lookAtCenter[2] + translation[2]); break; case VolumeSliceViewPlaneEnum::PARASAGITTAL: viewOffsetX = (m_lookAtCenter[1] + translation[1]); viewOffsetY = (m_lookAtCenter[2] + translation[2]); break; } float minScreenX = m_orthographicBounds[0] - viewOffsetX; float maxScreenX = m_orthographicBounds[1] - viewOffsetX; float minScreenY = m_orthographicBounds[2] - viewOffsetY; float maxScreenY = m_orthographicBounds[3] - viewOffsetY; /* * Get origin voxel IJK */ const float zeroXYZ[3] = { 0.0, 0.0, 0.0 }; int64_t originIJK[3]; m_volumeDrawInfo[0].volumeFile->enclosingVoxel(zeroXYZ[0], zeroXYZ[1], zeroXYZ[2], originIJK[0], originIJK[1], originIJK[2]); /* * Get XYZ center of origin Voxel */ float originVoxelXYZ[3]; m_volumeDrawInfo[0].volumeFile->indexToSpace(originIJK, originVoxelXYZ); float actualOrigin[3]; m_volumeDrawInfo[0].volumeFile->indexToSpace(originIJK, actualOrigin); float screenOffsetX = 0.0; float screenOffsetY = 0.0; float originOffsetX = 0.0; float originOffsetY = 0.0; switch (sliceViewPlane) { case VolumeSliceViewPlaneEnum::ALL: CaretAssert(0); break; case VolumeSliceViewPlaneEnum::AXIAL: screenOffsetX = m_lookAtCenter[0]; screenOffsetY = m_lookAtCenter[1]; originOffsetX = actualOrigin[0]; originOffsetY = actualOrigin[1]; break; case VolumeSliceViewPlaneEnum::CORONAL: screenOffsetX = m_lookAtCenter[0]; screenOffsetY = m_lookAtCenter[2]; originOffsetX = actualOrigin[0]; originOffsetY = actualOrigin[2]; break; case VolumeSliceViewPlaneEnum::PARASAGITTAL: screenOffsetX = m_lookAtCenter[1]; screenOffsetY = m_lookAtCenter[2]; originOffsetX = actualOrigin[1]; originOffsetY = actualOrigin[2]; break; } const int32_t alignVoxelsFlag = 1; if (alignVoxelsFlag == 1) { /* * Adjust for when selected slices are not at the origin */ const float xOffset = MathFunctions::remainder(screenOffsetX, voxelSize); const float yOffset = MathFunctions::remainder(screenOffsetY, voxelSize); originOffsetX -= xOffset; originOffsetY -= yOffset; const int64_t numVoxelsToLeft = static_cast(MathFunctions::round(minScreenX + originOffsetX) / voxelSize); const int64_t numVoxelsToRight = static_cast(MathFunctions::round(maxScreenX + originOffsetX) / voxelSize); const int64_t numVoxelsToBottom = static_cast(MathFunctions::round(minScreenY + originOffsetY) / voxelSize); const int64_t numVoxelsToTop = static_cast(MathFunctions::round(maxScreenY + originOffsetY)/ voxelSize); const float halfVoxel = voxelSize / 2.0; const float firstVoxelCenterX = (numVoxelsToLeft * voxelSize) + originOffsetX; const float lastVoxelCenterX = (numVoxelsToRight * voxelSize) + originOffsetX; const float firstVoxelCenterY = (numVoxelsToBottom * voxelSize) + originOffsetY; const float lastVoxelCenterY = (numVoxelsToTop * voxelSize) + originOffsetY; float newMinScreenX = firstVoxelCenterX - halfVoxel; float newMaxScreenX = lastVoxelCenterX + halfVoxel; float newMinScreenY = firstVoxelCenterY - halfVoxel; float newMaxScreenY = lastVoxelCenterY + halfVoxel; if (debugFlag) { const AString msg2 = ("Origin Voxel Coordinate: (" + AString::fromNumbers(actualOrigin, 3, ",") + "\n Oblique Screen X: (" + AString::number(minScreenX) + "," + AString::number(maxScreenX) + ") Y: (" + AString::number(minScreenY) + "," + AString::number(maxScreenY) + ")\nNew X: (" + AString::number(newMinScreenX) + "," + AString::number(newMaxScreenX) + ") Y: (" + AString::number(newMinScreenY) + "," + AString::number(newMaxScreenY) + ") Diff: (" + AString::number((newMaxScreenX - newMinScreenX) / voxelSize) + "," + AString::number((newMaxScreenY - newMinScreenY) / voxelSize) + ")"); std::cout << qPrintable(msg2) << std::endl; } minScreenX = newMinScreenX; maxScreenX = newMaxScreenX; minScreenY = newMinScreenY; maxScreenY = newMaxScreenY; } if (alignVoxelsFlag == 2) { const float quarterVoxelSize = voxelSize / 4.0; float newMinScreenX = (static_cast(minScreenX / voxelSize) * voxelSize) + quarterVoxelSize; float newMaxScreenX = (static_cast(maxScreenX / voxelSize) * voxelSize) - quarterVoxelSize; float newMinScreenY = (static_cast(minScreenY / voxelSize) * voxelSize) + quarterVoxelSize; float newMaxScreenY = (static_cast(maxScreenY / voxelSize) * voxelSize) - quarterVoxelSize; minScreenX = newMinScreenX; maxScreenX = newMaxScreenX; minScreenY = newMinScreenY; maxScreenY = newMaxScreenY; } /* * Set the corners of the screen for the respective view */ float bottomLeft[3]; float bottomRight[3]; float topRight[3]; float topLeft[3]; switch (sliceViewPlane) { case VolumeSliceViewPlaneEnum::ALL: CaretAssert(0); break; case VolumeSliceViewPlaneEnum::AXIAL: bottomLeft[0] = minScreenX; bottomLeft[1] = minScreenY; bottomLeft[2] = 0.0; bottomRight[0] = maxScreenX; bottomRight[1] = minScreenY; bottomRight[2] = 0.0; topRight[0] = maxScreenX; topRight[1] = maxScreenY; topRight[2] = 0.0; topLeft[0] = minScreenX; topLeft[1] = maxScreenY; topLeft[2] = 0.0; break; case VolumeSliceViewPlaneEnum::CORONAL: bottomLeft[0] = minScreenX; bottomLeft[1] = 0.0; bottomLeft[2] = minScreenY; bottomRight[0] = maxScreenX; bottomRight[1] = 0.0; bottomRight[2] = minScreenY; topRight[0] = maxScreenX; topRight[1] = 0.0; topRight[2] = maxScreenY; topLeft[0] = minScreenX; topLeft[1] = 0.0; topLeft[2] = maxScreenY; break; case VolumeSliceViewPlaneEnum::PARASAGITTAL: bottomLeft[0] = 0.0; bottomLeft[1] = minScreenX; bottomLeft[2] = minScreenY; bottomRight[0] = 0.0; bottomRight[1] = maxScreenX; bottomRight[2] = minScreenY; topRight[0] = 0.0; topRight[1] = maxScreenX; topRight[2] = maxScreenY; topLeft[0] = 0.0; topLeft[1] = minScreenX; topLeft[2] = maxScreenY; break; } /* * Transform the corners of the screen into model coordinates */ transformationMatrix.multiplyPoint3(bottomLeft); transformationMatrix.multiplyPoint3(bottomRight); transformationMatrix.multiplyPoint3(topRight); transformationMatrix.multiplyPoint3(topLeft); if (debugFlag) { const double bottomDist = MathFunctions::distance3D(bottomLeft, bottomRight); const double topDist = MathFunctions::distance3D(topLeft, topRight); const double bottomVoxels = bottomDist / voxelSize; const double topVoxels = topDist / voxelSize; const AString msg = ("Bottom Dist: " + AString::number(bottomDist) + " voxel size: " + AString::number(bottomVoxels) + " Top Dist: " + AString::number(bottomDist) + " voxel size: " + AString::number(topVoxels)); std::cout << qPrintable(msg) << std::endl; } if (debugFlag) { m_fixedPipelineDrawing->setLineWidth(3.0); glColor3f(1.0, 0.0, 0.0); glBegin(GL_LINE_LOOP); glVertex3fv(bottomLeft); glVertex3fv(bottomRight); glVertex3fv(topRight); glVertex3fv(topLeft); glEnd(); } /* * Unit vector and distance in model coords along left side of screen */ double bottomLeftToTopLeftUnitVector[3] = { topLeft[0] - bottomLeft[0], topLeft[1] - bottomLeft[1], topLeft[2] - bottomLeft[2], }; MathFunctions::normalizeVector(bottomLeftToTopLeftUnitVector); const double bottomLeftToTopLeftDistance = MathFunctions::distance3D(bottomLeft, topLeft); /* * Unit vector and distance in model coords along right side of screen */ double bottomRightToTopRightUnitVector[3] = { topRight[0] - bottomRight[0], topRight[1] - bottomRight[1], topRight[2] - bottomRight[2] }; MathFunctions::normalizeVector(bottomRightToTopRightUnitVector); const double bottomRightToTopRightDistance = MathFunctions::distance3D(bottomRight, topRight); /* * For fastest coloring, need to color data values as a group */ std::vector volumeSlices; for (int32_t i = 0; i < numVolumes; i++) { volumeSlices.push_back(VolumeSlice(m_volumeDrawInfo[i].volumeFile, m_volumeDrawInfo[i].mapIndex)); } bool showFirstVoxelCoordFlag = debugFlag; /* * Track voxels that will be drawn */ std::vector voxelsToDraw; if ((bottomLeftToTopLeftDistance > 0) && (bottomRightToTopRightDistance > 0)) { const double bottomLeftToTopLeftStep = voxelSize; const double numLeftSteps = (bottomLeftToTopLeftDistance / bottomLeftToTopLeftStep); const double bottomRightToTopRightStep = (bottomRightToTopRightDistance / numLeftSteps); const double dtVertical = bottomLeftToTopLeftStep / bottomLeftToTopLeftDistance; /* * Voxels are drawn in rows, left to right, across the screen, * starting at the bottom. */ double leftEdgeBottomCoord[3]; double leftEdgeTopCoord[3]; double rightEdgeBottomCoord[3]; double rightEdgeTopCoord[3]; for (double tVertical = 0.0, dLeft = 0.0, dRight = 0.0; tVertical < 1.0; tVertical += dtVertical, dLeft += bottomLeftToTopLeftStep, dRight += bottomRightToTopRightStep) { /* * Coordinate on left edge at BOTTOM of current row */ leftEdgeBottomCoord[0] = bottomLeft[0] + (dLeft * bottomLeftToTopLeftUnitVector[0]); leftEdgeBottomCoord[1] = bottomLeft[1] + (dLeft * bottomLeftToTopLeftUnitVector[1]); leftEdgeBottomCoord[2] = bottomLeft[2] + (dLeft * bottomLeftToTopLeftUnitVector[2]); /* * Coordinate on right edge at BOTTOM of current row */ rightEdgeBottomCoord[0] = bottomRight[0] + (dRight * bottomRightToTopRightUnitVector[0]); rightEdgeBottomCoord[1] = bottomRight[1] + (dRight * bottomRightToTopRightUnitVector[1]); rightEdgeBottomCoord[2] = bottomRight[2] + (dRight * bottomRightToTopRightUnitVector[2]); /* * Coordinate on left edge at TOP of current row */ leftEdgeTopCoord[0] = bottomLeft[0] + ((dLeft + bottomLeftToTopLeftStep) * bottomLeftToTopLeftUnitVector[0]); leftEdgeTopCoord[1] = bottomLeft[1] + ((dLeft + bottomLeftToTopLeftStep) * bottomLeftToTopLeftUnitVector[1]); leftEdgeTopCoord[2] = bottomLeft[2] + ((dLeft + bottomLeftToTopLeftStep) * bottomLeftToTopLeftUnitVector[2]); /* * Coordinate on right edge at TOP of current row */ rightEdgeTopCoord[0] = bottomRight[0] + ((dRight + bottomRightToTopRightStep) * bottomRightToTopRightUnitVector[0]); rightEdgeTopCoord[1] = bottomRight[1] + ((dRight + bottomRightToTopRightStep) * bottomRightToTopRightUnitVector[1]); rightEdgeTopCoord[2] = bottomRight[2] + ((dRight + bottomRightToTopRightStep) * bottomRightToTopRightUnitVector[2]); /* * Determine change in XYZ per voxel along the bottom of the current row */ const double bottomVoxelEdgeDistance = MathFunctions::distance3D(leftEdgeBottomCoord, rightEdgeBottomCoord); double bottomEdgeUnitVector[3]; MathFunctions::createUnitVector(leftEdgeBottomCoord, rightEdgeBottomCoord, bottomEdgeUnitVector); const double numVoxelsInRowFloat = bottomVoxelEdgeDistance / voxelSize; const int64_t numVoxelsInRow = MathFunctions::round(numVoxelsInRowFloat); const double bottomEdgeVoxelSize = bottomVoxelEdgeDistance / numVoxelsInRow; const double bottomVoxelEdgeDX = bottomEdgeVoxelSize * bottomEdgeUnitVector[0]; const double bottomVoxelEdgeDY = bottomEdgeVoxelSize * bottomEdgeUnitVector[1]; const double bottomVoxelEdgeDZ = bottomEdgeVoxelSize * bottomEdgeUnitVector[2]; /* * Determine change in XYZ per voxel along top of the current row */ const double topVoxelEdgeDistance = MathFunctions::distance3D(leftEdgeTopCoord, rightEdgeTopCoord); double topEdgeUnitVector[3]; MathFunctions::createUnitVector(leftEdgeTopCoord, rightEdgeTopCoord, topEdgeUnitVector); const double topEdgeVoxelSize = topVoxelEdgeDistance / numVoxelsInRow; const double topVoxelEdgeDX = topEdgeVoxelSize * topEdgeUnitVector[0]; const double topVoxelEdgeDY = topEdgeVoxelSize * topEdgeUnitVector[1]; const double topVoxelEdgeDZ = topEdgeVoxelSize * topEdgeUnitVector[2]; /* * Initialize bottom and top left coordinate of first voxel in row */ double bottomLeftVoxelCoord[3] = { leftEdgeBottomCoord[0], leftEdgeBottomCoord[1], leftEdgeBottomCoord[2] }; double topLeftVoxelCoord[3] = { leftEdgeTopCoord[0], leftEdgeTopCoord[1], leftEdgeTopCoord[2] }; const bool useInterpolatedVoxel = true; /* * Draw the voxels in the row */ for (int64_t i = 0; i < numVoxelsInRow; i++) { /* * Top right corner of voxel */ const double topRightVoxelCoord[3] = { topLeftVoxelCoord[0] + topVoxelEdgeDX, topLeftVoxelCoord[1] + topVoxelEdgeDY, topLeftVoxelCoord[2] + topVoxelEdgeDZ }; const float voxelCenter[3] = { (bottomLeftVoxelCoord[0] + topRightVoxelCoord[0]) * 0.5, (bottomLeftVoxelCoord[1] + topRightVoxelCoord[1]) * 0.5, (bottomLeftVoxelCoord[2] + topRightVoxelCoord[2]) * 0.5 }; bool printOriginVoxelInfo = false; if (debugFlag) { switch (sliceViewPlane) { case VolumeSliceViewPlaneEnum::ALL: break; case VolumeSliceViewPlaneEnum::AXIAL: if (showFirstVoxelCoordFlag) { const float dist = voxelCenter[0] - actualOrigin[0]; const AString msg = ("First Voxel Center: " + AString::fromNumbers(voxelCenter, 3, ",") + " Dist from origin voxel in X: " + AString::number(dist) + " Number of voxels between: " + AString::number(dist / voxelSize)); std::cout << qPrintable(msg) << std::endl; showFirstVoxelCoordFlag = false; } if ((bottomLeftVoxelCoord[0] < actualOrigin[0]) && (topRightVoxelCoord[0] > actualOrigin[0])) { if ((bottomLeftVoxelCoord[1] < actualOrigin[1]) && (topRightVoxelCoord[1] > actualOrigin[1])) { printOriginVoxelInfo = true; } } break; case VolumeSliceViewPlaneEnum::CORONAL: if (showFirstVoxelCoordFlag) { const float dist = voxelCenter[0] - actualOrigin[0]; const AString msg = ("First Voxel Center: " + AString::fromNumbers(voxelCenter, 3, ",") + " Dist from origin voxel in X: " + AString::number(dist) + " Number of voxels between: " + AString::number(dist / voxelSize)); std::cout << qPrintable(msg) << std::endl; showFirstVoxelCoordFlag = false; } if ((bottomLeftVoxelCoord[0] < actualOrigin[0]) && (topRightVoxelCoord[0] > actualOrigin[0])) { if ((bottomLeftVoxelCoord[2] < actualOrigin[2]) && (topRightVoxelCoord[2] > actualOrigin[2])) { printOriginVoxelInfo = true; } } break; case VolumeSliceViewPlaneEnum::PARASAGITTAL: if (showFirstVoxelCoordFlag) { const float dist = voxelCenter[1] - actualOrigin[1]; const AString msg = ("First Voxel Center: " + AString::fromNumbers(voxelCenter, 3, ",") + " Dist from origin voxel in Y: " + AString::number(dist) + " Number of voxels between: " + AString::number(dist / voxelSize)); std::cout << qPrintable(msg) << std::endl; showFirstVoxelCoordFlag = false; } if ((bottomLeftVoxelCoord[1] < actualOrigin[1]) && (topRightVoxelCoord[1] > actualOrigin[1])) { if ((bottomLeftVoxelCoord[2] < actualOrigin[2]) && (topRightVoxelCoord[2] > actualOrigin[2])) { printOriginVoxelInfo = true; } } break; } } if (printOriginVoxelInfo) { const AString msg = ("Origin voxel center when drawn is " + AString::fromNumbers(voxelCenter, 3, ",") + " but should be " + AString::fromNumbers(actualOrigin, 3, ",") + " Voxel Corners: (" + AString::fromNumbers(bottomLeftVoxelCoord, 3, ",") + ") (" + AString::fromNumbers(topRightVoxelCoord, 3, ",") + ")"); std::cout << qPrintable(msg) << std::endl; } /* * Loop through the volumes selected as overlays. */ VoxelToDraw* voxelDrawingInfo = NULL; for (int32_t iVol = 0; iVol < numVolumes; iVol++) { const BrainOpenGLFixedPipeline::VolumeDrawInfo& vdi = m_volumeDrawInfo[iVol]; const VolumeMappableInterface* volInter = vdi.volumeFile; const VolumeFile* volumeFile = volumeSlices[iVol].m_volumeFile; float value = 0; bool valueValidFlag = false; bool isPaletteMappedVolumeFile = false; if (volumeFile != NULL) { if (volumeFile->isMappedWithPalette()) { isPaletteMappedVolumeFile = true; } } const CiftiMappableDataFile* ciftiMappableFile = volumeSlices[iVol].m_ciftiMappableDataFile; if (useInterpolatedVoxel && isPaletteMappedVolumeFile) { value = volumeFile->interpolateValue(voxelCenter, VolumeFile::CUBIC, &valueValidFlag, vdi.mapIndex); } else if (ciftiMappableFile != NULL) { const int64_t voxelOffset = ciftiMappableFile->getMapDataOffsetForVoxelAtCoordinate(voxelCenter, vdi.mapIndex); if (voxelOffset >= 0) { CaretAssertVectorIndex(m_ciftiMappableFileData, iVol); const std::vector& data = m_ciftiMappableFileData[iVol]; CaretAssertVectorIndex(data, voxelOffset); value = data[voxelOffset]; valueValidFlag = true; } } else { value = volInter->getVoxelValue(voxelCenter, &valueValidFlag, vdi.mapIndex); } /* * Need to draw all voxels when editing */ if (volumeEditingDrawAllVoxelsFlag) { if (! valueValidFlag) { if (volumeFile != NULL) { if (volumeFile == voxelEditingVolumeFile) { value = voxelEditingValue; valueValidFlag = true; } } } } if (valueValidFlag) { if (voxelDrawingInfo == NULL) { /* * Bottom right corner of voxel */ const double bottomRightVoxelCoord[3] = { bottomLeftVoxelCoord[0] + bottomVoxelEdgeDX, bottomLeftVoxelCoord[1] + bottomVoxelEdgeDY, bottomLeftVoxelCoord[2] + bottomVoxelEdgeDZ }; /* * Top right corner of voxel */ const double topRightVoxelCoord[3] = { topLeftVoxelCoord[0] + topVoxelEdgeDX, topLeftVoxelCoord[1] + topVoxelEdgeDY, topLeftVoxelCoord[2] + topVoxelEdgeDZ }; voxelDrawingInfo = new VoxelToDraw(voxelCenter, bottomLeftVoxelCoord, bottomRightVoxelCoord, topRightVoxelCoord, topLeftVoxelCoord); voxelsToDraw.push_back(voxelDrawingInfo); } const int64_t offset = volumeSlices[iVol].addValue(value); voxelDrawingInfo->addVolumeValue(iVol, offset); } } /* * Move to the next voxel in the row */ bottomLeftVoxelCoord[0] += bottomVoxelEdgeDX; bottomLeftVoxelCoord[1] += bottomVoxelEdgeDY; bottomLeftVoxelCoord[2] += bottomVoxelEdgeDZ; topLeftVoxelCoord[0] += topVoxelEdgeDX; topLeftVoxelCoord[1] += topVoxelEdgeDY; topLeftVoxelCoord[2] += topVoxelEdgeDZ; } } } const int32_t browserTabIndex = m_browserTabContent->getTabNumber(); const DisplayPropertiesLabels* displayPropertiesLabels = m_brain->getDisplayPropertiesLabels(); const DisplayGroupEnum::Enum displayGroup = displayPropertiesLabels->getDisplayGroupForTab(browserTabIndex); /* * Color voxel values */ for (int32_t i = 0; i < numVolumes; i++) { const int64_t numValues = static_cast(volumeSlices[i].m_values.size()); if (numValues > 0) { volumeSlices[i].allocateColors(); VolumeMappableInterface* volume = volumeSlices[i].m_volumeMappableInterface; CaretMappableDataFile* mappableFile = dynamic_cast(volume); VolumeFile* volumeFile = volumeSlices[i].m_volumeFile; CaretAssert(mappableFile); const int32_t mapIndex = volumeSlices[i].m_mapIndex; const float* values = &volumeSlices[i].m_values[0]; uint8_t* rgba = &volumeSlices[i].m_rgba[0]; if (volumeEditingDrawAllVoxelsFlag && (volumeFile == voxelEditingVolumeFile)) { for (int64_t i = 0; i < numValues; i++) { const int64_t i4 = i * 4; rgba[i4] = 255; rgba[i4+1] = 255; rgba[i4+2] = 255; rgba[i4+3] = 255; } } 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); } } else if (mappableFile->isMappedWithLabelTable()) { GiftiLabelTable* labelTable = mappableFile->getMapLabelTable(mapIndex); NodeAndVoxelColoring::colorIndicesWithLabelTableForDisplayGroupTab(labelTable, values, numValues, displayGroup, browserTabIndex, rgba); } else { CaretAssert(0); } } } const int64_t numVoxelsToDraw = static_cast(voxelsToDraw.size()); /* * quadCoords is the coordinates for all four corners of a 'quad' * that is used to draw a voxel. quadRGBA is the colors for each * voxel drawn as a 'quad'. */ std::vector quadCoordsVector; std::vector quadNormalsVector; std::vector quadRGBAsVector; /* * Reserve space to avoid reallocations */ const int64_t coordinatesPerQuad = 4; const int64_t componentsPerCoordinate = 3; const int64_t colorComponentsPerCoordinate = 4; quadCoordsVector.resize(numVoxelsToDraw * coordinatesPerQuad * componentsPerCoordinate); quadNormalsVector.resize(quadCoordsVector.size()); quadRGBAsVector.resize(numVoxelsToDraw * coordinatesPerQuad * colorComponentsPerCoordinate); int64_t coordOffset = 0; int64_t normalOffset = 0; int64_t rgbaOffset = 0; float* quadCoords = &quadCoordsVector[0]; float* quadNormals = &quadNormalsVector[0]; uint8_t* quadRGBAs = &quadRGBAsVector[0]; for (int64_t iVox = 0; iVox < numVoxelsToDraw; iVox++) { CaretAssertVectorIndex(voxelsToDraw, iVox); VoxelToDraw* vtd = voxelsToDraw[iVox]; CaretAssert(vtd); uint8_t voxelRGBA[4] = { 0, 0, 0, 0 }; const int32_t numSlicesForVoxel = static_cast(vtd->m_sliceIndices.size()); for (int32_t iSlice = 0; iSlice < numSlicesForVoxel; iSlice++) { CaretAssertVectorIndex(vtd->m_sliceIndices, iSlice); CaretAssertVectorIndex(vtd->m_sliceOffsets, iSlice); const int32_t sliceIndex = vtd->m_sliceIndices[iSlice]; const int64_t voxelOffset = vtd->m_sliceOffsets[iSlice]; 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) { 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], voxelI, voxelJ, voxelK); if (volMap->indexValid(voxelI, voxelJ, voxelK)) { float diffXYZ[3]; vtd->getDiffXYZ(diffXYZ); addVoxelToIdentification(sliceIndex, volumeSlices[sliceIndex].m_mapIndex, voxelI, voxelJ, voxelK, diffXYZ, voxelRGBA); } } } } if (voxelRGBA[3] > 0) { float sliceNormalVector[3]; plane.getNormalVector(sliceNormalVector); CaretAssertVectorIndex(quadRGBAsVector, rgbaOffset + 3); quadRGBAs[rgbaOffset] = voxelRGBA[0]; quadRGBAs[rgbaOffset+1] = voxelRGBA[1]; quadRGBAs[rgbaOffset+2] = voxelRGBA[2]; quadRGBAs[rgbaOffset+3] = voxelRGBA[3]; rgbaOffset += 4; CaretAssertVectorIndex(quadNormalsVector, normalOffset + 2); quadNormals[normalOffset] = sliceNormalVector[0]; quadNormals[normalOffset+1] = sliceNormalVector[1]; quadNormals[normalOffset+2] = sliceNormalVector[2]; normalOffset += 3; CaretAssertVectorIndex(quadRGBAsVector, rgbaOffset + 3); quadRGBAs[rgbaOffset] = voxelRGBA[0]; quadRGBAs[rgbaOffset+1] = voxelRGBA[1]; quadRGBAs[rgbaOffset+2] = voxelRGBA[2]; quadRGBAs[rgbaOffset+3] = voxelRGBA[3]; rgbaOffset += 4; CaretAssertVectorIndex(quadNormalsVector, normalOffset + 2); quadNormals[normalOffset] = sliceNormalVector[0]; quadNormals[normalOffset+1] = sliceNormalVector[1]; quadNormals[normalOffset+2] = sliceNormalVector[2]; normalOffset += 3; CaretAssertVectorIndex(quadRGBAsVector, rgbaOffset + 3); quadRGBAs[rgbaOffset] = voxelRGBA[0]; quadRGBAs[rgbaOffset+1] = voxelRGBA[1]; quadRGBAs[rgbaOffset+2] = voxelRGBA[2]; quadRGBAs[rgbaOffset+3] = voxelRGBA[3]; rgbaOffset += 4; CaretAssertVectorIndex(quadNormalsVector, normalOffset + 2); quadNormals[normalOffset] = sliceNormalVector[0]; quadNormals[normalOffset+1] = sliceNormalVector[1]; quadNormals[normalOffset+2] = sliceNormalVector[2]; normalOffset += 3; CaretAssertVectorIndex(quadRGBAsVector, rgbaOffset + 3); quadRGBAs[rgbaOffset] = voxelRGBA[0]; quadRGBAs[rgbaOffset+1] = voxelRGBA[1]; quadRGBAs[rgbaOffset+2] = voxelRGBA[2]; quadRGBAs[rgbaOffset+3] = voxelRGBA[3]; rgbaOffset += 4; CaretAssertVectorIndex(quadNormalsVector, normalOffset + 2); quadNormals[normalOffset] = sliceNormalVector[0]; quadNormals[normalOffset+1] = sliceNormalVector[1]; quadNormals[normalOffset+2] = sliceNormalVector[2]; normalOffset += 3; CaretAssertVectorIndex(quadCoordsVector, coordOffset + 11); for (int32_t iq = 0; iq < 12; iq++) { quadCoords[coordOffset + iq] = vtd->m_coordinates[iq]; } coordOffset += 12; } } quadCoordsVector.resize(coordOffset); quadNormalsVector.resize(normalOffset); quadRGBAsVector.resize(rgbaOffset); for (std::vector::iterator iter = voxelsToDraw.begin(); iter != voxelsToDraw.end(); iter++) { VoxelToDraw* vtd = *iter; delete vtd; } voxelsToDraw.clear(); if ( ! quadCoordsVector.empty()) { glPushMatrix(); BrainOpenGLPrimitiveDrawing::drawQuads(quadCoordsVector, quadNormalsVector, quadRGBAsVector); glPopMatrix(); } } /** * Draw an orthogonal slice. * * NOTE: THIS METHOD ONLY DRAWS CORRECTLY IF THE VOLUME IS IN AN LPI OR RPI ORIENTATION. * * @param sliceViewPlane * The plane for slice drawing. * @param sliceCoordinates * Coordinates of the selected slice. * @param plane * Plane equation for the selected slice. */ void BrainOpenGLVolumeSliceDrawing::drawOrthogonalSlice_LPI_ONLY(const VolumeSliceViewPlaneEnum::Enum sliceViewPlane, const float sliceCoordinates[3], const Plane& plane) { const int32_t browserTabIndex = m_browserTabContent->getTabNumber(); const DisplayPropertiesLabels* displayPropertiesLabels = m_brain->getDisplayPropertiesLabels(); const DisplayGroupEnum::Enum displayGroup = displayPropertiesLabels->getDisplayGroupForTab(browserTabIndex); /* * 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); /* * Flat shading voxels not interpolated */ glShadeModel(GL_FLAT); CaretAssert(plane.isValidPlane()); if (plane.isValidPlane() == false) { return; } /* * Compute coordinate of point in center of first slice */ float selectedSliceCoordinate = 0.0; float sliceNormalVector[3] = { 0.0, 0.0, 0.0 }; plane.getNormalVector(sliceNormalVector); switch (sliceViewPlane) { case VolumeSliceViewPlaneEnum::ALL: CaretAssert(0); break; case VolumeSliceViewPlaneEnum::AXIAL: selectedSliceCoordinate = sliceCoordinates[2]; break; case VolumeSliceViewPlaneEnum::CORONAL: selectedSliceCoordinate = sliceCoordinates[1]; break; case VolumeSliceViewPlaneEnum::PARASAGITTAL: selectedSliceCoordinate = sliceCoordinates[0]; break; } /* * Holds colors for voxels in the slice * Outside of loop to minimize allocations * It is faster to make one call to * NodeAndVoxelColoring::colorScalarsWithPalette() with * all voxels in the slice than it is to call it * separately for each voxel. */ std::vector sliceVoxelsRgbaVector; /* * Draw each of the volumes separately so that each * is drawn with the correct voxel slices. */ const int32_t numberOfVolumesToDraw = static_cast(m_volumeDrawInfo.size()); for (int32_t iVol = 0; iVol < numberOfVolumesToDraw; iVol++) { const BrainOpenGLFixedPipeline::VolumeDrawInfo& volInfo = m_volumeDrawInfo[iVol]; const VolumeMappableInterface* volumeFile = volInfo.volumeFile; int64_t dimI, dimJ, dimK, numMaps, numComponents; volumeFile->getDimensions(dimI, dimJ, dimK, numMaps, numComponents); const int64_t mapIndex = volInfo.mapIndex; float originX, originY, originZ; volumeFile->indexToSpace(0, 0, 0, originX, originY, originZ); float x1, y1, z1; volumeFile->indexToSpace(1, 1, 1, x1, y1, z1); const float voxelStepX = x1 - originX; const float voxelStepY = y1 - originY; const float voxelStepZ = z1 - originZ; /* * Determine index of slice being viewed for the volume */ float coordinateOnSlice[3] = { originX, originY, originZ }; switch (sliceViewPlane) { case VolumeSliceViewPlaneEnum::ALL: CaretAssert(0); break; case VolumeSliceViewPlaneEnum::AXIAL: coordinateOnSlice[2] = selectedSliceCoordinate; break; case VolumeSliceViewPlaneEnum::CORONAL: coordinateOnSlice[1] = selectedSliceCoordinate; break; case VolumeSliceViewPlaneEnum::PARASAGITTAL: coordinateOnSlice[0] = selectedSliceCoordinate; break; } int64_t sliceIndicesForCoordinateOnSlice[3]; volumeFile->enclosingVoxel(coordinateOnSlice[0], coordinateOnSlice[1], coordinateOnSlice[2], sliceIndicesForCoordinateOnSlice[0], sliceIndicesForCoordinateOnSlice[1], sliceIndicesForCoordinateOnSlice[2]); int64_t sliceIndexForDrawing = -1; int64_t numVoxelsInSlice = 0; switch (sliceViewPlane) { case VolumeSliceViewPlaneEnum::ALL: CaretAssert(0); break; case VolumeSliceViewPlaneEnum::AXIAL: sliceIndexForDrawing = sliceIndicesForCoordinateOnSlice[2]; if ((sliceIndexForDrawing < 0) || (sliceIndexForDrawing >= dimK)) { continue; } numVoxelsInSlice = dimI * dimJ; break; case VolumeSliceViewPlaneEnum::CORONAL: sliceIndexForDrawing = sliceIndicesForCoordinateOnSlice[1]; if ((sliceIndexForDrawing < 0) || (sliceIndexForDrawing >= dimJ)) { continue; } numVoxelsInSlice = dimI * dimK; break; case VolumeSliceViewPlaneEnum::PARASAGITTAL: sliceIndexForDrawing = sliceIndicesForCoordinateOnSlice[0]; if ((sliceIndexForDrawing < 0) || (sliceIndexForDrawing >= dimI)) { continue; } numVoxelsInSlice = dimJ * dimK; break; } /* * Stores RGBA values for each voxel. * Use a vector for voxel colors so no worries about memory being freed. */ const int64_t numVoxelsInSliceRGBA = numVoxelsInSlice * 4; if (numVoxelsInSliceRGBA > static_cast(sliceVoxelsRgbaVector.size())) { sliceVoxelsRgbaVector.resize(numVoxelsInSliceRGBA); } uint8_t* sliceVoxelsRGBA = &sliceVoxelsRgbaVector[0]; /* * Get colors for all voxels in the slice. */ const int64_t validVoxelCount = volumeFile->getVoxelColorsForSliceInMap(m_brain->getPaletteFile(), mapIndex, sliceViewPlane, sliceIndexForDrawing, displayGroup, browserTabIndex, sliceVoxelsRGBA); /* * Is label outline mode? */ if (m_volumeDrawInfo[iVol].mapFile->isMappedWithLabelTable()) { int64_t xdim = 0; int64_t ydim = 0; switch (sliceViewPlane) { case VolumeSliceViewPlaneEnum::ALL: CaretAssert(0); break; case VolumeSliceViewPlaneEnum::AXIAL: xdim = dimI; ydim = dimJ; break; case VolumeSliceViewPlaneEnum::CORONAL: xdim = dimI; ydim = dimK; break; case VolumeSliceViewPlaneEnum::PARASAGITTAL: xdim = dimJ; ydim = dimK; break; } LabelDrawingTypeEnum::Enum labelDrawingType = LabelDrawingTypeEnum::DRAW_FILLED; CaretColorEnum::Enum outlineColor = CaretColorEnum::BLACK; const CaretMappableDataFile* mapFile = dynamic_cast(volumeFile); if (mapFile != NULL) { if (mapFile->isMappedWithLabelTable()) { const LabelDrawingProperties* props = mapFile->getLabelDrawingProperties(); labelDrawingType = props->getDrawingType(); outlineColor = props->getOutlineColor(); } } NodeAndVoxelColoring::convertSliceColoringToOutlineMode(sliceVoxelsRGBA, labelDrawingType, outlineColor, xdim, ydim); } int64_t selectedSliceIndices[3]; volumeFile->enclosingVoxel(sliceCoordinates[0], sliceCoordinates[1], sliceCoordinates[2], selectedSliceIndices[0], selectedSliceIndices[1], selectedSliceIndices[2]); const uint8_t volumeDrawingOpacity = static_cast(volInfo.opacity * 255.0); /* * Setup for drawing the voxels in the slice. */ float startCoordinate[3] = { originX - (voxelStepX / 2.0), originY - (voxelStepY / 2.0), originZ - (voxelStepZ / 2.0) }; float rowStep[3] = { 0.0, 0.0, 0.0 }; float columnStep[3] = { 0.0, 0.0, 0.0 }; int64_t numberOfRows = 0; int64_t numberOfColumns = 0; switch (sliceViewPlane) { case VolumeSliceViewPlaneEnum::ALL: CaretAssert(0); break; case VolumeSliceViewPlaneEnum::AXIAL: startCoordinate[2] = selectedSliceCoordinate; rowStep[1] = voxelStepY; columnStep[0] = voxelStepX; numberOfRows = dimJ; numberOfColumns = dimI; break; case VolumeSliceViewPlaneEnum::CORONAL: startCoordinate[1] = selectedSliceCoordinate; rowStep[2] = voxelStepZ; columnStep[0] = voxelStepX; numberOfRows = dimK; numberOfColumns = dimI; break; case VolumeSliceViewPlaneEnum::PARASAGITTAL: startCoordinate[0] = selectedSliceCoordinate; rowStep[2] = voxelStepZ; columnStep[1] = voxelStepY; numberOfRows = dimK; numberOfColumns = dimJ; break; } if (m_modelWholeBrain != NULL) { /* * After the a slice is drawn in ALL view, some layers * (volume surface outline) may be drawn in lines. As the * view is rotated, lines will partially appear and disappear * due to the lines having the same (extremely close) depth * values as the voxel polygons. OpenGL's Polygon Offset * only works with polygons and NOT with lines or points. * So, polygon offset cannot be used to move the depth * values for the lines and points "a little closer" to * the user. Instead, polygon offset is used to push * the underlaying slices "a little bit away" from the * user. * * Resolves WB-414 */ const float inverseSliceIndex = numberOfVolumesToDraw - iVol; const float factor = inverseSliceIndex * 1.0 + 1.0; const float units = inverseSliceIndex * 1.0 + 1.0; glEnable(GL_POLYGON_OFFSET_FILL); glPolygonOffset(factor, units); } /* * Draw the voxels in the slice. */ // const AString drawMsg("OLD LPI:" // "\n Axis: " + VolumeSliceViewPlaneEnum::toName(sliceViewPlane) // + "\n Start XYZ: " + AString::fromNumbers(startCoordinate, 3, ",") // + "\n Row Step: " + AString::fromNumbers(rowStep, 3, ",") // + "\n Column Step: " + AString::fromNumbers(columnStep, 3, ",") // + "\n Num Cols: " + AString::number(numberOfColumns) // + "\n Num Rows: " + AString::number(numberOfRows) // + "\n"); // std::cout << qPrintable(drawMsg) << std::endl; drawOrthogonalSliceVoxels(sliceNormalVector, startCoordinate, rowStep, columnStep, numberOfColumns, numberOfRows, sliceVoxelsRgbaVector, validVoxelCount, volumeFile, iVol, mapIndex, volumeDrawingOpacity); glDisable(GL_POLYGON_OFFSET_FILL); } showBrainordinateHighlightRegionOfInterest(sliceViewPlane, sliceCoordinates, sliceNormalVector); glDisable(GL_BLEND); glShadeModel(GL_SMOOTH); } /** * Find the given orientation in the given volume, find its corresponding * dimesion index. * * @param volume * The volume. * @param orientation * The requested orientation * @return * Index of dimension containing the orientation or * Negative if not found. */ static int32_t getDimensionContainingOrientation(const VolumeMappableInterface* volume, const VolumeSpace::OrientTypes orientation) { VolumeSpace::OrientTypes orients[3]; volume->getVolumeSpace().getOrientation(orients); for (int32_t i = 0; i < 3; i++) { if (orientation == orients[i]) { return i; } } return -1; } /** * Find the given orientation in the given volume, find its corresponding * dimesion index. * * @param volume * The volume. * @param orientation1 * First requested orientation * @param orientation2 * Second requested orientation * @return * Index of dimension containing the orientation or * Negative if not found. */ static int32_t getDimensionContainingSlicePlane(const VolumeMappableInterface* volume, const VolumeSliceViewPlaneEnum::Enum slicePlane) { int32_t indexOne = -1; int32_t indexTwo = -1; switch (slicePlane) { case caret::VolumeSliceViewPlaneEnum::ALL: break; case caret::VolumeSliceViewPlaneEnum::AXIAL: indexOne = getDimensionContainingOrientation(volume, VolumeSpace::INFERIOR_TO_SUPERIOR); indexTwo = getDimensionContainingOrientation(volume, VolumeSpace::SUPERIOR_TO_INFERIOR); break; case caret::VolumeSliceViewPlaneEnum::CORONAL: indexOne = getDimensionContainingOrientation(volume, VolumeSpace::ANTERIOR_TO_POSTERIOR); indexTwo = getDimensionContainingOrientation(volume, VolumeSpace::POSTERIOR_TO_ANTERIOR); break; case caret::VolumeSliceViewPlaneEnum::PARASAGITTAL: indexOne = getDimensionContainingOrientation(volume, VolumeSpace::LEFT_TO_RIGHT); indexTwo = getDimensionContainingOrientation(volume, VolumeSpace::RIGHT_TO_LEFT); break; } if ((indexOne >= 0) && (indexTwo >= 0)) { CaretLogSevere("Volume contains same axes orientations in more than one dimension"); } else if ((indexOne < 0) && (indexTwo < 0)) { /* probably occurs if volume is not plumb */ } else if (indexOne >= 0) { return indexOne; } else if (indexTwo >= 0) { return indexTwo; } return -1; } /** * Provides information on an axis in a volume. * Used for drawing volumes in any valid orientation. */ class AxisInfo { public: VolumeSliceViewPlaneEnum::Enum axis; /** index for use with slice indices/dimensions and 'this->axis' */ int64_t indexIntoIJK; /** index for use with XYZ coordinates */ int64_t indexIntoXYZ; /** number of voxels in dimension */ int64_t numberOfVoxels; /** step to move to next voxel */ int64_t voxelIndexStep; /** index of first voxel (either 0 or (numberOfVoxels - 1)) */ int64_t firstVoxelIndex; /** absolute spatial size (mm) of voxel */ float absoluteVoxelSize; /** spatial step to next voxel (signed) */ float voxelStepSize; /** true if this axis info is valid */ bool valid; AxisInfo() { valid = false; } void print() { const AString s("Axis: " + VolumeSliceViewPlaneEnum::toName(axis) + "\n IJK Index: " + AString::number(indexIntoIJK) + "\n IJK Index: " + AString::number(indexIntoXYZ) + "\n Number of voxels: " + AString::number(numberOfVoxels) + "\n First Voxel Index: " + AString::number(firstVoxelIndex) + "\n Voxel Index Step: " + AString::number(voxelIndexStep) + "\n Abs Voxel Size: " + AString::number(absoluteVoxelSize) + "\n Voxel Step Size: " + AString::number(voxelStepSize) + "\n"); std::cout << qPrintable(s) << std::endl; } }; /** * Get the info for the given axis in the given volume file * * @param volumeFile * The volume file. * @param axis * The axis * @param startWithMinimumCoordFlag * If true, voxel information will start with voxel that has minimum coordinate. * @param axisInfoOut * Output containing information for the given axis */ static void getAxisInfo(const VolumeMappableInterface* volumeFile, const VolumeSliceViewPlaneEnum::Enum axis, const bool startWithMinimumCoordFlag, AxisInfo& axisInfoOut) { /* * Data for axis may be in any dimension */ axisInfoOut.axis = axis; axisInfoOut.indexIntoIJK = getDimensionContainingSlicePlane(volumeFile, axis); if (axisInfoOut.indexIntoIJK < 0) { axisInfoOut.valid = false; return; } /* * X, Y, Z is always indices 0, 1, 2 */ switch (axis) { case VolumeSliceViewPlaneEnum::ALL: break; case VolumeSliceViewPlaneEnum::AXIAL: axisInfoOut.indexIntoXYZ = 2; break; case VolumeSliceViewPlaneEnum::CORONAL: axisInfoOut.indexIntoXYZ = 1; break; case VolumeSliceViewPlaneEnum::PARASAGITTAL: axisInfoOut.indexIntoXYZ = 0; break; } std::vector dimArray; volumeFile->getDimensions(dimArray); CaretAssertVectorIndex(dimArray, axisInfoOut.indexIntoIJK); axisInfoOut.numberOfVoxels = dimArray[axisInfoOut.indexIntoIJK]; axisInfoOut.firstVoxelIndex = 0; int64_t zeroIndices[3] = { 0, 0, 0 }; float xyzZero[3] = { 0.0, 0.0, 0.0 }; volumeFile->indexToSpace(zeroIndices, xyzZero); int64_t oneIndices[3] = { 1, 1, 1}; float xyzOne[3] = { 1.0, 1.0, 1.0 }; volumeFile->indexToSpace(oneIndices, xyzOne); axisInfoOut.voxelStepSize = (xyzOne[axisInfoOut.indexIntoXYZ] - xyzZero[axisInfoOut.indexIntoXYZ]); axisInfoOut.absoluteVoxelSize = std::fabs(axisInfoOut.voxelStepSize); axisInfoOut.voxelIndexStep = 1; if (startWithMinimumCoordFlag) { if (axisInfoOut.voxelStepSize < 0.0) { axisInfoOut.firstVoxelIndex = axisInfoOut.numberOfVoxels - 1; axisInfoOut.voxelStepSize = -axisInfoOut.voxelStepSize; axisInfoOut.voxelIndexStep = -1; } } else { if (axisInfoOut.voxelStepSize > 0.0) { axisInfoOut.firstVoxelIndex = axisInfoOut.numberOfVoxels - 1; axisInfoOut.voxelStepSize = -axisInfoOut.voxelStepSize; axisInfoOut.voxelIndexStep = -1; } } axisInfoOut.valid = true; //axisInfoOut.print(); } /** * Draw an orthogonal slice. * * @param sliceViewingPlane * The plane for slice drawing. * @param sliceCoordinates * Coordinates of the selected slice. * @param plane * Plane equation for the selected slice. */ void BrainOpenGLVolumeSliceDrawing::drawOrthogonalSlice(const VolumeSliceViewPlaneEnum::Enum sliceViewingPlane, const float sliceCoordinates[3], const Plane& plane) { CaretAssert(plane.isValidPlane()); if (plane.isValidPlane() == false) { return; } const int32_t browserTabIndex = m_browserTabContent->getTabNumber(); const DisplayPropertiesLabels* displayPropertiesLabels = m_brain->getDisplayPropertiesLabels(); const DisplayGroupEnum::Enum displayGroup = displayPropertiesLabels->getDisplayGroupForTab(browserTabIndex); /* * 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); /* * Flat shading voxels not interpolated */ glShadeModel(GL_FLAT); /* * Compute coordinate of point in center of first slice */ float selectedSliceCoordinate = 0.0; float sliceNormalVector[3] = { 0.0, 0.0, 0.0 }; plane.getNormalVector(sliceNormalVector); switch (sliceViewingPlane) { case VolumeSliceViewPlaneEnum::ALL: CaretAssert(0); break; case VolumeSliceViewPlaneEnum::AXIAL: selectedSliceCoordinate = sliceCoordinates[2]; break; case VolumeSliceViewPlaneEnum::CORONAL: selectedSliceCoordinate = sliceCoordinates[1]; break; case VolumeSliceViewPlaneEnum::PARASAGITTAL: selectedSliceCoordinate = sliceCoordinates[0]; break; } /* * Holds colors for voxels in the slice * Outside of loop to minimize allocations * It is faster to make one call to * NodeAndVoxelColoring::colorScalarsWithPalette() with * all voxels in the slice than it is to call it * separately for each voxel. */ std::vector sliceVoxelsRgbaVector; /* * Draw each of the volumes separately so that each * is drawn with the correct voxel slices. */ const int32_t numberOfVolumesToDraw = static_cast(m_volumeDrawInfo.size()); for (int32_t iVol = 0; iVol < numberOfVolumesToDraw; iVol++) { const BrainOpenGLFixedPipeline::VolumeDrawInfo& volInfo = m_volumeDrawInfo[iVol]; const VolumeMappableInterface* volumeFile = volInfo.volumeFile; /* * Find axis that correspond to the axis that are on * the screen horizontally and vertically. */ AxisInfo drawLeftToRightInfo; AxisInfo drawBottomToTopInfo; int64_t viewPlaneDimIndex = getDimensionContainingSlicePlane(volumeFile, sliceViewingPlane); int64_t sliceViewingPlaneIndexIntoXYZ = -1; switch (sliceViewingPlane) { case VolumeSliceViewPlaneEnum::ALL: CaretAssert(0); break; case VolumeSliceViewPlaneEnum::AXIAL: getAxisInfo(volumeFile, VolumeSliceViewPlaneEnum::PARASAGITTAL, true, drawLeftToRightInfo); getAxisInfo(volumeFile, VolumeSliceViewPlaneEnum::CORONAL, true, drawBottomToTopInfo); sliceViewingPlaneIndexIntoXYZ = 2; break; case VolumeSliceViewPlaneEnum::CORONAL: getAxisInfo(volumeFile, VolumeSliceViewPlaneEnum::PARASAGITTAL, true, drawLeftToRightInfo); getAxisInfo(volumeFile, VolumeSliceViewPlaneEnum::AXIAL, true, drawBottomToTopInfo); sliceViewingPlaneIndexIntoXYZ = 1; break; case VolumeSliceViewPlaneEnum::PARASAGITTAL: getAxisInfo(volumeFile, VolumeSliceViewPlaneEnum::CORONAL, false, drawLeftToRightInfo); getAxisInfo(volumeFile, VolumeSliceViewPlaneEnum::AXIAL, true, drawBottomToTopInfo); sliceViewingPlaneIndexIntoXYZ = 0; break; } CaretAssert(drawLeftToRightInfo.valid); CaretAssert(drawBottomToTopInfo.valid); CaretAssert(viewPlaneDimIndex != drawLeftToRightInfo.indexIntoIJK); CaretAssert(viewPlaneDimIndex != drawBottomToTopInfo.indexIntoIJK); CaretAssert(drawLeftToRightInfo.indexIntoIJK != drawBottomToTopInfo.indexIntoIJK); CaretAssert(sliceViewingPlaneIndexIntoXYZ != drawLeftToRightInfo.indexIntoXYZ); CaretAssert(sliceViewingPlaneIndexIntoXYZ != drawBottomToTopInfo.indexIntoXYZ); CaretAssert(drawLeftToRightInfo.indexIntoXYZ != drawBottomToTopInfo.indexIntoXYZ); /* * There must be at least two voxels in both dimensions. * If a dimension consists of a single voxel, then it is * likely a single slice volume and our viewpoint is * "in" the slice. * * Without this check the user would see a strange looking * line that is one voxel in width */ if ((drawLeftToRightInfo.numberOfVoxels <= 1) || (drawBottomToTopInfo.numberOfVoxels <= 1)) { continue; } /* * Spatial amount to move up row. */ float rowStepXYZ[3] = { 0.0, 0.0, 0.0 }; rowStepXYZ[drawBottomToTopInfo.indexIntoXYZ] = drawBottomToTopInfo.voxelStepSize; /* * Spatial amount to move right one column. */ float columnStepXYZ[3] = { 0.0, 0.0, 0.0 }; columnStepXYZ[drawLeftToRightInfo.indexIntoXYZ] = drawLeftToRightInfo.voxelStepSize; /* * Step in voxel dimensions to move right one column */ int64_t columnStepIJK[3] = { 0, 0, 0 }; columnStepIJK[drawLeftToRightInfo.indexIntoIJK] = drawLeftToRightInfo.voxelIndexStep; /* * Step in voxel dimensions to move up one row */ int64_t rowStepIJK[3] = { 0, 0, 0 }; rowStepIJK[drawBottomToTopInfo.indexIntoIJK] = drawBottomToTopInfo.voxelIndexStep; /* * XYZ needs to use regular X=0, Y=1, Z=2 indices */ int64_t sliceVoxelIndices[3] = { 0, 0, 0 }; float sliceVoxelXYZ[3] = { 0.0, 0.0, 0.0 }; sliceVoxelXYZ[sliceViewingPlaneIndexIntoXYZ] = selectedSliceCoordinate; //volumeFile->indexToSpace(sliceVoxelIndices, sliceVoxelXYZ); volumeFile->enclosingVoxel(sliceVoxelXYZ[0], sliceVoxelXYZ[1], sliceVoxelXYZ[2], sliceVoxelIndices[0], sliceVoxelIndices[1], sliceVoxelIndices[2]); /* * Find the index of the slice for drawing and verify that * it is a valid slice index. */ const int64_t sliceIndexForDrawing = sliceVoxelIndices[viewPlaneDimIndex]; std::vector volDim; volumeFile->getDimensions(volDim); const int64_t maximumAxisSliceIndex = volDim[viewPlaneDimIndex]; if ((sliceIndexForDrawing < 0) || (sliceIndexForDrawing >= maximumAxisSliceIndex)) { continue; } /* * Voxel indices for first voxel that is drawn at bottom left of screen */ int64_t firstVoxelIJK[3] = { -1, -1, -1 }; firstVoxelIJK[drawBottomToTopInfo.indexIntoIJK] = drawBottomToTopInfo.firstVoxelIndex; firstVoxelIJK[drawLeftToRightInfo.indexIntoIJK] = drawLeftToRightInfo.firstVoxelIndex; firstVoxelIJK[viewPlaneDimIndex] = sliceIndexForDrawing; /* * Coordinate of first voxel that is drawn at bottom left of screen */ float startCoordinateXYZ[3] = { 0.0, 0.0, 0.0 }; volumeFile->indexToSpace(firstVoxelIJK, startCoordinateXYZ); startCoordinateXYZ[drawLeftToRightInfo.indexIntoXYZ] -= (drawLeftToRightInfo.voxelStepSize / 2.0); startCoordinateXYZ[drawBottomToTopInfo.indexIntoXYZ] -= (drawBottomToTopInfo.voxelStepSize / 2.0); startCoordinateXYZ[viewPlaneDimIndex] = selectedSliceCoordinate; /* * Stores RGBA values for each voxel. * Use a vector for voxel colors so no worries about memory being freed. */ const int64_t numVoxelsInSlice = drawBottomToTopInfo.numberOfVoxels * drawLeftToRightInfo.numberOfVoxels; const int64_t numVoxelsInSliceRGBA = numVoxelsInSlice * 4; if (numVoxelsInSliceRGBA > static_cast(sliceVoxelsRgbaVector.size())) { sliceVoxelsRgbaVector.resize(numVoxelsInSliceRGBA); } uint8_t* sliceVoxelsRGBA = &sliceVoxelsRgbaVector[0]; /* * Get colors for all voxels in the slice. */ const int64_t validVoxelCount = volumeFile->getVoxelColorsForSliceInMap(volInfo.mapIndex, firstVoxelIJK, rowStepIJK, columnStepIJK, drawBottomToTopInfo.numberOfVoxels, drawLeftToRightInfo.numberOfVoxels, displayGroup, browserTabIndex, sliceVoxelsRGBA); /* * Is label outline mode? */ if (m_volumeDrawInfo[iVol].mapFile->isMappedWithLabelTable()) { int64_t xdim = drawLeftToRightInfo.numberOfVoxels; int64_t ydim = drawBottomToTopInfo.numberOfVoxels; LabelDrawingTypeEnum::Enum labelDrawingType = LabelDrawingTypeEnum::DRAW_FILLED; CaretColorEnum::Enum outlineColor = CaretColorEnum::BLACK; const CaretMappableDataFile* mapFile = dynamic_cast(volumeFile); if (mapFile != NULL) { if (mapFile->isMappedWithLabelTable()) { const LabelDrawingProperties* props = mapFile->getLabelDrawingProperties(); labelDrawingType = props->getDrawingType(); outlineColor = props->getOutlineColor(); } } NodeAndVoxelColoring::convertSliceColoringToOutlineMode(sliceVoxelsRGBA, labelDrawingType, outlineColor, xdim, ydim); } const uint8_t volumeDrawingOpacity = static_cast(volInfo.opacity * 255.0); if (m_modelWholeBrain != NULL) { /* * After the a slice is drawn in ALL view, some layers * (volume surface outline) may be drawn in lines. As the * view is rotated, lines will partially appear and disappear * due to the lines having the same (extremely close) depth * values as the voxel polygons. OpenGL's Polygon Offset * only works with polygons and NOT with lines or points. * So, polygon offset cannot be used to move the depth * values for the lines and points "a little closer" to * the user. Instead, polygon offset is used to push * the underlaying slices "a little bit away" from the * user. * * Resolves WB-414 */ const float inverseSliceIndex = numberOfVolumesToDraw - iVol; const float factor = inverseSliceIndex * 1.0 + 1.0; const float units = inverseSliceIndex * 1.0 + 1.0; glEnable(GL_POLYGON_OFFSET_FILL); glPolygonOffset(factor, units); } /* * Draw the voxels in the slice. */ // const AString drawMsg("NEW:" // "\n Axis: " + VolumeSliceViewPlaneEnum::toName(sliceViewingPlane) // + "\n Start XYZ: " + AString::fromNumbers(startCoordinateXYZ, 3, ",") // + "\n Row Step: " + AString::fromNumbers(rowStepXYZ, 3, ",") // + "\n Column Step: " + AString::fromNumbers(columnStepXYZ, 3, ",") // + "\n Num Cols: " + AString::number(drawLeftToRightInfo.numberOfVoxels) // + "\n Num Rows: " + AString::number(drawBottomToTopInfo.numberOfVoxels) // + "\n"); // std::cout << qPrintable(drawMsg) << std::endl; drawOrthogonalSliceVoxels(sliceNormalVector, startCoordinateXYZ, rowStepXYZ, columnStepXYZ, drawLeftToRightInfo.numberOfVoxels, drawBottomToTopInfo.numberOfVoxels, sliceVoxelsRgbaVector, validVoxelCount, volumeFile, iVol, volInfo.mapIndex, volumeDrawingOpacity); glDisable(GL_POLYGON_OFFSET_FILL); } showBrainordinateHighlightRegionOfInterest(sliceViewingPlane, sliceCoordinates, sliceNormalVector); glDisable(GL_BLEND); glShadeModel(GL_SMOOTH); } /** * Show brainordinate highlighting region of interest for the volume slice. * * @param sliceViewPlane * Slice plane viewed. * @param sliceCoordinates * Coordinates of the slice. * @param sliceNormalVector * Normal vector for the slice. */ void BrainOpenGLVolumeSliceDrawing::showBrainordinateHighlightRegionOfInterest(const VolumeSliceViewPlaneEnum::Enum sliceViewPlane, const float sliceCoordinates[3], const float sliceNormalVector[3]) { const BrainordinateRegionOfInterest* roi = m_brain->getBrainordinateHighlightRegionOfInterest(); if ( ! roi->hasVolumeVoxels()) { return; } if ( ! roi->isBrainordinateHighlightingEnabled()) { return; } const std::vector& voxelXYZ = roi->getVolumeVoxelsXYZ(); const int64_t numVoxels = static_cast(voxelXYZ.size() / 3); if (numVoxels <= 0) { return; } float voxelSize[3]; roi->getVolumeVoxelSize(voxelSize); CaretAssert(voxelSize[0] >= 0.0); CaretAssert(voxelSize[1] >= 0.0); CaretAssert(voxelSize[2] >= 0.0); float halfX = voxelSize[0] / 2.0; float halfY = voxelSize[1] / 2.0; float halfZ = voxelSize[2] / 2.0; int64_t axisIndex = 0; float sliceMinCoord = 0.0; float sliceMaxCoord = 0.0; switch (sliceViewPlane) { case VolumeSliceViewPlaneEnum::ALL: CaretAssert(0); break; case VolumeSliceViewPlaneEnum::AXIAL: sliceMinCoord = sliceCoordinates[2] - halfZ; sliceMaxCoord = sliceCoordinates[2] + halfZ; axisIndex = 2; break; case VolumeSliceViewPlaneEnum::CORONAL: sliceMinCoord = sliceCoordinates[1] - halfY; sliceMaxCoord = sliceCoordinates[1] + halfZ; axisIndex = 1; break; case VolumeSliceViewPlaneEnum::PARASAGITTAL: sliceMinCoord = sliceCoordinates[0] - halfX; sliceMaxCoord = sliceCoordinates[0] + halfX; axisIndex = 0; break; } std::vector quadsXYZ; for (int64_t i = 0; i < numVoxels; i++) { const int64_t i3 = i * 3; if ((voxelXYZ[i3 + axisIndex] >= sliceMinCoord) && (voxelXYZ[i3 + axisIndex] <= sliceMaxCoord)) { CaretAssertVectorIndex(voxelXYZ, i3+2); const float x = voxelXYZ[i3]; const float y = voxelXYZ[i3+1]; const float z = voxelXYZ[i3+2]; switch (sliceViewPlane) { case VolumeSliceViewPlaneEnum::ALL: CaretAssert(0); break; case VolumeSliceViewPlaneEnum::AXIAL: quadsXYZ.push_back(x - halfX); quadsXYZ.push_back(y - halfY); quadsXYZ.push_back(sliceCoordinates[2]); quadsXYZ.push_back(x + halfX); quadsXYZ.push_back(y - halfY); quadsXYZ.push_back(sliceCoordinates[2]); quadsXYZ.push_back(x + halfX); quadsXYZ.push_back(y + halfY); quadsXYZ.push_back(sliceCoordinates[2]); quadsXYZ.push_back(x - halfX); quadsXYZ.push_back(y + halfY); quadsXYZ.push_back(sliceCoordinates[2]); break; case VolumeSliceViewPlaneEnum::CORONAL: quadsXYZ.push_back(x - halfX); quadsXYZ.push_back(sliceCoordinates[1]); quadsXYZ.push_back(z - halfZ); quadsXYZ.push_back(x + halfX); quadsXYZ.push_back(sliceCoordinates[1]); quadsXYZ.push_back(z - halfZ); quadsXYZ.push_back(x + halfX); quadsXYZ.push_back(sliceCoordinates[1]); quadsXYZ.push_back(z + halfZ); quadsXYZ.push_back(x - halfX); quadsXYZ.push_back(sliceCoordinates[1]); quadsXYZ.push_back(z + halfZ); break; case VolumeSliceViewPlaneEnum::PARASAGITTAL: quadsXYZ.push_back(sliceCoordinates[0]); quadsXYZ.push_back(y + halfY); quadsXYZ.push_back(z - halfZ); quadsXYZ.push_back(sliceCoordinates[0]); quadsXYZ.push_back(y - halfY); quadsXYZ.push_back(z - halfZ); quadsXYZ.push_back(sliceCoordinates[0]); quadsXYZ.push_back(y - halfY); quadsXYZ.push_back(z + halfZ); quadsXYZ.push_back(sliceCoordinates[0]); quadsXYZ.push_back(y + halfY); quadsXYZ.push_back(z + halfZ); break; } } } const int64_t numVoxelsToDraw = (quadsXYZ.size() / 12); CaretAssert((numVoxelsToDraw * 12) == static_cast(quadsXYZ.size())); const int64_t numCoords = (quadsXYZ.size()) / 3; std::vector voxelQuadNormals; voxelQuadNormals.reserve(numVoxelsToDraw * 12); std::vector voxelQuadRgba; voxelQuadRgba.reserve(numVoxelsToDraw * 16); CaretPreferences* prefs = SessionManager::get()->getCaretPreferences(); uint8_t foregroundColor[4]; prefs->getBackgroundAndForegroundColors()->getColorForegroundVolumeView(foregroundColor); for (int32_t iNormalAndColor = 0; iNormalAndColor < numCoords; iNormalAndColor++) { voxelQuadRgba.push_back(foregroundColor[0]); voxelQuadRgba.push_back(foregroundColor[1]); voxelQuadRgba.push_back(foregroundColor[2]); voxelQuadRgba.push_back(255); voxelQuadNormals.push_back(sliceNormalVector[0]); voxelQuadNormals.push_back(sliceNormalVector[1]); voxelQuadNormals.push_back(sliceNormalVector[2]); } CaretAssert(quadsXYZ.size() == voxelQuadNormals.size()); CaretAssert((numVoxelsToDraw * 16) == static_cast(voxelQuadRgba.size())); BrainOpenGLPrimitiveDrawing::drawQuads(quadsXYZ, voxelQuadNormals, voxelQuadRgba); } /** * Draw identification symbols on volume slice with the given plane. * * @param plane * The plane equation. */ void BrainOpenGLVolumeSliceDrawing::drawIdentificationSymbols(const Plane& plane) { IdentificationManager* idManager = m_brain->getIdentificationManager(); SelectionItemVoxelIdentificationSymbol* symbolID = m_brain->getSelectionManager()->getVoxelIdentificationSymbol(); const std::vector voxelIDs = idManager->getIdentifiedItemsForVolume(); /* * Check for a 'selection' type mode */ bool isSelect = false; switch (m_fixedPipelineDrawing->mode) { case BrainOpenGLFixedPipeline::MODE_DRAWING: break; case BrainOpenGLFixedPipeline::MODE_IDENTIFICATION: if (symbolID->isEnabledForSelection()) { isSelect = true; glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); } else { return; } break; case BrainOpenGLFixedPipeline::MODE_PROJECTION: 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); } glPushMatrix(); glTranslatef(xyz[0], xyz[1], xyz[2]); m_fixedPipelineDrawing->drawSphereWithDiameter(rgba, symbolDiameter); glPopMatrix(); } } if (isSelect) { int voxelIdIndex = -1; float depth = -1.0; m_fixedPipelineDrawing->getIndexFromColorSelection(SelectionItemDataTypeEnum::VOXEL_IDENTIFICATION_SYMBOL, m_fixedPipelineDrawing->mouseX, m_fixedPipelineDrawing->mouseY, voxelIdIndex, depth); if (voxelIdIndex >= 0) { if (symbolID->isOtherScreenDepthCloserToViewer(depth)) { CaretAssertVectorIndex(voxelIDs, voxelIdIndex); const IdentifiedItemVoxel& voxel = voxelIDs[voxelIdIndex]; float xyz[3]; voxel.getXYZ(xyz); symbolID->setVoxelXYZ(xyz); symbolID->setBrain(m_brain); symbolID->setScreenDepth(depth); m_fixedPipelineDrawing->setSelectedItemScreenXYZ(symbolID, xyz); CaretLogFine("Selected Vertex Identification Symbol: " + QString::number(voxelIdIndex)); } } } } /** * Draw an orthogonal slice with culling to avoid drawing * voxels not visible in the viewport and reduce drawing time. * * @param sliceViewPlane * The plane for slice drawing. * @param sliceCoordinates * Coordinates of the selected slice. * @param plane * Plane equation for the selected slice. */ void BrainOpenGLVolumeSliceDrawing::drawOrthogonalSliceWithCulling(const VolumeSliceViewPlaneEnum::Enum sliceViewPlane, const float sliceCoordinates[3], const Plane& plane) { const int32_t browserTabIndex = m_browserTabContent->getTabNumber(); const DisplayPropertiesLabels* displayPropertiesLabels = m_brain->getDisplayPropertiesLabels(); const DisplayGroupEnum::Enum displayGroup = displayPropertiesLabels->getDisplayGroupForTab(browserTabIndex); /* * 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); /* * Flat shading voxels not interpolated */ glShadeModel(GL_FLAT); CaretAssert(plane.isValidPlane()); if (plane.isValidPlane() == false) { return; } /* * Compute coordinate of point in center of first slice */ float selectedSliceCoordinate = 0.0; float sliceNormalVector[3] = { 0.0, 0.0, 0.0 }; plane.getNormalVector(sliceNormalVector); switch (sliceViewPlane) { case VolumeSliceViewPlaneEnum::ALL: CaretAssert(0); break; case VolumeSliceViewPlaneEnum::AXIAL: selectedSliceCoordinate = sliceCoordinates[2]; break; case VolumeSliceViewPlaneEnum::CORONAL: selectedSliceCoordinate = sliceCoordinates[1]; break; case VolumeSliceViewPlaneEnum::PARASAGITTAL: selectedSliceCoordinate = sliceCoordinates[0]; break; } /* * Holds colors for voxels in the slice * Outside of loop to minimize allocations * It is faster to make one call to * NodeAndVoxelColoring::colorScalarsWithPalette() with * all voxels in the slice than it is to call it * separately for each voxel. */ std::vector sliceVoxelsRgbaVector; /* * Draw each of the volumes separately so that each * is drawn with the correct voxel slices. */ const int32_t numberOfVolumesToDraw = static_cast(m_volumeDrawInfo.size()); for (int32_t iVol = 0; iVol < numberOfVolumesToDraw; iVol++) { const BrainOpenGLFixedPipeline::VolumeDrawInfo& volInfo = m_volumeDrawInfo[iVol]; const VolumeMappableInterface* volumeFile = volInfo.volumeFile; int64_t culledFirstVoxelIJK[3]; int64_t culledLastVoxelIJK[3]; float voxelDeltaXYZ[3]; if ( ! getVolumeDrawingViewDependentCulling(sliceViewPlane, selectedSliceCoordinate, volumeFile, culledFirstVoxelIJK, culledLastVoxelIJK, voxelDeltaXYZ)) { /* volume does not have slice within the culled region */ continue; } /*const int64_t numVoxelsI = std::abs(culledLastVoxelIJK[0] - culledFirstVoxelIJK[0]) + 1; const int64_t numVoxelsJ = std::abs(culledLastVoxelIJK[1] - culledFirstVoxelIJK[1]) + 1; const int64_t numVoxelsK = std::abs(culledLastVoxelIJK[2] - culledFirstVoxelIJK[2]) + 1;//*/ int64_t numVoxelsX = -1, numVoxelsY = -1, numVoxelsZ = -1; int64_t sliceIndexForDrawing = -1; int64_t dimIJK[3], numMaps, numComponents; volumeFile->getDimensions(dimIJK[0], dimIJK[1], dimIJK[2], numMaps, numComponents); VolumeSpace::OrientTypes orient[3]; volumeFile->getVolumeSpace().getOrientation(orient);//use the volume's orientation to get the correct dimension for each axis bool skipDraw = false; for (int whichDim = 0; whichDim < 3; ++whichDim) { int64_t numVoxelsIndex = std::abs(culledLastVoxelIJK[whichDim] - culledFirstVoxelIJK[whichDim]) + 1; switch (orient[whichDim]) { case VolumeSpace::LEFT_TO_RIGHT: case VolumeSpace::RIGHT_TO_LEFT: numVoxelsX = numVoxelsIndex; if (sliceViewPlane == VolumeSliceViewPlaneEnum::PARASAGITTAL) { sliceIndexForDrawing = culledFirstVoxelIJK[whichDim]; if ((sliceIndexForDrawing < 0) || (sliceIndexForDrawing >= dimIJK[whichDim])) { skipDraw = true; } } break; case VolumeSpace::POSTERIOR_TO_ANTERIOR: case VolumeSpace::ANTERIOR_TO_POSTERIOR: numVoxelsY = numVoxelsIndex; if (sliceViewPlane == VolumeSliceViewPlaneEnum::CORONAL) { sliceIndexForDrawing = culledFirstVoxelIJK[whichDim]; if ((sliceIndexForDrawing < 0) || (sliceIndexForDrawing >= dimIJK[whichDim])) { skipDraw = true; } } break; case VolumeSpace::INFERIOR_TO_SUPERIOR: case VolumeSpace::SUPERIOR_TO_INFERIOR: numVoxelsZ = numVoxelsIndex; if (sliceViewPlane == VolumeSliceViewPlaneEnum::AXIAL) { sliceIndexForDrawing = culledFirstVoxelIJK[whichDim]; if ((sliceIndexForDrawing < 0) || (sliceIndexForDrawing >= dimIJK[whichDim])) { skipDraw = true; } } break; } } if (skipDraw) continue; const int64_t mapIndex = volInfo.mapIndex; float firstVoxelXYZ[3]; volumeFile->indexToSpace(culledFirstVoxelIJK[0], culledFirstVoxelIJK[1], culledFirstVoxelIJK[2], firstVoxelXYZ[0], firstVoxelXYZ[1], firstVoxelXYZ[2]); const float voxelStepX = voxelDeltaXYZ[0]; const float voxelStepY = voxelDeltaXYZ[1]; const float voxelStepZ = voxelDeltaXYZ[2]; int64_t numVoxelsInSlice = 0; switch (sliceViewPlane) { case VolumeSliceViewPlaneEnum::ALL: CaretAssert(0); break; case VolumeSliceViewPlaneEnum::AXIAL: numVoxelsInSlice = numVoxelsX * numVoxelsY; break; case VolumeSliceViewPlaneEnum::CORONAL: numVoxelsInSlice = numVoxelsX * numVoxelsZ; break; case VolumeSliceViewPlaneEnum::PARASAGITTAL: numVoxelsInSlice = numVoxelsY * numVoxelsZ; break; } /* * Stores RGBA values for each voxel. * Use a vector for voxel colors so no worries about memory being freed. */ const int64_t numVoxelsInSliceRGBA = numVoxelsInSlice * 4; if (numVoxelsInSliceRGBA != static_cast(sliceVoxelsRgbaVector.size())) { sliceVoxelsRgbaVector.resize(numVoxelsInSliceRGBA); } uint8_t* sliceVoxelsRGBA = &sliceVoxelsRgbaVector[0]; /* * Get colors for all voxels in the slice. */ const int64_t voxelCountXYZ[3] = { numVoxelsX, numVoxelsY, numVoxelsZ };//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, sliceViewPlane, sliceIndexForDrawing, culledFirstVoxelIJK, culledLastVoxelIJK, voxelCountXYZ, displayGroup, browserTabIndex, sliceVoxelsRGBA); /* * Is label outline mode? */ if (m_volumeDrawInfo[iVol].mapFile->isMappedWithLabelTable()) { int64_t xdim = 0; int64_t ydim = 0; switch (sliceViewPlane) { case VolumeSliceViewPlaneEnum::ALL: CaretAssert(0); break; case VolumeSliceViewPlaneEnum::AXIAL: xdim = numVoxelsX; ydim = numVoxelsY; break; case VolumeSliceViewPlaneEnum::CORONAL: xdim = numVoxelsX; ydim = numVoxelsZ; break; case VolumeSliceViewPlaneEnum::PARASAGITTAL: xdim = numVoxelsY; ydim = numVoxelsZ; break; } LabelDrawingTypeEnum::Enum labelDrawingType = LabelDrawingTypeEnum::DRAW_FILLED; CaretColorEnum::Enum outlineColor = CaretColorEnum::BLACK; const CaretMappableDataFile* mapFile = dynamic_cast(volumeFile); if (mapFile != NULL) { if (mapFile->isMappedWithLabelTable()) { const LabelDrawingProperties* props = mapFile->getLabelDrawingProperties(); labelDrawingType = props->getDrawingType(); outlineColor = props->getOutlineColor(); } } NodeAndVoxelColoring::convertSliceColoringToOutlineMode(sliceVoxelsRGBA, labelDrawingType, outlineColor, xdim, ydim); } const uint8_t volumeDrawingOpacity = static_cast(volInfo.opacity * 255.0); /* * 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) }; float rowStep[3] = { 0.0, 0.0, 0.0 }; float columnStep[3] = { 0.0, 0.0, 0.0 }; int64_t numberOfRows = 0; int64_t numberOfColumns = 0; switch (sliceViewPlane) { case VolumeSliceViewPlaneEnum::ALL: CaretAssert(0); break; case VolumeSliceViewPlaneEnum::AXIAL: rowStep[1] = voxelStepY; columnStep[0] = voxelStepX; numberOfRows = numVoxelsY;//WARNING: this is actually length of row, not number of rows, ditto for the rest numberOfColumns = numVoxelsX;//leaving it alone for now... break; case VolumeSliceViewPlaneEnum::CORONAL: rowStep[2] = voxelStepZ; columnStep[0] = voxelStepX; numberOfRows = numVoxelsZ; numberOfColumns = numVoxelsX; break; case VolumeSliceViewPlaneEnum::PARASAGITTAL: rowStep[2] = voxelStepZ; columnStep[1] = voxelStepY; numberOfRows = numVoxelsZ; numberOfColumns = numVoxelsY; break; } if (m_modelWholeBrain != NULL) { /* * After the a slice is drawn in ALL view, some layers * (volume surface outline) may be drawn in lines. As the * view is rotated, lines will partially appear and disappear * due to the lines having the same (extremely close) depth * values as the voxel polygons. OpenGL's Polygon Offset * only works with polygons and NOT with lines or points. * So, polygon offset cannot be used to move the depth * values for the lines and points "a little closer" to * the user. Instead, polygon offset is used to push * the underlaying slices "a little bit away" from the * user. * * Resolves WB-414 */ const float inverseSliceIndex = numberOfVolumesToDraw - iVol; const float factor = inverseSliceIndex * 1.0 + 1.0; const float units = inverseSliceIndex * 1.0 + 1.0; glEnable(GL_POLYGON_OFFSET_FILL); glPolygonOffset(factor, units); } else { /* * A layer may be "under" another layer and not be seen. * Draw all layers at the selected slice coordinate. */ switch (sliceViewPlane) { case VolumeSliceViewPlaneEnum::ALL: CaretAssert(0); break; case VolumeSliceViewPlaneEnum::AXIAL: startCoordinate[2] = selectedSliceCoordinate; break; case VolumeSliceViewPlaneEnum::CORONAL: startCoordinate[1] = selectedSliceCoordinate; break; case VolumeSliceViewPlaneEnum::PARASAGITTAL: startCoordinate[0] = selectedSliceCoordinate; break; } } /* * Draw the voxels in the slice. */ drawOrthogonalSliceVoxels(sliceNormalVector, startCoordinate, rowStep, columnStep, numberOfColumns, numberOfRows, sliceVoxelsRgbaVector, validVoxelCount, volumeFile, iVol, mapIndex, volumeDrawingOpacity); glDisable(GL_POLYGON_OFFSET_FILL); } showBrainordinateHighlightRegionOfInterest(sliceViewPlane, sliceCoordinates, sliceNormalVector); glDisable(GL_BLEND); glShadeModel(GL_SMOOTH); } /** * Create the equation for the slice plane * * @param sliceProjectionType * Type of projection for the slice drawing (oblique, orthogonal) * @param sliceViewPlane * View plane that is displayed. * @param montageSliceIndex * Selected montage slice index * @param planeOut * OUTPUT plane of slice after transforms. */ void BrainOpenGLVolumeSliceDrawing::createSlicePlaneEquation(const VolumeSliceProjectionTypeEnum::Enum sliceProjectionType, const VolumeSliceViewPlaneEnum::Enum sliceViewPlane, const float sliceCoordinates[3], Plane& planeOut) { /* * Default the slice normal vector to an orthogonal view */ float sliceNormalVector[3] = { 0.0, 0.0, 0.0 }; switch (sliceViewPlane) { case VolumeSliceViewPlaneEnum::ALL: case VolumeSliceViewPlaneEnum::AXIAL: sliceNormalVector[2] = 1.0; break; case VolumeSliceViewPlaneEnum::CORONAL: sliceNormalVector[1] = -1.0; break; case VolumeSliceViewPlaneEnum::PARASAGITTAL: sliceNormalVector[0] = -1.0; break; } switch (sliceProjectionType) { break; case VolumeSliceProjectionTypeEnum::VOLUME_SLICE_PROJECTION_OBLIQUE: { /* * Transform the slice normal vector by the oblique rotation * matrix so that the normal vector points out of the slice */ const Matrix4x4 obliqueRotationMatrix = m_browserTabContent->getObliqueVolumeRotationMatrix(); obliqueRotationMatrix.multiplyPoint3(sliceNormalVector); MathFunctions::normalizeVector(sliceNormalVector); } break; case VolumeSliceProjectionTypeEnum::VOLUME_SLICE_PROJECTION_ORTHOGONAL: break; } Plane plane(sliceNormalVector, sliceCoordinates); planeOut = plane; m_lookAtCenter[0] = sliceCoordinates[0]; m_lookAtCenter[1] = sliceCoordinates[1]; m_lookAtCenter[2] = sliceCoordinates[2]; } /** * Set the volume slice viewing transformation. This sets the position and * orientation of the camera. * * @param sliceProjectionType * Type of projection for the slice drawing (oblique, orthogonal) * @param sliceViewPlane * View plane that is displayed. * @param plane * Plane equation of selected slice. */ void BrainOpenGLVolumeSliceDrawing::setVolumeSliceViewingAndModelingTransformations(const VolumeSliceProjectionTypeEnum::Enum sliceProjectionType, const VolumeSliceViewPlaneEnum::Enum sliceViewPlane, const Plane& plane) { /* * Determine model size in screen Y when viewed */ BoundingBox boundingBox; m_volumeDrawInfo[0].volumeFile->getVoxelSpaceBoundingBox(boundingBox); const double centerX = boundingBox.getCenterX(); const double centerY = boundingBox.getCenterY(); const double centerZ = boundingBox.getCenterZ(); /* * Initialize the modelview matrix to the identity matrix * This places the camera at the origin, pointing down the * negative-Z axis with the up vector set to (0,1,0 => * positive-Y is up). */ glMatrixMode(GL_MODELVIEW); glLoadIdentity(); /* * Translate to place center of volume at origin */ double moveToCenterX = 0.0; double moveToCenterY = 0.0; double moveToCenterZ = 0.0; switch (sliceViewPlane) { case VolumeSliceViewPlaneEnum::ALL: CaretAssertMessage(0, "Should never get here"); break; case VolumeSliceViewPlaneEnum::AXIAL: moveToCenterX = -centerX; moveToCenterY = -centerY; break; case VolumeSliceViewPlaneEnum::CORONAL: moveToCenterX = -centerX; moveToCenterY = -centerZ; break; case VolumeSliceViewPlaneEnum::PARASAGITTAL: moveToCenterX = centerY; moveToCenterY = -centerZ; break; } glTranslated(moveToCenterX, moveToCenterY, moveToCenterZ); /* * Set "look at" to origin */ m_lookAtCenter[0] = 0.0; m_lookAtCenter[1] = 0.0; m_lookAtCenter[2] = 0.0; /* * Since an orthographic projection is used, the camera only needs * to be a little bit from the center along the plane's normal vector. */ double planeNormal[3]; plane.getNormalVector(planeNormal); double cameraXYZ[3] = { m_lookAtCenter[0] + planeNormal[0] * BrainOpenGLFixedPipeline::s_gluLookAtCenterFromEyeOffsetDistance, m_lookAtCenter[1] + planeNormal[1] * BrainOpenGLFixedPipeline::s_gluLookAtCenterFromEyeOffsetDistance, m_lookAtCenter[2] + planeNormal[2] * BrainOpenGLFixedPipeline::s_gluLookAtCenterFromEyeOffsetDistance }; /* * Set the up vector which indices which way is up (screen Y) */ float up[3] = { 0.0, 0.0, 0.0 }; switch (sliceViewPlane) { case VolumeSliceViewPlaneEnum::ALL: case VolumeSliceViewPlaneEnum::AXIAL: up[1] = 1.0; break; case VolumeSliceViewPlaneEnum::CORONAL: up[2] = 1.0; break; case VolumeSliceViewPlaneEnum::PARASAGITTAL: up[2] = 1.0; break; } /* * Now set the camera to look at the selected coordinate (center) * with the camera offset a little bit from the center. * This allows the slice's voxels to be drawn in the actual coordinates. */ gluLookAt(cameraXYZ[0], cameraXYZ[1], cameraXYZ[2], m_lookAtCenter[0], m_lookAtCenter[1], m_lookAtCenter[2], up[0], up[1], up[2]); const float* userTranslation = m_browserTabContent->getTranslation(); /* * Apply user translation */ glTranslatef(userTranslation[0], userTranslation[1], userTranslation[2]); /* * For oblique viewing, the up vector needs to be rotated by the * oblique rotation matrix. */ switch (sliceProjectionType) { case VolumeSliceProjectionTypeEnum::VOLUME_SLICE_PROJECTION_OBLIQUE: { Matrix4x4 m = m_browserTabContent->getObliqueVolumeRotationMatrix(); m.invert(); double mat4[16]; m.getMatrixForOpenGL(mat4); glMultMatrixd(mat4); } break; case VolumeSliceProjectionTypeEnum::VOLUME_SLICE_PROJECTION_ORTHOGONAL: break; } /* * Apply user scaling */ const float userScaling = m_browserTabContent->getScaling(); glScalef(userScaling, userScaling, userScaling); glGetDoublev(GL_MODELVIEW_MATRIX, m_viewingMatrix); } /** * Draw the layers type data. * * @param sliceDrawingType * Type of slice drawing (montage, single) * @param sliceProjectionType * Type of projection for the slice drawing (oblique, orthogonal) * @param sliceViewPlane * View plane that is displayed. * @param slicePlane * Plane of the slice. * @param sliceCoordinates * Coordinates of the selected slices. */ void BrainOpenGLVolumeSliceDrawing::drawLayers(const VolumeSliceDrawingTypeEnum::Enum sliceDrawingType, const VolumeSliceProjectionTypeEnum::Enum sliceProjectionType, const VolumeSliceViewPlaneEnum::Enum sliceViewPlane, const Plane& slicePlane, const float sliceCoordinates[3]) { bool drawCrosshairsFlag = true; bool drawFibersFlag = true; bool drawFociFlag = true; bool drawOutlineFlag = true; if (m_modelWholeBrain != NULL) { drawCrosshairsFlag = false; drawFibersFlag = false; drawFociFlag = false; } if ( ! m_identificationModeFlag) { if (slicePlane.isValidPlane()) { /* * Disable culling so that both sides of the triangles/quads are drawn. */ GLboolean cullFaceOn = glIsEnabled(GL_CULL_FACE); glDisable(GL_CULL_FACE); glPushMatrix(); GLboolean depthBufferEnabled = false; glGetBooleanv(GL_DEPTH_TEST, &depthBufferEnabled); /* * Use some polygon offset that will adjust the depth values of the * layers so that the layers depth values place the layers in front of * the volume slice. */ glEnable(GL_POLYGON_OFFSET_FILL); glPolygonOffset(0.0, 1.0); if (drawOutlineFlag) { drawSurfaceOutline(slicePlane); } if (drawFibersFlag) { glDisable(GL_DEPTH_TEST); m_fixedPipelineDrawing->drawFiberOrientations(&slicePlane, StructureEnum::ALL); m_fixedPipelineDrawing->drawFiberTrajectories(&slicePlane, StructureEnum::ALL); if (depthBufferEnabled) { glEnable(GL_DEPTH_TEST); } else { glDisable(GL_DEPTH_TEST); } } if (drawFociFlag) { glDisable(GL_DEPTH_TEST); drawVolumeSliceFoci(slicePlane); if (depthBufferEnabled) { glEnable(GL_DEPTH_TEST); } else { glDisable(GL_DEPTH_TEST); } } glDisable(GL_POLYGON_OFFSET_FILL); if (drawCrosshairsFlag) { glPushMatrix(); drawAxesCrosshairs(sliceProjectionType, sliceDrawingType, sliceViewPlane, sliceCoordinates); glPopMatrix(); if (depthBufferEnabled) { glEnable(GL_DEPTH_TEST); } else { glDisable(GL_DEPTH_TEST); } } glPopMatrix(); if (cullFaceOn) { glEnable(GL_CULL_FACE); } } } } /** * Draw surface outlines on the volume slices * * @param plane * Plane of the volume slice on which surface outlines are drawn. */ void BrainOpenGLVolumeSliceDrawing::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. * * @param plane * Plane of the volume slice on which surface outlines are drawn. */ void BrainOpenGLVolumeSliceDrawing::drawVolumeSliceFoci(const Plane& plane) { SelectionItemFocusVolume* idFocus = m_brain->getSelectionManager()->getVolumeFocusIdentification(); /* * Check for a 'selection' type mode */ bool isSelect = false; switch (m_fixedPipelineDrawing->mode) { case BrainOpenGLFixedPipeline::MODE_DRAWING: break; case BrainOpenGLFixedPipeline::MODE_IDENTIFICATION: if (idFocus->isEnabledForSelection()) { isSelect = true; glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); } else { return; } break; case BrainOpenGLFixedPipeline::MODE_PROJECTION: return; break; } VolumeMappableInterface* underlayVolume = m_volumeDrawInfo[0].volumeFile; float minVoxelSpacing; float maxVoxelSpacing; if ( ! getMinMaxVoxelSpacing(underlayVolume, minVoxelSpacing, maxVoxelSpacing)) { return; } const float sliceThickness = maxVoxelSpacing; const float halfSliceThickness = sliceThickness * 0.5; const DisplayPropertiesFoci* fociDisplayProperties = m_brain->getDisplayPropertiesFoci(); const DisplayGroupEnum::Enum displayGroup = fociDisplayProperties->getDisplayGroupForTab(m_fixedPipelineDrawing->windowTabIndex); const CaretColorEnum::Enum caretColor = fociDisplayProperties->getStandardColorType(displayGroup, m_fixedPipelineDrawing->windowTabIndex); float caretColorRGBA[4]; CaretColorEnum::toRGBFloat(caretColor, caretColorRGBA); if (fociDisplayProperties->isDisplayed(displayGroup, m_fixedPipelineDrawing->windowTabIndex) == false) { return; } const float focusDiameter = fociDisplayProperties->getFociSize(displayGroup, m_fixedPipelineDrawing->windowTabIndex); const FeatureColoringTypeEnum::Enum fociColoringType = fociDisplayProperties->getColoringType(displayGroup, m_fixedPipelineDrawing->windowTabIndex); bool drawAsSpheres = false; switch (fociDisplayProperties->getDrawingType(displayGroup, m_fixedPipelineDrawing->windowTabIndex)) { case FociDrawingTypeEnum::DRAW_AS_SPHERES: drawAsSpheres = true; break; case FociDrawingTypeEnum::DRAW_AS_SQUARES: break; } /* * Process each foci file */ const int32_t numberOfFociFiles = m_brain->getNumberOfFociFiles(); for (int32_t iFile = 0; iFile < numberOfFociFiles; iFile++) { FociFile* fociFile = m_brain->getFociFile(iFile); const GroupAndNameHierarchyModel* classAndNameSelection = fociFile->getGroupAndNameHierarchyModel(); if (classAndNameSelection->isSelected(displayGroup, m_fixedPipelineDrawing->windowTabIndex) == false) { continue; } const GiftiLabelTable* classColorTable = fociFile->getClassColorTable(); const GiftiLabelTable* nameColorTable = fociFile->getNameColorTable(); const int32_t numFoci = fociFile->getNumberOfFoci(); for (int32_t j = 0; j < numFoci; j++) { Focus* focus = fociFile->getFocus(j); const GroupAndNameHierarchyItem* groupNameItem = focus->getGroupNameSelectionItem(); if (groupNameItem != NULL) { if (groupNameItem->isSelected(displayGroup, m_fixedPipelineDrawing->windowTabIndex) == false) { continue; } } float rgba[4] = { 0.0, 0.0, 0.0, 1.0 }; switch (fociColoringType) { case FeatureColoringTypeEnum::FEATURE_COLORING_TYPE_CLASS: if (focus->isClassRgbaValid() == false) { const GiftiLabel* colorLabel = classColorTable->getLabelBestMatching(focus->getClassName()); if (colorLabel != NULL) { colorLabel->getColor(rgba); focus->setClassRgba(rgba); } else { focus->setClassRgba(rgba); } } focus->getClassRgba(rgba); break; case FeatureColoringTypeEnum::FEATURE_COLORING_TYPE_STANDARD_COLOR: rgba[0] = caretColorRGBA[0]; rgba[1] = caretColorRGBA[1]; rgba[2] = caretColorRGBA[2]; rgba[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); } } focus->getNameRgba(rgba); break; } const int32_t numProjections = focus->getNumberOfProjections(); for (int32_t k = 0; k < numProjections; k++) { const SurfaceProjectedItem* spi = focus->getProjection(k); if (spi->isVolumeXYZValid()) { float xyz[3]; spi->getVolumeXYZ(xyz); bool drawIt = false; if (plane.absoluteDistanceToPlane(xyz) < halfSliceThickness) { drawIt = true; } if (drawIt) { glPushMatrix(); glTranslatef(xyz[0], xyz[1], xyz[2]); if (isSelect) { uint8_t idRGBA[4]; m_fixedPipelineDrawing->colorIdentification->addItem(idRGBA, SelectionItemDataTypeEnum::FOCUS_VOLUME, iFile, // file index j, // focus index k);// projection index idRGBA[3] = 255; if (drawAsSpheres) { m_fixedPipelineDrawing->drawSphereWithDiameter(idRGBA, focusDiameter); } else { glColor4ubv(idRGBA); drawSquare(focusDiameter); } } else { if (drawAsSpheres) { m_fixedPipelineDrawing->drawSphereWithDiameter(rgba, focusDiameter); } else { glColor3fv(rgba); drawSquare(focusDiameter); } } glPopMatrix(); } } } } } if (isSelect) { int32_t fociFileIndex = -1; int32_t focusIndex = -1; int32_t focusProjectionIndex = -1; float depth = -1.0; m_fixedPipelineDrawing->getIndexFromColorSelection(SelectionItemDataTypeEnum::FOCUS_VOLUME, m_fixedPipelineDrawing->mouseX, m_fixedPipelineDrawing->mouseY, fociFileIndex, focusIndex, focusProjectionIndex, depth); if (fociFileIndex >= 0) { if (idFocus->isOtherScreenDepthCloserToViewer(depth)) { Focus* focus = m_brain->getFociFile(fociFileIndex)->getFocus(focusIndex); idFocus->setBrain(m_brain); idFocus->setFocus(focus); idFocus->setFociFile(m_brain->getFociFile(fociFileIndex)); idFocus->setFocusIndex(focusIndex); idFocus->setFocusProjectionIndex(focusProjectionIndex); idFocus->setVolumeFile(underlayVolume); idFocus->setScreenDepth(depth); float xyz[3]; const SurfaceProjectedItem* spi = focus->getProjection(focusProjectionIndex); spi->getVolumeXYZ(xyz); m_fixedPipelineDrawing->setSelectedItemScreenXYZ(idFocus, xyz); CaretLogFine("Selected Volume Focus Identification Symbol: " + QString::number(focusIndex)); } } } } /** * Draw the axes crosshairs. * * @param sliceProjectionType * Type of projection for the slice drawing (oblique, orthogonal) * @param sliceDrawingType * Type of slice drawing (montage, single) * @param sliceViewPlane * View plane that is displayed. * @param sliceCoordinates * Coordinates of the selected slices. */ void BrainOpenGLVolumeSliceDrawing::drawAxesCrosshairs(const VolumeSliceProjectionTypeEnum::Enum sliceProjectionType, const VolumeSliceDrawingTypeEnum::Enum sliceDrawingType, const VolumeSliceViewPlaneEnum::Enum sliceViewPlane, const float sliceCoordinates[3]) { CaretPreferences* prefs = SessionManager::get()->getCaretPreferences(); const bool drawCrosshairsFlag = prefs->isVolumeAxesCrosshairsDisplayed(); bool drawCrosshairLabelsFlag = prefs->isVolumeAxesLabelsDisplayed(); switch (sliceDrawingType) { case VolumeSliceDrawingTypeEnum::VOLUME_SLICE_DRAW_MONTAGE: drawCrosshairLabelsFlag = false; break; case VolumeSliceDrawingTypeEnum::VOLUME_SLICE_DRAW_SINGLE: break; } switch (sliceProjectionType) { case VolumeSliceProjectionTypeEnum::VOLUME_SLICE_PROJECTION_ORTHOGONAL: drawAxesCrosshairsOrthoAndOblique(sliceProjectionType, sliceViewPlane, sliceCoordinates, drawCrosshairsFlag, drawCrosshairLabelsFlag); break; case VolumeSliceProjectionTypeEnum::VOLUME_SLICE_PROJECTION_OBLIQUE: { glPushMatrix(); glLoadIdentity(); double mv[16]; glGetDoublev(GL_MODELVIEW_MATRIX, mv); Matrix4x4 mvm; mvm.setMatrixFromOpenGL(mv); float trans[3]; m_browserTabContent->getTranslation(trans); glTranslatef(trans[0], trans[1], trans[2]); drawAxesCrosshairsOrthoAndOblique(sliceProjectionType, sliceViewPlane, sliceCoordinates, drawCrosshairsFlag, drawCrosshairLabelsFlag); glPopMatrix(); } break; } } /** * Draw the axes crosshairs for an orthogonal slice. * * @param sliceProjectionType * Type of projection for the slice drawing (oblique, orthogonal) * @param sliceViewPlane * The slice plane view. * @param sliceCoordinates * Coordinates of the selected slices. * @param drawCrosshairsFlag * If true, draw the crosshairs. * @param drawCrosshairLabelsFlag * If true, draw the crosshair labels. */ void BrainOpenGLVolumeSliceDrawing::drawAxesCrosshairsOrthoAndOblique(const VolumeSliceProjectionTypeEnum::Enum sliceProjectionType, const VolumeSliceViewPlaneEnum::Enum sliceViewPlane, const float sliceCoordinates[3], const bool drawCrosshairsFlag, const bool drawCrosshairLabelsFlag) { bool obliqueModeFlag = false; switch (sliceProjectionType) { case VolumeSliceProjectionTypeEnum::VOLUME_SLICE_PROJECTION_OBLIQUE: obliqueModeFlag = true; break; case VolumeSliceProjectionTypeEnum::VOLUME_SLICE_PROJECTION_ORTHOGONAL: break; } GLboolean depthEnabled = GL_FALSE; glGetBooleanv(GL_DEPTH_TEST, &depthEnabled); glDisable(GL_DEPTH_TEST); const float bigValue = 10000.0; float horizontalAxisStartXYZ[3] = { sliceCoordinates[0], sliceCoordinates[1], sliceCoordinates[2] }; float horizontalAxisEndXYZ[3] = { sliceCoordinates[0], sliceCoordinates[1], sliceCoordinates[2] }; float verticalAxisStartXYZ[3] = { sliceCoordinates[0], sliceCoordinates[1], sliceCoordinates[2] }; float verticalAxisEndXYZ[3] = { sliceCoordinates[0], sliceCoordinates[1], sliceCoordinates[2] }; if (obliqueModeFlag) { switch (sliceViewPlane) { case VolumeSliceViewPlaneEnum::ALL: break; case VolumeSliceViewPlaneEnum::AXIAL: break; case VolumeSliceViewPlaneEnum::CORONAL: horizontalAxisStartXYZ[0] = sliceCoordinates[0]; horizontalAxisStartXYZ[1] = sliceCoordinates[2]; horizontalAxisStartXYZ[2] = sliceCoordinates[1]; horizontalAxisEndXYZ[0] = sliceCoordinates[0]; horizontalAxisEndXYZ[1] = sliceCoordinates[2]; horizontalAxisEndXYZ[2] = sliceCoordinates[1]; verticalAxisStartXYZ[0] = sliceCoordinates[0]; verticalAxisStartXYZ[1] = sliceCoordinates[1]; verticalAxisStartXYZ[2] = sliceCoordinates[2]; verticalAxisEndXYZ[0] = sliceCoordinates[0]; verticalAxisEndXYZ[1] = sliceCoordinates[1]; verticalAxisEndXYZ[2] = sliceCoordinates[2]; break; case VolumeSliceViewPlaneEnum::PARASAGITTAL: horizontalAxisStartXYZ[0] = sliceCoordinates[1]; horizontalAxisStartXYZ[1] = sliceCoordinates[2]; horizontalAxisStartXYZ[2] = sliceCoordinates[0]; horizontalAxisEndXYZ[0] = sliceCoordinates[1]; horizontalAxisEndXYZ[1] = sliceCoordinates[2]; horizontalAxisEndXYZ[2] = sliceCoordinates[0]; verticalAxisStartXYZ[0] = -sliceCoordinates[1]; verticalAxisStartXYZ[1] = sliceCoordinates[0]; verticalAxisStartXYZ[2] = sliceCoordinates[2]; verticalAxisEndXYZ[0] = -sliceCoordinates[1]; verticalAxisEndXYZ[1] = sliceCoordinates[0]; verticalAxisEndXYZ[2] = sliceCoordinates[2]; break; } } float axialRGBA[4]; getAxesColor(VolumeSliceViewPlaneEnum::AXIAL, axialRGBA); float coronalRGBA[4]; getAxesColor(VolumeSliceViewPlaneEnum::CORONAL, coronalRGBA); float paraRGBA[4]; getAxesColor(VolumeSliceViewPlaneEnum::PARASAGITTAL, paraRGBA); AString horizontalLeftText = ""; AString horizontalRightText = ""; AString verticalBottomText = ""; AString verticalTopText = ""; float* horizontalAxisRGBA = axialRGBA; float* verticalAxisRGBA = axialRGBA; switch (sliceViewPlane) { case VolumeSliceViewPlaneEnum::ALL: break; case VolumeSliceViewPlaneEnum::AXIAL: horizontalLeftText = "L"; horizontalRightText = "R"; horizontalAxisRGBA = coronalRGBA; horizontalAxisStartXYZ[0] -= bigValue; horizontalAxisEndXYZ[0] += bigValue; verticalBottomText = "P"; verticalTopText = "A"; verticalAxisRGBA = paraRGBA; verticalAxisStartXYZ[1] -= bigValue; verticalAxisEndXYZ[1] += bigValue; break; case VolumeSliceViewPlaneEnum::CORONAL: horizontalLeftText = "L"; horizontalRightText = "R"; horizontalAxisRGBA = axialRGBA; if (obliqueModeFlag) { horizontalAxisStartXYZ[0] -= bigValue; horizontalAxisEndXYZ[0] += bigValue; } else { horizontalAxisStartXYZ[0] -= bigValue; horizontalAxisEndXYZ[0] += bigValue; } verticalBottomText = "I"; verticalTopText = "S"; verticalAxisRGBA = paraRGBA; if (obliqueModeFlag) { verticalAxisStartXYZ[1] -= bigValue; verticalAxisEndXYZ[1] += bigValue; } else { verticalAxisStartXYZ[2] -= bigValue; verticalAxisEndXYZ[2] += bigValue; } break; case VolumeSliceViewPlaneEnum::PARASAGITTAL: horizontalLeftText = "A"; horizontalRightText = "P"; horizontalAxisRGBA = axialRGBA; if (obliqueModeFlag) { horizontalAxisStartXYZ[0] -= bigValue; horizontalAxisEndXYZ[0] += bigValue; } else { horizontalAxisStartXYZ[1] -= bigValue; horizontalAxisEndXYZ[1] += bigValue; } verticalBottomText = "I"; verticalTopText = "S"; verticalAxisRGBA = coronalRGBA; if (obliqueModeFlag) { verticalAxisStartXYZ[1] -= bigValue; verticalAxisEndXYZ[1] += bigValue; } else { verticalAxisStartXYZ[2] -= bigValue; verticalAxisEndXYZ[2] += bigValue; } break; } GLint viewport[4]; glGetIntegerv(GL_VIEWPORT, viewport); const int textOffset = 15; const int textLeftWindowXY[2] = { textOffset, (viewport[3] / 2) }; const int textRightWindowXY[2] = { viewport[2] - textOffset, (viewport[3] / 2) }; const int textBottomWindowXY[2] = { viewport[2] / 2, textOffset }; const int textTopWindowXY[2] = { (viewport[2] / 2), viewport[3] - textOffset }; /* * 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(); } 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], m_fixedPipelineDrawing->m_backgroundColorByte[2], 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); annotationText.setBoldStyleEnabled(true); annotationText.setFontPointSize(fontSize); annotationText.setBackgroundColor(CaretColorEnum::CUSTOM); annotationText.setTextColor(CaretColorEnum::CUSTOM); annotationText.setCustomTextColor(horizontalAxisRGBA); annotationText.setCustomBackgroundColor(backgroundRGBA); annotationText.setText(horizontalLeftText); m_fixedPipelineDrawing->drawTextAtViewportCoords(textLeftWindowXY[0], textLeftWindowXY[1], annotationText); annotationText.setText(horizontalRightText); m_fixedPipelineDrawing->drawTextAtViewportCoords(textRightWindowXY[0], textRightWindowXY[1], annotationText); annotationText.setCustomTextColor(verticalAxisRGBA); annotationText.setText(verticalBottomText); m_fixedPipelineDrawing->drawTextAtViewportCoords(textBottomWindowXY[0], textBottomWindowXY[1], annotationText); annotationText.setText(verticalTopText); m_fixedPipelineDrawing->drawTextAtViewportCoords(textTopWindowXY[0], textTopWindowXY[1], annotationText); } if (depthEnabled) { glEnable(GL_DEPTH_TEST); } } /** * Get the RGBA coloring for a slice view plane. * * @param sliceViewPlane * The slice view plane. * @param rgbaOut * Output colors ranging 0.0 to 1.0 */ void BrainOpenGLVolumeSliceDrawing::getAxesColor(const VolumeSliceViewPlaneEnum::Enum sliceViewPlane, float rgbaOut[4]) const { switch (sliceViewPlane) { case VolumeSliceViewPlaneEnum::ALL: CaretAssert(0); break; case VolumeSliceViewPlaneEnum::AXIAL: rgbaOut[0] = 0.0; rgbaOut[1] = 0.0; rgbaOut[2] = 1.0; rgbaOut[3] = 1.0; break; case VolumeSliceViewPlaneEnum::CORONAL: rgbaOut[0] = 0.0; rgbaOut[1] = 1.0; rgbaOut[2] = 0.0; rgbaOut[3] = 1.0; break; case VolumeSliceViewPlaneEnum::PARASAGITTAL: rgbaOut[0] = 1.0; rgbaOut[1] = 0.0; rgbaOut[2] = 0.0; rgbaOut[3] = 1.0; break; } } /** * Draw a one millimeter square facing the user. * NOTE: This method will alter the current * modelviewing matrices so caller may need * to enclose the call to this method within * glPushMatrix() and glPopMatrix(). * * @param size * Size of square. */ void BrainOpenGLVolumeSliceDrawing::drawSquare(const float size) { const float length = size * 0.5; /* * Draw both front and back side since in some instances, * such as surface montage, we are viweing from the far * side (from back of monitor) */ glBegin(GL_QUADS); glNormal3f(0.0, 0.0, 1.0); glVertex3f(-length, -length, 0.0); glVertex3f( length, -length, 0.0); glVertex3f( length, length, 0.0); glVertex3f(-length, length, 0.0); glNormal3f(0.0, 0.0, -1.0); glVertex3f(-length, -length, 0.0); glVertex3f(-length, length, 0.0); glVertex3f( length, length, 0.0); glVertex3f( length, -length, 0.0); glEnd(); } /** * Get the minimum and maximum distance between adjacent voxels in all * slices planes. Output spacing value are always non-negative even if * a right-to-left orientation. * * @param volume * Volume for which min/max spacing is requested. * @param minSpacingOut * Output minimum spacing. * @param maxSpacingOut * Output maximum spacing. * @return * True if min and max spacing are greater than zero. */ bool BrainOpenGLVolumeSliceDrawing::getMinMaxVoxelSpacing(const VolumeMappableInterface* volume, float& minSpacingOut, float& maxSpacingOut) const { CaretAssert(volume); float originX, originY, originZ; float x1, y1, z1; volume->indexToSpace(0, 0, 0, originX, originY, originZ); volume->indexToSpace(1, 1, 1, x1, y1, z1); const float dx = std::fabs(x1 - originX); const float dy = std::fabs(y1 - originY); const float dz = std::fabs(z1 - originZ); minSpacingOut = std::min(std::min(dx, dy), dz); maxSpacingOut = std::max(std::max(dx, dy), dz); if ((minSpacingOut > 0.0) && (maxSpacingOut > 0.0)) { return true; } return false; } /** * Draw orientation axes * * @param viewport * The viewport region for the orientation axes. */ void BrainOpenGLVolumeSliceDrawing::drawOrientationAxes(const int viewport[4]) { CaretPreferences* prefs = SessionManager::get()->getCaretPreferences(); const bool drawCylindersFlag = prefs->isVolumeAxesCrosshairsDisplayed(); const bool drawLabelsFlag = prefs->isVolumeAxesLabelsDisplayed(); /* * Set the viewport */ glViewport(viewport[0], viewport[1], viewport[2], viewport[3]); const double viewportWidth = viewport[2]; const double viewportHeight = viewport[3]; /* * Determine bounds for orthographic projection */ const double maxCoord = 100.0; const double minCoord = -maxCoord; double left = 0.0; double right = 0.0; double top = 0.0; double bottom = 0.0; const double nearDepth = -1000.0; const double farDepth = 1000.0; if (viewportHeight > viewportWidth) { left = minCoord; right = maxCoord; const double aspectRatio = (viewportHeight / viewportWidth); top = maxCoord * aspectRatio; bottom = minCoord * aspectRatio; } else { const double aspectRatio = (viewportWidth / viewportHeight); top = maxCoord; bottom = minCoord; left = minCoord * aspectRatio; right = maxCoord * aspectRatio; } /* * Set the orthographic projection */ glMatrixMode(GL_PROJECTION); glPushMatrix(); glLoadIdentity(); glOrtho(left, right, bottom, top, nearDepth, farDepth); glMatrixMode(GL_MODELVIEW); glPushMatrix(); glLoadIdentity(); { /* * Set the viewing transformation, places 'eye' so that it looks * at the 'model' which is, in this case, the axes */ double eyeX = 0.0; double eyeY = 0.0; double eyeZ = BrainOpenGLFixedPipeline::s_gluLookAtCenterFromEyeOffsetDistance; //100.0; const double centerX = 0; const double centerY = 0; const double centerZ = 0; const double upX = 0; const double upY = 1; const double upZ = 0; gluLookAt(eyeX, eyeY, eyeZ, centerX, centerY, centerZ, upX, upY, upZ); /* * Set the modeling transformation */ const Matrix4x4 obliqueRotationMatrix = m_browserTabContent->getObliqueVolumeRotationMatrix(); double rotationMatrix[16]; obliqueRotationMatrix.getMatrixForOpenGL(rotationMatrix); glMultMatrixd(rotationMatrix); /* * Disable depth buffer. Otherwise, when volume slices are drawn * black regions of the slices may set depth buffer and the occlude * the axes from display. */ GLboolean depthBufferEnabled = false; glGetBooleanv(GL_DEPTH_TEST, &depthBufferEnabled); glDisable(GL_DEPTH_TEST); const float red[4] = { 1.0, 0.0, 0.0, 1.0 }; const float green[4] = { 0.0, 1.0, 0.0, 1.0 }; const float blue[4] = { 0.0, 0.0, 1.0, 1.0 }; const double axisMaxCoord = maxCoord * 0.8; const double axisMinCoord = -axisMaxCoord; const double textMaxCoord = maxCoord * 0.9; 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 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 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 axesCrosshairRadius = 1.0; if (drawCylindersFlag) { m_fixedPipelineDrawing->drawCylinder(blue, axialPlaneMin, axialPlaneMax, axesCrosshairRadius); } AnnotationPointSizeText annotationText(AnnotationAttributesDefaultTypeEnum::NORMAL); annotationText.setHorizontalAlignment(AnnotationTextAlignHorizontalEnum::CENTER); annotationText.setVerticalAlignment(AnnotationTextAlignVerticalEnum::MIDDLE); annotationText.setFontPointSize(AnnotationTextFontPointSizeEnum::SIZE14); annotationText.setCoordinateSpace(AnnotationCoordinateSpaceEnum::STEREOTAXIC); annotationText.setLineColor(CaretColorEnum::NONE); annotationText.setTextColor(CaretColorEnum::CUSTOM); if (drawLabelsFlag) { annotationText.setCustomTextColor(blue); annotationText.setText("I"); m_fixedPipelineDrawing->drawTextAtModelCoords(axialTextMin, annotationText); annotationText.setText("S"); m_fixedPipelineDrawing->drawTextAtModelCoords(axialTextMax, annotationText); } if (drawCylindersFlag) { m_fixedPipelineDrawing->drawCylinder(green, coronalPlaneMin, coronalPlaneMax, axesCrosshairRadius); } if (drawLabelsFlag) { annotationText.setCustomTextColor(green); annotationText.setText("L"); m_fixedPipelineDrawing->drawTextAtModelCoords(coronalTextMin, annotationText); annotationText.setText("R"); m_fixedPipelineDrawing->drawTextAtModelCoords(coronalTextMax, annotationText); } if (drawCylindersFlag) { m_fixedPipelineDrawing->drawCylinder(red, paraPlaneMin, paraPlaneMax, axesCrosshairRadius); } if (drawLabelsFlag) { annotationText.setCustomTextColor(red); annotationText.setText("P"); m_fixedPipelineDrawing->drawTextAtModelCoords(paraTextMin, annotationText); annotationText.setText("A"); m_fixedPipelineDrawing->drawTextAtModelCoords(paraTextMax, annotationText); } } glPopMatrix(); glMatrixMode(GL_PROJECTION); glPopMatrix(); glMatrixMode(GL_MODELVIEW); } /** * Set the orthographic projection. * * @param sliceViewPlane * View plane that is displayed. * @param viewport * The viewport. */ void BrainOpenGLVolumeSliceDrawing::setOrthographicProjection(const VolumeSliceViewPlaneEnum::Enum sliceViewPlane, const int viewport[4]) { /* * Determine model size in screen Y when viewed */ BoundingBox boundingBox; m_volumeDrawInfo[0].volumeFile->getVoxelSpaceBoundingBox(boundingBox); /* * Set top and bottom to the min/max coordinate * that runs vertically on the screen */ double modelTop = 200.0; double modelBottom = -200.0; switch (sliceViewPlane) { case VolumeSliceViewPlaneEnum::ALL: CaretAssertMessage(0, "Should never get here"); break; case VolumeSliceViewPlaneEnum::AXIAL: modelTop = boundingBox.getMaxY(); modelBottom = boundingBox.getMinY(); break; case VolumeSliceViewPlaneEnum::CORONAL: modelTop = boundingBox.getMaxZ(); modelBottom = boundingBox.getMinZ(); break; case VolumeSliceViewPlaneEnum::PARASAGITTAL: modelTop = boundingBox.getMaxZ(); modelBottom = boundingBox.getMinZ(); break; } /* * Scale ratio makes region slightly larger than model */ const double zoom = 1.0; double scaleRatio = (1.0 / 0.98); if (zoom > 0.0) { scaleRatio /= zoom; } modelTop *= scaleRatio; modelBottom *= scaleRatio; /* * Determine aspect ratio of viewport */ const double viewportWidth = viewport[2]; const double viewportHeight = viewport[3]; const double aspectRatio = (viewportWidth / viewportHeight); /* * Set bounds of orthographic projection */ const double halfModelY = ((modelTop - modelBottom) / 2.0); const double orthoBottom = -halfModelY; const double orthoTop = halfModelY; const double orthoLeft = -halfModelY * aspectRatio; const double orthoRight = halfModelY * aspectRatio; const double nearDepth = -1000.0; const double farDepth = 1000.0; m_orthographicBounds[0] = orthoLeft; m_orthographicBounds[1] = orthoRight; m_orthographicBounds[2] = orthoBottom; m_orthographicBounds[3] = orthoTop; m_orthographicBounds[4] = nearDepth; m_orthographicBounds[5] = farDepth; glMatrixMode(GL_PROJECTION); glLoadIdentity(); glOrtho(m_orthographicBounds[0], m_orthographicBounds[1], m_orthographicBounds[2], m_orthographicBounds[3], m_orthographicBounds[4], m_orthographicBounds[5]); glMatrixMode(GL_MODELVIEW); } /** * Draw the voxels in an orthogonal slice. * * @param sliceNormalVector * Normal vector of the slice plane. * @param coordinate * Coordinate of first voxel in the slice (bottom left as begin viewed) * @param rowStep * Three-dimensional step to next row. * @param columnStep * Three-dimensional step to next column. * @param numberOfColumns * Number of columns in the slice. * @param numberOfRows * Number of rows in the slice. * @param sliceRGBA * RGBA coloring for voxels in the slice. * @param validVoxelCount * Number of voxels with valid coloring * @param volumeInterface * Index of the volume being drawn. * @param volumeIndex * Selected map in the volume being drawn. * @param mapIndex * Selected map in the volume being drawn. * @param sliceOpacity * Opacity from the overlay. */ void BrainOpenGLVolumeSliceDrawing::drawOrthogonalSliceVoxels(const float sliceNormalVector[3], const float coordinate[3], const float rowStep[3], const float columnStep[3], const int64_t numberOfColumns, const int64_t numberOfRows, const std::vector& sliceRGBA, const int64_t validVoxelCount, const VolumeMappableInterface* volumeInterface, const int32_t volumeIndex, const int32_t mapIndex, const uint8_t sliceOpacity) { /* * There are two ways to draw the voxels. * * Quad Indices: This method submits each vertex (coordinate, normal, rgba) * one time BUT it submits EVERY vertex in the slice. Note that the vertex * is shared by four voxels (except along an edge). For each valid voxel, * it submits the indices of the voxel's four vertices. This method is * efficient when many voxels are drawn since each vertex is submitted to * OpenGL one time and is shared by up to four voxels. However, when * only a few voxels are drawn, many unused vertices are submitted. * * Single Quads: For each voxel, this method submits the four vertices * (coordinate, normal, rgba) for each voxel drawn. Even though adjacent * voxels share vertices, the vertices are submitted for each voxel. When * only a small number of voxels in the slice are drawn, this method is * efficient since only the needed vertex data is submitted. However, * when many voxels are drawn, many vertices are duplicated making the * drawing inefficient. * * So, estimate the bytes that are passed to OpenGL for each drawing * mode and choose the drawing mode that requires the smallest number * of bytes. */ /* * Each vertex requires 28 bytes * (3 float xyz, 3 float normal xyz + 4 bytes color). * * Single quads uses four vertices per quad. * * Index quads requires four 4-byte ints for the quad's indices. */ const int64_t bytesPerVertex = 28; const int64_t totalVertexBytes = ((numberOfColumns + 1) * (numberOfRows + 1) * bytesPerVertex); const int64_t singleQuadBytes = (validVoxelCount * bytesPerVertex * 4); const int64_t indexQuadBytes = (totalVertexBytes + (16 * validVoxelCount)); bool drawWithQuadIndicesFlag = false; if (indexQuadBytes < singleQuadBytes) { drawWithQuadIndicesFlag = true; } if (drawWithQuadIndicesFlag) { drawOrthogonalSliceVoxelsQuadIndicesAndStrips(sliceNormalVector, coordinate, rowStep, columnStep, numberOfColumns, numberOfRows, sliceRGBA, volumeInterface, volumeIndex, mapIndex, sliceOpacity); } else { drawOrthogonalSliceVoxelsSingleQuads(sliceNormalVector, coordinate, rowStep, columnStep, numberOfColumns, numberOfRows, sliceRGBA, volumeInterface, volumeIndex, mapIndex, sliceOpacity); } } /** * Draw the voxels in an orthogonal slice with single quads. * * Four coordinates, normals, and colors are sent to OpenGL for each * quad that is drawn. This may be efficent when only a few voxels * are drawn but very inefficient when many voxels are drawn. * * @param sliceNormalVector * Normal vector of the slice plane. * @param coordinate * Coordinate of first voxel in the slice (bottom left as begin viewed) * @param rowStep * Three-dimensional step to next row. * @param columnStep * Three-dimensional step to next column. * @param numberOfColumns * Number of columns in the slice. * @param numberOfRows * Number of rows in the slice. * @param sliceRGBA * RGBA coloring for voxels in the slice. * @param volumeInterface * Index of the volume being drawn. * @param volumeIndex * Selected map in the volume being drawn. * @param mapIndex * Selected map in the volume being drawn. * @param sliceOpacity * Opacity from the overlay. */ void BrainOpenGLVolumeSliceDrawing::drawOrthogonalSliceVoxelsSingleQuads(const float sliceNormalVector[3], const float coordinate[3], const float rowStep[3], const float columnStep[3], const int64_t numberOfColumns, const int64_t numberOfRows, const std::vector& sliceRGBA, const VolumeMappableInterface* volumeInterface, const int32_t volumeIndex, const int32_t mapIndex, const uint8_t sliceOpacity) { /* * When performing voxel identification for editing voxels, * we need to draw EVERY voxel since the user may click * regions where the voxels are "off". */ bool volumeEditingDrawAllVoxelsFlag = false; if (m_identificationModeFlag) { SelectionItemVoxelEditing* voxelEditID = m_brain->getSelectionManager()->getVoxelEditingIdentification(); if (voxelEditID->isEnabledForSelection()) { const VolumeFile* vf = dynamic_cast(volumeInterface); if (vf == voxelEditID->getVolumeFileForEditing()) { volumeEditingDrawAllVoxelsFlag = true; } } } const int64_t numVoxelsInSlice = numberOfColumns * numberOfRows; /* * Allocate for quadrilateral drawing */ const int64_t numQuadCoords = numVoxelsInSlice * 12; const int64_t numQuadRgba = numVoxelsInSlice * 16; std::vector voxelQuadCoordinates; std::vector voxelQuadNormals; std::vector voxelQuadRgba; voxelQuadCoordinates.reserve(numQuadCoords); voxelQuadNormals.reserve(numQuadCoords); voxelQuadRgba.reserve(numQuadRgba); /* * Step to next row or column voxel */ const float rowStepX = rowStep[0]; const float rowStepY = rowStep[1]; const float rowStepZ = rowStep[2]; const float columnStepX = columnStep[0]; const float columnStepY = columnStep[1]; const float columnStepZ = columnStep[2]; /* * Draw each row */ for (int64_t jRow = 0; jRow < numberOfRows; jRow++) { /* * Coordinates on left side of row */ float rowBottomLeft[3] = { coordinate[0] + (jRow * rowStepX), coordinate[1] + (jRow * rowStepY), coordinate[2] + (jRow * rowStepZ) }; float rowTopLeft[3] = { rowBottomLeft[0] + rowStepX, rowBottomLeft[1] + rowStepY, rowBottomLeft[2] + rowStepZ }; /* * Draw each voxel in its column */ for (int64_t iCol = 0; iCol < numberOfColumns; iCol++) { /* * Offset of voxel in coloring. */ const int64_t sliceRgbaOffset = (4 * (iCol + (numberOfColumns * jRow))); const int64_t alphaOffset = sliceRgbaOffset + 3; uint8_t rgba[4] = { 0, 0, 0, 0 }; /* * Negative alpha means do not display */ CaretAssertVectorIndex(sliceRGBA, alphaOffset); if (sliceRGBA[alphaOffset] <= 0) { if (volumeIndex == 0) { /* * For first drawn volume, use an alpha of 255 so * so that black is drawn */ rgba[3] = 255; /* * OVERRIDES BLACK VOXEL ABOVE (255) * For first drawn volume, use an alpha of zero so * that the background shows through */ rgba[3] = 0; } } else { /* * Use overlay's opacity */ rgba[0] = sliceRGBA[sliceRgbaOffset]; rgba[1] = sliceRGBA[sliceRgbaOffset + 1]; rgba[2] = sliceRGBA[sliceRgbaOffset + 2]; rgba[3] = sliceOpacity; } /* * Set coordinates of voxel corners */ float voxelBottomLeft[3] = { rowBottomLeft[0] + (iCol * columnStepX), rowBottomLeft[1] + (iCol * columnStepY), rowBottomLeft[2] + (iCol * columnStepZ) }; float voxelBottomRight[3] = { voxelBottomLeft[0] + columnStepX, voxelBottomLeft[1] + columnStepY, voxelBottomLeft[2] + columnStepZ }; float voxelTopLeft[3] = { rowTopLeft[0] + (iCol * columnStepX), rowTopLeft[1] + (iCol * columnStepY), rowTopLeft[2] + (iCol * columnStepZ) }; float voxelTopRight[3] = { voxelTopLeft[0] + columnStepX, voxelTopLeft[1] + columnStepY, voxelTopLeft[2] + columnStepZ }; /* * Need to draw ALL voxels if performing * identificaiton for voxel editing. */ if (volumeEditingDrawAllVoxelsFlag) { rgba[3] = 255; } /* * Draw voxel based upon opacity */ if (rgba[3] > 0) { if (m_identificationModeFlag) { /* * Add info about voxel for identication. */ int64_t voxelI = 0; int64_t voxelJ = 0; int64_t voxelK = 0; const float voxelCenterX = (voxelBottomLeft[0] + voxelTopRight[0]) / 2.0; const float voxelCenterY = (voxelBottomLeft[1] + voxelTopRight[1]) / 2.0; const float voxelCenterZ = (voxelBottomLeft[2] + voxelTopRight[2]) / 2.0; volumeInterface->enclosingVoxel(voxelCenterX, voxelCenterY, voxelCenterZ, voxelI, voxelJ, voxelK); const float voxelDiffXYZ[3] = { voxelTopRight[0] - voxelBottomLeft[0], voxelTopRight[1] - voxelBottomLeft[1], voxelTopRight[2] - voxelBottomLeft[2], }; addVoxelToIdentification(volumeIndex, mapIndex, voxelI, voxelJ, voxelK, voxelDiffXYZ, rgba); } /* * Add voxel to quadrilaterals */ voxelQuadCoordinates.push_back(voxelBottomLeft[0]); voxelQuadCoordinates.push_back(voxelBottomLeft[1]); voxelQuadCoordinates.push_back(voxelBottomLeft[2]); voxelQuadCoordinates.push_back(voxelBottomRight[0]); voxelQuadCoordinates.push_back(voxelBottomRight[1]); voxelQuadCoordinates.push_back(voxelBottomRight[2]); voxelQuadCoordinates.push_back(voxelTopRight[0]); voxelQuadCoordinates.push_back(voxelTopRight[1]); voxelQuadCoordinates.push_back(voxelTopRight[2]); voxelQuadCoordinates.push_back(voxelTopLeft[0]); voxelQuadCoordinates.push_back(voxelTopLeft[1]); voxelQuadCoordinates.push_back(voxelTopLeft[2]); for (int32_t iNormalAndColor = 0; iNormalAndColor < 4; iNormalAndColor++) { voxelQuadRgba.push_back(rgba[0]); voxelQuadRgba.push_back(rgba[1]); voxelQuadRgba.push_back(rgba[2]); voxelQuadRgba.push_back(rgba[3]); voxelQuadNormals.push_back(sliceNormalVector[0]); voxelQuadNormals.push_back(sliceNormalVector[1]); voxelQuadNormals.push_back(sliceNormalVector[2]); } } } } /* * Draw the voxels. */ if (voxelQuadCoordinates.empty() == false) { BrainOpenGLPrimitiveDrawing::drawQuads(voxelQuadCoordinates, voxelQuadNormals, voxelQuadRgba); } } /** * Draw the voxels in an orthogonal slice using quad indices or strips. * * Each vertex (coordinate, its normal vector, and its color) is sent to OpenGL * one time. Index arrays are used to specify the vertices when drawing the * quads. * * This is efficient when many voxels are drawn but may be inefficent * when only a few voxels are drawn. * * @param sliceNormalVector * Normal vector of the slice plane. * @param firstVoxelCoordinate * Coordinate of first voxel in the slice (bottom left as begin viewed) * @param rowStep * Three-dimensional step to next row. * @param columnStep * Three-dimensional step to next column. * @param numberOfColumns * Number of columns in the slice. * @param numberOfRows * Number of rows in the slice. * @param sliceRGBA * RGBA coloring for voxels in the slice. * @param volumeInterface * Index of the volume being drawn. * @param volumeIndex * Selected map in the volume being drawn. * @param mapIndex * Selected map in the volume being drawn. * @param sliceOpacity * Opacity from the overlay. */ void BrainOpenGLVolumeSliceDrawing::drawOrthogonalSliceVoxelsQuadIndicesAndStrips(const float sliceNormalVector[3], const float firstVoxelCoordinate[3], const float rowStep[3], const float columnStep[3], const int64_t numberOfColumns, const int64_t numberOfRows, const std::vector& sliceRGBA, const VolumeMappableInterface* volumeInterface, const int32_t volumeIndex, const int32_t mapIndex, const uint8_t sliceOpacity) { const bool debugFlag = false; enum DrawType { DRAW_QUADS, DRAW_QUAD_STRIPS }; const DrawType drawType = DRAW_QUADS; /* * When performing voxel identification for editing voxels, * we need to draw EVERY voxel since the user may click * regions where the voxels are "off". */ bool volumeEditingDrawAllVoxelsFlag = false; if (m_identificationModeFlag) { SelectionItemVoxelEditing* voxelEditID = m_brain->getSelectionManager()->getVoxelEditingIdentification(); if (voxelEditID->isEnabledForSelection()) { const VolumeFile* vf = dynamic_cast(volumeInterface); if (vf == voxelEditID->getVolumeFileForEditing()) { volumeEditingDrawAllVoxelsFlag = true; } } } /* * Allocate vectors for quadrilateral drawing */ const int64_t totalCoordElements = (numberOfColumns + 1) * (numberOfRows + 1); const int64_t numQuadStripCoords = totalCoordElements * 3; const int64_t numQuadStripRGBA = totalCoordElements * 4; std::vector voxelQuadCoordinates; std::vector voxelQuadNormals; std::vector voxelQuadRgba; voxelQuadCoordinates.reserve(numQuadStripCoords); voxelQuadNormals.reserve(numQuadStripCoords); voxelQuadRgba.reserve(numQuadStripRGBA); /* * Step to next row or column voxel */ const float rowStepX = rowStep[0]; const float rowStepY = rowStep[1]; const float rowStepZ = rowStep[2]; const float columnStepX = columnStep[0]; const float columnStepY = columnStep[1]; const float columnStepZ = columnStep[2]; const float voxelStepX = rowStepX + columnStepX; const float voxelStepY = rowStepY + columnStepY; const float voxelStepZ = rowStepZ + columnStepZ; const float voxelStepXYZ[3] = { voxelStepX, voxelStepY, voxelStepZ }; const float halfVoxelStepX = (voxelStepX / 2.0); const float halfVoxelStepY = (voxelStepY / 2.0); const float halfVoxelStepZ = (voxelStepZ / 2.0); int64_t numberOfVoxelsToDraw = 0; int64_t voxelJMin = 10000000; int64_t voxelJMax = -10000000; /* * Loop through column COORDINATES */ float columnBottomCoord[3] = { firstVoxelCoordinate[0], firstVoxelCoordinate[1], firstVoxelCoordinate[2] }; for (int64_t iCol = 0; iCol <= numberOfColumns; iCol++) { if (iCol > 0) { columnBottomCoord[0] += columnStepX; columnBottomCoord[1] += columnStepY; columnBottomCoord[2] += columnStepZ; } /* * Loop through the row COORDINATES */ float rowCoord[3] = { columnBottomCoord[0], columnBottomCoord[1], columnBottomCoord[2] }; for (int64_t jRow = 0; jRow <= numberOfRows; jRow++) { if (jRow > 0) { rowCoord[0] += rowStepX; rowCoord[1] += rowStepY; rowCoord[2] += rowStepZ; } voxelQuadCoordinates.push_back(rowCoord[0]); voxelQuadCoordinates.push_back(rowCoord[1]); voxelQuadCoordinates.push_back(rowCoord[2]); voxelQuadNormals.push_back(sliceNormalVector[0]); voxelQuadNormals.push_back(sliceNormalVector[1]); voxelQuadNormals.push_back(sliceNormalVector[2]); uint8_t rgba[4] = { 0, 0, 0, 0 }; /* * With FLAT shading: * Quads: Uses top left coordinate for quad coloring * Quad Strip: Uses top right coordinate for quad coloring * So, the color is only set for this coordinate */ int64_t iColRGBA = iCol; int64_t jRowRGBA = jRow; switch (drawType) { case DRAW_QUADS: if (iColRGBA >= numberOfColumns) { iColRGBA = numberOfColumns - 1; } jRowRGBA = jRow - 1; break; case DRAW_QUAD_STRIPS: iColRGBA = iCol - 1; jRowRGBA = jRow - 1; break; } if ((iColRGBA >= 0) && (jRowRGBA >= 0)) { const int64_t voxelOffset = (iColRGBA + (numberOfColumns * jRowRGBA)); if (debugFlag) { std::cout << "col=" << iCol << " row=" << jRow << " voxel-offset=" << voxelOffset << std::endl; } /* * Offset of voxel in coloring. * Note that colors are stored in rows */ int64_t sliceRgbaOffset = (4 * voxelOffset); /* * An alpha greater than zero means the voxel is displayed */ const int64_t alphaOffset = sliceRgbaOffset + 3; CaretAssertVectorIndex(sliceRGBA, alphaOffset); if (sliceRGBA[alphaOffset] > 0) { /* * Use overlay's opacity for the voxel */ rgba[0] = sliceRGBA[sliceRgbaOffset]; rgba[1] = sliceRGBA[sliceRgbaOffset + 1]; rgba[2] = sliceRGBA[sliceRgbaOffset + 2]; rgba[3] = sliceOpacity; } } /* * Voxel editing requires drawing of all voxels so that * "off" voxels can be turned "on". */ if (volumeEditingDrawAllVoxelsFlag) { rgba[3] = 255; } /* * Draw voxel if non-zero opacity */ if (rgba[3] > 0) { ++numberOfVoxelsToDraw; if (m_identificationModeFlag) { /* * Identification information is encoded in the * RGBA coloring. */ const float voxelCenterX = rowCoord[0] -rowStep[0] + halfVoxelStepX; const float voxelCenterY = rowCoord[1] -rowStep[1] + halfVoxelStepY; const float voxelCenterZ = rowCoord[2] -rowStep[2] + halfVoxelStepZ; int64_t voxelI = 0; int64_t voxelJ = 0; int64_t voxelK = 0; volumeInterface->enclosingVoxel(voxelCenterX, voxelCenterY, voxelCenterZ, voxelI, voxelJ, voxelK); voxelJMin = std::min(voxelJMin, voxelJ); voxelJMax = std::max(voxelJMax, voxelJ); addVoxelToIdentification(volumeIndex, mapIndex, voxelI, voxelJ, voxelK, voxelStepXYZ, rgba); } } voxelQuadRgba.push_back(rgba[0]); voxelQuadRgba.push_back(rgba[1]); voxelQuadRgba.push_back(rgba[2]); voxelQuadRgba.push_back(rgba[3]); } } const int64_t numberOfCoordinates = voxelQuadCoordinates.size() / 3; if (debugFlag) { std::cout << "Num rows/cols: " << numberOfRows << ", " << numberOfColumns << std::endl; std::cout << "Total, 3, 4 " << totalCoordElements << ", " << numQuadStripCoords << ", " << numQuadStripRGBA << std::endl; std::cout << "Size coords: " << voxelQuadCoordinates.size() << std::endl; std::cout << "Size normals: " << voxelQuadNormals.size() << std::endl; std::cout << "Size rgba: " << voxelQuadRgba.size() << std::endl; std::cout << "Valid voxels: " << numberOfVoxelsToDraw << std::endl; for (int64_t i = 0; i < numberOfCoordinates; i++) { std::cout << i << ": "; CaretAssertVectorIndex(voxelQuadCoordinates, i*3 + 2); std::cout << qPrintable(AString::fromNumbers(&voxelQuadCoordinates[i*3], 3, ",")) << " "; CaretAssertVectorIndex(voxelQuadRgba, i*4 + 3); std::cout << qPrintable(AString::fromNumbers(&voxelQuadRgba[i*4], 4, ",")) << std::endl; } } /* * Setup indices into coordinates/normals/coloring to draw the quads */ switch (drawType) { case DRAW_QUADS: { std::vector quadIndices; quadIndices.reserve(numberOfVoxelsToDraw * 4); for (int64_t iCol = 0; iCol < numberOfColumns; iCol++) { const int64_t columnOffset = (iCol * (numberOfRows + 1)); for (int64_t jRow = 0; jRow < numberOfRows; jRow++) { const int64_t coordBottomLeftIndex = columnOffset + jRow; //(iCol * (numberOfRows + 1) + jRow); const int64_t coordTopLeftIndex = coordBottomLeftIndex + 1; const int64_t rgbaIndex = coordTopLeftIndex * 4; CaretAssert(coordBottomLeftIndex < numberOfCoordinates); CaretAssert(coordTopLeftIndex < numberOfCoordinates); CaretAssertVectorIndex(voxelQuadRgba, rgbaIndex + 3); if (voxelQuadRgba[rgbaIndex + 3] > 0) { /* * For quads: (bottom left, bottom right, top right, top left) * Color with flat shading comes from the top left coordinate */ const int32_t coordBottomRightIndex = coordBottomLeftIndex + (numberOfRows + 1); const int32_t coordTopRightIndex = coordBottomRightIndex + 1; CaretAssert(coordBottomRightIndex < numberOfCoordinates); CaretAssert(coordTopRightIndex < numberOfCoordinates); quadIndices.push_back(coordBottomLeftIndex); quadIndices.push_back(coordBottomRightIndex); quadIndices.push_back(coordTopRightIndex); quadIndices.push_back(coordTopLeftIndex); } } if (debugFlag) { std::cout << "Quad Indices: " << quadIndices.size() << std::endl; for (uint32_t i = 0; i < quadIndices.size(); i++) { std::cout << quadIndices[i] << " ("; const int32_t coordOffset = quadIndices[i] * 3; CaretAssertVectorIndex(voxelQuadCoordinates, coordOffset + 2); std::cout << qPrintable(AString::fromNumbers(&voxelQuadCoordinates[coordOffset], 3, ",")) << ") ("; const int32_t rgbaOffset = quadIndices[i] * 4; CaretAssertVectorIndex(voxelQuadRgba, rgbaOffset + 3); std::cout << qPrintable(AString::fromNumbers(&voxelQuadRgba[rgbaOffset], 4, ",")) << " " << std::endl; } std::cout << std::endl; } } if (debugFlag) { std::cout << "Drawing " << quadIndices.size() / 4 << " quads." << std::endl; } BrainOpenGLPrimitiveDrawing::drawQuadIndices(voxelQuadCoordinates, voxelQuadNormals, voxelQuadRgba, quadIndices); } break; case DRAW_QUAD_STRIPS: { int64_t stripCount = 0; const int64_t maxCoordsPerStrip = numberOfRows * 2 + 2; for (int64_t iCol = 0; iCol < numberOfColumns; iCol++) { std::vector quadIndices; quadIndices.reserve(maxCoordsPerStrip); for (int64_t jRow = 0; jRow < numberOfRows; jRow++) { const int32_t coordBottomLeftIndex = (iCol * (numberOfRows + 1) + jRow); const int32_t coordTopLeftIndex = coordBottomLeftIndex + 1; const int32_t coordBottomRightIndex = coordBottomLeftIndex + (numberOfRows + 1); const int32_t coordTopRightIndex = coordBottomRightIndex + 1; const int64_t rgbaIndex = coordTopRightIndex * 4; CaretAssert(coordBottomLeftIndex < numberOfCoordinates); CaretAssert(coordTopLeftIndex < numberOfCoordinates); CaretAssert(coordBottomRightIndex < numberOfCoordinates); CaretAssert(coordTopRightIndex < numberOfCoordinates); CaretAssertVectorIndex(voxelQuadRgba, rgbaIndex + 3); if (voxelQuadRgba[rgbaIndex + 3] > 0) { /* * For quad strips (bottom left, bottom right, top left, top right) */ if (quadIndices.empty()) { quadIndices.push_back(coordBottomLeftIndex); quadIndices.push_back(coordBottomRightIndex); } quadIndices.push_back(coordTopLeftIndex); quadIndices.push_back(coordTopRightIndex); } else { if ( ! quadIndices.empty()) { if (debugFlag) { std::cout << "Quad Indices: " << quadIndices.size() << std::endl; for (uint32_t i = 0; i < quadIndices.size(); i++) { std::cout << quadIndices[i] << " ("; const int32_t coordOffset = quadIndices[i] * 3; CaretAssertVectorIndex(voxelQuadCoordinates, coordOffset + 2); std::cout << qPrintable(AString::fromNumbers(&voxelQuadCoordinates[coordOffset], 3, ",")) << ") ("; const int32_t rgbaOffset = quadIndices[i] * 4; CaretAssertVectorIndex(voxelQuadRgba, rgbaOffset + 3); std::cout << qPrintable(AString::fromNumbers(&voxelQuadRgba[rgbaOffset], 4, ",")) << " " << std::endl; } std::cout << std::endl; } BrainOpenGLPrimitiveDrawing::drawQuadStrips(voxelQuadCoordinates, voxelQuadNormals, voxelQuadRgba, quadIndices); quadIndices.clear(); stripCount++; } } } if ( ! quadIndices.empty()) { if (debugFlag) { std::cout << "Quad Indices: " << quadIndices.size() << std::endl; for (uint32_t i = 0; i < quadIndices.size(); i++) { std::cout << quadIndices[i] << " ("; const int32_t coordOffset = quadIndices[i] * 3; CaretAssertVectorIndex(voxelQuadCoordinates, coordOffset + 2); std::cout << qPrintable(AString::fromNumbers(&voxelQuadCoordinates[coordOffset], 3, ",")) << ") ("; const int32_t rgbaOffset = quadIndices[i] * 4; CaretAssertVectorIndex(voxelQuadRgba, rgbaOffset + 3); std::cout << qPrintable(AString::fromNumbers(&voxelQuadRgba[rgbaOffset], 4, ",")) << " " << std::endl; } std::cout << std::endl; } BrainOpenGLPrimitiveDrawing::drawQuadStrips(voxelQuadCoordinates, voxelQuadNormals, voxelQuadRgba, quadIndices); quadIndices.clear(); stripCount++; } } if (debugFlag) { std::cout << "Strips drawn: " << stripCount << std::endl; } } break; } } /** * Reset for volume identification. * * Clear identification indices and if identification is enabled, * reserve a reasonable amount of space for the indices. */ void BrainOpenGLVolumeSliceDrawing::resetIdentification() { m_identificationIndices.clear(); if (m_identificationModeFlag) { int64_t estimatedNumberOfItems = 0; std::vector volumeDims; m_volumeDrawInfo[0].volumeFile->getDimensions(volumeDims); if (volumeDims.size() >= 3) { const int64_t maxDim = std::max(volumeDims[0], std::max(volumeDims[1], volumeDims[2])); estimatedNumberOfItems = maxDim * maxDim * IDENTIFICATION_INDICES_PER_VOXEL; } m_identificationIndices.reserve(estimatedNumberOfItems); } } /** * Add a voxel to the identification indices. * * @param volumeIndex * Index of the volume. * @param mapIndex * Index of the volume map. * @param voxelI * Voxel Index I * @param voxelJ * Voxel Index J * @param voxelK * Voxel Index K * @param voxelDiffXYZ * Change in XYZ from voxel bottom left to top right * @param rgbaForColorIdentificationOut * Encoded identification in RGBA color OUTPUT */ void BrainOpenGLVolumeSliceDrawing::addVoxelToIdentification(const int32_t volumeIndex, const int32_t mapIndex, const int32_t voxelI, const int32_t voxelJ, const int32_t voxelK, const float voxelDiffXYZ[3], uint8_t rgbaForColorIdentificationOut[4]) { const int32_t idIndex = m_identificationIndices.size() / IDENTIFICATION_INDICES_PER_VOXEL; m_fixedPipelineDrawing->colorIdentification->addItem(rgbaForColorIdentificationOut, SelectionItemDataTypeEnum::VOXEL, idIndex); rgbaForColorIdentificationOut[3] = 255; /* * ID stack requires integers to * use an integer pointer to the float values */ CaretAssert(sizeof(float) == sizeof(int32_t)); const int32_t* intPointerDiffXYZ = (int32_t*)voxelDiffXYZ; /* * If these items change, need to update reset and * processing of identification. */ m_identificationIndices.push_back(volumeIndex); m_identificationIndices.push_back(mapIndex); m_identificationIndices.push_back(voxelI); m_identificationIndices.push_back(voxelJ); m_identificationIndices.push_back(voxelK); m_identificationIndices.push_back(intPointerDiffXYZ[0]); m_identificationIndices.push_back(intPointerDiffXYZ[1]); m_identificationIndices.push_back(intPointerDiffXYZ[2]); } /** * Process voxel identification. */ void BrainOpenGLVolumeSliceDrawing::processIdentification() { int32_t identifiedItemIndex; float depth = -1.0; m_fixedPipelineDrawing->getIndexFromColorSelection(SelectionItemDataTypeEnum::VOXEL, m_fixedPipelineDrawing->mouseX, m_fixedPipelineDrawing->mouseY, identifiedItemIndex, depth); if (identifiedItemIndex >= 0) { const int32_t idIndex = identifiedItemIndex * IDENTIFICATION_INDICES_PER_VOXEL; const int32_t volDrawInfoIndex = m_identificationIndices[idIndex]; CaretAssertVectorIndex(m_volumeDrawInfo, volDrawInfoIndex); VolumeMappableInterface* vf = m_volumeDrawInfo[volDrawInfoIndex].volumeFile; const int64_t voxelIndices[3] = { m_identificationIndices[idIndex + 2], m_identificationIndices[idIndex + 3], m_identificationIndices[idIndex + 4] }; const float* floatDiffXYZ = (float*)&m_identificationIndices[idIndex + 5]; SelectionItemVoxel* voxelID = m_brain->getSelectionManager()->getVoxelIdentification(); if (voxelID->isEnabledForSelection()) { if (voxelID->isOtherScreenDepthCloserToViewer(depth)) { voxelID->setVoxelIdentification(m_brain, vf, voxelIndices, depth); float voxelCoordinates[3]; vf->indexToSpace(voxelIndices[0], voxelIndices[1], voxelIndices[2], voxelCoordinates[0], voxelCoordinates[1], voxelCoordinates[2]); m_fixedPipelineDrawing->setSelectedItemScreenXYZ(voxelID, voxelCoordinates); CaretLogFinest("Selected Voxel (3D): " + AString::fromNumbers(voxelIndices, 3, ",")); } } SelectionItemVoxelEditing* voxelEditID = m_brain->getSelectionManager()->getVoxelEditingIdentification(); if (voxelEditID->isEnabledForSelection()) { if (voxelEditID->getVolumeFileForEditing() == vf) { if (voxelEditID->isOtherScreenDepthCloserToViewer(depth)) { voxelEditID->setVoxelIdentification(m_brain, vf, voxelIndices, depth); voxelEditID->setVoxelDiffXYZ(floatDiffXYZ); float voxelCoordinates[3]; vf->indexToSpace(voxelIndices[0], voxelIndices[1], voxelIndices[2], voxelCoordinates[0], voxelCoordinates[1], voxelCoordinates[2]); m_fixedPipelineDrawing->setSelectedItemScreenXYZ(voxelEditID, voxelCoordinates); CaretLogFinest("Selected Voxel Editing (3D): Indices (" + AString::fromNumbers(voxelIndices, 3, ",") + ") Diff XYZ (" + AString::fromNumbers(floatDiffXYZ, 3, ",") + ")"); } } } } } /** * Get the maximum bounds that enclose the volumes and the minimum * voxel spacing from the volumes. * * @param boundsOut * Bounds of the volumes. * @param spacingOut * Minimum voxel spacing from the volumes. Always positive values (even if * volumes is oriented right to left). * */ bool BrainOpenGLVolumeSliceDrawing::getVoxelCoordinateBoundsAndSpacing(float boundsOut[6], float spacingOut[3]) { const int32_t numberOfVolumesToDraw = static_cast(m_volumeDrawInfo.size()); if (numberOfVolumesToDraw <= 0) { return false; } /* * Find maximum extent of all voxels and smallest voxel * size in each dimension. */ float minVoxelX = std::numeric_limits::max(); float maxVoxelX = -std::numeric_limits::max(); float minVoxelY = std::numeric_limits::max(); float maxVoxelY = -std::numeric_limits::max(); float minVoxelZ = std::numeric_limits::max(); float maxVoxelZ = -std::numeric_limits::max(); float voxelStepX = std::numeric_limits::max(); float voxelStepY = std::numeric_limits::max(); float voxelStepZ = std::numeric_limits::max(); for (int32_t i = 0; i < numberOfVolumesToDraw; i++) { const VolumeMappableInterface* volumeFile = m_volumeDrawInfo[i].volumeFile; int64_t dimI, dimJ, dimK, numMaps, numComponents; volumeFile->getDimensions(dimI, dimJ, dimK, numMaps, numComponents); float originX, originY, originZ; float x1, y1, z1; float lastX, lastY, lastZ; volumeFile->indexToSpace(0, 0, 0, originX, originY, originZ); volumeFile->indexToSpace(1, 1, 1, x1, y1, z1); volumeFile->indexToSpace(dimI - 1, dimJ - 1, dimK - 1, lastX, lastY, lastZ); const float dx = x1 - originX; const float dy = y1 - originY; const float dz = z1 - originZ; voxelStepX = std::min(voxelStepX, std::fabs(dx)); voxelStepY = std::min(voxelStepY, std::fabs(dy)); voxelStepZ = std::min(voxelStepZ, std::fabs(dz)); minVoxelX = std::min(minVoxelX, std::min(originX, lastX)); maxVoxelX = std::max(maxVoxelX, std::max(originX, lastX)); minVoxelY = std::min(minVoxelY, std::min(originY, lastY)); maxVoxelY = std::max(maxVoxelY, std::max(originY, lastY)); minVoxelZ = std::min(minVoxelZ, std::min(originZ, lastZ)); maxVoxelZ = std::max(maxVoxelZ, std::max(originZ, lastZ)); } boundsOut[0] = minVoxelX; boundsOut[1] = maxVoxelX; boundsOut[2] = minVoxelY; boundsOut[3] = maxVoxelY; boundsOut[4] = minVoxelZ; boundsOut[5] = maxVoxelZ; spacingOut[0] = voxelStepX; spacingOut[1] = voxelStepY; spacingOut[2] = voxelStepZ; bool valid = false; if ((maxVoxelX > minVoxelX) && (maxVoxelY > minVoxelY) && (maxVoxelZ > minVoxelZ) && (voxelStepX > 0.0) && (voxelStepY > 0.0) && (voxelStepZ > 0.0)) { valid = true; } return valid; } /** * Create the oblique transformation matrix. * * @param sliceCoordinates * Slice that is being drawn. * @param obliqueTransformationMatrixOut * OUTPUT transformation matrix for oblique viewing. */ void BrainOpenGLVolumeSliceDrawing::createObliqueTransformationMatrix(const float sliceCoordinates[3], Matrix4x4& obliqueTransformationMatrixOut) { /* * Initialize the oblique transformation matrix */ obliqueTransformationMatrixOut.identity(); /* * Get the oblique rotation matrix */ Matrix4x4 obliqueRotationMatrix = m_browserTabContent->getObliqueVolumeRotationMatrix(); /* * Create the transformation matrix */ obliqueTransformationMatrixOut.postmultiply(obliqueRotationMatrix); /* * Translate to selected coordinate */ obliqueTransformationMatrixOut.translate(sliceCoordinates[0], sliceCoordinates[1], sliceCoordinates[2]); } /** * Find portion (or all) of slice that fits inside the graphics window. * * @param sliceViewPlane * The orthogonal plane being viewed. * @param selectedSliceCoordinate * Coordinate of the slice being drawn. * @param volumeFile * Volume file that is to be drawn. * @param culledFirstVoxelIJKOut * First (bottom left) voxel that will be drawn for the volume file. * @param culledLastVoxelIJKOut * Last (top right) voxel that will be drawn for the volume file. * @param voxelDeltaXYZOut * Voxel sizes for the volume file. The element corresponding to the * slice plane being drawn will be zero (axial => [2]=0) */ bool BrainOpenGLVolumeSliceDrawing::getVolumeDrawingViewDependentCulling(const VolumeSliceViewPlaneEnum::Enum sliceViewPlane, const float selectedSliceCoordinate, const VolumeMappableInterface* volumeFile, int64_t culledFirstVoxelIJKOut[3], int64_t culledLastVoxelIJKOut[3], float voxelDeltaXYZOut[3]) { GLint viewport[4]; glGetIntegerv(GL_VIEWPORT, viewport); GLdouble projectionMatrix[16]; glGetDoublev(GL_PROJECTION_MATRIX, projectionMatrix); GLdouble modelMatrix[16]; glGetDoublev(GL_MODELVIEW_MATRIX, modelMatrix); const double vpMinX = viewport[0]; const double vpMaxX = viewport[0] + viewport[2]; const double vpMinY = viewport[1]; const double vpMaxY = viewport[1] + viewport[3]; GLdouble bottomLeftWin[3] = { vpMinX, vpMinY, 0.0 }; GLdouble bottomRightWin[3] = { vpMaxX, vpMinY, 0.0 }; GLdouble topRightWin[3] = { vpMaxX, vpMaxY, 0.0 }; GLdouble topLeftWin[3] = { vpMinX, vpMaxY, 0.0 }; GLdouble bottomLeftCoord[3]; int32_t cornersValidCount = 0; if (gluUnProject(bottomLeftWin[0], bottomLeftWin[1], bottomLeftWin[2], modelMatrix, projectionMatrix, viewport, &bottomLeftCoord[0], &bottomLeftCoord[1], &bottomLeftCoord[2])) { cornersValidCount++; } GLdouble bottomRightCoord[3]; if (gluUnProject(bottomRightWin[0], bottomRightWin[1], bottomRightWin[2], modelMatrix, projectionMatrix, viewport, &bottomRightCoord[0], &bottomRightCoord[1], &bottomRightCoord[2])) { cornersValidCount++; } GLdouble topRightCoord[3]; if (gluUnProject(topRightWin[0], topRightWin[1], topRightWin[2], modelMatrix, projectionMatrix, viewport, &topRightCoord[0], &topRightCoord[1], &topRightCoord[2])) { cornersValidCount++; } GLdouble topLeftCoord[3]; if (gluUnProject(topLeftWin[0], topLeftWin[1], topLeftWin[2], modelMatrix, projectionMatrix, viewport, &topLeftCoord[0], &topLeftCoord[1], &topLeftCoord[2])) { cornersValidCount++; } if (cornersValidCount != 4) { return false; } /* * Limit the corner coordinates to the volume's bounding box */ BoundingBox boundingBox; volumeFile->getVoxelSpaceBoundingBox(boundingBox); { float spaceX, spaceY, spaceZ; volumeFile->getVoxelSpacing(spaceX, spaceY, spaceZ); const float halfX = std::fabs(spaceX / 2.0); const float halfY = std::fabs(spaceY / 2.0); const float halfZ = std::fabs(spaceZ / 2.0); switch (sliceViewPlane) { case VolumeSliceViewPlaneEnum::ALL: CaretAssert(0); break; case VolumeSliceViewPlaneEnum::AXIAL: { const float maxZ = boundingBox.getMaxZ() + halfZ; const float minZ = boundingBox.getMinZ() - halfZ; if ((selectedSliceCoordinate < minZ) || (selectedSliceCoordinate > maxZ)) { return false; } } break; case VolumeSliceViewPlaneEnum::CORONAL: { const float maxY = boundingBox.getMaxY() + halfY; const float minY = boundingBox.getMinY() - halfY; if ((selectedSliceCoordinate < minY) || (selectedSliceCoordinate > maxY)) { return false; } } break; case VolumeSliceViewPlaneEnum::PARASAGITTAL: { const float maxX = boundingBox.getMaxX() + halfX; const float minX = boundingBox.getMinX() - halfX; if ((selectedSliceCoordinate < minX) || (selectedSliceCoordinate > maxX)) { return false; } } break; } } /* * Limit the corner coordinates to the volume's bounding box */ boundingBox.limitCoordinateToBoundingBox(bottomLeftCoord); boundingBox.limitCoordinateToBoundingBox(bottomRightCoord); boundingBox.limitCoordinateToBoundingBox(topRightCoord); boundingBox.limitCoordinateToBoundingBox(topLeftCoord); /* * The unproject functions will return the "in plane" coordinate (Z in axial view) * that is not correct for the slice being viewed. So, override the * Z-coordinate with the coordinate of the current slice plane. * * This must be done AFTER limiting coordinates to the volume's bounding box. * Otherwise, the culled voxels will always be the first or last slice when * the slice coordinate is NOT within the volume file. */ switch (sliceViewPlane) { case VolumeSliceViewPlaneEnum::ALL: CaretAssert(0); break; case VolumeSliceViewPlaneEnum::AXIAL: bottomLeftCoord[2] = selectedSliceCoordinate; bottomRightCoord[2] = selectedSliceCoordinate; topRightCoord[2] = selectedSliceCoordinate; topLeftCoord[2] = selectedSliceCoordinate; break; case VolumeSliceViewPlaneEnum::CORONAL: bottomLeftCoord[1] = selectedSliceCoordinate; bottomRightCoord[1] = selectedSliceCoordinate; topRightCoord[1] = selectedSliceCoordinate; topLeftCoord[1] = selectedSliceCoordinate; break; case VolumeSliceViewPlaneEnum::PARASAGITTAL: bottomLeftCoord[0] = selectedSliceCoordinate; bottomRightCoord[0] = selectedSliceCoordinate; topRightCoord[0] = selectedSliceCoordinate; topLeftCoord[0] = selectedSliceCoordinate; break; } /* * Note: Spacing may be negative for some orientations * and positive may be on left or bottom */ float voxelDeltaX, voxelDeltaY, voxelDeltaZ; volumeFile->getVoxelSpacing(voxelDeltaX, voxelDeltaY, voxelDeltaZ); voxelDeltaX = std::fabs(voxelDeltaX); voxelDeltaY = std::fabs(voxelDeltaY); voxelDeltaZ = std::fabs(voxelDeltaZ); if (bottomLeftCoord[0] > topRightCoord[0]) { voxelDeltaX = -voxelDeltaX; } if (bottomLeftCoord[1] > topRightCoord[1]) { voxelDeltaY = -voxelDeltaY; } if (bottomLeftCoord[2] > topRightCoord[2]) { voxelDeltaZ = -voxelDeltaZ; } bool adjustX = false; bool adjustY = false; bool adjustZ = false; switch (sliceViewPlane) { case VolumeSliceViewPlaneEnum::ALL: CaretAssert(0); break; case VolumeSliceViewPlaneEnum::AXIAL: adjustX = true; adjustY = true; voxelDeltaZ = 0.0; break; case VolumeSliceViewPlaneEnum::CORONAL: adjustX = true; adjustZ = true; voxelDeltaY = 0.0; break; case VolumeSliceViewPlaneEnum::PARASAGITTAL: adjustY = true; adjustZ = true; voxelDeltaX = 0.0; break; } /* * Adjust by one voxel to ensure full coverage */ if (adjustX) { bottomLeftCoord[0] -= voxelDeltaX; topRightCoord[0] += voxelDeltaX; } if (adjustY) { bottomLeftCoord[1] -= voxelDeltaY; topRightCoord[1] += voxelDeltaY; } if (adjustZ) { bottomLeftCoord[2] -= voxelDeltaZ; topRightCoord[2] += voxelDeltaZ; } int64_t bottomLeftIJK[3]; volumeFile->enclosingVoxel(bottomLeftCoord[0], bottomLeftCoord[1], bottomLeftCoord[2], bottomLeftIJK[0], bottomLeftIJK[1], bottomLeftIJK[2]); int64_t topRightIJK[3]; volumeFile->enclosingVoxel(topRightCoord[0], topRightCoord[1], topRightCoord[2], topRightIJK[0], topRightIJK[1], topRightIJK[2]); volumeFile->limitIndicesToValidIndices(bottomLeftIJK[0], bottomLeftIJK[1], bottomLeftIJK[2]); volumeFile->limitIndicesToValidIndices(topRightIJK[0], topRightIJK[1], topRightIJK[2]); culledFirstVoxelIJKOut[0] = bottomLeftIJK[0]; culledFirstVoxelIJKOut[1] = bottomLeftIJK[1]; culledFirstVoxelIJKOut[2] = bottomLeftIJK[2]; culledLastVoxelIJKOut[0] = topRightIJK[0]; culledLastVoxelIJKOut[1] = topRightIJK[1]; culledLastVoxelIJKOut[2] = topRightIJK[2]; voxelDeltaXYZOut[0] = voxelDeltaX; voxelDeltaXYZOut[1] = voxelDeltaY; voxelDeltaXYZOut[2] = voxelDeltaZ; return true; } /* ======================================================================= */ /** * Constructor * * @param volumeMappableInterface * Volume that contains the data values. * @param mapIndex * Index of selected map. */ BrainOpenGLVolumeSliceDrawing::VolumeSlice::VolumeSlice(VolumeMappableInterface* volumeMappableInterface, const int32_t mapIndex) { m_volumeMappableInterface = volumeMappableInterface; m_volumeFile = dynamic_cast(m_volumeMappableInterface); m_ciftiMappableDataFile = dynamic_cast(m_volumeMappableInterface); CaretAssert(m_volumeMappableInterface); m_mapIndex = mapIndex; CaretAssert(m_mapIndex >= 0); const int64_t sliceDim = 300; const int64_t numVoxels = sliceDim * sliceDim; m_values.reserve(numVoxels); } /** * Add a value and return its index. * * @param value * Value that is added. * @return * The index for the value. */ int64_t BrainOpenGLVolumeSliceDrawing::VolumeSlice::addValue(const float value) { const int64_t indx = static_cast(m_values.size()); m_values.push_back(value); return indx; } /** * Return RGBA colors for value using the value's index * returned by addValue(). * * @param indx * Index of the value. * @return * RGBA coloring for value. */ uint8_t* BrainOpenGLVolumeSliceDrawing::VolumeSlice::getRgbaForValueByIndex(const int64_t indx) { CaretAssertVectorIndex(m_rgba, indx * 4); return &m_rgba[indx*4]; } /** * Allocate colors for the voxel values */ void BrainOpenGLVolumeSliceDrawing::VolumeSlice::allocateColors() { m_rgba.resize(m_values.size() * 4); } /* ======================================================================= */ /** * Create a voxel for drawing. * * @param center * Center of voxel. * @param leftBottom * Left bottom coordinate of voxel. * @param rightBottom * Right bottom coordinate of voxel. * @param rightTop * Right top coordinate of voxel. * @param leftTop * Left top coordinate of voxel. */ BrainOpenGLVolumeSliceDrawing::VoxelToDraw::VoxelToDraw(const float center[3], const double leftBottom[3], const double rightBottom[3], const double rightTop[3], const double leftTop[3]) { m_center[0] = center[0]; m_center[1] = center[1]; m_center[2] = center[2]; m_coordinates[0] = leftBottom[0]; m_coordinates[1] = leftBottom[1]; m_coordinates[2] = leftBottom[2]; m_coordinates[3] = rightBottom[0]; m_coordinates[4] = rightBottom[1]; m_coordinates[5] = rightBottom[2]; m_coordinates[6] = rightTop[0]; m_coordinates[7] = rightTop[1]; m_coordinates[8] = rightTop[2]; m_coordinates[9] = leftTop[0]; m_coordinates[10] = leftTop[1]; m_coordinates[11] = leftTop[2]; const int64_t numSlices = 5; m_sliceIndices.reserve(numSlices); m_sliceOffsets.reserve(numSlices); } /** * Get the change in XYZ for the voxel ([top right] minus [bottom left]) * * @param dxyzOut * Change in XYZ from bottom left to top right */ void BrainOpenGLVolumeSliceDrawing::VoxelToDraw::getDiffXYZ(float dxyzOut[3]) const { dxyzOut[0] = m_coordinates[6] - m_coordinates[0]; dxyzOut[1] = m_coordinates[7] - m_coordinates[1]; dxyzOut[2] = m_coordinates[8] - m_coordinates[2]; } /** * Add a value from a volume slice. * * @param sliceIndex * Index of the slice. * @param sliceOffset * Offset of value in the slice. */ void BrainOpenGLVolumeSliceDrawing::VoxelToDraw::addVolumeValue(const int64_t sliceIndex, const int64_t sliceOffset) { CaretAssert(sliceIndex >= 0); CaretAssert(sliceOffset >= 0); m_sliceIndices.push_back(sliceIndex); m_sliceOffsets.push_back(sliceOffset); } connectome-workbench-1.2.3+git41-gc4c6c90/src/Brain/BrainOpenGLVolumeSliceDrawing.h000066400000000000000000000377251300200146000275520ustar00rootroot00000000000000#ifndef __BRAIN_OPEN_GL_VOLUME_SLICE_DRAWING_H__ #define __BRAIN_OPEN_GL_VOLUME_SLICE_DRAWING_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 "BrainOpenGLFixedPipeline.h" #include "CaretObject.h" #include "DisplayGroupEnum.h" #include "VolumeSliceProjectionTypeEnum.h" #include "VolumeSliceDrawingTypeEnum.h" #include "VolumeSliceViewPlaneEnum.h" namespace caret { class Brain; class BrowserTabContent; class CiftiMappableDataFile; class Matrix4x4; class ModelVolume; class ModelWholeBrain; class PaletteFile; class Plane; class VolumeMappableInterface; class BrainOpenGLVolumeSliceDrawing : public CaretObject { public: BrainOpenGLVolumeSliceDrawing(); virtual ~BrainOpenGLVolumeSliceDrawing(); void draw(BrainOpenGLFixedPipeline* fixedPipelineDrawing, BrowserTabContent* browserTabContent, std::vector& volumeDrawInfo, const VolumeSliceDrawingTypeEnum::Enum sliceDrawingType, const VolumeSliceProjectionTypeEnum::Enum sliceProjectionType, const int32_t viewport[4]); // ADD_NEW_METHODS_HERE private: /** * Holds values in the slice for a volume so that they * can be colored all at once which is more efficient than * colors singles voxels many times */ class VolumeSlice{ public: /** * Constructor * * @param volumeMappableInterface * Volume that contains the data values. */ VolumeSlice(VolumeMappableInterface* volumeMappableInterface, const int32_t mapIndex); /** * Add a value and return its index. * * @param value * Value that is added. * @return * The index for the value. */ int64_t addValue(const float value); /** * Return RGBA colors for value using the value's index * returned by addValue(). * * @param indx * Index of the value. * @return * RGBA coloring for value. */ uint8_t* getRgbaForValueByIndex(const int64_t indx); /** * Allocate colors for the voxel values */ void allocateColors(); /** * Volume containing the values */ VolumeMappableInterface* m_volumeMappableInterface; /** * If not NULL, it is a VolumeFile */ VolumeFile* m_volumeFile; /** * If not NULL, it is a Cifti Mappable Data File */ CiftiMappableDataFile* m_ciftiMappableDataFile; /** * Map index */ int32_t m_mapIndex; /** * The voxel values */ std::vector m_values; /** * Coloring corresponding to the values (4 components per voxel) */ std::vector m_rgba; }; /** * For each voxel, contains offsets to each layer */ class VoxelToDraw { public: VoxelToDraw(const float center[3], const double leftBottom[3], const double rightBottom[3], const double rightTop[3], const double leftTop[3]); void getDiffXYZ(float dxyzOut[3]) const; void addVolumeValue(const int64_t sliceIndex, const int64_t sliceOffset); /** * Center of voxel. */ float m_center[3]; /** * Corners of voxel */ float m_coordinates[12]; /* * Index of volume in VoxelsInSliceForVolume */ std::vector m_sliceIndices; /** * Offset in values in VoxelsInSliceForVolume */ std::vector m_sliceOffsets; }; BrainOpenGLVolumeSliceDrawing(const BrainOpenGLVolumeSliceDrawing&); BrainOpenGLVolumeSliceDrawing& operator=(const BrainOpenGLVolumeSliceDrawing&); void drawVolumeSlicesForAllStructuresView(const VolumeSliceProjectionTypeEnum::Enum sliceProjectionType, const int32_t viewport[4]); void drawVolumeSliceViewPlane(const VolumeSliceDrawingTypeEnum::Enum sliceDrawingType, const VolumeSliceProjectionTypeEnum::Enum sliceProjectionType, const VolumeSliceViewPlaneEnum::Enum sliceViewPlane, const int32_t viewport[4]); void drawVolumeSliceViewType(const VolumeSliceDrawingTypeEnum::Enum sliceDrawingType, const VolumeSliceProjectionTypeEnum::Enum sliceProjectionType, const VolumeSliceViewPlaneEnum::Enum sliceViewPlane, const int32_t viewport[4]); void drawVolumeSliceViewTypeMontage(const VolumeSliceDrawingTypeEnum::Enum sliceDrawingType, const VolumeSliceProjectionTypeEnum::Enum sliceProjectionType, const VolumeSliceViewPlaneEnum::Enum sliceViewPlane, const int32_t viewport[4]); void drawVolumeSliceViewProjection(const VolumeSliceDrawingTypeEnum::Enum sliceDrawingType, const VolumeSliceProjectionTypeEnum::Enum sliceProjectionType, const VolumeSliceViewPlaneEnum::Enum sliceViewPlane, const float sliceCoordinates[3], const int32_t viewport[4]); void drawObliqueSlice(const VolumeSliceViewPlaneEnum::Enum sliceViewPlane, Matrix4x4& transformationMatrix, const Plane& plane); void drawOrthogonalSlice_LPI_ONLY(const VolumeSliceViewPlaneEnum::Enum sliceViewPlane, const float sliceCoordinates[3], const Plane& plane); void drawOrthogonalSlice(const VolumeSliceViewPlaneEnum::Enum sliceViewPlane, const float sliceCoordinates[3], const Plane& plane); void drawOrthogonalSliceWithCulling(const VolumeSliceViewPlaneEnum::Enum sliceViewPlane, const float sliceCoordinates[3], const Plane& plane); void createSlicePlaneEquation(const VolumeSliceProjectionTypeEnum::Enum sliceProjectionType, const VolumeSliceViewPlaneEnum::Enum sliceViewPlane, const float sliceCoordinates[3], Plane& planeOut); void drawAxesCrosshairsOrthoAndOblique(const VolumeSliceProjectionTypeEnum::Enum sliceProjectionType, const VolumeSliceViewPlaneEnum::Enum sliceViewPlane, const float sliceCoordinates[3], const bool drawCrosshairsFlag, const bool drawCrosshairLabelsFlag); void setVolumeSliceViewingAndModelingTransformations(const VolumeSliceProjectionTypeEnum::Enum sliceProjectionType, const VolumeSliceViewPlaneEnum::Enum sliceViewPlane, const Plane& plane); void getAxesColor(const VolumeSliceViewPlaneEnum::Enum sliceViewPlane, float rgbaOut[4]) const; void drawLayers(const VolumeSliceDrawingTypeEnum::Enum sliceDrawingType, const VolumeSliceProjectionTypeEnum::Enum sliceProjectionType, const VolumeSliceViewPlaneEnum::Enum sliceViewPlane, const Plane& slicePlane, const float sliceCoordinates[3]); void drawSurfaceOutline(const Plane& plane); void drawVolumeSliceFoci(const Plane& plane); void drawAxesCrosshairs(const VolumeSliceProjectionTypeEnum::Enum sliceProjectionType, const VolumeSliceDrawingTypeEnum::Enum sliceDrawingType, const VolumeSliceViewPlaneEnum::Enum sliceViewPlane, const float sliceCoordinates[3]); bool getMinMaxVoxelSpacing(const VolumeMappableInterface* volume, float& minSpacingOut, float& maxSpacingOut) const; void drawSquare(const float size); void drawOrientationAxes(const int viewport[4]); void setOrthographicProjection(const VolumeSliceViewPlaneEnum::Enum sliceViewPlane, const int viewport[4]); void drawOrthogonalSliceVoxels(const float sliceNormalVector[3], const float coordinate[3], const float rowStep[3], const float columnStep[3], const int64_t numberOfColumns, const int64_t numberOfRows, const std::vector& sliceRGBA, const int64_t validVoxelCount, const VolumeMappableInterface* volumeInterface, const int32_t volumeIndex, const int32_t mapIndex, const uint8_t sliceOpacity); void drawOrthogonalSliceVoxelsSingleQuads(const float sliceNormalVector[3], const float coordinate[3], const float rowStep[3], const float columnStep[3], const int64_t numberOfColumns, const int64_t numberOfRows, const std::vector& sliceRGBA, const VolumeMappableInterface* volumeInterface, const int32_t volumeIndex, const int32_t mapIndex, const uint8_t sliceOpacity); void drawOrthogonalSliceVoxelsQuadIndicesAndStrips(const float sliceNormalVector[3], const float coordinate[3], const float rowStep[3], const float columnStep[3], const int64_t numberOfColumns, const int64_t numberOfRows, const std::vector& sliceRGBA, const VolumeMappableInterface* volumeInterface, const int32_t volumeIndex, const int32_t mapIndex, const uint8_t sliceOpacity); bool getVoxelCoordinateBoundsAndSpacing(float boundsOut[6], float spacingOut[3]); void createObliqueTransformationMatrix(const float sliceCoordinates[3], Matrix4x4& obliqueTransformationMatrixOut); void drawIdentificationSymbols(const Plane& plane); void addVoxelToIdentification(const int32_t volumeIndex, const int32_t mapIndex, const int32_t voxelI, const int32_t voxelJ, const int32_t voxelK, const float voxelDiffXYZ[3], uint8_t rgbaForColorIdentificationOut[4]); bool getVolumeDrawingViewDependentCulling(const VolumeSliceViewPlaneEnum::Enum sliceViewPlane, const float selectedSliceCoordinate, const VolumeMappableInterface* volumeFile, int64_t culledFirstVoxelIJKOut[3], int64_t culledLastVoxelIJKOut[3], float voxelDeltaXYZOut[3]); void showBrainordinateHighlightRegionOfInterest(const VolumeSliceViewPlaneEnum::Enum sliceViewPlane, const float sliceCoordinates[3], const float sliceNormalVector[3]); void processIdentification(); void resetIdentification(); ModelVolume* m_modelVolume; ModelWholeBrain* m_modelWholeBrain; VolumeMappableInterface* m_underlayVolume; Brain* m_brain; std::vector > m_ciftiMappableFileData; BrainOpenGLFixedPipeline* m_fixedPipelineDrawing; std::vector m_volumeDrawInfo; BrowserTabContent* m_browserTabContent; PaletteFile* m_paletteFile; DisplayGroupEnum::Enum m_displayGroup; int32_t m_tabIndex; double m_lookAtCenter[3]; double m_viewingMatrix[16]; double m_orthographicBounds[6]; std::vector m_identificationIndices; bool m_identificationModeFlag; static const int32_t IDENTIFICATION_INDICES_PER_VOXEL; // ADD_NEW_MEMBERS_HERE }; #ifdef __BRAIN_OPEN_GL_VOLUME_SLICE_DRAWING_DECLARE__ const int32_t BrainOpenGLVolumeSliceDrawing::IDENTIFICATION_INDICES_PER_VOXEL = 8; #endif // __BRAIN_OPEN_GL_VOLUME_SLICE_DRAWING_DECLARE__ } // namespace #endif //__BRAIN_OPEN_GL_VOLUME_SLICE_DRAWING_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Brain/BrainStructure.cxx000066400000000000000000001321041300200146000253000ustar00rootroot00000000000000/*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 #define __BRAIN_STRUCTURE_DEFINE__ #include "BrainStructure.h" #undef __BRAIN_STRUCTURE_DEFINE__ #include "Brain.h" #include "BrainStructureNodeAttributes.h" #include "CaretAssert.h" #include "CaretLogger.h" #include "CaretPointLocator.h" #include "CaretPreferences.h" #include "DataFileException.h" #include "ElapsedTimer.h" #include "EventBrainStructureGetAll.h" #include "EventManager.h" #include "EventNodeDataFilesGet.h" #include "EventModelAdd.h" #include "EventModelDelete.h" #include "EventSurfacesGet.h" #include "EventSurfaceStructuresValidGet.h" #include "GroupAndNameHierarchyModel.h" #include "IdentificationManager.h" #include "SelectionManager.h" #include "LabelFile.h" #include "MathFunctions.h" #include "MetricFile.h" #include "ModelSurface.h" #include "OverlaySet.h" #include "OverlaySetArray.h" #include "RgbaFile.h" #include "SceneClass.h" #include "ScenePathName.h" #include "SessionManager.h" #include "Surface.h" using namespace caret; /** * Constructor. * */ BrainStructure::BrainStructure(Brain* brain, StructureEnum::Enum structure) { m_brainStructureIdentifier = BrainStructure::s_brainStructureIdentifierCounter++; m_brain = brain; m_structure = structure; m_nodeAttributes = new BrainStructureNodeAttributes(); m_primaryAnatomicalSurface = NULL; std::vector overlaySurfaceStructures; overlaySurfaceStructures.push_back(m_structure); m_overlaySetArray = new OverlaySetArray(overlaySurfaceStructures, Overlay::INCLUDE_VOLUME_FILES_NO, "Structure " + StructureEnum::toGuiName(m_structure)); EventManager::get()->addEventListener(this, EventTypeEnum::EVENT_BRAIN_STRUCTURE_GET_ALL); EventManager::get()->addEventListener(this, EventTypeEnum::EVENT_GET_NODE_DATA_FILES); EventManager::get()->addEventListener(this, EventTypeEnum::EVENT_IDENTIFICATION_HIGHLIGHT_LOCATION); EventManager::get()->addEventListener(this, EventTypeEnum::EVENT_IDENTIFICATION_SYMBOL_REMOVAL); EventManager::get()->addEventListener(this, EventTypeEnum::EVENT_SURFACES_GET); EventManager::get()->addEventListener(this, EventTypeEnum::EVENT_SURFACE_STRUCTURES_VALID_GET); } /** * Destructor. */ BrainStructure::~BrainStructure() { EventManager::get()->removeAllEventsFromListener(this); delete m_overlaySetArray; /* * Make a copy of all surface pointers since * deleting surfaces will alter the actual * vector that stores the surfaces. */ std::vector allSurfaces(m_surfaces); for (uint64_t i = 0; i < allSurfaces.size(); i++) { removeAndMaybeDeleteSurface(allSurfaces[i], true); } m_surfaces.clear(); for (uint64_t i = 0; i < m_labelFiles.size(); i++) { delete m_labelFiles[i]; m_labelFiles[i] = NULL; } m_labelFiles.clear(); for (uint64_t i = 0; i < m_metricFiles.size(); i++) { delete m_metricFiles[i]; m_metricFiles[i] = NULL; } m_metricFiles.clear(); for (uint64_t i = 0; i < m_rgbaFiles.size(); i++) { delete m_rgbaFiles[i]; m_rgbaFiles[i] = NULL; } m_rgbaFiles.clear(); delete m_nodeAttributes; } /** * Get the structure for this BrainStructure. * * @return The structure. */ StructureEnum::Enum BrainStructure::getStructure() const { return m_structure; } /** * Add a label file. * * @param labelFile * Label file that is added. * @param addFileToBrainStructure * If true, add the file to the brain structure. This value is false * when a file is reloaded and already part of the brain structure. * @throw DataFileException * If the number of nodes in the label file does not match * the number of nodes in this brain structure. */ void BrainStructure::addLabelFile(LabelFile* labelFile, const bool addFileToBrainStructure) { CaretAssert(labelFile); int32_t numNodes = getNumberOfNodes(); if (numNodes > 0) { if (labelFile->getNumberOfNodes() != numNodes) { AString message = ("Label file contains " + AString::number(labelFile->getNumberOfNodes()) + " vertices but the " + StructureEnum::toGuiName(getStructure()) + " contains " + AString::number(numNodes) + " vertices."); DataFileException e(labelFile->getFileName(), message); CaretLogThrowing(e); throw e; } } if (labelFile->getStructure() != getStructure()) { AString message = ("Trying to add label file with structure \"" + StructureEnum::toGuiName(labelFile->getStructure()) + " to BrainStructure for \"" + StructureEnum::toGuiName(getStructure()) + "\n"); DataFileException e(labelFile->getFileName(), message); CaretLogThrowing(e); throw e; } if (addFileToBrainStructure) { m_labelFiles.push_back(labelFile); } } /** * Add a metric file. * * @param metricFile * Metric file that is added. * @param addFileToBrainStructure * If true, add the file to the brain structure. This value is false * when a file is reloaded and already part of the brain structure. * @throw DataFileException * If the number of nodes in the metric file does not match * the number of nodes in this brain structure. */ void BrainStructure::addMetricFile(MetricFile* metricFile, const bool addFileToBrainStructure) { CaretAssert(metricFile); int32_t numNodes = getNumberOfNodes(); if (numNodes > 0) { if (metricFile->getNumberOfNodes() != numNodes) { AString message = ("Metric file contains " + AString::number(metricFile->getNumberOfNodes()) + " vertices but the " + StructureEnum::toGuiName(getStructure()) + " contains " + AString::number(numNodes) + " vertices."); DataFileException e(metricFile->getFileName(), message); CaretLogThrowing(e); throw e; } } if (metricFile->getStructure() != getStructure()) { AString message = ("Trying to add metric file with structure \"" + StructureEnum::toGuiName(metricFile->getStructure()) + " to BrainStructure for \"" + StructureEnum::toGuiName(getStructure()) + "\n"); DataFileException e(metricFile->getFileName(), message); CaretLogThrowing(e); throw e; } if (addFileToBrainStructure) { m_metricFiles.push_back(metricFile); } } /** * Add an RGBA file. * * @param rgbaFile * RGBA file that is added. * @param addFileToBrainStructure * If true, add the file to the brain structure. This value is false * when a file is reloaded and already part of the brain structure. * @throw DataFileException * If the number of nodes in the RGBA file does not match * the number of nodes in this brain structure. */ void BrainStructure::addRgbaFile(RgbaFile* rgbaFile, const bool addFileToBrainStructure) { CaretAssert(rgbaFile); int32_t numNodes = getNumberOfNodes(); if (numNodes > 0) { if (rgbaFile->getNumberOfNodes() != numNodes) { AString message = ("RGBA File contains " + AString::number(rgbaFile->getNumberOfNodes()) + " vertices but the " + StructureEnum::toGuiName(getStructure()) + " contains " + AString::number(numNodes) + " vertices."); DataFileException e(rgbaFile->getFileName(), message); CaretLogThrowing(e); throw e; } } if (rgbaFile->getStructure() != getStructure()) { AString message = ("Trying to add metric file with structure \"" + StructureEnum::toGuiName(rgbaFile->getStructure()) + " to BrainStructure for \"" + StructureEnum::toGuiName(getStructure()) + "\n"); DataFileException e(rgbaFile->getFileName(), message); CaretLogThrowing(e); throw e; } if (addFileToBrainStructure) { m_rgbaFiles.push_back(rgbaFile); } } /** * Add a surface. * * @param surface * Surface that is added. * @param addFileToBrainStructure * If true, add the file to the brain structure. This value is false * when a file is reloaded and already part of the brain structure. * @throw DataFileException * If the number of nodes in the surface does not match * the number of nodes in this brain structure. */ void BrainStructure::addSurface(Surface* surface, const bool addFileToBrainStructure, const bool initilizeOverlaysFlag) { CaretAssert(surface); int32_t numNodes = getNumberOfNodes(); if (numNodes > 0) { if (surface->getNumberOfNodes() != numNodes) { AString message = ("Surface file contains " + AString::number(surface->getNumberOfNodes()) + " vertices but the " + StructureEnum::toGuiName(getStructure()) + " contains " + AString::number(numNodes) + " vertices."); DataFileException e(surface->getFileName(), message); CaretLogThrowing(e); throw e; } } if (surface->getStructure() != getStructure()) { AString message = ("Trying to add metric file with structure \"" + StructureEnum::toGuiName(surface->getStructure()) + " to BrainStructure for \"" + StructureEnum::toGuiName(getStructure()) + "\n"); DataFileException e(surface->getFileName(), message); CaretLogThrowing(e); throw e; } if (numNodes == 0) { const int32_t numSurfaceNodes = surface->getNumberOfNodes(); m_nodeAttributes->update(numSurfaceNodes); } surface->setBrainStructure(this); if (addFileToBrainStructure) { m_surfaces.push_back(surface); /* * Create a model for the surface. */ ModelSurface* mdcs = new ModelSurface(m_brain, surface); m_surfaceModelMap.insert(std::make_pair(surface, mdcs)); if (initilizeOverlaysFlag) { initializeOverlays(); } /* * Send the model added event. */ EventModelAdd addEvent(mdcs); EventManager::get()->sendEvent(addEvent.getPointer()); } } /** * Remove a surface from this brain structure and the surface * model and maybe delete the surface. * * @param surface * Surface that is removed. * @param deleteSurfaceFile * If true, delete the surface file. If false, surface is removed * but not delete and caller is responsible for deleting the surface. * @return * True if the surface was removed, else false. */ bool BrainStructure::removeAndMaybeDeleteSurface(Surface* surface, const bool deleteSurfaceFile) { CaretAssert(surface); std::vector::iterator iter = std::find(m_surfaces.begin(), m_surfaces.end(), surface); CaretAssertMessage((iter != m_surfaces.end()), "Trying to delete surface not in brain structure."); if (iter == m_surfaces.end()) { CaretLogSevere("Trying to delete surface not in brain structure."); return false; } std::map::iterator modelIter = m_surfaceModelMap.find(surface); CaretAssertMessage((modelIter != m_surfaceModelMap.end()), "Surface does not map to a model"); if (modelIter == m_surfaceModelMap.end()) { CaretLogSevere("Surface does not map to a model"); return false; } ModelSurface* mdcs = modelIter->second; /* * Remove from surface to model map. */ m_surfaceModelMap.erase(modelIter); /* * Remove the surface. */ m_surfaces.erase(iter); /* * Send the model deleted event. */ EventModelDelete deleteEvent(mdcs); EventManager::get()->sendEvent(deleteEvent.getPointer()); /* * Delete the model and the surface. */ delete mdcs; if (deleteSurfaceFile) { delete surface; } return true; } /** * Get the number of surfaces. * * @return * Number of surfaces. */ int BrainStructure::getNumberOfSurfaces() const { return static_cast(m_surfaces.size()); } /** * Get a surface at the specified index. * * @param indx * Index of surface. * @return * Surface at the specified index. */ Surface* BrainStructure::getSurface(int indx) { CaretAssertVectorIndex(m_surfaces, indx); return m_surfaces[indx]; } /** * Get a surface at the specified index. * * @param indx * Index of surface. * @return * Surface at the specified index. */ const Surface* BrainStructure::getSurface(int indx) const { CaretAssertVectorIndex(m_surfaces, indx); return m_surfaces[indx]; } /** * Get all surfaces of the given type in this brain structure. * @param surfaceType * Type of surface * @param surfacesOut * Output that will contain the surfaces. */ void BrainStructure::getSurfacesOfType(const SurfaceTypeEnum::Enum surfaceType, std::vector& surfacesOut) const { surfacesOut.clear(); const int32_t numSurfaces = getNumberOfSurfaces(); for (int32_t i = 0; i < numSurfaces; i++) { if (m_surfaces[i]->getSurfaceType() == surfaceType) { surfacesOut.push_back(m_surfaces[i]); } } } /** * Get all surfaces. * * @param surfaceOut * Output containing all surfaces. */ void BrainStructure::getSurfaces(std::vector& surfacesOut) const { surfacesOut = m_surfaces; } /** * @return The surface used for primary anatomical. * Returns NULL if no anatomical surfaces. */ const Surface* BrainStructure::getPrimaryAnatomicalSurfacePrivate() const { bool valid = false; if (m_primaryAnatomicalSurface != NULL) { const int32_t numSurfaces = getNumberOfSurfaces(); for (int32_t i = 0; i < numSurfaces; i++) { if (m_surfaces[i] == m_primaryAnatomicalSurface) { valid = true; break; } } } if (valid) { return m_primaryAnatomicalSurface; } m_primaryAnatomicalSurface = NULL; /* * Give preference to anatomical surfaces but if there are none * (perhaps the surface types are missing), use all surfaces. */ std::vector primaryAnatomicalSurfaces; getSurfacesOfType(SurfaceTypeEnum::ANATOMICAL, primaryAnatomicalSurfaces); if (primaryAnatomicalSurfaces.empty()) { primaryAnatomicalSurfaces = m_surfaces; } if (primaryAnatomicalSurfaces.empty() == false) { /* * Default to first surface */ m_primaryAnatomicalSurface = primaryAnatomicalSurfaces[0]; /* * Now look for a surface with certain strings in their name */ Surface* midThicknessSurface = NULL; Surface* whiteMatterSurface = NULL; Surface* pialSurface = NULL; Surface* anatomicalSurface = NULL; Surface* fiducialSurface = NULL; const int32_t numSurfaces = static_cast(primaryAnatomicalSurfaces.size()); for (int32_t i = 0; i < numSurfaces; i++) { /* * First, look for anatomical surfaces that are midthickness, * gray/white, and pial. */ if (primaryAnatomicalSurfaces[i]->getSurfaceType() == SurfaceTypeEnum::ANATOMICAL) { const SecondarySurfaceTypeEnum::Enum secondType = primaryAnatomicalSurfaces[i]->getSecondaryType(); const AString name = primaryAnatomicalSurfaces[i]->getFileNameNoPath().toLower(); if (secondType == SecondarySurfaceTypeEnum::MIDTHICKNESS) { if (midThicknessSurface == NULL) { midThicknessSurface = primaryAnatomicalSurfaces[i]; } } if (secondType == SecondarySurfaceTypeEnum::GRAY_WHITE) { if (whiteMatterSurface == NULL) { whiteMatterSurface = primaryAnatomicalSurfaces[i]; } } if (secondType == SecondarySurfaceTypeEnum::PIAL) { if (pialSurface == NULL) { pialSurface = primaryAnatomicalSurfaces[i]; } } } } /* * Since it is possible surfaces may not have valid types, * perform an additional search using name substrings. */ for (int32_t i = 0; i < numSurfaces; i++) { const AString name = primaryAnatomicalSurfaces[i]->getFileNameNoPath().toLower(); if (name.indexOf("midthick") >= 0) { if (midThicknessSurface == NULL) { midThicknessSurface = primaryAnatomicalSurfaces[i]; } } if (name.indexOf("white") >= 0) { if (whiteMatterSurface == NULL) { whiteMatterSurface = primaryAnatomicalSurfaces[i]; } } if (name.indexOf("pial") >= 0) { if (pialSurface == NULL) { pialSurface = primaryAnatomicalSurfaces[i]; } } if (name.indexOf("anatomical") >= 0) { if (anatomicalSurface == NULL) { anatomicalSurface = primaryAnatomicalSurfaces[i]; } } if (name.indexOf("fiducial") >= 0) { if (fiducialSurface == NULL) { fiducialSurface = primaryAnatomicalSurfaces[i]; } } } if (midThicknessSurface != NULL) { m_primaryAnatomicalSurface = midThicknessSurface; } else if (whiteMatterSurface != NULL) { m_primaryAnatomicalSurface = whiteMatterSurface; } else if (pialSurface != NULL) { m_primaryAnatomicalSurface = pialSurface; } else if (anatomicalSurface != NULL) { m_primaryAnatomicalSurface = anatomicalSurface; } else if (fiducialSurface != NULL) { m_primaryAnatomicalSurface = fiducialSurface; } } if (m_primaryAnatomicalSurface != NULL) { CaretLogFiner("Primary Anatomical Surface for " + StructureEnum::toGuiName(m_structure) + ": " + m_primaryAnatomicalSurface->getFileNameNoPath()); } else { CaretLogFiner("Primary Anatomical Surface for " + StructureEnum::toGuiName(m_structure) + " is invalid."); } return m_primaryAnatomicalSurface; } /** * @return The surface used for primary anatomical. * Returns NULL if no anatomical surfaces. */ const Surface* BrainStructure::getPrimaryAnatomicalSurface() const { return getPrimaryAnatomicalSurfacePrivate(); } /** * @return The surface used for primary anatomical. * Returns NULL if no anatomical surfaces. */ Surface* BrainStructure::getPrimaryAnatomicalSurface() { /* * Kludge to avoid duplicated code and ease maintenance */ const Surface* constSurface = getPrimaryAnatomicalSurfacePrivate(); Surface* s = (Surface*)constSurface; return s; } /** * Set the primary anatomical surface. * @param primaryAnatomicalSurface * New primary anatomical surface. */ void BrainStructure::setPrimaryAnatomicalSurface(Surface* primaryAnatomicalSurface) { m_primaryAnatomicalSurface = primaryAnatomicalSurface; } /** * Find and return the first surface encountered that contains the * given text in the name of the surface's filename. Text is searched * in an case-insensitive mode. * * @param text * Text that is to bound in the surface's filenname. * @return * Surface that contains the given text in its filename or * NULL if no surface matches. */ Surface* BrainStructure::getSurfaceContainingTextInName(const AString& text) { /* * Kludge to avoid duplicated code and ease maintenance */ const Surface* constSurface = getSurfaceContainingTextInNamePrivate(text); Surface* s = (Surface*)constSurface; return s; } /** * Find and return the first surface encountered that contains the * given text in the name of the surface's filename. Text is searched * in an case-insensitive mode. * * @param text * Text that is to bound in the surface's filenname. * @return * Surface that contains the given text in its filename or * NULL if no surface matches. */ const Surface* BrainStructure::getSurfaceContainingTextInName(const AString& text) const { return getSurfaceContainingTextInNamePrivate(text); } /** * Find and return the first surface encountered that contains the * given text in the name of the surface's filename. Text is searched * in an case-insensitive mode. * * @param text * Text that is to bound in the surface's filenname. * @return * Surface that contains the given text in its filename or * NULL if no surface matches. */ const Surface* BrainStructure::getSurfaceContainingTextInNamePrivate(const AString& text) const { for (std::vector::const_iterator iter = m_surfaces.begin(); iter != m_surfaces.end(); iter++) { const Surface* surface = *iter; const AString name = surface->getFileNameNoPath(); if (name.indexOf(text, 0, Qt::CaseInsensitive) >= 0) { return surface; } } return NULL; } /** * Is the surface in this brain structure? * @param surface * Surface that is tested for being in this brain structure. * @return Returns true if surface in brain structure, else false. */ bool BrainStructure::containsSurface(const Surface* surface) { CaretAssert(surface); if (std::find(m_surfaces.begin(), m_surfaces.end(), surface) != m_surfaces.end()) { return true; } return false; } /** * Find the surface with the given name. * @param surfaceFileName * Name of surface. * @param useAbsolutePath * If true the given surfaceFileName is an absolute path. * If false, the given surfaceFileName is just the file * name without any path. */ Surface* BrainStructure::getSurfaceWithName(const AString& surfaceFileName, const bool useAbsolutePath) { for (std::vector::iterator iter = m_surfaces.begin(); iter != m_surfaces.end(); iter++) { Surface* surface = *iter; const AString name = (useAbsolutePath ? surface->getFileName() : surface->getFileNameNoPath()); if (surfaceFileName == name) { return surface; } } return NULL; } /** * Get the brain that this brain structure is in. */ Brain* BrainStructure::getBrain() { return m_brain; } /** * Get the brain that this brain structure is in. */ const Brain* BrainStructure::getBrain() const { return m_brain; } /** * Get the number of nodes used by this brain structure. * * @return Number of nodes. */ int32_t BrainStructure::getNumberOfNodes() const { if (m_surfaces.empty() == false) { return m_surfaces[0]->getNumberOfNodes(); } return 0; } /** * Get all of the label files. * @param labelFilesOut * Will contain all label files after this method exits. */ void BrainStructure::getLabelFiles(std::vector& labelFilesOut) const { labelFilesOut.clear(); labelFilesOut.insert(labelFilesOut.end(), m_labelFiles.begin(), m_labelFiles.end()); } /** * Get the number of label files. * @return Number of label files. */ int32_t BrainStructure::getNumberOfLabelFiles() const { return m_labelFiles.size(); } /** * Get a label file at the specified index. * @param fileIndex * Index of the label file. * @return * Metric file at the index. */ LabelFile* BrainStructure::getLabelFile(const int32_t fileIndex) { CaretAssertVectorIndex(m_labelFiles, fileIndex); return m_labelFiles[fileIndex]; } /** * Get a label file at the specified index. * @param fileIndex * Index of the label file. * @return * Metric file at the index. */ const LabelFile* BrainStructure::getLabelFile(const int32_t fileIndex) const { CaretAssertVectorIndex(m_labelFiles, fileIndex); return m_labelFiles[fileIndex]; } /** * Get all of the metric files. * @param metricFilesOut * Will contain all metric files after this method exits. */ void BrainStructure::getMetricFiles(std::vector& metricFilesOut) const { metricFilesOut.clear(); metricFilesOut.insert(metricFilesOut.end(), m_metricFiles.begin(), m_metricFiles.end()); } /** * Get the number of metric files. * @return Number of metric files. */ int32_t BrainStructure::getNumberOfMetricFiles() const { return m_metricFiles.size(); } /** * Get a metric file at the specified index. * @param fileIndex * Index of the metric file. * @return * Metric file at the index. */ MetricFile* BrainStructure::getMetricFile(const int32_t fileIndex) { CaretAssertVectorIndex(m_metricFiles, fileIndex); return m_metricFiles[fileIndex]; } /** * Get a metric file at the specified index. * @param fileIndex * Index of the metric file. * @return * Metric file at the index. */ const MetricFile* BrainStructure::getMetricFile(const int32_t fileIndex) const { CaretAssertVectorIndex(m_metricFiles, fileIndex); return m_metricFiles[fileIndex]; } /** * Get the number of rgba files. * @return Number of rgba files. */ int32_t BrainStructure::getNumberOfRgbaFiles() const { return m_rgbaFiles.size(); } /** * Get a rgba file at the specified index. * @param fileIndex * Index of the rgba file. * @return * Metric file at the index. */ RgbaFile* BrainStructure::getRgbaFile(const int32_t fileIndex) { CaretAssertVectorIndex(m_rgbaFiles, fileIndex); return m_rgbaFiles[fileIndex]; } /** * Get a rgba file at the specified index. * @param fileIndex * Index of the rgba file. * @return * Metric file at the index. */ const RgbaFile* BrainStructure::getRgbaFile(const int32_t fileIndex) const { CaretAssertVectorIndex(m_rgbaFiles, fileIndex); return m_rgbaFiles[fileIndex]; } /** * Get all of the rgba files. * @param rgbaFilesOut * Will contain all rgba files after this method exits. */ void BrainStructure::getRgbaFiles(std::vector& rgbaFilesOut) const { rgbaFilesOut.clear(); rgbaFilesOut.insert(rgbaFilesOut.end(), m_rgbaFiles.begin(), m_rgbaFiles.end()); } /** * Receive events from the event manager. * * @param event * The event. */ void BrainStructure::receiveEvent(Event* event) { if (event->getEventType() == EventTypeEnum::EVENT_BRAIN_STRUCTURE_GET_ALL) { EventBrainStructureGetAll* brainStructureEvent = dynamic_cast(event); CaretAssert(brainStructureEvent); brainStructureEvent->addBrainStructure(this); brainStructureEvent->setEventProcessed(); } else if (event->getEventType() == EventTypeEnum::EVENT_GET_NODE_DATA_FILES) { EventNodeDataFilesGet* dataFilesEvent = dynamic_cast(event); CaretAssert(dataFilesEvent); const Surface* associatedSurface = dataFilesEvent->getSurface(); if (associatedSurface != NULL) { if (containsSurface(associatedSurface) == false) { return; } } for (std::vector::iterator labelIter = m_labelFiles.begin(); labelIter != m_labelFiles.end(); labelIter++) { dataFilesEvent->addFile(*labelIter); } for (std::vector::iterator metricIter = m_metricFiles.begin(); metricIter != m_metricFiles.end(); metricIter++) { dataFilesEvent->addFile(*metricIter); } for (std::vector::iterator rgbaIter = m_rgbaFiles.begin(); rgbaIter != m_rgbaFiles.end(); rgbaIter++) { dataFilesEvent->addFile(*rgbaIter); } dataFilesEvent->setEventProcessed(); } else if (event->getEventType() == EventTypeEnum::EVENT_SURFACES_GET) { EventSurfacesGet* getSurfacesEvent = dynamic_cast(event); CaretAssert(getSurfacesEvent); const int32_t numSurfaces = getNumberOfSurfaces(); for (int32_t i = 0; i < numSurfaces; i++) { getSurfacesEvent->addSurface(getSurface(i)); } getSurfacesEvent->setEventProcessed(); } else if (event->getEventType() == EventTypeEnum::EVENT_SURFACE_STRUCTURES_VALID_GET) { EventSurfaceStructuresValidGet* structEvent = dynamic_cast(event); CaretAssert(structEvent); structEvent->addStructure(m_structure, getNumberOfNodes()); } } /** * @return Return the unique identifier for this brain structure. */ int64_t BrainStructure::getBrainStructureIdentifier() const { return m_brainStructureIdentifier; } /** * Get the attributes for this brain structure. * @return * Attributes for this brain structure. */ BrainStructureNodeAttributes* BrainStructure::getNodeAttributes() { return m_nodeAttributes; } /** * Get the attributes for this brain structure. * @return * Attributes for this brain structure. */ const BrainStructureNodeAttributes* BrainStructure::getNodeAttributes() const { return m_nodeAttributes; } /** * Get all loaded data files. * @param allDataFilesOut * Data files are loaded into this parameter. */ void BrainStructure::getAllDataFiles(std::vector& allDataFilesOut) const { allDataFilesOut.insert(allDataFilesOut.end(), m_surfaces.begin(), m_surfaces.end()); allDataFilesOut.insert(allDataFilesOut.end(), m_labelFiles.begin(), m_labelFiles.end()); allDataFilesOut.insert(allDataFilesOut.end(), m_metricFiles.begin(), m_metricFiles.end()); allDataFilesOut.insert(allDataFilesOut.end(), m_rgbaFiles.begin(), m_rgbaFiles.end()); } /** * Remove the data file from memory but DO NOT delete it. * * @param caretDataFile * Caret file that is removed from the Brain. After calling this method * and the file was removed( true was returned), the caller is responsible * for deleting the file when it is no longer needed. * @return * True if the file was removed, else false. */ bool BrainStructure::removeWithoutDeleteDataFile(const CaretDataFile* caretDataFile) { std::vector::iterator surfaceIterator = std::find(m_surfaces.begin(), m_surfaces.end(), caretDataFile); if (surfaceIterator != m_surfaces.end()) { Surface* s = *surfaceIterator; removeAndMaybeDeleteSurface(s, false); return true; } std::vector::iterator labelIterator = std::find(m_labelFiles.begin(), m_labelFiles.end(), caretDataFile); if (labelIterator != m_labelFiles.end()) { m_labelFiles.erase(labelIterator); return true; } std::vector::iterator metricIterator = std::find(m_metricFiles.begin(), m_metricFiles.end(), caretDataFile); if (metricIterator != m_metricFiles.end()) { m_metricFiles.erase(metricIterator); return true; } std::vector::iterator rgbaIterator = std::find(m_rgbaFiles.begin(), m_rgbaFiles.end(), caretDataFile); if (rgbaIterator != m_rgbaFiles.end()) { m_rgbaFiles.erase(rgbaIterator); return true; } return false; } ///** // * Remove AND DELETE a data file from memory (does NOT delete file on disk.) // * Searches all of the loaded files for given file, and, when found // * deletes the file. // * // * @param caretDataFile // * Data file to remove. After calling this method and the file was // * deleted (true was returned) this pointer is no longer valid. // * @return // * true if file was removed, else false. // */ //bool //BrainStructure::removeAndDeleteDataFile(CaretDataFile* caretDataFile) //{ // if (removeWithoutDeleteDataFile(caretDataFile)) { // delete caretDataFile; // return true; // } // // return false; // std::vector::iterator surfaceIterator = // std::find(m_surfaces.begin(), // m_surfaces.end(), // caretDataFile); // if (surfaceIterator != m_surfaces.end()) { // Surface* s = *surfaceIterator; // removeSurface(s); // return true; // } // // std::vector::iterator labelIterator = // std::find(m_labelFiles.begin(), // m_labelFiles.end(), // caretDataFile); // if (labelIterator != m_labelFiles.end()) { // delete caretDataFile; // m_labelFiles.erase(labelIterator); // return true; // } // // std::vector::iterator metricIterator = // std::find(m_metricFiles.begin(), // m_metricFiles.end(), // caretDataFile); // if (metricIterator != m_metricFiles.end()) { // delete caretDataFile; // m_metricFiles.erase(metricIterator); // return true; // } // // std::vector::iterator rgbaIterator = // std::find(m_rgbaFiles.begin(), // m_rgbaFiles.end(), // caretDataFile); // if (rgbaIterator != m_rgbaFiles.end()) { // delete caretDataFile; // m_rgbaFiles.erase(rgbaIterator); // return true; // } // // return false; //} /** * Find a map in a metric file that contains shape data. * It first looks for a shape NIFTI intent code. If that * is not found it looks for curvature, shape, depth, etc. * @param metricFileOut * Output metric file that contains the shape map. * @param shapeMapIndexOut * Output containing index of shape map. * @return * True if a shape map was found, else false. */ bool BrainStructure::getMetricShapeMap(MetricFile* &shapeMetricFileOut, int32_t& shapeMapIndexOut) const { shapeMetricFileOut = NULL; shapeMapIndexOut = -1; MetricFile* depthMetricFile = NULL; int32_t depthMapIndex = -1; MetricFile* depthNamedMetricFile = NULL; MetricFile* curvatureMetricFile = NULL; int32_t curvatureMapIndex = -1; MetricFile* curvatureNamedMetricFile = NULL; MetricFile* shapeNamedMetricFile = NULL; MetricFile* sulcMetricFile = NULL; int32_t sulcMapIndex = -1; MetricFile* sulcNamedMetricFile = NULL; const int numFiles = m_metricFiles.size(); for (int32_t i = 0; i < numFiles; i++) { MetricFile* mf = m_metricFiles[i]; const AString filename = mf->getFileNameNoPath().toLower(); const int32_t numMaps = mf->getNumberOfMaps(); for (int32_t j = 0; j < numMaps; j++) { const AString mapName = mf->getMapName(j).toLower(); if (mapName.contains("sulc")) { if (sulcMetricFile == NULL) { sulcMetricFile = mf; sulcMapIndex = j; } } else if (mapName.contains("depth")) { if (depthMetricFile == NULL) { depthMetricFile = mf; depthMapIndex = j; } } else if (mapName.contains("curv")) { if (curvatureMetricFile == NULL) { curvatureMetricFile = mf; curvatureMapIndex = j; } } } if (filename.contains("sulc")) { if (numMaps > 0) { sulcNamedMetricFile = mf; } } if (filename.contains("shape")) { if (numMaps > 0) { shapeNamedMetricFile = mf; } } if (filename.contains("curv")) { if (numMaps > 0) { curvatureNamedMetricFile = mf; } } if (filename.contains("depth")) { if (numMaps > 0) { depthNamedMetricFile = mf; } } } if (sulcMetricFile != NULL) { shapeMetricFileOut = sulcMetricFile; shapeMapIndexOut = sulcMapIndex; } else if (depthMetricFile != NULL) { shapeMetricFileOut = depthMetricFile; shapeMapIndexOut = depthMapIndex; } else if (curvatureMetricFile != NULL) { shapeMetricFileOut = curvatureMetricFile; shapeMapIndexOut = curvatureMapIndex; } else if (sulcNamedMetricFile != NULL) { shapeMetricFileOut = sulcNamedMetricFile; shapeMapIndexOut = 0; } else if (depthNamedMetricFile != NULL) { shapeMetricFileOut = depthNamedMetricFile; shapeMapIndexOut = 0; } else if (curvatureNamedMetricFile != NULL) { shapeMetricFileOut = curvatureNamedMetricFile; shapeMapIndexOut = 0; } else if (shapeNamedMetricFile != NULL) { shapeMetricFileOut = shapeNamedMetricFile; shapeMapIndexOut = 0; } if ((shapeMetricFileOut != NULL) && (shapeMapIndexOut >= 0)) { return true; } return false; } /** * Get the overlay set for the given tab. * @param tabIndex * Index of tab. * @return * Overlay set at the given tab index. */ OverlaySet* BrainStructure::getOverlaySet(const int 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* BrainStructure::getOverlaySet(const int tabIndex) const { return m_overlaySetArray->getOverlaySet(tabIndex); } /** * Initilize the overlays for this model. */ void BrainStructure::initializeOverlays() { m_overlaySetArray->initializeOverlaySelections(); } /** * Create a scene for an instance of a class. * * @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. * * @return Pointer to SceneClass object representing the state of * this object. Under some circumstances a NULL pointer may be * returned. Caller will take ownership of returned object. */ SceneClass* BrainStructure::saveToScene(const SceneAttributes* sceneAttributes, const AString& instanceName) { SceneClass* sceneClass = new SceneClass(instanceName, "BrainStructure", 1); sceneClass->addInteger("numberOfNodes", getNumberOfNodes()); sceneClass->addEnumeratedType("m_structure", m_structure); sceneClass->addClass(m_nodeAttributes->saveToScene(sceneAttributes, "m_nodeAttributes")); /* * Save Group/Name Selection Hierarchies */ for (std::vector::iterator labelIter = m_labelFiles.begin(); labelIter != m_labelFiles.end(); labelIter++) { LabelFile* lf = *labelIter; sceneClass->addClass(lf->getGroupAndNameHierarchyModel()->saveToScene(sceneAttributes, lf->getFileNameNoPath())); } const Surface* primAnatSurface = getPrimaryAnatomicalSurface(); if (primAnatSurface != NULL) { sceneClass->addPathName("primaryAnatomicalSurface", primAnatSurface->getFileName()); } return sceneClass; } /** * Restore the state of an instance of a class. * * @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 containing the state that was previously * saved and should be restored. */ void BrainStructure::restoreFromScene(const SceneAttributes* sceneAttributes, const SceneClass* sceneClass) { if (sceneClass == NULL) { return; } const int32_t numNodes = sceneClass->getIntegerValue("numberOfNodes", 0); const StructureEnum::Enum structure = sceneClass->getEnumeratedTypeValue("m_structure", StructureEnum::INVALID); /* * Since there may be multiple brain structures in scene, * match by structure-type and number of nodes */ if ((numNodes == getNumberOfNodes()) && (structure == m_structure)) { m_nodeAttributes->restoreFromScene(sceneAttributes, sceneClass->getClass("m_nodeAttributes")); /* * Save Group/Name Selection Hierarchies */ for (std::vector::iterator labelIter = m_labelFiles.begin(); labelIter != m_labelFiles.end(); labelIter++) { LabelFile* lf = *labelIter; const SceneClass* labelClass = sceneClass->getClass(lf->getFileNameNoPath()); lf->getGroupAndNameHierarchyModel()->restoreFromScene(sceneAttributes, labelClass); } } const ScenePathName* primAnatScenePathName = sceneClass->getPathName("primaryAnatomicalSurface"); if (primAnatScenePathName != NULL) { const AString surfaceFileName = primAnatScenePathName->stringValue(); if ( ! surfaceFileName.isEmpty()) { for (std::vector::iterator iter = m_surfaces.begin(); iter != m_surfaces.end(); iter++) { Surface* surface = *iter; CaretAssert(surface); if (surface->getFileName() == surfaceFileName) { setPrimaryAnatomicalSurface(surface); break; } } } } } connectome-workbench-1.2.3+git41-gc4c6c90/src/Brain/BrainStructure.h000066400000000000000000000156321300200146000247330ustar00rootroot00000000000000 #ifndef __BRAIN_STRUCTURE_H__ #define __BRAIN_STRUCTURE_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 "BrainConstants.h" #include "CaretObject.h" #include "EventListenerInterface.h" #include "SceneableInterface.h" #include "StructureEnum.h" #include "SurfaceTypeEnum.h" namespace caret { class Brain; class BrainStructureNodeAttributes; class CaretDataFile; class LabelFile; class MetricFile; class ModelSurface; class OverlaySet; class OverlaySetArray; class RgbaFile; class Surface; /** * Maintains view of some type of object. */ class BrainStructure : public CaretObject, public EventListenerInterface, public SceneableInterface { public: BrainStructure(Brain* brain, StructureEnum::Enum structure); ~BrainStructure(); void receiveEvent(Event* event); virtual SceneClass* saveToScene(const SceneAttributes* sceneAttributes, const AString& instanceName); virtual void restoreFromScene(const SceneAttributes* sceneAttributes, const SceneClass* sceneClass); private: BrainStructure(const BrainStructure& s); BrainStructure& operator=(const BrainStructure& s); public: void addLabelFile(LabelFile* labelFile, const bool addFileToBrainStructure); void addMetricFile(MetricFile* metricFile, const bool addFileToBrainStructure); void addRgbaFile(RgbaFile* rgbaFile, const bool addFileToBrainStructure); void addSurface(Surface* surface, const bool addFileToBrainStructure, const bool initilizeOverlaysFlag); int getNumberOfSurfaces() const; Surface* getSurface(const int32_t indx); const Surface* getSurface(const int32_t indx) const; void getSurfacesOfType(const SurfaceTypeEnum::Enum surfaceType, std::vector& surfacesOut) const; bool containsSurface(const Surface* surface); const Surface* getPrimaryAnatomicalSurface() const; Surface* getPrimaryAnatomicalSurface(); const Surface* getSurfaceContainingTextInName(const AString& text) const; Surface* getSurfaceContainingTextInName(const AString& text); Surface* getSurfaceWithName(const AString& surfaceFileName, const bool useAbsolutePath); void getSurfaces(std::vector& surfacesOut) const; void setPrimaryAnatomicalSurface(Surface* surface); Brain* getBrain(); const Brain* getBrain() const; int32_t getNumberOfNodes() const; StructureEnum::Enum getStructure() const; int32_t getNumberOfLabelFiles() const; LabelFile* getLabelFile(const int32_t fileIndex); const LabelFile* getLabelFile(const int32_t fileIndex) const; void getLabelFiles(std::vector& labelFilesOut) const; int32_t getNumberOfMetricFiles() const; MetricFile* getMetricFile(const int32_t fileIndex); const MetricFile* getMetricFile(const int32_t fileIndex) const; void getMetricFiles(std::vector& metricFilesOut) const; int32_t getNumberOfRgbaFiles() const; RgbaFile* getRgbaFile(const int32_t fileIndex); const RgbaFile* getRgbaFile(const int32_t fileIndex) const; void getRgbaFiles(std::vector& labelFilesOut) const; int64_t getBrainStructureIdentifier() const; BrainStructureNodeAttributes* getNodeAttributes(); const BrainStructureNodeAttributes* getNodeAttributes() const; void getAllDataFiles(std::vector& allDataFilesOut) const; bool removeWithoutDeleteDataFile(const CaretDataFile* caretDataFile); //bool removeAndDeleteDataFile(CaretDataFile* caretDataFile); bool getMetricShapeMap(MetricFile* &metricFileOut, int32_t& shapeMapIndexOut) const; OverlaySet* getOverlaySet(const int tabIndex); const OverlaySet* getOverlaySet(const int tabIndex) const; void initializeOverlays(); private: const Surface* getPrimaryAnatomicalSurfacePrivate() const; const Surface* getSurfaceContainingTextInNamePrivate(const AString& text) const; bool removeAndMaybeDeleteSurface(Surface* surface, const bool deleteSurfaceFile); Brain* m_brain; StructureEnum::Enum m_structure; /** Overlays sets for this model and for each tab */ //OverlaySet* m_overlaySet[BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS]; OverlaySetArray* m_overlaySetArray; std::vector m_surfaces; std::vector m_labelFiles; std::vector m_metricFiles; std::vector m_rgbaFiles; /** Maps a surface to its model */ std::map m_surfaceModelMap; /** Unique number assigned to each brain structure. */ int64_t m_brainStructureIdentifier; /** Generates unique number assigned to each brain structure */ static int64_t s_brainStructureIdentifierCounter; BrainStructureNodeAttributes* m_nodeAttributes; mutable Surface* m_primaryAnatomicalSurface; }; #ifdef __BRAIN_STRUCTURE_DEFINE__ int64_t BrainStructure::s_brainStructureIdentifierCounter = 1; #endif // __BRAIN_STRUCTURE_DEFINE__ } // namespace #endif // __BRAIN_STRUCTURE_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Brain/BrainStructureNodeAttributes.cxx000066400000000000000000000066721300200146000301670ustar00rootroot00000000000000 /*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_STRUCTURE_NODE_ATTRIBUTE_DECLARE__ #include "BrainStructureNodeAttributes.h" #undef __BRAIN_STRUCTURE_NODE_ATTRIBUTE_DECLARE__ #include "CaretAssert.h" #include "SceneClass.h" using namespace caret; /** * \class caret::BrainStructureNodeAttributes * \brief Contains attributes for all node in a brain structure. * * Contains attributes for all nodes in a brain structure. * If the number of nodes in the brain structure changes, * this class' update() method must be called. */ /** * Constructor. */ BrainStructureNodeAttributes::BrainStructureNodeAttributes() : CaretObject() { this->update(0); } /** * Destructor. */ BrainStructureNodeAttributes::~BrainStructureNodeAttributes() { } /** * Get a description of this object's content. * @return String describing this object's content. */ AString BrainStructureNodeAttributes::toString() const { return ("BrainStructureNodeAttributes"); } void BrainStructureNodeAttributes::update(const int32_t /*numberOfNodes*/) { // if (numberOfNodes > 0) { // m_identificationType.resize(numberOfNodes); // this->setAllIdentificationNone(); // } // else { // m_identificationType.clear(); // } } /** * Create a scene for an instance of a class. * * @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. * * @return Pointer to SceneClass object representing the state of * this object. Under some circumstances a NULL pointer may be * returned. Caller will take ownership of returned object. */ SceneClass* BrainStructureNodeAttributes::saveToScene(const SceneAttributes* /*sceneAttributes*/, const AString& instanceName) { SceneClass* sceneClass = new SceneClass(instanceName, "BrainStructureNodeAttributes", 1); return sceneClass; } /** * Restore the state of an instance of a class. * * @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 containing the state that was previously * saved and should be restored. */ void BrainStructureNodeAttributes::restoreFromScene(const SceneAttributes* /*sceneAttributes*/, const SceneClass* sceneClass) { if (sceneClass == NULL) { return; } } connectome-workbench-1.2.3+git41-gc4c6c90/src/Brain/BrainStructureNodeAttributes.h000066400000000000000000000040471300200146000276060ustar00rootroot00000000000000#ifndef __BRAIN_STRUCTURE_NODE_ATTRIBUTE__H_ #define __BRAIN_STRUCTURE_NODE_ATTRIBUTE__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 "SceneableInterface.h" namespace caret { class BrainStructureNodeAttributes : public CaretObject, public SceneableInterface { public: BrainStructureNodeAttributes(); virtual ~BrainStructureNodeAttributes(); void update(const int32_t numberOfNodes); virtual SceneClass* saveToScene(const SceneAttributes* sceneAttributes, const AString& instanceName); virtual void restoreFromScene(const SceneAttributes* sceneAttributes, const SceneClass* sceneClass); private: BrainStructureNodeAttributes(const BrainStructureNodeAttributes&); BrainStructureNodeAttributes& operator=(const BrainStructureNodeAttributes&); public: virtual AString toString() const; private: }; #ifdef __BRAIN_STRUCTURE_NODE_ATTRIBUTE_DECLARE__ // #endif // __BRAIN_STRUCTURE_NODE_ATTRIBUTE_DECLARE__ } // namespace #endif //__BRAIN_STRUCTURE_NODE_ATTRIBUTE__H_ connectome-workbench-1.2.3+git41-gc4c6c90/src/Brain/BrowserTabContent.cxx000066400000000000000000004206371300200146000257440ustar00rootroot00000000000000 /*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 #define __BROWSER_TAB_CONTENT_DECLARE__ #include "BrowserTabContent.h" #undef __BROWSER_TAB_CONTENT_DECLARE__ #include "AnnotationColorBar.h" #include "BorderFile.h" #include "Brain.h" #include "BrainOpenGLViewportContent.h" #include "BrainStructure.h" #include "CaretAssert.h" #include "CaretDataFileSelectionModel.h" #include "CaretLogger.h" #include "CaretMappableDataFileAndMapSelectionModel.h" #include "ChartData.h" #include "ChartMatrixDisplayProperties.h" #include "CaretPreferences.h" #include "ChartableMatrixInterface.h" #include "ChartModelDataSeries.h" #include "CiftiBrainordinateDataSeriesFile.h" #include "CiftiConnectivityMatrixDenseDynamicFile.h" #include "ClippingPlaneGroup.h" #include "GroupAndNameHierarchyGroup.h" #include "GroupAndNameHierarchyModel.h" #include "GroupAndNameHierarchyName.h" #include "DeveloperFlagsEnum.h" #include "DisplayPropertiesBorders.h" #include "DisplayPropertiesFoci.h" #include "EventAnnotationColorBarGet.h" #include "EventCaretMappableDataFileMapsViewedInOverlays.h" #include "EventIdentificationHighlightLocation.h" #include "EventModelGetAll.h" #include "EventManager.h" #include "FociFile.h" #include "IdentificationManager.h" #include "LabelFile.h" #include "MathFunctions.h" #include "Matrix4x4.h" #include "ModelChart.h" #include "ModelSurface.h" #include "ModelSurfaceMontage.h" #include "ModelSurfaceSelector.h" #include "ModelTransform.h" #include "ModelVolume.h" #include "ModelWholeBrain.h" #include "Overlay.h" #include "OverlaySet.h" #include "PaletteColorMapping.h" #include "SceneAttributes.h" #include "SceneClass.h" #include "SceneClassAssistant.h" #include "SessionManager.h" #include "Surface.h" #include "SurfaceMontageConfigurationCerebellar.h" #include "SurfaceMontageConfigurationCerebral.h" #include "SurfaceMontageConfigurationFlatMaps.h" #include "SurfaceSelectionModel.h" #include "StructureEnum.h" #include "VolumeFile.h" #include "ViewingTransformations.h" #include "ViewingTransformationsCerebellum.h" #include "ViewingTransformationsVolume.h" #include "VolumeSliceSettings.h" #include "VolumeSurfaceOutlineModel.h" #include "VolumeSurfaceOutlineSetModel.h" #include "WholeBrainSurfaceSettings.h" using namespace caret; /** * Constructor. * @param tabNumber * Number for this tab. */ BrowserTabContent::BrowserTabContent(const int32_t tabNumber) : CaretObject() { isExecutingConstructor = true; s_allBrowserTabContent.insert(this); const CaretPreferences* prefs = SessionManager::get()->getCaretPreferences(); m_tabNumber = tabNumber; m_surfaceModelSelector = new ModelSurfaceSelector(); m_selectedModelType = ModelTypeEnum::MODEL_TYPE_INVALID; m_volumeModel = NULL; m_wholeBrainModel = NULL; m_surfaceMontageModel = NULL; m_chartModel = NULL; m_guiName = ""; m_userName = ""; m_volumeSurfaceOutlineSetModel = new VolumeSurfaceOutlineSetModel(); m_yokingGroup = YokingGroupEnum::YOKING_GROUP_OFF; m_identificationUpdatesVolumeSlices = prefs->isVolumeIdentificationDefaultedOn(); m_aspectRatio = 1.0; m_aspectRatioLocked = false; m_cerebellumViewingTransformation = new ViewingTransformationsCerebellum(); m_flatSurfaceViewingTransformation = new ViewingTransformations(); m_viewingTransformation = new ViewingTransformations(); m_volumeSliceViewingTransformation = new ViewingTransformationsVolume(); m_wholeBrainSurfaceSettings = new WholeBrainSurfaceSettings(); m_obliqueVolumeRotationMatrix = new Matrix4x4(); leftView(); m_volumeSliceSettings = new VolumeSliceSettings(); m_clippingPlaneGroup = new ClippingPlaneGroup(); m_sceneClassAssistant = new SceneClassAssistant(); m_sceneClassAssistant->add("m_tabNumber", &m_tabNumber); m_sceneClassAssistant->add("m_userName", &m_userName); m_sceneClassAssistant->add("m_selectedModelType", &m_selectedModelType); m_sceneClassAssistant->add("m_surfaceModelSelector", "ModelSurfaceSelector", m_surfaceModelSelector); m_sceneClassAssistant->add("m_volumeSurfaceOutlineSetModel", "VolumeSurfaceOutlineSetModel", m_volumeSurfaceOutlineSetModel); m_sceneClassAssistant->add("m_clippingPlaneGroup", "ClippingPlaneGroup", m_clippingPlaneGroup); m_sceneClassAssistant->add("m_cerebellumViewingTransformation", "ViewingTransformations", m_cerebellumViewingTransformation); m_sceneClassAssistant->add("m_flatSurfaceViewingTransformation", "ViewingTransformations", m_flatSurfaceViewingTransformation); m_sceneClassAssistant->add("m_viewingTransformation", "ViewingTransformations", m_viewingTransformation); m_sceneClassAssistant->add("m_volumeSliceViewingTransformation", "ViewingTransformations", m_volumeSliceViewingTransformation); m_sceneClassAssistant->add("m_volumeSliceSettings", "VolumeSliceSettings", m_volumeSliceSettings); m_sceneClassAssistant->add("m_wholeBrainSurfaceSettings", "WholeBrainSurfaceSettings", m_wholeBrainSurfaceSettings); m_sceneClassAssistant->add("m_identificationUpdatesVolumeSlices", &m_identificationUpdatesVolumeSlices); m_sceneClassAssistant->add("m_aspectRatio", &m_aspectRatio); m_sceneClassAssistant->add("m_aspectRatioLocked", &m_aspectRatioLocked); m_sceneClassAssistant->add("m_yokingGroup", &m_yokingGroup); EventManager::get()->addEventListener(this, EventTypeEnum::EVENT_ANNOTATION_COLOR_BAR_GET); EventManager::get()->addEventListener(this, EventTypeEnum::EVENT_IDENTIFICATION_HIGHLIGHT_LOCATION); EventManager::get()->addEventListener(this, EventTypeEnum::EVENT_CARET_MAPPABLE_DATA_FILE_MAPS_VIEWED_IN_OVERLAYS); isExecutingConstructor = false; /* * Need to be done from here */ if (prefs->isYokingDefaultedOn()) { setYokingGroup(YokingGroupEnum::YOKING_GROUP_A); } } /** * Destructor. */ BrowserTabContent::~BrowserTabContent() { EventManager::get()->removeAllEventsFromListener(this); s_allBrowserTabContent.erase(this); delete m_clippingPlaneGroup; delete m_flatSurfaceViewingTransformation; delete m_cerebellumViewingTransformation; delete m_viewingTransformation; delete m_volumeSliceViewingTransformation; delete m_obliqueVolumeRotationMatrix; delete m_surfaceModelSelector; m_surfaceModelSelector = NULL; delete m_volumeSurfaceOutlineSetModel; m_volumeSurfaceOutlineSetModel = NULL; delete m_volumeSliceSettings; delete m_wholeBrainSurfaceSettings; delete m_sceneClassAssistant; m_sceneClassAssistant = NULL; } /** * Clone the contents of the given browser tab. * @param tabToClone * Tab whose contents is cloned. */ void BrowserTabContent::cloneBrowserTabContent(BrowserTabContent* tabToClone) { CaretAssert(tabToClone); m_surfaceModelSelector->setSelectedStructure(tabToClone->m_surfaceModelSelector->getSelectedStructure()); m_surfaceModelSelector->setSelectedSurfaceModel(tabToClone->m_surfaceModelSelector->getSelectedSurfaceModel()); m_selectedModelType = tabToClone->m_selectedModelType; /* * */ EventModelGetAll allModelsEvent; EventManager::get()->sendEvent(allModelsEvent.getPointer()); std::vector allModels = allModelsEvent.getModels(); for (std::vector::iterator modelIter = allModels.begin(); modelIter != allModels.end(); modelIter++) { Model* model = *modelIter; model->copyTabContent(tabToClone->m_tabNumber, m_tabNumber); } *m_clippingPlaneGroup = *tabToClone->m_clippingPlaneGroup; m_yokingGroup = tabToClone->m_yokingGroup; *m_cerebellumViewingTransformation = *tabToClone->m_cerebellumViewingTransformation; *m_flatSurfaceViewingTransformation = *tabToClone->m_flatSurfaceViewingTransformation; *m_viewingTransformation = *tabToClone->m_viewingTransformation; *m_volumeSliceViewingTransformation = *tabToClone->m_volumeSliceViewingTransformation; *m_volumeSliceSettings = *tabToClone->m_volumeSliceSettings; *m_wholeBrainSurfaceSettings = *tabToClone->m_wholeBrainSurfaceSettings; *m_obliqueVolumeRotationMatrix = *tabToClone->m_obliqueVolumeRotationMatrix; m_identificationUpdatesVolumeSlices = tabToClone->m_identificationUpdatesVolumeSlices; Model* model = getModelForDisplay(); if (model != NULL) { Brain* brain = model->getBrain(); brain->copyDisplayProperties(tabToClone->getTabNumber(), getTabNumber()); const int32_t numberOfBrainStructures = brain->getNumberOfBrainStructures(); for (int32_t i = 0; i < numberOfBrainStructures; i++) { BrainStructure* bs = brain->getBrainStructure(i); const int32_t numLabelFiles = bs->getNumberOfLabelFiles(); for (int32_t j = 0; j < numLabelFiles; j++) { LabelFile* labelFile = bs->getLabelFile(j); labelFile->getGroupAndNameHierarchyModel()->copySelections(tabToClone->getTabNumber(), getTabNumber()); } } const int32_t numBorderFiles = brain->getNumberOfBorderFiles(); for (int32_t i = 0; i < numBorderFiles; i++) { BorderFile* bf = brain->getBorderFile(i); bf->getGroupAndNameHierarchyModel()->copySelections(tabToClone->getTabNumber(), getTabNumber()); } const int32_t numFociFiles = brain->getNumberOfFociFiles(); for (int32_t i = 0; i < numFociFiles; i++) { FociFile* ff = brain->getFociFile(i); ff->getGroupAndNameHierarchyModel()->copySelections(tabToClone->getTabNumber(), getTabNumber()); } } m_volumeSurfaceOutlineSetModel->copyVolumeSurfaceOutlineSetModel(tabToClone->getVolumeSurfaceOutlineSet()); } /** * @return Default name for this tab. */ AString BrowserTabContent::getDefaultName() const { AString s = "(" + AString::number(m_tabNumber + 1) + ") "; const Model* displayedModel = getModelForDisplay(); if (displayedModel != NULL) { const AString name = displayedModel->getNameForBrowserTab(); s += name; } return s; } /** * Get the name of this browser tab. * * @return Name of this tab. */ AString BrowserTabContent::getName() const { if ( ! m_userName.isEmpty()) { return m_userName; } return getDefaultName(); } /** * Set the user name of this tab. The user name * overrides the default naming. * * @param userName * User name for tab. */ void BrowserTabContent::setUserName(const AString& userName) { m_userName = userName; } /** * @return The user name. */ AString BrowserTabContent::getUserName() const { if (m_userName.isEmpty()) { return getDefaultName(); } return m_userName; } /** * Get a description of this object's content. * @return String describing this object's content. */ AString BrowserTabContent::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 BrowserTabContent::getDescriptionOfContent(PlainTextStringBuilder& descriptionOut) const { const int32_t tabIndex = getTabNumber(); descriptionOut.addLine("Browser Tab " + AString::number(tabIndex + 1) + ": "); descriptionOut.pushIndentation(); const Model* model = getModelForDisplay(); if (model != NULL) { bool chartFlag = false; bool surfaceFlag = false; bool surfaceMontageFlag = false; bool wholeBrainFlag = false; bool volumeFlag = false; switch (model->getModelType()) { case ModelTypeEnum::MODEL_TYPE_CHART: chartFlag = true; break; case ModelTypeEnum::MODEL_TYPE_INVALID: break; case ModelTypeEnum::MODEL_TYPE_SURFACE: surfaceFlag = true; break; case ModelTypeEnum::MODEL_TYPE_SURFACE_MONTAGE: surfaceMontageFlag = true; break; case ModelTypeEnum::MODEL_TYPE_VOLUME_SLICES: volumeFlag = true; break; case ModelTypeEnum::MODEL_TYPE_WHOLE_BRAIN: wholeBrainFlag = true; break; } if (chartFlag) { model->getDescriptionOfContent(tabIndex, descriptionOut); } else if (volumeFlag) { descriptionOut.addLine("Volume Slice View"); } else if (wholeBrainFlag) { descriptionOut.addLine("All View"); descriptionOut.pushIndentation(); if (isWholeBrainCerebellumEnabled()) { const Surface* cerebellumSurface = m_wholeBrainModel->getSelectedSurface(StructureEnum::CEREBELLUM, tabIndex); if (cerebellumSurface != NULL) { cerebellumSurface->getDescriptionOfContent(descriptionOut); } } if (isWholeBrainLeftEnabled()) { const Surface* leftSurface = m_wholeBrainModel->getSelectedSurface(StructureEnum::CORTEX_LEFT, tabIndex); if (leftSurface != NULL) { leftSurface->getDescriptionOfContent(descriptionOut); } } if (isWholeBrainRightEnabled()) { const Surface* rightSurface = m_wholeBrainModel->getSelectedSurface(StructureEnum::CORTEX_RIGHT, tabIndex); if (rightSurface != NULL) { rightSurface->getDescriptionOfContent(descriptionOut); } } descriptionOut.popIndentation(); } else if (surfaceFlag) { model->getDescriptionOfContent(tabIndex, descriptionOut); } else if (surfaceMontageFlag) { model->getDescriptionOfContent(tabIndex, descriptionOut); } if (wholeBrainFlag || volumeFlag) { descriptionOut.pushIndentation(); m_volumeSliceSettings->getDescriptionOfContent(model->getModelType(), descriptionOut); descriptionOut.popIndentation(); } if ( ! chartFlag) { getOverlaySet()->getDescriptionOfContent(descriptionOut); } } descriptionOut.popIndentation(); } /** * Get the selected model type. * * @return The selected model type. */ ModelTypeEnum::Enum BrowserTabContent::getSelectedModelType() const { return m_selectedModelType; } /** * Set the selected model type. * * @param selectedModelType * New selected model type. */ void BrowserTabContent::setSelectedModelType(ModelTypeEnum::Enum selectedModelType) { m_selectedModelType = selectedModelType; } /** * Get the model for DISPLAY. * * @return Pointer to displayed model or NULL * if none are available. */ Model* BrowserTabContent::getModelForDisplay() { Model* mdc = NULL; switch (m_selectedModelType) { case ModelTypeEnum::MODEL_TYPE_INVALID: break; case ModelTypeEnum::MODEL_TYPE_SURFACE: mdc = m_surfaceModelSelector->getSelectedSurfaceModel(); break; case ModelTypeEnum::MODEL_TYPE_SURFACE_MONTAGE: mdc = m_surfaceMontageModel; break; case ModelTypeEnum::MODEL_TYPE_VOLUME_SLICES: mdc = m_volumeModel; break; case ModelTypeEnum::MODEL_TYPE_WHOLE_BRAIN: mdc = m_wholeBrainModel; break; case ModelTypeEnum::MODEL_TYPE_CHART: mdc = m_chartModel; break; } return mdc; } /** * Get the model model for DISPLAY. * * @return Pointer to displayed model or NULL * if none are available. */ const Model* BrowserTabContent::getModelForDisplay() const { Model* mdc = NULL; switch (m_selectedModelType) { case ModelTypeEnum::MODEL_TYPE_INVALID: break; case ModelTypeEnum::MODEL_TYPE_SURFACE: mdc = m_surfaceModelSelector->getSelectedSurfaceModel(); break; case ModelTypeEnum::MODEL_TYPE_SURFACE_MONTAGE: mdc = m_surfaceMontageModel; break; case ModelTypeEnum::MODEL_TYPE_VOLUME_SLICES: mdc = m_volumeModel; break; case ModelTypeEnum::MODEL_TYPE_WHOLE_BRAIN: mdc = m_wholeBrainModel; break; case ModelTypeEnum::MODEL_TYPE_CHART: mdc = m_chartModel; break; } return mdc; } /** * Get the displayed chart model. * * @return Pointer to displayed chart model or * NULL if the displayed model is NOT a * chart. */ ModelChart* BrowserTabContent::getDisplayedChartModel() { ModelChart* mc = dynamic_cast(getModelForDisplay()); return mc; } /** * Get the displayed chart model. * * @return Pointer to displayed chart model or * NULL if the displayed model is NOT a * chart. */ const ModelChart* BrowserTabContent::getDisplayedChartModel() const { const ModelChart* mc = dynamic_cast(getModelForDisplay()); return mc; } /** * Get the displayed surface model. * * @return Pointer to displayed surface model or * NULL if the displayed model is NOT a * surface. */ ModelSurface* BrowserTabContent::getDisplayedSurfaceModel() { ModelSurface* mdcs = dynamic_cast(getModelForDisplay()); return mdcs; } /** * Get the displayed surface model. * * @return Pointer to displayed surface model or * NULL if the displayed model is NOT a * surface. */ const ModelSurface* BrowserTabContent::getDisplayedSurfaceModel() const { const ModelSurface* mdcs = dynamic_cast(getModelForDisplay()); return mdcs; } /** * Get the displayed volume model. * * @return Pointer to displayed volume model or * NULL if the displayed model is NOT a * volume. */ ModelVolume* BrowserTabContent::getDisplayedVolumeModel() { ModelVolume* mdcv = dynamic_cast(getModelForDisplay()); return mdcv; } /** * Get the displayed volume model. * * @return Pointer to displayed volume model or * NULL if the displayed model is NOT a * volume. */ const ModelVolume* BrowserTabContent::getDisplayedVolumeModel() const { const ModelVolume* mdcv = dynamic_cast(getModelForDisplay()); return mdcv; } /** * @return True if the displayed model is a cerebellum surface. */ bool BrowserTabContent::isCerebellumDisplayed() const { const ModelSurface* surfaceModel = getDisplayedSurfaceModel(); if (surfaceModel != NULL) { if (surfaceModel->getSurface()->getStructure() == StructureEnum::CEREBELLUM) { return true; } } const ModelSurfaceMontage* montageModel = getDisplayedSurfaceMontageModel(); if (montageModel != NULL) { if (montageModel->getSelectedConfigurationType(getTabNumber()) == SurfaceMontageConfigurationTypeEnum::CEREBELLAR_CORTEX_CONFIGURATION) { return true; } } return false; } /** * @return True if the displayed model is a flat surface. */ bool BrowserTabContent::isFlatSurfaceDisplayed() const { const ModelSurface* surfaceModel = getDisplayedSurfaceModel(); if (surfaceModel != NULL) { if (surfaceModel->getSurface()->getSurfaceType() == SurfaceTypeEnum::FLAT) { return true; } } const ModelSurfaceMontage* montageModel = getDisplayedSurfaceMontageModel(); if (montageModel != NULL) { if (montageModel->getSelectedConfigurationType(getTabNumber()) == SurfaceMontageConfigurationTypeEnum::FLAT_CONFIGURATION) { return true; } } return false; } /** * @return True if the displayed model is a chart */ bool BrowserTabContent::isChartDisplayed() const { const ModelChart* chartModel = getDisplayedChartModel(); if (chartModel != NULL) { return true; } return false; } /** * @return Is the displayed model a volume slice model? */ bool BrowserTabContent::isVolumeSlicesDisplayed() const { const ModelVolume* mdcv = dynamic_cast(getModelForDisplay()); const bool volumeFlag = (mdcv != NULL); return volumeFlag; } /** * @return Is the displayed model the whole brain model (ALL)? */ bool BrowserTabContent::isWholeBrainDisplayed() const { const ModelWholeBrain* mwb = dynamic_cast(getModelForDisplay()); const bool wholeBrainFlag = (mwb != NULL); return wholeBrainFlag; } /** * Get the displayed whole brain model. * * @return Pointer to displayed whole brain model or * NULL if the displayed model is NOT a * whole brain. */ ModelWholeBrain* BrowserTabContent::getDisplayedWholeBrainModel() { ModelWholeBrain* mdcwb = dynamic_cast(getModelForDisplay()); return mdcwb; } /** * @return Pointer to displayed surface montage model * or NULL if the displayed model is not a surface * montage model. */ ModelSurfaceMontage* BrowserTabContent::getDisplayedSurfaceMontageModel() { ModelSurfaceMontage* mdcsm = dynamic_cast(getModelForDisplay()); return mdcsm; } /** * @return Pointer to displayed surface montage model * or NULL if the displayed model is not a surface * montage model. */ const ModelSurfaceMontage* BrowserTabContent::getDisplayedSurfaceMontageModel() const { const ModelSurfaceMontage* mdcsm = dynamic_cast(getModelForDisplay()); return mdcsm; } /** * Get all of the available surface models. * * @return Vector containing all surface models. */ const std::vector BrowserTabContent::getAllSurfaceModels() const { return m_allSurfaceModels; } /** * @return The surface model selector used to * select a surface and structure. */ ModelSurfaceSelector* BrowserTabContent::getSurfaceModelSelector() { return m_surfaceModelSelector; } /** * Get the overlay assignments for this tab. * * @return Overlay assignments for this tab or NULL if no valid model. */ OverlaySet* BrowserTabContent::getOverlaySet() { Model* model = getModelForDisplay(); if (model != NULL) { return model->getOverlaySet(m_tabNumber); } return NULL; } /** * Get the overlay assignments for this tab. * * @return Overlay assignments for this tab or NULL if no valid model. */ const OverlaySet* BrowserTabContent::getOverlaySet() const { const Model* model = getModelForDisplay(); if (model != NULL) { return model->getOverlaySet(m_tabNumber); } return NULL; } /** * Get the tab number for this content. * * @return Tab number. */ int32_t BrowserTabContent::getTabNumber() const { return m_tabNumber; } /** * Update the selected models. */ void BrowserTabContent::update(const std::vector models) { m_surfaceModelSelector->updateSelector(models); const int32_t numModels = static_cast(models.size()); ModelVolume* previousVolumeModel = m_volumeModel; m_allSurfaceModels.clear(); m_surfaceModelSelector->getSelectableSurfaceModels(m_allSurfaceModels); m_volumeModel = NULL; m_wholeBrainModel = NULL; m_surfaceMontageModel = NULL; m_chartModel = NULL; for (int i = 0; i < numModels; i++) { Model* mdc = models[i]; ModelSurface* mdcs = dynamic_cast(mdc); ModelVolume* mdcv = dynamic_cast(mdc); ModelWholeBrain* mdcwb = dynamic_cast(mdc); ModelSurfaceMontage* mdcsm = dynamic_cast(mdc); ModelChart* mdch = dynamic_cast(mdc); if (mdcs != NULL) { /* nothing to do since the surface model selector handles surfaces */ } else if (mdcv != NULL) { CaretAssertMessage((m_volumeModel == NULL), "There is more than one volume model."); m_volumeModel = mdcv; } else if (mdcwb != NULL) { CaretAssertMessage((m_wholeBrainModel == NULL), "There is more than one whole brain model."); m_wholeBrainModel = mdcwb; } else if (mdcsm != NULL) { CaretAssertMessage((m_surfaceMontageModel == NULL), "There is more than one surface montage model."); m_surfaceMontageModel = mdcsm; } else if (mdch != NULL) { CaretAssertMessage((m_chartModel == NULL), "There is more than one surface chart model."); m_chartModel = mdch; } else { CaretAssertMessage(0, (AString("Unknown type of brain model.") + mdc->getNameForGUI(true))); } } switch (m_selectedModelType) { case ModelTypeEnum::MODEL_TYPE_INVALID: break; case ModelTypeEnum::MODEL_TYPE_SURFACE: if (m_surfaceModelSelector->getSelectedSurfaceModel() == NULL) { m_selectedModelType = ModelTypeEnum::MODEL_TYPE_INVALID; } break; case ModelTypeEnum::MODEL_TYPE_SURFACE_MONTAGE: if (m_surfaceMontageModel == NULL) { m_selectedModelType = ModelTypeEnum::MODEL_TYPE_INVALID; } break; case ModelTypeEnum::MODEL_TYPE_VOLUME_SLICES: if (m_volumeModel == NULL) { m_selectedModelType = ModelTypeEnum::MODEL_TYPE_INVALID; } break; case ModelTypeEnum::MODEL_TYPE_WHOLE_BRAIN: if (m_wholeBrainModel == NULL) { m_selectedModelType = ModelTypeEnum::MODEL_TYPE_INVALID; } break; case ModelTypeEnum::MODEL_TYPE_CHART: if (m_chartModel == 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; } else if (m_volumeModel != NULL) { m_selectedModelType = ModelTypeEnum::MODEL_TYPE_VOLUME_SLICES; } 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_chartModel != NULL) { m_selectedModelType = ModelTypeEnum::MODEL_TYPE_CHART; } } if (m_volumeModel != NULL) { if (m_volumeModel != previousVolumeModel) { VolumeMappableInterface* underlayVolume = m_volumeModel->getOverlaySet(m_tabNumber)->getUnderlayVolume(); if (underlayVolume != NULL) { /* * Set montage slice spacing based upon slices * in the Z-axis. */ std::vector dimensions; underlayVolume->getDimensions(dimensions); if (dimensions.size() >= 3) { const int32_t dimZ = dimensions[2]; if (dimZ > 0) { const int32_t maxZ = static_cast(dimZ * 0.90); const int32_t minZ = static_cast(dimZ * 0.10); const int32_t sliceRange = (maxZ - minZ); int32_t sliceSpacing = 1; if (sliceRange > 0) { const int32_t numSlicesViewed = (m_volumeSliceSettings->getMontageNumberOfRows() * m_volumeSliceSettings->getMontageNumberOfColumns()); sliceSpacing = (sliceRange / numSlicesViewed); } m_volumeSliceSettings->setMontageSliceSpacing(sliceSpacing); } } } } } } /** * Is the chart model selection valid? * * @return bool indicating validity. */ bool BrowserTabContent::isChartModelValid() const { bool valid = (m_chartModel != NULL); return valid; } /** * Is the surface model selection valid? * * @return bool indicating validity. */ bool BrowserTabContent::isSurfaceModelValid() const { bool valid = (m_allSurfaceModels.empty() == false); return valid; } /** * Is the volume model selection valid? * * @return bool indicating validity. */ bool BrowserTabContent::isVolumeSliceModelValid() const { bool valid = (m_volumeModel != NULL); return valid; } /** * Is the whole brain model selection valid? * * @return bool indicating validity. */ bool BrowserTabContent::isWholeBrainModelValid() const { bool valid = (m_wholeBrainModel != NULL); return valid; } /** * Is the surface montage model selection valid? * * @return bool indicating validity. */ bool BrowserTabContent::isSurfaceMontageModelValid() const { bool valid = (m_surfaceMontageModel != NULL); return valid; } /** * @return Is the aspect ratio locked? */ bool BrowserTabContent::isAspectRatioLocked() const { return m_aspectRatioLocked; } /** * Set the aspect ratio locked status. * * @param locked * New status. */ void BrowserTabContent::setAspectRatioLocked(const bool locked) { m_aspectRatioLocked = locked; } /** * @return The aspect ratio. */ float BrowserTabContent::getAspectRatio() const { return m_aspectRatio; } /** * Set the aspect ratio. * * @param aspectRatio * New value for aspect ratio. */ void BrowserTabContent::setAspectRatio(const float aspectRatio) { m_aspectRatio = aspectRatio; } /** * Receive an event. * * @param event * The event that the receive can respond to. */ void BrowserTabContent::receiveEvent(Event* event) { if (event->getEventType() == EventTypeEnum::EVENT_ANNOTATION_COLOR_BAR_GET) { EventAnnotationColorBarGet* colorBarEvent = dynamic_cast(event); CaretAssert(colorBarEvent); if (colorBarEvent->isGetAnnotationColorBarsForTabIndex(m_tabNumber)) { std::vector colorBars; getAnnotationColorBars(colorBars); colorBarEvent->addAnnotationColorBars(colorBars); } } else if (event->getEventType() == EventTypeEnum::EVENT_IDENTIFICATION_HIGHLIGHT_LOCATION) { EventIdentificationHighlightLocation* idLocationEvent = dynamic_cast(event); CaretAssert(idLocationEvent); Model* model = getModelForDisplay(); if (model == NULL) { return; } if (idLocationEvent->isTabSelected(getTabNumber())) { if (isIdentificationUpdatesVolumeSlices()) { const float* highlighXYZ = idLocationEvent->getXYZ(); for (int32_t windowTabNumber = 0; windowTabNumber < BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS; windowTabNumber++) { float volumeSliceXYZ[3] = { highlighXYZ[0], highlighXYZ[1], highlighXYZ[2] }; /* * If othogonal/montage viewing, do not alter the slice * coordinate in the axis being viewed */ if (getDisplayedVolumeModel() != NULL) { bool keepSliceCoordinateForSelectedAxis = false; switch (m_volumeSliceSettings->getSliceProjectionType()) { case VolumeSliceProjectionTypeEnum::VOLUME_SLICE_PROJECTION_ORTHOGONAL: if (getSliceViewPlane() != VolumeSliceViewPlaneEnum::ALL) { keepSliceCoordinateForSelectedAxis = true; } break; case VolumeSliceProjectionTypeEnum::VOLUME_SLICE_PROJECTION_OBLIQUE: //keepSliceCoordinateForSelectedAxis = true; break; } switch (m_volumeSliceSettings->getSliceDrawingType()) { case VolumeSliceDrawingTypeEnum::VOLUME_SLICE_DRAW_MONTAGE: keepSliceCoordinateForSelectedAxis = true; break; case VolumeSliceDrawingTypeEnum::VOLUME_SLICE_DRAW_SINGLE: break; } if (keepSliceCoordinateForSelectedAxis) { switch (getSliceViewPlane()) { case VolumeSliceViewPlaneEnum::ALL: volumeSliceXYZ[0] = getSliceCoordinateParasagittal(); volumeSliceXYZ[1] = getSliceCoordinateCoronal(); volumeSliceXYZ[2] = getSliceCoordinateAxial(); break; case VolumeSliceViewPlaneEnum::PARASAGITTAL: volumeSliceXYZ[0] = getSliceCoordinateParasagittal(); break; case VolumeSliceViewPlaneEnum::CORONAL: volumeSliceXYZ[1] = getSliceCoordinateCoronal(); break; case VolumeSliceViewPlaneEnum::AXIAL: volumeSliceXYZ[2] = getSliceCoordinateAxial(); break; } } } selectSlicesAtCoordinate(volumeSliceXYZ); //m_volumeSliceSettings->selectSlicesAtCoordinate(volumeSliceXYZ); } } } idLocationEvent->setEventProcessed(); } else if (event->getEventType() == EventTypeEnum::EVENT_CARET_MAPPABLE_DATA_FILE_MAPS_VIEWED_IN_OVERLAYS) { EventCaretMappableDataFileMapsViewedInOverlays* mapOverlayEvent = dynamic_cast(event); CaretAssert(mapOverlayEvent); OverlaySet* overlaySet = getOverlaySet(); const int32_t numOverlays = overlaySet->getNumberOfDisplayedOverlays(); for (int32_t i = (numOverlays - 1); i >= 0; i--) { Overlay* overlay = overlaySet->getOverlay(i); if (overlay->isEnabled()) { CaretMappableDataFile* mapFile; int32_t mapFileIndex; overlay->getSelectionData(mapFile, mapFileIndex); if (mapFile != NULL) { if (mapFile == mapOverlayEvent->getCaretMappableDataFile()) { if ((mapFileIndex >= 0) && (mapFileIndex < mapFile->getNumberOfMaps())) { mapOverlayEvent->addMapIndex(mapFileIndex); } } } } } } } /** * Get annotation color bars that should be drawn for this tab. * * @param colorBarsOut * Colorbars that should be drawn. */ void BrowserTabContent::getAnnotationColorBars(std::vector& colorBarsOut) { colorBarsOut.clear(); if (getModelForDisplay() == NULL) { return; } bool useOverlayFlag = false; bool useChartsFlag = false; switch (getSelectedModelType()) { case ModelTypeEnum::MODEL_TYPE_INVALID: break; case ModelTypeEnum::MODEL_TYPE_CHART: useChartsFlag = true; break; case ModelTypeEnum::MODEL_TYPE_SURFACE: useOverlayFlag = true; break; case ModelTypeEnum::MODEL_TYPE_SURFACE_MONTAGE: useOverlayFlag = true; break; case ModelTypeEnum::MODEL_TYPE_VOLUME_SLICES: useOverlayFlag = true; break; case ModelTypeEnum::MODEL_TYPE_WHOLE_BRAIN: useOverlayFlag = true; break; } std::vector colorBarMapFileInfo; if (useOverlayFlag) { OverlaySet* overlaySet = getOverlaySet(); const int32_t numOverlays = overlaySet->getNumberOfDisplayedOverlays(); for (int32_t i = (numOverlays - 1); i >= 0; i--) { Overlay* overlay = overlaySet->getOverlay(i); if (overlay->isEnabled()) { AnnotationColorBar* colorBar = overlay->getColorBar(); CaretMappableDataFile* mapFile; int32_t mapIndex; overlay->getSelectionData(mapFile, mapIndex); colorBarMapFileInfo.push_back(ColorBarFileMap(colorBar, mapFile, mapIndex)); } } } if (useChartsFlag) { ModelChart* modelChart = getDisplayedChartModel(); if (modelChart != NULL) { CaretDataFileSelectionModel* fileModel = NULL; switch (modelChart->getSelectedChartDataType(m_tabNumber)) { case ChartDataTypeEnum::CHART_DATA_TYPE_INVALID: break; case ChartDataTypeEnum::CHART_DATA_TYPE_LINE_DATA_SERIES: break; case ChartDataTypeEnum::CHART_DATA_TYPE_LINE_FREQUENCY_SERIES: break; case ChartDataTypeEnum::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) { fileModel = modelChart->getChartableMatrixParcelFileSelectionModel(m_tabNumber); } break; case ChartDataTypeEnum::CHART_DATA_TYPE_MATRIX_SERIES: if (modelChart->getSelectedChartDataType(m_tabNumber) == ChartDataTypeEnum::CHART_DATA_TYPE_MATRIX_SERIES) { fileModel = modelChart->getChartableMatrixSeriesFileSelectionModel(m_tabNumber); } } if (fileModel != NULL) { CaretDataFile* caretFile = fileModel->getSelectedFile(); if (caretFile != NULL) { CaretMappableDataFile* mapFile = dynamic_cast(caretFile); if (mapFile != NULL) { ChartableMatrixInterface* matrixFile = dynamic_cast(mapFile); if (matrixFile != NULL) { ChartMatrixDisplayProperties* props = matrixFile->getChartMatrixDisplayProperties(m_tabNumber); AnnotationColorBar* colorBar = props->getColorBar(); const int32_t mapIndex = 0; colorBarMapFileInfo.push_back(ColorBarFileMap(colorBar, mapFile, mapIndex)); } } } } } } const int32_t numColorBarMapInfo = static_cast(colorBarMapFileInfo.size()); for (int32_t i = 0; i < numColorBarMapInfo; i++) { CaretAssertVectorIndex(colorBarMapFileInfo, i); const ColorBarFileMap& info = colorBarMapFileInfo[i]; if (info.m_colorBar->isDisplayed()) { if (info.m_mapFile != NULL) { if (info.m_mapFile->isMappedWithPalette()) { if ((info.m_mapIndex >= 0) && (info.m_mapIndex < info.m_mapFile->getNumberOfMaps())) { PaletteColorMapping* paletteColorMapping = info.m_mapFile->getMapPaletteColorMapping(info.m_mapIndex); if (paletteColorMapping != NULL) { FastStatistics* statistics = NULL; switch (info.m_mapFile->getPaletteNormalizationMode()) { case PaletteNormalizationModeEnum::NORMALIZATION_ALL_MAP_DATA: statistics = const_cast(info.m_mapFile->getFileFastStatistics()); break; case PaletteNormalizationModeEnum::NORMALIZATION_SELECTED_MAP_DATA: statistics = const_cast(info.m_mapFile->getMapFastStatistics(info.m_mapIndex)); break; } paletteColorMapping->setupAnnotationColorBar(statistics, info.m_colorBar); info.m_colorBar->setTabIndex(m_tabNumber); colorBarsOut.push_back(info.m_colorBar); } } } } } } } /** * Get the map files for which a palette should be displayed in the * graphcis window. Note that the order of map files and indices starts * with the bottom most layer and ends with the top most overlay. * * @param mapFiles * Outut Map files that should have a palette displayed in the graphics window. * @param mapIndices * Output Indices of the maps in the mapFiles. */ void BrowserTabContent::getDisplayedPaletteMapFiles(std::vector& mapFiles, std::vector& mapIndices) { mapFiles.clear(); mapIndices.clear(); if (getModelForDisplay() == NULL) { return; } bool useOverlayFlag = false; bool useChartsFlag = false; switch (getSelectedModelType()) { case ModelTypeEnum::MODEL_TYPE_INVALID: break; case ModelTypeEnum::MODEL_TYPE_CHART: useChartsFlag = true; break; case ModelTypeEnum::MODEL_TYPE_SURFACE: useOverlayFlag = true; break; case ModelTypeEnum::MODEL_TYPE_SURFACE_MONTAGE: useOverlayFlag = true; break; case ModelTypeEnum::MODEL_TYPE_VOLUME_SLICES: useOverlayFlag = true; break; case ModelTypeEnum::MODEL_TYPE_WHOLE_BRAIN: useOverlayFlag = true; break; } if (useOverlayFlag) { OverlaySet* overlaySet = getOverlaySet(); const int32_t numOverlays = overlaySet->getNumberOfDisplayedOverlays(); for (int32_t i = (numOverlays - 1); i >= 0; i--) { Overlay* overlay = overlaySet->getOverlay(i); if (overlay->isEnabled()) { AnnotationColorBar* colorBar = overlay->getColorBar(); if (colorBar->isDisplayed()) { CaretMappableDataFile* mapFile; int32_t mapFileIndex; overlay->getSelectionData(mapFile, mapFileIndex); if (mapFile != NULL) { if (mapFile->isMappedWithPalette()) { if ((mapFileIndex >= 0) && (mapFileIndex < mapFile->getNumberOfMaps())) { mapFiles.push_back(mapFile); mapIndices.push_back(mapFileIndex); } } } } } } } if (useChartsFlag) { ModelChart* modelChart = getDisplayedChartModel(); if (modelChart != NULL) { CaretDataFileSelectionModel* fileModel = NULL; switch (modelChart->getSelectedChartDataType(m_tabNumber)) { case ChartDataTypeEnum::CHART_DATA_TYPE_INVALID: break; case ChartDataTypeEnum::CHART_DATA_TYPE_LINE_DATA_SERIES: break; case ChartDataTypeEnum::CHART_DATA_TYPE_LINE_FREQUENCY_SERIES: break; case ChartDataTypeEnum::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) { fileModel = modelChart->getChartableMatrixParcelFileSelectionModel(m_tabNumber); } break; case ChartDataTypeEnum::CHART_DATA_TYPE_MATRIX_SERIES: if (modelChart->getSelectedChartDataType(m_tabNumber) == ChartDataTypeEnum::CHART_DATA_TYPE_MATRIX_SERIES) { fileModel = modelChart->getChartableMatrixSeriesFileSelectionModel(m_tabNumber); } } if (fileModel != NULL) { CaretDataFile* caretFile = fileModel->getSelectedFile(); if (caretFile != NULL) { CaretMappableDataFile* mapFile = dynamic_cast(caretFile); if (mapFile != NULL) { ChartableMatrixInterface* matrixFile = dynamic_cast(mapFile); if (matrixFile != NULL) { ChartMatrixDisplayProperties* props = matrixFile->getChartMatrixDisplayProperties(m_tabNumber); if (props->getColorBar()->isDisplayed()) { /* * Matrix contains all file data and always * uses a map index of zero. */ mapFiles.push_back(mapFile); mapIndices.push_back(0); } } } } } } } } /** * @return The volume surface outline model for this tab. */ VolumeSurfaceOutlineSetModel* BrowserTabContent::getVolumeSurfaceOutlineSet() { return m_volumeSurfaceOutlineSetModel; } /** * @return The volume surface outline model for this tab. */ const VolumeSurfaceOutlineSetModel* BrowserTabContent::getVolumeSurfaceOutlineSet() const { return m_volumeSurfaceOutlineSetModel; } /** * @return Structures from surface(s) displayed in this tab. */ std::vector BrowserTabContent::getSurfaceStructuresDisplayed() { std::vector allDisplayedFiles; getFilesDisplayedInTab(allDisplayedFiles); std::set structures; for (std::vector::iterator fileIter = allDisplayedFiles.begin(); fileIter != allDisplayedFiles.end(); fileIter++) { CaretDataFile* file = *fileIter; CaretAssert(file); if (file->getDataFileType() == DataFileTypeEnum::SURFACE) { SurfaceFile* surfaceFile = dynamic_cast(file); CaretAssert(surfaceFile); structures.insert(surfaceFile->getStructure()); } } std::vector structuresOut(structures.begin(), structures.end()); return structuresOut; } /** * Get the data files displayed in this tab. * @param displayedDataFilesOut * Displayed data file are loaded into this parameter. */ void BrowserTabContent::getFilesDisplayedInTab(std::vector& displayedDataFilesOut) { displayedDataFilesOut.clear(); Model* model = getModelForDisplay(); if (model == NULL) { return; } std::set displayedDataFiles; const int32_t tabIndex = getTabNumber(); switch (getSelectedModelType()) { case ModelTypeEnum::MODEL_TYPE_INVALID: break; case ModelTypeEnum::MODEL_TYPE_SURFACE: { ModelSurface* ms = getDisplayedSurfaceModel(); displayedDataFiles.insert(ms->getSurface()); } break; case ModelTypeEnum::MODEL_TYPE_SURFACE_MONTAGE: { ModelSurfaceMontage* msm = getDisplayedSurfaceMontageModel(); switch (msm->getSelectedConfigurationType(tabIndex)) { case SurfaceMontageConfigurationTypeEnum::CEREBELLAR_CORTEX_CONFIGURATION: { SurfaceMontageConfigurationCerebellar* smcc = msm->getCerebellarConfiguration(tabIndex); if (smcc->isFirstSurfaceEnabled()) { displayedDataFiles.insert(smcc->getFirstSurfaceSelectionModel()->getSurface()); } if (smcc->isSecondSurfaceEnabled()) { displayedDataFiles.insert(smcc->getSecondSurfaceSelectionModel()->getSurface()); } } break; case SurfaceMontageConfigurationTypeEnum::CEREBRAL_CORTEX_CONFIGURATION: { SurfaceMontageConfigurationCerebral* smcc = msm->getCerebralConfiguration(tabIndex); if (smcc->isFirstSurfaceEnabled()) { if (smcc->isLeftEnabled()) { displayedDataFiles.insert(smcc->getLeftFirstSurfaceSelectionModel()->getSurface()); } if (smcc->isRightEnabled()) { displayedDataFiles.insert(smcc->getRightFirstSurfaceSelectionModel()->getSurface()); } } if (smcc->isSecondSurfaceEnabled()) { if (smcc->isLeftEnabled()) { displayedDataFiles.insert(smcc->getLeftSecondSurfaceSelectionModel()->getSurface()); } if (smcc->isRightEnabled()) { displayedDataFiles.insert(smcc->getRightSecondSurfaceSelectionModel()->getSurface()); } } } break; case SurfaceMontageConfigurationTypeEnum::FLAT_CONFIGURATION: { SurfaceMontageConfigurationFlatMaps* smcfm = msm->getFlatMapsConfiguration(tabIndex); if (smcfm->isLeftEnabled()) { displayedDataFiles.insert(smcfm->getLeftSurfaceSelectionModel()->getSurface()); } if (smcfm->isRightEnabled()) { displayedDataFiles.insert(smcfm->getRightSurfaceSelectionModel()->getSurface()); } if (smcfm->isCerebellumEnabled()) { displayedDataFiles.insert(smcfm->getCerebellumSurfaceSelectionModel()->getSurface()); } } break; } } break; case ModelTypeEnum::MODEL_TYPE_VOLUME_SLICES: { const int32_t numOutlines = m_volumeSurfaceOutlineSetModel->getNumberOfDislayedVolumeSurfaceOutlines(); for (int32_t i = 0; i < numOutlines; i++) { VolumeSurfaceOutlineModel* model = m_volumeSurfaceOutlineSetModel->getVolumeSurfaceOutlineModel(i); if (model->isDisplayed()) { displayedDataFiles.insert(model->getSurface()); } } } break; case ModelTypeEnum::MODEL_TYPE_WHOLE_BRAIN: { ModelWholeBrain* wbm = getDisplayedWholeBrainModel(); if (isWholeBrainLeftEnabled()) { displayedDataFiles.insert(wbm->getSelectedSurface(StructureEnum::CORTEX_LEFT, tabIndex)); } if (isWholeBrainRightEnabled()) { displayedDataFiles.insert(wbm->getSelectedSurface(StructureEnum::CORTEX_RIGHT, tabIndex)); } if (isWholeBrainCerebellumEnabled()) { displayedDataFiles.insert(wbm->getSelectedSurface(StructureEnum::CEREBELLUM, tabIndex)); } } break; case ModelTypeEnum::MODEL_TYPE_CHART: break; } /* * Check overlay files */ OverlaySet* overlaySet = model->getOverlaySet(tabIndex); const int32_t numOverlays = overlaySet->getNumberOfDisplayedOverlays(); for (int32_t i = 0; i < numOverlays; i++) { Overlay* overlay = overlaySet->getOverlay(i); if (overlay->isEnabled()) { CaretMappableDataFile* overlayDataFile = NULL; int32_t mapIndex; overlay->getSelectionData(overlayDataFile, mapIndex); if (overlayDataFile != NULL) { /* * Dense dynamic is encapsulated within its parent data-series * file so include both files. */ if (overlayDataFile->getDataFileType() == DataFileTypeEnum::CONNECTIVITY_DENSE_DYNAMIC) { CiftiConnectivityMatrixDenseDynamicFile* dynFile = dynamic_cast(overlayDataFile); CaretAssert(dynFile); displayedDataFiles.insert(dynFile->getParentBrainordinateDataSeriesFile()); } displayedDataFiles.insert(overlayDataFile); } } } /* * Check border files */ Brain* brain = model->getBrain(); const DisplayPropertiesBorders* borderDisplayProperties = brain->getDisplayPropertiesBorders(); const DisplayGroupEnum::Enum borderDisplayGroup = borderDisplayProperties->getDisplayGroupForTab(tabIndex); if (borderDisplayProperties->isDisplayed(borderDisplayGroup, tabIndex)) { const int32_t numBorderFiles = brain->getNumberOfBorderFiles(); for (int32_t i = 0; i < numBorderFiles; i++) { BorderFile* borderFile = brain->getBorderFile(i); const GroupAndNameHierarchyModel* classAndNameSelection = borderFile->getGroupAndNameHierarchyModel(); if (classAndNameSelection->isSelected(borderDisplayGroup, tabIndex)) { displayedDataFilesOut.push_back(borderFile); } } } /* * Check foci files */ const DisplayPropertiesFoci* fociDisplayProperties = brain->getDisplayPropertiesFoci(); const DisplayGroupEnum::Enum fociDisplayGroup = fociDisplayProperties->getDisplayGroupForTab(tabIndex); if (fociDisplayProperties->isDisplayed(fociDisplayGroup, tabIndex)) { const int32_t numFociFiles = brain->getNumberOfFociFiles(); for (int32_t i = 0; i < numFociFiles; i++) { FociFile* fociFile = brain->getFociFile(i); const GroupAndNameHierarchyModel* classAndNameSelection = fociFile->getGroupAndNameHierarchyModel(); if (classAndNameSelection->isSelected(fociDisplayGroup, tabIndex)) { displayedDataFilesOut.push_back(fociFile); } } } /* * Might be NULLs so filter them out and return the results */ for (std::set::iterator iter = displayedDataFiles.begin(); iter != displayedDataFiles.end(); iter++) { CaretDataFile* cdf = *iter; if (cdf != NULL) { displayedDataFilesOut.push_back(cdf); } } } ViewingTransformations* BrowserTabContent::getViewingTransformation() { if (isVolumeSlicesDisplayed()) { return m_volumeSliceViewingTransformation; } else if (isFlatSurfaceDisplayed()) { return m_flatSurfaceViewingTransformation; } else if (isCerebellumDisplayed()) { return m_cerebellumViewingTransformation; } return m_viewingTransformation; } const ViewingTransformations* BrowserTabContent::getViewingTransformation() const { if (isVolumeSlicesDisplayed()) { return m_volumeSliceViewingTransformation; } else if (isFlatSurfaceDisplayed()) { return m_flatSurfaceViewingTransformation; } else if (isCerebellumDisplayed()) { return m_cerebellumViewingTransformation; } return m_viewingTransformation; } /** * @return The viewing translation. */ const float* BrowserTabContent::getTranslation() const { return getViewingTransformation()->getTranslation(); } /** * Get the viewing translation. * * @param translationOut * Translation values output. */ void BrowserTabContent::getTranslation(float translationOut[3]) const { return getViewingTransformation()->getTranslation(translationOut); } /** * Set the viewing translation. * * @param translation * New translation values. */ void BrowserTabContent::setTranslation( const float translation[3]) { getViewingTransformation()->setTranslation(translation); updateYokedBrowserTabs(); } /** * Set the viewing translation. * * @param translationX * New translation X-value. * @param translationY * New translation Y-value. * @param translationZ * New translation Z-value. */ void BrowserTabContent::setTranslation(const float translationX, const float translationY, const float translationZ) { getViewingTransformation()->setTranslation(translationX, translationY, translationZ); updateYokedBrowserTabs(); } /** * @return The viewing scaling. */ float BrowserTabContent::getScaling() const { return getViewingTransformation()->getScaling(); } /** * Set the viewing scaling. * @param scaling * New value for scaling. */ void BrowserTabContent::setScaling(const float scaling) { return getViewingTransformation()->setScaling(scaling); updateYokedBrowserTabs(); } /** * @return The rotation matrix. */ Matrix4x4 BrowserTabContent::getRotationMatrix() const { return getViewingTransformation()->getRotationMatrix(); } /** * Set the rotation matrix. * * @param rotationMatrix * The new rotation matrix. */ void BrowserTabContent::setRotationMatrix(const Matrix4x4& rotationMatrix) { getViewingTransformation()->setRotationMatrix(rotationMatrix); updateYokedBrowserTabs(); } /** * @return The oblique volume rotation matrix. */ Matrix4x4 BrowserTabContent::getObliqueVolumeRotationMatrix() const { return *m_obliqueVolumeRotationMatrix; } /** * Set the oblique rotation matrix. * * @param obliqueRotationMatrix * The new oblique rotation matrix. */ void BrowserTabContent::setObliqueVolumeRotationMatrix(const Matrix4x4& obliqueRotationMatrix) { *m_obliqueVolumeRotationMatrix = obliqueRotationMatrix; updateYokedBrowserTabs(); } /** * Get the offset for the right flat map offset. * * @param offsetX * Output with X-offset. * @param offsetY * Output with Y-offset. */ void BrowserTabContent::getRightCortexFlatMapOffset(float& offsetX, float& offsetY) const { getViewingTransformation()->getRightCortexFlatMapOffset(offsetX, offsetY); } /** * Set the offset for the right flat map offset. * * @param offsetX * X-offset. * @param offsetY * Y-offset. */ void BrowserTabContent::setRightCortexFlatMapOffset(const float offsetX, const float offsetY) { getViewingTransformation()->setRightCortexFlatMapOffset(offsetX, offsetY); updateYokedBrowserTabs(); } /** * @return The right cortex flat map zoom factor. */ float BrowserTabContent::getRightCortexFlatMapZoomFactor() const { return getViewingTransformation()->getRightCortexFlatMapZoomFactor(); } /** * Set the right cortex flat map zoom factor. * * @param zoomFactor * The zoom factor. */ void BrowserTabContent::setRightCortexFlatMapZoomFactor(const float zoomFactor) { getViewingTransformation()->setRightCortexFlatMapZoomFactor(zoomFactor); } /** * Reset the view to the default view. */ void BrowserTabContent::resetView() { getViewingTransformation()->resetView(); if (isVolumeSlicesDisplayed()) { m_obliqueVolumeRotationMatrix->identity(); } updateYokedBrowserTabs(); } /** * Set to a right side view. */ void BrowserTabContent::rightView() { if (isVolumeSlicesDisplayed()) { return; } getViewingTransformation()->rightView(); updateYokedBrowserTabs(); } /** * set to a left side view. */ void BrowserTabContent::leftView() { if (isVolumeSlicesDisplayed()) { return; } getViewingTransformation()->leftView(); updateYokedBrowserTabs(); } /** * set to a anterior view. */ void BrowserTabContent::anteriorView() { if (isVolumeSlicesDisplayed()) { return; } getViewingTransformation()->anteriorView(); updateYokedBrowserTabs(); } /** * set to a posterior view. */ void BrowserTabContent::posteriorView() { if (isVolumeSlicesDisplayed()) { return; } getViewingTransformation()->posteriorView(); updateYokedBrowserTabs(); } /** * set to a dorsal view. */ void BrowserTabContent::dorsalView() { if (isVolumeSlicesDisplayed()) { return; } getViewingTransformation()->dorsalView(); updateYokedBrowserTabs(); } /** * set to a ventral view. */ void BrowserTabContent::ventralView() { if (isVolumeSlicesDisplayed()) { 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; } /** * Apply mouse rotation to the displayed model. * * @param mousePressX * X coordinate of where mouse was pressed. * @param mousePressY * X coordinate of where mouse was pressed. * @param mouseX * X coordinate of mouse. * @param mouseY * Y coordinate of mouse. * @param mouseDeltaX * Change in mouse X coordinate. * @param mouseDeltaY * Change in mouse Y coordinate. */ void BrowserTabContent::applyMouseRotation(BrainOpenGLViewportContent* viewportContent, const int32_t mousePressX, const int32_t mousePressY, const int32_t mouseX, const int32_t mouseY, const int32_t mouseDeltaX, const int32_t mouseDeltaY) { if (isVolumeSlicesDisplayed()) { switch (getSliceProjectionType()) { case VolumeSliceProjectionTypeEnum::VOLUME_SLICE_PROJECTION_OBLIQUE: { int viewport[4]; viewportContent->getModelViewport(viewport); VolumeSliceViewPlaneEnum::Enum slicePlane = this->getSliceViewPlane(); int sliceViewport[4] = { viewport[0], viewport[1], viewport[2], viewport[3] }; if (slicePlane == VolumeSliceViewPlaneEnum::ALL) { slicePlane = getSliceViewPlaneForVolumeAllSliceView(viewport, mousePressX, mousePressY, sliceViewport); } Matrix4x4 rotationMatrix = getObliqueVolumeRotationMatrix(); if (slicePlane == VolumeSliceViewPlaneEnum::ALL) { rotationMatrix.rotateX(-mouseDeltaY); rotationMatrix.rotateY(mouseDeltaX); } else { if ((mouseDeltaX != 0) || (mouseDeltaY != 0)) { const int previousMouseX = mouseX - mouseDeltaX; const int previousMouseY = mouseY - mouseDeltaY; /* * Need to account for the quadrants!!!! */ const float viewportCenter[3] = { sliceViewport[0] + sliceViewport[2] / 2, sliceViewport[1] + sliceViewport[3] / 2, 0.0 }; const float oldPos[3] = { previousMouseX, previousMouseY, 0.0 }; const float newPos[3] = { mouseX, mouseY, 0.0 }; /* * Compute normal vector from viewport center to * old mouse position to new mouse position. * If normal-Z is positive, mouse has been moved * in a counter clockwise motion relative to center. * If normal-Z is negative, mouse has moved clockwise. */ float normalDirection[3]; MathFunctions::normalVectorDirection(viewportCenter, oldPos, newPos, normalDirection); bool isClockwise = false; bool isCounterClockwise = false; if (normalDirection[2] > 0.0) { isCounterClockwise = true; } else if (normalDirection[2] < 0.0) { isClockwise = true; } if (isClockwise || isCounterClockwise) { float mouseDelta = std::sqrt(static_cast((mouseDeltaX * mouseDeltaX) + (mouseDeltaY * mouseDeltaY))); // /* // * Rotation needs to be oppposite for newer // * oblique slice drawing for volumes that // * do not have a voxel corresponding to // * the origin. // */ // mouseDelta = -mouseDelta; switch (slicePlane) { case VolumeSliceViewPlaneEnum::ALL: { CaretAssert(0); } break; case VolumeSliceViewPlaneEnum::AXIAL: { Matrix4x4 rotation; if (isClockwise) { rotation.rotateZ(mouseDelta); } else if (isCounterClockwise) { rotation.rotateZ(-mouseDelta); } rotationMatrix.premultiply(rotation); } break; case VolumeSliceViewPlaneEnum::CORONAL: { Matrix4x4 rotation; if (isClockwise) { rotation.rotateY(-mouseDelta); } else if (isCounterClockwise) { rotation.rotateY(mouseDelta); } rotationMatrix.premultiply(rotation); } break; case VolumeSliceViewPlaneEnum::PARASAGITTAL: { Matrix4x4 rotation; if (isClockwise) { rotation.rotateX(-mouseDelta); } else if (isCounterClockwise) { rotation.rotateX(mouseDelta); } rotationMatrix.premultiply(rotation); } break; } } } } setObliqueVolumeRotationMatrix(rotationMatrix); } break; case VolumeSliceProjectionTypeEnum::VOLUME_SLICE_PROJECTION_ORTHOGONAL: break; /* Orthogonal olume slices are not rotated */ break; } } else if (isChartDisplayed()) { /* no rotation for chart */ } else if (isCerebellumDisplayed()) { const float screenDX = mouseDeltaX; const float screenDY = mouseDeltaY; float rotateDX = 0.0; float rotateDY = 0.0; float rotateDZ = 0.0; ModelSurfaceMontage* montageModel = getDisplayedSurfaceMontageModel(); if (montageModel != NULL) { std::vector montageViewports; montageModel->getSurfaceMontageViewportsForTransformation(getTabNumber(), montageViewports); bool foundMontageViewportFlag = false; const int32_t numViewports = static_cast(montageViewports.size()); for (int32_t ivp = 0; ivp < numViewports; ivp++) { const SurfaceMontageViewport* smv = montageViewports[ivp]; if (smv->isInside(mousePressX, mousePressY)) { switch (smv->getProjectionViewType()) { case ProjectionViewTypeEnum::PROJECTION_VIEW_CEREBELLUM_ANTERIOR: rotateDX = screenDY; rotateDZ = screenDX; foundMontageViewportFlag = true; break; case ProjectionViewTypeEnum::PROJECTION_VIEW_CEREBELLUM_DORSAL: rotateDX = -screenDY; rotateDY = screenDX; foundMontageViewportFlag = true; break; case ProjectionViewTypeEnum::PROJECTION_VIEW_CEREBELLUM_POSTERIOR: rotateDX = -screenDY; rotateDZ = screenDX; foundMontageViewportFlag = true; break; case ProjectionViewTypeEnum::PROJECTION_VIEW_CEREBELLUM_VENTRAL: rotateDX = -screenDY; rotateDY = -screenDX; foundMontageViewportFlag = true; break; case ProjectionViewTypeEnum::PROJECTION_VIEW_CEREBELLUM_FLAT_SURFACE: foundMontageViewportFlag = true; break; case ProjectionViewTypeEnum::PROJECTION_VIEW_LEFT_LATERAL: break; case ProjectionViewTypeEnum::PROJECTION_VIEW_LEFT_MEDIAL: break; case ProjectionViewTypeEnum::PROJECTION_VIEW_LEFT_FLAT_SURFACE: break; case ProjectionViewTypeEnum::PROJECTION_VIEW_RIGHT_LATERAL: break; case ProjectionViewTypeEnum::PROJECTION_VIEW_RIGHT_MEDIAL: break; case ProjectionViewTypeEnum::PROJECTION_VIEW_RIGHT_FLAT_SURFACE: break; } } if (foundMontageViewportFlag) { break; } } } else { rotateDX = -screenDY; rotateDY = screenDX; } Matrix4x4 rotationMatrix = m_cerebellumViewingTransformation->getRotationMatrix(); rotationMatrix.rotateX(rotateDX); rotationMatrix.rotateY(rotateDY); rotationMatrix.rotateZ(rotateDZ); m_cerebellumViewingTransformation->setRotationMatrix(rotationMatrix); } else { ViewingTransformations* viewingTransform = getViewingTransformation(); if (getProjectionViewType() == ProjectionViewTypeEnum::PROJECTION_VIEW_RIGHT_LATERAL) { Matrix4x4 rotationMatrix = viewingTransform->getRotationMatrix(); rotationMatrix.rotateX(-mouseDeltaY); rotationMatrix.rotateY(-mouseDeltaX); viewingTransform->setRotationMatrix(rotationMatrix); } else { float dx = mouseDeltaX; float dy = mouseDeltaY; ModelSurfaceMontage* montageModel = getDisplayedSurfaceMontageModel(); if (montageModel != NULL) { std::vector montageViewports; montageModel->getSurfaceMontageViewportsForTransformation(getTabNumber(), montageViewports); bool isValid = false; bool isFlat = false; bool isLeft = false; bool isLateral = true; const int32_t numViewports = static_cast(montageViewports.size()); for (int32_t ivp = 0; ivp < numViewports; ivp++) { const SurfaceMontageViewport* smv = montageViewports[ivp]; if (smv->isInside(mousePressX, mousePressY)) { switch (smv->getProjectionViewType()) { case ProjectionViewTypeEnum::PROJECTION_VIEW_CEREBELLUM_ANTERIOR: break; case ProjectionViewTypeEnum::PROJECTION_VIEW_CEREBELLUM_DORSAL: break; case ProjectionViewTypeEnum::PROJECTION_VIEW_CEREBELLUM_POSTERIOR: break; case ProjectionViewTypeEnum::PROJECTION_VIEW_CEREBELLUM_VENTRAL: break; case ProjectionViewTypeEnum::PROJECTION_VIEW_CEREBELLUM_FLAT_SURFACE: break; case ProjectionViewTypeEnum::PROJECTION_VIEW_LEFT_LATERAL: isLeft = true; isLateral = true; break; case ProjectionViewTypeEnum::PROJECTION_VIEW_LEFT_MEDIAL: isLeft = true; isLateral = false; break; case ProjectionViewTypeEnum::PROJECTION_VIEW_LEFT_FLAT_SURFACE: isLeft = true; isFlat = true; break; case ProjectionViewTypeEnum::PROJECTION_VIEW_RIGHT_LATERAL: isLeft = false; isLateral = true; break; case ProjectionViewTypeEnum::PROJECTION_VIEW_RIGHT_MEDIAL: isLeft = false; isLateral = false; break; case ProjectionViewTypeEnum::PROJECTION_VIEW_RIGHT_FLAT_SURFACE: isLeft = false; isFlat = true; break; } isValid = true; } if (isValid) { break; } } if (isFlat) { /* * No rotation. */ dx = 0.0; dy = 0.0; } else if (isValid) { if (isLeft == false) { dx = -dx; } if (isLateral == false) { dy = -dy; } } } Matrix4x4 rotationMatrix = viewingTransform->getRotationMatrix(); rotationMatrix.rotateX(-dy); rotationMatrix.rotateY(dx); viewingTransform->setRotationMatrix(rotationMatrix); } } updateYokedBrowserTabs(); } /** * Apply mouse scaling to the displayed model. * * @param mouseDX * Change in mouse X coordinate. * @param mouseDY * Change in mouse Y coordinate. */ void BrowserTabContent::applyMouseScaling(const int32_t /*mouseDX*/, const int32_t mouseDY) { if (isChartDisplayed()) { ModelChart* modelChart = getDisplayedChartModel(); CaretAssert(modelChart); CaretDataFileSelectionModel* matrixSelectionModel = NULL; if (modelChart->getSelectedChartDataType(m_tabNumber) == ChartDataTypeEnum::CHART_DATA_TYPE_MATRIX_LAYER) { matrixSelectionModel = modelChart->getChartableMatrixParcelFileSelectionModel(m_tabNumber); } if (modelChart->getSelectedChartDataType(m_tabNumber) == ChartDataTypeEnum::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); } } } else { float scaling = getViewingTransformation()->getScaling(); if (mouseDY != 0.0) { scaling *= (1.0f + (mouseDY * 0.01)); } if (scaling < 0.01) { scaling = 0.01; } getViewingTransformation()->setScaling(scaling); } updateYokedBrowserTabs(); } /** * Apply mouse translation to the displayed model. * * @param viewportContent * Content of the viewport. * @param mousePressX * X coordinate of where mouse was pressed. * @param mousePressY * X coordinate of where mouse was pressed. * @param mouseDX * Change in mouse X coordinate. * @param mouseDY * Change in mouse Y coordinate. */ void BrowserTabContent::applyMouseTranslation(BrainOpenGLViewportContent* viewportContent, const int32_t mousePressX, const int32_t mousePressY, const int32_t mouseDX, const int32_t mouseDY) { const int tabIndex = getTabNumber(); if (isVolumeSlicesDisplayed()) { const float volumeSliceScaling = m_volumeSliceViewingTransformation->getScaling(); ModelVolume* modelVolume = getDisplayedVolumeModel(); VolumeMappableInterface* vf = modelVolume->getUnderlayVolumeFile(tabIndex); BoundingBox mybox; vf->getVoxelSpaceBoundingBox(mybox); float cubesize = std::max(std::max(mybox.getDifferenceX(), mybox.getDifferenceY()), mybox.getDifferenceZ());//factor volume bounding box into slowdown for zoomed in float slowdown = 0.005f * cubesize / volumeSliceScaling;//when zoomed in, make the movements slower to match - still changes based on viewport currently slowdown = 1.0; float dx = 0.0; float dy = 0.0; float dz = 0.0; switch (this->getSliceViewPlane()) { 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; break; case VolumeSliceViewPlaneEnum::CORONAL: dx = mouseDX * slowdown; dz = mouseDY * slowdown; break; case VolumeSliceViewPlaneEnum::PARASAGITTAL: dy = -mouseDX * slowdown; dz = mouseDY * slowdown; break; } float translation[3]; m_volumeSliceViewingTransformation->getTranslation(translation); translation[0] += dx; translation[1] += dy; translation[2] += dz; m_volumeSliceViewingTransformation->setTranslation(translation); } else if (isChartDisplayed()) { ModelChart* modelChart = getDisplayedChartModel(); CaretAssert(modelChart); CaretDataFileSelectionModel* matrixSelectionModel = NULL; if (modelChart->getSelectedChartDataType(m_tabNumber) == ChartDataTypeEnum::CHART_DATA_TYPE_MATRIX_LAYER) { matrixSelectionModel = modelChart->getChartableMatrixParcelFileSelectionModel(m_tabNumber); } if (modelChart->getSelectedChartDataType(m_tabNumber) == ChartDataTypeEnum::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 translation[2]; matrixProperties->getViewPanning(translation); translation[0] += mouseDX; translation[1] += mouseDY; matrixProperties->setViewPanning(translation); } } } else if (isCerebellumDisplayed()) { const float screenDX = mouseDX; const float screenDY = mouseDY; float translateDX = 0.0; float translateDY = 0.0; float translateDZ = 0.0; ModelSurfaceMontage* montageModel = getDisplayedSurfaceMontageModel(); if (montageModel != NULL) { std::vector montageViewports; montageModel->getSurfaceMontageViewportsForTransformation(getTabNumber(), montageViewports); bool foundMontageViewportFlag = false; const int32_t numViewports = static_cast(montageViewports.size()); for (int32_t ivp = 0; ivp < numViewports; ivp++) { const SurfaceMontageViewport* smv = montageViewports[ivp]; if (smv->isInside(mousePressX, mousePressY)) { switch (smv->getProjectionViewType()) { case ProjectionViewTypeEnum::PROJECTION_VIEW_CEREBELLUM_ANTERIOR: translateDX = screenDX; translateDY = screenDY; foundMontageViewportFlag = true; break; case ProjectionViewTypeEnum::PROJECTION_VIEW_CEREBELLUM_DORSAL: translateDX = screenDX; translateDY = screenDY; foundMontageViewportFlag = true; break; case ProjectionViewTypeEnum::PROJECTION_VIEW_CEREBELLUM_POSTERIOR: translateDX = screenDX; translateDY = screenDY; foundMontageViewportFlag = true; break; case ProjectionViewTypeEnum::PROJECTION_VIEW_CEREBELLUM_VENTRAL: translateDX = screenDX; translateDY = screenDY; foundMontageViewportFlag = true; break; case ProjectionViewTypeEnum::PROJECTION_VIEW_CEREBELLUM_FLAT_SURFACE: translateDX = screenDX; translateDY = screenDY; foundMontageViewportFlag = true; break; case ProjectionViewTypeEnum::PROJECTION_VIEW_LEFT_LATERAL: break; case ProjectionViewTypeEnum::PROJECTION_VIEW_LEFT_MEDIAL: break; case ProjectionViewTypeEnum::PROJECTION_VIEW_LEFT_FLAT_SURFACE: break; case ProjectionViewTypeEnum::PROJECTION_VIEW_RIGHT_LATERAL: break; case ProjectionViewTypeEnum::PROJECTION_VIEW_RIGHT_MEDIAL: break; case ProjectionViewTypeEnum::PROJECTION_VIEW_RIGHT_FLAT_SURFACE: break; } } if (foundMontageViewportFlag) { break; } } } else { translateDX = screenDX; translateDY = screenDY; } float translation[3]; m_cerebellumViewingTransformation->getTranslation(translation); translation[0] += translateDX; translation[1] += translateDY; translation[2] += translateDZ; m_cerebellumViewingTransformation->setTranslation(translation); } else { float dx = mouseDX; float dy = mouseDY; ModelSurfaceMontage* montageModel = getDisplayedSurfaceMontageModel(); if (montageModel != NULL) { std::vector montageViewports; montageModel->getSurfaceMontageViewportsForTransformation(getTabNumber(), montageViewports); bool isValid = false; bool isLeft = true; bool isLateral = false; const int32_t numViewports = static_cast(montageViewports.size()); for (int32_t ivp = 0; ivp < numViewports; ivp++) { const SurfaceMontageViewport* smv = montageViewports[ivp]; if (smv->isInside(mousePressX, mousePressY)) { switch (smv->getProjectionViewType()) { case ProjectionViewTypeEnum::PROJECTION_VIEW_CEREBELLUM_ANTERIOR: break; case ProjectionViewTypeEnum::PROJECTION_VIEW_CEREBELLUM_DORSAL: break; case ProjectionViewTypeEnum::PROJECTION_VIEW_CEREBELLUM_POSTERIOR: break; case ProjectionViewTypeEnum::PROJECTION_VIEW_CEREBELLUM_VENTRAL: break; case ProjectionViewTypeEnum::PROJECTION_VIEW_CEREBELLUM_FLAT_SURFACE: break; case ProjectionViewTypeEnum::PROJECTION_VIEW_LEFT_LATERAL: isLeft = true; isLateral = true; break; case ProjectionViewTypeEnum::PROJECTION_VIEW_LEFT_MEDIAL: isLeft = true; isLateral = false; break; case ProjectionViewTypeEnum::PROJECTION_VIEW_LEFT_FLAT_SURFACE: isLeft = true; isLateral = true; break; case ProjectionViewTypeEnum::PROJECTION_VIEW_RIGHT_LATERAL: isLeft = false; isLateral = true; break; case ProjectionViewTypeEnum::PROJECTION_VIEW_RIGHT_MEDIAL: isLeft = false; isLateral = false; break; case ProjectionViewTypeEnum::PROJECTION_VIEW_RIGHT_FLAT_SURFACE: isLeft = false; isLateral = true; break; } isValid = true; } if (isValid) { break; } } if (isValid) { if (isLeft == false) { dx = -dx; } if (isLateral == false) { dx = -dx; } float translation[3]; getViewingTransformation()->getTranslation(translation); translation[0] += dx; translation[1] += dy; getViewingTransformation()->setTranslation(translation); } } else { if (getProjectionViewType() == ProjectionViewTypeEnum::PROJECTION_VIEW_RIGHT_LATERAL) { dx = -dx; } else if (getProjectionViewType() == ProjectionViewTypeEnum::PROJECTION_VIEW_RIGHT_FLAT_SURFACE) { dx = -dx; } float translation[3]; getViewingTransformation()->getTranslation(translation); translation[0] += dx; translation[1] += dy; getViewingTransformation()->setTranslation(translation); } } updateYokedBrowserTabs(); } /** * Get the transformations for drawing a model. * * @param projectionViewType * Type of projection view * @param translationOut * Translation * @param rotationMatrixOut * OpenGL rotation matrix. * @param scalingOut * Scaling. */ void BrowserTabContent::getTransformationsForOpenGLDrawing(const ProjectionViewTypeEnum::Enum projectionViewType, float translationOut[3], double rotationMatrixOut[16], float& scalingOut) const { /* * Check for volume slice viewing */ if (isVolumeSlicesDisplayed()) { m_volumeSliceViewingTransformation->getTranslation(translationOut); Matrix4x4 rotationMatrix = m_volumeSliceViewingTransformation->getRotationMatrix(); rotationMatrix.getMatrixForOpenGL(rotationMatrixOut); scalingOut = m_volumeSliceViewingTransformation->getScaling(); return; } /* * Surfaces may need a modification to the rotation matrix * dependent upon the projection view type. */ Matrix4x4 rotationMatrix = getViewingTransformation()->getRotationMatrix(); getViewingTransformation()->getTranslation(translationOut); scalingOut = getViewingTransformation()->getScaling(); double rotationX, rotationY, rotationZ; rotationMatrix.getRotation(rotationX, rotationY, rotationZ); const double rotationFlippedX = -rotationX; const double rotationFlippedY = 180.0 - rotationY; switch (projectionViewType) { case ProjectionViewTypeEnum::PROJECTION_VIEW_CEREBELLUM_ANTERIOR: { Matrix4x4 matrixOut = rotationMatrix; Matrix4x4 anteriorMatrix; anteriorMatrix.setRotation(90.0, 0.0, -180.0); matrixOut.postmultiply(anteriorMatrix); matrixOut.getMatrixForOpenGL(rotationMatrixOut); return; } break; case ProjectionViewTypeEnum::PROJECTION_VIEW_CEREBELLUM_DORSAL: break; case ProjectionViewTypeEnum::PROJECTION_VIEW_CEREBELLUM_POSTERIOR: { Matrix4x4 matrixOut = rotationMatrix; Matrix4x4 posteriorMatrix; posteriorMatrix.setRotation(-90.0, 0.0, 0.0); matrixOut.postmultiply(posteriorMatrix); matrixOut.getMatrixForOpenGL(rotationMatrixOut); return; } break; case ProjectionViewTypeEnum::PROJECTION_VIEW_CEREBELLUM_VENTRAL: { Matrix4x4 matrixOut = rotationMatrix; Matrix4x4 ventralMatrix; ventralMatrix.setRotation(0.0, 180.0, 180.0); matrixOut.postmultiply(ventralMatrix); matrixOut.getMatrixForOpenGL(rotationMatrixOut); return; } break; case ProjectionViewTypeEnum::PROJECTION_VIEW_CEREBELLUM_FLAT_SURFACE: rotationX = 0.0; rotationY = 0.0; rotationZ = 0.0; break; case ProjectionViewTypeEnum::PROJECTION_VIEW_LEFT_LATERAL: break; case ProjectionViewTypeEnum::PROJECTION_VIEW_LEFT_MEDIAL: break; case ProjectionViewTypeEnum::PROJECTION_VIEW_LEFT_FLAT_SURFACE: rotationX = 0.0; rotationY = 0.0; rotationZ = 0.0; break; case ProjectionViewTypeEnum::PROJECTION_VIEW_RIGHT_LATERAL: rotationX = rotationFlippedX; rotationY = rotationFlippedY; break; case ProjectionViewTypeEnum::PROJECTION_VIEW_RIGHT_MEDIAL: rotationX = rotationFlippedX; rotationY = rotationFlippedY; break; case ProjectionViewTypeEnum::PROJECTION_VIEW_RIGHT_FLAT_SURFACE: rotationX = 0.0; rotationY = 180.0; rotationZ = 0.0; break; } Matrix4x4 matrix; matrix.setRotation(rotationX, rotationY, rotationZ); matrix.getMatrixForOpenGL(rotationMatrixOut); } /** * Place the transformations for the given window tab into * the model transform. * @param windowTabNumber * Tab number for transformations. * @param modelTransform * Model transform into which transformations are loaded. */ void BrowserTabContent::getTransformationsInModelTransform(ModelTransform& modelTransform) const { modelTransform.setTranslation(getTranslation()); const Matrix4x4 rotMatrix = getRotationMatrix(); float m[4][4]; rotMatrix.getMatrix(m); modelTransform.setRotation(m); const Matrix4x4 obliqueRotationMatrix = getObliqueVolumeRotationMatrix(); float mob[4][4]; obliqueRotationMatrix.getMatrix(mob); modelTransform.setObliqueRotation(mob); float rightFlatX, rightFlatY; getRightCortexFlatMapOffset(rightFlatX, rightFlatY); modelTransform.setRightCortexFlatMapOffset(rightFlatX, rightFlatY); modelTransform.setRightCortexFlatMapZoomFactor(getRightCortexFlatMapZoomFactor()); modelTransform.setScaling(getScaling()); } /** * Apply the transformations to the browser tab. * @param modelTransform * Model transform into which transformations are retrieved. */ void BrowserTabContent::setTransformationsFromModelTransform(const ModelTransform& modelTransform) { float translation[3]; modelTransform.getTranslation(translation); const float tx = translation[0]; const float ty = translation[1]; const float tz = translation[2]; setTranslation(tx, ty, tz); float m[4][4]; modelTransform.getRotation(m); Matrix4x4 rotationMatrix; rotationMatrix.setMatrix(m); setRotationMatrix(rotationMatrix); float mob[4][4]; modelTransform.getObliqueRotation(mob); Matrix4x4 obliqueRotationMatrix; obliqueRotationMatrix.setMatrix(mob); setObliqueVolumeRotationMatrix(obliqueRotationMatrix); const float scale = modelTransform.getScaling(); setScaling(scale); float rightFlatX, rightFlatY; modelTransform.getRightCortexFlatMapOffset(rightFlatX, rightFlatY); setRightCortexFlatMapOffset(rightFlatX, rightFlatY); const float rightFlatZoom = modelTransform.getRightCortexFlatMapZoomFactor(); setRightCortexFlatMapZoomFactor(rightFlatZoom); updateYokedBrowserTabs(); } /** * Create a scene for an instance of a class. * * @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 the class' instance. * * @return Pointer to SceneClass object representing the state of * this object. Under some circumstances a NULL pointer may be * returned. Caller will take ownership of returned object. */ SceneClass* BrowserTabContent::saveToScene(const SceneAttributes* sceneAttributes, const AString& instanceName) { SceneClass* sceneClass = new SceneClass(instanceName, "BrowserTabContent", 6); // WB-491 Flat Fixes //5); // WB-576 //4); // WB-491, 1/28/2015 //3); // version 3 as of 4/22/2014 float obliqueMatrix[16]; m_obliqueVolumeRotationMatrix->getMatrixForOpenGL(obliqueMatrix); sceneClass->addFloatArray("m_obliqueVolumeRotationMatrix", obliqueMatrix, 16); m_sceneClassAssistant->saveMembers(sceneAttributes, sceneClass); return sceneClass; } /** * Restore the state of an instance of a class. * * @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. May be NULL for some types of scenes. */ void BrowserTabContent::restoreFromScene(const SceneAttributes* sceneAttributes, const SceneClass* sceneClass) { if (sceneClass == NULL) { return; } m_sceneClassAssistant->restoreMembers(sceneAttributes, sceneClass); m_obliqueVolumeRotationMatrix->identity(); float obliqueMatrix[16]; const int32_t numInObliqueArray = sceneClass->getFloatArrayValue("m_obliqueVolumeRotationMatrix", obliqueMatrix, 16); if (numInObliqueArray == 16) { m_obliqueVolumeRotationMatrix->setMatrixFromOpenGL(obliqueMatrix); } /* * In older version of workbench, transformation were stored in the * model for each tab, so try to restore them. */ if (sceneClass->getVersionNumber() < 2) { float translation[3]; float scaling; float rotationMatrix[4][4]; const Model* model = getModelForDisplay(); if (model != NULL) { const bool valid = model->getOldSceneTransformation(m_tabNumber, translation, scaling, rotationMatrix); if (valid) { setTranslation(translation); setScaling(scaling); Matrix4x4 m; m.setMatrix(rotationMatrix); ModelSurface* ms = getDisplayedSurfaceModel(); if (ms != NULL) { /* * Right hemispheres need rotations changed for * proper viewing. */ const StructureEnum::Enum structure = ms->getSurface()->getStructure(); if (StructureEnum::isRight(structure)) { double rotationX, rotationY, rotationZ; m.getRotation(rotationX, rotationY, rotationZ); //rotationX = -rotationX; //rotationY = 180.0 - rotationY; rotationY = 90 + rotationY; rotationZ = -rotationZ; m.identity(); m.setRotation(rotationX, rotationY, rotationZ); } } setRotationMatrix(m); } } } if (getDisplayedWholeBrainModel() != NULL) { /* * As of 19sep2013 whole brain and volume slice settings were merged * (whole brain slice settings removed). For compatibility, if a * whole brain model is being viewed and whole brain slice settings * are found, allow them to override volume slice settings. */ const SceneClass* wholeBrainVolumeSettings = sceneClass->getClass("m_wholeBrainSliceSettings"); if (wholeBrainVolumeSettings != NULL) { VolumeSliceSettings settings; settings.restoreFromScene(sceneAttributes, wholeBrainVolumeSettings); *m_volumeSliceSettings = settings; } } /** * Check for now obsolete clipping coordinate array. If found it is an * old scene so update the clipping planes. */ const SceneClassArray* oldClippingCoordinateClassArray = sceneClass->getClassArray("m_clippingCoordinate"); if (oldClippingCoordinateClassArray != NULL) { float clipCoords[3]; if (sceneClass->getFloatArrayValue("m_clippingCoordinate", clipCoords, 3) != 3) { clipCoords[0] = 0.0; clipCoords[1] = 0.0; clipCoords[2] = 0.0; } float clipThick[3]; if (sceneClass->getFloatArrayValue("m_clippingThickness", clipThick, 3) != 3) { clipThick[0] = 0.0; clipThick[1] = 0.0; clipThick[2] = 0.0; } bool clipEnabled[3]; if (sceneClass->getBooleanArrayValue("m_clippingEnabled", clipEnabled, 3) != 3) { clipEnabled[0] = false; clipEnabled[1] = false; clipEnabled[2] = false; } m_clippingPlaneGroup->resetToDefaultValues(); m_clippingPlaneGroup->setXAxisSelected(clipEnabled[0]); m_clippingPlaneGroup->setYAxisSelected(clipEnabled[1]); m_clippingPlaneGroup->setZAxisSelected(clipEnabled[2]); m_clippingPlaneGroup->setTranslation(clipCoords); m_clippingPlaneGroup->setThickness(clipThick); } /* * In older version of workbench, there was no flat surface * viewing transformation as it used the same transformations * as other surfaces. */ if (sceneClass->getVersionNumber() < 3) { *m_flatSurfaceViewingTransformation = *m_viewingTransformation; } /* * Prior to WB-491 surface drawing used the maximum dimension for * scaling to fit the window height and this was almost * always the Y-axis. This worked well when the default view was * a dorsal view with the anterior pole was at the top of the display * and the posterior pole at the bottom of the display. However * the default view was changed to be a lateral view so the surface * did not scale to fit the window and there were problems with the * surfaces scaling improperly when the overlay toolbox was changed * in height. * * This code adjusts the surface scaling for older scenes so that * older scenes are restored properly with the newer default scaling * for a lateral view. * * See also: BrainOpenGLFixedPipeline::setOrthographicProjectionForWithBoundingBox() */ if (sceneClass->getVersionNumber() < 4) { Surface* surface = NULL; switch (getSelectedModelType()) { case ModelTypeEnum::MODEL_TYPE_CHART: break; case ModelTypeEnum::MODEL_TYPE_INVALID: break; case ModelTypeEnum::MODEL_TYPE_SURFACE: { ModelSurface* modelSurface = getDisplayedSurfaceModel(); if (modelSurface != NULL) { surface = modelSurface->getSurface(); } } break; case ModelTypeEnum::MODEL_TYPE_SURFACE_MONTAGE: { ModelSurfaceMontage* modelMontage = getDisplayedSurfaceMontageModel(); if (modelMontage != NULL) { std::vector surfaceMontageViewports; modelMontage->getSurfaceMontageViewportsForDrawing(getTabNumber(), surfaceMontageViewports); for (std::vector::iterator iter = surfaceMontageViewports.begin(); iter != surfaceMontageViewports.end(); iter++) { SurfaceMontageViewport* smv = *iter; if (smv->getSurface() != NULL) { surface = smv->getSurface(); break; } } } } break; case ModelTypeEnum::MODEL_TYPE_VOLUME_SLICES: break; case ModelTypeEnum::MODEL_TYPE_WHOLE_BRAIN: { ModelWholeBrain* modelWholeBrain = getDisplayedWholeBrainModel(); if (modelWholeBrain != NULL) { std::vector allSurfaces = modelWholeBrain->getSelectedSurfaces(getTabNumber()); if ( ! allSurfaces.empty()) { surface = allSurfaces[0]; } } } break; } if (surface != NULL) { if (surface->getSurfaceType() != SurfaceTypeEnum::FLAT) { const BoundingBox* boundingBox = surface->getBoundingBox(); const float zDiff = boundingBox->getDifferenceZ(); const float maxDim = std::max(std::max(boundingBox->getDifferenceX(), boundingBox->getDifferenceY()), zDiff); if (zDiff > 0.0) { const float scaleAdjustment = zDiff / maxDim; //maxDim / zDiff; float scaling = getScaling(); scaling *= scaleAdjustment; setScaling(scaling); } } } } if (sceneClass->getVersionNumber() < 5) { if ( ! m_userName.isEmpty()) { /* * Prior to version 5 and WB-576, * the tab number was ALWAYS displayed. */ m_userName.insert(0, "(" + AString::number(m_tabNumber + 1) + ") "); } } if (sceneClass->getVersionNumber() < 6) { /* * WB-491 sets the viewport for flat surfaces to fit the * flat surface when viewing a flat montage */ const ModelSurfaceMontage* msm = getDisplayedSurfaceMontageModel(); if (msm != NULL) { const SurfaceMontageConfigurationAbstract* smc = msm->getSelectedConfiguration(m_tabNumber); if (smc != NULL) { if (smc->getConfigurationType() == SurfaceMontageConfigurationTypeEnum::FLAT_CONFIGURATION) { setScaling(1.0); } } } } } /** * Get the clipping planes enabled attributes * * @param xEnabled * X clipping plane enabled. * @param yEnabled * Y clipping plane enabled. * @param zEnabled * Z clipping plane enabled. * @param surfaceEnabled * Surface clipping enabled. * @param volumeEnabled * Volume clipping enabled. * @param featuresEnabled * Features enabled. * @param displayClippingBox * Display the clipping box. */ void BrowserTabContent::getClippingPlaneEnabled(bool& xEnabled, bool& yEnabled, bool& zEnabled, bool& surfaceEnabled, bool& volumeEnabled, bool& featuresEnabled) const { xEnabled = m_clippingPlaneGroup->isXAxisSelected(); yEnabled = m_clippingPlaneGroup->isYAxisSelected(); zEnabled = m_clippingPlaneGroup->isZAxisSelected(); surfaceEnabled = m_clippingPlaneGroup->isSurfaceSelected(); volumeEnabled = m_clippingPlaneGroup->isVolumeSelected(); featuresEnabled = m_clippingPlaneGroup->isFeaturesSelected(); } /** * Set the clipping planes enabled attributes * * @param xEnabled * X clipping plane enabled. * @param yEnabled * Y clipping plane enabled. * @param zEnabled * Z clipping plane enabled. * @param surfaceEnabled * Surface clipping enabled. * @param volumeEnabled * Volume clipping enabled. * @param featuresEnabled * Features enabled. * @param displayClippingBox * Display the clipping box. */ void BrowserTabContent::setClippingPlaneEnabled(const bool xEnabled, const bool yEnabled, const bool zEnabled, const bool surfaceEnabled, const bool volumeEnabled, const bool featuresEnabled) { m_clippingPlaneGroup->setXAxisSelected(xEnabled); m_clippingPlaneGroup->setYAxisSelected(yEnabled); m_clippingPlaneGroup->setZAxisSelected(zEnabled); m_clippingPlaneGroup->setSurfaceSelected(surfaceEnabled); m_clippingPlaneGroup->setVolumeSelected(volumeEnabled); m_clippingPlaneGroup->setFeaturesSelected(featuresEnabled); updateYokedBrowserTabs(); } /** * Get the clipping planes transformations. * * @param panning * Panning (translation) of the clipping planes. * @param rotation * Rotation of clipping planes. * @param thickness * Thickness of the clipping planes. */ void BrowserTabContent::getClippingPlaneTransformation(float panning[3], float rotation[3], float thickness[3], bool& displayClippingBox) const { m_clippingPlaneGroup->getTranslation(panning); m_clippingPlaneGroup->getRotationAngles(rotation); m_clippingPlaneGroup->getThickness(thickness); displayClippingBox = m_clippingPlaneGroup->isDisplayClippingBoxSelected(); } /** * Set the clipping planes transformations. * * @param panning * Panning (translation) of the clipping planes. * @param rotation * Rotation of clipping planes. * @param thickness * Thickness of the clipping planes. */ void BrowserTabContent::setClippingPlaneTransformation(const float panning[3], const float rotation[3], const float thickness[3], const bool displayClippingBox) { m_clippingPlaneGroup->setTranslation(panning); m_clippingPlaneGroup->setRotationAngles(rotation); m_clippingPlaneGroup->setThickness(thickness); m_clippingPlaneGroup->setDisplayClippingBoxSelected(displayClippingBox); updateYokedBrowserTabs(); } /** * Get the clipping plane group (const method). * * NOTE: Because of yoking, only a const instance of the clipping plane * group is available. To adjust the clipping planes use the methods * in this class so that yoking is properly updated. */ const ClippingPlaneGroup* BrowserTabContent::getClippingPlaneGroup() const { return m_clippingPlaneGroup; } /** * Reset the clipping plane transformations. */ void BrowserTabContent::resetClippingPlaneTransformation() { m_clippingPlaneGroup->resetTransformation(); updateYokedBrowserTabs(); } /** * @return the projection view type (view from left/right) */ ProjectionViewTypeEnum::Enum BrowserTabContent::getProjectionViewType() const { ProjectionViewTypeEnum::Enum projectionViewType = ProjectionViewTypeEnum::PROJECTION_VIEW_LEFT_LATERAL; const ModelSurface* modelSurface = getDisplayedSurfaceModel(); if (modelSurface != NULL) { const SurfaceFile* surfaceFile = modelSurface->getSurface(); if (surfaceFile != NULL) { const StructureEnum::Enum structure = surfaceFile->getStructure(); const SurfaceTypeEnum::Enum surfaceType = surfaceFile->getSurfaceType(); if (StructureEnum::isRight(structure)) { projectionViewType = ProjectionViewTypeEnum::PROJECTION_VIEW_RIGHT_LATERAL; if (surfaceType == SurfaceTypeEnum::FLAT) { projectionViewType = ProjectionViewTypeEnum::PROJECTION_VIEW_RIGHT_FLAT_SURFACE; } } else { projectionViewType = ProjectionViewTypeEnum::PROJECTION_VIEW_LEFT_LATERAL; if (surfaceType == SurfaceTypeEnum::FLAT) { projectionViewType = ProjectionViewTypeEnum::PROJECTION_VIEW_LEFT_FLAT_SURFACE; } } } } return projectionViewType; } /** * @return The slice view plane. * */ VolumeSliceViewPlaneEnum::Enum BrowserTabContent::getSliceViewPlane() const { return m_volumeSliceSettings->getSliceViewPlane(); } /** * Set the slice view plane. * @param windowTabNumber * New value for slice plane. */ void BrowserTabContent::setSliceViewPlane(const VolumeSliceViewPlaneEnum::Enum slicePlane) { m_volumeSliceSettings->setSliceViewPlane(slicePlane); updateYokedBrowserTabs(); } /** * @return Type of slice drawing (single/montage) */ VolumeSliceDrawingTypeEnum::Enum BrowserTabContent::getSliceDrawingType() const { return m_volumeSliceSettings->getSliceDrawingType(); } /** * Set type of slice drawing (single/montage) * * @param sliceDrawingType * New value for slice drawing type. */ void BrowserTabContent::setSliceDrawingType(const VolumeSliceDrawingTypeEnum::Enum sliceDrawingType) { m_volumeSliceSettings->setSliceDrawingType(sliceDrawingType); updateYokedBrowserTabs(); } /** * @return Type of slice projection (oblique/orthogonal) */ VolumeSliceProjectionTypeEnum::Enum BrowserTabContent::getSliceProjectionType() const { return m_volumeSliceSettings->getSliceProjectionType(); } /** * Set type of slice projection (oblique/orthogonal) * * @param sliceProjectionType * New value for slice projection type. */ void BrowserTabContent::setSliceProjectionType(const VolumeSliceProjectionTypeEnum::Enum sliceProjectionType) { m_volumeSliceSettings->setSliceProjectionType(sliceProjectionType); updateYokedBrowserTabs(); } /** * @return the montage number of columns for the given window tab. */ int32_t BrowserTabContent::getMontageNumberOfColumns() const { return m_volumeSliceSettings->getMontageNumberOfColumns(); } /** * Set the montage number of columns in the given window tab. * @param montageNumberOfColumns * New value for montage number of columns */ void BrowserTabContent::setMontageNumberOfColumns(const int32_t montageNumberOfColumns) { m_volumeSliceSettings->setMontageNumberOfColumns(montageNumberOfColumns); updateYokedBrowserTabs(); } /** * @return the montage number of rows for the given window tab. */ int32_t BrowserTabContent::getMontageNumberOfRows() const { return m_volumeSliceSettings->getMontageNumberOfRows(); } /** * Set the montage number of rows. * @param montageNumberOfRows * New value for montage number of rows */ void BrowserTabContent::setMontageNumberOfRows(const int32_t montageNumberOfRows) { m_volumeSliceSettings->setMontageNumberOfRows(montageNumberOfRows); updateYokedBrowserTabs(); } /** * @return the montage slice spacing. */ int32_t BrowserTabContent::getMontageSliceSpacing() const { return m_volumeSliceSettings->getMontageSliceSpacing(); } /** * Set the montage slice spacing. * @param montageSliceSpacing * New value for montage slice spacing */ void BrowserTabContent::setMontageSliceSpacing(const int32_t montageSliceSpacing) { m_volumeSliceSettings->setMontageSliceSpacing(montageSliceSpacing); updateYokedBrowserTabs(); } /** * Set the selected slices to the origin. */ void BrowserTabContent::setSlicesToOrigin() { selectSlicesAtOrigin(); updateYokedBrowserTabs(); } /** * Reset the slices. */ void BrowserTabContent::reset() { if (isVolumeSlicesDisplayed() || isWholeBrainDisplayed()) { m_volumeSliceSettings->reset(); m_obliqueVolumeRotationMatrix->identity(); } updateYokedBrowserTabs(); } /** * Update the slices coordinates so that they are valid for * the given VolumeFile. * @param volumeFile * File for which slice coordinates are made valid. */ void BrowserTabContent::updateForVolumeFile(const VolumeMappableInterface* volumeFile) { m_volumeSliceSettings->updateForVolumeFile(volumeFile); } /** * Set the slice indices so that they are at the origin. */ void BrowserTabContent::selectSlicesAtOrigin() { m_volumeSliceSettings->selectSlicesAtOrigin(); updateYokedBrowserTabs(); } /** * Set the selected slices to the given coordinate. * @param xyz * Coordinate for selected slices. */ void BrowserTabContent::selectSlicesAtCoordinate(const float xyz[3]) { m_volumeSliceSettings->selectSlicesAtCoordinate(xyz); updateYokedBrowserTabs(); } /** * If true, selected volume slices in tab move to location * of the identification operation. */ bool BrowserTabContent::isIdentificationUpdatesVolumeSlices() const { return m_identificationUpdatesVolumeSlices; } /** * Update selected volume slices in tab move to location * of the identification operation. * * @param status * New status. */ void BrowserTabContent::setIdentificationUpdatesVolumeSlices(const bool status) { m_identificationUpdatesVolumeSlices = status; updateYokedBrowserTabs(); } /** * Return the axial slice index. * @return * Axial slice index or negative if invalid */ int64_t BrowserTabContent::getSliceIndexAxial(const VolumeMappableInterface* volumeFile) const { return m_volumeSliceSettings->getSliceIndexAxial(volumeFile); } /** * Set the axial slice index. * @param * New value for axial slice index. */ void BrowserTabContent::setSliceIndexAxial(const VolumeMappableInterface* volumeFile, const int64_t sliceIndexAxial) { m_volumeSliceSettings->setSliceIndexAxial(volumeFile, sliceIndexAxial); updateYokedBrowserTabs(); } /** * Return the coronal slice index. * @return * Coronal slice index. */ int64_t BrowserTabContent::getSliceIndexCoronal(const VolumeMappableInterface* volumeFile) const { return m_volumeSliceSettings->getSliceIndexCoronal(volumeFile); } /** * Set the coronal slice index. * @param sliceIndexCoronal * New value for coronal slice index. */ void BrowserTabContent::setSliceIndexCoronal(const VolumeMappableInterface* volumeFile, const int64_t sliceIndexCoronal) { m_volumeSliceSettings->setSliceIndexCoronal(volumeFile, sliceIndexCoronal); updateYokedBrowserTabs(); } /** * Return the parasagittal slice index. * @return * Parasagittal slice index. */ int64_t BrowserTabContent::getSliceIndexParasagittal(const VolumeMappableInterface* volumeFile) const { return m_volumeSliceSettings->getSliceIndexParasagittal(volumeFile); } /** * Set the parasagittal slice index. * @param sliceIndexParasagittal * New value for parasagittal slice index. */ void BrowserTabContent::setSliceIndexParasagittal(const VolumeMappableInterface* volumeFile, const int64_t sliceIndexParasagittal) { m_volumeSliceSettings->setSliceIndexParasagittal(volumeFile, sliceIndexParasagittal); updateYokedBrowserTabs(); } /** * @return Coordinate of axial slice. */ float BrowserTabContent::getSliceCoordinateAxial() const { return m_volumeSliceSettings->getSliceCoordinateAxial(); } /** * Set the coordinate for the axial slice. * @param z * Z-coordinate for axial slice. */ void BrowserTabContent::setSliceCoordinateAxial(const float z) { m_volumeSliceSettings->setSliceCoordinateAxial(z); updateYokedBrowserTabs(); } /** * @return Coordinate of coronal slice. */ float BrowserTabContent::getSliceCoordinateCoronal() const { return m_volumeSliceSettings->getSliceCoordinateCoronal(); } /** * Set the coordinate for the coronal slice. * @param y * Y-coordinate for coronal slice. */ void BrowserTabContent::setSliceCoordinateCoronal(const float y) { m_volumeSliceSettings->setSliceCoordinateCoronal(y); updateYokedBrowserTabs(); } /** * @return Coordinate of parasagittal slice. */ float BrowserTabContent::getSliceCoordinateParasagittal() const { return m_volumeSliceSettings->getSliceCoordinateParasagittal(); } /** * Set the coordinate for the parasagittal slice. * @param x * X-coordinate for parasagittal slice. */ void BrowserTabContent::setSliceCoordinateParasagittal(const float x) { m_volumeSliceSettings->setSliceCoordinateParasagittal(x); updateYokedBrowserTabs(); } /** * Is the parasagittal slice enabled? * @return * Enabled status of parasagittal slice. */ bool BrowserTabContent::isSliceParasagittalEnabled() const { return m_volumeSliceSettings->isSliceParasagittalEnabled(); } /** * Set the enabled status of the parasagittal slice. * @param sliceEnabledParasagittal * New enabled status. */ void BrowserTabContent::setSliceParasagittalEnabled(const bool sliceEnabledParasagittal) { m_volumeSliceSettings->setSliceParasagittalEnabled(sliceEnabledParasagittal); updateYokedBrowserTabs(); } /** * Is the coronal slice enabled? * @return * Enabled status of coronal slice. */ bool BrowserTabContent::isSliceCoronalEnabled() const { return m_volumeSliceSettings->isSliceCoronalEnabled(); } /** * Set the enabled status of the coronal slice. * @param sliceEnabledCoronal * New enabled status. */ void BrowserTabContent::setSliceCoronalEnabled(const bool sliceEnabledCoronal) { m_volumeSliceSettings->setSliceCoronalEnabled(sliceEnabledCoronal); updateYokedBrowserTabs(); } /** * Is the axial slice enabled? * @return * Enabled status of axial slice. */ bool BrowserTabContent::isSliceAxialEnabled() const { return m_volumeSliceSettings->isSliceAxialEnabled(); } /** * Set the enabled status of the axial slice. * @param sliceEnabledAxial * New enabled status. */ void BrowserTabContent::setSliceAxialEnabled(const bool sliceEnabledAxial) { m_volumeSliceSettings->setSliceAxialEnabled(sliceEnabledAxial); updateYokedBrowserTabs(); } /** * @return Enabled status for left cerebral cortex. */ bool BrowserTabContent::isWholeBrainLeftEnabled() const { return m_wholeBrainSurfaceSettings->isLeftEnabled(); } /** * Set the enabled status for the left hemisphere. * @param windowTabNumber * Index of window tab. * @param enabled * New enabled status. */ void BrowserTabContent::setWholeBrainLeftEnabled(const bool enabled) { m_wholeBrainSurfaceSettings->setLeftEnabled(enabled); updateYokedBrowserTabs(); } /** * @return Enabled status for right cerebral cortex. */ bool BrowserTabContent::isWholeBrainRightEnabled() const { return m_wholeBrainSurfaceSettings->isRightEnabled(); } /** * Set the enabled status for the right hemisphere. * @param enabled * New enabled status. */ void BrowserTabContent::setWholeBrainRightEnabled(const bool enabled) { m_wholeBrainSurfaceSettings->setRightEnabled(enabled); updateYokedBrowserTabs(); } /** * @return Enabled status for cerebellum. */ bool BrowserTabContent::isWholeBrainCerebellumEnabled() const { return m_wholeBrainSurfaceSettings->isCerebellumEnabled(); } /** * Set the enabled status for the cerebellum. * @param enabled * New enabled status. */ void BrowserTabContent::setWholeBrainCerebellumEnabled(const bool enabled) { m_wholeBrainSurfaceSettings->setCerebellumEnabled(enabled); updateYokedBrowserTabs(); } /** * @return The separation between the left and right surfaces. */ float BrowserTabContent::getWholeBrainLeftRightSeparation() const { return m_wholeBrainSurfaceSettings->getLeftRightSeparation(); } /** * Set the separation between the cerebellum and the left/right surfaces. * @param separation * New value for separation. */ void BrowserTabContent::setWholeBrainLeftRightSeparation(const float separation) { m_wholeBrainSurfaceSettings->setLeftRightSeparation(separation); updateYokedBrowserTabs(); } /** * @return The separation between the left/right surfaces. */ float BrowserTabContent::getWholeBrainCerebellumSeparation() const { return m_wholeBrainSurfaceSettings->getCerebellumSeparation(); } /** * Set the separation between the cerebellum and the left and right surfaces. * @param separation * New value for separation. */ void BrowserTabContent::setWholeBrainCerebellumSeparation(const float separation) { m_wholeBrainSurfaceSettings->setCerebellumSeparation(separation); updateYokedBrowserTabs(); } /** * @return Selected yoking group. */ YokingGroupEnum::Enum BrowserTabContent::getYokingGroup() const { return m_yokingGroup; } /** * Set the selected yoking group. * * @param yokingGroup * New value for yoking group. */ void BrowserTabContent::setYokingGroup(const YokingGroupEnum::Enum yokingGroup) { m_yokingGroup = yokingGroup; if (m_yokingGroup == YokingGroupEnum::YOKING_GROUP_OFF) { return; } /* * 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->getYokingGroup() == m_yokingGroup) { /* * If anything is added, also need to update updateYokedBrowserTabs() */ *m_viewingTransformation = *btc->m_viewingTransformation; *m_flatSurfaceViewingTransformation = *btc->m_flatSurfaceViewingTransformation; *m_cerebellumViewingTransformation = *btc->m_cerebellumViewingTransformation; *m_volumeSliceViewingTransformation = *btc->m_volumeSliceViewingTransformation; *m_volumeSliceSettings = *btc->m_volumeSliceSettings; *m_obliqueVolumeRotationMatrix = *btc->m_obliqueVolumeRotationMatrix; *m_clippingPlaneGroup = *btc->m_clippingPlaneGroup; m_identificationUpdatesVolumeSlices = btc->m_identificationUpdatesVolumeSlices; break; } } } } /** * @return Is this browser tab yoked? */ bool BrowserTabContent::isYoked() const { const bool yoked = (m_yokingGroup != YokingGroupEnum::YOKING_GROUP_OFF); return yoked; } /** * Update other browser tabs with yoked data. */ void BrowserTabContent::updateYokedBrowserTabs() { if (isExecutingConstructor) { return; } if (m_yokingGroup == 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->getYokingGroup() == m_yokingGroup) { *btc->m_viewingTransformation = *m_viewingTransformation; *btc->m_flatSurfaceViewingTransformation = *m_flatSurfaceViewingTransformation; *btc->m_cerebellumViewingTransformation = *m_cerebellumViewingTransformation; *btc->m_volumeSliceViewingTransformation = *m_volumeSliceViewingTransformation; *btc->m_volumeSliceSettings = *m_volumeSliceSettings; *btc->m_obliqueVolumeRotationMatrix = *m_obliqueVolumeRotationMatrix; *btc->m_clippingPlaneGroup = *m_clippingPlaneGroup; btc->m_identificationUpdatesVolumeSlices = m_identificationUpdatesVolumeSlices; } } } } connectome-workbench-1.2.3+git41-gc4c6c90/src/Brain/BrowserTabContent.h000066400000000000000000000434361300200146000253670ustar00rootroot00000000000000#ifndef __BROWSER_TAB_CONTENT__H_ #define __BROWSER_TAB_CONTENT__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 "CaretObject.h" #include "EventListenerInterface.h" #include "Model.h" #include "ModelTypeEnum.h" #include "Plane.h" #include "ProjectionViewTypeEnum.h" #include "SceneableInterface.h" #include "StructureEnum.h" #include "VolumeSliceDrawingTypeEnum.h" #include "VolumeSliceProjectionTypeEnum.h" #include "VolumeSliceViewPlaneEnum.h" #include "YokingGroupEnum.h" namespace caret { class AnnotationColorBar; class BrainOpenGLViewportContent; class CaretDataFile; class CaretMappableDataFile; class ClippingPlaneGroup; class Matrix4x4; class ModelChart; class ModelSurface; class ModelSurfaceMontage; class ModelSurfaceSelector; class ModelTransform; class ModelVolume; class ModelWholeBrain; class OverlaySet; class Palette; class PlainTextStringBuilder; class SceneClassAssistant; class Surface; class ViewingTransformations; class ViewingTransformationsCerebellum; class ViewingTransformationsVolume; class VolumeMappableInterface; class VolumeSliceSettings; class VolumeSurfaceOutlineSetModel; class WholeBrainSurfaceSettings; /// Maintains content in a brower's tab class BrowserTabContent : public CaretObject, public EventListenerInterface, public SceneableInterface { public: BrowserTabContent(const int32_t tabNumber); virtual ~BrowserTabContent(); void cloneBrowserTabContent(BrowserTabContent* tabToClone); virtual void receiveEvent(Event* event); virtual void getDescriptionOfContent(PlainTextStringBuilder& descriptionOut) const; virtual AString toString() const; AString getName() const; //void setGuiName(const AString& name); AString getUserName() const; void setUserName(const AString& userName); OverlaySet* getOverlaySet(); const OverlaySet* getOverlaySet() const; int32_t getTabNumber() const; ModelTypeEnum::Enum getSelectedModelType() const; void setSelectedModelType(ModelTypeEnum::Enum selectedModelType); const Model* getModelForDisplay() const; Model* getModelForDisplay(); ModelChart* getDisplayedChartModel(); const ModelChart* getDisplayedChartModel() const; ModelSurface* getDisplayedSurfaceModel(); const ModelSurface* getDisplayedSurfaceModel() const; ModelVolume* getDisplayedVolumeModel(); const ModelVolume* getDisplayedVolumeModel() const; ModelWholeBrain* getDisplayedWholeBrainModel(); ModelSurfaceMontage* getDisplayedSurfaceMontageModel(); const ModelSurfaceMontage* getDisplayedSurfaceMontageModel() const; const std::vector getAllSurfaceModels() const; ModelSurfaceSelector* getSurfaceModelSelector(); std::vector getSurfaceStructuresDisplayed(); bool isCerebellumDisplayed() const; bool isChartDisplayed() const; bool isFlatSurfaceDisplayed() const; bool isVolumeSlicesDisplayed() const; bool isWholeBrainDisplayed() const; void getFilesDisplayedInTab(std::vector& displayedDataFilesOut); void update(const std::vector models); bool isChartModelValid() const; bool isSurfaceModelValid() const; bool isVolumeSliceModelValid() const; bool isWholeBrainModelValid() const; bool isSurfaceMontageModelValid() const; void getAnnotationColorBars(std::vector& colorBarsOut); void getDisplayedPaletteMapFiles(std::vector& mapFiles, std::vector& mapIndices); VolumeSurfaceOutlineSetModel* getVolumeSurfaceOutlineSet(); const VolumeSurfaceOutlineSetModel* getVolumeSurfaceOutlineSet() const; bool isAspectRatioLocked() const; void setAspectRatioLocked(const bool locked); float getAspectRatio() const; void setAspectRatio(const float aspectRatio); void getClippingPlaneEnabled(bool& xEnabled, bool& yEnabled, bool& zEnabled, bool& surfaceEnabled, bool& volumeEnabled, bool& featuresEnabled) const; void setClippingPlaneEnabled(const bool xEnabled, const bool yEnabled, const bool zEnabled, const bool surfaceEnabled, const bool volumeEnabled, const bool featuresEnabled); void getClippingPlaneTransformation(float panning[3], float rotation[3], float thickness[3], bool& displayClippingBox) const; void setClippingPlaneTransformation(const float panning[3], const float rotation[3], const float thickness[3], const bool displayClippingBox); const ClippingPlaneGroup* getClippingPlaneGroup() const; void resetClippingPlaneTransformation(); const float* getTranslation() const; void getTranslation(float translationOut[3]) const; void setTranslation( const float translation[3]); void setTranslation(const float translationX, const float translationY, const float translationZ); float getScaling() const; void setScaling(const float scaling); Matrix4x4 getRotationMatrix() const; void setRotationMatrix(const Matrix4x4& rotationMatrix); Matrix4x4 getObliqueVolumeRotationMatrix() const; void setObliqueVolumeRotationMatrix(const Matrix4x4& obliqueRotationMatrix); void getRightCortexFlatMapOffset(float& offsetX, float& offsetY) const; void setRightCortexFlatMapOffset(const float offsetX, const float offsetY); float getRightCortexFlatMapZoomFactor() const; void setRightCortexFlatMapZoomFactor(const float zoomFactor); ProjectionViewTypeEnum::Enum getProjectionViewType() const; void resetView(); void rightView(); void leftView(); void anteriorView(); void posteriorView(); void dorsalView(); void ventralView(); void applyMouseRotation(BrainOpenGLViewportContent* viewportContent, const int32_t mousePressX, const int32_t mousePressY, const int32_t mouseX, const int32_t mouseY, const int32_t mouseDeltaX, const int32_t mouseDeltaY); void applyMouseScaling(const int32_t mouseDX, const int32_t mouseDY); void applyMouseTranslation(BrainOpenGLViewportContent* viewportContent, const int32_t mousePressX, const int32_t mousePressY, const int32_t mouseDX, const int32_t mouseDY); void getTransformationsForOpenGLDrawing(const ProjectionViewTypeEnum::Enum projectionViewType, float translationOut[3], double rotationMatrixOut[16], float& scalingOut) const; void getTransformationsInModelTransform(ModelTransform& modelTransform) const; void setTransformationsFromModelTransform(const ModelTransform& modelTransform); VolumeSliceViewPlaneEnum::Enum getSliceViewPlane() const; void setSliceViewPlane(VolumeSliceViewPlaneEnum::Enum sliceAxisMode); VolumeSliceDrawingTypeEnum::Enum getSliceDrawingType() const; void setSliceDrawingType(const VolumeSliceDrawingTypeEnum::Enum sliceDrawingType); VolumeSliceProjectionTypeEnum::Enum getSliceProjectionType() const; void setSliceProjectionType(const VolumeSliceProjectionTypeEnum::Enum sliceProjectionType); int32_t getMontageNumberOfColumns() const; void setMontageNumberOfColumns(const int32_t montageNumberOfColumns); int32_t getMontageNumberOfRows() const; void setMontageNumberOfRows(const int32_t montageNumberOfRows); int32_t getMontageSliceSpacing() const; void setMontageSliceSpacing(const int32_t montageSliceSpacing); void setSlicesToOrigin(); float getSliceCoordinateAxial() const; void setSliceCoordinateAxial(const float x); float getSliceCoordinateCoronal() const; void setSliceCoordinateCoronal(const float y); float getSliceCoordinateParasagittal() const; void setSliceCoordinateParasagittal(const float z); int64_t getSliceIndexAxial(const VolumeMappableInterface* volumeFile) const; void setSliceIndexAxial(const VolumeMappableInterface* volumeFile, const int64_t sliceIndexAxial); int64_t getSliceIndexCoronal(const VolumeMappableInterface* volumeFile) const; void setSliceIndexCoronal(const VolumeMappableInterface* volumeFile, const int64_t sliceIndexCoronal); int64_t getSliceIndexParasagittal(const VolumeMappableInterface* volumeFile) const; void setSliceIndexParasagittal(const VolumeMappableInterface* volumeFile, const int64_t sliceIndexParasagittal); bool isSliceParasagittalEnabled() const; void setSliceParasagittalEnabled(const bool sliceEnabledParasagittal); bool isSliceCoronalEnabled() const; void setSliceCoronalEnabled(const bool sliceEnabledCoronal); bool isSliceAxialEnabled() const; void setSliceAxialEnabled(const bool sliceEnabledAxial); void updateForVolumeFile(const VolumeMappableInterface* volumeFile); void selectSlicesAtOrigin(); void selectSlicesAtCoordinate(const float xyz[3]); bool isIdentificationUpdatesVolumeSlices() const; void setIdentificationUpdatesVolumeSlices(const bool status); void reset(); void updateYokedBrowserTabs(); bool isYoked() const; YokingGroupEnum::Enum getYokingGroup() const; void setYokingGroup(const YokingGroupEnum::Enum yokingType); bool isWholeBrainLeftEnabled() const; void setWholeBrainLeftEnabled(const bool enabled); bool isWholeBrainRightEnabled() const; void setWholeBrainRightEnabled(const bool enabled); bool isWholeBrainCerebellumEnabled() const; void setWholeBrainCerebellumEnabled(const bool enabled); float getWholeBrainLeftRightSeparation() const; void setWholeBrainLeftRightSeparation(const float separation); float getWholeBrainCerebellumSeparation() const; void setWholeBrainCerebellumSeparation(const float separation); virtual SceneClass* saveToScene(const SceneAttributes* sceneAttributes, const AString& instanceName); virtual void restoreFromScene(const SceneAttributes* sceneAttributes, const SceneClass* sceneClass); private: class ColorBarFileMap { public: ColorBarFileMap(AnnotationColorBar* colorBar, CaretMappableDataFile* mapFile, const int32_t mapIndex) : m_colorBar(colorBar), m_mapFile(mapFile), m_mapIndex(mapIndex) { } AnnotationColorBar* m_colorBar; CaretMappableDataFile* m_mapFile; int32_t m_mapIndex; }; BrowserTabContent(const BrowserTabContent&); BrowserTabContent& operator=(const BrowserTabContent&); 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; AString getDefaultName() const; /** Number of this tab */ int32_t m_tabNumber; /** Selected surface model */ ModelSurfaceSelector* m_surfaceModelSelector; /** Selected model type */ ModelTypeEnum::Enum m_selectedModelType; /** All surface models */ std::vector m_allSurfaceModels; /** The volume model */ ModelVolume* m_volumeModel; /** The whole brain model */ ModelWholeBrain* m_wholeBrainModel; /** The surface montage model */ ModelSurfaceMontage* m_surfaceMontageModel; /** The chart model */ ModelChart* m_chartModel; /** * Name requested by user interface - reflects contents * such as Surface, Volume Slices, etc */ AString m_guiName; /** * User can set the name of the tab. */ AString m_userName; /** * Clipping planes */ ClippingPlaneGroup* m_clippingPlaneGroup; /** * Rotation matrix for oblique volume viewing */ Matrix4x4* m_obliqueVolumeRotationMatrix; /** Yoking group */ YokingGroupEnum::Enum m_yokingGroup; /** Volume Surface Outlines */ VolumeSurfaceOutlineSetModel* m_volumeSurfaceOutlineSetModel; /** Assists with creating/restoring scenes */ SceneClassAssistant* m_sceneClassAssistant; /** Transformation for cerebellum viewing */ ViewingTransformationsCerebellum* m_cerebellumViewingTransformation; /** Transformation for surface/all viewing */ ViewingTransformations* m_viewingTransformation; /** Transformation for surface/all viewing */ ViewingTransformations* m_flatSurfaceViewingTransformation; /** Transformation for volume slices viewing */ ViewingTransformationsVolume* m_volumeSliceViewingTransformation; /** Volume slice settings for volume slices */ VolumeSliceSettings* m_volumeSliceSettings; /** Whole brain surface settings. */ WholeBrainSurfaceSettings* m_wholeBrainSurfaceSettings; /** aspect ratio */ float m_aspectRatio; /** aspect ratio locked */ bool m_aspectRatioLocked; /** * If true, selected volume slices in tab move to location * of the identification operation. */ bool m_identificationUpdatesVolumeSlices; /* * True if constructing an instance */ bool isExecutingConstructor; /** Contains all active browser tab content instances */ static std::set s_allBrowserTabContent; }; #ifdef __BROWSER_TAB_CONTENT_DECLARE__ std::set BrowserTabContent::s_allBrowserTabContent; #endif // __BROWSER_TAB_CONTENT_DECLARE__ } // namespace #endif //__BROWSER_TAB_CONTENT__H_ connectome-workbench-1.2.3+git41-gc4c6c90/src/Brain/CMakeLists.txt000066400000000000000000000214321300200146000243410ustar00rootroot00000000000000# # Name of project # PROJECT (Brain) # # Need XML from Qt # 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) # # Create the brain library # ADD_LIBRARY(Brain AnnotationArrangerExecutor.h AnnotationArrangerInputs.h AnnotationManager.h BorderDrawingTypeEnum.h Brain.h BrainOpenGL.h BrainOpenGLAnnotationDrawingFixedPipeline.h BrainOpenGLChartDrawingInterface.h BrainOpenGLChartDrawingFixedPipeline.h BrainOpenGLFixedPipeline.h BrainOpenGLPrimitiveDrawing.h BrainOpenGLShape.h BrainOpenGLShapeCone.h BrainOpenGLShapeCube.h BrainOpenGLShapeCylinder.h BrainOpenGLShapeRing.h BrainOpenGLShapeRingOutline.h BrainOpenGLShapeSphere.h BrainOpenGLTextRenderInterface.h BrainOpenGLTextureManager.h BrainOpenGLViewportContent.h BrainOpenGLVolumeObliqueSliceDrawing.h BrainOpenGLVolumeSliceDrawing.h BrainStructure.h BrainStructureNodeAttributes.h BrowserTabContent.h CaretDataFileSelectionModel.h CaretMappableDataFileAndMapSelectionModel.h CaretOpenGLInclude.h ChartingDataManager.h CiftiConnectivityMatrixDataFileManager.h CiftiFiberTrajectoryManager.h ClippingPlaneGroup.h DisplayProperties.h DisplayPropertiesAnnotation.h DisplayPropertiesBorders.h DisplayPropertiesFiberOrientation.h DisplayPropertiesFoci.h DisplayPropertiesImages.h DisplayPropertiesLabels.h DisplayPropertiesSurface.h DisplayPropertiesVolume.h DisplayPropertyDataBoolean.h DisplayPropertyDataEnum.h DisplayPropertyDataFloat.h DummyFontTextRenderer.h EventAnnotationColorBarGet.h EventBrainReset.h EventBrainStructureGetAll.h EventBrowserTabDelete.h EventBrowserTabGet.h EventBrowserTabGetAll.h EventBrowserTabGetAllViewed.h EventBrowserTabNew.h EventCaretMappableDataFileMapsViewedInOverlays.h EventDataFileAdd.h EventDataFileDelete.h EventDataFileRead.h EventDataFileReload.h EventGetBrainOpenGLTextRenderer.h EventIdentificationHighlightLocation.h EventModelAdd.h EventModelDelete.h EventModelGetAll.h EventModelSurfaceGet.h EventNodeDataFilesGet.h EventNodeIdentificationColorsGetFromCharts.h EventOverlayValidate.h EventSpecFileReadDataFiles.h EventSurfacesGet.h FeatureColoringTypeEnum.h FiberOrientationSamplesLoader.h FiberOrientationSamplesVector.h FiberOrientationSymbolTypeEnum.h FociDrawingTypeEnum.h FtglFontTextRenderer.h GapsAndMargins.h IdentificationManager.h IdentificationStringBuilder.h IdentificationTextGenerator.h IdentificationWithColor.h IdentifiedItem.h IdentifiedItemNode.h IdentifiedItemVoxel.h ImageDepthPositionEnum.h Model.h ModelChart.h ModelSurface.h ModelSurfaceMontage.h ModelSurfaceSelector.h ModelTypeEnum.h ModelVolume.h ModelWholeBrain.h Overlay.h OverlaySet.h OverlaySetArray.h ProjectionViewTypeEnum.h SelectionItemDataTypeEnum.h SelectionItem.h SelectionItemAnnotation.h SelectionItemBorderSurface.h SelectionItemChartDataSeries.h SelectionItemChartFrequencySeries.h SelectionItemChartMatrix.h SelectionItemChartTimeSeries.h SelectionItemCiftiConnectivityMatrixRowColumn.h SelectionItemFocusSurface.h SelectionItemFocusVolume.h SelectionItemImage.h SelectionItemImageControlPoint.h SelectionItemSurfaceNode.h SelectionItemSurfaceNodeIdentificationSymbol.h SelectionItemSurfaceTriangle.h SelectionItemVoxel.h SelectionItemVoxelEditing.h SelectionItemVoxelIdentificationSymbol.h SelectionManager.h SessionManager.h Surface.h SurfaceDrawingTypeEnum.h SurfaceMontageConfigurationAbstract.h SurfaceMontageConfigurationCerebellar.h SurfaceMontageConfigurationCerebral.h SurfaceMontageConfigurationTypeEnum.h SurfaceMontageConfigurationFlatMaps.h SurfaceMontageLayoutOrientationEnum.h SurfaceMontageViewport.h SurfaceNodeColoring.h SurfaceSelectionModel.h ViewingTransformations.h ViewingTransformationsCerebellum.h ViewingTransformationsVolume.h VolumeSliceDrawingTypeEnum.h VolumeSliceSettings.h VolumeSurfaceOutlineColorOrTabModel.h VolumeSurfaceOutlineModel.h VolumeSurfaceOutlineSetModel.h WholeBrainSurfaceSettings.h WholeBrainVoxelDrawingMode.h AnnotationArrangerExecutor.cxx AnnotationArrangerInputs.cxx AnnotationManager.cxx BorderDrawingTypeEnum.cxx Brain.cxx BrainOpenGL.cxx BrainOpenGLAnnotationDrawingFixedPipeline.cxx BrainOpenGLChartDrawingFixedPipeline.cxx BrainOpenGLFixedPipeline.cxx BrainOpenGLPrimitiveDrawing.cxx BrainOpenGLShape.cxx BrainOpenGLShapeCone.cxx BrainOpenGLShapeCube.cxx BrainOpenGLShapeCylinder.cxx BrainOpenGLShapeRing.cxx BrainOpenGLShapeRingOutline.cxx BrainOpenGLShapeSphere.cxx BrainOpenGLTextRenderInterface.cxx BrainOpenGLTextureManager.cxx BrainOpenGLViewportContent.cxx BrainOpenGLVolumeObliqueSliceDrawing.cxx BrainOpenGLVolumeSliceDrawing.cxx BrainStructure.cxx BrainStructureNodeAttributes.cxx BrowserTabContent.cxx CaretDataFileSelectionModel.cxx CaretMappableDataFileAndMapSelectionModel.cxx ChartingDataManager.cxx CiftiConnectivityMatrixDataFileManager.cxx CiftiFiberTrajectoryManager.cxx ClippingPlaneGroup.cxx DisplayProperties.cxx DisplayPropertiesAnnotation.cxx DisplayPropertiesBorders.cxx DisplayPropertiesFiberOrientation.cxx DisplayPropertiesFoci.cxx DisplayPropertiesImages.cxx DisplayPropertiesLabels.cxx DisplayPropertiesSurface.cxx DisplayPropertiesVolume.cxx DisplayPropertyDataBoolean.cxx DisplayPropertyDataFloat.cxx DummyFontTextRenderer.cxx EventAnnotationColorBarGet.cxx EventBrainReset.cxx EventBrainStructureGetAll.cxx EventBrowserTabDelete.cxx EventBrowserTabGet.cxx EventBrowserTabGetAll.cxx EventBrowserTabGetAllViewed.cxx EventBrowserTabNew.cxx EventCaretMappableDataFileMapsViewedInOverlays.cxx EventDataFileAdd.cxx EventDataFileDelete.cxx EventDataFileRead.cxx EventDataFileReload.cxx EventGetBrainOpenGLTextRenderer.cxx EventIdentificationHighlightLocation.cxx EventModelAdd.cxx EventModelDelete.cxx EventModelGetAll.cxx EventModelSurfaceGet.cxx EventNodeDataFilesGet.cxx EventNodeIdentificationColorsGetFromCharts.cxx EventOverlayValidate.cxx EventSpecFileReadDataFiles.cxx EventSurfacesGet.cxx FeatureColoringTypeEnum.cxx FiberOrientationSamplesLoader.cxx FiberOrientationSymbolTypeEnum.cxx FociDrawingTypeEnum.cxx FtglFontTextRenderer.cxx GapsAndMargins.cxx IdentificationManager.cxx IdentificationStringBuilder.cxx IdentificationTextGenerator.cxx IdentificationWithColor.cxx IdentifiedItem.cxx IdentifiedItemNode.cxx IdentifiedItemVoxel.cxx ImageDepthPositionEnum.cxx Model.cxx ModelChart.cxx ModelSurface.cxx ModelSurfaceMontage.cxx ModelSurfaceSelector.cxx ModelTypeEnum.cxx ModelVolume.cxx ModelWholeBrain.cxx Overlay.cxx OverlaySet.cxx OverlaySetArray.cxx ProjectionViewTypeEnum.cxx SelectionItemDataTypeEnum.cxx SelectionItem.cxx SelectionItemAnnotation.cxx SelectionItemBorderSurface.cxx SelectionItemChartDataSeries.cxx SelectionItemChartFrequencySeries.cxx SelectionItemChartMatrix.cxx SelectionItemChartTimeSeries.cxx SelectionItemCiftiConnectivityMatrixRowColumn.cxx SelectionItemFocusSurface.cxx SelectionItemFocusVolume.cxx SelectionItemImage.cxx SelectionItemImageControlPoint.cxx SelectionItemSurfaceNode.cxx SelectionItemSurfaceNodeIdentificationSymbol.cxx SelectionItemSurfaceTriangle.cxx SelectionItemVoxel.cxx SelectionItemVoxelIdentificationSymbol.cxx SelectionItemVoxelEditing.cxx SelectionManager.cxx SessionManager.cxx Surface.cxx SurfaceDrawingTypeEnum.cxx SurfaceMontageConfigurationAbstract.cxx SurfaceMontageConfigurationCerebellar.cxx SurfaceMontageConfigurationCerebral.cxx SurfaceMontageConfigurationTypeEnum.cxx SurfaceMontageConfigurationFlatMaps.cxx SurfaceMontageLayoutOrientationEnum.cxx SurfaceMontageViewport.cxx SurfaceNodeColoring.cxx SurfaceSelectionModel.cxx ViewingTransformations.cxx ViewingTransformationsCerebellum.cxx ViewingTransformationsVolume.cxx VolumeSliceDrawingTypeEnum.cxx VolumeSliceSettings.cxx VolumeSurfaceOutlineColorOrTabModel.cxx VolumeSurfaceOutlineModel.cxx VolumeSurfaceOutlineSetModel.cxx WholeBrainSurfaceSettings.cxx WholeBrainVoxelDrawingMode.cxx ) # # Find Headers # INCLUDE_DIRECTORIES( ${CMAKE_SOURCE_DIR}/Annotations ${CMAKE_SOURCE_DIR}/Brain ${CMAKE_SOURCE_DIR}/Charting ${CMAKE_SOURCE_DIR}/Cifti ${CMAKE_SOURCE_DIR}/FilesBase ${CMAKE_SOURCE_DIR}/Files ${CMAKE_SOURCE_DIR}/Palette ${CMAKE_SOURCE_DIR}/Gifti ${CMAKE_SOURCE_DIR}/Nifti ${CMAKE_SOURCE_DIR}/Scenes ${CMAKE_SOURCE_DIR}/Xml ${CMAKE_SOURCE_DIR}/Common ) IF (FREETYPE_FOUND) INCLUDE_DIRECTORIES( ${FTGL_INCLUDE_DIRS} ${FREETYPE_INCLUDE_DIR_ft2build} ${FREETYPE_INCLUDE_DIR_freetype2} ) ENDIF (FREETYPE_FOUND) connectome-workbench-1.2.3+git41-gc4c6c90/src/Brain/CaretDataFileSelectionModel.cxx000066400000000000000000000431101300200146000276010ustar00rootroot00000000000000 /*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 __CARET_DATA_FILE_SELECTION_MODEL_DECLARE__ #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 "SceneClass.h" #include "SceneClassAssistant.h" using namespace caret; /** * \class caret::CaretDataFileSelectionModel * \brief Selection model for a CaretDataFile. * \ingroup Brain * * Maintains selection of a type of CaretDataFile. Used in the GUI * by CaretDataFileSelectionComboBox. */ /** * Constructor. This constructor is private and one of the static * factory methods should be used to create new instances of * this class. * * @param brain * Brain containing the files. * @param structure * Structure for the files. * @param fileMode * File mode that indicates how files are chosen from the 'Brain'. */ CaretDataFileSelectionModel::CaretDataFileSelectionModel(Brain* brain, const StructureEnum::Enum structure, const FileMode fileMode) : CaretObject(), m_fileMode(fileMode), m_brain(brain), m_structure(structure) { m_overrideOfAvailableFilesValid = false; CaretAssert(brain); m_sceneAssistant = new SceneClassAssistant(); m_dataFileTypes.clear(); } /** * Destructor. */ CaretDataFileSelectionModel::~CaretDataFileSelectionModel() { delete m_sceneAssistant; } /** * Create a new instance of a Caret Data File Selection Model that * selects files of the given Data File Type from the given Brain. * * @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* model = new CaretDataFileSelectionModel(brain, StructureEnum::ALL, FILE_MODE_DATA_FILE_TYPE_ENUM); model->m_dataFileTypes.push_back(dataFileType); return model; } /** * Create a new instance of a Caret Data File Selection Model that * selects files of the given Data File Types from the given Brain. * * @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* model = new CaretDataFileSelectionModel(brain, StructureEnum::ALL, FILE_MODE_DATA_FILE_TYPE_ENUM); model->m_dataFileTypes.insert(model->m_dataFileTypes.end(), dataFileTypes.begin(), dataFileTypes.end()); return model; } /** * 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. * * @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'. * @param dataFileTypes * Types of the data file. */ CaretDataFileSelectionModel* CaretDataFileSelectionModel::newInstanceForCaretDataFileTypesInStructure(Brain* brain, const StructureEnum::Enum structure, const std::vector& dataFileTypes) { CaretDataFileSelectionModel* model = new CaretDataFileSelectionModel(brain, structure, FILE_MODE_DATA_FILE_TYPE_ENUM); model->m_dataFileTypes.insert(model->m_dataFileTypes.end(), dataFileTypes.begin(), dataFileTypes.end()); return model; } /** * 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* model = new CaretDataFileSelectionModel(brain, 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* model = new CaretDataFileSelectionModel(brain, StructureEnum::ALL, FILE_MODE_MULTI_STRUCTURE_BORDER_FILES); return model; } /** * Copy constructor. * @param obj * Object that is copied. */ CaretDataFileSelectionModel::CaretDataFileSelectionModel(const CaretDataFileSelectionModel& obj) : CaretObject(obj), SceneableInterface(), m_fileMode(obj.m_fileMode), m_brain(obj.m_brain) { this->copyHelperCaretDataFileSelectionModel(obj); } /** * Assignment operator. * @param obj * Data copied from obj to this. * @return * Reference to this object. */ CaretDataFileSelectionModel& CaretDataFileSelectionModel::operator=(const CaretDataFileSelectionModel& obj) { if (this != &obj) { CaretObject::operator=(obj); this->copyHelperCaretDataFileSelectionModel(obj); } return *this; } /** * Helps with copying an object of this type. * @param obj * Object that is copied. */ 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; m_overrideOfAvailableFilesValid = obj.m_overrideOfAvailableFilesValid; m_overrideOfAvailableFiles = obj.m_overrideOfAvailableFiles; } /** * @return The selected file or NULL if no file is available. */ CaretDataFile* CaretDataFileSelectionModel::getSelectedFile() { updateSelection(); return m_selectedFile; } /** * @return The selected file or NULL if no file is available. */ const CaretDataFile* CaretDataFileSelectionModel::getSelectedFile() const { updateSelection(); return m_selectedFile; } /** * Set the selected file or NULL if no file is available. * * @param selectedFile * Set the selected file. */ void CaretDataFileSelectionModel::setSelectedFile(CaretDataFile* selectedFile) { m_selectedFile = selectedFile; } /** * @return Files available for selection. */ std::vector CaretDataFileSelectionModel::getAvailableFiles() const { if (m_overrideOfAvailableFilesValid) { return m_overrideOfAvailableFiles; } std::vector caretDataFiles; switch (m_fileMode) { case FILE_MODE_DATA_FILE_TYPE_ENUM: { m_brain->getAllDataFilesWithDataFileTypes(m_dataFileTypes, caretDataFiles); } 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); /* * Want files that ARE parcel chartable */ if (chartParcelFile != NULL) { CaretMappableDataFile* mapFile = chartParcelFile->getMatrixChartCaretMappableDataFile(); caretDataFiles.push_back(mapFile); } } } case FILE_MODE_CHARTABLE_MATRIX_SERIES_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); /* * Want files that are NOT parcel chartable */ if (chartParcelFile == NULL) { CaretMappableDataFile* mapFile = chartFile->getMatrixChartCaretMappableDataFile(); caretDataFiles.push_back(mapFile); } } } 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); } } } break; } if (m_structure == StructureEnum::ALL) { return caretDataFiles; } std::vector caretDataFilesOut; for (std::vector::iterator iter = caretDataFiles.begin(); iter != caretDataFiles.end(); iter++) { CaretDataFile* cdf = *iter; const StructureEnum::Enum fileStructure = cdf->getStructure(); if ((fileStructure == StructureEnum::ALL) || (fileStructure == StructureEnum::INVALID)) { caretDataFilesOut.push_back(cdf); } else if (fileStructure == m_structure) { caretDataFilesOut.push_back(cdf); } } return caretDataFilesOut; } /** * Override the available files with the given files. Once this method * is called, these will be the available files until this method is called * again. * * @param availableFiles * Files that will be used in this model. */ void CaretDataFileSelectionModel::overrideAvailableDataFiles(std::vector& availableFiles) { m_overrideOfAvailableFiles = availableFiles; m_overrideOfAvailableFilesValid = true; } /** * @return Structure used in this model. */ StructureEnum::Enum CaretDataFileSelectionModel::getStructure() const { return m_structure; } /** * Set the structure used for this model. * * @param structure * New structure for this model. */ void CaretDataFileSelectionModel::setStructure(const StructureEnum::Enum structure) { m_structure = structure; updateSelection(); } /** * Update the selected file. */ void CaretDataFileSelectionModel::updateSelection() const { std::vector caretDataFiles = getAvailableFiles(); if (caretDataFiles.empty()) { m_selectedFile = NULL; return; } if (m_selectedFile != NULL) { if (std::find(caretDataFiles.begin(), caretDataFiles.end(), m_selectedFile) == caretDataFiles.end()) { m_selectedFile = NULL; } } if (m_selectedFile == NULL) { if (! caretDataFiles.empty()) { m_selectedFile = caretDataFiles[0]; } } } /** * Get a description of this object's content. * @return String describing this object's content. */ AString CaretDataFileSelectionModel::toString() const { return "CaretDataFileSelectionModel"; } /** * 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* CaretDataFileSelectionModel::saveToScene(const SceneAttributes* sceneAttributes, const AString& instanceName) { SceneClass* sceneClass = new SceneClass(instanceName, "CaretDataFileSelectionModel", 1); m_sceneAssistant->saveMembers(sceneAttributes, sceneClass); CaretDataFile* caretDataFile = getSelectedFile(); if (caretDataFile != NULL) { sceneClass->addPathName("selectedFileNameWithPath", caretDataFile->getFileName()); sceneClass->addString("selectedFileNameNoPath", caretDataFile->getFileNameNoPath()); } // 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 CaretDataFileSelectionModel::restoreFromScene(const SceneAttributes* sceneAttributes, const SceneClass* sceneClass) { if (sceneClass == NULL) { return; } setSelectedFile(NULL); m_sceneAssistant->restoreMembers(sceneAttributes, sceneClass); bool foundFileFlag = false; const AString selectedFileNameWithPath = sceneClass->getPathNameValue("selectedFileNameWithPath"); if ( ! selectedFileNameWithPath.isEmpty()) { std::vector caretDataFiles = getAvailableFiles(); for (std::vector::iterator iter = caretDataFiles.begin(); iter != caretDataFiles.end(); iter++) { CaretDataFile* cdf = *iter; if (cdf->getFileName() == selectedFileNameWithPath) { setSelectedFile(cdf); foundFileFlag = true; break; } } } if ( ! foundFileFlag) { const AString selectedFileName = sceneClass->getStringValue("selectedFileNameNoPath", ""); if ( ! selectedFileName.isEmpty()) { std::vector caretDataFiles = getAvailableFiles(); for (std::vector::iterator iter = caretDataFiles.begin(); iter != caretDataFiles.end(); iter++) { CaretDataFile* cdf = *iter; if (cdf->getFileNameNoPath() == selectedFileName) { setSelectedFile(cdf); break; } } } } //Uncomment if sub-classes must restore from scene //restoreSubClassDataFromScene(sceneAttributes, // sceneClass); } connectome-workbench-1.2.3+git41-gc4c6c90/src/Brain/CaretDataFileSelectionModel.h000066400000000000000000000132521300200146000272320ustar00rootroot00000000000000#ifndef __CARET_DATA_FILE_SELECTION_MODEL_H__ #define __CARET_DATA_FILE_SELECTION_MODEL_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 "DataFileTypeEnum.h" #include "SceneableInterface.h" #include "StructureEnum.h" namespace caret { class Brain; class CaretDataFile; class SceneClassAssistant; class CaretDataFileSelectionModel : public CaretObject, public SceneableInterface { public: static CaretDataFileSelectionModel* newInstanceForCaretDataFileType(Brain* brain, const DataFileTypeEnum::Enum dataFileType); static CaretDataFileSelectionModel* newInstanceForCaretDataFileTypes(Brain* brain, const std::vector& dataFileTypes); static CaretDataFileSelectionModel* newInstanceForCaretDataFileTypesInStructure(Brain* brain, const StructureEnum::Enum structure, const std::vector& dataFileTypes); static CaretDataFileSelectionModel* newInstanceForChartableMatrixParcelInterface(Brain* brain); static CaretDataFileSelectionModel* newInstanceForChartableMatrixSeriesInterface(Brain* brain); static CaretDataFileSelectionModel* newInstanceForMultiStructureBorderFiles(Brain* brain); virtual ~CaretDataFileSelectionModel(); CaretDataFileSelectionModel(const CaretDataFileSelectionModel& obj); CaretDataFileSelectionModel& operator=(const CaretDataFileSelectionModel& obj); CaretDataFile* getSelectedFile(); const CaretDataFile* getSelectedFile() const; StructureEnum::Enum getStructure() const; void setStructure(const StructureEnum::Enum structure); std::vector getAvailableFiles() const; void overrideAvailableDataFiles(std::vector& availableFiles); /** * @return Selected file dynamically cast to the templated * data file type. */ template inline T* getSelectedFileOfType() { CaretDataFile* cdf = getSelectedFile(); if (cdf != NULL) { return dynamic_cast(getSelectedFile()); } return NULL; } void setSelectedFile(CaretDataFile* selectedFile); // 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 implemetation by sub-classes. // protected: // virtual void saveSubClassDataToScene(const SceneAttributes* sceneAttributes, // SceneClass* sceneClass) = 0; // // virtual void restoreSubClassDataFromScene(const SceneAttributes* sceneAttributes, // const SceneClass* sceneClass) = 0; private: enum FileMode { FILE_MODE_CHARTABLE_MATRIX_PARCEL_INTERFACE, FILE_MODE_CHARTABLE_MATRIX_SERIES_INTERFACE, FILE_MODE_DATA_FILE_TYPE_ENUM, FILE_MODE_MULTI_STRUCTURE_BORDER_FILES }; CaretDataFileSelectionModel(Brain* brain, const StructureEnum::Enum structure, const FileMode fileMode); void copyHelperCaretDataFileSelectionModel(const CaretDataFileSelectionModel& obj); void updateSelection() const; const FileMode m_fileMode; std::vector m_overrideOfAvailableFiles; bool m_overrideOfAvailableFilesValid; Brain* m_brain; StructureEnum::Enum m_structure; std::vector m_dataFileTypes; mutable CaretDataFile* m_selectedFile; SceneClassAssistant* m_sceneAssistant; // ADD_NEW_MEMBERS_HERE }; #ifdef __CARET_DATA_FILE_SELECTION_MODEL_DECLARE__ // #endif // __CARET_DATA_FILE_SELECTION_MODEL_DECLARE__ } // namespace #endif //__CARET_DATA_FILE_SELECTION_MODEL_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Brain/CaretMappableDataFileAndMapSelectionModel.cxx000066400000000000000000000335001300200146000323260ustar00rootroot00000000000000 /*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 __CARET_MAPPABLE_DATA_FILE_AND_MAP_SELECTION_MODEL_DECLARE__ #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" #include "CaretMappableDataFile.h" #include "SceneClass.h" #include "SceneClassAssistant.h" using namespace caret; /** * \class caret::CaretMappableDataFileAndMapSelectionModel * \brief Model for selection of a CaretMappableDataFile and map index. * \ingroup Brain */ /** * Constructor. * * @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() { std::vector dataFileTypesVector; dataFileTypesVector.push_back(dataFileType); performConstruction(brain, dataFileTypesVector); } /** * Constructor for multiple types of files. * * @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() { performConstruction(brain, dataFileTypes); } /** * Destructor. */ CaretMappableDataFileAndMapSelectionModel::~CaretMappableDataFileAndMapSelectionModel() { delete m_caretDataFileSelectionModel; delete m_sceneAssistant; } /** * 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. */ void CaretMappableDataFileAndMapSelectionModel::performConstruction(Brain* brain, const std::vector& dataFileTypes) { m_brain = brain; m_dataFileTypes = dataFileTypes; for (std::vector::const_iterator typeIter = dataFileTypes.begin(); typeIter != dataFileTypes.end(); typeIter++) { const DataFileTypeEnum::Enum dataFileType = *typeIter; bool isMappableFile = false; switch (dataFileType) { case DataFileTypeEnum::ANNOTATION: CaretAssert(0); break; case DataFileTypeEnum::BORDER: break; case DataFileTypeEnum::CONNECTIVITY_DENSE: isMappableFile = true; break; case DataFileTypeEnum::CONNECTIVITY_DENSE_DYNAMIC: isMappableFile = true; break; case DataFileTypeEnum::CONNECTIVITY_DENSE_LABEL: isMappableFile = true; break; case DataFileTypeEnum::CONNECTIVITY_DENSE_PARCEL: isMappableFile = true; break; case DataFileTypeEnum::CONNECTIVITY_DENSE_SCALAR: isMappableFile = true; break; case DataFileTypeEnum::CONNECTIVITY_DENSE_TIME_SERIES: isMappableFile = true; break; case DataFileTypeEnum::CONNECTIVITY_FIBER_ORIENTATIONS_TEMPORARY: break; case DataFileTypeEnum::CONNECTIVITY_FIBER_TRAJECTORY_TEMPORARY: isMappableFile = true; break; case DataFileTypeEnum::CONNECTIVITY_PARCEL: isMappableFile = true; break; case DataFileTypeEnum::CONNECTIVITY_PARCEL_DENSE: isMappableFile = true; break; case DataFileTypeEnum::CONNECTIVITY_PARCEL_LABEL: isMappableFile = true; break; case DataFileTypeEnum::CONNECTIVITY_PARCEL_SCALAR: isMappableFile = true; break; case DataFileTypeEnum::CONNECTIVITY_PARCEL_SERIES: isMappableFile = true; break; case DataFileTypeEnum::CONNECTIVITY_SCALAR_DATA_SERIES: isMappableFile = true; break; case DataFileTypeEnum::FOCI: break; case DataFileTypeEnum::IMAGE: break; case DataFileTypeEnum::LABEL: isMappableFile = true; break; case DataFileTypeEnum::METRIC: isMappableFile = true; break; case DataFileTypeEnum::PALETTE: break; case DataFileTypeEnum::RGBA: isMappableFile = true; break; case DataFileTypeEnum::SCENE: break; case DataFileTypeEnum::SPECIFICATION: break; case DataFileTypeEnum::SURFACE: break; case DataFileTypeEnum::UNKNOWN: break; case DataFileTypeEnum::VOLUME: break; } CaretAssert(isMappableFile); if ( ! isMappableFile) { CaretLogSevere(DataFileTypeEnum::toGuiName(dataFileType) + " is not a valid mappable data file."); } } m_caretDataFileSelectionModel = CaretDataFileSelectionModel::newInstanceForCaretDataFileTypes(brain, dataFileTypes); m_selectedMapIndex = -1; m_sceneAssistant = new SceneClassAssistant(); m_sceneAssistant->add("m_caretDataFileSelectionModel", "CaretDataFileSelectionModel", m_caretDataFileSelectionModel); m_sceneAssistant->add("m_selectedMapIndex", &m_selectedMapIndex); } /** * Copy constructor. * @param obj * Object that is copied. */ CaretMappableDataFileAndMapSelectionModel::CaretMappableDataFileAndMapSelectionModel(const CaretMappableDataFileAndMapSelectionModel& obj) : CaretObject(obj), SceneableInterface(obj) { this->copyHelperCaretMappableDataFileAndMapSelectionModel(obj); } /** * Assignment operator. * @param obj * Data copied from obj to this. * @return * Reference to this object. */ CaretMappableDataFileAndMapSelectionModel& CaretMappableDataFileAndMapSelectionModel::operator=(const CaretMappableDataFileAndMapSelectionModel& obj) { if (this != &obj) { CaretObject::operator=(obj); this->copyHelperCaretMappableDataFileAndMapSelectionModel(obj); } return *this; } /** * Helps with copying an object of this type. * @param obj * Object that is copied. */ void CaretMappableDataFileAndMapSelectionModel::copyHelperCaretMappableDataFileAndMapSelectionModel(const CaretMappableDataFileAndMapSelectionModel& obj) { *m_caretDataFileSelectionModel = *obj.m_caretDataFileSelectionModel; m_selectedMapIndex = obj.m_selectedMapIndex; } /** * @return The data file selection model. */ CaretDataFileSelectionModel* CaretMappableDataFileAndMapSelectionModel::getCaretDataFileSelectionModel() { return m_caretDataFileSelectionModel; } /** * @return the selected file. */ CaretMappableDataFile* CaretMappableDataFileAndMapSelectionModel::getSelectedFile() { return dynamic_cast(m_caretDataFileSelectionModel->getSelectedFile()); } /** * @return the selected file. */ const CaretMappableDataFile* CaretMappableDataFileAndMapSelectionModel::getSelectedFile() const { return dynamic_cast(m_caretDataFileSelectionModel->getSelectedFile()); } /** * @return the selected map index (-1 if no map available). */ int32_t CaretMappableDataFileAndMapSelectionModel::getSelectedMapIndex() const { const CaretMappableDataFile* cmdf = getSelectedFile(); if (cmdf != NULL) { const int32_t numMaps = cmdf->getNumberOfMaps(); if (numMaps > 0) { if (m_selectedMapIndex < 0) { m_selectedMapIndex = 0; } else if (m_selectedMapIndex >= numMaps) { m_selectedMapIndex = numMaps - 1; } } else { m_selectedMapIndex = -1; } } else { m_selectedMapIndex = -1; } return m_selectedMapIndex; } /** * @return the files in this model. */ std::vector CaretMappableDataFileAndMapSelectionModel::getAvailableFiles() const { std::vector caretDataFiles = m_caretDataFileSelectionModel->getAvailableFiles(); std::vector mappableFiles; for (std::vector::iterator iter = caretDataFiles.begin(); iter != caretDataFiles.end(); iter++) { CaretMappableDataFile* cmdf = dynamic_cast(*iter); if (cmdf != NULL) { mappableFiles.push_back(cmdf); } } return mappableFiles; } /** * Override the available files with the given files. Once this method * is called, these will be the available files until this method is called * again. * * @param availableFiles * Files that will be used in this model. */ void CaretMappableDataFileAndMapSelectionModel::overrideAvailableDataFiles(std::vector& availableFiles) { std::vector files(availableFiles.begin(), availableFiles.end()); m_caretDataFileSelectionModel->overrideAvailableDataFiles(files); } /** * Set the selected file * * @param selectedFile * New selected file. */ 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) + " "); } 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; } } m_caretDataFileSelectionModel->setSelectedFile(selectedFile); } /** * Set the selected map index * * @param mapIndex * New selected map index. */ void CaretMappableDataFileAndMapSelectionModel::setSelectedMapIndex(const int32_t mapIndex) { m_selectedMapIndex = mapIndex; } /** * 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* CaretMappableDataFileAndMapSelectionModel::saveToScene(const SceneAttributes* sceneAttributes, const AString& instanceName) { SceneClass* sceneClass = new SceneClass(instanceName, "CaretMappableDataFileAndMapSelectionModel", 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 CaretMappableDataFileAndMapSelectionModel::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); } connectome-workbench-1.2.3+git41-gc4c6c90/src/Brain/CaretMappableDataFileAndMapSelectionModel.h000066400000000000000000000112521300200146000317530ustar00rootroot00000000000000#ifndef __CARET_MAPPABLE_DATA_FILE_AND_MAP_SELECTION_MODEL_H__ #define __CARET_MAPPABLE_DATA_FILE_AND_MAP_SELECTION_MODEL_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 "DataFileTypeEnum.h" #include "SceneableInterface.h" namespace caret { class Brain; class CaretDataFileSelectionModel; class CaretMappableDataFile; class SceneClassAssistant; class CaretMappableDataFileAndMapSelectionModel : public CaretObject, public SceneableInterface { public: CaretMappableDataFileAndMapSelectionModel(Brain* brain, const DataFileTypeEnum::Enum dataFileType); CaretMappableDataFileAndMapSelectionModel(Brain* brain, const std::vector& dataFileTypes); virtual ~CaretMappableDataFileAndMapSelectionModel(); CaretMappableDataFileAndMapSelectionModel(const CaretMappableDataFileAndMapSelectionModel& obj); CaretMappableDataFileAndMapSelectionModel& operator=(const CaretMappableDataFileAndMapSelectionModel& obj); CaretDataFileSelectionModel* getCaretDataFileSelectionModel(); CaretMappableDataFile* getSelectedFile(); const CaretMappableDataFile* getSelectedFile() const; int32_t getSelectedMapIndex() const; std::vector getAvailableFiles() const; void overrideAvailableDataFiles(std::vector& availableFiles); /** * @return Selected file dynamically cast to the templated * data file type. */ template inline T* getSelectedFileOfType() { CaretMappableDataFile* cmdf = getSelectedFile(); if (cmdf != NULL) { return dynamic_cast(getSelectedFile()); } return NULL; } void setSelectedFile(CaretMappableDataFile* selectedFile); void setSelectedMapIndex(const int32_t mapIndex); // 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 implemetation 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 copyHelperCaretMappableDataFileAndMapSelectionModel(const CaretMappableDataFileAndMapSelectionModel& obj); void performConstruction(Brain* brain, const std::vector& dataFileTypes); SceneClassAssistant* m_sceneAssistant; Brain* m_brain; std::vector m_dataFileTypes; CaretDataFileSelectionModel* m_caretDataFileSelectionModel; mutable int32_t m_selectedMapIndex; // ADD_NEW_MEMBERS_HERE }; #ifdef __CARET_MAPPABLE_DATA_FILE_AND_MAP_SELECTION_MODEL_DECLARE__ // #endif // __CARET_MAPPABLE_DATA_FILE_AND_MAP_SELECTION_MODEL_DECLARE__ } // namespace #endif //__CARET_MAPPABLE_DATA_FILE_AND_MAP_SELECTION_MODEL_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Brain/CaretOpenGLInclude.h000066400000000000000000000024151300200146000253610ustar00rootroot00000000000000#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 Brain */ #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__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Brain/ChartingDataManager.cxx000066400000000000000000000135031300200146000261510ustar00rootroot00000000000000 /*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 __CHARTING_DATA_MANAGER_DECLARE__ #include "ChartingDataManager.h" #undef __CHARTING_DATA_MANAGER_DECLARE__ #include "Brain.h" #include "CaretAssert.h" #include "EventManager.h" #include "EventMapYokingSelectMap.h" #include "ModelChart.h" #include "SurfaceFile.h" using namespace caret; /** * \class caret::ChartingDataManager * \brief Manages charting data. * \ingroup Brain */ /** * Constructor. */ ChartingDataManager::ChartingDataManager(Brain* brain) : CaretObject(), m_brain(brain) { CaretAssert(brain); /* * Need PROCESSED event (after others have handled the event) */ EventManager::get()->addProcessedEventListener(this, EventTypeEnum::EVENT_MAP_YOKING_SELECT_MAP); } /** * Destructor. */ ChartingDataManager::~ChartingDataManager() { EventManager::get()->removeAllEventsFromListener(this); } /** * Receive an event. * * @param event * The event that the receive can respond to. */ void ChartingDataManager::receiveEvent(Event* event) { if (event->getEventType() == EventTypeEnum::EVENT_MAP_YOKING_SELECT_MAP) { EventMapYokingSelectMap* yokeMapEvent = dynamic_cast(event); CaretAssert(yokeMapEvent); ModelChart* modelChart = m_brain->getChartModel(); if (modelChart != NULL) { modelChart->loadChartDataForYokedCiftiMappableFiles(yokeMapEvent->getMapYokingGroup(), yokeMapEvent->getMapIndex()); } } } /** * Load the average of chart data for a group of surface nodes. * * @param surfaceFile * The surface file * @param nodeIndices * Indices of nodes whose chart data is averaged */ void ChartingDataManager::loadAverageChartForSurfaceNodes(const SurfaceFile* surfaceFile, const std::vector& nodeIndices) const { CaretAssert(surfaceFile); ModelChart* modelChart = m_brain->getChartModel(); if (modelChart != NULL) { modelChart->loadAverageChartDataForSurfaceNodes(surfaceFile->getStructure(), surfaceFile->getNumberOfNodes(), nodeIndices); } } /** * Load chart data for a surface node. * * @param surfaceFile * The surface file * @param nodeIndex * Index of node. */ void ChartingDataManager::loadChartForSurfaceNode(const SurfaceFile* surfaceFile, const int32_t nodeIndex) const { CaretAssert(surfaceFile); ModelChart* modelChart = m_brain->getChartModel(); if (modelChart != NULL) { modelChart->loadChartDataForSurfaceNode(surfaceFile->getStructure(), surfaceFile->getNumberOfNodes(), nodeIndex); } } /** * Load chart data from given file at the given row. * * @param ciftiMapFile * The CIFTI file. * @param rowIndex * Index of row in the file. */ void ChartingDataManager::loadChartForCiftiMappableFileRow(CiftiMappableDataFile* ciftiMapFile, const int32_t rowIndex) const { CaretAssert(ciftiMapFile); ModelChart* modelChart = m_brain->getChartModel(); if (modelChart != NULL) { modelChart->loadChartDataForCiftiMappableFileRow(ciftiMapFile, rowIndex); } } /** * Load chart data for a voxel. * * @param xyz * Coordinate of voxel. */ void ChartingDataManager::loadChartForVoxelAtCoordinate(const float xyz[3]) const { ModelChart* modelChart = m_brain->getChartModel(); if (modelChart != NULL) { modelChart->loadChartDataForVoxelAtCoordinate(xyz); } } /** * @return True if there are charting that retrieve data from the network. */ bool ChartingDataManager::hasNetworkFiles() const { /* * At this time, all 'chartable' files are read in their entirety * so all data is local once the file is read. */ return false; // std::vector chartFiles; // if (requireChartingEnableInFiles) { // m_brain->getAllChartableDataFilesWithChartingEnabled(chartFiles); // } // else { // m_brain->getAllChartableDataFiles(chartFiles); // } // // for (std::vector::iterator fileIter = chartFiles.begin(); // fileIter != chartFiles.end(); // fileIter++) { // ChartableLineSeriesBrainordinateInterface* chartFile = *fileIter; // // CaretDataFile* caretDataFile = dynamic_cast(chartFile); // CaretAssert(caretDataFile); // if (caretDataFile->isEmpty() == false) { // const AString filename = caretDataFile->getFileName(); // if (CaretDataFile::isFileOnNetwork(filename)) { // return true; // } // } // } // // return false; } connectome-workbench-1.2.3+git41-gc4c6c90/src/Brain/ChartingDataManager.h000066400000000000000000000045041300200146000255770ustar00rootroot00000000000000#ifndef __CHARTING_DATA_MANAGER_H__ #define __CHARTING_DATA_MANAGER_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 "EventListenerInterface.h" namespace caret { class Brain; class CiftiMappableDataFile; class SurfaceFile; class ChartingDataManager : public CaretObject, public EventListenerInterface { public: ChartingDataManager(Brain* brain); virtual ~ChartingDataManager(); void loadAverageChartForSurfaceNodes(const SurfaceFile* surfaceFile, const std::vector& nodeIndices) const; void loadChartForSurfaceNode(const SurfaceFile* surfaceFile, const int32_t nodeIndex) const; void loadChartForVoxelAtCoordinate(const float xyz[3]) const; void loadChartForCiftiMappableFileRow(CiftiMappableDataFile* ciftiMapFile, const int32_t rowIndex) const; bool hasNetworkFiles() const; virtual void receiveEvent(Event* event); private: ChartingDataManager(const ChartingDataManager&); ChartingDataManager& operator=(const ChartingDataManager&); Brain* m_brain; // ADD_NEW_MEMBERS_HERE }; #ifdef __CHARTING_DATA_MANAGER_DECLARE__ // #endif // __CHARTING_DATA_MANAGER_DECLARE__ } // namespace #endif //__CHARTING_DATA_MANAGER_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Brain/CiftiConnectivityMatrixDataFileManager.cxx000066400000000000000000000466331300200146000320460ustar00rootroot00000000000000 /*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 __CIFTI_CONNECTIVITY_MATRIX_DATA_FILE_MANAGER_DECLARE__ #include "CiftiConnectivityMatrixDataFileManager.h" #undef __CIFTI_CONNECTIVITY_MATRIX_DATA_FILE_MANAGER_DECLARE__ #include "Brain.h" #include "CaretAssert.h" #include "CiftiConnectivityMatrixParcelFile.h" #include "CiftiMappableConnectivityMatrixDataFile.h" #include "EventBrowserTabGetAllViewed.h" #include "EventGetDisplayedDataFiles.h" #include "EventManager.h" #include "EventSurfaceColoringInvalidate.h" #include "SceneAttributes.h" #include "SceneClass.h" #include "SceneClassArray.h" #include "ScenePrimitiveArray.h" #include "Surface.h" #include "SurfaceFile.h" using namespace caret; /** * \class caret::CiftiConnectivityMatrixDataFileManager * \brief Manages loading data from cifti connectivity files * \ingroup Brain */ /** * Constructor. * * @param brain * Brain that uses this instance. */ CiftiConnectivityMatrixDataFileManager::CiftiConnectivityMatrixDataFileManager() : CaretObject() { } /** * Destructor. */ CiftiConnectivityMatrixDataFileManager::~CiftiConnectivityMatrixDataFileManager() { } /** * Get the connectivity files that are displayed. * * @param brain * Brain for which data is loaded. * @param ciftiMatrixFilesOut * Output with displayed files. */ void CiftiConnectivityMatrixDataFileManager::getDisplayedConnectivityMatrixFiles(Brain* brain, std::vector& ciftiMatrixFilesOut) const { ciftiMatrixFilesOut.clear(); /* * WB-633 New requirements for updating of connectivity data * Implementation on hold and explained in Jira. * * 7/15/2016 - USE ALL FILES, DO NOT TEST FOR DISPLAYED VS NOT-DISPLAYED FILES */ const bool allFilesFlag = true; if (allFilesFlag) { /* * All cifti matrix files, even if not displayed? */ brain->getAllCiftiConnectivityMatrixFiles(ciftiMatrixFilesOut); return; } std::vector ciftiMatrixFiles; brain->getAllCiftiConnectivityMatrixFiles(ciftiMatrixFiles); /* * Get all browser tabs and only save transformations for tabs * that are valid. */ EventBrowserTabGetAllViewed getViewedTabs; EventManager::get()->sendEvent(getViewedTabs.getPointer()); std::vector tabIndices = getViewedTabs.getViewdedBrowserTabIndices(); std::vector windowIndices; // empty is all windows EventGetDisplayedDataFiles displayedFilesEvent(windowIndices, tabIndices); EventManager::get()->sendEvent(displayedFilesEvent.getPointer()); std::set displayedDataFiles = displayedFilesEvent.getDisplayedDataFiles(); /* * Find CIFTI matrix files that are displayed in an overlay */ for (std::vector::iterator iter = ciftiMatrixFiles.begin(); iter != ciftiMatrixFiles.end(); iter++) { if (std::find(displayedDataFiles.begin(), displayedDataFiles.end(), *iter) != displayedDataFiles.end()) { ciftiMatrixFilesOut.push_back(*iter); } } } /** * Load row from the given parcel file. * * @param brain * Brain for which data is loaded. * @param parcelFile * The parcel file. * @param rowIndex * Index of the row. * @param columnIndex * Index of the column. * @param rowColumnInformationOut * Appends one string for each row/column loaded * @return * true if success, else false. */ bool CiftiConnectivityMatrixDataFileManager::loadRowOrColumnFromParcelFile(Brain* brain, CiftiConnectivityMatrixParcelFile* parcelFile, const int32_t rowIndex, const int32_t columnIndex, std::vector& rowColumnInformationOut) { CaretAssert(parcelFile); int32_t rowColumnIndexToLoad = -1; switch (parcelFile->getMatrixLoadingDimension()) { case ChartMatrixLoadingDimensionEnum::CHART_MATRIX_LOADING_BY_COLUMN: rowColumnIndexToLoad = columnIndex; break; case ChartMatrixLoadingDimensionEnum::CHART_MATRIX_LOADING_BY_ROW: rowColumnIndexToLoad = rowIndex; break; } /* * If yoked, find other files yoked to the same group */ const YokingGroupEnum::Enum selectedYokingGroup = parcelFile->getYokingGroup(); std::vector parcelFilesToLoadFrom; if (selectedYokingGroup != YokingGroupEnum::YOKING_GROUP_OFF) { for (int32_t i = 0; i < brain->getNumberOfConnectivityMatrixParcelFiles(); i++) { CiftiConnectivityMatrixParcelFile* pf = brain->getConnectivityMatrixParcelFile(i); if (pf->getYokingGroup() == selectedYokingGroup) { parcelFilesToLoadFrom.push_back(pf); } } } else { parcelFilesToLoadFrom.push_back(parcelFile); } PaletteFile* paletteFile = brain->getPaletteFile(); const int32_t mapIndex = 0; /* * Load row/color for the "parcelFile" and any other files with * which it is yoked. */ for (std::vector::iterator iter = parcelFilesToLoadFrom.begin(); iter != parcelFilesToLoadFrom.end(); iter++) { CiftiConnectivityMatrixParcelFile* pf = *iter; switch (pf->getMatrixLoadingDimension()) { case ChartMatrixLoadingDimensionEnum::CHART_MATRIX_LOADING_BY_COLUMN: pf->loadDataForColumnIndex(rowColumnIndexToLoad); pf->updateScalarColoringForMap(mapIndex, paletteFile); rowColumnInformationOut.push_back(pf->getFileNameNoPath() + " column index=" + AString::number(rowColumnIndexToLoad)); break; case ChartMatrixLoadingDimensionEnum::CHART_MATRIX_LOADING_BY_ROW: pf->loadDataForRowIndex(rowColumnIndexToLoad); pf->updateScalarColoringForMap(mapIndex, paletteFile); rowColumnInformationOut.push_back(pf->getFileNameNoPath() + " row index=" + AString::number(rowColumnIndexToLoad)); break; } } return true; } bool CiftiConnectivityMatrixDataFileManager::loadRowOrColumnFromConnectivityMatrixFile(Brain* brain, 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); rowColumnInformationOut.push_back(ciftiConnMatrixFile->getFileNameNoPath() + " row index=" + AString::number(rowIndex)); } else if (columnIndex >= 0) { ciftiConnMatrixFile->loadDataForColumnIndex(columnIndex); ciftiConnMatrixFile->updateScalarColoringForMap(mapIndex, paletteFile); rowColumnInformationOut.push_back(ciftiConnMatrixFile->getFileNameNoPath() + " column index=" + AString::number(columnIndex)); } return true; } /** * Load data for the given surface node index. * @param brain * Brain for which data is loaded. * @param surfaceFile * Surface File that contains the node (uses its structure). * @param nodeIndex * Index of the surface node. * @param rowColumnInformationOut * Appends one string for each row/column loaded * @return * true if any connectivity loaders are active, else false. */ bool CiftiConnectivityMatrixDataFileManager::loadDataForSurfaceNode(Brain* brain, const SurfaceFile* surfaceFile, const int32_t nodeIndex, std::vector& rowColumnInformationOut) { std::vector ciftiMatrixFiles; getDisplayedConnectivityMatrixFiles(brain, ciftiMatrixFiles); PaletteFile* paletteFile = brain->getPaletteFile(); bool haveData = false; for (std::vector::iterator iter = ciftiMatrixFiles.begin(); iter != ciftiMatrixFiles.end(); iter++) { CiftiMappableConnectivityMatrixDataFile* cmf = *iter; if (cmf->isEmpty() == false) { const int32_t mapIndex = 0; int64_t rowIndex = -1; int64_t columnIndex = -1; cmf->loadMapDataForSurfaceNode(mapIndex, surfaceFile->getNumberOfNodes(), surfaceFile->getStructure(), nodeIndex, rowIndex, columnIndex); cmf->updateScalarColoringForMap(mapIndex, paletteFile); haveData = true; if (rowIndex >= 0) { /* * Get row/column info for node */ rowColumnInformationOut.push_back(cmf->getFileNameNoPath() + " nodeIndex=" + AString::number(nodeIndex) + ", row index= " + AString::number(rowIndex)); } else if (columnIndex >= 0) { /* * Get row/column info for node */ rowColumnInformationOut.push_back(cmf->getFileNameNoPath() + " nodeIndex=" + AString::number(nodeIndex) + ", column index= " + AString::number(columnIndex)); } } } if (haveData) { EventManager::get()->sendEvent(EventSurfaceColoringInvalidate().getPointer()); } return haveData; } /** * Load data for each of the given surface node indices and average the data. * @param brain * Brain for which data is loaded. * @param surfaceFile * Surface File that contains the node (uses its structure). * @param nodeIndices * Indices of the surface nodes. * @return * true if any connectivity loaders are active, else false. */ bool CiftiConnectivityMatrixDataFileManager::loadAverageDataForSurfaceNodes(Brain* brain, const SurfaceFile* surfaceFile, const std::vector& nodeIndices) { std::vector ciftiMatrixFiles; getDisplayedConnectivityMatrixFiles(brain, ciftiMatrixFiles); PaletteFile* paletteFile = brain->getPaletteFile(); bool haveData = false; for (std::vector::iterator iter = ciftiMatrixFiles.begin(); iter != ciftiMatrixFiles.end(); iter++) { CiftiMappableConnectivityMatrixDataFile* cmf = *iter; if (cmf->isEmpty() == false) { const int32_t mapIndex = 0; cmf->loadMapAverageDataForSurfaceNodes(mapIndex, surfaceFile->getNumberOfNodes(), surfaceFile->getStructure(), nodeIndices); cmf->updateScalarColoringForMap(mapIndex, paletteFile); haveData = true; } } if (haveData) { EventManager::get()->sendEvent(EventSurfaceColoringInvalidate().getPointer()); } return haveData; } /** * Load data for the voxel near the given coordinate. * @param brain * Brain for which data is loaded. * @param xyz * Coordinate of voxel. * @param rowColumnInformationOut * Appends one string for each row/column loaded * @return * true if any connectivity loaders are active, else false. */ bool CiftiConnectivityMatrixDataFileManager::loadDataForVoxelAtCoordinate(Brain* brain, const float xyz[3], std::vector& rowColumnInformationOut) { PaletteFile* paletteFile = brain->getPaletteFile(); std::vector ciftiMatrixFiles; getDisplayedConnectivityMatrixFiles(brain, ciftiMatrixFiles); bool haveData = false; for (std::vector::iterator iter = ciftiMatrixFiles.begin(); iter != ciftiMatrixFiles.end(); iter++) { CiftiMappableConnectivityMatrixDataFile* cmf = *iter; if (cmf->isEmpty() == false) { const int32_t mapIndex = 0; int64_t rowIndex; int64_t columnIndex; cmf->loadMapDataForVoxelAtCoordinate(mapIndex, xyz, rowIndex, columnIndex); cmf->updateScalarColoringForMap(mapIndex, paletteFile); haveData = true; if (rowIndex >= 0) { /* * Get row/column info for node */ rowColumnInformationOut.push_back(cmf->getFileNameNoPath() + " Voxel XYZ=" + AString::fromNumbers(xyz, 3, ",") + ", row index= " + AString::number(rowIndex)); } else if (columnIndex >= 0) { /* * Get row/column info for node */ rowColumnInformationOut.push_back(cmf->getFileNameNoPath() + " Voxel XYZ=" + AString::fromNumbers(xyz, 3, ",") + ", column index= " + AString::number(columnIndex)); } } } if (haveData) { EventManager::get()->sendEvent(EventSurfaceColoringInvalidate().getPointer()); } return haveData; } /** * Load average data for the given voxel indices. * * @param brain * Brain for which data is loaded. * @param volumeDimensionIJK * Dimensions of the volume. * @param voxelIndices * Indices for averaging of data. * @return * true if any data was loaded, else false. * @throw DataFileException * If an error occurs. */ bool CiftiConnectivityMatrixDataFileManager::loadAverageDataForVoxelIndices(Brain* brain, const int64_t volumeDimensionIJK[3], const std::vector& voxelIndices) { PaletteFile* paletteFile = brain->getPaletteFile(); std::vector ciftiMatrixFiles; getDisplayedConnectivityMatrixFiles(brain, ciftiMatrixFiles); bool haveData = false; for (std::vector::iterator iter = ciftiMatrixFiles.begin(); iter != ciftiMatrixFiles.end(); iter++) { CiftiMappableConnectivityMatrixDataFile* cmf = *iter; if (cmf->isEmpty() == false) { const int32_t mapIndex = 0; if (cmf->loadMapAverageDataForVoxelIndices(mapIndex, volumeDimensionIJK, voxelIndices)) { } haveData = true; cmf->updateScalarColoringForMap(mapIndex, paletteFile); } } if (haveData) { EventManager::get()->sendEvent(EventSurfaceColoringInvalidate().getPointer()); } return haveData; } /** * @param brain * Brain for containing network files. * * @return True if there are enabled connectivity * files that retrieve data from the network. */ bool CiftiConnectivityMatrixDataFileManager::hasNetworkFiles(Brain* brain) const { std::vector ciftiMatrixFiles; getDisplayedConnectivityMatrixFiles(brain, ciftiMatrixFiles); for (std::vector::iterator iter = ciftiMatrixFiles.begin(); iter != ciftiMatrixFiles.end(); iter++) { CiftiMappableConnectivityMatrixDataFile* cmdf = *iter; if (cmdf->isEmpty() == false) { if (DataFile::isFileOnNetwork(cmdf->getFileName())) { const int32_t numMaps = cmdf->getNumberOfMaps(); for (int32_t i = 0; i < numMaps; i++) { if (cmdf->isMapDataLoadingEnabled(i)) { return true; } } } } } return false; } connectome-workbench-1.2.3+git41-gc4c6c90/src/Brain/CiftiConnectivityMatrixDataFileManager.h000066400000000000000000000076631300200146000314730ustar00rootroot00000000000000#ifndef __CIFTI_CONNECTIVITY_MATRIX_DATA_FILE_MANAGER_H__ #define __CIFTI_CONNECTIVITY_MATRIX_DATA_FILE_MANAGER_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 "VoxelIJK.h" namespace caret { class Brain; class CiftiConnectivityMatrixParcelFile; class CiftiMappableConnectivityMatrixDataFile; class SurfaceFile; class CiftiConnectivityMatrixDataFileManager : public CaretObject { public: CiftiConnectivityMatrixDataFileManager(); virtual ~CiftiConnectivityMatrixDataFileManager(); bool loadDataForSurfaceNode(Brain* brain, const SurfaceFile* surfaceFile, const int32_t nodeIndex, std::vector& rowColumnInformationOut); bool loadAverageDataForSurfaceNodes(Brain* brain, const SurfaceFile* surfaceFile, const std::vector& nodeIndices); bool loadDataForVoxelAtCoordinate(Brain* brain, const float xyz[3], std::vector& rowColumnInformationOut); bool loadAverageDataForVoxelIndices(Brain* brain, const int64_t volumeDimensionIJK[3], const std::vector& voxelIndices); bool loadRowOrColumnFromParcelFile(Brain* brain, CiftiConnectivityMatrixParcelFile* ciftiConnMatrixFile, const int32_t rowIndex, const int32_t columnIndex, std::vector& rowColumnInformationOut); bool loadRowOrColumnFromConnectivityMatrixFile(Brain* brain, CiftiMappableConnectivityMatrixDataFile* parcelFile, const int32_t rowIndex, const int32_t columnIndex, std::vector& rowColumnInformationOut); bool hasNetworkFiles(Brain* brain) const; private: CiftiConnectivityMatrixDataFileManager(const CiftiConnectivityMatrixDataFileManager&); CiftiConnectivityMatrixDataFileManager& operator=(const CiftiConnectivityMatrixDataFileManager&); public: // ADD_NEW_METHODS_HERE private: void getDisplayedConnectivityMatrixFiles(Brain* brain, std::vector& ciftiMatrixFilesOut) const; // ADD_NEW_MEMBERS_HERE }; #ifdef __CIFTI_CONNECTIVITY_MATRIX_DATA_FILE_MANAGER_DECLARE__ // #endif // __CIFTI_CONNECTIVITY_MATRIX_DATA_FILE_MANAGER_DECLARE__ } // namespace #endif //__CIFTI_CONNECTIVITY_MATRIX_DATA_FILE_MANAGER_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Brain/CiftiFiberTrajectoryManager.cxx000066400000000000000000000165301300200146000277000ustar00rootroot00000000000000 /*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 __CIFTI_FIBER_TRAJECTORY_MANAGER_DECLARE__ #include "CiftiFiberTrajectoryManager.h" #undef __CIFTI_FIBER_TRAJECTORY_MANAGER_DECLARE__ #include "Brain.h" #include "CaretAssert.h" #include "CiftiFiberOrientationFile.h" #include "CiftiFiberTrajectoryFile.h" #include "SceneAttributes.h" #include "SceneClass.h" #include "SurfaceFile.h" using namespace caret; /** * \class caret::CiftiFiberTrajectoryManager * \brief Manages loading of trajectory data. * \ingroup Brain */ /** * Constructor. */ CiftiFiberTrajectoryManager::CiftiFiberTrajectoryManager() : CaretObject() { } /** * Destructor. */ CiftiFiberTrajectoryManager::~CiftiFiberTrajectoryManager() { } /** * Load data for the given surface node index. * * @param surfaceFile * Surface File that contains the node (uses its structure). * @param nodeIndex * Index of the surface node. * @return * true if any data was loaded, else false. */ bool CiftiFiberTrajectoryManager::loadDataForSurfaceNode(Brain* brain, const SurfaceFile* surfaceFile, const int32_t nodeIndex, std::vector& rowColumnInformationOut) { bool dataWasLoaded = false; /* * Load fiber trajectory data */ const int32_t numTrajFiles = brain->getNumberOfConnectivityFiberTrajectoryFiles(); for (int32_t iTrajFileIndex = 0; iTrajFileIndex < numTrajFiles; iTrajFileIndex++) { CiftiFiberTrajectoryFile* trajFile = brain->getConnectivityFiberTrajectoryFile(iTrajFileIndex); const int64_t rowIndex = trajFile->loadDataForSurfaceNode(surfaceFile->getStructure(), surfaceFile->getNumberOfNodes(), nodeIndex); if (rowIndex >= 0) { /* * Get row/column info for node */ rowColumnInformationOut.push_back(trajFile->getFileNameNoPath() + " nodeIndex=" + AString::number(nodeIndex) + ", row index= " + AString::number(rowIndex)); dataWasLoaded = true; } } return dataWasLoaded; } /** * Load data for the given surface node index. * * @param surfaceFile * Surface File that contains the node (uses its structure). * @param nodeIndex * Index of the surface node. * @return * true if any data was loaded, else false. */ bool CiftiFiberTrajectoryManager::loadDataAverageForSurfaceNodes(Brain* brain, const SurfaceFile* surfaceFile, const std::vector& nodeIndices) { bool dataWasLoaded = false; AString errorMessage; /* * Load fiber trajectory data */ const int32_t numTrajFiles = brain->getNumberOfConnectivityFiberTrajectoryFiles(); for (int32_t iTrajFileIndex = 0; iTrajFileIndex < numTrajFiles; iTrajFileIndex++) { CiftiFiberTrajectoryFile* trajFile = brain->getConnectivityFiberTrajectoryFile(iTrajFileIndex); try { trajFile->loadDataAverageForSurfaceNodes(surfaceFile->getStructure(), surfaceFile->getNumberOfNodes(), nodeIndices); dataWasLoaded = true; } catch (const DataFileException& dfe) { errorMessage.appendWithNewLine(dfe.whatString()); } } if (errorMessage.isEmpty() == false) { throw DataFileException(errorMessage); } return dataWasLoaded; } /** * Load data for the voxel near the given coordinate. * @param xyz * Coordinate of voxel. * @param rowColumnInformationOut * Appends one string for each row/column loaded * @return * true if any connectivity loaders are active, else false. */ bool CiftiFiberTrajectoryManager::loadDataForVoxelAtCoordinate(Brain* brain, const float xyz[3], std::vector& rowColumnInformationOut) { std::vector ciftiTrajFiles; brain->getConnectivityFiberTrajectoryFiles(ciftiTrajFiles); bool haveData = false; for (std::vector::iterator iter = ciftiTrajFiles.begin(); iter != ciftiTrajFiles.end(); iter++) { CiftiFiberTrajectoryFile* trajFile = *iter; if (trajFile->isEmpty() == false) { const int64_t rowIndex = trajFile->loadMapDataForVoxelAtCoordinate(xyz); if (rowIndex >= 0) { /* * Get row/column info for node */ rowColumnInformationOut.push_back(trajFile->getFileNameNoPath() + " Voxel XYZ=" + AString::fromNumbers(xyz, 3, ",") + ", row index= " + AString::number(rowIndex)); haveData = true; } } } return haveData; } /** * Load average data for the given voxel indices. * * @param volumeDimensionIJK * Dimensions of the volume. * @param voxelIndices * Indices for averaging of data. * @return * true if any data was loaded, else false. * @throw DataFileException * If an error occurs. */ bool CiftiFiberTrajectoryManager::loadAverageDataForVoxelIndices(Brain* brain, const int64_t volumeDimensionIJK[3], const std::vector& voxelIndices) { std::vector ciftiTrajFiles; brain->getConnectivityFiberTrajectoryFiles(ciftiTrajFiles); bool haveData = false; for (std::vector::iterator iter = ciftiTrajFiles.begin(); iter != ciftiTrajFiles.end(); iter++) { CiftiFiberTrajectoryFile* trajFile = *iter; if (trajFile->isEmpty() == false) { trajFile->loadMapAverageDataForVoxelIndices(volumeDimensionIJK, voxelIndices); haveData = true; } } return haveData; } connectome-workbench-1.2.3+git41-gc4c6c90/src/Brain/CiftiFiberTrajectoryManager.h000066400000000000000000000051401300200146000273200ustar00rootroot00000000000000#ifndef __CIFTI_FIBER_TRAJECTORY_MANAGER_H__ #define __CIFTI_FIBER_TRAJECTORY_MANAGER_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 "VoxelIJK.h" namespace caret { class Brain; class SurfaceFile; class CiftiFiberTrajectoryManager : public CaretObject { public: CiftiFiberTrajectoryManager(); virtual ~CiftiFiberTrajectoryManager(); bool loadDataForSurfaceNode(Brain* brain, const SurfaceFile* surfaceFile, const int32_t nodeIndex, std::vector& rowColumnInformationOut); bool loadDataAverageForSurfaceNodes(Brain* brain, const SurfaceFile* surfaceFile, const std::vector& nodeIndices); bool loadDataForVoxelAtCoordinate(Brain* brain, const float xyz[3], std::vector& rowColumnInformationOut); bool loadAverageDataForVoxelIndices(Brain* brain, const int64_t volumeDimensionIJK[3], const std::vector& voxelIndices); // ADD_NEW_METHODS_HERE private: CiftiFiberTrajectoryManager(const CiftiFiberTrajectoryManager&); CiftiFiberTrajectoryManager& operator=(const CiftiFiberTrajectoryManager&); // ADD_NEW_MEMBERS_HERE }; #ifdef __CIFTI_FIBER_TRAJECTORY_MANAGER_DECLARE__ // #endif // __CIFTI_FIBER_TRAJECTORY_MANAGER_DECLARE__ } // namespace #endif //__CIFTI_FIBER_TRAJECTORY_MANAGER_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Brain/ClippingPlaneGroup.cxx000066400000000000000000000556121300200146000260760ustar00rootroot00000000000000 /*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 __CLIPPING_PLANE_GROUP_DECLARE__ #include "ClippingPlaneGroup.h" #undef __CLIPPING_PLANE_GROUP_DECLARE__ #include "CaretAssert.h" #include "Plane.h" #include "SceneClass.h" #include "SceneClassArray.h" #include "SceneClassAssistant.h" using namespace caret; /** * \class caret::ClippingPlaneGroup * \brief A group of clipping plane for clipping to a rectangular region * \ingroup Brain */ /** * Constructor. */ ClippingPlaneGroup::ClippingPlaneGroup() : CaretObject() { invalidateActiveClippingPlainEquations(); resetToDefaultValues(); m_sceneAssistant = new SceneClassAssistant(); m_sceneAssistant->add("m_displayClippingBoxStatus", &m_displayClippingBoxStatus); m_sceneAssistant->addArray("m_translation", m_translation, 3, 0.0); m_sceneAssistant->addArray("m_thickness", m_thickness, 3, 20.0); m_sceneAssistant->add("m_xAxisSelectionStatus", &m_xAxisSelectionStatus); m_sceneAssistant->add("m_yAxisSelectionStatus", &m_yAxisSelectionStatus); m_sceneAssistant->add("m_zAxisSelectionStatus", &m_zAxisSelectionStatus); m_sceneAssistant->add("m_surfaceSelectionStatus", &m_surfaceSelectionStatus); m_sceneAssistant->add("m_volumeSelectionStatus", &m_volumeSelectionStatus); m_sceneAssistant->add("m_featuresSelectionStatus", &m_featuresSelectionStatus); } /** * Destructor. */ ClippingPlaneGroup::~ClippingPlaneGroup() { delete m_sceneAssistant; invalidateActiveClippingPlainEquations(); } /** * Copy constructor. * @param obj * Object that is copied. */ ClippingPlaneGroup::ClippingPlaneGroup(const ClippingPlaneGroup& obj) : CaretObject(obj), SceneableInterface() { this->copyHelperClippingPlaneGroup(obj); } /** * Assignment operator. * @param obj * Data copied from obj to this. * @return * Reference to this object. */ ClippingPlaneGroup& ClippingPlaneGroup::operator=(const ClippingPlaneGroup& obj) { if (this != &obj) { CaretObject::operator=(obj); this->copyHelperClippingPlaneGroup(obj); } return *this; } /** * Helps with copying an object of this type. * @param obj * Object that is copied. */ void ClippingPlaneGroup::copyHelperClippingPlaneGroup(const ClippingPlaneGroup& obj) { for (int32_t i = 0; i < 3; i++) { m_translation[i] = obj.m_translation[i]; m_thickness[i] = obj.m_thickness[i]; } m_rotationMatrix = obj.m_rotationMatrix; m_xAxisSelectionStatus = obj.m_xAxisSelectionStatus; m_yAxisSelectionStatus = obj.m_yAxisSelectionStatus; m_zAxisSelectionStatus = obj.m_zAxisSelectionStatus; m_surfaceSelectionStatus = obj.m_surfaceSelectionStatus; m_volumeSelectionStatus = obj.m_volumeSelectionStatus; m_featuresSelectionStatus = obj.m_featuresSelectionStatus; m_displayClippingBoxStatus = obj.m_displayClippingBoxStatus; invalidateActiveClippingPlainEquations(); } /** * Reset the transformation. */ void ClippingPlaneGroup::resetTransformation() { for (int32_t i = 0; i < 3; i++) { m_translation[i] = 0.0; } m_thickness[0] = 180.0; m_thickness[1] = 250.0; m_thickness[2] = 220.0; m_rotationMatrix.identity(); invalidateActiveClippingPlainEquations(); } /** * Reset member values. */ void ClippingPlaneGroup::resetToDefaultValues() { resetTransformation(); m_displayClippingBoxStatus = false; m_xAxisSelectionStatus = false; m_yAxisSelectionStatus = false; m_zAxisSelectionStatus = false; m_surfaceSelectionStatus = true; m_volumeSelectionStatus = true; m_featuresSelectionStatus = true; invalidateActiveClippingPlainEquations(); } /** * @return The X-coordinate for the structure. * * @param structure * The structure. Note that right and left hemispheres are mirror flipped. */ float ClippingPlaneGroup::getXCoordinateForStructure(const StructureEnum::Enum structure) const { float x = m_translation[0]; if (StructureEnum::isLeft(structure)) { x = -x; } return x; } /** * Create the plane equation for the given plane identifier. * * @param planeIdentifier * Identifies plane that is created. * @param structure * The structure. Note that right and left hemispheres are mirror flipped. */ Plane* ClippingPlaneGroup::createClippingPlane(const PlaneIdentifier planeIdentifier, const StructureEnum::Enum structure) const { float normalVector[3] = { 0.0, 0.0, 0.0 }; float pointOnPlane[3] = { 0.0, 0.0, 0.0 }; /* * Note: the planes form a rectangular cuboid and we want to * clip what is OUTSIDE this rectangular cuboid. */ switch (planeIdentifier) { case PLANE_MINIMUM_X: /* * X Minimum */ normalVector[0] = 1.0; pointOnPlane[0] = getXCoordinateForStructure(structure) - (m_thickness[0] / 2.0); break; case PLANE_MAXIMUM_X: /* * X Maximum */ normalVector[0] = -1.0; pointOnPlane[0] = getXCoordinateForStructure(structure) + (m_thickness[0] / 2.0); break; case PLANE_MINIMUM_Y: /* * Y Minimum */ normalVector[1] = 1.0; pointOnPlane[1] = m_translation[1] - (m_thickness[1] / 2.0); break; case PLANE_MAXIMUM_Y: /* * Y Maximum */ normalVector[1] = -1.0; pointOnPlane[1] = m_translation[1] + (m_thickness[1] / 2.0); break; case PLANE_MINIMUM_Z: /* * Z Minimum */ normalVector[2] = 1.0; pointOnPlane[2] = m_translation[2] - (m_thickness[2] / 2.0); break; case PLANE_MAXIMUM_Z: /* * Z Maximum */ normalVector[2] = -1.0; pointOnPlane[2] = m_translation[2] + (m_thickness[2] / 2.0); break; default: CaretAssert(0); } Matrix4x4 rotMat = getRotationMatrixForStructure(structure); rotMat.multiplyPoint3(normalVector); rotMat.multiplyPoint3(pointOnPlane); Plane* plane = new Plane(normalVector, pointOnPlane); return plane; } /** * @return Planes representing the active clipping planes for the given * structure. * * @param structure * The structure. Note that right and left hemispheres are mirror flipped. */ std::vector ClippingPlaneGroup::getActiveClippingPlanesForStructure(const StructureEnum::Enum structure) const { updateActiveClippingPlainEquations(); std::vector planes; if (StructureEnum::isRight(structure)) { planes.insert(planes.end(), m_rightStructureActiveClippingPlanes.begin(), m_rightStructureActiveClippingPlanes.end()); } else { planes.insert(planes.end(), m_activeClippingPlanes.begin(), m_activeClippingPlanes.end()); } return planes; } /** * @return The rotation matrix. * * @param structure * Structure for the rotation matrix. A 'right' structure has rotations * 'mirror flipped'. */ Matrix4x4 ClippingPlaneGroup::getRotationMatrixForStructure(const StructureEnum::Enum structure) const { if (StructureEnum::isRight(structure)) { double rotationX, rotationY, rotationZ; m_rotationMatrix.getRotation(rotationX, rotationY, rotationZ); const int flipMode = 2; switch (flipMode) { case 1: rotationY = 180.0 - rotationY; rotationZ = 180.0 - rotationZ; break; case 2: rotationY = -rotationY; rotationZ = -rotationZ; break; case 3: rotationY = 180.0 - rotationY; rotationZ = -rotationZ; break; case 4: rotationY = - rotationY; rotationZ = 180.0 -rotationZ; break; } Matrix4x4 mat; mat.setRotation(rotationX, rotationY, rotationZ); return mat; // const double rotationFlippedY = 180.0 - rotationY; // const double rotationFlippedZ = 180.0 - rotationZ; // Matrix4x4 mat; // mat.setRotation(rotationX, //rotationFlippedX, // rotationFlippedY, // rotationFlippedZ); // return mat; } return m_rotationMatrix; } /** * Replace the rotation matrix. * * @param rotationMatrix * New rotation matrix. */ void ClippingPlaneGroup::setRotationMatrix(const Matrix4x4& rotationMatrix) { m_rotationMatrix = rotationMatrix; invalidateActiveClippingPlainEquations(); } /** * Get the rotation matrix using the given angles. * * @param rotationAngles * The X, Y, and Z rotation angles. */ void ClippingPlaneGroup::getRotationAngles(float rotationAngles[3]) const { double x, y, z; m_rotationMatrix.getRotation(x, y, z); rotationAngles[0] = x; rotationAngles[1] = y; rotationAngles[2] = z; } /** * Set the rotation matrix using the given angles. * * @param rotationAngles * The X, Y, and Z rotation angles. */ void ClippingPlaneGroup::setRotationAngles(const float rotationAngles[3]) { m_rotationMatrix.setRotation(rotationAngles[0], rotationAngles[1], rotationAngles[2]); invalidateActiveClippingPlainEquations(); } /** * Get the thickness values * * @param thickness * The thickness values. */ void ClippingPlaneGroup::getThickness(float thickness[3]) const { thickness[0] = m_thickness[0]; thickness[1] = m_thickness[1]; thickness[2] = m_thickness[2]; } /** * Set the translation values. * * @param translation * The translation values. */ void ClippingPlaneGroup::setTranslation(const float translation[3]) { m_translation[0] = translation[0]; m_translation[1] = translation[1]; m_translation[2] = translation[2]; invalidateActiveClippingPlainEquations(); } /** * Get the translation values * * @param translation * The translation values. */ void ClippingPlaneGroup::getTranslation(float translation[3]) const { translation[0] = m_translation[0]; translation[1] = m_translation[1]; translation[2] = m_translation[2]; } /** * Get the translation values for the given structure. * * @param structure * The structure. Note that right and left hemispheres are mirror flipped. * @param translation * The translation values. */ void ClippingPlaneGroup::getTranslationForStructure(const StructureEnum::Enum structure, float translation[3]) const { getTranslation(translation); translation[0] = getXCoordinateForStructure(structure); } /** * Set the thickness values. * * @param thickness * The thickness values. */ void ClippingPlaneGroup::setThickness(const float thickness[3]) { m_thickness[0] = thickness[0]; m_thickness[1] = thickness[1]; m_thickness[2] = thickness[2]; invalidateActiveClippingPlainEquations(); } /** * @return Is surface selected for clipping? */ bool ClippingPlaneGroup::isSurfaceSelected() const { return m_surfaceSelectionStatus; } /** * Set surface selected * * @param selected * New status. */ void ClippingPlaneGroup::setSurfaceSelected(const bool selected) { m_surfaceSelectionStatus = selected; invalidateActiveClippingPlainEquations(); } /** * @return Is volume selected for clipping? */ bool ClippingPlaneGroup::isVolumeSelected() const { return m_volumeSelectionStatus; } /** * Set volume selected * * @param selected * New status. */ void ClippingPlaneGroup::setVolumeSelected(const bool selected) { m_volumeSelectionStatus = selected; invalidateActiveClippingPlainEquations(); } /** * @return Is features selected for clipping? */ bool ClippingPlaneGroup::isFeaturesSelected() const { return m_featuresSelectionStatus; } /** * Set display clipping box selected * * @param selected * New status. */ void ClippingPlaneGroup::setDisplayClippingBoxSelected(const bool selected) { m_displayClippingBoxStatus = selected; invalidateActiveClippingPlainEquations(); } /** * @return Is display clipping box selected? */ bool ClippingPlaneGroup::isDisplayClippingBoxSelected() const { return m_displayClippingBoxStatus; } /** * @return Is features and any one or more axes selected for clipping? */ bool ClippingPlaneGroup::isFeaturesAndAnyAxisSelected() const { if (m_featuresSelectionStatus) { if (m_xAxisSelectionStatus || m_yAxisSelectionStatus || m_zAxisSelectionStatus) { return true; } } return false; } /** * Set features selected * * @param selected * New status. */ void ClippingPlaneGroup::setFeaturesSelected(const bool selected) { m_featuresSelectionStatus = selected; } /** * @return Is the X clipping axis selected? */ bool ClippingPlaneGroup::isXAxisSelected() const { return m_xAxisSelectionStatus; } /** * @return Is the Y clipping axis selected? */ bool ClippingPlaneGroup::isYAxisSelected() const { return m_yAxisSelectionStatus; } /** * @return Is the Z clipping axis selected? */ bool ClippingPlaneGroup::isZAxisSelected() const { return m_zAxisSelectionStatus; } /** * Set the selection status for the X-axis. * * @param xAxisSelected * New selection status for the X-axis. */ void ClippingPlaneGroup::setXAxisSelected(const bool xAxisSelected) { m_xAxisSelectionStatus = xAxisSelected; invalidateActiveClippingPlainEquations(); } /** * Set the selection status for the Y-axis. * * @param yAxisSelected * New selection status for the Y-axis. */ void ClippingPlaneGroup::setYAxisSelected(const bool yAxisSelected) { m_yAxisSelectionStatus = yAxisSelected; invalidateActiveClippingPlainEquations(); } /** * Set the selection status for the Z-axis. * * @param zAxisSelected * New selection status for the Z-axis. */ void ClippingPlaneGroup::setZAxisSelected(const bool zAxisSelected) { m_zAxisSelectionStatus = zAxisSelected; invalidateActiveClippingPlainEquations(); } /** * Is the coordinate inside the clipping planes? * * If a clipping plane for an axis is off, the coordinate is considered * to be inside the clipping plane. * * @param structure * The structure. Note that right and left hemispheres are mirror flipped. * @param xyz * The coordinate. * * @return * True if inside the clipping planes, else false. */ bool ClippingPlaneGroup::isCoordinateInsideClippingPlanesForStructure(const StructureEnum::Enum structure, const float xyz[3]) const { updateActiveClippingPlainEquations(); if (StructureEnum::isRight(structure)) { for (std::vector::iterator iter = m_rightStructureActiveClippingPlanes.begin(); iter != m_rightStructureActiveClippingPlanes.end(); iter++) { const Plane* plane = *iter; if (plane->signedDistanceToPlane(xyz) < 0.0) { return false; } } return true; } for (std::vector::iterator iter = m_activeClippingPlanes.begin(); iter != m_activeClippingPlanes.end(); iter++) { const Plane* plane = *iter; if (plane->signedDistanceToPlane(xyz) < 0.0) { return false; } } // USE THE CLIPPING PLANES EQUATIONS !!!! // if (m_xAxisSelectionStatus) { // const float x = getXCoordinateForStructure(structure); // const float minX = x - (m_thickness[0] / 2.0); // const float maxX = x + (m_thickness[0] / 2.0); // if (xyz[0] < minX) return false; // if (xyz[0] > maxX) return false; // } // // if (m_yAxisSelectionStatus) { // const float minY = m_translation[1] - (m_thickness[1] / 2.0); // const float maxY = m_translation[1] + (m_thickness[1] / 2.0); // if (xyz[1] < minY) return false; // if (xyz[1] > maxY) return false; // } // // if (m_zAxisSelectionStatus) { // const float minZ = m_translation[2] - (m_thickness[2] / 2.0); // const float maxZ = m_translation[2] + (m_thickness[2] / 2.0); // if (xyz[2] < minZ) return false; // if (xyz[2] > maxZ) return false; // } return true; } /** * Get a description of this object's content. * @return String describing this object's content. */ AString ClippingPlaneGroup::toString() const { return "ClippingPlaneGroup"; } /** * Update the active clipping planes. */ void ClippingPlaneGroup::updateActiveClippingPlainEquations() const { if (m_activeClippingPlanesValid) { return; } if (m_xAxisSelectionStatus) { m_activeClippingPlanes.push_back(createClippingPlane(PLANE_MINIMUM_X, StructureEnum::CORTEX_LEFT)); m_activeClippingPlanes.push_back(createClippingPlane(PLANE_MAXIMUM_X, StructureEnum::CORTEX_LEFT)); m_rightStructureActiveClippingPlanes.push_back(createClippingPlane(PLANE_MINIMUM_X, StructureEnum::CORTEX_RIGHT)); m_rightStructureActiveClippingPlanes.push_back(createClippingPlane(PLANE_MAXIMUM_X, StructureEnum::CORTEX_RIGHT)); } if (m_yAxisSelectionStatus) { m_activeClippingPlanes.push_back(createClippingPlane(PLANE_MINIMUM_Y, StructureEnum::CORTEX_LEFT)); m_activeClippingPlanes.push_back(createClippingPlane(PLANE_MAXIMUM_Y, StructureEnum::CORTEX_LEFT)); m_rightStructureActiveClippingPlanes.push_back(createClippingPlane(PLANE_MINIMUM_Y, StructureEnum::CORTEX_RIGHT)); m_rightStructureActiveClippingPlanes.push_back(createClippingPlane(PLANE_MAXIMUM_Y, StructureEnum::CORTEX_RIGHT)); } if (m_zAxisSelectionStatus) { m_activeClippingPlanes.push_back(createClippingPlane(PLANE_MINIMUM_Z, StructureEnum::CORTEX_LEFT)); m_activeClippingPlanes.push_back(createClippingPlane(PLANE_MAXIMUM_Z, StructureEnum::CORTEX_LEFT)); m_rightStructureActiveClippingPlanes.push_back(createClippingPlane(PLANE_MINIMUM_Z, StructureEnum::CORTEX_RIGHT)); m_rightStructureActiveClippingPlanes.push_back(createClippingPlane(PLANE_MAXIMUM_Z, StructureEnum::CORTEX_RIGHT)); } m_activeClippingPlanesValid = true; } /** * Invalidate and remove all of the active clipping planes. */ void ClippingPlaneGroup::invalidateActiveClippingPlainEquations() { m_activeClippingPlanesValid = false; for (std::vector::iterator iter = m_activeClippingPlanes.begin(); iter != m_activeClippingPlanes.end(); iter++) { delete *iter; } m_activeClippingPlanes.clear(); for (std::vector::iterator iter = m_rightStructureActiveClippingPlanes.begin(); iter != m_rightStructureActiveClippingPlanes.end(); iter++) { delete *iter; } m_rightStructureActiveClippingPlanes.clear(); } /** * 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* ClippingPlaneGroup::saveToScene(const SceneAttributes* sceneAttributes, const AString& instanceName) { SceneClass* sceneClass = new SceneClass(instanceName, "ClippingPlaneGroup", 1); m_sceneAssistant->saveMembers(sceneAttributes, sceneClass); float m[4][4]; m_rotationMatrix.getMatrix(m); sceneClass->addFloatArray("m_rotationMatrix", &m[0][0], 16); // 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 ClippingPlaneGroup::restoreFromScene(const SceneAttributes* sceneAttributes, const SceneClass* sceneClass) { resetToDefaultValues(); if (sceneClass == NULL) { return; } m_sceneAssistant->restoreMembers(sceneAttributes, sceneClass); float m[4][4]; const int32_t numElem = sceneClass->getFloatArrayValue("m_rotationMatrix", &m[0][0], 16); if (numElem == 16) { m_rotationMatrix.setMatrix(m); } else { m_rotationMatrix.identity(); } //Uncomment if sub-classes must restore from scene //restoreSubClassDataFromScene(sceneAttributes, // sceneClass); } connectome-workbench-1.2.3+git41-gc4c6c90/src/Brain/ClippingPlaneGroup.h000066400000000000000000000141651300200146000255210ustar00rootroot00000000000000#ifndef __CLIPPING_PLANE_GROUP_H__ #define __CLIPPING_PLANE_GROUP_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 "Matrix4x4.h" #include "SceneableInterface.h" #include "StructureEnum.h" namespace caret { class Plane; class SceneClassAssistant; class ClippingPlaneGroup : public CaretObject, public SceneableInterface { public: ClippingPlaneGroup(); virtual ~ClippingPlaneGroup(); ClippingPlaneGroup(const ClippingPlaneGroup& obj); ClippingPlaneGroup& operator=(const ClippingPlaneGroup& obj); void resetTransformation(); void resetToDefaultValues(); std::vector getActiveClippingPlanesForStructure(const StructureEnum::Enum structure) const; void getTranslation(float translation[3]) const; void setTranslation(const float translation[3]); void getTranslationForStructure(const StructureEnum::Enum structure, float translation[3]) const; Matrix4x4 getRotationMatrixForStructure(const StructureEnum::Enum structure) const; void setRotationMatrix(const Matrix4x4& rotationMatrix); void getRotationAngles(float rotationAngles[3]) const; void setRotationAngles(const float rotationAngles[3]); void setRotation(const float rotation[4][4]); void getThickness(float thickness[3]) const; void setThickness(const float thickness[3]); bool isXAxisSelected() const; bool isYAxisSelected() const; bool isZAxisSelected() const; void setXAxisSelected(const bool xAxisSelected); void setYAxisSelected(const bool yAxisSelected); void setZAxisSelected(const bool zAxisSelected); bool isDisplayClippingBoxSelected() const; void setDisplayClippingBoxSelected(const bool selected); bool isSurfaceSelected() const; void setSurfaceSelected(const bool selected); bool isVolumeSelected() const; void setVolumeSelected(const bool selected); bool isFeaturesSelected() const; bool isFeaturesAndAnyAxisSelected() const; void setFeaturesSelected(const bool selected); bool isCoordinateInsideClippingPlanesForStructure(const StructureEnum::Enum structure, const float xyz[3]) 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 implemetation by sub-classes. // protected: // virtual void saveSubClassDataToScene(const SceneAttributes* sceneAttributes, // SceneClass* sceneClass) = 0; // // virtual void restoreSubClassDataFromScene(const SceneAttributes* sceneAttributes, // const SceneClass* sceneClass) = 0; private: enum PlaneIdentifier { PLANE_MINIMUM_X, PLANE_MAXIMUM_X, PLANE_MINIMUM_Y, PLANE_MAXIMUM_Y, PLANE_MINIMUM_Z, PLANE_MAXIMUM_Z }; void copyHelperClippingPlaneGroup(const ClippingPlaneGroup& obj); Plane* createClippingPlane(const PlaneIdentifier planeIdentifier, const StructureEnum::Enum structure) const; void updateActiveClippingPlainEquations() const; void invalidateActiveClippingPlainEquations(); /** * For all everthing EXCEPT right structures */ mutable std::vector m_activeClippingPlanes; /** * Mirror flipped for RIGHT structure */ mutable std::vector m_rightStructureActiveClippingPlanes; mutable bool m_activeClippingPlanesValid; SceneClassAssistant* m_sceneAssistant; float getXCoordinateForStructure(const StructureEnum::Enum structure) const; float m_translation[3]; Matrix4x4 m_rotationMatrix; float m_thickness[3]; bool m_xAxisSelectionStatus; bool m_yAxisSelectionStatus; bool m_zAxisSelectionStatus; bool m_surfaceSelectionStatus; bool m_volumeSelectionStatus; bool m_featuresSelectionStatus; bool m_displayClippingBoxStatus; // ADD_NEW_MEMBERS_HERE }; #ifdef __CLIPPING_PLANE_GROUP_DECLARE__ // #endif // __CLIPPING_PLANE_GROUP_DECLARE__ } // namespace #endif //__CLIPPING_PLANE_GROUP_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Brain/DisplayProperties.cxx000066400000000000000000000030321300200146000260030ustar00rootroot00000000000000 /*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 __DISPLAY_PROPERTIES_DECLARE__ #include "DisplayProperties.h" #undef __DISPLAY_PROPERTIES_DECLARE__ #include "SceneClassAssistant.h" using namespace caret; /** * \class DisplayProperties * \brief Base class for all display properties. * * Base class for all display properties. */ /** * Constructor. */ DisplayProperties::DisplayProperties() : CaretObject() { m_sceneAssistant = new SceneClassAssistant(); } /** * Destructor. */ DisplayProperties::~DisplayProperties() { delete m_sceneAssistant; } /** * Get a description of this object's content. * @return String describing this object's content. */ AString DisplayProperties::toString() const { return "DisplayProperties"; } connectome-workbench-1.2.3+git41-gc4c6c90/src/Brain/DisplayProperties.h000066400000000000000000000051421300200146000254340ustar00rootroot00000000000000#ifndef __DISPLAY_PROPERTIES__H_ #define __DISPLAY_PROPERTIES__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 "SceneableInterface.h" namespace caret { class SceneClassAssistant; class DisplayProperties : public CaretObject, public SceneableInterface { protected: DisplayProperties(); public: virtual ~DisplayProperties(); /** * Reset all settings to their defaults * and remove any data. */ virtual void reset() = 0; /** * Update due to changes in data. */ virtual void update() = 0; /** * Copy the display properties. * * @param sourceTabIndex * Index of tab from which properties are copied. * @param targetTabIndex * Index of tab to which properties are copied. */ virtual void copyDisplayProperties(const int32_t sourceTabIndex, const int32_t targetTabIndex) = 0; virtual SceneClass* saveToScene(const SceneAttributes* sceneAttributes, const AString& instanceName) = 0; virtual void restoreFromScene(const SceneAttributes* sceneAttributes, const SceneClass* sceneClass) = 0; private: DisplayProperties(const DisplayProperties&); DisplayProperties& operator=(const DisplayProperties&); public: virtual AString toString() const; protected: SceneClassAssistant* m_sceneAssistant; }; #ifdef __DISPLAY_PROPERTIES_DECLARE__ // #endif // __DISPLAY_PROPERTIES_DECLARE__ } // namespace #endif //__DISPLAY_PROPERTIES__H_ connectome-workbench-1.2.3+git41-gc4c6c90/src/Brain/DisplayPropertiesAnnotation.cxx000066400000000000000000000404531300200146000300460ustar00rootroot00000000000000 /*LICENSE_START*/ /* * Copyright (C) 2015 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 __DISPLAY_PROPERTIES_ANNOTATION_DECLARE__ #include "DisplayPropertiesAnnotation.h" #undef __DISPLAY_PROPERTIES_ANNOTATION_DECLARE__ #include "Annotation.h" #include "AnnotationManager.h" #include "Brain.h" #include "CaretLogger.h" #include "SceneAttributes.h" #include "SceneClass.h" #include "SceneClassArray.h" #include "SceneClassAssistant.h" #include "SceneObjectMapIntegerKey.h" using namespace caret; /** * \class caret::DisplayPropertiesAnnotation * \brief Contains display properties for annotations. * \ingroup Brain */ /** * Constructor. */ DisplayPropertiesAnnotation::DisplayPropertiesAnnotation(Brain* parentBrain) : DisplayProperties(), m_parentBrain(parentBrain) { CaretAssert(parentBrain); resetPrivate(); m_sceneAssistant->add("m_displayAnnotations", &m_displayAnnotations); m_sceneAssistant->add("m_displayTextAnnotations", &m_displayTextAnnotations); m_sceneAssistant->addTabIndexedEnumeratedTypeArray("m_displayGroup", m_displayGroup); m_sceneAssistant->addArray("m_displayWindowAnnotationsInSingleTabViews", m_displayWindowAnnotationsInSingleTabViews, BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_WINDOWS, true); } /** * Destructor. */ DisplayPropertiesAnnotation::~DisplayPropertiesAnnotation() { } /** * Update the properties for a new/modified annotation. * * @param annotation * The new/updated annotation. */ void DisplayPropertiesAnnotation::updateForNewAnnotation(const Annotation* annotation) { CaretAssert(annotation); switch (annotation->getCoordinateSpace()) { case AnnotationCoordinateSpaceEnum::PIXELS: break; case AnnotationCoordinateSpaceEnum::STEREOTAXIC: break; case AnnotationCoordinateSpaceEnum::SURFACE: break; case AnnotationCoordinateSpaceEnum::TAB: break; case AnnotationCoordinateSpaceEnum::WINDOW: setDisplayWindowAnnotationsInSingleTabViews(annotation->getWindowIndex(), true); break; } setDisplayAnnotations(true); } /** * Copy the border display properties from one tab to another. * @param sourceTabIndex * Index of tab from which properties are copied. * @param targetTabIndex * Index of tab to which properties are copied. */ void DisplayPropertiesAnnotation::copyDisplayProperties(const int32_t sourceTabIndex, const int32_t targetTabIndex) { const DisplayGroupEnum::Enum displayGroup = this->getDisplayGroupForTab(sourceTabIndex); this->setDisplayGroupForTab(targetTabIndex, displayGroup); } /** * Reset all settings to default. * NOTE: reset() is virtual so can/should not be called from constructor. */ void DisplayPropertiesAnnotation::resetPrivate() { m_displayAnnotations = true; m_displayTextAnnotations = true; for (int32_t i = 0; i < BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_WINDOWS; i++) { m_displayWindowAnnotationsInSingleTabViews[i] = true; } for (int32_t i = 0; i < BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS; i++) { m_displayGroup[i] = DisplayGroupEnum::getDefaultValue(); } } /** * Reset all settings to their defaults * and remove any data. */ void DisplayPropertiesAnnotation::reset() { resetPrivate(); } /** * Update due to changes in data. */ void DisplayPropertiesAnnotation::update() { } /** * @return Status for displaying annotations */ bool DisplayPropertiesAnnotation::isDisplayAnnotations() const { return m_displayAnnotations; } /** * Set the display status for annotations * * @param status * New display status. */ void DisplayPropertiesAnnotation::setDisplayAnnotations(const bool status) { m_displayAnnotations = status; } /** * @return Status for displaying text annotations */ bool DisplayPropertiesAnnotation::isDisplayTextAnnotations() const { return m_displayTextAnnotations; } /** * Set the display status for text annotations * * @param status * New display status. */ void DisplayPropertiesAnnotation::setDisplayTextAnnotations(const bool status) { m_displayTextAnnotations = status; } /** * Get the display group for a given browser tab. * @param browserTabIndex * Index of browser tab. */ DisplayGroupEnum::Enum DisplayPropertiesAnnotation::getDisplayGroupForTab(const int32_t browserTabIndex) const { CaretAssertArrayIndex(this->displayGroup, BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS, browserTabIndex); return m_displayGroup[browserTabIndex]; } /** * Set the display group for a given browser tab. * @param browserTabIndex * Index of browser tab. * @param displayGroup * New value for display group. */ void DisplayPropertiesAnnotation::setDisplayGroupForTab(const int32_t browserTabIndex, const DisplayGroupEnum::Enum displayGroup) { CaretAssertArrayIndex(this->displayGroup, BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS, browserTabIndex); m_displayGroup[browserTabIndex] = displayGroup; } /** * Is the window annotation displayed in the given window index? * * @param windowIndex * Index of the window. * @return * True if displayed, else false. */ bool DisplayPropertiesAnnotation::isDisplayWindowAnnotationsInSingleTabViews(const int32_t windowIndex) const { CaretAssertArrayIndex(m_displayWindowAnnotationsInSingleTabViews, BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_WINDOWS, windowIndex); return m_displayWindowAnnotationsInSingleTabViews[windowIndex]; } /** * Set the window annotation displayed in the given window index. * * @param windowIndex * Index of the tab. * @param status * True if displayed, else false. */ void DisplayPropertiesAnnotation::setDisplayWindowAnnotationsInSingleTabViews(const int32_t windowIndex, const bool status) { CaretAssertArrayIndex(m_displayWindowAnnotationsInSingleTabViews, BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_WINDOWS, windowIndex); m_displayWindowAnnotationsInSingleTabViews[windowIndex] = status; } /** * Create a scene for an instance of a class. * * @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. * * @return Pointer to SceneClass object representing the state of * this object. Under some circumstances a NULL pointer may be * returned. Caller will take ownership of returned object. */ SceneClass* DisplayPropertiesAnnotation::saveToScene(const SceneAttributes* sceneAttributes, const AString& instanceName) { const std::vector tabIndices = sceneAttributes->getIndicesOfTabsForSavingToScene(); SceneClass* sceneClass = new SceneClass(instanceName, "DisplayPropertiesAnnotation", 2); m_sceneAssistant->saveMembers(sceneAttributes, sceneClass); switch (sceneAttributes->getSceneType()) { case SceneTypeEnum::SCENE_TYPE_FULL: break; case SceneTypeEnum::SCENE_TYPE_GENERIC: break; } return sceneClass; } /** * Restore the state of an instance of a class. * * @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 containing the state that was previously * saved and should be restored. */ void DisplayPropertiesAnnotation::restoreFromScene(const SceneAttributes* sceneAttributes, const SceneClass* sceneClass) { if (sceneClass == NULL) { return; } m_sceneAssistant->restoreMembers(sceneAttributes, sceneClass); if (sceneClass->getVersionNumber() <= 1) { restoreVersionOne(sceneClass); } switch (sceneAttributes->getSceneType()) { case SceneTypeEnum::SCENE_TYPE_FULL: break; case SceneTypeEnum::SCENE_TYPE_GENERIC: break; } } /** * Restore version one that contains a type display for each tab * (before the file->group->annotation hierarchy). * * @param sceneClass * SceneClass containing the state that was previously * saved and should be restored. */ void DisplayPropertiesAnnotation::restoreVersionOne(const SceneClass* sceneClass) { m_displayTextAnnotations = true; /* * Version one did not have display groups so default the display group * in each to to "Tab". */ for (int32_t i = 0; i < BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS; i++) { m_displayGroup[i] = DisplayGroupEnum::DISPLAY_GROUP_TAB; } /* * Default version one selections to "on" */ bool stereoStatusInTab[BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS]; bool surfaceStatusInTab[BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS]; bool tabStatusInTab[BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS]; for (int32_t i = 0; i < BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS; i++) { stereoStatusInTab[i] = true; 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 */ const SceneObjectMapIntegerKey* stereoAnnDisplay = sceneClass->getMapIntegerKey("m_displayModelAnnotations"); if (stereoAnnDisplay != NULL) { const std::vector mapKeys = stereoAnnDisplay->getKeys(); for (std::vector::const_iterator keyIter = mapKeys.begin(); keyIter != mapKeys.end(); keyIter++) { stereoStatusInTab[*keyIter] = stereoAnnDisplay->booleanValue(*keyIter); } } const SceneObjectMapIntegerKey* surfaceAnnDisplay = sceneClass->getMapIntegerKey("m_displaySurfaceAnnotations"); if (surfaceAnnDisplay != NULL) { const std::vector mapKeys = surfaceAnnDisplay->getKeys(); for (std::vector::const_iterator keyIter = mapKeys.begin(); keyIter != mapKeys.end(); keyIter++) { surfaceStatusInTab[*keyIter] = surfaceAnnDisplay->booleanValue(*keyIter); } } const SceneObjectMapIntegerKey* tabAnnDisplay = sceneClass->getMapIntegerKey("m_displayTabAnnotations"); if (tabAnnDisplay != NULL) { const std::vector mapKeys = tabAnnDisplay->getKeys(); for (std::vector::const_iterator keyIter = mapKeys.begin(); keyIter != mapKeys.end(); keyIter++) { tabStatusInTab[*keyIter] = tabAnnDisplay->booleanValue(*keyIter); } } bool windowStatus[BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_WINDOWS]; const int32_t numWindowStatus = sceneClass->getBooleanArrayValue("m_displayWindowAnnotations", windowStatus, BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_WINDOWS); /* * Apply version one selections to the annotations. */ std::vector allAnnotations = m_parentBrain->getAnnotationManager()->getAllAnnotations(); for (std::vector::iterator annIter = allAnnotations.begin(); annIter != allAnnotations.end(); annIter++) { Annotation* ann = *annIter; switch (ann->getCoordinateSpace()) { case AnnotationCoordinateSpaceEnum::PIXELS: CaretAssert(0); break; case AnnotationCoordinateSpaceEnum::STEREOTAXIC: if (stereoAnnDisplay != NULL) { for (int32_t iTab = 0; iTab < BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS; iTab++) { TriStateSelectionStatusEnum::Enum triStatus = TriStateSelectionStatusEnum::UNSELECTED; if (stereoStatusInTab[iTab]) { triStatus = TriStateSelectionStatusEnum::SELECTED; } ann->setItemDisplaySelected(DisplayGroupEnum::DISPLAY_GROUP_TAB, iTab, triStatus); } } break; case AnnotationCoordinateSpaceEnum::SURFACE: if (surfaceAnnDisplay != NULL) { for (int32_t iTab = 0; iTab < BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS; iTab++) { TriStateSelectionStatusEnum::Enum triStatus = TriStateSelectionStatusEnum::UNSELECTED; if (surfaceStatusInTab[iTab]) { triStatus = TriStateSelectionStatusEnum::SELECTED; } ann->setItemDisplaySelected(DisplayGroupEnum::DISPLAY_GROUP_TAB, iTab, triStatus); } } break; case AnnotationCoordinateSpaceEnum::TAB: if (tabAnnDisplay != NULL) { const int32_t tabIndex = ann->getTabIndex(); if ((tabIndex >= 0) && (tabIndex < BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS)) { TriStateSelectionStatusEnum::Enum triStatus = TriStateSelectionStatusEnum::UNSELECTED; if (tabStatusInTab[tabIndex]) { triStatus = TriStateSelectionStatusEnum::SELECTED; } ann->setItemDisplaySelected(DisplayGroupEnum::DISPLAY_GROUP_TAB, tabIndex, triStatus); } } break; case AnnotationCoordinateSpaceEnum::WINDOW: if (numWindowStatus) { const int32_t windowIndex = ann->getWindowIndex(); if ((windowIndex >= 0) && (windowIndex < BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_WINDOWS)) { TriStateSelectionStatusEnum::Enum triStatus = TriStateSelectionStatusEnum::UNSELECTED; if (windowStatus[windowIndex]) { triStatus = TriStateSelectionStatusEnum::SELECTED; } /* * Note: For window annotations, the display group * and tab are ignored. Window status is set using * the window index contained in the annotation. */ const int32_t tabIndex = 0; ann->setItemDisplaySelected(DisplayGroupEnum::DISPLAY_GROUP_TAB, tabIndex, triStatus); } } break; } } } connectome-workbench-1.2.3+git41-gc4c6c90/src/Brain/DisplayPropertiesAnnotation.h000066400000000000000000000070141300200146000274670ustar00rootroot00000000000000#ifndef __DISPLAY_PROPERTIES_ANNOTATION_H__ #define __DISPLAY_PROPERTIES_ANNOTATION_H__ /*LICENSE_START*/ /* * Copyright (C) 2015 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 "DisplayGroupEnum.h" #include "DisplayProperties.h" namespace caret { class Annotation; class Brain; class DisplayPropertiesAnnotation : public DisplayProperties { public: DisplayPropertiesAnnotation(Brain* parentBrain); virtual ~DisplayPropertiesAnnotation(); virtual void reset(); virtual void update(); bool isDisplayAnnotations() const; void setDisplayAnnotations(const bool status); bool isDisplayTextAnnotations() const; void setDisplayTextAnnotations(const bool status); DisplayGroupEnum::Enum getDisplayGroupForTab(const int32_t browserTabIndex) const; void setDisplayGroupForTab(const int32_t browserTabIndex, const DisplayGroupEnum::Enum displayGroup); bool isDisplayWindowAnnotationsInSingleTabViews(const int32_t windowIndex) const; void setDisplayWindowAnnotationsInSingleTabViews(const int32_t windowIndex, const bool status); void updateForNewAnnotation(const Annotation* annotation); virtual void copyDisplayProperties(const int32_t sourceTabIndex, const int32_t targetTabIndex); virtual SceneClass* saveToScene(const SceneAttributes* sceneAttributes, const AString& instanceName); virtual void restoreFromScene(const SceneAttributes* sceneAttributes, const SceneClass* sceneClass); // ADD_NEW_METHODS_HERE private: DisplayPropertiesAnnotation(const DisplayPropertiesAnnotation&); DisplayPropertiesAnnotation& operator=(const DisplayPropertiesAnnotation&); void resetPrivate(); void restoreVersionOne(const SceneClass* sceneClass); Brain* m_parentBrain; bool m_displayAnnotations; bool m_displayTextAnnotations; DisplayGroupEnum::Enum m_displayGroup[BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS]; bool m_displayWindowAnnotationsInSingleTabViews[BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_WINDOWS]; // ADD_NEW_MEMBERS_HERE }; #ifdef __DISPLAY_PROPERTIES_ANNOTATION_DECLARE__ // #endif // __DISPLAY_PROPERTIES_ANNOTATION_DECLARE__ } // namespace #endif //__DISPLAY_PROPERTIES_ANNOTATION_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Brain/DisplayPropertiesBorders.cxx000066400000000000000000000755121300200146000273400ustar00rootroot00000000000000 /*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 __DISPLAY_PROPERTIES_BORDERS_DECLARE__ #include "DisplayPropertiesBorders.h" #undef __DISPLAY_PROPERTIES_BORDERS_DECLARE__ #include "CaretAssert.h" #include "SceneAttributes.h" #include "SceneClass.h" #include "SceneClassAssistant.h" using namespace caret; /** * \class caret::DisplayPropertiesBorders * \brief Contains display properties for borders. * * Border display properties are available for every tab and also a * few 'display groups'. A number of methods in this class accept * both display group and tab index parameters. When the display * group is set to 'Tab', the tab index is used meaning that the * attribute requeted/sent is for use with a specifc tab. For an * other display group value, the attribute is for a display group * and the tab index is ignored. */ /** * Constructor. */ DisplayPropertiesBorders::DisplayPropertiesBorders() : DisplayProperties() { m_aboveSurfaceOffset = 0.0; const float defaultPointSize = 2.0; const float defaultLineSize = 1.0; const BorderDrawingTypeEnum::Enum defaultDrawingType = BorderDrawingTypeEnum::DRAW_AS_POINTS_SPHERES; const FeatureColoringTypeEnum::Enum defaultColoringType = FeatureColoringTypeEnum::FEATURE_COLORING_TYPE_NAME; const CaretColorEnum::Enum defaultColor = CaretColorEnum::BLACK; const float defaultUnstretchedLinesLength = 5.0; const bool defaultUnstretchedLinesSelection = true; for (int32_t i = 0; i < BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS; i++) { m_displayGroup[i] = DisplayGroupEnum::getDefaultValue(); m_displayStatusInTab[i] = false; m_contralateralDisplayStatusInTab[i] = false; m_lineWidthInTab[i] = defaultLineSize; m_pointSizeInTab[i] = defaultPointSize; m_coloringTypeInTab[i] = defaultColoringType; m_drawingTypeInTab[i] = defaultDrawingType; m_standardColorTypeInTab[i] = defaultColor; m_unstretchedLinesLengthInTab[i] = defaultUnstretchedLinesLength; m_unstretchedLinesStatusInTab[i] = defaultUnstretchedLinesSelection; } for (int32_t i = 0; i < DisplayGroupEnum::NUMBER_OF_GROUPS; i++) { m_displayStatusInDisplayGroup[i] = false; m_contralateralDisplayStatusInDisplayGroup[i] = false; m_lineWidthInDisplayGroup[i] = defaultLineSize; m_pointSizeInDisplayGroup[i] = defaultPointSize; m_coloringTypeInDisplayGroup[i] = defaultColoringType; m_drawingTypeInDisplayGroup[i] = defaultDrawingType; m_standardColorTypeInDisplayGroup[i] = defaultColor; m_unstretchedLinesLengthInDisplayGroup[i] = defaultUnstretchedLinesLength; m_unstretchedLinesStatusInDisplayGroup[i] = defaultUnstretchedLinesSelection; } m_sceneAssistant->addTabIndexedEnumeratedTypeArray("m_displayGroup", m_displayGroup); m_sceneAssistant->addTabIndexedBooleanArray("m_displayStatusInTab", m_displayStatusInTab); m_sceneAssistant->addTabIndexedBooleanArray("m_contralateralDisplayStatusInTab", m_contralateralDisplayStatusInTab); m_sceneAssistant->addTabIndexedFloatArray("m_lineWidthInTab", m_lineWidthInTab); m_sceneAssistant->addTabIndexedFloatArray("m_pointSizeInTab", m_pointSizeInTab); m_sceneAssistant->addArray("m_displayStatusInDisplayGroup", m_displayStatusInDisplayGroup, DisplayGroupEnum::NUMBER_OF_GROUPS, m_displayStatusInDisplayGroup[0]); m_sceneAssistant->addArray("m_contralateralDisplayStatusInDisplayGroup", m_contralateralDisplayStatusInDisplayGroup, DisplayGroupEnum::NUMBER_OF_GROUPS, m_contralateralDisplayStatusInDisplayGroup[0]); m_sceneAssistant->addArray("m_lineWidthInDisplayGroup", m_lineWidthInDisplayGroup, DisplayGroupEnum::NUMBER_OF_GROUPS, defaultLineSize); m_sceneAssistant->addArray("m_pointSizeInDisplayGroup", m_pointSizeInDisplayGroup, DisplayGroupEnum::NUMBER_OF_GROUPS, defaultPointSize); m_sceneAssistant->addArray("m_coloringTypeInDisplayGroup", m_coloringTypeInDisplayGroup, DisplayGroupEnum::NUMBER_OF_GROUPS, defaultColoringType); m_sceneAssistant->addTabIndexedEnumeratedTypeArray("m_coloringTypeInTab", m_coloringTypeInTab); m_sceneAssistant->addArray("m_drawingTypeInDisplayGroup", m_drawingTypeInDisplayGroup, DisplayGroupEnum::NUMBER_OF_GROUPS, BorderDrawingTypeEnum::DRAW_AS_POINTS_SPHERES); m_sceneAssistant->addTabIndexedEnumeratedTypeArray("m_drawingTypeInTab", m_drawingTypeInTab); m_sceneAssistant->addTabIndexedFloatArray("m_unstretchedLinesLengthInTab", m_unstretchedLinesLengthInTab); m_sceneAssistant->addTabIndexedBooleanArray("m_unstretchedLinesStatusInTab", m_unstretchedLinesStatusInTab); m_sceneAssistant->addArray("m_unstretchedLinesLengthInDisplayGroup", m_unstretchedLinesLengthInDisplayGroup, DisplayGroupEnum::NUMBER_OF_GROUPS, defaultUnstretchedLinesLength); m_sceneAssistant->addArray("m_unstretchedLinesStatusInDisplayGroup", m_unstretchedLinesStatusInDisplayGroup, DisplayGroupEnum::NUMBER_OF_GROUPS, defaultUnstretchedLinesSelection); m_sceneAssistant->add("m_aboveSurfaceOffset", &m_aboveSurfaceOffset); m_sceneAssistant->addArray("m_standardColorTypeInDisplayGroup", m_standardColorTypeInDisplayGroup, DisplayGroupEnum::NUMBER_OF_GROUPS, defaultColor); m_sceneAssistant->addTabIndexedEnumeratedTypeArray("m_standardColorTypeInTab", m_standardColorTypeInTab); } /** * Destructor. */ DisplayPropertiesBorders::~DisplayPropertiesBorders() { } /** * Reset all settings to their defaults * and remove any data. */ void DisplayPropertiesBorders::reset() { // for (int32_t i = 0; i < BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS; i++) { // this->displayStatus[i] = false; // this->contralateralDisplayStatus[i] = false; // this->displayGroup[i] = DisplayGroupEnum::DISPLAY_ALL_WINDOWS; // } } /** * Update due to changes in data. */ void DisplayPropertiesBorders::update() { } /** * Copy the border display properties from one tab to another. * @param sourceTabIndex * Index of tab from which properties are copied. * @param targetTabIndex * Index of tab to which properties are copied. */ void DisplayPropertiesBorders::copyDisplayProperties(const int32_t sourceTabIndex, const int32_t targetTabIndex) { const DisplayGroupEnum::Enum displayGroup = this->getDisplayGroupForTab(sourceTabIndex); this->setDisplayGroupForTab(targetTabIndex, displayGroup); this->m_contralateralDisplayStatusInTab[targetTabIndex] = this->m_contralateralDisplayStatusInTab[sourceTabIndex]; this->m_displayStatusInTab[targetTabIndex] = this->m_displayStatusInTab[sourceTabIndex]; this->m_drawingTypeInTab[targetTabIndex] = this->m_drawingTypeInTab[sourceTabIndex]; this->m_lineWidthInTab[targetTabIndex] = this->m_lineWidthInTab[sourceTabIndex]; this->m_pointSizeInTab[targetTabIndex] = this->m_pointSizeInTab[sourceTabIndex]; this->m_unstretchedLinesLengthInTab[targetTabIndex] = this->m_unstretchedLinesLengthInTab[sourceTabIndex]; this->m_standardColorTypeInDisplayGroup[targetTabIndex] = this->m_standardColorTypeInDisplayGroup[sourceTabIndex]; } /** * @return Display status of borders. * @param displayGroup * The display group. * @param tabIndex * Index of browser tab. */ bool DisplayPropertiesBorders::isDisplayed(const DisplayGroupEnum::Enum displayGroup, const int32_t tabIndex) const { CaretAssertArrayIndex(m_displayStatusInDisplayGroup, DisplayGroupEnum::NUMBER_OF_GROUPS, static_cast(displayGroup)); if (displayGroup == DisplayGroupEnum::DISPLAY_GROUP_TAB) { CaretAssertArrayIndex(m_displayStatusInTab, BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS, tabIndex); return m_displayStatusInTab[tabIndex]; } return m_displayStatusInDisplayGroup[displayGroup]; } /** * Set the display status for borders for the given display group. * @param displayGroup * The display group. * @param tabIndex * Index of browser tab. * @param displayStatus * New status. */ void DisplayPropertiesBorders::setDisplayed(const DisplayGroupEnum::Enum displayGroup, const int32_t tabIndex, const bool displayStatus) { CaretAssertArrayIndex(m_displayStatusInDisplayGroup, DisplayGroupEnum::NUMBER_OF_GROUPS, static_cast(displayGroup)); if (displayGroup == DisplayGroupEnum::DISPLAY_GROUP_TAB) { CaretAssertArrayIndex(m_displayStatusInTab, BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS, tabIndex); m_displayStatusInTab[tabIndex] = displayStatus; } else { m_displayStatusInDisplayGroup[displayGroup] = displayStatus; } } /** * @return Contralateral display status of borders. * @param displayGroup * The display group. * @param tabIndex * Index of browser tab. */ bool DisplayPropertiesBorders::isContralateralDisplayed(const DisplayGroupEnum::Enum displayGroup, const int32_t tabIndex) const { CaretAssertArrayIndex(m_contralateralDisplayStatusInDisplayGroup, DisplayGroupEnum::NUMBER_OF_GROUPS, static_cast(displayGroup)); if (displayGroup == DisplayGroupEnum::DISPLAY_GROUP_TAB) { CaretAssertArrayIndex(m_displayStatusInTab, BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS, tabIndex); return m_contralateralDisplayStatusInTab[tabIndex]; } return m_contralateralDisplayStatusInDisplayGroup[displayGroup]; } /** * Set the contralateral display status for borders. * @param displayGroup * The display group. * @param tabIndex * Index of browser tab. * @param contralateralDisplayStatus * New status. */ void DisplayPropertiesBorders::setContralateralDisplayed(const DisplayGroupEnum::Enum displayGroup, const int32_t tabIndex, const bool contralateralDisplayStatus) { CaretAssertArrayIndex(m_contralateralDisplayStatusInDisplayGroup, DisplayGroupEnum::NUMBER_OF_GROUPS, static_cast(displayGroup)); if (displayGroup == DisplayGroupEnum::DISPLAY_GROUP_TAB) { CaretAssertArrayIndex(m_displayStatusInTab, BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS, tabIndex); m_contralateralDisplayStatusInTab[tabIndex] = contralateralDisplayStatus; } else { m_contralateralDisplayStatusInDisplayGroup[displayGroup] = contralateralDisplayStatus; } } /** * Get the display group for a given browser tab. * @param browserTabIndex * Index of browser tab. */ DisplayGroupEnum::Enum DisplayPropertiesBorders::getDisplayGroupForTab(const int32_t browserTabIndex) const { CaretAssertArrayIndex(this->displayGroup, BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS, browserTabIndex); return m_displayGroup[browserTabIndex]; } /** * Set the display group for a given browser tab. * @param browserTabIndex * Index of browser tab. * @param displayGroup * New value for display group. */ void DisplayPropertiesBorders::setDisplayGroupForTab(const int32_t browserTabIndex, const DisplayGroupEnum::Enum displayGroup) { CaretAssertArrayIndex(this->displayGroup, BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS, browserTabIndex); m_displayGroup[browserTabIndex] = displayGroup; } /** * @return The point size. * @param displayGroup * The display group. * @param tabIndex * Index of browser tab. */ float DisplayPropertiesBorders::getPointSize(const DisplayGroupEnum::Enum displayGroup, const int32_t tabIndex) const { CaretAssertArrayIndex(m_pointSizeInDisplayGroup, DisplayGroupEnum::NUMBER_OF_GROUPS, static_cast(displayGroup)); if (displayGroup == DisplayGroupEnum::DISPLAY_GROUP_TAB) { CaretAssertArrayIndex(m_displayStatusInTab, BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS, tabIndex); return m_pointSizeInTab[tabIndex]; } return m_pointSizeInDisplayGroup[displayGroup]; } /** * Set the point size to the given value. * @param displayGroup * The display group. * @param tabIndex * Index of browser tab. * @param pointSize * New value for point size. */ void DisplayPropertiesBorders::setPointSize(const DisplayGroupEnum::Enum displayGroup, const int32_t tabIndex, const float pointSize) { CaretAssertArrayIndex(m_pointSizeInDisplayGroup, DisplayGroupEnum::NUMBER_OF_GROUPS, static_cast(displayGroup)); if (displayGroup == DisplayGroupEnum::DISPLAY_GROUP_TAB) { CaretAssertArrayIndex(m_displayStatusInTab, BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS, tabIndex); m_pointSizeInTab[tabIndex] = pointSize; } else { m_pointSizeInDisplayGroup[displayGroup] = pointSize; } } /** * @return The line width. * @param displayGroup * The display group. * @param tabIndex * Index of browser tab. */ float DisplayPropertiesBorders::getLineWidth(const DisplayGroupEnum::Enum displayGroup, const int32_t tabIndex) const { CaretAssertArrayIndex(m_lineWidthInDisplayGroup, DisplayGroupEnum::NUMBER_OF_GROUPS, static_cast(displayGroup)); if (displayGroup == DisplayGroupEnum::DISPLAY_GROUP_TAB) { CaretAssertArrayIndex(m_displayStatusInTab, BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS, tabIndex); return m_lineWidthInTab[tabIndex]; } return m_lineWidthInDisplayGroup[displayGroup]; } /** * Set the line width to the given value. * @param displayGroup * The display group. * @param tabIndex * Index of browser tab. * @param lineWidth * New value for line width. */ void DisplayPropertiesBorders::setLineWidth(const DisplayGroupEnum::Enum displayGroup, const int32_t tabIndex, const float lineWidth) { CaretAssertArrayIndex(m_lineWidthInDisplayGroup, DisplayGroupEnum::NUMBER_OF_GROUPS, static_cast(displayGroup)); if (displayGroup == DisplayGroupEnum::DISPLAY_GROUP_TAB) { CaretAssertArrayIndex(m_displayStatusInTab, BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS, tabIndex); m_lineWidthInTab[tabIndex] = lineWidth; } else { m_lineWidthInDisplayGroup[displayGroup] = lineWidth; } } /** * @return Is unstretched lines enabled? * * @param displayGroup * The display group. * @param tabIndex * Index of browser tab. */ bool DisplayPropertiesBorders::isUnstretchedLinesEnabled(const DisplayGroupEnum::Enum displayGroup, const int32_t tabIndex) const { CaretAssertArrayIndex(m_unstretchedLinesStatusInDisplayGroup, DisplayGroupEnum::NUMBER_OF_GROUPS, static_cast(displayGroup)); if (displayGroup == DisplayGroupEnum::DISPLAY_GROUP_TAB) { CaretAssertArrayIndex(m_unstretchedLinesStatusInTab, BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS, tabIndex); return m_unstretchedLinesStatusInTab[tabIndex]; } return m_unstretchedLinesStatusInDisplayGroup[displayGroup]; } /** * Set the unstretched lines status to the given value. * @param displayGroup * The display group. * @param tabIndex * Index of browser tab. * @param unstretchedLinesEnabled * New value for line unstretched lines status. */ void DisplayPropertiesBorders::setUnstretchedLinesEnabled(const DisplayGroupEnum::Enum displayGroup, const int32_t tabIndex, const bool unstretchedLinesEnabled) { CaretAssertArrayIndex(m_unstretchedLinesStatusInDisplayGroup, DisplayGroupEnum::NUMBER_OF_GROUPS, static_cast(displayGroup)); if (displayGroup == DisplayGroupEnum::DISPLAY_GROUP_TAB) { CaretAssertArrayIndex(m_unstretchedLinesStatusInTab, BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS, tabIndex); m_unstretchedLinesStatusInTab[tabIndex] = unstretchedLinesEnabled; } else { m_unstretchedLinesStatusInDisplayGroup[displayGroup] = unstretchedLinesEnabled; } } /** * @return Get unstretched lines length * * @param displayGroup * The display group. * @param tabIndex * Index of browser tab. */ float DisplayPropertiesBorders::getUnstretchedLinesLength(const DisplayGroupEnum::Enum displayGroup, const int32_t tabIndex) const { CaretAssertArrayIndex(m_unstretchedLinesLengthInDisplayGroup, DisplayGroupEnum::NUMBER_OF_GROUPS, static_cast(displayGroup)); if (displayGroup == DisplayGroupEnum::DISPLAY_GROUP_TAB) { CaretAssertArrayIndex(m_unstretchedLinesLengthInTab, BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS, tabIndex); return m_unstretchedLinesLengthInTab[tabIndex]; } return m_unstretchedLinesLengthInDisplayGroup[displayGroup]; } /** * Set the unstretched lines length to the given value. * @param displayGroup * The display group. * @param tabIndex * Index of browser tab. * @param unstretchedLinesLength * New value for unstretched lines length. */ void DisplayPropertiesBorders::setUnstretchedLinesLength(const DisplayGroupEnum::Enum displayGroup, const int32_t tabIndex, const float unstretchedLinesLength) { CaretAssertArrayIndex(m_unstretchedLinesLengthInDisplayGroup, DisplayGroupEnum::NUMBER_OF_GROUPS, static_cast(displayGroup)); if (displayGroup == DisplayGroupEnum::DISPLAY_GROUP_TAB) { CaretAssertArrayIndex(m_unstretchedLinesLengthInTab, BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS, tabIndex); m_unstretchedLinesLengthInTab[tabIndex] = unstretchedLinesLength; } else { m_unstretchedLinesLengthInDisplayGroup[displayGroup] = unstretchedLinesLength; } } /** * @return Above surface offset. */ float DisplayPropertiesBorders::getAboveSurfaceOffset() const { return m_aboveSurfaceOffset; } /** * Set the above surface offset. * * @param offset * New value for above surface offset. */ void DisplayPropertiesBorders::setAboveSurfaceOffset(const float offset) { m_aboveSurfaceOffset = offset; } /** * @return The drawing type. * @param displayGroup * The display group. * @param tabIndex * Index of browser tab. */ BorderDrawingTypeEnum::Enum DisplayPropertiesBorders::getDrawingType(const DisplayGroupEnum::Enum displayGroup, const int32_t tabIndex) const { CaretAssertArrayIndex(m_drawingTypeInDisplayGroup, DisplayGroupEnum::NUMBER_OF_GROUPS, static_cast(displayGroup)); if (displayGroup == DisplayGroupEnum::DISPLAY_GROUP_TAB) { CaretAssertArrayIndex(m_displayStatusInTab, BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS, tabIndex); return m_drawingTypeInTab[tabIndex]; } return m_drawingTypeInDisplayGroup[displayGroup]; } /** * Set the drawing type to the given value. * @param displayGroup * The display group. * @param tabIndex * Index of browser tab. * @param drawingType * New value for drawing type. */ void DisplayPropertiesBorders::setDrawingType(const DisplayGroupEnum::Enum displayGroup, const int32_t tabIndex, const BorderDrawingTypeEnum::Enum drawingType) { CaretAssertArrayIndex(m_drawingTypeInDisplayGroup, DisplayGroupEnum::NUMBER_OF_GROUPS, static_cast(displayGroup)); if (displayGroup == DisplayGroupEnum::DISPLAY_GROUP_TAB) { CaretAssertArrayIndex(m_displayStatusInTab, BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS, tabIndex); m_drawingTypeInTab[tabIndex] = drawingType; } else { m_drawingTypeInDisplayGroup[displayGroup] = drawingType; } } /** * @return The coloring type. * @param displayGroup * Display group. */ FeatureColoringTypeEnum::Enum DisplayPropertiesBorders::getColoringType(const DisplayGroupEnum::Enum displayGroup, const int32_t tabIndex) const { CaretAssertArrayIndex(m_coloringTypeInDisplayGroup, DisplayGroupEnum::NUMBER_OF_GROUPS, static_cast(displayGroup)); if (displayGroup == DisplayGroupEnum::DISPLAY_GROUP_TAB) { CaretAssertArrayIndex(m_coloringTypeInTab, BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS, tabIndex); return m_coloringTypeInTab[tabIndex]; } return m_coloringTypeInDisplayGroup[displayGroup]; } /** * Set the coloring type. * @param displayGroup * Display group. * @param coloringType * New value for coloring type. */ void DisplayPropertiesBorders::setColoringType(const DisplayGroupEnum::Enum displayGroup, const int32_t tabIndex, const FeatureColoringTypeEnum::Enum coloringType) { CaretAssertArrayIndex(m_coloringTypeInDisplayGroup, DisplayGroupEnum::NUMBER_OF_GROUPS, static_cast(displayGroup)); if (displayGroup == DisplayGroupEnum::DISPLAY_GROUP_TAB) { CaretAssertArrayIndex(m_coloringTypeInTab, BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS, tabIndex); m_coloringTypeInTab[tabIndex] = coloringType; } else { m_coloringTypeInDisplayGroup[displayGroup] = coloringType; } } /** * @return The standard caret coloring type. * @param displayGroup * Display group. */ CaretColorEnum::Enum DisplayPropertiesBorders::getStandardColorType(const DisplayGroupEnum::Enum displayGroup, const int32_t tabIndex) const { CaretAssertArrayIndex(m_standardColorTypeInDisplayGroup, DisplayGroupEnum::NUMBER_OF_GROUPS, static_cast(displayGroup)); if (displayGroup == DisplayGroupEnum::DISPLAY_GROUP_TAB) { CaretAssertArrayIndex(m_standardColorTypeInTab, BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS, tabIndex); return m_standardColorTypeInTab[tabIndex]; } return m_standardColorTypeInDisplayGroup[displayGroup]; } /** * Set the caret coloring type. * @param displayGroup * Display group. * @param color * New color for coloring type. */ void DisplayPropertiesBorders::setStandardColorType(const DisplayGroupEnum::Enum displayGroup, const int32_t tabIndex, const CaretColorEnum::Enum color) { CaretAssertArrayIndex(m_standardColorTypeInDisplayGroup, DisplayGroupEnum::NUMBER_OF_GROUPS, static_cast(displayGroup)); if (displayGroup == DisplayGroupEnum::DISPLAY_GROUP_TAB) { CaretAssertArrayIndex(m_standardColorTypeInTab, BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS, tabIndex); m_standardColorTypeInTab[tabIndex] = color; } else { m_standardColorTypeInDisplayGroup[displayGroup] = color; } } //template //const std::vector enumArrayToStrings(const ET enumArray[], // const std::vector& tabIndices) //{ // std::vector stringVector; // // const int32_t numTabs = static_cast(tabIndices.size()); // for (int32_t i = 0; i < numTabs; i++) { // const AString stringValue = ET::nameToString(enumArray[i], // false); // stringVector.push_back(stringValue); // } // return stringVector; //} // //template //class EnumConvert { //public: // static std::vector enumArrayToStringsForTabIndices(const ET enumArray[], // const std::vector& tabIndices) // { // std::vector stringVector; // // const int32_t numTabs = static_cast(tabIndices.size()); // for (int32_t i = 0; i < numTabs; i++) { // const int32_t tabIndex = tabIndices[i]; // const AString stringValue = T::toName(enumArray[tabIndex]); // stringVector.push_back(stringValue); // } // return stringVector; // } //}; /** * Create a scene for an instance of a class. * * @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. * * @return Pointer to SceneClass object representing the state of * this object. Under some circumstances a NULL pointer may be * returned. Caller will take ownership of returned object. */ SceneClass* DisplayPropertiesBorders::saveToScene(const SceneAttributes* sceneAttributes, const AString& instanceName) { SceneClass* sceneClass = new SceneClass(instanceName, "DisplayPropertiesBorders", 1); const std::vector tabIndices = sceneAttributes->getIndicesOfTabsForSavingToScene(); m_sceneAssistant->saveMembers(sceneAttributes, sceneClass); return sceneClass; } /** * Restore the state of an instance of a class. * * @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 containing the state that was previously * saved and should be restored. */ void DisplayPropertiesBorders::restoreFromScene(const SceneAttributes* sceneAttributes, const SceneClass* sceneClass) { if (sceneClass == NULL) { return; } m_sceneAssistant->restoreMembers(sceneAttributes, sceneClass); switch (sceneAttributes->getSceneType()) { case SceneTypeEnum::SCENE_TYPE_FULL: break; case SceneTypeEnum::SCENE_TYPE_GENERIC: break; } } connectome-workbench-1.2.3+git41-gc4c6c90/src/Brain/DisplayPropertiesBorders.h000066400000000000000000000172631300200146000267640ustar00rootroot00000000000000#ifndef __DISPLAY_PROPERTIES_BORDERS__H_ #define __DISPLAY_PROPERTIES_BORDERS__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 "BrainConstants.h" #include "BorderDrawingTypeEnum.h" #include "CaretColorEnum.h" #include "DisplayGroupEnum.h" #include "DisplayProperties.h" #include "FeatureColoringTypeEnum.h" namespace caret { class DisplayPropertiesBorders : public DisplayProperties { public: DisplayPropertiesBorders(); virtual ~DisplayPropertiesBorders(); virtual void reset(); virtual void update(); virtual void copyDisplayProperties(const int32_t sourceTabIndex, const int32_t targetTabIndex); bool isDisplayed(const DisplayGroupEnum::Enum displayGroup, const int32_t tabIndex) const; void setDisplayed(const DisplayGroupEnum::Enum displayGroup, const int32_t tabIndex, const bool displayStatus); bool isContralateralDisplayed(const DisplayGroupEnum::Enum displayGroup, const int32_t tabIndex) const; void setContralateralDisplayed(const DisplayGroupEnum::Enum displayGroup, const int32_t tabIndex, const bool contralateralDisplayStatus); DisplayGroupEnum::Enum getDisplayGroupForTab(const int32_t browserTabIndex) const; void setDisplayGroupForTab(const int32_t browserTabIndex, const DisplayGroupEnum::Enum displayGroup); float getPointSize(const DisplayGroupEnum::Enum displayGroup, const int32_t tabIndex) const; void setPointSize(const DisplayGroupEnum::Enum displayGroup, const int32_t tabIndex, const float pointSize); float getLineWidth(const DisplayGroupEnum::Enum displayGroup, const int32_t tabIndex) const; void setLineWidth(const DisplayGroupEnum::Enum displayGroup, const int32_t tabIndex, const float lineWidth); BorderDrawingTypeEnum::Enum getDrawingType(const DisplayGroupEnum::Enum displayGroup, const int32_t tabIndex) const; void setDrawingType(const DisplayGroupEnum::Enum displayGroup, const int32_t tabIndex, const BorderDrawingTypeEnum::Enum drawingType); FeatureColoringTypeEnum::Enum getColoringType(const DisplayGroupEnum::Enum displayGroup, const int32_t tabIndex) const; void setColoringType(const DisplayGroupEnum::Enum displayGroup, const int32_t tabIndex, const FeatureColoringTypeEnum::Enum drawingType); CaretColorEnum::Enum getStandardColorType(const DisplayGroupEnum::Enum displayGroup, const int32_t tabIndex) const; void setStandardColorType(const DisplayGroupEnum::Enum displayGroup, const int32_t tabIndex, const CaretColorEnum::Enum caretColor); bool isUnstretchedLinesEnabled(const DisplayGroupEnum::Enum displayGroup, const int32_t tabIndex) const; void setUnstretchedLinesEnabled(const DisplayGroupEnum::Enum displayGroup, const int32_t tabIndex, const bool unstretchedLinesEnabled); float getUnstretchedLinesLength(const DisplayGroupEnum::Enum displayGroup, const int32_t tabIndex) const; void setUnstretchedLinesLength(const DisplayGroupEnum::Enum displayGroup, const int32_t tabIndex, const float unstretchedLinesLength); float getAboveSurfaceOffset() const; void setAboveSurfaceOffset(const float Offset); virtual SceneClass* saveToScene(const SceneAttributes* sceneAttributes, const AString& instanceName); virtual void restoreFromScene(const SceneAttributes* sceneAttributes, const SceneClass* sceneClass); private: DisplayPropertiesBorders(const DisplayPropertiesBorders&); DisplayPropertiesBorders& operator=(const DisplayPropertiesBorders&); DisplayGroupEnum::Enum m_displayGroup[BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS]; bool m_displayStatusInDisplayGroup[DisplayGroupEnum::NUMBER_OF_GROUPS]; bool m_displayStatusInTab[BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS]; bool m_contralateralDisplayStatusInDisplayGroup[DisplayGroupEnum::NUMBER_OF_GROUPS]; bool m_contralateralDisplayStatusInTab[BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS]; float m_pointSizeInDisplayGroup[DisplayGroupEnum::NUMBER_OF_GROUPS]; float m_pointSizeInTab[BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS]; float m_lineWidthInDisplayGroup[DisplayGroupEnum::NUMBER_OF_GROUPS]; float m_lineWidthInTab[BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS]; BorderDrawingTypeEnum::Enum m_drawingTypeInDisplayGroup[DisplayGroupEnum::NUMBER_OF_GROUPS]; BorderDrawingTypeEnum::Enum m_drawingTypeInTab[BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS]; FeatureColoringTypeEnum::Enum m_coloringTypeInDisplayGroup[DisplayGroupEnum::NUMBER_OF_GROUPS]; FeatureColoringTypeEnum::Enum m_coloringTypeInTab[BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS]; CaretColorEnum::Enum m_standardColorTypeInDisplayGroup[DisplayGroupEnum::NUMBER_OF_GROUPS]; CaretColorEnum::Enum m_standardColorTypeInTab[BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS]; bool m_unstretchedLinesStatusInDisplayGroup[DisplayGroupEnum::NUMBER_OF_GROUPS]; bool m_unstretchedLinesStatusInTab[BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS]; float m_unstretchedLinesLengthInDisplayGroup[DisplayGroupEnum::NUMBER_OF_GROUPS]; float m_unstretchedLinesLengthInTab[BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS]; float m_aboveSurfaceOffset; }; #ifdef __DISPLAY_PROPERTIES_BORDERS_DECLARE__ // #endif // __DISPLAY_PROPERTIES_BORDERS_DECLARE__ } // namespace #endif //__DISPLAY_PROPERTIES_BORDERS__H_ connectome-workbench-1.2.3+git41-gc4c6c90/src/Brain/DisplayPropertiesFiberOrientation.cxx000066400000000000000000001006051300200146000311730ustar00rootroot00000000000000 /*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 __DISPLAY_PROPERTIES_FIBER_ORIENTATION_DECLARE__ #include "DisplayPropertiesFiberOrientation.h" #undef __DISPLAY_PROPERTIES_FIBER_ORIENTATION_DECLARE__ #include "CaretAssert.h" #include "SceneAttributes.h" #include "SceneClass.h" #include "SceneClassAssistant.h" using namespace caret; /** * \class caret::DisplayPropertiesFiberOrientation * \brief Contains display properties for borders. * * Border display properties are available for every tab and also a * few 'display groups'. A number of methods in this class accept * both display group and tab index parameters. When the display * group is set to 'Tab', the tab index is used meaning that the * attribute requeted/sent is for use with a specifc tab. For an * other display group value, the attribute is for a display group * and the tab index is ignored. */ /** * Constructor. */ DisplayPropertiesFiberOrientation::DisplayPropertiesFiberOrientation() : DisplayProperties() { const float aboveLimit = 0.63; const float belowLimit = -0.63; const float minimumMagnitude = 0.05; const bool drawWithMagnitude = true; const float lengthMultiplier = 6.0; const float fanMultiplier = 3.0; const FiberOrientationColoringTypeEnum::Enum coloringType = FiberOrientationColoringTypeEnum::FIBER_COLORING_XYZ_AS_RGB; const FiberOrientationSymbolTypeEnum::Enum symbolType = FiberOrientationSymbolTypeEnum::FIBER_SYMBOL_LINES; const bool displaySphereOrientions = false; for (int32_t i = 0; i < BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS; i++) { m_displayGroup[i] = DisplayGroupEnum::getDefaultValue(); m_displayStatusInTab[i] = false; m_aboveLimitInTab[i] = aboveLimit; m_belowLimitInTab[i] = belowLimit; m_minimumMagnitudeInTab[i] = minimumMagnitude; m_drawWithMagnitudeInTab[i] = drawWithMagnitude; m_lengthMultiplierInTab[i] = lengthMultiplier; m_fiberColoringTypeInTab[i] = coloringType; m_fiberSymbolTypeInTab[i] = symbolType; m_fanMultiplierInTab[i] = fanMultiplier; m_displaySphereOrientationsInTab[i] = displaySphereOrientions; } for (int32_t i = 0; i < DisplayGroupEnum::NUMBER_OF_GROUPS; i++) { m_displayStatusInDisplayGroup[i] = false; m_aboveLimitInDisplayGroup[i] = aboveLimit; m_belowLimitInDisplayGroup[i] = belowLimit; m_minimumMagnitudeInDisplayGroup[i] = minimumMagnitude; m_drawWithMagnitudeInDisplayGroup[i] = drawWithMagnitude; m_lengthMultiplierInDisplayGroup[i] = lengthMultiplier; m_fiberColoringTypeInDisplayGroup[i] = coloringType; m_fiberSymbolTypeInDisplayGroup[i] = symbolType; m_fanMultiplierInDisplayGroup[i] = fanMultiplier; m_displaySphereOrientationsInDisplayGroup[i] = displaySphereOrientions; } m_sceneAssistant->addTabIndexedEnumeratedTypeArray("m_displayGroup", m_displayGroup); m_sceneAssistant->addTabIndexedBooleanArray("m_displayStatusInTab", m_displayStatusInTab); m_sceneAssistant->addTabIndexedFloatArray("m_aboveLimitInTab", m_aboveLimitInTab); m_sceneAssistant->addTabIndexedFloatArray("m_belowLimitInTab", m_belowLimitInTab); m_sceneAssistant->addTabIndexedFloatArray("m_minimumMagnitudeInTab", m_minimumMagnitudeInTab); m_sceneAssistant->addTabIndexedBooleanArray("m_drawWithMagnitudeInTab", m_drawWithMagnitudeInTab); m_sceneAssistant->addTabIndexedFloatArray("m_lengthMultiplierInTab", m_lengthMultiplierInTab); m_sceneAssistant->addTabIndexedFloatArray("m_fanMultiplierInTab", m_fanMultiplierInTab); m_sceneAssistant->addTabIndexedBooleanArray("m_displaySphereOrientationsInTab", m_displaySphereOrientationsInTab); m_sceneAssistant->addArray("m_drawingTypeInDisplayGroup", m_fiberColoringTypeInDisplayGroup, DisplayGroupEnum::NUMBER_OF_GROUPS, FiberOrientationColoringTypeEnum::FIBER_COLORING_XYZ_AS_RGB); m_sceneAssistant->addArray("m_fiberSymbolTypeInDisplayGroup", m_fiberSymbolTypeInDisplayGroup, DisplayGroupEnum::NUMBER_OF_GROUPS, FiberOrientationSymbolTypeEnum::FIBER_SYMBOL_LINES); m_sceneAssistant->addArray("m_displayStatusInDisplayGroup", m_displayStatusInDisplayGroup, DisplayGroupEnum::NUMBER_OF_GROUPS, m_displayStatusInDisplayGroup[0]); m_sceneAssistant->addArray("m_aboveLimitInDisplayGroup", m_aboveLimitInDisplayGroup, DisplayGroupEnum::NUMBER_OF_GROUPS, m_aboveLimitInDisplayGroup[0]); m_sceneAssistant->addArray("m_belowLimitInDisplayGroup", m_belowLimitInDisplayGroup, DisplayGroupEnum::NUMBER_OF_GROUPS, m_belowLimitInDisplayGroup[0]); m_sceneAssistant->addArray("m_minimumMagnitudeInDisplayGroup", m_minimumMagnitudeInDisplayGroup, DisplayGroupEnum::NUMBER_OF_GROUPS, m_minimumMagnitudeInDisplayGroup[0]); m_sceneAssistant->addArray("m_drawWithMagnitudeInDisplayGroup", m_drawWithMagnitudeInDisplayGroup, DisplayGroupEnum::NUMBER_OF_GROUPS, m_drawWithMagnitudeInDisplayGroup[0]); m_sceneAssistant->addArray("m_lengthMultiplierInDisplayGroup", m_lengthMultiplierInDisplayGroup, DisplayGroupEnum::NUMBER_OF_GROUPS, m_lengthMultiplierInDisplayGroup[0]); m_sceneAssistant->addArray("m_fanMultiplierInDisplayGroup", m_fanMultiplierInDisplayGroup, DisplayGroupEnum::NUMBER_OF_GROUPS, m_fanMultiplierInDisplayGroup[0]); m_sceneAssistant->addArray("m_displaySphereOrientationsInTab", m_displaySphereOrientationsInTab, DisplayGroupEnum::NUMBER_OF_GROUPS, m_displaySphereOrientationsInTab[0]); m_sceneAssistant->addTabIndexedEnumeratedTypeArray("m_fiberColoringTypeInTab", m_fiberColoringTypeInTab); m_sceneAssistant->addTabIndexedEnumeratedTypeArray("m_fiberSymbolTypeInTab", m_fiberSymbolTypeInTab); } /** * Destructor. */ DisplayPropertiesFiberOrientation::~DisplayPropertiesFiberOrientation() { } /** * Reset all settings to their defaults * and remove any data. */ void DisplayPropertiesFiberOrientation::reset() { } /** * Update due to changes in data. */ void DisplayPropertiesFiberOrientation::update() { } /** * Copy the fiber orientation display properties from one tab to another. * @param sourceTabIndex * Index of tab from which properties are copied. * @param targetTabIndex * Index of tab to which properties are copied. */ void DisplayPropertiesFiberOrientation::copyDisplayProperties(const int32_t sourceTabIndex, const int32_t targetTabIndex) { const DisplayGroupEnum::Enum displayGroup = this->getDisplayGroupForTab(sourceTabIndex); this->setDisplayGroupForTab(targetTabIndex, displayGroup); m_displayStatusInTab[targetTabIndex] = m_displayStatusInTab[sourceTabIndex]; m_aboveLimitInTab[targetTabIndex] = m_aboveLimitInTab[sourceTabIndex]; m_belowLimitInTab[targetTabIndex] = m_belowLimitInTab[sourceTabIndex]; m_minimumMagnitudeInTab[targetTabIndex] = m_minimumMagnitudeInTab[sourceTabIndex]; m_drawWithMagnitudeInTab[targetTabIndex] = m_drawWithMagnitudeInTab[sourceTabIndex]; m_lengthMultiplierInTab[targetTabIndex] = m_lengthMultiplierInTab[sourceTabIndex]; m_fiberColoringTypeInTab[targetTabIndex] = m_fiberColoringTypeInTab[sourceTabIndex]; m_fanMultiplierInTab[targetTabIndex] = m_fanMultiplierInTab[sourceTabIndex]; } /** * @return Display status of fiber orientations. * @param displayGroup * The display group. * @param tabIndex * Index of browser tab. */ bool DisplayPropertiesFiberOrientation::isDisplayed(const DisplayGroupEnum::Enum displayGroup, const int32_t tabIndex) const { CaretAssertArrayIndex(m_displayStatusInDisplayGroup, DisplayGroupEnum::NUMBER_OF_GROUPS, static_cast(displayGroup)); if (displayGroup == DisplayGroupEnum::DISPLAY_GROUP_TAB) { CaretAssertArrayIndex(m_displayStatusInTab, BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS, tabIndex); return m_displayStatusInTab[tabIndex]; } return m_displayStatusInDisplayGroup[displayGroup]; } /** * Set the display status for fiber orientations for the given display group. * @param displayGroup * The display group. * @param tabIndex * Index of browser tab. * @param displayStatus * New status. */ void DisplayPropertiesFiberOrientation::setDisplayed(const DisplayGroupEnum::Enum displayGroup, const int32_t tabIndex, const bool displayStatus) { CaretAssertArrayIndex(m_displayStatusInDisplayGroup, DisplayGroupEnum::NUMBER_OF_GROUPS, static_cast(displayGroup)); if (displayGroup == DisplayGroupEnum::DISPLAY_GROUP_TAB) { CaretAssertArrayIndex(m_displayStatusInTab, BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS, tabIndex); m_displayStatusInTab[tabIndex] = displayStatus; } else { m_displayStatusInDisplayGroup[displayGroup] = displayStatus; } } /** * @return Display status of sphere orientations. * @param displayGroup * The display group. * @param tabIndex * Index of browser tab. */ bool DisplayPropertiesFiberOrientation::isSphereOrientationsDisplayed(const DisplayGroupEnum::Enum displayGroup, const int32_t tabIndex) const { CaretAssertArrayIndex(m_displaySphereOrientationsInDisplayGroup, DisplayGroupEnum::NUMBER_OF_GROUPS, static_cast(displayGroup)); if (displayGroup == DisplayGroupEnum::DISPLAY_GROUP_TAB) { CaretAssertArrayIndex(m_displaySphereOrientationsInTab, BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS, tabIndex); return m_displaySphereOrientationsInTab[tabIndex]; } return m_displaySphereOrientationsInDisplayGroup[displayGroup]; } /** * Set the display status for sphere orientations for the given display group. * @param displayGroup * The display group. * @param tabIndex * Index of browser tab. * @param displayStatus * New status. */ void DisplayPropertiesFiberOrientation::setSphereOrientationsDisplayed(const DisplayGroupEnum::Enum displayGroup, const int32_t tabIndex, const bool displaySphereOrientations) { CaretAssertArrayIndex(m_displaySphereOrientationsInDisplayGroup, DisplayGroupEnum::NUMBER_OF_GROUPS, static_cast(displayGroup)); if (displayGroup == DisplayGroupEnum::DISPLAY_GROUP_TAB) { CaretAssertArrayIndex(m_displaySphereOrientationsInTab, BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS, tabIndex); m_displaySphereOrientationsInTab[tabIndex] = displaySphereOrientations; } else { m_displaySphereOrientationsInDisplayGroup[displayGroup] = displaySphereOrientations; } } /** * Get the display group for a given browser tab. * @param browserTabIndex * Index of browser tab. */ DisplayGroupEnum::Enum DisplayPropertiesFiberOrientation::getDisplayGroupForTab(const int32_t browserTabIndex) const { CaretAssertArrayIndex(this->displayGroup, BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS, browserTabIndex); return m_displayGroup[browserTabIndex]; } /** * Set the display group for a given browser tab. * @param browserTabIndex * Index of browser tab. * @param displayGroup * New value for display group. */ void DisplayPropertiesFiberOrientation::setDisplayGroupForTab(const int32_t browserTabIndex, const DisplayGroupEnum::Enum displayGroup) { CaretAssertArrayIndex(this->displayGroup, BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS, browserTabIndex); m_displayGroup[browserTabIndex] = displayGroup; } /** * @return Draw with magnitude status. * @param displayGroup * The display group. * @param tabIndex * Index of browser tab. */ bool DisplayPropertiesFiberOrientation::isDrawWithMagnitude(const DisplayGroupEnum::Enum displayGroup, const int32_t tabIndex) const { CaretAssertArrayIndex(m_drawWithMagnitudeInDisplayGroup, DisplayGroupEnum::NUMBER_OF_GROUPS, static_cast(displayGroup)); if (displayGroup == DisplayGroupEnum::DISPLAY_GROUP_TAB) { CaretAssertArrayIndex(m_drawWithMagnitudeInTab, BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS, tabIndex); return m_drawWithMagnitudeInTab[tabIndex]; } return m_drawWithMagnitudeInDisplayGroup[displayGroup]; } /** * Set the draw with magnitude status for the given display group. * @param displayGroup * The display group. * @param tabIndex * Index of browser tab. * @param drawWithMagnitude * New status. */ void DisplayPropertiesFiberOrientation::setDrawWithMagnitude(const DisplayGroupEnum::Enum displayGroup, const int32_t tabIndex, const bool drawWithMagnitude) { CaretAssertArrayIndex(m_drawWithMagnitudeInDisplayGroup, DisplayGroupEnum::NUMBER_OF_GROUPS, static_cast(displayGroup)); if (displayGroup == DisplayGroupEnum::DISPLAY_GROUP_TAB) { CaretAssertArrayIndex(m_drawWithMagnitudeInTab, BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS, tabIndex); m_drawWithMagnitudeInTab[tabIndex] = drawWithMagnitude; } else { m_drawWithMagnitudeInDisplayGroup[displayGroup] = drawWithMagnitude; } } /** * @return The Above limit. * @param displayGroup * The display group. * @param tabIndex * Index of browser tab. */ float DisplayPropertiesFiberOrientation::getAboveLimit(const DisplayGroupEnum::Enum displayGroup, const int32_t tabIndex) const { CaretAssertArrayIndex(m_aboveLimitInDisplayGroup, DisplayGroupEnum::NUMBER_OF_GROUPS, static_cast(displayGroup)); if (displayGroup == DisplayGroupEnum::DISPLAY_GROUP_TAB) { CaretAssertArrayIndex(m_aboveLimitInTab, BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS, tabIndex); return m_aboveLimitInTab[tabIndex]; } return m_aboveLimitInDisplayGroup[displayGroup]; } /** * Set the above limit to the given value. * @param displayGroup * The display group. * @param tabIndex * Index of browser tab. * @param aboveLimit * New value for above limit. */ void DisplayPropertiesFiberOrientation::setAboveLimit(const DisplayGroupEnum::Enum displayGroup, const int32_t tabIndex, const float aboveLimit) { CaretAssertArrayIndex(m_aboveLimitInDisplayGroup, DisplayGroupEnum::NUMBER_OF_GROUPS, static_cast(displayGroup)); if (displayGroup == DisplayGroupEnum::DISPLAY_GROUP_TAB) { CaretAssertArrayIndex(m_aboveLimitInTab, BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS, tabIndex); m_aboveLimitInTab[tabIndex] = aboveLimit; } else { m_aboveLimitInDisplayGroup[displayGroup] = aboveLimit; } } /** * @return The below limit. * @param displayGroup * The display group. * @param tabIndex * Index of browser tab. */ float DisplayPropertiesFiberOrientation::getBelowLimit(const DisplayGroupEnum::Enum displayGroup, const int32_t tabIndex) const { CaretAssertArrayIndex(m_belowLimitInDisplayGroup, DisplayGroupEnum::NUMBER_OF_GROUPS, static_cast(displayGroup)); if (displayGroup == DisplayGroupEnum::DISPLAY_GROUP_TAB) { CaretAssertArrayIndex(m_belowLimitInTab, BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS, tabIndex); return m_belowLimitInTab[tabIndex]; } return m_belowLimitInDisplayGroup[displayGroup]; } /** * Set the below limit to the given value. * @param displayGroup * The display group. * @param tabIndex * Index of browser tab. * @param pointSize * New value for below limit. */ void DisplayPropertiesFiberOrientation::setBelowLimit(const DisplayGroupEnum::Enum displayGroup, const int32_t tabIndex, const float belowLimit) { CaretAssertArrayIndex(m_belowLimitInDisplayGroup, DisplayGroupEnum::NUMBER_OF_GROUPS, static_cast(displayGroup)); if (displayGroup == DisplayGroupEnum::DISPLAY_GROUP_TAB) { CaretAssertArrayIndex(m_belowLimitInTab, BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS, tabIndex); m_belowLimitInTab[tabIndex] = belowLimit; } else { m_belowLimitInDisplayGroup[displayGroup] = belowLimit; } } /** * Set the above and below limits for all display groups and tabs. * @param * Value for all above limits. * @param * Value for all below limits. */ void DisplayPropertiesFiberOrientation::setAboveAndBelowLimitsForAll(const float aboveLimit, const float belowLimit) { for (int32_t i = 0; i < BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS; i++) { m_aboveLimitInTab[i] = aboveLimit; m_belowLimitInTab[i] = belowLimit; } for (int32_t i = 0; i < DisplayGroupEnum::NUMBER_OF_GROUPS; i++) { m_aboveLimitInDisplayGroup[i] = aboveLimit; m_belowLimitInDisplayGroup[i] = belowLimit; } } /** * @return The minimum magnitude. * @param displayGroup * The display group. * @param tabIndex * Index of browser tab. */ float DisplayPropertiesFiberOrientation::getMinimumMagnitude(const DisplayGroupEnum::Enum displayGroup, const int32_t tabIndex) const { CaretAssertArrayIndex(m_minimumMagnitudeInDisplayGroup, DisplayGroupEnum::NUMBER_OF_GROUPS, static_cast(displayGroup)); if (displayGroup == DisplayGroupEnum::DISPLAY_GROUP_TAB) { CaretAssertArrayIndex(m_minimumMagnitudeInTab, BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS, tabIndex); return m_minimumMagnitudeInTab[tabIndex]; } return m_minimumMagnitudeInDisplayGroup[displayGroup]; } /** * Set the minimum magnitude to the given value. * @param displayGroup * The display group. * @param tabIndex * Index of browser tab. * @param minimumMagnitude * New value for minimum magnitude. */ void DisplayPropertiesFiberOrientation::setMinimumMagnitude(const DisplayGroupEnum::Enum displayGroup, const int32_t tabIndex, const float minimumMagnitude) { CaretAssertArrayIndex(m_minimumMagnitudeInDisplayGroup, DisplayGroupEnum::NUMBER_OF_GROUPS, static_cast(displayGroup)); if (displayGroup == DisplayGroupEnum::DISPLAY_GROUP_TAB) { CaretAssertArrayIndex(m_minimumMagnitudeInTab, BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS, tabIndex); m_minimumMagnitudeInTab[tabIndex] = minimumMagnitude; } else { m_minimumMagnitudeInDisplayGroup[displayGroup] = minimumMagnitude; } } /** * @return The length multiplier. * @param displayGroup * The display group. * @param tabIndex * Index of browser tab. */ float DisplayPropertiesFiberOrientation::getLengthMultiplier(const DisplayGroupEnum::Enum displayGroup, const int32_t tabIndex) const { CaretAssertArrayIndex(m_lengthMultiplierInDisplayGroup, DisplayGroupEnum::NUMBER_OF_GROUPS, static_cast(displayGroup)); if (displayGroup == DisplayGroupEnum::DISPLAY_GROUP_TAB) { CaretAssertArrayIndex(m_lengthMultiplierInTab, BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS, tabIndex); return m_lengthMultiplierInTab[tabIndex]; } return m_lengthMultiplierInDisplayGroup[displayGroup]; } /** * Set the length multiplier to the given value. * @param displayGroup * The display group. * @param tabIndex * Index of browser tab. * @param lengthMultiplier * New value for length multiplier */ void DisplayPropertiesFiberOrientation::setLengthMultiplier(const DisplayGroupEnum::Enum displayGroup, const int32_t tabIndex, const float lengthMultiplier) { CaretAssertArrayIndex(m_lengthMultiplierInDisplayGroup, DisplayGroupEnum::NUMBER_OF_GROUPS, static_cast(displayGroup)); if (displayGroup == DisplayGroupEnum::DISPLAY_GROUP_TAB) { CaretAssertArrayIndex(m_lengthMultiplierInTab, BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS, tabIndex); m_lengthMultiplierInTab[tabIndex] = lengthMultiplier; } else { m_lengthMultiplierInDisplayGroup[displayGroup] = lengthMultiplier; } } /** * @return The coloring type. * @param displayGroup * The display group. * @param tabIndex * Index of browser tab. */ FiberOrientationColoringTypeEnum::Enum DisplayPropertiesFiberOrientation::getColoringType(const DisplayGroupEnum::Enum displayGroup, const int32_t tabIndex) const { CaretAssertArrayIndex(m_fiberColoringTypeInDisplayGroup, DisplayGroupEnum::NUMBER_OF_GROUPS, static_cast(displayGroup)); if (displayGroup == DisplayGroupEnum::DISPLAY_GROUP_TAB) { CaretAssertArrayIndex(m_fiberColoringTypeInTab, BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS, tabIndex); return m_fiberColoringTypeInTab[tabIndex]; } return m_fiberColoringTypeInDisplayGroup[displayGroup]; } /** * Set the coloring type to the given value. * @param displayGroup * The display group. * @param tabIndex * Index of browser tab. * @param coloringType * New value for coloring type. */ void DisplayPropertiesFiberOrientation::setColoringType(const DisplayGroupEnum::Enum displayGroup, const int32_t tabIndex, const FiberOrientationColoringTypeEnum::Enum coloringType) { CaretAssertArrayIndex(m_fiberColoringTypeInDisplayGroup, DisplayGroupEnum::NUMBER_OF_GROUPS, static_cast(displayGroup)); if (displayGroup == DisplayGroupEnum::DISPLAY_GROUP_TAB) { CaretAssertArrayIndex(m_fiberColoringTypeInTab, BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS, tabIndex); m_fiberColoringTypeInTab[tabIndex] = coloringType; } else { m_fiberColoringTypeInDisplayGroup[displayGroup] = coloringType; } } /** * @return The symbol type. * @param displayGroup * The display group. * @param tabIndex * Index of browser tab. */ FiberOrientationSymbolTypeEnum::Enum DisplayPropertiesFiberOrientation::getSymbolType(const DisplayGroupEnum::Enum displayGroup, const int32_t tabIndex) const { CaretAssertArrayIndex(m_fiberSymbolTypeInDisplayGroup, DisplayGroupEnum::NUMBER_OF_GROUPS, static_cast(displayGroup)); if (displayGroup == DisplayGroupEnum::DISPLAY_GROUP_TAB) { CaretAssertArrayIndex(m_fiberSymbolTypeInTab, BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS, tabIndex); return m_fiberSymbolTypeInTab[tabIndex]; } return m_fiberSymbolTypeInDisplayGroup[displayGroup]; } /** * Set the symbol type to the given value. * @param displayGroup * The display group. * @param tabIndex * Index of browser tab. * @param symbolType * New value for symbol type. */ void DisplayPropertiesFiberOrientation::setSymbolType(const DisplayGroupEnum::Enum displayGroup, const int32_t tabIndex, const FiberOrientationSymbolTypeEnum::Enum symbolType) { CaretAssertArrayIndex(m_fiberSymbolTypeInDisplayGroup, DisplayGroupEnum::NUMBER_OF_GROUPS, static_cast(displayGroup)); if (displayGroup == DisplayGroupEnum::DISPLAY_GROUP_TAB) { CaretAssertArrayIndex(m_fiberSymbolTypeInTab, BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS, tabIndex); m_fiberSymbolTypeInTab[tabIndex] = symbolType; } else { m_fiberSymbolTypeInDisplayGroup[displayGroup] = symbolType; } } /** * Create a scene for an instance of a class. * * @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. * * @return Pointer to SceneClass object representing the state of * this object. Under some circumstances a NULL pointer may be * returned. Caller will take ownership of returned object. */ SceneClass* DisplayPropertiesFiberOrientation::saveToScene(const SceneAttributes* sceneAttributes, const AString& instanceName) { SceneClass* sceneClass = new SceneClass(instanceName, "DisplayPropertiesFiberOrientation", 1); m_sceneAssistant->saveMembers(sceneAttributes, sceneClass); return sceneClass; } /** * Restore the state of an instance of a class. * * @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 containing the state that was previously * saved and should be restored. */ void DisplayPropertiesFiberOrientation::restoreFromScene(const SceneAttributes* sceneAttributes, const SceneClass* sceneClass) { if (sceneClass == NULL) { return; } m_sceneAssistant->restoreMembers(sceneAttributes, sceneClass); switch (sceneAttributes->getSceneType()) { case SceneTypeEnum::SCENE_TYPE_FULL: break; case SceneTypeEnum::SCENE_TYPE_GENERIC: break; } } /** * @return The fan multiplier. * @param displayGroup * The display group. * @param tabIndex * Index of browser tab. */ float DisplayPropertiesFiberOrientation::getFanMultiplier(const DisplayGroupEnum::Enum displayGroup, const int32_t tabIndex) const { CaretAssertArrayIndex(m_fanMultiplierInDisplayGroup, DisplayGroupEnum::NUMBER_OF_GROUPS, static_cast(displayGroup)); if (displayGroup == DisplayGroupEnum::DISPLAY_GROUP_TAB) { CaretAssertArrayIndex(m_fanMultiplierInTab, BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS, tabIndex); return m_fanMultiplierInTab[tabIndex]; } return m_fanMultiplierInDisplayGroup[displayGroup]; } /** * Set the fan multiplier to the given value. * @param displayGroup * The display group. * @param tabIndex * Index of browser tab. * @param lengthMultiplier * New value for fan multiplier */ void DisplayPropertiesFiberOrientation::setFanMultiplier(const DisplayGroupEnum::Enum displayGroup, const int32_t tabIndex, const float fanMultiplier) { CaretAssertArrayIndex(m_fanMultiplierInDisplayGroup, DisplayGroupEnum::NUMBER_OF_GROUPS, static_cast(displayGroup)); if (displayGroup == DisplayGroupEnum::DISPLAY_GROUP_TAB) { CaretAssertArrayIndex(m_fanMultiplierInTab, BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS, tabIndex); m_fanMultiplierInTab[tabIndex] = fanMultiplier; } else { m_fanMultiplierInDisplayGroup[displayGroup] = fanMultiplier; } } connectome-workbench-1.2.3+git41-gc4c6c90/src/Brain/DisplayPropertiesFiberOrientation.h000066400000000000000000000204331300200146000306200ustar00rootroot00000000000000#ifndef __DISPLAY_PROPERTIES_FIBER_ORIENTATION__H_ #define __DISPLAY_PROPERTIES_FIBER_ORIENTATION__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 "BrainConstants.h" #include "DisplayGroupEnum.h" #include "DisplayProperties.h" #include "FiberOrientationColoringTypeEnum.h" #include "FiberOrientationSymbolTypeEnum.h" namespace caret { class DisplayPropertiesFiberOrientation : public DisplayProperties { public: DisplayPropertiesFiberOrientation(); virtual ~DisplayPropertiesFiberOrientation(); virtual void reset(); virtual void update(); virtual void copyDisplayProperties(const int32_t sourceTabIndex, const int32_t targetTabIndex); bool isDisplayed(const DisplayGroupEnum::Enum displayGroup, const int32_t tabIndex) const; void setDisplayed(const DisplayGroupEnum::Enum displayGroup, const int32_t tabIndex, const bool displayStatus); bool isDrawWithMagnitude(const DisplayGroupEnum::Enum displayGroup, const int32_t tabIndex) const; void setDrawWithMagnitude(const DisplayGroupEnum::Enum displayGroup, const int32_t tabIndex, const bool drawWithMagnitude); DisplayGroupEnum::Enum getDisplayGroupForTab(const int32_t browserTabIndex) const; void setDisplayGroupForTab(const int32_t browserTabIndex, const DisplayGroupEnum::Enum displayGroup); float getAboveLimit(const DisplayGroupEnum::Enum displayGroup, const int32_t tabIndex) const; void setAboveLimit(const DisplayGroupEnum::Enum displayGroup, const int32_t tabIndex, const float aboveLimit); void setAboveAndBelowLimitsForAll(const float aboveLimit, const float belowLimit); float getBelowLimit(const DisplayGroupEnum::Enum displayGroup, const int32_t tabIndex) const; void setBelowLimit(const DisplayGroupEnum::Enum displayGroup, const int32_t tabIndex, const float aboveLimit); float getMinimumMagnitude(const DisplayGroupEnum::Enum displayGroup, const int32_t tabIndex) const; void setMinimumMagnitude(const DisplayGroupEnum::Enum displayGroup, const int32_t tabIndex, const float minimumMagnitude); float getLengthMultiplier(const DisplayGroupEnum::Enum displayGroup, const int32_t tabIndex) const; void setLengthMultiplier(const DisplayGroupEnum::Enum displayGroup, const int32_t tabIndex, const float lengthMultiplier); float getFanMultiplier(const DisplayGroupEnum::Enum displayGroup, const int32_t tabIndex) const; void setFanMultiplier(const DisplayGroupEnum::Enum displayGroup, const int32_t tabIndex, const float fanMultiplier); FiberOrientationColoringTypeEnum::Enum getColoringType(const DisplayGroupEnum::Enum displayGroup, const int32_t tabIndex) const; void setColoringType(const DisplayGroupEnum::Enum displayGroup, const int32_t tabIndex, const FiberOrientationColoringTypeEnum::Enum coloringType); FiberOrientationSymbolTypeEnum::Enum getSymbolType(const DisplayGroupEnum::Enum displayGroup, const int32_t tabIndex) const; void setSymbolType(const DisplayGroupEnum::Enum displayGroup, const int32_t tabIndex, const FiberOrientationSymbolTypeEnum::Enum symbolType); bool isSphereOrientationsDisplayed(const DisplayGroupEnum::Enum displayGroup, const int32_t tabIndex) const; void setSphereOrientationsDisplayed(const DisplayGroupEnum::Enum displayGroup, const int32_t tabIndex, const bool displaySphereOrientations); virtual SceneClass* saveToScene(const SceneAttributes* sceneAttributes, const AString& instanceName); virtual void restoreFromScene(const SceneAttributes* sceneAttributes, const SceneClass* sceneClass); private: DisplayPropertiesFiberOrientation(const DisplayPropertiesFiberOrientation&); DisplayPropertiesFiberOrientation& operator=(const DisplayPropertiesFiberOrientation&); DisplayGroupEnum::Enum m_displayGroup[BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS]; bool m_displayStatusInDisplayGroup[DisplayGroupEnum::NUMBER_OF_GROUPS]; bool m_displayStatusInTab[BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS]; float m_aboveLimitInDisplayGroup[DisplayGroupEnum::NUMBER_OF_GROUPS]; float m_aboveLimitInTab[BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS]; float m_belowLimitInDisplayGroup[DisplayGroupEnum::NUMBER_OF_GROUPS]; float m_belowLimitInTab[BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS]; float m_minimumMagnitudeInDisplayGroup[DisplayGroupEnum::NUMBER_OF_GROUPS]; float m_minimumMagnitudeInTab[BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS]; bool m_drawWithMagnitudeInDisplayGroup[DisplayGroupEnum::NUMBER_OF_GROUPS]; bool m_drawWithMagnitudeInTab[BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS]; float m_lengthMultiplierInDisplayGroup[DisplayGroupEnum::NUMBER_OF_GROUPS]; float m_lengthMultiplierInTab[BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS]; float m_fanMultiplierInDisplayGroup[DisplayGroupEnum::NUMBER_OF_GROUPS]; float m_fanMultiplierInTab[BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS]; FiberOrientationColoringTypeEnum::Enum m_fiberColoringTypeInDisplayGroup[DisplayGroupEnum::NUMBER_OF_GROUPS]; FiberOrientationColoringTypeEnum::Enum m_fiberColoringTypeInTab[BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS]; FiberOrientationSymbolTypeEnum::Enum m_fiberSymbolTypeInDisplayGroup[DisplayGroupEnum::NUMBER_OF_GROUPS]; FiberOrientationSymbolTypeEnum::Enum m_fiberSymbolTypeInTab[BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS]; bool m_displaySphereOrientationsInDisplayGroup[DisplayGroupEnum::NUMBER_OF_GROUPS]; bool m_displaySphereOrientationsInTab[BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS]; // friend class BrainOpenGLFixedPipeline; }; #ifdef __DISPLAY_PROPERTIES_FIBER_ORIENTATION_DECLARE__ // #endif // __DISPLAY_PROPERTIES_FIBER_ORIENTATION_DECLARE__ } // namespace #endif //__DISPLAY_PROPERTIES_FIBER_ORIENTATION__H_ connectome-workbench-1.2.3+git41-gc4c6c90/src/Brain/DisplayPropertiesFoci.cxx000066400000000000000000000557011300200146000266160ustar00rootroot00000000000000 /*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 __DISPLAY_PROPERTIES_FOCI_DECLARE__ #include "DisplayPropertiesFoci.h" #undef __DISPLAY_PROPERTIES_FOCI_DECLARE__ #include "CaretAssert.h" #include "SceneAttributes.h" #include "SceneClass.h" #include "SceneClassAssistant.h" using namespace caret; /** * \class caret::DisplayPropertiesFoci * \brief Contains display properties for foci. */ /** * Constructor. */ DisplayPropertiesFoci::DisplayPropertiesFoci() : DisplayProperties() { const CaretColorEnum::Enum defaultColor = CaretColorEnum::BLACK; for (int32_t i = 0; i < BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS; i++) { m_displayGroup[i] = DisplayGroupEnum::getDefaultValue(); m_pasteOntoSurfaceInTab[i] = false; m_displayStatusInTab[i] = false; m_contralateralDisplayStatusInTab[i] = false; m_fociSizeInTab[i] = 4.0; m_coloringTypeInTab[i] = FeatureColoringTypeEnum::FEATURE_COLORING_TYPE_NAME; m_drawingTypeInTab[i] = FociDrawingTypeEnum::DRAW_AS_SQUARES; m_standardColorTypeInTab[i] = defaultColor; } for (int32_t i = 0; i < DisplayGroupEnum::NUMBER_OF_GROUPS; i++) { m_pasteOntoSurfaceInDisplayGroup[i] = false; m_displayStatusInDisplayGroup[i] = false; m_contralateralDisplayStatusInDisplayGroup[i] = false; m_fociSizeInDisplayGroup[i] = 4.0; m_coloringTypeInDisplayGroup[i] = FeatureColoringTypeEnum::FEATURE_COLORING_TYPE_NAME; m_drawingTypeInDisplayGroup[i] = FociDrawingTypeEnum::DRAW_AS_SQUARES; m_standardColorTypeInDisplayGroup[i] = defaultColor; } m_sceneAssistant->addTabIndexedEnumeratedTypeArray("m_displayGroup", m_displayGroup); m_sceneAssistant->addTabIndexedBooleanArray("m_pasteOntoSurfaceInTab", m_pasteOntoSurfaceInTab); m_sceneAssistant->addTabIndexedBooleanArray("m_displayStatusInTab", m_displayStatusInTab); m_sceneAssistant->addTabIndexedBooleanArray("m_contralateralDisplayStatusInTab", m_contralateralDisplayStatusInTab); m_sceneAssistant->addTabIndexedFloatArray("m_fociSizeInTab", m_fociSizeInTab); m_sceneAssistant->addTabIndexedEnumeratedTypeArray("m_coloringTypeInTab", m_coloringTypeInTab); m_sceneAssistant->addTabIndexedEnumeratedTypeArray("m_drawingTypeInTab", m_drawingTypeInTab); m_sceneAssistant->addArray("m_pasteOntoSurfaceInDisplayGroup", m_pasteOntoSurfaceInDisplayGroup, DisplayGroupEnum::NUMBER_OF_GROUPS, m_pasteOntoSurfaceInDisplayGroup[0]); m_sceneAssistant->addArray("m_displayStatusInDisplayGroup", m_displayStatusInDisplayGroup, DisplayGroupEnum::NUMBER_OF_GROUPS, m_displayStatusInDisplayGroup[0]); m_sceneAssistant->addArray("m_contralateralDisplayStatusInDisplayGroup", m_contralateralDisplayStatusInDisplayGroup, DisplayGroupEnum::NUMBER_OF_GROUPS, m_contralateralDisplayStatusInDisplayGroup[0]); m_sceneAssistant->addArray("m_fociSizeInDisplayGroup", m_fociSizeInDisplayGroup, DisplayGroupEnum::NUMBER_OF_GROUPS, m_fociSizeInDisplayGroup[0]); m_sceneAssistant->addArray("m_coloringTypeInDisplayGroup", m_coloringTypeInDisplayGroup, DisplayGroupEnum::NUMBER_OF_GROUPS, FeatureColoringTypeEnum::FEATURE_COLORING_TYPE_NAME); m_sceneAssistant->addArray("m_drawingTypeInDisplayGroup", m_drawingTypeInDisplayGroup, DisplayGroupEnum::NUMBER_OF_GROUPS, FociDrawingTypeEnum::DRAW_AS_SQUARES); m_sceneAssistant->addArray("m_standardColorTypeInDisplayGroup", m_standardColorTypeInDisplayGroup, DisplayGroupEnum::NUMBER_OF_GROUPS, defaultColor); m_sceneAssistant->addTabIndexedEnumeratedTypeArray("m_standardColorTypeInTab", m_standardColorTypeInTab); } /** * Destructor. */ DisplayPropertiesFoci::~DisplayPropertiesFoci() { } /** * Copy the border display properties from one tab to another. * @param sourceTabIndex * Index of tab from which properties are copied. * @param targetTabIndex * Index of tab to which properties are copied. */ void DisplayPropertiesFoci::copyDisplayProperties(const int32_t sourceTabIndex, const int32_t targetTabIndex) { const DisplayGroupEnum::Enum displayGroup = this->getDisplayGroupForTab(sourceTabIndex); this->setDisplayGroupForTab(targetTabIndex, displayGroup); m_coloringTypeInTab[targetTabIndex] = m_coloringTypeInTab[sourceTabIndex]; m_contralateralDisplayStatusInTab[targetTabIndex] = m_contralateralDisplayStatusInTab[sourceTabIndex]; m_displayStatusInTab[targetTabIndex] = m_displayStatusInTab[sourceTabIndex]; m_drawingTypeInTab[targetTabIndex] = m_drawingTypeInTab[sourceTabIndex]; m_fociSizeInTab[targetTabIndex] = m_fociSizeInTab[sourceTabIndex]; m_pasteOntoSurfaceInTab[targetTabIndex] = m_pasteOntoSurfaceInTab[sourceTabIndex]; m_standardColorTypeInTab[targetTabIndex] = m_standardColorTypeInTab[sourceTabIndex]; } /** * Reset all settings to their defaults * and remove any data. */ void DisplayPropertiesFoci::reset() { // for (int32_t i = 0; i < BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS; i++) { // m_displayStatus[i] = true; // m_contralateralDisplayStatus[i] = false; // m_displayGroup[i] = DisplayGroupEnum::DISPLAY_ALL_WINDOWS; // m_pasteOntoSurface[i] = false; // } } /** * Update due to changes in data. */ void DisplayPropertiesFoci::update() { } /** * @return Display status of foci. * @param displayGroup * Display group. */ bool DisplayPropertiesFoci::isDisplayed(const DisplayGroupEnum::Enum displayGroup, const int32_t tabIndex) const { CaretAssertArrayIndex(m_displayStatusInDisplayGroup, DisplayGroupEnum::NUMBER_OF_GROUPS, static_cast(displayGroup)); if (displayGroup == DisplayGroupEnum::DISPLAY_GROUP_TAB) { CaretAssertArrayIndex(m_displayStatusInTab, BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS, tabIndex); return m_displayStatusInTab[tabIndex]; } return m_displayStatusInDisplayGroup[displayGroup]; } /** * Set the display status for foci. * @param displayGroup * Display group. * @param displayStatus * New status. */ void DisplayPropertiesFoci::setDisplayed(const DisplayGroupEnum::Enum displayGroup, const int32_t tabIndex, const bool displayStatus) { CaretAssertArrayIndex(m_displayStatusInDisplayGroup, DisplayGroupEnum::NUMBER_OF_GROUPS, static_cast(displayGroup)); if (displayGroup == DisplayGroupEnum::DISPLAY_GROUP_TAB) { CaretAssertArrayIndex(m_displayStatusInTab, BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS, tabIndex); m_displayStatusInTab[tabIndex] = displayStatus; } else { m_displayStatusInDisplayGroup[displayGroup] = displayStatus; } } /** * @return Contralateral display status of foci. * @param displayGroup * Display group. * @param browserTabIndex * Index of browser tab. */ bool DisplayPropertiesFoci::isContralateralDisplayed(const DisplayGroupEnum::Enum displayGroup, const int32_t tabIndex) const { CaretAssertArrayIndex(m_contralateralDisplayStatusInDisplayGroup, DisplayGroupEnum::NUMBER_OF_GROUPS, static_cast(displayGroup)); if (displayGroup == DisplayGroupEnum::DISPLAY_GROUP_TAB) { CaretAssertArrayIndex(m_contralateralDisplayStatusInTab, BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS, tabIndex); return m_contralateralDisplayStatusInTab[tabIndex]; } return m_contralateralDisplayStatusInDisplayGroup[displayGroup]; } /** * Set the contralateral display status for foci. * @param displayGroup * Display group. * @param contralateralDisplayStatus * New status. */ void DisplayPropertiesFoci::setContralateralDisplayed(const DisplayGroupEnum::Enum displayGroup, const int32_t tabIndex, const bool contralateralDisplayStatus) { CaretAssertArrayIndex(m_contralateralDisplayStatusInDisplayGroup, DisplayGroupEnum::NUMBER_OF_GROUPS, static_cast(displayGroup)); if (displayGroup == DisplayGroupEnum::DISPLAY_GROUP_TAB) { CaretAssertArrayIndex(m_contralateralDisplayStatusInTab, BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS, tabIndex); m_contralateralDisplayStatusInTab[tabIndex] = contralateralDisplayStatus; } else { m_contralateralDisplayStatusInDisplayGroup[displayGroup] = contralateralDisplayStatus; } } /** * Get the display group for a given browser tab. * @param browserTabIndex * Index of browser tab. */ DisplayGroupEnum::Enum DisplayPropertiesFoci::getDisplayGroupForTab(const int32_t browserTabIndex) const { CaretAssertArrayIndex(m_displayGroup, BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS, browserTabIndex); return m_displayGroup[browserTabIndex]; } /** * Set the display group for a given browser tab. * @param browserTabIndex * Index of browser tab. * @param displayGroup * New value for display group. */ void DisplayPropertiesFoci::setDisplayGroupForTab(const int32_t browserTabIndex, const DisplayGroupEnum::Enum displayGroup) { CaretAssertArrayIndex(m_displayGroup, BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS, browserTabIndex); m_displayGroup[browserTabIndex] = displayGroup; } /** * @return The foci size. * @param displayGroup * Display group. */ float DisplayPropertiesFoci::getFociSize(const DisplayGroupEnum::Enum displayGroup, const int32_t tabIndex) const { CaretAssertArrayIndex(m_fociSizeInDisplayGroup, DisplayGroupEnum::NUMBER_OF_GROUPS, static_cast(displayGroup)); if (displayGroup == DisplayGroupEnum::DISPLAY_GROUP_TAB) { CaretAssertArrayIndex(m_fociSizeInTab, BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS, tabIndex); return m_fociSizeInTab[tabIndex]; } return m_fociSizeInDisplayGroup[displayGroup]; } /** * Set the foci size to the given value. * @param displayGroup * Display group. * @param fociSize * New value for foci size. */ void DisplayPropertiesFoci::setFociSize(const DisplayGroupEnum::Enum displayGroup, const int32_t tabIndex, const float fociSize) { CaretAssertArrayIndex(m_fociSizeInDisplayGroup, DisplayGroupEnum::NUMBER_OF_GROUPS, static_cast(displayGroup)); if (displayGroup == DisplayGroupEnum::DISPLAY_GROUP_TAB) { CaretAssertArrayIndex(m_fociSizeInTab, BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS, tabIndex); m_fociSizeInTab[tabIndex] = fociSize; } else { m_fociSizeInDisplayGroup[displayGroup] = fociSize; } } /** * @return The coloring type. * @param displayGroup * Display group. */ FeatureColoringTypeEnum::Enum DisplayPropertiesFoci::getColoringType(const DisplayGroupEnum::Enum displayGroup, const int32_t tabIndex) const { CaretAssertArrayIndex(m_coloringTypeInDisplayGroup, DisplayGroupEnum::NUMBER_OF_GROUPS, static_cast(displayGroup)); if (displayGroup == DisplayGroupEnum::DISPLAY_GROUP_TAB) { CaretAssertArrayIndex(m_coloringTypeInTab, BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS, tabIndex); return m_coloringTypeInTab[tabIndex]; } return m_coloringTypeInDisplayGroup[displayGroup]; } /** * Set the coloring type. * @param displayGroup * Display group. * @param coloringType * New value for coloring type. */ void DisplayPropertiesFoci::setColoringType(const DisplayGroupEnum::Enum displayGroup, const int32_t tabIndex, const FeatureColoringTypeEnum::Enum coloringType) { CaretAssertArrayIndex(m_coloringTypeInDisplayGroup, DisplayGroupEnum::NUMBER_OF_GROUPS, static_cast(displayGroup)); if (displayGroup == DisplayGroupEnum::DISPLAY_GROUP_TAB) { CaretAssertArrayIndex(m_coloringTypeInTab, BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS, tabIndex); m_coloringTypeInTab[tabIndex] = coloringType; } else { m_coloringTypeInDisplayGroup[displayGroup] = coloringType; } } /** * @return The standard caret coloring type. * @param displayGroup * Display group. */ CaretColorEnum::Enum DisplayPropertiesFoci::getStandardColorType(const DisplayGroupEnum::Enum displayGroup, const int32_t tabIndex) const { CaretAssertArrayIndex(m_standardColorTypeInDisplayGroup, DisplayGroupEnum::NUMBER_OF_GROUPS, static_cast(displayGroup)); if (displayGroup == DisplayGroupEnum::DISPLAY_GROUP_TAB) { CaretAssertArrayIndex(m_standardColorTypeInTab, BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS, tabIndex); return m_standardColorTypeInTab[tabIndex]; } return m_standardColorTypeInDisplayGroup[displayGroup]; } /** * Set the caret coloring type. * @param displayGroup * Display group. * @param color * New color for coloring type. */ void DisplayPropertiesFoci::setStandardColorType(const DisplayGroupEnum::Enum displayGroup, const int32_t tabIndex, const CaretColorEnum::Enum color) { CaretAssertArrayIndex(m_standardColorTypeInDisplayGroup, DisplayGroupEnum::NUMBER_OF_GROUPS, static_cast(displayGroup)); if (displayGroup == DisplayGroupEnum::DISPLAY_GROUP_TAB) { CaretAssertArrayIndex(m_standardColorTypeInTab, BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS, tabIndex); m_standardColorTypeInTab[tabIndex] = color; } else { m_standardColorTypeInDisplayGroup[displayGroup] = color; } } /** * @param displayGroup * Display group. * @return The drawing type. */ FociDrawingTypeEnum::Enum DisplayPropertiesFoci::getDrawingType(const DisplayGroupEnum::Enum displayGroup, const int32_t tabIndex) const { CaretAssertArrayIndex(m_drawingTypeInDisplayGroup, DisplayGroupEnum::NUMBER_OF_GROUPS, static_cast(displayGroup)); if (displayGroup == DisplayGroupEnum::DISPLAY_GROUP_TAB) { CaretAssertArrayIndex(m_drawingTypeInTab, BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS, tabIndex); return m_drawingTypeInTab[tabIndex]; } return m_drawingTypeInDisplayGroup[displayGroup]; } /** * Set the drawing type to the given value. * @param displayGroup * Display group. * @param drawingType * New value for drawing type. */ void DisplayPropertiesFoci::setDrawingType(const DisplayGroupEnum::Enum displayGroup, const int32_t tabIndex, const FociDrawingTypeEnum::Enum drawingType) { CaretAssertArrayIndex(m_drawingTypeInDisplayGroup, DisplayGroupEnum::NUMBER_OF_GROUPS, static_cast(displayGroup)); if (displayGroup == DisplayGroupEnum::DISPLAY_GROUP_TAB) { CaretAssertArrayIndex(m_drawingTypeInTab, BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS, tabIndex); m_drawingTypeInTab[tabIndex] = drawingType; } else { m_drawingTypeInDisplayGroup[displayGroup] = drawingType; } } /** * Set paste onto surface so the foci are placed directly on the surface. * @param displayGroup * Display group. * @param enabled * True if pasting foci onto surface is enabled. */ void DisplayPropertiesFoci::setPasteOntoSurface(const DisplayGroupEnum::Enum displayGroup, const int32_t tabIndex, const bool enabled) { CaretAssertArrayIndex(m_pasteOntoSurfaceInDisplayGroup, DisplayGroupEnum::NUMBER_OF_GROUPS, static_cast(displayGroup)); if (displayGroup == DisplayGroupEnum::DISPLAY_GROUP_TAB) { CaretAssertArrayIndex(m_pasteOntoSurfaceInTab, BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS, tabIndex); m_pasteOntoSurfaceInTab[tabIndex] = enabled; } else { m_pasteOntoSurfaceInDisplayGroup[displayGroup] = enabled; } } /** * @param displayGroup * Display group. * @return True if foci are pasted onto surface. */ bool DisplayPropertiesFoci::isPasteOntoSurface(const DisplayGroupEnum::Enum displayGroup, const int32_t tabIndex) const { CaretAssertArrayIndex(m_pasteOntoSurfaceInDisplayGroup, DisplayGroupEnum::NUMBER_OF_GROUPS, static_cast(displayGroup)); if (displayGroup == DisplayGroupEnum::DISPLAY_GROUP_TAB) { CaretAssertArrayIndex(m_pasteOntoSurfaceInTab, BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS, tabIndex); return m_pasteOntoSurfaceInTab[tabIndex]; } return m_pasteOntoSurfaceInDisplayGroup[displayGroup]; } /** * Create a scene for an instance of a class. * * @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. * * @return Pointer to SceneClass object representing the state of * this object. Under some circumstances a NULL pointer may be * returned. Caller will take ownership of returned object. */ SceneClass* DisplayPropertiesFoci::saveToScene(const SceneAttributes* sceneAttributes, const AString& instanceName) { const std::vector tabIndices = sceneAttributes->getIndicesOfTabsForSavingToScene(); SceneClass* sceneClass = new SceneClass(instanceName, "DisplayPropertiesFoci", 1); m_sceneAssistant->saveMembers(sceneAttributes, sceneClass); switch (sceneAttributes->getSceneType()) { case SceneTypeEnum::SCENE_TYPE_FULL: break; case SceneTypeEnum::SCENE_TYPE_GENERIC: break; } return sceneClass; } /** * Restore the state of an instance of a class. * * @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 containing the state that was previously * saved and should be restored. */ void DisplayPropertiesFoci::restoreFromScene(const SceneAttributes* sceneAttributes, const SceneClass* sceneClass) { if (sceneClass == NULL) { return; } m_sceneAssistant->restoreMembers(sceneAttributes, sceneClass); switch (sceneAttributes->getSceneType()) { case SceneTypeEnum::SCENE_TYPE_FULL: break; case SceneTypeEnum::SCENE_TYPE_GENERIC: break; } } connectome-workbench-1.2.3+git41-gc4c6c90/src/Brain/DisplayPropertiesFoci.h000066400000000000000000000145671300200146000262500ustar00rootroot00000000000000#ifndef __DISPLAY_PROPERTIES_FOCI__H_ #define __DISPLAY_PROPERTIES_FOCI__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 "BrainConstants.h" #include "CaretColorEnum.h" #include "DisplayGroupEnum.h" #include "DisplayProperties.h" #include "FeatureColoringTypeEnum.h" #include "FociDrawingTypeEnum.h" namespace caret { class DisplayPropertiesFoci : public DisplayProperties { public: DisplayPropertiesFoci(); virtual ~DisplayPropertiesFoci(); virtual void reset(); virtual void update(); virtual void copyDisplayProperties(const int32_t sourceTabIndex, const int32_t targetTabIndex); bool isDisplayed(const DisplayGroupEnum::Enum displayGroup, const int32_t tabIndex) const; void setDisplayed(const DisplayGroupEnum::Enum displayGroup, const int32_t tabIndex, const bool displayStatus); bool isContralateralDisplayed(const DisplayGroupEnum::Enum displayGroup, const int32_t tabIndex) const; void setContralateralDisplayed(const DisplayGroupEnum::Enum displayGroup, const int32_t tabIndex, const bool contralateralDisplayStatus); DisplayGroupEnum::Enum getDisplayGroupForTab(const int32_t browserTabIndex) const; void setDisplayGroupForTab(const int32_t browserTabIndex, const DisplayGroupEnum::Enum displayGroup); float getFociSize(const DisplayGroupEnum::Enum displayGroup, const int32_t tabIndex) const; void setFociSize(const DisplayGroupEnum::Enum displayGroup, const int32_t tabIndex, const float pointSize); FeatureColoringTypeEnum::Enum getColoringType(const DisplayGroupEnum::Enum displayGroup, const int32_t tabIndex) const; void setColoringType(const DisplayGroupEnum::Enum displayGroup, const int32_t tabIndex, const FeatureColoringTypeEnum::Enum coloringType); FociDrawingTypeEnum::Enum getDrawingType(const DisplayGroupEnum::Enum displayGroup, const int32_t tabIndex) const; void setDrawingType(const DisplayGroupEnum::Enum displayGroup, const int32_t tabIndex, const FociDrawingTypeEnum::Enum drawingType); CaretColorEnum::Enum getStandardColorType(const DisplayGroupEnum::Enum displayGroup, const int32_t tabIndex) const; void setStandardColorType(const DisplayGroupEnum::Enum displayGroup, const int32_t tabIndex, const CaretColorEnum::Enum caretColor); void setPasteOntoSurface(const DisplayGroupEnum::Enum displayGroup, const int32_t tabIndex, const bool enabled); bool isPasteOntoSurface(const DisplayGroupEnum::Enum displayGroup, const int32_t tabIndex) const; virtual SceneClass* saveToScene(const SceneAttributes* sceneAttributes, const AString& instanceName); virtual void restoreFromScene(const SceneAttributes* sceneAttributes, const SceneClass* sceneClass); private: DisplayPropertiesFoci(const DisplayPropertiesFoci&); DisplayPropertiesFoci& operator=(const DisplayPropertiesFoci&); DisplayGroupEnum::Enum m_displayGroup[BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS]; bool m_displayStatusInDisplayGroup[DisplayGroupEnum::NUMBER_OF_GROUPS]; bool m_displayStatusInTab[BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS]; bool m_contralateralDisplayStatusInDisplayGroup[DisplayGroupEnum::NUMBER_OF_GROUPS]; bool m_contralateralDisplayStatusInTab[BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS]; bool m_pasteOntoSurfaceInDisplayGroup[DisplayGroupEnum::NUMBER_OF_GROUPS]; bool m_pasteOntoSurfaceInTab[BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS]; float m_fociSizeInDisplayGroup[DisplayGroupEnum::NUMBER_OF_GROUPS]; float m_fociSizeInTab[BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS]; FeatureColoringTypeEnum::Enum m_coloringTypeInDisplayGroup[DisplayGroupEnum::NUMBER_OF_GROUPS]; FeatureColoringTypeEnum::Enum m_coloringTypeInTab[BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS]; FociDrawingTypeEnum::Enum m_drawingTypeInDisplayGroup[DisplayGroupEnum::NUMBER_OF_GROUPS]; FociDrawingTypeEnum::Enum m_drawingTypeInTab[BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS]; CaretColorEnum::Enum m_standardColorTypeInDisplayGroup[DisplayGroupEnum::NUMBER_OF_GROUPS]; CaretColorEnum::Enum m_standardColorTypeInTab[BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS]; }; #ifdef __DISPLAY_PROPERTIES_FOCI_DECLARE__ // #endif // __DISPLAY_PROPERTIES_FOCI_DECLARE__ } // namespace #endif //__DISPLAY_PROPERTIES_FOCI__H_ connectome-workbench-1.2.3+git41-gc4c6c90/src/Brain/DisplayPropertiesImages.cxx000066400000000000000000000502671300200146000271450ustar00rootroot00000000000000 /*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 __DISPLAY_PROPERTIES_IMAGES_DECLARE__ #include "DisplayPropertiesImages.h" #undef __DISPLAY_PROPERTIES_IMAGES_DECLARE__ #include "Brain.h" #include "CaretLogger.h" #include "DisplayPropertyDataBoolean.h" #include "DisplayPropertyDataFloat.h" #include "ImageFile.h" #include "SceneAttributes.h" #include "SceneClass.h" #include "SceneClassAssistant.h" #include "ScenePathName.h" #include "ScenePathNameArray.h" using namespace caret; /** * \class caret::DisplayPropertiesImages * \brief Contains display properties for images. * \ingroup Brain */ /** * Constructor. */ DisplayPropertiesImages::DisplayPropertiesImages(Brain* parentBrain) : DisplayProperties(), m_parentBrain(parentBrain) { CaretAssert(parentBrain); const ImageDepthPositionEnum::Enum defaultImageDepth = ImageDepthPositionEnum::BACK; for (int32_t i = 0; i < BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS; i++) { m_displayGroup[i] = DisplayGroupEnum::getDefaultValue(); m_imageFileInTab[i] = NULL; } for (int32_t i = 0; i < DisplayGroupEnum::NUMBER_OF_GROUPS; i++) { m_imageFileInDisplayGroup[i] = NULL; } m_imageDepthPosition.initialize(defaultImageDepth); m_displayStatus.grabNew(new DisplayPropertyDataBoolean(false)); m_controlPointDisplayStatus.grabNew(new DisplayPropertyDataBoolean(true)); m_thresholdMinimum.grabNew(new DisplayPropertyDataFloat(0)); m_thresholdMaximum.grabNew(new DisplayPropertyDataFloat(255)); m_opacity.grabNew(new DisplayPropertyDataFloat(1.0)); m_sceneAssistant->add("m_displayStatus", "DisplayPropertyDataBoolean", m_displayStatus); m_sceneAssistant->add("m_controlPointDisplayStatus", "DisplayPropertyDataBoolean", m_controlPointDisplayStatus); m_sceneAssistant->addTabIndexedEnumeratedTypeArray("m_displayGroup", m_displayGroup); m_sceneAssistant->add("m_imageDepthPosition", "DisplayPropertyDataEnum", &m_imageDepthPosition); m_sceneAssistant->add("m_thresholdMinimum", "DisplayPropertyDataFloat", m_thresholdMinimum); m_sceneAssistant->add("m_thresholdMaximum", "DisplayPropertyDataFloat", m_thresholdMaximum); m_sceneAssistant->add("m_opacity", "DisplayPropertyDataFloat", m_opacity); } /** * Destructor. */ DisplayPropertiesImages::~DisplayPropertiesImages() { } /** * Copy the border display properties from one tab to another. * @param sourceTabIndex * Index of tab from which properties are copied. * @param targetTabIndex * Index of tab to which properties are copied. */ void DisplayPropertiesImages::copyDisplayProperties(const int32_t sourceTabIndex, const int32_t targetTabIndex) { const DisplayGroupEnum::Enum displayGroup = this->getDisplayGroupForTab(sourceTabIndex); this->setDisplayGroupForTab(targetTabIndex, displayGroup); m_displayStatus->copyValues(sourceTabIndex, targetTabIndex); } /** * Reset all settings to their defaults * and remove any data. */ void DisplayPropertiesImages::reset() { // for (int32_t i = 0; i < BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS; i++) { // m_displayStatus[i] = true; // m_contralateralDisplayStatus[i] = false; // m_displayGroup[i] = DisplayGroupEnum::DISPLAY_ALL_WINDOWS; // m_pasteOntoSurface[i] = false; // } } /** * Update due to changes in data. */ void DisplayPropertiesImages::update() { } /** * @return Display status of images. * @param displayGroup * Display group. * @param tabIndex * The tab index. */ bool DisplayPropertiesImages::isDisplayed(const DisplayGroupEnum::Enum displayGroup, const int32_t tabIndex) const { return m_displayStatus->getValue(displayGroup, tabIndex); } /** * Set the display status for image. * @param displayGroup * Display group. * @param tabIndex * The tab index. * @param displayStatus * New status. */ void DisplayPropertiesImages::setDisplayed(const DisplayGroupEnum::Enum displayGroup, const int32_t tabIndex, const bool displayStatus) { m_displayStatus->setValue(displayGroup, tabIndex, displayStatus); } /** * @return Display status of image control points. * @param displayGroup * Display group. * @param tabIndex * The tab index. */ bool DisplayPropertiesImages::isControlPointsDisplayed(const DisplayGroupEnum::Enum displayGroup, const int32_t tabIndex) const { return m_controlPointDisplayStatus->getValue(displayGroup, tabIndex); } /** * Set the display status for image control points. * @param displayGroup * Display group. * @param tabIndex * The tab index. * @param displayStatus * New status. */ void DisplayPropertiesImages::setControlPointsDisplayed(const DisplayGroupEnum::Enum displayGroup, const int32_t tabIndex, const bool displayStatus) { m_controlPointDisplayStatus->setValue(displayGroup, tabIndex, displayStatus); } /** * Get the display group for a given browser tab. * @param browserTabIndex * Index of browser tab. */ DisplayGroupEnum::Enum DisplayPropertiesImages::getDisplayGroupForTab(const int32_t browserTabIndex) const { CaretAssertArrayIndex(m_displayGroup, BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS, browserTabIndex); return m_displayGroup[browserTabIndex]; } /** * Set the display group for a given browser tab. * @param browserTabIndex * Index of browser tab. * @param displayGroup * New value for display group. */ void DisplayPropertiesImages::setDisplayGroupForTab(const int32_t browserTabIndex, const DisplayGroupEnum::Enum displayGroup) { CaretAssertArrayIndex(m_displayGroup, BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS, browserTabIndex); m_displayGroup[browserTabIndex] = displayGroup; } /** * Get the selected image file. * * @param displayGroup * Display group. * @param tabIndex * The tab index. * @return * The selected image file. */ ImageFile* DisplayPropertiesImages::getSelectedImageFile(const DisplayGroupEnum::Enum displayGroup, const int32_t tabIndex) const { ImageFile* imageFile = NULL; CaretAssertArrayIndex(m_imageFileInDisplayGroup, DisplayGroupEnum::NUMBER_OF_GROUPS, static_cast(displayGroup)); if (displayGroup == DisplayGroupEnum::DISPLAY_GROUP_TAB) { CaretAssertArrayIndex(m_imageFileInTab, BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS, tabIndex); imageFile = m_imageFileInTab[tabIndex]; } else { const int32_t displayGroupInt = DisplayGroupEnum::toIntegerCode(displayGroup); CaretAssertArrayIndex(m_imageFileInDisplayGroup, DisplayGroupEnum::NUMBER_OF_GROUPS, displayGroupInt); imageFile = m_imageFileInDisplayGroup[displayGroupInt]; } const std::vector allImageFiles = m_parentBrain->getAllImagesFiles(); if (imageFile != NULL) { if (std::find(allImageFiles.begin(), allImageFiles.end(), imageFile) == allImageFiles.end()) { imageFile = NULL; } } if (imageFile == NULL) { if ( ! allImageFiles.empty()) { imageFile = allImageFiles[0]; } if (displayGroup == DisplayGroupEnum::DISPLAY_GROUP_TAB) { CaretAssertArrayIndex(m_imageFileInTab, BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS, tabIndex); m_imageFileInTab[tabIndex] = imageFile; } else { CaretAssertArrayIndex(m_imageFileInDisplayGroup, DisplayGroupEnum::NUMBER_OF_GROUPS, (int)displayGroup); m_imageFileInDisplayGroup[displayGroup] = imageFile; } } return imageFile; } /** * Set the selected image. * @param displayGroup * Display group. * @param tabIndex * The tab index. * @param imageFile * Newly selected image file. */ void DisplayPropertiesImages::setSelectedImageFile(const DisplayGroupEnum::Enum displayGroup, const int32_t tabIndex, ImageFile* imageFile) { CaretAssertArrayIndex(m_imageFileInDisplayGroup, DisplayGroupEnum::NUMBER_OF_GROUPS, static_cast(displayGroup)); if (displayGroup == DisplayGroupEnum::DISPLAY_GROUP_TAB) { CaretAssertArrayIndex(m_imageFileInTab, BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS, tabIndex); m_imageFileInTab[tabIndex] = imageFile; } else { m_imageFileInDisplayGroup[displayGroup] = imageFile; } } /** * Get the minimum threshold value. * * @param displayGroup * Selected display group. * @param tabIndex * Selected tab. * @return * Threshold value for given display group and tab. */ float DisplayPropertiesImages::getThresholdMinimum(const DisplayGroupEnum::Enum displayGroup, const int32_t tabIndex) const { return m_thresholdMinimum->getValue(displayGroup, tabIndex); } /** * Set the minimum threshold value. * * @param displayGroup * Selected display group. * @param tabIndex * Selected tab. * @param value * Threshold value for given display group and tab. */ void DisplayPropertiesImages::setThresholdMinimum(const DisplayGroupEnum::Enum displayGroup, const int32_t tabIndex, const float value) { m_thresholdMinimum->setValue(displayGroup, tabIndex, value); } /** * Get the maximum threshold value. * * @param displayGroup * Selected display group. * @param tabIndex * Selected tab. * @return * Threshold value for given display group and tab. */ float DisplayPropertiesImages::getThresholdMaximum(const DisplayGroupEnum::Enum displayGroup, const int32_t tabIndex) const { return m_thresholdMaximum->getValue(displayGroup, tabIndex); } /** * Set the maximum threshold value. * * @param displayGroup * Selected display group. * @param tabIndex * Selected tab. * @param value * Threshold value for given display group and tab. */ void DisplayPropertiesImages::setThresholdMaximum(const DisplayGroupEnum::Enum displayGroup, const int32_t tabIndex, const float value) { m_thresholdMaximum->setValue(displayGroup, tabIndex, value); } /** * Get the opacity value. * * @param displayGroup * Selected display group. * @param tabIndex * Selected tab. * @return * Opacity value for given display group and tab. */ float DisplayPropertiesImages::getOpacity(const DisplayGroupEnum::Enum displayGroup, const int32_t tabIndex) const { return m_opacity->getValue(displayGroup, tabIndex); } /** * Set the opacity value. * * @param displayGroup * Selected display group. * @param tabIndex * Selected tab. * @param value * Opacity value for given display group and tab. */ void DisplayPropertiesImages::setOpacity(const DisplayGroupEnum::Enum displayGroup, const int32_t tabIndex, const float value) { m_opacity->setValue(displayGroup, tabIndex, value); } /** * @return The image position type. * @param displayGroup * Selected display group. * @param tabIndex * Selected tab. */ ImageDepthPositionEnum::Enum DisplayPropertiesImages::getImagePosition(const DisplayGroupEnum::Enum displayGroup, const int32_t tabIndex) const { return m_imageDepthPosition.getValue(displayGroup, tabIndex); } /** * Set the image position type. * @param displayGroup * Selected display group. * @param tabIndex * Selected tab. * @param positionType * New value for position type. */ void DisplayPropertiesImages::setImagePosition(const DisplayGroupEnum::Enum displayGroup, const int32_t tabIndex, const ImageDepthPositionEnum::Enum positionType) { m_imageDepthPosition.setValue(displayGroup, tabIndex, positionType); } /** * Create a scene for an instance of a class. * * @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. * * @return Pointer to SceneClass object representing the state of * this object. Under some circumstances a NULL pointer may be * returned. Caller will take ownership of returned object. */ SceneClass* DisplayPropertiesImages::saveToScene(const SceneAttributes* sceneAttributes, const AString& instanceName) { const std::vector tabIndices = sceneAttributes->getIndicesOfTabsForSavingToScene(); SceneClass* sceneClass = new SceneClass(instanceName, "DisplayPropertiesImages", 1); m_sceneAssistant->saveMembers(sceneAttributes, sceneClass); switch (sceneAttributes->getSceneType()) { case SceneTypeEnum::SCENE_TYPE_FULL: break; case SceneTypeEnum::SCENE_TYPE_GENERIC: break; } std::vector pathNamesTabs; for (int32_t iTab = 0; iTab < BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS; iTab++) { const ImageFile* imageFile = getSelectedImageFile(DisplayGroupEnum::DISPLAY_GROUP_TAB, iTab); if (imageFile != NULL) { pathNamesTabs.push_back(imageFile->getFileName()); } else { pathNamesTabs.push_back(""); } } ScenePathNameArray* tabArray = new ScenePathNameArray("m_imageFileInTab", pathNamesTabs); sceneClass->addChild(tabArray); std::vector pathNamesDisplayGroup; for (int32_t idg = 0; idg < DisplayGroupEnum::NUMBER_OF_GROUPS; idg++) { DisplayGroupEnum::Enum displayGroup = DisplayGroupEnum::fromIntegerCode(idg, NULL); const ImageFile* imageFile = getSelectedImageFile(displayGroup, 0); if (imageFile != NULL) { pathNamesDisplayGroup.push_back(imageFile->getFileName()); } else { pathNamesDisplayGroup.push_back(""); } } ScenePathNameArray* displayGroupArray = new ScenePathNameArray("m_imageFileInDisplayGroup", pathNamesDisplayGroup); sceneClass->addChild(displayGroupArray); return sceneClass; } /** * Restore the state of an instance of a class. * * @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 containing the state that was previously * saved and should be restored. */ void DisplayPropertiesImages::restoreFromScene(const SceneAttributes* sceneAttributes, const SceneClass* sceneClass) { for (int32_t i = 0; i < BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS; i++) { m_imageFileInTab[i] = NULL; } for (int32_t i = 0; i < DisplayGroupEnum::NUMBER_OF_GROUPS; i++) { m_imageFileInDisplayGroup[i] = NULL; } if (sceneClass == NULL) { return; } m_sceneAssistant->restoreMembers(sceneAttributes, sceneClass); switch (sceneAttributes->getSceneType()) { case SceneTypeEnum::SCENE_TYPE_FULL: break; case SceneTypeEnum::SCENE_TYPE_GENERIC: break; } const ScenePathNameArray* tabArray = sceneClass->getPathNameArray("m_imageFileInTab"); if (tabArray != NULL) { const int32_t numElements = tabArray->getNumberOfArrayElements(); const int32_t maxElem = std::min(numElements, (int32_t)BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS); for (int32_t iTab = 0; iTab < maxElem; iTab++) { const ScenePathName* spn = tabArray->getScenePathNameAtIndex(iTab); CaretAssert(spn); const AString filename = spn->stringValue(); if ( ! filename.isEmpty()) { m_imageFileInTab[iTab] = findImageFile(filename); } } } const ScenePathNameArray* displayGroupArray = sceneClass->getPathNameArray("m_imageFileInDisplayGroup"); if (displayGroupArray != NULL) { const int32_t numElements = displayGroupArray->getNumberOfArrayElements(); const int32_t maxElem = std::min(numElements, (int32_t)DisplayGroupEnum::NUMBER_OF_GROUPS); for (int32_t idg = 0; idg < maxElem; idg++) { const ScenePathName* spn = displayGroupArray->getScenePathNameAtIndex(idg); CaretAssert(spn); const AString filename = spn->stringValue(); if ( ! filename.isEmpty()) { DisplayGroupEnum::Enum displayGroup = DisplayGroupEnum::fromIntegerCode(idg, NULL); m_imageFileInDisplayGroup[displayGroup] = findImageFile(filename); } } } } /** * Find an image file by matching the full path name to the name of the * image file name. If full path name does not match match to just * the name of the file without any path. * * @param imageFiles * All of the image files. * @param imageFileName * Name of the image file. * @return * Matched image file, otherwise NULL. */ ImageFile* DisplayPropertiesImages::findImageFile(const AString& imageFileName) const { ImageFile* fullPathNameFile = NULL; ImageFile* nameOnlyFile = NULL; FileInformation fileInfo(imageFileName); const AString nameNoPath = fileInfo.getFileName(); const std::vector allImageFiles = m_parentBrain->getAllImagesFiles(); for (std::vector::const_iterator iter = allImageFiles.begin(); iter != allImageFiles.end(); iter++) { ImageFile* imageFile = *iter; if (imageFile->getFileName() == imageFileName) { fullPathNameFile = imageFile; break; } if (imageFile->getFileNameNoPath() == nameNoPath) { nameOnlyFile = imageFile; } } if (fullPathNameFile != NULL) { return fullPathNameFile; } if (nameOnlyFile == NULL) { CaretLogWarning("Unable to find image file: " + imageFileName); } return nameOnlyFile; } connectome-workbench-1.2.3+git41-gc4c6c90/src/Brain/DisplayPropertiesImages.h000066400000000000000000000136221300200146000265640ustar00rootroot00000000000000#ifndef __DISPLAY_PROPERTIES_IMAGES_H__ #define __DISPLAY_PROPERTIES_IMAGES_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 "BrainConstants.h" #include "CaretPointer.h" #include "DisplayGroupEnum.h" #include "DisplayProperties.h" #include "DisplayPropertyDataEnum.h" #include "ImageDepthPositionEnum.h" namespace caret { class Brain; class DisplayPropertyDataBoolean; class DisplayPropertyDataFloat; class ImageFile; class DisplayPropertiesImages : public DisplayProperties { public: DisplayPropertiesImages(Brain* parentBrain); virtual ~DisplayPropertiesImages(); virtual void reset(); virtual void update(); virtual void copyDisplayProperties(const int32_t sourceTabIndex, const int32_t targetTabIndex); bool isDisplayed(const DisplayGroupEnum::Enum displayGroup, const int32_t tabIndex) const; void setDisplayed(const DisplayGroupEnum::Enum displayGroup, const int32_t tabIndex, const bool displayStatus); bool isControlPointsDisplayed(const DisplayGroupEnum::Enum displayGroup, const int32_t tabIndex) const; void setControlPointsDisplayed(const DisplayGroupEnum::Enum displayGroup, const int32_t tabIndex, const bool displayStatus); DisplayGroupEnum::Enum getDisplayGroupForTab(const int32_t browserTabIndex) const; void setDisplayGroupForTab(const int32_t browserTabIndex, const DisplayGroupEnum::Enum displayGroup); ImageFile* getSelectedImageFile(const DisplayGroupEnum::Enum displayGroup, const int32_t tabIndex) const; void setSelectedImageFile(const DisplayGroupEnum::Enum displayGroup, const int32_t tabIndex, ImageFile* imageFile); float getThresholdMinimum(const DisplayGroupEnum::Enum displayGroup, const int32_t tabIndex) const; void setThresholdMinimum(const DisplayGroupEnum::Enum displayGroup, const int32_t tabIndex, const float value); float getThresholdMaximum(const DisplayGroupEnum::Enum displayGroup, const int32_t tabIndex) const; void setThresholdMaximum(const DisplayGroupEnum::Enum displayGroup, const int32_t tabIndex, const float value); float getOpacity(const DisplayGroupEnum::Enum displayGroup, const int32_t tabIndex) const; void setOpacity(const DisplayGroupEnum::Enum displayGroup, const int32_t tabIndex, const float value); ImageDepthPositionEnum::Enum getImagePosition(const DisplayGroupEnum::Enum displayGroup, const int32_t tabIndex) const; void setImagePosition(const DisplayGroupEnum::Enum displayGroup, const int32_t tabIndex, const ImageDepthPositionEnum::Enum positionType); virtual SceneClass* saveToScene(const SceneAttributes* sceneAttributes, const AString& instanceName); virtual void restoreFromScene(const SceneAttributes* sceneAttributes, const SceneClass* sceneClass); // ADD_NEW_METHODS_HERE private: DisplayPropertiesImages(const DisplayPropertiesImages&); DisplayPropertiesImages& operator=(const DisplayPropertiesImages&); ImageFile* findImageFile(const AString& imageFileName) const; Brain* m_parentBrain; DisplayGroupEnum::Enum m_displayGroup[BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS]; CaretPointer m_displayStatus; CaretPointer m_controlPointDisplayStatus; mutable ImageFile* m_imageFileInDisplayGroup[DisplayGroupEnum::NUMBER_OF_GROUPS]; mutable ImageFile* m_imageFileInTab[BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS]; DisplayPropertyDataEnum m_imageDepthPosition; CaretPointer m_thresholdMinimum; CaretPointer m_thresholdMaximum; CaretPointer m_opacity; // ADD_NEW_MEMBERS_HERE }; #ifdef __DISPLAY_PROPERTIES_IMAGES_DECLARE__ // #endif // __DISPLAY_PROPERTIES_IMAGES_DECLARE__ } // namespace #endif //__DISPLAY_PROPERTIES_IMAGES_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Brain/DisplayPropertiesLabels.cxx000066400000000000000000000127041300200146000271340ustar00rootroot00000000000000 /*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 __DISPLAY_PROPERTIES_LABELS_DECLARE__ #include "DisplayPropertiesLabels.h" #include "SceneClassAssistant.h" #include "SceneAttributes.h" #include "SceneClass.h" #undef __DISPLAY_PROPERTIES_LABELS_DECLARE__ using namespace caret; /** * \class caret::DisplayPropertiesLabels * \brief Display properties for labels */ /** * Constructor. */ DisplayPropertiesLabels::DisplayPropertiesLabels() : DisplayProperties() { for (int32_t i = 0; i < BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS; i++) { m_displayGroup[i] = DisplayGroupEnum::getDefaultValue(); } m_sceneAssistant->addTabIndexedEnumeratedTypeArray("m_displayGroup", m_displayGroup); } /** * Destructor. */ DisplayPropertiesLabels::~DisplayPropertiesLabels() { } /** * Copy the border display properties from one tab to another. * @param sourceTabIndex * Index of tab from which properties are copied. * @param targetTabIndex * Index of tab to which properties are copied. */ void DisplayPropertiesLabels::copyDisplayProperties(const int32_t sourceTabIndex, const int32_t targetTabIndex) { const DisplayGroupEnum::Enum displayGroup = this->getDisplayGroupForTab(sourceTabIndex); this->setDisplayGroupForTab(targetTabIndex, displayGroup); } /** * Reset all settings to their defaults * and remove any data. */ void DisplayPropertiesLabels::reset() { } /** * Update due to changes in data. */ void DisplayPropertiesLabels::update() { } /** * Get the display group for a given browser tab. * @param browserTabIndex * Index of browser tab. */ DisplayGroupEnum::Enum DisplayPropertiesLabels::getDisplayGroupForTab(const int32_t browserTabIndex) const { CaretAssertArrayIndex(m_displayGroup, BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS, browserTabIndex); return m_displayGroup[browserTabIndex]; } /** * Set the display group for a given browser tab. * @param browserTabIndex * Index of browser tab. * @param displayGroup * New value for display group. */ void DisplayPropertiesLabels::setDisplayGroupForTab(const int32_t browserTabIndex, const DisplayGroupEnum::Enum displayGroup) { CaretAssertArrayIndex(m_displayGroup, BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS, browserTabIndex); m_displayGroup[browserTabIndex] = displayGroup; } /** * Create a scene for an instance of a class. * * @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. * * @return Pointer to SceneClass object representing the state of * this object. Under some circumstances a NULL pointer may be * returned. Caller will take ownership of returned object. */ SceneClass* DisplayPropertiesLabels::saveToScene(const SceneAttributes* sceneAttributes, const AString& instanceName) { const std::vector tabIndices = sceneAttributes->getIndicesOfTabsForSavingToScene(); SceneClass* sceneClass = new SceneClass(instanceName, "DisplayPropertiesLabels", 1); m_sceneAssistant->saveMembers(sceneAttributes, sceneClass); switch (sceneAttributes->getSceneType()) { case SceneTypeEnum::SCENE_TYPE_FULL: break; case SceneTypeEnum::SCENE_TYPE_GENERIC: break; } return sceneClass; } /** * Restore the state of an instance of a class. * * @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 containing the state that was previously * saved and should be restored. */ void DisplayPropertiesLabels::restoreFromScene(const SceneAttributes* sceneAttributes, const SceneClass* sceneClass) { if (sceneClass == NULL) { return; } m_sceneAssistant->restoreMembers(sceneAttributes, sceneClass); switch (sceneAttributes->getSceneType()) { case SceneTypeEnum::SCENE_TYPE_FULL: break; case SceneTypeEnum::SCENE_TYPE_GENERIC: break; } } connectome-workbench-1.2.3+git41-gc4c6c90/src/Brain/DisplayPropertiesLabels.h000066400000000000000000000050461300200146000265620ustar00rootroot00000000000000#ifndef __DISPLAY_PROPERTIES_LABELS__H_ #define __DISPLAY_PROPERTIES_LABELS__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 "BrainConstants.h" #include "DisplayGroupEnum.h" #include "DisplayProperties.h" namespace caret { class DisplayPropertiesLabels : public DisplayProperties { public: DisplayPropertiesLabels(); virtual ~DisplayPropertiesLabels(); virtual void reset(); virtual void update(); virtual void copyDisplayProperties(const int32_t sourceTabIndex, const int32_t targetTabIndex); virtual SceneClass* saveToScene(const SceneAttributes* sceneAttributes, const AString& instanceName); virtual void restoreFromScene(const SceneAttributes* sceneAttributes, const SceneClass* sceneClass); DisplayGroupEnum::Enum getDisplayGroupForTab(const int32_t browserTabIndex) const; void setDisplayGroupForTab(const int32_t browserTabIndex, const DisplayGroupEnum::Enum displayGroup); private: DisplayPropertiesLabels(const DisplayPropertiesLabels&); DisplayPropertiesLabels& operator=(const DisplayPropertiesLabels&); public: // ADD_NEW_METHODS_HERE private: // ADD_NEW_MEMBERS_HERE DisplayGroupEnum::Enum m_displayGroup[BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS]; }; #ifdef __DISPLAY_PROPERTIES_LABELS_DECLARE__ // #endif // __DISPLAY_PROPERTIES_LABELS_DECLARE__ } // namespace #endif //__DISPLAY_PROPERTIES_LABELS__H_ connectome-workbench-1.2.3+git41-gc4c6c90/src/Brain/DisplayPropertiesSurface.cxx000066400000000000000000000144111300200146000273170ustar00rootroot00000000000000 /*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 __DISPLAY_PROPERTIES_SURFACE_DECLARE__ #include "DisplayPropertiesSurface.h" #undef __DISPLAY_PROPERTIES_SURFACE_DECLARE__ #include "SceneClassAssistant.h" #include "SceneAttributes.h" #include "SceneClass.h" using namespace caret; /** * \class DisplayPropertiesSurface * \brief Display properties for surface drawing attributes. * * Display properties for surface drawing attributes. */ /** * Constructor. */ DisplayPropertiesSurface::DisplayPropertiesSurface() : DisplayProperties() { m_displayNormalVectors = false; m_linkSize = 2.0; m_nodeSize = 2.0; m_surfaceDrawingType = SurfaceDrawingTypeEnum::DRAW_AS_TRIANGLES; m_opacity = 1.0; m_sceneAssistant->add("m_displayNormalVectors", &m_displayNormalVectors); m_sceneAssistant->add("m_linkSize", &m_linkSize); m_sceneAssistant->add("m_nodeSize", &m_nodeSize); m_sceneAssistant->add("m_opacity", &m_opacity); m_sceneAssistant->add("m_surfaceDrawingType", &m_surfaceDrawingType); } /** * Destructor. */ DisplayPropertiesSurface::~DisplayPropertiesSurface() { } /** * Reset all settings to their defaults * and remove any data. */ void DisplayPropertiesSurface::reset() { } /** * Update due to changes in data. */ void DisplayPropertiesSurface::update() { } /** * Copy the display properties from one tab to another. * @param sourceTabIndex * Index of tab from which properties are copied. * @param targetTabIndex * Index of tab to which properties are copied. */ void DisplayPropertiesSurface::copyDisplayProperties(const int32_t /*sourceTabIndex*/, const int32_t /*targetTabIndex*/) { } /** * Create a scene for an instance of a class. * * @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. * * @return Pointer to SceneClass object representing the state of * this object. Under some circumstances a NULL pointer may be * returned. Caller will take ownership of returned object. */ SceneClass* DisplayPropertiesSurface::saveToScene(const SceneAttributes* sceneAttributes, const AString& instanceName) { SceneClass* sceneClass = new SceneClass(instanceName, "DisplayPropertiesSurface", 1); m_sceneAssistant->saveMembers(sceneAttributes, sceneClass); switch (sceneAttributes->getSceneType()) { case SceneTypeEnum::SCENE_TYPE_FULL: break; case SceneTypeEnum::SCENE_TYPE_GENERIC: break; } return sceneClass; } /** * Restore the state of an instance of a class. * * @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 containing the state that was previously * saved and should be restored. */ void DisplayPropertiesSurface::restoreFromScene(const SceneAttributes* sceneAttributes, const SceneClass* sceneClass) { if (sceneClass == NULL) { return; } m_sceneAssistant->restoreMembers(sceneAttributes, sceneClass); switch (sceneAttributes->getSceneType()) { case SceneTypeEnum::SCENE_TYPE_FULL: break; case SceneTypeEnum::SCENE_TYPE_GENERIC: break; } } /** * @return The surface drawing type */ SurfaceDrawingTypeEnum::Enum DisplayPropertiesSurface::getSurfaceDrawingType() const { return m_surfaceDrawingType; } /** * Set the surface drawing type. * * @param surfaceDrawingType * The surface drawing type */ void DisplayPropertiesSurface::setSurfaceDrawingType(const SurfaceDrawingTypeEnum::Enum surfaceDrawingType) { m_surfaceDrawingType = surfaceDrawingType; } /** * @return Node size. */ float DisplayPropertiesSurface::getNodeSize() const { return m_nodeSize; } /** * Set node size * * @param nodeSize * New node size. */ void DisplayPropertiesSurface::setNodeSize(const float nodeSize) { m_nodeSize = nodeSize; } /** * @return Link size. */ float DisplayPropertiesSurface::getLinkSize() const { return m_linkSize; } /** * Set link size. * * @param linkSize * New link size. */ void DisplayPropertiesSurface::setLinkSize(const float linkSize) { m_linkSize = linkSize; } /** * @return Display normal vectors. */ bool DisplayPropertiesSurface::isDisplayNormalVectors() const { return m_displayNormalVectors; } /** * Set display normal vectors. * * @param displayNormalVectors * New value for display normal vectors. */ void DisplayPropertiesSurface::setDisplayNormalVectors(const bool displayNormalVectors) { m_displayNormalVectors = displayNormalVectors; } /** * @return The overall surface opacity. */ float DisplayPropertiesSurface::getOpacity() const { return m_opacity; } /** * Set the overall surface opacity. * * @param opacity * New value for opacity. */ void DisplayPropertiesSurface::setOpacity(const float opacity) { m_opacity = opacity; } connectome-workbench-1.2.3+git41-gc4c6c90/src/Brain/DisplayPropertiesSurface.h000066400000000000000000000055151300200146000267510ustar00rootroot00000000000000#ifndef __DISPLAY_PROPERTIES_SURFACE_H_ #define __DISPLAY_PROPERTIES_SURFACE_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 "DisplayProperties.h" #include "SurfaceDrawingTypeEnum.h" namespace caret { class Surface; class DisplayPropertiesSurface : public DisplayProperties { public: DisplayPropertiesSurface(); virtual ~DisplayPropertiesSurface(); void reset(); void update(); virtual void copyDisplayProperties(const int32_t sourceTabIndex, const int32_t targetTabIndex); virtual SceneClass* saveToScene(const SceneAttributes* sceneAttributes, const AString& instanceName); virtual void restoreFromScene(const SceneAttributes* sceneAttributes, const SceneClass* sceneClass); SurfaceDrawingTypeEnum::Enum getSurfaceDrawingType() const; void setSurfaceDrawingType(const SurfaceDrawingTypeEnum::Enum surfaceDrawingType); float getNodeSize() const; void setNodeSize(const float nodeSize); float getLinkSize() const; void setLinkSize(const float linkSize); bool isDisplayNormalVectors() const; void setDisplayNormalVectors(const bool displayNormalVectors); float getOpacity() const; void setOpacity(const float opacity); private: DisplayPropertiesSurface(const DisplayPropertiesSurface&); DisplayPropertiesSurface& operator=(const DisplayPropertiesSurface&); float m_nodeSize; float m_linkSize; bool m_displayNormalVectors; SurfaceDrawingTypeEnum::Enum m_surfaceDrawingType; float m_opacity; }; #ifdef __DISPLAY_PROPERTIES_SURFACE_DECLARE__ #endif // __DISPLAY_PROPERTIES_SURFACE_DECLARE__ } // namespace #endif //__DISPLAY_PROPERTIES_SURFACE_H_ connectome-workbench-1.2.3+git41-gc4c6c90/src/Brain/DisplayPropertiesVolume.cxx000066400000000000000000000072031300200146000271770ustar00rootroot00000000000000 /*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 __DISPLAY_PROPERTIES_VOLUME_DECLARE__ #include "DisplayPropertiesVolume.h" #undef __DISPLAY_PROPERTIES_VOLUME_DECLARE__ #include "SceneAttributes.h" #include "SceneClass.h" using namespace caret; /** * \class DisplayPropertiesVolume * \brief Display properties for volume slices. * * Display properties for volume slices. */ /** * Constructor. */ DisplayPropertiesVolume::DisplayPropertiesVolume() : DisplayProperties() { } /** * Destructor. */ DisplayPropertiesVolume::~DisplayPropertiesVolume() { } /** * Reset all settings to their defaults * and remove any data. */ void DisplayPropertiesVolume::reset() { } /** * Update due to changes in data. */ void DisplayPropertiesVolume::update() { } /** * Copy the display properties from one tab to another. * @param sourceTabIndex * Index of tab from which properties are copied. * @param targetTabIndex * Index of tab to which properties are copied. */ void DisplayPropertiesVolume::copyDisplayProperties(const int32_t /*sourceTabIndex*/, const int32_t /*targetTabIndex*/) { } /** * Create a scene for an instance of a class. * * @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. * * @return Pointer to SceneClass object representing the state of * this object. Under some circumstances a NULL pointer may be * returned. Caller will take ownership of returned object. */ SceneClass* DisplayPropertiesVolume::saveToScene(const SceneAttributes* sceneAttributes, const AString& instanceName) { SceneClass* sceneClass = new SceneClass(instanceName, "DisplayPropertiesVolume", 1); switch (sceneAttributes->getSceneType()) { case SceneTypeEnum::SCENE_TYPE_FULL: break; case SceneTypeEnum::SCENE_TYPE_GENERIC: break; } return sceneClass; } /** * Restore the state of an instance of a class. * * @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 containing the state that was previously * saved and should be restored. */ void DisplayPropertiesVolume::restoreFromScene(const SceneAttributes* sceneAttributes, const SceneClass* sceneClass) { if (sceneClass == NULL) { return; } switch (sceneAttributes->getSceneType()) { case SceneTypeEnum::SCENE_TYPE_FULL: break; case SceneTypeEnum::SCENE_TYPE_GENERIC: break; } } connectome-workbench-1.2.3+git41-gc4c6c90/src/Brain/DisplayPropertiesVolume.h000066400000000000000000000037641300200146000266340ustar00rootroot00000000000000#ifndef __DISPLAY_PROPERTIES_VOLUME__H_ #define __DISPLAY_PROPERTIES_VOLUME__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 "DisplayProperties.h" namespace caret { class Surface; class DisplayPropertiesVolume : public DisplayProperties { public: DisplayPropertiesVolume(); virtual ~DisplayPropertiesVolume(); void reset(); void update(); virtual void copyDisplayProperties(const int32_t sourceTabIndex, const int32_t targetTabIndex); virtual SceneClass* saveToScene(const SceneAttributes* sceneAttributes, const AString& instanceName); virtual void restoreFromScene(const SceneAttributes* sceneAttributes, const SceneClass* sceneClass); private: DisplayPropertiesVolume(const DisplayPropertiesVolume&); DisplayPropertiesVolume& operator=(const DisplayPropertiesVolume&); }; #ifdef __DISPLAY_PROPERTIES_VOLUME_DECLARE__ #endif // __DISPLAY_PROPERTIES_VOLUME_DECLARE__ } // namespace #endif //__DISPLAY_PROPERTIES_VOLUME__H_ connectome-workbench-1.2.3+git41-gc4c6c90/src/Brain/DisplayPropertyDataBoolean.cxx000066400000000000000000000140461300200146000275740ustar00rootroot00000000000000 /*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 __DISPLAY_PROPERTY_DATA_BOOLEAN_DECLARE__ #include "DisplayPropertyDataBoolean.h" #undef __DISPLAY_PROPERTY_DATA_BOOLEAN_DECLARE__ #include "CaretAssert.h" #include "SceneClass.h" #include "SceneClassAssistant.h" using namespace caret; /** * \class caret::DisplayPropertyDataBoolean * \brief Boolean data for display property tab/display group * \ingroup Brain */ /** * Constructor that initializes with the given default value. * * @param defaultValue * Default data value. */ DisplayPropertyDataBoolean::DisplayPropertyDataBoolean(const bool defaultValue) : CaretObject() { setAllValues(defaultValue); m_sceneAssistant = new SceneClassAssistant(); m_sceneAssistant->addTabIndexedBooleanArray("m_tabValues", m_tabValues); m_sceneAssistant->addArray("m_displayGroupValues", m_displayGroupValues, DisplayGroupEnum::NUMBER_OF_GROUPS, defaultValue); } /** * Destructor. */ DisplayPropertyDataBoolean::~DisplayPropertyDataBoolean() { delete m_sceneAssistant; } /** * Copy the values from one tab to another. * * @param sourceTabIndex * Index of tab from which values are copied. * @param targetTabIndex * Index of tab to which values are copied. */ void DisplayPropertyDataBoolean::copyValues(const int32_t sourceTabIndex, const int32_t targetTabIndex) { CaretAssertArrayIndex(m_tabValues, BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS, sourceTabIndex); CaretAssertArrayIndex(m_tabValues, BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS, targetTabIndex); m_tabValues[targetTabIndex] = m_tabValues[sourceTabIndex]; } /** * @return The data value. * * @param displayGroup * Display group. * @param tabIndex * Tab index. */ bool DisplayPropertyDataBoolean::getValue(const DisplayGroupEnum::Enum displayGroup, const int32_t tabIndex) const { CaretAssertArrayIndex(m_displayGroupValues, DisplayGroupEnum::NUMBER_OF_GROUPS, static_cast(displayGroup)); if (displayGroup == DisplayGroupEnum::DISPLAY_GROUP_TAB) { CaretAssertArrayIndex(m_tabValues, BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS, tabIndex); return m_tabValues[tabIndex]; } return m_displayGroupValues[displayGroup]; } /** * Set all display groups and tabs to the given value. * * @param value * The value. */ void DisplayPropertyDataBoolean::setAllValues(const bool value) { for (int32_t i = 0; i < BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS; i++) { m_tabValues[i] = value; } for (int32_t i = 0; i < DisplayGroupEnum::NUMBER_OF_GROUPS; i++) { m_displayGroupValues[i] = value; } } /** * Set the data value. * * @param displayGroup * Display group. * @param tabIndex * Tab index. * @param value * New value. */ void DisplayPropertyDataBoolean::setValue(const DisplayGroupEnum::Enum displayGroup, const int32_t tabIndex, const bool value) { CaretAssertArrayIndex(m_displayGroupValues, DisplayGroupEnum::NUMBER_OF_GROUPS, static_cast(displayGroup)); if (displayGroup == DisplayGroupEnum::DISPLAY_GROUP_TAB) { CaretAssertArrayIndex(m_tabValues, BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS, tabIndex); m_tabValues[tabIndex] = value; } else { m_displayGroupValues[displayGroup] = value; } } /** * 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* DisplayPropertyDataBoolean::saveToScene(const SceneAttributes* sceneAttributes, const AString& instanceName) { SceneClass* sceneClass = new SceneClass(instanceName, "DisplayPropertyDataBoolean", 1); m_sceneAssistant->saveMembers(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 DisplayPropertyDataBoolean::restoreFromScene(const SceneAttributes* sceneAttributes, const SceneClass* sceneClass) { if (sceneClass == NULL) { return; } m_sceneAssistant->restoreMembers(sceneAttributes, sceneClass); } connectome-workbench-1.2.3+git41-gc4c6c90/src/Brain/DisplayPropertyDataBoolean.h000066400000000000000000000065021300200146000272170ustar00rootroot00000000000000#ifndef __DISPLAY_PROPERTY_DATA_BOOLEAN_H__ #define __DISPLAY_PROPERTY_DATA_BOOLEAN_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 "DisplayGroupEnum.h" #include "SceneableInterface.h" namespace caret { class SceneClassAssistant; class DisplayPropertyDataBoolean : public CaretObject, public SceneableInterface { public: DisplayPropertyDataBoolean(const bool defaultValue); virtual ~DisplayPropertyDataBoolean(); void setAllValues(const bool value); void copyValues(const int32_t sourceTabIndex, const int32_t targetTabIndex); bool getValue(const DisplayGroupEnum::Enum displayGroup, const int32_t tabIndex) const; void setValue(const DisplayGroupEnum::Enum displayGroup, const int32_t tabIndex, const bool value); // 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: DisplayPropertyDataBoolean(const DisplayPropertyDataBoolean& obj); DisplayPropertyDataBoolean& operator=(const DisplayPropertyDataBoolean& obj); SceneClassAssistant* m_sceneAssistant; bool m_displayGroupValues[DisplayGroupEnum::NUMBER_OF_GROUPS]; bool m_tabValues[BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS]; // ADD_NEW_MEMBERS_HERE }; #ifdef __DISPLAY_PROPERTY_DATA_BOOLEAN_DECLARE__ // #endif // __DISPLAY_PROPERTY_DATA_BOOLEAN_DECLARE__ } // namespace #endif //__DISPLAY_PROPERTY_DATA_BOOLEAN_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Brain/DisplayPropertyDataEnum.h000066400000000000000000000233061300200146000265450ustar00rootroot00000000000000#ifndef __DISPLAY_PROPERTY_DATA_ENUM_H__ #define __DISPLAY_PROPERTY_DATA_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 "BrainConstants.h" #include "CaretAssert.h" #include "CaretObject.h" #include "DisplayGroupEnum.h" #include "SceneAttributes.h" #include "SceneClass.h" #include "SceneClassAssistant.h" #include "SceneableInterface.h" /** * \class caret::DisplayPropertyDataEnum * \brief Enumerated data for display property tab/display group * \ingroup Brain */ namespace caret { class SceneClassAssistant; template class DisplayPropertyDataEnum : public CaretObject, public SceneableInterface { public: /** * Constructor. */ DisplayPropertyDataEnum() : CaretObject() { m_sceneAssistant.grabNew(NULL); } /** * Initializes with the given default value. * * MUST BE CALLED BEFORE ANY OTHER METHOD !!! * * @param defaultValue * Default data value. */ void initialize(const ET defaultValue) { m_sceneAssistant.grabNew(new SceneClassAssistant()); /* * NOTE: must be called after creating scene class assistant */ setAllValues(defaultValue); m_sceneAssistant->addTabIndexedEnumeratedTypeArray("m_tabValues", m_tabValues); m_sceneAssistant->addArray("m_displayGroupValues", m_displayGroupValues, DisplayGroupEnum::NUMBER_OF_GROUPS, defaultValue); } /** * Destructor. */ virtual ~DisplayPropertyDataEnum() { } /** * Set all display groups and tabs to the given value. * * @param value * The value. */ void setAllValues(const ET value) { CaretAssertMessage(m_sceneAssistant.getPointer(), "Failed to call initialize(defaultValue) for the enumerated type"); for (int32_t i = 0; i < BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS; i++) { m_tabValues[i] = value; } for (int32_t i = 0; i < DisplayGroupEnum::NUMBER_OF_GROUPS; i++) { m_displayGroupValues[i] = value; } } /** * Copy the values from one tab to another. * * @param sourceTabIndex * Index of tab from which values are copied. * @param targetTabIndex * Index of tab to which values are copied. */ void copyValues(const int32_t sourceTabIndex, const int32_t targetTabIndex) { CaretAssertMessage(m_sceneAssistant.getPointer(), "Failed to call initialize(defaultValue) for the enumerated type"); CaretAssertArrayIndex(m_tabValues, BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS, sourceTabIndex); CaretAssertArrayIndex(m_tabValues, BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS, targetTabIndex); m_tabValues[targetTabIndex] = m_tabValues[sourceTabIndex]; } /** * @return The data value. * * @param displayGroup * Display group. * @param tabIndex * Tab index. */ ET getValue(const DisplayGroupEnum::Enum displayGroup, const int32_t tabIndex) const { CaretAssertMessage(m_sceneAssistant.getPointer(), "Failed to call initialize(defaultValue) for the enumerated type"); CaretAssertArrayIndex(m_displayGroupValues, DisplayGroupEnum::NUMBER_OF_GROUPS, static_cast(displayGroup)); if (displayGroup == DisplayGroupEnum::DISPLAY_GROUP_TAB) { CaretAssertArrayIndex(m_tabValues, BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS, tabIndex); return m_tabValues[tabIndex]; } return m_displayGroupValues[displayGroup]; } /** * Set the data value. * * @param displayGroup * Display group. * @param tabIndex * Tab index. * @param value * New value. */ void setValue(const DisplayGroupEnum::Enum displayGroup, const int32_t tabIndex, const ET value) { CaretAssertMessage(m_sceneAssistant.getPointer(), "Failed to call initialize(defaultValue) for the enumerated type"); CaretAssertArrayIndex(m_displayGroupValues, DisplayGroupEnum::NUMBER_OF_GROUPS, static_cast(displayGroup)); if (displayGroup == DisplayGroupEnum::DISPLAY_GROUP_TAB) { CaretAssertArrayIndex(m_tabValues, BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS, tabIndex); m_tabValues[tabIndex] = value; } else { m_displayGroupValues[displayGroup] = value; } } /** * 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. */ virtual SceneClass* saveToScene(const SceneAttributes* sceneAttributes, const AString& instanceName) { CaretAssertMessage(m_sceneAssistant.getPointer(), "Failed to call initialize(defaultValue) for the enumerated type"); SceneClass* sceneClass = new SceneClass(instanceName, "DisplayPropertyDataEnum", 1); m_sceneAssistant->saveMembers(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. */ virtual void restoreFromScene(const SceneAttributes* sceneAttributes, const SceneClass* sceneClass) { CaretAssertMessage(m_sceneAssistant.getPointer(), "Failed to call initialize(defaultValue) for the enumerated type"); if (sceneClass == NULL) { return; } m_sceneAssistant->restoreMembers(sceneAttributes, 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: DisplayPropertyDataEnum(const DisplayPropertyDataEnum& obj); DisplayPropertyDataEnum& operator=(const DisplayPropertyDataEnum& obj); CaretPointer m_sceneAssistant; ET m_displayGroupValues[DisplayGroupEnum::NUMBER_OF_GROUPS]; ET m_tabValues[BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS]; // ADD_NEW_MEMBERS_HERE }; #ifdef __DISPLAY_PROPERTY_DATA_ENUM_DECLARE__ // #endif // __DISPLAY_PROPERTY_DATA_ENUM_DECLARE__ } // namespace #endif //__DISPLAY_PROPERTY_DATA_ENUM_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Brain/DisplayPropertyDataFloat.cxx000066400000000000000000000140101300200146000272510ustar00rootroot00000000000000 /*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 __DISPLAY_PROPERTY_DATA_FLOAT_DECLARE__ #include "DisplayPropertyDataFloat.h" #undef __DISPLAY_PROPERTY_DATA_FLOAT_DECLARE__ #include "CaretAssert.h" #include "SceneClass.h" #include "SceneClassAssistant.h" using namespace caret; /** * \class caret::DisplayPropertyDataFloat * \brief Float data for display property tab/display group * \ingroup Brain */ /** * Constructor that initializes with the given default value. * * @param defaultValue * Default data value. */ DisplayPropertyDataFloat::DisplayPropertyDataFloat(const float defaultValue) : CaretObject() { setAllValues(defaultValue); m_sceneAssistant = new SceneClassAssistant(); m_sceneAssistant->addTabIndexedFloatArray("m_tabValues", m_tabValues); m_sceneAssistant->addArray("m_displayGroupValues", m_displayGroupValues, DisplayGroupEnum::NUMBER_OF_GROUPS, defaultValue); } /** * Destructor. */ DisplayPropertyDataFloat::~DisplayPropertyDataFloat() { delete m_sceneAssistant; } /** * Copy the values from one tab to another. * * @param sourceTabIndex * Index of tab from which values are copied. * @param targetTabIndex * Index of tab to which values are copied. */ void DisplayPropertyDataFloat::copyValues(const int32_t sourceTabIndex, const int32_t targetTabIndex) { CaretAssertArrayIndex(m_tabValues, BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS, sourceTabIndex); CaretAssertArrayIndex(m_tabValues, BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS, targetTabIndex); m_tabValues[targetTabIndex] = m_tabValues[sourceTabIndex]; } /** * @return The data value. * * @param displayGroup * Display group. * @param tabIndex * Tab index. */ float DisplayPropertyDataFloat::getValue(const DisplayGroupEnum::Enum displayGroup, const int32_t tabIndex) const { CaretAssertArrayIndex(m_displayGroupValues, DisplayGroupEnum::NUMBER_OF_GROUPS, static_cast(displayGroup)); if (displayGroup == DisplayGroupEnum::DISPLAY_GROUP_TAB) { CaretAssertArrayIndex(m_tabValues, BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS, tabIndex); return m_tabValues[tabIndex]; } return m_displayGroupValues[displayGroup]; } /** * Set all display groups and tabs to the given value. * * @param value * The value. */ void DisplayPropertyDataFloat::setAllValues(const float value) { for (int32_t i = 0; i < BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS; i++) { m_tabValues[i] = value; } for (int32_t i = 0; i < DisplayGroupEnum::NUMBER_OF_GROUPS; i++) { m_displayGroupValues[i] = value; } } /** * Set the data value. * * @param displayGroup * Display group. * @param tabIndex * Tab index. * @param value * New value. */ void DisplayPropertyDataFloat::setValue(const DisplayGroupEnum::Enum displayGroup, const int32_t tabIndex, const float value) { CaretAssertArrayIndex(m_displayGroupValues, DisplayGroupEnum::NUMBER_OF_GROUPS, static_cast(displayGroup)); if (displayGroup == DisplayGroupEnum::DISPLAY_GROUP_TAB) { CaretAssertArrayIndex(m_tabValues, BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS, tabIndex); m_tabValues[tabIndex] = value; } else { m_displayGroupValues[displayGroup] = value; } } /** * 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* DisplayPropertyDataFloat::saveToScene(const SceneAttributes* sceneAttributes, const AString& instanceName) { SceneClass* sceneClass = new SceneClass(instanceName, "DisplayPropertyDataFloat", 1); m_sceneAssistant->saveMembers(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 DisplayPropertyDataFloat::restoreFromScene(const SceneAttributes* sceneAttributes, const SceneClass* sceneClass) { if (sceneClass == NULL) { return; } m_sceneAssistant->restoreMembers(sceneAttributes, sceneClass); } connectome-workbench-1.2.3+git41-gc4c6c90/src/Brain/DisplayPropertyDataFloat.h000066400000000000000000000064601300200146000267100ustar00rootroot00000000000000#ifndef __DISPLAY_PROPERTY_DATA_FLOAT_H__ #define __DISPLAY_PROPERTY_DATA_FLOAT_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 "DisplayGroupEnum.h" #include "SceneableInterface.h" namespace caret { class SceneClassAssistant; class DisplayPropertyDataFloat : public CaretObject, public SceneableInterface { public: DisplayPropertyDataFloat(const float defaultValue); virtual ~DisplayPropertyDataFloat(); void setAllValues(const float value); void copyValues(const int32_t sourceTabIndex, const int32_t targetTabIndex); float getValue(const DisplayGroupEnum::Enum displayGroup, const int32_t tabIndex) const; void setValue(const DisplayGroupEnum::Enum displayGroup, const int32_t tabIndex, const float value); // 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: DisplayPropertyDataFloat(const DisplayPropertyDataFloat& obj); DisplayPropertyDataFloat& operator=(const DisplayPropertyDataFloat& obj); SceneClassAssistant* m_sceneAssistant; float m_displayGroupValues[DisplayGroupEnum::NUMBER_OF_GROUPS]; float m_tabValues[BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS]; // ADD_NEW_MEMBERS_HERE }; #ifdef __DISPLAY_PROPERTY_DATA_FLOAT_DECLARE__ // #endif // __DISPLAY_PROPERTY_DATA_FLOAT_DECLARE__ } // namespace #endif //__DISPLAY_PROPERTY_DATA_FLOAT_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Brain/DummyFontTextRenderer.cxx000066400000000000000000000141101300200146000265760ustar00rootroot00000000000000 /*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 __DUMMY_FONT_TEXT_RENDERER_DECLARE__ #include "DummyFontTextRenderer.h" #undef __DUMMY_FONT_TEXT_RENDERER_DECLARE__ #include "CaretAssert.h" using namespace caret; /** * \class caret::DummyFontTextRenderer * \brief A dummy font text renderer used when no valid text renderer is available. * \ingroup Brain * * This "dummy" text renderer is used when a valid text renderer (Qt, FTGL) is * not available. Without this dummy text renderer, any graphics code that * draws text would need to check that the text renderer is valid. This * dummy text renderer does nothing other than prevent the software from * crashing. */ /** * Constructor. */ DummyFontTextRenderer::DummyFontTextRenderer() : BrainOpenGLTextRenderInterface() { } /** * Destructor. */ DummyFontTextRenderer::~DummyFontTextRenderer() { } /** * @return The font system is valid. */ bool DummyFontTextRenderer::isValid() const { return true; } /** * Draw annnotation text at the given viewport coordinates using * the the annotations attributes for the style of text. * * Depth testing is DISABLED when drawing text with this method. * * @param viewportX * Viewport X-coordinate. * @param viewportY * Viewport Y-coordinate. * @param annotationText * Annotation text and attributes. */ void DummyFontTextRenderer::drawTextAtViewportCoords(const double /*viewportX*/, const double /*viewportY*/, const AnnotationText& /*annotationText*/) { } /** * Draw annnotation text at the given viewport coordinates using * the the annotations attributes for the style of text. * * Depth testing is ENABLED when drawing text with this method. * * @param viewportX * Viewport X-coordinate. * @param viewportY * Viewport Y-coordinate. * @param viewportZ * Viewport Z-coordinate. * @param annotationText * Annotation text and attributes. */ void DummyFontTextRenderer::drawTextAtViewportCoords(const double /*viewportX*/, const double /*viewportY*/, const double /*viewportZ*/, const AnnotationText& /*annotationText*/) { } /** * Draw annnotation text at the given model coordinates using * the the annotations attributes for the style of text. * * Depth testing is ENABLED when drawing text with this method. * * @param modelX * Model X-coordinate. * @param modelY * Model Y-coordinate. * @param modelZ * Model Z-coordinate. * @param annotationText * Annotation text and attributes. */ void DummyFontTextRenderer::drawTextAtModelCoords(const double /*modelX*/, const double /*modelY*/, const double /*modelZ*/, const AnnotationText& /*annotationText*/) { } /** * Get the bounds of text (in pixels) using the given text * attributes. * * See http://ftgl.sourceforge.net/docs/html/metrics.png * * @param annotationText * Text that is to be drawn. * @param viewportX * Viewport X-coordinate. * @param viewportY * Viewport Y-coordinate. * @param viewportZ * Viewport Z-coordinate. * @param viewportHeight * Height of the viewport needed for percentage height text. * @param bottomLeftOut * The bottom left corner of the text bounds. * @param bottomRightOut * The bottom right corner of the text bounds. * @param topRightOut * The top right corner of the text bounds. * @param topLeftOut * The top left corner of the text bounds. */ void DummyFontTextRenderer::getBoundsForTextAtViewportCoords(const AnnotationText& /*annotationText*/, const double /*viewportX*/, const double /*viewportY*/, const double /*viewportZ*/, const double /*viewportHeight*/, double* /*bottomLeftOut[3]*/, double* /*bottomRightOut[3]*/, double* /*topRightOut[3]*/, double* /*topLeftOut[3]*/) { } /** * Get the estimated width and height of text (in pixels) using the given text * attributes. * * See http://ftgl.sourceforge.net/docs/html/metrics.png * * @param annotationText * Text for width and height estimation. * @param viewportHeight * Height of the viewport needed for percentage height text. * @param widthOut * Estimated width of text. * @param heightOut * Estimated height of text. */ void DummyFontTextRenderer::getTextWidthHeightInPixels(const AnnotationText& /*annotationText*/, const double /*viewportHeight*/, double& /*widthOut*/, double& /*heightOut*/) { } /** * @return Name of the text renderer. */ AString DummyFontTextRenderer::getName() const { return "Dummy (No OpenGL font system)"; } connectome-workbench-1.2.3+git41-gc4c6c90/src/Brain/DummyFontTextRenderer.h000066400000000000000000000067161300200146000262400ustar00rootroot00000000000000#ifndef __DUMMY_FONT_TEXT_RENDERER_H__ #define __DUMMY_FONT_TEXT_RENDERER_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 "BrainOpenGLTextRenderInterface.h" namespace caret { class DummyFontTextRenderer : public BrainOpenGLTextRenderInterface { public: DummyFontTextRenderer(); virtual ~DummyFontTextRenderer(); bool isValid() const; virtual void drawTextAtViewportCoords(const double viewportX, const double viewportY, const double viewportZ, const AnnotationText& annotationText); virtual void drawTextAtViewportCoords(const double viewportX, const double viewportY, const AnnotationText& annotationText); virtual void drawTextAtModelCoords(const double modelX, const double modelY, const double modelZ, const AnnotationText& annotationText); virtual void getTextWidthHeightInPixels(const AnnotationText& annotationText, const double viewportHeight, double& widthOut, double& heightOut); virtual void getBoundsForTextAtViewportCoords(const AnnotationText& annotationText, const double viewportX, const double viewportY, const double viewportZ, const double viewportHeight, double bottomLeftOut[3], double bottomRightOut[3], double topRightOut[3], double topLeftOut[3]); virtual AString getName() const; // ADD_NEW_METHODS_HERE private: DummyFontTextRenderer(const DummyFontTextRenderer&); DummyFontTextRenderer& operator=(const DummyFontTextRenderer&); // ADD_NEW_MEMBERS_HERE }; #ifdef __DUMMY_FONT_TEXT_RENDERER_DECLARE__ // #endif // __DUMMY_FONT_TEXT_RENDERER_DECLARE__ } // namespace #endif //__DUMMY_FONT_TEXT_RENDERER_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Brain/EventAnnotationColorBarGet.cxx000066400000000000000000000065161300200146000275330ustar00rootroot00000000000000 /*LICENSE_START*/ /* * Copyright (C) 2015 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_COLOR_BAR_GET_DECLARE__ #include "EventAnnotationColorBarGet.h" #undef __EVENT_ANNOTATION_COLOR_BAR_GET_DECLARE__ #include "CaretAssert.h" #include "EventTypeEnum.h" using namespace caret; /** * \class caret::EventAnnotationColorBarGet * \brief Event to get annontation color bars for tab(s). * \ingroup Brain */ /** * Constructor for getting annotations for ALL tabs. */ EventAnnotationColorBarGet::EventAnnotationColorBarGet() : Event(EventTypeEnum::EVENT_ANNOTATION_COLOR_BAR_GET), m_allTabsFlag(true) { } /** * Constructor for getting annotations for the given tab index. * * @param tabIndex * Index of tab for which color bars are requested. */ EventAnnotationColorBarGet::EventAnnotationColorBarGet(const int32_t tabIndex) : Event(EventTypeEnum::EVENT_ANNOTATION_COLOR_BAR_GET), m_allTabsFlag(false) { m_tabIndices.insert(tabIndex); } /** * Constructor for getting annotations for the given tab indices. * * @param tabIndices * Indices of tabs for which color bars are requested. If * the indices are empty no colorbars will be gotten. */ EventAnnotationColorBarGet::EventAnnotationColorBarGet(const std::vector& tabIndices) : Event(EventTypeEnum::EVENT_ANNOTATION_COLOR_BAR_GET), m_allTabsFlag(false) { m_tabIndices.insert(tabIndices.begin(), tabIndices.end()); } /** * Destructor. */ EventAnnotationColorBarGet::~EventAnnotationColorBarGet() { } /** * Add annotation color bars. * * @param annotationColorBars * Annotation color bars that are added. */ void EventAnnotationColorBarGet::addAnnotationColorBars(const std::vector& annotationColorBars) { m_annotationColorBars.insert(m_annotationColorBars.end(), annotationColorBars.begin(), annotationColorBars.end()); } /** * Are annotation color bars requested for the given tab index. * * @param tabIndex * Index of tab. * @return * True if annotations are requested for the given tab index, else false. */ bool EventAnnotationColorBarGet::isGetAnnotationColorBarsForTabIndex(const int32_t tabIndex) { if (m_allTabsFlag) { return true; } else if (m_tabIndices.find(tabIndex) != m_tabIndices.end()) { return true; } return false; } /** * @return The annotation color bars after event completes. */ std::vector EventAnnotationColorBarGet::getAnnotationColorBars() const { return m_annotationColorBars; } connectome-workbench-1.2.3+git41-gc4c6c90/src/Brain/EventAnnotationColorBarGet.h000066400000000000000000000042731300200146000271560ustar00rootroot00000000000000#ifndef __EVENT_ANNOTATION_COLOR_BAR_GET_H__ #define __EVENT_ANNOTATION_COLOR_BAR_GET_H__ /*LICENSE_START*/ /* * Copyright (C) 2015 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 AnnotationColorBar; class EventAnnotationColorBarGet : public Event { public: EventAnnotationColorBarGet(); EventAnnotationColorBarGet(const int32_t tabIndex); EventAnnotationColorBarGet(const std::vector& tabIndices); virtual ~EventAnnotationColorBarGet(); void addAnnotationColorBars(const std::vector& colorBars); bool isGetAnnotationColorBarsForTabIndex(const int32_t tabIndex); std::vector getAnnotationColorBars() const; // ADD_NEW_METHODS_HERE private: EventAnnotationColorBarGet(const EventAnnotationColorBarGet&); EventAnnotationColorBarGet& operator=(const EventAnnotationColorBarGet&); bool m_allTabsFlag; std::set m_tabIndices; std::vector m_annotationColorBars; // ADD_NEW_MEMBERS_HERE }; #ifdef __EVENT_ANNOTATION_COLOR_BAR_GET_DECLARE__ // #endif // __EVENT_ANNOTATION_COLOR_BAR_GET_DECLARE__ } // namespace #endif //__EVENT_ANNOTATION_COLOR_BAR_GET_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Brain/EventBrainReset.cxx000066400000000000000000000026741300200146000253740ustar00rootroot00000000000000 /*LICENSE_START*/ /* * Copyright (C) 2015 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_BRAIN_RESET_DECLARE__ #include "EventBrainReset.h" #undef __EVENT_BRAIN_RESET_DECLARE__ #include "CaretAssert.h" #include "EventTypeEnum.h" using namespace caret; /** * \class caret::EventBrainReset * \brief Event issued when brain is reset (new spec or scene loaded). * \ingroup Brain */ /** * Constructor. */ EventBrainReset::EventBrainReset(Brain* brain) : Event(EventTypeEnum::EVENT_BRAIN_RESET), m_brain(brain) { } /** * Destructor. */ EventBrainReset::~EventBrainReset() { } /** * @return Brain that was reset. */ Brain* EventBrainReset::getBrain() const { return m_brain; } connectome-workbench-1.2.3+git41-gc4c6c90/src/Brain/EventBrainReset.h000066400000000000000000000030421300200146000250070ustar00rootroot00000000000000#ifndef __EVENT_BRAIN_RESET_H__ #define __EVENT_BRAIN_RESET_H__ /*LICENSE_START*/ /* * Copyright (C) 2015 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 Brain; class EventBrainReset : public Event { public: EventBrainReset(Brain* brain); virtual ~EventBrainReset(); Brain* getBrain() const; // ADD_NEW_METHODS_HERE private: EventBrainReset(const EventBrainReset&); EventBrainReset& operator=(const EventBrainReset&); Brain* m_brain; // ADD_NEW_MEMBERS_HERE }; #ifdef __EVENT_BRAIN_RESET_DECLARE__ // #endif // __EVENT_BRAIN_RESET_DECLARE__ } // namespace #endif //__EVENT_BRAIN_RESET_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Brain/EventBrainStructureGetAll.cxx000066400000000000000000000053551300200146000274020ustar00rootroot00000000000000 /*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_BRAIN_STRUCTURE_GET_ALL_DECLARE__ #include "EventBrainStructureGetAll.h" #undef __EVENT_BRAIN_STRUCTURE_GET_ALL_DECLARE__ #include "BrainStructure.h" #include "CaretAssert.h" #include "EventTypeEnum.h" using namespace caret; /** * \class caret::EventBrainStructureGetAll * \brief Get all brain structures. * \ingroup Brain */ /** * Constructor. */ EventBrainStructureGetAll::EventBrainStructureGetAll() : Event(EventTypeEnum::EVENT_BRAIN_STRUCTURE_GET_ALL) { } /** * Destructor. */ EventBrainStructureGetAll::~EventBrainStructureGetAll() { } /** * Add a brain structure. * * @param brainStructure * Brain structure that is added. */ void EventBrainStructureGetAll::addBrainStructure(BrainStructure* brainStructure) { m_brainStructures.push_back(brainStructure); } /** * @return Number of brain structures that were found. */ int32_t EventBrainStructureGetAll::getNumberOfBrainStructures() const { return m_brainStructures.size(); } /** * Get the brain structure at the given index. * * @param indx * Index of the brain structure. * @return * Brain structure at the given index. */ BrainStructure* EventBrainStructureGetAll::getBrainStructureByIndex(const int32_t indx) { CaretAssertVectorIndex(m_brainStructures, indx); return m_brainStructures[indx]; } /** * Get the brain structure of the specified type. * * @param structure * Type of structure. * @return * Brain structure with the given structure type or NULL if not found. */ BrainStructure* EventBrainStructureGetAll::getBrainStructureByStructure(const StructureEnum::Enum structure) { for (std::vector::iterator iter = m_brainStructures.begin(); iter != m_brainStructures.end(); iter++) { BrainStructure* bs = *iter; if (bs->getStructure() == structure) { return bs; } } return NULL; } connectome-workbench-1.2.3+git41-gc4c6c90/src/Brain/EventBrainStructureGetAll.h000066400000000000000000000040241300200146000270170ustar00rootroot00000000000000#ifndef __EVENT_BRAIN_STRUCTURE_GET_ALL_H__ #define __EVENT_BRAIN_STRUCTURE_GET_ALL_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" #include "StructureEnum.h" namespace caret { class BrainStructure; class EventBrainStructureGetAll : public Event { public: EventBrainStructureGetAll(); virtual ~EventBrainStructureGetAll(); void addBrainStructure(BrainStructure* brainStructure); int32_t getNumberOfBrainStructures() const; BrainStructure* getBrainStructureByIndex(const int32_t indx); BrainStructure* getBrainStructureByStructure(const StructureEnum::Enum structure); private: EventBrainStructureGetAll(const EventBrainStructureGetAll&); EventBrainStructureGetAll& operator=(const EventBrainStructureGetAll&); public: // ADD_NEW_METHODS_HERE private: std::vector m_brainStructures; // ADD_NEW_MEMBERS_HERE }; #ifdef __EVENT_BRAIN_STRUCTURE_GET_ALL_DECLARE__ // #endif // __EVENT_BRAIN_STRUCTURE_GET_ALL_DECLARE__ } // namespace #endif //__EVENT_BRAIN_STRUCTURE_GET_ALL_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Brain/EventBrowserTabDelete.cxx000066400000000000000000000033741300200146000265310ustar00rootroot00000000000000/*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 "CaretAssert.h" #include "BrowserTabContent.h" #include "EventBrowserTabDelete.h" using namespace caret; /** * Constructor. */ EventBrowserTabDelete::EventBrowserTabDelete(BrowserTabContent* browserTab) : Event(EventTypeEnum::EVENT_BROWSER_TAB_DELETE) { CaretAssert(browserTab); m_browserTab = browserTab; m_browserTabIndex = browserTab->getTabNumber(); } /** * Destructor. */ EventBrowserTabDelete::~EventBrowserTabDelete() { } /** * Get the browser tab that is to be deleted. * Note that this may point to a browser tab that * has been deleted and using the pointer in this * case could be a disaster. * * @return * Pointer to browser tab that is to be deleted. */ BrowserTabContent* EventBrowserTabDelete::getBrowserTab() { return m_browserTab; } /** * @return Index of browser tab being deleted. */ int32_t EventBrowserTabDelete::getBrowserTabIndex() const { return m_browserTabIndex; } connectome-workbench-1.2.3+git41-gc4c6c90/src/Brain/EventBrowserTabDelete.h000066400000000000000000000031761300200146000261560ustar00rootroot00000000000000#ifndef __EVENT_BROWSER_TAB_DELETE_H__ #define __EVENT_BROWSER_TAB_DELETE_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 "Event.h" namespace caret { class BrowserTabContent; /// Event for deleting a browser tab class EventBrowserTabDelete : public Event { public: EventBrowserTabDelete(BrowserTabContent* browserTab); virtual ~EventBrowserTabDelete(); BrowserTabContent* getBrowserTab(); int32_t getBrowserTabIndex() const; private: EventBrowserTabDelete(const EventBrowserTabDelete&); EventBrowserTabDelete& operator=(const EventBrowserTabDelete&); BrowserTabContent* m_browserTab; int32_t m_browserTabIndex; }; } // namespace #endif // __EVENT_BROWSER_TAB_DELETE_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Brain/EventBrowserTabGet.cxx000066400000000000000000000032721300200146000260430ustar00rootroot00000000000000/*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 "EventBrowserTabGet.h" using namespace caret; /** * Constructor. */ EventBrowserTabGet::EventBrowserTabGet(const int32_t tabNumber) : Event(EventTypeEnum::EVENT_BROWSER_TAB_GET) { this->tabNumber = tabNumber; this->browserTab = NULL; } /** * Destructor. */ EventBrowserTabGet::~EventBrowserTabGet() { } /** * Get the browser tab that is to be deleted. * * @return * Pointer to browser tab that is to be deleted. */ BrowserTabContent* EventBrowserTabGet::getBrowserTab() { return this->browserTab; } /** * Set the browser tab for the requested tab number. * @param browserTab The tab. */ void EventBrowserTabGet::setBrowserTab(BrowserTabContent* browserTab) { this->browserTab = browserTab; } /** * @return Returns the requested tab number. */ int32_t EventBrowserTabGet::getTabNumber() const { return this->tabNumber; } connectome-workbench-1.2.3+git41-gc4c6c90/src/Brain/EventBrowserTabGet.h000066400000000000000000000032211300200146000254620ustar00rootroot00000000000000#ifndef __EVENT_BROWSER_TAB_GET_H__ #define __EVENT_BROWSER_TAB_GET_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 "Event.h" namespace caret { class BrowserTabContent; /// Get a browser tab by its tab number class EventBrowserTabGet : public Event { public: EventBrowserTabGet(const int32_t tabNumber); virtual ~EventBrowserTabGet(); BrowserTabContent* getBrowserTab(); void setBrowserTab(BrowserTabContent* browserTab); int32_t getTabNumber() const; private: EventBrowserTabGet(const EventBrowserTabGet&); EventBrowserTabGet& operator=(const EventBrowserTabGet&); BrowserTabContent* browserTab; int32_t tabNumber; }; } // namespace #endif // __EVENT_BROWSER_TAB_GET_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Brain/EventBrowserTabGetAll.cxx000066400000000000000000000046101300200146000264710ustar00rootroot00000000000000/*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 "BrowserTabContent.h" #include "CaretAssert.h" #include "EventBrowserTabGetAll.h" using namespace caret; /** * Constructor. */ EventBrowserTabGetAll::EventBrowserTabGetAll() : Event(EventTypeEnum::EVENT_BROWSER_TAB_GET_ALL) { } /** * Destructor. */ EventBrowserTabGetAll::~EventBrowserTabGetAll() { } /** * @return The number of browser tabs. */ int32_t EventBrowserTabGetAll::getNumberOfBrowserTabs() const { return this->allBrowserTabs.size(); } /** * Get the browser tab at the given index. * @param indx * Index of the browser tab. * @return Browser tab at the given index. */ BrowserTabContent* EventBrowserTabGetAll::getBrowserTab(const int32_t indx) { CaretAssertVectorIndex(this->allBrowserTabs, indx); return this->allBrowserTabs[indx]; } /** * Add a browser tab. * @param browserTab * Tab that is added. */ void EventBrowserTabGetAll::addBrowserTab(BrowserTabContent* browserTab) { this->allBrowserTabs.push_back(browserTab); } /** * @return All browser tabs. */ std::vector EventBrowserTabGetAll::getAllBrowserTabs() const { return this->allBrowserTabs; } /** * @return The indices of all browser tabs. */ std::vector EventBrowserTabGetAll::getBrowserTabIndices() const { std::vector tabIndices; for (std::vector::const_iterator iter = allBrowserTabs.begin(); iter != allBrowserTabs.end(); iter++) { const BrowserTabContent* btc = *iter; tabIndices.push_back(btc->getTabNumber()); } return tabIndices; } connectome-workbench-1.2.3+git41-gc4c6c90/src/Brain/EventBrowserTabGetAll.h000066400000000000000000000034351300200146000261220ustar00rootroot00000000000000#ifndef __EVENT_BROWSER_TAB_GET_ALL_H__ #define __EVENT_BROWSER_TAB_GET_ALL_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 "Event.h" namespace caret { class BrowserTabContent; /// Get all browser tabs class EventBrowserTabGetAll : public Event { public: EventBrowserTabGetAll(); virtual ~EventBrowserTabGetAll(); int32_t getNumberOfBrowserTabs() const; BrowserTabContent* getBrowserTab(const int32_t indx); std::vector getAllBrowserTabs() const; void addBrowserTab(BrowserTabContent* browserTab); std::vector getBrowserTabIndices() const; private: EventBrowserTabGetAll(const EventBrowserTabGetAll&); EventBrowserTabGetAll& operator=(const EventBrowserTabGetAll&); std::vector allBrowserTabs; }; } // namespace #endif // __EVENT_BROWSER_TAB_GET_ALL_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Brain/EventBrowserTabGetAllViewed.cxx000066400000000000000000000064421300200146000276420ustar00rootroot00000000000000 /*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_BROWSER_TAB_GET_ALL_VIEWED_DECLARE__ #include "EventBrowserTabGetAllViewed.h" #undef __EVENT_BROWSER_TAB_GET_ALL_VIEWED_DECLARE__ #include "BrowserTabContent.h" #include "CaretAssert.h" #include "EventTypeEnum.h" using namespace caret; /** * \class caret::EventBrowserTabGetAllViewed * \brief Event that gets all viewed browser tabs * \ingroup GuiQt * * For each window, it will add its selected browser tab. If * the window is in 'Tile Tabs' mode, all of its browser tabs * are added. */ /** * Constructor. */ EventBrowserTabGetAllViewed::EventBrowserTabGetAllViewed() : Event(EventTypeEnum::EVENT_BROWSER_TAB_GET_ALL_VIEWED) { } /** * Destructor. */ EventBrowserTabGetAllViewed::~EventBrowserTabGetAllViewed() { } /** * Add a viewed browser tab. * * @param browserTabContent * Browser tab that is added. */ void EventBrowserTabGetAllViewed::addViewedBrowserTab(BrowserTabContent* browserTabContent) { m_viewedBrowserTabs.push_back(browserTabContent); } /** * @return A vector containing the viewed browser tabs */ std::vector EventBrowserTabGetAllViewed::getViewedBrowserTabs() const { return m_viewedBrowserTabs; } /** * @return A vector containing the indices of the viewed browser tabs. */ std::vector EventBrowserTabGetAllViewed::getViewdedBrowserTabIndices() const { std::vector tabIndices; for (std::vector::const_iterator iter = m_viewedBrowserTabs.begin(); iter != m_viewedBrowserTabs.end(); iter++) { const BrowserTabContent* btc = *iter; tabIndices.push_back(btc->getTabNumber()); } return tabIndices; } /** * @return A vector containing surface structures in the viewed browser tabs. */ std::vector EventBrowserTabGetAllViewed::getViewedSurfaceStructures() const { std::set structureSet; for (std::vector::const_iterator iter = m_viewedBrowserTabs.begin(); iter != m_viewedBrowserTabs.end(); iter++) { BrowserTabContent* btc = *iter; std::vector tabStructures = btc->getSurfaceStructuresDisplayed(); structureSet.insert(tabStructures.begin(), tabStructures.end()); } std::vector structuresOut(structureSet.begin(), structureSet.end()); return structuresOut; } connectome-workbench-1.2.3+git41-gc4c6c90/src/Brain/EventBrowserTabGetAllViewed.h000066400000000000000000000040671300200146000272700ustar00rootroot00000000000000#ifndef __EVENT_BROWSER_TAB_GET_ALL_VIEWED_H__ #define __EVENT_BROWSER_TAB_GET_ALL_VIEWED_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" #include "StructureEnum.h" namespace caret { class BrowserTabContent; class EventBrowserTabGetAllViewed : public Event { public: EventBrowserTabGetAllViewed(); virtual ~EventBrowserTabGetAllViewed(); void addViewedBrowserTab(BrowserTabContent* browserTabContent); std::vector getViewedBrowserTabs() const; std::vector getViewdedBrowserTabIndices() const; std::vector getViewedSurfaceStructures() const; private: EventBrowserTabGetAllViewed(const EventBrowserTabGetAllViewed&); EventBrowserTabGetAllViewed& operator=(const EventBrowserTabGetAllViewed&); public: // ADD_NEW_METHODS_HERE private: // ADD_NEW_MEMBERS_HERE std::vector m_viewedBrowserTabs; }; #ifdef __EVENT_BROWSER_TAB_GET_ALL_VIEWED_DECLARE__ // #endif // __EVENT_BROWSER_TAB_GET_ALL_VIEWED_DECLARE__ } // namespace #endif //__EVENT_BROWSER_TAB_GET_ALL_VIEWED_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Brain/EventBrowserTabNew.cxx000066400000000000000000000031011300200146000260440ustar00rootroot00000000000000/*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 "EventBrowserTabNew.h" using namespace caret; /** * Constructor. */ EventBrowserTabNew::EventBrowserTabNew() : Event(EventTypeEnum::EVENT_BROWSER_TAB_NEW) { this->browserTab = NULL; } /** * Destructor. */ EventBrowserTabNew::~EventBrowserTabNew() { } /** * Get the browser tab that was created. * * @return * Pointer to browser tab that was created or * NULL if a browser tab could not be created. */ BrowserTabContent* EventBrowserTabNew::getBrowserTab() { return this->browserTab; } /** * Set the created browser tab. * * @param browserTab * Browser tab that was created. */ void EventBrowserTabNew::setBrowserTab(BrowserTabContent* browserTab) { this->browserTab = browserTab; } connectome-workbench-1.2.3+git41-gc4c6c90/src/Brain/EventBrowserTabNew.h000066400000000000000000000030501300200146000254740ustar00rootroot00000000000000#ifndef __EVENT_BROWSER_TAB_NEW_H__ #define __EVENT_BROWSER_TAB_NEW_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 "Event.h" namespace caret { class BrowserTabContent; /// Event for creating a new browser tab class EventBrowserTabNew : public Event { public: EventBrowserTabNew(); virtual ~EventBrowserTabNew(); BrowserTabContent* getBrowserTab(); void setBrowserTab(BrowserTabContent* browserTab); private: EventBrowserTabNew(const EventBrowserTabNew&); EventBrowserTabNew& operator=(const EventBrowserTabNew&); BrowserTabContent* browserTab; }; } // namespace #endif // __EVENT_BROWSER_TAB_NEW_H__ EventCaretMappableDataFileMapsViewedInOverlays.cxx000066400000000000000000000046001300200146000333410ustar00rootroot00000000000000connectome-workbench-1.2.3+git41-gc4c6c90/src/Brain /*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_CARET_MAPPABLE_DATA_FILE_MAPS_VIEWED_IN_OVERLAYS_DECLARE__ #include "EventCaretMappableDataFileMapsViewedInOverlays.h" #undef __EVENT_CARET_MAPPABLE_DATA_FILE_MAPS_VIEWED_IN_OVERLAYS_DECLARE__ #include "CaretAssert.h" #include "EventTypeEnum.h" using namespace caret; /** * \class caret::EventCaretMappableDataFileMapsViewedInOverlays * \brief Get maps viewed as an overlay for the given data file type * \ingroup Brain */ /** * Constructor. */ EventCaretMappableDataFileMapsViewedInOverlays::EventCaretMappableDataFileMapsViewedInOverlays(const CaretMappableDataFile* caretMappableDataFile) : Event(EventTypeEnum::EVENT_CARET_MAPPABLE_DATA_FILE_MAPS_VIEWED_IN_OVERLAYS), m_caretMappableDataFile(caretMappableDataFile) { CaretAssert(caretMappableDataFile); } /** * Destructor. */ EventCaretMappableDataFileMapsViewedInOverlays::~EventCaretMappableDataFileMapsViewedInOverlays() { } /** * @return The type of data file for which maps viewed as overlay are desired. */ const CaretMappableDataFile* EventCaretMappableDataFileMapsViewedInOverlays::getCaretMappableDataFile() const { return m_caretMappableDataFile; } /** * Add map index for caret mappable data file. * * @param mapIndex * Selected map index for data file. */ void EventCaretMappableDataFileMapsViewedInOverlays::addMapIndex(const int32_t mapIndex) { m_mapIndices.insert(mapIndex); } /** * @return Displayed as overlay map indices for file. */ std::set EventCaretMappableDataFileMapsViewedInOverlays::getSelectedMapIndices() const { return m_mapIndices; } connectome-workbench-1.2.3+git41-gc4c6c90/src/Brain/EventCaretMappableDataFileMapsViewedInOverlays.h000066400000000000000000000043661300200146000330560ustar00rootroot00000000000000#ifndef __EVENT_CARET_MAPPABLE_DATA_FILE_MAPS_VIEWED_IN_OVERLAYS_H__ #define __EVENT_CARET_MAPPABLE_DATA_FILE_MAPS_VIEWED_IN_OVERLAYS_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 "Event.h" namespace caret { class CaretMappableDataFile; class EventCaretMappableDataFileMapsViewedInOverlays : public Event { public: EventCaretMappableDataFileMapsViewedInOverlays(const CaretMappableDataFile* caretMappableDataFile); virtual ~EventCaretMappableDataFileMapsViewedInOverlays(); const CaretMappableDataFile* getCaretMappableDataFile() const; void addMapIndex(const int32_t mapIndex); std::set getSelectedMapIndices() const; // ADD_NEW_METHODS_HERE private: EventCaretMappableDataFileMapsViewedInOverlays(const EventCaretMappableDataFileMapsViewedInOverlays&); EventCaretMappableDataFileMapsViewedInOverlays& operator=(const EventCaretMappableDataFileMapsViewedInOverlays&); // ADD_NEW_MEMBERS_HERE const CaretMappableDataFile* m_caretMappableDataFile; std::set m_mapIndices; }; #ifdef __EVENT_CARET_MAPPABLE_DATA_FILE_MAPS_VIEWED_IN_OVERLAYS_DECLARE__ // #endif // __EVENT_CARET_MAPPABLE_DATA_FILE_MAPS_VIEWED_IN_OVERLAYS_DECLARE__ } // namespace #endif //__EVENT_CARET_MAPPABLE_DATA_FILE_MAPS_VIEWED_IN_OVERLAYS_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Brain/EventDataFileAdd.cxx000066400000000000000000000030221300200146000254040ustar00rootroot00000000000000 /*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_DATA_FILE_ADD_DECLARE__ #include "EventDataFileAdd.h" #undef __EVENT_DATA_FILE_ADD_DECLARE__ #include "CaretAssert.h" #include "EventTypeEnum.h" using namespace caret; /** * \class caret::EventDataFileAdd * \brief Add a data file to the Brain. * \ingroup Brain */ /** * Constructor. */ EventDataFileAdd::EventDataFileAdd(CaretDataFile* caretDataFile) : Event(EventTypeEnum::EVENT_DATA_FILE_ADD) { CaretAssert(caretDataFile); m_caretDataFile = caretDataFile; } /** * Destructor. */ EventDataFileAdd::~EventDataFileAdd() { } /** * @return Caret data file that is added to the brain. */ CaretDataFile* EventDataFileAdd::getCaretDataFile() { return m_caretDataFile; } connectome-workbench-1.2.3+git41-gc4c6c90/src/Brain/EventDataFileAdd.h000066400000000000000000000031651300200146000250410ustar00rootroot00000000000000#ifndef __EVENT_DATA_FILE_ADD_H__ #define __EVENT_DATA_FILE_ADD_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 CaretDataFile; class EventDataFileAdd : public Event { public: EventDataFileAdd(CaretDataFile* caretDataFile); virtual ~EventDataFileAdd(); CaretDataFile* getCaretDataFile(); // ADD_NEW_METHODS_HERE private: EventDataFileAdd(const EventDataFileAdd&); EventDataFileAdd& operator=(const EventDataFileAdd&); CaretDataFile* m_caretDataFile; // ADD_NEW_MEMBERS_HERE }; #ifdef __EVENT_DATA_FILE_ADD_DECLARE__ // #endif // __EVENT_DATA_FILE_ADD_DECLARE__ } // namespace #endif //__EVENT_DATA_FILE_ADD_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Brain/EventDataFileDelete.cxx000066400000000000000000000031701300200146000261220ustar00rootroot00000000000000 /*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_DATA_FILE_DELETE_DECLARE__ #include "EventDataFileDelete.h" #undef __EVENT_DATA_FILE_DELETE_DECLARE__ #include "CaretAssert.h" #include "EventTypeEnum.h" using namespace caret; /** * \class caret::EventDataFileDelete * \brief Event for deleting a data file from the brain. * \ingroup Brain */ /** * Constructor. * * @param caretDataFile * File that is deleted. */ EventDataFileDelete::EventDataFileDelete(CaretDataFile* caretDataFile) : Event(EventTypeEnum::EVENT_DATA_FILE_DELETE) { CaretAssert(caretDataFile); m_caretDataFile = caretDataFile; } /** * Destructor. */ EventDataFileDelete::~EventDataFileDelete() { } /** * @return Caret data file that is added to the brain. */ CaretDataFile* EventDataFileDelete::getCaretDataFile() { return m_caretDataFile; } connectome-workbench-1.2.3+git41-gc4c6c90/src/Brain/EventDataFileDelete.h000066400000000000000000000032251300200146000255500ustar00rootroot00000000000000#ifndef __EVENT_DATA_FILE_DELETE_H__ #define __EVENT_DATA_FILE_DELETE_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 CaretDataFile; class EventDataFileDelete : public Event { public: EventDataFileDelete(CaretDataFile* dataFile); virtual ~EventDataFileDelete(); CaretDataFile* getCaretDataFile(); // ADD_NEW_METHODS_HERE private: EventDataFileDelete(const EventDataFileDelete&); EventDataFileDelete& operator=(const EventDataFileDelete&); CaretDataFile* m_caretDataFile; // ADD_NEW_MEMBERS_HERE }; #ifdef __EVENT_DATA_FILE_DELETE_DECLARE__ // #endif // __EVENT_DATA_FILE_DELETE_DECLARE__ } // namespace #endif //__EVENT_DATA_FILE_DELETE_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Brain/EventDataFileRead.cxx000066400000000000000000000166001300200146000255750ustar00rootroot00000000000000/*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 "CaretAssert.h" #include "EventDataFileRead.h" using namespace caret; /** * Constructor. * * @param loadIntoBrain * Brain into which file is loaded. */ EventDataFileRead::EventDataFileRead(Brain* loadIntoBrain) : Event(EventTypeEnum::EVENT_DATA_FILE_READ) { this->loadIntoBrain = loadIntoBrain; this->username = ""; this->password = ""; CaretAssert(this->loadIntoBrain); } /** * Destructor. */ EventDataFileRead::~EventDataFileRead() { } /** * Add a data file for reading. * * @param dataFileType * Type of data file. * @param dataFileName * Name of the data file. */ void EventDataFileRead::addDataFile(const DataFileTypeEnum::Enum dataFileType, const AString& dataFileName) { CaretAssert(dataFileType != DataFileTypeEnum::UNKNOWN); CaretAssert(dataFileName.isEmpty() == false); m_dataFiles.push_back(FileData(StructureEnum::INVALID, dataFileType, dataFileName, false)); } /** * Add a data file for reading. * * @param structure * Structure for file if not present in file. * @param dataFileType * Type of data file. * @param dataFileName * Name of the data file. */ void EventDataFileRead::addDataFile(const StructureEnum::Enum structure, const DataFileTypeEnum::Enum dataFileType, const AString& dataFileName) { //CaretAssert(structure != StructureEnum::INVALID); CaretAssert(dataFileType != DataFileTypeEnum::UNKNOWN); CaretAssert(dataFileName.isEmpty() == false); m_dataFiles.push_back(FileData(structure, dataFileType, dataFileName, false)); } /** * @return Number of data files to read. */ int32_t EventDataFileRead::getNumberOfDataFilesToRead() const { return m_dataFiles.size(); } /** * Get the name of the data file that is to be loaded. * * @param dataFileIndex * Index of the data file. * @return Name of data file to load. */ AString EventDataFileRead::getDataFileName(const int32_t dataFileIndex) const { CaretAssertVectorIndex(m_dataFiles, dataFileIndex); return m_dataFiles[dataFileIndex].m_dataFileName; } /** * Get the type of data file for loading. * * @param dataFileIndex * Index of the data file. * @return Type of file for loading. */ DataFileTypeEnum::Enum EventDataFileRead::getDataFileType(const int32_t dataFileIndex) const { CaretAssertVectorIndex(m_dataFiles, dataFileIndex); return m_dataFiles[dataFileIndex].m_dataFileType; } /** * Get brain into which file is loaded. * @return Brain into which file is loaded. */ Brain* EventDataFileRead::getLoadIntoBrain() { return this->loadIntoBrain; } /** * @param dataFileIndex * Index of the data file. * @return The structure associated with the data file. */ StructureEnum::Enum EventDataFileRead::getStructure(const int32_t dataFileIndex) const { CaretAssertVectorIndex(m_dataFiles, dataFileIndex); return m_dataFiles[dataFileIndex].m_structure; } /** * Get the error message for a file. * @param dataFileIndex * Index of the file. * @return * Error message. */ AString EventDataFileRead::getFileErrorMessage(const int32_t dataFileIndex) const { CaretAssertVectorIndex(m_dataFiles, dataFileIndex); return m_dataFiles[dataFileIndex].m_errorMessage; } /** * Set the error message for a file. * @param dataFileIndex * Index of the file. * @param errorMessage * Error message. */ void EventDataFileRead::setFileErrorMessage(const int32_t dataFileIndex, const AString& errorMessage) { CaretAssertVectorIndex(m_dataFiles, dataFileIndex); m_dataFiles[dataFileIndex].m_errorMessage = errorMessage; } /** * Was there an error reading the file at the given index. * @param dataFileIndex * Index of the file. * @return * true if there was an error, otherwise false. */ bool EventDataFileRead::isFileError(const int32_t dataFileIndex) const { CaretAssertVectorIndex(m_dataFiles, dataFileIndex); return (m_dataFiles[dataFileIndex].m_errorMessage.isEmpty() == false); } /** * @param dataFileIndex * Index of the data file. * @return True if the file could not be read due * to an invalid structure. */ bool EventDataFileRead::isFileErrorInvalidStructure(const int32_t dataFileIndex) const { CaretAssertVectorIndex(m_dataFiles, dataFileIndex); return m_dataFiles[dataFileIndex].m_invalidStructureError; } /** * Set the invalid structure status. * @param dataFileIndex * Index of the data file. * @param status * New invalid structure status (true if invalid). */ void EventDataFileRead::setFileErrorInvalidStructure(const int32_t dataFileIndex, const bool status) { CaretAssertVectorIndex(m_dataFiles, dataFileIndex); m_dataFiles[dataFileIndex].m_invalidStructureError = status; } /** * @return File that was read for the given index. */ CaretDataFile* EventDataFileRead::getDataFileRead(const int32_t dataFileIndex) { CaretAssertVectorIndex(m_dataFiles, dataFileIndex); return m_dataFiles[dataFileIndex].m_caretDataFileThatWasRead; } /** * Set the file that was read. * * @param dataFileIndex * Index of the file. * @param caretDataFile * Pointer to file that was read for given index. */ void EventDataFileRead::setDataFileRead(const int32_t dataFileIndex, CaretDataFile* caretDataFile) { CaretAssertVectorIndex(m_dataFiles, dataFileIndex); m_dataFiles[dataFileIndex].m_caretDataFileThatWasRead = caretDataFile; } /** * @return The username. */ AString EventDataFileRead::getUsername() const { return this->username; } /** * @return The password. */ AString EventDataFileRead::getPassword() const { return this->password; } /** * Set the username and password. * * @param username * Name of user account. * @param password * Password of user account. */ void EventDataFileRead::setUsernameAndPassword(const AString& username, const AString& password) { this->username = username; this->password = password; } /** * @param dataFileIndex * Index of the data file. * @return After file is read, mark it as modified. */ bool EventDataFileRead::isFileToBeMarkedModified(const int32_t dataFileIndex) const { CaretAssertVectorIndex(m_dataFiles, dataFileIndex); return m_dataFiles[dataFileIndex].m_markFileAsModified; } connectome-workbench-1.2.3+git41-gc4c6c90/src/Brain/EventDataFileRead.h000066400000000000000000000102761300200146000252250ustar00rootroot00000000000000#ifndef __EVENT_DATA_FILE_READ_H__ #define __EVENT_DATA_FILE_READ_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 "DataFileTypeEnum.h" #include "Event.h" #include "StructureEnum.h" namespace caret { class Brain; class CaretDataFile; /** * Event for reading one or more data files. */ class EventDataFileRead : public Event { public: EventDataFileRead(Brain* loadIntoBrain); virtual ~EventDataFileRead(); void addDataFile(const DataFileTypeEnum::Enum dataFileType, const AString& dataFileName); void addDataFile(const StructureEnum::Enum structure, const DataFileTypeEnum::Enum dataFileType, const AString& dataFileName); int32_t getNumberOfDataFilesToRead() const; AString getDataFileName(const int32_t dataFileIndex) const; DataFileTypeEnum::Enum getDataFileType(const int32_t dataFileIndex) const; Brain* getLoadIntoBrain(); StructureEnum::Enum getStructure(const int32_t dataFileIndex) const; AString getFileErrorMessage(const int32_t dataFileIndex) const; void setFileErrorMessage(const int32_t dataFileIndex, const AString& errorMessage); bool isFileError(const int32_t dataFileIndex) const; bool isFileErrorInvalidStructure(const int32_t dataFileIndex) const; void setFileErrorInvalidStructure(const int32_t dataFileIndex, const bool status); AString getUsername() const; AString getPassword() const; void setUsernameAndPassword(const AString& username, const AString& password); bool isFileToBeMarkedModified(const int32_t dataFileIndex) const; CaretDataFile* getDataFileRead(const int32_t dataFileIndex); void setDataFileRead(const int32_t dataFileIndex, CaretDataFile* caretDataFile); private: class FileData { public: FileData(const StructureEnum::Enum structure, const DataFileTypeEnum::Enum dataFileType, const AString& dataFileName, const bool markFileAsModified) : m_structure(structure), m_dataFileType(dataFileType), m_dataFileName(dataFileName), m_markFileAsModified(markFileAsModified) { m_invalidStructureError = false; m_caretDataFileThatWasRead = NULL; } ~FileData() { } StructureEnum::Enum m_structure; DataFileTypeEnum::Enum m_dataFileType; AString m_dataFileName; AString m_errorMessage; CaretDataFile* m_caretDataFileThatWasRead; bool m_markFileAsModified; bool m_invalidStructureError; }; std::vector m_dataFiles; EventDataFileRead(const EventDataFileRead&); EventDataFileRead& operator=(const EventDataFileRead&); Brain* loadIntoBrain; AString username; AString password; }; } // namespace #endif // __EVENT_DATA_FILE_READ_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Brain/EventDataFileReload.cxx000066400000000000000000000060441300200146000261310ustar00rootroot00000000000000 /*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_DATA_FILE_RELOAD_DECLARE__ #include "EventDataFileReload.h" #undef __EVENT_DATA_FILE_RELOAD_DECLARE__ #include "EventTypeEnum.h" using namespace caret; /** * \class caret::EventDataFileReload * \brief Event for reloading a Caret Data File. * \ingroup Brain */ /** * Constructor. * * Note: If reload fails, the caretDataFile WILL BE DELETED and the pointer * must no longer be deferenced. * * @param brain * Brain into which file is reloaded. * @param caretDataFile * Caret data file that is reloaded. */ EventDataFileReload::EventDataFileReload(Brain* brain, CaretDataFile* caretDataFile) : Event(EventTypeEnum::EVENT_DATA_FILE_RELOAD), m_brain(brain), m_caretDataFile(caretDataFile) { } /** * Destructor. */ EventDataFileReload::~EventDataFileReload() { } /** * @return Brain into which file is loaded. */ Brain* EventDataFileReload::getBrain() { return this->m_brain; } /** * @return The Caret Data File that will be reloaded. */ CaretDataFile* EventDataFileReload::getCaretDataFile() { return m_caretDataFile; } /** * @return true if there was an error reloading the file, else false. */ bool EventDataFileReload::isError() const { const bool errorFlag = (m_errorMessage.isEmpty() == false); return errorFlag; } /** * @return The error message. */ AString EventDataFileReload::getErrorMessage() const { return m_errorMessage; } /** * Set there error message describing reloading error. * * @param errorMessage * Message describing the error. */ void EventDataFileReload::setErrorMessage(const AString& errorMessage) { m_errorMessage = errorMessage; } /** * @return The username. */ AString EventDataFileReload::getUsername() const { return m_username; } /** * @return The password. */ AString EventDataFileReload::getPassword() const { return m_password; } /** * Set the username and password. * * @param username * Name of user account. * @param password * Password of user account. */ void EventDataFileReload::setUsernameAndPassword(const AString& username, const AString& password) { m_username = username; m_password = password; } connectome-workbench-1.2.3+git41-gc4c6c90/src/Brain/EventDataFileReload.h000066400000000000000000000044431300200146000255570ustar00rootroot00000000000000#ifndef __EVENT_DATA_FILE_RELOAD_H__ #define __EVENT_DATA_FILE_RELOAD_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 Brain; class CaretDataFile; class EventDataFileReload : public Event { public: EventDataFileReload(Brain* brain, CaretDataFile* caretDataFile); virtual ~EventDataFileReload(); Brain* getBrain(); CaretDataFile* getCaretDataFile(); bool isError() const; AString getErrorMessage() const; void setErrorMessage(const AString& errorMessage); AString getUsername() const; AString getPassword() const; void setUsernameAndPassword(const AString& username, const AString& password); private: EventDataFileReload(const EventDataFileReload&); EventDataFileReload& operator=(const EventDataFileReload&); public: // ADD_NEW_METHODS_HERE private: Brain* m_brain; CaretDataFile* m_caretDataFile; AString m_username; AString m_password; AString m_errorMessage; // ADD_NEW_MEMBERS_HERE }; #ifdef __EVENT_DATA_FILE_RELOAD_DECLARE__ // #endif // __EVENT_DATA_FILE_RELOAD_DECLARE__ } // namespace #endif //__EVENT_DATA_FILE_RELOAD_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Brain/EventGetBrainOpenGLTextRenderer.cxx000066400000000000000000000040761300200146000304300ustar00rootroot00000000000000 /*LICENSE_START*/ /* * Copyright (C) 2015 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_GET_BRAIN_OPEN_G_L_TEXT_RENDERER_DECLARE__ #include "EventGetBrainOpenGLTextRenderer.h" #undef __EVENT_GET_BRAIN_OPEN_G_L_TEXT_RENDERER_DECLARE__ #include "CaretAssert.h" #include "EventTypeEnum.h" using namespace caret; /** * \class caret::EventGetBrainOpenGLTextRenderer * \brief Event to get a text renderer for a window * \ingroup Brain */ /** * Constructor. */ EventGetBrainOpenGLTextRenderer::EventGetBrainOpenGLTextRenderer(const int32_t windowIndex) : Event(EventTypeEnum::EVENT_GET_TEXT_RENDERER_FOR_WINDOW), m_windowIndex(windowIndex) { m_textRenderer = NULL; } /** * Destructor. */ 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). */ BrainOpenGLTextRenderInterface* EventGetBrainOpenGLTextRenderer::getTextRenderer() const { return m_textRenderer; } /** * Set the text renderer. * * @param textRenderer * The text renderer. */ void EventGetBrainOpenGLTextRenderer::setTextRenderer(BrainOpenGLTextRenderInterface* textRenderer) { m_textRenderer = textRenderer; } connectome-workbench-1.2.3+git41-gc4c6c90/src/Brain/EventGetBrainOpenGLTextRenderer.h000066400000000000000000000040311300200146000300440ustar00rootroot00000000000000#ifndef __EVENT_GET_BRAIN_OPEN_G_L_TEXT_RENDERER_H__ #define __EVENT_GET_BRAIN_OPEN_G_L_TEXT_RENDERER_H__ /*LICENSE_START*/ /* * Copyright (C) 2015 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 BrainOpenGLTextRenderInterface; class EventGetBrainOpenGLTextRenderer : public Event { public: EventGetBrainOpenGLTextRenderer(const int32_t windowIndex); virtual ~EventGetBrainOpenGLTextRenderer(); int32_t getWindowIndex() const; BrainOpenGLTextRenderInterface* getTextRenderer() const; void setTextRenderer(BrainOpenGLTextRenderInterface* textRenderer); // ADD_NEW_METHODS_HERE private: EventGetBrainOpenGLTextRenderer(const EventGetBrainOpenGLTextRenderer&); EventGetBrainOpenGLTextRenderer& operator=(const EventGetBrainOpenGLTextRenderer&); const int32_t m_windowIndex; BrainOpenGLTextRenderInterface* m_textRenderer; // ADD_NEW_MEMBERS_HERE }; #ifdef __EVENT_GET_BRAIN_OPEN_G_L_TEXT_RENDERER_DECLARE__ // #endif // __EVENT_GET_BRAIN_OPEN_G_L_TEXT_RENDERER_DECLARE__ } // namespace #endif //__EVENT_GET_BRAIN_OPEN_G_L_TEXT_RENDERER_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Brain/EventIdentificationHighlightLocation.cxx000066400000000000000000000060131300200146000315770ustar00rootroot00000000000000/*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 "BrainConstants.h" #include "CaretAssert.h" #include "EventIdentificationHighlightLocation.h" using namespace caret; /** * Constructor for identification event of location. * * @parma tabIndex * Index of tab in which identification took place. This value may * be negative indicating that the identification request is not * for a specific browser tab. One source for this is the Select Brainordinate * option on the Information Window. * @param xyz * Stereotaxic location of selected item. */ EventIdentificationHighlightLocation::EventIdentificationHighlightLocation(const int32_t tabIndex, const float xyz[3], const LOAD_FIBER_ORIENTATION_SAMPLES_MODE loadFiberOrientationSamplesMode) : Event(EventTypeEnum::EVENT_IDENTIFICATION_HIGHLIGHT_LOCATION), m_tabIndex(tabIndex), m_loadFiberOrientationSamplesMode(loadFiberOrientationSamplesMode) { /* * NOTE: a negative value is allowed. */ CaretAssert(tabIndex < BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS); m_xyz[0] = xyz[0]; m_xyz[1] = xyz[1]; m_xyz[2] = xyz[2]; } /** * Destructor. */ EventIdentificationHighlightLocation::~EventIdentificationHighlightLocation() { } /** * @return The stereotaxic location of the identification (valid for all). */ const float* EventIdentificationHighlightLocation::getXYZ() const { return m_xyz; } /** * Is the tab with the given index selected for identification operations? * * @param tabIndex * Index of tab. * @return True if tab is selected, else false. */ bool EventIdentificationHighlightLocation::isTabSelected(const int32_t tabIndex) const { /* * All tabs? */ if (m_tabIndex < 0) { return true; } else if (m_tabIndex == tabIndex) { return true; } return false; } /** * @return The mode for loading of fiber orientation samples. */ EventIdentificationHighlightLocation::LOAD_FIBER_ORIENTATION_SAMPLES_MODE EventIdentificationHighlightLocation::getLoadFiberOrientationSamplesMode() const { return m_loadFiberOrientationSamplesMode; } connectome-workbench-1.2.3+git41-gc4c6c90/src/Brain/EventIdentificationHighlightLocation.h000066400000000000000000000044301300200146000312250ustar00rootroot00000000000000#ifndef __EVENT_IDENTIFICATION_HIGHLIGHT_LOCATION_H__ #define __EVENT_IDENTIFICATION_HIGHLIGHT_LOCATION_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" #include "StructureEnum.h" namespace caret { /// Highlight location of an indentification class EventIdentificationHighlightLocation : public Event { public: enum LOAD_FIBER_ORIENTATION_SAMPLES_MODE { LOAD_FIBER_ORIENTATION_SAMPLES_MODE_YES, LOAD_FIBER_ORIENTATION_SAMPLES_MODE_NO }; EventIdentificationHighlightLocation(const int32_t tabIndex, const float xyz[3], const LOAD_FIBER_ORIENTATION_SAMPLES_MODE loadFiberOrientationSamplesMode); virtual ~EventIdentificationHighlightLocation(); const float* getXYZ() const; bool isTabSelected(const int32_t tabIndex) const; LOAD_FIBER_ORIENTATION_SAMPLES_MODE getLoadFiberOrientationSamplesMode() const; private: EventIdentificationHighlightLocation(const EventIdentificationHighlightLocation&); EventIdentificationHighlightLocation& operator=(const EventIdentificationHighlightLocation&); const int32_t m_tabIndex; float m_xyz[3]; const LOAD_FIBER_ORIENTATION_SAMPLES_MODE m_loadFiberOrientationSamplesMode; }; } // namespace #endif // __EVENT_IDENTIFICATION_HIGHLIGHT_LOCATION_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Brain/EventModelAdd.cxx000066400000000000000000000024041300200146000247760ustar00rootroot00000000000000/*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 "CaretAssert.h" #include "EventModelAdd.h" using namespace caret; /** * Constructor. * * @param model * Model to add. */ EventModelAdd::EventModelAdd(Model* model) : Event(EventTypeEnum::EVENT_MODEL_ADD) { m_model = model; CaretAssert(m_model); } /** * Destructor. */ EventModelAdd::~EventModelAdd() { } /** * @return Model that is to be added. */ Model* EventModelAdd::getModel() { return m_model; } connectome-workbench-1.2.3+git41-gc4c6c90/src/Brain/EventModelAdd.h000066400000000000000000000025471300200146000244330ustar00rootroot00000000000000#ifndef __EVENT_MODEL_ADD_H__ #define __EVENT_MODEL_ADD_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 Model; /// Event for adding models class EventModelAdd : public Event { public: EventModelAdd(Model* model); virtual ~EventModelAdd(); Model* getModel(); private: EventModelAdd(const EventModelAdd&); EventModelAdd& operator=(const EventModelAdd&); Model* m_model; }; } // namespace #endif // __EVENT_MODEL_ADD_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Brain/EventModelDelete.cxx000066400000000000000000000023631300200146000255140ustar00rootroot00000000000000/*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 "CaretAssert.h" #include "EventModelDelete.h" using namespace caret; /** * Constructor. */ EventModelDelete::EventModelDelete(Model* model) : Event(EventTypeEnum::EVENT_MODEL_DELETE) { m_model = model; CaretAssert(m_model); } /** * Destructor. */ EventModelDelete::~EventModelDelete() { } /** * @return Model that is to be deleted. */ Model* EventModelDelete::getModel() { return m_model; } connectome-workbench-1.2.3+git41-gc4c6c90/src/Brain/EventModelDelete.h000066400000000000000000000026271300200146000251440ustar00rootroot00000000000000#ifndef __EVENT_MODEL_DELETE_H__ #define __EVENT_MODEL_DELETE_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 Model; /// Event for deleting models class EventModelDelete : public Event { public: EventModelDelete(Model* model); virtual ~EventModelDelete(); Model* getModel(); private: EventModelDelete(const EventModelDelete&); EventModelDelete& operator=(const EventModelDelete&); Model* m_model; }; } // namespace #endif // __EVENT_MODEL_DELETE_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Brain/EventModelGetAll.cxx000066400000000000000000000052671300200146000254700ustar00rootroot00000000000000/*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 "EventModelGetAll.h" #include "ModelSurface.h" using namespace caret; /** * Constructor. */ EventModelGetAll::EventModelGetAll() : Event(EventTypeEnum::EVENT_MODEL_GET_ALL) { } /** * Destructor. */ EventModelGetAll::~EventModelGetAll() { } /** * Add models. * @param modelsToAdd * These model added. */ void EventModelGetAll::addModels( const std::vector& modelsToAdd) { m_models.insert(m_models.end(), modelsToAdd.begin(), modelsToAdd.end()); } /** * Get the model. * * @return vector containing the model. */ const std::vector EventModelGetAll::getModels() const { return this->m_models; } /** * Is a model valid? * * @param model * Model that is checked for validity. * * @return true if valid, else false. */ bool EventModelGetAll::isModelValid( const Model* model) const { if (std::find(this->m_models.begin(), this->m_models.end(), model) != m_models.end()) { return true; } return false; } /** * Get the first model. * * @return Pointer to first model or * NULL if there are no model. */ Model* EventModelGetAll::getFirstModel() const { if (m_models.empty() == false) { return m_models[0]; } return NULL; } /** * Get the first model surface. * * @return Pointer to first model surface or * NULL if there are no model surfaces. */ ModelSurface* EventModelGetAll::getFirstModelSurface() const { ModelSurface* surfaceModelOut = NULL; const int32_t numModels = static_cast(m_models.size()); for (int32_t i = 0; i < numModels; i++) { surfaceModelOut = dynamic_cast(m_models[i]); if (surfaceModelOut != NULL) { break; } } return surfaceModelOut; } connectome-workbench-1.2.3+git41-gc4c6c90/src/Brain/EventModelGetAll.h000066400000000000000000000033701300200146000251060ustar00rootroot00000000000000#ifndef __EVENT_GET_MODEL_DISPLAY_CONTROLLERS_H__ #define __EVENT_GET_MODEL_DISPLAY_CONTROLLERS_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 "Event.h" namespace caret { class Model; class ModelSurface; /// Event for getting models class EventModelGetAll : public Event { public: EventModelGetAll(); virtual ~EventModelGetAll(); void addModels(const std::vector& modelsToAdd); const std::vector getModels() const; bool isModelValid(const Model* model) const; Model* getFirstModel() const; ModelSurface* getFirstModelSurface() const; private: EventModelGetAll(const EventModelGetAll&); EventModelGetAll& operator=(const EventModelGetAll&); std::vector m_models; }; } // namespace #endif // __EVENT_GET_MODEL_DISPLAY_CONTROLLERS_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Brain/EventModelSurfaceGet.cxx000066400000000000000000000032751300200146000263450ustar00rootroot00000000000000/*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 "CaretAssert.h" #include "EventModelSurfaceGet.h" using namespace caret; /** * Constructor. */ EventModelSurfaceGet::EventModelSurfaceGet(const Surface* surface) : Event(EventTypeEnum::EVENT_MODEL_SURFACE_GET), surface(surface) { CaretAssert(surface); } /** * Destructor. */ EventModelSurfaceGet::~EventModelSurfaceGet() { } /** * @return The model surface that was found. */ ModelSurface* EventModelSurfaceGet::getModelSurface() { return m_modelSurface; } /** * Set the model surface. * @param modelSurface * Model surface that matches the specified surface. */ void EventModelSurfaceGet::setModelSurface(ModelSurface* modelSurface) { m_modelSurface = modelSurface; } /** * @return Returns the surface for which the model surface is requested. */ const Surface* EventModelSurfaceGet::getSurface() const { return this->surface; } connectome-workbench-1.2.3+git41-gc4c6c90/src/Brain/EventModelSurfaceGet.h000066400000000000000000000033141300200146000257640ustar00rootroot00000000000000#ifndef __EVENT_MODEL_SURFACE_GET_H__ #define __EVENT_MODEL_SURFACE_GET_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 "Event.h" namespace caret { class ModelSurface; class Surface; /// Find the Surface model that contains a specific Surface. class EventModelSurfaceGet : public Event { public: EventModelSurfaceGet(const Surface* surface); virtual ~EventModelSurfaceGet(); ModelSurface* getModelSurface(); void setModelSurface(ModelSurface* modelSurface); const Surface* getSurface() const; private: EventModelSurfaceGet(const EventModelSurfaceGet&); EventModelSurfaceGet& operator=(const EventModelSurfaceGet&); ModelSurface* m_modelSurface; const Surface* surface; }; } // namespace #endif // __EVENT_MODEL_SURFACE_GET_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Brain/EventNodeDataFilesGet.cxx000066400000000000000000000057451300200146000264420ustar00rootroot00000000000000/*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 "CaretAssert.h" #include "EventNodeDataFilesGet.h" #include "LabelFile.h" #include "MetricFile.h" #include "RgbaFile.h" using namespace caret; /** * Constructor for node data files that are * associated with ANY surface. */ EventNodeDataFilesGet::EventNodeDataFilesGet() : Event(EventTypeEnum::EVENT_GET_NODE_DATA_FILES), surface(NULL) { } /** * Constructor for node data files that are * associated with a specific surface. */ EventNodeDataFilesGet::EventNodeDataFilesGet(const Surface* surfaceIn) : Event(EventTypeEnum::EVENT_GET_NODE_DATA_FILES), surface(surfaceIn) { } /** * Destructor. */ EventNodeDataFilesGet::~EventNodeDataFilesGet() { } /** * Add a node data file. * @param nodeDataFile * Data file that is added. */ void EventNodeDataFilesGet::addFile(GiftiTypeFile* nodeDataFile) { CaretAssert(nodeDataFile); if (nodeDataFile->getNumberOfColumns() <= 0) { return; } LabelFile* lf = dynamic_cast(nodeDataFile); if (lf != NULL) { this->labelFiles.push_back(lf); return; } MetricFile* mf = dynamic_cast(nodeDataFile); if (mf != NULL) { this->metricFiles.push_back(mf); return; } RgbaFile* rf = dynamic_cast(nodeDataFile); if (rf != NULL) { this->rgbaFiles.push_back(rf); return; } CaretAssertMessage(0, "Unsupported vertex data file: " + AString(typeid(nodeDataFile).name()) + " New vertex data file added?"); } /** * @return Returns all data files. */ void EventNodeDataFilesGet::getAllFiles(std::vector& allFilesOut) const { allFilesOut.clear(); allFilesOut.insert(allFilesOut.end(), this->labelFiles.begin(), this->labelFiles.end()); allFilesOut.insert(allFilesOut.end(), this->metricFiles.begin(), this->metricFiles.end()); allFilesOut.insert(allFilesOut.end(), this->rgbaFiles.begin(), this->rgbaFiles.end()); } connectome-workbench-1.2.3+git41-gc4c6c90/src/Brain/EventNodeDataFilesGet.h000066400000000000000000000051221300200146000260540ustar00rootroot00000000000000#ifndef __EVENT_NODE_DATA_FILES_GET_H__ #define __EVENT_NODE_DATA_FILES_GET_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 GiftiTypeFile; class LabelFile; class MetricFile; class Model; class RgbaFile; class Surface; /// Event that gets all node data files. class EventNodeDataFilesGet : public Event { public: EventNodeDataFilesGet(); EventNodeDataFilesGet(const Surface* surface); virtual ~EventNodeDataFilesGet(); void addFile(GiftiTypeFile* nodeDataFile); /** * @return Returns the surface for which associated data * files are requested. If NULL, then node data files * from all brain structures are requested. */ const Surface* getSurface() const { return this->surface; } /** @return Returns the label files. */ std::vector getLabelFiles() const { return this->labelFiles; } /** @return Returns the metric files. */ std::vector getMetricFiles() const { return this->metricFiles; } /** @return Returns the rgba files. */ std::vector getRgbaFiles() const { return this->rgbaFiles; } void getAllFiles(std::vector& allFilesOut) const; private: EventNodeDataFilesGet(const EventNodeDataFilesGet&); EventNodeDataFilesGet& operator=(const EventNodeDataFilesGet&); const Surface* surface; std::vector labelFiles; std::vector metricFiles; std::vector rgbaFiles; }; } // namespace #endif // __EVENT_NODE_DATA_FILES_GET_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Brain/EventNodeIdentificationColorsGetFromCharts.cxx000066400000000000000000000102131300200146000326740ustar00rootroot00000000000000 /*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_NODE_IDENTIFICATION_COLORS_GET_FROM_CHARTS_DECLARE__ #include "EventNodeIdentificationColorsGetFromCharts.h" #undef __EVENT_NODE_IDENTIFICATION_COLORS_GET_FROM_CHARTS_DECLARE__ #include "CaretAssert.h" #include "EventTypeEnum.h" using namespace caret; /** * \class caret::EventNodeIdentificationColorsGetFromCharts * \brief Get colors for node identification systems displayed in charts * \ingroup Brain */ /** * Constructor. * * @param structure * Structure for matching to nodes in charts. * @param tabIndex * Index of tab * @param nodeIndices * Indices of nodes for which chart colors are requested. */ EventNodeIdentificationColorsGetFromCharts::EventNodeIdentificationColorsGetFromCharts(const StructureEnum::Enum structure, const int32_t tabIndex, const std::vector& nodeIndices) : Event(EventTypeEnum::EVENT_NODE_IDENTIFICATION_COLORS_GET_FROM_CHARTS) { m_structureName = StructureEnum::toName(structure); m_tabIndex = tabIndex; m_nodeIndices = nodeIndices; } /** * Destructor. */ EventNodeIdentificationColorsGetFromCharts::~EventNodeIdentificationColorsGetFromCharts() { } /** * Add a node and its color (this method is called by charts). * * @param nodeIndex * Index of the node. * @param rgb * RGB coloring for the node. */ void EventNodeIdentificationColorsGetFromCharts::addNode(const int32_t nodeIndex, const float rgb[3]) { if (m_nodeRgbColor.find(nodeIndex) != m_nodeRgbColor.end()) { return; } RgbColor rgbColor; rgbColor.rgb[0] = rgb[0] * 255.0; rgbColor.rgb[1] = rgb[1] * 255.0; rgbColor.rgb[2] = rgb[2] * 255.0; m_nodeRgbColor.insert(std::pair(nodeIndex, rgbColor)); } /** * Apply chart color to node. If there is chart coloring for the node * with the given index, it is applied. Otherwise, no action is taken. * This method is called by drawing code. * * @param nodeIndex * Index of the node. * @param rgb * Receives coloring for the node from the chart coloring (if available). */ void EventNodeIdentificationColorsGetFromCharts::applyChartColorToNode(const int32_t nodeIndex, uint8_t rgb[3]) { std::map::const_iterator iter = m_nodeRgbColor.find(nodeIndex); if (iter != m_nodeRgbColor.end()) { RgbColor rgbColor = iter->second; rgb[0] = rgbColor.rgb[0]; rgb[1] = rgbColor.rgb[1]; rgb[2] = rgbColor.rgb[2]; } } /** * @return Name of the surface node's structure. This method is called by charts. */ AString EventNodeIdentificationColorsGetFromCharts::getStructureName() const { return m_structureName; } /** * @return Index of tab where notification symbols are displayed. */ int32_t EventNodeIdentificationColorsGetFromCharts::getTabIndex() const { return m_tabIndex; } /** * @return Indices of nodes for which chart colors are requested. */ std::vector EventNodeIdentificationColorsGetFromCharts::getNodeIndices() const { return m_nodeIndices; } connectome-workbench-1.2.3+git41-gc4c6c90/src/Brain/EventNodeIdentificationColorsGetFromCharts.h000066400000000000000000000052171300200146000323310ustar00rootroot00000000000000#ifndef __EVENT_NODE_IDENTIFICATION_COLORS_GET_FROM_CHARTS_H__ #define __EVENT_NODE_IDENTIFICATION_COLORS_GET_FROM_CHARTS_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 "Event.h" #include "StructureEnum.h" namespace caret { class EventNodeIdentificationColorsGetFromCharts : public Event { public: EventNodeIdentificationColorsGetFromCharts(const StructureEnum::Enum structure, const int32_t tabIndex, const std::vector& nodeIndices); virtual ~EventNodeIdentificationColorsGetFromCharts(); void addNode(const int32_t nodeIndex, const float rgba[4]); void applyChartColorToNode(const int32_t nodeIndex, uint8_t rgba[4]); AString getStructureName() const; int32_t getTabIndex() const; std::vector getNodeIndices() const; // ADD_NEW_METHODS_HERE private: EventNodeIdentificationColorsGetFromCharts(const EventNodeIdentificationColorsGetFromCharts&); EventNodeIdentificationColorsGetFromCharts& operator=(const EventNodeIdentificationColorsGetFromCharts&); // ADD_NEW_MEMBERS_HERE AString m_structureName; int32_t m_tabIndex; std::vector m_nodeIndices; struct RgbColor { uint8_t rgb[3]; }; std::map m_nodeRgbColor; }; #ifdef __EVENT_NODE_IDENTIFICATION_COLORS_GET_FROM_CHARTS_DECLARE__ // #endif // __EVENT_NODE_IDENTIFICATION_COLORS_GET_FROM_CHARTS_DECLARE__ } // namespace #endif //__EVENT_NODE_IDENTIFICATION_COLORS_GET_FROM_CHARTS_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Brain/EventOverlayValidate.cxx000066400000000000000000000034441300200146000264250ustar00rootroot00000000000000 /*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_OVERLAY_VALIDATE_DECLARE__ #include "EventOverlayValidate.h" #undef __EVENT_OVERLAY_VALIDATE_DECLARE__ #include "EventTypeEnum.h" using namespace caret; /** * \class caret::EventOverlayValidate * \brief Test an overlay for validity (it exists). * \ingroup Brain */ /** * Constructor. */ EventOverlayValidate::EventOverlayValidate(const Overlay* overlay) : Event(EventTypeEnum::EVENT_OVERLAY_VALIDATE), m_overlay(overlay) { m_valid = false; } /** * Destructor. */ EventOverlayValidate::~EventOverlayValidate() { } /** * @return true if the overlay was found to be valid. */ bool EventOverlayValidate::isValidOverlay() const { return m_valid; } /** * Set the validity if the given overlay is the overlay * that was passed to the constructor. * * @param overlay * Overlay tested for match. */ void EventOverlayValidate::testValidOverlay(const Overlay* overlay) { if (m_overlay == overlay) { m_valid = true; } } connectome-workbench-1.2.3+git41-gc4c6c90/src/Brain/EventOverlayValidate.h000066400000000000000000000034011300200146000260430ustar00rootroot00000000000000#ifndef __EVENT_OVERLAY_VALIDATE_H__ #define __EVENT_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 Overlay; class EventOverlayValidate : public Event { public: EventOverlayValidate(const Overlay* overlay); virtual ~EventOverlayValidate(); bool isValidOverlay() const; void testValidOverlay(const Overlay* overlay); private: EventOverlayValidate(const EventOverlayValidate&); EventOverlayValidate& operator=(const EventOverlayValidate&); public: // ADD_NEW_METHODS_HERE private: // ADD_NEW_MEMBERS_HERE const Overlay* m_overlay; bool m_valid; }; #ifdef __EVENT_OVERLAY_VALIDATE_DECLARE__ // #endif // __EVENT_OVERLAY_VALIDATE_DECLARE__ } // namespace #endif //__EVENT_OVERLAY_VALIDATE_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Brain/EventSpecFileReadDataFiles.cxx000066400000000000000000000047271300200146000274020ustar00rootroot00000000000000/*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 "CaretAssert.h" #include "EventSpecFileReadDataFiles.h" using namespace caret; /** * Constructor. * * @param loadIntoBrain * Brain into which file is loaded. * @param specFile * Spec file that has its files read. */ EventSpecFileReadDataFiles::EventSpecFileReadDataFiles(Brain* loadIntoBrain, SpecFile* specFile) : Event(EventTypeEnum::EVENT_SPEC_FILE_READ_DATA_FILES) { this->loadIntoBrain = loadIntoBrain; this->specFile = specFile; this->username = ""; this->password = ""; CaretAssert(this->loadIntoBrain); CaretAssert(this->specFile); } /** * Destructor. */ EventSpecFileReadDataFiles::~EventSpecFileReadDataFiles() { } /** * @return The spec file that is to have its data files loaded. */ SpecFile* EventSpecFileReadDataFiles::getSpecFile() { return this->specFile; } /** * @return The brain into which files is loaded. */ Brain* EventSpecFileReadDataFiles::getLoadIntoBrain() { return this->loadIntoBrain; } /** * @return The username. */ AString EventSpecFileReadDataFiles::getUsername() const { return this->username; } /** * @return The password. */ AString EventSpecFileReadDataFiles::getPassword() const { return this->password; } /** * Set the username and password. * * @param username * Name of user account. * @param password * Password of user account. */ void EventSpecFileReadDataFiles::setUsernameAndPassword(const AString& username, const AString& password) { this->username = username; this->password = password; } connectome-workbench-1.2.3+git41-gc4c6c90/src/Brain/EventSpecFileReadDataFiles.h000066400000000000000000000041001300200146000270100ustar00rootroot00000000000000#ifndef __EVENT_SPEC_FILE_READ_DATA_FILES_H__ #define __EVENT_SPEC_FILE_READ_DATA_FILES_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 "DataFileTypeEnum.h" #include "Event.h" namespace caret { class Brain; class SpecFile; /** * Event for reading data files selected in a spec file. */ class EventSpecFileReadDataFiles : public Event { public: EventSpecFileReadDataFiles(Brain* loadIntoBrain, SpecFile* specFile); virtual ~EventSpecFileReadDataFiles(); SpecFile* getSpecFile(); Brain* getLoadIntoBrain(); AString getUsername() const; AString getPassword() const; void setUsernameAndPassword(const AString& username, const AString& password); private: EventSpecFileReadDataFiles(const EventSpecFileReadDataFiles&); EventSpecFileReadDataFiles& operator=(const EventSpecFileReadDataFiles&); Brain* loadIntoBrain; SpecFile* specFile; AString username; AString password; bool errorInvalidStructure; }; } // namespace #endif // __EVENT_SPEC_FILE_READ_DATA_FILES_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Brain/EventSurfacesGet.cxx000066400000000000000000000064141300200146000255450ustar00rootroot00000000000000/*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 "CaretAssert.h" #include "EventSurfacesGet.h" #include "Surface.h" using namespace caret; /** * Construct an event for getting surfaces from * all structures and surface types. Methods * are provided for constraining the surfaces * to those with specified structures and/or * surface types. */ EventSurfacesGet::EventSurfacesGet() : Event(EventTypeEnum::EVENT_SURFACES_GET) { } /** * Destructore. */ EventSurfacesGet::~EventSurfacesGet() { } /** * Add a surface. If the surface does not * meet any surface/structure constraints, * it will not be added. * * @param surface * Surface that is added. */ void EventSurfacesGet::addSurface(Surface* surface) { if (this->structureConstraints.empty() == false) { const StructureEnum::Enum structure = surface->getStructure(); if (std::find(this->structureConstraints.begin(), this->structureConstraints.end(), structure) == this->structureConstraints.end()) { return; } } if (this->surfaceTypeConstraints.empty() == false) { const SurfaceTypeEnum::Enum surfaceType = surface->getSurfaceType(); if (std::find(this->surfaceTypeConstraints.begin(), this->surfaceTypeConstraints.end(), surfaceType) == this->surfaceTypeConstraints.end()) { return; } } this->surfaces.push_back(surface); } /** * Add a structure contraints. If structure constraints * are added, only surface of the specified structures * are obtained. May be called more than once for * constraining to multiple surface types. * * @param structure * Structure for constraining selection. */ void EventSurfacesGet::addStructureConstraint(const StructureEnum::Enum structure) { this->structureConstraints.push_back(structure); } /** * Add a surface type contraints. If surface type constraints * are added, only surfaces of the specified surface types * are obtained. May be called more than once for constraining * to multiple surface types. * * @param surfaceType * Surface type for constraining selection. */ void EventSurfacesGet::addSurfaceTypeConstraint(const SurfaceTypeEnum::Enum surfaceType) { this->surfaceTypeConstraints.push_back(surfaceType); } /** * @return Surfaces that were obtained. */ std::vector EventSurfacesGet::getSurfaces() const { return this->surfaces; } connectome-workbench-1.2.3+git41-gc4c6c90/src/Brain/EventSurfacesGet.h000066400000000000000000000035251300200146000251720ustar00rootroot00000000000000#ifndef __EVENT_SURFACES_GET_H__ #define __EVENT_SURFACES_GET_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" #include "StructureEnum.h" #include "SurfaceTypeEnum.h" namespace caret { class Surface; /// Get surfaces class EventSurfacesGet : public Event { public: EventSurfacesGet(); virtual ~EventSurfacesGet(); void addSurface(Surface* surface); void addStructureConstraint(const StructureEnum::Enum structure); void addSurfaceTypeConstraint(const SurfaceTypeEnum::Enum surfaceType); std::vector getSurfaces() const; private: EventSurfacesGet(const EventSurfacesGet&); EventSurfacesGet& operator=(const EventSurfacesGet&); std::vector surfaces; std::vector structureConstraints; std::vector surfaceTypeConstraints; }; } // namespace #endif // __EVENT_SURFACES_GET_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Brain/FeatureColoringTypeEnum.cxx000066400000000000000000000234071300200146000271100ustar00rootroot00000000000000 /*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 __FEATURE_COLORING_TYPE_ENUM_DECLARE__ #include "FeatureColoringTypeEnum.h" #undef __FEATURE_COLORING_TYPE_ENUM_DECLARE__ #include "CaretAssert.h" using namespace caret; /** * \class caret::FeatureColoringTypeEnum * \brief Coloring type for features. * \ingroup GuiQt */ /** * Constructor. * * @param enumValue * An enumerated value. * @param name * Name of enumerated value. * * @param guiName * User-friendly name for use in user-interface. */ FeatureColoringTypeEnum::FeatureColoringTypeEnum(const Enum enumValue, const AString& name, const AString& guiName) { this->enumValue = enumValue; this->integerCode = integerCodeCounter++; this->name = name; this->guiName = guiName; } /** * Destructor. */ FeatureColoringTypeEnum::~FeatureColoringTypeEnum() { } /** * Initialize the enumerated metadata. */ void FeatureColoringTypeEnum::initialize() { if (initializedFlag) { return; } initializedFlag = true; enumData.push_back(FeatureColoringTypeEnum(FEATURE_COLORING_TYPE_CLASS, "FEATURE_COLORING_TYPE_CLASS", "Class")); enumData.push_back(FeatureColoringTypeEnum(FEATURE_COLORING_TYPE_NAME, "FEATURE_COLORING_TYPE_NAME", "Name")); enumData.push_back(FeatureColoringTypeEnum(FEATURE_COLORING_TYPE_STANDARD_COLOR, "FEATURE_COLORING_TYPE_STANDARD_COLOR", "Standard Color")); } /** * 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 FeatureColoringTypeEnum* FeatureColoringTypeEnum::findData(const Enum enumValue) { if (initializedFlag == false) initialize(); size_t num = enumData.size(); for (size_t i = 0; i < num; i++) { const FeatureColoringTypeEnum* 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 FeatureColoringTypeEnum::toName(Enum enumValue) { if (initializedFlag == false) initialize(); const FeatureColoringTypeEnum* 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. */ FeatureColoringTypeEnum::Enum FeatureColoringTypeEnum::fromName(const AString& nameIn, bool* isValidOut) { AString name = nameIn; /* * Translate foci coloring type that was replaced */ if (name == "FOCI_COLORING_TYPE_CLASS") { name = "FEATURE_COLORING_TYPE_CLASS"; } else if (name == "FOCI_COLORING_TYPE_NAME") { name = "FEATURE_COLORING_TYPE_NAME"; } if (initializedFlag == false) initialize(); bool validFlag = false; Enum enumValue = FEATURE_COLORING_TYPE_NAME; for (std::vector::iterator iter = enumData.begin(); iter != enumData.end(); iter++) { const FeatureColoringTypeEnum& 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 FeatureColoringTypeEnum")); } return enumValue; } /** * Get a GUI string representation of the enumerated type. * @param enumValue * Enumerated value. * @return * String representing enumerated value. */ AString FeatureColoringTypeEnum::toGuiName(Enum enumValue) { if (initializedFlag == false) initialize(); const FeatureColoringTypeEnum* 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. */ FeatureColoringTypeEnum::Enum FeatureColoringTypeEnum::fromGuiName(const AString& guiName, bool* isValidOut) { if (initializedFlag == false) initialize(); bool validFlag = false; Enum enumValue = FEATURE_COLORING_TYPE_NAME; for (std::vector::iterator iter = enumData.begin(); iter != enumData.end(); iter++) { const FeatureColoringTypeEnum& 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 FeatureColoringTypeEnum")); } return enumValue; } /** * Get the integer code for a data type. * * @return * Integer code for data type. */ int32_t FeatureColoringTypeEnum::toIntegerCode(Enum enumValue) { if (initializedFlag == false) initialize(); const FeatureColoringTypeEnum* 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. */ FeatureColoringTypeEnum::Enum FeatureColoringTypeEnum::fromIntegerCode(const int32_t integerCode, bool* isValidOut) { if (initializedFlag == false) initialize(); bool validFlag = false; Enum enumValue = FEATURE_COLORING_TYPE_NAME; for (std::vector::iterator iter = enumData.begin(); iter != enumData.end(); iter++) { const FeatureColoringTypeEnum& 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 FeatureColoringTypeEnum")); } 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 FeatureColoringTypeEnum::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 FeatureColoringTypeEnum::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(FeatureColoringTypeEnum::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 FeatureColoringTypeEnum::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(FeatureColoringTypeEnum::toGuiName(iter->enumValue)); } if (isSorted) { std::sort(allGuiNames.begin(), allGuiNames.end()); } } connectome-workbench-1.2.3+git41-gc4c6c90/src/Brain/FeatureColoringTypeEnum.h000066400000000000000000000064051300200146000265340ustar00rootroot00000000000000#ifndef __FEATURE_COLORING_TYPE_ENUM__H_ #define __FEATURE_COLORING_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 FeatureColoringTypeEnum { public: /** * Enumerated values. */ enum Enum { /** Use class for coloring */ FEATURE_COLORING_TYPE_CLASS, /** Use name for coloring */ FEATURE_COLORING_TYPE_NAME, /** Use color name for standard color */ FEATURE_COLORING_TYPE_STANDARD_COLOR }; ~FeatureColoringTypeEnum(); 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: FeatureColoringTypeEnum(const Enum enumValue, const AString& name, const AString& guiName); static const FeatureColoringTypeEnum* 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 __FEATURE_COLORING_TYPE_ENUM_DECLARE__ std::vector FeatureColoringTypeEnum::enumData; bool FeatureColoringTypeEnum::initializedFlag = false; int32_t FeatureColoringTypeEnum::integerCodeCounter = 0; #endif // __FEATURE_COLORING_TYPE_ENUM_DECLARE__ } // namespace #endif //__FEATURE_COLORING_TYPE_ENUM__H_ connectome-workbench-1.2.3+git41-gc4c6c90/src/Brain/FiberOrientationSamplesLoader.cxx000066400000000000000000000346211300200146000302500ustar00rootroot00000000000000 /*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 __FIBER_ORIENTATION_SAMPLES_LOADER_DECLARE__ #include "FiberOrientationSamplesLoader.h" #undef __FIBER_ORIENTATION_SAMPLES_LOADER_DECLARE__ #include "Brain.h" #include "CaretAssert.h" #include "CiftiFiberOrientationFile.h" #include "DataFileException.h" #include "FiberOrientation.h" #include "FileInformation.h" #include "EventIdentificationHighlightLocation.h" #include "EventManager.h" #include "SpecFile.h" #include "VolumeFile.h" using namespace caret; /** * \class caret::FiberOrientationSamplesLoader * \brief Loads Fiber Orientation samples display on a sphere in features toolbox * \ingroup Brain */ /** * Constructor. */ FiberOrientationSamplesLoader::FiberOrientationSamplesLoader() : CaretObject() { m_sampleVolumesLoadAttemptValid = false; m_sampleVolumesValid = false; for (int32_t i = 0; i < 3; i++) { m_sampleMagnitudeVolumes[i] = NULL; m_sampleThetaVolumes[i] = NULL; m_samplePhiVolumes[i] = NULL; } m_lastIdentificationValid = false; EventManager::get()->addEventListener(this, EventTypeEnum::EVENT_IDENTIFICATION_HIGHLIGHT_LOCATION); } /** * Destructor. */ FiberOrientationSamplesLoader::~FiberOrientationSamplesLoader() { reset(); EventManager::get()->removeAllEventsFromListener(this); } /** * Reset all settings to their defaults * and remove any data. */ void FiberOrientationSamplesLoader::reset() { m_sampleVolumesLoadAttemptValid = false; m_sampleVolumesValid = false; for (int32_t i = 0; i < 3; i++) { if (m_sampleMagnitudeVolumes[i] != NULL) { delete m_sampleMagnitudeVolumes[i]; m_sampleMagnitudeVolumes[i] = NULL; } if (m_sampleThetaVolumes[i] != NULL) { delete m_sampleThetaVolumes[i]; m_sampleThetaVolumes[i] = NULL; } if (m_samplePhiVolumes[i] != NULL) { delete m_samplePhiVolumes[i]; m_samplePhiVolumes[i] = NULL; } } m_lastIdentificationValid = false; } /** * Receive events from the event manager. * * @param event * Event sent by event manager. */ void FiberOrientationSamplesLoader::receiveEvent(Event* event) { if (event->getEventType() == EventTypeEnum::EVENT_IDENTIFICATION_HIGHLIGHT_LOCATION) { EventIdentificationHighlightLocation* idEvent = dynamic_cast(event); CaretAssert(idEvent); switch (idEvent->getLoadFiberOrientationSamplesMode()) { case EventIdentificationHighlightLocation::LOAD_FIBER_ORIENTATION_SAMPLES_MODE_NO: break; case EventIdentificationHighlightLocation::LOAD_FIBER_ORIENTATION_SAMPLES_MODE_YES: { const float* xyz = idEvent->getXYZ(); if (xyz != NULL) { m_lastIdentificationValid = true; m_lastIdentificationXYZ[0] = xyz[0]; m_lastIdentificationXYZ[1] = xyz[1]; m_lastIdentificationXYZ[2] = xyz[2]; } } break; } } } /** * Get the fiber orientation vectors for display on a sphere. * * @param brain * Brain for which vectors are loaded. * @param xVectors * Vectors for X-orientation. * @param yVectors * Vectors for Y-orientation. * @param zVectors * Vectors for Z-orientation. * @param fiberOrientation * The nearby fiber orientation * @param errorMessageOut * Will contain any error messages. * This error message will only be set in some cases when there is an * error. * @return * True if data is valid, else false. */ bool FiberOrientationSamplesLoader::getFiberOrientationSphericalSamplesVectors(Brain* brain, std::vector& xVectors, std::vector& yVectors, std::vector& zVectors, FiberOrientation* &fiberOrientationOut, AString& errorMessageOut) { CaretAssert(brain); errorMessageOut = ""; fiberOrientationOut = NULL; if (m_lastIdentificationValid) { if (loadSphericalOrientationVolumes(brain, errorMessageOut) == false) { return false; } if (brain->getNumberOfConnectivityFiberOrientationFiles() > 0) { CiftiFiberOrientationFile* cfof = brain->getConnectivityFiberOrientationFile(0); FiberOrientation* nearestFiberOrientation = cfof->getFiberOrientationNearestCoordinate(m_lastIdentificationXYZ, 3.0); if (nearestFiberOrientation != NULL) { fiberOrientationOut = nearestFiberOrientation; } } int64_t ijk[3]; m_sampleThetaVolumes[0]->enclosingVoxel(m_lastIdentificationXYZ, ijk); if (m_sampleThetaVolumes[0]->indexValid(ijk)) { std::vector dims; m_sampleThetaVolumes[0]->getDimensions(dims); const int64_t numberOfOrientations = dims[3]; xVectors.resize(numberOfOrientations); yVectors.resize(numberOfOrientations); zVectors.resize(numberOfOrientations); for (int32_t iAxis = 0; iAxis < 3; iAxis++) { for (int64_t iOrient = 0; iOrient < numberOfOrientations; iOrient++) { const float theta = m_sampleThetaVolumes[iAxis]->getValue(ijk[0], ijk[1], ijk[2], iOrient, 0); const float phi = m_samplePhiVolumes[iAxis]->getValue(ijk[0], ijk[1], ijk[2], iOrient, 0); const float magnitude = m_sampleMagnitudeVolumes[iAxis]->getValue(ijk[0], ijk[1], ijk[2], iOrient, 0); switch (iAxis) { case 0: { FiberOrientationSamplesVector& ov = xVectors[iOrient]; ov.direction[0] = -std::sin(theta) * std::cos(phi); ov.direction[1] = std::sin(theta) * std::sin(phi); ov.direction[2] = std::cos(theta); ov.magnitude = magnitude; ov.setColor(); } break; case 1: { FiberOrientationSamplesVector& ov = yVectors[iOrient]; ov.direction[0] = -std::sin(theta) * std::cos(phi); ov.direction[1] = std::sin(theta) * std::sin(phi); ov.direction[2] = std::cos(theta); ov.magnitude = magnitude; ov.setColor(); } break; case 2: { FiberOrientationSamplesVector& ov = zVectors[iOrient]; ov.direction[0] = -std::sin(theta) * std::cos(phi); ov.direction[1] = std::sin(theta) * std::sin(phi); ov.direction[2] = std::cos(theta); ov.magnitude = magnitude; ov.setColor(); } break; } } } return true; } } return false; } /** * Get the volumes containing the spherical orienations. * * @param brain * Brain for which vectors are loaded. * @param magnitudeVolumesOut * The volumes containing the magnitudes. * @param phiAngleVolumesOut * The volumes containing the phi angles. * @param thetaAngleVolumesOut * The volumes containing the theta angles. * */ bool FiberOrientationSamplesLoader::loadSphericalOrientationVolumes(Brain* brain, AString& errorMessageOut) { errorMessageOut = ""; FileInformation specFileInfo(brain->getSpecFile()->getFileName()); const AString directoryName = specFileInfo.getPathName(); if (m_sampleVolumesValid == false) { if (m_sampleVolumesLoadAttemptValid == false) { const AString filePrefix = "merged_"; const AString fileSuffix = "samples.nii.gz"; std::vector allVolumes; for (int32_t i = 0; i < 3; i++) { m_sampleMagnitudeVolumes[i] = new VolumeFile(); m_samplePhiVolumes[i] = new VolumeFile(); m_sampleThetaVolumes[i] = new VolumeFile(); const AString fileNumber = AString::number(i + 1); try { const AString magFileName = (filePrefix + "f" + fileNumber + fileSuffix); FileInformation magFileInfo(directoryName, magFileName); const AString magFilePath = magFileInfo.getAbsoluteFilePath(); m_sampleMagnitudeVolumes[i]->readFile(magFilePath); allVolumes.push_back(m_sampleMagnitudeVolumes[i]); } catch (const DataFileException& dfe) { if (errorMessageOut.isEmpty() == false) { errorMessageOut += "\n"; } errorMessageOut += dfe.whatString(); } try { const AString phiFileName = (filePrefix + "ph" + fileNumber + fileSuffix); FileInformation phiFileInfo(directoryName, phiFileName); const AString phiFilePath = phiFileInfo.getAbsoluteFilePath(); m_samplePhiVolumes[i]->readFile(phiFilePath); allVolumes.push_back(m_samplePhiVolumes[i]); } catch (const DataFileException& dfe) { if (errorMessageOut.isEmpty() == false) { errorMessageOut += "\n"; } errorMessageOut += dfe.whatString(); } try { const AString thetaFileName = (filePrefix + "th" + fileNumber + fileSuffix); FileInformation thetaFileInfo(directoryName, thetaFileName); const AString thetaFilePath = thetaFileInfo.getAbsoluteFilePath(); m_sampleThetaVolumes[i]->readFile(thetaFilePath); allVolumes.push_back(m_sampleThetaVolumes[i]); } catch (const DataFileException& dfe) { if (errorMessageOut.isEmpty() == false) { errorMessageOut += "\n"; } errorMessageOut += dfe.whatString(); } } if (errorMessageOut.isEmpty()) { std::vector dims; for (std::vector::iterator iter = allVolumes.begin(); iter != allVolumes.end(); iter++) { VolumeFile* vf = *iter; std::vector volDims; vf->getDimensions(volDims); if (dims.empty()) { dims = volDims; } else if (dims != volDims) { errorMessageOut += "ERROR: Sample volumes have mis-matched dimensions"; } } m_sampleVolumesValid = true; } m_sampleVolumesLoadAttemptValid = true; } } if (m_sampleVolumesValid) { return true; } return false; } connectome-workbench-1.2.3+git41-gc4c6c90/src/Brain/FiberOrientationSamplesLoader.h000066400000000000000000000062451300200146000276760ustar00rootroot00000000000000#ifndef __FIBER_ORIENTATION_SAMPLES_LOADER_H__ #define __FIBER_ORIENTATION_SAMPLES_LOADER_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 "EventListenerInterface.h" #include "FiberOrientationSamplesVector.h" namespace caret { class Brain; class FiberOrientation; class VolumeFile; class FiberOrientationSamplesLoader : public CaretObject, public EventListenerInterface { public: FiberOrientationSamplesLoader(); virtual ~FiberOrientationSamplesLoader(); bool getFiberOrientationSphericalSamplesVectors(Brain* brain, std::vector& xVectors, std::vector& yVectors, std::vector& zVectors, FiberOrientation* &fiberOrientationOut, AString& errorMessageOut); void reset(); void receiveEvent(Event* event); private: FiberOrientationSamplesLoader(const FiberOrientationSamplesLoader&); FiberOrientationSamplesLoader& operator=(const FiberOrientationSamplesLoader&); bool loadSphericalOrientationVolumes(Brain* brain, AString& errorMessageOut); /** Tried to load sample volumes since last reset (they may or may not be valid) */ bool m_sampleVolumesLoadAttemptValid; /** Sample volumes were loaded and are valid */ bool m_sampleVolumesValid; /* sample magnitude volumes */ VolumeFile* m_sampleMagnitudeVolumes[3]; /* sample theta angle volumes */ VolumeFile* m_sampleThetaVolumes[3]; /* sample phi angle volumes */ VolumeFile* m_samplePhiVolumes[3]; /* last identified location */ float m_lastIdentificationXYZ[3]; /* last identification valid */ bool m_lastIdentificationValid; }; #ifdef __FIBER_ORIENTATION_SAMPLES_LOADER_DECLARE__ // #endif // __FIBER_ORIENTATION_SAMPLES_LOADER_DECLARE__ } // namespace #endif //__FIBER_ORIENTATION_SAMPLES_LOADER_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Brain/FiberOrientationSamplesVector.h000066400000000000000000000036651300200146000277350ustar00rootroot00000000000000#ifndef __FIBER_ORIENTATION_SAMPLES_VECTOR_H__ #define __FIBER_ORIENTATION_SAMPLES_VECTOR_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*/ namespace caret { /** * \struct caret::FiberOrientationSamplesVector * \brief Stores a fiber orientation samples vector * \ingroup Brain */ struct FiberOrientationSamplesVector { /** The direction vector */ float direction[3]; /** The magnitude (length) */ float magnitude; /** RGB coloring for vector */ float rgb[3]; /** * Set the RGB color to absolute values of the directional vector */ void setColor() { rgb[0] = (direction[0] >= 0.0) ? direction[0] : -direction[0]; rgb[1] = (direction[1] >= 0.0) ? direction[1] : -direction[1]; rgb[2] = (direction[2] >= 0.0) ? direction[2] : -direction[2]; } }; #ifdef __FIBER_ORIENTATION_SAMPLES_VECTOR_DECLARE__ // #endif // __FIBER_ORIENTATION_SAMPLES_VECTOR_DECLARE__ } // namespace #endif //__FIBER_ORIENTATION_SAMPLES_VECTOR_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Brain/FiberOrientationSymbolTypeEnum.cxx000066400000000000000000000227371300200146000304560ustar00rootroot00000000000000 /*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 __FIBER_ORIENTATION_SYMBOL_TYPE_ENUM_DECLARE__ #include "FiberOrientationSymbolTypeEnum.h" #undef __FIBER_ORIENTATION_SYMBOL_TYPE_ENUM_DECLARE__ #include "CaretAssert.h" using namespace caret; /** * \class caret::FiberOrientationSymbolTypeEnum * \brief * * */ /** * Constructor. * * @param enumValue * An enumerated value. * @param name * Name of enumerated value. * * @param guiName * User-friendly name for use in user-interface. */ FiberOrientationSymbolTypeEnum::FiberOrientationSymbolTypeEnum(const Enum enumValue, const AString& name, const AString& guiName) { this->enumValue = enumValue; this->integerCode = integerCodeCounter++; this->name = name; this->guiName = guiName; } /** * Destructor. */ FiberOrientationSymbolTypeEnum::~FiberOrientationSymbolTypeEnum() { } /** * Initialize the enumerated metadata. */ void FiberOrientationSymbolTypeEnum::initialize() { if (initializedFlag) { return; } initializedFlag = true; enumData.push_back(FiberOrientationSymbolTypeEnum(FIBER_SYMBOL_FANS, "FIBER_SYMBOL_FANS", "Fans")); enumData.push_back(FiberOrientationSymbolTypeEnum(FIBER_SYMBOL_LINES, "FIBER_SYMBOL_LINES", "Lines")); } /** * 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 FiberOrientationSymbolTypeEnum* FiberOrientationSymbolTypeEnum::findData(const Enum enumValue) { if (initializedFlag == false) initialize(); size_t num = enumData.size(); for (size_t i = 0; i < num; i++) { const FiberOrientationSymbolTypeEnum* 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 FiberOrientationSymbolTypeEnum::toName(Enum enumValue) { if (initializedFlag == false) initialize(); const FiberOrientationSymbolTypeEnum* 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. */ FiberOrientationSymbolTypeEnum::Enum FiberOrientationSymbolTypeEnum::fromName(const AString& name, bool* isValidOut) { if (initializedFlag == false) initialize(); bool validFlag = false; Enum enumValue = FIBER_SYMBOL_LINES; for (std::vector::iterator iter = enumData.begin(); iter != enumData.end(); iter++) { const FiberOrientationSymbolTypeEnum& 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 FiberOrientationSymbolTypeEnum")); } return enumValue; } /** * Get a GUI string representation of the enumerated type. * @param enumValue * Enumerated value. * @return * String representing enumerated value. */ AString FiberOrientationSymbolTypeEnum::toGuiName(Enum enumValue) { if (initializedFlag == false) initialize(); const FiberOrientationSymbolTypeEnum* 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. */ FiberOrientationSymbolTypeEnum::Enum FiberOrientationSymbolTypeEnum::fromGuiName(const AString& guiName, bool* isValidOut) { if (initializedFlag == false) initialize(); bool validFlag = false; Enum enumValue = FIBER_SYMBOL_LINES; for (std::vector::iterator iter = enumData.begin(); iter != enumData.end(); iter++) { const FiberOrientationSymbolTypeEnum& 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 FiberOrientationSymbolTypeEnum")); } return enumValue; } /** * Get the integer code for a data type. * * @return * Integer code for data type. */ int32_t FiberOrientationSymbolTypeEnum::toIntegerCode(Enum enumValue) { if (initializedFlag == false) initialize(); const FiberOrientationSymbolTypeEnum* 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. */ FiberOrientationSymbolTypeEnum::Enum FiberOrientationSymbolTypeEnum::fromIntegerCode(const int32_t integerCode, bool* isValidOut) { if (initializedFlag == false) initialize(); bool validFlag = false; Enum enumValue = FIBER_SYMBOL_LINES; for (std::vector::iterator iter = enumData.begin(); iter != enumData.end(); iter++) { const FiberOrientationSymbolTypeEnum& 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 FiberOrientationSymbolTypeEnum")); } 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 FiberOrientationSymbolTypeEnum::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 FiberOrientationSymbolTypeEnum::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(FiberOrientationSymbolTypeEnum::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 FiberOrientationSymbolTypeEnum::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(FiberOrientationSymbolTypeEnum::toGuiName(iter->enumValue)); } if (isSorted) { std::sort(allGuiNames.begin(), allGuiNames.end()); } } connectome-workbench-1.2.3+git41-gc4c6c90/src/Brain/FiberOrientationSymbolTypeEnum.h000066400000000000000000000064101300200146000300710ustar00rootroot00000000000000#ifndef __FIBER_ORIENTATION_SYMBOL_TYPE_ENUM__H_ #define __FIBER_ORIENTATION_SYMBOL_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 FiberOrientationSymbolTypeEnum { public: /** * Enumerated values. */ enum Enum { /** Use Fans for drawing fibers */ FIBER_SYMBOL_FANS, /** Use Lines for drawing fibers */ FIBER_SYMBOL_LINES }; ~FiberOrientationSymbolTypeEnum(); 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: FiberOrientationSymbolTypeEnum(const Enum enumValue, const AString& name, const AString& guiName); static const FiberOrientationSymbolTypeEnum* 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 __FIBER_ORIENTATION_SYMBOL_TYPE_ENUM_DECLARE__ std::vector FiberOrientationSymbolTypeEnum::enumData; bool FiberOrientationSymbolTypeEnum::initializedFlag = false; int32_t FiberOrientationSymbolTypeEnum::integerCodeCounter = 0; #endif // __FIBER_ORIENTATION_SYMBOL_TYPE_ENUM_DECLARE__ } // namespace #endif //__FIBER_ORIENTATION_SYMBOL_TYPE_ENUM__H_ connectome-workbench-1.2.3+git41-gc4c6c90/src/Brain/FociDrawingTypeEnum.cxx000066400000000000000000000217541300200146000262170ustar00rootroot00000000000000 /*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 __FOCI_DRAWING_TYPE_ENUM_DECLARE__ #include "FociDrawingTypeEnum.h" #undef __FOCI_DRAWING_TYPE_ENUM_DECLARE__ #include "CaretAssert.h" using namespace caret; /** * \class caret::FociDrawingTypeEnum * \brief * * */ /** * Constructor. * * @param enumValue * An enumerated value. * @param name * Name of enumerated value. * * @param guiName * User-friendly name for use in user-interface. */ FociDrawingTypeEnum::FociDrawingTypeEnum(const Enum enumValue, const AString& name, const AString& guiName) { this->enumValue = enumValue; this->integerCode = integerCodeCounter++; this->name = name; this->guiName = guiName; } /** * Destructor. */ FociDrawingTypeEnum::~FociDrawingTypeEnum() { } /** * Initialize the enumerated metadata. */ void FociDrawingTypeEnum::initialize() { if (initializedFlag) { return; } initializedFlag = true; enumData.push_back(FociDrawingTypeEnum(DRAW_AS_SPHERES, "DRAW_AS_SPHERES", "Spheres")); enumData.push_back(FociDrawingTypeEnum(DRAW_AS_SQUARES, "DRAW_AS_SQUARES", "Squares")); } /** * 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 FociDrawingTypeEnum* FociDrawingTypeEnum::findData(const Enum enumValue) { if (initializedFlag == false) initialize(); size_t num = enumData.size(); for (size_t i = 0; i < num; i++) { const FociDrawingTypeEnum* 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 FociDrawingTypeEnum::toName(Enum enumValue) { if (initializedFlag == false) initialize(); const FociDrawingTypeEnum* 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. */ FociDrawingTypeEnum::Enum FociDrawingTypeEnum::fromName(const AString& name, bool* isValidOut) { if (initializedFlag == false) initialize(); bool validFlag = false; Enum enumValue = DRAW_AS_SPHERES; for (std::vector::iterator iter = enumData.begin(); iter != enumData.end(); iter++) { const FociDrawingTypeEnum& 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 FociDrawingTypeEnum")); } return enumValue; } /** * Get a GUI string representation of the enumerated type. * @param enumValue * Enumerated value. * @return * String representing enumerated value. */ AString FociDrawingTypeEnum::toGuiName(Enum enumValue) { if (initializedFlag == false) initialize(); const FociDrawingTypeEnum* 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. */ FociDrawingTypeEnum::Enum FociDrawingTypeEnum::fromGuiName(const AString& guiName, bool* isValidOut) { if (initializedFlag == false) initialize(); bool validFlag = false; Enum enumValue = DRAW_AS_SPHERES; for (std::vector::iterator iter = enumData.begin(); iter != enumData.end(); iter++) { const FociDrawingTypeEnum& 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 FociDrawingTypeEnum")); } return enumValue; } /** * Get the integer code for a data type. * * @return * Integer code for data type. */ int32_t FociDrawingTypeEnum::toIntegerCode(Enum enumValue) { if (initializedFlag == false) initialize(); const FociDrawingTypeEnum* 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. */ FociDrawingTypeEnum::Enum FociDrawingTypeEnum::fromIntegerCode(const int32_t integerCode, bool* isValidOut) { if (initializedFlag == false) initialize(); bool validFlag = false; Enum enumValue = DRAW_AS_SPHERES; for (std::vector::iterator iter = enumData.begin(); iter != enumData.end(); iter++) { const FociDrawingTypeEnum& 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 FociDrawingTypeEnum")); } 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 FociDrawingTypeEnum::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 FociDrawingTypeEnum::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(FociDrawingTypeEnum::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 FociDrawingTypeEnum::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(FociDrawingTypeEnum::toGuiName(iter->enumValue)); } if (isSorted) { std::sort(allGuiNames.begin(), allGuiNames.end()); } } connectome-workbench-1.2.3+git41-gc4c6c90/src/Brain/FociDrawingTypeEnum.h000066400000000000000000000061401300200146000256340ustar00rootroot00000000000000#ifndef __FOCI_DRAWING_TYPE_ENUM__H_ #define __FOCI_DRAWING_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 FociDrawingTypeEnum { public: /** * Enumerated values for foci drawing type. */ enum Enum { /** Draw as spheres */ DRAW_AS_SPHERES, /** Draw as squares*/ DRAW_AS_SQUARES }; ~FociDrawingTypeEnum(); 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: FociDrawingTypeEnum(const Enum enumValue, const AString& name, const AString& guiName); static const FociDrawingTypeEnum* 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 __FOCI_DRAWING_TYPE_ENUM_DECLARE__ std::vector FociDrawingTypeEnum::enumData; bool FociDrawingTypeEnum::initializedFlag = false; int32_t FociDrawingTypeEnum::integerCodeCounter = 0; #endif // __FOCI_DRAWING_TYPE_ENUM_DECLARE__ } // namespace #endif //__FOCI_DRAWING_TYPE_ENUM__H_ connectome-workbench-1.2.3+git41-gc4c6c90/src/Brain/FtglFontTextRenderer.cxx000066400000000000000000002103161300200146000264050ustar00rootroot00000000000000 /*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*/ /** * Qt licence is included since some of its GLWidget API is copied. */ /**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtOpenGL 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 Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/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 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #define __FTGL_FONT_TEXT_RENDERER_DECLARE__ #include "FtglFontTextRenderer.h" #undef __FTGL_FONT_TEXT_RENDERER_DECLARE__ #include #include #include #include #include #include "AnnotationPointSizeText.h" #include "BrainOpenGLPrimitiveDrawing.h" #include "CaretAssert.h" #include "CaretLogger.h" #include "CaretOpenGLInclude.h" #include "MathFunctions.h" #include "Matrix4x4.h" #ifdef HAVE_FREETYPE #include using namespace FTGL; #endif // HAVE_FREETYPE using namespace caret; static const bool debugPrintFlag = false; static const bool drawCrosshairsAtFontStartingCoordinate = false; /** * \class caret::FtglFontTextRenderer * \brief Text rendering using FTGL * \ingroup Brain * * Draws text using the FTGL library. * * FTGL's Texture font is used for drawing text. * The texture font is positioned using the current OpenGL * model view matrix which allows rotation and scaling. * * There are other font types such as Pixmap font. The * pixmap font was used, but it cannot be rotated nor * scaled. In addition, the pixmmap font drawing * requires a raster position and if the raster position * is slightly outside the viewport all text is clipped. */ /** * Constructor. */ FtglFontTextRenderer::FtglFontTextRenderer() : BrainOpenGLTextRenderInterface() { m_defaultFont = NULL; #ifdef HAVE_FREETYPE AnnotationPointSizeText defaultAnnotationText(AnnotationAttributesDefaultTypeEnum::NORMAL); defaultAnnotationText.setFontPointSize(AnnotationTextFontPointSizeEnum::SIZE14); defaultAnnotationText.setFont(AnnotationTextFontNameEnum::VERA); defaultAnnotationText.setItalicStyleEnabled(false); defaultAnnotationText.setBoldStyleEnabled(false); defaultAnnotationText.setUnderlineStyleEnabled(false); m_defaultFont = getFont(defaultAnnotationText, true); #endif // HAVE_FREETYPE m_depthTestingStatus = DEPTH_TEST_NO; } /** * Destructor. */ FtglFontTextRenderer::~FtglFontTextRenderer() { #ifdef HAVE_FREETYPE for (FONT_MAP_ITERATOR iter = m_fontNameToFontMap.begin(); iter != m_fontNameToFontMap.end(); iter++) { delete iter->second; } m_fontNameToFontMap.clear(); /* * Do not delete "m_defaultFont" since it points to a font * in m_fontNameToFontMap. Doing so would cause * a double delete. */ #endif // HAVE_FREETYPE } /** * @return The font system is valid. */ bool FtglFontTextRenderer::isValid() const { return (m_defaultFont != NULL); } /* * Get the font with the given font attributes. * If the font is not created, return the default font. * * @param annotationText * Annotation Text that is to be drawn. * @param creatingDefaultFontFlag * True if creating the default font. * @return * The FTGL font. If there are errors this value will * be NULL. */ FTFont* FtglFontTextRenderer::getFont(const AnnotationText& annotationText, const bool creatingDefaultFontFlag) { #ifdef HAVE_FREETYPE const AString fontName = annotationText.getFontRenderingEncodedName(m_viewportHeight); /* * Has the font already has been created? */ FONT_MAP_ITERATOR fontIter = m_fontNameToFontMap.find(fontName); if (fontIter != m_fontNameToFontMap.end()) { FontData* fontData = fontIter->second; CaretAssert(fontData); return fontData->m_font; } /* * Create and save the font */ FontData* fontData = new FontData(annotationText, m_viewportHeight); if (fontData->m_valid) { /* * Request font is valid. */ m_fontNameToFontMap.insert(std::make_pair(fontName, fontData)); CaretLogFine("Created font with encoded name " + fontName); return fontData->m_font; } else { /* * Error creating font */ delete fontData; fontData = NULL; /* * Issue a message about failure to create font but * don't print same message more than once. */ if (std::find(m_failedFontNames.begin(), m_failedFontNames.end(), fontName) == m_failedFontNames.end()) { m_failedFontNames.insert(fontName); CaretLogSevere("Failed to create font with encoded name " + fontName); } } /* * Were we trying to create the default font? */ if (creatingDefaultFontFlag) { return NULL; } /* * Failed so use the default font. */ return m_defaultFont; #else // HAVE_FREETYPE CaretLogSevere("Trying to use FTGL Font rendering but FTGL is not valid."); return NULL; #endif // HAVE_FREETYPE } /** * Draw the text piceces at their assigned viewport coordinates. * * @param annotationText * Annotation text and attributes. * @param textMatrix * Text broken up into cells with viewport coordinates assigned. * */ void FtglFontTextRenderer::drawTextAtViewportCoordinatesInternal(const AnnotationText& annotationText, const TextStringGroup& textStringGroup) { #ifdef HAVE_FREETYPE FTFont* font = getFont(annotationText, false); if (! font) { return; } if (annotationText.getText().isEmpty()) { return; } saveStateOfOpenGL(); BrainOpenGL::testForOpenGLError("At beginning of " "FtglFontTextRenderer::drawTextAtViewportCoordinatesInternal " "while drawing text: " + annotationText.getText()); /* * Depth testing ? */ switch (m_depthTestingStatus) { case DEPTH_TEST_NO: glDisable(GL_DEPTH_TEST); break; case DEPTH_TEST_YES: glEnable(GL_DEPTH_TEST); break; } /* * Get the viewport */ GLint viewport[4]; glGetIntegerv(GL_VIEWPORT, viewport); /* * Get depth range for orthographic projection. */ GLdouble depthRange[2]; glGetDoublev(GL_DEPTH_RANGE, depthRange); /* * Set the orthographic projection so that its origin is in the bottom * left corner. It needs to be there since we are drawing in window * coordinates. We do not know the true size of the window but that * is okay since we can set the orthographic view so that the bottom * left corner is the origin and the top right corner is the top * right corner of the user's viewport. */ glMatrixMode(GL_PROJECTION); glLoadIdentity(); glOrtho(0, (viewport[2]), 0, (viewport[3]), depthRange[0], depthRange[1]); /* * Viewing projection is just the identity matrix since * we are drawing in window coordinates. */ glMatrixMode(GL_MODELVIEW); glLoadIdentity(); if (debugPrintFlag || drawCrosshairsAtFontStartingCoordinate) { GLfloat savedRGBA[4]; glGetFloatv(GL_CURRENT_COLOR, savedRGBA); glColor3f(1.0, 0.0, 0.0); glLineWidth(1.0); glPushMatrix(); glTranslatef(textStringGroup.m_viewportX, textStringGroup.m_viewportY, 0.0); const float xy = 500.0; glBegin(GL_LINES); glVertex2f(-xy, 0.0); glVertex2f( xy, 0.0); glVertex2f(0.0, -xy); glVertex2f(0.0, xy); glEnd(); glPopMatrix(); glColor3f(savedRGBA[0], savedRGBA[1], savedRGBA[2]); } 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, bottomLeft, bottomRight, topRight, topLeft, rotationPointXYZ); rotationPointXYZ[2] = 0.0; glPushMatrix(); glLoadIdentity(); applyBackgroundColoring(textStringGroup); applyTextColoring(annotationText); const float rotationAngle = annotationText.getRotationAngle(); glTranslated(rotationPointXYZ[0], rotationPointXYZ[1], rotationPointXYZ[2]); glRotated(rotationAngle, 0.0, 0.0, -1.0); for (std::vector::const_iterator iter = textStringGroup.m_textStrings.begin(); iter != textStringGroup.m_textStrings.end(); iter++) { const TextString* ts = *iter; double x = ts->m_viewportX; double y = ts->m_viewportY; double z = ts->m_viewportZ; applyTextColoring(annotationText); for (std::vector::const_iterator charIter = ts->m_characters.begin(); charIter != ts->m_characters.end(); charIter++) { const TextCharacter* tc = *charIter; x += tc->m_offsetX; y += tc->m_offsetY; z += tc->m_offsetZ; const double offsetX = x - rotationPointXYZ[0]; const double offsetY = y - rotationPointXYZ[1]; const double offsetZ = z - rotationPointXYZ[2]; glPushMatrix(); glTranslated(offsetX, offsetY, offsetZ); font->Render(&tc->m_character, 1); glPopMatrix(); } if (ts->m_underlineThickness > 0.0) { glPushMatrix(); glTranslated(ts->m_viewportX - rotationPointXYZ[0], ts->m_viewportY - rotationPointXYZ[1], 0.0); const double underlineY = ts->m_stringGlyphsMinY + underlineOffsetY; uint8_t foregroundRgba[4]; annotationText.getTextColorRGBA(foregroundRgba); drawUnderline(ts->m_stringGlyphsMinX, ts->m_stringGlyphsMaxX, underlineY, z, //0.0, // Z textStringGroup.m_underlineThickness, foregroundRgba); glPopMatrix(); } if (ts->m_outlineThickness > 0.0) { 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, foregroundRgba); glPopMatrix(); } } glPopMatrix(); BrainOpenGL::testForOpenGLError("At end of " "FtglFontTextRenderer::drawTextAtViewportCoordinatesInternal " "while drawing text: " + annotationText.getText()); restoreStateOfOpenGL(); #else // HAVE_FREETYPE CaretLogSevere("Trying to use FTGL Font rendering but it cannot be used due to FreeType not found."); #endif // HAVE_FREETYPE } /** * Draw underline using the given coordinates. * * @param lineStartX * X start of underline * @param lineEndX * X end of underline * @param lineY * Y of underline * @param lineZ * Z of underline * @param underlineThickness * Thickness of underline * @param foregroundRGBA * Color for drawing underline. */ void FtglFontTextRenderer::drawUnderline(const double lineStartX, const double lineEndX, const double lineY, const double lineZ, const double underlineThickness, uint8_t foregroundRgba[4]) { /* * Need to enable anti-aliasing for smooth lines */ glEnable(GL_LINE_SMOOTH); glEnable(GL_BLEND); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); glHint(GL_LINE_SMOOTH_HINT, GL_NICEST); std::vector underlineCoords; underlineCoords.insert(underlineCoords.end(), lineStartX); underlineCoords.insert(underlineCoords.end(), lineY); underlineCoords.insert(underlineCoords.end(), lineZ); underlineCoords.insert(underlineCoords.end(), lineEndX); underlineCoords.insert(underlineCoords.end(), lineY); underlineCoords.insert(underlineCoords.end(), lineZ); BrainOpenGLPrimitiveDrawing::drawLines(underlineCoords, foregroundRgba, underlineThickness); glDisable(GL_LINE_SMOOTH); glDisable(GL_BLEND); } /** * Draw outline using the given coordinates. * * @param minX * X start of outline * @param maxX * X end of outline * @param minY * Y start of outline * @param maxY * Y end of outline * @param z * Z of outline * @param outlineThickness * Thickness of outline * @param foregroundRGBA * Color for drawing outline. */ void FtglFontTextRenderer::drawOutline(const double minX, const double maxX, const double minY, const double maxY, const double z, const double outlineThickness, uint8_t foregroundRgba[4]) { /* * Need to enable anti-aliasing for smooth lines */ glEnable(GL_LINE_SMOOTH); glEnable(GL_BLEND); 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 }; 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); glDisable(GL_LINE_SMOOTH); glDisable(GL_BLEND); } /** * Expand a box by given amounts in X and Y. * * @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 extraSpaceX * Extra space to add in X. * @param extraSpaceY * Extra space to add in Y. */ void FtglFontTextRenderer::expandBox(float bottomLeft[3], float bottomRight[3], float topRight[3], float topLeft[3], const float extraSpaceX, const float extraSpaceY) { float widthVector[3]; MathFunctions::subtractVectors(topRight, topLeft, widthVector); MathFunctions::normalizeVector(widthVector); float heightVector[3]; MathFunctions::subtractVectors(topLeft, bottomLeft, heightVector); MathFunctions::normalizeVector(heightVector); const float widthSpacingX = extraSpaceX * widthVector[0]; const float widthSpacingY = extraSpaceY * widthVector[1]; const float heightSpacingX = extraSpaceX * heightVector[0]; const float heightSpacingY = extraSpaceY * heightVector[1]; topLeft[0] += (-widthSpacingX + heightSpacingX); topLeft[1] += (-widthSpacingY + heightSpacingY); topRight[0] += (widthSpacingX + heightSpacingX); topRight[1] += (widthSpacingY + heightSpacingY); bottomLeft[0] += (-widthSpacingX - heightSpacingX); bottomLeft[1] += (-widthSpacingY - heightSpacingY); bottomRight[0] += (widthSpacingX - heightSpacingX); bottomRight[1] += (widthSpacingY - heightSpacingY); } /** * Draw annnotation text at the given viewport coordinates using * the the annotations attributes for the style of text. * * Depth testing is DISABLED when drawing text with this method. * * @param viewportX * Viewport X-coordinate. * @param viewportY * Viewport Y-coordinate. * @param annotationText * Annotation text and attributes. */ void FtglFontTextRenderer::drawTextAtViewportCoords(const double viewportX, const double viewportY, const AnnotationText& annotationText) { setViewportHeight(); drawTextAtViewportCoordsInternal(DEPTH_TEST_NO, viewportX, viewportY, 0.0, annotationText); } /** * Draw annnotation text at the given viewport coordinates using * the the annotations attributes for the style of text. * * Depth testing is ENABLED when drawing text with this method. * * @param viewportX * Viewport X-coordinate. * @param viewportY * Viewport Y-coordinate. * @param viewportZ * Viewport Z-coordinate. * @param annotationText * Annotation text and attributes. */ void FtglFontTextRenderer::drawTextAtViewportCoords(const double viewportX, const double viewportY, const double viewportZ, const AnnotationText& annotationText) { setViewportHeight(); drawTextAtViewportCoordsInternal(DEPTH_TEST_YES, viewportX, viewportY, viewportZ, annotationText); } /** * Draw annnotation text at the given viewport coordinates using * the the annotations attributes for the style of text and the * depth testing method. * * @param depthTesting * Type of depth testing when drawing text. * @param viewportX * Viewport X-coordinate. * @param viewportY * Viewport Y-coordinate. * @param viewportZ * Viewport Z-coordinate. * @param annotationText * Annotation text and attributes. */ void FtglFontTextRenderer::drawTextAtViewportCoordsInternal(const DepthTestEnum depthTesting, const double viewportX, const double viewportY, const double viewportZ, const AnnotationText& annotationText) { if (annotationText.getText().isEmpty()) { return; } FTFont* font = getFont(annotationText, false); if ( ! font) { return; } m_depthTestingStatus = depthTesting; TextStringGroup tsg(annotationText, font, viewportX, viewportY, viewportZ, annotationText.getRotationAngle()); //tsg.print(); drawTextAtViewportCoordinatesInternal(annotationText, tsg); } /** * Get the bounds of text (in pixels) using the given text * attributes. A margin is included around the text. * * See http://ftgl.sourceforge.net/docs/html/metrics.png * * @param annotationText * Text that is to be drawn. * @param viewportX * Viewport X-coordinate. * @param viewportY * Viewport Y-coordinate. * @param viewportZ * Viewport Z-coordinate. * @param viewportHeight * Height of the viewport needed for percentage height text. * @param bottomLeftOut * The bottom left corner of the text bounds. * @param bottomRightOut * The bottom right corner of the text bounds. * @param topRightOut * The top right corner of the text bounds. * @param topLeftOut * The top left corner of the text bounds. */ void FtglFontTextRenderer::getBoundsForTextAtViewportCoords(const AnnotationText& annotationText, const double viewportX, const double viewportY, const double viewportZ, const double viewportHeight, double bottomLeftOut[3], double bottomRightOut[3], double topRightOut[3], double topLeftOut[3]) { setViewportHeight(); m_viewportHeight = viewportHeight; FTFont* font = getFont(annotationText, false); if ( ! font) { return; } TextStringGroup textStringGroup(annotationText, font, viewportX, viewportY, viewportZ, annotationText.getRotationAngle()); double rotationPointXYZ[3]; textStringGroup.getViewportBounds(s_textMarginSize, bottomLeftOut, bottomRightOut, topRightOut, topLeftOut, rotationPointXYZ); } /** * Apply the background coloring by drawing a rectangle in the background * color that encloses the text. If the background color is * invalid (alpha => 0), no action is taken. * * @param annotationText * Annotation Text that is to be drawn. * @param bottomLeftOut * The bottom left corner of the text bounds. * @param bottomRightOut * The bottom right corner of the text bounds. * @param topRightOut * The top right corner of the text bounds. * @param topLeftOut * The top left corner of the text bounds. */ void FtglFontTextRenderer::applyBackgroundColoring(const TextStringGroup& textStringGroup) { double bottomLeft[3], bottomRight[3], topRight[3], topLeft[3], rotationPointXYZ[3]; textStringGroup.getViewportBounds(s_textMarginSize, bottomLeft, bottomRight, topRight, topLeft, rotationPointXYZ); float backgroundColor[4]; textStringGroup.m_annotationText.getBackgroundColorRGBA(backgroundColor); if (backgroundColor[3] > 0.0) { // const AString bg("Background for \"" + annotationText.getText() + ": BL=" // + AString::fromNumbers(bottomLeftOut, 3, ",") + " BR=" // + AString::fromNumbers(bottomRightOut, 3, ",") + " TR=" // + AString::fromNumbers(topRightOut, 3, ",") + " TL=" // + AString::fromNumbers(topLeftOut, 3, ",")); // std::cout << qPrintable(bg) << std::endl; glColor4fv(backgroundColor); glBegin(GL_TRIANGLE_STRIP); glVertex3dv(bottomLeft); glVertex3dv(bottomRight); glVertex3dv(topLeft); glVertex3dv(topRight); glEnd(); } } /** * Apply the foreground color. * * @param annotationText * Annotation Text that is to be drawn. */ void FtglFontTextRenderer::applyTextColoring(const AnnotationText& annotationText) { float textColor[4]; annotationText.getTextColorRGBA(textColor); glColor4fv(textColor); } /** * Set the height of the viewport. This method must be called * at the beginning of all public methods. */ void FtglFontTextRenderer::setViewportHeight() { GLint viewport[4]; glGetIntegerv(GL_VIEWPORT, viewport); m_viewportHeight = viewport[3]; } /** * Get the estimated width and height of text (in pixels) using the given text * attributes. * * See http://ftgl.sourceforge.net/docs/html/metrics.png * * @param annotationText * Text for width and height estimation. * @param viewportHeight * Height of the viewport needed for percentage height text. * @param widthOut * Estimated width of text. * @param heightOut * Estimated height of text. */ void FtglFontTextRenderer::getTextWidthHeightInPixels(const AnnotationText& annotationText, const double viewportHeight, double& widthOut, double& heightOut) { setViewportHeight(); 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); widthOut = MathFunctions::distance3D(bottomLeft, bottomRight); heightOut = MathFunctions::distance3D(topLeft, bottomLeft); } /** * Draw annnotation text at the given model coordinates using * the the annotations attributes for the style of text. * * Depth testing is ENABLED when drawing text with this method. * * @param modelX * Model X-coordinate. * @param modelY * Model Y-coordinate. * @param modelZ * Model Z-coordinate. * @param annotationText * Annotation text and attributes. */ void FtglFontTextRenderer::drawTextAtModelCoords(const double modelX, const double modelY, const double modelZ, const AnnotationText& annotationText) { setViewportHeight(); m_depthTestingStatus = DEPTH_TEST_YES; GLdouble modelMatrix[16]; GLdouble projectionMatrix[16]; GLint viewport[4]; glGetDoublev(GL_MODELVIEW_MATRIX, modelMatrix); glGetDoublev(GL_PROJECTION_MATRIX, projectionMatrix); glGetIntegerv(GL_VIEWPORT, viewport); /* * Project model coordinate to a window coordinate. */ GLdouble windowX, windowY, windowZ; if (gluProject(modelX, modelY, modelZ, modelMatrix, projectionMatrix, viewport, &windowX, &windowY, &windowZ) == GL_TRUE) { /* * 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, negative the Z-value to be negative. */ windowZ = -windowZ; /* * Convert window coordinate to viewport coordinatde */ const double x = windowX - viewport[0]; const double y = windowY - viewport[1]; drawTextAtViewportCoordsInternal(DEPTH_TEST_YES, x, y, windowZ, annotationText); } else { CaretLogSevere("gluProject() failed for drawing text at model coordinates."); } } /** * Save the state of OpenGL. * Copied from Qt's qgl.cpp, qt_save_gl_state(). */ void FtglFontTextRenderer::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_CULL_FACE); glDisable(GL_LIGHTING); glDisable(GL_STENCIL_TEST); glDisable(GL_DEPTH_TEST); glEnable(GL_BLEND); glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA); } /** * Restore the state of OpenGL. * Copied from Qt's qgl.cpp, qt_restore_gl_state(). */ void FtglFontTextRenderer::restoreStateOfOpenGL() { glMatrixMode(GL_TEXTURE); glPopMatrix(); glMatrixMode(GL_PROJECTION); glPopMatrix(); glMatrixMode(GL_MODELVIEW); glPopMatrix(); glPopAttrib(); glPopClientAttrib(); } /** * Constructs invalid font data. */ FtglFontTextRenderer::FontData::FontData() { m_valid = false; m_font = NULL; } /** * Constructs a font with the given attributes. * Called should verify that this instance is valid (construction was successful). * * @param annotationText * Annotation Text that is to be drawn. * @param viewportHeight * Height of the viewport in which text is drawn. */ FtglFontTextRenderer::FontData::FontData(const AnnotationText& annotationText, const int32_t viewportHeight) { m_valid = false; m_font = NULL; #ifdef HAVE_FREETYPE const AnnotationTextFontNameEnum::Enum fontName = annotationText.getFont(); AString fontFileName = AnnotationTextFontNameEnum::getResourceFontFileName(fontName); if (annotationText.isBoldStyleEnabled() && annotationText.isItalicStyleEnabled()) { fontFileName = AnnotationTextFontNameEnum::getResourceBoldItalicFontFileName(fontName); } else if (annotationText.isBoldStyleEnabled()) { fontFileName = AnnotationTextFontNameEnum::getResourceBoldFontFileName(fontName); } else if (annotationText.isItalicStyleEnabled()) { fontFileName = AnnotationTextFontNameEnum::getResourceItalicFontFileName(fontName); } CaretAssert( ! fontFileName.isEmpty()); /* * Open the font file in the Workbench resources */ QFile file(fontFileName); if (file.open(QFile::ReadOnly)) { /* * Read all data which MUST remain valid until the FTGL * font is no longer used. */ m_fontData = file.readAll(); const size_t numBytes = m_fontData.size(); if (numBytes > 0) { /* * Create the FTGL font. */ m_font = new FTTextureFont((const unsigned char*)m_fontData.data(), numBytes); CaretAssert(m_font); /* * Font created successfully ? */ if ( ! m_font->Error()) { /* * Font size successful ? */ const int32_t fontSizePoints = annotationText.getFontSizeForDrawing(viewportHeight); if (m_font->FaceSize(fontSizePoints)) { m_valid = true; CaretLogFine("Created font size=" + AString::number(fontSizePoints) + " from font file " + file.fileName()); } else { CaretLogSevere("Error creating font size " + AString::number(fontSizePoints) + " from font file " + file.fileName()); } } else { CaretLogSevere("Error creating FTGL Font from " + file.fileName() + ". Error code from FreeType is " + AString::number(m_font->Error())); } } else { CaretLogSevere("Error reading data for FTGL Font from " + file.fileName() + " error: " + file.errorString()); } } else { CaretLogSevere("Unable to open FTGL Font File " + file.fileName() + " error: " + file.errorString()); } if ( ! m_valid) { m_fontData.clear(); if (m_font != NULL) { delete m_font; m_font = NULL; } } #endif // HAVE_FREETYPE } /** * Destructs font data. */ FtglFontTextRenderer::FontData::~FontData() { #ifdef HAVE_FREETYPE if (m_font != NULL) { delete m_font; m_font = NULL; } #endif // HAVE_FREETYPE } /** * @return Name of the text renderer. */ AString FtglFontTextRenderer::getName() const { return "FTGL Text Renderer"; } /* ================================================================================== */ /** * Constructor. * * @param character * The character for this instance. * @param horizontalAdvance * The advance used when drawing the string in a horizontal orientation. * @param glyphMinX * Minimum X relative to the character's origin form FTGL/Freetype. * @param glyphMaxX * Maximum X relative to the character's origin form FTGL/Freetype. * @param glyphMinY * Minimum Y relative to the character's origin form FTGL/Freetype. * @param glyphMaxY * Maximum Y relative to the character's origin form FTGL/Freetype. */ FtglFontTextRenderer::TextCharacter::TextCharacter(const wchar_t& character, const double horizontalAdvance, const double glyphMinX, const double glyphMaxX, const double glyphMinY, const double glyphMaxY) : m_character(character), m_horizontalAdvance(horizontalAdvance), m_glyphMinX(glyphMinX), m_glyphMaxX(glyphMaxX), m_glyphMinY(glyphMinY), m_glyphMaxY(glyphMaxY), m_offsetX(0.0), m_offsetY(0.0), m_offsetZ(0.0) { } /** * Destructor. */ FtglFontTextRenderer::TextCharacter::~TextCharacter() { } /** * Print information about this string using the given offset. * * @param offsetString * Offset prepended to each line that is printed. */ void FtglFontTextRenderer::TextCharacter::print(const AString& offsetString) { const QString msg(offsetString + "Char: " + QString::fromWCharArray(&m_character, 1) + " horizAdvance=" + AString::number(m_horizontalAdvance) + " minX=" + AString::number(m_glyphMinX) + " maxX=" + AString::number(m_glyphMaxX) + " minY=" + AString::number(m_glyphMinY) + " maxY=" + AString::number(m_glyphMaxY)); const QString msg2(offsetString + " Offset x=" + AString::number(m_offsetX) + " y=" + AString::number(m_offsetY) + " z=" + AString::number(m_offsetZ)); std::cout << qPrintable(msg) << std::endl; std::cout << qPrintable(msg2) << std::endl; } /* ================================================================================== */ /** * Constructor * * @param textString * The text string. * @parm orientation * Orientation of the text string. * @param underlineThickness * Thickness of underline for the text. * @param outlineThickness * Thickness of outline for the text. * @param font * Font for drawing the text string. */ FtglFontTextRenderer::TextString::TextString(const QString& textString, const AnnotationTextOrientationEnum::Enum orientation, const double underlineThickness, const double outlineThickness, FTFont* font) : m_underlineThickness(underlineThickness), m_outlineThickness(outlineThickness), m_viewportX(0.0), m_viewportY(0.0), m_viewportZ(0.0), m_stringGlyphsMinX(0.0), m_stringGlyphsMaxX(0.0), m_stringGlyphsMinY(0.0), m_stringGlyphsMaxY(0.0) { #ifdef HAVE_FREETYPE /* * Split the string into individual characters. */ const int32_t numChars = textString.length(); for (int32_t i = 0; i < numChars; i++) { const std::wstring theWideCharStr = textString.mid(i, 2).toStdWString(); const wchar_t theWideChar = theWideCharStr.at(0); FTBBox bbox = font->BBox(theWideCharStr.c_str(), 1); /* * A space character has a valid horizontal advance. * BUT, the bounds of the space character are all zero and since * the bounds are used for stacked text vertical advance we need * some value so use the bounds for a lowercase 'o'. * * The L indicates a wide character (wchar_t) */ if ((theWideChar == L' ') && (orientation == AnnotationTextOrientationEnum::STACKED)) { bbox = font->BBox("o"); } double advanceValue = 0.0; if (i < (numChars - 1)) { /* * We need to use both the currrent AND the next character * when calculating the advance for it to be correct. * In some instances the bounding boxes of two adjacent * characters will overlap. An example is the character * sequence 'AV' especially with a 'serif' font that has * small horizontal lines attached to the bottom legs of * the 'A' and the top of the 'V'. * * See https://en.wikipedia.org/wiki/Kerning * 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, advanceValue, bbox.Lower().Xf(), bbox.Upper().Xf(), bbox.Lower().Yf(), bbox.Upper().Yf()); m_characters.push_back(tc); } /* * Set the offset for each character in this text string */ initializeTextCharacterOffsets(orientation); /* * Set the bounds of the characters in this string. */ setGlyphBounds(); #endif // HAVE_FREETYPE } /** * Destructor. */ FtglFontTextRenderer::TextString::~TextString() { for (std::vector::iterator iter = m_characters.begin(); iter != m_characters.end(); iter++) { delete *iter; } m_characters.clear(); } /** * Initialize the offset of each character from * its preceding character. * * @param orientation * Orientation of the text string. */ void FtglFontTextRenderer::TextString::initializeTextCharacterOffsets(const AnnotationTextOrientationEnum::Enum orientation) { bool stackedTextFlag = false; switch (orientation) { case AnnotationTextOrientationEnum::HORIZONTAL: break; case AnnotationTextOrientationEnum::STACKED: stackedTextFlag = true; break; } double stringMinX = std::numeric_limits::max(); double stringMaxX = -std::numeric_limits::max(); const float verticalSpacing = s_textMarginSize * 2.0; /* * For each character, set its offset from the previous character */ const int numChars = static_cast(m_characters.size()); for (int32_t iChar = 0; iChar < numChars; iChar++) { CaretAssertVectorIndex(m_characters, iChar); TextCharacter* tc = m_characters[iChar]; double offsetX = 0.0; double offsetY = 0.0; double offsetZ = 0.0; if (iChar > 0) { CaretAssertVectorIndex(m_characters, iChar - 1); const TextCharacter* prevChar = m_characters[iChar - 1]; if (stackedTextFlag) { double offsetY1 = prevChar->m_glyphMinY; double offsetY2 = -verticalSpacing; double offsetY3 = -tc->m_glyphMaxY; offsetY = (offsetY1 + offsetY2 + offsetY3); } else { offsetX = prevChar->m_horizontalAdvance; } } tc->m_offsetX = offsetX; tc->m_offsetY = offsetY; tc->m_offsetZ = offsetZ; stringMinX = std::min(stringMinX, tc->m_glyphMinX); stringMaxX = std::max(stringMaxX, tc->m_glyphMaxX); } /* * When drawing stacked text, the characters * need their X offset adjusted so that they * are horizontally aligned in the center of * the "column" containing the text. */ if (stackedTextFlag) { /* * X-coordinate of character in the vertical column * so that the characters are centered horizontally * in the column. */ std::vector characterColumnCoordX(numChars, 0.0); const double columnWidth = stringMaxX - stringMinX; if (columnWidth > 0.0) { for (int32_t iChar = 0; iChar < numChars; iChar++) { CaretAssertVectorIndex(m_characters, iChar); TextCharacter* tc = m_characters[iChar]; const double characterWidth = tc->m_glyphMaxX - tc->m_glyphMinX; if (characterWidth > 0.0) { const double leftAndRightPadding = columnWidth - characterWidth; const double leftPadding = leftAndRightPadding / 2.0; const double characterX = leftPadding - tc->m_glyphMinX; // std::cout << "Char " << qPrintable(tc->m_character // + ": " // + " CharMinX=" // + QString::number(tc->m_glyphMinX) // + " CharMaxX=" // + QString::number(tc->m_glyphMaxX) // + " CharWidth=" // + QString::number(characterWidth) // + " Padding=" // + QString::number(leftPadding) // + " CharacterX=" // + QString::number(characterX)) << std::endl; if (leftPadding >= -0.01) { CaretAssertVectorIndex(characterColumnCoordX, iChar); characterColumnCoordX[iChar] = characterX; } else { CaretLogSevere("Text Character (" + QString::fromWCharArray(&tc->m_character, 1) + ") has invalid left padding=" + QString::number(leftPadding)); } } } /* * Set the character offsets. * SECOND and additional characters are offset from * the immediately previous character */ for (int32_t iChar = 0; iChar < numChars; iChar++) { CaretAssertVectorIndex(m_characters, iChar); TextCharacter* tc = m_characters[iChar]; if (iChar >= 1) { CaretAssertVectorIndex(m_characters, iChar - 1); CaretAssertVectorIndex(m_characters, iChar); tc->m_offsetX = characterColumnCoordX[iChar] - characterColumnCoordX[iChar - 1]; } else { CaretAssertVectorIndex(m_characters, iChar); tc->m_offsetX = characterColumnCoordX[iChar]; } } } } } /** * Print information about this string using the given offset. * * @param offsetString * Offset prepended to each line that is printed. */ void FtglFontTextRenderer::TextString::print(const AString& offsetString) { QString msg(offsetString + "Text String Bounds: minx=" + QString::number(m_stringGlyphsMinX) + " maxx=" + QString::number(m_stringGlyphsMaxX) + " miny=" + QString::number(m_stringGlyphsMinY) + " maxy=" + QString::number(m_stringGlyphsMaxY)); std::cout << qPrintable(msg) << std::endl; const QString doubleOffset(offsetString + offsetString); for (std::vector::iterator iter = m_characters.begin(); iter != m_characters.end(); iter++) { TextCharacter* tc = *iter; tc->print(doubleOffset); } } /** * Set the bounds of the glyphs for the text string. * The origin is at the first character. */ void FtglFontTextRenderer::TextString::setGlyphBounds() { m_stringGlyphsMinX = std::numeric_limits::max(); m_stringGlyphsMaxX = -std::numeric_limits::max(); m_stringGlyphsMinY = std::numeric_limits::max(); m_stringGlyphsMaxY = -std::numeric_limits::max(); double x = 0.0; double y = 0.0; for (std::vector::iterator iter = m_characters.begin(); iter != m_characters.end(); iter++) { TextCharacter* tc = *iter; x += tc->m_offsetX; y += tc->m_offsetY; const double left = x + tc->m_glyphMinX; const double right = x + tc->m_glyphMaxX; const double bottom = y + tc->m_glyphMinY - m_underlineThickness; const double top = y + tc->m_glyphMaxY; m_stringGlyphsMinX = std::min(m_stringGlyphsMinX, left); m_stringGlyphsMaxX = std::max(m_stringGlyphsMaxX, right); m_stringGlyphsMinY = std::min(m_stringGlyphsMinY, bottom); m_stringGlyphsMaxY = std::max(m_stringGlyphsMaxY, top); } } /** * Get the bounds of the text string in viewport coordinates. * * @param viewportMinX * Viewport minimum X. * @param viewportMaxX * Viewport maximum X. * @param viewportMinY * Viewport minimum Y. * @param viewportMaxY * Viewport minimum Y. */ void FtglFontTextRenderer::TextString::getTextBoundsInViewportCoordinates(double& viewportMinX, double& viewportMaxX, double& viewportMinY, double& viewportMaxY) const { viewportMinX = m_viewportX + m_stringGlyphsMinX; viewportMaxX = m_viewportX + m_stringGlyphsMaxX; viewportMinY = m_viewportY + m_stringGlyphsMinY; viewportMaxY = m_viewportY + m_stringGlyphsMaxY; } /* ================================================================================== */ /** * Constructor for a text string group. * * @param annotationText * The text annotation. * @param font * Font used for drawing the annotation. * @param viewportX * X-coordinate in the viewport. * @param viewportY * Y-coordinate in the viewport. * @param viewportZ * Z-coordinate in the viewport. * @param rotationAngle * Rotation angle for the text. */ FtglFontTextRenderer::TextStringGroup::TextStringGroup(const AnnotationText& annotationText, FTFont* font, const double viewportX, const double viewportY, const double viewportZ, const double rotationAngle) : m_annotationText(annotationText), m_font(font), m_viewportX(viewportX), 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), m_viewportBoundsMaxY(0.0) { #ifdef HAVE_FREETYPE CaretAssert(font); /* * The underline for text is scaled with the size of the font * Underline is drawn anytime thickness is greater than zero */ if (annotationText.isUnderlineStyleEnabled()) { if (annotationText.getOrientation() == AnnotationTextOrientationEnum::HORIZONTAL) { m_underlineThickness = std::max((font->FaceSize() / 14.0), 1.0); } } /* * The outline for text is scaled with the size of the font * Outline is drawn anytime thickness is greater than zero * and is drawn using the foreground color */ if (annotationText.getLineColor() != CaretColorEnum::NONE) { if (annotationText.getOrientation() == AnnotationTextOrientationEnum::HORIZONTAL) { m_outlineThickness = m_annotationText.getLineWidth(); } } /* * Each row (horizontal text) or column (vertical text) is * separated by a newline character */ QStringList textList = m_annotationText.getText().split('\n', QString::KeepEmptyParts); const int32_t textListSize = textList.size(); for (int32_t i = 0; i < textListSize; i++) { TextString* ts = new TextString(textList.at(i), annotationText.getOrientation(), m_underlineThickness, m_outlineThickness, font); m_textStrings.push_back(ts); } initializeTextPositions(); updateTextBounds(); applyAlignmentsToTextStrings(); /* * Alignment moves text so bounds need to be updated */ updateTextBounds(); #endif // HAVE_FREETYPE } /** * Destructor. */ FtglFontTextRenderer::TextStringGroup::~TextStringGroup() { for (std::vector::iterator iter = m_textStrings.begin(); iter != m_textStrings.end(); iter++) { delete *iter; } m_textStrings.clear(); } /** * Get the bounds of the all text that is drawn. The * bounds is used for selection and the background color. * * @param margin * Margin added around sides * @param bottomLeftOut * Coordinate of bottom left. * @param bottomRightOut * Coordinate of bottom right. * @param topRightOut * Coordinate of top right. * @param topLeftOut * Coordinate of top left. * @param rotationPointXYZOut * Output containing rotation point for bounds. */ void FtglFontTextRenderer::TextStringGroup::getViewportBounds(const double margin, double bottomLeftOut[3], double bottomRightOut[3], double topRightOut[3], double topLeftOut[3], double rotationPointXYZOut[3]) const { bottomLeftOut[0] = m_viewportBoundsMinX; bottomLeftOut[1] = m_viewportBoundsMinY; bottomLeftOut[2] = m_viewportZ; bottomRightOut[0] = m_viewportBoundsMaxX; bottomRightOut[1] = m_viewportBoundsMinY; bottomRightOut[2] = m_viewportZ; topRightOut[0] = m_viewportBoundsMaxX; topRightOut[1] = m_viewportBoundsMaxY; topRightOut[2] = m_viewportZ; topLeftOut[0] = m_viewportBoundsMinX; topLeftOut[1] = m_viewportBoundsMaxY; topLeftOut[2] = m_viewportZ; double rotationX = m_viewportBoundsMinX; double rotationY = m_viewportBoundsMinY; double rotationZ = m_viewportZ; switch (m_annotationText.getVerticalAlignment()) { case AnnotationTextAlignVerticalEnum::BOTTOM: switch (m_annotationText.getHorizontalAlignment()) { case AnnotationTextAlignHorizontalEnum::CENTER: rotationX = (bottomLeftOut[0] + bottomRightOut[0]) / 2.0; rotationY = (bottomLeftOut[1] + bottomRightOut[1]) / 2.0; rotationZ = (bottomLeftOut[2] + bottomRightOut[2]) / 2.0; break; case AnnotationTextAlignHorizontalEnum::LEFT: rotationX = bottomLeftOut[0]; rotationY = bottomLeftOut[1]; rotationZ = bottomLeftOut[2]; break; case AnnotationTextAlignHorizontalEnum::RIGHT: rotationX = bottomRightOut[0]; rotationY = bottomRightOut[1]; rotationZ = bottomRightOut[2]; break; } break; case AnnotationTextAlignVerticalEnum::MIDDLE: switch (m_annotationText.getHorizontalAlignment()) { case AnnotationTextAlignHorizontalEnum::CENTER: rotationX = (bottomLeftOut[0] + bottomRightOut[0] + topLeftOut[0] + topRightOut[0]) / 4.0; rotationY = (bottomLeftOut[1] + bottomRightOut[1] + topLeftOut[1] + topRightOut[1]) / 4.0; rotationZ = (bottomLeftOut[2] + bottomRightOut[2] + topLeftOut[2] + topRightOut[2]) / 4.0; break; case AnnotationTextAlignHorizontalEnum::LEFT: rotationX = (bottomLeftOut[0] + topLeftOut[0]) / 2.0; rotationY = (bottomLeftOut[1] + topLeftOut[1]) / 2.0; rotationZ = (bottomLeftOut[2] + topLeftOut[2]) / 2.0; break; case AnnotationTextAlignHorizontalEnum::RIGHT: rotationX = (topRightOut[0] + bottomRightOut[0]) / 2.0; rotationY = (topRightOut[1] + bottomRightOut[1]) / 2.0; rotationZ = (topRightOut[2] + bottomRightOut[2]) / 2.0; break; } break; case AnnotationTextAlignVerticalEnum::TOP: switch (m_annotationText.getHorizontalAlignment()) { case AnnotationTextAlignHorizontalEnum::CENTER: rotationX = (topLeftOut[0] + topRightOut[0]) / 2.0; rotationY = (topLeftOut[1] + topRightOut[1]) / 2.0; rotationZ = (topLeftOut[2] + topRightOut[2]) / 2.0; break; case AnnotationTextAlignHorizontalEnum::LEFT: rotationX = topLeftOut[0]; rotationY = topLeftOut[1]; rotationZ = topLeftOut[2]; break; case AnnotationTextAlignHorizontalEnum::RIGHT: rotationX = topRightOut[0]; rotationY = topRightOut[1]; rotationZ = topRightOut[2]; break; } break; } rotationPointXYZOut[0] = rotationX; rotationPointXYZOut[1] = rotationY; rotationPointXYZOut[2] = rotationZ; Matrix4x4 matrix; matrix.translate(-rotationX, -rotationY, 0.0); matrix.rotateZ(-m_rotationAngle); matrix.translate(rotationX, rotationY, 0.0); /* * Add margin to the viewport bounds. * Margin is NOT included when rotation point is computed * as it will move the rotation point to the wrong position. */ bottomLeftOut[0] -= margin; bottomLeftOut[1] -= margin; bottomRightOut[0] += margin; bottomRightOut[1] -= margin; topRightOut[0] += margin; topRightOut[1] += margin; topLeftOut[0] -= margin; topLeftOut[1] += margin; matrix.multiplyPoint3(bottomLeftOut); matrix.multiplyPoint3(bottomRightOut); matrix.multiplyPoint3(topRightOut); matrix.multiplyPoint3(topLeftOut); } /** * Begin to postion the text strings. */ void FtglFontTextRenderer::TextStringGroup::initializeTextPositions() { double x = m_viewportX; double y = m_viewportY; double z = m_viewportZ; const int32_t numStrings = static_cast(m_textStrings.size()); for (int32_t iStr = 0; iStr < numStrings; iStr++) { CaretAssertVectorIndex(m_textStrings, iStr); TextString* textString = m_textStrings[iStr]; if (iStr >= 1) { CaretAssertVectorIndex(m_textStrings, iStr - 1); TextString* prevTextString = m_textStrings[iStr - 1]; switch (m_annotationText.getOrientation()) { case AnnotationTextOrientationEnum::HORIZONTAL: { /* * Move coordinate DOWN for next ROW of text */ 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 offsetY = (offsetY1 + offsetY2 + offsetY3 + offsetY4); y += offsetY; } break; case AnnotationTextOrientationEnum::STACKED: { /* * Move coordinate RIGHT for next COLUMN of text */ const double offsetX1 = prevTextString->m_stringGlyphsMaxX; const double offsetX2 = s_textMarginSize; const double offsetX3 = -textString->m_stringGlyphsMinX; const double offsetX = (offsetX1 + offsetX2 + offsetX3); x += offsetX; } break; } } textString->m_viewportX = x; textString->m_viewportY = y; textString->m_viewportZ = z; } } /** * Print info about the text. */ void FtglFontTextRenderer::TextStringGroup::print() { QString msg("Text String Group: " + m_annotationText.getText()); std::cout << qPrintable(msg) << std::endl; for (std::vector::iterator iter = m_textStrings.begin(); iter != m_textStrings.end(); iter++) { TextString* ts = *iter; ts->print(" "); } std::cout << std::endl; } /** * Update the bounds (extent) of the text. */ void FtglFontTextRenderer::TextStringGroup::updateTextBounds() { m_viewportBoundsMinX = std::numeric_limits::max(); m_viewportBoundsMaxX = -std::numeric_limits::max(); m_viewportBoundsMinY = std::numeric_limits::max(); m_viewportBoundsMaxY = -std::numeric_limits::max(); double x = 0.0; double y = 0.0; for (std::vector::iterator iter = m_textStrings.begin(); iter != m_textStrings.end(); iter++) { TextString* ts = *iter; double stringMinX = 0.0; double stringMaxX = 0.0; double stringMinY = 0.0; double stringMaxY = 0.0; ts->getTextBoundsInViewportCoordinates(stringMinX, stringMaxX, stringMinY, stringMaxY); const double left = x + stringMinX; const double right = x + stringMaxX; const double bottom = y + stringMinY; const double top = y + stringMaxY; m_viewportBoundsMinX = std::min(m_viewportBoundsMinX, left); m_viewportBoundsMaxX = std::max(m_viewportBoundsMaxX, right); m_viewportBoundsMinY = std::min(m_viewportBoundsMinY, bottom); m_viewportBoundsMaxY = std::max(m_viewportBoundsMaxY, top); } } /** * Adjust the positions of the horizontal text strings so * they are aligned properly for both horizontal and * vertical alignment. */ void FtglFontTextRenderer::TextStringGroup::applyAlignmentsToHorizontalTextStrings() { double dy = 0.0; switch (m_annotationText.getVerticalAlignment()) { case AnnotationTextAlignVerticalEnum::BOTTOM: dy = m_viewportY - m_viewportBoundsMinY; break; case AnnotationTextAlignVerticalEnum::MIDDLE: dy = m_viewportY - (m_viewportBoundsMinY + m_viewportBoundsMaxY) / 2.0; break; case AnnotationTextAlignVerticalEnum::TOP: dy = m_viewportY - m_viewportBoundsMaxY; break; } for (std::vector::iterator iter = m_textStrings.begin(); iter != m_textStrings.end(); iter++) { TextString* ts = *iter; double dx = 0.0; switch (m_annotationText.getHorizontalAlignment()) { case AnnotationTextAlignHorizontalEnum::CENTER: { const double centerX = (ts->m_stringGlyphsMinX + ts->m_stringGlyphsMaxX) / 2.0; dx = -centerX; } break; case AnnotationTextAlignHorizontalEnum::LEFT: dx = -ts->m_stringGlyphsMinX; break; case AnnotationTextAlignHorizontalEnum::RIGHT: dx = -ts->m_stringGlyphsMaxX; break; } ts->m_viewportX += dx; ts->m_viewportY += dy; } } /** * Adjust the positions of the stacked text strings so * they are aligned properly for both horizontal and * vertical alignment. */ void FtglFontTextRenderer::TextStringGroup::applyAlignmentsToStackedTextStrings() { double dx = 0.0; switch (m_annotationText.getHorizontalAlignment()) { case AnnotationTextAlignHorizontalEnum::CENTER: { const double centerX = (m_viewportBoundsMinX + m_viewportBoundsMaxX) / 2.0; dx = m_viewportX - centerX; } break; case AnnotationTextAlignHorizontalEnum::LEFT: dx = m_viewportX - m_viewportBoundsMinX; break; case AnnotationTextAlignHorizontalEnum::RIGHT: dx = m_viewportX - m_viewportBoundsMaxX; break; } for (std::vector::iterator iter = m_textStrings.begin(); iter != m_textStrings.end(); iter++) { TextString* ts = *iter; double dy = 0.0; switch (m_annotationText.getVerticalAlignment()) { case AnnotationTextAlignVerticalEnum::BOTTOM: dy = -ts->m_stringGlyphsMinY; break; case AnnotationTextAlignVerticalEnum::MIDDLE: dy = - (ts->m_stringGlyphsMinY + ts->m_stringGlyphsMaxY) / 2.0; break; case AnnotationTextAlignVerticalEnum::TOP: dy = -ts->m_stringGlyphsMaxY; break; } ts->m_viewportX += dx; ts->m_viewportY += dy; } } /** * Adjust the positions of the text strings so * they are aligned properly. */ void FtglFontTextRenderer::TextStringGroup::applyAlignmentsToTextStrings() { switch (m_annotationText.getOrientation()) { case AnnotationTextOrientationEnum::HORIZONTAL: applyAlignmentsToHorizontalTextStrings(); break; case AnnotationTextOrientationEnum::STACKED: applyAlignmentsToStackedTextStrings(); break; } } connectome-workbench-1.2.3+git41-gc4c6c90/src/Brain/FtglFontTextRenderer.h000066400000000000000000000270241300200146000260340ustar00rootroot00000000000000#ifndef __FTGL_FONT_TEXT_RENDERER_H__ #define __FTGL_FONT_TEXT_RENDERER_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 "AnnotationTextAlignHorizontalEnum.h" #include "AnnotationTextOrientationEnum.h" #include "BrainOpenGLTextRenderInterface.h" class FTFont; namespace caret { class FtglFontTextRenderer : public BrainOpenGLTextRenderInterface { public: FtglFontTextRenderer(); virtual ~FtglFontTextRenderer(); bool isValid() const; virtual void drawTextAtViewportCoords(const double viewportX, const double viewportY, const AnnotationText& annotationText); virtual void drawTextAtViewportCoords(const double viewportX, const double viewportY, const double viewportZ, const AnnotationText& annotationText); virtual void drawTextAtModelCoords(const double modelX, const double modelY, const double modelZ, const AnnotationText& annotationText); virtual void getTextWidthHeightInPixels(const AnnotationText& annotationText, const double viewportHeight, double& widthOut, double& heightOut); virtual void getBoundsForTextAtViewportCoords(const AnnotationText& annotationText, const double viewportX, const double viewportY, const double viewportZ, const double viewportHeight, double bottomLeftOut[3], double bottomRightOut[3], double topRightOut[3], double topLeftOut[3]); virtual AString getName() const; private: enum DepthTestEnum { DEPTH_TEST_NO, DEPTH_TEST_YES }; FtglFontTextRenderer(const FtglFontTextRenderer&); FtglFontTextRenderer& operator=(const FtglFontTextRenderer&); void drawTextAtViewportCoordsInternal(const DepthTestEnum depthTesting, const double viewportX, const double viewportY, const double viewportZ, const AnnotationText& annotationText); FTFont* getFont(const AnnotationText& annotationText, const bool creatingDefaultFontFlag); void drawUnderline(const double lineStartX, const double lineEndX, const double lineY, const double lineZ, const double underlineThickness, uint8_t foregroundRgba[4]); void drawOutline(const double minX, const double maxX, const double minY, const double maxY, const double z, const double outlineThickness, uint8_t foregroundRgba[4]); static void expandBox(float bottomLeft[3], float bottomRight[3], float topRight[3], float topLeft[3], const float extraSpaceX, const float extraSpaceY); class FontData { public: FontData(); FontData(const AnnotationText& annotationText, const int32_t viewportHeight); ~FontData(); void initialize(const AString& fontFileName); QByteArray m_fontData; FTFont* m_font; bool m_valid; }; /** * A text character */ class TextCharacter { public: TextCharacter(const wchar_t& character, const double horizontalAdvance, const double glyphMinX, const double glyphMaxX, const double glyphMinY, const double glyphMaxY); ~TextCharacter(); void print(const AString& offsetString); const wchar_t m_character; const double m_horizontalAdvance; /* * Bounding box that encloses text with 0.0 at the "pen" * http://www.freetype.org/freetype2/docs/glyphs/glyphs-3.html */ const double m_glyphMinX; const double m_glyphMaxX; const double m_glyphMinY; const double m_glyphMaxY; /* * Offset from previous character */ double m_offsetX; double m_offsetY; double m_offsetZ; }; /** * A string of text */ class TextString { public: TextString(const QString& textString, const AnnotationTextOrientationEnum::Enum orientation, const double underlineThickness, const double outlineThickness, FTFont* font); ~TextString(); void print(const AString& offsetString); void setGlyphBounds(); void getTextBoundsInViewportCoordinates(double& viewportMinX, double& viewportMaxX, double& viewportMinY, double& viewportMaxY) const; const double m_underlineThickness; const double m_outlineThickness; std::vector m_characters; double m_viewportX; double m_viewportY; double m_viewportZ; /* * Bounds relative to origin of first character */ double m_stringGlyphsMinX; double m_stringGlyphsMaxX; double m_stringGlyphsMinY; double m_stringGlyphsMaxY; private: void initializeTextCharacterOffsets(const AnnotationTextOrientationEnum::Enum orientation); }; /** * The "high level" text object containing strings of text */ class TextStringGroup { public: TextStringGroup(const AnnotationText& annotationText, FTFont* font, const double viewportX, const double viewportY, const double viewportZ, const double rotationAngle); ~TextStringGroup(); void getViewportBounds(const double margin, double bottomLeftOut[3], double bottomRightOut[3], double topRightOut[3], double topLeftOut[3], double rotationPointXYZOut[3]) const; void print(); std::vector m_textStrings; const AnnotationText& m_annotationText; FTFont* m_font; const double m_viewportX; const double m_viewportY; const double m_viewportZ; const double m_rotationAngle; double m_underlineThickness; double m_outlineThickness; /* * Bounds relative to origin of first character */ double m_viewportBoundsMinX; double m_viewportBoundsMaxX; double m_viewportBoundsMinY; double m_viewportBoundsMaxY; private: void applyAlignmentsToHorizontalTextStrings(); void applyAlignmentsToStackedTextStrings(); void applyAlignmentsToTextStrings(); void initializeTextPositions(); void updateTextBounds(); void splitTextIntoLines(); }; void drawTextAtViewportCoordinatesInternal(const AnnotationText& annotationText, const TextStringGroup& textStringGroup); void applyTextColoring(const AnnotationText& annotationText); void applyBackgroundColoring(const TextStringGroup& textStringGroup); void setViewportHeight(); void saveStateOfOpenGL(); void restoreStateOfOpenGL(); /** * The default font. DO NOT delete it since it points to * a font in "m_fontNameToFontMap". */ FTFont* m_defaultFont; /** * Map for caching fonts */ typedef std::map FONT_MAP; /** * Iterator for cached fonts. */ typedef FONT_MAP::iterator FONT_MAP_ITERATOR; /** * Caches fonts as they are created */ FONT_MAP m_fontNameToFontMap; /** * Tracks fonts that failed creation to avoid * printing an error message more than once. */ std::set m_failedFontNames; /** Depth testing enabled status */ DepthTestEnum m_depthTestingStatus; /** Height of the viewport */ int32_t m_viewportHeight; static const double s_textMarginSize; }; #ifdef __FTGL_FONT_TEXT_RENDERER_DECLARE__ const double FtglFontTextRenderer::s_textMarginSize = 3.0; #endif // __FTGL_FONT_TEXT_RENDERER_DECLARE__ } // namespace #endif //__FTGL_FONT_TEXT_RENDERER_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Brain/GapsAndMargins.cxx000066400000000000000000000326421300200146000251700ustar00rootroot00000000000000 /*LICENSE_START*/ /* * Copyright (C) 2015 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 __GAPS_AND_MARGINS_DECLARE__ #include "GapsAndMargins.h" #undef __GAPS_AND_MARGINS_DECLARE__ #include "CaretAssert.h" #include "SceneClass.h" #include "SceneClassAssistant.h" using namespace caret; /** * \class caret::GapsAndMargins * \brief Gaps for surface montage and volume montage. Margins for tabs. * \ingroup Brain */ /** * Constructor. */ GapsAndMargins::GapsAndMargins() : CaretObject() { reset(); m_sceneAssistant = new SceneClassAssistant(); m_sceneAssistant->addArray("m_tabMarginsBottom", m_tabMarginsBottom, BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS, 0.0); m_sceneAssistant->addArray("m_tabMarginsLeft", m_tabMarginsLeft, BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS, 0.0); m_sceneAssistant->addArray("m_tabMarginsRight", m_tabMarginsRight, BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS, 0.0); m_sceneAssistant->addArray("m_tabMarginsTop", m_tabMarginsTop, BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS, 0.0); m_sceneAssistant->addArray("m_surfaceMontageHorizontalGaps", m_surfaceMontageHorizontalGaps, BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_WINDOWS, 0.0); m_sceneAssistant->addArray("m_surfaceMontageVerticalGaps", m_surfaceMontageVerticalGaps, BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_WINDOWS, 0.0); m_sceneAssistant->addArray("m_volumeMontageHorizontalGaps", m_volumeMontageHorizontalGaps, BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_WINDOWS, 0.0); m_sceneAssistant->addArray("m_volumeMontageVerticalGaps", m_volumeMontageVerticalGaps, BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_WINDOWS, 0.0); } /** * Destructor. */ GapsAndMargins::~GapsAndMargins() { delete m_sceneAssistant; } /** * Get a description of this object's content. * @return String describing this object's content. */ AString GapsAndMargins::toString() const { return "GapsAndMargins"; } void GapsAndMargins::reset() { for (int32_t i = 0; i < BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS; i++) { m_tabMarginsBottom[i] = 0.0; m_tabMarginsLeft[i] = 0.0; m_tabMarginsRight[i] = 0.0; m_tabMarginsTop[i] = 0.0; } for (int32_t i = 0; i < BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_WINDOWS; i++) { m_surfaceMontageHorizontalGaps[i] = 0.0; m_surfaceMontageVerticalGaps[i] = 0.0; m_volumeMontageHorizontalGaps[i] = 0.0; m_volumeMontageVerticalGaps[i] = 0.0; } } /** * Get the LEFT margin for the given tab. * * @param tabIndex * Index of the tab. * @return * Left margin for the tab. */ float GapsAndMargins::getMarginLeftForTab(const int32_t tabIndex) const { CaretAssertArrayIndex(m_tabMarginsLeft, BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS, tabIndex); return m_tabMarginsLeft[tabIndex]; } /** * Get the RIGHT margin for the given tab. * * @param tabIndex * Index of the tab. * @return * Right margin for the tab. */ float GapsAndMargins::getMarginRightForTab(const int32_t tabIndex) const { CaretAssertArrayIndex(m_tabMarginsRight, BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS, tabIndex); return m_tabMarginsRight[tabIndex]; } /** * Get the BOTTOM margin for the given tab. * * @param tabIndex * Index of the tab. * @return * Bottom margin for the tab. */ float GapsAndMargins::getMarginBottomForTab(const int32_t tabIndex) const { CaretAssertArrayIndex(m_tabMarginsBottom, BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS, tabIndex); return m_tabMarginsBottom[tabIndex]; } /** * Get the TOP margin for the given tab. * * @param tabIndex * Index of the tab. * @return * Top margin for the tab. */ float GapsAndMargins::getMarginTopForTab(const int32_t tabIndex) const { CaretAssertArrayIndex(m_tabMarginsTop, BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS, tabIndex); return m_tabMarginsTop[tabIndex]; } /** * Set the LEFT margin for the given tab. * * @param tabIndex * Index of the tab. * @param margin * Left margin for the tab. */ void GapsAndMargins::setMarginLeftForTab(const int32_t tabIndex, const float margin) { CaretAssertArrayIndex(m_tabMarginsLeft, BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS, tabIndex); m_tabMarginsLeft[tabIndex] = margin; } /** * Set the RIGHT margin for the given tab. * * @param tabIndex * Index of the tab. * @param margin * Right margin for the tab. */ void GapsAndMargins::setMarginRightForTab(const int32_t tabIndex, const float margin) { CaretAssertArrayIndex(m_tabMarginsRight, BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS, tabIndex); m_tabMarginsRight[tabIndex] = margin; } /** * Set the BOTTOM margin for the given tab. * * @param tabIndex * Index of the tab. * @param margin * Bottom margin for the tab. */ void GapsAndMargins::setMarginBottomForTab(const int32_t tabIndex, const float margin) { CaretAssertArrayIndex(m_tabMarginsBottom, BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS, tabIndex); m_tabMarginsBottom[tabIndex] = margin; } /** * Set the Top margin for the given tab. * * @param tabIndex * Index of the tab. * @param margin * Top margin for the tab. */ void GapsAndMargins::setMarginTopForTab(const int32_t tabIndex, const float margin) { CaretAssertArrayIndex(m_tabMarginsTop, BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS, tabIndex); m_tabMarginsTop[tabIndex] = margin; } /** * @return The surface montage horizontal gap. * * @param windowIndex * Index of window. */ float GapsAndMargins::getSurfaceMontageHorizontalGapForWindow(const int32_t windowIndex) const { CaretAssertArrayIndex(m_surfaceMontageHorizontalGaps, BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_WINDOWS, windowIndex); return m_surfaceMontageHorizontalGaps[windowIndex]; } /** * @return The surface montage vertical gap. * * @param windowIndex * Index of window. */ float GapsAndMargins::getSurfaceMontageVerticalGapForWindow(const int32_t windowIndex) const { CaretAssertArrayIndex(m_surfaceMontageVerticalGaps, BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_WINDOWS, windowIndex); return m_surfaceMontageVerticalGaps[windowIndex]; } /** * @return The volume montage horizontal gap. * * @param windowIndex * Index of window. */ float GapsAndMargins::getVolumeMontageHorizontalGapForWindow(const int32_t windowIndex) const { CaretAssertArrayIndex(m_volumeMontageHorizontalGaps, BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_WINDOWS, windowIndex); return m_volumeMontageHorizontalGaps[windowIndex]; } /** * @return The volume montage vertical gap. * * @param windowIndex * Index of window. */ float GapsAndMargins::getVolumeMontageVerticalGapForWindow(const int32_t windowIndex) const { CaretAssertArrayIndex(m_volumeMontageVerticalGaps, BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_WINDOWS, windowIndex); return m_volumeMontageVerticalGaps[windowIndex]; } /** * Set the surface montage horizontal gap. * * @param windowIndex * Index of window. * @param gap * New value for horizontal gap. */ void GapsAndMargins::setSurfaceMontageHorizontalGapForWindow(const int32_t windowIndex, const float gap) { CaretAssertArrayIndex(m_surfaceMontageHorizontalGaps, BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_WINDOWS, windowIndex); m_surfaceMontageHorizontalGaps[windowIndex] = gap; } /** * Set the surface montage vertical gap. * * @param windowIndex * Index of window. * @param gap * New value for vertical gap. */ void GapsAndMargins::setSurfaceMontageVerticalGapForWindow(const int32_t windowIndex, const float gap) { CaretAssertArrayIndex(m_surfaceMontageVerticalGaps, BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_WINDOWS, windowIndex); m_surfaceMontageVerticalGaps[windowIndex] = gap; } /** * Set the volume montage horizontal gap. * * @param windowIndex * Index of window. * @param gap * New value for horizontal gap. */ void GapsAndMargins::setVolumeMontageHorizontalGapForWindow(const int32_t windowIndex, const float gap) { CaretAssertArrayIndex(m_volumeMontageHorizontalGaps, BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_WINDOWS, windowIndex); m_volumeMontageHorizontalGaps[windowIndex] = gap; } /** * Set the volume montage vertical gap. * * @param windowIndex * Index of window. * @param gap * New value for vertical gap. */ void GapsAndMargins::setVolumeMontageVerticalGapForWindow(const int32_t windowIndex, const float gap) { CaretAssertArrayIndex(m_volumeMontageVerticalGaps, BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_WINDOWS, windowIndex); m_volumeMontageVerticalGaps[windowIndex] = gap; } /** * Get the margins for drawing a tab. * * @param tabIndex * Index of the tab. * @param viewportWidth * Width of viewport. * @param viewportHeight * Height of viewport. * @param MarginOut * Margin for * @param MarginOut * Margin for * @param MarginOut * Margin for * @param MarginOut * Margin for */ void GapsAndMargins::getMarginsInPixelsForDrawing(const int32_t tabIndex, const int32_t viewportWidth, const int32_t viewportHeight, int32_t& leftMarginOut, int32_t& rightMarginOut, int32_t& bottomMarginOut, int32_t& topMarginOut) const { leftMarginOut = 0; rightMarginOut = 0; bottomMarginOut = 0; topMarginOut = 0; topMarginOut = static_cast(viewportHeight * (getMarginTopForTab(tabIndex) / 100.0)); bottomMarginOut = static_cast(viewportHeight * (getMarginBottomForTab(tabIndex) / 100.0)); leftMarginOut = static_cast(viewportWidth * (getMarginLeftForTab(tabIndex) / 100.0)); rightMarginOut = static_cast(viewportWidth * (getMarginRightForTab(tabIndex) / 100.0)); } /** * 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* GapsAndMargins::saveToScene(const SceneAttributes* sceneAttributes, const AString& instanceName) { SceneClass* sceneClass = new SceneClass(instanceName, "GapsAndMargins", 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 GapsAndMargins::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); } connectome-workbench-1.2.3+git41-gc4c6c90/src/Brain/GapsAndMargins.h000066400000000000000000000130341300200146000246070ustar00rootroot00000000000000#ifndef __GAPS_AND_MARGINS_H__ #define __GAPS_AND_MARGINS_H__ /*LICENSE_START*/ /* * Copyright (C) 2015 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 "SceneableInterface.h" namespace caret { class SceneClassAssistant; class GapsAndMargins : public CaretObject, public SceneableInterface { public: GapsAndMargins(); virtual ~GapsAndMargins(); float getMarginLeftForTab(const int32_t tabIndex) const; float getMarginRightForTab(const int32_t tabIndex) const; float getMarginBottomForTab(const int32_t tabIndex) const; float getMarginTopForTab(const int32_t tabIndex) const; void setMarginLeftForTab(const int32_t tabIndex, const float margin); void setMarginRightForTab(const int32_t tabIndex, const float margin); void setMarginBottomForTab(const int32_t tabIndex, const float margin); void setMarginTopForTab(const int32_t tabIndex, const float margin); float getSurfaceMontageHorizontalGapForWindow(const int32_t windowIndex) const; float getSurfaceMontageVerticalGapForWindow(const int32_t windowIndex) const; float getVolumeMontageHorizontalGapForWindow(const int32_t windowIndex) const; float getVolumeMontageVerticalGapForWindow(const int32_t windowIndex) const; void setSurfaceMontageHorizontalGapForWindow(const int32_t windowIndex, const float gap); void setSurfaceMontageVerticalGapForWindow(const int32_t windowIndex, const float gap); void setVolumeMontageHorizontalGapForWindow(const int32_t windowIndex, const float gap); void setVolumeMontageVerticalGapForWindow(const int32_t windowIndex, const float gap); void reset(); // ADD_NEW_METHODS_HERE virtual AString toString() const; void getMarginsInPixelsForDrawing(const int32_t tabIndex, const int32_t viewportWidth, const int32_t viewportHeight, int32_t& leftMarginOut, int32_t& rightMarginOut, int32_t& bottomMarginOut, int32_t& topMarginOut) 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 implemetation by sub-classes. // protected: // virtual void saveSubClassDataToScene(const SceneAttributes* sceneAttributes, // SceneClass* sceneClass) = 0; // // virtual void restoreSubClassDataFromScene(const SceneAttributes* sceneAttributes, // const SceneClass* sceneClass) = 0; private: GapsAndMargins(const GapsAndMargins&); GapsAndMargins& operator=(const GapsAndMargins&); SceneClassAssistant* m_sceneAssistant; float m_tabMarginsLeft[BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS]; float m_tabMarginsRight[BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS]; float m_tabMarginsBottom[BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS]; float m_tabMarginsTop[BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS]; float m_surfaceMontageHorizontalGaps[BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_WINDOWS]; float m_surfaceMontageVerticalGaps[BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_WINDOWS]; float m_volumeMontageHorizontalGaps[BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_WINDOWS]; float m_volumeMontageVerticalGaps[BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_WINDOWS]; // ADD_NEW_MEMBERS_HERE }; #ifdef __GAPS_AND_MARGINS_DECLARE__ // #endif // __GAPS_AND_MARGINS_DECLARE__ } // namespace #endif //__GAPS_AND_MARGINS_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Brain/IdentificationManager.cxx000066400000000000000000000466651300200146000265700ustar00rootroot00000000000000 /*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 __IDENTIFICATION_MANAGER_DECLARE__ #include "IdentificationManager.h" #undef __IDENTIFICATION_MANAGER_DECLARE__ #include "Brain.h" #include "BrowserTabContent.h" #include "CaretAssert.h" #include "CaretLogger.h" #include "EventBrowserTabGetAll.h" #include "EventManager.h" #include "IdentifiedItemNode.h" #include "IdentifiedItemVoxel.h" #include "MathFunctions.h" #include "SceneClassAssistant.h" #include "SceneClass.h" #include "ScenePrimitive.h" using namespace caret; /** * \class caret::IdentificationManager * \brief Manages identified items. */ /** * Constructor. */ IdentificationManager::IdentificationManager() : SceneableInterface() { m_sceneAssistant = new SceneClassAssistant(); m_contralateralIdentificationEnabled = false; m_identificationSymbolColor = CaretColorEnum::WHITE; m_identificationContralateralSymbolColor = CaretColorEnum::LIME; m_identifcationSymbolSize = 3.0; m_identifcationMostRecentSymbolSize = 5.0; m_sceneAssistant->add("m_contralateralIdentificationEnabled", &m_contralateralIdentificationEnabled); m_sceneAssistant->add("m_identifcationSymbolSize", &m_identifcationSymbolSize); m_sceneAssistant->add("m_identifcationMostRecentSymbolSize", &m_identifcationMostRecentSymbolSize); m_sceneAssistant->add("m_identificationSymbolColor", &m_identificationSymbolColor); m_sceneAssistant->add("m_identificationContralateralSymbolColor", &m_identificationContralateralSymbolColor); removeAllIdentifiedItems(); } /** * Destructor. */ IdentificationManager::~IdentificationManager() { removeAllIdentifiedItems(); delete m_sceneAssistant; } /** * Add an identified item. * @param item * Identified item that is added. * NOTE: Takes ownership of this item and will delete, at the appropriate time. * If item is a node and contralateral identification is enabled, the contralateral * structure will be set in the node item. */ void IdentificationManager::addIdentifiedItem(IdentifiedItem* item) { CaretAssert(item); IdentifiedItemNode* nodeItem = dynamic_cast(item); if (nodeItem != NULL) { if (m_contralateralIdentificationEnabled) { const StructureEnum::Enum contralateralStructure = StructureEnum::getContralateralStructure(nodeItem->getStructure()); nodeItem->setContralateralStructure(contralateralStructure); } } addIdentifiedItemPrivate(item); } /** * Add an identified item. * @param item * Identified item that is added. * NOTE: Takes ownership of this item and will delete, at the appropriate time. */ void IdentificationManager::addIdentifiedItemPrivate(IdentifiedItem* item) { CaretAssert(item); m_mostRecentIdentifiedItem = item; m_identifiedItems.push_back(item); } /** * @return String containing identification text for information window. */ AString IdentificationManager::getIdentificationText() const { AString text; for (std::list::const_iterator iter = m_identifiedItems.begin(); iter != m_identifiedItems.end(); iter++) { const IdentifiedItem* item = *iter; if (text.isEmpty() == false) { text += "

"; } text += item->getText(); } return text; } /** * Remove all identification text. Node and voxels items have their text * removed and all other identification items are removed. */ void IdentificationManager::removeIdentificationText() { std::list idItemsToKeep; for (std::list::iterator iter = m_identifiedItems.begin(); iter != m_identifiedItems.end(); iter++) { IdentifiedItem* item = *iter; IdentifiedItemNode* nodeItem = dynamic_cast(item); IdentifiedItemVoxel* voxelItem = dynamic_cast(item); if ((nodeItem != NULL) || (voxelItem != NULL)) { item->clearText(); idItemsToKeep.push_back(item); } else { if (m_mostRecentIdentifiedItem == item) { m_mostRecentIdentifiedItem = NULL; } delete item; } } m_identifiedItems = idItemsToKeep; } /** * Get identified nodes for the surface with the given structure and * number of nodes. * * @param structure * The structure * @param surfaceNumberOfNodes * Number of nodes in surface. */ std::vector IdentificationManager::getNodeIdentifiedItemsForSurface(const StructureEnum::Enum structure, const int32_t surfaceNumberOfNodes) const { std::vector nodeItemsOut; for (std::list::const_iterator iter = m_identifiedItems.begin(); iter != m_identifiedItems.end(); iter++) { const IdentifiedItem* item = *iter; const IdentifiedItemNode* nodeItem = dynamic_cast(item); if (nodeItem != NULL) { if (nodeItem->isValid()) { if (nodeItem->getSurfaceNumberOfNodes() == surfaceNumberOfNodes) { bool useIt = false; if (nodeItem->getStructure() == structure) { useIt = true; } else if (nodeItem->getContralateralStructure() == structure) { useIt = true; } if (useIt) { IdentifiedItemNode nodeID(*nodeItem); const float* symbolRGB = CaretColorEnum::toRGB(m_identificationSymbolColor); nodeID.setSymbolRGB(symbolRGB); const float* contralateralSymbolRGB = CaretColorEnum::toRGB(m_identificationContralateralSymbolColor); nodeID.setContralateralSymbolRGB(contralateralSymbolRGB); if (item == m_mostRecentIdentifiedItem) { nodeID.setSymbolSize(m_identifcationMostRecentSymbolSize); } else { nodeID.setSymbolSize(m_identifcationSymbolSize); } nodeItemsOut.push_back(nodeID); } } } } } return nodeItemsOut; } /** * @return All identified voxels. */ std::vector IdentificationManager::getIdentifiedItemsForVolume() const { std::vector itemsOut; for (std::list::const_iterator iter = m_identifiedItems.begin(); iter != m_identifiedItems.end(); iter++) { const IdentifiedItem* item = *iter; const IdentifiedItemVoxel* voxelItem = dynamic_cast(item); if (voxelItem != NULL) { if (voxelItem->isValid()) { IdentifiedItemVoxel voxelID(*voxelItem); const float* symbolRGB = CaretColorEnum::toRGB(m_identificationSymbolColor); voxelID.setSymbolRGB(symbolRGB); voxelID.setSymbolSize(m_identifcationSymbolSize); itemsOut.push_back(voxelID); } } } return itemsOut; } /** * Remove any identification for the node in the surface with the given * structure and number of nodes. * * @param structure * The structure * @param surfaceNumberOfNodes * Number of nodes in surface. * @param nodeIndex * Index of the node. */ void IdentificationManager::removeIdentifiedNodeItem(const StructureEnum::Enum structure, const int32_t surfaceNumberOfNodes, const int32_t nodeIndex) { for (std::list::iterator iter = m_identifiedItems.begin(); iter != m_identifiedItems.end(); iter++) { IdentifiedItem* item = *iter; const IdentifiedItemNode* node = dynamic_cast(item); if (node != NULL) { if ((node->getStructure() == structure) || (node->getContralateralStructure() == structure)) { if ((node->getSurfaceNumberOfNodes() == surfaceNumberOfNodes) && (node->getNodeIndex() == nodeIndex)) { m_identifiedItems.erase(iter); delete item; return; } } } } } /** * Remove identified voxel at the given coordinate. * * @param xyz * Coordinates for voxel that is to be removed. */ void IdentificationManager::removeIdentifiedVoxelItem(const float xyz[3]) { const float tolerance = 0.01; for (std::list::iterator iter = m_identifiedItems.begin(); iter != m_identifiedItems.end(); iter++) { IdentifiedItem* item = *iter; const IdentifiedItemVoxel* voxel = dynamic_cast(item); if (voxel != NULL) { if (voxel->isValid()) { float voxelXYZ[3]; voxel->getXYZ(voxelXYZ); const float distSQ = MathFunctions::distanceSquared3D(xyz, voxelXYZ); if (distSQ < tolerance) { m_identifiedItems.erase(iter); delete item; return; } } } } } /** * Remove all identified items. */ void IdentificationManager::removeAllIdentifiedItems() { for (std::list::iterator iter = m_identifiedItems.begin(); iter != m_identifiedItems.end(); iter++) { IdentifiedItem* item = *iter; delete item; } m_identifiedItems.clear(); m_mostRecentIdentifiedItem = NULL; } /** * Remove all identification symbols while preserving text. * * Text from identification symbols for surface or volume are * inserted into new identified items and the symbol items * are removed. */ void IdentificationManager::removeAllIdentifiedSymbols() { std::list idItemsToKeep; for (std::list::iterator iter = m_identifiedItems.begin(); iter != m_identifiedItems.end(); iter++) { IdentifiedItem* item = *iter; IdentifiedItemNode* nodeItem = dynamic_cast(item); IdentifiedItemVoxel* voxelItem = dynamic_cast(item); IdentifiedItem* itemToKeep = NULL; if ((nodeItem != NULL) || (voxelItem != NULL)) { if (m_mostRecentIdentifiedItem == item) { m_mostRecentIdentifiedItem = NULL; } itemToKeep = new IdentifiedItem(item->getText()); delete item; } else { itemToKeep = item; } if (itemToKeep != NULL) { if (itemToKeep->getText().isEmpty()) { delete itemToKeep; itemToKeep = NULL; } else { idItemsToKeep.push_back(itemToKeep); } } } m_identifiedItems = idItemsToKeep; } /** * @return Status of contralateral identification. */ bool IdentificationManager::isContralateralIdentificationEnabled() const { return m_contralateralIdentificationEnabled; } /** * Set status of contralateral identification. * @param * New status. */ void IdentificationManager::setContralateralIdentificationEnabled(const bool enabled) { m_contralateralIdentificationEnabled = enabled; } /** * @return The size of the identification symbol */ float IdentificationManager::getIdentificationSymbolSize() const { return m_identifcationSymbolSize; } /** * Set the size of the identification symbol * @param symbolSize * New size of symbol. */ void IdentificationManager::setIdentificationSymbolSize(const float symbolSize) { m_identifcationSymbolSize = symbolSize; } /** * @return The size of the most recent identification symbol */ float IdentificationManager::getMostRecentIdentificationSymbolSize() const { return m_identifcationMostRecentSymbolSize; } /** * Set the size of the most recent identification symbol * @param symbolSize * New size of symbol. */ void IdentificationManager::setMostRecentIdentificationSymbolSize(const float symbolSize) { m_identifcationMostRecentSymbolSize = symbolSize; } /** * @return The color of the identification symbol. */ CaretColorEnum::Enum IdentificationManager::getIdentificationSymbolColor() const { return m_identificationSymbolColor; } /** * Set the color of the identification symbol. * @param color * New color. */ void IdentificationManager::setIdentificationSymbolColor(const CaretColorEnum::Enum color) { m_identificationSymbolColor = color; } /** * @return The color of the contralateral identification symbol. */ CaretColorEnum::Enum IdentificationManager::getIdentificationContralateralSymbolColor() const { return m_identificationContralateralSymbolColor; } /** * Set the color of the contralateral identification symbol. * @param color * New color. */ void IdentificationManager::setIdentificationContralateralSymbolColor(const CaretColorEnum::Enum color) { m_identificationContralateralSymbolColor = color; } /** * Create a scene for an instance of a class. * * @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. * * @return Pointer to SceneClass object representing the state of * this object. Under some circumstances a NULL pointer may be * returned. Caller will take ownership of returned object. */ SceneClass* IdentificationManager::saveToScene(const SceneAttributes* sceneAttributes, const AString& instanceName) { switch (sceneAttributes->getSceneType()) { case SceneTypeEnum::SCENE_TYPE_FULL: break; case SceneTypeEnum::SCENE_TYPE_GENERIC: break; } SceneClass* sceneClass = new SceneClass(instanceName, "IdentificationManager", 1); m_sceneAssistant->saveMembers(sceneAttributes, sceneClass); for (std::list::iterator iter = m_identifiedItems.begin(); iter != m_identifiedItems.end(); iter++) { IdentifiedItem* item = *iter; sceneClass->addClass(item->saveToScene(sceneAttributes, "identifiedItem")); } return sceneClass; } /** * Restore the state of an instance of a class. * * @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. May be NULL for some types of scenes. */ void IdentificationManager::restoreFromScene(const SceneAttributes* sceneAttributes, const SceneClass* sceneClass) { if (sceneClass == NULL) { return; } removeAllIdentifiedItems(); m_sceneAssistant->restoreMembers(sceneAttributes, sceneClass); const int32_t numChildren = sceneClass->getNumberOfObjects(); for (int32_t i = 0; i < numChildren; i++) { const SceneObject* so = sceneClass->getObjectAtIndex(i); if (so->getName() == "identifiedItem") { const SceneClass* sc = dynamic_cast(so); if (sc != NULL) { const AString className = sc->getClassName(); if (className == "IdentifiedItem") { IdentifiedItem* item = new IdentifiedItem(); item->restoreFromScene(sceneAttributes, sc); if (item->isValid()) { addIdentifiedItemPrivate(item); } else { delete item; } } else if (className == "IdentifiedItemNode") { IdentifiedItemNode* item = new IdentifiedItemNode(); item->restoreFromScene(sceneAttributes, sc); if (item->isValid()) { addIdentifiedItemPrivate(item); } else { delete item; } } else if (className == "IdentifiedItemVoxel") { IdentifiedItemVoxel* item = new IdentifiedItemVoxel(); item->restoreFromScene(sceneAttributes, sc); if (item->isValid()) { addIdentifiedItemPrivate(item); } else { delete item; } } else { const AString msg = ("IdentifiedItem from scene is invalid. " "Has a new IdentifiedItem type been added? " "Class name=" + className); CaretAssertMessage(0, msg); CaretLogSevere(msg); } } } } /* * "m_volumeIdentificationEnabled" was removed when volume identification * was made a tab property. If this item is present in the scene, * update volume ID status in all tabs. */ const ScenePrimitive* idPrimitive = sceneClass->getPrimitive("m_volumeIdentificationEnabled"); if (idPrimitive != NULL) { const bool volumeID = sceneClass->getBooleanValue("m_volumeIdentificationEnabled"); EventBrowserTabGetAll allTabs; EventManager::get()->sendEvent(allTabs.getPointer()); std::vector tabContent = allTabs.getAllBrowserTabs(); for (std::vector::iterator iter = tabContent.begin(); iter != tabContent.end(); iter++) { BrowserTabContent* btc = *iter; btc->setIdentificationUpdatesVolumeSlices(volumeID); } } } connectome-workbench-1.2.3+git41-gc4c6c90/src/Brain/IdentificationManager.h000066400000000000000000000104611300200146000261760ustar00rootroot00000000000000#ifndef __IDENTIFICATION_MANAGER_H__ #define __IDENTIFICATION_MANAGER_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 "CaretColorEnum.h" #include "SceneableInterface.h" #include "StructureEnum.h" namespace caret { class IdentifiedItem; class IdentifiedItemNode; class IdentifiedItemVoxel; class SceneClassAssistant; class IdentificationManager : public SceneableInterface { public: IdentificationManager(); virtual ~IdentificationManager(); void addIdentifiedItem(IdentifiedItem* item); AString getIdentificationText() const; std::vector getNodeIdentifiedItemsForSurface(const StructureEnum::Enum structure, const int32_t surfaceNumberOfNodes) const; std::vector getIdentifiedItemsForVolume() const; void removeIdentifiedNodeItem(const StructureEnum::Enum structure, const int32_t surfaceNumberOfNodes, const int32_t nodeIndex); void removeIdentifiedVoxelItem(const float xyz[3]); void removeIdentificationText(); void removeAllIdentifiedItems(); void removeAllIdentifiedSymbols(); bool isContralateralIdentificationEnabled() const; void setContralateralIdentificationEnabled(const bool enabled); float getIdentificationSymbolSize() const; void setIdentificationSymbolSize(const float symbolSize); float getMostRecentIdentificationSymbolSize() const; void setMostRecentIdentificationSymbolSize(const float symbolSize); CaretColorEnum::Enum getIdentificationSymbolColor() const; void setIdentificationSymbolColor(const CaretColorEnum::Enum color); CaretColorEnum::Enum getIdentificationContralateralSymbolColor() const; void setIdentificationContralateralSymbolColor(const CaretColorEnum::Enum color); virtual SceneClass* saveToScene(const SceneAttributes* sceneAttributes, const AString& instanceName); virtual void restoreFromScene(const SceneAttributes* sceneAttributes, const SceneClass* sceneClass); private: IdentificationManager(const IdentificationManager&); IdentificationManager& operator=(const IdentificationManager&); public: // ADD_NEW_METHODS_HERE private: void addIdentifiedItemPrivate(IdentifiedItem* item); // ADD_NEW_MEMBERS_HERE SceneClassAssistant* m_sceneAssistant; std::list m_identifiedItems; AString m_previousIdentifiedItemsText; IdentifiedItem* m_mostRecentIdentifiedItem; bool m_contralateralIdentificationEnabled; float m_identifcationSymbolSize; float m_identifcationMostRecentSymbolSize; CaretColorEnum::Enum m_identificationSymbolColor; CaretColorEnum::Enum m_identificationContralateralSymbolColor; }; #ifdef __IDENTIFICATION_MANAGER_DECLARE__ // #endif // __IDENTIFICATION_MANAGER_DECLARE__ } // namespace #endif //__IDENTIFICATION_MANAGER_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Brain/IdentificationStringBuilder.cxx000066400000000000000000000167111300200146000277600ustar00rootroot00000000000000/*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 "IdentificationStringBuilder.h" using namespace caret; /** * Constructor. * * */ IdentificationStringBuilder::IdentificationStringBuilder() : HtmlStringBuilder() { } /** * Destructor */ IdentificationStringBuilder::~IdentificationStringBuilder() { } /** * Add text to the string. A newline is added at the end. * A colon is added after the bold text but in normal text. * If "normalText" is an empty string, no text is output. * * @param indentFlag Indent the text. * @param boldText The bold text that starts the line. * @param normalText The normal text placed after the bold text. * */ void IdentificationStringBuilder::addLine( const bool indentFlag, const AString& boldText, const AString& normalText) { if (normalText.length() <= 0) { return; } if (indentFlag) { addIndent(); } addBold(boldText); add(": "); add(normalText); this->addLineBreak(); } /** * Add text to the string. A newline is added at the end. * If "normalText" is an empty string, no text is output. * * @param indentFlag Indent the text. * @param normalText The normal text placed after the bold text. * */ void IdentificationStringBuilder::addLine( const bool indentFlag, const AString& normalText) { if (normalText.length() <= 0) { return; } if (indentFlag) { addIndent(); } add(normalText); this->addLineBreak();} /** * Add text to the string. A newline is added at the end. * A colon is added after the bold text but in normal text. * * @param indentFlag Indent the text. * @param boldText The bold text that starts the line. * @param number The number placed after the bold text. * @param displayOnlyIfNumberIsNonZeroFlag - only display text if * number is non-zero. * */ void IdentificationStringBuilder::addLine( const bool indentFlag, const AString& boldText, const int32_t number, const bool displayOnlyIfNumberIsNonZeroFlag) { bool displayIt = true; if (displayOnlyIfNumberIsNonZeroFlag) { displayIt = (number != 0); } if (displayIt) { addLine(indentFlag, boldText, AString::number(number)); } } /** * Add text to the string. A newline is added at the end. * A colon is added after the bold text but in normal text. * * @param indentFlag Indent the text. * @param boldText The bold text that starts the line. * @param number The number placed after the bold text. * @param displayOnlyIfNumberIsNonZeroFlag - only display text if * number is non-zero. * */ void IdentificationStringBuilder::addLine( const bool indentFlag, const AString& boldText, const float number, const bool displayOnlyIfNumberIsNonZeroFlag) { bool displayIt = true; if (displayOnlyIfNumberIsNonZeroFlag) { displayIt = (number != 0.0f); } if (displayIt) { addLine(indentFlag, boldText, AString::number(number)); } } /** * Add text to the string. A newline is added at the end. * A colon is added after the bold text but in normal text. * * @param indentFlag Indent the text. * @param boldText The bold text that starts the line. * @param floatArray The float array placed after the bold text. * @param floatArrayNumberOfElements Number of elements in the array. * @param displayOnlyIfNonZeroElementInArrayFlag - only display text if * array contains at least one non-zero element. * */ void IdentificationStringBuilder::addLine( const bool indentFlag, const AString& boldText, const float floatArray[], const int floatArrayNumberOfElements, const bool displayOnlyIfNonZeroElementInArrayFlag) { bool displayIt = true; if (displayOnlyIfNonZeroElementInArrayFlag) { displayIt = false; for (int64_t i = 0; i < floatArrayNumberOfElements; i++) { if (floatArray[i] != 0.0f) { displayIt = true; break; } } } if (displayIt) { AString sbf; sbf.reserve(1024); for (int i = 0; i < floatArrayNumberOfElements; i++) { if (i == 0) { sbf.append("("); } else { sbf.append(", "); } sbf.append(AString::number(floatArray[i])); if (i == (floatArrayNumberOfElements - 1)) { sbf.append(")"); } } addLine(indentFlag, boldText, sbf); } } /** * Add a list of objects using the "toString()" method. If an * object in the list is null, a single blank character is placed * into the values list. * @param indentFlag true if indentation is needed. * @param boldText Bold text that is displayed before the values. * @param objectList List of objects that have the toString() * value displayed. * @param displayOnlyIfNonNullElementInArrayFlag Text is only * displayed if one of the items in the list is not null. * */ void IdentificationStringBuilder::addLine( const bool indentFlag, const AString& boldText, const std::vector& objectList, const bool displayOnlyIfNonNullElementInArrayFlag) { bool displayIt = true; if (displayOnlyIfNonNullElementInArrayFlag) { displayIt = false; const int64_t numObjects = static_cast(objectList.size()); for (int64_t i = 0; i < numObjects; i++) { if (objectList[i] != NULL) { displayIt = true; break; } } } if (displayIt) { AString sbf; sbf.reserve(2048); int numItems = objectList.size(); for (int i = 0; i < numItems; i++) { if (i == 0) { sbf.append("("); } else { sbf.append(", "); } const CaretObject* obj = objectList[i]; if (obj != NULL) { sbf.append(obj->toString()); } if (i == (numItems - 1)) { sbf.append(")"); } } addLine(indentFlag, boldText, sbf); } } /** * Indent the line. */ void IdentificationStringBuilder::addIndent() { this->addSpaces(4); } connectome-workbench-1.2.3+git41-gc4c6c90/src/Brain/IdentificationStringBuilder.h000066400000000000000000000053751300200146000274110ustar00rootroot00000000000000#ifndef __IDENTIFICATIONSTRINGBUILDER_H__ #define __IDENTIFICATIONSTRINGBUILDER_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 "HtmlStringBuilder.h" #include #include namespace caret { class IdentificationStringBuilder : public HtmlStringBuilder { public: IdentificationStringBuilder(); virtual ~IdentificationStringBuilder(); private: IdentificationStringBuilder(const IdentificationStringBuilder& o); IdentificationStringBuilder& operator=(const IdentificationStringBuilder& o); public: void addLine( const bool indentFlag, const AString& boldText, const AString& normalText); void addLine( const bool indentFlag, const AString& normalText); void addLine( const bool indentFlag, const AString& boldText, const int32_t number, const bool displayOnlyIfNumberIsNonZeroFlag); void addLine( const bool indentFlag, const AString& boldText, const float number, const bool displayOnlyIfNumberIsNonZeroFlag); void addLine(const bool indentFlag, const AString& boldText, const float floatArray[], const int floatArrayNumberOfElements, const bool displayOnlyIfNonZeroElementInArrayFlag); void addLine( const bool indentFlag, const AString& boldText, const std::vector& objectList, const bool displayOnlyIfNonNullElementInArrayFlag); void addIndent(); }; } // namespace #endif // __IDENTIFICATIONSTRINGBUILDER_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Brain/IdentificationTextGenerator.cxx000066400000000000000000001414331300200146000277760ustar00rootroot00000000000000 /*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 __IDENTIFICATION_TEXT_GENERATOR_DECLARE__ #include "IdentificationTextGenerator.h" #undef __IDENTIFICATION_TEXT_GENERATOR_DECLARE__ #include "Border.h" #include "Brain.h" #include "BrainStructure.h" #include "BrowserTabContent.h" #include "CaretAssert.h" #include "CaretMappableDataFile.h" #include "ChartDataCartesian.h" #include "ChartDataSource.h" #include "ChartModelDataSeries.h" #include "ChartableMatrixInterface.h" #include "CiftiMappableConnectivityMatrixDataFile.h" #include "CiftiMappableDataFile.h" #include "CaretVolumeExtension.h" #include "EventBrowserTabGetAll.h" #include "EventManager.h" #include "FileInformation.h" #include "FociFile.h" #include "Focus.h" #include "GiftiLabel.h" #include "ImageFile.h" #include "OverlaySet.h" #include "SelectionItemBorderSurface.h" #include "SelectionItemChartDataSeries.h" #include "SelectionItemChartFrequencySeries.h" #include "SelectionItemChartMatrix.h" #include "SelectionItemCiftiConnectivityMatrixRowColumn.h" #include "SelectionItemChartTimeSeries.h" #include "SelectionItemFocusSurface.h" #include "SelectionItemFocusVolume.h" #include "SelectionItemImage.h" #include "SelectionItemSurfaceNode.h" #include "SelectionItemVoxel.h" #include "SelectionManager.h" #include "IdentificationStringBuilder.h" #include "LabelFile.h" #include "MetricFile.h" #include "Surface.h" #include "SurfaceProjectedItem.h" #include "SurfaceProjectionBarycentric.h" #include "SurfaceProjectionVanEssen.h" #include "VolumeFile.h" using namespace caret; /** * \class IdentificationTextGenerator * \brief Creates text describing selected data. * * Examine the selected data and generate descriptive text. */ /** * Constructor. */ IdentificationTextGenerator::IdentificationTextGenerator() : CaretObject() { } /** * Destructor. */ IdentificationTextGenerator::~IdentificationTextGenerator() { } /** * Create identification text from selection in the identification manager. * @param idManager * Identification manager containing selection. * @param brain * The brain. */ AString IdentificationTextGenerator::createIdentificationText(const SelectionManager* idManager, const Brain* brain) const { CaretAssert(idManager); CaretAssert(brain); IdentificationStringBuilder idText; const SelectionItemSurfaceNode* surfaceID = idManager->getSurfaceNodeIdentification(); this->generateSurfaceIdentificationText(idText, brain, surfaceID); this->generateSurfaceBorderIdentifcationText(idText, idManager->getSurfaceBorderIdentification()); this->generateSurfaceFociIdentifcationText(idText, idManager->getSurfaceFocusIdentification()); this->generateVolumeFociIdentifcationText(idText, idManager->getVolumeFocusIdentification()); this->generateVolumeIdentificationText(idText, brain, idManager->getVoxelIdentification()); this->generateChartDataSeriesIdentificationText(idText, idManager->getChartDataSeriesIdentification()); this->generateChartFrequencySeriesIdentificationText(idText, idManager->getChartFrequencySeriesIdentification()); this->generateChartTimeSeriesIdentificationText(idText, idManager->getChartTimeSeriesIdentification()); this->generateChartMatrixIdentificationText(idText, idManager->getChartMatrixIdentification()); this->generateCiftiConnectivityMatrixIdentificationText(idText, idManager->getCiftiConnectivityMatrixRowColumnIdentification()); this->generateImageIdentificationText(idText, idManager->getImageIdentification()); return idText.toString(); } /** * Generate identification text for volume voxel identification. * * @param idText * String builder for identification text. * @param brain * The brain. * @param idVolumeVoxel * Information for volume voxel ID. */ void IdentificationTextGenerator::generateVolumeIdentificationText(IdentificationStringBuilder& idText, const Brain* brain, const SelectionItemVoxel* idVolumeVoxel) const { if (idVolumeVoxel->isValid() == false) { return; } int64_t ijk[3]; const VolumeMappableInterface* idVolumeFile = idVolumeVoxel->getVolumeFile(); idVolumeVoxel->getVoxelIJK(ijk); float x, y, z; idVolumeFile->indexToSpace(ijk[0], ijk[1], ijk[2], x, y, z); idText.addLine(false, "Voxel XYZ (" + AString::number(x) + ", " + AString::number(y) + ", " + AString::number(z) + ")"); const float xyz[3] = { x, y, z }; /* * Get all volume files */ std::vector volumeInterfaces; const int32_t numVolumeFiles = brain->getNumberOfVolumeFiles(); for (int32_t i = 0; i < numVolumeFiles; i++) { const VolumeFile* vf = brain->getVolumeFile(i); volumeInterfaces.push_back(vf); } /* * Get the CIFTI files that are volume mappable */ 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) { if (cmdf->isVolumeMappable()) { volumeInterfaces.push_back(cmdf); } } } /* * In first loop, show values for 'idVolumeFile' (the underlay volume) * In second loop, show values for all other volume files */ const int32_t numberOfVolumeMappableFiles = static_cast(volumeInterfaces.size()); for (int32_t iLoop = 0; iLoop < 2; iLoop++) { for (int32_t i = 0; i < numberOfVolumeMappableFiles; i++) { const VolumeMappableInterface* volumeInterfaceFile = volumeInterfaces[i]; const VolumeFile* volumeFile = dynamic_cast(volumeInterfaceFile); const CiftiMappableDataFile* ciftiFile = dynamic_cast(volumeInterfaceFile); CaretAssert((volumeFile != NULL) || (ciftiFile != NULL)); const CaretMappableDataFile* caretMappableDataFile = dynamic_cast(volumeInterfaceFile); CaretAssert(caretMappableDataFile != NULL); if (volumeInterfaceFile == idVolumeFile) { if (iLoop != 0) { continue; } } else if (iLoop == 0) { continue; } int64_t vfI, vfJ, vfK; volumeInterfaceFile->enclosingVoxel(x, y, z, vfI, vfJ, vfK); if (volumeInterfaceFile->indexValid(vfI, vfJ, vfK)) { if (volumeFile != NULL) { AString boldText = caretMappableDataFile->getFileNameNoPath(); boldText += (" IJK (" + AString::number(vfI) + ", " + AString::number(vfJ) + ", " + AString::number(vfK) + ") "); AString text; const int32_t numMaps = caretMappableDataFile->getNumberOfMaps(); for (int jMap = 0; jMap < numMaps; jMap++) { if (jMap > 0) { text += " "; } if (volumeFile != NULL) { if (volumeFile->getType() == SubvolumeAttributes::LABEL) { const int32_t labelIndex = static_cast(volumeFile->getValue(vfI, vfJ, vfK, jMap)); const GiftiLabelTable* glt = volumeFile->getMapLabelTable(jMap); const GiftiLabel* gl = glt->getLabel(labelIndex); if (gl != NULL) { text += gl->getName(); } else { text += ("LABLE_MISSING_FOR_INDEX=" + AString::number(labelIndex)); } } else if (volumeFile->getType() == SubvolumeAttributes::RGB) { if (volumeFile->getNumberOfComponents() == 4) { text += ("RGBA(" + AString::number(volumeFile->getValue(vfI, vfJ, vfK, jMap, 0)) + "," + AString::number(volumeFile->getValue(vfI, vfJ, vfK, jMap, 1)) + "," + AString::number(volumeFile->getValue(vfI, vfJ, vfK, jMap, 2)) + "," + AString::number(volumeFile->getValue(vfI, vfJ, vfK, jMap, 3)) + ")"); } else if (volumeFile->getNumberOfComponents() == 3) { text += ("RGB(" + AString::number(volumeFile->getValue(vfI, vfJ, vfK, jMap, 0)) + "," + AString::number(volumeFile->getValue(vfI, vfJ, vfK, jMap, 1)) + "," + AString::number(volumeFile->getValue(vfI, vfJ, vfK, jMap, 2)) + ")"); } } else { text += AString::number(volumeFile->getValue(vfI, vfJ, vfK, jMap)); } } else if (ciftiFile != NULL) { } } idText.addLine(true, boldText, text); } else if (ciftiFile != NULL) { if (ciftiFile->isEmpty() == false) { const int numMaps = ciftiFile->getNumberOfMaps(); std::vector mapIndices; for (int32_t i = 0; i < numMaps; i++) { mapIndices.push_back(i); } /* * Limit dense scalar and data series to maps selected in the overlays * from all tabs. */ bool limitMapIndicesFlag = false; switch (ciftiFile->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: limitMapIndicesFlag = true; break; case DataFileTypeEnum::CONNECTIVITY_DENSE_TIME_SERIES: limitMapIndicesFlag = true; 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: limitMapIndicesFlag = true; break; case DataFileTypeEnum::CONNECTIVITY_PARCEL_SCALAR: limitMapIndicesFlag = true; break; case DataFileTypeEnum::CONNECTIVITY_PARCEL_SERIES: limitMapIndicesFlag = true; break; case DataFileTypeEnum::CONNECTIVITY_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: CaretAssert(0); break; case DataFileTypeEnum::VOLUME: break; } if (limitMapIndicesFlag) { 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]; if (ciftiFile->getVolumeVoxelIdentificationForMaps(mapIndices, xyz, voxelIJK, textValue)) { AString boldText = (DataFileTypeEnum::toOverlayTypeName(ciftiFile->getDataFileType()) + " " + ciftiFile->getFileNameNoPath() + " IJK (" + AString::number(voxelIJK[0]) + ", " + AString::number(voxelIJK[1]) + ", " + AString::number(voxelIJK[2]) + ") "); idText.addLine(true, boldText, textValue); } } } } } } } /** * Generate identification text for a surface node identification. * @param idText * String builder for identification text. * @param brain * The brain. * @param browserTabContent * Content of the browser tab. * @param idSurfaceNode * Information for surface node ID. */ void IdentificationTextGenerator::generateSurfaceIdentificationText(IdentificationStringBuilder& idText, const Brain* brain, const SelectionItemSurfaceNode* idSurfaceNode) const { const Surface* surface = idSurfaceNode->getSurface(); const int32_t nodeNumber = idSurfaceNode->getNodeNumber(); if ((surface != NULL) && (nodeNumber >= 0)) { AString surfaceID; surfaceID += ("VERTEX " + StructureEnum::toGuiName(surface->getStructure())); idText.addLine(false, surfaceID, nodeNumber, false); const float* xyz = surface->getCoordinate(nodeNumber); idText.addLine(true, SurfaceTypeEnum::toGuiName(surface->getSurfaceType()).toUpper() + " XYZ: " + AString::number(xyz[0]) + ", " + AString::number(xyz[1]) + ", " + AString::number(xyz[2])); 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(); ciftiMapIter != allCiftiMappableDataFiles.end(); ciftiMapIter++) { const CiftiMappableDataFile* cmdf = *ciftiMapIter; AString boldText = (DataFileTypeEnum::toOverlayTypeName(cmdf->getDataFileType()) + " " + cmdf->getFileNameNoPath()); std::vector mapIndices; for (int32_t i = 0; i < cmdf->getNumberOfMaps(); i++) { mapIndices.push_back(i); } /* * Limit dense scalar and data series to maps selected in the overlays * from all tabs. */ bool limitMapIndicesFlag = false; switch (cmdf->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: limitMapIndicesFlag = true; break; case DataFileTypeEnum::CONNECTIVITY_DENSE_TIME_SERIES: limitMapIndicesFlag = true; 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: limitMapIndicesFlag = true; break; case DataFileTypeEnum::CONNECTIVITY_PARCEL_SCALAR: limitMapIndicesFlag = true; break; case DataFileTypeEnum::CONNECTIVITY_PARCEL_SERIES: limitMapIndicesFlag = true; break; case DataFileTypeEnum::CONNECTIVITY_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: CaretAssert(0); break; case DataFileTypeEnum::VOLUME: break; } if (limitMapIndicesFlag) { getMapIndicesOfFileUsedInOverlays(cmdf, mapIndices); } AString textValue; const bool valid = cmdf->getSurfaceNodeIdentificationForMaps(mapIndices, surface->getStructure(), nodeNumber, surface->getNumberOfNodes(), textValue); if (valid) { idText.addLine(true, 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); // } // } // } // } // } } const int32_t numLabelFiles = brainStructure->getNumberOfLabelFiles(); for (int32_t i = 0; i < numLabelFiles; i++) { const LabelFile* lf = brainStructure->getLabelFile(i); AString boldText = "LABEL " + lf->getFileNameNoPath() + ":"; AString text; const int numMaps = lf->getNumberOfMaps(); for (int32_t j = 0; j < numMaps; j++) { AString labelName = lf->getLabelName(nodeNumber, j); if (labelName.isEmpty()) { labelName = ("Map-" + AString::number(j + 1)); } text += (" " + labelName); } idText.addLine(true, boldText, text); } const int32_t numMetricFiles = brainStructure->getNumberOfMetricFiles(); for (int32_t i = 0; i < numMetricFiles; i++) { const MetricFile* mf = brainStructure->getMetricFile(i); AString boldText = "METRIC " + mf->getFileNameNoPath() + ":"; AString text; const int numMaps = mf->getNumberOfMaps(); for (int32_t j = 0; j < numMaps; j++) { text += (" " + AString::number(mf->getValue(nodeNumber, j))); } idText.addLine(true, boldText, text); } } } /** * Find the usage of the file's maps in all overlays. * * @param caretMappableDataFile * The file whose usage is desired. * @param mapIndicesOut * Indices of maps of the file that are used in overlays. */ void IdentificationTextGenerator::getMapIndicesOfFileUsedInOverlays(const CaretMappableDataFile* caretMappableDataFile, std::vector& mapIndicesOut) const { mapIndicesOut.clear(); EventBrowserTabGetAll allTabsEvent; EventManager::get()->sendEvent(allTabsEvent.getPointer()); const std::vector allTabs = allTabsEvent.getAllBrowserTabs(); for (std::vector::const_iterator tabIter = allTabs.begin(); tabIter != allTabs.end(); tabIter++) { BrowserTabContent* tabContent = *tabIter; OverlaySet* overlaySet = tabContent->getOverlaySet(); if (overlaySet != NULL) { std::vector mapIndices; overlaySet->getSelectedMapIndicesForFile(caretMappableDataFile, false, // true => enabled overlays mapIndices); mapIndicesOut.insert(mapIndicesOut.end(), mapIndices.begin(), mapIndices.end()); } } /* * Sort and remove all duplicates */ if (mapIndicesOut.empty() == false) { std::sort(mapIndicesOut.begin(), mapIndicesOut.end()); std::vector::iterator uniqueIter = std::unique(mapIndicesOut.begin(), mapIndicesOut.end()); mapIndicesOut.resize(std::distance(mapIndicesOut.begin(), uniqueIter)); } } /** * Generate identification text for a data series chart. * @param idText * String builder for identification text. * @param idChartDataSeries * Information for chart id. */ void IdentificationTextGenerator::generateChartDataSeriesIdentificationText(IdentificationStringBuilder& idText, const SelectionItemChartDataSeries* idChartDataSeries) const { if (idChartDataSeries->isValid()) { //const ChartModelDataSeries* chartModelDataSeries = idChartDataSeries->getChartModelDataSeries(); const ChartDataCartesian* chartDataCartesian = idChartDataSeries->getChartDataCartesian(); const ChartDataSource* chartDataSource = chartDataCartesian->getChartDataSource(); generateChartDataSourceText(idText, "DATA SERIES CHART", chartDataSource); } } /** * Generate identification text for a data series chart. * @param idText * String builder for identification text. * @param idChartDataSeries * Information for chart id. */ void IdentificationTextGenerator::generateChartFrequencySeriesIdentificationText(IdentificationStringBuilder& idText, const SelectionItemChartFrequencySeries* idChartFrequencySeries) const { if (idChartFrequencySeries->isValid()) { const ChartDataCartesian* chartDataCartesian = idChartFrequencySeries->getChartDataCartesian(); const ChartDataSource* chartDataSource = chartDataCartesian->getChartDataSource(); generateChartDataSourceText(idText, "FREQUENCY SERIES CHART", chartDataSource); } } /** * Generate identification text for a matrix chart. * @param idText * String builder for identification text. * @param idChartMatrix * Information for matrix chart id. */ void IdentificationTextGenerator::generateChartMatrixIdentificationText(IdentificationStringBuilder& idText, const SelectionItemChartMatrix* idChartMatrix) const { if (idChartMatrix->isValid()) { const ChartableMatrixInterface* chartMatrixInterface = idChartMatrix->getChartableMatrixInterface(); const CaretMappableDataFile* caretMappableDataFile = chartMatrixInterface->getMatrixChartCaretMappableDataFile(); const int32_t rowIndex = idChartMatrix->getMatrixRowIndex(); const int32_t columnIndex = idChartMatrix->getMatrixColumnIndex(); AString rowName; AString columnName; AString cellValue; const bool validData = chartMatrixInterface->getMatrixCellAttributes(rowIndex, columnIndex, cellValue, rowName, columnName); AString boldText("MATRIX CHART"); idText.addLine(false, boldText, caretMappableDataFile->getFileNameNoPath()); if (validData) { idText.addLine(true, ("Row " + AString::number(rowIndex + 1)), rowName); idText.addLine(true, ("Column " + AString::number(columnIndex + 1)), columnName); idText.addLine(true, "Value", cellValue); } } } /** * Generate identification text for a CIFTI Connectivity Matrix Row/Column * @param idText * String builder for identification text. * @param idCiftiConnMatrix * Information for CIFTI Connectivity Matrix Row/Column. */ void IdentificationTextGenerator::generateCiftiConnectivityMatrixIdentificationText(IdentificationStringBuilder& idText, const SelectionItemCiftiConnectivityMatrixRowColumn* idCiftiConnMatrix) const { if (idCiftiConnMatrix->isValid()) { const CiftiMappableConnectivityMatrixDataFile* connMatrixFile = idCiftiConnMatrix->getCiftiConnectivityMatrixFile(); const int32_t rowIndex = idCiftiConnMatrix->getMatrixRowIndex(); const int32_t colIndex = idCiftiConnMatrix->getMatrixColumnIndex(); AString boldText("MATRIX ROW/COLUMN"); idText.addLine(false, boldText, connMatrixFile->getFileNameNoPath()); AString rowName = ""; AString colName = ""; bool validData = true; if (validData) { if (rowIndex >= 0) { idText.addLine(true, ("Row " + AString::number(rowIndex + 1)), rowName); } if (colIndex >= 0) { idText.addLine(true, ("Column " + AString::number(colIndex + 1)), colName); } } } } /** * Generate identification text for chart data source. * @param idText * String builder for identification text. * @param typeOfChartText * Text describing the type of chart. * @param chartDataSource * Source of chart data. */ void IdentificationTextGenerator::generateChartDataSourceText(IdentificationStringBuilder& idText, const AString& typeOfChartText, const ChartDataSource* chartDataSource) const { AString chartFileName = chartDataSource->getChartableFileName(); if (! chartFileName.isEmpty()) { chartFileName = FileInformation(chartFileName).getFileName(); } idText.addLine(false, typeOfChartText, chartDataSource->getChartableFileName()); switch (chartDataSource->getDataSourceMode()) { case ChartDataSourceModeEnum::CHART_DATA_SOURCE_MODE_INVALID: break; case ChartDataSourceModeEnum::CHART_DATA_SOURCE_MODE_FILE_ROW: { AString fileName; int32_t rowIndex; chartDataSource->getFileRow(fileName, rowIndex); idText.addLine(true, "File", fileName); idText.addLine(true, "Row", AString::number(rowIndex + 1)); } break; case ChartDataSourceModeEnum::CHART_DATA_SOURCE_MODE_SURFACE_NODE_INDEX: { AString structureName; int32_t numberOfNodes; int32_t nodeIndex; chartDataSource->getSurfaceNode(structureName, numberOfNodes, nodeIndex); idText.addLine(true, "Structure", structureName); idText.addLine(true, "Vertex Index", AString::number(nodeIndex)); } break; case ChartDataSourceModeEnum::CHART_DATA_SOURCE_MODE_SURFACE_NODE_INDICES_AVERAGE: { AString structureName; int32_t numberOfNodes; std::vector nodeIndices; chartDataSource->getSurfaceNodeAverage(structureName, numberOfNodes, nodeIndices); idText.addLine(true, "Structure", structureName); idText.addLine(true, "Vertex Average Count", AString::number(nodeIndices.size())); } break; case ChartDataSourceModeEnum::CHART_DATA_SOURCE_MODE_VOXEL_IJK: { float voxelXYZ[3]; chartDataSource->getVolumeVoxel(voxelXYZ); idText.addLine(true, "Voxel XYZ", AString::fromNumbers(voxelXYZ, 3, ",")); } break; } } /** * Generate identification text for a time series chart. * @param idText * String builder for identification text. * @param idChartTimeSeries * Information for chart id. */ void IdentificationTextGenerator::generateChartTimeSeriesIdentificationText(IdentificationStringBuilder& idText, const SelectionItemChartTimeSeries* idChartTimeSeries) const { if (idChartTimeSeries->isValid()) { //const ChartModelDataSeries* chartModelDataSeries = idChartDataSeries->getChartModelDataSeries(); const ChartDataCartesian* chartDataCartesian = idChartTimeSeries->getChartDataCartesian(); const ChartDataSource* chartDataSource = chartDataCartesian->getChartDataSource(); generateChartDataSourceText(idText, "TIME SERIES CHART", chartDataSource); } } /** * Generate identification text for a surface border identification. * @param idText * String builder for identification text. * @param idSurfaceBorder * Information for surface border ID. */ void IdentificationTextGenerator::generateSurfaceBorderIdentifcationText(IdentificationStringBuilder& idText, const SelectionItemBorderSurface* idSurfaceBorder) const { if (idSurfaceBorder->isValid()) { const Border* border = idSurfaceBorder->getBorder(); const SurfaceProjectedItem* spi = border->getPoint(idSurfaceBorder->getBorderPointIndex()); float xyz[3]; spi->getProjectedPosition(*idSurfaceBorder->getSurface(), xyz, false); AString boldText = ("BORDER " + StructureEnum::toGuiName(spi->getStructure()) + " Name: " + border->getName()); if (border->getClassName().isEmpty() == false) { boldText += (" ClassName: " + border->getClassName() + ": "); } const AString text = ("(" + AString::number(idSurfaceBorder->getBorderIndex()) + "," + AString::number(idSurfaceBorder->getBorderPointIndex()) + ") (" + AString::fromNumbers(xyz, 3, ",") + ")"); idText.addLine(false, boldText, text); } } /** * Generate identification text for a surface focus identification. * @param idText * String builder for identification text. * @param idSurfaceFocus * Information for surface focus ID. */void IdentificationTextGenerator::generateSurfaceFociIdentifcationText(IdentificationStringBuilder& idText, const SelectionItemFocusSurface* idSurfaceFocus) const { if (idSurfaceFocus->isValid()) { const Focus* focus = idSurfaceFocus->getFocus(); idText.addLine(false, "FOCUS", focus->getName()); idText.addLine(true, "Index", AString::number(idSurfaceFocus->getFocusIndex())); const int32_t projectionIndex = idSurfaceFocus->getFocusProjectionIndex(); const SurfaceProjectedItem* spi = focus->getProjection(projectionIndex); float xyzProj[3]; spi->getProjectedPosition(*idSurfaceFocus->getSurface(), xyzProj, false); float xyzStereo[3]; spi->getStereotaxicXYZ(xyzStereo); idText.addLine(true, "Structure", StructureEnum::toGuiName(spi->getStructure())); if (spi->isStereotaxicXYZValid()) { idText.addLine(true, "XYZ (Stereotaxic)", xyzStereo, 3, true); } else { idText.addLine(true, "XYZ (Stereotaxic)", "Invalid"); } bool projValid = false; AString xyzProjName = "XYZ (Projected)"; if (spi->getBarycentricProjection()->isValid()) { xyzProjName = "XYZ (Projected to Triangle)"; projValid = true; } else if (spi->getVanEssenProjection()->isValid()) { xyzProjName = "XYZ (Projected to Edge)"; projValid = true; } if (projValid) { idText.addLine(true, xyzProjName, xyzProj, 3, true); } else { idText.addLine(true, xyzProjName, "Invalid"); } const int32_t numberOfProjections = focus->getNumberOfProjections(); for (int32_t i = 0; i < numberOfProjections; i++) { if (i != projectionIndex) { const SurfaceProjectedItem* proj = focus->getProjection(i); AString projTypeName = ""; if (proj->getBarycentricProjection()->isValid()) { projTypeName = "Triangle"; } else if (proj->getVanEssenProjection()->isValid()) { projTypeName = "Edge"; } if (projTypeName.isEmpty() == false) { const AString txt = (StructureEnum::toGuiName(proj->getStructure()) + " (" + projTypeName + ")"); idText.addLine(true, "Ambiguous Projection", txt); } } } idText.addLine(true, "Area", focus->getArea()); idText.addLine(true, "Class Name", focus->getClassName()); idText.addLine(true, "Comment", focus->getComment()); idText.addLine(true, "Extent", focus->getExtent(), true); idText.addLine(true, "Geography", focus->getGeography()); idText.addLine(true, "Region of Interest", focus->getRegionOfInterest()); idText.addLine(true, "Statistic", focus->getStatistic()); } } /** * Generate identification text for a volume focus identification. * @param idText * String builder for identification text. * @param idVolumeFocus * Information for surface focus ID. */ void IdentificationTextGenerator::generateVolumeFociIdentifcationText(IdentificationStringBuilder& idText, const SelectionItemFocusVolume* idVolumeFocus) const { if (idVolumeFocus->isValid()) { const Focus* focus = idVolumeFocus->getFocus(); const SurfaceProjectedItem* spi = focus->getProjection(idVolumeFocus->getFocusProjectionIndex()); float xyzVolume[3]; spi->getVolumeXYZ(xyzVolume); float xyzStereo[3]; spi->getStereotaxicXYZ(xyzStereo); idText.addLine(false, "FOCUS", focus->getName()); idText.addLine(true, "Index", AString::number(idVolumeFocus->getFocusIndex())); idText.addLine(true, "Structure", StructureEnum::toGuiName(spi->getStructure())); if (spi->isStereotaxicXYZValid()) { idText.addLine(true, "XYZ (Stereotaxic)", xyzStereo, 3, true); } else { idText.addLine(true, "XYZ (Stereotaxic)", "Invalid"); } AString xyzVolumeName = "XYZ (Volume)"; idText.addLine(true, xyzVolumeName, xyzVolume, 3, true); idText.addLine(true, "Area", focus->getArea()); idText.addLine(true, "Class Name", focus->getClassName()); idText.addLine(true, "Comment", focus->getComment()); idText.addLine(true, "Extent", focus->getExtent(), true); idText.addLine(true, "Geography", focus->getGeography()); idText.addLine(true, "Region of Interest", focus->getRegionOfInterest()); idText.addLine(true, "Statistic", focus->getStatistic()); } } /** * Generate identification text for image identification. * @param idText * String builder for identification text. * @param idImage * Information for image ID. */ void IdentificationTextGenerator::generateImageIdentificationText(IdentificationStringBuilder& idText, const SelectionItemImage* idImage) const { if (idImage->isValid()) { AString text = ("Image " + idImage->getImageFile()->getFileNameNoPath() + " Pixel IJ (" + AString::number(idImage->getPixelI()) + "," + AString::number(idImage->getPixelJ()) + ")"); uint8_t pixelRGBA[4] = { 0, 0, 0, 0 }; idImage->getPixelRGBA(pixelRGBA); text.append(" RGBA (" + AString::fromNumbers(pixelRGBA, 4, ",") + ")"); idText.addLine(false, text); } } /** * Get a description of this object's content. * @return String describing this object's content. */ AString IdentificationTextGenerator::toString() const { return "IdentificationTextGenerator"; } connectome-workbench-1.2.3+git41-gc4c6c90/src/Brain/IdentificationTextGenerator.h000066400000000000000000000122121300200146000274130ustar00rootroot00000000000000#ifndef __IDENTIFICATION_TEXT_GENERATOR__H_ #define __IDENTIFICATION_TEXT_GENERATOR__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" namespace caret { class Brain; class BrowserTabContent; class CaretMappableDataFile; class ChartDataSource; class SelectionItemBorderSurface; class SelectionItemChartDataSeries; class SelectionItemChartFrequencySeries; class SelectionItemChartMatrix; class SelectionItemCiftiConnectivityMatrixRowColumn; class SelectionItemChartTimeSeries; class SelectionItemFocusSurface; class SelectionItemFocusVolume; class SelectionItemImage; class SelectionItemSurfaceNode; class SelectionItemVoxel; class SelectionManager; class IdentificationStringBuilder; class IdentificationTextGenerator : public CaretObject { public: IdentificationTextGenerator(); virtual ~IdentificationTextGenerator(); AString createIdentificationText(const SelectionManager* idManager, const Brain* brain) const; private: IdentificationTextGenerator(const IdentificationTextGenerator&); IdentificationTextGenerator& operator=(const IdentificationTextGenerator&); public: virtual AString toString() const; private: void generateSurfaceBorderIdentifcationText(IdentificationStringBuilder& idText, const SelectionItemBorderSurface* idSurfaceBorder) const; void generateSurfaceFociIdentifcationText(IdentificationStringBuilder& idText, const SelectionItemFocusSurface* idSurfaceFocus) const; void generateVolumeFociIdentifcationText(IdentificationStringBuilder& idText, const SelectionItemFocusVolume* idVolumeFocus) const; void generateSurfaceIdentificationText(IdentificationStringBuilder& idText, const Brain* brain, const SelectionItemSurfaceNode* idSurfaceNode) const; void generateImageIdentificationText(IdentificationStringBuilder& idText, const SelectionItemImage* idImage) const; void generateVolumeIdentificationText(IdentificationStringBuilder& idText, const Brain* brain, const SelectionItemVoxel* idVolumeVoxel) const; void generateChartDataSeriesIdentificationText(IdentificationStringBuilder& idText, const SelectionItemChartDataSeries* idChartDataSeries) const; void generateChartFrequencySeriesIdentificationText(IdentificationStringBuilder& idText, const SelectionItemChartFrequencySeries* idChartFrequencySeries) const; void generateChartMatrixIdentificationText(IdentificationStringBuilder& idText, const SelectionItemChartMatrix* idChartMatrix) const; void generateCiftiConnectivityMatrixIdentificationText(IdentificationStringBuilder& idText, const SelectionItemCiftiConnectivityMatrixRowColumn* idCiftiConnMatrix) const; void generateChartTimeSeriesIdentificationText(IdentificationStringBuilder& idText, const SelectionItemChartTimeSeries* idChartTimeSeries) const; void getMapIndicesOfFileUsedInOverlays(const CaretMappableDataFile* caretMappableDataFile, std::vector& mapIndicesOut) const; void generateChartDataSourceText(IdentificationStringBuilder& idText, const AString& typeOfChartText, const ChartDataSource* chartDataSource) const; }; #ifdef __IDENTIFICATION_TEXT_GENERATOR_DECLARE__ // #endif // __IDENTIFICATION_TEXT_GENERATOR_DECLARE__ } // namespace #endif //__IDENTIFICATION_TEXT_GENERATOR__H_ connectome-workbench-1.2.3+git41-gc4c6c90/src/Brain/IdentificationWithColor.cxx000066400000000000000000000125751300200146000271210ustar00rootroot00000000000000 /*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 __IDENTIFICATION_WITH_COLOR_DECLARE__ #include "IdentificationWithColor.h" #undef __IDENTIFICATION_WITH_COLOR_DECLARE__ #include "CaretAssert.h" using namespace caret; /** * \class IdentificationWithColor * \brief Assists with identification of items using colors. * * To perform identification of items, an identifier * is encoded as RGB colors. The color present at * the location of identification is then decoded * to recover the identifier. * * Since there can be many items, this class * should be used once for each type of item. */ /** * Constructor. */ IdentificationWithColor::IdentificationWithColor() : CaretObject() { this->itemCounter = 0; this->items.reserve(250000); } /** * Destructor. */ IdentificationWithColor::~IdentificationWithColor() { } /** * Gets an RGB color that is associated with the * specified indices. * @param rgb * Output color components. * @param dataType * Type of data. * @param index1 * First index of item. * @param index2 * Optional second index of item. * @param index3 * Optional third index of item. */ void IdentificationWithColor::addItem(uint8_t rgbOut[4], const SelectionItemDataTypeEnum::Enum dataType, const int32_t index1, const int32_t index2, const int32_t index3) { if (this->itemCounter >= static_cast(this->items.size())) { this->items.push_back(Item()); } IdentificationWithColor::encodeIntegerIntoRGB(this->itemCounter, rgbOut); rgbOut[3] = 255; Item& item = this->items[this->itemCounter]; item.dataType = dataType; item.rgb[0] = rgbOut[0]; item.rgb[1] = rgbOut[1]; item.rgb[2] = rgbOut[2]; item.index1 = index1; item.index2 = index2; item.index3 = index3; itemCounter++; } /** * Gets indices associated with an RGB color. * @param rgbOut * Output color components. * @param dataType * Type of data. * @param index1Out * Set to first index of item. * @param index2 * Set to second index of item. This paramter * may be NULL; * @param index3 * Set to third index of item. This paramter * may be NULL; */ void IdentificationWithColor::getItem(const uint8_t rgb[4], const SelectionItemDataTypeEnum::Enum dataType, int32_t* index1Out, int32_t* index2Out, int32_t* index3Out) const { CaretAssert(index1Out); const int32_t integerValue = IdentificationWithColor::decodeIntegerFromRGB(rgb); *index1Out = -1; if (index2Out != NULL) { *index2Out = -1; } if (index3Out != NULL) { *index3Out = -1; } if ((integerValue < 0) || (integerValue >= this->itemCounter)) { return; } const Item& item = this->items[integerValue]; if (dataType == item.dataType) { *index1Out = item.index1; if (index2Out != NULL) { *index2Out = item.index2; } if (index3Out != NULL) { *index3Out = item.index3; } } } /** * Reset for a new round of identification. * @param estimatedNumberOfItems * The estimated number of items. Must be non-negative * and can improve performance if greater than or equal * to the number of items. */ void IdentificationWithColor::reset(const int32_t estimatedNumberOfItems) { this->itemCounter = 0; if (estimatedNumberOfItems > static_cast(this->items.size())) { this->items.reserve(estimatedNumberOfItems); } } /** * Encode an integer as RGB values. * @param integerValue * Integer value. * @param rgbOut * Output RGB. */ void IdentificationWithColor::encodeIntegerIntoRGB(const int32_t integerValue, uint8_t rgbOut[3]) { rgbOut[2] = (uint8_t)(integerValue & 0xff); rgbOut[1] = (uint8_t)((integerValue >> 8) & 0xff); rgbOut[0] = (uint8_t)((integerValue >> 16) & 0xff); } /** * Decode an integer from RGB values. * @param rgb * RGB value. * @return The integer value. */ int32_t IdentificationWithColor::decodeIntegerFromRGB(const uint8_t rgb[3]) { int32_t r = rgb[0] & 0xff; int32_t g = rgb[1] & 0xff; int32_t b = rgb[2] & 0xff; int32_t colorValue = (r << 16) + (g << 8) + b; return colorValue; } /** * Get a description of this object's content. * @return String describing this object's content. */ AString IdentificationWithColor::toString() const { return "IdentificationWithColor"; } connectome-workbench-1.2.3+git41-gc4c6c90/src/Brain/IdentificationWithColor.h000066400000000000000000000053441300200146000265420ustar00rootroot00000000000000#ifndef __IDENTIFICATION_WITH_COLOR__H_ #define __IDENTIFICATION_WITH_COLOR__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 "SelectionItemDataTypeEnum.h" namespace caret { class IdentificationWithColor : public CaretObject { public: IdentificationWithColor(); virtual ~IdentificationWithColor(); void addItem(uint8_t rgbOut[4], const SelectionItemDataTypeEnum::Enum dataType, const int32_t index1, const int32_t index2 = -1, const int32_t index3 = -1); void getItem(const uint8_t rgb[4], const SelectionItemDataTypeEnum::Enum dataType, int32_t* index1Out, int32_t* index2Out = NULL, int32_t* index3Out = NULL) const; void reset(const int32_t estimatedNumberOfItems = -1); private: IdentificationWithColor(const IdentificationWithColor&); IdentificationWithColor& operator=(const IdentificationWithColor&); static void encodeIntegerIntoRGB(const int32_t integerValue, uint8_t rgbOut[3]); static int32_t decodeIntegerFromRGB(const uint8_t rgb[3]); public: virtual AString toString() const; private: class Item { public: SelectionItemDataTypeEnum::Enum dataType; uint8_t rgb[3]; int32_t index1; int32_t index2; int32_t index3; }; std::vector items; int32_t itemCounter; }; #ifdef __IDENTIFICATION_WITH_COLOR_DECLARE__ // #endif // __IDENTIFICATION_WITH_COLOR_DECLARE__ } // namespace #endif //__IDENTIFICATION_WITH_COLOR__H_ connectome-workbench-1.2.3+git41-gc4c6c90/src/Brain/IdentifiedItem.cxx000066400000000000000000000147761300200146000252250ustar00rootroot00000000000000 /*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 __IDENTIFIED_ITEM_DECLARE__ #include "IdentifiedItem.h" #undef __IDENTIFIED_ITEM_DECLARE__ #include "SceneClassAssistant.h" using namespace caret; /** * \class caret::IdentifiedItem * \brief Describes an identified item. */ /** * Constructor. * */ IdentifiedItem::IdentifiedItem() : CaretObject() { initializeMembers(); } /** * Constructor. * * @param text * Text describing the identified item. */ IdentifiedItem::IdentifiedItem(const AString& text) : CaretObject() { initializeMembers(); m_text = text; } /** * Destructor. */ IdentifiedItem::~IdentifiedItem() { delete m_sceneAssistant; } /** * Copy constructor. * @param obj * Object that is copied. */ IdentifiedItem::IdentifiedItem(const IdentifiedItem& obj) : CaretObject(obj), SceneableInterface() { this->initializeMembers(); this->copyHelperIdentifiedItem(obj); } /** * Assignment operator. * @param obj * Data copied from obj to this. * @return * Reference to this object. */ IdentifiedItem& IdentifiedItem::operator=(const IdentifiedItem& obj) { if (this != &obj) { CaretObject::operator=(obj); this->copyHelperIdentifiedItem(obj); } return *this; } /** * Initialize a new instance of this class. */ 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); } /** * Helps with copying an object of this type. * @param obj * Object that is copied. */ void IdentifiedItem::copyHelperIdentifiedItem(const IdentifiedItem& obj) { m_text = obj.m_text; m_showIdentificationSymbol = obj.m_showIdentificationSymbol; } /** * @return Is this item valid? Typically only used when restoring * from scene. */ bool IdentifiedItem::isValid() const { if (m_text.isEmpty() == false) { return true; } return false; } /** * Append text to this item's text. */ void IdentifiedItem::appendText(const AString& text) { m_text += text; } /** * Clear the text for this item. */ void IdentifiedItem::clearText() { m_text = ""; } /** * @return The text describing the identified item. */ AString IdentifiedItem::getText() const { return m_text; } /** * Get a description of this object's content. * @return String describing this object's content. */ AString 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. * * @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. * * @return Pointer to SceneClass object representing the state of * this object. Under some circumstances a NULL pointer may be * returned. Caller will take ownership of returned object. */ SceneClass* IdentifiedItem::saveToScene(const SceneAttributes* sceneAttributes, const AString& instanceName) { SceneClass* sceneClass = new SceneClass(instanceName, "IdentifiedItem", 1); saveMembers(sceneAttributes, sceneClass); return sceneClass; } /** * Restore the state of an instance of a class. * * @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. May be NULL for some types of scenes. */ void IdentifiedItem::restoreFromScene(const SceneAttributes* sceneAttributes, const SceneClass* sceneClass) { if (sceneClass == NULL) { return; } restoreMembers(sceneAttributes, sceneClass); } /** * Restore members (protected function for derived classes). * * @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. */ void IdentifiedItem::restoreMembers(const SceneAttributes* sceneAttributes, const SceneClass* sceneClass) { m_sceneAssistant->restoreMembers(sceneAttributes, sceneClass); } /** * Save members (protected function for derived classes). * * @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. */ void IdentifiedItem::saveMembers(const SceneAttributes* sceneAttributes, SceneClass* sceneClass) { m_sceneAssistant->saveMembers(sceneAttributes, sceneClass); } connectome-workbench-1.2.3+git41-gc4c6c90/src/Brain/IdentifiedItem.h000066400000000000000000000055621300200146000246430ustar00rootroot00000000000000#ifndef __IDENTIFIED_ITEM_H__ #define __IDENTIFIED_ITEM_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 "SceneableInterface.h" namespace caret { class SceneClassAssistant; class IdentifiedItem : public CaretObject, public SceneableInterface { public: IdentifiedItem(); IdentifiedItem(const AString& text); virtual ~IdentifiedItem(); IdentifiedItem(const IdentifiedItem& obj); IdentifiedItem& operator=(const IdentifiedItem& obj); // ADD_NEW_METHODS_HERE virtual bool isValid() const; void appendText(const AString& text); void clearText(); AString getText() const; bool isShowIdentificationSymbol() const; virtual AString toString() const; virtual SceneClass* saveToScene(const SceneAttributes* sceneAttributes, const AString& instanceName); virtual void restoreFromScene(const SceneAttributes* sceneAttributes, const SceneClass* sceneClass); protected: virtual void restoreMembers(const SceneAttributes* sceneAttributes, const SceneClass* sceneClass); 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); void initializeMembers(); // ADD_NEW_MEMBERS_HERE AString m_text; bool m_showIdentificationSymbol; SceneClassAssistant* m_sceneAssistant; }; #ifdef __IDENTIFIED_ITEM_DECLARE__ // #endif // __IDENTIFIED_ITEM_DECLARE__ } // namespace #endif //__IDENTIFIED_ITEM_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Brain/IdentifiedItemNode.cxx000066400000000000000000000260751300200146000260260ustar00rootroot00000000000000 /*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 __IDENTIFIED_ITEM_NODE_DECLARE__ #include "IdentifiedItemNode.h" #undef __IDENTIFIED_ITEM_NODE_DECLARE__ #include "CaretPreferences.h" #include "SceneAttributes.h" #include "SceneClassAssistant.h" #include "SessionManager.h" using namespace caret; /** * \class caret::IdentifiedItem * \brief Describes an identified item. */ /** * Constructor. * * @param text * Text describing the identified item. * @param structure * Structure on which identification took place. * @param contralateralStructure * Contralateral of identification structure. * @param surfaceNumberOfNodes * Number of nodes in the surface on which identification took place. * @param nodeIndex * Index of node that was identified. * */ IdentifiedItemNode::IdentifiedItemNode() : IdentifiedItem() { initializeMembers(); } /** * Constructor. * * @param text * Text describing the identified item. * @param structure * Structure on which identification took place. * @param contralateralStructure * Contralateral of identification structure. * @param surfaceNumberOfNodes * Number of nodes in the surface on which identification took place. * @param nodeIndex * Index of node that was identified. * */ IdentifiedItemNode::IdentifiedItemNode(const AString& text, const StructureEnum::Enum structure, const int32_t surfaceNumberOfNodes, const int32_t nodeIndex) : IdentifiedItem(text) { initializeMembers(); m_structure = structure; m_surfaceNumberOfNodes = surfaceNumberOfNodes, m_nodeIndex = nodeIndex; } /** * Destructor. */ IdentifiedItemNode::~IdentifiedItemNode() { delete m_sceneAssistant; } /** * Copy constructor. * @param obj * Object that is copied. */ IdentifiedItemNode::IdentifiedItemNode(const IdentifiedItemNode& obj) : IdentifiedItem(obj) { initializeMembers(); this->copyHelperIdentifiedItemNode(obj); } /** * Assignment operator. * @param obj * Data copied from obj to this. * @return * Reference to this object. */ IdentifiedItemNode& IdentifiedItemNode::operator=(const IdentifiedItemNode& obj) { if (this != &obj) { IdentifiedItem::operator=(obj); this->copyHelperIdentifiedItemNode(obj); } return *this; } /** * Helps with copying an object of this type. * @param obj * Object that is copied. */ void IdentifiedItemNode::copyHelperIdentifiedItemNode(const IdentifiedItemNode& obj) { m_structure = obj.m_structure; m_contralateralStructure = obj.m_contralateralStructure; m_surfaceNumberOfNodes = obj.m_surfaceNumberOfNodes; m_nodeIndex = obj.m_nodeIndex; m_symbolRGB[0] = obj.m_symbolRGB[0]; m_symbolRGB[1] = obj.m_symbolRGB[1]; m_symbolRGB[2] = obj.m_symbolRGB[2]; m_contralateralSymbolRGB[0] = obj.m_contralateralSymbolRGB[0]; m_contralateralSymbolRGB[1] = obj.m_contralateralSymbolRGB[1]; 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()); } /** * Initialize members of this class. */ void IdentifiedItemNode::initializeMembers() { m_structure = StructureEnum::INVALID; m_contralateralStructure = StructureEnum::INVALID; m_surfaceNumberOfNodes = -1, m_nodeIndex = -1; m_sceneAssistant = new SceneClassAssistant(); m_sceneAssistant->add("m_structure", &m_structure); m_sceneAssistant->add("m_contralateralStructure", &m_contralateralStructure); m_sceneAssistant->add("m_surfaceNumberOfNodes", &m_surfaceNumberOfNodes); m_sceneAssistant->add("m_nodeIndex", &m_nodeIndex); 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()); } /** * @return Is this item valid? Typically only used when restoring * from scene. */ bool IdentifiedItemNode::isValid() const { if (m_structure == StructureEnum::INVALID) { return false; } if (m_surfaceNumberOfNodes <= 0) { return false; } if (m_nodeIndex < 0) { return false; } return true; } /** * @return The structure for the identified node. */ StructureEnum::Enum IdentifiedItemNode::getStructure() const { return m_structure; } /** * @return The contralateral structure of the identified node. */ StructureEnum::Enum IdentifiedItemNode::getContralateralStructure() const { return m_contralateralStructure; } /** * Set the contralateral structure. * @param contralateralStructure * The contralateral structure. */ void IdentifiedItemNode::setContralateralStructure(const StructureEnum::Enum contralateralStructure) { m_contralateralStructure = contralateralStructure; } /** * @return The number of nodes in the surface on which identification took place. */ int32_t IdentifiedItemNode::getSurfaceNumberOfNodes() const { return m_surfaceNumberOfNodes; } /** * @return The index of the surface node that was identified. */ int32_t IdentifiedItemNode::getNodeIndex() const { return m_nodeIndex; } /** * @return The color for the symbol's identification symbol. */ const float* IdentifiedItemNode::getSymbolRGB() const { return m_symbolRGB; } /** * @return The color for the symbol's identification symbol on the * contralateral surface. */ const float* IdentifiedItemNode::getContralateralSymbolRGB() const { return m_contralateralSymbolRGB; } /** * Get color for the identification symbol. * * @param rgbaOut * RGBA ranging 0 to 255. */ void IdentifiedItemNode::getSymbolRGBA(uint8_t rgbaOut[4]) const { rgbaOut[0] = static_cast(m_symbolRGB[0] * 255.0); rgbaOut[1] = static_cast(m_symbolRGB[1] * 255.0); rgbaOut[2] = static_cast(m_symbolRGB[2] * 255.0); rgbaOut[3] = 255; } /** * Get color for the contralateral identification symbol. * * @param rgbaOut * RGBA ranging 0 to 255. */ void IdentifiedItemNode::getContralateralSymbolRGB(uint8_t rgbaOut[4]) const { rgbaOut[0] = static_cast(m_contralateralSymbolRGB[0] * 255.0); rgbaOut[1] = static_cast(m_contralateralSymbolRGB[1] * 255.0); rgbaOut[2] = static_cast(m_contralateralSymbolRGB[2] * 255.0); rgbaOut[3] = 255; } /** * @return The size of the symbol. */ float IdentifiedItemNode::getSymbolSize() const { return m_symbolSize; } /** * Set the color for the identification symbol. * * @param rgb * Red, green, blue color components for identification system. */ void IdentifiedItemNode::setSymbolRGB(const float* rgb) { m_symbolRGB[0] = rgb[0]; m_symbolRGB[1] = rgb[1]; m_symbolRGB[2] = rgb[2]; } /** * Set the color for the contralateral identification symbol. * * @param rgb * Red, green, blue color components for identification system. */ void IdentifiedItemNode::setContralateralSymbolRGB(const float* rgb) { m_contralateralSymbolRGB[0] = rgb[0]; m_contralateralSymbolRGB[1] = rgb[1]; m_contralateralSymbolRGB[2] = rgb[2]; } /** * Set the size of the identification symbol. * * @param symbolSize * Size of identification symbol. */ void IdentifiedItemNode::setSymbolSize(const float symbolSize) { m_symbolSize = symbolSize; } /** * Get a description of this object's content. * @return String describing this object's content. */ AString IdentifiedItemNode::toString() const { const AString s = (IdentifiedItem::toString() + ", m_structure=" + StructureEnum::toName(m_structure) + ", m_contralateralStructure=" + StructureEnum::toName(m_contralateralStructure) + ", m_surfaceNumberOfNodes=" + AString::number(m_surfaceNumberOfNodes) + ", m_nodeIndex=" + AString::number(m_nodeIndex)); return s; } /** * Create a scene for an instance of a class. * * @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. * * @return Pointer to SceneClass object representing the state of * this object. Under some circumstances a NULL pointer may be * returned. Caller will take ownership of returned object. */ SceneClass* IdentifiedItemNode::saveToScene(const SceneAttributes* sceneAttributes, const AString& instanceName) { switch (sceneAttributes->getSceneType()) { case SceneTypeEnum::SCENE_TYPE_FULL: break; case SceneTypeEnum::SCENE_TYPE_GENERIC: break; } SceneClass* sceneClass = new SceneClass(instanceName, "IdentifiedItemNode", 1); m_sceneAssistant->saveMembers(sceneAttributes, sceneClass); /* * Save data in parent class.+ */ saveMembers(sceneAttributes, sceneClass); return sceneClass; } /** * Restore the state of an instance of a class. * * @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. May be NULL for some types of scenes. */ void IdentifiedItemNode::restoreFromScene(const SceneAttributes* sceneAttributes, const SceneClass* sceneClass) { if (sceneClass == NULL) { return; } m_sceneAssistant->restoreMembers(sceneAttributes, sceneClass); /* * Restores data in parent class. */ restoreMembers(sceneAttributes, sceneClass); } connectome-workbench-1.2.3+git41-gc4c6c90/src/Brain/IdentifiedItemNode.h000066400000000000000000000067621300200146000254540ustar00rootroot00000000000000#ifndef __IDENTIFIED_ITEM_NODE_H__ #define __IDENTIFIED_ITEM_NODE_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 "IdentifiedItem.h" #include "StructureEnum.h" namespace caret { class IdentifiedItemNode : public IdentifiedItem { public: IdentifiedItemNode(); IdentifiedItemNode(const AString& text, const StructureEnum::Enum structure, const int32_t surfaceNumberOfNodes, const int32_t nodeIndex); virtual ~IdentifiedItemNode(); IdentifiedItemNode(const IdentifiedItemNode& obj); IdentifiedItemNode& operator=(const IdentifiedItemNode& obj); // ADD_NEW_METHODS_HERE virtual bool isValid() const; // AString getText() const; StructureEnum::Enum getStructure() const; StructureEnum::Enum getContralateralStructure() const; void setContralateralStructure(const StructureEnum::Enum contralateralStructure); int32_t getSurfaceNumberOfNodes() const; int32_t getNodeIndex() const; const float* getSymbolRGB() const; const float* getContralateralSymbolRGB() const; void getSymbolRGBA(uint8_t rgbaOut[4]) const; void getContralateralSymbolRGB(uint8_t rgbaOut[4]) const; float getSymbolSize() const; void setSymbolRGB(const float* rgb); void setContralateralSymbolRGB(const float* rgb); void setSymbolSize(const float symbolSize); virtual AString toString() const; virtual SceneClass* saveToScene(const SceneAttributes* sceneAttributes, const AString& instanceName); virtual void restoreFromScene(const SceneAttributes* sceneAttributes, const SceneClass* sceneClass); private: void copyHelperIdentifiedItemNode(const IdentifiedItemNode& obj); void initializeMembers(); // ADD_NEW_MEMBERS_HERE StructureEnum::Enum m_structure; StructureEnum::Enum m_contralateralStructure; int32_t m_surfaceNumberOfNodes; int32_t m_nodeIndex; float m_symbolRGB[3]; float m_contralateralSymbolRGB[3]; float m_symbolSize; SceneClassAssistant* m_sceneAssistant; }; #ifdef __IDENTIFIED_ITEM_NODE_DECLARE__ // #endif // __IDENTIFIED_ITEM_NODE_DECLARE__ } // namespace #endif //__IDENTIFIED_ITEM_NODE_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Brain/IdentifiedItemVoxel.cxx000066400000000000000000000170631300200146000262330ustar00rootroot00000000000000 /*LICENSE_START*/ /* * Copyright (C) 2015 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 __IDENTIFIED_ITEM_VOXEL_DECLARE__ #include "IdentifiedItemVoxel.h" #undef __IDENTIFIED_ITEM_VOXEL_DECLARE__ #include "CaretAssert.h" #include "CaretPreferences.h" #include "SceneClass.h" #include "SceneClassAssistant.h" #include "SessionManager.h" using namespace caret; /** * \class caret::IdentifiedItemVoxel * \brief Identified voxel. * \ingroup Brain */ /** * Constructor. */ IdentifiedItemVoxel::IdentifiedItemVoxel() : IdentifiedItem() { initializeMembers(); } /** * Constructor. */ IdentifiedItemVoxel::IdentifiedItemVoxel(const AString& text, const float xyz[3]) : IdentifiedItem(text) { initializeMembers(); m_xyz[0] = xyz[0]; m_xyz[1] = xyz[1]; m_xyz[2] = xyz[2]; } /** * Destructor. */ IdentifiedItemVoxel::~IdentifiedItemVoxel() { delete m_sceneAssistant; } /** * Copy constructor. * @param obj * Object that is copied. */ IdentifiedItemVoxel::IdentifiedItemVoxel(const IdentifiedItemVoxel& obj) : IdentifiedItem(obj) { initializeMembers(); this->copyHelperIdentifiedItemVoxel(obj); } /** * Assignment operator. * @param obj * Data copied from obj to this. * @return * Reference to this object. */ IdentifiedItemVoxel& IdentifiedItemVoxel::operator=(const IdentifiedItemVoxel& obj) { if (this != &obj) { IdentifiedItem::operator=(obj); this->copyHelperIdentifiedItemVoxel(obj); } return *this; } /** * Initialize members of this class. */ void IdentifiedItemVoxel::initializeMembers() { m_xyz[0] = 0.0; m_xyz[1] = 0.0; m_xyz[2] = 0.0; m_symbolRGB[0] = 0; m_symbolRGB[1] = 0; m_symbolRGB[1] = 0; m_symbolSize = 0.0; m_sceneAssistant = new SceneClassAssistant(); 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()); } /** * Helps with copying an object of this type. * @param obj * Object that is copied. */ void IdentifiedItemVoxel::copyHelperIdentifiedItemVoxel(const IdentifiedItemVoxel& obj) { m_xyz[0] = obj.m_xyz[0]; m_xyz[1] = obj.m_xyz[1]; m_xyz[2] = obj.m_xyz[2]; m_symbolRGB[0] = obj.m_symbolRGB[0]; m_symbolRGB[1] = obj.m_symbolRGB[1]; 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()); } /** * Get the coordinates of the identified voxel. * * @param xyzOut * Output with coordinates of voxel. */ void IdentifiedItemVoxel::getXYZ(float xyzOut[3]) const { xyzOut[0] = m_xyz[0]; xyzOut[1] = m_xyz[1]; xyzOut[2] = m_xyz[2]; } /** * @return Is this item valid? Typically only used when restoring * from scene. True if the symbol size is greater than zero, * else false. */ bool IdentifiedItemVoxel::isValid() const { if (m_symbolSize > 0.0) { return true; } return true; } /** * @return The color for the symbol's identification symbol. */ const float* IdentifiedItemVoxel::getSymbolRGB() const { return m_symbolRGB; } /** * Get color for the identification symbol. * * @param rgbaOut * RGBA ranging 0 to 255. */ void IdentifiedItemVoxel::getSymbolRGBA(uint8_t rgbaOut[4]) const { rgbaOut[0] = static_cast(m_symbolRGB[0] * 255.0); rgbaOut[1] = static_cast(m_symbolRGB[1] * 255.0); rgbaOut[2] = static_cast(m_symbolRGB[2] * 255.0); rgbaOut[3] = 255; } /** * @return The size of the symbol. */ float IdentifiedItemVoxel::getSymbolSize() const { return m_symbolSize; } /** * Set the color for the identification symbol. * * @param rgb * Red, green, blue color components for identification system. */ void IdentifiedItemVoxel::setSymbolRGB(const float* rgb) { m_symbolRGB[0] = rgb[0]; m_symbolRGB[1] = rgb[1]; m_symbolRGB[2] = rgb[2]; } /** * Set the size of the symbol. * * @param symbolSize * Size of the symbol. */ void IdentifiedItemVoxel::setSymbolSize(const float symbolSize) { m_symbolSize = symbolSize; } /** * Get a description of this object's content. * @return String describing this object's content. */ AString IdentifiedItemVoxel::toString() const { const AString s = (IdentifiedItem::toString() + ", m_xyz=" + AString::fromNumbers(m_xyz, 3, ", ")); return s; } /** * 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* IdentifiedItemVoxel::saveToScene(const SceneAttributes* sceneAttributes, const AString& instanceName) { SceneClass* sceneClass = new SceneClass(instanceName, "IdentifiedItemVoxel", 1); m_sceneAssistant->saveMembers(sceneAttributes, sceneClass); /* * Save data in parent class. */ 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 IdentifiedItemVoxel::restoreFromScene(const SceneAttributes* sceneAttributes, const SceneClass* sceneClass) { if (sceneClass == NULL) { return; } m_sceneAssistant->restoreMembers(sceneAttributes, sceneClass); /* * Restores data in parent class. */ restoreMembers(sceneAttributes, sceneClass); //Uncomment if sub-classes must restore from scene //restoreSubClassDataFromScene(sceneAttributes, // sceneClass); } connectome-workbench-1.2.3+git41-gc4c6c90/src/Brain/IdentifiedItemVoxel.h000066400000000000000000000063251300200146000256570ustar00rootroot00000000000000#ifndef __IDENTIFIED_ITEM_VOXEL_H__ #define __IDENTIFIED_ITEM_VOXEL_H__ /*LICENSE_START*/ /* * Copyright (C) 2015 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 "IdentifiedItem.h" namespace caret { class SceneClassAssistant; class IdentifiedItemVoxel : public IdentifiedItem{ public: IdentifiedItemVoxel(); IdentifiedItemVoxel(const AString& text, const float xyz[3]); virtual ~IdentifiedItemVoxel(); IdentifiedItemVoxel(const IdentifiedItemVoxel& obj); IdentifiedItemVoxel& operator=(const IdentifiedItemVoxel& obj); virtual bool isValid() const; void getXYZ(float xyzOut[3]) const; const float* getSymbolRGB() const; void getSymbolRGBA(uint8_t rgbaOut[4]) const; float getSymbolSize() const; void setSymbolRGB(const float* rgb); void setSymbolSize(const float symbolSize); virtual AString toString() const; // 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 implemetation 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 copyHelperIdentifiedItemVoxel(const IdentifiedItemVoxel& obj); void initializeMembers(); float m_xyz[3]; float m_symbolRGB[3]; float m_symbolSize; SceneClassAssistant* m_sceneAssistant; // ADD_NEW_MEMBERS_HERE }; #ifdef __IDENTIFIED_ITEM_VOXEL_DECLARE__ // #endif // __IDENTIFIED_ITEM_VOXEL_DECLARE__ } // namespace #endif //__IDENTIFIED_ITEM_VOXEL_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Brain/ImageDepthPositionEnum.cxx000066400000000000000000000253171300200146000267140ustar00rootroot00000000000000 /*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 __IMAGE_DEPTH_POSITION_ENUM_DECLARE__ #include "ImageDepthPositionEnum.h" #undef __IMAGE_DEPTH_POSITION_ENUM_DECLARE__ #include "CaretAssert.h" using namespace caret; /** * \class caret::ImageDepthPositionEnum * \brief Depth position for images * * * * 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_imageDepthPositionEnumComboBox; * * Declare a slot that is called when user changes selection * private slots: * void imageDepthPositionEnumComboBoxItemActivated(); * * Implementation File (.cxx) * Include the header files * #include "EnumComboBoxTemplate.h" * #include "ImageDepthPositionEnum.h" * * Instatiate: * m_imageDepthPositionEnumComboBox = new EnumComboBoxTemplate(this); * m_imageDepthPositionEnumComboBox->setup(); * * Get notified when the user changes the selection: * QObject::connect(m_imageDepthPositionEnumComboBox, SIGNAL(itemActivated()), * this, SLOT(imageDepthPositionEnumComboBoxItemActivated())); * * Update the selection: * m_imageDepthPositionEnumComboBox->setSelectedItem(NEW_VALUE); * * Read the selection: * const ImageDepthPositionEnum::Enum VARIABLE = m_imageDepthPositionEnumComboBox->getSelectedItem(); * */ /** * Constructor. * * @param enumValue * An enumerated value. * @param name * Name of enumerated value. * * @param guiName * User-friendly name for use in user-interface. */ ImageDepthPositionEnum::ImageDepthPositionEnum(const Enum enumValue, const AString& name, const AString& guiName) { this->enumValue = enumValue; this->integerCode = integerCodeCounter++; this->name = name; this->guiName = guiName; } /** * Destructor. */ ImageDepthPositionEnum::~ImageDepthPositionEnum() { } /** * Initialize the enumerated metadata. */ void ImageDepthPositionEnum::initialize() { if (initializedFlag) { return; } initializedFlag = true; enumData.push_back(ImageDepthPositionEnum(BACK, "BACK", "Back")); enumData.push_back(ImageDepthPositionEnum(FRONT, "FRONT", "Front")); enumData.push_back(ImageDepthPositionEnum(MIDDLE, "MIDDLE", "Middle")); } /** * 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 ImageDepthPositionEnum* ImageDepthPositionEnum::findData(const Enum enumValue) { if (initializedFlag == false) initialize(); size_t num = enumData.size(); for (size_t i = 0; i < num; i++) { const ImageDepthPositionEnum* 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 ImageDepthPositionEnum::toName(Enum enumValue) { if (initializedFlag == false) initialize(); const ImageDepthPositionEnum* 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. */ ImageDepthPositionEnum::Enum ImageDepthPositionEnum::fromName(const AString& name, bool* isValidOut) { if (initializedFlag == false) initialize(); bool validFlag = false; Enum enumValue = ImageDepthPositionEnum::enumData[0].enumValue; for (std::vector::iterator iter = enumData.begin(); iter != enumData.end(); iter++) { const ImageDepthPositionEnum& 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 ImageDepthPositionEnum")); } return enumValue; } /** * Get a GUI string representation of the enumerated type. * @param enumValue * Enumerated value. * @return * String representing enumerated value. */ AString ImageDepthPositionEnum::toGuiName(Enum enumValue) { if (initializedFlag == false) initialize(); const ImageDepthPositionEnum* 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. */ ImageDepthPositionEnum::Enum ImageDepthPositionEnum::fromGuiName(const AString& guiName, bool* isValidOut) { if (initializedFlag == false) initialize(); bool validFlag = false; Enum enumValue = ImageDepthPositionEnum::enumData[0].enumValue; for (std::vector::iterator iter = enumData.begin(); iter != enumData.end(); iter++) { const ImageDepthPositionEnum& 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 ImageDepthPositionEnum")); } return enumValue; } /** * Get the integer code for a data type. * * @return * Integer code for data type. */ int32_t ImageDepthPositionEnum::toIntegerCode(Enum enumValue) { if (initializedFlag == false) initialize(); const ImageDepthPositionEnum* 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. */ ImageDepthPositionEnum::Enum ImageDepthPositionEnum::fromIntegerCode(const int32_t integerCode, bool* isValidOut) { if (initializedFlag == false) initialize(); bool validFlag = false; Enum enumValue = ImageDepthPositionEnum::enumData[0].enumValue; for (std::vector::iterator iter = enumData.begin(); iter != enumData.end(); iter++) { const ImageDepthPositionEnum& 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 ImageDepthPositionEnum")); } 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 ImageDepthPositionEnum::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 ImageDepthPositionEnum::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(ImageDepthPositionEnum::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 ImageDepthPositionEnum::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(ImageDepthPositionEnum::toGuiName(iter->enumValue)); } if (isSorted) { std::sort(allGuiNames.begin(), allGuiNames.end()); } } connectome-workbench-1.2.3+git41-gc4c6c90/src/Brain/ImageDepthPositionEnum.h000066400000000000000000000062371300200146000263410ustar00rootroot00000000000000#ifndef __IMAGE_DEPTH_POSITION_ENUM_H__ #define __IMAGE_DEPTH_POSITION_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 ImageDepthPositionEnum { public: /** * Enumerated values. */ enum Enum { /** In back of models */ BACK, /** In front of models */ FRONT, /** In the middle of the models */ MIDDLE }; ~ImageDepthPositionEnum(); 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: ImageDepthPositionEnum(const Enum enumValue, const AString& name, const AString& guiName); static const ImageDepthPositionEnum* 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 __IMAGE_DEPTH_POSITION_ENUM_DECLARE__ std::vector ImageDepthPositionEnum::enumData; bool ImageDepthPositionEnum::initializedFlag = false; int32_t ImageDepthPositionEnum::integerCodeCounter = 0; #endif // __IMAGE_DEPTH_POSITION_ENUM_DECLARE__ } // namespace #endif //__IMAGE_DEPTH_POSITION_ENUM_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Brain/Model.cxx000066400000000000000000000342141300200146000233670ustar00rootroot00000000000000/*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 "CaretAssert.h" #include "CaretLogger.h" #include "EventManager.h" #include "Model.h" #include "ModelSurface.h" #include "ModelVolume.h" #include "OverlaySet.h" #include "SceneAttributes.h" #include "SceneClass.h" #include "SceneClassArray.h" #include "Surface.h" using namespace caret; /** * Constructor. * @param m_modelType Type of this model. * @param brain Brain that 'owns' this model. */ Model::Model(const ModelTypeEnum::Enum modelType, Brain* brain) : CaretObject() { m_brain = brain; initializeMembersModel(); m_modelType = modelType; } /** * Destructor */ Model::~Model() { } void Model::initializeMembersModel() { } /** * @return The type of model. */ ModelTypeEnum::Enum Model::getModelType() const { return m_modelType; } /** * Get a String for use in the GUI. * * @return String for use in the GUI. * */ AString Model::toString() const { return getNameForGUI(true); } /** * Get a text description of the window's content. * * @param tabIndex * Index of the tab for content description. * @param descriptionOut * Description of the window's content. */ void Model::getDescriptionOfContent(const int32_t /*tabIndex*/, PlainTextStringBuilder& descriptionOut) const { descriptionOut.addLine(getNameForGUI(true)); } /** * Get the brain that created this model. * @return The brain. */ Brain* Model::getBrain() { return m_brain; } /** * Intended for overriding by sub-classes so that they * can selected the desired surfaces after file loading. */ void Model::initializeSelectedSurfaces() { /* nothing */ } /** * Create a scene for an instance of a class. * * @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 the class' instance. * * @return Pointer to SceneClass object representing the state of * this object. Under some circumstances a NULL pointer may be * returned. Caller will take ownership of returned object. */ SceneClass* Model::saveToScene(const SceneAttributes* sceneAttributes, const AString& instanceName) { SceneClass* sceneClass = new SceneClass(instanceName, "Model", 1); /* * Do not use scene assistant to save model type since special handling * is needed when it is restored. */ sceneClass->addEnumeratedType("m_modelType", m_modelType); if (m_modelType == ModelTypeEnum::MODEL_TYPE_SURFACE) { const ModelSurface* surfaceModel = dynamic_cast(this); CaretAssert(surfaceModel); sceneClass->addString("surfaceName", surfaceModel->getSurface()->getFileNameNoPath()); } /* * Get indices of tabs that are to be saved to scene. */ const std::vector tabIndices = sceneAttributes->getIndicesOfTabsForSavingToScene(); const int32_t numActiveTabs = static_cast(tabIndices.size()); /* * Save the overlays (except for yoking) */ std::vector overlaySetClassVector; for (int32_t iat = 0; iat < numActiveTabs; iat++) { const int32_t tabIndex = tabIndices[iat]; SceneClass* overlaySetClass = new SceneClass(("modelOverlay[" + AString::number(iat) + "]"), "OverlaySet", 1); overlaySetClass->addInteger("tabIndex", tabIndex); overlaySetClass->addChild(getOverlaySet(tabIndex)->saveToScene(sceneAttributes, "overlaySet")); overlaySetClassVector.push_back(overlaySetClass); } SceneClassArray* overlaySetClassArray = new SceneClassArray("m_overlaySet", overlaySetClassVector); sceneClass->addChild(overlaySetClassArray); /* * Save information specific to the type of model */ saveModelSpecificInformationToScene(sceneAttributes, sceneClass); return sceneClass; } /** * Restore the state of an instance of a class. * * @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. May be NULL for some types of scenes. */ void Model::restoreFromScene(const SceneAttributes* sceneAttributes, const SceneClass* sceneClass) { if (sceneClass == NULL) { return; } /* * This model was created by the parent scene class. * The model type in the scene should match what was saved. * If not, a serious (programming) error has occurred. */ const ModelTypeEnum::Enum savedModelType = sceneClass->getEnumeratedTypeValue("m_modelType", ModelTypeEnum::MODEL_TYPE_INVALID); if (savedModelType == ModelTypeEnum::MODEL_TYPE_INVALID) { CaretLogSevere("Non-matching model type when restoring scene: " + ModelTypeEnum::toName(savedModelType)); return; } if (savedModelType != m_modelType) { return; } if (m_modelType == ModelTypeEnum::MODEL_TYPE_SURFACE) { const AString surfaceName = sceneClass->getStringValue("surfaceName", "NOT-FOUND"); const ModelSurface* surfaceModel = dynamic_cast(this); CaretAssert(surfaceModel); if (surfaceName != surfaceModel->getSurface()->getFileNameNoPath()) { /* * Exit as this is not the surface for restoring (name does not match) */ return; } } /* * Restore the overlays (except for yoking) */ const SceneClassArray* overlaySetClassArray = sceneClass->getClassArray("m_overlaySet"); if (overlaySetClassArray != NULL) { const int32_t numSavedOverlaySets = overlaySetClassArray->getNumberOfArrayElements(); for (int32_t i = 0; i < numSavedOverlaySets; i++) { const SceneClass* overlaySceneClass = overlaySetClassArray->getClassAtIndex(i); const int32_t tabIndex = overlaySceneClass->getIntegerValue("tabIndex", -1); const SceneClass* overlayClass = overlaySceneClass->getClass("overlaySet"); if ((tabIndex >= 0) && (overlayClass != NULL)) { getOverlaySet(tabIndex)->restoreFromScene(sceneAttributes, overlayClass); } } } /* * Restore any information specific to type of model */ restoreModelSpecificInformationFromScene(sceneAttributes, sceneClass); /* * Check for transformations that are stored in the model's scene. * These are only present in older scene files (circa March 2013 * and earlier). */ m_oldSceneTransformations.resize(BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS); for (int32_t i = 0; i < BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS; i++) { m_oldSceneTransformations[i].m_rotationValid = false; m_oldSceneTransformations[i].m_scalingValid = false; m_oldSceneTransformations[i].m_translationValid = false; } /* * Restore scaling */ const SceneClassArray* scalingClassArray = sceneClass->getClassArray("m_scaling"); if (scalingClassArray != NULL) { const int32_t numSavedScaling = scalingClassArray->getNumberOfArrayElements(); for (int32_t ism = 0; ism < numSavedScaling; ism++) { const SceneClass* scalingClass = scalingClassArray->getClassAtIndex(ism); const int32_t tabIndex = scalingClass->getIntegerValue("tabIndex", -1); if ((tabIndex >= 0) && (tabIndex < BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS)) { m_oldSceneTransformations[tabIndex].m_scaling = scalingClass->getFloatValue("scaling", 1.0); m_oldSceneTransformations[tabIndex].m_scalingValid = true; } } } /* * Restore translation */ const SceneClassArray* translationClassArray = sceneClass->getClassArray("m_translation"); if (translationClassArray != NULL) { const int32_t numSavedTanslations = translationClassArray->getNumberOfArrayElements(); for (int32_t ism = 0; ism < numSavedTanslations; ism++) { const SceneClass* translationClass = translationClassArray->getClassAtIndex(ism); const int32_t tabIndex = translationClass->getIntegerValue("tabIndex", -1); const int32_t viewingTransformIndex = translationClass->getIntegerValue("viewingTransformIndex", -1); if ((tabIndex >= 0) && (tabIndex < BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS)) { if (viewingTransformIndex == 0) { if (translationClass->getFloatArrayValue("translation", m_oldSceneTransformations[tabIndex].m_translation, 3) == 3) { m_oldSceneTransformations[tabIndex].m_translationValid = true; } } } } } /* * Restore rotation matrices */ const SceneClassArray* rotationMatrixClassArray = sceneClass->getClassArray("m_viewingRotationMatrix"); if (rotationMatrixClassArray != NULL) { const int32_t numSavedMatrices = rotationMatrixClassArray->getNumberOfArrayElements(); for (int32_t ism = 0; ism < numSavedMatrices; ism++) { const SceneClass* rotationMatrixClass = rotationMatrixClassArray->getClassAtIndex(ism); const int32_t tabIndex = rotationMatrixClass->getIntegerValue("tabIndex", -1); const int32_t viewingTransformIndex = rotationMatrixClass->getIntegerValue("viewingTransformIndex", -1); if ((tabIndex >= 0) && (tabIndex < BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS)) { if (viewingTransformIndex == 0) { if (rotationMatrixClass->getFloatArrayValue("matrix", (float*)m_oldSceneTransformations[tabIndex].m_rotationMatrix, 16) == 16) { m_oldSceneTransformations[tabIndex].m_rotationValid = true; } } } } } } /** * Get transformations for a given tab from older scenes that were * created when the transformations were present in every model for * every tab. Transformations have since been moved into the * browser tab content. * * @param tabIndex * Index of tab for transformation. * @param translationOut * The translation for the given tab. * @param scalingOut * The scaling for the given tab. * @param rotationMatrixOut * The rotation matrix for the given tab. * @return * true if the transformations are valid, else false. */ bool Model::getOldSceneTransformation(const int tabIndex, float translationOut[3], float& scalingOut, float rotationMatrixOut[4][4]) const { if ((tabIndex >= 0) && (tabIndex < static_cast(m_oldSceneTransformations.size()))) { const OldSceneTransformation& ost = m_oldSceneTransformations[tabIndex]; if (ost.m_rotationValid && ost.m_scalingValid && ost.m_translationValid) { translationOut[0] = ost.m_translation[0]; translationOut[1] = ost.m_translation[1]; translationOut[2] = ost.m_translation[2]; scalingOut = ost.m_scaling; for (int32_t i = 0; i < 4; i++) { for (int32_t j = 0; j < 4; j++) { rotationMatrixOut[i][j] = ost.m_rotationMatrix[i][j]; } } return true; } } return 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 Model::copyTabContent(const int32_t /*sourceTabIndex*/, const int32_t /*destinationTabIndex*/) { } connectome-workbench-1.2.3+git41-gc4c6c90/src/Brain/Model.h000066400000000000000000000100531300200146000230070ustar00rootroot00000000000000#ifndef __MODEL_DISPLAY_CONTROLLER_H__ #define __MODEL_DISPLAY_CONTROLLER_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 "BrainConstants.h" #include "CaretObject.h" #include "ModelTypeEnum.h" #include "SceneableInterface.h" namespace caret { class Brain; class OverlaySet; class PlainTextStringBuilder; /// Base class for a model class Model : public CaretObject, public SceneableInterface { protected: Model(const ModelTypeEnum::Enum modelType, Brain* brain); virtual ~Model(); private: Model(const Model& o); Model& operator=(const Model& o); void initializeMembersModel(); public: virtual void initializeOverlays() = 0; Brain* getBrain(); ModelTypeEnum::Enum getModelType() const; virtual AString getNameForGUI(const bool includeStructureFlag) const = 0; virtual AString getNameForBrowserTab() const = 0; virtual AString toString() const; virtual void getDescriptionOfContent(const int32_t tabIndex, PlainTextStringBuilder& descriptionOut) const; virtual OverlaySet* getOverlaySet(const int tabIndex) = 0; virtual const OverlaySet* getOverlaySet(const int tabIndex) const = 0; virtual void initializeSelectedSurfaces(); virtual SceneClass* saveToScene(const SceneAttributes* sceneAttributes, const AString& instanceName); virtual void restoreFromScene(const SceneAttributes* sceneAttributes, const SceneClass* sceneClass); bool getOldSceneTransformation(const int tabIndex, float translationOut[3], float& scalingOut, float rotationMatrixOut[4][4]) const; virtual void copyTabContent(const int32_t sourceTabIndex, const int32_t destinationTabIndex); protected: virtual void saveModelSpecificInformationToScene(const SceneAttributes* sceneAttributes, SceneClass* sceneClass) = 0; virtual void restoreModelSpecificInformationFromScene(const SceneAttributes* sceneAttributes, const SceneClass* sceneClass) = 0; /** Brain which contains the model */ Brain* m_brain; private: /** * Transformations in older scene files when transforms were stored * in each of the models for every tab. */ class OldSceneTransformation { public: float m_translation[3]; float m_scaling; float m_rotationMatrix[4][4]; bool m_translationValid; bool m_scalingValid; bool m_rotationValid; }; ModelTypeEnum::Enum m_modelType; std::vector m_oldSceneTransformations; }; } // namespace #endif // __MODEL_DISPLAY_CONTROLLER_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Brain/ModelChart.cxx000066400000000000000000002276661300200146000243700ustar00rootroot00000000000000/*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 "Brain.h" #include "BrowserTabContent.h" #include "CaretAssert.h" #include "CaretLogger.h" #include "CaretMappableDataFile.h" #include "ChartAxis.h" #include "ChartableLineSeriesBrainordinateInterface.h" #include "CaretDataFileSelectionModel.h" #include "CaretLogger.h" #include "CaretMappableDataFileAndMapSelectionModel.h" #include "ChartableLineSeriesRowColumnInterface.h" #include "ChartableMatrixInterface.h" #include "ChartData.h" #include "ChartDataCartesian.h" #include "ChartDataSource.h" #include "ChartModelDataSeries.h" #include "ChartModelFrequencySeries.h" #include "ChartModelTimeSeries.h" #include "CiftiMappableDataFile.h" #include "CiftiScalarDataSeriesFile.h" #include "EventBrowserTabGetAll.h" #include "EventManager.h" #include "EventNodeIdentificationColorsGetFromCharts.h" #include "ModelChart.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. * */ ModelChart::ModelChart(Brain* brain) : Model(ModelTypeEnum::MODEL_TYPE_CHART, brain) { std::vector overlaySurfaceStructures; 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, DataFileTypeEnum::CONNECTIVITY_SCALAR_DATA_SERIES); } initializeCharts(); EventManager::get()->addEventListener(this, EventTypeEnum::EVENT_NODE_IDENTIFICATION_COLORS_GET_FROM_CHARTS); } /** * Destructor */ ModelChart::~ModelChart() { delete m_overlaySetArray; EventManager::get()->removeAllEventsFromListener(this); removeAllCharts(); for (int32_t i = 0; i < BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS; i++) { delete m_chartableMatrixFileSelectionModel[i]; m_chartableMatrixFileSelectionModel[i] = NULL; delete m_chartableMatrixSeriesFileSelectionModel[i]; m_chartableMatrixSeriesFileSelectionModel[i] = NULL; } } 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_chartModelDataSeries[i] = new ChartModelDataSeries(); m_chartModelDataSeries[i]->getLeftAxis()->setText("Value"); m_chartModelDataSeries[i]->getBottomAxis()->setText("Map Index"); m_chartModelFrequencySeries[i] = new ChartModelFrequencySeries(); m_chartModelFrequencySeries[i]->getLeftAxis()->setText("Value"); m_chartModelFrequencySeries[i]->getBottomAxis()->setText("Frequency"); m_chartModelTimeSeries[i] = new ChartModelTimeSeries(); m_chartModelTimeSeries[i]->getLeftAxis()->setText("Activity"); m_chartModelTimeSeries[i]->getBottomAxis()->setText("Time"); } } /** * Reset this model. */ void ModelChart::reset() { removeAllCharts(); initializeCharts(); } /** * Remove all of the charts. */ void ModelChart::removeAllCharts() { for (int32_t i = 0; i < BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS; i++) { if (m_chartModelDataSeries[i] != NULL) { delete m_chartModelDataSeries[i]; m_chartModelDataSeries[i] = NULL; } if (m_chartModelFrequencySeries[i] != NULL) { delete m_chartModelFrequencySeries[i]; m_chartModelFrequencySeries[i] = NULL; } if (m_chartModelTimeSeries[i] != NULL) { delete m_chartModelTimeSeries[i]; m_chartModelTimeSeries[i] = NULL; } } m_dataSeriesChartData.clear(); m_frequencySeriesChartData.clear(); m_timeSeriesChartData.clear(); m_previousChartMatrixFiles.clear(); } /** * 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 ModelChart::loadAverageChartDataForSurfaceNodes(const StructureEnum::Enum structure, const int32_t surfaceNumberOfNodes, const std::vector& nodeIndices) { std::map > chartFileEnabledTabs; getTabsAndBrainordinateChartFilesForLineChartLoading(chartFileEnabledTabs); for (std::map >::iterator fileTabIter = chartFileEnabledTabs.begin(); fileTabIter != chartFileEnabledTabs.end(); fileTabIter++) { ChartableLineSeriesBrainordinateInterface* chartFile = fileTabIter->first; const std::vector tabIndices = fileTabIter->second; CaretAssert(chartFile); ChartData* chartData = chartFile->loadAverageLineSeriesChartDataForSurfaceNodes(structure, nodeIndices); if (chartData != NULL) { ChartDataSource* dataSource = chartData->getChartDataSource(); dataSource->setSurfaceNodeAverage(chartFile->getLineSeriesChartCaretMappableDataFile()->getFileName(), StructureEnum::toName(structure), surfaceNumberOfNodes, nodeIndices); addChartToChartModels(tabIndices, chartData); } } } /** * Load chart data for voxel at the given coordinate. * * @param xyz * Coordinate of voxel. * @throws * DataFileException if there is an error loading data. */ void ModelChart::loadChartDataForVoxelAtCoordinate(const float xyz[3]) { std::map > chartFileEnabledTabs; getTabsAndBrainordinateChartFilesForLineChartLoading(chartFileEnabledTabs); for (std::map >::iterator fileTabIter = chartFileEnabledTabs.begin(); fileTabIter != chartFileEnabledTabs.end(); fileTabIter++) { ChartableLineSeriesBrainordinateInterface* chartFile = fileTabIter->first; const std::vector tabIndices = fileTabIter->second; CaretAssert(chartFile); ChartData* chartData = chartFile->loadLineSeriesChartDataForVoxelAtCoordinate(xyz); if (chartData != NULL) { ChartDataSource* dataSource = chartData->getChartDataSource(); dataSource->setVolumeVoxel(chartFile->getLineSeriesChartCaretMappableDataFile()->getFileName(), xyz); addChartToChartModels(tabIndices, chartData); } } } /** * Load chart data for CIFTI Map files yoked to the given yoking group. * * @param mapYokingGroup * The map yoking group. * @param mapIndex * The map index. */ void ModelChart::loadChartDataForYokedCiftiMappableFiles(const MapYokingGroupEnum::Enum mapYokingGroup, const int32_t mapIndex) { if (mapYokingGroup == MapYokingGroupEnum::MAP_YOKING_GROUP_OFF) { return; } std::map > chartFileEnabledTabs; getTabsAndRowColumnChartFilesForLineChartLoading(chartFileEnabledTabs); for (std::map >::iterator fileTabIter = chartFileEnabledTabs.begin(); fileTabIter != chartFileEnabledTabs.end(); fileTabIter++) { ChartableLineSeriesRowColumnInterface* chartFile = fileTabIter->first; CaretAssert(chartFile); CiftiScalarDataSeriesFile* csdsf = dynamic_cast(chartFile); if (csdsf != NULL) { std::vector matchedTabIndices; const std::vector tabIndices = fileTabIter->second; for (std::vector::const_iterator tabIter = tabIndices.begin(); tabIter != tabIndices.end(); tabIter++) { const int32_t tabIndex = *tabIter; if (csdsf->getMatrixRowColumnMapYokingGroup(tabIndex) == mapYokingGroup) { matchedTabIndices.push_back(tabIndex); } } if ( ! matchedTabIndices.empty()) { ChartData* chartData = chartFile->loadLineSeriesChartDataForRow(mapIndex); if (chartData != NULL) { ChartDataSource* dataSource = chartData->getChartDataSource(); dataSource->setFileRow(chartFile->getLineSeriesChartCaretMappableDataFile()->getFileName(), mapIndex); addChartToChartModels(matchedTabIndices, chartData); } } } } } /** * Load chart data from given file at the given row. * * @param ciftiMapFile * The CIFTI file. * @param rowIndex * Index of row in the file. */ void ModelChart::loadChartDataForCiftiMappableFileRow(CiftiMappableDataFile* ciftiMapFile, const int32_t rowIndex) { CaretAssert(ciftiMapFile); std::map > chartFileEnabledTabs; getTabsAndRowColumnChartFilesForLineChartLoading(chartFileEnabledTabs); for (std::map >::iterator fileTabIter = chartFileEnabledTabs.begin(); fileTabIter != chartFileEnabledTabs.end(); fileTabIter++) { ChartableLineSeriesRowColumnInterface* chartFile = fileTabIter->first; if (ciftiMapFile == dynamic_cast(chartFile)) { const std::vector tabIndices = fileTabIter->second; CaretAssert(chartFile); ChartData* chartData = chartFile->loadLineSeriesChartDataForRow(rowIndex); if (chartData != NULL) { ChartDataSource* dataSource = chartData->getChartDataSource(); dataSource->setFileRow(chartFile->getLineSeriesChartCaretMappableDataFile()->getFileName(), rowIndex); addChartToChartModels(tabIndices, chartData); } } } // 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."); // } // } } /** * Add the chart to the given tabs. * * @param tabIndices * Indices of tabs for chart data * @param chartData * Chart data that is added. */ void ModelChart::addChartToChartModels(const std::vector& tabIndices, ChartData* chartData) { CaretAssert(chartData); const ChartDataTypeEnum::Enum chartDataDataType = chartData->getChartDataType(); switch (chartDataDataType) { case ChartDataTypeEnum::CHART_DATA_TYPE_INVALID: CaretAssert(0); break; case ChartDataTypeEnum::CHART_DATA_TYPE_LINE_DATA_SERIES: { ChartDataCartesian* cdc = dynamic_cast(chartData); CaretAssert(cdc); QSharedPointer cdcPtr(cdc); for (std::vector::const_iterator iter = tabIndices.begin(); iter != tabIndices.end(); iter++) { const int32_t tabIndex = *iter; CaretAssertArrayIndex(m_chartModelDataSeries, BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS, tabIndex); m_chartModelDataSeries[tabIndex]->addChartData(cdcPtr); } m_dataSeriesChartData.push_front(cdcPtr.toWeakRef()); } break; case ChartDataTypeEnum::CHART_DATA_TYPE_LINE_FREQUENCY_SERIES: { ChartDataCartesian* cdc = dynamic_cast(chartData); CaretAssert(cdc); QSharedPointer cdcPtr(cdc); for (std::vector::const_iterator iter = tabIndices.begin(); iter != tabIndices.end(); iter++) { const int32_t tabIndex = *iter; CaretAssertArrayIndex(m_chartModelFrequencySeries, BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS, tabIndex); m_chartModelFrequencySeries[tabIndex]->addChartData(cdcPtr); } m_frequencySeriesChartData.push_front(cdcPtr.toWeakRef()); } break; case ChartDataTypeEnum::CHART_DATA_TYPE_LINE_TIME_SERIES: { ChartDataCartesian* cdc = dynamic_cast(chartData); CaretAssert(cdc); QSharedPointer cdcPtr(cdc); for (std::vector::const_iterator iter = tabIndices.begin(); iter != tabIndices.end(); iter++) { const int32_t tabIndex = *iter; CaretAssertArrayIndex(m_chartModelTimeSeries, BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS, tabIndex); m_chartModelTimeSeries[tabIndex]->addChartData(cdcPtr); } m_timeSeriesChartData.push_front(cdcPtr.toWeakRef()); } break; case ChartDataTypeEnum::CHART_DATA_TYPE_MATRIX_LAYER: CaretAssert(0); break; case ChartDataTypeEnum::CHART_DATA_TYPE_MATRIX_SERIES: break; } } /** * Get tabs and brainordinate chart files for loading chart data. * * @param chartBrainordinateFileEnabledTabsOut * Map with first being a chartable file and the second being * tabs for which that chartable file is enabled. */ 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; getTabsAndLineSeriesChartFilesForLineChartLoading(chartFileEnabledTabs); for (std::map >::iterator iter = chartFileEnabledTabs.begin(); iter != chartFileEnabledTabs.end(); iter++) { ChartableLineSeriesBrainordinateInterface* brainChartFile = dynamic_cast(iter->first); if (brainChartFile != NULL) { chartBrainordinateFileEnabledTabsOut.insert(std::make_pair(brainChartFile, iter->second)); } } } /** * Get tabs and row column chart files for loading chart data. * * @param chartRowColumnFilesEnabledTabsOut * Map with first being a chartable file and the second being * tabs for which that chartable file is enabled. */ void ModelChart::getTabsAndRowColumnChartFilesForLineChartLoading(std::map >& chartRowColumnFilesEnabledTabsOut) const { chartRowColumnFilesEnabledTabsOut.clear(); std::map > chartFileEnabledTabs; getTabsAndLineSeriesChartFilesForLineChartLoading(chartFileEnabledTabs); for (std::map >::iterator iter = chartFileEnabledTabs.begin(); iter != chartFileEnabledTabs.end(); iter++) { ChartableLineSeriesRowColumnInterface* rowColChartFile = dynamic_cast(iter->first); if (rowColChartFile != NULL) { chartRowColumnFilesEnabledTabsOut.insert(std::make_pair(rowColChartFile, iter->second)); } } } /** * Get line series chart files for loading chart data. * * @param chartFileEnabledTabsOut * Map with first being a chartable file and the second being * tabs for which that chartable file is enabled. */ void ModelChart::getTabsAndLineSeriesChartFilesForLineChartLoading(std::map >& chartFileEnabledTabsOut) const { chartFileEnabledTabsOut.clear(); EventBrowserTabGetAll allTabsEvent; EventManager::get()->sendEvent(allTabsEvent.getPointer()); std::vector validTabIndices = allTabsEvent.getBrowserTabIndices(); std::vector chartFiles; m_brain->getAllChartableLineSeriesDataFiles(chartFiles); for (std::vector::iterator iter = chartFiles.begin(); iter != chartFiles.end(); iter++) { ChartableLineSeriesInterface* 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)); } } } /** * 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 ModelChart::loadChartDataForSurfaceNode(const StructureEnum::Enum structure, const int32_t surfaceNumberOfNodes, const int32_t nodeIndex) { std::map > chartFileEnabledTabs; getTabsAndBrainordinateChartFilesForLineChartLoading(chartFileEnabledTabs); for (std::map >::iterator fileTabIter = chartFileEnabledTabs.begin(); fileTabIter != chartFileEnabledTabs.end(); fileTabIter++) { ChartableLineSeriesBrainordinateInterface* chartFile = fileTabIter->first; const std::vector tabIndices = fileTabIter->second; CaretAssert(chartFile); ChartData* chartData = chartFile->loadLineSeriesChartDataForSurfaceNode(structure, nodeIndex); if (chartData != NULL) { ChartDataSource* dataSource = chartData->getChartDataSource(); dataSource->setSurfaceNode(chartFile->getLineSeriesChartCaretMappableDataFile()->getFileName(), StructureEnum::toName(structure), surfaceNumberOfNodes, nodeIndex); addChartToChartModels(tabIndices, chartData); } } } /** * Receive an event. * * @param event * The event that the receive can respond to. */ void ModelChart::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 ModelChart::getNameForGUI(const bool /*includeStructureFlag*/) const { AString name = "Chart"; return name; } /** * @return The name that should be displayed in the tab * displaying this model. */ AString ModelChart::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* ModelChart::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* ModelChart::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 ModelChart::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 ModelChart::saveModelSpecificInformationToScene(const SceneAttributes* sceneAttributes, SceneClass* sceneClass) { std::vector tabIndices = sceneAttributes->getIndicesOfTabsForSavingToScene(); std::set validChartDataIDs; saveChartModelsToScene(sceneAttributes, sceneClass, tabIndices, validChartDataIDs); sceneClass->addEnumeratedTypeArrayForTabIndices("m_selectedChartDataType", m_selectedChartDataType, tabIndices); /* * Save matrix chart models to scene. */ SceneObjectMapIntegerKey* matrixSceneMap = new SceneObjectMapIntegerKey("chartableMatrixFileSelectionModelMap", SceneObjectDataTypeEnum::SCENE_CLASS); std::vector matrixSelectionVector; for (std::vector::const_iterator tabIter = tabIndices.begin(); tabIter != tabIndices.end(); tabIter++) { const int32_t tabIndex = *tabIter; matrixSceneMap->addClass(tabIndex, m_chartableMatrixFileSelectionModel[tabIndex]->saveToScene(sceneAttributes, "m_chartableMatrixFileSelectionModel")); } sceneClass->addChild(matrixSceneMap); /* * Save matrix series chart models to scene. */ SceneObjectMapIntegerKey* matrixSeriesSceneMap = new SceneObjectMapIntegerKey("chartableMatrixSeriesFileSelectionModelMap", SceneObjectDataTypeEnum::SCENE_CLASS); std::vector matrixSeriesSelectionVector; for (std::vector::const_iterator tabIter = tabIndices.begin(); tabIter != tabIndices.end(); tabIter++) { const int32_t tabIndex = *tabIter; matrixSeriesSceneMap->addClass(tabIndex, m_chartableMatrixSeriesFileSelectionModel[tabIndex]->saveToScene(sceneAttributes, "m_chartableMatrixSeriesFileSelectionModel")); } sceneClass->addChild(matrixSeriesSceneMap); } /** * 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 ModelChart::restoreModelSpecificInformationFromScene(const SceneAttributes* sceneAttributes, const SceneClass* sceneClass) { reset(); /* * Restore the chart models */ restoreChartModelsFromScene(sceneAttributes, sceneClass); sceneClass->getEnumerateTypeArrayForTabIndices("m_selectedChartDataType", m_selectedChartDataType); /* * Restore matrix chart models from scene. */ const SceneObjectMapIntegerKey* matrixSceneMap = sceneClass->getMapIntegerKey("chartableMatrixFileSelectionModelMap"); if (matrixSceneMap != NULL) { const std::vector tabIndices = matrixSceneMap->getKeys(); for (std::vector::const_iterator tabIter = tabIndices.begin(); tabIter != tabIndices.end(); tabIter++) { const int32_t tabIndex = *tabIter; const SceneClass* sceneClass = matrixSceneMap->classValue(tabIndex); m_chartableMatrixFileSelectionModel[tabIndex]->restoreFromScene(sceneAttributes, sceneClass); } } /* * Restore matrix chart series models from scene. */ const SceneObjectMapIntegerKey* matrixSeriesSceneMap = sceneClass->getMapIntegerKey("chartableMatrixSeriesFileSelectionModelMap"); if (matrixSeriesSceneMap != NULL) { const std::vector tabIndices = matrixSeriesSceneMap->getKeys(); for (std::vector::const_iterator tabIter = tabIndices.begin(); tabIter != tabIndices.end(); tabIter++) { const int32_t tabIndex = *tabIter; const SceneClass* sceneClass = matrixSeriesSceneMap->classValue(tabIndex); m_chartableMatrixSeriesFileSelectionModel[tabIndex]->restoreFromScene(sceneAttributes, sceneClass); } } } /** * Save chart models 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::saveChartModelsToScene(const SceneAttributes* sceneAttributes, SceneClass* sceneClass, const std::vector& tabIndices, std::set& validChartDataIDsOut) { validChartDataIDsOut.clear(); std::set chartDataForSavingToSceneSet; /* * Save chart models to scene. */ std::vector chartModelVector; for (std::vector::const_iterator tabIter = tabIndices.begin(); tabIter != tabIndices.end(); tabIter++) { const int32_t tabIndex = *tabIter; ChartModel* chartModel = NULL; switch (getSelectedChartDataType(tabIndex)) { 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_FREQUENCY_SERIES: chartModel = getSelectedFrequencySeriesChartModel(tabIndex); break; case ChartDataTypeEnum::CHART_DATA_TYPE_LINE_DATA_SERIES: chartModel = getSelectedDataSeriesChartModel(tabIndex); break; case ChartDataTypeEnum::CHART_DATA_TYPE_LINE_TIME_SERIES: chartModel = getSelectedTimeSeriesChartModel(tabIndex); break; } if (chartModel != NULL) { SceneClass* chartModelClass = chartModel->saveToScene(sceneAttributes, "chartModel"); if (chartModelClass == NULL) { continue; } SceneClass* chartClassContainer = new SceneClass("chartClassContainer", "ChartClassContainer", 1); chartClassContainer->addInteger("tabIndex", tabIndex); chartClassContainer->addEnumeratedType("chartDataType", chartModel->getChartDataType()); chartClassContainer->addClass(chartModelClass); chartModelVector.push_back(chartClassContainer); /* * Add chart data that is in models saved to scene. * */ std::vector chartDatasInModel = chartModel->getAllChartDatas(); chartDataForSavingToSceneSet.insert(chartDatasInModel.begin(), chartDatasInModel.end()); } } if ( ! chartModelVector.empty()) { SceneClassArray* modelArray = new SceneClassArray("chartModelArray", chartModelVector); sceneClass->addChild(modelArray); } if ( ! chartDataForSavingToSceneSet.empty()) { std::vector chartDataClassVector; for (std::set::iterator cdIter = chartDataForSavingToSceneSet.begin(); cdIter != chartDataForSavingToSceneSet.end(); cdIter++) { ChartData* chartData = *cdIter; SceneClass* chartDataClass = chartData->saveToScene(sceneAttributes, "chartData"); SceneClass* chartDataContainer = new SceneClass("chartDataContainer", "ChartDataContainer", 1); chartDataContainer->addEnumeratedType("chartDataType", chartData->getChartDataType()); chartDataContainer->addClass(chartDataClass); chartDataClassVector.push_back(chartDataContainer); } if ( ! chartDataClassVector.empty()) { SceneClassArray* dataArray = new SceneClassArray("chartDataArray", chartDataClassVector); sceneClass->addChild(dataArray); } } } /** * Restore the chart models 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 ModelChart::restoreChartModelsFromScene(const SceneAttributes* sceneAttributes, const SceneClass* sceneClass) { /* * Restore the chart models */ const SceneClassArray* chartModelArray = sceneClass->getClassArray("chartModelArray"); if (chartModelArray != NULL) { const int numElements = chartModelArray->getNumberOfArrayElements(); for (int32_t i = 0; i < numElements; i++) { 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 SceneClass* chartModelClass = chartClassContainer->getClass("chartModel"); if ((tabIndex >= 0) && (chartDataType != ChartDataTypeEnum::CHART_DATA_TYPE_INVALID) && (chartModelClass != NULL)) { CaretAssertArrayIndex(m_chartModelDataSeries, BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS, tabIndex); switch (chartDataType) { case ChartDataTypeEnum::CHART_DATA_TYPE_INVALID: break; case ChartDataTypeEnum::CHART_DATA_TYPE_MATRIX_LAYER: CaretAssert(0); break; case ChartDataTypeEnum::CHART_DATA_TYPE_MATRIX_SERIES: break; case ChartDataTypeEnum::CHART_DATA_TYPE_LINE_DATA_SERIES: m_chartModelDataSeries[tabIndex]->restoreFromScene(sceneAttributes, chartModelClass); break; case ChartDataTypeEnum::CHART_DATA_TYPE_LINE_FREQUENCY_SERIES: m_chartModelFrequencySeries[tabIndex]->restoreFromScene(sceneAttributes, chartModelClass); break; case ChartDataTypeEnum::CHART_DATA_TYPE_LINE_TIME_SERIES: m_chartModelTimeSeries[tabIndex]->restoreFromScene(sceneAttributes, chartModelClass); break; } } } } } /* * Restore the chart data. */ std::vector > restoredChartData; const SceneClassArray* chartDataArray = sceneClass->getClassArray("chartDataArray"); if (chartDataArray != NULL) { const int numElements = chartDataArray->getNumberOfArrayElements(); 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 SceneClass* chartDataClass = chartDataContainer->getClass("chartData"); if ((chartDataType != ChartDataTypeEnum::CHART_DATA_TYPE_INVALID) && (chartDataClass != NULL)) { ChartData* chartData = ChartData::newChartDataForChartDataType(chartDataType); CaretAssert(chartData); /* * The chart's points are saved in the scene and this * function call restores the points. This is part of * the original implementation. However, it was decided * that the when the scene is restored, the data should * be loaded from the file to reflect any changes made * to the data file. But, we still load the chart points * saved to the scene in case the file is missing. */ chartData->restoreFromScene(sceneAttributes, chartDataClass); /* * Now load the chart data from the file using the * information in the chart's data source. If this is * successful, then overwrite the chart data points * that were loaded from the scene file. * * Also need to copy the unique identifier so that * the chart data goes to the correct model. */ ChartData* newChartData = loadCartesianChartWhenRestoringScene(chartData); if (newChartData != NULL) { newChartData->setUniqueIdentifier(chartData->getUniqueIdentifier()); newChartData->copySelectionStatusForAllTabs(chartData); delete chartData; chartData = newChartData; } else { const AString msg("FAILED to load line chart data from file for " + chartData->getChartDataSource()->getDescription() + " so chart points from scene will be used. If content of the file " " has changed since the scene was created, the chart may not be accurate."); sceneAttributes->addToErrorMessage(msg); CaretLogWarning(msg); } restoredChartData.push_back(QSharedPointer(chartData)); } } } } /* * Have chart models restore pointers to chart data * The chart models use shared pointers are used since the chart * data may be in multiple tabs. User may remove the charts * from some tabs but not others and shared pointers make management * easier. */ for (int32_t i = 0; i < BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS; i++) { m_chartModelDataSeries[i]->restoreChartDataFromScene(sceneAttributes, restoredChartData); } for (int32_t i = 0; i < BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS; i++) { m_chartModelFrequencySeries[i]->restoreChartDataFromScene(sceneAttributes, restoredChartData); } for (int32_t i = 0; i < BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS; i++) { m_chartModelTimeSeries[i]->restoreChartDataFromScene(sceneAttributes, restoredChartData); } /* * The chart data are also saved here as weak pointers so * that they can be saved to a scene only one time. If */ for (std::vector >::iterator rcdIter = restoredChartData.begin(); rcdIter != restoredChartData.end(); rcdIter++) { QSharedPointer chartPointer = *rcdIter; switch (chartPointer->getChartDataType()) { case ChartDataTypeEnum::CHART_DATA_TYPE_INVALID: CaretAssert(0); break; case ChartDataTypeEnum::CHART_DATA_TYPE_MATRIX_LAYER: CaretAssert(0); break; case ChartDataTypeEnum::CHART_DATA_TYPE_MATRIX_SERIES: CaretAssert(0); break; case ChartDataTypeEnum::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: { QSharedPointer cartChartPointer = chartPointer.dynamicCast(); CaretAssert( ! cartChartPointer.isNull()); m_frequencySeriesChartData.push_back(cartChartPointer); } break; case ChartDataTypeEnum::CHART_DATA_TYPE_LINE_TIME_SERIES: { QSharedPointer cartChartPointer = chartPointer.dynamicCast(); CaretAssert( ! cartChartPointer.isNull()); m_timeSeriesChartData.push_back(cartChartPointer); } break; } } } /** * Load the cartesian chart data using the given chart data source. * * @param chartData * ChartData that is cast to ChartDataCartesian and if successful, * the chart is duplicated using data from files. * @return * Pointer to successfully loaded chart data or NULL if not found or error. */ ChartData* ModelChart::loadCartesianChartWhenRestoringScene(const ChartData* chartData) { CaretAssert(chartData); ChartDataCartesian* chartDataOut = NULL; const ChartDataCartesian* cartesianChart = dynamic_cast(chartData); if (cartesianChart != NULL) { const ChartDataTypeEnum::Enum chartDataType = cartesianChart->getChartDataType(); std::vector chartableDataFiles; m_brain->getAllChartableLineSeriesDataFiles(chartableDataFiles); for (std::vector::iterator iter = chartableDataFiles.begin(); iter != chartableDataFiles.end(); iter++) { ChartableLineSeriesBrainordinateInterface* chartableBrainFile = dynamic_cast(*iter); ChartableLineSeriesRowColumnInterface* chartableRowColumnFile = dynamic_cast(*iter); if (chartableBrainFile != NULL) { if (chartableBrainFile->isLineSeriesChartDataTypeSupported(chartDataType)) { CaretMappableDataFile* chartMapFile = chartableBrainFile->getLineSeriesChartCaretMappableDataFile(); CaretAssert(chartMapFile); const ChartDataSource* chartDataSource = cartesianChart->getChartDataSource(); CaretAssert(chartDataSource); const AString chartMapFileName = chartMapFile->getFileName(); const AString chartSourceFileName = chartDataSource->getChartableFileName(); if (chartMapFileName == chartSourceFileName) { const ChartDataSourceModeEnum::Enum sourceMode = chartDataSource->getDataSourceMode(); switch (sourceMode) { case ChartDataSourceModeEnum::CHART_DATA_SOURCE_MODE_INVALID: break; case ChartDataSourceModeEnum::CHART_DATA_SOURCE_MODE_FILE_ROW: { CaretAssert(0); } break; case ChartDataSourceModeEnum::CHART_DATA_SOURCE_MODE_SURFACE_NODE_INDEX: { AString structureName; int32_t surfaceNumberOfNodes = -1; int32_t nodeIndex = -1; chartDataSource->getSurfaceNode(structureName, surfaceNumberOfNodes, nodeIndex); bool structureNameValid = false; const StructureEnum::Enum structure = StructureEnum::fromName(structureName, &structureNameValid); chartDataOut = chartableBrainFile->loadLineSeriesChartDataForSurfaceNode(structure, nodeIndex); } break; case ChartDataSourceModeEnum::CHART_DATA_SOURCE_MODE_SURFACE_NODE_INDICES_AVERAGE: { AString structureName; int32_t surfaceNumberOfNodes = -1; std::vector nodeIndices; chartDataSource->getSurfaceNodeAverage(structureName, surfaceNumberOfNodes, nodeIndices); bool structureNameValid = false; const StructureEnum::Enum structure = StructureEnum::fromName(structureName, &structureNameValid); chartDataOut = chartableBrainFile->loadAverageLineSeriesChartDataForSurfaceNodes(structure, nodeIndices); } break; case ChartDataSourceModeEnum::CHART_DATA_SOURCE_MODE_VOXEL_IJK: { float voxelXYZ[3]; chartDataSource->getVolumeVoxel(voxelXYZ); chartDataOut= chartableBrainFile->loadLineSeriesChartDataForVoxelAtCoordinate(voxelXYZ); } break; } } } } if (chartableRowColumnFile != NULL) { if (chartableRowColumnFile->isLineSeriesChartDataTypeSupported(chartDataType)) { CaretMappableDataFile* chartMapFile = chartableRowColumnFile->getLineSeriesChartCaretMappableDataFile(); CaretAssert(chartMapFile); const ChartDataSource* chartDataSource = cartesianChart->getChartDataSource(); CaretAssert(chartDataSource); const AString chartMapFileName = chartMapFile->getFileName(); const AString chartSourceFileName = chartDataSource->getChartableFileName(); if (chartMapFileName == chartSourceFileName) { const ChartDataSourceModeEnum::Enum sourceMode = chartDataSource->getDataSourceMode(); switch (sourceMode) { case ChartDataSourceModeEnum::CHART_DATA_SOURCE_MODE_INVALID: break; case ChartDataSourceModeEnum::CHART_DATA_SOURCE_MODE_FILE_ROW: { AString chartFileName; int32_t fileRowIndex; chartDataSource->getFileRow(chartFileName, fileRowIndex); chartDataOut = chartableRowColumnFile->loadLineSeriesChartDataForRow(fileRowIndex); } break; case ChartDataSourceModeEnum::CHART_DATA_SOURCE_MODE_SURFACE_NODE_INDEX: { CaretAssert(0); } break; case ChartDataSourceModeEnum::CHART_DATA_SOURCE_MODE_SURFACE_NODE_INDICES_AVERAGE: { CaretAssert(0); } break; case ChartDataSourceModeEnum::CHART_DATA_SOURCE_MODE_VOXEL_IJK: { CaretAssert(0); } break; } } } } } } if (chartDataOut != NULL) { /* * Copy the source of the chart (node, surface, voxel, etc) */ chartDataOut->getChartDataSource()->copy(chartData->getChartDataSource()); const ChartDataCartesian* chartCartData = dynamic_cast(chartData); if (chartCartData != NULL) { chartDataOut->setColor(chartCartData->getColor()); } } return chartDataOut; } /** * Get a text description of the window's content. * * @param tabIndex * Index of the tab for content description. * @param descriptionOut * Description of the window's content. */ void ModelChart::getDescriptionOfContent(const int32_t tabIndex, PlainTextStringBuilder& descriptionOut) const { ChartModel* chartModel = NULL; switch (getSelectedChartDataType(tabIndex)) { case ChartDataTypeEnum::CHART_DATA_TYPE_INVALID: break; case ChartDataTypeEnum::CHART_DATA_TYPE_MATRIX_LAYER: { CaretDataFileSelectionModel* sm = m_chartableMatrixFileSelectionModel[tabIndex]; const CaretDataFile* caretFile = sm->getSelectedFile(); if (caretFile != NULL) { descriptionOut.addLine("Matrix (layer) chart for: " + caretFile->getFileNameNoPath()); return; } } break; case ChartDataTypeEnum::CHART_DATA_TYPE_MATRIX_SERIES: { CaretDataFileSelectionModel* sm = m_chartableMatrixSeriesFileSelectionModel[tabIndex]; const CaretDataFile* caretFile = sm->getSelectedFile(); if (caretFile != NULL) { descriptionOut.addLine("Matrix (series) chart for: " + caretFile->getFileNameNoPath()); return; } } break; case ChartDataTypeEnum::CHART_DATA_TYPE_LINE_DATA_SERIES: chartModel = const_cast(getSelectedDataSeriesChartModel(tabIndex)); break; case ChartDataTypeEnum::CHART_DATA_TYPE_LINE_FREQUENCY_SERIES: chartModel = const_cast(getSelectedFrequencySeriesChartModel(tabIndex)); break; case ChartDataTypeEnum::CHART_DATA_TYPE_LINE_TIME_SERIES: chartModel = const_cast(getSelectedTimeSeriesChartModel(tabIndex)); break; } const ChartModel* chartModelConst = chartModel; if (chartModel != NULL) { descriptionOut.addLine("Chart Type: " + ChartDataTypeEnum::toGuiName(chartModel->getChartDataType())); descriptionOut.pushIndentation(); const std::vector cdVec = chartModelConst->getAllChartDatas(); for (std::vector::const_iterator iter = cdVec.begin(); iter != cdVec.end(); iter++) { const ChartData* cd = *iter; if (cd->isSelected(tabIndex)) { descriptionOut.addLine(cd->getChartDataSource()->getDescription()); } } if (chartModel->isAverageChartDisplaySupported()) { if (chartModel->isAverageChartDisplaySelected()) { descriptionOut.addLine("Average Chart Displayed"); } } descriptionOut.popIndentation(); } else { descriptionOut.addLine("No charts to display"); } } /** * 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 ModelChart::copyTabContent(const int32_t sourceTabIndex, const int32_t destinationTabIndex) { Model::copyTabContent(sourceTabIndex, destinationTabIndex); m_overlaySetArray->copyOverlaySet(sourceTabIndex, destinationTabIndex); m_selectedChartDataType[destinationTabIndex] = m_selectedChartDataType[sourceTabIndex]; *m_chartModelDataSeries[destinationTabIndex] = *m_chartModelDataSeries[sourceTabIndex]; *m_chartModelFrequencySeries[destinationTabIndex] = *m_chartModelFrequencySeries[sourceTabIndex]; *m_chartModelTimeSeries[destinationTabIndex] = *m_chartModelTimeSeries[sourceTabIndex]; m_chartableMatrixFileSelectionModel[destinationTabIndex]->setSelectedFile(m_chartableMatrixFileSelectionModel[sourceTabIndex]->getSelectedFile()); m_chartableMatrixSeriesFileSelectionModel[destinationTabIndex]->setSelectedFile(m_chartableMatrixSeriesFileSelectionModel[sourceTabIndex]->getSelectedFile()); } /** * Set the type of chart selected in the given tab. * * @param tabIndex * Index of tab. * @param dataType * Type of data for chart. */ void ModelChart::setSelectedChartDataType(const int32_t tabIndex, const ChartDataTypeEnum::Enum dataType) { CaretAssertArrayIndex(m_selectedChartDataType, BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS, tabIndex); m_selectedChartDataType[tabIndex] = dataType; } /** * Get the valid chart data types based upon the currently loaded files. * * @param validChartDataTypesOut * Output containing valid chart data types. */ void ModelChart::getValidChartDataTypes(std::vector& validChartDataTypesOut) const { validChartDataTypesOut.clear(); bool haveDataSeries = false; bool haveFrequencySeries = false; bool haveMatrixLayers = false; bool haveMatrixSeries = false; bool haveTimeSeries = false; std::vector allLineChartableFiles; m_brain->getAllChartableLineSeriesDataFiles(allLineChartableFiles); for (std::vector::iterator fileIter = allLineChartableFiles.begin(); fileIter != allLineChartableFiles.end(); fileIter++) { ChartableLineSeriesInterface* 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 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); for (std::vector::iterator fileIter = allMatrixChartableFiles.begin(); fileIter != allMatrixChartableFiles.end(); fileIter++) { ChartableMatrixInterface* chartFile = *fileIter; std::vector chartDataTypes; chartFile->getSupportedMatrixChartDataTypes(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: haveMatrixLayers = true; break; case ChartDataTypeEnum::CHART_DATA_TYPE_MATRIX_SERIES: haveMatrixSeries = true; break; case ChartDataTypeEnum::CHART_DATA_TYPE_LINE_DATA_SERIES: break; case ChartDataTypeEnum::CHART_DATA_TYPE_LINE_FREQUENCY_SERIES: break; case ChartDataTypeEnum::CHART_DATA_TYPE_LINE_TIME_SERIES: break; } } } if (haveDataSeries) { validChartDataTypesOut.push_back(ChartDataTypeEnum::CHART_DATA_TYPE_LINE_DATA_SERIES); } if (haveFrequencySeries) { validChartDataTypesOut.push_back(ChartDataTypeEnum::CHART_DATA_TYPE_LINE_FREQUENCY_SERIES); } if (haveMatrixLayers) { validChartDataTypesOut.push_back(ChartDataTypeEnum::CHART_DATA_TYPE_MATRIX_LAYER); } if (haveMatrixSeries) { validChartDataTypesOut.push_back(ChartDataTypeEnum::CHART_DATA_TYPE_MATRIX_SERIES); } if (haveTimeSeries) { validChartDataTypesOut.push_back(ChartDataTypeEnum::CHART_DATA_TYPE_LINE_TIME_SERIES); } } /** * Get the type of chart selected in the given tab. * * @param tabIndex * Index of tab. * @return * Chart type in the given tab. */ ChartDataTypeEnum::Enum ModelChart::getSelectedChartDataType(const int32_t tabIndex) const { CaretAssertArrayIndex(m_selectedChartDataType, BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS, tabIndex); ChartDataTypeEnum::Enum chartDataType = m_selectedChartDataType[tabIndex]; /* * Verify that the selected chart data type is valid. */ std::vector validChartDataTypes; getValidChartDataTypes(validChartDataTypes); if (std::find(validChartDataTypes.begin(), validChartDataTypes.end(), chartDataType) == validChartDataTypes.end()) { chartDataType = ChartDataTypeEnum::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 ( ! 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; /* * 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(); iter != allChartDataTypes.end(); iter++) { const ChartDataTypeEnum::Enum cdt = *iter; if (std::find(validChartDataTypes.begin(), validChartDataTypes.end(), cdt) != validChartDataTypes.end()) { if (chartDataType == ChartDataTypeEnum::CHART_DATA_TYPE_INVALID) { chartDataType = cdt; } switch (cdt) { case ChartDataTypeEnum::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; } break; case ChartDataTypeEnum::CHART_DATA_TYPE_MATRIX_SERIES: if (chartDataTypeWithValidData == ChartDataTypeEnum::CHART_DATA_TYPE_INVALID) { chartDataTypeWithValidData = ChartDataTypeEnum::CHART_DATA_TYPE_MATRIX_SERIES; } break; case ChartDataTypeEnum::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; } } break; case ChartDataTypeEnum::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; } } break; case ChartDataTypeEnum::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; } } break; } } } if (chartDataTypeWithValidData != ChartDataTypeEnum::CHART_DATA_TYPE_INVALID) { chartDataType = chartDataTypeWithValidData; } else if (chartDataType == ChartDataTypeEnum::CHART_DATA_TYPE_INVALID) { chartDataType = validChartDataTypes[0]; } } } /* * Selected type may have changed due to loaded files changing */ m_selectedChartDataType[tabIndex] = chartDataType; return chartDataType; } /** * Get the data series chart model selected in the given tab. * * @param tabIndex * Index of tab. * @return * Data series chart model in the given tab. */ ChartModelDataSeries* ModelChart::getSelectedDataSeriesChartModel(const int32_t tabIndex) { const ChartModelDataSeries* model = getSelectedDataSeriesChartModelHelper(tabIndex); if (model == NULL) { return NULL; } ChartModelDataSeries* nonConstModel = const_cast(model); return nonConstModel; } /** * Get the data series chart model selected in the given tab. * * @param tabIndex * Index of tab. * @return * Data series chart model in the given tab. */ const ChartModelDataSeries* ModelChart::getSelectedDataSeriesChartModel(const int32_t tabIndex) const { return getSelectedDataSeriesChartModelHelper(tabIndex); } /** * Helper to get the data series chart model selected in the given tab. * * @param tabIndex * Index of tab. * @return * Data series chart model in the given tab. */ const ChartModelDataSeries* ModelChart::getSelectedDataSeriesChartModelHelper(const int32_t tabIndex) const { CaretAssertArrayIndex(m_chartModelDataSeries, BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS, tabIndex); return m_chartModelDataSeries[tabIndex]; } /** * Get the time series chart model selected in the given tab. * * @param tabIndex * Index of tab. * @return * time series chart model in the given tab. */ ChartModelFrequencySeries* ModelChart::getSelectedFrequencySeriesChartModel(const int32_t tabIndex) { const ChartModelFrequencySeries* model = getSelectedFrequencySeriesChartModelHelper(tabIndex); if (model == NULL) { return NULL; } ChartModelFrequencySeries* nonConstModel = const_cast(model); return nonConstModel; } /** * Get the time series chart model selected in the given tab. * * @param tabIndex * Index of tab. * @return * time series chart model in the given tab. */ const ChartModelFrequencySeries* ModelChart::getSelectedFrequencySeriesChartModel(const int32_t tabIndex) const { return getSelectedFrequencySeriesChartModelHelper(tabIndex); } /** * Helper to get the time series chart model selected in the given tab. * * @param tabIndex * Index of tab. * @return * time series chart model in the given tab. */ const ChartModelFrequencySeries* ModelChart::getSelectedFrequencySeriesChartModelHelper(const int32_t tabIndex) const { CaretAssertArrayIndex(m_chartModelFrequencySeries, BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS, tabIndex); return m_chartModelFrequencySeries[tabIndex]; } /** * Get the time series chart model selected in the given tab. * * @param tabIndex * Index of tab. * @return * time series chart model in the given tab. */ ChartModelTimeSeries* ModelChart::getSelectedTimeSeriesChartModel(const int32_t tabIndex) { const ChartModelTimeSeries* model = getSelectedTimeSeriesChartModelHelper(tabIndex); if (model == NULL) { return NULL; } ChartModelTimeSeries* nonConstModel = const_cast(model); return nonConstModel; } /** * Get the time series chart model selected in the given tab. * * @param tabIndex * Index of tab. * @return * time series chart model in the given tab. */ const ChartModelTimeSeries* ModelChart::getSelectedTimeSeriesChartModel(const int32_t tabIndex) const { return getSelectedTimeSeriesChartModelHelper(tabIndex); } /** * Helper to get the time series chart model selected in the given tab. * * @param tabIndex * Index of tab. * @return * time series chart model in the given tab. */ const ChartModelTimeSeries* ModelChart::getSelectedTimeSeriesChartModelHelper(const int32_t tabIndex) const { CaretAssertArrayIndex(m_chartModelTimeSeries, BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS, tabIndex); return m_chartModelTimeSeries[tabIndex]; } /** * Get the chartable matrix parcel file selection model for the given tab. * * @param tabIndex * Index of the tab. * @return * Chartable file selection model for the tab. */ CaretDataFileSelectionModel* ModelChart::getChartableMatrixParcelFileSelectionModel(const int32_t tabIndex) { CaretAssertArrayIndex(m_chartableMatrixFileSelectionModel, BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS, tabIndex); return m_chartableMatrixFileSelectionModel[tabIndex]; } /** * Get the chartable matrix series file selection model for the given tab. * * @param tabIndex * Index of the tab. * @return * Chartable file selection model for the tab. */ CaretDataFileSelectionModel* ModelChart::getChartableMatrixSeriesFileSelectionModel(const int32_t tabIndex) { CaretAssertArrayIndex(m_chartableMatrixSeriesFileSelectionModel, BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS, tabIndex); return m_chartableMatrixSeriesFileSelectionModel[tabIndex]; } connectome-workbench-1.2.3+git41-gc4c6c90/src/Brain/ModelChart.h000066400000000000000000000205521300200146000237760ustar00rootroot00000000000000#ifndef __MODEL_CHART_H__ #define __MODEL_CHART_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 "ChartDataTypeEnum.h" #include "EventListenerInterface.h" #include "MapYokingGroupEnum.h" #include "Model.h" #include "StructureEnum.h" namespace caret { class CaretDataFileSelectionModel; class ChartData; class ChartDataCartesian; class ChartDataSource; class ChartableLineSeriesBrainordinateInterface; class ChartableLineSeriesInterface; class ChartableLineSeriesRowColumnInterface; class ChartableMatrixInterface; class ChartModel; class ChartModelDataSeries; class ChartModelFrequencySeries; class ChartModelTimeSeries; class CiftiConnectivityMatrixParcelFile; class CiftiMappableDataFile; class OverlaySetArray; class SurfaceFile; /// Controls the display of a chart. class ModelChart : public Model, public EventListenerInterface { public: ModelChart(Brain* brain); virtual ~ModelChart(); void initializeOverlays(); AString getNameForGUI(const bool includeStructureFlag) const; virtual AString getNameForBrowserTab() const; 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 loadChartDataForYokedCiftiMappableFiles(const MapYokingGroupEnum::Enum mapYokingGroup, const int32_t mapIndex); OverlaySet* getOverlaySet(const int tabIndex); const OverlaySet* getOverlaySet(const int tabIndex) const; virtual void receiveEvent(Event* event); void getValidChartDataTypes(std::vector& validChartDataTypesOut) const; ChartDataTypeEnum::Enum getSelectedChartDataType(const int32_t tabIndex) const; void setSelectedChartDataType(const int32_t tabIndex, const ChartDataTypeEnum::Enum dataType); ChartModelDataSeries* getSelectedDataSeriesChartModel(const int32_t tabIndex); const ChartModelDataSeries* getSelectedDataSeriesChartModel(const int32_t tabIndex) const; ChartModelFrequencySeries* getSelectedFrequencySeriesChartModel(const int32_t tabIndex); const ChartModelFrequencySeries* getSelectedFrequencySeriesChartModel(const int32_t tabIndex) const; ChartModelTimeSeries* getSelectedTimeSeriesChartModel(const int32_t tabIndex); const ChartModelTimeSeries* getSelectedTimeSeriesChartModel(const int32_t tabIndex) const; virtual void getDescriptionOfContent(const int32_t tabIndex, PlainTextStringBuilder& descriptionOut) const; virtual void copyTabContent(const int32_t sourceTabIndex, const int32_t destinationTabIndex); void reset(); CaretDataFileSelectionModel* getChartableMatrixParcelFileSelectionModel(const int32_t tabIndex); CaretDataFileSelectionModel* getChartableMatrixSeriesFileSelectionModel(const int32_t tabIndex); protected: virtual void saveModelSpecificInformationToScene(const SceneAttributes* sceneAttributes, SceneClass* sceneClass); virtual void restoreModelSpecificInformationFromScene(const SceneAttributes* sceneAttributes, const SceneClass* sceneClass); private: ModelChart(const ModelChart&); ModelChart& operator=(const ModelChart&); void addChartToChartModels(const std::vector& tabIndices, ChartData* chartData); void initializeCharts(); void removeAllCharts(); const ChartModelDataSeries* getSelectedDataSeriesChartModelHelper(const int32_t tabIndex) const; const ChartModelFrequencySeries* getSelectedFrequencySeriesChartModelHelper(const int32_t tabIndex) const; const ChartModelTimeSeries* getSelectedTimeSeriesChartModelHelper(const int32_t tabIndex) const; void saveChartModelsToScene(const SceneAttributes* sceneAttributes, SceneClass* sceneClass, const std::vector& tabIndices, std::set& validChartDataIDsOut); void restoreChartModelsFromScene(const SceneAttributes* sceneAttributes, const SceneClass* sceneClass); void getTabsAndBrainordinateChartFilesForLineChartLoading(std::map >& chartBrainordinateFileEnabledTabsOut) const; void getTabsAndRowColumnChartFilesForLineChartLoading(std::map >& chartRowColumnFilesEnabledTabsOut) const; void getTabsAndLineSeriesChartFilesForLineChartLoading(std::map >& chartFileEnabledTabsOut) const; ChartData* loadCartesianChartWhenRestoringScene(const ChartData* chartData); /** Overlays sets for this model and for each tab */ OverlaySetArray* m_overlaySetArray; mutable ChartDataTypeEnum::Enum m_selectedChartDataType[BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS]; /** Chart model for data-series data */ ChartModelDataSeries* m_chartModelDataSeries[BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS]; /** Chart model for frequency-series data */ ChartModelFrequencySeries* m_chartModelFrequencySeries[BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS]; /** Chart model for time-series data */ ChartModelTimeSeries* m_chartModelTimeSeries[BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS]; /** Contains data series charts */ std::list > m_dataSeriesChartData; /** Contains time series charts */ std::list > m_frequencySeriesChartData; /** Contains time series charts */ std::list > m_timeSeriesChartData; std::vector m_previousChartMatrixFiles; CaretDataFileSelectionModel* m_chartableMatrixFileSelectionModel[BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS]; CaretDataFileSelectionModel* m_chartableMatrixSeriesFileSelectionModel[BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS]; SceneClassAssistant* m_sceneAssistant; }; } // namespace #endif // __MODEL_CHART_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Brain/ModelSurface.cxx000066400000000000000000000166311300200146000247030ustar00rootroot00000000000000/*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 "BrainStructure.h" #include "BrowserTabContent.h" #include "BoundingBox.h" #include "CaretAssert.h" #include "EventManager.h" #include "EventModelSurfaceGet.h" #include "ModelSurface.h" #include "Brain.h" #include "BrainOpenGL.h" #include "OverlaySet.h" #include "SceneClass.h" #include "SceneClassAssistant.h" #include "Surface.h" using namespace caret; /** * Constructor. * @param surface - surface for this model. * */ ModelSurface::ModelSurface(Brain* brain, Surface* surface) : Model(ModelTypeEnum::MODEL_TYPE_SURFACE, brain) { CaretAssert(surface); initializeMembersModelSurface(); m_surface = surface; EventManager::get()->addEventListener(this, EventTypeEnum::EVENT_MODEL_SURFACE_GET); } /** * Destructor */ ModelSurface::~ModelSurface() { EventManager::get()->removeAllEventsFromListener(this); } /** * Receive an event. * * @param event * The event that the receive can respond to. */ void ModelSurface::receiveEvent(Event* event) { if (event->getEventType() == EventTypeEnum::EVENT_MODEL_SURFACE_GET) { EventModelSurfaceGet* getSurfaceEvent = dynamic_cast(event); CaretAssert(getSurfaceEvent); /* * Looking for this model? */ if (getSurfaceEvent->getSurface() == getSurface()) { getSurfaceEvent->setModelSurface(this); getSurfaceEvent->setEventProcessed(); } } else { CaretAssertMessage(0, "Unexpected event: " + EventTypeEnum::toName(event->getEventType())); } } void ModelSurface::initializeMembersModelSurface() { m_surface = NULL; } /** * Get the surface in this model. * @return Surface in this model. */ Surface* ModelSurface::getSurface() { return m_surface; } /** * Get the surface in this model. * @return Surface in this model. */ const Surface* ModelSurface::getSurface() const { return m_surface; } /** * 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 ModelSurface::getNameForGUI(const bool includeStructureFlag) const { AString name; if (includeStructureFlag) { const StructureEnum::Enum structure = m_surface->getStructure(); name += StructureEnum::toGuiName(structure); name += " "; } name += m_surface->getFileNameNoPath(); return name; } /** * @return The name that should be displayed in the tab * displaying this model. */ AString ModelSurface::getNameForBrowserTab() const { const StructureEnum::Enum structure = m_surface->getStructure(); AString name = StructureEnum::toGuiName(structure); if (structure == StructureEnum::CEREBELLUM) { name = "Cbllm"; } return name; } /** * Set the scaling so that the model fills the window. * */ //void //ModelSurface::setDefaultScalingToFitWindow() //{ // BoundingBox bounds; // m_surface->getBounds(bounds); // // float bigY = std::max(std::abs(bounds.getMinY()), bounds.getMaxY()); // float percentScreenY = BrainOpenGL::getModelViewingHalfWindowHeight() * 0.90f; // float scale = percentScreenY / bigY; // m_defaultModelScaling = scale; // // for (int32_t i = 0; i < BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS; i++) { // setScaling(i, m_defaultModelScaling); // } //} /** * Get the overlay set for the given tab. * @param tabIndex * Index of tab. * @return * Overlay set at the given tab index. */ OverlaySet* ModelSurface::getOverlaySet(const int tabIndex) { if (m_surface != NULL) { BrainStructure* brainStructure = m_surface->getBrainStructure(); if (brainStructure != NULL) { return brainStructure->getOverlaySet(tabIndex); } } return NULL; } /** * Get the overlay set for the given tab. * @param tabIndex * Index of tab. * @return * Overlay set at the given tab index. */ const OverlaySet* ModelSurface::getOverlaySet(const int tabIndex) const { if (m_surface != NULL) { const BrainStructure* brainStructure = m_surface->getBrainStructure(); if (brainStructure != NULL) { return brainStructure->getOverlaySet(tabIndex); } } return NULL; } /** * Initilize the overlays for this model. */ void ModelSurface::initializeOverlays() { } /** * 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 ModelSurface::saveModelSpecificInformationToScene(const SceneAttributes* /*sceneAttributes*/, SceneClass* /*sceneClass*/) { /* nothing to add to scene */ } /** * 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 ModelSurface::restoreModelSpecificInformationFromScene(const SceneAttributes* /*sceneAttributes*/, const SceneClass* /*sceneClass*/) { /* nothing to restore from scene */ } /** * Get a text description of the window's content. * * @param tabIndex * Index of the tab for content description. * @param descriptionOut * Description of the window's content. */ void ModelSurface::getDescriptionOfContent(const int32_t /*tabIndex*/, PlainTextStringBuilder& descriptionOut) const { AString msg; const Surface* surface = getSurface(); if (surface != NULL) { surface->getDescriptionOfContent(descriptionOut); } } /** * 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 ModelSurface::copyTabContent(const int32_t sourceTabIndex, const int32_t destinationTabIndex) { Model::copyTabContent(sourceTabIndex, destinationTabIndex); } connectome-workbench-1.2.3+git41-gc4c6c90/src/Brain/ModelSurface.h000066400000000000000000000054751300200146000243340ustar00rootroot00000000000000#ifndef __MODEL_SURFACE_H__ #define __MODEL_SURFACE_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 "EventListenerInterface.h" #include "Model.h" namespace caret { class Surface; /// Controls the display of a surface. class ModelSurface : public Model, public EventListenerInterface { public: ModelSurface(Brain* brain, Surface* surface); virtual ~ModelSurface(); virtual void receiveEvent(Event* event); OverlaySet* getOverlaySet(const int tabIndex); const OverlaySet* getOverlaySet(const int tabIndex) const; void initializeOverlays(); virtual void getDescriptionOfContent(const int32_t tabIndex, PlainTextStringBuilder& descriptionOut) const; virtual void copyTabContent(const int32_t sourceTabIndex, const int32_t destinationTabIndex); private: ModelSurface(const ModelSurface&); ModelSurface& operator=(const ModelSurface&); private: void initializeMembersModelSurface(); public: Surface* getSurface(); const Surface* getSurface() const; AString getNameForGUI(const bool includeStructureFlag) const; virtual AString getNameForBrowserTab() const; //void setDefaultScalingToFitWindow(); protected: virtual void saveModelSpecificInformationToScene(const SceneAttributes* sceneAttributes, SceneClass* sceneClass); virtual void restoreModelSpecificInformationFromScene(const SceneAttributes* sceneAttributes, const SceneClass* sceneClass); private: /**Surface that uses this model */ Surface* m_surface; }; } // namespace #endif // __MODEL_SURFACE_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Brain/ModelSurfaceMontage.cxx000066400000000000000000001234331300200146000262150ustar00rootroot00000000000000/*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 "BrainStructure.h" #include "BrowserTabContent.h" #include "BoundingBox.h" #include "Brain.h" #include "BrainOpenGL.h" #include "CaretAssert.h" #include "EventManager.h" #include "EventModelSurfaceGet.h" #include "ModelSurfaceMontage.h" #include "OverlaySet.h" #include "SceneAttributes.h" #include "SceneClass.h" #include "SceneClassArray.h" #include "SceneClassAssistant.h" #include "SceneObjectMapIntegerKey.h" #include "ScenePrimitive.h" #include "SurfaceMontageConfigurationCerebellar.h" #include "SurfaceMontageConfigurationCerebral.h" #include "SurfaceMontageConfigurationFlatMaps.h" #include "SurfaceSelectionModel.h" using namespace caret; /** * Constructor. * @param surface - surface for this model. * */ ModelSurfaceMontage::ModelSurfaceMontage(Brain* brain) : Model(ModelTypeEnum::MODEL_TYPE_SURFACE_MONTAGE, brain) { std::vector validSurfaceTypes; validSurfaceTypes.push_back(SurfaceTypeEnum::ANATOMICAL); validSurfaceTypes.push_back(SurfaceTypeEnum::RECONSTRUCTION); validSurfaceTypes.push_back(SurfaceTypeEnum::INFLATED); validSurfaceTypes.push_back(SurfaceTypeEnum::VERY_INFLATED); for (int32_t i = 0; i < BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS; i++) { m_selectedConfigurationType[i] = SurfaceMontageConfigurationTypeEnum::CEREBRAL_CORTEX_CONFIGURATION; m_cerebellarConfiguration[i] = new SurfaceMontageConfigurationCerebellar(i); m_cerebralConfiguration[i] = new SurfaceMontageConfigurationCerebral(i); m_flatMapsConfiguration[i] = new SurfaceMontageConfigurationFlatMaps(i); } std::vector overlaySurfaceStructures; overlaySurfaceStructures.push_back(StructureEnum::CORTEX_LEFT); overlaySurfaceStructures.push_back(StructureEnum::CORTEX_RIGHT); } /** * Destructor */ ModelSurfaceMontage::~ModelSurfaceMontage() { EventManager::get()->removeAllEventsFromListener(this); for (int32_t i = 0; i < BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS; i++) { delete m_cerebellarConfiguration[i]; delete m_cerebralConfiguration[i]; delete m_flatMapsConfiguration[i]; } } /** * Receive an event. * * @param event * The event that the receive can respond to. */ void ModelSurfaceMontage::receiveEvent(Event* /*event*/) { } /** * Initialize the selected surfaces. */ void ModelSurfaceMontage::initializeSelectedSurfaces() { for (int32_t i = 0; i < BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS; i++) { m_cerebellarConfiguration[i]->initializeSelectedSurfaces(); m_cerebralConfiguration[i]->initializeSelectedSurfaces(); m_flatMapsConfiguration[i]->initializeSelectedSurfaces(); } } /** * 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 ModelSurfaceMontage::getNameForGUI(const bool /*includeStructureFlag*/) const { AString name = "Surface Montage"; return name; } /** * @return The name that should be displayed in the tab * displaying this model. */ AString ModelSurfaceMontage::getNameForBrowserTab() const { AString name = "Montage"; return name; } /** * Get the overlay set for the given tab. * @param tabIndex * Index of tab. * @return * Overlay set at the given tab index. */ OverlaySet* ModelSurfaceMontage::getOverlaySet(const int tabIndex) { return getSelectedConfiguration(tabIndex)->getOverlaySet(); } /** * Get the overlay set for the given tab. * @param tabIndex * Index of tab. * @return * Overlay set at the given tab index. */ const OverlaySet* ModelSurfaceMontage::getOverlaySet(const int tabIndex) const { return getSelectedConfiguration(tabIndex)->getOverlaySet(); } /** * Initilize the overlays for this model. */ void ModelSurfaceMontage::initializeOverlays() { for (int32_t i = 0; i < BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS; i++) { m_cerebellarConfiguration[i]->getOverlaySet()->initializeOverlays(); m_cerebralConfiguration[i]->getOverlaySet()->initializeOverlays(); m_flatMapsConfiguration[i]->getOverlaySet()->initializeOverlays(); } } /** * Get a surface for the given struture in the given tab. Since there * may be one surface of the given structure, the returned surface * may be different in future calls based upon the surfaces the user * has chosen for display. * * @param structure * Structure for the surface * @param windowTabNumber * Tab number of window. * @param Pointer to selected surface for given structure or NULL if not available. */ Surface* ModelSurfaceMontage::getSelectedSurface(const StructureEnum::Enum structure, const int32_t windowTabNumber) { std::vector selectionModels; switch (getSelectedConfigurationType(windowTabNumber)) { case SurfaceMontageConfigurationTypeEnum::CEREBELLAR_CORTEX_CONFIGURATION: { SurfaceMontageConfigurationCerebellar* smcc = getCerebellarConfiguration(windowTabNumber); if (structure == StructureEnum::CEREBELLUM) { selectionModels.push_back(smcc->getFirstSurfaceSelectionModel()); selectionModels.push_back(smcc->getSecondSurfaceSelectionModel()); } } break; case SurfaceMontageConfigurationTypeEnum::CEREBRAL_CORTEX_CONFIGURATION: { SurfaceMontageConfigurationCerebral* smcc = getCerebralConfiguration(windowTabNumber); switch (structure) { case StructureEnum::CORTEX_LEFT: selectionModels.push_back(smcc->getLeftFirstSurfaceSelectionModel()); selectionModels.push_back(smcc->getLeftSecondSurfaceSelectionModel()); break; case StructureEnum::CORTEX_RIGHT: selectionModels.push_back(smcc->getRightFirstSurfaceSelectionModel()); selectionModels.push_back(smcc->getRightSecondSurfaceSelectionModel()); break; default: break; } } break; case SurfaceMontageConfigurationTypeEnum::FLAT_CONFIGURATION: { SurfaceMontageConfigurationFlatMaps* smcfm = getFlatMapsConfiguration(windowTabNumber); switch (structure) { case StructureEnum::CEREBELLUM: selectionModels.push_back(smcfm->getCerebellumSurfaceSelectionModel()); case StructureEnum::CORTEX_LEFT: selectionModels.push_back(smcfm->getLeftSurfaceSelectionModel()); break; case StructureEnum::CORTEX_RIGHT: selectionModels.push_back(smcfm->getRightSurfaceSelectionModel()); break; default: break; } } break; } Surface* surfaceOut = NULL; for (std::vector::iterator iter = selectionModels.begin(); iter != selectionModels.end(); iter++) { SurfaceSelectionModel* sm = *iter; if (sm != NULL) { surfaceOut = sm->getSurface(); break; } } return surfaceOut; } /** * Get the selected configuration for the given tab. * * @param tabIndex * Index of tab for the selected configuration. */ SurfaceMontageConfigurationAbstract* ModelSurfaceMontage::getSelectedConfiguration(const int32_t tabIndex) { switch (getSelectedConfigurationType(tabIndex)) { case SurfaceMontageConfigurationTypeEnum::CEREBELLAR_CORTEX_CONFIGURATION: CaretAssertArrayIndex(m_cerebellarConfiguration, BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS, tabIndex); return m_cerebellarConfiguration[tabIndex]; break; case SurfaceMontageConfigurationTypeEnum::CEREBRAL_CORTEX_CONFIGURATION: CaretAssertArrayIndex(m_cerebralConfiguration, BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS, tabIndex); return m_cerebralConfiguration[tabIndex]; break; case SurfaceMontageConfigurationTypeEnum::FLAT_CONFIGURATION: CaretAssertArrayIndex(m_flatMapsConfiguration, BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS, tabIndex); return m_flatMapsConfiguration[tabIndex]; break; } return NULL; } /** * Get the selected configuration for the given tab. * * @param tabIndex * Index of tab for the selected configuration. */ const SurfaceMontageConfigurationAbstract* ModelSurfaceMontage::getSelectedConfiguration(const int32_t tabIndex) const { ModelSurfaceMontage* msm = const_cast(this); return msm->getSelectedConfiguration(tabIndex); } /** * @return The type of configuration in the given tab. * * @param tabIndex * Index of the tab. */ SurfaceMontageConfigurationTypeEnum::Enum ModelSurfaceMontage::getSelectedConfigurationType(const int32_t tabIndex) const { /* * Find valid configurations */ std::vector< SurfaceMontageConfigurationTypeEnum::Enum> validTypes; if (m_cerebellarConfiguration[tabIndex]->isValid()) { validTypes.push_back(SurfaceMontageConfigurationTypeEnum::CEREBELLAR_CORTEX_CONFIGURATION); } if (m_cerebralConfiguration[tabIndex]->isValid()) { validTypes.push_back(SurfaceMontageConfigurationTypeEnum::CEREBRAL_CORTEX_CONFIGURATION); } if (m_flatMapsConfiguration[tabIndex]->isValid()) { validTypes.push_back(SurfaceMontageConfigurationTypeEnum::FLAT_CONFIGURATION); } /* * Verify selected type is valid */ bool validTypeSelected = false; switch (m_selectedConfigurationType[tabIndex]) { case SurfaceMontageConfigurationTypeEnum::CEREBELLAR_CORTEX_CONFIGURATION: CaretAssertArrayIndex(m_cerebellarConfiguration, BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS, tabIndex); if (m_cerebellarConfiguration[tabIndex]->isValid()) { validTypeSelected = true; } break; case SurfaceMontageConfigurationTypeEnum::CEREBRAL_CORTEX_CONFIGURATION: CaretAssertArrayIndex(m_cerebralConfiguration, BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS, tabIndex); if (m_cerebralConfiguration[tabIndex]->isValid()) { validTypeSelected = true; } break; case SurfaceMontageConfigurationTypeEnum::FLAT_CONFIGURATION: CaretAssertArrayIndex(m_flatMapsConfiguration, BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS, tabIndex); if (m_flatMapsConfiguration[tabIndex]->isValid()) { validTypeSelected = true; } break; } /* * If configuration type selected is not valid, choose another * configuration type. */ if ( ! validTypeSelected) { if ( ! validTypes.empty()) { m_selectedConfigurationType[tabIndex] = validTypes[0]; } } return m_selectedConfigurationType[tabIndex]; } /** * Set type of configuration in the given tab. * * @param tabIndex * Index of the tab. * @param configurationType * New configuration type for the tab index. */ void ModelSurfaceMontage::setSelectedConfigurationType(const int32_t tabIndex, const SurfaceMontageConfigurationTypeEnum::Enum configurationType) { m_selectedConfigurationType[tabIndex] = configurationType; } /** * Get the cerebellar configuration in the given tab. * * @param tabIndex * Index of tab. * @return * Cerebellar configuration. */ SurfaceMontageConfigurationCerebellar * ModelSurfaceMontage::getCerebellarConfiguration(const int32_t tabIndex) { CaretAssertArrayIndex(m_cerebellarConfiguration, BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS, tabIndex); return m_cerebellarConfiguration[tabIndex]; } /** * Get the cerebellar configuration in the given tab. * * @param tabIndex * Index of tab. * @return * Cerebellar configuration. */ const SurfaceMontageConfigurationCerebellar* ModelSurfaceMontage::getCerebellarConfiguration(const int32_t tabIndex) const { CaretAssertArrayIndex(m_cerebellarConfiguration, BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS, tabIndex); return m_cerebellarConfiguration[tabIndex]; } /** * Get the cerebral configuration in the given tab. * * @param tabIndex * Index of tab. * @return * Cerebral configuration. */ SurfaceMontageConfigurationCerebral * ModelSurfaceMontage::getCerebralConfiguration(const int32_t tabIndex) { CaretAssertArrayIndex(m_cerebralConfiguration, BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS, tabIndex); return m_cerebralConfiguration[tabIndex]; } /** * Get the cerebral configuration in the given tab. * * @param tabIndex * Index of tab. * @return * Cerebral configuration. */ const SurfaceMontageConfigurationCerebral* ModelSurfaceMontage::getCerebralConfiguration(const int32_t tabIndex) const { CaretAssertArrayIndex(m_cerebralConfiguration, BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS, tabIndex); return m_cerebralConfiguration[tabIndex]; } /** * Get the flat maps configuration in the given tab. * * @param tabIndex * Index of tab. * @return * Flat maps configuration. */ SurfaceMontageConfigurationFlatMaps * ModelSurfaceMontage::getFlatMapsConfiguration(const int32_t tabIndex) { CaretAssertArrayIndex(m_flatMapsConfiguration, BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS, tabIndex); return m_flatMapsConfiguration[tabIndex]; } /** * Get the flat maps configuration in the given tab. * * @param tabIndex * Index of tab. * @return * Flat maps configuration. */ const SurfaceMontageConfigurationFlatMaps* ModelSurfaceMontage::getFlatMapsConfiguration(const int32_t tabIndex) const { CaretAssertArrayIndex(m_flatMapsConfiguration, BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS, tabIndex); return m_flatMapsConfiguration[tabIndex]; } /** * 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 ModelSurfaceMontage::saveModelSpecificInformationToScene(const SceneAttributes* sceneAttributes, SceneClass* sceneClass) { sceneClass->addInteger("montageVersion", 3); SceneObjectMapIntegerKey* cerebellarConfigurationMap = new SceneObjectMapIntegerKey("m_cerebellarConfiguration", SceneObjectDataTypeEnum::SCENE_CLASS); SceneObjectMapIntegerKey* cerebralConfigurationMap = new SceneObjectMapIntegerKey("m_cerebralConfiguration", SceneObjectDataTypeEnum::SCENE_CLASS); SceneObjectMapIntegerKey* flatConfigurationMap = new SceneObjectMapIntegerKey("m_flatMapsConfiguration", SceneObjectDataTypeEnum::SCENE_CLASS); /* * Get indices of tabs that are to be saved to scene. */ const std::vector tabIndices = sceneAttributes->getIndicesOfTabsForSavingToScene(); const int32_t numActiveTabs = static_cast(tabIndices.size()); sceneClass->addEnumeratedTypeArrayForTabIndices("m_selectedConfigurationType", m_selectedConfigurationType, tabIndices); for (int32_t i = 0; i < numActiveTabs; i++) { const int32_t tabIndex = tabIndices[i]; const AString tabString = ("[" + AString::number(tabIndex) + "]"); cerebellarConfigurationMap->addClass(tabIndex, m_cerebellarConfiguration[tabIndex]->saveToScene(sceneAttributes, "m_cerebellarConfiguration" + tabString)); cerebralConfigurationMap->addClass(tabIndex, m_cerebralConfiguration[tabIndex]->saveToScene(sceneAttributes, "m_cerebralConfiguration" + tabString)); flatConfigurationMap->addClass(tabIndex, m_flatMapsConfiguration[tabIndex]->saveToScene(sceneAttributes, "m_flatMapsConfiguration" + tabString)); } sceneClass->addChild(cerebellarConfigurationMap); sceneClass->addChild(cerebralConfigurationMap); sceneClass->addChild(flatConfigurationMap); // /* // * Surfaces // */ // SceneObjectMapIntegerKey* leftSurfaceMap = new SceneObjectMapIntegerKey("m_leftSurfaceSelectionModel", // SceneObjectDataTypeEnum::SCENE_CLASS); // SceneObjectMapIntegerKey* leftSecondSurfaceMap = new SceneObjectMapIntegerKey("m_leftSecondSurfaceSelectionModel", // SceneObjectDataTypeEnum::SCENE_CLASS); // SceneObjectMapIntegerKey* rightSurfaceMap = new SceneObjectMapIntegerKey("m_rightSurfaceSelectionModel", // SceneObjectDataTypeEnum::SCENE_CLASS); // SceneObjectMapIntegerKey* rightSecondSurfaceMap = new SceneObjectMapIntegerKey("m_rightSecondSurfaceSelectionModel", // SceneObjectDataTypeEnum::SCENE_CLASS); // for (int32_t iat = 0; iat < numActiveTabs; iat++) { // const int32_t tabIndex = tabIndices[iat]; // const AString tabString = ("[" + AString::number(tabIndex) + "]"); // // leftSurfaceMap->addClass(tabIndex, // m_leftSurfaceSelectionModel[tabIndex]->saveToScene(sceneAttributes, // "m_leftSurfaceSelectionModel" + tabString)); // leftSecondSurfaceMap->addClass(tabIndex, // m_leftSecondSurfaceSelectionModel[tabIndex]->saveToScene(sceneAttributes, // "m_leftSecondSurfaceSelectionModel" + tabString)); // rightSurfaceMap->addClass(tabIndex, // m_rightSurfaceSelectionModel[tabIndex]->saveToScene(sceneAttributes, // "m_rightSurfaceSelectionModel" + tabString)); // rightSecondSurfaceMap->addClass(tabIndex, // m_rightSecondSurfaceSelectionModel[tabIndex]->saveToScene(sceneAttributes, // "m_rightSecondSurfaceSelectionModel" + tabString)); // } // sceneClass->addChild(leftSurfaceMap); // sceneClass->addChild(leftSecondSurfaceMap); // sceneClass->addChild(rightSurfaceMap); // sceneClass->addChild(rightSecondSurfaceMap); // // /* // * Selections // */ // SceneObjectMapIntegerKey* rightEnabledMap = new SceneObjectMapIntegerKey("m_rightEnabled", // SceneObjectDataTypeEnum::SCENE_BOOLEAN); // SceneObjectMapIntegerKey* leftEnabledMap = new SceneObjectMapIntegerKey("m_leftEnabled", // SceneObjectDataTypeEnum::SCENE_BOOLEAN); // SceneObjectMapIntegerKey* firstSurfaceEnabledMap = new SceneObjectMapIntegerKey("m_firstSurfaceEnabled", // SceneObjectDataTypeEnum::SCENE_BOOLEAN); // SceneObjectMapIntegerKey* secondSurfaceEnabledMap = new SceneObjectMapIntegerKey("m_secondSurfaceEnabled", // SceneObjectDataTypeEnum::SCENE_BOOLEAN); // for (int32_t iat = 0; iat < numActiveTabs; iat++) { // const int32_t tabIndex = tabIndices[iat]; // rightEnabledMap->addBoolean(tabIndex, // m_rightEnabled[tabIndex]); // leftEnabledMap->addBoolean(tabIndex, // m_leftEnabled[tabIndex]); // firstSurfaceEnabledMap->addBoolean(tabIndex, // m_firstSurfaceEnabled[tabIndex]); // secondSurfaceEnabledMap->addBoolean(tabIndex, // m_secondSurfaceEnabled[tabIndex]); // } // sceneClass->addChild(rightEnabledMap); // sceneClass->addChild(leftEnabledMap); // sceneClass->addChild(firstSurfaceEnabledMap); // sceneClass->addChild(secondSurfaceEnabledMap); } /** * 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 ModelSurfaceMontage::restoreModelSpecificInformationFromScene(const SceneAttributes* sceneAttributes, const SceneClass* sceneClass) { const int32_t montageVersion = sceneClass->getIntegerValue("montageVersion", 1); if (montageVersion >= 3) { /* * Restore Cerebellum */ const SceneObjectMapIntegerKey* cerebellumMap = sceneClass->getMapIntegerKey("m_cerebellarConfiguration"); if (cerebellumMap != NULL) { const std::map& structureMap = cerebellumMap->getMap(); for (std::map::const_iterator iter = structureMap.begin(); iter != structureMap.end(); iter++) { const int32_t key = iter->first; const SceneClass* cerebellarSceneClass = dynamic_cast(iter->second); CaretAssertArrayIndex(m_cerebellarConfiguration, BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS, key); m_cerebellarConfiguration[key]->restoreFromScene(sceneAttributes, cerebellarSceneClass); } } /* * Restore Cortext */ const SceneObjectMapIntegerKey* cerebralMap = sceneClass->getMapIntegerKey("m_cerebralConfiguration"); if (cerebellumMap != NULL) { const std::map& structureMap = cerebralMap->getMap(); for (std::map::const_iterator iter = structureMap.begin(); iter != structureMap.end(); iter++) { const int32_t key = iter->first; const SceneClass* cerebralSceneClass = dynamic_cast(iter->second); CaretAssertArrayIndex(m_cerebralConfiguration, BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS, key); m_cerebralConfiguration[key]->restoreFromScene(sceneAttributes, cerebralSceneClass); } } /* * Restore flat maps */ const SceneObjectMapIntegerKey* flatMap = sceneClass->getMapIntegerKey("m_flatMapsConfiguration"); if (cerebellumMap != NULL) { const std::map& structureMap = flatMap->getMap(); for (std::map::const_iterator iter = structureMap.begin(); iter != structureMap.end(); iter++) { const int32_t key = iter->first; const SceneClass* flatClass = dynamic_cast(iter->second); CaretAssertArrayIndex(m_flatMapsConfiguration, BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS, key); m_flatMapsConfiguration[key]->restoreFromScene(sceneAttributes, flatClass); } } sceneClass->getEnumerateTypeArrayForTabIndices("m_selectedConfigurationType", m_selectedConfigurationType); } else { restoreFromSceneVersionTwoAndEarlier(sceneAttributes, sceneClass, montageVersion); return; } } /** * Restore information from the ModelSurfaceMontage that existed prior * to the cerebellar, cerebral, and flat montage configurations. * * @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 information is restored. */ void ModelSurfaceMontage::restoreFromSceneVersionTwoAndEarlier(const SceneAttributes* sceneAttributes, const SceneClass* sceneClass, const int32_t montageVersion) { /* * Restore left surface */ const SceneObjectMapIntegerKey* leftSurfaceMap = sceneClass->getMapIntegerKey("m_leftSurfaceSelectionModel"); if (leftSurfaceMap != NULL) { const std::map& surfaceMap = leftSurfaceMap->getMap(); for (std::map::const_iterator iter = surfaceMap.begin(); iter != surfaceMap.end(); iter++) { const int32_t key = iter->first; const SceneClass* surfaceClass = dynamic_cast(iter->second); CaretAssertArrayIndex(m_cerebralConfiguration, BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS, key); SurfaceSelectionModel* ssm = m_cerebralConfiguration[key]->getLeftFirstSurfaceSelectionModel(); ssm->restoreFromScene(sceneAttributes, surfaceClass); } } /* * Restore left second surface */ const SceneObjectMapIntegerKey* leftSecondSurfaceMap = sceneClass->getMapIntegerKey("m_leftSecondSurfaceSelectionModel"); if (leftSecondSurfaceMap != NULL) { const std::map& surfaceMap = leftSecondSurfaceMap->getMap(); for (std::map::const_iterator iter = surfaceMap.begin(); iter != surfaceMap.end(); iter++) { const int32_t key = iter->first; const SceneClass* surfaceClass = dynamic_cast(iter->second); CaretAssertArrayIndex(m_cerebralConfiguration, BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS, key); SurfaceSelectionModel* ssm = m_cerebralConfiguration[key]->getLeftSecondSurfaceSelectionModel(); ssm->restoreFromScene(sceneAttributes, surfaceClass); } } /* * Restore right surface */ const SceneObjectMapIntegerKey* rightSurfaceMap = sceneClass->getMapIntegerKey("m_rightSurfaceSelectionModel"); if (rightSurfaceMap != NULL) { const std::map& surfaceMap = rightSurfaceMap->getMap(); for (std::map::const_iterator iter = surfaceMap.begin(); iter != surfaceMap.end(); iter++) { const int32_t key = iter->first; const SceneClass* surfaceClass = dynamic_cast(iter->second); CaretAssertArrayIndex(m_cerebralConfiguration, BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS, key); SurfaceSelectionModel* ssm = m_cerebralConfiguration[key]->getRightFirstSurfaceSelectionModel(); ssm->restoreFromScene(sceneAttributes, surfaceClass); } } /* * Restore right second surface */ const SceneObjectMapIntegerKey* rightSecondSurfaceMap = sceneClass->getMapIntegerKey("m_rightSecondSurfaceSelectionModel"); if (rightSecondSurfaceMap != NULL) { const std::map& surfaceMap = rightSecondSurfaceMap->getMap(); for (std::map::const_iterator iter = surfaceMap.begin(); iter != surfaceMap.end(); iter++) { const int32_t key = iter->first; const SceneClass* surfaceClass = dynamic_cast(iter->second); CaretAssertArrayIndex(m_cerebralConfiguration, BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS, key); SurfaceSelectionModel* ssm = m_cerebralConfiguration[key]->getRightSecondSurfaceSelectionModel(); ssm->restoreFromScene(sceneAttributes, surfaceClass); } } if (montageVersion <= 1) { /* * Version 1 had only dual option so enable items added in version 2 */ for (int32_t i = 0; i < BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS; i++) { CaretAssertArrayIndex(m_cerebralConfiguration, BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS, i); m_cerebralConfiguration[i]->setLeftEnabled(true); m_cerebralConfiguration[i]->setRightEnabled(true); m_cerebralConfiguration[i]->setFirstSurfaceEnabled(true); m_cerebralConfiguration[i]->setSecondSurfaceEnabled(true); } /* * Restore dual configuration as second surface */ const SceneObjectMapIntegerKey* dualConfigurationMap = sceneClass->getMapIntegerKey("m_dualConfigurationEnabled"); if (dualConfigurationMap != NULL) { const std::map& dataMap = dualConfigurationMap->getMap(); for (std::map::const_iterator iter = dataMap.begin(); iter != dataMap.end(); iter++) { const int32_t key = iter->first; const ScenePrimitive* primitive = dynamic_cast(iter->second); CaretAssertArrayIndex(m_cerebralConfiguration, BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS, key); m_cerebralConfiguration[key]->setSecondSurfaceEnabled(primitive->booleanValue()); } } } else { /* * Restore left enabled */ const SceneObjectMapIntegerKey* leftEnabledMap = sceneClass->getMapIntegerKey("m_leftEnabled"); if (leftEnabledMap != NULL) { const std::map& dataMap = leftEnabledMap->getMap(); for (std::map::const_iterator iter = dataMap.begin(); iter != dataMap.end(); iter++) { const int32_t key = iter->first; const ScenePrimitive* primitive = dynamic_cast(iter->second); CaretAssertArrayIndex(m_cerebralConfiguration, BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS, key); m_cerebralConfiguration[key]->setLeftEnabled(primitive->booleanValue()); } } /* * Restore right enabled */ const SceneObjectMapIntegerKey* rightEnabledMap = sceneClass->getMapIntegerKey("m_rightEnabled"); if (rightEnabledMap != NULL) { const std::map& dataMap = rightEnabledMap->getMap(); for (std::map::const_iterator iter = dataMap.begin(); iter != dataMap.end(); iter++) { const int32_t key = iter->first; const ScenePrimitive* primitive = dynamic_cast(iter->second); CaretAssertArrayIndex(m_cerebralConfiguration, BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS, key); m_cerebralConfiguration[key]->setRightEnabled(primitive->booleanValue()); } } /* * Restore first surface enabled */ const SceneObjectMapIntegerKey* firstSurfaceEnabledMap = sceneClass->getMapIntegerKey("m_firstSurfaceEnabled"); if (firstSurfaceEnabledMap != NULL) { const std::map& dataMap = firstSurfaceEnabledMap->getMap(); for (std::map::const_iterator iter = dataMap.begin(); iter != dataMap.end(); iter++) { const int32_t key = iter->first; const ScenePrimitive* primitive = dynamic_cast(iter->second); CaretAssertArrayIndex(m_cerebralConfiguration, BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS, key); m_cerebralConfiguration[key]->setFirstSurfaceEnabled(primitive->booleanValue()); } } /* * Restore second surface enabled */ const SceneObjectMapIntegerKey* secondSurfaceEnabledMap = sceneClass->getMapIntegerKey("m_secondSurfaceEnabled"); if (secondSurfaceEnabledMap != NULL) { const std::map& dataMap = secondSurfaceEnabledMap->getMap(); for (std::map::const_iterator iter = dataMap.begin(); iter != dataMap.end(); iter++) { const int32_t key = iter->first; const ScenePrimitive* primitive = dynamic_cast(iter->second); CaretAssertArrayIndex(m_cerebralConfiguration, BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS, key); m_cerebralConfiguration[key]->setSecondSurfaceEnabled(primitive->booleanValue()); } } } /* * Previous surface montage did not have lateral/medial selections */ for (int32_t i = 0; i < BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS; i++) { CaretAssertArrayIndex(m_cerebralConfiguration, BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS, i); m_cerebralConfiguration[i]->setLateralEnabled(true); m_cerebralConfiguration[i]->setMedialEnabled(true); } } /** * Get the number of rows and columns in the displayed surface montage. * * @param tabIndex * Index of tab. * @param numberOfRowsOut * Number of rows in the displayed montage. * @param numberOfColumnsOut * Number of columns in the displayed montage. */ void ModelSurfaceMontage::getSurfaceMontageNumberOfRowsAndColumns(const int32_t tabIndex, int32_t& numberOfRowsOut, int32_t& numberOfColumnsOut) const { numberOfRowsOut = 1; numberOfColumnsOut = 1; ModelSurfaceMontage* nonConstMSM = const_cast(this); std::vector surfaceMontageViewports; nonConstMSM->getSurfaceMontageViewportsForDrawing(tabIndex, surfaceMontageViewports); SurfaceMontageViewport::getNumberOfRowsAndColumns(surfaceMontageViewports, numberOfRowsOut, numberOfColumnsOut); } /** * Get the montage viewports for drawing by OpenGL. The montage viewports * will be updated prior to returning them. OpenGL will update * the viewing dimensions (x, y, width, height) in the returned montage * viewports. * * @param tabIndex * Tab for which viewports are requested. * @param surfaceMontageViewports * The montage viewports. */ void ModelSurfaceMontage::getSurfaceMontageViewportsForDrawing(const int32_t tabIndex, std::vector& surfaceMontageViewports) { SurfaceMontageConfigurationAbstract* config = getSelectedConfiguration(tabIndex); if (config != NULL) { config->getSurfaceMontageViewportsForDrawing(surfaceMontageViewports); } else { surfaceMontageViewports.clear(); } } /** * Get the montage viewports that will be used by the mouse transformations. * * @param tabIndex * Tab for which viewports are requested. * @param surfaceMontageViewports * The montage viewports. */ void ModelSurfaceMontage::getSurfaceMontageViewportsForTransformation(const int32_t tabIndex, std::vector& surfaceMontageViewports) const { const SurfaceMontageConfigurationAbstract* config = getSelectedConfiguration(tabIndex); if (config != NULL) { config->getSurfaceMontageViewportsForTransformation(surfaceMontageViewports); } else { surfaceMontageViewports.clear(); } } /** * @return A string describing the model. */ AString ModelSurfaceMontage::toString() const { AString msg; msg.appendWithNewLine("Surface Montage: "); return msg; } /** * Get a text description of the window's content. * * @param tabIndex * Index of the tab for content description. * @param descriptionOut * Description of the window's content. */ void ModelSurfaceMontage::getDescriptionOfContent(const int32_t tabIndex, PlainTextStringBuilder& descriptionOut) const { const SurfaceMontageConfigurationAbstract* config = getSelectedConfiguration(tabIndex); if (config != NULL) { config->getDescriptionOfContent(descriptionOut); } } /** * 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 ModelSurfaceMontage::copyTabContent(const int32_t sourceTabIndex, const int32_t destinationTabIndex) { Model::copyTabContent(sourceTabIndex, destinationTabIndex); CaretAssertArrayIndex(m_selectedConfigurationType, BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS, sourceTabIndex); CaretAssertArrayIndex(m_selectedConfigurationType, BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS, destinationTabIndex); m_cerebellarConfiguration[destinationTabIndex]->copyConfiguration(m_cerebellarConfiguration[sourceTabIndex]); m_cerebralConfiguration[destinationTabIndex]->copyConfiguration(m_cerebralConfiguration[sourceTabIndex]); m_flatMapsConfiguration[destinationTabIndex]->copyConfiguration(m_flatMapsConfiguration[sourceTabIndex]); m_selectedConfigurationType[destinationTabIndex] = m_selectedConfigurationType[sourceTabIndex]; } connectome-workbench-1.2.3+git41-gc4c6c90/src/Brain/ModelSurfaceMontage.h000066400000000000000000000132111300200146000256320ustar00rootroot00000000000000#ifndef __MODEL_SURFACE_MONTAGE_H__ #define __MODEL_SURFACE_MONTAGE_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 "EventListenerInterface.h" #include "Model.h" #include "StructureEnum.h" #include "SurfaceMontageViewport.h" #include "SurfaceMontageConfigurationTypeEnum.h" namespace caret { class SurfaceMontageConfigurationAbstract; class SurfaceMontageConfigurationCerebellar; class SurfaceMontageConfigurationCerebral; class SurfaceMontageConfigurationFlatMaps; /// Controls the display of a surface montage class ModelSurfaceMontage : public Model, public EventListenerInterface { public: ModelSurfaceMontage(Brain* brain); virtual ~ModelSurfaceMontage(); virtual void initializeSelectedSurfaces(); virtual void receiveEvent(Event* event); OverlaySet* getOverlaySet(const int tabIndex); const OverlaySet* getOverlaySet(const int tabIndex) const; void initializeOverlays(); Surface* getSelectedSurface(const StructureEnum::Enum structure, const int32_t windowTabNumber); AString getNameForGUI(const bool includeStructureFlag) const; virtual AString getNameForBrowserTab() const; void getSurfaceMontageNumberOfRowsAndColumns(const int32_t tabIndex, int32_t& numberOfRowsOut, int32_t& numberOfColumnsOut) const; void getSurfaceMontageViewportsForDrawing(const int32_t tabIndex, std::vector& surfaceMontageViewports); void getSurfaceMontageViewportsForTransformation(const int32_t tabIndex, std::vector& surfaceMontageViewports) const; SurfaceMontageConfigurationTypeEnum::Enum getSelectedConfigurationType(const int32_t tabIndex) const; void setSelectedConfigurationType(const int32_t tabIndex, const SurfaceMontageConfigurationTypeEnum::Enum configurationType); SurfaceMontageConfigurationAbstract* getSelectedConfiguration(const int32_t tabIndex); const SurfaceMontageConfigurationAbstract* getSelectedConfiguration(const int32_t tabIndex) const; SurfaceMontageConfigurationCerebellar * getCerebellarConfiguration(const int32_t tabIndex); const SurfaceMontageConfigurationCerebellar* getCerebellarConfiguration(const int32_t tabIndex) const; SurfaceMontageConfigurationCerebral * getCerebralConfiguration(const int32_t tabIndex); const SurfaceMontageConfigurationCerebral* getCerebralConfiguration(const int32_t tabIndex) const; SurfaceMontageConfigurationFlatMaps * getFlatMapsConfiguration(const int32_t tabIndex); const SurfaceMontageConfigurationFlatMaps* getFlatMapsConfiguration(const int32_t tabIndex) const; virtual AString toString() const; virtual void getDescriptionOfContent(const int32_t tabIndex, PlainTextStringBuilder& descriptionOut) const; virtual void copyTabContent(const int32_t sourceTabIndex, const int32_t destinationTabIndex); protected: virtual void saveModelSpecificInformationToScene(const SceneAttributes* sceneAttributes, SceneClass* sceneClass); virtual void restoreModelSpecificInformationFromScene(const SceneAttributes* sceneAttributes, const SceneClass* sceneClass); private: ModelSurfaceMontage(const ModelSurfaceMontage&); ModelSurfaceMontage& operator=(const ModelSurfaceMontage&); void restoreFromSceneVersionTwoAndEarlier(const SceneAttributes* sceneAttributes, const SceneClass* sceneClass, const int32_t montageVersion); SurfaceMontageConfigurationCerebellar* m_cerebellarConfiguration[BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS]; SurfaceMontageConfigurationCerebral* m_cerebralConfiguration[BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS]; SurfaceMontageConfigurationFlatMaps* m_flatMapsConfiguration[BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS]; mutable SurfaceMontageConfigurationTypeEnum::Enum m_selectedConfigurationType[BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS]; }; } // namespace #endif // __MODEL_SURFACE_MONTAGE_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Brain/ModelSurfaceSelector.cxx000066400000000000000000000321711300200146000264010ustar00rootroot00000000000000 /*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 __MODEL_SURFACE_SELECTOR_DECLARE__ #include "ModelSurfaceSelector.h" #undef __MODEL_SURFACE_SELECTOR_DECLARE__ #include "Brain.h" #include "BrainStructure.h" #include "EventManager.h" #include "EventModelGetAll.h" #include "ModelSurface.h" #include "SceneAttributes.h" #include "SceneClass.h" #include "SceneClassAssistant.h" #include "Surface.h" #include using namespace caret; /** * \class ModelSurfaceSelector * \brief Maintains selection of surface model * * Maintains selection of a surface model with the ability to limit the * surface models to those from a specific brain structure. */ /** * Constructor. */ ModelSurfaceSelector::ModelSurfaceSelector() : CaretObject() { m_defaultStructure = StructureEnum::ALL; m_selectedStructure = StructureEnum::ALL; m_selectedSurfaceModel = NULL; } /** * Destructor. */ ModelSurfaceSelector::~ModelSurfaceSelector() { } /** * @return The selected surface model. */ ModelSurface* ModelSurfaceSelector::getSelectedSurfaceModel() { this->updateSelector(); return m_selectedSurfaceModel; } /** * @return The selected structure. */ StructureEnum::Enum ModelSurfaceSelector::getSelectedStructure() { this->updateSelector(); return m_selectedStructure; } /** * Set the selected surface model. * * @param surfaceModel * Model that is selected. */ void ModelSurfaceSelector::setSelectedSurfaceModel(ModelSurface* surfaceModel) { m_selectedSurfaceModel = surfaceModel; if (m_selectedStructure != StructureEnum::ALL) { m_selectedStructure = surfaceModel->getSurface()->getStructure(); } this->updateSelector(); } /** * Set the selected structure. * * @param selectedStructure * Structure that is selected. */ void ModelSurfaceSelector::setSelectedStructure(const StructureEnum::Enum selectedStructure) { m_selectedStructure = selectedStructure; this->updateSelector(); } /** * Get the structures available for selection. * * @param selectableStructuresOut * Output containing structures that can be selected. */ void ModelSurfaceSelector::getSelectableStructures( std::vector& selectableStructuresOut) const { selectableStructuresOut.clear(); selectableStructuresOut.insert(selectableStructuresOut.end(), m_availableStructures.begin(), m_availableStructures.end()); } /** * Get the surface models available for selection. * * @param selectableSurfaceModelsOut * Output containing surface models that can be selected. */ void ModelSurfaceSelector::getSelectableSurfaceModels( std::vector& selectableSurfaceModelsOut) const { selectableSurfaceModelsOut.clear(); selectableSurfaceModelsOut.insert(selectableSurfaceModelsOut.end(), m_availableSurfaceModels.begin(), m_availableSurfaceModels.end()); } /** * Update the selector with the available surface models. */ void ModelSurfaceSelector::updateSelector(const std::vector modelDisplayModels) { m_allSurfaceModels.clear(); for (std::vector::const_iterator iter = modelDisplayModels.begin(); iter != modelDisplayModels.end(); iter++) { ModelSurface* surfaceModel = dynamic_cast(*iter); if (surfaceModel != NULL) { m_allSurfaceModels.push_back(surfaceModel); } } this->updateSelector(); } /** * Update the selector with the available surface models. */ void ModelSurfaceSelector::updateSelector() { bool haveCortexLeft = false; bool haveCortexRight = false; bool haveCerebellum = false; /* * Find the ALL surface models and structures */ for (std::vector::const_iterator iter = m_allSurfaceModels.begin(); iter != m_allSurfaceModels.end(); iter++) { ModelSurface* surfaceModel = *iter; const Surface* surface = surfaceModel->getSurface(); const StructureEnum::Enum structure = surface->getStructure(); switch (structure) { case StructureEnum::CEREBELLUM: haveCerebellum = true; break; case StructureEnum::CORTEX_LEFT: haveCortexLeft = true; break; case StructureEnum::CORTEX_RIGHT: haveCortexRight = true; break; default: break; } } /* * Determine which structures are available. */ m_availableStructures.clear(); m_availableStructures.push_back(StructureEnum::ALL); if (haveCerebellum) { m_availableStructures.push_back(StructureEnum::CEREBELLUM); } if (haveCortexLeft) { m_availableStructures.push_back(StructureEnum::CORTEX_LEFT); } if (haveCortexRight) { m_availableStructures.push_back(StructureEnum::CORTEX_RIGHT); } /* * Update the structure selection. */ if (std::find(m_availableStructures.begin(), m_availableStructures.end(), m_selectedStructure) == m_availableStructures.end()) { if (m_availableStructures.empty() == false) { m_selectedStructure = m_availableStructures[0]; } else { m_selectedStructure = m_defaultStructure; } } /* * Update the available surface models. */ m_availableSurfaceModels.clear(); for (std::vector::iterator iter = m_allSurfaceModels.begin(); iter != m_allSurfaceModels.end(); iter++) { ModelSurface* surfaceModel = *iter; const Surface* surface = surfaceModel->getSurface(); const StructureEnum::Enum structure = surface->getStructure(); bool useIt = false; if (m_selectedStructure == StructureEnum::ALL) { useIt = true; } else if (m_selectedStructure == structure) { useIt = true; } if (useIt) { m_availableSurfaceModels.push_back(surfaceModel); } } /* * Update the surface selection. */ if (std::find(m_availableSurfaceModels.begin(), m_availableSurfaceModels.end(), m_selectedSurfaceModel) == m_availableSurfaceModels.end()) { /* * Selected model is not found. */ m_selectedSurfaceModel = NULL; /* * First, see if a previous model for structure still exists, if so, use it. */ std::map::iterator iter = m_previousSelectedSurfaceModel.find(m_selectedStructure); if (iter != m_previousSelectedSurfaceModel.end()) { ModelSurface* previousModel = iter->second; if (std::find(m_availableSurfaceModels.begin(), m_availableSurfaceModels.end(), previousModel) != m_availableSurfaceModels.end()) { m_selectedSurfaceModel = previousModel; } } /* * Still not found? */ if (m_selectedSurfaceModel == NULL) { /* * Default to first */ if (m_availableSurfaceModels.empty() == false) { m_selectedSurfaceModel = m_availableSurfaceModels[0]; /* * Try to find and used the primary anatomical surface. */ Brain* brain = m_selectedSurfaceModel->getBrain(); if (brain != NULL) { BrainStructure* brainStructure = brain->getBrainStructure(m_selectedStructure, false); if (brainStructure != NULL) { const Surface* volInteractSurface = brainStructure->getPrimaryAnatomicalSurface(); if (volInteractSurface != NULL) { const int numSurfaceModels = static_cast(m_availableSurfaceModels.size()); for (int32_t i = 0; i < numSurfaceModels; i++) { if (m_availableSurfaceModels[i]->getSurface() == volInteractSurface) { m_selectedSurfaceModel = m_availableSurfaceModels[i]; break; } } } } } } } } /* * Save model for retrieval later. */ if (m_selectedSurfaceModel != NULL) { m_previousSelectedSurfaceModel[m_selectedStructure] = m_selectedSurfaceModel; } } /** * Get a description of this object's content. * @return String describing this object's content. */ AString ModelSurfaceSelector::toString() const { AString msg = "selectedStructure=" + StructureEnum::toName(m_selectedStructure) + "selectedSurface="; if (m_selectedSurfaceModel != NULL) { msg += m_selectedSurfaceModel->getNameForGUI(false); } return msg; } /** * Create a scene for an instance of a class. * * @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 the class' instance. * * @return Pointer to SceneClass object representing the state of * this object. Under some circumstances a NULL pointer may be * returned. Caller will take ownership of returned object. */ SceneClass* ModelSurfaceSelector::saveToScene(const SceneAttributes* /*sceneAttributes*/, const AString& instanceName) { SceneClass* sceneClass = new SceneClass(instanceName, "ModelSurfaceSelector", 1); sceneClass->addEnumeratedType("m_selectedStructure", m_selectedStructure); if (m_selectedSurfaceModel != NULL) { const Surface* surface = m_selectedSurfaceModel->getSurface(); if (surface != NULL) { const AString filename = surface->getFileNameNoPath(); sceneClass->addString("surfaceFileName", filename); } } return sceneClass; } /** * Restore the state of an instance of a class. * * @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. May be NULL for some types of scenes. */ void ModelSurfaceSelector::restoreFromScene(const SceneAttributes* /*sceneAttributes*/, const SceneClass* sceneClass) { if (sceneClass == NULL) { return; } m_selectedStructure = sceneClass->getEnumeratedTypeValue("m_selectedStructure", m_selectedStructure); setSelectedStructure(m_selectedStructure); const AString surfaceFileName = sceneClass->getStringValue("surfaceFileName", ""); ModelSurface* matchedSurfaceModel = NULL; if (surfaceFileName.isEmpty() == false) { for (std::vector::iterator iter = m_availableSurfaceModels.begin(); iter != m_availableSurfaceModels.end(); iter++) { const Surface* surface = (*iter)->getSurface(); if (surfaceFileName == surface->getFileNameNoPath()) { matchedSurfaceModel = *iter; break; } } } /* * Note: setSelectedSurfaceModel() will update the content of * m_availableSurfaceModels and the above iterators will become * invalid. So setSelectedSurfaceModel() must be called outside * of the loop. * Bug found by JS. */ if (matchedSurfaceModel != NULL) { setSelectedSurfaceModel(matchedSurfaceModel); } } connectome-workbench-1.2.3+git41-gc4c6c90/src/Brain/ModelSurfaceSelector.h000066400000000000000000000061621300200146000260270ustar00rootroot00000000000000#ifndef __MODEL_SURFACE_SELECTOR__H_ #define __MODEL_SURFACE_SELECTOR__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 "CaretObject.h" #include "SceneableInterface.h" #include "StructureEnum.h" namespace caret { class Model; class ModelSurface; class ModelSurfaceSelector : public CaretObject, public SceneableInterface { public: ModelSurfaceSelector(); virtual ~ModelSurfaceSelector(); ModelSurface* getSelectedSurfaceModel(); StructureEnum::Enum getSelectedStructure(); void setSelectedSurfaceModel(ModelSurface* surfaceModel); void setSelectedStructure(const StructureEnum::Enum selectedStructure); void getSelectableStructures( std::vector& selectableSructuresOut) const; void getSelectableSurfaceModels( std::vector& selectableSurfaceModelsOut) const; void updateSelector(const std::vector modelDisplayModels); AString toString() const; virtual SceneClass* saveToScene(const SceneAttributes* sceneAttributes, const AString& instanceName); virtual void restoreFromScene(const SceneAttributes* sceneAttributes, const SceneClass* sceneClass); private: ModelSurfaceSelector(const ModelSurfaceSelector&); ModelSurfaceSelector& operator=(const ModelSurfaceSelector&); void updateSelector(); private: std::vector m_availableStructures; StructureEnum::Enum m_selectedStructure; StructureEnum::Enum m_defaultStructure; std::vector m_allSurfaceModels; std::vector m_availableSurfaceModels; ModelSurface* m_selectedSurfaceModel; std::map m_previousSelectedSurfaceModel; }; #ifdef __MODEL_SURFACE_SELECTOR_DECLARE__ // #endif // __MODEL_SURFACE_SELECTOR_DECLARE__ } // namespace #endif //__MODEL_SURFACE_SELECTOR__H_ connectome-workbench-1.2.3+git41-gc4c6c90/src/Brain/ModelTypeEnum.cxx000066400000000000000000000206071300200146000250570ustar00rootroot00000000000000 /*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 __MODEL_DISPLAY_CONTROLLER_TYPE_ENUM_DECLARE__ #include "ModelTypeEnum.h" #undef __MODEL_DISPLAY_CONTROLLER_TYPE_ENUM_DECLARE__ #include "CaretAssert.h" using namespace caret; /** * Constructor. * * @param enumValue * An enumerated value. * @param integerCode * Integer code for this enumerated value. * * @param name * Name of enumerated value. * * @param guiName * User-friendly name for use in user-interface. */ ModelTypeEnum::ModelTypeEnum(const Enum enumValue, const int32_t integerCode, const AString& name, const AString& guiName) { this->enumValue = enumValue; this->integerCode = integerCode; this->name = name; this->guiName = guiName; } /** * Destructor. */ ModelTypeEnum::~ModelTypeEnum() { } /** * Initialize the enumerated metadata. */ void ModelTypeEnum::initialize() { if (initializedFlag) { return; } initializedFlag = true; 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_SURFACE, 2, "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")); enumData.push_back(ModelTypeEnum(MODEL_TYPE_WHOLE_BRAIN, 5, "MODEL_TYPE_WHOLE_BRAIN", "Whole Brain")); } /** * 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 ModelTypeEnum* ModelTypeEnum::findData(const Enum enumValue) { if (initializedFlag == false) initialize(); size_t num = enumData.size(); for (size_t i = 0; i < num; i++) { const ModelTypeEnum* 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 ModelTypeEnum::toName(Enum enumValue) { if (initializedFlag == false) initialize(); const ModelTypeEnum* 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. */ ModelTypeEnum::Enum ModelTypeEnum::fromName(const AString& name, bool* isValidOut) { if (initializedFlag == false) initialize(); bool validFlag = false; Enum enumValue = MODEL_TYPE_INVALID; for (std::vector::iterator iter = enumData.begin(); iter != enumData.end(); iter++) { const ModelTypeEnum& 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 ModelTypeEnum")); } return enumValue; } /** * Get a GUI string representation of the enumerated type. * @param enumValue * Enumerated value. * @return * String representing enumerated value. */ AString ModelTypeEnum::toGuiName(Enum enumValue) { if (initializedFlag == false) initialize(); const ModelTypeEnum* enumInstance = findData(enumValue); return enumInstance->guiName; } /** * Get an enumerated value corresponding to its GUI name. * @param guiName * 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. */ ModelTypeEnum::Enum ModelTypeEnum::fromGuiName(const AString& guiName, bool* isValidOut) { if (initializedFlag == false) initialize(); bool validFlag = false; Enum enumValue = MODEL_TYPE_INVALID; for (std::vector::iterator iter = enumData.begin(); iter != enumData.end(); iter++) { const ModelTypeEnum& 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 ModelTypeEnum")); } return enumValue; } /** * Get the integer code for a data type. * @param enumValue * Enumerated value. * @return * Integer code for data type. */ int32_t ModelTypeEnum::toIntegerCode(Enum enumValue) { if (initializedFlag == false) initialize(); const ModelTypeEnum* 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. */ ModelTypeEnum::Enum ModelTypeEnum::fromIntegerCode(const int32_t integerCode, bool* isValidOut) { if (initializedFlag == false) initialize(); bool validFlag = false; Enum enumValue = MODEL_TYPE_INVALID; for (std::vector::iterator iter = enumData.begin(); iter != enumData.end(); iter++) { const ModelTypeEnum& 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 ModelTypeEnum")); } 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 ModelTypeEnum::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); } } connectome-workbench-1.2.3+git41-gc4c6c90/src/Brain/ModelTypeEnum.h000066400000000000000000000061111300200146000244760ustar00rootroot00000000000000#ifndef __MODEL_DISPLAY_CONTROLLER_TYPE_ENUM__H_ #define __MODEL_DISPLAY_CONTROLLER_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 { /// Enumerated type for class ModelTypeEnum { public: /** * Enumerated values. */ enum Enum { /** Invalid */ MODEL_TYPE_INVALID, /** Chart */ MODEL_TYPE_CHART, /** Surface */ MODEL_TYPE_SURFACE, /** Surface Montage */ MODEL_TYPE_SURFACE_MONTAGE, /** Volume Slices */ MODEL_TYPE_VOLUME_SLICES, /** Whole Brain */ MODEL_TYPE_WHOLE_BRAIN }; ~ModelTypeEnum(); 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); private: ModelTypeEnum(const Enum enumValue, const int32_t integerCode, const AString& name, const AString& guiName); static const ModelTypeEnum* 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; /** 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 __MODEL_DISPLAY_CONTROLLER_TYPE_ENUM_DECLARE__ std::vector ModelTypeEnum::enumData; bool ModelTypeEnum::initializedFlag = false; #endif // __MODEL_DISPLAY_CONTROLLER_TYPE_ENUM_DECLARE__ } // namespace #endif //__MODEL_DISPLAY_CONTROLLER_TYPE_ENUM__H_ connectome-workbench-1.2.3+git41-gc4c6c90/src/Brain/ModelVolume.cxx000066400000000000000000000145141300200146000245600ustar00rootroot00000000000000/*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 "Brain.h" #include "BrowserTabContent.h" #include "EventBrowserTabGet.h" #include "EventManager.h" #include "Overlay.h" #include "OverlaySet.h" #include "OverlaySetArray.h" #include "ModelVolume.h" #include "SceneAttributes.h" #include "SceneClass.h" #include "SceneClassAssistant.h" #include "SceneEnumeratedType.h" #include "VolumeFile.h" using namespace caret; /** * Constructor. * @param brain - brain to which this volume model belongs. * */ ModelVolume::ModelVolume(Brain* brain) : Model(ModelTypeEnum::MODEL_TYPE_VOLUME_SLICES, brain) { std::vector overlaySurfaceStructures; m_overlaySetArray = new OverlaySetArray(overlaySurfaceStructures, Overlay::INCLUDE_VOLUME_FILES_YES, "Volume View"); m_lastVolumeFile = NULL; /* * Scene note: overlaySet is restored by parent class */ m_sceneAssistant = new SceneClassAssistant(); } /** * Destructor */ ModelVolume::~ModelVolume() { EventManager::get()->removeAllEventsFromListener(this); delete m_overlaySetArray; delete m_sceneAssistant; } /** * 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 ModelVolume::getNameForGUI(const bool /*includeStructureFlag*/) const { return "Volume"; } /** * @return The name that should be displayed in the tab * displaying this model. */ AString ModelVolume::getNameForBrowserTab() const { return "Volume"; } /** * Get the bottom-most active volume in the given window tab. * If no overlay is set to volume data, one will be set to a * volume if there is a volume loaded. * @param windowTabNumber * Tab number for content. * @return * Bottom-most volume or NULL if no volumes available. */ VolumeMappableInterface* ModelVolume::getUnderlayVolumeFile(const int32_t windowTabNumber) const { OverlaySet* overlaySet = m_overlaySetArray->getOverlaySet(windowTabNumber); VolumeMappableInterface* vf = overlaySet->getUnderlayVolume(); if (vf == NULL) { vf = overlaySet->setUnderlayToVolume(); } // EventBrowserTabGet getBrowserTabEvent(windowTabNumber); // EventManager::get()->sendEvent(getBrowserTabEvent.getPointer()); // BrowserTabContent* btc = getBrowserTabEvent.getBrowserTab(); // if (btc != NULL) { // OverlaySet* overlaySet = btc->getOverlaySet(); // vf = overlaySet->getUnderlayVolume(); // if (vf == NULL) { // vf = overlaySet->setUnderlayToVolume(); // } // } return vf; } /** * Update the model. * @param windowTabNumber * Tab number of window. */ void ModelVolume::updateModel(const int32_t /*windowTabNumber*/) { } /** * Receive events from the event manager. * * @param event * The event. */ void ModelVolume::receiveEvent(Event* /*event*/) { } /** * Get the overlay set for the given tab. * @param tabIndex * Index of tab. * @return * Overlay set at the given tab index. */ OverlaySet* ModelVolume::getOverlaySet(const int 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* ModelVolume::getOverlaySet(const int tabIndex) const { return m_overlaySetArray->getOverlaySet(tabIndex); } /** * Initilize the overlays for this model. */ void ModelVolume::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 ModelVolume::saveModelSpecificInformationToScene(const SceneAttributes* sceneAttributes, SceneClass* sceneClass) { 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 ModelVolume::restoreModelSpecificInformationFromScene(const SceneAttributes* sceneAttributes, const SceneClass* sceneClass) { if (sceneClass == NULL) { return; } m_sceneAssistant->restoreMembers(sceneAttributes, sceneClass); } /** * 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 ModelVolume::copyTabContent(const int32_t sourceTabIndex, const int32_t destinationTabIndex) { Model::copyTabContent(sourceTabIndex, destinationTabIndex); m_overlaySetArray->copyOverlaySet(sourceTabIndex, destinationTabIndex); } connectome-workbench-1.2.3+git41-gc4c6c90/src/Brain/ModelVolume.h000066400000000000000000000055161300200146000242070ustar00rootroot00000000000000#ifndef __MODEL_DISPLAY_CONTROLLER_VOLUME_H__ #define __MODEL_DISPLAY_CONTROLLER_VOLUME_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 "EventListenerInterface.h" #include "Model.h" namespace caret { class Brain; class SceneClassAssistant; class OverlaySetArray; class VolumeMappableInterface; /// Controls the display of a volumes. class ModelVolume : public Model, public EventListenerInterface { public: ModelVolume(Brain* brain); virtual ~ModelVolume(); VolumeMappableInterface* getUnderlayVolumeFile(const int32_t windowTabNumber) const; void updateModel(const int32_t windowTabNumber); void receiveEvent(Event* event); OverlaySet* getOverlaySet(const int tabIndex); const OverlaySet* getOverlaySet(const int tabIndex) const; void initializeOverlays(); virtual void copyTabContent(const int32_t sourceTabIndex, const int32_t destinationTabIndex); protected: virtual void saveModelSpecificInformationToScene(const SceneAttributes* sceneAttributes, SceneClass* sceneClass); virtual void restoreModelSpecificInformationFromScene(const SceneAttributes* sceneAttributes, const SceneClass* sceneClass); private: ModelVolume(const ModelVolume&); ModelVolume& operator=(const ModelVolume&); private: public: AString getNameForGUI(const bool includeStructureFlag) const; virtual AString getNameForBrowserTab() const; private: VolumeMappableInterface* m_lastVolumeFile; /** Overlays sets for this model and for each tab */ OverlaySetArray* m_overlaySetArray; SceneClassAssistant* m_sceneAssistant; }; } // namespace #endif // __MODEL_DISPLAY_CONTROLLER_VOLUME_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Brain/ModelWholeBrain.cxx000066400000000000000000000566541300200146000253560ustar00rootroot00000000000000/*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 __MODEL_WHOLE_BRAIN_DEFINE__ #include "ModelWholeBrain.h" #undef __MODEL_WHOLE_BRAIN_DEFINE__ #include "Brain.h" #include "BrainStructure.h" #include "BrowserTabContent.h" #include "EventBrowserTabGet.h" #include "EventManager.h" #include "EventModelGetAll.h" #include "EventSurfacesGet.h" #include "IdentificationManager.h" #include "ModelSurface.h" #include "OverlaySet.h" #include "OverlaySetArray.h" #include "SceneClass.h" #include "SceneClassArray.h" #include "SceneClassAssistant.h" #include "Surface.h" using namespace caret; /** * Constructor. * @param brain - brain to which this whole brain model belongs. * */ ModelWholeBrain::ModelWholeBrain(Brain* brain) : Model(ModelTypeEnum::MODEL_TYPE_WHOLE_BRAIN, brain) { if (s_anatomicalSurfaceTypes.empty()) { SurfaceTypeEnum::getAllAnatomicallyShapedEnums(s_anatomicalSurfaceTypes); } for (int32_t i = 0; i < BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS; i++) { m_selectedSurfaceType[i] = SurfaceTypeEnum::ANATOMICAL; m_cerebellumEnabled[i] = true; m_leftEnabled[i] = true; m_rightEnabled[i] = true; m_leftRightSeparation[i] = 0.0; m_cerebellumSeparation[i] = 0.0; } std::vector overlaySurfaceStructures; overlaySurfaceStructures.push_back(StructureEnum::CORTEX_LEFT); overlaySurfaceStructures.push_back(StructureEnum::CORTEX_RIGHT); overlaySurfaceStructures.push_back(StructureEnum::CEREBELLUM); m_overlaySetArray = new OverlaySetArray(overlaySurfaceStructures, Overlay::INCLUDE_VOLUME_FILES_YES, "All View"); EventManager::get()->addEventListener(this, EventTypeEnum::EVENT_IDENTIFICATION_HIGHLIGHT_LOCATION); m_sceneAssistant = new SceneClassAssistant(); m_sceneAssistant->addTabIndexedEnumeratedTypeArray("m_selectedSurfaceType", m_selectedSurfaceType); m_sceneAssistant->addTabIndexedBooleanArray("m_leftEnabled", m_leftEnabled); m_sceneAssistant->addTabIndexedBooleanArray("m_rightEnabled", m_rightEnabled); m_sceneAssistant->addTabIndexedBooleanArray("m_cerebellumEnabled", m_cerebellumEnabled); m_sceneAssistant->addTabIndexedFloatArray("m_leftRightSeparation", m_leftRightSeparation); m_sceneAssistant->addTabIndexedFloatArray("m_cerebellumSeparation", m_cerebellumSeparation); } /** * Destructor */ ModelWholeBrain::~ModelWholeBrain() { EventManager::get()->removeAllEventsFromListener(this); delete m_overlaySetArray; delete m_sceneAssistant; } /** * Get the available surface types. * @param surfaceTypesOut * Output loaded with the available surface types. */ void ModelWholeBrain::getAvailableSurfaceTypes(std::vector& surfaceTypesOut) { updateModel(); surfaceTypesOut.clear(); surfaceTypesOut.insert(surfaceTypesOut.end(), m_availableSurfaceTypes.begin(), m_availableSurfaceTypes.end()); } /** * Get the selected surface type for the given tab. * * @param windowTabNumber * The tab. * @return * Surface type for the tab. */ SurfaceTypeEnum::Enum ModelWholeBrain::getSelectedSurfaceType(const int32_t windowTabNumber) { updateModel(); return m_selectedSurfaceType[windowTabNumber]; } /** * Update this model. */ void ModelWholeBrain::updateModel() { /* * Get all of the surfaces */ EventSurfacesGet surfaceEvent; EventManager::get()->sendEvent(surfaceEvent.getPointer()); std::vector allSurfaces = surfaceEvent.getSurfaces(); /* * Update the available surface types */ m_availableSurfaceTypes.clear(); for (std::vector::iterator surfIter = allSurfaces.begin(); surfIter != allSurfaces.end(); surfIter++) { m_availableSurfaceTypes.insert((*surfIter)->getSurfaceType()); } /* * Set the default surface type from the available anatomical types */ std::vector::iterator defaultSurfaceTypeIter = std::find_first_of(s_anatomicalSurfaceTypes.begin(), s_anatomicalSurfaceTypes.end(), m_availableSurfaceTypes.begin(), m_availableSurfaceTypes.end()); const SurfaceTypeEnum::Enum defaultSurfaceType = ((defaultSurfaceTypeIter != s_anatomicalSurfaceTypes.end()) ? *defaultSurfaceTypeIter : SurfaceTypeEnum::ANATOMICAL); /* * If the selected surface type in a tab is no longer valid, update * selected surface type. */ for (int32_t iTab = 0; iTab < BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS; iTab++) { if (std::find(m_availableSurfaceTypes.begin(), m_availableSurfaceTypes.end(), m_selectedSurfaceType[iTab]) == m_availableSurfaceTypes.end()) { m_selectedSurfaceType[iTab] = defaultSurfaceType; } } /* * Deselect any surfaces that are no longer valid. They will get updated * the next time getSelected */ for (int32_t tabIndex = 0; tabIndex < BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS; tabIndex++) { for (std::map, Surface*>::iterator mapIter = m_selectedSurface[tabIndex].begin(); mapIter != m_selectedSurface[tabIndex].end(); mapIter++) { if (std::find(allSurfaces.begin(), allSurfaces.end(), mapIter->second) == allSurfaces.end()) { mapIter->second = NULL; } } } } ///** // * Update this model. // */ //void //ModelWholeBrain::updateModel() //{ // /* // * Get all model to find loaded surface types. // */ // EventModelGetAll eventGetModels; // EventManager::get()->sendEvent(eventGetModels.getPointer()); // const std::vector allModels = // eventGetModels.getModels(); // // /* // * Get ALL possible surface types. // */ // std::vector allSurfaceTypes; // SurfaceTypeEnum::getAllEnums(allSurfaceTypes); // const int32_t numEnumTypes = static_cast(allSurfaceTypes.size()); // std::vector surfaceTypeValid(numEnumTypes, false); // // /* // * Find surface types that are actually used. // */ // for (std::vector::const_iterator iter = allModels.begin(); // iter != allModels.end(); // iter++) { // ModelSurface* surfaceModel = // dynamic_cast(*iter); // if (surfaceModel != NULL) { // SurfaceTypeEnum::Enum surfaceType = surfaceModel->getSurface()->getSurfaceType(); // // for (int i = 0; i < numEnumTypes; i++) { // if (allSurfaceTypes[i] == surfaceType) { // surfaceTypeValid[i] = true; // break; // } // } // } // } // // /* // * Set the available surface types. // */ // m_availableSurfaceTypes.clear(); // for (int i = 0; i < numEnumTypes; i++) { // if (surfaceTypeValid[i]) { // m_availableSurfaceTypes.push_back(allSurfaceTypes[i]); // } // } // // // // /* // * Update the selected surface and volume types. // */ // for (int32_t i = 0; i < BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS; i++) { // if (std::find(m_availableSurfaceTypes.begin(), // m_availableSurfaceTypes.end(), // m_selectedSurfaceType[i]) == m_availableSurfaceTypes.end()) { // if (m_availableSurfaceTypes.empty() == false) { // m_selectedSurfaceType[i] = m_availableSurfaceTypes[0]; // } // else { // m_selectedSurfaceType[i] = SurfaceTypeEnum::ANATOMICAL; // } // } // // VolumeMappableInterface* vf = getUnderlayVolumeFile(i); // if (vf != NULL) { //// m_volumeSlicesSelected[i].updateForVolumeFile(vf); // } // } //} /** * Set the selected surface type. * @param windowTabNumber * Index of window tab. * @param surfaceType * New surface type. */ void ModelWholeBrain::setSelectedSurfaceType(const int32_t windowTabNumber, const SurfaceTypeEnum::Enum surfaceType) { m_selectedSurfaceType[windowTabNumber] = surfaceType; updateModel(); } /** * 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 ModelWholeBrain::getNameForGUI(const bool /*includeStructureFlag*/) const { return "Whole Brain"; } /** * @return The name that should be displayed in the tab * displaying this model. */ AString ModelWholeBrain::getNameForBrowserTab() const { return "All "; } /** * Get the bottom-most active volume in the given window tab. * @param windowTabNumber * Tab number for content. * @return * Bottom-most volume or NULL if not available (such as * when all overlay are not volumes or they are disabled). */ VolumeMappableInterface* ModelWholeBrain::getUnderlayVolumeFile(const int32_t windowTabNumber) const { VolumeMappableInterface* vf = NULL; EventBrowserTabGet getBrowserTabEvent(windowTabNumber); EventManager::get()->sendEvent(getBrowserTabEvent.getPointer()); BrowserTabContent* btc = getBrowserTabEvent.getBrowserTab(); if (btc != NULL) { OverlaySet* overlaySet = btc->getOverlaySet(); if (overlaySet != NULL) { vf = overlaySet->getUnderlayVolume(); } } return vf; } /** * @return Return the surfaces displayed in the given tab. * @param windowTabIndex * THe tab. */ std::vector ModelWholeBrain::getSelectedSurfaces(const int32_t windowTabIndex) { std::vector surfaces; /* * Get the surfaces. */ Brain* brain = getBrain(); 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 = getSelectedSurface(structure, windowTabIndex); surfaces.push_back(surface); } return surfaces; } /** * Get the surface for the given structure in the given tab that is for * the currently selected surface type. * * @param structure * Structure for the surface * @param windowTabNumber * Tab number of window. * @param Pointer to selected surface for given structure or NULL if not available. */ Surface* ModelWholeBrain::getSelectedSurface(const StructureEnum::Enum structure, const int32_t windowTabNumber) { const SurfaceTypeEnum::Enum surfaceType = getSelectedSurfaceType(windowTabNumber); std::pair key = std::make_pair(structure, surfaceType); /* * Get the currently selected surface. */ Surface* s = m_selectedSurface[windowTabNumber][key]; /* * See if currently selected surface is valid. */ BrainStructure* brainStructure = m_brain->getBrainStructure(structure, false); if (brainStructure == NULL) { return NULL; } std::vector surfaces; brainStructure->getSurfacesOfType(surfaceType, surfaces); if (std::find(surfaces.begin(), surfaces.end(), s) == surfaces.end()) { s = NULL; } /* * If no selected surface, use first surface. */ if (s == NULL) { if (surfaces.empty() == false) { s = surfaces[0]; } } m_selectedSurface[windowTabNumber][key] = s; return s; } /** * Set the selected surface for the given structure in the given window tab * that is for the currently selected surface type. * surface type. * * @param structure * Structure for the surface * @param windowTabNumber * Tab number of window. * @param surface * Newly selected surface. */ void ModelWholeBrain::setSelectedSurface(const StructureEnum::Enum structure, const int32_t windowTabNumber, Surface* surface) { const SurfaceTypeEnum::Enum surfaceType = getSelectedSurfaceType(windowTabNumber); std::pair key = std::make_pair(structure, surfaceType); m_selectedSurface[windowTabNumber][key] = surface; } /** * Receive events from the event manager. * * @param event * The event. */ void ModelWholeBrain::receiveEvent(Event* /*event*/) { } /** * Get the overlay set for the given tab. * @param tabIndex * Index of tab. * @return * Overlay set at the given tab index. */ OverlaySet* ModelWholeBrain::getOverlaySet(const int 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* ModelWholeBrain::getOverlaySet(const int tabIndex) const { return m_overlaySetArray->getOverlaySet(tabIndex); } /** * Initilize the overlays for this model. */ void ModelWholeBrain::initializeOverlays() { m_overlaySetArray->initializeOverlaySelections(); } /** * Initialize the selected surfaces. */ void ModelWholeBrain::initializeSelectedSurfaces() { std::vector surfaceTypes; getAvailableSurfaceTypes(surfaceTypes); if (std::find(surfaceTypes.begin(), surfaceTypes.end(), SurfaceTypeEnum::ANATOMICAL) != surfaceTypes.end()) { for (int32_t iTab = 0; iTab < BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS; iTab++) { setSelectedSurfaceType(iTab, SurfaceTypeEnum::ANATOMICAL); } } const int32_t numberOfStructures = m_brain->getNumberOfBrainStructures(); for (int32_t i = 0; i < numberOfStructures; i++) { const BrainStructure* brainStructure = m_brain->getBrainStructure(i); Surface* surface = const_cast(brainStructure->getPrimaryAnatomicalSurface()); if (surface != NULL) { const StructureEnum::Enum structure = brainStructure->getStructure(); for (int32_t iTab = 0; iTab < BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS; iTab++) { setSelectedSurface(structure, iTab, surface); } } } } /** * 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 ModelWholeBrain::saveModelSpecificInformationToScene(const SceneAttributes* sceneAttributes, SceneClass* sceneClass) { m_sceneAssistant->saveMembers(sceneAttributes, sceneClass); const std::vector tabIndices = sceneAttributes->getIndicesOfTabsForSavingToScene(); const int32_t numTabs = static_cast(tabIndices.size()); std::vector classesForSelectedSurfaceArray; for (int32_t i = 0; i < numTabs; i++) { const int32_t tabIndex = tabIndices[i]; const SurfaceTypeEnum::Enum selectedSurfaceType = getSelectedSurfaceType(tabIndex); /* * Updates selected surfaces */ getSelectedSurfaces(tabIndex); for (std::map, Surface*>::iterator mapIter = m_selectedSurface[tabIndex].begin(); mapIter != m_selectedSurface[tabIndex].end(); mapIter++) { std::pair structureSurfaceType = mapIter->first; const SurfaceTypeEnum::Enum surfaceType = structureSurfaceType.second; if (surfaceType == selectedSurfaceType) { Surface* surface = mapIter->second; if (surface != NULL) { const StructureEnum::Enum structure = structureSurfaceType.first; const AString name = ("m_selectedSurface[" + AString::number(tabIndex) + "]"); SceneClass* surfaceClass = new SceneClass(name, "SurfaceSelectionMap", 1); surfaceClass->addInteger("tabIndex", tabIndex); surfaceClass->addEnumeratedType("structure", structure); surfaceClass->addEnumeratedType("surfaceType", surfaceType); surfaceClass->addString("surfaceName", surface->getFileNameNoPath()); classesForSelectedSurfaceArray.push_back(surfaceClass); } } } } sceneClass->addChild(new SceneClassArray("m_selectedSurface", classesForSelectedSurfaceArray)); } /** * 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 ModelWholeBrain::restoreModelSpecificInformationFromScene(const SceneAttributes* sceneAttributes, const SceneClass* sceneClass) { if (sceneClass == NULL) { return; } /* * Restore selected surface */ const SceneClassArray* surfaceSelectionArray = sceneClass->getClassArray("m_selectedSurface"); if (surfaceSelectionArray != NULL) { const int32_t numClasses = surfaceSelectionArray->getNumberOfArrayElements(); for (int32_t ica = 0; ica < numClasses; ica++) { const SceneClass* surfaceClass = surfaceSelectionArray->getClassAtIndex(ica); const int32_t tabIndex = surfaceClass->getIntegerValue("tabIndex", -1); const StructureEnum::Enum structure = surfaceClass->getEnumeratedTypeValue("structure", StructureEnum::INVALID); const SurfaceTypeEnum::Enum surfaceType = surfaceClass->getEnumeratedTypeValue("surfaceType", SurfaceTypeEnum::UNKNOWN); const AString surfaceName = surfaceClass->getStringValue("surfaceName", ""); if ((tabIndex >= 0) && (structure != StructureEnum::INVALID) && (surfaceType != SurfaceTypeEnum::UNKNOWN) && (surfaceName.isEmpty() == false)) { BrainStructure* brainStructure = getBrain()->getBrainStructure(structure, false); if (brainStructure != NULL) { const int32_t numSurfaces = brainStructure->getNumberOfSurfaces(); for (int32_t i = 0; i < numSurfaces; i++) { Surface* surface = brainStructure->getSurface(i); const AString loadedSurfaceName = surface->getFileName(); if (loadedSurfaceName.endsWith(surfaceName)) { setSelectedSurfaceType(tabIndex, surfaceType); setSelectedSurface(structure, tabIndex, surface); break; } } } } } } /* * Need to do after */ m_sceneAssistant->restoreMembers(sceneAttributes, sceneClass); } /** * 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 ModelWholeBrain::copyTabContent(const int32_t sourceTabIndex, const int32_t destinationTabIndex) { Model::copyTabContent(sourceTabIndex, destinationTabIndex); CaretAssertArrayIndex(m_selectedSurfaceType, BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS, sourceTabIndex); CaretAssertArrayIndex(m_selectedSurfaceType, BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS, destinationTabIndex); m_selectedSurface[destinationTabIndex] = m_selectedSurface[sourceTabIndex]; m_selectedSurfaceType[destinationTabIndex] = m_selectedSurfaceType[sourceTabIndex]; m_leftEnabled[destinationTabIndex] = m_leftEnabled[sourceTabIndex]; m_rightEnabled[destinationTabIndex] = m_rightEnabled[sourceTabIndex]; m_cerebellumEnabled[destinationTabIndex] = m_cerebellumEnabled[sourceTabIndex]; m_leftRightSeparation[destinationTabIndex] = m_leftRightSeparation[sourceTabIndex]; m_cerebellumSeparation[destinationTabIndex] = m_cerebellumSeparation[sourceTabIndex]; m_overlaySetArray->copyOverlaySet(sourceTabIndex, destinationTabIndex); } connectome-workbench-1.2.3+git41-gc4c6c90/src/Brain/ModelWholeBrain.h000066400000000000000000000121461300200146000247670ustar00rootroot00000000000000#ifndef __MODEL_WHOLE_BRAIN_H__ #define __MODEL_WHOLE_BRAIN_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 "BrainConstants.h" #include "EventListenerInterface.h" #include "Model.h" #include "StructureEnum.h" #include "SurfaceTypeEnum.h" namespace caret { class Brain; class OverlaySetArray; class Surface; class SceneClassAssistant; class VolumeMappableInterface; /// Controls the display of a whole brain. class ModelWholeBrain : public Model, public EventListenerInterface { public: ModelWholeBrain(Brain* brain); virtual ~ModelWholeBrain(); VolumeMappableInterface* getUnderlayVolumeFile(const int32_t windowTabNumber) const; void getAvailableSurfaceTypes(std::vector& surfaceTypesOut); SurfaceTypeEnum::Enum getSelectedSurfaceType(const int32_t windowTabNumber); void setSelectedSurfaceType(const int32_t windowTabNumber, const SurfaceTypeEnum::Enum surfaceType); std::vector getSelectedSurfaces(const int32_t windowTabNumber); Surface* getSelectedSurface(const StructureEnum::Enum structure, const int32_t windowTabNumber); void setSelectedSurface(const StructureEnum::Enum structure, const int32_t windowTabNumber, Surface* surface); void receiveEvent(Event* event); OverlaySet* getOverlaySet(const int tabIndex); const OverlaySet* getOverlaySet(const int tabIndex) const; void initializeOverlays(); virtual void initializeSelectedSurfaces(); virtual void copyTabContent(const int32_t sourceTabIndex, const int32_t destinationTabIndex); void updateModel(); protected: virtual void saveModelSpecificInformationToScene(const SceneAttributes* sceneAttributes, SceneClass* sceneClass); virtual void restoreModelSpecificInformationFromScene(const SceneAttributes* sceneAttributes, const SceneClass* sceneClass); private: ModelWholeBrain(const ModelWholeBrain&); ModelWholeBrain& operator=(const ModelWholeBrain&); public: AString getNameForGUI(const bool includeStructureFlag) const; virtual AString getNameForBrowserTab() const; private: /** Type of surface for display */ mutable SurfaceTypeEnum::Enum m_selectedSurfaceType[BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS]; /** Selected surface for structure/surface-type */ std::map, Surface*> m_selectedSurface[BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS]; /** Available surface types */ std::set m_availableSurfaceTypes; bool m_leftEnabled[BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS]; bool m_rightEnabled[BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS]; bool m_cerebellumEnabled[BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS]; float m_leftRightSeparation[BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS]; float m_cerebellumSeparation[BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS]; /** Surface types that have an anatomical appearance. */ static std::vector s_anatomicalSurfaceTypes; // mutable VolumeSliceCoordinateSelection m_volumeSlicesSelected[BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS]; VolumeMappableInterface* m_lastVolumeFile; /** Overlays sets for this model and for each tab */ OverlaySetArray* m_overlaySetArray; SceneClassAssistant* m_sceneAssistant; }; #ifdef __MODEL_WHOLE_BRAIN_DEFINE__ std::vector ModelWholeBrain::s_anatomicalSurfaceTypes; #endif // __MODEL_WHOLE_BRAIN_DEFINE__ } // namespace #endif // __MODEL_WHOLE_BRAIN_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Brain/Overlay.cxx000066400000000000000000000616431300200146000237560ustar00rootroot00000000000000 /*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 __OVERLAY_DECLARE__ #include "Overlay.h" #undef __OVERLAY_DECLARE__ #include "AnnotationColorBar.h" #include "CaretAssert.h" #include "CaretLogger.h" #include "CaretMappableDataFile.h" #include "CiftiConnectivityMatrixDenseDynamicFile.h" #include "EventCaretMappableDataFilesGet.h" #include "EventManager.h" #include "EventOverlayValidate.h" #include "LabelFile.h" #include "MetricFile.h" #include "PlainTextStringBuilder.h" #include "RgbaFile.h" #include "SceneClass.h" #include "SceneClassAssistant.h" #include "Surface.h" #include "VolumeFile.h" using namespace caret; /** * \class Overlay * \brief An overlay for selection of mappable data. */ /** * Constructor for files in the given structurs and perhaps volume files. * * @param includeSurfaceStructures * Surface structures for files available in this overlay. * @param includeVolumeFiles * Include (or not) volume files. */ Overlay::Overlay(const std::vector& includeSurfaceStructures, const Overlay::IncludeVolumeFiles includeVolumeFiles) : m_includeSurfaceStructures(includeSurfaceStructures), m_includeVolumeFiles(includeVolumeFiles) { m_opacity = 1.0; m_selectedMapFile = NULL; m_selectedMapIndex = -1; m_name = "Overlay "; m_enabled = true; m_mapYokingGroup = MapYokingGroupEnum::MAP_YOKING_GROUP_OFF; m_wholeBrainVoxelDrawingMode = WholeBrainVoxelDrawingMode::DRAW_VOXELS_ON_TWO_D_SLICES; m_colorBar = new AnnotationColorBar(AnnotationAttributesDefaultTypeEnum::NORMAL); m_colorBar->setCoordinateSpace(AnnotationCoordinateSpaceEnum::WINDOW); m_sceneAssistant = new SceneClassAssistant(); m_sceneAssistant->add("m_opacity", &m_opacity); m_sceneAssistant->add("m_enabled", &m_enabled); m_sceneAssistant->add("m_wholeBrainVoxelDrawingMode", &m_wholeBrainVoxelDrawingMode); m_sceneAssistant->add("m_mapYokingGroup", &m_mapYokingGroup); m_sceneAssistant->add("m_colorBar", "AnnotationColorBar", m_colorBar); EventManager::get()->addEventListener(this, EventTypeEnum::EVENT_OVERLAY_VALIDATE); } /** * Destructor. */ Overlay::~Overlay() { EventManager::get()->removeAllEventsFromListener(this); delete m_colorBar; delete m_sceneAssistant; } /** * Receives events that this object is listening for. * * @param event * An event. */ void Overlay::receiveEvent(Event* event) { if (event->getEventType() == EventTypeEnum::EVENT_OVERLAY_VALIDATE) { EventOverlayValidate* eov = dynamic_cast(event); CaretAssert(eov); eov->testValidOverlay(this); } } /** * Set the number of this overlay. * * @param overlayIndex * Index for this overlay. */ void Overlay::setOverlayNumber(const int32_t overlayIndex) { m_name = "Overlay " + AString::number(overlayIndex + 1); } /** * Get the opacity. * * @return The opacity. */ float Overlay::getOpacity() const { return m_opacity; } /** * Set the opacity. * * @param opacity * New value for opacity. */ void Overlay::setOpacity(const float opacity) { m_opacity = opacity; } /** * @return The voxel drawing mode for whole brain. */ WholeBrainVoxelDrawingMode::Enum Overlay::getWholeBrainVoxelDrawingMode() const { return m_wholeBrainVoxelDrawingMode; } /** * Set the voxel drawing mode for whole brain. * * @param wholeBrainVoxelDrawingMode * New mode. */ void Overlay::setWholeBrainVoxelDrawingMode(const WholeBrainVoxelDrawingMode::Enum wholeBrainVoxelDrawingMode) { m_wholeBrainVoxelDrawingMode = wholeBrainVoxelDrawingMode; } AString Overlay::getName() const { return m_name; } /** * Get a description of this object's content. * @return String describing this object's content. */ AString Overlay::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 Overlay::getDescriptionOfContent(PlainTextStringBuilder& descriptionOut) const { Overlay* me = const_cast(this); if (me != NULL) { if (me->isEnabled()) { CaretMappableDataFile* mapFile = NULL; int32_t mapIndex = 0; me->getSelectionData(mapFile, mapIndex); if (mapFile != NULL) { descriptionOut.addLine("File: "+ mapFile->getFileNameNoPath()); if (mapFile->hasMapAttributes()) { if ((mapIndex >= 0) && (mapIndex < mapFile->getNumberOfMaps())) { descriptionOut.addLine("Map Index: " + AString::number(mapIndex + 1)); descriptionOut.addLine("Map Name: " + mapFile->getMapName(mapIndex)); } } descriptionOut.addLine("Structure: " + StructureEnum::toGuiName(mapFile->getStructure())); } } } } /** * @return Enabled status for this surface overlay. */ bool Overlay::isEnabled() const { return m_enabled; } /** * Set the enabled status for this surface overlay. * @param enabled * New status. */ void Overlay::setEnabled(const bool enabled) { m_enabled = enabled; } /** * Copy the data from the given overlay to this overlay. * @param overlay * Overlay from which data is transferred. */ void Overlay::copyData(const Overlay* overlay) { CaretAssert(overlay); /* * These members are not copied since they * identify the overlay: * name * overlayIndex * */ m_opacity = overlay->m_opacity; m_enabled = overlay->m_enabled; m_selectedMapFile = overlay->m_selectedMapFile; m_selectedMapIndex = overlay->m_selectedMapIndex; m_mapYokingGroup = overlay->m_mapYokingGroup; *m_colorBar = *overlay->m_colorBar; } /** * Swap the data from the given overlay to this overlay. * @param overlay * Overlay from which data is transferred. */ void Overlay::swapData(Overlay* overlay) { Overlay* swapOverlay = new Overlay(m_includeSurfaceStructures, m_includeVolumeFiles); swapOverlay->copyData(overlay); overlay->copyData(this); copyData(swapOverlay); delete swapOverlay; } /** * Return the selection information. This method is typically * called to update the user-interface. * * @param selectedMapFileOut * The selected map file. May be NULL. * @param selectedMapIndexOut * Index of selected map in the selected file. */ void Overlay::getSelectionData(CaretMappableDataFile* &selectedMapFileOut, int32_t& selectedMapIndexOut) { std::vector mapFiles; getSelectionData(mapFiles, selectedMapFileOut, //mapUniqueID, selectedMapIndexOut); } /** * 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 selectedMapUniqueIDOut * UniqueID of selected map. * @param selectedMapIndexOut * Index of selected map in the selected file. */ void Overlay::getSelectionData(std::vector& mapFilesOut, CaretMappableDataFile* &selectedMapFileOut, //AString& selectedMapUniqueIDOut, int32_t& selectedMapIndexOut) { mapFilesOut.clear(); selectedMapFileOut = NULL; selectedMapIndexOut = -1; /** * Get the data files. */ std::vector allDataFiles; EventCaretMappableDataFilesGet eventGetMapDataFiles; EventManager::get()->sendEvent(eventGetMapDataFiles.getPointer()); eventGetMapDataFiles.getAllFiles(allDataFiles); bool showVolumeMapFiles = false; switch (m_includeVolumeFiles) { case INCLUDE_VOLUME_FILES_NO: break; case INCLUDE_VOLUME_FILES_YES: showVolumeMapFiles = true; break; } bool showSurfaceMapFiles = false; if ( ! m_includeSurfaceStructures.empty()) { showSurfaceMapFiles = true; } /* * Use only those data files that meet criteria. */ for (std::vector::iterator iter = allDataFiles.begin(); iter != allDataFiles.end(); iter++) { CaretMappableDataFile* mapFile = *iter; bool useIt = false; if (mapFile->isSurfaceMappable()) { if (showSurfaceMapFiles) { for (std::vector::const_iterator iter = m_includeSurfaceStructures.begin(); iter != m_includeSurfaceStructures.end(); iter++) { if (mapFile->isMappableToSurfaceStructure(*iter)) { useIt = true; break; } } } } if (mapFile->isVolumeMappable()) { if (showVolumeMapFiles) { useIt = true; } } if (useIt) { if (mapFile->getDataFileType() == DataFileTypeEnum::CONNECTIVITY_DENSE_DYNAMIC) { CiftiConnectivityMatrixDenseDynamicFile* dynConnFile = dynamic_cast(mapFile); CaretAssert(dynConnFile); useIt = dynConnFile->isEnabledAsLayer(); } } 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 selected data file is valid, see if selected * map is still valid. If not, use first map. */ if (m_selectedMapFile != NULL) { if (m_selectedMapIndex >= m_selectedMapFile->getNumberOfMaps()) { m_selectedMapIndex = m_selectedMapFile->getNumberOfMaps() - 1; } if (m_selectedMapIndex < 0) { m_selectedMapIndex = 0; } } else { /* * Use first map in first file that has one or more maps. */ if (m_selectedMapFile == NULL) { if (mapFilesOut.empty() == false) { for (std::vector::iterator iter = mapFilesOut.begin(); iter != mapFilesOut.end(); iter++) { CaretMappableDataFile* mapTypeFile = *iter; if (mapTypeFile->getNumberOfMaps() > 0) { m_selectedMapFile = mapTypeFile; m_selectedMapIndex = 0; } } } } } selectedMapFileOut = m_selectedMapFile; if (selectedMapFileOut != NULL) { // /* // * Update for overlay yoking // */ // if (m_mapYokingGroup != MapYokingGroupEnum::MAP_YOKING_GROUP_OFF) { // const int32_t yokeMapIndex = MapYokingGroupEnum::getSelectedMapIndex(m_mapYokingGroup); // if ((yokeMapIndex >= 0) // && (yokeMapIndex < selectedMapFileOut->getNumberOfMaps())) { // m_selectedMapIndex = yokeMapIndex; // } // else if (yokeMapIndex >= selectedMapFileOut->getNumberOfMaps()) { // m_selectedMapIndex = selectedMapFileOut->getNumberOfMaps() - 1; // } // } // selectedMapIndexOut = m_selectedMapIndex; //m_selectedMapFile->getMapIndexFromUniqueID(selectedMapUniqueIDOut); } } /** * Set the selected map file and map. * @param selectedMapFile * File that is selected. * @param selectedMapName * Map name that is selected. */ void Overlay::setSelectionData(CaretMappableDataFile* selectedMapFile, const int32_t selectedMapIndex) { m_selectedMapFile = selectedMapFile; m_selectedMapIndex = selectedMapIndex; if (m_mapYokingGroup != MapYokingGroupEnum::MAP_YOKING_GROUP_OFF) { if (m_selectedMapFile == NULL) { m_mapYokingGroup = MapYokingGroupEnum::MAP_YOKING_GROUP_OFF; } // if (selectedMapFile != NULL) { // MapYokingGroupEnum::setSelectedMapIndex(m_mapYokingGroup, // selectedMapIndex); // } // else { // m_mapYokingGroup = MapYokingGroupEnum::MAP_YOKING_GROUP_OFF; // } } } /** * @return Selected map yoking group. */ MapYokingGroupEnum::Enum Overlay::getMapYokingGroup() const { return m_mapYokingGroup; } /** * Set the map yoking group. * * @param mapYokingGroup * New value for map yoking group. */ void Overlay::setMapYokingGroup(const MapYokingGroupEnum::Enum mapYokingGroup) { m_mapYokingGroup = mapYokingGroup; } /** * @return The color bar displayed in graphics window. */ AnnotationColorBar* Overlay::getColorBar() { return m_colorBar; } /** * @return The color bar displayed in graphics window (const method). */ const AnnotationColorBar* Overlay::getColorBar() const { return m_colorBar; } /** * Create a scene for an instance of a class. * * @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 the class' instance. * * @return Pointer to SceneClass object representing the state of * this object. Under some circumstances a NULL pointer may be * returned. Caller will take ownership of returned object. */ SceneClass* Overlay::saveToScene(const SceneAttributes* sceneAttributes, const AString& instanceName) { SceneClass* sceneClass = new SceneClass(instanceName, "Overlay", 1); m_sceneAssistant->saveMembers(sceneAttributes, sceneClass); std::vector mapFiles; CaretMappableDataFile* selectedMapFile = NULL; //AString selectedMapUniqueID; int32_t selectedMapIndex; getSelectionData(mapFiles, selectedMapFile, //selectedMapUniqueID, selectedMapIndex); if ((selectedMapFile != NULL) && (selectedMapIndex >= 0)) { sceneClass->addPathName("selectedMapFileNameWithPath", selectedMapFile->getFileName()); sceneClass->addString("selectedMapFile", selectedMapFile->getFileNameNoPath()); sceneClass->addString("selectedMapName", selectedMapFile->getMapName(selectedMapIndex)); sceneClass->addInteger("selectedMapIndex", selectedMapIndex); } else { sceneClass->addPathName("selectedMapFileNameWithPath", ""); sceneClass->addString("selectedMapFile", ""); sceneClass->addString("selectedMapName", ""); sceneClass->addInteger("selectedMapIndex", -1); } return sceneClass; } /** * Restore the state of an instance of a class. * * @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. May be NULL for some types of scenes. */ void Overlay::restoreFromScene(const SceneAttributes* sceneAttributes, const SceneClass* sceneClass) { if (sceneClass == NULL) { return; } /* * Making a call to getSelectionData() to get the availble * map files */ std::vector mapFiles; CaretMappableDataFile* unusedSelectedMapFile = NULL; AString unusedSelectedMapUniqueID; int32_t unusedSelectedMapIndex; getSelectionData(mapFiles, unusedSelectedMapFile, //unusedSelectedMapUniqueID, unusedSelectedMapIndex); m_sceneAssistant->restoreMembers(sceneAttributes, sceneClass); /* * "m_paletteDisplayedFlag" controlled display of palette colorbar * prior to the addition of AnnotationColorBar */ const AString paletteDisplayedFlagString = sceneClass->getStringValue("m_paletteDisplayedFlag"); if ( ! paletteDisplayedFlagString.isEmpty()) { m_colorBar->reset(); m_colorBar->setDisplayed(sceneClass->getBooleanValue("m_paletteDisplayedFlag")); } /* * OverlayYokingGroup was replaced by MapYokingGroup. * If an overlay yoking group is found, convert it to * a map yoking group. */ const AString overlayYokingGroupName = sceneClass->getEnumeratedTypeValueAsString("m_yokingGroup", ""); if ( ! overlayYokingGroupName.isEmpty()) { bool valid = false; const MapYokingGroupEnum::Enum mapGroup = MapYokingGroupEnum::fromOverlayYokingGroupEnumName(overlayYokingGroupName, &valid); if (valid) { m_mapYokingGroup = mapGroup; } } const AString selectedMapFileNameWithPath = sceneClass->getPathNameValue("selectedMapFileNameWithPath"); const AString selectedMapFileName = sceneClass->getStringValue("selectedMapFile", ""); const AString selectedMapUniqueID = sceneClass->getStringValue("selectedMapUniqueID", ""); const AString selectedMapName = sceneClass->getStringValue("selectedMapName", ""); const int32_t selectedMapIndex = sceneClass->getIntegerValue("selectedMapIndex", -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* foundUniqueIdMapFile = NULL; int32_t foundUniqueIdMapIndex= -1; 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 (foundUniqueIdMapIndex < 0) { const int uniqueIndex = mapFile->getMapIndexFromUniqueID(selectedMapUniqueID); if (uniqueIndex >= 0) { foundUniqueIdMapFile = mapFile; foundUniqueIdMapIndex = uniqueIndex; } } 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 (foundUniqueIdMapIndex >= 0) { if (foundUniqueIdMapFile != NULL) { setSelectionData(foundUniqueIdMapFile, foundUniqueIdMapIndex); found = true; } } } if (! found) { if (foundMapNameIndex >= 0) { if (foundMapNameFile != NULL) { setSelectionData(foundMapNameFile, foundMapNameIndex); found = true; } } } if (found == false) { /* * If not found by unique ID, try to find map by name */ if (selectedMapName.isEmpty() == false) { 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; } } } } } /* * If file found but not matching map, use first map from the file. * This may occur when the map does not have a name. */ if (found == false) { if (matchedMapFile != NULL) { if (matchedMapFile->getNumberOfMaps() > 0) { setSelectionData(matchedMapFile, 0); } } } } connectome-workbench-1.2.3+git41-gc4c6c90/src/Brain/Overlay.h000066400000000000000000000121601300200146000233710ustar00rootroot00000000000000#ifndef __OVERLAY__H_ #define __OVERLAY__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 "CaretObject.h" #include "DataFileTypeEnum.h" #include "EventListenerInterface.h" #include "MapYokingGroupEnum.h" #include "PlainTextStringBuilder.h" #include "SceneableInterface.h" #include "WholeBrainVoxelDrawingMode.h" #include "StructureEnum.h" namespace caret { class AnnotationColorBar; class CaretMappableDataFile; class SceneClassAssistant; class Overlay : public CaretObject, public EventListenerInterface, public SceneableInterface { public: enum IncludeVolumeFiles { INCLUDE_VOLUME_FILES_YES, INCLUDE_VOLUME_FILES_NO }; Overlay(const std::vector& includeSurfaceStructures, const Overlay::IncludeVolumeFiles includeVolumeFiles); virtual ~Overlay(); virtual void receiveEvent(Event* event); float getOpacity() const; void setOpacity(const float opacity); AString getName() const; void setOverlayNumber(const int32_t overlayIndex); virtual AString toString() const; virtual void getDescriptionOfContent(PlainTextStringBuilder& descriptionOut) const; bool isEnabled() const; void setEnabled(const bool enabled); WholeBrainVoxelDrawingMode::Enum getWholeBrainVoxelDrawingMode() const; void setWholeBrainVoxelDrawingMode(const WholeBrainVoxelDrawingMode::Enum wholeBrainVoxelDrawingMode); void copyData(const Overlay* overlay); void swapData(Overlay* overlay); void getSelectionData(std::vector& mapFilesOut, CaretMappableDataFile* &selectedMapFileOut, int32_t& selectedMapIndexOut); void getSelectionData(CaretMappableDataFile* &selectedMapFileOut, int32_t& selectedMapIndexOut); void setSelectionData(CaretMappableDataFile* selectedMapFile, const int32_t selectedMapIndex); MapYokingGroupEnum::Enum getMapYokingGroup() const; void setMapYokingGroup(const MapYokingGroupEnum::Enum mapYokingGroup); AnnotationColorBar* getColorBar(); const AnnotationColorBar* getColorBar() const; virtual SceneClass* saveToScene(const SceneAttributes* sceneAttributes, const AString& instanceName); virtual void restoreFromScene(const SceneAttributes* sceneAttributes, const SceneClass* sceneClass); private: Overlay(const Overlay&); Overlay& operator=(const Overlay&); /** Surface structures of data files displayed in this overlay */ const std::vector m_includeSurfaceStructures; /** Include volume files in this overlay */ const IncludeVolumeFiles m_includeVolumeFiles; /** Name of overlay (DO NOT COPY)*/ AString m_name; /** Index of this overlay (DO NOT COPY)*/ int32_t m_overlayIndex; /** opacity for overlay */ float m_opacity; /** enabled status */ mutable bool m_enabled; /** map yoking group */ MapYokingGroupEnum::Enum m_mapYokingGroup; /** available mappable files */ //std::vector m_mapFiles; /** selected mappable file */ CaretMappableDataFile* m_selectedMapFile; /** selected map index */ int32_t m_selectedMapIndex; /** selected data file map unique id */ //AString m_selectedMapUniqueID; /** Voxel drawing mode in Whole Brain View */ WholeBrainVoxelDrawingMode::Enum m_wholeBrainVoxelDrawingMode; /** The color bar displayed in the graphics window */ AnnotationColorBar* m_colorBar; /** helps with scene save/restore */ SceneClassAssistant* m_sceneAssistant; }; #ifdef __OVERLAY_DECLARE__ #endif // __OVERLAY_DECLARE__ } // namespace #endif //__OVERLAY__H_ connectome-workbench-1.2.3+git41-gc4c6c90/src/Brain/OverlaySet.cxx000066400000000000000000001340071300200146000244250ustar00rootroot00000000000000 /*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 #define __OVERLAY_SET_DECLARE__ #include "OverlaySet.h" #undef __OVERLAY_SET_DECLARE__ #include "BrainStructure.h" #include "CaretAssert.h" #include "CaretLogger.h" #include "CaretMappableDataFile.h" #include "CiftiBrainordinateLabelFile.h" #include "CiftiBrainordinateScalarFile.h" #include "EventCaretMappableDataFilesGet.h" #include "EventManager.h" #include "EventMapYokingSelectMap.h" #include "EventMapYokingValidation.h" #include "LabelFile.h" #include "MetricFile.h" #include "ModelSurfaceMontage.h" #include "ModelVolume.h" #include "ModelWholeBrain.h" #include "Overlay.h" #include "PlainTextStringBuilder.h" #include "Scene.h" #include "SceneClass.h" #include "SceneClassArray.h" #include "SceneClassAssistant.h" #include "Surface.h" #include "VolumeFile.h" using namespace caret; /** * \class OverlaySet * \brief Contains a set of overlay assignments * * The maximum number of overlays is fixed. The number * of overlays presented to the user varies and is * controlled using the ToolBox in a Browser Window. * * The primary overlay is always the overlay at index zero. * The underlay is the overlay at (numberOfDisplayedOverlays - 1). * When models are colored, the overlays are assigned * starting with the underlay and concluding with the primary * overlay. */ /** * \class OverlaySet * \brief Contains a set of overlay assignments * * The maximum number of overlays is fixed. The number * of overlays presented to the user varies and is * controlled using the ToolBox in a Browser Window. * * The primary overlay is always the overlay at index zero. * The underlay is the overlay at (numberOfDisplayedOverlays - 1). * When models are colored, the overlays are assigned * starting with the underlay and concluding with the primary * overlay. */ /** * Constructor for the given surface structures, surface types, and volumes. * * @param name * Name for this overlay set * @param tabIndex * Index of tab for this overlay set. * @param includeSurfaceStructures * Surface structures for data files displayed in this overlay set. * @param includeVolumeFiles * Surface structures for data files displayed in this overlay set. */ OverlaySet::OverlaySet(const AString& name, const int32_t tabIndex, const std::vector& includeSurfaceStructures, const Overlay::IncludeVolumeFiles includeVolumeFiles) : CaretObject(), m_name(name), m_tabIndex(tabIndex), m_includeSurfaceStructures(includeSurfaceStructures), m_includeVolumeFiles(includeVolumeFiles) { m_numberOfDisplayedOverlays = BrainConstants::MINIMUM_NUMBER_OF_OVERLAYS; m_sceneAssistant = new SceneClassAssistant(); m_sceneAssistant->add("m_numberOfDisplayedOverlays", &m_numberOfDisplayedOverlays); for (int i = 0; i < BrainConstants::MAXIMUM_NUMBER_OF_OVERLAYS; i++) { m_overlays[i] = new Overlay(includeSurfaceStructures, includeVolumeFiles); } EventManager::get()->addEventListener(this, EventTypeEnum::EVENT_MAP_YOKING_VALIDATION); EventManager::get()->addEventListener(this, EventTypeEnum::EVENT_MAP_YOKING_SELECT_MAP); } /** * Destructor. */ OverlaySet::~OverlaySet() { EventManager::get()->removeAllEventsFromListener(this); for (int i = 0; i < BrainConstants::MAXIMUM_NUMBER_OF_OVERLAYS; i++) { delete m_overlays[i]; } delete m_sceneAssistant; } /** * Copy the given overlay set to this overlay set. * @param overlaySet * Overlay set that is copied. */ void OverlaySet::copyOverlaySet(const OverlaySet* overlaySet) { for (int i = 0; i < BrainConstants::MAXIMUM_NUMBER_OF_OVERLAYS; i++) { m_overlays[i]->copyData(overlaySet->getOverlay(i)); } m_numberOfDisplayedOverlays = overlaySet->m_numberOfDisplayedOverlays; } /** * @return Returns the top-most overlay regardless of its enabled status. */ Overlay* OverlaySet::getPrimaryOverlay() { return m_overlays[0]; } /** * @return Returns the underlay which is the lowest * displayed overlay. */ Overlay* OverlaySet::getUnderlay() { return m_overlays[getNumberOfDisplayedOverlays() - 1]; } /* * Get the bottom-most overlay that is a volume file for the given * browser tab. * @param browserTabContent * Content of browser tab. * @return Returns the bottom-most overlay that is set a a volume file. * Will return NULL if no, enabled overlays are set to a volume file. */ VolumeMappableInterface* OverlaySet::getUnderlayVolume() { VolumeMappableInterface* vf = NULL; for (int32_t i = (getNumberOfDisplayedOverlays() - 1); i >= 0; i--) { if (m_overlays[i]->isEnabled()) { CaretMappableDataFile* mapFile = NULL; int32_t mapIndex; m_overlays[i]->getSelectionData(mapFile, mapIndex); if (mapFile != NULL) { if (mapFile->isVolumeMappable()) { vf = dynamic_cast(mapFile); if (vf != NULL) { break; } } } } } return vf; } /** * If NO overlay (any overlay) is set to a volume, set the underlay to the first * volume that it finds. * @return Returns the volume file that was selected or NULL if no * volume file was found. */ VolumeMappableInterface* OverlaySet::setUnderlayToVolume() { VolumeMappableInterface * vf = getUnderlayVolume(); if (vf == NULL) { const int32_t overlayIndex = getNumberOfDisplayedOverlays() - 1; if (overlayIndex >= 0) { std::vector mapFiles; CaretMappableDataFile* mapFile; //AString mapUniqueID; int32_t mapIndex; m_overlays[overlayIndex]->getSelectionData(mapFiles, mapFile, //mapUniqueID, mapIndex); const int32_t numMapFiles = static_cast(mapFiles.size()); for (int32_t i = 0; i < numMapFiles; i++) { if (mapFiles[i]->isVolumeMappable()) { vf = dynamic_cast(mapFiles[i]); if (vf != NULL) { CaretMappableDataFile* cmdf = dynamic_cast(vf); CaretAssert(cmdf); m_overlays[overlayIndex]->setSelectionData(cmdf, 0); break; } } } } } return vf; } /** * Get the overlay at the specified index. * @param overlayNumber * Index of the overlay. * @return Overlay at the given index. */ const Overlay* OverlaySet::getOverlay(const int32_t overlayNumber) const { CaretAssertArrayIndex(m_overlays, BrainConstants::MAXIMUM_NUMBER_OF_OVERLAYS, overlayNumber); return m_overlays[overlayNumber]; } /** * Get the overlay at the specified index. * @param overlayNumber * Index of the overlay. * @return Overlay at the given index. */ Overlay* OverlaySet::getOverlay(const int32_t overlayNumber) { CaretAssertArrayIndex(m_overlays, BrainConstants::MAXIMUM_NUMBER_OF_OVERLAYS, overlayNumber); return m_overlays[overlayNumber]; } /** * Get a description of this object's content. * @return String describing this object's content. */ AString OverlaySet::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 OverlaySet::getDescriptionOfContent(PlainTextStringBuilder& descriptionOut) const { descriptionOut.addLine("Overlay Set"); const int numOverlays = getNumberOfDisplayedOverlays(); for (int32_t i = 0; i < numOverlays; i++) { if (getOverlay(i)->isEnabled()) { descriptionOut.pushIndentation(); descriptionOut.addLine("Overlay " + AString::number(i + 1) + ": "); descriptionOut.pushIndentation(); getOverlay(i)->getDescriptionOfContent(descriptionOut); descriptionOut.popIndentation(); descriptionOut.popIndentation(); } } } /** * Add a displayed overlay. If the maximum * number of surface overlays is reached, * this method has no effect. */ void OverlaySet::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 OverlaySet::getNumberOfDisplayedOverlays() const { return m_numberOfDisplayedOverlays; } /** * Sets the number of displayed overlays. * @param numberOfDisplayedOverlays * Number of overlays for display. */ void OverlaySet::setNumberOfDisplayedOverlays(const int32_t numberOfDisplayedOverlays) { const int32_t oldNumberOfDisplayedOverlays = m_numberOfDisplayedOverlays; m_numberOfDisplayedOverlays = numberOfDisplayedOverlays; if (m_numberOfDisplayedOverlays < BrainConstants::MINIMUM_NUMBER_OF_OVERLAYS) { m_numberOfDisplayedOverlays = BrainConstants::MINIMUM_NUMBER_OF_OVERLAYS; } if (m_numberOfDisplayedOverlays > BrainConstants::MAXIMUM_NUMBER_OF_OVERLAYS) { m_numberOfDisplayedOverlays = BrainConstants::MAXIMUM_NUMBER_OF_OVERLAYS; } /* * If one overlay added (probably through GUI), * shift all overlays down one position so that * new overlay appears at the top */ const int32_t numberOfOverlaysAdded = m_numberOfDisplayedOverlays - oldNumberOfDisplayedOverlays; if (numberOfOverlaysAdded == 1) { for (int32_t i = (m_numberOfDisplayedOverlays - 1); i >= 0; i--) { moveDisplayedOverlayDown(i); } } } /** * Insert an overlay below this overlay * @param overlayIndex * Index of overlay for which an overlay is added below */ void OverlaySet::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 OverlaySet::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 OverlaySet::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++) { m_overlays[i]->copyData(m_overlays[i+1]); } } } /** * 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 OverlaySet::moveDisplayedOverlayUp(const int32_t overlayIndex) { if (overlayIndex > 0) { m_overlays[overlayIndex]->swapData(m_overlays[overlayIndex - 1]); } } /** * 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 OverlaySet::moveDisplayedOverlayDown(const int32_t overlayIndex) { const int32_t nextOverlayIndex = overlayIndex + 1; if (nextOverlayIndex < m_numberOfDisplayedOverlays) { m_overlays[overlayIndex]->swapData(m_overlays[nextOverlayIndex]); } } /** * Match the desired names to maps (or file if no maps match) and optionally * structure to files/maps and return the matches. * * Note: If there are NO match names, all files are matched using the first * map in each file. * * @param matchedFilesOut * Output to which matched files are APPENDED. * @param matchedFileIndicesOut * Output to which matched file indices are APPENDED. * @param matchToStructures * If not empty, only include files that map to these structures. If * matchToVolumeData is true, this parameter is ignored. If this is (empty * OR All) AND matchToVolumeData is false, all structures match. * @param dataFileType * Data file type of desired files. * @param matchToVolumeData * Include only files that map to volume data. If true, matchToStructures * is ignored. * @param matchToNamesRegularExpressionText * Text for regular expression used for name matching. * @param matchToNamesRegularExpressionResult * Status of regular expression matching for inclusion of file. * @param matchOneFilePerStructure * If true, limit matched files so there is no more than one file * for each structure. * @param * True if matching files were found, else false. */ bool OverlaySet::findFilesWithMapNamed(std::vector& matchedFilesOut, std::vector& matchedFileIndicesOut, const std::vector& matchToStructures, const DataFileTypeEnum::Enum dataFileType, const bool matchToVolumeData, const AString& matchToNamesRegularExpressionText, const bool matchToNamesRegularExpressionResult, const bool matchOneFilePerStructure) { std::vector matchedFiles; std::vector matchedFileIndices; /* * Aggregate matching names and make them lower case */ QRegExp regularExpression; if (matchToNamesRegularExpressionText.isEmpty() == false) { regularExpression = QRegExp(matchToNamesRegularExpressionText); } /* * Get files matching data type */ EventCaretMappableDataFilesGet mapFileGetEvent(dataFileType); EventManager::get()->sendEvent(mapFileGetEvent.getPointer()); std::vector matchToMapFiles; mapFileGetEvent.getAllFiles(matchToMapFiles); const int32_t numberOfMatchFiles = static_cast(matchToMapFiles.size()); if (numberOfMatchFiles <= 0) { return false; } /* * Determine which files should be tested by examing * structure or if volume */ std::vector testMapFiles; for (int32_t iFile = 0; iFile < numberOfMatchFiles; iFile++) { bool fileMatchFlag = false; CaretMappableDataFile* mapFile = matchToMapFiles[iFile]; /* * Volume mappable files only? */ if (matchToVolumeData) { if (mapFile->isVolumeMappable()) { fileMatchFlag = true; } } /* * Test structures? */ if (matchToStructures.empty() == false) { const StructureEnum::Enum mapFileStructure = mapFile->getStructure(); /* * File maps to ALL structures? */ if (mapFileStructure == StructureEnum::ALL) { fileMatchFlag = true; } else { /* * Specific structutures */ if (std::find(matchToStructures.begin(), matchToStructures.end(), mapFileStructure) != matchToStructures.end()) { fileMatchFlag = true; } } } if (fileMatchFlag) { if (mapFile->getNumberOfMaps() > 0) { testMapFiles.push_back(mapFile); } } } /* * No files to test? */ const int32_t numTestFiles = static_cast(testMapFiles.size()); if (numTestFiles <= 0) { return false; } std::set matchedStructures; /* * If there are names to match */ if (matchToNamesRegularExpressionText.isEmpty() == false) { /* * First preference is matching MAP name */ for (int32_t iFile = 0; iFile < numTestFiles; iFile++) { CaretMappableDataFile* mapFile = testMapFiles[iFile]; const StructureEnum::Enum mapFileStructure = mapFile->getStructure(); if (matchOneFilePerStructure) { if (matchedStructures.find(mapFileStructure) != matchedStructures.end()) { continue; } } /* * If NOT matching, exclude files whose name matches */ if (matchToNamesRegularExpressionResult == false) { const AString fileName = mapFile->getFileNameNoPath().toLower(); const bool fileNameMatch = (regularExpression.indexIn(fileName) >= 0); if (fileNameMatch) { continue; } } const int32_t numMaps = mapFile->getNumberOfMaps(); for (int32_t iMap = 0; iMap < numMaps; iMap++) { const AString mapName = mapFile->getMapName(iMap).toLower(); const bool match = (regularExpression.indexIn(mapName) >= 0); if (match == matchToNamesRegularExpressionResult) { matchedFiles.push_back(mapFile); matchedFileIndices.push_back(iMap); matchedStructures.insert(mapFileStructure); break; } } } /* * Find matching FILE name if NO map matches */ if (matchedFiles.empty()) { for (int32_t iFile = 0; iFile < numTestFiles; iFile++) { CaretMappableDataFile* mapFile = testMapFiles[iFile]; const StructureEnum::Enum mapFileStructure = mapFile->getStructure(); if (matchOneFilePerStructure) { if (matchedStructures.find(mapFileStructure) != matchedStructures.end()) { continue; } } const AString fileName = mapFile->getFileNameNoPath().toLower(); const bool match = (regularExpression.indexIn(fileName) >= 0); if (match == matchToNamesRegularExpressionResult) { matchedFiles.push_back(mapFile); matchedFileIndices.push_back(0); matchedStructures.insert(mapFileStructure); } } } } else { /* * No names to match so just match to first map in each file */ for (int32_t iFile = 0; iFile < numTestFiles; iFile++) { CaretMappableDataFile* mapFile = testMapFiles[iFile]; const StructureEnum::Enum mapFileStructure = mapFile->getStructure(); if (matchOneFilePerStructure) { if (matchedStructures.find(mapFileStructure) != matchedStructures.end()) { continue; } } matchedFiles.push_back(mapFile); matchedFileIndices.push_back(0); matchedStructures.insert(mapFileStructure); } } CaretAssert(matchedFiles.size() == matchedFileIndices.size()); const bool filesFound = (matchedFiles.empty() == false); /* * APPEND to output, do not replace */ matchedFilesOut.insert(matchedFilesOut.end(), matchedFiles.begin(), matchedFiles.end()); matchedFileIndicesOut.insert(matchedFileIndicesOut.end(), matchedFileIndices.begin(), matchedFileIndices.end()); return filesFound; } /** * Find underlay files. * * @param matchToStructures * Structures to include. * @param includeVolumeFiles * Include volume files. * @param filesOut * Output containing files that were selected. * @param mapIndicesOut * Output containing maps indices in files that were selected. */ void OverlaySet::findUnderlayFiles( const std::vector& matchToStructures, const bool includeVolumeFiles, std::vector& filesOut, std::vector& mapIndicesOut) { /* * First, try to find CIFTI shape files */ if (findFilesWithMapNamed(filesOut, mapIndicesOut, matchToStructures, DataFileTypeEnum::CONNECTIVITY_DENSE_SCALAR, false, s_shapeMatchRegularExpressionText, true, true) == false) { /* * Second, try to find METRIC shape files */ findFilesWithMapNamed(filesOut, mapIndicesOut, matchToStructures, DataFileTypeEnum::METRIC, false, s_shapeMatchRegularExpressionText, true, true); } if (includeVolumeFiles) { std::vector volumeFiles = getVolumeFiles(); const int32_t numVolumes = static_cast(volumeFiles.size()); if (numVolumes > 0) { bool foundAnatomyVolume = false; for (int32_t i = 0; i < numVolumes; i++) { VolumeFile* vf = volumeFiles[i]; if (vf->getType() == SubvolumeAttributes::ANATOMY) { if (vf->getNumberOfMaps() > 0) { filesOut.push_back(vf); mapIndicesOut.push_back(0); foundAnatomyVolume = true; break; } } } if (foundAnatomyVolume == false) { for (int32_t i = 0; i < numVolumes; i++) { VolumeFile* vf = volumeFiles[i]; bool testIt = true; if (vf->getType() == SubvolumeAttributes::LABEL) { testIt = false; } if (testIt) { if (vf->getNumberOfMaps() > 0) { PaletteColorMapping* pcm = vf->getMapPaletteColorMapping(0); if (pcm != NULL) { const AString paletteName = pcm->getSelectedPaletteName(); if (paletteName.contains("gray") || paletteName.contains("grey")) { filesOut.push_back(vf); mapIndicesOut.push_back(0); foundAnatomyVolume = true; break; } } } } } } } } CaretAssert(filesOut.size() == mapIndicesOut.size()); } /** * @return All volume files. */ std::vector OverlaySet::getVolumeFiles() const { std::vector volumeFiles; EventCaretMappableDataFilesGet mapFileGetEvent(DataFileTypeEnum::VOLUME); EventManager::get()->sendEvent(mapFileGetEvent.getPointer()); std::vector matchToMapFiles; mapFileGetEvent.getAllFiles(matchToMapFiles); for (std::vector::iterator iter = matchToMapFiles.begin(); iter != matchToMapFiles.end(); iter++) { VolumeFile* vf = dynamic_cast(*iter); CaretAssert(vf); volumeFiles.push_back(vf); } return volumeFiles; } /** * Find middle layer files. * * @param matchToStructures * Structures to include. * @param includeVolumeFiles * Include volume files. * @param filesOut * Output containing files that were selected. * @param mapIndicesOut * Output containing maps indices in files that were selected. */ void OverlaySet::findMiddleLayerFiles(const std::vector& matchToStructures, const bool includeVolumeFiles, std::vector& filesOut, std::vector& mapIndicesOut) { std::vector matchToNames; /* * First, try to find CIFTI scalar files with myelin */ if (findFilesWithMapNamed(filesOut, mapIndicesOut, matchToStructures, DataFileTypeEnum::CONNECTIVITY_DENSE_SCALAR, includeVolumeFiles, s_myelinMatchRegularExpressionText, true, false) == false) { /* * Second, try to find METRIC files with neither shape nor myelin */ findFilesWithMapNamed(filesOut, mapIndicesOut, matchToStructures, DataFileTypeEnum::METRIC, includeVolumeFiles, s_myelinMatchRegularExpressionText, true, false); } /* * Second, try to find CIFTI scalar files with neither shape nor myelin */ if (findFilesWithMapNamed(filesOut, mapIndicesOut, matchToStructures, DataFileTypeEnum::CONNECTIVITY_DENSE_SCALAR, includeVolumeFiles, s_shapeMyelinMatchRegularExpressionText, false, false) == false) { /* * Second, try to find METRIC files with neither shape nor myelin */ findFilesWithMapNamed(filesOut, mapIndicesOut, matchToStructures, DataFileTypeEnum::METRIC, includeVolumeFiles, s_shapeMyelinMatchRegularExpressionText, false, false); } if (includeVolumeFiles) { std::vector volumeFiles = getVolumeFiles(); const int32_t numVolumes = static_cast(volumeFiles.size()); for (int32_t i = 0; i < numVolumes; i++) { VolumeFile* vf = volumeFiles[i]; if ((vf->getType() == SubvolumeAttributes::FUNCTIONAL)) { if (vf->getNumberOfMaps() > 0) { filesOut.push_back(vf); mapIndicesOut.push_back(0); break; } } } } CaretAssert(filesOut.size() == mapIndicesOut.size()); } /** * Find overlay files. * * @param matchToStructures * Structures to include. * @param includeVolumeFiles * Include volume files. * @param filesOut * Output containing files that were selected. * @param mapIndicesOut * Output containing maps indices in files that were selected. */ void OverlaySet::findOverlayFiles(const std::vector& matchToStructures, const bool includeVolumeFiles, std::vector& filesOut, std::vector& mapIndicesOut) { /* * First, try to find CIFTI LABEL files */ if (findFilesWithMapNamed(filesOut, mapIndicesOut, matchToStructures, DataFileTypeEnum::CONNECTIVITY_DENSE_LABEL, includeVolumeFiles, "", true, true) == false) { /* * Second, try to find LABEL files */ findFilesWithMapNamed(filesOut, mapIndicesOut, matchToStructures, DataFileTypeEnum::LABEL, includeVolumeFiles, "", true, true); } if (includeVolumeFiles) { std::vector volumeFiles = getVolumeFiles(); const int32_t numVolumes = static_cast(volumeFiles.size()); for (int32_t i = 0; i < numVolumes; i++) { VolumeFile* vf = volumeFiles[i]; if (vf->getType() == SubvolumeAttributes::LABEL) { if (vf->getNumberOfMaps() > 0) { filesOut.push_back(vf); mapIndicesOut.push_back(0); break; } } } } CaretAssert(filesOut.size() == mapIndicesOut.size()); } /** * Initialize the overlays. */ void OverlaySet::initializeOverlays() { bool isMatchToVolumeUnderlay = false; bool isMatchToVolumeOverlays = false; switch (m_includeVolumeFiles) { case Overlay::INCLUDE_VOLUME_FILES_NO: break; case Overlay::INCLUDE_VOLUME_FILES_YES: /* * If no surface structures, then it must be volume slice view * so allow volumes to be in the overlays. */ if (m_includeSurfaceStructures.empty()) { isMatchToVolumeOverlays = true; } isMatchToVolumeUnderlay = true; break; } /* * Underlays consist of anatomical type data */ std::vector underlayMapFiles; std::vector underlayMapIndices; findUnderlayFiles(m_includeSurfaceStructures, isMatchToVolumeUnderlay, underlayMapFiles, underlayMapIndices); /* * Middle layers are Cifti labels or Gifti Labels * that do not contain shape data */ std::vector middleLayerMapFiles; std::vector middleLayerMapIndices; findMiddleLayerFiles(m_includeSurfaceStructures, isMatchToVolumeOverlays, middleLayerMapFiles, middleLayerMapIndices); /* * Overlays consist of Cifti scalars or Gifti Metric */ std::vector overlayMapFiles; std::vector overlayMapIndices; findOverlayFiles(m_includeSurfaceStructures, isMatchToVolumeOverlays, overlayMapFiles, overlayMapIndices); const int32_t numberOfUnderlayFiles = static_cast(underlayMapFiles.size()); /* * Number of overlay that are displayed. */ const int32_t numberOfDisplayedOverlays = getNumberOfDisplayedOverlays(); /* * Track overlay that were initialized */ std::vector overlayInitializedFlag(numberOfDisplayedOverlays, false); /* * Put in the shape files at the bottom * Note that highest overlay index is bottom */ int32_t overlayIndexForUnderlay = (numberOfDisplayedOverlays - 1); for (int32_t underlayFileIndex = 0; underlayFileIndex < numberOfUnderlayFiles; underlayFileIndex++) { if (overlayIndexForUnderlay >= 0) { Overlay* overlay = getOverlay(overlayIndexForUnderlay); overlay->setSelectionData(underlayMapFiles[underlayFileIndex], underlayMapIndices[underlayFileIndex]); overlayInitializedFlag[overlayIndexForUnderlay] = true; overlayIndexForUnderlay--; } else { break; } } /* * Combine overlay and middle layer files */ std::vector upperLayerFiles; std::vector upperLayerIndices; upperLayerFiles.insert(upperLayerFiles.end(), overlayMapFiles.begin(), overlayMapFiles.end()); upperLayerIndices.insert(upperLayerIndices.end(), overlayMapIndices.begin(), overlayMapIndices.end()); upperLayerFiles.insert(upperLayerFiles.end(), middleLayerMapFiles.begin(), middleLayerMapFiles.end()); upperLayerIndices.insert(upperLayerIndices.end(), middleLayerMapIndices.begin(), middleLayerMapIndices.end()); CaretAssert(upperLayerFiles.size() == upperLayerIndices.size()); const int32_t numberOfUpperFiles = static_cast(upperLayerFiles.size()); /* * Put in overlay and middle layer files */ for (int32_t upperFileIndex = 0; upperFileIndex < numberOfUpperFiles; upperFileIndex++) { /* * Find available overlay */ int32_t upperLayerOverlayIndex = -1; for (int32_t overlayIndex = 0; overlayIndex < numberOfDisplayedOverlays; overlayIndex++) { if (overlayInitializedFlag[overlayIndex] == false) { upperLayerOverlayIndex = overlayIndex; break; } } if (upperLayerOverlayIndex >= 0) { Overlay* upperLayerOverlay = getOverlay(upperLayerOverlayIndex); upperLayerOverlay->setSelectionData(upperLayerFiles[upperFileIndex], upperLayerIndices[upperFileIndex]); overlayInitializedFlag[upperLayerOverlayIndex] = true; } else { break; } } /* * Disable overlays that were not initialized */ for (int32_t i = 0; i < numberOfDisplayedOverlays; i++) { CaretAssertVectorIndex(overlayInitializedFlag, i); getOverlay(i)->setEnabled(overlayInitializedFlag[i]); } } /** * Get any label files that are selected and applicable for the given surface. * @param surface * Surface for which label files are desired. * @param labelFilesOut * Label files that are applicable to the given surface. * @param labelMapIndicesOut * Selected map indices in the output label files. */ void OverlaySet::getLabelFilesForSurface(const Surface* surface, std::vector& labelFilesOut, std::vector& labelMapIndicesOut) { CaretAssert(surface); labelFilesOut.clear(); labelMapIndicesOut.clear(); const int32_t numberOfOverlays = getNumberOfDisplayedOverlays(); for (int32_t i = 0; i < numberOfOverlays; i++) { Overlay* overlay = getOverlay(i); if (overlay->isEnabled()) { CaretMappableDataFile* mapFile; int32_t mapIndex; overlay->getSelectionData(mapFile, mapIndex); if (mapFile != NULL) { if (mapFile->getDataFileType() == DataFileTypeEnum::LABEL) { if (mapFile->getStructure() == surface->getStructure()) { LabelFile* labelFile = dynamic_cast(mapFile); labelFilesOut.push_back(labelFile); labelMapIndicesOut.push_back(mapIndex); } } } } } } /** * For the given caret mappable data file, find overlays in which the * file is selected and return the indices of the selected maps. * * @param caretMappableDataFile * The caret mappable data file. * @param isLimitToEnabledOverlays * If true, only include map indices for overlay that are enabled. * Otherwise, include map indices for all overlays. * @param selectedMapIndicesOut * Output containing map indices for the given caret mappable data files * that are selected as overlays in this overlay set. */ void OverlaySet::getSelectedMapIndicesForFile(const CaretMappableDataFile* caretMappableDataFile, const bool isLimitToEnabledOverlays, std::vector& selectedMapIndicesOut) const { selectedMapIndicesOut.clear(); /* * Put indices in a set to avoid duplicates and keep them sorted. */ std::set mapIndicesSet; const int32_t numberOfOverlays = getNumberOfDisplayedOverlays(); for (int32_t i = 0; i < numberOfOverlays; i++) { Overlay* overlay = const_cast(getOverlay(i)); bool checkIt = true; if (isLimitToEnabledOverlays) { if (overlay->isEnabled() == false) { checkIt = false; } } if (checkIt) { CaretMappableDataFile* mapFile; int32_t mapIndex; overlay->getSelectionData(mapFile, mapIndex); if (mapFile == caretMappableDataFile) { mapIndicesSet.insert(mapIndex); } } } selectedMapIndicesOut.insert(selectedMapIndicesOut.end(), mapIndicesSet.begin(), mapIndicesSet.end()); } /** * Reset the yoking status of all overlays to off. */ void OverlaySet::resetOverlayYokingToOff() { for (int i = 0; i < BrainConstants::MAXIMUM_NUMBER_OF_OVERLAYS; i++) { m_overlays[i]->setMapYokingGroup(MapYokingGroupEnum::MAP_YOKING_GROUP_OFF); } } /** * Create a scene for an instance of a class. * * @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 the class' instance. * * @return Pointer to SceneClass object representing the state of * this object. Under some circumstances a NULL pointer may be * returned. Caller will take ownership of returned object. */ SceneClass* OverlaySet::saveToScene(const SceneAttributes* sceneAttributes, const AString& instanceName) { SceneClass* sceneClass = new SceneClass(instanceName, "OverlaySet", 1); m_sceneAssistant->saveMembers(sceneAttributes, sceneClass); // const int32_t numOverlaysToSave = BrainConstants::MAXIMUM_NUMBER_OF_OVERLAYS; 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); return sceneClass; } /** * Restore the state of an instance of a class. * * @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. May be NULL for some types of scenes. */ void OverlaySet::restoreFromScene(const SceneAttributes* sceneAttributes, const SceneClass* sceneClass) { if (sceneClass == NULL) { return; } m_sceneAssistant->restoreMembers(sceneAttributes, sceneClass); 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++) { m_overlays[i]->restoreFromScene(sceneAttributes, overlayClassArray->getClassAtIndex(i)); } } } /** * Receive an event. * * @param event * An event for which this instance is listening. */ void OverlaySet::receiveEvent(Event* event) { 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++) { Overlay* overlay = getOverlay(j); CaretMappableDataFile* mapFile = NULL; int32_t mapIndex = -1; overlay->getSelectionData(mapFile, mapIndex); if (mapFile != NULL) { 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++) { Overlay* overlay = getOverlay(j); if (overlay->getMapYokingGroup() == selectMapEvent->getMapYokingGroup()) { CaretMappableDataFile* mapFile = NULL; int32_t mapIndex = -1; overlay->getSelectionData(mapFile, mapIndex); if (mapFile != NULL) { if (yokingGroupMapIndex < mapFile->getNumberOfMaps()) { overlay->setSelectionData(mapFile, yokingGroupMapIndex); } if (mapFile == eventMapFile) { overlay->setEnabled(yokingGroupSelectedStatus); } } } } selectMapEvent->setEventProcessed(); } // const MapYokingGroupEnum::Enum mapYokingGroup = selectMapEvent->get } } connectome-workbench-1.2.3+git41-gc4c6c90/src/Brain/OverlaySet.h000066400000000000000000000155311300200146000240520ustar00rootroot00000000000000#ifndef __OVERLAY_SET__H_ #define __OVERLAY_SET__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 "BrainConstants.h" #include "CaretObject.h" #include "EventListenerInterface.h" #include "Overlay.h" #include "SceneableInterface.h" #include "StructureEnum.h" namespace caret { class BrowserTabContent; class LabelFile; class SceneClassAssistant; class Surface; class PlaneTextStringBuilder; class VolumeFile; class VolumeMappableInterface; class OverlaySet : public CaretObject, public EventListenerInterface, public SceneableInterface { public: OverlaySet(const AString& name, const int32_t tabIndex, const std::vector& includeSurfaceStructures, const Overlay::IncludeVolumeFiles includeVolumeFiles); virtual ~OverlaySet(); virtual void receiveEvent(Event* event); void copyOverlaySet(const OverlaySet* overlaySet); Overlay* getPrimaryOverlay(); Overlay* getUnderlay(); VolumeMappableInterface* getUnderlayVolume(); Overlay* getOverlay(const int32_t overlayNumber); const Overlay* getOverlay(const int32_t overlayNumber) const; void addDisplayedOverlay(); void setNumberOfDisplayedOverlays(const int32_t numberOfDisplayedOverlays); int32_t getNumberOfDisplayedOverlays() 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); VolumeMappableInterface* setUnderlayToVolume(); void initializeOverlays(); void getSelectedMapIndicesForFile(const CaretMappableDataFile* caretMappableDataFile, const bool isLimitToEnabledOverlays, std::vector& selectedMapIndicesOut) const; void getLabelFilesForSurface(const Surface* surface, std::vector& labelFilesOut, std::vector& labelMapIndicesOut); void resetOverlayYokingToOff(); virtual SceneClass* saveToScene(const SceneAttributes* sceneAttributes, const AString& instanceName); virtual void restoreFromScene(const SceneAttributes* sceneAttributes, const SceneClass* sceneClass); public: virtual AString toString() const; virtual void getDescriptionOfContent(PlainTextStringBuilder& descriptionOut) const; private: void findUnderlayFiles(const std::vector& matchToStructures, const bool includeVolumeFiles, std::vector& filesOut, std::vector& mapIndicesOut); void findMiddleLayerFiles(const std::vector& matchToStructures, const bool includeVolumeFiles, std::vector& filesOut, std::vector& mapIndicesOut); void findOverlayFiles(const std::vector& matchToStructures, const bool includeVolumeFiles, std::vector& filesOut, std::vector& mapIndicesOut); OverlaySet(const OverlaySet&); OverlaySet& operator=(const OverlaySet&); Overlay* m_overlays[BrainConstants::MAXIMUM_NUMBER_OF_OVERLAYS]; bool findFilesWithMapNamed(std::vector& matchedFilesOut, std::vector& matchedFileIndicesOut, const std::vector& matchToStructures, const DataFileTypeEnum::Enum dataFileType, const bool matchToVolumeData, const AString& matchToNamesRegularExpressionText, const bool matchToNamesRegularExpressionResult, const bool matchOneFilePerStructure); std::vector getVolumeFiles() const; AString m_name; int32_t m_tabIndex; /** Surface structures of data files displayed in this overlay */ const std::vector m_includeSurfaceStructures; /** Include volume files in this overlay */ const Overlay::IncludeVolumeFiles m_includeVolumeFiles; int32_t m_numberOfDisplayedOverlays; SceneClassAssistant* m_sceneAssistant; /** regular expression for matching myeline names - NOT saved to scenes */ static const AString s_myelinMatchRegularExpressionText; /** regular expression for matching shape names - NOT saved to scenes */ static const AString s_shapeMatchRegularExpressionText; /** regular expression for matching shape and myelin names - NOT saved to scenes */ static const AString s_shapeMyelinMatchRegularExpressionText; }; #ifdef __OVERLAY_SET_DECLARE__ AString const OverlaySet::s_myelinMatchRegularExpressionText = "(myelin)"; AString const OverlaySet::s_shapeMatchRegularExpressionText = "(sulc|shape|curv|depth|thick)"; AString const OverlaySet::s_shapeMyelinMatchRegularExpressionText = "(myelin|sulc|shape|curv|depth|thick)"; #endif // __OVERLAY_SET_DECLARE__ } // namespace #endif //__OVERLAY_SET__H_ connectome-workbench-1.2.3+git41-gc4c6c90/src/Brain/OverlaySetArray.cxx000066400000000000000000000116151300200146000254230ustar00rootroot00000000000000 /*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 __OVERLAY_SET_ARRAY_DECLARE__ #include "OverlaySetArray.h" #undef __OVERLAY_SET_ARRAY_DECLARE__ #include "BrainConstants.h" #include "CaretAssert.h" #include "EventBrowserTabDelete.h" #include "EventManager.h" #include "OverlaySet.h" using namespace caret; /** * \class caret::OverlaySetArray * \brief Maintains an array of overlay sets for use with a model * \ingroup Brain */ /** * Constructor. * * @param includeSurfaceStructures * Surface structures for files available in this overlay. * @param includeVolumeFiles * Include (or not) volume files. * @param name * Name of model using this overlay set. This name is displayed * if there is an attempt to yoke models with incompatible overlays. */ OverlaySetArray::OverlaySetArray(const std::vector& includeSurfaceStructures, const Overlay::IncludeVolumeFiles includeVolumeFiles, const AString& name) : CaretObject(), m_name(name) { m_overlaySets.resize(BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS); for (int32_t i = 0; i < BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS; i++) { m_overlaySets[i] = new OverlaySet(name, i, includeSurfaceStructures, includeVolumeFiles); } EventManager::get()->addEventListener(this, EventTypeEnum::EVENT_BROWSER_TAB_DELETE); } /** * Destructor. */ OverlaySetArray::~OverlaySetArray() { EventManager::get()->removeAllEventsFromListener(this); for (std::vector::iterator iter = m_overlaySets.begin(); iter != m_overlaySets.end(); iter++) { delete *iter; } m_overlaySets.clear(); } /** * Get a description of this object's content. * @return String describing this object's content. */ AString OverlaySetArray::toString() const { return "OverlaySetArray"; } /** * @return The number of overlay sets. */ int32_t OverlaySetArray::getNumberOfOverlaySets() { return m_overlaySets.size(); } /** * Get the overlay set at the given index. * * @param indx * Index of overlay set. * @return * Overlay set at given index. */ OverlaySet* OverlaySetArray::getOverlaySet(const int32_t indx) { CaretAssertVectorIndex(m_overlaySets, indx); return m_overlaySets[indx]; } /** * Initialize the overlay selections. */ void OverlaySetArray::initializeOverlaySelections() { for (int32_t iTab = 0; iTab < BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS; iTab++) { CaretAssertVectorIndex(m_overlaySets, iTab); m_overlaySets[iTab]->initializeOverlays(); } } /** * Receive an event. * * @param event * An event for which this instance is listening. */ void OverlaySetArray::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 overlay yoking for the tab. */ const int32_t tabIndex = deleteTabEvent->getBrowserTabIndex(); if ((tabIndex > 0) && (tabIndex < getNumberOfOverlaySets())) { m_overlaySets[tabIndex]->resetOverlayYokingToOff(); } deleteTabEvent->setEventProcessed(); } } /** * Copy the 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 OverlaySetArray::copyOverlaySet(const int32_t sourceTabIndex, const int32_t destinationTabIndex) { CaretAssertVectorIndex(m_overlaySets, sourceTabIndex); CaretAssertVectorIndex(m_overlaySets, destinationTabIndex); const OverlaySet* sourceOverlaySet = m_overlaySets[sourceTabIndex]; OverlaySet* destinationOverlaySet = m_overlaySets[destinationTabIndex]; destinationOverlaySet->copyOverlaySet(sourceOverlaySet); } connectome-workbench-1.2.3+git41-gc4c6c90/src/Brain/OverlaySetArray.h000066400000000000000000000045431300200146000250520ustar00rootroot00000000000000#ifndef __OVERLAY_SET_ARRAY_H__ #define __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 "EventListenerInterface.h" #include "Overlay.h" namespace caret { class OverlaySet; class OverlaySetArray : public CaretObject, public EventListenerInterface { public: OverlaySetArray(const std::vector& includeSurfaceStructures, const Overlay::IncludeVolumeFiles includeVolumeFiles, const AString& name); virtual ~OverlaySetArray(); int32_t getNumberOfOverlaySets(); OverlaySet* getOverlaySet(const int32_t indx); void initializeOverlaySelections(); void copyOverlaySet(const int32_t sourceTabIndex, const int32_t destinationTabIndex); private: OverlaySetArray(const OverlaySetArray&); OverlaySetArray& operator=(const OverlaySetArray&); public: // ADD_NEW_METHODS_HERE virtual AString toString() const; virtual void receiveEvent(Event* event); private: // ADD_NEW_MEMBERS_HERE void initialize(); /** Name for this overlay set array */ AString m_name; /** The overlay sets */ std::vector m_overlaySets; }; #ifdef __OVERLAY_SET_ARRAY_DECLARE__ // #endif // __OVERLAY_SET_ARRAY_DECLARE__ } // namespace #endif //__OVERLAY_SET_ARRAY_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Brain/ProjectionViewTypeEnum.cxx000066400000000000000000000264011300200146000267640ustar00rootroot00000000000000 /*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 __PROJECTION_VIEW_TYPE_ENUM_DECLARE__ #include "ProjectionViewTypeEnum.h" #undef __PROJECTION_VIEW_TYPE_ENUM_DECLARE__ #include "CaretAssert.h" using namespace caret; /** * \class caret::ProjectionViewTypeEnum * \brief Type for viewing models from left or right */ /** * Constructor. * * @param enumValue * An enumerated value. * @param name * Name of enumerated value. * * @param guiName * User-friendly name for use in user-interface. */ ProjectionViewTypeEnum::ProjectionViewTypeEnum(const Enum enumValue, const AString& name, const AString& guiName) { this->enumValue = enumValue; this->integerCode = integerCodeCounter++; this->name = name; this->guiName = guiName; } /** * Destructor. */ ProjectionViewTypeEnum::~ProjectionViewTypeEnum() { } /** * Initialize the enumerated metadata. */ void ProjectionViewTypeEnum::initialize() { if (initializedFlag) { return; } initializedFlag = true; enumData.push_back(ProjectionViewTypeEnum(PROJECTION_VIEW_CEREBELLUM_ANTERIOR, "PROJECTION_VIEW_CEREBELLUM_ANTERIOR", "Cerebellum Anterior")); enumData.push_back(ProjectionViewTypeEnum(PROJECTION_VIEW_CEREBELLUM_DORSAL, "PROJECTION_VIEW_CEREBELLUM_DORSAL", "Cerebellum Dorsal")); enumData.push_back(ProjectionViewTypeEnum(PROJECTION_VIEW_CEREBELLUM_POSTERIOR, "PROJECTION_VIEW_CEREBELLUM_POSTERIOR", "Cerebellum Posterior")); enumData.push_back(ProjectionViewTypeEnum(PROJECTION_VIEW_CEREBELLUM_VENTRAL, "PROJECTION_VIEW_CEREBELLUM_VENTRAL", "Cerebellum Ventral")); enumData.push_back(ProjectionViewTypeEnum(PROJECTION_VIEW_CEREBELLUM_FLAT_SURFACE, "PROJECTION_VIEW_CEREBELLUM_FLAT_SURFACE", "Cerebellum Flat")); enumData.push_back(ProjectionViewTypeEnum(PROJECTION_VIEW_LEFT_LATERAL, "PROJECTION_VIEW_LEFT_LATERAL", "Left Lateral")); enumData.push_back(ProjectionViewTypeEnum(PROJECTION_VIEW_LEFT_MEDIAL, "PROJECTION_VIEW_LEFT_MEDIAL", "Left Medial")); enumData.push_back(ProjectionViewTypeEnum(PROJECTION_VIEW_LEFT_FLAT_SURFACE, "PROJECTION_VIEW_LEFT_FLAT_SURFACE", "Left Flat")); enumData.push_back(ProjectionViewTypeEnum(PROJECTION_VIEW_RIGHT_LATERAL, "PROJECTION_VIEW_RIGHT_LATERAL", "Right Lateral")); enumData.push_back(ProjectionViewTypeEnum(PROJECTION_VIEW_RIGHT_MEDIAL, "PROJECTION_VIEW_RIGHT_MEDIAL", "Right Medial")); enumData.push_back(ProjectionViewTypeEnum(PROJECTION_VIEW_RIGHT_FLAT_SURFACE, "PROJECTION_VIEW_RIGHT_FLAT_SURFACE", "Right Flat")); } /** * 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 ProjectionViewTypeEnum* ProjectionViewTypeEnum::findData(const Enum enumValue) { if (initializedFlag == false) initialize(); size_t num = enumData.size(); for (size_t i = 0; i < num; i++) { const ProjectionViewTypeEnum* 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 ProjectionViewTypeEnum::toName(Enum enumValue) { if (initializedFlag == false) initialize(); const ProjectionViewTypeEnum* 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. */ ProjectionViewTypeEnum::Enum ProjectionViewTypeEnum::fromName(const AString& name, bool* isValidOut) { if (initializedFlag == false) initialize(); bool validFlag = false; Enum enumValue = PROJECTION_VIEW_LEFT_LATERAL; for (std::vector::iterator iter = enumData.begin(); iter != enumData.end(); iter++) { const ProjectionViewTypeEnum& 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 ProjectionViewTypeEnum")); } return enumValue; } /** * Get a GUI string representation of the enumerated type. * @param enumValue * Enumerated value. * @return * String representing enumerated value. */ AString ProjectionViewTypeEnum::toGuiName(Enum enumValue) { if (initializedFlag == false) initialize(); const ProjectionViewTypeEnum* 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. */ ProjectionViewTypeEnum::Enum ProjectionViewTypeEnum::fromGuiName(const AString& guiName, bool* isValidOut) { if (initializedFlag == false) initialize(); bool validFlag = false; Enum enumValue = PROJECTION_VIEW_LEFT_LATERAL; for (std::vector::iterator iter = enumData.begin(); iter != enumData.end(); iter++) { const ProjectionViewTypeEnum& 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 ProjectionViewTypeEnum")); } return enumValue; } /** * Get the integer code for a data type. * * @return * Integer code for data type. */ int32_t ProjectionViewTypeEnum::toIntegerCode(Enum enumValue) { if (initializedFlag == false) initialize(); const ProjectionViewTypeEnum* 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. */ ProjectionViewTypeEnum::Enum ProjectionViewTypeEnum::fromIntegerCode(const int32_t integerCode, bool* isValidOut) { if (initializedFlag == false) initialize(); bool validFlag = false; Enum enumValue = PROJECTION_VIEW_LEFT_LATERAL; for (std::vector::iterator iter = enumData.begin(); iter != enumData.end(); iter++) { const ProjectionViewTypeEnum& 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 ProjectionViewTypeEnum")); } 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 ProjectionViewTypeEnum::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 ProjectionViewTypeEnum::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(ProjectionViewTypeEnum::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 ProjectionViewTypeEnum::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(ProjectionViewTypeEnum::toGuiName(iter->enumValue)); } if (isSorted) { std::sort(allGuiNames.begin(), allGuiNames.end()); } } connectome-workbench-1.2.3+git41-gc4c6c90/src/Brain/ProjectionViewTypeEnum.h000066400000000000000000000100061300200146000264030ustar00rootroot00000000000000#ifndef __PROJECTION_VIEW_TYPE_ENUM_H__ #define __PROJECTION_VIEW_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 ProjectionViewTypeEnum { public: /** * Enumerated values. */ enum Enum { /** Viewing cerebellum in anterior orientation */ PROJECTION_VIEW_CEREBELLUM_ANTERIOR, /** Viewing cerebellum in dorsal orientation */ PROJECTION_VIEW_CEREBELLUM_DORSAL, /** Viewing cerebellum in posterior orientation */ PROJECTION_VIEW_CEREBELLUM_POSTERIOR, /** Viewing cerebellum in ventral orientation */ PROJECTION_VIEW_CEREBELLUM_VENTRAL, /** Viewing cerebellum in flat surface */ PROJECTION_VIEW_CEREBELLUM_FLAT_SURFACE, /** Viewing models from left lateral */ PROJECTION_VIEW_LEFT_LATERAL, /** Viewing models from left medial */ PROJECTION_VIEW_LEFT_MEDIAL, /** Viewing model left flat surface */ PROJECTION_VIEW_LEFT_FLAT_SURFACE, /** Viewing models from right */ PROJECTION_VIEW_RIGHT_LATERAL, /** Viewing models from right medial */ PROJECTION_VIEW_RIGHT_MEDIAL, /** Viewing model right flat surface */ PROJECTION_VIEW_RIGHT_FLAT_SURFACE }; ~ProjectionViewTypeEnum(); 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: ProjectionViewTypeEnum(const Enum enumValue, const AString& name, const AString& guiName); static const ProjectionViewTypeEnum* 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 __PROJECTION_VIEW_TYPE_ENUM_DECLARE__ std::vector ProjectionViewTypeEnum::enumData; bool ProjectionViewTypeEnum::initializedFlag = false; int32_t ProjectionViewTypeEnum::integerCodeCounter = 0; #endif // __PROJECTION_VIEW_TYPE_ENUM_DECLARE__ } // namespace #endif //__PROJECTION_VIEW_TYPE_ENUM_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Brain/SelectionItem.cxx000066400000000000000000000145071300200146000250760ustar00rootroot00000000000000 /*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 __SELECTION_ITEM_DECLARE__ #include "SelectionItem.h" #undef __SELECTION_ITEM_DECLARE__ #include using namespace caret; #include "SelectionItemDataTypeEnum.h" /** * \class SelectionItem * \brief Abstract class for selected items. * * Abstract class for selected items. */ /** * Constructor. */ SelectionItem::SelectionItem(const SelectionItemDataTypeEnum::Enum itemDataType) : CaretObject() { m_itemDataType = itemDataType; m_enabledForSelection = true; m_brain = NULL; m_screenDepth = 0.0; m_screenXYZ[0] = 0.0; m_screenXYZ[1] = 0.0; m_screenXYZ[2] = std::numeric_limits::max(); m_modelXYZ[0] = 0.0; m_modelXYZ[1] = 0.0; m_modelXYZ[2] = 0.0; } /** * Destructor. */ SelectionItem::~SelectionItem() { } /** * Copy constructor. * @param obj * Object that is copied. */ SelectionItem::SelectionItem(const SelectionItem& obj) : CaretObject(obj) { copyHelperSelectionItem(obj); } /** * Assignment operator. * @param obj * Data copied from obj to this. * @return * Reference to this object. */ SelectionItem& SelectionItem::operator=(const SelectionItem& obj) { if (this != &obj) { CaretObject::operator=(obj); copyHelperSelectionItem(obj); } return *this; } /** * Helps with copying an object of this type. * @param ff * Object that is copied. */ void SelectionItem::copyHelperSelectionItem(const SelectionItem& idItem) { m_brain = idItem.m_brain; m_enabledForSelection = idItem.m_enabledForSelection; m_screenDepth = idItem.m_screenDepth; m_screenXYZ[0] = idItem.m_screenXYZ[0]; m_screenXYZ[1] = idItem.m_screenXYZ[1]; m_screenXYZ[2] = idItem.m_screenXYZ[2]; m_modelXYZ[0] = idItem.m_modelXYZ[0]; m_modelXYZ[1] = idItem.m_modelXYZ[1]; m_modelXYZ[2] = idItem.m_modelXYZ[2]; } /** * Reset this selection item. Deriving * classes should override this method to * reset its selection data and also call * the method in this class. */ void SelectionItem::reset() { m_brain = NULL; m_screenDepth = 0.0; m_screenXYZ[0] = 0.0; m_screenXYZ[1] = 0.0; m_screenXYZ[2] = std::numeric_limits::max(); m_modelXYZ[0] = 0.0; m_modelXYZ[1] = 0.0; m_modelXYZ[2] = 0.0; } /** * Get a description of this object's content. * @return String describing this object's content. */ AString SelectionItem::toString() const { AString text = ""; text += ("Type: " + SelectionItemDataTypeEnum::toGuiName(m_itemDataType) + "\n"); text += ("Depth: " + AString::number(m_screenDepth) + "\n"); text += ("Model XYZ: " + AString::fromNumbers(m_modelXYZ, 3, ", ") + "\n"); text += ("Screen XYZ: " + AString::fromNumbers(m_screenXYZ, 3, ", ") + "\n"); return text; } /** * @return The type of selected item. */ SelectionItemDataTypeEnum::Enum SelectionItem::getItemDataType() const { return m_itemDataType; } /** * @return Data type enabled for selection. */ bool SelectionItem::isEnabledForSelection() const { return m_enabledForSelection; } /** * Set the data type enabled for selection. * @param enabled * New value for selection enabled status. */ void SelectionItem::setEnabledForSelection(const bool enabled) { m_enabledForSelection = enabled; } /** * @return Brain in which identification item resides. */ Brain* SelectionItem::getBrain() { return m_brain; } /** * Set the brain. * * param brain * Brain in which identification item resides. */ void SelectionItem::setBrain(Brain* brain) { m_brain = brain; } /** * @return Screen depth of item. */ double SelectionItem::getScreenDepth() const { return m_screenDepth; } /** * Set the screen depth of the item. * @param screenDepth * New value for screen depth. */ void SelectionItem::setScreenDepth(const double screenDepth) { m_screenDepth = screenDepth; } /** * Get the screen XYZ of the selected item. * @param screenXYZ * XYZ out. */ void SelectionItem::getScreenXYZ(double screenXYZ[3]) const { screenXYZ[0] = m_screenXYZ[0]; screenXYZ[1] = m_screenXYZ[1]; screenXYZ[2] = m_screenXYZ[2]; } /** * Set the screen XYZ of the selected item. * @param screenXYZ * new XYZ. */ void SelectionItem::setScreenXYZ(const double screenXYZ[3]) { m_screenXYZ[0] = screenXYZ[0]; m_screenXYZ[1] = screenXYZ[1]; m_screenXYZ[2] = screenXYZ[2]; } /** * Get the model XYZ of the selected item. * @param modelXYZ * XYZ out. */ void SelectionItem::getModelXYZ(double modelXYZ[3]) const { modelXYZ[0] = m_modelXYZ[0]; modelXYZ[1] = m_modelXYZ[1]; modelXYZ[2] = m_modelXYZ[2]; } /** * Set the model XYZ of the selected item. * @param modelXYZ * new XYZ. */ void SelectionItem::setModelXYZ(const double modelXYZ[3]) { m_modelXYZ[0] = modelXYZ[0]; m_modelXYZ[1] = modelXYZ[1]; m_modelXYZ[2] = modelXYZ[2]; } /** * Is the other screen depth closer to the viewer than the currently * selected item? So, if true is returned, then replace the * current identification item * * (1) If there is no selected item, true is immediately returned. * (2) If there is an selected item and the other screen depth is closer * to the viewer, true is returned. * (3) false is returned. * * @param otherScreenDepth * Screen depth for testing. * @return result of test. */ bool SelectionItem::isOtherScreenDepthCloserToViewer(const double otherScreenDepth) const { if (isValid() == false) { return true; } if (otherScreenDepth < m_screenDepth) { return true; } return false; } connectome-workbench-1.2.3+git41-gc4c6c90/src/Brain/SelectionItem.h000066400000000000000000000054251300200146000245220ustar00rootroot00000000000000#ifndef __SELECTION_ITEM__H_ #define __SELECTION_ITEM__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 "SelectionItemDataTypeEnum.h" namespace caret { class Brain; class SelectionItem : public CaretObject { protected: SelectionItem(const SelectionItemDataTypeEnum::Enum itemDataType); SelectionItem(const SelectionItem&); SelectionItem& operator=(const SelectionItem&); public: virtual ~SelectionItem(); SelectionItemDataTypeEnum::Enum getItemDataType() const; bool isEnabledForSelection() const; void setEnabledForSelection(const bool enabled); Brain* getBrain(); void setBrain(Brain* brain); bool isOtherScreenDepthCloserToViewer(const double otherScreenDepth) const; double getScreenDepth() const; void setScreenDepth(const double screenDepth); void getScreenXYZ(double screenXYZ[3]) const; void setScreenXYZ(const double screenXYZ[3]); void getModelXYZ(double modelXYZ[3]) const; void setModelXYZ(const double modelXYZ[3]); /** * @return Is the selected item valid? */ virtual bool isValid() const = 0; virtual void reset(); private: public: virtual AString toString() const; protected: SelectionItemDataTypeEnum::Enum m_itemDataType; bool m_enabledForSelection; Brain* m_brain; double m_screenDepth; double m_screenXYZ[3]; double m_modelXYZ[3]; private: void copyHelperSelectionItem(const SelectionItem& idItem); }; #ifdef __SELECTION_ITEM_DECLARE__ // #endif // __SELECTION_ITEM_DECLARE__ } // namespace #endif //__SELECTION_ITEM__H_ connectome-workbench-1.2.3+git41-gc4c6c90/src/Brain/SelectionItemAnnotation.cxx000066400000000000000000000077471300200146000271410ustar00rootroot00000000000000 /*LICENSE_START*/ /* * Copyright (C) 2015 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_ANNOTATION_DECLARE__ #include "SelectionItemAnnotation.h" #undef __SELECTION_ITEM_ANNOTATION_DECLARE__ #include "AnnotationText.h" #include "CaretAssert.h" using namespace caret; /** * \class caret::SelectionItemAnnotation * \brief Contains information about a selected annotation. * \ingroup Brain */ /** * Constructor. */ SelectionItemAnnotation::SelectionItemAnnotation() : SelectionItem(SelectionItemDataTypeEnum::ANNOTATION) { } /** * Destructor. */ SelectionItemAnnotation::~SelectionItemAnnotation() { } /** * Reset this selection item. */ void SelectionItemAnnotation::reset() { SelectionItem::reset(); /* * Just have pointers to the annotations. * We do not 'own' them. */ m_annotationFile = NULL; m_annotation = NULL; m_sizingHandle = AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_NONE; } /** * @return Is this selected item valid? */ bool SelectionItemAnnotation::isValid() const { return (m_annotation != NULL); } /** * @return The selected annotation. */ Annotation* SelectionItemAnnotation::getAnnotation() const { return m_annotation; } /** * @return The file containing the selected annotation. */ AnnotationFile* SelectionItemAnnotation::getAnnotationFile() const { return m_annotationFile; } /** * @return Sizing handle selected in the selected annotation. */ AnnotationSizingHandleTypeEnum::Enum SelectionItemAnnotation::getSizingHandle() const { return m_sizingHandle; } /** * Add a annotation to the selected annotations. * * @param annotationFile * File containing the annotation. * @param annotation. * Annotation that is added. * @param annotationSizingHandle * Sizing handle that is selected. */ void SelectionItemAnnotation::setAnnotation(AnnotationFile* annotationFile, Annotation* annotation, const AnnotationSizingHandleTypeEnum::Enum annotationSizingHandle) { CaretAssert(annotationFile); CaretAssert(annotation); m_annotationFile = annotationFile; m_annotation = annotation; m_sizingHandle = annotationSizingHandle; } /** * Get a description of m_ object's content. * @return String describing m_ object's content. */ AString SelectionItemAnnotation::toString() const { AString text = SelectionItem::toString(); text += ("Annotation type=" + AnnotationTypeEnum::toGuiName(m_annotation->getType()) + " sizeHandleType=" + AnnotationSizingHandleTypeEnum::toGuiName(m_sizingHandle)); AnnotationText* textAnn = dynamic_cast(m_annotation); if (textAnn != NULL) { text += (" text=" + textAnn->getText()); } // text += ("Surface: " + ((surface != NULL) ? surface->getFileNameNoPath() : "INVALID") + "\n"); // text += ("Border File: " + ((borderFile != NULL) ? borderFile->getFileNameNoPath() : "INVALID") + "\n"); // text += ("Border: " + ((border != NULL) ? border->getName() : "INVALID") + "\n"); // text += ("Border Index: " + AString::number(borderIndex) + "\n"); // text += ("Border Point Index: " + AString::number(borderPointIndex) + "\n"); return text; } connectome-workbench-1.2.3+git41-gc4c6c90/src/Brain/SelectionItemAnnotation.h000066400000000000000000000045061300200146000265540ustar00rootroot00000000000000#ifndef __SELECTION_ITEM_ANNOTATION_H__ #define __SELECTION_ITEM_ANNOTATION_H__ /*LICENSE_START*/ /* * Copyright (C) 2015 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 "AnnotationSizingHandleTypeEnum.h" #include "SelectionItem.h" namespace caret { class Annotation; class AnnotationFile; class SelectionItemAnnotation : public SelectionItem { public: SelectionItemAnnotation(); virtual ~SelectionItemAnnotation(); virtual bool isValid() const; void reset(); virtual AString toString() const; Annotation* getAnnotation() const; AnnotationFile* getAnnotationFile() const; AnnotationSizingHandleTypeEnum::Enum getSizingHandle() const; void setAnnotation(AnnotationFile* annotationFile, Annotation* annotation, const AnnotationSizingHandleTypeEnum::Enum annotationSizingHandle); // ADD_NEW_METHODS_HERE private: SelectionItemAnnotation(const SelectionItemAnnotation&); SelectionItemAnnotation& operator=(const SelectionItemAnnotation&); AnnotationFile* m_annotationFile; Annotation* m_annotation; AnnotationSizingHandleTypeEnum::Enum m_sizingHandle; // ADD_NEW_MEMBERS_HERE }; #ifdef __SELECTION_ITEM_ANNOTATION_DECLARE__ // #endif // __SELECTION_ITEM_ANNOTATION_DECLARE__ } // namespace #endif //__SELECTION_ITEM_ANNOTATION_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Brain/SelectionItemBorderSurface.cxx000066400000000000000000000113671300200146000275460ustar00rootroot00000000000000 /*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 __SELECTION_ITEM_BORDER_SURFACE_DECLARE__ #include "SelectionItemBorderSurface.h" #undef __SELECTION_ITEM_BORDER_SURFACE_DECLARE__ #include "Border.h" #include "BorderFile.h" #include "Surface.h" using namespace caret; /** * \class SelectionItemBorderSurface * \brief Contains information about the selected border. */ /** * Constructor. */ SelectionItemBorderSurface::SelectionItemBorderSurface() : SelectionItem(SelectionItemDataTypeEnum::BORDER_SURFACE) { this->surface = NULL; this->border = NULL; this->borderFile = NULL; this->borderIndex = -1; this->borderPointIndex = -1; } /** * Destructor. */ SelectionItemBorderSurface::~SelectionItemBorderSurface() { } /** * Reset this selection item. */ void SelectionItemBorderSurface::reset() { SelectionItem::reset(); this->surface = NULL; this->border = NULL; this->borderFile = NULL; this->borderIndex = -1; this->borderPointIndex = -1; } /** * @return Is this selected item valid? */ bool SelectionItemBorderSurface::isValid() const { return (this->border != NULL); } /** * @return Surface on which border was drawn. */ const Surface* SelectionItemBorderSurface::getSurface() const { return this->surface; } /** * @return Surface on which border was drawn. */ Surface* SelectionItemBorderSurface::getSurface() { return this->surface; } /** * Set the surface on which border was drawn. * @param surface * New value for surface. */ void SelectionItemBorderSurface::setSurface(Surface* surface) { this->surface = surface; } /** * @return The border that was selected. */ const Border* SelectionItemBorderSurface::getBorder() const { return this->border; } /** * @return The border that was selected. */ Border* SelectionItemBorderSurface::getBorder() { return this->border; } /** * Set the border that was selected. * @param border * New value for border. */ void SelectionItemBorderSurface::setBorder(Border* border) { this->border = border; } /** * @return The border file containing border that was selected. */ const BorderFile* SelectionItemBorderSurface::getBorderFile() const { return this->borderFile; } /** * @return The border file containing border that was selected. */ BorderFile* SelectionItemBorderSurface::getBorderFile() { return this->borderFile; } /** * Set the border file containing border that was selected. * @param borderFile * New value for border file. */ void SelectionItemBorderSurface::setBorderFile(BorderFile* borderFile) { this->borderFile = borderFile; } /** * return Index of selected border. */ int32_t SelectionItemBorderSurface::getBorderIndex() const { return this->borderIndex; } /** * Set index of selected border. * @param borderIndex * New value for border index. */ void SelectionItemBorderSurface::setBorderIndex(const int32_t borderIndex) { this->borderIndex = borderIndex; } /** * return Index of selected border. */ int32_t SelectionItemBorderSurface::getBorderPointIndex() const { return this->borderPointIndex; } /** * Set index of selected border. * @param borderIndex * New value for border index. */ void SelectionItemBorderSurface::setBorderPointIndex(const int32_t borderPointIndex) { this->borderPointIndex = borderPointIndex; } /** * Get a description of m_ object's content. * @return String describing m_ object's content. */ AString SelectionItemBorderSurface::toString() const { AString text = SelectionItem::toString(); text += ("Surface: " + ((surface != NULL) ? surface->getFileNameNoPath() : "INVALID") + "\n"); text += ("Border File: " + ((borderFile != NULL) ? borderFile->getFileNameNoPath() : "INVALID") + "\n"); text += ("Border: " + ((border != NULL) ? border->getName() : "INVALID") + "\n"); text += ("Border Index: " + AString::number(borderIndex) + "\n"); text += ("Border Point Index: " + AString::number(borderPointIndex) + "\n"); return text; } connectome-workbench-1.2.3+git41-gc4c6c90/src/Brain/SelectionItemBorderSurface.h000066400000000000000000000050271300200146000271670ustar00rootroot00000000000000#ifndef __SELECTION_ITEM_BORDER_SURFACE__H_ #define __SELECTION_ITEM_BORDER_SURFACE__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 "SelectionItem.h" namespace caret { class Border; class BorderFile; class Surface; class SelectionItemBorderSurface : public SelectionItem { public: SelectionItemBorderSurface(); virtual ~SelectionItemBorderSurface(); virtual bool isValid() const; Surface* getSurface(); const Surface* getSurface() const; Border* getBorder(); const Border* getBorder() const; void setBorder(Border* border); BorderFile* getBorderFile(); const BorderFile* getBorderFile() const; void setBorderFile(BorderFile* borderFile); void setSurface(Surface* surface); int32_t getBorderIndex() const; void setBorderIndex(const int32_t borderIndex); int32_t getBorderPointIndex() const; void setBorderPointIndex(const int32_t borderPointIndex); void reset(); virtual AString toString() const; private: SelectionItemBorderSurface(const SelectionItemBorderSurface&); SelectionItemBorderSurface& operator=(const SelectionItemBorderSurface&); Border* border; BorderFile* borderFile; Surface* surface; int32_t borderIndex; int32_t borderPointIndex; }; #ifdef __SELECTION_ITEM_BORDER_SURFACE_DECLARE__ // #endif // __SELECTION_ITEM_BORDER_SURFACE_DECLARE__ } // namespace #endif //__SELECTION_ITEM_BORDER_SURFACE__H_ connectome-workbench-1.2.3+git41-gc4c6c90/src/Brain/SelectionItemChartDataSeries.cxx000066400000000000000000000102041300200146000300130ustar00rootroot00000000000000 /*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 __SELECTION_ITEM_CHART_DATA_SERIES_DECLARE__ #include "SelectionItemChartDataSeries.h" #undef __SELECTION_ITEM_CHART_DATA_SERIES_DECLARE__ #include "CaretAssert.h" using namespace caret; /** * \class caret::SelectionItemChartDataSeries * \brief Contains selection of a data-series chart. * \ingroup Brain */ /** * Constructor. */ SelectionItemChartDataSeries::SelectionItemChartDataSeries() : SelectionItem(SelectionItemDataTypeEnum::CHART_DATA_SERIES) { m_chartModelDataSeries = NULL; m_chartDataCartesian = NULL; m_chartDataPointIndex = -1; } /** * Destructor. */ SelectionItemChartDataSeries::~SelectionItemChartDataSeries() { } /** * Copy constructor. * @param obj * Object that is copied. */ SelectionItemChartDataSeries::SelectionItemChartDataSeries(const SelectionItemChartDataSeries& obj) : SelectionItem(obj) { this->copyHelperSelectionItemChartDataSeries(obj); } /** * Assignment operator. * @param obj * Data copied from obj to this. * @return * Reference to this object. */ SelectionItemChartDataSeries& SelectionItemChartDataSeries::operator=(const SelectionItemChartDataSeries& obj) { if (this != &obj) { SelectionItem::operator=(obj); this->copyHelperSelectionItemChartDataSeries(obj); } return *this; } /** * Helps with copying an object of this type. * @param obj * Object that is copied. */ void SelectionItemChartDataSeries::copyHelperSelectionItemChartDataSeries(const SelectionItemChartDataSeries& obj) { m_chartModelDataSeries = obj.m_chartModelDataSeries; m_chartDataCartesian = obj.m_chartDataCartesian; m_chartDataPointIndex = obj.m_chartDataPointIndex; } /** * @return True if the selected chart is valid, else false. */ bool SelectionItemChartDataSeries::isValid() const { if ((m_chartModelDataSeries != NULL) && (m_chartDataCartesian != NULL) && (m_chartDataPointIndex >= 0)) { return true; } return false; } /** * Reset the selections. */ void SelectionItemChartDataSeries::reset() { m_chartModelDataSeries = NULL; m_chartDataCartesian = NULL; m_chartDataPointIndex = -1; } /** * @return */ ChartModelDataSeries* SelectionItemChartDataSeries::getChartModelDataSeries() const { return m_chartModelDataSeries; } /** * @return */ ChartDataCartesian* SelectionItemChartDataSeries::getChartDataCartesian() const { return m_chartDataCartesian; } /** * @return */ int32_t SelectionItemChartDataSeries::getChartDataPointIndex() const { return m_chartDataPointIndex; } /** * Set the selected chart information. * * @param chartModelDataSeries * Data series chart model that was selected. * @param chartDataCartesian * Cartesian chart data that was selected. * @param chartDataPointIndex * Point index of selected chart data. */ void SelectionItemChartDataSeries::setChart(ChartModelDataSeries* chartModelDataSeries, ChartDataCartesian* chartDataCartesian, const int32_t chartDataPointIndex) { CaretAssert(chartModelDataSeries); CaretAssert(chartDataCartesian); CaretAssert(chartDataPointIndex >= 0); m_chartModelDataSeries = chartModelDataSeries; m_chartDataCartesian = chartDataCartesian; m_chartDataPointIndex = chartDataPointIndex; } connectome-workbench-1.2.3+git41-gc4c6c90/src/Brain/SelectionItemChartDataSeries.h000066400000000000000000000046551300200146000274550ustar00rootroot00000000000000#ifndef __SELECTION_ITEM_CHART_DATA_SERIES_H__ #define __SELECTION_ITEM_CHART_DATA_SERIES_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 "SelectionItem.h" namespace caret { class ChartDataCartesian; class ChartModelDataSeries; class SelectionItemChartDataSeries : public SelectionItem { public: SelectionItemChartDataSeries(); virtual ~SelectionItemChartDataSeries(); SelectionItemChartDataSeries(const SelectionItemChartDataSeries& obj); SelectionItemChartDataSeries& operator=(const SelectionItemChartDataSeries& obj); virtual bool isValid() const; virtual void reset(); ChartModelDataSeries* getChartModelDataSeries() const; ChartDataCartesian* getChartDataCartesian() const; int32_t getChartDataPointIndex() const; void setChart(ChartModelDataSeries* chartModelDataSeries, ChartDataCartesian* chartDataCartesian, const int32_t chartDataPointIndex); // ADD_NEW_METHODS_HERE private: void copyHelperSelectionItemChartDataSeries(const SelectionItemChartDataSeries& obj); ChartModelDataSeries* m_chartModelDataSeries; ChartDataCartesian* m_chartDataCartesian; int32_t m_chartDataPointIndex; // ADD_NEW_MEMBERS_HERE }; #ifdef __SELECTION_ITEM_CHART_DATA_SERIES_DECLARE__ // #endif // __SELECTION_ITEM_CHART_DATA_SERIES_DECLARE__ } // namespace #endif //__SELECTION_ITEM_CHART_DATA_SERIES_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Brain/SelectionItemChartFrequencySeries.cxx000066400000000000000000000105211300200146000311050ustar00rootroot00000000000000 /*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 __SELECTION_ITEM_CHART_FREQUENCY_SERIES_DECLARE__ #include "SelectionItemChartFrequencySeries.h" #undef __SELECTION_ITEM_CHART_FREQUENCY_SERIES_DECLARE__ #include "CaretAssert.h" using namespace caret; /** * \class caret::SelectionItemChartFrequencySeries * \brief Contains selection of a data-series chart. * \ingroup Brain */ /** * Constructor. */ SelectionItemChartFrequencySeries::SelectionItemChartFrequencySeries() : SelectionItem(SelectionItemDataTypeEnum::CHART_FREQUENCY_SERIES) { m_chartModelFrequencySeries = NULL; m_chartDataCartesian = NULL; m_chartDataPointIndex = -1; } /** * Destructor. */ SelectionItemChartFrequencySeries::~SelectionItemChartFrequencySeries() { } /** * Copy constructor. * @param obj * Object that is copied. */ SelectionItemChartFrequencySeries::SelectionItemChartFrequencySeries(const SelectionItemChartFrequencySeries& obj) : SelectionItem(obj) { this->copyHelperSelectionItemChartFrequencySeries(obj); } /** * Assignment operator. * @param obj * Data copied from obj to this. * @return * Reference to this object. */ SelectionItemChartFrequencySeries& SelectionItemChartFrequencySeries::operator=(const SelectionItemChartFrequencySeries& obj) { if (this != &obj) { SelectionItem::operator=(obj); this->copyHelperSelectionItemChartFrequencySeries(obj); } return *this; } /** * Helps with copying an object of this type. * @param obj * Object that is copied. */ void SelectionItemChartFrequencySeries::copyHelperSelectionItemChartFrequencySeries(const SelectionItemChartFrequencySeries& obj) { m_chartModelFrequencySeries = obj.m_chartModelFrequencySeries; m_chartDataCartesian = obj.m_chartDataCartesian; m_chartDataPointIndex = obj.m_chartDataPointIndex; } /** * @return True if the selected chart is valid, else false. */ bool SelectionItemChartFrequencySeries::isValid() const { if ((m_chartModelFrequencySeries != NULL) && (m_chartDataCartesian != NULL) && (m_chartDataPointIndex >= 0)) { return true; } return false; } /** * Reset the selections. */ void SelectionItemChartFrequencySeries::reset() { m_chartModelFrequencySeries = NULL; m_chartDataCartesian = NULL; m_chartDataPointIndex = -1; } /** * @return */ ChartModelFrequencySeries* SelectionItemChartFrequencySeries::getChartModelFrequencySeries() const { return m_chartModelFrequencySeries; } /** * @return */ ChartDataCartesian* SelectionItemChartFrequencySeries::getChartDataCartesian() const { return m_chartDataCartesian; } /** * @return */ int32_t SelectionItemChartFrequencySeries::getChartDataPointIndex() const { return m_chartDataPointIndex; } /** * Set the selected chart information. * * @param chartModelFrequencySeries * Frequency series chart model that was selected. * @param chartDataCartesian * Cartesian chart data that was selected. * @param chartDataPointIndex * Point index of selected chart data. */ void SelectionItemChartFrequencySeries::setChart(ChartModelFrequencySeries* chartModelFrequencySeries, ChartDataCartesian* chartDataCartesian, const int32_t chartDataPointIndex) { CaretAssert(chartModelFrequencySeries); CaretAssert(chartDataCartesian); CaretAssert(chartDataPointIndex >= 0); m_chartModelFrequencySeries = chartModelFrequencySeries; m_chartDataCartesian = chartDataCartesian; m_chartDataPointIndex = chartDataPointIndex; } connectome-workbench-1.2.3+git41-gc4c6c90/src/Brain/SelectionItemChartFrequencySeries.h000066400000000000000000000050261300200146000305360ustar00rootroot00000000000000#ifndef __SELECTION_ITEM_CHART_FREQUENCY_SERIES_H__ #define __SELECTION_ITEM_CHART_FREQUENCY_SERIES_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 "SelectionItem.h" namespace caret { class ChartDataCartesian; class ChartModelFrequencySeries; class SelectionItemChartFrequencySeries : public SelectionItem { public: SelectionItemChartFrequencySeries(); virtual ~SelectionItemChartFrequencySeries(); SelectionItemChartFrequencySeries(const SelectionItemChartFrequencySeries& obj); SelectionItemChartFrequencySeries& operator=(const SelectionItemChartFrequencySeries& obj); virtual bool isValid() const; virtual void reset(); ChartModelFrequencySeries* getChartModelFrequencySeries() const; ChartDataCartesian* getChartDataCartesian() const; int32_t getChartDataPointIndex() const; void setChart(ChartModelFrequencySeries* chartModelFrequencySeries, ChartDataCartesian* chartDataCartesian, const int32_t chartDataPointIndex); // ADD_NEW_METHODS_HERE private: void copyHelperSelectionItemChartFrequencySeries(const SelectionItemChartFrequencySeries& obj); ChartModelFrequencySeries* m_chartModelFrequencySeries; ChartDataCartesian* m_chartDataCartesian; int32_t m_chartDataPointIndex; // ADD_NEW_MEMBERS_HERE }; #ifdef __SELECTION_ITEM_CHART_FREQUENCY_SERIES_DECLARE__ // #endif // __SELECTION_ITEM_CHART_FREQUENCY_SERIES_DECLARE__ } // namespace #endif //__SELECTION_ITEM_CHART_FREQUENCY_SERIES_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Brain/SelectionItemChartMatrix.cxx000066400000000000000000000100401300200146000272310ustar00rootroot00000000000000 /*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 __SELECTION_ITEM_CHART_MATRIX_DECLARE__ #include "SelectionItemChartMatrix.h" #undef __SELECTION_ITEM_CHART_MATRIX_DECLARE__ #include "CaretAssert.h" using namespace caret; /** * \class caret::SelectionItemChartMatrix * \brief Contains selection of a data-series chart. * \ingroup Brain */ /** * Constructor. */ SelectionItemChartMatrix::SelectionItemChartMatrix() : SelectionItem(SelectionItemDataTypeEnum::CHART_MATRIX) { m_chartableMatrixInterface = NULL; m_matrixRowIndex = -1; m_matrixColumnIndex = -1; } /** * Destructor. */ SelectionItemChartMatrix::~SelectionItemChartMatrix() { } /** * Copy constructor. * @param obj * Object that is copied. */ SelectionItemChartMatrix::SelectionItemChartMatrix(const SelectionItemChartMatrix& obj) : SelectionItem(obj) { this->copyHelperSelectionItemChartMatrix(obj); } /** * Assignment operator. * @param obj * Data copied from obj to this. * @return * Reference to this object. */ SelectionItemChartMatrix& SelectionItemChartMatrix::operator=(const SelectionItemChartMatrix& obj) { if (this != &obj) { SelectionItem::operator=(obj); this->copyHelperSelectionItemChartMatrix(obj); } return *this; } /** * Helps with copying an object of this type. * @param obj * Object that is copied. */ void SelectionItemChartMatrix::copyHelperSelectionItemChartMatrix(const SelectionItemChartMatrix& obj) { m_chartableMatrixInterface = obj.m_chartableMatrixInterface; m_matrixRowIndex = obj.m_matrixRowIndex; m_matrixColumnIndex = obj.m_matrixColumnIndex; } /** * @return True if the selected chart is valid, else false. */ bool SelectionItemChartMatrix::isValid() const { if ((m_chartableMatrixInterface != NULL) && (m_matrixRowIndex >= 0) && (m_matrixColumnIndex >= 0)) { return true; } return false; } /** * Reset the selections. */ void SelectionItemChartMatrix::reset() { m_chartableMatrixInterface = NULL; m_matrixRowIndex = -1; m_matrixColumnIndex = -1; } /** * @return The Chartable Matrix Interface (file) */ ChartableMatrixInterface* SelectionItemChartMatrix::getChartableMatrixInterface() const { return m_chartableMatrixInterface; } /** * @return Matrix row index. */ int32_t SelectionItemChartMatrix::getMatrixRowIndex() const { return m_matrixRowIndex; } /** * @return Matrix column index. */ int32_t SelectionItemChartMatrix::getMatrixColumnIndex() const { return m_matrixColumnIndex; } /** * Set the selection information. * * @param chartableMatrixInterface * The chartable matrix interface (file) * @param matrixRowIndex * Row index * @param matrixColumnIndex * Column index */ void SelectionItemChartMatrix::setChartMatrix(ChartableMatrixInterface* chartableMatrixInterface, const int32_t matrixRowIndex, const int32_t matrixColumnIndex) { CaretAssert(chartableMatrixInterface); CaretAssert(matrixRowIndex >= 0); CaretAssert(matrixColumnIndex >= 0); m_chartableMatrixInterface = chartableMatrixInterface; m_matrixRowIndex = matrixRowIndex; m_matrixColumnIndex = matrixColumnIndex; } connectome-workbench-1.2.3+git41-gc4c6c90/src/Brain/SelectionItemChartMatrix.h000066400000000000000000000045201300200146000266640ustar00rootroot00000000000000#ifndef __SELECTION_ITEM_CHART_MATRIX_H__ #define __SELECTION_ITEM_CHART_MATRIX_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 "SelectionItem.h" namespace caret { class ChartableMatrixInterface; class SelectionItemChartMatrix : public SelectionItem { public: SelectionItemChartMatrix(); virtual ~SelectionItemChartMatrix(); SelectionItemChartMatrix(const SelectionItemChartMatrix& obj); SelectionItemChartMatrix& operator=(const SelectionItemChartMatrix& obj); virtual bool isValid() const; virtual void reset(); ChartableMatrixInterface* getChartableMatrixInterface() const; int32_t getMatrixRowIndex() const; int32_t getMatrixColumnIndex() const; void setChartMatrix(ChartableMatrixInterface* chartableMatrixInterface, const int32_t matrixRowIndex, const int32_t matrixColumnIndex); // ADD_NEW_METHODS_HERE private: void copyHelperSelectionItemChartMatrix(const SelectionItemChartMatrix& obj); ChartableMatrixInterface* m_chartableMatrixInterface; int32_t m_matrixRowIndex; int32_t m_matrixColumnIndex; // ADD_NEW_MEMBERS_HERE }; #ifdef __SELECTION_ITEM_CHART_MATRIX_DECLARE__ // #endif // __SELECTION_ITEM_CHART_MATRIX_DECLARE__ } // namespace #endif //__SELECTION_ITEM_CHART_MATRIX_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Brain/SelectionItemChartTimeSeries.cxx000066400000000000000000000102041300200146000300400ustar00rootroot00000000000000 /*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 __SELECTION_ITEM_CHART_TIME_SERIES_DECLARE__ #include "SelectionItemChartTimeSeries.h" #undef __SELECTION_ITEM_CHART_TIME_SERIES_DECLARE__ #include "CaretAssert.h" using namespace caret; /** * \class caret::SelectionItemChartTimeSeries * \brief Contains selection of a time-series chart. * \ingroup Brain */ /** * Constructor. */ SelectionItemChartTimeSeries::SelectionItemChartTimeSeries() : SelectionItem(SelectionItemDataTypeEnum::CHART_TIME_SERIES) { m_chartModelTimeSeries = NULL; m_chartDataCartesian = NULL; m_chartDataPointIndex = -1; } /** * Destructor. */ SelectionItemChartTimeSeries::~SelectionItemChartTimeSeries() { } /** * Copy constructor. * @param obj * Object that is copied. */ SelectionItemChartTimeSeries::SelectionItemChartTimeSeries(const SelectionItemChartTimeSeries& obj) : SelectionItem(obj) { this->copyHelperSelectionItemChartTimeSeries(obj); } /** * Assignment operator. * @param obj * Data copied from obj to this. * @return * Reference to this object. */ SelectionItemChartTimeSeries& SelectionItemChartTimeSeries::operator=(const SelectionItemChartTimeSeries& obj) { if (this != &obj) { SelectionItem::operator=(obj); this->copyHelperSelectionItemChartTimeSeries(obj); } return *this; } /** * Helps with copying an object of this type. * @param obj * Object that is copied. */ void SelectionItemChartTimeSeries::copyHelperSelectionItemChartTimeSeries(const SelectionItemChartTimeSeries& obj) { m_chartModelTimeSeries = obj.m_chartModelTimeSeries; m_chartDataCartesian = obj.m_chartDataCartesian; m_chartDataPointIndex = obj.m_chartDataPointIndex; } /** * @return True if the selected chart is valid, else false. */ bool SelectionItemChartTimeSeries::isValid() const { if ((m_chartModelTimeSeries != NULL) && (m_chartDataCartesian != NULL) && (m_chartDataPointIndex >= 0)) { return true; } return false; } /** * Reset the selections. */ void SelectionItemChartTimeSeries::reset() { m_chartModelTimeSeries = NULL; m_chartDataCartesian = NULL; m_chartDataPointIndex = -1; } /** * @return */ ChartModelTimeSeries* SelectionItemChartTimeSeries::getChartModelTimeSeries() const { return m_chartModelTimeSeries; } /** * @return */ ChartDataCartesian* SelectionItemChartTimeSeries::getChartDataCartesian() const { return m_chartDataCartesian; } /** * @return */ int32_t SelectionItemChartTimeSeries::getChartDataPointIndex() const { return m_chartDataPointIndex; } /** * Set the selected chart information. * * @param chartModelTimeSeries * Data series chart model that was selected. * @param chartDataCartesian * Cartesian chart data that was selected. * @param chartDataPointIndex * Point index of selected chart data. */ void SelectionItemChartTimeSeries::setChart(ChartModelTimeSeries* chartModelTimeSeries, ChartDataCartesian* chartDataCartesian, const int32_t chartDataPointIndex) { CaretAssert(chartModelTimeSeries); CaretAssert(chartDataCartesian); CaretAssert(chartDataPointIndex >= 0); m_chartModelTimeSeries = chartModelTimeSeries; m_chartDataCartesian = chartDataCartesian; m_chartDataPointIndex = chartDataPointIndex; } connectome-workbench-1.2.3+git41-gc4c6c90/src/Brain/SelectionItemChartTimeSeries.h000066400000000000000000000046551300200146000275020ustar00rootroot00000000000000#ifndef __SELECTION_ITEM_CHART_TIME_SERIES_H__ #define __SELECTION_ITEM_CHART_TIME_SERIES_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 "SelectionItem.h" namespace caret { class ChartDataCartesian; class ChartModelTimeSeries; class SelectionItemChartTimeSeries : public SelectionItem { public: SelectionItemChartTimeSeries(); virtual ~SelectionItemChartTimeSeries(); SelectionItemChartTimeSeries(const SelectionItemChartTimeSeries& obj); SelectionItemChartTimeSeries& operator=(const SelectionItemChartTimeSeries& obj); virtual bool isValid() const; virtual void reset(); ChartModelTimeSeries* getChartModelTimeSeries() const; ChartDataCartesian* getChartDataCartesian() const; int32_t getChartDataPointIndex() const; void setChart(ChartModelTimeSeries* chartModelTimeSeries, ChartDataCartesian* chartDataCartesian, const int32_t chartDataPointIndex); // ADD_NEW_METHODS_HERE private: void copyHelperSelectionItemChartTimeSeries(const SelectionItemChartTimeSeries& obj); ChartModelTimeSeries* m_chartModelTimeSeries; ChartDataCartesian* m_chartDataCartesian; int32_t m_chartDataPointIndex; // ADD_NEW_MEMBERS_HERE }; #ifdef __SELECTION_ITEM_CHART_TIME_SERIES_DECLARE__ // #endif // __SELECTION_ITEM_CHART_TIME_SERIES_DECLARE__ } // namespace #endif //__SELECTION_ITEM_CHART_TIME_SERIES_H__ SelectionItemCiftiConnectivityMatrixRowColumn.cxx000066400000000000000000000126711300200146000334300ustar00rootroot00000000000000connectome-workbench-1.2.3+git41-gc4c6c90/src/Brain /*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 __SELECTION_ITEM_CIFTI_CONNECTIVITY_MATRIX_ROW_COLUMN_DECLARE__ #include "SelectionItemCiftiConnectivityMatrixRowColumn.h" #undef __SELECTION_ITEM_CIFTI_CONNECTIVITY_MATRIX_ROW_COLUMN_DECLARE__ #include "CaretAssert.h" using namespace caret; /** * \class caret::SelectionItemCiftiConnectivityMatrixRowColumn * \brief Contains selection of a row or column in a CIFTI connectivity matrix file. * \ingroup Brain */ /** * Constructor. */ SelectionItemCiftiConnectivityMatrixRowColumn::SelectionItemCiftiConnectivityMatrixRowColumn() : SelectionItem(SelectionItemDataTypeEnum::CIFTI_CONNECTIVITY_MATRIX_ROW_COLUMN) { m_ciftiConnectivityMatrixFile = NULL; m_matrixRowIndex = -1; m_matrixColumnIndex = -1; } /** * Destructor. */ SelectionItemCiftiConnectivityMatrixRowColumn::~SelectionItemCiftiConnectivityMatrixRowColumn() { } /** * Copy constructor. * @param obj * Object that is copied. */ SelectionItemCiftiConnectivityMatrixRowColumn::SelectionItemCiftiConnectivityMatrixRowColumn(const SelectionItemCiftiConnectivityMatrixRowColumn& obj) : SelectionItem(obj) { this->copyHelperSelectionItemCiftiConnectivityMatrixRowColumn(obj); } /** * Assignment operator. * @param obj * Data copied from obj to this. * @return * Reference to this object. */ SelectionItemCiftiConnectivityMatrixRowColumn& SelectionItemCiftiConnectivityMatrixRowColumn::operator=(const SelectionItemCiftiConnectivityMatrixRowColumn& obj) { if (this != &obj) { SelectionItem::operator=(obj); this->copyHelperSelectionItemCiftiConnectivityMatrixRowColumn(obj); } return *this; } /** * Helps with copying an object of this type. * @param obj * Object that is copied. */ void SelectionItemCiftiConnectivityMatrixRowColumn::copyHelperSelectionItemCiftiConnectivityMatrixRowColumn(const SelectionItemCiftiConnectivityMatrixRowColumn& obj) { m_ciftiConnectivityMatrixFile = obj.m_ciftiConnectivityMatrixFile; m_matrixRowIndex = obj.m_matrixRowIndex; m_matrixColumnIndex = obj.m_matrixColumnIndex; } /** * @return True if the CIFTI connectivity matrix row or column is valid, else false. */ bool SelectionItemCiftiConnectivityMatrixRowColumn::isValid() const { if (m_ciftiConnectivityMatrixFile != NULL) { if ((m_matrixRowIndex >= 0) || (m_matrixColumnIndex >= 0)) { return true; } } return false; } /** * Reset the selections. */ void SelectionItemCiftiConnectivityMatrixRowColumn::reset() { m_ciftiConnectivityMatrixFile = NULL; m_matrixRowIndex = -1; m_matrixColumnIndex = -1; } /** * @return CIFTI connectivity matrix file. */ CiftiMappableConnectivityMatrixDataFile* SelectionItemCiftiConnectivityMatrixRowColumn::getCiftiConnectivityMatrixFile() const { return m_ciftiConnectivityMatrixFile; } /** * @return CIFTI connectivity matrix row index. A negative value * indicates that the row index is invalid. */ int32_t SelectionItemCiftiConnectivityMatrixRowColumn::getMatrixRowIndex() const { return m_matrixRowIndex; } /** * @return CIFTI connectivity matrix column index. A negative value * indicates that the column index is invalid. */ int32_t SelectionItemCiftiConnectivityMatrixRowColumn::getMatrixColumnIndex() const { return m_matrixColumnIndex; } /** * Set the selection to a CIFTI connectivity matrix file row. * * @param ciftiConnectivityMatrixFile * The CIFTI connectivity matrix file * @param matrixRowIndex * Row index */ void SelectionItemCiftiConnectivityMatrixRowColumn::setFileRow(CiftiMappableConnectivityMatrixDataFile* ciftiConnectivityMatrixFile, const int32_t matrixRowIndex) { CaretAssert(ciftiConnectivityMatrixFile); CaretAssert(matrixRowIndex >= 0); m_ciftiConnectivityMatrixFile = ciftiConnectivityMatrixFile; m_matrixRowIndex = matrixRowIndex; m_matrixColumnIndex = -1; } /** * Set the selection to a CIFTI connectivity matrix file column. * * @param ciftiConnectivityMatrixFile * The CIFTI connectivity matrix file * @param matrixColumnIndex * Column index */ void SelectionItemCiftiConnectivityMatrixRowColumn::setFileColumn(CiftiMappableConnectivityMatrixDataFile* ciftiConnectivityMatrixFile, const int32_t matrixColumnIndex) { CaretAssert(ciftiConnectivityMatrixFile); CaretAssert(matrixColumnIndex >= 0); m_ciftiConnectivityMatrixFile = ciftiConnectivityMatrixFile; m_matrixRowIndex = -1; m_matrixColumnIndex = matrixColumnIndex; } connectome-workbench-1.2.3+git41-gc4c6c90/src/Brain/SelectionItemCiftiConnectivityMatrixRowColumn.h000066400000000000000000000055201300200146000331270ustar00rootroot00000000000000#ifndef __SELECTION_ITEM_CIFTI_CONNECTIVITY_MATRIX_ROW_COLUMN_H__ #define __SELECTION_ITEM_CIFTI_CONNECTIVITY_MATRIX_ROW_COLUMN_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 "SelectionItem.h" namespace caret { class CiftiMappableConnectivityMatrixDataFile; class SelectionItemCiftiConnectivityMatrixRowColumn : public SelectionItem { public: SelectionItemCiftiConnectivityMatrixRowColumn(); virtual ~SelectionItemCiftiConnectivityMatrixRowColumn(); SelectionItemCiftiConnectivityMatrixRowColumn(const SelectionItemCiftiConnectivityMatrixRowColumn& obj); SelectionItemCiftiConnectivityMatrixRowColumn& operator=(const SelectionItemCiftiConnectivityMatrixRowColumn& obj); virtual bool isValid() const; virtual void reset(); CiftiMappableConnectivityMatrixDataFile* getCiftiConnectivityMatrixFile() const; int32_t getMatrixRowIndex() const; int32_t getMatrixColumnIndex() const; void setFileRow(CiftiMappableConnectivityMatrixDataFile* ciftiConnectivityMatrixFile, const int32_t matrixRowIndex); void setFileColumn(CiftiMappableConnectivityMatrixDataFile* ciftiConnectivityMatrixFile, const int32_t matrixColumnIndex); // ADD_NEW_METHODS_HERE private: void copyHelperSelectionItemCiftiConnectivityMatrixRowColumn(const SelectionItemCiftiConnectivityMatrixRowColumn& obj); CiftiMappableConnectivityMatrixDataFile* m_ciftiConnectivityMatrixFile; int32_t m_matrixRowIndex; int32_t m_matrixColumnIndex; // ADD_NEW_MEMBERS_HERE }; #ifdef __SELECTION_ITEM_CIFTI_CONNECTIVITY_MATRIX_ROW_COLUMN_DECLARE__ // #endif // __SELECTION_ITEM_CIFTI_CONNECTIVITY_MATRIX_ROW_COLUMN_DECLARE__ } // namespace #endif //__SELECTION_ITEM_CIFTI_CONNECTIVITY_MATRIX_ROW_COLUMN_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Brain/SelectionItemDataTypeEnum.cxx000066400000000000000000000307361300200146000273610ustar00rootroot00000000000000 /*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 __SELECTION_ITEM_DATA_TYPE_ENUM_DECLARE__ #include "SelectionItemDataTypeEnum.h" #undef __SELECTION_ITEM_DATA_TYPE_ENUM_DECLARE__ #include "CaretAssert.h" using namespace caret; /** * Constructor. * * @param enumValue * An enumerated value. * @param name * Name of enumerated value. * * @param guiName * User-friendly name for use in user-interface. */ SelectionItemDataTypeEnum::SelectionItemDataTypeEnum(const Enum enumValue, const AString& name, const AString& guiName) { this->enumValue = enumValue; this->integerCode = integerCodeCounter++; this->name = name; this->guiName = guiName; } /** * Destructor. */ SelectionItemDataTypeEnum::~SelectionItemDataTypeEnum() { } /** * Initialize the enumerated metadata. */ void SelectionItemDataTypeEnum::initialize() { if (initializedFlag) { return; } initializedFlag = true; enumData.push_back(SelectionItemDataTypeEnum(INVALID, "INVALID", "Invalid")); enumData.push_back(SelectionItemDataTypeEnum(ANNOTATION, "ANNOTATION", "Annotation")); enumData.push_back(SelectionItemDataTypeEnum(BORDER_SURFACE, "BORDER_SURFACE", "Surface Border")); enumData.push_back(SelectionItemDataTypeEnum(BORDER_VOLUME, "BORDER_VOLUME", "Volume Border")); enumData.push_back(SelectionItemDataTypeEnum(CHART_DATA_SERIES, "CHART_DATA_SERIES", "Data-Series Chart")); enumData.push_back(SelectionItemDataTypeEnum(CHART_FREQUENCY_SERIES, "CHART_FREQUENCY_SERIES", "Frequency-Series Chart")); enumData.push_back(SelectionItemDataTypeEnum(CHART_MATRIX, "CHART_MATRIX", "Matrix Chart")); enumData.push_back(SelectionItemDataTypeEnum(CHART_TIME_SERIES, "CHART_TIME_SERIES", "Time-Series Chart")); enumData.push_back(SelectionItemDataTypeEnum(CIFTI_CONNECTIVITY_MATRIX_ROW_COLUMN, "CIFTI_CONNECTIVITY_MATRIX_ROW_COLUMN", "CIFTI Connectivity Row or Column")); enumData.push_back(SelectionItemDataTypeEnum(FOCUS_SURFACE, "FOCUS_SURFACE", "Surface Focus")); enumData.push_back(SelectionItemDataTypeEnum(FOCUS_VOLUME, "FOCUS_VOLUME", "Volume Focus")); enumData.push_back(SelectionItemDataTypeEnum(IMAGE, "IMAGE", "Image")); enumData.push_back(SelectionItemDataTypeEnum(IMAGE_CONTROL_POINT, "IMAGE_CONTROL_POINT", "Image Control Point")); enumData.push_back(SelectionItemDataTypeEnum(SURFACE_NODE, "SURFACE_NODE", "Surface Vertex")); enumData.push_back(SelectionItemDataTypeEnum(SURFACE_NODE_IDENTIFICATION_SYMBOL, "SURFACE_NODE_IDENTIFICATION_SYMBOL", "Surface Vertex Identification Symbol")); enumData.push_back(SelectionItemDataTypeEnum(SURFACE_TRIANGLE, "SURFACE_TRIANGLE", "Surface Triangle")); enumData.push_back(SelectionItemDataTypeEnum(VOXEL, "VOXEL", "Voxel")); enumData.push_back(SelectionItemDataTypeEnum(VOXEL_EDITING, "VOXEL_EDITING", "Voxel Editing")); enumData.push_back(SelectionItemDataTypeEnum(VOXEL_IDENTIFICATION_SYMBOL, "VOXEL_IDENTIFICATION_SYMBOL", "Voxel Identification Symbol")); } /** * 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 SelectionItemDataTypeEnum* SelectionItemDataTypeEnum::findData(const Enum enumValue) { if (initializedFlag == false) initialize(); size_t num = enumData.size(); for (size_t i = 0; i < num; i++) { const SelectionItemDataTypeEnum* 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 SelectionItemDataTypeEnum::toName(Enum enumValue) { if (initializedFlag == false) initialize(); const SelectionItemDataTypeEnum* 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. */ SelectionItemDataTypeEnum::Enum SelectionItemDataTypeEnum::fromName(const AString& name, bool* isValidOut) { if (initializedFlag == false) initialize(); bool validFlag = false; Enum enumValue = INVALID; for (std::vector::iterator iter = enumData.begin(); iter != enumData.end(); iter++) { const SelectionItemDataTypeEnum& 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 SelectionItemDataTypeEnum")); } return enumValue; } /** * Get a GUI string representation of the enumerated type. * @param enumValue * Enumerated value. * @return * String representing enumerated value. */ AString SelectionItemDataTypeEnum::toGuiName(Enum enumValue) { if (initializedFlag == false) initialize(); const SelectionItemDataTypeEnum* enumInstance = findData(enumValue); return enumInstance->guiName; } /** * Get an enumerated value corresponding to its GUI name. * @param guiName * 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. */ SelectionItemDataTypeEnum::Enum SelectionItemDataTypeEnum::fromGuiName(const AString& guiName, bool* isValidOut) { if (initializedFlag == false) initialize(); bool validFlag = false; Enum enumValue = INVALID; for (std::vector::iterator iter = enumData.begin(); iter != enumData.end(); iter++) { const SelectionItemDataTypeEnum& 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 SelectionItemDataTypeEnum")); } return enumValue; } /** * Get the integer code for a data type. * * @return * Integer code for data type. */ int32_t SelectionItemDataTypeEnum::toIntegerCode(Enum enumValue) { if (initializedFlag == false) initialize(); const SelectionItemDataTypeEnum* 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. */ SelectionItemDataTypeEnum::Enum SelectionItemDataTypeEnum::fromIntegerCode(const int32_t integerCode, bool* isValidOut) { if (initializedFlag == false) initialize(); bool validFlag = false; Enum enumValue = INVALID; for (std::vector::iterator iter = enumData.begin(); iter != enumData.end(); iter++) { const SelectionItemDataTypeEnum& 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 SelectionItemDataTypeEnum")); } 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 SelectionItemDataTypeEnum::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 SelectionItemDataTypeEnum::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(SelectionItemDataTypeEnum::toName(iter->enumValue)); } if (isSorted) { std::sort(allNames.begin(), allNames.end()); } } /** * Get all of the GUI names of the enumerated type values. * * @param allGuiNames * 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 SelectionItemDataTypeEnum::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(SelectionItemDataTypeEnum::toGuiName(iter->enumValue)); } if (isSorted) { std::sort(allGuiNames.begin(), allGuiNames.end()); } } connectome-workbench-1.2.3+git41-gc4c6c90/src/Brain/SelectionItemDataTypeEnum.h000066400000000000000000000104431300200146000267770ustar00rootroot00000000000000#ifndef __SELECTION_ITEM_DATA_TYPE_ENUM__H_ #define __SELECTION_ITEM_DATA_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 SelectionItemDataTypeEnum * \brief Enumerated type for selected items * * Enumerated data type for selected items. */ class SelectionItemDataTypeEnum { public: /** * Enumerated values. */ enum Enum { /** Invalid */ INVALID, /** Annotation */ ANNOTATION, /** Border on Surface */ BORDER_SURFACE, /** Border on Volume Slice */ BORDER_VOLUME, /** Data-Series Chart */ CHART_DATA_SERIES, /** Frequency-Series Chart */ CHART_FREQUENCY_SERIES, /** Matrix chart */ CHART_MATRIX, /** Time-Series Chart */ CHART_TIME_SERIES, /** CIFTI Connectivity Matrix Row or Column */ CIFTI_CONNECTIVITY_MATRIX_ROW_COLUMN, /** Focus on Surface */ FOCUS_SURFACE, /** Focus on Volume */ FOCUS_VOLUME, /* Image */ IMAGE, /* Image Control Point */ IMAGE_CONTROL_POINT, /** Surface Node*/ SURFACE_NODE, /** Surface Node Identification Symbol */ SURFACE_NODE_IDENTIFICATION_SYMBOL, /** Surface Triangle */ SURFACE_TRIANGLE, /** Volume Voxel */ VOXEL, /** Voxel Editing */ VOXEL_EDITING, /** Voxel identification symbol */ VOXEL_IDENTIFICATION_SYMBOL }; ~SelectionItemDataTypeEnum(); 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: SelectionItemDataTypeEnum(const Enum enumValue, const AString& name, const AString& guiName); static const SelectionItemDataTypeEnum* 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 __SELECTION_ITEM_DATA_TYPE_ENUM_DECLARE__ std::vector SelectionItemDataTypeEnum::enumData; bool SelectionItemDataTypeEnum::initializedFlag = false; int32_t SelectionItemDataTypeEnum::integerCodeCounter = 0; #endif // __SELECTION_ITEM_DATA_TYPE_ENUM_DECLARE__ } // namespace #endif //__SELECTION_ITEM_DATA_TYPE_ENUM__H_ connectome-workbench-1.2.3+git41-gc4c6c90/src/Brain/SelectionItemFocusSurface.cxx000066400000000000000000000113151300200146000274010ustar00rootroot00000000000000 /*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 __SELECTION_ITEM_FOCUS_SURFACE_DECLARE__ #include "SelectionItemFocusSurface.h" #undef __SELECTION_ITEM_FOCUS_SURFACE_DECLARE__ #include "FociFile.h" #include "Focus.h" #include "Surface.h" using namespace caret; /** * \class SelectionItemFocusSurface * \brief Contains information about the selected focus. */ /** * Constructor. */ SelectionItemFocusSurface::SelectionItemFocusSurface() : SelectionItem(SelectionItemDataTypeEnum::FOCUS_SURFACE) { this->surface = NULL; this->focus = NULL; this->fociFile = NULL; this->focusIndex = -1; this->focusProjectionIndex = -1; } /** * Destructor. */ SelectionItemFocusSurface::~SelectionItemFocusSurface() { } /** * Reset this selection item. */ void SelectionItemFocusSurface::reset() { SelectionItem::reset(); this->surface = NULL; this->focus = NULL; this->fociFile = NULL; this->focusIndex = -1; this->focusProjectionIndex = -1; } /** * @return Is this selected item valid? */ bool SelectionItemFocusSurface::isValid() const { return (this->focus != NULL); } /** * @return Surface on which focus was drawn. */ const Surface* SelectionItemFocusSurface::getSurface() const { return this->surface; } /** * @return Surface on which focus was drawn. */ Surface* SelectionItemFocusSurface::getSurface() { return this->surface; } /** * Set the surface on which focus was drawn. * @param surface * New value for surface. */ void SelectionItemFocusSurface::setSurface(Surface* surface) { this->surface = surface; } /** * @return The focus that was selected. */ const Focus* SelectionItemFocusSurface::getFocus() const { return this->focus; } /** * @return The focus that was selected. */ Focus* SelectionItemFocusSurface::getFocus() { return this->focus; } /** * Set the focus that was selected. * @param focus * New value for focus. */ void SelectionItemFocusSurface::setFocus(Focus* focus) { this->focus = focus; } /** * @return The focus file containing focus that was selected. */ const FociFile* SelectionItemFocusSurface::getFociFile() const { return this->fociFile; } /** * @return The focus file containing focus that was selected. */ FociFile* SelectionItemFocusSurface::getFociFile() { return this->fociFile; } /** * Set the focus file containing focus that was selected. * @param fociFile * New value for focus file. */ void SelectionItemFocusSurface::setFociFile(FociFile* fociFile) { this->fociFile = fociFile; } /** * return Index of selected focus. */ int32_t SelectionItemFocusSurface::getFocusIndex() const { return this->focusIndex; } /** * Set index of selected focus. * @param focusIndex * New value for focus index. */ void SelectionItemFocusSurface::setFocusIndex(const int32_t focusIndex) { this->focusIndex = focusIndex; } /** * return Projection Index of selected focus. */ int32_t SelectionItemFocusSurface::getFocusProjectionIndex() const { return this->focusProjectionIndex; } /** * Set projection index of selected focus. * @param focusProjectionIndex * New value for focus index. */ void SelectionItemFocusSurface::setFocusProjectionIndex(const int32_t focusProjectionIndex) { this->focusProjectionIndex = focusProjectionIndex; } /** * Get a description of m_ object's content. * @return String describing m_ object's content. */ AString SelectionItemFocusSurface::toString() const { AString text = SelectionItem::toString(); text += ("Surface: " + ((surface != NULL) ? surface->getFileNameNoPath() : "INVALID") + "\n"); text += ("Foci File: " + ((fociFile != NULL) ? fociFile->getFileNameNoPath() : "INVALID") + "\n"); text += ("Focus: " + ((focus != NULL) ? focus->getName() : "INVALID") + "\n"); text += ("Focus Index: " + AString::number(focusIndex) + "\n"); text += ("Focus Projection Index: " + AString::number(focusProjectionIndex) + "\n"); return text; } connectome-workbench-1.2.3+git41-gc4c6c90/src/Brain/SelectionItemFocusSurface.h000066400000000000000000000047571300200146000270420ustar00rootroot00000000000000#ifndef __SELECTION_ITEM_FOCUS_SURFACE__H_ #define __SELECTION_ITEM_FOCUS_SURFACE__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 "SelectionItem.h" namespace caret { class Focus; class FociFile; class Surface; class SelectionItemFocusSurface : public SelectionItem { public: SelectionItemFocusSurface(); virtual ~SelectionItemFocusSurface(); virtual bool isValid() const; Surface* getSurface(); const Surface* getSurface() const; Focus* getFocus(); const Focus* getFocus() const; void setFocus(Focus* focus); FociFile* getFociFile(); const FociFile* getFociFile() const; void setFociFile(FociFile* fociFile); void setSurface(Surface* surface); int32_t getFocusIndex() const; void setFocusIndex(const int32_t focusIndex); int32_t getFocusProjectionIndex() const; void setFocusProjectionIndex(const int32_t focusIndex); void reset(); virtual AString toString() const; private: SelectionItemFocusSurface(const SelectionItemFocusSurface&); SelectionItemFocusSurface& operator=(const SelectionItemFocusSurface&); Focus* focus; FociFile* fociFile; Surface* surface; int32_t focusIndex; int32_t focusProjectionIndex; }; #ifdef __SELECTION_ITEM_FOCUS_SURFACE_DECLARE__ // #endif // __SELECTION_ITEM_FOCUS_SURFACE_DECLARE__ } // namespace #endif //__SELECTION_ITEM_FOCUS_SURFACE__H_ connectome-workbench-1.2.3+git41-gc4c6c90/src/Brain/SelectionItemFocusVolume.cxx000066400000000000000000000117211300200146000272610ustar00rootroot00000000000000 /*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 __SELECTION_ITEM_FOCUS_VOLUME_DECLARE__ #include "SelectionItemFocusVolume.h" #undef __SELECTION_ITEM_FOCUS_VOLUME_DECLARE__ #include "Focus.h" #include "FociFile.h" #include "VolumeFile.h" using namespace caret; /** * \class SelectionItemFocusVolume * \brief Contains information about the selected focus. */ /** * Constructor. */ SelectionItemFocusVolume::SelectionItemFocusVolume() : SelectionItem(SelectionItemDataTypeEnum::FOCUS_VOLUME) { this->volumeFile = NULL; this->focus = NULL; this->fociFile = NULL; this->focusIndex = -1; this->focusProjectionIndex = -1; } /** * Destructor. */ SelectionItemFocusVolume::~SelectionItemFocusVolume() { } /** * Reset this selection item. */ void SelectionItemFocusVolume::reset() { SelectionItem::reset(); this->volumeFile = NULL; this->focus = NULL; this->fociFile = NULL; this->focusIndex = -1; this->focusProjectionIndex = -1; } /** * @return Is this selected item valid? */ bool SelectionItemFocusVolume::isValid() const { return (this->focus != NULL); } /** * @return VolumeFile on which focus was drawn. */ const VolumeMappableInterface* SelectionItemFocusVolume::getVolumeFile() const { return this->volumeFile; } /** * @return VolumeFile on which focus was drawn. */ VolumeMappableInterface* SelectionItemFocusVolume::getVolumeFile() { return this->volumeFile; } /** * Set the volume file on which focus was drawn. * @param volumeFile * New value for volumeFile. */ void SelectionItemFocusVolume::setVolumeFile(VolumeMappableInterface* volumeFile) { this->volumeFile = volumeFile; } /** * @return The focus that was selected. */ const Focus* SelectionItemFocusVolume::getFocus() const { return this->focus; } /** * @return The focus that was selected. */ Focus* SelectionItemFocusVolume::getFocus() { return this->focus; } /** * Set the focus that was selected. * @param focus * New value for focus. */ void SelectionItemFocusVolume::setFocus(Focus* focus) { this->focus = focus; } /** * @return The focus file containing focus that was selected. */ const FociFile* SelectionItemFocusVolume::getFociFile() const { return this->fociFile; } /** * @return The focus file containing focus that was selected. */ FociFile* SelectionItemFocusVolume::getFociFile() { return this->fociFile; } /** * Set the focus file containing focus that was selected. * @param fociFile * New value for focus file. */ void SelectionItemFocusVolume::setFociFile(FociFile* fociFile) { this->fociFile = fociFile; } /** * return Index of selected focus. */ int32_t SelectionItemFocusVolume::getFocusIndex() const { return this->focusIndex; } /** * Set index of selected focus. * @param focusIndex * New value for focus index. */ void SelectionItemFocusVolume::setFocusIndex(const int32_t focusIndex) { this->focusIndex = focusIndex; } /** * return Projection Index of selected focus. */ int32_t SelectionItemFocusVolume::getFocusProjectionIndex() const { return this->focusProjectionIndex; } /** * Set projection index of selected focus. * @param focusProjectionIndex * New value for focus index. */ void SelectionItemFocusVolume::setFocusProjectionIndex(const int32_t focusProjectionIndex) { this->focusProjectionIndex = focusProjectionIndex; } /** * Get a description of m_ object's content. * @return String describing m_ object's content. */ AString SelectionItemFocusVolume::toString() const { AString name = "INVALID"; if (volumeFile != NULL) { CaretMappableDataFile* cmdf = dynamic_cast(volumeFile); if (cmdf != NULL) { name = cmdf->getFileNameNoPath(); } } AString text = SelectionItem::toString(); text += ("Volume File: " + name + "\n"); text += ("Foci File: " + ((fociFile != NULL) ? fociFile->getFileNameNoPath() : "INVALID") + "\n"); text += ("Focus: " + ((focus != NULL) ? focus->getName() : "INVALID") + "\n"); text += ("Focus Index: " + AString::number(focusIndex) + "\n"); text += ("Focus Projection Index: " + AString::number(focusProjectionIndex) + "\n"); return text; } connectome-workbench-1.2.3+git41-gc4c6c90/src/Brain/SelectionItemFocusVolume.h000066400000000000000000000051011300200146000267010ustar00rootroot00000000000000#ifndef __SELECTION_ITEM_FOCUS_VOLUME__H_ #define __SELECTION_ITEM_FOCUS_VOLUME__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 "SelectionItem.h" namespace caret { class Focus; class FociFile; class VolumeMappableInterface; class SelectionItemFocusVolume : public SelectionItem { public: SelectionItemFocusVolume(); virtual ~SelectionItemFocusVolume(); virtual bool isValid() const; VolumeMappableInterface* getVolumeFile(); const VolumeMappableInterface* getVolumeFile() const; Focus* getFocus(); const Focus* getFocus() const; void setFocus(Focus* focus); FociFile* getFociFile(); const FociFile* getFociFile() const; void setFociFile(FociFile* fociFile); void setVolumeFile(VolumeMappableInterface* volumeFile); int32_t getFocusIndex() const; void setFocusIndex(const int32_t focusIndex); int32_t getFocusProjectionIndex() const; void setFocusProjectionIndex(const int32_t focusIndex); void reset(); virtual AString toString() const; private: SelectionItemFocusVolume(const SelectionItemFocusVolume&); SelectionItemFocusVolume& operator=(const SelectionItemFocusVolume&); Focus* focus; FociFile* fociFile; VolumeMappableInterface* volumeFile; int32_t focusIndex; int32_t focusProjectionIndex; }; #ifdef __SELECTION_ITEM_FOCUS_VOLUME_DECLARE__ // #endif // __SELECTION_ITEM_FOCUS_VOLUME_DECLARE__ } // namespace #endif //__SELECTION_ITEM_FOCUS_VOLUME__H_ connectome-workbench-1.2.3+git41-gc4c6c90/src/Brain/SelectionItemImage.cxx000066400000000000000000000072501300200146000260360ustar00rootroot00000000000000 /*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 __SELECTION_ITEM_IMAGE_DECLARE__ #include "SelectionItemImage.h" #undef __SELECTION_ITEM_IMAGE_DECLARE__ #include "ImageFile.h" using namespace caret; /** * \class SelectionItemImage * \brief Contains information about the selected image. */ /** * Constructor. */ SelectionItemImage::SelectionItemImage() : SelectionItem(SelectionItemDataTypeEnum::IMAGE) { m_imageFile = NULL; m_pixelI = -1; m_pixelJ = -1; m_pixelRGBA[0] = 0; m_pixelRGBA[1] = 0; m_pixelRGBA[2] = 0; m_pixelRGBA[3] = 0; } /** * Destructor. */ SelectionItemImage::~SelectionItemImage() { } /** * @return Pixel I. */ int32_t SelectionItemImage::getPixelI() const { return m_pixelI; } /** * @return Pixel J. */ int32_t SelectionItemImage::getPixelJ() const { return m_pixelJ; } /** * Set pixel I * * @param i * Value for I. */ void SelectionItemImage::setPixelI(const int32_t i) { m_pixelI = i; } /** * Set pixel J * * @param j * Value for J. */ void SelectionItemImage::setPixelJ(const int32_t j) { m_pixelJ = j; } /** * Get the pixel RGBA value. * * @param pixelRGBAOut * Output containing pixel RGBA. */ void SelectionItemImage::getPixelRGBA(uint8_t pixelRGBAOut[4]) const { pixelRGBAOut[0] = m_pixelRGBA[0]; pixelRGBAOut[1] = m_pixelRGBA[1]; pixelRGBAOut[2] = m_pixelRGBA[2]; pixelRGBAOut[3] = m_pixelRGBA[3]; } /** * Set the pixel RGBA value. * * @param pixelRGBA * Pixel RGBA. */ void SelectionItemImage::setPixelRGBA(const uint8_t pixelRGBA[4]) { m_pixelRGBA[0] = pixelRGBA[0]; m_pixelRGBA[1] = pixelRGBA[1]; m_pixelRGBA[2] = pixelRGBA[2]; m_pixelRGBA[3] = pixelRGBA[3]; } /** * Reset this selection item. */ void SelectionItemImage::reset() { SelectionItem::reset(); m_imageFile = NULL; m_pixelI = -1; m_pixelJ = -1; m_pixelRGBA[0] = 0; m_pixelRGBA[1] = 0; m_pixelRGBA[2] = 0; m_pixelRGBA[3] = 0; } /** * @return Is this selected item valid? */ bool SelectionItemImage::isValid() const { return (m_imageFile != NULL); } /** * @return Image that was selected (NULL if not valid). */ const ImageFile* SelectionItemImage::getImageFile() const { return m_imageFile; } /** * @return Image that was selected (NULL if not valid). */ ImageFile* SelectionItemImage::getImageFile() { return m_imageFile; } /** * Set the image that was selected. * * @param imageFile * Pointer to selected image (NULL if not valid). */ void SelectionItemImage::setImageFile(ImageFile* imageFile) { m_imageFile = imageFile; } /** * Get a description of m_ object's content. * @return String describing m_ object's content. */ AString SelectionItemImage::toString() const { AString text = SelectionItem::toString(); text += ("ImageFile: " + ((m_imageFile != NULL) ? m_imageFile->getFileNameNoPath() : "INVALID") + "\n"); return text; } connectome-workbench-1.2.3+git41-gc4c6c90/src/Brain/SelectionItemImage.h000066400000000000000000000042761300200146000254700ustar00rootroot00000000000000#ifndef __SELECTION_ITEM_IMAGE__H_ #define __SELECTION_ITEM_IMAGE__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 "SelectionItem.h" namespace caret { class ImageFile; class SelectionItemImage : public SelectionItem { public: SelectionItemImage(); virtual ~SelectionItemImage(); virtual bool isValid() const; ImageFile* getImageFile(); const ImageFile* getImageFile() const; void setImageFile(ImageFile* imageFile); int32_t getPixelI() const; int32_t getPixelJ() const; void setPixelI(const int32_t i); void setPixelJ(const int32_t j); void getPixelRGBA(uint8_t pixelRGBAOut[4]) const; void setPixelRGBA(const uint8_t pixelRGBA[4]); virtual void reset(); virtual AString toString() const; private: SelectionItemImage(const SelectionItemImage&); SelectionItemImage& operator=(const SelectionItemImage&); ImageFile* m_imageFile; int32_t m_pixelI; int32_t m_pixelJ; uint8_t m_pixelRGBA[4]; }; #ifdef __SELECTION_ITEM_IMAGE_DECLARE__ // #endif // __SELECTION_ITEM_IMAGE_DECLARE__ } // namespace #endif //__SELECTION_ITEM_IMAGE__H_ connectome-workbench-1.2.3+git41-gc4c6c90/src/Brain/SelectionItemImageControlPoint.cxx000066400000000000000000000120361300200146000304070ustar00rootroot00000000000000 /*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 __SELECTION_ITEM_IMAGE_CONTROL_POINT_DECLARE__ #include "SelectionItemImageControlPoint.h" #undef __SELECTION_ITEM_IMAGE_CONTROL_POINT_DECLARE__ #include "ControlPointFile.h" #include "ImageFile.h" using namespace caret; /** * \class SelectionItemImageControlPoint * \brief Contains information about the selected control point. */ /** * Constructor. */ SelectionItemImageControlPoint::SelectionItemImageControlPoint() : SelectionItem(SelectionItemDataTypeEnum::IMAGE_CONTROL_POINT) { m_imageFile = NULL; m_controlPointFile = NULL; m_controlPoint = NULL; m_controlPointIndexInFile = -1; } /** * Destructor. */ SelectionItemImageControlPoint::~SelectionItemImageControlPoint() { } /** * Reset this selection item. */ void SelectionItemImageControlPoint::reset() { SelectionItem::reset(); m_imageFile = NULL; m_controlPointFile = NULL; m_controlPoint = NULL; m_controlPointIndexInFile = -1; } /** * @return Is this selected item valid? */ bool SelectionItemImageControlPoint::isValid() const { if (m_imageFile == NULL) { return false; } if (m_controlPointFile == NULL) { return false; } if (m_controlPoint == NULL) { return false; } if ((m_controlPointIndexInFile < 0) || (m_controlPointIndexInFile >= m_controlPointFile->getNumberOfControlPoints())) { return false; } return true; } /** * @return Image File that was selected (NULL if not valid). */ const ImageFile* SelectionItemImageControlPoint::getImageFile() const { return m_imageFile; } /** * @return Image File that was selected (NULL if not valid). */ ImageFile* SelectionItemImageControlPoint::getImageFile() { return m_imageFile; } /** * Set the image file that was selected. * * @param imageFile * Pointer to selected image file (NULL if not valid). */ void SelectionItemImageControlPoint::setImageFile(ImageFile* imageFile) { m_imageFile = imageFile; } /** * @return Control point that was selected (NULL if not valid). */ const ControlPointFile* SelectionItemImageControlPoint::getControlPointFile() const { return m_controlPointFile; } /** * @return Control point that was selected (NULL if not valid). */ ControlPointFile* SelectionItemImageControlPoint::getControlPointFile() { return m_controlPointFile; } /** * Set the control point file that was selected. * * @param controlPointFile * Pointer to selected control point file (NULL if not valid). */ void SelectionItemImageControlPoint::setControlPointFile(ControlPointFile* controlPointFile) { m_controlPointFile = controlPointFile; } /** * @return Index of the control point in the control point file (negative if invalid) */ int32_t SelectionItemImageControlPoint::getControlPointIndexInFile() const { return m_controlPointIndexInFile; } /** * Set the index of the control point in the file. * * @param controlPointIndexInFile * Index of the control point in the file. */ void SelectionItemImageControlPoint::setControlPointIndexInFile(const int32_t controlPointIndexInFile) { m_controlPointIndexInFile = controlPointIndexInFile; } /** * @return Pointer to the control point (NULL if invalid) */ ControlPoint3D* SelectionItemImageControlPoint::getControlPoint() { return m_controlPoint; } /** * @return Pointer to the control point (NULL if invalid) */ const ControlPoint3D* SelectionItemImageControlPoint::getControlPoint() const { return m_controlPoint; } /** * Set the control point. * * @param controlPoint * Pointer to the control point. */ void SelectionItemImageControlPoint::setControlPoint(ControlPoint3D* controlPoint) { m_controlPoint = controlPoint; } /** * Get a description of m_ object's content. * @return String describing m_ object's content. */ AString SelectionItemImageControlPoint::toString() const { AString text = SelectionItem::toString(); text += ("ImageFile: " + ((m_imageFile != NULL) ? m_imageFile->getFileNameNoPath() : "INVALID") + "\n"); text += ("ControlPointFile: " + ((m_imageFile != NULL) ? m_controlPointFile->getFileNameNoPath() : "INVALID") + "\n"); text += ("Index in File: " + AString::number(m_controlPointIndexInFile) + "\n"); return text; } connectome-workbench-1.2.3+git41-gc4c6c90/src/Brain/SelectionItemImageControlPoint.h000066400000000000000000000052071300200146000300360ustar00rootroot00000000000000#ifndef __SELECTION_ITEM_IMAGE_CONTROL_POINT_H_ #define __SELECTION_ITEM_IMAGE_CONTROL_POINT_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 "SelectionItem.h" namespace caret { class ControlPointFile; class ControlPoint3D; class ImageFile; class SelectionItemImageControlPoint : public SelectionItem { public: SelectionItemImageControlPoint(); virtual ~SelectionItemImageControlPoint(); virtual bool isValid() const; ImageFile* getImageFile(); const ImageFile* getImageFile() const; void setImageFile(ImageFile* imageFile); ControlPointFile* getControlPointFile(); const ControlPointFile* getControlPointFile() const; void setControlPointFile(ControlPointFile* imageFile); int32_t getControlPointIndexInFile() const; void setControlPointIndexInFile(const int32_t controlPointIndexInFile); ControlPoint3D* getControlPoint(); const ControlPoint3D* getControlPoint() const; void setControlPoint(ControlPoint3D* controlPoint); virtual void reset(); virtual AString toString() const; private: SelectionItemImageControlPoint(const SelectionItemImageControlPoint&); SelectionItemImageControlPoint& operator=(const SelectionItemImageControlPoint&); ImageFile* m_imageFile; ControlPointFile* m_controlPointFile; ControlPoint3D* m_controlPoint; int32_t m_controlPointIndexInFile; }; #ifdef __SELECTION_ITEM_IMAGE_CONTROL_POINT_DECLARE__ // #endif // __SELECTION_ITEM_IMAGE_CONTROL_POINT_DECLARE__ } // namespace #endif //__SELECTION_ITEM_IMAGE_CONTROL_POINT_H_ connectome-workbench-1.2.3+git41-gc4c6c90/src/Brain/SelectionItemSurfaceNode.cxx000066400000000000000000000106431300200146000272120ustar00rootroot00000000000000 /*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 __SELECTION_ITEM_SURFACE_NODE_DECLARE__ #include "SelectionItemSurfaceNode.h" #undef __SELECTION_ITEM_SURFACE_NODE_DECLARE__ #include "Surface.h" using namespace caret; /** * \class SelectionItemSurfaceNode * \brief Selected node. * * Information about the selected node. */ /** * Constructor. */ SelectionItemSurfaceNode::SelectionItemSurfaceNode() : SelectionItem(SelectionItemDataTypeEnum::SURFACE_NODE) { m_surface = NULL; m_contralateralFlag = false; m_nodeNumber = -1; } /** * Destructor. */ SelectionItemSurfaceNode::~SelectionItemSurfaceNode() { } /** * Copy constructor. * @param obj * Object that is copied. */ SelectionItemSurfaceNode::SelectionItemSurfaceNode(const SelectionItemSurfaceNode& obj) : SelectionItem(obj) { copyHelperSelectionItemSurfaceNode(obj); } /** * Assignment operator. * @param obj * Data copied from obj to m_. * @return * Reference to m_ object. */ SelectionItemSurfaceNode& SelectionItemSurfaceNode::operator=(const SelectionItemSurfaceNode& obj) { if (this != &obj) { SelectionItem::operator=(obj); copyHelperSelectionItemSurfaceNode(obj); } return *this; } /** * Helps with copying an object of m_ type. * @param ff * Object that is copied. */ void SelectionItemSurfaceNode::copyHelperSelectionItemSurfaceNode(const SelectionItemSurfaceNode& idItem) { m_surface = idItem.m_surface; m_nodeNumber = idItem.m_nodeNumber; m_contralateralFlag = idItem.m_contralateralFlag; } /** * Reset the selection item. */ void SelectionItemSurfaceNode::reset() { SelectionItem::reset(); m_surface = NULL; m_nodeNumber = -1; m_contralateralFlag = false; } /** * @return Is m_ selected item valid? */ bool SelectionItemSurfaceNode::isValid() const { return ((m_surface != NULL) && (m_nodeNumber >= 0)); } /** * @return Surface containing selected node. */ const Surface* SelectionItemSurfaceNode::getSurface() const { return m_surface; } /** * @return Surface containing selected node. */ Surface* SelectionItemSurfaceNode::getSurface() { return m_surface; } /** * Set the surface containing the selected node. * @param surface * New value for surface. * */ void SelectionItemSurfaceNode::setSurface(Surface* surface) { m_surface = surface; } /** * return Number of selected node. */ int32_t SelectionItemSurfaceNode::getNodeNumber() const { return m_nodeNumber; } /** * Set node number that was selected. * @param nodeNumber * New value for node. */ void SelectionItemSurfaceNode::setNodeNumber(const int32_t nodeNumber) { m_nodeNumber = nodeNumber; } ///** // * @return Is m_ a contralateral identification? // */ //bool //SelectionItemSurfaceNode::isContralateral() const //{ // return m_contralateralFlag; //} // ///** // * Set contralateral identification status. // * @param status // * New status. // */ //void //SelectionItemSurfaceNode::setContralateral(const bool status) //{ // m_contralateralFlag = status; //} /** * Get a description of m_ object's content. * @return String describing m_ object's content. */ AString SelectionItemSurfaceNode::toString() const { AString text = SelectionItem::toString(); text += ("Surface: " + ((m_surface != NULL) ? m_surface->getFileNameNoPath() : "INVALID") + "\n"); text += "Vertex: " + AString::number(m_nodeNumber) + "\n"; if (isValid() && (m_surface != NULL)) { text += "Coordinate: " + AString::fromNumbers(m_surface->getCoordinate(m_nodeNumber), 3, ", ") + "\n"; } text += "Contralateral: " + AString::fromBool(m_contralateralFlag) + "\n"; return text; } connectome-workbench-1.2.3+git41-gc4c6c90/src/Brain/SelectionItemSurfaceNode.h000066400000000000000000000043761300200146000266450ustar00rootroot00000000000000#ifndef __SELECTION_ITEM_SURFACE_NODE__H_ #define __SELECTION_ITEM_SURFACE_NODE__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 "SelectionItem.h" namespace caret { class Surface; class SelectionItemSurfaceNode : public SelectionItem { public: SelectionItemSurfaceNode(); virtual ~SelectionItemSurfaceNode(); SelectionItemSurfaceNode(const SelectionItemSurfaceNode&); SelectionItemSurfaceNode& operator=(const SelectionItemSurfaceNode&); virtual bool isValid() const; Surface* getSurface(); const Surface* getSurface() const; void setSurface(Surface* surface); int32_t getNodeNumber() const; void setNodeNumber(const int32_t nodeNumber); // bool isContralateral() const; // // void setContralateral(const bool status); // virtual void reset(); virtual AString toString() const; private: void copyHelperSelectionItemSurfaceNode(const SelectionItemSurfaceNode& idItem); public: private: Surface* m_surface; int32_t m_nodeNumber; bool m_contralateralFlag; }; #ifdef __SELECTION_ITEM_SURFACE_NODE_DECLARE__ // #endif // __SELECTION_ITEM_SURFACE_NODE_DECLARE__ } // namespace #endif //__SELECTION_ITEM_SURFACE_NODE__H_ connectome-workbench-1.2.3+git41-gc4c6c90/src/Brain/SelectionItemSurfaceNodeIdentificationSymbol.cxx000066400000000000000000000063231300200146000332520ustar00rootroot00000000000000 /*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 __SELECTION_ITEM_SURFACE_NODE_IDENTIFICATION_SYMBOL_DECLARE__ #include "SelectionItemSurfaceNodeIdentificationSymbol.h" #undef __SELECTION_ITEM_SURFACE_NODE_IDENTIFICATION_SYMBOL_DECLARE__ #include "Surface.h" using namespace caret; /** * \class SelectionItemSurfaceNodeIdentificationSymbol * \brief Selected node symbol * * Information about the selected node symbol. */ /** * Constructor. */ SelectionItemSurfaceNodeIdentificationSymbol::SelectionItemSurfaceNodeIdentificationSymbol() : SelectionItem(SelectionItemDataTypeEnum::SURFACE_NODE_IDENTIFICATION_SYMBOL) { this->surface = NULL; this->nodeNumber = -1; } /** * Destructor. */ SelectionItemSurfaceNodeIdentificationSymbol::~SelectionItemSurfaceNodeIdentificationSymbol() { } /** * Reset this selection item. */ void SelectionItemSurfaceNodeIdentificationSymbol::reset() { SelectionItem::reset(); this->surface = NULL; this->nodeNumber = -1; } /** * @return Is this selected item valid? */ bool SelectionItemSurfaceNodeIdentificationSymbol::isValid() const { return (this->nodeNumber >= 0); } /** * @return Surface containing selected node. */ const Surface* SelectionItemSurfaceNodeIdentificationSymbol::getSurface() const { return this->surface; } /** * @return Surface containing selected node. */ Surface* SelectionItemSurfaceNodeIdentificationSymbol::getSurface() { return this->surface; } /** * Set the surface containing the selected node. * @param surface * New value for surface. * */ void SelectionItemSurfaceNodeIdentificationSymbol::setSurface(Surface* surface) { this->surface = surface; } /** * return Number of selected node. */ int32_t SelectionItemSurfaceNodeIdentificationSymbol::getNodeNumber() const { return this->nodeNumber; } /** * Set node number that was selected. * @param nodeNumber * New value for node. */ void SelectionItemSurfaceNodeIdentificationSymbol::setNodeNumber(const int32_t nodeNumber) { this->nodeNumber = nodeNumber; } /** * Get a description of this object's content. * @return String describing this object's content. */ AString SelectionItemSurfaceNodeIdentificationSymbol::toString() const { AString text = SelectionItem::toString(); text += ("Surface: " + ((surface != NULL) ? surface->getFileNameNoPath() : "INVALID") + "\n"); text += "Vertex: " + AString::number(this->nodeNumber) + "\n"; return text; } connectome-workbench-1.2.3+git41-gc4c6c90/src/Brain/SelectionItemSurfaceNodeIdentificationSymbol.h000066400000000000000000000043541300200146000327010ustar00rootroot00000000000000#ifndef __SELECTION_ITEM_SURFACE_NODE_IDENTIFICATION_SYMBOL__H_ #define __SELECTION_ITEM_SURFACE_NODE_IDENTIFICATION_SYMBOL__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 "SelectionItem.h" namespace caret { class Surface; class SelectionItemSurfaceNodeIdentificationSymbol : public SelectionItem { public: SelectionItemSurfaceNodeIdentificationSymbol(); virtual ~SelectionItemSurfaceNodeIdentificationSymbol(); virtual bool isValid() const; Surface* getSurface(); const Surface* getSurface() const; void setSurface(Surface* surface); int32_t getNodeNumber() const; void setNodeNumber(const int32_t nodeNumber); virtual void reset(); virtual AString toString() const; private: SelectionItemSurfaceNodeIdentificationSymbol(const SelectionItemSurfaceNodeIdentificationSymbol&); SelectionItemSurfaceNodeIdentificationSymbol& operator=(const SelectionItemSurfaceNodeIdentificationSymbol&); public: private: Surface* surface; int32_t nodeNumber; }; #ifdef __SELECTION_ITEM_SURFACE_NODE_IDENTIFICATION_SYMBOL_DECLARE__ // #endif // __SELECTION_ITEM_SURFACE_NODE_IDENTIFICATION_SYMBOL_DECLARE__ } // namespace #endif //__SELECTION_ITEM_SURFACE_NODE_IDENTIFICATION_SYMBOL__H_ connectome-workbench-1.2.3+git41-gc4c6c90/src/Brain/SelectionItemSurfaceTriangle.cxx000066400000000000000000000133401300200146000300670ustar00rootroot00000000000000 /*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 __SELECTION_ITEM_SURFACE_TRIANGLE_DECLARE__ #include "SelectionItemSurfaceTriangle.h" #undef __SELECTION_ITEM_SURFACE_TRIANGLE_DECLARE__ #include #include "Surface.h" using namespace caret; /** * \class SelectionItemSurfaceTriangle * \brief Selected node. * * Information about the selected node. */ /** * Constructor. */ SelectionItemSurfaceTriangle::SelectionItemSurfaceTriangle() : SelectionItem(SelectionItemDataTypeEnum::SURFACE_TRIANGLE) { this->surface = NULL; this->triangleNumber = -1; this->nearestNodeNumber = -1; this->nearestNodeScreenXYZ[0] = 0.0; this->nearestNodeScreenXYZ[1] = 0.0; this->nearestNodeScreenXYZ[2] = std::numeric_limits::max(); this->nearestNodeModelXYZ[0] = 0.0; this->nearestNodeModelXYZ[1] = 0.0; this->nearestNodeModelXYZ[2] = 0.0; } /** * Destructor. */ SelectionItemSurfaceTriangle::~SelectionItemSurfaceTriangle() { } /** * Reset this selection item. */ void SelectionItemSurfaceTriangle::reset() { SelectionItem::reset(); this->surface = NULL; this->triangleNumber = -1; this->nearestNodeNumber = -1; this->nearestNodeScreenXYZ[0] = 0.0; this->nearestNodeScreenXYZ[1] = 0.0; this->nearestNodeScreenXYZ[2] = std::numeric_limits::max(); this->nearestNodeModelXYZ[0] = 0.0; this->nearestNodeModelXYZ[1] = 0.0; this->nearestNodeModelXYZ[2] = 0.0; } /** * return Is this selected item valid? */ bool SelectionItemSurfaceTriangle::isValid() const { return (this->triangleNumber >= 0); } /** * return Surface containing selected node. */ Surface* SelectionItemSurfaceTriangle::getSurface() { return this->surface; } /** * Set the surface containing the selected node. * @param surface * New value for surface. * */ void SelectionItemSurfaceTriangle::setSurface(Surface* surface) { this->surface = surface; } /** * return Number of selected triangle. */ int32_t SelectionItemSurfaceTriangle::getTriangleNumber() const { return this->triangleNumber; } /** * Set triangle number that was selected. * @param triangleNumber * New value for triangle. */ void SelectionItemSurfaceTriangle::setTriangleNumber(const int32_t triangleNumber) { this->triangleNumber = triangleNumber; } /** * @return Node nearest the mouse click in screen X&Y coordinates. * Will return negative if invalid. */ int32_t SelectionItemSurfaceTriangle::getNearestNodeNumber() const { return this->nearestNodeNumber; } /** * Set the node nearest to the mouse click in screen X&Y coordinates. * @param nearestNodeNumber * New value for the node. */ void SelectionItemSurfaceTriangle::setNearestNode(const int32_t nearestNodeNumber) { this->nearestNodeNumber = nearestNodeNumber; } /** * Get a description of this object's content. * @return String describing this object's content. */ AString SelectionItemSurfaceTriangle::toString() const { AString text = SelectionItem::toString(); text += ("Surface: " + ((surface != NULL) ? surface->getFileNameNoPath() : "INVALID") + "\n"); text += "Triangle: " + AString::number(this->triangleNumber) + "\n"; text += "Nearest Vertex: " + AString::number(this->nearestNodeNumber) + "\n"; if (this->isValid() && (surface != NULL)) { if (this->nearestNodeNumber >= 0) { text += "Coordinate: " + AString::fromNumbers(surface->getCoordinate(this->nearestNodeNumber), 3, ", ") + "\n"; } } return text; } /** * Get the screen XYZ of the nearest node. * @param nearestNodeScreenXYZ * XYZ out. */ void SelectionItemSurfaceTriangle::getNearestNodeScreenXYZ(double nearestNodeScreenXYZ[3]) const { nearestNodeScreenXYZ[0] = this->nearestNodeScreenXYZ[0]; nearestNodeScreenXYZ[1] = this->nearestNodeScreenXYZ[1]; nearestNodeScreenXYZ[2] = this->nearestNodeScreenXYZ[2]; } /** * Set the screen XYZ of the nearest node. * @param nearestNodeScreenXYZ * new XYZ. */ void SelectionItemSurfaceTriangle::setNearestNodeScreenXYZ(const double nearestNodeScreenXYZ[3]) { this->nearestNodeScreenXYZ[0] = nearestNodeScreenXYZ[0]; this->nearestNodeScreenXYZ[1] = nearestNodeScreenXYZ[1]; this->nearestNodeScreenXYZ[2] = nearestNodeScreenXYZ[2]; } /** * Get the model XYZ of the nearest node. * @param nearestNodeModelXYZ * XYZ out. */ void SelectionItemSurfaceTriangle::getNearestNodeModelXYZ(double nearestNodeModelXYZ[3]) const { nearestNodeModelXYZ[0] = this->nearestNodeModelXYZ[0]; nearestNodeModelXYZ[1] = this->nearestNodeModelXYZ[1]; nearestNodeModelXYZ[2] = this->nearestNodeModelXYZ[2]; } /** * Set the model XYZ of the nearest node. * @param nearestNodeModelXYZ * new XYZ. */ void SelectionItemSurfaceTriangle::setNearestNodeModelXYZ(const double nearestNodeModelXYZ[3]) { this->nearestNodeModelXYZ[0] = nearestNodeModelXYZ[0]; this->nearestNodeModelXYZ[1] = nearestNodeModelXYZ[1]; this->nearestNodeModelXYZ[2] = nearestNodeModelXYZ[2]; } connectome-workbench-1.2.3+git41-gc4c6c90/src/Brain/SelectionItemSurfaceTriangle.h000066400000000000000000000050521300200146000275150ustar00rootroot00000000000000#ifndef __SELECTION_ITEM_SURFACE_TRIANGLE__H_ #define __SELECTION_ITEM_SURFACE_TRIANGLE__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 "SelectionItem.h" namespace caret { class Surface; class SelectionItemSurfaceTriangle : public SelectionItem { public: SelectionItemSurfaceTriangle(); virtual ~SelectionItemSurfaceTriangle(); virtual bool isValid() const; Surface* getSurface(); void setSurface(Surface* surface); int32_t getTriangleNumber() const; void setTriangleNumber(const int32_t triangleNumber); int32_t getNearestNodeNumber() const; void setNearestNode(const int32_t nearestNodeNumber); void getNearestNodeScreenXYZ(double screenXYZ[3]) const; void setNearestNodeScreenXYZ(const double screenXYZ[3]); void getNearestNodeModelXYZ(double modelXYZ[3]) const; void setNearestNodeModelXYZ(const double modelXYZ[3]); virtual void reset(); virtual AString toString() const; private: SelectionItemSurfaceTriangle(const SelectionItemSurfaceTriangle&); SelectionItemSurfaceTriangle& operator=(const SelectionItemSurfaceTriangle&); private: Surface* surface; int32_t triangleNumber; int32_t nearestNodeNumber; double nearestNodeScreenXYZ[3]; double nearestNodeModelXYZ[3]; }; #ifdef __SELECTION_ITEM_SURFACE_TRIANGLE_DECLARE__ // #endif // __SELECTION_ITEM_SURFACE_TRIANGLE_DECLARE__ } // namespace #endif //__SELECTION_ITEM_SURFACE_TRIANGLE__H_ connectome-workbench-1.2.3+git41-gc4c6c90/src/Brain/SelectionItemVoxel.cxx000066400000000000000000000114741300200146000261140ustar00rootroot00000000000000 /*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 __SELECTION_ITEM_VOXEL_DECLARE__ #include "SelectionItemVoxel.h" #undef __SELECTION_ITEM_VOXEL_DECLARE__ #include "CaretAssert.h" #include "VolumeFile.h" using namespace caret; /** * \class SelectionItemVoxel * \brief Selected voxel. * * Information about an selected voxel. */ /** * Constructor. */ SelectionItemVoxel::SelectionItemVoxel() : SelectionItem(SelectionItemDataTypeEnum::VOXEL) { /* * Note: reset() is virtual so cannot call from constructor. */ resetPrivate(); } /** * Constructor for child classes. * * @param itemDataType * The selection item data type for child class. */ SelectionItemVoxel::SelectionItemVoxel(const SelectionItemDataTypeEnum::Enum itemDataType) : SelectionItem(itemDataType) { /* * Note: reset() is virtual so cannot call from constructor. */ resetPrivate(); } /** * Destructor. */ SelectionItemVoxel::~SelectionItemVoxel() { } /** * Copy constructor. * @param obj * Object that is copied. */ SelectionItemVoxel::SelectionItemVoxel(const SelectionItemVoxel& obj) : SelectionItem(obj) { copyHelperSelectionItemVoxel(obj); } /** * Assignment operator. * @param obj * Data copied from obj to m_. * @return * Reference to m_ object. */ SelectionItemVoxel& SelectionItemVoxel::operator=(const SelectionItemVoxel& obj) { if (this != &obj) { SelectionItem::operator=(obj); copyHelperSelectionItemVoxel(obj); } return *this; } /** * Helps with copying an object of m_ type. * @param ff * Object that is copied. */ void SelectionItemVoxel::copyHelperSelectionItemVoxel(const SelectionItemVoxel& idItem) { m_volumeFile = idItem.m_volumeFile; m_voxelIJK[0] = idItem.m_voxelIJK[0]; m_voxelIJK[1] = idItem.m_voxelIJK[1]; m_voxelIJK[2] = idItem.m_voxelIJK[2]; } /** * Reset this selection item. */ void SelectionItemVoxel::reset() { SelectionItem::reset(); resetPrivate(); } /** * Reset this selection item. */ void SelectionItemVoxel::resetPrivate() { m_volumeFile = NULL; m_voxelIJK[0] = -1; m_voxelIJK[1] = -1; m_voxelIJK[2] = -1; } /** * @return The volume file. */ const VolumeMappableInterface* SelectionItemVoxel::getVolumeFile() const { return m_volumeFile; } /** * Get the voxel indices. * @param voxelIJK * Output containing voxel indices. */ void SelectionItemVoxel::getVoxelIJK(int64_t voxelIJK[3]) const { voxelIJK[0] = m_voxelIJK[0]; voxelIJK[1] = m_voxelIJK[1]; voxelIJK[2] = m_voxelIJK[2]; } /** * Set the volume file. * * @param brain * Brain containing the volume. * @param volumeFile * New value for volume file. * @param voxelIJK * New value for voxel indices. * @param screenDepth * The screen depth. */ void SelectionItemVoxel::setVoxelIdentification(Brain* brain, VolumeMappableInterface* volumeFile, const int64_t voxelIJK[3], const double screenDepth) { setBrain(brain); m_volumeFile = volumeFile; m_voxelIJK[0] = voxelIJK[0]; m_voxelIJK[1] = voxelIJK[1]; m_voxelIJK[2] = voxelIJK[2]; setScreenDepth(screenDepth); } /** * @return Is this selected item valid? */ bool SelectionItemVoxel::isValid() const { return (m_volumeFile != NULL); } /** * Get a description of this object's content. * @return String describing this object's content. */ AString SelectionItemVoxel::toString() const { AString text = SelectionItem::toString(); AString name = "INVALID"; if (m_volumeFile != NULL) { CaretMappableDataFile* cmdf = dynamic_cast(m_volumeFile); if (cmdf != NULL) { name = cmdf->getFileNameNoPath(); } } text += ("Volume: " + name); text += ("Voxel: " + AString::number(m_voxelIJK[0]) + ", " + AString::number(m_voxelIJK[1]) + ", " + AString::number(m_voxelIJK[2]) + "\n"); return text; } connectome-workbench-1.2.3+git41-gc4c6c90/src/Brain/SelectionItemVoxel.h000066400000000000000000000044621300200146000255400ustar00rootroot00000000000000#ifndef __SELECTION_ITEM_VOXEL__H_ #define __SELECTION_ITEM_VOXEL__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 "SelectionItem.h" namespace caret { class VolumeMappableInterface; class SelectionItemVoxel : public SelectionItem { public: SelectionItemVoxel(); virtual ~SelectionItemVoxel(); SelectionItemVoxel(const SelectionItemVoxel&); SelectionItemVoxel& operator=(const SelectionItemVoxel&); virtual bool isValid() const; const VolumeMappableInterface* getVolumeFile() const; void getVoxelIJK(int64_t voxelIJK[3]) const; void setVoxelIdentification(Brain* brain, VolumeMappableInterface* volumeFile, const int64_t voxelIJK[3], const double screenDepth); virtual void reset(); virtual AString toString() const; protected: SelectionItemVoxel(const SelectionItemDataTypeEnum::Enum itemDataType); private: void copyHelperSelectionItemVoxel(const SelectionItemVoxel& idItem); void resetPrivate(); VolumeMappableInterface* m_volumeFile; int64_t m_voxelIJK[3]; }; #ifdef __SELECTION_ITEM_VOXEL_DECLARE__ // #endif // __SELECTION_ITEM_VOXEL_DECLARE__ } // namespace #endif //__SELECTION_ITEM_VOXEL__H_ connectome-workbench-1.2.3+git41-gc4c6c90/src/Brain/SelectionItemVoxelEditing.cxx000066400000000000000000000060251300200146000274140ustar00rootroot00000000000000 /*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 __SELECTION_ITEM_VOXEL_EDITING_DECLARE__ #include "SelectionItemVoxelEditing.h" #undef __SELECTION_ITEM_VOXEL_EDITING_DECLARE__ #include "CaretAssert.h" using namespace caret; /** * \class caret::SelectionItemVoxelEditing * \brief Information about selected voxel used in volume editing. * \ingroup Brain * * Reports selection of any voxel, even those that are * not displayed. This allows the user to select "off" voxels * so that they can be turned on when editing a volume. */ /** * Constructor. */ SelectionItemVoxelEditing::SelectionItemVoxelEditing() : SelectionItemVoxel(SelectionItemDataTypeEnum::VOXEL_EDITING) { m_volumeFileForEditing = NULL; } /** * Destructor. */ SelectionItemVoxelEditing::~SelectionItemVoxelEditing() { } /** * @return Volume file that is being edited. */ const VolumeFile* SelectionItemVoxelEditing::getVolumeFileForEditing() const { return m_volumeFileForEditing; } /** * @return Volume file that is being edited. */ VolumeFile* SelectionItemVoxelEditing::getVolumeFileForEditing() { return m_volumeFileForEditing; } /** * Set the volume file that is being edited. * * @param volumeFile * Volume file that is being edited. */ void SelectionItemVoxelEditing::setVolumeFileForEditing(VolumeFile* volumeFile) { m_volumeFileForEditing = volumeFile; } /** * Reset this selection item. */ void SelectionItemVoxelEditing::reset() { SelectionItemVoxel::reset(); m_volumeFileForEditing = NULL; } /** * Set the voxel diff XYZ (bottom left to top right * of screen drawing). * * @param voxelDiffXYZ * Difference from voxel bottom left to top right on screen. */ void SelectionItemVoxelEditing::setVoxelDiffXYZ(const float voxelDiffXYZ[3]) { m_voxelDiffXYZ[0] = voxelDiffXYZ[0]; m_voxelDiffXYZ[1] = voxelDiffXYZ[1]; m_voxelDiffXYZ[2] = voxelDiffXYZ[2]; } /** * Get the voxel diff XYZ (bottom left to top right * of screen drawing). * * @param voxelDiffXYZ * Difference from voxel bottom left to top right on screen. */ void SelectionItemVoxelEditing::getVoxelDiffXYZ(float voxelDiffXYZ[3]) const { voxelDiffXYZ[0] = m_voxelDiffXYZ[0]; voxelDiffXYZ[1] = m_voxelDiffXYZ[1]; voxelDiffXYZ[2] = m_voxelDiffXYZ[2]; } connectome-workbench-1.2.3+git41-gc4c6c90/src/Brain/SelectionItemVoxelEditing.h000066400000000000000000000041101300200146000270320ustar00rootroot00000000000000#ifndef __SELECTION_ITEM_VOXEL_EDITING_H__ #define __SELECTION_ITEM_VOXEL_EDITING_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 "SelectionItemVoxel.h" namespace caret { class VolumeFile; class SelectionItemVoxelEditing : public SelectionItemVoxel { public: SelectionItemVoxelEditing(); virtual ~SelectionItemVoxelEditing(); const VolumeFile* getVolumeFileForEditing() const; VolumeFile* getVolumeFileForEditing(); void setVolumeFileForEditing(VolumeFile* volumeFile); void setVoxelDiffXYZ(const float voxelDiffXYZ[3]); void getVoxelDiffXYZ(float voxelDiffXYZ[3]) const; virtual void reset(); // ADD_NEW_METHODS_HERE private: SelectionItemVoxelEditing(const SelectionItemVoxelEditing&); SelectionItemVoxelEditing& operator=(const SelectionItemVoxelEditing&); VolumeFile* m_volumeFileForEditing; float m_voxelDiffXYZ[3]; // ADD_NEW_MEMBERS_HERE }; #ifdef __SELECTION_ITEM_VOXEL_EDITING_DECLARE__ // #endif // __SELECTION_ITEM_VOXEL_EDITING_DECLARE__ } // namespace #endif //__SELECTION_ITEM_VOXEL_EDITING_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Brain/SelectionItemVoxelIdentificationSymbol.cxx000066400000000000000000000103141300200146000321440ustar00rootroot00000000000000 /*LICENSE_START*/ /* * Copyright (C) 2015 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_VOXEL_IDENTIFICATION_SYMBOL_DECLARE__ #include "SelectionItemVoxelIdentificationSymbol.h" #undef __SELECTION_ITEM_VOXEL_IDENTIFICATION_SYMBOL_DECLARE__ #include "CaretAssert.h" using namespace caret; /** * \class caret::SelectionItemVoxelIdentificationSymbol * \brief * \ingroup Brain * * */ /** * Constructor. */ SelectionItemVoxelIdentificationSymbol::SelectionItemVoxelIdentificationSymbol() : SelectionItem(SelectionItemDataTypeEnum::VOXEL_IDENTIFICATION_SYMBOL) { /* * Note: reset() is virtual so cannot call from constructor. */ resetPrivate(); } /** * Destructor. */ SelectionItemVoxelIdentificationSymbol::~SelectionItemVoxelIdentificationSymbol() { } /** * Copy constructor. * @param obj * Object that is copied. */ SelectionItemVoxelIdentificationSymbol::SelectionItemVoxelIdentificationSymbol(const SelectionItemVoxelIdentificationSymbol& obj) : SelectionItem(obj) { this->copyHelperSelectionItemVoxelIdentificationSymbol(obj); } /** * Assignment operator. * @param obj * Data copied from obj to this. * @return * Reference to this object. */ SelectionItemVoxelIdentificationSymbol& SelectionItemVoxelIdentificationSymbol::operator=(const SelectionItemVoxelIdentificationSymbol& obj) { if (this != &obj) { SelectionItem::operator=(obj); this->copyHelperSelectionItemVoxelIdentificationSymbol(obj); } return *this; } /** * Helps with copying an object of this type. * @param obj * Object that is copied. */ void SelectionItemVoxelIdentificationSymbol::copyHelperSelectionItemVoxelIdentificationSymbol(const SelectionItemVoxelIdentificationSymbol& obj) { m_voxelXYZ[0] = obj.m_voxelXYZ[0]; m_voxelXYZ[1] = obj.m_voxelXYZ[1]; m_voxelXYZ[2] = obj.m_voxelXYZ[2]; m_voxelValid = obj.m_voxelValid; } /** * Get the coordinates of the voxel identification symbol. * * @param xyzOut * Coordinates of the voxel identification symbol. */ void SelectionItemVoxelIdentificationSymbol::getVoxelXYZ(float xyzOut[3]) const { xyzOut[0] = m_voxelXYZ[0]; xyzOut[1] = m_voxelXYZ[1]; xyzOut[2] = m_voxelXYZ[2]; } /** * Set the coordinates of the voxel identification symbol. * * @param xyzOut * Coordinates of the voxel identification symbol. */ void SelectionItemVoxelIdentificationSymbol::setVoxelXYZ(const float xyz[3]) { m_voxelXYZ[0] = xyz[0]; m_voxelXYZ[1] = xyz[1]; m_voxelXYZ[2] = xyz[2]; m_voxelValid = true; } /** * Reset this selection item. */ void SelectionItemVoxelIdentificationSymbol::reset() { SelectionItem::reset(); resetPrivate(); } /** * Reset this items data. */ void SelectionItemVoxelIdentificationSymbol::resetPrivate() { m_voxelValid = false; m_voxelXYZ[0] = 0.0; m_voxelXYZ[1] = 0.0; m_voxelXYZ[2] = 0.0; } /** * @return Is this selected item valid? */ bool SelectionItemVoxelIdentificationSymbol::isValid() const { return m_voxelValid; } /** * Get a description of this object's content. * @return String describing this object's content. */ AString SelectionItemVoxelIdentificationSymbol::toString() const { AString text = SelectionItem::toString(); text += ("Voxel XYZ: " + AString::fromNumbers(m_voxelXYZ, 3, ", ") + "\n" + "Valid: " + AString::fromBool(m_voxelValid) + "\n"); return text; } connectome-workbench-1.2.3+git41-gc4c6c90/src/Brain/SelectionItemVoxelIdentificationSymbol.h000066400000000000000000000043311300200146000315730ustar00rootroot00000000000000#ifndef __SELECTION_ITEM_VOXEL_IDENTIFICATION_SYMBOL_H__ #define __SELECTION_ITEM_VOXEL_IDENTIFICATION_SYMBOL_H__ /*LICENSE_START*/ /* * Copyright (C) 2015 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 SelectionItemVoxelIdentificationSymbol : public SelectionItem { public: SelectionItemVoxelIdentificationSymbol(); virtual ~SelectionItemVoxelIdentificationSymbol(); SelectionItemVoxelIdentificationSymbol(const SelectionItemVoxelIdentificationSymbol& obj); SelectionItemVoxelIdentificationSymbol& operator=(const SelectionItemVoxelIdentificationSymbol& obj); void getVoxelXYZ(float xyzOut[3]) const; void setVoxelXYZ(const float xyz[3]); virtual bool isValid() const; virtual void reset(); virtual AString toString() const; // ADD_NEW_METHODS_HERE private: void copyHelperSelectionItemVoxelIdentificationSymbol(const SelectionItemVoxelIdentificationSymbol& obj); void resetPrivate(); float m_voxelXYZ[3]; bool m_voxelValid; // ADD_NEW_MEMBERS_HERE }; #ifdef __SELECTION_ITEM_VOXEL_IDENTIFICATION_SYMBOL_DECLARE__ // #endif // __SELECTION_ITEM_VOXEL_IDENTIFICATION_SYMBOL_DECLARE__ } // namespace #endif //__SELECTION_ITEM_VOXEL_IDENTIFICATION_SYMBOL_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Brain/SelectionManager.cxx000066400000000000000000000556511300200146000255570ustar00rootroot00000000000000 /*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 "CaretOpenGLInclude.h" #include #include #include "BrainConstants.h" #include "CaretAssert.h" #include "CaretLogger.h" #define __SELECTION_MANAGER_DECLARE__ #include "SelectionManager.h" #undef __SELECTION_MANAGER_DECLARE__ #include "SelectionItemAnnotation.h" #include "SelectionItemBorderSurface.h" #include "SelectionItemChartDataSeries.h" #include "SelectionItemChartFrequencySeries.h" #include "SelectionItemChartMatrix.h" #include "SelectionItemChartTimeSeries.h" #include "SelectionItemCiftiConnectivityMatrixRowColumn.h" #include "SelectionItemFocusSurface.h" #include "SelectionItemFocusVolume.h" #include "SelectionItemImage.h" #include "SelectionItemImageControlPoint.h" #include "SelectionItemSurfaceNode.h" #include "SelectionItemSurfaceNodeIdentificationSymbol.h" #include "SelectionItemSurfaceTriangle.h" #include "SelectionItemVoxel.h" #include "SelectionItemVoxelEditing.h" #include "SelectionItemVoxelIdentificationSymbol.h" #include "IdentificationTextGenerator.h" #include "Surface.h" using namespace caret; /** * \class SelectionManager * \brief Manages identification. * * Manages identification. */ /** * Constructor. */ SelectionManager::SelectionManager() : CaretObject() { m_annotationIdentification = new SelectionItemAnnotation(); m_surfaceBorderIdentification = new SelectionItemBorderSurface(); m_chartDataSeriesIdentification = new SelectionItemChartDataSeries(); m_chartDataFrequencyIdentification = new SelectionItemChartFrequencySeries(); m_chartMatrixIdentification = new SelectionItemChartMatrix(); m_ciftiConnectivityMatrixRowColumnIdentfication = new SelectionItemCiftiConnectivityMatrixRowColumn(); m_chartTimeSeriesIdentification = new SelectionItemChartTimeSeries(); m_surfaceFocusIdentification = new SelectionItemFocusSurface(); m_volumeFocusIdentification = new SelectionItemFocusVolume(); m_imageIdentification = new SelectionItemImage(); m_imageControlPointIdentification = new SelectionItemImageControlPoint(); m_surfaceNodeIdentification = new SelectionItemSurfaceNode(); m_surfaceNodeIdentificationSymbol = new SelectionItemSurfaceNodeIdentificationSymbol(); m_surfaceTriangleIdentification = new SelectionItemSurfaceTriangle(); m_voxelIdentification = new SelectionItemVoxel(); m_voxelIdentificationSymbol = new SelectionItemVoxelIdentificationSymbol(); m_voxelEditingIdentification = new SelectionItemVoxelEditing(); m_allSelectionItems.push_back(m_annotationIdentification); m_allSelectionItems.push_back(m_surfaceBorderIdentification); m_allSelectionItems.push_back(m_chartDataSeriesIdentification); m_allSelectionItems.push_back(m_chartDataFrequencyIdentification); m_allSelectionItems.push_back(m_chartMatrixIdentification); m_allSelectionItems.push_back(m_chartTimeSeriesIdentification); m_allSelectionItems.push_back(m_ciftiConnectivityMatrixRowColumnIdentfication); m_allSelectionItems.push_back(m_surfaceFocusIdentification); m_allSelectionItems.push_back(m_surfaceNodeIdentification); m_allSelectionItems.push_back(m_surfaceNodeIdentificationSymbol); m_allSelectionItems.push_back(m_surfaceTriangleIdentification); m_allSelectionItems.push_back(m_imageIdentification); m_allSelectionItems.push_back(m_imageControlPointIdentification); m_allSelectionItems.push_back(m_voxelIdentification); m_allSelectionItems.push_back(m_voxelIdentificationSymbol); m_allSelectionItems.push_back(m_voxelEditingIdentification); m_allSelectionItems.push_back(m_volumeFocusIdentification); m_surfaceSelectedItems.push_back(m_surfaceNodeIdentification); m_surfaceSelectedItems.push_back(m_surfaceTriangleIdentification); m_layeredSelectedItems.push_back(m_surfaceBorderIdentification); m_layeredSelectedItems.push_back(m_surfaceFocusIdentification); m_volumeSelectedItems.push_back(m_voxelIdentification); m_volumeSelectedItems.push_back(m_voxelEditingIdentification); m_volumeSelectedItems.push_back(m_volumeFocusIdentification); m_idTextGenerator = new IdentificationTextGenerator(); m_lastSelectedItem = NULL; reset(); EventManager::get()->addEventListener(this, EventTypeEnum::EVENT_IDENTIFICATION_SYMBOL_REMOVAL); } /** * Destructor. */ SelectionManager::~SelectionManager() { reset(); delete m_annotationIdentification; m_annotationIdentification = NULL; delete m_surfaceBorderIdentification; m_surfaceBorderIdentification = NULL; delete m_chartDataSeriesIdentification; m_chartDataSeriesIdentification = NULL; delete m_chartDataFrequencyIdentification; m_chartDataFrequencyIdentification = NULL; delete m_chartMatrixIdentification; m_chartMatrixIdentification = NULL; delete m_chartTimeSeriesIdentification; m_chartTimeSeriesIdentification = NULL; delete m_ciftiConnectivityMatrixRowColumnIdentfication; m_ciftiConnectivityMatrixRowColumnIdentfication = NULL; delete m_surfaceFocusIdentification; m_surfaceFocusIdentification = NULL; delete m_imageIdentification; m_imageIdentification = NULL; delete m_imageControlPointIdentification; m_imageControlPointIdentification = NULL; delete m_surfaceNodeIdentification; m_surfaceNodeIdentification = NULL; delete m_surfaceNodeIdentificationSymbol; m_surfaceNodeIdentificationSymbol = NULL; delete m_surfaceTriangleIdentification; m_surfaceTriangleIdentification = NULL; delete m_voxelIdentification; m_voxelIdentification = NULL; delete m_voxelEditingIdentification; m_voxelEditingIdentification = NULL; delete m_voxelIdentificationSymbol; m_voxelIdentificationSymbol = NULL; delete m_volumeFocusIdentification; m_volumeFocusIdentification = NULL; delete m_idTextGenerator; m_idTextGenerator = NULL; if (m_lastSelectedItem != NULL) { delete m_lastSelectedItem; } EventManager::get()->removeAllEventsFromListener(this); } /** * Receive events from the event manager. * * @param event * The event. */ void SelectionManager::receiveEvent(Event* /*event*/) { } /** * Filter selections to arbitrate between triangle/node * and to remove any selections behind another selection. * * @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 SelectionManager::filterSelections(const bool applySelectionBackgroundFiltering) { AString logText; for (std::vector::iterator iter = m_allSelectionItems.begin(); iter != m_allSelectionItems.end(); iter++) { SelectionItem* item = *iter; if (item->isValid()) { logText += ("\n" + item->toString() + "\n"); } } CaretLogFine("Selected Items BEFORE filtering: " + logText); SelectionItemSurfaceTriangle* triangleID = m_surfaceTriangleIdentification; SelectionItemSurfaceNode* nodeID = m_surfaceNodeIdentification; // // If both a node and triangle are found // if ((nodeID->getNodeNumber() >= 0) && (triangleID->getTriangleNumber() >= 0)) { // // Is node further from user than triangle? // double depthDiff = m_surfaceNodeIdentification->getScreenDepth() - triangleID->getScreenDepth(); if (depthDiff > 0.00001) { // // Do not use node // m_surfaceNodeIdentification->reset(); } } // // Have a triangle ? // const int32_t triangleNumber = triangleID->getTriangleNumber(); if (triangleNumber >= 0) { // // If no node, use node in nearest triangle // if (m_surfaceNodeIdentification->getNodeNumber() < 0) { const int32_t nearestNode = triangleID->getNearestNodeNumber(); if (nearestNode >= 0) { CaretLogFine("Switched vertex to triangle nearest vertex ." + AString::number(nearestNode)); nodeID->setNodeNumber(nearestNode); nodeID->setScreenDepth(triangleID->getScreenDepth()); nodeID->setSurface(triangleID->getSurface()); double xyz[3]; triangleID->getNearestNodeScreenXYZ(xyz); nodeID->setScreenXYZ(xyz); triangleID->getNearestNodeModelXYZ(xyz); nodeID->setModelXYZ(xyz); nodeID->setBrain(triangleID->getBrain()); } } } /* * See if node identification symbol is too far from selected node. * This may occur if the symbol is on the other side of the surface. */ if ((m_surfaceNodeIdentificationSymbol->getNodeNumber() >= 0) && (m_surfaceNodeIdentification->getNodeNumber() >= 0)) { const double depthDiff = (m_surfaceNodeIdentificationSymbol->getScreenDepth() - m_surfaceNodeIdentification->getScreenDepth()); if (depthDiff > 0.01) { m_surfaceNodeIdentificationSymbol->reset(); } else { m_surfaceNodeIdentification->reset(); } } if (m_voxelIdentificationSymbol->isValid() && m_voxelIdentification->isValid()) { const double depthDiff = (m_voxelIdentificationSymbol->getScreenDepth() - m_voxelIdentification->getScreenDepth()); if (depthDiff > 0.01) { m_voxelIdentificationSymbol->reset(); } else { m_voxelIdentification->reset(); } } if (applySelectionBackgroundFiltering) { clearDistantSelections(); } logText = ""; for (std::vector::iterator iter = m_allSelectionItems.begin(); iter != m_allSelectionItems.end(); iter++) { SelectionItem* item = *iter; if (item->isValid()) { logText += ("\n" + item->toString() + "\n"); } } CaretLogFine("Selected Items AFTER filtering: " + logText); } /** * Examine the selection groups and manipulate them * so that there are not items selected in more * than one group. */ void SelectionManager::clearDistantSelections() { std::vector* > itemGroups; /* * Make layers items slightly closer since they are * often pasted onto the surface. */ for (std::vector::iterator iter = m_layeredSelectedItems.begin(); iter != m_layeredSelectedItems.end(); iter++) { SelectionItem* item = *iter; item->setScreenDepth(item->getScreenDepth()* 0.99); } itemGroups.push_back(&m_layeredSelectedItems); itemGroups.push_back(&m_surfaceSelectedItems); itemGroups.push_back(&m_volumeSelectedItems); std::vector* minDepthGroup = NULL; double minDepth = std::numeric_limits::max(); for (std::vector* >::iterator iter = itemGroups.begin(); iter != itemGroups.end(); iter++) { std::vector* group = *iter; SelectionItem* minDepthItem = getMinimumDepthFromMultipleSelections(*group); if (minDepthItem != NULL) { double md = minDepthItem->getScreenDepth(); if (md < minDepth) { minDepthGroup = group; minDepth = md; } } } if (minDepthGroup != NULL) { for (std::vector* >::iterator iter = itemGroups.begin(); iter != itemGroups.end(); iter++) { std::vector* group = *iter; if (group != minDepthGroup) { for (std::vector::iterator iter = group->begin(); iter != group->end(); iter++) { SelectionItem* item = *iter; item->reset(); } } } } } /** * Reset all selected items except for the given selected item. * @param selectedItem * SelectedItem that is NOT reset. */ void SelectionManager::clearOtherSelectedItems(SelectionItem* selectedItem) { for (std::vector::iterator iter = m_allSelectionItems.begin(); iter != m_allSelectionItems.end(); iter++) { SelectionItem* item = *iter; if (item != selectedItem) { item->reset(); } } } /** * From the list of selectable items, find the item with the * minimum depth. * @param items List of selectable items. * @return Reference to selectable item with the minimum depth * or NULL if no valid selectable items in the list. */ SelectionItem* SelectionManager::getMinimumDepthFromMultipleSelections(std::vector items) const { double minDepth = std::numeric_limits::max(); SelectionItem* minDepthItem = NULL; for (std::vector::iterator iter = items.begin(); iter != items.end(); iter++) { SelectionItem* item = *iter; if (item->isValid()) { if (item->getScreenDepth() < minDepth) { minDepthItem = item; minDepth = item->getScreenDepth(); } } } return minDepthItem; } /** * Set the enabled status for all selection items. * * @param status * New status for ALL selection items. */ void SelectionManager::setAllSelectionsEnabled(const bool status) { for (std::vector::iterator iter = m_allSelectionItems.begin(); iter != m_allSelectionItems.end(); iter++) { SelectionItem* item = *iter; item->setEnabledForSelection(status); } } /** * Get text describing the current identification data. * @param brain * Brain containing the data. */ AString SelectionManager::getIdentificationText(const Brain* brain) const { CaretAssert(brain); const AString text = m_idTextGenerator->createIdentificationText(this, brain); return text; } /** * Reset all identification. */ void SelectionManager::reset() { for (std::vector::iterator iter = m_allSelectionItems.begin(); iter != m_allSelectionItems.end(); iter++) { SelectionItem* item = *iter; item->reset(); } } /** * @return Identification for annotations. */ SelectionItemAnnotation* SelectionManager::getAnnotationIdentification() { return m_annotationIdentification; } /** * @return Identification for annotations. */ const SelectionItemAnnotation* SelectionManager::getAnnotationIdentification() const { return m_annotationIdentification; } /** * @return Identification for image. */ SelectionItemImage* SelectionManager::getImageIdentification() { return m_imageIdentification; } /** * @return Identification for image. */ const SelectionItemImage* SelectionManager::getImageIdentification() const { return m_imageIdentification; } /** * @return Identification for image control point. */ SelectionItemImageControlPoint* SelectionManager::getImageControlPointIdentification() { return m_imageControlPointIdentification; } /** * @return Identification for image control point. */ const SelectionItemImageControlPoint* SelectionManager::getImageControlPointIdentification() const { return m_imageControlPointIdentification; } /** * @return Identification for surface node. */ SelectionItemSurfaceNode* SelectionManager::getSurfaceNodeIdentification() { return m_surfaceNodeIdentification; } /** * @return Identification for surface node. */ const SelectionItemSurfaceNode* SelectionManager::getSurfaceNodeIdentification() const { return m_surfaceNodeIdentification; } /** * @return Identification for surface node. */ const SelectionItemSurfaceNodeIdentificationSymbol* SelectionManager::getSurfaceNodeIdentificationSymbol() const { return m_surfaceNodeIdentificationSymbol; } /** * @return Identification for surface node. */ SelectionItemSurfaceNodeIdentificationSymbol* SelectionManager::getSurfaceNodeIdentificationSymbol() { return m_surfaceNodeIdentificationSymbol; } /** * @return Identification for surface triangle. */ SelectionItemSurfaceTriangle* SelectionManager::getSurfaceTriangleIdentification() { return m_surfaceTriangleIdentification; } /** * @return Identification for surface triangle. */ const SelectionItemSurfaceTriangle* SelectionManager::getSurfaceTriangleIdentification() const { return m_surfaceTriangleIdentification; } /** * @return Identification for voxels. */ const SelectionItemVoxel* SelectionManager::getVoxelIdentification() const { return m_voxelIdentification; } /** * @return Identification for voxels. */ SelectionItemVoxel* SelectionManager::getVoxelIdentification() { return m_voxelIdentification; } /** * @return Identification for voxel identification system. */ const SelectionItemVoxelIdentificationSymbol* SelectionManager::getVoxelIdentificationSymbol() const { return m_voxelIdentificationSymbol; } /** * @return Identification for voxels. */ SelectionItemVoxelIdentificationSymbol* SelectionManager::getVoxelIdentificationSymbol() { return m_voxelIdentificationSymbol; } /** * @return Identification for voxel editing. */ const SelectionItemVoxelEditing* SelectionManager::getVoxelEditingIdentification() const { return m_voxelEditingIdentification; } /** * @return Identification for voxel editing. */ SelectionItemVoxelEditing* SelectionManager::getVoxelEditingIdentification() { return m_voxelEditingIdentification; } /** * @return Identification for borders. */ SelectionItemBorderSurface* SelectionManager::getSurfaceBorderIdentification() { return m_surfaceBorderIdentification; } /** * @return Identification for borders. */ const SelectionItemBorderSurface* SelectionManager::getSurfaceBorderIdentification() const { return m_surfaceBorderIdentification; } /** * @return Identification for foci. */ SelectionItemFocusSurface* SelectionManager::getSurfaceFocusIdentification() { return m_surfaceFocusIdentification; } /** * @return Identification for foci. */ const SelectionItemFocusSurface* SelectionManager::getSurfaceFocusIdentification() const { return m_surfaceFocusIdentification; } /** * @return Identification for foci. */ SelectionItemFocusVolume* SelectionManager::getVolumeFocusIdentification() { return m_volumeFocusIdentification; } /** * @return Identification for foci. */ const SelectionItemFocusVolume* SelectionManager::getVolumeFocusIdentification() const { return m_volumeFocusIdentification; } /** * @return Identification for data-series chart. */ SelectionItemChartDataSeries* SelectionManager::getChartDataSeriesIdentification() { return m_chartDataSeriesIdentification; } /** * @return Identification for data-series chart (const method). */ const SelectionItemChartDataSeries* SelectionManager::getChartDataSeriesIdentification() const { return m_chartDataSeriesIdentification; } /** * @return Identification for frequency-series chart. */ SelectionItemChartFrequencySeries* SelectionManager::getChartFrequencySeriesIdentification() { return m_chartDataFrequencyIdentification; } /** * @return Identification for frequency-series chart (const method). */ const SelectionItemChartFrequencySeries* SelectionManager::getChartFrequencySeriesIdentification() const { return m_chartDataFrequencyIdentification; } /** * @return Identification for matrix chart. */ SelectionItemChartMatrix* SelectionManager::getChartMatrixIdentification() { return m_chartMatrixIdentification; } /** * @return Identification for matrix chart (const method) */ const SelectionItemChartMatrix* SelectionManager::getChartMatrixIdentification() const { return m_chartMatrixIdentification; } /** * @return Identification for CIFTI Connectivity Matrix Row/Column. */ SelectionItemCiftiConnectivityMatrixRowColumn* SelectionManager::getCiftiConnectivityMatrixRowColumnIdentification() { return m_ciftiConnectivityMatrixRowColumnIdentfication; } /** * @return Identification for CIFTI Connectivity Matrix Row/Column. */ const SelectionItemCiftiConnectivityMatrixRowColumn* SelectionManager::getCiftiConnectivityMatrixRowColumnIdentification() const { return m_ciftiConnectivityMatrixRowColumnIdentfication; } /** * @return Identification for time-series chart. */ SelectionItemChartTimeSeries* SelectionManager::getChartTimeSeriesIdentification() { return m_chartTimeSeriesIdentification; } /** * @return Identification for time-series chart (const method). */ const SelectionItemChartTimeSeries* SelectionManager::getChartTimeSeriesIdentification() const { return m_chartTimeSeriesIdentification; } /** * Get a description of this object's content. * @return String describing this object's content. */ AString SelectionManager::toString() const { return "SelectionManager"; } /** * @return The last selected item (may be NULL). */ const SelectionItem* SelectionManager::getLastSelectedItem() const { return m_lastSelectedItem; } /** * Set the last selected item to the given item. * * @param lastItem * The last item that was selected; */ void SelectionManager::setLastSelectedItem(const SelectionItem* lastItem) { if (m_lastSelectedItem != NULL) { delete m_lastSelectedItem; } m_lastSelectedItem = NULL; if (lastItem != NULL) { const SelectionItemSurfaceNode* nodeID = dynamic_cast(lastItem); const SelectionItemVoxel* voxelID = dynamic_cast(lastItem); if (nodeID != NULL) { m_lastSelectedItem = new SelectionItemSurfaceNode(*nodeID); } else if (voxelID != NULL) { m_lastSelectedItem = new SelectionItemVoxel(*voxelID); } else { CaretAssertMessage(0, ("Unsupported last ID type" + lastItem->toString())); } } } connectome-workbench-1.2.3+git41-gc4c6c90/src/Brain/SelectionManager.h000066400000000000000000000175161300200146000252020ustar00rootroot00000000000000#ifndef __SELECTION_MANAGER__H_ #define __SELECTION_MANAGER__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 "EventListenerInterface.h" namespace caret { class Annotation; class Brain; class BrowserTabContent; class SelectionItemAnnotation; class SelectionItem; class SelectionItemBorderSurface; class SelectionItemChartDataSeries; class SelectionItemChartFrequencySeries; class SelectionItemChartMatrix; class SelectionItemChartTimeSeries; class SelectionItemCiftiConnectivityMatrixRowColumn; class SelectionItemFocusSurface; class SelectionItemFocusVolume; class SelectionItemImage; class SelectionItemImageControlPoint; class SelectionItemSurfaceNode; class SelectionItemSurfaceNodeIdentificationSymbol; class SelectionItemSurfaceTriangle; class SelectionItemVoxel; class SelectionItemVoxelEditing; class SelectionItemVoxelIdentificationSymbol; class IdentificationTextGenerator; class Surface; class SelectionManager : public CaretObject, public EventListenerInterface { public: SelectionManager(); virtual ~SelectionManager(); void receiveEvent(Event* event); void reset(); SelectionItemAnnotation* getAnnotationIdentification(); const SelectionItemAnnotation* getAnnotationIdentification() const; SelectionItemBorderSurface* getSurfaceBorderIdentification(); const SelectionItemBorderSurface* getSurfaceBorderIdentification() const; SelectionItemFocusSurface* getSurfaceFocusIdentification(); const SelectionItemFocusSurface* getSurfaceFocusIdentification() const; SelectionItemFocusVolume* getVolumeFocusIdentification(); const SelectionItemFocusVolume* getVolumeFocusIdentification() const; SelectionItemImage* getImageIdentification(); const SelectionItemImage* getImageIdentification() const; SelectionItemImageControlPoint* getImageControlPointIdentification(); const SelectionItemImageControlPoint* getImageControlPointIdentification() const; SelectionItemSurfaceNode* getSurfaceNodeIdentification(); const SelectionItemSurfaceNode* getSurfaceNodeIdentification() const; SelectionItemSurfaceNodeIdentificationSymbol* getSurfaceNodeIdentificationSymbol(); const SelectionItemSurfaceNodeIdentificationSymbol* getSurfaceNodeIdentificationSymbol() const; SelectionItemSurfaceTriangle* getSurfaceTriangleIdentification(); const SelectionItemSurfaceTriangle* getSurfaceTriangleIdentification() const; const SelectionItemVoxel* getVoxelIdentification() const; SelectionItemVoxel* getVoxelIdentification(); SelectionItemVoxelIdentificationSymbol* getVoxelIdentificationSymbol(); const SelectionItemVoxelIdentificationSymbol* getVoxelIdentificationSymbol() const; SelectionItemVoxelEditing* getVoxelEditingIdentification(); const SelectionItemVoxelEditing* getVoxelEditingIdentification() const; SelectionItemChartDataSeries* getChartDataSeriesIdentification(); const SelectionItemChartDataSeries* getChartDataSeriesIdentification() const; SelectionItemChartFrequencySeries* getChartFrequencySeriesIdentification(); const SelectionItemChartFrequencySeries* getChartFrequencySeriesIdentification() const; SelectionItemChartMatrix* getChartMatrixIdentification(); const SelectionItemChartMatrix* getChartMatrixIdentification() const; SelectionItemChartTimeSeries* getChartTimeSeriesIdentification(); const SelectionItemChartTimeSeries* getChartTimeSeriesIdentification() const; SelectionItemCiftiConnectivityMatrixRowColumn* getCiftiConnectivityMatrixRowColumnIdentification(); const SelectionItemCiftiConnectivityMatrixRowColumn* getCiftiConnectivityMatrixRowColumnIdentification() const; AString getIdentificationText(const Brain* brain) const; void filterSelections(const bool applySelectionBackgroundFiltering); void clearDistantSelections(); void clearOtherSelectedItems(SelectionItem* selectedItem); const SelectionItem* getLastSelectedItem() const; void setLastSelectedItem(const SelectionItem* lastItem); void setAllSelectionsEnabled(const bool status); private: SelectionManager(const SelectionManager&); SelectionManager& operator=(const SelectionManager&); public: virtual AString toString() const; private: SelectionItem* getMinimumDepthFromMultipleSelections(std::vector items) const; /** ALL items */ std::vector m_allSelectionItems; /** Layered items (foci, borders, etc.) */ std::vector m_layeredSelectedItems; /** Surface items (nodes, triangles) */ std::vector m_surfaceSelectedItems; /** Volume items */ std::vector m_volumeSelectedItems; SelectionItemAnnotation* m_annotationIdentification; SelectionItemBorderSurface* m_surfaceBorderIdentification; SelectionItemChartDataSeries* m_chartDataSeriesIdentification; SelectionItemChartFrequencySeries* m_chartDataFrequencyIdentification; SelectionItemChartMatrix* m_chartMatrixIdentification; SelectionItemChartTimeSeries* m_chartTimeSeriesIdentification; SelectionItemCiftiConnectivityMatrixRowColumn* m_ciftiConnectivityMatrixRowColumnIdentfication; SelectionItemFocusSurface* m_surfaceFocusIdentification; SelectionItemFocusVolume* m_volumeFocusIdentification; SelectionItemImage* m_imageIdentification; SelectionItemImageControlPoint* m_imageControlPointIdentification; SelectionItemSurfaceNode* m_surfaceNodeIdentification; SelectionItemSurfaceNodeIdentificationSymbol* m_surfaceNodeIdentificationSymbol; SelectionItemSurfaceTriangle* m_surfaceTriangleIdentification; IdentificationTextGenerator* m_idTextGenerator; SelectionItemVoxel* m_voxelIdentification; SelectionItemVoxelIdentificationSymbol* m_voxelIdentificationSymbol; SelectionItemVoxelEditing* m_voxelEditingIdentification; /** Last selected item DOES NOT GET PUT IN m_allSelectionItems */ SelectionItem* m_lastSelectedItem; }; #ifdef __SELECTION_MANAGER_DECLARE__ // #endif // __SELECTION_MANAGER_DECLARE__ } // namespace #endif //__SELECTION_MANAGER__H_ connectome-workbench-1.2.3+git41-gc4c6c90/src/Brain/SessionManager.cxx000066400000000000000000000717611300200146000252550ustar00rootroot00000000000000 /*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 __SESSION_MANAGER_DECLARE__ #include "SessionManager.h" #undef __SESSION_MANAGER_DECLARE__ #include "ApplicationInformation.h" #include "BackgroundAndForegroundColorsSceneHelper.h" #include "Brain.h" #include "BrowserTabContent.h" #include "CaretAssert.h" #include "CaretLogger.h" #include "CaretPreferences.h" #include "CiftiConnectivityMatrixDataFileManager.h" #include "CiftiFiberTrajectoryManager.h" #include "ElapsedTimer.h" #include "EventManager.h" #include "EventBrowserTabDelete.h" #include "EventBrowserTabGet.h" #include "EventBrowserTabGetAll.h" #include "EventBrowserTabIndicesGetAll.h" #include "EventBrowserTabNew.h" #include "EventModelAdd.h" #include "EventModelDelete.h" #include "EventModelGetAll.h" #include "EventProgressUpdate.h" #include "ImageCaptureSettings.h" #include "LogManager.h" #include "MapYokingGroupEnum.h" #include "ModelWholeBrain.h" #include "SceneAttributes.h" #include "SceneClass.h" #include "SceneClassArray.h" #include "ScenePrimitiveArray.h" #include "VolumeSurfaceOutlineSetModel.h" using namespace caret; /** * Constructor. */ SessionManager::SessionManager() : CaretObject(), EventListenerInterface(), SceneableInterface() { m_caretPreferences = new CaretPreferences(); m_imageCaptureDialogSettings = new ImageCaptureSettings(); m_ciftiConnectivityMatrixDataFileManager = new CiftiConnectivityMatrixDataFileManager(); m_ciftiFiberTrajectoryManager = new CiftiFiberTrajectoryManager(); for (int32_t i = 0; i < BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS; i++) { m_browserTabs[i] = NULL; } 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_MODEL_ADD); EventManager::get()->addEventListener(this, EventTypeEnum::EVENT_MODEL_DELETE); EventManager::get()->addEventListener(this, EventTypeEnum::EVENT_MODEL_GET_ALL); Brain* brain = new Brain(); m_brains.push_back(brain); } /** * Destructor. */ SessionManager::~SessionManager() { /* * Delete browser tab content. Workbench requests deletion of tab content * as browser tabs are closed. However, command line does not issue * commands to delete tabs so this code will do so. */ for (int32_t i = 0; i < BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS; i++) { if (m_browserTabs[i] != NULL) { delete m_browserTabs[i]; m_browserTabs[i] = NULL; } } int32_t numberOfBrains = getNumberOfBrains(); for (int32_t i = (numberOfBrains - 1); i >= 0; i--) { delete m_brains[i]; } m_brains.clear(); EventManager::get()->removeAllEventsFromListener(this); delete m_ciftiConnectivityMatrixDataFileManager; delete m_ciftiFiberTrajectoryManager; delete m_imageCaptureDialogSettings; delete m_caretPreferences; } /** * Create the session manager. * This must be called one AND ONLY one time prior to any * other Caret mechanisms. * * @param applicationType * The type of application (command line or GUI). */ void SessionManager::createSessionManager(const ApplicationTypeEnum::Enum applicationType) { CaretAssertMessage((s_singletonSessionManager == NULL), "Session manager has already been created."); /* * Set the type of application. */ ApplicationInformation::setApplicationType(applicationType); /* * Create log manager. */ LogManager::createLogManager(); /* * Create event manager first. */ EventManager::createEventManager(); /* * Create session manager. */ s_singletonSessionManager = new SessionManager(); } /** * Delete the session manager. * This may only be called one time after session manager is created. */ void SessionManager::deleteSessionManager() { CaretAssertMessage((s_singletonSessionManager != NULL), "Session manager does not exist, cannot delete it."); delete s_singletonSessionManager; s_singletonSessionManager = NULL; /* * Session manager must be deleted before the event * manager is deleted. */ EventManager::deleteEventManager(); LogManager::deleteLogManager(); } /** * Get the one and only session manager. * * @return Pointer to the session manager. */ SessionManager* SessionManager::get() { CaretAssertMessage(s_singletonSessionManager, "Session manager was not created.\n" "It must be created with SessionManager::createSessionManager()."); return s_singletonSessionManager; } /** * Add a brain to this session. * In most cases, there is one brain per subject. * * @param shareDisplayPropertiesFlag * If true display properties are shared which * is appropriate for the GUI version of Caret. * For SumsDB, false may be appropriate to keep * each subject independent of the other. * * @return New brain that was added. */ Brain* SessionManager::addBrain(const bool /*shareDisplayPropertiesFlag*/) { CaretAssertMessage(0, "Adding brains not implemented at this time."); return NULL; } /** * Get the number of brains. * There is always at least one brain. * * @return Number of brains. */ int32_t SessionManager::getNumberOfBrains() const { return m_brains.size(); } /** * Get the brain at the specified index. * There is always one brain so passing * zero as the index will always work. * * @param brainIndex * Index of brain. * @return * Brain at specified index. */ Brain* SessionManager::getBrain(const int32_t brainIndex) const { CaretAssertVectorIndex(m_brains, brainIndex); return m_brains[brainIndex]; } /** * Get a description of this object's content. * @return String describing this object's content. */ AString SessionManager::toString() const { return "SessionManager"; } /** * Receive events. * * @param event * Event that needs to be processed. */ void SessionManager::receiveEvent(Event* event) { if (event->getEventType() == EventTypeEnum::EVENT_BROWSER_TAB_NEW) { EventBrowserTabNew* tabEvent = dynamic_cast(event); CaretAssert(tabEvent); tabEvent->setEventProcessed(); bool createdTab = false; for (int32_t i = 0; i < BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS; i++) { if (m_browserTabs[i] == NULL) { BrowserTabContent* tab = new BrowserTabContent(i); tab->update(m_models); m_browserTabs[i] = tab; tabEvent->setBrowserTab(tab); createdTab = true; break; } } if (createdTab == false) { tabEvent->setErrorMessage("Workbench is exhausted. It cannot create any more tabs."); } } else if (event->getEventType() == EventTypeEnum::EVENT_BROWSER_TAB_DELETE) { EventBrowserTabDelete* tabEvent = dynamic_cast(event); CaretAssert(tabEvent); BrowserTabContent* tab = tabEvent->getBrowserTab(); for (int32_t i = 0; i < BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS; i++) { if (m_browserTabs[i] == tab) { delete m_browserTabs[i]; m_browserTabs[i] = NULL; tabEvent->setEventProcessed(); break; } } } else if (event->getEventType() == EventTypeEnum::EVENT_BROWSER_TAB_GET) { EventBrowserTabGet* tabEvent = dynamic_cast(event); CaretAssert(tabEvent); tabEvent->setEventProcessed(); const int32_t tabNumber = tabEvent->getTabNumber(); CaretAssertArrayIndex(m_browserTabs, BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS, tabNumber); tabEvent->setBrowserTab(m_browserTabs[tabNumber]); } else if (event->getEventType() == EventTypeEnum::EVENT_BROWSER_TAB_GET_ALL) { EventBrowserTabGetAll* tabEvent = dynamic_cast(event); CaretAssert(tabEvent); tabEvent->setEventProcessed(); for (int32_t i = 0; i < BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS; i++) { if (m_browserTabs[i] != NULL) { tabEvent->addBrowserTab(m_browserTabs[i]); } } } else if (event->getEventType() == EventTypeEnum::EVENT_BROWSER_TAB_INDICES_GET_ALL) { EventBrowserTabIndicesGetAll* tabEvent = dynamic_cast(event); CaretAssert(tabEvent); tabEvent->setEventProcessed(); for (int32_t i = 0; i < BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS; i++) { if (m_browserTabs[i] != NULL) { tabEvent->addBrowserTabIndex(m_browserTabs[i]->getTabNumber()); } } } else if (event->getEventType() == EventTypeEnum::EVENT_MODEL_ADD) { EventModelAdd* addModelsEvent = dynamic_cast(event); CaretAssert(addModelsEvent); addModelsEvent->setEventProcessed(); m_models.push_back(addModelsEvent->getModel()); updateBrowserTabContents(); } else if (event->getEventType() == EventTypeEnum::EVENT_MODEL_DELETE) { EventModelDelete* deleteModelsEvent = dynamic_cast(event); CaretAssert(deleteModelsEvent); deleteModelsEvent->setEventProcessed(); Model* model = deleteModelsEvent->getModel(); std::vector::iterator iter = std::find(m_models.begin(), m_models.end(), model); CaretAssertMessage(iter != m_models.end(), "Trying to delete non-existent model"); m_models.erase(iter); updateBrowserTabContents(); } else if (event->getEventType() == EventTypeEnum::EVENT_MODEL_GET_ALL) { EventModelGetAll* getModelsEvent = dynamic_cast(event); CaretAssert(getModelsEvent); getModelsEvent->setEventProcessed(); getModelsEvent->addModels(m_models); } } /** * Update all of the browser tab contents since the models have changed. */ void SessionManager::updateBrowserTabContents() { for (int32_t i = 0; i < BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS; i++) { if (m_browserTabs[i] != NULL) { m_browserTabs[i]->update(m_models); } } } /** * @return The caret preferences */ CaretPreferences* SessionManager::getCaretPreferences() { return m_caretPreferences; } /** * Create a scene for an instance of a class. * * @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. * * @return Pointer to SceneClass object representing the state of * this object. Under some circumstances a NULL pointer may be * returned. Caller will take ownership of returned object. */ SceneClass* SessionManager::saveToScene(const SceneAttributes* sceneAttributes, const AString& instanceName) { switch (sceneAttributes->getSceneType()) { case SceneTypeEnum::SCENE_TYPE_FULL: break; case SceneTypeEnum::SCENE_TYPE_GENERIC: break; } SceneClass* sceneClass = new SceneClass(instanceName, "SessionManager", 1); { /* * Save map yoking groups. */ std::vector mapYokingGroups; MapYokingGroupEnum::getAllEnums(mapYokingGroups); const int32_t numMapYokingGroups = static_cast(mapYokingGroups.size()); if (numMapYokingGroups > 0) { std::vector mapMapSelections(numMapYokingGroups, 1); CaretArray mapMapEnabled(numMapYokingGroups); for (int32_t i = 0; i < numMapYokingGroups; i++) { mapMapEnabled[i] = false; } for (int32_t i = 0; i < numMapYokingGroups; i++) { const MapYokingGroupEnum::Enum enumValue = mapYokingGroups[i]; if (enumValue != MapYokingGroupEnum::MAP_YOKING_GROUP_OFF) { const int32_t groupIndex = MapYokingGroupEnum::toIntegerCode(enumValue); const int32_t mapIndex = MapYokingGroupEnum::getSelectedMapIndex(enumValue); mapMapSelections[groupIndex] = mapIndex; mapMapEnabled[groupIndex] = MapYokingGroupEnum::isEnabled(enumValue); } } sceneClass->addIntegerArray("MapYokingIndexArray", &mapMapSelections[0], numMapYokingGroups); sceneClass->addBooleanArray("MapYokingEnabledArray", &mapMapEnabled[0], numMapYokingGroups); } } /* * Save brains */ std::vector brainSceneClasses; const int32_t numBrains = m_brains.size(); for (int32_t i = 0; i < numBrains; i++) { brainSceneClasses.push_back(m_brains[i]->saveToScene(sceneAttributes, "m_brains")); } sceneClass->addChild(new SceneClassArray("m_brains", brainSceneClasses)); /* * Get the tabs that are to be included in the scene */ const std::vector tabIndicesForScene = sceneAttributes->getIndicesOfTabsForSavingToScene(); /* * Save browser tabs */ std::vector browserTabSceneClasses; for (int32_t tabIndex = 0; tabIndex < BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS; tabIndex++) { if (std::find(tabIndicesForScene.begin(), tabIndicesForScene.end(), tabIndex) != tabIndicesForScene.end()) { BrowserTabContent* btc = m_browserTabs[tabIndex]; if (btc != NULL) { browserTabSceneClasses.push_back(btc->saveToScene(sceneAttributes, "m_browserTabs")); } } } sceneClass->addChild(new SceneClassArray("m_browserTabs", browserTabSceneClasses)); sceneClass->addChild(m_imageCaptureDialogSettings->saveToScene(sceneAttributes, "m_imageCaptureDialogSettings")); /* * Save the background and foreground colors to the scene */ const BackgroundAndForegroundColors* colorsPointer = m_caretPreferences->getBackgroundAndForegroundColors(); CaretAssert(colorsPointer); BackgroundAndForegroundColors colors(*colorsPointer); BackgroundAndForegroundColorsSceneHelper colorHelper(colors); sceneClass->addChild(colorHelper.saveToScene(sceneAttributes, "backgroundAndForegroundColors")); return sceneClass; } /** * Restore the state of an instance of a class. * * @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. May be NULL for some types of scenes. */ void SessionManager::restoreFromScene(const SceneAttributes* sceneAttributes, const SceneClass* sceneClass) { /* * Default to user preferences for colors */ m_caretPreferences->setBackgroundAndForegroundColorsMode(BackgroundAndForegroundColorsModeEnum::USER_PREFERENCES); if (sceneClass == NULL) { return; } int32_t progressCounter = 0; const int32_t PROGRESS_RESTORING_BRAIN = progressCounter++; const int32_t PROGRESS_RESTORING_TABS = progressCounter++; const int32_t PROGRESS_RESTORING_GUI = progressCounter++; const int32_t PROGRESS_RESTORING_TOTAL = progressCounter++; ElapsedTimer timer; timer.start(); EventProgressUpdate progressEvent(0, PROGRESS_RESTORING_TOTAL, PROGRESS_RESTORING_BRAIN, "Restoring Brain"); EventManager::get()->sendEvent(progressEvent.getPointer()); if (progressEvent.isCancelled()) { resetBrains(true); return; } switch (sceneAttributes->getSceneType()) { case SceneTypeEnum::SCENE_TYPE_FULL: break; case SceneTypeEnum::SCENE_TYPE_GENERIC: break; } { /* * Restore map yoking groups */ std::vector mapYokingGroups; MapYokingGroupEnum::getAllEnums(mapYokingGroups); const int32_t numMapYokingGroups = static_cast(mapYokingGroups.size()); if (numMapYokingGroups > 0) { std::vector mapIndexSelections(numMapYokingGroups, 1); sceneClass->getIntegerArrayValue("MapYokingIndexArray", &mapIndexSelections[0], numMapYokingGroups, 1); for (int32_t i = 0; i < numMapYokingGroups; i++) { bool isValid = false; const MapYokingGroupEnum::Enum enumValue = MapYokingGroupEnum::fromIntegerCode(i, &isValid); if (enumValue != MapYokingGroupEnum::MAP_YOKING_GROUP_OFF) { if (isValid) { MapYokingGroupEnum::setSelectedMapIndex(enumValue, mapIndexSelections[i]); } else { MapYokingGroupEnum::setSelectedMapIndex(enumValue, 1); } } } const ScenePrimitiveArray* mapEnabledArray = sceneClass->getPrimitiveArray("MapYokingEnabledArray"); if (mapEnabledArray != NULL) { for (int32_t i = 0; i < numMapYokingGroups; i++) { bool isValid = false; const MapYokingGroupEnum::Enum enumValue = MapYokingGroupEnum::fromIntegerCode(i, &isValid); if (enumValue != MapYokingGroupEnum::MAP_YOKING_GROUP_OFF) { /* * Enabled status was added on 10/17/2014. * Previous to this data, there was no enabled status and * we will assume enabled status was on. */ bool enabledStatus = true; if (isValid) { if (mapEnabledArray != NULL) { if (i < mapEnabledArray->getNumberOfArrayElements()) { enabledStatus = mapEnabledArray->booleanValue(i); } } } MapYokingGroupEnum::setEnabled(enumValue, enabledStatus); } } } } } { /* * Restore overlay yoking groups * Note: Overlay yoking groups were replaced by MapYokingGroupEnum * So if overlay yoking group is found, the scene was created * before map yoking groups. */ const ScenePrimitiveArray* overlayEnabledArray = sceneClass->getPrimitiveArray("OverlayYokingEnabledArray"); const ScenePrimitiveArray* mapIndexArray = sceneClass->getPrimitiveArray("OverlayYokingGroupArray"); if (mapIndexArray != NULL) { const int32_t numMapIndexElements = mapIndexArray->getNumberOfArrayElements(); for (int32_t i = 0; i < numMapIndexElements; i++) { bool isValid = false; const MapYokingGroupEnum::Enum enumValue = MapYokingGroupEnum::fromIntegerCode(i, &isValid); if (enumValue != MapYokingGroupEnum::MAP_YOKING_GROUP_OFF) { if (isValid) { MapYokingGroupEnum::setSelectedMapIndex(enumValue, mapIndexArray->integerValue(i)); } else { MapYokingGroupEnum::setSelectedMapIndex(enumValue, 1); } } } if (overlayEnabledArray != NULL) { const int32_t numMapEnabledElements = overlayEnabledArray->getNumberOfArrayElements(); for (int32_t i = 0; i < numMapEnabledElements; i++) { bool isValid = false; const MapYokingGroupEnum::Enum enumValue = MapYokingGroupEnum::fromIntegerCode(i, &isValid); if (enumValue != MapYokingGroupEnum::MAP_YOKING_GROUP_OFF) { /* * Enabled status was added on 10/17/2014. * Previous to this data, there was no enabled status and * we will assume enabled status was on. */ bool enabledStatus = true; if (isValid) { if (overlayEnabledArray != NULL) { if (i < overlayEnabledArray->getNumberOfArrayElements()) { enabledStatus = overlayEnabledArray->booleanValue(i); } } } MapYokingGroupEnum::setEnabled(enumValue, enabledStatus); } } } } } /* * Restore brains */ const SceneClassArray* brainArray = sceneClass->getClassArray("m_brains"); const int32_t numBrainClasses = brainArray->getNumberOfArrayElements(); for (int32_t i = 0; i < numBrainClasses; i++) { const SceneClass* brainClass = brainArray->getClassAtIndex(i); if (i < static_cast(m_brains.size())) { m_brains[i]->restoreFromScene(sceneAttributes, brainClass); } else { Brain* brain = new Brain(); brain->restoreFromScene(sceneAttributes, brainClass); } } CaretLogFine("Time to restore brain was " + QString::number(timer.getElapsedTimeSeconds(), 'f', 3) + " seconds"); timer.reset(); progressEvent.setProgress(PROGRESS_RESTORING_TABS, "Restoring Content of Browser Tabs"); EventManager::get()->sendEvent(progressEvent.getPointer()); if (progressEvent.isCancelled()) { resetBrains(true); return; } /* * Remove all tabs */ for (int32_t iTab = 0; iTab < BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS; iTab++) { if (m_browserTabs[iTab] != NULL) { delete m_browserTabs[iTab]; m_browserTabs[iTab] = NULL; } } /* * Restore tabs */ const SceneClassArray* browserTabArray = sceneClass->getClassArray("m_browserTabs"); const int32_t numBrowserTabClasses = browserTabArray->getNumberOfArrayElements(); for (int32_t i = 0; i < numBrowserTabClasses; i++) { const SceneClass* browserTabClass = browserTabArray->getClassAtIndex(i); BrowserTabContent* tab = new BrowserTabContent(i); tab->update(m_models); tab->getVolumeSurfaceOutlineSet()->selectSurfacesAfterSpecFileLoaded(m_brains[0], false); tab->restoreFromScene(sceneAttributes, browserTabClass); const int32_t tabIndex = tab->getTabNumber(); CaretAssert(tabIndex >= 0); m_browserTabs[tabIndex] = tab; } /* * Restore foreground and background colors to scene foreground and background colors */ if (sceneAttributes->isUseSceneForegroundAndBackgroundColors()) { BackgroundAndForegroundColors colors; BackgroundAndForegroundColorsSceneHelper colorHelper(colors); colorHelper.restoreFromScene(sceneAttributes, sceneClass->getClass("backgroundAndForegroundColors")); if (colorHelper.wasRestoredFromScene()) { m_caretPreferences->setBackgroundAndForegroundColorsMode(BackgroundAndForegroundColorsModeEnum::SCENE); m_caretPreferences->setSceneBackgroundAndForegroundColors(colors); } else { m_caretPreferences->setBackgroundAndForegroundColorsMode(BackgroundAndForegroundColorsModeEnum::USER_PREFERENCES); } } m_imageCaptureDialogSettings->restoreFromScene(sceneAttributes, sceneClass->getClass("m_imageCaptureDialogSettings")); CaretLogFine("Time to restore browser tab content was " + QString::number(timer.getElapsedTimeSeconds(), 'f', 3) + " seconds"); timer.reset(); progressEvent.setProgress(PROGRESS_RESTORING_GUI, "Restoring Graphical User Interface"); EventManager::get()->sendEvent(progressEvent.getPointer()); if (progressEvent.isCancelled()) { resetBrains(true); return; } } /** * Reset the first brain and remove all other brains. */ void SessionManager::resetBrains(const bool keepSceneFiles) { const int32_t numBrains = static_cast(m_brains.size()); for (int32_t i = 0; i < numBrains; i++) { if (i > 0) { delete m_brains[i]; } else if (keepSceneFiles) { m_brains[i]->resetBrainKeepSceneFiles(); } else { m_brains[i]->resetBrain(); } } if (numBrains > 1) { m_brains.resize(1); } } /** * @param The CIFTI connectivity matrix data file manager */ CiftiConnectivityMatrixDataFileManager* SessionManager::getCiftiConnectivityMatrixDataFileManager() { return m_ciftiConnectivityMatrixDataFileManager; } /** * @param The CIFTI connectivity matrix data file manager */ const CiftiConnectivityMatrixDataFileManager* SessionManager::getCiftiConnectivityMatrixDataFileManager() const { return m_ciftiConnectivityMatrixDataFileManager; } /** * @return The CIFTI Fiber Trajectory Manager */ CiftiFiberTrajectoryManager* SessionManager::getCiftiFiberTrajectoryManager() { return m_ciftiFiberTrajectoryManager; } /** * @return The CIFTI Fiber Trajectory Manager (const method) */ const CiftiFiberTrajectoryManager* SessionManager::getCiftiFiberTrajectoryManager() const { return m_ciftiFiberTrajectoryManager; } /** * @return Image capture settings for image capture dialog. */ ImageCaptureSettings* SessionManager::getImageCaptureDialogSettings() { return m_imageCaptureDialogSettings; } /** * @return Image capture settings for image capture dialog (const method) */ const ImageCaptureSettings* SessionManager::getImageCaptureDialogSettings() const { return m_imageCaptureDialogSettings; } connectome-workbench-1.2.3+git41-gc4c6c90/src/Brain/SessionManager.h000066400000000000000000000103341300200146000246670ustar00rootroot00000000000000#ifndef __SESSION_MANAGER__H_ #define __SESSION_MANAGER__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 "ApplicationTypeEnum.h" #include "BrainConstants.h" #include "CaretObject.h" #include "EventListenerInterface.h" #include "SceneableInterface.h" namespace caret { class Brain; class BrowserTabContent; class CaretPreferences; class CiftiConnectivityMatrixDataFileManager; class CiftiFiberTrajectoryManager; class ImageCaptureSettings; class Model; /// Manages a Caret session which contains 'global' brain data. class SessionManager : public CaretObject, public EventListenerInterface, public SceneableInterface { public: static void createSessionManager(const ApplicationTypeEnum::Enum applicationType); static void deleteSessionManager(); static SessionManager* get(); void receiveEvent(Event* event); Brain* addBrain(const bool shareDisplayPropertiesFlag); int32_t getNumberOfBrains() const; Brain* getBrain(const int32_t brainIndex) const; CaretPreferences* getCaretPreferences(); CiftiConnectivityMatrixDataFileManager* getCiftiConnectivityMatrixDataFileManager(); const CiftiConnectivityMatrixDataFileManager* getCiftiConnectivityMatrixDataFileManager() const; CiftiFiberTrajectoryManager* getCiftiFiberTrajectoryManager(); const CiftiFiberTrajectoryManager* getCiftiFiberTrajectoryManager() const; ImageCaptureSettings* getImageCaptureDialogSettings(); const ImageCaptureSettings* getImageCaptureDialogSettings() const; virtual SceneClass* saveToScene(const SceneAttributes* sceneAttributes, const AString& instanceName); virtual void restoreFromScene(const SceneAttributes* sceneAttributes, const SceneClass* sceneClass); private: SessionManager(); virtual ~SessionManager(); SessionManager(const SessionManager&); SessionManager& operator=(const SessionManager&); public: virtual AString toString() const; private: void updateBrowserTabContents(); void resetBrains(const bool keepSceneFiles); /** The session manager */ static SessionManager* s_singletonSessionManager; /** The browser tabs */ BrowserTabContent* m_browserTabs[BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS]; /** Holds valid models */ std::vector m_models; /** Holds all loaded brains */ std::vector m_brains; /** Caret's preferences */ CaretPreferences* m_caretPreferences; /** Loads connectivity matrix data */ CiftiConnectivityMatrixDataFileManager* m_ciftiConnectivityMatrixDataFileManager; /** Loads fiber trajectory data */ CiftiFiberTrajectoryManager* m_ciftiFiberTrajectoryManager; /** Settings for image capture dialog */ ImageCaptureSettings* m_imageCaptureDialogSettings; }; #ifdef __SESSION_MANAGER_DECLARE__ SessionManager* SessionManager::s_singletonSessionManager = NULL; #endif // __SESSION_MANAGER_DECLARE__ } // namespace #endif //__SESSION_MANAGER__H_ connectome-workbench-1.2.3+git41-gc4c6c90/src/Brain/Surface.cxx000066400000000000000000000056271300200146000237250ustar00rootroot00000000000000/*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 "BoundingBox.h" #include "BrainStructure.h" #include "Surface.h" using namespace caret; /** * Constructor. */ Surface::Surface() { this->initializeMemberSurface(); } /** * Destructor. */ Surface::~Surface() { } /** * Copy constructor. * * @param s * Surface that is copied. */ Surface::Surface(const Surface& s) : SurfaceFile(s) { this->copyHelperSurface(s); } /** * Assignment operator. * * @param s * Contents of "s" are assigned to this. */ Surface& Surface::operator=(const Surface& s) { if (this != &s) { SurfaceFile::operator=(s); this->copyHelperSurface(s); } return *this; } /** * */ AString Surface::getNameForGUI(bool includeStructureFlag) const { AString msg; if (includeStructureFlag) { msg += StructureEnum::toGuiName(this->getStructure()); msg += " "; } msg += SurfaceTypeEnum::toGuiName(this->getSurfaceType()); msg += " "; msg += this->getFileNameNoPath(); return msg; } /** * Set a bounding box using this surface's coordinates. * * @param boundingBoxOut * Bounding box that is updated. */ void Surface::getBounds(BoundingBox& boundingBoxOut) const { boundingBoxOut.set(this->getCoordinate(0), this->getNumberOfNodes()); } /** * Initialize members of this class. */ void Surface::initializeMemberSurface() { this->brainStructure = NULL; } /** * Helps with copying this surface. */ void Surface::copyHelperSurface(const Surface& /*s*/) { this->initializeMemberSurface(); this->computeNormals(); } /** * @return Brain structure that holds this surface. */ const BrainStructure* Surface::getBrainStructure() const { return this->brainStructure; } /** * @return Brain structure that holds this surface. */ BrainStructure* Surface::getBrainStructure() { return this->brainStructure; } /** * Set brain structure that holds this surface. * @param brainStructure * New value for brain structure. */ void Surface::setBrainStructure(BrainStructure* brainStructure) { this->brainStructure = brainStructure; } connectome-workbench-1.2.3+git41-gc4c6c90/src/Brain/Surface.h000066400000000000000000000034251300200146000233440ustar00rootroot00000000000000 #ifndef __SURFACE_H__ #define __SURFACE_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 "SurfaceFile.h" namespace caret { class BoundingBox; class BrainStructure; /** * Maintains view of some type of object. */ class Surface : public SurfaceFile { public: Surface(); ~Surface(); Surface(const Surface& s); Surface& operator=(const Surface& s); AString getNameForGUI(bool includeStructureFlag) const; void getBounds(BoundingBox& boundingBoxOut) const; const BrainStructure* getBrainStructure() const; BrainStructure* getBrainStructure(); void setBrainStructure(BrainStructure* brainStructure); private: void initializeMemberSurface(); void copyHelperSurface(const Surface& s); BrainStructure* brainStructure; }; } // namespace #endif // __SURFACE_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Brain/SurfaceDrawingTypeEnum.cxx000066400000000000000000000227521300200146000267260ustar00rootroot00000000000000 /*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 __SURFACE_DRAWING_TYPE_ENUM_DECLARE__ #include "SurfaceDrawingTypeEnum.h" #undef __SURFACE_DRAWING_TYPE_ENUM_DECLARE__ #include "CaretAssert.h" using namespace caret; /** * \class caret::SurfaceDrawingTypeEnum * \brief * * */ /** * Constructor. * * @param enumValue * An enumerated value. * @param name * Name of enumerated value. * * @param guiName * User-friendly name for use in user-interface. */ SurfaceDrawingTypeEnum::SurfaceDrawingTypeEnum(const Enum enumValue, const AString& name, const AString& guiName) { this->enumValue = enumValue; this->integerCode = integerCodeCounter++; this->name = name; this->guiName = guiName; } /** * Destructor. */ SurfaceDrawingTypeEnum::~SurfaceDrawingTypeEnum() { } /** * Initialize the enumerated metadata. */ void SurfaceDrawingTypeEnum::initialize() { if (initializedFlag) { return; } initializedFlag = true; enumData.push_back(SurfaceDrawingTypeEnum(DRAW_HIDE, "DRAW_HIDE", "Hide")); enumData.push_back(SurfaceDrawingTypeEnum(DRAW_AS_LINKS, "DRAW_AS_LINKS", "Links (Edges)")); enumData.push_back(SurfaceDrawingTypeEnum(DRAW_AS_NODES, "DRAW_AS_NODES", "Vertices")); enumData.push_back(SurfaceDrawingTypeEnum(DRAW_AS_TRIANGLES, "DRAW_AS_TRIANGLES", "Triangles")); } /** * 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 SurfaceDrawingTypeEnum* SurfaceDrawingTypeEnum::findData(const Enum enumValue) { if (initializedFlag == false) initialize(); size_t num = enumData.size(); for (size_t i = 0; i < num; i++) { const SurfaceDrawingTypeEnum* 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 SurfaceDrawingTypeEnum::toName(Enum enumValue) { if (initializedFlag == false) initialize(); const SurfaceDrawingTypeEnum* 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. */ SurfaceDrawingTypeEnum::Enum SurfaceDrawingTypeEnum::fromName(const AString& name, bool* isValidOut) { if (initializedFlag == false) initialize(); bool validFlag = false; Enum enumValue = DRAW_AS_TRIANGLES; for (std::vector::iterator iter = enumData.begin(); iter != enumData.end(); iter++) { const SurfaceDrawingTypeEnum& 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 SurfaceDrawingTypeEnum")); } return enumValue; } /** * Get a GUI string representation of the enumerated type. * @param enumValue * Enumerated value. * @return * String representing enumerated value. */ AString SurfaceDrawingTypeEnum::toGuiName(Enum enumValue) { if (initializedFlag == false) initialize(); const SurfaceDrawingTypeEnum* 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. */ SurfaceDrawingTypeEnum::Enum SurfaceDrawingTypeEnum::fromGuiName(const AString& guiName, bool* isValidOut) { if (initializedFlag == false) initialize(); bool validFlag = false; Enum enumValue = DRAW_AS_TRIANGLES; for (std::vector::iterator iter = enumData.begin(); iter != enumData.end(); iter++) { const SurfaceDrawingTypeEnum& 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 SurfaceDrawingTypeEnum")); } return enumValue; } /** * Get the integer code for a data type. * * @return * Integer code for data type. */ int32_t SurfaceDrawingTypeEnum::toIntegerCode(Enum enumValue) { if (initializedFlag == false) initialize(); const SurfaceDrawingTypeEnum* 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. */ SurfaceDrawingTypeEnum::Enum SurfaceDrawingTypeEnum::fromIntegerCode(const int32_t integerCode, bool* isValidOut) { if (initializedFlag == false) initialize(); bool validFlag = false; Enum enumValue = DRAW_AS_TRIANGLES; for (std::vector::iterator iter = enumData.begin(); iter != enumData.end(); iter++) { const SurfaceDrawingTypeEnum& 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 SurfaceDrawingTypeEnum")); } 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 SurfaceDrawingTypeEnum::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 SurfaceDrawingTypeEnum::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(SurfaceDrawingTypeEnum::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 SurfaceDrawingTypeEnum::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(SurfaceDrawingTypeEnum::toGuiName(iter->enumValue)); } if (isSorted) { std::sort(allGuiNames.begin(), allGuiNames.end()); } } connectome-workbench-1.2.3+git41-gc4c6c90/src/Brain/SurfaceDrawingTypeEnum.h000066400000000000000000000063531300200146000263520ustar00rootroot00000000000000#ifndef __SURFACE_DRAWING_TYPE_ENUM__H_ #define __SURFACE_DRAWING_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 SurfaceDrawingTypeEnum { public: /** * Enumerated values. */ enum Enum { /** Do not draw the surface */ DRAW_HIDE, /** Draw as links (edges) */ DRAW_AS_LINKS, /** Draw as nodes */ DRAW_AS_NODES, /** Draw as triangles */ DRAW_AS_TRIANGLES }; ~SurfaceDrawingTypeEnum(); 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: SurfaceDrawingTypeEnum(const Enum enumValue, const AString& name, const AString& guiName); static const SurfaceDrawingTypeEnum* 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 __SURFACE_DRAWING_TYPE_ENUM_DECLARE__ std::vector SurfaceDrawingTypeEnum::enumData; bool SurfaceDrawingTypeEnum::initializedFlag = false; int32_t SurfaceDrawingTypeEnum::integerCodeCounter = 0; #endif // __SURFACE_DRAWING_TYPE_ENUM_DECLARE__ } // namespace #endif //__SURFACE_DRAWING_TYPE_ENUM__H_ connectome-workbench-1.2.3+git41-gc4c6c90/src/Brain/SurfaceMontageConfigurationAbstract.cxx000066400000000000000000000212721300200146000314460ustar00rootroot00000000000000 /*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 __SURFACE_MONTAGE_CONFIGURATION_ABSTRACT_DECLARE__ #include "SurfaceMontageConfigurationAbstract.h" #undef __SURFACE_MONTAGE_CONFIGURATION_ABSTRACT_DECLARE__ #include "CaretAssert.h" #include "OverlaySet.h" #include "SceneClass.h" #include "SceneClassAssistant.h" using namespace caret; /** * \class caret::SurfaceMontageConfiguration * \brief Abstract class for surface montage configurations * \ingroup Brain */ /** * Constructor. * * @param supportsLayoutOrientation * True if the subclass supports layout orientation (landscape/portrait). */ SurfaceMontageConfigurationAbstract::SurfaceMontageConfigurationAbstract(const SurfaceMontageConfigurationTypeEnum::Enum configurationType, const SupportLayoutOrientation supportsLayoutOrientation) : CaretObject(), m_configurationType(configurationType), m_supportsLayoutOrientation(supportsLayoutOrientation) { m_sceneAssistant = new SceneClassAssistant(); m_overlaySet = NULL; m_layoutOrientation = SurfaceMontageLayoutOrientationEnum::LANDSCAPE_LAYOUT_ORIENTATION; /* * Note: Since these members are initialized by the constructor, * they do not need to be saved to the scene: * m_configuration * m_supportsLayoutOrientation * m_surfaceMontageViewports * */ m_sceneAssistant->add("m_layoutOrientation", &m_layoutOrientation); } /** * Destructor. */ SurfaceMontageConfigurationAbstract::~SurfaceMontageConfigurationAbstract() { delete m_sceneAssistant; CaretAssertMessage(m_overlaySet, "Did you forget to call setupOverlaySet() from subclass constructor?"); delete m_overlaySet; } /** * Get a description of this object's content. * @return String describing this object's content. */ AString SurfaceMontageConfigurationAbstract::toString() const { return "SurfaceMontageConfiguration"; } /** * Setup the overlay set for the subclass. * * @param includeSurfaceStructures * Surface structures supported by subclass. */ void SurfaceMontageConfigurationAbstract::setupOverlaySet(const AString& overlaySetName, const int32_t tabIndex, const std::vector& includeSurfaceStructures) { m_overlaySet = new OverlaySet(overlaySetName, tabIndex, includeSurfaceStructures, Overlay::INCLUDE_VOLUME_FILES_NO); m_sceneAssistant->add("m_overlaySet", "OverlaySet", m_overlaySet); } /** * @return The overlay set */ OverlaySet* SurfaceMontageConfigurationAbstract::getOverlaySet() { CaretAssertMessage(m_overlaySet, "Did you forget to call setupOverlaySet() from subclass constructor?"); return m_overlaySet; } /** * @return The overlay set (const method) */ const OverlaySet* SurfaceMontageConfigurationAbstract::getOverlaySet() const { CaretAssertMessage(m_overlaySet, "Did you forget to call setupOverlaySet() from subclass constructor?"); return m_overlaySet; } /** * 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* SurfaceMontageConfigurationAbstract::saveToScene(const SceneAttributes* sceneAttributes, const AString& instanceName) { SceneClass* sceneClass = new SceneClass(instanceName, "SurfaceMontageConfigurationAbstract", 1); m_sceneAssistant->saveMembers(sceneAttributes, sceneClass); saveMembersToScene(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 SurfaceMontageConfigurationAbstract::restoreFromScene(const SceneAttributes* sceneAttributes, const SceneClass* sceneClass) { if (sceneClass == NULL) { return; } m_sceneAssistant->restoreMembers(sceneAttributes, sceneClass); restoreMembersFromScene(sceneAttributes, sceneClass); } /** * @return The configuration type. */ SurfaceMontageConfigurationTypeEnum::Enum SurfaceMontageConfigurationAbstract::getConfigurationType() const { return m_configurationType; } /** * @return Configuration has a layout orientation. */ bool SurfaceMontageConfigurationAbstract::hasLayoutOrientation() const { return (m_supportsLayoutOrientation == SUPPORTS_LAYOUT_ORIENTATION_YES); } /** * @return The selected layout orientation. */ SurfaceMontageLayoutOrientationEnum::Enum SurfaceMontageConfigurationAbstract::getLayoutOrientation() const { return m_layoutOrientation; } /** * Set the layout orientation. * * @param layoutOrientation * New value for layout orientation. */ void SurfaceMontageConfigurationAbstract::setLayoutOrientation(const SurfaceMontageLayoutOrientationEnum::Enum layoutOrientation) { m_layoutOrientation = layoutOrientation; } /** * Get the montage viewports for drawing by OpenGL. The montage viewports * will be updated prior to returning them. OpenGL will update * the viewing dimensions (x, y, width, height) in the returned montage * viewports. * * @param surfaceMontageViewports * The montage viewports. */ void SurfaceMontageConfigurationAbstract::getSurfaceMontageViewportsForDrawing(std::vector& surfaceMontageViewports) { m_surfaceMontageViewports.clear(); updateSurfaceMontageViewports(m_surfaceMontageViewports); for (std::vector::iterator iter = m_surfaceMontageViewports.begin(); iter != m_surfaceMontageViewports.end(); iter++) { SurfaceMontageViewport& svp = *iter; surfaceMontageViewports.push_back(&svp); } } /** * Get the montage viewports that will be used by the mouse transformations. * * @param surfaceMontageViewports * The montage viewports. */ void SurfaceMontageConfigurationAbstract::getSurfaceMontageViewportsForTransformation(std::vector& surfaceMontageViewports) const { surfaceMontageViewports.clear(); for (std::vector::const_iterator iter = m_surfaceMontageViewports.begin(); iter != m_surfaceMontageViewports.end(); iter++) { const SurfaceMontageViewport& svp = *iter; surfaceMontageViewports.push_back(&svp); } } /** * Copy the given configuration to this configurtion. * * @param configuration. * Configuration that is copied. */ void SurfaceMontageConfigurationAbstract::copyConfiguration(SurfaceMontageConfigurationAbstract* configuration) { CaretAssert(m_configurationType == configuration->m_configurationType); m_overlaySet->copyOverlaySet(configuration->m_overlaySet); m_layoutOrientation = configuration->m_layoutOrientation; m_surfaceMontageViewports = configuration->m_surfaceMontageViewports; } connectome-workbench-1.2.3+git41-gc4c6c90/src/Brain/SurfaceMontageConfigurationAbstract.h000066400000000000000000000123071300200146000310720ustar00rootroot00000000000000#ifndef __SURFACE_MONTAGE_CONFIGURATION_ABSTRACT_H__ #define __SURFACE_MONTAGE_CONFIGURATION_ABSTRACT_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 "SceneableInterface.h" #include "StructureEnum.h" #include "SurfaceMontageConfigurationTypeEnum.h" #include "SurfaceMontageLayoutOrientationEnum.h" #include "SurfaceMontageViewport.h" namespace caret { class OverlaySet; class PlainTextStringBuilder; class SceneClassAssistant; class SurfaceMontageViewport; class SurfaceMontageConfigurationAbstract : public CaretObject, public SceneableInterface { public: enum SupportLayoutOrientation { SUPPORTS_LAYOUT_ORIENTATION_YES, SUPPORTS_LAYOUT_ORIENTATION_NO, }; SurfaceMontageConfigurationAbstract(const SurfaceMontageConfigurationTypeEnum::Enum configuration, const SupportLayoutOrientation supportsLayoutOrientation); virtual ~SurfaceMontageConfigurationAbstract(); SurfaceMontageConfigurationTypeEnum::Enum getConfigurationType() const; virtual void initializeSelectedSurfaces() = 0; virtual bool isValid() = 0; OverlaySet* getOverlaySet(); const OverlaySet* getOverlaySet() const; bool hasLayoutOrientation() const; SurfaceMontageLayoutOrientationEnum::Enum getLayoutOrientation() const; void setLayoutOrientation(const SurfaceMontageLayoutOrientationEnum::Enum layoutOrientation); virtual SceneClass* saveToScene(const SceneAttributes* sceneAttributes, const AString& instanceName); virtual void restoreFromScene(const SceneAttributes* sceneAttributes, const SceneClass* sceneClass); void getSurfaceMontageViewportsForDrawing(std::vector& surfaceMontageViewports); void getSurfaceMontageViewportsForTransformation(std::vector& surfaceMontageViewports) const; virtual void copyConfiguration(SurfaceMontageConfigurationAbstract* configuration); private: SurfaceMontageConfigurationAbstract(const SurfaceMontageConfigurationAbstract&); SurfaceMontageConfigurationAbstract& operator=(const SurfaceMontageConfigurationAbstract&); protected: /** * Update the montage viewports using the current selected surfaces and settings. * * @param surfaceMontageViewports * Will be loaded with the montage viewports. */ virtual void updateSurfaceMontageViewports(std::vector& surfaceMontageViewports) = 0; void setupOverlaySet(const AString& overlaySetName, const int32_t tabIndex, const std::vector& includeSurfaceStructures); virtual void saveMembersToScene(const SceneAttributes* sceneAttributes, SceneClass* sceneClass) = 0; virtual void restoreMembersFromScene(const SceneAttributes* sceneAttributes, const SceneClass* sceneClass) = 0; public: // ADD_NEW_METHODS_HERE virtual AString toString() const; virtual void getDescriptionOfContent(PlainTextStringBuilder& descriptionOut) const = 0; virtual void getDisplayedSurfaces(std::vector& surfacesOut) const = 0; private: SceneClassAssistant* m_sceneAssistant; const SurfaceMontageConfigurationTypeEnum::Enum m_configurationType; const SupportLayoutOrientation m_supportsLayoutOrientation; SurfaceMontageLayoutOrientationEnum::Enum m_layoutOrientation; OverlaySet* m_overlaySet; std::vector m_surfaceMontageViewports; // ADD_NEW_MEMBERS_HERE friend class ModelSurfaceMontage; }; #ifdef __SURFACE_MONTAGE_CONFIGURATION_ABSTRACT_DECLARE__ // #endif // __SURFACE_MONTAGE_CONFIGURATION_ABSTRACT_DECLARE__ } // namespace #endif //__SURFACE_MONTAGE_CONFIGURATION_ABSTRACT_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Brain/SurfaceMontageConfigurationCerebellar.cxx000066400000000000000000000560161300200146000317470ustar00rootroot00000000000000 /*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 __SURFACE_MONTAGE_CONFIGURATION_CEREBELLAR_DECLARE__ #include "SurfaceMontageConfigurationCerebellar.h" #undef __SURFACE_MONTAGE_CONFIGURATION_CEREBELLAR_DECLARE__ #include "CaretAssert.h" #include "PlainTextStringBuilder.h" #include "SceneClass.h" #include "SceneClassAssistant.h" #include "Surface.h" #include "SurfaceSelectionModel.h" using namespace caret; /** * \class caret::SurfaceMontageConfigurationCerebellar * \brief Surface montage configuration for cerebellum. * \ingroup Brain */ /** * Constructor. */ SurfaceMontageConfigurationCerebellar::SurfaceMontageConfigurationCerebellar(const int32_t tabIndex) : SurfaceMontageConfigurationAbstract(SurfaceMontageConfigurationTypeEnum::CEREBELLAR_CORTEX_CONFIGURATION, SUPPORTS_LAYOUT_ORIENTATION_YES) { std::vector validSurfaceTypes; SurfaceTypeEnum::getAllEnumsExceptFlat(validSurfaceTypes); m_firstSurfaceSelectionModel = new SurfaceSelectionModel(StructureEnum::CEREBELLUM, validSurfaceTypes); m_secondSurfaceSelectionModel = new SurfaceSelectionModel(StructureEnum::CEREBELLUM, validSurfaceTypes); m_firstSurfaceEnabled = true; m_secondSurfaceEnabled = false; m_dorsalEnabled = true; m_ventralEnabled = true; m_anteriorEnabled = true; m_posteriorEnabled = true; m_sceneAssistant = new SceneClassAssistant(); m_sceneAssistant->add("m_firstSurfaceSelectionModel", "SurfaceSelectionModel", m_firstSurfaceSelectionModel); m_sceneAssistant->add("m_secondSurfaceSelectionModel", "SurfaceSelectionModel", m_secondSurfaceSelectionModel); m_sceneAssistant->add("m_firstSurfaceEnabled", &m_firstSurfaceEnabled); m_sceneAssistant->add("m_secondSurfaceEnabled", &m_secondSurfaceEnabled); m_sceneAssistant->add("m_dorsalEnabled", &m_dorsalEnabled); m_sceneAssistant->add("m_ventralEnabled", &m_ventralEnabled); m_sceneAssistant->add("m_anteriorEnabled", &m_anteriorEnabled); m_sceneAssistant->add("m_posteriorEnabled", &m_posteriorEnabled); std::vector supportedStructures; supportedStructures.push_back(StructureEnum::CEREBELLUM); setupOverlaySet("Cerebellar Montage", tabIndex, supportedStructures); } /** * Destructor. */ SurfaceMontageConfigurationCerebellar::~SurfaceMontageConfigurationCerebellar() { delete m_firstSurfaceSelectionModel; delete m_secondSurfaceSelectionModel; delete m_sceneAssistant; } /** * @return Is this configuration valid? */ bool SurfaceMontageConfigurationCerebellar::isValid() { const bool valid = (getFirstSurfaceSelectionModel()->getSurface() != NULL); return valid; } /** * Initialize the selected surfaces. */ void SurfaceMontageConfigurationCerebellar::initializeSelectedSurfaces() { } /** * @return First surface selection model. */ SurfaceSelectionModel* SurfaceMontageConfigurationCerebellar::getFirstSurfaceSelectionModel() { return m_firstSurfaceSelectionModel; } /** * @return First surface selection model. */ const SurfaceSelectionModel* SurfaceMontageConfigurationCerebellar::getFirstSurfaceSelectionModel() const { return m_firstSurfaceSelectionModel; } /** * @return Second surface selection model. */ SurfaceSelectionModel* SurfaceMontageConfigurationCerebellar::getSecondSurfaceSelectionModel() { return m_secondSurfaceSelectionModel; } /** * @return Second surface selection model. */ const SurfaceSelectionModel* SurfaceMontageConfigurationCerebellar::getSecondSurfaceSelectionModel() const { return m_secondSurfaceSelectionModel; } /** * @return Is first surface enabled. */ bool SurfaceMontageConfigurationCerebellar::isFirstSurfaceEnabled() const { return m_firstSurfaceEnabled; } /** * Set first surface enabled status. * * @param enabled * New enabled status. */ void SurfaceMontageConfigurationCerebellar::setFirstSurfaceEnabled(const bool enabled) { m_firstSurfaceEnabled = enabled; } /** * @return Is second surface enabled. */ bool SurfaceMontageConfigurationCerebellar::isSecondSurfaceEnabled() const { return m_secondSurfaceEnabled; } /** * Set first surface enabled status. * * @param enabled * New enabled status. */ void SurfaceMontageConfigurationCerebellar::setSecondSurfaceEnabled(const bool enabled) { m_secondSurfaceEnabled = enabled; } /** * @return Is dorsal enabled. */ bool SurfaceMontageConfigurationCerebellar::isDorsalEnabled() const { return m_dorsalEnabled; } /** * Set dorsal enabled. * * @param enabled * New enabled status. */ void SurfaceMontageConfigurationCerebellar::setDorsalEnabled(const bool enabled) { m_dorsalEnabled = enabled; } /** * @return Is ventral enabled. */ bool SurfaceMontageConfigurationCerebellar::isVentralEnabled() const { return m_ventralEnabled; } /** * Set ventral enabled. * * @param enabled * New enabled status. */ void SurfaceMontageConfigurationCerebellar::setVentralEnabled(const bool enabled) { m_ventralEnabled = enabled; } /** * @return Is anterior enabled. */ bool SurfaceMontageConfigurationCerebellar::isAnteriorEnabled() const { return m_anteriorEnabled; } /** * Set anterior enabled. * * @param enabled * New enabled status. */ void SurfaceMontageConfigurationCerebellar::setAnteriorEnabled(const bool enabled) { m_anteriorEnabled = enabled; } /** * @return Is posterior enabled. */ bool SurfaceMontageConfigurationCerebellar::isPosteriorEnabled() const { return m_posteriorEnabled; } /** * Set posterior enabled. * * @param enabled * New enabled status. */ void SurfaceMontageConfigurationCerebellar::setPosteriorEnabled(const bool enabled) { m_posteriorEnabled = enabled; } /** * Update the montage viewports using the current selected surfaces and settings. * * @param surfaceMontageViewports * Will be loaded with the montage viewports. */ void SurfaceMontageConfigurationCerebellar::updateSurfaceMontageViewports(std::vector& surfaceMontageViewports) { surfaceMontageViewports.clear(); std::vector anteriorViewports; std::vector dorsalViewports; std::vector posteriorViewports; std::vector ventralViewports; int32_t numFirst = 0; if (m_firstSurfaceEnabled) { Surface* surface = m_firstSurfaceSelectionModel->getSurface(); if (surface != NULL) { if (m_anteriorEnabled) { anteriorViewports.push_back(SurfaceMontageViewport(surface, ProjectionViewTypeEnum::PROJECTION_VIEW_CEREBELLUM_ANTERIOR)); numFirst++; } if (m_dorsalEnabled) { dorsalViewports.push_back(SurfaceMontageViewport(surface, ProjectionViewTypeEnum::PROJECTION_VIEW_CEREBELLUM_DORSAL)); numFirst++; } if (m_posteriorEnabled) { posteriorViewports.push_back(SurfaceMontageViewport(surface, ProjectionViewTypeEnum::PROJECTION_VIEW_CEREBELLUM_POSTERIOR)); numFirst++; } if (m_ventralEnabled) { ventralViewports.push_back(SurfaceMontageViewport(surface, ProjectionViewTypeEnum::PROJECTION_VIEW_CEREBELLUM_VENTRAL)); numFirst++; } } } int32_t numSecond = 0; if (m_secondSurfaceEnabled) { Surface* surface = m_secondSurfaceSelectionModel->getSurface(); if (surface != NULL) { if (m_anteriorEnabled) { anteriorViewports.push_back(SurfaceMontageViewport(surface, ProjectionViewTypeEnum::PROJECTION_VIEW_CEREBELLUM_ANTERIOR)); numSecond++; } if (m_dorsalEnabled) { dorsalViewports.push_back(SurfaceMontageViewport(surface, ProjectionViewTypeEnum::PROJECTION_VIEW_CEREBELLUM_DORSAL)); numSecond++; } if (m_posteriorEnabled) { posteriorViewports.push_back(SurfaceMontageViewport(surface, ProjectionViewTypeEnum::PROJECTION_VIEW_CEREBELLUM_POSTERIOR)); numSecond++; } if (m_ventralEnabled) { ventralViewports.push_back(SurfaceMontageViewport(surface, ProjectionViewTypeEnum::PROJECTION_VIEW_CEREBELLUM_VENTRAL)); numSecond++; } } } std::vector allViewports; allViewports.insert(allViewports.end(), dorsalViewports.begin(), dorsalViewports.end()); allViewports.insert(allViewports.end(), ventralViewports.begin(), ventralViewports.end()); allViewports.insert(allViewports.end(), anteriorViewports.begin(), anteriorViewports.end()); allViewports.insert(allViewports.end(), posteriorViewports.begin(), posteriorViewports.end()); const int32_t totalNum = numFirst + numSecond; CaretAssert(static_cast(allViewports.size()) == totalNum); if (totalNum == 1) { surfaceMontageViewports.insert(surfaceMontageViewports.end(), allViewports.begin(), allViewports.end()); CaretAssert(surfaceMontageViewports.size() == 1); surfaceMontageViewports[0].setRowAndColumn(0, 0); } else if (totalNum == 2) { surfaceMontageViewports.insert(surfaceMontageViewports.end(), allViewports.begin(), allViewports.end()); CaretAssert(surfaceMontageViewports.size() == 2); switch (getLayoutOrientation()) { case SurfaceMontageLayoutOrientationEnum::LANDSCAPE_LAYOUT_ORIENTATION: surfaceMontageViewports[0].setRowAndColumn(0, 0); surfaceMontageViewports[1].setRowAndColumn(0, 1); break; case SurfaceMontageLayoutOrientationEnum::PORTRAIT_LAYOUT_ORIENTATION: surfaceMontageViewports[0].setRowAndColumn(0, 0); surfaceMontageViewports[1].setRowAndColumn(1, 0); break; } } else if (totalNum == 3) { surfaceMontageViewports.insert(surfaceMontageViewports.end(), allViewports.begin(), allViewports.end()); CaretAssert(surfaceMontageViewports.size() == 3); for (int32_t i = 0; i < 3; i++) { SurfaceMontageViewport& svp = surfaceMontageViewports[i]; switch (svp.getProjectionViewType()) { case ProjectionViewTypeEnum::PROJECTION_VIEW_CEREBELLUM_ANTERIOR: svp.setRowAndColumn(1, 0); break; case ProjectionViewTypeEnum::PROJECTION_VIEW_CEREBELLUM_DORSAL: svp.setRowAndColumn(0, 0); break; case ProjectionViewTypeEnum::PROJECTION_VIEW_CEREBELLUM_POSTERIOR: svp.setRowAndColumn(1, 1); break; case ProjectionViewTypeEnum::PROJECTION_VIEW_CEREBELLUM_VENTRAL: svp.setRowAndColumn(0, 1); break; default: CaretAssert(0); } } } else if (totalNum == 4) { if ((numFirst == 4) || (numSecond == 4)){ surfaceMontageViewports.insert(surfaceMontageViewports.end(), allViewports.begin(), allViewports.end()); CaretAssert(surfaceMontageViewports.size() == 4); surfaceMontageViewports[0].setRowAndColumn(0, 0); surfaceMontageViewports[1].setRowAndColumn(0, 1); surfaceMontageViewports[2].setRowAndColumn(1, 0); surfaceMontageViewports[3].setRowAndColumn(1, 1); } else if (numFirst == numSecond) { surfaceMontageViewports.insert(surfaceMontageViewports.end(), allViewports.begin(), allViewports.end()); CaretAssert(surfaceMontageViewports.size() == 4); surfaceMontageViewports[0].setRowAndColumn(0, 0); surfaceMontageViewports[1].setRowAndColumn(0, 1); surfaceMontageViewports[2].setRowAndColumn(1, 0); surfaceMontageViewports[3].setRowAndColumn(1, 1); } else { CaretAssert(0); } } else if (totalNum == 6) { surfaceMontageViewports.insert(surfaceMontageViewports.end(), allViewports.begin(), allViewports.end()); CaretAssert(surfaceMontageViewports.size() == 6); switch (getLayoutOrientation()) { case SurfaceMontageLayoutOrientationEnum::LANDSCAPE_LAYOUT_ORIENTATION: surfaceMontageViewports[0].setRowAndColumn(0, 0); surfaceMontageViewports[1].setRowAndColumn(1, 0); surfaceMontageViewports[2].setRowAndColumn(0, 1); surfaceMontageViewports[3].setRowAndColumn(1, 1); surfaceMontageViewports[4].setRowAndColumn(0, 2); surfaceMontageViewports[5].setRowAndColumn(1, 2); break; case SurfaceMontageLayoutOrientationEnum::PORTRAIT_LAYOUT_ORIENTATION: surfaceMontageViewports[0].setRowAndColumn(0, 0); surfaceMontageViewports[1].setRowAndColumn(0, 1); surfaceMontageViewports[2].setRowAndColumn(1, 0); surfaceMontageViewports[3].setRowAndColumn(1, 1); surfaceMontageViewports[4].setRowAndColumn(2, 0); surfaceMontageViewports[5].setRowAndColumn(2, 1); break; } } else if (totalNum == 8) { surfaceMontageViewports.insert(surfaceMontageViewports.end(), allViewports.begin(), allViewports.end()); CaretAssert(surfaceMontageViewports.size() == 8); switch (getLayoutOrientation()) { case SurfaceMontageLayoutOrientationEnum::LANDSCAPE_LAYOUT_ORIENTATION: surfaceMontageViewports[0].setRowAndColumn(0, 0); surfaceMontageViewports[1].setRowAndColumn(0, 2); surfaceMontageViewports[2].setRowAndColumn(0, 1); surfaceMontageViewports[3].setRowAndColumn(0, 3); surfaceMontageViewports[4].setRowAndColumn(1, 0); surfaceMontageViewports[5].setRowAndColumn(1, 2); surfaceMontageViewports[6].setRowAndColumn(1, 1); surfaceMontageViewports[7].setRowAndColumn(1, 3); break; case SurfaceMontageLayoutOrientationEnum::PORTRAIT_LAYOUT_ORIENTATION: surfaceMontageViewports[0].setRowAndColumn(0, 0); surfaceMontageViewports[1].setRowAndColumn(0, 1); surfaceMontageViewports[2].setRowAndColumn(1, 0); surfaceMontageViewports[3].setRowAndColumn(1, 1); surfaceMontageViewports[4].setRowAndColumn(2, 0); surfaceMontageViewports[5].setRowAndColumn(2, 1); surfaceMontageViewports[6].setRowAndColumn(3, 0); surfaceMontageViewports[7].setRowAndColumn(3, 1); break; } } else if (totalNum > 0) { CaretAssert(0); } // const int32_t numViewports = static_cast(surfaceMontageViewports.size()); // CaretAssert(totalNum == numViewports); // // std::cout << "Orientation: " << SurfaceMontageLayoutOrientationEnum::toName(getLayoutOrientation()) << std::endl; // for (int32_t i = 0; i < numViewports; i++) { // const SurfaceMontageViewport& svp = surfaceMontageViewports[i]; // std::cout << qPrintable("(" // + AString::number(svp.getRow()) // + "," // + AString::number(svp.getColumn()) // + ") " // + ProjectionViewTypeEnum::toName(svp.getProjectionViewType())) // << std::endl; // } // std::cout << std::endl; } /** * Save members to the given scene class. * * @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 information is added. */ void SurfaceMontageConfigurationCerebellar::saveMembersToScene(const SceneAttributes* sceneAttributes, SceneClass* sceneClass) { 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 information is restored. */ void SurfaceMontageConfigurationCerebellar::restoreMembersFromScene(const SceneAttributes* sceneAttributes, const SceneClass* sceneClass) { m_sceneAssistant->restoreMembers(sceneAttributes, sceneClass); } /** * Get a description of this object's content. * @return String describing this object's content. */ AString SurfaceMontageConfigurationCerebellar::toString() const { PlainTextStringBuilder tb; getDescriptionOfContent(tb); return tb.getText(); } /** * Get a text description of the instance's content. * * @param descriptionOut * Description of the instance's content. */ void SurfaceMontageConfigurationCerebellar::getDescriptionOfContent(PlainTextStringBuilder& descriptionOut) const { descriptionOut.addLine("Cerebellar Montage: "); descriptionOut.pushIndentation(); const Surface* firstSurface = getFirstSurfaceSelectionModel()->getSurface(); if (firstSurface != NULL) { if (isFirstSurfaceEnabled()) { descriptionOut.addLine("Surface:"); descriptionOut.pushIndentation(); firstSurface->getDescriptionOfContent(descriptionOut); descriptionOut.popIndentation(); } } const Surface* secondSurface = getSecondSurfaceSelectionModel()->getSurface(); if (secondSurface != NULL) { if (isSecondSurfaceEnabled()) { descriptionOut.addLine("Surface:"); descriptionOut.pushIndentation(); secondSurface->getDescriptionOfContent(descriptionOut); descriptionOut.popIndentation(); } } AString viewsMsg = "Selected Views: "; if (isAnteriorEnabled()) { viewsMsg += " Anterior"; } if (isDorsalEnabled()) { viewsMsg += " Dorsal"; } if (isPosteriorEnabled()) { viewsMsg += " Posterior"; } if (isVentralEnabled()) { viewsMsg += " Ventral"; } descriptionOut.addLine(viewsMsg); descriptionOut.popIndentation(); } /** * Get all surfaces displayed in this configuration. * * @param surfaceOut * Will contain all displayed surfaces upon exit. */ void SurfaceMontageConfigurationCerebellar::getDisplayedSurfaces(std::vector& surfacesOut) const { surfacesOut.clear(); const Surface* firstSurface = getFirstSurfaceSelectionModel()->getSurface(); if (firstSurface != NULL) { if (isFirstSurfaceEnabled()) { surfacesOut.push_back(const_cast(firstSurface)); } } const Surface* secondSurface = getSecondSurfaceSelectionModel()->getSurface(); if (secondSurface != NULL) { if (isSecondSurfaceEnabled()) { if (secondSurface != firstSurface) { surfacesOut.push_back(const_cast(secondSurface)); } } } } /** * Copy the given configuration to this configurtion. * * @param configuration. * Configuration that is copied. */ void SurfaceMontageConfigurationCerebellar::copyConfiguration(SurfaceMontageConfigurationAbstract* configuration) { SurfaceMontageConfigurationCerebellar* cerebellarConfiguration = dynamic_cast(configuration); CaretAssert(cerebellarConfiguration); SurfaceMontageConfigurationAbstract::copyConfiguration(configuration); m_firstSurfaceSelectionModel->setSurface(cerebellarConfiguration->m_firstSurfaceSelectionModel->getSurface()); m_secondSurfaceSelectionModel->setSurface(cerebellarConfiguration->m_secondSurfaceSelectionModel->getSurface()); m_firstSurfaceEnabled = cerebellarConfiguration->m_firstSurfaceEnabled; m_secondSurfaceEnabled = cerebellarConfiguration->m_secondSurfaceEnabled; m_dorsalEnabled = cerebellarConfiguration->m_dorsalEnabled; m_ventralEnabled = cerebellarConfiguration->m_ventralEnabled; m_posteriorEnabled = cerebellarConfiguration->m_posteriorEnabled; m_anteriorEnabled = cerebellarConfiguration->m_anteriorEnabled; } connectome-workbench-1.2.3+git41-gc4c6c90/src/Brain/SurfaceMontageConfigurationCerebellar.h000066400000000000000000000103371300200146000313700ustar00rootroot00000000000000#ifndef __SURFACE_MONTAGE_CONFIGURATION_CEREBELLAR_H__ #define __SURFACE_MONTAGE_CONFIGURATION_CEREBELLAR_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 "SurfaceMontageConfigurationAbstract.h" namespace caret { class SceneClassAssistant; class SurfaceSelectionModel; class SurfaceMontageConfigurationCerebellar : public SurfaceMontageConfigurationAbstract { public: SurfaceMontageConfigurationCerebellar(const int32_t tabIndex); virtual ~SurfaceMontageConfigurationCerebellar(); virtual void initializeSelectedSurfaces(); SurfaceSelectionModel* getFirstSurfaceSelectionModel(); const SurfaceSelectionModel* getFirstSurfaceSelectionModel() const; SurfaceSelectionModel* getSecondSurfaceSelectionModel(); const SurfaceSelectionModel* getSecondSurfaceSelectionModel() const; bool isFirstSurfaceEnabled() const; void setFirstSurfaceEnabled(const bool enabled); bool isSecondSurfaceEnabled() const; void setSecondSurfaceEnabled(const bool enabled); bool isDorsalEnabled() const; void setDorsalEnabled(const bool enabled); bool isVentralEnabled() const; void setVentralEnabled(const bool enabled); bool isAnteriorEnabled() const; void setAnteriorEnabled(const bool enabled); bool isPosteriorEnabled() const; void setPosteriorEnabled(const bool enabled); virtual void updateSurfaceMontageViewports(std::vector& surfaceMontageViewports); virtual bool isValid(); virtual void copyConfiguration(SurfaceMontageConfigurationAbstract* configuration); private: SurfaceMontageConfigurationCerebellar(const SurfaceMontageConfigurationCerebellar&); SurfaceMontageConfigurationCerebellar& operator=(const SurfaceMontageConfigurationCerebellar&); public: virtual AString toString() const; virtual void getDescriptionOfContent(PlainTextStringBuilder& descriptionOut) const; virtual void getDisplayedSurfaces(std::vector& surfacesOut) const; // ADD_NEW_METHODS_HERE protected: virtual void saveMembersToScene(const SceneAttributes* sceneAttributes, SceneClass* sceneClass); virtual void restoreMembersFromScene(const SceneAttributes* sceneAttributes, const SceneClass* sceneClass); private: SceneClassAssistant* m_sceneAssistant; SurfaceSelectionModel* m_firstSurfaceSelectionModel; SurfaceSelectionModel* m_secondSurfaceSelectionModel; bool m_firstSurfaceEnabled; bool m_secondSurfaceEnabled; bool m_dorsalEnabled; bool m_ventralEnabled; bool m_anteriorEnabled; bool m_posteriorEnabled; // ADD_NEW_MEMBERS_HERE friend class ModelSurfaceMontage; }; #ifdef __SURFACE_MONTAGE_CONFIGURATION_CEREBELLAR_DECLARE__ // #endif // __SURFACE_MONTAGE_CONFIGURATION_CEREBELLAR_DECLARE__ } // namespace #endif //__SURFACE_MONTAGE_CONFIGURATION_CEREBELLAR_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Brain/SurfaceMontageConfigurationCerebral.cxx000066400000000000000000000712141300200146000314230ustar00rootroot00000000000000 /*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 __SURFACE_MONTAGE_CONFIGURATION_CEREBRAL_DECLARE__ #include "SurfaceMontageConfigurationCerebral.h" #undef __SURFACE_MONTAGE_CONFIGURATION_CEREBRAL_DECLARE__ #include "BrainStructure.h" #include "CaretAssert.h" #include "EventBrainStructureGetAll.h" #include "EventManager.h" #include "PlainTextStringBuilder.h" #include "SceneClass.h" #include "SceneClassAssistant.h" #include "Surface.h" #include "SurfaceSelectionModel.h" using namespace caret; /** * \class caret::SurfaceMontageConfigurationCerebral * \brief Surface montage cerebral cortext configuration. * \ingroup Brain */ /** * Constructor. */ SurfaceMontageConfigurationCerebral::SurfaceMontageConfigurationCerebral(const int32_t tabIndex) : SurfaceMontageConfigurationAbstract(SurfaceMontageConfigurationTypeEnum::CEREBRAL_CORTEX_CONFIGURATION, SUPPORTS_LAYOUT_ORIENTATION_YES) { std::vector validSurfaceTypes; SurfaceTypeEnum::getAllEnumsExceptFlat(validSurfaceTypes); m_leftFirstSurfaceSelectionModel = new SurfaceSelectionModel(StructureEnum::CORTEX_LEFT, validSurfaceTypes); m_leftSecondSurfaceSelectionModel = new SurfaceSelectionModel(StructureEnum::CORTEX_LEFT, validSurfaceTypes); m_rightFirstSurfaceSelectionModel = new SurfaceSelectionModel(StructureEnum::CORTEX_RIGHT, validSurfaceTypes); m_rightSecondSurfaceSelectionModel = new SurfaceSelectionModel(StructureEnum::CORTEX_RIGHT, validSurfaceTypes); m_leftEnabled = true; m_rightEnabled = true; m_firstSurfaceEnabled = true; m_secondSurfaceEnabled = false; m_lateralEnabled = true; m_medialEnabled = true; m_sceneAssistant = new SceneClassAssistant(); m_sceneAssistant->add("m_leftFirstSurfaceSelectionModel", "SurfaceSelectionModel", m_leftFirstSurfaceSelectionModel); m_sceneAssistant->add("m_leftSecondSurfaceSelectionModel", "SurfaceSelectionModel", m_leftSecondSurfaceSelectionModel); m_sceneAssistant->add("m_rightFirstSurfaceSelectionModel", "SurfaceSelectionModel", m_rightFirstSurfaceSelectionModel); m_sceneAssistant->add("m_rightSecondSurfaceSelectionModel", "SurfaceSelectionModel", m_rightSecondSurfaceSelectionModel); m_sceneAssistant->add("m_leftEnabled", &m_leftEnabled); m_sceneAssistant->add("m_rightEnabled", &m_rightEnabled); m_sceneAssistant->add("m_firstSurfaceEnabled", &m_firstSurfaceEnabled); m_sceneAssistant->add("m_secondSurfaceEnabled", &m_secondSurfaceEnabled); m_sceneAssistant->add("m_lateralEnabled", &m_lateralEnabled); m_sceneAssistant->add("m_medialEnabled", &m_medialEnabled); std::vector supportedStructures; supportedStructures.push_back(StructureEnum::CORTEX_LEFT); supportedStructures.push_back(StructureEnum::CORTEX_RIGHT); setupOverlaySet("Cerebral Montage", tabIndex, supportedStructures); } /** * Destructor. */ SurfaceMontageConfigurationCerebral::~SurfaceMontageConfigurationCerebral() { delete m_leftFirstSurfaceSelectionModel; delete m_leftSecondSurfaceSelectionModel; delete m_rightFirstSurfaceSelectionModel; delete m_rightSecondSurfaceSelectionModel; delete m_sceneAssistant; } /** * Initialize the selected surfaces. */ void SurfaceMontageConfigurationCerebral::initializeSelectedSurfaces() { EventBrainStructureGetAll brainStructureEvent; EventManager::get()->sendEvent(brainStructureEvent.getPointer()); Surface* leftAnatSurface = NULL; BrainStructure* leftBrainStructure = brainStructureEvent.getBrainStructureByStructure(StructureEnum::CORTEX_LEFT); if (leftBrainStructure != NULL) { leftAnatSurface = leftBrainStructure->getPrimaryAnatomicalSurface(); } Surface* rightAnatSurface = NULL; BrainStructure* rightBrainStructure = brainStructureEvent.getBrainStructureByStructure(StructureEnum::CORTEX_RIGHT); if (rightBrainStructure != NULL) { rightAnatSurface = rightBrainStructure->getPrimaryAnatomicalSurface(); } m_leftFirstSurfaceSelectionModel->setSurfaceToType(SurfaceTypeEnum::ANATOMICAL); if (leftAnatSurface != NULL) { m_leftFirstSurfaceSelectionModel->setSurface(leftAnatSurface); } m_leftSecondSurfaceSelectionModel->setSurfaceToType(SurfaceTypeEnum::INFLATED, SurfaceTypeEnum::VERY_INFLATED); m_rightFirstSurfaceSelectionModel->setSurfaceToType(SurfaceTypeEnum::ANATOMICAL); if (rightAnatSurface != NULL) { m_rightFirstSurfaceSelectionModel->setSurface(rightAnatSurface); } m_rightSecondSurfaceSelectionModel->setSurfaceToType(SurfaceTypeEnum::INFLATED, SurfaceTypeEnum::VERY_INFLATED); } /** * @return Is this configuration valid? */ bool SurfaceMontageConfigurationCerebral::isValid() { const bool valid = ((getLeftFirstSurfaceSelectionModel()->getSurface() != NULL) || (getRightFirstSurfaceSelectionModel()->getSurface() != NULL)); return valid; } /** * Update the montage viewports using the current selected surfaces and settings. * * @param surfaceMontageViewports * Will be loaded with the montage viewports. */ void SurfaceMontageConfigurationCerebral::updateSurfaceMontageViewports(std::vector& surfaceMontageViewports) { surfaceMontageViewports.clear(); std::vector leftLateralViewports; std::vector leftMedialViewports; std::vector rightLateralViewports; std::vector rightMedialViewports; if (m_leftEnabled) { if (m_firstSurfaceEnabled) { Surface* leftSurface = m_leftFirstSurfaceSelectionModel->getSurface(); if (leftSurface != NULL) { if (m_lateralEnabled) { SurfaceMontageViewport smv(leftSurface, ProjectionViewTypeEnum::PROJECTION_VIEW_LEFT_LATERAL); leftLateralViewports.push_back(smv); } if (m_medialEnabled) { SurfaceMontageViewport smv(leftSurface, ProjectionViewTypeEnum::PROJECTION_VIEW_LEFT_MEDIAL); leftMedialViewports.push_back(smv); } } } if (m_secondSurfaceEnabled) { Surface* leftSurface = m_leftSecondSurfaceSelectionModel->getSurface(); if (leftSurface != NULL) { if (m_lateralEnabled) { SurfaceMontageViewport smv(leftSurface, ProjectionViewTypeEnum::PROJECTION_VIEW_LEFT_LATERAL); leftLateralViewports.push_back(smv); } if (m_medialEnabled) { SurfaceMontageViewport smv(leftSurface, ProjectionViewTypeEnum::PROJECTION_VIEW_LEFT_MEDIAL); leftMedialViewports.push_back(smv); } } } } if (m_rightEnabled) { if (m_firstSurfaceEnabled) { Surface* rightSurface = m_rightFirstSurfaceSelectionModel->getSurface(); if (rightSurface != NULL) { if (m_lateralEnabled) { SurfaceMontageViewport smv(rightSurface, ProjectionViewTypeEnum::PROJECTION_VIEW_RIGHT_LATERAL); rightLateralViewports.push_back(smv); } if (m_medialEnabled) { SurfaceMontageViewport smv(rightSurface, ProjectionViewTypeEnum::PROJECTION_VIEW_RIGHT_MEDIAL); rightMedialViewports.push_back(smv); } } } } if (m_rightEnabled) { if (m_secondSurfaceEnabled) { Surface* rightSurface = m_rightSecondSurfaceSelectionModel->getSurface(); if (rightSurface != NULL) { if (m_lateralEnabled) { SurfaceMontageViewport smv(rightSurface, ProjectionViewTypeEnum::PROJECTION_VIEW_RIGHT_LATERAL); rightLateralViewports.push_back(smv); } if (m_medialEnabled) { SurfaceMontageViewport smv(rightSurface, ProjectionViewTypeEnum::PROJECTION_VIEW_RIGHT_MEDIAL); rightMedialViewports.push_back(smv); } } } } std::vector leftViewports; leftViewports.insert(leftViewports.end(), leftLateralViewports.begin(), leftLateralViewports.end()); leftViewports.insert(leftViewports.end(), leftMedialViewports.begin(), leftMedialViewports.end()); std::vector rightViewports; rightViewports.insert(rightViewports.end(), rightLateralViewports.begin(), rightLateralViewports.end()); rightViewports.insert(rightViewports.end(), rightMedialViewports.begin(), rightMedialViewports.end()); const int32_t numLeft = static_cast(leftViewports.size()); const int32_t numRight = static_cast(rightViewports.size()); const int32_t totalNum = numLeft + numRight; if (totalNum == 1) { surfaceMontageViewports.insert(surfaceMontageViewports.end(), leftViewports.begin(), leftViewports.end()); surfaceMontageViewports.insert(surfaceMontageViewports.end(), rightViewports.begin(), rightViewports.end()); CaretAssert(surfaceMontageViewports.size() == 1); surfaceMontageViewports[0].setRowAndColumn(0, 0); } else if (totalNum == 2) { surfaceMontageViewports.insert(surfaceMontageViewports.end(), leftViewports.begin(), leftViewports.end()); surfaceMontageViewports.insert(surfaceMontageViewports.end(), rightViewports.begin(), rightViewports.end()); CaretAssert(surfaceMontageViewports.size() == 2); switch (getLayoutOrientation()) { case SurfaceMontageLayoutOrientationEnum::LANDSCAPE_LAYOUT_ORIENTATION: surfaceMontageViewports[0].setRowAndColumn(0, 0); surfaceMontageViewports[1].setRowAndColumn(0, 1); break; case SurfaceMontageLayoutOrientationEnum::PORTRAIT_LAYOUT_ORIENTATION: surfaceMontageViewports[0].setRowAndColumn(0, 0); surfaceMontageViewports[1].setRowAndColumn(1, 0); break; } } else if (totalNum == 4) { if (numLeft == 4) { surfaceMontageViewports.insert(surfaceMontageViewports.end(), leftLateralViewports.begin(), leftLateralViewports.end()); surfaceMontageViewports.insert(surfaceMontageViewports.end(), leftMedialViewports.begin(), leftMedialViewports.end()); CaretAssert(surfaceMontageViewports.size() == 4); surfaceMontageViewports[0].setRowAndColumn(0, 0); surfaceMontageViewports[1].setRowAndColumn(1, 0); surfaceMontageViewports[2].setRowAndColumn(0, 1); surfaceMontageViewports[3].setRowAndColumn(1, 1); } else if (numRight == 4) { surfaceMontageViewports.insert(surfaceMontageViewports.end(), rightLateralViewports.begin(), rightLateralViewports.end()); surfaceMontageViewports.insert(surfaceMontageViewports.end(), rightMedialViewports.begin(), rightMedialViewports.end()); CaretAssert(surfaceMontageViewports.size() == 4); surfaceMontageViewports[0].setRowAndColumn(0, 0); surfaceMontageViewports[1].setRowAndColumn(1, 0); surfaceMontageViewports[2].setRowAndColumn(0, 1); surfaceMontageViewports[3].setRowAndColumn(1, 1); } else if (numLeft == numRight) { surfaceMontageViewports.insert(surfaceMontageViewports.end(), leftViewports.begin(), leftViewports.end()); surfaceMontageViewports.insert(surfaceMontageViewports.end(), rightViewports.begin(), rightViewports.end()); CaretAssert(surfaceMontageViewports.size() == 4); surfaceMontageViewports[0].setRowAndColumn(0, 0); surfaceMontageViewports[1].setRowAndColumn(1, 0); surfaceMontageViewports[2].setRowAndColumn(0, 1); surfaceMontageViewports[3].setRowAndColumn(1, 1); } else { CaretAssert(0); } } else if (totalNum == 8) { surfaceMontageViewports.insert(surfaceMontageViewports.end(), leftLateralViewports.begin(), leftLateralViewports.end()); surfaceMontageViewports.insert(surfaceMontageViewports.end(), leftMedialViewports.begin(), leftMedialViewports.end()); surfaceMontageViewports.insert(surfaceMontageViewports.end(), rightLateralViewports.begin(), rightLateralViewports.end()); surfaceMontageViewports.insert(surfaceMontageViewports.end(), rightMedialViewports.begin(), rightMedialViewports.end()); CaretAssert(surfaceMontageViewports.size() == 8); switch (getLayoutOrientation()) { case SurfaceMontageLayoutOrientationEnum::LANDSCAPE_LAYOUT_ORIENTATION: surfaceMontageViewports[0].setRowAndColumn(0, 0); surfaceMontageViewports[1].setRowAndColumn(1, 0); surfaceMontageViewports[2].setRowAndColumn(0, 1); surfaceMontageViewports[3].setRowAndColumn(1, 1); surfaceMontageViewports[4].setRowAndColumn(0, 2); surfaceMontageViewports[5].setRowAndColumn(1, 2); surfaceMontageViewports[6].setRowAndColumn(0, 3); surfaceMontageViewports[7].setRowAndColumn(1, 3); break; case SurfaceMontageLayoutOrientationEnum::PORTRAIT_LAYOUT_ORIENTATION: surfaceMontageViewports[0].setRowAndColumn(0, 0); surfaceMontageViewports[1].setRowAndColumn(1, 0); surfaceMontageViewports[2].setRowAndColumn(0, 1); surfaceMontageViewports[3].setRowAndColumn(1, 1); surfaceMontageViewports[4].setRowAndColumn(2, 0); surfaceMontageViewports[5].setRowAndColumn(3, 0); surfaceMontageViewports[6].setRowAndColumn(2, 1); surfaceMontageViewports[7].setRowAndColumn(3, 1); break; } } else if (totalNum > 0) { CaretAssert(0); } CaretAssert(totalNum == static_cast(surfaceMontageViewports.size())); // std::cout << "Orientation: " << SurfaceMontageLayoutOrientationEnum::toName(getLayoutOrientation()) << std::endl; // for (int32_t i = 0; i < numViewports; i++) { // const SurfaceMontageViewport& svp = surfaceMontageViewports[i]; // std::cout << qPrintable("(" // + AString::number(svp.getRow()) // + "," // + AString::number(svp.getColumn()) // + ") " // + ProjectionViewTypeEnum::toName(svp.getProjectionViewType())) // << std::endl; // } // std::cout << std::endl; } /** * Save members to the given scene class. * * @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 information is added. */ void SurfaceMontageConfigurationCerebral::saveMembersToScene(const SceneAttributes* sceneAttributes, SceneClass* sceneClass) { 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 information is restored. */ void SurfaceMontageConfigurationCerebral::restoreMembersFromScene(const SceneAttributes* sceneAttributes, const SceneClass* sceneClass) { m_sceneAssistant->restoreMembers(sceneAttributes, sceneClass); } /** * @return Is left enabled? */ bool SurfaceMontageConfigurationCerebral::isLeftEnabled() const { return m_leftEnabled; } /** * Set left enabled * @param enabled * New status */ void SurfaceMontageConfigurationCerebral::setLeftEnabled(const bool enabled) { m_leftEnabled = enabled; } /** * @return Is right enabled? */ bool SurfaceMontageConfigurationCerebral::isRightEnabled() const { return m_rightEnabled; } /** * Set right enabled * @param enabled * New status */ void SurfaceMontageConfigurationCerebral::setRightEnabled(const bool enabled) { m_rightEnabled = enabled; } /** * @return Is lateral enabled? */ bool SurfaceMontageConfigurationCerebral::isLateralEnabled() const { return m_lateralEnabled; } /** * Set lateral enabled * @param enabled * New status */ void SurfaceMontageConfigurationCerebral::setLateralEnabled(const bool enabled) { m_lateralEnabled = enabled; } /** * @return Is medial enabled? */ bool SurfaceMontageConfigurationCerebral::isMedialEnabled() const { return m_medialEnabled; } /** * Set medial enabled * @param enabled * New status */ void SurfaceMontageConfigurationCerebral::setMedialEnabled(const bool enabled) { m_medialEnabled = enabled; } /** * @return Is enabled? */ bool SurfaceMontageConfigurationCerebral::isFirstSurfaceEnabled() const { return m_firstSurfaceEnabled; } /** * Set first surface enabled * @param enabled * New status */ void SurfaceMontageConfigurationCerebral::setFirstSurfaceEnabled(const bool enabled) { m_firstSurfaceEnabled = enabled; } /** * @return Is first surfce enabled? */ bool SurfaceMontageConfigurationCerebral::isSecondSurfaceEnabled() const { return m_secondSurfaceEnabled; } /** * Set second surface enabled * @param enabled * New status */ void SurfaceMontageConfigurationCerebral::setSecondSurfaceEnabled(const bool enabled) { m_secondSurfaceEnabled = enabled; } /** * @return the left first surface selection in this configuration. */ SurfaceSelectionModel* SurfaceMontageConfigurationCerebral::getLeftFirstSurfaceSelectionModel() { return m_leftFirstSurfaceSelectionModel; } /** * @return the left first surface selection in this configuration. */ const SurfaceSelectionModel* SurfaceMontageConfigurationCerebral::getLeftFirstSurfaceSelectionModel() const { return m_leftFirstSurfaceSelectionModel; } /** * @return the left second surface selection in this configuration. */ SurfaceSelectionModel* SurfaceMontageConfigurationCerebral::getLeftSecondSurfaceSelectionModel() { return m_leftSecondSurfaceSelectionModel; } /** * @return the left second surface selection in this configuration. */ const SurfaceSelectionModel* SurfaceMontageConfigurationCerebral::getLeftSecondSurfaceSelectionModel() const { return m_leftSecondSurfaceSelectionModel; } /** * @return the right first surface selection in this configuration. */ SurfaceSelectionModel* SurfaceMontageConfigurationCerebral::getRightFirstSurfaceSelectionModel() { return m_rightFirstSurfaceSelectionModel; } /** * @return the right first surface selection in this configuration. */ const SurfaceSelectionModel* SurfaceMontageConfigurationCerebral::getRightFirstSurfaceSelectionModel() const { return m_rightFirstSurfaceSelectionModel; } /** * @return the right second surface selection in this configuration. */ SurfaceSelectionModel* SurfaceMontageConfigurationCerebral::getRightSecondSurfaceSelectionModel() { return m_rightSecondSurfaceSelectionModel; } /** * @return the right second surface selection in this configuration. */ const SurfaceSelectionModel* SurfaceMontageConfigurationCerebral::getRightSecondSurfaceSelectionModel() const { return m_rightSecondSurfaceSelectionModel; } /** * Get a description of this object's content. * @return String describing this object's content. */ AString SurfaceMontageConfigurationCerebral::toString() const { PlainTextStringBuilder tb; getDescriptionOfContent(tb); return tb.getText(); } /** * Get a text description of the instance's content. * * @param descriptionOut * Description of the instance's content. */ void SurfaceMontageConfigurationCerebral::getDescriptionOfContent(PlainTextStringBuilder& descriptionOut) const { AString msg; descriptionOut.addLine("Cerebral Montage: "); descriptionOut.pushIndentation(); if (isLeftEnabled()) { if (isFirstSurfaceEnabled()) { const Surface* firstLeftSurface = getLeftFirstSurfaceSelectionModel()->getSurface(); if (firstLeftSurface != NULL) { descriptionOut.addLine("Left Surface:"); descriptionOut.pushIndentation(); firstLeftSurface->getDescriptionOfContent(descriptionOut); descriptionOut.popIndentation(); } } if (isSecondSurfaceEnabled()) { const Surface* secondLeftSurface = getLeftSecondSurfaceSelectionModel()->getSurface(); if (secondLeftSurface != NULL) { descriptionOut.addLine("Left Surface:"); descriptionOut.pushIndentation(); secondLeftSurface->getDescriptionOfContent(descriptionOut); descriptionOut.popIndentation(); } } } if (isRightEnabled()) { if (isFirstSurfaceEnabled()) { const Surface* firstRightSurface = getRightFirstSurfaceSelectionModel()->getSurface(); if (firstRightSurface != NULL) { descriptionOut.addLine("Right Surface:"); descriptionOut.pushIndentation(); firstRightSurface->getDescriptionOfContent(descriptionOut); descriptionOut.popIndentation(); } } if (isSecondSurfaceEnabled()) { const Surface* secondRightSurface = getRightSecondSurfaceSelectionModel()->getSurface(); if (secondRightSurface != NULL) { descriptionOut.addLine("Right Surface:"); descriptionOut.pushIndentation(); secondRightSurface->getDescriptionOfContent(descriptionOut); descriptionOut.popIndentation(); } } } AString viewsMsg = "Selected Views: "; if (isLateralEnabled()) { viewsMsg += " Lateral"; } if (isMedialEnabled()) { viewsMsg += " Medial"; } descriptionOut.addLine(viewsMsg); descriptionOut.popIndentation(); } /** * Get all surfaces displayed in this configuration. * * @param surfaceOut * Will contain all displayed surfaces upon exit. */ void SurfaceMontageConfigurationCerebral::getDisplayedSurfaces(std::vector& surfacesOut) const { surfacesOut.clear(); if (isLeftEnabled()) { Surface* firstLeftSurface = NULL;; if (isFirstSurfaceEnabled()) { firstLeftSurface = const_cast(getLeftFirstSurfaceSelectionModel()->getSurface()); if (firstLeftSurface != NULL) { surfacesOut.push_back(const_cast(firstLeftSurface)); } } if (isSecondSurfaceEnabled()) { const Surface* secondLeftSurface = getLeftSecondSurfaceSelectionModel()->getSurface(); if (secondLeftSurface != NULL) { if (secondLeftSurface != firstLeftSurface) { surfacesOut.push_back(const_cast(secondLeftSurface)); } } } } if (isRightEnabled()) { Surface* firstRightSurface = NULL; if (isFirstSurfaceEnabled()) { firstRightSurface = const_cast(getRightFirstSurfaceSelectionModel()->getSurface()); if (firstRightSurface != NULL) { surfacesOut.push_back(const_cast(firstRightSurface)); } } if (isSecondSurfaceEnabled()) { const Surface* secondRightSurface = getRightSecondSurfaceSelectionModel()->getSurface(); if (secondRightSurface != NULL) { if (secondRightSurface != firstRightSurface) { surfacesOut.push_back(const_cast(secondRightSurface)); } } } } } /** * Copy the given configuration to this configurtion. * * @param configuration. * Configuration that is copied. */ void SurfaceMontageConfigurationCerebral::copyConfiguration(SurfaceMontageConfigurationAbstract* configuration) { SurfaceMontageConfigurationAbstract::copyConfiguration(configuration); SurfaceMontageConfigurationCerebral* cerebralConfiguration = dynamic_cast(configuration); CaretAssert(cerebralConfiguration); m_leftFirstSurfaceSelectionModel->setSurface(cerebralConfiguration->m_leftFirstSurfaceSelectionModel->getSurface()); m_leftSecondSurfaceSelectionModel->setSurface(cerebralConfiguration->m_leftSecondSurfaceSelectionModel->getSurface()); m_rightFirstSurfaceSelectionModel->setSurface(cerebralConfiguration->m_rightFirstSurfaceSelectionModel->getSurface()); m_rightSecondSurfaceSelectionModel->setSurface(cerebralConfiguration->m_rightSecondSurfaceSelectionModel->getSurface()); m_leftEnabled = cerebralConfiguration->m_leftEnabled; m_rightEnabled = cerebralConfiguration->m_rightEnabled; m_firstSurfaceEnabled = cerebralConfiguration->m_firstSurfaceEnabled; m_secondSurfaceEnabled = cerebralConfiguration->m_secondSurfaceEnabled; m_lateralEnabled = cerebralConfiguration->m_lateralEnabled; m_medialEnabled = cerebralConfiguration->m_medialEnabled; } connectome-workbench-1.2.3+git41-gc4c6c90/src/Brain/SurfaceMontageConfigurationCerebral.h000066400000000000000000000112531300200146000310450ustar00rootroot00000000000000#ifndef __SURFACE_MONTAGE_CONFIGURATION_CEREBRAL_H__ #define __SURFACE_MONTAGE_CONFIGURATION_CEREBRAL_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 "SurfaceMontageConfigurationAbstract.h" namespace caret { class SceneClassAssistant; class SurfaceSelectionModel; class SurfaceMontageConfigurationCerebral : public SurfaceMontageConfigurationAbstract { public: SurfaceMontageConfigurationCerebral(const int32_t tabIndex); virtual ~SurfaceMontageConfigurationCerebral(); SurfaceSelectionModel* getLeftFirstSurfaceSelectionModel(); const SurfaceSelectionModel* getLeftFirstSurfaceSelectionModel() const; SurfaceSelectionModel* getLeftSecondSurfaceSelectionModel(); const SurfaceSelectionModel* getLeftSecondSurfaceSelectionModel() const; SurfaceSelectionModel* getRightFirstSurfaceSelectionModel(); const SurfaceSelectionModel* getRightFirstSurfaceSelectionModel() const; SurfaceSelectionModel* getRightSecondSurfaceSelectionModel(); const SurfaceSelectionModel* getRightSecondSurfaceSelectionModel() const; bool isLeftEnabled() const; void setLeftEnabled(const bool enabled); bool isRightEnabled() const; void setRightEnabled(const bool enabled); bool isLateralEnabled() const; void setLateralEnabled(const bool enabled); bool isMedialEnabled() const; void setMedialEnabled(const bool enabled); bool isFirstSurfaceEnabled() const; void setFirstSurfaceEnabled(const bool enabled); bool isSecondSurfaceEnabled() const; void setSecondSurfaceEnabled(const bool enabled); virtual void initializeSelectedSurfaces(); virtual bool isValid(); virtual void updateSurfaceMontageViewports(std::vector& surfaceMontageViewports); virtual void copyConfiguration(SurfaceMontageConfigurationAbstract* configuration); private: SurfaceMontageConfigurationCerebral(const SurfaceMontageConfigurationCerebral&); SurfaceMontageConfigurationCerebral& operator=(const SurfaceMontageConfigurationCerebral&); public: virtual AString toString() const; virtual void getDescriptionOfContent(PlainTextStringBuilder& descriptionOut) const; virtual void getDisplayedSurfaces(std::vector& surfacesOut) const; // ADD_NEW_METHODS_HERE protected: virtual void saveMembersToScene(const SceneAttributes* sceneAttributes, SceneClass* sceneClass); virtual void restoreMembersFromScene(const SceneAttributes* sceneAttributes, const SceneClass* sceneClass); private: SceneClassAssistant* m_sceneAssistant; SurfaceSelectionModel* m_leftFirstSurfaceSelectionModel; SurfaceSelectionModel* m_leftSecondSurfaceSelectionModel; SurfaceSelectionModel* m_rightFirstSurfaceSelectionModel; SurfaceSelectionModel* m_rightSecondSurfaceSelectionModel; bool m_leftEnabled; bool m_rightEnabled; bool m_firstSurfaceEnabled; bool m_secondSurfaceEnabled; bool m_lateralEnabled; bool m_medialEnabled; // ADD_NEW_MEMBERS_HERE friend class ModelSurfaceMontage; }; #ifdef __SURFACE_MONTAGE_CONFIGURATION_CEREBRAL_DECLARE__ // #endif // __SURFACE_MONTAGE_CONFIGURATION_CEREBRAL_DECLARE__ } // namespace #endif //__SURFACE_MONTAGE_CONFIGURATION_CEREBRAL_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Brain/SurfaceMontageConfigurationFlatMaps.cxx000066400000000000000000000325531300200146000314160ustar00rootroot00000000000000 /*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 __SURFACE_MONTAGE_CONFIGURATION_FLAT_MAPS_DECLARE__ #include "SurfaceMontageConfigurationFlatMaps.h" #undef __SURFACE_MONTAGE_CONFIGURATION_FLAT_MAPS_DECLARE__ #include "CaretAssert.h" #include "PlainTextStringBuilder.h" #include "SceneClass.h" #include "SceneClassAssistant.h" #include "Surface.h" #include "SurfaceSelectionModel.h" using namespace caret; /** * \class caret::SurfaceMontageConfigurationFlatMaps * \brief Surface montage configuration for flat maps. * \ingroup Brain */ /** * Constructor. */ SurfaceMontageConfigurationFlatMaps::SurfaceMontageConfigurationFlatMaps(const int32_t tabIndex) : SurfaceMontageConfigurationAbstract(SurfaceMontageConfigurationTypeEnum::FLAT_CONFIGURATION, SUPPORTS_LAYOUT_ORIENTATION_NO) { std::vector validSurfaceTypes; validSurfaceTypes.push_back(SurfaceTypeEnum::FLAT); m_leftSurfaceSelectionModel = new SurfaceSelectionModel(StructureEnum::CORTEX_LEFT, validSurfaceTypes); m_rightSurfaceSelectionModel = new SurfaceSelectionModel(StructureEnum::CORTEX_RIGHT, validSurfaceTypes); m_cerebellumSurfaceSelectionModel = new SurfaceSelectionModel(StructureEnum::CEREBELLUM, validSurfaceTypes); m_leftEnabled = true; m_rightEnabled = true; m_cerebellumEnabled = true; m_sceneAssistant = new SceneClassAssistant(); m_sceneAssistant->add("m_leftSurfaceSelectionModel", "SurfaceSelectionModel", m_leftSurfaceSelectionModel); m_sceneAssistant->add("m_rightSurfaceSelectionModel", "SurfaceSelectionModel", m_rightSurfaceSelectionModel); m_sceneAssistant->add("m_cerebellumSurfaceSelectionModel", "SurfaceSelectionModel", m_cerebellumSurfaceSelectionModel); m_sceneAssistant->add("m_leftEnabled", &m_leftEnabled); m_sceneAssistant->add("m_rightEnabled", &m_rightEnabled); m_sceneAssistant->add("m_cerebellumEnabled", &m_cerebellumEnabled); std::vector supportedStructures; supportedStructures.push_back(StructureEnum::CEREBELLUM); supportedStructures.push_back(StructureEnum::CORTEX_LEFT); supportedStructures.push_back(StructureEnum::CORTEX_RIGHT); setupOverlaySet("Flat Montage", tabIndex, supportedStructures); } /** * Destructor. */ SurfaceMontageConfigurationFlatMaps::~SurfaceMontageConfigurationFlatMaps() { delete m_leftSurfaceSelectionModel; delete m_rightSurfaceSelectionModel; delete m_cerebellumSurfaceSelectionModel; delete m_sceneAssistant; } /** * Initialize the selected surfaces. */ void SurfaceMontageConfigurationFlatMaps::initializeSelectedSurfaces() { } /** * @return Is this configuration valid? */ bool SurfaceMontageConfigurationFlatMaps::isValid() { const bool valid = ((getLeftSurfaceSelectionModel()->getSurface() != NULL) || (getRightSurfaceSelectionModel()->getSurface() != NULL) || (getCerebellumSurfaceSelectionModel()->getSurface() != NULL)); return valid; } /** * @return Is left enabled? */ bool SurfaceMontageConfigurationFlatMaps::isLeftEnabled() const { return m_leftEnabled; } /** * Set left enabled * @param enabled * New status */ void SurfaceMontageConfigurationFlatMaps::setLeftEnabled(const bool enabled) { m_leftEnabled = enabled; } /** * @return Is right enabled? */ bool SurfaceMontageConfigurationFlatMaps::isRightEnabled() const { return m_rightEnabled; } /** * Set right enabled * @param enabled * New status */ void SurfaceMontageConfigurationFlatMaps::setRightEnabled(const bool enabled) { m_rightEnabled = enabled; } /** * @return Is cerebellum enabled? */ bool SurfaceMontageConfigurationFlatMaps::isCerebellumEnabled() const { return m_cerebellumEnabled; } /** * Set cerebellum enabled * @param enabled * New status */ void SurfaceMontageConfigurationFlatMaps::setCerebellumEnabled(const bool enabled) { m_cerebellumEnabled = enabled; } /** * @return the left surface selection in this configuration. */ SurfaceSelectionModel* SurfaceMontageConfigurationFlatMaps::getLeftSurfaceSelectionModel() { return m_leftSurfaceSelectionModel; } /** * @return the left surface selection in this configuration. */ const SurfaceSelectionModel* SurfaceMontageConfigurationFlatMaps::getLeftSurfaceSelectionModel() const { return m_leftSurfaceSelectionModel; } /** * @return the right surface selection in this configuration. */ SurfaceSelectionModel* SurfaceMontageConfigurationFlatMaps::getRightSurfaceSelectionModel() { return m_rightSurfaceSelectionModel; } /** * @return the right surface selection in this configuration. */ const SurfaceSelectionModel* SurfaceMontageConfigurationFlatMaps::getRightSurfaceSelectionModel() const { return m_rightSurfaceSelectionModel; } /** * @return the cerebellum surface selection in this configuration. */ SurfaceSelectionModel* SurfaceMontageConfigurationFlatMaps::getCerebellumSurfaceSelectionModel() { return m_cerebellumSurfaceSelectionModel; } /** * @return the cerebellum surface selection in this configuration. */ const SurfaceSelectionModel* SurfaceMontageConfigurationFlatMaps::getCerebellumSurfaceSelectionModel() const { return m_cerebellumSurfaceSelectionModel; } /** * Update the montage viewports using the current selected surfaces and settings. * * @param surfaceMontageViewports * Will be loaded with the montage viewports. */ void SurfaceMontageConfigurationFlatMaps::updateSurfaceMontageViewports(std::vector& surfaceMontageViewports) { surfaceMontageViewports.clear(); if (m_leftEnabled) { Surface* leftSurface = m_leftSurfaceSelectionModel->getSurface(); if (leftSurface != NULL) { SurfaceMontageViewport smv(leftSurface, ProjectionViewTypeEnum::PROJECTION_VIEW_LEFT_FLAT_SURFACE); surfaceMontageViewports.push_back(smv); } } if (m_rightEnabled) { Surface* rightSurface = m_rightSurfaceSelectionModel->getSurface(); if (rightSurface != NULL) { SurfaceMontageViewport smv(rightSurface, ProjectionViewTypeEnum::PROJECTION_VIEW_RIGHT_FLAT_SURFACE); surfaceMontageViewports.push_back(smv); } } if (m_cerebellumEnabled) { Surface* cerebellumSurface = m_cerebellumSurfaceSelectionModel->getSurface(); if (cerebellumSurface != NULL) { SurfaceMontageViewport smv(cerebellumSurface, ProjectionViewTypeEnum::PROJECTION_VIEW_CEREBELLUM_FLAT_SURFACE); surfaceMontageViewports.push_back(smv); } } const int32_t numSurfaces = static_cast(surfaceMontageViewports.size()); for (int32_t i = 0; i < numSurfaces; i++) { switch (getLayoutOrientation()) { case SurfaceMontageLayoutOrientationEnum::LANDSCAPE_LAYOUT_ORIENTATION: surfaceMontageViewports[i].setRowAndColumn(0, i); break; case SurfaceMontageLayoutOrientationEnum::PORTRAIT_LAYOUT_ORIENTATION: surfaceMontageViewports[i].setRowAndColumn(i, 0); break; } } } /** * Save members to the given scene class. * * @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 information is added. */ void SurfaceMontageConfigurationFlatMaps::saveMembersToScene(const SceneAttributes* sceneAttributes, SceneClass* sceneClass) { 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 information is restored. */ void SurfaceMontageConfigurationFlatMaps::restoreMembersFromScene(const SceneAttributes* sceneAttributes, const SceneClass* sceneClass) { m_sceneAssistant->restoreMembers(sceneAttributes, sceneClass); } /** * Get a description of this object's content. * @return String describing this object's content. */ AString SurfaceMontageConfigurationFlatMaps::toString() const { PlainTextStringBuilder tb; getDescriptionOfContent(tb); return tb.getText(); } /** * Get a text description of the instance's content. * * @param descriptionOut * Description of the instance's content. */ void SurfaceMontageConfigurationFlatMaps::getDescriptionOfContent(PlainTextStringBuilder& descriptionOut) const { descriptionOut.addLine("Flat Montage: "); if (isLeftEnabled()) { const Surface* firstLeftSurface = getLeftSurfaceSelectionModel()->getSurface(); if (firstLeftSurface != NULL) { descriptionOut.addLine("Left Surface:"); descriptionOut.pushIndentation(); firstLeftSurface->getDescriptionOfContent(descriptionOut); descriptionOut.popIndentation(); } } if (isRightEnabled()) { const Surface* firstRightSurface = getRightSurfaceSelectionModel()->getSurface(); if (firstRightSurface != NULL) { descriptionOut.addLine("Right Surface:"); descriptionOut.pushIndentation(); firstRightSurface->getDescriptionOfContent(descriptionOut); descriptionOut.popIndentation(); } } if (isCerebellumEnabled()) { const Surface* cerebellumSurface = getCerebellumSurfaceSelectionModel()->getSurface(); if (cerebellumSurface != NULL) { descriptionOut.addLine("Cerebellum Surface:"); descriptionOut.pushIndentation(); cerebellumSurface->getDescriptionOfContent(descriptionOut); descriptionOut.popIndentation(); } } } /** * Get all surfaces displayed in this configuration. * * @param surfaceOut * Will contain all displayed surfaces upon exit. */ void SurfaceMontageConfigurationFlatMaps::getDisplayedSurfaces(std::vector& surfacesOut) const { surfacesOut.clear(); if (isLeftEnabled()) { const Surface* firstLeftSurface = getLeftSurfaceSelectionModel()->getSurface(); if (firstLeftSurface != NULL) { surfacesOut.push_back(const_cast(firstLeftSurface)); } } if (isRightEnabled()) { const Surface* firstRightSurface = getRightSurfaceSelectionModel()->getSurface(); if (firstRightSurface != NULL) { surfacesOut.push_back(const_cast(firstRightSurface)); } } if (isCerebellumEnabled()) { const Surface* cerebellumSurface = getCerebellumSurfaceSelectionModel()->getSurface(); if (cerebellumSurface != NULL) { surfacesOut.push_back(const_cast(cerebellumSurface)); } } } /** * Copy the given configuration to this configurtion. * * @param configuration. * Configuration that is copied. */ void SurfaceMontageConfigurationFlatMaps::copyConfiguration(SurfaceMontageConfigurationAbstract* configuration) { SurfaceMontageConfigurationAbstract::copyConfiguration(configuration); SurfaceMontageConfigurationFlatMaps* flatConfiguration = dynamic_cast(configuration); CaretAssert(flatConfiguration); m_leftSurfaceSelectionModel->setSurface(flatConfiguration->m_leftSurfaceSelectionModel->getSurface()); m_rightSurfaceSelectionModel->setSurface(flatConfiguration->m_rightSurfaceSelectionModel->getSurface()); m_cerebellumSurfaceSelectionModel->setSurface(flatConfiguration->m_cerebellumSurfaceSelectionModel->getSurface()); m_leftEnabled = flatConfiguration->m_leftEnabled; m_rightEnabled = flatConfiguration->m_rightEnabled; m_cerebellumEnabled = flatConfiguration->m_cerebellumEnabled; } connectome-workbench-1.2.3+git41-gc4c6c90/src/Brain/SurfaceMontageConfigurationFlatMaps.h000066400000000000000000000077211300200146000310420ustar00rootroot00000000000000#ifndef __SURFACE_MONTAGE_CONFIGURATION_FLAT_MAPS_H__ #define __SURFACE_MONTAGE_CONFIGURATION_FLAT_MAPS_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 "SurfaceMontageConfigurationAbstract.h" namespace caret { class SceneClassAssistant; class SurfaceSelectionModel; class SurfaceMontageConfigurationFlatMaps : public SurfaceMontageConfigurationAbstract { public: SurfaceMontageConfigurationFlatMaps(const int32_t tabIndex); virtual ~SurfaceMontageConfigurationFlatMaps(); virtual void initializeSelectedSurfaces(); virtual bool isValid(); SurfaceSelectionModel* getLeftSurfaceSelectionModel(); const SurfaceSelectionModel* getLeftSurfaceSelectionModel() const; SurfaceSelectionModel* getRightSurfaceSelectionModel(); const SurfaceSelectionModel* getRightSurfaceSelectionModel() const; SurfaceSelectionModel* getCerebellumSurfaceSelectionModel(); const SurfaceSelectionModel* getCerebellumSurfaceSelectionModel() const; bool isLeftEnabled() const; void setLeftEnabled(const bool enabled); bool isRightEnabled() const; void setRightEnabled(const bool enabled); bool isCerebellumEnabled() const; void setCerebellumEnabled(const bool enabled); virtual void updateSurfaceMontageViewports(std::vector& surfaceMontageViewports); virtual void copyConfiguration(SurfaceMontageConfigurationAbstract* configuration); private: SurfaceMontageConfigurationFlatMaps(const SurfaceMontageConfigurationFlatMaps&); SurfaceMontageConfigurationFlatMaps& operator=(const SurfaceMontageConfigurationFlatMaps&); public: virtual AString toString() const; virtual void getDescriptionOfContent(PlainTextStringBuilder& descriptionOut) const; virtual void getDisplayedSurfaces(std::vector& surfacesOut) const; // ADD_NEW_METHODS_HERE protected: virtual void saveMembersToScene(const SceneAttributes* sceneAttributes, SceneClass* sceneClass); virtual void restoreMembersFromScene(const SceneAttributes* sceneAttributes, const SceneClass* sceneClass); private: SceneClassAssistant* m_sceneAssistant; SurfaceSelectionModel* m_leftSurfaceSelectionModel; SurfaceSelectionModel* m_rightSurfaceSelectionModel; SurfaceSelectionModel* m_cerebellumSurfaceSelectionModel; bool m_leftEnabled; bool m_rightEnabled; bool m_cerebellumEnabled; // ADD_NEW_MEMBERS_HERE friend class ModelSurfaceMontage; }; #ifdef __SURFACE_MONTAGE_CONFIGURATION_FLAT_MAPS_DECLARE__ // #endif // __SURFACE_MONTAGE_CONFIGURATION_FLAT_MAPS_DECLARE__ } // namespace #endif //__SURFACE_MONTAGE_CONFIGURATION_FLAT_MAPS_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Brain/SurfaceMontageConfigurationTypeEnum.cxx000066400000000000000000000272031300200146000314510ustar00rootroot00000000000000 /*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 __SURFACE_MONTAGE_CONFIGURATION_TYPE_ENUM_DECLARE__ #include "SurfaceMontageConfigurationTypeEnum.h" #undef __SURFACE_MONTAGE_CONFIGURATION_TYPE_ENUM_DECLARE__ #include "CaretAssert.h" using namespace caret; /** * \class caret::SurfaceMontageConfigurationTypeEnum * \brief * * * * 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_surfaceMontageConfigurationTypeEnumComboBox; * * Declare a slot that is called when user changes selection * private slots: * void surfaceMontageConfigurationTypeEnumComboBoxItemActivated(); * * Implementation File (.cxx) * Include the header files * #include "EnumComboBoxTemplate.h" * #include "SurfaceMontageConfigurationTypeEnum.h" * * Instatiate: * m_surfaceMontageConfigurationTypeEnumComboBox = new EnumComboBoxTemplate(this); * m_surfaceMontageConfigurationTypeEnumComboBox->setup(); * * Get notified when the user changes the selection: * QObject::connect(m_surfaceMontageConfigurationTypeEnumComboBox, SIGNAL(itemActivated()), * this, SLOT(surfaceMontageConfigurationTypeEnumComboBoxItemActivated())); * * Update the selection: * m_surfaceMontageConfigurationTypeEnumComboBox->setSelectedItem(NEW_VALUE); * * Read the selection: * const SurfaceMontageConfigurationTypeEnum::Enum VARIABLE = m_surfaceMontageConfigurationTypeEnumComboBox->getSelectedItem(); * */ /** * Constructor. * * @param enumValue * An enumerated value. * @param name * Name of enumerated value. * * @param guiName * User-friendly name for use in user-interface. */ SurfaceMontageConfigurationTypeEnum::SurfaceMontageConfigurationTypeEnum(const Enum enumValue, const AString& name, const AString& guiName) { this->enumValue = enumValue; this->integerCode = integerCodeCounter++; this->name = name; this->guiName = guiName; } /** * Destructor. */ SurfaceMontageConfigurationTypeEnum::~SurfaceMontageConfigurationTypeEnum() { } /** * Initialize the enumerated metadata. */ void SurfaceMontageConfigurationTypeEnum::initialize() { if (initializedFlag) { return; } initializedFlag = true; enumData.push_back(SurfaceMontageConfigurationTypeEnum(CEREBELLAR_CORTEX_CONFIGURATION, "CEREBELLAR_CORTEX_CONFIGURATION", "Cerebellar Cortex")); enumData.push_back(SurfaceMontageConfigurationTypeEnum(CEREBRAL_CORTEX_CONFIGURATION, "CEREBRAL_CORTEX_CONFIGURATION", "Cerebral Cortex")); enumData.push_back(SurfaceMontageConfigurationTypeEnum(FLAT_CONFIGURATION, "FLAT_CONFIGURATION", "Flat Maps")); } /** * 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 SurfaceMontageConfigurationTypeEnum* SurfaceMontageConfigurationTypeEnum::findData(const Enum enumValue) { if (initializedFlag == false) initialize(); size_t num = enumData.size(); for (size_t i = 0; i < num; i++) { const SurfaceMontageConfigurationTypeEnum* 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 SurfaceMontageConfigurationTypeEnum::toName(Enum enumValue) { if (initializedFlag == false) initialize(); const SurfaceMontageConfigurationTypeEnum* 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. */ SurfaceMontageConfigurationTypeEnum::Enum SurfaceMontageConfigurationTypeEnum::fromName(const AString& name, bool* isValidOut) { if (initializedFlag == false) initialize(); bool validFlag = false; Enum enumValue = SurfaceMontageConfigurationTypeEnum::enumData[0].enumValue; for (std::vector::iterator iter = enumData.begin(); iter != enumData.end(); iter++) { const SurfaceMontageConfigurationTypeEnum& 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 SurfaceMontageConfigurationTypeEnum")); } return enumValue; } /** * Get a GUI string representation of the enumerated type. * @param enumValue * Enumerated value. * @return * String representing enumerated value. */ AString SurfaceMontageConfigurationTypeEnum::toGuiName(Enum enumValue) { if (initializedFlag == false) initialize(); const SurfaceMontageConfigurationTypeEnum* 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. */ SurfaceMontageConfigurationTypeEnum::Enum SurfaceMontageConfigurationTypeEnum::fromGuiName(const AString& guiName, bool* isValidOut) { if (initializedFlag == false) initialize(); bool validFlag = false; Enum enumValue = SurfaceMontageConfigurationTypeEnum::enumData[0].enumValue; for (std::vector::iterator iter = enumData.begin(); iter != enumData.end(); iter++) { const SurfaceMontageConfigurationTypeEnum& 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 SurfaceMontageConfigurationTypeEnum")); } return enumValue; } /** * Get the integer code for a data type. * * @return * Integer code for data type. */ int32_t SurfaceMontageConfigurationTypeEnum::toIntegerCode(Enum enumValue) { if (initializedFlag == false) initialize(); const SurfaceMontageConfigurationTypeEnum* 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. */ SurfaceMontageConfigurationTypeEnum::Enum SurfaceMontageConfigurationTypeEnum::fromIntegerCode(const int32_t integerCode, bool* isValidOut) { if (initializedFlag == false) initialize(); bool validFlag = false; Enum enumValue = SurfaceMontageConfigurationTypeEnum::enumData[0].enumValue; for (std::vector::iterator iter = enumData.begin(); iter != enumData.end(); iter++) { const SurfaceMontageConfigurationTypeEnum& 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 SurfaceMontageConfigurationTypeEnum")); } 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 SurfaceMontageConfigurationTypeEnum::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 SurfaceMontageConfigurationTypeEnum::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(SurfaceMontageConfigurationTypeEnum::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 SurfaceMontageConfigurationTypeEnum::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(SurfaceMontageConfigurationTypeEnum::toGuiName(iter->enumValue)); } if (isSorted) { std::sort(allGuiNames.begin(), allGuiNames.end()); } } connectome-workbench-1.2.3+git41-gc4c6c90/src/Brain/SurfaceMontageConfigurationTypeEnum.h000066400000000000000000000065311300200146000310770ustar00rootroot00000000000000#ifndef __SURFACE_MONTAGE_CONFIGURATION_TYPE_ENUM_H__ #define __SURFACE_MONTAGE_CONFIGURATION_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 SurfaceMontageConfigurationTypeEnum { public: /** * Enumerated values. */ enum Enum { /** */ CEREBELLAR_CORTEX_CONFIGURATION, /** */ CEREBRAL_CORTEX_CONFIGURATION, /** */ FLAT_CONFIGURATION }; ~SurfaceMontageConfigurationTypeEnum(); 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: SurfaceMontageConfigurationTypeEnum(const Enum enumValue, const AString& name, const AString& guiName); static const SurfaceMontageConfigurationTypeEnum* 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 __SURFACE_MONTAGE_CONFIGURATION_TYPE_ENUM_DECLARE__ std::vector SurfaceMontageConfigurationTypeEnum::enumData; bool SurfaceMontageConfigurationTypeEnum::initializedFlag = false; int32_t SurfaceMontageConfigurationTypeEnum::integerCodeCounter = 0; #endif // __SURFACE_MONTAGE_CONFIGURATION_TYPE_ENUM_DECLARE__ } // namespace #endif //__SURFACE_MONTAGE_CONFIGURATION_TYPE_ENUM_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Brain/SurfaceMontageLayoutOrientationEnum.cxx000066400000000000000000000266471300200146000315040ustar00rootroot00000000000000 /*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 __SURFACE_MONTAGE_LAYOUT_ORIENTATION_ENUM_DECLARE__ #include "SurfaceMontageLayoutOrientationEnum.h" #undef __SURFACE_MONTAGE_LAYOUT_ORIENTATION_ENUM_DECLARE__ #include "CaretAssert.h" using namespace caret; /** * \class caret::SurfaceMontageLayoutOrientationEnum * \brief * * * * 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_surfaceMontageLayoutOrientationEnumComboBox; * * Declare a slot that is called when user changes selection * private slots: * void surfaceMontageLayoutOrientationEnumComboBoxItemActivated(); * * Implementation File (.cxx) * Include the header files * #include "EnumComboBoxTemplate.h" * #include "SurfaceMontageLayoutOrientationEnum.h" * * Instatiate: * m_surfaceMontageLayoutOrientationEnumComboBox = new EnumComboBoxTemplate(this); * m_surfaceMontageLayoutOrientationEnumComboBox->setup(); * * Get notified when the user changes the selection: * QObject::connect(m_surfaceMontageLayoutOrientationEnumComboBox, SIGNAL(itemActivated()), * this, SLOT(surfaceMontageLayoutOrientationEnumComboBoxItemActivated())); * * Update the selection: * m_surfaceMontageLayoutOrientationEnumComboBox->setSelectedItem(NEW_VALUE); * * Read the selection: * const SurfaceMontageLayoutOrientationEnum::Enum VARIABLE = m_surfaceMontageLayoutOrientationEnumComboBox->getSelectedItem(); * */ /** * Constructor. * * @param enumValue * An enumerated value. * @param name * Name of enumerated value. * * @param guiName * User-friendly name for use in user-interface. */ SurfaceMontageLayoutOrientationEnum::SurfaceMontageLayoutOrientationEnum(const Enum enumValue, const AString& name, const AString& guiName) { this->enumValue = enumValue; this->integerCode = integerCodeCounter++; this->name = name; this->guiName = guiName; } /** * Destructor. */ SurfaceMontageLayoutOrientationEnum::~SurfaceMontageLayoutOrientationEnum() { } /** * Initialize the enumerated metadata. */ void SurfaceMontageLayoutOrientationEnum::initialize() { if (initializedFlag) { return; } initializedFlag = true; enumData.push_back(SurfaceMontageLayoutOrientationEnum(LANDSCAPE_LAYOUT_ORIENTATION, "LANDSCAPE_LAYOUT_ORIENTATION", "Landscape")); enumData.push_back(SurfaceMontageLayoutOrientationEnum(PORTRAIT_LAYOUT_ORIENTATION, "PORTRAIT_LAYOUT_ORIENTATION", "Portrait")); } /** * 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 SurfaceMontageLayoutOrientationEnum* SurfaceMontageLayoutOrientationEnum::findData(const Enum enumValue) { if (initializedFlag == false) initialize(); size_t num = enumData.size(); for (size_t i = 0; i < num; i++) { const SurfaceMontageLayoutOrientationEnum* 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 SurfaceMontageLayoutOrientationEnum::toName(Enum enumValue) { if (initializedFlag == false) initialize(); const SurfaceMontageLayoutOrientationEnum* 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. */ SurfaceMontageLayoutOrientationEnum::Enum SurfaceMontageLayoutOrientationEnum::fromName(const AString& name, bool* isValidOut) { if (initializedFlag == false) initialize(); bool validFlag = false; Enum enumValue = SurfaceMontageLayoutOrientationEnum::enumData[0].enumValue; for (std::vector::iterator iter = enumData.begin(); iter != enumData.end(); iter++) { const SurfaceMontageLayoutOrientationEnum& 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 SurfaceMontageLayoutOrientationEnum")); } return enumValue; } /** * Get a GUI string representation of the enumerated type. * @param enumValue * Enumerated value. * @return * String representing enumerated value. */ AString SurfaceMontageLayoutOrientationEnum::toGuiName(Enum enumValue) { if (initializedFlag == false) initialize(); const SurfaceMontageLayoutOrientationEnum* 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. */ SurfaceMontageLayoutOrientationEnum::Enum SurfaceMontageLayoutOrientationEnum::fromGuiName(const AString& guiName, bool* isValidOut) { if (initializedFlag == false) initialize(); bool validFlag = false; Enum enumValue = SurfaceMontageLayoutOrientationEnum::enumData[0].enumValue; for (std::vector::iterator iter = enumData.begin(); iter != enumData.end(); iter++) { const SurfaceMontageLayoutOrientationEnum& 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 SurfaceMontageLayoutOrientationEnum")); } return enumValue; } /** * Get the integer code for a data type. * * @return * Integer code for data type. */ int32_t SurfaceMontageLayoutOrientationEnum::toIntegerCode(Enum enumValue) { if (initializedFlag == false) initialize(); const SurfaceMontageLayoutOrientationEnum* 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. */ SurfaceMontageLayoutOrientationEnum::Enum SurfaceMontageLayoutOrientationEnum::fromIntegerCode(const int32_t integerCode, bool* isValidOut) { if (initializedFlag == false) initialize(); bool validFlag = false; Enum enumValue = SurfaceMontageLayoutOrientationEnum::enumData[0].enumValue; for (std::vector::iterator iter = enumData.begin(); iter != enumData.end(); iter++) { const SurfaceMontageLayoutOrientationEnum& 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 SurfaceMontageLayoutOrientationEnum")); } 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 SurfaceMontageLayoutOrientationEnum::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 SurfaceMontageLayoutOrientationEnum::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(SurfaceMontageLayoutOrientationEnum::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 SurfaceMontageLayoutOrientationEnum::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(SurfaceMontageLayoutOrientationEnum::toGuiName(iter->enumValue)); } if (isSorted) { std::sort(allGuiNames.begin(), allGuiNames.end()); } } connectome-workbench-1.2.3+git41-gc4c6c90/src/Brain/SurfaceMontageLayoutOrientationEnum.h000066400000000000000000000064511300200146000311200ustar00rootroot00000000000000#ifndef __SURFACE_MONTAGE_LAYOUT_ORIENTATION_ENUM_H__ #define __SURFACE_MONTAGE_LAYOUT_ORIENTATION_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 SurfaceMontageLayoutOrientationEnum { public: /** * Enumerated values. */ enum Enum { /** */ LANDSCAPE_LAYOUT_ORIENTATION, /** */ PORTRAIT_LAYOUT_ORIENTATION }; ~SurfaceMontageLayoutOrientationEnum(); 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: SurfaceMontageLayoutOrientationEnum(const Enum enumValue, const AString& name, const AString& guiName); static const SurfaceMontageLayoutOrientationEnum* 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 __SURFACE_MONTAGE_LAYOUT_ORIENTATION_ENUM_DECLARE__ std::vector SurfaceMontageLayoutOrientationEnum::enumData; bool SurfaceMontageLayoutOrientationEnum::initializedFlag = false; int32_t SurfaceMontageLayoutOrientationEnum::integerCodeCounter = 0; #endif // __SURFACE_MONTAGE_LAYOUT_ORIENTATION_ENUM_DECLARE__ } // namespace #endif //__SURFACE_MONTAGE_LAYOUT_ORIENTATION_ENUM_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Brain/SurfaceMontageViewport.cxx000066400000000000000000000127521300200146000267750ustar00rootroot00000000000000 /*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 __SURFACE_MONTAGE_VIEWPORT_DECLARE__ #include "SurfaceMontageViewport.h" #undef __SURFACE_MONTAGE_VIEWPORT_DECLARE__ #include "CaretAssert.h" #include "Surface.h" #include "StructureEnum.h" using namespace caret; /** * \class caret::SurfaceMontageViewport * \brief Viewports in a surface montage */ /** * Constructor. * * @param surface * Surface in the montage. * @param projectionViewType * Projection view type. */ SurfaceMontageViewport::SurfaceMontageViewport(Surface* surface, const ProjectionViewTypeEnum::Enum projectionViewType) : CaretObject() { CaretAssert(surface); m_surface = surface; m_structure = surface->getStructure(); m_projectionViewType = projectionViewType; m_viewport[0] = -1; m_viewport[1] = -1; m_viewport[2] = -1; m_viewport[3] = -1; m_row = -1; m_column = -1; } /** * Destructor. */ SurfaceMontageViewport::~SurfaceMontageViewport() { } /** * @return true if the coordinates are inside the viewport, else false. * @param x * X-coordinate * @param y * Y-coordinate */ bool SurfaceMontageViewport::isInside(const int32_t x, const int32_t y) const { if (x < m_viewport[0]) return false; if (x > (m_viewport[0] + m_viewport[2])) return false; if (y < m_viewport[1]) return false; if (y > (m_viewport[1] + m_viewport[3])) return false; return true; } /** * Set the row and column. * * @param row * New value for row. * @param column * New value for column. */ void SurfaceMontageViewport::setRowAndColumn(const int32_t row, const int32_t column) { m_row = row; m_column = column; } /** * Get the viewport. * * @param viewportOut * Output containing viewport. */ void SurfaceMontageViewport::getViewport(int32_t viewportOut[4]) const { CaretAssert((m_viewport[0] >= 0) && (m_viewport[1] >= 0) && (m_viewport[2] > 0) && (m_viewport[3] > 1)); viewportOut[0] = m_viewport[0]; viewportOut[1] = m_viewport[1]; viewportOut[2] = m_viewport[2]; viewportOut[3] = m_viewport[3]; } /** * Set the viewport for this item. * @param viewport * Values for viewport. */ void SurfaceMontageViewport::setViewport(const int32_t viewport[4]) { m_viewport[0] = viewport[0]; m_viewport[1] = viewport[1]; m_viewport[2] = viewport[2]; m_viewport[3] = viewport[3]; } /** * @return Row of this item (0 is top) */ int32_t SurfaceMontageViewport::getRow() const { CaretAssert(m_row >= 0); return m_row; } /** * @return Column of this item (0 is left) */ int32_t SurfaceMontageViewport::getColumn() const { CaretAssert(m_column >= 0); return m_column; } /** * @return X-coordinate in viewport. */ int32_t SurfaceMontageViewport::getX() const { CaretAssert( m_viewport[0] >= 0); return m_viewport[0]; } /** * @return Y-Coordinate in viewport. */ int32_t SurfaceMontageViewport::getY() const { CaretAssert(m_viewport[1] >= 0); return m_viewport[1]; } /** * @return Width of viewport. */ int32_t SurfaceMontageViewport::getWidth() const { CaretAssert(m_viewport[2] > 0); return m_viewport[2]; } /** * @return Height of viewport. */ int32_t SurfaceMontageViewport::getHeight() const { CaretAssert(m_viewport[3] > 0); return m_viewport[3]; } /** * Find the number of rows and columns for the given montage viewports. * * @param montageViewports * The montage viewports. * @param numberOfRowsOut * Output number of rows. * @param numberOfColumnsOut * Output number of columns. */ void SurfaceMontageViewport::getNumberOfRowsAndColumns(const std::vector& montageViewports, int32_t& numberOfRowsOut, int32_t& numberOfColumnsOut) { numberOfRowsOut = 0; numberOfColumnsOut = 0; if (montageViewports.empty()) { return; } for (std::vector::const_iterator iter = montageViewports.begin(); iter != montageViewports.end(); iter++) { const SurfaceMontageViewport* svp = *iter; const int32_t row = svp->getRow(); const int32_t column = svp->getColumn(); if (row > numberOfRowsOut) { numberOfRowsOut = row; } if (column > numberOfColumnsOut) { numberOfColumnsOut = column; } } /* * Add one since row and column in viewports are indices that range 0 to 1 */ numberOfRowsOut++; numberOfColumnsOut++; } connectome-workbench-1.2.3+git41-gc4c6c90/src/Brain/SurfaceMontageViewport.h000066400000000000000000000065401300200146000264200ustar00rootroot00000000000000#ifndef __SURFACE_MONTAGE_VIEWPORT_H__ #define __SURFACE_MONTAGE_VIEWPORT_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 "ProjectionViewTypeEnum.h" #include "StructureEnum.h" namespace caret { class Surface; class SurfaceMontageViewport : public CaretObject { public: // SurfaceMontageViewport(); SurfaceMontageViewport(Surface* surface, const ProjectionViewTypeEnum::Enum projectionViewType); virtual ~SurfaceMontageViewport(); /** * @return The surface in the viewport. */ Surface* getSurface() const { return m_surface; } ProjectionViewTypeEnum::Enum getProjectionViewType() const { return m_projectionViewType; } bool isInside(const int32_t x, const int32_t y) const; /** * @return Row of this item (0 is top) */ int32_t getRow() const; /** * @return Column of this item (0 is left) */ int32_t getColumn() const; void setRowAndColumn(const int32_t row, const int32_t column); /** * @return X-coordinate in viewport. */ int32_t getX() const; /** * @return Y-Coordinate in viewport. */ int32_t getY() const; /** * @return Width of viewport. */ int32_t getWidth() const; /** * @return Height of viewport. */ int32_t getHeight() const; void getViewport(int32_t viewportOut[4]) const; void setViewport(const int32_t viewport[4]); static void getNumberOfRowsAndColumns(const std::vector& montageViewports, int32_t& numberOfRowsOut, int32_t& numberOfColumnsOut); private: int32_t m_row; int32_t m_column; int32_t m_viewport[4]; Surface* m_surface; ProjectionViewTypeEnum::Enum m_projectionViewType; StructureEnum::Enum m_structure; // ADD_NEW_METHODS_HERE private: // ADD_NEW_MEMBERS_HERE }; #ifdef __SURFACE_MONTAGE_VIEWPORT_DECLARE__ // #endif // __SURFACE_MONTAGE_VIEWPORT_DECLARE__ } // namespace #endif //__SURFACE_MONTAGE_VIEWPORT_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Brain/SurfaceNodeColoring.cxx000066400000000000000000001644221300200146000262270ustar00rootroot00000000000000 /*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 __SURFACE_NODE_COLORING_DECLARE__ #include "SurfaceNodeColoring.h" #undef __SURFACE_NODE_COLORING_DECLARE__ #include "Brain.h" #include "BrainordinateRegionOfInterest.h" #include "BrainStructure.h" #include "BrowserTabContent.h" #include "EventBrowserTabGet.h" #include "CaretAssert.h" #include "CaretLogger.h" #include "CaretPreferences.h" #include "CiftiBrainordinateDataSeriesFile.h" #include "CiftiBrainordinateLabelFile.h" #include "CiftiBrainordinateScalarFile.h" #include "CiftiConnectivityMatrixParcelFile.h" #include "CiftiMappableConnectivityMatrixDataFile.h" #include "CiftiParcelLabelFile.h" #include "CiftiParcelScalarFile.h" #include "CiftiParcelSeriesFile.h" #include "DisplayPropertiesSurface.h" #include "GroupAndNameHierarchyModel.h" #include "DisplayPropertiesLabels.h" #include "EventManager.h" #include "EventModelSurfaceGet.h" #include "GiftiLabel.h" #include "GiftiLabelTable.h" #include "GroupAndNameHierarchyGroup.h" #include "LabelFile.h" #include "LabelDrawingProperties.h" #include "MetricFile.h" #include "ModelSurface.h" #include "ModelSurfaceMontage.h" #include "ModelVolume.h" #include "ModelWholeBrain.h" #include "NodeAndVoxelColoring.h" #include "Overlay.h" #include "OverlaySet.h" #include "Palette.h" #include "PaletteColorMapping.h" #include "PaletteFile.h" #include "PaletteScalarAndColor.h" #include "RgbaFile.h" #include "SessionManager.h" #include "Surface.h" #include "TopologyHelper.h" using namespace caret; /** * Constructor. */ SurfaceNodeColoring::SurfaceNodeColoring() : CaretObject() { } /** * Destructor. */ SurfaceNodeColoring::~SurfaceNodeColoring() { } /** * Get a description of this object's content. * @return String describing this object's content. */ AString SurfaceNodeColoring::toString() const { return "SurfaceNodeColoring"; } /** * Assign color components to surface nodes. * If colors are currently valid, no changes are made to the surface coloring. * @param model * Model that is displayed. If NULL use find ModelSurface * for the surface. This case occurs when needing surface coloring * when surface outline is drawn on volume slices. * @param surface * Surface that is displayed. * @param browserTabIndex * Index of tab in which model is displayed. */ float* SurfaceNodeColoring::colorSurfaceNodes(Model* model, Surface* surface, const int32_t browserTabIndex) { CaretAssert(surface); ModelSurface* surfaceModel = dynamic_cast(model); ModelSurfaceMontage* surfaceMontageModel = dynamic_cast(model); ModelWholeBrain* wholeBrainModel = dynamic_cast(model); OverlaySet* overlaySet = NULL; float* rgba = NULL; EventBrowserTabGet getBrowserTab(browserTabIndex); EventManager::get()->sendEvent(getBrowserTab.getPointer()); BrowserTabContent* browserTabContent = getBrowserTab.getBrowserTab(); Brain* brain = NULL; if (model != NULL) { brain = model->getBrain(); } /* * For a NULL model, find and use the surface model for the * surface and in the same tab as the volume model. This typically * occurs when the volume surface outline is drawn over a volume slice. */ if (model == NULL) { EventModelSurfaceGet surfaceGet(surface); EventManager::get()->sendEvent(surfaceGet.getPointer()); surfaceModel = surfaceGet.getModelSurface(); CaretAssert(surfaceModel); if (surfaceModel != NULL) { brain = surfaceModel->getBrain(); } /* * If whole brain is displayed in the tab, use coloring * from whole brain instead of surface. */ if (browserTabContent != NULL) { ModelWholeBrain* wholeBrain = browserTabContent->getDisplayedWholeBrainModel(); if (wholeBrain != NULL) { wholeBrainModel = wholeBrain; brain = wholeBrainModel->getBrain(); surfaceModel = NULL; } ModelSurfaceMontage* surfMont = browserTabContent->getDisplayedSurfaceMontageModel(); if (surfMont != NULL) { surfaceMontageModel = surfMont; brain = surfaceMontageModel->getBrain(); surfaceModel = NULL; } } } /* * Get coloring and overlays for the valid model. */ if (surfaceModel != NULL) { rgba = surface->getSurfaceNodeColoringRgbaForBrowserTab(browserTabIndex); overlaySet = surfaceModel->getOverlaySet(browserTabIndex); } else if (surfaceMontageModel != NULL) { rgba = surface->getSurfaceMontageNodeColoringRgbaForBrowserTab(browserTabIndex); overlaySet = surfaceMontageModel->getOverlaySet(browserTabIndex); } else if (wholeBrainModel != NULL) { rgba = surface->getWholeBrainNodeColoringRgbaForBrowserTab(browserTabIndex); overlaySet = wholeBrainModel->getOverlaySet(browserTabIndex); } CaretAssert(overlaySet); /* * RGBA will be Non-NULL if the surface HAS valid coloring */ if (rgba != NULL) { return rgba; } /* * Drawing type for labels */ DisplayPropertiesLabels* displayPropertiesLabels = NULL; if (brain != NULL) { displayPropertiesLabels = brain->getDisplayPropertiesLabels(); } const int numNodes = surface->getNumberOfNodes(); const int numColorComponents = numNodes * 4; float *rgbaColor = new float[numColorComponents]; /* * Color the surface nodes */ this->colorSurfaceNodes(displayPropertiesLabels, browserTabIndex, surface, overlaySet, rgbaColor); if (surfaceModel != NULL) { surface->setSurfaceNodeColoringRgbaForBrowserTab(browserTabIndex, rgbaColor); rgba = surface->getSurfaceNodeColoringRgbaForBrowserTab(browserTabIndex); } else if (surfaceMontageModel != NULL) { surface->setSurfaceMontageNodeColoringRgbaForBrowserTab(browserTabIndex, rgbaColor); rgba = surface->getSurfaceMontageNodeColoringRgbaForBrowserTab(browserTabIndex); } else if (wholeBrainModel != NULL) { surface->setWholeBrainNodeColoringRgbaForBrowserTab(browserTabIndex, rgbaColor); rgba = surface->getWholeBrainNodeColoringRgbaForBrowserTab(browserTabIndex); } if(rgbaColor) delete [] rgbaColor; return rgba; } /** * Show brainordinate region of interest highlighting on the surface. * * @param brain * The brain. * @param surface * Surface on which highlighting is displayed. * @param rgbaNodeColors * Node coloring that is updated with highlighting. */ void SurfaceNodeColoring::showBrainordinateHighlightRegionOfInterest(const Brain* brain, const Surface* surface, float* rgbaNodeColors) { CaretPreferences* prefs = SessionManager::get()->getCaretPreferences(); uint8_t foregroundColorByte[4]; prefs->getBackgroundAndForegroundColors()->getColorForegroundSurfaceView(foregroundColorByte); const float foregroundColor[4] = { foregroundColorByte[0], foregroundColorByte[1], foregroundColorByte[2], 1.0 }; const BrainordinateRegionOfInterest* roi = brain->getBrainordinateHighlightRegionOfInterest(); CaretAssert(roi); if (roi->isBrainordinateHighlightingEnabled()) { const StructureEnum::Enum structure = surface->getStructure(); const int64_t surfaceNumberOfNodes = surface->getNumberOfNodes(); if (roi->hasNodesForSurfaceStructure(structure, surfaceNumberOfNodes)) { const std::vector& nodeIndices = roi->getNodesForSurfaceStructure(structure, surfaceNumberOfNodes); for (std::vector::const_iterator nodeIter = nodeIndices.begin(); nodeIter != nodeIndices.end(); nodeIter++) { const int64_t nodeIndex = *nodeIter; const int64_t rgbaIndex = nodeIndex * 4; CaretAssertArrayIndex(rgbaNodeColors, surfaceNumberOfNodes*4 , rgbaIndex + 3); rgbaNodeColors[rgbaIndex] = foregroundColor[0]; rgbaNodeColors[rgbaIndex+1] = foregroundColor[1]; rgbaNodeColors[rgbaIndex+2] = foregroundColor[2]; rgbaNodeColors[rgbaIndex+3] = foregroundColor[3]; } } } } /** * Assign color components to surface nodes. * * @param surface * Surface that has its nodes colored. * @param overlaySet * Surface overlay assignments for surface. * @param rgbaNodeColors * RGBA color components that are set by this method. */ void SurfaceNodeColoring::colorSurfaceNodes(const DisplayPropertiesLabels* displayPropertiesLabels, const int32_t browserTabIndex, const Surface* surface, OverlaySet* overlaySet, float* rgbaNodeColors) { const int32_t numNodes = surface->getNumberOfNodes(); const int32_t numberOfDisplayedOverlays = overlaySet->getNumberOfDisplayedOverlays(); /* * Default color. */ for (int32_t i = 0; i < numNodes; i++) { const int32_t i4 = i * 4; rgbaNodeColors[i4] = 0.70; rgbaNodeColors[i4+1] = 0.70; rgbaNodeColors[i4+2] = 0.70; rgbaNodeColors[i4+3] = 1.0; } const BrainStructure* brainStructure = surface->getBrainStructure(); CaretAssert(brainStructure); const Brain* brain = brainStructure->getBrain(); CaretAssert(brain); bool firstOverlayFlag = true; float* overlayRGBV = new float[numNodes * 4]; for (int32_t iOver = (numberOfDisplayedOverlays - 1); iOver >= 0; iOver--) { Overlay* overlay = overlaySet->getOverlay(iOver); if (overlay->isEnabled()) { std::vector mapFiles; CaretMappableDataFile* selectedMapFile; int32_t selectedMapIndex; overlay->getSelectionData(mapFiles, selectedMapFile, selectedMapIndex); DataFileTypeEnum::Enum mapDataFileType = DataFileTypeEnum::UNKNOWN; if (selectedMapFile != NULL) { mapDataFileType = selectedMapFile->getDataFileType(); } bool isColoringValid = false; switch (mapDataFileType) { case DataFileTypeEnum::ANNOTATION: break; case DataFileTypeEnum::BORDER: break; case DataFileTypeEnum::CONNECTIVITY_DENSE: { CiftiMappableConnectivityMatrixDataFile* cmf = dynamic_cast(selectedMapFile); isColoringValid = assignCiftiMappableConnectivityMatrixColoring(brainStructure, cmf, selectedMapIndex, numNodes, overlayRGBV); } break; case DataFileTypeEnum::CONNECTIVITY_DENSE_DYNAMIC: { CiftiMappableConnectivityMatrixDataFile* cmf = dynamic_cast(selectedMapFile); isColoringValid = assignCiftiMappableConnectivityMatrixColoring(brainStructure, cmf, selectedMapIndex, numNodes, overlayRGBV); } break; case DataFileTypeEnum::CONNECTIVITY_DENSE_LABEL: isColoringValid = this->assignCiftiDenseLabelColoring(displayPropertiesLabels, browserTabIndex, brainStructure, surface, dynamic_cast(selectedMapFile), selectedMapIndex, numNodes, overlayRGBV); break; case DataFileTypeEnum::CONNECTIVITY_DENSE_PARCEL: { CiftiMappableConnectivityMatrixDataFile* cmf = dynamic_cast(selectedMapFile); isColoringValid = assignCiftiMappableConnectivityMatrixColoring(brainStructure, cmf, selectedMapIndex, numNodes, overlayRGBV); } break; case DataFileTypeEnum::CONNECTIVITY_DENSE_SCALAR: isColoringValid = this->assignCiftiScalarColoring(brainStructure, dynamic_cast(selectedMapFile), selectedMapIndex, numNodes, overlayRGBV); break; case DataFileTypeEnum::CONNECTIVITY_DENSE_TIME_SERIES: isColoringValid = this->assignCiftiDataSeriesColoring(brainStructure, dynamic_cast(selectedMapFile), selectedMapIndex, numNodes, overlayRGBV); break; case DataFileTypeEnum::CONNECTIVITY_FIBER_ORIENTATIONS_TEMPORARY: break; case DataFileTypeEnum::CONNECTIVITY_FIBER_TRAJECTORY_TEMPORARY: break; case DataFileTypeEnum::CONNECTIVITY_PARCEL: { CiftiMappableConnectivityMatrixDataFile* cmf = dynamic_cast(selectedMapFile); isColoringValid = assignCiftiMappableConnectivityMatrixColoring(brainStructure, cmf, selectedMapIndex, numNodes, overlayRGBV); } break; case DataFileTypeEnum::CONNECTIVITY_PARCEL_DENSE: { CiftiMappableConnectivityMatrixDataFile* cmf = dynamic_cast(selectedMapFile); isColoringValid = assignCiftiMappableConnectivityMatrixColoring(brainStructure, cmf, selectedMapIndex, numNodes, overlayRGBV); } break; case DataFileTypeEnum::CONNECTIVITY_PARCEL_LABEL: { CiftiParcelLabelFile* cplf = dynamic_cast(selectedMapFile); isColoringValid = assignCiftiParcelLabelColoring(displayPropertiesLabels, browserTabIndex, brainStructure, surface, cplf, selectedMapIndex, numNodes, overlayRGBV); } break; case DataFileTypeEnum::CONNECTIVITY_PARCEL_SCALAR: isColoringValid = this->assignCiftiParcelScalarColoring(brainStructure, dynamic_cast(selectedMapFile), selectedMapIndex, numNodes, overlayRGBV); break; case DataFileTypeEnum::CONNECTIVITY_PARCEL_SERIES: isColoringValid = this->assignCiftiParcelSeriesColoring(brainStructure, dynamic_cast(selectedMapFile), selectedMapIndex, numNodes, overlayRGBV); break; case DataFileTypeEnum::CONNECTIVITY_SCALAR_DATA_SERIES: break; case DataFileTypeEnum::FOCI: break; case DataFileTypeEnum::IMAGE: break; case DataFileTypeEnum::LABEL: isColoringValid = this->assignLabelColoring(displayPropertiesLabels, browserTabIndex, brainStructure, surface, dynamic_cast(selectedMapFile), selectedMapIndex, numNodes, overlayRGBV); break; case DataFileTypeEnum::METRIC: isColoringValid = this->assignMetricColoring(brainStructure, dynamic_cast(selectedMapFile), selectedMapIndex, numNodes, overlayRGBV); break; case DataFileTypeEnum::PALETTE: break; case DataFileTypeEnum::RGBA: isColoringValid = this->assignRgbaColoring(brainStructure, dynamic_cast(selectedMapFile), selectedMapIndex, numNodes, overlayRGBV); break; case DataFileTypeEnum::SCENE: break; case DataFileTypeEnum::SPECIFICATION: break; case DataFileTypeEnum::SURFACE: break; case DataFileTypeEnum::VOLUME: break; case DataFileTypeEnum::UNKNOWN: break; } if (isColoringValid) { const float opacity = overlay->getOpacity(); const float oneMinusOpacity = 1.0 - opacity; for (int32_t i = 0; i < numNodes; i++) { const int32_t i4 = i * 4; const float valid = overlayRGBV[i4 + 3]; if (valid > 0.0 ) { if (opacity < 1.0) { if (firstOverlayFlag) { /* * When first overlay, there is nothing to * blend with */ rgbaNodeColors[i4] = (overlayRGBV[i4] * opacity); rgbaNodeColors[i4+1] = (overlayRGBV[i4+1] * opacity); rgbaNodeColors[i4+2] = (overlayRGBV[i4+2] * opacity); } else { /* * Blend with underlaying colors */ rgbaNodeColors[i4] = (overlayRGBV[i4] * opacity) + (rgbaNodeColors[i4] * oneMinusOpacity); rgbaNodeColors[i4+1] = (overlayRGBV[i4+1] * opacity) + (rgbaNodeColors[i4+1] * oneMinusOpacity); rgbaNodeColors[i4+2] = (overlayRGBV[i4+2] * opacity) + (rgbaNodeColors[i4+2] * oneMinusOpacity); } } else { /* * No opacity so simple replace coloring */ rgbaNodeColors[i4] = overlayRGBV[i4]; rgbaNodeColors[i4+1] = overlayRGBV[i4+1]; rgbaNodeColors[i4+2] = overlayRGBV[i4+2]; } } } firstOverlayFlag = false; } } } /* * Opacity from first overlay is used as overall surface opacity * so replace alpha with opacity */ const float opacity = brain->getDisplayPropertiesSurface()->getOpacity(); if (opacity < 1.0) { for (int32_t i = 0; i < numNodes; i++) { const int32_t i4 = i * 4; rgbaNodeColors[i4+3] = opacity; } } showBrainordinateHighlightRegionOfInterest(brain, surface, rgbaNodeColors); delete[] overlayRGBV; } /** * Assign label coloring to nodes * @param brainStructure * The brain structure that contains the data files. * @param labelFile * Label file that is selected. * @param labelMapUniqueID * UniqueID of selected map. * @param numberOfNodes * Number of nodes in surface. * @param rgbv * Color components set by this method. * Red, green, blue, valid. If the valid component is * zero, it indicates that the overlay did not assign * any coloring to the node. * @return * True if coloring is valid, else false. */ bool SurfaceNodeColoring::assignLabelColoring(const DisplayPropertiesLabels* displayPropertiesLabels, const int32_t browserTabIndex, const BrainStructure* /*brainStructure*/, const Surface* surface, const LabelFile* labelFile, const int32_t displayColumn, const int32_t numberOfNodes, float* rgbv) { if (labelFile == NULL) { return false; } if ( ! labelFile->isMappableToSurfaceStructure(surface->getStructure())) { return false; } const LabelDrawingProperties* props = labelFile->getLabelDrawingProperties(); LabelDrawingTypeEnum::Enum labelDrawingType = props->getDrawingType(); CaretColorEnum::Enum outlineColor = props->getOutlineColor(); const DisplayGroupEnum::Enum displayGroup = displayPropertiesLabels->getDisplayGroupForTab(browserTabIndex); const GroupAndNameHierarchyModel* classNameModel = labelFile->getGroupAndNameHierarchyModel(); if (classNameModel->isSelected(displayGroup, browserTabIndex) == false) { return false; } if (displayColumn < 0) { return false; } /* * Invalidate all coloring. */ for (int32_t i = 0; i < numberOfNodes; i++) { rgbv[i*4+3] = 0.0; } const GiftiLabelTable* labelTable = labelFile->getLabelTable(); CaretAssert(surface); CaretPointer topologyHelper = surface->getTopologyHelper(); /* * Assign colors from labels to nodes */ std::vector labelKeys; for (int32_t i = 0; i < numberOfNodes; i++) { labelKeys.push_back(labelFile->getLabelKey(i, displayColumn)); } const bool drawMedialWallFilledFlag = props->isDrawMedialWallFilled(); assignLabelTableColors(labelTable, labelDrawingType, outlineColor, topologyHelper, displayGroup, browserTabIndex, labelKeys, drawMedialWallFilledFlag, rgbv); return true; } /** * Assign label coloring to surface nodes. * * @param labelTable * The label table. * @param labelDrawingType * Label drawing type. * @param outlineColor * Outline color. * @param topologyHelper * The topology helper for node neighbors. * @param displayGroup * Selected Display Group * @param browserTabIndex * Index of browser tab. * @param labelIndices * Indices of labels for each node. * @param medialWallLabelKey * Index of the medial wall label key * @param drawMedialWallFilledFlag * True if medial wall is always filled * @param rgbv * RGB coloring. (4 per node). * */ void SurfaceNodeColoring::assignLabelTableColors(const GiftiLabelTable* labelTable, const LabelDrawingTypeEnum::Enum labelDrawingType, const CaretColorEnum::Enum outlineColor, const CaretPointer topologyHelper, const DisplayGroupEnum::Enum displayGroup, const int32_t browserTabIndex, const std::vector& labelIndices, const bool drawMedialWallFilledFlag, float* rgbv) { const int32_t numberOfIndices = static_cast(labelIndices.size()); /* * Invalidate all coloring. */ for (int32_t i = 0; i < numberOfIndices; i++) { rgbv[i*4+3] = 0.0; } float outlineRGBA[4]; CaretColorEnum::toRGBFloat(outlineColor, outlineRGBA); outlineRGBA[3] = 1.0; /* * Assign colors from labels to nodes */ float nodeRGBA[4]; for (int32_t i = 0; i < numberOfIndices; i++) { CaretAssertVectorIndex(labelIndices, i); const int32_t labelKey= static_cast(labelIndices[i]); const GiftiLabel* label = labelTable->getLabel(labelKey); if (label == NULL) { continue; } const GroupAndNameHierarchyItem* nameItem = label->getGroupNameSelectionItem(); if (nameItem != NULL) { if (nameItem->isSelected(displayGroup, browserTabIndex) == false) { continue; } } /* * Initialize node color to its label's color */ label->getColor(nodeRGBA); if (nodeRGBA[3] <= 0.0) { continue; } /* * If a node is the same color as all of its neighbors, * use the fill color. Otherwise, use the outline color. */ bool isLabelBoundaryNode = false; int32_t numNeighbors = 0; const int32_t* allNeighbors = topologyHelper->getNodeNeighbors(i, numNeighbors); for (int32_t n = 0; n < numNeighbors; n++) { const int32_t neighborNodeIndex = allNeighbors[n]; CaretAssertVectorIndex(labelIndices, neighborNodeIndex); const int32_t neighborLabelKey = static_cast(labelIndices[neighborNodeIndex]); if (labelKey != neighborLabelKey) { isLabelBoundaryNode = true; break; } } /* * User may request that medial wall is always filled * and never receives outlining */ bool doOutlineFlag = true; if (drawMedialWallFilledFlag) { if (label->isMedialWallName()) { doOutlineFlag = false; } } if (doOutlineFlag) { switch (labelDrawingType) { case LabelDrawingTypeEnum::DRAW_FILLED: break; case LabelDrawingTypeEnum::DRAW_FILLED_WITH_OUTLINE_COLOR: if (isLabelBoundaryNode) { nodeRGBA[0] = outlineRGBA[0]; nodeRGBA[1] = outlineRGBA[1]; nodeRGBA[2] = outlineRGBA[2]; nodeRGBA[3] = outlineRGBA[3]; } break; case LabelDrawingTypeEnum::DRAW_OUTLINE_COLOR: if (isLabelBoundaryNode) { nodeRGBA[0] = outlineRGBA[0]; nodeRGBA[1] = outlineRGBA[1]; nodeRGBA[2] = outlineRGBA[2]; nodeRGBA[3] = outlineRGBA[3]; } else { nodeRGBA[3] = 0.0; } break; case LabelDrawingTypeEnum::DRAW_OUTLINE_LABEL_COLOR: if ( ! isLabelBoundaryNode) { nodeRGBA[3] = 0.0; } break; } } const int32_t i4 = i * 4; rgbv[i4] = nodeRGBA[0]; rgbv[i4+1] = nodeRGBA[1]; rgbv[i4+2] = nodeRGBA[2]; rgbv[i4+3] = nodeRGBA[3]; } } /** * Assign metric coloring to nodes * @param brainStructure * The brain structure that contains the data files. * @param metricFile * Metric file that is selected. * @param metricMapUniqueID * UniqueID of selected map. * @param numberOfNodes * Number of nodes in surface. * @param rgbv * Color components set by this method. * Red, green, blue, valid. If the valid component is * zero, it indicates that the overlay did not assign * any coloring to the node. * @return * True if coloring is valid, else false. */ bool SurfaceNodeColoring::assignMetricColoring(const BrainStructure* brainStructure, MetricFile* metricFile, const int32_t displayColumn, const int32_t numberOfNodes, float* rgbv) { if (displayColumn < 0) { return false; } if ( ! metricFile->isMappableToSurfaceStructure(brainStructure->getStructure())) { return false; } const PaletteColorMapping* paletteColorMapping = metricFile->getPaletteColorMapping(displayColumn); /* * Invalidate all coloring. */ for (int32_t i = 0; i < numberOfNodes; i++) { rgbv[i*4+3] = 0.0; } /* * Get min/max ranges. */ int thresholdColumn = metricFile->getColumnIndexFromColumnName(paletteColorMapping->getThresholdDataName()); if (thresholdColumn < 0) { thresholdColumn = displayColumn; } const float* metricDisplayData = metricFile->getValuePointerForColumn(displayColumn); const float* metricThresholdData = metricFile->getValuePointerForColumn(thresholdColumn); FastStatistics* statistics = NULL; switch (metricFile->getPaletteNormalizationMode()) { case PaletteNormalizationModeEnum::NORMALIZATION_ALL_MAP_DATA: statistics = const_cast(metricFile->getFileFastStatistics()); break; case PaletteNormalizationModeEnum::NORMALIZATION_SELECTED_MAP_DATA: statistics = const_cast(metricFile->getMapFastStatistics(displayColumn)); break; } 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)) { NodeAndVoxelColoring::colorScalarsWithPalette(statistics, paletteColorMapping, palette, metricDisplayData, metricThresholdData, numberOfNodes, rgbv); } else { CaretLogSevere("Selected palette for metric is invalid: \"" + paletteName + "\""); } return true; } /** * Assign cifti scalar coloring to nodes * @param brainStructure * The brain structure that contains the data files. * @param ciftiScalarFile * Cifti Scalar file that is selected. * @param ciftiMapUniqueID * UniqueID of selected map. * @param numberOfNodes * Number of nodes in surface. * @param rgbv * Color components set by this method. * Red, green, blue, valid. If the valid component is * zero, it indicates that the overlay did not assign * any coloring to the node. * @return * True if coloring is valid, else false. */ bool SurfaceNodeColoring::assignCiftiMappableConnectivityMatrixColoring(const BrainStructure* brainStructure, CiftiMappableConnectivityMatrixDataFile* ciftiConnectivityMatrixFile, const int32_t mapIndex, const int32_t numberOfNodes, float* rgbv) { CaretAssert(ciftiConnectivityMatrixFile); if (mapIndex < 0) { return false; } /* * Invalidate all coloring. */ for (int32_t i = 0; i < numberOfNodes; i++) { rgbv[i*4+3] = 0.0; } const StructureEnum::Enum structure = brainStructure->getStructure(); std::vector dataValues(numberOfNodes); ciftiConnectivityMatrixFile->getMapSurfaceNodeColoring(brainStructure->getBrain()->getPaletteFile(), mapIndex, structure, rgbv, &dataValues[0], numberOfNodes); CiftiConnectivityMatrixParcelFile* parcelFile = dynamic_cast(ciftiConnectivityMatrixFile); if (parcelFile != NULL) { const Surface* surface = brainStructure->getPrimaryAnatomicalSurface(); CaretPointer topologyHelper = surface->getTopologyHelper(); std::set nodeSet; bool selectedParcelValid = false; selectedParcelValid = ciftiConnectivityMatrixFile->getParcelNodesElementForSelectedParcel(nodeSet,structure); std::vector selectedParcelNodes = std::vector(nodeSet.begin(), nodeSet.end()); if(selectedParcelValid) { const CaretColorEnum::Enum parcelColor = parcelFile->getSelectedParcelColor(); const float* rgb = CaretColorEnum::toRGB(parcelColor); CaretAssert(rgb); switch (parcelFile->getSelectedParcelColoringMode()) { case CiftiParcelColoringModeEnum::CIFTI_PARCEL_COLORING_OFF: break; case CiftiParcelColoringModeEnum::CIFTI_PARCEL_COLORING_FILL: { for(uint64_t pNode = 0; pNode < selectedParcelNodes.size(); pNode++) { int64_t nodeIndex = selectedParcelNodes[pNode]; if(nodeIndex >= 0 && nodeIndex < numberOfNodes) { uint64_t node4 = nodeIndex*4; rgbv[node4] = rgb[0]; rgbv[node4+1] = rgb[1]; rgbv[node4+2] = rgb[2]; rgbv[node4+3] = 1.0; continue; } } } break; case CiftiParcelColoringModeEnum::CIFTI_PARCEL_COLORING_OUTLINE: { /* * Check for any neighbors with different label key. */ //make a quick lookup table std::vector selectedNodesLookup(numberOfNodes,0); for(uint64_t pNode = 0;pNode < selectedParcelNodes.size();pNode++) { int64_t nodeIndex = selectedParcelNodes[pNode]; if(nodeIndex >= 0 && nodeIndex < (int64_t)selectedNodesLookup.size()) { selectedNodesLookup[nodeIndex] = 1; } } for(uint64_t pNode = 0;pNode < selectedParcelNodes.size();pNode++) { int32_t numNeighbors = 0; const int64_t nodeIndex = selectedParcelNodes[pNode]; const int32_t* allNeighbors = topologyHelper->getNodeNeighbors(nodeIndex, numNeighbors); for (int32_t n = 0; n < numNeighbors; n++) { const int32_t neighbor = allNeighbors[n]; if (!selectedNodesLookup[neighbor]) { int64_t node4 = nodeIndex*4; rgbv[node4] = rgb[0]; rgbv[node4+1] = rgb[1]; rgbv[node4+2] = rgb[2]; rgbv[node4+3] = 1.0; continue; } } } } break; } } } return true; } /** * Assign cifti dense label coloring to nodes * @param brainStructure * The brain structure that contains the data files. * @param ciftiLabelFile * Cifti Label file that is selected. * @param ciftiMapUniqueID * UniqueID of selected map. * @param numberOfNodes * Number of nodes in surface. * @param rgbv * Color components set by this method. * Red, green, blue, valid. If the valid component is * zero, it indicates that the overlay did not assign * any coloring to the node. * @return * True if coloring is valid, else false. */ bool SurfaceNodeColoring::assignCiftiDenseLabelColoring(const DisplayPropertiesLabels* displayPropertiesLabels, const int32_t browserTabIndex, const BrainStructure* brainStructure, const Surface* surface, CiftiBrainordinateLabelFile* ciftiLabelFile, const int32_t mapIndex, const int32_t numberOfNodes, float* rgbv) { CaretAssert(displayPropertiesLabels); CaretAssert(brainStructure); CaretAssert(ciftiLabelFile); CaretAssert(rgbv); Brain* brain = (Brain*)(brainStructure->getBrain()); std::vector allCiftiBrainordinateLabelFiles; brain->getConnectivityDenseLabelFiles(allCiftiBrainordinateLabelFiles); if (mapIndex < 0) { return false; } const DisplayGroupEnum::Enum displayGroup = displayPropertiesLabels->getDisplayGroupForTab(browserTabIndex); const LabelDrawingProperties* props = ciftiLabelFile->getLabelDrawingProperties(); LabelDrawingTypeEnum::Enum labelDrawingType = props->getDrawingType(); CaretColorEnum::Enum outlineColor = props->getOutlineColor(); /* * Invalidate all coloring. */ for (int32_t i = 0; i < numberOfNodes; i++) { rgbv[i*4+3] = 0.0; } /* * Update coloring */ if (ciftiLabelFile->isMapColoringValid(mapIndex) == false) { ciftiLabelFile->updateScalarColoringForMap(mapIndex, brain->getPaletteFile()); } std::vector dataValues(numberOfNodes); /* * Assigns colors for all nodes. */ const StructureEnum::Enum structure = brainStructure->getStructure(); ciftiLabelFile->getMapSurfaceNodeColoring(brainStructure->getBrain()->getPaletteFile(), mapIndex, structure, rgbv, &dataValues[0], numberOfNodes); CaretAssert(surface); CaretPointer topologyHelper = surface->getTopologyHelper(); /* * All nodes are colored. Remove coloring for nodes whose * label is not selected. */ GiftiLabelTable* labelTable = ciftiLabelFile->getMapLabelTable(mapIndex); CaretAssert(labelTable); const bool drawMedialWallFilledFlag = props->isDrawMedialWallFilled(); assignLabelTableColors(labelTable, labelDrawingType, outlineColor, topologyHelper, displayGroup, browserTabIndex, dataValues, drawMedialWallFilledFlag, rgbv); return true; } /** * Assign cifti parcel label coloring to nodes * @param brainStructure * The brain structure that contains the data files. * @param ciftiParcelLabelFile * Cifti Parcel Label file that is selected. * @param mapIndex * Index of selected map. * @param numberOfNodes * Number of nodes in surface. * @param rgbv * Color components set by this method. * Red, green, blue, valid. If the valid component is * zero, it indicates that the overlay did not assign * any coloring to the node. * @return * True if coloring is valid, else false. */ bool SurfaceNodeColoring::assignCiftiParcelLabelColoring(const DisplayPropertiesLabels* displayPropertiesLabels, const int32_t browserTabIndex, const BrainStructure* brainStructure, const Surface* surface, CiftiParcelLabelFile* ciftiParcelLabelFile, const int32_t mapIndex, const int32_t numberOfNodes, float* rgbv) { CaretAssert(displayPropertiesLabels); CaretAssert(brainStructure); CaretAssert(ciftiParcelLabelFile); CaretAssert(rgbv); Brain* brain = (Brain*)(brainStructure->getBrain()); std::vector allCiftiBrainordinateLabelFiles; brain->getConnectivityDenseLabelFiles(allCiftiBrainordinateLabelFiles); if (mapIndex < 0) { return false; } const DisplayGroupEnum::Enum displayGroup = displayPropertiesLabels->getDisplayGroupForTab(browserTabIndex); const LabelDrawingProperties* props = ciftiParcelLabelFile->getLabelDrawingProperties(); LabelDrawingTypeEnum::Enum labelDrawingType = props->getDrawingType(); CaretColorEnum::Enum outlineColor = props->getOutlineColor(); /* * Invalidate all coloring. */ for (int32_t i = 0; i < numberOfNodes; i++) { rgbv[i*4+3] = 0.0; } /* * Update coloring */ if (ciftiParcelLabelFile->isMapColoringValid(mapIndex) == false) { ciftiParcelLabelFile->updateScalarColoringForMap(mapIndex, brain->getPaletteFile()); } std::vector dataValues(numberOfNodes); /* * Assigns colors for all nodes. */ const StructureEnum::Enum structure = brainStructure->getStructure(); ciftiParcelLabelFile->getMapSurfaceNodeColoring(brainStructure->getBrain()->getPaletteFile(), mapIndex, structure, rgbv, &dataValues[0], numberOfNodes); CaretAssert(surface); CaretPointer topologyHelper = surface->getTopologyHelper(); /* * All nodes are colored. Remove coloring for nodes whose * label is not selected. */ GiftiLabelTable* labelTable = ciftiParcelLabelFile->getMapLabelTable(mapIndex); CaretAssert(labelTable); const bool drawMedialWallFilledFlag = props->isDrawMedialWallFilled(); assignLabelTableColors(labelTable, labelDrawingType, outlineColor, topologyHelper, displayGroup, browserTabIndex, dataValues, drawMedialWallFilledFlag, rgbv); return true; } /** * Assign cifti scalar coloring to nodes * @param brainStructure * The brain structure that contains the data files. * @param ciftiScalarFile * Cifti Scalar file that is selected. * @param ciftiMapUniqueID * UniqueID of selected map. * @param numberOfNodes * Number of nodes in surface. * @param rgbv * Color components set by this method. * Red, green, blue, valid. If the valid component is * zero, it indicates that the overlay did not assign * any coloring to the node. * @return * True if coloring is valid, else false. */ bool SurfaceNodeColoring::assignCiftiScalarColoring(const BrainStructure* brainStructure, CiftiBrainordinateScalarFile* ciftiScalarFile, const int32_t mapIndex, const int32_t numberOfNodes, float* rgbv) { Brain* brain = (Brain*)(brainStructure->getBrain()); std::vector allCiftiBrainordinateScalarFiles; brain->getConnectivityDenseScalarFiles(allCiftiBrainordinateScalarFiles); if (mapIndex < 0) { return false; } /* * Invalidate all coloring. */ for (int32_t i = 0; i < numberOfNodes; i++) { rgbv[i*4+3] = 0.0; } /* * Update coloring */ if (ciftiScalarFile->isMapColoringValid(mapIndex) == false) { ciftiScalarFile->updateScalarColoringForMap(mapIndex, brain->getPaletteFile()); } std::vector dataValues(numberOfNodes); const StructureEnum::Enum structure = brainStructure->getStructure(); ciftiScalarFile->getMapSurfaceNodeColoring(brainStructure->getBrain()->getPaletteFile(), mapIndex, structure, rgbv, &dataValues[0], numberOfNodes); return true; } /** * Assign cifti parcel scalar coloring to nodes * @param brainStructure * The brain structure that contains the data files. * @param ciftiScalarFile * Cifti Scalar file that is selected. * @param ciftiMapUniqueID * UniqueID of selected map. * @param numberOfNodes * Number of nodes in surface. * @param rgbv * Color components set by this method. * Red, green, blue, valid. If the valid component is * zero, it indicates that the overlay did not assign * any coloring to the node. * @return * True if coloring is valid, else false. */ bool SurfaceNodeColoring::assignCiftiParcelScalarColoring(const BrainStructure* brainStructure, CiftiParcelScalarFile* ciftiParcelScalarFile, const int32_t mapIndex, const int32_t numberOfNodes, float* rgbv) { Brain* brain = (Brain*)(brainStructure->getBrain()); std::vector allCiftiParcelScalarFiles; brain->getConnectivityParcelScalarFiles(allCiftiParcelScalarFiles); if (mapIndex < 0) { return false; } /* * Invalidate all coloring. */ for (int32_t i = 0; i < numberOfNodes; i++) { rgbv[i*4+3] = 0.0; } /* * Update coloring */ if (ciftiParcelScalarFile->isMapColoringValid(mapIndex) == false) { ciftiParcelScalarFile->updateScalarColoringForMap(mapIndex, brain->getPaletteFile()); } std::vector dataValues(numberOfNodes); const StructureEnum::Enum structure = brainStructure->getStructure(); ciftiParcelScalarFile->getMapSurfaceNodeColoring(brainStructure->getBrain()->getPaletteFile(), mapIndex, structure, rgbv, &dataValues[0], numberOfNodes); return true; } /** * Assign cifti scalar coloring to nodes * @param brainStructure * The brain structure that contains the data files. * @param ciftiDataSeriesFile * Cifti Data Series file that is selected. * @param ciftiMapUniqueID * UniqueID of selected map. * @param numberOfNodes * Number of nodes in surface. * @param rgbv * Color components set by this method. * Red, green, blue, valid. If the valid component is * zero, it indicates that the overlay did not assign * any coloring to the node. * @return * True if coloring is valid, else false. */ bool SurfaceNodeColoring::assignCiftiDataSeriesColoring(const BrainStructure* brainStructure, CiftiBrainordinateDataSeriesFile* ciftiDataSeriesFile, const int32_t mapIndex, const int32_t numberOfNodes, float* rgbv) { Brain* brain = (Brain*)(brainStructure->getBrain()); std::vector allCiftiDataSeriesFiles; brain->getConnectivityDataSeriesFiles(allCiftiDataSeriesFiles); if (mapIndex < 0) { return false; } /* * Invalidate all coloring. */ for (int32_t i = 0; i < numberOfNodes; i++) { rgbv[i*4+3] = 0.0; } /* * Update coloring */ if (ciftiDataSeriesFile->isMapColoringValid(mapIndex) == false) { ciftiDataSeriesFile->updateScalarColoringForMap(mapIndex, brain->getPaletteFile()); } /* * Get Coloring */ std::vector dataValues(numberOfNodes); const StructureEnum::Enum structure = brainStructure->getStructure(); ciftiDataSeriesFile->getMapSurfaceNodeColoring(brainStructure->getBrain()->getPaletteFile(), mapIndex, structure, rgbv, &dataValues[0], numberOfNodes); return true; } /** * Assign cifti parcel series coloring to nodes * @param brainStructure * The brain structure that contains the data files. * @param ciftiParcelSeriesFile * Cifti Parcel Series file that is selected. * @param ciftiMapUniqueID * UniqueID of selected map. * @param numberOfNodes * Number of nodes in surface. * @param rgbv * Color components set by this method. * Red, green, blue, valid. If the valid component is * zero, it indicates that the overlay did not assign * any coloring to the node. * @return * True if coloring is valid, else false. */ bool SurfaceNodeColoring::assignCiftiParcelSeriesColoring(const BrainStructure* brainStructure, CiftiParcelSeriesFile* ciftiParcelSeriesFile, const int32_t mapIndex, const int32_t numberOfNodes, float* rgbv) { Brain* brain = (Brain*)(brainStructure->getBrain()); std::vector allCiftiParcelSeriesFiles; brain->getConnectivityParcelSeriesFiles(allCiftiParcelSeriesFiles); if (mapIndex < 0) { return false; } /* * Invalidate all coloring. */ for (int32_t i = 0; i < numberOfNodes; i++) { rgbv[i*4+3] = 0.0; } /* * Update coloring */ if (ciftiParcelSeriesFile->isMapColoringValid(mapIndex) == false) { ciftiParcelSeriesFile->updateScalarColoringForMap(mapIndex, brain->getPaletteFile()); } /* * Get Coloring */ std::vector dataValues(numberOfNodes); const StructureEnum::Enum structure = brainStructure->getStructure(); ciftiParcelSeriesFile->getMapSurfaceNodeColoring(brainStructure->getBrain()->getPaletteFile(), mapIndex, structure, rgbv, &dataValues[0], numberOfNodes); return true; } /** * Assign RGBA coloring to nodes * @param brainStructure * The brain structure that contains the data files. * @param rgbaFile * RGBA file that is selected. * @param metricMapUniqueID * UniqueID of selected map. * @param numberOfNodes * Number of nodes in surface. * @param rgbv * Color components set by this method. * Red, green, blue, valid. If the valid component is * zero, it indicates that the overlay did not assign * any coloring to the node. * @return * True if coloring is valid, else false. */ bool SurfaceNodeColoring::assignRgbaColoring(const BrainStructure* /*brainStructure*/, const RgbaFile* /*rgbaFile*/, const int32_t /*mapIndex*/, const int32_t numberOfNodes, float* rgbv) { CaretAssertMessage(0, "Add implementation."); 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; } return true; } connectome-workbench-1.2.3+git41-gc4c6c90/src/Brain/SurfaceNodeColoring.h000066400000000000000000000176351300200146000256570ustar00rootroot00000000000000#ifndef __SURFACE_NODE_COLORING__H_ #define __SURFACE_NODE_COLORING__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 "CaretObject.h" #include "CaretPointer.h" #include "DisplayGroupEnum.h" #include "LabelDrawingTypeEnum.h" namespace caret { class Brain; class BrainStructure; class BrowserTabContent; class CiftiMappableConnectivityMatrixDataFile; class CiftiBrainordinateDataSeriesFile; class CiftiBrainordinateLabelFile; class CiftiBrainordinateScalarFile; class CiftiParcelLabelFile; class CiftiParcelScalarFile; class CiftiParcelSeriesFile; class DisplayPropertiesLabels; class GiftiLabelTable; class Model; class LabelFile; class MetricFile; class OverlaySet; class Palette; class PaletteColorMapping; class RgbaFile; class Surface; class TopologyHelper; /// Performs coloring of surface nodes class SurfaceNodeColoring : public CaretObject { public: SurfaceNodeColoring(); virtual ~SurfaceNodeColoring(); float* colorSurfaceNodes(Model* model, Surface* surface, const int32_t browserTabIndex); private: SurfaceNodeColoring(const SurfaceNodeColoring&); SurfaceNodeColoring& operator=(const SurfaceNodeColoring&); public: virtual AString toString() const; private: enum MetricColorType { METRIC_COLOR_TYPE_NORMAL, METRIC_COLOR_TYPE_POS_THRESH_COLOR, METRIC_COLOR_TYPE_NEG_THRESH_COLOR, METRIC_COLOR_TYPE_DO_NOT_COLOR }; void colorSurfaceNodes(const DisplayPropertiesLabels* dpl, const int32_t browserTabIndex, const Surface* surface, OverlaySet* overlaySet, float* rgbaNodeColors); bool assignLabelColoring(const DisplayPropertiesLabels* dpl, const int32_t browserTabIndex, const BrainStructure* brainStructure, const Surface* surface, const LabelFile* labelFile, const int32_t mapIndex, const int32_t numberOfNodes, float* rgbv); bool assignCiftiDenseLabelColoring(const DisplayPropertiesLabels* displayPropertiesLabels, const int32_t browserTabIndex, const BrainStructure* brainStructure, const Surface* surface, CiftiBrainordinateLabelFile* ciftiLabelFile, const int32_t mapIndex, const int32_t numberOfNodes, float* rgbv); bool assignCiftiScalarColoring(const BrainStructure* brainStructure, CiftiBrainordinateScalarFile* ciftiScalarFile, const int32_t mapIndex, const int32_t numberOfNodes, float* rgbv); bool assignCiftiParcelLabelColoring(const DisplayPropertiesLabels* displayPropertiesLabels, const int32_t browserTabIndex, const BrainStructure* brainStructure, const Surface* surface, CiftiParcelLabelFile* ciftiParcelLabelFile, const int32_t mapIndex, const int32_t numberOfNodes, float* rgbv); bool assignCiftiParcelScalarColoring(const BrainStructure* brainStructure, CiftiParcelScalarFile* ciftiScalarFile, const int32_t mapIndex, const int32_t numberOfNodes, float* rgbv); bool assignCiftiDataSeriesColoring(const BrainStructure* brainStructure, CiftiBrainordinateDataSeriesFile* ciftiDataSeriesFile, const int32_t mapIndex, const int32_t numberOfNodes, float* rgbv); bool assignCiftiParcelSeriesColoring(const BrainStructure* brainStructure, CiftiParcelSeriesFile* ciftiParcelSeriesFile, const int32_t mapIndex, const int32_t numberOfNodes, float* rgbv); bool assignCiftiMappableConnectivityMatrixColoring(const BrainStructure* brainStructure, CiftiMappableConnectivityMatrixDataFile* ciftiConnectivityMatrixFile, const int32_t mapIndex, const int32_t numberOfNodes, float* rgbv); bool assignMetricColoring(const BrainStructure* brainStructure, MetricFile* metricFile, const int32_t mapIndex, const int32_t numberOfNodes, float* rgbv); bool assignRgbaColoring(const BrainStructure* brainStructure, const RgbaFile* rgbaFile, const int32_t mapIndex, const int32_t numberOfNodes, float* rgbv); void assignLabelTableColors(const GiftiLabelTable* labelTable, const LabelDrawingTypeEnum::Enum labelDrawingType, const CaretColorEnum::Enum outlineColor, const CaretPointer topologyHelper, const DisplayGroupEnum::Enum displayGroup, const int32_t browserTabIndex, const std::vector& labelIndices, const bool drawMedialWallFilledFlag, float* rgbv); void showBrainordinateHighlightRegionOfInterest(const Brain* brain, const Surface* surface, float* rgbaNodeColors); }; #ifdef __SURFACE_NODE_COLORING_DECLARE__ #endif // __SURFACE_NODE_COLORING_DECLARE__ } // namespace #endif //__SURFACE_NODE_COLORING__H_ connectome-workbench-1.2.3+git41-gc4c6c90/src/Brain/SurfaceSelectionModel.cxx000066400000000000000000000337531300200146000265550ustar00rootroot00000000000000 /*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 __SURFACE_SELECTION_MODEL_DECLARE__ #include "SurfaceSelectionModel.h" #undef __SURFACE_SELECTION_MODEL_DECLARE__ #include "BrainStructure.h" #include "EventBrainStructureGetAll.h" #include "EventManager.h" #include "EventSurfacesGet.h" #include "SceneAttributes.h" #include "SceneClass.h" #include "Surface.h" using namespace caret; /** * \class SurfaceSelection * \brief Maintains selection of a surface. * * Maintains selection of a surface. * * The constructors allow one to limit that available surfaces to those * from specified structures and surface types. * * If the selected surface becomes invalid, a different surface will * be selected. */ /** * Constructor for surfaces from a specific structure and of the given * surface types. * * @param structure * Limit to surfaces from this structure. * @param surfaceTypes * Types of surfaces that are available for selection. */ SurfaceSelectionModel::SurfaceSelectionModel(const StructureEnum::Enum structure, const std::vector& surfaceTypes) : CaretObject() { m_allowableStructures.push_back(structure); m_allowableSurfaceTypes = surfaceTypes; CaretAssert( ! surfaceTypes.empty()); } /** * Constructor for surfaces from any structure and of the given * surface types. * * @param surfaceTypes * Types of surfaces that are available for selection. */ SurfaceSelectionModel::SurfaceSelectionModel(const std::vector& surfaceTypes) : CaretObject() { m_allowableSurfaceTypes = surfaceTypes; CaretAssert( ! surfaceTypes.empty()); } /** * Destructor. */ SurfaceSelectionModel::~SurfaceSelectionModel() { } /** * @return The selected surface (NULL if none) */ Surface* SurfaceSelectionModel::getSurface() { updateModel(); return m_selectedSurface; } /** * @return The selected surface (NULL if none) */ const Surface* SurfaceSelectionModel::getSurface() const { updateModel(); return m_selectedSurface; } /** * Set the selected surface. * @param surface * New seleted surface. */ void SurfaceSelectionModel::setSurface(Surface* surface) { m_selectedSurface = surface; } /** * Set the selected surface to a surface of any of the given types with * first type having highest priority and last type having lowest priority. * * @param surfaceType * Highest priority type. * @param surfaceType2 * Second priority type. * @param surfaceType3 * Third priority type. * @param surfaceType4 * Fourth priority type. * @param surfaceType5 * Lowest priority type. */ void SurfaceSelectionModel::setSurfaceToType(const SurfaceTypeEnum::Enum surfaceType, const SurfaceTypeEnum::Enum surfaceType2, const SurfaceTypeEnum::Enum surfaceType3, const SurfaceTypeEnum::Enum surfaceType4, const SurfaceTypeEnum::Enum surfaceType5) { std::vector surfaces = getAvailableSurfaces(); std::vector surfaceTypes; surfaceTypes.push_back(surfaceType); if (surfaceType2 != SurfaceTypeEnum::UNKNOWN) { surfaceTypes.push_back(surfaceType2); } if (surfaceType3 != SurfaceTypeEnum::UNKNOWN) { surfaceTypes.push_back(surfaceType3); } if (surfaceType4 != SurfaceTypeEnum::UNKNOWN) { surfaceTypes.push_back(surfaceType4); } if (surfaceType5 != SurfaceTypeEnum::UNKNOWN) { surfaceTypes.push_back(surfaceType5); } for (std::vector::iterator typeIter = surfaceTypes.begin(); typeIter != surfaceTypes.end(); typeIter++) { const SurfaceTypeEnum::Enum type = *typeIter; for (std::vector::iterator surfaceIter = surfaces.begin(); surfaceIter != surfaces.end(); surfaceIter++) { Surface* s = *surfaceIter; if (s->getSurfaceType() == type) { setSurface(s); return; } } } } /** * @return A vector containing surfaces available * for selection. */ std::vector SurfaceSelectionModel::getAvailableSurfaces() const { std::vector unknownSurfaces; std::vector reconstructionSurfaces; std::vector anatomicalSurfaces; std::vector inflatedSurfaces; std::vector veryInflatedSurfaces; std::vector sphericalSurfaces; std::vector semiSphericalSurfaces; std::vector ellipsoidSurfaces; std::vector flatSurfaces; std::vector hullSurfaces; /* * Get ALL surfaces */ EventSurfacesGet getSurfacesEvent; EventManager::get()->sendEvent(getSurfacesEvent.getPointer()); std::vector allSurfaces = getSurfacesEvent.getSurfaces(); for (std::vector::iterator iter = allSurfaces.begin(); iter != allSurfaces.end(); iter++) { Surface* surface = *iter; /* * Filter by structure */ bool passesStructureTestFlag = false; if (m_allowableStructures.empty()) { passesStructureTestFlag = true; } else { const StructureEnum::Enum structure = surface->getStructure(); if (std::find(m_allowableStructures.begin(), m_allowableStructures.end(), structure) != m_allowableStructures.end()) { passesStructureTestFlag = true; } } if (passesStructureTestFlag) { const SurfaceTypeEnum::Enum surfaceType = surface->getSurfaceType(); if (std::find(m_allowableSurfaceTypes.begin(), m_allowableSurfaceTypes.end(), surfaceType) != m_allowableSurfaceTypes.end()) { switch (surfaceType) { case SurfaceTypeEnum::UNKNOWN: unknownSurfaces.push_back(surface); break; case SurfaceTypeEnum::RECONSTRUCTION: reconstructionSurfaces.push_back(surface); break; case SurfaceTypeEnum::ANATOMICAL: anatomicalSurfaces.push_back(surface); break; case SurfaceTypeEnum::INFLATED: inflatedSurfaces.push_back(surface); break; case SurfaceTypeEnum::VERY_INFLATED: veryInflatedSurfaces.push_back(surface); break; case SurfaceTypeEnum::SPHERICAL: sphericalSurfaces.push_back(surface); break; case SurfaceTypeEnum::SEMI_SPHERICAL: semiSphericalSurfaces.push_back(surface); break; case SurfaceTypeEnum::ELLIPSOID: ellipsoidSurfaces.push_back(surface); break; case SurfaceTypeEnum::FLAT: flatSurfaces.push_back(surface); break; case SurfaceTypeEnum::HULL: hullSurfaces.push_back(surface); break; } } } } std::vector surfacesOut; surfacesOut.insert(surfacesOut.end(), anatomicalSurfaces.begin(), anatomicalSurfaces.end()); surfacesOut.insert(surfacesOut.end(), reconstructionSurfaces.begin(), reconstructionSurfaces.end()); surfacesOut.insert(surfacesOut.end(), inflatedSurfaces.begin(), inflatedSurfaces.end()); surfacesOut.insert(surfacesOut.end(), veryInflatedSurfaces.begin(), veryInflatedSurfaces.end()); surfacesOut.insert(surfacesOut.end(), sphericalSurfaces.begin(), sphericalSurfaces.end()); surfacesOut.insert(surfacesOut.end(), semiSphericalSurfaces.begin(), semiSphericalSurfaces.end()); surfacesOut.insert(surfacesOut.end(), ellipsoidSurfaces.begin(), ellipsoidSurfaces.end()); surfacesOut.insert(surfacesOut.end(), hullSurfaces.begin(), hullSurfaces.end()); surfacesOut.insert(surfacesOut.end(), flatSurfaces.begin(), flatSurfaces.end()); surfacesOut.insert(surfacesOut.end(), unknownSurfaces.begin(), unknownSurfaces.end()); return surfacesOut; } /** * Update the model. * May be needed if the loaded surfaces change. */ void SurfaceSelectionModel::updateModel() const { std::vector surfaces = getAvailableSurfaces(); if (surfaces.empty()) { m_selectedSurface = NULL; return; } if (m_selectedSurface != NULL) { if (std::find(surfaces.begin(), surfaces.end(), m_selectedSurface) == surfaces.end()) { m_selectedSurface = NULL; } } if (m_selectedSurface == NULL) { EventBrainStructureGetAll brainStructureEvent; EventManager::get()->sendEvent(brainStructureEvent.getPointer()); const int32_t numBrainStructures = brainStructureEvent.getNumberOfBrainStructures(); for (int32_t i = 0; i < numBrainStructures; i++) { BrainStructure* bs = brainStructureEvent.getBrainStructureByIndex(i); /* * Use the primary anatomical surface if it is acceptable */ Surface* primaryAnatomicalSurface = NULL; if (m_allowableStructures.empty()) { primaryAnatomicalSurface = bs->getPrimaryAnatomicalSurface(); } else { const StructureEnum::Enum structure = bs->getStructure(); if (std::find(m_allowableStructures.begin(), m_allowableStructures.end(), structure) != m_allowableStructures.end()) { primaryAnatomicalSurface = bs->getPrimaryAnatomicalSurface(); break; } } if (primaryAnatomicalSurface != NULL) { if (std::find(surfaces.begin(), surfaces.end(), primaryAnatomicalSurface) != surfaces.end()) { m_selectedSurface = primaryAnatomicalSurface; break; } } } if (m_selectedSurface == NULL) { m_selectedSurface = surfaces[0]; } } } /** * Create a scene for an instance of a class. * * @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 the class' instance. * * @return Pointer to SceneClass object representing the state of * this object. Under some circumstances a NULL pointer may be * returned. Caller will take ownership of returned object. */ SceneClass* SurfaceSelectionModel::saveToScene(const SceneAttributes* /*sceneAttributes*/, const AString& instanceName) { SceneClass* sceneClass = new SceneClass(instanceName, "SurfaceSelectionModel", 1); Surface* surface = getSurface(); if (surface != NULL) { sceneClass->addString("m_selectedSurface", surface->getFileNameNoPath()); } return sceneClass; } /** * Restore the state of an instance of a class. * * @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. May be NULL for some types of scenes. */ void SurfaceSelectionModel::restoreFromScene(const SceneAttributes* /*sceneAttributes*/, const SceneClass* sceneClass) { if (sceneClass == NULL) { return; } 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); break; } } } } connectome-workbench-1.2.3+git41-gc4c6c90/src/Brain/SurfaceSelectionModel.h000066400000000000000000000062751300200146000262010ustar00rootroot00000000000000#ifndef __SURFACE_SELECTION_MODEL_H__ #define __SURFACE_SELECTION_MODEL_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 "SceneableInterface.h" #include "StructureEnum.h" #include "SurfaceTypeEnum.h" namespace caret { class Surface; class SurfaceSelectionModel : public CaretObject, public SceneableInterface { public: SurfaceSelectionModel(const StructureEnum::Enum structure, const std::vector& surfaceTypes); SurfaceSelectionModel(const std::vector& surfaceTypes); virtual ~SurfaceSelectionModel(); Surface* getSurface(); const Surface* getSurface() const; void setSurface(Surface* surface); void setSurfaceToType(const SurfaceTypeEnum::Enum surfaceType, const SurfaceTypeEnum::Enum surfaceType2 = SurfaceTypeEnum::UNKNOWN, const SurfaceTypeEnum::Enum surfaceType3 = SurfaceTypeEnum::UNKNOWN, const SurfaceTypeEnum::Enum surfaceType4 = SurfaceTypeEnum::UNKNOWN, const SurfaceTypeEnum::Enum surfaceType5 = SurfaceTypeEnum::UNKNOWN); std::vector getAvailableSurfaces() const; void updateModel() const; virtual SceneClass* saveToScene(const SceneAttributes* sceneAttributes, const AString& instanceName); virtual void restoreFromScene(const SceneAttributes* sceneAttributes, const SceneClass* sceneClass); private: SurfaceSelectionModel(const SurfaceSelectionModel&); SurfaceSelectionModel& operator=(const SurfaceSelectionModel&); mutable Surface* m_selectedSurface; /** If empty, allow any structure, otherwise restrict to these structures */ std::vector m_allowableStructures; /** If empty, allow any surface type, otherwise restrict to these types */ std::vector m_allowableSurfaceTypes; }; #ifdef __SURFACE_SELECTION_MODEL_DECLARE__ // #endif // __SURFACE_SELECTION_MODEL_DECLARE__ } // namespace #endif //__SURFACE_SELECTION_MODEL_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Brain/ViewingTransformations.cxx000066400000000000000000000244771300200146000270630ustar00rootroot00000000000000 /*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 __VIEWING_TRANSFORMATIONS_DECLARE__ #include "ViewingTransformations.h" #undef __VIEWING_TRANSFORMATIONS_DECLARE__ #include "SceneClass.h" #include "SceneClassAssistant.h" using namespace caret; /** * \class caret::ViewingTransformations * \brief Viewing transformations (pan/rotate/zoom). * \ingroup Brain */ /** * Constructor. */ ViewingTransformations::ViewingTransformations() : CaretObject() { m_sceneAssistant = new SceneClassAssistant(); m_rotationMatrix = new Matrix4x4(); m_translation[0] = 0.0; m_translation[1] = 0.0; m_translation[2] = 0.0; m_scaling = 1.0; m_rightCortexFlatMapOffset[0] = 0.0; m_rightCortexFlatMapOffset[1] = 0.0; m_rightCortexFlatMapZoomFactor = 1.0; m_sceneAssistant->addArray("m_translation", m_translation, 3, 0.0); m_sceneAssistant->add("m_scaling", &m_scaling); m_sceneAssistant->addArray("m_rightCortexFlatMapOffset", m_rightCortexFlatMapOffset, 2, 0.0); m_sceneAssistant->add("m_rightCortexFlatMapZoomFactor", &m_rightCortexFlatMapZoomFactor); } /** * Destructor. */ ViewingTransformations::~ViewingTransformations() { delete m_rotationMatrix; delete m_sceneAssistant; } /** * Copy constructor. * @param obj * Object that is copied. */ ViewingTransformations::ViewingTransformations(const ViewingTransformations& obj) : CaretObject(obj), SceneableInterface(obj) { this->copyHelperViewingTransformations(obj); } /** * Assignment operator. * @param obj * Data copied from obj to this. * @return * Reference to this object. */ ViewingTransformations& ViewingTransformations::operator=(const ViewingTransformations& obj) { if (this != &obj) { CaretObject::operator=(obj); this->copyHelperViewingTransformations(obj); } return *this; } /** * Helps with copying an object of this type. * @param obj * Object that is copied. */ void ViewingTransformations::copyHelperViewingTransformations(const ViewingTransformations& obj) { *m_rotationMatrix = *obj.m_rotationMatrix; m_translation[0] = obj.m_translation[0]; m_translation[1] = obj.m_translation[1]; m_translation[2] = obj.m_translation[2]; m_scaling = obj.m_scaling; m_rightCortexFlatMapOffset[0] = obj.m_rightCortexFlatMapOffset[0]; m_rightCortexFlatMapOffset[1] = obj.m_rightCortexFlatMapOffset[1]; m_rightCortexFlatMapZoomFactor = obj.m_rightCortexFlatMapZoomFactor; } /** * @return The viewing translation. */ const float* ViewingTransformations::getTranslation() const { return m_translation; } /** * Get the viewing translation. * * @param translationOut * Translation values output. */ void ViewingTransformations::getTranslation(float translationOut[3]) const { translationOut[0] = m_translation[0]; translationOut[1] = m_translation[1]; translationOut[2] = m_translation[2]; } /** * Set the viewing translation. * * @param translation * New translation values. */ void ViewingTransformations::setTranslation( const float translation[3]) { m_translation[0] = translation[0]; m_translation[1] = translation[1]; m_translation[2] = translation[2]; } /** * Set the viewing translation. * * @param translationX * New translation X-value. * @param translationY * New translation Y-value. * @param translationZ * New translation Z-value. */ void ViewingTransformations::setTranslation(const float translationX, const float translationY, const float translationZ) { m_translation[0] = translationX; m_translation[1] = translationY; m_translation[2] = translationZ; } /** * @return The viewing scaling. */ float ViewingTransformations::getScaling() const { return m_scaling; } /** * Set the viewing scaling. * @param scaling * New value for scaling. */ void ViewingTransformations::setScaling(const float scaling) { m_scaling = scaling; } /** * @return The rotation matrix. */ Matrix4x4 ViewingTransformations::getRotationMatrix() const { return *m_rotationMatrix; } /** * Set the rotation matrix. * * @param rotationMatrix * The new rotation matrix. */ void ViewingTransformations::setRotationMatrix(const Matrix4x4& rotationMatrix) { *m_rotationMatrix = rotationMatrix; } /** * Get the offset for the right cortex flat map. * * @param rightCortexFlatMapOffsetX * Output with X offset. * @param rightCortexFlatMapOffsetY * Output with Y offset. */ void ViewingTransformations::getRightCortexFlatMapOffset(float& rightCortexFlatMapOffsetX, float& rightCortexFlatMapOffsetY) const { rightCortexFlatMapOffsetX = m_rightCortexFlatMapOffset[0]; rightCortexFlatMapOffsetY = m_rightCortexFlatMapOffset[1]; } /** * Set the offset for the right cortex flat map. * * @param rightCortexFlatMapOffsetX * New X offset. * @param rightCortexFlatMapOffsetY * New Y offset. */ void ViewingTransformations::setRightCortexFlatMapOffset(const float rightCortexFlatMapOffsetX, const float rightCortexFlatMapOffsetY) { m_rightCortexFlatMapOffset[0] = rightCortexFlatMapOffsetX; m_rightCortexFlatMapOffset[1] = rightCortexFlatMapOffsetY; } /** * @return The right flat cortex flat map offset. */ float ViewingTransformations::getRightCortexFlatMapZoomFactor() const { return m_rightCortexFlatMapZoomFactor; } /** * Set the right flat cortex flat map offset. * * @param rightCortexFlatMapZoomFactor * The right flat cortex flat map offset. */ void ViewingTransformations::setRightCortexFlatMapZoomFactor(const float rightCortexFlatMapZoomFactor) { m_rightCortexFlatMapZoomFactor = rightCortexFlatMapZoomFactor; } /** * Reset the view to the default view for a SURFACE */ void ViewingTransformations::resetView() { setTranslation(0.0, 0.0, 0.0); m_rotationMatrix->identity(); setScaling(1.0); setRightCortexFlatMapOffset(0.0, 0.0); setRightCortexFlatMapZoomFactor(1.0); leftView(); } /** * Set to a right side view. */ void ViewingTransformations::rightView() { m_rotationMatrix->identity(); m_rotationMatrix->rotateY(-90.0); m_rotationMatrix->rotateZ(-90.0); } /** * set to a left side view. */ void ViewingTransformations::leftView() { m_rotationMatrix->identity(); m_rotationMatrix->rotateY(90.0); m_rotationMatrix->rotateZ(90.0); } /** * set to a anterior view. */ void ViewingTransformations::anteriorView() { m_rotationMatrix->identity(); m_rotationMatrix->setRotation(90.0, 0.0, -180.0); // m_rotationMatrix->rotateX(-90.0); // m_rotationMatrix->rotateY(180.0); } /** * set to a posterior view. */ void ViewingTransformations::posteriorView() { m_rotationMatrix->identity(); m_rotationMatrix->setRotation(-90.0, 0.0, 0.0); // m_rotationMatrix->rotateX(-90.0); } /** * set to a dorsal view. */ void ViewingTransformations::dorsalView() { // m_rotationMatrix->identity(); m_rotationMatrix->setRotation(0.0, 0.0, 90.0); } /** * set to a ventral view. */ void ViewingTransformations::ventralView() { // m_rotationMatrix->identity(); // m_rotationMatrix->rotateY(-180.0); m_rotationMatrix->setRotation(0.0, 180.0, 90.0); } /** * Get a description of this object's content. * @return String describing this object's content. */ AString ViewingTransformations::toString() const { return "ViewingTransformations"; } /** * 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* ViewingTransformations::saveToScene(const SceneAttributes* sceneAttributes, const AString& instanceName) { SceneClass* sceneClass = new SceneClass(instanceName, "ViewingTransformations", 1); m_sceneAssistant->saveMembers(sceneAttributes, sceneClass); /* * Save rotation matrices. */ float matrix[4][4]; m_rotationMatrix->getMatrix(matrix); sceneClass->addFloatArray("m_rotationMatrix", (float*)matrix, 16); 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 ViewingTransformations::restoreFromScene(const SceneAttributes* sceneAttributes, const SceneClass* sceneClass) { if (sceneClass == NULL) { return; } m_sceneAssistant->restoreMembers(sceneAttributes, sceneClass); /* * Restore rotation matrices. */ float matrix[4][4]; if (sceneClass->getFloatArrayValue("m_rotationMatrix", (float*)matrix, 16) == 16) { m_rotationMatrix->setMatrix(matrix); } else { m_rotationMatrix->identity(); } } connectome-workbench-1.2.3+git41-gc4c6c90/src/Brain/ViewingTransformations.h000066400000000000000000000075441300200146000265040ustar00rootroot00000000000000#ifndef __VIEWING_TRANSFORMATIONS_H__ #define __VIEWING_TRANSFORMATIONS_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 "Matrix4x4.h" #include "SceneableInterface.h" namespace caret { class SceneClassAssistant; class ViewingTransformations : public CaretObject, public SceneableInterface { public: ViewingTransformations(); virtual ~ViewingTransformations(); ViewingTransformations(const ViewingTransformations& obj); ViewingTransformations& operator=(const ViewingTransformations& obj); const float* getTranslation() const; void getTranslation(float translationOut[3]) const; void setTranslation( const float translation[3]); void setTranslation(const float translationX, const float translationY, const float translationZ); float getScaling() const; void setScaling(const float scaling); Matrix4x4 getRotationMatrix() const; void setRotationMatrix(const Matrix4x4& rotationMatrix); void getRightCortexFlatMapOffset(float& rightCortexFlatMapOffsetX, float& rightCortexFlatMapOffsetY) const; void setRightCortexFlatMapOffset(const float rightCortexFlatMapOffsetX, const float rightCortexFlatMapOffsetY); float getRightCortexFlatMapZoomFactor() const; void setRightCortexFlatMapZoomFactor(const float rightCortexFlatMapZoomFactor); virtual void resetView(); virtual void rightView(); virtual void leftView(); virtual void anteriorView(); virtual void posteriorView(); virtual void dorsalView(); virtual void ventralView(); virtual SceneClass* saveToScene(const SceneAttributes* sceneAttributes, const AString& instanceName); virtual void restoreFromScene(const SceneAttributes* sceneAttributes, const SceneClass* sceneClass); // ADD_NEW_METHODS_HERE virtual AString toString() const; protected: /** Rotation matrix. */ Matrix4x4* m_rotationMatrix; /** Translation */ float m_translation[3]; /** Scaling. */ float m_scaling; /** Offset for right cortex flat map */ float m_rightCortexFlatMapOffset[2]; float m_rightCortexFlatMapZoomFactor; private: void copyHelperViewingTransformations(const ViewingTransformations& obj); SceneClassAssistant* m_sceneAssistant; // ADD_NEW_MEMBERS_HERE }; #ifdef __VIEWING_TRANSFORMATIONS_DECLARE__ // #endif // __VIEWING_TRANSFORMATIONS_DECLARE__ } // namespace #endif //__VIEWING_TRANSFORMATIONS_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Brain/ViewingTransformationsCerebellum.cxx000066400000000000000000000055231300200146000310520ustar00rootroot00000000000000 /*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 __VIEWING_TRANSFORMATIONS_CEREBELLUM_DECLARE__ #include "ViewingTransformationsCerebellum.h" #undef __VIEWING_TRANSFORMATIONS_CEREBELLUM_DECLARE__ #include "CaretAssert.h" using namespace caret; /** * \class caret::ViewingTransformationsCerebellum * \brief Viewing transformations (pan/rotate/zoom) for cerebellum. * \ingroup Brain * * Extends ViewingTransformations with differences for cerebellum viewing. */ /** * Constructor. */ ViewingTransformationsCerebellum::ViewingTransformationsCerebellum() : ViewingTransformations() { } /** * Destructor. */ ViewingTransformationsCerebellum::~ViewingTransformationsCerebellum() { } /** * Copy constructor. * @param obj * Object that is copied. */ ViewingTransformationsCerebellum::ViewingTransformationsCerebellum(const ViewingTransformationsCerebellum& obj) : ViewingTransformations(obj) { this->copyHelperViewingTransformationsCerebellum(obj); } /** * Assignment operator. * @param obj * Data copied from obj to this. * @return * Reference to this object. */ ViewingTransformationsCerebellum& ViewingTransformationsCerebellum::operator=(const ViewingTransformationsCerebellum& obj) { if (this != &obj) { ViewingTransformations::operator=(obj); this->copyHelperViewingTransformationsCerebellum(obj); } return *this; } /** * Helps with copying an object of this type. * @param obj * Object that is copied. */ void ViewingTransformationsCerebellum::copyHelperViewingTransformationsCerebellum(const ViewingTransformationsCerebellum& /*obj*/) { /* nothing to copy */ } /** * Reset the view to the default view. */ void ViewingTransformationsCerebellum::resetView() { ViewingTransformations::resetView(); dorsalView(); } /** * set to a dorsal view. */ void ViewingTransformationsCerebellum::dorsalView() { m_rotationMatrix->setRotation(0.0, 0.0, 0.0); } /** * set to a ventral view. */ void ViewingTransformationsCerebellum::ventralView() { m_rotationMatrix->setRotation(0.0, 180.0, 180.0); } connectome-workbench-1.2.3+git41-gc4c6c90/src/Brain/ViewingTransformationsCerebellum.h000066400000000000000000000036371300200146000305030ustar00rootroot00000000000000#ifndef __VIEWING_TRANSFORMATIONS_CEREBELLUM_H__ #define __VIEWING_TRANSFORMATIONS_CEREBELLUM_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 "ViewingTransformations.h" namespace caret { class ViewingTransformationsCerebellum : public ViewingTransformations { public: ViewingTransformationsCerebellum(); virtual ~ViewingTransformationsCerebellum(); ViewingTransformationsCerebellum(const ViewingTransformationsCerebellum& obj); ViewingTransformationsCerebellum& operator=(const ViewingTransformationsCerebellum& obj); virtual void resetView(); virtual void ventralView(); virtual void dorsalView(); // ADD_NEW_METHODS_HERE private: void copyHelperViewingTransformationsCerebellum(const ViewingTransformationsCerebellum& obj); // ADD_NEW_MEMBERS_HERE }; #ifdef __VIEWING_TRANSFORMATIONS_CEREBELLUM_DECLARE__ // #endif // __VIEWING_TRANSFORMATIONS_CEREBELLUM_DECLARE__ } // namespace #endif //__VIEWING_TRANSFORMATIONS_CEREBELLUM_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Brain/ViewingTransformationsVolume.cxx000066400000000000000000000047441300200146000302460ustar00rootroot00000000000000 /*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 __VIEWING_TRANSFORMATIONS_VOLUME_DECLARE__ #include "ViewingTransformationsVolume.h" #undef __VIEWING_TRANSFORMATIONS_VOLUME_DECLARE__ #include "CaretAssert.h" using namespace caret; /** * \class caret::ViewingTransformationsVolume * \brief Viewing transformations (pan/rotate/zoom) for volume. * \ingroup Brain * * Extends ViewingTransformations with differences for volume viewing. */ /** * Constructor. */ ViewingTransformationsVolume::ViewingTransformationsVolume() : ViewingTransformations() { } /** * Destructor. */ ViewingTransformationsVolume::~ViewingTransformationsVolume() { } /** * Copy constructor. * @param obj * Object that is copied. */ ViewingTransformationsVolume::ViewingTransformationsVolume(const ViewingTransformationsVolume& obj) : ViewingTransformations(obj) { this->copyHelperViewingTransformationsVolume(obj); } /** * Assignment operator. * @param obj * Data copied from obj to this. * @return * Reference to this object. */ ViewingTransformationsVolume& ViewingTransformationsVolume::operator=(const ViewingTransformationsVolume& obj) { if (this != &obj) { ViewingTransformations::operator=(obj); this->copyHelperViewingTransformationsVolume(obj); } return *this; } /** * Helps with copying an object of this type. * @param obj * Object that is copied. */ void ViewingTransformationsVolume::copyHelperViewingTransformationsVolume(const ViewingTransformationsVolume& /*obj*/) { } /** * Reset the view to the default view for a VOLUME. */ void ViewingTransformationsVolume::resetView() { ViewingTransformations::resetView(); m_rotationMatrix->identity(); } connectome-workbench-1.2.3+git41-gc4c6c90/src/Brain/ViewingTransformationsVolume.h000066400000000000000000000034271300200146000276700ustar00rootroot00000000000000#ifndef __VIEWING_TRANSFORMATIONS_VOLUME_H__ #define __VIEWING_TRANSFORMATIONS_VOLUME_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 "ViewingTransformations.h" namespace caret { class ViewingTransformationsVolume : public ViewingTransformations { public: ViewingTransformationsVolume(); virtual ~ViewingTransformationsVolume(); ViewingTransformationsVolume(const ViewingTransformationsVolume& obj); ViewingTransformationsVolume& operator=(const ViewingTransformationsVolume& obj); virtual void resetView(); // ADD_NEW_METHODS_HERE private: void copyHelperViewingTransformationsVolume(const ViewingTransformationsVolume& obj); // ADD_NEW_MEMBERS_HERE }; #ifdef __VIEWING_TRANSFORMATIONS_VOLUME_DECLARE__ // #endif // __VIEWING_TRANSFORMATIONS_VOLUME_DECLARE__ } // namespace #endif //__VIEWING_TRANSFORMATIONS_VOLUME_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Brain/VolumeSliceDrawingTypeEnum.cxx000066400000000000000000000256501300200146000275650ustar00rootroot00000000000000 /*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 __VOLUME_SLICE_DRAWING_TYPE_ENUM_DECLARE__ #include "VolumeSliceDrawingTypeEnum.h" #undef __VOLUME_SLICE_DRAWING_TYPE_ENUM_DECLARE__ #include "CaretAssert.h" using namespace caret; /** * \class caret::VolumeSliceDrawingTypeEnum * \brief Enumerated type for type of volume slice drawing * * Enumerated type for drawing a single slice or a montage of slices * * 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_volumeSliceDrawingTypeEnumComboBox; * * Declare a slot that is called when user changes selection * private slots: * void volumeSliceDrawingTypeEnumComboBoxItemActivated(); * * Implementation File (.cxx) * Include the header files * #include "EnumComboBoxTemplate.h" * #include "VolumeSliceDrawingTypeEnum.h" * * Instatiate: * m_volumeSliceDrawingTypeEnumComboBox = new EnumComboBoxTemplate(this); * m_volumeSliceDrawingTypeEnumComboBox->setup(); * * Get notified when the user changes the selection: * QObject::connect(m_volumeSliceDrawingTypeEnumComboBox, SIGNAL(itemActivated()), * this, SLOT(volumeSliceDrawingTypeEnumComboBoxItemActivated())); * * Update the selection: * m_volumeSliceDrawingTypeEnumComboBox->setSelectedItem(NEW_VALUE); * * Read the selection: * const VolumeSliceDrawingTypeEnum::Enum VARIABLE = m_volumeSliceDrawingTypeEnumComboBox->getSelectedItem(); * */ /** * Constructor. * * @param enumValue * An enumerated value. * @param name * Name of enumerated value. * * @param guiName * User-friendly name for use in user-interface. */ VolumeSliceDrawingTypeEnum::VolumeSliceDrawingTypeEnum(const Enum enumValue, const AString& name, const AString& guiName) { this->enumValue = enumValue; this->integerCode = integerCodeCounter++; this->name = name; this->guiName = guiName; } /** * Destructor. */ VolumeSliceDrawingTypeEnum::~VolumeSliceDrawingTypeEnum() { } /** * Initialize the enumerated metadata. */ void VolumeSliceDrawingTypeEnum::initialize() { if (initializedFlag) { return; } initializedFlag = true; enumData.push_back(VolumeSliceDrawingTypeEnum(VOLUME_SLICE_DRAW_MONTAGE, "VOLUME_SLICE_DRAW_MONTAGE", "Draw a montage of slices")); enumData.push_back(VolumeSliceDrawingTypeEnum(VOLUME_SLICE_DRAW_SINGLE, "VOLUME_SLICE_DRAW_SINGLE", "Draw a single slice")); } /** * 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 VolumeSliceDrawingTypeEnum* VolumeSliceDrawingTypeEnum::findData(const Enum enumValue) { if (initializedFlag == false) initialize(); size_t num = enumData.size(); for (size_t i = 0; i < num; i++) { const VolumeSliceDrawingTypeEnum* 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 VolumeSliceDrawingTypeEnum::toName(Enum enumValue) { if (initializedFlag == false) initialize(); const VolumeSliceDrawingTypeEnum* 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. */ VolumeSliceDrawingTypeEnum::Enum VolumeSliceDrawingTypeEnum::fromName(const AString& name, bool* isValidOut) { if (initializedFlag == false) initialize(); bool validFlag = false; Enum enumValue = VolumeSliceDrawingTypeEnum::enumData[0].enumValue; for (std::vector::iterator iter = enumData.begin(); iter != enumData.end(); iter++) { const VolumeSliceDrawingTypeEnum& 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 VolumeSliceDrawingTypeEnum")); } return enumValue; } /** * Get a GUI string representation of the enumerated type. * @param enumValue * Enumerated value. * @return * String representing enumerated value. */ AString VolumeSliceDrawingTypeEnum::toGuiName(Enum enumValue) { if (initializedFlag == false) initialize(); const VolumeSliceDrawingTypeEnum* 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. */ VolumeSliceDrawingTypeEnum::Enum VolumeSliceDrawingTypeEnum::fromGuiName(const AString& guiName, bool* isValidOut) { if (initializedFlag == false) initialize(); bool validFlag = false; Enum enumValue = VolumeSliceDrawingTypeEnum::enumData[0].enumValue; for (std::vector::iterator iter = enumData.begin(); iter != enumData.end(); iter++) { const VolumeSliceDrawingTypeEnum& 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 VolumeSliceDrawingTypeEnum")); } return enumValue; } /** * Get the integer code for a data type. * * @return * Integer code for data type. */ int32_t VolumeSliceDrawingTypeEnum::toIntegerCode(Enum enumValue) { if (initializedFlag == false) initialize(); const VolumeSliceDrawingTypeEnum* 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. */ VolumeSliceDrawingTypeEnum::Enum VolumeSliceDrawingTypeEnum::fromIntegerCode(const int32_t integerCode, bool* isValidOut) { if (initializedFlag == false) initialize(); bool validFlag = false; Enum enumValue = VolumeSliceDrawingTypeEnum::enumData[0].enumValue; for (std::vector::iterator iter = enumData.begin(); iter != enumData.end(); iter++) { const VolumeSliceDrawingTypeEnum& 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 VolumeSliceDrawingTypeEnum")); } 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 VolumeSliceDrawingTypeEnum::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 VolumeSliceDrawingTypeEnum::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(VolumeSliceDrawingTypeEnum::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 VolumeSliceDrawingTypeEnum::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(VolumeSliceDrawingTypeEnum::toGuiName(iter->enumValue)); } if (isSorted) { std::sort(allGuiNames.begin(), allGuiNames.end()); } } connectome-workbench-1.2.3+git41-gc4c6c90/src/Brain/VolumeSliceDrawingTypeEnum.h000066400000000000000000000063211300200146000272040ustar00rootroot00000000000000#ifndef __VOLUME_SLICE_DRAWING_TYPE_ENUM_H__ #define __VOLUME_SLICE_DRAWING_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 VolumeSliceDrawingTypeEnum { public: /** * Enumerated values. */ enum Enum { /** Draw a montage of slices */ VOLUME_SLICE_DRAW_MONTAGE, /** Draw a single slice */ VOLUME_SLICE_DRAW_SINGLE }; ~VolumeSliceDrawingTypeEnum(); 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: VolumeSliceDrawingTypeEnum(const Enum enumValue, const AString& name, const AString& guiName); static const VolumeSliceDrawingTypeEnum* 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_DRAWING_TYPE_ENUM_DECLARE__ std::vector VolumeSliceDrawingTypeEnum::enumData; bool VolumeSliceDrawingTypeEnum::initializedFlag = false; int32_t VolumeSliceDrawingTypeEnum::integerCodeCounter = 0; #endif // __VOLUME_SLICE_DRAWING_TYPE_ENUM_DECLARE__ } // namespace #endif //__VOLUME_SLICE_DRAWING_TYPE_ENUM_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Brain/VolumeSliceSettings.cxx000066400000000000000000000545641300200146000263110ustar00rootroot00000000000000 /*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 __VOLUME_SLICE_SETTINGS_DECLARE__ #include "VolumeSliceSettings.h" #undef __VOLUME_SLICE_SETTINGS_DECLARE__ #include "CaretLogger.h" #include "PlainTextStringBuilder.h" #include "SceneClass.h" #include "SceneClassAssistant.h" #include "SceneEnumeratedType.h" #include "VolumeFile.h" using namespace caret; /** * \class caret::VolumeSliceSettings * \brief Settings that control the display of volume slices. * \ingroup Brain */ /** * Constructor. */ VolumeSliceSettings::VolumeSliceSettings() : CaretObject() { m_sliceViewPlane = VolumeSliceViewPlaneEnum::AXIAL; m_sliceDrawingType = VolumeSliceDrawingTypeEnum::VOLUME_SLICE_DRAW_SINGLE; m_sliceProjectionType = VolumeSliceProjectionTypeEnum::VOLUME_SLICE_PROJECTION_ORTHOGONAL; m_montageNumberOfColumns = 6; // was 3; m_montageNumberOfRows = 4; m_montageSliceSpacing = 5; m_sliceCoordinateAxial = 0.0; m_sliceCoordinateCoronal = 0.0; m_sliceCoordinateParasagittal = 0.0; m_sliceEnabledAxial = true; m_sliceEnabledCoronal = true; m_sliceEnabledParasagittal = true; m_initializedFlag = false; //m_lastVolumeFile = NULL; m_sceneAssistant = new SceneClassAssistant(); m_sceneAssistant->add("m_sliceViewPlane", &m_sliceViewPlane); m_sceneAssistant->add("m_sliceDrawingType", &m_sliceDrawingType); m_sceneAssistant->add("m_sliceProjectionType", &m_sliceProjectionType); m_sceneAssistant->add("m_montageNumberOfColumns", &m_montageNumberOfColumns); m_sceneAssistant->add("m_montageNumberOfRows", &m_montageNumberOfRows); m_sceneAssistant->add("m_montageSliceSpacing", &m_montageSliceSpacing); m_sceneAssistant->add("m_sliceCoordinateAxial", &m_sliceCoordinateAxial); m_sceneAssistant->add("m_sliceCoordinateCoronal", &m_sliceCoordinateCoronal); m_sceneAssistant->add("m_sliceCoordinateParasagittal", &m_sliceCoordinateParasagittal); m_sceneAssistant->add("m_sliceEnabledAxial", &m_sliceEnabledAxial); m_sceneAssistant->add("m_sliceEnabledCoronal", &m_sliceEnabledCoronal); m_sceneAssistant->add("m_sliceEnabledParasagittal", &m_sliceEnabledParasagittal); } /** * Destructor. */ VolumeSliceSettings::~VolumeSliceSettings() { delete m_sceneAssistant; } /** * Copy constructor. * @param obj * Object that is copied. */ VolumeSliceSettings::VolumeSliceSettings(const VolumeSliceSettings& obj) : CaretObject(obj), SceneableInterface(obj) { this->copyHelperVolumeSliceSettings(obj); } /** * Assignment operator. * @param obj * Data copied from obj to this. * @return * Reference to this object. */ VolumeSliceSettings& VolumeSliceSettings::operator=(const VolumeSliceSettings& obj) { if (this != &obj) { CaretObject::operator=(obj); this->copyHelperVolumeSliceSettings(obj); } return *this; } /** * Helps with copying an object of this type. * @param obj * Object that is copied. */ void VolumeSliceSettings::copyHelperVolumeSliceSettings(const VolumeSliceSettings& obj) { m_sliceViewPlane = obj.m_sliceViewPlane; m_sliceDrawingType = obj.m_sliceDrawingType; m_sliceProjectionType = obj.m_sliceProjectionType; m_montageNumberOfColumns = obj.m_montageNumberOfColumns; m_montageNumberOfRows = obj.m_montageNumberOfRows; m_montageSliceSpacing = obj.m_montageSliceSpacing; m_sliceCoordinateParasagittal = obj.m_sliceCoordinateParasagittal; m_sliceCoordinateCoronal = obj.m_sliceCoordinateCoronal; m_sliceCoordinateAxial = obj.m_sliceCoordinateAxial; m_sliceEnabledParasagittal = obj.m_sliceEnabledParasagittal; m_sliceEnabledCoronal = obj.m_sliceEnabledCoronal; m_sliceEnabledAxial = obj.m_sliceEnabledAxial; m_initializedFlag = true; } /** * Get a description of this object's content. * @return String describing this object's content. */ AString VolumeSliceSettings::toString() const { return "VolumeSliceSettings"; } /** * Get a text description of the instance's content. * * @param modelType * Type of model. * @param descriptionOut * Description of the instance's content. */ void VolumeSliceSettings::getDescriptionOfContent(const ModelTypeEnum::Enum modelType, PlainTextStringBuilder& descriptionOut) const { bool volumeFlag = false; bool wholeBrainFlag = false; switch (modelType) { case ModelTypeEnum::MODEL_TYPE_CHART: 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: volumeFlag = true; break; case ModelTypeEnum::MODEL_TYPE_WHOLE_BRAIN: wholeBrainFlag = true; break; } bool showParasagittalCoordinate = false; bool showCoronalCoordinate = false; bool showAxialCoordinate = false; descriptionOut.addLine("Volume Slice Settings: "); descriptionOut.pushIndentation(); if (volumeFlag) { descriptionOut.addLine(" View Plane: " + VolumeSliceViewPlaneEnum::toGuiName(m_sliceViewPlane)); showParasagittalCoordinate = true; showCoronalCoordinate = true; showAxialCoordinate = true; } if (wholeBrainFlag) { if (m_sliceEnabledParasagittal) { showParasagittalCoordinate = true; } if (m_sliceEnabledCoronal) { showCoronalCoordinate = true; } if (m_sliceEnabledAxial) { showAxialCoordinate = true; } } if (showParasagittalCoordinate) { descriptionOut.addLine(" Parasagittal (X-axis) Coordinate: " + AString::number(m_sliceCoordinateParasagittal, 'f', 3)); } if (showCoronalCoordinate) { descriptionOut.addLine(" Coronal (Y-axis) Coordinate: " + AString::number(m_sliceCoordinateCoronal, 'f', 3)); } if (showAxialCoordinate) { descriptionOut.addLine(" Axial (Z-axis) Coordinate: " + AString::number(m_sliceCoordinateAxial, 'f', 3)); } descriptionOut.popIndentation(); } /** * @return The slice view plane. * */ VolumeSliceViewPlaneEnum::Enum VolumeSliceSettings::getSliceViewPlane() const { return m_sliceViewPlane; } /** * Set the slice view plane. * @param windowTabNumber * New value for slice plane. */ void VolumeSliceSettings::setSliceViewPlane(const VolumeSliceViewPlaneEnum::Enum slicePlane) { m_sliceViewPlane = slicePlane; } /** * @return Type of slice drawing (single/montage) */ VolumeSliceDrawingTypeEnum::Enum VolumeSliceSettings::getSliceDrawingType() const { return m_sliceDrawingType; } /** * Set type of slice drawing (single/montage) * * @param sliceDrawingType * New value for slice drawing type. */ void VolumeSliceSettings::setSliceDrawingType(const VolumeSliceDrawingTypeEnum::Enum sliceDrawingType) { m_sliceDrawingType = sliceDrawingType; } /** * @return Type of slice projection (oblique/orthogonal) */ VolumeSliceProjectionTypeEnum::Enum VolumeSliceSettings::getSliceProjectionType() const { return m_sliceProjectionType; } /** * Set type of slice projection (oblique/orthogonal) * * @param sliceProjectionType * New value for slice projection type. */ void VolumeSliceSettings::setSliceProjectionType(const VolumeSliceProjectionTypeEnum::Enum sliceProjectionType) { m_sliceProjectionType = sliceProjectionType; } /** * @return the montage number of columns for the given window tab. */ int32_t VolumeSliceSettings::getMontageNumberOfColumns() const { return m_montageNumberOfColumns; } /** * Set the montage number of columns in the given window tab. * @param montageNumberOfColumns * New value for montage number of columns */ void VolumeSliceSettings::setMontageNumberOfColumns(const int32_t montageNumberOfColumns) { m_montageNumberOfColumns = montageNumberOfColumns; } /** * @return the montage number of rows for the given window tab. */ int32_t VolumeSliceSettings::getMontageNumberOfRows() const { return m_montageNumberOfRows; } /** * Set the montage number of rows. * @param montageNumberOfRows * New value for montage number of rows */ void VolumeSliceSettings::setMontageNumberOfRows(const int32_t montageNumberOfRows) { m_montageNumberOfRows = montageNumberOfRows; } /** * @return the montage slice spacing. */ int32_t VolumeSliceSettings::getMontageSliceSpacing() const { return m_montageSliceSpacing; } /** * Set the montage slice spacing. * @param montageSliceSpacing * New value for montage slice spacing */ void VolumeSliceSettings::setMontageSliceSpacing(const int32_t montageSliceSpacing) { m_montageSliceSpacing = montageSliceSpacing; } /** * Set the selected slices to the origin. */ void VolumeSliceSettings::setSlicesToOrigin() { selectSlicesAtOrigin(); } /** * Reset the slices. */ void VolumeSliceSettings::reset() { m_sliceCoordinateAxial = 0.0; m_sliceCoordinateCoronal = 0.0; m_sliceCoordinateParasagittal = 0.0; m_sliceEnabledAxial = true; m_sliceEnabledCoronal = true; m_sliceEnabledParasagittal = true; m_initializedFlag = false; } /** * Update the slices coordinates so that they are valid for * the given VolumeFile. * @param volumeFile * File for which slice coordinates are made valid. */ void VolumeSliceSettings::updateForVolumeFile(const VolumeMappableInterface* volumeFile) { if (volumeFile == NULL) { reset(); return; } if (m_initializedFlag == false) { m_initializedFlag = true; selectSlicesAtOrigin(); } /* * These calls will make the slices valid */ getSliceIndexParasagittal(volumeFile); getSliceIndexCoronal(volumeFile); getSliceIndexAxial(volumeFile); } /** * Set the slice indices so that they are at the origin. */ void VolumeSliceSettings::selectSlicesAtOrigin() { m_sliceCoordinateAxial = 0.0; m_sliceCoordinateCoronal = 0.0; m_sliceCoordinateParasagittal = 0.0; } /** * Set the selected slices to the given coordinate. * @param xyz * Coordinate for selected slices. */ void VolumeSliceSettings::selectSlicesAtCoordinate(const float xyz[3]) { m_sliceCoordinateParasagittal = xyz[0]; m_sliceCoordinateCoronal = xyz[1]; m_sliceCoordinateAxial = xyz[2]; } /** * Return the axial slice index. * @return * Axial slice index or negative if invalid */ int64_t VolumeSliceSettings::getSliceIndexAxial(const VolumeMappableInterface* volumeFile) const { CaretAssert(volumeFile); std::vector dimensions; volumeFile->getDimensions(dimensions); int64_t axialSliceOut = -1; if (dimensions[2] >= 0) { int64_t paragittalSlice, coronalSlice; volumeFile->enclosingVoxel(m_sliceCoordinateParasagittal, m_sliceCoordinateCoronal, m_sliceCoordinateAxial, paragittalSlice, coronalSlice, axialSliceOut); if (axialSliceOut < 0) { axialSliceOut = 0; float xyz[3]; volumeFile->indexToSpace(0, 0, axialSliceOut, xyz); m_sliceCoordinateAxial = xyz[2]; } else if (axialSliceOut >= dimensions[2]) { axialSliceOut = dimensions[2] - 1; float xyz[3]; volumeFile->indexToSpace(0, 0, axialSliceOut, xyz); m_sliceCoordinateAxial = xyz[2]; } } return axialSliceOut; } /** * Set the axial slice index. * @param * New value for axial slice index. */ void VolumeSliceSettings::setSliceIndexAxial(const VolumeMappableInterface* volumeFile, const int64_t sliceIndexAxial) { CaretAssert(volumeFile); float xyz[3]; volumeFile->indexToSpace(0, 0, sliceIndexAxial, xyz); m_sliceCoordinateAxial = xyz[2]; } /** * Return the coronal slice index. * @return * Coronal slice index. */ int64_t VolumeSliceSettings::getSliceIndexCoronal(const VolumeMappableInterface* volumeFile) const { CaretAssert(volumeFile); std::vector dimensions; volumeFile->getDimensions(dimensions); int64_t coronalSliceOut = -1; if (dimensions[1] >= 0) { int64_t paragittalSlice, axialSlice; volumeFile->enclosingVoxel(m_sliceCoordinateParasagittal, m_sliceCoordinateCoronal, m_sliceCoordinateAxial, paragittalSlice, coronalSliceOut, axialSlice); if (coronalSliceOut < 0) { coronalSliceOut = 0; float xyz[3]; volumeFile->indexToSpace(0, coronalSliceOut, 0, xyz); m_sliceCoordinateCoronal = xyz[1]; } else if (coronalSliceOut >= dimensions[1]) { coronalSliceOut = dimensions[1] - 1; float xyz[3]; volumeFile->indexToSpace(0, coronalSliceOut, 0, xyz); m_sliceCoordinateCoronal = xyz[1]; } } return coronalSliceOut; } /** * Set the coronal slice index. * @param sliceIndexCoronal * New value for coronal slice index. */ void VolumeSliceSettings::setSliceIndexCoronal(const VolumeMappableInterface* volumeFile, const int64_t sliceIndexCoronal) { CaretAssert(volumeFile); float xyz[3]; volumeFile->indexToSpace(0, sliceIndexCoronal, 0, xyz); m_sliceCoordinateCoronal = xyz[1]; } /** * Return the parasagittal slice index. * @return * Parasagittal slice index. */ int64_t VolumeSliceSettings::getSliceIndexParasagittal(const VolumeMappableInterface* volumeFile) const { std::vector dimensions; volumeFile->getDimensions(dimensions); int64_t parasagittalSliceOut = -1; if (dimensions[0] >= 0) { int64_t coronalSlice, axialSlice; volumeFile->enclosingVoxel(m_sliceCoordinateParasagittal, m_sliceCoordinateCoronal, m_sliceCoordinateAxial, parasagittalSliceOut, coronalSlice, axialSlice); if (parasagittalSliceOut < 0) { parasagittalSliceOut = 0; float xyz[3]; volumeFile->indexToSpace(parasagittalSliceOut, 0, 0, xyz); m_sliceCoordinateParasagittal = xyz[0]; } else if (parasagittalSliceOut >= dimensions[0]) { parasagittalSliceOut = dimensions[0] - 1; float xyz[3]; volumeFile->indexToSpace(parasagittalSliceOut, 0, 0, xyz); m_sliceCoordinateParasagittal = xyz[0]; } } return parasagittalSliceOut; } /** * Set the parasagittal slice index. * @param sliceIndexParasagittal * New value for parasagittal slice index. */ void VolumeSliceSettings::setSliceIndexParasagittal(const VolumeMappableInterface* volumeFile, const int64_t sliceIndexParasagittal) { CaretAssert(volumeFile); float xyz[3]; volumeFile->indexToSpace(sliceIndexParasagittal, 0, 0, xyz); m_sliceCoordinateParasagittal = xyz[0]; } /** * @return Coordinate of axial slice. */ float VolumeSliceSettings::getSliceCoordinateAxial() const { return m_sliceCoordinateAxial; } /** * Set the coordinate for the axial slice. * @param z * Z-coordinate for axial slice. */ void VolumeSliceSettings::setSliceCoordinateAxial(const float z) { m_sliceCoordinateAxial = z; } /** * @return Coordinate of coronal slice. */ float VolumeSliceSettings::getSliceCoordinateCoronal() const { return m_sliceCoordinateCoronal; } /** * Set the coordinate for the coronal slice. * @param y * Y-coordinate for coronal slice. */ void VolumeSliceSettings::setSliceCoordinateCoronal(const float y) { m_sliceCoordinateCoronal = y; } /** * @return Coordinate of parasagittal slice. */ float VolumeSliceSettings::getSliceCoordinateParasagittal() const { return m_sliceCoordinateParasagittal; } /** * Set the coordinate for the parasagittal slice. * @param x * X-coordinate for parasagittal slice. */ void VolumeSliceSettings::setSliceCoordinateParasagittal(const float x) { m_sliceCoordinateParasagittal = x; } /** * Is the parasagittal slice enabled? * @return * Enabled status of parasagittal slice. */ bool VolumeSliceSettings::isSliceParasagittalEnabled() const { return m_sliceEnabledParasagittal; } /** * Set the enabled status of the parasagittal slice. * @param sliceEnabledParasagittal * New enabled status. */ void VolumeSliceSettings::setSliceParasagittalEnabled(const bool sliceEnabledParasagittal) { m_sliceEnabledParasagittal = sliceEnabledParasagittal; } /** * Is the coronal slice enabled? * @return * Enabled status of coronal slice. */ bool VolumeSliceSettings::isSliceCoronalEnabled() const { return m_sliceEnabledCoronal; } /** * Set the enabled status of the coronal slice. * @param sliceEnabledCoronal * New enabled status. */ void VolumeSliceSettings::setSliceCoronalEnabled(const bool sliceEnabledCoronal) { m_sliceEnabledCoronal = sliceEnabledCoronal; } /** * Is the axial slice enabled? * @return * Enabled status of axial slice. */ bool VolumeSliceSettings::isSliceAxialEnabled() const { return m_sliceEnabledAxial; } /** * Set the enabled status of the axial slice. * @param sliceEnabledAxial * New enabled status. */ void VolumeSliceSettings::setSliceAxialEnabled(const bool sliceEnabledAxial) { m_sliceEnabledAxial = sliceEnabledAxial; } /** * 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* VolumeSliceSettings::saveToScene(const SceneAttributes* sceneAttributes, const AString& instanceName) { SceneClass* sceneClass = new SceneClass(instanceName, "VolumeSliceSettings", 2); m_sceneAssistant->saveMembers(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 VolumeSliceSettings::restoreFromScene(const SceneAttributes* sceneAttributes, const SceneClass* sceneClass) { if (sceneClass == NULL) { return; } /* * Added in scene version 2 */ m_sliceDrawingType = VolumeSliceDrawingTypeEnum::VOLUME_SLICE_DRAW_SINGLE; m_sliceProjectionType = VolumeSliceProjectionTypeEnum::VOLUME_SLICE_PROJECTION_ORTHOGONAL; m_sceneAssistant->restoreMembers(sceneAttributes, sceneClass); if (sceneClass->getVersionNumber() < 2) { /* * Set slice drawing type and projection type using old slice view mode. */ const AString oldViewModeValue = sceneClass->getEnumeratedTypeValueAsString("m_sliceViewMode"); if (! oldViewModeValue.isEmpty()) { if (oldViewModeValue == "MONTAGE") { m_sliceDrawingType = VolumeSliceDrawingTypeEnum::VOLUME_SLICE_DRAW_MONTAGE; m_sliceProjectionType = VolumeSliceProjectionTypeEnum::VOLUME_SLICE_PROJECTION_ORTHOGONAL; } else if (oldViewModeValue == "OBLIQUE") { m_sliceDrawingType = VolumeSliceDrawingTypeEnum::VOLUME_SLICE_DRAW_SINGLE; m_sliceProjectionType = VolumeSliceProjectionTypeEnum::VOLUME_SLICE_PROJECTION_OBLIQUE; } else if (oldViewModeValue == "ORTHOGONAL") { m_sliceDrawingType = VolumeSliceDrawingTypeEnum::VOLUME_SLICE_DRAW_SINGLE; m_sliceProjectionType = VolumeSliceProjectionTypeEnum::VOLUME_SLICE_PROJECTION_ORTHOGONAL; } else { CaretLogWarning("Unrecognized value for old m_sliceViewMode: " + oldViewModeValue); } } } /* * Restoring scene initialize all members. * If this is not done, the slices will be reset * in updateForVolumeFile(). */ m_initializedFlag = true; } connectome-workbench-1.2.3+git41-gc4c6c90/src/Brain/VolumeSliceSettings.h000066400000000000000000000147171300200146000257320ustar00rootroot00000000000000#ifndef __VOLUME_SLICE_SETTINGS_H__ #define __VOLUME_SLICE_SETTINGS_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 "ModelTypeEnum.h" #include "SceneableInterface.h" #include "VolumeSliceDrawingTypeEnum.h" #include "VolumeSliceProjectionTypeEnum.h" #include "VolumeSliceViewPlaneEnum.h" namespace caret { class PlainTextStringBuilder; class SceneClassAssistant; class VolumeMappableInterface; class VolumeSliceCoordinateSelection; class VolumeSliceSettings : public CaretObject, public SceneableInterface { public: VolumeSliceSettings(); virtual ~VolumeSliceSettings(); VolumeSliceSettings(const VolumeSliceSettings& obj); VolumeSliceSettings& operator=(const VolumeSliceSettings& obj); VolumeSliceViewPlaneEnum::Enum getSliceViewPlane() const; void setSliceViewPlane(VolumeSliceViewPlaneEnum::Enum sliceAxisMode); VolumeSliceDrawingTypeEnum::Enum getSliceDrawingType() const; void setSliceDrawingType(const VolumeSliceDrawingTypeEnum::Enum sliceDrawingType); VolumeSliceProjectionTypeEnum::Enum getSliceProjectionType() const; void setSliceProjectionType(const VolumeSliceProjectionTypeEnum::Enum sliceProjectionType); int32_t getMontageNumberOfColumns() const; void setMontageNumberOfColumns(const int32_t montageNumberOfColumns); int32_t getMontageNumberOfRows() const; void setMontageNumberOfRows(const int32_t montageNumberOfRows); int32_t getMontageSliceSpacing() const; void setMontageSliceSpacing(const int32_t montageSliceSpacing); VolumeSliceCoordinateSelection* getSelectedVolumeSlices(VolumeMappableInterface* underlayVolumeFile); const VolumeSliceCoordinateSelection* getSelectedVolumeSlices(VolumeMappableInterface* underlayVolumeFile) const; void setSlicesToOrigin(); float getSliceCoordinateAxial() const; void setSliceCoordinateAxial(const float x); float getSliceCoordinateCoronal() const; void setSliceCoordinateCoronal(const float y); float getSliceCoordinateParasagittal() const; void setSliceCoordinateParasagittal(const float z); int64_t getSliceIndexAxial(const VolumeMappableInterface* volumeFile) const; void setSliceIndexAxial(const VolumeMappableInterface* volumeFile, const int64_t sliceIndexAxial); int64_t getSliceIndexCoronal(const VolumeMappableInterface* volumeFile) const; void setSliceIndexCoronal(const VolumeMappableInterface* volumeFile, const int64_t sliceIndexCoronal); int64_t getSliceIndexParasagittal(const VolumeMappableInterface* volumeFile) const; void setSliceIndexParasagittal(const VolumeMappableInterface* volumeFile, const int64_t sliceIndexParasagittal); bool isSliceParasagittalEnabled() const; void setSliceParasagittalEnabled(const bool sliceEnabledParasagittal); bool isSliceCoronalEnabled() const; void setSliceCoronalEnabled(const bool sliceEnabledCoronal); bool isSliceAxialEnabled() const; void setSliceAxialEnabled(const bool sliceEnabledAxial); void updateForVolumeFile(const VolumeMappableInterface* volumeFile); void selectSlicesAtOrigin(); void selectSlicesAtCoordinate(const float xyz[3]); void reset(); virtual SceneClass* saveToScene(const SceneAttributes* sceneAttributes, const AString& instanceName); virtual void restoreFromScene(const SceneAttributes* sceneAttributes, const SceneClass* sceneClass); // ADD_NEW_METHODS_HERE virtual AString toString() const; virtual void getDescriptionOfContent(const ModelTypeEnum::Enum modelType, PlainTextStringBuilder& descriptionOut) const; private: void copyHelperVolumeSliceSettings(const VolumeSliceSettings& obj); // ADD_NEW_MEMBERS_HERE /** Axis of slice being viewed */ VolumeSliceViewPlaneEnum::Enum m_sliceViewPlane; /** Type of slice drawing (single/montage) */ VolumeSliceDrawingTypeEnum::Enum m_sliceDrawingType; /** Type of slice projection (oblique/orthogonal) */ VolumeSliceProjectionTypeEnum::Enum m_sliceProjectionType; /** Number of montage rows */ int32_t m_montageNumberOfRows; /** Number of montage columns */ int32_t m_montageNumberOfColumns; /** Montage slice spacing */ int32_t m_montageSliceSpacing; mutable float m_sliceCoordinateParasagittal; mutable float m_sliceCoordinateCoronal; mutable float m_sliceCoordinateAxial; bool m_sliceEnabledParasagittal; bool m_sliceEnabledCoronal; bool m_sliceEnabledAxial; bool m_initializedFlag; //VolumeFile* m_lastVolumeFile; SceneClassAssistant* m_sceneAssistant; }; #ifdef __VOLUME_SLICE_SETTINGS_DECLARE__ // #endif // __VOLUME_SLICE_SETTINGS_DECLARE__ } // namespace #endif //__VOLUME_SLICE_SETTINGS_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Brain/VolumeSurfaceOutlineColorOrTabModel.cxx000066400000000000000000000373451300200146000313670ustar00rootroot00000000000000 /*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 __VOLUME_SURFACE_OUTLINE_COLOR_OR_TAB_MODEL_DECLARE__ #include "VolumeSurfaceOutlineColorOrTabModel.h" #undef __VOLUME_SURFACE_OUTLINE_COLOR_OR_TAB_MODEL_DECLARE__ #include "BrainConstants.h" #include "BrowserTabContent.h" #include "CaretAssert.h" #include "EventBrowserTabGet.h" #include "EventManager.h" #include "SceneClass.h" using namespace caret; /** * \class caret::VolumeSurfaceOutlineColorOrTabModel * \brief Model for selection of Color or Tab for Surface Outline * * Allows selection of a color or a browser tab for volume surface * outline. If a color is selected, the surface outline is drawn * in that color. If a browser tab is selected, the surface outline * is drawn in the current coloring for the selected surface using * the coloring assigned to the surface in the selected browser tab. * * Note: Only valid browser tabs are available for selection. */ /** * Constructor. */ VolumeSurfaceOutlineColorOrTabModel::VolumeSurfaceOutlineColorOrTabModel() : CaretObject() { m_selectedItem = NULL; for (int32_t i = 0; i < BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS; i++) { Item* item = new Item(i); m_allItems.push_back(item); } std::vector allColors; CaretColorEnum::getColorEnums(allColors); for (std::vector::iterator iter = allColors.begin(); iter != allColors.end(); iter++) { Item* item = new Item(*iter); m_allItems.push_back(item); } for (std::vector::iterator iter = m_allItems.begin(); iter != m_allItems.end(); iter++) { Item* item = *iter; if (item->isValid()) { m_selectedItem = const_cast(item); break; } } } /** * Destructor. */ VolumeSurfaceOutlineColorOrTabModel::~VolumeSurfaceOutlineColorOrTabModel() { for (std::vector::iterator iter = m_allItems.begin(); iter != m_allItems.end(); iter++) { Item* item = *iter; delete item; } m_allItems.clear(); } /** * Copy the given volume surface outline color or tab model. * @param modelToCopy * Model that is copied. */ void VolumeSurfaceOutlineColorOrTabModel::copyVolumeSurfaceOutlineColorOrTabModel(VolumeSurfaceOutlineColorOrTabModel* modelToCopy) { Item* otherItem = modelToCopy->getSelectedItem(); switch (otherItem->getItemType()) { case VolumeSurfaceOutlineColorOrTabModel::Item::ITEM_TYPE_COLOR: setColor(otherItem->getColor()); break; case VolumeSurfaceOutlineColorOrTabModel::Item::ITEM_TYPE_BROWSER_TAB: setBrowserTabIndex(otherItem->getBrowserTabIndex()); break; } } /** * @return All of the valid items for this model. */ std::vector VolumeSurfaceOutlineColorOrTabModel::getValidItems() { std::vector items; /* * Return all valid items */ for (std::vector::iterator iter = m_allItems.begin(); iter != m_allItems.end(); iter++) { Item* item = *iter; if (item->isValid()) { items.push_back(item); } } return items; } /** * @return Pointer to selected item (NULL if selection * is invalid. */ VolumeSurfaceOutlineColorOrTabModel::Item* VolumeSurfaceOutlineColorOrTabModel::getSelectedItem() { const int32_t numItems = static_cast(m_allItems.size()); /* * Make sure selected item is valid */ int32_t itemIndex = -1; if (m_selectedItem != NULL) { for (int32_t i = 0; i < numItems; i++) { if (m_allItems[i] == m_selectedItem) { if (m_allItems[i]->isValid() == false) { /* * Selected item is invalid */ m_selectedItem = NULL; } itemIndex = i; break; } } } if (m_selectedItem == NULL) { /* * Choose the previous valid item */ if (itemIndex >= 0) { for (int iBack = (itemIndex - 1); iBack >= 0; iBack--) { if (m_allItems[iBack]->isValid()) { m_selectedItem = m_allItems[iBack]; break; } } } if (m_selectedItem == NULL) { /* * Choose first valid item */ for (int i = 0; i < numItems; i++) { if (m_allItems[i]->isValid()) { m_selectedItem = m_allItems[i]; break; } } } } return m_selectedItem; } /** * Set the selected item. * @param item * New selected item. */ void VolumeSurfaceOutlineColorOrTabModel::setSelectedItem(const Item* item) { std::vector allItems = getValidItems(); const int32_t numItems = static_cast(allItems.size()); for (int32_t i = 0; i < numItems; i++) { if (item->equals(*allItems[i])) { m_selectedItem = allItems[i]; break; } } } /** * Set the selection to the given color. * @param color * Color that is to be selected. */ void VolumeSurfaceOutlineColorOrTabModel::setColor(const CaretColorEnum::Enum color) { std::vector allItems = getValidItems(); const int32_t numItems = static_cast(allItems.size()); for (int32_t i = 0; i < numItems; i++) { if (allItems[i]->getItemType() == Item::ITEM_TYPE_COLOR) { if (allItems[i]->getColor() == color) { setSelectedItem(allItems[i]); break; } } } } /** * Set the selection to the given browser tab. * @param browserTabIndex * Index of browser tab for selection. */ void VolumeSurfaceOutlineColorOrTabModel::setBrowserTabIndex(const int32_t browserTabIndex) { std::vector allItems = getValidItems(); const int32_t numItems = static_cast(allItems.size()); for (int32_t i = 0; i < numItems; i++) { if (allItems[i]->getItemType() == Item::ITEM_TYPE_BROWSER_TAB) { if (allItems[i]->getBrowserTabIndex() == browserTabIndex) { setSelectedItem(allItems[i]); break; } } } } /** * Create a scene for an instance of a class. * * @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. * * @return Pointer to SceneClass object representing the state of * this object. Under some circumstances a NULL pointer may be * returned. Caller will take ownership of returned object. */ SceneClass* VolumeSurfaceOutlineColorOrTabModel::saveToScene(const SceneAttributes* sceneAttributes, const AString& instanceName) { SceneClass* sceneClass = new SceneClass(instanceName, "VolumeSurfaceOutlineColorOrTabModel", 1); if (m_selectedItem != NULL) { sceneClass->addChild(m_selectedItem->saveToScene(sceneAttributes, "m_selectedItem")); } return sceneClass; } /** * Restore the state of an instance of a class. * * @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 containing the state that was previously * saved and should be restored. */ void VolumeSurfaceOutlineColorOrTabModel::restoreFromScene(const SceneAttributes* sceneAttributes, const SceneClass* sceneClass) { if (sceneClass == NULL) { return; } Item item(0); item.restoreFromScene(sceneAttributes, sceneClass->getClass("m_selectedItem")); setSelectedItem(&item); } //====================================================================== /** * \class caret::VolumeSurfaceOutlineColorOrTabModel::Item * \brief An item in VolumeSurfaceOutlineColorOrTabModel. * * At this time, item is either a color or a browser * tab index. */ /** * Constructor for a Caret Color. * @param color * The caret color. */ VolumeSurfaceOutlineColorOrTabModel::Item::Item(const CaretColorEnum::Enum color) { m_color = color; m_browserTabIndex = 0; m_itemType = ITEM_TYPE_COLOR; } /** * Constructor for a browser tab. * @param browserTabIndex * Index of browser tab. */ VolumeSurfaceOutlineColorOrTabModel::Item::Item(const int32_t browserTabIndex) { m_color = CaretColorEnum::BLACK; m_browserTabIndex = browserTabIndex; m_itemType = ITEM_TYPE_BROWSER_TAB; } /** * Destructor. */ VolumeSurfaceOutlineColorOrTabModel::Item::~Item() { } /** * Is this item equal to another item? * * @param item * Item for comparison. * @return * True if items are equal, else false. */ bool VolumeSurfaceOutlineColorOrTabModel::Item::equals(const Item& item) const { if (m_itemType == item.m_itemType) { switch (m_itemType) { case ITEM_TYPE_BROWSER_TAB: if (m_browserTabIndex == item.m_browserTabIndex) { return true; } break; case ITEM_TYPE_COLOR: if (m_color == item.m_color) { return true; } break; } } return false; } /** * @return Is this item valid? */ bool VolumeSurfaceOutlineColorOrTabModel::Item::isValid() const { bool valid = false; switch (m_itemType) { case ITEM_TYPE_BROWSER_TAB: if (getBrowserTabContent() != NULL) { valid = true; } break; case ITEM_TYPE_COLOR: valid = true; break; } return valid; } /** * @return Name of this item. */ AString VolumeSurfaceOutlineColorOrTabModel::Item::getName() { AString name = "PROGRAM ERROR"; switch(m_itemType) { case ITEM_TYPE_BROWSER_TAB: { // BrowserTabContent* btc = getBrowserTabContent(); // if (btc != NULL) { // name = ("Tab " // + AString::number(btc->getTabNumber() + 1)); // } name = ("Tab " + AString::number(m_browserTabIndex + 1)); } break; case ITEM_TYPE_COLOR: name = CaretColorEnum::toGuiName(m_color); break; } return name; } /** * @return Type of item. */ VolumeSurfaceOutlineColorOrTabModel::Item::ItemType VolumeSurfaceOutlineColorOrTabModel::Item::getItemType() const { return m_itemType; } /** * @return Pointer to browser tab in this item or NULL * if this item does NOT contain a browser tab. */ BrowserTabContent* VolumeSurfaceOutlineColorOrTabModel::Item::getBrowserTabContent() { EventBrowserTabGet getTabEvent(m_browserTabIndex); EventManager::get()->sendEvent(getTabEvent.getPointer()); BrowserTabContent* tabContent = getTabEvent.getBrowserTab(); return tabContent; } /** * @return Pointer to browser tab in this item or NULL * if this item does NOT contain a browser tab. */ const BrowserTabContent* VolumeSurfaceOutlineColorOrTabModel::Item::getBrowserTabContent() const { EventBrowserTabGet getTabEvent(m_browserTabIndex); EventManager::get()->sendEvent(getTabEvent.getPointer()); BrowserTabContent* tabContent = getTabEvent.getBrowserTab(); return tabContent; } /** * @return Index of browser tab in this item. This will always * return an integer greater than or equal to zero. Use isItemValid() * to ensure this item is valid. */ int32_t VolumeSurfaceOutlineColorOrTabModel::Item::getBrowserTabIndex() const { return m_browserTabIndex; } /** * @return Enumerated type for color in this item. Returned * value is undefined if a color is NOT in this item. */ CaretColorEnum::Enum VolumeSurfaceOutlineColorOrTabModel::Item::getColor() { return m_color; } /** * Create a scene for an instance of a class. * * @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. * * @return Pointer to SceneClass object representing the state of * this object. Under some circumstances a NULL pointer may be * returned. Caller will take ownership of returned object. */ SceneClass* VolumeSurfaceOutlineColorOrTabModel::Item::saveToScene(const SceneAttributes* /*sceneAttributes*/, const AString& instanceName) { SceneClass* sceneClass = new SceneClass(instanceName, "VolumeSurfaceOutlineColorOrTabModel::Item", 1); sceneClass->addInteger("m_browserTabIndex", m_browserTabIndex); sceneClass->addEnumeratedType("m_color", m_color); switch (m_itemType) { case ITEM_TYPE_COLOR: sceneClass->addString("m_itemType", "ITEM_TYPE_COLOR"); break; case ITEM_TYPE_BROWSER_TAB: sceneClass->addString("m_itemType", "ITEM_TYPE_BROWSER_TAB"); break; } sceneClass->addInteger("m_browserTabIndex", m_browserTabIndex); return sceneClass; } /** * Restore the state of an instance of a class. * * @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 containing the state that was previously * saved and should be restored. */ void VolumeSurfaceOutlineColorOrTabModel::Item::restoreFromScene(const SceneAttributes* /*sceneAttributes*/, const SceneClass* sceneClass) { if (sceneClass == NULL) { return; } m_browserTabIndex = sceneClass->getIntegerValue("m_browserTabIndex"); m_color = sceneClass->getEnumeratedTypeValue("m_color", CaretColorEnum::BLUE); const AString itemTypeName = sceneClass->getStringValue("m_itemType", "ITEM_TYPE_COLOR"); if (itemTypeName == "ITEM_TYPE_BROWSER_TAB") { m_itemType = ITEM_TYPE_BROWSER_TAB; } else if (itemTypeName == "ITEM_TYPE_COLOR") { m_itemType = ITEM_TYPE_COLOR; } else { CaretAssert(0); } } connectome-workbench-1.2.3+git41-gc4c6c90/src/Brain/VolumeSurfaceOutlineColorOrTabModel.h000066400000000000000000000100231300200146000307740ustar00rootroot00000000000000#ifndef __VOLUME_SURFACE_OUTLINE_COLOR_OR_TAB_MODEL__H_ #define __VOLUME_SURFACE_OUTLINE_COLOR_OR_TAB_MODEL__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 "BrainConstants.h" #include "CaretColorEnum.h" #include "CaretObject.h" #include "SceneableInterface.h" namespace caret { class BrowserTabContent; class SceneAttributes; class VolumeSurfaceOutlineColorOrTabModel : public CaretObject, public SceneableInterface { public: class Item : public SceneableInterface { public: /** * Type of item. */ enum ItemType { /** Item is a browser tab */ ITEM_TYPE_BROWSER_TAB, /** Item is a color */ ITEM_TYPE_COLOR }; Item(const CaretColorEnum::Enum color); Item(const int32_t browserTabIndex); ~Item(); bool isValid() const; bool equals(const Item& item) const; AString getName(); ItemType getItemType() const; BrowserTabContent* getBrowserTabContent(); const BrowserTabContent* getBrowserTabContent() const; int32_t getBrowserTabIndex() const; CaretColorEnum::Enum getColor(); virtual SceneClass* saveToScene(const SceneAttributes* sceneAttributes, const AString& instanceName); virtual void restoreFromScene(const SceneAttributes* sceneAttributes, const SceneClass* sceneClass); private: int32_t m_browserTabIndex; CaretColorEnum::Enum m_color; ItemType m_itemType; }; public: VolumeSurfaceOutlineColorOrTabModel(); virtual ~VolumeSurfaceOutlineColorOrTabModel(); std::vector getValidItems(); void copyVolumeSurfaceOutlineColorOrTabModel(VolumeSurfaceOutlineColorOrTabModel* modelToCopy); Item* getSelectedItem(); void setSelectedItem(const Item* item); void setColor(const CaretColorEnum::Enum color); void setBrowserTabIndex(const int32_t browserTabIndex); virtual SceneClass* saveToScene(const SceneAttributes* sceneAttributes, const AString& instanceName); virtual void restoreFromScene(const SceneAttributes* sceneAttributes, const SceneClass* sceneClass); private: VolumeSurfaceOutlineColorOrTabModel(const VolumeSurfaceOutlineColorOrTabModel&); VolumeSurfaceOutlineColorOrTabModel& operator=(const VolumeSurfaceOutlineColorOrTabModel&); std::vector m_allItems; Item* m_selectedItem; }; #ifdef __VOLUME_SURFACE_OUTLINE_COLOR_OR_TAB_MODEL_DECLARE__ // #endif // __VOLUME_SURFACE_OUTLINE_COLOR_OR_TAB_MODEL_DECLARE__ } // namespace #endif //__VOLUME_SURFACE_OUTLINE_COLOR_OR_TAB_MODEL__H_ connectome-workbench-1.2.3+git41-gc4c6c90/src/Brain/VolumeSurfaceOutlineModel.cxx000066400000000000000000000145361300200146000274350ustar00rootroot00000000000000 /*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 __VOLUME_SURFACE_OUTLINE_MODEL_DECLARE__ #include "VolumeSurfaceOutlineModel.h" #undef __VOLUME_SURFACE_OUTLINE_MODEL_DECLARE__ #include "SceneClass.h" #include "SceneClassAssistant.h" #include "SurfaceSelectionModel.h" #include "SurfaceTypeEnum.h" #include "VolumeSurfaceOutlineColorOrTabModel.h" using namespace caret; /** * \class VolumeSurfaceOutlineSelection * \brief Controls display of a volume surface outline. * * Controls display of a volume surface outline. */ /** * Constructor. */ VolumeSurfaceOutlineModel::VolumeSurfaceOutlineModel() : CaretObject() { std::vector validSurfaceTypes; validSurfaceTypes.push_back(SurfaceTypeEnum::ANATOMICAL); validSurfaceTypes.push_back(SurfaceTypeEnum::RECONSTRUCTION); validSurfaceTypes.push_back(SurfaceTypeEnum::INFLATED); validSurfaceTypes.push_back(SurfaceTypeEnum::VERY_INFLATED); m_displayed = false; m_thickness = VolumeSurfaceOutlineModel::DEFAULT_LINE_THICKNESS; 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_surfaceSelectionModel", "SurfaceSelectionModel", m_surfaceSelectionModel); m_sceneAssistant->add("m_colorOrTabModel", "VolumeSurfaceOutlineColorOrTabModel", m_colorOrTabModel); } /** * Destructor. */ VolumeSurfaceOutlineModel::~VolumeSurfaceOutlineModel() { delete m_surfaceSelectionModel; delete m_colorOrTabModel; delete m_sceneAssistant; } /** * Copy the given volume surface outline model to this model. * @param modelToCopy * Model that is copied. */ void VolumeSurfaceOutlineModel::copyVolumeSurfaceOutlineModel(VolumeSurfaceOutlineModel* modelToCopy) { m_displayed = modelToCopy->m_displayed; m_thickness = modelToCopy->m_thickness; m_surfaceSelectionModel->setSurface(modelToCopy->getSurface()); VolumeSurfaceOutlineColorOrTabModel* colorTabToCopy = modelToCopy->getColorOrTabModel(); m_colorOrTabModel->copyVolumeSurfaceOutlineColorOrTabModel(colorTabToCopy); } /** * Get a description of this object's content. * @return String describing this object's content. */ AString VolumeSurfaceOutlineModel::toString() const { return "VolumeSurfaceOutlineSelection"; } /** * @return Is this surface outline displayed? */ bool VolumeSurfaceOutlineModel::isDisplayed() const { return m_displayed; } /** * Set the display status of the surface outline. * @param displayed * New display status. */ void VolumeSurfaceOutlineModel::setDisplayed(const bool displayed) { m_displayed = displayed; } /** * @return Thickness for drawing surface. */ float VolumeSurfaceOutlineModel::getThickness() const { return m_thickness; } /** * Set the thickness for drawing the surface. * @param thickness * New value for thickness. */ void VolumeSurfaceOutlineModel::setThickness(const float thickness) { m_thickness = thickness; } /** * @return The surface selector used to select the surface. */ SurfaceSelectionModel* VolumeSurfaceOutlineModel::getSurfaceSelectionModel() { return m_surfaceSelectionModel; } /** * @return Get the selected surface. */ const Surface* VolumeSurfaceOutlineModel::getSurface() const { return m_surfaceSelectionModel->getSurface(); } /** * @return Get the selected surface. */ Surface* VolumeSurfaceOutlineModel::getSurface() { return m_surfaceSelectionModel->getSurface(); } /** * @return The model for color or tab selection. */ VolumeSurfaceOutlineColorOrTabModel* VolumeSurfaceOutlineModel::getColorOrTabModel() { return m_colorOrTabModel; } /** * @return The model for color or tab selection. */ const VolumeSurfaceOutlineColorOrTabModel* VolumeSurfaceOutlineModel::getColorOrTabModel() const { return m_colorOrTabModel; } /** * Create a scene for an instance of a class. * * @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. * * @return Pointer to SceneClass object representing the state of * this object. Under some circumstances a NULL pointer may be * returned. Caller will take ownership of returned object. */ SceneClass* VolumeSurfaceOutlineModel::saveToScene(const SceneAttributes* sceneAttributes, const AString& instanceName) { SceneClass* sceneClass = new SceneClass(instanceName, "VolumeSurfaceOutlineModel", 1); m_sceneAssistant->saveMembers(sceneAttributes, sceneClass); return sceneClass; } /** * Restore the state of an instance of a class. * * @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 containing the state that was previously * saved and should be restored. */ void VolumeSurfaceOutlineModel::restoreFromScene(const SceneAttributes* sceneAttributes, const SceneClass* sceneClass) { if (sceneClass == NULL) { return; } m_sceneAssistant->restoreMembers(sceneAttributes, sceneClass); } connectome-workbench-1.2.3+git41-gc4c6c90/src/Brain/VolumeSurfaceOutlineModel.h000066400000000000000000000060671300200146000270620ustar00rootroot00000000000000#ifndef __VOLUME_SURFACE_OUTLINE_MODEL__H_ #define __VOLUME_SURFACE_OUTLINE_MODEL__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 "SceneableInterface.h" namespace caret { class Surface; class SceneAttributes; class SceneClassAssistant; class SurfaceSelectionModel; class VolumeSurfaceOutlineColorOrTabModel; class VolumeSurfaceOutlineModel : public CaretObject, public SceneableInterface { public: VolumeSurfaceOutlineModel(); virtual ~VolumeSurfaceOutlineModel(); void copyVolumeSurfaceOutlineModel(VolumeSurfaceOutlineModel* modelToCopy); bool isDisplayed() const; void setDisplayed(const bool displayed); float getThickness() const; void setThickness(const float thickness); SurfaceSelectionModel* getSurfaceSelectionModel(); const Surface* getSurface() const; Surface* getSurface(); VolumeSurfaceOutlineColorOrTabModel* getColorOrTabModel(); const VolumeSurfaceOutlineColorOrTabModel* getColorOrTabModel() const; virtual SceneClass* saveToScene(const SceneAttributes* sceneAttributes, const AString& instanceName); virtual void restoreFromScene(const SceneAttributes* sceneAttributes, const SceneClass* sceneClass); static const int32_t DEFAULT_LINE_THICKNESS; private: VolumeSurfaceOutlineModel(const VolumeSurfaceOutlineModel&); VolumeSurfaceOutlineModel& operator=(const VolumeSurfaceOutlineModel&); public: virtual AString toString() const; private: bool m_displayed; float m_thickness; SurfaceSelectionModel* m_surfaceSelectionModel; VolumeSurfaceOutlineColorOrTabModel* m_colorOrTabModel; SceneClassAssistant* m_sceneAssistant; }; #ifdef __VOLUME_SURFACE_OUTLINE_MODEL_DECLARE__ const int32_t VolumeSurfaceOutlineModel::DEFAULT_LINE_THICKNESS = 2.0; #endif // __VOLUME_SURFACE_OUTLINE_MODEL_DECLARE__ } // namespace #endif //__VOLUME_SURFACE_OUTLINE_MODEL__H_ connectome-workbench-1.2.3+git41-gc4c6c90/src/Brain/VolumeSurfaceOutlineSetModel.cxx000066400000000000000000000340511300200146000301030ustar00rootroot00000000000000 /*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 __VOLUME_SURFACE_OUTLINE_SET_MODEL_DECLARE__ #include "VolumeSurfaceOutlineSetModel.h" #undef __VOLUME_SURFACE_OUTLINE_SET_MODEL_DECLARE__ #include "Brain.h" #include "BrainStructure.h" #include "BrowserTabContent.h" #include "CaretAssert.h" #include "EventBrowserTabGetAll.h" #include "EventManager.h" #include "ModelSurface.h" #include "SceneClassAssistant.h" #include "SceneClass.h" #include "SceneClassArray.h" #include "Surface.h" #include "SurfaceSelectionModel.h" #include "VolumeSurfaceOutlineColorOrTabModel.h" #include "VolumeSurfaceOutlineModel.h" #include "SceneableInterface.h" using namespace caret; /** * \class caret::VolumeSurfaceOutlineSetModel * \brief Holds a set of VolumeSurfaceOutlineModels * * Holds a set of VolumeSurfaceOutlineModels. Users * may add additional surface outline models up to * a fixed limit. There is also a minimum number * that are displayed. */ /** * Constructor. */ VolumeSurfaceOutlineSetModel::VolumeSurfaceOutlineSetModel() : CaretObject() { 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_sceneAssistant = new SceneClassAssistant(); m_sceneAssistant->add("m_numberOfDisplayedVolumeSurfaceOutlines", &m_numberOfDisplayedVolumeSurfaceOutlines); } /** * Destructor. */ VolumeSurfaceOutlineSetModel::~VolumeSurfaceOutlineSetModel() { for (int32_t i = 0; i < BrainConstants::MAXIMUM_NUMBER_OF_VOLUME_SURFACE_OUTLINES; i++) { delete m_outlineModels[i]; } delete m_sceneAssistant; } /** * @return The number of volume surface outlines. */ int32_t VolumeSurfaceOutlineSetModel::getNumberOfDislayedVolumeSurfaceOutlines() const { return m_numberOfDisplayedVolumeSurfaceOutlines; } /** * Copy the given volume surface outline set model. * @param setModel * Model that is copied. */ void VolumeSurfaceOutlineSetModel::copyVolumeSurfaceOutlineSetModel(VolumeSurfaceOutlineSetModel* setModel) { for (int32_t i = 0; i < BrainConstants::MAXIMUM_NUMBER_OF_VOLUME_SURFACE_OUTLINES; i++) { m_outlineModels[i]->copyVolumeSurfaceOutlineModel(setModel->getVolumeSurfaceOutlineModel(i)); } m_numberOfDisplayedVolumeSurfaceOutlines = setModel->getNumberOfDislayedVolumeSurfaceOutlines(); } /** * Set the number of volume surface outlines. * @param numberDisplayed * Number of displayed volume surface outlines. */ void VolumeSurfaceOutlineSetModel::setNumberOfDisplayedVolumeSurfaceOutlines(const int32_t numberDisplayed) { m_numberOfDisplayedVolumeSurfaceOutlines = numberDisplayed; if (m_numberOfDisplayedVolumeSurfaceOutlines < BrainConstants::MINIMUM_NUMBER_OF_VOLUME_SURFACE_OUTLINES) { m_numberOfDisplayedVolumeSurfaceOutlines = BrainConstants::MINIMUM_NUMBER_OF_VOLUME_SURFACE_OUTLINES; } if (m_numberOfDisplayedVolumeSurfaceOutlines > BrainConstants::MAXIMUM_NUMBER_OF_VOLUME_SURFACE_OUTLINES) { m_numberOfDisplayedVolumeSurfaceOutlines = BrainConstants::MAXIMUM_NUMBER_OF_VOLUME_SURFACE_OUTLINES; } } /** * @return The volume surface outline model at the given index. * @param indx * Index of volume surface outline model. */ VolumeSurfaceOutlineModel* VolumeSurfaceOutlineSetModel::getVolumeSurfaceOutlineModel(const int32_t indx) { CaretAssertArrayIndex(m_outlineModels, BrainConstants::MAXIMUM_NUMBER_OF_VOLUME_SURFACE_OUTLINES, indx); return m_outlineModels[indx]; } /** * @return The volume surface outline model at the given index. * @param indx * Index of volume surface outline model. */ const VolumeSurfaceOutlineModel* VolumeSurfaceOutlineSetModel::getVolumeSurfaceOutlineModel(const int32_t indx) const { CaretAssertArrayIndex(m_outlineModels, BrainConstants::MAXIMUM_NUMBER_OF_VOLUME_SURFACE_OUTLINES, indx); return m_outlineModels[indx]; } /** * Set the default selected surfaces after a spec file is loaded. * @searchForTabs * If true, examine the loaded tabs to find left and right surfaces. */ void VolumeSurfaceOutlineSetModel::selectSurfacesAfterSpecFileLoaded(Brain* brain, const bool searchForTabs) { EventBrowserTabGetAll getAllTabs; EventManager::get()->sendEvent(getAllTabs.getPointer()); /* * Find tabs with left/right */ int32_t leftTabIndex = -1; int32_t rightTabIndex = -1; const int numTabs = getAllTabs.getNumberOfBrowserTabs(); if (searchForTabs) { for (int32_t i = 0; i < numTabs; i++) { BrowserTabContent* tabContent = getAllTabs.getBrowserTab(i); ModelSurface* surfaceModel = tabContent->getDisplayedSurfaceModel(); if (surfaceModel != NULL) { const StructureEnum::Enum structure = surfaceModel->getSurface()->getStructure(); switch (structure) { case StructureEnum::CORTEX_LEFT: leftTabIndex = tabContent->getTabNumber(); break; case StructureEnum::CORTEX_RIGHT: rightTabIndex = tabContent->getTabNumber(); break; default: break; } } } } else { if (numTabs >= 1) { leftTabIndex = 0; } if (numTabs >= 2) { rightTabIndex = 1; } } Surface* leftMidThickSurface = NULL; Surface* leftWhiteSurface = NULL; Surface* leftPialSurface = NULL; BrainStructure* leftBrainStructure = brain->getBrainStructure(StructureEnum::CORTEX_LEFT, false); if (leftBrainStructure != NULL) { leftMidThickSurface = leftBrainStructure->getSurfaceContainingTextInName("midthick"); if (leftMidThickSurface == NULL) { leftMidThickSurface = leftBrainStructure->getPrimaryAnatomicalSurface(); } leftWhiteSurface = leftBrainStructure->getSurfaceContainingTextInName("white"); leftPialSurface = leftBrainStructure->getSurfaceContainingTextInName("pial"); } Surface* rightMidThickSurface = NULL; Surface* rightWhiteSurface = NULL; Surface* rightPialSurface = NULL; BrainStructure* rightBrainStructure = brain->getBrainStructure(StructureEnum::CORTEX_RIGHT, false); if (rightBrainStructure != NULL) { rightMidThickSurface = rightBrainStructure->getSurfaceContainingTextInName("midthick"); if (rightMidThickSurface == NULL) { rightMidThickSurface = rightBrainStructure->getPrimaryAnatomicalSurface(); } rightWhiteSurface = rightBrainStructure->getSurfaceContainingTextInName("white"); rightPialSurface = rightBrainStructure->getSurfaceContainingTextInName("pial"); } 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); } 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); } /** * Add a surface outline at the given outlineIndex. The * outlineIndex is incremented. * * @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. * @param color * If browserTabIndex is less than zero, the color source * is set to this color. * @param outlineIndex * If an outline was added, it is placed at this value * and it is incremented. If this index is greater * than or equal to the number of available surface * outlines, no action is taken. */ void VolumeSurfaceOutlineSetModel::addSurfaceOutline(Surface* surface, const float thickness, const int32_t browserTabIndex, const CaretColorEnum::Enum color, int32_t& outlineIndex) { if (surface != NULL) { if (surface->getSurfaceType() == SurfaceTypeEnum::ANATOMICAL) { if (outlineIndex < BrainConstants::MAXIMUM_NUMBER_OF_VOLUME_SURFACE_OUTLINES) { VolumeSurfaceOutlineModel* vsos = m_outlineModels[outlineIndex]; vsos->getSurfaceSelectionModel()->setSurface(surface); vsos->setThickness(thickness); if (browserTabIndex >= 0) { vsos->getColorOrTabModel()->setBrowserTabIndex(browserTabIndex); } else { vsos->getColorOrTabModel()->setColor(color); } outlineIndex++; } } } } /** * Create a scene for an instance of a class. * * @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. * * @return Pointer to SceneClass object representing the state of * this object. Under some circumstances a NULL pointer may be * returned. Caller will take ownership of returned object. */ SceneClass* VolumeSurfaceOutlineSetModel::saveToScene(const SceneAttributes* sceneAttributes, const AString& instanceName) { SceneClass* sceneClass = new SceneClass(instanceName, "VolumeSurfaceOutlineSetModel", 1); m_sceneAssistant->saveMembers(sceneAttributes, sceneClass); std::vector outlineModelSceneClassVector; for (int32_t i = 0; i < BrainConstants::MAXIMUM_NUMBER_OF_VOLUME_SURFACE_OUTLINES; i++ ) { outlineModelSceneClassVector.push_back(m_outlineModels[i]->saveToScene(sceneAttributes, ("m_outlineModels[" + AString::number(i) + "]"))); } sceneClass->addChild(new SceneClassArray("m_outlineModels", outlineModelSceneClassVector)); return sceneClass; } /** * Restore the state of an instance of a class. * * @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 containing the state that was previously * saved and should be restored. */ void VolumeSurfaceOutlineSetModel::restoreFromScene(const SceneAttributes* sceneAttributes, const SceneClass* sceneClass) { if (sceneClass == NULL) { return; } m_sceneAssistant->restoreMembers(sceneAttributes, sceneClass); const SceneClassArray* outlineModelsArrayClass = sceneClass->getClassArray("m_outlineModels"); if (outlineModelsArrayClass != NULL) { const int32_t maxNum = std::min(outlineModelsArrayClass->getNumberOfArrayElements(), (int32_t)BrainConstants::MAXIMUM_NUMBER_OF_VOLUME_SURFACE_OUTLINES); for (int32_t i = 0; i < maxNum; i++) { m_outlineModels[i]->restoreFromScene(sceneAttributes, outlineModelsArrayClass->getClassAtIndex(i)); } } } connectome-workbench-1.2.3+git41-gc4c6c90/src/Brain/VolumeSurfaceOutlineSetModel.h000066400000000000000000000063231300200146000275310ustar00rootroot00000000000000#ifndef __VOLUME_SURFACE_OUTLINE_SET_MODEL__H_ #define __VOLUME_SURFACE_OUTLINE_SET_MODEL__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 "BrainConstants.h" #include "CaretColorEnum.h" #include "CaretObject.h" #include "SceneableInterface.h" namespace caret { class Brain; class Surface; class SceneAttributes; class SceneClassAssistant; class VolumeSurfaceOutlineModel; class VolumeSurfaceOutlineSetModel : public CaretObject, public SceneableInterface { public: VolumeSurfaceOutlineSetModel(); virtual ~VolumeSurfaceOutlineSetModel(); void copyVolumeSurfaceOutlineSetModel(VolumeSurfaceOutlineSetModel* setModel); int32_t getNumberOfDislayedVolumeSurfaceOutlines() const; void setNumberOfDisplayedVolumeSurfaceOutlines(const int32_t numberDisplayed); VolumeSurfaceOutlineModel* getVolumeSurfaceOutlineModel(const int32_t indx); const VolumeSurfaceOutlineModel* getVolumeSurfaceOutlineModel(const int32_t indx) const; void selectSurfacesAfterSpecFileLoaded(Brain* brain, const bool searchForTabs); virtual SceneClass* saveToScene(const SceneAttributes* sceneAttributes, const AString& instanceName); virtual void restoreFromScene(const SceneAttributes* sceneAttributes, const SceneClass* sceneClass); private: VolumeSurfaceOutlineSetModel(const VolumeSurfaceOutlineSetModel&); VolumeSurfaceOutlineSetModel& operator=(const VolumeSurfaceOutlineSetModel&); void addSurfaceOutline(Surface* surface, const float thickness, const int32_t browserTabIndex, const CaretColorEnum::Enum color, int32_t& outlineIndex); VolumeSurfaceOutlineModel* m_outlineModels[BrainConstants::MAXIMUM_NUMBER_OF_VOLUME_SURFACE_OUTLINES]; int32_t m_numberOfDisplayedVolumeSurfaceOutlines; SceneClassAssistant* m_sceneAssistant; }; #ifdef __VOLUME_SURFACE_OUTLINE_SET_MODEL_DECLARE__ // #endif // __VOLUME_SURFACE_OUTLINE_SET_MODEL_DECLARE__ } // namespace #endif //__VOLUME_SURFACE_OUTLINE_SET_MODEL__H_ connectome-workbench-1.2.3+git41-gc4c6c90/src/Brain/WholeBrainSurfaceSettings.cxx000066400000000000000000000151321300200146000274110ustar00rootroot00000000000000 /*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 __WHOLE_BRAIN_SURFACE_SETTINGS_DECLARE__ #include "WholeBrainSurfaceSettings.h" #undef __WHOLE_BRAIN_SURFACE_SETTINGS_DECLARE__ #include "SceneClass.h" #include "SceneClassAssistant.h" using namespace caret; /** * \class caret::WholeBrainSurfaceSettings * \brief Surface settings for whole brain. * \ingroup Brain */ /** * Constructor. */ WholeBrainSurfaceSettings::WholeBrainSurfaceSettings() : CaretObject() { m_cerebellumEnabled = true; m_leftEnabled = true; m_rightEnabled = true; m_leftRightSeparation = 0.0; m_cerebellumSeparation = 0.0; m_sceneAssistant = new SceneClassAssistant(); m_sceneAssistant->add("m_leftEnabled", &m_leftEnabled); m_sceneAssistant->add("m_rightEnabled", &m_rightEnabled); m_sceneAssistant->add("m_cerebellumEnabled", &m_cerebellumEnabled); m_sceneAssistant->add("m_leftRightSeparation", &m_leftRightSeparation); m_sceneAssistant->add("m_cerebellumSeparation", &m_cerebellumSeparation); } /** * Destructor. */ WholeBrainSurfaceSettings::~WholeBrainSurfaceSettings() { delete m_sceneAssistant; } /** * Copy constructor. * @param obj * Object that is copied. */ WholeBrainSurfaceSettings::WholeBrainSurfaceSettings(const WholeBrainSurfaceSettings& obj) : CaretObject(obj), SceneableInterface(obj) { this->copyHelperWholeBrainSurfaceSettings(obj); } /** * Assignment operator. * @param obj * Data copied from obj to this. * @return * Reference to this object. */ WholeBrainSurfaceSettings& WholeBrainSurfaceSettings::operator=(const WholeBrainSurfaceSettings& obj) { if (this != &obj) { CaretObject::operator=(obj); this->copyHelperWholeBrainSurfaceSettings(obj); } return *this; } /** * Helps with copying an object of this type. * @param obj * Object that is copied. */ void WholeBrainSurfaceSettings::copyHelperWholeBrainSurfaceSettings(const WholeBrainSurfaceSettings& obj) { m_cerebellumEnabled = obj.m_cerebellumEnabled; m_leftEnabled = obj.m_leftEnabled; m_rightEnabled = obj.m_rightEnabled; m_leftRightSeparation = obj.m_leftRightSeparation; m_cerebellumSeparation = obj.m_cerebellumSeparation; } /** * Get a description of this object's content. * @return String describing this object's content. */ AString WholeBrainSurfaceSettings::toString() const { return "WholeBrainSurfaceSettings"; } /** * @return Enabled status for left cerebral cortex. */ bool WholeBrainSurfaceSettings::isLeftEnabled() const { return m_leftEnabled; } /** * Set the enabled status for the left hemisphere. * @param windowTabNumber * Index of window tab. * @param enabled * New enabled status. */ void WholeBrainSurfaceSettings::setLeftEnabled(const bool enabled) { m_leftEnabled = enabled; } /** * @return Enabled status for right cerebral cortex. */ bool WholeBrainSurfaceSettings::isRightEnabled() const { return m_rightEnabled; } /** * Set the enabled status for the right hemisphere. * @param enabled * New enabled status. */ void WholeBrainSurfaceSettings::setRightEnabled(const bool enabled) { m_rightEnabled = enabled; } /** * @return Enabled status for cerebellum. */ bool WholeBrainSurfaceSettings::isCerebellumEnabled() const { return m_cerebellumEnabled; } /** * Set the enabled status for the cerebellum. * @param enabled * New enabled status. */ void WholeBrainSurfaceSettings::setCerebellumEnabled(const bool enabled) { m_cerebellumEnabled = enabled; } /** * @return The separation between the left and right surfaces. */ float WholeBrainSurfaceSettings::getLeftRightSeparation() const { return m_leftRightSeparation; } /** * Set the separation between the cerebellum and the left/right surfaces. * @param separation * New value for separation. */ void WholeBrainSurfaceSettings::setLeftRightSeparation(const float separation) { m_leftRightSeparation = separation; } /** * @return The separation between the left/right surfaces. */ float WholeBrainSurfaceSettings::getCerebellumSeparation() const { return m_cerebellumSeparation; } /** * Set the separation between the cerebellum and the eft and right surfaces. * @param separation * New value for separation. */ void WholeBrainSurfaceSettings::setCerebellumSeparation(const float separation) { m_cerebellumSeparation = separation; } /** * 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* WholeBrainSurfaceSettings::saveToScene(const SceneAttributes* sceneAttributes, const AString& instanceName) { SceneClass* sceneClass = new SceneClass(instanceName, "WholeBrainSurfaceSettings", 1); m_sceneAssistant->saveMembers(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 WholeBrainSurfaceSettings::restoreFromScene(const SceneAttributes* sceneAttributes, const SceneClass* sceneClass) { if (sceneClass == NULL) { return; } m_sceneAssistant->restoreMembers(sceneAttributes, sceneClass); } connectome-workbench-1.2.3+git41-gc4c6c90/src/Brain/WholeBrainSurfaceSettings.h000066400000000000000000000056521300200146000270440ustar00rootroot00000000000000#ifndef __WHOLE_BRAIN_SURFACE_SETTINGS_H__ #define __WHOLE_BRAIN_SURFACE_SETTINGS_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 "SceneableInterface.h" namespace caret { class SceneClassAssistant; class WholeBrainSurfaceSettings : public CaretObject, public SceneableInterface { public: WholeBrainSurfaceSettings(); virtual ~WholeBrainSurfaceSettings(); WholeBrainSurfaceSettings(const WholeBrainSurfaceSettings& obj); WholeBrainSurfaceSettings& operator=(const WholeBrainSurfaceSettings& obj); bool isLeftEnabled() const; void setLeftEnabled(const bool enabled); bool isRightEnabled() const; void setRightEnabled(const bool enabled); bool isCerebellumEnabled() const; void setCerebellumEnabled(const bool enabled); float getLeftRightSeparation() const; void setLeftRightSeparation(const float separation); float getCerebellumSeparation() const; void setCerebellumSeparation(const float separation); // 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); private: void copyHelperWholeBrainSurfaceSettings(const WholeBrainSurfaceSettings& obj); SceneClassAssistant* m_sceneAssistant; bool m_leftEnabled; bool m_rightEnabled; bool m_cerebellumEnabled; float m_leftRightSeparation; float m_cerebellumSeparation; // ADD_NEW_MEMBERS_HERE }; #ifdef __WHOLE_BRAIN_SURFACE_SETTINGS_DECLARE__ // #endif // __WHOLE_BRAIN_SURFACE_SETTINGS_DECLARE__ } // namespace #endif //__WHOLE_BRAIN_SURFACE_SETTINGS_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Brain/WholeBrainVoxelDrawingMode.cxx000066400000000000000000000232441300200146000275210ustar00rootroot00000000000000 /*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 __WHOLE_BRAIN_VOXEL_DRAWING_MODE_ENUM_DECLARE__ #include "WholeBrainVoxelDrawingMode.h" #undef __WHOLE_BRAIN_VOXEL_DRAWING_MODE_ENUM_DECLARE__ #include "CaretAssert.h" using namespace caret; /** * \class caret::WholeBrainVoxelDrawingMode * \brief Enumerated type for drawing of voxels in whole brain view. */ /** * Constructor. * * @param enumValue * An enumerated value. * @param name * Name of enumerated value. * * @param guiName * User-friendly name for use in user-interface. */ WholeBrainVoxelDrawingMode::WholeBrainVoxelDrawingMode(const Enum enumValue, const AString& name, const AString& guiName) { this->enumValue = enumValue; this->integerCode = integerCodeCounter++; this->name = name; this->guiName = guiName; } /** * Destructor. */ WholeBrainVoxelDrawingMode::~WholeBrainVoxelDrawingMode() { } /** * Initialize the enumerated metadata. */ void WholeBrainVoxelDrawingMode::initialize() { if (initializedFlag) { return; } initializedFlag = true; enumData.push_back(WholeBrainVoxelDrawingMode(DRAW_VOXELS_AS_THREE_D_CUBES, "DRAW_VOXELS_AS_THREE_D_CUBES", "Draw Voxels as Cubes (3D)")); enumData.push_back(WholeBrainVoxelDrawingMode(DRAW_VOXELS_AS_ROUNDED_THREE_D_CUBES, "DRAW_VOXELS_AS_ROUNDED_THREE_D_CUBES", "Draw Voxels as Rounded Cubes (3D)")); enumData.push_back(WholeBrainVoxelDrawingMode(DRAW_VOXELS_ON_TWO_D_SLICES, "DRAW_VOXELS_ON_TWO_D_SLICES", "Draw Voxels on Slices (2D)")); } /** * 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 WholeBrainVoxelDrawingMode* WholeBrainVoxelDrawingMode::findData(const Enum enumValue) { if (initializedFlag == false) initialize(); size_t num = enumData.size(); for (size_t i = 0; i < num; i++) { const WholeBrainVoxelDrawingMode* 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 WholeBrainVoxelDrawingMode::toName(Enum enumValue) { if (initializedFlag == false) initialize(); const WholeBrainVoxelDrawingMode* 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. */ WholeBrainVoxelDrawingMode::Enum WholeBrainVoxelDrawingMode::fromName(const AString& name, bool* isValidOut) { if (initializedFlag == false) initialize(); bool validFlag = false; Enum enumValue = DRAW_VOXELS_ON_TWO_D_SLICES; for (std::vector::iterator iter = enumData.begin(); iter != enumData.end(); iter++) { const WholeBrainVoxelDrawingMode& 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 WholeBrainVoxelDrawingMode")); } return enumValue; } /** * Get a GUI string representation of the enumerated type. * @param enumValue * Enumerated value. * @return * String representing enumerated value. */ AString WholeBrainVoxelDrawingMode::toGuiName(Enum enumValue) { if (initializedFlag == false) initialize(); const WholeBrainVoxelDrawingMode* 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. */ WholeBrainVoxelDrawingMode::Enum WholeBrainVoxelDrawingMode::fromGuiName(const AString& guiName, bool* isValidOut) { if (initializedFlag == false) initialize(); bool validFlag = false; Enum enumValue = DRAW_VOXELS_ON_TWO_D_SLICES; for (std::vector::iterator iter = enumData.begin(); iter != enumData.end(); iter++) { const WholeBrainVoxelDrawingMode& 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 WholeBrainVoxelDrawingMode")); } return enumValue; } /** * Get the integer code for a data type. * * @return * Integer code for data type. */ int32_t WholeBrainVoxelDrawingMode::toIntegerCode(Enum enumValue) { if (initializedFlag == false) initialize(); const WholeBrainVoxelDrawingMode* 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. */ WholeBrainVoxelDrawingMode::Enum WholeBrainVoxelDrawingMode::fromIntegerCode(const int32_t integerCode, bool* isValidOut) { if (initializedFlag == false) initialize(); bool validFlag = false; Enum enumValue = DRAW_VOXELS_ON_TWO_D_SLICES; for (std::vector::iterator iter = enumData.begin(); iter != enumData.end(); iter++) { const WholeBrainVoxelDrawingMode& 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 WholeBrainVoxelDrawingMode")); } 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 WholeBrainVoxelDrawingMode::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 WholeBrainVoxelDrawingMode::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(WholeBrainVoxelDrawingMode::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 WholeBrainVoxelDrawingMode::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(WholeBrainVoxelDrawingMode::toGuiName(iter->enumValue)); } if (isSorted) { std::sort(allGuiNames.begin(), allGuiNames.end()); } } connectome-workbench-1.2.3+git41-gc4c6c90/src/Brain/WholeBrainVoxelDrawingMode.h000066400000000000000000000066161300200146000271520ustar00rootroot00000000000000#ifndef __WHOLE_BRAIN_VOXEL_DRAWING_MODE_ENUM_H__ #define __WHOLE_BRAIN_VOXEL_DRAWING_MODE_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 WholeBrainVoxelDrawingMode { public: /** * Enumerated values. */ enum Enum { /** In Whole Brain, view volume data as 3D cubes */ DRAW_VOXELS_AS_THREE_D_CUBES, /** In Whole Brain, view volume data as Rounded 3D cubes */ DRAW_VOXELS_AS_ROUNDED_THREE_D_CUBES, /** In Whole Brain, view volume data on slices */ DRAW_VOXELS_ON_TWO_D_SLICES }; ~WholeBrainVoxelDrawingMode(); 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: WholeBrainVoxelDrawingMode(const Enum enumValue, const AString& name, const AString& guiName); static const WholeBrainVoxelDrawingMode* 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 __WHOLE_BRAIN_VOXEL_DRAWING_MODE_ENUM_DECLARE__ std::vector WholeBrainVoxelDrawingMode::enumData; bool WholeBrainVoxelDrawingMode::initializedFlag = false; int32_t WholeBrainVoxelDrawingMode::integerCodeCounter = 0; #endif // __WHOLE_BRAIN_VOXEL_DRAWING_MODE_ENUM_DECLARE__ } // namespace #endif //__WHOLE_BRAIN_VOXEL_DRAWING_MODE_ENUM_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/CMakeLists.txt000077500000000000000000000433471300200146000233220ustar00rootroot00000000000000# # Minimum required version of CMAKE # CMAKE_MINIMUM_REQUIRED (VERSION 2.8) MESSAGE("\nCMake Version: ${CMAKE_VERSION}\n") # # # #message(INFORMATION "\nTo get the correct version of QT, qmake must be in the PATH\n") #TSC: use "CACHE " syntax in SET commands so they can be overridden by cmake options # # 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) ENDIF(${CMAKE_SYSTEM_NAME} MATCHES "Linux") ELSEIF(WIN32) ADD_DEFINITIONS(-DCARET_OS_WINDOWS) 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") 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}) 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$") # # Clang compiler on Mac # UNSET(CLANG_FLAG) IF (${CMAKE_CXX_COMPILER} MATCHES "^.*clang\\+\\+.*") SET(CLANG_FLAG TRUE) ENDIF (${CMAKE_CXX_COMPILER} MATCHES "^.*clang\\+\\+.*") 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) # # IF GNU compiler, functions without a return type or switch # 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) 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) # # Define flag to avoid trying to compile SIMD stuff (coded for x86 only) # SET(WORKBENCH_USE_SIMD TRUE CACHE BOOL "try to compile with SIMD support") # # If we should try to use SIMD, check whether cpuinfo compiles # 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}") 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) 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}") ELSE (OPENSSL_FOUND) MESSAGE("OpenSSL NOT FOUND") ENDIF(OPENSSL_FOUND) # # Must have QT 4.8 or later # 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}) # # Try to find Qwt, utherwise use bundle # 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}") ELSE (Qwt_FOUND) MESSAGE("Qwt library not found, using bundled") ENDIF (Qwt_FOUND) # # 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. # # The environment variables are: # OPENMP_COMPILE_OPTION=-fopenmp # OPENMP_HEADER_DIR=/usr/local/clang-openmp-opt/llvm/build/Release/include # OPENMP_LIB_DIR=/usr/local/clang-openmp-opt/llvm/build/Release/lib # UNSET(OPENMP_FOUND) IF (EXISTS $ENV{OPENMP_HEADER_DIR}) MESSAGE("OpenMP Header File: $ENV{OPENMP_HEADER_DIR}") IF (EXISTS $ENV{OPENMP_LIB_DIR}) MESSAGE("OpenMP Library File: $ENV{OPENMP_LIB_DIR}") SET (STUFF $ENV{OPENMP_COMPILE_OPTION}) IF (DEFINED STUFF) MESSAGE("OpenMP Compiler Option: $ENV{OPENMP_COMPILE_OPTION}") SET(OpenMP_CXX_FLAGS "-I$ENV{OPENMP_HEADER_DIR} $ENV{OPENMP_COMPILE_OPTION}") SET(OpenMP_C_FLAGS "-I$ENV{OPENMP_HEADER_DIR} $ENV{OPENMP_COMPILE_OPTION}") SET(CMAKE_EXE_LINKER_FLAGS ${CMAKE_EXE_LINKER_FLAGS} -L$ENV{OPENMP_LIB_DIR}) SET(OPENMP_FOUND TRUE) ENDIF (DEFINED STUFF) ENDIF (EXISTS $ENV{OPENMP_LIB_DIR}) ENDIF (EXISTS $ENV{OPENMP_HEADER_DIR}) # # IF OpenMP not found through environment variables, # Use CMAKE's Find OpenMP module # IF (NOT OPENMP_FOUND) FIND_PACKAGE(OpenMP) ENDIF (NOT OPENMP_FOUND) # # If OpenMP is found, may need to set compiler and linker flags # 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}") # # Try to link static with Intel Compiler # IF (${CMAKE_CXX_COMPILER} MATCHES "^.*icpc$") MESSAGE(WARNING "Intel Compiler Being Used") SET (CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -openmp-link=static") SET (CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -static-intel") ADD_DEFINITIONS("-static-intel") ENDIF() ELSE (OPENMP_FOUND) MESSAGE(WARNING "OpenMP was not found") IF (CLANG_FLAG) # # The clang compiler does not support OpenMP so it produces many warnings # with "Unknown pragma ignored". So, tell clang to ignore unknown pragmas # so the message is not printed. # ADD_DEFINITIONS("-Wno-unknown-pragmas") ENDIF (CLANG_FLAG) ENDIF(OPENMP_FOUND) MESSAGE("\nC++ flags ${CMAKE_CXX_FLAGS}\n") # # MUST have ZLIB # FIND_PACKAGE(ZLIB) IF ( ZLIB_FOUND ) INCLUDE_DIRECTORIES(${ZLIB_INCLUDE_DIRS}) ELSE (ZLIB_FOUND) MESSAGE(FATAL_ERROR "ZLIB was not found") ENDIF (ZLIB_FOUND) # # Try to find QuaZip, otherwise use bundled # FIND_PACKAGE(QuaZip) IF (QUAZIP_FOUND) MESSAGE("QuaZip library found") MESSAGE(" INCLUDES ${QUAZIP_INCLUDE_DIRS}") MESSAGE(" LIBS ${QUAZIP_LIBRARIES}") ELSE (QUAZIP_FOUND) MESSAGE("QuaZip library not found, using bundled") # # Quazip needs this defined here for static linking on windows # IF(WIN32) IF(MSVC) ADD_DEFINITIONS(-DQUAZIP_STATIC) ENDIF(MSVC) ENDIF(WIN32) ENDIF (QUAZIP_FOUND) # # Find FreeType # SET(FTGL_FONT_MODULE_FOR_LINKING "") 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}") ELSE (FTGL_FOUND) MESSAGE("FTGL library not found, using bundled") ENDIF (FTGL_FOUND) ADD_DEFINITIONS(-DHAVE_FREETYPE) 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(" ") ENDIF (FREETYPE_FOUND) # # Fixes issue with XCode and newer version of CMake. # It prevents the ZERO_CHECK dependency from running # (which is very slow) every time a build is performed # in XCode. # 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 # SET(OSMESA_FOUND FALSE) SET(OSMESA_DEFINITION "") SET(OSMESA_OFFSCREEN_LIBRARY "") 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") FIND_LIBRARY(OSMESA_LIBRARY_FOUND NAMES OSMesa HINTS $ENV{OSMESA_DIR}/lib) FIND_LIBRARY(OSMESA_GL_LIBRARY_FOUND NAMES GL HINTS $ENV{OSMESA_DIR}/lib) FIND_LIBRARY(OSMESA_GLU_LIBRARY_FOUND NAMES GLU HINTS $ENV{OSMESA_DIR}/lib) MESSAGE("OSMesa lib: " ${OSMESA_LIBRARY}) 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_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}") 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("") #============================================================================= # # All subdirectories that will be configured for building # IF (NOT QUAZIP_FOUND) ADD_SUBDIRECTORY ( Quazip ) ENDIF (NOT QUAZIP_FOUND) ADD_SUBDIRECTORY ( Common ) ADD_SUBDIRECTORY ( Xml ) ADD_SUBDIRECTORY ( Scenes ) ADD_SUBDIRECTORY ( OSMesaDummy ) IF (FREETYPE_FOUND AND NOT FTGL_FOUND) ADD_SUBDIRECTORY ( FtglFont ) ENDIF (FREETYPE_FOUND AND NOT FTGL_FOUND) ADD_SUBDIRECTORY ( Annotations ) ADD_SUBDIRECTORY ( Charting ) ADD_SUBDIRECTORY ( Palette ) ADD_SUBDIRECTORY ( FilesBase ) ADD_SUBDIRECTORY ( Nifti ) ADD_SUBDIRECTORY ( Gifti ) ADD_SUBDIRECTORY ( Cifti ) ADD_SUBDIRECTORY ( Files ) ADD_SUBDIRECTORY ( OperationsBase ) ADD_SUBDIRECTORY ( Algorithms ) ADD_SUBDIRECTORY ( Operations ) ADD_SUBDIRECTORY ( Brain ) IF (NOT Qwt_FOUND) ADD_SUBDIRECTORY ( Qwt ) ENDIF (NOT Qwt_FOUND) ADD_SUBDIRECTORY ( GuiQt ) ADD_SUBDIRECTORY ( Commands ) ADD_SUBDIRECTORY ( Desktop ) ADD_SUBDIRECTORY ( CommandLine ) ADD_SUBDIRECTORY ( Tests ) if (WORKBENCH_USE_SIMD AND CPUINFO_COMPILES) ADD_SUBDIRECTORY ( kloewe/cpuinfo ) ADD_SUBDIRECTORY ( kloewe/dot ) ENDIF() # #CTest tests # ENABLE_TESTING() connectome-workbench-1.2.3+git41-gc4c6c90/src/CMakeScripts/000077500000000000000000000000001300200146000230745ustar00rootroot00000000000000connectome-workbench-1.2.3+git41-gc4c6c90/src/CMakeScripts/copy_mac_frameworks.sh000077500000000000000000000007241300200146000274700ustar00rootroot00000000000000#!/bin/sh # # This script copies adds the frameworks to the Mac Bundles # exeName=$1 for buildType in Debug/ Release/ RelWithDebInfo/ MinRelSize/ "" do echo "BUILD TYPE ${buildType}" appName=${buildType}${exeName}.app exeName=${appName}/Contents/MacOS/${exeName} echo "App ${appName}" echo "Exe ${exeName}" if [ -f ${exeName} ] ; then if [ ! -d ${appName}/Contents/Frameworks ] ; then macdeployqt ${appName} fi fi done connectome-workbench-1.2.3+git41-gc4c6c90/src/CMakeScripts/copy_mac_icon.sh000077500000000000000000000010051300200146000262310ustar00rootroot00000000000000#!/bin/sh # # This script copies the mac icon into # into the Mac App's Resources directory. # exeName=$1 iconName=$2 for buildType in Debug/ Release/ RelWithDebInfo/ MinRelSize/ "" do echo "BUILD TYPE ${buildType}" appName=${buildType}${exeName}.app/Contents/MacOS/${exeName} echo "App ${appName}" if [ -f ${appName} ] ; then cp $iconName ${buildType}${exeName}.app/Contents/Resources #cp -R ${QTDIR}/src/gui/mac/qt_menu.nib ${buildType}${exeName}.app/Contents/Resources fi done connectome-workbench-1.2.3+git41-gc4c6c90/src/CMakeScripts/copy_mac_nib.sh000077500000000000000000000020151300200146000260530ustar00rootroot00000000000000#!/bin/sh # # This script copies the qt_menu.nib directory from QT # into the Mac App's Resources directory. # exeName=$1 for buildType in Debug/ Release/ RelWithDebInfo/ MinRelSize/ "" do echo "BUILD TYPE ${buildType}" appName=${buildType}${exeName}.app/Contents/MacOS/${exeName} echo "App ${appName}" if [ -f ${appName} ] ; then if [ ! -d ${buildType}${exeName}.app/Contents/Resources ] ; then echo "Creating resources directory" mkdir ${buildType}${exeName}.app/Contents/Resources fi nib1=${QTDIR}/src/gui/mac/qt_menu.nib echo "nib1: ${nib1}" if [ -f ${nib1} ] ; then echo "Copying NIB file 1" cp -R ${QTDIR}/src/gui/mac/qt_menu.nib ${buildType}${exeName}.app/Contents/Resources fi nib2=${QTDIR}/lib/QtGui.framework/Versions/4/Resources/qt_menu.nib echo "nib2: ${nib2}" if [ -d ${nib2} ] ; then echo "Copying NIB file 2" cp -R ${nib2} ${buildType}${exeName}.app/Contents/Resources fi fi done connectome-workbench-1.2.3+git41-gc4c6c90/src/CMakeScripts/git_commit_info.cmake.in000066400000000000000000000022111300200146000276450ustar00rootroot00000000000000FIND_PACKAGE(Git QUIET) IF(GIT_FOUND) EXECUTE_PROCESS( COMMAND ${GIT_EXECUTABLE} rev-parse HEAD OUTPUT_VARIABLE "COMMIT" OUTPUT_STRIP_TRAILING_WHITESPACE ERROR_QUIET RESULT_VARIABLE commit_success ) IF(NOT ${commit_success} EQUAL 0) SET(COMMIT "unknown") ENDIF(NOT ${commit_success} EQUAL 0) EXECUTE_PROCESS( COMMAND ${GIT_EXECUTABLE} log -1 --pretty=format:%ai OUTPUT_VARIABLE "COMMIT_DATE" OUTPUT_STRIP_TRAILING_WHITESPACE ERROR_QUIET RESULT_VARIABLE commit_date_success ) IF(NOT ${commit_date_success} EQUAL 0) SET(COMMIT_DATE "unknown") ENDIF(NOT ${commit_date_success} EQUAL 0) ELSE(GIT_FOUND) SET(COMMIT "unknown") SET(COMMIT_DATE "unknown") ENDIF(GIT_FOUND) #configure_file only detects changes to the input file, not changes to variable values being used (not surprising) #so, let the custom command that calls this script handle dependencies as to when it should run, then force it to run whenever this script is used by deleting the output first FILE(REMOVE ${OUTFILE}) CONFIGURE_FILE(${INFILE} ${OUTFILE} @ONLY) connectome-workbench-1.2.3+git41-gc4c6c90/src/CMakeScripts/save.sh000077500000000000000000000011741300200146000243740ustar00rootroot00000000000000#/bin/sh -v set on echo "Current Directory" `pwd` if [ -f Debug/desktop.app/Contents/MacOS/desktop ] ; then # cp -R $QTDIR/src/gui/mac/qt_menu.nib Debug/desktop.app/Contents/Resources ; fi if [ -f Release/desktop.app/Contents/MacOS/desktop ] ; then echo "Exists" if [ ! -d Debug/desktop.app/Contents/Resources ] ; then mkdir Debug/desktop.app/Contents/Resources fi fi if [ -f RelWithDebInfo/desktop.app/Contents/MacOS/desktop ] ; then echo "Exists" fi if [ -f MinRelSize/desktop.app/Contents/MacOS/desktop ] ; then echo "Exists" fi if [ -f desktop.app/Contents/MacOS/desktop ] ; then echo "Exists" fi connectome-workbench-1.2.3+git41-gc4c6c90/src/Charting/000077500000000000000000000000001300200146000223035ustar00rootroot00000000000000connectome-workbench-1.2.3+git41-gc4c6c90/src/Charting/CMakeLists.txt000066400000000000000000000022751300200146000250510ustar00rootroot00000000000000 # # Name of Project # PROJECT(Charting) # # Create a library # ADD_LIBRARY(Charting ChartAxis.h ChartAxisCartesian.h ChartAxisLocationEnum.h ChartAxisTypeEnum.h ChartAxisUnitsEnum.h ChartData.h ChartDataCartesian.h ChartDataSource.h ChartDataSourceModeEnum.h ChartDataTypeEnum.h ChartMatrixDisplayProperties.h ChartMatrixLoadingDimensionEnum.h ChartMatrixScaleModeEnum.h ChartModel.h ChartModelCartesian.h ChartModelDataSeries.h ChartModelFrequencySeries.h ChartModelTimeSeries.h ChartPoint.h ChartScaleAutoRanging.h ChartSelectionModeEnum.h ChartAxis.cxx ChartAxisCartesian.cxx ChartAxisLocationEnum.cxx ChartAxisTypeEnum.cxx ChartAxisUnitsEnum.cxx ChartData.cxx ChartDataCartesian.cxx ChartDataSource.cxx ChartDataSourceModeEnum.cxx ChartDataTypeEnum.cxx ChartMatrixDisplayProperties.cxx ChartMatrixLoadingDimensionEnum.cxx ChartMatrixScaleModeEnum.cxx ChartModel.cxx ChartModelCartesian.cxx ChartModelDataSeries.cxx ChartModelFrequencySeries.cxx ChartModelTimeSeries.cxx ChartPoint.cxx ChartScaleAutoRanging.cxx ChartSelectionModeEnum.cxx ) # # Include directories # INCLUDE_DIRECTORIES( ${CMAKE_SOURCE_DIR}/Annotations ${CMAKE_SOURCE_DIR}/Charting ${CMAKE_SOURCE_DIR}/Common ${CMAKE_SOURCE_DIR}/Scenes ) connectome-workbench-1.2.3+git41-gc4c6c90/src/Charting/ChartAxis.cxx000066400000000000000000000216701300200146000247230ustar00rootroot00000000000000 /*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_AXIS_DECLARE__ #include "ChartAxis.h" #undef __CHART_AXIS_DECLARE__ #include "CaretAssert.h" #include "ChartAxisCartesian.h" #include "ChartScaleAutoRanging.h" #include "SceneClass.h" #include "SceneClassAssistant.h" using namespace caret; /** * \class caret::ChartAxis * \brief Contains information about a chart axis. * \ingroup Charting */ /** * Constructor. * * @param axisType * The axis type. * @param axisLocation. * Axis location. */ ChartAxis::ChartAxis(const ChartAxisTypeEnum::Enum axisType, const ChartAxisLocationEnum::Enum axisLocation) : CaretObject(), SceneableInterface(), m_axisType(axisType), m_axisLocation(axisLocation) { initializeMembersChartAxis(); } /** * Create and return an axis of the given type and at the given location. * * @param axisType * Type of axis. * @param axisLocation * Location of axis. * @return * Axis that was created. */ ChartAxis* ChartAxis::newChartAxisForTypeAndLocation(const ChartAxisTypeEnum::Enum axisType, const ChartAxisLocationEnum::Enum axisLocation) { ChartAxis* axis = NULL; switch (axisType) { case ChartAxisTypeEnum::CHART_AXIS_TYPE_CARTESIAN: axis = new ChartAxisCartesian(axisLocation); break; case ChartAxisTypeEnum::CHART_AXIS_TYPE_NONE: CaretAssert(0); break; } return axis; } /** * Destructor. */ ChartAxis::~ChartAxis() { delete m_sceneAssistant; } /** * Initialize members of a new instance. */ void ChartAxis::initializeMembersChartAxis() { m_parentChartModel = NULL; m_autoRangeScaleEnabled = true; m_axisUnits = ChartAxisUnitsEnum::CHART_AXIS_UNITS_NONE; m_labelFontSize = 12; m_visible = false; m_sceneAssistant = new SceneClassAssistant(); m_sceneAssistant->add("m_axisLocation", &m_axisLocation); m_sceneAssistant->add("m_autoRangeScaleEnabled", &m_autoRangeScaleEnabled); m_sceneAssistant->add("m_labelFontSize", &m_labelFontSize); m_sceneAssistant->add("m_visible", &m_visible); m_sceneAssistant->add("m_text", &m_text); } /** * Copy constructor. * @param obj * Object that is copied. */ ChartAxis::ChartAxis(const ChartAxis& obj) : CaretObject(obj), SceneableInterface(obj) { initializeMembersChartAxis(); copyHelperChartAxis(obj); } /** * Assignment operator. * @param obj * Data copied from obj to this. * @return * Reference to this object. */ ChartAxis& ChartAxis::operator=(const ChartAxis& obj) { if (this != &obj) { CaretObject::operator=(obj); this->copyHelperChartAxis(obj); } return *this; } /** * Helps with copying an object of this type. * @param obj * Object that is copied. */ void ChartAxis::copyHelperChartAxis(const ChartAxis& obj) { m_parentChartModel = NULL; m_axisType = obj.m_axisType; m_axisLocation = obj.m_axisLocation; m_text = obj.m_text; m_axisUnits = obj.m_axisUnits; m_labelFontSize = obj.m_labelFontSize; m_visible = obj.m_visible; m_autoRangeScaleEnabled = obj.m_autoRangeScaleEnabled; } /** * @return The type of the axis. */ ChartAxisTypeEnum::Enum ChartAxis::getAxisType() const { return m_axisType; } /** * @return The location of the axis. */ ChartAxisLocationEnum::Enum ChartAxis::getAxisLocation() const { return m_axisLocation; } /** * Set the parent chart model. * * @param parentChartModel * Chart in which this axis is used. */ void ChartAxis::setParentChartModel(ChartModel* parentChartModel) { m_parentChartModel = parentChartModel; } /** * @return The chart model that uses this axis (may be NULL). */ ChartModel* ChartAxis::getParentChartModel() { return m_parentChartModel; } /** * @return The chart model that uses this axis (may be NULL). */ const ChartModel* ChartAxis::getParentChartModel() const { return m_parentChartModel; } /** * Get a description of this object's content. * @return String describing this object's content. */ AString ChartAxis::toString() const { return "ChartAxis"; } /** * @return Text for axis label. */ AString ChartAxis::getText() const { return m_text; } /** * Set text for axis label. * * @param text * New text for label. */ void ChartAxis::setText(const AString& text) { m_text = text; } /** * @return Axis Units. */ ChartAxisUnitsEnum::Enum ChartAxis::getAxisUnits() const { return m_axisUnits; } /** * Set the units for the axis. * * @param axisUnits * New value for axis units. */ void ChartAxis::setAxisUnits(const ChartAxisUnitsEnum::Enum axisUnits) { m_axisUnits = axisUnits; } /** * Return the suffix for the axis units */ AString ChartAxis::getAxisUnitsSuffix() const { AString suffix; switch (m_axisUnits) { case ChartAxisUnitsEnum::CHART_AXIS_UNITS_NONE: break; case ChartAxisUnitsEnum::CHART_AXIS_UNITS_FREQUENCY_HERTZ: suffix = "hz"; break; case ChartAxisUnitsEnum::CHART_AXIS_UNITS_TIME_SECONDS: suffix = "s"; break; } return suffix; } /** * @return Font size for the label's text. */ int32_t ChartAxis::getLabelFontSize() const { return m_labelFontSize; } /** * Set font size for label's text. * * @param fontSize * New value for font size. */ void ChartAxis::setLabelFontSize(const float fontSize) { m_labelFontSize = static_cast(fontSize); } /** * @return True if this axis should be displayed. */ bool ChartAxis::isVisible() const { return m_visible; } /** * Set this axis should be displayed. * * @param visible * True if displayed, else false. */ void ChartAxis::setVisible(const bool visible) { m_visible = visible; } /** * Is auto range scale enabled (scale matches data) */ bool ChartAxis::isAutoRangeScaleEnabled() const { return m_autoRangeScaleEnabled; } /** * Set auto range scale enabled (scale matches data) * * @param autoRangeScaleEnabled * New status. */ void ChartAxis::setAutoRangeScaleEnabled(const bool autoRangeScaleEnabled) { m_autoRangeScaleEnabled = autoRangeScaleEnabled; if (m_autoRangeScaleEnabled) { updateForAutoRangeScale(); } } /** * Create a scene for an instance of a class. * * @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 the class' instance. * * @return Pointer to SceneClass object representing the state of * this object. Under some circumstances a NULL pointer may be * returned. Caller will take ownership of returned object. */ SceneClass* ChartAxis::saveToScene(const SceneAttributes* sceneAttributes, const AString& instanceName) { SceneClass* sceneClass = new SceneClass(instanceName, "ChartAxis", 1); m_sceneAssistant->saveMembers(sceneAttributes, sceneClass); saveSubClassDataToScene(sceneAttributes, sceneClass); return sceneClass; } /** * Restore the state of an instance of a class. * * @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. May be NULL for some types of scenes. */ void ChartAxis::restoreFromScene(const SceneAttributes* sceneAttributes, const SceneClass* sceneClass) { if (sceneClass == NULL) { return; } m_sceneAssistant->restoreMembers(sceneAttributes, sceneClass); restoreSubClassDataFromScene(sceneAttributes, sceneClass); } connectome-workbench-1.2.3+git41-gc4c6c90/src/Charting/ChartAxis.h000066400000000000000000000115721300200146000243500ustar00rootroot00000000000000#ifndef __CHART_AXIS_H__ #define __CHART_AXIS_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 "ChartAxisLocationEnum.h" #include "ChartAxisTypeEnum.h" #include "ChartAxisUnitsEnum.h" #include "SceneableInterface.h" namespace caret { class ChartModel; class SceneClassAssistant; class ChartAxis : public CaretObject, public SceneableInterface { public: enum Axis { AXIS_BOTTOM, AXIS_LEFT, AXIS_RIGHT, AXIS_TOP }; static ChartAxis* newChartAxisForTypeAndLocation(const ChartAxisTypeEnum::Enum axisType, const ChartAxisLocationEnum::Enum axisLocation); virtual ~ChartAxis(); ChartAxis(const ChartAxis&); ChartAxis& operator=(const ChartAxis&); /** * At times a copy of chart axis will be needed BUT it must be * the proper subclass so copy constructor and assignment operator * will 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. */ virtual ChartAxis* clone() const = 0; void setParentChartModel(ChartModel* parentChartModel); ChartAxisTypeEnum::Enum getAxisType() const; ChartAxisLocationEnum::Enum getAxisLocation() const; AString getText() const; void setText(const AString& text); ChartAxisUnitsEnum::Enum getAxisUnits() const; void setAxisUnits(const ChartAxisUnitsEnum::Enum axisUnits); AString getAxisUnitsSuffix() const; int32_t getLabelFontSize() const; void setLabelFontSize(const float fontSize); bool isAutoRangeScaleEnabled() const; void setAutoRangeScaleEnabled(const bool autoRangeScaleEnabled); bool isVisible() const; void setVisible(const bool visible); /** * Update for auto range scale. */ virtual void updateForAutoRangeScale() = 0; virtual SceneClass* saveToScene(const SceneAttributes* sceneAttributes, const AString& instanceName); virtual void restoreFromScene(const SceneAttributes* sceneAttributes, const SceneClass* sceneClass); protected: ChartAxis(const ChartAxisTypeEnum::Enum axisType, const ChartAxisLocationEnum::Enum axisLocation); ChartModel* getParentChartModel(); const ChartModel* getParentChartModel() const; virtual void saveSubClassDataToScene(const SceneAttributes* sceneAttributes, SceneClass* sceneClass) = 0; virtual void restoreSubClassDataFromScene(const SceneAttributes* sceneAttributes, const SceneClass* sceneClass) = 0; private: void copyHelperChartAxis(const ChartAxis& obj); public: // ADD_NEW_METHODS_HERE virtual AString toString() const; private: void initializeMembersChartAxis(); ChartAxisTypeEnum::Enum m_axisType; ChartAxisLocationEnum::Enum m_axisLocation; ChartModel* m_parentChartModel; AString m_text; ChartAxisUnitsEnum::Enum m_axisUnits; int32_t m_labelFontSize; bool m_visible; bool m_autoRangeScaleEnabled; /** helps with scene save/restore */ SceneClassAssistant* m_sceneAssistant; // ADD_NEW_MEMBERS_HERE }; #ifdef __CHART_AXIS_DECLARE__ // #endif // __CHART_AXIS_DECLARE__ } // namespace #endif //__CHART_AXIS_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Charting/ChartAxisCartesian.cxx000066400000000000000000000442561300200146000265620ustar00rootroot00000000000000 /*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_AXIS_CARTESIAN_DECLARE__ #include "ChartAxisCartesian.h" #undef __CHART_AXIS_CARTESIAN_DECLARE__ #include "CaretAssert.h" #include "CaretLogger.h" #include "ChartModelCartesian.h" #include "ChartScaleAutoRanging.h" #include "EventAlertUser.h" #include "EventManager.h" #include "MathFunctions.h" #include "SceneClass.h" #include "SceneClassAssistant.h" using namespace caret; /** * \class caret::ChartAxisCartesian * \brief Axes for Cartesian Data. * \ingroup Charting */ /** * Constructor. * * @param axisLocation. * Axis location. */ ChartAxisCartesian::ChartAxisCartesian(const ChartAxisLocationEnum::Enum axisLocation) : ChartAxis(ChartAxisTypeEnum::CHART_AXIS_TYPE_CARTESIAN, axisLocation) { initializeMembersChartAxisCartesian(); } /** * Destructor. */ ChartAxisCartesian::~ChartAxisCartesian() { delete m_sceneAssistant; } /** * Copy constructor. * @param obj * Object that is copied. */ ChartAxisCartesian::ChartAxisCartesian(const ChartAxisCartesian& obj) : ChartAxis(obj) { initializeMembersChartAxisCartesian(); this->copyHelperChartAxisCartesian(obj); } /** * Assignment operator. * @param obj * Data copied from obj to this. * @return * Reference to this object. */ ChartAxisCartesian& ChartAxisCartesian::operator=(const ChartAxisCartesian& obj) { if (this != &obj) { ChartAxis::operator=(obj); this->copyHelperChartAxisCartesian(obj); } return *this; } /** * Helps with copying an object of this type. * @param obj * Object that is copied. */ void ChartAxisCartesian::copyHelperChartAxisCartesian(const ChartAxisCartesian& obj) { m_maximumValue = obj.m_maximumValue; m_minimumValue = obj.m_minimumValue; m_digitsRightOfDecimal = obj.m_digitsRightOfDecimal; } /** * Initialize class members. */ void ChartAxisCartesian::initializeMembersChartAxisCartesian() { m_minimumValue = 0.0; m_maximumValue = 1.0; m_digitsRightOfDecimal = 1; m_axisLabelsMinimumValue = 0.0; m_axisLabelsMaximumValue = 1.0; m_sceneAssistant = new SceneClassAssistant(); m_sceneAssistant->add("m_minimumValue", &m_minimumValue); m_sceneAssistant->add("m_maximumValue", &m_maximumValue); m_sceneAssistant->add("m_digitsRightOfDecimal", &m_digitsRightOfDecimal); m_sceneAssistant->add("m_axisLabelsMinimumValue", &m_axisLabelsMinimumValue); m_sceneAssistant->add("m_axisLabelsMaximumValue", &m_axisLabelsMaximumValue); } /** * @return Minimum value for axis. */ float ChartAxisCartesian::getMinimumValue() const { return m_minimumValue; } /** * Set minimum value for axis. * * @param minimumValue * New minimum value for axis. */ void ChartAxisCartesian::setMinimumValue(const float minimumValue) { m_minimumValue = minimumValue; } /** * @return Maximum value for axis. */ float ChartAxisCartesian::getMaximumValue() const { return m_maximumValue; } /** * Set maximum value for axis. * * @param maximumValue * New maximum value for axis. */ void ChartAxisCartesian::setMaximumValue(const float maximumValue) { m_maximumValue = maximumValue; } /** * Update for auto range scale. */ void ChartAxisCartesian::updateForAutoRangeScale() { if (isAutoRangeScaleEnabled()) { const ChartModel* chartModel = getParentChartModel(); CaretAssert(chartModel); const ChartModelCartesian* chartModelCartesian = dynamic_cast(chartModel); float minX, maxX, minY, maxY; chartModelCartesian->getBounds(minX, maxX, minY, maxY); float minValue = m_minimumValue; float maxValue = m_maximumValue; switch (getAxisLocation()) { case ChartAxisLocationEnum::CHART_AXIS_LOCATION_BOTTOM: minValue = minX; maxValue = maxX; break; case ChartAxisLocationEnum::CHART_AXIS_LOCATION_LEFT: minValue = minY; maxValue = maxY; break; case ChartAxisLocationEnum::CHART_AXIS_LOCATION_RIGHT: minValue = minY; maxValue = maxY; break; case ChartAxisLocationEnum::CHART_AXIS_LOCATION_TOP: minValue = minX; maxValue = maxX; break; } m_minimumValue = minValue; m_maximumValue = maxValue; } double scaleStep = 0.0; double scaleMin = 0.0; double scaleMax = 0.0; int32_t digitsRightOfDecimal = 0; ChartScaleAutoRanging::createAutoScale(m_minimumValue, m_maximumValue, scaleMin, scaleMax, scaleStep, digitsRightOfDecimal); m_axisLabelsMinimumValue = scaleMin; m_axisLabelsMaximumValue = scaleMax; m_axisLabelsStepValue = scaleStep; m_digitsRightOfDecimal = digitsRightOfDecimal; /** * Use auto-scaled range for left and right axis */ if (isAutoRangeScaleEnabled()) { switch (getAxisLocation()) { case ChartAxisLocationEnum::CHART_AXIS_LOCATION_BOTTOM: break; case ChartAxisLocationEnum::CHART_AXIS_LOCATION_LEFT: m_minimumValue = m_axisLabelsMinimumValue; m_maximumValue = m_axisLabelsMaximumValue; break; case ChartAxisLocationEnum::CHART_AXIS_LOCATION_RIGHT: m_minimumValue = m_axisLabelsMinimumValue; m_maximumValue = m_axisLabelsMaximumValue; break; case ChartAxisLocationEnum::CHART_AXIS_LOCATION_TOP: break; } } } /** * Get the axis labels and their positions for drawing the scale. * * @param axisLengthInPixels * Length of axis in pixels. * @param fontSizeInPixels * Size of the font in pixels. * @param labelOffsetInPixelsOut * Output containing offset in pixels for the scale labels. * @param labelTextOut * Output containing text for scale labels. */ void ChartAxisCartesian::getLabelsAndPositions(const float axisLengthInPixels, const float /*fontSizeInPixels*/, std::vector& labelOffsetInPixelsOut, std::vector& labelTextOut) { labelOffsetInPixelsOut.clear(); labelTextOut.clear(); if (axisLengthInPixels < 25.0) { return; } updateForAutoRangeScale(); float dataStart = m_minimumValue; float dataEnd = m_maximumValue; float dataRange = (m_maximumValue - m_minimumValue); if (dataRange <= 0.0) { return; } float labelsStart = m_axisLabelsMinimumValue; float labelsEnd = m_axisLabelsMaximumValue; /* * 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 ChartModel* chartModel = getParentChartModel(); CaretAssert(chartModel); const ChartModelCartesian* chartModelCartesian = dynamic_cast(chartModel); static std::set invalidChartModelCartesians; if (invalidChartModelCartesians.find(chartModelCartesian) == invalidChartModelCartesians.end()) { invalidChartModelCartesians.insert(chartModelCartesian); 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."); EventManager::get()->sendEvent(EventAlertUser(msg).getPointer()); } return; } float labelsRange = (m_axisLabelsMaximumValue - m_axisLabelsMinimumValue); if (labelsRange <= 0.0) { return; } const float tickLabelsStep = m_axisLabelsStepValue; if (tickLabelsStep <= 0.0) { return; } float labelValue = labelsStart; while (labelValue <= labelsEnd) { float labelParametricValue = (labelValue - dataStart) / dataRange; float labelValueForText = labelValue; if (dataRange >= 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) - dataStart) / dataRange; if (nextParametricValue > 0.05) { labelParametricValue = 0.0; labelValueForText = dataStart; } } } 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) - dataStart) / dataRange; if (prevParametricValue < 0.95) { labelParametricValue = 1.0; labelValueForText = dataEnd; } } } if (labelParametricValue > 1.0) { if (labelParametricValue < 1.01) { labelParametricValue = 1.0; } } } if ((labelParametricValue >= 0.0) && (labelParametricValue <= 1.0)) { const float labelPixelsPosition = axisLengthInPixels * labelParametricValue; const AString labelText = AString::number(labelValueForText, 'f', m_digitsRightOfDecimal); labelOffsetInPixelsOut.push_back(labelPixelsPosition); labelTextOut.push_back(labelText); } else { // std::cout << "Label value=" << labelValue << " parametric=" << labelParametricValue << " failed." << std::endl; } labelValue += tickLabelsStep; } } // 29 April 2014 ///** // * Get the axis labels and their positions for drawing the scale. // * // * @param axisLengthInPixels // * Length of axis in pixels. // * @param fontSizeInPixels // * Size of the font in pixels. // * @param labelOffsetInPixelsOut // * Output containing offset in pixels for the scale labels. // * @param labelTextOut // * Output containing text for scale labels. // */ //void //ChartAxisCartesian::getLabelsAndPositions(const float axisLengthInPixels, // const float /*fontSizeInPixels*/, // std::vector& labelOffsetInPixelsOut, // std::vector& labelTextOut) //{ // labelOffsetInPixelsOut.clear(); // labelTextOut.clear(); // // if (axisLengthInPixels < 25.0) { // return; // } // // updateForAutoRangeScale(); // // const float numberOfTicks = 5; // // float dataStart = m_minimumValue; // float dataEnd = m_maximumValue; // float dataRange = (m_maximumValue - m_minimumValue); // if (dataRange <= 0.0) { // return; // } // // float labelsStart = m_axisLabelsMinimumValue; // float labelsRange = (m_axisLabelsMaximumValue - m_axisLabelsMinimumValue); // if (labelsRange <= 0.0) { // return; // } // const float tickLabelsStep = labelsRange / numberOfTicks; // if (tickLabelsStep <= 0.0) { // return; // } // // float labelValue = labelsStart; // for (int32_t i = 0; i <= numberOfTicks; i++) { // float labelParametricValue = (labelValue - dataStart) / dataRange; // // float labelValueForText = labelValue; // // if (dataRange >= 10.0) { // if (i == 0) { // /* // * 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) - dataStart) / dataRange; // if (nextParametricValue > 0.05) { // labelParametricValue = 0.0; // labelValueForText = dataStart; // } // } // } // // if (labelParametricValue < 0.0) { // if (labelParametricValue >= -0.01) { // labelParametricValue = 0.0; // } // } // // if (i == numberOfTicks) { // /* // * Like above, ensures a value is displayed at the right // * edge of the axis. // */ // if (labelParametricValue > 1.0) { // const float prevParametricValue = ((labelValue - tickLabelsStep) - dataStart) / dataRange; // if (prevParametricValue < 0.95) { // labelParametricValue = 1.0; // labelValueForText = dataEnd; // } // } // } // // if (labelParametricValue > 1.0) { // if (labelParametricValue < 1.01) { // labelParametricValue = 1.0; // } // } // } // // if ((labelParametricValue >= 0.0) // && (labelParametricValue <= 1.0)) { // const float labelPixelsPosition = axisLengthInPixels * labelParametricValue; // const AString labelText = AString::number(labelValueForText, 'f', m_digitsRightOfDecimal); // // labelOffsetInPixelsOut.push_back(labelPixelsPosition); // labelTextOut.push_back(labelText); // } // else { //// std::cout << "Label value=" << labelValue << " parametric=" << labelParametricValue << " failed." << std::endl; // } // // labelValue += tickLabelsStep; // } //} /** * 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. */ ChartAxis* ChartAxisCartesian::clone() const { ChartAxisCartesian* cloneCopy = new ChartAxisCartesian(*this); return cloneCopy; } /** * 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 ChartAxisCartesian::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 ChartAxisCartesian::restoreSubClassDataFromScene(const SceneAttributes* sceneAttributes, const SceneClass* sceneClass) { m_sceneAssistant->restoreMembers(sceneAttributes, sceneClass); } connectome-workbench-1.2.3+git41-gc4c6c90/src/Charting/ChartAxisCartesian.h000066400000000000000000000060621300200146000262000ustar00rootroot00000000000000#ifndef __CHART_AXIS_CARTESIAN_H__ #define __CHART_AXIS_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 "ChartAxis.h" namespace caret { class ChartModelCartesian; class ChartAxisCartesian : public ChartAxis { public: virtual ~ChartAxisCartesian(); ChartAxisCartesian(const ChartAxisCartesian& obj); ChartAxisCartesian& operator=(const ChartAxisCartesian& obj); virtual ChartAxis* clone() const; float getMinimumValue() const; void setMinimumValue(const float minimumValue); float getMaximumValue() const; void setMaximumValue(const float maximumValue); void getLabelsAndPositions(const float axisLengthInPixels, const float fontSizeInPixels, std::vector& labelOffsetInPixelsOut, std::vector& labelTextOut); // ADD_NEW_METHODS_HERE protected: ChartAxisCartesian(const ChartAxisLocationEnum::Enum axisLocation); virtual void updateForAutoRangeScale(); virtual void saveSubClassDataToScene(const SceneAttributes* sceneAttributes, SceneClass* sceneClass); virtual void restoreSubClassDataFromScene(const SceneAttributes* sceneAttributes, const SceneClass* sceneClass); private: void copyHelperChartAxisCartesian(const ChartAxisCartesian& obj); void initializeMembersChartAxisCartesian(); SceneClassAssistant* m_sceneAssistant; mutable float m_maximumValue; mutable float m_minimumValue; mutable int32_t m_digitsRightOfDecimal; mutable float m_axisLabelsMinimumValue; mutable float m_axisLabelsMaximumValue; mutable float m_axisLabelsStepValue; // ADD_NEW_MEMBERS_HERE friend class ChartAxis; }; #ifdef __CHART_AXIS_CARTESIAN_DECLARE__ // #endif // __CHART_AXIS_CARTESIAN_DECLARE__ } // namespace #endif //__CHART_AXIS_CARTESIAN_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Charting/ChartAxisLocationEnum.cxx000066400000000000000000000255461300200146000272470ustar00rootroot00000000000000 /*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_AXIS_LOCATION_ENUM_DECLARE__ #include "ChartAxisLocationEnum.h" #undef __CHART_AXIS_LOCATION_ENUM_DECLARE__ #include "CaretAssert.h" using namespace caret; /** * \class caret::ChartAxisLocationEnum * \brief Location of a chart axis. * * 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_chartAxisLocationEnumComboBox; * * Declare a slot that is called when user changes selection * private slots: * void chartAxisLocationEnumComboBoxItemActivated(); * * Implementation File (.cxx) * Include the header files * #include "EnumComboBoxTemplate.h" * #include "ChartAxisLocationEnum.h" * * Instatiate: * m_chartAxisLocationEnumComboBox = new EnumComboBoxTemplate(this); * m_chartAxisLocationEnumComboBox->setup(); * * Get notified when the user changes the selection: * QObject::connect(m_chartAxisLocationEnumComboBox, SIGNAL(itemActivated()), * this, SLOT(chartAxisLocationEnumComboBoxItemActivated())); * * Update the selection: * m_chartAxisLocationEnumComboBox->setSelectedItem(NEW_VALUE); * * Read the selection: * const ChartAxisLocationEnum::Enum VARIABLE = m_chartAxisLocationEnumComboBox->getSelectedItem(); * */ /** * Constructor. * * @param enumValue * An enumerated value. * @param name * Name of enumerated value. * * @param guiName * User-friendly name for use in user-interface. */ ChartAxisLocationEnum::ChartAxisLocationEnum(const Enum enumValue, const AString& name, const AString& guiName) { this->enumValue = enumValue; this->integerCode = integerCodeCounter++; this->name = name; this->guiName = guiName; } /** * Destructor. */ ChartAxisLocationEnum::~ChartAxisLocationEnum() { } /** * Initialize the enumerated metadata. */ void ChartAxisLocationEnum::initialize() { if (initializedFlag) { return; } initializedFlag = true; enumData.push_back(ChartAxisLocationEnum(CHART_AXIS_LOCATION_BOTTOM, "CHART_AXIS_LOCATION_BOTTOM", "Bottom")); enumData.push_back(ChartAxisLocationEnum(CHART_AXIS_LOCATION_LEFT, "CHART_AXIS_LOCATION_LEFT", "Left")); enumData.push_back(ChartAxisLocationEnum(CHART_AXIS_LOCATION_RIGHT, "CHART_AXIS_LOCATION_RIGHT", "Right")); enumData.push_back(ChartAxisLocationEnum(CHART_AXIS_LOCATION_TOP, "CHART_AXIS_LOCATION_TOP", "Top")); } /** * 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 ChartAxisLocationEnum* ChartAxisLocationEnum::findData(const Enum enumValue) { if (initializedFlag == false) initialize(); size_t num = enumData.size(); for (size_t i = 0; i < num; i++) { const ChartAxisLocationEnum* 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 ChartAxisLocationEnum::toName(Enum enumValue) { if (initializedFlag == false) initialize(); const ChartAxisLocationEnum* 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. */ ChartAxisLocationEnum::Enum ChartAxisLocationEnum::fromName(const AString& name, bool* isValidOut) { if (initializedFlag == false) initialize(); bool validFlag = false; Enum enumValue = ChartAxisLocationEnum::enumData[0].enumValue; for (std::vector::iterator iter = enumData.begin(); iter != enumData.end(); iter++) { const ChartAxisLocationEnum& 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 ChartAxisLocationEnum")); } return enumValue; } /** * Get a GUI string representation of the enumerated type. * @param enumValue * Enumerated value. * @return * String representing enumerated value. */ AString ChartAxisLocationEnum::toGuiName(Enum enumValue) { if (initializedFlag == false) initialize(); const ChartAxisLocationEnum* 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. */ ChartAxisLocationEnum::Enum ChartAxisLocationEnum::fromGuiName(const AString& guiName, bool* isValidOut) { if (initializedFlag == false) initialize(); bool validFlag = false; Enum enumValue = ChartAxisLocationEnum::enumData[0].enumValue; for (std::vector::iterator iter = enumData.begin(); iter != enumData.end(); iter++) { const ChartAxisLocationEnum& 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 ChartAxisLocationEnum")); } return enumValue; } /** * Get the integer code for a data type. * * @return * Integer code for data type. */ int32_t ChartAxisLocationEnum::toIntegerCode(Enum enumValue) { if (initializedFlag == false) initialize(); const ChartAxisLocationEnum* 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. */ ChartAxisLocationEnum::Enum ChartAxisLocationEnum::fromIntegerCode(const int32_t integerCode, bool* isValidOut) { if (initializedFlag == false) initialize(); bool validFlag = false; Enum enumValue = ChartAxisLocationEnum::enumData[0].enumValue; for (std::vector::iterator iter = enumData.begin(); iter != enumData.end(); iter++) { const ChartAxisLocationEnum& 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 ChartAxisLocationEnum")); } 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 ChartAxisLocationEnum::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 ChartAxisLocationEnum::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(ChartAxisLocationEnum::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 ChartAxisLocationEnum::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(ChartAxisLocationEnum::toGuiName(iter->enumValue)); } if (isSorted) { std::sort(allGuiNames.begin(), allGuiNames.end()); } } connectome-workbench-1.2.3+git41-gc4c6c90/src/Charting/ChartAxisLocationEnum.h000066400000000000000000000063631300200146000266700ustar00rootroot00000000000000#ifndef __CHART_AXIS_LOCATION_ENUM_H__ #define __CHART_AXIS_LOCATION_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 ChartAxisLocationEnum { public: /** * Enumerated values. */ enum Enum { /** Axis at bottom */ CHART_AXIS_LOCATION_BOTTOM, /** Axis at left */ CHART_AXIS_LOCATION_LEFT, /** Axis at right */ CHART_AXIS_LOCATION_RIGHT, /** Axis at top */ CHART_AXIS_LOCATION_TOP }; ~ChartAxisLocationEnum(); 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: ChartAxisLocationEnum(const Enum enumValue, const AString& name, const AString& guiName); static const ChartAxisLocationEnum* 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_AXIS_LOCATION_ENUM_DECLARE__ std::vector ChartAxisLocationEnum::enumData; bool ChartAxisLocationEnum::initializedFlag = false; int32_t ChartAxisLocationEnum::integerCodeCounter = 0; #endif // __CHART_AXIS_LOCATION_ENUM_DECLARE__ } // namespace #endif //__CHART_AXIS_LOCATION_ENUM_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Charting/ChartAxisTypeEnum.cxx000066400000000000000000000243621300200146000264130ustar00rootroot00000000000000 /*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_AXIS_TYPE_ENUM_DECLARE__ #include "ChartAxisTypeEnum.h" #undef __CHART_AXIS_TYPE_ENUM_DECLARE__ #include "CaretAssert.h" using namespace caret; /** * \class caret::ChartAxisTypeEnum * \brief Type for a chart axis. * * 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_chartAxisTypeEnumComboBox; * * Declare a slot that is called when user changes selection * private slots: * void chartAxisTypeEnumComboBoxItemActivated(); * * Implementation File (.cxx) * Include the header files * #include "EnumComboBoxTemplate.h" * #include "ChartAxisTypeEnum.h" * * Instatiate: * m_chartAxisTypeEnumComboBox = new EnumComboBoxTemplate(this); * m_chartAxisTypeEnumComboBox->setup(); * * Get notified when the user changes the selection: * QObject::connect(m_chartAxisTypeEnumComboBox, SIGNAL(itemActivated()), * this, SLOT(chartAxisTypeEnumComboBoxItemActivated())); * * Update the selection: * m_chartAxisTypeEnumComboBox->setSelectedItem(NEW_VALUE); * * Read the selection: * const ChartAxisTypeEnum::Enum VARIABLE = m_chartAxisTypeEnumComboBox->getSelectedItem(); * */ /** * Constructor. * * @param enumValue * An enumerated value. * @param name * Name of enumerated value. * * @param guiName * User-friendly name for use in user-interface. */ ChartAxisTypeEnum::ChartAxisTypeEnum(const Enum enumValue, const AString& name, const AString& guiName) { this->enumValue = enumValue; this->integerCode = integerCodeCounter++; this->name = name; this->guiName = guiName; } /** * Destructor. */ ChartAxisTypeEnum::~ChartAxisTypeEnum() { } /** * Initialize the enumerated metadata. */ void ChartAxisTypeEnum::initialize() { if (initializedFlag) { return; } initializedFlag = true; enumData.push_back(ChartAxisTypeEnum(CHART_AXIS_TYPE_NONE, "CHART_AXIS_TYPE_NONE", "None Axis")); enumData.push_back(ChartAxisTypeEnum(CHART_AXIS_TYPE_CARTESIAN, "CHART_AXIS_TYPE_CARTESIAN", "Cartesian Axis")); } /** * 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 ChartAxisTypeEnum* ChartAxisTypeEnum::findData(const Enum enumValue) { if (initializedFlag == false) initialize(); size_t num = enumData.size(); for (size_t i = 0; i < num; i++) { const ChartAxisTypeEnum* 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 ChartAxisTypeEnum::toName(Enum enumValue) { if (initializedFlag == false) initialize(); const ChartAxisTypeEnum* 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. */ ChartAxisTypeEnum::Enum ChartAxisTypeEnum::fromName(const AString& name, bool* isValidOut) { if (initializedFlag == false) initialize(); bool validFlag = false; Enum enumValue = ChartAxisTypeEnum::enumData[0].enumValue; for (std::vector::iterator iter = enumData.begin(); iter != enumData.end(); iter++) { const ChartAxisTypeEnum& 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 ChartAxisTypeEnum")); } return enumValue; } /** * Get a GUI string representation of the enumerated type. * @param enumValue * Enumerated value. * @return * String representing enumerated value. */ AString ChartAxisTypeEnum::toGuiName(Enum enumValue) { if (initializedFlag == false) initialize(); const ChartAxisTypeEnum* 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. */ ChartAxisTypeEnum::Enum ChartAxisTypeEnum::fromGuiName(const AString& guiName, bool* isValidOut) { if (initializedFlag == false) initialize(); bool validFlag = false; Enum enumValue = ChartAxisTypeEnum::enumData[0].enumValue; for (std::vector::iterator iter = enumData.begin(); iter != enumData.end(); iter++) { const ChartAxisTypeEnum& 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 ChartAxisTypeEnum")); } return enumValue; } /** * Get the integer code for a data type. * * @return * Integer code for data type. */ int32_t ChartAxisTypeEnum::toIntegerCode(Enum enumValue) { if (initializedFlag == false) initialize(); const ChartAxisTypeEnum* 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. */ ChartAxisTypeEnum::Enum ChartAxisTypeEnum::fromIntegerCode(const int32_t integerCode, bool* isValidOut) { if (initializedFlag == false) initialize(); bool validFlag = false; Enum enumValue = ChartAxisTypeEnum::enumData[0].enumValue; for (std::vector::iterator iter = enumData.begin(); iter != enumData.end(); iter++) { const ChartAxisTypeEnum& 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 ChartAxisTypeEnum")); } 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 ChartAxisTypeEnum::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 ChartAxisTypeEnum::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(ChartAxisTypeEnum::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 ChartAxisTypeEnum::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(ChartAxisTypeEnum::toGuiName(iter->enumValue)); } if (isSorted) { std::sort(allGuiNames.begin(), allGuiNames.end()); } } connectome-workbench-1.2.3+git41-gc4c6c90/src/Charting/ChartAxisTypeEnum.h000066400000000000000000000060651300200146000260400ustar00rootroot00000000000000#ifndef __CHART_AXIS_TYPE_ENUM_H__ #define __CHART_AXIS_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 ChartAxisTypeEnum { public: /** * Enumerated values. */ enum Enum { /** No Type */ CHART_AXIS_TYPE_NONE, /** Cartesian Data */ CHART_AXIS_TYPE_CARTESIAN }; ~ChartAxisTypeEnum(); 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: ChartAxisTypeEnum(const Enum enumValue, const AString& name, const AString& guiName); static const ChartAxisTypeEnum* 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_AXIS_TYPE_ENUM_DECLARE__ std::vector ChartAxisTypeEnum::enumData; bool ChartAxisTypeEnum::initializedFlag = false; int32_t ChartAxisTypeEnum::integerCodeCounter = 0; #endif // __CHART_AXIS_TYPE_ENUM_DECLARE__ } // namespace #endif //__CHART_AXIS_TYPE_ENUM_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Charting/ChartAxisUnitsEnum.cxx000066400000000000000000000250411300200146000265670ustar00rootroot00000000000000 /*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_AXIS_UNITS_ENUM_DECLARE__ #include "ChartAxisUnitsEnum.h" #undef __CHART_AXIS_UNITS_ENUM_DECLARE__ #include "CaretAssert.h" using namespace caret; /** * \class caret::ChartAxisUnitsEnum * \brief Units for chart axes * * 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_chartDataAxisUnitsEnumComboBox; * * Declare a slot that is called when user changes selection * private slots: * void chartDataAxisUnitsEnumComboBoxItemActivated(); * * Implementation File (.cxx) * Include the header files * #include "EnumComboBoxTemplate.h" * #include "ChartAxisUnitsEnum.h" * * Instatiate: * m_chartDataAxisUnitsEnumComboBox = new EnumComboBoxTemplate(this); * m_chartDataAxisUnitsEnumComboBox->setup(); * * Get notified when the user changes the selection: * QObject::connect(m_chartDataAxisUnitsEnumComboBox, SIGNAL(itemActivated()), * this, SLOT(chartDataAxisUnitsEnumComboBoxItemActivated())); * * Update the selection: * m_chartDataAxisUnitsEnumComboBox->setSelectedItem(NEW_VALUE); * * Read the selection: * const ChartAxisUnitsEnum::Enum VARIABLE = m_chartDataAxisUnitsEnumComboBox->getSelectedItem(); * */ /** * Constructor. * * @param enumValue * An enumerated value. * @param name * Name of enumerated value. * * @param guiName * User-friendly name for use in user-interface. */ ChartAxisUnitsEnum::ChartAxisUnitsEnum(const Enum enumValue, const AString& name, const AString& guiName) { this->enumValue = enumValue; this->integerCode = integerCodeCounter++; this->name = name; this->guiName = guiName; } /** * Destructor. */ ChartAxisUnitsEnum::~ChartAxisUnitsEnum() { } /** * Initialize the enumerated metadata. */ void ChartAxisUnitsEnum::initialize() { if (initializedFlag) { return; } initializedFlag = true; enumData.push_back(ChartAxisUnitsEnum(CHART_AXIS_UNITS_NONE, "CHART_AXIS_UNITS_NONE", "None")); enumData.push_back(ChartAxisUnitsEnum(CHART_AXIS_UNITS_FREQUENCY_HERTZ, "CHART_AXIS_UNITS_FREQUENCY_HERTZ", "Frequency")); enumData.push_back(ChartAxisUnitsEnum(CHART_AXIS_UNITS_TIME_SECONDS, "CHART_AXIS_UNITS_TIME_SECONDS", "Time")); } /** * 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 ChartAxisUnitsEnum* ChartAxisUnitsEnum::findData(const Enum enumValue) { if (initializedFlag == false) initialize(); size_t num = enumData.size(); for (size_t i = 0; i < num; i++) { const ChartAxisUnitsEnum* 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 ChartAxisUnitsEnum::toName(Enum enumValue) { if (initializedFlag == false) initialize(); const ChartAxisUnitsEnum* 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. */ ChartAxisUnitsEnum::Enum ChartAxisUnitsEnum::fromName(const AString& name, bool* isValidOut) { if (initializedFlag == false) initialize(); bool validFlag = false; Enum enumValue = ChartAxisUnitsEnum::enumData[0].enumValue; for (std::vector::iterator iter = enumData.begin(); iter != enumData.end(); iter++) { const ChartAxisUnitsEnum& 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 ChartAxisUnitsEnum")); } return enumValue; } /** * Get a GUI string representation of the enumerated type. * @param enumValue * Enumerated value. * @return * String representing enumerated value. */ AString ChartAxisUnitsEnum::toGuiName(Enum enumValue) { if (initializedFlag == false) initialize(); const ChartAxisUnitsEnum* 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. */ ChartAxisUnitsEnum::Enum ChartAxisUnitsEnum::fromGuiName(const AString& guiName, bool* isValidOut) { if (initializedFlag == false) initialize(); bool validFlag = false; Enum enumValue = ChartAxisUnitsEnum::enumData[0].enumValue; for (std::vector::iterator iter = enumData.begin(); iter != enumData.end(); iter++) { const ChartAxisUnitsEnum& 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 ChartAxisUnitsEnum")); } return enumValue; } /** * Get the integer code for a data type. * * @return * Integer code for data type. */ int32_t ChartAxisUnitsEnum::toIntegerCode(Enum enumValue) { if (initializedFlag == false) initialize(); const ChartAxisUnitsEnum* 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. */ ChartAxisUnitsEnum::Enum ChartAxisUnitsEnum::fromIntegerCode(const int32_t integerCode, bool* isValidOut) { if (initializedFlag == false) initialize(); bool validFlag = false; Enum enumValue = ChartAxisUnitsEnum::enumData[0].enumValue; for (std::vector::iterator iter = enumData.begin(); iter != enumData.end(); iter++) { const ChartAxisUnitsEnum& 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 ChartAxisUnitsEnum")); } 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 ChartAxisUnitsEnum::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 ChartAxisUnitsEnum::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(ChartAxisUnitsEnum::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 ChartAxisUnitsEnum::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(ChartAxisUnitsEnum::toGuiName(iter->enumValue)); } if (isSorted) { std::sort(allGuiNames.begin(), allGuiNames.end()); } } connectome-workbench-1.2.3+git41-gc4c6c90/src/Charting/ChartAxisUnitsEnum.h000066400000000000000000000062161300200146000262170ustar00rootroot00000000000000#ifndef __CHART_AXIS_UNITS_ENUM_H__ #define __CHART_AXIS_UNITS_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 ChartAxisUnitsEnum { public: /** * Enumerated values. */ enum Enum { /** No units */ CHART_AXIS_UNITS_NONE, /** Frequency units */ CHART_AXIS_UNITS_FREQUENCY_HERTZ, /** Time units */ CHART_AXIS_UNITS_TIME_SECONDS }; ~ChartAxisUnitsEnum(); 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: ChartAxisUnitsEnum(const Enum enumValue, const AString& name, const AString& guiName); static const ChartAxisUnitsEnum* 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_AXIS_UNITS_ENUM_DECLARE__ std::vector ChartAxisUnitsEnum::enumData; bool ChartAxisUnitsEnum::initializedFlag = false; int32_t ChartAxisUnitsEnum::integerCodeCounter = 0; #endif // __CHART_AXIS_UNITS_ENUM_DECLARE__ } // namespace #endif //__CHART_AXIS_UNITS_ENUM_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Charting/ChartData.cxx000066400000000000000000000213301300200146000246610ustar00rootroot00000000000000 /*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_DATA_DECLARE__ #include "ChartData.h" #undef __CHART_DATA_DECLARE__ #include "CaretAssert.h" #include "ChartDataCartesian.h" #include "ChartDataSource.h" #include "SceneClass.h" #include "SceneClassAssistant.h" #include "SystemUtilities.h" using namespace caret; /** * \class caret::ChartData * \brief Base class for chart data. * \ingroup Charting */ /** * Constructor. * * @param chartDataType * Type of chart model. */ ChartData::ChartData(const ChartDataTypeEnum::Enum chartDataType) : CaretObject(), SceneableInterface(), m_chartDataType(chartDataType) { initializeMembersChartData(); } /** * Destructor. */ ChartData::~ChartData() { delete m_sceneAssistant; delete m_chartDataSource; } /** * Initialize members of a new instance. */ void ChartData::initializeMembersChartData() { m_chartDataSource = new ChartDataSource(); for (int32_t i = 0; i < BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS; i++) { m_selectionStatus[i] = true; } m_uniqueIdentifier = SystemUtilities::createUniqueID(); m_sceneAssistant = new SceneClassAssistant(); m_sceneAssistant->add("m_chartDataSource", "ChartDataSource", m_chartDataSource); m_sceneAssistant->addTabIndexedBooleanArray("m_selectionStatus", m_selectionStatus); m_sceneAssistant->add("m_uniqueIdentifier", &m_uniqueIdentifier); } /** * Copy constructor. * @param obj * Object that is copied. */ ChartData::ChartData(const ChartData& obj) : CaretObject(obj), SceneableInterface(obj), m_chartDataType(obj.m_chartDataType) { initializeMembersChartData(); this->copyHelperChartData(obj); } /** * Assignment operator. * @param obj * Data copied from obj to this. * @return * Reference to this object. */ ChartData& ChartData::operator=(const ChartData& obj) { if (this != &obj) { CaretObject::operator=(obj); this->copyHelperChartData(obj); } return *this; } /** * Helps with copying an object of this type. * @param obj * Object that is copied. */ void ChartData::copyHelperChartData(const ChartData& obj) { CaretAssert(0); m_chartDataType = obj.m_chartDataType; *m_chartDataSource = *obj.m_chartDataSource; for (int32_t i = 0; i < BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS; i++) { m_selectionStatus[i] = obj.m_selectionStatus[i]; } } /** * Create a new instance of a chart for the given data type. * * @param chartDataType * Type of chart data. * @return * Pointer to new instance. */ ChartData* ChartData::newChartDataForChartDataType(const ChartDataTypeEnum::Enum chartDataType) { ChartData* chartData = NULL; switch (chartDataType) { case ChartDataTypeEnum::CHART_DATA_TYPE_INVALID: CaretAssert(0); break; case ChartDataTypeEnum::CHART_DATA_TYPE_MATRIX_LAYER: CaretAssert(0); break; case ChartDataTypeEnum::CHART_DATA_TYPE_MATRIX_SERIES: break; case ChartDataTypeEnum::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: chartData = new ChartDataCartesian(chartDataType, ChartAxisUnitsEnum::CHART_AXIS_UNITS_NONE, ChartAxisUnitsEnum::CHART_AXIS_UNITS_TIME_SECONDS); break; case ChartDataTypeEnum::CHART_DATA_TYPE_LINE_TIME_SERIES: chartData = new ChartDataCartesian(chartDataType, ChartAxisUnitsEnum::CHART_AXIS_UNITS_NONE, ChartAxisUnitsEnum::CHART_AXIS_UNITS_TIME_SECONDS); break; } return chartData; } /** * 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* ChartData::saveToScene(const SceneAttributes* sceneAttributes, const AString& instanceName) { SceneClass* sceneClass = new SceneClass(instanceName, "ChartData", 1); 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 ChartData::restoreFromScene(const SceneAttributes* sceneAttributes, const SceneClass* sceneClass) { if (sceneClass == NULL) { return; } m_sceneAssistant->restoreMembers(sceneAttributes, sceneClass); restoreSubClassDataFromScene(sceneAttributes, sceneClass); } /** * @return The chart data model type. */ ChartDataTypeEnum::Enum ChartData::getChartDataType() const { return m_chartDataType; } /** * @return The source of the chart data (const method). */ const ChartDataSource* ChartData::getChartDataSource() const { return m_chartDataSource; } /** * @return The source of the chart data. */ ChartDataSource* ChartData::getChartDataSource() { return m_chartDataSource; } /** * @return The selection status in the given tab * @param tabIndex * Index of the tab. */ bool ChartData::isSelected(const int32_t tabIndex) const { CaretAssertArrayIndex(m_selectionStatus, BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS, tabIndex); return m_selectionStatus[tabIndex]; } /** * Set the selection status. * * @param selectionStatus * New selection status. */ void ChartData::setSelected(const int32_t tabIndex, const bool selectionStatus) { CaretAssertArrayIndex(m_selectionStatus, BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS, tabIndex); m_selectionStatus[tabIndex] = selectionStatus; /* * When selection status is true, * notify parent. */ // if (m_selectionStatus[tabIndex]) { // if (m_parentChartModel != NULL) { // m_parentChartModel->childChartDataSelectionChanged(this); // } // } } /** * Copy the selection status for all tabs from the given chart data to me. * * @param copyFrom * Chart data from which selection status is copied. */ void ChartData::copySelectionStatusForAllTabs(const ChartData* copyFrom) { for (int32_t i = 0; i < BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS; i++) { m_selectionStatus[i] = copyFrom->m_selectionStatus[i]; } } /** * @return The Unique Identifier (UUID). */ AString ChartData::getUniqueIdentifier() const { return m_uniqueIdentifier; } /** * Set the unique identifier. * * @param uniqueIdentifier * The new unique identifier. */ void ChartData::setUniqueIdentifier(const AString& uniqueIdentifier) { m_uniqueIdentifier = uniqueIdentifier; } /** * Get a description of this object's content. * @return String describing this object's content. */ AString ChartData::toString() const { return "ChartData"; } connectome-workbench-1.2.3+git41-gc4c6c90/src/Charting/ChartData.h000066400000000000000000000116321300200146000243120ustar00rootroot00000000000000#ifndef __CHART_DATA_H__ #define __CHART_DATA_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 "BrainConstants.h" #include "CaretObject.h" #include "ChartDataTypeEnum.h" #include "SceneableInterface.h" namespace caret { class ChartDataSource; class SceneClassAssistant; class ChartData : public CaretObject, public SceneableInterface { public: static ChartData* newChartDataForChartDataType(const ChartDataTypeEnum::Enum chartDataType); virtual ~ChartData(); /** * At times a copy of chart data will be needed BUT it must be * the proper subclass so copy constructor and assignment operator * will 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. */ virtual ChartData* clone() const = 0; ChartDataTypeEnum::Enum getChartDataType() const; const ChartDataSource* getChartDataSource() const; ChartDataSource* getChartDataSource(); bool isSelected(const int32_t tabIndex) const; void setSelected(const int32_t tabIndex, const bool selectionStatus); void clearSelected(); AString getUniqueIdentifier() const; void setUniqueIdentifier(const AString& uniqueIdentifier); void copySelectionStatusForAllTabs(const ChartData* copyFrom); virtual SceneClass* saveToScene(const SceneAttributes* sceneAttributes, const AString& instanceName); virtual void restoreFromScene(const SceneAttributes* sceneAttributes, const SceneClass* sceneClass); // ADD_NEW_METHODS_HERE virtual AString toString() const; protected: ChartData(const ChartDataTypeEnum::Enum chartDataType); ChartData(const ChartData& obj); ChartData& operator=(const ChartData& obj); /** * Save subclass data to the scene. sceneClass * will be valid and any scene data should be added to it. * * @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. */ virtual void saveSubClassDataToScene(const SceneAttributes* sceneAttributes, SceneClass* sceneClass) = 0; /** * Restore file data from the scene. The scene class * will be valid and any scene data may be obtained from it. * * @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. */ virtual void restoreSubClassDataFromScene(const SceneAttributes* sceneAttributes, const SceneClass* sceneClass) = 0; private: void initializeMembersChartData(); void copyHelperChartData(const ChartData& obj); SceneClassAssistant* m_sceneAssistant; ChartDataTypeEnum::Enum m_chartDataType; ChartDataSource* m_chartDataSource; bool m_selectionStatus[BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS]; AString m_uniqueIdentifier; // ADD_NEW_MEMBERS_HERE }; #ifdef __CHART_DATA_DECLARE__ // #endif // __CHART_DATA_DECLARE__ } // namespace #endif //__CHART_DATA_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Charting/ChartDataCartesian.cxx000066400000000000000000000325331300200146000265220ustar00rootroot00000000000000 /*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_DATA_CARTESIAN_DECLARE__ #include "ChartDataCartesian.h" #undef __CHART_DATA_CARTESIAN_DECLARE__ #include #include #include "CaretAssert.h" #include "ChartPoint.h" #include "SceneClass.h" #include "SceneClassAssistant.h" using namespace caret; /** * \class caret::ChartDataCartesian * \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. */ ChartDataCartesian::ChartDataCartesian(const ChartDataTypeEnum::Enum chartDataType, const ChartAxisUnitsEnum::Enum dataAxisUnitsX, const ChartAxisUnitsEnum::Enum dataAxisUnitsY) : ChartData(chartDataType), m_dataAxisUnitsX(dataAxisUnitsX), m_dataAxisUnitsY(dataAxisUnitsY) { initializeMembersChartDataCartesian(); } /** * Destructor. */ ChartDataCartesian::~ChartDataCartesian() { removeAllPoints(); delete m_sceneAssistant; } /** * Initialize members of a new instance. */ void ChartDataCartesian::initializeMembersChartDataCartesian() { m_boundsValid = false; m_color = CaretColorEnum::RED; m_timeStartInSecondsAxisX = 0.0; m_timeStepInSecondsAxisX = 1.0; std::vector colorEnums; CaretColorEnum::getColorEnums(colorEnums); const int32_t numCaretColors = static_cast(colorEnums.size()); bool colorFound = false; while ( ! colorFound) { ChartDataCartesian::caretColorIndex++; if (ChartDataCartesian::caretColorIndex >= numCaretColors) { ChartDataCartesian::caretColorIndex = 0; } if (colorEnums[ChartDataCartesian::caretColorIndex] == CaretColorEnum::BLACK) { /* do not use black */ } else if (colorEnums[ChartDataCartesian::caretColorIndex] == CaretColorEnum::WHITE) { /* do not use white */ } else { m_color = colorEnums[ChartDataCartesian::caretColorIndex]; colorFound = true; } } m_sceneAssistant = new SceneClassAssistant(); 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_timeStartInSecondsAxisX", &m_timeStartInSecondsAxisX); m_sceneAssistant->add("m_timeStepInSecondsAxisX", &m_timeStepInSecondsAxisX); } /** * Remove all points in the model. */ void ChartDataCartesian::removeAllPoints() { for (std::vector::const_iterator iter = m_points.begin(); iter != m_points.end(); iter++) { delete *iter; } m_points.clear(); m_boundsValid = false; } /** * 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. */ ChartData* ChartDataCartesian::clone() const { ChartDataCartesian* cloneCopy = new ChartDataCartesian(*this); return cloneCopy; } /** * Copy constructor. * @param obj * Object that is copied. */ ChartDataCartesian::ChartDataCartesian(const ChartDataCartesian& obj) : ChartData(obj), m_dataAxisUnitsX(obj.m_dataAxisUnitsX), m_dataAxisUnitsY(obj.m_dataAxisUnitsY) { initializeMembersChartDataCartesian(); this->copyHelperChartDataCartesian(obj); } /** * Assignment operator. * @param obj * Data copied from obj to this. * @return * Reference to this object. */ ChartDataCartesian& ChartDataCartesian::operator=(const ChartDataCartesian& obj) { if (this != &obj) { ChartData::operator=(obj); this->copyHelperChartDataCartesian(obj); } return *this; } /** * Helps with copying an object of this type. * @param obj * Object that is copied. */ void ChartDataCartesian::copyHelperChartDataCartesian(const ChartDataCartesian& obj) { CaretAssert(0); m_dataAxisUnitsX = obj.m_dataAxisUnitsX; m_dataAxisUnitsY = obj.m_dataAxisUnitsY; removeAllPoints(); for (std::vector::const_iterator iter = obj.m_points.begin(); iter != obj.m_points.end(); iter++) { const ChartPoint* cp = *iter; m_points.push_back(new ChartPoint(*cp)); } m_boundsValid = false; m_color = obj.m_color; m_timeStartInSecondsAxisX = obj.m_timeStartInSecondsAxisX; m_timeStepInSecondsAxisX = obj.m_timeStepInSecondsAxisX; } /** * Add a point. * * @param x * X-coordinate. * @param y * Y-coordinate. */ void ChartDataCartesian::addPoint(const float x, const float y) { m_points.push_back(new ChartPoint(x, y)); m_boundsValid = false; } /** * @return Number of points. */ int32_t ChartDataCartesian::getNumberOfPoints() const { return m_points.size(); } /** * Get the point at the given index. * * @param pointIndex * Index of point. * @return * Point at the given index. */ const ChartPoint* ChartDataCartesian::getPointAtIndex(const int32_t pointIndex) const { CaretAssertVectorIndex(m_points, pointIndex); return m_points[pointIndex]; } /** * Get the bounds of all of the points. * * @param xMinimumOut * Minimum X-coordinate of all points. * @param xMaximumOut * Maximum X-coordinate of all points. * @param yMinimumOut * Minimum Y-coordinate of all points. * @param yMaximumOut * Maximum Y-coordinate of all points. */ void ChartDataCartesian::getBounds(float& xMinimumOut, float& xMaximumOut, float& yMinimumOut, float& yMaximumOut) const { if (! m_boundsValid) { float xMin = 0.0; float xMax = 0.0; float yMin = 0.0; float yMax = 0.0; float zMin = 0.0; float zMax = 0.0; const int32_t numPoints = getNumberOfPoints(); if (numPoints > 0) { xMin = std::numeric_limits::max(); xMax = -std::numeric_limits::max(); yMin = std::numeric_limits::max(); yMax = -std::numeric_limits::max(); for (int32_t i = 0; i < numPoints; i++) { const float* xy = getPointAtIndex(i)->getXY(); const float x = xy[0]; const float y = xy[1]; if (x < xMin) xMin = x; if (x > xMax) xMax = x; if (y < yMin) yMin = y; if (y > yMax) yMax = y; } m_boundsValid = true; } m_bounds[0] = xMin; m_bounds[1] = xMax; m_bounds[2] = yMin; m_bounds[3] = yMax; m_bounds[4] = zMin; m_bounds[5] = zMax; } xMinimumOut = m_bounds[0]; xMaximumOut = m_bounds[1]; yMinimumOut = m_bounds[2]; yMaximumOut = m_bounds[3]; } /** * @return The time start in seconds for the X-Axis (Valid when * the X-axis is time) */ float ChartDataCartesian::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 ChartDataCartesian::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 ChartDataCartesian::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 ChartDataCartesian::setTimeStepInSecondsAxisX(const float timeStepInSecondsAxisX) { m_timeStepInSecondsAxisX = timeStepInSecondsAxisX; } /** * @return Data units for X axis */ ChartAxisUnitsEnum::Enum ChartDataCartesian::getDataAxisUnitsX() { return m_dataAxisUnitsX; } /** * @return Data units for Y axis */ ChartAxisUnitsEnum::Enum ChartDataCartesian::getDataAxisUnitsY() { return m_dataAxisUnitsY; } /** * @return Color for chart */ CaretColorEnum::Enum ChartDataCartesian::getColor() const { return m_color; } /** * Set the color for the chart. * * @param color * New color for chart. */ void ChartDataCartesian::setColor(const CaretColorEnum::Enum color) { m_color = color; } /** * 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 ChartDataCartesian::saveSubClassDataToScene(const SceneAttributes* sceneAttributes, SceneClass* sceneClass) { SceneClass* chartDataCartesian = new SceneClass("chartDataCartesian", "ChartDataCartesian", 1); m_sceneAssistant->saveMembers(sceneAttributes, chartDataCartesian); const int32_t numPoints2D = getNumberOfPoints(); if (numPoints2D > 0) { chartDataCartesian->addInteger("numberOfPoints2D", numPoints2D); AString pointString; pointString.reserve(numPoints2D * 2 * 10); QTextStream textStream(&pointString, QIODevice::WriteOnly); for (int32_t i = 0; i < numPoints2D; i++) { const float* xy = m_points[i]->getXY(); textStream << xy[0] << " " << xy[1] << " "; } chartDataCartesian->addString("points2D", pointString); } sceneClass->addClass(chartDataCartesian); } /** * 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 ChartDataCartesian::restoreSubClassDataFromScene(const SceneAttributes* sceneAttributes, const SceneClass* sceneClass) { removeAllPoints(); const SceneClass* chartDataCartesian = sceneClass->getClass("chartDataCartesian"); if (chartDataCartesian == NULL) { return; } m_sceneAssistant->restoreMembers(sceneAttributes, chartDataCartesian); const int32_t numPoints2D = chartDataCartesian->getIntegerValue("numberOfPoints2D", -1); if (numPoints2D > 0) { AString pointString = chartDataCartesian->getStringValue("points2D", ""); if ( ! pointString.isEmpty()) { float x, y; QTextStream textStream(&pointString, QIODevice::ReadOnly); for (int32_t i = 0; i < numPoints2D; i++) { if (textStream.atEnd()) { sceneAttributes->addToErrorMessage("Tried to read " + AString::number(numPoints2D) + " but only got " + AString::number(i)); break; } textStream >> x; textStream >> y; m_points.push_back(new ChartPoint(x, y)); } } } } connectome-workbench-1.2.3+git41-gc4c6c90/src/Charting/ChartDataCartesian.h000066400000000000000000000074051300200146000261470ustar00rootroot00000000000000#ifndef __CHART_DATA_CARTESIAN_H__ #define __CHART_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 "ChartAxisUnitsEnum.h" #include "ChartData.h" namespace caret { class ChartPoint; class ChartDataCartesian : public ChartData { public: ChartDataCartesian(const ChartDataTypeEnum::Enum chartDataType, const ChartAxisUnitsEnum::Enum dataAxisUnitsX, const ChartAxisUnitsEnum::Enum dataAxisUnitsY); virtual ~ChartDataCartesian(); virtual ChartData* clone() const; void addPoint(const float x, const float y); int32_t getNumberOfPoints() const; const ChartPoint* getPointAtIndex(const int32_t pointIndex) const; void getBounds(float& xMinimumOut, float& xMaximumOut, float& yMinimumOut, float& yMaximumOut) const; ChartAxisUnitsEnum::Enum getDataAxisUnitsX(); ChartAxisUnitsEnum::Enum getDataAxisUnitsY(); CaretColorEnum::Enum getColor() const; void setColor(const CaretColorEnum::Enum color); float getTimeStartInSecondsAxisX() const; void setTimeStartInSecondsAxisX(const float timeStart); float getTimeStepInSecondsAxisX() const; void setTimeStepInSecondsAxisX(const float timeStep); // ADD_NEW_METHODS_HERE protected: virtual void saveSubClassDataToScene(const SceneAttributes* sceneAttributes, SceneClass* sceneClass); virtual void restoreSubClassDataFromScene(const SceneAttributes* sceneAttributes, const SceneClass* sceneClass); private: ChartDataCartesian(const ChartDataCartesian& obj); ChartDataCartesian& operator=(const ChartDataCartesian& obj); void copyHelperChartDataCartesian(const ChartDataCartesian& obj); void initializeMembersChartDataCartesian(); void removeAllPoints(); std::vector m_points; mutable float m_bounds[6]; mutable bool m_boundsValid; ChartAxisUnitsEnum::Enum m_dataAxisUnitsX; ChartAxisUnitsEnum::Enum m_dataAxisUnitsY; CaretColorEnum::Enum m_color; float m_timeStartInSecondsAxisX; float m_timeStepInSecondsAxisX; static int32_t caretColorIndex; SceneClassAssistant* m_sceneAssistant; // ADD_NEW_MEMBERS_HERE }; #ifdef __CHART_DATA_CARTESIAN_DECLARE__ int32_t ChartDataCartesian::caretColorIndex = 0; #endif // __CHART_DATA_CARTESIAN_DECLARE__ } // namespace #endif //__CHART_DATA_CARTESIAN_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Charting/ChartDataSource.cxx000066400000000000000000000346631300200146000260570ustar00rootroot00000000000000 /*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 #define __CHART_DATA_SOURCE_DECLARE__ #include "ChartDataSource.h" #undef __CHART_DATA_SOURCE_DECLARE__ #include "CaretAssert.h" #include "CaretLogger.h" #include "FileInformation.h" #include "SceneClass.h" #include "SceneClassArray.h" #include "SceneClassAssistant.h" using namespace caret; /** * \class caret::ChartDataSource * \brief Contains source of data that is displayed in a chart. * \ingroup Charting */ /** * Constructor. */ ChartDataSource::ChartDataSource() : CaretObject(), SceneableInterface() { initializeMembersChartDataSource(); } /** * Destructor. */ ChartDataSource::~ChartDataSource() { delete m_sceneAssistant; } /** * Initialize members of a new instance. */ void ChartDataSource::initializeMembersChartDataSource() { m_dataSourceMode = ChartDataSourceModeEnum::CHART_DATA_SOURCE_MODE_INVALID; m_nodeIndex = -1; m_voxelXYZ[0] = 0.0; m_voxelXYZ[1] = 0.0; m_voxelXYZ[2] = 0.0; m_fileRowIndex = -1; m_sceneAssistant = new SceneClassAssistant(); m_sceneAssistant->add("m_dataSourceMode", &m_dataSourceMode); m_sceneAssistant->add("m_nodeIndex", &m_nodeIndex); m_sceneAssistant->add("m_surfaceNumberOfNodes", &m_surfaceNumberOfNodes); m_sceneAssistant->add("m_surfaceStructureName", &m_surfaceStructureName); m_sceneAssistant->addArray("m_voxelXYZ", m_voxelXYZ, 3, -1); m_sceneAssistant->add("m_fileRowIndex", &m_fileRowIndex); } /** * Copy constructor. * @param obj * Object that is copied. */ ChartDataSource::ChartDataSource(const ChartDataSource& obj) : CaretObject(obj), SceneableInterface(obj) { initializeMembersChartDataSource(); this->copyHelperChartDataSource(obj); } /** * Assignment operator. * @param obj * Data copied from obj to this. * @return * Reference to this object. */ ChartDataSource& ChartDataSource::operator=(const ChartDataSource& obj) { if (this != &obj) { CaretObject::operator=(obj); this->copyHelperChartDataSource(obj); } return *this; } /** * Copy the given data source to me. * * @param copyFrom * Chart data source that is copied to me. */ void ChartDataSource::copy(const ChartDataSource* copyFrom) { CaretAssert(copyFrom); copyHelperChartDataSource(*copyFrom); } /** * Helps with copying an object of this type. * @param obj * Object that is copied. */ void ChartDataSource::copyHelperChartDataSource(const ChartDataSource& obj) { m_dataSourceMode = obj.m_dataSourceMode; m_chartableFileName = obj.m_chartableFileName; m_nodeIndex = obj.m_nodeIndex; m_surfaceNumberOfNodes = obj.m_surfaceNumberOfNodes; m_surfaceStructureName = obj.m_surfaceStructureName; m_nodeIndicesAverage = obj.m_nodeIndicesAverage; m_voxelXYZ[0] = obj.m_voxelXYZ[0]; m_voxelXYZ[1] = obj.m_voxelXYZ[1]; m_voxelXYZ[2] = obj.m_voxelXYZ[2]; m_fileRowIndex = obj.m_fileRowIndex; } /** * @return Name of the chartable file. */ AString ChartDataSource::getChartableFileName() const { return m_chartableFileName; } /** * Setup for a surface node source. * * @param chartableFileName * Name of the chartable file. * @param surfaceStructureName * Name of surface structure. * @param surfaceNumberOfNodes * Number of nodes in the surface. * @param nodeIndex * Index of the surface node. */ void ChartDataSource::setSurfaceNode(const AString& chartableFileName, const AString& surfaceStructureName, const int32_t surfaceNumberOfNodes, const int32_t nodeIndex) { CaretAssert(nodeIndex >= 0); m_dataSourceMode = ChartDataSourceModeEnum::CHART_DATA_SOURCE_MODE_SURFACE_NODE_INDEX; m_chartableFileName = chartableFileName; m_surfaceNumberOfNodes = surfaceNumberOfNodes; m_surfaceStructureName = surfaceStructureName; m_nodeIndex = nodeIndex; } /** * Is the given node the source of the data? * * @param surfaceStructureName * Name of surface structure. * @param nodeIndex * Index of the surface node. * @return * True if node is source of data, else false. */ bool ChartDataSource::isSurfaceNodeSourceOfData(const AString& surfaceStructureName, const int32_t nodeIndex) const { if (m_dataSourceMode == ChartDataSourceModeEnum::CHART_DATA_SOURCE_MODE_SURFACE_NODE_INDEX) { if (m_nodeIndex == nodeIndex) { if (m_surfaceStructureName == surfaceStructureName) { return true; } } } return false; } /** * @return Mode indicating source of the data. */ ChartDataSourceModeEnum::Enum ChartDataSource::getDataSourceMode() const { return m_dataSourceMode; } /** * Get the surface node data source. * * @param surfaceStructureName * Name of surface structure. * @param surfaceNumberOfNodes * Number of nodes in the surface. * @param nodeIndex * Index of the surface node. */ void ChartDataSource::getSurfaceNode(AString& surfaceStructureName, int32_t& surfaceNumberOfNodes, int32_t& nodeIndex) const { surfaceStructureName = m_surfaceStructureName; surfaceNumberOfNodes = m_surfaceNumberOfNodes; nodeIndex = m_nodeIndex; } /** * Get the surface node average data source. * * @param surfaceStructureName * Name of surface structure. * @param surfaceNumberOfNodes * Number of nodes in the surface. * @param nodeIndices * Indices of the surface node. */ void ChartDataSource::getSurfaceNodeAverage(AString& surfaceStructureName, int32_t& surfaceNumberOfNodes, std::vector& nodeIndices) const { surfaceStructureName = m_surfaceStructureName; surfaceNumberOfNodes = m_surfaceNumberOfNodes; nodeIndices = m_nodeIndicesAverage; } /** * Get the surface node average data source. * * @param chartableFileName * Name of the chartable file. * @param surfaceStructureName * Name of surface structure. * @param surfaceNumberOfNodes * Number of nodes in the surface. * @param nodeIndices * Indices of the surface node. */ void ChartDataSource::setSurfaceNodeAverage(const AString& chartableFileName, const AString& surfaceStructureName, const int32_t surfaceNumberOfNodes, const std::vector& nodeIndices) { m_dataSourceMode = ChartDataSourceModeEnum::CHART_DATA_SOURCE_MODE_SURFACE_NODE_INDICES_AVERAGE; m_chartableFileName = chartableFileName; m_surfaceStructureName = surfaceStructureName; m_surfaceNumberOfNodes = surfaceNumberOfNodes; m_nodeIndicesAverage = nodeIndices; } /** * Get the volume voxel data source. * * @param ijk * Indices of the voxel. */ void ChartDataSource::getVolumeVoxel(float xyz[3]) const { xyz[0] = m_voxelXYZ[0]; xyz[1] = m_voxelXYZ[1]; xyz[2] = m_voxelXYZ[2]; } /** * Set the volume voxel data source. * * @param chartableFileName * Name of the chartable file. * @param ijk * Indices of the voxel. */ void ChartDataSource::setVolumeVoxel(const AString& chartableFileName, const float xyz[3]) { m_dataSourceMode = ChartDataSourceModeEnum::CHART_DATA_SOURCE_MODE_VOXEL_IJK; m_chartableFileName = chartableFileName; m_voxelXYZ[0] = xyz[0]; m_voxelXYZ[1] = xyz[1]; m_voxelXYZ[2] = xyz[2]; } /** * Get the file row data source. * * @param chartableFileName * Name of the file. * @param fileRowIndex * Index of the row. */ void ChartDataSource::getFileRow(AString& chartableFileName, int32_t& fileRowIndex) const { chartableFileName = m_chartableFileName; fileRowIndex = m_fileRowIndex; } /** * Set the file row data source. * * @param chartableFileName * Name of the file. * @param fileRowIndex * Index of the row. */ void ChartDataSource::setFileRow(const AString& chartableFileName, const int32_t fileRowIndex) { m_dataSourceMode = ChartDataSourceModeEnum::CHART_DATA_SOURCE_MODE_FILE_ROW; m_chartableFileName = chartableFileName; m_fileRowIndex = fileRowIndex; } /** * Get a description of this object's content. * @return String describing this object's content. */ AString ChartDataSource::getDescription() const { AString s; switch (m_dataSourceMode) { case ChartDataSourceModeEnum::CHART_DATA_SOURCE_MODE_INVALID: break; case ChartDataSourceModeEnum::CHART_DATA_SOURCE_MODE_FILE_ROW: s += (" Row " + AString::number(m_fileRowIndex + 1)); break; case ChartDataSourceModeEnum::CHART_DATA_SOURCE_MODE_SURFACE_NODE_INDEX: s += (m_surfaceStructureName + ": Vertex " + AString::number(m_nodeIndex)); break; case ChartDataSourceModeEnum::CHART_DATA_SOURCE_MODE_SURFACE_NODE_INDICES_AVERAGE: s += (m_surfaceStructureName + ": Average of " + AString::number(m_nodeIndicesAverage.size()) + " Vertices"); break; case ChartDataSourceModeEnum::CHART_DATA_SOURCE_MODE_VOXEL_IJK: s += ("Voxel XYZ (" + AString::fromNumbers(m_voxelXYZ, 3, ",") + ")"); break; } if ( ! s.isEmpty()) { FileInformation fileInfo(m_chartableFileName); s += (" from " + fileInfo.getFileName()); } return s; } /** * Get a description of this object's content. * @return String describing this object's content. */ AString ChartDataSource::toString() const { AString s = "ChartDataSource"; return s; } /** * 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* ChartDataSource::saveToScene(const SceneAttributes* sceneAttributes, const AString& instanceName) { SceneClass* sceneClass = new SceneClass(instanceName, "ChartDataSource", 1); m_sceneAssistant->saveMembers(sceneAttributes, sceneClass); sceneClass->addPathName("m_chartableFileName", m_chartableFileName); switch (m_dataSourceMode) { case ChartDataSourceModeEnum::CHART_DATA_SOURCE_MODE_INVALID: break; case ChartDataSourceModeEnum::CHART_DATA_SOURCE_MODE_FILE_ROW: break; case ChartDataSourceModeEnum::CHART_DATA_SOURCE_MODE_SURFACE_NODE_INDEX: break; case ChartDataSourceModeEnum::CHART_DATA_SOURCE_MODE_SURFACE_NODE_INDICES_AVERAGE: { const int32_t numNodes = static_cast(m_nodeIndicesAverage.size()); if (numNodes > 0) { sceneClass->addIntegerArray("m_nodeIndicesAverage", &m_nodeIndicesAverage[0], numNodes); } } break; case ChartDataSourceModeEnum::CHART_DATA_SOURCE_MODE_VOXEL_IJK: break; } 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 ChartDataSource::restoreFromScene(const SceneAttributes* sceneAttributes, const SceneClass* sceneClass) { if (sceneClass == NULL) { return; } m_sceneAssistant->restoreMembers(sceneAttributes, sceneClass); m_chartableFileName = sceneClass->getPathNameValue("m_chartableFileName"); switch (m_dataSourceMode) { case ChartDataSourceModeEnum::CHART_DATA_SOURCE_MODE_INVALID: break; case ChartDataSourceModeEnum::CHART_DATA_SOURCE_MODE_FILE_ROW: break; case ChartDataSourceModeEnum::CHART_DATA_SOURCE_MODE_SURFACE_NODE_INDEX: break; case ChartDataSourceModeEnum::CHART_DATA_SOURCE_MODE_SURFACE_NODE_INDICES_AVERAGE: { const SceneClassArray* nodeArray = sceneClass->getClassArray("m_nodeIndicesAverage"); if (nodeArray != NULL) { const int32_t numNodes = nodeArray->getNumberOfArrayElements(); if (numNodes > 0) { m_nodeIndicesAverage.resize(numNodes); sceneClass->getIntegerArrayValue("m_nodeIndicesAverage", &m_nodeIndicesAverage[0], numNodes); } } } break; case ChartDataSourceModeEnum::CHART_DATA_SOURCE_MODE_VOXEL_IJK: break; } } connectome-workbench-1.2.3+git41-gc4c6c90/src/Charting/ChartDataSource.h000066400000000000000000000104051300200146000254700ustar00rootroot00000000000000#ifndef __CHART_DATA_SOURCE_H__ #define __CHART_DATA_SOURCE_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 "ChartDataSourceModeEnum.h" #include "SceneableInterface.h" namespace caret { class SceneClassAssistant; class ChartDataSource : public CaretObject, public SceneableInterface { public: ChartDataSource(); virtual ~ChartDataSource(); ChartDataSource(const ChartDataSource&); ChartDataSource& operator=(const ChartDataSource&); void copy(const ChartDataSource* copyFrom); virtual SceneClass* saveToScene(const SceneAttributes* sceneAttributes, const AString& instanceName); virtual void restoreFromScene(const SceneAttributes* sceneAttributes, const SceneClass* sceneClass); ChartDataSourceModeEnum::Enum getDataSourceMode() const; AString getChartableFileName() const; void getSurfaceNode(AString& surfaceStructureName, int32_t& surfaceNumberOfNodes, int32_t& nodeIndex) const; void setSurfaceNode(const AString& chartableFileName, const AString& surfaceStructureName, const int32_t surfaceNumberOfNodes, const int32_t nodeIndex); bool isSurfaceNodeSourceOfData(const AString& surfaceStructureName, const int32_t nodeIndex) const; void getSurfaceNodeAverage(AString& surfaceStructureName, int32_t& surfaceNumberOfNodes, std::vector& nodeIndices) const; void setSurfaceNodeAverage(const AString& chartableFileName, const AString& surfaceStructureName, const int32_t surfaceNumberOfNodes, const std::vector& nodeIndices); void getVolumeVoxel(float xyz[3]) const; void setVolumeVoxel(const AString& chartableFileName, const float xyz[3]); void getFileRow(AString& chartableFileName, int32_t& fileRowIndex) const; void setFileRow(const AString& chartableFileName, const int32_t fileRowIndex); AString getDescription() const; private: void copyHelperChartDataSource(const ChartDataSource& obj); public: // ADD_NEW_METHODS_HERE virtual AString toString() const; private: void initializeMembersChartDataSource(); SceneClassAssistant* m_sceneAssistant; ChartDataSourceModeEnum::Enum m_dataSourceMode; AString m_chartableFileName; int32_t m_surfaceNumberOfNodes; AString m_surfaceStructureName; std::vector m_nodeIndicesAverage; int32_t m_nodeIndex; float m_voxelXYZ[3]; int32_t m_fileRowIndex; // ADD_NEW_MEMBERS_HERE }; #ifdef __CHART_DATA_SOURCE_DECLARE__ // #endif // __CHART_DATA_SOURCE_DECLARE__ } // namespace #endif //__CHART_DATA_SOURCE_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Charting/ChartDataSourceModeEnum.cxx000066400000000000000000000267161300200146000275110ustar00rootroot00000000000000 /*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_DATA_SOURCE_MODE_ENUM_DECLARE__ #include "ChartDataSourceModeEnum.h" #undef __CHART_DATA_SOURCE_MODE_ENUM_DECLARE__ #include "CaretAssert.h" using namespace caret; /** * \class caret::ChartDataSourceModeEnum * \brief Enumerated type for shource source of a chart. * * * * 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_chartDataSourceModeEnumComboBox; * * Declare a slot that is called when user changes selection * private slots: * void chartDataSourceModeEnumComboBoxItemActivated(); * * Implementation File (.cxx) * Include the header files * #include "EnumComboBoxTemplate.h" * #include "ChartDataSourceModeEnum.h" * * Instatiate: * m_chartDataSourceModeEnumComboBox = new EnumComboBoxTemplate(this); * m_chartDataSourceModeEnumComboBox->setup(); * * Get notified when the user changes the selection: * QObject::connect(m_chartDataSourceModeEnumComboBox, SIGNAL(itemActivated()), * this, SLOT(chartDataSourceModeEnumComboBoxItemActivated())); * * Update the selection: * m_chartDataSourceModeEnumComboBox->setSelectedItem(NEW_VALUE); * * Read the selection: * const ChartDataSourceModeEnum::Enum VARIABLE = m_chartDataSourceModeEnumComboBox->getSelectedItem(); * */ /** * Constructor. * * @param enumValue * An enumerated value. * @param name * Name of enumerated value. * * @param guiName * User-friendly name for use in user-interface. */ ChartDataSourceModeEnum::ChartDataSourceModeEnum(const Enum enumValue, const AString& name, const AString& guiName) { this->enumValue = enumValue; this->integerCode = integerCodeCounter++; this->name = name; this->guiName = guiName; } /** * Destructor. */ ChartDataSourceModeEnum::~ChartDataSourceModeEnum() { } /** * Initialize the enumerated metadata. */ void ChartDataSourceModeEnum::initialize() { if (initializedFlag) { return; } initializedFlag = true; enumData.push_back(ChartDataSourceModeEnum(CHART_DATA_SOURCE_MODE_INVALID, "CHART_DATA_SOURCE_MODE_INVALID", "Invalid")); enumData.push_back(ChartDataSourceModeEnum(CHART_DATA_SOURCE_MODE_FILE_ROW, "CHART_DATA_SOURCE_MODE_FILE_ROW", "Chart Source File Row")); enumData.push_back(ChartDataSourceModeEnum(CHART_DATA_SOURCE_MODE_SURFACE_NODE_INDEX, "CHART_DATA_SOURCE_MODE_SURFACE_NODE_INDEX", "Chart Source Surface Node")); enumData.push_back(ChartDataSourceModeEnum(CHART_DATA_SOURCE_MODE_SURFACE_NODE_INDICES_AVERAGE, "CHART_DATA_SOURCE_MODE_SURFACE_NODE_INDICES_AVERAGE", "Chart Source Average of Surface Nodes")); enumData.push_back(ChartDataSourceModeEnum(CHART_DATA_SOURCE_MODE_VOXEL_IJK, "CHART_DATA_SOURCE_MODE_VOXEL_IJK", "Chart Source Voxel")); } /** * 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 ChartDataSourceModeEnum* ChartDataSourceModeEnum::findData(const Enum enumValue) { if (initializedFlag == false) initialize(); size_t num = enumData.size(); for (size_t i = 0; i < num; i++) { const ChartDataSourceModeEnum* 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 ChartDataSourceModeEnum::toName(Enum enumValue) { if (initializedFlag == false) initialize(); const ChartDataSourceModeEnum* 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. */ ChartDataSourceModeEnum::Enum ChartDataSourceModeEnum::fromName(const AString& name, bool* isValidOut) { if (initializedFlag == false) initialize(); bool validFlag = false; Enum enumValue = ChartDataSourceModeEnum::enumData[0].enumValue; for (std::vector::iterator iter = enumData.begin(); iter != enumData.end(); iter++) { const ChartDataSourceModeEnum& 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 ChartDataSourceModeEnum")); } return enumValue; } /** * Get a GUI string representation of the enumerated type. * @param enumValue * Enumerated value. * @return * String representing enumerated value. */ AString ChartDataSourceModeEnum::toGuiName(Enum enumValue) { if (initializedFlag == false) initialize(); const ChartDataSourceModeEnum* 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. */ ChartDataSourceModeEnum::Enum ChartDataSourceModeEnum::fromGuiName(const AString& guiName, bool* isValidOut) { if (initializedFlag == false) initialize(); bool validFlag = false; Enum enumValue = ChartDataSourceModeEnum::enumData[0].enumValue; for (std::vector::iterator iter = enumData.begin(); iter != enumData.end(); iter++) { const ChartDataSourceModeEnum& 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 ChartDataSourceModeEnum")); } return enumValue; } /** * Get the integer code for a data type. * * @return * Integer code for data type. */ int32_t ChartDataSourceModeEnum::toIntegerCode(Enum enumValue) { if (initializedFlag == false) initialize(); const ChartDataSourceModeEnum* 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. */ ChartDataSourceModeEnum::Enum ChartDataSourceModeEnum::fromIntegerCode(const int32_t integerCode, bool* isValidOut) { if (initializedFlag == false) initialize(); bool validFlag = false; Enum enumValue = ChartDataSourceModeEnum::enumData[0].enumValue; for (std::vector::iterator iter = enumData.begin(); iter != enumData.end(); iter++) { const ChartDataSourceModeEnum& 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 ChartDataSourceModeEnum")); } 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 ChartDataSourceModeEnum::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 ChartDataSourceModeEnum::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(ChartDataSourceModeEnum::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 ChartDataSourceModeEnum::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(ChartDataSourceModeEnum::toGuiName(iter->enumValue)); } if (isSorted) { std::sort(allGuiNames.begin(), allGuiNames.end()); } } connectome-workbench-1.2.3+git41-gc4c6c90/src/Charting/ChartDataSourceModeEnum.h000066400000000000000000000067461300200146000271370ustar00rootroot00000000000000#ifndef __CHART_DATA_SOURCE_MODE_ENUM_H__ #define __CHART_DATA_SOURCE_MODE_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 ChartDataSourceModeEnum { public: /** * Enumerated values. */ enum Enum { /** Invalid mode */ CHART_DATA_SOURCE_MODE_INVALID, /** Chart is from a file's row */ CHART_DATA_SOURCE_MODE_FILE_ROW, /** Chart is from a surface node index */ CHART_DATA_SOURCE_MODE_SURFACE_NODE_INDEX, /** Chart is from an average of surface node indices */ CHART_DATA_SOURCE_MODE_SURFACE_NODE_INDICES_AVERAGE, /** Chart is from a voxel index */ CHART_DATA_SOURCE_MODE_VOXEL_IJK }; ~ChartDataSourceModeEnum(); 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: ChartDataSourceModeEnum(const Enum enumValue, const AString& name, const AString& guiName); static const ChartDataSourceModeEnum* 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_DATA_SOURCE_MODE_ENUM_DECLARE__ std::vector ChartDataSourceModeEnum::enumData; bool ChartDataSourceModeEnum::initializedFlag = false; int32_t ChartDataSourceModeEnum::integerCodeCounter = 0; #endif // __CHART_DATA_SOURCE_MODE_ENUM_DECLARE__ } // namespace #endif //__CHART_DATA_SOURCE_MODE_ENUM_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Charting/ChartDataTypeEnum.cxx000066400000000000000000000272101300200146000263530ustar00rootroot00000000000000 /*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_DATA_TYPE_ENUM_DECLARE__ #include "ChartDataTypeEnum.h" #undef __CHART_DATA_TYPE_ENUM_DECLARE__ #include "CaretAssert.h" using namespace caret; /** * \class caret::ChartDataTypeEnum * \brief Enumerated type for type of chart data. * * 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_chartDataTypeEnumComboBox; * * Declare a slot that is called when user changes selection * private slots: * void chartDataTypeEnumComboBoxItemActivated(); * * Implementation File (.cxx) * Include the header files * #include "EnumComboBoxTemplate.h" * #include "ChartDataTypeEnum.h" * * Instatiate: * m_chartDataTypeEnumComboBox = new EnumComboBoxTemplate(this); * 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); * * Read the selection: * const ChartDataTypeEnum::Enum VARIABLE = m_chartDataTypeEnumComboBox->getSelectedItem(); * */ /** * Constructor. * * @param enumValue * An enumerated value. * @param name * Name of enumerated value. * * @param guiName * User-friendly name for use in user-interface. */ ChartDataTypeEnum::ChartDataTypeEnum(const Enum enumValue, const AString& name, const AString& guiName) { this->enumValue = enumValue; this->integerCode = integerCodeCounter++; this->name = name; this->guiName = guiName; } /** * Destructor. */ ChartDataTypeEnum::~ChartDataTypeEnum() { } /** * Initialize the enumerated metadata. */ void ChartDataTypeEnum::initialize() { if (initializedFlag) { return; } initializedFlag = true; enumData.push_back(ChartDataTypeEnum(CHART_DATA_TYPE_INVALID, "CHART_DATA_TYPE_INVALID", "Invalid")); enumData.push_back(ChartDataTypeEnum(CHART_DATA_TYPE_LINE_DATA_SERIES, "CHART_DATA_TYPE_LINE_DATA_SERIES", "Data Series")); enumData.push_back(ChartDataTypeEnum(CHART_DATA_TYPE_LINE_FREQUENCY_SERIES, "CHART_DATA_TYPE_LINE_FREQUENCY_SERIES", "Frequency Series")); enumData.push_back(ChartDataTypeEnum(CHART_DATA_TYPE_LINE_TIME_SERIES, "CHART_DATA_TYPE_LINE_TIME_SERIES", "Time Series")); enumData.push_back(ChartDataTypeEnum(CHART_DATA_TYPE_MATRIX_LAYER, "CHART_DATA_TYPE_MATRIX_LAYER", "Matrix - Layer")); enumData.push_back(ChartDataTypeEnum(CHART_DATA_TYPE_MATRIX_SERIES, "CHART_DATA_TYPE_MATRIX_SERIES", "Matrix - Series")); } /** * 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 ChartDataTypeEnum* ChartDataTypeEnum::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]; 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 ChartDataTypeEnum::toName(Enum enumValue) { if (initializedFlag == false) initialize(); const ChartDataTypeEnum* enumInstance = findData(enumValue); return enumInstance->name; } /** * Get an enumerated value corresponding to its name. * @param nameIn * 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. */ ChartDataTypeEnum::Enum ChartDataTypeEnum::fromName(const AString& nameIn, bool* isValidOut) { if (initializedFlag == false) initialize(); /* * Convert from obsolete names */ AString name(nameIn); if (name == "CHART_DATA_TYPE_MATRIX") { name = "CHART_DATA_TYPE_MATRIX_LAYER"; } else if (name == "CHART_DATA_TYPE_DATA_SERIES") { name = "CHART_DATA_TYPE_LINE_DATA_SERIES"; } else if (name == "CHART_DATA_TYPE_TIME_SERIES") { name = "CHART_DATA_TYPE_LINE_TIME_SERIES"; } bool validFlag = false; Enum enumValue = ChartDataTypeEnum::enumData[0].enumValue; for (std::vector::iterator iter = enumData.begin(); iter != enumData.end(); iter++) { const ChartDataTypeEnum& 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 ChartDataTypeEnum")); } return enumValue; } /** * Get a GUI string representation of the enumerated type. * @param enumValue * Enumerated value. * @return * String representing enumerated value. */ AString ChartDataTypeEnum::toGuiName(Enum enumValue) { if (initializedFlag == false) initialize(); const ChartDataTypeEnum* enumInstance = findData(enumValue); return enumInstance->guiName; } /** * Get an enumerated value corresponding to its GUI name. * @param guiNameIn * 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. */ ChartDataTypeEnum::Enum ChartDataTypeEnum::fromGuiName(const AString& guiNameIn, bool* isValidOut) { if (initializedFlag == false) initialize(); AString guiName(guiNameIn); if (guiName == "Matrix") { guiName = "Matrix - Layer"; } bool validFlag = false; Enum enumValue = ChartDataTypeEnum::enumData[0].enumValue; for (std::vector::iterator iter = enumData.begin(); iter != enumData.end(); iter++) { const ChartDataTypeEnum& 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 ChartDataTypeEnum")); } return enumValue; } /** * Get the integer code for a data type. * * @return * Integer code for data type. */ int32_t ChartDataTypeEnum::toIntegerCode(Enum enumValue) { if (initializedFlag == false) initialize(); const ChartDataTypeEnum* 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. */ ChartDataTypeEnum::Enum ChartDataTypeEnum::fromIntegerCode(const int32_t integerCode, bool* isValidOut) { if (initializedFlag == false) initialize(); bool validFlag = false; Enum enumValue = ChartDataTypeEnum::enumData[0].enumValue; for (std::vector::iterator iter = enumData.begin(); iter != enumData.end(); iter++) { const ChartDataTypeEnum& 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 ChartDataTypeEnum")); } 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 ChartDataTypeEnum::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 ChartDataTypeEnum::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(ChartDataTypeEnum::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 ChartDataTypeEnum::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(ChartDataTypeEnum::toGuiName(iter->enumValue)); } if (isSorted) { std::sort(allGuiNames.begin(), allGuiNames.end()); } } connectome-workbench-1.2.3+git41-gc4c6c90/src/Charting/ChartDataTypeEnum.h000066400000000000000000000065761300200146000260140ustar00rootroot00000000000000#ifndef __CHART_DATA_TYPE_ENUM_H__ #define __CHART_DATA_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 ChartDataTypeEnum { public: /** * Enumerated values. */ enum Enum { /** Invalid */ CHART_DATA_TYPE_INVALID, /** Line Data Series */ CHART_DATA_TYPE_LINE_DATA_SERIES, /** Line Frequency Series */ CHART_DATA_TYPE_LINE_FREQUENCY_SERIES, /** Line Time Series */ CHART_DATA_TYPE_LINE_TIME_SERIES, /** Matrix (connectivity in layer) */ CHART_DATA_TYPE_MATRIX_LAYER, /** Matrix (series data) */ CHART_DATA_TYPE_MATRIX_SERIES }; ~ChartDataTypeEnum(); 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: ChartDataTypeEnum(const Enum enumValue, const AString& name, const AString& guiName); static const ChartDataTypeEnum* 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_DATA_TYPE_ENUM_DECLARE__ std::vector ChartDataTypeEnum::enumData; bool ChartDataTypeEnum::initializedFlag = false; int32_t ChartDataTypeEnum::integerCodeCounter = 0; #endif // __CHART_DATA_TYPE_ENUM_DECLARE__ } // namespace #endif //__CHART_DATA_TYPE_ENUM_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Charting/ChartMatrixDisplayProperties.cxx000066400000000000000000000276321300200146000306720ustar00rootroot00000000000000 /*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_MATRIX_DISPLAY_PROPERTIES_DECLARE__ #include "ChartMatrixDisplayProperties.h" #undef __CHART_MATRIX_DISPLAY_PROPERTIES_DECLARE__ #include "AnnotationColorBar.h" #include "CaretAssert.h" #include "SceneClass.h" #include "SceneClassAssistant.h" using namespace caret; /** * \class caret::ChartMatrixDisplayProperties * \brief Properites for display of matrix charts. * \ingroup Charting */ /** * Constructor. */ ChartMatrixDisplayProperties::ChartMatrixDisplayProperties() : CaretObject() { m_scaleMode = ChartMatrixScaleModeEnum::CHART_MATRIX_SCALE_AUTO; m_cellWidth = 10.0; m_cellHeight = 10.0; m_highlightSelectedRowColumn = true; m_displayGridLines = true; m_colorBar = new AnnotationColorBar(AnnotationAttributesDefaultTypeEnum::NORMAL); resetPropertiesToDefault(); m_sceneAssistant = new SceneClassAssistant(); m_sceneAssistant->add("m_cellWidth", &m_cellWidth); m_sceneAssistant->add("m_cellHeight", &m_cellHeight); m_sceneAssistant->add("m_viewZooming", &m_viewZooming); m_sceneAssistant->addArray("m_viewPanning", m_viewPanning, 2, 0.0); m_sceneAssistant->add("m_highlightSelectedRowColumn", &m_highlightSelectedRowColumn); m_sceneAssistant->add("m_displayGridLines", &m_displayGridLines); m_sceneAssistant->add("m_colorBar", "AnnotationColorBar", m_colorBar); m_sceneAssistant->add("m_scaleMode", &m_scaleMode); } /** * Destructor. */ ChartMatrixDisplayProperties::~ChartMatrixDisplayProperties() { delete m_colorBar; delete m_sceneAssistant; } /** * Copy constructor. * @param obj * Object that is copied. */ ChartMatrixDisplayProperties::ChartMatrixDisplayProperties(const ChartMatrixDisplayProperties& obj) : CaretObject(obj), SceneableInterface() { this->copyHelperChartMatrixDisplayProperties(obj); } /** * Assignment operator. * @param obj * Data copied from obj to this. * @return * Reference to this object. */ ChartMatrixDisplayProperties& ChartMatrixDisplayProperties::operator=(const ChartMatrixDisplayProperties& obj) { if (this != &obj) { CaretObject::operator=(obj); this->copyHelperChartMatrixDisplayProperties(obj); } return *this; } /** * Reset to the default. */ void ChartMatrixDisplayProperties::resetPropertiesToDefault() { m_viewPanning[0] = 0.0; m_viewPanning[1] = 0.0; m_viewZooming = 1.0; // m_cellWidth = 10.0; // m_cellHeight = 10.0; setScaleMode(ChartMatrixScaleModeEnum::CHART_MATRIX_SCALE_AUTO); } /** * Helps with copying an object of this type. * @param obj * Object that is copied. */ void ChartMatrixDisplayProperties::copyHelperChartMatrixDisplayProperties(const ChartMatrixDisplayProperties& obj) { m_viewPanning[0] = obj.m_viewPanning[0]; m_viewPanning[1] = obj.m_viewPanning[1]; m_viewZooming = obj.m_viewZooming; m_cellWidth = obj.m_cellWidth; m_cellHeight = obj.m_cellHeight; m_scaleMode = obj.m_scaleMode; *m_colorBar = *obj.m_colorBar; } /** * Get a description of this object's content. * @return String describing this object's content. */ AString ChartMatrixDisplayProperties::toString() const { return "ChartMatrixDisplayProperties"; } /** * @return Widgth of matrix cell in pixels */ float ChartMatrixDisplayProperties::getCellWidth() const { float cellWidth = m_cellWidth; switch (m_scaleMode) { case ChartMatrixScaleModeEnum::CHART_MATRIX_SCALE_AUTO: break; case ChartMatrixScaleModeEnum::CHART_MATRIX_SCALE_MANUAL: if (s_manualScaleModeWindowHeightScaling > 0.0) { cellWidth *= s_manualScaleModeWindowWidthScaling; } break; } return cellWidth; } /** * Set width of matrix cell in pixels * @param cellWidth * New value for size of matrix cell width in pixels */ void ChartMatrixDisplayProperties::setCellWidth(const float cellWidth) { m_cellWidth = cellWidth; } /** * @return Height of matrix cell in pixels */ float ChartMatrixDisplayProperties::getCellHeight() const { float cellHeight = m_cellHeight; switch (m_scaleMode) { case ChartMatrixScaleModeEnum::CHART_MATRIX_SCALE_AUTO: break; case ChartMatrixScaleModeEnum::CHART_MATRIX_SCALE_MANUAL: if (s_manualScaleModeWindowHeightScaling > 0.0) { cellHeight *= s_manualScaleModeWindowHeightScaling; } break; } return cellHeight; } /** * Set height of matrix cell in pixels * @param cellHeight * New value for size of matrix cell in pixels */ void ChartMatrixDisplayProperties::setCellHeight(const float cellHeight) { m_cellHeight = cellHeight; } /** * @return zooming for view of matrix */ float ChartMatrixDisplayProperties::getViewZooming() const { return m_viewZooming; } /** * Set zooming for view of matrix * @param viewZooming * New value for zooming for view of matrix */ void ChartMatrixDisplayProperties::setViewZooming(const float viewZooming) { m_viewZooming = viewZooming; setScaleMode(ChartMatrixScaleModeEnum::CHART_MATRIX_SCALE_MANUAL); } /** * @return panning for view of matrix */ void ChartMatrixDisplayProperties::getViewPanning(float viewPanningOut[2]) const { viewPanningOut[0] = m_viewPanning[0]; viewPanningOut[1] = m_viewPanning[1]; switch (m_scaleMode) { case ChartMatrixScaleModeEnum::CHART_MATRIX_SCALE_AUTO: break; case ChartMatrixScaleModeEnum::CHART_MATRIX_SCALE_MANUAL: if (s_manualScaleModeWindowWidthScaling > 0.0) { viewPanningOut[0] *= s_manualScaleModeWindowWidthScaling; } if (s_manualScaleModeWindowHeightScaling > 0.0) { viewPanningOut[1] *= s_manualScaleModeWindowHeightScaling; } break; } } /** * Set panning for view of matrix * @param viewPanning[2] * New value for panning for view of matrix */ void ChartMatrixDisplayProperties::setViewPanning(const float viewPanning[2]) { m_viewPanning[0] = viewPanning[0]; m_viewPanning[1] = viewPanning[1]; setScaleMode(ChartMatrixScaleModeEnum::CHART_MATRIX_SCALE_MANUAL); } /** * @return scale mode for view of matrix */ ChartMatrixScaleModeEnum::Enum ChartMatrixDisplayProperties::getScaleMode() const { return m_scaleMode; } /** * Set the scale mode. * * @param scaleMode * New value for scale mode. */ void ChartMatrixDisplayProperties::setScaleMode(const ChartMatrixScaleModeEnum::Enum scaleMode) { m_scaleMode = scaleMode; } /** * @return The color bar displayed in graphics window. */ AnnotationColorBar* ChartMatrixDisplayProperties::getColorBar() { return m_colorBar; } /** * @return The color bar displayed in graphics window (const method). */ const AnnotationColorBar* ChartMatrixDisplayProperties::getColorBar() const { return m_colorBar; } /** * 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* ChartMatrixDisplayProperties::saveToScene(const SceneAttributes* sceneAttributes, const AString& instanceName) { SceneClass* sceneClass = new SceneClass(instanceName, "ChartMatrixDisplayProperties", 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 ChartMatrixDisplayProperties::restoreFromScene(const SceneAttributes* sceneAttributes, const SceneClass* sceneClass) { if (sceneClass == NULL) { return; } m_sceneAssistant->restoreMembers(sceneAttributes, sceneClass); /* * "m_paletteDisplayedFlag" controlled display of palette colorbar * prior to the addition of AnnotationColorBar */ const AString colorBarDisplayedFlagString = sceneClass->getStringValue("m_colorBarDisplayed"); if ( ! colorBarDisplayedFlagString.isEmpty()) { m_colorBar->reset(); m_colorBar->setDisplayed(sceneClass->getBooleanValue("m_colorBarDisplayed")); } //Uncomment if sub-classes must restore from scene //restoreSubClassDataFromScene(sceneAttributes, // sceneClass); } /** * Is the selected row/column highlighted? */ bool ChartMatrixDisplayProperties::isSelectedRowColumnHighlighted() const { return m_highlightSelectedRowColumn; } /** * Set the selected row/column highlighted status. * * @param highlightStatus * New status for lighlighting selected row/column. */ void ChartMatrixDisplayProperties::setSelectedRowColumnHighlighted(const bool highlightStatus) { m_highlightSelectedRowColumn = highlightStatus; } /** * Are the grid lines displayed? */ bool ChartMatrixDisplayProperties::isGridLinesDisplayed() const { return m_displayGridLines; } /** * Set the grid lines displayed for the given tab. * * @param displayGridLines * True if grid lines are displayed, else false. */ void ChartMatrixDisplayProperties::setGridLinesDisplayed(const bool displayGridLines) { m_displayGridLines = displayGridLines; } /** * Set scaling of the manual width and heights for matrix cells. * When image capture is performed and the user is capturing the * image in a size different than the actual window size, the matrix * cell width and heights need to be scaled so that they are the same * percentage width and height of the window. * * If the scale mode is manual, these scaling values are used by * the getCellWidth() and getCellHeight() to adjust the pixel width * and height for matrix cell drawing. * * NOTE: THIS METHOD SHOULD BE CALLED JUST BEFORE IMAGE CAPTURE * WHEN SCALING OF MATRIX CELL WIDTH AND HEIGHT IS NEEDED. * IMMEDIATELY AFTER IMAGE CAPTURE, THIS METHOD SHOULD BE CALLED * WITH A VALUE OF ONE FOR BOTH THE WINDOW WIDTH AND HEIGHT SCALING. */ void ChartMatrixDisplayProperties::setManualScaleModeWindowWidthHeightScaling(const float windowWidthScaling, const float windowHeightScaling) { s_manualScaleModeWindowWidthScaling = windowWidthScaling; s_manualScaleModeWindowHeightScaling = windowHeightScaling; } connectome-workbench-1.2.3+git41-gc4c6c90/src/Charting/ChartMatrixDisplayProperties.h000066400000000000000000000120301300200146000303010ustar00rootroot00000000000000#ifndef __CHART_MATRIX_DISPLAY_PROPERTIES_H__ #define __CHART_MATRIX_DISPLAY_PROPERTIES_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 "BrainConstants.h" #include "CaretObject.h" #include "ChartMatrixScaleModeEnum.h" #include "SceneableInterface.h" namespace caret { class AnnotationColorBar; class SceneClassAssistant; class ChartMatrixDisplayProperties : public CaretObject, public SceneableInterface { public: ChartMatrixDisplayProperties(); virtual ~ChartMatrixDisplayProperties(); ChartMatrixDisplayProperties(const ChartMatrixDisplayProperties& obj); ChartMatrixDisplayProperties& operator=(const ChartMatrixDisplayProperties& obj); float getCellWidth() const; void setCellWidth(const float cellSizeX); float getCellHeight() const; void setCellHeight(const float cellHeight); float getViewZooming() const; void setViewZooming(const float viewZooming); void getViewPanning(float viewPanningOut[2]) const; void setViewPanning(const float viewPanning[2]); ChartMatrixScaleModeEnum::Enum getScaleMode() const; void setScaleMode(const ChartMatrixScaleModeEnum::Enum scaleMode); bool isGridLinesDisplayed() const; void setGridLinesDisplayed(const bool displayGridLines); void resetPropertiesToDefault(); bool isSelectedRowColumnHighlighted() const; void setSelectedRowColumnHighlighted(const bool highlightStatus); AnnotationColorBar* getColorBar(); const AnnotationColorBar* getColorBar() 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); static void setManualScaleModeWindowWidthHeightScaling(const float windowWidthScaling, const float windowHeightScaling); // 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 implemetation 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 copyHelperChartMatrixDisplayProperties(const ChartMatrixDisplayProperties& obj); SceneClassAssistant* m_sceneAssistant; /** matrix cell width in pixels*/ float m_cellWidth; /** matrix cell height in pixels*/ float m_cellHeight; /** zooming for view of matrix*/ float m_viewZooming; /** panning for view of matrix*/ float m_viewPanning[2]; /** scale mode for view of matrix*/ ChartMatrixScaleModeEnum::Enum m_scaleMode; /** Highlight the selected row/column */ bool m_highlightSelectedRowColumn; /** Display grid lines */ bool m_displayGridLines; /** The color bar displayed in the graphics window */ AnnotationColorBar* m_colorBar; static float s_manualScaleModeWindowWidthScaling; static float s_manualScaleModeWindowHeightScaling; // ADD_NEW_MEMBERS_HERE }; #ifdef __CHART_MATRIX_DISPLAY_PROPERTIES_DECLARE__ float ChartMatrixDisplayProperties::s_manualScaleModeWindowWidthScaling = 1.0; float ChartMatrixDisplayProperties::s_manualScaleModeWindowHeightScaling = 1.0; #endif // __CHART_MATRIX_DISPLAY_PROPERTIES_DECLARE__ } // namespace #endif //__CHART_MATRIX_DISPLAY_PROPERTIES_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Charting/ChartMatrixLoadingDimensionEnum.cxx000066400000000000000000000263121300200146000312520ustar00rootroot00000000000000 /*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_MATRIX_LOADING_TYPE_ENUM_DECLARE__ #include "ChartMatrixLoadingDimensionEnum.h" #undef __CHART_MATRIX_LOADING_TYPE_ENUM_DECLARE__ #include "CaretAssert.h" using namespace caret; /** * \class caret::ChartMatrixLoadingDimensionEnum * \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_ChartMatrixLoadingDimensionEnumComboBox; * * Declare a slot that is called when user changes selection * private slots: * void ChartMatrixLoadingDimensionEnumComboBoxItemActivated(); * * Implementation File (.cxx) * Include the header files * #include "EnumComboBoxTemplate.h" * #include "ChartMatrixLoadingDimensionEnum.h" * * Instatiate: * m_ChartMatrixLoadingDimensionEnumComboBox = new EnumComboBoxTemplate(this); * m_ChartMatrixLoadingDimensionEnumComboBox->setup(); * * Get notified when the user changes the selection: * QObject::connect(m_ChartMatrixLoadingDimensionEnumComboBox, SIGNAL(itemActivated()), * this, SLOT(ChartMatrixLoadingDimensionEnumComboBoxItemActivated())); * * Update the selection: * m_ChartMatrixLoadingDimensionEnumComboBox->setSelectedItem(NEW_VALUE); * * Read the selection: * const ChartMatrixLoadingDimensionEnum::Enum VARIABLE = m_ChartMatrixLoadingDimensionEnumComboBox->getSelectedItem(); * */ /** * Constructor. * * @param enumValue * An enumerated value. * @param name * Name of enumerated value. * * @param guiName * User-friendly name for use in user-interface. */ ChartMatrixLoadingDimensionEnum::ChartMatrixLoadingDimensionEnum(const Enum enumValue, const AString& name, const AString& guiName) { this->enumValue = enumValue; this->integerCode = integerCodeCounter++; this->name = name; this->guiName = guiName; } /** * Destructor. */ ChartMatrixLoadingDimensionEnum::~ChartMatrixLoadingDimensionEnum() { } /** * Initialize the enumerated metadata. */ void ChartMatrixLoadingDimensionEnum::initialize() { if (initializedFlag) { return; } initializedFlag = true; enumData.push_back(ChartMatrixLoadingDimensionEnum(CHART_MATRIX_LOADING_BY_ROW, "CHART_MATRIX_LOADING_BY_ROW", "Row")); enumData.push_back(ChartMatrixLoadingDimensionEnum(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 ChartMatrixLoadingDimensionEnum* ChartMatrixLoadingDimensionEnum::findData(const Enum enumValue) { if (initializedFlag == false) initialize(); size_t num = enumData.size(); for (size_t i = 0; i < num; i++) { const ChartMatrixLoadingDimensionEnum* 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 ChartMatrixLoadingDimensionEnum::toName(Enum enumValue) { if (initializedFlag == false) initialize(); const ChartMatrixLoadingDimensionEnum* 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. */ ChartMatrixLoadingDimensionEnum::Enum ChartMatrixLoadingDimensionEnum::fromName(const AString& name, bool* isValidOut) { if (initializedFlag == false) initialize(); bool validFlag = false; Enum enumValue = ChartMatrixLoadingDimensionEnum::enumData[0].enumValue; for (std::vector::iterator iter = enumData.begin(); iter != enumData.end(); iter++) { const ChartMatrixLoadingDimensionEnum& 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 ChartMatrixLoadingDimensionEnum")); } return enumValue; } /** * Get a GUI string representation of the enumerated type. * @param enumValue * Enumerated value. * @return * String representing enumerated value. */ AString ChartMatrixLoadingDimensionEnum::toGuiName(Enum enumValue) { if (initializedFlag == false) initialize(); const ChartMatrixLoadingDimensionEnum* 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. */ ChartMatrixLoadingDimensionEnum::Enum ChartMatrixLoadingDimensionEnum::fromGuiName(const AString& guiName, bool* isValidOut) { if (initializedFlag == false) initialize(); bool validFlag = false; Enum enumValue = ChartMatrixLoadingDimensionEnum::enumData[0].enumValue; for (std::vector::iterator iter = enumData.begin(); iter != enumData.end(); iter++) { const ChartMatrixLoadingDimensionEnum& 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 ChartMatrixLoadingDimensionEnum")); } return enumValue; } /** * Get the integer code for a data type. * * @return * Integer code for data type. */ int32_t ChartMatrixLoadingDimensionEnum::toIntegerCode(Enum enumValue) { if (initializedFlag == false) initialize(); const ChartMatrixLoadingDimensionEnum* 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. */ ChartMatrixLoadingDimensionEnum::Enum ChartMatrixLoadingDimensionEnum::fromIntegerCode(const int32_t integerCode, bool* isValidOut) { if (initializedFlag == false) initialize(); bool validFlag = false; Enum enumValue = ChartMatrixLoadingDimensionEnum::enumData[0].enumValue; for (std::vector::iterator iter = enumData.begin(); iter != enumData.end(); iter++) { const ChartMatrixLoadingDimensionEnum& 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 ChartMatrixLoadingDimensionEnum")); } 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 ChartMatrixLoadingDimensionEnum::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 ChartMatrixLoadingDimensionEnum::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(ChartMatrixLoadingDimensionEnum::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 ChartMatrixLoadingDimensionEnum::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(ChartMatrixLoadingDimensionEnum::toGuiName(iter->enumValue)); } if (isSorted) { std::sort(allGuiNames.begin(), allGuiNames.end()); } } connectome-workbench-1.2.3+git41-gc4c6c90/src/Charting/ChartMatrixLoadingDimensionEnum.h000066400000000000000000000064031300200146000306760ustar00rootroot00000000000000#ifndef __CHART_MATRIX_LOADING_TYPE_ENUM_H__ #define __CHART_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 ChartMatrixLoadingDimensionEnum { public: /** * Enumerated values. */ enum Enum { /** Load data from column */ CHART_MATRIX_LOADING_BY_COLUMN, /** Load data from row */ CHART_MATRIX_LOADING_BY_ROW }; ~ChartMatrixLoadingDimensionEnum(); 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: ChartMatrixLoadingDimensionEnum(const Enum enumValue, const AString& name, const AString& guiName); static const ChartMatrixLoadingDimensionEnum* 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_MATRIX_LOADING_TYPE_ENUM_DECLARE__ std::vector ChartMatrixLoadingDimensionEnum::enumData; bool ChartMatrixLoadingDimensionEnum::initializedFlag = false; int32_t ChartMatrixLoadingDimensionEnum::integerCodeCounter = 0; #endif // __CHART_MATRIX_LOADING_TYPE_ENUM_DECLARE__ } // namespace #endif //__CHART_MATRIX_LOADING_TYPE_ENUM_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Charting/ChartMatrixScaleModeEnum.cxx000066400000000000000000000253371300200146000276710ustar00rootroot00000000000000 /*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_MATRIX_SCALE_MODE_ENUM_DECLARE__ #include "ChartMatrixScaleModeEnum.h" #undef __CHART_MATRIX_SCALE_MODE_ENUM_DECLARE__ #include "CaretAssert.h" using namespace caret; /** * \class caret::ChartMatrixScaleModeEnum * \brief Scale mode for viewing chart matrices. * * * * 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_chartMatrixScaleModeEnumComboBox; * * Declare a slot that is called when user changes selection * private slots: * void chartMatrixScaleModeEnumComboBoxItemActivated(); * * Implementation File (.cxx) * Include the header files * #include "EnumComboBoxTemplate.h" * #include "ChartMatrixScaleModeEnum.h" * * Instatiate: * m_chartMatrixScaleModeEnumComboBox = new EnumComboBoxTemplate(this); * m_chartMatrixScaleModeEnumComboBox->setup(); * * Get notified when the user changes the selection: * QObject::connect(m_chartMatrixScaleModeEnumComboBox, SIGNAL(itemActivated()), * this, SLOT(chartMatrixScaleModeEnumComboBoxItemActivated())); * * Update the selection: * m_chartMatrixScaleModeEnumComboBox->setSelectedItem(NEW_VALUE); * * Read the selection: * const ChartMatrixScaleModeEnum::Enum VARIABLE = m_chartMatrixScaleModeEnumComboBox->getSelectedItem(); * */ /** * Constructor. * * @param enumValue * An enumerated value. * @param name * Name of enumerated value. * * @param guiName * User-friendly name for use in user-interface. */ ChartMatrixScaleModeEnum::ChartMatrixScaleModeEnum(const Enum enumValue, const AString& name, const AString& guiName) { this->enumValue = enumValue; this->integerCode = integerCodeCounter++; this->name = name; this->guiName = guiName; } /** * Destructor. */ ChartMatrixScaleModeEnum::~ChartMatrixScaleModeEnum() { } /** * Initialize the enumerated metadata. */ void ChartMatrixScaleModeEnum::initialize() { if (initializedFlag) { return; } initializedFlag = true; enumData.push_back(ChartMatrixScaleModeEnum(CHART_MATRIX_SCALE_AUTO, "CHART_MATRIX_SCALE_AUTO", "Auto")); enumData.push_back(ChartMatrixScaleModeEnum(CHART_MATRIX_SCALE_MANUAL, "CHART_MATRIX_SCALE_MANUAL", "Manual")); } /** * 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 ChartMatrixScaleModeEnum* ChartMatrixScaleModeEnum::findData(const Enum enumValue) { if (initializedFlag == false) initialize(); size_t num = enumData.size(); for (size_t i = 0; i < num; i++) { const ChartMatrixScaleModeEnum* 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 ChartMatrixScaleModeEnum::toName(Enum enumValue) { if (initializedFlag == false) initialize(); const ChartMatrixScaleModeEnum* 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. */ ChartMatrixScaleModeEnum::Enum ChartMatrixScaleModeEnum::fromName(const AString& name, bool* isValidOut) { if (initializedFlag == false) initialize(); bool validFlag = false; Enum enumValue = ChartMatrixScaleModeEnum::enumData[0].enumValue; for (std::vector::iterator iter = enumData.begin(); iter != enumData.end(); iter++) { const ChartMatrixScaleModeEnum& 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 ChartMatrixScaleModeEnum")); } return enumValue; } /** * Get a GUI string representation of the enumerated type. * @param enumValue * Enumerated value. * @return * String representing enumerated value. */ AString ChartMatrixScaleModeEnum::toGuiName(Enum enumValue) { if (initializedFlag == false) initialize(); const ChartMatrixScaleModeEnum* 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. */ ChartMatrixScaleModeEnum::Enum ChartMatrixScaleModeEnum::fromGuiName(const AString& guiName, bool* isValidOut) { if (initializedFlag == false) initialize(); bool validFlag = false; Enum enumValue = ChartMatrixScaleModeEnum::enumData[0].enumValue; for (std::vector::iterator iter = enumData.begin(); iter != enumData.end(); iter++) { const ChartMatrixScaleModeEnum& 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 ChartMatrixScaleModeEnum")); } return enumValue; } /** * Get the integer code for a data type. * * @return * Integer code for data type. */ int32_t ChartMatrixScaleModeEnum::toIntegerCode(Enum enumValue) { if (initializedFlag == false) initialize(); const ChartMatrixScaleModeEnum* 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. */ ChartMatrixScaleModeEnum::Enum ChartMatrixScaleModeEnum::fromIntegerCode(const int32_t integerCode, bool* isValidOut) { if (initializedFlag == false) initialize(); bool validFlag = false; Enum enumValue = ChartMatrixScaleModeEnum::enumData[0].enumValue; for (std::vector::iterator iter = enumData.begin(); iter != enumData.end(); iter++) { const ChartMatrixScaleModeEnum& 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 ChartMatrixScaleModeEnum")); } 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 ChartMatrixScaleModeEnum::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 ChartMatrixScaleModeEnum::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(ChartMatrixScaleModeEnum::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 ChartMatrixScaleModeEnum::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(ChartMatrixScaleModeEnum::toGuiName(iter->enumValue)); } if (isSorted) { std::sort(allGuiNames.begin(), allGuiNames.end()); } } connectome-workbench-1.2.3+git41-gc4c6c90/src/Charting/ChartMatrixScaleModeEnum.h000066400000000000000000000062511300200146000273100ustar00rootroot00000000000000#ifndef __CHART_MATRIX_SCALE_MODE_ENUM_H__ #define __CHART_MATRIX_SCALE_MODE_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 ChartMatrixScaleModeEnum { public: /** * Enumerated values. */ enum Enum { /** Auto scale mode */ CHART_MATRIX_SCALE_AUTO, /** Manual scale mode */ CHART_MATRIX_SCALE_MANUAL }; ~ChartMatrixScaleModeEnum(); 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: ChartMatrixScaleModeEnum(const Enum enumValue, const AString& name, const AString& guiName); static const ChartMatrixScaleModeEnum* 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_MATRIX_SCALE_MODE_ENUM_DECLARE__ std::vector ChartMatrixScaleModeEnum::enumData; bool ChartMatrixScaleModeEnum::initializedFlag = false; int32_t ChartMatrixScaleModeEnum::integerCodeCounter = 0; #endif // __CHART_MATRIX_SCALE_MODE_ENUM_DECLARE__ } // namespace #endif //__CHART_MATRIX_SCALE_MODE_ENUM_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Charting/ChartModel.cxx000066400000000000000000000671031300200146000250600ustar00rootroot00000000000000 /*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_MODEL_DECLARE__ #include "ChartModel.h" #undef __CHART_MODEL_DECLARE__ #include "CaretAssert.h" #include "CaretLogger.h" #include "ChartAxis.h" #include "ChartData.h" #include "ChartDataCartesian.h" #include "ChartDataSource.h" #include "SceneClass.h" #include "SceneClassArray.h" #include "SceneClassAssistant.h" #include "SceneStringArray.h" using namespace caret; /** * \class caret::ChartModel * \brief Base class for chart model * \ingroup Charting * * Base class for chart model that displays chart data of the same type. * Some data types may require mututal exclusive display. */ /** * Constructor. * * @param chartDataType * Model type of chart that is managed. * @param chartSelectionMode * The selection mode. */ ChartModel::ChartModel(const ChartDataTypeEnum::Enum chartDataType, const ChartSelectionModeEnum::Enum chartSelectionMode) : CaretObject(), SceneableInterface(), m_chartDataType(chartDataType), m_chartSelectionMode(chartSelectionMode) { m_bottomAxis = NULL; m_leftAxis = NULL; m_rightAxis = NULL; m_topAxis = NULL; switch (m_chartSelectionMode) { case ChartSelectionModeEnum::CHART_SELECTION_MODE_ANY: m_maximumNumberOfChartDatasToDisplay = 5; break; case ChartSelectionModeEnum::CHART_SELECTION_MODE_SINGLE: m_maximumNumberOfChartDatasToDisplay = 1; break; } m_averageChartDisplaySelected = false; m_sceneAssistant = new SceneClassAssistant(); m_sceneAssistant->add("m_maximumNumberOfChartDatasToDisplay", &m_maximumNumberOfChartDatasToDisplay); m_sceneAssistant->add("m_averageChartDisplaySelected", &m_averageChartDisplaySelected); } /** * Destructor. */ ChartModel::~ChartModel() { delete m_sceneAssistant; removeAllAxes(); removeChartDataPrivate(); } /** * Remove the data data. * NOTE: This method cannot be called by constructor/destructor since * it calls a virtual method. */ void ChartModel::removeChartData() { removeChartDataPrivate(); updateAfterChartDataHasBeenAddedOrRemoved(); } /** * Remove the data data. */ void ChartModel::removeChartDataPrivate() { m_chartDatas.clear(); } /** * Remove all axes. */ void ChartModel::removeAllAxes() { if (m_bottomAxis != NULL) { delete m_bottomAxis; m_bottomAxis = NULL; } if (m_leftAxis != NULL) { delete m_leftAxis; m_leftAxis = NULL; } if (m_rightAxis != NULL) { delete m_rightAxis; m_rightAxis = NULL; } if (m_topAxis != NULL) { delete m_topAxis; m_topAxis = NULL; } } /** * Copy constructor. * @param obj * Object that is copied. */ ChartModel::ChartModel(const ChartModel& obj) : CaretObject(obj), SceneableInterface(obj), m_chartDataType(obj.m_chartDataType), m_chartSelectionMode(obj.m_chartSelectionMode) { this->copyHelperChartModel(obj); } /** * Assignment operator. * @param obj * Data copied from obj to this. * @return * Reference to this object. */ ChartModel& ChartModel::operator=(const ChartModel& obj) { if (this != &obj) { this->copyHelperChartModel(obj); } return *this; } /** * Helps with copying an object of this type. * @param obj * Object that is copied. */ void ChartModel::copyHelperChartModel(const ChartModel& obj) { m_chartDataType = obj.m_chartDataType; m_chartSelectionMode = obj.m_chartSelectionMode; m_maximumNumberOfChartDatasToDisplay = obj.m_maximumNumberOfChartDatasToDisplay; removeAllAxes(); if (obj.m_leftAxis != NULL) { setLeftAxis(obj.m_leftAxis->clone()); } if (obj.m_rightAxis != NULL) { setRightAxis(obj.m_rightAxis->clone()); } if (obj.m_bottomAxis != NULL) { setBottomAxis(obj.m_bottomAxis->clone()); } if (obj.m_topAxis != NULL) { setTopAxis(obj.m_topAxis->clone()); } m_averageChartDisplaySelected = obj.m_averageChartDisplaySelected; removeChartData(); m_chartDatas = obj.m_chartDatas; ChartData* selectedChartData = NULL; switch (m_chartSelectionMode) { case ChartSelectionModeEnum::CHART_SELECTION_MODE_ANY: break; case ChartSelectionModeEnum::CHART_SELECTION_MODE_SINGLE: { /* * If no item selected, choose oldest */ if (selectedChartData == NULL) { const int32_t numData = static_cast(m_chartDatas.size()); if (numData > 0) { const int32_t lastIndex = numData - 1; selectedChartData = m_chartDatas[lastIndex].data(); } } if (selectedChartData != NULL) { /* * Calling the setSelected method will ensure that * mutual exclusion for selection is maintained */ // selectedChartData->setSelected(true); } } break; } updateAfterChartDataHasBeenAddedOrRemoved(); } /** * @return The chart data type. */ ChartDataTypeEnum::Enum ChartModel::getChartDataType() const { return m_chartDataType; } /** * Get a description of this object's content. * @return String describing this object's content. */ AString ChartModel::toString() const { return "ChartModel"; } /** * @return Support for multiple chart display. Some chart types allow * it and others do not. */ ChartSelectionModeEnum::Enum ChartModel::getChartSelectionMode() const { return m_chartSelectionMode; } /** * Is this model empty (zero charts)? */ bool ChartModel::isEmpty() const { return m_chartDatas.empty(); } /** * Add a chart model to this controller. * * @param chartData * Model that is added. */ void ChartModel::addChartData(const QSharedPointer& chartData) { /* * If the display is limited to one chart and both the chart * in deque and the chart to add are both cartesian charts, * copy the chart color. */ if (getMaximumNumberOfChartDatasToDisplay() == 1) { if ( ! m_chartDatas.empty()) { ChartData* firstChartData = getChartDataAtIndex(0); ChartDataCartesian* cartesianChart = dynamic_cast(firstChartData); if (cartesianChart != NULL) { ChartDataCartesian* newCartesianChart = dynamic_cast(chartData.data()); if (newCartesianChart != NULL) { newCartesianChart->setColor(cartesianChart->getColor()); } } } } m_chartDatas.push_front(chartData); updateUsingMaximumNumberOfChartDatasToDisplay(); updateAfterChartDataHasBeenAddedOrRemoved(); } /** * Update so the number of charts is never greater than * the maximum. */ void ChartModel::updateUsingMaximumNumberOfChartDatasToDisplay() { /* * If needed, remove extra items at end of deque */ const int32_t numToRemove = (static_cast(m_chartDatas.size()) - m_maximumNumberOfChartDatasToDisplay); if (numToRemove > 0) { for (int32_t i = 0; i < numToRemove; i++) { m_chartDatas.pop_back(); } } switch (m_chartSelectionMode) { case ChartSelectionModeEnum::CHART_SELECTION_MODE_ANY: break; case ChartSelectionModeEnum::CHART_SELECTION_MODE_SINGLE: { /* * See if any item is selected */ bool haveSelectedItem = false; const int32_t numData = static_cast(m_chartDatas.size()); for (int32_t i = 0; i < numData; i++) { } /* * If no item selected, selected oldest */ if ( ! haveSelectedItem) { if (numData >= 0) { } } } break; } } /** * @return All chart datas (const method) */ std::vector ChartModel::getAllChartDatas() const { std::vector datasOut; for (std::deque >::const_iterator iter = m_chartDatas.begin(); iter != m_chartDatas.end(); iter++) { datasOut.push_back(iter->data()); } return datasOut; } /** * @return All chart datas. */ std::vector ChartModel::getAllChartDatas() { std::vector datasOut; for (std::deque >::const_iterator iter = m_chartDatas.begin(); iter != m_chartDatas.end(); iter++) { datasOut.push_back(iter->data()); } return datasOut; } /** * @return All SELECTED chart datas in the given tab. * @param tabIndex * Index of tab. */ std::vector ChartModel::getAllSelectedChartDatas(const int32_t tabIndex) const { std::vector datasOut; for (std::deque >::const_iterator iter = m_chartDatas.begin(); iter != m_chartDatas.end(); iter++) { ChartData* cd = iter->data(); if (cd->isSelected(tabIndex)) { datasOut.push_back(cd); } } return datasOut; } /** * @return Number of chart data. */ int32_t ChartModel::getNumberOfChartData() const { return m_chartDatas.size(); } /** * Get the chart data at the given index. * * @param chartDataIndex * Index of desired chart data. * @return * ChartData at the given index. */ ChartData* ChartModel::getChartDataAtIndex(const int32_t chartDataIndex) { CaretAssertVectorIndex(m_chartDatas, chartDataIndex); return m_chartDatas[chartDataIndex].data(); } /** * Get the chart data at the given index (const method). * * @param chartDataIndex * Index of desired chart data. * @return * ChartData at the given index. */ const ChartData* ChartModel::getChartDataAtIndex(const int32_t chartDataIndex) const { CaretAssertVectorIndex(m_chartDatas, chartDataIndex); return m_chartDatas[chartDataIndex].data(); } /** * Move the chart data at the given index by swapping with the chart * data at (chartDataIndex - 1). * * @param chartDataIndex * Index of the chart data. */ void ChartModel::moveChartDataAtIndexToOneLowerIndex(const int32_t chartDataIndex) { CaretAssertVectorIndex(m_chartDatas, chartDataIndex); if (chartDataIndex > 0) { std::swap(m_chartDatas[chartDataIndex], m_chartDatas[chartDataIndex - 1]); updateAfterChartDataHasBeenAddedOrRemoved(); } } /** * Move the chart data at the given index by swapping with the chart * data at (chartDataIndex + 1). * * @param chartDataIndex * Index of the chart data. */ void ChartModel::moveChartDataAtIndexToOneHigherIndex(const int32_t chartDataIndex) { CaretAssertVectorIndex(m_chartDatas, chartDataIndex); if (chartDataIndex < (static_cast(m_chartDatas.size()) - 1)) { std::swap(m_chartDatas[chartDataIndex], m_chartDatas[chartDataIndex + 1]); updateAfterChartDataHasBeenAddedOrRemoved(); } } /** * Remove the chart data at the given index. * * @param chartDataIndex * Index of the chart data. */ void ChartModel::removeChartAtIndex(const int32_t chartDataIndex) { CaretAssertVectorIndex(m_chartDatas, chartDataIndex); const int32_t lastIndex = m_chartDatas.size() - 1; for (int32_t i = chartDataIndex; i < lastIndex; i++) { CaretAssertVectorIndex(m_chartDatas, (i + 1)); m_chartDatas[i] = m_chartDatas[i + 1]; } m_chartDatas.resize(m_chartDatas.size() - 1); updateAfterChartDataHasBeenAddedOrRemoved(); } /** * @return Is average chart data display selected. * NOTE: Not all charts support an average. */ bool ChartModel::isAverageChartDisplaySelected() const { if (isAverageChartDisplaySupported()) { return m_averageChartDisplaySelected; } return false; } /** * Set the average chart data selected. * NOTE: Not all charts support an average. * * @param selected * New status. */ void ChartModel::setAverageChartDisplaySelected(const bool selected) { m_averageChartDisplaySelected = selected; } /** * @return The MAXIMUM number of chart models for display. * * NOTE: This value MAY BE GREATER than the actual number of chart models * that are available. */ int32_t ChartModel::getMaximumNumberOfChartDatasToDisplay() const { return m_maximumNumberOfChartDatasToDisplay; } /** * Set the number of most recent chart models for display. * * @param numberToDisplay * New number of most recent models for display. */ void ChartModel::setMaximumNumberOfChartDatasToDisplay(const int32_t numberToDisplay) { CaretAssert(numberToDisplay > 0); switch (m_chartSelectionMode) { case ChartSelectionModeEnum::CHART_SELECTION_MODE_ANY: m_maximumNumberOfChartDatasToDisplay = numberToDisplay; break; case ChartSelectionModeEnum::CHART_SELECTION_MODE_SINGLE: m_maximumNumberOfChartDatasToDisplay = 1; break; } updateUsingMaximumNumberOfChartDatasToDisplay(); updateAfterChartDataHasBeenAddedOrRemoved(); } /** * @return Chart Axis for left. NULL if axis not valid. */ ChartAxis* ChartModel::getLeftAxis() { return m_leftAxis; } /** * @return Chart Axis for left (const method). NULL if axis not valid. */ const ChartAxis* ChartModel::getLeftAxis() const { return m_leftAxis; } /** * Set the bottom axis. Replaces current axis. * * @param bottomAxis * New bottom axis. */ void ChartModel::setBottomAxis(ChartAxis* bottomAxis) { if (m_bottomAxis != NULL) { delete m_bottomAxis; } m_bottomAxis = bottomAxis; m_bottomAxis->setParentChartModel(this); } /** * @return Chart Axis for right. NULL if axis not valid. */ ChartAxis* ChartModel::getRightAxis() { return m_rightAxis; } /** * @return Chart Axis for right (const method). NULL if axis not valid. */ const ChartAxis* ChartModel::getRightAxis() const { return m_rightAxis; } /** * Set the right axis. Replaces current axis. * * @param rightAxis * New right axis. */ void ChartModel::setRightAxis(ChartAxis* rightAxis) { if (m_rightAxis != NULL) { delete m_rightAxis; } m_rightAxis = rightAxis; m_rightAxis->setParentChartModel(this); } /** * @return Chart Bottom for bottom. NULL if axis not valid. */ ChartAxis* ChartModel::getBottomAxis() { return m_bottomAxis; } /** * @return Chart Axis for bottom (const method). NULL if axis not valid. */ const ChartAxis* ChartModel::getBottomAxis() const { return m_bottomAxis; } /** * Set the left axis. Replaces current axis. * * @param leftAxis * New left axis. */ void ChartModel::setLeftAxis(ChartAxis* leftAxis) { if (m_leftAxis != NULL) { delete m_leftAxis; } m_leftAxis = leftAxis; m_leftAxis->setParentChartModel(this); } /** * @return Chart Axis for top. NULL if axis not valid. */ ChartAxis* ChartModel::getTopAxis() { return m_topAxis; } /** * @return Chart Axis for top (const method). NULL if axis not valid. */ const ChartAxis* ChartModel::getTopAxis() const { return m_topAxis; } /** * Set the top axis. Replaces current axis. * * @param topAxis * New top axis. */ void ChartModel::setTopAxis(ChartAxis* topAxis) { if (m_topAxis != NULL) { delete m_topAxis; } m_topAxis = topAxis; m_topAxis->setParentChartModel(this); } /** * Called by child ChartData when its selection status changes. * * If the selection mode is mutually exclusive, this method * ensures that no more than one child is selected. * * If the selection mode is NOT mutually exclusive, no * action is taken. */ void ChartModel::childChartDataSelectionChanged(ChartData* /*childChartData*/) { switch (m_chartSelectionMode) { case ChartSelectionModeEnum::CHART_SELECTION_MODE_ANY: break; case ChartSelectionModeEnum::CHART_SELECTION_MODE_SINGLE: // if (childChartData->isSelected()) { // const int32_t numChartData = static_cast(m_chartDatas.size()); // for (int32_t i = 0; i < numChartData; i++) { // ChartData* cd = m_chartDatas[i]; // if (cd != childChartData) { // cd->setSelected(false); // } // } // } break; } } /** * Update after chart data has been added or removed. */ void ChartModel::updateAfterChartDataHasBeenAddedOrRemoved() { if (m_bottomAxis != NULL) { m_bottomAxis->updateForAutoRangeScale(); } if (m_leftAxis != NULL) { m_leftAxis->updateForAutoRangeScale(); } if (m_rightAxis != NULL) { m_rightAxis->updateForAutoRangeScale(); } if (m_topAxis != NULL) { m_topAxis->updateForAutoRangeScale(); } } /** * Create a scene for an instance of a class. * * @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 the class' instance. * * @return Pointer to SceneClass object representing the state of * this object. Under some circumstances a NULL pointer may be * returned. Caller will take ownership of returned object. */ SceneClass* ChartModel::saveToScene(const SceneAttributes* sceneAttributes, const AString& instanceName) { const int32_t numChartData = static_cast(m_chartDatas.size()); if (numChartData <= 0) { return NULL; } SceneClass* sceneClass = new SceneClass(instanceName, "ChartModel", 2); m_sceneAssistant->saveMembers(sceneAttributes, sceneClass); if (numChartData > 0) { std::vector chartDataUniqueIDs; for (int32_t i = 0; i < numChartData; i++) { chartDataUniqueIDs.push_back(m_chartDatas[i]->getUniqueIdentifier()); } sceneClass->addStringArray("chartUniqueIDsArray", &chartDataUniqueIDs[0], numChartData); } if (m_bottomAxis != NULL) { sceneClass->addEnumeratedType("bottomAxisType", m_bottomAxis->getAxisType()); sceneClass->addChild(m_bottomAxis->saveToScene(sceneAttributes, "m_bottomAxis")); } if (m_leftAxis != NULL) { sceneClass->addEnumeratedType("leftAxisType", m_leftAxis->getAxisType()); sceneClass->addChild(m_leftAxis->saveToScene(sceneAttributes, "m_leftAxis")); } if (m_rightAxis != NULL) { sceneClass->addEnumeratedType("rightAxisType", m_rightAxis->getAxisType()); sceneClass->addChild(m_rightAxis->saveToScene(sceneAttributes, "m_rightAxis")); } if (m_topAxis != NULL) { sceneClass->addEnumeratedType("topAxisType", m_topAxis->getAxisType()); sceneClass->addChild(m_topAxis->saveToScene(sceneAttributes, "m_topAxis")); } saveSubClassDataToScene(sceneAttributes, sceneClass); return sceneClass; } /** * Restore the state of an instance of a class. * * @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. May be NULL for some types of scenes. */ void ChartModel::restoreFromScene(const SceneAttributes* sceneAttributes, const SceneClass* sceneClass) { if (sceneClass == NULL) { return; } const int32_t versionNumber = sceneClass->getVersionNumber(); removeChartData(); m_sceneAssistant->restoreMembers(sceneAttributes, sceneClass); removeAllAxes(); /* * Restore bottom axis */ const ChartAxisTypeEnum::Enum bottomAxisType = sceneClass->getEnumeratedTypeValue("bottomAxisType", ChartAxisTypeEnum::CHART_AXIS_TYPE_NONE); if (bottomAxisType != ChartAxisTypeEnum::CHART_AXIS_TYPE_NONE) { ChartAxis* axis = ChartAxis::newChartAxisForTypeAndLocation(bottomAxisType, ChartAxisLocationEnum::CHART_AXIS_LOCATION_BOTTOM); axis->restoreFromScene(sceneAttributes, sceneClass->getClass("m_bottomAxis")); setBottomAxis(axis); } /* * Restore left axis */ const ChartAxisTypeEnum::Enum leftAxisType = sceneClass->getEnumeratedTypeValue("leftAxisType", ChartAxisTypeEnum::CHART_AXIS_TYPE_NONE); if (leftAxisType != ChartAxisTypeEnum::CHART_AXIS_TYPE_NONE) { ChartAxis* axis = ChartAxis::newChartAxisForTypeAndLocation(leftAxisType, ChartAxisLocationEnum::CHART_AXIS_LOCATION_LEFT); axis->restoreFromScene(sceneAttributes, sceneClass->getClass("m_leftAxis")); setLeftAxis(axis); } if (versionNumber >= 2) { /* * Restore right axis */ const ChartAxisTypeEnum::Enum rightAxisType = sceneClass->getEnumeratedTypeValue("rightAxisType", ChartAxisTypeEnum::CHART_AXIS_TYPE_NONE); if (rightAxisType != ChartAxisTypeEnum::CHART_AXIS_TYPE_NONE) { ChartAxis* axis = ChartAxis::newChartAxisForTypeAndLocation(rightAxisType, ChartAxisLocationEnum::CHART_AXIS_LOCATION_RIGHT); axis->restoreFromScene(sceneAttributes, sceneClass->getClass("m_rightAxis")); setRightAxis(axis); } /* * Restore top axis */ const ChartAxisTypeEnum::Enum topAxisType = sceneClass->getEnumeratedTypeValue("topAxisType", ChartAxisTypeEnum::CHART_AXIS_TYPE_NONE); if (topAxisType != ChartAxisTypeEnum::CHART_AXIS_TYPE_NONE) { ChartAxis* axis = ChartAxis::newChartAxisForTypeAndLocation(topAxisType, ChartAxisLocationEnum::CHART_AXIS_LOCATION_TOP); axis->restoreFromScene(sceneAttributes, sceneClass->getClass("m_topAxis")); setTopAxis(axis); } } /* * Restore unique IDs of ChartData */ m_chartDataUniqueIDsRestoredFromScene.clear(); const ScenePrimitiveArray* chartUniqueIDsArray = sceneClass->getPrimitiveArray("chartUniqueIDsArray"); if (chartUniqueIDsArray != NULL) { const int32_t numElements = chartUniqueIDsArray->getNumberOfArrayElements(); m_chartDataUniqueIDsRestoredFromScene.resize(numElements); chartUniqueIDsArray->stringValues(m_chartDataUniqueIDsRestoredFromScene, ""); /* * Need to reverse order so that multiple charts are restored * in the correct order. Done here rather than when saving * scenes so that old scenes are restored correctly. */ if ( ! m_chartDataUniqueIDsRestoredFromScene.empty()) { std::reverse(m_chartDataUniqueIDsRestoredFromScene.begin(), m_chartDataUniqueIDsRestoredFromScene.end()); } } restoreSubClassDataFromScene(sceneAttributes, sceneClass); } /** * Restore chart data from scene by matching unique identifiers from charts * * @param restoredChartData * Chart data restored from scenes. */ void ChartModel::restoreChartDataFromScene(const SceneAttributes* sceneAttributes, std::vector >& restoredChartData) { const int32_t numChartUniqueIDsFromScene = static_cast(m_chartDataUniqueIDsRestoredFromScene.size()); if (numChartUniqueIDsFromScene > 0) { std::vector > chartDataVector(numChartUniqueIDsFromScene); /* * Need to keep chart data in same order as when scene was created */ for (int32_t i = 0; i < numChartUniqueIDsFromScene; i++) { for (std::vector >::iterator chartIter = restoredChartData.begin(); chartIter != restoredChartData.end(); chartIter++) { QSharedPointer cd = *chartIter; if (cd->getUniqueIdentifier() == m_chartDataUniqueIDsRestoredFromScene[i]) { if (cd->getChartDataType() == m_chartDataType) { chartDataVector[i] = cd; break; } } } } /* * Add the restored chart data */ for (int32_t i = 0; i < numChartUniqueIDsFromScene; i++) { QSharedPointer cd = chartDataVector[i]; if (cd.isNull()) { const AString msg("Failed to restore chart with Unique ID: " + m_chartDataUniqueIDsRestoredFromScene[i]); sceneAttributes->addToErrorMessage(msg); CaretLogSevere(msg); } else { addChartData(cd); } } } m_chartDataUniqueIDsRestoredFromScene.clear(); updateUsingMaximumNumberOfChartDatasToDisplay(); updateAfterChartDataHasBeenAddedOrRemoved(); } connectome-workbench-1.2.3+git41-gc4c6c90/src/Charting/ChartModel.h000066400000000000000000000143151300200146000245020ustar00rootroot00000000000000#ifndef __CHART_MODEL_H__ #define __CHART_MODEL_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 "CaretObject.h" #include "ChartDataTypeEnum.h" #include "ChartSelectionModeEnum.h" #include "SceneableInterface.h" namespace caret { class ChartAxis; class ChartData; class SceneClassAssistant; class ChartModel : public CaretObject, public SceneableInterface { public: ChartModel(const ChartDataTypeEnum::Enum chartDataType, const ChartSelectionModeEnum::Enum chartSelectionMode); ChartModel(const ChartModel&); ChartModel& operator=(const ChartModel&); virtual ~ChartModel(); void removeChartData(); ChartDataTypeEnum::Enum getChartDataType() const; ChartSelectionModeEnum::Enum getChartSelectionMode() const; void addChartData(const QSharedPointer& chartData); virtual int32_t getMaximumNumberOfChartDatasToDisplay() const; void setMaximumNumberOfChartDatasToDisplay(const int32_t numberToDisplay); bool isEmpty() const; std::vector getAllChartDatas() const; std::vector getAllChartDatas(); std::vector getAllSelectedChartDatas(const int32_t tabIndex) const; int32_t getNumberOfChartData() const; ChartData* getChartDataAtIndex(const int32_t chartDataIndex); const ChartData* getChartDataAtIndex(const int32_t chartDataIndex) const; void moveChartDataAtIndexToOneLowerIndex(const int32_t chartDataIndex); void moveChartDataAtIndexToOneHigherIndex(const int32_t chartDataIndex); void removeChartAtIndex(const int32_t chartDataIndex); /** * @return Is an average of data supported? */ virtual bool isAverageChartDisplaySupported() const = 0; /** * Get the average for charts in the given tab. * * @param tabIndex * Index of the tab. * * @return * The average chart data. Will return NULL if either * no data to average or model does not support an average. * Includes only those chart data that are displayed. */ virtual const ChartData* getAverageChartDataForDisplay(const int32_t tabIndex) const = 0; bool isAverageChartDisplaySelected() const; void setAverageChartDisplaySelected(const bool selected); ChartAxis* getLeftAxis(); const ChartAxis* getLeftAxis() const; ChartAxis* getRightAxis(); const ChartAxis* getRightAxis() const; ChartAxis* getBottomAxis(); const ChartAxis* getBottomAxis() const; ChartAxis* getTopAxis(); const ChartAxis* getTopAxis() const; virtual SceneClass* saveToScene(const SceneAttributes* sceneAttributes, const AString& instanceName); virtual void restoreFromScene(const SceneAttributes* sceneAttributes, const SceneClass* sceneClass); void restoreChartDataFromScene(const SceneAttributes* sceneAttributes, std::vector >& restoredChartData); // ADD_NEW_METHODS_HERE virtual AString toString() const; protected: void setBottomAxis(ChartAxis* leftAxis); void setLeftAxis(ChartAxis* leftAxis); void setRightAxis(ChartAxis* leftAxis); void setTopAxis(ChartAxis* leftAxis); virtual void updateAfterChartDataHasBeenAddedOrRemoved(); virtual void saveSubClassDataToScene(const SceneAttributes* sceneAttributes, SceneClass* sceneClass) = 0; virtual void restoreSubClassDataFromScene(const SceneAttributes* sceneAttributes, const SceneClass* sceneClass) = 0; private: void copyHelperChartModel(const ChartModel& obj); void childChartDataSelectionChanged(ChartData* childChartData); void removeChartDataPrivate(); void removeAllAxes(); void updateUsingMaximumNumberOfChartDatasToDisplay(); ChartDataTypeEnum::Enum m_chartDataType; ChartSelectionModeEnum::Enum m_chartSelectionMode; std::deque > m_chartDatas; int32_t m_maximumNumberOfChartDatasToDisplay; ChartAxis* m_leftAxis; ChartAxis* m_rightAxis; ChartAxis* m_bottomAxis; ChartAxis* m_topAxis; std::vector m_chartDataUniqueIDsRestoredFromScene; bool m_averageChartDisplaySelected; /** helps with scene save/restore */ SceneClassAssistant* m_sceneAssistant; // ADD_NEW_MEMBERS_HERE friend class ChartData; }; #ifdef __CHART_MODEL_DECLARE__ // #endif // __CHART_MODEL_DECLARE__ } // namespace #endif //__CHART_MODEL_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Charting/ChartModelCartesian.cxx000066400000000000000000000241121300200146000267030ustar00rootroot00000000000000 /*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 #define __CHART_MODEL_CARTESIAN_DECLARE__ #include "ChartModelCartesian.h" #undef __CHART_MODEL_CARTESIAN_DECLARE__ #include "CaretAssert.h" #include "ChartAxis.h" #include "ChartAxisCartesian.h" #include "ChartDataCartesian.h" #include "ChartPoint.h" #include "ChartScaleAutoRanging.h" #include "SceneClassAssistant.h" using namespace caret; /** * \class caret::ChartModelCartesian * \brief Chart Model for cartesian data. * \ingroup Charting */ /** * Constructor. * * @param chartDataDataType * The chart model data type. * @param dataAxisUnitsX * Units for the X-axis. * @param dataAxisUnitsY * Units for the Y-axis. */ ChartModelCartesian::ChartModelCartesian(const ChartDataTypeEnum::Enum chartDataType, const ChartAxisUnitsEnum::Enum dataAxisUnitsX, const ChartAxisUnitsEnum::Enum dataAxisUnitsY) : ChartModel(chartDataType, ChartSelectionModeEnum::CHART_SELECTION_MODE_ANY) { m_averageChartData = NULL; m_lineWidth = 1.0; setLeftAxis(ChartAxis::newChartAxisForTypeAndLocation(ChartAxisTypeEnum::CHART_AXIS_TYPE_CARTESIAN, ChartAxisLocationEnum::CHART_AXIS_LOCATION_LEFT)); getLeftAxis()->setAxisUnits(dataAxisUnitsY); getLeftAxis()->setVisible(true); setBottomAxis(ChartAxis::newChartAxisForTypeAndLocation(ChartAxisTypeEnum::CHART_AXIS_TYPE_CARTESIAN, ChartAxisLocationEnum::CHART_AXIS_LOCATION_BOTTOM)); getBottomAxis()->setAxisUnits(dataAxisUnitsX); getBottomAxis()->setVisible(true); m_sceneAssistant = new SceneClassAssistant(); m_sceneAssistant->add("m_lineWidth", &m_lineWidth); } /** * Destructor. */ ChartModelCartesian::~ChartModelCartesian() { if (m_averageChartData != NULL) { delete m_averageChartData; } delete m_sceneAssistant; } /** * Copy constructor. * @param obj * Object that is copied. */ ChartModelCartesian::ChartModelCartesian(const ChartModelCartesian& obj) : ChartModel(obj) { this->copyHelperChartModelCartesian(obj); } /** * Assignment operator. * @param obj * Data copied from obj to this. * @return * Reference to this object. */ ChartModelCartesian& ChartModelCartesian::operator=(const ChartModelCartesian& obj) { if (this != &obj) { ChartModel::operator=(obj); this->copyHelperChartModelCartesian(obj); } return *this; } /** * Helps with copying an object of this type. * @param obj * Object that is copied. */ void ChartModelCartesian::copyHelperChartModelCartesian(const ChartModelCartesian& /*obj*/) { if (m_averageChartData != NULL) { delete m_averageChartData; m_averageChartData = NULL; } } /** * @return Is an average of data supported? */ bool ChartModelCartesian::isAverageChartDisplaySupported() const { return true; } /** * Get the average for charts in the given tab. * * @param tabIndex * Index of the tab. * * @return * The average chart data. Will return NULL if either * no data to average or model does not support an average. * Includes only those chart data that are displayed. */ const ChartData* ChartModelCartesian::getAverageChartDataForDisplay(const int32_t tabIndex) const { if (m_averageChartData != NULL) { delete m_averageChartData; m_averageChartData = NULL; } /* * Data may be from multiple files so compute an average of those * that match the first (newest) file. */ const std::vector allData = getAllSelectedChartDatas(tabIndex); if ( ! allData.empty()) { std::vector xValue; std::vector ySum; int64_t averageCounter = 0; ChartDataTypeEnum::Enum firstChartDataType = ChartDataTypeEnum::CHART_DATA_TYPE_INVALID; bool firstFlag = true; for (std::vector::const_iterator iter = allData.begin(); iter != allData.end(); iter++) { const ChartData* chartData = *iter; const ChartDataCartesian* cartesianData = dynamic_cast(chartData); CaretAssert(cartesianData); const int64_t numPoints = cartesianData->getNumberOfPoints(); if (firstFlag) { if (numPoints > 0) { firstFlag = false; xValue.resize(numPoints); ySum.resize(numPoints); for (int64_t i = 0; i < numPoints; i++) { const ChartPoint* point = cartesianData->getPointAtIndex(i); xValue[i] = point->getX(); ySum[i] = point->getY(); } firstChartDataType = cartesianData->getChartDataType(); averageCounter = 1; } } else { if (numPoints == static_cast(ySum.size())) { for (int64_t i = 0; i < numPoints; i++) { const ChartPoint* point = cartesianData->getPointAtIndex(i); ySum[i] += point->getY(); } averageCounter++; } } } if (averageCounter > 0) { const int64_t numPoints = static_cast(ySum.size()); for (int64_t i = 0; i < numPoints; i++) { ySum[i] /= averageCounter; } m_averageChartData = dynamic_cast(ChartData::newChartDataForChartDataType(firstChartDataType)); for (int32_t i = 0; i < numPoints; i++) { m_averageChartData->addPoint(xValue[i], ySum[i]); } } } return m_averageChartData; } /** * Get the bounds of all of the data in themodel. * * @param boundsMinX * Minimum X-coordinate of all points. * @param boundsMaxX * Maximum X-coordinate of all points. * @param boundsMinY * Minimum Y-coordinate of all points. * @param boundsMaxY * Maximum Y-coordinate of all points. */ void ChartModelCartesian::getBounds(float& boundsMinX, float& boundsMaxX, float& boundsMinY, float& boundsMaxY) const { boundsMinX = 0.0; boundsMaxX = 0.0; boundsMinY = 0.0; boundsMaxY = 0.0; const std::vector allData = getAllChartDatas(); if ( ! allData.empty()) { boundsMinX = std::numeric_limits::max(); boundsMaxX = -std::numeric_limits::max(); boundsMinY = std::numeric_limits::max(); boundsMaxY = -std::numeric_limits::max(); for (std::vector::const_iterator iter = allData.begin(); iter != allData.end(); iter++) { const ChartData* chartData = *iter; const ChartDataCartesian* cartesianData = dynamic_cast(chartData); CaretAssert(cartesianData); float xMin, xMax, yMin, yMax; cartesianData->getBounds(xMin, xMax, yMin, yMax); if (xMin < boundsMinX) boundsMinX = xMin; if (xMax > boundsMaxX) boundsMaxX = xMax; if (yMin < boundsMinY) boundsMinY = yMin; if (yMax > boundsMaxY) boundsMaxY = yMax; } } } /** * Get the line width for the chart. * * @return * Line width for the chart. */ float ChartModelCartesian::getLineWidth() const { return m_lineWidth; } /** * Set the line width for the chart. * * param lineWidth * Line width for chart. */ void ChartModelCartesian::setLineWidth(const float lineWidth) { m_lineWidth = lineWidth; } /** * 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 ChartModelCartesian::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 ChartModelCartesian::restoreSubClassDataFromScene(const SceneAttributes* sceneAttributes, const SceneClass* sceneClass) { m_sceneAssistant->restoreMembers(sceneAttributes, sceneClass); } connectome-workbench-1.2.3+git41-gc4c6c90/src/Charting/ChartModelCartesian.h000066400000000000000000000057061300200146000263400ustar00rootroot00000000000000#ifndef __CHART_MODEL_CARTESIAN_H__ #define __CHART_MODEL_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 "ChartAxisUnitsEnum.h" #include "ChartModel.h" namespace caret { class ChartDataCartesian; class ChartModelCartesian : public ChartModel { public: ChartModelCartesian(const ChartDataTypeEnum::Enum chartDataType, const ChartAxisUnitsEnum::Enum dataAxisUnitsX, const ChartAxisUnitsEnum::Enum dataAxisUnitsY); virtual ~ChartModelCartesian(); ChartModelCartesian(const ChartModelCartesian& obj); ChartModelCartesian& operator=(const ChartModelCartesian& obj); void getBounds(float& boundsMinX, float& boundsMaxX, float& boundsMinY, float& boundsMaxY) const; virtual bool isAverageChartDisplaySupported() const; virtual const ChartData* getAverageChartDataForDisplay(const int32_t tabIndex) const; float getLineWidth() const; void setLineWidth(const float lineWidth); // ADD_NEW_METHODS_HERE protected: virtual void saveSubClassDataToScene(const SceneAttributes* sceneAttributes, SceneClass* sceneClass); virtual void restoreSubClassDataFromScene(const SceneAttributes* sceneAttributes, const SceneClass* sceneClass); private: void copyHelperChartModelCartesian(const ChartModelCartesian& obj); // void adjustAxisDefaultRange(float& minValue, // float& maxValue); SceneClassAssistant* m_sceneAssistant; mutable ChartDataCartesian* m_averageChartData; float m_lineWidth; // ADD_NEW_MEMBERS_HERE }; #ifdef __CHART_MODEL_CARTESIAN_DECLARE__ // #endif // __CHART_MODEL_CARTESIAN_DECLARE__ } // namespace #endif //__CHART_MODEL_CARTESIAN_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Charting/ChartModelDataSeries.cxx000066400000000000000000000043551300200146000270250ustar00rootroot00000000000000 /*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_MODEL_DATA_SERIES_DECLARE__ #include "ChartModelDataSeries.h" #undef __CHART_MODEL_DATA_SERIES_DECLARE__ #include "CaretAssert.h" using namespace caret; /** * \class caret::ChartModelDataSeries * \brief Chart model for line series charts. * \ingroup Charting */ /** * Constructor. */ ChartModelDataSeries::ChartModelDataSeries() : ChartModelCartesian(ChartDataTypeEnum::CHART_DATA_TYPE_LINE_DATA_SERIES, ChartAxisUnitsEnum::CHART_AXIS_UNITS_NONE, ChartAxisUnitsEnum::CHART_AXIS_UNITS_NONE) { } /** * Destructor. */ ChartModelDataSeries::~ChartModelDataSeries() { } /** * Copy constructor. * @param obj * Object that is copied. */ ChartModelDataSeries::ChartModelDataSeries(const ChartModelDataSeries& obj) : ChartModelCartesian(obj) { this->copyHelperChartModelDataSeries(obj); } /** * Assignment operator. * @param obj * Data copied from obj to this. * @return * Reference to this object. */ ChartModelDataSeries& ChartModelDataSeries::operator=(const ChartModelDataSeries& obj) { if (this != &obj) { ChartModelCartesian::operator=(obj); this->copyHelperChartModelDataSeries(obj); } return *this; } /** * Helps with copying an object of this type. * @param obj * Object that is copied. */ void ChartModelDataSeries::copyHelperChartModelDataSeries(const ChartModelDataSeries& /*obj*/) { } connectome-workbench-1.2.3+git41-gc4c6c90/src/Charting/ChartModelDataSeries.h000066400000000000000000000031731300200146000264470ustar00rootroot00000000000000#ifndef __CHART_MODEL_DATA_SERIES_H__ #define __CHART_MODEL_DATA_SERIES_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 "ChartModelCartesian.h" namespace caret { class ChartModelDataSeries : public ChartModelCartesian { public: ChartModelDataSeries(); virtual ~ChartModelDataSeries(); ChartModelDataSeries(const ChartModelDataSeries& obj); ChartModelDataSeries& operator=(const ChartModelDataSeries& obj); // ADD_NEW_METHODS_HERE private: void copyHelperChartModelDataSeries(const ChartModelDataSeries& obj); // ADD_NEW_MEMBERS_HERE }; #ifdef __CHART_MODEL_DATA_SERIES_DECLARE__ // #endif // __CHART_MODEL_DATA_SERIES_DECLARE__ } // namespace #endif //__CHART_MODEL_DATA_SERIES_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Charting/ChartModelFrequencySeries.cxx000066400000000000000000000045411300200146000301120ustar00rootroot00000000000000 /*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_MODEL_FREQUENCY_SERIES_DECLARE__ #include "ChartModelFrequencySeries.h" #undef __CHART_MODEL_FREQUENCY_SERIES_DECLARE__ #include "CaretAssert.h" using namespace caret; /** * \class caret::ChartModelFrequencySeries * \brief Chart model for frequency series charts. * \ingroup Charting */ /** * Constructor. */ ChartModelFrequencySeries::ChartModelFrequencySeries() : ChartModelCartesian(ChartDataTypeEnum::CHART_DATA_TYPE_LINE_FREQUENCY_SERIES, ChartAxisUnitsEnum::CHART_AXIS_UNITS_FREQUENCY_HERTZ, ChartAxisUnitsEnum::CHART_AXIS_UNITS_NONE) { } /** * Destructor. */ ChartModelFrequencySeries::~ChartModelFrequencySeries() { } /** * Copy constructor. * @param obj * Object that is copied. */ ChartModelFrequencySeries::ChartModelFrequencySeries(const ChartModelFrequencySeries& obj) : ChartModelCartesian(obj) { this->copyHelperChartModelFrequencySeries(obj); } /** * Assignment operator. * @param obj * Data copied from obj to this. * @return * Reference to this object. */ ChartModelFrequencySeries& ChartModelFrequencySeries::operator=(const ChartModelFrequencySeries& obj) { if (this != &obj) { ChartModelCartesian::operator=(obj); this->copyHelperChartModelFrequencySeries(obj); } return *this; } /** * Helps with copying an object of this type. * @param obj * Object that is copied. */ void ChartModelFrequencySeries::copyHelperChartModelFrequencySeries(const ChartModelFrequencySeries& /*obj*/) { } connectome-workbench-1.2.3+git41-gc4c6c90/src/Charting/ChartModelFrequencySeries.h000066400000000000000000000033011300200146000275300ustar00rootroot00000000000000#ifndef __CHART_MODEL_FREQUENCY_SERIES_H__ #define __CHART_MODEL_FREQUENCY_SERIES_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 "ChartModelCartesian.h" namespace caret { class ChartModelFrequencySeries : public ChartModelCartesian { public: ChartModelFrequencySeries(); virtual ~ChartModelFrequencySeries(); ChartModelFrequencySeries(const ChartModelFrequencySeries& obj); ChartModelFrequencySeries& operator=(const ChartModelFrequencySeries& obj); // ADD_NEW_METHODS_HERE private: void copyHelperChartModelFrequencySeries(const ChartModelFrequencySeries& obj); // ADD_NEW_MEMBERS_HERE }; #ifdef __CHART_MODEL_FREQUENCY_SERIES_DECLARE__ // #endif // __CHART_MODEL_FREQUENCY_SERIES_DECLARE__ } // namespace #endif //__CHART_MODEL_FREQUENCY_SERIES_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Charting/ChartModelTimeSeries.cxx000066400000000000000000000043651300200146000270530ustar00rootroot00000000000000 /*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_MODEL_TIME_SERIES_DECLARE__ #include "ChartModelTimeSeries.h" #undef __CHART_MODEL_TIME_SERIES_DECLARE__ #include "CaretAssert.h" using namespace caret; /** * \class caret::ChartModelTimeSeries * \brief Chart model for line series charts. * \ingroup Charting */ /** * Constructor. */ ChartModelTimeSeries::ChartModelTimeSeries() : ChartModelCartesian(ChartDataTypeEnum::CHART_DATA_TYPE_LINE_TIME_SERIES, ChartAxisUnitsEnum::CHART_AXIS_UNITS_TIME_SECONDS, ChartAxisUnitsEnum::CHART_AXIS_UNITS_NONE) { } /** * Destructor. */ ChartModelTimeSeries::~ChartModelTimeSeries() { } /** * Copy constructor. * @param obj * Object that is copied. */ ChartModelTimeSeries::ChartModelTimeSeries(const ChartModelTimeSeries& obj) : ChartModelCartesian(obj) { this->copyHelperChartModelTimeSeries(obj); } /** * Assignment operator. * @param obj * Data copied from obj to this. * @return * Reference to this object. */ ChartModelTimeSeries& ChartModelTimeSeries::operator=(const ChartModelTimeSeries& obj) { if (this != &obj) { ChartModelCartesian::operator=(obj); this->copyHelperChartModelTimeSeries(obj); } return *this; } /** * Helps with copying an object of this type. * @param obj * Object that is copied. */ void ChartModelTimeSeries::copyHelperChartModelTimeSeries(const ChartModelTimeSeries& /*obj*/) { } connectome-workbench-1.2.3+git41-gc4c6c90/src/Charting/ChartModelTimeSeries.h000066400000000000000000000031731300200146000264740ustar00rootroot00000000000000#ifndef __CHART_MODEL_TIME_SERIES_H__ #define __CHART_MODEL_TIME_SERIES_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 "ChartModelCartesian.h" namespace caret { class ChartModelTimeSeries : public ChartModelCartesian { public: ChartModelTimeSeries(); virtual ~ChartModelTimeSeries(); ChartModelTimeSeries(const ChartModelTimeSeries& obj); ChartModelTimeSeries& operator=(const ChartModelTimeSeries& obj); // ADD_NEW_METHODS_HERE private: void copyHelperChartModelTimeSeries(const ChartModelTimeSeries& obj); // ADD_NEW_MEMBERS_HERE }; #ifdef __CHART_MODEL_TIME_SERIES_DECLARE__ // #endif // __CHART_MODEL_TIME_SERIES_DECLARE__ } // namespace #endif //__CHART_MODEL_TIME_SERIES_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Charting/ChartPoint.cxx000066400000000000000000000050661300200146000251110ustar00rootroot00000000000000 /*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_POINT_DECLARE__ #include "ChartPoint.h" #undef __CHART_POINT_DECLARE__ #include "CaretAssert.h" using namespace caret; /** * \class caret::ChartPoint * \brief Point displayed in a chart. * \ingroup Charting */ /** * Constructor. * * @param x * X coordinate of point. * @param y * Y coordinate of point. */ ChartPoint::ChartPoint(const float x, const float y) : CaretObject() { m_xyz[0] = x; m_xyz[1] = y; m_xyz[2] = 0.0; } /** * Destructor. */ ChartPoint::~ChartPoint() { } /** * Copy constructor. * @param obj * Object that is copied. */ ChartPoint::ChartPoint(const ChartPoint& obj) : CaretObject(obj) { this->copyHelperChartPoint(obj); } /** * Assignment operator. * @param obj * Data copied from obj to this. * @return * Reference to this object. */ ChartPoint& ChartPoint::operator=(const ChartPoint& obj) { if (this != &obj) { CaretObject::operator=(obj); this->copyHelperChartPoint(obj); } return *this; } /** * Helps with copying an object of this type. * @param obj * Object that is copied. */ void ChartPoint::copyHelperChartPoint(const ChartPoint& obj) { m_xyz[0] = obj.m_xyz[0]; m_xyz[1] = obj.m_xyz[1]; m_xyz[2] = obj.m_xyz[2]; } /** * Get a description of this object's content. * @return String describing this object's content. */ AString ChartPoint::toString() const { return "ChartPoint"; } /** * @return Pointer to X & Y coordinates. */ const float* ChartPoint::getXY() const { return &m_xyz[0]; } /** * @return X coordinate. */ float ChartPoint::getX() const { return m_xyz[0]; } /** * @return Y coordinate. */ float ChartPoint::getY() const { return m_xyz[1]; } connectome-workbench-1.2.3+git41-gc4c6c90/src/Charting/ChartPoint.h000066400000000000000000000033061300200146000245310ustar00rootroot00000000000000#ifndef __CHART_POINT_H__ #define __CHART_POINT_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" namespace caret { class ChartPoint : public CaretObject { public: ChartPoint(const float x, const float y); virtual ~ChartPoint(); ChartPoint(const ChartPoint& obj); ChartPoint& operator=(const ChartPoint& obj); const float* getXY() const; float getX() const; float getY() const; // ADD_NEW_METHODS_HERE virtual AString toString() const; private: void copyHelperChartPoint(const ChartPoint& obj); float m_xyz[3]; // ADD_NEW_MEMBERS_HERE }; #ifdef __CHART_POINT_DECLARE__ // #endif // __CHART_POINT_DECLARE__ } // namespace #endif //__CHART_POINT_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Charting/ChartScaleAutoRanging.cxx000066400000000000000000000342611300200146000272050ustar00rootroot00000000000000 /*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 #define __CHART_SCALE_AUTO_RANGING_DECLARE__ #include "ChartScaleAutoRanging.h" #undef __CHART_SCALE_AUTO_RANGING_DECLARE__ #include "CaretAssert.h" #include "CaretLogger.h" using namespace caret; /** * \class caret::ChartScaleAutoRanging * \brief Adjust minimum and maximum value for auto ranging. * \ingroup Charting */ /** * Constructor. */ ChartScaleAutoRanging::ChartScaleAutoRanging() : CaretObject() { } /** * Destructor. */ ChartScaleAutoRanging::~ChartScaleAutoRanging() { } /** * Get a description of this object's content. * @return String describing this object's content. */ AString ChartScaleAutoRanging::toString() const { return "ChartScaleAutoRanging"; } static const double smallNegValue = -1.0e-6; static const double smallPosValue = 1.0e-6; /** * Is the value roughly zero? * * @param value * The value * @return * True if value is roughly zero, else false. */ bool ChartScaleAutoRanging::isZero(const double value) { if ((value >= smallNegValue) && (value < smallPosValue)) { return true; } return false; } ///** // * Adjust the magnitude of a value so that it is an integral // * value that is more/less positive (if positive) or more/less negative // * (if negative). // * // * @param valueIn // * The input value. // * @param rangeIn // * Range between minimum and maximum values. // * @param increaseMagnitudeFlag // * True if increasing value, else false. // * @return // * The adjusted value. // */ //double //ChartScaleAutoRanging::adjustValueMagnitude(const double valueIn, // const double rangeIn, // const bool increaseMagnitudeFlag) //{ // double outputValue = valueIn; // double range = rangeIn; // // if (isZero(valueIn)) { // outputValue = 0.0; // } // else if (isZero(range)) { // outputValue = valueIn; // } // else { // double value = valueIn; // double scaleAmount = 1.0; // // /* // * If value is less than one scale it up to be // * greater than or equal to one // */ // if (range < 1.0) { // double originalRange = range; // int32_t counter = 0; // while (range < 1.0) { // scaleAmount *= 10.0; // range = originalRange * scaleAmount; // counter++; // if (counter > 10) { // /* // * Should never happen but don't get stuck in loop // */ // break; // } // } // // value *= scaleAmount; // } // // /* // * Only works for positive values // * Will be converted back to negative later // */ // bool flipFlag = false; // if (value < 0.0) { // flipFlag = true; // value = -value; // } // // /* // * Determine size of value (10s, 100s, 1000s, etc) // */ // const double rangeLog10 = std::log10(range); // double logLessOne = std::floor(rangeLog10); // if (logLessOne >= 1) { // --logLessOne; // } // else { // logLessOne = 0.0; // } // // const int64_t intLogValue = std::pow(10.0, logLessOne); // // /* // * Adust the value // */ // double adjValue = (increaseMagnitudeFlag // ? (value + intLogValue) // : (value - intLogValue)); // // bool addSmallPercentFlag = true; // if (addSmallPercentFlag) { // const double percentAmount = range * 0.05; // adjValue = (increaseMagnitudeFlag // ? (adjValue + percentAmount) // : (adjValue - percentAmount)); // } // // double intValue = (int64_t)adjValue; // if (intLogValue >= 1) { // intValue = (int64_t)adjValue / intLogValue; // } // // double newValue = intValue * intLogValue; // // if (flipFlag) { // newValue = -newValue; // } // // outputValue = newValue / scaleAmount; // } // // return outputValue; //} // ///** // * Increase the value. // * // * @param valueIn // * The input value. // * @param range // * Range between minimum and maximum values. // * @return // * The adjusted value. // */ //double //ChartScaleAutoRanging::adjustValueUp(double valueIn, // const double range) //{ // return adjustValueMagnitude(valueIn, range, true); //} // ///** // * Decrease the value. // * // * @param valueIn // * The input value. // * @param range // * Range between minimum and maximum values. // * @return // * The adjusted value. // */ //double //ChartScaleAutoRanging::adjustValueDown(double valueIn, // const double range) //{ // return adjustValueMagnitude(valueIn, range, false); //} // ///** // * Adjust the data min/max values to that they extend a little // * beyond the rannge of the data but at an integral value. // * // * @param minValueInOut // * Minimum value that is adjusted. // * @param maxValueInOut // * Maximum value that is adjusted. // */ //void //ChartScaleAutoRanging::adjustAxisDefaultRange(float& minValueInOut, // float& maxValueInOut) //{ // double maxValue = maxValueInOut; // double minValue = minValueInOut; // // /* // * Handle instance where max value is greater than // * min value. // */ // bool invertedRangeFlag = false; // double dataRange = maxValue - minValue; // if (dataRange < 0.0) { // std::swap(minValue, maxValue); // dataRange = -dataRange; // invertedRangeFlag = true; // } // // if (maxValue > 0) { // maxValue = adjustValueUp(maxValue, dataRange); // } // else if (maxValue < 0.0) { // maxValue = adjustValueDown(maxValue, dataRange); // } // // if (minValue > 0) { // minValue = adjustValueDown(minValue, dataRange); // } // else if (minValue < 0.0) { // minValue = adjustValueUp(minValue, dataRange); // } // // CaretLogFine("Range was (" // + AString::number(minValueInOut) // + ", " // + AString::number(maxValueInOut) // + ") now (" // + AString::number(minValue) // + ", " // + AString::number(maxValue) // + ")"); // // if (invertedRangeFlag) { // minValueInOut = maxValue; // maxValueInOut = minValue; // } // else { // minValueInOut = minValue; // maxValueInOut = maxValue; // } //} //#ifdef POW_NOT_TRUSTWORTHY ///* if roundoff errors in pow cause problems, use this: */ // //double expt(a, n) //double a; //register int n; //{ // double x; // // x = 1.; // if (n>0) for (; n>0; n--) x *= a; // else for (; n<0; n++) x /= a; // return x; //} // //#else //# define expt(a, n) pow(a, (double)(n)) //#endif static double expt(double a, int32_t n) { return std::pow(a, static_cast(n)); } /* * nicenum: find a "nice" number approximately equal to x. * Round the number if round=1, take ceiling if round=0 */ static double nicenum(double x, int round) { int expv; /* exponent of x */ double f; /* fractional part of x */ double nf; /* nice, rounded fraction */ expv = static_cast(std::floor(std::log10(x))); f = x/expt(10., expv); /* between 1 and 10 */ if (round) if (f<1.5) nf = 1.; else if (f<3.) nf = 2.; else if (f<7.) nf = 5.; else nf = 10.; else if (f<=1.) nf = 1.; else if (f<=2.) nf = 2.; else if (f<=5.) nf = 5.; else nf = 10.; return nf*expt(10., expv); } //#define NTICK 5 /* desired number of tick marks */ //void //loose_label(double min, double max) //{ // char str[6], temp[20]; // int nfrac; // double d; /* tick mark spacing */ // double graphmin, graphmax; /* graph range min and max */ // double range, x; // // /* we expect min!=max */ // range = nicenum(max-min, 0); // d = nicenum(range/(NTICK-1), 1); // graphmin = floor(min/d)*d; // graphmax = ceil(max/d)*d; // nfrac = std::max(-floor(log10(d)), (double)0); /* # of fractional digits to show */ // sprintf(str, "%%.%df", nfrac); /* simplest axis labels */ // // printf("graphmin=%g graphmax=%g increment=%g\n", graphmin, graphmax, d); // for (x=graphmin; x(std::max(-std::floor(log10(d)), (double)0)); /* # of fractional digits to show */ // sprintf(str, "%%.%df", nfrac); /* simplest axis labels */ // // printf("graphmin=%g graphmax=%g increment=%g\n", graphmin, graphmax, d); // for (x=graphmin; x #endif // __CHART_SCALE_AUTO_RANGING_DECLARE__ } // namespace #endif //__CHART_SCALE_AUTO_RANGING_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Charting/ChartSelectionModeEnum.cxx000066400000000000000000000251551300200146000274000ustar00rootroot00000000000000 /*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_SELECTION_MODE_ENUM_DECLARE__ #include "ChartSelectionModeEnum.h" #undef __CHART_SELECTION_MODE_ENUM_DECLARE__ #include "CaretAssert.h" using namespace caret; /** * \class caret::ChartSelectionModeEnum * \brief Enumerated type for chart selection 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_chartSelectionModeEnumComboBox; * * Declare a slot that is called when user changes selection * private slots: * void chartSelectionModeEnumComboBoxItemActivated(); * * Implementation File (.cxx) * Include the header files * #include "EnumComboBoxTemplate.h" * #include "ChartSelectionModeEnum.h" * * Instatiate: * m_chartSelectionModeEnumComboBox = new EnumComboBoxTemplate(this); * m_chartSelectionModeEnumComboBox->setup(); * * Get notified when the user changes the selection: * QObject::connect(m_chartSelectionModeEnumComboBox, SIGNAL(itemActivated()), * this, SLOT(chartSelectionModeEnumComboBoxItemActivated())); * * Update the selection: * m_chartSelectionModeEnumComboBox->setSelectedItem(NEW_VALUE); * * Read the selection: * const ChartSelectionModeEnum::Enum VARIABLE = m_chartSelectionModeEnumComboBox->getSelectedItem(); * */ /** * Constructor. * * @param enumValue * An enumerated value. * @param name * Name of enumerated value. * * @param guiName * User-friendly name for use in user-interface. */ ChartSelectionModeEnum::ChartSelectionModeEnum(const Enum enumValue, const AString& name, const AString& guiName) { this->enumValue = enumValue; this->integerCode = integerCodeCounter++; this->name = name; this->guiName = guiName; } /** * Destructor. */ ChartSelectionModeEnum::~ChartSelectionModeEnum() { } /** * Initialize the enumerated metadata. */ void ChartSelectionModeEnum::initialize() { if (initializedFlag) { return; } initializedFlag = true; enumData.push_back(ChartSelectionModeEnum(CHART_SELECTION_MODE_ANY, "CHART_SELECTION_MODE_ANY", "Any item(s) can be selected")); enumData.push_back(ChartSelectionModeEnum(CHART_SELECTION_MODE_SINGLE, "CHART_SELECTION_MODE_SINGLE", "Only one item can be selected")); } /** * 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 ChartSelectionModeEnum* ChartSelectionModeEnum::findData(const Enum enumValue) { if (initializedFlag == false) initialize(); size_t num = enumData.size(); for (size_t i = 0; i < num; i++) { const ChartSelectionModeEnum* 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 ChartSelectionModeEnum::toName(Enum enumValue) { if (initializedFlag == false) initialize(); const ChartSelectionModeEnum* 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. */ ChartSelectionModeEnum::Enum ChartSelectionModeEnum::fromName(const AString& name, bool* isValidOut) { if (initializedFlag == false) initialize(); bool validFlag = false; Enum enumValue = ChartSelectionModeEnum::enumData[0].enumValue; for (std::vector::iterator iter = enumData.begin(); iter != enumData.end(); iter++) { const ChartSelectionModeEnum& 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 ChartSelectionModeEnum")); } return enumValue; } /** * Get a GUI string representation of the enumerated type. * @param enumValue * Enumerated value. * @return * String representing enumerated value. */ AString ChartSelectionModeEnum::toGuiName(Enum enumValue) { if (initializedFlag == false) initialize(); const ChartSelectionModeEnum* 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. */ ChartSelectionModeEnum::Enum ChartSelectionModeEnum::fromGuiName(const AString& guiName, bool* isValidOut) { if (initializedFlag == false) initialize(); bool validFlag = false; Enum enumValue = ChartSelectionModeEnum::enumData[0].enumValue; for (std::vector::iterator iter = enumData.begin(); iter != enumData.end(); iter++) { const ChartSelectionModeEnum& 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 ChartSelectionModeEnum")); } return enumValue; } /** * Get the integer code for a data type. * * @return * Integer code for data type. */ int32_t ChartSelectionModeEnum::toIntegerCode(Enum enumValue) { if (initializedFlag == false) initialize(); const ChartSelectionModeEnum* 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. */ ChartSelectionModeEnum::Enum ChartSelectionModeEnum::fromIntegerCode(const int32_t integerCode, bool* isValidOut) { if (initializedFlag == false) initialize(); bool validFlag = false; Enum enumValue = ChartSelectionModeEnum::enumData[0].enumValue; for (std::vector::iterator iter = enumData.begin(); iter != enumData.end(); iter++) { const ChartSelectionModeEnum& 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 ChartSelectionModeEnum")); } 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 ChartSelectionModeEnum::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 ChartSelectionModeEnum::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(ChartSelectionModeEnum::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 ChartSelectionModeEnum::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(ChartSelectionModeEnum::toGuiName(iter->enumValue)); } if (isSorted) { std::sort(allGuiNames.begin(), allGuiNames.end()); } } connectome-workbench-1.2.3+git41-gc4c6c90/src/Charting/ChartSelectionModeEnum.h000066400000000000000000000063111300200146000270160ustar00rootroot00000000000000#ifndef __CHART_SELECTION_MODE_ENUM_H__ #define __CHART_SELECTION_MODE_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 ChartSelectionModeEnum { public: /** * Enumerated values. */ enum Enum { /** Any number of items can be selected from none to all */ CHART_SELECTION_MODE_ANY, /** Only one item can be selected at any time */ CHART_SELECTION_MODE_SINGLE }; ~ChartSelectionModeEnum(); 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: ChartSelectionModeEnum(const Enum enumValue, const AString& name, const AString& guiName); static const ChartSelectionModeEnum* 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_SELECTION_MODE_ENUM_DECLARE__ std::vector ChartSelectionModeEnum::enumData; bool ChartSelectionModeEnum::initializedFlag = false; int32_t ChartSelectionModeEnum::integerCodeCounter = 0; #endif // __CHART_SELECTION_MODE_ENUM_DECLARE__ } // namespace #endif //__CHART_SELECTION_MODE_ENUM_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Cifti/000077500000000000000000000000001300200146000216025ustar00rootroot00000000000000connectome-workbench-1.2.3+git41-gc4c6c90/src/Cifti/CMakeLists.txt000066400000000000000000000017141300200146000243450ustar00rootroot00000000000000 # # Name of Project # PROJECT (Cifti) # # Need XML from Qt # SET(QT_DONT_USE_QTGUI) SET(QT_USE_QTNETWORK TRUE) # # Add QT for includes # INCLUDE (${QT_USE_FILE}) # # Create GIFTI Library # ADD_LIBRARY(Cifti CiftiInterface.h CiftiXMLOld.h CiftiXMLElements.h CiftiXMLReader.h CiftiXMLWriter.h CiftiFile.h CiftiXML.h CiftiMappingType.h CiftiBrainModelsMap.h CiftiLabelsMap.h CiftiParcelsMap.h CiftiScalarsMap.h CiftiSeriesMap.h CiftiVersion.h CiftiInterface.cxx CiftiXMLOld.cxx CiftiXMLElements.cxx CiftiXMLReader.cxx CiftiXMLWriter.cxx CiftiFile.cxx CiftiXML.cxx CiftiMappingType.cxx CiftiBrainModelsMap.cxx CiftiLabelsMap.cxx CiftiParcelsMap.cxx CiftiScalarsMap.cxx CiftiSeriesMap.cxx CiftiVersion.cxx ) # # Find Headers # INCLUDE_DIRECTORIES( ${CMAKE_SOURCE_DIR}/Annotations ${CMAKE_SOURCE_DIR}/FilesBase ${CMAKE_SOURCE_DIR}/Cifti ${CMAKE_SOURCE_DIR}/Palette ${CMAKE_SOURCE_DIR}/Gifti ${CMAKE_SOURCE_DIR}/Nifti ${CMAKE_SOURCE_DIR}/Xml ${CMAKE_SOURCE_DIR}/Common ) connectome-workbench-1.2.3+git41-gc4c6c90/src/Cifti/CiftiBrainModelsMap.cxx000066400000000000000000001124201300200146000261420ustar00rootroot00000000000000/*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 "CiftiBrainModelsMap.h" #include "CaretException.h" #include #include using namespace std; using namespace caret; void CiftiBrainModelsMap::addSurfaceModel(const int64_t& numberOfNodes, const StructureEnum::Enum& structure, const float* roi) { vector tempVector;//pass-through to the other addSurfaceModel after converting roi to vector of indices tempVector.reserve(numberOfNodes);//to make it allocate only once for (int64_t i = 0; i < numberOfNodes; ++i) { if (roi == NULL || roi[i] > 0.0f) { tempVector.push_back(i); } } addSurfaceModel(numberOfNodes, structure, tempVector); } void CiftiBrainModelsMap::addSurfaceModel(const int64_t& numberOfNodes, const StructureEnum::Enum& structure, const vector& nodeList) { if (m_surfUsed.find(structure) != m_surfUsed.end()) { throw CaretException("surface structures cannot be repeated in a brain models map"); } BrainModelPriv myModel; myModel.m_type = SURFACE; myModel.m_brainStructure = structure; myModel.m_surfaceNumberOfNodes = numberOfNodes; myModel.m_nodeIndices = nodeList; myModel.setupSurface(getNextStart());//do internal setup - also does error checking m_modelsInfo.push_back(myModel); m_surfUsed[structure] = m_modelsInfo.size() - 1; } void CiftiBrainModelsMap::BrainModelPriv::setupSurface(const int64_t& start) { if (m_surfaceNumberOfNodes < 1) { throw CaretException("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 } m_modelEnd = start + listSize;//one after last vector used(m_surfaceNumberOfNodes, false); m_nodeToIndexLookup = vector(m_surfaceNumberOfNodes, -1);//reset all to -1 to start for (int64_t i = 0; i < listSize; ++i) { if (m_nodeIndices[i] < 0) { throw CaretException("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"); } if (used[m_nodeIndices[i]]) { throw CaretException("vertex list contains reused index"); } used[m_nodeIndices[i]] = true; m_nodeToIndexLookup[m_nodeIndices[i]] = start + i; } } void CiftiBrainModelsMap::addVolumeModel(const StructureEnum::Enum& structure, const vector& ijkList) { if (m_volUsed.find(structure) != m_volUsed.end()) { throw CaretException("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 } if (listSize % 3 != 0) { throw CaretException("voxel list must have a length that is a multiple of 3"); } int64_t numElems = listSize / 3; const int64_t* dims = NULL; if (!m_ignoreVolSpace) { if (!m_haveVolumeSpace) { throw CaretException("you must set the volume space before adding volume models"); } dims = m_volSpace.getDims(); } CaretCompact3DLookup > tempLookup = m_voxelToIndexLookup;//a copy of the lookup should be faster than other methods of checking for overlap and repeat int64_t nextStart = getNextStart(); for (int64_t index = 0; index < numElems; ++index)//do all error checking before adding to lookup { 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"); } 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]) + ", " + 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"); } tempLookup.at(ijkList[index3], ijkList[index3 + 1], ijkList[index3 + 2]) = pair(nextStart + index, structure); } m_voxelToIndexLookup = tempLookup; BrainModelPriv myModel; myModel.m_type = VOXELS; myModel.m_brainStructure = structure; myModel.m_voxelIndicesIJK = ijkList; myModel.m_modelStart = nextStart; myModel.m_modelEnd = nextStart + numElems;//one after last m_modelsInfo.push_back(myModel); m_volUsed[structure] = m_modelsInfo.size() - 1; } void CiftiBrainModelsMap::clear() { m_modelsInfo.clear(); m_haveVolumeSpace = false; m_ignoreVolSpace = false; m_voxelToIndexLookup.clear(); m_surfUsed.clear(); m_volUsed.clear(); } int64_t CiftiBrainModelsMap::getIndexForNode(const int64_t& node, const StructureEnum::Enum& structure) const { CaretAssert(node >= 0); map::const_iterator iter = m_surfUsed.find(structure); if (iter == m_surfUsed.end()) { return -1; } CaretAssertVectorIndex(m_modelsInfo, iter->second); const BrainModelPriv& myModel = m_modelsInfo[iter->second]; if (node >= myModel.m_surfaceNumberOfNodes) return -1; CaretAssertVectorIndex(myModel.m_nodeToIndexLookup, node); return myModel.m_nodeToIndexLookup[node]; } int64_t CiftiBrainModelsMap::getIndexForVoxel(const int64_t* ijk, StructureEnum::Enum* structureOut) const { return getIndexForVoxel(ijk[0], ijk[1], ijk[2], structureOut); } int64_t CiftiBrainModelsMap::getIndexForVoxel(const int64_t& i, const int64_t& j, const int64_t& k, StructureEnum::Enum* structureOut) const { const pair* iter = m_voxelToIndexLookup.find(i, j, k);//the lookup tolerates weirdness like negatives if (iter == NULL) return -1; if (structureOut != NULL) *structureOut = iter->second; return iter->first; } CiftiBrainModelsMap::IndexInfo CiftiBrainModelsMap::getInfoForIndex(const int64_t index) const { CaretAssert(index >= 0 && index < getLength()); IndexInfo ret; int numModels = (int)m_modelsInfo.size(); int low = 0, high = numModels - 1;//bisection search while (low != high) { int guess = (low + high) / 2; if (m_modelsInfo[guess].m_modelEnd > index)//modelEnd is 1 after last valid index, equal to next start if there is a next { if (m_modelsInfo[guess].m_modelStart > index) { high = guess - 1; } else { high = guess; low = guess; } } else { low = guess + 1; } } CaretAssert(index >= m_modelsInfo[low].m_modelStart && index < m_modelsInfo[low].m_modelEnd);//otherwise we have a broken invariant ret.m_structure = m_modelsInfo[low].m_brainStructure; ret.m_type = m_modelsInfo[low].m_type; if (ret.m_type == SURFACE) { ret.m_surfaceNode = m_modelsInfo[low].m_nodeIndices[index - m_modelsInfo[low].m_modelStart]; } else { int64_t baseIndex = 3 * (index - m_modelsInfo[low].m_modelStart); ret.m_ijk[0] = m_modelsInfo[low].m_voxelIndicesIJK[baseIndex]; ret.m_ijk[1] = m_modelsInfo[low].m_voxelIndicesIJK[baseIndex + 1]; ret.m_ijk[2] = m_modelsInfo[low].m_voxelIndicesIJK[baseIndex + 2]; } return ret; } int64_t CiftiBrainModelsMap::getLength() const { return getNextStart(); } vector CiftiBrainModelsMap::getModelInfo() const { vector ret; int numModels = (int)m_modelsInfo.size(); ret.resize(numModels); for (int i = 0; i < numModels; ++i) { ret[i].m_structure = m_modelsInfo[i].m_brainStructure; ret[i].m_type = m_modelsInfo[i].m_type; ret[i].m_indexStart = m_modelsInfo[i].m_modelStart; ret[i].m_indexCount = m_modelsInfo[i].m_modelEnd - m_modelsInfo[i].m_modelStart; } return ret; } int64_t CiftiBrainModelsMap::getNextStart() const { if (m_modelsInfo.size() == 0) return 0; return m_modelsInfo.back().m_modelEnd;//NOTE: the models are sorted by their index range, so this works } const vector& CiftiBrainModelsMap::getNodeList(const StructureEnum::Enum& structure) const { 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 } CaretAssertVectorIndex(m_modelsInfo, iter->second); return m_modelsInfo[iter->second].m_nodeIndices; } vector CiftiBrainModelsMap::getSurfaceMap(const StructureEnum::Enum& structure) const { vector ret; map::const_iterator iter = m_surfUsed.find(structure); if (iter == m_surfUsed.end()) { throw CaretException("getSurfaceMap called for nonexistant structure");//also throw, for consistency } CaretAssertVectorIndex(m_modelsInfo, iter->second); const BrainModelPriv& myModel = m_modelsInfo[iter->second]; int64_t numUsed = (int64_t)myModel.m_nodeIndices.size(); ret.resize(numUsed); for (int64_t i = 0; i < numUsed; ++i) { ret[i].m_ciftiIndex = myModel.m_modelStart + i; ret[i].m_surfaceNode = myModel.m_nodeIndices[i]; } return ret; } int64_t CiftiBrainModelsMap::getSurfaceNumberOfNodes(const StructureEnum::Enum& structure) const { map::const_iterator iter = m_surfUsed.find(structure); if (iter == m_surfUsed.end()) { return -1; } CaretAssertVectorIndex(m_modelsInfo, iter->second); const BrainModelPriv& myModel = m_modelsInfo[iter->second]; return myModel.m_surfaceNumberOfNodes; } vector CiftiBrainModelsMap::getSurfaceStructureList() const { vector ret; ret.reserve(m_surfUsed.size());//we can use this to tell us how many there are, but it has reordered them int numModels = (int)m_modelsInfo.size(); for (int i = 0; i < numModels; ++i)//we need them in the order they occur in { if (m_modelsInfo[i].m_type == SURFACE) { ret.push_back(m_modelsInfo[i].m_brainStructure); } } return ret; } bool CiftiBrainModelsMap::hasSurfaceData(const StructureEnum::Enum& structure) const { map::const_iterator iter = m_surfUsed.find(structure); return (iter != m_surfUsed.end()); } vector CiftiBrainModelsMap::getFullVolumeMap() const { vector ret; int numModels = (int)m_modelsInfo.size(); for (int i = 0; i < numModels; ++i) { if (m_modelsInfo[i].m_type == VOXELS) { const BrainModelPriv& myModel = m_modelsInfo[i]; int64_t listSize = (int64_t)myModel.m_voxelIndicesIJK.size(); CaretAssert(listSize % 3 == 0); int64_t numUsed = listSize / 3; if (ret.size() == 0) ret.reserve(numUsed);//keep it from doing multiple expansion copies on the first model for (int64_t i = 0; i < numUsed; ++i) { int64_t i3 = i * 3; VolumeMap temp; temp.m_ciftiIndex = myModel.m_modelStart + i; temp.m_ijk[0] = myModel.m_voxelIndicesIJK[i3]; temp.m_ijk[1] = myModel.m_voxelIndicesIJK[i3 + 1]; temp.m_ijk[2] = myModel.m_voxelIndicesIJK[i3 + 2]; ret.push_back(temp); } } } return ret; } const VolumeSpace& CiftiBrainModelsMap::getVolumeSpace() const { CaretAssert(!m_ignoreVolSpace); if (!m_haveVolumeSpace) { throw CaretException("getVolumeSpace called when no volume space exists"); } return m_volSpace; } vector CiftiBrainModelsMap::getVolumeStructureList() const { vector ret; ret.reserve(m_volUsed.size());//we can use this to tell us how many there are, but it has reordered them int numModels = (int)m_modelsInfo.size(); for (int i = 0; i < numModels; ++i)//we need them in the order they occur in { if (m_modelsInfo[i].m_type == VOXELS) { ret.push_back(m_modelsInfo[i].m_brainStructure); } } return ret; } vector CiftiBrainModelsMap::getVolumeStructureMap(const StructureEnum::Enum& structure) const { vector ret; map::const_iterator iter = m_volUsed.find(structure); if (iter == m_volUsed.end()) { throw CaretException("getVolumeStructureMap called for nonexistant structure");//also throw, for consistency } CaretAssertVectorIndex(m_modelsInfo, iter->second); const BrainModelPriv& myModel = m_modelsInfo[iter->second]; int64_t listSize = (int64_t)myModel.m_voxelIndicesIJK.size(); CaretAssert(listSize % 3 == 0); int64_t numUsed = listSize / 3; ret.resize(numUsed); for (int64_t i = 0; i < numUsed; ++i) { int64_t i3 = i * 3; ret[i].m_ciftiIndex = myModel.m_modelStart + i; ret[i].m_ijk[0] = myModel.m_voxelIndicesIJK[i3]; ret[i].m_ijk[1] = myModel.m_voxelIndicesIJK[i3 + 1]; ret[i].m_ijk[2] = myModel.m_voxelIndicesIJK[i3 + 2]; } return ret; } const vector& CiftiBrainModelsMap::getVoxelList(const StructureEnum::Enum& structure) const { 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 } CaretAssertVectorIndex(m_modelsInfo, iter->second); return m_modelsInfo[iter->second].m_voxelIndicesIJK; } bool CiftiBrainModelsMap::hasVolumeData() const { return (m_volUsed.size() != 0); } bool CiftiBrainModelsMap::hasVolumeData(const StructureEnum::Enum& structure) const { map::const_iterator iter = m_volUsed.find(structure); return (iter != m_volUsed.end()); } void CiftiBrainModelsMap::setVolumeSpace(const VolumeSpace& space) { for (map::const_iterator iter = m_volUsed.begin(); iter != m_volUsed.end(); ++iter)//the main time this loop isn't empty is parsing cifti-1 { CaretAssertVectorIndex(m_modelsInfo, iter->second); const BrainModelPriv& myModel = m_modelsInfo[iter->second]; int64_t listSize = (int64_t)myModel.m_voxelIndicesIJK.size(); CaretAssert(listSize % 3 == 0); for (int64_t i3 = 0; i3 < listSize; i3 += 3) { 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"); } } } m_ignoreVolSpace = false; m_haveVolumeSpace = true; m_volSpace = space; } bool CiftiBrainModelsMap::operator==(const CiftiMappingType& rhs) const { if (rhs.getType() != getType()) return false; 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 false; if (m_haveVolumeSpace && (m_volSpace != myrhs.m_volSpace)) return false; return (m_modelsInfo == myrhs.m_modelsInfo);//NOTE: these are sorted by index range, so this works } bool CiftiBrainModelsMap::approximateMatch(const CiftiMappingType& rhs, QString* explanation) const { if (rhs.getType() != getType()) { if (explanation != NULL) *explanation = CiftiMappingType::mappingTypeToName(rhs.getType()) + " mapping never matches " + CiftiMappingType::mappingTypeToName(getType()); return false; } const CiftiBrainModelsMap& myrhs = dynamic_cast(rhs);//there is no user-specified metadata, but we want informative messages, so copy and modify the code from == 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) { if (explanation != NULL) *explanation = "one of the mappings has no volume data"; return false; } if (m_haveVolumeSpace && (m_volSpace != myrhs.m_volSpace)) { if (explanation != NULL) *explanation = "mappings have a different volume space"; return false; } if (m_modelsInfo != myrhs.m_modelsInfo) { if (explanation != NULL) *explanation = "mappings include different brainordinates"; return false; } return true; } bool CiftiBrainModelsMap::BrainModelPriv::operator==(const BrainModelPriv& rhs) const { if (m_brainStructure != rhs.m_brainStructure) return false; if (m_type != rhs.m_type) return false; if (m_modelStart != rhs.m_modelStart) return false; if (m_modelEnd != rhs.m_modelEnd) return false; if (m_type == SURFACE) { if (m_surfaceNumberOfNodes != rhs.m_surfaceNumberOfNodes) return false; int64_t listSize = (int64_t)m_nodeIndices.size(); CaretAssert((int64_t)rhs.m_nodeIndices.size() == listSize);//this should already be checked by start/end above for (int64_t i = 0; i < listSize; ++i) { if (m_nodeIndices[i] != rhs.m_nodeIndices[i]) return false; } } else { int64_t listSize = (int64_t)m_voxelIndicesIJK.size(); CaretAssert((int64_t)rhs.m_voxelIndicesIJK.size() == listSize);//this should already be checked by start/end above for (int64_t i = 0; i < listSize; ++i) { if (m_voxelIndicesIJK[i] != rhs.m_voxelIndicesIJK[i]) return false; } } return true; } void CiftiBrainModelsMap::readXML1(QXmlStreamReader& xml) { clear(); m_ignoreVolSpace = true;//because in cifti-1, the volume space is not in this element - so, we rely on CiftiXML to check for volume data, and set the volume space afterwards vector parsedModels; for (xml.readNext(); !xml.atEnd() && !xml.isEndElement(); xml.readNext()) { switch (xml.tokenType()) { case QXmlStreamReader::StartElement: { QStringRef name = xml.name(); if (name != "BrainModel") { throw CaretException("unexpected element in brain models map: " + name.toString()); } ParseHelperModel thisModel; thisModel.parseBrainModel1(xml); if (xml.hasError()) return; parsedModels.push_back(thisModel); break;//the readNext in the for will remove the BrainModel end element } default: break; } } if (xml.hasError()) return; sort(parsedModels.begin(), parsedModels.end()); int64_t numModels = (int64_t)parsedModels.size();//because we haven't checked them for unique values of BrainStructure yet...yeah, its paranoid int64_t curOffset = 0; for (int64_t i = 0; i < numModels; ++i) { if (parsedModels[i].m_offset != curOffset) { if (parsedModels[i].m_offset < curOffset) { throw CaretException("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"); } } curOffset += parsedModels[i].m_count; } for (int64_t i = 0; i < numModels; ++i) { if (parsedModels[i].m_type == SURFACE) { addSurfaceModel(parsedModels[i].m_surfaceNumberOfNodes, parsedModels[i].m_brainStructure, parsedModels[i].m_nodeIndices); } else { addVolumeModel(parsedModels[i].m_brainStructure, parsedModels[i].m_voxelIndicesIJK); } } m_ignoreVolSpace = false;//in case there are no voxels, but some will be added later CaretAssert(xml.isEndElement() && xml.name() == "MatrixIndicesMap"); } void CiftiBrainModelsMap::readXML2(QXmlStreamReader& xml) { clear(); vector parsedModels; for (xml.readNext(); !xml.atEnd() && !xml.isEndElement(); xml.readNext()) { switch (xml.tokenType()) { case QXmlStreamReader::StartElement: { QStringRef name = xml.name(); if (name == "BrainModel") { ParseHelperModel thisModel; thisModel.parseBrainModel2(xml); if (xml.hasError()) break; parsedModels.push_back(thisModel); } else if (name == "Volume") { if (m_haveVolumeSpace) { throw CaretException("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()); } break;//the readNext in the for will remove the BrainModel or Volume end element } default: break; } } if (xml.hasError()) return; sort(parsedModels.begin(), parsedModels.end()); int64_t numModels = (int64_t)parsedModels.size();//because we haven't checked them for unique values of BrainStructure yet...yeah, its paranoid int64_t curOffset = 0; for (int64_t i = 0; i < numModels; ++i) { if (parsedModels[i].m_offset != curOffset) { if (parsedModels[i].m_offset < curOffset) { throw CaretException("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"); } } curOffset += parsedModels[i].m_count; } for (int64_t i = 0; i < numModels; ++i) { if (parsedModels[i].m_type == SURFACE) { addSurfaceModel(parsedModels[i].m_surfaceNumberOfNodes, parsedModels[i].m_brainStructure, parsedModels[i].m_nodeIndices); } else { addVolumeModel(parsedModels[i].m_brainStructure, parsedModels[i].m_voxelIndicesIJK); } } CaretAssert(xml.isEndElement() && xml.name() == "MatrixIndicesMap"); } void CiftiBrainModelsMap::ParseHelperModel::parseBrainModel1(QXmlStreamReader& xml) { QXmlStreamAttributes attrs = xml.attributes(); if (!attrs.hasAttribute("ModelType")) { throw CaretException("BrainModel missing required attribute ModelType"); } QStringRef value = attrs.value("ModelType"); if (value == "CIFTI_MODEL_TYPE_SURFACE") { m_type = SURFACE; } else if (value == "CIFTI_MODEL_TYPE_VOXELS") { m_type = VOXELS; } else { throw CaretException("invalid value for ModelType: " + value.toString()); } if (!attrs.hasAttribute("BrainStructure")) { throw CaretException("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()); } if (!attrs.hasAttribute("IndexOffset")) { throw CaretException("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"); } if (!attrs.hasAttribute("IndexCount")) { throw CaretException("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"); } if (m_type == SURFACE) { if (!attrs.hasAttribute("SurfaceNumberOfNodes")) { throw CaretException("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"); } if (!xml.readNextStartElement())//special case in cifti-1 { m_nodeIndices.resize(m_count); for (int64_t i = 0; i < m_count; ++i) { m_nodeIndices[i] = i; } } else { if (xml.name() != "NodeIndices") { throw CaretException("unexpected element in BrainModel of SURFACE type: " + xml.name().toString()); } m_nodeIndices = readIndexArray(xml); xml.readNext();//remove the end element of NodeIndices } if (xml.hasError()) return; if ((int64_t)m_nodeIndices.size() != m_count) { throw CaretException("number of vertex indices does not match IndexCount"); } } else { if (!xml.readNextStartElement()) { throw CaretException("BrainModel requires a child element"); } if (xml.name() != "VoxelIndicesIJK") { throw CaretException("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"); } if ((int64_t)m_voxelIndicesIJK.size() != m_count * 3) { throw CaretException("number of voxel indices does not match IndexCount"); } xml.readNext();//remove the end element of VoxelIndicesIJK } while (!xml.atEnd() && !xml.isEndElement())//locate the end element of BrainModel { switch(xml.readNext()) { case QXmlStreamReader::StartElement: throw CaretException("unexpected second element in BrainModel: " + xml.name().toString()); default: break; } } CaretAssert(xml.isEndElement() && xml.name() == "BrainModel"); } void CiftiBrainModelsMap::ParseHelperModel::parseBrainModel2(QXmlStreamReader& xml) { QXmlStreamAttributes attrs = xml.attributes(); if (!attrs.hasAttribute("ModelType")) { throw CaretException("BrainModel missing required attribute ModelType"); } QStringRef value = attrs.value("ModelType"); if (value == "CIFTI_MODEL_TYPE_SURFACE") { m_type = SURFACE; } else if (value == "CIFTI_MODEL_TYPE_VOXELS") { m_type = VOXELS; } else { throw CaretException("invalid value for ModelType: " + value.toString()); } if (!attrs.hasAttribute("BrainStructure")) { throw CaretException("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()); } if (!attrs.hasAttribute("IndexOffset")) { throw CaretException("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"); } if (!attrs.hasAttribute("IndexCount")) { throw CaretException("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"); } if (m_type == SURFACE) { if (!attrs.hasAttribute("SurfaceNumberOfVertices")) { throw CaretException("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"); } if (!xml.readNextStartElement()) { throw CaretException("BrainModel requires a child element"); } if (xml.name() != "VertexIndices") { throw CaretException("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"); } xml.readNext();//remove the end element of NodeIndices } else { if (!xml.readNextStartElement()) { throw CaretException("BrainModel requires a child element"); } if (xml.name() != "VoxelIndicesIJK") { throw CaretException("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"); } if ((int64_t)m_voxelIndicesIJK.size() != m_count * 3) { throw CaretException("number of voxel indices does not match IndexCount"); } xml.readNext();//remove the end element of VoxelIndicesIJK } while (!xml.atEnd() && !xml.isEndElement())//locate the end element of BrainModel { switch(xml.readNext()) { case QXmlStreamReader::StartElement: throw CaretException("unexpected second element in BrainModel: " + xml.name().toString()); default: break; } } CaretAssert(xml.isEndElement() && xml.name() == "BrainModel"); } vector CiftiBrainModelsMap::ParseHelperModel::readIndexArray(QXmlStreamReader& xml) { vector ret; QString text = xml.readElementText();//raises error if it encounters a start element if (xml.hasError()) return ret; QStringList separated = text.split(QRegExp("\\s+"), QString::SkipEmptyParts); int64_t numElems = separated.size(); ret.reserve(numElems); for (int64_t i = 0; i < numElems; ++i) { bool ok = false; ret.push_back(separated[i].toLongLong(&ok)); if (!ok) { throw CaretException("found noninteger in index array: " + separated[i]); } if (ret.back() < 0) { throw CaretException("found negative integer in index array: " + separated[i]); } } return ret; } void CiftiBrainModelsMap::writeXML1(QXmlStreamWriter& xml) const { CaretAssert(!m_ignoreVolSpace); xml.writeAttribute("IndicesMapToDataType", "CIFTI_INDEX_TYPE_BRAIN_MODELS"); int numModels = (int)m_modelsInfo.size(); for (int i = 0; i < numModels; ++i) { const BrainModelPriv& myModel = m_modelsInfo[i]; xml.writeStartElement("BrainModel"); xml.writeAttribute("IndexOffset", QString::number(myModel.m_modelStart)); xml.writeAttribute("IndexCount", QString::number(myModel.m_modelEnd - myModel.m_modelStart)); xml.writeAttribute("BrainStructure", StructureEnum::toCiftiName(myModel.m_brainStructure)); if (myModel.m_type == SURFACE) { xml.writeAttribute("ModelType", "CIFTI_MODEL_TYPE_SURFACE"); xml.writeAttribute("SurfaceNumberOfNodes", QString::number(myModel.m_surfaceNumberOfNodes)); xml.writeStartElement("NodeIndices"); QString text = ""; int64_t numNodes = (int64_t)myModel.m_nodeIndices.size(); for (int64_t j = 0; j < numNodes; ++j) { if (j != 0) text += " "; text += QString::number(myModel.m_nodeIndices[j]); } xml.writeCharacters(text); xml.writeEndElement(); } else { xml.writeAttribute("ModelType", "CIFTI_MODEL_TYPE_VOXELS"); xml.writeStartElement("VoxelIndicesIJK"); QString text = ""; int64_t listSize = (int64_t)myModel.m_voxelIndicesIJK.size(); CaretAssert(listSize % 3 == 0); for (int64_t j = 0; j < listSize; j += 3) { text += QString::number(myModel.m_voxelIndicesIJK[j]) + " " + QString::number(myModel.m_voxelIndicesIJK[j + 1]) + " " + QString::number(myModel.m_voxelIndicesIJK[j + 2]) + "\n"; } xml.writeCharacters(text); xml.writeEndElement(); } xml.writeEndElement(); } } void CiftiBrainModelsMap::writeXML2(QXmlStreamWriter& xml) const { CaretAssert(!m_ignoreVolSpace); xml.writeAttribute("IndicesMapToDataType", "CIFTI_INDEX_TYPE_BRAIN_MODELS"); if (hasVolumeData())//could be m_haveVolumeSpace if we want to be able to write a volspace without having voxels, but that seems silly { m_volSpace.writeCiftiXML2(xml); } int numModels = (int)m_modelsInfo.size(); for (int i = 0; i < numModels; ++i) { const BrainModelPriv& myModel = m_modelsInfo[i]; xml.writeStartElement("BrainModel"); xml.writeAttribute("IndexOffset", QString::number(myModel.m_modelStart)); xml.writeAttribute("IndexCount", QString::number(myModel.m_modelEnd - myModel.m_modelStart)); xml.writeAttribute("BrainStructure", StructureEnum::toCiftiName(myModel.m_brainStructure)); if (myModel.m_type == SURFACE) { xml.writeAttribute("ModelType", "CIFTI_MODEL_TYPE_SURFACE"); xml.writeAttribute("SurfaceNumberOfVertices", QString::number(myModel.m_surfaceNumberOfNodes)); xml.writeStartElement("VertexIndices"); QString text = ""; int64_t numNodes = (int64_t)myModel.m_nodeIndices.size(); for (int64_t j = 0; j < numNodes; ++j) { if (j != 0) text += " "; text += QString::number(myModel.m_nodeIndices[j]); } xml.writeCharacters(text); xml.writeEndElement(); } else { xml.writeAttribute("ModelType", "CIFTI_MODEL_TYPE_VOXELS"); xml.writeStartElement("VoxelIndicesIJK"); QString text = ""; int64_t listSize = (int64_t)myModel.m_voxelIndicesIJK.size(); CaretAssert(listSize % 3 == 0); for (int64_t j = 0; j < listSize; j += 3) { text += QString::number(myModel.m_voxelIndicesIJK[j]) + " " + QString::number(myModel.m_voxelIndicesIJK[j + 1]) + " " + QString::number(myModel.m_voxelIndicesIJK[j + 2]) + "\n"; } xml.writeCharacters(text); xml.writeEndElement(); } xml.writeEndElement(); } } connectome-workbench-1.2.3+git41-gc4c6c90/src/Cifti/CiftiBrainModelsMap.h000066400000000000000000000147731300200146000256030ustar00rootroot00000000000000#ifndef __CIFTI_BRAIN_MODELS_MAP_H__ #define __CIFTI_BRAIN_MODELS_MAP_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 "CiftiMappingType.h" #include "CaretCompact3DLookup.h" #include "StructureEnum.h" #include "VolumeSpace.h" #include #include #include namespace caret { class CiftiBrainModelsMap : public CiftiMappingType { public: enum ModelType { SURFACE, VOXELS }; struct SurfaceMap { int64_t m_ciftiIndex; int64_t m_surfaceNode; }; struct VolumeMap { int64_t m_ciftiIndex; int64_t m_ijk[3]; }; struct ModelInfo { ModelType m_type; StructureEnum::Enum m_structure; int64_t m_indexStart, m_indexCount;//these are intended only for summary info, use getSurfaceMap, etc for the index to vertex/voxel mappings }; struct IndexInfo { ModelType m_type; StructureEnum::Enum m_structure; int64_t m_surfaceNode;//only one of these two will be valid int64_t m_ijk[3]; }; bool hasVolumeData() const; bool hasVolumeData(const StructureEnum::Enum& structure) const; bool hasSurfaceData(const StructureEnum::Enum& structure) const; int64_t getIndexForNode(const int64_t& node, const StructureEnum::Enum& structure) const; int64_t getIndexForVoxel(const int64_t* ijk, StructureEnum::Enum* structureOut = NULL) const; int64_t getIndexForVoxel(const int64_t& i, const int64_t& j, const int64_t& k, StructureEnum::Enum* structureOut = NULL) const; IndexInfo getInfoForIndex(const int64_t index) const; std::vector getSurfaceMap(const StructureEnum::Enum& structure) const; std::vector getFullVolumeMap() const; std::vector getVolumeStructureMap(const StructureEnum::Enum& structure) const; const VolumeSpace& getVolumeSpace() const; int64_t getSurfaceNumberOfNodes(const StructureEnum::Enum& structure) const; std::vector getSurfaceStructureList() const; std::vector getVolumeStructureList() const; const std::vector& getNodeList(const StructureEnum::Enum& structure) const;//useful for copying mappings to a new dense mapping const std::vector& getVoxelList(const StructureEnum::Enum& structure) const; std::vector getModelInfo() const; CiftiBrainModelsMap() { m_haveVolumeSpace = false; m_ignoreVolSpace = false; } void addSurfaceModel(const int64_t& numberOfNodes, const StructureEnum::Enum& structure, const float* roi = NULL); void addSurfaceModel(const int64_t& numberOfNodes, const StructureEnum::Enum& structure, const std::vector& nodeList); void addVolumeModel(const StructureEnum::Enum& structure, const std::vector& ijkList); void setVolumeSpace(const VolumeSpace& space); void clear(); CiftiMappingType* clone() const { return new CiftiBrainModelsMap(*this); } MappingType getType() const { return BRAIN_MODELS; } int64_t getLength() const; bool operator==(const CiftiMappingType& rhs) const; bool approximateMatch(const CiftiMappingType& rhs, QString* explanation = NULL) const; void readXML1(QXmlStreamReader& xml); void readXML2(QXmlStreamReader& xml); void writeXML1(QXmlStreamWriter& xml) const; void writeXML2(QXmlStreamWriter& xml) const; private: struct BrainModelPriv { ModelType m_type; StructureEnum::Enum m_brainStructure; int64_t m_surfaceNumberOfNodes; std::vector m_nodeIndices; std::vector m_voxelIndicesIJK; int64_t m_modelStart, m_modelEnd;//stuff only needed for optimization - models are kept in sorted order by their index ranges std::vector m_nodeToIndexLookup; bool operator==(const BrainModelPriv& rhs) const; bool operator!=(const BrainModelPriv& rhs) const { return !((*this) == rhs); } void setupSurface(const int64_t& start); }; VolumeSpace m_volSpace; bool m_haveVolumeSpace, m_ignoreVolSpace;//second is needed for parsing cifti-1 std::vector m_modelsInfo; std::map m_surfUsed, m_volUsed; CaretCompact3DLookup > m_voxelToIndexLookup;//make one unified lookup rather than separate lookups per volume structure int64_t getNextStart() const; struct ParseHelperModel {//specifically to allow the parsed elements to be sorted before using addSurfaceModel/addVolumeModel ModelType m_type; StructureEnum::Enum m_brainStructure; int64_t m_surfaceNumberOfNodes; std::vector m_nodeIndices; std::vector m_voxelIndicesIJK; int64_t m_offset, m_count; bool operator<(const ParseHelperModel& rhs) const { if (m_offset < rhs.m_offset) return true; if (m_offset > rhs.m_offset) return false;//get the common cases first if (m_count < rhs.m_count) return true;//in case we have a zero-length model - this shouldn't happen, usually return false; } void parseBrainModel1(QXmlStreamReader& xml); void parseBrainModel2(QXmlStreamReader& xml); static std::vector readIndexArray(QXmlStreamReader& xml); }; }; } #endif //__CIFTI_BRAIN_MODELS_MAP_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Cifti/CiftiFile.cxx000066400000000000000000001053111300200146000241650ustar00rootroot00000000000000/*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 "CiftiFile.h" #include "ByteOrderEnum.h" #include "CaretAssert.h" #include "CaretHttpManager.h" #include "CaretLogger.h" #include "DataFileException.h" #include "FileInformation.h" #include "MultiDimArray.h" #include "MultiDimIterator.h" #include "NiftiIO.h" using namespace std; using namespace caret; //private implementation classes namespace { class CiftiOnDiskImpl : public CiftiFile::WriteImplInterface { mutable NiftiIO m_nifti;//because file objects aren't stateless (current position), so reading "changes" them 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 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; } QString getFilename() const { return m_nifti.getFilename(); } 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); }; class CiftiMemoryImpl : public CiftiFile::WriteImplInterface { MultiDimArray m_array; public: CiftiMemoryImpl(const CiftiXML& xml); void getRow(float* dataOut, const std::vector& indexSelect, const bool& tolerateShortRead) const; void getColumn(float* dataOut, const int64_t& index) const; bool isInMemory() const { return true; } void setRow(const float* dataIn, const std::vector& indexSelect); void setColumn(const float* dataIn, const int64_t& index); }; class CiftiXnatImpl : public CiftiFile::ReadImplInterface { CiftiXML m_xml;//because we need to parse it to check the dimensions anyway CaretHttpRequest m_baseRequest; void init(const QString& url); void getReqAsFloats(float* data, const int64_t& dataSize, CaretHttpRequest& request) const; int64_t getSizeFromReq(CaretHttpRequest& request); public: CiftiXnatImpl(const QString& url, const QString& user, const QString& pass); CiftiXnatImpl(const QString& url);//reuse existing user/pass, or access non-protected url - in the future, maybe only the second use (private http manager) 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; } }; bool shouldSwap(const CiftiFile::ENDIAN& endian) { if (ByteSwapping::isBigEndian()) { if (endian == CiftiFile::LITTLE) return true; } else { if (endian == CiftiFile::BIG) return true; } return false;//default for all other enum values is to write native endian } bool dontRewrite(const CiftiFile::ENDIAN& endian) { return (endian == CiftiFile::ANY); } } CiftiFile::ReadImplInterface::~ReadImplInterface() { } CiftiFile::WriteImplInterface::~WriteImplInterface() { } CiftiFile::CiftiFile(const QString& fileName) { m_endianPref = NATIVE; 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(); 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(); m_dims = m_xml.getDimensions(); m_onDiskVersion = m_xml.getParsedVersion(); m_fileName = 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(); CaretPointer newRead(new CiftiXnatImpl(url, user, pass)); m_readingImpl = newRead; m_xml = newRead->getCiftiXML(); m_dims = m_xml.getDimensions(); m_fileName = url; } 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(); CaretPointer newRead(new CiftiXnatImpl(url)); m_readingImpl = newRead; m_xml = newRead->getCiftiXML(); m_dims = m_xml.getDimensions(); m_fileName = url; } void CiftiFile::setWritingFile(const QString& fileName, const CiftiVersion& writingVersion, const ENDIAN& endian) { m_writingFile = FileInformation(fileName).getAbsoluteFilePath();//always resolve paths as soon as they enter CiftiFile, in case some clown changes directory before writing data m_writingImpl.grabNew(NULL);//prevent writing to previous writing implementation, let the next set...() set up for writing m_onDiskVersion = writingVersion;//so that we can do on-disk writing with the old version m_fileName = fileName; m_endianPref = endian; } 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"); bool writeSwapped = shouldSwap(endian); FileInformation myInfo(fileName); QString canonicalFilename = myInfo.getCanonicalFilePath();//NOTE: returns EMPTY STRING for nonexistant file const CiftiOnDiskImpl* testImpl = dynamic_cast(m_readingImpl.getPointer()); bool collision = false, hadWriter = (m_writingImpl != NULL); if (testImpl != NULL && canonicalFilename != "" && FileInformation(testImpl->getFilename()).getCanonicalFilePath() == canonicalFilename) {//empty string test is so that we don't say collision if both are nonexistant - could happen if file is removed/unlinked while reading on some filesystems if (m_onDiskVersion == writingVersion && !m_xml.mutablesModified() && (dontRewrite(endian) || writeSwapped == testImpl->isSwapped())) return;//don't need to copy to itself collision = true;//we need to copy to memory temporarily CaretPointer tempMemory(new CiftiMemoryImpl(m_xml)); copyImplData(m_readingImpl, tempMemory, m_dims); 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)); 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 { m_onDiskVersion = writingVersion;//also record the current version number m_readingImpl = tempWrite;//replace the temporary memory version if (hadWriter)//if it was in read-write mode { m_writingImpl = tempWrite;//set the writer too } } m_xml.clearMutablesModified(); } void CiftiFile::convertToInMemory() { if (isInMemory()) return; m_writingFile = "";//make sure it doesn't do on-disk when set...() is called if (m_readingImpl == NULL) return;//not set up yet CaretPointer tempWrite(new CiftiMemoryImpl(m_xml));//if we get an error while reading, free the memory immediately, and don't leave m_readingImpl and m_writingImpl pointing to different things copyImplData(m_readingImpl, tempWrite, m_dims); m_writingImpl = tempWrite; m_readingImpl = tempWrite; } bool CiftiFile::isInMemory() const { if (m_readingImpl == NULL) { return (m_writingFile == "");//return what it would be if verifyWriteImpl() was called } else { return m_readingImpl->isInMemory(); } } void CiftiFile::getRow(float* dataOut, const vector& indexSelect, const bool& tolerateShortRead) const { if (m_dims.empty()) throw DataFileException("getRow called on uninitialized CiftiFile"); if (m_readingImpl == NULL) return;//NOT an error because we are pretending to have a matrix already, while we are waiting for setRow to actually start writing the file m_readingImpl->getRow(dataOut, indexSelect, tolerateShortRead); } void CiftiFile::getColumn(float* dataOut, const int64_t& index) const { if (m_dims.empty()) throw DataFileException("getColumn called on uninitialized CiftiFile"); if (m_dims.size() != 2) throw DataFileException("getColumn called on non-2D CiftiFile"); if (m_readingImpl == NULL) return;//NOT an error because we are pretending to have a matrix already, while we are waiting for setRow to actually start writing the file m_readingImpl->getColumn(dataOut, index); } void CiftiFile::setCiftiXML(const CiftiXML& xml, const bool useOldMetadata) { 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(); if (oldmd != NULL) { GiftiMetaData newmd = *oldmd;//make a copy oldmd = NULL;//don't leave a potentially dangling pointer around m_xml = xml;//because this will result in a new pointer for the metadata GiftiMetaData* changemd = m_xml.getFileMetaData(); if (changemd != NULL) { *changemd = newmd; } } else { m_xml = xml; } } 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"); } } void CiftiFile::setCiftiXML(const CiftiXMLOld& xml, const bool useOldMetadata) { QString xmlText; xml.writeXML(xmlText); CiftiXML tempXML;//so that we can use the same code path tempXML.readXML(xmlText); if (tempXML.getDimensionLength(CiftiXML::ALONG_ROW) < 1) { CiftiSeriesMap& tempMap = tempXML.getSeriesMap(CiftiXML::ALONG_ROW); tempMap.setLength(xml.getDimensionLength(CiftiXMLOld::ALONG_ROW)); } if (tempXML.getDimensionLength(CiftiXML::ALONG_COLUMN) < 1) { CiftiSeriesMap& tempMap = tempXML.getSeriesMap(CiftiXML::ALONG_COLUMN); tempMap.setLength(xml.getDimensionLength(CiftiXMLOld::ALONG_COLUMN)); } setCiftiXML(tempXML, useOldMetadata); } void CiftiFile::setRow(const float* dataIn, const vector& indexSelect) { verifyWriteImpl(); m_writingImpl->setRow(dataIn, indexSelect); } void CiftiFile::setColumn(const float* dataIn, const int64_t& index) { verifyWriteImpl(); if (m_dims.size() != 2) throw DataFileException("setColumn called on non-2D CiftiFile"); m_writingImpl->setColumn(dataIn, index); } //compatibility with old interface void CiftiFile::getRow(float* dataOut, const int64_t& index, const bool& tolerateShortRead) const { if (m_dims.empty()) throw DataFileException("getRow called on uninitialized CiftiFile"); if (m_dims.size() != 2) throw DataFileException("getRow with single index called on non-2D CiftiFile"); if (m_readingImpl == NULL) return;//NOT an error because we are pretending to have a matrix already, while we are waiting for setRow to actually start writing the file vector tempvec(1, index);//could use a member if we need more speed m_readingImpl->getRow(dataOut, tempvec, tolerateShortRead); } void CiftiFile::getRow(float* dataOut, const int64_t& index) const { getRow(dataOut, index, false);//once CiftiInterface is gone, we can collapse this into a default value } int64_t CiftiFile::getNumberOfRows() const { if (m_dims.empty()) throw DataFileException("getNumberOfRows called on uninitialized CiftiFile"); if (m_dims.size() != 2) throw DataFileException("getNumberOfRows called on non-2D CiftiFile"); return m_dims[1];//length of a column } int64_t CiftiFile::getNumberOfColumns() const { if (m_dims.empty()) throw DataFileException("getNumberOfRows called on uninitialized CiftiFile"); if (m_dims.size() != 2) throw DataFileException("getNumberOfRows called on non-2D CiftiFile"); return m_dims[0];//length of a row } void CiftiFile::setRow(const float* dataIn, const int64_t& index) { verifyWriteImpl(); if (m_dims.size() != 2) throw DataFileException("setRow with single index called on non-2D CiftiFile"); vector tempvec(1, index);//could use a member if we need more speed m_writingImpl->setRow(dataIn, tempvec); } //*///end old compatibility functions void CiftiFile::verifyWriteImpl() {//this is where the magic happens - we want to emulate being a simple in-memory file, but actually be reading/writing on-disk when possible if (m_writingImpl != NULL) return; CaretAssert(!m_dims.empty());//if the xml hasn't been set, then we can't do anything meaningful if (m_dims.empty()) throw DataFileException("setRow or setColumn attempted on uninitialized CiftiFile"); if (m_writingFile == "") { if (m_readingImpl != NULL) { convertToInMemory(); } else { m_writingImpl.grabNew(new CiftiMemoryImpl(m_xml)); } } else {//NOTE: m_onDiskVersion gets set in setWritingFile if (m_readingImpl != NULL) { CiftiOnDiskImpl* testImpl = dynamic_cast(m_readingImpl.getPointer()); if (testImpl != NULL) { QString canonicalCurrent = FileInformation(testImpl->getFilename()).getCanonicalFilePath();//returns "" if nonexistant, if unlinked while open if (canonicalCurrent != "" && canonicalCurrent == FileInformation(m_writingFile).getCanonicalFilePath())//these were already absolute { convertToInMemory();//save existing data in memory before we clobber file } } } m_writingImpl.grabNew(new CiftiOnDiskImpl(m_writingFile, m_xml, m_onDiskVersion, shouldSwap(m_endianPref)));//this constructor makes new file for writing if (m_readingImpl != NULL) { copyImplData(m_readingImpl, m_writingImpl, m_dims); } } m_readingImpl = m_writingImpl;//read-only implementations are set up in specialized functions } void CiftiFile::copyImplData(const ReadImplInterface* from, WriteImplInterface* to, const vector& dims) { vector iterateDims(dims.begin() + 1, dims.end()); vector scratchRow(dims[0]); for (MultiDimIterator iter(iterateDims); !iter.atEnd(); ++iter) { from->getRow(scratchRow.data(), *iter, false); to->setRow(scratchRow.data(), *iter); } } CiftiMemoryImpl::CiftiMemoryImpl(const CiftiXML& xml) { CaretAssert(xml.getNumberOfDimensions() != 0); m_array.resize(xml.getDimensions()); } void CiftiMemoryImpl::getRow(float* dataOut, const vector& indexSelect, const bool&) const { const float* ref = m_array.get(1, indexSelect); int64_t rowSize = m_array.getDimensions()[0];//we don't accept 0-D CiftiXML, so this will always work for (int64_t i = 0; i < rowSize; ++i) { dataOut[i] = ref[i]; } } void CiftiMemoryImpl::getColumn(float* dataOut, const int64_t& index) const { CaretAssert(m_array.getDimensions().size() == 2);//otherwise, CiftiFile shouldn't have called this const float* ref = m_array.get(2, vector());//empty vector is intentional, only 2 dimensions exist, so no more to select from int64_t rowSize = m_array.getDimensions()[0]; int64_t colSize = m_array.getDimensions()[1]; CaretAssert(index >= 0 && index < rowSize);//because we are doing the indexing math manually for speed for (int64_t i = 0; i < colSize; ++i) { dataOut[i] = ref[index + rowSize * i]; } } void CiftiMemoryImpl::setRow(const float* dataIn, const vector& indexSelect) { float* ref = m_array.get(1, indexSelect); int64_t rowSize = m_array.getDimensions()[0];//we don't accept 0-D CiftiXML, so this will always work for (int64_t i = 0; i < rowSize; ++i) { ref[i] = dataIn[i]; } } void CiftiMemoryImpl::setColumn(const float* dataIn, const int64_t& index) { CaretAssert(m_array.getDimensions().size() == 2);//otherwise, CiftiFile shouldn't have called this float* ref = m_array.get(2, vector());//empty vector is intentional, only 2 dimensions exist, so no more to select from int64_t rowSize = m_array.getDimensions()[0]; int64_t colSize = m_array.getDimensions()[1]; CaretAssert(index >= 0 && index < rowSize);//because we are doing the indexing math manually for speed for (int64_t i = 0; i < colSize; ++i) { ref[index + rowSize * i] = dataIn[i]; } } CiftiOnDiskImpl::CiftiOnDiskImpl(const QString& filename) {//opens existing file for reading m_nifti.openRead(filename);//read-only, so we don't need write permission to read a cifti file if (m_nifti.getNumComponents() != 1) throw DataFileException("complex or rgb datatype found in file '" + filename + "', these are not supported in cifti"); const NiftiHeader& myHeader = m_nifti.getHeader(); int numExts = (int)myHeader.m_extensions.size(), whichExt = -1; for (int i = 0; i < numExts; ++i) { if (myHeader.m_extensions[i]->m_ecode == NIFTI_ECODE_CIFTI) { whichExt = i; break; } } if (whichExt == -1) throw DataFileException("no cifti extension found in file '" + filename + "'"); m_xml.readXML(QByteArray(myHeader.m_extensions[whichExt]->m_bytes.data(), myHeader.m_extensions[whichExt]->m_bytes.size()));//CiftiXML should be under 2GB vector dimCheck = m_nifti.getDimensions(); if (dimCheck.size() < 5) { if (m_xml.getParsedVersion() == CiftiVersion(1, 0) && dimCheck.size() == 2)//QUIRK: we wrote some cifti-1 files with the dimensions in dim[1] and dim[2] { CaretLogWarning("invalid dimensions in cifti file '" + filename + "', attempting recovery");//becase cifti-1 was 2D only, we can try to recover vector dimFix(4, 1); dimFix.push_back(dimCheck[0]); dimFix.push_back(dimCheck[1]); dimCheck = dimFix; m_nifti.overrideDimensions(dimCheck);//will actually get overridden again below since cifti-1 has reversed first dims } else { throw DataFileException("invalid dimensions in cifti file '" + filename + "'"); } } for (int i = 0; i < 4; ++i) { if (dimCheck[i] != 1) throw DataFileException("non-singular dimension #" + QString::number(i + 1) + " in cifti file '" + filename + "'"); } if (m_xml.getParsedVersion().hasReversedFirstDims()) { while (dimCheck.size() < 6) dimCheck.push_back(1);//just in case int64_t temp = dimCheck[4];//note: nifti dim[5] is the 5th dimension, index 4 in this vector dimCheck[4] = dimCheck[5]; dimCheck[5] = temp; m_nifti.overrideDimensions(dimCheck); } 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 { m_xml.getSeriesMap(i - 4).setLength(dimCheck[i]);//and only in a series map } else { if (m_xml.getDimensionLength(i - 4) != dimCheck[i]) { throw DataFileException("xml and nifti header disagree on matrix dimensions"); } } } } namespace { void warnForBadExtension(const QString& filename, const CiftiXML& myXML) { char junk[16]; 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) { 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"); } break; case 3001: if (!filename.endsWith(".dconn.nii")) { CaretLogWarning("dense by dense cifti file '" + filename + "' should be saved ending in .dconn.nii, see wb_command -cifti-help"); } break; case 3002: if (!filename.endsWith(".dtseries.nii")) { CaretLogWarning("series by dense cifti file '" + filename + "' should be saved ending in .dtseries.nii, see wb_command -cifti-help"); } break; case 3003: if (!filename.endsWith(".pconn.nii")) { CaretLogWarning("parcels by parcels cifti file '" + filename + "' should be saved ending in .pconn.nii, see wb_command -cifti-help"); } break; case 3004: if (!filename.endsWith(".ptseries.nii")) { CaretLogWarning("series by parcels cifti file '" + filename + "' should be saved ending in .ptseries.nii, see wb_command -cifti-help"); } break; case 3006://3005 unused in practice if (!(filename.endsWith(".dscalar.nii") || filename.endsWith(".dfan.nii") || filename.endsWith(".fiberTEMP.nii"))) {//there are additional special extensions in the standard for this mapping combination (specializations of scalar maps) //also include our fiberTEMP special extension CaretLogWarning("scalars by dense cifti file '" + filename + "' should be saved ending in .dscalar.nii, see wb_command -cifti-help"); } break; case 3007: if (!filename.endsWith(".dlabel.nii")) { CaretLogWarning("labels by dense cifti file '" + filename + "' should be saved ending in .dlabel.nii, see wb_command -cifti-help"); } break; case 3008: if (!filename.endsWith(".pscalar.nii")) { CaretLogWarning("scalars by parcels cifti file '" + filename + "' should be saved ending in .pscalar.nii, see wb_command -cifti-help"); } break; case 3009: if (!filename.endsWith(".pdconn.nii")) { CaretLogWarning("dense by parcels cifti file '" + filename + "' should be saved ending in .pdconn.nii, see wb_command -cifti-help"); } break; case 3010: if (!filename.endsWith(".dpconn.nii")) { CaretLogWarning("parcels by dense cifti file '" + filename + "' should be saved ending in .dpconn.nii, see wb_command -cifti-help"); } break; case 3011: if (!filename.endsWith(".pconnseries.nii")) { CaretLogWarning("parcels by parcels by series cifti file '" + filename + "' should be saved ending in .pconnseries.nii, see wb_command -cifti-help"); } break; case 3012: if (!filename.endsWith(".pconnscalar.nii")) { 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) {//starts writing new file warnForBadExtension(filename, xml); NiftiHeader outHeader; outHeader.setDataType(NIFTI_TYPE_FLOAT32);//actually redundant currently, default is float32 char intentName[16]; int32_t intentCode = xml.getIntentInfo(version, intentName); outHeader.setIntent(intentCode, intentName); QByteArray xmlBytes = xml.writeXMLToQByteArray(version); CaretPointer outExtension(new NiftiExtension()); outExtension->m_ecode = NIFTI_ECODE_CIFTI; int numBytes = xmlBytes.size(); outExtension->m_bytes.resize(numBytes); for (int i = 0; i < numBytes; ++i) { outExtension->m_bytes[i] = xmlBytes[i]; } outHeader.m_extensions.push_back(outExtension); vector matrixDims = xml.getDimensions(); vector niftiDims(4, 1);//the reserved space and time dims niftiDims.insert(niftiDims.end(), matrixDims.begin(), matrixDims.end()); if (version.hasReversedFirstDims()) { vector headerDims = niftiDims; while (headerDims.size() < 6) headerDims.push_back(1);//just in case int64_t temp = headerDims[4]; headerDims[4] = headerDims[5]; headerDims[5] = temp; outHeader.setDimensions(headerDims);//give the header the reversed dimensions m_nifti.writeNew(filename, outHeader, 2, true, swapEndian); m_nifti.overrideDimensions(niftiDims);//and then tell the nifti reader to use the correct dimensions } else { outHeader.setDimensions(niftiDims); m_nifti.writeNew(filename, outHeader, 2, true, swapEndian); } m_xml = xml; } 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 } void CiftiOnDiskImpl::getColumn(float* dataOut, const int64_t& index) const { CaretAssert(m_xml.getNumberOfDimensions() == 2);//otherwise this shouldn't be called CaretAssert(index >= 0 && index < m_xml.getDimensionLength(CiftiXML::ALONG_ROW)); CaretLogFine("getColumn called on CiftiOnDiskImpl, this will be slow");//generate logging messages at a low priority vector indexSelect(2); indexSelect[0] = index; int64_t colLength = m_xml.getDimensionLength(CiftiXML::ALONG_COLUMN); for (int64_t i = 0; i < colLength; ++i)//assume if they really want getColumn on disk, they don't want their pagecache obliterated, so read it 1 element at a time { indexSelect[1] = i; m_nifti.readData(dataOut + i, 4, indexSelect);//4 means just the 4 reserved dimensions, so 1 element of the matrix } } void CiftiOnDiskImpl::setRow(const float* dataIn, const vector& indexSelect) { m_nifti.writeData(dataIn, 5, indexSelect); } void CiftiOnDiskImpl::setColumn(const float* dataIn, const int64_t& index) { CaretAssert(m_xml.getNumberOfDimensions() == 2);//otherwise this shouldn't be called CaretAssert(index >= 0 && index < m_xml.getDimensionLength(CiftiXML::ALONG_ROW)); CaretLogFine("setColumn called on CiftiOnDiskImpl, this will be slow");//generate logging messages at a low priority vector indexSelect(2); indexSelect[0] = index; int64_t colLength = m_xml.getDimensionLength(CiftiXML::ALONG_COLUMN); for (int64_t i = 0; i < colLength; ++i)//don't do RMW, so write it 1 element at a time { indexSelect[1] = i; m_nifti.writeData(dataIn + i, 4, indexSelect);//4 means just the 4 reserved dimensions, so 1 element of the matrix } } CiftiXnatImpl::CiftiXnatImpl(const QString& url, const QString& user, const QString& pass) { CaretHttpManager::setAuthentication(url, user, pass); init(url); } CiftiXnatImpl::CiftiXnatImpl(const QString& url) { init(url); } void CiftiXnatImpl::init(const QString& url) { m_baseRequest.m_url = url; m_baseRequest.m_method = CaretHttpManager::POST_ARGUMENTS; int32_t start = url.indexOf('?'); bool foundSearchID = false; bool foundResource = false; while ((!foundSearchID) && (!foundResource)) { if (start == -1) { throw DataFileException("Error: searchID not found in URL string"); } if (url.mid(start + 1, 9) == "searchID=") { foundSearchID = true; } else if (url.mid(start + 1, 9) == "resource=") { foundResource = true; } start = url.indexOf('&', start + 1); } m_baseRequest.m_queries.push_back(make_pair(AString("type"), AString("dconn"))); CaretHttpRequest metadata = m_baseRequest; if (foundResource) { metadata.m_queries.push_back(make_pair(AString("metadata"), AString("true"))); } else { metadata.m_queries.push_back(make_pair(AString("metadata"), AString(""))); } CaretHttpResponse myResponse; CaretHttpManager::httpRequest(metadata, myResponse); if (!myResponse.m_ok) { throw DataFileException("Error opening URL, response code: " + AString::number(myResponse.m_responseCode)); } myResponse.m_body.push_back('\0');//null terminate it so we can construct an AString easily - CaretHttpManager is nice and pre-reserves this room for this purpose AString theBody(myResponse.m_body.data()); m_xml.readXML(theBody); if (m_xml.getNumberOfDimensions() != 2) { throw DataFileException("only 2D cifti are supported via URL at this time"); } if (m_xml.getMappingType(CiftiXML::ALONG_ROW) == CiftiMappingType::SERIES && m_xml.getDimensionLength(CiftiXML::ALONG_ROW) < 1) { CaretHttpRequest rowRequest = m_baseRequest; rowRequest.m_queries.push_back(make_pair(AString("row-index"), AString("0"))); m_xml.getSeriesMap(CiftiXML::ALONG_ROW).setLength(getSizeFromReq(rowRequest)); } if (m_xml.getMappingType(CiftiXML::ALONG_COLUMN) == CiftiMappingType::SERIES && m_xml.getDimensionLength(CiftiXML::ALONG_COLUMN) < 1) { CaretHttpRequest columnRequest = m_baseRequest; columnRequest.m_queries.push_back(make_pair(AString("column-index"), AString("0"))); m_xml.getSeriesMap(CiftiXML::ALONG_COLUMN).setLength(getSizeFromReq(columnRequest)); } CaretLogFine("Connected URL: " + url + "\nRow/Column length:" + QString::number(m_xml.getDimensionLength(CiftiXML::ALONG_ROW)) + "/" + QString::number(m_xml.getDimensionLength(CiftiXML::ALONG_COLUMN))); } void CiftiXnatImpl::getReqAsFloats(float* data, const int64_t& dataSize, CaretHttpRequest& request) const { CaretHttpResponse myResponse; CaretHttpManager::httpRequest(request, myResponse); if (!myResponse.m_ok) { throw DataFileException("Error getting row, response code: " + AString::number(myResponse.m_responseCode)); } if (myResponse.m_body.size() % 4 != 0)//expect a multiple of 4 bytes { throw DataFileException("Bad reply, number of bytes is not a multiple of 4"); } int32_t numItems = *((int32_t*)myResponse.m_body.data()); if (ByteOrderEnum::isSystemBigEndian()) { ByteSwapping::swap(numItems); } if (numItems * 4 + 4 != (int64_t)myResponse.m_body.size()) { throw DataFileException("Bad reply, number of items does not match length of reply"); } if (dataSize != numItems) { throw DataFileException("Bad reply, number of items does not match header"); } float* myPointer = (float*)(myResponse.m_body.data() + 4);//skip the first element (which is an int32) for (int i = 0; i < numItems; ++i) { data[i] = myPointer[i]; } if (ByteOrderEnum::isSystemBigEndian()) { ByteSwapping::swapArray(data, numItems); } } int64_t CiftiXnatImpl::getSizeFromReq(CaretHttpRequest& request) { CaretHttpResponse myResponse; CaretHttpManager::httpRequest(request, myResponse); if (!myResponse.m_ok) { throw DataFileException("Error getting row, response code: " + AString::number(myResponse.m_responseCode)); } if (myResponse.m_body.size() % 4 != 0)//expect a multiple of 4 bytes { throw DataFileException("Bad reply, number of bytes is not a multiple of 4"); } int32_t numItems = *((int32_t*)myResponse.m_body.data()); if (ByteOrderEnum::isSystemBigEndian()) { ByteSwapping::swap(numItems); } if (numItems * 4 + 4 != (int64_t)myResponse.m_body.size()) { throw DataFileException("Bad reply, number of items does not match length of reply"); } return numItems; } void CiftiXnatImpl::getRow(float* dataOut, const vector& indexSelect, const bool&) const { CaretAssert(indexSelect.size() == 1); CaretHttpRequest rowRequest = m_baseRequest; rowRequest.m_queries.push_back(make_pair(AString("row-index"), AString::number(indexSelect[0]))); getReqAsFloats(dataOut, m_xml.getDimensionLength(CiftiXML::ALONG_ROW), rowRequest); } void CiftiXnatImpl::getColumn(float* dataOut, const int64_t& index) const { CaretHttpRequest columnRequest = m_baseRequest; columnRequest.m_queries.push_back(make_pair(AString("column-index"), AString::number(index))); getReqAsFloats(dataOut, m_xml.getDimensionLength(CiftiXML::ALONG_COLUMN), columnRequest); } connectome-workbench-1.2.3+git41-gc4c6c90/src/Cifti/CiftiFile.h000066400000000000000000000122761300200146000236210ustar00rootroot00000000000000#ifndef __CIFTI_FILE_H__ #define __CIFTI_FILE_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 "CaretPointer.h" #include "CiftiInterface.h" #include "CiftiXML.h" #include "CiftiXMLOld.h" #include "MultiDimIterator.h" #include #include namespace caret { class CiftiFile : public CiftiInterface { public: enum ENDIAN { ANY,//so that writeFile() with default endian argument can do nothing after setWritingFile with any endian argument - uses native if there is no rewrite to avoid NATIVE,//as long as there are more than two options anyway, provide a convenience option so people don't need to figure out the machine endianness for a common case LITTLE, BIG }; CiftiFile() { m_endianPref = NATIVE; } 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 convertToInMemory(); QString getFileName() const { return m_fileName; } bool isInMemory() const; void getRow(float* dataOut, const std::vector& indexSelect, const bool& tolerateShortRead = false) const;//tolerateShortRead is useful for on-disk writing when it is easiest to do RMW multiple times on a new file const std::vector& getDimensions() const { return m_dims; } MultiDimIterator getIteratorOverRows() const { return MultiDimIterator(std::vector(m_dims.begin() + 1, m_dims.end())); } void getColumn(float* dataOut, const int64_t& index) const;//for 2D only, will be slow if on disk! void setCiftiXML(const CiftiXML& xml, const bool useOldMetadata = true); void setCiftiXML(const CiftiXMLOld &xml, const bool useOldMetadata = true);//set xml from old implementation 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! 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; int64_t getNumberOfColumns() const; void setRow(const float* dataIn, const int64_t& index);//backwards compatibility for old CiftiFile class ReadImplInterface { public: virtual void getRow(float* dataOut, const std::vector& indexSelect, const bool& tolerateShortRead) const = 0; virtual void getColumn(float* dataOut, const int64_t& index) const = 0; virtual bool isInMemory() const { return false; } virtual ~ReadImplInterface(); }; //assume if you can write to it, you can also read from it class WriteImplInterface : public ReadImplInterface { public: virtual void setRow(const float* dataIn, const std::vector& indexSelect) = 0; virtual void setColumn(const float* dataIn, const int64_t& index) = 0; virtual ~WriteImplInterface(); }; private: std::vector m_dims; CaretPointer m_writingImpl;//this will be equal to m_readingImpl when non-null CaretPointer m_readingImpl; QString m_writingFile, m_fileName; //CiftiXML m_xml;//uncomment when we drop CiftiInterface CiftiVersion m_onDiskVersion; ENDIAN m_endianPref; void verifyWriteImpl(); static void copyImplData(const ReadImplInterface* from, WriteImplInterface* to, const std::vector& dims); }; } #endif //__CIFTI_FILE_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Cifti/CiftiInterface.cxx000066400000000000000000000431721300200146000252140ustar00rootroot00000000000000/*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 "CiftiInterface.h" #include "DataFileException.h" #include #include using namespace caret; using namespace std; CiftiInterface::CiftiInterface() { m_dataRangeValid = false; } void CiftiInterface::invalidateDataRange() { m_dataRangeValid = false; } bool CiftiInterface::getDataRangeFromAllMaps(float& minOut, float& maxOut) const { if (!m_dataRangeValid) { int64_t numRows = getNumberOfRows(), rowSize = getNumberOfColumns(); if (numRows <= 0 || rowSize <= 0) { maxOut = numeric_limits::max(); minOut = -maxOut; return false; } m_dataRangeMin = numeric_limits::max(); m_dataRangeMax = -m_dataRangeMin; vector tempRow(rowSize); for (int64_t row = 0; row < numRows; ++row) { getRow(tempRow.data(), row); for (int64_t i = 0; i < rowSize; ++i) { if (tempRow[i] > m_dataRangeMax) m_dataRangeMax = tempRow[i]; if (tempRow[i] < m_dataRangeMin) m_dataRangeMin = tempRow[i]; } } m_dataRangeValid = true; } minOut = m_dataRangeMin; maxOut = m_dataRangeMax; return true; } CiftiXMLOld CiftiInterface::getCiftiXMLOld() const { CiftiXMLOld ret; if (m_xml.getNumberOfDimensions() != 2) throw DataFileException("can't convert to old XML because number of dimensions isn't 2"); ret.readXML(m_xml.writeXMLToString(CiftiVersion(1, 0))); if (ret.getDimensionLength(CiftiXMLOld::ALONG_ROW) < 1) { ret.setRowNumberOfTimepoints(m_xml.getDimensionLength(CiftiXML::ALONG_ROW)); } if (ret.getDimensionLength(CiftiXMLOld::ALONG_COLUMN) < 1) { ret.setColumnNumberOfTimepoints(m_xml.getDimensionLength(CiftiXML::ALONG_COLUMN)); } return ret; } bool CiftiInterface::checkColumnIndex(int64_t index) const { if (index < 0 || index >= getNumberOfColumns()) { return false; } return true; } bool CiftiInterface::checkRowIndex(int64_t index) const { if (index < 0 || index >= getNumberOfRows()) { return false; } return true; } bool CiftiInterface::getColumnFromNode(float* columnOut, const int64_t node, const caret::StructureEnum::Enum structure) const { int64_t myIndex = -1; const CiftiMappingType& myGenMap = *(m_xml.getMap(CiftiXML::ALONG_ROW)); if (myGenMap.getType() == CiftiMappingType::BRAIN_MODELS) { const CiftiBrainModelsMap& myMap = (const CiftiBrainModelsMap&)myGenMap; myIndex = myMap.getIndexForNode(node, structure); } else if (myGenMap.getType() == CiftiMappingType::PARCELS) { const CiftiParcelsMap& myMap = (const CiftiParcelsMap&)myGenMap; myIndex = myMap.getIndexForNode(node, structure); } if (!checkColumnIndex(myIndex)) return false; getColumn(columnOut, myIndex); return true; } bool CiftiInterface::getColumnFromVoxel(float* columnOut, const int64_t* ijk) const { int64_t myIndex = -1; const CiftiMappingType& myGenMap = *(m_xml.getMap(CiftiXML::ALONG_ROW)); if (myGenMap.getType() == CiftiMappingType::BRAIN_MODELS) { const CiftiBrainModelsMap& myMap = (const CiftiBrainModelsMap&)myGenMap; myIndex = myMap.getIndexForVoxel(ijk); } else if (myGenMap.getType() == CiftiMappingType::PARCELS) { const CiftiParcelsMap& myMap = (const CiftiParcelsMap&)myGenMap; myIndex = myMap.getIndexForVoxel(ijk); } if (!checkColumnIndex(myIndex)) return false; getColumn(columnOut, myIndex); return true; } bool CiftiInterface::getRowFromNode(float* rowOut, const int64_t node, const caret::StructureEnum::Enum structure) const { int64_t myIndex = -1; const CiftiMappingType& myGenMap = *(m_xml.getMap(CiftiXML::ALONG_COLUMN)); if (myGenMap.getType() == CiftiMappingType::BRAIN_MODELS) { const CiftiBrainModelsMap& myMap = (const CiftiBrainModelsMap&)myGenMap; myIndex = myMap.getIndexForNode(node, structure); } else if (myGenMap.getType() == CiftiMappingType::PARCELS) { const CiftiParcelsMap& myMap = (const CiftiParcelsMap&)myGenMap; myIndex = myMap.getIndexForNode(node, structure); } if (!checkRowIndex(myIndex)) return false; getRow(rowOut, myIndex); return true; } bool CiftiInterface::getRowFromNode(float* rowOut, const int64_t node, const caret::StructureEnum::Enum structure, int64_t& rowIndexOut) const { rowIndexOut = -1; const CiftiMappingType& myGenMap = *(m_xml.getMap(CiftiXML::ALONG_COLUMN)); if (myGenMap.getType() == CiftiMappingType::BRAIN_MODELS) { const CiftiBrainModelsMap& myMap = (const CiftiBrainModelsMap&)myGenMap; rowIndexOut = myMap.getIndexForNode(node, structure); } else if (myGenMap.getType() == CiftiMappingType::PARCELS) { const CiftiParcelsMap& myMap = (const CiftiParcelsMap&)myGenMap; rowIndexOut = myMap.getIndexForNode(node, structure); } if (!checkRowIndex(rowIndexOut)) return false; getRow(rowOut, rowIndexOut); return true; } bool CiftiInterface::getRowFromVoxel(float* rowOut, const int64_t* ijk) const { int64_t myIndex = -1; const CiftiMappingType& myGenMap = *(m_xml.getMap(CiftiXML::ALONG_COLUMN)); if (myGenMap.getType() == CiftiMappingType::BRAIN_MODELS) { const CiftiBrainModelsMap& myMap = (const CiftiBrainModelsMap&)myGenMap; myIndex = myMap.getIndexForVoxel(ijk); } else if (myGenMap.getType() == CiftiMappingType::PARCELS) { const CiftiParcelsMap& myMap = (const CiftiParcelsMap&)myGenMap; myIndex = myMap.getIndexForVoxel(ijk); } if (!checkRowIndex(myIndex)) return false; getRow(rowOut, myIndex); return true; } bool CiftiInterface::getColumnFromVoxelCoordinate(float* columnOut, const float* xyz) const { int64_t myIndex = -1; int64_t ijk[3]; const CiftiMappingType& myGenMap = *(m_xml.getMap(CiftiXML::ALONG_ROW)); if (myGenMap.getType() == CiftiMappingType::BRAIN_MODELS) { const CiftiBrainModelsMap& myMap = (const CiftiBrainModelsMap&)myGenMap; myMap.getVolumeSpace().enclosingVoxel(xyz, ijk); myIndex = myMap.getIndexForVoxel(ijk); } else if (myGenMap.getType() == CiftiMappingType::PARCELS) { const CiftiParcelsMap& myMap = (const CiftiParcelsMap&)myGenMap; myMap.getVolumeSpace().enclosingVoxel(xyz, ijk); myIndex = myMap.getIndexForVoxel(ijk); } if (!checkColumnIndex(myIndex)) return false; getColumn(columnOut, myIndex); return true; } bool CiftiInterface::getRowFromVoxelCoordinate(float* rowOut, const float* xyz) const { int64_t myIndex = -1; int64_t ijk[3]; const CiftiMappingType& myGenMap = *(m_xml.getMap(CiftiXML::ALONG_COLUMN)); if (myGenMap.getType() == CiftiMappingType::BRAIN_MODELS) { const CiftiBrainModelsMap& myMap = (const CiftiBrainModelsMap&)myGenMap; myMap.getVolumeSpace().enclosingVoxel(xyz, ijk); myIndex = myMap.getIndexForVoxel(ijk); } else if (myGenMap.getType() == CiftiMappingType::PARCELS) { const CiftiParcelsMap& myMap = (const CiftiParcelsMap&)myGenMap; myMap.getVolumeSpace().enclosingVoxel(xyz, ijk); myIndex = myMap.getIndexForVoxel(ijk); } if (!checkRowIndex(myIndex)) return false; getRow(rowOut, myIndex); return true; } bool CiftiInterface::getRowFromVoxelCoordinate(float* rowOut, const float* xyz, int64_t& rowIndexOut) const { rowIndexOut = -1; int64_t ijk[3]; const CiftiMappingType& myGenMap = *(m_xml.getMap(CiftiXML::ALONG_COLUMN)); if (myGenMap.getType() == CiftiMappingType::BRAIN_MODELS) { const CiftiBrainModelsMap& myMap = (const CiftiBrainModelsMap&)myGenMap; myMap.getVolumeSpace().enclosingVoxel(xyz, ijk); rowIndexOut = myMap.getIndexForVoxel(ijk); } else if (m_xml.getMappingType(CiftiXML::ALONG_ROW) == CiftiMappingType::PARCELS) { const CiftiParcelsMap& myMap = (const CiftiParcelsMap&)myGenMap; myMap.getVolumeSpace().enclosingVoxel(xyz, ijk); rowIndexOut = myMap.getIndexForVoxel(ijk); } if (!checkRowIndex(rowIndexOut)) return false; getRow(rowOut, rowIndexOut); return true; } bool CiftiInterface::getColumnFromTimepoint(float* columnOut, const float seconds) const { const CiftiMappingType& myGenMap = *(m_xml.getMap(CiftiXML::ALONG_ROW)); if (myGenMap.getType() != CiftiMappingType::SERIES) return false; const CiftiSeriesMap& myMap = (const CiftiSeriesMap&)myGenMap; int64_t myIndex = (int64_t)floor((seconds - myMap.getStart()) / myMap.getStep() + 0.5f); if (!checkColumnIndex(myIndex)) return false; getColumn(columnOut, myIndex); return true; } //column and frame are the same value currently, this function exists only //to keep the concepts of frame and time separate from being conflated bool CiftiInterface::getColumnFromFrame(float* columnOut, const int frame) const { if(!checkColumnIndex(frame)) { return false; } getColumn(columnOut, frame); return true; } bool CiftiInterface::getRowFromTimepoint(float* rowOut, const float seconds) const { const CiftiMappingType& myGenMap = *(m_xml.getMap(CiftiXML::ALONG_COLUMN)); if (myGenMap.getType() != CiftiMappingType::SERIES) return false; const CiftiSeriesMap& myMap = (const CiftiSeriesMap&)myGenMap; int64_t myIndex = (int64_t)floor((seconds - myMap.getStart()) / myMap.getStep() + 0.5f); if (!checkColumnIndex(myIndex)) return false; getRow(rowOut, myIndex); return true; } bool CiftiInterface::getSurfaceMapForRows(vector& mappingOut, const StructureEnum::Enum structure) const { if (m_xml.getMappingType(CiftiXML::ALONG_ROW) != CiftiMappingType::BRAIN_MODELS) return false; mappingOut = m_xml.getBrainModelsMap(CiftiXML::ALONG_ROW).getSurfaceMap(structure);//will throw on structure missing return true; } bool CiftiInterface::getSurfaceMapForColumns(vector& mappingOut, const StructureEnum::Enum structure) const { if (m_xml.getMappingType(CiftiXML::ALONG_COLUMN) != CiftiMappingType::BRAIN_MODELS) return false; mappingOut = m_xml.getBrainModelsMap(CiftiXML::ALONG_COLUMN).getSurfaceMap(structure);//will throw on structure missing return true; } bool CiftiInterface::getVolumeMapForRows(vector& mappingOut) const { if (m_xml.getMappingType(CiftiXML::ALONG_ROW) != CiftiMappingType::BRAIN_MODELS) return false; mappingOut = m_xml.getBrainModelsMap(CiftiXML::ALONG_ROW).getFullVolumeMap(); return true; } bool CiftiInterface::getVolumeMapForColumns(vector& mappingOut) const { if (m_xml.getMappingType(CiftiXML::ALONG_COLUMN) != CiftiMappingType::BRAIN_MODELS) return false; mappingOut = m_xml.getBrainModelsMap(CiftiXML::ALONG_COLUMN).getFullVolumeMap(); return true; } int64_t CiftiInterface::getRowSurfaceNumberOfNodes(const StructureEnum::Enum structure) const { if (m_xml.getMappingType(CiftiXML::ALONG_ROW) == CiftiMappingType::BRAIN_MODELS) { return m_xml.getBrainModelsMap(CiftiXML::ALONG_ROW).getSurfaceNumberOfNodes(structure);//will throw on structure missing } if (m_xml.getMappingType(CiftiXML::ALONG_ROW) == CiftiMappingType::PARCELS) { return m_xml.getParcelsMap(CiftiXML::ALONG_ROW).getSurfaceNumberOfNodes(structure); } return -1; // if (m_xml.getMappingType(CiftiXML::ALONG_ROW) != CiftiMappingType::BRAIN_MODELS) return -1; // return m_xml.getBrainModelsMap(CiftiXML::ALONG_ROW).getSurfaceNumberOfNodes(structure);//will throw on structure missing } int64_t CiftiInterface::getColumnSurfaceNumberOfNodes(const StructureEnum::Enum structure) const { if (m_xml.getMappingType(CiftiXML::ALONG_COLUMN) == CiftiMappingType::BRAIN_MODELS) { return m_xml.getBrainModelsMap(CiftiXML::ALONG_COLUMN).getSurfaceNumberOfNodes(structure);//will throw on structure missing } if (m_xml.getMappingType(CiftiXML::ALONG_COLUMN) == CiftiMappingType::PARCELS) { return m_xml.getParcelsMap(CiftiXML::ALONG_COLUMN).getSurfaceNumberOfNodes(structure); } return -1; // if (m_xml.getMappingType(CiftiXML::ALONG_COLUMN) != CiftiMappingType::BRAIN_MODELS) return -1; // return m_xml.getBrainModelsMap(CiftiXML::ALONG_COLUMN).getSurfaceNumberOfNodes(structure);//will throw on structure missing } bool CiftiInterface::getRowTimestep(float& seconds) const { if (m_xml.getMappingType(CiftiXML::ALONG_ROW) != CiftiMappingType::SERIES) return false; seconds = m_xml.getSeriesMap(CiftiXML::ALONG_ROW).getStep(); return true; } bool CiftiInterface::getColumnTimestep(float& seconds) const { if (m_xml.getMappingType(CiftiXML::ALONG_COLUMN) != CiftiMappingType::SERIES) return false; seconds = m_xml.getSeriesMap(CiftiXML::ALONG_COLUMN).getStep(); return true; } bool CiftiInterface::getVolumeAttributesForPlumb(VolumeSpace::OrientTypes orientOut[3], int64_t dimensionsOut[3], float originOut[3], float spacingOut[3]) const { for (int i = 0; i < m_xml.getNumberOfDimensions(); ++i) { if (m_xml.getMappingType(i) == CiftiMappingType::BRAIN_MODELS) { const CiftiBrainModelsMap& myMap = m_xml.getBrainModelsMap(i); if (myMap.hasVolumeData()) { if (!myMap.getVolumeSpace().isPlumb()) return false; myMap.getVolumeSpace().getOrientAndSpacingForPlumb(orientOut, spacingOut, originOut); const int64_t* dims = myMap.getVolumeSpace().getDims(); dimensionsOut[0] = dims[0]; dimensionsOut[1] = dims[1]; dimensionsOut[2] = dims[2]; return true; } } else if (m_xml.getMappingType(i) == CiftiMappingType::PARCELS) { const CiftiParcelsMap& myMap = m_xml.getParcelsMap(i); if (myMap.hasVolumeData()) { if (!myMap.getVolumeSpace().isPlumb()) return false; myMap.getVolumeSpace().getOrientAndSpacingForPlumb(orientOut, spacingOut, originOut); const int64_t* dims = myMap.getVolumeSpace().getDims(); dimensionsOut[0] = dims[0]; dimensionsOut[1] = dims[1]; dimensionsOut[2] = dims[2]; return true; } } } return false; } bool CiftiInterface::hasRowVolumeData() const { if (m_xml.getMappingType(CiftiXML::ALONG_ROW) == CiftiMappingType::BRAIN_MODELS) { return m_xml.getBrainModelsMap(CiftiXML::ALONG_ROW).hasVolumeData(); } else if (m_xml.getMappingType(CiftiXML::ALONG_ROW) == CiftiMappingType::PARCELS) { return m_xml.getParcelsMap(CiftiXML::ALONG_ROW).hasVolumeData(); } return false; } bool CiftiInterface::hasColumnVolumeData() const { if (m_xml.getMappingType(CiftiXML::ALONG_COLUMN) == CiftiMappingType::BRAIN_MODELS) { return m_xml.getBrainModelsMap(CiftiXML::ALONG_COLUMN).hasVolumeData(); } else if (m_xml.getMappingType(CiftiXML::ALONG_COLUMN) == CiftiMappingType::PARCELS) { return m_xml.getParcelsMap(CiftiXML::ALONG_COLUMN).hasVolumeData(); } return false; } bool CiftiInterface::hasRowSurfaceData(const StructureEnum::Enum structure) const { if (m_xml.getMappingType(CiftiXML::ALONG_ROW) == CiftiMappingType::BRAIN_MODELS) { return m_xml.getBrainModelsMap(CiftiXML::ALONG_ROW).hasSurfaceData(structure); } else if (m_xml.getMappingType(CiftiXML::ALONG_ROW) == CiftiMappingType::PARCELS) { return m_xml.getParcelsMap(CiftiXML::ALONG_ROW).hasSurfaceData(structure); } return false; } bool CiftiInterface::hasColumnSurfaceData(const StructureEnum::Enum structure) const { if (m_xml.getMappingType(CiftiXML::ALONG_COLUMN) == CiftiMappingType::BRAIN_MODELS) { return m_xml.getBrainModelsMap(CiftiXML::ALONG_COLUMN).hasSurfaceData(structure); } else if (m_xml.getMappingType(CiftiXML::ALONG_COLUMN) == CiftiMappingType::PARCELS) { return m_xml.getParcelsMap(CiftiXML::ALONG_COLUMN).hasSurfaceData(structure); } return false; } AString CiftiInterface::getMapNameForColumnIndex(const int& index) const { if (m_xml.getMappingType(CiftiXML::ALONG_COLUMN) == CiftiMappingType::SCALARS) { return m_xml.getScalarsMap(CiftiXML::ALONG_COLUMN).getMapName(index); } else if (m_xml.getMappingType(CiftiXML::ALONG_COLUMN) == CiftiMappingType::LABELS) { return m_xml.getLabelsMap(CiftiXML::ALONG_COLUMN).getMapName(index); } return ""; } AString CiftiInterface::getMapNameForRowIndex(const int& index) const { if (m_xml.getMappingType(CiftiXML::ALONG_ROW) == CiftiMappingType::SCALARS) { return m_xml.getScalarsMap(CiftiXML::ALONG_ROW).getMapName(index); } else if (m_xml.getMappingType(CiftiXML::ALONG_ROW) == CiftiMappingType::LABELS) { return m_xml.getLabelsMap(CiftiXML::ALONG_ROW).getMapName(index); } return ""; } CiftiInterface::~CiftiInterface() { } connectome-workbench-1.2.3+git41-gc4c6c90/src/Cifti/CiftiInterface.h000066400000000000000000000143661300200146000246440ustar00rootroot00000000000000#ifndef __CIFTI_INTERFACE_H__ #define __CIFTI_INTERFACE_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 "CiftiXML.h" #include "CiftiXMLOld.h" namespace caret { class CiftiInterface { mutable bool m_dataRangeValid; mutable float m_dataRangeMin, m_dataRangeMax; protected: CiftiXML m_xml; CiftiInterface(); public: bool checkRowIndex(int64_t index) const; bool checkColumnIndex(int64_t index) const; ///get a row virtual void getRow(float* rowOut, const int64_t& rowIndex) const = 0; ///get a column virtual void getColumn(float* columnOut, const int64_t& columnIndex) const = 0; ///get row size virtual int64_t getNumberOfColumns() const = 0; ///get column size virtual int64_t getNumberOfRows() const = 0; ///get a reference to the XML structure const CiftiXML& getCiftiXML() const { return m_xml; } ///get the old XML structure CiftiXMLOld getCiftiXMLOld() const; ///get a row by surface and node - returns false if not found in mapping bool getRowFromNode(float* rowOut, const int64_t node, const StructureEnum::Enum structure) const; ///get a row by surface and node - returns false if not found in mapping bool getRowFromNode(float* rowOut, const int64_t node, const StructureEnum::Enum structure, int64_t& rowIndexOut) const; ///get a column by surface and node - returns false if not found in mapping bool getColumnFromNode(float* columnOut, const int64_t node, const StructureEnum::Enum structure) const; ///get a row by voxel index - returns false if not found in mapping bool getRowFromVoxel(float* rowOut, const int64_t* ijk) const; ///get a column by voxel index - returns false if not found in mapping bool getColumnFromVoxel(float* columnOut, const int64_t* ijk) const; ///get a row by voxel coordinate - returns false if not found in mapping bool getRowFromVoxelCoordinate(float* rowOut, const float* xyz) const; ///get a row by voxel coordinate - returns false if not found in mapping bool getRowFromVoxelCoordinate(float* rowOut, const float* xyz, int64_t& rowIndexOut) const; ///get a column by voxel coordinate - returns false if not found in mapping bool getColumnFromVoxelCoordinate(float* columnOut, const float* xyz) const; ///get a row by timepoint bool getRowFromTimepoint(float* rowOut, const float seconds) const; ///get a column by timepoint bool getColumnFromTimepoint(float* columnOut, const float seconds) const; ///get a column by frame bool getColumnFromFrame(float* columnOut, const int frame) const; ///get data range bool getDataRangeFromAllMaps(float& minOut, float& maxOut) const; ///called when something changes the data void invalidateDataRange(); ///get the mapping for a surface in rows, returns false and empty vector if not found bool getSurfaceMapForRows(std::vector& mappingOut, const StructureEnum::Enum structure) const; ///get the mapping for a surface in columns, returns false and empty vector if not found bool getSurfaceMapForColumns(std::vector& mappingOut, const StructureEnum::Enum structure) const; ///get the mapping for a surface in rows, returns false and empty vector if not found bool getVolumeMapForRows(std::vector& mappingOut) const; ///get the mapping for a surface in columns, returns false and empty vector if not found bool getVolumeMapForColumns(std::vector& mappingOut) const; ///get the original number of nodes of the surfaces used to make this cifti, for rows int64_t getRowSurfaceNumberOfNodes(const StructureEnum::Enum structure) const; ///get the original number of nodes of the surfaces used to make this cifti, for columns int64_t getColumnSurfaceNumberOfNodes(const StructureEnum::Enum structure) const; ///get the timestep for rows, returns false if not timeseries bool getRowTimestep(float& seconds) const; ///get the timestep for columns, returns false if not timeseries bool getColumnTimestep(float& seconds) const; ///get dimensions, spacing, origin for the volume attribute - returns false if not plumb - NOTE: only uses the volume space of the first dimension that has one bool getVolumeAttributesForPlumb(VolumeSpace::OrientTypes orientOut[3], int64_t dimensionsOut[3], float originOut[3], float spacingOut[3]) const; bool hasRowVolumeData() const; bool hasColumnVolumeData() const; bool hasRowSurfaceData(const StructureEnum::Enum structure) const; bool hasColumnSurfaceData(const StructureEnum::Enum structure) const; ///get the map name for an index along a column AString getMapNameForColumnIndex(const int& index) const; ///get the map name for an index along a row AString getMapNameForRowIndex(const int& index) const; virtual ~CiftiInterface(); }; } #endif //__CIFTI_INTERFACE_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Cifti/CiftiLabelsMap.cxx000066400000000000000000000253461300200146000251570ustar00rootroot00000000000000/*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 "CiftiLabelsMap.h" #include "CaretAssert.h" #include "CaretException.h" #include "CaretLogger.h" using namespace caret; void CiftiLabelsMap::clear() { m_maps.clear(); } GiftiLabelTable* CiftiLabelsMap::getMapLabelTable(const int64_t& index) const { CaretAssertVectorIndex(m_maps, index); return &(m_maps[index].m_labelTable); } GiftiMetaData* CiftiLabelsMap::getMapMetadata(const int64_t& index) const { CaretAssertVectorIndex(m_maps, index); return &(m_maps[index].m_metaData); } const QString& CiftiLabelsMap::getMapName(const int64_t& index) const { CaretAssertVectorIndex(m_maps, index); return m_maps[index].m_name; } int64_t CiftiLabelsMap::getIndexFromNumberOrName(const QString& numberOrName) const { bool ok = false; int64_t ret = numberOrName.toLongLong(&ok) - 1;//quirk: use string "1" as the first index if (ok) { if (ret < 0 || ret >= getLength()) return -1;//if it is a number, do not try to use it as a name, under any circumstances return ret; } else { int64_t length = getLength(); for (int64_t i = 0; i < length; ++i) { if (numberOrName == getMapName(i)) return i; } return -1; } } void CiftiLabelsMap::setLength(const int64_t& length) { CaretAssert(length > 0); m_maps.resize(length); } void CiftiLabelsMap::setMapName(const int64_t& index, const QString& mapName) const { CaretAssertVectorIndex(m_maps, index); m_maps[index].m_name = mapName; m_namesModified = true; } bool CiftiLabelsMap::approximateMatch(const CiftiMappingType& rhs, QString* explanation) const { switch (rhs.getType()) { case SCALARS: case SERIES://maybe? case LABELS: if (getLength() != rhs.getLength()) { if (explanation != NULL) *explanation = "mappings have different length"; return false; } else return true; default: if (explanation != NULL) *explanation = CiftiMappingType::mappingTypeToName(rhs.getType()) + " mapping never matches " + CiftiMappingType::mappingTypeToName(getType()); return false; } } bool CiftiLabelsMap::operator==(const CiftiMappingType& rhs) const { if (rhs.getType() != getType()) return false; const CiftiLabelsMap& myrhs = dynamic_cast(rhs); return (m_maps == myrhs.m_maps); } bool CiftiLabelsMap::LabelMap::operator==(const LabelMap& rhs) const { if (m_name != rhs.m_name) return false; if (m_labelTable != rhs.m_labelTable) return false; return (m_metaData == rhs.m_metaData); } void CiftiLabelsMap::readXML1(QXmlStreamReader& xml) { CaretLogFiner("parsing nonstandard labels mapping type in cifti-1"); clear(); for (xml.readNext(); !xml.atEnd() && !xml.isEndElement(); xml.readNext()) { switch (xml.tokenType()) { case QXmlStreamReader::StartElement: { if (xml.name() != "NamedMap") { throw CaretException("unexpected element in labels map: " + xml.name().toString()); } LabelMap tempMap; tempMap.readXML1(xml); if (xml.hasError()) return; m_maps.push_back(tempMap); break; } default: break; } } } void CiftiLabelsMap::readXML2(QXmlStreamReader& xml) { clear(); for (xml.readNext(); !xml.atEnd() && !xml.isEndElement(); xml.readNext()) { switch (xml.tokenType()) { case QXmlStreamReader::StartElement: { if (xml.name() != "NamedMap") { throw CaretException("unexpected element in labels map: " + xml.name().toString()); } LabelMap tempMap; tempMap.readXML2(xml); if (xml.hasError()) return; m_maps.push_back(tempMap); break; } default: break; } } } void CiftiLabelsMap::LabelMap::readXML1(QXmlStreamReader& xml) { bool haveName = false, haveTable = false, haveMetaData = false; for (xml.readNext(); !xml.atEnd() && !xml.isEndElement(); xml.readNext()) { switch (xml.tokenType()) { case QXmlStreamReader::StartElement: { QStringRef name = xml.name(); if (name == "MetaData") { if (haveMetaData) { throw CaretException("MetaData specified multiple times in one NamedMap"); } m_metaData.readCiftiXML1(xml); if (xml.hasError()) return; haveMetaData = true; } else if (name == "LabelTable") { if (haveTable) { throw CaretException("LabelTable specified multiple times in one NamedMap"); } m_labelTable.readFromQXmlStreamReader(xml); if (xml.hasError()) return; haveTable = true; } else if (name == "MapName") { if (haveName) { throw CaretException("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()); } break; } default: break; } } if (!haveName) { throw CaretException("NamedMap missing required child element MapName"); } if (!haveTable) { throw CaretException("NamedMap in labels mapping missing required child element LabelTable"); } } void CiftiLabelsMap::LabelMap::readXML2(QXmlStreamReader& xml) { bool haveName = false, haveTable = false, haveMetaData = false; for (xml.readNext(); !xml.atEnd() && !xml.isEndElement(); xml.readNext()) { switch (xml.tokenType()) { case QXmlStreamReader::StartElement: { QStringRef name = xml.name(); if (name == "MetaData") { if (haveMetaData) { throw CaretException("MetaData specified multiple times in one NamedMap"); } m_metaData.readCiftiXML2(xml); if (xml.hasError()) return; haveMetaData = true; } else if (name == "LabelTable") { if (haveTable) { throw CaretException("LabelTable specified multiple times in one NamedMap"); } m_labelTable.readFromQXmlStreamReader(xml); if (xml.hasError()) return; haveTable = true; } else if (name == "MapName") { if (haveName) { throw CaretException("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()); } break; } default: break; } } if (!haveName) { throw CaretException("NamedMap missing required child element MapName"); } if (!haveTable) { throw CaretException("NamedMap in labels mapping missing required child element LabelTable"); } } void CiftiLabelsMap::writeXML1(QXmlStreamWriter& xml) const { CaretLogFiner("writing nonstandard labels mapping type in cifti-1"); xml.writeAttribute("IndicesMapToDataType", "CIFTI_INDEX_TYPE_LABELS"); int64_t numMaps = (int64_t)m_maps.size(); for (int64_t i = 0; i < numMaps; ++i) { xml.writeStartElement("NamedMap"); xml.writeTextElement("MapName", m_maps[i].m_name); m_maps[i].m_metaData.writeCiftiXML1(xml); m_maps[i].m_labelTable.writeAsXML(xml); xml.writeEndElement(); } } void CiftiLabelsMap::writeXML2(QXmlStreamWriter& xml) const { int64_t numMaps = (int64_t)m_maps.size(); xml.writeAttribute("IndicesMapToDataType", "CIFTI_INDEX_TYPE_LABELS"); for (int64_t i = 0; i < numMaps; ++i) { xml.writeStartElement("NamedMap"); xml.writeTextElement("MapName", m_maps[i].m_name); m_maps[i].m_metaData.writeCiftiXML2(xml); m_maps[i].m_labelTable.writeAsXML(xml); xml.writeEndElement(); } } //support for internal objects that track modified status CiftiLabelsMap::CiftiLabelsMap() { m_namesModified = false; } CiftiLabelsMap::CiftiLabelsMap(const CiftiLabelsMap& rhs) { m_namesModified = false; m_maps = rhs.m_maps;//no idea what happens to the modification status of members here } CiftiLabelsMap& CiftiLabelsMap::operator=(const CiftiLabelsMap& rhs) { m_namesModified = false; m_maps = rhs.m_maps;//ditto return *this; } bool CiftiLabelsMap::mutablesModified() const { if (m_namesModified) return true; for (int64_t i = 0; i < getLength(); ++i) { if (getMapLabelTable(i)->isModified()) return true; if (getMapMetadata(i)->isModified()) return true; } return false; } void CiftiLabelsMap::clearMutablesModified() const { m_namesModified = false; for (int64_t i = 0; i < getLength(); ++i) { getMapLabelTable(i)->clearModified(); getMapMetadata(i)->clearModified(); } } connectome-workbench-1.2.3+git41-gc4c6c90/src/Cifti/CiftiLabelsMap.h000066400000000000000000000060341300200146000245750ustar00rootroot00000000000000#ifndef __CIFTI_LABELS_MAP_H__ #define __CIFTI_LABELS_MAP_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 "CiftiMappingType.h" #include "CaretPointer.h" #include "GiftiMetaData.h" #include "GiftiLabelTable.h" #include #include namespace caret { class CiftiLabelsMap : public CiftiMappingType { public: CiftiLabelsMap(); CiftiLabelsMap(const CiftiLabelsMap& rhs); CiftiLabelsMap& operator=(const CiftiLabelsMap& rhs); GiftiMetaData* getMapMetadata(const int64_t& index) const;//HACK: allow modification of label table and metadata within XML without setting the xml on a file again GiftiLabelTable* getMapLabelTable(const int64_t& index) const; const QString& getMapName(const int64_t& index) const; int64_t getIndexFromNumberOrName(const QString& numberOrName) const; QString getIndexName(const int64_t& index) const { return getMapName(index); } void setMapName(const int64_t& index, const QString& mapName) const;//HACK: ditto void setLength(const int64_t& length); void clear(); CiftiMappingType* clone() const { return new CiftiLabelsMap(*this); } MappingType getType() const { return LABELS; } int64_t getLength() const { return m_maps.size(); } bool operator==(const CiftiMappingType& rhs) const; bool approximateMatch(const CiftiMappingType& rhs, QString* explanation = NULL) const; void readXML1(QXmlStreamReader& xml); void readXML2(QXmlStreamReader& xml); void writeXML1(QXmlStreamWriter& xml) const; void writeXML2(QXmlStreamWriter& xml) const; bool mutablesModified() const; void clearMutablesModified() const; private: mutable bool m_namesModified; struct LabelMap { mutable QString m_name;//we need a better way to change metadata in an in-memory file mutable GiftiMetaData m_metaData;//ditto mutable GiftiLabelTable m_labelTable;//ditto bool operator==(const LabelMap& rhs) const; void readXML1(QXmlStreamReader& xml); void readXML2(QXmlStreamReader& xml); }; std::vector m_maps; }; } #endif //__CIFTI_LABELS_MAP_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Cifti/CiftiMappingType.cxx000066400000000000000000000036621300200146000255510ustar00rootroot00000000000000/*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 "CiftiMappingType.h" #include "CaretAssert.h" using namespace caret; CiftiMappingType::~CiftiMappingType() {//to ensure that the class's vtable gets defined in an object file } int64_t CiftiMappingType::getIndexFromNumberOrName(const QString& numberOrName) const { bool ok = false; int64_t ret = numberOrName.toLongLong(&ok) - 1;//quirk: use string "1" as the first index if (!ok) return -1; if (ret < 0 || ret >= getLength()) return -1; return ret; } QString CiftiMappingType::getIndexName(const int64_t&) const { return ""; } QString CiftiMappingType::mappingTypeToName(const CiftiMappingType::MappingType& type) { switch (type) { case BRAIN_MODELS: return "BRAIN_MODELS"; case PARCELS: return "PARCELS"; case SERIES: return "SERIES"; case SCALARS: return "SCALARS"; case LABELS: return "LABELS"; } CaretAssert(0); return ""; } bool CiftiMappingType::mutablesModified() const { return false; } void CiftiMappingType::clearMutablesModified() const { //nothing } connectome-workbench-1.2.3+git41-gc4c6c90/src/Cifti/CiftiMappingType.h000066400000000000000000000053531300200146000251750ustar00rootroot00000000000000#ifndef __CIFTI_MAPPING_TYPE_H__ #define __CIFTI_MAPPING_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*/ #include "stdint.h" #include #include #include namespace caret { class CiftiMappingType { public: enum MappingType { BRAIN_MODELS = 1,//compatibility values with old XML enum, in case someone uses the wrong enum PARCELS = 3,//fibers doesn't exist in 2.0 SERIES = 4, SCALARS = 5, LABELS = 6 }; virtual CiftiMappingType* clone() const = 0;//make a copy, preserving the actual type - NOTE: this returns a dynamic allocation that is not owned by anything virtual MappingType getType() const = 0; virtual int64_t getLength() const = 0; virtual int64_t getIndexFromNumberOrName(const QString& numberOrName) const; virtual QString getIndexName(const int64_t& index) const; virtual bool operator==(const CiftiMappingType& rhs) const = 0;//used to check for merging mappings when writing the XML - must compare EVERYTHING that goes into the XML bool operator!=(const CiftiMappingType& rhs) const { return !((*this) == rhs); } virtual bool approximateMatch(const CiftiMappingType& rhs, QString* explanation = NULL) const = 0;//check if things like doing index-wise math would make sense virtual void readXML1(QXmlStreamReader& xml) = 0;//mainly to shorten the type-specific code in CiftiXML virtual void readXML2(QXmlStreamReader& xml) = 0; virtual void writeXML1(QXmlStreamWriter& xml) const = 0; virtual void writeXML2(QXmlStreamWriter& xml) const = 0; virtual bool mutablesModified() const; virtual void clearMutablesModified() const;//HACK: clear modified status on a const object virtual ~CiftiMappingType(); static QString mappingTypeToName(const MappingType& type); }; } #endif //__CIFTI_MAPPING_TYPE_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Cifti/CiftiParcelsMap.cxx000066400000000000000000000743141300200146000253450ustar00rootroot00000000000000/*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 "CiftiParcelsMap.h" #include "CaretException.h" #include "CaretLogger.h" #include #include using namespace std; using namespace caret; void CiftiParcelsMap::addParcel(const CiftiParcelsMap::Parcel& parcel) { int64_t thisParcel = m_parcels.size();//slight hack: current number of parcels will be this parcel's index for (int64_t i = 0; i < thisParcel; ++i)//also use it as endpoint of looping over existing parcels { 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 } } int64_t voxelListSize = (int64_t)parcel.m_voxelIndices.size(); CaretCompact3DLookup tempLookup = m_volLookup;//a copy of the lookup should be faster than other methods of checking for overlap and repeat if (voxelListSize != 0) { const int64_t* dims = NULL; if (!m_ignoreVolSpace) { if (!m_haveVolumeSpace) { throw CaretException("you must set the volume space before adding parcels that use voxels"); } dims = m_volSpace.getDims(); } for (set::const_iterator iter = parcel.m_voxelIndices.begin(); iter != parcel.m_voxelIndices.end(); ++iter)//do all error checking before adding to lookup - might be unnecessary { 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"); } 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"); } if (tempLookup.find(iter->m_ijk) != NULL) { throw CaretException("parcels may not overlap in voxels"); } tempLookup.at(iter->m_ijk) = thisParcel; } } for (map >::const_iterator iter = parcel.m_surfaceNodes.begin(); iter != parcel.m_surfaceNodes.end(); ++iter) { 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"); } 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 } for (set::const_iterator iter2 = nodeSet.begin(); iter2 != nodeSet.end(); ++iter2) { if (*iter2 < 0) { throw CaretException("found negative vertex in parcel"); } if (*iter2 >= info->second.m_numNodes) { throw CaretException("found invalid vertex in parcel"); } if (info->second.m_lookup[*iter2] != -1) { throw CaretException("parcels may not overlap in vertices"); } } } if (voxelListSize != 0)//all error checking done, modify { m_volLookup = tempLookup; } for (map >::const_iterator iter = parcel.m_surfaceNodes.begin(); iter != parcel.m_surfaceNodes.end(); ++iter) { map::iterator info = m_surfInfo.find(iter->first); CaretAssert(info != m_surfInfo.end()); const set& nodeSet = iter->second; for (set::const_iterator iter2 = nodeSet.begin(); iter2 != nodeSet.end(); ++iter2) { CaretAssertVectorIndex(info->second.m_lookup, *iter2); info->second.m_lookup[*iter2] = thisParcel; } } m_parcels.push_back(parcel); } void CiftiParcelsMap::addSurface(const int64_t& numberOfNodes, const StructureEnum::Enum& structure) { 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"); } SurfaceInfo tempInfo; tempInfo.m_numNodes = numberOfNodes; tempInfo.m_lookup.resize(numberOfNodes, -1); m_surfInfo[structure] = tempInfo; } void CiftiParcelsMap::clear() { m_haveVolumeSpace = false; m_ignoreVolSpace = false; m_parcels.clear(); m_surfInfo.clear(); m_volLookup.clear(); } void CiftiParcelsMap::setVolumeSpace(const VolumeSpace& space) { const int64_t* dims = space.getDims(); int64_t numParcels = (int64_t)m_parcels.size(); for (int64_t i = 0; i < numParcels; ++i) { const set& voxelList = m_parcels[i].m_voxelIndices; for (set::const_iterator iter = voxelList.begin(); iter != voxelList.end(); ++iter) { if (iter->m_ijk[0] >= dims[0] || iter->m_ijk[1] >= dims[1] || iter->m_ijk[2] >= dims[2]) { throw CaretException("parcels may not contain voxel indices outside the volume space"); } } } m_haveVolumeSpace = true; m_ignoreVolSpace = false; m_volSpace = space; } int64_t CiftiParcelsMap::getIndexForNode(const int64_t& node, const StructureEnum::Enum& structure) const { CaretAssert(node >= 0); map::const_iterator test = m_surfInfo.find(structure); if (test == m_surfInfo.end()) return -1; if (node >= test->second.m_numNodes) return -1; CaretAssertVectorIndex(test->second.m_lookup, node); return test->second.m_lookup[node]; } int64_t CiftiParcelsMap::getIndexForVoxel(const int64_t* ijk) const { return getIndexForVoxel(ijk[0], ijk[1], ijk[2]); } int64_t CiftiParcelsMap::getIndexForVoxel(const int64_t& i, const int64_t& j, const int64_t& k) const { const int64_t* test = m_volLookup.find(i, j, k);//the lookup tolerates weirdness like negatives if (test == NULL) return -1; return *test; } vector CiftiParcelsMap::getParcelSurfaceStructures() const { vector ret; ret.reserve(m_surfInfo.size()); for (map::const_iterator iter = m_surfInfo.begin(); iter != m_surfInfo.end(); ++iter) { ret.push_back(iter->first); } return ret; } int64_t CiftiParcelsMap::getIndexFromNumberOrName(const QString& numberOrName) const { bool ok = false; int64_t ret = numberOrName.toLongLong(&ok) - 1;//quirk: use string "1" as the first index if (ok) { if (ret < 0 || ret >= getLength()) return -1;//if it is a number, do not try to use it as a name, under any circumstances return ret; } else { int64_t length = getLength(); for (int64_t i = 0; i < length; ++i) { if (numberOrName == m_parcels[i].m_name) return i; } return -1; } } QString CiftiParcelsMap::getIndexName(const int64_t& index) const { CaretAssertVectorIndex(m_parcels, index); return m_parcels[index].m_name; } 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"); } return m_volSpace; } int64_t CiftiParcelsMap::getSurfaceNumberOfNodes(const StructureEnum::Enum& structure) const { map::const_iterator iter = m_surfInfo.find(structure); if (iter == m_surfInfo.end()) return -1; return iter->second.m_numNodes; } bool CiftiParcelsMap::hasSurface(const StructureEnum::Enum& structure) const { return m_surfInfo.find(structure) != m_surfInfo.end(); } bool CiftiParcelsMap::hasSurfaceData(const StructureEnum::Enum& structure) const { if (m_surfInfo.find(structure) == m_surfInfo.end()) return false; int64_t numParcels = (int64_t)m_parcels.size(); for (int64_t i = 0; i < numParcels; ++i) { map >::const_iterator iter = m_parcels[i].m_surfaceNodes.find(structure); if (iter != m_parcels[i].m_surfaceNodes.end() && iter->second.size() != 0) return true; } return false; } bool CiftiParcelsMap::hasVolumeData() const { CaretAssert(!m_ignoreVolSpace); int64_t numParcels = (int64_t)m_parcels.size();//NOTE: this function is used when reading cifti-1 to determine whether it is an error to not have a volume space, so we can't just check m_haveVolumeSpace for (int64_t i = 0; i < numParcels; ++i) { if (m_parcels[i].m_voxelIndices.size() != 0) return true; } return false; } bool CiftiParcelsMap::approximateMatch(const CiftiMappingType& rhs, QString* explanation) const { if (rhs.getType() != getType()) { if (explanation != NULL) *explanation = CiftiMappingType::mappingTypeToName(rhs.getType()) + " mapping never matches " + CiftiMappingType::mappingTypeToName(getType()); return false; } const CiftiParcelsMap& myrhs = dynamic_cast(rhs); CaretAssert(!m_ignoreVolSpace && !myrhs.m_ignoreVolSpace); if (m_haveVolumeSpace != myrhs.m_haveVolumeSpace) { if (explanation != NULL) *explanation = "one of the mappings has no volume data"; return false; } if (m_haveVolumeSpace && (m_volSpace != myrhs.m_volSpace)) { if (explanation != NULL) *explanation = "mappings have a different volume space"; return false; } if (m_surfInfo.size() != myrhs.m_surfInfo.size()) { if (explanation != NULL) *explanation = "mappings have a different number of surfaces used"; return false;//as below, return false if they won't write the mapping part to xml the same - 1 to 1 compare only requires 1 simple loop } for (map::const_iterator iter = m_surfInfo.begin(); iter != m_surfInfo.end(); ++iter) { map::const_iterator rhsiter = myrhs.m_surfInfo.find(iter->first); if (rhsiter == myrhs.m_surfInfo.end()) {//technically, they might still have the same meaning, if the surface isn't used, but they will still write differently, so false if (explanation != NULL) *explanation = StructureEnum::toName(iter->first) + " surface expected but not found"; return false; } if (iter->second.m_numNodes != rhsiter->second.m_numNodes) { if (explanation != NULL) *explanation = "different number of vertices for surface " + StructureEnum::toName(iter->first); return false; } } if (m_parcels.size() != myrhs.m_parcels.size()) { if (explanation != NULL) *explanation = "different number of parcels"; return false; } for (int64_t i = 0; i < (int64_t)m_parcels.size(); ++i) { if (!m_parcels[i].approximateMatch(myrhs.m_parcels[i], explanation)) return false; } return true; } bool CiftiParcelsMap::operator==(const CiftiMappingType& rhs) const { if (rhs.getType() != getType()) return false; const CiftiParcelsMap& myrhs = dynamic_cast(rhs); CaretAssert(!m_ignoreVolSpace && !myrhs.m_ignoreVolSpace); if (m_haveVolumeSpace != myrhs.m_haveVolumeSpace) return false; if (m_haveVolumeSpace && m_volSpace != myrhs.m_volSpace) return false; if (m_surfInfo.size() != myrhs.m_surfInfo.size()) return false;//as below, return false if they won't write to xml the same - 1 to 1 compare only requires 1 simple loop for (map::const_iterator iter = m_surfInfo.begin(); iter != m_surfInfo.end(); ++iter) { map::const_iterator rhsiter = myrhs.m_surfInfo.find(iter->first); if (rhsiter == myrhs.m_surfInfo.end()) return false;//technically, they might still have the same meaning, if the surface isn't used, but they will still write differently, so false if (iter->second.m_numNodes != rhsiter->second.m_numNodes) return false; } return (m_parcels == myrhs.m_parcels); } bool CiftiParcelsMap::Parcel::operator==(const CiftiParcelsMap::Parcel& rhs) const { if (m_name != rhs.m_name) return false; if (m_voxelIndices != rhs.m_voxelIndices) return false; return (m_surfaceNodes == rhs.m_surfaceNodes); } //same, but don't test name bool CiftiParcelsMap::Parcel::approximateMatch(const CiftiParcelsMap::Parcel& rhs, QString* explanation) const { bool nameMatches = (m_name == rhs.m_name);//for more informative explanations if (m_voxelIndices != rhs.m_voxelIndices) { if (explanation != NULL) { if (nameMatches) { *explanation = "parcel '" + m_name + "' uses different voxels than parcel in other map"; } else { *explanation = "parcel '" + m_name + "' uses different voxels than same-index parcel '" + rhs.m_name + "' in other map"; } } return false; } if (m_surfaceNodes != rhs.m_surfaceNodes) { if (explanation != NULL) { if (nameMatches) { *explanation = "parcel '" + m_name + "' uses different surface vertices than parcel in other map"; } else { *explanation = "parcel '" + m_name + "' uses different surface vertices than same-index parcel '" + rhs.m_name + "' in other map"; } } return false; } return true; } void CiftiParcelsMap::readXML1(QXmlStreamReader& xml) { CaretLogFiner("parsing nonstandard parcels mapping type in cifti-1"); clear(); m_ignoreVolSpace = true;//cifti-1 has volume space outside the index map vector myParcels;//because we need to add the surfaces first while (xml.readNextStartElement()) { QStringRef name = xml.name(); if (name == "Surface") { QXmlStreamAttributes attrs = xml.attributes(); if (!attrs.hasAttribute("BrainStructure")) { throw CaretException("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()); } if (!attrs.hasAttribute("SurfaceNumberOfNodes")) { throw CaretException("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()); } addSurface(numNodes, tempStructure);//let the standard modification functions do error checking if (xml.readNextStartElement()) { throw CaretException("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()); } } int64_t numParcels = (int64_t)myParcels.size(); for (int64_t i = 0; i < numParcels; ++i) { addParcel(myParcels[i]); } m_ignoreVolSpace = false;//in case there are no voxels, but some will be added later CaretAssert(xml.isEndElement() && xml.name() == "MatrixIndicesMap"); } void CiftiParcelsMap::readXML2(QXmlStreamReader& xml) { clear(); vector myParcels;//because we need to add the surfaces and volume space first while (xml.readNextStartElement()) { QStringRef name = xml.name(); if (name == "Surface") { QXmlStreamAttributes attrs = xml.attributes(); if (!attrs.hasAttribute("BrainStructure")) { throw CaretException("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()); } if (!attrs.hasAttribute("SurfaceNumberOfVertices")) { throw CaretException("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()); } addSurface(numNodes, tempStructure);//let the standard modification functions do error checking if (xml.readNextStartElement()) { throw CaretException("unexpected element inside Surface: " + xml.name().toString()); } } else if (name == "Parcel") { myParcels.push_back(readParcel2(xml)); if (xml.hasError()) return; } else if (name == "Volume") { if (m_haveVolumeSpace) { throw CaretException("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()); } } int64_t numParcels = (int64_t)myParcels.size(); for (int64_t i = 0; i < numParcels; ++i) { addParcel(myParcels[i]); } CaretAssert(xml.isEndElement() && xml.name() == "MatrixIndicesMap"); } CiftiParcelsMap::Parcel CiftiParcelsMap::readParcel1(QXmlStreamReader& xml) { Parcel ret; bool haveVoxels = false; QXmlStreamAttributes attrs = xml.attributes(); if (!attrs.hasAttribute("Name")) { throw CaretException("Parcel element missing required attribute Name"); } ret.m_name = attrs.value("Name").toString(); while (xml.readNextStartElement()) { QStringRef name = xml.name(); if (name == "Nodes") { QXmlStreamAttributes attrs1 = xml.attributes(); if (!attrs1.hasAttribute("BrainStructure")) { throw CaretException("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()); } if (ret.m_surfaceNodes.find(myStructure) != ret.m_surfaceNodes.end()) { throw CaretException("Nodes elements may not reuse a BrainStructure within a Parcel"); } set& mySet = ret.m_surfaceNodes[myStructure]; vector array = readIndexArray(xml); if (xml.hasError()) return ret; int64_t arraySize = (int64_t)array.size(); for (int64_t i = 0; i < arraySize; ++i) { if (mySet.find(array[i]) != mySet.end()) { throw CaretException("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"); } 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"); } 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"); } ret.m_voxelIndices.insert(temp); } haveVoxels = true; } else { throw CaretException("unexpected element in Parcel: " + name.toString()); } } CaretAssert(xml.isEndElement() && xml.name() == "Parcel"); return ret; } CiftiParcelsMap::Parcel CiftiParcelsMap::readParcel2(QXmlStreamReader& xml) { Parcel ret; bool haveVoxels = false; QXmlStreamAttributes attrs = xml.attributes(); if (!attrs.hasAttribute("Name")) { throw CaretException("Parcel element missing required attribute Name"); } ret.m_name = attrs.value("Name").toString(); while (xml.readNextStartElement()) { QStringRef name = xml.name(); if (name == "Vertices") { QXmlStreamAttributes attrs1 = xml.attributes(); if (!attrs1.hasAttribute("BrainStructure")) { throw CaretException("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()); } if (ret.m_surfaceNodes.find(myStructure) != ret.m_surfaceNodes.end()) { throw CaretException("Vertices elements may not reuse a BrainStructure within a Parcel"); } set& mySet = ret.m_surfaceNodes[myStructure]; vector array = readIndexArray(xml); if (xml.hasError()) return ret; int64_t arraySize = (int64_t)array.size(); for (int64_t i = 0; i < arraySize; ++i) { if (mySet.find(array[i]) != mySet.end()) { throw CaretException("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"); } 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"); } 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"); } ret.m_voxelIndices.insert(temp); } haveVoxels = true; } else { throw CaretException("unexpected element in Parcel: " + name.toString()); } } CaretAssert(xml.isEndElement() && xml.name() == "Parcel"); return ret; } vector CiftiParcelsMap::readIndexArray(QXmlStreamReader& xml) { vector ret; QString text = xml.readElementText();//raises error if it encounters a start element if (xml.hasError()) return ret; QStringList separated = text.split(QRegExp("\\s+"), QString::SkipEmptyParts); int64_t numElems = separated.size(); ret.reserve(numElems); for (int64_t i = 0; i < numElems; ++i) { bool ok = false; ret.push_back(separated[i].toLongLong(&ok)); if (!ok) { throw CaretException("found noninteger in index array: " + separated[i]); } } return ret; } void CiftiParcelsMap::writeXML1(QXmlStreamWriter& xml) const { CaretAssert(!m_ignoreVolSpace); CaretLogFiner("writing nonstandard parcels mapping type in cifti-1"); xml.writeAttribute("IndicesMapToDataType", "CIFTI_INDEX_TYPE_PARCELS"); for (map::const_iterator iter = m_surfInfo.begin(); iter != m_surfInfo.end(); ++iter) { xml.writeStartElement("Surface"); xml.writeAttribute("BrainStructure", StructureEnum::toCiftiName(iter->first)); xml.writeAttribute("SurfaceNumberOfNodes", QString::number(iter->second.m_numNodes)); xml.writeEndElement(); } int64_t numParcels = m_parcels.size(); for (int64_t i = 0; i < numParcels; ++i) { xml.writeStartElement("Parcel"); xml.writeAttribute("Name", m_parcels[i].m_name); int64_t numVoxels = (int64_t)m_parcels[i].m_voxelIndices.size(); if (numVoxels != 0) { xml.writeStartElement("VoxelIndicesIJK"); for (set::const_iterator iter = m_parcels[i].m_voxelIndices.begin(); iter != m_parcels[i].m_voxelIndices.end(); ++iter) { xml.writeCharacters(QString::number(iter->m_ijk[0]) + " " + QString::number(iter->m_ijk[1]) + " " + QString::number(iter->m_ijk[2]) + "\n"); } xml.writeEndElement(); } for (map >::const_iterator iter = m_parcels[i].m_surfaceNodes.begin(); iter != m_parcels[i].m_surfaceNodes.end(); ++iter) { if (iter->second.size() != 0)//prevent writing empty elements, regardless { xml.writeStartElement("Nodes"); xml.writeAttribute("BrainStructure", StructureEnum::toCiftiName(iter->first)); set::const_iterator iter2 = iter->second.begin();//which also allows us to write the first one outside the loop, to not add whitespace on the front or back xml.writeCharacters(QString::number(*iter2)); ++iter2; for (; iter2 != iter->second.end(); ++iter2) { xml.writeCharacters(" " + QString::number(*iter2)); } xml.writeEndElement(); } } xml.writeEndElement(); } } void CiftiParcelsMap::writeXML2(QXmlStreamWriter& xml) const { CaretAssert(!m_ignoreVolSpace); xml.writeAttribute("IndicesMapToDataType", "CIFTI_INDEX_TYPE_PARCELS"); if (hasVolumeData())//could be m_haveVolumeSpace if we want to be able to write a volspace without having voxels, but that seems silly { m_volSpace.writeCiftiXML2(xml); } for (map::const_iterator iter = m_surfInfo.begin(); iter != m_surfInfo.end(); ++iter) { xml.writeStartElement("Surface"); xml.writeAttribute("BrainStructure", StructureEnum::toCiftiName(iter->first)); xml.writeAttribute("SurfaceNumberOfVertices", QString::number(iter->second.m_numNodes)); xml.writeEndElement(); } int64_t numParcels = m_parcels.size(); for (int64_t i = 0; i < numParcels; ++i) { xml.writeStartElement("Parcel"); xml.writeAttribute("Name", m_parcels[i].m_name); int64_t numVoxels = (int64_t)m_parcels[i].m_voxelIndices.size(); if (numVoxels != 0) { xml.writeStartElement("VoxelIndicesIJK"); for (set::const_iterator iter = m_parcels[i].m_voxelIndices.begin(); iter != m_parcels[i].m_voxelIndices.end(); ++iter) { xml.writeCharacters(QString::number(iter->m_ijk[0]) + " " + QString::number(iter->m_ijk[1]) + " " + QString::number(iter->m_ijk[2]) + "\n"); } xml.writeEndElement(); } for (map >::const_iterator iter = m_parcels[i].m_surfaceNodes.begin(); iter != m_parcels[i].m_surfaceNodes.end(); ++iter) { if (iter->second.size() != 0)//prevent writing empty elements, regardless { xml.writeStartElement("Vertices"); xml.writeAttribute("BrainStructure", StructureEnum::toCiftiName(iter->first)); set::const_iterator iter2 = iter->second.begin();//which also allows us to write the first one outside the loop, to not add whitespace on the front or back xml.writeCharacters(QString::number(*iter2)); ++iter2; for (; iter2 != iter->second.end(); ++iter2) { xml.writeCharacters(" " + QString::number(*iter2)); } xml.writeEndElement(); } } xml.writeEndElement(); } } connectome-workbench-1.2.3+git41-gc4c6c90/src/Cifti/CiftiParcelsMap.h000066400000000000000000000076601300200146000247720ustar00rootroot00000000000000#ifndef __CIFTI_PARCELS_MAP_H__ #define __CIFTI_PARCELS_MAP_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 "CiftiMappingType.h" #include "CaretCompact3DLookup.h" #include "StructureEnum.h" #include "VolumeSpace.h" #include "VoxelIJK.h" #include #include #include namespace caret { class CiftiParcelsMap : public CiftiMappingType { public: struct Parcel { std::map > m_surfaceNodes; std::set m_voxelIndices; QString m_name; bool operator==(const Parcel& rhs) const; bool operator!=(const Parcel& rhs) const { return !((*this) == rhs); } bool approximateMatch(const Parcel& rhs, QString* explanation = NULL) const; }; bool hasVolumeData() const; bool hasSurface(const StructureEnum::Enum& structure) const;//only checks whether surface has been added/read bool hasSurfaceData(const StructureEnum::Enum& structure) const; const VolumeSpace& getVolumeSpace() const; int64_t getSurfaceNumberOfNodes(const StructureEnum::Enum& structure) const; int64_t getIndexForNode(const int64_t& node, const StructureEnum::Enum& structure) const; int64_t getIndexForVoxel(const int64_t* ijk) const; int64_t getIndexForVoxel(const int64_t& i, const int64_t& j, const int64_t& k) const; std::vector getParcelSurfaceStructures() const; const std::vector& getParcels() const { return m_parcels; } int64_t getIndexFromNumberOrName(const QString& numberOrName) const; QString getIndexName(const int64_t& index) const; CiftiParcelsMap() { m_haveVolumeSpace = false; m_ignoreVolSpace = false; } void addSurface(const int64_t& numberOfNodes, const StructureEnum::Enum& structure); void setVolumeSpace(const VolumeSpace& space); void addParcel(const Parcel& parcel); void clear(); CiftiMappingType* clone() const { return new CiftiParcelsMap(*this); } MappingType getType() const { return PARCELS; } int64_t getLength() const { return m_parcels.size(); } bool operator==(const CiftiMappingType& rhs) const; bool approximateMatch(const CiftiMappingType& rhs, QString* explanation = NULL) const; void readXML1(QXmlStreamReader& xml); void readXML2(QXmlStreamReader& xml); void writeXML1(QXmlStreamWriter& xml) const; void writeXML2(QXmlStreamWriter& xml) const; private: std::vector m_parcels; VolumeSpace m_volSpace; bool m_haveVolumeSpace, m_ignoreVolSpace;//second is needed for parsing cifti-1 struct SurfaceInfo { int64_t m_numNodes; std::vector m_lookup; }; CaretCompact3DLookup m_volLookup; std::map m_surfInfo; static Parcel readParcel1(QXmlStreamReader& xml); static Parcel readParcel2(QXmlStreamReader& xml); static std::vector readIndexArray(QXmlStreamReader& xml); }; } #endif //__CIFTI_PARCELS_MAP_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Cifti/CiftiScalarsMap.cxx000066400000000000000000000303621300200146000253370ustar00rootroot00000000000000/*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 "CiftiScalarsMap.h" #include "CaretAssert.h" #include "CaretException.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" using namespace caret; void CiftiScalarsMap::clear() { m_maps.clear(); } GiftiMetaData* CiftiScalarsMap::getMapMetadata(const int64_t& index) const { CaretAssertVectorIndex(m_maps, index); return &(m_maps[index].m_metaData); } const QString& CiftiScalarsMap::getMapName(const int64_t& index) const { CaretAssertVectorIndex(m_maps, index); return m_maps[index].m_name; } int64_t CiftiScalarsMap::getIndexFromNumberOrName(const QString& numberOrName) const { bool ok = false; int64_t ret = numberOrName.toLongLong(&ok) - 1;//quirk: use string "1" as the first index if (ok) { if (ret < 0 || ret >= getLength()) return -1;//if it is a number, do not try to use it as a name, under any circumstances return ret; } else { int64_t length = getLength(); for (int64_t i = 0; i < length; ++i) { if (numberOrName == getMapName(i)) return i; } return -1; } } PaletteColorMapping* CiftiScalarsMap::getMapPalette(const int64_t& index) const { CaretAssertVectorIndex(m_maps, index); return m_maps[index].getPalette(); } PaletteColorMapping* CiftiScalarsMap::ScalarMap::getPalette() const { if (m_palette != NULL) { return m_palette; } m_palette.grabNew(new PaletteColorMapping()); if (m_metaData.exists("PaletteColorMapping")) { try { m_palette->decodeFromStringXML(m_metaData.get("PaletteColorMapping")); } catch (XmlException& e) { CaretLogWarning("failed to parse palette settings from metadata: " + e.whatString()); } } return m_palette; } void CiftiScalarsMap::setLength(const int64_t& length) { CaretAssert(length > 0); m_maps.resize(length); } void CiftiScalarsMap::setMapName(const int64_t& index, const QString& mapName) const { CaretAssertVectorIndex(m_maps, index); m_maps[index].m_name = mapName; } bool CiftiScalarsMap::approximateMatch(const CiftiMappingType& rhs, QString* explanation) const { switch (rhs.getType()) { case SCALARS: case SERIES://maybe? case LABELS: if (getLength() != rhs.getLength()) { if (explanation != NULL) *explanation = "mappings have different length"; return false; } else return true; default: if (explanation != NULL) *explanation = CiftiMappingType::mappingTypeToName(rhs.getType()) + " mapping never matches " + CiftiMappingType::mappingTypeToName(getType()); return false; } } bool CiftiScalarsMap::operator==(const CiftiMappingType& rhs) const { if (rhs.getType() != getType()) return false; const CiftiScalarsMap& myrhs = dynamic_cast(rhs); return (m_maps == myrhs.m_maps); } bool CiftiScalarsMap::ScalarMap::operator==(const CiftiScalarsMap::ScalarMap& rhs) const { if (m_name != rhs.m_name) return false; if (*(getPalette()) != *(rhs.getPalette())) return false; GiftiMetaData mytemp = m_metaData, rhstemp = rhs.m_metaData; mytemp.remove("PaletteColorMapping");//we already compared the true palettes, so don't compare the metadata that may or may not encode them if (m_metaData.exists(GiftiMetaDataXmlElements::METADATA_NAME_UNIQUE_ID))//HACK: fix the copy-breaks-UUID silliness { mytemp.set(GiftiMetaDataXmlElements::METADATA_NAME_UNIQUE_ID, m_metaData.get(GiftiMetaDataXmlElements::METADATA_NAME_UNIQUE_ID)); } rhstemp.remove("PaletteColorMapping"); if (rhs.m_metaData.exists(GiftiMetaDataXmlElements::METADATA_NAME_UNIQUE_ID)) { rhstemp.set(GiftiMetaDataXmlElements::METADATA_NAME_UNIQUE_ID, rhs.m_metaData.get(GiftiMetaDataXmlElements::METADATA_NAME_UNIQUE_ID)); } return (mytemp == rhstemp); } void CiftiScalarsMap::readXML1(QXmlStreamReader& xml) { CaretLogFiner("parsing nonstandard scalars mapping type in cifti-1"); clear(); for (xml.readNext(); !xml.atEnd() && !xml.isEndElement(); xml.readNext()) { switch (xml.tokenType()) { case QXmlStreamReader::StartElement: { if (xml.name() != "NamedMap") { throw CaretException("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); if (xml.hasError()) return; break; } default: break; } } CaretAssert(xml.isEndElement() && xml.name() == "MatrixIndicesMap"); } void CiftiScalarsMap::readXML2(QXmlStreamReader& xml) { clear(); for (xml.readNext(); !xml.atEnd() && !xml.isEndElement(); xml.readNext()) { switch (xml.tokenType()) { case QXmlStreamReader::StartElement: { if (xml.name() != "NamedMap") { throw CaretException("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); if (xml.hasError()) return; break; } default: break; } } CaretAssert(xml.isEndElement() && xml.name() == "MatrixIndicesMap"); } void CiftiScalarsMap::ScalarMap::readXML1(QXmlStreamReader& xml) { bool haveName = false, haveMetaData = false; for (xml.readNext(); !xml.atEnd() && !xml.isEndElement(); xml.readNext()) { switch (xml.tokenType()) { case QXmlStreamReader::StartElement: { QStringRef name = xml.name(); if (name == "MetaData") { if (haveMetaData) { throw CaretException("MetaData specified multiple times in one NamedMap"); } m_metaData.readCiftiXML1(xml); if (xml.hasError()) return; haveMetaData = true; } else if (name == "MapName") { if (haveName) { throw CaretException("MapName specified multiple times in one NamedMap"); } m_name = xml.readElementText();//raises error if element encountered if (xml.hasError()) return; haveName = true; } else if (name == "LabelTable") { CaretLogWarning("ignoring LabelTable in Cifti-1 Scalars mapping"); xml.readElementText(QXmlStreamReader::SkipChildElements);//accept some malformed Cifti-1 files if (xml.hasError()) return; } else { throw CaretException("unexpected element in NamedMap: " + name.toString()); } break; } default: break; } } if (!haveName) { throw CaretException("NamedMap missing required child element MapName"); } CaretAssert(xml.isEndElement() && xml.name() == "NamedMap"); } void CiftiScalarsMap::ScalarMap::readXML2(QXmlStreamReader& xml) { bool haveName = false, haveMetaData = false; for (xml.readNext(); !xml.atEnd() && !xml.isEndElement(); xml.readNext()) { switch (xml.tokenType()) { case QXmlStreamReader::StartElement: { QStringRef name = xml.name(); if (name == "MetaData") { if (haveMetaData) { throw CaretException("MetaData specified multiple times in one NamedMap"); } m_metaData.readCiftiXML2(xml); if (xml.hasError()) return; haveMetaData = true; } else if (name == "MapName") { if (haveName) { throw CaretException("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()); } break; } default: break; } } if (!haveName) { throw CaretException("NamedMap missing required child element MapName"); } CaretAssert(xml.isEndElement() && xml.name() == "NamedMap"); } void CiftiScalarsMap::writeXML1(QXmlStreamWriter& xml) const { CaretLogFiner("writing nonstandard scalars mapping type in cifti-1"); xml.writeAttribute("IndicesMapToDataType", "CIFTI_INDEX_TYPE_SCALARS"); int64_t numMaps = (int64_t)m_maps.size(); for (int64_t i = 0; i < numMaps; ++i) { xml.writeStartElement("NamedMap"); xml.writeTextElement("MapName", m_maps[i].m_name); if (m_maps[i].m_palette != NULL) { m_maps[i].m_metaData.set("PaletteColorMapping", m_maps[i].m_palette->encodeInXML()); } m_maps[i].m_metaData.writeCiftiXML1(xml); xml.writeEndElement(); } } void CiftiScalarsMap::writeXML2(QXmlStreamWriter& xml) const { int64_t numMaps = (int64_t)m_maps.size(); xml.writeAttribute("IndicesMapToDataType", "CIFTI_INDEX_TYPE_SCALARS"); for (int64_t i = 0; i < numMaps; ++i) { xml.writeStartElement("NamedMap"); xml.writeTextElement("MapName", m_maps[i].m_name); if (m_maps[i].m_palette != NULL) { m_maps[i].m_metaData.set("PaletteColorMapping", m_maps[i].m_palette->encodeInXML()); } m_maps[i].m_metaData.writeCiftiXML1(xml); xml.writeEndElement(); } } //support for internal objects that track modified status CiftiScalarsMap::CiftiScalarsMap() { m_namesModified = false; } CiftiScalarsMap::CiftiScalarsMap(const CiftiScalarsMap& rhs) { m_maps = rhs.m_maps; clearMutablesModified();//this newly created map is not modified } CiftiScalarsMap& CiftiScalarsMap::operator=(const CiftiScalarsMap& rhs) { m_maps = rhs.m_maps; clearMutablesModified();//this map isn't const, so don't count it as modified return *this; } CiftiScalarsMap::CiftiScalarsMap(const int64_t& length) { m_namesModified = false; setLength(length); } bool CiftiScalarsMap::mutablesModified() const { if (m_namesModified) return true; for (int64_t i = 0; i < getLength(); ++i) { if (m_maps[i].m_palette != NULL && m_maps[i].m_palette->isModified()) return true; if (getMapMetadata(i)->isModified()) return true; } return false; } void CiftiScalarsMap::clearMutablesModified() const { m_namesModified = false; for (int64_t i = 0; i < getLength(); ++i) { if (m_maps[i].m_palette != NULL) m_maps[i].m_palette->clearModified(); getMapMetadata(i)->clearModified(); } } connectome-workbench-1.2.3+git41-gc4c6c90/src/Cifti/CiftiScalarsMap.h000066400000000000000000000063571300200146000247730ustar00rootroot00000000000000#ifndef __CIFTI_SCALARS_MAP_H__ #define __CIFTI_SCALARS_MAP_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 "CiftiMappingType.h" #include "CaretPointer.h" #include "GiftiMetaData.h" #include "PaletteColorMapping.h" #include #include namespace caret { class CiftiScalarsMap : public CiftiMappingType { public: CiftiScalarsMap(); CiftiScalarsMap(const CiftiScalarsMap& rhs); CiftiScalarsMap& operator=(const CiftiScalarsMap& rhs); explicit CiftiScalarsMap(const int64_t& length); GiftiMetaData* getMapMetadata(const int64_t& index) const;//HACK: allow modification of palette and metadata within XML without setting the xml on a file again PaletteColorMapping* getMapPalette(const int64_t& index) const; const QString& getMapName(const int64_t& index) const; int64_t getIndexFromNumberOrName(const QString& numberOrName) const; QString getIndexName(const int64_t& index) const { return getMapName(index); } void setMapName(const int64_t& index, const QString& mapName) const;//HACK: ditto void setLength(const int64_t& length); void clear();//do we need this? CiftiMappingType* clone() const { return new CiftiScalarsMap(*this); } MappingType getType() const { return SCALARS; } int64_t getLength() const { return m_maps.size(); } bool operator==(const CiftiMappingType& rhs) const; bool approximateMatch(const CiftiMappingType& rhs, QString* explanation = NULL) const; void readXML1(QXmlStreamReader& xml); void readXML2(QXmlStreamReader& xml); void writeXML1(QXmlStreamWriter& xml) const; void writeXML2(QXmlStreamWriter& xml) const; bool mutablesModified() const; void clearMutablesModified() const; private: mutable bool m_namesModified; struct ScalarMap { mutable QString m_name;//we need a better way to change metadata in an in-memory file mutable GiftiMetaData m_metaData;//ditto mutable CaretPointer m_palette;//ditto - note, this actually gets written into the metadata PaletteColorMapping* getPalette() const; bool operator==(const ScalarMap& rhs) const; void readXML1(QXmlStreamReader& xml); void readXML2(QXmlStreamReader& xml); }; std::vector m_maps; }; } #endif //__CIFTI_SCALARS_MAP_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Cifti/CiftiSeriesMap.cxx000066400000000000000000000207571300200146000252100ustar00rootroot00000000000000/*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 "CiftiSeriesMap.h" #include "CaretAssert.h" #include "CaretException.h" #include "CaretLogger.h" #include using namespace caret; using namespace std; void CiftiSeriesMap::setLength(const int64_t& length) { CaretAssert(length > 0); m_length = length; } CiftiSeriesMap::Unit CiftiSeriesMap::stringToUnit(const QString& string, bool& ok) { ok = true; if (string == "SECOND") { return SECOND; } else if (string == "HERTZ") { return HERTZ; } else if (string == "METER") { return METER; } else if (string == "RADIAN") { return RADIAN; } ok = false; return SECOND; } QString CiftiSeriesMap::unitToString(const CiftiSeriesMap::Unit& theUnit) { switch (theUnit) { case SECOND: return "SECOND"; case HERTZ: return "HERTZ"; case METER: return "METER"; case RADIAN: return "RADIAN"; } CaretAssert(false); return "UNKNOWN"; } vector CiftiSeriesMap::getAllUnits() { vector ret; ret.push_back(SECOND); ret.push_back(HERTZ); ret.push_back(METER); ret.push_back(RADIAN); return ret; } void CiftiSeriesMap::readXML1(QXmlStreamReader& xml) { QXmlStreamAttributes attrs = xml.attributes(); float newStart = 0.0f, newStep = -1.0f, mult = 0.0f; bool ok = false; if (!attrs.hasAttribute("TimeStepUnits")) { throw CaretException("timepoints mapping is missing required attribute TimeStepUnits"); } QStringRef unitString = attrs.value("TimeStepUnits"); if (unitString == "NIFTI_UNITS_SEC") { mult = 1.0f; } else if (unitString == "NIFTI_UNITS_MSEC") { mult = 0.001f; } else if (unitString == "NIFTI_UNITS_USEC") { mult = 0.000001f; } else { throw CaretException("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()); } } if (!attrs.hasAttribute("TimeStep")) { throw CaretException("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()); } if (xml.readNextStartElement()) { throw CaretException("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; m_step = newStep; m_unit = SECOND; CaretAssert(xml.isEndElement() && xml.name() == "MatrixIndicesMap"); } void CiftiSeriesMap::readXML2(QXmlStreamReader& xml) { QXmlStreamAttributes attrs = xml.attributes(); float newStart = 0.0f, newStep = -1.0f, mult = 0.0f; int64_t newLength = -1; Unit newUnit; bool ok = false; if (!attrs.hasAttribute("SeriesUnit")) { throw CaretException("series mapping is missing required attribute SeriesUnit"); } QStringRef unitString = attrs.value("SeriesUnit"); if (unitString == "HERTZ") { newUnit = HERTZ; } else if (unitString == "METER") { newUnit = METER; } else if (unitString == "RADIAN") { newUnit = RADIAN; } else if (unitString == "SECOND") { newUnit = SECOND; } else { throw CaretException("unrecognized value for SeriesUnit: " + unitString.toString()); } if (!attrs.hasAttribute("SeriesExponent")) { throw CaretException("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()); } mult = pow(10.0f, exponent); if (!attrs.hasAttribute("SeriesStart")) { throw CaretException("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()); } if (!attrs.hasAttribute("SeriesStep")) { throw CaretException("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()); } if (!attrs.hasAttribute("NumberOfSeriesPoints")) { throw CaretException("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()); } if (newLength < 1) { throw CaretException("NumberOfSeriesPoints must be positive"); } if (xml.readNextStartElement()) { throw CaretException("unexpected element in series map: " + xml.name().toString()); } m_length = newLength; m_start = newStart; m_step = newStep; m_unit = newUnit; CaretAssert(xml.isEndElement() && xml.name() == "MatrixIndicesMap"); } void CiftiSeriesMap::writeXML1(QXmlStreamWriter& xml) const { CaretAssert(m_length != -1); if (m_unit != SECOND) { CaretLogWarning("changing series units to seconds for CIFTI-1 XML"); } xml.writeAttribute("IndicesMapToDataType", "CIFTI_INDEX_TYPE_TIME_POINTS"); float mult = 1.0f; QString unitString = "NIFTI_UNITS_SEC"; float test = m_step; if (test == 0.0f) test = m_start; if (test != 0.0f) { if (abs(test) < 0.00005f) { mult = 1000000.0f; unitString = "NIFTI_UNITS_USEC"; } else if (abs(test) < 0.05f) { mult = 1000.0f; unitString = "NIFTI_UNITS_MSEC"; } } xml.writeAttribute("TimeStepUnits", unitString); xml.writeAttribute("TimeStart", QString::number(mult * m_start, 'f', 7));//even though it is nonstandard, write it, always xml.writeAttribute("TimeStep", QString::number(mult * m_step, 'f', 7)); } void CiftiSeriesMap::writeXML2(QXmlStreamWriter& xml) const { CaretAssert(m_length != -1); xml.writeAttribute("IndicesMapToDataType", "CIFTI_INDEX_TYPE_SERIES"); int exponent = 0; float test = m_step; if (test == 0.0f) test = m_start; if (test != 0.0f) { exponent = 3 * (int)floor((log10(test) - log10(0.05f)) / 3.0f);//some magic to get the exponent that is a multiple of 3 that puts the test value in [0.05, 50] } float mult = pow(10.0f, -exponent); QString unitString; switch (m_unit) { case HERTZ: unitString = "HERTZ"; break; case METER: unitString = "METER"; break; case RADIAN: unitString = "RADIAN"; break; case SECOND: unitString = "SECOND"; break; } xml.writeAttribute("NumberOfSeriesPoints", QString::number(m_length)); xml.writeAttribute("SeriesExponent", QString::number(exponent)); xml.writeAttribute("SeriesStart", QString::number(mult * m_start, 'f', 7)); xml.writeAttribute("SeriesStep", QString::number(mult * m_step, 'f', 7)); xml.writeAttribute("SeriesUnit", unitString); } connectome-workbench-1.2.3+git41-gc4c6c90/src/Cifti/CiftiSeriesMap.h000066400000000000000000000100341300200146000246200ustar00rootroot00000000000000#ifndef __CIFTI_SERIES_MAP_H__ #define __CIFTI_SERIES_MAP_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 "CiftiMappingType.h" namespace caret { class CiftiSeriesMap : public CiftiMappingType { public: enum Unit { HERTZ, METER, RADIAN, SECOND };//should this go somewhere else? float getStart() const { return m_start; }//using getter/setter as style choice to match other mapping types float getStep() const { return m_step; }//getter for number of series points is getLength(), specified by CiftiIndexMap Unit getUnit() const { return m_unit; } CiftiSeriesMap() { m_start = 0.0f; m_step = 1.0f; m_unit = SECOND; m_length = -1;//to make it clear an improperly initialized series map object was used } CiftiSeriesMap(const int64_t& length, const float& start = 0.0f, const float& step = 1.0f, const Unit& unit = SECOND) { m_start = start; m_step = step; m_unit = unit; m_length = length; } void setStart(const float& start) { m_start = start; } void setStep(const float& step) { m_step = step; } void setUnit(const Unit& unit) { m_unit = unit; } void setLength(const int64_t& length); static Unit stringToUnit(const QString& string, bool& ok); static QString unitToString(const Unit& theUnit); static std::vector getAllUnits(); CiftiMappingType* clone() const { return new CiftiSeriesMap(*this); } MappingType getType() const { return SERIES; } int64_t getLength() const { return m_length; } bool operator==(const CiftiMappingType& rhs) const { if (rhs.getType() != getType()) return false; const CiftiSeriesMap& temp = dynamic_cast(rhs); return (temp.m_length == m_length && temp.m_unit == m_unit && temp.m_start == m_start && temp.m_step == m_step); } bool approximateMatch(const CiftiMappingType& rhs, QString* explanation = NULL) const { switch (rhs.getType()) { case SCALARS://maybe? case SERIES: case LABELS://maybe? if (getLength() != rhs.getLength()) { if (explanation != NULL) *explanation = "mappings have different length"; return false; } else return true; default: if (explanation != NULL) *explanation = CiftiMappingType::mappingTypeToName(rhs.getType()) + " mapping never matches " + CiftiMappingType::mappingTypeToName(getType()); return false; } } void readXML1(QXmlStreamReader& xml); void readXML2(QXmlStreamReader& xml); void writeXML1(QXmlStreamWriter& xml) const; void writeXML2(QXmlStreamWriter& xml) const; private: int64_t m_length; float m_start, m_step;//exponent gets applied to these on reading Unit m_unit; }; } #endif //__CIFTI_SERIES_MAP_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Cifti/CiftiVersion.cxx000066400000000000000000000062641300200146000247420ustar00rootroot00000000000000/*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 "CiftiVersion.h" #include "CaretException.h" using namespace caret; CiftiVersion::CiftiVersion() { m_major = 2; m_minor = 0; } CiftiVersion::CiftiVersion(const int16_t& major, const int16_t& minor) { m_major = major; m_minor = minor; } CiftiVersion::CiftiVersion(const QString& versionString) { int result = versionString.indexOf('.'); bool ok = false; if (result < 0) { m_minor = 0; m_major = versionString.toShort(&ok); if (!ok) throw CaretException("improperly formatted CIFTI version string: '" + versionString + "'"); } else { if (result == 0) throw CaretException("improperly formatted CIFTI version string: '" + versionString + "'"); m_major = versionString.mid(0, result).toShort(&ok); if (!ok) throw CaretException("improperly formatted CIFTI version string: '" + versionString + "'"); m_minor = versionString.mid(result + 1).toShort(&ok); if (!ok) throw CaretException("improperly formatted CIFTI version string: '" + versionString + "'"); } } bool CiftiVersion::hasReversedFirstDims() const { if (m_major == 1 && m_minor == 0) return true; return false; } bool CiftiVersion::operator<(const CiftiVersion& rhs) const { if (m_major < rhs.m_major) return true; if (m_major == rhs.m_major && m_minor < rhs.m_minor) return true; return false; } bool CiftiVersion::operator<=(const CiftiVersion& rhs) const { if (m_major < rhs.m_major) return true; if (m_major == rhs.m_major && m_minor <= rhs.m_minor) return true; return false; } bool CiftiVersion::operator==(const CiftiVersion& rhs) const { if (m_major == rhs.m_major && m_minor == rhs.m_minor) return true; return false; } bool CiftiVersion::operator!=(const CiftiVersion& rhs) const { return !(*this == rhs); } bool CiftiVersion::operator>(const caret::CiftiVersion& rhs) const { if (m_major > rhs.m_major) return true; if (m_major == rhs.m_major && m_minor > rhs.m_minor) return true; return false; } bool CiftiVersion::operator>=(const caret::CiftiVersion& rhs) const { if (m_major > rhs.m_major) return true; if (m_major == rhs.m_major && m_minor >= rhs.m_minor) return true; return false; } QString CiftiVersion::toString() const { QString ret = QString::number(m_major); if (m_minor != 0) ret += "." + QString::number(m_minor); return ret; } connectome-workbench-1.2.3+git41-gc4c6c90/src/Cifti/CiftiVersion.h000066400000000000000000000033371300200146000243650ustar00rootroot00000000000000#ifndef __CIFTI_VERSION_H__ #define __CIFTI_VERSION_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 "stdint.h" namespace caret { class CiftiVersion { int16_t m_major, m_minor; public: int16_t getMajor() const { return m_major; } int16_t getMinor() const { return m_minor; } CiftiVersion(); CiftiVersion(const int16_t& major, const int16_t& minor); CiftiVersion(const QString& versionString); QString toString() const; bool operator<(const CiftiVersion& rhs) const; bool operator>(const CiftiVersion& rhs) const; bool operator==(const CiftiVersion& rhs) const; bool operator!=(const CiftiVersion& rhs) const; bool operator<=(const CiftiVersion& rhs) const; bool operator>=(const CiftiVersion& rhs) const; ///quirk tests bool hasReversedFirstDims() const; }; } #endif //__CIFTI_VERSION_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Cifti/CiftiXML.cxx000066400000000000000000001045301300200146000237500ustar00rootroot00000000000000/*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 "CiftiXML.h" #include "CaretAssert.h" #include "CaretException.h" #include "CaretLogger.h" #include "GiftiMetaData.h" #include "PaletteColorMapping.h" #include #include using namespace std; using namespace caret; int CiftiXML::directionFromString(const QString& input) { bool ok = false; int converted = input.toInt(&ok); if (ok) { if (converted < 1) throw CaretException("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'"); } QString CiftiXML::directionFromStringExplanation() { return "The direction can be either an integer starting from 1, or the strings 'ROW' or 'COLUMN'."; } CiftiXML::CiftiXML(const CiftiXML& rhs) { copyHelper(rhs); } CiftiXML& CiftiXML::operator=(const CiftiXML& rhs) { if (this != &rhs) copyHelper(rhs); return *this; } void CiftiXML::copyHelper(const CiftiXML& rhs) { int numDims = (int)rhs.m_indexMaps.size(); m_indexMaps.resize(numDims); for (int i = 0; i < numDims; ++i) { m_indexMaps[i] = CaretPointer(rhs.m_indexMaps[i]->clone()); } m_parsedVersion = rhs.m_parsedVersion; m_fileMetaData = rhs.m_fileMetaData; if (rhs.m_filePalette != NULL) { m_filePalette.grabNew(new PaletteColorMapping()); *(m_filePalette) = *(rhs.m_filePalette); } else { m_filePalette.grabNew(NULL); } } bool CiftiXML::operator==(const CiftiXML& rhs) const { int numDims = getNumberOfDimensions(); if (rhs.getNumberOfDimensions() != numDims) return false; if (m_fileMetaData != rhs.m_fileMetaData) return false; if ((*getFilePalette()) != (*rhs.getFilePalette())) return false; for (int i = 0; i < numDims; ++i) { const CiftiMappingType* left = getMap(i), *right = rhs.getMap(i); if (left == NULL && right == NULL) continue; if (left == NULL || right == NULL) return false;//only one NULL, due to above test if ((*left) != (*right)) return false;//finally can dereference them } return true; } bool CiftiXML::approximateMatch(const CiftiXML& rhs) const { int numDims = getNumberOfDimensions(); if (rhs.getNumberOfDimensions() != numDims) return false; for (int i = 0; i < numDims; ++i) { const CiftiMappingType* left = getMap(i), *right = rhs.getMap(i); if (left == NULL && right == NULL) continue; if (left == NULL || right == NULL) return false;//only one NULL, due to above test if (!left->approximateMatch(*right)) return false;//finally can dereference them } return true; } const CiftiMappingType* CiftiXML::getMap(const int& direction) const { CaretAssertVectorIndex(m_indexMaps, direction); return m_indexMaps[direction]; } CiftiMappingType* CiftiXML::getMap(const int& direction) { CaretAssertVectorIndex(m_indexMaps, direction); return m_indexMaps[direction]; } GiftiMetaData* CiftiXML::getFileMetaData() const { return &m_fileMetaData; } PaletteColorMapping* CiftiXML::getFilePalette() const { if (m_filePalette != NULL) { return m_filePalette; } m_filePalette.grabNew(new PaletteColorMapping()); if (m_fileMetaData.exists("PaletteColorMapping")) { try { m_filePalette->decodeFromStringXML(m_fileMetaData.get("PaletteColorMapping")); } catch (XmlException& e) { CaretLogWarning("failed to parse palette settings from metadata: " + e.whatString()); } } return m_filePalette; } const CiftiBrainModelsMap& CiftiXML::getBrainModelsMap(const int& direction) const { CaretAssertVectorIndex(m_indexMaps, direction); CaretAssert(getMappingType(direction) == CiftiMappingType::BRAIN_MODELS);//assert so we catch it in debug return dynamic_cast(*getMap(direction));//let release throw bad_cast or segfault } CiftiBrainModelsMap& CiftiXML::getBrainModelsMap(const int& direction) { CaretAssertVectorIndex(m_indexMaps, direction); CaretAssert(getMappingType(direction) == CiftiMappingType::BRAIN_MODELS);//assert so we catch it in debug return dynamic_cast(*getMap(direction));//let release throw bad_cast or segfault } const CiftiLabelsMap& CiftiXML::getLabelsMap(const int& direction) const { CaretAssertVectorIndex(m_indexMaps, direction); CaretAssert(getMappingType(direction) == CiftiMappingType::LABELS);//assert so we catch it in debug return dynamic_cast(*getMap(direction));//let release throw bad_cast or segfault } CiftiLabelsMap& CiftiXML::getLabelsMap(const int& direction) { CaretAssertVectorIndex(m_indexMaps, direction); CaretAssert(getMappingType(direction) == CiftiMappingType::LABELS);//assert so we catch it in debug return dynamic_cast(*getMap(direction));//let release throw bad_cast or segfault } const CiftiParcelsMap& CiftiXML::getParcelsMap(const int& direction) const { CaretAssertVectorIndex(m_indexMaps, direction); CaretAssert(getMappingType(direction) == CiftiMappingType::PARCELS);//assert so we catch it in debug return dynamic_cast(*getMap(direction));//let release throw bad_cast or segfault } CiftiParcelsMap& CiftiXML::getParcelsMap(const int& direction) { CaretAssertVectorIndex(m_indexMaps, direction); CaretAssert(getMappingType(direction) == CiftiMappingType::PARCELS);//assert so we catch it in debug return dynamic_cast(*getMap(direction));//let release throw bad_cast or segfault } const CiftiScalarsMap& CiftiXML::getScalarsMap(const int& direction) const { CaretAssertVectorIndex(m_indexMaps, direction); CaretAssert(getMappingType(direction) == CiftiMappingType::SCALARS);//assert so we catch it in debug return dynamic_cast(*getMap(direction));//let release throw bad_cast or segfault } CiftiScalarsMap& CiftiXML::getScalarsMap(const int& direction) { CaretAssertVectorIndex(m_indexMaps, direction); CaretAssert(getMappingType(direction) == CiftiMappingType::SCALARS);//assert so we catch it in debug return dynamic_cast(*getMap(direction));//let release throw bad_cast or segfault } const CiftiSeriesMap& CiftiXML::getSeriesMap(const int& direction) const { CaretAssertVectorIndex(m_indexMaps, direction); CaretAssert(getMappingType(direction) == CiftiMappingType::SERIES);//assert so we catch it in debug return dynamic_cast(*getMap(direction));//let release throw bad_cast or segfault } CiftiSeriesMap& CiftiXML::getSeriesMap(const int& direction) { CaretAssertVectorIndex(m_indexMaps, direction); CaretAssert(getMappingType(direction) == CiftiMappingType::SERIES);//assert so we catch it in debug return dynamic_cast(*getMap(direction));//let release throw bad_cast or segfault } int64_t CiftiXML::getDimensionLength(const int& direction) const { const CiftiMappingType* tempMap = getMap(direction); CaretAssert(tempMap != NULL); return tempMap->getLength(); } vector CiftiXML::getDimensions() const { vector ret(getNumberOfDimensions()); for (int i = 0; i < (int)ret.size(); ++i) { ret[i] = getDimensionLength(i); } return ret; } CiftiMappingType::MappingType CiftiXML::getMappingType(const int& direction) const { CaretAssertVectorIndex(m_indexMaps, direction); CaretAssert(m_indexMaps[direction] != NULL); return m_indexMaps[direction]->getType(); } void CiftiXML::setMap(const int& direction, const CiftiMappingType& mapIn) { CaretAssertVectorIndex(m_indexMaps, direction); if (mapIn.getType() == CiftiMappingType::LABELS) { for (int i = 0; i < getNumberOfDimensions(); ++i) { 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"); } } } m_indexMaps[direction] = CaretPointer(mapIn.clone()); } void CiftiXML::setNumberOfDimensions(const int& num) { m_indexMaps.resize(num); } void CiftiXML::clear() { setNumberOfDimensions(0); m_filePalette.grabNew(NULL); m_fileMetaData.clear(false); m_parsedVersion = CiftiVersion(); } void CiftiXML::readXML(const QString& text) { QXmlStreamReader xml(text); readXML(xml); } void CiftiXML::readXML(const QByteArray& data) { QString text(data);//constructing a qstring appears to be the simplest way to remove trailing nulls, which otherwise trip an "Extra content at end of document" error readXML(text);//then put it through the string reader, just to simplify code paths } int32_t CiftiXML::getIntentInfo(const CiftiVersion& writingVersion, char intentNameOut[16]) const { int32_t ret; const char* name = NULL; if (writingVersion == CiftiVersion(1, 0))//cifti-1: unknown didn't exist, and "ConnDense" was default { ret = 3001;//default name = "ConnDense"; if (getNumberOfDimensions() > 0 && getMappingType(0) == CiftiMappingType::SERIES) { ret = 3002; name = "ConnDenseTime"; }//same logic as was actually used in CiftiFile if (getNumberOfDimensions() > 1 && getMappingType(1) == CiftiMappingType::SERIES) { ret = 3002; name = "ConnDenseTime"; }//NOTE: name for this code is different than cifti-2 } else if (writingVersion == CiftiVersion(1, 1) || writingVersion == CiftiVersion(2, 0)) {//cifti-2 ret = 3000;//default name = "ConnUnknown"; switch (getNumberOfDimensions()) { case 2: { CiftiMappingType::MappingType first = getMappingType(0), second = getMappingType(1); if (first == CiftiMappingType::BRAIN_MODELS && second == CiftiMappingType::BRAIN_MODELS) { ret = 3001; name = "ConnDense"; } if (first == CiftiMappingType::SERIES && second == CiftiMappingType::BRAIN_MODELS) { ret = 3002; name = "ConnDenseSeries"; } if (first == CiftiMappingType::PARCELS && second == CiftiMappingType::PARCELS) { ret = 3003; name = "ConnParcels"; } if (first == CiftiMappingType::SERIES && second == CiftiMappingType::PARCELS) { ret = 3004; name = "ConnParcelSries"; }//NOTE: 3005 is reserved but not used if (first == CiftiMappingType::SCALARS && second == CiftiMappingType::BRAIN_MODELS) { ret = 3006; name = "ConnDenseScalar"; } if (first == CiftiMappingType::LABELS && second == CiftiMappingType::BRAIN_MODELS) { ret = 3007; name = "ConnDenseLabel"; } if (first == CiftiMappingType::SCALARS && second == CiftiMappingType::PARCELS) { ret = 3008; name = "ConnParcelScalr"; } if (first == CiftiMappingType::BRAIN_MODELS && second == CiftiMappingType::PARCELS) { ret = 3009; name = "ConnParcelDense"; } if (first == CiftiMappingType::PARCELS && second == CiftiMappingType::BRAIN_MODELS) { ret = 3010; name = "ConnDenseParcel"; } break; } case 3: { CiftiMappingType::MappingType first = getMappingType(0), second = getMappingType(1), third = getMappingType(2); if (first == CiftiMappingType::PARCELS && second == CiftiMappingType::PARCELS && third == CiftiMappingType::SERIES) { ret = 3011; name = "ConnPPSr"; } if (first == CiftiMappingType::PARCELS && second == CiftiMappingType::PARCELS && third == CiftiMappingType::SCALARS) { ret = 3012; name = "ConnPPSc"; } break; } default: break; } } else { throw CaretException("unknown cifti version: " + writingVersion.toString()); } int i; for (i = 0; i < 16 && name[i] != '\0'; ++i) intentNameOut[i] = name[i]; for (; i < 16; ++i) intentNameOut[i] = '\0'; return ret; } void CiftiXML::readXML(QXmlStreamReader& xml) { clear(); try { bool haveCifti = false; for (; !xml.atEnd(); xml.readNext()) { if (xml.isStartElement()) { QStringRef name = xml.name(); if (name == "CIFTI") { if (haveCifti) { throw CaretException("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."); } if (m_parsedVersion == CiftiVersion(1, 0))//switch/case on major/minor would be much harder to read { parseCIFTI1(xml); if (xml.hasError()) break; } else if (m_parsedVersion == CiftiVersion(1, 1)) { CaretLogWarning("parsing cifti version '1.1', this should not exist in the wild"); parseCIFTI2(xml);//we used "1.1" to test our cifti-2 implementation if (xml.hasError()) break; } else if (m_parsedVersion == CiftiVersion(2, 0)) { parseCIFTI2(xml); if (xml.hasError()) break; } else { throw CaretException("unknown Cifti Version: '" + m_parsedVersion.toString()); } haveCifti = true; } else { throw CaretException("unexpected root element in Cifti XML: " + name.toString()); } } } if (!xml.hasError() && !haveCifti) { throw CaretException("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 } if(xml.hasError()) { throw CaretException("Cifti XML error: " + xml.errorString()); } } void CiftiXML::parseCIFTI1(QXmlStreamReader& xml) { QXmlStreamAttributes attributes = xml.attributes(); if (attributes.hasAttribute("NumberOfMatrices")) { if (attributes.value("NumberOfMatrices") != "1") { throw CaretException("attribute NumberOfMatrices in CIFTI is required to be 1 for CIFTI-1"); } } else { throw CaretException("missing attribute NumberOfMatrices in CIFTI"); } bool haveMatrix = false; while (!xml.atEnd()) { xml.readNext(); if (xml.isStartElement()) { QStringRef name = xml.name(); if (name == "Matrix") { if (haveMatrix) { throw CaretException("Matrix element may only be specified once"); } parseMatrix1(xml); if (xml.hasError()) return; haveMatrix = true; } else { throw CaretException("unexpected element in CIFTI: " + name.toString()); } } else if (xml.isEndElement()) { break; } } if (!haveMatrix) { throw CaretException("Matrix element not found in CIFTI"); } if (xml.hasError()) return; CaretAssert(xml.isEndElement() && xml.name() == "CIFTI"); } void CiftiXML::parseCIFTI2(QXmlStreamReader& xml)//yes, these will often have largely similar code, but it seems cleaner than having only some functions split, or constantly rechecking the version {//also, helps keep changes to cifti-2 away from code that parses cifti-1 bool haveMatrix = false; while (!xml.atEnd()) { xml.readNext(); if (xml.hasError()) return; if (xml.isStartElement()) { QStringRef name = xml.name(); if (name == "Matrix") { if (haveMatrix) { throw CaretException("Matrix element may only be specified once"); } parseMatrix2(xml); if (xml.hasError()) return; haveMatrix = true; } else { throw CaretException("unexpected element in CIFTI: " + name.toString()); } } else if (xml.isEndElement()) { break; } } if (!haveMatrix) { throw CaretException("Matrix element not found in CIFTI"); } CaretAssert(xml.isEndElement() && xml.name() == "CIFTI"); } void CiftiXML::parseMatrix1(QXmlStreamReader& xml) { VolumeSpace fileVolSpace; bool haveVolSpace = false, haveMetadata = false; while (!xml.atEnd()) { xml.readNext(); if (xml.hasError()) return; if (xml.isStartElement()) { QStringRef name = xml.name(); if (name == "MetaData") { if (haveMetadata) { throw CaretException("MetaData may only be specified once in Matrix"); } m_fileMetaData.readCiftiXML1(xml); if (xml.hasError()) return; haveMetadata = true; } else if (name == "MatrixIndicesMap") { parseMatrixIndicesMap1(xml); if (xml.hasError()) return; } else if (name == "Volume") { if (haveVolSpace) { throw CaretException("Volume may only be specified once in Matrix"); } fileVolSpace.readCiftiXML1(xml); if (xml.hasError()) return; haveVolSpace = true; } else if (name == "LabelTable") { CaretLogFiner("skipping unused LabelTable element in Matrix in CIFTI-1"); xml.readElementText(QXmlStreamReader::SkipChildElements); } else { throw CaretException("unexpected element in Matrix: " + name.toString()); } } else if (xml.isEndElement()) { break; } } for (int i = 0; i < (int)m_indexMaps.size(); ++i) { if (m_indexMaps[i] == NULL) { 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) + "'"); } switch (m_indexMaps[i]->getType()) { case CiftiMappingType::BRAIN_MODELS: { CiftiBrainModelsMap& myMap = dynamic_cast(*(m_indexMaps[i])); if (myMap.hasVolumeData()) { if (haveVolSpace) { myMap.setVolumeSpace(fileVolSpace);//also does the needed checking of voxel indices } else { throw CaretException("BrainModels map uses voxels, but no Volume element exists"); } } break; } case CiftiMappingType::PARCELS: { CiftiParcelsMap& myMap = dynamic_cast(*(m_indexMaps[i])); if (myMap.hasVolumeData()) { if (haveVolSpace) { myMap.setVolumeSpace(fileVolSpace);//ditto } else { throw CaretException("Parcels map uses voxels, but no Volume element exists"); } } break; } default: break; } } CaretAssert(xml.isEndElement() && xml.name() == "Matrix"); } void CiftiXML::parseMatrix2(QXmlStreamReader& xml) { bool haveMetadata = false; while (!xml.atEnd()) { xml.readNext(); if (xml.hasError()) return; if (xml.isStartElement()) { QStringRef name = xml.name(); if (name == "MetaData") { if (haveMetadata) { throw CaretException("MetaData may only be specified once in Matrix"); } m_fileMetaData.readCiftiXML2(xml); if (xml.hasError()) return; haveMetadata = true; } else if (name == "MatrixIndicesMap") { parseMatrixIndicesMap2(xml); if (xml.hasError()) return; } else { throw CaretException("unexpected element in Matrix: " + name.toString()); } } else if (xml.isEndElement()) { break; } } for (int i = 0; i < (int)m_indexMaps.size(); ++i) { if (m_indexMaps[i] == NULL) { throw CaretException("missing mapping for dimension '" + QString::number(i) + "'"); } } CaretAssert(xml.isEndElement() && xml.name() == "Matrix"); } void CiftiXML::parseMatrixIndicesMap1(QXmlStreamReader& xml) { QXmlStreamAttributes attributes = xml.attributes(); if (!attributes.hasAttribute("AppliesToMatrixDimension")) { throw CaretException("missing attribute AppliesToMatrixDimension in MatrixIndicesMap"); } if (!attributes.hasAttribute("IndicesMapToDataType")) { throw CaretException("missing attribute IndicesMapToDataType in MatrixIndicesMap"); } QStringList values = attributes.value("AppliesToMatrixDimension").toString().split(','); bool ok = false; set used; for(int i = 0; i < values.size(); i++) { int parsed = values[i].toInt(&ok); if (!ok || parsed < 0) { throw CaretException("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]); } used.insert(parsed); } CaretPointer toRead; QStringRef type = attributes.value("IndicesMapToDataType"); if (type == "CIFTI_INDEX_TYPE_BRAIN_MODELS") { toRead = CaretPointer(new CiftiBrainModelsMap()); } else if (type == "CIFTI_INDEX_TYPE_TIME_POINTS") { toRead = CaretPointer(new CiftiSeriesMap()); } else if (type == "CIFTI_INDEX_TYPE_LABELS") {//this and below are nonstandard toRead = CaretPointer(new CiftiLabelsMap()); } else if (type == "CIFTI_INDEX_TYPE_PARCELS") { toRead = CaretPointer(new CiftiParcelsMap()); } else if (type == "CIFTI_INDEX_TYPE_SCALARS") { toRead = CaretPointer(new CiftiScalarsMap()); } else { throw CaretException("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; bool first = true; for (set::iterator iter = used.begin(); iter != used.end(); ++iter) { if (*iter >= (int)m_indexMaps.size()) m_indexMaps.resize(*iter + 1); if (first) { m_indexMaps[*iter] = toRead; first = false; } else { m_indexMaps[*iter] = CaretPointer(toRead->clone());//make in-memory information independent per-dimension, rather than dealing with deduplication everywhere } } CaretAssert(xml.isEndElement() && xml.name() == "MatrixIndicesMap"); } void CiftiXML::parseMatrixIndicesMap2(QXmlStreamReader& xml) { QXmlStreamAttributes attributes = xml.attributes(); if (!attributes.hasAttribute("AppliesToMatrixDimension")) { throw CaretException("missing attribute AppliesToMatrixDimension in MatrixIndicesMap"); } if (!attributes.hasAttribute("IndicesMapToDataType")) { throw CaretException("missing attribute IndicesMapToDataType in MatrixIndicesMap"); } QStringList values = attributes.value("AppliesToMatrixDimension").toString().split(','); bool ok = false; set used; for(int i = 0; i < values.size(); i++) { int parsed = values[i].toInt(&ok); if (!ok || parsed < 0) { throw CaretException("bad value in AppliesToMatrixDimension list: " + values[i]); } if (used.find(parsed) != used.end()) { throw CaretException("AppliesToMatrixDimension contains repeated value: " + values[i]); } used.insert(parsed); } CaretPointer toRead; QStringRef type = attributes.value("IndicesMapToDataType"); if (type == "CIFTI_INDEX_TYPE_BRAIN_MODELS") { toRead = CaretPointer(new CiftiBrainModelsMap()); } else if (type == "CIFTI_INDEX_TYPE_LABELS") { toRead = CaretPointer(new CiftiLabelsMap()); } else if (type == "CIFTI_INDEX_TYPE_PARCELS") { toRead = CaretPointer(new CiftiParcelsMap()); } else if (type == "CIFTI_INDEX_TYPE_SCALARS") { toRead = CaretPointer(new CiftiScalarsMap()); } else if (type == "CIFTI_INDEX_TYPE_SERIES") { toRead = CaretPointer(new CiftiSeriesMap()); } else { throw CaretException("invalid value for IndicesMapToDataType in CIFTI-1: " + type.toString()); } toRead->readXML2(xml); if (xml.hasError()) return; bool first = true; for (set::iterator iter = used.begin(); iter != used.end(); ++iter) { if (*iter >= (int)m_indexMaps.size()) m_indexMaps.resize(*iter + 1); if (first) { m_indexMaps[*iter] = toRead; first = false; } else { m_indexMaps[*iter] = CaretPointer(toRead->clone());//make in-memory information independent per-dimension, rather than dealing with deduplication everywhere } } CaretAssert(xml.isEndElement() && xml.name() == "MatrixIndicesMap"); } QByteArray CiftiXML::writeXMLToQByteArray(const CiftiVersion& writingVersion) const { QByteArray ret; QXmlStreamWriter xml(&ret); xml.setAutoFormatting(true); xml.writeStartDocument(); writeXML(xml, writingVersion); xml.writeEndDocument(); return ret; } QString CiftiXML::writeXMLToString(const CiftiVersion& writingVersion) const { QString ret; QXmlStreamWriter xml(&ret); xml.setAutoFormatting(true); xml.writeStartDocument(); writeXML(xml, writingVersion); xml.writeEndDocument(); return ret; } void CiftiXML::writeXML(QXmlStreamWriter& xml, const CiftiVersion& writingVersion) const { xml.writeStartElement("CIFTI"); xml.writeAttribute("Version", writingVersion.toString()); if (writingVersion == CiftiVersion(1, 0))//switch/case on major/minor would be much harder to read { xml.writeAttribute("NumberOfMatrices", "1"); writeMatrix1(xml); } else if (writingVersion == CiftiVersion(1, 1)) {//we used "1.1" to test our cifti-2 implementation - should we even allow this? CaretLogWarning("writing cifti version '1.1', this should not exist in the wild"); writeMatrix2(xml); } else if (writingVersion == CiftiVersion(2, 0)) { writeMatrix2(xml); } else { throw CaretException("unknown Cifti writing version: '" + writingVersion.toString() + "'"); } xml.writeEndElement(); } void CiftiXML::writeMatrix1(QXmlStreamWriter& xml) const { int numDims = (int)m_indexMaps.size(); bool haveVolData = false; 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"); switch (m_indexMaps[i]->getType()) { case CiftiMappingType::BRAIN_MODELS: { const CiftiBrainModelsMap& myMap = dynamic_cast(*(m_indexMaps[i])); if (myMap.hasVolumeData()) { if (haveVolData) { if (myMap.getVolumeSpace() != volSpace) { throw CaretException("cannot write different volume spaces for different dimensions in CIFTI-1"); } } else { haveVolData = true; volSpace = myMap.getVolumeSpace(); } } break; } case CiftiMappingType::PARCELS: { const CiftiParcelsMap& myMap = dynamic_cast(*(m_indexMaps[i])); if (myMap.hasVolumeData()) { if (haveVolData) { if (myMap.getVolumeSpace() != volSpace) { throw CaretException("cannot write different volume spaces for different dimensions in CIFTI-1"); } } else { haveVolData = true; volSpace = myMap.getVolumeSpace(); } } break; } default: break; } } xml.writeStartElement("Matrix"); if (m_filePalette != NULL) { m_fileMetaData.set("PaletteColorMapping", m_filePalette->encodeInXML()); } m_fileMetaData.writeCiftiXML1(xml); if (haveVolData) { volSpace.writeCiftiXML1(xml); } vector used(numDims, false); for (int i = 0; i < numDims; ++i) { if (!used[i]) { used[i] = true; int outputNum = i; if (outputNum < 2) outputNum = 1 - outputNum;//ie, swap 0 and 1 QString appliesTo = QString::number(outputNum);//initialize containing just the current dimension for (int j = i + 1; j < numDims; ++j)//compare to all later unused dimensions for deduplication {//technically, shouldn't need to check for previously used as long as equality is exact, but means maybe fewer comparisons, and to prevent a bug in == from getting stranger behavior if (!used[j]) { if ((*m_indexMaps[i]) == (*m_indexMaps[j])) { outputNum = j; if (outputNum < 2) outputNum = 1 - outputNum; appliesTo += "," + QString::number(outputNum); used[j] = true; } } } xml.writeStartElement("MatrixIndicesMap");//should the CiftiIndexMap do this instead, and we pass appliesTo to it as string? probably not important, we won't use them in any other xml xml.writeAttribute("AppliesToMatrixDimension", appliesTo); m_indexMaps[i]->writeXML1(xml); xml.writeEndElement(); } } xml.writeEndElement(); } 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"); } xml.writeStartElement("Matrix"); if (m_filePalette != NULL) { m_fileMetaData.set("PaletteColorMapping", m_filePalette->encodeInXML()); } m_fileMetaData.writeCiftiXML2(xml); vector used(numDims, false); for (int i = 0; i < numDims; ++i) { if (!used[i]) { used[i] = true; QString appliesTo = QString::number(i);//initialize containing just the current dimension for (int j = i + 1; j < numDims; ++j)//compare to all later unused dimensions for deduplication {//technically, shouldn't need to check for previously used as long as equality is exact, but means maybe fewer comparisons, and to prevent a bug in == from getting stranger behavior if (!used[j]) { if ((*m_indexMaps[i]) == (*m_indexMaps[j])) { appliesTo += "," + QString::number(j); used[j] = true; } } } xml.writeStartElement("MatrixIndicesMap");//should the CiftiIndexMap do this instead, and we pass appliesTo to it as string? probably not important, we won't use them in any other xml xml.writeAttribute("AppliesToMatrixDimension", appliesTo); m_indexMaps[i]->writeXML2(xml); xml.writeEndElement(); } } xml.writeEndElement(); } bool CiftiXML::mutablesModified() const { if (m_fileMetaData.isModified()) return true; if (m_filePalette != NULL && m_filePalette->isModified()) return true; for (int d = 0; d < (int)m_indexMaps.size(); ++d) { if (m_indexMaps[d] != NULL && m_indexMaps[d]->mutablesModified()) return true; } return false; } void CiftiXML::clearMutablesModified() const { m_fileMetaData.clearModified(); if (m_filePalette != NULL) m_filePalette->clearModified(); for (int d = 0; d < (int)m_indexMaps.size(); ++d) { if (m_indexMaps[d] != NULL) m_indexMaps[d]->clearMutablesModified(); } } connectome-workbench-1.2.3+git41-gc4c6c90/src/Cifti/CiftiXML.h000066400000000000000000000123541300200146000233770ustar00rootroot00000000000000#ifndef __CIFTI_XML_H__ #define __CIFTI_XML_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 "CaretPointer.h" #include "CiftiMappingType.h" #include "CiftiVersion.h" #include "GiftiMetaData.h" //could also be forward declared #include "PaletteColorMapping.h" //just include the mapping types themselves, for convenience #include "CiftiBrainModelsMap.h" #include "CiftiLabelsMap.h" #include "CiftiParcelsMap.h" #include "CiftiScalarsMap.h" #include "CiftiSeriesMap.h" #include #include #include namespace caret { class CiftiXML { public: enum { ALONG_ROW = 0, ALONG_COLUMN = 1, ALONG_STACK = 2//better name for this? }; int getNumberOfDimensions() const { return m_indexMaps.size(); } const CiftiVersion& getParsedVersion() const { return m_parsedVersion; } const CiftiMappingType* getMap(const int& direction) const;//can return null in unfilled XML object CiftiMappingType* getMap(const int& direction);//can return null in unfilled XML object GiftiMetaData* getFileMetaData() const;//HACK: allow modification of palette and metadata within XML without setting the xml on a file again PaletteColorMapping* getFilePalette() const; CiftiMappingType::MappingType getMappingType(const int& direction) const;//convenience functions const CiftiBrainModelsMap& getBrainModelsMap(const int& direction) const; CiftiBrainModelsMap& getBrainModelsMap(const int& direction); const CiftiLabelsMap& getLabelsMap(const int& direction) const; CiftiLabelsMap& getLabelsMap(const int& direction); const CiftiParcelsMap& getParcelsMap(const int& direction) const; CiftiParcelsMap& getParcelsMap(const int& direction); const CiftiScalarsMap& getScalarsMap(const int& direction) const; CiftiScalarsMap& getScalarsMap(const int& direction); const CiftiSeriesMap& getSeriesMap(const int& direction) const; CiftiSeriesMap& getSeriesMap(const int& direction); int64_t getDimensionLength(const int& direction) const; std::vector getDimensions() const; void setNumberOfDimensions(const int& num); void setMap(const int& direction, const CiftiMappingType& mapIn); void clear(); void readXML(QXmlStreamReader& xml); void readXML(const QString& text); void readXML(const QByteArray& data); QString writeXMLToString(const CiftiVersion& writingVersion = CiftiVersion()) const; QByteArray writeXMLToQByteArray(const CiftiVersion& writingVersion = CiftiVersion()) const; void writeXML(QXmlStreamWriter& xml, const CiftiVersion& writingVersion = CiftiVersion()) const; ///uses the mapping types to figure out what the intent info should be int32_t getIntentInfo(const CiftiVersion& writingVersion, char intentNameOut[16]) const; CiftiXML() { } CiftiXML(const CiftiXML& rhs); CiftiXML& operator=(const CiftiXML& rhs); bool operator==(const CiftiXML& rhs) const; bool operator!=(const CiftiXML& rhs) const { return !((*this) == rhs); } bool approximateMatch(const CiftiXML& rhs) const; bool mutablesModified() const; void clearMutablesModified() const;//HACK: clear modified status on a const object static int directionFromString(const QString& input);//convenience conversion function, throws on error static QString directionFromStringExplanation();//and explanation text private: std::vector > m_indexMaps; CiftiVersion m_parsedVersion; mutable GiftiMetaData m_fileMetaData;//hack to allow metadata to be modified without allowing dimension-changing operations mutable CaretPointer m_filePalette; void copyHelper(const CiftiXML& rhs); //parsing functions void parseCIFTI1(QXmlStreamReader& xml); void parseMatrix1(QXmlStreamReader& xml); void parseCIFTI2(QXmlStreamReader& xml); void parseMatrix2(QXmlStreamReader& xml); void parseMatrixIndicesMap1(QXmlStreamReader& xml); void parseMatrixIndicesMap2(QXmlStreamReader& xml); //writing functions void writeMatrix1(QXmlStreamWriter& xml) const; void writeMatrix2(QXmlStreamWriter& xml) const; }; } #endif //__CIFTI_XML_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Cifti/CiftiXMLElements.cxx000066400000000000000000000472051300200146000254520ustar00rootroot00000000000000/*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 "CiftiXMLElements.h" #include "DataFileException.h" #include "GiftiLabelTable.h" #include "PaletteColorMapping.h" #include "VoxelIJK.h" #include #include using namespace caret; using namespace std; void CiftiBrainModelElement::setupLookup(CiftiMatrixIndicesMapElement& myMap) { if (m_modelType == CIFTI_MODEL_TYPE_SURFACE) { if (m_nodeIndices.size() == 0 && m_indexCount != 0) { if (m_indexCount != m_surfaceNumberOfNodes) { throw DataFileException("empty index list found with nonzero indexCount, but indexCount and surfaceNumberOfNodes don't match"); } m_nodeToIndexLookup.resize(m_surfaceNumberOfNodes); for (int i = 0; i < (int)m_surfaceNumberOfNodes; ++i) { m_nodeToIndexLookup[i] = i + m_indexOffset; } } else { if (m_indexCount != (int64_t)m_nodeIndices.size()) { throw DataFileException("indexCount and size of nodeIndices don't match"); } m_nodeToIndexLookup.resize(m_surfaceNumberOfNodes); for (int i = 0; i < (int)m_surfaceNumberOfNodes; ++i) { m_nodeToIndexLookup[i] = -1; } for (int i = 0; i < (int)m_indexCount; ++i) { m_nodeToIndexLookup[m_nodeIndices[i]] = i + m_indexOffset; } } } if (m_modelType == CIFTI_MODEL_TYPE_VOXELS) {//we need access to the Volume element to support the "empty list" behavior, but since it is never used, and will be removed from the spec, ignore it int64_t voxListSize = (int64_t)m_voxelIndicesIJK.size(); for (int64_t i = 0; i < voxListSize; i += 3) { myMap.m_voxelToIndexLookup.at(m_voxelIndicesIJK[i], m_voxelIndicesIJK[i + 1], m_voxelIndicesIJK[i + 2]) = m_indexOffset + (i / 3); } } } void CiftiMatrixIndicesMapElement::setupLookup() { if (m_indicesMapToDataType == CIFTI_INDEX_TYPE_BRAIN_MODELS) { stable_sort(m_brainModels.begin(), m_brainModels.end());//in order to make the overlapping range check easy, and so that iterating through maps goes linearly through the file int numModels = (int)m_brainModels.size(); set usedSurf, usedVol; for (int i = 0; i < numModels; ++i) { if (i > 0 && m_brainModels[i - 1].m_indexOffset + m_brainModels[i - 1].m_indexCount > m_brainModels[i].m_indexOffset) { throw DataFileException("overlapping index ranges found in a brain models dimension"); } switch (m_brainModels[i].m_modelType) { case CIFTI_MODEL_TYPE_SURFACE: if (usedSurf.find(m_brainModels[i].m_brainStructure) != usedSurf.end()) { throw DataFileException("structure " + StructureEnum::toName(m_brainModels[i].m_brainStructure) + " used multiple times in surface models"); } usedSurf.insert(m_brainModels[i].m_brainStructure); break; case CIFTI_MODEL_TYPE_VOXELS: if (usedVol.find(m_brainModels[i].m_brainStructure) != usedVol.end()) { throw DataFileException("structure " + StructureEnum::toName(m_brainModels[i].m_brainStructure) + " used multiple times in volume models"); } usedVol.insert(m_brainModels[i].m_brainStructure); break; case CIFTI_MODEL_TYPE_INVALID: CaretAssert(false); throw DataFileException("found 'invalid' brain model type, tell the developers"); break; } m_brainModels[i].setupLookup(*this); } } if (m_indicesMapToDataType != CIFTI_INDEX_TYPE_PARCELS) return; int numSurfs = (int)m_parcelSurfaces.size(); for (int i = 0; i < numSurfs; ++i) { m_parcelSurfaces[i].m_lookup.resize(m_parcelSurfaces[i].m_numNodes); for (int j = 0; j < m_parcelSurfaces[i].m_numNodes; ++j) { m_parcelSurfaces[i].m_lookup[j] = -1; } for (int j = i + 1; j < numSurfs; ++j) { if (m_parcelSurfaces[i].m_structure == m_parcelSurfaces[j].m_structure) { throw DataFileException("multiple surfaces with same structure found in a parcel map"); } } } int numParcels = (int)m_parcels.size(); for (int i = 0; i < numParcels; ++i) { const CiftiParcelElement& myParcel = m_parcels[i]; int numSurfParts = (int)myParcel.m_nodeElements.size(); vector surfUsed(m_parcelSurfaces.size(), false); for (int j = 0; j < numSurfParts; ++j) { const CiftiParcelNodesElement& myNodeElement = myParcel.m_nodeElements[j]; StructureEnum::Enum myStruct = myNodeElement.m_structure; int whichSurf; for (whichSurf = 0; whichSurf < numSurfs; ++whichSurf) { if (m_parcelSurfaces[whichSurf].m_structure == myStruct) break; } if (whichSurf >= numSurfs) { throw DataFileException("parcel '" + myParcel.m_parcelName + "' specifies a structure that doesn't match a specified surface"); } if (surfUsed[whichSurf]) { throw DataFileException("parcel '" + myParcel.m_parcelName + "' specified a surface structure more than once"); } CiftiParcelSurfaceElement& mySurf = m_parcelSurfaces[whichSurf]; surfUsed[whichSurf] = true; int numNodes = myNodeElement.m_nodes.size(); for (int k = 0; k < numNodes; ++k) { int64_t node = myNodeElement.m_nodes[k]; if (node < 0 || node >= mySurf.m_numNodes) { throw DataFileException("node number " + AString::number(node) + " is invalid for surface " + StructureEnum::toName(myStruct)); } if (mySurf.m_lookup[node] != -1) { throw DataFileException("surface node " + AString::number(node) + " in surface " + StructureEnum::toName(myStruct) + " specified more than once"); } mySurf.m_lookup[node] = i; } } int64_t voxListSize = (int64_t)myParcel.m_voxelIndicesIJK.size(); for (int64_t j = 0; j < voxListSize; j += 3) { int64_t* test = m_voxelToIndexLookup.find(myParcel.m_voxelIndicesIJK[j], myParcel.m_voxelIndicesIJK[j + 1], myParcel.m_voxelIndicesIJK[j + 2]); if (test != NULL && *test != i) { throw DataFileException("voxel " + AString::number(myParcel.m_voxelIndicesIJK[j]) + ", " + AString::number(myParcel.m_voxelIndicesIJK[j + 1]) + ", " + AString::number(myParcel.m_voxelIndicesIJK[j + 2]) + " used by more than one parcel"); } m_voxelToIndexLookup.at(myParcel.m_voxelIndicesIJK[j], myParcel.m_voxelIndicesIJK[j + 1], myParcel.m_voxelIndicesIJK[j + 2]) = i; } } } bool CiftiMatrixIndicesMapElement::operator==(const CiftiMatrixIndicesMapElement& rhs) const {//NOTE: don't check the applies to dimension vector, this should check only the mapping, not how it is used if (this == &rhs) return true;//compare pointers to skip checking object against itself if (m_indicesMapToDataType != rhs.m_indicesMapToDataType) return false; switch (m_indicesMapToDataType) { case CIFTI_INDEX_TYPE_INVALID: break;//is there anything to check? case CIFTI_INDEX_TYPE_BRAIN_MODELS: { if (m_brainModels.size() != rhs.m_brainModels.size()) return false; vector used(rhs.m_brainModels.size(), false);//prevent reuse, in case some idiocy winds up having overlapping mappings for (size_t i = 0; i < m_brainModels.size(); ++i)//need to allow the mappings to be placed in a different order, as long as the cifti index ranges line up { bool found = false; for (size_t j = 0; j < rhs.m_brainModels.size(); ++j) { if (!used[j] && m_brainModels[i] == rhs.m_brainModels[j]) { used[j] = true; found = true; break; } } if (!found) return false; } } break; case CIFTI_INDEX_TYPE_FIBERS: break;//??? case CIFTI_INDEX_TYPE_PARCELS: { if (m_parcels.size() != rhs.m_parcels.size() || m_parcelSurfaces.size() != rhs.m_parcelSurfaces.size()) return false; vector used(rhs.m_parcelSurfaces.size(), false); for (size_t i = 0; i < m_parcelSurfaces.size(); ++i) { bool found = false; for (size_t j = 0; j < rhs.m_parcelSurfaces.size(); ++j) { if (!used[j] && m_parcelSurfaces[i] == rhs.m_parcelSurfaces[j]) { used[j] = true; found = true; break; } } if (!found) return false; } for (size_t i = 0; i < m_parcels.size(); ++i) { if (m_parcels[i] != rhs.m_parcels[i]) return false; } } break; case CIFTI_INDEX_TYPE_TIME_POINTS: { if (m_numTimeSteps != rhs.m_numTimeSteps) return false; float timestep, rhtimestep; switch (m_timeStepUnits) { case NIFTI_UNITS_SEC: timestep = m_timeStep; break; case NIFTI_UNITS_MSEC: timestep = m_timeStep * 0.001f; break; case NIFTI_UNITS_USEC: timestep = m_timeStep * 0.000001f; break; default: return false; } switch (rhs.m_timeStepUnits) { case NIFTI_UNITS_SEC: rhtimestep = rhs.m_timeStep; break; case NIFTI_UNITS_MSEC: rhtimestep = rhs.m_timeStep * 0.001f; break; case NIFTI_UNITS_USEC: rhtimestep = rhs.m_timeStep * 0.000001f; break; default: return false; } const float TOLERANCE = 0.999f;//if they don't match exactly, and either one of them is zero, or their ratio is far from 1, say they don't match if (timestep != rhtimestep && (timestep == 0.0f || rhtimestep == 0.0f || timestep / rhtimestep < TOLERANCE || rhtimestep / timestep < TOLERANCE)) return false; } break; case CIFTI_INDEX_TYPE_LABELS: case CIFTI_INDEX_TYPE_SCALARS: { size_t size = m_namedMaps.size(); if (rhs.m_namedMaps.size() != size) return false; for (size_t i = 0; i < size; ++i) { if (m_namedMaps[i] != rhs.m_namedMaps[i]) return false; } } break; } return true; } bool CiftiBrainModelElement::operator==(const CiftiBrainModelElement& rhs) const { if (m_indexOffset != rhs.m_indexOffset) return false; if (m_indexCount != rhs.m_indexCount) return false; if (m_modelType != rhs.m_modelType) return false; if (m_brainStructure != rhs.m_brainStructure) return false; switch (m_modelType) { case CIFTI_MODEL_TYPE_SURFACE: { size_t numIndices = m_nodeIndices.size(), rhsIndices = rhs.m_nodeIndices.size(); if (m_surfaceNumberOfNodes != rhs.m_surfaceNumberOfNodes) return false; if (numIndices == 0) { if (rhsIndices != 0) { if ((int64_t)rhsIndices != rhs.m_indexCount) return false; for (size_t i = 0; i < rhsIndices; ++i) { if (rhs.m_nodeIndices[i] != (int64_t)i) return false; } } } else { if (rhsIndices == 0) { if ((int64_t)numIndices != m_indexCount) return false; for (size_t i = 0; i < numIndices; ++i) { if (m_nodeIndices[i] != (int64_t)i) return false; } } else { if (numIndices != rhsIndices) return false; for (size_t i = 0; i < numIndices; ++i) { if (m_nodeIndices[i] != rhs.m_nodeIndices[i]) return false; } } } } break; case CIFTI_MODEL_TYPE_VOXELS: { size_t numIndices = m_voxelIndicesIJK.size(), rhsIndices = m_voxelIndicesIJK.size(); if (numIndices != rhsIndices) return false; for (size_t i = 0; i < numIndices; ++i)//treat them as flat, even though they aren't { if (m_voxelIndicesIJK[i] != rhs.m_voxelIndicesIJK[i]) return false; } } break; case CIFTI_MODEL_TYPE_INVALID: CaretAssert(false); throw DataFileException("found 'invalid' brain model type, tell the developers"); break; } return true; } bool CiftiBrainModelElement::operator<(const CiftiBrainModelElement& rhs) const { return m_indexOffset < rhs.m_indexOffset; } bool CiftiNamedMapElement::operator==(const CiftiNamedMapElement& rhs) const { if (m_mapName != rhs.m_mapName) return false; if (m_labelTable == NULL) { if (rhs.m_labelTable != NULL) return false; } else { if (rhs.m_labelTable == NULL) return false; if (!m_labelTable->matches(*(rhs.m_labelTable))) return false; } return true; } CiftiNamedMapElement::CiftiNamedMapElement(const CiftiNamedMapElement& rhs) { m_mapName = rhs.m_mapName; m_defaultPalette = rhs.m_defaultPalette; m_mapMetaData = rhs.m_mapMetaData; if (rhs.m_palette != NULL) m_palette.grabNew(new PaletteColorMapping(*(rhs.m_palette))); if (rhs.m_labelTable != NULL) m_labelTable.grabNew(new GiftiLabelTable(*(rhs.m_labelTable))); } CiftiNamedMapElement& CiftiNamedMapElement::operator=(const CiftiNamedMapElement& rhs) { if (this == &rhs) return *this; m_mapName = rhs.m_mapName; m_mapMetaData = rhs.m_mapMetaData; if (rhs.m_palette != NULL) { m_palette.grabNew(new PaletteColorMapping(*(rhs.m_palette))); } else { m_palette.grabNew(NULL); } if (rhs.m_labelTable != NULL) { m_labelTable.grabNew(new GiftiLabelTable(*(rhs.m_labelTable))); } else { m_labelTable.grabNew(NULL); } return *this; } ///NOTE: this is not a standard equality test, it skips checking the nodes list because we do that elsewhere bool CiftiParcelNodesElement::operator==(const CiftiParcelNodesElement& rhs) const { if (m_structure != rhs.m_structure) return false; if (m_nodes.size() != rhs.m_nodes.size()) return false; /*for (size_t i = 0; i < m_nodes.size(); ++i) { if (m_nodes[i] != rhs.m_nodes[i]) return false; }//*///not needed, we check node equivalence by checking the lookup in each surface, this way wouldn't work for same nodes in different order anyway return true; } bool CiftiParcelElement::operator==(const CiftiParcelElement& rhs) const { if (m_parcelName != rhs.m_parcelName) return false; if (m_nodeElements.size() != rhs.m_nodeElements.size() || m_voxelIndicesIJK.size() != rhs.m_voxelIndicesIJK.size()) return false; vector used(rhs.m_nodeElements.size(), false); for (size_t i = 0; i < m_nodeElements.size(); ++i) { bool found = false; for (size_t j = 0; j < rhs.m_nodeElements.size(); ++j) { if (!used[j] && m_nodeElements[i] == rhs.m_nodeElements[j]) { found = true; used[j] = true; break; } } if (!found) return false; } set myvoxset, rhsvoxset;//different orders mean the same thing, so sort voxels, then compare for (size_t i = 0; i < m_voxelIndicesIJK.size(); i += 3) { myvoxset.insert(VoxelIJK(m_voxelIndicesIJK.data() + i)); rhsvoxset.insert(VoxelIJK(rhs.m_voxelIndicesIJK.data() + i)); } return (myvoxset == rhsvoxset); } bool CiftiParcelSurfaceElement::operator==(const CiftiParcelSurfaceElement& rhs) const { if (m_structure != rhs.m_structure) return false; if (m_numNodes != rhs.m_numNodes) return false; CaretAssert(m_numNodes == (int64_t)m_lookup.size()); CaretAssert(rhs.m_lookup.size() == m_lookup.size()); for (size_t i = 0; i < m_lookup.size(); ++i) {//check lookup instead of checking node lists in parcels if (m_lookup[i] != rhs.m_lookup[i]) return false; } return true; } CiftiMatrixElement::CiftiMatrixElement(const CiftiMatrixElement& rhs) { m_labelTable = rhs.m_labelTable; m_userMetaData = rhs.m_userMetaData; if (rhs.m_palette != NULL) m_palette.grabNew(new PaletteColorMapping(*(rhs.m_palette))); m_defaultPalette = rhs.m_defaultPalette; m_matrixIndicesMap = rhs.m_matrixIndicesMap; m_volume = rhs.m_volume; } CiftiMatrixElement& CiftiMatrixElement::operator=(const caret::CiftiMatrixElement& rhs) { if (this == &rhs) return *this; m_labelTable = rhs.m_labelTable; m_userMetaData = rhs.m_userMetaData; if (rhs.m_palette != NULL) m_palette.grabNew(new PaletteColorMapping(*(rhs.m_palette))); m_defaultPalette = rhs.m_defaultPalette; m_matrixIndicesMap = rhs.m_matrixIndicesMap; m_volume = rhs.m_volume; return *this; } CiftiNamedMapElement::CiftiNamedMapElement() {//just so that the destructors of GiftiLabelTable and PaletteColorMapping are resolved without including them into the .h m_defaultPalette = false; } CiftiNamedMapElement::~CiftiNamedMapElement() { } CiftiMatrixElement::CiftiMatrixElement() {//ditto m_defaultPalette = false; } CiftiMatrixElement::~CiftiMatrixElement() { } connectome-workbench-1.2.3+git41-gc4c6c90/src/Cifti/CiftiXMLElements.h000066400000000000000000000231251300200146000250720ustar00rootroot00000000000000/*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*/ #ifndef __CIFTI_XML_ELEMENTS #define __CIFTI_XML_ELEMENTS #include #include #include #include "AString.h" #include "CaretPointer.h" #include "CaretCompact3DLookup.h" #include "CiftiVersion.h" #include "nifti2.h" #include "StructureEnum.h" /* Cifti Defines */ namespace caret { class PaletteColorMapping; class GiftiLabelTable; /*! ModelType */ enum ModelType { CIFTI_MODEL_TYPE_INVALID, CIFTI_MODEL_TYPE_SURFACE=1,/*!< CIFTI_MODEL_TYPE_SURFACE*/ CIFTI_MODEL_TYPE_VOXELS=2/*!< CIFTI_MODEL_TYPE_VOXELS*/ }; /*! IndicesMapToDataType*/ enum IndicesMapToDataType { CIFTI_INDEX_TYPE_INVALID, CIFTI_INDEX_TYPE_BRAIN_MODELS=1,/*!< CIFTI_INDEX_TYPE_BRAIN_MODELS*/ CIFTI_INDEX_TYPE_FIBERS=2,/*!< CIFTI_INDEX_TYPE_FIBERS*/ CIFTI_INDEX_TYPE_PARCELS=3,/*!< CIFTI_INDEX_TYPE_PARCELS*/ CIFTI_INDEX_TYPE_TIME_POINTS=4,/*!< CIFTI_INDEX_TYPE_TIME_POINTS*/ CIFTI_INDEX_TYPE_SCALARS=5, CIFTI_INDEX_TYPE_LABELS=6 }; typedef int voxelIndexType; class CiftiMatrixIndicesMapElement; /// Cifti Brain Model XML Element class CiftiBrainModelElement { public: //CiftiBrainModelElement(); int64_t m_indexOffset; /*!< Index of first element in dimension of the matrix for this brain structure. The value is the number of elements, NOT the number of bytes. */ int64_t m_indexCount; /*!< Number of elements in this brain model. */ ModelType m_modelType; /*!< Type of model representing the brain structure. */ StructureEnum::Enum m_brainStructure; /*!< Identifies the brain structure. Valid values are contained in nifti2.h */ int64_t m_surfaceNumberOfNodes; /*!< This attribute contains the actual (or true) number of nodes in the surface that is associated with this BrainModel.*/ //children std::vector m_nodeIndices; /*!< Contains a list of nodes indices for a BrainModel with ModelType equal to CIFTI_MODEL_TYPE_SURFACE.*/ std::vector m_voxelIndicesIJK; /*!< Identifies the voxels that model a brain structure. */ std::vector m_nodeToIndexLookup;//used by CiftiXML to quickly lookup indexes by node number void setupLookup(CiftiMatrixIndicesMapElement& myMap);//convenience function to populate lookup bool operator==(const CiftiBrainModelElement& rhs) const; bool operator<(const CiftiBrainModelElement& rhs) const;//for sorting by starting index }; struct CiftiNamedMapElement { mutable AString m_mapName; mutable CaretPointer m_labelTable; mutable std::map m_mapMetaData;/*!< User Meta Data*/ mutable CaretPointer m_palette;//palette settings storage mutable bool m_defaultPalette;//secondary variable to enable resetting the palette to use defaults, which will make it not write the palette to file CiftiNamedMapElement(); CiftiNamedMapElement(const CiftiNamedMapElement& rhs);//to turn copy and assignment into a deep copy of the label table ~CiftiNamedMapElement(); CiftiNamedMapElement& operator=(const CiftiNamedMapElement& rhs); bool operator==(const CiftiNamedMapElement& rhs) const; bool operator!=(const CiftiNamedMapElement& rhs) const { return !(*this == rhs); } }; struct CiftiParcelNodesElement { StructureEnum::Enum m_structure; std::vector m_nodes; bool operator==(const CiftiParcelNodesElement& rhs) const; bool operator!=(const CiftiParcelNodesElement& rhs) const { return !(*this == rhs); } }; struct CiftiParcelElement { AString m_parcelName; std::vector m_nodeElements; std::vector m_voxelIndicesIJK; bool operator==(const CiftiParcelElement& rhs) const; bool operator!=(const CiftiParcelElement& rhs) const { return !(*this == rhs); } }; struct CiftiParcelSurfaceElement { StructureEnum::Enum m_structure; int64_t m_numNodes; std::vector m_lookup; bool operator==(const CiftiParcelSurfaceElement& rhs) const; bool operator!=(const CiftiParcelSurfaceElement& rhs) const { return !(*this == rhs); } }; /// Cifti Matrix Indices Map XML Element class CiftiMatrixIndicesMapElement { public: CiftiMatrixIndicesMapElement() { m_timeStep = -1.0; m_timeStart = -1.0; m_hasTimeStart = false; m_timeStepUnits = -1; m_numTimeSteps = -1; m_indicesMapToDataType = CIFTI_INDEX_TYPE_INVALID; } std::vector m_appliesToMatrixDimension; /*!< Lists the dimension(s) of the matrix to which this MatrixIndicesMap applies. */ IndicesMapToDataType m_indicesMapToDataType; /*!< Type of data to which the MatrixIndicesMap applies. */ double m_timeStep; /*!< Indicates amount of time between each timepoint. */ double m_timeStart; bool m_hasTimeStart; int m_timeStepUnits;/*!< Indicates units of TimeStep. */ int m_numTimeSteps;//used by CiftiXML to store the information that is critically lacking in the XML extension std::vector m_brainModels;/*!< A vector array of Brain Models */ CaretCompact3DLookup m_voxelToIndexLookup;//make one unified lookup rather than separate lookups per volume structure std::vector m_namedMaps; std::vector m_parcels; std::vector m_parcelSurfaces; void setupLookup(); bool operator==(const CiftiMatrixIndicesMapElement& rhs) const; }; /// Cifti Label XML Element class CiftiLabelElement { public: CiftiLabelElement() { m_red = m_green = m_blue = m_alpha = m_x = m_y = m_z = 0.0; } unsigned long long m_key;/*!< Corresponding index, starting at zero, of a row and/or column of the connectivity matrix.*/ float m_red;/*!< Red color component for label. Value is floating point with range 0.0 to 1.0.*/ float m_green;/*!< Green color component for label. Value is floating point with range 0.0 to 1.0.*/ float m_blue;/*!< Blue color component for label. Value is floating point with range 0.0 to 1.0.*/ float m_alpha;/*!< Alpha color component for label. Value is floating point with range 0.0 to 1.0.*/ float m_x;/*!< X-coordinate of spatial location associated with the label. Value is floating point.*/ float m_y;/*!< Y-coordinate of spatial location associated with the label. Value is floating point.*/ float m_z;/*!< Z-coordinate of spatial location associated with the label. Value is floating point.*/ QString m_text;/*!< Text of the label.*/ }; /// Transformation Matrix Voxel Indices IJK to XYZ XML Element class TransformationMatrixVoxelIndicesIJKtoXYZElement { public: unsigned long m_dataSpace;/*!< Contains the name of the space of the voxels prior to application of the transformation matrix.*/ unsigned long m_transformedSpace;/*!< Contains the name of the space of the voxels after application of the transformation of voxel IJK indices to spatial XYZ coordinates. */ unsigned long m_unitsXYZ;/*!< Identifies the units of the spatial XYZ coordinates.*/ float m_transform[16];/*!< A 16 element matrix in row-major form which contains a matrix that for conversion of Voxel IJK Indices to spatial XYZ coordinates (+X=>right, +Y=>anterior, +Z=>posterior).*/ }; /// Cifti Volume XML Element class CiftiVolumeElement { public: std::vector m_transformationMatrixVoxelIndicesIJKtoXYZ;/*!< Vector array of 0 or more TransformationMatrixVoxelIndicesIJKtoXYZ*/ unsigned int m_volumeDimensions[3];/*!< Three integer values that indicate original dimensions of the volume from which the voxels were extracted.*/ }; /// Cifti Matrix XML Element class CiftiMatrixElement { public: CiftiMatrixElement(const CiftiMatrixElement& rhs); CiftiMatrixElement& operator=(const CiftiMatrixElement& rhs); CiftiMatrixElement(); ~CiftiMatrixElement(); std::vector m_labelTable;/*!< The Matrix's Label Table (optional)*///TODO: replace this with GiftiLabelTable (or remove? not being used for anything) mutable std::map m_userMetaData;/*!< User Meta Data*/ mutable CaretPointer m_palette;//palette settings storage mutable bool m_defaultPalette;//secondary variable to enable resetting the palette to use defaults, which will make it not write the palette to file std::vector m_matrixIndicesMap;/*!< Vector array of one or more Matrix Indices Map Elements*/ std::vector m_volume;/*!< Volume Element*/ }; /// Cifti Root XML Element class CiftiRootElement { public: CiftiRootElement() { m_numberOfMatrices = 0; } CiftiVersion m_version;/*!< Version String*/ unsigned long m_numberOfMatrices;/*!< Number of Matrices*/ std::vector m_matrices; /*!< Matrices, currently there is only matrix, but future versions may allow for more */ }; } #endif //__CIFTI_ELEMENTS connectome-workbench-1.2.3+git41-gc4c6c90/src/Cifti/CiftiXMLOld.cxx000066400000000000000000002460111300200146000244100ustar00rootroot00000000000000/*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 "CaretAssert.h" #include "CiftiXMLOld.h" #include "DataFileContentInformation.h" #include "DataFileException.h" #include "FloatMatrix.h" #include "GiftiLabelTable.h" #include "Palette.h" #include "PaletteColorMapping.h" #include "CiftiXML.h" #include "CaretLogger.h" using namespace caret; using namespace std; /*void CiftiXMLOld::testNewXML(const QString& xmlString) { CiftiXML test1, test2; CiftiXMLOld compare1, compare2; AString stage; try { stage = "1_newRead1"; test1.readXML(AString(xmlString)); for (int i = 0; i < test1.getNumberOfDimensions(); ++i) { if (test1.getMappingType(i) == CiftiMappingType::SERIES) { CiftiSeriesMap myMap = test1.getSeriesMap(i); myMap.setLength(1);//there are asserts in series map that upon writing, it must know the length, so fake it for now test1.setMap(i, myMap); } } stage = "2_newWrite2"; AString newWritten2 = test1.writeXMLToString(CiftiVersion(1, 1)); stage = "3_newRead2"; test2.readXML(newWritten2); stage = "4_newCompare"; if (test1.getNumberOfDimensions() == test2.getNumberOfDimensions()) { for (int i = 0; i < test1.getNumberOfDimensions(); ++i) { if (*(test1.getMap(i)) != *(test2.getMap(i))) { CaretLogWarning("comparison in new cifti xml failed, dimension " + AString::number(i) + " not equal"); } } } else { CaretLogWarning("comparison in new cifti xml failed, different number of dimensions"); } stage = "5_newWrite1"; AString newWritten1 = test2.writeXMLToString(CiftiVersion(1, 0)); stage = "6_oldRead1"; compare1.readXML(newWritten1, false);//prevent recursively triggering test compare2.readXML(xmlString, false); if (!compare1.matchesForRows(compare2)) { CaretLogWarning("comparison in old xml failed for rows"); } if (!compare1.matchesForColumns(compare2)) { CaretLogWarning("comparison in old xml failed for columns"); } } catch (CaretException& e) { CaretLogWarning("error testing new xml: stage '" + stage + "', error: " + e.whatString()); } catch (std::exception& e) { CaretLogWarning("error testing new xml: stage '" + stage + "', error: " + e.what()); } catch (...) { CaretLogWarning("error testing new xml: stage '" + stage + "', unknown throw type"); } }//*/ /*void CiftiXMLOld::testNewXML(const QByteArray& xmlBytes) { CiftiXML test1, test2; CiftiXMLOld compare1, compare2; AString stage; try { stage = "1_newRead1"; test1.readXML(xmlBytes); for (int i = 0; i < test1.getNumberOfDimensions(); ++i) { if (test1.getMappingType(i) == CiftiMappingType::SERIES) { CiftiSeriesMap myMap = test1.getSeriesMap(i); myMap.setLength(1);//there are asserts in series map that upon writing, it must know the length, so fake it for now test1.setMap(i, myMap); } } stage = "2_newWrite2"; QByteArray newWritten2 = test1.writeXMLToQByteArray(CiftiVersion(1, 1)); stage = "3_newRead2"; test2.readXML(newWritten2); stage = "4_newCompare"; if (test1.getNumberOfDimensions() == test2.getNumberOfDimensions()) { for (int i = 0; i < test1.getNumberOfDimensions(); ++i) { if (*(test1.getMap(i)) != *(test2.getMap(i))) { CaretLogWarning("comparison in new cifti xml failed, dimension " + AString::number(i) + " not equal"); } } } else { CaretLogWarning("comparison in new cifti xml failed, different number of dimensions"); } stage = "5_newWrite1"; QByteArray newWritten1 = test2.writeXMLToQByteArray(CiftiVersion(1, 0)); stage = "6_oldRead1"; compare1.readXML(newWritten1, false);//prevent recursively triggering test compare2.readXML(xmlBytes, false); if (!compare1.matchesForRows(compare2)) { CaretLogWarning("comparison in old xml failed for rows"); } if (!compare1.matchesForColumns(compare2)) { CaretLogWarning("comparison in old xml failed for columns"); } } catch (CaretException& e) { CaretLogWarning("error testing new xml: stage '" + stage + "', error: " + e.whatString()); } catch (std::exception& e) { CaretLogWarning("error testing new xml: stage '" + stage + "', error: " + e.what()); } catch (...) { CaretLogWarning("error testing new xml: stage '" + stage + "', unknown throw type"); } }//*/ CiftiXMLOld::CiftiXMLOld() { m_dimToMapLookup.resize(2, -1);//assume matrix is 2D, for backwards compatibility with Row/Column functions } map* CiftiXMLOld::getFileMetaData() const { if (m_root.m_matrices.size() == 0) return NULL; return &(m_root.m_matrices[0].m_userMetaData); } int64_t CiftiXMLOld::getSurfaceIndex(const int64_t& node, const CiftiBrainModelElement* myElement) const { if (myElement == NULL || myElement->m_modelType != CIFTI_MODEL_TYPE_SURFACE) return -1; if (node < 0 || node > (int64_t)(myElement->m_surfaceNumberOfNodes)) return -1; CaretAssertVectorIndex(myElement->m_nodeToIndexLookup, node); return myElement->m_nodeToIndexLookup[node]; } int64_t CiftiXMLOld::getColumnIndexForNode(const int64_t& node, const StructureEnum::Enum& structure) const { return getSurfaceIndex(node, findSurfaceModel(m_dimToMapLookup[0], structure));//a column index is an index to get an entire column, so index ALONG a row } int64_t CiftiXMLOld::getRowIndexForNode(const int64_t& node, const StructureEnum::Enum& structure) const { return getSurfaceIndex(node, findSurfaceModel(m_dimToMapLookup[1], structure)); } int64_t CiftiXMLOld::getVolumeIndex(const int64_t* ijk, const int& myMapIndex) const { if (myMapIndex == -1 || m_root.m_matrices.size() == 0) return -1; CaretAssertVectorIndex(m_root.m_matrices[0].m_matrixIndicesMap, myMapIndex); const CiftiMatrixIndicesMapElement* myMap = &(m_root.m_matrices[0].m_matrixIndicesMap[myMapIndex]); if (myMap->m_indicesMapToDataType != CIFTI_INDEX_TYPE_BRAIN_MODELS) { return -1; } if (m_root.m_matrices[0].m_volume.size() == 0) return -1; const CiftiVolumeElement& myVol = m_root.m_matrices[0].m_volume[0]; if (ijk[0] < 0 || ijk[0] >= (int64_t)myVol.m_volumeDimensions[0]) return -1;//some shortcuts to not search all the voxels on invalid coords if (ijk[1] < 0 || ijk[1] >= (int64_t)myVol.m_volumeDimensions[1]) return -1; if (ijk[2] < 0 || ijk[2] >= (int64_t)myVol.m_volumeDimensions[2]) return -1; const int64_t* test = myMap->m_voxelToIndexLookup.find(ijk); if (test == NULL) return -1; return *test; } int64_t CiftiXMLOld::getColumnIndexForVoxel(const int64_t* ijk) const { return getVolumeIndex(ijk, m_dimToMapLookup[0]); } int64_t CiftiXMLOld::getRowIndexForVoxel(const int64_t* ijk) const { return getVolumeIndex(ijk, m_dimToMapLookup[1]); } bool CiftiXMLOld::getSurfaceMap(const int& direction, vector& mappingOut, const StructureEnum::Enum& structure) const { if (direction < 0 || direction >= (int)m_dimToMapLookup.size()) { mappingOut.clear(); return false; } const CiftiBrainModelElement* myModel = findSurfaceModel(m_dimToMapLookup[direction], structure); if (myModel == NULL || myModel->m_modelType != CIFTI_MODEL_TYPE_SURFACE) { mappingOut.clear(); return false; } int64_t mappingSize = myModel->m_indexCount; mappingOut.resize(mappingSize); if (myModel->m_nodeIndices.size() == 0) { CaretAssert(myModel->m_indexCount == myModel->m_surfaceNumberOfNodes); for (int i = 0; i < mappingSize; ++i) { mappingOut[i].m_ciftiIndex = myModel->m_indexOffset + i; mappingOut[i].m_surfaceNode = i; } } else { for (int i = 0; i < mappingSize; ++i) { mappingOut[i].m_ciftiIndex = myModel->m_indexOffset + i; mappingOut[i].m_surfaceNode = myModel->m_nodeIndices[i]; } } return true; } bool CiftiXMLOld::getSurfaceMapForColumns(vector& mappingOut, const StructureEnum::Enum& structure) const { return getSurfaceMap(ALONG_COLUMN, mappingOut, structure); } bool CiftiXMLOld::getSurfaceMapForRows(vector& mappingOut, const StructureEnum::Enum& structure) const { return getSurfaceMap(ALONG_ROW, mappingOut, structure); } bool CiftiXMLOld::getVolumeMap(const int& direction, vector& mappingOut) const { mappingOut.clear(); if (direction < 0 || direction >= (int)m_dimToMapLookup.size()) { return false; } int myMapIndex = m_dimToMapLookup[direction]; if (myMapIndex == -1 || m_root.m_matrices.size() == 0) { return false; } CaretAssertVectorIndex(m_root.m_matrices[0].m_matrixIndicesMap, myMapIndex); const CiftiMatrixIndicesMapElement* myMap = &(m_root.m_matrices[0].m_matrixIndicesMap[myMapIndex]); if (myMap->m_indicesMapToDataType != CIFTI_INDEX_TYPE_BRAIN_MODELS) { return false; } int64_t myIndex = 0; bool first = true; for (int64_t i = 0; i < (int64_t)myMap->m_brainModels.size(); ++i) { if (myMap->m_brainModels[i].m_modelType == CIFTI_MODEL_TYPE_VOXELS) { const vector& myVoxels = myMap->m_brainModels[i].m_voxelIndicesIJK; int64_t voxelArraySize = (int64_t)myVoxels.size(); int64_t modelOffset = myMap->m_brainModels[i].m_indexOffset; int64_t j1 = 0; if (first) { mappingOut.reserve(voxelArraySize / 3);//skip the tiny vector reallocs first = false; } for (int64_t j = 0; j < voxelArraySize; j += 3) { mappingOut.push_back(CiftiVolumeMap());//default constructor should be NOOP and get removed by compiler mappingOut[myIndex].m_ciftiIndex = modelOffset + j1; mappingOut[myIndex].m_ijk[0] = myVoxels[j]; mappingOut[myIndex].m_ijk[1] = myVoxels[j + 1]; mappingOut[myIndex].m_ijk[2] = myVoxels[j + 2]; ++j1; ++myIndex; } } } return true; } bool CiftiXMLOld::getVolumeMapForColumns(vector& mappingOut) const { return getVolumeMap(ALONG_COLUMN, mappingOut); } bool CiftiXMLOld::getVolumeMapForRows(vector& mappingOut) const { return getVolumeMap(ALONG_ROW, mappingOut); } void CiftiXMLOld::getVoxelInfoInDataFileContentInformation(const int& direction, DataFileContentInformation& dataFileInformation) const { if (direction < 0 || direction >= (int)m_dimToMapLookup.size()) { return; } int myMapIndex = m_dimToMapLookup[direction]; if (myMapIndex == -1 || m_root.m_matrices.size() == 0) { return; } CaretAssertVectorIndex(m_root.m_matrices[0].m_matrixIndicesMap, myMapIndex); const CiftiMatrixIndicesMapElement* myMap = &(m_root.m_matrices[0].m_matrixIndicesMap[myMapIndex]); if (myMap->m_indicesMapToDataType != CIFTI_INDEX_TYPE_BRAIN_MODELS) { return; } VolumeSpace volumeSpace; getVolumeSpace(volumeSpace); const int64_t* dims = volumeSpace.getDims(); dataFileInformation.addNameAndValue("Dimensions", AString::fromNumbers(dims, 3, ",")); VolumeSpace::OrientTypes orientation[3]; float spacing[3]; float origin[3]; volumeSpace.getOrientAndSpacingForPlumb(orientation, spacing, origin); dataFileInformation.addNameAndValue("Spacing", AString::fromNumbers(spacing, 3, ",")); dataFileInformation.addNameAndValue("Origin", AString::fromNumbers(origin, 3, ",")); const std::vector >& sform = volumeSpace.getSform(); for (uint32_t i = 0; i < sform.size(); i++) { dataFileInformation.addNameAndValue(("sform row " + AString::number(i)), AString::fromNumbers(sform[i], ",")); } int64_t myIndex = 0; for (int64_t i = 0; i < (int64_t)myMap->m_brainModels.size(); ++i) { if (myMap->m_brainModels[i].m_modelType == CIFTI_MODEL_TYPE_VOXELS) { const vector& myVoxels = myMap->m_brainModels[i].m_voxelIndicesIJK; int64_t voxelArraySize = (int64_t)myVoxels.size(); int64_t modelOffset = myMap->m_brainModels[i].m_indexOffset; int64_t j1 = 0; const AString structureName = StructureEnum::toGuiName(myMap->m_brainModels[i].m_brainStructure); for (int64_t j = 0; j < voxelArraySize; j += 3) { const int64_t ijk[3] = { myVoxels[j], myVoxels[j + 1], myVoxels[j + 2] }; float xyz[3]; volumeSpace.indexToSpace(ijk, xyz); const AString msg = ("ijk=(" + AString::fromNumbers(ijk, 3, ",") + "), xyz=(" + AString::fromNumbers(xyz, 3, ", ") + "), row=" + AString::number(modelOffset + j1) + " "); dataFileInformation.addNameAndValue(structureName, msg); ++j1; ++myIndex; } } } } bool CiftiXMLOld::getVolumeStructureMap(const int& direction, vector& mappingOut, const StructureEnum::Enum& structure) const { mappingOut.clear(); CaretAssertVectorIndex(m_dimToMapLookup, direction); int myMapIndex = m_dimToMapLookup[direction]; const CiftiBrainModelElement* myModel = findVolumeModel(myMapIndex, structure); if (myModel == NULL) { return false; } int64_t size = (int64_t)myModel->m_voxelIndicesIJK.size(); CaretAssert(size % 3 == 0); mappingOut.resize(size / 3); int64_t index = 0; for (int64_t i = 0; i < size; i += 3) { mappingOut[index].m_ciftiIndex = myModel->m_indexOffset + index; mappingOut[index].m_ijk[0] = myModel->m_voxelIndicesIJK[i]; mappingOut[index].m_ijk[1] = myModel->m_voxelIndicesIJK[i + 1]; mappingOut[index].m_ijk[2] = myModel->m_voxelIndicesIJK[i + 2]; ++index; } return true; } bool CiftiXMLOld::getVolumeStructureMapForColumns(vector& mappingOut, const StructureEnum::Enum& structure) const { return getVolumeStructureMap(ALONG_COLUMN, mappingOut, structure); } bool CiftiXMLOld::getVolumeStructureMapForRows(vector& mappingOut, const StructureEnum::Enum& structure) const { return getVolumeStructureMap(ALONG_ROW, mappingOut, structure); } bool CiftiXMLOld::getVolumeModelMappings(vector& mappingsOut, const int& myMapIndex) const { mappingsOut.clear(); if (myMapIndex == -1 || m_root.m_matrices.size() == 0) { return false; } CaretAssertVectorIndex(m_root.m_matrices[0].m_matrixIndicesMap, myMapIndex); const CiftiMatrixIndicesMapElement* myMap = &(m_root.m_matrices[0].m_matrixIndicesMap[myMapIndex]); if (myMap->m_indicesMapToDataType != CIFTI_INDEX_TYPE_BRAIN_MODELS) { return false; } int numModels = (int)myMap->m_brainModels.size(); mappingsOut.reserve(numModels); for (int i = 0; i < numModels; ++i) { if (myMap->m_brainModels[i].m_modelType == CIFTI_MODEL_TYPE_VOXELS) { mappingsOut.push_back(CiftiVolumeStructureMap()); int whichMap = (int)mappingsOut.size() - 1; mappingsOut[whichMap].m_structure = myMap->m_brainModels[i].m_brainStructure; int numIndices = (int)myMap->m_brainModels[i].m_indexCount; mappingsOut[whichMap].m_map.resize(numIndices); for (int index = 0; index < numIndices; ++index) { mappingsOut[whichMap].m_map[index].m_ciftiIndex = myMap->m_brainModels[i].m_indexOffset + index; int64_t i3 = index * 3; mappingsOut[whichMap].m_map[index].m_ijk[0] = myMap->m_brainModels[i].m_voxelIndicesIJK[i3]; mappingsOut[whichMap].m_map[index].m_ijk[1] = myMap->m_brainModels[i].m_voxelIndicesIJK[i3 + 1]; mappingsOut[whichMap].m_map[index].m_ijk[2] = myMap->m_brainModels[i].m_voxelIndicesIJK[i3 + 2]; } } } return true; } bool CiftiXMLOld::getVolumeModelMapsForColumns(vector& mappingsOut) const { return getVolumeModelMappings(mappingsOut, m_dimToMapLookup[1]); } bool CiftiXMLOld::getVolumeModelMapsForRows(vector& mappingsOut) const { return getVolumeModelMappings(mappingsOut, m_dimToMapLookup[0]); } bool CiftiXMLOld::getStructureLists(const int& direction, vector& surfaceList, vector& volumeList) const { surfaceList.clear(); volumeList.clear(); if (direction < 0 || direction >= (int)m_dimToMapLookup.size()) { return false; } int myMapIndex = m_dimToMapLookup[direction]; if (myMapIndex == -1 || m_root.m_matrices.size() == 0) { return false; } CaretAssertVectorIndex(m_root.m_matrices[0].m_matrixIndicesMap, myMapIndex); const CiftiMatrixIndicesMapElement* myMap = &(m_root.m_matrices[0].m_matrixIndicesMap[myMapIndex]); if (myMap->m_indicesMapToDataType != CIFTI_INDEX_TYPE_BRAIN_MODELS) { return false; } int numModels = (int)myMap->m_brainModels.size(); for (int i = 0; i < numModels; ++i) { switch (myMap->m_brainModels[i].m_modelType) { case CIFTI_MODEL_TYPE_SURFACE: surfaceList.push_back(myMap->m_brainModels[i].m_brainStructure); break; case CIFTI_MODEL_TYPE_VOXELS: volumeList.push_back(myMap->m_brainModels[i].m_brainStructure); break; default: break; } } return true; } bool CiftiXMLOld::getStructureListsForColumns(vector& surfaceList, vector& volumeList) const { return getStructureLists(ALONG_COLUMN, surfaceList, volumeList); } bool CiftiXMLOld::getStructureListsForRows(vector& surfaceList, vector& volumeList) const { return getStructureLists(ALONG_ROW, surfaceList, volumeList); } int CiftiXMLOld::getNumberOfBrainModels(const int& direction) const { CaretAssertVectorIndex(m_dimToMapLookup, direction); int myMapIndex = m_dimToMapLookup[direction]; if (myMapIndex == -1 || m_root.m_matrices.size() == 0) { return -1; } CaretAssertVectorIndex(m_root.m_matrices[0].m_matrixIndicesMap, myMapIndex); const CiftiMatrixIndicesMapElement* myMap = &(m_root.m_matrices[0].m_matrixIndicesMap[myMapIndex]); if (myMap->m_indicesMapToDataType != CIFTI_INDEX_TYPE_BRAIN_MODELS) { return -1; } return (int)myMap->m_brainModels.size();//reuse of type and structure combinations not allowed, so this is limited to number of structure enum values times number of model types (2) } CiftiBrainModelInfo CiftiXMLOld::getBrainModelInfo(const int& direction, const int& whichModel) const { CiftiBrainModelInfo ret; CaretAssertVectorIndex(m_dimToMapLookup, direction); int myMapIndex = m_dimToMapLookup[direction]; if (myMapIndex == -1 || m_root.m_matrices.size() == 0) { return ret; } CaretAssertVectorIndex(m_root.m_matrices[0].m_matrixIndicesMap, myMapIndex); const CiftiMatrixIndicesMapElement* myMap = &(m_root.m_matrices[0].m_matrixIndicesMap[myMapIndex]); if (myMap->m_indicesMapToDataType != CIFTI_INDEX_TYPE_BRAIN_MODELS) { return ret; } CaretAssertVectorIndex(myMap->m_brainModels, whichModel); ret.m_type = myMap->m_brainModels[whichModel].m_modelType; ret.m_structure = myMap->m_brainModels[whichModel].m_brainStructure; return ret; } void CiftiXMLOld::rootChanged() { m_dimToMapLookup.clear();//first, invalidate everything m_dimToMapLookup.resize(2, -1);//assume matrix is 2D, for backwards compatibility with Row/Column functions if (m_root.m_matrices.size() == 0) { return;//it shouldn't crash if it has no matrix, so return instead of throw } CiftiMatrixElement& myMatrix = m_root.m_matrices[0];//assume only one matrix int numMaps = (int)myMatrix.m_matrixIndicesMap.size(); for (int i = 0; i < numMaps; ++i) { CiftiMatrixIndicesMapElement& myMap = myMatrix.m_matrixIndicesMap[i]; int numDimensions = (int)myMap.m_appliesToMatrixDimension.size(); for (int j = 0; j < numDimensions; ++j) { if (myMap.m_appliesToMatrixDimension[j] < 0) throw DataFileException("negative value in m_appliesToMatrixDimension"); while (m_dimToMapLookup.size() <= (size_t)myMap.m_appliesToMatrixDimension[j]) { m_dimToMapLookup.push_back(-1); } m_dimToMapLookup[myMap.m_appliesToMatrixDimension[j]] = i; myMap.setupLookup(); } } } int64_t CiftiXMLOld::getColumnSurfaceNumberOfNodes(const StructureEnum::Enum& structure) const { return getSurfaceNumberOfNodes(ALONG_COLUMN, structure); } int64_t CiftiXMLOld::getRowSurfaceNumberOfNodes(const StructureEnum::Enum& structure) const { return getSurfaceNumberOfNodes(ALONG_ROW, structure); } int64_t CiftiXMLOld::getSurfaceNumberOfNodes(const int& direction, const StructureEnum::Enum& structure) const { if (direction < 0 || direction >= (int)m_dimToMapLookup.size()) return -1; if (m_root.m_matrices.size() == 0) return -1; CaretAssertVectorIndex(m_root.m_matrices[0].m_matrixIndicesMap, m_dimToMapLookup[direction]); const CiftiMatrixIndicesMapElement& myMap = m_root.m_matrices[0].m_matrixIndicesMap[m_dimToMapLookup[direction]]; IndicesMapToDataType myType = myMap.m_indicesMapToDataType; if (myType == CIFTI_INDEX_TYPE_BRAIN_MODELS) { const CiftiBrainModelElement* myModel = findSurfaceModel(m_dimToMapLookup[direction], structure); if (myModel == NULL) return -1; return myModel->m_surfaceNumberOfNodes; } else if (myType == CIFTI_INDEX_TYPE_PARCELS) { int numSurfs = (int)myMap.m_parcelSurfaces.size(); for (int i = 0; i < numSurfs; ++i) { if (myMap.m_parcelSurfaces[i].m_structure == structure) { return myMap.m_parcelSurfaces[i].m_numNodes; } } } return -1; } int64_t CiftiXMLOld::getVolumeIndex(const float* xyz, const int& myMapIndex) const { if (m_root.m_matrices.size() == 0) { return -1; } if (m_root.m_matrices[0].m_volume.size() == 0) { return -1; } const CiftiVolumeElement& myVol = m_root.m_matrices[0].m_volume[0]; if (myVol.m_transformationMatrixVoxelIndicesIJKtoXYZ.size() == 0) { return -1; } const TransformationMatrixVoxelIndicesIJKtoXYZElement& myTrans = myVol.m_transformationMatrixVoxelIndicesIJKtoXYZ[0];//oh the humanity FloatMatrix myMatrix = FloatMatrix::zeros(4, 4); for (int i = 0; i < 3; ++i)//NEVER trust the fourth row of input, NEVER! { for (int j = 0; j < 4; ++j) { myMatrix[i][j] = myTrans.m_transform[i * 4 + j]; } } switch (myTrans.m_unitsXYZ) { case NIFTI_UNITS_MM: break; case NIFTI_UNITS_METER: myMatrix *= 1000.0f; break; case NIFTI_UNITS_MICRON: myMatrix *= 0.001f; break; default: return -1; }; myMatrix[3][3] = 1.0f;//i COULD do this by making a fake volume file, but that seems kinda hacky FloatMatrix toIndexSpace = myMatrix.inverse();//invert to convert the other direction FloatMatrix myCoord = FloatMatrix::zeros(4, 1);//column vector myCoord[0][0] = xyz[0]; myCoord[1][0] = xyz[1]; myCoord[2][0] = xyz[2]; myCoord[3][0] = 1.0f; FloatMatrix myIndices = toIndexSpace * myCoord;//matrix multiply int64_t ijk[3]; ijk[0] = (int64_t)floor(myIndices[0][0] + 0.5f); ijk[1] = (int64_t)floor(myIndices[1][0] + 0.5f); ijk[2] = (int64_t)floor(myIndices[2][0] + 0.5f); return getVolumeIndex(ijk, myMapIndex); } int64_t CiftiXMLOld::getColumnIndexForVoxelCoordinate(const float* xyz) const { return getVolumeIndex(xyz, m_dimToMapLookup[0]); } int64_t CiftiXMLOld::getRowIndexForVoxelCoordinate(const float* xyz) const { return getVolumeIndex(xyz, m_dimToMapLookup[1]); } int64_t CiftiXMLOld::getTimestepIndex(const float& seconds, const int& myMapIndex) const { if (myMapIndex == -1 || m_root.m_matrices.size() == 0) { return false; } CaretAssertVectorIndex(m_root.m_matrices[0].m_matrixIndicesMap, myMapIndex); const CiftiMatrixIndicesMapElement* myMap = &(m_root.m_matrices[0].m_matrixIndicesMap[myMapIndex]); float myStep; if (!getTimestep(myStep, myMapIndex)) { return -1; } float rawIndex = seconds / myStep; int64_t ret = (int64_t)floor(rawIndex + 0.5f); if (ret < 0 || ret >= myMap->m_numTimeSteps) return -1;//NOTE: should this have a different error value if it is after the end of the timeseries return ret; } int64_t CiftiXMLOld::getColumnIndexForTimepoint(const float& seconds) const { return getTimestepIndex(seconds, m_dimToMapLookup[0]); } int64_t CiftiXMLOld::getRowIndexForTimepoint(const float& seconds) const { return getTimestepIndex(seconds, m_dimToMapLookup[1]); } bool CiftiXMLOld::getTimestep(float& seconds, const int& myMapIndex) const { if (myMapIndex == -1 || m_root.m_matrices.size() == 0) { return false; } CaretAssertVectorIndex(m_root.m_matrices[0].m_matrixIndicesMap, myMapIndex); const CiftiMatrixIndicesMapElement* myMap = &(m_root.m_matrices[0].m_matrixIndicesMap[myMapIndex]); if (myMap->m_indicesMapToDataType != CIFTI_INDEX_TYPE_TIME_POINTS) { return false; } switch (myMap->m_timeStepUnits) { case NIFTI_UNITS_SEC: seconds = myMap->m_timeStep; break; case NIFTI_UNITS_MSEC: seconds = myMap->m_timeStep * 0.001f; break; case NIFTI_UNITS_USEC: seconds = myMap->m_timeStep * 0.000001f; break; default: return false; }; return true; } bool CiftiXMLOld::getColumnTimestep(float& seconds) const { return getTimestep(seconds, m_dimToMapLookup[1]); } bool CiftiXMLOld::getRowTimestep(float& seconds) const { return getTimestep(seconds, m_dimToMapLookup[0]); } bool CiftiXMLOld::getTimestart(float& seconds, const int& myMapIndex) const { if (myMapIndex == -1 || m_root.m_matrices.size() == 0) { return false; } CaretAssertVectorIndex(m_root.m_matrices[0].m_matrixIndicesMap, myMapIndex); const CiftiMatrixIndicesMapElement* myMap = &(m_root.m_matrices[0].m_matrixIndicesMap[myMapIndex]); if (myMap->m_indicesMapToDataType != CIFTI_INDEX_TYPE_TIME_POINTS || myMap->m_hasTimeStart == false) { return false; } switch (myMap->m_timeStepUnits) { case NIFTI_UNITS_SEC: seconds = myMap->m_timeStart; break; case NIFTI_UNITS_MSEC: seconds = myMap->m_timeStart * 0.001f; break; case NIFTI_UNITS_USEC: seconds = myMap->m_timeStart * 0.000001f; break; default: return false; }; return true; } bool CiftiXMLOld::getColumnTimestart(float& seconds) const { return getTimestart(seconds, m_dimToMapLookup[1]); } bool CiftiXMLOld::getRowTimestart(float& seconds) const { return getTimestart(seconds, m_dimToMapLookup[0]); } bool CiftiXMLOld::getColumnNumberOfTimepoints(int& numTimepoints) const { if (m_dimToMapLookup[1] == -1 || m_root.m_matrices.size() == 0) { return false; } CaretAssertVectorIndex(m_root.m_matrices[0].m_matrixIndicesMap, m_dimToMapLookup[1]); const CiftiMatrixIndicesMapElement* myMap = &(m_root.m_matrices[0].m_matrixIndicesMap[m_dimToMapLookup[1]]); if (myMap->m_indicesMapToDataType != CIFTI_INDEX_TYPE_TIME_POINTS) { return false; } numTimepoints = myMap->m_numTimeSteps; return true; } bool CiftiXMLOld::getRowNumberOfTimepoints(int& numTimepoints) const { if (m_dimToMapLookup[0] == -1 || m_root.m_matrices.size() == 0) { return false; } CaretAssertVectorIndex(m_root.m_matrices[0].m_matrixIndicesMap, m_dimToMapLookup[0]); const CiftiMatrixIndicesMapElement* myMap = &(m_root.m_matrices[0].m_matrixIndicesMap[m_dimToMapLookup[0]]); if (myMap->m_indicesMapToDataType != CIFTI_INDEX_TYPE_TIME_POINTS) { return false; } numTimepoints = myMap->m_numTimeSteps; return true; } bool CiftiXMLOld::getParcelsForColumns(vector& parcelsOut) const { return getParcels(ALONG_COLUMN, parcelsOut); } bool CiftiXMLOld::getParcelsForRows(vector& parcelsOut) const { return getParcels(ALONG_ROW, parcelsOut); } bool CiftiXMLOld::getParcels(const int& direction, vector< CiftiParcelElement >& parcelsOut) const { CaretAssertVectorIndex(m_dimToMapLookup, direction); if (m_dimToMapLookup[direction] == -1 || m_root.m_matrices.size() == 0) { parcelsOut.clear(); return false; } CaretAssertVectorIndex(m_root.m_matrices[0].m_matrixIndicesMap, m_dimToMapLookup[direction]); const CiftiMatrixIndicesMapElement* myMap = &(m_root.m_matrices[0].m_matrixIndicesMap[m_dimToMapLookup[direction]]); if (myMap->m_indicesMapToDataType != CIFTI_INDEX_TYPE_PARCELS) { parcelsOut.clear(); return false; } parcelsOut = myMap->m_parcels; return true; } bool CiftiXMLOld::getParcelSurfaceStructures(const int& direction, vector& structuresOut) const { structuresOut.clear(); if (direction < 0 || direction >= (int)m_dimToMapLookup.size()) return false; if (m_dimToMapLookup[direction] == -1 || m_root.m_matrices.size() == 0) { return false; } CaretAssertVectorIndex(m_root.m_matrices[0].m_matrixIndicesMap, m_dimToMapLookup[direction]); const CiftiMatrixIndicesMapElement& myMap = m_root.m_matrices[0].m_matrixIndicesMap[m_dimToMapLookup[direction]]; if (myMap.m_indicesMapToDataType != CIFTI_INDEX_TYPE_PARCELS) return false; for (int i = 0; i < (int)myMap.m_parcelSurfaces.size(); ++i) { structuresOut.push_back(myMap.m_parcelSurfaces[i].m_structure); } return true; } int64_t CiftiXMLOld::getParcelForNode(const int64_t& node, const StructureEnum::Enum& structure, const int& myMapIndex) const { if (node < 0 || myMapIndex == -1 || m_root.m_matrices.size() == 0) { return -1; } CaretAssertVectorIndex(m_root.m_matrices[0].m_matrixIndicesMap, myMapIndex); const CiftiMatrixIndicesMapElement& myMap = m_root.m_matrices[0].m_matrixIndicesMap[myMapIndex]; if (myMap.m_indicesMapToDataType != CIFTI_INDEX_TYPE_PARCELS) { return -1; } for (int i = 0; i < (int)myMap.m_parcelSurfaces.size(); ++i) { if (myMap.m_parcelSurfaces[i].m_structure == structure) { if (node < myMap.m_parcelSurfaces[i].m_numNodes) { return myMap.m_parcelSurfaces[i].m_lookup[node]; } else { return -1; } } } return -1; } int64_t CiftiXMLOld::getColumnParcelForNode(const int64_t& node, const StructureEnum::Enum& structure) const { return getParcelForNode(node, structure, m_dimToMapLookup[1]); } int64_t CiftiXMLOld::getRowParcelForNode(const int64_t& node, const caret::StructureEnum::Enum& structure) const { return getParcelForNode(node, structure, m_dimToMapLookup[0]); } int64_t CiftiXMLOld::getParcelForVoxel(const int64_t* ijk, const int& myMapIndex) const { if (ijk[0] < 0 || ijk[1] < 0 || ijk[2] < 0 || myMapIndex == -1 || m_root.m_matrices.size() == 0) { return -1; } CaretAssertVectorIndex(m_root.m_matrices[0].m_matrixIndicesMap, myMapIndex); const CiftiMatrixIndicesMapElement& myMap = m_root.m_matrices[0].m_matrixIndicesMap[myMapIndex]; if (myMap.m_indicesMapToDataType != CIFTI_INDEX_TYPE_PARCELS) { return -1; } const int64_t* test = myMap.m_voxelToIndexLookup.find(ijk); if (test == NULL) return -1; return *test; } int64_t CiftiXMLOld::getColumnParcelForVoxel(const int64_t* ijk) const { return getParcelForVoxel(ijk, m_dimToMapLookup[1]); } int64_t CiftiXMLOld::getRowParcelForVoxel(const int64_t* ijk) const { return getParcelForVoxel(ijk, m_dimToMapLookup[0]); } bool CiftiXMLOld::setColumnNumberOfTimepoints(const int& numTimepoints) { if (m_dimToMapLookup[1] == -1 || m_root.m_matrices.size() == 0) { return false; } CaretAssertVectorIndex(m_root.m_matrices[0].m_matrixIndicesMap, m_dimToMapLookup[1]); CiftiMatrixIndicesMapElement* myMap = &(m_root.m_matrices[0].m_matrixIndicesMap[m_dimToMapLookup[1]]); if (myMap->m_indicesMapToDataType != CIFTI_INDEX_TYPE_TIME_POINTS) { return false; } myMap->m_numTimeSteps = numTimepoints; return true; } bool CiftiXMLOld::setRowNumberOfTimepoints(const int& numTimepoints) { if (m_dimToMapLookup[0] == -1 || m_root.m_matrices.size() == 0) { return false; } CaretAssertVectorIndex(m_root.m_matrices[0].m_matrixIndicesMap, m_dimToMapLookup[0]); CiftiMatrixIndicesMapElement* myMap = &(m_root.m_matrices[0].m_matrixIndicesMap[m_dimToMapLookup[0]]); if (myMap->m_indicesMapToDataType != CIFTI_INDEX_TYPE_TIME_POINTS) { return false; } myMap->m_numTimeSteps = numTimepoints; return true; } bool CiftiXMLOld::setTimestep(const float& seconds, const int& myMapIndex) { if (myMapIndex == -1 || m_root.m_matrices.size() == 0) { return false; } CaretAssertVectorIndex(m_root.m_matrices[0].m_matrixIndicesMap, myMapIndex); CiftiMatrixIndicesMapElement* myMap = &(m_root.m_matrices[0].m_matrixIndicesMap[myMapIndex]); if (myMap->m_indicesMapToDataType != CIFTI_INDEX_TYPE_TIME_POINTS) { return false; } float temp = 1.0f; getTimestart(temp, myMapIndex);//convert to seconds myMap->m_timeStart = temp; myMap->m_timeStepUnits = NIFTI_UNITS_SEC; myMap->m_timeStep = seconds; return true; } bool CiftiXMLOld::setColumnTimestep(const float& seconds) { return setTimestep(seconds, m_dimToMapLookup[1]); } bool CiftiXMLOld::setRowTimestep(const float& seconds) { return setTimestep(seconds, m_dimToMapLookup[0]); } bool CiftiXMLOld::setTimestart(const float& seconds, const int& myMapIndex) { if (myMapIndex == -1 || m_root.m_matrices.size() == 0) { return false; } CaretAssertVectorIndex(m_root.m_matrices[0].m_matrixIndicesMap, myMapIndex); CiftiMatrixIndicesMapElement* myMap = &(m_root.m_matrices[0].m_matrixIndicesMap[myMapIndex]); if (myMap->m_indicesMapToDataType != CIFTI_INDEX_TYPE_TIME_POINTS) { return false; } float temp = 1.0f; getTimestep(temp, myMapIndex);//convert timestep to seconds myMap->m_timeStep = temp; myMap->m_timeStepUnits = NIFTI_UNITS_SEC; myMap->m_timeStart = seconds; myMap->m_hasTimeStart = true; return true; } bool CiftiXMLOld::setColumnTimestart(const float& seconds) { return setTimestart(seconds, m_dimToMapLookup[1]); } bool CiftiXMLOld::setRowTimestart(const float& seconds) { return setTimestart(seconds, m_dimToMapLookup[0]); } bool CiftiXMLOld::getVolumeAttributesForPlumb(VolumeSpace::OrientTypes orientOut[3], int64_t dimensionsOut[3], float originOut[3], float spacingOut[3]) const { VolumeSpace mySpace; if (!getVolumeSpace(mySpace)) return false; const int64_t* myDims = mySpace.getDims(); dimensionsOut[0] = myDims[0]; dimensionsOut[1] = myDims[1]; dimensionsOut[2] = myDims[2]; mySpace.getOrientAndSpacingForPlumb(orientOut, spacingOut, originOut); return true; } bool CiftiXMLOld::getVolumeDimsAndSForm(int64_t dimsOut[3], vector >& sformOut) const { if (m_root.m_matrices.size() == 0) { return false; } if (m_root.m_matrices[0].m_volume.size() == 0) { return false; } const CiftiVolumeElement& myVol = m_root.m_matrices[0].m_volume[0]; if (myVol.m_transformationMatrixVoxelIndicesIJKtoXYZ.size() == 0) { return false; } const TransformationMatrixVoxelIndicesIJKtoXYZElement& myTrans = myVol.m_transformationMatrixVoxelIndicesIJKtoXYZ[0];//oh the humanity FloatMatrix myMatrix = FloatMatrix::zeros(3, 4);//no fourth row for (int i = 0; i < 3; ++i) { for (int j = 0; j < 4; ++j) { myMatrix[i][j] = myTrans.m_transform[i * 4 + j]; } } switch (myTrans.m_unitsXYZ) { case NIFTI_UNITS_MM: break; case NIFTI_UNITS_METER: myMatrix *= 1000.0f; break; case NIFTI_UNITS_MICRON: myMatrix *= 0.001f; break; default: return false; }; sformOut = myMatrix.getMatrix(); dimsOut[0] = myVol.m_volumeDimensions[0]; dimsOut[1] = myVol.m_volumeDimensions[1]; dimsOut[2] = myVol.m_volumeDimensions[2]; return true; } void CiftiXMLOld::setVolumeDimsAndSForm(const int64_t dims[3], const vector >& sform) { CaretAssert(sform.size() == 3 || sform.size() == 4); if (hasVolumeData(ALONG_COLUMN) || hasVolumeData(ALONG_ROW)) { VolumeSpace tempSpace; if (getVolumeSpace(tempSpace))//if it fails to get a volume space when it has volume data...allow it to set it, I guess { if (!tempSpace.matches(VolumeSpace(dims, sform))) { throw DataFileException("cannot change the volume space of cifti xml that already has volume mapping(s)"); } return; } } if (m_root.m_matrices.size() == 0) { m_root.m_matrices.resize(1); } if (m_root.m_matrices[0].m_volume.size() == 0) { m_root.m_matrices[0].m_volume.resize(1); } CiftiVolumeElement& myVol = m_root.m_matrices[0].m_volume[0]; if (myVol.m_transformationMatrixVoxelIndicesIJKtoXYZ.size() == 0) { myVol.m_transformationMatrixVoxelIndicesIJKtoXYZ.resize(1); } TransformationMatrixVoxelIndicesIJKtoXYZElement& myTrans = myVol.m_transformationMatrixVoxelIndicesIJKtoXYZ[0];//oh the humanity for (int i = 0; i < 3; ++i) { CaretAssert(sform[i].size() == 4); for (int j = 0; j < 4; ++j) { myTrans.m_transform[i * 4 + j] = sform[i][j]; } } myTrans.m_transform[12] = 0.0f;//force last row to be set to 0 0 0 1 internally for sanity, even though we don't use it myTrans.m_transform[13] = 0.0f; myTrans.m_transform[14] = 0.0f; myTrans.m_transform[15] = 1.0f; myTrans.m_unitsXYZ = NIFTI_UNITS_MM; myVol.m_volumeDimensions[0] = dims[0]; myVol.m_volumeDimensions[1] = dims[1]; myVol.m_volumeDimensions[2] = dims[2]; } bool CiftiXMLOld::getVolumeSpace(VolumeSpace& volSpaceOut) const { if (m_root.m_matrices.size() == 0) { return false; } if (m_root.m_matrices[0].m_volume.size() == 0) { return false; } const CiftiVolumeElement& myVol = m_root.m_matrices[0].m_volume[0]; if (myVol.m_transformationMatrixVoxelIndicesIJKtoXYZ.size() == 0) { return false; } const TransformationMatrixVoxelIndicesIJKtoXYZElement& myTrans = myVol.m_transformationMatrixVoxelIndicesIJKtoXYZ[0];//oh the humanity FloatMatrix myMatrix = FloatMatrix::zeros(3, 4);//no fourth row for (int i = 0; i < 3; ++i) { for (int j = 0; j < 4; ++j) { myMatrix[i][j] = myTrans.m_transform[i * 4 + j]; } } switch (myTrans.m_unitsXYZ) { case NIFTI_UNITS_MM: break; case NIFTI_UNITS_METER: myMatrix *= 1000.0f; break; case NIFTI_UNITS_MICRON: myMatrix *= 0.001f; break; default: return false; }; int64_t dims[3] = { myVol.m_volumeDimensions[0], myVol.m_volumeDimensions[1], myVol.m_volumeDimensions[2] }; volSpaceOut.setSpace(dims, myMatrix.getMatrix()); return true; } AString CiftiXMLOld::getMapName(const int& direction, const int64_t& index) const { if (m_dimToMapLookup[direction] == -1 || m_root.m_matrices.size() == 0) { return ""; } CaretAssertVectorIndex(m_root.m_matrices[0].m_matrixIndicesMap, m_dimToMapLookup[direction]); const CiftiMatrixIndicesMapElement& myMap = m_root.m_matrices[0].m_matrixIndicesMap[m_dimToMapLookup[direction]]; if (myMap.m_indicesMapToDataType != CIFTI_INDEX_TYPE_SCALARS && myMap.m_indicesMapToDataType != CIFTI_INDEX_TYPE_LABELS) { return ""; } CaretAssertVectorIndex(myMap.m_namedMaps, index); return myMap.m_namedMaps[index].m_mapName; } int64_t CiftiXMLOld::getMapIndexFromNameOrNumber(const int& direction, const AString& numberOrName) const { if (m_dimToMapLookup[direction] == -1 || m_root.m_matrices.size() == 0) { return -1; } CaretAssertVectorIndex(m_root.m_matrices[0].m_matrixIndicesMap, m_dimToMapLookup[direction]); const CiftiMatrixIndicesMapElement& myMap = m_root.m_matrices[0].m_matrixIndicesMap[m_dimToMapLookup[direction]]; bool ok = false; int32_t ret = numberOrName.toInt(&ok) - 1;//compensate for 1-indexing that command line parsing uses if (ok)//always work for integers, even when it is something like brain models or parcels, code that cares can check the mapping type { if (ret < 0 || ret >= getDimensionLength(direction)) { ret = -1; } } else {//DO NOT search by name if the string was parsed as an integer correctly, or some idiot who names their maps as integers will get confused //when getting map "12" out of a file after the file expands to more than 12 elements suddenly does something different if (myMap.m_indicesMapToDataType != CIFTI_INDEX_TYPE_SCALARS && myMap.m_indicesMapToDataType != CIFTI_INDEX_TYPE_LABELS) { return -1;//if we don't have map names to look at, return early rather than repeatedly trying to match against "" (which would be incorrect anyway) } int64_t numMaps = getDimensionLength(direction); ret = -1; for (int64_t i = 0; i < numMaps; ++i) { if (getMapName(direction, i) == numberOrName) { ret = i; break; } } } return ret; } AString CiftiXMLOld::getMapNameForColumnIndex(const int64_t& index) const { return getMapName(ALONG_COLUMN, index); } AString CiftiXMLOld::getMapNameForRowIndex(const int64_t& index) const { return getMapName(ALONG_ROW, index); } bool CiftiXMLOld::setMapNameForIndex(const int& direction, const int64_t& index, const AString& name) const { if (direction < 0 || direction >= (int)m_dimToMapLookup.size()) { return false; } int myMapIndex = m_dimToMapLookup[direction]; if (myMapIndex == -1 || m_root.m_matrices.size() == 0) { return false; } const CiftiMatrixIndicesMapElement& myMap = m_root.m_matrices[0].m_matrixIndicesMap[myMapIndex]; if (myMap.m_indicesMapToDataType != CIFTI_INDEX_TYPE_SCALARS && myMap.m_indicesMapToDataType != CIFTI_INDEX_TYPE_LABELS) { return false; } CaretAssertVectorIndex(myMap.m_namedMaps, index); myMap.m_namedMaps[index].m_mapName = name; return true; } bool CiftiXMLOld::setMapNameForColumnIndex(const int64_t& index, const AString& name) const { return setMapNameForIndex(ALONG_COLUMN, index, name); } bool CiftiXMLOld::setMapNameForRowIndex(const int64_t& index, const AString& name) const { return setMapNameForIndex(ALONG_ROW, index, name); } GiftiLabelTable* CiftiXMLOld::getMapLabelTable(const int& direction, const int64_t& index) const { if (m_dimToMapLookup[direction] == -1 || m_root.m_matrices.size() == 0) { return NULL; } CaretAssertVectorIndex(m_root.m_matrices[0].m_matrixIndicesMap, m_dimToMapLookup[direction]); const CiftiMatrixIndicesMapElement& myMap = m_root.m_matrices[0].m_matrixIndicesMap[m_dimToMapLookup[direction]]; if (myMap.m_indicesMapToDataType != CIFTI_INDEX_TYPE_LABELS) { return NULL; } CaretAssertVectorIndex(myMap.m_namedMaps, index); return myMap.m_namedMaps[index].m_labelTable; } GiftiLabelTable* CiftiXMLOld::getLabelTableForColumnIndex(const int64_t& index) const { return getMapLabelTable(ALONG_COLUMN, index); } GiftiLabelTable* CiftiXMLOld::getLabelTableForRowIndex(const int64_t& index) const { return getMapLabelTable(ALONG_ROW, index); } bool CiftiXMLOld::setLabelTable(const int64_t& index, const GiftiLabelTable& labelTable, const int& myMapIndex) { if (myMapIndex == -1 || m_root.m_matrices.size() == 0) { return false; } CaretAssertVectorIndex(m_root.m_matrices[0].m_matrixIndicesMap, myMapIndex); CiftiMatrixIndicesMapElement& myMap = m_root.m_matrices[0].m_matrixIndicesMap[myMapIndex]; if (myMap.m_indicesMapToDataType != CIFTI_INDEX_TYPE_LABELS) { return false; } CaretAssertVectorIndex(myMap.m_namedMaps, index); if (myMap.m_namedMaps[index].m_labelTable == NULL)//should never happen, but just in case { myMap.m_namedMaps[index].m_labelTable.grabNew(new GiftiLabelTable(labelTable)); } else { *(myMap.m_namedMaps[index].m_labelTable) = labelTable; } return true; } bool CiftiXMLOld::setLabelTableForColumnIndex(const int64_t& index, const GiftiLabelTable& labelTable) { return setLabelTable(index, labelTable, m_dimToMapLookup[1]); } bool CiftiXMLOld::setLabelTableForRowIndex(const int64_t& index, const GiftiLabelTable& labelTable) { return setLabelTable(index, labelTable, m_dimToMapLookup[0]); } bool CiftiXMLOld::hasVolumeData(const int& direction) const { if (direction < 0 || direction >= (int)m_dimToMapLookup.size()) { return false; } int myMapIndex = m_dimToMapLookup[direction]; if (myMapIndex == -1 || m_root.m_matrices.size() == 0) { return false; } CaretAssertVectorIndex(m_root.m_matrices[0].m_matrixIndicesMap, myMapIndex); const CiftiMatrixIndicesMapElement* myMap = &(m_root.m_matrices[0].m_matrixIndicesMap[myMapIndex]); if (m_root.m_matrices[0].m_volume.size() == 0) { return false; } if (myMap == NULL) { return false; } if (myMap->m_indicesMapToDataType == CIFTI_INDEX_TYPE_BRAIN_MODELS) { const CiftiVolumeElement& myVol = m_root.m_matrices[0].m_volume[0]; if (myVol.m_transformationMatrixVoxelIndicesIJKtoXYZ.size() == 0) { return false; } for (int64_t i = 0; i < (int64_t)myMap->m_brainModels.size(); ++i) { if (myMap->m_brainModels[i].m_modelType == CIFTI_MODEL_TYPE_VOXELS) { return true; } } } else if (myMap->m_indicesMapToDataType == CIFTI_INDEX_TYPE_PARCELS) { for (int64_t i = 0; i < (int64_t)myMap->m_parcels.size(); ++i) { if (myMap->m_parcels[i].m_voxelIndicesIJK.size() != 0) { return true; } }//TSC: I now think it should say true for parcels as long as there are voxels, useful for checking whether the volume XML element is needed } return false; } bool CiftiXMLOld::hasRowVolumeData() const { return hasVolumeData(ALONG_ROW); } bool CiftiXMLOld::hasColumnVolumeData() const { return hasVolumeData(ALONG_COLUMN); } bool CiftiXMLOld::hasColumnSurfaceData(const StructureEnum::Enum& structure) const { return hasSurfaceData(ALONG_COLUMN, structure); } bool CiftiXMLOld::hasRowSurfaceData(const StructureEnum::Enum& structure) const { return hasSurfaceData(ALONG_ROW, structure); } bool CiftiXMLOld::hasSurfaceData(const int& direction, const StructureEnum::Enum& structure) const { if (direction < 0 || direction >= (int)m_dimToMapLookup.size()) { return false; } int myMapIndex = m_dimToMapLookup[direction]; if (myMapIndex == -1 || m_root.m_matrices.size() == 0) { return false; } CaretAssertVectorIndex(m_root.m_matrices[0].m_matrixIndicesMap, myMapIndex); const CiftiMatrixIndicesMapElement* myMap = &(m_root.m_matrices[0].m_matrixIndicesMap[myMapIndex]); if (myMap->m_indicesMapToDataType == CIFTI_INDEX_TYPE_BRAIN_MODELS) { return (findSurfaceModel(m_dimToMapLookup[direction], structure) != NULL); } else if (myMap->m_indicesMapToDataType == CIFTI_INDEX_TYPE_PARCELS) { bool found = false; for (int i = 0; i < (int)myMap->m_parcelSurfaces.size(); ++i) { if (myMap->m_parcelSurfaces[i].m_structure == structure) { found = true;//TODO: figure out if we should just return true here } } if (!found) return false; for (int64_t i = 0; i < (int64_t)myMap->m_parcels.size(); ++i) { const CiftiParcelElement& myParcel = myMap->m_parcels[i]; for (int j = 0; j < (int)myParcel.m_nodeElements.size(); ++j) { const CiftiParcelNodesElement& myNodes = myParcel.m_nodeElements[j]; if (myNodes.m_structure == structure && myNodes.m_nodes.size() != 0) return true;//instead of checking that at least one parcel actually uses it } } return false; } else { return false; } } bool CiftiXMLOld::addSurfaceModelToColumns(const int& numberOfNodes, const StructureEnum::Enum& structure, const float* roi) { return addSurfaceModel(ALONG_COLUMN, numberOfNodes, structure, roi); } bool CiftiXMLOld::addSurfaceModelToRows(const int& numberOfNodes, const StructureEnum::Enum& structure, const float* roi) { return addSurfaceModel(ALONG_ROW, numberOfNodes, structure, roi); } bool CiftiXMLOld::addSurfaceModel(const int& direction, const int& numberOfNodes, const StructureEnum::Enum& structure, const float* roi) { CaretAssertVectorIndex(m_dimToMapLookup, direction); separateMaps(); if (m_dimToMapLookup[direction] == -1 || m_root.m_matrices.size() == 0) { return false; } CaretAssertVectorIndex(m_root.m_matrices[0].m_matrixIndicesMap, m_dimToMapLookup[direction]); CiftiMatrixIndicesMapElement* myMap = &(m_root.m_matrices[0].m_matrixIndicesMap[m_dimToMapLookup[direction]]); if (myMap->m_indicesMapToDataType != CIFTI_INDEX_TYPE_BRAIN_MODELS) return false; if (findSurfaceModel(m_dimToMapLookup[direction], structure) != NULL) return false; CiftiBrainModelElement tempModel; tempModel.m_brainStructure = structure; tempModel.m_modelType = CIFTI_MODEL_TYPE_SURFACE; tempModel.m_indexOffset = getNewRangeStart(m_dimToMapLookup[direction]); tempModel.m_surfaceNumberOfNodes = numberOfNodes; if (roi == NULL) { tempModel.m_indexCount = numberOfNodes; } else { tempModel.m_indexCount = 0; tempModel.m_nodeIndices.reserve(numberOfNodes); bool allNodes = true; for (int i = 0; i < numberOfNodes; ++i) { if (roi[i] > 0.0f) { tempModel.m_nodeIndices.push_back(i); } else { allNodes = false; } } if (allNodes) { tempModel.m_nodeIndices.clear(); tempModel.m_indexCount = numberOfNodes; } else { tempModel.m_indexCount = (unsigned long long)tempModel.m_nodeIndices.size(); } } myMap->m_brainModels.push_back(tempModel); myMap->m_brainModels.back().setupLookup(*myMap); return true; } bool CiftiXMLOld::addSurfaceModelToColumns(const int& numberOfNodes, const StructureEnum::Enum& structure, const vector& nodeList) { return addSurfaceModel(ALONG_COLUMN, numberOfNodes, structure, nodeList); } bool CiftiXMLOld::addSurfaceModelToRows(const int& numberOfNodes, const StructureEnum::Enum& structure, const vector& nodeList) { return addSurfaceModel(ALONG_ROW, numberOfNodes, structure, nodeList); } bool CiftiXMLOld::addSurfaceModel(const int& direction, const int& numberOfNodes, const StructureEnum::Enum& structure, const vector& nodeList) { CaretAssertVectorIndex(m_dimToMapLookup, direction); separateMaps(); if (m_dimToMapLookup[direction] == -1 || m_root.m_matrices.size() == 0) { return false; } CaretAssertVectorIndex(m_root.m_matrices[0].m_matrixIndicesMap, m_dimToMapLookup[direction]); CaretAssertMessage(checkSurfaceNodes(nodeList, numberOfNodes), "node list has node numbers that don't exist in the surface"); CiftiMatrixIndicesMapElement* myMap = &(m_root.m_matrices[0].m_matrixIndicesMap[m_dimToMapLookup[direction]]);//call the check function inside an assert so it never does the check in release builds if (myMap->m_indicesMapToDataType != CIFTI_INDEX_TYPE_BRAIN_MODELS) return false; if (findSurfaceModel(m_dimToMapLookup[direction], structure) != NULL) return false; CiftiBrainModelElement tempModel; tempModel.m_brainStructure = structure; tempModel.m_modelType = CIFTI_MODEL_TYPE_SURFACE; tempModel.m_indexOffset = getNewRangeStart(m_dimToMapLookup[direction]); tempModel.m_surfaceNumberOfNodes = numberOfNodes; tempModel.m_indexCount = (int64_t)nodeList.size(); if ((int)nodeList.size() == numberOfNodes) { bool sequential = true; for (int i = 0; i < numberOfNodes; ++i) { if (nodeList[i] != i) { sequential = false; break; } } if (!sequential) { tempModel.m_nodeIndices = nodeList; } } else { tempModel.m_nodeIndices = nodeList; } myMap->m_brainModels.push_back(tempModel); myMap->m_brainModels.back().setupLookup(*myMap); return true; } bool CiftiXMLOld::checkSurfaceNodes(const vector& nodeList, const int& numberOfNodes) const { int listSize = (int)nodeList.size(); for (int i = 0; i < listSize; ++i) { if (nodeList[i] < 0 || nodeList[i] >= numberOfNodes) return false; } return true; } bool CiftiXMLOld::addVolumeModel(const int& direction, const vector& ijkList, const StructureEnum::Enum& structure) { CaretAssertVectorIndex(m_dimToMapLookup, direction); separateMaps(); if (m_dimToMapLookup[direction] == -1 || m_root.m_matrices.size() == 0) { return false; } CaretAssertVectorIndex(m_root.m_matrices[0].m_matrixIndicesMap, m_dimToMapLookup[direction]); CiftiMatrixIndicesMapElement* myMap = &(m_root.m_matrices[0].m_matrixIndicesMap[m_dimToMapLookup[direction]]); if (myMap->m_indicesMapToDataType != CIFTI_INDEX_TYPE_BRAIN_MODELS) return false; if (findVolumeModel(m_dimToMapLookup[direction], structure) != NULL) return false; CaretAssertMessage(checkVolumeIndices(ijkList), "volume voxel list doesn't match cifti volume space, do setVolumeDimsAndSForm first"); CiftiBrainModelElement tempModel;//call the check function inside an assert so it never does the check in release builds tempModel.m_brainStructure = structure; tempModel.m_modelType = CIFTI_MODEL_TYPE_VOXELS; tempModel.m_indexOffset = getNewRangeStart(m_dimToMapLookup[direction]); tempModel.m_indexCount = ijkList.size() / 3; tempModel.m_voxelIndicesIJK = ijkList; myMap->m_brainModels.push_back(tempModel); return true; } bool CiftiXMLOld::addVolumeModelToColumns(const vector& ijkList, const StructureEnum::Enum& structure) { return addVolumeModel(ALONG_COLUMN, ijkList, structure); } bool CiftiXMLOld::addVolumeModelToRows(const vector& ijkList, const StructureEnum::Enum& structure) { return addVolumeModel(ALONG_ROW, ijkList, structure); } bool CiftiXMLOld::addParcelSurfaceToColumns(const int& numberOfNodes, const StructureEnum::Enum& structure) { return addParcelSurface(ALONG_COLUMN, numberOfNodes, structure); } bool CiftiXMLOld::addParcelSurfaceToRows(const int& numberOfNodes, const StructureEnum::Enum& structure) { return addParcelSurface(ALONG_ROW, numberOfNodes, structure); } bool CiftiXMLOld::addParcelSurface(const int& direction, const int& numberOfNodes, const caret::StructureEnum::Enum& structure) { if (direction < 0 || direction >= (int)m_dimToMapLookup.size()) { return false; } separateMaps(); if (numberOfNodes < 1 || m_dimToMapLookup[direction] == -1 || m_root.m_matrices.size() == 0) { return false; } CaretAssertVectorIndex(m_root.m_matrices[0].m_matrixIndicesMap, m_dimToMapLookup[direction]); CiftiMatrixIndicesMapElement& myMap = m_root.m_matrices[0].m_matrixIndicesMap[m_dimToMapLookup[direction]]; if (myMap.m_indicesMapToDataType != CIFTI_INDEX_TYPE_PARCELS) return false; CiftiParcelSurfaceElement tempSurf; tempSurf.m_numNodes = numberOfNodes; tempSurf.m_structure = structure; myMap.m_parcelSurfaces.push_back(tempSurf); myMap.setupLookup();//TODO: make the lookup maintenance incremental return true; } bool CiftiXMLOld::addParcelToColumns(const CiftiParcelElement& parcel) { return addParcel(ALONG_COLUMN, parcel); } bool CiftiXMLOld::addParcelToRows(const caret::CiftiParcelElement& parcel) { return addParcel(ALONG_ROW, parcel); } bool CiftiXMLOld::addParcel(const int& direction, const CiftiParcelElement& parcel) { if (direction < 0 || direction >= (int)m_dimToMapLookup.size()) { return false; } separateMaps(); if (m_dimToMapLookup[direction] == -1 || m_root.m_matrices.size() == 0) { return false; } CaretAssertVectorIndex(m_root.m_matrices[0].m_matrixIndicesMap, m_dimToMapLookup[direction]); CiftiMatrixIndicesMapElement& myMap = m_root.m_matrices[0].m_matrixIndicesMap[m_dimToMapLookup[direction]]; if (myMap.m_indicesMapToDataType != CIFTI_INDEX_TYPE_PARCELS) return false; if (!checkVolumeIndices(parcel.m_voxelIndicesIJK)) return false; myMap.m_parcels.push_back(parcel);//NOTE: setupLookup does error checking for nodes try { myMap.setupLookup();//TODO: make the lookup maintenance incremental, decide on throw vs bool return, separate sanity checking? } catch (...) { return false; } return true; } bool CiftiXMLOld::checkVolumeIndices(const vector& ijkList) const { int64_t listSize = (int64_t)ijkList.size(); if (listSize == 0) return true; if (listSize % 3 != 0) return false; int64_t dims[3]; vector > sform;//not used, but needed by the funciton if (!getVolumeDimsAndSForm(dims, sform)) return false; for (int i = 0; i < listSize; i += 3) { if (ijkList[i] < 0 || ijkList[i] >= dims[0]) return false; if (ijkList[i + 1] < 0 || ijkList[i + 1] >= dims[1]) return false; if (ijkList[i + 2] < 0 || ijkList[i + 2] >= dims[2]) return false; } return true; } void CiftiXMLOld::applyColumnMapToRows() { if (m_dimToMapLookup[0] == m_dimToMapLookup[1]) return; applyDimensionHelper(1, 0); } void CiftiXMLOld::applyRowMapToColumns() { if (m_dimToMapLookup[0] == m_dimToMapLookup[1]) return; applyDimensionHelper(0, 1); } void CiftiXMLOld::applyDimensionHelper(const int& from, const int& to) { if (m_root.m_matrices.size() == 0) return; CiftiMatrixElement& myMatrix = m_root.m_matrices[0];//assume only one matrix int numMaps = (int)myMatrix.m_matrixIndicesMap.size(); for (int i = 0; i < numMaps; ++i) { CiftiMatrixIndicesMapElement& myMap = myMatrix.m_matrixIndicesMap[i]; int numDimensions = (int)myMap.m_appliesToMatrixDimension.size(); for (int j = 0; j < numDimensions; ++j) { if (myMap.m_appliesToMatrixDimension[j] == to) { myMap.m_appliesToMatrixDimension.erase(myMap.m_appliesToMatrixDimension.begin() + j); --numDimensions; --j; break; } } for (int j = 0; j < numDimensions; ++j) { if (myMap.m_appliesToMatrixDimension[j] == from) { myMap.m_appliesToMatrixDimension.push_back(to); break; } } if (myMap.m_appliesToMatrixDimension.size() == 0) { myMatrix.m_matrixIndicesMap.erase(myMatrix.m_matrixIndicesMap.begin() + i); for (int j = 0; j < (int)m_dimToMapLookup.size(); ++j) { if (m_dimToMapLookup[j] > i) --m_dimToMapLookup[j]; } --numMaps; --i;//make sure we don't skip a map due to an erase } } m_dimToMapLookup[to] = m_dimToMapLookup[from]; } void CiftiXMLOld::resetColumnsToBrainModels() { resetDirectionToBrainModels(ALONG_COLUMN); } void CiftiXMLOld::resetRowsToBrainModels() { resetDirectionToBrainModels(ALONG_ROW); } void CiftiXMLOld::resetDirectionToBrainModels(const int& direction) { if (m_dimToMapLookup[direction] == -1) { m_dimToMapLookup[direction] = createMap(direction); } else { separateMaps(); } CiftiMatrixIndicesMapElement myMap; myMap.m_appliesToMatrixDimension.push_back(direction); myMap.m_indicesMapToDataType = CIFTI_INDEX_TYPE_BRAIN_MODELS; m_root.m_matrices[0].m_matrixIndicesMap[m_dimToMapLookup[direction]] = myMap; } void CiftiXMLOld::resetColumnsToTimepoints(const float& timestep, const int& timepoints, const float& timestart) { if (m_dimToMapLookup[1] == -1) { m_dimToMapLookup[1] = createMap(1); } else { separateMaps(); } CiftiMatrixIndicesMapElement myMap; myMap.m_appliesToMatrixDimension.push_back(1); myMap.m_indicesMapToDataType = CIFTI_INDEX_TYPE_TIME_POINTS; myMap.m_timeStepUnits = NIFTI_UNITS_SEC; myMap.m_timeStep = timestep; myMap.m_timeStart = timestart; myMap.m_hasTimeStart = true; myMap.m_numTimeSteps = timepoints; m_root.m_matrices[0].m_matrixIndicesMap[m_dimToMapLookup[1]] = myMap; } void CiftiXMLOld::resetRowsToTimepoints(const float& timestep, const int& timepoints, const float& timestart) { if (m_dimToMapLookup[0] == -1) { m_dimToMapLookup[0] = createMap(0); } else { separateMaps(); } CiftiMatrixIndicesMapElement myMap; myMap.m_appliesToMatrixDimension.push_back(0); myMap.m_indicesMapToDataType = CIFTI_INDEX_TYPE_TIME_POINTS; myMap.m_timeStepUnits = NIFTI_UNITS_SEC; myMap.m_timeStep = timestep; myMap.m_timeStart = timestart; myMap.m_hasTimeStart = true; myMap.m_numTimeSteps = timepoints; m_root.m_matrices[0].m_matrixIndicesMap[m_dimToMapLookup[0]] = myMap; } void CiftiXMLOld::resetColumnsToScalars(const int64_t& numMaps) { resetDirectionToScalars(ALONG_COLUMN, numMaps); } void CiftiXMLOld::resetRowsToScalars(const int64_t& numMaps) { resetDirectionToScalars(ALONG_ROW, numMaps); } void CiftiXMLOld::resetDirectionToScalars(const int& direction, const int64_t& numMaps) { CaretAssertVectorIndex(m_dimToMapLookup, direction); if (m_dimToMapLookup[direction] == -1) { m_dimToMapLookup[direction] = createMap(direction); } else { separateMaps(); } CiftiMatrixIndicesMapElement myMap; myMap.m_appliesToMatrixDimension.push_back(direction); myMap.m_indicesMapToDataType = CIFTI_INDEX_TYPE_SCALARS; myMap.m_namedMaps.resize(numMaps); m_root.m_matrices[0].m_matrixIndicesMap[m_dimToMapLookup[direction]] = myMap; } void CiftiXMLOld::resetColumnsToLabels(const int64_t& numMaps) { resetDirectionToLabels(ALONG_COLUMN, numMaps); } void CiftiXMLOld::resetRowsToLabels(const int64_t& numMaps) { resetDirectionToLabels(ALONG_ROW, numMaps); } void CiftiXMLOld::resetDirectionToLabels(const int& direction, const int64_t& numMaps) { CaretAssertVectorIndex(m_dimToMapLookup, direction); if (m_dimToMapLookup[direction] == -1) { m_dimToMapLookup[direction] = createMap(direction); } else { separateMaps(); } CiftiMatrixIndicesMapElement myMap; myMap.m_appliesToMatrixDimension.push_back(direction); myMap.m_indicesMapToDataType = CIFTI_INDEX_TYPE_LABELS; myMap.m_namedMaps.resize(numMaps); for (int64_t i = 0; i < numMaps; ++i) { myMap.m_namedMaps[i].m_labelTable.grabNew(new GiftiLabelTable()); } m_root.m_matrices[0].m_matrixIndicesMap[m_dimToMapLookup[direction]] = myMap; } void CiftiXMLOld::resetColumnsToParcels() { resetDirectionToParcels(ALONG_COLUMN); } void CiftiXMLOld::resetRowsToParcels() { resetDirectionToParcels(ALONG_ROW); } void CiftiXMLOld::resetDirectionToParcels(const int& direction) { CaretAssertVectorIndex(m_dimToMapLookup, direction); if (m_dimToMapLookup[direction] == -1) { m_dimToMapLookup[direction] = createMap(direction); } else { separateMaps(); } CiftiMatrixIndicesMapElement myMap; myMap.m_appliesToMatrixDimension.push_back(direction); myMap.m_indicesMapToDataType = CIFTI_INDEX_TYPE_PARCELS; m_root.m_matrices[0].m_matrixIndicesMap[m_dimToMapLookup[direction]] = myMap; } int CiftiXMLOld::createMap(int dimension) { CiftiMatrixIndicesMapElement tempMap; tempMap.m_appliesToMatrixDimension.push_back(dimension); if (m_root.m_matrices.size() == 0) { m_root.m_matrices.resize(1); m_root.m_numberOfMatrices = 1;//TODO: remove this variable } CiftiMatrixElement& myMatrix = m_root.m_matrices[0];//assume only one matrix myMatrix.m_matrixIndicesMap.push_back(tempMap); return myMatrix.m_matrixIndicesMap.size() - 1; } void CiftiXMLOld::separateMaps() { if (m_root.m_matrices.size() == 0) return; CiftiMatrixElement& myMatrix = m_root.m_matrices[0];//assume only one matrix int numMaps = (int)myMatrix.m_matrixIndicesMap.size(); for (int i = 0; i < numMaps; ++i)//don't need to loop over newly created maps { CiftiMatrixIndicesMapElement myMap = myMatrix.m_matrixIndicesMap[i];//make a copy because we are modifying this vector int numDimensions = (int)myMap.m_appliesToMatrixDimension.size(); for (int j = 1; j < numDimensions; ++j)//leave the first in place { int whichDim = myMap.m_appliesToMatrixDimension[j]; myMatrix.m_matrixIndicesMap.push_back(myMap); myMatrix.m_matrixIndicesMap.back().m_appliesToMatrixDimension.resize(1); myMatrix.m_matrixIndicesMap.back().m_appliesToMatrixDimension[0] = whichDim; m_dimToMapLookup[whichDim] = myMatrix.m_matrixIndicesMap.size() - 1; } myMatrix.m_matrixIndicesMap[i].m_appliesToMatrixDimension.resize(1);//ditch all but the first, they have their own maps } } int64_t CiftiXMLOld::getNewRangeStart(const int& myMapIndex) const { if (myMapIndex == -1 || m_root.m_matrices.size() == 0) { CaretAssert(false); } CaretAssertVectorIndex(m_root.m_matrices[0].m_matrixIndicesMap, myMapIndex); const CiftiMatrixIndicesMapElement* myMap = &(m_root.m_matrices[0].m_matrixIndicesMap[myMapIndex]); CaretAssert(myMap != NULL && myMap->m_indicesMapToDataType == CIFTI_INDEX_TYPE_BRAIN_MODELS); int numModels = (int)myMap->m_brainModels.size(); int64_t curRet = 0; for (int i = 0; i < numModels; ++i) { int64_t thisEnd = myMap->m_brainModels[i].m_indexOffset + myMap->m_brainModels[i].m_indexCount; if (thisEnd > curRet) { curRet = thisEnd; } } return curRet; } int64_t CiftiXMLOld::getDimensionLength(const int& direction) const { if (direction < 0 || direction >= (int)m_dimToMapLookup.size()) { throw DataFileException("getDimensionLength called on nonexistant dimension"); } int myMapIndex = m_dimToMapLookup[direction]; if (myMapIndex == -1 || m_root.m_matrices.size() == 0) { throw DataFileException("getDimensionLength called on nonexistant dimension"); } CaretAssertVectorIndex(m_root.m_matrices[0].m_matrixIndicesMap, myMapIndex); const CiftiMatrixIndicesMapElement* myMap = &(m_root.m_matrices[0].m_matrixIndicesMap[myMapIndex]); if (myMap->m_indicesMapToDataType == CIFTI_INDEX_TYPE_TIME_POINTS) { return myMap->m_numTimeSteps; } else if (myMap->m_indicesMapToDataType == CIFTI_INDEX_TYPE_BRAIN_MODELS) { return getNewRangeStart(m_dimToMapLookup[direction]); } else if (myMap->m_indicesMapToDataType == CIFTI_INDEX_TYPE_SCALARS || myMap->m_indicesMapToDataType == CIFTI_INDEX_TYPE_LABELS) { return myMap->m_namedMaps.size(); } else if (myMap->m_indicesMapToDataType == CIFTI_INDEX_TYPE_PARCELS) { return myMap->m_parcels.size(); } else { throw DataFileException("unknown cifti mapping type"); } } int64_t CiftiXMLOld::getNumberOfColumns() const {//number of columns is LENGTH OF A ROW return getDimensionLength(ALONG_ROW); } int64_t CiftiXMLOld::getNumberOfRows() const { return getDimensionLength(ALONG_COLUMN); } IndicesMapToDataType CiftiXMLOld::getColumnMappingType() const { if (m_dimToMapLookup[1] == -1 || m_root.m_matrices.size() == 0) { return CIFTI_INDEX_TYPE_INVALID; } CaretAssertVectorIndex(m_root.m_matrices[0].m_matrixIndicesMap, m_dimToMapLookup[1]); const CiftiMatrixIndicesMapElement* myMap = &(m_root.m_matrices[0].m_matrixIndicesMap[m_dimToMapLookup[1]]); return myMap->m_indicesMapToDataType; } IndicesMapToDataType CiftiXMLOld::getRowMappingType() const { if (m_dimToMapLookup[0] == -1 || m_root.m_matrices.size() == 0) { return CIFTI_INDEX_TYPE_INVALID; } CaretAssertVectorIndex(m_root.m_matrices[0].m_matrixIndicesMap, m_dimToMapLookup[0]); const CiftiMatrixIndicesMapElement* myMap = &(m_root.m_matrices[0].m_matrixIndicesMap[m_dimToMapLookup[0]]); return myMap->m_indicesMapToDataType; } IndicesMapToDataType CiftiXMLOld::getMappingType(const int& direction) const { if (direction < 0 || direction >= (int)m_dimToMapLookup.size()) { CaretAssertMessage(false, "CiftiXML::getMappingType called with invalid direction"); return CIFTI_INDEX_TYPE_INVALID; } if (m_dimToMapLookup[direction] == -1 || m_root.m_matrices.size() == 0) { return CIFTI_INDEX_TYPE_INVALID; } CaretAssertVectorIndex(m_root.m_matrices[0].m_matrixIndicesMap, m_dimToMapLookup[direction]); const CiftiMatrixIndicesMapElement* myMap = &(m_root.m_matrices[0].m_matrixIndicesMap[m_dimToMapLookup[direction]]); return myMap->m_indicesMapToDataType; } const CiftiBrainModelElement* CiftiXMLOld::findSurfaceModel(const int& myMapIndex, const StructureEnum::Enum& structure) const { if (myMapIndex == -1 || m_root.m_matrices.size() == 0) { return NULL; } CaretAssertVectorIndex(m_root.m_matrices[0].m_matrixIndicesMap, myMapIndex); const CiftiMatrixIndicesMapElement* myMap = &(m_root.m_matrices[0].m_matrixIndicesMap[myMapIndex]); if (myMap->m_indicesMapToDataType != CIFTI_INDEX_TYPE_BRAIN_MODELS) return NULL; const vector& myModels = myMap->m_brainModels; int numModels = myModels.size(); for (int i = 0; i < numModels; ++i) { if (myModels[i].m_brainStructure == structure && myModels[i].m_modelType == CIFTI_MODEL_TYPE_SURFACE) { return &(myModels[i]); } } return NULL; } const CiftiBrainModelElement* CiftiXMLOld::findVolumeModel(const int& myMapIndex, const StructureEnum::Enum& structure) const { if (myMapIndex == -1 || m_root.m_matrices.size() == 0) { return NULL; } CaretAssertVectorIndex(m_root.m_matrices[0].m_matrixIndicesMap, myMapIndex); const CiftiMatrixIndicesMapElement* myMap = &(m_root.m_matrices[0].m_matrixIndicesMap[myMapIndex]); if (myMap->m_indicesMapToDataType != CIFTI_INDEX_TYPE_BRAIN_MODELS) return NULL; const vector& myModels = myMap->m_brainModels; int numModels = myModels.size(); for (int i = 0; i < numModels; ++i) { if (myModels[i].m_brainStructure == structure && myModels[i].m_modelType == CIFTI_MODEL_TYPE_VOXELS) { return &(myModels[i]); } } return NULL; } bool CiftiXMLOld::matchesForColumns(const CiftiXMLOld& rhs) const { return mappingMatches(ALONG_COLUMN, rhs, ALONG_COLUMN); } bool CiftiXMLOld::matchesForRows(const CiftiXMLOld& rhs) const { return mappingMatches(ALONG_ROW, rhs, ALONG_ROW); } bool CiftiXMLOld::mappingMatches(const int& direction, const CiftiXMLOld& other, const int& otherDirection) const { CaretAssertVectorIndex(m_dimToMapLookup, direction); CaretAssertVectorIndex(other.m_dimToMapLookup, otherDirection); if (m_root.m_matrices.size() == 0 || m_dimToMapLookup[direction] == -1) { return (other.m_root.m_matrices.size() == 0 || other.m_dimToMapLookup[otherDirection] == -1); } CaretAssertVectorIndex(m_root.m_matrices[0].m_matrixIndicesMap, m_dimToMapLookup[direction]); CaretAssertVectorIndex(other.m_root.m_matrices[0].m_matrixIndicesMap, other.m_dimToMapLookup[otherDirection]); if (hasVolumeData(direction) && !(other.hasVolumeData(otherDirection) && matchesVolumeSpace(other))) return false; return (m_root.m_matrices[0].m_matrixIndicesMap[m_dimToMapLookup[direction]] == other.m_root.m_matrices[0].m_matrixIndicesMap[other.m_dimToMapLookup[otherDirection]]); } void CiftiXMLOld::copyMapping(const int& direction, const CiftiXMLOld& other, const int& otherDirection) { CaretAssert(direction > -1 && otherDirection > -1); if ((int)other.m_dimToMapLookup.size() <= otherDirection || other.m_dimToMapLookup[otherDirection] == -1) { throw DataFileException("copyMapping called with nonexistant mapping to copy"); } bool copyVolSpace = false, haveVoxels = false; for (int i = 0; i < (int)m_dimToMapLookup.size(); ++i) { if (i != direction && hasVolumeData(i)) { haveVoxels = true; } } if (other.hasVolumeData(otherDirection)) { if (haveVoxels) { if (!matchesVolumeSpace(other)) throw DataFileException("cannot copy mapping from other cifti due to volume space mismatch"); } else { copyVolSpace = true; } } else { if (!haveVoxels) { m_root.m_matrices[0].m_volume.clear(); } } if (m_dimToMapLookup[direction] == -1) { m_dimToMapLookup[direction] = createMap(direction); } else { separateMaps(); } if (copyVolSpace) {//we have checked that this is okay because if we have any voxel data, it is in the map that is about to be replaced m_root.m_matrices[0].m_volume = other.m_root.m_matrices[0].m_volume; } CiftiMatrixIndicesMapElement& myMap = m_root.m_matrices[0].m_matrixIndicesMap[m_dimToMapLookup[direction]]; myMap = other.m_root.m_matrices[0].m_matrixIndicesMap[other.m_dimToMapLookup[otherDirection]]; myMap.m_appliesToMatrixDimension.clear(); myMap.m_appliesToMatrixDimension.push_back(direction);//the member lookups should already be valid, copy works } map* CiftiXMLOld::getMapMetadata(const int& direction, const int& index) const { CaretAssertVectorIndex(m_dimToMapLookup, direction); int myMapIndex = m_dimToMapLookup[direction]; if (myMapIndex == -1 || m_root.m_matrices.size() == 0) { return NULL; } CaretAssertVectorIndex(m_root.m_matrices[0].m_matrixIndicesMap, myMapIndex); const CiftiMatrixIndicesMapElement& myMap = m_root.m_matrices[0].m_matrixIndicesMap[myMapIndex]; if (myMap.m_indicesMapToDataType != CIFTI_INDEX_TYPE_LABELS && myMap.m_indicesMapToDataType != CIFTI_INDEX_TYPE_SCALARS) { return NULL; } CaretAssertVectorIndex(myMap.m_namedMaps, index); return &(myMap.m_namedMaps[index].m_mapMetaData); } PaletteColorMapping* CiftiXMLOld::getFilePalette() const { if (m_root.m_matrices.size() == 0) return NULL; if (m_root.m_matrices[0].m_palette == NULL) {//load from metadata m_root.m_matrices[0].m_palette.grabNew(new PaletteColorMapping()); map::const_iterator myIter = m_root.m_matrices[0].m_userMetaData.find("PaletteColorMapping"); if (myIter != m_root.m_matrices[0].m_userMetaData.end()) { m_root.m_matrices[0].m_palette->decodeFromStringXML(myIter->second); } /*} else {//will be needed if we make default palettes a user preference if (m_root.m_matrices[0].m_defaultPalette) {//TODO: load the current defaults into the existing palette, find some way of only doing this if the defaults were modified since last time this was called }//*/ } return m_root.m_matrices[0].m_palette.getPointer(); } PaletteColorMapping* CiftiXMLOld::getMapPalette(const int& direction, const int& index) const { CaretAssertVectorIndex(m_dimToMapLookup, direction); int myMapIndex = m_dimToMapLookup[direction]; if (myMapIndex == -1 || m_root.m_matrices.size() == 0) { return NULL; } CaretAssertVectorIndex(m_root.m_matrices[0].m_matrixIndicesMap, myMapIndex); const CiftiMatrixIndicesMapElement& myMap = m_root.m_matrices[0].m_matrixIndicesMap[myMapIndex]; if (myMap.m_indicesMapToDataType != CIFTI_INDEX_TYPE_SCALARS) { return NULL; } CaretAssertVectorIndex(myMap.m_namedMaps, index); const CiftiNamedMapElement& myElem = myMap.m_namedMaps[index]; if (myElem.m_palette == NULL) {//load from metadata myElem.m_palette.grabNew(new PaletteColorMapping()); map::const_iterator myIter = myElem.m_mapMetaData.find("PaletteColorMapping"); if (myIter != myElem.m_mapMetaData.end()) { myElem.m_palette->decodeFromStringXML(myIter->second); } /*} else {//will be needed if we make default palettes a user preference if (m_root.m_matrices[0].m_defaultPalette) {//TODO: load the current defaults into the existing palette, find some way of only doing this if the defaults were modified since last time this was called }//*/ } return myElem.m_palette; } bool CiftiXMLOld::operator==(const caret::CiftiXMLOld& rhs) const { if (this == &rhs) return true;//compare pointers to skip checking object against itself if (m_root.m_matrices.size() != 1 || m_root.m_matrices[0].m_matrixIndicesMap.size() != 2) { throw DataFileException("old CIFTI XML implementation only supports single-matrix, 2D cifti"); } if (m_root.m_matrices.size() != rhs.m_root.m_matrices.size()) return false; if (m_root.m_matrices[0].m_matrixIndicesMap.size() != rhs.m_root.m_matrices[0].m_matrixIndicesMap.size()) return false; if (!matchesVolumeSpace(rhs)) return false; if (!matchesForColumns(rhs)) return false; if (!matchesForRows(rhs)) return false; return true; } bool CiftiXMLOld::matchesVolumeSpace(const CiftiXMLOld& rhs) const { if (hasColumnVolumeData() || hasRowVolumeData()) { if (!(rhs.hasColumnVolumeData() || rhs.hasRowVolumeData())) { return false; } } else { if (rhs.hasColumnVolumeData() || rhs.hasRowVolumeData()) { return false; } else { return true;//don't check for matching/existing sforms if there are no voxel maps in either } } int64_t dims[3], rdims[3]; vector > sform, rsform; if (!getVolumeDimsAndSForm(dims, sform) || !rhs.getVolumeDimsAndSForm(rdims, rsform)) {//should NEVER happen CaretAssertMessage(false, "has*VolumeData() and getVolumeDimsAndSForm() disagree"); throw DataFileException("has*VolumeData() and getVolumeDimsAndSForm() disagree"); } const float TOLER_RATIO = 0.999f;//ratio a spacing element can mismatch by for (int i = 0; i < 3; ++i) { if (dims[i] != rdims[i]) return false; for (int j = 0; j < 4; ++j) { float left = sform[i][j]; float right = rsform[i][j]; if (left != right && (left == 0.0f || right == 0.0f || left / right < TOLER_RATIO || right / left < TOLER_RATIO)) return false; } } return true; } void CiftiXMLOld::swapMappings(const int& direction1, const int& direction2) { CaretAssertVectorIndex(m_dimToMapLookup, direction1); CaretAssertVectorIndex(m_dimToMapLookup, direction2); if (direction1 < 0 || direction1 >= (int)m_dimToMapLookup.size() || direction2 < 0 || direction2 >= (int)m_dimToMapLookup.size()) { throw DataFileException("invalid direction specified to swapMappings, notify the developers"); } int mapIndex1 = m_dimToMapLookup[direction1]; int mapIndex2 = m_dimToMapLookup[direction2]; if (mapIndex1 == -1 || mapIndex2 == -1 || m_root.m_matrices.size() == 0) { throw DataFileException("invalid direction specified to swapMappings, notify the developers"); } CaretAssertVectorIndex(m_root.m_matrices[0].m_matrixIndicesMap, mapIndex1); CaretAssertVectorIndex(m_root.m_matrices[0].m_matrixIndicesMap, mapIndex2); if (mapIndex1 == mapIndex2) return;//nothing to do if they refer to the same mapping CiftiMatrixIndicesMapElement& mapRef1 = m_root.m_matrices[0].m_matrixIndicesMap[mapIndex1];//give them shorter variable names CiftiMatrixIndicesMapElement& mapRef2 = m_root.m_matrices[0].m_matrixIndicesMap[mapIndex2]; m_dimToMapLookup[direction1] = mapIndex2;//swap them by changing the lookup values m_dimToMapLookup[direction2] = mapIndex1; int numApply = (int)mapRef1.m_appliesToMatrixDimension.size(), i;//but we also need to modify the "applies to" lists for (i = 0; i < numApply; ++i) { if (mapRef1.m_appliesToMatrixDimension[i] == direction1)//we made the references from the old lookup values, so these are same { mapRef1.m_appliesToMatrixDimension[i] = direction2;//change the "applies to" element break; } } CaretAssert(i < numApply);//otherwise, we didn't find the element to modify, ie, something went horribly wrong numApply = (int)mapRef2.m_appliesToMatrixDimension.size();//and for the other mapping for (i = 0; i < numApply; ++i) { if (mapRef2.m_appliesToMatrixDimension[i] == direction2) { mapRef2.m_appliesToMatrixDimension[i] = direction1; break; } } CaretAssert(i < numApply); } connectome-workbench-1.2.3+git41-gc4c6c90/src/Cifti/CiftiXMLOld.h000066400000000000000000000626531300200146000240450ustar00rootroot00000000000000/*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*/ #ifndef __CIFTI_XML_OLD__ #define __CIFTI_XML_OLD__ #include "StructureEnum.h" #include "CiftiXMLElements.h" #include "CiftiXMLReader.h" #include "CiftiXMLWriter.h" #include "VolumeBase.h" #include "VolumeSpace.h" #include #include namespace caret { class DataFileContentInformation; /// Simple Container class for storing Cifti XML meta data struct CiftiSurfaceMap { int64_t m_ciftiIndex; int64_t m_surfaceNode; }; struct CiftiVolumeMap { int64_t m_ciftiIndex; int64_t m_ijk[3]; }; struct CiftiVolumeStructureMap { std::vector m_map; StructureEnum::Enum m_structure; }; struct CiftiBrainModelInfo { ModelType m_type; StructureEnum::Enum m_structure; CiftiBrainModelInfo() { m_type = CIFTI_MODEL_TYPE_INVALID; m_structure = StructureEnum::INVALID; } }; class CiftiXMLOld { public: enum { ALONG_ROW = 0, ALONG_COLUMN = 1, ALONG_STACK = 2//better name for this? }; /** * Default Constructor * * Default Constructor */ CiftiXMLOld(); /** * Constructor * * Constructor, create class using already existing Cifti xml tree * @param xml_root */ //CiftiXML(CiftiRootElement &xml_root) { m_root = xml_root; rootChanged(); } /** * Constructor * * Constructor, create class using ASCII formatted byte array that * containes xml meta data text * @param bytes */ CiftiXMLOld(const QByteArray &bytes) { readXML(bytes); } /** * Constructor * * Constructor, create class using QString that contains xml * meta data text * @param xml_string */ CiftiXMLOld(const QString &xml_string) { readXML(xml_string); } /** * Constructor * * Constructor, create class using QXmlStreamReader. * QXmlStreamReader is assumed to be reading from Cifti XML * Text. * @param xml_stream */ CiftiXMLOld(QXmlStreamReader &xml_stream) { readXML(xml_stream); } /** * readXML * * readXML, replacing the currently Cifti XML Root, if it exists * @param bytes an ASCII formatted byte array that contains Cifti XML data */ void readXML(const QByteArray &bytes) { QString text(bytes);readXML(text); } /** * readXML * * readXML, replacing the currently Cifti XML Root, if it exists * @param text QString that contains Cifti XML data */ void readXML(const QString &text) {QXmlStreamReader xml(text); readXML(xml); } /** * readXML * * readXML, replacing the currently Cifti XML Root, if it exists * @param xml_stream */ void readXML(QXmlStreamReader &xml_stream) { CiftiXMLReader myReader; myReader.parseCiftiXML(xml_stream,m_root); rootChanged(); } /** * writeXML * * write the Cifti XML data to the supplied QString * @param text */ void writeXML(QString &text) const { text = ""; QXmlStreamWriter xml(&text); CiftiXMLWriter myWriter; myWriter.writeCiftiXML(xml,m_root); }//we don't use the old writer in testing, so it won't recurse /** * writeXML * * write the Cifti XML data to the supplied byte array. * @param bytes */ void writeXML(QByteArray &bytes) const { bytes.clear(); QXmlStreamWriter xml(&bytes); CiftiXMLWriter myWriter; myWriter.writeCiftiXML(xml,m_root); } //static void testNewXML(const QString& xmlString); //static void testNewXML(const QByteArray& xmlString); /** * setXMLRoot * * set the Cifti XML root * @param xml_root */ //void setXMLRoot (CiftiRootElement &xml_root) { m_root = xml_root; rootChanged(); } /** * getXMLRoot * * get a copy of the Cifti XML Root * @param xml_root */ //void getXMLRoot (CiftiRootElement &xml_root) const { xml_root = m_root; } const CiftiVersion& getVersion() const { return m_root.m_version; } ///get the row index for a node, returns -1 if it doesn't find a matching mapping int64_t getRowIndexForNode(const int64_t& node, const StructureEnum::Enum& structure) const; ///get the column index for a node, returns -1 if it doesn't find a matching mapping int64_t getColumnIndexForNode(const int64_t& node, const StructureEnum::Enum& structure) const; ///get the row index for a voxel, returns -1 if it doesn't find a matching mapping int64_t getRowIndexForVoxel(const int64_t* ijk) const; ///get the column index for a voxel, returns -1 if it doesn't find a matching mapping int64_t getColumnIndexForVoxel(const int64_t* ijk) const; ///get the column index for a voxel coordinate, returns -1 if the closest indexes have no cifti data int64_t getRowIndexForVoxelCoordinate(const float* xyz) const; ///get the column index for a voxel coordinate, returns -1 if the closest indexes have no cifti data int64_t getColumnIndexForVoxelCoordinate(const float* xyz) const; ///get row index for a timepoint int64_t getRowIndexForTimepoint(const float& seconds) const; ///get row index for a timepoint int64_t getColumnIndexForTimepoint(const float& seconds) const; ///get the mapping for a surface in rows, returns false and empty vector if not found bool getSurfaceMapForRows(std::vector& mappingOut, const StructureEnum::Enum& structure) const; ///get the mapping for a surface in columns, returns false and empty vector if not found bool getSurfaceMapForColumns(std::vector& mappingOut, const StructureEnum::Enum& structure) const; ///get the mapping for a surface in columns, returns false and empty vector if not found bool getSurfaceMap(const int& direction, std::vector& mappingOut, const StructureEnum::Enum& structure) const; ///get the mapping for a volume in rows, returns false and empty vector if not found bool getVolumeMapForRows(std::vector& mappingOut) const; ///get the mapping for a volume in columns, returns false and empty vector if not found bool getVolumeMapForColumns(std::vector& mappingOut) const; ///get the mapping for a volume, returns false and empty vector if not found bool getVolumeMap(const int& direction, std::vector& mappingOut) const; void getVoxelInfoInDataFileContentInformation(const int& direction, DataFileContentInformation& dataFileInformation) const; ///get the mapping for a volume structure in rows, returns false and empty vector if not found bool getVolumeStructureMapForRows(std::vector& mappingOut, const StructureEnum::Enum& structure) const; ///get the mapping for a volume structure in columns, returns false and empty vector if not found bool getVolumeStructureMapForColumns(std::vector& mappingOut, const StructureEnum::Enum& structure) const; ///get the mapping for a volume structure bool getVolumeStructureMap(const int& direction, std::vector& mappingOut, const StructureEnum::Enum& structure) const; ///get the lists of what structures exist bool getStructureListsForRows(std::vector& surfaceList, std::vector& volumeList) const; ///get the lists of what structures exist bool getStructureListsForColumns(std::vector& surfaceList, std::vector& volumeList) const; ///get the lists of what structures exist bool getStructureLists(const int& direction, std::vector& surfaceList, std::vector& volumeList) const; ///get the number of structures for a brain model mapping int getNumberOfBrainModels(const int& direction) const; ///get structure info by structure index CiftiBrainModelInfo getBrainModelInfo(const int& direction, const int& whichModel) const; ///get the list of volume parcels and their maps in rows, returns false and empty vector if not found bool getVolumeModelMapsForRows(std::vector& mappingsOut) const; ///get the list of volume parcels and their maps in columns, returns false and empty vector if not found bool getVolumeModelMapsForColumns(std::vector& mappingsOut) const; ///get the original number of nodes of the surfaces used to make this cifti, for rows int64_t getRowSurfaceNumberOfNodes(const StructureEnum::Enum& structure) const; ///get the original number of nodes of the surfaces used to make this cifti, for columns int64_t getColumnSurfaceNumberOfNodes(const StructureEnum::Enum& structure) const; ///get the original number of nodes of the surfaces used to make this cifti along a direction int64_t getSurfaceNumberOfNodes(const int& direction, const StructureEnum::Enum& structure) const; ///get the timestep for rows, returns false if not timeseries bool getRowTimestep(float& seconds) const; ///get the timestep for columns, returns false if not timeseries bool getColumnTimestep(float& seconds) const; ///get the timestart for rows, returns false if not set or not timeseries bool getRowTimestart(float& seconds) const; ///get the timestart for columns, returns false if not set or not timeseries bool getColumnTimestart(float& seconds) const; ///get the number of timepoints for rows, returns false if not timeseries, sets -1 if unknown number of timepoints bool getRowNumberOfTimepoints(int& numTimepoints) const; ///get the number of timepoints for rows, returns false if not timeseries, sets -1 if unknown number of timepoints bool getColumnNumberOfTimepoints(int& numTimepoints) const; ///get the parcels for rows bool getParcelsForRows(std::vector& parcelsOut) const; ///get the parcels for columns bool getParcelsForColumns(std::vector& parcelsOut) const; ///get the parcels for a dimension bool getParcels(const int& direction, std::vector& parcelsOut) const; ///get the parcel surface structures bool getParcelSurfaceStructures(const int& direction, std::vector& structuresOut) const; ///get the row parcel for a node int64_t getRowParcelForNode(const int64_t& node, const StructureEnum::Enum& structure) const; ///get the column parcel for a node int64_t getColumnParcelForNode(const int64_t& node, const StructureEnum::Enum& structure) const; ///get the row parcel for a voxel int64_t getRowParcelForVoxel(const int64_t* ijk) const; ///get the row parcel for a voxel int64_t getColumnParcelForVoxel(const int64_t* ijk) const; ///set the timestep for rows, returns false if not timeseries bool setRowTimestep(const float& seconds); ///set the timestep for columns, returns false if not timeseries bool setColumnTimestep(const float& seconds); ///set the timestart for rows, returns false if not set or not timeseries bool setRowTimestart(const float& seconds); ///set the timestart for columns, returns false if not set or not timeseries bool setColumnTimestart(const float& seconds); ///set the number of timepoints for rows, returns false if not timeseries bool setRowNumberOfTimepoints(const int& numTimepoints); ///set the number of timepoints for rows, returns false if not timeseries bool setColumnNumberOfTimepoints(const int& numTimepoints); ///set rows to be brain models, and clear the list of brain models for rows void resetRowsToBrainModels(); ///set columns to be brain models, and clear the list of brain models for columns void resetColumnsToBrainModels(); ///set direction to be brain models, and clear it void resetDirectionToBrainModels(const int& direction); ///add a surface brain model to the list of brain models for rows bool addSurfaceModelToRows(const int& numberOfNodes, const StructureEnum::Enum& structure, const float* roi = NULL); ///add a surface brain model to the list of brain models for columns bool addSurfaceModelToColumns(const int& numberOfNodes, const StructureEnum::Enum& structure, const float* roi = NULL); ///add a surface brain model to the list of brain models for rows bool addSurfaceModelToRows(const int& numberOfNodes, const StructureEnum::Enum& structure, const std::vector& nodeList); ///add a surface brain model to the list of brain models for columns bool addSurfaceModelToColumns(const int& numberOfNodes, const StructureEnum::Enum& structure, const std::vector& nodeList); ///add a volume brain model to the list of brain models for rows bool addVolumeModelToRows(const std::vector& ijkList, const StructureEnum::Enum& structure); ///add a volume brain model to the list of brain models for columns bool addVolumeModelToColumns(const std::vector& ijkList, const StructureEnum::Enum& structure); ///add surface or volume model by direction bool addSurfaceModel(const int& direction, const int& numberOfNodes, const StructureEnum::Enum& structure, const float* roi = NULL); bool addSurfaceModel(const int& direction, const int& numberOfNodes, const StructureEnum::Enum& structure, const std::vector& nodeList); bool addVolumeModel(const int& direction, const std::vector& ijkList, const StructureEnum::Enum& structure); ///add a surface to the list of parcel surfaces for rows bool addParcelSurfaceToRows(const int& numberOfNodes, const StructureEnum::Enum& structure); ///add a surface to the list of parcel surfaces for columns bool addParcelSurfaceToColumns(const int& numberOfNodes, const StructureEnum::Enum& structure); ///add a surface to the list of parcel surfaces bool addParcelSurface(const int& direction, const int& numberOfNodes, const StructureEnum::Enum& structure); ///add a parcel to rows bool addParcelToRows(const CiftiParcelElement& parcel); ///add a parcel to columns bool addParcelToColumns(const CiftiParcelElement& parcel); ///add a parcel to columns bool addParcel(const int& direction, const CiftiParcelElement& parcel); ///set rows to be of type timepoints void resetRowsToTimepoints(const float& timestep, const int& numTimepoints, const float& timestart = 0.0f); ///set columns to be of type timepoints void resetColumnsToTimepoints(const float& timestep, const int& numTimepoints, const float& timestart = 0.0f); ///set rows to be of type scalars void resetRowsToScalars(const int64_t& numMaps); ///set columns to be of type scalars void resetColumnsToScalars(const int64_t& numMaps); ///set a direction to scalars void resetDirectionToScalars(const int& direction, const int64_t& numMaps); ///set rows to be of type labels void resetRowsToLabels(const int64_t& numMaps); ///set columns to be of type labels void resetColumnsToLabels(const int64_t& numMaps); ///set a direction to labels void resetDirectionToLabels(const int& direction, const int64_t& numMaps); ///set rows to be of type parcels void resetRowsToParcels(); ///set columns to be of type parcels void resetColumnsToParcels(); ///set dimension to be of type parcels void resetDirectionToParcels(const int& direction); ///get the map name for an index along a column AString getMapNameForColumnIndex(const int64_t& index) const; ///get the map name for an index along a row AString getMapNameForRowIndex(const int64_t& index) const; ///get the map name for an index AString getMapName(const int& direction, const int64_t& index) const; ///get the index for a map number/name - NOTE: returns -1 if mapping type doesn't support names int64_t getMapIndexFromNameOrNumber(const int& direction, const AString& numberOrName) const; //HACK: const method returns non-const GiftiLabelTable pointer because getCiftiXML MUST return a const CiftiXML&, but we need to be able to change the label table ///get the label table for an index along a column GiftiLabelTable* getLabelTableForColumnIndex(const int64_t& index) const; //HACK: const method returns non-const GiftiLabelTable pointer because getCiftiXML MUST return a const CiftiXML&, but we need to be able to change the label table ///get the label table for an index along a row GiftiLabelTable* getLabelTableForRowIndex(const int64_t& index) const; //HACK: const method returns non-const GiftiLabelTable pointer because getCiftiXML MUST return a const CiftiXML&, but we need to be able to change the label table ///get the label table for an index GiftiLabelTable* getMapLabelTable(const int& direction, const int64_t& myMapIndex) const; ///set the map name for an index along a column bool setMapNameForColumnIndex(const int64_t& index, const AString& name) const; ///set the map name for an index along a row bool setMapNameForRowIndex(const int64_t& index, const AString& name) const; ///set the map name for an index bool setMapNameForIndex(const int& direction, const int64_t& index, const AString& name) const; ///set the label table for an index along a column bool setLabelTableForColumnIndex(const int64_t& index, const GiftiLabelTable& labelTable); ///set the label table for an index along a row bool setLabelTableForRowIndex(const int64_t& index, const GiftiLabelTable& labelTable); ///set the column map to also apply to rows void applyColumnMapToRows(); ///set the row map to also apply to columns void applyRowMapToColumns(); ///get the number of rows (column length) int64_t getNumberOfRows() const; ///get the number of columns (row length) int64_t getNumberOfColumns() const; ///get the length of a dimension int64_t getDimensionLength(const int& direction) const; ///get what mapping type the rows use IndicesMapToDataType getRowMappingType() const; ///get what mapping type the columns use IndicesMapToDataType getColumnMappingType() const; ///get what mapping type a dimension uses IndicesMapToDataType getMappingType(const int& direction) const; ///get dimensions, spacing, origin for the volume attribute - returns false if not plumb bool getVolumeAttributesForPlumb(VolumeSpace::OrientTypes orientOut[3], int64_t dimensionsOut[3], float originOut[3], float spacingOut[3]) const; ///get dimensions and sform, useful for making a volume bool getVolumeDimsAndSForm(int64_t dimsOut[3], std::vector >& sformOut) const; ///set the volume space void setVolumeDimsAndSForm(const int64_t dims[3], const std::vector >& sform); ///get volume space object bool getVolumeSpace(VolumeSpace& volSpaceOut) const; ///swap mappings between two directions void swapMappings(const int& direction1, const int& direction2); ///check what types of data it has bool hasRowVolumeData() const; bool hasColumnVolumeData() const; bool hasVolumeData(const int& direction) const; bool hasRowSurfaceData(const StructureEnum::Enum& structure) const; bool hasColumnSurfaceData(const StructureEnum::Enum& structure) const; bool hasSurfaceData(const int& direction, const StructureEnum::Enum& structure) const; ///comparison bool mappingMatches(const int& direction, const CiftiXMLOld& other, const int& otherDirection) const; bool matchesForRows(const CiftiXMLOld& rhs) const; bool matchesForColumns(const CiftiXMLOld& rhs) const; bool matchesVolumeSpace(const CiftiXMLOld& rhs) const; bool operator==(const CiftiXMLOld& rhs) const; bool operator!=(const CiftiXMLOld& rhs) const { return !((*this) == rhs); } ///take a mapping from another xml object void copyMapping(const int& direction, const CiftiXMLOld& other, const int& otherDirection); std::map* getFileMetaData() const; std::map* getMapMetadata(const int& direction, const int& index) const; //HACK: const method returns non-const PaletteColorMapping pointer because getCiftiXML MUST return a const CiftiXML&, but we need to be able to change the palette PaletteColorMapping* getFilePalette() const; PaletteColorMapping* getMapPalette(const int& direction, const int& index) const; protected: CiftiRootElement m_root; //int m_rowMapIndex, m_colMapIndex; std::vector m_dimToMapLookup; ///updates the member variables associated with our root, should only be needed after reading from XML void rootChanged(); ///convenience functions to grab the correct model out of the tree, to replace the rowLeft/rowRight stuff (since we might have other surfaces too) const CiftiBrainModelElement* findSurfaceModel(const int& myMapIndex, const StructureEnum::Enum& structure) const; const CiftiBrainModelElement* findVolumeModel(const int& myMapIndex, const StructureEnum::Enum& structure) const; ///some boilerplate to get the correct index in a particular mapping int64_t getSurfaceIndex(const int64_t& node, const CiftiBrainModelElement* myModel) const; int64_t getVolumeIndex(const int64_t* ijk, const int& myMapIndex) const; int64_t getVolumeIndex(const float* xyz, const int& myMapIndex) const; int64_t getTimestepIndex(const float& seconds, const int& myMapIndex) const; int64_t getParcelForNode(const int64_t& node, const StructureEnum::Enum& structure, const int& myMapIndex) const; int64_t getParcelForVoxel(const int64_t* ijk, const int& myMapIndex) const; ///boilerplate for map information bool getTimestep(float& seconds, const int& myMapIndex) const; bool getTimestart(float& seconds, const int& myMapIndex) const; bool setTimestep(const float& seconds, const int& myMapIndex); bool setTimestart(const float& seconds, const int& myMapIndex); bool setLabelTable(const int64_t& index, const GiftiLabelTable& labelTable, const int& myMapIndex); ///some boilerplate to retrieve mappings bool getVolumeModelMappings(std::vector& mappingsOut, const int& myMapIndex) const; ///miscellaneous bool checkVolumeIndices(const std::vector& ijkList) const; bool checkSurfaceNodes(const std::vector& nodeList, const int& numberOfNodes) const; void applyDimensionHelper(const int& from, const int& to); int64_t getNewRangeStart(const int& myMapIndex) const; void separateMaps(); int createMap(int dimension); }; } #endif //__CIFTI_XML_OLD__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Cifti/CiftiXMLReader.cxx000066400000000000000000000771451300200146000251060ustar00rootroot00000000000000/*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 "CaretLogger.h" #include "CiftiXMLElements.h" #include "CiftiXMLReader.h" #include "DataFileException.h" #include "GiftiLabelTable.h" using namespace caret; using namespace std; void CiftiXMLReader::parseCiftiXML(QXmlStreamReader &xml, CiftiRootElement &rootElement) { while (!xml.atEnd() && !xml.hasError()) { xml.readNext(); QString test = xml.name().toString(); if(xml.isStartElement()) { QString elementName = xml.name().toString(); if(elementName == "CIFTI") { QXmlStreamAttributes attributes = xml.attributes(); if(attributes.hasAttribute("Version")) { rootElement.m_version = CiftiVersion(attributes.value("Version").toString()); m_readingVersion = rootElement.m_version; if (m_readingVersion != CiftiVersion(1, 0)) xml.raiseError("cannot read version " + m_readingVersion.toString() + " with old XML code"); } else xml.raiseError("Cifti XML Header missing Version String."); if(attributes.hasAttribute("NumberOfMatrices")) rootElement.m_numberOfMatrices = attributes.value("NumberOfMatrices").toString().toULong(); else xml.raiseError("Cifti XML Header missing number of matrices."); } else if(elementName == "Matrix") { rootElement.m_matrices.push_back(CiftiMatrixElement()); parseMatrixElement(xml,rootElement.m_matrices.back()); } else xml.raiseError("unknown element in Cifti: " + elementName); } } if(xml.hasError()) { throw DataFileException("XML error: " + xml.errorString()); } else if(!xml.atEnd()) { CaretLogWarning("Finished parsing Cifti XML without error, but not at end of XML"); } if (rootElement.m_numberOfMatrices != rootElement.m_matrices.size()) { CaretLogWarning("NumberOfMatrices does not match number of elements"); } } void CiftiXMLReader::parseMatrixElement(QXmlStreamReader &xml, CiftiMatrixElement &matrixElement) { QString test = xml.name().toString(); while (!(xml.isEndElement() && (xml.name().toString() == "Matrix")) && !xml.hasError()) {// && xml.name() == "Matrix") { xml.readNext(); QString test2 = xml.name().toString(); if(xml.isStartElement()) { QString elementName = xml.name().toString(); if(elementName == "MetaData") { parseMetaData(xml,matrixElement.m_userMetaData); } else if(elementName == "LabelTable") { parseLabelTable(xml,matrixElement.m_labelTable); } else if(elementName == "MatrixIndicesMap") { matrixElement.m_matrixIndicesMap.push_back(CiftiMatrixIndicesMapElement()); parseMatrixIndicesMap(xml,matrixElement.m_matrixIndicesMap.back()); } else if(elementName == "Volume") { matrixElement.m_volume.push_back(CiftiVolumeElement()); parseVolume(xml,matrixElement.m_volume.back()); if (xml.hasError()) return; } else xml.raiseError("unknown element in Matrix: " + elementName); } } QString test2=xml.name().toString(); //check end element if(!xml.hasError()) if(!xml.isEndElement() || (xml.name().toString() != "Matrix")) xml.raiseError("Matrix end tag not found."); } void CiftiXMLReader::parseMetaData(QXmlStreamReader &xml, map &userMetaData) { while (!(xml.isEndElement() && (xml.name().toString() == "MetaData")) && !xml.hasError()) {// && xml.name() == "MetaData") { xml.readNext(); if(xml.isStartElement()) { QString elementName = xml.name().toString(); if(elementName == "MD") { parseMetaDataElement(xml,userMetaData); } else xml.raiseError("unknown element in MetaData: " + elementName); } } //check for end element if(!xml.isEndElement() || (xml.name().toString() != "MetaData")) xml.raiseError("MetaData end tag not found."); } void CiftiXMLReader::parseMetaDataElement(QXmlStreamReader &xml, map &userMetaData) { QString name; QString value; QString test; bool haveName = false, haveValue = false; while (!(xml.isEndElement() && (xml.name().toString() == "MD")) && !xml.hasError()) { test = xml.name().toString(); xml.readNext(); if(xml.isStartElement()) { QString elementName = xml.name().toString(); if(elementName == "Name") { xml.readNext(); if(xml.tokenType() != QXmlStreamReader::Characters) { return; } name = xml.text().toString(); haveName = true; xml.readNext(); if(!xml.isEndElement()) xml.raiseError("End element for meta data name tag not found."); } else if(elementName == "Value") { xml.readNext(); if(xml.tokenType() != QXmlStreamReader::Characters) { return; } value = xml.text().toString(); haveValue = true; xml.readNext(); if(!xml.isEndElement()) xml.raiseError("End element for meta data value tag not found."); } else xml.raiseError("unknown element in MD: " + elementName); } } if (!haveName || !haveValue) xml.raiseError("MD element is missing name or value"); userMetaData[name] = value; if(!xml.isEndElement() || (xml.name().toString() != "MD")) xml.raiseError("End element for MD tag not found"); } void CiftiXMLReader::parseLabelTable(QXmlStreamReader &xml, std::vector &labelTable) { while (!(xml.isEndElement() && (xml.name().toString() == "LabelTable"))&& !xml.hasError()) {// && xml.name() == "Matrix") { xml.readNext(); if(xml.isStartElement()) { QString elementName = xml.name().toString(); if(elementName == "Label") { labelTable.push_back(CiftiLabelElement()); parseLabel(xml,labelTable.back()); } else xml.raiseError("unknown element in LabelTable: " + elementName); } } //check end element if(!xml.isEndElement() || (xml.name().toString() != "LabelTable")) { xml.raiseError("End element for label table not found."); } } void CiftiXMLReader::parseLabel(QXmlStreamReader &xml, CiftiLabelElement &label) { if(!(xml.name().toString() == "Label")) xml.raiseError("Error parsing Label\n"); QXmlStreamAttributes attributes = xml.attributes(); //get attribute values if(attributes.hasAttribute("Key")) label.m_key = attributes.value("Key").toString().toULongLong(); else xml.raiseError("Label does not contain Key value\n"); if(attributes.hasAttribute("Red")) label.m_red = attributes.value("Red").toString().toFloat(); else xml.raiseError("Label does not contain Red value\n"); if(attributes.hasAttribute("Green")) label.m_green = attributes.value("Green").toString().toFloat(); else xml.raiseError("Label does not contain Green value\n"); if(attributes.hasAttribute("Blue")) label.m_blue = attributes.value("Blue").toString().toFloat(); else xml.raiseError("Label does not contain Blue value\n"); if(attributes.hasAttribute("Alpha")) label.m_alpha = attributes.value("Alpha").toString().toFloat(); else xml.raiseError("Label does not contain Alpha value\n"); if(attributes.hasAttribute("X")) label.m_x = attributes.value("X").toString().toFloat(); else xml.raiseError("Label does not contain X value\n"); if(attributes.hasAttribute("Y")) label.m_y = attributes.value("Y").toString().toFloat(); else xml.raiseError("Label does not contain Y value\n"); if(attributes.hasAttribute("Z")) label.m_z = attributes.value("Z").toString().toFloat(); else xml.raiseError("Label does not contain Z value\n"); //get Label Text xml.readNext(); if(xml.tokenType() != QXmlStreamReader::Characters) { return; } label.m_text = xml.text().toString(); //get end element xml.readNext(); if(!xml.isEndElement()) { xml.raiseError("End element for label not found."); } } void CiftiXMLReader::parseMatrixIndicesMap(QXmlStreamReader &xml, CiftiMatrixIndicesMapElement &matrixIndicesMap) { QXmlStreamAttributes attributes = xml.attributes(); //get attribute values if(attributes.hasAttribute("AppliesToMatrixDimension")) { QStringList values = attributes.value("AppliesToMatrixDimension").toString().split(','); bool ok = false; for(int i = 0;ireadFromQXmlStreamReader(xml);//we need to do something to read through the label table, so give it to the parser always if (!needLabels)//if it shouldn't exist, drop it { CaretLogWarning("found label table in a scalar map, discarding"); namedMap.m_labelTable.grabNew(NULL); } haveLabelTable = true; } else if (xml.name() == "MetaData") { if (haveMetaData) { xml.raiseError("MetaData specified more than once in NamedMap"); break; } parseMetaData(xml, namedMap.m_mapMetaData); haveMetaData = true; } else { xml.raiseError("unknown element in NamedMap: " + xml.name().toString()); break; } } xml.readNext(); } if (!xml.hasError() && (!haveName)) { xml.raiseError("NamedMap element is missing MapName element"); } if (!xml.hasError() && !haveLabelTable && needLabels) { xml.raiseError("NamedMap element is missing LabelTable element while type is CIFTI_INDEX_TYPE_LABELS"); } if (!xml.hasError() && (!xml.isEndElement() || xml.name() != "NamedMap")) { xml.raiseError("unexpected element in NamedMap: " + xml.name().toString()); } } void CiftiXMLReader::parseParcel(QXmlStreamReader& xml, CiftiParcelElement& parcel) { QXmlStreamAttributes attributes = xml.attributes(); if (attributes.hasAttribute("Name")) { parcel.m_parcelName = attributes.value("Name").toString(); } else { xml.raiseError("Required attribute 'Name' missing from Parcel"); } xml.readNext(); while (!xml.hasError() && !(xml.isEndElement() && xml.name() == "Parcel")) { if (xml.isStartElement()) { if (xml.name() == "Nodes") { parcel.m_nodeElements.push_back(CiftiParcelNodesElement()); parseParcelNodes(xml, parcel.m_nodeElements.back()); } else if (xml.name() == "VoxelIndicesIJK") { xml.readNext(); if(xml.tokenType() == QXmlStreamReader::Characters) { QString voxelIndicesIJK = xml.text().toString(); QStringList list = voxelIndicesIJK.split(QRegExp("\\D+"),QString::SkipEmptyParts); if(list.count()%3) xml.raiseError("VoxelIndicesIJK has an incomplete triplet"); bool ok = true; for(int i = 0;i namespace caret { class CiftiXMLReader { CiftiVersion m_readingVersion; void parseMatrixElement(QXmlStreamReader &xml, CiftiMatrixElement &matrixElement); void parseMetaData(QXmlStreamReader &xml, std::map &metaData); void parseMetaDataElement(QXmlStreamReader &xml, std::map &userMetaData); void parseLabelTable(QXmlStreamReader &xml, std::vector &labelElement); void parseLabel(QXmlStreamReader &xml, CiftiLabelElement &label); void parseMatrixIndicesMap(QXmlStreamReader &xml, CiftiMatrixIndicesMapElement &matrixIndicesMap); void parseBrainModel(QXmlStreamReader &xml, CiftiBrainModelElement &brainModel); void parseNamedMap(QXmlStreamReader &xml, CiftiNamedMapElement &namedMap, const bool needLabels); void parseParcel(QXmlStreamReader &xml, CiftiParcelElement& parcel); void parseParcelNodes(QXmlStreamReader &xml, CiftiParcelNodesElement& parcelNodes); void parseVolume(QXmlStreamReader &xml, CiftiVolumeElement &volume); void parseTransformationMatrixVoxelIndicesIJKtoXYZ(QXmlStreamReader &xml, TransformationMatrixVoxelIndicesIJKtoXYZElement &transform); public: void parseCiftiXML(QXmlStreamReader &xml, CiftiRootElement &rootElement); }; } #endif //__CIFTI_XML_READER_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Cifti/CiftiXMLWriter.cxx000066400000000000000000000367321300200146000251550ustar00rootroot00000000000000/*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 "CiftiXMLWriter.h" #include "GiftiLabelTable.h" #include "PaletteColorMapping.h" using namespace caret; using namespace std; void CiftiXMLWriter::writeCiftiXML(QXmlStreamWriter &xml, const CiftiRootElement &rootElement) { m_writingVersion = CiftiVersion(1, 0);//HACK: this writer can't support other versions of XML, the structure is too closely tied to the 1.0 representation xml.setAutoFormatting(true); xml.writeStartElement("CIFTI"); xml.writeAttribute("Version", m_writingVersion.toString()); xml.writeAttribute("NumberOfMatrices",QString::number(rootElement.m_numberOfMatrices)); for(unsigned int i = 0;i metadataCopy = matrixElement.m_userMetaData;//since we may be modifying the map, we must make a copy of it if (matrixElement.m_palette != NULL) {//NULL palette means we didn't mess with palette at all if (matrixElement.m_defaultPalette && !(matrixElement.m_palette->isModified())) {//it is set to use the default palette instead of a custom palette, so remove the palette item from metadata, if it exists metadataCopy.erase("PaletteColorMapping"); } else { metadataCopy["PaletteColorMapping"] = matrixElement.m_palette->encodeInXML(); } } if(metadataCopy.size() > 0) writeMetaData(xml,metadataCopy); if(matrixElement.m_volume.size() > 0) writeVolume(xml, matrixElement.m_volume[0]); if(matrixElement.m_labelTable.size() > 0) writeLabelTable(xml, matrixElement.m_labelTable); for(unsigned int i = 0;i &metaData) { xml.writeStartElement("MetaData"); map::const_iterator i; for (i = metaData.begin(); i != metaData.end(); ++i) { writeMetaDataElement(xml,i->first,i->second); } xml.writeEndElement(); } void CiftiXMLWriter::writeMetaDataElement(QXmlStreamWriter &xml, const AString &name, const AString &value) { xml.writeStartElement("MD"); xml.writeStartElement("Name"); xml.writeCharacters(name); xml.writeEndElement();//Name xml.writeStartElement("Value"); xml.writeCharacters(value); xml.writeEndElement();//Value xml.writeEndElement();//MD } void CiftiXMLWriter::writeLabelTable(QXmlStreamWriter &xml, const std::vector &labelElement) { xml.writeStartElement("LabelTable"); for(unsigned int i=0;i0) { QString str; xml.writeAttribute("TimeStep",str.sprintf("%.10f",matrixIndicesMap.m_timeStep)); if (matrixIndicesMap.m_hasTimeStart) xml.writeAttribute("TimeStart",str.sprintf("%.10f",matrixIndicesMap.m_timeStart)); xml.writeAttribute("TimeStepUnits",timeStepUnits); } if(matrixIndicesMap.m_appliesToMatrixDimension.size()) { int lastElement = matrixIndicesMap.m_appliesToMatrixDimension.size() -1; QString appliesToMatrixDimension, str; for(int i = 0;i &ind = brainModel.m_voxelIndicesIJK; unsigned long long lastVoxelIndex = ind.size(); if(lastVoxelIndex) { xml.writeStartElement("VoxelIndicesIJK"); QString line( "%1 %2 %3\n"); if((lastVoxelIndex%3)) { std::cout << "Error writing BrainModel, invalid number of voxel indices:" << lastVoxelIndex << std::endl; return;//TODO throw exception } //else //std::cout << "voxel indices ok:" << lastVoxelIndex<< std::endl; for(unsigned int i = 0;i < lastVoxelIndex;i+=3) { xml.writeCharacters(line.arg(QString::number(ind[i]),QString::number(ind[i+1]),QString::number(ind[i+2]))); } xml.writeEndElement();//voxelIndicesIJK } xml.writeEndElement(); } void CiftiXMLWriter::writeNamedMap(QXmlStreamWriter& xml, const CiftiNamedMapElement& namedMap) { xml.writeStartElement("NamedMap"); xml.writeStartElement("MapName"); xml.writeCharacters(namedMap.m_mapName); xml.writeEndElement(); if (namedMap.m_labelTable != NULL) { namedMap.m_labelTable->writeAsXML(xml); } map metadataCopy = namedMap.m_mapMetaData;//make a copy because we may need to modify it to integrate palette if (namedMap.m_palette != NULL) {//NULL palette means we didn't mess with palette at all, leave metadata unchanged if (namedMap.m_defaultPalette && !(namedMap.m_palette->isModified())) {//it is set to use the default palette instead of a custom palette, so remove the palette item from metadata, if it exists metadataCopy.erase("PaletteColorMapping"); } else { metadataCopy["PaletteColorMapping"] = namedMap.m_palette->encodeInXML(); } } if (metadataCopy.size() != 0) { writeMetaData(xml, metadataCopy); } xml.writeEndElement(); } void CiftiXMLWriter::writeParcel(QXmlStreamWriter& xml, const CiftiParcelElement& parcel) { xml.writeStartElement("Parcel"); xml.writeAttribute("Name", parcel.m_parcelName); int numNodeElements = (int)parcel.m_nodeElements.size(); for (int i = 0; i < numNodeElements; ++i) { writeParcelNodes(xml, parcel.m_nodeElements[i]); } int numVoxInds = (int)parcel.m_voxelIndicesIJK.size(); if (numVoxInds > 0) { xml.writeStartElement("VoxelIndicesIJK"); xml.writeCharacters(AString::number(parcel.m_voxelIndicesIJK[0])); int state = 0; for (int i = 1; i < numVoxInds; ++i) { if (state >= 2) { state = 0; xml.writeCharacters("\n"); } else { ++state; xml.writeCharacters(" "); } xml.writeCharacters(AString::number(parcel.m_voxelIndicesIJK[i])); } xml.writeEndElement(); } xml.writeEndElement(); } void CiftiXMLWriter::writeParcelNodes(QXmlStreamWriter& xml, const CiftiParcelNodesElement& parcelNodes) { int numNodes = (int)parcelNodes.m_nodes.size(); if (numNodes > 0)//don't write empty elements even if they exist in the tree { xml.writeStartElement("Nodes"); xml.writeAttribute("BrainStructure", StructureEnum::toCiftiName(parcelNodes.m_structure)); xml.writeCharacters(AString::number(parcelNodes.m_nodes[0])); for (int i = 1; i < numNodes; ++i) { xml.writeCharacters(" "); xml.writeCharacters(AString::number(parcelNodes.m_nodes[i])); } xml.writeEndElement(); } } void CiftiXMLWriter::writeVolume(QXmlStreamWriter &xml, const CiftiVolumeElement &volume) { xml.writeStartElement("Volume"); QString str("%1,%2,%3"); xml.writeAttribute("VolumeDimensions", str.arg(QString::number(volume.m_volumeDimensions[0]),QString::number(volume.m_volumeDimensions[1]),QString::number(volume.m_volumeDimensions[2]))); for(unsigned int i = 0;i 0) xml.writeAttribute("DataSpace", dataSpaceString); if(transformedSpaceString.length() > 0) xml.writeAttribute("TransformedSpace", transformedSpaceString); if(unitsXYZString.length() > 0) xml.writeAttribute("UnitsXYZ", unitsXYZString); QString matrixString; for(int i = 0;i<12;i++) { matrixString += QString::number(transform.m_transform[i], 'f', 10) + " "; } for (int i = 0; i < 3; ++i)//always write 0 0 0 1, ignore the actual last row { matrixString += QString::number(0.0f, 'f', 10) + " "; } matrixString += QString::number(1.0f, 'f', 10); xml.writeCharacters(matrixString); xml.writeEndElement(); } void CiftiXMLWriter::getModelTypeString(int modelType, QString &modelTypeString) { if(modelType == CIFTI_MODEL_TYPE_SURFACE) modelTypeString = "CIFTI_MODEL_TYPE_SURFACE"; else if(modelType == CIFTI_MODEL_TYPE_VOXELS) modelTypeString = "CIFTI_MODEL_TYPE_VOXELS"; } void CiftiXMLWriter::getDataSpaceString(int dataSpace, QString &dataSpaceString) { if(dataSpace == NIFTI_XFORM_UNKNOWN) dataSpaceString = "NIFTI_XFORM_UNKNOWN"; else if(dataSpace == NIFTI_XFORM_SCANNER_ANAT) dataSpaceString = "NIFTI_XFORM_SCANNER_ANAT"; else if(dataSpace == NIFTI_XFORM_ALIGNED_ANAT) dataSpaceString = "NIFTI_XFORM_ALIGNED_ANAT"; else if(dataSpace == NIFTI_XFORM_TALAIRACH) dataSpaceString = "NIFTI_XFORM_TALAIRACH"; else if(dataSpace == NIFTI_XFORM_MNI_152) dataSpaceString = "NIFTI_XFORM_MNI_152"; } void CiftiXMLWriter::getUnitsXYZString(int unitsXYZ, QString &unitsXYZString) { if(unitsXYZ == NIFTI_UNITS_MM) unitsXYZString = "NIFTI_UNITS_MM"; else if(unitsXYZ == NIFTI_UNITS_MICRON) unitsXYZString = "NIFTI_UNITS_MICRON"; } connectome-workbench-1.2.3+git41-gc4c6c90/src/Cifti/CiftiXMLWriter.h000066400000000000000000000050341300200146000245710ustar00rootroot00000000000000/*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*/ #ifndef __CIFTI_XML_WRITER_H__ #define __CIFTI_XML_WRITER_H__ #include #include "CiftiXMLElements.h" namespace caret { class CiftiXMLWriter { CiftiVersion m_writingVersion; void writeMatrixElement(QXmlStreamWriter &xml, const CiftiMatrixElement &matrixElement); void writeMetaData(QXmlStreamWriter &xml, const std::map &metaData); void writeMetaDataElement(QXmlStreamWriter &xml, const AString &name, const AString &value); void writeLabelTable(QXmlStreamWriter &xml, const std::vector &labelElement); void writeLabel(QXmlStreamWriter &xml, const CiftiLabelElement &label); void writeMatrixIndicesMap(QXmlStreamWriter &xml, const CiftiMatrixIndicesMapElement &matrixIndicesMap); void writeBrainModel(QXmlStreamWriter &xml, const CiftiBrainModelElement &brainModel); void writeNamedMap(QXmlStreamWriter& xml, const CiftiNamedMapElement& namedMap); void writeParcel(QXmlStreamWriter& xml, const CiftiParcelElement& parcel); void writeParcelNodes(QXmlStreamWriter& xml, const CiftiParcelNodesElement& parcelNodes); void writeVolume(QXmlStreamWriter &xml, const CiftiVolumeElement &volume); void writeTransformationMatrixVoxelIndicesIJKtoXYZ(QXmlStreamWriter &xml, const TransformationMatrixVoxelIndicesIJKtoXYZElement &transform); void getModelTypeString(int modelType, QString &modelTypeString); void getDataSpaceString(int dataSpace, QString &dataSpaceString); void getUnitsXYZString(int UnitsXYZ, QString &unitsXYZString); public: void writeCiftiXML(QXmlStreamWriter &xml, const CiftiRootElement &rootElement); }; } #endif //__CIFTI_XML_WRITER_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Cifti/Doxyfile000066400000000000000000002045441300200146000233210ustar00rootroot00000000000000# Doxyfile 1.7.1 # This file describes the settings to be used by the documentation system # doxygen (www.doxygen.org) for a project # # All text after a hash (#) is considered a comment and will be ignored # The format is: # TAG = value [value, ...] # For lists items can also be appended using: # TAG += value [value, ...] # Values that contain spaces should be placed between quotes (" ") #--------------------------------------------------------------------------- # Project related configuration options #--------------------------------------------------------------------------- # This tag specifies the encoding used for all characters in the config file # that follow. The default is UTF-8 which is also the encoding used for all # text before the first occurrence of this tag. Doxygen uses libiconv (or the # iconv built into libc) for the transcoding. See # http://www.gnu.org/software/libiconv for the list of possible encodings. DOXYFILE_ENCODING = UTF-8 # The PROJECT_NAME tag is a single word (or a sequence of words surrounded # by quotes) that should identify the project. PROJECT_NAME = Cifti # The PROJECT_NUMBER tag can be used to enter a project or revision number. # This could be handy for archiving the generated documentation or # if some version control system is used. PROJECT_NUMBER = # The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) # base path where the generated documentation will be put. # If a relative path is entered, it will be relative to the location # where doxygen was started. If left blank the current directory will be used. OUTPUT_DIRECTORY = ./doxy # If the CREATE_SUBDIRS tag is set to YES, then doxygen will create # 4096 sub-directories (in 2 levels) under the output directory of each output # format and will distribute the generated files over these directories. # Enabling this option can be useful when feeding doxygen a huge amount of # source files, where putting all generated files in the same directory would # otherwise cause performance problems for the file system. CREATE_SUBDIRS = NO # The OUTPUT_LANGUAGE tag is used to specify the language in which all # documentation generated by doxygen is written. Doxygen will use this # information to generate all constant output in the proper language. # The default language is English, other supported languages are: # Afrikaans, Arabic, Brazilian, Catalan, Chinese, Chinese-Traditional, # Croatian, Czech, Danish, Dutch, Esperanto, Farsi, Finnish, French, German, # Greek, Hungarian, Italian, Japanese, Japanese-en (Japanese with English # messages), Korean, Korean-en, Lithuanian, Norwegian, Macedonian, Persian, # Polish, Portuguese, Romanian, Russian, Serbian, Serbian-Cyrilic, Slovak, # Slovene, Spanish, Swedish, Ukrainian, and Vietnamese. OUTPUT_LANGUAGE = English # If the BRIEF_MEMBER_DESC tag is set to YES (the default) Doxygen will # include brief member descriptions after the members that are listed in # the file and class documentation (similar to JavaDoc). # Set to NO to disable this. BRIEF_MEMBER_DESC = YES # If the REPEAT_BRIEF tag is set to YES (the default) Doxygen will prepend # the brief description of a member or function before the detailed description. # Note: if both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the # brief descriptions will be completely suppressed. REPEAT_BRIEF = YES # This tag implements a quasi-intelligent brief description abbreviator # that is used to form the text in various listings. Each string # in this list, if found as the leading text of the brief description, will be # stripped from the text and the result after processing the whole list, is # used as the annotated text. Otherwise, the brief description is used as-is. # If left blank, the following values are used ("$name" is automatically # replaced with the name of the entity): "The $name class" "The $name widget" # "The $name file" "is" "provides" "specifies" "contains" # "represents" "a" "an" "the" ABBREVIATE_BRIEF = # If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then # Doxygen will generate a detailed section even if there is only a brief # description. ALWAYS_DETAILED_SEC = NO # If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all # inherited members of a class in the documentation of that class as if those # members were ordinary class members. Constructors, destructors and assignment # operators of the base classes will not be shown. INLINE_INHERITED_MEMB = NO # If the FULL_PATH_NAMES tag is set to YES then Doxygen will prepend the full # path before files name in the file list and in the header files. If set # to NO the shortest path that makes the file name unique will be used. FULL_PATH_NAMES = YES # If the FULL_PATH_NAMES tag is set to YES then the STRIP_FROM_PATH tag # can be used to strip a user-defined part of the path. Stripping is # only done if one of the specified strings matches the left-hand part of # the path. The tag can be used to show relative paths in the file list. # If left blank the directory from which doxygen is run is used as the # path to strip. STRIP_FROM_PATH = # The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of # the path mentioned in the documentation of a class, which tells # the reader which header file to include in order to use a class. # If left blank only the name of the header file containing the class # definition is used. Otherwise one should specify the include paths that # are normally passed to the compiler using the -I flag. STRIP_FROM_INC_PATH = # If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter # (but less readable) file names. This can be useful is your file systems # doesn't support long names like on DOS, Mac, or CD-ROM. SHORT_NAMES = NO # If the JAVADOC_AUTOBRIEF tag is set to YES then Doxygen # will interpret the first line (until the first dot) of a JavaDoc-style # comment as the brief description. If set to NO, the JavaDoc # comments will behave just like regular Qt-style comments # (thus requiring an explicit @brief command for a brief description.) JAVADOC_AUTOBRIEF = NO # If the QT_AUTOBRIEF tag is set to YES then Doxygen will # interpret the first line (until the first dot) of a Qt-style # comment as the brief description. If set to NO, the comments # will behave just like regular Qt-style comments (thus requiring # an explicit \brief command for a brief description.) QT_AUTOBRIEF = NO # The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make Doxygen # treat a multi-line C++ special comment block (i.e. a block of //! or /// # comments) as a brief description. This used to be the default behaviour. # The new default is to treat a multi-line C++ comment block as a detailed # description. Set this tag to YES if you prefer the old behaviour instead. MULTILINE_CPP_IS_BRIEF = NO # If the INHERIT_DOCS tag is set to YES (the default) then an undocumented # member inherits the documentation from any documented member that it # re-implements. INHERIT_DOCS = YES # If the SEPARATE_MEMBER_PAGES tag is set to YES, then doxygen will produce # a new page for each member. If set to NO, the documentation of a member will # be part of the file/class/namespace that contains it. SEPARATE_MEMBER_PAGES = NO # The TAB_SIZE tag can be used to set the number of spaces in a tab. # Doxygen uses this value to replace tabs by spaces in code fragments. TAB_SIZE = 8 # This tag can be used to specify a number of aliases that acts # as commands in the documentation. An alias has the form "name=value". # For example adding "sideeffect=\par Side Effects:\n" will allow you to # put the command \sideeffect (or @sideeffect) in the documentation, which # will result in a user-defined paragraph with heading "Side Effects:". # You can put \n's in the value part of an alias to insert newlines. ALIASES = # Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C # sources only. Doxygen will then generate output that is more tailored for C. # For instance, some of the names that are used will be different. The list # of all members will be omitted, etc. OPTIMIZE_OUTPUT_FOR_C = NO # Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java # sources only. Doxygen will then generate output that is more tailored for # Java. For instance, namespaces will be presented as packages, qualified # scopes will look different, etc. OPTIMIZE_OUTPUT_JAVA = NO # Set the OPTIMIZE_FOR_FORTRAN tag to YES if your project consists of Fortran # sources only. Doxygen will then generate output that is more tailored for # Fortran. OPTIMIZE_FOR_FORTRAN = NO # Set the OPTIMIZE_OUTPUT_VHDL tag to YES if your project consists of VHDL # sources. Doxygen will then generate output that is tailored for # VHDL. OPTIMIZE_OUTPUT_VHDL = NO # Doxygen selects the parser to use depending on the extension of the files it # parses. With this tag you can assign which parser to use for a given extension. # Doxygen has a built-in mapping, but you can override or extend it using this # tag. The format is ext=language, where ext is a file extension, and language # is one of the parsers supported by doxygen: IDL, Java, Javascript, CSharp, C, # C++, D, PHP, Objective-C, Python, Fortran, VHDL, C, C++. For instance to make # doxygen treat .inc files as Fortran files (default is PHP), and .f files as C # (default is Fortran), use: inc=Fortran f=C. Note that for custom extensions # you also need to set FILE_PATTERNS otherwise the files are not read by doxygen. EXTENSION_MAPPING = # If you use STL classes (i.e. std::string, std::vector, etc.) but do not want # to include (a tag file for) the STL sources as input, then you should # set this tag to YES in order to let doxygen match functions declarations and # definitions whose arguments contain STL classes (e.g. func(std::string); v.s. # func(std::string) {}). This also make the inheritance and collaboration # diagrams that involve STL classes more complete and accurate. BUILTIN_STL_SUPPORT = YES # If you use Microsoft's C++/CLI language, you should set this option to YES to # enable parsing support. CPP_CLI_SUPPORT = NO # Set the SIP_SUPPORT tag to YES if your project consists of sip sources only. # Doxygen will parse them like normal C++ but will assume all classes use public # instead of private inheritance when no explicit protection keyword is present. SIP_SUPPORT = NO # For Microsoft's IDL there are propget and propput attributes to indicate getter # and setter methods for a property. Setting this option to YES (the default) # will make doxygen to replace the get and set methods by a property in the # documentation. This will only work if the methods are indeed getting or # setting a simple type. If this is not the case, or you want to show the # methods anyway, you should set this option to NO. IDL_PROPERTY_SUPPORT = YES # If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC # tag is set to YES, then doxygen will reuse the documentation of the first # member in the group (if any) for the other members of the group. By default # all members of a group must be documented explicitly. DISTRIBUTE_GROUP_DOC = NO # Set the SUBGROUPING tag to YES (the default) to allow class member groups of # the same type (for instance a group of public functions) to be put as a # subgroup of that type (e.g. under the Public Functions section). Set it to # NO to prevent subgrouping. Alternatively, this can be done per class using # the \nosubgrouping command. SUBGROUPING = YES # When TYPEDEF_HIDES_STRUCT is enabled, a typedef of a struct, union, or enum # is documented as struct, union, or enum with the name of the typedef. So # typedef struct TypeS {} TypeT, will appear in the documentation as a struct # with name TypeT. When disabled the typedef will appear as a member of a file, # namespace, or class. And the struct will be named TypeS. This can typically # be useful for C code in case the coding convention dictates that all compound # types are typedef'ed and only the typedef is referenced, never the tag name. TYPEDEF_HIDES_STRUCT = NO # The SYMBOL_CACHE_SIZE determines the size of the internal cache use to # determine which symbols to keep in memory and which to flush to disk. # When the cache is full, less often used symbols will be written to disk. # For small to medium size projects (<1000 input files) the default value is # probably good enough. For larger projects a too small cache size can cause # doxygen to be busy swapping symbols to and from disk most of the time # causing a significant performance penality. # If the system has enough physical memory increasing the cache will improve the # performance by keeping more symbols in memory. Note that the value works on # a logarithmic scale so increasing the size by one will rougly double the # memory usage. The cache size is given by this formula: # 2^(16+SYMBOL_CACHE_SIZE). The valid range is 0..9, the default is 0, # corresponding to a cache size of 2^16 = 65536 symbols SYMBOL_CACHE_SIZE = 0 #--------------------------------------------------------------------------- # Build related configuration options #--------------------------------------------------------------------------- # If the EXTRACT_ALL tag is set to YES doxygen will assume all entities in # documentation are documented, even if no documentation was available. # Private class members and static file members will be hidden unless # the EXTRACT_PRIVATE and EXTRACT_STATIC tags are set to YES EXTRACT_ALL = NO # If the EXTRACT_PRIVATE tag is set to YES all private members of a class # will be included in the documentation. EXTRACT_PRIVATE = NO # If the EXTRACT_STATIC tag is set to YES all static members of a file # will be included in the documentation. EXTRACT_STATIC = NO # If the EXTRACT_LOCAL_CLASSES tag is set to YES classes (and structs) # defined locally in source files will be included in the documentation. # If set to NO only classes defined in header files are included. EXTRACT_LOCAL_CLASSES = NO # This flag is only useful for Objective-C code. When set to YES local # methods, which are defined in the implementation section but not in # the interface are included in the documentation. # If set to NO (the default) only methods in the interface are included. EXTRACT_LOCAL_METHODS = NO # If this flag is set to YES, the members of anonymous namespaces will be # extracted and appear in the documentation as a namespace called # 'anonymous_namespace{file}', where file will be replaced with the base # name of the file that contains the anonymous namespace. By default # anonymous namespace are hidden. EXTRACT_ANON_NSPACES = NO # If the HIDE_UNDOC_MEMBERS tag is set to YES, Doxygen will hide all # undocumented members of documented classes, files or namespaces. # If set to NO (the default) these members will be included in the # various overviews, but no documentation section is generated. # This option has no effect if EXTRACT_ALL is enabled. HIDE_UNDOC_MEMBERS = YES # If the HIDE_UNDOC_CLASSES tag is set to YES, Doxygen will hide all # undocumented classes that are normally visible in the class hierarchy. # If set to NO (the default) these classes will be included in the various # overviews. This option has no effect if EXTRACT_ALL is enabled. HIDE_UNDOC_CLASSES = YES # If the HIDE_FRIEND_COMPOUNDS tag is set to YES, Doxygen will hide all # friend (class|struct|union) declarations. # If set to NO (the default) these declarations will be included in the # documentation. HIDE_FRIEND_COMPOUNDS = NO # If the HIDE_IN_BODY_DOCS tag is set to YES, Doxygen will hide any # documentation blocks found inside the body of a function. # If set to NO (the default) these blocks will be appended to the # function's detailed documentation block. HIDE_IN_BODY_DOCS = NO # The INTERNAL_DOCS tag determines if documentation # that is typed after a \internal command is included. If the tag is set # to NO (the default) then the documentation will be excluded. # Set it to YES to include the internal documentation. INTERNAL_DOCS = NO # If the CASE_SENSE_NAMES tag is set to NO then Doxygen will only generate # file names in lower-case letters. If set to YES upper-case letters are also # allowed. This is useful if you have classes or files whose names only differ # in case and if your file system supports case sensitive file names. Windows # and Mac users are advised to set this option to NO. CASE_SENSE_NAMES = YES # If the HIDE_SCOPE_NAMES tag is set to NO (the default) then Doxygen # will show members with their full class and namespace scopes in the # documentation. If set to YES the scope will be hidden. HIDE_SCOPE_NAMES = NO # If the SHOW_INCLUDE_FILES tag is set to YES (the default) then Doxygen # will put a list of the files that are included by a file in the documentation # of that file. SHOW_INCLUDE_FILES = YES # If the FORCE_LOCAL_INCLUDES tag is set to YES then Doxygen # will list include files with double quotes in the documentation # rather than with sharp brackets. FORCE_LOCAL_INCLUDES = NO # If the INLINE_INFO tag is set to YES (the default) then a tag [inline] # is inserted in the documentation for inline members. INLINE_INFO = YES # If the SORT_MEMBER_DOCS tag is set to YES (the default) then doxygen # will sort the (detailed) documentation of file and class members # alphabetically by member name. If set to NO the members will appear in # declaration order. SORT_MEMBER_DOCS = YES # If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the # brief documentation of file, namespace and class members alphabetically # by member name. If set to NO (the default) the members will appear in # declaration order. SORT_BRIEF_DOCS = NO # If the SORT_MEMBERS_CTORS_1ST tag is set to YES then doxygen # will sort the (brief and detailed) documentation of class members so that # constructors and destructors are listed first. If set to NO (the default) # the constructors will appear in the respective orders defined by # SORT_MEMBER_DOCS and SORT_BRIEF_DOCS. # This tag will be ignored for brief docs if SORT_BRIEF_DOCS is set to NO # and ignored for detailed docs if SORT_MEMBER_DOCS is set to NO. SORT_MEMBERS_CTORS_1ST = NO # If the SORT_GROUP_NAMES tag is set to YES then doxygen will sort the # hierarchy of group names into alphabetical order. If set to NO (the default) # the group names will appear in their defined order. SORT_GROUP_NAMES = NO # If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be # sorted by fully-qualified names, including namespaces. If set to # NO (the default), the class list will be sorted only by class name, # not including the namespace part. # Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES. # Note: This option applies only to the class list, not to the # alphabetical list. SORT_BY_SCOPE_NAME = NO # The GENERATE_TODOLIST tag can be used to enable (YES) or # disable (NO) the todo list. This list is created by putting \todo # commands in the documentation. GENERATE_TODOLIST = YES # The GENERATE_TESTLIST tag can be used to enable (YES) or # disable (NO) the test list. This list is created by putting \test # commands in the documentation. GENERATE_TESTLIST = YES # The GENERATE_BUGLIST tag can be used to enable (YES) or # disable (NO) the bug list. This list is created by putting \bug # commands in the documentation. GENERATE_BUGLIST = YES # The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or # disable (NO) the deprecated list. This list is created by putting # \deprecated commands in the documentation. GENERATE_DEPRECATEDLIST= YES # The ENABLED_SECTIONS tag can be used to enable conditional # documentation sections, marked by \if sectionname ... \endif. ENABLED_SECTIONS = # The MAX_INITIALIZER_LINES tag determines the maximum number of lines # the initial value of a variable or define consists of for it to appear in # the documentation. If the initializer consists of more lines than specified # here it will be hidden. Use a value of 0 to hide initializers completely. # The appearance of the initializer of individual variables and defines in the # documentation can be controlled using \showinitializer or \hideinitializer # command in the documentation regardless of this setting. MAX_INITIALIZER_LINES = 30 # Set the SHOW_USED_FILES tag to NO to disable the list of files generated # at the bottom of the documentation of classes and structs. If set to YES the # list will mention the files that were used to generate the documentation. SHOW_USED_FILES = YES # If the sources in your project are distributed over multiple directories # then setting the SHOW_DIRECTORIES tag to YES will show the directory hierarchy # in the documentation. The default is NO. SHOW_DIRECTORIES = NO # Set the SHOW_FILES tag to NO to disable the generation of the Files page. # This will remove the Files entry from the Quick Index and from the # Folder Tree View (if specified). The default is YES. SHOW_FILES = YES # Set the SHOW_NAMESPACES tag to NO to disable the generation of the # Namespaces page. # This will remove the Namespaces entry from the Quick Index # and from the Folder Tree View (if specified). The default is YES. SHOW_NAMESPACES = YES # The FILE_VERSION_FILTER tag can be used to specify a program or script that # doxygen should invoke to get the current version for each file (typically from # the version control system). Doxygen will invoke the program by executing (via # popen()) the command , where is the value of # the FILE_VERSION_FILTER tag, and is the name of an input file # provided by doxygen. Whatever the program writes to standard output # is used as the file version. See the manual for examples. FILE_VERSION_FILTER = # The LAYOUT_FILE tag can be used to specify a layout file which will be parsed # by doxygen. The layout file controls the global structure of the generated # output files in an output format independent way. The create the layout file # that represents doxygen's defaults, run doxygen with the -l option. # You can optionally specify a file name after the option, if omitted # DoxygenLayout.xml will be used as the name of the layout file. LAYOUT_FILE = #--------------------------------------------------------------------------- # configuration options related to warning and progress messages #--------------------------------------------------------------------------- # The QUIET tag can be used to turn on/off the messages that are generated # by doxygen. Possible values are YES and NO. If left blank NO is used. QUIET = NO # The WARNINGS tag can be used to turn on/off the warning messages that are # generated by doxygen. Possible values are YES and NO. If left blank # NO is used. WARNINGS = YES # If WARN_IF_UNDOCUMENTED is set to YES, then doxygen will generate warnings # for undocumented members. If EXTRACT_ALL is set to YES then this flag will # automatically be disabled. WARN_IF_UNDOCUMENTED = YES # If WARN_IF_DOC_ERROR is set to YES, doxygen will generate warnings for # potential errors in the documentation, such as not documenting some # parameters in a documented function, or documenting parameters that # don't exist or using markup commands wrongly. WARN_IF_DOC_ERROR = YES # This WARN_NO_PARAMDOC option can be abled to get warnings for # functions that are documented, but have no documentation for their parameters # or return value. If set to NO (the default) doxygen will only warn about # wrong or incomplete parameter documentation, but not about the absence of # documentation. WARN_NO_PARAMDOC = NO # The WARN_FORMAT tag determines the format of the warning messages that # doxygen can produce. The string should contain the $file, $line, and $text # tags, which will be replaced by the file and line number from which the # warning originated and the warning text. Optionally the format may contain # $version, which will be replaced by the version of the file (if it could # be obtained via FILE_VERSION_FILTER) WARN_FORMAT = "$file:$line: $text" # The WARN_LOGFILE tag can be used to specify a file to which warning # and error messages should be written. If left blank the output is written # to stderr. WARN_LOGFILE = #--------------------------------------------------------------------------- # configuration options related to the input files #--------------------------------------------------------------------------- # The INPUT tag can be used to specify the files and/or directories that contain # documented source files. You may enter file names like "myfile.cpp" or # directories like "/usr/src/myproject". Separate the files or directories # with spaces. INPUT = . # This tag can be used to specify the character encoding of the source files # that doxygen parses. Internally doxygen uses the UTF-8 encoding, which is # also the default input encoding. Doxygen uses libiconv (or the iconv built # into libc) for the transcoding. See http://www.gnu.org/software/libiconv for # the list of possible encodings. INPUT_ENCODING = UTF-8 # If the value of the INPUT tag contains directories, you can use the # FILE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp # and *.h) to filter out the source-files in the directories. If left # blank the following patterns are tested: # *.c *.cc *.cxx *.cpp *.c++ *.java *.ii *.ixx *.ipp *.i++ *.inl *.h *.hh *.hxx # *.hpp *.h++ *.idl *.odl *.cs *.php *.php3 *.inc *.m *.mm *.py *.f90 FILE_PATTERNS = *.h \ *.cxx # The RECURSIVE tag can be used to turn specify whether or not subdirectories # should be searched for input files as well. Possible values are YES and NO. # If left blank NO is used. RECURSIVE = NO # The EXCLUDE tag can be used to specify files and/or directories that should # excluded from the INPUT source files. This way you can easily exclude a # subdirectory from a directory tree whose root is specified with the INPUT tag. EXCLUDE = # The EXCLUDE_SYMLINKS tag can be used select whether or not files or # directories that are symbolic links (a Unix filesystem feature) are excluded # from the input. EXCLUDE_SYMLINKS = NO # If the value of the INPUT tag contains directories, you can use the # EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude # certain files from those directories. Note that the wildcards are matched # against the file with absolute path, so to exclude all test directories # for example use the pattern */test/* EXCLUDE_PATTERNS = # The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names # (namespaces, classes, functions, etc.) that should be excluded from the # output. The symbol name can be a fully qualified name, a word, or if the # wildcard * is used, a substring. Examples: ANamespace, AClass, # AClass::ANamespace, ANamespace::*Test EXCLUDE_SYMBOLS = # The EXAMPLE_PATH tag can be used to specify one or more files or # directories that contain example code fragments that are included (see # the \include command). EXAMPLE_PATH = # If the value of the EXAMPLE_PATH tag contains directories, you can use the # EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp # and *.h) to filter out the source-files in the directories. If left # blank all files are included. EXAMPLE_PATTERNS = # If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be # searched for input files to be used with the \include or \dontinclude # commands irrespective of the value of the RECURSIVE tag. # Possible values are YES and NO. If left blank NO is used. EXAMPLE_RECURSIVE = NO # The IMAGE_PATH tag can be used to specify one or more files or # directories that contain image that are included in the documentation (see # the \image command). IMAGE_PATH = # The INPUT_FILTER tag can be used to specify a program that doxygen should # invoke to filter for each input file. Doxygen will invoke the filter program # by executing (via popen()) the command , where # is the value of the INPUT_FILTER tag, and is the name of an # input file. Doxygen will then use the output that the filter program writes # to standard output. # If FILTER_PATTERNS is specified, this tag will be # ignored. INPUT_FILTER = # The FILTER_PATTERNS tag can be used to specify filters on a per file pattern # basis. # Doxygen will compare the file name with each pattern and apply the # filter if there is a match. # The filters are a list of the form: # pattern=filter (like *.cpp=my_cpp_filter). See INPUT_FILTER for further # info on how filters are used. If FILTER_PATTERNS is empty, INPUT_FILTER # is applied to all files. FILTER_PATTERNS = # If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using # INPUT_FILTER) will be used to filter the input files when producing source # files to browse (i.e. when SOURCE_BROWSER is set to YES). FILTER_SOURCE_FILES = NO #--------------------------------------------------------------------------- # configuration options related to source browsing #--------------------------------------------------------------------------- # If the SOURCE_BROWSER tag is set to YES then a list of source files will # be generated. Documented entities will be cross-referenced with these sources. # Note: To get rid of all source code in the generated output, make sure also # VERBATIM_HEADERS is set to NO. SOURCE_BROWSER = NO # Setting the INLINE_SOURCES tag to YES will include the body # of functions and classes directly in the documentation. INLINE_SOURCES = NO # Setting the STRIP_CODE_COMMENTS tag to YES (the default) will instruct # doxygen to hide any special comment blocks from generated source code # fragments. Normal C and C++ comments will always remain visible. STRIP_CODE_COMMENTS = YES # If the REFERENCED_BY_RELATION tag is set to YES # then for each documented function all documented # functions referencing it will be listed. REFERENCED_BY_RELATION = NO # If the REFERENCES_RELATION tag is set to YES # then for each documented function all documented entities # called/used by that function will be listed. REFERENCES_RELATION = NO # If the REFERENCES_LINK_SOURCE tag is set to YES (the default) # and SOURCE_BROWSER tag is set to YES, then the hyperlinks from # functions in REFERENCES_RELATION and REFERENCED_BY_RELATION lists will # link to the source code. # Otherwise they will link to the documentation. REFERENCES_LINK_SOURCE = YES # If the USE_HTAGS tag is set to YES then the references to source code # will point to the HTML generated by the htags(1) tool instead of doxygen # built-in source browser. The htags tool is part of GNU's global source # tagging system (see http://www.gnu.org/software/global/global.html). You # will need version 4.8.6 or higher. USE_HTAGS = NO # If the VERBATIM_HEADERS tag is set to YES (the default) then Doxygen # will generate a verbatim copy of the header file for each class for # which an include is specified. Set to NO to disable this. VERBATIM_HEADERS = YES #--------------------------------------------------------------------------- # configuration options related to the alphabetical class index #--------------------------------------------------------------------------- # If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index # of all compounds will be generated. Enable this if the project # contains a lot of classes, structs, unions or interfaces. ALPHABETICAL_INDEX = YES # If the alphabetical index is enabled (see ALPHABETICAL_INDEX) then # the COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns # in which this list will be split (can be a number in the range [1..20]) COLS_IN_ALPHA_INDEX = 5 # In case all classes in a project start with a common prefix, all # classes will be put under the same header in the alphabetical index. # The IGNORE_PREFIX tag can be used to specify one or more prefixes that # should be ignored while generating the index headers. IGNORE_PREFIX = #--------------------------------------------------------------------------- # configuration options related to the HTML output #--------------------------------------------------------------------------- # If the GENERATE_HTML tag is set to YES (the default) Doxygen will # generate HTML output. GENERATE_HTML = YES # The HTML_OUTPUT tag is used to specify where the HTML docs will be put. # If a relative path is entered the value of OUTPUT_DIRECTORY will be # put in front of it. If left blank `html' will be used as the default path. HTML_OUTPUT = html # The HTML_FILE_EXTENSION tag can be used to specify the file extension for # each generated HTML page (for example: .htm,.php,.asp). If it is left blank # doxygen will generate files with .html extension. HTML_FILE_EXTENSION = .html # The HTML_HEADER tag can be used to specify a personal HTML header for # each generated HTML page. If it is left blank doxygen will generate a # standard header. HTML_HEADER = # The HTML_FOOTER tag can be used to specify a personal HTML footer for # each generated HTML page. If it is left blank doxygen will generate a # standard footer. HTML_FOOTER = # The HTML_STYLESHEET tag can be used to specify a user-defined cascading # style sheet that is used by each HTML page. It can be used to # fine-tune the look of the HTML output. If the tag is left blank doxygen # will generate a default style sheet. Note that doxygen will try to copy # the style sheet file to the HTML output directory, so don't put your own # stylesheet in the HTML output directory as well, or it will be erased! HTML_STYLESHEET = # The HTML_COLORSTYLE_HUE tag controls the color of the HTML output. # Doxygen will adjust the colors in the stylesheet and background images # according to this color. Hue is specified as an angle on a colorwheel, # see http://en.wikipedia.org/wiki/Hue for more information. # For instance the value 0 represents red, 60 is yellow, 120 is green, # 180 is cyan, 240 is blue, 300 purple, and 360 is red again. # The allowed range is 0 to 359. HTML_COLORSTYLE_HUE = 220 # The HTML_COLORSTYLE_SAT tag controls the purity (or saturation) of # the colors in the HTML output. For a value of 0 the output will use # grayscales only. A value of 255 will produce the most vivid colors. HTML_COLORSTYLE_SAT = 100 # The HTML_COLORSTYLE_GAMMA tag controls the gamma correction applied to # the luminance component of the colors in the HTML output. Values below # 100 gradually make the output lighter, whereas values above 100 make # the output darker. The value divided by 100 is the actual gamma applied, # so 80 represents a gamma of 0.8, The value 220 represents a gamma of 2.2, # and 100 does not change the gamma. HTML_COLORSTYLE_GAMMA = 80 # If the HTML_TIMESTAMP tag is set to YES then the footer of each generated HTML # page will contain the date and time when the page was generated. Setting # this to NO can help when comparing the output of multiple runs. HTML_TIMESTAMP = YES # If the HTML_ALIGN_MEMBERS tag is set to YES, the members of classes, # files or namespaces will be aligned in HTML using tables. If set to # NO a bullet list will be used. HTML_ALIGN_MEMBERS = YES # If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML # documentation will contain sections that can be hidden and shown after the # page has loaded. For this to work a browser that supports # JavaScript and DHTML is required (for instance Mozilla 1.0+, Firefox # Netscape 6.0+, Internet explorer 5.0+, Konqueror, or Safari). HTML_DYNAMIC_SECTIONS = NO # If the GENERATE_DOCSET tag is set to YES, additional index files # will be generated that can be used as input for Apple's Xcode 3 # integrated development environment, introduced with OSX 10.5 (Leopard). # To create a documentation set, doxygen will generate a Makefile in the # HTML output directory. Running make will produce the docset in that # directory and running "make install" will install the docset in # ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find # it at startup. # See http://developer.apple.com/tools/creatingdocsetswithdoxygen.html # for more information. GENERATE_DOCSET = NO # When GENERATE_DOCSET tag is set to YES, this tag determines the name of the # feed. A documentation feed provides an umbrella under which multiple # documentation sets from a single provider (such as a company or product suite) # can be grouped. DOCSET_FEEDNAME = "Doxygen generated docs" # When GENERATE_DOCSET tag is set to YES, this tag specifies a string that # should uniquely identify the documentation set bundle. This should be a # reverse domain-name style string, e.g. com.mycompany.MyDocSet. Doxygen # will append .docset to the name. DOCSET_BUNDLE_ID = org.doxygen.Project # When GENERATE_PUBLISHER_ID tag specifies a string that should uniquely identify # the documentation publisher. This should be a reverse domain-name style # string, e.g. com.mycompany.MyDocSet.documentation. DOCSET_PUBLISHER_ID = org.doxygen.Publisher # The GENERATE_PUBLISHER_NAME tag identifies the documentation publisher. DOCSET_PUBLISHER_NAME = Publisher # If the GENERATE_HTMLHELP tag is set to YES, additional index files # will be generated that can be used as input for tools like the # Microsoft HTML help workshop to generate a compiled HTML help file (.chm) # of the generated HTML documentation. GENERATE_HTMLHELP = NO # If the GENERATE_HTMLHELP tag is set to YES, the CHM_FILE tag can # be used to specify the file name of the resulting .chm file. You # can add a path in front of the file if the result should not be # written to the html output directory. CHM_FILE = # If the GENERATE_HTMLHELP tag is set to YES, the HHC_LOCATION tag can # be used to specify the location (absolute path including file name) of # the HTML help compiler (hhc.exe). If non-empty doxygen will try to run # the HTML help compiler on the generated index.hhp. HHC_LOCATION = # If the GENERATE_HTMLHELP tag is set to YES, the GENERATE_CHI flag # controls if a separate .chi index file is generated (YES) or that # it should be included in the master .chm file (NO). GENERATE_CHI = NO # If the GENERATE_HTMLHELP tag is set to YES, the CHM_INDEX_ENCODING # is used to encode HtmlHelp index (hhk), content (hhc) and project file # content. CHM_INDEX_ENCODING = # If the GENERATE_HTMLHELP tag is set to YES, the BINARY_TOC flag # controls whether a binary table of contents is generated (YES) or a # normal table of contents (NO) in the .chm file. BINARY_TOC = NO # The TOC_EXPAND flag can be set to YES to add extra items for group members # to the contents of the HTML help documentation and to the tree view. TOC_EXPAND = NO # If the GENERATE_QHP tag is set to YES and both QHP_NAMESPACE and # QHP_VIRTUAL_FOLDER are set, an additional index file will be generated # that can be used as input for Qt's qhelpgenerator to generate a # Qt Compressed Help (.qch) of the generated HTML documentation. GENERATE_QHP = NO # If the QHG_LOCATION tag is specified, the QCH_FILE tag can # be used to specify the file name of the resulting .qch file. # The path specified is relative to the HTML output folder. QCH_FILE = # The QHP_NAMESPACE tag specifies the namespace to use when generating # Qt Help Project output. For more information please see # http://doc.trolltech.com/qthelpproject.html#namespace QHP_NAMESPACE = org.doxygen.Project # The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating # Qt Help Project output. For more information please see # http://doc.trolltech.com/qthelpproject.html#virtual-folders QHP_VIRTUAL_FOLDER = doc # If QHP_CUST_FILTER_NAME is set, it specifies the name of a custom filter to # add. For more information please see # http://doc.trolltech.com/qthelpproject.html#custom-filters QHP_CUST_FILTER_NAME = # The QHP_CUST_FILT_ATTRS tag specifies the list of the attributes of the # custom filter to add. For more information please see # # Qt Help Project / Custom Filters. QHP_CUST_FILTER_ATTRS = # The QHP_SECT_FILTER_ATTRS tag specifies the list of the attributes this # project's # filter section matches. # # Qt Help Project / Filter Attributes. QHP_SECT_FILTER_ATTRS = # If the GENERATE_QHP tag is set to YES, the QHG_LOCATION tag can # be used to specify the location of Qt's qhelpgenerator. # If non-empty doxygen will try to run qhelpgenerator on the generated # .qhp file. QHG_LOCATION = # If the GENERATE_ECLIPSEHELP tag is set to YES, additional index files # will be generated, which together with the HTML files, form an Eclipse help # plugin. To install this plugin and make it available under the help contents # menu in Eclipse, the contents of the directory containing the HTML and XML # files needs to be copied into the plugins directory of eclipse. The name of # the directory within the plugins directory should be the same as # the ECLIPSE_DOC_ID value. After copying Eclipse needs to be restarted before # the help appears. GENERATE_ECLIPSEHELP = NO # A unique identifier for the eclipse help plugin. When installing the plugin # the directory name containing the HTML and XML files should also have # this name. ECLIPSE_DOC_ID = org.doxygen.Project # The DISABLE_INDEX tag can be used to turn on/off the condensed index at # top of each HTML page. The value NO (the default) enables the index and # the value YES disables it. DISABLE_INDEX = NO # This tag can be used to set the number of enum values (range [1..20]) # that doxygen will group on one line in the generated HTML documentation. ENUM_VALUES_PER_LINE = 4 # The GENERATE_TREEVIEW tag is used to specify whether a tree-like index # structure should be generated to display hierarchical information. # If the tag value is set to YES, a side panel will be generated # containing a tree-like index structure (just like the one that # is generated for HTML Help). For this to work a browser that supports # JavaScript, DHTML, CSS and frames is required (i.e. any modern browser). # Windows users are probably better off using the HTML help feature. GENERATE_TREEVIEW = NO # By enabling USE_INLINE_TREES, doxygen will generate the Groups, Directories, # and Class Hierarchy pages using a tree view instead of an ordered list. USE_INLINE_TREES = NO # If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be # used to set the initial width (in pixels) of the frame in which the tree # is shown. TREEVIEW_WIDTH = 250 # When the EXT_LINKS_IN_WINDOW option is set to YES doxygen will open # links to external symbols imported via tag files in a separate window. EXT_LINKS_IN_WINDOW = NO # Use this tag to change the font size of Latex formulas included # as images in the HTML documentation. The default is 10. Note that # when you change the font size after a successful doxygen run you need # to manually remove any form_*.png images from the HTML output directory # to force them to be regenerated. FORMULA_FONTSIZE = 10 # Use the FORMULA_TRANPARENT tag to determine whether or not the images # generated for formulas are transparent PNGs. Transparent PNGs are # not supported properly for IE 6.0, but are supported on all modern browsers. # Note that when changing this option you need to delete any form_*.png files # in the HTML output before the changes have effect. FORMULA_TRANSPARENT = YES # When the SEARCHENGINE tag is enabled doxygen will generate a search box # for the HTML output. The underlying search engine uses javascript # and DHTML and should work on any modern browser. Note that when using # HTML help (GENERATE_HTMLHELP), Qt help (GENERATE_QHP), or docsets # (GENERATE_DOCSET) there is already a search function so this one should # typically be disabled. For large projects the javascript based search engine # can be slow, then enabling SERVER_BASED_SEARCH may provide a better solution. SEARCHENGINE = YES # When the SERVER_BASED_SEARCH tag is enabled the search engine will be # implemented using a PHP enabled web server instead of at the web client # using Javascript. Doxygen will generate the search PHP script and index # file to put on the web server. The advantage of the server # based approach is that it scales better to large projects and allows # full text search. The disadvances is that it is more difficult to setup # and does not have live searching capabilities. SERVER_BASED_SEARCH = NO #--------------------------------------------------------------------------- # configuration options related to the LaTeX output #--------------------------------------------------------------------------- # If the GENERATE_LATEX tag is set to YES (the default) Doxygen will # generate Latex output. GENERATE_LATEX = NO # The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put. # If a relative path is entered the value of OUTPUT_DIRECTORY will be # put in front of it. If left blank `latex' will be used as the default path. LATEX_OUTPUT = latex # The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be # invoked. If left blank `latex' will be used as the default command name. # Note that when enabling USE_PDFLATEX this option is only used for # generating bitmaps for formulas in the HTML output, but not in the # Makefile that is written to the output directory. LATEX_CMD_NAME = latex # The MAKEINDEX_CMD_NAME tag can be used to specify the command name to # generate index for LaTeX. If left blank `makeindex' will be used as the # default command name. MAKEINDEX_CMD_NAME = makeindex # If the COMPACT_LATEX tag is set to YES Doxygen generates more compact # LaTeX documents. This may be useful for small projects and may help to # save some trees in general. COMPACT_LATEX = NO # The PAPER_TYPE tag can be used to set the paper type that is used # by the printer. Possible values are: a4, a4wide, letter, legal and # executive. If left blank a4wide will be used. PAPER_TYPE = a4wide # The EXTRA_PACKAGES tag can be to specify one or more names of LaTeX # packages that should be included in the LaTeX output. EXTRA_PACKAGES = # The LATEX_HEADER tag can be used to specify a personal LaTeX header for # the generated latex document. The header should contain everything until # the first chapter. If it is left blank doxygen will generate a # standard header. Notice: only use this tag if you know what you are doing! LATEX_HEADER = # If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated # is prepared for conversion to pdf (using ps2pdf). The pdf file will # contain links (just like the HTML output) instead of page references # This makes the output suitable for online browsing using a pdf viewer. PDF_HYPERLINKS = YES # If the USE_PDFLATEX tag is set to YES, pdflatex will be used instead of # plain latex in the generated Makefile. Set this option to YES to get a # higher quality PDF documentation. USE_PDFLATEX = YES # If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \\batchmode. # command to the generated LaTeX files. This will instruct LaTeX to keep # running if errors occur, instead of asking the user for help. # This option is also used when generating formulas in HTML. LATEX_BATCHMODE = NO # If LATEX_HIDE_INDICES is set to YES then doxygen will not # include the index chapters (such as File Index, Compound Index, etc.) # in the output. LATEX_HIDE_INDICES = NO # If LATEX_SOURCE_CODE is set to YES then doxygen will include # source code with syntax highlighting in the LaTeX output. # Note that which sources are shown also depends on other settings # such as SOURCE_BROWSER. LATEX_SOURCE_CODE = NO #--------------------------------------------------------------------------- # configuration options related to the RTF output #--------------------------------------------------------------------------- # If the GENERATE_RTF tag is set to YES Doxygen will generate RTF output # The RTF output is optimized for Word 97 and may not look very pretty with # other RTF readers or editors. GENERATE_RTF = NO # The RTF_OUTPUT tag is used to specify where the RTF docs will be put. # If a relative path is entered the value of OUTPUT_DIRECTORY will be # put in front of it. If left blank `rtf' will be used as the default path. RTF_OUTPUT = rtf # If the COMPACT_RTF tag is set to YES Doxygen generates more compact # RTF documents. This may be useful for small projects and may help to # save some trees in general. COMPACT_RTF = NO # If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated # will contain hyperlink fields. The RTF file will # contain links (just like the HTML output) instead of page references. # This makes the output suitable for online browsing using WORD or other # programs which support those fields. # Note: wordpad (write) and others do not support links. RTF_HYPERLINKS = NO # Load stylesheet definitions from file. Syntax is similar to doxygen's # config file, i.e. a series of assignments. You only have to provide # replacements, missing definitions are set to their default value. RTF_STYLESHEET_FILE = # Set optional variables used in the generation of an rtf document. # Syntax is similar to doxygen's config file. RTF_EXTENSIONS_FILE = #--------------------------------------------------------------------------- # configuration options related to the man page output #--------------------------------------------------------------------------- # If the GENERATE_MAN tag is set to YES (the default) Doxygen will # generate man pages GENERATE_MAN = YES # The MAN_OUTPUT tag is used to specify where the man pages will be put. # If a relative path is entered the value of OUTPUT_DIRECTORY will be # put in front of it. If left blank `man' will be used as the default path. MAN_OUTPUT = man # The MAN_EXTENSION tag determines the extension that is added to # the generated man pages (default is the subroutine's section .3) MAN_EXTENSION = .3 # If the MAN_LINKS tag is set to YES and Doxygen generates man output, # then it will generate one additional man file for each entity # documented in the real man page(s). These additional files # only source the real man page, but without them the man command # would be unable to find the correct page. The default is NO. MAN_LINKS = NO #--------------------------------------------------------------------------- # configuration options related to the XML output #--------------------------------------------------------------------------- # If the GENERATE_XML tag is set to YES Doxygen will # generate an XML file that captures the structure of # the code including all documentation. GENERATE_XML = NO # The XML_OUTPUT tag is used to specify where the XML pages will be put. # If a relative path is entered the value of OUTPUT_DIRECTORY will be # put in front of it. If left blank `xml' will be used as the default path. XML_OUTPUT = xml # The XML_SCHEMA tag can be used to specify an XML schema, # which can be used by a validating XML parser to check the # syntax of the XML files. XML_SCHEMA = # The XML_DTD tag can be used to specify an XML DTD, # which can be used by a validating XML parser to check the # syntax of the XML files. XML_DTD = # If the XML_PROGRAMLISTING tag is set to YES Doxygen will # dump the program listings (including syntax highlighting # and cross-referencing information) to the XML output. Note that # enabling this will significantly increase the size of the XML output. XML_PROGRAMLISTING = YES #--------------------------------------------------------------------------- # configuration options for the AutoGen Definitions output #--------------------------------------------------------------------------- # If the GENERATE_AUTOGEN_DEF tag is set to YES Doxygen will # generate an AutoGen Definitions (see autogen.sf.net) file # that captures the structure of the code including all # documentation. Note that this feature is still experimental # and incomplete at the moment. GENERATE_AUTOGEN_DEF = NO #--------------------------------------------------------------------------- # configuration options related to the Perl module output #--------------------------------------------------------------------------- # If the GENERATE_PERLMOD tag is set to YES Doxygen will # generate a Perl module file that captures the structure of # the code including all documentation. Note that this # feature is still experimental and incomplete at the # moment. GENERATE_PERLMOD = NO # If the PERLMOD_LATEX tag is set to YES Doxygen will generate # the necessary Makefile rules, Perl scripts and LaTeX code to be able # to generate PDF and DVI output from the Perl module output. PERLMOD_LATEX = NO # If the PERLMOD_PRETTY tag is set to YES the Perl module output will be # nicely formatted so it can be parsed by a human reader. # This is useful # if you want to understand what is going on. # On the other hand, if this # tag is set to NO the size of the Perl module output will be much smaller # and Perl will parse it just the same. PERLMOD_PRETTY = YES # The names of the make variables in the generated doxyrules.make file # are prefixed with the string contained in PERLMOD_MAKEVAR_PREFIX. # This is useful so different doxyrules.make files included by the same # Makefile don't overwrite each other's variables. PERLMOD_MAKEVAR_PREFIX = #--------------------------------------------------------------------------- # Configuration options related to the preprocessor #--------------------------------------------------------------------------- # If the ENABLE_PREPROCESSING tag is set to YES (the default) Doxygen will # evaluate all C-preprocessor directives found in the sources and include # files. ENABLE_PREPROCESSING = YES # If the MACRO_EXPANSION tag is set to YES Doxygen will expand all macro # names in the source code. If set to NO (the default) only conditional # compilation will be performed. Macro expansion can be done in a controlled # way by setting EXPAND_ONLY_PREDEF to YES. MACRO_EXPANSION = NO # If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES # then the macro expansion is limited to the macros specified with the # PREDEFINED and EXPAND_AS_DEFINED tags. EXPAND_ONLY_PREDEF = NO # If the SEARCH_INCLUDES tag is set to YES (the default) the includes files # in the INCLUDE_PATH (see below) will be search if a #include is found. SEARCH_INCLUDES = YES # The INCLUDE_PATH tag can be used to specify one or more directories that # contain include files that are not input files but should be processed by # the preprocessor. INCLUDE_PATH = # You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard # patterns (like *.h and *.hpp) to filter out the header-files in the # directories. If left blank, the patterns specified with FILE_PATTERNS will # be used. INCLUDE_FILE_PATTERNS = # The PREDEFINED tag can be used to specify one or more macro names that # are defined before the preprocessor is started (similar to the -D option of # gcc). The argument of the tag is a list of macros of the form: name # or name=definition (no spaces). If the definition and the = are # omitted =1 is assumed. To prevent a macro definition from being # undefined via #undef or recursively expanded use the := operator # instead of the = operator. PREDEFINED = # If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then # this tag can be used to specify a list of macro names that should be expanded. # The macro definition that is found in the sources will be used. # Use the PREDEFINED tag if you want to use a different macro definition. EXPAND_AS_DEFINED = # If the SKIP_FUNCTION_MACROS tag is set to YES (the default) then # doxygen's preprocessor will remove all function-like macros that are alone # on a line, have an all uppercase name, and do not end with a semicolon. Such # function macros are typically used for boiler-plate code, and will confuse # the parser if not removed. SKIP_FUNCTION_MACROS = YES #--------------------------------------------------------------------------- # Configuration::additions related to external references #--------------------------------------------------------------------------- # The TAGFILES option can be used to specify one or more tagfiles. # Optionally an initial location of the external documentation # can be added for each tagfile. The format of a tag file without # this location is as follows: # # TAGFILES = file1 file2 ... # Adding location for the tag files is done as follows: # # TAGFILES = file1=loc1 "file2 = loc2" ... # where "loc1" and "loc2" can be relative or absolute paths or # URLs. If a location is present for each tag, the installdox tool # does not have to be run to correct the links. # Note that each tag file must have a unique name # (where the name does NOT include the path) # If a tag file is not located in the directory in which doxygen # is run, you must also specify the path to the tagfile here. TAGFILES = # When a file name is specified after GENERATE_TAGFILE, doxygen will create # a tag file that is based on the input files it reads. GENERATE_TAGFILE = # If the ALLEXTERNALS tag is set to YES all external classes will be listed # in the class index. If set to NO only the inherited external classes # will be listed. ALLEXTERNALS = NO # If the EXTERNAL_GROUPS tag is set to YES all external groups will be listed # in the modules index. If set to NO, only the current project's groups will # be listed. EXTERNAL_GROUPS = YES # The PERL_PATH should be the absolute path and name of the perl script # interpreter (i.e. the result of `which perl'). PERL_PATH = /usr/bin/perl #--------------------------------------------------------------------------- # Configuration options related to the dot tool #--------------------------------------------------------------------------- # If the CLASS_DIAGRAMS tag is set to YES (the default) Doxygen will # generate a inheritance diagram (in HTML, RTF and LaTeX) for classes with base # or super classes. Setting the tag to NO turns the diagrams off. Note that # this option is superseded by the HAVE_DOT option below. This is only a # fallback. It is recommended to install and use dot, since it yields more # powerful graphs. CLASS_DIAGRAMS = YES # You can define message sequence charts within doxygen comments using the \msc # command. Doxygen will then run the mscgen tool (see # http://www.mcternan.me.uk/mscgen/) to produce the chart and insert it in the # documentation. The MSCGEN_PATH tag allows you to specify the directory where # the mscgen tool resides. If left empty the tool is assumed to be found in the # default search path. MSCGEN_PATH = # If set to YES, the inheritance and collaboration graphs will hide # inheritance and usage relations if the target is undocumented # or is not a class. HIDE_UNDOC_RELATIONS = YES # If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is # available from the path. This tool is part of Graphviz, a graph visualization # toolkit from AT&T and Lucent Bell Labs. The other options in this section # have no effect if this option is set to NO (the default) HAVE_DOT = YES # The DOT_NUM_THREADS specifies the number of dot invocations doxygen is # allowed to run in parallel. When set to 0 (the default) doxygen will # base this on the number of processors available in the system. You can set it # explicitly to a value larger than 0 to get control over the balance # between CPU load and processing speed. DOT_NUM_THREADS = 0 # By default doxygen will write a font called FreeSans.ttf to the output # directory and reference it in all dot files that doxygen generates. This # font does not include all possible unicode characters however, so when you need # these (or just want a differently looking font) you can specify the font name # using DOT_FONTNAME. You need need to make sure dot is able to find the font, # which can be done by putting it in a standard location or by setting the # DOTFONTPATH environment variable or by setting DOT_FONTPATH to the directory # containing the font. DOT_FONTNAME = FreeSans.ttf # The DOT_FONTSIZE tag can be used to set the size of the font of dot graphs. # The default size is 10pt. DOT_FONTSIZE = 10 # By default doxygen will tell dot to use the output directory to look for the # FreeSans.ttf font (which doxygen will put there itself). If you specify a # different font using DOT_FONTNAME you can set the path where dot # can find it using this tag. DOT_FONTPATH = # If the CLASS_GRAPH and HAVE_DOT tags are set to YES then doxygen # will generate a graph for each documented class showing the direct and # indirect inheritance relations. Setting this tag to YES will force the # the CLASS_DIAGRAMS tag to NO. CLASS_GRAPH = YES # If the COLLABORATION_GRAPH and HAVE_DOT tags are set to YES then doxygen # will generate a graph for each documented class showing the direct and # indirect implementation dependencies (inheritance, containment, and # class references variables) of the class with other documented classes. COLLABORATION_GRAPH = YES # If the GROUP_GRAPHS and HAVE_DOT tags are set to YES then doxygen # will generate a graph for groups, showing the direct groups dependencies GROUP_GRAPHS = YES # If the UML_LOOK tag is set to YES doxygen will generate inheritance and # collaboration diagrams in a style similar to the OMG's Unified Modeling # Language. UML_LOOK = NO # If set to YES, the inheritance and collaboration graphs will show the # relations between templates and their instances. TEMPLATE_RELATIONS = NO # If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDE_GRAPH, and HAVE_DOT # tags are set to YES then doxygen will generate a graph for each documented # file showing the direct and indirect include dependencies of the file with # other documented files. INCLUDE_GRAPH = YES # If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDED_BY_GRAPH, and # HAVE_DOT tags are set to YES then doxygen will generate a graph for each # documented header file showing the documented files that directly or # indirectly include this file. INCLUDED_BY_GRAPH = YES # If the CALL_GRAPH and HAVE_DOT options are set to YES then # doxygen will generate a call dependency graph for every global function # or class method. Note that enabling this option will significantly increase # the time of a run. So in most cases it will be better to enable call graphs # for selected functions only using the \callgraph command. CALL_GRAPH = YES # If the CALLER_GRAPH and HAVE_DOT tags are set to YES then # doxygen will generate a caller dependency graph for every global function # or class method. Note that enabling this option will significantly increase # the time of a run. So in most cases it will be better to enable caller # graphs for selected functions only using the \callergraph command. CALLER_GRAPH = YES # If the GRAPHICAL_HIERARCHY and HAVE_DOT tags are set to YES then doxygen # will graphical hierarchy of all classes instead of a textual one. GRAPHICAL_HIERARCHY = YES # If the DIRECTORY_GRAPH, SHOW_DIRECTORIES and HAVE_DOT tags are set to YES # then doxygen will show the dependencies a directory has on other directories # in a graphical way. The dependency relations are determined by the #include # relations between the files in the directories. DIRECTORY_GRAPH = YES # The DOT_IMAGE_FORMAT tag can be used to set the image format of the images # generated by dot. Possible values are png, jpg, or gif # If left blank png will be used. DOT_IMAGE_FORMAT = png # The tag DOT_PATH can be used to specify the path where the dot tool can be # found. If left blank, it is assumed the dot tool can be found in the path. DOT_PATH = # The DOTFILE_DIRS tag can be used to specify one or more directories that # contain dot files that are included in the documentation (see the # \dotfile command). DOTFILE_DIRS = # The DOT_GRAPH_MAX_NODES tag can be used to set the maximum number of # nodes that will be shown in the graph. If the number of nodes in a graph # becomes larger than this value, doxygen will truncate the graph, which is # visualized by representing a node as a red box. Note that doxygen if the # number of direct children of the root node in a graph is already larger than # DOT_GRAPH_MAX_NODES then the graph will not be shown at all. Also note # that the size of a graph can be further restricted by MAX_DOT_GRAPH_DEPTH. DOT_GRAPH_MAX_NODES = 50 # The MAX_DOT_GRAPH_DEPTH tag can be used to set the maximum depth of the # graphs generated by dot. A depth value of 3 means that only nodes reachable # from the root by following a path via at most 3 edges will be shown. Nodes # that lay further from the root node will be omitted. Note that setting this # option to 1 or 2 may greatly reduce the computation time needed for large # code bases. Also note that the size of a graph can be further restricted by # DOT_GRAPH_MAX_NODES. Using a depth of 0 means no depth restriction. MAX_DOT_GRAPH_DEPTH = 0 # Set the DOT_TRANSPARENT tag to YES to generate images with a transparent # background. This is disabled by default, because dot on Windows does not # seem to support this out of the box. Warning: Depending on the platform used, # enabling this option may lead to badly anti-aliased labels on the edges of # a graph (i.e. they become hard to read). DOT_TRANSPARENT = NO # Set the DOT_MULTI_TARGETS tag to YES allow dot to generate multiple output # files in one run (i.e. multiple -o and -T options on the command line). This # makes dot run faster, but since only newer versions of dot (>1.8.10) # support this, this feature is disabled by default. DOT_MULTI_TARGETS = YES # If the GENERATE_LEGEND tag is set to YES (the default) Doxygen will # generate a legend page explaining the meaning of the various boxes and # arrows in the dot generated graphs. GENERATE_LEGEND = YES # If the DOT_CLEANUP tag is set to YES (the default) Doxygen will # remove the intermediate dot files that are used to generate # the various graphs. DOT_CLEANUP = YES connectome-workbench-1.2.3+git41-gc4c6c90/src/Cifti/QUICKSTART000066400000000000000000000031131300200146000232150ustar00rootroot00000000000000The entry point for reading and writing Cifti data is the object CiftiFile. To get a handle to a Cifti File, use the following syntax: CiftiFile cf("testciftifile.dtseries.nii"), cf2;//starts on-disk reading, and makes an uninitialized second object cf2.setWritingFile(testoutcifti.dtseries.nii");//prepare on-disk writing mode //cf.convertToInMemory();//if you want to read entire file into memory now CiftiFile gives access to the file in two parts, the Cifti XML, and the Cifti Matrix data. To access the data use the following functions: //get the CiftiXML object, containing mapping info for each dimension const CiftiXML& xml = cf->getCiftiXML(); cf2->setCiftiXML(xml);//setting xml is required before writing rows of data, as the xml controls the dimensions and mapping info //read/write Cifti rows vector scratchRow(cf.getDimensions()[0]); //simplified functions for 2D only cf.getRow(scratchRow.data(), 0); cf2.setRow(scratchRow.data(), 0); //example specifically for 3D cifti vector indices(2, 0);//[0 0] cf.getRow(scratchRow.data(), indices); cf2.setRow(scratchRow.data(), indices); //write current state to a new Cifti File, without changing what file subsequent setRow calls set data in (if any) cf.writeFile("outputfile.dtseries.nii"); For more detailed information on how to manipulate Cifti data, look at the source files in the examples directory Other references: nitrc page: http://www.nitrc.org/projects/cifti/ effectively same library, available under BSD 2-clause, and adapted to work with either libxml++ or qt: https://github.com/Washington-University/CiftiLib connectome-workbench-1.2.3+git41-gc4c6c90/src/Cifti/examples/000077500000000000000000000000001300200146000234205ustar00rootroot00000000000000connectome-workbench-1.2.3+git41-gc4c6c90/src/Cifti/examples/rewrite.cxx000066400000000000000000000063471300200146000256370ustar00rootroot00000000000000/*LICENSE_START*/ /* * Copyright (C) 2015 Washington University School of Medicine * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be included * in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*LICENSE_END*/ #include "CaretException.h" #include "CiftiFile.h" #include #include using namespace std; using namespace caret; /**\file rewrite.cxx This program reads a Cifti file from argv[1], and writes it out to argv[2] with a second CiftiFile object. It uses on-disk reading and writing, so DO NOT have both filenames point to the same file, CiftiFile truncates without any warning when told to write to an existing file. \include rewrite.cxx */ int main(int argc, char** argv) { if (argc < 3) { cout << "this program requires two arguments: an input cifti file, and an output filename to write it to" << endl; return 1; } try { CiftiFile inputFile(argv[1]);//on-disk reading by default //inputFile.convertToInMemory();//if you want to read it into memory first CiftiFile outputFile; outputFile.setWritingFile(argv[2]);//sets up on-disk writing with default writing version, from CiftiVersion's default constructor outputFile.setCiftiXML(inputFile.getCiftiXML());//the CiftiXML is how you access all the mapping information const vector& dims = inputFile.getDimensions(); vector scratchRow(dims[0]);//read/write a row at a time for (MultiDimIterator iter = inputFile.getIteratorOverRows(); !iter.atEnd(); ++iter) {//helper class to iterate over 2D and 3D cifti with the same code - the "+ 1" is to drop the first dimension (row length) inputFile.getRow(scratchRow.data(), *iter); outputFile.setRow(scratchRow.data(), *iter); } outputFile.writeFile(argv[2]);//because we called setWritingFile with this filename (and default cifti version), this will return immediately //NOTE: if you call writeFile with a different writing version (takes its default from CiftiVersion constructor) than setWritingFile, it will rewrite the entire file after reading it into memory } catch (CiftiException& e) { cerr << "Caught CaretException: " + e.whatString() << endl; return 1; } return 0; } connectome-workbench-1.2.3+git41-gc4c6c90/src/Cifti/examples/xmlinfo.cxx000066400000000000000000000140111300200146000256150ustar00rootroot00000000000000/*LICENSE_START*/ /* * Copyright (C) 2015 Washington University School of Medicine * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be included * in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*LICENSE_END*/ #include "CaretException.h" #include "CiftiFile.h" #include #include using namespace std; using namespace caret; /**\file xmlinfo.cxx This program reads a Cifti file from argv[1], and prints out a summary of the XML. \include xmlinfo.cxx */ int main(int argc, char** argv) { if (argc < 2) { cout << "this program requires one argument: an input cifti file" << endl; return 1; } try { CiftiFile inputFile(argv[1]);//on-disk reading by default, and we only need the XML header anyway const CiftiXML& myXML = inputFile.getCiftiXML(); for (int whichDim = 0; whichDim < myXML.getNumberOfDimensions(); ++whichDim) { cout << "Dimension " << whichDim << ": "; switch (myXML.getMappingType(whichDim)) { case CiftiMappingType::BRAIN_MODELS: { const CiftiBrainModelsMap& myMap = myXML.getBrainModelsMap(whichDim); cout << "Brain Models, length " << myMap.getLength() << endl; vector myInfo = myMap.getModelInfo();//this is only summary info, same order as the models are in the cifti indices for (int i = 0; i < (int)myInfo.size(); ++i)//to get the lists of vertices/voxels for a model, see getSurfaceMap, getVolumeStructureMap, and getFullVolumeMap { switch (myInfo[i].m_type) { case CiftiBrainModelsMap::SURFACE: cout << " Surface " << StructureEnum::toName(myInfo[i].m_structure) << ": "; cout << myInfo[i].m_indexCount << " out of " << myMap.getSurfaceNumberOfNodes(myInfo[i].m_structure) << " vertices" << endl; break; case CiftiBrainModelsMap::VOXELS: cout << " Voxels " << StructureEnum::toName(myInfo[i].m_structure) << ": "; cout << myInfo[i].m_indexCount << " voxels" << endl; break; } } break; } case CiftiMappingType::LABELS: { const CiftiLabelsMap& myMap = myXML.getLabelsMap(whichDim); cout << "Labels, length " << myMap.getLength() << endl; for (int i = 0; i < myMap.getLength(); ++i) { cout << " Index " << i << ": " << myMap.getMapName(i) << endl; } break; } case CiftiMappingType::PARCELS: { const CiftiParcelsMap& myMap = myXML.getParcelsMap(whichDim); cout << "Parcels, length " << myMap.getLength() << endl; const vector& myParcels = myMap.getParcels(); for (int i = 0; i < (int)myParcels.size(); ++i) { cout << " Index " << i << ", name '" << myParcels[i].m_name << "': "; int numVerts = 0; for (map >::const_iterator iter = myParcels[i].m_surfaceNodes.begin(); iter != myParcels[i].m_surfaceNodes.end(); ++iter) { numVerts += iter->second.size(); } cout << numVerts << " vertices, " << myParcels[i].m_voxelIndices.size() << " voxels" << endl; } break; } case CiftiMappingType::SCALARS: { const CiftiScalarsMap& myMap = myXML.getScalarsMap(whichDim); cout << "Scalars, length " << myMap.getLength() << endl; for (int i = 0; i < myMap.getLength(); ++i) { cout << " Index " << i << ": " << myMap.getMapName(i) << endl; } break; } case CiftiMappingType::SERIES: { const CiftiSeriesMap& myMap = myXML.getSeriesMap(whichDim); cout << "Series, length " << myMap.getLength() << endl; cout << " Start: " << myMap.getStart() << endl; cout << " Step: " << myMap.getStep() << endl; cout << " Unit: " << CiftiSeriesMap::unitToString(myMap.getUnit()) << endl; break; } } cout << endl;//extra line break between dimensions } } catch (CaretException& e) { cerr << "Caught CaretException: " + e.whatString() << endl; return 1; } return 0; } connectome-workbench-1.2.3+git41-gc4c6c90/src/CommandLine/000077500000000000000000000000001300200146000227325ustar00rootroot00000000000000connectome-workbench-1.2.3+git41-gc4c6c90/src/CommandLine/CMakeLists.txt000066400000000000000000000035361300200146000255010ustar00rootroot00000000000000 # # Name of project # 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}) # # Name of executable # set (EXE_NAME wb_command) # # Resources # QT4_ADD_RESOURCES(IMAGE_RCS_SRCS ../Resources/GeneralResources/general_resources.qrc) # # Create the executable # Apple creates a bundle # IF (APPLE) ADD_EXECUTABLE(${EXE_NAME} MACOSX_BUNDLE wb_command.cxx ${IMAGE_RCS_SRCS} ) ELSE (APPLE) ADD_EXECUTABLE(${EXE_NAME} wb_command.cxx ${IMAGE_RCS_SRCS} ) ENDIF (APPLE) # # Libraries that are linked # Note: OSMESA library variables will be empty and have no effect # if OSMESA is not available # TARGET_LINK_LIBRARIES(${EXE_NAME} Commands Operations Algorithms OperationsBase Brain ${FTGL_LIBRARIES} Files Annotations Palette Gifti Cifti Nifti Charting FilesBase Scenes Xml Common ${QUAZIP_LIBRARIES} ${FREETYPE_LIBRARIES} ${QT_LIBRARIES} ${OSMESA_OFFSCREEN_LIBRARY} ${OSMESA_GL_LIBRARY} ${OSMESA_GLU_LIBRARY} ${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) # # Find Headers # INCLUDE_DIRECTORIES( ${CMAKE_SOURCE_DIR}/CommandLine ${CMAKE_SOURCE_DIR}/Commands ${CMAKE_SOURCE_DIR}/Operations ${CMAKE_SOURCE_DIR}/Algorithms ${CMAKE_SOURCE_DIR}/Annotations ${CMAKE_SOURCE_DIR}/OperationsBase ${CMAKE_SOURCE_DIR}/Brain ${CMAKE_SOURCE_DIR}/Charting ${CMAKE_SOURCE_DIR}/Palette ${CMAKE_SOURCE_DIR}/Files ${CMAKE_SOURCE_DIR}/Gifti ${CMAKE_SOURCE_DIR}/Cifti ${CMAKE_SOURCE_DIR}/Nifti ${CMAKE_SOURCE_DIR}/FilesBase ${CMAKE_SOURCE_DIR}/Scenes ${CMAKE_SOURCE_DIR}/Xml ${CMAKE_SOURCE_DIR}/Common ${QUAZIP_INCLUDE_DIRS} ) connectome-workbench-1.2.3+git41-gc4c6c90/src/CommandLine/README.txt000066400000000000000000000003151300200146000244270ustar00rootroot00000000000000To add new commands, see the README.txt in Commands. For wb_shortcuts, note that its development does not take place in this repository, see https://github.com/Washington-University/wb_shortcuts instead. connectome-workbench-1.2.3+git41-gc4c6c90/src/CommandLine/bashcomplete_wb_command000066400000000000000000000102631300200146000275130ustar00rootroot00000000000000# bash completion for wb_command # Copyright (C) 2016 Washington University School of Medicine # # Permission is hereby granted, free of charge, to any person obtaining # a copy of this software and associated documentation files (the # "Software"), to deal in the Software without restriction, including # without limitation the rights to use, copy, modify, merge, publish, # distribute, sublicense, and/or sell copies of the Software, and to # permit persons to whom the Software is furnished to do so, subject to # the following conditions: # # The above copyright notice and this permission notice shall be included # in all copies or substantial portions of the Software. # # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. # IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY # CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, # 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 function _wb_command() { #debug must be "false" or "true", because it *executes* it (because i'm lazy) local debug=false COMPREPLY=() #use bash-completion's hacks to deal with the word splitting we don't want local cur words cword #use only space for splitting _get_comp_words_by_ref -n "'"'"><=;|&(:' cur words cword #NOTE: cur has the partial word that is *before* the cursor, but words has the entire word under the cursor #wb_command doesn't need to know what the current partial word is, it spits out space-separated glob patterns and the like #but we will need it in compgen afterwards local wbopts="" if ! shopt -q extglob then #bash-completion enables extglob, but work around it if it isn't set wbopts="-noextglob" fi #only give wb_command the options coming *before* the one to complete, not even the partial word to complete #also don't include the executable name ${words[0]} $debug && echo $debug && echo "exe call: wb_command -completion $wbopts ${words[@]:1:$((cword - 1))}" local exereplyraw=`wb_command -completion $wbopts "${words[@]:1:$((cword - 1))}" 2> /dev/null` local exestatus=$? if [[ $exestatus != 0 ]] then return 1 fi $debug && echo "exereplayraw: $exereplyraw" #need to cause backslashes to escape spaces... local -a exereply IFS=' ' read -a exereply <<< "$exereplyraw" $debug && echo "exereply: ${exereply[@]}" for ((i = 0; i < ${#exereply[@]}; i += 2)) do $debug && echo "key: ${exereply[$i]}" $debug && echo "value: ${exereply[$((i + 1))]}" case "${exereply[$i]}" in 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"`) ;; wordlist) #for the things accepted as bool, maybe other uses - happens to function like "option" for now COMPREPLY+=(`compgen -W "${exereply[$((i + 1))]}" -- "$cur"`) ;; esac done $debug && echo "COMPREPLY: ${COMPREPLY[@]}" return 0 } complete -o filenames -o bashdefault -o default -F _wb_command wb_command #completion for wb_shortcuts - could be a separate file, but this is simpler function _wb_shortcuts () { COMPREPLY=() #use bash-completion's hacks to deal with the word splitting we don't want #we can only complete the first word, so only get cur and cword local cur cword #use only space for splitting _get_comp_words_by_ref -n "'"'"><=;|&(:' cur cword if ((cword != 1)) then return 0 fi local -a switches readarray -t switches < <(wb_shortcuts -list-functions | awk '{print $1}') COMPREPLY=(`compgen -W "${switches[*]}" -- "$cur"`) } complete -o filenames -o bashdefault -o default -F _wb_shortcuts wb_shortcuts connectome-workbench-1.2.3+git41-gc4c6c90/src/CommandLine/wb_command.cxx000066400000000000000000000137031300200146000255700ustar00rootroot00000000000000/*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 "AString.h" #include "CaretAssert.h" #include "CaretHttpManager.h" #include "CaretCommandLine.h" #include "CaretLogger.h" #include "CommandOperationManager.h" #include "ProgramParameters.h" #include "SessionManager.h" #include "SystemUtilities.h" #include "VolumeFile.h" using namespace caret; using namespace std; static int runCommand(int argc, char* argv[]) { ProgramParameters parameters(argc, argv); caret_global_commandLine_init(parameters); /* * Log the command parameters. */ CaretLogFine("Running: " + caret_global_commandLine); CommandOperationManager* commandManager = NULL; int ret = 0; try { commandManager = CommandOperationManager::getCommandOperationManager(); commandManager->runCommand(parameters); } catch (CaretException& e) { cerr << "\nWhile running:\n" << caret_global_commandLine.toLocal8Bit().constData() << "\n\nERROR: " << e.whatString().toLocal8Bit().constData() << endl << endl; ret = -1; } catch (bad_alloc& e) {//if we stop using a handler for new cerr << "\nWhile running:\n" << caret_global_commandLine.toLocal8Bit().constData() << "\n\nERROR: " << e.what() << endl; cerr << endl << "OUT OF MEMORY" << endl << endl << "This means that Workbench is unable to get memory that it needs." << endl << "Possible causes:" << endl << " (1) Your computer lacks sufficient RAM." << endl << " (2) Swap space is too small (you might increase it)." << endl << " (3) Your computer may be using an non-English character" << endl//is this relevant? << " set. Try switching to the English character set." << endl << endl; ret = -1; } catch (exception& e) { cerr << "\nWhile running:\n" << caret_global_commandLine.toLocal8Bit().constData() << "\n\nERROR: " << e.what() << endl << endl; ret = -1; } catch (...) { cerr << "\nWhile running:\n" << caret_global_commandLine.toLocal8Bit().constData() << "\n\nERROR: caught unknown exception type, rethrowing..." << endl << endl; if (commandManager != NULL) { CommandOperationManager::deleteCommandOperationManager(); } throw;//rethrow, the runtime might print the type } if (commandManager != NULL) { CommandOperationManager::deleteCommandOperationManager(); } return ret; } static int doCompletion(int argc, char* argv[]) { //we don't handle any interactive arguments in this file (only completion related arguments, which should never be used interactively) //so, just call completion on next level ProgramParameters parameters(argc, argv); caret_global_commandLine_init(parameters); try { parameters.nextString("-completion");//drop the completion switch bool useExtGlob = true;//bash-completion turns on extglob by default, so it is usually safe if (parameters.hasNext()) { if (parameters.nextString("test for option") == "-noextglob") { useExtGlob = false; } else { parameters.backup(); } } CommandOperationManager* commandManager = CommandOperationManager::getCommandOperationManager(); AString compHints = commandManager->doCompletion(parameters, useExtGlob); cout << compHints.toLocal8Bit().constData() << endl; } catch (...) { return 1; } return 0; } int main(int argc, char* argv[]) { srand(time(NULL)); //short-circuit to avoid things like palette initialization so that command line completion can be more responsive //also should be easier to avoid various logging statements, which we don't want (stderr would destroy the current prompt, and stdout would interfere with the completion output) if (argc > 1 && AString::fromLocal8Bit(argv[1]) == "-completion") { return doCompletion(argc, argv); } int result = 0; { /* * Handle uncaught exceptions */ SystemUtilities::setUnexpectedHandler(); /* * Create the session manager. */ SessionManager::createSessionManager(ApplicationTypeEnum::APPLICATION_TYPE_COMMAND_LINE); /* * Disable volume voxel coloring since it can be a little slow * and voxel coloring is not needed for any commands (at this * time). */ VolumeFile::setVoxelColoringEnabled(false); QCoreApplication myApp(argc, argv);//so that it doesn't need to link against gui result = runCommand(argc, argv); /* * Delete the session manager. */ SessionManager::deleteSessionManager(); CaretHttpManager::deleteHttpManager();//does this belong in some other singleton manager? myApp.processEvents();//since we don't exec(), let it clean up any ->deleteLater()s } /* * See if any objects were not deleted. */ CaretObject::printListOfObjectsNotDeleted(true); return result; } connectome-workbench-1.2.3+git41-gc4c6c90/src/CommandLine/wb_shortcuts000077500000000000000000000657461300200146000254300ustar00rootroot00000000000000#!/bin/bash # Copyright (C) 2016 Washington University School of Medicine # # Permission is hereby granted, free of charge, to any person obtaining # a copy of this software and associated documentation files (the # "Software"), to deal in the Software without restriction, including # without limitation the rights to use, copy, modify, merge, publish, # distribute, sublicense, and/or sell copies of the Software, and to # permit persons to whom the Software is furnished to do so, subject to # the following conditions: # # The above copyright notice and this permission notice shall be included # in all copies or substantial portions of the Software. # # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. # IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY # CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, # TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE # SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. set -ue global_cmd_line="$0 $*" global_script_name="wb_shortcuts" global_script_version="beta-0.3" function version () { echo "$global_script_name, version $global_script_version" } function usage () { version echo echo "Information options:" echo " -help display this help info" echo " -version display version info" echo " -list-functions show available functions" echo " -all-functions-help show all functions and their help info - VERY LONG" echo #wrap guide for 80 columns | echo "To get the help information of a function, run it without any additional" echo " arguments." echo echo "If the first argument is not recognized, all functions that start with the" echo " argument are displayed." echo } #the main function is so that we can put the main code before other function definitions #it simply gets called as 'main "$@"' after all functions are defined function main () { #NOTE: if we have a lot of functions, check_functions could cause a short startup delay, could make a debug setting to control it check_functions if (($# < 1)) then usage exit 0 fi case "$1" in (-version) version ;; (-help) usage ;; (-list-functions) list_functions ;; (-all-functions-help) all_functions_help ;; (*) #magic switch matching and function name conversion happens in here do_operation "$@" ;; esac } #automagic helpers for function matching #NOTE: mac still ships with a version of bash from 2007(!) #so, don't use associative arrays or anything fancy declare -a global_switch declare -a global_descrip function create_function () { local function_switch="$1" shift if [[ "$function_switch" == *' '* ]] then echo "ASSERT FAILURE: switch '$function_switch' contains space(s)" exit 1 fi if [[ "$function_switch" != "-"* ]] then echo "ASSERT FAILURE: switch '$function_switch' has no leading dash" exit 1 fi if [[ "$*" == "" ]] then echo "ASSERT FAILURE: switch '$function_switch' has empty short description" exit 1 fi #use $* in case they didn't quote the short description global_switch+=("$function_switch") global_descrip+=("$*") } function switch_to_func_name () { printf '%s' "$1" | sed 's/^-//' | sed 's/-/_/g' } #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 create_function "-border-file-concatenate" "MERGE BORDER FILES" function border_file_concatenate () { local function_switch="$1" shift if (($# < 1)) then echo "`switch_to_descrip $function_switch`" echo " $global_script_name $function_switch" echo " " echo " " echo " ..." echo #wrap guide for 80 columns | echo " Additional input files may be specified after the mandatory input file." echo return 0 fi if (($# < 2)) then error "function requires 2 or more arguments" fi local -a merge_arg_array for ((i = 2; i <= $#; ++i)) do merge_arg_array+=("-border" "${!i}") done wb_command -border-merge "$1" "${merge_arg_array[@]}" } create_function "-cifti-concatenate" "MERGE MAPS OF CIFTI FILES" function cifti_concatenate () { local function_switch="$1" shift if (($# < 1)) then echo "`switch_to_descrip $function_switch`" echo " $global_script_name $function_switch" echo " [-map ] - use only the specified map from each input" echo " file" echo " " echo " " 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 return 0 fi local -a maparg if [[ "$1" == "-map" ]] then if (($# < 2)) then error "-map option requires an argument" fi maparg=("-column" "$2") shift 2 fi if (($# < 2)) then error "function requires 2 or more arguments" fi local -a merge_arg_array for ((i = 2; i <= $#; ++i)) do merge_arg_array+=("-cifti" "${!i}" ${maparg[@]+"${maparg[@]}"}) done wb_command -cifti-merge "$1" "${merge_arg_array[@]}" } create_function "-cifti-demean" "DEMEAN/NORMALIZE AND CONCATENATE" function cifti_demean () { local function_switch="$1" shift if (($# < 1)) then echo "`switch_to_descrip $function_switch`" echo " $global_script_name $function_switch" echo " [-normalize] - also normalize input files" echo " " echo " " echo " ..." echo #wrap guide for 80 columns | echo " Demeans each input file (optionally normalizes by stdev) and then" echo " concatenates them. Additional input files may be specified after the" echo " mandatory input file." echo return 0 fi local normalize=0 if [[ "$1" == "-normalize" ]] then normalize=1 shift fi if (($# < 2)) then error "function requires 2 or more arguments" fi local -a merge_arg_array for ((i = 2; i <= $#; ++i)) do #we might not make the stdev temporary, but it isn't a problem to add it anyway add_temporary_files "$1.temp${i}-mean-$$" "$1.temp${i}-normed-$$" "$1.temp${i}-stdev-$$" wb_command -cifti-reduce "${!i}" MEAN "$1.temp${i}-mean-$$" if [[ $normalize == 1 ]] then wb_command -cifti-reduce "${!i}" SAMPSTDEV "$1.temp${i}-stdev-$$" wb_command -cifti-math '(x - mean) / stdev' "$1.temp${i}-normed-$$" -fixnan 0 \ -var x "${!i}" \ -var mean "$1.temp${i}-mean-$$" -select 1 1 -repeat \ -var stdev "$1.temp${i}-stdev-$$" -select 1 1 -repeat rm -f "$1.temp${i}-mean-$$" "$1.temp${i}-stdev-$$" else wb_command -cifti-math 'x - mean' "$1.temp${i}-normed-$$" -fixnan 0 \ -var x "${!i}" \ -var mean "$1.temp${i}-mean-$$" -select 1 1 -repeat rm -f "$1.temp${i}-mean-$$" fi merge_arg_array+=("-cifti" "$1.temp${i}-normed-$$") done wb_command -cifti-merge "$1" "${merge_arg_array[@]}" #let the exit hook clean up the normed temporaries, we're done } create_function "-cifti-dlabel-split-cortex" "SEPARATE SURFACE LABELS INTO LEFT/RIGHT" function cifti_dlabel_split_cortex () { local function_switch="$1" shift if (($# < 1)) then echo "`switch_to_descrip $function_switch`" echo " $global_script_name $function_switch" echo " - input dlabel file" echo " - output - output dlabel file" echo #wrap guide for 80 columns | echo " For every label represented on left or right cortex, rename it with a" echo " prefix of 'L_' or 'R_', and change the label values as needed to keep" echo " the new names separate." echo return 0 fi if (($# != 2)) then error "function requires 2 arguments, $# provided" fi #to prevent it from also changing label keys in a multi-map dlabel (because gifti doesn't support label tables per-map), #we need to loop through the columns local i num_maps=`wb_command -file-information -only-number-of-maps "$1"` local -a mergeargs for ((i = 1; i <= num_maps; ++i)) do add_temporary_files "$2".temp$i.dlabel.nii "$2".temp$i.L.label.gii "$2".temp$i.R.label.gii "$2".temp$i.nii.gz add_temporary_files "$2".temp$i.L.txt "$2".temp$i.R.txt "$2".temp$i.vol.txt wb_command -cifti-merge "$2".temp$i.dlabel.nii -cifti "$1" -column $i wb_command -cifti-separate "$2".temp$i.dlabel.nii COLUMN -label CORTEX_LEFT "$2".temp$i.L.label.gii -label CORTEX_RIGHT "$2".temp$i.R.label.gii -volume-all "$2".temp$i.nii.gz -crop #these are temporary files and will be deleted regardless, go ahead and overwrite them #LEFT wb_command -gifti-label-add-prefix "$2".temp$i.L.label.gii L_ "$2".temp$i.L.label.gii #need to remove the unused ones from the table so we don't get stupid overlaps wb_command -label-export-table "$2".temp$i.L.label.gii "$2".temp$i.L.txt #mute the warning from loading a label as a metric wb_command -metric-label-import "$2".temp$i.L.label.gii \ "$2".temp$i.L.txt \ "$2".temp$i.L.label.gii \ -drop-unused-labels 2> /dev/null #RIGHT wb_command -gifti-label-add-prefix "$2".temp$i.R.label.gii R_ "$2".temp$i.R.label.gii #need to remove the unused ones from the table so we don't get stupid overlaps wb_command -label-export-table "$2".temp$i.R.label.gii "$2".temp$i.R.txt #mute the warning from loading a label as a metric wb_command -metric-label-import "$2".temp$i.R.label.gii \ "$2".temp$i.R.txt \ "$2".temp$i.R.label.gii \ -drop-unused-labels 2> /dev/null #VOLUME #need to also remove unused labels from this, too wb_command -volume-label-export-table "$2".temp$i.nii.gz 1 "$2".temp$i.vol.txt #no warning to mute wb_command -volume-label-import "$2".temp$i.nii.gz \ "$2".temp$i.vol.txt \ "$2".temp$i.nii.gz \ -drop-unused-labels wb_command -cifti-create-dense-from-template "$2".temp$i.dlabel.nii "$2".temp$i.dlabel.nii -label CORTEX_LEFT "$2".temp$i.L.label.gii -label CORTEX_RIGHT "$2".temp$i.R.label.gii -volume-all "$2".temp$i.nii.gz -from-cropped rm -f "$2".temp$i.L.label.gii "$2".temp$i.R.label.gii "$2".temp$i.nii.gz rm -f "$2".temp$i.L.txt "$2".temp$i.R.txt "$2".temp$i.vol.txt mergeargs+=(-cifti "$2".temp$i.dlabel.nii) done wb_command -cifti-merge "$2" "${mergeargs[@]}" #let the cleanup function remove the intermediate cifti files } create_function "-command-help-search" "SEARCH WB_COMMAND HELP" function command_help_search () { local function_switch="$1" shift if (($# < 1)) then echo "`switch_to_descrip $function_switch`" echo " $global_script_name $function_switch" echo " [-also-deprecated] - also search deprecated commands" echo " - grep pattern to search for" echo #wrap guide for 80 columns | echo " Searches for wb_command processing commands that contain the pattern." echo return 0 fi local -a switches readarray -t switches < <(wb_command -list-commands | awk '{print $1}') if [[ "$1" == "-also-deprecated" ]] then shift local -a depswitches readarray -t depswitches < <(wb_command -list-deprecated-commands | awk '{print $1}') switches+=("${depswitches[@]}") fi #we could add a -- option to denote end of options, but then it would be harder to search for -- rather than for an option if (($# < 1)) then error "function requires an argument" fi #requiring quotes when searching for a phrase is slightly inconvenient #on the other hand, a phrase could get split across lines, so that is error prone anyway #if (($# != 1)) #then # warning "more than one argument provided, check for unquoted spaces and spelling of options" #fi local i for ((i = 0; i < ${#switches[@]}; ++i)) do #use $* in case someone didn't quote, but keep it all in the pattern argument local matches=`wb_command "${switches[$i]}" | grep -i -e "$*"` if [[ "$matches" != "" ]] then printf '%s\n' "${switches[$i]}:" printf '%s\n\n' "$matches" fi done } create_function "-freesurfer-resample-prep" "CREATE MIDTHICKNESSES FROM FREESURFER" function freesurfer_resample_prep () { local function_switch="$1" shift if (($# < 1)) then echo "`switch_to_descrip $function_switch`" echo " $global_script_name $function_switch" echo " - the freesurfer white surface" echo " - the freesurfer pial surface" echo " " echo " " echo " - output - the midthickness on the current" echo " freesurfer mesh, in gifti format" echo " - output - likewise, on the new mesh" echo " - output - the freesurfer sphere converted to" echo " gifti, must end in '.surf.gii'" echo #wrap guide for 80 columns | echo " NOTE: freesurfer's mris_convert must be installed and in the \$PATH in" echo " order to use this function, for converting the surfaces to GIFTI format." echo echo " Generate the various surface files used for resampling individual data" echo " from FreeSurfer to fs_LR. This generates the gifti-format sphere, and" echo " both midthickness surfaces needed by the -area-surfs option of wb_command" echo " -metric-resample, -label-resample, and similar commands." echo return 0 fi if (($# != 7)) then error "function requires 7 arguments, $# provided" fi if [[ "$7" != *.surf.gii ]] then error " filename must end in '.surf.gii'" fi add_temporary_files "$5.temp$$.white.surf.gii" "$5.temp$$.pial.surf.gii" if [[ "$5" == */* ]] then #if output name includes a path (even relative), then we don't need to futz with it to prevent freesurfer from dumping the converted file in the INPUT file's directory mris_convert "$1" "$5.temp$$.white.surf.gii" mris_convert "$2" "$5.temp$$.pial.surf.gii" else #if you give freesurfer a bare filename with no path for output, it dumps it in the input directory, regardless of cwd #so, put ./ on the beginning to tell it to mend its ways mris_convert "$1" ./"$5.temp$$.white.surf.gii" mris_convert "$2" ./"$5.temp$$.pial.surf.gii" fi mris_convert "$3" "$7" wb_command -surface-average "$5" -surf "$5.temp$$.white.surf.gii" -surf "$5.temp$$.pial.surf.gii" rm -f "$5.temp$$.white.surf.gii" "$5.temp$$.pial.surf.gii" wb_command -surface-resample "$5" "$7" "$4" BARYCENTRIC "$6" } create_function "-import-probtrack-roi" "CONVERT ROI TRACKS TO CIFTI" function import_probtrack_roi () { local function_switch="$1" shift if (($# < 1)) then echo "`switch_to_descrip $function_switch`" echo " $global_script_name $function_switch" echo " - the text file from probtrackx2" echo " - the ROI used as the seed mask, as cifti" echo " - output - the data converted to cifti dscalar" echo #wrap guide for 80 columns | echo " The file should contain the ROI used as the mask, and should" echo " be in the desired brainordinate space." echo return 0 fi if (($# != 3)) then error "function requires 3 arguments, $# provided" fi #make temporaries based on the output file name add_temporary_files "$3.temp1-$$" "$3.temp2-$$" wb_command -cifti-restrict-dense-map "$2" COLUMN "$3.temp1-$$" -cifti-roi "$2" wb_command -cifti-convert -from-text "$1" "$3.temp1-$$" "$3.temp2-$$" -reset-scalars rm -f "$3.temp1-$$" wb_command -cifti-create-dense-from-template "$2" "$3" -cifti "$3.temp2-$$" rm -f "$3.temp2-$$" } create_function "-label-concatenate" "MERGE MAPS OF LABEL FILES" function label_concatenate () { local function_switch="$1" shift if (($# < 1)) then echo "`switch_to_descrip $function_switch`" echo " $global_script_name $function_switch" echo " [-map ] - use only the specified map from each input" echo " file" echo " " echo " " 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 return 0 fi local -a maparg if [[ "$1" == "-map" ]] then if (($# < 2)) then error "-map option requires an argument" fi maparg=("-column" "$2") shift 2 fi if (($# < 2)) then error "function requires 2 or more arguments" fi local -a merge_arg_array local i for ((i = 2; i <= $#; ++i)) do merge_arg_array+=("-label" "${!i}" ${maparg[@]+"${maparg[@]}"}) done wb_command -label-merge "$1" "${merge_arg_array[@]}" } create_function "-metric-concatenate" "MERGE MAPS OF METRIC FILES" function metric_concatenate () { local function_switch="$1" shift if (($# < 1)) then echo "`switch_to_descrip $function_switch`" echo " $global_script_name $function_switch" echo " [-map ] - use only the specified map from each input" echo " file" echo " " echo " " 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 return 0 fi local -a maparg if [[ "$1" == "-map" ]] then if (($# < 2)) then error "-map option requires an argument" fi maparg=("-column" "$2") shift 2 fi if (($# < 2)) then error "function requires 2 or more arguments" fi local -a merge_arg_array local i for ((i = 2; i <= $#; ++i)) do merge_arg_array+=("-metric" "${!i}" ${maparg[@]+"${maparg[@]}"}) done wb_command -metric-merge "$1" "${merge_arg_array[@]}" } create_function "-scene-file-concatenate" "MERGE SCENES OF SCENE FILES" function scene_file_concatenate () { local function_switch="$1" shift if (($# < 1)) then echo "`switch_to_descrip $function_switch`" echo " $global_script_name $function_switch" echo " " echo " " echo " ..." echo #wrap guide for 80 columns | echo " Additional input files may be specified after the mandatory input file." echo return 0 fi if (($# < 2)) then error "function requires 2 or more arguments" fi local -a merge_arg_array local i for ((i = 2; i <= $#; ++i)) do merge_arg_array+=("-scene-file" "${!i}") done wb_command -scene-file-merge "$1" "${merge_arg_array[@]}" } create_function "-volume-concatenate" "MERGE MAPS OF VOLUME FILES" function volume_concatenate () { local function_switch="$1" shift if (($# < 1)) then echo "`switch_to_descrip $function_switch`" echo " $global_script_name $function_switch" echo " [-map ] - use only the specified map from each input" echo " file" echo " " echo " " 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 return 0 fi local -a maparg if [[ "$1" == "-map" ]] then if (($# < 2)) then error "-map option requires an argument" fi maparg=("-subvolume" "$2") shift 2 fi if (($# < 2)) then error "function requires 2 or more arguments" 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[@]}"}) done wb_command -volume-merge "$1" "${merge_arg_array[@]}" } #additional helper functions function do_operation () { #use the existence of a short description to denote existence of the function local i for ((i = 0; i < ${#global_switch[@]}; ++i)) do if [[ "${global_switch[$i]}" == "$1" ]] then `switch_to_func_name "$1"` "$@" return $? fi done local maxlength=0 for ((i = 0; i < ${#global_switch[@]}; ++i)) do if [[ "${global_switch[$i]}" == "$1"* ]] then local thislength=`printf '%s' "${global_switch[$i]}" | wc -c` if (( thislength > maxlength )) then maxlength=$thislength fi fi done if (( maxlength == 0 )) then error "the switch '$1' does not match any functions" fi for ((i = 0; i < ${#global_switch[@]}; ++i)) do if [[ "${global_switch[$i]}" == "$1"* ]] then printf "%-${maxlength}s %s\n" "${global_switch[$i]}" "${global_descrip[$i]}" fi done | sort } function switch_to_descrip () { #because we aren't using associative arrays, because mac's ancient bash local i for ((i = 0; i < ${#global_switch[@]}; ++i)) do if [[ "${global_switch[$i]}" == "$1" ]] then printf '%s' "${global_descrip[$i]}" return 0 fi done return 1 } function list_functions () { local i maxlength=0 for ((i = 0; i < ${#global_switch[@]}; ++i)) do local thislength=`printf '%s' "${global_switch[$i]}" | wc -c` if (( thislength > maxlength )) then maxlength=$thislength fi done for ((i = 0; i < ${#global_switch[@]}; ++i)) do printf "%-${maxlength}s %s\n" "${global_switch[$i]}" "${global_descrip[$i]}" done | sort } function all_functions_help () { #assume that the commands all print their help info when given no additional arguments besides the command switch itself local i for ((i = 0; i < ${#global_switch[@]}; ++i)) do `switch_to_func_name "${global_switch[$i]}"` "${global_switch[$i]}" done } function error () { echo >&2 echo "While running:" echo "$global_cmd_line" >&2 echo >&2 echo "Error: $*" >&2 echo >&2 exit 1 } function warning () { echo >&2 echo "Warning: $*" >&2 echo >&2 } function check_functions () { local i for ((i = 0; i < ${#global_switch[@]}; ++i)) do local function_name=`switch_to_func_name "${global_switch[$i]}"` if [[ `type -t "$function_name"` != 'function' ]] then echo "ASSERT FAILURE: switch '${global_switch[$i]}' does not have matching function '$function_name'" exit 1 fi done } global_temporary_files=() function add_temporary_files () { global_temporary_files+=("$@") } function cleanup () { #unset variable gets tripped by @ expansion on empty array #so first test if the array is empty if ((${#global_temporary_files[@]} > 0)) then rm -f -- "${global_temporary_files[@]}" &> /dev/null fi } trap cleanup EXIT #start the main code, now that all the functions are defined main "$@" connectome-workbench-1.2.3+git41-gc4c6c90/src/Commands/000077500000000000000000000000001300200146000223055ustar00rootroot00000000000000connectome-workbench-1.2.3+git41-gc4c6c90/src/Commands/CMakeLists.txt000066400000000000000000000023601300200146000250460ustar00rootroot00000000000000# # Name of project # PROJECT (Commands) # # Need XML from Qt # SET(QT_DONT_USE_QTGUI) # # Add QT for includes # INCLUDE (${QT_USE_FILE}) # # Create the brain library # ADD_LIBRARY(Commands CommandClassAddMember.h CommandClassCreate.h CommandClassCreateAlgorithm.h CommandClassCreateBase.h CommandClassCreateEnum.h CommandClassCreateOperation.h CommandC11xTesting.h CommandException.h CommandOperation.h CommandOperationManager.h CommandParser.h CommandUnitTest.h CommandClassAddMember.cxx CommandClassCreate.cxx CommandClassCreateAlgorithm.cxx CommandClassCreateBase.cxx CommandClassCreateEnum.cxx CommandClassCreateOperation.cxx CommandC11xTesting.cxx CommandException.cxx CommandOperation.cxx CommandOperationManager.cxx CommandParser.cxx CommandUnitTest.cxx ) # # Find Headers # INCLUDE_DIRECTORIES( ${CMAKE_SOURCE_DIR}/Commands ${CMAKE_SOURCE_DIR}/Algorithms ${CMAKE_SOURCE_DIR}/Annotations ${CMAKE_SOURCE_DIR}/Charting ${CMAKE_SOURCE_DIR}/Operations ${CMAKE_SOURCE_DIR}/OperationsBase ${CMAKE_SOURCE_DIR}/Brain ${CMAKE_SOURCE_DIR}/FilesBase ${CMAKE_SOURCE_DIR}/Files ${CMAKE_SOURCE_DIR}/Gifti ${CMAKE_SOURCE_DIR}/Cifti ${CMAKE_SOURCE_DIR}/Palette ${CMAKE_SOURCE_DIR}/Nifti ${CMAKE_SOURCE_DIR}/Scenes ${CMAKE_SOURCE_DIR}/Xml ${CMAKE_SOURCE_DIR}/Common ) connectome-workbench-1.2.3+git41-gc4c6c90/src/Commands/CommandC11xTesting.cxx000066400000000000000000000104611300200146000264040ustar00rootroot00000000000000/*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 // Not on Intel #include #include #include "CaretAssert.h" #include "CommandC11xTesting.h" #include "ProgramParameters.h" using namespace caret; /** * Constructor. */ CommandC11xTesting::CommandC11xTesting() : CommandOperation("-c11x-test", "C11X Compiler Compatibility Testing") { } /** * Destructor. */ CommandC11xTesting::~CommandC11xTesting() { } AString CommandC11xTesting::getHelpInformation(const AString& /*programName*/) { AString helpInfo = ("\n" "Test for C11x support in compilers.\n" "\n" "Usage: \n" " \n" ); return helpInfo; } /** * Execute the operation. * * @param parameters * Parameters for the operation. * @throws CommandException * If the command failed. * @throws ProgramParametersException * If there is an error in the parameters. */ void CommandC11xTesting::executeOperation(ProgramParameters& /*parameters*/) { #ifdef WORKBENCH_HAVE_C11X Cpp11xTesting cppx; cppx.test(); #endif // WORKBENCH_HAVE_C11X } #ifdef WORKBENCH_HAVE_C11X /** * Constructor. * * Tests delegating constructor (calls another constructor within same class) * Not supported by Intel compiler */ Cpp11xTesting::Cpp11xTesting() /*: Cp11xTesting(5)*/ { } /** * Constructor. */ Cpp11xTesting::Cpp11xTesting(const int /*value*/) { } /** * Destructor. */ Cpp11xTesting::~Cpp11xTesting() { } /** * Test 'noexcept' keyword indicating no exception is thrown * Not supported by Intel Compiler */ //void //Cpp11xTesting::methodName() noexcept() //{ // //} void Cpp11xTesting::test() { std::cout << "Initialized value of X: " << m_x << std::endl; std::cout << std::endl; /* * Space no longer needed between consecutive '>' */ std::vector> vvi; /* * Initialization list for vector * Not supported by Intel Compiler */ std::vector values; // { 3, 8, 5 }; values.push_back(3); values.push_back(8); values.push_back(5); /* * Iterator over vector using both 'auto' and the new iterator */ std::cout << "Vector auto and new iterator: "; for (auto i : values) { std::cout << i << " "; } std::cout << std::endl; /* * 'nullptr' instead of NULL or zero. */ float* fptr = nullptr; if (fptr == nullptr) { std::cout << "nullptr works" << std::endl; } /* * Array initialization, new style * Not supported by Intel compiler */ //int a[] { 1, 3, 5 }; int a[] = { 1, 3, 5 }; /* * Iteration over array using both 'auto' and the new iterator */ std::cout << "Array auto and new iterator: "; for (auto i : a) { std::cout << i << " "; } std::cout << std::endl; /* * Tuple * Not supported by Intel compiler */ //std::tuple tp(5, 9.1, 12.5); //std::cout << "Tuple 2nd value: " << std::get<1>(tp) << std::endl; /* * Test Lambda */ auto l = [] (const char* s) { std::cout << "Lambda: " << s << std::endl; }; l("lambda test"); } /** * 'override' keyword would cause an error if superclass did not have methodName() * Not supported by Intel compiler */ //void //SubClass:methodName() override noexcept //{ // //} #endif // WORKBENCH_HAVE_C11X connectome-workbench-1.2.3+git41-gc4c6c90/src/Commands/CommandC11xTesting.h000066400000000000000000000046021300200146000260310ustar00rootroot00000000000000#ifndef __COMMAND_C11X_TESTING__H__ #define __COMMAND_C11X_TESTING__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 "CommandOperation.h" namespace caret { /// Command that adds members and getter/setter methods to a class class CommandC11xTesting : public CommandOperation { public: CommandC11xTesting(); virtual ~CommandC11xTesting(); virtual void executeOperation(ProgramParameters& parameters); AString getHelpInformation(const AString& /*programName*/); private: CommandC11xTesting(const CommandC11xTesting&); CommandC11xTesting& operator=(const CommandC11xTesting&); }; #ifdef WORKBENCH_HAVE_C11X class Cpp11xTesting { public: Cpp11xTesting(); Cpp11xTesting(const int value); // Intel does not suport 'noexcept' keyword //virtual void methodName() noexcept; virtual ~Cpp11xTesting(); void test(); private: Cpp11xTesting(const Cpp11xTesting&); Cpp11xTesting& operator=(const Cpp11xTesting&); /* * Initialization of a member's value. */ int m_x = 5; }; class SubClass : public Cpp11xTesting { SubClass() : Cpp11xTesting(5) { } virtual ~SubClass() { } // Intel does not support 'override' keyword //virtual void methodName() override noexcept; }; #endif // WORKBENCH_HAVE_C11X } // namespace #endif // __COMMAND_C11X_TESTING__H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Commands/CommandClassAddMember.cxx000066400000000000000000000442331300200146000271440ustar00rootroot00000000000000/*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 "CaretAssert.h" #include "CommandClassAddMember.h" #include "CommandClassCreate.h" #include "DataFileException.h" #include "FileInformation.h" #include "ProgramParameters.h" #include "TextFile.h" using namespace caret; /** * Constructor. */ CommandClassAddMember::CommandClassAddMember() : CommandOperation("-class-add-member", "ADD MEMBER AND GETTER/SETTER TO SOURCE CODE FILES (.h and .cxx)") { } /** * Destructor. */ CommandClassAddMember::~CommandClassAddMember() { } AString CommandClassAddMember::getHelpInformation(const AString& /*programName*/) { AString helpInfo = ("\n" "Add members to class header (.h) and implementation (.cxx) files.\n" "\n" "Usage: \n" " [-add-to-files]\n" " [-m ]...\n" "\n" " If the -add-to-files is not specified, the code for the\n" " header and implementation files is printed to the \n" " terminal.\n" "\n" " If the -add-to-files is specified, the class files are\n" " expected to be in the current directory and named\n" " .h and .cxx. The header \n" " file must contain this text in its private section:\n" " " + CommandClassCreate::getNewMembersString() + "\n" " The implementation file must contain this text in\n" " its public section:\n" " " + CommandClassCreate::getNewMethodsString() + "\n" " If either of these text string are missing, the code \n" " that would have been added to the file(s) is printed\n" " to the terminal.\n" " \n" " For each member, three text strings separated by a space\n" " must be provided and they are the name of the member\n" " its data type, and a description of the member. If the\n" " description contains spaces the description must be\n" " enclosed in double quotes (\"\").\n" " \n" " If the data type begins with a capital letter, it is\n" " assumed to be the name of a class. In this case, both\n" " const and non-const getters are created but not setter\n" " is created. Otherwise, the data type is expected to be\n" " a primitive type and both a getter and a setter are\n" " created. Note that AString and QString are treated as\n" " primitive types.\n" " \n" ); return helpInfo; } /** * Execute the operation. * * @param parameters * Parameters for the operation. * @throws CommandException * If the command failed. * @throws ProgramParametersException * If there is an error in the parameters. */ void CommandClassAddMember::executeOperation(ProgramParameters& parameters) { bool isAddToFiles = false; AString headerMemberCode = ""; AString headerMethodCode = ""; AString implementationCode = ""; const AString indentText = " "; /* * Process parameters */ const AString className = parameters.nextString("Class Name"); while (parameters.hasNext()) { const AString param = parameters.nextString(""); if (param == "-add-to-files") { isAddToFiles = true; } else if (param == "-m") { /* * Adding a member */ AString name = parameters.nextString("Member name"); if (name.indexOf(" ") >= 0) { throw CommandException("Member named \"" + name + "\" cannot contain spaces."); } if (name.isEmpty()) { throw CommandException("A member name is empty."); } else { if (name[0].isLetter() == false) { throw CommandException("Member named \"" + name + "\" must begin with a letter."); } if (name.indexOf(" ") >= 0) { throw CommandException("Member named \"" + name + "\" cannot contain spaces."); } } AString dataType = parameters.nextString("Data type for member " + name); if (dataType.isEmpty()) { throw CommandException("A data type is empty."); } if (dataType.indexOf(" ") >= 0) { throw CommandException("Data Type named \"" + name + "\" cannot contain spaces."); } const AString description = parameters.nextString("Description for member " + name); /* * If first letter of data type is upper-case, assume it * is a class and use a pointer for the class. However, * treat AString and QString as primitive types. */ bool isString = false; bool isClass = false; const QChar firstDataTypeChar = dataType[0]; if (firstDataTypeChar.isUpper()) { if ((dataType == "AString") || (dataType == "QString")) { isString = true; } else { isClass = true; } } const AString returnType = dataType + (isClass ? "*" : ""); /* * Determine partial name for getter and setter * handling removal or addition of "m_" for * member name. */ AString getSetName = name; AString parameterName = name; if (name.startsWith("m_")) { getSetName = name.mid(2); parameterName = name.mid(2); } else { name = "m_" + name; } /* * Create the names for the getter and setter methods */ QChar firstGetSetNameChar = getSetName[0]; if (firstGetSetNameChar.isLower()) { firstGetSetNameChar = firstGetSetNameChar.toUpper(); getSetName[0] = firstGetSetNameChar; } const AString getterName = ("get" + getSetName); const AString setterName = ("set" + getSetName); /* * Declare the member for the header file */ if (description.isEmpty() == false) { headerMemberCode += (indentText + "/** " + description + "*/\n"); } headerMemberCode += (indentText + returnType + " " + name + ";" + "\n" + "\n"); const AString classColonName = (className + "::"); if (isClass) { /* * For class members, create only getter methods * that are both const and non-const */ for (int32_t i = 0; i < 2; i++) { const bool isConstMethod = (i == 0); headerMethodCode += (indentText + (isConstMethod ? "const " : "") + returnType + " " + getterName + "()" + (isConstMethod ? " const" : "") + ";\n" + "\n"); implementationCode += ("/**\n" " * @return " + description + "\n" " */\n" + (isConstMethod ? "const " : "") + returnType + "\n" + classColonName + getterName + "()" + (isConstMethod ? " const" : "") + "\n" + "{\n" + " return " + name + ";\n" + "}\n" + "\n"); } } else { /* * Getter and setter in header file */ headerMethodCode += (indentText + returnType + " " + getterName + "() const;\n" + "\n"); headerMethodCode += (indentText + "void " + setterName + "(const " + dataType + (isString ? "& " : " ") + parameterName + ");\n" + "\n"); /* * Getter and setter in implementation file */ implementationCode += ("/**\n" " * @return " + description + "\n" " */\n" + returnType + "\n" + classColonName + getterName + "() const\n" + "{\n" + " return " + name + ";\n" + "}\n" + "\n"); implementationCode += ("/**\n" " * Set " + description + "\n" " * @param " + parameterName + "\n" " * New value for " + description + "\n" " */\n" + "void\n" + classColonName + setterName + "(const " + dataType + (isString ? "& " : " ") + parameterName + ")\n" + "{\n" + " " + name + " = " + parameterName + ";\n" + "}\n" + "\n"); } } else { throw ProgramParametersException("Unrecognized parameter: " + param); } } if (headerMemberCode.isEmpty()) { throw CommandException("No members were specified."); } AString errorMessage; bool isWriteToTerminal = true; if (isAddToFiles) { const AString headerFileName = className + ".h"; const AString implementationFileName = className + ".cxx"; FileInformation headerInfo(headerFileName); if (headerInfo.exists() == false) { errorMessage += headerFileName + " was not found."; } FileInformation impInfo(implementationFileName); if (impInfo.exists() == false) { errorMessage += implementationFileName + " was not found."; } /* * Copy existing files to the temporary directory */ QDir tempDir = QDir::temp(); AString tempHeaderFileName = tempDir.absoluteFilePath(headerFileName); AString tempImplementationFileName = tempDir.absoluteFilePath(implementationFileName); try { TextFile headerTextFile; headerTextFile.readFile(headerFileName); headerTextFile.writeFile(tempHeaderFileName); TextFile implementationTextFile; implementationTextFile.readFile(implementationFileName); implementationTextFile.writeFile(tempImplementationFileName); std::cout << "Backup of header file: " << qPrintable(tempHeaderFileName) << std::endl; std::cout << "Backup of implementation file: " << qPrintable(tempImplementationFileName) << std::endl; AString headerText = headerTextFile.getText(); const int newMemberOffset = findInsertionOffset(headerText, CommandClassCreate::getNewMembersString()); if (newMemberOffset >= 0) { headerMemberCode += "\n"; headerText.insert(newMemberOffset, headerMemberCode); } else { if (errorMessage.isEmpty()) { errorMessage += "\n"; } errorMessage += ("Unable to insert new members in header file. Did not find text " + CommandClassCreate::getNewMembersString() + " in the file " + headerFileName); } const int newMethodDeclareOffset = findInsertionOffset(headerText, CommandClassCreate::getNewMethodsString()); if (newMethodDeclareOffset >= 0) { headerMethodCode += "\n"; headerText.insert(newMethodDeclareOffset, headerMethodCode); } else { if (errorMessage.isEmpty()) { errorMessage += "\n"; } errorMessage += ("Unable to insert new methods in header file. Did not find text " + CommandClassCreate::getNewMethodsString() + " in the file " + headerFileName); } if ((newMemberOffset >= 0) && (newMethodDeclareOffset >= 0)) { headerTextFile.replaceText(headerText); isWriteToTerminal = false; headerTextFile.writeFile(headerFileName); } implementationCode += "\n"; implementationTextFile.addText("\n" + implementationCode); implementationTextFile.writeFile(implementationFileName); } catch (const DataFileException& dfe) { throw CommandException(dfe); } } if (isWriteToTerminal) { std::cout << std::endl; std::cout << "Header Code Getter Setter -------------------------------------" << std::endl; std::cout << qPrintable(headerMethodCode) << std::endl; std::cout << "Header Code Declaration ---------------------------------------" << std::endl; std::cout << qPrintable(headerMemberCode) << std::endl; std::cout << "Implementation Code -------------------------------------------" << std::endl; std::cout << qPrintable(implementationCode) << std::endl; } if (errorMessage.isEmpty() == false) { throw CommandException(errorMessage); } } /** * Find the offset of the new line or carriage return in the given text * using the given search text. Offset is first character after the * new line or carriage return. * * @param text * Text that is searched. * @param searchForText * Text that is searched for. * @return * Offset of the search text in the text or negative if * the search text is not found. */ int CommandClassAddMember::findInsertionOffset(const AString& text, const AString& searchForText) { int indx = text.indexOf(searchForText); if (indx >= 0) { while (indx >= 0) { const QChar ch = text[indx]; if ((ch == '\n') || (ch == '\r')) { indx++; break; } indx--; } return indx; } return -1; } connectome-workbench-1.2.3+git41-gc4c6c90/src/Commands/CommandClassAddMember.h000066400000000000000000000032671300200146000265730ustar00rootroot00000000000000#ifndef __COMMAND_CLASS_ADD_MEMBER__H__ #define __COMMAND_CLASS_ADD_MEMBER__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 "CommandOperation.h" namespace caret { /// Command that adds members and getter/setter methods to a class class CommandClassAddMember : public CommandOperation { public: CommandClassAddMember(); virtual ~CommandClassAddMember(); virtual void executeOperation(ProgramParameters& parameters); AString getHelpInformation(const AString& /*programName*/); private: CommandClassAddMember(const CommandClassAddMember&); CommandClassAddMember& operator=(const CommandClassAddMember&); int findInsertionOffset(const AString& text, const AString& searchForText); }; } // namespace #endif // __COMMAND_CLASS_ADD_MEMBER__H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Commands/CommandClassCreate.cxx000066400000000000000000000664661300200146000265430ustar00rootroot00000000000000/*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 "CaretAssertion.h" #include "CommandClassCreate.h" #include "DataFileException.h" #include "FileInformation.h" #include "ProgramParameters.h" #include "TextFile.h" using namespace caret; /** * Constructor. */ CommandClassCreate::CommandClassCreate() : CommandClassCreateBase("-class-create", "CREATE CLASS SOURCE CODE FILES (.h and .cxx)") { } /** * Destructor. */ CommandClassCreate::~CommandClassCreate() { } AString CommandClassCreate::getHelpInformation(const AString& /*programName*/) { AString helpInfo = ("\n" "Create class header (.h) and implementation (.cxx) files.\n" "\n" "Usage: \n" " [-copy] \n" " [-event-class ]\n" " [-event-listener] \n" " [-no-parent] \n" " [-parent ] \n" "\n" "\n" "Options: \n" " -copy\n" " Adds copy constructor and assignment operator\n" " \n" " -event-class \n" " When creating an Event subclass, using this\n" " option will automatically set the parent\n" " class to Event and place the given event\n" " enumerated type value into the parameter\n" " for the Event class constructor.\n" " \n" " For the there is no need\n" " to prepend it with \"EventTypeEnum::\".\n" " \n" " -event-listener \n" " Implement the EventListenerInterface so\n" " that the class may listen for events.\n" " \n" " -no-parent\n" " Created class is not derived from any other\n" " class. By default, the parent class is\n" " CaretObject.\n" " \n" " -parent \n" " Specify the parent (derived from) class.\n" " By default, the parent class is CaretObject.\n" " \n" " -scene\n" " Implement the SceneableInterface so that \n" " instances of the class can be restored from \n" " and saved to scenes. \n" " \n" " -scene-sub-class\n" " Adds methods that can be called by the super- \n" " class so that this sub-class can save and \n" " restore data to and from scenes. \n" " \n" " This option should only be used when creating\n" " a class whose super class implements the \n" " SceneableInterface\n" " \n" ); return helpInfo; } /** * Execute the operation. * * @param parameters * Parameters for the operation. * @throws CommandException * If the command failed. * @throws ProgramParametersException * If there is an error in the parameters. */ void CommandClassCreate::executeOperation(ProgramParameters& parameters) { const AString className = parameters.nextString("Class Name"); AString derivedFromClassName = "CaretObject"; AString eventTypeEnumName; bool hasCopyAndAssignment = false; bool hasEventListener = false; bool hasScenes = false; bool hasScenesSubClass = false; while (parameters.hasNext()) { const AString& param = parameters.nextString("Create Class Parameter"); if (param == "-copy") { hasCopyAndAssignment = true; } else if (param == "-event-class") { eventTypeEnumName = parameters.nextString("Event Type Enum Name"); if (eventTypeEnumName.contains("::") == false) { eventTypeEnumName.insert(0, "EventTypeEnum::"); } } else if (param == "-event-listener") { hasEventListener = true; } else if (param == "-parent") { derivedFromClassName = parameters.nextString("Parent Class Name"); if (derivedFromClassName.isEmpty()) { throw CommandException("Parent class name is empty."); } else { if (derivedFromClassName[0].isUpper() == false) { throw CommandException("Parent class name must begin with a Capital Letter"); } } } else if (param == "-no-parent") { derivedFromClassName = ""; } else if (param == "-scene") { hasScenes = true; } else if (param == "-scene-sub-class") { hasScenesSubClass = true; } else { throw CommandException("Invalid parameter: " + param); } } if (hasScenes && hasScenesSubClass) { throw CommandException("Only one, but not both scene options " "may be specified: " "-scene -scene-sub-class"); } if (className.isEmpty()) { throw CommandException("class name is empty."); } AString errorMessage; if (eventTypeEnumName.isEmpty() == false) { derivedFromClassName = "Event"; if (className.startsWith("Event") == false) { errorMessage += ("Event classes must being with \"Event\"\n"); } } if (className[0].isUpper() == false) { errorMessage += "First letter of class name must be upper case.\n"; } const AString headerFileName = className + ".h"; const AString implementationFileName = className + ".cxx"; FileInformation headerInfo(headerFileName); if (headerInfo.exists()) { errorMessage += headerFileName + " exists and this command will not overwrite it.\n"; } FileInformation impInfo(implementationFileName); if (impInfo.exists()) { errorMessage += implementationFileName + " exists and this command will not overwrite it.\n"; } if (errorMessage.isEmpty() == false) { throw CommandException(errorMessage); } AString ifndefName; AString ifdefNameStaticDeclarations; this->getIfDefNames(className, ifndefName, ifdefNameStaticDeclarations); this->createHeaderFile(headerFileName, className, derivedFromClassName, ifndefName, ifdefNameStaticDeclarations, hasCopyAndAssignment, hasEventListener, hasScenes, hasScenesSubClass); this->createImplementationFile(implementationFileName, className, derivedFromClassName, eventTypeEnumName, ifdefNameStaticDeclarations, hasCopyAndAssignment, hasEventListener, hasScenes, hasScenesSubClass); } /** * Create and write the header (.h) file. * * @param outputFileName * Name for file that is written. * @param className * Name of class. * @param derivedFromClassName * Name of class from which this class is derived. * @param ifdefName * Name of "ifndef" value. * @param ifdefNameStaticDeclaration * Name for "infdef" of static declarations. * @param hasCopyAndAssignment * Has copy constructor and assignment operator. * @param hasEventListener * Class implements the EventListener interface * @param hasSceneInterface * Class implements the SceneableInterface for scene support. * @param hasSubClassSceneSaving * Parent class implements the SceneableInterface so add methods * for saving sub-class data to scene that will be called by parent. */ void CommandClassCreate::createHeaderFile(const AString& outputFileName, const AString& className, const AString& derivedFromClassName, const AString& ifndefName, const AString& ifdefNameStaticDeclaration, const bool hasCopyAndAssignment, const bool hasEventListener, const bool hasSceneInterface, const bool hasSubClassSceneSaving) { AString t; AString derivedFromDeclaration; if (derivedFromClassName.isEmpty() == false) { derivedFromDeclaration = (" : public " + derivedFromClassName); } t += ("#ifndef " + ifndefName + "\n"); t += ("#define " + ifndefName + "\n"); t += this->getCopyright(); t += ("\n"); t += (this->getIncludeDeclaration(derivedFromClassName) + "\n"); t += ("\n"); if (hasEventListener) { t += (this->getIncludeDeclaration("EventListenerInterface") + "\n"); if (derivedFromDeclaration.isEmpty()) { derivedFromDeclaration += (" : "); } else { derivedFromDeclaration += (", "); } derivedFromDeclaration += ("public EventListenerInterface"); } if (hasSceneInterface) { t += (this->getIncludeDeclaration("SceneableInterface") + "\n"); if (derivedFromDeclaration.isEmpty()) { derivedFromDeclaration += (" : "); } else { derivedFromDeclaration += (", "); } derivedFromDeclaration += ("public SceneableInterface"); } t += ("\n"); t += ("\n"); t += ("namespace caret {\n"); if (hasSceneInterface) { t += (" class SceneClassAssistant;\n"); } t += ("\n"); t += (" class " + className + derivedFromDeclaration + " {\n"); t += (" \n"); if (derivedFromClassName.startsWith("Q") || derivedFromClassName.startsWith("WuQ")) { t += (" Q_OBJECT\n"); t += ("\n"); } t += (" public:\n"); t += (" " + className + "();\n"); t += (" \n"); t += (" virtual ~" + className + "();\n"); t += (" \n"); if (hasCopyAndAssignment) { t += (" " + className + "(const " + className + "& obj);\n"); t += ("\n"); t += (" " + className + "& operator=(const " + className + "& obj);\n"); 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 += ("\n"); t += (" " + getNewMethodsString() + "\n"); t += ("\n"); if (derivedFromClassName == "CaretObject") { t += (" virtual AString toString() const;\n"); t += (" \n"); } if (hasEventListener) { t += (" virtual void receiveEvent(Event* event);\n"); t += ("\n"); } if (hasSceneInterface) { t += (" virtual SceneClass* saveToScene(const SceneAttributes* sceneAttributes,\n"); t += (" const AString& instanceName);\n"); t += ("\n"); t += (" virtual void restoreFromScene(const SceneAttributes* sceneAttributes,\n"); t += (" const SceneClass* sceneClass);\n"); t += ("\n"); } if (hasSceneInterface || hasSubClassSceneSaving) { AString comment = ""; AString virtualZero =""; if (hasSceneInterface) { comment = "//"; virtualZero = " = 0"; } t += (" \n"); t += (" \n"); t += (" \n"); t += (" \n"); t += (" \n"); if (hasSceneInterface) { t += ("// If there will be sub-classes of this class that need to save\n"); t += ("// and restore data from scenes, these pure virtual methods can\n"); t += ("// be uncommented to force their implementation by sub-classes.\n"); } t += (comment + " protected: \n"); t += (comment + " virtual void saveSubClassDataToScene(const SceneAttributes* sceneAttributes,\n"); t += (comment + " SceneClass* sceneClass)" + virtualZero + ";\n"); t += (comment + "\n"); t += (comment + " virtual void restoreSubClassDataFromScene(const SceneAttributes* sceneAttributes,\n"); t += (comment + " const SceneClass* sceneClass)" + virtualZero + ";\n"); t += ("\n"); } t += (" private:\n"); if (hasCopyAndAssignment) { 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"); t += ("\n"); } t += (" " + getNewMembersString() + "\n"); t += ("\n"); t += (" };\n"); t += (" \n"); t += ("#ifdef " + ifdefNameStaticDeclaration + "\n"); t += (" // \n"); t += ("#endif // " + ifdefNameStaticDeclaration + "\n"); t += ("\n"); t += ("} // namespace\n"); t += ("#endif //" + ifndefName + "\n"); TextFile tf; tf.replaceText(t); try { tf.writeFile(outputFileName); } catch (const DataFileException& e) { throw CommandException(e); } } /** * Create and write the implementation (.cxx) file. * * @param outputFileName * Name for file that is written. * @param className * Name of class. * @param derivedFromClassName * Name of parent class * @param eventTypeEnumName * Name of event type enumerated type (if subclass of Event). * @param ifdefNameStaticDeclaration * Name for "infdef" of static declarations. * @param hasCopyAndAssignment * Has copy constructor and assignment operator. * @param hasEventListener * Class implements the EventListener interface * @param hasSceneInterface * Class implements the SceneableInterface for scene support. * @param hasSubClassSceneSaving * Parent class implements the SceneableInterface so add methods * for saving sub-class data to scene that will be called by parent. */ void CommandClassCreate::createImplementationFile(const AString& outputFileName, const AString& className, const AString& derivedFromClassName, const AString& eventTypeEnumName, const AString& ifdefNameStaticDeclaration, const bool hasCopyAndAssignment, const bool hasEventListener, const bool hasSceneInterface, const bool hasSubClassSceneSaving) { AString module; FileInformation dirInfo(QDir::currentPath()); if (dirInfo.exists()) { if (dirInfo.isDirectory()) { module = dirInfo.getFileName(); } } AString t; t += this->getCopyright(); t += ("#define " + ifdefNameStaticDeclaration + "\n"); t += ("#include \"" + className + ".h\"\n"); t += ("#undef " + ifdefNameStaticDeclaration + "\n"); t += ("\n"); t += (this->getIncludeDeclaration("CaretAssert") + "\n"); if (eventTypeEnumName.isEmpty() == false) { t += ("#include \"EventTypeEnum.h\"\n"); t += ("\n"); } if (hasEventListener) { t += (this->getIncludeDeclaration("EventManager") + "\n"); } if (hasSceneInterface || hasSubClassSceneSaving) { t += (this->getIncludeDeclaration("SceneClass") + "\n"); t += (this->getIncludeDeclaration("SceneClassAssistant") + "\n"); t += ("\n"); } t += ("using namespace caret;\n"); t += ("\n"); t += ("\n"); t += (" \n"); t += ("/**\n"); t += (" * \\class caret::" + className + " \n"); t += (" * \\brief \n"); if (module.isEmpty() == false) { t += (" * \\ingroup " + module + "\n"); } t += (" *\n"); t += (" * \n"); t += (" */\n"); t += ("\n"); t += ("/**\n"); t += (" * Constructor.\n"); t += (" */\n"); t += ("" + className + "::" + className + "()\n"); if (derivedFromClassName.isEmpty() == false) { t += (": " + derivedFromClassName + "("+ eventTypeEnumName + ")\n"); } t += ("{\n"); t += (" \n"); if (hasSceneInterface || hasSubClassSceneSaving) { t += (" m_sceneAssistant = new SceneClassAssistant();\n"); t += (" \n"); } if (hasEventListener) { t += ("// EventManager::get()->addEventListener(this, EventTypeEnum::);\n"); } t += ("}\n"); t += ("\n"); t += ("/**\n"); t += (" * Destructor.\n"); t += (" */\n"); t += ("" + className + "::~" + className + "()\n"); t += ("{\n"); if (hasEventListener) { t += (" EventManager::get()->removeAllEventsFromListener(this);\n"); } if (hasSceneInterface || hasSubClassSceneSaving) { t += (" delete m_sceneAssistant;\n"); } t += ("}\n"); t += ("\n"); if (hasCopyAndAssignment) { t += ("/**\n"); t += (" * Copy constructor.\n"); t += (" * @param obj\n"); t += (" * Object that is copied.\n"); t += (" */\n"); t += ("" + className + "::" + className + "(const " + className + "& obj)\n"); t += (": " + derivedFromClassName + "(obj)\n"); t += ("{\n"); t += (" this->copyHelper" + className + "(obj);\n"); t += ("}\n"); t += ("\n"); t += ("/**\n"); t += (" * Assignment operator.\n"); t += (" * @param obj\n"); t += (" * Data copied from obj to this.\n"); t += (" * @return \n"); t += (" * Reference to this object.\n"); t += (" */\n"); t += ("" + className + "&\n"); t += ("" + className + "::operator=(const " + className + "& obj)\n"); t += ("{\n"); t += (" if (this != &obj) {\n"); t += (" " + derivedFromClassName + "::operator=(obj);\n"); t += (" this->copyHelper" + className + "(obj);\n"); t += (" }\n"); t += (" return *this; \n"); t += ("}\n"); t += ("\n"); t += ("/**\n"); t += (" * Helps with copying an object of this type.\n"); t += (" * @param obj\n"); t += (" * Object that is copied.\n"); t += (" */\n"); t += ("void \n"); t += ("" + className + "::copyHelper" + className + "(const " + className + "& obj)\n"); t += ("{\n"); t += (" \n"); t += ("}\n"); t += ("\n"); } if (derivedFromClassName == "CaretObject") { t += ("/**\n"); t += (" * Get a description of this object's content.\n"); t += (" * @return String describing this object's content.\n"); t += (" */\n"); t += ("AString \n"); t += ("" + className + "::toString() const\n"); t += ("{\n"); t += (" return \"" + className + "\";\n"); t += ("}\n"); t += ("\n"); } if (hasEventListener) { t += ("/**\n"); t += (" * Receive an event.\n"); t += (" *\n"); t += (" * @param event\n"); t += (" * An event for which this instance is listening.\n"); t += (" */\n"); t += ("void\n"); t += (className + "::receiveEvent(Event* event)\n"); t += ("{\n"); t += ("// if (event->getEventType() == EventTypeEnum::) {\n"); t += ("// eventName = dynamic_cast(event);\n"); t += ("// CaretAssert(eventName);\n"); t += ("//\n"); t += ("// event->setEventProcessed();\n"); t += ("// }\n"); t += ("}\n"); t += ("\n"); } if (hasSceneInterface) { t += ("/**\n"); t += (" * Save information specific to this type of model to the scene.\n"); t += (" *\n"); t += (" * @param sceneAttributes\n"); t += (" * Attributes for the scene. Scenes may be of different types\n"); t += (" * (full, generic, etc) and the attributes should be checked when\n"); t += (" * saving the scene.\n"); t += (" *\n"); t += (" * @param instanceName\n"); t += (" * Name of instance in the scene.\n"); t += (" */\n"); t += ("SceneClass*\n"); t += (className + "::saveToScene(const SceneAttributes* sceneAttributes,\n"); t += (" const AString& instanceName)\n"); t += ("{\n"); t += (" SceneClass* sceneClass = new SceneClass(instanceName,\n"); t += (" \"" + className + "\",\n"); t += (" 1);\n"); t += (" m_sceneAssistant->saveMembers(sceneAttributes,\n"); t += (" sceneClass);\n"); t += (" \n"); t += (" // Uncomment if sub-classes must save to scene\n"); t += (" //saveSubClassDataToScene(sceneAttributes,\n"); t += (" // sceneClass);\n"); t += (" \n"); t += (" return sceneClass;\n"); t += ("}\n"); t += ("\n"); t += ("/**\n"); t += (" * Restore information specific to the type of model from the scene.\n"); t += (" *\n"); t += (" * @param sceneAttributes\n"); t += (" * Attributes for the scene. Scenes may be of different types\n"); t += (" * (full, generic, etc) and the attributes should be checked when\n"); t += (" * restoring the scene.\n"); t += (" *\n"); t += (" * @param sceneClass\n"); t += (" * sceneClass from which model specific information is obtained.\n"); t += (" */\n"); t += ("void\n"); t += (className + "::restoreFromScene(const SceneAttributes* sceneAttributes,\n"); t += (" const SceneClass* sceneClass)\n"); t += ("{\n"); t += (" if (sceneClass == NULL) {\n"); t += (" return;\n"); t += (" }\n"); t += (" \n"); t += (" m_sceneAssistant->restoreMembers(sceneAttributes,\n"); t += (" sceneClass); \n"); t += (" \n"); t += (" //Uncomment if sub-classes must restore from scene\n"); t += (" //restoreSubClassDataFromScene(sceneAttributes,\n"); t += (" // sceneClass);\n"); t += (" \n"); t += ("}\n"); t += ("\n"); } if (hasSubClassSceneSaving) { t += ("/**\n"); t += (" * Save subclass data to the scene.\n"); t += (" *\n"); t += (" * @param sceneAttributes\n"); t += (" * Attributes for the scene. Scenes may be of different types\n"); t += (" * (full, generic, etc) and the attributes should be checked when\n"); t += (" * restoring the scene.\n"); t += (" *\n"); t += (" * @param sceneClass\n"); t += (" * sceneClass to which data members should be added. Will always\n"); t += (" * be valid (non-NULL).\n"); t += (" */\n"); t += ("void\n"); t += (className + "::saveSubClassDataToScene(const SceneAttributes* sceneAttributes,\n"); t += (" SceneClass* sceneClass)\n"); t += ("{\n"); t += (" m_sceneAssistant->saveMembers(sceneAttributes,\n"); t += (" sceneClass);\n"); t += ("}\n"); t += ("\n"); t += ("/**\n"); t += (" * Restore file data from the scene.\n"); t += (" *\n"); t += (" * @param sceneAttributes\n"); t += (" * Attributes for the scene. Scenes may be of different types\n"); t += (" * (full, generic, etc) and the attributes should be checked when\n"); t += (" * restoring the scene.\n"); t += (" *\n"); t += (" * @param sceneClass\n"); t += (" * sceneClass for the instance of a class that implements\n"); t += (" * this interface. Will NEVER be NULL.\n"); t += (" */\n"); t += ("void\n"); t += (className + "::restoreSubClassDataFromScene(const SceneAttributes* sceneAttributes,\n"); t += (" const SceneClass* sceneClass)\n"); t += ("{\n"); t += (" m_sceneAssistant->restoreMembers(sceneAttributes,\n"); t += (" sceneClass);\n"); t += ("}\n"); t += ("\n"); } TextFile tf; tf.replaceText(t); try { tf.writeFile(outputFileName); } catch (const DataFileException& e) { throw CommandException(e); } } connectome-workbench-1.2.3+git41-gc4c6c90/src/Commands/CommandClassCreate.h000066400000000000000000000060611300200146000261510ustar00rootroot00000000000000#ifndef __COMMAND_CLASS_CREATE__H__ #define __COMMAND_CLASS_CREATE__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 "CommandClassCreateBase.h" namespace caret { /// Command that creates class files. class CommandClassCreate : public CommandClassCreateBase { public: CommandClassCreate(); virtual ~CommandClassCreate(); virtual void executeOperation(ProgramParameters& parameters); AString getHelpInformation(const AString& /*programName*/); /** This String is used by the command that adds new members to a class */ static AString getNewMembersString() { return "// ADD_NEW_MEMBERS_HERE"; } /** This String is used by the command that adds new METHODS to a class */ static AString getNewMethodsString() { return "// ADD_NEW_METHODS_HERE"; } private: CommandClassCreate(const CommandClassCreate&); CommandClassCreate& operator=(const CommandClassCreate&); void createHeaderFile(const AString& outputFileName, const AString& className, const AString& derivedFromClassName, const AString& ifdefName, const AString& ifdefNameStaticDeclaration, const bool hasCopyAndAssignment, const bool hasEventListener, const bool hasSceneInterface, const bool hasSubClassSceneSaving); void createImplementationFile(const AString& outputFileName, const AString& className, const AString& derivedFromClassName, const AString& eventTypeEnumName, const AString& ifdefNameStaticDeclaration, const bool hasCopyAndAssignment, const bool hasEventListener, const bool hasSceneInterface, const bool hasSubClassSceneSaving); }; } // namespace #endif // __COMMAND_CLASS_CREATE_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Commands/CommandClassCreateAlgorithm.cxx000066400000000000000000000373471300200146000304060ustar00rootroot00000000000000/*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 "CaretAssertion.h" #include "CommandClassCreateAlgorithm.h" #include "DataFileException.h" #include "FileInformation.h" #include "ProgramParameters.h" #include "TextFile.h" using namespace caret; /** * Constructor. */ CommandClassCreateAlgorithm::CommandClassCreateAlgorithm() : CommandClassCreateBase("-class-create-algorithm", "CREATE SOURCE CODE CLASS FILES (.h, .cxx) FOR ALGORITHM") { } /** * Destructor. */ CommandClassCreateAlgorithm::~CommandClassCreateAlgorithm() { } /** * @return The help information. */ AString CommandClassCreateAlgorithm::getHelpInformation(const AString& /*programName*/) { AString helpInfo = ("\n" "Create Algorithm Class header (.h) and implementation (.cxx) files.\n" "\n" "Usage: \n" " \n" " \n" "\n" " algorithm-class-name\n" " Required name of the algorithm class that MUST start with \"Algorithm\"\n" " \n" " command-line-switch\n" " Required command line switch for algorithm.\n" " \n" " short-description\n" " Required short description within double quotes.\n" " \n" ); return helpInfo; } /** * Execute the operation. * * @param parameters * Parameters for the operation. * @throws CommandException * If the command failed. * @throws ProgramParametersException * If there is an error in the parameters. */ void CommandClassCreateAlgorithm::executeOperation(ProgramParameters& parameters) { const AString algorithmClassName = parameters.nextString("Algorithm Class Name"); const AString commandLineSwitch = parameters.nextString("Command Line Switch"); const AString shortDescription = parameters.nextString("Short Description"); if (parameters.hasNext()) { const AString param = parameters.nextString("Parameter"); throw CommandException("Unexpected extra parameter: " + param); } AString errorMessage; if (algorithmClassName.isEmpty()) { throw CommandException("Algorithm Class Name is empty."); } else { if (algorithmClassName.startsWith("Algorithm") == false) { throw CommandException("Algorithm Class Name must start with \"Algorithm\".\n"); } if (algorithmClassName == "Algorithm") { throw CommandException("\"Algorithm\" is not allowed for Algorithm Class Name"); } } if (commandLineSwitch.isEmpty()) { throw CommandException("Command line switch is empty."); } else if (commandLineSwitch.startsWith("-") == false) { throw CommandException("Command line must begin with \"-\"."); } if (shortDescription.isEmpty()) { throw CommandException("Short description is empty."); } const AString headerFileName = algorithmClassName + ".h"; const AString implementationFileName = algorithmClassName + ".cxx"; FileInformation headerInfo(headerFileName); if (headerInfo.exists()) { errorMessage += headerFileName + " exists and this command will not overwrite it.\n"; } FileInformation impInfo(implementationFileName); if (impInfo.exists()) { errorMessage += implementationFileName + " exists and this command will not overwrite it.\n"; } if (errorMessage.isEmpty() == false) { throw CommandException(errorMessage); } AString ifndefName; AString ifdefNameStaticDeclarations; this->getIfDefNames(algorithmClassName, ifndefName, ifdefNameStaticDeclarations); this->createHeaderFile(headerFileName, algorithmClassName, ifndefName); this->createImplementationFile(implementationFileName, algorithmClassName, commandLineSwitch, shortDescription); } /** * Create and write the header (.h) file. * * @param outputFileName * Name for file that is written. * @param algorithmClassName * Name of algorithm type class. * @param ifndefName * Name of "ifndef" value. */ void CommandClassCreateAlgorithm::createHeaderFile(const AString& outputFileName, const AString& algorithmClassName, const AString& ifndefName) { AString t; t += ("#ifndef " + ifndefName + "\n"); t += ("#define " + ifndefName + "\n"); t += this->getCopyright(); t += ("\n"); t += ("#include \"AbstractAlgorithm.h\"\n"); t += ("\n"); t += ("namespace caret {\n"); t += ("\n"); t += (" class " + algorithmClassName + " : public AbstractAlgorithm {\n"); t += ("\n"); t += (" private:\n"); t += (" " + algorithmClassName + "(); \n"); t += ("\n"); t += (" protected:\n"); t += (" static float getSubAlgorithmWeight();\n"); t += ("\n"); t += (" static float getAlgorithmInternalWeight();\n"); t += ("\n"); t += (" public:\n"); t += (" " + algorithmClassName + "(ProgressObject* myProgObj /*INSERT PARAMETERS HERE*/); \n"); t += ("\n"); t += (" static OperationParameters* getParameters();\n"); t += ("\n"); t += (" static void useParameters(OperationParameters* myParams, \n"); t += (" ProgressObject* myProgObj);\n"); t += ("\n"); t += (" static AString getCommandSwitch();\n"); t += ("\n"); t += (" static AString getShortDescription();\n"); t += ("\n"); t += (" };\n"); t += ("\n"); t += (" typedef TemplateAutoOperation<" + algorithmClassName + "> Auto" + algorithmClassName + ";\n"); t += ("\n"); t += ("} // namespace\n"); t += ("\n"); t += ("#endif //" + ifndefName + "\n"); t += ("\n"); TextFile tf; tf.replaceText(t); try { tf.writeFile(outputFileName); } catch (const DataFileException& e) { throw CommandException(e); } } /** * Create and write the implementation (.cxx) file. * * @param outputFileName * Name for file that is written. * @param algorithmClassName * Name of algorithm type class. * @param commandLineSwitch * Command line switch for algorithm. * @param shortDescription * Short description of algorithm. */ void CommandClassCreateAlgorithm::createImplementationFile(const AString& outputFileName, const AString& algorithmClassName, const AString& commandLineSwitch, const AString& shortDescription) { AString t; t += this->getCopyright(); t += ("#include \"CaretAssert.h\"\n"); t += ("#include \"CaretLogger.h\"\n"); t += ("\n"); t += ("#include \"" + algorithmClassName + ".h\"\n"); t += ("#include \"AlgorithmException.h\"\n"); t += ("\n"); t += ("using namespace caret;\n"); t += ("\n"); t += ("/**\n"); t += (" * \\class caret::" + algorithmClassName + " \n"); t += (" * \\brief " + shortDescription + "\n"); t += (" *\n"); t += (" * \n"); t += (" */\n"); t += ("\n"); t += ("/**\n"); t += (" * @return Command line switch\n"); t += (" */\n"); t += ("AString\n"); t += ("" + algorithmClassName + "::getCommandSwitch()\n"); t += ("{\n"); t += (" return \"" + commandLineSwitch + "\";\n"); t += ("}\n"); t += ("\n"); t += ("/**\n"); t += (" * @return Short description of algorithm\n"); t += (" */\n"); t += ("AString\n"); t += ("" + algorithmClassName + "::getShortDescription()\n"); t += ("{\n"); t += (" return \"" + shortDescription.toUpper() + "\";\n"); t += ("}\n"); t += ("\n"); t += ("/**\n"); t += (" * @return Parameters for algorithm\n"); t += (" */\n"); t += ("OperationParameters*\n"); t += ("" + algorithmClassName + "::getParameters()\n"); t += ("{\n"); t += (" OperationParameters* ret = new OperationParameters();\n"); t += (" \n"); t += (" /*\n"); t += (" * Example parameters:\n"); t += (" *\n"); t += (" * ret->addSurfaceParameter(1, \"surface\", \"the surface to compute on\");\n"); t += (" *\n"); t += (" * ret->addMetricOutputParameter(2, \"metric\", \"the output metric\");\n"); t += (" *\n"); t += (" * OptionalParameter* columnSelect = ret->createOptionalParameter(3, \"-column\", \"select a single column\");\n"); t += (" *\n"); t += (" * columnSelect->addStringParameter(1, \"column\", \"the column number or name\")\n"); t += (" */\n"); t += (" AString helpText = (\"This is where you set the help text. DO NOT add the info about what the command line format is\"\n"); t += (" \"and do not give the command switch, short description, or the short descriptions of parameters. Do not indent,\"\n"); t += (" \"add newlines, or format the text in any way other than to separate paragraphs within the help text prose\");\n"); t += ("\n"); t += (" ret->setHelpText(helpText);\n"); t += (" \n"); t += (" return ret;\n"); t += ("}\n"); t += ("\n"); t += ("/**\n"); t += (" * Use Parameters and perform algorithm\n"); t += (" * @param myParams\n"); t += (" * Parameters for algorithm\n"); t += (" * @param myProgObj\n"); t += (" * The progress object\n"); t += (" * @throws\n"); t += (" * AlgorithmException if errors\n"); t += (" */\n"); t += ("void\n"); t += ("" + algorithmClassName + "::useParameters(OperationParameters* myParams,\n"); t += (" ProgressObject* myProgObj)\n"); t += ("{\n"); t += (" /*\n"); t += (" * Example parameter processing:\n"); t += (" *\n"); t += (" * Gets the surface with key 1\n"); t += (" * SurfaceFile* mySurf = myParams->getSurface(1);\n"); t += (" *\n"); t += (" * Gets the output metric with key 2\n"); t += (" * MetricFile* myMetricOut = myParams->getOutputMetric(2);\n"); t += (" *\n"); t += (" * Gets optional parameter with key 3\n"); t += (" * OptionalParameter* columnSelect = myParams->getOptionalParameter(3);\n"); t += (" * int columnNum = -1;\n"); t += (" * if (columnSelect->m_present) {\n"); t += (" * columnNum = (int)myMetric->getMapIndexFromNameOrNumber(columnSelect->getString(1));\n"); t += (" * if (columnNum < 0) {\n"); t += (" * throw AlgorithmException(\"invalid column specified\");\n"); t += (" * }\n"); t += (" * }\n"); t += (" */\n"); t += (" \n"); t += (" /*\n"); t += (" * Constructs and executes the algorithm \n"); t += (" */\n"); t += (" " + algorithmClassName + "(myProgObj /* INSERT PARAMETERS HERE */);\n"); t += (" \n"); t += ("}\n"); t += ("\n"); t += ("/**\n"); t += (" * Constructor\n"); t += (" *\n"); t += (" * Calling the constructor will execute the algorithm\n"); t += (" *\n"); t += (" * @param myProgObj\n"); t += (" * Parameters for algorithm\n"); t += (" */\n"); t += (algorithmClassName + "::" + algorithmClassName + "(ProgressObject* myProgObj /* INSERT PARAMETERS HERE - may get compilation error if no parameters added */)\n"); t += (" : AbstractAlgorithm(myProgObj)\n"); t += ("{\n"); t += (" /*\n"); t += (" * Uncomment these if you use another algorithm inside here\n"); t += (" *\n"); t += (" * ProgressObject* subAlgProgress1 = NULL;\n"); t += (" * if (myProgObj != NULL) {\n"); t += (" * subAlgProgress1 = myProgObj->addAlgorithm(AlgorithmInsertNameHere::getAlgorithmWeight());\n"); t += (" * }\n"); t += (" */\n"); t += (" \n"); t += (" /*\n"); t += (" * Sets the algorithm up to use the progress object, and will \n"); t += (" * finish the progress object automatically when the algorithm terminates\n"); t += (" */\n"); t += (" LevelProgress myProgress(myProgObj);\n"); t += (" \n"); t += (" /*\n"); t += (" * How you say you are halfway done with the INTERNAL work of the algorithm\n"); t += (" * will report finished automatically when this function ends (myProgress goes out \n"); t += (" of scope, destructor triggers finish\n"); t += (" */\n"); t += (" //myProgress.reportProgress(0.5f);\n"); t += (" \n"); t += ("}\n"); t += ("\n"); t += ("/**\n"); t += (" * @return Algorithm internal weight\n"); t += (" */\n"); t += ("float\n"); t += ("" + algorithmClassName + "::getAlgorithmInternalWeight()\n"); t += ("{\n"); t += (" /*\n"); t += (" * override this if needed, if the progress bar isn't smooth\n"); t += (" */\n"); t += (" return 1.0f;\n"); t += ("}\n"); t += ("\n"); t += ("/**\n"); t += (" * @return Algorithm sub-algorithm weight\n"); t += (" */\n"); t += ("float\n"); t += ("" + algorithmClassName + "::getSubAlgorithmWeight()\n"); t += ("{\n"); t += (" /*\n"); t += (" * If you use a subalgorithm\n"); t += (" */\n"); t += (" //return AlgorithmInsertNameHere::getAlgorithmWeight()\n"); t += (" return 0.0f;\n"); t += ("}\n"); t += ("\n"); TextFile tf; tf.replaceText(t); try { tf.writeFile(outputFileName); } catch (const DataFileException& e) { throw CommandException(e); } std::cout << std::endl; std::cout << "Algorithm was created successfully." << std::endl; std::cout << std::endl; std::cout << "Add the class files to Algorithms/CMakeLists.txt:" << std::endl; std::cout << " " << qPrintable(algorithmClassName) << ".h" << std::endl; std::cout << " " << qPrintable(algorithmClassName) << ".cxx" << std::endl; std::cout << std::endl; std::cout << "Add the header file and algorithm to Commands/CommandOperationManager.cxx:" << std::endl; std::cout << " #include \"" << qPrintable(algorithmClassName) << ".h\"" << std::endl; std::cout << " this->commandOperations.push_back(new CommandParser(new Auto" << algorithmClassName << "()));" << std::endl; std::cout << std::endl; } connectome-workbench-1.2.3+git41-gc4c6c90/src/Commands/CommandClassCreateAlgorithm.h000066400000000000000000000041311300200146000300140ustar00rootroot00000000000000#ifndef __COMMAND_CLASS_CREATE_ALGORITHM_H__ #define __COMMAND_CLASS_CREATE_ALGORITHM_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 "CommandClassCreateBase.h" namespace caret { /// Command that creates class files for an algorithm class CommandClassCreateAlgorithm : public CommandClassCreateBase { public: CommandClassCreateAlgorithm(); virtual ~CommandClassCreateAlgorithm(); virtual void executeOperation(ProgramParameters& parameters); AString getHelpInformation(const AString& /*programName*/); private: CommandClassCreateAlgorithm(const CommandClassCreateAlgorithm&); CommandClassCreateAlgorithm& operator=(const CommandClassCreateAlgorithm&); void createHeaderFile(const AString& outputFileName, const AString& algorithmClassName, const AString& ifndefName); void createImplementationFile(const AString& outputFileName, const AString& algorithmClassName, const AString& commandLineSwitch, const AString& shortDescription); }; } // namespace #endif // __COMMAND_CLASS_CREATE_ALGORITHM_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Commands/CommandClassCreateBase.cxx000066400000000000000000000111771300200146000273230ustar00rootroot00000000000000/*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 "CaretAssert.h" #include "CommandClassCreateBase.h" #include "ProgramParameters.h" #include "SystemUtilities.h" using namespace caret; /** * Constructor. */ CommandClassCreateBase::CommandClassCreateBase(const AString& commandLineSwitch, const AString& operationShortDescription) : CommandOperation(commandLineSwitch, operationShortDescription) { } /** * Destructor. */ CommandClassCreateBase::~CommandClassCreateBase() { } /** * Get the names for use with #ifdef declarations. * @param classNameIn * Name of class. * @param ifndefNameOut * Name for use with #ifndef at beginning of file. * @param ifdefNameStaticDeclarationOut * Name for use when declarating static members at * end of the header file. */ void CommandClassCreateBase::getIfDefNames(const AString& classNameIn, AString& ifndefNameOut, AString& ifdefNameStaticDeclarationOut) { ifndefNameOut = ""; ifdefNameStaticDeclarationOut = ""; const int32_t classNameLength = classNameIn.length(); ifndefNameOut += "_"; for (int32_t i = 0; i < classNameLength; i++) { QChar c = classNameIn[i]; if (c.isUpper()) { ifndefNameOut += "_"; } ifndefNameOut += c.toUpper(); } ifdefNameStaticDeclarationOut = ifndefNameOut + "_DECLARE__"; ifndefNameOut += "_H__"; } /** * Get the copyright. * @return Text containing copyright. */ AString CommandClassCreateBase::getCopyright() { const AString year = SystemUtilities::getYear(); AString text; text.append("\n"); text.append("/*LICENSE_START*/\n"); text.append("/*\n"); text.append(" * Copyright (C) " + year + " Washington University School of Medicine\n"); text.append(" *\n"); text.append(" * This program is free software; you can redistribute it and/or modify\n"); text.append(" * it under the terms of the GNU General Public License as published by\n"); text.append(" * the Free Software Foundation; either version 2 of the License, or\n"); text.append(" * (at your option) any later version.\n"); text.append(" *\n"); text.append(" * This program is distributed in the hope that it will be useful,\n"); text.append(" * but WITHOUT ANY WARRANTY; without even the implied warranty of\n"); text.append(" * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n"); text.append(" * GNU General Public License for more details.\n"); text.append(" *\n"); text.append(" * You should have received a copy of the GNU General Public License along\n"); text.append(" * with this program; if not, write to the Free Software Foundation, Inc.,\n"); text.append(" * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\n"); text.append(" */\n"); text.append("/*LICENSE_END*/\n"); text.append("\n"); return text; } /** * Get the #include declaration for an include file. * @param includeFileName * Name of include file. * @return * Include declaration (no newline at end of line). */ AString CommandClassCreateBase::getIncludeDeclaration(const AString& includeFileName) const { if (includeFileName.isEmpty()) { return ""; } AString txt = "#include "; const QChar firstChar = includeFileName[0]; if (firstChar.isLower()) { txt += ("<" + includeFileName + ">"); } else if (firstChar == 'Q') { txt += ("<" + includeFileName + ">"); } else { AString dotH = ""; if (includeFileName.endsWith(".h") == false) { dotH = ".h"; } txt += ("\"" + includeFileName + dotH + "\""); } return txt; } connectome-workbench-1.2.3+git41-gc4c6c90/src/Commands/CommandClassCreateBase.h000066400000000000000000000034731300200146000267500ustar00rootroot00000000000000#ifndef __COMMAND_CLASS_CREATE_BASE_H__ #define __COMMAND_CLASS_CREATE_BASE_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 "CommandOperation.h" namespace caret { /// Base class for creating class files. class CommandClassCreateBase : public CommandOperation { protected: CommandClassCreateBase(const AString& commandLineSwitch, const AString& operationShortDescription); public: virtual ~CommandClassCreateBase(); private: CommandClassCreateBase(const CommandClassCreateBase&); CommandClassCreateBase& operator=(const CommandClassCreateBase&); protected: AString getIncludeDeclaration(const AString& includeFileName) const; AString getCopyright(); void getIfDefNames(const AString& className, AString& ifdefName, AString& ifdefNameStaticDeclaration); }; } // namespace #endif // __COMMAND_CLASS_CREATE_BASE_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Commands/CommandClassCreateEnum.cxx000066400000000000000000000734601300200146000273600ustar00rootroot00000000000000/*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 "CaretAssertion.h" #include "CommandClassCreateEnum.h" #include "DataFileException.h" #include "FileInformation.h" #include "ProgramParameters.h" #include "TextFile.h" using namespace caret; /** * Constructor. */ CommandClassCreateEnum::CommandClassCreateEnum() : CommandClassCreateBase("-class-create-enum", "CREATE SOURCE CODE CLASS FILES (.h, .cxx) FOR ENUMERATED TYPE") { } /** * Destructor. */ CommandClassCreateEnum::~CommandClassCreateEnum() { } AString CommandClassCreateEnum::getHelpInformation(const AString& /*programName*/) { AString helpInfo = ("\n" "Create enumerated type header (.h) and implementation (.cxx) files.\n" "\n" "Usage: \n" " \n" " \n" "\n" " enum-class-name\n" " Name of the enumerated type. Must end in \"Enum\"\n" " \n" " number-of-values\n" " Number of values in the enumerated type.\n" " \n" " auto-number\n" " Automatically generated integer codes corresponding\n" " to the enumerated values. Value for this parameter\n" " are \"true\" and \"false\".\n" " \n" " [enum-name-1] [enum-name-2]...[enum-name-N]\n" " Optional names for the enumerated values. \n" " \n" " If the number of names listed is greater than\n" " the \"number-of-values\" parameter, the \"number-of-values\"\n" " will become the number of names. If the number\n" " of names is is less than the \"number-of-values\",\n" " empty entries will be created.\n" " \n" ); return helpInfo; } /** * Execute the operation. * * @param parameters * Parameters for the operation. * @throws CommandException * If the command failed. * @throws ProgramParametersException * If there is an error in the parameters. */ void CommandClassCreateEnum::executeOperation(ProgramParameters& parameters) { const AString enumClassName = parameters.nextString("Enum Class Name"); int32_t numberOfEnumValues = parameters.nextInt("Number of Enum Values"); const bool isAutoNumber = parameters.nextBoolean("Auto Number (true/false)"); std::vector enumValueNames; while (parameters.hasNext()) { enumValueNames.push_back(parameters.nextString("Enum Value")); } const int32_t numEnumValueNames = static_cast(enumValueNames.size()); if (numEnumValueNames > 0) { if (numEnumValueNames > numberOfEnumValues) { numberOfEnumValues = numEnumValueNames; } } if (enumClassName.isEmpty()) { throw CommandException("Enum class name is empty."); } AString errorMessage; if (enumClassName.endsWith("Enum") == false) { errorMessage += "Name of class MUST end with \"Enum\".\n"; } if (enumClassName[0].isLower()) { errorMessage += "First letter of class name must be upper case.\n"; } const AString headerFileName = enumClassName + ".h"; const AString implementationFileName = enumClassName + ".cxx"; FileInformation headerInfo(headerFileName); if (headerInfo.exists()) { errorMessage += headerFileName + " exists and this command will not overwrite it.\n"; } FileInformation impInfo(implementationFileName); if (impInfo.exists()) { errorMessage += implementationFileName + " exists and this command will not overwrite it.\n"; } if (errorMessage.isEmpty() == false) { throw CommandException(errorMessage); } AString ifndefName; AString ifdefNameStaticDeclarations; this->getIfDefNames(enumClassName, ifndefName, ifdefNameStaticDeclarations); this->createHeaderFile(headerFileName, enumClassName, ifndefName, ifdefNameStaticDeclarations, numberOfEnumValues, enumValueNames, isAutoNumber); this->createImplementationFile(implementationFileName, enumClassName, ifdefNameStaticDeclarations, numberOfEnumValues, enumValueNames, isAutoNumber); } /** * Create and write the header (.h) file. * * @param outputFileName * Name for file that is written. * @param enumClassName * Name of enumerated type class. * @param ifdefName * Name of "ifndef" value. * @param ifdefNameStaticDeclaration * Name for "infdef" of static declarations. * @param numberOfEnumValues * Number of enumerated type values. * @param enumValueNames * Names for the enumerated values. * @param isAutoNumber * Automatically assign numers/indices to the enumerated values. */ void CommandClassCreateEnum::createHeaderFile(const AString& outputFileName, const AString& enumClassName, const AString& ifndefName, const AString& ifdefNameStaticDeclaration, const int32_t numberOfEnumValues, const std::vector& enumValueNames, const bool isAutoNumber) { AString t; t += ("#ifndef " + ifndefName + "\n"); t += ("#define " + ifndefName + "\n"); t += this->getCopyright(); t += ("\n"); t += ("#include \n"); t += ("#include \n"); t += ("#include \"AString.h\"\n"); t += ("\n"); t += ("namespace caret {\n"); t += ("\n"); t += ("class " + enumClassName + " {\n"); t += ("\n"); t += ("public:\n"); t += (" /**\n"); t += (" * Enumerated values.\n"); t += (" */\n"); t += (" enum Enum {\n"); for (int indx = 0; indx < numberOfEnumValues; indx++) { t += (" /** */\n"); t += (" "); if (indx < static_cast(enumValueNames.size())) { t +=enumValueNames[indx]; } if (indx < (numberOfEnumValues - 1)) { t += (",\n"); } else { t += ("\n"); } } t += (" };\n"); t += ("\n"); t += ("\n"); t += (" ~" + enumClassName + "();\n"); t += ("\n"); t += (" static AString toName(Enum enumValue);\n"); t += (" \n"); t += (" static Enum fromName(const AString& name, bool* isValidOut);\n"); t += (" \n"); t += (" static AString toGuiName(Enum enumValue);\n"); t += (" \n"); t += (" static Enum fromGuiName(const AString& guiName, bool* isValidOut);\n"); t += (" \n"); t += (" static int32_t toIntegerCode(Enum enumValue);\n"); t += (" \n"); t += (" static Enum fromIntegerCode(const int32_t integerCode, bool* isValidOut);\n"); t += ("\n"); t += (" static void getAllEnums(std::vector& allEnums);\n"); t += ("\n"); t += (" static void getAllNames(std::vector& allNames, const bool isSorted);\n"); t += ("\n"); t += (" static void getAllGuiNames(std::vector& allGuiNames, const bool isSorted);\n"); t += ("\n"); t += ("private:\n"); t += (" " + enumClassName + "(const Enum enumValue, \n"); if (isAutoNumber == false) { t += (" const int32_t integerCode, \n"); } t += (" const AString& name,\n"); t += (" const AString& guiName);\n"); t += ("\n"); t += (" static const " + enumClassName + "* findData(const Enum enumValue);\n"); t += ("\n"); t += (" /** Holds all instance of enum values and associated metadata */\n"); t += (" static std::vector<" + enumClassName + "> enumData;\n"); t += ("\n"); t += (" /** Initialize instances that contain the enum values and metadata */\n"); t += (" static void initialize();\n"); t += ("\n"); t += (" /** Indicates instance of enum values and metadata have been initialized */\n"); t += (" static bool initializedFlag;\n"); t += (" \n"); if (isAutoNumber) { t += (" /** Auto generated integer codes */\n"); t += (" static int32_t integerCodeCounter;\n"); t += (" \n"); } t += (" /** The enumerated type value for an instance */\n"); t += (" Enum enumValue;\n"); t += ("\n"); t += (" /** The integer code associated with an enumerated value */\n"); t += (" int32_t integerCode;\n"); t += ("\n"); t += (" /** The name, a text string that is identical to the enumerated value */\n"); t += (" AString name;\n"); t += (" \n"); t += (" /** A user-friendly name that is displayed in the GUI */\n"); t += (" AString guiName;\n"); t += ("};\n"); t += ("\n"); t += ("#ifdef " + ifdefNameStaticDeclaration + "\n"); t += ("std::vector<" + enumClassName + "> " + enumClassName + "::enumData;\n"); t += ("bool " + enumClassName + "::initializedFlag = false;\n"); if (isAutoNumber) { t += ("int32_t " + enumClassName + "::integerCodeCounter = 0; \n"); } t += ("#endif // " + ifdefNameStaticDeclaration + "\n"); t += ("\n"); t += ("} // namespace\n"); t += ("#endif //" + ifndefName + "\n"); TextFile tf; tf.replaceText(t); try { tf.writeFile(outputFileName); } catch (const DataFileException& e) { throw CommandException(e); } } /** * Create and write the implementation (.cxx) file. * * @param outputFileName * Name for file that is written. * @param enumClassName * Name of enumerated type class. * @param ifdefNameStaticDeclaration * Name for "infdef" of static declarations. * @param numberOfEnumValues * Number of enumerated type values. * @param enumValueNames * Names for the enumerated values. * @param isAutoNumber * Automatically assign numers/indices to the enumerated values. */ void CommandClassCreateEnum::createImplementationFile(const AString& outputFileName, const AString& enumClassName, const AString& ifdefNameStaticDeclaration, const int32_t numberOfEnumValues, const std::vector& enumValueNames, const bool isAutoNumber) { AString t; t += this->getCopyright(); t += ("#include \n"); t += ("#define " + ifdefNameStaticDeclaration + "\n"); t += ("#include \"" + enumClassName + ".h\"\n"); t += ("#undef " + ifdefNameStaticDeclaration + "\n"); t += ("\n"); t += ("#include \"CaretAssert.h\"\n"); t += ("\n"); t += ("using namespace caret;\n"); t += ("\n"); t += (" \n"); t += ("/**\n"); t += (" * \\class caret::" + enumClassName + " \n"); t += (" * \\brief \n"); t += (" *\n"); t += (" * \n"); t += (" *\n"); t += (getEnumComboBoxTemplateHelpInfo(enumClassName)); t += (" */\n"); t += ("\n"); t += ("/**\n"); t += (" * Constructor.\n"); t += (" *\n"); t += (" * @param enumValue\n"); t += (" * An enumerated value.\n"); if (isAutoNumber == false) { t += (" * @param integerCode\n"); t += (" * Integer code for this enumerated value.\n"); t += (" *\n"); } t += (" * @param name\n"); t += (" * Name of enumerated value.\n"); t += (" *\n"); t += (" * @param guiName\n"); t += (" * User-friendly name for use in user-interface.\n"); t += (" */\n"); t += ("" + enumClassName + "::" + enumClassName + "(const Enum enumValue,\n"); if (isAutoNumber == false) { t += (" const int32_t integerCode,\n"); } t += (" const AString& name,\n"); t += (" const AString& guiName)\n"); t += ("{\n"); t += (" this->enumValue = enumValue;\n"); if (isAutoNumber) { t += (" this->integerCode = integerCodeCounter++;\n"); } else { t += (" this->integerCode = integerCode;\n"); } t += (" this->name = name;\n"); t += (" this->guiName = guiName;\n"); t += ("}\n"); t += ("\n"); t += ("/**\n"); t += (" * Destructor.\n"); t += (" */\n"); t += ("" + enumClassName + "::~" + enumClassName + "()\n"); t += ("{\n"); t += ("}\n"); t += ("\n"); t += ("/**\n"); t += (" * Initialize the enumerated metadata.\n"); t += (" */\n"); t += ("void\n"); t += ("" + enumClassName + "::initialize()\n"); t += ("{\n"); t += (" if (initializedFlag) {\n"); t += (" return;\n"); t += (" }\n"); t += (" initializedFlag = true;\n"); t += ("\n"); for (int32_t indx = 0; indx < numberOfEnumValues; indx++) { AString name = ""; AString guiName = name; if (indx < static_cast(enumValueNames.size())) { name = enumValueNames[indx]; /* * Name: "NAME_OF_ENUM_VALUE" * guiName: "Name Of Enum Value" */ guiName = name.toLower(); const int numChars = guiName.length(); for (int32_t k = 0; k < numChars; k++) { if (k == 0) { guiName[k] = guiName[k].toUpper(); } else if (guiName[k] == '_') { guiName[k] = ' '; if (k < (numChars - 1)) { k++; guiName[k] = guiName[k].toUpper(); } } } } t += (" enumData.push_back(" + enumClassName + "(" + name + ", \n"); if (isAutoNumber == false) { t += (" " + AString::number(indx) + ", \n"); } t += (" \"" + name + "\", \n"); t += (" \"" + guiName + "\"));\n"); t += (" \n"); } t += ("}\n"); t += ("\n"); t += ("/**\n"); t += (" * Find the data for and enumerated value.\n"); t += (" * @param enumValue\n"); t += (" * The enumerated value.\n"); t += (" * @return Pointer to data for this enumerated type\n"); t += (" * or NULL if no data for type or if type is invalid.\n"); t += (" */\n"); t += ("const " + enumClassName + "*\n"); t += ("" + enumClassName + "::findData(const Enum enumValue)\n"); t += ("{\n"); t += (" if (initializedFlag == false) initialize();\n"); t += ("\n"); t += (" size_t num = enumData.size();\n"); t += (" for (size_t i = 0; i < num; i++) {\n"); t += (" const " + enumClassName + "* d = &enumData[i];\n"); t += (" if (d->enumValue == enumValue) {\n"); t += (" return d;\n"); t += (" }\n"); t += (" }\n"); t += ("\n"); t += (" return NULL;\n"); t += ("}\n"); t += ("\n"); t += ("/**\n"); t += (" * Get a string representation of the enumerated type.\n"); t += (" * @param enumValue \n"); t += (" * Enumerated value.\n"); t += (" * @return \n"); t += (" * String representing enumerated value.\n"); t += (" */\n"); t += ("AString \n"); t += ("" + enumClassName + "::toName(Enum enumValue) {\n"); t += (" if (initializedFlag == false) initialize();\n"); t += (" \n"); t += (" const " + enumClassName + "* enumInstance = findData(enumValue);\n"); t += (" return enumInstance->name;\n"); t += ("}\n"); t += ("\n"); t += ("/**\n"); t += (" * Get an enumerated value corresponding to its name.\n"); t += (" * @param name \n"); t += (" * Name of enumerated value.\n"); t += (" * @param isValidOut \n"); t += (" * If not NULL, it is set indicating that a\n"); t += (" * enum value exists for the input name.\n"); t += (" * @return \n"); t += (" * Enumerated value.\n"); t += (" */\n"); t += ("" + enumClassName + "::Enum \n"); t += ("" + enumClassName + "::fromName(const AString& name, bool* isValidOut)\n"); t += ("{\n"); t += (" if (initializedFlag == false) initialize();\n"); t += (" \n"); t += (" bool validFlag = false;\n"); t += (" Enum enumValue = " + enumClassName + "::enumData[0].enumValue;\n"); t += (" \n"); t += (" for (std::vector<" + enumClassName + ">::iterator iter = enumData.begin();\n"); t += (" iter != enumData.end();\n"); t += (" iter++) {\n"); t += (" const " + enumClassName + "& d = *iter;\n"); t += (" if (d.name == name) {\n"); t += (" enumValue = d.enumValue;\n"); t += (" validFlag = true;\n"); t += (" break;\n"); t += (" }\n"); t += (" }\n"); t += (" \n"); t += (" if (isValidOut != 0) {\n"); t += (" *isValidOut = validFlag;\n"); t += (" }\n"); t += (" else if (validFlag == false) {\n"); t += (" CaretAssertMessage(0, AString(\"Name \" + name + \"failed to match enumerated value for type " + enumClassName + "\"));\n"); t += (" }\n"); t += (" return enumValue;\n"); t += ("}\n"); t += ("\n"); t += ("/**\n"); t += (" * Get a GUI string representation of the enumerated type.\n"); t += (" * @param enumValue \n"); t += (" * Enumerated value.\n"); t += (" * @return \n"); t += (" * String representing enumerated value.\n"); t += (" */\n"); t += ("AString \n"); t += ("" + enumClassName + "::toGuiName(Enum enumValue) {\n"); t += (" if (initializedFlag == false) initialize();\n"); t += (" \n"); t += (" const " + enumClassName + "* enumInstance = findData(enumValue);\n"); t += (" return enumInstance->guiName;\n"); t += ("}\n"); t += ("\n"); t += ("/**\n"); t += (" * Get an enumerated value corresponding to its GUI name.\n"); t += (" * @param s \n"); t += (" * Name of enumerated value.\n"); t += (" * @param isValidOut \n"); t += (" * If not NULL, it is set indicating that a\n"); t += (" * enum value exists for the input name.\n"); t += (" * @return \n"); t += (" * Enumerated value.\n"); t += (" */\n"); t += ("" + enumClassName + "::Enum \n"); t += ("" + enumClassName + "::fromGuiName(const AString& guiName, bool* isValidOut)\n"); t += ("{\n"); t += (" if (initializedFlag == false) initialize();\n"); t += (" \n"); t += (" bool validFlag = false;\n"); t += (" Enum enumValue = " + enumClassName + "::enumData[0].enumValue;\n"); t += (" \n"); t += (" for (std::vector<" + enumClassName + ">::iterator iter = enumData.begin();\n"); t += (" iter != enumData.end();\n"); t += (" iter++) {\n"); t += (" const " + enumClassName + "& d = *iter;\n"); t += (" if (d.guiName == guiName) {\n"); t += (" enumValue = d.enumValue;\n"); t += (" validFlag = true;\n"); t += (" break;\n"); t += (" }\n"); t += (" }\n"); t += (" \n"); t += (" if (isValidOut != 0) {\n"); t += (" *isValidOut = validFlag;\n"); t += (" }\n"); t += (" else if (validFlag == false) {\n"); t += (" CaretAssertMessage(0, AString(\"guiName \" + guiName + \"failed to match enumerated value for type " + enumClassName + "\"));\n"); t += (" }\n"); t += (" return enumValue;\n"); t += ("}\n"); t += ("\n"); t += ("/**\n"); t += (" * Get the integer code for a data type.\n"); t += (" *\n"); t += (" * @return\n"); t += (" * Integer code for data type.\n"); t += (" */\n"); t += ("int32_t\n"); t += ("" + enumClassName + "::toIntegerCode(Enum enumValue)\n"); t += ("{\n"); t += (" if (initializedFlag == false) initialize();\n"); t += (" const " + enumClassName + "* enumInstance = findData(enumValue);\n"); t += (" return enumInstance->integerCode;\n"); t += ("}\n"); t += ("\n"); t += ("/**\n"); t += (" * Find the data type corresponding to an integer code.\n"); t += (" *\n"); t += (" * @param integerCode\n"); t += (" * Integer code for enum.\n"); t += (" * @param isValidOut\n"); t += (" * If not NULL, on exit isValidOut will indicate if\n"); t += (" * integer code is valid.\n"); t += (" * @return\n"); t += (" * Enum for integer code.\n"); t += (" */\n"); t += ("" + enumClassName + "::Enum\n"); t += ("" + enumClassName + "::fromIntegerCode(const int32_t integerCode, bool* isValidOut)\n"); t += ("{\n"); t += (" if (initializedFlag == false) initialize();\n"); t += (" \n"); t += (" bool validFlag = false;\n"); t += (" Enum enumValue = " + enumClassName + "::enumData[0].enumValue;\n"); t += (" \n"); t += (" for (std::vector<" + enumClassName + ">::iterator iter = enumData.begin();\n"); t += (" iter != enumData.end();\n"); t += (" iter++) {\n"); t += (" const " + enumClassName + "& enumInstance = *iter;\n"); t += (" if (enumInstance.integerCode == integerCode) {\n"); t += (" enumValue = enumInstance.enumValue;\n"); t += (" validFlag = true;\n"); t += (" break;\n"); t += (" }\n"); t += (" }\n"); t += (" \n"); t += (" if (isValidOut != 0) {\n"); t += (" *isValidOut = validFlag;\n"); t += (" }\n"); t += (" else if (validFlag == false) {\n"); t += (" CaretAssertMessage(0, AString(\"Integer code \" + AString::number(integerCode) + \"failed to match enumerated value for type " + enumClassName + "\"));\n"); t += (" }\n"); t += (" return enumValue;\n"); t += ("}\n"); t += ("\n"); t += ("/**\n"); t += (" * Get all of the enumerated type values. The values can be used\n"); t += (" * as parameters to toXXX() methods to get associated metadata.\n"); t += (" *\n"); t += (" * @param allEnums\n"); t += (" * A vector that is OUTPUT containing all of the enumerated values.\n"); t += (" */\n"); t += ("void\n"); t += ("" + enumClassName + "::getAllEnums(std::vector<" + enumClassName + "::Enum>& allEnums)\n"); t += ("{\n"); t += (" if (initializedFlag == false) initialize();\n"); t += (" \n"); t += (" allEnums.clear();\n"); t += (" \n"); t += (" for (std::vector<" + enumClassName + ">::iterator iter = enumData.begin();\n"); t += (" iter != enumData.end();\n"); t += (" iter++) {\n"); t += (" allEnums.push_back(iter->enumValue);\n"); t += (" }\n"); t += ("}\n"); t += ("\n"); t += ("/**\n"); t += (" * Get all of the names of the enumerated type values.\n"); t += (" *\n"); t += (" * @param allNames\n"); t += (" * A vector that is OUTPUT containing all of the names of the enumerated values.\n"); t += (" * @param isSorted\n"); t += (" * If true, the names are sorted in alphabetical order.\n"); t += (" */\n"); t += ("void\n"); t += ("" + enumClassName + "::getAllNames(std::vector& allNames, const bool isSorted)\n"); t += ("{\n"); t += (" if (initializedFlag == false) initialize();\n"); t += (" \n"); t += (" allNames.clear();\n"); t += (" \n"); t += (" for (std::vector<" + enumClassName + ">::iterator iter = enumData.begin();\n"); t += (" iter != enumData.end();\n"); t += (" iter++) {\n"); t += (" allNames.push_back(" + enumClassName + "::toName(iter->enumValue));\n"); t += (" }\n"); t += (" \n"); t += (" if (isSorted) {\n"); t += (" std::sort(allNames.begin(), allNames.end());\n"); t += (" }\n"); t += ("}\n"); t += ("\n"); t += ("/**\n"); t += (" * Get all of the GUI names of the enumerated type values.\n"); t += (" *\n"); t += (" * @param allNames\n"); t += (" * A vector that is OUTPUT containing all of the GUI names of the enumerated values.\n"); t += (" * @param isSorted\n"); t += (" * If true, the names are sorted in alphabetical order.\n"); t += (" */\n"); t += ("void\n"); t += ("" + enumClassName + "::getAllGuiNames(std::vector& allGuiNames, const bool isSorted)\n"); t += ("{\n"); t += (" if (initializedFlag == false) initialize();\n"); t += (" \n"); t += (" allGuiNames.clear();\n"); t += (" \n"); t += (" for (std::vector<" + enumClassName + ">::iterator iter = enumData.begin();\n"); t += (" iter != enumData.end();\n"); t += (" iter++) {\n"); t += (" allGuiNames.push_back(" + enumClassName + "::toGuiName(iter->enumValue));\n"); t += (" }\n"); t += (" \n"); t += (" if (isSorted) {\n"); t += (" std::sort(allGuiNames.begin(), allGuiNames.end());\n"); t += (" }\n"); t += ("}\n"); t += ("\n"); TextFile tf; tf.replaceText(t); try { tf.writeFile(outputFileName); } catch (const DataFileException& e) { throw CommandException(e); } } /** * Get a string containing information on how to use this enumerated type * with the EnumComboBoxTemplate in the GUI. * * @param enumClassName * Name of the enumerated type. * @return * String containing usage information. */ AString CommandClassCreateEnum::getEnumComboBoxTemplateHelpInfo(const AString& enumClassName) const { const AString firstLetter = enumClassName.left(1).toLower(); const AString memberName = ("m_" + firstLetter + enumClassName.mid(1) + "ComboBox"); const AString slotName = (memberName.mid(2) + "ItemActivated()"); const AString templateParameter = ("<" + enumClassName + "," + enumClassName + "::Enum>"); const AString setupIndentString(memberName.length() + QString("->setup<").length(), ' '); AString s(" * Using this enumerated type in the GUI with an EnumComboBoxTemplate\n" " * \n" " * Header File (.h)\n" " * Forward declare the data type:\n" " * class EnumComboBoxTemplate;\n" " * \n" " * Declare the member:\n" " * EnumComboBoxTemplate* " + memberName + ";\n" " * \n" " * Declare a slot that is called when user changes selection\n" " * private slots:\n" " * void " + slotName + ";\n" " * \n" " * Implementation File (.cxx)\n" " * Include the header files\n" " * #include \"EnumComboBoxTemplate.h\"\n" " * #include \"" + enumClassName + ".h\"\n" " * \n" " * Instatiate:\n" " * " + memberName + " = new EnumComboBoxTemplate(this);\n" " * " + memberName + "->setup" + templateParameter + "();\n" " * \n" " * Get notified when the user changes the selection: \n" " * QObject::connect(" + memberName + ", SIGNAL(itemActivated()),\n" " * this, SLOT(" + slotName + "));\n" " * \n" " * Update the selection:\n" " * " + memberName + "->setSelectedItem" + templateParameter + "(NEW_VALUE);\n" " * \n" " * Read the selection:\n" " * const " + enumClassName + "::Enum VARIABLE = " + memberName + "->getSelectedItem" + templateParameter + "();\n" " * \n"); return s; } connectome-workbench-1.2.3+git41-gc4c6c90/src/Commands/CommandClassCreateEnum.h000066400000000000000000000050571300200146000270020ustar00rootroot00000000000000#ifndef __COMMAND_CLASS_CREATE_ENUM_H__ #define __COMMAND_CLASS_CREATE_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 "CommandClassCreateBase.h" namespace caret { /// Command that creates class files for an enumerated type. class CommandClassCreateEnum : public CommandClassCreateBase { public: CommandClassCreateEnum(); virtual ~CommandClassCreateEnum(); virtual void executeOperation(ProgramParameters& parameters); AString getHelpInformation(const AString& /*programName*/); private: CommandClassCreateEnum(const CommandClassCreateEnum&); CommandClassCreateEnum& operator=(const CommandClassCreateEnum&); void createHeaderFile(const AString& outputFileName, const AString& enumClassName, const AString& ifdefName, const AString& ifdefNameStaticDeclaration, const int32_t numberOfEnumValues, const std::vector& enumValueNames, const bool isAutoNumber); void createImplementationFile(const AString& outputFileName, const AString& enumClassName, const AString& ifdefNameStaticDeclaration, const int32_t numberOfEnumValues, const std::vector& enumValueNames, const bool isAutoNumber); AString getEnumComboBoxTemplateHelpInfo(const AString& enumClassName) const; }; } // namespace #endif // __COMMAND_CLASS_CREATE_ENUM_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Commands/CommandClassCreateOperation.cxx000066400000000000000000000263211300200146000304060ustar00rootroot00000000000000/*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 "CaretAssertion.h" #include "CommandClassCreateOperation.h" #include "DataFileException.h" #include "FileInformation.h" #include "ProgramParameters.h" #include "TextFile.h" using namespace caret; /** * Constructor. */ CommandClassCreateOperation::CommandClassCreateOperation() : CommandClassCreateBase("-class-create-operation", "CREATE SOURCE CODE CLASS FILES (.h, .cxx) FOR OPERATION") { } /** * Destructor. */ CommandClassCreateOperation::~CommandClassCreateOperation() { } AString CommandClassCreateOperation::getHelpInformation(const AString& /*programName*/) { AString helpInfo = ("\n" "Create Operation Class header (.h) and implementation (.cxx) files.\n" "\n" "Usage: \n" " \n" " \n" " [-no-parameters]\n" "\n" " operation-class-name\n" " Required name of the operation class that MUST start with \"Operation\"\n" " \n" " command-line-switch\n" " Required command line switch for operation.\n" " \n" " short-description\n" " Required short description within double quotes.\n" " \n" " -no-parameters\n" " Optional parameter if the operation does not use parameters.\n" " \n" ); return helpInfo; } /** * Execute the operation. * * @param parameters * Parameters for the operation. * @throws CommandException * If the command failed. * @throws ProgramParametersException * If there is an error in the parameters. */ void CommandClassCreateOperation::executeOperation(ProgramParameters& parameters) { const AString operationClassName = parameters.nextString("Operation Class Name"); const AString commandLineSwitch = parameters.nextString("Command Line Switch"); const AString shortDescription = parameters.nextString("Short Description"); bool hasParametersFlag = true; while (parameters.hasNext()) { const AString& param = parameters.nextString("Create Class Parameter"); if (param == "-no-parameters") { hasParametersFlag = false; } else { throw CommandException("Invalid parameter: " + param); } } AString errorMessage; if (operationClassName.isEmpty()) { throw CommandException("Operation Class Name is empty."); } else { if (operationClassName.startsWith("Operation") == false) { throw CommandException("Operation Class Name must start with \"Operation\".\n"); } if (operationClassName == "Operation") { throw CommandException("\"Operation\" is not allowed for Operation Class Name"); } } if (commandLineSwitch.isEmpty()) { throw CommandException("Command line switch is empty."); } else if (commandLineSwitch.startsWith("-") == false) { throw CommandException("Command line must begin with \"-\"."); } if (shortDescription.isEmpty()) { throw CommandException("Short description is empty."); } const AString headerFileName = operationClassName + ".h"; const AString implementationFileName = operationClassName + ".cxx"; FileInformation headerInfo(headerFileName); if (headerInfo.exists()) { errorMessage += headerFileName + " exists and this command will not overwrite it.\n"; } FileInformation impInfo(implementationFileName); if (impInfo.exists()) { errorMessage += implementationFileName + " exists and this command will not overwrite it.\n"; } if (errorMessage.isEmpty() == false) { throw CommandException(errorMessage); } AString ifndefName; AString ifdefNameStaticDeclarations; this->getIfDefNames(operationClassName, ifndefName, ifdefNameStaticDeclarations); this->createHeaderFile(headerFileName, operationClassName, ifndefName, hasParametersFlag); this->createImplementationFile(implementationFileName, operationClassName, commandLineSwitch, shortDescription); } /** * Create and write the header (.h) file. * * @param outputFileName * Name for file that is written. * @param operationClassName * Name of operation type class. * @param ifndefName * Name of "ifndef" value. * @param hasParameters * True if the operation has parameters. */ void CommandClassCreateOperation::createHeaderFile(const AString& outputFileName, const AString& operationClassName, const AString& ifndefName, const bool hasParameters) { AString t; t += ("#ifndef " + ifndefName + "\n"); t += ("#define " + ifndefName + "\n"); t += this->getCopyright(); t += ("\n"); t += ("#include \"AbstractOperation.h\"\n"); t += ("\n"); t += ("namespace caret {\n"); t += ("\n"); t += (" class " + operationClassName + " : public AbstractOperation {\n"); t += ("\n"); t += (" public:\n"); t += (" static OperationParameters* getParameters();\n"); t += ("\n"); t += (" static void useParameters(OperationParameters* myParams, \n"); t += (" ProgressObject* myProgObj);\n"); t += ("\n"); t += (" static AString getCommandSwitch();\n"); t += ("\n"); t += (" static AString getShortDescription();\n"); t += ("\n"); if (hasParameters == false) { t += (" static bool takesParameters() { return false; }\n"); t += ("\n"); } t += (" };\n"); t += ("\n"); t += (" typedef TemplateAutoOperation<" + operationClassName + "> Auto" + operationClassName + ";\n"); t += ("\n"); t += ("} // namespace\n"); t += ("\n"); t += ("#endif //" + ifndefName + "\n"); t += ("\n"); TextFile tf; tf.replaceText(t); try { tf.writeFile(outputFileName); } catch (const DataFileException& e) { throw CommandException(e); } } /** * Create and write the implementation (.cxx) file. * * @param outputFileName * Name for file that is written. * @param operationClassName * Name of operation type class. * @param commandLineSwitch * Command line switch for operation. * @param shortDescription * Short description of operation. */ void CommandClassCreateOperation::createImplementationFile(const AString& outputFileName, const AString& operationClassName, const AString& commandLineSwitch, const AString& shortDescription) { AString t; t += this->getCopyright(); t += ("#include \"CaretAssert.h\"\n"); t += ("#include \"CaretLogger.h\"\n"); t += ("\n"); t += ("#include \"" + operationClassName + ".h\"\n"); t += ("#include \"OperationException.h\"\n"); t += ("\n"); t += ("using namespace caret;\n"); t += ("\n"); t += ("/**\n"); t += (" * \\class caret::" + operationClassName + " \n"); t += (" * \\brief " + shortDescription + "\n"); t += (" *\n"); t += (" * \n"); t += (" */\n"); t += ("\n"); t += ("/**\n"); t += (" * @return Command line switch\n"); t += (" */\n"); t += ("AString\n"); t += ("" + operationClassName + "::getCommandSwitch()\n"); t += ("{\n"); t += (" return \"" + commandLineSwitch + "\";\n"); t += ("}\n"); t += ("\n"); t += ("/**\n"); t += (" * @return Short description of operation\n"); t += (" */\n"); t += ("AString\n"); t += ("" + operationClassName + "::getShortDescription()\n"); t += ("{\n"); t += (" return \"" + shortDescription.toUpper() + "\";\n"); t += ("}\n"); t += ("\n"); t += ("/**\n"); t += (" * @return Parameters for operation\n"); t += (" */\n"); t += ("OperationParameters*\n"); t += ("" + operationClassName + "::getParameters()\n"); t += ("{\n"); t += (" OperationParameters* ret = new OperationParameters();\n"); t += (" \n"); t += (" AString helpText;\n"); t += (" \n"); t += (" ret->setHelpText(helpText);\n"); t += (" \n"); t += (" return ret;\n"); t += ("}\n"); t += ("\n"); t += ("/**\n"); t += (" * Use Parameters and perform operation\n"); t += (" */\n"); t += ("void\n"); t += ("" + operationClassName + "::useParameters(OperationParameters* myParams,\n"); t += (" ProgressObject* myProgObj)\n"); t += ("{\n"); t += (" LevelProgress myProgress(myProgObj);\n"); t += (" \n"); t += ("}\n"); t += ("\n"); TextFile tf; tf.replaceText(t); try { tf.writeFile(outputFileName); } catch (const DataFileException& e) { throw CommandException(e); } std::cout << std::endl; std::cout << "Operation was created successfully." << std::endl; std::cout << std::endl; std::cout << "Add the class files to Operations/CMakeLists.txt:" << std::endl; std::cout << " " << qPrintable(operationClassName) << ".h" << std::endl; std::cout << " " << qPrintable(operationClassName) << ".cxx" << std::endl; std::cout << std::endl; std::cout << "Add the header file and operation to Commands/CommandOperationManager.cxx:" << std::endl; std::cout << " #include \"" << qPrintable(operationClassName) << ".h\"" << std::endl; std::cout << " this->commandOperations.push_back(new CommandParser(new Auto" << operationClassName << "()));" << std::endl; std::cout << std::endl; } connectome-workbench-1.2.3+git41-gc4c6c90/src/Commands/CommandClassCreateOperation.h000066400000000000000000000042211300200146000300260ustar00rootroot00000000000000#ifndef __COMMAND_CLASS_CREATE_OPERATION_H__ #define __COMMAND_CLASS_CREATE_OPERATION_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 "CommandClassCreateBase.h" namespace caret { /// Command that creates class files for an operation class CommandClassCreateOperation : public CommandClassCreateBase { public: CommandClassCreateOperation(); virtual ~CommandClassCreateOperation(); virtual void executeOperation(ProgramParameters& parameters); AString getHelpInformation(const AString& /*programName*/); private: CommandClassCreateOperation(const CommandClassCreateOperation&); CommandClassCreateOperation& operator=(const CommandClassCreateOperation&); void createHeaderFile(const AString& outputFileName, const AString& operationClassName, const AString& ifndefName, const bool hasParameters); void createImplementationFile(const AString& outputFileName, const AString& operationClassName, const AString& commandLineSwitch, const AString& shortDescription); }; } // namespace #endif // __COMMAND_CLASS_CREATE_OPERATION_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Commands/CommandException.cxx000066400000000000000000000041731300200146000262730ustar00rootroot00000000000000/*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 "CommandException.h" #include using namespace caret; /** * Constructor. * */ CommandException::CommandException() : CaretException() { this->initializeMembersCommandException(); } /** * Constructor that uses stack trace from the exception * passed in as a parameter. * * @param e Any exception whose stack trace becomes * this exception's stack trace. * */ CommandException::CommandException( const CaretException& e) : CaretException(e) { this->initializeMembersCommandException(); } /** * Constructor. * * @param s Description of the exception. * */ CommandException::CommandException(const AString& s) : CaretException(s) { this->initializeMembersCommandException(); } /** * Copy Constructor. * @param e * Exception that is copied. */ CommandException::CommandException(const CommandException& e) : CaretException(e) { } /** * Assignment operator. * @param e * Exception that is copied. * @return * Copy of the exception. */ CommandException& CommandException::operator=(const CommandException& e) { if (this != &e) { CaretException::operator=(e); } return *this; } /** * Destructor */ CommandException::~CommandException() throw() { } void CommandException::initializeMembersCommandException() { } connectome-workbench-1.2.3+git41-gc4c6c90/src/Commands/CommandException.h000066400000000000000000000027101300200146000257130ustar00rootroot00000000000000#ifndef __COMMAND_EXCEPTION_H__ #define __COMMAND_EXCEPTION_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 "CaretException.h" namespace caret { /// An exception thrown during command processing. class CommandException : public CaretException { public: CommandException(); CommandException(const CaretException& e); CommandException(const AString& s); CommandException(const CommandException& e); CommandException& operator=(const CommandException& e); virtual ~CommandException() throw(); private: void initializeMembersCommandException(); }; } // namespace #endif // __COMMAND_EXCEPTION_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Commands/CommandOperation.cxx000066400000000000000000000050171300200146000262730ustar00rootroot00000000000000/*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 "CommandOperation.h" #include "CaretAssert.h" using namespace caret; /** * Constructor. * @param commandLineSwitch * Switch to select this command. * @param operationShortDescription * Short description of the command. */ CommandOperation::CommandOperation(const AString& commandLineSwitch, const AString& operationShortDescription) : CaretObject() { this->commandLineSwitch = commandLineSwitch; this->operationShortDescription = operationShortDescription; for (int i = 0; i < commandLineSwitch.length(); ++i)//release build should optimize out empty loops { CaretAssert(commandLineSwitch[i].unicode() < 128); } } /** * Destructor. */ CommandOperation::~CommandOperation() { } /** * Execute the command. * * @param parameters * Parameters for the operation. * @throws CommandException * If the command failed. */ void CommandOperation::execute(ProgramParameters& parameters, const bool& preventProvenance) { if (preventProvenance) { disableProvenance();//let provenance-ignorant commands not need to deal with an unused parameter } this->executeOperation(parameters); } void CommandOperation::disableProvenance() { } AString CommandOperation::doCompletion(ProgramParameters&, const bool&) { return ""; } bool CommandOperation::takesParameters() { return true; } /** * Get the short description of the operation. */ AString CommandOperation::getOperationShortDescription() const { return this->operationShortDescription; } /** * Get the command line switch for selecting the operation. */ AString CommandOperation::getCommandLineSwitch() const { return this->commandLineSwitch; } connectome-workbench-1.2.3+git41-gc4c6c90/src/Commands/CommandOperation.h000066400000000000000000000052501300200146000257170ustar00rootroot00000000000000#ifndef __COMMAND_OPERATION_H__ #define __COMMAND_OPERATION_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 "CommandException.h" #include "ProgramParametersException.h" #include "AString.h" namespace caret { class ProgramParameters; /// Abstract class for a command operation. class CommandOperation : public CaretObject { public: virtual ~CommandOperation(); void execute(ProgramParameters& parameters, const bool& preventProvenance); virtual AString doCompletion(ProgramParameters& parameters, const bool& useExtGlob); protected: /** * Execute the operation. * * @param parameters * Parameters for the operation. * @throws CommandException * If the command failed. * @throws ProgramParametersException * If there is an error in the parameters. */ virtual void executeOperation(ProgramParameters& parameters) = 0; virtual void disableProvenance(); CommandOperation(const AString& commandLineSwitch, const AString& operationShortDescription); private: CommandOperation(); CommandOperation(const CommandOperation&); CommandOperation& operator=(const CommandOperation&); public: AString getOperationShortDescription() const; AString getCommandLineSwitch() const; virtual AString getHelpInformation(const AString& programName) = 0; virtual bool takesParameters(); private: /** Short description listing commands purpose */ AString operationShortDescription; /** Switch on command line */ AString commandLineSwitch; }; } // namespace #endif // __COMMAND_OPERATION_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Commands/CommandOperationManager.cxx000066400000000000000000001710151300200146000275700ustar00rootroot00000000000000/*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 __COMMAND_OPERATION_MANAGER_DEFINE__ #include "CommandOperationManager.h" #undef __COMMAND_OPERATION_MANAGER_DEFINE__ #include "AlgorithmBorderResample.h" #include "AlgorithmBorderToVertices.h" #include "AlgorithmCiftiAllLabelsToROIs.h" #include "AlgorithmCiftiAverage.h" #include "AlgorithmCiftiAverageDenseROI.h" #include "AlgorithmCiftiAverageROICorrelation.h" #include "AlgorithmCiftiCorrelation.h" #include "AlgorithmCiftiCorrelationGradient.h" #include "AlgorithmCiftiCreateDenseScalar.h" #include "AlgorithmCiftiCreateDenseTimeseries.h" #include "AlgorithmCiftiCreateLabel.h" #include "AlgorithmCiftiCrossCorrelation.h" #include "AlgorithmCiftiDilate.h" #include "AlgorithmCiftiErode.h" #include "AlgorithmCiftiExtrema.h" #include "AlgorithmCiftiFalseCorrelation.h" #include "AlgorithmCiftiFindClusters.h" #include "AlgorithmCiftiGradient.h" #include "AlgorithmCiftiLabelAdjacency.h" #include "AlgorithmCiftiLabelToBorder.h" #include "AlgorithmCiftiLabelToROI.h" #include "AlgorithmCiftiMergeDense.h" #include "AlgorithmCiftiMergeParcels.h" #include "AlgorithmCiftiPairwiseCorrelation.h" #include "AlgorithmCiftiParcellate.h" #include "AlgorithmCiftiParcelMappingToLabel.h" #include "AlgorithmCiftiReduce.h" #include "AlgorithmCiftiReorder.h" #include "AlgorithmCiftiReplaceStructure.h" #include "AlgorithmCiftiResample.h" #include "AlgorithmCiftiROIsFromExtrema.h" #include "AlgorithmCiftiSeparate.h" #include "AlgorithmCiftiSmoothing.h" #include "AlgorithmCiftiTranspose.h" #include "AlgorithmCiftiVectorOperation.h" #include "AlgorithmCreateSignedDistanceVolume.h" #include "AlgorithmFiberDotProducts.h" #include "AlgorithmFociResample.h" #include "AlgorithmGiftiAllLabelsToROIs.h" #include "AlgorithmGiftiLabelAddPrefix.h" #include "AlgorithmGiftiLabelToROI.h" #include "AlgorithmLabelDilate.h" #include "AlgorithmLabelErode.h" #include "AlgorithmLabelModifyKeys.h" #include "AlgorithmLabelResample.h" #include "AlgorithmLabelToBorder.h" #include "AlgorithmLabelToVolumeMapping.h" #include "AlgorithmMetricDilate.h" #include "AlgorithmMetricErode.h" #include "AlgorithmMetricEstimateFWHM.h" #include "AlgorithmMetricExtrema.h" #include "AlgorithmMetricFalseCorrelation.h" #include "AlgorithmMetricFillHoles.h" #include "AlgorithmMetricFindClusters.h" #include "AlgorithmMetricGradient.h" #include "AlgorithmMetricReduce.h" #include "AlgorithmMetricRegression.h" #include "AlgorithmMetricRemoveIslands.h" #include "AlgorithmMetricResample.h" #include "AlgorithmMetricROIsFromExtrema.h" #include "AlgorithmMetricROIsToBorder.h" #include "AlgorithmMetricSmoothing.h" #include "AlgorithmMetricTFCE.h" #include "AlgorithmMetricToVolumeMapping.h" #include "AlgorithmMetricVectorOperation.h" #include "AlgorithmMetricVectorTowardROI.h" #include "AlgorithmNodesInsideBorder.h" //-border-to-rois #include "AlgorithmSignedDistanceToSurface.h" #include "AlgorithmSurfaceAffineRegression.h" #include "AlgorithmSurfaceApplyAffine.h" #include "AlgorithmSurfaceApplyWarpfield.h" #include "AlgorithmSurfaceAverage.h" #include "AlgorithmSurfaceCortexLayer.h" #include "AlgorithmSurfaceCreateSphere.h" #include "AlgorithmSurfaceCurvature.h" #include "AlgorithmSurfaceDistortion.h" #include "AlgorithmSurfaceFlipLR.h" #include "AlgorithmSurfaceGenerateInflated.h" #include "AlgorithmSurfaceInflation.h" #include "AlgorithmSurfaceMatch.h" #include "AlgorithmSurfaceModifySphere.h" #include "AlgorithmSurfaceResample.h" #include "AlgorithmSurfaceSmoothing.h" #include "AlgorithmSurfaceSphereProjectUnproject.h" #include "AlgorithmSurfaceToSurface3dDistance.h" #include "AlgorithmSurfaceWedgeVolume.h" #include "AlgorithmVolumeAffineResample.h" #include "AlgorithmVolumeAllLabelsToROIs.h" #include "AlgorithmVolumeDilate.h" #include "AlgorithmVolumeErode.h" #include "AlgorithmVolumeEstimateFWHM.h" #include "AlgorithmVolumeExtrema.h" #include "AlgorithmVolumeFillHoles.h" #include "AlgorithmVolumeFindClusters.h" #include "AlgorithmVolumeGradient.h" #include "AlgorithmVolumeLabelProbability.h" #include "AlgorithmVolumeLabelToROI.h" #include "AlgorithmVolumeLabelToSurfaceMapping.h" #include "AlgorithmVolumeParcelResampling.h" #include "AlgorithmVolumeParcelResamplingGeneric.h" #include "AlgorithmVolumeParcelSmoothing.h" #include "AlgorithmVolumeReduce.h" #include "AlgorithmVolumeRemoveIslands.h" #include "AlgorithmVolumeROIsFromExtrema.h" #include "AlgorithmVolumeSmoothing.h" #include "AlgorithmVolumeTFCE.h" #include "AlgorithmVolumeToSurfaceMapping.h" #include "AlgorithmVolumeVectorOperation.h" #include "AlgorithmVolumeWarpfieldResample.h" #include "OperationAddToSpecFile.h" #include "OperationBackendAverageDenseROI.h" #include "OperationBackendAverageROICorrelation.h" #include "OperationBorderExportColorTable.h" #include "OperationBorderFileExportToCaret5.h" #include "OperationBorderLength.h" #include "OperationBorderMerge.h" #include "OperationCiftiChangeMapping.h" #include "OperationCiftiChangeTimestep.h" #include "OperationCiftiConvert.h" #include "OperationCiftiConvertToScalar.h" #include "OperationCiftiCopyMapping.h" #include "OperationCiftiCreateDenseFromTemplate.h" #include "OperationCiftiCreateParcellatedFromTemplate.h" #include "OperationCiftiCreateScalarSeries.h" #include "OperationCiftiEstimateFWHM.h" #include "OperationCiftiExportDenseMapping.h" #include "OperationCiftiLabelExportTable.h" #include "OperationCiftiLabelImport.h" #include "OperationCiftiMath.h" #include "OperationCiftiMerge.h" #include "OperationCiftiPalette.h" #include "OperationCiftiResampleDconnMemory.h" #include "AlgorithmCiftiRestrictDenseMap.h" #include "OperationCiftiROIAverage.h" #include "OperationCiftiSeparateAll.h" #include "OperationCiftiStats.h" #include "OperationCiftiWeightedStats.h" #include "OperationConvertAffine.h" #include "OperationConvertFiberOrientations.h" #include "OperationConvertMatrix4ToMatrix2.h" #include "OperationConvertMatrix4ToWorkbenchSparse.h" #include "OperationConvertWarpfield.h" #include "OperationEstimateFiberBinghams.h" #include "OperationFileConvert.h" #include "OperationFileInformation.h" #include "OperationFociGetProjectionVertex.h" #include "OperationFociListCoords.h" #include "OperationGiftiConvert.h" #include "OperationLabelExportTable.h" #include "OperationLabelMask.h" #include "OperationLabelMerge.h" #include "OperationMetadataRemoveProvenance.h" #include "OperationMetadataStringReplace.h" #include "OperationMetricConvert.h" #include "OperationMetricLabelImport.h" #include "OperationMetricMask.h" #include "OperationMetricMath.h" #include "OperationMetricMerge.h" #include "OperationMetricPalette.h" #include "OperationMetricStats.h" #include "OperationMetricVertexSum.h" #include "OperationMetricWeightedStats.h" #include "OperationNiftiInformation.h" #include "OperationProbtrackXDotConvert.h" #include "OperationSceneFileMerge.h" #include "OperationSceneFileRelocate.h" #include "OperationSetMapName.h" #include "OperationSetMapNames.h" #include "OperationSetStructure.h" #include "OperationShowScene.h" #include "OperationSpecFileMerge.h" #include "OperationSpecFileRelocate.h" #include "OperationSurfaceClosestVertex.h" #include "OperationSurfaceCoordinatesToMetric.h" #include "OperationSurfaceCutResample.h" #include "OperationSurfaceFlipNormals.h" #include "OperationSurfaceGeodesicDistance.h" #include "OperationSurfaceGeodesicROIs.h" #include "OperationSurfaceInformation.h" #include "OperationSurfaceNormals.h" #include "OperationSurfaceVertexAreas.h" #include "OperationVolumeCapturePlane.h" #include "OperationVolumeCopyExtensions.h" #include "OperationVolumeCreate.h" #include "OperationVolumeLabelExportTable.h" #include "OperationVolumeLabelImport.h" #include "OperationVolumeMath.h" #include "OperationVolumeMerge.h" #include "OperationVolumePalette.h" #include "OperationVolumeReorient.h" #include "OperationVolumeSetSpace.h" #include "OperationVolumeStats.h" #include "OperationVolumeWeightedStats.h" #include "OperationWbsparseMergeDense.h" #include "OperationZipSceneFile.h" #include "OperationZipSpecFile.h" #include "AlgorithmException.h" #include "ApplicationInformation.h" #include "CommandParser.h" #include "OperationException.h" #include "CommandClassAddMember.h" #include "CommandClassCreate.h" #include "CommandClassCreateAlgorithm.h" #include "CommandClassCreateEnum.h" #include "CommandClassCreateOperation.h" #include "CommandC11xTesting.h" #include "CommandUnitTest.h" #include "ProgramParameters.h" #include "CaretLogger.h" #include "dot_wrapper.h" #include "StructureEnum.h" #include using namespace caret; using namespace std; /** * Get the command operation manager. * * return * Pointer to the command operation manager. */ CommandOperationManager* CommandOperationManager::getCommandOperationManager() { if (singletonCommandOperationManager == NULL) { singletonCommandOperationManager = new CommandOperationManager(); } return singletonCommandOperationManager; } /** * Delete the command operation manager. */ void CommandOperationManager::deleteCommandOperationManager() { if (singletonCommandOperationManager != NULL) { delete singletonCommandOperationManager; singletonCommandOperationManager = NULL; } } /** * Constructor. */ CommandOperationManager::CommandOperationManager() { this->commandOperations.push_back(new CommandParser(new AutoAlgorithmBorderResample())); this->commandOperations.push_back(new CommandParser(new AutoAlgorithmBorderToVertices())); this->commandOperations.push_back(new CommandParser(new AutoAlgorithmCiftiAllLabelsToROIs())); this->commandOperations.push_back(new CommandParser(new AutoAlgorithmCiftiAverage())); this->commandOperations.push_back(new CommandParser(new AutoAlgorithmCiftiAverageDenseROI())); this->commandOperations.push_back(new CommandParser(new AutoAlgorithmCiftiAverageROICorrelation())); this->commandOperations.push_back(new CommandParser(new AutoAlgorithmCiftiCorrelation())); this->commandOperations.push_back(new CommandParser(new AutoAlgorithmCiftiCorrelationGradient())); this->commandOperations.push_back(new CommandParser(new AutoAlgorithmCiftiCreateDenseScalar())); this->commandOperations.push_back(new CommandParser(new AutoAlgorithmCiftiCreateDenseTimeseries())); this->commandOperations.push_back(new CommandParser(new AutoAlgorithmCiftiCreateLabel())); this->commandOperations.push_back(new CommandParser(new AutoAlgorithmCiftiCrossCorrelation())); this->commandOperations.push_back(new CommandParser(new AutoAlgorithmCiftiDilate())); this->commandOperations.push_back(new CommandParser(new AutoAlgorithmCiftiErode())); this->commandOperations.push_back(new CommandParser(new AutoAlgorithmCiftiExtrema())); this->commandOperations.push_back(new CommandParser(new AutoAlgorithmCiftiFalseCorrelation())); 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 AutoAlgorithmCiftiLabelToBorder())); this->commandOperations.push_back(new CommandParser(new AutoAlgorithmCiftiLabelToROI())); this->commandOperations.push_back(new CommandParser(new AutoAlgorithmCiftiMergeDense())); this->commandOperations.push_back(new CommandParser(new AutoAlgorithmCiftiMergeParcels())); this->commandOperations.push_back(new CommandParser(new AutoAlgorithmCiftiPairwiseCorrelation())); this->commandOperations.push_back(new CommandParser(new AutoAlgorithmCiftiParcellate())); this->commandOperations.push_back(new CommandParser(new AutoAlgorithmCiftiParcelMappingToLabel())); this->commandOperations.push_back(new CommandParser(new AutoAlgorithmCiftiReduce())); this->commandOperations.push_back(new CommandParser(new AutoAlgorithmCiftiReorder())); this->commandOperations.push_back(new CommandParser(new AutoAlgorithmCiftiReplaceStructure())); this->commandOperations.push_back(new CommandParser(new AutoAlgorithmCiftiResample())); this->commandOperations.push_back(new CommandParser(new AutoAlgorithmCiftiRestrictDenseMap())); this->commandOperations.push_back(new CommandParser(new AutoAlgorithmCiftiROIsFromExtrema())); this->commandOperations.push_back(new CommandParser(new AutoAlgorithmCiftiSeparate())); this->commandOperations.push_back(new CommandParser(new AutoAlgorithmCiftiSmoothing())); this->commandOperations.push_back(new CommandParser(new AutoAlgorithmCiftiTranspose())); this->commandOperations.push_back(new CommandParser(new AutoAlgorithmCiftiVectorOperation())); this->commandOperations.push_back(new CommandParser(new AutoAlgorithmCreateSignedDistanceVolume())); this->commandOperations.push_back(new CommandParser(new AutoAlgorithmFiberDotProducts())); this->commandOperations.push_back(new CommandParser(new AutoAlgorithmFociResample())); this->commandOperations.push_back(new CommandParser(new AutoAlgorithmGiftiAllLabelsToROIs())); this->commandOperations.push_back(new CommandParser(new AutoAlgorithmGiftiLabelAddPrefix())); this->commandOperations.push_back(new CommandParser(new AutoAlgorithmGiftiLabelToROI())); 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 AutoAlgorithmLabelResample())); this->commandOperations.push_back(new CommandParser(new AutoAlgorithmLabelToBorder())); this->commandOperations.push_back(new CommandParser(new AutoAlgorithmLabelToVolumeMapping())); this->commandOperations.push_back(new CommandParser(new AutoAlgorithmMetricDilate())); this->commandOperations.push_back(new CommandParser(new AutoAlgorithmMetricErode())); this->commandOperations.push_back(new CommandParser(new AutoAlgorithmMetricEstimateFWHM())); this->commandOperations.push_back(new CommandParser(new AutoAlgorithmMetricExtrema())); this->commandOperations.push_back(new CommandParser(new AutoAlgorithmMetricFalseCorrelation())); this->commandOperations.push_back(new CommandParser(new AutoAlgorithmMetricFillHoles())); this->commandOperations.push_back(new CommandParser(new AutoAlgorithmMetricFindClusters())); this->commandOperations.push_back(new CommandParser(new AutoAlgorithmMetricGradient())); this->commandOperations.push_back(new CommandParser(new AutoAlgorithmMetricReduce())); this->commandOperations.push_back(new CommandParser(new AutoAlgorithmMetricRegression())); this->commandOperations.push_back(new CommandParser(new AutoAlgorithmMetricRemoveIslands())); this->commandOperations.push_back(new CommandParser(new AutoAlgorithmMetricResample())); this->commandOperations.push_back(new CommandParser(new AutoAlgorithmMetricROIsFromExtrema())); this->commandOperations.push_back(new CommandParser(new AutoAlgorithmMetricROIsToBorder())); this->commandOperations.push_back(new CommandParser(new AutoAlgorithmMetricSmoothing())); this->commandOperations.push_back(new CommandParser(new AutoAlgorithmMetricTFCE())); this->commandOperations.push_back(new CommandParser(new AutoAlgorithmMetricToVolumeMapping())); this->commandOperations.push_back(new CommandParser(new AutoAlgorithmMetricVectorOperation())); this->commandOperations.push_back(new CommandParser(new AutoAlgorithmMetricVectorTowardROI())); this->commandOperations.push_back(new CommandParser(new AutoAlgorithmNodesInsideBorder()));//-border-to-rois this->commandOperations.push_back(new CommandParser(new AutoAlgorithmSignedDistanceToSurface())); this->commandOperations.push_back(new CommandParser(new AutoAlgorithmSurfaceAffineRegression())); this->commandOperations.push_back(new CommandParser(new AutoAlgorithmSurfaceApplyAffine())); this->commandOperations.push_back(new CommandParser(new AutoAlgorithmSurfaceApplyWarpfield())); this->commandOperations.push_back(new CommandParser(new AutoAlgorithmSurfaceAverage())); this->commandOperations.push_back(new CommandParser(new AutoAlgorithmSurfaceCortexLayer())); this->commandOperations.push_back(new CommandParser(new AutoAlgorithmSurfaceCreateSphere())); this->commandOperations.push_back(new CommandParser(new AutoAlgorithmSurfaceCurvature())); this->commandOperations.push_back(new CommandParser(new AutoAlgorithmSurfaceDistortion())); this->commandOperations.push_back(new CommandParser(new AutoAlgorithmSurfaceFlipLR())); this->commandOperations.push_back(new CommandParser(new AutoAlgorithmSurfaceGenerateInflated())); this->commandOperations.push_back(new CommandParser(new AutoAlgorithmSurfaceInflation())); this->commandOperations.push_back(new CommandParser(new AutoAlgorithmSurfaceMatch())); this->commandOperations.push_back(new CommandParser(new AutoAlgorithmSurfaceModifySphere())); this->commandOperations.push_back(new CommandParser(new AutoAlgorithmSurfaceResample())); this->commandOperations.push_back(new CommandParser(new AutoAlgorithmSurfaceSmoothing())); this->commandOperations.push_back(new CommandParser(new AutoAlgorithmSurfaceSphereProjectUnproject())); this->commandOperations.push_back(new CommandParser(new AutoAlgorithmSurfaceToSurface3dDistance())); this->commandOperations.push_back(new CommandParser(new AutoAlgorithmSurfaceWedgeVolume())); 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 AutoAlgorithmVolumeErode())); this->commandOperations.push_back(new CommandParser(new AutoAlgorithmVolumeEstimateFWHM())); this->commandOperations.push_back(new CommandParser(new AutoAlgorithmVolumeExtrema())); this->commandOperations.push_back(new CommandParser(new AutoAlgorithmVolumeFillHoles())); this->commandOperations.push_back(new CommandParser(new AutoAlgorithmVolumeFindClusters())); this->commandOperations.push_back(new CommandParser(new AutoAlgorithmVolumeGradient())); this->commandOperations.push_back(new CommandParser(new AutoAlgorithmVolumeLabelProbability())); this->commandOperations.push_back(new CommandParser(new AutoAlgorithmVolumeLabelToROI())); this->commandOperations.push_back(new CommandParser(new AutoAlgorithmVolumeLabelToSurfaceMapping())); this->commandOperations.push_back(new CommandParser(new AutoAlgorithmVolumeParcelResampling())); this->commandOperations.push_back(new CommandParser(new AutoAlgorithmVolumeParcelResamplingGeneric())); this->commandOperations.push_back(new CommandParser(new AutoAlgorithmVolumeParcelSmoothing())); this->commandOperations.push_back(new CommandParser(new AutoAlgorithmVolumeReduce())); this->commandOperations.push_back(new CommandParser(new AutoAlgorithmVolumeRemoveIslands())); this->commandOperations.push_back(new CommandParser(new AutoAlgorithmVolumeROIsFromExtrema())); this->commandOperations.push_back(new CommandParser(new AutoAlgorithmVolumeSmoothing())); 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 AutoAlgorithmVolumeWarpfieldResample())); this->commandOperations.push_back(new CommandParser(new AutoOperationAddToSpecFile())); this->commandOperations.push_back(new CommandParser(new AutoOperationBackendAverageDenseROI())); this->commandOperations.push_back(new CommandParser(new AutoOperationBackendAverageROICorrelation())); this->commandOperations.push_back(new CommandParser(new AutoOperationBorderExportColorTable())); this->commandOperations.push_back(new CommandParser(new AutoOperationBorderFileExportToCaret5())); this->commandOperations.push_back(new CommandParser(new AutoOperationBorderLength())); this->commandOperations.push_back(new CommandParser(new AutoOperationBorderMerge())); this->commandOperations.push_back(new CommandParser(new AutoOperationCiftiChangeMapping())); this->commandOperations.push_back(new CommandParser(new AutoOperationCiftiConvert())); this->commandOperations.push_back(new CommandParser(new AutoOperationCiftiCreateDenseFromTemplate())); this->commandOperations.push_back(new CommandParser(new AutoOperationCiftiCreateParcellatedFromTemplate())); this->commandOperations.push_back(new CommandParser(new AutoOperationCiftiCreateScalarSeries())); this->commandOperations.push_back(new CommandParser(new AutoOperationCiftiEstimateFWHM())); this->commandOperations.push_back(new CommandParser(new AutoOperationCiftiExportDenseMapping())); this->commandOperations.push_back(new CommandParser(new AutoOperationCiftiLabelExportTable())); this->commandOperations.push_back(new CommandParser(new AutoOperationCiftiLabelImport())); this->commandOperations.push_back(new CommandParser(new AutoOperationCiftiMath())); this->commandOperations.push_back(new CommandParser(new AutoOperationCiftiMerge())); this->commandOperations.push_back(new CommandParser(new AutoOperationCiftiPalette())); this->commandOperations.push_back(new CommandParser(new AutoOperationCiftiResampleDconnMemory())); this->commandOperations.push_back(new CommandParser(new AutoOperationCiftiROIAverage())); this->commandOperations.push_back(new CommandParser(new AutoOperationCiftiStats())); this->commandOperations.push_back(new CommandParser(new AutoOperationCiftiWeightedStats())); this->commandOperations.push_back(new CommandParser(new AutoOperationConvertAffine())); this->commandOperations.push_back(new CommandParser(new AutoOperationConvertFiberOrientations())); this->commandOperations.push_back(new CommandParser(new AutoOperationConvertMatrix4ToMatrix2())); this->commandOperations.push_back(new CommandParser(new AutoOperationConvertMatrix4ToWorkbenchSparse())); this->commandOperations.push_back(new CommandParser(new AutoOperationConvertWarpfield())); this->commandOperations.push_back(new CommandParser(new AutoOperationEstimateFiberBinghams())); this->commandOperations.push_back(new CommandParser(new AutoOperationFileConvert())); this->commandOperations.push_back(new CommandParser(new AutoOperationFileInformation())); this->commandOperations.push_back(new CommandParser(new AutoOperationFociGetProjectionVertex())); this->commandOperations.push_back(new CommandParser(new AutoOperationFociListCoords())); this->commandOperations.push_back(new CommandParser(new AutoOperationGiftiConvert())); this->commandOperations.push_back(new CommandParser(new AutoOperationLabelExportTable())); this->commandOperations.push_back(new CommandParser(new AutoOperationLabelMask())); this->commandOperations.push_back(new CommandParser(new AutoOperationLabelMerge())); this->commandOperations.push_back(new CommandParser(new AutoOperationMetadataRemoveProvenance())); this->commandOperations.push_back(new CommandParser(new AutoOperationMetadataStringReplace())); this->commandOperations.push_back(new CommandParser(new AutoOperationMetricConvert())); this->commandOperations.push_back(new CommandParser(new AutoOperationMetricLabelImport())); this->commandOperations.push_back(new CommandParser(new AutoOperationMetricMask())); this->commandOperations.push_back(new CommandParser(new AutoOperationMetricMath())); this->commandOperations.push_back(new CommandParser(new AutoOperationMetricMerge())); this->commandOperations.push_back(new CommandParser(new AutoOperationMetricPalette())); this->commandOperations.push_back(new CommandParser(new AutoOperationMetricStats())); this->commandOperations.push_back(new CommandParser(new AutoOperationMetricWeightedStats())); this->commandOperations.push_back(new CommandParser(new AutoOperationNiftiInformation())); this->commandOperations.push_back(new CommandParser(new AutoOperationProbtrackXDotConvert())); this->commandOperations.push_back(new CommandParser(new AutoOperationSceneFileMerge())); this->commandOperations.push_back(new CommandParser(new AutoOperationSceneFileRelocate())); this->commandOperations.push_back(new CommandParser(new AutoOperationSetMapNames())); this->commandOperations.push_back(new CommandParser(new AutoOperationSetStructure())); if (OperationShowScene::isShowSceneCommandAvailable()) { this->commandOperations.push_back(new CommandParser(new AutoOperationShowScene())); } this->commandOperations.push_back(new CommandParser(new AutoOperationSpecFileMerge())); this->commandOperations.push_back(new CommandParser(new AutoOperationSpecFileRelocate())); this->commandOperations.push_back(new CommandParser(new AutoOperationSurfaceClosestVertex())); this->commandOperations.push_back(new CommandParser(new AutoOperationSurfaceCoordinatesToMetric())); this->commandOperations.push_back(new CommandParser(new AutoOperationSurfaceCutResample())); this->commandOperations.push_back(new CommandParser(new AutoOperationSurfaceFlipNormals())); this->commandOperations.push_back(new CommandParser(new AutoOperationSurfaceGeodesicDistance())); 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 AutoOperationSurfaceVertexAreas())); this->commandOperations.push_back(new CommandParser(new AutoOperationVolumeCapturePlane())); this->commandOperations.push_back(new CommandParser(new AutoOperationVolumeCopyExtensions())); this->commandOperations.push_back(new CommandParser(new AutoOperationVolumeCreate())); this->commandOperations.push_back(new CommandParser(new AutoOperationVolumeLabelExportTable())); this->commandOperations.push_back(new CommandParser(new AutoOperationVolumeLabelImport())); this->commandOperations.push_back(new CommandParser(new AutoOperationVolumeMath())); this->commandOperations.push_back(new CommandParser(new AutoOperationVolumeMerge())); this->commandOperations.push_back(new CommandParser(new AutoOperationVolumePalette())); this->commandOperations.push_back(new CommandParser(new AutoOperationVolumeReorient())); this->commandOperations.push_back(new CommandParser(new AutoOperationVolumeSetSpace())); this->commandOperations.push_back(new CommandParser(new AutoOperationVolumeStats())); this->commandOperations.push_back(new CommandParser(new AutoOperationVolumeWeightedStats())); this->commandOperations.push_back(new CommandParser(new AutoOperationWbsparseMergeDense())); this->commandOperations.push_back(new CommandParser(new AutoOperationZipSceneFile())); this->commandOperations.push_back(new CommandParser(new AutoOperationZipSpecFile())); this->commandOperations.push_back(new CommandClassAddMember()); this->commandOperations.push_back(new CommandClassCreate()); this->commandOperations.push_back(new CommandClassCreateAlgorithm()); this->commandOperations.push_back(new CommandClassCreateEnum()); this->commandOperations.push_back(new CommandClassCreateOperation()); #ifdef WORKBENCH_HAVE_C11X this->commandOperations.push_back(new CommandC11xTesting()); #endif // WORKBENCH_HAVE_C11X this->commandOperations.push_back(new CommandUnitTest()); this->deprecatedOperations.push_back(new CommandParser(new AutoOperationCiftiChangeTimestep())); this->deprecatedOperations.push_back(new CommandParser(new AutoOperationCiftiConvertToScalar())); this->deprecatedOperations.push_back(new CommandParser(new AutoOperationCiftiCopyMapping())); this->deprecatedOperations.push_back(new CommandParser(new AutoOperationCiftiSeparateAll())); this->deprecatedOperations.push_back(new CommandParser(new AutoOperationMetricVertexSum())); this->deprecatedOperations.push_back(new CommandParser(new AutoOperationSetMapName())); } /** * Destructor. */ CommandOperationManager::~CommandOperationManager() { uint64_t numberOfCommands = this->commandOperations.size(); for (uint64_t i = 0; i < numberOfCommands; i++) { delete this->commandOperations[i]; this->commandOperations[i] = NULL; } this->commandOperations.clear(); uint64_t numberOfDeprecated = this->deprecatedOperations.size(); for (uint64_t i = 0; i < numberOfDeprecated; i++) { delete this->deprecatedOperations[i]; this->deprecatedOperations[i] = NULL; } this->deprecatedOperations.clear(); } /** * Run a command. * * @param parameters * Reference to the command's parameters. * @throws CommandException * If the command failed. */ void CommandOperationManager::runCommand(ProgramParameters& parameters) { vector globalOptionArgs; bool preventProvenance = getGlobalOption(parameters, "-disable-provenance", 0, globalOptionArgs);//check these BEFORE we test if we have a command switch, because they remove the switch and arguments from the ProgramParameters if (getGlobalOption(parameters, "-logging", 1, globalOptionArgs)) { bool valid = false; const LogLevelEnum::Enum level = LogLevelEnum::fromName(globalOptionArgs[0], &valid); if (!valid) throw CommandException("unrecognized logging level: '" + globalOptionArgs[0] + "'"); CaretLogger::getLogger()->setLevel(level); } if (getGlobalOption(parameters, "-simd", 1, globalOptionArgs)) { bool valid = false; const DotSIMDEnum::Enum impl = DotSIMDEnum::fromName(globalOptionArgs[0], &valid); if (!valid) throw CommandException("unrecognized SIMD type: '" + globalOptionArgs[0] + "'"); DotSIMDEnum::Enum retval = dot_set_impl(impl); if (impl != DOT_AUTO && retval != impl) { CaretLogWarning("SIMD type '" + DotSIMDEnum::toName(impl) + "' not supported (could be cpu, compiler, or build options), using '" + DotSIMDEnum::toName(retval) + "'"); } } const uint64_t numberOfCommands = this->commandOperations.size(); const uint64_t numberOfDeprecated = this->deprecatedOperations.size(); if (parameters.hasNext() == false) { printHelpInfo(); return; } AString commandSwitch; commandSwitch = fixUnicode(parameters.nextString("Command Name"), false); if (commandSwitch == "-help") { printHelpInfo(); } else if (commandSwitch == "-arguments-help") { printArgumentsHelp("wb_command"); } else if (commandSwitch == "-cifti-help") { printCiftiHelp("wb_command"); } else if (commandSwitch == "-gifti-help") { printGiftiHelp("wb_command"); } else if (commandSwitch == "-version") { printVersionInfo(); } else if (commandSwitch == "-list-commands") { printAllCommands(); } else if (commandSwitch == "-list-deprecated-commands") { printDeprecatedCommands(); } else if (commandSwitch == "-all-commands-help") { printAllCommandsHelpInfo("wb_command"); } else { CommandOperation* operation = NULL; for (uint64_t i = 0; i < numberOfCommands; i++) { if (this->commandOperations[i]->getCommandLineSwitch() == commandSwitch) { operation = this->commandOperations[i]; break; } } if (operation == NULL) { for (uint64_t i = 0; i < numberOfDeprecated; i++) { if (this->deprecatedOperations[i]->getCommandLineSwitch() == commandSwitch) { operation = this->deprecatedOperations[i]; break; } } } if (operation == NULL) { if (!parameters.hasNext()) { printAllCommandsMatching(commandSwitch); } else { throw CommandException("Command \"" + commandSwitch + "\" not found."); } } else { if (!parameters.hasNext() && operation->takesParameters()) { cout << operation->getHelpInformation("wb_command") << endl; } else { operation->execute(parameters, preventProvenance); } } } } AString CommandOperationManager::doCompletion(ProgramParameters& parameters, const bool& useExtGlob) { AString ret; vector globalOptionArgs; /*OptionInfo provInfo = */parseGlobalOption(parameters, "-disable-provenance", 0, globalOptionArgs, true);//we need to at least strip out the global options for other parsing to work OptionInfo loggingInfo = parseGlobalOption(parameters, "-logging", 1, globalOptionArgs, true);//the previous option doesn't take arguments, doesn't need completion testing if (loggingInfo.specified && !loggingInfo.complete) {//user is tab completing the logging option, and as it only takes one argument, we know what the completions are vector logLevels; LogLevelEnum::getAllEnums(logLevels); ret = "wordlist "; for (int i = 0; i < (int)logLevels.size(); ++i) { if (i != 0) ret += "\\ ";//backslash-escaped space to leave the list of levels as one word ret += LogLevelEnum::toName(logLevels[i]); } return ret; } OptionInfo simdInfo = parseGlobalOption(parameters, "-simd", 1, globalOptionArgs, true);//the previous option doesn't take arguments, doesn't need completion testing if (simdInfo.specified && !simdInfo.complete) {//user is tab completing the logging option, and as it only takes one argument, we know what the completions are vector simdTypes = DotSIMDEnum::getAllEnums(); ret = "wordlist "; for (int i = 0; i < (int)simdTypes.size(); ++i) { if (i != 0) ret += "\\ ";//backslash-escaped space to leave the list of levels as one word ret += DotSIMDEnum::toName(simdTypes[i]); } return ret; } ret = "wordlist -disable-provenance\\ -logging\\ -simd";//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"; for (uint64_t i = 0; i < numberOfCommands; i++) { ret += "\\ " + commandOperations[i]->getCommandLineSwitch(); } for (uint64_t i = 0; i < numberOfDeprecated; i++) { ret += "\\ " + deprecatedOperations[i]->getCommandLineSwitch(); } return ret; } //only processing commands take additional arguments, so we can now ignore -help and similar AString commandSwitch; commandSwitch = fixUnicode(parameters.nextString("Command Name"), true); for (uint64_t i = 0; i < numberOfCommands; i++) { if (commandOperations[i]->getCommandLineSwitch() == commandSwitch) { AString commandCompletion = commandOperations[i]->doCompletion(parameters, useExtGlob); if (commandCompletion != "") { if (ret != "") ret += " "; ret += commandCompletion; } return ret; } } for (uint64_t i = 0; i < numberOfDeprecated; i++) { if (deprecatedOperations[i]->getCommandLineSwitch() == commandSwitch) { AString commandCompletion = deprecatedOperations[i]->doCompletion(parameters, useExtGlob); if (commandCompletion != "") { if (ret != "") ret += " "; ret += commandCompletion; } return ret; } } //if we get here, then the operation switch is wrong, or goes to an informational option return ret; } bool CommandOperationManager::getGlobalOption(ProgramParameters& parameters, const AString& optionString, const int& numArgs, vector& arguments) { OptionInfo retval = parseGlobalOption(parameters, optionString, numArgs, arguments, false); if (retval.specified && !retval.complete) { throw CommandException("missing argument #" + AString::number(arguments.size() + 1) + " to global option "); } return retval.specified; } CommandOperationManager::OptionInfo CommandOperationManager::parseGlobalOption(ProgramParameters& parameters, const AString& optionString, const int& numArgs, vector& arguments, const bool& quiet) { OptionInfo ret;//initializes to false, false, -1 int32_t initialIndex = parameters.getParameterIndex();//before returning, restore initial index - also, only search from current index onwards while (parameters.hasNext()) { bool hyphenReplaced = false; AString testRaw = parameters.nextString("global option"); AString test = testRaw.fixUnicodeHyphens(&hyphenReplaced, NULL, quiet); if (test == optionString) { if (!quiet && hyphenReplaced) { CaretLogWarning("replaced non-ascii hyphen/dash characters in global option '" + testRaw + "' with ascii '-'"); }//delay parameter removal until we know we have all arguments if (!quiet && ret.specified) { CaretLogInfo("global option '" + optionString + "' specified multiple times"); } arguments.clear(); OptionInfo temp;//initializes to false, false, -1 temp.specified = true; temp.index = parameters.getParameterIndex() - 1;//the function actually reports the index of the next parameter for (int i = 0; i < numArgs; ++i) { if (!parameters.hasNext()) { return temp;//complete is still false - since this is a fatal parsing error, don't reset the parameter index } arguments.push_back(parameters.nextString("global option argument")); } for (int i = 0; i < numArgs; ++i) { parameters.remove();//remove arguments } parameters.remove();//remove option switch temp.complete = true; ret = temp; } } parameters.setParameterIndex(initialIndex); return ret; } /** * Print all of the commands. */ void CommandOperationManager::printAllCommands() { map cmdMap; int64_t longestSwitch = 0; const uint64_t numberOfCommands = this->commandOperations.size(); for (uint64_t i = 0; i < numberOfCommands; i++) { CommandOperation* op = this->commandOperations[i]; const AString cmdSwitch = op->getCommandLineSwitch(); const int64_t switchLength = cmdSwitch.length(); if (switchLength > longestSwitch) { longestSwitch = switchLength; } cmdMap.insert(make_pair(cmdSwitch, op->getOperationShortDescription())); #ifndef NDEBUG const AString helpInfo = op->getHelpInformation("");//TSC: generating help info takes a little processing (populating and walking an OperationParameters tree for each command) if (helpInfo.isEmpty()) {//So, test the same define as for asserts and skip this check in release CaretLogSevere("Command has no help info: " + cmdSwitch); } #endif } for (map::iterator iter = cmdMap.begin(); iter != cmdMap.end(); iter++) { AString cmdSwitch = iter->first; cmdSwitch = cmdSwitch.leftJustified(longestSwitch + 2, ' '); AString description = iter->second; cout << qPrintable(cmdSwitch) << qPrintable(description) << endl; } } void CommandOperationManager::printDeprecatedCommands() { map cmdMap; int64_t longestSwitch = 0; const uint64_t numberOfDeprecated = this->deprecatedOperations.size(); for (uint64_t i = 0; i < numberOfDeprecated; i++) { CommandOperation* op = this->deprecatedOperations[i]; const AString cmdSwitch = op->getCommandLineSwitch(); const int64_t switchLength = cmdSwitch.length(); if (switchLength > longestSwitch) { longestSwitch = switchLength; } cmdMap.insert(make_pair(cmdSwitch, op->getOperationShortDescription())); #ifndef NDEBUG const AString helpInfo = op->getHelpInformation("");//TSC: generating help info takes a little processing (populating and walking an OperationParameters tree for each command) if (helpInfo.isEmpty()) {//So, test the same define as for asserts and skip this check in release CaretLogSevere("Command has no help info: " + cmdSwitch); } #endif } for (map::iterator iter = cmdMap.begin(); iter != cmdMap.end(); iter++) { AString cmdSwitch = iter->first; cmdSwitch = cmdSwitch.leftJustified(longestSwitch + 2, ' '); AString description = iter->second; cout << qPrintable(cmdSwitch) << qPrintable(description) << endl; } } /** * Print all of the commands matching a partial switch. */ void CommandOperationManager::printAllCommandsMatching(const AString& partialSwitch) { map cmdMap; int64_t longestSwitch = -1; const uint64_t numberOfCommands = this->commandOperations.size(); for (uint64_t i = 0; i < numberOfCommands; i++) { CommandOperation* op = this->commandOperations[i]; const AString cmdSwitch = op->getCommandLineSwitch(); if (cmdSwitch.startsWith(partialSwitch)) { const int64_t switchLength = cmdSwitch.length(); if (switchLength > longestSwitch) { longestSwitch = switchLength; } cmdMap.insert(make_pair(cmdSwitch, op->getOperationShortDescription())); #ifndef NDEBUG const AString helpInfo = op->getHelpInformation("");//TSC: generating help info takes a little processing (populating and walking an OperationParameters tree for each command) if (helpInfo.isEmpty()) {//So, test the same define as for asserts and skip this check in release CaretLogSevere("Command has no help info: " + cmdSwitch); } #endif } } if (longestSwitch == -1)//no command found { throw CommandException("the switch '" + partialSwitch + "' does not match any processing commands"); } for (map::iterator iter = cmdMap.begin(); iter != cmdMap.end(); iter++) { AString cmdSwitch = iter->first; if (cmdSwitch.startsWith(partialSwitch)) { cmdSwitch = cmdSwitch.leftJustified(longestSwitch + 2, ' '); AString description = iter->second; cout << qPrintable(cmdSwitch) << qPrintable(description) << endl; } } } /** * Get the command operations. * * @return * A vector containing the command operations. * Do not modify the returned value. */ vector CommandOperationManager::getCommandOperations() { return this->commandOperations; } void CommandOperationManager::printHelpInfo() { cout << ApplicationInformation().getSummaryInformationInString("\n"); //guide for wrap, assuming 80 columns: | cout << endl << "Information options:" << endl; cout << " -help show this help info" << endl; cout << " -arguments-help explain the format of subcommand help info" << 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; cout << " -list-commands list all processing subcommands" << endl; 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; cout << endl; cout << "If the first argument is not recognized, all processing commands that start" << endl; cout << " with the argument are displayed" << endl; cout << endl; } void CommandOperationManager::printArgumentsHelp(const AString& programName) { //guide for wrap, assuming 80 columns: | cout << " To get the help information on a subcommand, run it without any additional" << endl; cout << " arguments. Options can occur in any position within the correct scope, and" << endl; cout << " can have suboptions, which must occur within the scope of the option. The" << endl; cout << " easiest way to get this right is to specify options and arguments in the" << endl; cout << " order they are listed. As an example, consider this help information:" << endl; cout << endl;//guide for wrap, assuming 80 columns: | cout << "$ " << programName << " -volume-math" << endl; cout << "EVALUATE EXPRESSION ON VOLUME FILES" << endl; cout << " " << programName << " -volume-math" << endl; cout << " - the expression to evaluate, in quotes" << endl; cout << " - output - the output volume" << endl; cout << endl;//guide for wrap, assuming 80 columns: | 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 << " - 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: | cout << " [-subvolume] - select a single subvolume" << endl; cout << " - the subvolume number or name" << endl; cout << endl;//guide for wrap, assuming 80 columns: | cout << " [-repeat] - reuse a single subvolume for each subvolume of calculation" << endl; cout << "..." << endl; 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 << " '' 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; cout << " Each option starts a new scope, and all options and arguments end any scope" << endl; cout << " that they are not valid in. For example, this command is correct:" << endl; cout << endl;//guide for wrap, assuming 80 columns: | cout << "$ " << programName << " -volume-math 'sin(x)' sin_x.nii.gz -fixnan 0 -var x x.nii.gz -subvolume 1" << endl; cout << endl; cout << " as is this one (though less intuitive):" << endl; cout << endl; cout << "$ " << programName << " -volume-math -fixnan 0 'sin(x)' -var x -subvolume 1 x.nii.gz sin_x.nii.gz" << endl; cout << endl;//guide for wrap, assuming 80 columns: | cout << " while this one is not, because the -fixnan option ends the scope of the -var" << endl; cout << " option before all of its mandatory arguments are given:" << endl; cout << endl; cout << "$ " << programName << " -volume-math 'sin(x)' sin_x.nii.gz -var x -fixnan 0 x.nii.gz -subvolume 1" << endl; cout << endl; //guide for wrap, assuming 80 columns: | cout << " and this one is incorrect because the -subvolume option occurs after the" << endl; cout << " scope of the -var option has ended due to -fixnan:" << endl; cout << endl; cout << "$ " << programName << " -volume-math 'sin(x)' sin_x.nii.gz -var x x.nii.gz -fixnan 0 -subvolume 1" << endl; cout << endl;//guide for wrap, assuming 80 columns: | cout << " and this one is similarly incorrect because the -subvolume option occurs" << endl; cout << " after the scope of the -var option has ended due to the volume-out argument:" << endl; cout << endl; cout << "$ " << programName << " -volume-math 'sin(x)' -fixnan 0 -var x x.nii.gz sin_x.nii.gz -subvolume 1" << endl; cout << endl; } void CommandOperationManager::printCiftiHelp(const AString& /*programName*/) { //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 << " '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; cout << " opposite direction, as it is conceptually simpler. A single cifti file is a" << endl; 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 << 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; cout << " 'V1', and the surface vertices in V1)" << endl; cout << " Scalars: each index is simply given a name (i.e. 'Myelin')" << endl; cout << " Series: each index is assigned a quantity in a linear series (i.e., a" << endl; cout << " timeseries of 0 sec, 0.7 sec, 1.4 sec, ...)" << endl; cout << " Labels: each index is assigned a name (i.e., 'Visual Areas'), but also a" << endl; cout << " list of labels that maps integer data values to names and colors (i.e." << endl; cout << " {(5, 'V1', #ff0000), (7, 'V2', #00ff00), ...}" << endl; cout << endl;//guide for wrap, assuming 80 columns: | cout << " The commands that operate on cifti files often require you to specify which" << endl; cout << " dimension they should operate on. Because cifti files can contain 3" << endl; cout << " dimensions, we specify them as which dimension to operate along, that is, " << endl; cout << " the ROW dimension refers to the mapping along the length of a row." << endl; cout << " Additionally, the ROW dimension is the *first* dimension in a cifti file," << endl; cout << " unlike 2D matrices in linear algebra. This means that increasing the value" << endl; cout << " of the first index moves rightwards in the matrix, not downwards." << endl; cout << endl;//guide for wrap, assuming 80 columns: | cout << " The common types of cifti files and the mapping types they use are:" << endl; cout << " dconn: ROW is dense, COLUMN is dense" << endl; cout << " dscalar: ROW is scalars, COLUMN is dense" << endl; cout << " dtseries: ROW is series, COLUMN is dense" << endl; cout << " dlabel: ROW is labels, COLUMN is dense" << endl; cout << " pconn: ROW is parcels, COLUMN is parcels" << endl; cout << " pdconn: ROW is dense, COLUMN is parcels" << endl; cout << " dpconn: ROW is parcels, COLUMN is dense" << endl; cout << " pscalar: ROW is scalars, COLUMN is parcels" << endl; cout << " ptseries: ROW is series, COLUMN is parcels" << endl; cout << endl;//guide for wrap, assuming 80 columns: | cout << " For the full details of the CIFTI format, see" << endl; cout << " http://www.nitrc.org/projects/cifti/" << endl; cout << endl;//guide for wrap, assuming 80 columns: | } void CommandOperationManager::printGiftiHelp(const AString& /*programName*/) { //guide for wrap, assuming 80 columns: | cout << " The GIFTI format is an established data file format intended for use with" << endl; cout << " surface-based data. It has subtypes for geometry (.surf.gii), continuous" << endl; cout << " data (.func.gii, .shape.gii), and integer label data (.label.gii). The" << endl; cout << " files that contain data, rather than geometry, consist mainly of a 2D array," << endl; cout << " with one dimension having length equal to the number of vertices in the" << endl; cout << " surface. Label files (.label.gii) also contain a list of integer values" << endl; cout << " that are used in the file, plus a name and a color for each one. In" << endl; cout << " workbench, the files for continuous data are called 'metric files', and" << endl; cout << " .func.gii is usually the preferred extension, but there is no difference in" << endl; cout << " file format between .func.gii and .shape.gii. Geometry files are simply" << endl; cout << " called 'surface files', and must contain only the coordinate and triangle" << endl; cout << " arrays. Notably, other software may put data arrays (the equivalent of a" << endl; cout << " metric file) into the same file as the geometry information. Workbench does" << endl; cout << " not support this kind of combined format, and you must use other tools to" << endl; cout << " separate the data array from the geometry." << endl; cout << endl;//guide for wrap, assuming 80 columns: | cout << " For the full details of the GIFTI format, see" << endl; cout << " http://www.nitrc.org/projects/gifti/" << endl; cout << endl;//guide for wrap, assuming 80 columns: | } void CommandOperationManager::printVersionInfo() { ApplicationInformation myInfo; vector myLines; myInfo.getAllInformation(myLines); for (int i = 0; i < (int)myLines.size(); ++i) { cout << myLines[i] << endl; } } void CommandOperationManager::printAllCommandsHelpInfo(const AString& programName) { map cmdMap; const uint64_t numberOfCommands = this->commandOperations.size(); for (uint64_t i = 0; i < numberOfCommands; i++) { CommandOperation* op = this->commandOperations[i]; cmdMap[op->getCommandLineSwitch()] = op; } for (map::iterator iter = cmdMap.begin(); iter != cmdMap.end(); iter++) { cout << iter->first << endl; cout << iter->second->getHelpInformation(programName) << endl << endl; } } AString CommandOperationManager::fixUnicode(const AString& input, const bool& quiet) { bool hyphenReplaced = false, otherNonAscii = false; AString ret = input.fixUnicodeHyphens(&hyphenReplaced, &otherNonAscii, quiet); if (otherNonAscii) { throw CaretException("found non-ascii character in operation switch '" + input + "', but one that is not recognized as a dash/hyphen/minus"); } if (!quiet && hyphenReplaced) { CaretLogWarning("replaced non-ascii hyphen/dash characters in operation switch '" + input + "' with ascii '-'"); } return ret; } connectome-workbench-1.2.3+git41-gc4c6c90/src/Commands/CommandOperationManager.h000066400000000000000000000066041300200146000272160ustar00rootroot00000000000000#ifndef __COMMAND_OPERATION_MANAGER_H__ #define __COMMAND_OPERATION_MANAGER_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 "CaretObject.h" #include "CommandException.h" namespace caret { class CommandOperation; class ProgramParameters; /// Manages all command operations. class CommandOperationManager : public CaretObject { public: static CommandOperationManager* getCommandOperationManager(); static void deleteCommandOperationManager(); ~CommandOperationManager(); void runCommand(ProgramParameters& parameters); AString doCompletion(ProgramParameters& parameters, const bool& useExtGlob); std::vector getCommandOperations(); private: CommandOperationManager(); CommandOperationManager(const CommandOperationManager&); CommandOperationManager& operator=(const CommandOperationManager&); void printAllCommands(); void printDeprecatedCommands(); void printAllCommandsMatching(const AString& partialSwitch); void printAllCommandsHelpInfo(const AString& programName); void printHelpInfo(); void printArgumentsHelp(const AString& programName); void printCiftiHelp(const AString& programName); void printGiftiHelp(const AString& programName); void printVersionInfo(); bool getGlobalOption(ProgramParameters& parameters, const AString& optionString, const int& numArgs, std::vector& arguments); struct OptionInfo { bool specified; bool complete; int index;//only valid when complete is false, might not be needed OptionInfo() { specified = false; complete = false; index = -1; } }; OptionInfo parseGlobalOption(ProgramParameters& parameters, const AString& optionString, const int& numArgs, std::vector& arguments, const bool& quiet); static AString fixUnicode(const AString& input, const bool& quiet); private: std::vector commandOperations, deprecatedOperations; static CommandOperationManager* singletonCommandOperationManager; }; #ifdef __COMMAND_OPERATION_MANAGER_DEFINE__ CommandOperationManager* CommandOperationManager::singletonCommandOperationManager = NULL; #endif // __COMMAND_OPERATION_MANAGER_DEFINE__ } // namespace #endif // __COMMAND_OPERATION_MANAGER_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Commands/CommandParser.cxx000066400000000000000000001547311300200146000255770ustar00rootroot00000000000000/*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 "CommandParser.h" #include "AlgorithmException.h" #include "ApplicationInformation.h" #include "BorderFile.h" #include "CaretAssert.h" #include "CaretCommandLine.h" #include "CaretDataFileHelper.h" #include "CaretLogger.h" #include "CiftiFile.h" #include "DataFileException.h" #include "FileInformation.h" #include "FociFile.h" #include "GiftiMetaData.h" #include "LabelFile.h" #include "MetricFile.h" #include "OperationException.h" #include "SurfaceFile.h" #include "VolumeFile.h" #include #include using namespace caret; using namespace std; const AString CommandParser::PROVENANCE_NAME = "Provenance"; const AString CommandParser::PARENT_PROVENANCE_NAME = "ParentProvenance"; const AString CommandParser::PROGRAM_PROVENANCE_NAME = "ProgramProvenance"; const AString CommandParser::CWD_PROVENANCE_NAME = "WorkingDirectory"; CommandParser::CommandParser(AutoOperationInterface* myAutoOper) : CommandOperation(myAutoOper->getCommandSwitch(), myAutoOper->getShortDescription()), OperationParserInterface(myAutoOper) { m_doProvenance = true; } void CommandParser::disableProvenance() { m_doProvenance = false; } void CommandParser::executeOperation(ProgramParameters& parameters) { CaretPointer myAlgParams(m_autoOper->getParameters());//could be an autopointer, but this is safer vector myOutAssoc; m_provenance = caret_global_commandLine; //the idea is to have m_provenance set before the command executes, so it can be overridden, but have m_parentProvenance set AFTER the processing is complete //the parent provenance should never be generated manually m_parentProvenance = "";//in case someone tries to use the same instance more than once m_workingDir = QDir::currentPath();//get the current path, in case some stupid command changes the working directory //these get set on output files during writeOutput (and for on-disk in provenanceBeforeOperation) parseComponent(myAlgParams.getPointer(), parameters, myOutAssoc);//parsing block parameters.verifyAllParametersProcessed(); makeOnDiskOutputs(myOutAssoc);//check for input on-disk files used as output on-disk files //code to show what arguments map to what parameters should go here if (m_doProvenance) provenanceBeforeOperation(myOutAssoc); m_autoOper->useParameters(myAlgParams.getPointer(), NULL);//TODO: progress status for caret_command? would probably get messed up by any command info output vector uncheckedWarnings = myAlgParams->findUncheckedParams("the command"); for (size_t i = 0; i < uncheckedWarnings.size(); ++i) { CaretLogWarning("developer warning: " + uncheckedWarnings[i]); } if (m_doProvenance) provenanceAfterOperation(myOutAssoc); //TODO: deallocate input files - give abstract parameter a virtual deallocate method? use CaretPointer and rely on reference counting? writeOutput(myOutAssoc); } void CommandParser::showParsedOperation(ProgramParameters& parameters) { CaretPointer myAlgParams(m_autoOper->getParameters());//could be an autopointer, but this is safer vector myOutAssoc; parseComponent(myAlgParams.getPointer(), parameters, myOutAssoc, true);//parsing block parameters.verifyAllParametersProcessed(); //don't execute or write parsed output } void CommandParser::parseComponent(ParameterComponent* myComponent, ProgramParameters& parameters, vector& outAssociation, bool debug) {//IMPORTANT: update completionComponent() and friends with any change to parsing logic 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 AString rawArg = parameters.nextString(myComponent->m_paramList[i]->m_shortName); AString nextArg = rawArg.fixUnicodeHyphens(&hyphenReplaced); if (hyphenReplaced) { CaretLogWarning("replaced non-ascii hyphen/dash characters in argument '" + rawArg + "' with ascii '-'"); } if (!nextArg.isEmpty() && nextArg[0] == '-') { bool success = parseOption(nextArg, myComponent, parameters, outAssociation, debug); if (!success) { switch (myComponent->m_paramList[i]->getType()) { case OperationParametersEnum::STRING: case OperationParametersEnum::INT: case OperationParametersEnum::DOUBLE: break;//it is probably a negative number, so don't throw an exception unless it fails to parse as one default: throw ProgramParametersException("Invalid option \"" + nextArg + "\" while next required argument is <" + myComponent->m_paramList[i]->m_shortName + ">, option is either incorrect, or incorrectly placed"); }; } else { --i; continue;//so skip trying to parse it as a required argument } } const OperationParametersEnum::Enum nextType = myComponent->m_paramList[i]->getType();// need in catch statement below try { switch (myComponent->m_paramList[i]->getType()) { case OperationParametersEnum::BOOL: { parameters.backup(); ((BooleanParameter*)myComponent->m_paramList[i])->m_parameter = parameters.nextBoolean(myComponent->m_paramList[i]->m_shortName); if (debug) { cout << "Parameter <" << myComponent->m_paramList[i]->m_shortName << "> parsed as "; cout << (((BooleanParameter*)myComponent->m_paramList[i])->m_parameter ? "true" : "false") << endl; } break; } case OperationParametersEnum::BORDER: { CaretPointer myFile(new BorderFile()); myFile->readFile(nextArg); if (m_doProvenance) { const GiftiMetaData* md = myFile->getFileMetaData(); if (md != NULL) { AString prov = md->get(PROVENANCE_NAME); if (prov != "") { m_parentProvenance += nextArg + ":\n" + prov + "\n\n"; } } } ((BorderParameter*)myComponent->m_paramList[i])->m_parameter = myFile; if (debug) { cout << "Parameter <" << myComponent->m_paramList[i]->m_shortName << "> opened file with name "; cout << nextArg << endl; } break; } case OperationParametersEnum::CIFTI: { FileInformation myInfo(nextArg); CaretPointer myFile(new CiftiFile()); myFile->openFile(nextArg); m_inputCiftiNames[myInfo.getCanonicalFilePath()] = myFile;//track input cifti, so we can check their size if (m_doProvenance)//just an optimization, if we aren't going to write provenance, don't generate it, either { const GiftiMetaData* md = myFile->getCiftiXML().getFileMetaData(); if (md != NULL) { if (md->exists(PROVENANCE_NAME)) { AString provenance = md->get(PROVENANCE_NAME); if (provenance != "") { m_parentProvenance += nextArg + ":\n" + provenance + "\n\n"; } } } } ((CiftiParameter*)myComponent->m_paramList[i])->m_parameter = myFile; if (debug) { cout << "Parameter <" << myComponent->m_paramList[i]->m_shortName << "> opened file with name "; cout << nextArg << endl; } break; } case OperationParametersEnum::DOUBLE: { parameters.backup(); ((DoubleParameter*)myComponent->m_paramList[i])->m_parameter = parameters.nextDouble(myComponent->m_paramList[i]->m_shortName); if (debug) { cout << "Parameter <" << myComponent->m_paramList[i]->m_shortName << "> parsed as "; cout << ((DoubleParameter*)myComponent->m_paramList[i])->m_parameter << endl; } break; } case OperationParametersEnum::FOCI: { CaretPointer myFile(new FociFile()); myFile->readFile(nextArg); if (m_doProvenance) { const GiftiMetaData* md = myFile->getFileMetaData(); if (md != NULL) { AString prov = md->get(PROVENANCE_NAME); if (prov != "") { m_parentProvenance += nextArg + ":\n" + prov + "\n\n"; } } } ((FociParameter*)myComponent->m_paramList[i])->m_parameter = myFile; if (debug) { cout << "Parameter <" << myComponent->m_paramList[i]->m_shortName << "> opened file with name "; cout << nextArg << endl; } break; } case OperationParametersEnum::INT: { parameters.backup(); ((IntegerParameter*)myComponent->m_paramList[i])->m_parameter = parameters.nextLong(myComponent->m_paramList[i]->m_shortName); if (debug) { cout << "Parameter <" << myComponent->m_paramList[i]->m_shortName << "> parsed as "; cout << ((IntegerParameter*)myComponent->m_paramList[i])->m_parameter << endl; } break; } case OperationParametersEnum::LABEL: { CaretPointer myFile(new LabelFile()); myFile->readFile(nextArg); if (m_doProvenance) { const GiftiMetaData* md = myFile->getFileMetaData(); if (md != NULL) { AString prov = md->get(PROVENANCE_NAME); if (prov != "") { m_parentProvenance += nextArg + ":\n" + prov + "\n\n"; } } } ((LabelParameter*)myComponent->m_paramList[i])->m_parameter = myFile; if (debug) { cout << "Parameter <" << myComponent->m_paramList[i]->m_shortName << "> opened file with name "; cout << nextArg << endl; } break; } case OperationParametersEnum::METRIC: { CaretPointer myFile(new MetricFile()); myFile->readFile(nextArg); if (m_doProvenance) { const GiftiMetaData* md = myFile->getFileMetaData(); if (md != NULL) { AString prov = md->get(PROVENANCE_NAME); if (prov != "") { m_parentProvenance += nextArg + ":\n" + prov + "\n\n"; } } } ((MetricParameter*)myComponent->m_paramList[i])->m_parameter = myFile; if (debug) { cout << "Parameter <" << myComponent->m_paramList[i]->m_shortName << "> opened file with name "; cout << nextArg << endl; } break; } case OperationParametersEnum::STRING: { ((StringParameter*)myComponent->m_paramList[i])->m_parameter = nextArg; if (debug) { cout << "Parameter <" << myComponent->m_paramList[i]->m_shortName << "> parsed as "; cout << ((StringParameter*)myComponent->m_paramList[i])->m_parameter << endl; } break; } case OperationParametersEnum::SURFACE: { CaretPointer myFile(new SurfaceFile()); myFile->readFile(nextArg); if (m_doProvenance) { const GiftiMetaData* md = myFile->getFileMetaData(); if (md != NULL) { AString prov = md->get(PROVENANCE_NAME); if (prov != "") { m_parentProvenance += nextArg + ":\n" + prov + "\n\n"; } } } ((SurfaceParameter*)myComponent->m_paramList[i])->m_parameter = myFile; if (debug) { cout << "Parameter <" << myComponent->m_paramList[i]->m_shortName << "> opened file with name "; cout << nextArg << endl; } break; } case OperationParametersEnum::VOLUME: { CaretPointer myFile(new VolumeFile()); myFile->readFile(nextArg); if (m_doProvenance) { const GiftiMetaData* md = myFile->getFileMetaData(); if (md != NULL) { AString prov = md->get(PROVENANCE_NAME); if (prov != "") { m_parentProvenance += nextArg + ":\n" + prov + "\n\n"; } } } ((VolumeParameter*)myComponent->m_paramList[i])->m_parameter = myFile; if (debug) { cout << "Parameter <" << myComponent->m_paramList[i]->m_shortName << "> opened file with name "; cout << nextArg << endl; } break; } }; } catch (const bad_alloc&) { switch (nextType) { case OperationParametersEnum::BORDER: case OperationParametersEnum::CIFTI: case OperationParametersEnum::FOCI: case OperationParametersEnum::LABEL: case OperationParametersEnum::METRIC: case OperationParametersEnum::SURFACE: case OperationParametersEnum::VOLUME: /* * Provide information to the user about which * file caused the std::bad_alloc including * the size of the file. */ throw DataFileException(nextArg, CaretDataFileHelper::createBadAllocExceptionMessage(nextArg)); break; case OperationParametersEnum::DOUBLE: case OperationParametersEnum::INT: case OperationParametersEnum::STRING: case OperationParametersEnum::BOOL: throw DataFileException("Unable to allocate memory for input: " + nextArg); break; } } } 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 AString rawArg = parameters.nextString(myComponent->m_outputList[i]->m_shortName); AString nextArg = rawArg.fixUnicodeHyphens(&hyphenReplaced); if (hyphenReplaced) { CaretLogWarning("replaced non-ascii hyphen/dash characters in argument '" + rawArg + "' with ascii '-'"); } if (!nextArg.isEmpty() && nextArg[0] == '-') { bool success = parseOption(nextArg, myComponent, parameters, outAssociation, debug); if (!success) { throw ProgramParametersException("Invalid option \"" + nextArg + "\" while next reqired argument is <" + myComponent->m_outputList[i]->m_shortName + ">, option is either incorrect, or incorrectly placed"); } --i;//options do not set required arguments continue;//so rewind the index and skip trying to parse it as a required argument } OutputAssoc tempItem; tempItem.m_fileName = nextArg; tempItem.m_param = myComponent->m_outputList[i]; switch (myComponent->m_outputList[i]->getType())//allocate outputs that only have in-memory implementations { case OperationParametersEnum::BORDER: { CaretPointer& myFile = ((BorderParameter*)(myComponent->m_outputList[i]))->m_parameter; myFile.grabNew(new BorderFile()); break; } case OperationParametersEnum::CIFTI: break;//we create this in makeOnDiskOutputs(), and do the metadata stuff in provenanceForOnDiskOutputs() for this type case OperationParametersEnum::FOCI: { CaretPointer& myFile = ((FociParameter*)(myComponent->m_outputList[i]))->m_parameter; myFile.grabNew(new FociFile()); break; } case OperationParametersEnum::LABEL: { CaretPointer& myFile = ((LabelParameter*)(myComponent->m_outputList[i]))->m_parameter; myFile.grabNew(new LabelFile()); break; } case OperationParametersEnum::METRIC: { CaretPointer& myFile = ((MetricParameter*)(myComponent->m_outputList[i]))->m_parameter; myFile.grabNew(new MetricFile()); break; } case OperationParametersEnum::SURFACE: { CaretPointer& myFile = ((SurfaceParameter*)(myComponent->m_outputList[i]))->m_parameter; myFile.grabNew(new SurfaceFile()); break; } case OperationParametersEnum::VOLUME: { CaretPointer& myFile = ((VolumeParameter*)(myComponent->m_outputList[i]))->m_parameter; myFile.grabNew(new VolumeFile()); break; } case OperationParametersEnum::DOUBLE://ignore these output types case OperationParametersEnum::INT: case OperationParametersEnum::STRING: case OperationParametersEnum::BOOL: CaretLogWarning("encountered ignored output type, " + OperationParametersEnum::toName(myComponent->m_outputList[i]->getType())); break; } outAssociation.push_back(tempItem); if (debug) { cout << "Output parameter <" << tempItem.m_param->m_shortName << "> given output name "; cout << tempItem.m_fileName << endl; } } parseRemainingOptions(myComponent, parameters, outAssociation, debug); } bool CommandParser::parseOption(const AString& mySwitch, ParameterComponent* myComponent, ProgramParameters& parameters, vector& outAssociation, bool debug) { for (uint32_t i = 0; i < myComponent->m_optionList.size(); ++i) { if (mySwitch == myComponent->m_optionList[i]->m_optionSwitch) { if (debug) { cout << "Now parsing option " << myComponent->m_optionList[i]->m_optionSwitch << endl; } if (myComponent->m_optionList[i]->m_present) { throw ProgramParametersException("Option \"" + mySwitch + "\" specified more than once"); } myComponent->m_optionList[i]->m_present = true; parseComponent(myComponent->m_optionList[i], parameters, outAssociation, debug); if (debug) { cout << "Finished parsing option " << myComponent->m_optionList[i]->m_optionSwitch << endl; } return true; } } for (uint32_t i = 0; i < myComponent->m_repeatableOptions.size(); ++i) { if (mySwitch == myComponent->m_repeatableOptions[i]->m_optionSwitch) { if (debug) { cout << "Now parsing repeatable option " << myComponent->m_repeatableOptions[i]->m_optionSwitch << endl; } myComponent->m_repeatableOptions[i]->m_instances.push_back(new ParameterComponent(myComponent->m_repeatableOptions[i]->m_template)); parseComponent(myComponent->m_repeatableOptions[i]->m_instances.back(), parameters, outAssociation, debug); if (debug) { cout << "Finished parsing repeatable option " << myComponent->m_repeatableOptions[i]->m_optionSwitch << endl; } return true; } } return false; } void CommandParser::parseRemainingOptions(ParameterComponent* myComponent, ProgramParameters& parameters, vector& outAssociation, bool debug) { while (parameters.hasNext()) { bool hyphenReplaced = false; //TSC: until someone complains, I say non-unicode dashes don't belong on the command line, EVER AString rawArg = parameters.nextString("option"); AString nextArg = rawArg.fixUnicodeHyphens(&hyphenReplaced); if (hyphenReplaced) { CaretLogWarning("replaced non-ascii hyphen/dash characters in argument '" + rawArg + "' with ascii '-'"); } if (!nextArg.isEmpty() && nextArg[0] == '-') { bool success = parseOption(nextArg, myComponent, parameters, outAssociation, debug); if (!success) { parameters.backup(); return; } } else { parameters.backup(); return; } } } AString CommandParser::doCompletion(ProgramParameters& parameters, const bool& useExtGlob) { CaretPointer myAlgParams(m_autoOper->getParameters());//could be an autopointer, but this is safer CompletionInfo ret = completionComponent(myAlgParams.getPointer(), parameters, useExtGlob); if (parameters.hasNext()) return "";//you're off the edge of the map, find the monsters yourself return ret.completionHints; } CommandParser::CompletionInfo CommandParser::completionComponent(ParameterComponent* myComponent, ProgramParameters& parameters, const bool& useExtGlob) { CompletionInfo ret;//initializes complete to false for (int i = 0; i < (int)myComponent->m_paramList.size(); ++i) { //a bit complicated... //if there is no next parameter, obviously we should do completion based on current mandatory parameter //if the next parameter starts with -, it must either be an option, numeric, or string // if the option hasn't been completed, return its completion info // *if the option is completed, but no parameters remain, need to add the mandatory argument completion to the returned completion info // 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 AString rawArg = parameters.nextString(myComponent->m_paramList[i]->m_shortName); AString nextArg = rawArg.fixUnicodeHyphens(); if (!nextArg.isEmpty() && nextArg[0] == '-') { CompletionInfo optionInfo = completionOption(nextArg, myComponent, parameters, useExtGlob); if (!optionInfo.found) { switch (myComponent->m_paramList[i]->getType()) { case OperationParametersEnum::STRING: case OperationParametersEnum::INT: case OperationParametersEnum::DOUBLE: break;//it is probably a negative number, so let it parse as one default: //NOTE: we know the command will fail to parse, now what? break;//pretend it will parse as a filename, and continue? }; continue;//assume the parameter works in the given position and move on } else {//specified option switch did match an option if (!optionInfo.complete) {//if the option's mandatory parameters weren't completed, don't add any completion from the current context, just pass the result through return optionInfo; } if (!parameters.hasNext()) {//save these hints, then let it proceed to the completion section ret.completionHints = optionInfo.completionHints; } else { --i; continue;//so skip trying to parse it as a required argument, and restart the loop on the same iteration } } } else { continue;//parameter is empty or doesn't start with -, assume it works fine } }//the above conditional does a continue unless we need to do completion now switch (myComponent->m_paramList[i]->getType()) { case OperationParametersEnum::BOOL: if (ret.completionHints != "") ret.completionHints += " "; ret.completionHints += "wordlist true\\ TRUE\\ false\\ FALSE"; break; case OperationParametersEnum::BORDER: if (ret.completionHints != "") ret.completionHints += " "; if (useExtGlob) { ret.completionHints += "fileglob *.?(wb_)border"; } else { ret.completionHints += "fileglob *.border fileglob *.wb_border"; } break; case OperationParametersEnum::CIFTI: if (ret.completionHints != "") ret.completionHints += " "; ret.completionHints += "fileglob *.*.nii";//cifti standard allows arbitrary secondary extensions, and volume files are nearly always .nii.gz anyway break;//could restrict it if someone complains (maybe a preference) case OperationParametersEnum::FOCI: if (ret.completionHints != "") ret.completionHints += " "; if (useExtGlob) { ret.completionHints += "fileglob *.?(wb_)foci"; } else { ret.completionHints += "fileglob *.foci fileglob *.wb_foci"; } break; case OperationParametersEnum::LABEL://is there a caret5 extension for this gifti file type? if (ret.completionHints != "") ret.completionHints += " "; ret.completionHints += "fileglob *.label.gii"; break; case OperationParametersEnum::METRIC://include the caret5 extension that is often the same format if (ret.completionHints != "") ret.completionHints += " "; if (useExtGlob)//also allow label files for now, see if anyone complains (maybe a preference) { ret.completionHints += "fileglob *.@(@(func|shape|label).gii|metric)"; } else { ret.completionHints += "fileglob *.func.gii fileglob *.shape.gii fileglob *.metric fileglob *.label.gii"; } break; case OperationParametersEnum::SURFACE: if (ret.completionHints != "") ret.completionHints += " "; ret.completionHints += "fileglob *.surf.gii"; break; case OperationParametersEnum::VOLUME: if (ret.completionHints != "") ret.completionHints += " "; if (useExtGlob) {//special functionality - when we have extglob, we can exclude common cifti extensions ret.completionHints += "fileglob !(*.dconn|*.dtseries|*.pconn|*.ptseries|*.dscalar|*.dlabel|*.pscalar|*.pdconn|*.dpconn|*.pconnseries|*.pconnscalar|*.plabel).nii?(.gz)"; } else {//when we don't...sorry - maybe have a preference to exclude uncompressed nifti in this condition (not great either) //if it is really a problem, we could introduce a "fileregexp" response type and do it manually with sed or grep ret.completionHints += "fileglob *.nii.gz fileglob *.nii"; } break; case OperationParametersEnum::STRING://for strings, since we use them for filenames sometimes, glob to filenames, I guess if (ret.completionHints != "") ret.completionHints += " "; ret.completionHints += "fileglob *"; break; case OperationParametersEnum::DOUBLE: case OperationParametersEnum::INT: //numeric types don't get any completion hints break; } AString optionHints = completionOptionHints(myComponent, useExtGlob); if (optionHints != "") { if (ret.completionHints != "") ret.completionHints += " "; ret.completionHints += optionHints; } return ret; } for (int i = 0; i < (int)myComponent->m_outputList.size(); ++i) {//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); AString nextArg = rawArg.fixUnicodeHyphens(); if (!nextArg.isEmpty() && nextArg[0] == '-') { CompletionInfo optionInfo = completionOption(nextArg, myComponent, parameters, useExtGlob); if (!optionInfo.found) { //NOTE: we know the command will fail to parse, now what? continue;//pretend the parameter works in the given position and move on? } else {//specified option switch did match an option if (!optionInfo.complete) {//if the option's mandatory parameters weren't completed, don't add any completion from the current context, just pass the result through return optionInfo; } if (!parameters.hasNext()) {//save these hints, then let it proceed to the completion section ret.completionHints = optionInfo.completionHints; } else { --i; continue;//so skip trying to parse it as a required argument, and restart the loop on the same iteration } } } else { continue;//parameter is empty or doesn't start with -, assume it works fine } }//the above conditional does a continue unless we need to do completion now if (ret.completionHints != "") ret.completionHints += " "; ret.completionHints += "fileglob *";//we are specifying an output file, so glob to everything so they can easily reuse names from other format files AString optionHints = completionOptionHints(myComponent, useExtGlob); if (optionHints != "") { ret.completionHints += " " + optionHints; } return ret; } CompletionInfo remainInfo = completionRemainingOptions(myComponent, parameters, useExtGlob);//returns last option parsed (so, the one to do completion on, if applicable) if (remainInfo.found) { if (!remainInfo.complete) return remainInfo; if (!parameters.hasNext()) { AString localOpts = completionOptionHints(myComponent, useExtGlob); if (remainInfo.completionHints != "") remainInfo.completionHints += " "; remainInfo.completionHints += localOpts; return remainInfo; } } ret.complete = true; if (!parameters.hasNext())//no remaining options matched, but we are out of parameters, so fill in any options that could come next { ret.completionHints = completionOptionHints(myComponent, useExtGlob); } return ret; } CommandParser::CompletionInfo CommandParser::completionOption(const AString& mySwitch, ParameterComponent* myComponent, ProgramParameters& parameters, const bool& useExtGlob) { for (uint32_t i = 0; i < myComponent->m_optionList.size(); ++i) { if (mySwitch == myComponent->m_optionList[i]->m_optionSwitch) { if (myComponent->m_optionList[i]->m_present) { //NOTE: we know the parsing will fail here, now what? //pretend it is repeatable and move on, I guess } myComponent->m_optionList[i]->m_present = true; CompletionInfo optionInfo = completionComponent(myComponent->m_optionList[i], parameters, useExtGlob); optionInfo.found = true; return optionInfo; } } for (uint32_t i = 0; i < myComponent->m_repeatableOptions.size(); ++i) { if (mySwitch == myComponent->m_repeatableOptions[i]->m_optionSwitch) { myComponent->m_repeatableOptions[i]->m_instances.push_back(new ParameterComponent(myComponent->m_repeatableOptions[i]->m_template)); CompletionInfo optionInfo = completionComponent(myComponent->m_repeatableOptions[i]->m_instances.back(), parameters, useExtGlob); optionInfo.found = true; return optionInfo; } } return CompletionInfo();//found initializes to false } AString CommandParser::completionOptionHints(ParameterComponent* myComponent, const bool& /*useExtGlob*/) { AString ret; for (uint32_t i = 0; i < myComponent->m_optionList.size(); ++i) { if (!myComponent->m_optionList[i]->m_present) {//don't suggest non-repeatable options we already have if (ret == "") { ret = "wordlist "; } else { ret += "\\ "; } ret += myComponent->m_optionList[i]->m_optionSwitch; } } for (uint32_t i = 0; i < myComponent->m_repeatableOptions.size(); ++i) {//include all repeatable options if (ret == "") { ret = "wordlist "; } else { ret += "\\ "; } ret += myComponent->m_repeatableOptions[i]->m_optionSwitch; } return ret; } CommandParser::CompletionInfo CommandParser::completionRemainingOptions(ParameterComponent* myComponent, ProgramParameters& parameters, const bool& useExtGlob) {//NOTE: completionComponent will complete the local options if needed when found is false CompletionInfo prev; while (parameters.hasNext()) { //TSC: until someone complains, I say non-unicode dashes don't belong on the command line, EVER AString rawArg = parameters.nextString("option"); AString nextArg = rawArg.fixUnicodeHyphens(); if (!nextArg.isEmpty() && nextArg[0] == '-') { CompletionInfo temp = completionOption(nextArg, myComponent, parameters, useExtGlob); if (!temp.found) { parameters.backup(); return prev;//more things to parse, could return default constructed instead } prev = temp;//it was found, so update what we found } else {//more things to parse, but they don't go here parameters.backup(); return CompletionInfo();//found initializes to false } } return prev;//no parameters remain, return whatever was found } void CommandParser::provenanceBeforeOperation(const vector& outAssociation) { vector versionInfo;//need this for on-disk outputs, because we have to set it before the command executes ApplicationInformation myInfo; myInfo.getAllInformation(versionInfo); AString versionProvenance; for (int i = 0; i < (int)versionInfo.size(); ++i) { versionProvenance += versionInfo[i] + "\n"; } for (uint32_t i = 0; i < outAssociation.size(); ++i) { AbstractParameter* myParam = outAssociation[i].m_param; switch (myParam->getType()) { case OperationParametersEnum::CIFTI: { CiftiFile* myFile = ((CiftiParameter*)myParam)->m_parameter; CiftiXML myXML; CiftiSeriesMap tempMap; tempMap.setLength(1); tempMap.setStep(1.0f); tempMap.setStart(0.0f); tempMap.setUnit(CiftiSeriesMap::SECOND); myXML.setNumberOfDimensions(2); myXML.setMap(0, tempMap); myXML.setMap(1, tempMap); GiftiMetaData* mymd = myXML.getFileMetaData(); mymd->set(PROVENANCE_NAME, m_provenance); mymd->set(PROGRAM_PROVENANCE_NAME, versionProvenance);//cifti is on-disk, so set all provenance now, because we can't later mymd->set(CWD_PROVENANCE_NAME, m_workingDir); if (m_parentProvenance != "") { mymd->set(PARENT_PROVENANCE_NAME, m_parentProvenance); } myFile->setCiftiXML(myXML, false);//tells it to use this new metadata, rather than copying metadata from the old XML (which is default so that provenance metadata persists through naive usage) break; } default: break; } } } void CommandParser::provenanceAfterOperation(const vector& outAssociation) { vector versionInfo;//now we need this information for outputs that are in memory until written ApplicationInformation myInfo; myInfo.getAllInformation(versionInfo); AString versionProvenance; for (int i = 0; i < (int)versionInfo.size(); ++i) { versionProvenance += versionInfo[i] + "\n"; } for (uint32_t i = 0; i < outAssociation.size(); ++i) { AbstractParameter* myParam = outAssociation[i].m_param; GiftiMetaData* md = NULL; switch (myParam->getType()) { case OperationParametersEnum::BORDER: { BorderFile* myFile = ((BorderParameter*)myParam)->m_parameter; md = myFile->getFileMetaData(); break; } case OperationParametersEnum::FOCI: { FociFile* myFile = ((FociParameter*)myParam)->m_parameter; md = myFile->getFileMetaData(); break; } case OperationParametersEnum::LABEL: { LabelFile* myFile = ((LabelParameter*)myParam)->m_parameter; md = myFile->getFileMetaData(); break; } case OperationParametersEnum::METRIC: { MetricFile* myFile = ((MetricParameter*)myParam)->m_parameter; md = myFile->getFileMetaData(); break; } case OperationParametersEnum::SURFACE: { SurfaceFile* myFile = ((SurfaceParameter*)myParam)->m_parameter; md = myFile->getFileMetaData(); break; } case OperationParametersEnum::VOLUME: { VolumeFile* myFile = ((VolumeParameter*)myParam)->m_parameter; md = myFile->getFileMetaData(); break; } default: break; } if (md != NULL) { md->set(PROVENANCE_NAME, m_provenance); md->set(PROGRAM_PROVENANCE_NAME, versionProvenance); md->set(CWD_PROVENANCE_NAME, m_workingDir); if (m_parentProvenance != "") { md->set(PARENT_PROVENANCE_NAME, m_parentProvenance); } } } } void CommandParser::makeOnDiskOutputs(const vector& outAssociation) { for (uint32_t i = 0; i < outAssociation.size(); ++i) { AbstractParameter* myParam = outAssociation[i].m_param; switch (myParam->getType()) { case OperationParametersEnum::CIFTI: { CiftiParameter* myCiftiParam = (CiftiParameter*)myParam; FileInformation myInfo(outAssociation[i].m_fileName); map::iterator iter = m_inputCiftiNames.find(myInfo.getCanonicalFilePath()); if (iter != m_inputCiftiNames.end()) { vector dims = iter->second->getDimensions(); int64_t totalSize = sizeof(float); for (int j = 0; j < (int)dims.size(); ++j) { totalSize *= dims[j]; } if (totalSize > ((int64_t)2) * 1024 * 1024 * 1024)//suppress the message for non-large input files, on the assumption that the output file will be the same size { CaretLogInfo("Computing output file '" + outAssociation[i].m_fileName + "' in memory due to collision with input file"); } myCiftiParam->m_parameter.grabNew(new CiftiFile()); } else { myCiftiParam->m_parameter.grabNew(new CiftiFile()); myCiftiParam->m_parameter->setWritingFile(outAssociation[i].m_fileName); } break; } default: break; } } } void CommandParser::writeOutput(const vector& outAssociation) { for (uint32_t i = 0; i < outAssociation.size(); ++i) { AbstractParameter* myParam = outAssociation[i].m_param; switch (myParam->getType()) { case OperationParametersEnum::BOOL://ignores the name you give the output for now, but what gives primitive type output and how is it used? cout << "Output Boolean \"" << myParam->m_shortName << "\" value is " << ((BooleanParameter*)myParam)->m_parameter << endl; break; case OperationParametersEnum::BORDER: { BorderFile* myFile = ((BorderParameter*)myParam)->m_parameter; myFile->writeFile(outAssociation[i].m_fileName); break; } case OperationParametersEnum::CIFTI: { 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 break; } case OperationParametersEnum::DOUBLE: cout << "Output Floating Point \"" << myParam->m_shortName << "\" value is " << ((DoubleParameter*)myParam)->m_parameter << endl; break; case OperationParametersEnum::INT: cout << "Output Integer \"" << myParam->m_shortName << "\" value is " << ((IntegerParameter*)myParam)->m_parameter << endl; break; case OperationParametersEnum::FOCI: { FociFile* myFile = ((FociParameter*)myParam)->m_parameter; myFile->writeFile(outAssociation[i].m_fileName); break; } case OperationParametersEnum::LABEL: { LabelFile* myFile = ((LabelParameter*)myParam)->m_parameter; myFile->writeFile(outAssociation[i].m_fileName); break; } case OperationParametersEnum::METRIC: { MetricFile* myFile = ((MetricParameter*)myParam)->m_parameter; myFile->writeFile(outAssociation[i].m_fileName); break; } case OperationParametersEnum::STRING: cout << "Output String \"" << myParam->m_shortName << "\" value is " << ((StringParameter*)myParam)->m_parameter << endl; break; case OperationParametersEnum::SURFACE: { SurfaceFile* myFile = ((SurfaceParameter*)myParam)->m_parameter; myFile->writeFile(outAssociation[i].m_fileName); break; } case OperationParametersEnum::VOLUME: { VolumeFile* myFile = ((VolumeParameter*)myParam)->m_parameter; myFile->writeFile(outAssociation[i].m_fileName); break; } default: CaretAssertMessage(false, "Writing of this parameter type has not been implemented in this parser");//assert instead of throw because this is a code error, not a user error throw CommandException("Internal parsing error, please let the developers know what you just tried to do");//but don't let release pass by it either } } } AString CommandParser::getHelpInformation(const AString& programName) { m_minIndent = 0; m_indentIncrement = 3; m_maxWidth = 79;//leave a space on the right edge of an 80-wide terminal so that it looks better - TODO: get the terminal width from some system call m_maxIndent = 31;//don't let indenting take up more than this int curIndent = m_minIndent; AString ret; ret = formatString(getOperationShortDescription(), curIndent, true); curIndent += m_indentIncrement; ret += getIndentString(curIndent) + programName + " " + getCommandLineSwitch() + "\n";//DO NOT format the command that people may want to copy and paste, added hyphens would be disastrous curIndent += m_indentIncrement; OperationParameters* myAlgParams = m_autoOper->getParameters(); addComponentDescriptions(ret, myAlgParams, curIndent); ret += "\n";//separate prose with a newline addHelpProse(ret, myAlgParams, curIndent); delete myAlgParams; return ret; } void CommandParser::addHelpComponent(AString& info, ParameterComponent* myComponent, int curIndent) { for (int i = 0; i < (int)myComponent->m_paramList.size(); ++i) { info += formatString("<" + myComponent->m_paramList[i]->m_shortName + ">", curIndent, true); } for (int i = 0; i < (int)myComponent->m_outputList.size(); ++i) { info += formatString("<" + myComponent->m_outputList[i]->m_shortName + ">", curIndent, true); } addHelpOptions(info, myComponent, curIndent); } void CommandParser::addHelpOptions(AString& info, ParameterComponent* myComponent, int curIndent) { for (int i = 0; i < (int)myComponent->m_optionList.size(); ++i) { info += formatString("[" + myComponent->m_optionList[i]->m_optionSwitch + "]", curIndent, true); addHelpComponent(info, myComponent->m_optionList[i], curIndent + m_indentIncrement);//indent arguments to options } for (int i = 0; i < (int)myComponent->m_repeatableOptions.size(); ++i) { info += formatString("[" + myComponent->m_repeatableOptions[i]->m_optionSwitch + "] (repeatable)", curIndent, true); addHelpComponent(info, &(myComponent->m_repeatableOptions[i]->m_template), curIndent + m_indentIncrement);//indent arguments to options } } void CommandParser::addHelpProse(AString& info, OperationParameters* myAlgParams, int curIndent) {//NOTE: does not currently format tabs well, don't use them AString* rawProse = &(myAlgParams->getHelpText());//friendlier name info += formatString(*rawProse, curIndent, false);//don't indent on added newlines in the prose } AString CommandParser::formatString(const AString& in, int curIndent, bool addIndent) {//NOTE: does not currently format tabs well, don't use them AString curIndentString = getIndentString(curIndent); bool haveAddedBreak = false; AString ret; int charMax = m_maxWidth - curIndentString.size(); int curIndex = 0; while (curIndex < in.size()) { if (addIndent) { if (haveAddedBreak) { curIndentString = getIndentString(curIndent + m_indentIncrement); charMax = m_maxWidth - curIndentString.size(); } else { curIndentString = getIndentString(curIndent); charMax = m_maxWidth - curIndentString.size(); } } int endIndex = curIndex; while (endIndex - curIndex < charMax && endIndex < in.size() && in[endIndex] != '\n') {//start by crawling until newline or at max width ++endIndex; } if (endIndex >= in.size()) { ret += curIndentString + in.mid(curIndex, endIndex - curIndex) + "\n"; } else { if (in[endIndex] == '\n') { while (endIndex < in.size() && in[endIndex] == '\n') {//crawl over any additional newlines ++endIndex; } haveAddedBreak = false; ret += curIndentString + in.mid(curIndex, endIndex - curIndex); } else { int savedEnd = endIndex; while (endIndex > curIndex && in[endIndex] != ' ') {//crawl in reverse until a space, or reaching curIndex - change this if you want hyphenation to take place more often than lines without any spaces --endIndex; } if (endIndex > curIndex) {//found a space we can break at while (endIndex > curIndex && in[endIndex] == ' ') {//don't print any of the spaces --endIndex; } if (endIndex > curIndex) { ++endIndex;//print the character before the space } haveAddedBreak = true; ret += curIndentString + in.mid(curIndex, endIndex - curIndex) + "\n"; } else {//hyphenate endIndex = savedEnd - 1; haveAddedBreak = true; ret += curIndentString + in.mid(curIndex, endIndex - curIndex) + "-\n"; } } } curIndex = endIndex; if (haveAddedBreak)//don't skip spaces after literal newlines { while (curIndex < in.size() && in[curIndex] == ' ') {//skip spaces ++curIndex; } } } return ret; } void CommandParser::addComponentDescriptions(AString& info, ParameterComponent* myComponent, int curIndent) { for (int i = 0; i < (int)myComponent->m_paramList.size(); ++i) { info += formatString("<" + myComponent->m_paramList[i]->m_shortName + "> - " + myComponent->m_paramList[i]->m_description, curIndent, true); } for (int i = 0; i < (int)myComponent->m_outputList.size(); ++i) { info += formatString("<" + myComponent->m_outputList[i]->m_shortName + "> - output - " + myComponent->m_outputList[i]->m_description, curIndent, true); } addOptionDescriptions(info, myComponent, curIndent); } void CommandParser::addOptionDescriptions(AString& info, ParameterComponent* myComponent, int curIndent) { for (int i = 0; i < (int)myComponent->m_optionList.size(); ++i) { info += "\n" + formatString("[" + myComponent->m_optionList[i]->m_optionSwitch + "] - " + myComponent->m_optionList[i]->m_description, curIndent, true); addComponentDescriptions(info, myComponent->m_optionList[i], curIndent + m_indentIncrement);//indent arguments to options } for (int i = 0; i < (int)myComponent->m_repeatableOptions.size(); ++i) { info += "\n" + formatString("[" + myComponent->m_repeatableOptions[i]->m_optionSwitch + "] - repeatable - " + myComponent->m_repeatableOptions[i]->m_description, curIndent, true); addComponentDescriptions(info, &(myComponent->m_repeatableOptions[i]->m_template), curIndent + m_indentIncrement);//indent arguments to options } } AString CommandParser::getIndentString(int desired) { AString space(" "); int num = desired; if (num > m_maxIndent) num = m_maxIndent; if (num < m_minIndent) num = m_minIndent; return space.repeated(num); } bool CommandParser::takesParameters() { return m_autoOper->takesParameters(); } connectome-workbench-1.2.3+git41-gc4c6c90/src/Commands/CommandParser.h000066400000000000000000000107571300200146000252230ustar00rootroot00000000000000#ifndef __COMMAND_PARSER_H__ #define __COMMAND_PARSER_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 "OperationParameters.h" #include "AbstractOperation.h" #include "CommandOperation.h" #include "ProgramParameters.h" #include "CommandException.h" #include "ProgramParametersException.h" #include #include namespace caret { class CommandParser : public CommandOperation, OperationParserInterface { int m_minIndent, m_maxIndent, m_indentIncrement, m_maxWidth; AString m_provenance, m_parentProvenance, m_workingDir; bool m_doProvenance; const static AString PROVENANCE_NAME, PARENT_PROVENANCE_NAME, PROGRAM_PROVENANCE_NAME, CWD_PROVENANCE_NAME;//TODO: put this elsewhere? std::map m_inputCiftiNames; struct OutputAssoc {//how the output is stored is up to the parser, in the GUI it should load into memory without writing to disk AString m_fileName; AbstractParameter* m_param; }; struct CompletionInfo { bool complete, found;//found is only used for options AString completionHints;//only valid when hasNext returns false during the component CompletionInfo() { complete = false; found = false; } }; void parseComponent(ParameterComponent* myComponent, ProgramParameters& parameters, std::vector& outAssociation, bool debug = false); bool parseOption(const AString& mySwitch, ParameterComponent* myComponent, ProgramParameters& parameters, std::vector& outAssociation, bool debug); 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 writeOutput(const std::vector& outAssociation); AString getIndentString(int desired); void addHelpComponent(AString& info, ParameterComponent* myComponent, int curIndent); void addHelpOptions(AString& info, ParameterComponent* myAlgParams, int curIndent); void addHelpProse(AString& info, OperationParameters* myAlgParams, int curIndent); void addComponentDescriptions(AString& info, ParameterComponent* myComponent, int curIndent); void addOptionDescriptions(AString& info, ParameterComponent* myComponent, int curIndent); AString formatString(const AString& in, int curIndent, bool addIndent); CompletionInfo completionComponent(ParameterComponent* myComponent, ProgramParameters& parameters, const bool& useExtGlob); CompletionInfo completionOption(const AString& mySwitch, ParameterComponent* myComponent, ProgramParameters& parameters, const bool& useExtGlob); AString completionOptionHints(ParameterComponent* myComponent, const bool& useExtGlob); CompletionInfo completionRemainingOptions(ParameterComponent* myComponent, ProgramParameters& parameters, const bool& useExtGlob); public: CommandParser(AutoOperationInterface* myAutoOper); void disableProvenance(); void executeOperation(ProgramParameters& parameters); void showParsedOperation(ProgramParameters& parameters); AString doCompletion(ProgramParameters& parameters, const bool& useExtGlob); AString getHelpInformation(const AString& programName); bool takesParameters(); }; }; #endif //__COMMAND_PARSER_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Commands/CommandUnitTest.cxx000066400000000000000000000034021300200146000261060ustar00rootroot00000000000000/*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 "CaretAssertion.h" #include "CommandUnitTest.h" #include "Plane.h" #include "SystemUtilities.h" using namespace caret; /** * Constructor. */ CommandUnitTest::CommandUnitTest() : CommandOperation("-unit-test", "UNIT TESTING") { } /** * Destructor. */ CommandUnitTest::~CommandUnitTest() { } /** * Execute the operation. * * @param parameters * Parameters for the operation. * @throws CommandException * If the command failed. * @throws ProgramParametersException * If there is an error in the parameters. */ void CommandUnitTest::executeOperation(ProgramParameters& /*parameters*/) { std::ostream* stream = &std::cout; CaretAssertion::unitTest(*stream, true); *stream << std::endl; Plane::unitTest(*stream, true); *stream << std::endl; SystemUtilities::unitTest(*stream, true); *stream << std::endl; } connectome-workbench-1.2.3+git41-gc4c6c90/src/Commands/CommandUnitTest.h000066400000000000000000000030671300200146000255420ustar00rootroot00000000000000#ifndef __COMMAND_UNIT_TEST_H__ #define __COMMAND_UNIT_TEST_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 "CommandOperation.h" namespace caret { /// Command operation for unit testing. class CommandUnitTest : public CommandOperation { public: CommandUnitTest(); virtual ~CommandUnitTest(); virtual void executeOperation(ProgramParameters& parameters); AString getHelpInformation(const AString& /*programName*/) { return " "; }; virtual bool takesParameters() { return false; } private: CommandUnitTest(const CommandUnitTest&); CommandUnitTest& operator=(const CommandUnitTest&); }; } // namespace #endif // __COMMAND_UNIT_TEST_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Commands/README.txt000066400000000000000000000021521300200146000240030ustar00rootroot00000000000000Do not write new commands in this directory, that method is obsolete, new commands should be made as either Operations or Algorithms, as follows: The defining difference between Operations and Algorithms is whether they contain a way for other code to use them easily. Algorithms usually provide a "constructor" that takes the inputs and outputs, but really executes the thing the algorithm does (the reasoning for this is that it is the shortest method to access without an instance, so we are abusing the class concept for the purpose of associating several static methods). On the other hand, Operations only operate from arguments given to a parser of some kind. As such, Operations should be used only for things that would not be useful to other code (printing information to stdout or a text file, providing command line access to something that is already trivial to do in code). Once you know whether the command should be an Algorithm or Operation, open either Algorithms/AlgorithmTemplate.h.txt or Operations/OperationTemplate.h.txt, and follow the instructions in the block comment just below the license notice. connectome-workbench-1.2.3+git41-gc4c6c90/src/Common/000077500000000000000000000000001300200146000217745ustar00rootroot00000000000000connectome-workbench-1.2.3+git41-gc4c6c90/src/Common/AString.cxx000066400000000000000000000562421300200146000241000ustar00rootroot00000000000000/*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 "AString.h" #include "CaretLogger.h" #include using namespace caret; std::ostream& operator << (std::ostream &lhs, const AString &rhs) { return lhs << rhs.toStdString(); } /** * Convert a vector of values into a string. * @param v * The vector of values. * @param separator * Inserted between each pair of values. * @return * String containing the vector's values separated * by the separator. */ AString AString::fromNumbers(const std::vector& v, const AString& separator) { AString s; for (uint64_t i = 0; i < v.size(); i++) { if (i > 0) { s += separator; } s += AString::number(v[i]); } return s; } /** * Convert a vector of values into a string. * @param v * The vector of values. * @param separator * Inserted between each pair of values. * @return * String containing the vector's values separated * by the separator. */ AString AString::fromNumbers(const std::vector& v, const AString& separator) { AString s; for (uint64_t i = 0; i < v.size(); i++) { if (i > 0) { s += separator; } s += AString::number(v[i]); } return s; } /** * Convert a vector of values into a string. * @param v * The vector of values. * @param separator * Inserted between each pair of values. * @return * String containing the vector's values separated * by the separator. */ AString AString::fromNumbers(const std::vector& v, const AString& separator) { AString s; for (uint64_t i = 0; i < v.size(); i++) { if (i > 0) { s += separator; } s += AString::number(v[i]); } return s; } /** * Convert a vector of values into a string. * @param v * The vector of values. * @param separator * Inserted between each pair of values. * @return * String containing the vector's values separated * by the separator. */ AString AString::fromNumbers(const std::vector& v, const AString& separator) { AString s; for (uint64_t i = 0; i < v.size(); i++) { if (i > 0) { s += separator; } s += AString::number(v[i]); } return s; } /** * Convert a vector of values into a string. * @param v * The vector of values. * @param separator * Inserted between each pair of values. * @return * String containing the vector's values separated * by the separator. */ AString AString::fromNumbers(const std::vector& v, const AString& separator) { AString s; for (uint64_t i = 0; i < v.size(); i++) { if (i > 0) { s += separator; } s += AString::number(v[i]); } return s; } /** * Convert a vector of values into a string. * @param v * The vector of values. * @param separator * Inserted between each pair of values. * @return * String containing the vector's values separated * by the separator. */ AString AString::fromNumbers(const std::vector& v, const AString& separator) { AString s; for (uint64_t i = 0; i < v.size(); i++) { if (i > 0) { s += separator; } s += AString::number(v[i]); } return s; } /** * Convert a vector of values into a string. * @param v * The vector of values. * @param separator * Inserted between each pair of values. * @return * String containing the vector's values separated * by the separator. */ AString AString::fromNumbers(const std::vector& v, const AString& separator) { AString s; for (uint64_t i = 0; i < v.size(); i++) { if (i > 0) { s += separator; } s += AString::number(v[i]); } return s; } /** * Convert a vector of values into a string. * @param v * The vector of values. * @param separator * Inserted between each pair of values. * @return * String containing the vector's values separated * by the separator. */ AString AString::fromNumbers(const std::vector& v, const AString& separator) { AString s; for (uint64_t i = 0; i < v.size(); i++) { if (i > 0) { s += separator; } s += AString::number(v[i]); } return s; } /** * Convert an array of values into a string. * @param array * The array of values. * @param numberOfElements * Number of elements in the array. * @param separator * Inserted between each pair of values. * @return * String containing the array values separated * by the separator. */ AString AString::fromNumbers(const float* array, const int64_t numberOfElements, const AString& separator) { AString s; for (int64_t i = 0; i < numberOfElements; i++) { if (i > 0) { s += separator; } s += AString::number(array[i]); } return s; } /** * Convert an array of values into a string. * @param array * The array of values. * @param numberOfElements * Number of elements in the array. * @param separator * Inserted between each pair of values. * @return * String containing the array values separated * by the separator. */ AString AString::fromNumbers(const int8_t* array, const int64_t numberOfElements, const AString& separator) { AString s; for (int64_t i = 0; i < numberOfElements; i++) { if (i > 0) { s += separator; } s += AString::number(array[i]); } return s; } /** * Convert an array of values into a string. * @param array * The array of values. * @param numberOfElements * Number of elements in the array. * @param separator * Inserted between each pair of values. * @return * String containing the array values separated * by the separator. */ AString AString::fromNumbers(const uint8_t* array, const int64_t numberOfElements, const AString& separator) { AString s; for (int64_t i = 0; i < numberOfElements; i++) { if (i > 0) { s += separator; } s += AString::number(array[i]); } return s; } /** * Convert an array of values into a string. * @param array * The array of values. * @param numberOfElements * Number of elements in the array. * @param separator * Inserted between each pair of values. * @return * String containing the array values separated * by the separator. */ AString AString::fromNumbers(const int32_t* array, const int64_t numberOfElements, const AString& separator) { AString s; for (int64_t i = 0; i < numberOfElements; i++) { if (i > 0) { s += separator; } s += AString::number(array[i]); } return s; } /** * Convert an array of values into a string. * @param array * The array of values. * @param numberOfElements * Number of elements in the array. * @param separator * Inserted between each pair of values. * @return * String containing the array values separated * by the separator. */ AString AString::fromNumbers(const int64_t* array, const int64_t numberOfElements, const AString& separator) { AString s; for (int64_t i = 0; i < numberOfElements; i++) { if (i > 0) { s += separator; } s += AString::number(array[i]); } return s; } /** * Convert an array of values into a string. * @param array * The array of values. * @param numberOfElements * Number of elements in the array. * @param separator * Inserted between each pair of values. * @return * String containing the array values separated * by the separator. */ AString AString::fromNumbers(const double* array, const int64_t numberOfElements, const AString& separator) { AString s; for (int64_t i = 0; i < numberOfElements; i++) { if (i > 0) { s += separator; } s += AString::number(array[i]); } return s; } /** * Convert the contents of given string to floats. Each * piece of text is converted to float. If a piece of * text does not convert to a float, it is ignored. This * should allow separation with characters other than * whitespace. * * @param s * String convert to floats. * @param numbersOut * Vector that will contain the given of this string * as numbers. */ void AString::toNumbers(const AString& s, std::vector& numbersOut) { AString copy = s; QTextStream stream(©); float floatValue; while (stream.atEnd() == false) { /* * Try to read a float value from the current position */ stream >> floatValue; /* * If the text stream could not create a float from * the current text position, the corrupt data flag * will be set. */ if (stream.status() == QTextStream::ReadCorruptData) { /* * Reset the status of the string (to OK) and * then read one character to remove the character * from the stream since it was not the start * of a number. */ stream.resetStatus(); QChar oneChar; stream >> oneChar; } else { numbersOut.push_back(floatValue); } } // AString copy = s; // QTextStream stream(©); // // AString numberString; // bool valid = false; // while (stream.atEnd() == false) { // stream >> numberString; // const float floatValue = numberString.toFloat(&valid); // if (valid) { // numbersOut.push_back(floatValue); // } // } } /** * Convert the contents of given string to ints. Each * piece of text is converted to int. If a piece of * text does not convert to an int, it is ignored. This * should allow separation with characters other than * whitespace. * * @param s * String convert to ints. * @param numbersOut * Vector that will contain the given of this string * as numbers. */ void AString::toNumbers(const AString& s, std::vector& numbersOut) { AString copy = s; QTextStream stream(©); AString numberString; bool valid = false; while (stream.atEnd() == false) { stream >> numberString; const int32_t intValue = numberString.toInt(&valid); if (valid) { numbersOut.push_back(intValue); } } } /** * Convert the string to a boolean value. * These case insensitive values are considered true: * true, t, 1, 1.0. All others are considered false. * * @return * Boolean value interpreted from contents of * this string. */ bool AString::toBool() const { const AString s = this->toLower(); if ((s == "true") || (s == "t") || (s == "1") || (s == "1.0")) { return true; } return false; } /** * Convert the boolean value to a string. * @param b * The boolean value. * @return "true" if true, else "false". */ AString AString::fromBool(const bool b) { if (b) { return "true"; } return "false"; } /** * Convert any URLs in this string to * HTML hyperlinks. * "http://www.wustl.edu" becomes "http://wwww.wustl.edu" * @param sin * String that may contain URLs. * @return * Input string with any URLs replace with hyperlinks. */ AString AString::convertURLsToHyperlinks() const { std::vector url; std::vector urlStart; const AString& sin = *this; if (sin.indexOf("http://") == -1) { return sin; } else { // // Create a modifiable copy // AString s(sin); // // loop since there may be more than one URL // bool done = false; int startPos = 0; while(done == false) { // // Find the beginning of the URL // const int httpStart = s.indexOf("http://", startPos); // // Was the start of a URL found // if (httpStart == -1) { done = true; } else { // // Find the end of the URL // int httpEnd = s.indexOfAnyChar(" \t\n\r", httpStart + 1); // // May not find end since end of string // int httpLength; if (httpEnd == -1) { httpLength = s.length() - httpStart; } else { httpLength = httpEnd - httpStart; } // // Get the http URL // const AString httpString = s.mid(httpStart, httpLength); url.push_back(httpString); urlStart.push_back(httpStart); // // Prepare for next search // startPos = httpStart; //if (startPos > 0) { startPos = startPos + 1; //} } } if (url.empty() == false) { const int startNum = static_cast(url.size()) - 1; for (int i = startNum; i >= 0; i--) { const int len = url[i].length(); // // Create the trailing part of the hyperlink and insert it // AString trailingHyperLink("\">"); trailingHyperLink.append(url[i]); trailingHyperLink.append(""); s.insert(urlStart[i] + len, trailingHyperLink); // // Insert the beginning of the hyperlink // s.insert(urlStart[i], " " and "" * * Replace some characters with their HTML escaped characters */ AString AString::convertToHtmlPage() const { return convertToHtmlPageWithCssFontHeight(-1); } /** * Convert the text string to an HTML page using the given font size * by enclosing text between: * "" and "" * * Note: This uses the font tag's size attribute which is not supported * by HTML5. * * Also replaces some characters with their HTML escaped characters * * @param fontSize * Size of the font. */ AString AString::convertToHtmlPageWithFontSize(const int fontSize) const { /* * If already HTML (assumes "html" is the first six characters), * no need to convert. */ if (this->startsWith("", Qt::CaseInsensitive)) { return *this; } AString htmlString(""); htmlString.append(""); htmlString.append(this->replaceHtmlSpecialCharactersWithEscapeCharacters()); htmlString.append(""); htmlString.append(""); return htmlString; } /** * Convert the text string to an HTML page using the given font height * by enclosing text between: * "

" * * Note: HTML produced by this method and displayed in a QTextBrowser had * some problems with cutting and pasting on some Macs not working. * * Also replaces some characters with their HTML escaped characters * * @param fontHeight * Height of the font (if negative no font height is applied). */ AString AString::convertToHtmlPageWithCssFontHeight(const int fontHeight) const { /* * If already HTML (assumes "html" is the first six characters), * no need to convert. */ if (this->startsWith("", Qt::CaseInsensitive)) { return *this; } AString htmlString(""); if (fontHeight > 0) { htmlString.append("

"); } htmlString.append(this->replaceHtmlSpecialCharactersWithEscapeCharacters()); if (fontHeight > 0) { htmlString.append("

"); } htmlString.append(""); return htmlString; } /** * @return A copy of this string with any HTML special characters replaced * by their escape sequences. */ AString AString::replaceHtmlSpecialCharactersWithEscapeCharacters() const { AString htmlString; const int64_t length = this->count(); for (int64_t i = 0; i < length; i++) { const QChar ch = this->at(i); switch (ch.toAscii()) { case '&': htmlString.append("&"); break; case '<': htmlString.append("<"); break; case '>': htmlString.append(">"); break; case '\'': htmlString.append("'"); break; case '\"': htmlString.append("""); break; case ' ': htmlString.append(" "); break; case '\n': htmlString.append("
"); break; default: htmlString.append(ch); break; } } return htmlString; } AString AString::fixUnicodeHyphens(bool* hyphenReplaced, bool* hadOtherNonAscii, const bool& quiet) const { AString ret = this->normalized(QString::NormalizationForm_C);//first, normalize multi-char forms to their combined equivalents, etc, tons of nasties for (int i = 0; i < ret.length(); ++i) { ushort charCode = ret[i].unicode(); if (charCode > 127) { if ((charCode >= 8208 && charCode <= 8213) || //hyphens, dashes, bar charCode == 8722 || //minus charCode == 11834 || charCode == 11835 || //two and three em dash charCode == 65123 || //small hyphen-minus charCode == 65293)// { if (!quiet) CaretLogFine("character code " + AString::number(charCode) + " replaced with ascii dash"); ret[i] = '-'; if (hyphenReplaced != NULL) *hyphenReplaced = true; } else {//other stuff if (!quiet) CaretLogInfo("non-ascii character code " + AString::number(charCode) + " not recognized as dash/hyphen/minus"); if (hadOtherNonAscii != NULL) *hadOtherNonAscii = true; } } } return ret; } /** * Returns the index position of any character in * 'str' in this string. * @param str Characters that are searched * for in this string. * @param from String position (default is first character). */ int32_t AString::indexOfAnyChar(const AString& str, int from) const { const AString& s = *this; const int len = s.length(); if (from < 0)//use the same "from" logic as Qt { from += len;//-2 starts at second to last character if (from < 0) from = 0;//can't start before the beginning } const int len2 = str.length(); for (int i = from; i < len; i++) { for (int j = 0; j < len2; j++) { if (s[i] == str[j]) { return i; } } } return -1; } /** * @return The index position of the first character that is NOT * the character 'ch'. Returns -1 if all characters in the * string are 'ch'. */ int32_t AString::indexNotOf(const QChar& ch) const { const int32_t len = length(); for (int32_t i = 0; i < len; i++) { if (at(i) != ch) { return i; } } return -1; } /** * Return a 'C' char array containing the value * of the string. This method is necessary since * on an instance of Ubuntu Linux, an invalid ASCII * character is found on the end of data returned * by toLocal8Bit().constData();. This method * will replace any non-ascii characters with "_". * All trailing non ASCII characters will be removed. * * @return * Char array of ASCII characters with a string * terminator '\0' at the end. Caller MUST * free memory by running delete[] on the * returned array. */ char* AString::toCharArray() const { /* * Convert to a byte array */ QByteArray byteArray = this->toLocal8Bit(); const int32_t numBytes = byteArray.length(); if (numBytes > 0) { char* charOut = new char[numBytes + 1];//are there any byteArrays that don't already come with a line terminator? int32_t lastAsciiChar = -1; for (int32_t i = 0; i < numBytes; i++) { char c = byteArray.at(i); if ((c == 10) || ((c >= 32) && (c <= 126))) { charOut[i] = c; lastAsciiChar = i; } else if((c == 0) && i == (numBytes-1)) { charOut[i] = c; } else { charOut[i] = '_'; } } charOut[lastAsciiChar + 1] = '\0'; return charOut; } char* s = new char[1]; s[0] = 0; return s; } /** * If this string is not empty append a newline. * Next, append the given string. */ void AString::appendWithNewLine(const AString& str) { if (isEmpty() == false) { append("\n"); } append(str); } /** * Count the number of matching characters with the other string * starting at the end of the strings. * * Example: this="someText" rhs="moreText" => result=4 * @rhs * The other string. * @return * Number of character that match from the end of this and rhs. */ int64_t AString::countMatchingCharactersFromEnd(const AString& rhs) const { int64_t matchCount = 0; const int64_t myLength = length(); const int64_t rhsLength = rhs.length(); const int64_t minLength = std::min(myLength, rhsLength); for (int32_t offset = 1; offset <= minLength; offset++) { if (at(myLength - offset) == rhs.at(rhsLength - offset)) { matchCount++; } else { break; } } return matchCount; } connectome-workbench-1.2.3+git41-gc4c6c90/src/Common/AString.h000066400000000000000000000144431300200146000235220ustar00rootroot00000000000000/*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*/ #ifndef ASTRING_H #define ASTRING_H #include #include #include namespace caret { class AString : public QString { public: AString() : QString() {} AString(const QChar *unicode, int size) : QString(unicode, size) {} explicit AString(const QChar *unicode) : QString(unicode) {} // Qt5: merge with the above AString(QChar c) : QString(c) {} AString(int size, QChar c) : QString(size, c) {} AString(const QLatin1String &latin1) : QString(latin1) {} AString(const AString &string) : QString(string) {} AString(const QString &string) : QString(string) {} AString(const char *ch) : QString(ch){} AString(const QByteArray &a) : QString(a) {} AString(const Null &t) : QString(t) {} AString &operator=(const Null &t) { QString::operator=(t); return *this; } //AString(int size, Qt::Initialization) : QString(size,Qt::Initialization) {} //using QString::operator=; AString &operator=(QChar c) { QString::operator=(c); return *this;} AString &operator=(const QString &string) { QString::operator=(string); return *this;} AString &operator=(const QLatin1String &latin1) { QString::operator=(latin1); return *this;} AString &operator=(const char *ch) { QString::operator=(ch); return *this;} AString &operator=(const QByteArray &a) { QString::operator=(a); return *this;} AString &operator=(char c) { QString::operator=(c); return *this;} //std::string compatibility operator std::string () {return this->toStdString(); } //char * compatibility operator const char* () {return this->toAscii(); } //double compatiblity operator double () {return this->toDouble(); } //float compatiblity operator float () { return this->toFloat(); } //int compatiblity operator int () { return this->toInt(); } //long compatiblity operator long () { return this->toLong(); } //long long compatiblity operator long long () { return this->toLongLong(); } //unsigned int compatiblity operator unsigned int () { return this->toUInt(); } //unsigned long compatiblity operator unsigned long () { return this->toULong(); } //unsigned long long compatiblity operator unsigned long long () { return this->toULongLong(); } /// convert to a const char* (the operator() does not work in C++ library I/O functions) //const char* c_str() const { return qPrintable(*this); } char* toCharArray() const; AString convertURLsToHyperlinks() const; AString convertToHtmlPage() const; AString convertToHtmlPageWithFontSize(const int fontSize) const; AString convertToHtmlPageWithCssFontHeight(const int fontHeight) const; int32_t indexOfAnyChar(const AString& str, int from = 0) const; int32_t indexNotOf(const QChar& ch) const; void appendWithNewLine(const AString& str); int64_t countMatchingCharactersFromEnd(const AString& rhs) const; static void toNumbers(const AString& s, std::vector& numbersOut); static void toNumbers(const AString& s, std::vector& numbersOut); bool toBool() const; //I may move these outside the class since they don't require access to the class's internals static AString fromNumbers(const std::vector& v, const AString& separator); static AString fromNumbers(const std::vector& v, const AString& separator); static AString fromNumbers(const std::vector& v, const AString& separator); static AString fromNumbers(const std::vector& v, const AString& separator); static AString fromNumbers(const std::vector& v, const AString& separator); static AString fromNumbers(const std::vector& v, const AString& separator); static AString fromNumbers(const std::vector& v, const AString& separator); static AString fromNumbers(const std::vector& v, const AString& separator); static AString fromNumbers(const float* array, const int64_t numberOfElements, const AString& separator); static AString fromNumbers(const uint8_t* array,const int64_t numberOfElements, const AString& separator); static AString fromNumbers(const int8_t* array,const int64_t numberOfElements, const AString& separator); static AString fromNumbers(const int32_t* array,const int64_t numberOfElements, const AString& separator); static AString fromNumbers(const int64_t* array,const int64_t numberOfElements, const AString& separator); static AString fromNumbers(const double* array, const int64_t numberOfElements, const AString& separator); static AString fromBool(const bool b); AString replaceHtmlSpecialCharactersWithEscapeCharacters() const; AString fixUnicodeHyphens(bool* hyphenReplaced = NULL, bool* hadOtherNonAscii = NULL, const bool& quiet = false) const; }; } #include #include std::ostream& operator << (std::ostream &lhs, const caret::AString &rhs); #endif // ASTRING_H connectome-workbench-1.2.3+git41-gc4c6c90/src/Common/AStringNaturalComparison.cxx000066400000000000000000000153621300200146000274600ustar00rootroot00000000000000 /*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 __A_STRING_NATURAL_COMPARISON_DECLARE__ #include "AStringNaturalComparison.h" #undef __A_STRING_NATURAL_COMPARISON_DECLARE__ #include "CaretAssert.h" using namespace caret; /** * \class caret::AStringNaturalComparison * \brief Class for performing a "natural comparison" of strings. * \ingroup Common * * This class is designed for use as a function object with the * Standard Template Library. However, it also contains a static * method for naturally comparing" two strings. * * Normal string comparison just compares the ASCII values of characters. * However, when the string contains numeric sequences, the strings are * sorted as expected. This class will perform string comparison where * any sequence of numbers is treated as a 'single character'. * * When the two 'characters' being compared are both non-numeric, they are * compared by their ASCII codes. When the two 'characters' being compared * are both numbers, a numeric comparison is performed. When one 'character' * is numeric and the other 'character' is non-numeric, the number is * considered "less than". * * Sequence produced by normal string comparison (note the position of 32abc): * 1xyz, 32abc, 4ab * * Sequence produced by natural string comparison (note the position of 32abc): * 1xyz, 4ab, 32abc */ /** * Constructor. */ AStringNaturalComparison::AStringNaturalComparison() { } /** * Destructor. */ AStringNaturalComparison::~AStringNaturalComparison() { } /** * Function object so that this class can be used as for * comparison in Standard Template Library containers. * * Performs a NATURAL COMPARISON where a contiguous sequence * of digits is treated as a single character so that text * string with numbers in them are properly sorted. * * @param s1 * First string for comparison. * @param s2 * Second string for comparison. * @return * Negative value if (s1 < s2), Positive if (s1 > s2), and * Zero if (s1 == s2). */ bool AStringNaturalComparison::operator() (const AString& s1, const AString& s2) const { const int32_t result = AStringNaturalComparison::compare(s1, s2); // std::cout << "Compare (" // << qPrintable(s1) // << ", " // << qPrintable(s2) // << "): " // << AString::fromBool(result) // << std::endl; if (result < 0) { return true; } return false; } /** * Static method for natural comparison of two strings. * * Performs a NATURAL COMPARISON where a contiguous sequence * of digits is treated as a single character so that text * string with numbers in them are properly sorted. * * @param string1 * First string for comparison. * @param string2 * Second string for comparison. * @return * Negative value if (string1 < string2), Positive if (string1 > string2), and * Zero if (string1 == string2). */ int32_t AStringNaturalComparison::compare(const AString& string1, const AString& string2) { const StringParser s1(string1); const StringParser s2(string2); bool s1IsNumber = false; bool s2IsNumber = false; /* * Loop through the 'characters' until corresponding * 'characters' do not match. * * Note that a consecutive sequence of digits is * considered a single 'character'. */ while (s1.hasMore() && s2.hasMore()) { const int64_t ch1 = s1.nextChar(s1IsNumber); const int64_t ch2 = s2.nextChar(s2IsNumber); CaretAssert(ch1 >= 0); CaretAssert(ch2 >= 0); if (s1IsNumber && s2IsNumber) { /* * Both 'character's are numbers */ if (ch1 < ch2) { return -1; } else if (ch1 > ch2) { return 1; } } else if (s1IsNumber) { return -1; } else if (s2IsNumber) { return 1; } else { /* * Both 'characters' are NOT numbers */ if (ch1 < ch2) { return -1; } else if (ch1 > ch2) { return 1; } } } /* * The shorter string is considered "less than" */ if (s1.hasMore()) { return 1; } else if (s2.hasMore()) { return -1; } /* * Strings must be identical. */ return 0; } /* ===================================================================== */ /** * \class caret::AStringNaturalComparison::StringParser * \brief Class for "String parsing" that treats any consecutive sequence * of numbers as a single character. * \ingroup Common */ /** * Constructor. * * @param s * String that will be parsed. */ AStringNaturalComparison::StringParser::StringParser(const AString& s) : m_s(s), m_pos(0), m_len(s.length()) { } /** * Returns the "next character" in the string. Any consecutive sequence of * digits is considered a single character. * * @param isNumberOut * If the value returned is a number, this parameter will be true, * else false. * @return * The unicode value for the next character or numeric value os a * sequence of digits. */ int64_t AStringNaturalComparison::StringParser::nextChar(bool& isNumberOut) const { isNumberOut = false; if (m_pos >= m_len) { return -1; } const QChar ch = m_s[m_pos]; ++m_pos; if (ch.isDigit()) { int64_t numericValue = ch.digitValue(); while (m_pos < m_len) { const QChar nextChar = m_s[m_pos]; if (nextChar.isDigit()) { ++m_pos; numericValue = (numericValue * 10) + nextChar.digitValue(); } else { break; } } isNumberOut = true; return numericValue; } else { return ch.unicode(); } return -1; } connectome-workbench-1.2.3+git41-gc4c6c90/src/Common/AStringNaturalComparison.h000066400000000000000000000042671300200146000271070ustar00rootroot00000000000000#ifndef __A_STRING_NATURAL_COMPARISON_H__ #define __A_STRING_NATURAL_COMPARISON_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 "AString.h" namespace caret { class AStringNaturalComparison { public: AStringNaturalComparison(); AStringNaturalComparison(const AStringNaturalComparison&) { } virtual ~AStringNaturalComparison(); bool operator() (const AString& s1, const AString& s2) const; static int32_t compare(const AString& string1, const AString& string2); // ADD_NEW_METHODS_HERE private: AStringNaturalComparison& operator=(const AStringNaturalComparison&); // ADD_NEW_MEMBERS_HERE class StringParser { public: StringParser(const AString& s); int64_t nextChar(bool& isNumberOut) const; inline bool hasMore() const { return (m_pos < m_len); } private: const AString& m_s; mutable int32_t m_pos; int32_t m_len; }; }; #ifdef __A_STRING_NATURAL_COMPARISON_DECLARE__ // #endif // __A_STRING_NATURAL_COMPARISON_DECLARE__ } // namespace #endif //__A_STRING_NATURAL_COMPARISON_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Common/ApplicationInformation.cxx.in000066400000000000000000000124261300200146000276030ustar00rootroot00000000000000 /*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 __APPLICATION_INFORMATION_DECLARE__ #include "ApplicationInformation.h" #undef __APPLICATION_INFORMATION_DECLARE__ #include "CaretOMP.h" #include "FileInformation.h" using namespace caret; /** * \class caret::ApplicationInformation * \brief Provides application information. * * Provides application information (name, version, etc). */ /** * Constructor. */ ApplicationInformation::ApplicationInformation() : CaretObject() { this->name = "Connectome Workbench"; this->version = "1.2.3"; this->commit = "Commit: @COMMIT@"; this->commitDate = "Commit Date: @COMMIT_DATE@"; #ifdef NDEBUG this->compiledWithDebugOn = "Compiled Debug: NO"; #else this->compiledWithDebugOn = "Compiled Debug: YES"; #endif // NDEBUG #ifdef CARET_OMP this->compiledWithOpenMP = "Compiled with OpenMP: YES"; #else this->compiledWithOpenMP = "Compiled with OpenMP: NO"; #endif this->operatingSystemName = "Operating System: Unknown"; #ifdef CARET_OS_LINUX this->operatingSystemName = "Operating System: Linux"; #endif // CARET_OS_MACOSX #ifdef CARET_OS_MACOSX this->operatingSystemName = "Operating System: Apple OSX"; #endif // CARET_OS_MACOSX #ifdef CARET_OS_WINDOWS this->operatingSystemName = "Operating System: Windows"; #endif // CARET_OS_MACOSX } /** * Destructor. */ ApplicationInformation::~ApplicationInformation() { } /** * @return Name of the application. */ AString ApplicationInformation::getName() const { return this->name; } /** * @return Version of application. */ AString ApplicationInformation::getVersion() const { return this->version; } /** * @return Commit info of application. */ AString ApplicationInformation::getCommit() const { return this->commit; } /** * @return Text indicating if Workbench was compiled with Debug on. */ AString ApplicationInformation::getCompiledWithDebugStatus() const { return this->compiledWithDebugOn; } /** * Get all information. * @param informationValues * Output information. */ void ApplicationInformation::getAllInformation(std::vector& informationValues) const { informationValues.clear(); informationValues.push_back(this->name); informationValues.push_back("Type: " + ApplicationTypeEnum::toGuiName(s_applicationType)); informationValues.push_back("Version: " + this->version); informationValues.push_back("Qt Compiled Version: " + QString(QT_VERSION_STR)); informationValues.push_back("Qt Runtime Version: " + QString(qVersion())); informationValues.push_back(commit); informationValues.push_back(commitDate); informationValues.push_back(compiledWithOpenMP); #if defined COMPILER_NAME FileInformation fileInfo(COMPILER_NAME); informationValues.push_back(QString("Compiler: ") + fileInfo.getFileNameFollowedByPathNameForGUI()); #endif #if defined COMPILER_VERSION informationValues.push_back(QString("Compiler Version: ") + QString(COMPILER_VERSION)); #endif informationValues.push_back(this->compiledWithDebugOn); informationValues.push_back(this->operatingSystemName); } /** * Get all of the application information in a string with each line * separated by the given 'separator'. * * @param separator * The separator is placed between each line of information. A newline * ("\n") is commonly used for the separator. */ AString ApplicationInformation::getAllInformationInString(const AString& separator) const { std::vector informationValues; getAllInformation(informationValues); AString infoOut; for (std::vector::iterator iter = informationValues.begin(); iter != informationValues.end(); iter++) { infoOut += *iter; infoOut += separator; } return infoOut; } AString ApplicationInformation::getSummaryInformationInString(const AString& separator) const { AString infoOut = "Version: " + version + separator; infoOut += commitDate + separator; infoOut += operatingSystemName + separator; return infoOut; } /** * @return The type of application. */ ApplicationTypeEnum::Enum ApplicationInformation::getApplicationType() { return s_applicationType; } /** * Set the application type. * * @param applicationType * New value for application type. */ void ApplicationInformation::setApplicationType(const ApplicationTypeEnum::Enum applicationType) { s_applicationType = applicationType; } connectome-workbench-1.2.3+git41-gc4c6c90/src/Common/ApplicationInformation.h000066400000000000000000000047751300200146000266330ustar00rootroot00000000000000#ifndef __APPLICATION_INFORMATION__H_ #define __APPLICATION_INFORMATION__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 "ApplicationTypeEnum.h" #include "CaretObject.h" namespace caret { class ApplicationInformation : public CaretObject { public: ApplicationInformation(); virtual ~ApplicationInformation(); AString getName() const; AString getVersion() const; AString getCommit() const; void getAllInformation(std::vector& informationValues) const; AString getAllInformationInString(const AString& separator) const; AString getSummaryInformationInString(const AString& separator) const; AString getCompiledWithDebugStatus() const; static ApplicationTypeEnum::Enum getApplicationType(); static void setApplicationType(const ApplicationTypeEnum::Enum applicationType); private: ApplicationInformation(const ApplicationInformation&); ApplicationInformation& operator=(const ApplicationInformation&); AString name; AString version; AString commit; AString commitDate; AString compiledWithDebugOn; AString operatingSystemName; AString compiledWithOpenMP; static ApplicationTypeEnum::Enum s_applicationType; }; #ifdef __APPLICATION_INFORMATION_DECLARE__ ApplicationTypeEnum::Enum ApplicationInformation::s_applicationType = ApplicationTypeEnum::APPLICATION_TYPE_INVALID; #endif // __APPLICATION_INFORMATION_DECLARE__ } // namespace #endif //__APPLICATION_INFORMATION__H_ connectome-workbench-1.2.3+git41-gc4c6c90/src/Common/ApplicationTypeEnum.cxx000066400000000000000000000253031300200146000264550ustar00rootroot00000000000000 /*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 __APPLICATION_TYPE_ENUM_DECLARE__ #include "ApplicationTypeEnum.h" #undef __APPLICATION_TYPE_ENUM_DECLARE__ #include "CaretAssert.h" using namespace caret; /** * \class caret::ApplicationTypeEnum * \brief * * * * 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_applicationTypeEnumComboBox; * * Declare a slot that is called when user changes selection * private slots: * void applicationTypeEnumComboBoxItemActivated(); * * Implementation File (.cxx) * Include the header files * #include "EnumComboBoxTemplate.h" * #include "ApplicationTypeEnum.h" * * Instatiate: * m_applicationTypeEnumComboBox = new EnumComboBoxTemplate(this); * m_applicationTypeEnumComboBox->setup(); * * Get notified when the user changes the selection: * QObject::connect(m_applicationTypeEnumComboBox, SIGNAL(itemActivated()), * this, SLOT(applicationTypeEnumComboBoxItemActivated())); * * Update the selection: * m_applicationTypeEnumComboBox->setSelectedItem(NEW_VALUE); * * Read the selection: * const ApplicationTypeEnum::Enum VARIABLE = m_applicationTypeEnumComboBox->getSelectedItem(); * */ /** * Constructor. * * @param enumValue * An enumerated value. * @param name * Name of enumerated value. * * @param guiName * User-friendly name for use in user-interface. */ ApplicationTypeEnum::ApplicationTypeEnum(const Enum enumValue, const AString& name, const AString& guiName) { this->enumValue = enumValue; this->integerCode = integerCodeCounter++; this->name = name; this->guiName = guiName; } /** * Destructor. */ ApplicationTypeEnum::~ApplicationTypeEnum() { } /** * Initialize the enumerated metadata. */ void ApplicationTypeEnum::initialize() { if (initializedFlag) { return; } initializedFlag = true; enumData.push_back(ApplicationTypeEnum(APPLICATION_TYPE_INVALID, "APPLICATION_TYPE_INVALID", "Invalid or Not Set")); enumData.push_back(ApplicationTypeEnum(APPLICATION_TYPE_COMMAND_LINE, "APPLICATION_TYPE_COMMAND_LINE", "Command Line Application")); enumData.push_back(ApplicationTypeEnum(APPLICATION_TYPE_GRAPHICAL_USER_INTERFACE, "APPLICATION_TYPE_GRAPHICAL_USER_INTERFACE", "Graphical User Interface Application")); } /** * 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 ApplicationTypeEnum* ApplicationTypeEnum::findData(const Enum enumValue) { if (initializedFlag == false) initialize(); size_t num = enumData.size(); for (size_t i = 0; i < num; i++) { const ApplicationTypeEnum* 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 ApplicationTypeEnum::toName(Enum enumValue) { if (initializedFlag == false) initialize(); const ApplicationTypeEnum* 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. */ ApplicationTypeEnum::Enum ApplicationTypeEnum::fromName(const AString& name, bool* isValidOut) { if (initializedFlag == false) initialize(); bool validFlag = false; Enum enumValue = ApplicationTypeEnum::enumData[0].enumValue; for (std::vector::iterator iter = enumData.begin(); iter != enumData.end(); iter++) { const ApplicationTypeEnum& 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 ApplicationTypeEnum")); } return enumValue; } /** * Get a GUI string representation of the enumerated type. * @param enumValue * Enumerated value. * @return * String representing enumerated value. */ AString ApplicationTypeEnum::toGuiName(Enum enumValue) { if (initializedFlag == false) initialize(); const ApplicationTypeEnum* 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. */ ApplicationTypeEnum::Enum ApplicationTypeEnum::fromGuiName(const AString& guiName, bool* isValidOut) { if (initializedFlag == false) initialize(); bool validFlag = false; Enum enumValue = ApplicationTypeEnum::enumData[0].enumValue; for (std::vector::iterator iter = enumData.begin(); iter != enumData.end(); iter++) { const ApplicationTypeEnum& 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 ApplicationTypeEnum")); } return enumValue; } /** * Get the integer code for a data type. * * @return * Integer code for data type. */ int32_t ApplicationTypeEnum::toIntegerCode(Enum enumValue) { if (initializedFlag == false) initialize(); const ApplicationTypeEnum* 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. */ ApplicationTypeEnum::Enum ApplicationTypeEnum::fromIntegerCode(const int32_t integerCode, bool* isValidOut) { if (initializedFlag == false) initialize(); bool validFlag = false; Enum enumValue = ApplicationTypeEnum::enumData[0].enumValue; for (std::vector::iterator iter = enumData.begin(); iter != enumData.end(); iter++) { const ApplicationTypeEnum& 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 ApplicationTypeEnum")); } 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 ApplicationTypeEnum::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 ApplicationTypeEnum::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(ApplicationTypeEnum::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 ApplicationTypeEnum::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(ApplicationTypeEnum::toGuiName(iter->enumValue)); } if (isSorted) { std::sort(allGuiNames.begin(), allGuiNames.end()); } } connectome-workbench-1.2.3+git41-gc4c6c90/src/Common/ApplicationTypeEnum.h000066400000000000000000000063041300200146000261020ustar00rootroot00000000000000#ifndef __APPLICATION_TYPE_ENUM_H__ #define __APPLICATION_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 ApplicationTypeEnum { public: /** * Enumerated values. */ enum Enum { /** Invalid */ APPLICATION_TYPE_INVALID, /** Command Line Application */ APPLICATION_TYPE_COMMAND_LINE, /** Graphical User Interface Application */ APPLICATION_TYPE_GRAPHICAL_USER_INTERFACE }; ~ApplicationTypeEnum(); 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: ApplicationTypeEnum(const Enum enumValue, const AString& name, const AString& guiName); static const ApplicationTypeEnum* 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 __APPLICATION_TYPE_ENUM_DECLARE__ std::vector ApplicationTypeEnum::enumData; bool ApplicationTypeEnum::initializedFlag = false; int32_t ApplicationTypeEnum::integerCodeCounter = 0; #endif // __APPLICATION_TYPE_ENUM_DECLARE__ } // namespace #endif //__APPLICATION_TYPE_ENUM_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Common/BackgroundAndForegroundColors.cxx000066400000000000000000000250761300200146000304510ustar00rootroot00000000000000 /*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 __BACKGROUND_AND_FOREGROUND_COLORS_DECLARE__ #include "BackgroundAndForegroundColors.h" #undef __BACKGROUND_AND_FOREGROUND_COLORS_DECLARE__ #include "CaretAssert.h" using namespace caret; /** * \class caret::BackgroundAndForegroundColors * \brief Contains background and foreground colors used in graphics drawing. * \ingroup Common */ /** * Constructor. */ BackgroundAndForegroundColors::BackgroundAndForegroundColors() : CaretObject() { reset(); } /** * Destructor. */ BackgroundAndForegroundColors::~BackgroundAndForegroundColors() { } /** * Copy constructor. * @param obj * Object that is copied. */ BackgroundAndForegroundColors::BackgroundAndForegroundColors(const BackgroundAndForegroundColors& obj) : CaretObject(obj) { copyHelperBackgroundAndForegroundColors(obj); } /** * Assignment operator. * @param obj * Data copied from obj to this. * @return * Reference to this object. */ BackgroundAndForegroundColors& BackgroundAndForegroundColors::operator=(const BackgroundAndForegroundColors& obj) { if (this != &obj) { CaretObject::operator=(obj); copyHelperBackgroundAndForegroundColors(obj); } return *this; } /** * Helps with copying an object of this type. * @param obj * Object that is copied. */ 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]; } } /** * Reset the colors to the default values. */ void BackgroundAndForegroundColors::reset() { const uint8_t backRed = 0; const uint8_t backGreen = 0; const uint8_t backBlue = 0; const uint8_t foreRed = 255; const uint8_t foreGreen = 255; const uint8_t foreBlue = 255; setColor(m_colorForegroundAll, foreRed, foreGreen, foreBlue); setColor(m_colorBackgroundAll, backRed, backGreen, backBlue); setColor(m_colorForegroundChart, foreRed, foreGreen, foreBlue); setColor(m_colorBackgroundChart, backRed, backGreen, backBlue); setColor(m_colorForegroundSurface, foreRed, foreGreen, foreBlue); setColor(m_colorBackgroundSurface, backRed, backGreen, backBlue); setColor(m_colorForegroundVolume, foreRed, foreGreen, foreBlue); setColor(m_colorBackgroundVolume, backRed, backGreen, backBlue); const uint8_t chartRed = 255; const uint8_t chartGreen = 255; const uint8_t chartBlue = 255; setColor(m_colorChartMatrixGridLines, chartRed, chartGreen, chartBlue); } /** * Get the foreground color for viewing the ALL model. * * @param colorForeground * RGB color components ranging [0, 255]. */ void BackgroundAndForegroundColors::getColorForegroundAllView(uint8_t colorForeground[3]) const { for (int32_t i = 0; i < 3; i++) { colorForeground[i] = m_colorForegroundAll[i]; } } /** * Set the foreground color for viewing the ALL model. * * @param colorForeground * RGB color components ranging [0, 255]. */ void BackgroundAndForegroundColors::setColorForegroundAllView(const uint8_t colorForeground[3]) { for (int32_t i = 0; i < 3; i++) { m_colorForegroundAll[i] = colorForeground[i]; } } /** * Get the background color for viewing the ALL model. * * @param colorBackground * RGB color components ranging [0, 255]. */ void BackgroundAndForegroundColors::getColorBackgroundAllView(uint8_t colorBackground[3]) const { for (int32_t i = 0; i < 3; i++) { colorBackground[i] = m_colorBackgroundAll[i]; } } /** * Set the background color for viewing the ALL model. * * @param colorBackground * RGB color components ranging [0, 255]. */ void BackgroundAndForegroundColors::setColorBackgroundAllView(const uint8_t colorBackground[3]) { for (int32_t i = 0; i < 3; i++) { m_colorBackgroundAll[i] = colorBackground[i]; } } /** * Get the foreground color for viewing the CHART model. * * @param colorForeground * RGB color components ranging [0, 255]. */ void BackgroundAndForegroundColors::getColorForegroundChartView(uint8_t colorForeground[3]) const { for (int32_t i = 0; i < 3; i++) { colorForeground[i] = m_colorForegroundChart[i]; } } /** * Set the foreground color for viewing the CHART model. * * @param colorForeground * RGB color components ranging [0, 255]. */ void BackgroundAndForegroundColors::setColorForegroundChartView(const uint8_t colorForeground[3]) { for (int32_t i = 0; i < 3; i++) { m_colorForegroundChart[i] = colorForeground[i]; } } /** * Get the background color for viewing the CHART model. * * @param colorBackground * RGB color components ranging [0, 255]. */ void BackgroundAndForegroundColors::getColorBackgroundChartView(uint8_t colorBackground[3]) const { for (int32_t i = 0; i < 3; i++) { colorBackground[i] = m_colorBackgroundChart[i]; } } /** * Set the background color for viewing the CHART model. * * @param colorBackground * RGB color components ranging [0, 255]. */ void BackgroundAndForegroundColors::setColorBackgroundChartView(const uint8_t colorBackground[3]) { for (int32_t i = 0; i < 3; i++) { m_colorBackgroundChart[i] = colorBackground[i]; } } /** * Get the foreground color for viewing the SURFACE model. * * @param colorForeground * RGB color components ranging [0, 255]. */ void BackgroundAndForegroundColors::getColorForegroundSurfaceView(uint8_t colorForeground[3]) const { for (int32_t i = 0; i < 3; i++) { colorForeground[i] = m_colorForegroundSurface[i]; } } /** * Set the foreground color for viewing the SURFACE model. * * @param colorForeground * RGB color components ranging [0, 255]. */ void BackgroundAndForegroundColors::setColorForegroundSurfaceView(const uint8_t colorForeground[3]) { for (int32_t i = 0; i < 3; i++) { m_colorForegroundSurface[i] = colorForeground[i]; } } /** * Get the background color for viewing the SURFACE model. * * @param colorBackground * RGB color components ranging [0, 255]. */ void BackgroundAndForegroundColors::getColorBackgroundSurfaceView(uint8_t colorBackground[3]) const { for (int32_t i = 0; i < 3; i++) { colorBackground[i] = m_colorBackgroundSurface[i]; } } /** * Get the background color for viewing the SURFACE model. * * @param colorBackground * RGB color components ranging [0, 255]. */ void BackgroundAndForegroundColors::setColorBackgroundSurfaceView(const uint8_t colorBackground[3]) { for (int32_t i = 0; i < 3; i++) { m_colorBackgroundSurface[i] = colorBackground[i]; } } /** * Get the foreground color for viewing the VOLUME model. * * @param colorForeground * RGB color components ranging [0, 255]. */ void BackgroundAndForegroundColors::getColorForegroundVolumeView(uint8_t colorForeground[3]) const { for (int32_t i = 0; i < 3; i++) { colorForeground[i] = m_colorForegroundVolume[i]; } } /** * Set the foreground color for viewing the VOLUME model. * * @param colorForeground * RGB color components ranging [0, 255]. */ void BackgroundAndForegroundColors::setColorForegroundVolumeView(const uint8_t colorForeground[3]) { for (int32_t i = 0; i < 3; i++) { m_colorForegroundVolume[i] = colorForeground[i]; } } /** * Get the background color for viewing the VOLUME model. * * @param colorBackground * RGB color components ranging [0, 255]. */ void BackgroundAndForegroundColors::getColorBackgroundVolumeView(uint8_t colorBackground[3]) const { for (int32_t i = 0; i < 3; i++) { colorBackground[i] = m_colorBackgroundVolume[i]; } } /** * Set the background color for viewing the VOLUME model. * * @param colorBackground * RGB color components ranging [0, 255]. */ void BackgroundAndForegroundColors::setColorBackgroundVolumeView(const uint8_t colorBackground[3]) { for (int32_t i = 0; i < 3; i++) { m_colorBackgroundVolume[i] = colorBackground[i]; } } /** * Get the color for chart matrix grid lines * * @param colorChartMatrixGridLines * RGB color components ranging [0, 255]. */ void BackgroundAndForegroundColors::getColorChartMatrixGridLines(uint8_t colorChartMatrixGridLines[3]) const { for (int32_t i = 0; i < 3; i++) { colorChartMatrixGridLines[i] = m_colorChartMatrixGridLines[i]; } } /** * Set the color for chart matrix grid lines * * @param colorChartMatrixGridLines * RGB color components ranging [0, 255]. */ void BackgroundAndForegroundColors::setColorChartMatrixGridLines(const uint8_t colorChartMatrixGridLines[3]) { for (int32_t i = 0; i < 3; i++) { m_colorChartMatrixGridLines[i] = colorChartMatrixGridLines[i]; } } /** * Set a color with the given color components that range 0 to 255. * * @param color * Color that is set. * @param red * Red component. * @param green * Green component. * @param blue * Blue component. */ void BackgroundAndForegroundColors::setColor(uint8_t color[3], const uint8_t red, const uint8_t green, const uint8_t blue) { color[0] = red; color[1] = green; color[2] = blue; } connectome-workbench-1.2.3+git41-gc4c6c90/src/Common/BackgroundAndForegroundColors.h000066400000000000000000000100251300200146000300620ustar00rootroot00000000000000#ifndef __BACKGROUND_AND_FOREGROUND_COLORS_H__ #define __BACKGROUND_AND_FOREGROUND_COLORS_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" namespace caret { class BackgroundAndForegroundColors : public CaretObject { public: BackgroundAndForegroundColors(); virtual ~BackgroundAndForegroundColors(); BackgroundAndForegroundColors(const BackgroundAndForegroundColors& obj); BackgroundAndForegroundColors& operator=(const BackgroundAndForegroundColors& obj); void reset(); // ADD_NEW_METHODS_HERE void getColorForegroundAllView(uint8_t colorForeground[3]) const; void setColorForegroundAllView(const uint8_t colorForeground[3]); void getColorBackgroundAllView(uint8_t colorForeground[3]) const; void setColorBackgroundAllView(const uint8_t colorForeground[3]); void getColorForegroundChartView(uint8_t colorForeground[3]) const; void setColorForegroundChartView(const uint8_t colorForeground[3]); void getColorBackgroundChartView(uint8_t colorForeground[3]) const; void setColorBackgroundChartView(const uint8_t colorForeground[3]); void getColorForegroundSurfaceView(uint8_t colorForeground[3]) const; void setColorForegroundSurfaceView(const uint8_t colorForeground[3]); void getColorBackgroundSurfaceView(uint8_t colorForeground[3]) const; void setColorBackgroundSurfaceView(const uint8_t colorForeground[3]); void getColorForegroundVolumeView(uint8_t colorForeground[3]) const; void setColorForegroundVolumeView(const uint8_t colorForeground[3]); void getColorBackgroundVolumeView(uint8_t colorForeground[3]) const; void setColorBackgroundVolumeView(const uint8_t colorForeground[3]); void getColorChartMatrixGridLines(uint8_t colorChartMatrixGridLines[3]) const; void setColorChartMatrixGridLines(const uint8_t colorChartMatrixGridLines[3]); private: void copyHelperBackgroundAndForegroundColors(const BackgroundAndForegroundColors& obj); void setColor(uint8_t color[3], const uint8_t red, const uint8_t green, const uint8_t blue); uint8_t m_colorForegroundAll[3]; uint8_t m_colorBackgroundAll[3]; uint8_t m_colorForegroundChart[3]; uint8_t m_colorBackgroundChart[3]; uint8_t m_colorForegroundSurface[3]; uint8_t m_colorBackgroundSurface[3]; uint8_t m_colorForegroundVolume[3]; uint8_t m_colorBackgroundVolume[3]; uint8_t m_colorChartMatrixGridLines[3]; // ADD_NEW_MEMBERS_HERE friend class BackgroundAndForegroundColorsSceneHelper; friend class CaretPreferences; }; #ifdef __BACKGROUND_AND_FOREGROUND_COLORS_DECLARE__ // #endif // __BACKGROUND_AND_FOREGROUND_COLORS_DECLARE__ } // namespace #endif //__BACKGROUND_AND_FOREGROUND_COLORS_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Common/BackgroundAndForegroundColorsModeEnum.cxx000066400000000000000000000267461300200146000321100ustar00rootroot00000000000000 /*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 __BACKGROUND_AND_FOREGROUND_COLORS_MODE_ENUM_DECLARE__ #include "BackgroundAndForegroundColorsModeEnum.h" #undef __BACKGROUND_AND_FOREGROUND_COLORS_MODE_ENUM_DECLARE__ #include "CaretAssert.h" using namespace caret; /** * \class caret::BackgroundAndForegroundColorsModeEnum * \brief * * * * 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_backgroundAndForegroundColorsModeEnumComboBox; * * Declare a slot that is called when user changes selection * private slots: * void backgroundAndForegroundColorsModeEnumComboBoxItemActivated(); * * Implementation File (.cxx) * Include the header files * #include "EnumComboBoxTemplate.h" * #include "BackgroundAndForegroundColorsModeEnum.h" * * Instatiate: * m_backgroundAndForegroundColorsModeEnumComboBox = new EnumComboBoxTemplate(this); * m_backgroundAndForegroundColorsModeEnumComboBox->setup(); * * Get notified when the user changes the selection: * QObject::connect(m_backgroundAndForegroundColorsModeEnumComboBox, SIGNAL(itemActivated()), * this, SLOT(backgroundAndForegroundColorsModeEnumComboBoxItemActivated())); * * Update the selection: * m_backgroundAndForegroundColorsModeEnumComboBox->setSelectedItem(NEW_VALUE); * * Read the selection: * const BackgroundAndForegroundColorsModeEnum::Enum VARIABLE = m_backgroundAndForegroundColorsModeEnumComboBox->getSelectedItem(); * */ /** * Constructor. * * @param enumValue * An enumerated value. * @param name * Name of enumerated value. * * @param guiName * User-friendly name for use in user-interface. */ BackgroundAndForegroundColorsModeEnum::BackgroundAndForegroundColorsModeEnum(const Enum enumValue, const AString& name, const AString& guiName) { this->enumValue = enumValue; this->integerCode = integerCodeCounter++; this->name = name; this->guiName = guiName; } /** * Destructor. */ BackgroundAndForegroundColorsModeEnum::~BackgroundAndForegroundColorsModeEnum() { } /** * Initialize the enumerated metadata. */ void BackgroundAndForegroundColorsModeEnum::initialize() { if (initializedFlag) { return; } initializedFlag = true; enumData.push_back(BackgroundAndForegroundColorsModeEnum(SCENE, "SCENE", "Scene")); enumData.push_back(BackgroundAndForegroundColorsModeEnum(USER_PREFERENCES, "USER_PREFERENCES", "User Preferences")); } /** * 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 BackgroundAndForegroundColorsModeEnum* BackgroundAndForegroundColorsModeEnum::findData(const Enum enumValue) { if (initializedFlag == false) initialize(); size_t num = enumData.size(); for (size_t i = 0; i < num; i++) { const BackgroundAndForegroundColorsModeEnum* 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 BackgroundAndForegroundColorsModeEnum::toName(Enum enumValue) { if (initializedFlag == false) initialize(); const BackgroundAndForegroundColorsModeEnum* 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. */ BackgroundAndForegroundColorsModeEnum::Enum BackgroundAndForegroundColorsModeEnum::fromName(const AString& name, bool* isValidOut) { if (initializedFlag == false) initialize(); bool validFlag = false; Enum enumValue = BackgroundAndForegroundColorsModeEnum::enumData[0].enumValue; for (std::vector::iterator iter = enumData.begin(); iter != enumData.end(); iter++) { const BackgroundAndForegroundColorsModeEnum& 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 BackgroundAndForegroundColorsModeEnum")); } return enumValue; } /** * Get a GUI string representation of the enumerated type. * @param enumValue * Enumerated value. * @return * String representing enumerated value. */ AString BackgroundAndForegroundColorsModeEnum::toGuiName(Enum enumValue) { if (initializedFlag == false) initialize(); const BackgroundAndForegroundColorsModeEnum* 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. */ BackgroundAndForegroundColorsModeEnum::Enum BackgroundAndForegroundColorsModeEnum::fromGuiName(const AString& guiName, bool* isValidOut) { if (initializedFlag == false) initialize(); bool validFlag = false; Enum enumValue = BackgroundAndForegroundColorsModeEnum::enumData[0].enumValue; for (std::vector::iterator iter = enumData.begin(); iter != enumData.end(); iter++) { const BackgroundAndForegroundColorsModeEnum& 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 BackgroundAndForegroundColorsModeEnum")); } return enumValue; } /** * Get the integer code for a data type. * * @return * Integer code for data type. */ int32_t BackgroundAndForegroundColorsModeEnum::toIntegerCode(Enum enumValue) { if (initializedFlag == false) initialize(); const BackgroundAndForegroundColorsModeEnum* 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. */ BackgroundAndForegroundColorsModeEnum::Enum BackgroundAndForegroundColorsModeEnum::fromIntegerCode(const int32_t integerCode, bool* isValidOut) { if (initializedFlag == false) initialize(); bool validFlag = false; Enum enumValue = BackgroundAndForegroundColorsModeEnum::enumData[0].enumValue; for (std::vector::iterator iter = enumData.begin(); iter != enumData.end(); iter++) { const BackgroundAndForegroundColorsModeEnum& 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 BackgroundAndForegroundColorsModeEnum")); } 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 BackgroundAndForegroundColorsModeEnum::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 BackgroundAndForegroundColorsModeEnum::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(BackgroundAndForegroundColorsModeEnum::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 BackgroundAndForegroundColorsModeEnum::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(BackgroundAndForegroundColorsModeEnum::toGuiName(iter->enumValue)); } if (isSorted) { std::sort(allGuiNames.begin(), allGuiNames.end()); } } connectome-workbench-1.2.3+git41-gc4c6c90/src/Common/BackgroundAndForegroundColorsModeEnum.h000066400000000000000000000065141300200146000315240ustar00rootroot00000000000000#ifndef __BACKGROUND_AND_FOREGROUND_COLORS_MODE_ENUM_H__ #define __BACKGROUND_AND_FOREGROUND_COLORS_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 BackgroundAndForegroundColorsModeEnum { public: /** * Enumerated values. */ enum Enum { /** Scene Colors */ SCENE, /** User Preferences Colors */ USER_PREFERENCES }; ~BackgroundAndForegroundColorsModeEnum(); 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: BackgroundAndForegroundColorsModeEnum(const Enum enumValue, const AString& name, const AString& guiName); static const BackgroundAndForegroundColorsModeEnum* 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 __BACKGROUND_AND_FOREGROUND_COLORS_MODE_ENUM_DECLARE__ std::vector BackgroundAndForegroundColorsModeEnum::enumData; bool BackgroundAndForegroundColorsModeEnum::initializedFlag = false; int32_t BackgroundAndForegroundColorsModeEnum::integerCodeCounter = 0; #endif // __BACKGROUND_AND_FOREGROUND_COLORS_MODE_ENUM_DECLARE__ } // namespace #endif //__BACKGROUND_AND_FOREGROUND_COLORS_MODE_ENUM_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Common/Base64.cxx000066400000000000000000000223521300200146000235500ustar00rootroot00000000000000/*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*/ /*========================================================================= Program: Visualization Toolkit Module: $RCSfile: Base64.cxx,v $ Copyright (c) Ken Martin, Will Schroeder, Bill Lorensen All rights reserved. See Copyright.txt or http://www.kitware.com/Copyright.htm for details. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the above copyright notice for more information. =========================================================================*/ #include "CaretAssert.h" #include "Base64.h" using namespace caret; //---------------------------------------------------------------------------- static const unsigned char Base64EncodeTable[65] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" "abcdefghijklmnopqrstuvwxyz" "0123456789+/"; //---------------------------------------------------------------------------- inline static unsigned char Base64EncodeChar(unsigned char c) { CaretAssert( c < 65 ); return Base64EncodeTable[c]; } Base64::Base64() {} Base64::~Base64() {} //---------------------------------------------------------------------------- void Base64::EncodeTriplet(unsigned char i0, unsigned char i1, unsigned char i2, unsigned char *o0, unsigned char *o1, unsigned char *o2, unsigned char *o3) { *o0 = Base64EncodeChar((i0 >> 2) & 0x3F); *o1 = Base64EncodeChar(((i0 << 4) & 0x30)|((i1 >> 4) & 0x0F)); *o2 = Base64EncodeChar(((i1 << 2) & 0x3C)|((i2 >> 6) & 0x03)); *o3 = Base64EncodeChar(i2 & 0x3F); } //---------------------------------------------------------------------------- void Base64::EncodePair(unsigned char i0, unsigned char i1, unsigned char *o0, unsigned char *o1, unsigned char *o2, unsigned char *o3) { *o0 = Base64EncodeChar((i0 >> 2) & 0x3F); *o1 = Base64EncodeChar(((i0 << 4) & 0x30)|((i1 >> 4) & 0x0F)); *o2 = Base64EncodeChar(((i1 << 2) & 0x3C)); *o3 = '='; } //---------------------------------------------------------------------------- void Base64::EncodeSingle(unsigned char i0, unsigned char *o0, unsigned char *o1, unsigned char *o2, unsigned char *o3) { *o0 = Base64EncodeChar((i0 >> 2) & 0x3F); *o1 = Base64EncodeChar(((i0 << 4) & 0x30)); *o2 = '='; *o3 = '='; } //---------------------------------------------------------------------------- uint64_t Base64::encode(const unsigned char *input, uint64_t length, unsigned char *output, int32_t mark_end) { const unsigned char *ptr = input; const unsigned char *end = input + length; unsigned char *optr = output; // Encode complete triplet while ((end - ptr) >= 3) { Base64::EncodeTriplet(ptr[0], ptr[1], ptr[2], &optr[0], &optr[1], &optr[2], &optr[3]); ptr += 3; optr += 4; } // Encodes a 2-byte ending into 3 bytes and 1 pad byte and writes. if (end - ptr == 2) { Base64::EncodePair(ptr[0], ptr[1], &optr[0], &optr[1], &optr[2], &optr[3]); optr += 4; } // Encodes a 1-byte ending into 2 bytes and 2 pad bytes else if (end - ptr == 1) { Base64::EncodeSingle(ptr[0], &optr[0], &optr[1], &optr[2], &optr[3]); optr += 4; } // Do we need to mark the end else if (mark_end) { optr[0] = optr[1] = optr[2] = optr[3] = '='; optr += 4; } return optr - output; } //---------------------------------------------------------------------------- static const unsigned char Base64DecodeTable[256] = { 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0x3E,0xFF,0xFF,0xFF,0x3F, 0x34,0x35,0x36,0x37,0x38,0x39,0x3A,0x3B, 0x3C,0x3D,0xFF,0xFF,0xFF,0x00,0xFF,0xFF, 0xFF,0x00,0x01,0x02,0x03,0x04,0x05,0x06, 0x07,0x08,0x09,0x0A,0x0B,0x0C,0x0D,0x0E, 0x0F,0x10,0x11,0x12,0x13,0x14,0x15,0x16, 0x17,0x18,0x19,0xFF,0xFF,0xFF,0xFF,0xFF, 0xFF,0x1A,0x1B,0x1C,0x1D,0x1E,0x1F,0x20, 0x21,0x22,0x23,0x24,0x25,0x26,0x27,0x28, 0x29,0x2A,0x2B,0x2C,0x2D,0x2E,0x2F,0x30, 0x31,0x32,0x33,0xFF,0xFF,0xFF,0xFF,0xFF, //------------------------------------- 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF }; //---------------------------------------------------------------------------- inline static unsigned char Base64DecodeChar(unsigned char c) { return Base64DecodeTable[c]; } //---------------------------------------------------------------------------- int Base64::DecodeTriplet(unsigned char i0, unsigned char i1, unsigned char i2, unsigned char i3, unsigned char *o0, unsigned char *o1, unsigned char *o2) { unsigned char d0, d1, d2, d3; d0 = Base64DecodeChar(i0); d1 = Base64DecodeChar(i1); d2 = Base64DecodeChar(i2); d3 = Base64DecodeChar(i3); // Make sure all characters were valid if (d0 == 0xFF || d1 == 0xFF || d2 == 0xFF || d3 == 0xFF) { return 0; } // Decode the 3 bytes *o0 = ((d0 << 2) & 0xFC) | ((d1 >> 4) & 0x03); *o1 = ((d1 << 4) & 0xF0) | ((d2 >> 2) & 0x0F); *o2 = ((d2 << 6) & 0xC0) | ((d3 >> 0) & 0x3F); // Return the number of bytes actually decoded if (i2 == '=') { return 1; } if (i3 == '=') { return 2; } return 3; } //---------------------------------------------------------------------------- uint64_t Base64::decode(const unsigned char *input, uint64_t length, unsigned char *output, uint64_t max_input_length) { const unsigned char *ptr = input; unsigned char *optr = output; // Decode complete triplet if (max_input_length) { const unsigned char *end = input + max_input_length; while (ptr < end) { int len = Base64::DecodeTriplet(ptr[0], ptr[1], ptr[2], ptr[3], &optr[0], &optr[1], &optr[2]); optr += len; if(len < 3) { return optr - output; } ptr += 4; } } else { unsigned char *oend = output + length; while ((oend - optr) >= 3) { int len = Base64::DecodeTriplet(ptr[0], ptr[1], ptr[2], ptr[3], &optr[0], &optr[1], &optr[2]); optr += len; if(len < 3) { return optr - output; } ptr += 4; } // Decode the last triplet unsigned char temp; if (oend - optr == 2) { int len = Base64::DecodeTriplet(ptr[0], ptr[1], ptr[2], ptr[3], &optr[0], &optr[1], &temp); optr += (len > 2 ? 2 : len); } else if (oend - optr == 1) { unsigned char temp2; int len = Base64::DecodeTriplet(ptr[0], ptr[1], ptr[2], ptr[3], &optr[0], &temp, &temp2); optr += (len > 2 ? 2 : len); } } return optr - output; } connectome-workbench-1.2.3+git41-gc4c6c90/src/Common/Base64.h000066400000000000000000000120461300200146000231740ustar00rootroot00000000000000#ifndef __BASE64_H__ #define __BASE64_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*/ /*========================================================================= Program: Visualization Toolkit Module: $RCSfile: Base64.h,v $ Copyright (c) Ken Martin, Will Schroeder, Bill Lorensen All rights reserved. See Copyright.txt or http://www.kitware.com/Copyright.htm for details. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the above copyright notice for more information. =========================================================================*/ // .NAME Base64 - base64 encode and decode utilities. // .SECTION Description // Base64 implements base64 encoding and decoding. #include #include "CaretObject.h" namespace caret { /** * This is copied directly from VTK's vtkBase64Utilities class. */ class Base64 : public CaretObject { private: Base64(); ~Base64(); public: // Description: // Encode 'length' bytes from the input buffer and store the // encoded stream into the output buffer. Return the length of // the encoded stream. Note that the output buffer must be allocated // by the caller (length * 1.5 should be a safe estimate). // If 'mark_end' is true than an extra set of 4 bytes is added // to the end of the stream if the input is a multiple of 3 bytes. // These bytes are invalid chars and therefore they will stop the decoder // thus enabling the caller to decode a stream without actually knowing // how much data to expect (if the input is not a multiple of 3 bytes then // the extra padding needed to complete the encode 4 bytes will stop the // decoding anyway). static uint64_t encode(const unsigned char *input, uint64_t length, unsigned char *output, int32_t mark_end = 0); // Description: // Decode bytes from the input buffer and store the decoded stream // into the output buffer until 'length' bytes have been decoded. // Return the real length of the decoded stream (which should be equal to // 'length'). Note that the output buffer must be allocated by the caller. // If 'max_input_length' is not null, then it specifies the number of // encoded bytes that should be at most read from the input buffer. In // that case the 'length' parameter is ignored. This enables the caller // to decode a stream without actually knowing how much decoded data to // expect (of course, the buffer must be large enough). static uint64_t decode(const unsigned char *input, uint64_t length, unsigned char *output, uint64_t max_input_length = 0); private: // Description: // Decode 4 bytes into 3 bytes. static int DecodeTriplet(unsigned char i0, unsigned char i1, unsigned char i2, unsigned char i3, unsigned char *o0, unsigned char *o1, unsigned char *o2); // Description: // Encode 3 bytes into 4 bytes static void EncodeTriplet(unsigned char i0, unsigned char i1, unsigned char i2, unsigned char *o0, unsigned char *o1, unsigned char *o2, unsigned char *o3); // Description: // Encode 2 bytes into 4 bytes static void EncodePair(unsigned char i0, unsigned char i1, unsigned char *o0, unsigned char *o1, unsigned char *o2, unsigned char *o3); // Description: // Encode 1 byte into 4 bytes static void EncodeSingle(unsigned char i0, unsigned char *o0, unsigned char *o1, unsigned char *o2, unsigned char *o3); }; } // namespace #endif // __BASE64_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Common/BoundingBox.cxx000066400000000000000000000322101300200146000247340ustar00rootroot00000000000000/*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 "BoundingBox.h" using namespace caret; /** * Constructor setup for update of an update method. * */ BoundingBox::BoundingBox() : CaretObject() { this->initializeMembersBoundingBox(); this->resetForUpdate(); } /** * Create a bounding box from a six-dimensional array containing * min-X, max-X, min-Y, max-Y, min-Z, max-Z. * * @param minMaxXYZ - array described above. * @throws IllegalArgumentException if the array does not contain six * elements or a min value is greater than a max value. * */ BoundingBox::BoundingBox( const float minMaxXYZ[]) : CaretObject() { this->initializeMembersBoundingBox(); for (int i = 0; i < 6; i++) { this->boundingBox[i] = minMaxXYZ[i]; } } /** * Copy constructor. * @param bb * BoundingBox that is copied. */ BoundingBox::BoundingBox(const BoundingBox& bb) : CaretObject(bb) { this->initializeMembersBoundingBox(); this->copyHelper(bb); } /** * Assignment operator. * @param bb * BoundingBox that replace this bounding box. */ BoundingBox& BoundingBox::operator=(const BoundingBox& bb) { if (this != &bb) { CaretObject::operator=(bb); this->copyHelper(bb); } return *this; } /** * Destructor */ BoundingBox::~BoundingBox() { } /** * Initialize data members. */ void BoundingBox::initializeMembersBoundingBox() { } /** * Helps with copy constructor and assignemnt operator. */ void BoundingBox::copyHelper(const BoundingBox& bo) { for (int i = 0; i < 6; i++) { this->boundingBox[i] = bo.boundingBox[i]; } } /** * Reset a new bounding box with the minimum and maximum values * all set to zero. */ void BoundingBox::resetZeros() { this->set(0.0, 0.0, 0.0, 0.0, 0.0, 0.0); } /** * Reset a bounding box with the minimum values initialized to * the minimum float value and the maximum values initialized to the * maximum float value. Use one of the setMax() or setMinY() methods * to update this bounding box. */ void BoundingBox::resetWithMaximumExtent() { const float f = std::numeric_limits::max(); this->set(-f, f, -f, f, -f, f); } /** * Reset a bounding box with the minimum values initialized to * the maximum float value and the maximum values initialized to the * minimum float value. Use the update(float[]) method to update * the bounding box. */ void BoundingBox::resetForUpdate() { const float f = std::numeric_limits::max(); this->set(f, -f, f, -f, f, -f); } /** * Set bounding box using the array of points. * @param points3D * Array of three dimensional points. * @param numPoints * Number of points. */ void BoundingBox::set(const float* points3D, const int64_t numPoints) { this->resetForUpdate(); for (int64_t i = 0; i < numPoints; i++) { this->update(&points3D[i*3]); } } /** * Set a new bounding box. * @param minX Minimum X-coordinate for bounding box. * @param maxX Maximum X-coordinate for bounding box. * @param minY Minimum Y-coordinate for bounding box. * @param maxY Maximum Y-coordinate for bounding box. * @param minZ Minimum Z-coordinate for bounding box. * @param maxZ Maximum Z-coordinate for bounding box. */ void BoundingBox::set( const float minX, const float maxX, const float minY, const float maxY, const float minZ, const float maxZ) { this->boundingBox[0] = minX; this->boundingBox[1] = maxX; this->boundingBox[2] = minY; this->boundingBox[3] = maxY; this->boundingBox[4] = minZ; this->boundingBox[5] = maxZ; } /** * Set a bounding box from a six-dimensional array containing * min-X, max-X, min-Y, max-Y, min-Z, max-Z. * * @param minMaxXYZ - array described above. */ void BoundingBox::set(const float minMaxXYZ[6]) { this->boundingBox[0] = minMaxXYZ[0]; this->boundingBox[1] = minMaxXYZ[1]; this->boundingBox[2] = minMaxXYZ[2]; this->boundingBox[3] = minMaxXYZ[3]; this->boundingBox[4] = minMaxXYZ[4]; this->boundingBox[5] = minMaxXYZ[5]; } /** * Update the bounding box with the XYZ value passed in. The bound box * must have been created with newInstanceForUpdate() or properly * initialized by the user. * * @param xyz - Three dimensional array containing XYZ. * @throws IllegalArgumentException if array does not have three elements. * */ void BoundingBox::update(const float xyz[3]) { if (xyz[0] < this->boundingBox[0]) this->boundingBox[0] = xyz[0]; if (xyz[0] > this->boundingBox[1]) this->boundingBox[1] = xyz[0]; if (xyz[1] < this->boundingBox[2]) this->boundingBox[2] = xyz[1]; if (xyz[1] > this->boundingBox[3]) this->boundingBox[3] = xyz[1]; if (xyz[2] < this->boundingBox[4]) this->boundingBox[4] = xyz[2]; if (xyz[2] > this->boundingBox[5]) this->boundingBox[5] = xyz[2]; } /** * Update the bounding box with the XYZ value passed in. The bound box * must have been created with newInstanceForUpdate() or properly * initialized by the user. * * @param x * X-coordinate. * @param y * Y-coordinate. * @param Z * Z-coordinate. * @throws IllegalArgumentException if array does not have three elements. * */ void BoundingBox::update(const float x, const float y, const float z) { if (x < this->boundingBox[0]) this->boundingBox[0] = x; if (x > this->boundingBox[1]) this->boundingBox[1] = x; if (y < this->boundingBox[2]) this->boundingBox[2] = y; if (y > this->boundingBox[3]) this->boundingBox[3] = y; if (z < this->boundingBox[4]) this->boundingBox[4] = z; if (z > this->boundingBox[5]) this->boundingBox[5] = z; } /** * Get the bounds in an array. * @return Array of six containing minX, maxX, minY, maxY, minZ, maxZ. * */ const float* BoundingBox::getBounds() const { return this->boundingBox; } /** * Get the bounds in an array. * @param Output array of six containing minX, maxX, minY, maxY, minZ, maxZ. * */ void BoundingBox::getBounds(float bounds[6]) const { bounds[0] = this->boundingBox[0]; bounds[1] = this->boundingBox[1]; bounds[2] = this->boundingBox[2]; bounds[3] = this->boundingBox[3]; bounds[4] = this->boundingBox[4]; bounds[5] = this->boundingBox[5]; } /** * Get the X-Coordinate difference. * @return X-Coordinate difference. * */ float BoundingBox::getDifferenceX() const { return (this->boundingBox[1] - this->boundingBox[0]); } /** * Get the Y-Coordinate difference. * @return Y-Coordinate difference. * */ float BoundingBox::getDifferenceY() const { return (this->boundingBox[3] - this->boundingBox[2]); } /** * Get the Z-Coordinate difference. * @return Z-Coordinate difference. * */ float BoundingBox::getDifferenceZ() const { return (this->boundingBox[5] - this->boundingBox[4]); } /** * Get the X minimum value. * @return Its value. * */ float BoundingBox::getMinX() const { return this->boundingBox[0]; } /** * Get the X maximum value. * @return Its value. * */ float BoundingBox::getMaxX() const { return this->boundingBox[1]; } /** * Get the Y minimum value. * @return Its value. * */ float BoundingBox::getMinY() const { return this->boundingBox[2]; } /** * Get the Y maximum value. * @return Its value. * */ float BoundingBox::getMaxY() const { return this->boundingBox[3]; } /** * Get the Z minimum value. * @return Its value. * */ float BoundingBox::getMinZ() const { return this->boundingBox[4]; } /** * Get the Z maximum value. * @return Its value. * */ float BoundingBox::getMaxZ() const { return this->boundingBox[5]; } /** * Get the minimum XYZ of the bounding box. * @return Minimum XYZ of the bounding box. * in an array that the caller MUST delete[]; */ float* BoundingBox::getMinXYZ() const { float* f = new float[3]; f[0] = this->boundingBox[0]; f[1] = this->boundingBox[2]; f[2] = this->boundingBox[4]; return f; } /** * Get the maximum XYZ of the bounding box. * @return Maximum XYZ of the bounding box * in an array that the caller MUST delete[]; */ float* BoundingBox::getMaxXYZ() const { float* f = new float[3]; f[0] = this->boundingBox[1]; f[1] = this->boundingBox[3]; f[2] = this->boundingBox[5]; return f; } /** * Set the minimum X value. * @param value - new value. * */ void BoundingBox::setMinX(const float value) { this->boundingBox[0] = value; } /** * Set the maximum X value. * @param value - new value. * */ void BoundingBox::setMaxX(const float value) { this->boundingBox[1] = value; } /** * Set the minimum Y value. * @param value - new value. * */ void BoundingBox::setMinY(const float value) { this->boundingBox[2] = value; } /** * Set the maximum Y value. * @param value - new value. * */ void BoundingBox::setMaxY(const float value) { this->boundingBox[3] = value; } /** * Set the minimum Z value. * @param value - new value. * */ void BoundingBox::setMinZ(const float value) { this->boundingBox[4] = value; } /** * Set the maximum Z value. * @param value - new value. * */ void BoundingBox::setMaxZ(const float value) { this->boundingBox[5] = value; } /** * @return X-Coordinate at center of the bounding box. */ float BoundingBox::getCenterX() const { const float centerX = (this->boundingBox[0] + this->boundingBox[1]) * 0.5; return centerX; } /** * @return Y-Coordinate at center of the bounding box. */ float BoundingBox::getCenterY() const { const float centerY = (this->boundingBox[2] + this->boundingBox[3]) * 0.5; return centerY; } /** * @return Z-Coordinate at center of the bounding box. */ float BoundingBox::getCenterZ() const { const float centerZ = (this->boundingBox[4] + this->boundingBox[5]) * 0.5; return centerZ; } /** * Get the center of the bounding box. * @param centerOut * Three dimensional array into which the center is loaded. */ void BoundingBox::getCenter(float centerOut[3]) const { centerOut[0] = (this->boundingBox[0] + this->boundingBox[1]) * 0.5; centerOut[1] = (this->boundingBox[2] + this->boundingBox[3]) * 0.5; centerOut[2] = (this->boundingBox[4] + this->boundingBox[5]) * 0.5; } /** * Is the coordinate within the bounding box? * @param xyz - The coordinate. * @return True if coordinate is within the bounding box, else false. * */ bool BoundingBox::isCoordinateWithinBoundingBox(const float xyz[]) const { if (xyz[0] < this->boundingBox[0]) return false; if (xyz[0] > this->boundingBox[1]) return false; if (xyz[1] < this->boundingBox[2]) return false; if (xyz[1] > this->boundingBox[3]) return false; if (xyz[2] < this->boundingBox[4]) return false; if (xyz[2] > this->boundingBox[5]) return false; return true; } /** * Limit the given coordinate to the bounding box. * * @param xyz * The coordinate. */ void BoundingBox::limitCoordinateToBoundingBox(float xyz[3]) const { if (xyz[0] < this->boundingBox[0]) xyz[0] = this->boundingBox[0]; if (xyz[0] > this->boundingBox[1]) xyz[0] = this->boundingBox[1]; if (xyz[1] < this->boundingBox[2]) xyz[1] = this->boundingBox[2]; if (xyz[1] > this->boundingBox[3]) xyz[1] = this->boundingBox[3]; if (xyz[2] < this->boundingBox[4]) xyz[2] = this->boundingBox[4]; if (xyz[2] > this->boundingBox[5]) xyz[2] = this->boundingBox[5]; } /** * Limit the given coordinate to the bounding box. * * @param xyz * The coordinate. */ void BoundingBox::limitCoordinateToBoundingBox(double xyz[3]) const { if (xyz[0] < this->boundingBox[0]) xyz[0] = this->boundingBox[0]; if (xyz[0] > this->boundingBox[1]) xyz[0] = this->boundingBox[1]; if (xyz[1] < this->boundingBox[2]) xyz[1] = this->boundingBox[2]; if (xyz[1] > this->boundingBox[3]) xyz[1] = this->boundingBox[3]; if (xyz[2] < this->boundingBox[4]) xyz[2] = this->boundingBox[4]; if (xyz[2] > this->boundingBox[5]) xyz[2] = this->boundingBox[5]; } /** * Get String representation of bounding box. * @return String containing bounding box. * */ AString BoundingBox::toString() const { std::stringstream str; str << "BoundingBox=[" << this->boundingBox[0] << "," << this->boundingBox[1] << "," << this->boundingBox[2] << "," << this->boundingBox[3] << "," << this->boundingBox[4] << "," << this->boundingBox[5] << "]"; AString s = AString::fromStdString(str.str()); return s; } connectome-workbench-1.2.3+git41-gc4c6c90/src/Common/BoundingBox.h000066400000000000000000000060061300200146000243650ustar00rootroot00000000000000#ifndef __BOUNDINGBOX_H__ #define __BOUNDINGBOX_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 namespace caret { /** * A bounding box - minimum and maximum X, Y, and Z values for something. */ class BoundingBox : public CaretObject { public: BoundingBox(); BoundingBox(const float minMaxXYZ[]); BoundingBox(const BoundingBox& bb); BoundingBox& operator=(const BoundingBox& co); virtual ~BoundingBox(); public: void resetZeros(); void resetWithMaximumExtent(); void resetForUpdate(); void set(const float* points3D, const int64_t numPoints); void set(const float minX, const float maxX, const float minY, const float maxY, const float minZ, const float maxZ); void set(const float minMaxXYZ[6]); void update(const float xyz[]); void update(const float x, const float y, const float z); const float* getBounds() const; void getBounds(float bounds[6]) const; float getDifferenceX() const; float getDifferenceY() const; float getDifferenceZ() const; float getMinX() const; float getMaxX() const; float getMinY() const; float getMaxY() const; float getMinZ() const; float getMaxZ() const; float* getMinXYZ() const; float* getMaxXYZ() const; void setMinX(const float value); void setMaxX(const float value); void setMinY(const float value); void setMaxY(const float value); void setMinZ(const float value); void setMaxZ(const float value); float getCenterX() const; float getCenterY() const; float getCenterZ() const; void getCenter(float centerOut[3]) const; bool isCoordinateWithinBoundingBox(const float xyz[]) const; void limitCoordinateToBoundingBox(float xyz[3]) const; void limitCoordinateToBoundingBox(double xyz[3]) const; AString toString() const; private: void initializeMembersBoundingBox(); void copyHelper(const BoundingBox& bo); private: float boundingBox[6]; }; } // namespace #endif // __BOUNDINGBOX_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Common/BrainConstants.cxx000066400000000000000000000016521300200146000254540ustar00rootroot00000000000000 /*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_CONSTANTS_DECLARE__ #include "BrainConstants.h" #undef __BRAIN_CONSTANTS_DECLARE__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Common/BrainConstants.h000066400000000000000000000040311300200146000250730ustar00rootroot00000000000000#ifndef __BRAIN_CONSTANTS__H_ #define __BRAIN_CONSTANTS__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 namespace caret { /// Constants for use in the Brain class BrainConstants { public: enum { /// Maximum number of browser windows MAXIMUM_NUMBER_OF_BROWSER_WINDOWS = 10, /// Maximum number of browser tabs MAXIMUM_NUMBER_OF_BROWSER_TABS = 100, /// Maximum number of overlays MAXIMUM_NUMBER_OF_OVERLAYS = 50, /// Minimum number of overlays MINIMUM_NUMBER_OF_OVERLAYS = 3, /// Minimum number of volume surface outlines MINIMUM_NUMBER_OF_VOLUME_SURFACE_OUTLINES = 4, /// Maximum number of volume surface outlines MAXIMUM_NUMBER_OF_VOLUME_SURFACE_OUTLINES = 25 }; private: BrainConstants(); ~BrainConstants(); BrainConstants(const BrainConstants&); BrainConstants& operator=(const BrainConstants&); }; #ifdef __BRAIN_CONSTANTS_DECLARE__ #endif // __BRAIN_CONSTANTS_DECLARE__ } // namespace #endif //__BRAIN_CONSTANTS__H_ connectome-workbench-1.2.3+git41-gc4c6c90/src/Common/ByteOrderEnum.cxx000066400000000000000000000107521300200146000252510ustar00rootroot00000000000000/*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 __BYTE_ORDER_DECLARE__ #include "ByteOrderEnum.h" #undef __BYTE_ORDER_DECLARE__ using namespace caret; /** * Constructor. * * @param e * An enumerated value. * @param name * Name of enumberated value. */ ByteOrderEnum::ByteOrderEnum( const Enum e, const AString& name) { this->e = e; this->name = name; } /** * Destructor. */ ByteOrderEnum::~ByteOrderEnum() { } /** * Get the system byte order. * @return * Byte order of the system. */ ByteOrderEnum::Enum ByteOrderEnum::getSystemEndian() { ByteOrderEnum::initialize(); return ByteOrderEnum::systemEndian; } /** * Is the system byte order little endian? * @return * true if system is little endian byte order. */ bool ByteOrderEnum::isSystemLittleEndian() { ByteOrderEnum::initialize(); return (ByteOrderEnum::systemEndian == ENDIAN_LITTLE); } /** * Is the system byte order big endian? * @return * true if system is big endian byte order. */ bool ByteOrderEnum::isSystemBigEndian() { ByteOrderEnum::initialize(); return (ByteOrderEnum::systemEndian == ENDIAN_BIG); } void ByteOrderEnum::initialize() { if (initializedFlag) { return; } initializedFlag = true; enumData.push_back(ByteOrderEnum(ENDIAN_BIG,"ENDIAN_BIG")); enumData.push_back(ByteOrderEnum(ENDIAN_LITTLE,"ENDIAN_LITTLE")); uint32_t intVal = 0x00000001; unsigned char* c = (unsigned char*)&intVal; ByteOrderEnum::systemEndian = ByteOrderEnum::ENDIAN_BIG; if (*c == 0x01) systemEndian = ByteOrderEnum::ENDIAN_LITTLE; } /** * Get the enum value for this enumerated item. * @return the value for this enumerated item. */ ByteOrderEnum::Enum ByteOrderEnum::getEnum() const { return this->e; } /** * Get the enum name for this enumerated item. * @return the name for this enumerated item. */ AString ByteOrderEnum::getName() const { return this->name; } /** * Find the data for and enumerated value. * @param e * The enumerated value. * @return Pointer to data for this enumerated type * or NULL if no data for type or if type is invalid. */ const ByteOrderEnum* ByteOrderEnum::findData(const Enum e) { initialize(); int64_t num = enumData.size(); for (int64_t i = 0; i < num; i++) { const ByteOrderEnum* d = &enumData[i]; if (d->e == e) { return d; } } return NULL; } /** * Get a string representation of the enumerated type. * @param e * Enumerated value. * @return * String representing enumerated value. */ AString ByteOrderEnum::toName(Enum e) { initialize(); AString s; for (std::vector::iterator iter = enumData.begin(); iter != enumData.end(); iter++) { const ByteOrderEnum& d = *iter; if (d.e == e) { s = d.name; break; } } return s; } /** * Get an enumerated value corresponding to its 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. */ ByteOrderEnum::Enum ByteOrderEnum::fromName(const AString& s, bool* isValidOut) { initialize(); bool validFlag = false; Enum e = ENDIAN_LITTLE; for (std::vector::iterator iter = enumData.begin(); iter != enumData.end(); iter++) { const ByteOrderEnum& d = *iter; if (d.name == s) { e = d.e; validFlag = true; break; } } if (isValidOut != 0) { *isValidOut = validFlag; } return e; } connectome-workbench-1.2.3+git41-gc4c6c90/src/Common/ByteOrderEnum.h000066400000000000000000000040051300200146000246700ustar00rootroot00000000000000#ifndef __BYTE_ORDER_H__ #define __BYTE_ORDER_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 namespace caret { /** * Byte order. */ class ByteOrderEnum { public: /** ENDIAN Types */ enum Enum { /** */ ENDIAN_BIG, /** */ ENDIAN_LITTLE }; ~ByteOrderEnum(); static ByteOrderEnum::Enum getSystemEndian(); static bool isSystemLittleEndian(); static bool isSystemBigEndian(); static AString toName(Enum e); static Enum fromName(const AString& s, bool* isValidOut); private: ByteOrderEnum(const Enum e, const AString& name); Enum getEnum() const; AString getName() const; static std::vector enumData; static void initialize(); static bool initializedFlag; static Enum systemEndian; Enum e; AString name; static const ByteOrderEnum* findData(const Enum e); }; #ifdef __BYTE_ORDER_DECLARE__ std::vector ByteOrderEnum::enumData; bool ByteOrderEnum::initializedFlag = false; ByteOrderEnum::Enum ByteOrderEnum::systemEndian; #endif // __BYTE_ORDER_DECLARE__ } // namespace #endif // __BYTE_ORDER_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Common/ByteSwapping.cxx000066400000000000000000000055101300200146000251350ustar00rootroot00000000000000/*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 "ByteSwapping.h" using namespace caret; /** * Swap bytes for the specified type. */ void ByteSwapping::swapBytes(int16_t* n, const uint64_t numToSwap) { for (uint64_t i = 0; i < numToSwap; i++) { char* bytes = (char*)&n[i]; char temp = bytes[0]; bytes[0] = bytes[1]; bytes[1] = temp; } } /** * Swap bytes for the specified type. */ void ByteSwapping::swapBytes(uint16_t* n, const uint64_t numToSwap) { swapBytes((int16_t*)n, numToSwap); } /** * Swap bytes for the specified type. */ void ByteSwapping::swapBytes(int32_t* n, const uint64_t numToSwap) { for (uint64_t i = 0; i < numToSwap; i++) { char* bytes = (char*)&n[i]; char temp = bytes[0]; bytes[0] = bytes[3]; bytes[3] = temp; temp = bytes[1]; bytes[1] = bytes[2]; bytes[2] = temp; } } /** * */ void ByteSwapping::swapBytes(uint32_t* n, const uint64_t numToSwap) { swapBytes((int32_t*)n, numToSwap); } /** * Swap bytes for the specified type. */ void ByteSwapping::swapBytes(int64_t* n, const uint64_t numToSwap) { for (uint64_t i = 0; i < numToSwap; i++) { char* bytes = (char*)&n[i]; char temp = bytes[0]; bytes[0] = bytes[7]; bytes[7] = temp; temp = bytes[1]; bytes[1] = bytes[6]; bytes[6] = temp; temp = bytes[2]; bytes[2] = bytes[5]; bytes[5] = temp; temp = bytes[3]; bytes[3] = bytes[4]; bytes[4] = temp; } } /** * Swap bytes for the specified type. */ void ByteSwapping::swapBytes(uint64_t* n, const uint64_t numToSwap) { swapBytes((int64_t*)n, numToSwap); } /** * Swap bytes for the specified type. */ void ByteSwapping::swapBytes(float* n, const uint64_t numToSwap) { swapBytes((int32_t*)n, numToSwap); } /** * Swap bytes for the specified type. */ void ByteSwapping::swapBytes(double* n, const uint64_t numToSwap) { swapBytes((int64_t*)n, numToSwap); } void ByteSwapping::swapBytes(long double* n, const uint64_t numToSwap) { swapArray(n, numToSwap); } connectome-workbench-1.2.3+git41-gc4c6c90/src/Common/ByteSwapping.h000066400000000000000000000057251300200146000245720ustar00rootroot00000000000000#ifndef __BYTE_SWAPPING_H__ #define __BYTE_SWAPPING_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 namespace caret { /** * This class contains static methods for byte swapping data, typically used * when reading binary data files. */ class ByteSwapping { private: ByteSwapping() { } ~ByteSwapping() { } public: static void swapBytes(uint8_t*, const uint64_t) { }//define them for completeness, so templated stuff can just call it without specializing static void swapBytes(int8_t*, const uint64_t) { } static void swapBytes(uint16_t* n, const uint64_t numToSwap); static void swapBytes(int16_t* n, const uint64_t numToSwap); static void swapBytes(int32_t* n, const uint64_t numToSwap); static void swapBytes(uint32_t* n, const uint64_t numToSwap); static void swapBytes(int64_t* n, const uint64_t numToSwap); static void swapBytes(uint64_t* n, const uint64_t numToSwap); static void swapBytes(float* n, const uint64_t numToSwap); static void swapBytes(double* n, const uint64_t numToSwap); static void swapBytes(long double* n, const uint64_t numToSwap); template static void swap(T& toSwap);//templated versions, to replace hand-coding variants template static void swapArray(T* toSwap, const uint64_t& count); inline static bool isBigEndian() { uint16_t test = 1; return (((char*)&test)[0] == 0); } }; template void ByteSwapping::swap(T& toSwap) { if (sizeof(T) == 1) return;//we could specialize 1-byte types, but this should optimize out T temp = toSwap; char* from = (char*)&temp; char* to = (char*)&toSwap; for (int i = 0; i < (int)sizeof(T); ++i) { to[i] = from[sizeof(T) - i - 1]; } } template void ByteSwapping::swapArray(T* toSwap, const uint64_t& count) { if (sizeof(T) == 1) return;//ditto for (uint64_t i = 0; i < count; ++i) { swap(toSwap[i]); } } } #endif // __BYTE_SWAPPING_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Common/CMakeLists.txt000066400000000000000000000140621300200146000245370ustar00rootroot00000000000000 # # Name of Project # PROJECT(Common) # # QT include files # SET(QT_USE_QTXML TRUE) SET(QT_DONT_USE_QTGUI TRUE) SET(QT_USE_QTNETWORK TRUE) INCLUDE(${QT_USE_FILE}) #message("QT VERSION: " ${QT_VERSION_MAJOR} ${QT_VERSION_MINOR} ${QT_VERSION_PATCH}) SET(MOC_INPUT_HEADER_FILES CaretHttpManager.h ) QT4_WRAP_CPP(MOC_SOURCE_FILES ${MOC_INPUT_HEADER_FILES}) # # Create a library # ADD_LIBRARY(Common ApplicationInformation.h ApplicationTypeEnum.h AString.h AStringNaturalComparison.h BackgroundAndForegroundColors.h BackgroundAndForegroundColorsModeEnum.h Base64.h BoundingBox.h BrainConstants.h ByteOrderEnum.h ByteSwapping.h CaretAssert.h CaretAssertion.h CaretBinaryFile.h CaretColorEnum.h CaretCommandLine.h CaretCompact3DLookup.h CaretCompactLookup.h CaretException.h CaretFunctionName.h CaretHeap.h CaretHttpManager.h CaretJsonObject.h CaretLogger.h CaretMathExpression.h CaretMutex.h CaretObject.h CaretObjectTracksModification.h CaretOMP.h CaretPointer.h CaretPointLocator.h CaretPreferences.h CaretTemporaryFile.h CaretUndoCommand.h CaretUndoStack.h CubicSpline.h DataCompressZLib.h DataFile.h DataFileContentCopyMoveInterface.h DataFileContentCopyMoveParameters.h DataFileContentInformation.h DataFileException.h DataFileInterface.h DataFileTypeEnum.h DescriptiveStatistics.h DeveloperFlagsEnum.h DisplayGroupAndTabItemInterface.h DisplayGroupEnum.h DrawnWithOpenGLTextureInfo.h DrawnWithOpenGLTextureInterface.h ElapsedTimer.h Event.h EventAlertUser.h EventBrowserTabIndicesGetAll.h EventGetViewportSize.h EventListenerInterface.h EventManager.h EventPaletteGetByName.h EventProgressUpdate.h EventOpenGLTexture.h EventTypeEnum.h FastStatistics.h FileAdapter.h FileInformation.h FloatMatrix.h Histogram.h HtmlStringBuilder.h ImageCaptureMethodEnum.h Logger.h LogHandler.h LogHandlerStandardError.h LogLevelEnum.h LogManager.h LogRecord.h MathFunctionEnum.h MathFunctions.h MatrixFunctions.h ModelTransform.h MultiDimArray.h MultiDimIterator.h NetworkException.h NumericFormatModeEnum.h NumericTextFormatting.h OctTree.h OpenGLDrawingMethodEnum.h PlainTextStringBuilder.h Plane.h ProgramParameters.h ProgramParametersException.h ProgressObject.h ProgressReportingInterface.h ReductionEnum.h ReductionOperation.h SpecFileDialogViewFilesTypeEnum.h SpeciesEnum.h StereotaxicSpaceEnum.h StringTableModel.h StructureEnum.h SystemUtilities.h TileTabsConfiguration.h TracksModificationInterface.h TriStateSelectionStatusEnum.h Vector3D.h VectorOperation.h VoxelIJK.h YokingGroupEnum.h ${MOC_SOURCE_FILES} ${CMAKE_BINARY_DIR}/Common/ApplicationInformation.cxx ApplicationTypeEnum.cxx AString.cxx AStringNaturalComparison.cxx BackgroundAndForegroundColors.cxx BackgroundAndForegroundColorsModeEnum.cxx Base64.cxx BoundingBox.cxx BrainConstants.cxx ByteOrderEnum.cxx ByteSwapping.cxx CaretAssertion.cxx CaretBinaryFile.cxx CaretColorEnum.cxx CaretCommandLine.cxx CaretException.cxx CaretHttpManager.cxx CaretJsonObject.cxx CaretLogger.cxx CaretMathExpression.cxx CaretObject.cxx CaretObjectTracksModification.cxx CaretPointLocator.cxx CaretPreferences.cxx CaretTemporaryFile.cxx CaretUndoCommand.cxx CaretUndoStack.cxx CubicSpline.cxx DataCompressZLib.cxx DataFile.cxx DataFileContentCopyMoveParameters.cxx DataFileContentInformation.cxx DataFileException.cxx DataFileTypeEnum.cxx DescriptiveStatistics.cxx DeveloperFlagsEnum.cxx DisplayGroupAndTabItemInterface.cxx DisplayGroupEnum.cxx DrawnWithOpenGLTextureInfo.cxx ElapsedTimer.cxx Event.cxx EventAlertUser.cxx EventBrowserTabIndicesGetAll.cxx EventGetViewportSize.cxx EventManager.cxx EventPaletteGetByName.cxx EventProgressUpdate.cxx EventOpenGLTexture.cxx EventTypeEnum.cxx FastStatistics.cxx FileAdapter.cxx FileInformation.cxx FloatMatrix.cxx Histogram.cxx HtmlStringBuilder.cxx ImageCaptureMethodEnum.cxx Logger.cxx LogHandler.cxx LogHandlerStandardError.cxx LogLevelEnum.cxx LogManager.cxx LogRecord.cxx MathFunctionEnum.cxx MathFunctions.cxx ModelTransform.cxx NetworkException.cxx NumericFormatModeEnum.cxx NumericTextFormatting.cxx OpenGLDrawingMethodEnum.cxx PlainTextStringBuilder.cxx Plane.cxx ProgramParameters.cxx ProgramParametersException.cxx ProgressObject.cxx ReductionEnum.cxx ReductionOperation.cxx SpecFileDialogViewFilesTypeEnum.cxx SpeciesEnum.cxx StereotaxicSpaceEnum.cxx StringTableModel.cxx StructureEnum.cxx SystemUtilities.cxx TileTabsConfiguration.cxx TriStateSelectionStatusEnum.cxx Vector3D.cxx VectorOperation.cxx YokingGroupEnum.cxx ) ADD_DEFINITIONS(-DCOMPILER_NAME="${CMAKE_CXX_COMPILER}") ADD_DEFINITIONS(-DCOMPILER_VERSION="${CMAKE_CXX_COMPILER_VERSION}") # # Include directories # INCLUDE_DIRECTORIES( ${CMAKE_SOURCE_DIR}/Common ) SET (GIT_REPOSITORY "${CMAKE_SOURCE_DIR}/../.git") IF (EXISTS ${GIT_REPOSITORY}) ADD_CUSTOM_COMMAND( OUTPUT ${CMAKE_BINARY_DIR}/Common/ApplicationInformation.cxx COMMAND ${CMAKE_COMMAND} -DINFILE="${CMAKE_SOURCE_DIR}/Common/ApplicationInformation.cxx.in" -DOUTFILE="${CMAKE_BINARY_DIR}/Common/ApplicationInformation.cxx" -P "${CMAKE_SOURCE_DIR}/CMakeScripts/git_commit_info.cmake.in" WORKING_DIRECTORY ${GIT_REPOSITORY}/.. DEPENDS ApplicationInformation.cxx.in ${CMAKE_SOURCE_DIR}/CMakeScripts/git_commit_info.cmake.in ${GIT_REPOSITORY}/HEAD ${GIT_REPOSITORY}/index COMMENT "Setting commit info" ) ELSE(EXISTS ${GIT_REPOSITORY}) ADD_CUSTOM_COMMAND( OUTPUT ${CMAKE_BINARY_DIR}/Common/ApplicationInformation.cxx COMMAND ${CMAKE_COMMAND} -DINFILE="${CMAKE_SOURCE_DIR}/Common/ApplicationInformation.cxx.in" -DOUTFILE="${CMAKE_BINARY_DIR}/Common/ApplicationInformation.cxx" -P "${CMAKE_SOURCE_DIR}/CMakeScripts/git_commit_info.cmake.in" WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}/.. DEPENDS ApplicationInformation.cxx.in ${CMAKE_SOURCE_DIR}/CMakeScripts/git_commit_info.cmake.in COMMENT "No repository found, setting commit info to 'unknown'. Reconfigure to look for the repository again." ) 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() connectome-workbench-1.2.3+git41-gc4c6c90/src/Common/CaretAssert.h000066400000000000000000000114231300200146000243660ustar00rootroot00000000000000#ifndef __CARET_ASSERT_H__ #define __CARET_ASSERT_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 "CaretAssertion.h" #ifdef NDEBUG #define CaretAssert(e) ((void)0) #define CaretAssertToDoWarning() ((void)0) #define CaretAssertToDoFatal() ((void)0) #define CaretAssertMessage(e, m) ((void)0) #define CaretAssertArrayIndex(a, n, i) ((void) 0) #define CaretAssertVectorIndex(v, i) ((void) 0) #else // NDEBUG /** * \def CaretAssert * * If the expression evaluates to zero, a message is printed * showing the file its line number. A call stack may also * be printed. Lastly, abort() is called. * @param e * Expression that is tested. */ #define CaretAssert(e) \ (((e) == 0) \ ? caret::CaretAssertion::assertFailed(#e, __FILE__, __LINE__) \ : (void)0) /** * \def CaretAssertToDoWarning * * Its purpose is to add "easy to find" reminders in the code * for items that will function without crashing but still * need some additional work. It is expected * CaretAssertToDoWarning will rarely, if ever, be in code that * is pushed to the main repository. * * A message is printed showing the file and its line number. * A call stack may also be printed. Does not call abort(). */ #define CaretAssertToDoWarning() \ caret::CaretAssertion::assertToDoWarning(__FILE__, __LINE__) /** * \def CaretAssertToDoFatal * * Its purpose is to add "easy to find" reminders in the code * for items that are unlikely to be encountered and that * will likely crash or cause significant errors * in the functionality. It is expected * CaretAssertToDoFatal will rarely, if ever, be in code that * is pushed to the main repository. * * A message is printed showing the file and its line number. * A call stack may also be printed. WILL CALL abort() * and terminate execution. */ #define CaretAssertToDoFatal() \ caret::CaretAssertion::assertToDoFatal(__FILE__, __LINE__) /** * \def CaretAssertMessage * * If the expression evaluates to zero, a message is printed * showing the file its line number. The users message is * then printed. A call stack may also * be printed. Lastly, abort() is called. * @param e * Expression that is tested. * @param m * Message that is printed. */ #define CaretAssertMessage(e, m) \ (((e) == 0) \ ? caret::CaretAssertion::assertFailed(#e, AString(m), __FILE__, __LINE__) \ : (void)0) /** * \def CaretAssertArrayIndex * * If the array index is out of bounds, a message is printed * listing the array name, the arrays number of elements, the * invalid 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 array variable. * @param n * Number of elements in the array. * @param i * The index into the array. */ #define CaretAssertArrayIndex(a, n, i) \ ((((i) < 0) || ((i) >= (n))) \ ? caret::CaretAssertion::assertArrayIndexFailed(#a, n, i, __FILE__, __LINE__) \ : (void)0) /** * \def CaretAssertVectorIndex * * If the vector index is out of bounds, a message is printed * listing the vector name, the vector's number of elements, the * invalid vector 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 v * The vector. * @param i * The index into the vector. */ #define CaretAssertVectorIndex(v, i) \ ((((i) < 0) || ((i) >= (static_cast(v.size())))) \ ? caret::CaretAssertion::assertVectorIndexFailed(#v, (static_cast(v.size())), i, __FILE__, __LINE__) \ : (void)0) #endif // NDEBUG #endif // __CARET_ASSERT_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Common/CaretAssertion.cxx000066400000000000000000000310501300200146000254450ustar00rootroot00000000000000/*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 #define __CARET_ASSERTION_DEFINE__ #include "CaretAssertion.h" #undef __CARET_ASSERTION_DEFINE__ #include "CaretAssert.h" #include "SystemUtilities.h" using namespace caret; /** * Called when an assertion has failed. * The following events will occur: * Prints the expression that failed, the name of * file, and the line number in the file. If * a callstack (backtrace) is available, it will * also be printed. Lastly, abort() is called. * * * @param expression * Expression that failed assertion testing. * @param filename * Name of file in which assertion failed. * @param lineNumber * Line number where assertion failed. */ void CaretAssertion::assertFailed(const char* expression, const char* filename, const int64_t lineNumber) { CaretAssertion::assertFailed(expression, NULL, filename, lineNumber); } /** * Called for a CaretAssertToDoWarning() * * The following events will occur: * Prints the name of * file, and the line number in the file. If * a callstack (backtrace) is available, it will * also be printed. DOES NOT call abort. * * * @param filename * Name of file in which assertion failed. * @param lineNumber * Line number where assertion failed. */ void CaretAssertion::assertToDoWarning(const char* filename, const int64_t lineNumber) { std::cerr \ << "CaretAssertToDo WARNING" << std::endl << "File: " << filename << std::endl << "Line number: " << lineNumber << std::endl << std::endl; const AString s = SystemUtilities::getBackTrace(); if (s.isEmpty() == false) { std::cerr << s << std::endl << std::endl; } } /** * Called for a CaretAssertToDoFatal() * * The following events will occur: * Prints the name of * file, and the line number in the file. If * a callstack (backtrace) is available, it will * also be printed. Does call abort. * * * @param filename * Name of file in which assertion failed. * @param lineNumber * Line number where assertion failed. */ void CaretAssertion::assertToDoFatal(const char* filename, const int64_t lineNumber) { std::cerr \ << "CaretAssertToDo FATAL" << std::endl << "File: " << filename << std::endl << "Line number: " << lineNumber << 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(); } } /** * Called when an assertion has failed. * The following events will occur: * Prints the expression that failed, the name of * file, and the line number in the file. If * a callstack (backtrace) is available, it will * also be printed. Lastly, abort() is called. * * * @param expression * Expression that failed assertion testing. * @param message * Message that is printed. * @param filename * Name of file in which assertion failed. * @param lineNumber * Line number where assertion failed. */ void CaretAssertion::assertFailed(const char* expression, const char* message, const char* filename, const int64_t lineNumber) { std::cerr \ << "Assertion Failure of expression \"" << expression << "\"" << std::endl << "File: " << filename << std::endl << "Line number: " << lineNumber << std::endl << std::endl; if (message != NULL) { std::cerr << "Message: " << message << 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(); } } /** * Called when an 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::assertArrayIndexFailed(const char* arrayName, const int64_t arrayNumberOfElements, const int64_t arrayIndex, const char* filename, const int64_t lineNumber) { std::cerr \ << "Assertion of 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(); } } /** * Called when an vector index assertion has failed. * The following events will occur: * Prints the name of the vector, the number of * elements in the vector, the invalid vector 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 vectorName * Name of the vector. * @param vectorNumberOfElements * Number of elements in the vector. * @param vectorIndex * Invalid vector index. * @param filename * Name of file in which assertion failed. * @param lineNumber * Line number where assertion failed. */ void CaretAssertion::assertVectorIndexFailed(const char* vectorName, const int64_t vectorNumberOfElements, const int64_t vectorIndex, const char* filename, const int64_t lineNumber) { std::cerr \ << "Assertion of Vector Bounds failed for vector: " << vectorName << std::endl << "File: " << filename << std::endl << "Line number: " << lineNumber << std::endl; std::cerr << "Index: " << vectorIndex << std::endl << "Number of elements in vector: " << vectorNumberOfElements << 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. * * @param stream * Stream to which messages are written. * @param isVerbose * Print detailed messages. */ void CaretAssertion::unitTest(std::ostream& stream, const bool isVerbose) { #ifdef NDEBUG if (isVerbose) { stream << "Unit testing of CaretAssertion will not take place since software is not compiled with debug on." << std::endl; } return; #else CaretAssertion::unitTestFlag = true; stream << "CaretAssertion::unitTest is starting" << std::endl; /* * Redirect std::err to the string stream. */ std::ostringstream str; std::streambuf* cerrSave = std::cerr.rdbuf(); std::cerr.rdbuf(str.rdbuf()); int32_t zero = 0; int32_t one = 1; CaretAssert(zero); CaretAssertion::unitTestHelper(stream, "Assert Zero", str.str(), false, isVerbose); str.str(""); CaretAssert(one); CaretAssertion::unitTestHelper(stream, "Assert One", str.str(), true, isVerbose); str.str(""); CaretAssertMessage(zero, "This test should fail along with this message being printed."); CaretAssertion::unitTestHelper(stream, "Assert Message Zero", str.str(), false, isVerbose); str.str(""); CaretAssertMessage(one, "This test SHOULD NOT fail."); CaretAssertion::unitTestHelper(stream, "Assert Message One", str.str(), true, isVerbose); str.str(""); /*int32_t someArray[] = { 1, 2, 3 }; someArray[1] = 2;//*/ CaretAssertArrayIndex(someArray, 3, -1); CaretAssertion::unitTestHelper(stream, "Assert Array Index -1", str.str(), false, isVerbose); str.str(""); CaretAssertArrayIndex(someArray, 3, 3); CaretAssertion::unitTestHelper(stream, "Assert Array Index 3", str.str(), false, isVerbose); str.str(""); CaretAssertArrayIndex(someArray, 3, 5); CaretAssertion::unitTestHelper(stream, "Assert Array Index 5", str.str(), false, isVerbose); str.str(""); CaretAssertArrayIndex(someArray, 3, 2); CaretAssertion::unitTestHelper(stream, "Assert Aray Index 2", str.str(), true, isVerbose); str.str(""); std::vector someVector; someVector.push_back(1); someVector.push_back(2); CaretAssertVectorIndex(someVector, 1); CaretAssertion::unitTestHelper(stream, "Assert Vector Index 1", str.str(), true, isVerbose); str.str(""); CaretAssertVectorIndex(someVector, -1); // Yes, do get signed/unsigned warning CaretAssertion::unitTestHelper(stream, "Assert Vector Index -1", str.str(), false, isVerbose); str.str(""); CaretAssertVectorIndex(someVector, 2); CaretAssertion::unitTestHelper(stream, "Assert Vector Index 2", str.str(), false, isVerbose); str.str(""); /* * Restore std::err */ std::cerr.rdbuf(cerrSave); stream << "CaretAssertion::unitTest has ended" << std::endl << std::endl;; CaretAssertion::unitTestFlag = false; #endif } void CaretAssertion::unitTestHelper(std::ostream& stream, const std::string& testName, const std::string& testOutput, const bool correctTestStatus, const bool isVerbose) { /* * Should test pass? */ if (correctTestStatus) { if (testOutput.empty() == false) { stream << "ERROR: CaretAssertion unit test failed but should have passed. " << std::endl << "Test Name: " << testName << std::endl << "Test Output: " << testOutput << std::endl << std::endl; } else if (isVerbose) { stream << "Test " << testName << " functioned correclty (passed)." << std::endl; } } else { if (testOutput.empty()) { stream << "ERROR: CaretAssertion unit test passed but should have failed." << std::endl << "Test Name: " << testName << std::endl << std::endl; } else if (isVerbose) { stream << "Test " << testName << " functioned correctly (failed)." << std::endl; } } } connectome-workbench-1.2.3+git41-gc4c6c90/src/Common/CaretAssertion.h000066400000000000000000000062271300200146000251020ustar00rootroot00000000000000#ifndef __CARET_ASSERTION_H__ #define __CARET_ASSERTION_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 namespace caret { /** * Contains static methods for assertion processing. * * DO NOT USE THIS CLASS!!!! * USE THE MACROS DEFINED IN CaretAssert.h */ class CaretAssertion { private: CaretAssertion(); CaretAssertion(const CaretAssertion& o); CaretAssertion& operator=(const CaretAssertion&); ~CaretAssertion(); public: static void assertFailed(const char* expression, const char* filename, const int64_t lineNumber); static void assertFailed(const char* expression, const char* message, const char* filename, const int64_t lineNumber); static void assertToDoWarning(const char* filename, const int64_t lineNumber); static void assertToDoFatal(const char* filename, const int64_t lineNumber); static void assertArrayIndexFailed(const char* arrayName, const int64_t arrayNumberOfElements, const int64_t arrayIndex, const char* filename, const int64_t lineNumber); static void assertVectorIndexFailed(const char* vectorName, const int64_t vectorNumberOfElements, const int64_t vectorIndex, const char* filename, const int64_t lineNumber); static void unitTest(std::ostream& stream, const bool isVerbose); private: static void unitTestHelper(std::ostream& stream, const std::string& testOutput, const std::string& testName, const bool correctTestStatus, const bool isVerbose); static bool unitTestFlag; }; #ifdef __CARET_ASSERTION_DEFINE__ bool CaretAssertion::unitTestFlag = false; #endif // __CARET_ASSERTION_DEFINE__ } // namespace #endif // __CARET_ASSERTION_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Common/CaretBinaryFile.cxx000066400000000000000000000302431300200146000255250ustar00rootroot00000000000000/*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*/ //try to force large file support from zlib, any other file reading calls #ifndef CARET_OS_MACOSX #define _LARGEFILE64_SOURCE #define _LFS64_LARGEFILE 1 #define _FILE_OFFSET_BITS 64 #endif #include "CaretAssert.h" #include "CaretBinaryFile.h" #include "CaretLogger.h" #include "DataFileException.h" #include #include "zlib.h" #include using namespace caret; using namespace std; //private implementation classes namespace caret { #ifdef ZLIB_VERSION class ZFileImpl : public CaretBinaryFile::ImplInterface { gzFile m_zfile; const static int64_t CHUNK_SIZE; public: ZFileImpl() { m_zfile = NULL; } void open(const QString& filename, const CaretBinaryFile::OpenMode& opmode); void close(); void seek(const int64_t& position); int64_t pos(); void read(void* dataOut, const int64_t& count, int64_t* numRead); void write(const void* dataIn, const int64_t& count); ~ZFileImpl(); }; const int64_t ZFileImpl::CHUNK_SIZE = 1<<26;//64MiB, large enough for good performance, small enough for zlib, must convert to uint32 #endif //ZLIB_VERSION class QFileImpl : public CaretBinaryFile::ImplInterface { QFile m_file; const static int64_t CHUNK_SIZE; public: void open(const QString& filename, const CaretBinaryFile::OpenMode& opmode); void close(); void seek(const int64_t& position); int64_t pos(); void read(void* dataOut, const int64_t& count, int64_t* numRead); void write(const void* dataIn, const int64_t& count); }; const int64_t QFileImpl::CHUNK_SIZE = 1<<30;//1GiB, QT4 apparently chokes at more than 2GiB via buffer.read using int32 } CaretBinaryFile::ImplInterface::~ImplInterface() { } CaretBinaryFile::CaretBinaryFile(const QString& filename, const OpenMode& fileMode) { open(filename, fileMode); } void CaretBinaryFile::close() { m_curMode = NONE; if (m_impl == NULL) return; m_impl->close(); m_impl.grabNew(NULL); } QString CaretBinaryFile::getFilename() const { if (m_impl == NULL) return "";//don't throw, its not really a problem return m_impl->getFilename(); } bool CaretBinaryFile::getOpenForRead() { return (m_curMode | READ) != 0; } bool CaretBinaryFile::getOpenForWrite() { return (m_curMode | WRITE) != 0; } void CaretBinaryFile::open(const QString& filename, const OpenMode& opmode) { close(); if (opmode == NONE) throw DataFileException("can't open file with NONE mode"); if (filename.endsWith(".gz")) { #ifdef ZLIB_VERSION m_impl.grabNew(new ZFileImpl()); #else //ZLIB_VERSION throw DataFileException("can't open .gz file '" + filename + "', compiled without zlib support"); #endif //ZLIB_VERSION } else { m_impl.grabNew(new QFileImpl()); } m_impl->open(filename, opmode); m_curMode = opmode; } void CaretBinaryFile::read(void* dataOut, const int64_t& count, int64_t* numRead) { CaretAssert(count >= 0);//not sure about allowing 0 if (!getOpenForRead()) throw DataFileException("file is not open for reading"); m_impl->read(dataOut, count, numRead); } void CaretBinaryFile::seek(const int64_t& position) { CaretAssert(position >= 0); if (m_curMode == NONE) throw DataFileException("file is not open, can't seek"); m_impl->seek(position); } int64_t CaretBinaryFile::pos() { if (m_curMode == NONE) throw DataFileException("file is not open, can't report position"); return m_impl->pos(); } void CaretBinaryFile::write(const void* dataIn, const int64_t& count) { CaretAssert(count >= 0);//not sure about allowing 0 if (!getOpenForWrite()) throw DataFileException("file is not open for writing"); m_impl->write(dataIn, count); } #ifdef ZLIB_VERSION void ZFileImpl::open(const QString& filename, const CaretBinaryFile::OpenMode& opmode) { close();//don't need to, but just because m_fileName = filename; const char* mode = NULL; switch (opmode)//we only support a limited number of combinations, and the string modes are quirky { case CaretBinaryFile::READ: mode = "rb"; break; case CaretBinaryFile::WRITE_TRUNCATE: mode = "wb";//you have to do "r+b" in order to ask it to not truncate, which zlib doesn't support anyway break; default: throw DataFileException("compressed file only supports READ and WRITE_TRUNCATE modes"); } #if !defined(CARET_OS_MACOSX) && ZLIB_VERNUM > 0x1232 m_zfile = gzopen64(filename.toLocal8Bit().constData(), mode); #else m_zfile = gzopen(filename.toLocal8Bit().constData(), mode); #endif if (m_zfile == NULL) { if (!QFile::exists(filename)) { if (!(opmode & CaretBinaryFile::TRUNCATE)) { throw DataFileException("failed to open compressed file '" + filename + "', file does not exist, or folder permissions prevent seeing it"); } else {//use same logic as QFile impl for now throw DataFileException("failed to open compressed file '" + filename + "', unable to create file"); } }//TODO: check gzerror and errno for more informative error messages throw DataFileException("failed to open compressed file '" + filename + "'"); } } void ZFileImpl::close() { if (m_zfile == NULL) return;//happens when closed and then destroyed, error opening if (gzclose(m_zfile) != 0) throw DataFileException("error closing compressed file '" + m_fileName + "'"); m_zfile = NULL; } void ZFileImpl::read(void* dataOut, const int64_t& count, int64_t* numRead) { if (m_zfile == NULL) throw DataFileException("read called on unopened ZFileImpl");//shouldn't happen int64_t totalRead = 0; int readret = 0;//to preserve the info of the read that broke early while (totalRead < count) { int64_t iterSize = min(count - totalRead, CHUNK_SIZE); readret = gzread(m_zfile, ((char*)dataOut) + totalRead, iterSize); if (readret < 1) break;//0 or -1 indicate eof or error totalRead += readret; } if (numRead == NULL) { if (totalRead != count) { if (readret < 0) throw DataFileException("error while reading compressed file '" + m_fileName + "'"); throw DataFileException("premature end of file in compressed file '" + m_fileName + "'"); } } else { *numRead = totalRead; } } void ZFileImpl::seek(const int64_t& position) { if (m_zfile == NULL) throw DataFileException("seek called on unopened ZFileImpl");//shouldn't happen if (pos() == position) return;//slight hack, since gzseek is slow or nonfunctional for some cases, so don't try it unless necessary #if !defined(CARET_OS_MACOSX) && ZLIB_VERNUM > 0x1232 int64_t ret = gzseek64(m_zfile, position, SEEK_SET); #else int64_t ret = gzseek(m_zfile, position, SEEK_SET); #endif if (ret != position) throw DataFileException("seek failed in compressed file '" + m_fileName + "'"); } int64_t ZFileImpl::pos() { if (m_zfile == NULL) throw DataFileException("pos called on unopened ZFileImpl");//shouldn't happen #if !defined(CARET_OS_MACOSX) && ZLIB_VERNUM > 0x1232 return gztell64(m_zfile); #else return gztell(m_zfile); #endif } void ZFileImpl::write(const void* dataIn, const int64_t& count) { if (m_zfile == NULL) throw DataFileException("read called on unopened ZFileImpl");//shouldn't happen int64_t totalWritten = 0; while (totalWritten < count) { int64_t iterSize = min(count - totalWritten, CHUNK_SIZE); int writeret = gzwrite(m_zfile, ((const char*)dataIn) + totalWritten, iterSize); if (writeret < 1) break;//0 or -1 indicate eof or error totalWritten += writeret; } if (totalWritten != count) throw DataFileException("failed to write to compressed file '" + m_fileName + "'"); } ZFileImpl::~ZFileImpl() { try//throwing from a destructor is a bad idea { close(); } catch (CaretException& e) {//handles DataFileException, should be the only culprit CaretLogSevere(e.whatString()); } catch (exception& e) { CaretLogSevere(e.what()); } catch (...) { CaretLogSevere("caught unknown exception type while closing a compressed file"); } } #endif //ZLIB_VERSION void QFileImpl::open(const QString& filename, const CaretBinaryFile::OpenMode& opmode) { close();//don't need to, but just because m_fileName = filename; QIODevice::OpenMode mode = QIODevice::NotOpen;//means 0 if (opmode & CaretBinaryFile::READ) mode |= QIODevice::ReadOnly; if (opmode & CaretBinaryFile::WRITE) mode |= QIODevice::WriteOnly; if (opmode & CaretBinaryFile::TRUNCATE) mode |= QIODevice::Truncate;//expect QFile to recognize silliness like TRUNCATE by itself m_file.setFileName(filename); if (!m_file.open(mode)) { if (!m_file.exists()) { if (!(opmode & CaretBinaryFile::TRUNCATE)) { throw DataFileException("failed to open file '" + filename + "', file does not exist, or folder permissions prevent seeing it"); } else {//m_file.error() doesn't help identify this case, see below throw DataFileException("failed to open file '" + filename + "', unable to create file"); } } 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"); default: throw DataFileException("failed to open file '" + filename + "'"); } } } void QFileImpl::close() { m_file.close(); } void QFileImpl::read(void* dataOut, const int64_t& count, int64_t* numRead) { int64_t total = 0; int64_t readret = -1; while (total < count) { int64_t maxToRead = min(count - total, CHUNK_SIZE); readret = m_file.read(((char*)dataOut) + total, maxToRead);//QFile chokes on large reads also if (readret < 1) break;//0 or -1 means error or eof total += readret; } if (numRead == NULL) { if (total != count) { if (readret < 0) throw DataFileException("error while reading file '" + m_fileName + "'"); throw DataFileException("premature end of file in '" + m_fileName + "'"); } } else { *numRead = total; } } void QFileImpl::seek(const int64_t& position) { if (!m_file.seek(position)) throw DataFileException("seek failed in file '" + m_fileName + "'"); } int64_t QFileImpl::pos() { return m_file.pos(); } void QFileImpl::write(const void* dataIn, const int64_t& count) { int64_t total = 0; int64_t writeret = -1; while (total < count) { int64_t maxToWrite = min(count - total, CHUNK_SIZE); writeret = m_file.write((const char*)dataIn, maxToWrite);//QFile probably also chokes on large writes if (writeret < 1) break;//0 or -1 means error or eof total += writeret; } const AString msg = ("failed to write file '" + m_fileName + "'. Tried to write " + AString::number(count) + " bytes but actually wrote " + AString::number(total) + " bytes."); if (total != count) throw DataFileException(msg); } connectome-workbench-1.2.3+git41-gc4c6c90/src/Common/CaretBinaryFile.h000066400000000000000000000057621300200146000251620ustar00rootroot00000000000000#ifndef __CARET_BINARY_FILE_H__ #define __CARET_BINARY_FILE_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 "CaretPointer.h" #include #include namespace caret { //class to hide difference between compressed and standard binary file reading, and to automate error checking (throws if problem) class CaretBinaryFile { public: enum OpenMode { NONE = 0, READ = 1, WRITE = 2, READ_WRITE = 3,//for convenience TRUNCATE = 4, WRITE_TRUNCATE = 6,//ditto READ_WRITE_TRUNCATE = 7//ditto }; CaretBinaryFile() { } ///constructor that opens file CaretBinaryFile(const QString& filename, const OpenMode& fileMode = READ); void open(const QString& filename, const OpenMode& opmode = READ); void close(); QString getFilename() const;//not a reference because when no file is open, m_impl is NULL bool getOpenForRead(); bool getOpenForWrite(); void seek(const int64_t& position); 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 class ImplInterface { protected: QString m_fileName;//filename is tracked here so error messages can be implementation-specific public: virtual void open(const QString& filename, const OpenMode& opmode) = 0; virtual void close() = 0; const QString& getFilename() const { return m_fileName; } virtual void seek(const int64_t& position) = 0; virtual int64_t pos() = 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(); }; private: CaretPointer m_impl; OpenMode m_curMode;//so implementation classes don't have to track it }; } //namespace caret #endif //__CARET_BINARY_FILE_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Common/CaretColorEnum.cxx000066400000000000000000000423411300200146000254060ustar00rootroot00000000000000 /*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 __CARET_COLOR_ENUM_DECLARE__ #include "CaretColorEnum.h" #undef __CARET_COLOR_ENUM_DECLARE__ #include "CaretAssert.h" using namespace caret; /** * \class caret::CaretColorEnum * \brief Enumerate types for standard colors (HTML 4.01) * * Enumerated types for standard colors */ /** * Constructor. * * @param enumValue * An enumerated value. * @param name * Name of enumerated value. * @param guiName * User-friendly name for use in user-interface. * @param red * Red color component [0 - 1] * @param green * Green color component [0 - 1] * @param blue * Blue color component [0 - 1] */ CaretColorEnum::CaretColorEnum(const Enum enumValue, const AString& name, const AString& guiName, const float red, const float green, const float blue) { this->enumValue = enumValue; this->integerCode = integerCodeCounter++; this->name = name; this->guiName = guiName; this->rgb[0]= red; this->rgb[1]= green; this->rgb[2]= blue; } /** * Destructor. */ CaretColorEnum::~CaretColorEnum() { } /** * Initialize the enumerated metadata. */ void CaretColorEnum::initialize() { if (initializedFlag) { return; } initializedFlag = true; enumData.push_back(CaretColorEnum(NONE, "NONE", "None", 0, 0, 0)); enumData.push_back(CaretColorEnum(CUSTOM, "CUSTOM", "Custom", 1, 1, 1)); enumData.push_back(CaretColorEnum(AQUA, "AQUA", "Aqua", 0, 1, 1)); enumData.push_back(CaretColorEnum(BLACK, "BLACK", "Black", 0, 0, 0)); enumData.push_back(CaretColorEnum(BLUE, "BLUE", "Blue", 0, 0, 1)); enumData.push_back(CaretColorEnum(FUCHSIA, "FUCHSIA", "Fuchsia", 1, 0, 1)); enumData.push_back(CaretColorEnum(GRAY, "GRAY", "Gray", 0.50, 0.50, 0.50)); enumData.push_back(CaretColorEnum(GREEN, "GREEN", "Green", 0, 0.5, 0)); enumData.push_back(CaretColorEnum(LIME, "LIME", "Lime", 0, 1, 0)); enumData.push_back(CaretColorEnum(MAROON, "MAROON", "Maroon", 0.5, 0, 0)); enumData.push_back(CaretColorEnum(NAVY, "NAVY", "Navy", 0, 0, 0.5)); enumData.push_back(CaretColorEnum(OLIVE, "OLIVE", "Olive", 0.5, 0.5, 0)); enumData.push_back(CaretColorEnum(PURPLE, "PURPLE", "Purple", 0.5, 0, 0.5)); enumData.push_back(CaretColorEnum(RED, "RED", "Red", 1, 0, 0)); enumData.push_back(CaretColorEnum(SILVER, "SILVER", "Silver", 0.75, 0.75, 0.75)); enumData.push_back(CaretColorEnum(TEAL, "TEAL", "Teal", 0, 0.5, 0.5)); enumData.push_back(CaretColorEnum(WHITE, "WHITE", "White", 1, 1, 1)); enumData.push_back(CaretColorEnum(YELLOW, "YELLOW", "Yellow", 1, 1, 0)); } /** * 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 CaretColorEnum* CaretColorEnum::findData(const Enum enumValue) { if (initializedFlag == false) initialize(); size_t num = enumData.size(); for (size_t i = 0; i < num; i++) { const CaretColorEnum* 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 CaretColorEnum::toName(Enum enumValue) { if (initializedFlag == false) initialize(); const CaretColorEnum* 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. */ CaretColorEnum::Enum CaretColorEnum::fromName(const AString& name, bool* isValidOut) { if (initializedFlag == false) initialize(); bool validFlag = false; Enum enumValue = BLACK; for (std::vector::iterator iter = enumData.begin(); iter != enumData.end(); iter++) { const CaretColorEnum& 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 CaretColorEnum")); } return enumValue; } /** * Get a GUI string representation of the enumerated type. * @param enumValue * Enumerated value. * @return * String representing enumerated value. */ AString CaretColorEnum::toGuiName(Enum enumValue) { if (initializedFlag == false) initialize(); const CaretColorEnum* enumInstance = findData(enumValue); return enumInstance->guiName; } /** * Get the RGB components (ranging zero to one) of the enumerated type. * @param enumValue * Enumerated value. * @return Pointer to RGB components. */ const float* CaretColorEnum::toRGB(Enum enumValue) { if (initializedFlag == false) initialize(); const CaretColorEnum* enumInstance = findData(enumValue); return enumInstance->rgb; } /** * Get the RGB components (ranging zero to one) of the enumerated type. * @param enumValue * Enumerated value. * @param rgbOut * Output with RGB components [0.0, 1.0] */ void CaretColorEnum::toRGBFloat(Enum enumValue, float rgbOut[3]) { if (initializedFlag == false) initialize(); const CaretColorEnum* enumInstance = findData(enumValue); rgbOut[0] = enumInstance->rgb[0]; rgbOut[1] = enumInstance->rgb[1]; rgbOut[2] = enumInstance->rgb[2]; } /** * Get the RGB components (ranging zero to one) of the enumerated type. * @param enumValue * Enumerated value. * @param rgbOut * Output with RGB components [0.0, 1.0] */ void CaretColorEnum::toRGBByte(Enum enumValue, uint8_t rgbOut[3]) { float rgbFloat[3]; toRGBFloat(enumValue, rgbFloat); rgbOut[0] = static_cast(rgbFloat[0] * 255.0); rgbOut[1] = static_cast(rgbFloat[1] * 255.0); rgbOut[2] = static_cast(rgbFloat[2] * 255.0); } /** * 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. */ CaretColorEnum::Enum CaretColorEnum::fromGuiName(const AString& guiName, bool* isValidOut) { if (initializedFlag == false) initialize(); bool validFlag = false; Enum enumValue = BLACK; for (std::vector::iterator iter = enumData.begin(); iter != enumData.end(); iter++) { const CaretColorEnum& 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 CaretColorEnum")); } return enumValue; } /** * Get the integer code for a data type. * * @return * Integer code for data type. */ int32_t CaretColorEnum::toIntegerCode(Enum enumValue) { if (initializedFlag == false) initialize(); const CaretColorEnum* 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. */ CaretColorEnum::Enum CaretColorEnum::fromIntegerCode(const int32_t integerCode, bool* isValidOut) { if (initializedFlag == false) initialize(); bool validFlag = false; Enum enumValue = BLACK; for (std::vector::iterator iter = enumData.begin(); iter != enumData.end(); iter++) { const CaretColorEnum& 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 CaretColorEnum")); } return enumValue; } /** * Get all of the enumerated type values THAT REPRESENT A COLOR. 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::getAllEnums(std::vector& allColorEnums) { if (initializedFlag == false) initialize(); getColorAndOptionalEnums(allColorEnums, OPTION_NO_OPTIONS); } /** * Get all of the enumerated type values THAT REPRESENT A COLOR. 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::getColorEnums(std::vector& allColorEnums) { if (initializedFlag == false) initialize(); getColorAndOptionalEnums(allColorEnums, OPTION_NO_OPTIONS); } /** * Get all of the enumerated type values THAT REPRESENT A COLOR and the specified * optional enums. 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 VALID COLOR enumerated values * and also the optional enums. * @param colorOptions * Bitwise OR of the color options. */ void CaretColorEnum::getColorAndOptionalEnums(std::vector& allEnums, const int64_t colorOptions) { if (initializedFlag == false) initialize(); allEnums.clear(); const bool includeCustomColorFlag = (colorOptions & OPTION_INCLUDE_CUSTOM_COLOR); const bool includeNoneColorFlag = (colorOptions & OPTION_INCLUDE_NONE_COLOR); for (std::vector::iterator iter = enumData.begin(); iter != enumData.end(); iter++) { const CaretColorEnum::Enum colorEnum = iter->enumValue; bool addColorEnumFlag = true; if (colorEnum == CUSTOM) { if ( ! includeCustomColorFlag) { addColorEnumFlag = false; } } else if (colorEnum == NONE) { if ( ! includeNoneColorFlag) { addColorEnumFlag = false; } } if (addColorEnumFlag) { 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 //CaretColorEnum::getAllNames(std::vector& allNames, // const bool isSorted) //{ // if (initializedFlag == false) initialize(); // // allNames.clear(); // // std::vector allEnums; // CaretColorEnum::getColorEnums(allEnums); // for (std::vector::iterator iter = allEnums.begin(); // iter != allEnums.end(); // iter++) { // allNames.push_back(CaretColorEnum::toName(*iter)); // } // // 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 //CaretColorEnum::getAllGuiNames(std::vector& allGuiNames, // const bool isSorted) //{ // if (initializedFlag == false) initialize(); // // allGuiNames.clear(); // // std::vector allEnums; // CaretColorEnum::getColorEnums(allEnums); // for (std::vector::iterator iter = allEnums.begin(); // iter != allEnums.end(); // iter++) { // allGuiNames.push_back(CaretColorEnum::toGuiName(*iter)); // } // // if (isSorted) { // std::sort(allGuiNames.begin(), allGuiNames.end()); // } //} connectome-workbench-1.2.3+git41-gc4c6c90/src/Common/CaretColorEnum.h000066400000000000000000000104641300200146000250340ustar00rootroot00000000000000#ifndef __CARET_COLOR_ENUM__H_ #define __CARET_COLOR_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 CaretColorEnum { public: /** * Enumerated values. */ enum Enum { /** No coloring */ NONE, /** Custom coloring */ CUSTOM, /** AQUA */ AQUA, /** Black */ BLACK, /** Blue */ BLUE, /** Fuchsia */ FUCHSIA, /** Gray */ GRAY, /** Green */ GREEN, /** Lime */ LIME, /** Maroon */ MAROON, /** Navy Blue */ NAVY, /** Olive */ OLIVE, /** Purple */ PURPLE, /** Red */ RED, /** Silver */ SILVER, /** Teal */ TEAL, /** White */ WHITE, /** Yellow */ YELLOW }; enum ColorOptions { OPTION_NO_OPTIONS = 0, OPTION_INCLUDE_CUSTOM_COLOR = 1, OPTION_INCLUDE_NONE_COLOR = 2 }; ~CaretColorEnum(); 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& allColorEnums); static void getColorEnums(std::vector& allColorEnums); static void getColorAndOptionalEnums(std::vector& allEnums, const int64_t colorOptions); // static void getAllNames(std::vector& allNames, const bool isSorted); // // static void getAllGuiNames(std::vector& allGuiNames, const bool isSorted); static const float* toRGB(Enum enumValue); static void toRGBFloat(Enum enumValue, float rgbOut[3]); static void toRGBByte(Enum enumValue, uint8_t rgbOut[3]); private: CaretColorEnum(const Enum enumValue, const AString& name, const AString& guiName, const float red, const float green, const float blue); static const CaretColorEnum* 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; /** RGB color components */ float rgb[3]; }; #ifdef __CARET_COLOR_ENUM_DECLARE__ std::vector CaretColorEnum::enumData; bool CaretColorEnum::initializedFlag = false; int32_t CaretColorEnum::integerCodeCounter = 0; #endif // __CARET_COLOR_ENUM_DECLARE__ } // namespace #endif //__CARET_COLOR_ENUM__H_ connectome-workbench-1.2.3+git41-gc4c6c90/src/Common/CaretCommandLine.cxx000066400000000000000000000055541300200146000256760ustar00rootroot00000000000000/*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 "CaretCommandLine.h" #include "ProgramParameters.h" using namespace caret; AString caret::caret_global_commandLine; namespace {//private namespace void add_parameter(const AString& param) { if (caret_global_commandLine.size() != 0) { caret_global_commandLine += " "; } if (param.indexOfAnyChar(" $();&<>\"`*?{|") != -1)//check for things that the shell is likely to treat specially EXCEPT for ' itself - assume bash for now, but ignore some more specialized cases {//NOTE: not checking for \ or replacing with \\, because it is rare except in windows native paths where it will wreak havok to double it if (param.indexOf('\'') != -1)//oh joy, ' also {//we COULD check if it is safe to use "", but "" and non-CDATA xml text don't look nice (we avoid CDATA in CIFTI because the matlab GIFTI toolbox at least used to choke on it after conversion) AString replaced = param; replaced.replace('\'', "'\\''");//that is '\'' caret_global_commandLine += "'" + replaced + "'"; } else { caret_global_commandLine += "'" + param + "'"; } } else { if (param.indexOf('\'') != -1)//has ' but no other problems, doesn't need quoting { AString replaced = param; replaced.replace('\'', "\\'");//that is \' caret_global_commandLine += replaced; } else { caret_global_commandLine += param; } } } } void caret::caret_global_commandLine_init(const ProgramParameters& params) { int32_t numParams = params.getNumberOfParameters(); caret_global_commandLine = ""; add_parameter(params.getProgramName()); for (int32_t i = 0; i < numParams; ++i) { add_parameter(params.getParameter(i)); } } void caret::caret_global_commandLine_init(const int& argc, const char *const * argv) { ProgramParameters params(argc, argv); caret_global_commandLine_init(params); } connectome-workbench-1.2.3+git41-gc4c6c90/src/Common/CaretCommandLine.h000066400000000000000000000023251300200146000253140ustar00rootroot00000000000000#ifndef __CARET_COMMAND_LINE_H__ #define __CARET_COMMAND_LINE_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 "AString.h" namespace caret { class ProgramParameters; extern AString caret_global_commandLine; void caret_global_commandLine_init(const ProgramParameters& params); void caret_global_commandLine_init(const int& argc, const char *const * argv); } #endif //__CARET_COMMAND_LINE_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Common/CaretCompact3DLookup.h000066400000000000000000000103751300200146000261010ustar00rootroot00000000000000#ifndef __CARET_COMPACT_3D_LOOKUP_H__ #define __CARET_COMPACT_3D_LOOKUP_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 "CaretCompactLookup.h" namespace caret { template class CaretCompact3DLookup { CaretCompactLookup > > m_lookup;//the whole point of this class is to deal with this ugliness public: ///creates the element if it didn't exist, and returns a reference to it T& at(const int64_t& index1, const int64_t& index2, const int64_t& index3); ///creates the element if it didn't exist, and returns a reference to it T& at(const int64_t index[3]) { return at(index[0], index[1], index[2]); } ///add or overwrite an element in the lookup void insert(const int64_t& index1, const int64_t& index2, const int64_t& index3, const T& value) { at(index1, index2, index3) = value; } ///add or overwrite an element in the lookup void insert(const int64_t index[3], const T& value) { at(index) = value; } ///returns a pointer to the desired element, or NULL if no such element is found T* find(const int64_t& index1, const int64_t& index2, const int64_t& index3); ///returns a pointer to the desired element, or NULL if no such element is found T* find(const int64_t index[3]) { return find(index[0], index[1], index[2]); } ///returns a pointer to the desired element, or NULL if no such element is found const T* find(const int64_t& index1, const int64_t& index2, const int64_t& index3) const; ///returns a pointer to the desired element, or NULL if no such element is found const T* find(const int64_t index[3]) const { return find(index[0], index[1], index[2]); } ///empties the lookup void clear(); }; template T& CaretCompact3DLookup::at(const int64_t& index1, const int64_t& index2, const int64_t& index3) { return m_lookup[index3][index2][index1];//a lot of complexity is hidden in those operator[]s } template T* CaretCompact3DLookup::find(const int64_t& index1, const int64_t& index2, const int64_t& index3) { typename CaretCompactLookup > >::iterator iter1 = m_lookup.find(index3);//oh the humanity if (iter1 == m_lookup.end()) return NULL; typename CaretCompactLookup >::iterator iter2 = iter1->find(index2); if (iter2 == iter1->end()) return NULL; typename CaretCompactLookup::iterator iter3 = iter2->find(index1); if (iter3 == iter2->end()) return NULL; return &(*iter3); } template const T* CaretCompact3DLookup::find(const int64_t& index1, const int64_t& index2, const int64_t& index3) const { typename CaretCompactLookup > >::const_iterator iter1 = m_lookup.find(index3);//oh the humanity if (iter1 == m_lookup.end()) return NULL; typename CaretCompactLookup >::const_iterator iter2 = iter1->find(index2); if (iter2 == iter1->end()) return NULL; typename CaretCompactLookup::const_iterator iter3 = iter2->find(index1); if (iter3 == iter2->end()) return NULL; return &(*iter3); } template void CaretCompact3DLookup::clear() { m_lookup.clear(); } } #endif //__CARET_COMPACT_3D_LOOKUP_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Common/CaretCompactLookup.h000066400000000000000000000225641300200146000257150ustar00rootroot00000000000000#ifndef __CARET_COMPACT_LOOKUP_H__ #define __CARET_COMPACT_LOOKUP_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 "CaretAssert.h" #include "stdint.h" #include namespace caret { template class CaretCompactLookup { struct Chunk { int64_t start; std::vector elements; }; std::vector m_chunks; public: class iterator { CaretCompactLookup& m_container; std::size_t m_chunk; int64_t m_elem; iterator(CaretCompactLookup& container, std::size_t chunk, int64_t elem) : m_container(container), m_chunk(chunk), m_elem(elem) { } public: bool operator==(const iterator& rhs) const { if (&m_container != &(rhs.m_container)) return false; if (m_chunk != rhs.m_chunk) return false; if (m_elem != rhs.m_elem) return false; return true; } T& operator*() { CaretAssert(m_chunk < m_container.m_chunks.size()); CaretAssert(m_elem >= 0 && m_elem < (int64_t)m_container.m_chunks[m_chunk].elements.size()); return m_container.m_chunks[m_chunk].elements[m_elem]; } T* operator->() { CaretAssert(m_chunk < m_container.m_chunks.size()); CaretAssert(m_elem >= 0 && m_elem < (int64_t)m_container.m_chunks[m_chunk].elements.size()); return &(m_container.m_chunks[m_chunk].elements[m_elem]); } friend class CaretCompactLookup; }; class const_iterator { const CaretCompactLookup& m_container; std::size_t m_chunk; int64_t m_elem; const_iterator(const CaretCompactLookup& container, std::size_t chunk, std::size_t elem) : m_container(container), m_chunk(chunk), m_elem(elem) { } public: bool operator==(const const_iterator& rhs) const { if (&m_container != &(rhs.m_container)) return false; if (m_chunk != rhs.m_chunk) return false; if (m_elem != rhs.m_elem) return false; return true; } const T& operator*() { CaretAssert(m_chunk < m_container.m_chunks.size()); CaretAssert(m_elem >= 0 && m_elem < (int64_t)m_container.m_chunks[m_chunk].elements.size()); return m_container.m_chunks[m_chunk].elements[m_elem]; } const T* operator->() { CaretAssert(m_chunk < m_container.m_chunks.size()); CaretAssert(m_elem >= 0 && m_elem < (int64_t)m_container.m_chunks[m_chunk].elements.size()); return &(m_container.m_chunks[m_chunk].elements[m_elem]); } friend class CaretCompactLookup; }; ///creates the element if it didn't exist, and returns a reference to it T& operator[](const int64_t& index); ///returns an iterator pointing to the desired element, or one equal to end() if no such element is found iterator find(const int64_t& index); ///returns a const_iterator pointing to the desired element, or one equal to end() if no such element is found const_iterator find(const int64_t& index) const; iterator end(); const_iterator end() const; ///empties the lookup void clear(); }; template T& CaretCompactLookup::operator[](const int64_t& index) { std::size_t numChunks = m_chunks.size(); std::size_t low = 0, high = numChunks, guess;//NOTE: low is 0 because size_t is unsigned, really means -1 bool attach_low = false, attach_high = false; while (low < high)//bisection search for the chunk with the largest start value not greater than { guess = (low + high - 1) / 2;//which is why we subtract 1 here CaretAssert(guess < m_chunks.size()); if (m_chunks[guess].start > index) { high = guess; } else { low = guess + 1; } }//NOTE: low == high after loop ends if (high > 0 && m_chunks[high - 1].start + (int64_t)(m_chunks[high - 1].elements.size()) > index)//element exists, return it { CaretAssertVectorIndex(m_chunks[high -1].elements, index - m_chunks[high - 1].start); return m_chunks[high - 1].elements[index - m_chunks[high - 1].start]; } if (high > 0 && m_chunks[high - 1].start + (int64_t)(m_chunks[high - 1].elements.size()) == index) attach_low = true;//index is 1 beyond the range if (high < numChunks && m_chunks[high].start == index + 1) attach_high = true;//index is 1 before the next range if (attach_low) { std::vector& lowvec = m_chunks[high - 1].elements; std::size_t retIndex = lowvec.size(); lowvec.push_back(T()); if (attach_high) { std::vector& highvec = m_chunks[high].elements; lowvec.insert(lowvec.end(), highvec.begin(), highvec.end()); m_chunks.erase(m_chunks.begin() + high); } return lowvec[retIndex]; } else { if (attach_high) { std::vector& highvec = m_chunks[high].elements; highvec.insert(highvec.begin(), T());//add a new element to the start of the vector (yes, this could be slow) m_chunks[high].start = index;//and change the start value of the chunk return highvec[0]; } else { m_chunks.insert(m_chunks.begin() + high, Chunk()); m_chunks[high].start = index; m_chunks[high].elements.push_back(T()); return m_chunks[high].elements[0]; } } } template typename CaretCompactLookup::iterator CaretCompactLookup::find(const int64_t& index) { std::size_t numChunks = m_chunks.size(); std::size_t low = 0, high = numChunks, guess;//NOTE: low is 0 because size_t is unsigned, really means -1 while (low < high)//bisection search for the chunk with the largest start value not greater than { guess = (low + high - 1) / 2;//which is why we subtract 1 here CaretAssert(guess < m_chunks.size()); if (m_chunks[guess].start > index) { high = guess; } else { low = guess + 1; } }//NOTE: low == high after loop ends if (high > 0 && m_chunks[high - 1].start + (int64_t)(m_chunks[high - 1].elements.size()) > index)//element exists, return it { std::size_t outIndex = index - m_chunks[high - 1].start; CaretAssert(outIndex < m_chunks[high - 1].elements.size()); return iterator(*this, high - 1, outIndex); } return end(); } template typename CaretCompactLookup::const_iterator CaretCompactLookup::find(const int64_t& index) const { std::size_t numChunks = m_chunks.size(); std::size_t low = 0, high = numChunks, guess;//NOTE: low is 0 because size_t is unsigned, really means -1 while (low < high)//bisection search for the chunk with the largest start value not greater than { guess = (low + high - 1) / 2;//which is why we subtract 1 here CaretAssert(guess < m_chunks.size()); if (m_chunks[guess].start > index) { high = guess; } else { low = guess + 1; } }//NOTE: low == high after loop ends if (high > 0 && m_chunks[high - 1].start + (int64_t)(m_chunks[high - 1].elements.size()) > index)//element exists, return it { std::size_t outIndex = index - m_chunks[high - 1].start; CaretAssert(outIndex < m_chunks[high - 1].elements.size()); return const_iterator(*this, high - 1, outIndex); } return end(); } template typename CaretCompactLookup::iterator CaretCompactLookup::end() { return iterator(*this, 0, -1); } template typename CaretCompactLookup::const_iterator CaretCompactLookup::end() const { return const_iterator(*this, 0, -1); } template void CaretCompactLookup::clear() { m_chunks.clear(); } } #endif //__CARET_COMPACT_LOOKUP_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Common/CaretException.cxx000066400000000000000000000053231300200146000254400ustar00rootroot00000000000000/*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 "CaretException.h" #include "SystemUtilities.h" #include using namespace caret; /** * Constructor. * */ CaretException::CaretException() : std::runtime_error("") { this->initializeMembersCaretException(); } /** * Constructor. * * @param s Description of the exception. * */ CaretException::CaretException( const AString& s) : std::runtime_error(s.toStdString()) { this->initializeMembersCaretException(); this->exceptionDescription = s; } /** * Copy Constructor. * @param e * Exception that is copied. */ CaretException::CaretException(const CaretException& e) : std::runtime_error(e) { this->exceptionDescription = e.exceptionDescription; this->callStack = e.callStack; } /** * Assignment operator. * @param e * Exception that is copied. * @return * Copy of the exception. */ CaretException& CaretException::operator=(const CaretException& e) { if (this != &e) { std::runtime_error::operator=(e); this->exceptionDescription = e.exceptionDescription; this->callStack = e.callStack; } return *this; } /** * Destructor */ CaretException::~CaretException() throw() { } void CaretException::initializeMembersCaretException() { this->exceptionDescription = ""; this->callStack = SystemUtilities::getBackTrace(); } /** * Get the current call stack. * @return String containing the call stack. */; AString CaretException::getCallStack() const { return callStack; } /** * Get a message describing the exception. * @return A message describing the exception. */ AString CaretException::whatString() const throw() { return this->exceptionDescription; } /** * Allow subclasses to override the exception description. * * @param s Description of the exception. */ void CaretException::setExceptionDescription(const AString& s) { this->exceptionDescription = s; } connectome-workbench-1.2.3+git41-gc4c6c90/src/Common/CaretException.h000066400000000000000000000032441300200146000250650ustar00rootroot00000000000000#ifndef __CARETEXCEPTION_H__ #define __CARETEXCEPTION_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 namespace caret { /** * An exception thrown during Caret processing. */ class CaretException : public std::runtime_error { public: CaretException(); CaretException(const AString& s); CaretException(const CaretException& e); CaretException& operator=(const CaretException& e); virtual ~CaretException() throw(); virtual AString whatString() const throw(); AString getCallStack() const; protected: void setExceptionDescription(const AString& s); private: /// Description of the exception AString exceptionDescription; /// the call stack AString callStack; void initializeMembersCaretException(); }; } // namespace #endif // __CARETEXCEPTION_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Common/CaretFunctionName.h000066400000000000000000000023561300200146000255200ustar00rootroot00000000000000#ifndef __CARET_FUNCTION_NAME_H__ #define __CARET_FUNCTION_NAME_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*/ #ifdef CARET_OS_WINDOWS_MSVC /** * \def __CARET_FUNCTION_NAME__ * * A macro that contains the name of the current * function. This macro's definition is platform * dependent (Visual C++ vs. GCC). */ #define __CARET_FUNCTION_NAME__ __FUNCSIG__ #else #define __CARET_FUNCTION_NAME__ __PRETTY_FUNCTION__ #endif #endif //__CARET_FUNCTION_NAME_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Common/CaretHeap.h000066400000000000000000000370421300200146000240070ustar00rootroot00000000000000#ifndef __CARET_HEAP__ #define __CARET_HEAP__ #include "CaretAssertion.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 "stdint.h" #include "CaretAssert.h" namespace caret { ///heap base used to be able to modify existing data's keys, and to automatically indirect the heap data through indexing, so that all heap reordering involves only integer assignments template class CaretHeapBase { struct DataStruct { K m_key; T m_data; int64_t m_index; DataStruct(const K& key, const T& data) : m_key(key), m_data(data) { } }; std::vector m_datastore; std::vector m_heap; std::vector m_unusedStore; ///primitive used to make the code a bit nicer, modifies the heap and the field in the data to point to it ///DOES NOT GUARANTEE CONSISTENT HEAP STATE void put(const int64_t& dataIndex, const int64_t& heapLoc); //uses curiously recurring template pattern to use child class's comparison without virtual - can't make a typedef missing template parameters, so this is the most convenient way for usage inline bool compare(const K& left, const K& right) { return C::mycompare(left, right); } void heapify_up(const int64_t& start); void heapify_down(const int64_t& start); protected: CaretHeapBase() { }//this is not a usable class by itself - use CaretMinHeap or CaretMaxHeap public: ///this heap is special, save the return value of push() and you can modify the key/data later (constant time for data, log(heapsize) time to change key) int64_t push(const T& data, const K& key); ///look at the data of the top element T& top(K* key = NULL); ///remove and return the top element T pop(K* key = NULL); ///modify the key, and reheapify void changekey(const int64_t& dataIndex, K key); ///delete the element with the key, and reheapify T remove(const int64_t& dataIndex, K* key = NULL); ///modify or just access the data - could be an operator[] but that would be kind of confusing T& data(const int64_t& dataIndex); ///preallocate for efficiency, if you know about how big it will be void reserve(int64_t expectedSize); ///retrieve the key value const K& getKey(const int64_t& dataIndex) const; ///check for empty bool isEmpty() const; ///get number of elements int64_t size() const; ///reset the heap void clear(); }; ///simpler heap base for more basic (and not indirected) use, give it pointers if your data struct is nontrivial template class CaretSimpleHeapBase { struct DataStruct { K m_key; T m_data; DataStruct(const K& key, const T& data) : m_key(key), m_data(data) { } }; std::vector m_heap; //uses curiously recurring template pattern to use child class's comparison without virtual inline bool compare(const K& left, const K& right) { return C::mycompare(left, right); } void heapify_up(const int64_t& start); void heapify_down(const int64_t& start); protected: CaretSimpleHeapBase() { }//this is not a usable class by itself - use CaretMinHeap or CaretMaxHeap public: void push(const T& data, const K& key); ///look at the data of the top element T& top(K* key = NULL); ///remove and return the top element T pop(K* key = NULL); ///preallocate for efficiency, if you know about how big it will be void reserve(int64_t expectedSize); ///check for empty bool isEmpty() const; ///get number of elements int64_t size() const; ///reset the heap void clear(); }; ///minheap with advanced features template class CaretMinHeap : public CaretHeapBase > { public: static inline bool mycompare(const K& left, const K& right) { return left < right; } }; ///maxheap with advanced features template class CaretMaxHeap : public CaretHeapBase > { public: static inline bool mycompare(const K& left, const K& right) { return right < left; } }; ///basic minheap template class CaretSimpleMinHeap : public CaretSimpleHeapBase > { public: static inline bool mycompare(const K& left, const K& right) { return left < right; } }; ///basic maxheap template class CaretSimpleMaxHeap : public CaretSimpleHeapBase > { public: static inline bool mycompare(const K& left, const K& right) { return right < left; } }; template void CaretHeapBase::changekey(const int64_t& dataIndex, K key) { CaretAssertVectorIndex(m_datastore, dataIndex); CaretAssert(m_datastore[dataIndex].m_index != -1); K oldkey = m_datastore[dataIndex].m_key; m_datastore[dataIndex].m_key = key; if (compare(oldkey, key)) { heapify_down(m_datastore[dataIndex].m_index); } else { heapify_up(m_datastore[dataIndex].m_index); } } template T CaretHeapBase::remove(const int64_t& dataIndex, K* key) { CaretAssertVectorIndex(m_datastore, dataIndex); CaretAssert(m_datastore[dataIndex].m_index != -1); int64_t myHeapIndex = m_datastore[dataIndex].m_index; T ret = m_datastore[dataIndex].m_data; if (key != NULL) *key = m_datastore[dataIndex].m_key; if (myHeapIndex < (int64_t)(m_heap.size() - 1))//don't try to do stuff other than removing it if it is the last element { K removedKey = m_datastore[dataIndex].m_key; K newKey = m_datastore[m_heap.back()].m_key; put(m_heap.back(), myHeapIndex);//replace the removed element's position with the last m_heap.pop_back(); if (compare(removedKey, newKey))//and heapify it { heapify_down(myHeapIndex); } else { heapify_up(myHeapIndex); } } else { m_heap.pop_back(); } m_unusedStore.push_back(dataIndex);//mark the removed data location as unused m_datastore[dataIndex].m_index = -1; return ret; } template T& CaretHeapBase::data(const int64_t& dataIndex) { CaretAssertVectorIndex(m_datastore, dataIndex); CaretAssert(m_datastore[dataIndex].m_index != -1); return m_datastore[dataIndex].m_data; } template void CaretHeapBase::heapify_down(const int64_t& start) { CaretAssertVectorIndex(m_heap, start); int64_t cur = start, nextInd = (start << 1) + 1, mySize = (int64_t)m_heap.size(); int64_t temp = m_heap[start];//save current data index, don't swap it around until we stop while (nextInd < mySize) { if (nextInd + 1 < mySize && compare(m_datastore[m_heap[nextInd + 1]].m_key, m_datastore[m_heap[nextInd]].m_key)) { ++nextInd; } if (compare(m_datastore[m_heap[nextInd]].m_key, m_datastore[temp].m_key)) { put(m_heap[nextInd], cur);//move the best child up cur = nextInd;//advance current nextInd = (cur << 1) + 1; } else { break; } } if (cur != start) put(temp, cur);//stopped, now we put it and finish, but only if we moved something } template void CaretHeapBase::heapify_up(const int64_t& start) { CaretAssertVectorIndex(m_heap, start); int64_t cur = start, nextInd = (start - 1) >> 1; int64_t temp = m_heap[start]; while (cur > 0) { if (compare(m_datastore[temp].m_key, m_datastore[m_heap[nextInd]].m_key)) { put(m_heap[nextInd], cur); cur = nextInd; nextInd = (cur - 1) >> 1; } else { break; } } if (cur != start) put(temp, cur); } template T CaretHeapBase::pop(K* key) { CaretAssert(m_heap.size() > 0); T ret = m_datastore[m_heap[0]].m_data; if (key != NULL) *key = m_datastore[m_heap[0]].m_key; m_unusedStore.push_back(m_heap[0]);//should this try garbage collection? currently, the T data will remain until overwritten...would require another level of indirection to fix m_datastore[m_heap[0]].m_index = -1; if (m_heap.size() > 1) { put(m_heap[m_heap.size() - 1], 0); m_heap.pop_back(); heapify_down(0); } else { m_heap.pop_back(); } return ret; } template int64_t CaretHeapBase::push(const T& data, const K& key) { int64_t dataLoc; if (m_unusedStore.size() > 0) { dataLoc = m_unusedStore[m_unusedStore.size() - 1]; m_datastore[dataLoc].m_key = key; m_datastore[dataLoc].m_data = data; m_unusedStore.pop_back(); } else { dataLoc = m_datastore.size(); m_datastore.push_back(DataStruct(key, data)); } m_datastore[dataLoc].m_index = (int64_t)m_heap.size(); m_heap.push_back(dataLoc); heapify_up(m_heap.size() - 1); return dataLoc; } template void CaretHeapBase::put(const int64_t& dataIndex, const int64_t& heapLoc) { CaretAssertVectorIndex(m_datastore, dataIndex); CaretAssertVectorIndex(m_heap, heapLoc); m_datastore[dataIndex].m_index = heapLoc; m_heap[heapLoc] = dataIndex; } template void CaretHeapBase::reserve(int64_t expectedSize) { if (expectedSize <= 0) return; m_heap.reserve(expectedSize); m_datastore.reserve(expectedSize); m_unusedStore.reserve(expectedSize);//expect them to eventually pop() everything } template T& CaretHeapBase::top(K* key) { CaretAssert(m_heap.size() > 0); if (key != NULL) *key = m_datastore[m_heap[0]].m_key; return m_datastore[m_heap[0]].m_data; } template bool CaretHeapBase::isEmpty() const { return m_heap.size() == 0; } template int64_t CaretHeapBase::size() const { return (int64_t)m_heap.size(); } template void CaretHeapBase::clear() { m_heap.clear(); m_datastore.clear(); m_unusedStore.clear(); } template void CaretSimpleHeapBase::heapify_down(const int64_t& start) { CaretAssertVectorIndex(m_heap, start); int64_t cur = start, nextInd = (start << 1) + 1, mySize = (int64_t)m_heap.size(); DataStruct temp = m_heap[start];//save current data, don't swap it around until we stop while (nextInd < mySize) { if (nextInd + 1 < mySize && compare(m_heap[nextInd + 1].m_key, m_heap[nextInd].m_key)) { ++nextInd; } if (compare(m_heap[nextInd].m_key, temp.m_key)) { m_heap[cur] = m_heap[nextInd];//move the best child up cur = nextInd;//advance current nextInd = (cur << 1) + 1; } else { break; } } if (cur != start) m_heap[cur] = temp;//stopped, now we put it and finish, but only if we moved something } template void CaretSimpleHeapBase::heapify_up(const int64_t& start) { CaretAssertVectorIndex(m_heap, start); int64_t cur = start, nextInd = (start - 1) >> 1; DataStruct temp = m_heap[start]; while (cur > 0) { if (compare(temp.m_key, m_heap[nextInd].m_key)) { m_heap[cur] = m_heap[nextInd]; cur = nextInd; nextInd = (cur - 1) >> 1; } else { break; } } if (cur != start) m_heap[cur] = temp; } template T CaretSimpleHeapBase::pop(K* key) { CaretAssert(m_heap.size() > 0); T ret = m_heap[0].m_data; if (key != NULL) *key = m_heap[0].m_key; if (m_heap.size() > 1) { m_heap[0] = m_heap[m_heap.size() - 1]; m_heap.pop_back(); heapify_down(0); } else { m_heap.pop_back(); } return ret; } template void CaretSimpleHeapBase::push(const T& data, const K& key) { m_heap.push_back(DataStruct(key, data)); heapify_up(m_heap.size() - 1); } template void CaretSimpleHeapBase::reserve(int64_t expectedSize) { if (expectedSize <= 0) return; m_heap.reserve(expectedSize); } template T& CaretSimpleHeapBase::top(K* key) { CaretAssert(m_heap.size() > 0); if (key != NULL) *key = m_heap[0].m_key; return m_heap[0].m_data; } template bool CaretSimpleHeapBase::isEmpty() const { return m_heap.size() == 0; } template int64_t CaretSimpleHeapBase::size() const { return (int64_t)m_heap.size(); } template void CaretSimpleHeapBase::clear() { m_heap.clear(); } } #endif //__CARET_HEAP__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Common/CaretHttpManager.cxx000066400000000000000000000401011300200146000257050ustar00rootroot00000000000000/*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 "CaretHttpManager.h" #include "CaretAssert.h" #include "CaretPointer.h" #include "CaretLogger.h" #include "NetworkException.h" #include using namespace caret; using namespace std; CaretHttpManager* CaretHttpManager::m_singleton = NULL; CaretHttpManager::CaretHttpManager() : QObject() { connect(&m_netMgr, SIGNAL(sslErrors(QNetworkReply*, const QList & )), this, SLOT(handleSslErrors(QNetworkReply*, const QList & ))); } CaretHttpManager* CaretHttpManager::getHttpManager() { if (m_singleton == NULL) { m_singleton = new CaretHttpManager(); } return m_singleton; } void CaretHttpManager::deleteHttpManager() { if (m_singleton != NULL) { delete m_singleton; } } QNetworkAccessManager* CaretHttpManager::getQNetManager() { return &(getHttpManager()->m_netMgr); } static QString caretHttpRequestToName(const CaretHttpRequest &caretHttpRequest) { QString name("Unknown"); switch (caretHttpRequest.m_method) { case CaretHttpManager::GET: name = "GET"; break; case CaretHttpManager::HEAD: name = "HEAD"; break; case CaretHttpManager::POST_ARGUMENTS: name = "POST ARGUMENTS"; break; case CaretHttpManager::POST_FILE: name = "POST FILE"; break; } return name; } static void logHeadersFromRequest(const QNetworkRequest& request, const CaretHttpRequest &caretHttpRequest) { AString infoText; infoText.appendWithNewLine("Request " + caretHttpRequestToName(caretHttpRequest) + ": " + request.url().toString()); infoText.appendWithNewLine(" Headers: "); QList readHeaderList = request.rawHeaderList(); const int numItems = readHeaderList.size(); if (numItems > 0) { for (int32_t i = 0; i < numItems; i++) { const QByteArray headerName = readHeaderList.at(i); if ( ! headerName.isEmpty()) { const QByteArray headerValue = request.rawHeader(headerName); infoText.appendWithNewLine(" Name: " + QString(headerName)); infoText.appendWithNewLine(" Value: " + QString(headerValue)); } } } else { infoText.appendWithNewLine(" Contains no headers"); } CaretLogInfo(infoText); } void CaretHttpManager::getHeaders(const QNetworkReply& reply, std::map& headersOut) { QList readHeaderList = reply.rawHeaderList(); const int numItems = readHeaderList.size(); if (numItems > 0) { for (int32_t i = 0; i < numItems; i++) { const QByteArray headerName = readHeaderList.at(i); if ( ! headerName.isEmpty()) { const QByteArray headerValue = reply.rawHeader(headerName); headersOut.insert(make_pair(QString(headerName), QString(headerValue))); } } } } static void logHeadersFromReply(const QNetworkReply& reply, const CaretHttpRequest &caretHttpRequest, const CaretHttpResponse &caretHttpResponse) { AString infoText; infoText.appendWithNewLine("Reply " + caretHttpRequestToName(caretHttpRequest) + " URL (" + QString::number(caretHttpResponse.m_responseCode) + ") Header: "); if ( ! caretHttpResponse.m_responseCodeValid) { infoText.appendWithNewLine("RESPONSE CODE IS NOT VALID."); } const QNetworkReply::NetworkError networkErrorCode = reply.error(); if (networkErrorCode != QNetworkReply::NoError) { infoText.appendWithNewLine("Network Error Code (See QNetworkReply::NetworkError for description): " + QString::number(static_cast(networkErrorCode))); } QList readHeaderList = reply.rawHeaderList(); const int numItems = readHeaderList.size(); if (numItems > 0) { for (int32_t i = 0; i < numItems; i++) { const QByteArray headerName = readHeaderList.at(i); if ( ! headerName.isEmpty()) { const QByteArray headerValue = reply.rawHeader(headerName); infoText.appendWithNewLine(" Name: " + QString(headerName)); infoText.appendWithNewLine(" Value: " + QString(headerValue)); } } } else { infoText.appendWithNewLine(" Contains no headers"); } CaretLogInfo(infoText); } void CaretHttpManager::httpRequest(const CaretHttpRequest &request, CaretHttpResponse &response) { // /* // * Clear headers from response here only. // * For logging into Spring, it always redirects and the // * JSESSIONID is provided in the first reply but not // * in the reply from the redirect. // */ // response.m_headers.clear(); /* * Code for redirection is from the Qt HTTP example httpwindow.cpp. */ httpRequestPrivate(request, response); if (response.m_responseCode == 302) { if (response.m_redirectionUrlValid) { CaretHttpRequest redirectedRequest = request; redirectedRequest.m_url = response.m_redirectionUrl.toString(); CaretLogInfo("Received and processing redirection request from " + request.m_url + " to " + redirectedRequest.m_url); CaretHttpResponse redirectedResponse; redirectedResponse.m_headers = response.m_headers; // copy headers from first attempt (may have JSESSIONID) httpRequestPrivate(redirectedRequest, redirectedResponse); /* need header from orginal and redirected response */ std::map allHeaders = response.m_headers; allHeaders.insert(redirectedRequest.m_headers.begin(), redirectedRequest.m_headers.end()); response = redirectedResponse; response.m_headers = allHeaders; if (redirectedResponse.m_body.size() > 0) { redirectedResponse.m_body.push_back(0); QString str(&redirectedResponse.m_body[0]); CaretLogFine("Redirected response content: " + str); //std::cout << "Redirected response content: " << qPrintable(str) << std::endl; } } } } void CaretHttpManager::httpRequestPrivate(const CaretHttpRequest &request, CaretHttpResponse &response) { QEventLoop myLoop; QNetworkRequest myRequest; myRequest.setSslConfiguration(QSslConfiguration::defaultConfiguration()); CaretHttpManager* myCaretMgr = getHttpManager(); AString myServerString = getServerString(request.m_url); bool have_auth = false; for (int i = 0; i < (int)myCaretMgr->m_authList.size(); ++i) { if (myServerString == myCaretMgr->m_authList[i].m_serverString) { QString unencoded = myCaretMgr->m_authList[i].m_user + ":" + myCaretMgr->m_authList[i].m_pass; myRequest.setRawHeader("Authorization", "Basic " + unencoded.toLocal8Bit().toBase64()); CaretLogInfo("Found auth for URL " + request.m_url); have_auth = true; break; } } if (!have_auth) { CaretLogInfo("NO AUTH FOUND for URL " + request.m_url); } QNetworkReply* myReply = NULL; QUrl myUrl = QUrl::fromUserInput(request.m_url); 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); } QNetworkAccessManager* myQNetMgr = &(myCaretMgr->m_netMgr); bool first = true; QByteArray postData; CaretPointer postUploadFile; // file needs to remain in scope until upload finished switch (request.m_method) { case POST_ARGUMENTS: { for (int32_t i = 0; i < (int32_t)request.m_arguments.size(); ++i) { if (!first) postData += "&"; if (request.m_arguments[i].second == "") { postData += request.m_arguments[i].first; } else { //postData += request.m_arguments[i].first + "=" + request.m_arguments[i].second; postData += QUrl::toPercentEncoding(request.m_arguments[i].first) + "=" + QUrl::toPercentEncoding(request.m_arguments[i].second); } first = false; } myRequest.setHeader(QNetworkRequest::ContentTypeHeader, "application/x-www-form-urlencoded"); 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.setUrl(myUrl); myReply = myQNetMgr->post(myRequest, postData); CaretLogInfo("POST ARGUMENTS URL: " + myUrl.toString()); } break; case POST_FILE: { postUploadFile.grabNew(new QFile(request.m_uploadFileName)); if (postUploadFile->open(QFile::ReadOnly)) { //myRequest.setHeader(QNetworkRequest::ContentTypeHeader, "application/x-www-form-urlencoded"); for (std::map::const_iterator headerIter = request.m_headers.begin(); headerIter != request.m_headers.end(); headerIter++) { myRequest.setRawHeader(headerIter->first.toAscii(), headerIter->second.toAscii()); // std::cout << "POST FILE header: " << qPrintable(headerIter->first) // << ": " << qPrintable(headerIter->second) << std::endl; } myRequest.setUrl(myUrl); myReply = myQNetMgr->post(myRequest, postUploadFile); CaretLogInfo("POST FILE URL: " + myUrl.toString()); } else { } } break; case GET: for (int32_t i = 0; i < (int32_t)request.m_arguments.size(); ++i) { myUrl.addQueryItem(request.m_arguments[i].first, request.m_arguments[i].second); } myRequest.setUrl(myUrl); CaretLogInfo("GET URL: " + myUrl.toString()); myReply = myQNetMgr->get(myRequest); break; case HEAD: for (int32_t i = 0; i < (int32_t)request.m_arguments.size(); ++i) { myUrl.addQueryItem(request.m_arguments[i].first, request.m_arguments[i].second); } myRequest.setUrl(myUrl); CaretLogInfo("HEAD URL: " + myUrl.toString()); myReply = myQNetMgr->head(myRequest); break; }; //QObject::connect(myReply, SIGNAL(sslErrors(QList)), &myLoop, SLOT(quit())); //QObject::connect(myQNetMgr, SIGNAL(authenticationRequired(QNetworkReply*,QAuthenticator*)), myCaretMgr, SLOT(authenticationCallback(QNetworkReply*,QAuthenticator*))); QObject::connect(myReply, SIGNAL(finished()), &myLoop, SLOT(quit()));//this is safe, because nothing will hand this thread events except queued through this thread's event mechanism /*QObject::connect(myReply, SIGNAL(sslErrors(const QList & )), CaretHttpManager::getHttpManager(), SLOT(handleSslErrors(const QList & )));//*/ myLoop.exec();//so, they can only be delivered after myLoop.exec() starts response.m_method = request.m_method; response.m_ok = false; response.m_redirectionUrlValid = false; response.m_responseCode = -1; response.m_responseCodeValid = false; response.m_headers.clear(); const QVariant responseCodeVariant = myReply->attribute(QNetworkRequest::HttpStatusCodeAttribute); if ( ! responseCodeVariant.isNull()) { response.m_responseCode = myReply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(); response.m_responseCodeValid = true; } if (response.m_responseCode == 200) { response.m_ok = true; } else { QVariant redirectionTarget = myReply->attribute(QNetworkRequest::RedirectionTargetAttribute); if ( ! redirectionTarget.isNull()) { response.m_redirectionUrl = myUrl.resolved(redirectionTarget.toUrl()); if (response.m_redirectionUrl.isValid()) { response.m_redirectionUrlValid = true; } } } getHeaders(*myReply, response.m_headers); const bool showHeaderValues = false; if (showHeaderValues || (response.m_responseCode != 200)) { logHeadersFromRequest(myRequest, request); logHeadersFromReply(*myReply, request, response); } QByteArray myBody = myReply->readAll(); int64_t mySize = myBody.size(); response.m_body.reserve(mySize + 1);//make room for the null terminator that will sometimes be added to the end response.m_body.resize(mySize);//but don't set size to include it for (int64_t i = 0; i < mySize; ++i) { response.m_body[i] = myBody[(int)i];//because QByteArray apparently just uses int - hope we won't need to transfer 2GB on a system that uses int32 for this } delete myReply; } void CaretHttpManager::setAuthentication(const AString& url, const AString& user, const AString& password) { CaretHttpManager* myCaretMgr = getHttpManager(); AString myServerString = getServerString(url); CaretLogInfo("Setting auth for server " + myServerString); for (int i = 0; i < (int)myCaretMgr->m_authList.size(); ++i) { if (myServerString == myCaretMgr->m_authList[i].m_serverString) {//for the moment, only allow one auth token per server in one instance of caret, so replace myCaretMgr->m_authList[i].m_user = user; myCaretMgr->m_authList[i].m_pass = password; return; } }//not found, need to add AuthEntry myAuth; myAuth.m_serverString = myServerString; myAuth.m_user = user; myAuth.m_pass = password; myCaretMgr->m_authList.push_back(myAuth); } void CaretHttpManager::handleSslErrors(QNetworkReply* reply, const QList &/*errors*/) { /*qDebug() << "handleSslErrors: "; foreach (QSslError e, errors) { qDebug() << "ssl error: " << e; }*/ reply->ignoreSslErrors(); } /*void CaretHttpManager::authenticationCallback(QNetworkReply* reply, QAuthenticator* authenticator) { if (reply->url() != QUrl::fromUserInput(m_authURL))//note: a redirect will cause this to break, this is ON PURPOSE so that auth isn't sent to a redirect { throw NetworkException("Authentication requested from different URL than authentication set for"); } authenticator->setUser(m_authUser); authenticator->setPassword(m_authPass); m_authURL = ""; m_authUser = ""; m_authPass = ""; }//*/ //currently not used, because callback doesn't work for the way xnat is set up, and doesn't fit well with synchronous requests AString CaretHttpManager::getServerString(const AString& url) { QUrl fullURL = QUrl::fromUserInput(url); AString ret = fullURL.toEncoded(QUrl::RemovePath | QUrl::StripTrailingSlash | QUrl::RemoveQuery | QUrl::RemoveUserInfo); return ret; } connectome-workbench-1.2.3+git41-gc4c6c90/src/Common/CaretHttpManager.h000066400000000000000000000063071300200146000253440ustar00rootroot00000000000000#ifndef __CARET_HTTP_MANAGER_H__ #define __CARET_HTTP_MANAGER_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 "stdint.h" #include "AString.h" namespace caret { struct CaretHttpRequest; struct CaretHttpResponse; class CaretHttpManager : public QObject { Q_OBJECT struct AuthEntry { AString m_serverString; AString m_user; AString m_pass; }; QNetworkAccessManager m_netMgr; CaretHttpManager(); static CaretHttpManager* m_singleton; std::vector m_authList; static AString getServerString(const AString& url); static void httpRequestPrivate(const CaretHttpRequest& request, CaretHttpResponse& response); static void getHeaders(const QNetworkReply& reply, std::map& headersOut); public: enum Method { GET, POST_ARGUMENTS, POST_FILE, HEAD }; static CaretHttpManager* getHttpManager(); static void deleteHttpManager(); static void httpRequest(const CaretHttpRequest& request, CaretHttpResponse& response); static QNetworkAccessManager* getQNetManager(); static void setAuthentication(const AString& url, const AString& user, const AString& password); public slots: void handleSslErrors(QNetworkReply* reply, const QList &/*errors*/); //void authenticationCallback(QNetworkReply* reply, QAuthenticator* authenticator); }; struct CaretHttpResponse { CaretHttpManager::Method m_method; std::vector m_body; bool m_ok; int32_t m_responseCode; bool m_responseCodeValid; QUrl m_redirectionUrl; bool m_redirectionUrlValid; std::map m_headers; // map so that newer values replace older values }; struct CaretHttpRequest { CaretHttpManager::Method m_method; AString m_url; AString m_uploadFileName; // used when mode is POST_FILE std::vector > m_arguments, m_queries;//arguments go to post data if method is POST_ARGUMENTS, queries stay as queries std::map m_headers; // map so that newer values replace older values }; } #endif // __CARET_HTTP_MANAGER_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Common/CaretJsonObject.cxx000066400000000000000000000135551300200146000255500ustar00rootroot00000000000000 /*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 ""; } connectome-workbench-1.2.3+git41-gc4c6c90/src/Common/CaretJsonObject.h000066400000000000000000000036421300200146000251710ustar00rootroot00000000000000#ifndef __CARET_JSON_OBJECT_H__ #define __CARET_JSON_OBJECT_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" namespace caret { class CaretJsonObject : public CaretObject { public: CaretJsonObject(const AString& jsonText); virtual ~CaretJsonObject(); int32_t count() const; 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&); CaretJsonObject& operator=(const CaretJsonObject&); void processNameValueString(const AString& nameValueString); void trimLeadingTrailingDoubleQuotes(AString& text); std::map m_data; // ADD_NEW_MEMBERS_HERE }; #ifdef __CARET_JSON_OBJECT_DECLARE__ // #endif // __CARET_JSON_OBJECT_DECLARE__ } // namespace #endif //__CARET_JSON_OBJECT_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Common/CaretLogger.cxx000066400000000000000000000017241300200146000247220ustar00rootroot00000000000000 /*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*/ /* * Need so that static members are allocated */ #define __CARET_LOGGER_DEFINE__ #include "CaretLogger.h" #undef __CARET_LOGGER_DEFINE__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Common/CaretLogger.h000066400000000000000000000142461300200146000243520ustar00rootroot00000000000000#ifndef __CARET_LOGGER__H_ #define __CARET_LOGGER__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 "CaretFunctionName.h" #include "CaretObject.h" #include "Logger.h" namespace caret { /** * \brief The Caret Logger. * * This is the Caret Logger. Its single instance is created by * the LogManager. * * Do not use this class. Instead use the macro in this file * to send log messages at various levels. The macros are * designed so that the message is not created unless the * specific level (or below) is enabled. */ class CaretLogger { public: /** * Set the Caret logger * @param logger The logger's value. */ static void setLogger(Logger* logger) { CaretLogger::logger = logger; } /** * Get the Caret Logger. * @return The Caret logger */ inline static Logger* getLogger() { return CaretLogger::logger; } private: CaretLogger() { } ~CaretLogger() { } CaretLogger(const CaretLogger&); CaretLogger& operator=(const CaretLogger&); /** The caret logger. It is created by the LogManager. */ static Logger* logger; }; #ifdef __CARET_LOGGER_DEFINE__ Logger* CaretLogger::logger = NULL; #endif // __CARET_LOGGER_DEFINE__ } // namespace /** * \def CaretLogSevere * * Log a message at the SEVERE level. * Severe items typically prevent normal program execution. * @param TEXT * Text that is logged. */ #define CaretLogSevere(TEXT) \ ((caret::CaretLogger::getLogger()->isSevere()) \ ? caret::CaretLogger::getLogger()->log(caret::LogLevelEnum::SEVERE, __CARET_FUNCTION_NAME__, __FILE__, __LINE__, (TEXT)) \ : (void)0) /** * \def CaretLogWarning * * Log a message at the WARNING level. * Warning messages indicate potential problems. * @param TEXT * Text that is logged. */ #define CaretLogWarning(TEXT) \ ((caret::CaretLogger::getLogger()->isWarning()) \ ? caret::CaretLogger::getLogger()->log(caret::LogLevelEnum::WARNING, __CARET_FUNCTION_NAME__, __FILE__, __LINE__, (TEXT)) \ : (void)0) /** * \def CaretLogInfo * * Log a message at the INFO level. * Informational messages that may be helpful to end users. * @param TEXT * Text that is logged. */ #define CaretLogInfo(TEXT) \ ((caret::CaretLogger::getLogger()->isInfo()) \ ? caret::CaretLogger::getLogger()->log(caret::LogLevelEnum::INFO, __CARET_FUNCTION_NAME__, __FILE__, __LINE__, (TEXT)) \ : (void)0) /** * \def CaretLogConfig * * Log a message at the CONFIG level. * Configuration messages typically involve versions of * libraries, operating system, etc, and may help with * issues on specific configurations. * @param TEXT * Text that is logged. */ #define CaretLogConfig(TEXT) \ ((caret::CaretLogger::getLogger()->isConfig()) \ ? caret::CaretLogger::getLogger()->log(caret::LogLevelEnum::CONFIG, __CARET_FUNCTION_NAME__, __FILE__, __LINE__, (TEXT)) \ : (void)0) /** * \def CaretLogFine * * Log a message at the FINE level. * Fine messages are for developers such as minor, recoverable failures. * @param TEXT * Text that is logged. */ #define CaretLogFine(TEXT) \ ((caret::CaretLogger::getLogger()->isFine()) \ ? caret::CaretLogger::getLogger()->log(caret::LogLevelEnum::FINE, __CARET_FUNCTION_NAME__, __FILE__, __LINE__, (TEXT)) \ : (void)0) /** * \def CaretLogFiner * * Log a message at the FINER level. * Finer messages for for developers to provide detailed tracing messages. * Typically events and exceptions are logged at the this level. * @param TEXT * Text that is logged. */ #define CaretLogFiner(TEXT) \ ((caret::CaretLogger::getLogger()->isFiner()) \ ? caret::CaretLogger::getLogger()->log(caret::LogLevelEnum::FINER, __CARET_FUNCTION_NAME__, __FILE__, __LINE__, (TEXT)) \ : (void)0) /** * \def CaretLogFinest * * Log a message at the FINEST level. * Finest messages are for developers to provide highly detailed tracing messages. * @param TEXT * Text that is logged. */ #define CaretLogFinest(TEXT) \ ((caret::CaretLogger::getLogger()->isFinest()) \ ? caret::CaretLogger::getLogger()->log(caret::LogLevelEnum::FINEST, __CARET_FUNCTION_NAME__, __FILE__, __LINE__, (TEXT)) \ : (void)0) /** * \def CaretLogEntering * * Log a message indicating the function that one is in. * This is typically used when entering the function. * This message is logged at the FINER level. */ #define CaretLogEntering() \ ((caret::CaretLogger::getLogger()->isFiner()) \ ? caret::CaretLogger::getLogger()->entering(__CARET_FUNCTION_NAME__, __FILE__, __LINE__) \ : (void)0) /** * \def CaretLogExiting * * Log a message indicating the function that one is in. * This is typically used when entering the function. * This message is logged at the FINER level. */ #define CaretLogExiting() \ ((caret::CaretLogger::getLogger()->isFiner()) \ ? caret::CaretLogger::getLogger()->exiting(__CARET_FUNCTION_NAME__, __FILE__, __LINE__) \ : (void)0) /** * \def CaretLogThrowing * * Log a message at the FINER level for an exception class that is * derived from CaretException. * @param CARET_EXCEPTION * CaretException that is logged. */ #define CaretLogThrowing(CARET_EXCEPTION) \ ((caret::CaretLogger::getLogger()->isFiner()) \ ? caret::CaretLogger::getLogger()->throwingCaretException(__CARET_FUNCTION_NAME__, __FILE__, __LINE__, CARET_EXCEPTION) \ : (void)0) #endif //__CARET_LOGGER__H_ connectome-workbench-1.2.3+git41-gc4c6c90/src/Common/CaretMathExpression.cxx000066400000000000000000001110461300200146000264530ustar00rootroot00000000000000/*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 "CaretAssert.h" #include "CaretException.h" #include "CaretLogger.h" #include "CaretMathExpression.h" #include using namespace caret; using namespace std; CaretMathExpression::CaretMathExpression(const AString& expression) { m_input = expression; m_position = 0; m_end = m_input.size(); m_root = orExpr(); if (skipWhitespace())//if we DON'T hit the end of input, there are extra characters - like if the input is "x + 1 $blah" { throw CaretException("extra characters on end of expression input: '" + m_input.mid(m_position) + "'"); } CaretLogFiner("parsed '" + expression + "' as '" + toString() + "'"); } double CaretMathExpression::evaluate(const vector& variableValues) const { CaretAssert(variableValues.size() == m_varNames.size()); return m_root->eval(variableValues); } vector CaretMathExpression::getVarNames() const { vector ret(m_varNames.size()); for (map::const_iterator iter = m_varNames.begin(); iter != m_varNames.end(); ++iter) { CaretAssert(iter->second < (int)ret.size()); ret[iter->second] = iter->first; } return ret; } AString CaretMathExpression::getExpressionHelpInfo() { AString ret = AString("Expressions consist of constants, variables, operators, parentheses, and functions, in infix notation, such as 'exp(-x + 3) * scale'. ") + "Variables are strings of any length, using the characters a-z, A-Z, 0-9, and _, but may not take the name of a named constant. " + "Currently, there is only one named constant, PI. " + "The operators are +, -, *, /, ^, >, <, >=, <=, ==, !=, !, &&, ||. " + "These behave as in C, except that ^ is exponentiation, i.e. pow(x, y), and takes higher precedence than other binary operators (also, '-3^-4^-5' means '-(3^(-(4^-5)))'). " + "The <=, >=, ==, and != operators are given a small amount of wiggle room, equal to one millionth of the smaller of the absolute values of the values being compared.\n\n" + "Comparison and logical operators return 0 or 1, you can do masking with expressions like 'x * (mask > 0)'. " + "For all logical operators, an input is considered true iff it is greater than 0. " + "The expression '0 < x < 5' is not syntactically wrong, but it will NOT do what is desired, because it is evaluated left to right, i.e. '((0 < x) < 5)', " + "which will always return 1, as both possible results of a comparison are less than 5. A warning is generated if an expression of this type is detected. " + "Use something like 'x > 0 && x < 5' to get the desired behavior.\n\n" + "Whitespace between elements is ignored, ' sin ( 2 * x ) ' is equivalent to 'sin(2*x)', but 's in(2*x)' is an error. " + "Implied multiplication is not allowed, the expression '2x' will be parsed as a variable. " + "Parentheses are (), do not use [] or {}. " + "Functions require parentheses, the expression 'sin x' is an error.\n\n" + "The following functions are supported:\n\n"; vector myFuncs; MathFunctionEnum::getAllEnums(myFuncs); for (int i = 0; i < (int)myFuncs.size(); ++i) { ret += " " + MathFunctionEnum::toName(myFuncs[i]) + ": " + MathFunctionEnum::toExplanation(myFuncs[i]) + "\n"; } return ret; } double CaretMathExpression::MathNode::eval(const vector& values) const { double ret = 0.0; switch (m_type) { case OR: { int end = (int)m_arguments.size(); CaretAssert(end > 1); bool temp = (m_arguments[0]->eval(values) > 0.0); for (int i = 1; i < end; ++i) { if (temp) break;//lazy evaluation temp = (m_arguments[i]->eval(values) > 0.0);//don't need to actually do ||, we already know all the ones to the left are false } ret = temp ? 1.0 : 0.0; break; } case AND: { int end = (int)m_arguments.size(); CaretAssert(end > 1); bool temp = (m_arguments[0]->eval(values) > 0.0); for (int i = 1; i < end; ++i) { if (!temp) break;//lazy evaluation temp = (m_arguments[i]->eval(values) > 0.0);//don't need to actually do &&, we already know all the ones to the left are true } ret = temp ? 1.0 : 0.0; break; } case EQUAL: { int end = (int)m_arguments.size(); CaretAssert(end > 1); CaretAssert((int)m_invert.size() == end); ret = m_arguments[0]->eval(values); for (int i = 1; i < end; ++i) { double temp = m_arguments[i]->eval(values); float adjust = min(abs(ret), abs(temp)) / 1000000;//because == doesn't always work as expected, include a fudge factor based on the approximate precision of float bool equal = (ret >= m_arguments[i]->eval(values) - adjust) && (ret <= m_arguments[i]->eval(values) + adjust); if (m_invert[i]) { ret = equal ? 0.0 : 1.0; } else { ret = equal ? 1.0 : 0.0; } } break; } case GREATERLESS: { int end = (int)m_arguments.size();//yes, you can chain < and >, but it will produce a parsing warning, as it evaluates left to right CaretAssert(end > 1); CaretAssert((int)m_invert.size() == end); CaretAssert((int)m_inclusive.size() == end); ret = m_arguments[0]->eval(values); for (int i = 1; i < end; ++i) { double temp = m_arguments[i]->eval(values); if (m_inclusive[i]) { float adjust = min(abs(ret), abs(temp)) / 1000000;//because == doesn't always work as expected, include a fudge factor based on the approximate precision of float if (m_invert[i]) { ret = (ret <= temp + adjust ? 1.0 : 0.0);//don't trust booleans to cast to 0 and 1, just because } else { ret = (ret >= temp - adjust ? 1.0 : 0.0); } } else { if (m_invert[i]) { ret = (ret < temp ? 1.0 : 0.0); } else { ret = (ret > temp ? 1.0 : 0.0); } } } break; } case ADDSUB: { int end = (int)m_arguments.size(); CaretAssert(end > 1); CaretAssert((int)m_invert.size() == end); ret = m_arguments[0]->eval(values); for (int i = 1; i < end; ++i) { if (m_invert[i]) { ret -= m_arguments[i]->eval(values); } else { ret += m_arguments[i]->eval(values); } } break; } case MULTDIV: { int end = (int)m_arguments.size(); CaretAssert(end > 1); CaretAssert((int)m_invert.size() == end); ret = m_arguments[0]->eval(values); for (int i = 1; i < end; ++i) { if (m_invert[i]) { ret /= m_arguments[i]->eval(values); } else { ret *= m_arguments[i]->eval(values); } } break; } case NOT: CaretAssert(m_arguments.size() == 1); ret = ((m_arguments[0]->eval(values)) > 0.0) ? 0.0 : 1.0; break; case NEGATE: CaretAssert(m_arguments.size() == 1); ret = -(m_arguments[0]->eval(values)); break; case POW: { CaretAssert(m_arguments.size() == 2);//a^b^c always gets parsed into 2 power nodes, because -a^-b is -(a^(-b)) ret = pow(m_arguments[0]->eval(values), m_arguments[1]->eval(values)); break; } case FUNC: { switch (m_function)//this could be (partly) moved into MathFunctionEnum, but it wouldn't strictly be an enum class then { case MathFunctionEnum::SIN: CaretAssert(m_arguments.size() == 1); ret = sin(m_arguments[0]->eval(values)); break; case MathFunctionEnum::COS: CaretAssert(m_arguments.size() == 1); ret = cos(m_arguments[0]->eval(values)); break; case MathFunctionEnum::TAN: CaretAssert(m_arguments.size() == 1); ret = tan(m_arguments[0]->eval(values)); break; case MathFunctionEnum::ASIN: CaretAssert(m_arguments.size() == 1); ret = asin(m_arguments[0]->eval(values)); break; case MathFunctionEnum::ACOS: CaretAssert(m_arguments.size() == 1); ret = acos(m_arguments[0]->eval(values)); break; case MathFunctionEnum::ATAN: CaretAssert(m_arguments.size() == 1); ret = atan(m_arguments[0]->eval(values)); break; case MathFunctionEnum::SINH: CaretAssert(m_arguments.size() == 1); ret = sinh(m_arguments[0]->eval(values)); break; case MathFunctionEnum::COSH: CaretAssert(m_arguments.size() == 1); ret = cosh(m_arguments[0]->eval(values)); break; case MathFunctionEnum::TANH: CaretAssert(m_arguments.size() == 1); ret = tanh(m_arguments[0]->eval(values)); break; case MathFunctionEnum::ASINH: { CaretAssert(m_arguments.size() == 1); //ret = asinh(m_arguments[0].eval(values));//will work, and be preferred, when we use c++11, but doesn't work on windows with previous standard double arg = m_arguments[0]->eval(values); if (arg > 0) { ret = log(arg + sqrt(arg * arg + 1)); } else { ret = -log(-arg + sqrt(arg * arg + 1));//special case negative for stability in large negatives } break; } case MathFunctionEnum::ACOSH: { CaretAssert(m_arguments.size() == 1); //ret = acosh(m_arguments[0].eval(values)); double arg = m_arguments[0]->eval(values); ret = log(arg + sqrt(arg * arg - 1)); break; } case MathFunctionEnum::ATANH: { CaretAssert(m_arguments.size() == 1); //ret = atanh(m_arguments[0].eval(values)); double arg = m_arguments[0]->eval(values); ret = 0.5 * log((1 + arg) / (1 - arg)); break; } case MathFunctionEnum::LN: CaretAssert(m_arguments.size() == 1); ret = log(m_arguments[0]->eval(values)); break; case MathFunctionEnum::EXP: CaretAssert(m_arguments.size() == 1); ret = exp(m_arguments[0]->eval(values)); break; case MathFunctionEnum::LOG: CaretAssert(m_arguments.size() == 1); ret = log10(m_arguments[0]->eval(values)); break; case MathFunctionEnum::SQRT: CaretAssert(m_arguments.size() == 1); ret = sqrt(m_arguments[0]->eval(values)); break; case MathFunctionEnum::ABS: CaretAssert(m_arguments.size() == 1); ret = abs(m_arguments[0]->eval(values)); break; case MathFunctionEnum::FLOOR: CaretAssert(m_arguments.size() == 1); ret = floor(m_arguments[0]->eval(values)); break; case MathFunctionEnum::ROUND: { CaretAssert(m_arguments.size() == 1); double temp = m_arguments[0]->eval(values);//windows doesn't use c99 when compiling c++ earlier than c++11, so implement manually if (temp > 0.0) { ret = floor(temp + 0.5); } else { ret = ceil(temp - 0.5); } break; } case MathFunctionEnum::CEIL: CaretAssert(m_arguments.size() == 1); ret = ceil(m_arguments[0]->eval(values)); break; case MathFunctionEnum::ATAN2: CaretAssert(m_arguments.size() == 2); ret = atan2(m_arguments[0]->eval(values), m_arguments[1]->eval(values)); break; case MathFunctionEnum::MIN: { CaretAssert(m_arguments.size() == 2); ret = m_arguments[0]->eval(values); double other = m_arguments[1]->eval(values); if (ret > other) ret = other; break; } case MathFunctionEnum::MAX: { CaretAssert(m_arguments.size() == 2); ret = m_arguments[0]->eval(values); double other = m_arguments[1]->eval(values); if (ret < other) ret = other; break; } case MathFunctionEnum::MOD: { CaretAssert(m_arguments.size() == 2); double second = m_arguments[1]->eval(values); if (second == 0.0) { ret = 0.0; } else { double first = m_arguments[0]->eval(values); ret = first - second * floor(first / second); } break; } case MathFunctionEnum::CLAMP: { CaretAssert(m_arguments.size() == 3); ret = m_arguments[0]->eval(values); double low = m_arguments[1]->eval(values); double high = m_arguments[2]->eval(values); if (ret < low) { ret = low; } if (ret > high) { ret = high; } break; } case INVALID: CaretAssertMessage(0, "MathNode is type FUNC but INVALID function"); throw CaretException("parsing problem in CaretMathExpression"); } break; } case VAR: CaretAssertVectorIndex(values, m_varIndex); ret = values[m_varIndex]; break; case CONST: ret = m_constVal; break; case INVALID: CaretAssertMessage(0, "parsing left INVALID MathNode"); throw CaretException("parsing problem in CaretMathExpression"); } return ret; } AString CaretMathExpression::MathNode::toString(const std::vector& varNames) const { AString ret = ""; bool addParens = true; switch (m_type) { case OR: { int end = (int)m_arguments.size(); CaretAssert(end > 1); ret = m_arguments[0]->toString(varNames); for (int i = 1; i < end; ++i) { ret += "||" + m_arguments[i]->toString(varNames); } break; } case AND: { int end = (int)m_arguments.size(); CaretAssert(end > 1); ret = m_arguments[0]->toString(varNames); for (int i = 1; i < end; ++i) { ret += "&&" + m_arguments[i]->toString(varNames); } break; } case EQUAL: { int end = (int)m_arguments.size(); CaretAssert(end > 1); CaretAssert((int)m_invert.size() == end); ret = m_arguments[0]->toString(varNames); for (int i = 1; i < end; ++i) { if (m_invert[i]) { ret += "!=" + m_arguments[i]->toString(varNames); } else { ret += "==" + m_arguments[i]->toString(varNames); } } break; } case GREATERLESS: { int end = (int)m_arguments.size(); CaretAssert(end > 1); CaretAssert((int)m_invert.size() == end); CaretAssert((int)m_inclusive.size() == end); ret = m_arguments[0]->toString(varNames); for (int i = 1; i < end; ++i) { if (m_inclusive[i]) { if (m_invert[i]) { ret += "<=" + m_arguments[i]->toString(varNames); } else { ret += ">=" + m_arguments[i]->toString(varNames); } } else { if (m_invert[i]) { ret += "<" + m_arguments[i]->toString(varNames); } else { ret += ">" + m_arguments[i]->toString(varNames); } } } break; } case ADDSUB: { int end = (int)m_arguments.size(); CaretAssert(end > 1); CaretAssert((int)m_invert.size() == end); ret = m_arguments[0]->toString(varNames); for (int i = 1; i < end; ++i) { if (m_invert[i]) { ret += "-" + m_arguments[i]->toString(varNames); } else { ret += "+" + m_arguments[i]->toString(varNames); } } break; } case MULTDIV: { int end = (int)m_arguments.size(); CaretAssert(end > 1); CaretAssert((int)m_invert.size() == end); ret = m_arguments[0]->toString(varNames); for (int i = 1; i < end; ++i) { if (m_invert[i]) { ret += "/" + m_arguments[i]->toString(varNames); } else { ret += "*" + m_arguments[i]->toString(varNames); } } break; } case NOT: CaretAssert(m_arguments.size() == 1); ret = "!" + m_arguments[0]->toString(varNames); break; case NEGATE: CaretAssert(m_arguments.size() == 1); ret = "-" + m_arguments[0]->toString(varNames); break; case POW: CaretAssert(m_arguments.size() == 2); ret = m_arguments[0]->toString(varNames) + "^" + m_arguments[1]->toString(varNames); break; case FUNC: { addParens = false; int end = (int)m_arguments.size(); ret = MathFunctionEnum::toName(m_function) + "("; if (end > 0) ret += m_arguments[0]->toString(varNames);//allow for functions that take 0 arguments for (int i = 1; i < end; ++i) { ret += "," + m_arguments[i]->toString(varNames); } ret += ")"; break; } case VAR: addParens = false; CaretAssertVectorIndex(varNames, m_varIndex); ret = varNames[m_varIndex]; break; case CONST: addParens = false; if (m_constName != "") { ret = m_constName; } else { ret = AString::number(m_constVal, 'g', 15); } break; case INVALID: CaretAssertMessage(0, "toString called on invalid MathNode"); throw CaretException("parsing problem in CaretMathExpression"); } if (addParens) ret = "(" + ret + ")";//parenthesize almost everything return ret; } AString CaretMathExpression::toString() const { return m_root->toString(getVarNames()); } bool CaretMathExpression::skipWhitespace()//return false if end of input { while (m_position < m_end && m_input[m_position].isSpace()) ++m_position; return m_position < m_end; } bool CaretMathExpression::accept(const char& c) { if (!skipWhitespace()) return false;//end of input if (m_input[m_position] == c) { ++m_position; return true; } return false; } void CaretMathExpression::expect(const char& c, const int& exprStart) { CaretAssert(exprStart < m_end); if (!skipWhitespace()) throw CaretException("unexpected end of input while parsing '" + m_input.mid(exprStart) + "', expected '" + AString(c) + "'"); if (m_input[m_position] != c) throw CaretException("expected '" + AString(c) + "', got '" + m_input[m_position] + "' while parsing '" + m_input.mid(exprStart, m_position - exprStart + 1) + "'"); ++m_position; } CaretPointer CaretMathExpression::orExpr() { int start = m_position;//for error reporting CaretPointer temp = andExpr(); if (accept('|')) { CaretPointer ret(new MathNode(MathNode::OR)); ret->m_arguments.push_back(temp); do { expect('|', start); ret->m_arguments.push_back(andExpr()); } while (accept('|')); return ret; } return temp; } CaretPointer CaretMathExpression::andExpr() { int start = m_position;//for error reporting CaretPointer temp = equalExpr(); if (accept('&')) { CaretPointer ret(new MathNode(MathNode::AND)); ret->m_arguments.push_back(temp); do { expect('&', start); ret->m_arguments.push_back(equalExpr()); } while (accept('&')); return ret; } return temp; } CaretPointer CaretMathExpression::equalExpr() { int start = m_position;//for error reporting CaretPointer temp = greaterLessExpr(); bool good = false, invert;//means not equal if (accept('=')) { good = true; invert = false; } else if (accept('!')) { good = true; invert = true; } if (good) { CaretPointer ret(new MathNode(MathNode::EQUAL)); ret->m_arguments.push_back(temp); ret->m_invert.push_back(false);//first one isn't used, they apply to the operator to the left of the argument do { expect('=', start); ret->m_arguments.push_back(greaterLessExpr()); ret->m_invert.push_back(invert); good = false; if (accept('=')) { good = true; invert = false; } else if (accept('!')) { good = true; invert = true; } } while (good); if (ret->m_arguments.size() > 2) { CaretLogWarning("expression '" + m_input.mid(start, m_position - start) + "' will do equality comparison left to right"); } return ret; } return temp; } CaretPointer CaretMathExpression::greaterLessExpr() { int start = m_position;//for error reporting CaretPointer temp = addSubExpr(); bool good = false, invert, inclusive = false;//invert means less than if (accept('>')) { good = true; invert = false; } else if (accept('<')) { good = true; invert = true; } if (good && accept('=')) inclusive = true; if (good) { CaretPointer ret(new MathNode(MathNode::GREATERLESS)); ret->m_arguments.push_back(temp); ret->m_invert.push_back(false);//first one isn't used, they apply to the operator to the left of the argument ret->m_inclusive.push_back(false);//ditto do { ret->m_arguments.push_back(addSubExpr()); ret->m_invert.push_back(invert); ret->m_inclusive.push_back(inclusive); good = false; inclusive = false; if (accept('>')) { good = true; invert = false; } else if (accept('<')) { good = true; invert = true; } if (good && accept('=')) inclusive = true; } while (good); if (ret->m_arguments.size() > 2) { CaretLogWarning("expression '" + m_input.mid(start, m_position - start) + "' will do inequality comparison left to right"); } return ret; } return temp; } CaretPointer CaretMathExpression::addSubExpr() { CaretPointer temp = multDivExpr(); bool good = false, invert;//means subtract if (accept('+')) { good = true; invert = false; } else if (accept('-')) { good = true; invert = true; } if (good) { CaretPointer ret(new MathNode(MathNode::ADDSUB)); ret->m_arguments.push_back(temp); ret->m_invert.push_back(false);//first one isn't used, they apply to the operator to the left of the argument do { ret->m_arguments.push_back(multDivExpr()); ret->m_invert.push_back(invert); good = false; if (accept('+')) { good = true; invert = false; } else if (accept('-')) { good = true; invert = true; } } while (good); return ret; } return temp; } CaretPointer CaretMathExpression::multDivExpr() { CaretPointer temp = unaryExpr(); bool good = false, invert;//means divide if (accept('*')) { good = true; invert = false; } else if (accept('/')) { good = true; invert = true; } if (good) { CaretPointer ret(new MathNode(MathNode::MULTDIV)); ret->m_arguments.push_back(temp); ret->m_invert.push_back(false);//first one isn't used, they apply to the operator to the left of the argument do { ret->m_arguments.push_back(unaryExpr()); ret->m_invert.push_back(invert); good = false; if (accept('*')) { good = true; invert = false; } else if (accept('/')) { good = true; invert = true; } } while (good); return ret; } return temp; } CaretPointer CaretMathExpression::unaryExpr() { CaretPointer ret; if (accept('-')) { ret.grabNew(new MathNode(MathNode::NEGATE)); ret->m_arguments.push_back(unaryExpr());//allow -----x and other silliness, because it is more complicated to try to prevent it return ret; } else if (accept('!')) { ret.grabNew(new MathNode(MathNode::NOT)); ret->m_arguments.push_back(unaryExpr());//similarly, !!!!x...and !!--!---!!!!-!!-!!!1, etc return ret; } else if (accept('+')) {//unary plus does nothing return unaryExpr();//this is not infinitely recursive, accept() removes a char of input } return powExpr(); } CaretPointer CaretMathExpression::powExpr() { CaretPointer temp = funcExpr(); if (accept('^')) { CaretPointer ret(new MathNode(MathNode::POW)); ret->m_arguments.push_back(temp); ret->m_arguments.push_back(unaryExpr());//because -x^y is -(x^y), but x^-y is x^(-y) return ret; } return temp; } CaretPointer CaretMathExpression::funcExpr() { int start = m_position; if (accept('(')) { CaretPointer ret = orExpr(); expect(')', start); return ret; }//and now a non-predictive bit - checking for valid function name int funcnameEnd = m_input.indexOf('(', m_position); if (funcnameEnd != -1) { bool ok = false; MathFunctionEnum::Enum myfunc = MathFunctionEnum::fromName(m_input.mid(m_position, funcnameEnd - m_position).trimmed(), &ok); if (ok) { int numArgs = -1; switch(myfunc) { case MathFunctionEnum::SIN: case MathFunctionEnum::COS: case MathFunctionEnum::TAN: case MathFunctionEnum::ASIN: case MathFunctionEnum::ACOS: case MathFunctionEnum::ATAN: case MathFunctionEnum::SINH: case MathFunctionEnum::COSH: case MathFunctionEnum::TANH: case MathFunctionEnum::ASINH: case MathFunctionEnum::ACOSH: case MathFunctionEnum::ATANH: case MathFunctionEnum::LN: case MathFunctionEnum::EXP: case MathFunctionEnum::LOG: case MathFunctionEnum::SQRT: case MathFunctionEnum::ABS: case MathFunctionEnum::FLOOR: case MathFunctionEnum::ROUND: case MathFunctionEnum::CEIL: numArgs = 1; break; case MathFunctionEnum::ATAN2: case MathFunctionEnum::MIN: case MathFunctionEnum::MAX: case MathFunctionEnum::MOD: numArgs = 2; break; case MathFunctionEnum::CLAMP: numArgs = 3; break; case MathFunctionEnum::INVALID://this should not happen CaretAssert(false); } CaretPointer ret(new MathNode(MathNode::FUNC)); ret->m_function = myfunc; m_position = funcnameEnd + 1;//skip the ( of the function if (!accept(')'))//zero arguments case { ret->m_arguments.push_back(orExpr()); while (accept(',')) { ret->m_arguments.push_back(orExpr()); } expect(')', start); } if ((int)ret->m_arguments.size() != numArgs) throw CaretException("function '" + MathFunctionEnum::toName(myfunc) + "' takes " + AString::number(numArgs) + " argument(s), but was given " + AString::number(ret->m_arguments.size()) + ": '" + m_input.mid(start, m_position - start) + "'"); return ret; } } return terminal();//if it isn't parenthesis or a function, the only thing left is terminal } CaretPointer CaretMathExpression::terminal() { CaretPointer ret = tryLiteral();//try literal first, our relaxed rules for variable names overlap with integers if (ret != NULL) return ret; if (!skipWhitespace()) throw CaretException("unexpected end of input, expected operand");//now, try named constant/variable int varEnd = m_position; bool onlydigits = true; while (varEnd < m_end) { QChar thisChar = m_input[varEnd];//allow letters, numbers, and underscore if (!thisChar.isLetterOrNumber() && thisChar != '_') { break; } if (!thisChar.isDigit()) onlydigits = false;//if there are only digits, this is not a variable, tryLiteral failed on something that was intended to be a literal ++varEnd; } double constVal = 0.0f; AString identifier = m_input.mid(m_position, varEnd - m_position); if (identifier.size() == 0)//hit an invalid character or end of input before getting any valid characters {//figure out why we stopped, give appropriate error if (varEnd >= m_end) throw CaretException("unexpected end of input, expected operand"); throw CaretException("unexpected character '" + AString(m_input[varEnd]) + "' at beginning of operand");//if it fails for all prefix unary, parens, function, literal, variable, then this character can never start an operand } if (onlydigits) throw CaretException("error parsing literal value beginning with '" + identifier + "'"); m_position = varEnd; if (getNamedConstant(identifier, constVal)) { ret.grabNew(new MathNode(MathNode::CONST)); ret->m_constName = identifier; ret->m_constVal = constVal; return ret; } ret.grabNew(new MathNode(MathNode::VAR)); map::const_iterator iter = m_varNames.find(identifier); if (iter == m_varNames.end()) { int index = (int)m_varNames.size(); m_varNames.insert(make_pair(identifier, index)); ret->m_varIndex = index; } else { ret->m_varIndex = iter->second; } return ret; } CaretPointer CaretMathExpression::tryLiteral() { if (!skipWhitespace()) throw CaretException("unexpected end of input, expected operand");//now, try literal int litstart = m_position, litend = m_position; if (m_input[litend] == '-' || m_input[litend] == '+') ++litend;//allow literals to start with - or + : however, - will not happen, due to unary - (which does that so that -2^-2 works as -(2^(-2)) bool havedot = false, haveexp = false; while (litend < m_end) { QChar mychar = m_input[litend]; if (mychar.isDigit() || mychar == '.')//manually test for multiple '.' for better error messages, it can never be correct as a non-literal { ++litend; if (mychar == '.') { if (havedot) throw CaretException("multiple '.' characters in literal: '" + m_input.mid(litstart, litend - litstart) + "'"); havedot = true; } continue; } if (mychar == 'e' || mychar == 'E') {//scientific notation, don't throw on multiple 'e', because '2e-3e' is subtraction of two variables if (haveexp) return CaretPointer();//but do stop early, as it won't be a literal haveexp = true; ++litend; if (litend >= m_end) break; if (m_input[litend] == '-' || m_input[litend] == '+') ++litend;//allow + or - after e continue; }//spaces are not allowed inside literals break;//yes, break at the end of a while loop - anything we don't recognize as valid ends the loop } bool ok = false; double litVal = m_input.mid(litstart, litend - litstart).toDouble(&ok); if (!ok) return CaretPointer(); m_position = litend; CaretPointer ret(new MathNode(MathNode::CONST)); ret->m_constVal = litVal; return ret; } bool CaretMathExpression::getNamedConstant(const AString& name, double& valueOut) { if (name == "PI") { valueOut = 3.1415926535897932;//double can do about 16 decimal digits, give 17 for rounding return true; } return false;//presumably we will have more named constants later } connectome-workbench-1.2.3+git41-gc4c6c90/src/Common/CaretMathExpression.h000066400000000000000000000066621300200146000261070ustar00rootroot00000000000000#ifndef __CARET_MATH_EXPRESSION_H__ #define __CARET_MATH_EXPRESSION_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 "AString.h" #include "CaretPointer.h" #include "MathFunctionEnum.h" #include #include namespace caret { class CaretMathExpression { struct MathNode { enum ExprType { INVALID, OR, AND, EQUAL, GREATERLESS, ADDSUB, MULTDIV, NOT, NEGATE, POW, FUNC, VAR, CONST }; ExprType m_type; MathFunctionEnum::Enum m_function; AString m_constName; double m_constVal; int m_varIndex; std::vector m_invert;//whether it is subtract rather than add, or divide instead of multiply, or less instead of greater std::vector m_inclusive;//whether greater/less includes equals std::vector > m_arguments; MathNode() { m_type = INVALID; m_function = MathFunctionEnum::INVALID; } MathNode(const ExprType& type) { m_type = type; m_function = MathFunctionEnum::INVALID; } double eval(const std::vector& values) const; AString toString(const std::vector& varNames) const; }; std::map m_varNames; AString m_input; int m_position, m_end; CaretPointer m_root; bool skipWhitespace(); bool accept(const char& c); void expect(const char& c, const int& exprStart); CaretPointer orExpr();//hopefully we have enough stack space that we won't overflow without a hundred or so levels of parenthesis/functions/exponents CaretPointer andExpr(); CaretPointer equalExpr(); CaretPointer greaterLessExpr(); CaretPointer addSubExpr(); CaretPointer multDivExpr(); CaretPointer unaryExpr();//accepts --x, !!1, !--!-!!PI, etc CaretPointer powExpr(); CaretPointer funcExpr();//also parenthesis CaretPointer terminal();//literal, const, variable CaretPointer tryLiteral();//NOTE: does not throw except on early end of input, returns NULL on failure public: static AString getExpressionHelpInfo(); static bool getNamedConstant(const AString& name, double& valueOut); CaretMathExpression(const AString& expression); double evaluate(const std::vector& variableValues) const; std::vector getVarNames() const; AString toString() const;//the expression, with a lot of parentheses added }; } // namespace #endif //__CARET_MATH_EXPRESSION_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Common/CaretMutex.h000066400000000000000000000046351300200146000242360ustar00rootroot00000000000000/*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*/ #ifndef __CARET_MUTEX_H__ #define __CARET_MUTEX_H__ #include "CaretOMP.h" //omp mutexes are faster than QMutex, especially without contention #ifdef CARET_OMP namespace caret { class CaretMutex { omp_lock_t m_lock; public: CaretMutex(const CaretMutex&) { omp_init_lock(&m_lock); };//allow copy, assign, but make them do nothing other than default construct CaretMutex& operator=(const CaretMutex&) { return *this; }; CaretMutex() { omp_init_lock(&m_lock); } ~CaretMutex() { omp_destroy_lock(&m_lock); } friend class CaretMutexLocker; }; class CaretMutexLocker { CaretMutex* m_mutex; CaretMutexLocker();//disallow default construction, assign CaretMutexLocker& operator=(const CaretMutexLocker& rhs); public: CaretMutexLocker(CaretMutex* mutex) { m_mutex = mutex; omp_set_lock(&(m_mutex->m_lock)); } ~CaretMutexLocker() { omp_unset_lock(&(m_mutex->m_lock)); } }; } #else //if we don't have openmp, fall back to QMutex #include namespace caret { class CaretMutex : public QMutex { public: CaretMutex(RecursionMode mode = NonRecursive) : QMutex(mode) { } CaretMutex(const CaretMutex&) : QMutex() { };//allow copy, assign, but make them do nothing other than default construct CaretMutex& operator=(const CaretMutex&) { return *this; }; }; class CaretMutexLocker : public QMutexLocker { public: CaretMutexLocker(CaretMutex* theMutex) : QMutexLocker(theMutex) { } }; } #endif #endif //__CARET_MUTEX_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Common/CaretOMP.h000066400000000000000000000042621300200146000235630ustar00rootroot00000000000000#ifndef __CARET_OMP_H__ #define __CARET_OMP_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*/ ///this include file is for anything specific to how we use openmp in caret, and also for convenience ///for instance, you don't need #ifdef guards around including this file, unlike omp.h #ifdef _OPENMP #include "omp.h" ///another define to use in guards, might be easier to remember #define CARET_OMP #endif //_OPENMP ///some defines for #pragma omp, in case we want to add default options (for workarounds or global options) ///if we never need to use these for workarounds or global options, so be it ///NOTE: neither "#pragma" nor "omp" can be put into a define and have it work correctly, so they can't be made that much more friendly ///defined regardless so the preprocessor and compiler aren't any more confused if _OPENMP isn't defined (will complain about ignoring pragma if used without guards) ///DO NOT add scheduling to this list, scheduling choice needs to be able to be changed #define CARET_PAR_OPTIONS #define CARET_FOR_OPTIONS #define CARET_SINGLE_OPTIONS ///and defines to combine them with the pragmas they are intended for ///use them as "#pragma omp CARET_PARFOR [other options]" #define CARET_PAR parallel CARET_PAR_OPTIONS #define CARET_FOR for CARET_FOR_OPTIONS #define CARET_PARFOR parallel for CARET_PAR_OPTIONS CARET_FOR_OPTIONS #define CARET_SINGLE single CARET_SINGLE_OPTIONS #endif //__CARET_OMP_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Common/CaretObject.cxx000066400000000000000000000103121300200146000247020ustar00rootroot00000000000000/*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 #define __CARET_OBJECT_DECLARE_H__ #include "CaretObject.h" #undef __CARET_OBJECT_DECLARE_H__ #include "SystemUtilities.h" using namespace caret; /** * Constructor. * */ CaretObject::CaretObject() { this->initializeMembersCaretObject(); } /** * Copy constructor. * */ CaretObject::CaretObject(const CaretObject& co) { this->initializeMembersCaretObject(); this->copyHelper(co); } /** * Destructor */ CaretObject::~CaretObject() { #ifndef NDEBUG /* * Erase returns the number of objects deleted. * If zero, then the object has already been deleted. */ uint64_t numDeleted = CaretObject::allocatedObjects.erase(this); if (numDeleted <= 0) { std::cerr << "Destructor for a CaretObject called but the object is not allocated " << "and this implies that the object has already been deleted."; } #endif } CaretObject& CaretObject::operator=(const CaretObject& co) { if (this != &co) { this->copyHelper(co); } return *this; } void CaretObject::initializeMembersCaretObject() { #ifndef NDEBUG SystemBacktrace myBacktrace; SystemUtilities::getBackTrace(myBacktrace); CaretObject::allocatedObjects.insert( std::make_pair(this, myBacktrace)); /*CaretObject::allocatedObjects.insert( std::make_pair(this, SystemUtilities::getBackTrace()));//*/ #endif } void CaretObject::copyHelper(const CaretObject&) { } /** * Get String representation of caret object. * @return String containing caret object. * */ AString CaretObject::toString() const { AString s = "CaretObjectType=" + this->className(); return s; } /** * Get the class name of this object. * @return * Class name of the object. */ AString CaretObject::className() const { AString name(typeid(*this).name()); return name; } /** * Print a list of CaretObjects that were not deleted. */ void CaretObject::printListOfObjectsNotDeleted(const bool showCallStack) { #ifndef NDEBUG int count = 0; if (CaretObject::allocatedObjects.empty() == false) { std::cout << "These Caret Objects were not deleted:" << std::endl; for (CARET_OBJECT_TRACKER_MAP_ITERATOR iter = CaretObject::allocatedObjects.begin(); iter != allocatedObjects.end(); iter++) { const unsigned long objectAddress = (long long)iter->first; //const CaretObject* caretObject = iter->first; const CaretObjectInfo& caretObjectInfo = iter->second; // below will crash if item has been deleted //std::cout << caretObject->toString().toStdString() << std::endl; std::cout << "Address (hex)=" << std::hex << objectAddress << std::endl; if (showCallStack) { std::cout << caretObjectInfo.m_backtrace.toSymbolString() << std::endl; } std::cout << std::endl; count++; } } if (count > 0) { std::cout << std::dec << count << " objects were not deleted." << std::endl; } #endif } /** * Constructor. * @param backtrace * The backtrace. */ CaretObject::CaretObjectInfo::CaretObjectInfo(const SystemBacktrace& backtrace) { m_backtrace = backtrace; } /** * Destructor. */ CaretObject::CaretObjectInfo::~CaretObjectInfo() { } connectome-workbench-1.2.3+git41-gc4c6c90/src/Common/CaretObject.h000066400000000000000000000041751300200146000243410ustar00rootroot00000000000000#ifndef __CARETOBJECT_H__ #define __CARETOBJECT_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 "SystemUtilities.h" namespace caret { /** * A base class for all objects that are not derived * from third party libraries. */ class CaretObject { protected: CaretObject(); CaretObject(const CaretObject& o); CaretObject& operator=(const CaretObject& co); public: virtual ~CaretObject(); virtual AString toString() const; AString className() const; static void printListOfObjectsNotDeleted(const bool showCallStack); private: /** * Info about an allocated object. */ class CaretObjectInfo { public: CaretObjectInfo(const SystemBacktrace& backtrace); ~CaretObjectInfo(); SystemBacktrace m_backtrace; }; void copyHelper(const CaretObject& co); void initializeMembersCaretObject(); typedef std::map CARET_OBJECT_TRACKER_MAP; typedef CARET_OBJECT_TRACKER_MAP::iterator CARET_OBJECT_TRACKER_MAP_ITERATOR; static CARET_OBJECT_TRACKER_MAP allocatedObjects; }; #ifdef __CARET_OBJECT_DECLARE_H__ CaretObject::CARET_OBJECT_TRACKER_MAP CaretObject::allocatedObjects; #endif //__CARET_OBJECT_DECLARE_H__ } // namespace #endif // __CARETOBJECT_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Common/CaretObjectTracksModification.cxx000066400000000000000000000061651300200146000304130ustar00rootroot00000000000000 /*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 __CARET_OBJECT_TRACKS_MODIFICATION_DECLARE__ #include "CaretObjectTracksModification.h" #undef __CARET_OBJECT_TRACKS_MODIFICATION_DECLARE__ using namespace caret; /** * \class caret::CaretObjectTracksModification * \brief CaretObject base class with implementation of tracks modification interface. */ /** * Constructor. */ CaretObjectTracksModification::CaretObjectTracksModification() : CaretObject(), TracksModificationInterface() { this->modifiedFlag = false; } /** * Destructor. */ CaretObjectTracksModification::~CaretObjectTracksModification() { } /** * Copy constructor. * @param obj * Object that is copied. */ CaretObjectTracksModification::CaretObjectTracksModification(const CaretObjectTracksModification& obj) : CaretObject(obj), TracksModificationInterface() { this->copyHelperCaretObjectTracksModification(obj); } /** * Assignment operator. * @param obj * Data copied from obj to this. * @return * Reference to this object. */ CaretObjectTracksModification& CaretObjectTracksModification::operator=(const CaretObjectTracksModification& obj) { if (this != &obj) { CaretObject::operator=(obj); this->copyHelperCaretObjectTracksModification(obj); } return *this; } /** * Helps with copying an object of this type. * @param obj * Object that is copied. */ void CaretObjectTracksModification::copyHelperCaretObjectTracksModification(const CaretObjectTracksModification& /*obj*/) { this->modifiedFlag = false; // do not copy modification status } /** * Set the status to modified. */ void CaretObjectTracksModification::setModified() { this->modifiedFlag = true; } /** * Set the status to unmodified. */ void CaretObjectTracksModification::clearModified() { this->modifiedFlag = false; } /** * Is the object modified? * @return true if modified, else false. */ bool CaretObjectTracksModification::isModified() const { return this->modifiedFlag; } /** * Get a description of this object's content. * @return String describing this object's content. */ AString CaretObjectTracksModification::toString() const { const AString text = (CaretObject::toString() + "\nCaretObjectTracksModification::modifiedFlag=" + AString::fromBool(this->modifiedFlag)); return text; } connectome-workbench-1.2.3+git41-gc4c6c90/src/Common/CaretObjectTracksModification.h000066400000000000000000000037251300200146000300370ustar00rootroot00000000000000#ifndef __CARET_OBJECT_TRACKS_MODIFICATION__H_ #define __CARET_OBJECT_TRACKS_MODIFICATION__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 "TracksModificationInterface.h" namespace caret { class CaretObjectTracksModification : public CaretObject, public TracksModificationInterface { public: CaretObjectTracksModification(); virtual ~CaretObjectTracksModification(); CaretObjectTracksModification(const CaretObjectTracksModification& obj); CaretObjectTracksModification& operator=(const CaretObjectTracksModification& obj); virtual AString toString() const; virtual void setModified(); virtual void clearModified(); virtual bool isModified() const; private: void copyHelperCaretObjectTracksModification(const CaretObjectTracksModification& obj); bool modifiedFlag; }; #ifdef __CARET_OBJECT_TRACKS_MODIFICATION_DECLARE__ // #endif // __CARET_OBJECT_TRACKS_MODIFICATION_DECLARE__ } // namespace #endif //__CARET_OBJECT_TRACKS_MODIFICATION__H_ connectome-workbench-1.2.3+git41-gc4c6c90/src/Common/CaretPointLocator.cxx000066400000000000000000000354641300200146000261300ustar00rootroot00000000000000/*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 "CaretPointLocator.h" #include "CaretHeap.h" #include using namespace caret; using namespace std; void CaretPointLocator::addPoint(Oct >* thisOct, const float point[3], const int64_t index, const int32_t pointSet) { if (thisOct->m_leaf) { thisOct->m_data.m_vector->push_back(Point(point, index, pointSet)); int curSize = (int)thisOct->m_data.m_vector->size(); if (curSize > NUM_POINTS_SPLIT) {//test that not all points are the same, or that they have some minimum percentage spread, or... vector& myVecRef = *(thisOct->m_data.m_vector); Vector3D minBox = myVecRef[0].m_point, maxBox = myVecRef[0].m_point, tempvec; tempvec[0] = thisOct->m_bounds[0][2] - thisOct->m_bounds[0][0]; tempvec[1] = thisOct->m_bounds[1][2] - thisOct->m_bounds[1][0]; tempvec[2] = thisOct->m_bounds[2][2] - thisOct->m_bounds[2][0]; float diagonal = tempvec.length(); bool safeToSplit = false; for (int i = 1; i < curSize; ++i) {//this will slow down if lots of stuff is continuously put in an Oct that is far too big - use random sampling? if (myVecRef[i].m_point[0] < minBox[0]) minBox[0] = myVecRef[i].m_point[0]; if (myVecRef[i].m_point[1] < minBox[1]) minBox[1] = myVecRef[i].m_point[1]; if (myVecRef[i].m_point[2] < minBox[2]) minBox[2] = myVecRef[i].m_point[2]; if (myVecRef[i].m_point[0] > maxBox[0]) maxBox[0] = myVecRef[i].m_point[0]; if (myVecRef[i].m_point[1] > maxBox[1]) maxBox[1] = myVecRef[i].m_point[1]; if (myVecRef[i].m_point[2] > maxBox[2]) maxBox[2] = myVecRef[i].m_point[2]; tempvec = minBox - maxBox; if (tempvec.length() > 0.01f * diagonal)//make sure points aren't all identical, would go to infinity recursively { safeToSplit = true; break; } } if (safeToSplit) { thisOct->makeChildren(); for (int i = 0; i < curSize; ++i) { addPoint(thisOct->containingChild(myVecRef[i].m_point), myVecRef[i].m_point, myVecRef[i].m_index, myVecRef[i].m_mySet); } thisOct->m_data.freeData(); } } } else { addPoint(thisOct->containingChild(point), point, index, pointSet); } } int32_t CaretPointLocator::addPointSet(const float* coordsIn, const int64_t numCoords) { CaretMutexLocker locked(&m_modifyMutex); int32_t setNum = newIndex(); if (numCoords < 1) return setNum; if (m_tree == NULL) { Vector3D minBox, maxBox; minBox = maxBox = coordsIn;//hack - first triple for (int64_t i = 1; i < numCoords; ++i) { int64_t i3 = i * 3; if (coordsIn[i3] < minBox[0]) minBox[0] = coordsIn[i3]; if (coordsIn[i3 + 1] < minBox[1]) minBox[1] = coordsIn[i3 + 1]; if (coordsIn[i3 + 2] < minBox[2]) minBox[2] = coordsIn[i3 + 2]; if (coordsIn[i3] > maxBox[0]) maxBox[0] = coordsIn[i3]; if (coordsIn[i3 + 1] > maxBox[1]) maxBox[1] = coordsIn[i3 + 1]; if (coordsIn[i3 + 2] > maxBox[2]) maxBox[2] = coordsIn[i3 + 2]; } m_tree = new Oct >(minBox, maxBox); } for (int64_t i = 0; i < numCoords; ++i) { int64_t i3 = i * 3; m_tree = m_tree->makeContains(coordsIn + i3);//make new root if needed addPoint(m_tree, coordsIn + i3, i, setNum);//and add the point } return setNum; } CaretPointLocator::CaretPointLocator(const float* coordsIn, const int64_t numCoords) { m_nextSetIndex = 1;//next set will be set #1 m_tree = NULL; if (numCoords >= 1) { Vector3D minBox, maxBox; minBox = maxBox = coordsIn;//hack - first triple for (int64_t i = 1; i < numCoords; ++i) { int64_t i3 = i * 3; if (coordsIn[i3] < minBox[0]) minBox[0] = coordsIn[i3]; if (coordsIn[i3 + 1] < minBox[1]) minBox[1] = coordsIn[i3 + 1]; if (coordsIn[i3 + 2] < minBox[2]) minBox[2] = coordsIn[i3 + 2]; if (coordsIn[i3] > maxBox[0]) maxBox[0] = coordsIn[i3]; if (coordsIn[i3 + 1] > maxBox[1]) maxBox[1] = coordsIn[i3 + 1]; if (coordsIn[i3 + 2] > maxBox[2]) maxBox[2] = coordsIn[i3 + 2]; } m_tree = new Oct >(minBox, maxBox); for (int64_t i = 0; i < numCoords; ++i) { int64_t i3 = i * 3; addPoint(m_tree, coordsIn + i3, i, 0);//this is set #0 } } } CaretPointLocator::CaretPointLocator(const float minBounds[3], const float maxBounds[3]) { m_nextSetIndex = 0; m_tree = new Oct >(minBounds, maxBounds); } int64_t CaretPointLocator::closestPoint(const float target[3], LocatorInfo* infoOut) const { if (m_tree == NULL) return -1; CaretSimpleMinHeap >*, float> myHeap; bool first = true; float bestDist2 = -1.0f, bestDist = -1.0f, tempf, curDist = m_tree->distToPoint(target); Vector3D bestPoint; int64_t bestIndex = -1; int32_t bestSet = -1; myHeap.push(m_tree, curDist); while (curDist < bestDist || first) { Oct >* thisOct = myHeap.pop(); if (thisOct->m_leaf) { vector& myVecRef = *(thisOct->m_data.m_vector); int curSize = (int)myVecRef.size(); for (int i = 0; i < curSize; ++i) { tempf = MathFunctions::distanceSquared3D(myVecRef[i].m_point, target); if (tempf < bestDist2 || first) { first = false; bestDist2 = tempf; bestPoint = myVecRef[i].m_point; bestSet = myVecRef[i].m_mySet; bestIndex = myVecRef[i].m_index; } } bestDist = sqrt(bestDist2); } else { for (int ii = 0; ii < 2; ++ii) { for (int ij = 0; ij < 2; ++ij) { for (int ik = 0; ik < 2; ++ik) { tempf = thisOct->m_children[ii][ij][ik]->distToPoint(target); if (tempf < bestDist || first) { myHeap.push(thisOct->m_children[ii][ij][ik], tempf); } } } } } if (myHeap.isEmpty()) { break;//allows us to use top() without violating an assertion } myHeap.top(&curDist);//get the key for the next item } if (infoOut != NULL) { infoOut->whichSet = bestSet; infoOut->coords = bestPoint; infoOut->index = bestIndex; } return bestIndex; } int64_t CaretPointLocator::closestPointLimited(const float target[3], const float& maxDist, LocatorInfo* infoOut) const { if (m_tree == NULL) return -1; float curDist2 = m_tree->distSquaredToPoint(target), maxDist2 = maxDist * maxDist; if (curDist2 > maxDist2) { if (infoOut != NULL) { infoOut->whichSet = -1; infoOut->index = -1; } return -1; } CaretSimpleMinHeap >*, float> myHeap; bool first = true; float bestDist2 = -1.0f, tempf; Vector3D bestPoint; int64_t bestIndex = -1; int32_t bestSet = -1; myHeap.push(m_tree, curDist2); while (curDist2 < bestDist2 || first) { Oct >* thisOct = myHeap.pop(); if (thisOct->m_leaf) { vector& myVecRef = *(thisOct->m_data.m_vector); int curSize = (int)myVecRef.size(); for (int i = 0; i < curSize; ++i) { tempf = MathFunctions::distanceSquared3D(myVecRef[i].m_point, target); if (tempf < bestDist2 || (first && tempf <= maxDist2)) { first = false; bestDist2 = tempf; bestPoint = myVecRef[i].m_point; bestSet = myVecRef[i].m_mySet; bestIndex = myVecRef[i].m_index; } } } else { for (int ii = 0; ii < 2; ++ii) { for (int ij = 0; ij < 2; ++ij) { for (int ik = 0; ik < 2; ++ik) { tempf = thisOct->m_children[ii][ij][ik]->distSquaredToPoint(target); if (tempf < bestDist2 || (first && tempf <= maxDist2)) { myHeap.push(thisOct->m_children[ii][ij][ik], tempf); } } } } } if (myHeap.isEmpty()) { break;//allows us to use top() without violating an assertion } myHeap.top(&curDist2);//get the key for the next item } if (infoOut != NULL) { infoOut->whichSet = bestSet; infoOut->coords = bestPoint; infoOut->index = bestIndex; } return bestIndex; } set CaretPointLocator::pointsInRange(const float target[3], const float& maxDist) const { set ret; if (m_tree == NULL) return ret; float curDist2 = m_tree->distSquaredToPoint(target), maxDist2 = maxDist * maxDist; if (curDist2 > maxDist2) return ret; vector >*> myStack;//since we don't need the points sorted by distance myStack.push_back(m_tree); while (!myStack.empty()) { Oct >* thisOct = myStack.back(); myStack.pop_back(); if (thisOct->m_leaf) { vector& myVecRef = *(thisOct->m_data.m_vector); int curSize = (int)myVecRef.size(); for (int i = 0; i < curSize; ++i) { float tempf = MathFunctions::distanceSquared3D(myVecRef[i].m_point, target); if (tempf <= maxDist2) { ret.insert(LocatorInfo(myVecRef[i].m_index, myVecRef[i].m_mySet, myVecRef[i].m_point)); } } } else { for (int ii = 0; ii < 2; ++ii) { for (int ij = 0; ij < 2; ++ij) { for (int ik = 0; ik < 2; ++ik) { float tempf = thisOct->m_children[ii][ij][ik]->distSquaredToPoint(target); if (tempf <= maxDist2) { myStack.push_back(thisOct->m_children[ii][ij][ik]); } } } } } } return ret; } bool CaretPointLocator::anyInRange(const float target[3], const float& maxDist) const { if (m_tree == NULL) return false; float curDist2 = m_tree->distSquaredToPoint(target), maxDist2 = maxDist * maxDist, tempf; if (curDist2 > maxDist2) return false; CaretSimpleMinHeap >*, float> myHeap;//closer octs are more likely to contain a close enough point myHeap.push(m_tree, curDist2); while (!myHeap.isEmpty()) { Oct >* thisOct = myHeap.pop(&curDist2); if (thisOct->m_leaf) { vector& myVecRef = *(thisOct->m_data.m_vector); int curSize = (int)myVecRef.size(); for (int i = 0; i < curSize; ++i) { tempf = MathFunctions::distanceSquared3D(myVecRef[i].m_point, target); if (tempf < maxDist2) { return true; } } } else { for (int ii = 0; ii < 2; ++ii) { for (int ij = 0; ij < 2; ++ij) { for (int ik = 0; ik < 2; ++ik) { tempf = thisOct->m_children[ii][ij][ik]->distSquaredToPoint(target); if (tempf <= maxDist2) { myHeap.push(thisOct->m_children[ii][ij][ik], tempf); } } } } } } return false; } int32_t CaretPointLocator::newIndex() { if (m_unusedIndexes.empty()) { return m_nextSetIndex++; } else { int32_t ret = m_unusedIndexes[m_unusedIndexes.size() - 1]; m_unusedIndexes.pop_back(); return ret; } } void CaretPointLocator::removePointSet(int32_t whichSet) { CaretMutexLocker locked(&m_modifyMutex); m_unusedIndexes.push_back(whichSet); removeSetHelper(m_tree, whichSet); } void CaretPointLocator::removeSetHelper(Oct >* thisOct, int32_t thisSet) { if (thisOct == NULL) return; if (thisOct->m_leaf) { vector& myVecRef = *(thisOct->m_data.m_vector); int curSize = (int)myVecRef.size(); bool match = false; for (int i = 0; i < curSize; ++i)//make sure something gets removed, so we don't have to do an allocation if it isn't needed { if (myVecRef[i].m_mySet == thisSet) { match = true; break; } } if (match) { vector tempvec; tempvec.reserve(curSize - 1);//because at least one is getting removed for (int i = 0; i < curSize; ++i) { if (myVecRef[i].m_mySet != thisSet) { tempvec.push_back(myVecRef[i]); } } myVecRef = tempvec; } } else { for (int ii = 0; ii < 2; ++ii) { for (int ij = 0; ij < 2; ++ij) { for (int ik = 0; ik < 2; ++ik) { removeSetHelper(thisOct->m_children[ii][ij][ik], thisSet); } } } } } connectome-workbench-1.2.3+git41-gc4c6c90/src/Common/CaretPointLocator.h000066400000000000000000000075331300200146000255510ustar00rootroot00000000000000#ifndef __CARET_POINT_LOCATOR_H__ #define __CARET_POINT_LOCATOR_H__ #include "CaretAssertion.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 "CaretMutex.h" #include "OctTree.h" #include "Vector3D.h" #include #include namespace caret { struct LocatorInfo { int64_t index; int32_t whichSet; Vector3D coords; LocatorInfo(const int64_t& indexIn, const int32_t& whichSetIn, const Vector3D& coordsIn) : index(indexIn), whichSet(whichSetIn), coords(coordsIn) { } bool operator==(const LocatorInfo& rhs) const { return (index == rhs.index) && (whichSet == rhs.whichSet); }//ignore coords bool operator<(const LocatorInfo& rhs) const { if (whichSet == rhs.whichSet)//expect multi-set usage with pointsInRange to be rare, but still separate by whichSet { return index < rhs.index; } else { return whichSet < rhs.whichSet; } } }; class CaretPointLocator { struct Point { Vector3D m_point; int64_t m_index; int32_t m_mySet; Point(const float point[3], const int64_t index, const int32_t mySet) { m_point = point; m_index = index; m_mySet = mySet; } }; CaretMutex m_modifyMutex;//thread safety, don't let multiple threads modify the point sets at once Oct >* m_tree; int32_t m_nextSetIndex; std::vector m_unusedIndexes; void addPoint(Oct >* thisOct, const float point[3], const int64_t index, const int32_t pointSet); int32_t newIndex(); static const int NUM_POINTS_SPLIT = 100; void removeSetHelper(Oct >* thisOct, const int32_t thisSet); CaretPointLocator(); public: ///make an empty point locator with given bounding box (bounding box can expand later, but may be less efficient CaretPointLocator(const float minBounds[3], const float maxBounds[3]); ///make a point locator with the bounding box of this point set, and use this point set as set #0 CaretPointLocator(const float* coordsIn, const int64_t numCoords); ///add a point set, SAVE THE RETURN VALUE because it is how you identify which point set found points belong to int32_t addPointSet(const float* coordsIn, const int64_t numCoords); ///remove a point set by its set number void removePointSet(const int32_t whichSet); ///returns the index of the closest point, and optionally which point set and the coords int64_t closestPoint(const float target[3], LocatorInfo* infoOut = NULL) const; int64_t closestPointLimited(const float target[3], const float& maxDist, LocatorInfo* infoOut = NULL) const; std::set pointsInRange(const float target[3], const float& maxDist) const; bool anyInRange(const float target[3], const float& maxDist) const; }; } #endif //__CARET_POINT_LOCATOR_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Common/CaretPointer.h000066400000000000000000000715201300200146000245510ustar00rootroot00000000000000#ifndef __CARET_POINTER_H__ #define __CARET_POINTER_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 "CaretMutex.h" #include "CaretAssert.h" //NOTE: AFAIK, shared_ptr and raw pointers don't get along (can't pass to an old ownership-taking object without changing it to use shared_ptr) // so, these smart pointers have .releasePointer() which stops any smart pointer from deleting it (via an extra variable alongside the refcount) namespace caret { namespace _caret_pointer_impl {//namespace to hide things that shouldn't be used outside the header struct CaretPointerShare {//can't be member type because member types of templates on different types are incompatible int64_t m_refCount; bool m_doNotDelete; CaretPointerShare() { m_refCount = 1;//NOTE: don't initialize to 0, this way we don't have to change it every time we make one m_doNotDelete = false; } }; struct CaretPointerSyncShare {//same, but with mutex int64_t m_refCount; CaretMutex m_mutex;//protects m_refCount, m_doNotDelete bool m_doNotDelete; CaretPointerSyncShare() { m_refCount = 1; m_doNotDelete = false; } }; template class CaretPointerCommon {//provides only identical functionality between the four types - having a pointer member, and having ==, !=, a getPointer() method, and decay to pointer protected: T* m_pointer; CaretPointerCommon() { }//prevent standalone use, initialize with the share in derived classes public: template bool operator==(const T2* right) const { return m_pointer == right; } template bool operator!=(const T2* right) const { return !(*this == right); } template bool operator==(const CaretPointerCommon& right) const { return m_pointer == right.m_pointer; } template bool operator!=(const CaretPointerCommon& right) const { return !(*this == right); } operator T *const&() const { return m_pointer; }//never allow modifying the pointer, and also work when object is const template friend class CaretPointerCommon;//because for const compatibility, we need to access a different template's members }; template class CaretPointerBase : public CaretPointerCommon {//provides common functionality between just the 2 pointer types protected: using CaretPointerCommon::m_pointer; CaretPointerBase() { }//prevent standalone use public: T*& getPointer() { return m_pointer; } T *const& getPointer() const { return m_pointer; } T& operator*() const { CaretAssert(m_pointer != NULL); return *(m_pointer); } T *const& operator->() const { CaretAssert(m_pointer != NULL); return m_pointer; } }; template class CaretArrayBase : public CaretPointerCommon {//provides common functionality between just the 2 array types protected: using CaretPointerCommon::m_pointer; int64_t m_size; CaretArrayBase() { }//prevent standalone use, initialize size with share in derived classes public: T *const& getArray() const { return m_pointer; } template T& operator[](const I& index) { CaretAssert(m_pointer != NULL); CaretAssert(index >= 0 && (int64_t)index < m_size); return m_pointer[index]; } template const T& operator[](const I& index) const { CaretAssert(m_pointer != NULL); CaretAssert(index >= 0 && (int64_t)index < m_size); return m_pointer[index]; } const int64_t& size() const { return m_size; } }; } template class CaretPointerNonsync : public _caret_pointer_impl::CaretPointerBase { using _caret_pointer_impl::CaretPointerCommon::m_pointer; _caret_pointer_impl::CaretPointerShare* m_share; public: CaretPointerNonsync(); ~CaretPointerNonsync(); CaretPointerNonsync(const CaretPointerNonsync& right);//because a templated function apparently can't override default copy template CaretPointerNonsync(const CaretPointerNonsync& right); explicit CaretPointerNonsync(T* right); CaretPointerNonsync& operator=(const CaretPointerNonsync& right);//or default = template CaretPointerNonsync& operator=(const CaretPointerNonsync& right); void grabNew(T* right);//substitute for operator= to bare pointer int64_t getReferenceCount() const; ///breaks the hold on the pointer that is currently held by this, NO instances will delete it (setting is per-pointer, not per-instance) T*const& releasePointer(); template friend class CaretPointerNonsync;//because for const compatibility, we need to access a different template's members }; template class CaretPointer : public _caret_pointer_impl::CaretPointerBase { using _caret_pointer_impl::CaretPointerCommon::m_pointer; _caret_pointer_impl::CaretPointerSyncShare* m_share; mutable CaretMutex m_mutex;//protects members from modification while reading, or from reading while modifying public: CaretPointer(); ~CaretPointer(); CaretPointer(const CaretPointer& right); template CaretPointer(const CaretPointer& right); explicit CaretPointer(T* right); CaretPointer& operator=(const CaretPointer& right); template CaretPointer& operator=(const CaretPointer& right); void grabNew(T* right); int64_t getReferenceCount() const; ///breaks the hold on the pointer that is currently held by this, NO instances will delete it (setting is per-pointer, not per-instance) T*const& releasePointer(); template friend class CaretPointer; }; //separate array because delete and delete[] are different, and use indexing on one, and dereference/arrow on the other template class CaretArrayNonsync : public _caret_pointer_impl::CaretArrayBase { using _caret_pointer_impl::CaretPointerCommon::m_pointer; using _caret_pointer_impl::CaretArrayBase::m_size; _caret_pointer_impl::CaretPointerShare* m_share;//same share because it doesn't contain any specific information about what it is counting public: CaretArrayNonsync(); ~CaretArrayNonsync(); CaretArrayNonsync(const CaretArrayNonsync& right); template CaretArrayNonsync(const CaretArrayNonsync& right); CaretArrayNonsync(int64_t size);//for simpler construction CaretArrayNonsync(int64_t size, const T& initializer);//plus initialization CaretArrayNonsync& operator=(const CaretArrayNonsync& right); template CaretArrayNonsync& operator=(const CaretArrayNonsync& right); int64_t getReferenceCount() const; ///breaks the hold on the pointer that is currently held by this, NO instances will delete it (setting is per-pointer, not per-instance) T*const& releasePointer(); template friend class CaretArrayNonsync; }; template class CaretArray : public _caret_pointer_impl::CaretArrayBase { using _caret_pointer_impl::CaretPointerCommon::m_pointer; using _caret_pointer_impl::CaretArrayBase::m_size; _caret_pointer_impl::CaretPointerSyncShare* m_share;//same share because it doesn't contain any specific information about what it is counting mutable CaretMutex m_mutex;//protects members from modification while reading, or from reading while modifying public: CaretArray(); ~CaretArray(); CaretArray(const CaretArray& right); template CaretArray(const CaretArray& right); CaretArray(int64_t size);//for simpler construction CaretArray(int64_t size, const T& initializer);//plus initialization CaretArray& operator=(const CaretArray& right); template CaretArray& operator=(const CaretArray& right); int64_t getReferenceCount() const; ///breaks the hold on the pointer that is currently held by this, NO instances will delete it (setting is per-pointer, not per-instance) T*const& releasePointer(); template friend class CaretArray; }; //NOTE:begin pointer functions template CaretPointerNonsync::CaretPointerNonsync() { m_share = NULL; m_pointer = NULL; } template CaretPointerNonsync::CaretPointerNonsync(const CaretPointerNonsync& right) : _caret_pointer_impl::CaretPointerBase() { m_share = right.m_share; m_pointer = right.m_pointer; if (m_share != NULL) ++(m_share->m_refCount); } template template CaretPointerNonsync::CaretPointerNonsync(const CaretPointerNonsync& right) : _caret_pointer_impl::CaretPointerBase() { m_share = right.m_share; m_pointer = right.m_pointer; if (m_share != NULL) ++(m_share->m_refCount); } template CaretPointerNonsync::CaretPointerNonsync(T* right) { if (right == NULL) { m_share = NULL; m_pointer = NULL; } else { try { m_share = new _caret_pointer_impl::CaretPointerShare();//starts refcount at 1 } catch (...) {//don't leak the passed memory when exceptions happen delete right; throw; } m_pointer = right; } } template CaretPointerNonsync& CaretPointerNonsync::operator=(const CaretPointerNonsync& right) { if (this == &right) return *this;//short circuit self assignment CaretPointerNonsync temp(right);//copy construct from it, takes care type checking _caret_pointer_impl::CaretPointerShare* tempShare = temp.m_share;//swap the members T* tempPointer = temp.m_pointer; temp.m_share = m_share; temp.m_pointer = m_pointer; m_share = tempShare; m_pointer = tempPointer; return *this;//temp destructor takes care of the rest } template template CaretPointerNonsync& CaretPointerNonsync::operator=(const CaretPointerNonsync& right) {//self asignment won't hit this operator= CaretPointerNonsync temp(right);//copy construct from it, takes care of type checking _caret_pointer_impl::CaretPointerShare* tempShare = temp.m_share;//swap the members T* tempPointer = temp.m_pointer; temp.m_share = m_share; temp.m_pointer = m_pointer; m_share = tempShare; m_pointer = tempPointer; return *this;//temp destructor takes care of the rest } template void CaretPointerNonsync::grabNew(T* right) { CaretPointerNonsync temp(right);//construct from the pointer _caret_pointer_impl::CaretPointerShare* tempShare = temp.m_share;//swap the members T* tempPointer = temp.m_pointer; temp.m_share = m_share; temp.m_pointer = m_pointer; m_share = tempShare; m_pointer = tempPointer;//destructor of temp takes care of the rest } template CaretPointerNonsync::~CaretPointerNonsync() { if (m_share == NULL) return; --(m_share->m_refCount); if (m_share->m_refCount == 0) { if (!m_share->m_doNotDelete) delete m_pointer; delete m_share; } } template int64_t CaretPointerNonsync::getReferenceCount() const { if (m_share == NULL) { return 0; } return m_share->m_refCount; } template T*const& CaretPointerNonsync::releasePointer() { if (m_share != NULL) { m_share->m_doNotDelete = true; } return m_pointer; } //NOTE:begin sync pointer functions template CaretPointer::CaretPointer() { m_share = NULL; m_pointer = NULL; } template CaretPointer::CaretPointer(const CaretPointer& right) : _caret_pointer_impl::CaretPointerBase() {//don't need to lock self during constructor CaretMutexLocker locked(&(right.m_mutex));//don't let right modify its share until our reference is counted if (right.m_share == NULL)//guarantees it won't be deleted, because right has a counted reference { m_share = NULL; m_pointer = NULL; } else { CaretMutexLocker locked2(&(right.m_share->m_mutex)); ++(right.m_share->m_refCount); m_share = right.m_share;//now our reference is counted and we have the share, we can unlock everything m_pointer = right.m_pointer; } } template template CaretPointer::CaretPointer(const CaretPointer& right) : _caret_pointer_impl::CaretPointerBase() {//don't need to lock self during constructor CaretMutexLocker locked(&(right.m_mutex));//don't let right modify its share until our reference is counted if (right.m_share == NULL)//guarantees it won't be deleted, because right has a counted reference { m_share = NULL; m_pointer = NULL; } else { CaretMutexLocker locked2(&(right.m_share->m_mutex)); ++(right.m_share->m_refCount); m_share = right.m_share;//now our reference is counted and we have the share, we can unlock everything m_pointer = right.m_pointer; } } template CaretPointer::CaretPointer(T* right) {//don't need to lock self during constructor if (right == NULL) { m_share = NULL; m_pointer = NULL; } else { try { m_share = new _caret_pointer_impl::CaretPointerSyncShare();//starts refcount at 1 } catch (...) { delete right; throw; } m_pointer = right; } } template CaretPointer& CaretPointer::operator=(const CaretPointer& right) { if (this == &right) return *this;//short circuit self assignment CaretPointer temp(right);//copy construct from it, takes care of locking and type checking _caret_pointer_impl::CaretPointerSyncShare* tempShare = temp.m_share;//prepare to swap the members T* tempPointer = temp.m_pointer; CaretMutexLocker locked(&m_mutex);//lock myself before using internal state temp.m_share = m_share; temp.m_pointer = m_pointer; m_share = tempShare; m_pointer = tempPointer; return *this;//temp destructor takes care of the rest } template template CaretPointer& CaretPointer::operator=(const CaretPointer& right) {//self asignment won't hit this operator= CaretPointer temp(right);//copy construct from it, takes care of locking and type checking _caret_pointer_impl::CaretPointerSyncShare* tempShare = temp.m_share;//prepare to swap the members T* tempPointer = temp.m_pointer; CaretMutexLocker locked(&m_mutex);//lock myself before using internal state temp.m_share = m_share; temp.m_pointer = m_pointer; m_share = tempShare; m_pointer = tempPointer; return *this;//temp destructor takes care of the rest } template void CaretPointer::grabNew(T* right) { CaretPointer temp(right);//construct from the pointer _caret_pointer_impl::CaretPointerSyncShare* tempShare = temp.m_share;//prepare to swap the members T* tempPointer = temp.m_pointer; CaretMutexLocker locked(&m_mutex);//lock myself before using internal state temp.m_share = m_share; temp.m_pointer = m_pointer; m_share = tempShare; m_pointer = tempPointer;//destructor of temp takes care of the rest } template CaretPointer::~CaretPointer() {//access during destructor is programmer error, don't lock self if (m_share == NULL) return; bool deleteShare = false; { CaretMutexLocker locked(&(m_share->m_mutex));//do lock the refcount, though --(m_share->m_refCount); if (m_share->m_refCount == 0) { deleteShare = true; if (!m_share->m_doNotDelete) delete m_pointer; } }//unlock refcount mutex before deleting the object that contains it, otherwise Very Bad Things if (deleteShare) { delete m_share; } } template int64_t CaretPointer::getReferenceCount() const { CaretMutexLocker locked(&m_mutex);//lock so that m_share can't be deleted in the middle if (m_share == NULL) { return 0; } return m_share->m_refCount; } template T*const& CaretPointer::releasePointer() { CaretMutexLocker locked(&m_mutex);//lock to keep m_share and m_pointer coherent until after return - must return the pointer that was released if (m_share != NULL) { m_share->m_doNotDelete = true; } return m_pointer; } //NOTE:begin array functions template CaretArrayNonsync::CaretArrayNonsync() { m_share = NULL; m_pointer = NULL; m_size = 0; } template CaretArrayNonsync::CaretArrayNonsync(const CaretArrayNonsync& right) : _caret_pointer_impl::CaretArrayBase() { m_share = right.m_share; m_pointer = right.m_pointer; m_size = right.m_size; if (m_share != NULL) ++(m_share->m_refCount); } template template CaretArrayNonsync::CaretArrayNonsync(const CaretArrayNonsync& right) : _caret_pointer_impl::CaretArrayBase() { m_share = right.m_share; m_pointer = right.m_pointer; m_size = right.m_size; if (m_share != NULL) ++(m_share->m_refCount); } template CaretArrayNonsync::CaretArrayNonsync(int64_t size) { if (size > 0) { m_share = new _caret_pointer_impl::CaretPointerShare(); try { m_pointer = new T[size]; } catch (...) {//don't leak share objects if we can't allocate the memory delete m_share; m_share = NULL;//also keep state consistent throw; } m_size = size; } else { m_share = NULL; m_pointer = NULL; m_size = 0; } } template CaretArrayNonsync::CaretArrayNonsync(int64_t size, const T& initializer) { if (size > 0) { m_share = new _caret_pointer_impl::CaretPointerShare(); try { m_pointer = new T[size]; } catch (...) { delete m_share; m_share = NULL; throw; } m_size = size; T* end = m_pointer + size, *iter = m_pointer; do { *iter = initializer;//somewhat optimized, since this code will probably get used many places ++iter; } while (iter != end); } else { m_share = NULL; m_pointer = NULL; m_size = 0; } } template CaretArrayNonsync& CaretArrayNonsync::operator=(const CaretArrayNonsync& right) { CaretArrayNonsync temp(right); _caret_pointer_impl::CaretPointerShare* tempShare = temp.m_share;//swap the shares and fill members T* tempPointer = temp.m_pointer; int64_t tempSize = temp.m_size; temp.m_share = m_share; temp.m_pointer = m_pointer; temp.m_size = m_size; m_share = tempShare; m_pointer = tempPointer; m_size = tempSize; return *this;//destructor of temp cleans up } template template CaretArrayNonsync& CaretArrayNonsync::operator=(const CaretArrayNonsync& right) { CaretArrayNonsync temp(right); _caret_pointer_impl::CaretPointerShare* tempShare = temp.m_share;//swap the shares and fill members T* tempPointer = temp.m_pointer; int64_t tempSize = temp.m_size; temp.m_share = m_share; temp.m_pointer = m_pointer; temp.m_size = m_size; m_share = tempShare; m_pointer = tempPointer; m_size = tempSize; return *this;//destructor of temp cleans up } template CaretArrayNonsync::~CaretArrayNonsync() { if (m_share == NULL) return; --(m_share->m_refCount); if (m_share->m_refCount == 0) { if (!m_share->m_doNotDelete) delete[] m_pointer; delete m_share; } } template int64_t CaretArrayNonsync::getReferenceCount() const { if (m_share == NULL) { return 0; } return m_share->m_refCount; } template T*const& CaretArrayNonsync::releasePointer() { if (m_share != NULL) { m_share->m_doNotDelete = true; } return m_pointer; } //NOTE:begin sync array functions template CaretArray::CaretArray() { m_share = NULL; m_pointer = NULL; m_size = 0; } template CaretArray::CaretArray(const CaretArray& right) : _caret_pointer_impl::CaretArrayBase() {//don't need to lock self during constructor CaretMutexLocker locked(&(right.m_mutex));//don't let right modify its share until our reference is counted if (right.m_share == NULL)//guarantees it won't be deleted, because right has a counted reference { m_share = NULL; m_pointer = NULL; m_size = 0; } else { CaretMutexLocker locked2(&(right.m_share->m_mutex)); ++(right.m_share->m_refCount); m_share = right.m_share;//now our reference is counted and we have the share, we can unlock everything m_pointer = right.m_pointer; m_size = right.m_size; } } template template CaretArray::CaretArray(const CaretArray& right) : _caret_pointer_impl::CaretArrayBase() {//don't need to lock self during constructor CaretMutexLocker locked(&(right.m_mutex));//don't let right modify its share until our reference is counted if (right.m_share == NULL)//guarantees it won't be deleted, because right has a counted reference { m_share = NULL; m_pointer = NULL; m_size = 0; } else { CaretMutexLocker locked2(&(right.m_share->m_mutex)); ++(right.m_share->m_refCount); m_share = right.m_share;//now our reference is counted and we have the share, we can unlock everything this->m_pointer = right.m_pointer; m_size = right.m_size; } } template CaretArray::CaretArray(int64_t size) { if (size > 0) { m_share = new _caret_pointer_impl::CaretPointerSyncShare(); try { m_pointer = new T[size]; } catch (...) { delete m_share; m_share = NULL; throw; } m_size = size; } else { m_share = NULL; m_pointer = NULL; m_size = 0; } } template CaretArray::CaretArray(int64_t size, const T& initializer) { if (size > 0) { m_share = new _caret_pointer_impl::CaretPointerSyncShare(); try { m_pointer = new T[size]; } catch (...) { delete m_share; m_share = NULL; throw; } m_size = size; T* end = m_pointer + size, *iter = m_pointer; do { *iter = initializer;//somewhat optimized, since this code will probably get used many places ++iter; } while (iter != end); } else { m_share = NULL; m_pointer = NULL; m_size = 0; } } template CaretArray& CaretArray::operator=(const CaretArray& right) { CaretArray temp(right);//copy construct from it, takes care of locking _caret_pointer_impl::CaretPointerSyncShare* tempShare = temp.m_share;//prepare to swap the shares and fill members T* tempPointer = temp.m_pointer; int64_t tempSize = temp.m_size; CaretMutexLocker locked(&m_mutex);//lock myself before using internal state temp.m_share = m_share; temp.m_pointer = m_pointer; temp.m_size = m_size; m_share = tempShare; m_pointer = tempPointer; m_size = tempSize; return *this;//destructor of temp cleans up } template template CaretArray& CaretArray::operator=(const CaretArray& right) { CaretArray temp(right);//copy construct from it, takes care of locking _caret_pointer_impl::CaretPointerSyncShare* tempShare = temp.m_share;//prepare to swap the shares and fill members T* tempPointer = temp.m_pointer; int64_t tempSize = temp.m_size; CaretMutexLocker locked(&m_mutex);//lock myself before using internal state temp.m_share = m_share; temp.m_pointer = m_pointer; temp.m_size = m_size; m_share = tempShare; m_pointer = tempPointer; m_size = tempSize; return *this;//destructor of temp cleans up } template CaretArray::~CaretArray() {//access during destructor is programmer error, don't lock self if (m_share == NULL) return; bool deleteShare = false; { CaretMutexLocker locked(&(m_share->m_mutex));//do lock the refcount, though --(m_share->m_refCount); if (m_share->m_refCount == 0) { deleteShare = true; if (!m_share->m_doNotDelete) delete[] m_pointer; } }//left refcount unlock before deleting the object that contains it if (deleteShare) { delete m_share; } } template int64_t CaretArray::getReferenceCount() const { CaretMutexLocker locked(&m_mutex);//lock to keep m_share from being deleted if (m_share == NULL) { return 0; } return m_share->m_refCount; } template T*const& CaretArray::releasePointer() { CaretMutexLocker locked(&m_mutex);//lock because m_pointer and m_share need to remain coherent if (m_share != NULL) { m_share->m_doNotDelete = true; } return m_pointer; } } #endif //__CARET_POINTER_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Common/CaretPreferences.cxx000066400000000000000000001326541300200146000257530ustar00rootroot00000000000000 /*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 __CARET_PREFERENCES_DECLARE__ #include "CaretPreferences.h" #undef __CARET_PREFERENCES_DECLARE__ #include #include #include #include #include "CaretAssert.h" #include "CaretLogger.h" #include "ModelTransform.h" #include "TileTabsConfiguration.h" using namespace caret; /** * \class caret::CaretPreferences * \brief Preferences for use in Caret. * * Maintains preferences for use in Caret. The * preferences are read only one time, when an * instance is created. If a preference is changed, * it is written. */ /** * Constructor. */ CaretPreferences::CaretPreferences() : CaretObject() { this->qSettings = new QSettings("brainvis.wustl.edu", "Caret7"); this->readPreferences(); m_colorsMode = BackgroundAndForegroundColorsModeEnum::USER_PREFERENCES; } /** * Destructor. */ CaretPreferences::~CaretPreferences() { this->removeAllCustomViews(); this->removeAllTileTabsConfigurations(); delete this->qSettings; } /** * Get the boolean value for the given preference name. * @param name * Name of the preference. * @param defaultValue * Value returned in the preference with the given name was not found. * @return * Boolean value of preference or defaultValue if the * named preference is not found. */ bool CaretPreferences::getBoolean(const AString& name, const bool defaultValue) { bool b = this->qSettings->value(name, defaultValue).toBool(); return b; } /** * Set the given preference name with the boolean value. * @param * Name of the preference. * @param * New value for preference. */ void CaretPreferences::setBoolean(const AString& name, const bool value) { this->qSettings->setValue(name, value); } /** * Get the boolean value for the given preference name. * @param name * Name of the preference. * @param defaultValue * Value returned in the preference with the given name was not found. * @return * Integer value of preference or defaultValue if the * named preference is not found. */ int CaretPreferences::getInteger(const AString& name, const int defaultValue) { int b = this->qSettings->value(name, defaultValue).toInt(); return b; } /** * Set the given preference name with the integer value. * @param * Name of the preference. * @param * New value for preference. */ void CaretPreferences::setInteger(const AString& name, const int value) { this->qSettings->setValue(name, value); } /** * Get the string value for the given preference name. * @param name * Name of the preference. * @param defaultValue * Value returned in the preference with the given name was not found. * @return * String value of preference or defaultValue if the * named preference is not found. */ AString CaretPreferences::getString(const AString& name, const AString& defaultValue) { AString s = this->qSettings->value(name, defaultValue).toString(); return s; } /** * Set the given preference name with the string value. * @param * Name of the preference. * @param * New value for preference. */ void CaretPreferences::setString(const AString& name, const AString& value) { this->qSettings->setValue(name, value); } /** * Remove all custom views. */ void CaretPreferences::removeAllCustomViews() { for (std::vector::iterator iter = this->customViews.begin(); iter != this->customViews.end(); iter++) { delete *iter; } this->customViews.clear(); } /** * @return Names of custom views sorted by name. May want to precede this * method with a call to 'readCustomViews(true)' so that the custom views * are the latest from the settings. */ std::vector CaretPreferences::getCustomViewNames() const { std::vector names; for (std::vector::const_iterator iter = this->customViews.begin(); iter != this->customViews.end(); iter++) { const ModelTransform* mt = *iter; names.push_back(mt->getName()); } std::sort(names.begin(), names.end()); return names; } /** * @return Names and comments of custom views sorted by name. May want to precede this * method with a call to 'readCustomViews(true)' so that the custom views * are the latest from the settings. */ std::vector > CaretPreferences::getCustomViewNamesAndComments() const { std::vector customViewNames = getCustomViewNames(); std::vector > namesAndComments; for (std::vector::const_iterator iter = customViewNames.begin(); iter != customViewNames.end(); iter++) { const AString name = *iter; ModelTransform modelTransform; if (getCustomView(name, modelTransform)) { const AString comment = modelTransform.getComment(); namesAndComments.push_back(std::make_pair(name, comment)); } } return namesAndComments; } /** * Get a custom view with the given name. * * @param customViewName * Name of requested custom view. * @param modelTransformOut * Custom view will be loaded into this model transform. * @return true if a custom view with the name exists. If no * custom view exists with the name, false is returned and * the model transform will be the identity transform. */ bool CaretPreferences::getCustomView(const AString& customViewName, ModelTransform& modelTransformOut) const { for (std::vector::const_iterator iter = this->customViews.begin(); iter != this->customViews.end(); iter++) { const ModelTransform* mt = *iter; if (customViewName == mt->getName()) { modelTransformOut = *mt; return true; } } modelTransformOut.setToIdentity(); return false; } /** * Add or update a custom view. If a custom view exists with the name * in the given model transform it is replaced. * * @param modelTransform * Custom view's model transform. */ void CaretPreferences::addOrReplaceCustomView(const ModelTransform& modelTransform) { bool addNewCustomView = true; for (std::vector::iterator iter = this->customViews.begin(); iter != this->customViews.end(); iter++) { ModelTransform* mt = *iter; if (mt->getName() == modelTransform.getName()) { *mt = modelTransform; addNewCustomView = false; break; } } if (addNewCustomView) { this->customViews.push_back(new ModelTransform(modelTransform)); } this->writeCustomViews(); } /** * Remove the custom view with the given name. * * @param customViewName * Name of custom view. */ void CaretPreferences::removeCustomView(const AString& customViewName) { for (std::vector::iterator iter = this->customViews.begin(); iter != this->customViews.end(); iter++) { ModelTransform* mt = *iter; if (mt->getName() == customViewName) { this->customViews.erase(iter); delete mt; break; } } this->writeCustomViews(); } /** * Write the custom views. */ void CaretPreferences::writeCustomViews() { /* * Remove "userViews" that were replaced by customView */ this->qSettings->remove("userViews"); this->qSettings->beginWriteArray(NAME_CUSTOM_VIEWS); const int32_t numViews = static_cast(this->customViews.size()); for (int32_t i = 0; i < numViews; i++) { this->qSettings->setArrayIndex(i); this->qSettings->setValue(AString::number(i), this->customViews[i]->getAsString()); } this->qSettings->endArray(); this->qSettings->sync(); } /** * Remove all of the tile tabs configurations. */ void CaretPreferences::removeAllTileTabsConfigurations() { for (std::vector::iterator iter = this->tileTabsConfigurations.begin(); iter != this->tileTabsConfigurations.end(); iter++) { delete *iter; } this->tileTabsConfigurations.clear(); } /** * Write the tile tabs configurations. */ void CaretPreferences::writeTileTabsConfigurations() { this->qSettings->beginWriteArray(NAME_TILE_TABS_CONFIGURATIONS); const int32_t numViews = static_cast(this->tileTabsConfigurations.size()); for (int32_t i = 0; i < numViews; i++) { this->qSettings->setArrayIndex(i); this->qSettings->setValue(AString::number(i), this->tileTabsConfigurations[i]->encodeInXML()); } this->qSettings->endArray(); this->qSettings->sync(); } /** * Read the tile tabs configuration. Since user's may want to use them * in multiple instance of workbench that are running, this method allows * the tile tab configurations to be read without affecting other preferences. * * @param performSync * Sync with preferences since preferences may have been changed * by a concurrently running workbench. */ void CaretPreferences::readTileTabsConfigurations(const bool performSync) { if (performSync) { this->qSettings->sync(); } this->removeAllTileTabsConfigurations(); /* * Read Configurations */ const int numConfigurations = this->qSettings->beginReadArray(NAME_TILE_TABS_CONFIGURATIONS); for (int i = 0; i < numConfigurations; i++) { this->qSettings->setArrayIndex(i); const AString configString = this->qSettings->value(AString::number(i)).toString(); TileTabsConfiguration* ttc = new TileTabsConfiguration(); if (ttc->decodeFromXML(configString)) { this->tileTabsConfigurations.push_back(ttc); } else { delete ttc; } } this->qSettings->endArray(); } /** * @return The Tile tabs configurations sorted by name. */ std::vector CaretPreferences::getTileTabsConfigurationsSortedByName() const { /* * Copy the configurations and sort them by name. */ std::vector configurations; configurations.insert(configurations.end(), this->tileTabsConfigurations.begin(), this->tileTabsConfigurations.end()); std::sort(configurations.begin(), configurations.end(), TileTabsConfiguration::lessThanComparisonByName); return configurations; } /** * Get the tile tabs configuration with the given unique identifier. * * @param uniqueIdentifier * Unique identifier of the requested tile tabs configuration. * @return * Pointer to tile tabs configuration with the given unique identifier * or NULL is it does not exist. */ TileTabsConfiguration* CaretPreferences::getTileTabsConfigurationByUniqueIdentifier(const AString& uniqueIdentifier) { for (std::vector::const_iterator iter = this->tileTabsConfigurations.begin(); iter != this->tileTabsConfigurations.end(); iter++) { TileTabsConfiguration* ttc = *iter; if (ttc->getUniqueIdentifier() == uniqueIdentifier) { return ttc; } } return NULL; } /** * Get the tile tabs configuration with the given unique identifier. * * @param uniqueIdentifier * Unique identifier of the requested tile tabs configuration. * @return * Pointer to tile tabs configuration with the given unique identifier * or NULL is it does not exist. */ const TileTabsConfiguration* CaretPreferences::getTileTabsConfigurationByUniqueIdentifier(const AString& uniqueIdentifier) const { for (std::vector::const_iterator iter = this->tileTabsConfigurations.begin(); iter != this->tileTabsConfigurations.end(); iter++) { const TileTabsConfiguration* ttc = *iter; if (ttc->getUniqueIdentifier() == uniqueIdentifier) { return ttc; } } return NULL; } /** * Get the tile tabs configuration with the given name. * * @param name * Name of the requested tile tabs configuration. * @return * Pointer to tile tabs configuration with the given name * or NULL is it does not exist. */ TileTabsConfiguration* CaretPreferences::getTileTabsConfigurationByName(const AString& name) const { for (std::vector::const_iterator iter = this->tileTabsConfigurations.begin(); iter != this->tileTabsConfigurations.end(); iter++) { TileTabsConfiguration* ttc = *iter; if (name == ttc->getName()) { return ttc; } } return NULL; } /** * Add a new tile tabs configuration. * * @param tileTabsConfiguration * New tile tabs configuration that is added. */ void CaretPreferences::addTileTabsConfiguration(TileTabsConfiguration* tileTabsConfiguration) { this->tileTabsConfigurations.push_back(tileTabsConfiguration); this->writeTileTabsConfigurations(); } /** * Remove the tile tabs configuration with the given name. * * @param tileTabsUniqueIdentifier * Unique identifier of configuration that will be removed. */ void CaretPreferences::removeTileTabsConfigurationByUniqueIdentifier(const AString& tileTabsUniqueIdentifier) { for (std::vector::iterator iter = this->tileTabsConfigurations.begin(); iter != this->tileTabsConfigurations.end(); iter++) { TileTabsConfiguration* ttc = *iter; if (ttc->getUniqueIdentifier() == tileTabsUniqueIdentifier) { this->tileTabsConfigurations.erase(iter); delete ttc; break; } } this->writeTileTabsConfigurations(); } /** * @return Pointer to the background and foreground colors for * use when drawing graphics. The colors returned may be * either the user's preferences or the from the currently * loaded scene. */ const BackgroundAndForegroundColors* CaretPreferences::getBackgroundAndForegroundColors() const { switch (m_colorsMode) { case BackgroundAndForegroundColorsModeEnum::SCENE: return &this->sceneColors; break; case BackgroundAndForegroundColorsModeEnum::USER_PREFERENCES: return &this->userColors; break; } CaretAssert(0); return &this->userColors; } /** * @return The USER'S preferred background and foreground colors. * This method is used only by the PreferencesDialog to * update the user's preferred colors. */ BackgroundAndForegroundColors CaretPreferences::getUserBackgroundAndForegroundColors() { return this->userColors; } /** * Set the USER'S preferred background and foreground colors. * This method is used only by the PreferencesDialog to * update the user's preferred colors. */ void CaretPreferences::setUserBackgroundAndForegroundColors(const BackgroundAndForegroundColors& colors) { /* * "in memory" colors */ this->userColors = colors; /* * Update preferences file with colors */ writeUnsignedByteArray(NAME_COLOR_FOREGROUND_ALL, this->userColors.m_colorForegroundAll, 3); writeUnsignedByteArray(NAME_COLOR_BACKGROUND_ALL, this->userColors.m_colorBackgroundAll, 3); writeUnsignedByteArray(NAME_COLOR_FOREGROUND_CHART, this->userColors.m_colorForegroundChart, 3); writeUnsignedByteArray(NAME_COLOR_BACKGROUND_CHART, this->userColors.m_colorBackgroundChart, 3); writeUnsignedByteArray(NAME_COLOR_FOREGROUND_SURFACE, this->userColors.m_colorForegroundSurface, 3); writeUnsignedByteArray(NAME_COLOR_BACKGROUND_SURFACE, this->userColors.m_colorBackgroundSurface, 3); writeUnsignedByteArray(NAME_COLOR_FOREGROUND_VOLUME, this->userColors.m_colorForegroundVolume, 3); writeUnsignedByteArray(NAME_COLOR_BACKGROUND_VOLUME, this->userColors.m_colorBackgroundVolume, 3); writeUnsignedByteArray(NAME_COLOR_CHART_MATRIX_GRID_LINES, this->userColors.m_colorChartMatrixGridLines, 3); } /** * Set the SCENE background and foreground colors. * This method is called when scenes are restored. */ void CaretPreferences::setSceneBackgroundAndForegroundColors(const BackgroundAndForegroundColors& colors) { this->sceneColors = colors; } /** * @return Mode for background and foreground colors. */ BackgroundAndForegroundColorsModeEnum::Enum CaretPreferences::getBackgroundAndForegroundColorsMode() const { return m_colorsMode; } /** * Set the mode for background and foreground colors. * NOTE: This is a transient value and NOT saved to preferences. * * @param colorsMode * New colors mode. */ void CaretPreferences::setBackgroundAndForegroundColorsMode(const BackgroundAndForegroundColorsModeEnum::Enum colorsMode) { m_colorsMode = colorsMode; } /** * Get the previous spec files. * * @param previousSpecFiles * Will contain previous spec files. */ void CaretPreferences::getPreviousSpecFiles(std::vector& previousSpecFiles) const { previousSpecFiles = this->previousSpecFiles; } /** * Add to the previous spec files. * * @param specFileName * Spec file added to the previous spec files. */ void CaretPreferences::addToPreviousSpecFiles(const AString& specFileName) { if (specFileName.isEmpty() == false) { this->addToPrevious(this->previousSpecFiles, specFileName); } const int32_t num = static_cast(this->previousSpecFiles.size()); this->qSettings->beginWriteArray(NAME_PREVIOUS_SPEC_FILES); for (int i = 0; i < num; i++) { this->qSettings->setArrayIndex(i); this->qSettings->setValue(AString::number(i), this->previousSpecFiles[i]); } this->qSettings->endArray(); this->qSettings->sync(); } /** * Clear the previous spec files. */ void CaretPreferences::clearPreviousSpecFiles() { this->previousSpecFiles.clear(); this->addToPreviousSpecFiles(""); } /** * Get the previous scene files. * * @param previousSceneFiles * Will contain previous scene files. */ void CaretPreferences::getPreviousSceneFiles(std::vector& previousSceneFiles) const { previousSceneFiles = this->previousSceneFiles; } /** * Add to the previous scene files. * * @param sceneFileName * Scene file added to the previous scene files. */ void CaretPreferences::addToPreviousSceneFiles(const AString& sceneFileName) { if (sceneFileName.isEmpty() == false) { this->addToPrevious(this->previousSceneFiles, sceneFileName); } const int32_t num = static_cast(this->previousSceneFiles.size()); this->qSettings->beginWriteArray(NAME_PREVIOUS_SCENE_FILES); for (int i = 0; i < num; i++) { this->qSettings->setArrayIndex(i); this->qSettings->setValue(AString::number(i), this->previousSceneFiles[i]); } this->qSettings->endArray(); this->qSettings->sync(); } /** * Clear the previous scene files. */ void CaretPreferences::clearPreviousSceneFiles() { this->previousSceneFiles.clear(); this->addToPreviousSceneFiles(""); } /** * Get the directories that were used in the Open File Dialog. * * @param previousOpenFileDirectories * Will contain previous directories. */ void CaretPreferences::getPreviousOpenFileDirectories(std::vector& previousOpenFileDirectories) const { previousOpenFileDirectories = this->previousOpenFileDirectories; } /** * Get the directories that were used in the Open File Dialog. * * @param previousOpenFileDirectories * Will contain previous directories. */ void CaretPreferences::getPreviousOpenFileDirectories(QStringList& previousOpenFileDirectoriesList) const { previousOpenFileDirectoriesList.clear(); const int32_t numDirectories = static_cast(this->previousOpenFileDirectories.size()); for (int32_t i = 0; i < numDirectories; i++) { previousOpenFileDirectoriesList.append(this->previousOpenFileDirectories[i]); } } /** * Add to the previous directories that were used in the Open File Dialog. * * @param directoryName * Directory added to the previous directories from Open File Dialog. */ void CaretPreferences::addToPreviousOpenFileDirectories(const AString& directoryName) { this->addToPrevious(this->previousOpenFileDirectories, directoryName); const int32_t num = static_cast(this->previousOpenFileDirectories.size()); this->qSettings->beginWriteArray(NAME_PREVIOUS_OPEN_FILE_DIRECTORIES); for (int i = 0; i < num; i++) { this->qSettings->setArrayIndex(i); this->qSettings->setValue(AString::number(i), this->previousOpenFileDirectories[i]); } this->qSettings->endArray(); this->qSettings->sync(); } /** * Add to a list of previous, removing any matching entries * and limiting the size of the list. * * @param previousDeque * Deque containing the previous elements. * @param newName * Name that is added at the front. */ void CaretPreferences::addToPrevious(std::vector& previousVector, const AString& newName) { /* * Note: remove moves duplicate elements to after 'pos' but * does not change the size of the container so use erase * to remove the duplicate elements from the container. */ std::vector::iterator pos = std::remove(previousVector.begin(), previousVector.end(), newName); previousVector.erase(pos, previousVector.end()); const uint64_t MAX_ELEMENTS = 10; if (previousVector.size() > MAX_ELEMENTS) { previousVector.erase(previousVector.begin() + MAX_ELEMENTS, previousVector.end()); } previousVector.insert(previousVector.begin(), newName); } /** * @return The logging level. */ LogLevelEnum::Enum CaretPreferences::getLoggingLevel() const { return this->loggingLevel; } /** * Set the logging level. * Will also update the level in the Caret Logger. * @param loggingLevel * New value for logging level. */ void CaretPreferences::setLoggingLevel(const LogLevelEnum::Enum loggingLevel) { this->loggingLevel = loggingLevel; const AString name = LogLevelEnum::toName(this->loggingLevel); this->qSettings->setValue(NAME_LOGGING_LEVEL, name); this->qSettings->sync(); CaretLogger::getLogger()->setLevel(loggingLevel); } /** * @return The OpenGL Drawing method. */ OpenGLDrawingMethodEnum::Enum CaretPreferences::getOpenDrawingMethod() const { OpenGLDrawingMethodEnum::Enum drawMethod = this->openGLDrawingMethod; /* * Disable vertex buffers for now */ drawMethod = OpenGLDrawingMethodEnum::DRAW_WITH_VERTEX_BUFFERS_OFF; return drawMethod; } /** * Set the OpenGL Drawing method. * * @param openGLDrawingMethod * New value for OpenGL Drawing method. */ void CaretPreferences::setOpenGLDrawingMethod(const OpenGLDrawingMethodEnum::Enum openGLDrawingMethod) { this->openGLDrawingMethod = openGLDrawingMethod; this->setString(NAME_OPENGL_DRAWING_METHOD, OpenGLDrawingMethodEnum::toName(this->openGLDrawingMethod)); } /** * @return The view file type for manage files dialog. */ SpecFileDialogViewFilesTypeEnum::Enum CaretPreferences::getManageFilesViewFileType() const { return this->manageFilesViewFileType; } /** * Set the view file type for manage files dialog. * * @param manageFilesViewFileType * New view file type. */ void CaretPreferences::setManageFilesViewFileType(const SpecFileDialogViewFilesTypeEnum::Enum manageFilesViewFileType) { this->manageFilesViewFileType = manageFilesViewFileType; this->setString(NAME_MANAGE_FILES_VIEW_FILE_TYPE, SpecFileDialogViewFilesTypeEnum::toName(this->manageFilesViewFileType)); } /** * @return Show surface identification symbols? */ bool CaretPreferences::isShowSurfaceIdentificationSymbols() const { return this->showSurfaceIdentificationSymbols; } /** * Set show surface identification symbols. * * @param showSymbols * New status. */ void CaretPreferences::setShowSurfaceIdentificationSymbols(const bool showSymbols) { this->showSurfaceIdentificationSymbols = showSymbols; this->setBoolean(NAME_SHOW_SURFACE_IDENTIFICATION_SYMBOLS, this->showSurfaceIdentificationSymbols); } /** * @return Show volume identification symbols? */ bool CaretPreferences::isShowVolumeIdentificationSymbols() const { return this->showVolumeIdentificationSymbols; } /** * Set show volume identification symbols. * * @param showSymbols * New status. */ void CaretPreferences::setShowVolumeIdentificationSymbols(const bool showSymbols) { this->showVolumeIdentificationSymbols = showSymbols; this->setBoolean(NAME_SHOW_VOLUME_IDENTIFICATION_SYMBOLS, this->showVolumeIdentificationSymbols); } /** * @return Is dynamic connectivity defaulted on? */ bool CaretPreferences::isDynamicConnectivityDefaultedOn() const { return this->dynamicConnectivityDefaultedOn; } /** * Set dynamic connectivity defaulted on. * * @param defaultedOn * New status. */ void CaretPreferences::setDynamicConnectivityDefaultedOn(const bool defaultedOn) { this->dynamicConnectivityDefaultedOn = defaultedOn; this->setBoolean(NAME_DYNAMIC_CONNECTIVITY_ON, defaultedOn); } /** * @return The image capture method. */ ImageCaptureMethodEnum::Enum CaretPreferences::getImageCaptureMethod() const { return this->imageCaptureMethod; } /** * Set the image capture method. * * @param imageCaptureMethod * New value for image capture method. */ void CaretPreferences::setImageCaptureMethod(const ImageCaptureMethodEnum::Enum imageCaptureMethod) { this->imageCaptureMethod = imageCaptureMethod; this->setString(NAME_IMAGE_CAPTURE_METHOD, ImageCaptureMethodEnum::toName(this->imageCaptureMethod)); } /** * @return Save remote login to preferences. */ bool CaretPreferences::isRemoteFilePasswordSaved() { return this->remoteFileLoginSaved; } /** * Set saving of remote login and password to preferences. * * @param saveRemoteLoginToPreferences * New status. */ void CaretPreferences::setRemoteFilePasswordSaved(const bool saveRemotePasswordToPreferences) { this->remoteFileLoginSaved = saveRemotePasswordToPreferences; this->setBoolean(NAME_REMOTE_FILE_LOGIN_SAVED, this->remoteFileLoginSaved); this->qSettings->sync(); } /** * Get the remote file username and password * * @param userNameOut * Contains username upon exit * @param passwordOut * Contains password upon exit. */ void CaretPreferences::getRemoteFileUserNameAndPassword(AString& userNameOut, AString& passwordOut) const { userNameOut = this->remoteFileUserName; passwordOut = this->remoteFilePassword; } /** * Set the remote file username and password * * @param userName * New value for username. * @param passwordOut * New value for password. */ void CaretPreferences::setRemoteFileUserNameAndPassword(const AString& userName, const AString& password) { this->remoteFileUserName = userName; this->remoteFilePassword = password; this->setString(NAME_REMOTE_FILE_USER_NAME, userName); this->setString(NAME_REMOTE_FILE_PASSWORD, password); this->qSettings->sync(); } /** * @return Are axes crosshairs displayed? */ bool CaretPreferences::isVolumeAxesCrosshairsDisplayed() const { return this->displayVolumeAxesCrosshairs; } /** * Set axes crosshairs displayed * @param displayed * New status. */ void CaretPreferences::setVolumeAxesCrosshairsDisplayed(const bool displayed) { this->displayVolumeAxesCrosshairs = displayed; this->setBoolean(CaretPreferences::NAME_VOLUME_AXES_CROSSHAIRS, this->displayVolumeAxesCrosshairs); this->qSettings->sync(); } /** * @return Are axes labels displayed? */ bool CaretPreferences::isVolumeAxesLabelsDisplayed() const { return this->displayVolumeAxesLabels; } /** * Set axes labels displayed * @param displayed * New status. */ void CaretPreferences::setVolumeAxesLabelsDisplayed(const bool displayed) { this->displayVolumeAxesLabels = displayed; this->setBoolean(CaretPreferences::NAME_VOLUME_AXES_LABELS, this->displayVolumeAxesLabels); this->qSettings->sync(); } /** * @return Are montage axes coordinates displayed? */ bool CaretPreferences::isVolumeMontageAxesCoordinatesDisplayed() const { return this->displayVolumeAxesCoordinates; } /** * Set montage axes coordinates displayed * @param displayed * New status. */ void CaretPreferences::setVolumeMontageAxesCoordinatesDisplayed(const bool displayed) { this->displayVolumeAxesCoordinates = displayed; this->setBoolean(CaretPreferences::NAME_VOLUME_AXES_COORDINATE, this->displayVolumeAxesCoordinates); this->qSettings->sync(); } /** * @return The volume montage gap. */ int32_t CaretPreferences::getVolumeMontageGap() const { return this->volumeMontageGap; } /** * Set the volume montage gap. * * @param volumeMontageGap * New value for montage gap. */ void CaretPreferences::setVolumeMontageGap(const int32_t volumeMontageGap) { this->volumeMontageGap = volumeMontageGap; this->setInteger(CaretPreferences::NAME_VOLUME_MONTAGE_GAP, this->volumeMontageGap); this->qSettings->sync(); } /** * @return The volume montage coordinate precision */ int32_t CaretPreferences::getVolumeMontageCoordinatePrecision() const { return this->volumeMontageCoordinatePrecision; } /** * Set the volume montage coordinate precision * * @param volumeMontageCoordinatePrecision * New value for montage coordinate precision */ void CaretPreferences::setVolumeMontageCoordinatePrecision(const int32_t volumeMontageCoordinatePrecision) { this->volumeMontageCoordinatePrecision = volumeMontageCoordinatePrecision; this->setInteger(CaretPreferences::NAME_VOLUME_MONTAGE_COORDINATE_PRECISION, this->volumeMontageCoordinatePrecision); this->qSettings->sync(); } /** * @return Is the splash screen enabled? */ bool CaretPreferences::isSplashScreenEnabled() const { return this->splashScreenEnabled; } /** * Set the splash screen enabled. * @param enabled * New status. */ void CaretPreferences::setSplashScreenEnabled(const bool enabled) { this->splashScreenEnabled = enabled; this->setBoolean(CaretPreferences::NAME_SPLASH_SCREEN, this->splashScreenEnabled); this->qSettings->sync(); } /** * @return Is the Develop Menu enabled? */ bool CaretPreferences::isDevelopMenuEnabled() const { return this->developMenuEnabled; } /** * Set the Develop Menu enabled. * @param enabled * New status. */ void CaretPreferences::setDevelopMenuEnabled(const bool enabled) { this->developMenuEnabled = enabled; this->setBoolean(CaretPreferences::NAME_DEVELOP_MENU, this->developMenuEnabled); this->qSettings->sync(); } /** * @param Is yoking defaulted on ? */ bool CaretPreferences::isYokingDefaultedOn() const { return this->yokingDefaultedOn; } /** * Set yoking defaulted on * * @param status * New status for yoking on. */ void CaretPreferences::setYokingDefaultedOn(const bool status) { this->yokingDefaultedOn = status; this->setBoolean(CaretPreferences::NAME_YOKING_DEFAULT_ON, this->yokingDefaultedOn); this->qSettings->sync(); } /** * @param Is volume identification defaulted on ? */ bool CaretPreferences::isVolumeIdentificationDefaultedOn() const { return this->volumeIdentificationDefaultedOn; } /** * Set volume identification defaulted on * * @param status * New status for yoking on. */ void CaretPreferences::setVolumeIdentificationDefaultedOn(const bool status) { this->volumeIdentificationDefaultedOn = status; this->setBoolean(CaretPreferences::NAME_VOLUME_IDENTIFICATION_DEFAULTED_ON, this->volumeIdentificationDefaultedOn); this->qSettings->sync(); } /** * Read an unsigned byte array to the preferences. * * @param name * Name for preferences * @param array * The array that is read. * @param numberOfElements * Number of elements in the array. */ void CaretPreferences::readUnsignedByteArray(const AString& name, uint8_t array[], const int32_t numberOfElements) { const int numAvailable = this->qSettings->beginReadArray(name); const int numToRead = std::min(numAvailable, numberOfElements); for (int i = 0; i < numToRead; i++) { this->qSettings->setArrayIndex(i); array[i] = static_cast(this->qSettings->value(AString::number(i)).toInt()); } this->qSettings->endArray(); } /** * Write an unsigned byte array to the preferences. * * @param name * Name for preferences * @param array * The array that is written. * @param numberOfElements * Number of elements in the array. */ void CaretPreferences::writeUnsignedByteArray(const AString& name, const uint8_t array[], const int32_t numberOfElements) { this->qSettings->beginWriteArray(name); for (int i = 0; i < numberOfElements; i++) { this->qSettings->setArrayIndex(i); this->qSettings->setValue(AString::number(i), array[i]); } this->qSettings->endArray(); this->qSettings->sync(); } /** * Initialize/Read the preferences */ void CaretPreferences::readPreferences() { userColors.reset(); uint8_t colorRGB[3] = { 0, 0, 0 }; userColors.getColorForegroundAllView(colorRGB); readUnsignedByteArray(NAME_COLOR_FOREGROUND_ALL, colorRGB, 3); userColors.setColorForegroundAllView(colorRGB); userColors.getColorBackgroundAllView(colorRGB); readUnsignedByteArray(NAME_COLOR_BACKGROUND_ALL, colorRGB, 3); userColors.setColorBackgroundAllView(colorRGB); userColors.getColorForegroundChartView(colorRGB); readUnsignedByteArray(NAME_COLOR_FOREGROUND_CHART, colorRGB, 3); userColors.setColorForegroundChartView(colorRGB); userColors.getColorBackgroundChartView(colorRGB); readUnsignedByteArray(NAME_COLOR_BACKGROUND_CHART, colorRGB, 3); userColors.setColorBackgroundChartView(colorRGB); userColors.getColorForegroundSurfaceView(colorRGB); readUnsignedByteArray(NAME_COLOR_FOREGROUND_SURFACE, colorRGB, 3); userColors.setColorForegroundSurfaceView(colorRGB); userColors.getColorBackgroundSurfaceView(colorRGB); readUnsignedByteArray(NAME_COLOR_BACKGROUND_SURFACE, colorRGB, 3); userColors.setColorBackgroundSurfaceView(colorRGB); userColors.getColorForegroundVolumeView(colorRGB); readUnsignedByteArray(NAME_COLOR_FOREGROUND_VOLUME, colorRGB, 3); userColors.setColorForegroundVolumeView(colorRGB); userColors.getColorBackgroundVolumeView(colorRGB); readUnsignedByteArray(NAME_COLOR_BACKGROUND_VOLUME, colorRGB, 3); userColors.setColorBackgroundVolumeView(colorRGB); userColors.getColorChartMatrixGridLines(colorRGB); readUnsignedByteArray(NAME_COLOR_CHART_MATRIX_GRID_LINES, colorRGB, 3); userColors.setColorChartMatrixGridLines(colorRGB); this->previousSpecFiles.clear(); const int numPrevSpec = this->qSettings->beginReadArray(NAME_PREVIOUS_SPEC_FILES); for (int i = 0; i < numPrevSpec; i++) { this->qSettings->setArrayIndex(i); previousSpecFiles.push_back(this->qSettings->value(AString::number(i)).toString()); } this->qSettings->endArray(); this->previousSceneFiles.clear(); const int numPrevScene = this->qSettings->beginReadArray(NAME_PREVIOUS_SCENE_FILES); for (int i = 0; i < numPrevScene; i++) { this->qSettings->setArrayIndex(i); previousSceneFiles.push_back(this->qSettings->value(AString::number(i)).toString()); } this->qSettings->endArray(); this->previousOpenFileDirectories.clear(); const int numPrevDir = this->qSettings->beginReadArray(NAME_PREVIOUS_OPEN_FILE_DIRECTORIES); for (int i = 0; i < numPrevDir; i++) { this->qSettings->setArrayIndex(i); previousOpenFileDirectories.push_back(this->qSettings->value(AString::number(i)).toString()); } this->qSettings->endArray(); this->readCustomViews(false); this->readTileTabsConfigurations(); AString levelName = this->qSettings->value(NAME_LOGGING_LEVEL, LogLevelEnum::toName(LogLevelEnum::INFO)).toString(); bool valid = false; LogLevelEnum::Enum logLevel = LogLevelEnum::fromName(levelName, &valid); if (valid == false) { logLevel = LogLevelEnum::INFO; } this->setLoggingLevel(logLevel); ImageCaptureMethodEnum::Enum defaultCaptureType = ImageCaptureMethodEnum::IMAGE_CAPTURE_WITH_RENDER_PIXMAP; AString imageCaptureMethodName = this->qSettings->value(NAME_IMAGE_CAPTURE_METHOD, ImageCaptureMethodEnum::toName(defaultCaptureType)).toString(); bool validImageCaptureMethodName = false; this->imageCaptureMethod = ImageCaptureMethodEnum::fromName(imageCaptureMethodName, &validImageCaptureMethodName); if ( ! validImageCaptureMethodName) { this->imageCaptureMethod = defaultCaptureType; } AString openGLDrawingMethodName = this->qSettings->value(NAME_OPENGL_DRAWING_METHOD, OpenGLDrawingMethodEnum::toName(OpenGLDrawingMethodEnum::DRAW_WITH_VERTEX_BUFFERS_OFF)).toString(); bool validDrawingMethod = false; this->openGLDrawingMethod = OpenGLDrawingMethodEnum::fromName(openGLDrawingMethodName, &validDrawingMethod); if ( ! validDrawingMethod) { this->openGLDrawingMethod = OpenGLDrawingMethodEnum::DRAW_WITH_VERTEX_BUFFERS_OFF; } AString viewFileTypesName = this->qSettings->value(NAME_MANAGE_FILES_VIEW_FILE_TYPE, SpecFileDialogViewFilesTypeEnum::toName(SpecFileDialogViewFilesTypeEnum::VIEW_FILES_ALL)).toString(); bool viewFilesTypeValid = false; this->manageFilesViewFileType = SpecFileDialogViewFilesTypeEnum::fromName(viewFileTypesName, &viewFilesTypeValid); if ( ! viewFilesTypeValid) { this->manageFilesViewFileType = SpecFileDialogViewFilesTypeEnum::VIEW_FILES_ALL; } this->displayVolumeAxesLabels = this->getBoolean(CaretPreferences::NAME_VOLUME_AXES_LABELS, true); this->displayVolumeAxesCrosshairs = this->getBoolean(CaretPreferences::NAME_VOLUME_AXES_CROSSHAIRS, true); this->displayVolumeAxesCoordinates = this->getBoolean(CaretPreferences::NAME_VOLUME_AXES_COORDINATE, true); this->volumeMontageGap = this->getInteger(CaretPreferences::NAME_VOLUME_MONTAGE_GAP, 3); this->volumeMontageCoordinatePrecision = this->getInteger(CaretPreferences::NAME_VOLUME_MONTAGE_COORDINATE_PRECISION, 0); this->animationStartTime = 0.0;//this->qSettings->value(CaretPreferences::NAME_ANIMATION_START_TIME).toDouble(); this->splashScreenEnabled = this->getBoolean(CaretPreferences::NAME_SPLASH_SCREEN, true); this->developMenuEnabled = this->getBoolean(CaretPreferences::NAME_DEVELOP_MENU, false); this->yokingDefaultedOn = this->getBoolean(CaretPreferences::NAME_YOKING_DEFAULT_ON, true); this->volumeIdentificationDefaultedOn = this->getBoolean(CaretPreferences::NAME_VOLUME_IDENTIFICATION_DEFAULTED_ON, true); this->dynamicConnectivityDefaultedOn = this->getBoolean(CaretPreferences::NAME_DYNAMIC_CONNECTIVITY_ON, true); this->remoteFileUserName = this->getString(NAME_REMOTE_FILE_USER_NAME); this->remoteFilePassword = this->getString(NAME_REMOTE_FILE_PASSWORD); this->remoteFileLoginSaved = this->getBoolean(NAME_REMOTE_FILE_LOGIN_SAVED, false); this->showSurfaceIdentificationSymbols = this->getBoolean(NAME_SHOW_SURFACE_IDENTIFICATION_SYMBOLS, true); this->showVolumeIdentificationSymbols = this->getBoolean(NAME_SHOW_VOLUME_IDENTIFICATION_SYMBOLS, true); } /** * Read the custom views. Since user's may want to use them * in multiple instance of workbench that are running, this method allows * the custom views to be read without affecting other preferences. * * @param performSync * Sync with preferences since preferences may have been changed * by a concurrently running workbench. */ void CaretPreferences::readCustomViews(const bool performSync) { if (performSync) { this->qSettings->sync(); } this->removeAllCustomViews(); /* * Previously had "userViews" prior to CustomViews */ const int numUserViews = this->qSettings->beginReadArray("userViews"); for (int i = 0; i < numUserViews; i++) { this->qSettings->setArrayIndex(i); const AString viewString = this->qSettings->value(AString::number(i)).toString(); ModelTransform uv; if (uv.setFromString(viewString)) { this->customViews.push_back(new ModelTransform(uv)); } } this->qSettings->endArray(); /* * Read Custom Views */ const int numCustomViews = this->qSettings->beginReadArray(NAME_CUSTOM_VIEWS); for (int i = 0; i < numCustomViews; i++) { this->qSettings->setArrayIndex(i); const AString viewString = this->qSettings->value(AString::number(i)).toString(); ModelTransform uv; if (uv.setFromString(viewString)) { this->customViews.push_back(new ModelTransform(uv)); } } this->qSettings->endArray(); } void CaretPreferences::getAnimationStartTime(double& time) { time = animationStartTime; } void CaretPreferences::setAnimationStartTime(const double& time) { animationStartTime = time; } /** * Get a description of this object's content. * @return String describing this object's content. */ AString CaretPreferences::toString() const { return "CaretPreferences"; } /** * Convert RGB bytes to Floats. * * @param bytesRGB * Byte RGB color [0, 255] INPUT * @param floatRGB * Float RGB color [0.0, 1.0] OUTPUT */ void CaretPreferences::byteRgbToFloatRgb(const uint8_t byteRGB[3], float floatRGB[3]) { for (int32_t i = 0; i < 3; i++) { floatRGB[i] = static_cast(byteRGB[i]) / 255.0; } } connectome-workbench-1.2.3+git41-gc4c6c90/src/Common/CaretPreferences.h000066400000000000000000000370631300200146000253760ustar00rootroot00000000000000#ifndef __CARET_PREFERENCES__H_ #define __CARET_PREFERENCES__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 "BackgroundAndForegroundColors.h" #include "BackgroundAndForegroundColorsModeEnum.h" #include "CaretObject.h" #include "LogLevelEnum.h" #include "ImageCaptureMethodEnum.h" #include "OpenGLDrawingMethodEnum.h" #include "SpecFileDialogViewFilesTypeEnum.h" class QSettings; class QStringList; namespace caret { class ModelTransform; class TileTabsConfiguration; class CaretPreferences : public CaretObject { public: CaretPreferences(); virtual ~CaretPreferences(); const BackgroundAndForegroundColors* getBackgroundAndForegroundColors() const; BackgroundAndForegroundColors getUserBackgroundAndForegroundColors(); void setUserBackgroundAndForegroundColors(const BackgroundAndForegroundColors& colors); BackgroundAndForegroundColorsModeEnum::Enum getBackgroundAndForegroundColorsMode() const; void setSceneBackgroundAndForegroundColors(const BackgroundAndForegroundColors& colors); void setBackgroundAndForegroundColorsMode(const BackgroundAndForegroundColorsModeEnum::Enum colorsMode); void getPreviousSpecFiles(std::vector& previousSpecFiles) const; void addToPreviousSpecFiles(const AString& specFileName); void clearPreviousSpecFiles(); void getPreviousSceneFiles(std::vector& previousSceneFiles) const; void addToPreviousSceneFiles(const AString& specFileName); void clearPreviousSceneFiles(); void getPreviousOpenFileDirectories(std::vector& previousOpenFileDirectories) const; void getPreviousOpenFileDirectories(QStringList& previousOpenFileDirectories) const; void addToPreviousOpenFileDirectories(const AString& directoryName); LogLevelEnum::Enum getLoggingLevel() const; void setLoggingLevel(const LogLevelEnum::Enum loggingLevel); ImageCaptureMethodEnum::Enum getImageCaptureMethod() const; void setImageCaptureMethod(const ImageCaptureMethodEnum::Enum imageCaptureMethod); OpenGLDrawingMethodEnum::Enum getOpenDrawingMethod() const; void setOpenGLDrawingMethod(const OpenGLDrawingMethodEnum::Enum openGLDrawingMethod); bool isVolumeAxesCrosshairsDisplayed() const; void setVolumeAxesCrosshairsDisplayed(const bool displayed); bool isVolumeAxesLabelsDisplayed() const; void setVolumeAxesLabelsDisplayed(const bool displayed); bool isVolumeMontageAxesCoordinatesDisplayed() const; void setVolumeMontageAxesCoordinatesDisplayed(const bool displayed); int32_t getVolumeMontageGap() const; void setVolumeMontageGap(const int32_t volumeMontageGap); int32_t getVolumeMontageCoordinatePrecision() const; void setVolumeMontageCoordinatePrecision(const int32_t volumeMontageCoordinatePrecision); void setAnimationStartTime(const double &time); void getAnimationStartTime(double &time); bool isSplashScreenEnabled() const; void setSplashScreenEnabled(const bool enabled); bool isDevelopMenuEnabled() const; void setDevelopMenuEnabled(const bool enabled); void readTileTabsConfigurations(const bool performSync = true); std::vector getTileTabsConfigurationsSortedByName() const; TileTabsConfiguration* getTileTabsConfigurationByUniqueIdentifier(const AString& uniqueIdentifier); const TileTabsConfiguration* getTileTabsConfigurationByUniqueIdentifier(const AString& uniqueIdentifier) const; TileTabsConfiguration* getTileTabsConfigurationByName(const AString& name) const; void addTileTabsConfiguration(TileTabsConfiguration* tileTabsConfiguration); void removeTileTabsConfigurationByUniqueIdentifier(const AString& tileTabsUniqueIdentifier); void writeTileTabsConfigurations(); void readCustomViews(const bool performSync = true); std::vector getCustomViewNames() const; std::vector > getCustomViewNamesAndComments() const; bool getCustomView(const AString& customViewName, ModelTransform& modelTransformOut) const; void addOrReplaceCustomView(const ModelTransform& modelTransform); void removeCustomView(const AString& customViewName); bool isRemoteFilePasswordSaved(); void setRemoteFilePasswordSaved(const bool saveRemotePasswordToPreferences); void getRemoteFileUserNameAndPassword(AString& userNameOut, AString& passwordOut) const; void setRemoteFileUserNameAndPassword(const AString& userName, const AString& password); static void byteRgbToFloatRgb(const uint8_t byteRGB[3], float floatRGB[3]); bool isYokingDefaultedOn() const; void setYokingDefaultedOn(const bool status); bool isVolumeIdentificationDefaultedOn() const; void setVolumeIdentificationDefaultedOn(const bool status); SpecFileDialogViewFilesTypeEnum::Enum getManageFilesViewFileType() const; void setManageFilesViewFileType(const SpecFileDialogViewFilesTypeEnum::Enum manageFilesViewFileType); bool isShowSurfaceIdentificationSymbols() const; void setShowSurfaceIdentificationSymbols(const bool showSymbols); bool isShowVolumeIdentificationSymbols() const; void setShowVolumeIdentificationSymbols(const bool showSymbols); bool isDynamicConnectivityDefaultedOn() const; void setDynamicConnectivityDefaultedOn(const bool defaultedOn); private: CaretPreferences(const CaretPreferences&); CaretPreferences& operator=(const CaretPreferences&); public: virtual AString toString() const; private: bool getBoolean(const AString& name, const bool defaultValue = false); void setBoolean(const AString& name, const bool value); int getInteger(const AString& name, const int defaultValue = false); void setInteger(const AString& name, const int value); AString getString(const AString& name, const AString& defaultValue = ""); void setString(const AString& name, const AString& value); void addToPrevious(std::vector& previousVector, const AString& newName); void readUnsignedByteArray(const AString& name, uint8_t array[], const int32_t numberOfElements); void writeUnsignedByteArray(const AString& name, const uint8_t array[], const int32_t numberOfElements); void readPreferences(); void removeAllTileTabsConfigurations(); void removeAllCustomViews(); void writeCustomViews(); mutable QSettings* qSettings; BackgroundAndForegroundColors userColors; BackgroundAndForegroundColors sceneColors; /** NOTE: colors mode is NOT saved to preferences */ BackgroundAndForegroundColorsModeEnum::Enum m_colorsMode; std::vector previousSpecFiles; std::vector previousSceneFiles; std::vector previousOpenFileDirectories; LogLevelEnum::Enum loggingLevel; ImageCaptureMethodEnum::Enum imageCaptureMethod; OpenGLDrawingMethodEnum::Enum openGLDrawingMethod; std::vector customViews; std::vector tileTabsConfigurations; bool displayVolumeAxesCrosshairs; bool displayVolumeAxesLabels; bool displayVolumeAxesCoordinates; int32_t volumeMontageGap; int32_t volumeMontageCoordinatePrecision; bool splashScreenEnabled; bool developMenuEnabled; double animationStartTime; bool volumeIdentificationDefaultedOn; bool showSurfaceIdentificationSymbols; bool showVolumeIdentificationSymbols; bool dynamicConnectivityDefaultedOn; bool yokingDefaultedOn; AString remoteFileUserName; AString remoteFilePassword; bool remoteFileLoginSaved; SpecFileDialogViewFilesTypeEnum::Enum manageFilesViewFileType; static const AString NAME_ANIMATION_START_TIME; static const AString NAME_VOLUME_AXES_CROSSHAIRS; static const AString NAME_VOLUME_AXES_LABELS; static const AString NAME_VOLUME_AXES_COORDINATE; static const AString NAME_VOLUME_MONTAGE_GAP; static const AString NAME_VOLUME_MONTAGE_COORDINATE_PRECISION; static const AString NAME_COLOR_BACKGROUND; static const AString NAME_COLOR_FOREGROUND; static const AString NAME_COLOR_BACKGROUND_ALL; static const AString NAME_COLOR_FOREGROUND_ALL; static const AString NAME_COLOR_BACKGROUND_CHART; static const AString NAME_COLOR_FOREGROUND_CHART; static const AString NAME_COLOR_BACKGROUND_SURFACE; static const AString NAME_COLOR_FOREGROUND_SURFACE; 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_DEVELOP_MENU; static const AString NAME_DYNAMIC_CONNECTIVITY_ON; static const AString NAME_IMAGE_CAPTURE_METHOD; static const AString NAME_LOGGING_LEVEL; static const AString NAME_MANAGE_FILES_VIEW_FILE_TYPE; static const AString NAME_OPENGL_DRAWING_METHOD; static const AString NAME_PREVIOUS_SCENE_FILES; static const AString NAME_PREVIOUS_SPEC_FILES; static const AString NAME_PREVIOUS_OPEN_FILE_DIRECTORIES; static const AString NAME_SPLASH_SCREEN; static const AString NAME_CUSTOM_VIEWS; static const AString NAME_REMOTE_FILE_USER_NAME; static const AString NAME_REMOTE_FILE_PASSWORD; static const AString NAME_REMOTE_FILE_LOGIN_SAVED; static const AString NAME_SHOW_SURFACE_IDENTIFICATION_SYMBOLS; static const AString NAME_SHOW_VOLUME_IDENTIFICATION_SYMBOLS; static const AString NAME_TILE_TABS_CONFIGURATIONS; static const AString NAME_VOLUME_IDENTIFICATION_DEFAULTED_ON; static const AString NAME_YOKING_DEFAULT_ON; }; #ifdef __CARET_PREFERENCES_DECLARE__ const AString CaretPreferences::NAME_ANIMATION_START_TIME = "animationStartTime"; const AString CaretPreferences::NAME_VOLUME_AXES_CROSSHAIRS = "volumeAxesCrosshairs"; const AString CaretPreferences::NAME_VOLUME_AXES_LABELS = "volumeAxesLabels"; const AString CaretPreferences::NAME_VOLUME_AXES_COORDINATE = "volumeAxesCoordinates"; const AString CaretPreferences::NAME_VOLUME_MONTAGE_GAP = "volumeMontageGap"; const AString CaretPreferences::NAME_VOLUME_MONTAGE_COORDINATE_PRECISION = "volumeMontageCoordinatePrecision"; const AString CaretPreferences::NAME_COLOR_BACKGROUND = "colorBackground"; const AString CaretPreferences::NAME_COLOR_FOREGROUND = "colorForeground"; const AString CaretPreferences::NAME_COLOR_BACKGROUND_ALL = "colorBackgroundAll"; const AString CaretPreferences::NAME_COLOR_FOREGROUND_ALL = "colorForegroundAll"; const AString CaretPreferences::NAME_COLOR_BACKGROUND_CHART = "colorBackgroundChart"; const AString CaretPreferences::NAME_COLOR_FOREGROUND_CHART = "colorForegroundChart"; const AString CaretPreferences::NAME_COLOR_BACKGROUND_SURFACE = "colorBackgroundSurface"; const AString CaretPreferences::NAME_COLOR_FOREGROUND_SURFACE = "colorForegroundSurface"; 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_DEVELOP_MENU = "developMenu"; const AString CaretPreferences::NAME_DYNAMIC_CONNECTIVITY_ON = "dynamicConnectivityDefaultedOn"; const AString CaretPreferences::NAME_IMAGE_CAPTURE_METHOD = "imageCaptureMethod"; const AString CaretPreferences::NAME_LOGGING_LEVEL = "loggingLevel"; const AString CaretPreferences::NAME_MANAGE_FILES_VIEW_FILE_TYPE = "manageFilesViewFileType"; const AString CaretPreferences::NAME_OPENGL_DRAWING_METHOD = "openGLDrawingMethod"; const AString CaretPreferences::NAME_PREVIOUS_SCENE_FILES = "previousSceneFiles"; const AString CaretPreferences::NAME_PREVIOUS_SPEC_FILES = "previousSpecFiles"; const AString CaretPreferences::NAME_PREVIOUS_OPEN_FILE_DIRECTORIES = "previousOpenFileDirectories"; const AString CaretPreferences::NAME_SPLASH_SCREEN = "splashScreen"; const AString CaretPreferences::NAME_CUSTOM_VIEWS = "customViews"; const AString CaretPreferences::NAME_REMOTE_FILE_USER_NAME = "remoteFileUserName"; const AString CaretPreferences::NAME_REMOTE_FILE_PASSWORD = "remoteFilePassword"; const AString CaretPreferences::NAME_REMOTE_FILE_LOGIN_SAVED = "removeFileLoginSaved"; const AString CaretPreferences::NAME_SHOW_SURFACE_IDENTIFICATION_SYMBOLS = "showSurfaceIdentificationSymbols"; const AString CaretPreferences::NAME_SHOW_VOLUME_IDENTIFICATION_SYMBOLS = "showVolumeIdentificationSymbols"; const AString CaretPreferences::NAME_TILE_TABS_CONFIGURATIONS = "tileTabsConfigurations"; const AString CaretPreferences::NAME_VOLUME_IDENTIFICATION_DEFAULTED_ON = "volumeIdentificationDefaultedOn"; const AString CaretPreferences::NAME_YOKING_DEFAULT_ON = "yokingDefaultedOn"; #endif // __CARET_PREFERENCES_DECLARE__ } // namespace #endif //__CARET_PREFERENCES__H_ connectome-workbench-1.2.3+git41-gc4c6c90/src/Common/CaretTemporaryFile.cxx000066400000000000000000000210601300200146000262600ustar00rootroot00000000000000 /*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 __CARET_TEMPORARY_FILE_DECLARE__ #include "CaretTemporaryFile.h" #undef __CARET_TEMPORARY_FILE_DECLARE__ #include #include "CaretHttpManager.h" #include "DataFileException.h" using namespace caret; /** * \class caret::CaretTemporaryFile * \brief Reads and writes a temporary file. * \ingroup Common * * Reads and writes a temporary file. When an instance of this class * goes out of scope, the temporary file will be deleted. This class * is able to read a file that resides on an HTTP server (filename * starts with "http://"). * * QTemporaryFile is encapsulated by this class. */ /** * Constructor. */ CaretTemporaryFile::CaretTemporaryFile() : DataFile() { m_temporaryFile = NULL; initializeCaretTemporaryFile(); } /** * Destructor. */ CaretTemporaryFile::~CaretTemporaryFile() { delete m_temporaryFile; } /** * Initialize the temporary file.. */ void CaretTemporaryFile::initializeCaretTemporaryFile() { if (m_temporaryFile != NULL) { delete m_temporaryFile; } m_temporaryFile = new QTemporaryFile(); setFileName(m_temporaryFile->fileName()); } /** * Clear the temporary file. * Destroys the encapsulated QTemporaryFile. */ void CaretTemporaryFile::clear() { DataFile::clear(); initializeCaretTemporaryFile(); } /** * Is the file empty (contains no data)? * * @return * true if the file is empty, else false. */ bool CaretTemporaryFile::isEmpty() const { const bool fileEmpty = (m_temporaryFile->size() <= 0); return fileEmpty; } AString CaretTemporaryFile::getFileName() const { return m_temporaryFile->fileName(); } AString CaretTemporaryFile::getFileNameNoPath() const { CaretTemporaryFile* ctf = const_cast(this); ctf->setFileName(m_temporaryFile->fileName()); return DataFile::getFileNameNoPath(); } /** * This method does nothing as the temporary file's name * is generated by QTemporaryFile. * * @param filename * Name for file. */ void CaretTemporaryFile::setFileName(const AString& filename) { /* * Needed for getFileNameNoPath() functionality. */ DataFile::setFileName(filename); } /** * Read the file at the given path into the temporary file. * * @param filename * Name of the data file. * @throws DataFileException * If the file was not successfully read. */ void CaretTemporaryFile::readFile(const AString& filename) { if (DataFile::isFileOnNetwork(filename)) { /* * Read file on network. * Sort of a kludge, read from the network as a string of bytes * and then write the bytes to a temporary file. */ CaretHttpRequest request; request.m_method = CaretHttpManager::GET; request.m_url = filename; CaretHttpResponse response; CaretHttpManager::httpRequest(request, response); if (response.m_ok == false) { QString msg = ("HTTP error HTTP Response Code=" + AString::number(response.m_responseCode)); throw DataFileException(filename, msg); } const int64_t numBytes = response.m_body.size(); if (numBytes > 0) { AString tempPath = QDir::tempPath(); if (!tempPath.endsWith('/')) tempPath += '/';//qt decided to make this behavior OS-dependent, for no apparent reason m_temporaryFile->setFileTemplate(tempPath + "qt_temp.XXXXXX." + filename.section('/', -1).section('.', 1));//strip all but the filename, then take all the parts of the extension if (m_temporaryFile->open()) { const int64_t numBytesWritten = m_temporaryFile->write(&response.m_body[0], numBytes); if (numBytesWritten != numBytes) { throw DataFileException(filename, " Tried to write " + QString::number(numBytes) + " bytes to temporary file " + m_temporaryFile->fileName() + " but only wrote " + AString::number(numBytesWritten) + " bytes."); } m_temporaryFile->close(); } else { throw DataFileException(filename, "Unable to open temporary file for writing its content."); } } else { throw DataFileException(filename, "Failed to read any data from file."); } } else { /* * Read local file. */ QFile file(filename); checkFileReadability(filename); if (file.open(QFile::ReadOnly)) { QByteArray byteArray = file.readAll(); file.close(); const int numBytes = byteArray.length(); if (numBytes > 0) { if (m_temporaryFile->open()) { const int64_t numBytesWritten = m_temporaryFile->write(byteArray); if (numBytesWritten != numBytes) { throw DataFileException(filename, " Tried to write " + QString::number(numBytes) + " bytes to temporary file " + m_temporaryFile->fileName() + " but only wrote " + AString::number(numBytesWritten) + " bytes."); } m_temporaryFile->close(); } else { throw DataFileException(m_temporaryFile->fileName(), "Unable to open temporary file for writing its content."); } } else { throw DataFileException(filename, "No data read from file, is it empty?"); } } else { throw DataFileException(filename, "Unable to open file for reading its content."); } } } /** * Write the contents of the temporary file to a local file with * the given name. * * @param filename * Name of the local data file. * @throws DataFileException * If the file was not successfully written. */ void CaretTemporaryFile::writeFile(const AString& filename) { checkFileWritability(filename); if (isEmpty()) { throw DataFileException(filename, "No data (temporary file is empty) to write to file."); } const QString tempFileName = m_temporaryFile->fileName(); QFile fileIn(tempFileName); if (fileIn.open(QFile::ReadOnly)) { QByteArray byteArray = fileIn.readAll(); fileIn.close(); QFile fileOut(filename); if (fileOut.open(QFile::WriteOnly)) { fileOut.write(byteArray); fileOut.close(); } else { fileOut.close(); throw DataFileException(filename, "Unable to open file for writing its content."); } } else { fileIn.close(); throw DataFileException(tempFileName, "Unable to open temporary file for reading its content."); } } connectome-workbench-1.2.3+git41-gc4c6c90/src/Common/CaretTemporaryFile.h000066400000000000000000000040211300200146000257030ustar00rootroot00000000000000#ifndef __CARET_TEMPORARY_FILE_H__ #define __CARET_TEMPORARY_FILE_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 "DataFile.h" class QTemporaryFile; namespace caret { class CaretTemporaryFile : public DataFile { public: CaretTemporaryFile(); virtual ~CaretTemporaryFile(); virtual void clear(); virtual bool isEmpty() const; virtual AString getFileName() const; virtual AString getFileNameNoPath() const; virtual void setFileName(const AString& filename); virtual void readFile(const AString& filename); virtual void writeFile(const AString& filename); // ADD_NEW_METHODS_HERE private: CaretTemporaryFile(const CaretTemporaryFile&); CaretTemporaryFile& operator=(const CaretTemporaryFile&); private: void initializeCaretTemporaryFile(); QTemporaryFile* m_temporaryFile; // ADD_NEW_MEMBERS_HERE }; #ifdef __CARET_TEMPORARY_FILE_DECLARE__ // #endif // __CARET_TEMPORARY_FILE_DECLARE__ } // namespace #endif //__CARET_TEMPORARY_FILE_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Common/CaretUndoCommand.cxx000066400000000000000000000136031300200146000257060ustar00rootroot00000000000000 /*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*/ /* * NOTE: The Qt license is included because the CaretUndoStack API * including the method comments is copied from QUndoStack. */ /**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtOpenGL 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 Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/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 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #define __CARET_UNDO_COMMAND_DECLARE__ #include "CaretUndoCommand.h" #undef __CARET_UNDO_COMMAND_DECLARE__ #include "CaretAssert.h" #include "CaretLogger.h" using namespace caret; /** * \class caret::CaretUndoCommand * \brief Abstract class for a command that supports 'undo' and 'redo'. * \ingroup Common * * Abstract class for commands that allow 'undo' and 'redo'. * * Design is based off Qt's QUndoCommand. * * While using QUndoCommand would be preferred, it cannot be used because * It is located in Qt's GUI module which is not accessible to * Caret's "lower level" modules. */ /** * Constructor. */ CaretUndoCommand::CaretUndoCommand() : CaretObject(), m_windowIndex(-1), m_mergeFlag(false) { } /** * Destructor. */ CaretUndoCommand::~CaretUndoCommand() { } /** * @return Description of command. */ AString CaretUndoCommand::getDescription() const { return m_description; } /** * Set description of command. * @param descripton * New value for description of command. */ void CaretUndoCommand::setDescription(const AString& description) { m_description = description; } /** * @return Is merge enabled for this command? * * When merge is enabled and a command is passed * to CaretUndoStack::push(), CaretUndoStack will * try to merge the command with the command at * the top of the undo stack. */ bool CaretUndoCommand::isMergeEnabled() const { return m_mergeFlag; } /** * Set the merge status for this command. * * When merge is enabled and a command is passed * to CaretUndoStack::push(), CaretUndoStack will * try to merge the command with the command at * the top of the undo stack. * * @param mergeStatus * New merge status for this command. */ void CaretUndoCommand::setMergeEnabled(const bool mergeStatus) { m_mergeFlag = mergeStatus; } /** * @return Index of window in which command is executed. * Will be negative if not valid. */ int32_t CaretUndoCommand::getWindowIndex() const { return m_windowIndex; } /** * Set index of window in which command is executed. * * @param windowIndex * Index of window. Will be negative if invalid. */ void CaretUndoCommand::setWindowIndex(const int32_t windowIndex) { m_windowIndex = windowIndex; } /** * Attempts to merge this command with command. Returns true on success; otherwise returns false. * * If this function returns true, calling this command's redo() must have the same effect as * redoing both this command and command. Similarly, calling this command's undo() must have * the same effect as undoing command and this command. * * The default implementation returns false. * * @return True if the given command was merged with this command, else false. */ bool CaretUndoCommand::mergeWith(const CaretUndoCommand* /*command*/) { CaretLogWarning("The default implementation of CaretUndoCommand::mergeWith() was called and this should never happened. " "This method should have been overriden by its sub class."); return false; } connectome-workbench-1.2.3+git41-gc4c6c90/src/Common/CaretUndoCommand.h000066400000000000000000000112751300200146000253360ustar00rootroot00000000000000#ifndef __CARET_UNDO_COMMAND_H__ #define __CARET_UNDO_COMMAND_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*/ /* * NOTE: The Qt license is included because the CaretUndoStack API * including the method comments is copied from QUndoStack. */ /**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtOpenGL 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 Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/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 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #include "CaretObject.h" namespace caret { class CaretUndoCommand : public CaretObject { public: CaretUndoCommand(); virtual ~CaretUndoCommand(); AString getDescription() const; void setDescription(const AString& description); bool isMergeEnabled() const; void setMergeEnabled(const bool mergeStatus); virtual bool mergeWith(const CaretUndoCommand* command); int32_t getWindowIndex() const; void setWindowIndex(const int32_t windowIndex); /** * Operation that "redoes" the command. * * @param errorMessageOut * Output containing error message. * @return * True if the command executed successfully, else false. */ virtual bool redo(AString& errorMessageOut) = 0; /** * Operation that "undoes" the command. * * @param errorMessageOut * Output containing error message. * @return * True if the command executed successfully, else false. */ virtual bool undo(AString& errorMessageOut) = 0; // ADD_NEW_METHODS_HERE private: CaretUndoCommand(const CaretUndoCommand&); CaretUndoCommand& operator=(const CaretUndoCommand&); AString m_description; int32_t m_windowIndex; bool m_mergeFlag; // ADD_NEW_MEMBERS_HERE }; #ifdef __CARET_UNDO_COMMAND_DECLARE__ // #endif // __CARET_UNDO_COMMAND_DECLARE__ } // namespace #endif //__CARET_UNDO_COMMAND_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Common/CaretUndoStack.cxx000066400000000000000000000436561300200146000254100ustar00rootroot00000000000000 /*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*/ /* * NOTE: The Qt license is included because the CaretUndoStack API * including the method comments is copied from QUndoStack. */ /**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtOpenGL 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 Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/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 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #define __CARET_UNDO_STACK_DECLARE__ #include "CaretUndoStack.h" #undef __CARET_UNDO_STACK_DECLARE__ #include "CaretAssert.h" #include "CaretLogger.h" #include "CaretUndoCommand.h" using namespace caret; /** * \class caret::CaretUndoStack * \brief An abstract class for an undo stack implementation., * \ingroup Common * * Stack for undo and redo of commands. * * Design is based off Qt's QUndoStack. * * While using QUndoStack would be preferred, it cannot be used because: * (1) It is located in Qt's GUI module which is not accessible to * Caret's "lower level" modules. * (2) QUndoStack is targeted to text documents. * * An undo stack maintains a stack of commands that have been * applied to a document. * * New commands are pushed on the stack using push(). Commands can be undone * and redone using undo() and redo(). * * CaretUndoStack keeps track of the current command. This is the command which * will be executed by the next call to redo(). The index of this command is * returned by index(). If the top-most command on the stack has already * been redone, index() is equal to count(). */ /** * Constructs an empty undo stack in the "clean" state. */ CaretUndoStack::CaretUndoStack() : CaretObject() { m_undoLimit = 0; m_undoStackIndex = 0; } /** * Destroys the undo stack, deleting any commands that are on it. */ CaretUndoStack::~CaretUndoStack() { clear(); } /** * Returns true if there is a command available for redo; * otherwise returns false. * * This function returns false if the stack is empty or if the top * command on the stack has already been redone. * * While the Qt documentation states "Synonymous with index() == count()" * the actual QUndoStack code indicates the comment should be * "Synonymous with index() < count()". */ bool CaretUndoStack::canRedo() const { if (m_undoStack.empty()) { return false; } if (index() < count()) { return true; } return false; } /** * Returns true if there is a command available for undo; otherwise * returns false. * * This function returns false if the stack is empty, or if the bottom * command on the stack has already been undone. * * While the Qt documentation staties "Synonymous with index() == 0", viewing * the actual QUndoStack code indicates the comment should be * "Synonymous with index() > 0". */ bool CaretUndoStack::canUndo() const { if (m_undoStack.empty()) { return false; } if (index() > 0) { return true; } return false; } /** * @return Number of commands on the undo stack. */ int32_t CaretUndoStack::count() const { return m_undoStack.size(); } /** * Returns the index of the current command. This is the command that will * be executed on the next call to redo(). It is not always the top-most * command on the stack, since a number of commands may have been undone. * * @return Index of current command. This value ranges from 1 to count() * when the stack contains elements and zero when the stack is empty. */ int32_t CaretUndoStack::index() const { return m_undoStackIndex; } /** * Delete the command at the given index. The index of the current * command may change (if it is greater than the index). After * calling this method, the command that was at the index has * been deleted, so NEVER use it after calling this method. * * @param index * Index of command that is removed. */ void CaretUndoStack::deleteCommandAtIndex(const int32_t index) { if ((index >= 0) || (index < static_cast(m_undoStack.size()))) { CaretUndoCommand* undoCommand = m_undoStack.at(index); m_undoStack.erase(m_undoStack.begin() + index); delete undoCommand; } else { CaretLogWarning("CaretUndoStack::command() called with invalid index=" + AString::number(index)); } } /** * Clears the command stack by deleting all commands on it, and returns * the stack to the clean state. * * Commands are not undone or redone; the state of the edited object * remains unchanged. * * This function is usually used when the contents of the document * are abandoned. */ void CaretUndoStack::clear() { for (std::deque::iterator iter = m_undoStack.begin(); iter != m_undoStack.end(); iter++) { delete *iter; } m_undoStack.clear(); m_undoStackIndex = 0; } /** * Returns a const pointer to the command at index. * * This function returns a const pointer, because modifying a command, once * it has been pushed onto the stack and executed, almost always causes * corruption of the state of the document, if the command is later * undone or redone. * * @param index * Index of the item in the undo stack. * @return * Item at the given index or NULL if the index is invalid. */ const CaretUndoCommand* CaretUndoStack::command(const int32_t index) const { if ((index >= 0) || (index < static_cast(m_undoStack.size()))) { return m_undoStack.at(index); } else { CaretLogWarning("CaretUndoStack::command() called with invalid index=" + AString::number(index)); } return NULL; } /** * Execute (redo()) the command and then push the command. * * @seealso push() * * @param newCommand * Command that is executed and then pushed onto the stack. * @param windowIndex * Index of window (may be invalid => negative) * @param errorMessageOut * Output containing error message. * @return * True if the redo executed successfully, else false. */ bool CaretUndoStack::pushAndRedo(CaretUndoCommand* newCommand, const int32_t windowIndex, AString& errorMessageOut) { CaretAssert(newCommand); errorMessageOut.clear(); newCommand->setWindowIndex(windowIndex); const bool validFlag = newCommand->redo(errorMessageOut); newCommand->setWindowIndex(-1); push(newCommand); return validFlag; } /** * Pushes the given command onto the stack. Unlike QUndoStack the * command's redo() method IS NOT called. * * If commands were undone before cmd was pushed, the current command and * all commands above it are deleted. Hence cmd always ends up being the * top-most on the stack. * * If the command's merge status is set, an attempt will be made to * merge the given command with the command at the top of the stack. * If successful there is no need to push the new command onto the stack * since it has been merged and the command will be deleted. If merge * is NOT successful, the command will be pushed onto the stack. * * Once a command is pushed, the stack takes ownership of it. There are * no getters to return the command, since modifying it after it has been * executed will almost always lead to corruption of the document's state. * * @param newCommand * Command pushed onto the stack. */ void CaretUndoStack::push(CaretUndoCommand* newCommand) { if (index() < count()) { /* * Delete any commands that have been "undone" */ const int numToDeleteAtBack = count() - index(); for (int32_t i = 0; i < numToDeleteAtBack; i++) { delete m_undoStack.back(); m_undoStack.pop_back(); } } if (newCommand->isMergeEnabled()) { if ( ! m_undoStack.empty()) { CaretUndoCommand* command = m_undoStack.back(); CaretAssert(command); if (command->mergeWith(newCommand)) { delete newCommand; return; } } } m_undoStack.push_back(newCommand); if (m_undoLimit > 0) { /* * Delete oldest command(s) when undo limit is exceeded */ const int32_t numToDeleteAtFront = count() - m_undoLimit; for (int32_t i = 0; i < numToDeleteAtFront; i++) { delete m_undoStack.front(); m_undoStack.pop_front(); } } m_undoStackIndex = count(); } /** * Redoes the current command by calling QUndoCommand::redo(). * Increments the current command index. * * If the stack is empty, or if the top command on the stack has already * been redone, this function does nothing. * * @param windowIndex * Index of window in which redo was requested. * @param errorMessageOut * Output containing error message. * @return * True if the redo executed successfully, else false. */ bool CaretUndoStack::redoInWindow(const int32_t windowIndex, AString& errorMessageOut) { // redo(); errorMessageOut.clear(); if (m_undoStack.empty()) { errorMessageOut = "No command to redo."; return false; } bool validFlag = true; if ((m_undoStackIndex >= 0) && (m_undoStackIndex < count())) { CaretUndoCommand* command = m_undoStack.at(m_undoStackIndex); CaretAssert(command); command->setWindowIndex(windowIndex); validFlag = redoCommand(command, errorMessageOut); command->setWindowIndex(-1); ++m_undoStackIndex; } return validFlag; } /** * Redoes the current command by calling QUndoCommand::redo(). * Increments the current command index. * * If the stack is empty, or if the top command on the stack has already * been redone, this function does nothing. * * @param errorMessageOut * Output containing error message. * @return * True if the redo executed successfully, else false. */ bool CaretUndoStack::redo(AString& errorMessageOut) { return redoInWindow(-1, errorMessageOut); // if (m_undoStack.empty()) { // return; // } // // if ((m_undoStackIndex >= 0) // && (m_undoStackIndex < count())) { // redoCommand(m_undoStack.at(m_undoStackIndex)); // ++m_undoStackIndex; // } } /** * @return Returns the description of the command which will be * redone in the next call to redo(). */ AString CaretUndoStack::redoText() { AString text; if ( ! m_undoStack.empty()) { if ((m_undoStackIndex >= 0) && (m_undoStackIndex < count())) { text = m_undoStack.at(m_undoStackIndex)->getDescription(); } } return text; } /** * Undoes the command below the current command by calling QUndoCommand::undo(). * Decrements the current command index. * * If the stack is empty, or if the bottom command on the stack has already * been undone, this function does nothing. * * @param windowIndex * Index of window in which undo was requested. * @param errorMessageOut * Output containing error message. * @return * True if the redo executed successfully, else false. */ bool CaretUndoStack::undoInWindow(const int32_t windowIndex, AString& errorMessageOut) { // undo(); errorMessageOut.clear(); if (m_undoStack.empty()) { errorMessageOut = "No command to undo."; return false; } bool validFlag = true; if ((m_undoStackIndex > 0) && (m_undoStackIndex <= count())) { --m_undoStackIndex; CaretUndoCommand* command = m_undoStack.at(m_undoStackIndex); CaretAssert(command); command->setWindowIndex(windowIndex); validFlag = undoCommand(command, errorMessageOut); command->setWindowIndex(-1); } return validFlag; } /** * Undoes the command below the current command by calling QUndoCommand::undo(). * Decrements the current command index. * * If the stack is empty, or if the bottom command on the stack has already * been undone, this function does nothing. * * @param errorMessageOut * Output containing error message. * @return * True if the command executed successfully, else false.void */ bool CaretUndoStack::undo(AString& errorMessageOut) { return undoInWindow(-1, errorMessageOut); // if (m_undoStack.empty()) { // return; // } // // if ((m_undoStackIndex > 0) // && (m_undoStackIndex <= count())) { // --m_undoStackIndex; // undoCommand(m_undoStack.at(m_undoStackIndex)); // } } /** * @return Returns the description of the command which will be * undone in the next call to undo(). */ AString CaretUndoStack::undoText() { AString text; if ( ! m_undoStack.empty()) { if ((m_undoStackIndex > 0) && (m_undoStackIndex <= count())) { const int32_t undoIndex = m_undoStackIndex - 1; text = m_undoStack.at(undoIndex)->getDescription(); } } return text; } /** * When the number of commands on a stack exceedes the stack's undo limit, * commands are deleted from the bottom of the stack. The default * value is 0, which means that there is no limit. * * This property may only be set when the undo stack is empty, since setting * it on a non-empty stack might delete the command at the current index. * Calling setUndoLimit() on a non-empty stack and a warning is logged. * * @param undoLimit * New value for maximum number of undo commands. */ void CaretUndoStack::setUndoLimit(const int32_t undoLimit) { if (m_undoStack.empty()) { if (undoLimit >= 0) { m_undoLimit = undoLimit; } else { CaretLogWarning("CaretUndoStack::setUndoLimit() called with invalid value=" + AString::number(undoLimit)); } } else { CaretLogWarning("CaretUndoStack::setUndoLimit() called while undo stack contains elements." " New undo limit ignored."); } } /** * Apply a 'redo' using the given command. * * Subclasses may override this method. * * @param cmd * Command that performs a 'redo'. * @param errorMessageOut * Output containing error message. * @return * True if the command executed successfully, else false. */ bool CaretUndoStack::redoCommand(CaretUndoCommand* cmd, AString& errorMessageOut) { CaretAssert(cmd); const bool validFlag = cmd->redo(errorMessageOut); return validFlag; } /** * Apply an 'undo' using the given command. * * Subclasses may override this method. * * @param cmd * Command that performs a 'undo'. * @param errorMessageOut * Output containing error message. * @return * True if the command executed successfully, else false. */ bool CaretUndoStack::undoCommand(CaretUndoCommand* cmd, AString& errorMessageOut) { CaretAssert(cmd); return cmd->undo(errorMessageOut); } /** * Undo ALL changes. * * @param errorMessageOut * Output containing error message. * @return * True if the command executed successfully, else false. */ bool CaretUndoStack::undoAll(AString& errorMessageOut) { bool validFlag = true; while (canUndo()) { AString msg; if ( ! undo(msg)) { validFlag = false; errorMessageOut.appendWithNewLine(msg); } } return validFlag; } connectome-workbench-1.2.3+git41-gc4c6c90/src/Common/CaretUndoStack.h000066400000000000000000000124311300200146000250200ustar00rootroot00000000000000#ifndef __CARET_UNDO_STACK_H__ #define __CARET_UNDO_STACK_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*/ /* * NOTE: The Qt license is included because the CaretUndoStack API * including the method comments is copied from QUndoStack. */ /**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtOpenGL 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 Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/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 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #include #include "CaretObject.h" namespace caret { class CaretUndoCommand; class CaretUndoStack : public CaretObject { public: CaretUndoStack(); virtual ~CaretUndoStack(); bool canUndo() const; bool canRedo() const; void clear(); int32_t count() const; void deleteCommandAtIndex(const int32_t index); int32_t index() const; const CaretUndoCommand* command(const int32_t index) const; void push(CaretUndoCommand* newCommand); bool pushAndRedo(CaretUndoCommand* newCommand, const int32_t windowIndex, AString& errorMessageOut); bool redo(AString& errorMessageOut); bool redoInWindow(const int32_t windowIndex, AString& errorMessageOut); AString redoText(); bool undo(AString& errorMessageOut); bool undoInWindow(const int32_t windowIndex, AString& errorMessageOut); bool undoAll(AString& errorMessageOut); AString undoText(); void setUndoLimit(const int32_t undoLimit); // ADD_NEW_METHODS_HERE protected: virtual bool redoCommand(CaretUndoCommand* cmd, AString& errorMessageOut); virtual bool undoCommand(CaretUndoCommand* cmd, AString& errorMessageOut); private: CaretUndoStack(const CaretUndoStack&); CaretUndoStack& operator=(const CaretUndoStack&); /** * The undo "stack". A deque is used so that items * can be removed when the size of the "stack" exceeds * the undo limit. */ std::deque m_undoStack; /** * The index of the current command. */ int32_t m_undoStackIndex; int32_t m_undoLimit; // ADD_NEW_MEMBERS_HERE }; #ifdef __CARET_UNDO_STACK_DECLARE__ // #endif // __CARET_UNDO_STACK_DECLARE__ } // namespace #endif //__CARET_UNDO_STACK_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Common/CubicSpline.cxx000066400000000000000000000071271300200146000247270ustar00rootroot00000000000000/*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 "CubicSpline.h" #include "CaretAssert.h" using namespace caret; CubicSpline::CubicSpline() { } CubicSpline CubicSpline::hermite(float frac, bool lowEdge, bool highEdge) {//these equations are derived from hermite basis functions, plug the commented m0, m1 into the hermite representation to rederive CaretAssert(frac > -0.01f && frac < 1.01f);//give some leeway for rounding errors CubicSpline ret; float frac2 = frac * frac; float frac3 = frac2 * frac; if (lowEdge) {//edge case: m0 = p[2] - p[1] if (highEdge) {//edge case: m1 = p[2] - p[1] - makes it linear interpolation - why are you doing cubic spline with only 2 points? ret.m_weights[0] = 0.0f; ret.m_weights[1] = 1.0f - frac; ret.m_weights[2] = frac; ret.m_weights[3] = 0.0f; } else {//m1 = (p[3] - p[1]) / 2 ret.m_weights[0] = 0.0f; ret.m_weights[1] = 0.5f * frac3 - 0.5 * frac2 - frac + 1.0f;//.5t^3 - .5t^2 - t + 1 ret.m_weights[2] = -frac3 + frac2 + frac;//-t^3 + t^2 + t ret.m_weights[3] = 0.5f * frac3 - frac2;//.5t^3 - t^2 } } else {//m0 = (p[2] - p[0]) / 2 if (highEdge) {//edge case: m1 = p[2] - p[1] ret.m_weights[0] = -0.5f * frac3 + frac2 - 0.5f * frac;//-.5t^3 + t^2 - .5t ret.m_weights[1] = frac3 - 2.0f * frac2 + 1;//t^3 - 2t^2 + 1 ret.m_weights[2] = -0.5f * frac3 + frac2 + 0.5f * frac;//-.5t^3 + t^2 + .5t ret.m_weights[3] = 0.0f; } else {//m1 = (p[3] - p[1]) / 2 -- majority case ret.m_weights[0] = -0.5f * frac3 + frac2 - 0.5f * frac;//-.5t^3 + t^2 - .5t ret.m_weights[1] = 1.5f * frac3 - 2.5f * frac2 + 1.0f;//1.5t^3 - 2.5t^2 + 1 ret.m_weights[2] = -1.5f * frac3 + 2.0f * frac2 + 0.5f * frac;//-1.5t^3 + 2t^2 + .5t ret.m_weights[3] = 0.5f * frac3 - frac2;//.5t^3 - t^2 } } return ret; } CubicSpline CubicSpline::bspline(float frac, bool lowEdge, bool highEdge) { CaretAssert(frac > -0.01f && frac < 1.01f);//give some leeway for rounding errors CubicSpline ret; float frac2 = frac * frac; float frac3 = frac2 * frac; ret.m_weights[0] = (-frac3 + 3.0f * frac2 - 3.0f * frac + 1.0f) / 6.0f;//the standard blending function ret.m_weights[1] = (3.0f * frac3 - 6.0f * frac2 + 4.0f) / 6.0f; ret.m_weights[2] = (-3.0f * frac3 + 3.0f * frac2 + 3.0f * frac + 1.0f) / 6.0f; ret.m_weights[3] = frac3 / 6.0f; if (lowEdge) { ret.m_weights[1] += ret.m_weights[0];//pretend outside range repeats the edge value ret.m_weights[0] = 0.0f;//ignore input outside the range } if (highEdge) { ret.m_weights[2] += ret.m_weights[3];//ditto ret.m_weights[3] = 0.0f; } return ret; } connectome-workbench-1.2.3+git41-gc4c6c90/src/Common/CubicSpline.h000066400000000000000000000051161300200146000243500ustar00rootroot00000000000000#ifndef __CUBIC_SPLINE_H__ #define __CUBIC_SPLINE_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*/ namespace caret { class CubicSpline { float m_weights[4]; CubicSpline(); public: ///takes as input the fraction in [0, 1] along the middle (used) range of the spline, low and high edge set whether it doesn't have p[0] or p[3] to use, respectively static CubicSpline hermite(float frac, bool lowEdge, bool highEdge); ///NOTE: data should be deconvolved before using this spline static CubicSpline bspline(float frac, bool lowEdge, bool highEdge); //splines will be reused, so this part should be fast for the majority case (testing for if it is an edge case would slow it down for the majority case) ///evaluate the spline with these samples inline float evaluate(const float p0, const float p1, const float p2, const float p3) const { return p0 * m_weights[0] + p1 * m_weights[1] + p2 * m_weights[2] + p3 * m_weights[3]; } ///convenience function for edge evaluating without a dummy argument inline float evalLowEdge(const float p1, const float p2, const float p3) { return p1 * m_weights[1] + p2 * m_weights[2] + p3 * m_weights[3]; } ///convenience function for edge evaluating without a dummy argument inline float evalHighEdge(const float p0, const float p1, const float p2) { return p0 * m_weights[0] + p1 * m_weights[1] + p2 * m_weights[2]; } ///convenience function for edge evaluating without dummy arguments inline float evalBothEdge(const float p1, const float p2) { return p1 * m_weights[1] + p2 * m_weights[2]; } }; } #endif //__CUBIC_SPLINE_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Common/DataCompressZLib.cxx000066400000000000000000000100551300200146000256670ustar00rootroot00000000000000/*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*/ /*========================================================================= Program: Visualization Toolkit Module: $RCSfile: DataCompressZLib.cxx,v $ Copyright (c) Ken Martin, Will Schroeder, Bill Lorensen All rights reserved. See Copyright.txt or http://www.kitware.com/Copyright.htm for details. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the above copyright notice for more information. =========================================================================*/ #include "DataCompressZLib.h" #include "MathFunctions.h" #include "zlib.h" using namespace caret; //---------------------------------------------------------------------------- DataCompressZLib::DataCompressZLib() { this->compressionLevel = Z_DEFAULT_COMPRESSION; } //---------------------------------------------------------------------------- DataCompressZLib::~DataCompressZLib() { } int32_t DataCompressZLib::getCompressionLevel() { return this->compressionLevel; } void DataCompressZLib::setCompressionLevel(const int32_t compressionLevel) { this->compressionLevel = MathFunctions::clamp(compressionLevel, 0, 9); } //---------------------------------------------------------------------------- uint64_t DataCompressZLib::compressData(const unsigned char* uncompressedData, uint64_t uncompressedSize, unsigned char* compressedData, const uint64_t compressionSpace) { uLongf compressedSize = compressionSpace; Bytef* cd = reinterpret_cast(compressedData); const Bytef* ud = reinterpret_cast(uncompressedData); // Call zlib's compress function. if(compress2(cd, &compressedSize, ud, uncompressedSize, this->compressionLevel) != Z_OK) { //vtkErrorMacro("Zlib error while compressing data."); return 0; } return compressedSize; } //---------------------------------------------------------------------------- uint64_t DataCompressZLib::uncompressData(const unsigned char* compressedData, uint64_t compressedSize, unsigned char* uncompressedData, uint64_t uncompressedSize) { uLongf decSize = uncompressedSize; Bytef* ud = reinterpret_cast(uncompressedData); const Bytef* cd = reinterpret_cast(compressedData); // Call zlib's uncompress function. if(uncompress(ud, &decSize, cd, compressedSize) != Z_OK) { //vtkErrorMacro("Zlib error while uncompressing data."); return 0; } // Make sure the output size matched that expected. if(decSize != uncompressedSize) { //vtkErrorMacro("Decompression produced incorrect size.\n" // "Expected " << uncompressedSize << " and got " << decSize); return 0; } return decSize; } //---------------------------------------------------------------------------- unsigned long DataCompressZLib::getMaximumCompressionSpace(unsigned long size) { // ZLib specifies that destination buffer must be 0.1% larger + 12 bytes. return size + (size+999)/1000 + 12; } connectome-workbench-1.2.3+git41-gc4c6c90/src/Common/DataCompressZLib.h000066400000000000000000000062041300200146000253150ustar00rootroot00000000000000#ifndef __DATA_COMPRESS_ZLIB_H__ #define __DATA_COMPRESS_ZLIB_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*/ /*========================================================================= Program: Visualization Toolkit Module: $RCSfile: DataCompressZLib.h,v $ Copyright (c) Ken Martin, Will Schroeder, Bill Lorensen All rights reserved. See Copyright.txt or http://www.kitware.com/Copyright.htm for details. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the above copyright notice for more information. =========================================================================*/ // .NAME DataCompressZLib - Data compression using zlib. // .SECTION Description // DataCompressZLib provides a concrete vtkDataCompressor class // using zlib for compressing and uncompressing data. #include #include "CaretObject.h" namespace caret { /* * Copied from vtkZLibDataCompresson. */ class DataCompressZLib : public CaretObject { public: DataCompressZLib(); ~DataCompressZLib(); // Description: // Get the maximum space that may be needed to store data of the // given uncompressed size after compression. This is the minimum // size of the output buffer that can be passed to the four-argument // Compress method. unsigned long getMaximumCompressionSpace(unsigned long size); // Description: // Get/Set the compression level. //vtkSetClampMacro(CompressionLevel, int, 0, 9); //vtkGetMacro(CompressionLevel, int); int32_t getCompressionLevel(); void setCompressionLevel(const int32_t compressionLevel); // Compression method required by vtkDataCompressor. uint64_t compressData(const unsigned char* uncompressedData, uint64_t uncompressedSize, unsigned char* compressedData, const uint64_t compressionSpace); // Decompression method required by vtkDataCompressor. uint64_t uncompressData(const unsigned char* compressedData, uint64_t compressedSize, unsigned char* uncompressedData, uint64_t uncompressedSiz); protected: int compressionLevel; }; } // namespace #endif // __DATA_COMPRESS_ZLIB_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Common/DataFile.cxx000066400000000000000000000131541300200146000241750ustar00rootroot00000000000000/*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 "DataFile.h" #include "DataFileContentInformation.h" #include "DataFileException.h" #include "FileInformation.h" using namespace caret; /** * Constructor. */ DataFile::DataFile() : CaretObject(), DataFileInterface() { this->initializeMembersDataFile(); } /** * Destructor. */ DataFile::~DataFile() { this->initializeMembersDataFile(); } /** * Copy constructor. * @param df * Data file that is copied. */ DataFile::DataFile(const DataFile& df) : CaretObject(df), DataFileInterface() { this->copyHelperDataFile(df); } /** * Assignment operator. * @param df * Contents that is assigned to this file. * @return * This file assigned the contents of "df". */ DataFile& DataFile::operator=(const DataFile& df) { if (this != &df) { CaretObject::operator=(df); this->copyHelperDataFile(df); } return *this; } /** * Assists with copying file's contents. */ void DataFile::copyHelperDataFile(const DataFile& df) { this->filename = df.filename; modifiedFlag = false; } /** * Initialize the members of the data file. */ void DataFile::initializeMembersDataFile() { this->filename = ""; modifiedFlag = false; } /** * Clear the contents of the file. */ void DataFile::clear() { this->initializeMembersDataFile(); } /** * @return Name of the data file including any path. */ AString DataFile::getFileName() const { return this->filename; } /** * @return Name of the data file excluding any path. */ AString DataFile::getFileNameNoPath() const { FileInformation fileInfo(this->filename); return fileInfo.getFileName(); } /** * Set the name of the data file. * * @param filename * New name of data file. */ void DataFile::setFileName(const AString& filename) { if (this->filename != filename) { this->filename = filename; this->setModified(); } } /** * Add information about the file to the data file information. * * @param dataFileInformation * Consolidates information about a data file. */ void DataFile::addToDataFileContentInformation(DataFileContentInformation& dataFileInformation) { dataFileInformation.addNameAndValue("Name", getFileName()); } /** * Set the status to modified. */ void DataFile::setModified() { this->modifiedFlag = true; } /** * Set the status to unmodified. */ void DataFile::clearModified() { this->modifiedFlag = false; } /** * Is the object modified? * @return true if modified, else false. */ bool DataFile::isModified() const { return this->modifiedFlag; } /** * Is the filename a path on the network (http:// etc) * @param filename * Name of file. * @return true if filename appears to be a network path. */ bool DataFile::isFileOnNetwork(const AString& filename) { if (filename.startsWith("http://") || filename.startsWith("https://")) { return true; } return false; } /** * If the filename is local, make sure the file exists and * its permissions allow the file to be read. * * @param filename * Name of file. * @throws DataFileException * If there is a problem that will prevent the file from being read. */ void DataFile::checkFileReadability(const AString& filename) { if (filename.isEmpty()) { throw DataFileException("Name of file for reading is empty."); } if (isFileOnNetwork(filename)) { return; } FileInformation fileInfo(filename); if (fileInfo.exists() == false) { throw DataFileException(filename, "File does not exist."); } if (fileInfo.isDirectory()) { throw DataFileException(filename, "Filename is a directory, not a file."); } if (fileInfo.isReadable() == false) { throw DataFileException(filename, "File is not readable due its permissions."); } } /** * If the filename is local, see if the file exists and * its permissions allow the file to be written. * * @param filename * Name of file. * @throws DataFileException * If there is a problem that will prevent the file from being read. */ void DataFile::checkFileWritability(const AString& filename) { if (filename.isEmpty()) { throw DataFileException("Name of file for writing is empty."); } if (isFileOnNetwork(filename)) { return; } FileInformation fileInfo(filename); if (fileInfo.exists()) { if (fileInfo.isDirectory()) { throw DataFileException(filename, "Filename is a directory, not a file."); } if (fileInfo.isWritable() == false) { throw DataFileException(filename, "File is not writable due its permissions."); } } } connectome-workbench-1.2.3+git41-gc4c6c90/src/Common/DataFile.h000066400000000000000000000056151300200146000236250ustar00rootroot00000000000000#ifndef __DATAFILE_H__ #define __DATAFILE_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 "CaretObject.h" #include "DataFileInterface.h" namespace caret { class DataFileContentInformation; /** * Abstract Data File. */ class DataFile : public CaretObject, public DataFileInterface { protected: DataFile(); virtual ~DataFile(); DataFile(const DataFile& s); DataFile& operator=(const DataFile&); public: virtual AString getFileName() const; virtual AString getFileNameNoPath() const; virtual void setFileName(const AString& filename); virtual void addToDataFileContentInformation(DataFileContentInformation& dataFileInformation); virtual void setPreferOnDiskReading(const bool&) { } /** * Read the data file. * * @param filename * Name of the data file. * @throws DataFileException * If the file was not successfully read. */ virtual void readFile(const AString& filename) = 0; /** * Write the data file. * * @param filename * Name of the data file. * @throws DataFileException * If the file was not successfully written. */ virtual void writeFile(const AString& filename) = 0; virtual void setModified(); virtual void clearModified(); virtual bool isModified() const; virtual void clear(); static bool isFileOnNetwork(const AString& filename); void checkFileReadability(const AString& filename); void checkFileWritability(const AString& filename); private: void copyHelperDataFile(const DataFile& df); void initializeMembersDataFile(); /** name of data file */ AString filename; /** modification status */ bool modifiedFlag; }; } // namespace #endif // __DATAFILE_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Common/DataFileContentCopyMoveInterface.h000066400000000000000000000051351300200146000304600ustar00rootroot00000000000000#ifndef __DATA_FILE_CONTENT_COPY_MOVE_INTERFACE_H__ #define __DATA_FILE_CONTENT_COPY_MOVE_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 "CaretObject.h" /** * \class caret::DataFileContentCopyMoveInterface * \brief Interface for data file that can copy their content to other files of same type * \ingroup Common */ namespace caret { class DataFile; class DataFileContentCopyMoveParameters; class DataFileContentCopyMoveInterface : public CaretObject { public: DataFileContentCopyMoveInterface() { } virtual ~DataFileContentCopyMoveInterface() { } /** * @return Pointer to DataFile that implements this interface */ virtual DataFile* getAsDataFile() = 0; /** * Append content from the given data file copy/move interface to this instance * * @param copyMoveParameters * Parameters used for copy/move. * @throws DataFileException * If there is an error. */ virtual void appendContentFromDataFile(const DataFileContentCopyMoveParameters& copyMoveParameters) = 0; /** * @return A new instance of the same file type. File is empty. */ virtual DataFileContentCopyMoveInterface* newInstanceOfDataFile() const = 0; private: DataFileContentCopyMoveInterface(const DataFileContentCopyMoveInterface&); DataFileContentCopyMoveInterface& operator=(const DataFileContentCopyMoveInterface&); // ADD_NEW_MEMBERS_HERE }; #ifdef __DATA_FILE_CONTENT_COPY_MOVE_INTERFACE_DECLARE__ // #endif // __DATA_FILE_CONTENT_COPY_MOVE_INTERFACE_DECLARE__ } // namespace #endif //__DATA_FILE_CONTENT_COPY_MOVE_INTERFACE_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Common/DataFileContentCopyMoveParameters.cxx000066400000000000000000000053171300200146000312400ustar00rootroot00000000000000 /*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 __DATA_FILE_CONTENT_COPY_MOVE_PARAMETERS_DECLARE__ #include "DataFileContentCopyMoveParameters.h" #undef __DATA_FILE_CONTENT_COPY_MOVE_PARAMETERS_DECLARE__ #include "CaretAssert.h" using namespace caret; /** * \class caret::DataFileContentCopyMoveParameters * \brief Parameters for copying/moving data in data files. * \ingroup Common */ /** * Constructor for data file that is copied/moved with all parameters off. * * @param dataFileCopyMoveInterface * Interface of file that is copied/moved. * @param windowIndex * Index of window (negative if invalid). */ DataFileContentCopyMoveParameters::DataFileContentCopyMoveParameters(const DataFileContentCopyMoveInterface* dataFileCopyMoveInterface, const int32_t windowIndex) : CaretObject(), m_dataFileCopyMoveInterface(dataFileCopyMoveInterface), m_windowIndex(windowIndex), m_optionSelectedItems(false) { } /** * Destructor. */ DataFileContentCopyMoveParameters::~DataFileContentCopyMoveParameters() { } /** * @return Copy/Move interface for file whose content is copy/moved. */ const DataFileContentCopyMoveInterface* DataFileContentCopyMoveParameters::getDataFileCopyMoveInterfaceToCopy() const { return m_dataFileCopyMoveInterface; } /** * @return Index of window (invalid if negative). */ int32_t DataFileContentCopyMoveParameters::getWindowIndex() const { return m_windowIndex; } /** * @return Is the option for copy/move selected items set? */ bool DataFileContentCopyMoveParameters::isOptionSelectedItems() const { return m_optionSelectedItems; } /** * Set the option for copy/move selected items. * * @param optionSelectedItems * New status for copy/move of selected items. */ void DataFileContentCopyMoveParameters::setOptionSelectedItems(const bool optionSelectedItems) { m_optionSelectedItems = optionSelectedItems; } connectome-workbench-1.2.3+git41-gc4c6c90/src/Common/DataFileContentCopyMoveParameters.h000066400000000000000000000044711300200146000306650ustar00rootroot00000000000000#ifndef __DATA_FILE_CONTENT_COPY_MOVE_PARAMETERS_H__ #define __DATA_FILE_CONTENT_COPY_MOVE_PARAMETERS_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" namespace caret { class DataFileContentCopyMoveInterface; class DataFileContentCopyMoveParameters : public CaretObject { public: DataFileContentCopyMoveParameters(const DataFileContentCopyMoveInterface* dataFileCopyMoveInterface, const int32_t windowIndex); virtual ~DataFileContentCopyMoveParameters(); const DataFileContentCopyMoveInterface* getDataFileCopyMoveInterfaceToCopy() const; int32_t getWindowIndex() const; bool isOptionSelectedItems() const; void setOptionSelectedItems(const bool optionSelectedItems); // ADD_NEW_METHODS_HERE private: DataFileContentCopyMoveParameters(const DataFileContentCopyMoveParameters&); DataFileContentCopyMoveParameters& operator=(const DataFileContentCopyMoveParameters&); const DataFileContentCopyMoveInterface* m_dataFileCopyMoveInterface; const int32_t m_windowIndex; bool m_optionSelectedItems; // ADD_NEW_MEMBERS_HERE }; #ifdef __DATA_FILE_CONTENT_COPY_MOVE_PARAMETERS_DECLARE__ // #endif // __DATA_FILE_CONTENT_COPY_MOVE_PARAMETERS_DECLARE__ } // namespace #endif //__DATA_FILE_CONTENT_COPY_MOVE_PARAMETERS_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Common/DataFileContentInformation.cxx000066400000000000000000000137251300200146000277420ustar00rootroot00000000000000 /*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 __DATA_FILE_CONTENT_INFORMATION_DECLARE__ #include "DataFileContentInformation.h" #undef __DATA_FILE_CONTENT_INFORMATION_DECLARE__ #include "CaretAssert.h" using namespace caret; /** * \class caret::DataFileContentInformation * \brief Assembles and provides information about a data file. * \ingroup Common */ /** * Constructor. */ DataFileContentInformation::DataFileContentInformation() : CaretObject() { /* * Initialize options */ setOptionFlag(OPTION_SHOW_MAP_INFORMATION, true); setOptionFlag(OPTION_SHOW_CIFTI_LABEL_MAPPING, false); } /** * Destructor. */ DataFileContentInformation::~DataFileContentInformation() { } /** * Add a name and value pair. * * @param name * The name. * @param value * The value. */ void DataFileContentInformation::addNameAndValue(const AString& name, const AString& value) { QChar colonChar = ' '; if (name.indexNotOf(' ') >= 0) { colonChar = ':'; } m_namesAndValues.push_back(std::make_pair((name + colonChar), value)); } /** * Add a name and value pair. * * @param name * The name. * @param value * The value. */ void DataFileContentInformation::addNameAndValue(const AString& name, const int32_t value) { addNameAndValue(name, AString::number(value)); // m_namesAndValues.push_back(std::make_pair((name + ":"), // AString::number(value))); } /** * Add a name and value pair. * * @param name * The name. * @param value * The value. */ void DataFileContentInformation::addNameAndValue(const AString& name, const int64_t value) { addNameAndValue(name, AString::number(value)); // m_namesAndValues.push_back(std::make_pair((name + ":"), // AString::number(value))); } /** * Add a name and value pair. * * @param name * The name. * @param value * The value. */ void DataFileContentInformation::addNameAndValue(const AString& name, const double value, const int32_t precision) { addNameAndValue(name, AString::number(value, 'f', precision)); // m_namesAndValues.push_back(std::make_pair((name + ":"), // AString::number(value, 'f', precision))); } /** * Add a name and value pair. * * @param name * The name. * @param value * The value. */ void DataFileContentInformation::addNameAndValue(const AString& name, const bool value) { addNameAndValue(name, AString::fromBool(value)); // m_namesAndValues.push_back(std::make_pair((name + ":"), // AString::fromBool(value))); } /** * Add freeform text. * * @param text * Text that is added. */ void DataFileContentInformation::addText(const AString& text) { m_text.append(text); } /** * @return All of the information in a string. * * The names and labels are formatted so that the labels and * values are aligned. */ AString DataFileContentInformation::getInformationInString() const { AString textOut; const int32_t numNamesAndValues = static_cast(m_namesAndValues.size()); if (numNamesAndValues > 0) { int32_t longestLabelLength = 0; for (int32_t i = 0; i < numNamesAndValues; i++) { longestLabelLength = std::max(m_namesAndValues[i].first.length(), longestLabelLength); } longestLabelLength += 3; for (int32_t i = 0; i < numNamesAndValues; i++) { AString label = m_namesAndValues[i].first; textOut.appendWithNewLine(label.leftJustified(longestLabelLength) + m_namesAndValues[i].second); } textOut.append("\n"); } textOut.append(m_text); return textOut; } /** * Is an option flag on? * * @param optionFlag * The option flag. * @return * True if the option flag is on, else false. */ bool DataFileContentInformation::isOptionFlag(const OptionFlag optionFlag) const { std::map::const_iterator iter = m_optionFlags.find(optionFlag); if (iter != m_optionFlags.end()) { return iter->second; } return false; } /** * Set an option flag. * * @param optionFlag * The option flag. * @param flagValue * New value for option flag. */ void DataFileContentInformation::setOptionFlag(const OptionFlag optionFlag, const bool flagValue) { std::map::iterator iter = m_optionFlags.find(optionFlag); if (iter != m_optionFlags.end()) { iter->second = flagValue; } else { m_optionFlags.insert(std::pair(optionFlag, flagValue)); } } connectome-workbench-1.2.3+git41-gc4c6c90/src/Common/DataFileContentInformation.h000066400000000000000000000063331300200146000273640ustar00rootroot00000000000000#ifndef __DATA_FILE_CONTENT_INFORMATION_H__ #define __DATA_FILE_CONTENT_INFORMATION_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 "CaretObject.h" namespace caret { class DataFileContentInformation : public CaretObject { public: /** * Option flags for commands. * All options are off by default. * If an option should be on by default, call setOptionFlag() * from this class' constructor. */ enum OptionFlag { /** * Show information about each map for files that support maps. */ OPTION_SHOW_MAP_INFORMATION, /* * Show detailed information about CIFTI Label Mappings */ OPTION_SHOW_CIFTI_LABEL_MAPPING }; DataFileContentInformation(); virtual ~DataFileContentInformation(); void addNameAndValue(const AString& name, const AString& value); void addNameAndValue(const AString& name, const int32_t value); void addNameAndValue(const AString& name, const int64_t value); void addNameAndValue(const AString& name, const double value, const int32_t precision = 3); void addNameAndValue(const AString& name, const bool value); void addText(const AString& text); AString getInformationInString() const; bool isOptionFlag(const OptionFlag optionFlag) const; void setOptionFlag(const OptionFlag optionFlag, const bool flagValue); private: DataFileContentInformation(const DataFileContentInformation&); DataFileContentInformation& operator=(const DataFileContentInformation&); public: // ADD_NEW_METHODS_HERE private: std::vector > m_namesAndValues; AString m_text; std::map m_optionFlags; // ADD_NEW_MEMBERS_HERE }; #ifdef __DATA_FILE_CONTENT_INFORMATION_DECLARE__ // #endif // __DATA_FILE_CONTENT_INFORMATION_DECLARE__ } // namespace #endif //__DATA_FILE_CONTENT_INFORMATION_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Common/DataFileException.cxx000066400000000000000000000075041300200146000260560ustar00rootroot00000000000000/*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 "DataFileException.h" #include "FileInformation.h" #include using namespace caret; /** * Constructor. * */ DataFileException::DataFileException() : CaretException() { this->initializeMembersDataFileException(); } /** * Constructor that uses stack trace from the exception * passed in as a parameter. * * @param e Any exception whose stack trace becomes * this exception's stack trace. * */ DataFileException::DataFileException( const CaretException& e) : CaretException(e) { this->initializeMembersDataFileException(); } /** * Constructor that accepts the exception messge. * * @param s Description of the exception. * */ DataFileException::DataFileException(const AString& s) : CaretException(s) { this->initializeMembersDataFileException(); } /** * Constructor that accepts name of file and the exception message. * * The exception message will become name of the file, a newline, * path of the file, a newline, and the description of the * exception. * * @param dataFileName Name of the data file that caused exception. * @param s Description of the exception. * */ DataFileException::DataFileException(const AString& dataFileName, const AString& s) : CaretException(s) { this->initializeMembersDataFileException(); AString msg = s; if ( ! dataFileName.isEmpty()) { msg += "\n"; FileInformation fileInfo(dataFileName); const AString pathName = fileInfo.getPathName(); msg.appendWithNewLine("File: " + fileInfo.getFileName()); if ( ! pathName.isEmpty()) { if (pathName != ".") { msg.appendWithNewLine("Path: " + pathName); } } } this->setExceptionDescription(msg); } /** * Copy Constructor. * @param e * Exception that is copied. */ DataFileException::DataFileException(const DataFileException& e) : CaretException(e) { this->errorInvalidStructure = e.errorInvalidStructure; } /** * Assignment operator. * @param e * Exception that is copied. * @return * Copy of the exception. */ DataFileException& DataFileException::operator=(const DataFileException& e) { if (this != &e) { CaretException::operator=(e); this->errorInvalidStructure = e.errorInvalidStructure; } return *this; } /** * Destructor */ DataFileException::~DataFileException() throw() { } void DataFileException::initializeMembersDataFileException() { this->errorInvalidStructure = false; } /** * @return True if the file could not be read due * to an invalid structure. */ bool DataFileException::isErrorInvalidStructure() const { return this->errorInvalidStructure; } /** * Set the invalid structure status when the file * cannot be read due to the structure being invalid. * * @param status * New invalid structure status (true if invalid). */ void DataFileException::setErrorInvalidStructure(const bool status) { this->errorInvalidStructure = status; } connectome-workbench-1.2.3+git41-gc4c6c90/src/Common/DataFileException.h000066400000000000000000000033441300200146000255010ustar00rootroot00000000000000#ifndef __DATAFILEEXCEPTION_H__ #define __DATAFILEEXCEPTION_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 "CaretException.h" namespace caret { /** * An exception thrown during data file operations. */ class DataFileException : public CaretException { public: DataFileException(); DataFileException(const CaretException& e); DataFileException(const AString& s); DataFileException(const AString& dataFileName, const AString& s); DataFileException(const DataFileException& e); DataFileException& operator=(const DataFileException& e); virtual ~DataFileException() throw(); bool isErrorInvalidStructure() const; void setErrorInvalidStructure(const bool status); private: void initializeMembersDataFileException(); bool errorInvalidStructure; }; } // namespace #endif // __DATAFILEEXCEPTION_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Common/DataFileInterface.h000066400000000000000000000050741300200146000254450ustar00rootroot00000000000000#ifndef __DATAFILEINTERFACE_H__ #define __DATAFILEINTERFACE_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 "TracksModificationInterface.h" namespace caret { /** * Abstract class for data file behavior. */ class DataFileInterface : public TracksModificationInterface { protected: DataFileInterface() { } virtual ~DataFileInterface() { } private: DataFileInterface(const DataFileInterface& s); DataFileInterface& operator=(const DataFileInterface&); public: /** * Is the file empty (contains no data)? * * @return * true if the file is empty, else false. */ virtual bool isEmpty() const = 0; /** * Get the name of the data file. * * @return Name of the data file. */ virtual AString getFileName() const = 0; /** * Set the name of the data file. * * @param filename * New name of data file. */ virtual void setFileName(const AString& filename) = 0; /** * Read the data file. * * @param filename * Name of the data file. * @throws DataFileException * If the file was not successfully read. */ virtual void readFile(const AString& filename) = 0; /** * Write the data file. * * @param filename * Name of the data file. * @throws DataFileException * If the file was not successfully written. */ virtual void writeFile(const AString& filename) = 0; }; } // namespace #endif // __DATAFILEINTERFACE_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Common/DataFileTypeEnum.cxx000066400000000000000000000725101300200146000256650ustar00rootroot00000000000000 /*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 __DATA_FILE_TYPE_ENUM_DECLARE__ #include "DataFileTypeEnum.h" #undef __DATA_FILE_TYPE_ENUM_DECLARE__ #include "CaretAssert.h" #include "CaretLogger.h" using namespace caret; /** * \class caret::DataFileTypeEnum * \brief An enumerated type for data files. */ /** * Constructor. * * @param enumValue * An enumerated value. * @param name * Name of enumberated value. * @param guiName * Name displayed in the user-interface * @param overlayTypeName * Name displayed in overlay type combo box * @param fileIsUsedWithOneStructure * True if file is used with ONE structure (eg node file (surface, metric, etc). * @param fileExtensionOne * File extension * @param fileExtensionTwo * Additional File extension * @param fileExtensionThree * Additional File extension */ DataFileTypeEnum::DataFileTypeEnum(const Enum enumValue, const AString& name, const AString& guiName, const AString& overlayTypeName, const bool fileIsUsedWithOneStructure, const AString& fileExtensionOne, const AString& fileExtensionTwo, const AString& fileExtensionThree, const AString& fileExtensionFour) { this->enumValue = enumValue; this->integerCode = DataFileTypeEnum::integerCodeGenerator++; this->name = name; this->guiName = guiName; this->overlayTypeName = overlayTypeName; this->oneStructureFlag = fileIsUsedWithOneStructure; if (fileExtensionOne.isEmpty() == false) { this->fileExtensions.push_back(fileExtensionOne); } if (fileExtensionTwo.isEmpty() == false) { this->fileExtensions.push_back(fileExtensionTwo); } if (fileExtensionThree.isEmpty() == false) { this->fileExtensions.push_back(fileExtensionThree); } if (fileExtensionFour.isEmpty() == false) { this->fileExtensions.push_back(fileExtensionFour); } AString filterText = this->guiName + " Files ("; for (std::vector::const_iterator iter = this->fileExtensions.begin(); iter != this->fileExtensions.end(); iter++) { if (iter != fileExtensions.begin()) { filterText += " "; } filterText += ("*." + *iter); } filterText += ")"; this->qFileDialogNameFilter = filterText; } /** * Destructor. */ DataFileTypeEnum::~DataFileTypeEnum() { } /** * Initialize the enumerated metadata. */ void DataFileTypeEnum::initialize() { if (initializedFlag) { return; } initializedFlag = true; enumData.push_back(DataFileTypeEnum(ANNOTATION, "ANNOTATION", "Annotation", "ANNOTATION", false, "annot", "wb_annot")); enumData.push_back(DataFileTypeEnum(BORDER, "BORDER", "Border", "BORDER", true, "border", "wb_border")); enumData.push_back(DataFileTypeEnum(CONNECTIVITY_DENSE, "CONNECTIVITY_DENSE", "Connectivity - Dense", "CONNECTIVITY", false, "dconn.nii")); enumData.push_back(DataFileTypeEnum(CONNECTIVITY_DENSE_DYNAMIC, "CONNECTIVITY_DENSE_DYNAMIC", "Connectivity - Dense Dynamic", "CONNECTIVITY DYNAMIC", false, "dynconn.nii")); enumData.push_back(DataFileTypeEnum(CONNECTIVITY_DENSE_LABEL, "CONNECTIVITY_DENSE_LABEL", "Connectivity - Dense Label", "CIFTI LABELS", false, "dlabel.nii")); enumData.push_back(DataFileTypeEnum(CONNECTIVITY_DENSE_PARCEL, "CONNECTIVITY_DENSE_PARCEL", "Connectivity - Dense Parcel", "CIFTI DENSE PARCEL", false, "dpconn.nii")); enumData.push_back(DataFileTypeEnum(CONNECTIVITY_DENSE_SCALAR, "CONNECTIVITY_DENSE_SCALAR", "Connectivity - Dense Scalar", "CIFTI SCALARS", false, "dscalar.nii")); enumData.push_back(DataFileTypeEnum(CONNECTIVITY_DENSE_TIME_SERIES, "CONNECTIVITY_DENSE_TIME_SERIES", "Connectivity - Dense Data Series", "DATA SERIES", false, "dtseries.nii")); enumData.push_back(DataFileTypeEnum(CONNECTIVITY_FIBER_ORIENTATIONS_TEMPORARY, "CONNECTIVITY_FIBER_ORIENTATIONS_TEMPORARY", "Connectivity - 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", "FIBER TRAJECTORY TEMPORARY", false, "trajTEMP.wbsparse")); enumData.push_back(DataFileTypeEnum(CONNECTIVITY_PARCEL, "CONNECTIVITY_PARCEL", "Connectivity - Parcel", "CIFTI PARCEL", false, "pconn.nii")); enumData.push_back(DataFileTypeEnum(CONNECTIVITY_PARCEL_DENSE, "CONNECTIVITY_PARCEL_DENSE", "Connectivity - Parcel Dense", "CIFTI PARCEL DENSE", false, "pdconn.nii")); enumData.push_back(DataFileTypeEnum(CONNECTIVITY_PARCEL_LABEL, "CONNECTIVITY_PARCEL_LABEL", "Connectivity - Parcel Label", "CIFTI PARCEL LABEL", false, "plabel.nii")); enumData.push_back(DataFileTypeEnum(CONNECTIVITY_PARCEL_SCALAR, "CONNECTIVITY_PARCEL_SCALAR", "Connectivity - Parcel Scalar", "CIFTI PARCEL SCALAR", false, "pscalar.nii")); enumData.push_back(DataFileTypeEnum(CONNECTIVITY_PARCEL_SERIES, "CONNECTIVITY_PARCEL_SERIES", "Connectivity - 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", false, "sdseries.nii")); enumData.push_back(DataFileTypeEnum(FOCI, "FOCI", "Foci", "FOCI", false, "foci", "wb_foci")); enumData.push_back(DataFileTypeEnum(IMAGE, "IMAGE", "Image", "IMAGE", false, "jpg", "jpeg", "png", "ppm")); enumData.push_back(DataFileTypeEnum(LABEL, "LABEL", "Label", "LABEL", true, "label.gii")); enumData.push_back(DataFileTypeEnum(METRIC, "METRIC", "Metric", "METRIC", true, "func.gii", "shape.gii")); enumData.push_back(DataFileTypeEnum(PALETTE, "PALETTE", "Palette", "PALETTE", false, "palette", "wb_palette")); enumData.push_back(DataFileTypeEnum(RGBA, "RGBA", "RGBA", "RGBA", true, "rgba.gii")); enumData.push_back(DataFileTypeEnum(SCENE, "SCENE", "Scene", "SCENE", false, "scene", "wb_scene")); enumData.push_back(DataFileTypeEnum(SPECIFICATION, "SPECIFICATION", "Specification", "SPECIFICATION", false, "spec", "wb_spec")); enumData.push_back(DataFileTypeEnum(SURFACE, "SURFACE", "Surface", "SURFACE", true, "surf.gii")); enumData.push_back(DataFileTypeEnum(UNKNOWN, "UNKNOWN", "Unknown", "UNKNOWN", false, "unknown")); enumData.push_back(DataFileTypeEnum(VOLUME, "VOLUME", "Volume", "VOLUME", false, "nii", "nii.gz")); } /** * 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 DataFileTypeEnum* DataFileTypeEnum::findData(const Enum enumValue) { if (initializedFlag == false) initialize(); size_t num = enumData.size(); for (size_t i = 0; i < num; i++) { const DataFileTypeEnum* 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 DataFileTypeEnum::toName(Enum enumValue) { if (initializedFlag == false) initialize(); const DataFileTypeEnum* 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. */ DataFileTypeEnum::Enum DataFileTypeEnum::fromName(const AString& nameIn, bool* isValidOut) { AString name = nameIn; if (initializedFlag == false) initialize(); bool validFlag = false; Enum enumValue = UNKNOWN; /* * Maintain compatibility with early spec files */ if (name.startsWith("SURFACE_")) { CaretLogWarning("Obsolete spec file tag \"" + name + "\", replace with SURFACE"); name = "SURFACE"; } else if (name.startsWith("VOLUME_")) { CaretLogWarning("Obsolete spec file tag \"" + name + "\", replace with VOLUME"); name = "VOLUME"; } for (std::vector::iterator iter = enumData.begin(); iter != enumData.end(); iter++) { const DataFileTypeEnum& 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 DataFileTypeEnum")); } return enumValue; } /** * Get a GUI string representation of the enumerated type. * @param enumValue * Enumerated value. * @return * String representing enumerated value. */ AString DataFileTypeEnum::toGuiName(Enum enumValue) { if (initializedFlag == false) initialize(); const DataFileTypeEnum* 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. */ DataFileTypeEnum::Enum DataFileTypeEnum::fromGuiName(const AString& guiName, bool* isValidOut) { if (initializedFlag == false) initialize(); bool validFlag = false; Enum enumValue = UNKNOWN; 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; } } if (isValidOut != 0) { *isValidOut = validFlag; } else if (validFlag == false) { CaretAssertMessage(0, AString("guiName \"" + guiName + "\" failed to match enumerated value for type DataFileTypeEnum")); } return enumValue; } /** * Get a Overlay Type Name representation of the enumerated type. * @param enumValue * Enumerated value. * @return * String representing enumerated value. */ AString DataFileTypeEnum::toOverlayTypeName(Enum enumValue) { if (initializedFlag == false) initialize(); const DataFileTypeEnum* enumInstance = findData(enumValue); return enumInstance->overlayTypeName; } /** * Get an enumerated value corresponding to its overlay type name. * @param s * Overlay 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. */ DataFileTypeEnum::Enum DataFileTypeEnum::fromOverlayTypeName(const AString& overlayTypeName, bool* isValidOut) { if (initializedFlag == false) initialize(); bool validFlag = false; Enum enumValue = UNKNOWN; for (std::vector::iterator iter = enumData.begin(); iter != enumData.end(); iter++) { const DataFileTypeEnum& d = *iter; if (d.overlayTypeName == overlayTypeName) { enumValue = d.enumValue; validFlag = true; break; } } if (isValidOut != 0) { *isValidOut = validFlag; } else if (validFlag == false) { CaretAssertMessage(0, AString("guiName \"" + overlayTypeName + "\" failed to match enumerated value for type DataFileTypeEnum")); } return enumValue; } /** * Get the integer code for a data type. * * @return * Integer code for data type. */ int32_t DataFileTypeEnum::toIntegerCode(Enum enumValue) { if (initializedFlag == false) initialize(); const DataFileTypeEnum* enumInstance = findData(enumValue); return enumInstance->integerCode; } /** * Get an enumerated value corresponding to its QFileDialog filter name. * @param qFileDialogNameFilter * 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. */ DataFileTypeEnum::Enum DataFileTypeEnum::fromQFileDialogFilter(const AString& qFileDialogNameFilter, bool* isValidOut) { if (initializedFlag == false) initialize(); bool validFlag = false; Enum enumValue = UNKNOWN; for (std::vector::iterator iter = enumData.begin(); iter != enumData.end(); iter++) { const DataFileTypeEnum& d = *iter; if (d.qFileDialogNameFilter == qFileDialogNameFilter) { enumValue = d.enumValue; validFlag = true; break; } } if (isValidOut != 0) { *isValidOut = validFlag; } else if (validFlag == false) { CaretAssertMessage(0, AString("qFileDialogNameFilter \"" + qFileDialogNameFilter + " \"failed to match enumerated value for type DataFileTypeEnum")); } return enumValue; } /** * Get the file filter text for use in a QFileDialog. * * @param enumValue * Enumerated type for file filter. * @return * Text containing file filter. */ AString DataFileTypeEnum::toQFileDialogFilter(const Enum enumValue) { if (initializedFlag == false) initialize(); const DataFileTypeEnum* enumInstance = findData(enumValue); return enumInstance->qFileDialogNameFilter; } /** * Is the file type used with a one structure (node based file, surface, label, etc.)? * @param enumValue * Enumerated type for file filter. * @return true if used with one structure, else false. */ bool DataFileTypeEnum::isFileUsedWithOneStructure(const Enum enumValue) { if (initializedFlag == false) initialize(); const DataFileTypeEnum* enumInstance = findData(enumValue); return enumInstance->oneStructureFlag; } /** * @return All valid file extensions for the given enum value. * @param enumValue * Enumerated type for file extensions. */ std::vector DataFileTypeEnum::getAllFileExtensions(const Enum enumValue) { if (initializedFlag == false) initialize(); const DataFileTypeEnum* enumInstance = findData(enumValue); return enumInstance->fileExtensions; } /** * @return All valid file extensions for all file types except UNKNOWN * and CONNECTIVITY_DENSE_DYNAMIC */ std::vector DataFileTypeEnum::getFilesExtensionsForEveryFile() { std::vector allExtensions; for (std::vector::iterator enumIter = enumData.begin(); enumIter != enumData.end(); enumIter++) { if (enumIter->enumValue == DataFileTypeEnum::CONNECTIVITY_DENSE_DYNAMIC) { /* nothing */ } else if (enumIter->enumValue == DataFileTypeEnum::UNKNOWN) { /* nothing */ } else { allExtensions.insert(allExtensions.end(), enumIter->fileExtensions.begin(), enumIter->fileExtensions.end()); } } return allExtensions; } /** * If the given filename does not contain a file extension that is valid * for the given data file type, add the first valid file extension from * the given data file type. * * @param filename * Name of file that may not have the correct file extension. * @param enumValue * The data file type. * @return * Input file name to which a file extension may have been added. */ AString DataFileTypeEnum::addFileExtensionIfMissing(const AString& filenameIn, const Enum enumValue) { AString filename = filenameIn; if (initializedFlag == false) initialize(); const DataFileTypeEnum* enumInstance = findData(enumValue); /* * See if filename ends with any of the available extensions for the * given data file type. */ for (std::vector::const_iterator iter = enumInstance->fileExtensions.begin(); iter != enumInstance->fileExtensions.end(); iter++) { const AString ext = ("." + *iter); if (filename.endsWith(ext)) { return filename; } } /* * Add default extension. */ const AString defaultExtension = DataFileTypeEnum::toFileExtension(enumValue); filename += ("." + defaultExtension); return filename; } /** * Get the primary file extension for the file type. * @param enumValue * The data file type. * @return * Extension for file type. */ AString DataFileTypeEnum::toFileExtension(const Enum enumValue) { if (initializedFlag == false) initialize(); const DataFileTypeEnum* enumInstance = findData(enumValue); AString ext = "file"; if (enumInstance->fileExtensions.empty() == false) { ext = enumInstance->fileExtensions[0]; } return ext; } /*** * Does the filename have a valid extension for the given file type? * * @param filename * Name of file that may not have the correct file extension. * @param enumValue * The data file type. * @return * True if the filename has a valid extension, else false. */ bool DataFileTypeEnum::isValidFileExtension(const AString& filename, const Enum enumValue) { if (initializedFlag == false) initialize(); const DataFileTypeEnum* enumInstance = findData(enumValue); /* * See if filename ends with any of the available extensions for the * given data file type. */ for (std::vector::const_iterator iter = enumInstance->fileExtensions.begin(); iter != enumInstance->fileExtensions.end(); iter++) { const AString ext = ("." + *iter); if (filename.endsWith(ext)) { return true; } } return false; } /** * For the filename, match its extension to a DataFileType enumerated type. * @param filename * Name of file. * @param isValidOut * If not NULL, it is set indicating that a * enum value exists for the input name. * @return * Enumerated value. */ DataFileTypeEnum::Enum DataFileTypeEnum::fromFileExtension(const AString& filename, bool* isValidOut) { if (initializedFlag == false) initialize(); bool validFlag = false; Enum enumValue = UNKNOWN; for (std::vector::iterator iter = enumData.begin(); iter != enumData.end(); iter++) { const DataFileTypeEnum& d = *iter; const std::vector extensions = iter->fileExtensions; for (std::vector::const_iterator extIter = extensions.begin(); extIter != extensions.end(); extIter++) { /* * Need to add "." to avoid ambiguous matching ("dconn.nii, pdconn.nii) */ const AString extensionWithDot = ("." + *extIter); if (filename.endsWith(extensionWithDot)) { enumValue = d.enumValue; validFlag = true; break; } } if (validFlag) { break; } } if (isValidOut != 0) { *isValidOut = validFlag; } else if (validFlag == false) { CaretAssertMessage(0, AString("filename \"" + filename + " \"has no matching extensions in DataFileTypeEnum")); } return enumValue; } /** * 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. */ DataFileTypeEnum::Enum DataFileTypeEnum::fromIntegerCode(const int32_t integerCode, bool* isValidOut) { if (initializedFlag == false) initialize(); bool validFlag = false; Enum enumValue = UNKNOWN; for (std::vector::iterator iter = enumData.begin(); iter != enumData.end(); iter++) { const DataFileTypeEnum& 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 DataFileTypeEnum")); } 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. * @param options * Bitwise mask for options */ void DataFileTypeEnum::getAllEnums(std::vector& allEnums, const uint32_t options) { if (initializedFlag == false) initialize(); allEnums.clear(); const bool includeDenseDynamicFlag = (options & OPTIONS_INCLUDE_CONNECTIVITY_DENSE_DYNAMIC); const bool includeUnknownFlag = (options & OPTIONS_INCLUDE_UNKNOWN); for (std::vector::iterator iter = enumData.begin(); iter != enumData.end(); iter++) { if (iter->enumValue == CONNECTIVITY_DENSE_DYNAMIC) { if ( ! includeDenseDynamicFlag) { continue; } } if (iter->enumValue == UNKNOWN) { if ( ! includeUnknownFlag) { continue; } } allEnums.push_back(iter->enumValue); } } /** * Is the enumerated type a connectivity enumerated type? * @param enumValue * The enumerated type value. * @return * true if so, else false. */ bool DataFileTypeEnum::isConnectivityDataType(const Enum enumValue) { const AString name = DataFileTypeEnum::toName(enumValue); if (name.startsWith("CONNECTIVITY")) { return true; } return false; } /** * Get all connectivity enumerated type values. * @param connectivityEnumsOut * Will be loaded with all connectivity enumerated types. */ void DataFileTypeEnum::getAllConnectivityEnums(std::vector& connectivityEnumsOut) { if (initializedFlag == false) initialize(); connectivityEnumsOut.clear(); for (std::vector::iterator iter = enumData.begin(); iter != enumData.end(); iter++) { if (DataFileTypeEnum::isConnectivityDataType(iter->enumValue)) { connectivityEnumsOut.push_back(iter->enumValue); } } } connectome-workbench-1.2.3+git41-gc4c6c90/src/Common/DataFileTypeEnum.h000066400000000000000000000151551300200146000253140ustar00rootroot00000000000000#ifndef __DATA_FILE_TYPE_ENUM__H_ #define __DATA_FILE_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 DataFileTypeEnum { public: /** * Enumerated values. */ enum Enum { /** Annotation */ ANNOTATION, /** Border */ BORDER, /** Connectivity - Dense */ CONNECTIVITY_DENSE, /** Connectivity - Dense Dynamic (correlate from time-series)*/ CONNECTIVITY_DENSE_DYNAMIC, /** Connectivity - Dense Label */ CONNECTIVITY_DENSE_LABEL, /** Connectivity - Dense Parcel */ CONNECTIVITY_DENSE_PARCEL, /** Connectivity - Dense Scalar */ CONNECTIVITY_DENSE_SCALAR, /** Connectivity - Dense Time Series */ CONNECTIVITY_DENSE_TIME_SERIES, /** Connectivity - Fiber Orientations TEMPORARY */ CONNECTIVITY_FIBER_ORIENTATIONS_TEMPORARY, /** Connectivity - Fiber Trajectory TEMPORARY */ CONNECTIVITY_FIBER_TRAJECTORY_TEMPORARY, /** Connectivity - Parcel */ CONNECTIVITY_PARCEL, /** Connectivity - Parcel Dense*/ CONNECTIVITY_PARCEL_DENSE, /** Connectivity - Parcel Label*/ CONNECTIVITY_PARCEL_LABEL, /** Connectivity - Parcel Scalar */ CONNECTIVITY_PARCEL_SCALAR, /** Connectivity - Parcel Series */ CONNECTIVITY_PARCEL_SERIES, /** Connectivity - Scalar Data Series */ CONNECTIVITY_SCALAR_DATA_SERIES, /** Foci */ FOCI, /** Image */ IMAGE, /** Labels */ LABEL, /** Metric */ METRIC, /** Palette */ PALETTE, /** RGBA */ RGBA, /** Scene */ SCENE, /** Specification */ SPECIFICATION, /** Surface */ SURFACE, /** Unknown */ UNKNOWN, /** Volume */ VOLUME }; /** * Options for getting all enumerated values. * Bitwise 'OR' for multiple options */ enum Options { /** No options */ OPTIONS_NONE = 0, /** Include the dense dynamic data file type */ OPTIONS_INCLUDE_CONNECTIVITY_DENSE_DYNAMIC = 1, /** Include the unknown data file type */ OPTIONS_INCLUDE_UNKNOWN = 2 }; ~DataFileTypeEnum(); 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 AString toOverlayTypeName(Enum enumValue); static Enum fromOverlayTypeName(const AString& overlayTypeName, bool* isValidOut); static int32_t toIntegerCode(Enum enumValue); static Enum fromIntegerCode(const int32_t integerCode, bool* isValidOut); static void getAllEnums(std::vector& allEnums, const uint32_t options); static Enum fromQFileDialogFilter(const AString& qFileDialogNameFilter, bool* isValidOut); static AString toQFileDialogFilter(const Enum enumValue); static Enum fromFileExtension(const AString& filename, bool* isValidOut); static AString toFileExtension(const Enum enumValue); static bool isValidFileExtension(const AString& filename, const Enum enumValue); static std::vector getAllFileExtensions(const Enum enumValue); static std::vector getFilesExtensionsForEveryFile(); static bool isFileUsedWithOneStructure(const Enum enumValue); static bool isConnectivityDataType(const Enum enumValue); static void getAllConnectivityEnums(std::vector& connectivityEnumsOut); static AString addFileExtensionIfMissing(const AString& filename, const Enum enumValue); private: DataFileTypeEnum(const Enum enumValue, const AString& name, const AString& guiName, const AString& overlayTypeName, const bool fileIsUsedWithOneStructure, const AString& fileExtensionOne, const AString& fileExtensionTwo = "", const AString& fileExtensionThree = "", const AString& fileExtensionFour = ""); static const DataFileTypeEnum* 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; /** Automatically generates the integer code */ static int32_t integerCodeGenerator; /** 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; /** Name for use in overlay selection */ AString overlayTypeName; /** Extension(s) for the file */ std::vector fileExtensions; /** Name filter for use in a QFileDialog */ AString qFileDialogNameFilter; /** Is file for use with one structure */ bool oneStructureFlag; }; #ifdef __DATA_FILE_TYPE_ENUM_DECLARE__ std::vector DataFileTypeEnum::enumData; bool DataFileTypeEnum::initializedFlag = false; int32_t DataFileTypeEnum::integerCodeGenerator = 0; #endif // __DATA_FILE_TYPE_ENUM_DECLARE__ } // namespace #endif //__DATA_FILE_TYPE_ENUM__H_ connectome-workbench-1.2.3+git41-gc4c6c90/src/Common/DescriptiveStatistics.cxx000066400000000000000000000430571300200146000270650ustar00rootroot00000000000000 /*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 __DESCRIPTIVE_STATISTICS_DECLARE__ #include "DescriptiveStatistics.h" #undef __DESCRIPTIVE_STATISTICS_DECLARE__ #include #include #include #include "CaretAssert.h" #include "MathFunctions.h" using namespace caret; using namespace std; /** * \class caret::DescriptiveStatistics * \brief Contains descriptive statics for some group of data. * * Provides descriptive statistics for a group of data * that includes mininmum and maximum values, percentiles, * a histogram, and statistical measurements. */ /** * Constructor that allows setting the number of elements in the histogram. * * @param histogramNumberOfElements * Number of elemnents for the histogram. Must be positive!! * @param percentileDivisions * Number of percentiles. */ DescriptiveStatistics::DescriptiveStatistics(const int64_t histogramNumberOfElements, const int64_t percentileDivisions) : CaretObject() { m_histogramNumberOfElements = histogramNumberOfElements; CaretAssert(m_histogramNumberOfElements > 2); m_percentileDivisions = percentileDivisions; CaretAssert(m_percentileDivisions > 2); m_lastInputNumberOfValues = -1; m_histogram = new int64_t[m_histogramNumberOfElements]; m_positivePercentiles = new float[m_percentileDivisions]; m_negativePercentiles = new float[m_percentileDivisions]; this->invalidateData(); } /** * Destructor. */ DescriptiveStatistics::~DescriptiveStatistics() { if (m_histogram != NULL) { delete[] m_histogram; } if (m_positivePercentiles != NULL) { delete[] m_positivePercentiles; } if (m_negativePercentiles != NULL) { delete[] m_negativePercentiles; } } /** * Invalidate data so that next call to update() * recreates the statistics. */ void DescriptiveStatistics::invalidateData() { m_lastInputNumberOfValues = -1; m_validCount = 0; m_infCount = 0; m_negInfCount = 0; m_nanCount = 0; m_minimumValue = 0.0; m_maximumValue = 0.0; m_containsNegativeValues = false; m_containsPositiveValues = false; m_mean = 0.0; m_median = 0.0; m_standardDeviationPopulation = 0.0; m_standardDeviationSample = 0.0; } /** * Update the statistics with the given data but * limit the range of values to the given * minimum and maximum values. * * @param values * Values for which statistics are calculated. * @param numberOfValues * Number of elements in values array. * @param mostPositiveValueInclusive * Values more positive than this value are excluded. * @param leastPositiveValueInclusive * Values less positive than this value are excluded. * @param leastNegativeValueInclusive * Values less negative than this value are excluded. * @param mostNegativeValueInclusive * Values more negative than this value are excluded. * @param includeZeroValues * If true zero values (very near zero) are included. */ void DescriptiveStatistics::update(const std::vector& values, const float mostPositiveValueInclusive, const float leastPositiveValueInclusive, const float leastNegativeValueInclusive, const float mostNegativeValueInclusive, const bool includeZeroValues) { const float* valuesArray = (values.empty() ? NULL : &values[0]); const int64_t numberOfValues = (values.empty() ? 0 : values.size()); this->update(valuesArray, numberOfValues, mostPositiveValueInclusive, leastPositiveValueInclusive, leastNegativeValueInclusive, mostNegativeValueInclusive, includeZeroValues); } /** * Update the statistics with the given data but * limit the range of values to the given * minimum and maximum values. * * @param values * Values for which statistics are calculated. * @param numberOfValues * Number of elements in values array. * @param mostPositiveValueInclusive * Values more positive than this value are excluded. * @param leastPositiveValueInclusive * Values less positive than this value are excluded. * @param leastNegativeValueInclusive * Values less negative than this value are excluded. * @param mostNegativeValueInclusive * Values more negative than this value are excluded. * @param includeZeroValues * If true zero values (very near zero) are included. */ void DescriptiveStatistics::update(const float* valuesIn, const int64_t numberOfValuesIn, const float mostPositiveValueInclusive, const float leastPositiveValueInclusive, const float leastNegativeValueInclusive, const float mostNegativeValueInclusive, const bool includeZeroValues) { bool needUpdate = false; if (m_lastInputNumberOfValues <= 0) { needUpdate = true; } else { if ((numberOfValuesIn != m_lastInputNumberOfValues) || (mostPositiveValueInclusive != m_lastInputMostPositiveValueInclusive) || (leastPositiveValueInclusive != m_lastInputLeastPositiveValueInclusive) || (leastNegativeValueInclusive != m_lastInputLeastNegativeValueInclusive) || (mostNegativeValueInclusive != m_lastInputMostNegativeValueInclusive) || (includeZeroValues != m_lastInputIncludeZeroValues)) { needUpdate = true; } } if (needUpdate == false) { return; } this->invalidateData(); m_lastInputNumberOfValues = numberOfValuesIn; m_lastInputMostPositiveValueInclusive = mostPositiveValueInclusive; m_lastInputLeastPositiveValueInclusive = leastPositiveValueInclusive; m_lastInputLeastNegativeValueInclusive = leastNegativeValueInclusive; m_lastInputMostNegativeValueInclusive = mostNegativeValueInclusive; m_lastInputIncludeZeroValues = includeZeroValues; std::vector valuesVector; valuesVector.reserve(numberOfValuesIn); for (int64_t i = 0; i < numberOfValuesIn; i++) { bool useIt = false; const float v = valuesIn[i]; if (v >= leastPositiveValueInclusive) { if (v <= mostPositiveValueInclusive) { useIt = true; } } else if (v <= leastNegativeValueInclusive) { if (v >= mostNegativeValueInclusive) { useIt = true; } } if (useIt) { if (includeZeroValues == false) { if (MathFunctions::isZero(v)) { useIt = false; } } if (useIt) { valuesVector.push_back(v); } } } const float* values = (valuesVector.empty() ? NULL : &valuesVector[0]); const int64_t numberOfValues = static_cast(valuesVector.size()); std::fill(m_histogram, m_histogram + m_histogramNumberOfElements, 0.0); std::fill(m_positivePercentiles, m_positivePercentiles + m_percentileDivisions, 0.0); std::fill(m_negativePercentiles, m_negativePercentiles + m_percentileDivisions, 0.0); if (numberOfValues <= 0) { return; } if (numberOfValues == 1) { const float v = values[0]; m_mean = v; m_median = v; m_histogram[m_histogramNumberOfElements / 2] = v; fill(m_positivePercentiles, m_positivePercentiles + m_percentileDivisions, v); fill(m_negativePercentiles, m_negativePercentiles + m_percentileDivisions, v); m_minimumValue = v; m_maximumValue = v; return; } /* * Copy and sort the input data. */ float* sortedValues = new float[numberOfValues]; for (int64_t i = 0; i < numberOfValues; ++i) {//remove and count non-numerical values if (values[i] != values[i]) { ++m_nanCount; continue; } if (values[i] < -1.0f && (values[i] * 2.0f == values[i])) { ++m_negInfCount; continue; } if (values[i] > 1.0f && (values[i] * 2.0f == values[i])) { ++m_infCount; continue; } sortedValues[m_validCount] = values[i]; ++m_validCount; } sort(sortedValues, sortedValues + m_validCount); /* * Minimum and maximum values */ this->m_minimumValue = sortedValues[0]; this->m_maximumValue = sortedValues[numberOfValues - 1]; /* * Find most/least negative/positive indices in sorted data. */ int64_t mostNegativeIndex = -1; int64_t leastNegativeIndex = -1; int64_t leastPositiveIndex = -1; int64_t mostPositiveIndex = -1; if (sortedValues[0] < 0.0f) { mostNegativeIndex = 0; } if (sortedValues[0] > 0.0f) { leastPositiveIndex = 0; } if (sortedValues[m_validCount - 1] > 0.0f) { mostPositiveIndex = m_validCount - 1; } if (sortedValues[m_validCount - 1] < 0.0f) { leastNegativeIndex = m_validCount - 1; } if (leastNegativeIndex == -1 && leastPositiveIndex == -1) {//need to find where the zeros start and end int64_t start = -1, end = m_validCount, guess, nextEnd = m_validCount; while (end - start > 1) {//bisection search for last negative guess = (start + end) / 2; CaretAssertArrayIndex(sortedValues, m_validCount, guess); if (sortedValues[guess] < 0.0f) { start = guess; } else { end = guess; if (sortedValues[guess] > 0.0f) { nextEnd = guess;//save some time on the next search } } } leastNegativeIndex = start; end = nextEnd;//don't reinitialize start, it is just before the first nonnegative already while (end - start > 1) {//bisection search for first positive guess = (start + end) / 2; CaretAssertArrayIndex(sortedValues, m_validCount, guess); if (sortedValues[guess] > 0.0f) { end = guess; } else { start = guess; } } leastPositiveIndex = end; } /* * Determine negative percentiles * Note: that index 0 is least negative, last index is most negative */ const int64_t numNegativeValues = leastNegativeIndex - mostNegativeIndex + 1; if (mostNegativeIndex != -1) { m_containsNegativeValues = true; m_negativePercentiles[0] = sortedValues[leastNegativeIndex]; for (int64_t i = 1; i < m_percentileDivisions - 1; i++) { int64_t indx = leastNegativeIndex - (int64_t)(((double)i * (numNegativeValues - 1)) / m_percentileDivisions + 0.5); CaretAssertArrayIndex(sortedValues, m_validCount, indx); if (indx < 0) indx = 0; if (indx >= m_validCount) indx = m_validCount - 1; m_negativePercentiles[i] = sortedValues[indx]; } m_negativePercentiles[m_percentileDivisions - 1] = sortedValues[mostNegativeIndex]; } /* * Determine positive percentiles */ const int64_t numPositiveValues = mostPositiveIndex - leastPositiveIndex + 1; if (mostPositiveIndex != -1) { this->m_containsPositiveValues = true; m_positivePercentiles[0] = sortedValues[leastPositiveIndex]; for (int64_t i = 1; i < m_percentileDivisions - 1; i++) { int64_t indx = (int64_t)(((double)i * (numPositiveValues - 1)) / m_percentileDivisions + 0.5) + leastPositiveIndex; CaretAssertArrayIndex(sortedValues, m_validCount, indx); if (indx < 0) indx = 0; if (indx >= m_validCount) indx = m_validCount - 1; m_positivePercentiles[i] = sortedValues[indx]; } m_positivePercentiles[m_percentileDivisions - 1] = sortedValues[mostPositiveIndex]; } /* * Prepare for histogram of all data */ const float minValue = sortedValues[0]; const float maxValue = sortedValues[m_validCount - 1]; const float bucketSize = (maxValue - minValue) / m_histogramNumberOfElements; /* * Prepare for statistics */ double sum = 0.0; double sumSQ = 0.0; /* * Create histogram and statistics. */ for (int64_t i = 0; i < m_validCount; i++) { const float v = sortedValues[i]; int64_t indx = (v - minValue) / bucketSize; if (indx >= m_histogramNumberOfElements) indx = m_histogramNumberOfElements - 1;//NEVER trust floats to not have rounding errors when nonzero if (indx < 0) indx = 0;//probably not needed, involves subtracting equals CaretAssertArrayIndex(m_histogram, m_histogramNumberOfElements, indx); m_histogram[indx]++; sum += v; const float v2 = v * v; sumSQ += v2; } /* * Compute statistics of all. * Pop Variance = (sum(x^2) - [(sum(x))^2] / N) / N */ m_mean = sum / m_validCount; m_median = sortedValues[m_validCount / 2]; const double numerator = (sumSQ - ((sum*sum) / m_validCount)); m_standardDeviationPopulation = -1.0; m_standardDeviationSample = -1.0; if (m_validCount > 0) { m_standardDeviationPopulation = sqrt(numerator / m_validCount); if (m_validCount > 1) { m_standardDeviationSample = sqrt(numerator / (m_validCount - 1)); } } delete[] sortedValues; } /** * Update the statistics with the given data. * @param values * Values for which statistics are calculated. * @param numberOfValues * Number of elements in values array. */ void DescriptiveStatistics::update(const float* values, const int64_t numberOfValues) { this->update(values, numberOfValues, std::numeric_limits::max(), 0.0, 0.0, -std::numeric_limits::max(), true); } /** * Update the statistics with the given data. * @param values * Vector of values for which statistics are calculated. */ void DescriptiveStatistics::update(const std::vector& values) { this->update(values, std::numeric_limits::max(), 0.0, 0.0, -std::numeric_limits::max(), true); } /** * Get the value that is greater than 'percent' of positive values. * * @param percent * The percent which ranges inclusively from 0 to 100. * @return * Value that is greater than 'percent' of the positive values. */ float DescriptiveStatistics::getPositivePercentile(const float percent) const { const float myIndex = (percent / 100 * (m_percentileDivisions - 1));//noninteger index to interpolate at int64_t lowIndex = (int64_t)floor(myIndex); int64_t highIndex = (int64_t)ceil(myIndex); if (highIndex <= 0) return m_positivePercentiles[0]; if (lowIndex >= m_percentileDivisions - 1) return m_positivePercentiles[m_percentileDivisions - 1]; if (lowIndex == highIndex) return m_positivePercentiles[lowIndex]; float lowWeight = highIndex - myIndex; float highWeight = myIndex - lowIndex; return (lowWeight * m_positivePercentiles[lowIndex] + highWeight * m_positivePercentiles[highIndex]) / (lowWeight + highWeight); } /** * Get the value that is more negative than 'percent' of negative values. * * @param percent * The percent which ranges inclusively from 0 to 100. * @return * Value that is more negative than 'percent' of the negative values. */ float DescriptiveStatistics::getNegativePercentile(const float percent) const { const float myIndex = (percent / 100 * (m_percentileDivisions - 1));//noninteger index to interpolate at int64_t lowIndex = (int64_t)floor(myIndex); int64_t highIndex = (int64_t)ceil(myIndex); if (highIndex <= 0) return m_negativePercentiles[0]; if (lowIndex >= m_percentileDivisions - 1) return m_negativePercentiles[m_percentileDivisions - 1]; if (lowIndex == highIndex) return m_negativePercentiles[lowIndex]; float lowWeight = highIndex - myIndex; float highWeight = myIndex - lowIndex; return (lowWeight * m_negativePercentiles[lowIndex] + highWeight * m_negativePercentiles[highIndex]) / (lowWeight + highWeight); } /** * Get a description of this object's content. * @return String describing this object's content. */ AString DescriptiveStatistics::toString() const { return "DescriptiveStatistics"; } connectome-workbench-1.2.3+git41-gc4c6c90/src/Common/DescriptiveStatistics.h000066400000000000000000000175731300200146000265160ustar00rootroot00000000000000#ifndef __DESCRIPTIVE_STATISTICS__H_ #define __DESCRIPTIVE_STATISTICS__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 "CaretObject.h" namespace caret { class DescriptiveStatistics : public CaretObject { public: DescriptiveStatistics(const int64_t histogramNumberOfElements = 100, const int64_t percentileDivisions = 1001); virtual ~DescriptiveStatistics(); void update(const float* values, const int64_t numberOfValues); void update(const float* values, const int64_t numberOfValues, const float mostPositiveValueInclusive, const float leastPositiveValueInclusive, const float leastNegativeValueInclusive, const float mostNegativeValueInclusive, const bool includeZeroValues); void update(const std::vector& values); void update(const std::vector& values, const float mostPositiveValueInclusive, const float leastPositiveValueInclusive, const float leastNegativeValueInclusive, const float mostNegativeValueInclusive, const bool includeZeroValues); float getPositivePercentile(const float percent) const; float getNegativePercentile(const float percent) const; void invalidateData(); /** * @return Does the data contains positive values. */ bool hasPositiveValues() const { return this->m_containsPositiveValues; } /** * @return Does the data contains negative values. */ bool hasNegativeValues() const { return this->m_containsNegativeValues; } /** * @return The number of elements in the histogram. */ int64_t getHistogramNumberOfElements() const { return this->m_histogramNumberOfElements; } /** * @return Get the histogram for all values. The number of elements * is HISTOGRAM_NUMBER_OF_ELEMENTS. */ const int64_t* getHistogram() const { return this->m_histogram; } /** * @return The most positive value. */ float getMostPositiveValue() const { return this->m_positivePercentiles[m_percentileDivisions - 1]; } /** * @return The least positive value. */ float getLeastPositiveValue() const { return this->m_positivePercentiles[0]; } /** * @return The most negative value. */ float getMostNegativeValue() const { return this->m_negativePercentiles[m_percentileDivisions - 1]; } /** * @return The least negative value. */ float getLeastNegativeValue() const { return this->m_negativePercentiles[0]; } /** * @return The minimum value. */ float getMinimumValue() const { return this->m_minimumValue; } /** * @return The maximum value. */ float getMaximumValue() const { return this->m_maximumValue; } /** * @return The mean (average) value. */ float getMean() const { return this->m_mean; } /** * @return The median value. */ float getMedian() const { return this->m_median; } /** * @return The population standard deviation (divide by N). */ float getPopulationStandardDeviation() const { return this->m_standardDeviationPopulation; } /** * @return The sample standard deviation (divide by N - 1). */ float getStandardDeviationSample() const { return this->m_standardDeviationSample; } private: DescriptiveStatistics(const DescriptiveStatistics&); DescriptiveStatistics& operator=(const DescriptiveStatistics&); public: virtual AString toString() const; private: /** * Contains the histogram which provides the * distribution of the data. */ int64_t* m_histogram; /** * Contains the number of elements in the histograms. */ int64_t m_histogramNumberOfElements; ///contains number of divisions in the percentiles int64_t m_percentileDivisions; /** * Contains the negative percentiles. * * Index 0 contains the least negative value less than zero. * * Index 'X' contains the value that is less than * 'X' percent of values. * * The last index contains the most negative value. */ float* m_negativePercentiles; /** Indicates that negative data is present. */ bool m_containsNegativeValues; /** * Contains the positive percentiles. * * Index 0 contains the least positive value greater than zero. * * Index 'X' contains the value that is greater than * 'X' percent of values. * * The last index contains the greatest positive value. */ float* m_positivePercentiles; /** Indicates that positive data is present. */ bool m_containsPositiveValues; /** minimum value regardless of sign */ float m_minimumValue; /** maximum value regardless of sign */ float m_maximumValue; /** The mean (average) value. */ float m_mean; /** The population standard deviation of all values (divide by N). */ float m_standardDeviationPopulation; /** The sample standard deviation of all values (divide by N - 1). */ float m_standardDeviationSample; /** The median (middle) value. */ float m_median; ///counts of each class of number int64_t m_validCount, m_infCount, m_negInfCount, m_nanCount; /// last input value for number of (prevents unnecessary updates) int64_t m_lastInputNumberOfValues; /// last input value for most positive (prevents unnecessary updates) float m_lastInputMostPositiveValueInclusive; /// last input value for least positive (prevents unnecessary updates) float m_lastInputLeastPositiveValueInclusive; /// last input value for least negative (prevents unnecessary updates) float m_lastInputLeastNegativeValueInclusive; /// last input value for most negative (prevents unnecessary updates) float m_lastInputMostNegativeValueInclusive; /// last input value for include zeros (prevents unnecessary updates) bool m_lastInputIncludeZeroValues; }; #ifdef __DESCRIPTIVE_STATISTICS_DECLARE__ // #endif // __DESCRIPTIVE_STATISTICS_DECLARE__ } // namespace #endif //__DESCRIPTIVE_STATISTICS__H_ connectome-workbench-1.2.3+git41-gc4c6c90/src/Common/DeveloperFlagsEnum.cxx000066400000000000000000000261171300200146000262560ustar00rootroot00000000000000 /*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 __DEVELOPER_FLAGS_ENUM_DECLARE__ #include "DeveloperFlagsEnum.h" #undef __DEVELOPER_FLAGS_ENUM_DECLARE__ #include "CaretAssert.h" using namespace caret; /** * \class caret::DeveloperFlagsEnum * \brief Flags used during development. * * 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_developerFlagsEnumComboBox; * * Declare a slot that is called when user changes selection * private slots: * void developerFlagsEnumComboBoxItemActivated(); * * Implementation File (.cxx) * Include the header files * #include "EnumComboBoxTemplate.h" * #include "DeveloperFlagsEnum.h" * * Instatiate: * m_developerFlagsEnumComboBox = new EnumComboBoxTemplate(this); * m_developerFlagsEnumComboBox->setup(); * * Get notified when the user changes the selection: * QObject::connect(m_developerFlagsEnumComboBox, SIGNAL(itemActivated()), * this, SLOT(developerFlagsEnumComboBoxItemActivated())); * * Update the selection: * m_developerFlagsEnumComboBox->setSelectedItem(NEW_VALUE); * * Read the selection: * const DeveloperFlagsEnum::Enum VARIABLE = m_developerFlagsEnumComboBox->getSelectedItem(); * */ /** * Constructor. * * @param enumValue * An enumerated value. * @param name * Name of enumerated value. * * @param guiName * User-friendly name for use in user-interface. */ DeveloperFlagsEnum::DeveloperFlagsEnum(const Enum enumValue, const AString& name, const AString& guiName) { this->enumValue = enumValue; this->integerCode = integerCodeCounter++; this->name = name; this->guiName = guiName; this->flagStatus = false; /* * Initialization (true/false) of enums as desired */ switch (this->enumValue) { case DEVELOPER_FLAG_UNUSED: this->flagStatus = false; break; } } /** * Destructor. */ DeveloperFlagsEnum::~DeveloperFlagsEnum() { } /** * Initialize the enumerated metadata. */ void DeveloperFlagsEnum::initialize() { if (initializedFlag) { return; } initializedFlag = true; enumData.push_back(DeveloperFlagsEnum(DEVELOPER_FLAG_UNUSED, "DEVELOPER_FLAG_UNUSED", "Developer flag unused")); } /** * 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. */ DeveloperFlagsEnum* DeveloperFlagsEnum::findData(const Enum enumValue) { if (initializedFlag == false) initialize(); size_t num = enumData.size(); for (size_t i = 0; i < num; i++) { DeveloperFlagsEnum* 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 DeveloperFlagsEnum::toName(Enum enumValue) { if (initializedFlag == false) initialize(); const DeveloperFlagsEnum* 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. */ DeveloperFlagsEnum::Enum DeveloperFlagsEnum::fromName(const AString& name, bool* isValidOut) { if (initializedFlag == false) initialize(); bool validFlag = false; Enum enumValue = DeveloperFlagsEnum::enumData[0].enumValue; for (std::vector::iterator iter = enumData.begin(); iter != enumData.end(); iter++) { const DeveloperFlagsEnum& 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 DeveloperFlagsEnum")); } return enumValue; } /** * Get a GUI string representation of the enumerated type. * @param enumValue * Enumerated value. * @return * String representing enumerated value. */ AString DeveloperFlagsEnum::toGuiName(Enum enumValue) { if (initializedFlag == false) initialize(); const DeveloperFlagsEnum* 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. */ DeveloperFlagsEnum::Enum DeveloperFlagsEnum::fromGuiName(const AString& guiName, bool* isValidOut) { if (initializedFlag == false) initialize(); bool validFlag = false; Enum enumValue = DeveloperFlagsEnum::enumData[0].enumValue; for (std::vector::iterator iter = enumData.begin(); iter != enumData.end(); iter++) { const DeveloperFlagsEnum& 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 DeveloperFlagsEnum")); } return enumValue; } /** * Get the integer code for a data type. * * @return * Integer code for data type. */ int32_t DeveloperFlagsEnum::toIntegerCode(Enum enumValue) { if (initializedFlag == false) initialize(); const DeveloperFlagsEnum* 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. */ DeveloperFlagsEnum::Enum DeveloperFlagsEnum::fromIntegerCode(const int32_t integerCode, bool* isValidOut) { if (initializedFlag == false) initialize(); bool validFlag = false; Enum enumValue = DeveloperFlagsEnum::enumData[0].enumValue; for (std::vector::iterator iter = enumData.begin(); iter != enumData.end(); iter++) { const DeveloperFlagsEnum& 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 DeveloperFlagsEnum")); } 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 DeveloperFlagsEnum::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 DeveloperFlagsEnum::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(DeveloperFlagsEnum::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 DeveloperFlagsEnum::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(DeveloperFlagsEnum::toGuiName(iter->enumValue)); } if (isSorted) { std::sort(allGuiNames.begin(), allGuiNames.end()); } } /** * Is the developer flag set? * * @param enumValue * Enum value for flag * @return * True/False status for flag. */ bool DeveloperFlagsEnum::isFlag(const Enum enumValue) { if (initializedFlag == false) initialize(); const DeveloperFlagsEnum* enumInstance = findData(enumValue); return enumInstance->flagStatus; } /** * Set the developer flag. * * @param enumValue * Enum value for flag * @param flagStatus * True/False status for flag. */ void DeveloperFlagsEnum::setFlag(const Enum enumValue, const bool flagStatus) { if (initializedFlag == false) initialize(); DeveloperFlagsEnum* enumInstance = findData(enumValue); enumInstance->flagStatus = flagStatus; } connectome-workbench-1.2.3+git41-gc4c6c90/src/Common/DeveloperFlagsEnum.h000066400000000000000000000062461300200146000257040ustar00rootroot00000000000000#ifndef __DEVELOPER_FLAGS_ENUM_H__ #define __DEVELOPER_FLAGS_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 DeveloperFlagsEnum { public: /** * Enumerated values. */ enum Enum { DEVELOPER_FLAG_UNUSED }; ~DeveloperFlagsEnum(); 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 bool isFlag(const Enum enumValue); static void setFlag(const Enum enumValue, const bool flagStatus); private: DeveloperFlagsEnum(const Enum enumValue, const AString& name, const AString& guiName); static DeveloperFlagsEnum* 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; /** Flag status */ bool flagStatus; }; #ifdef __DEVELOPER_FLAGS_ENUM_DECLARE__ std::vector DeveloperFlagsEnum::enumData; bool DeveloperFlagsEnum::initializedFlag = false; int32_t DeveloperFlagsEnum::integerCodeCounter = 0; #endif // __DEVELOPER_FLAGS_ENUM_DECLARE__ } // namespace #endif //__DEVELOPER_FLAGS_ENUM_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Common/DisplayGroupAndTabItemInterface.cxx000066400000000000000000000205321300200146000306560ustar00rootroot00000000000000 /*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 __DISPLAY_GROUP_AND_TAB_ITEM_INTERFACE_DECLARE__ #include "DisplayGroupAndTabItemInterface.h" #undef __DISPLAY_GROUP_AND_TAB_ITEM_INTERFACE_DECLARE__ #include "CaretAssert.h" #include "CaretLogger.h" using namespace caret; /** * \class caret::DisplayGroupAndTabItemInterface * \brief Interface for items in a display group tab selection hierarchy * \ingroup Common */ /** * Constructor. */ DisplayGroupAndTabItemInterface::DisplayGroupAndTabItemInterface() { } /** * Destructor. */ DisplayGroupAndTabItemInterface::~DisplayGroupAndTabItemInterface() { } ///** // * Copy constructor. // * @param obj // * Object that is copied. // */ //DisplayGroupAndTabItemInterface::DisplayGroupAndTabItemInterface(const DisplayGroupAndTabItemInterface& obj) //: CaretObject(obj) //{ // this->copyHelperDisplayGroupAndTabItemInterface(obj); //} // ///** // * Assignment operator. // * @param obj // * Data copied from obj to this. // * @return // * Reference to this object. // */ //DisplayGroupAndTabItemInterface& //DisplayGroupAndTabItemInterface::operator=(const DisplayGroupAndTabItemInterface& obj) //{ // if (this != &obj) { // CaretObject::operator=(obj); // this->copyHelperDisplayGroupAndTabItemInterface(obj); // } // return *this; //} // ///** // * Helps with copying an object of this type. // * @param obj // * Object that is copied. // */ //void //DisplayGroupAndTabItemInterface::copyHelperDisplayGroupAndTabItemInterface(const DisplayGroupAndTabItemInterface& obj) //{ // //} /** * Set the display status to SELECTED for the given tab and * UNSELECTED for all other tabs. * * @param tabIndex * Index of the tab. */ void DisplayGroupAndTabItemInterface::setItemDisplaySelectedInOneTab(const int32_t tabIndex) { for (int32_t i = 0; i < BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS; i++) { setItemDisplaySelected(DisplayGroupEnum::DISPLAY_GROUP_TAB, i, TriStateSelectionStatusEnum::UNSELECTED); } setItemDisplaySelected(DisplayGroupEnum::DISPLAY_GROUP_TAB, tabIndex, TriStateSelectionStatusEnum::SELECTED); } /** * Set the display status to SELECTED for all tabs. */ void DisplayGroupAndTabItemInterface::setItemDisplaySelectedInAllTabs() { for (int32_t i = 0; i < BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS; i++) { setItemDisplaySelected(DisplayGroupEnum::DISPLAY_GROUP_TAB, i, TriStateSelectionStatusEnum::SELECTED); } } /** * Set the display status to SELECTED for all groups (except TAB). */ void DisplayGroupAndTabItemInterface::setItemDisplaySelectedInAllGroups() { std::vector groupEnums; DisplayGroupEnum::getAllEnumsExceptTab(groupEnums); for (std::vector::iterator iter = groupEnums.begin(); iter != groupEnums.end(); iter++) { setItemDisplaySelected(*iter, 0, TriStateSelectionStatusEnum::SELECTED); } } /** * Set the display status to SELECTED for the given group (not TAB) and * UNSELECTED for all other groups (not TAB). * * @param tabIndex * Index of the tab. */ void DisplayGroupAndTabItemInterface::setItemDisplaySelectedInOneGroup(const DisplayGroupEnum::Enum displayGroup) { if (displayGroup == DisplayGroupEnum::DISPLAY_GROUP_TAB) { CaretLogWarning("DisplayGroupEnum::DISPLAY_GROUP_TAB not allowed."); return; } std::vector groupEnums; DisplayGroupEnum::getAllEnumsExceptTab(groupEnums); for (std::vector::iterator iter = groupEnums.begin(); iter != groupEnums.end(); iter++) { const DisplayGroupEnum::Enum enumValue = *iter; TriStateSelectionStatusEnum::Enum status = TriStateSelectionStatusEnum::UNSELECTED; if (displayGroup == enumValue) { status = TriStateSelectionStatusEnum::SELECTED; } setItemDisplaySelected(*iter, 0, status); } } /** * Helps with display selection status of all children. * * @param displayGroupTabInterface * Item that has all of its children selected for display. * @param displayGroup * The display group. * @param tabIndex * Index of the tab. * @return * Selection status for children. If there are no children, * 'UNSELECTED' is returned. */ TriStateSelectionStatusEnum::Enum DisplayGroupAndTabItemInterface::getChildrenDisplaySelectedHelper(DisplayGroupAndTabItemInterface* displayGroupTabInterface, const DisplayGroupEnum::Enum displayGroup, const int32_t tabIndex) { TriStateSelectionStatusEnum::Enum status = TriStateSelectionStatusEnum::UNSELECTED; std::vector children = displayGroupTabInterface->getItemChildren(); const int numChildren = static_cast(children.size()); if (numChildren > 0) { int32_t selectedCount = 0; int32_t partialSelectedCount = 0; for (int32_t i = 0; i < numChildren; i++) { CaretAssertVectorIndex(children, i); switch (children[i]->getItemDisplaySelected(displayGroup, tabIndex)) { case TriStateSelectionStatusEnum::PARTIALLY_SELECTED: partialSelectedCount++; break; case TriStateSelectionStatusEnum::SELECTED: selectedCount++; break; case TriStateSelectionStatusEnum::UNSELECTED: break; } } if (selectedCount == numChildren) { status = TriStateSelectionStatusEnum::SELECTED; } else if ((selectedCount > 0) || (partialSelectedCount > 0)) { status = TriStateSelectionStatusEnum::PARTIALLY_SELECTED; } } return status; } /** * Helps with display selection of all children. * * @param displayGroupTabInterface * Item that has all of its children selected for display. * @param displayGroup * The display group. * @param tabIndex * Index of the tab. * @param status * New selection status. * @return * True if there are children and their status was set, else false. */ bool DisplayGroupAndTabItemInterface::setChildrenDisplaySelectedHelper(DisplayGroupAndTabItemInterface* displayGroupTabInterface, const DisplayGroupEnum::Enum displayGroup, const int32_t tabIndex, const TriStateSelectionStatusEnum::Enum status) { CaretAssert(displayGroupTabInterface); std::vector children = displayGroupTabInterface->getItemChildren(); for (std::vector::iterator childIter = children.begin(); childIter != children.end(); childIter++) { DisplayGroupAndTabItemInterface* child = *childIter; child->setItemDisplaySelected(displayGroup, tabIndex, status); } return ( ! children.empty()); } connectome-workbench-1.2.3+git41-gc4c6c90/src/Common/DisplayGroupAndTabItemInterface.h000066400000000000000000000161461300200146000303110ustar00rootroot00000000000000#ifndef __DISPLAY_GROUP_AND_TAB_ITEM_INTERFACE_H__ #define __DISPLAY_GROUP_AND_TAB_ITEM_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 "BrainConstants.h" #include "DisplayGroupEnum.h" #include "TriStateSelectionStatusEnum.h" namespace caret { class DisplayGroupAndTabItemInterface { protected: /** * Constructor */ DisplayGroupAndTabItemInterface(); public: /** * Destructor */ virtual ~DisplayGroupAndTabItemInterface(); /** * @return Number of children. */ virtual int32_t getNumberOfItemChildren() const = 0; /** * Get child at the given index. * * @param index * Index of the child. * @return * Child at the given index. */ virtual DisplayGroupAndTabItemInterface* getItemChild(const int32_t index) const = 0; /** * @return Children of this item. */ virtual std::vector getItemChildren() const = 0; /** * @return Parent of this item. */ virtual DisplayGroupAndTabItemInterface* getItemParent() const = 0; /** * Set the parent of this item. * * @param itemParent * Parent of this item. */ virtual void setItemParent(DisplayGroupAndTabItemInterface* itemParent) = 0; /** * @return Name of this item. */ virtual AString getItemName() const = 0; /** * Get the icon color for this item. Icon is filled with background * color, outline color is drawn around edges, and text color is small * square in center. For any colors that do not apply, use an alpha * value (last element) of zero. * * @param backgroundRgbaOut * Red, green, blue, alpha components for background ranging [0, 1]. * @param outlineRgbaOut * Red, green, blue, alpha components for outline ranging [0, 1]. * @param textRgbaOut * Red, green, blue, alpha components for text ranging [0, 1]. */ virtual void getItemIconColorsRGBA(float backgroundRgbaOut[4], float outlineRgbaOut[4], float textRgbaOut[4]) const = 0; /** * @return This item can be expanded. */ virtual bool isItemExpandable() const = 0; /** * @return Is this item expanded in the given display group/tab? * * @param displayGroup * The display group. * @param tabIndex * Index of the tab. */ virtual bool isItemExpanded(const DisplayGroupEnum::Enum displayGroup, const int32_t tabIndex) const = 0; /** * Set this item's expanded status in the given display group/tab. * * @param displayGroup * The display group. * @param tabIndex * Index of the tab. * @param status * New expanded status. */ virtual void setItemExpanded(const DisplayGroupEnum::Enum displayGroup, const int32_t tabIndex, const bool status) = 0; /** * Get display selection status in the given display group/tab? * * @param displayGroup * The display group. * @param tabIndex * Index of the tab. */ virtual TriStateSelectionStatusEnum::Enum getItemDisplaySelected(const DisplayGroupEnum::Enum displayGroup, const int32_t tabIndex) const = 0; /** * Set display this item selected in the given display group/tab. * * @param displayGroup * The display group. * @param tabIndex * Index of the tab. * @param status * New selection status. */ virtual void setItemDisplaySelected(const DisplayGroupEnum::Enum displayGroup, const int32_t tabIndex, const TriStateSelectionStatusEnum::Enum status) = 0; virtual void setItemDisplaySelectedInOneTab(const int32_t tabIndex); virtual void setItemDisplaySelectedInAllTabs(); virtual void setItemDisplaySelectedInAllGroups(); virtual void setItemDisplaySelectedInOneGroup(const DisplayGroupEnum::Enum displayGroup); /** * Is this item selected for editing in the given window? * * @param windowIndex * Index of the window. * @return * Selection status. */ virtual bool isItemSelectedForEditingInWindow(const int32_t windowIndex) = 0; static TriStateSelectionStatusEnum::Enum getChildrenDisplaySelectedHelper(DisplayGroupAndTabItemInterface* displayGroupTabInterface, const DisplayGroupEnum::Enum displayGroup, const int32_t tabIndex); static bool setChildrenDisplaySelectedHelper(DisplayGroupAndTabItemInterface* displayGroupTabInterface, const DisplayGroupEnum::Enum displayGroup, const int32_t tabIndex, const TriStateSelectionStatusEnum::Enum status); // ADD_NEW_METHODS_HERE private: //DisplayGroupAndTabItemInterface(const DisplayGroupAndTabItemInterface& obj); //DisplayGroupAndTabItemInterface& operator=(const DisplayGroupAndTabItemInterface& obj); // ADD_NEW_MEMBERS_HERE }; #ifdef __DISPLAY_GROUP_AND_TAB_ITEM_INTERFACE_DECLARE__ // #endif // __DISPLAY_GROUP_AND_TAB_ITEM_INTERFACE_DECLARE__ } // namespace #endif //__DISPLAY_GROUP_AND_TAB_ITEM_INTERFACE_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Common/DisplayGroupEnum.cxx000066400000000000000000000250501300200146000257710ustar00rootroot00000000000000 /*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 __DISPLAY_GROUP_ENUM_DECLARE__ #include "DisplayGroupEnum.h" #undef __DISPLAY_GROUP_ENUM_DECLARE__ #include "CaretAssert.h" using namespace caret; /** * \class caret::DisplayGroupEnum * \brief Enumerated types for grouping related data. * * This class provides an enumerated type that * can be used by other classes to group related * properties, typically display properties. */ /** * Constructor. * * @param enumValue * An enumerated value. * @param name * Name of enumerated value. * * @param guiName * User-friendly name for use in user-interface. */ DisplayGroupEnum::DisplayGroupEnum(const Enum enumValue, const AString& name, const AString& guiName) { this->enumValue = enumValue; this->integerCode = integerCodeCounter++; this->name = name; this->guiName = guiName; } /** * Destructor. */ DisplayGroupEnum::~DisplayGroupEnum() { } /** * Initialize the enumerated metadata. */ void DisplayGroupEnum::initialize() { if (initializedFlag) { return; } initializedFlag = true; enumData.push_back(DisplayGroupEnum(DISPLAY_GROUP_TAB, "DISPLAY_GROUP_TAB", "Tab")); enumData.push_back(DisplayGroupEnum(DISPLAY_GROUP_A, "DISPLAY_GROUP_A", "Group A")); enumData.push_back(DisplayGroupEnum(DISPLAY_GROUP_B, "DISPLAY_GROUP_B", "Group B")); enumData.push_back(DisplayGroupEnum(DISPLAY_GROUP_C, "DISPLAY_GROUP_C", "Group C")); enumData.push_back(DisplayGroupEnum(DISPLAY_GROUP_D, "DISPLAY_GROUP_D", "Group D")); if (static_cast(enumData.size()) != DisplayGroupEnum::NUMBER_OF_GROUPS) { CaretAssertMessage(0, "NUMBER_OF_GROUPS constant is incorrect. New ENUMs added?"); } } /** * 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 DisplayGroupEnum* DisplayGroupEnum::findData(const Enum enumValue) { if (initializedFlag == false) initialize(); size_t num = enumData.size(); for (size_t i = 0; i < num; i++) { const DisplayGroupEnum* 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 DisplayGroupEnum::toName(Enum enumValue) { if (initializedFlag == false) initialize(); const DisplayGroupEnum* 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. */ DisplayGroupEnum::Enum DisplayGroupEnum::fromName(const AString& name, bool* isValidOut) { if (initializedFlag == false) initialize(); bool validFlag = false; Enum enumValue = getDefaultValue(); for (std::vector::iterator iter = enumData.begin(); iter != enumData.end(); iter++) { const DisplayGroupEnum& 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 DisplayGroupEnum")); } return enumValue; } /** * Get a GUI string representation of the enumerated type. * @param enumValue * Enumerated value. * @return * String representing enumerated value. */ AString DisplayGroupEnum::toGuiName(Enum enumValue) { if (initializedFlag == false) initialize(); const DisplayGroupEnum* 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. */ DisplayGroupEnum::Enum DisplayGroupEnum::fromGuiName(const AString& guiName, bool* isValidOut) { if (initializedFlag == false) initialize(); bool validFlag = false; Enum enumValue = getDefaultValue(); for (std::vector::iterator iter = enumData.begin(); iter != enumData.end(); iter++) { const DisplayGroupEnum& 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 DisplayGroupEnum")); } return enumValue; } /** * Get the integer code for a data type. * * @return * Integer code for data type. */ int32_t DisplayGroupEnum::toIntegerCode(Enum enumValue) { if (initializedFlag == false) initialize(); const DisplayGroupEnum* 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. */ DisplayGroupEnum::Enum DisplayGroupEnum::fromIntegerCode(const int32_t integerCode, bool* isValidOut) { if (initializedFlag == false) initialize(); bool validFlag = false; Enum enumValue = getDefaultValue(); for (std::vector::iterator iter = enumData.begin(); iter != enumData.end(); iter++) { const DisplayGroupEnum& 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 DisplayGroupEnum")); } 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 DisplayGroupEnum::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 enumerated type values for GROUPs. Like getAllEnums() * except TAB is excluded. DISPLAY_GROUP_A, DISPLAY_GROUP_B, etc. * * @param allGroupEnums * A vector that is OUTPUT containing all of the GROUP enumerated values (TAB excluded). */ void DisplayGroupEnum::getAllEnumsExceptTab(std::vector& allGroupEnums) { if (initializedFlag == false) initialize(); allGroupEnums.clear(); for (std::vector::iterator iter = enumData.begin(); iter != enumData.end(); iter++) { if (iter->enumValue != DisplayGroupEnum::DISPLAY_GROUP_TAB) { allGroupEnums.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 DisplayGroupEnum::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(DisplayGroupEnum::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 DisplayGroupEnum::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(DisplayGroupEnum::toGuiName(iter->enumValue)); } if (isSorted) { std::sort(allGuiNames.begin(), allGuiNames.end()); } } /** * @return The default value for a display group. */ DisplayGroupEnum::Enum DisplayGroupEnum::getDefaultValue() { return DISPLAY_GROUP_A; } connectome-workbench-1.2.3+git41-gc4c6c90/src/Common/DisplayGroupEnum.h000066400000000000000000000067611300200146000254260ustar00rootroot00000000000000#ifndef __DISPLAY_GROUP_ENUM__H_ #define __DISPLAY_GROUP_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 DisplayGroupEnum { public: /** * Enumerated values. */ enum Enum { /** Unique to Tab */ DISPLAY_GROUP_TAB, /** Group A */ DISPLAY_GROUP_A, /** Group B */ DISPLAY_GROUP_B, /** Group C */ DISPLAY_GROUP_C, /** Group D */ DISPLAY_GROUP_D /* IF A NEW ENUM IS ADDED UPDATE NUMBER_OF_GROUPS ENUM BELOW */ }; enum Misc { /** Number of groups, update if new enums are added */ NUMBER_OF_GROUPS = 5 }; ~DisplayGroupEnum(); 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 getAllEnumsExceptTab(std::vector& allGroupEnums); static void getAllNames(std::vector& allNames, const bool isSorted); static void getAllGuiNames(std::vector& allGuiNames, const bool isSorted); static DisplayGroupEnum::Enum getDefaultValue(); private: DisplayGroupEnum(const Enum enumValue, const AString& name, const AString& guiName); static const DisplayGroupEnum* 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 __DISPLAY_GROUP_ENUM_DECLARE__ std::vector DisplayGroupEnum::enumData; bool DisplayGroupEnum::initializedFlag = false; int32_t DisplayGroupEnum::integerCodeCounter = 0; #endif // __DISPLAY_GROUP_ENUM_DECLARE__ } // namespace #endif //__DISPLAY_GROUP_ENUM__H_ connectome-workbench-1.2.3+git41-gc4c6c90/src/Common/DrawnWithOpenGLTextureInfo.cxx000066400000000000000000000140321300200146000276710ustar00rootroot00000000000000 /*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"; } connectome-workbench-1.2.3+git41-gc4c6c90/src/Common/DrawnWithOpenGLTextureInfo.h000066400000000000000000000044421300200146000273220ustar00rootroot00000000000000#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__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Common/DrawnWithOpenGLTextureInterface.cxx000066400000000000000000000041741300200146000307040ustar00rootroot00000000000000 /*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) { } connectome-workbench-1.2.3+git41-gc4c6c90/src/Common/DrawnWithOpenGLTextureInterface.h000066400000000000000000000054001300200146000303220ustar00rootroot00000000000000#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__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Common/ElapsedTimer.cxx000066400000000000000000000063571300200146000251110ustar00rootroot00000000000000 /*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 __ELAPSED_TIMER_DECLARE__ #include "ElapsedTimer.h" #undef __ELAPSED_TIMER_DECLARE__ #include "CaretAssert.h" using namespace caret; /** * Constructor. */ ElapsedTimer::ElapsedTimer() : CaretObject() { m_started = false; } /** * Destructor. */ ElapsedTimer::~ElapsedTimer() { } /** * Get a description of this object's content. * @return String describing this object's content. */ AString ElapsedTimer::toString() const { return "ElapsedTimer"; } /** * Start the timer. */ void ElapsedTimer::start() { #ifdef CARET_OS_WINDOWS m_startTime.m_tickCount = GetTickCount();//TODO: find a good way to use getTickCount64() so it doesn't have a reset at 49 days of uptime #else gettimeofday(&(m_startTime.m_timeVal), NULL); #endif m_started = true; } /** * Reset the timer as if no time had elapsed. */ void ElapsedTimer::reset() { /* * Start will change the time to 'now'. */ this->start(); } /** * Get the elapsed time in seconds. * * @return Elapsed time in seconds. */ double ElapsedTimer::getElapsedTimeSeconds() const { return getElapsedTimeMilliseconds() / 1000.0; } /** * Get the elapsed time in milliseconds. * * @return Elapsed time in milliseconds. */ double ElapsedTimer::getElapsedTimeMilliseconds() const { CaretAssertMessage(m_started, "Timer has not been started"); MyTimeStore endTime; #ifdef CARET_OS_WINDOWS endTime.m_tickCount = GetTickCount();//TODO: find a good way to use getTickCount64() when possible so it doesn't have a reset at 49 days of uptime if (endTime.m_tickCount < m_startTime.m_tickCount)//check for the 49 day wrap { endTime.m_tickCount += ((uint64_t)1)<<32;//m_tickCount is 64 bit, so this doesn't overflow } const double diffTimeMilli = (double)(endTime.m_tickCount - m_startTime.m_tickCount);//contrary to its name, it returns milliseconds, not ticks #else gettimeofday(&(endTime.m_timeVal), NULL); double diffSeconds = endTime.m_timeVal.tv_sec - m_startTime.m_timeVal.tv_sec; double diffMicroseconds = endTime.m_timeVal.tv_usec - m_startTime.m_timeVal.tv_usec; /*if (diffMicroseconds < 0) {//this is only needed if we are displaying both parts separately diffMicroseconds += 1000000; diffSeconds -= 1;//don't forget to subtract the second you just added to microseconds }//*/ const double diffTimeMilli = diffSeconds * 1000.0 + (diffMicroseconds / 1000.0); #endif return diffTimeMilli; } connectome-workbench-1.2.3+git41-gc4c6c90/src/Common/ElapsedTimer.h000066400000000000000000000042661300200146000245330ustar00rootroot00000000000000#ifndef __ELAPSED_TIMER__H_ #define __ELAPSED_TIMER__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*/ #ifdef CARET_OS_WINDOWS #include "windows.h" #else #include #endif #include "CaretObject.h" namespace caret { //getTimeOfDay() isn't cross-platform, so use some ifdefs for windows #ifdef CARET_OS_WINDOWS struct MyTimeStore { uint64_t m_tickCount;//can store return from GetTickCount64() which doesn't reset at 49 days, but is vista and above only };//also useful for detecting and correcting for a wrap, can just add (uint64_t)1<<32 #else struct MyTimeStore { struct timeval m_timeVal; }; #endif /// An elapsed timer class ElapsedTimer : public CaretObject { public: ElapsedTimer(); virtual ~ElapsedTimer(); void start(); void reset(); double getElapsedTimeSeconds() const; double getElapsedTimeMilliseconds() const; private: ElapsedTimer(const ElapsedTimer&); ElapsedTimer& operator=(const ElapsedTimer&); public: virtual AString toString() const; private: MyTimeStore m_startTime; bool m_started; }; #ifdef __ELAPSED_TIMER_DECLARE__ // #endif // __ELAPSED_TIMER_DECLARE__ } // namespace #endif //__ELAPSED_TIMER__H_ connectome-workbench-1.2.3+git41-gc4c6c90/src/Common/Event.cxx000066400000000000000000000063371300200146000236120ustar00rootroot00000000000000/*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" using namespace caret; /** * Constructor. * * @param eventType * The type of the event. */ Event::Event(const EventTypeEnum::Enum eventType) { this->eventType = eventType; this->errorMessage = ""; this->errorStatus = false; this->eventProcessedCount = 0; } /** * Destructor. */ Event::~Event() { } /** * Get the type of event. * * @return * The event type. */ EventTypeEnum::Enum Event::getEventType() const { return this->eventType; } /** * Get a pointer to this event. The intention of this * method is to allow the sender of the event to create * this event ?statically? (not with new) and then send * "event"->getPointer() to the event manager since * &eventObject will not cast automatically to an * Event object pointer. * * @return * Pointer to this object with the base Event class. */ Event* Event::getPointer() { return this; } /** * Was there an error processing this event? * * @return * True if there was an error processing the * event else false. */ bool Event::isError() const { return this->errorStatus; } /** * Get the error message which is only * valid if isError() returns true. * * @return * A message describing the error. */ AString Event::getErrorMessage() const { return this->errorMessage; } /** * Consumer of an event can set the error message * to indicate that there was an error processing * the event. * * If the given error message is not empty, * calling this method will result in the error * status being set and the event manager will not * send this event to any other receivers. * * @param errorMessage * The error message. */ void Event::setErrorMessage(const AString& errorMessage) { if (errorMessage.isEmpty() == false) { this->errorMessage = errorMessage; this->errorStatus = true; } } /** * Set the this event was processed by a listener. */ void Event::setEventProcessed() { this->eventProcessedCount++; } /** * Get the number of times the event was processed. * * @preturn Count of receivers that processed this event. */ int32_t Event::getEventProcessCount() const { return this->eventProcessedCount; } /** * Get String representation of caret object. * @return String containing caret object. * */ AString Event::toString() const { AString s = EventTypeEnum::toName(this->eventType); return s; } connectome-workbench-1.2.3+git41-gc4c6c90/src/Common/Event.h000066400000000000000000000040431300200146000232270ustar00rootroot00000000000000#ifndef __EVENT_H__ #define __EVENT_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 "EventTypeEnum.h" namespace caret { /// Base class for an event. class Event : public CaretObject { public: EventTypeEnum::Enum getEventType() const; virtual ~Event(); Event* getPointer(); bool isError() const; AString getErrorMessage() const; void setErrorMessage(const AString& errorMessage); virtual AString toString() const; void setEventProcessed(); int32_t getEventProcessCount() const; protected: Event(const EventTypeEnum::Enum eventType); private: Event(const Event&); Event& operator=(const Event&); /** The type of event */ EventTypeEnum::Enum eventType; /** The error message */ AString errorMessage; /** Tracks error status */ bool errorStatus; /** Number of times event was processed. */ int32_t eventProcessedCount; friend class EventManager; }; } // namespace #endif // __EVENT_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Common/EventAlertUser.cxx000066400000000000000000000037641300200146000254420ustar00rootroot00000000000000 /*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_ALERT_USER_DECLARE__ #include "EventAlertUser.h" #undef __EVENT_ALERT_USER_DECLARE__ #include "CaretAssert.h" #include "EventTypeEnum.h" using namespace caret; /** * \class caret::EventAlertUser * \brief Event that alerts the user to a problem * \ingroup Common * * If there is a problem in non-GUI code, it may be desirable to inform * the user of a problem. However, when this type of problem occurs, * passing this information "up the call stack" may be problematic and * using an exception may not be possible or desired. * * In these instance this event class may be used. When there is a GUI, * this event is received by the GuiManager and a pop-up is presented * to the user. If there is not a GUI, the EventManager will not send * this event and instead, log the message at the severe level. */ /** * Constructor for an alert message. */ EventAlertUser::EventAlertUser(const AString& message) : Event(EventTypeEnum::EVENT_ALERT_USER), m_message(message) { } /** * Destructor. */ EventAlertUser::~EventAlertUser() { } /** * @return The message for the alert. */ AString EventAlertUser::getMessage() const { return m_message; } connectome-workbench-1.2.3+git41-gc4c6c90/src/Common/EventAlertUser.h000066400000000000000000000030351300200146000250560ustar00rootroot00000000000000#ifndef __EVENT_ALERT_USER_H__ #define __EVENT_ALERT_USER_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 EventAlertUser : public Event { public: EventAlertUser(const AString& message); virtual ~EventAlertUser(); AString getMessage() const; // ADD_NEW_METHODS_HERE private: EventAlertUser(const EventAlertUser&); EventAlertUser& operator=(const EventAlertUser&); const AString m_message; // ADD_NEW_MEMBERS_HERE }; #ifdef __EVENT_ALERT_USER_DECLARE__ // #endif // __EVENT_ALERT_USER_DECLARE__ } // namespace #endif //__EVENT_ALERT_USER_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Common/EventBrowserTabIndicesGetAll.cxx000066400000000000000000000045071300200146000301720ustar00rootroot00000000000000 /*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 __EVENT_BROWSER_TAB_INDICES_GET_ALL_DECLARE__ #include "EventBrowserTabIndicesGetAll.h" #undef __EVENT_BROWSER_TAB_INDICES_GET_ALL_DECLARE__ #include "CaretAssert.h" #include "EventTypeEnum.h" using namespace caret; /** * \class caret::EventBrowserTabIndicesGetAll * \brief Event to get indices of all valid browser tabs. * \ingroup Common */ /** * Constructor. */ EventBrowserTabIndicesGetAll::EventBrowserTabIndicesGetAll() : Event(EventTypeEnum::EVENT_BROWSER_TAB_INDICES_GET_ALL) { } /** * Destructor. */ EventBrowserTabIndicesGetAll::~EventBrowserTabIndicesGetAll() { } /** * Add the tab index of a valid browser tab. * * @parm browserTabIndex * Index of the browser tab. */ void EventBrowserTabIndicesGetAll::addBrowserTabIndex(const int32_t browserTabIndex) { m_browserTabIndices.push_back(browserTabIndex); } /** * @return Indices of all valid browser tabs. */ std::vector EventBrowserTabIndicesGetAll::getAllBrowserTabIndices() const { return m_browserTabIndices; } /** * Get the validity of a browser tab. * * @parm browserTabIndex * Index of the browser tab. * @return * True if browser tab index is valid, else false. */ bool EventBrowserTabIndicesGetAll::isValidBrowserTabIndex(const int32_t browserTabIndex) { if (std::find(m_browserTabIndices.begin(), m_browserTabIndices.end(), browserTabIndex) != m_browserTabIndices.end()) { return true; } return false; } connectome-workbench-1.2.3+git41-gc4c6c90/src/Common/EventBrowserTabIndicesGetAll.h000066400000000000000000000036001300200146000276100ustar00rootroot00000000000000#ifndef __EVENT_BROWSER_TAB_INDICES_GET_ALL_H__ #define __EVENT_BROWSER_TAB_INDICES_GET_ALL_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 EventBrowserTabIndicesGetAll : public Event { public: EventBrowserTabIndicesGetAll(); virtual ~EventBrowserTabIndicesGetAll(); void addBrowserTabIndex(const int32_t browserTabIndex); std::vector getAllBrowserTabIndices() const; bool isValidBrowserTabIndex(const int32_t browserTabIndex); // ADD_NEW_METHODS_HERE private: EventBrowserTabIndicesGetAll(const EventBrowserTabIndicesGetAll&); EventBrowserTabIndicesGetAll& operator=(const EventBrowserTabIndicesGetAll&); std::vector m_browserTabIndices; // ADD_NEW_MEMBERS_HERE }; #ifdef __EVENT_BROWSER_TAB_INDICES_GET_ALL_DECLARE__ // #endif // __EVENT_BROWSER_TAB_INDICES_GET_ALL_DECLARE__ } // namespace #endif //__EVENT_BROWSER_TAB_INDICES_GET_ALL_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Common/EventGetViewportSize.cxx000066400000000000000000000064251300200146000266430ustar00rootroot00000000000000 /*LICENSE_START*/ /* * Copyright (C) 2015 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_GET_VIEWPORT_SIZE_DECLARE__ #include "EventGetViewportSize.h" #undef __EVENT_GET_VIEWPORT_SIZE_DECLARE__ #include "CaretAssert.h" #include "EventTypeEnum.h" using namespace caret; /** * \class caret::EventGetViewportSize * \brief Event to get the viewport size for a tab. * \ingroup GuiQt */ /** * Constructor for finding a specific tab index. * * @param tabIndex * Index of tab for which viewport size is requested. */ //EventGetViewportSize::EventGetViewportSize(const int32_t tabIndex) //: Event(EventTypeEnum::EVENT_BROWSER_TAB_GET_VIEWPORT_SIZE), //m_mode(MODE_TAB_INDEX), //m_tabIndex(tabIndex), //m_viewportValid(false) //{ // m_viewport[0] = 0; // m_viewport[1] = 0; // m_viewport[2] = 0; // m_viewport[3] = 0; //} /** * Constructor for finding a surface or volume montage. * * @param mode * The mode. */ EventGetViewportSize::EventGetViewportSize(const Mode mode, const int32_t index) : Event(EventTypeEnum::EVENT_GET_VIEWPORT_SIZE), m_mode(mode), m_index(index), m_viewportValid(false) { m_viewport[0] = 0; m_viewport[1] = 0; m_viewport[2] = 0; m_viewport[3] = 0; } /** * Destructor. */ EventGetViewportSize::~EventGetViewportSize() { } /** * @return Is the viewport size valid (width and height greater than zero)? */ bool EventGetViewportSize::isViewportSizeValid() const { return m_viewportValid; } /** * @return The mode. */ EventGetViewportSize::Mode EventGetViewportSize::getMode() const { return m_mode; } /** * @return The tab/window index for which the viewport size is requested. */ int32_t EventGetViewportSize::getIndex() const { return m_index; } /** * Get the viewport size. * * @param viewportOut * Output containing viewport x, y, width, height. */ void EventGetViewportSize::getViewportSize(int32_t viewportOut[4]) const { viewportOut[0] = m_viewport[0]; viewportOut[1] = m_viewport[1]; viewportOut[2] = m_viewport[2]; viewportOut[3] = m_viewport[3]; } /** * Set the viewport size. * * @param viewport * Viewport x, y, width, height. */ void EventGetViewportSize::setViewportSize(const int32_t viewport[4]) { m_viewportValid = false; m_viewport[0] = viewport[0]; m_viewport[1] = viewport[1]; m_viewport[2] = viewport[2]; m_viewport[3] = viewport[3]; if ((m_viewport[2] > 0) && (m_viewport[3] > 0)) { m_viewportValid = true; } } connectome-workbench-1.2.3+git41-gc4c6c90/src/Common/EventGetViewportSize.h000066400000000000000000000044071300200146000262660ustar00rootroot00000000000000#ifndef __EVENT_GET_VIEWPORT_SIZE_H__ #define __EVENT_GET_VIEWPORT_SIZE_H__ /*LICENSE_START*/ /* * Copyright (C) 2015 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 EventGetViewportSize : public Event { public: enum Mode { MODE_SURFACE_MONTAGE, MODE_TAB_BEFORE_MARGINS_INDEX, MODE_TAB_AFTER_MARGINS_INDEX, MODE_VOLUME_MONTAGE, MODE_WINDOW_INDEX, }; // EventGetViewportSize(const int32_t tabIndex); EventGetViewportSize(const Mode mode, const int32_t index); virtual ~EventGetViewportSize(); Mode getMode() const; int32_t getIndex() const; bool isViewportSizeValid() const; void getViewportSize(int32_t viewportOut[4]) const; void setViewportSize(const int32_t viewport[4]); // ADD_NEW_METHODS_HERE private: EventGetViewportSize(const EventGetViewportSize&); EventGetViewportSize& operator=(const EventGetViewportSize&); const Mode m_mode; const int32_t m_index; int32_t m_viewport[4]; bool m_viewportValid; // ADD_NEW_MEMBERS_HERE }; #ifdef __EVENT_GET_VIEWPORT_SIZE_DECLARE__ // #endif // __EVENT_GET_VIEWPORT_SIZE_DECLARE__ } // namespace #endif //__EVENT_GET_VIEWPORT_SIZE_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Common/EventListenerInterface.h000066400000000000000000000037211300200146000265600ustar00rootroot00000000000000#ifndef __EVENT_LISTENER_INTERFACE_H__ #define __EVENT_LISTENER_INTERFACE_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*/ namespace caret { class Event; /** * \brief Interface for objects receiving events. * * This interface must be implemented by any object that * wants to receive events. */ class EventListenerInterface { protected: /** * Constructor. */ EventListenerInterface() { } /** * Destructor. */ virtual ~EventListenerInterface() { } private: EventListenerInterface(const EventListenerInterface&) { } EventListenerInterface& operator=(const EventListenerInterface& ei); /*{ return *this; }//*/ //TSC: removed implementation to prevent "parameter unused" warnings, since it is private in an interface class public: /** * Receive an event. * * @param event * The event that the receive can respond to. */ virtual void receiveEvent(Event* event) = 0; }; } // namespace #endif // __EVENT_LISTENER_INTERFACE_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Common/EventManager.cxx000066400000000000000000000523101300200146000250750ustar00rootroot00000000000000/*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 #define __EVENT_MANAGER_MAIN__ #include "Event.h" #include "EventManager.h" #undef __EVENT_MANAGER_MAIN__ #include "ApplicationInformation.h" #include "CaretAssert.h" #include "CaretLogger.h" #include "EventAlertUser.h" #include "EventListenerInterface.h" using namespace caret; /** * \class caret::EventManager * \brief The event manager. * * The event manager processes events * from senders to receivers. * * Events are sent by calling this class' sendEvent() * method. * * Objects that wish to receive events must (1) extend * publicly EventListenerInterface, (2) implement * EventListenerInterface's receiveEvent() method, * (3) Call one of two methods in EventManger, * addEventListener() or addProcessedEventListener() which * are typically called from the object's constructor, and * (4) call removeEventFromListener() or removeAllEventsFromListener * to cease listening for events which is typciall called * from the object's constructor. * * In most cases addEventListener() is used to request events. * addProcessedEventListener() is used when an object wants * to be notified of an event but not until after it has been * processed by at least one other receiver. For example, * a event for a new window may be sent. A receiver of the * event will create the new window. Other receivers may * want to know AFTER the window has been created in which * case these receivers will use addProcessedEventListener(). */ /** * Constructor. */ EventManager::EventManager() { m_eventIssuedCounter = 0; m_eventBlockingCounter.resize(EventTypeEnum::EVENT_COUNT, 0); } /** * Destructor. */ EventManager::~EventManager() { /* * Verify that all listeners were removed. */ for (int32_t i = 0; i < EventTypeEnum::EVENT_COUNT; i++) { EVENT_LISTENER_CONTAINER el = m_eventListeners[i]; if (el.empty() == false) { EventTypeEnum::Enum enumValue = static_cast(i); std::cout << "Not all listeners removed for event " << EventTypeEnum::toName(enumValue) << ", count is: " << el.size() << std::endl; } } /* * Verify that all processed listeners were removed. */ for (int32_t i = 0; i < EventTypeEnum::EVENT_COUNT; i++) { EVENT_LISTENER_CONTAINER el = m_eventProcessedListeners[i]; if (el.empty() == false) { EventTypeEnum::Enum enumValue = static_cast(i); std::cout << "Not all listeners removed for processed event " << EventTypeEnum::toName(enumValue) << ", count is: " << el.size() << std::endl; } } } /** * Create the event manager. */ void EventManager::createEventManager() { CaretAssertMessage((EventManager::s_singletonEventManager == NULL), "Event manager has already been created."); EventManager::s_singletonEventManager = new EventManager(); } /** * Delete the event manager. * This may only be called one time after event manager is created. */ void EventManager::deleteEventManager() { CaretAssertMessage((EventManager::s_singletonEventManager != NULL), "Event manager does not exist, cannot delete it."); delete EventManager::s_singletonEventManager; EventManager::s_singletonEventManager = NULL; } /** * Get the one and only event mangers. * * @return Pointer to the event manager. */ EventManager* EventManager::get() { CaretAssertMessage(EventManager::s_singletonEventManager, "Event manager was not created.\n" "It must be created with EventManager::createEventManager()."); return EventManager::s_singletonEventManager; } /** * Add a listener for a specific event. * * @param eventListener * Listener for an event. * @param listenForEventType * Type of event that is wanted. */ void EventManager::addEventListener(EventListenerInterface* eventListener, const EventTypeEnum::Enum listenForEventType) { #ifdef CONTAINER_VECTOR m_eventListeners[listenForEventType].push_back(eventListener); #elif CONTAINER_HASH_SET m_eventListeners[listenForEventType].insert(eventListener); #elif CONTAINER_SET m_eventListeners[listenForEventType].insert(eventListener); #else INTENTIONAL_COMPILER_ERROR_MISSING_CONTAINER_TYPE #endif //std::cout << "Adding listener from class " //<< typeid(*eventListener).name() //<< " for " //<< EventTypeEnum::toName(listenForEventType) //<< std::endl; } /** * Add a listener for a specific event but only receive the * event AFTER it has been processed. * * @param eventListener * Listener for an event. * @param listenForEventType * Type of event that is wanted. */ void EventManager::addProcessedEventListener(EventListenerInterface* eventListener, const EventTypeEnum::Enum listenForEventType) { #ifdef CONTAINER_VECTOR m_eventProcessedListeners[listenForEventType].push_back(eventListener); #elif CONTAINER_HASH_SET m_eventProcessedListeners[listenForEventType].insert(eventListener); #elif CONTAINER_SET m_eventProcessedListeners[listenForEventType].insert(eventListener); #else INTENTIONAL_COMPILER_ERROR_MISSING_CONTAINER_TYPE #endif //std::cout << "Adding listener from class " //<< typeid(*eventListener).name() //<< " for " //<< EventTypeEnum::toName(listenForEventType) //<< std::endl; } /** * Stop listening for an event. * * @param eventListener * Listener for an event. * @param listenForEventType * Type of event that is no longer wanted. */ void EventManager::removeEventFromListener(EventListenerInterface* eventListener, const EventTypeEnum::Enum listenForEventType) { #ifdef CONTAINER_VECTOR /* * Remove from NORMAL listeners */ EVENT_LISTENER_CONTAINER& listeners = m_eventListeners[listenForEventType]; EVENT_LISTENER_CONTAINER_ITERATOR eventIter = std::find(listeners.begin(), listeners.end(), eventListener); if (eventIter != listeners.end()) { listeners.erase(eventIter); } /* * Remove from PROCESSED listeners * These are issued AFTER all of the NORMAL listeners have been notified */ EVENT_LISTENER_CONTAINER& processedListeners = m_eventProcessedListeners[listenForEventType]; EVENT_LISTENER_CONTAINER_ITERATOR processedEventIter = std::find(processedListeners.begin(), processedListeners.end(), 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); #elif CONTAINER_SET m_eventListeners[listenForEventType].erase(eventListener); m_eventProcessedListeners[listenForEventType].erase(eventListener); #else INTENTIONAL_COMPILER_ERROR_MISSING_CONTAINER_TYPE #endif } /** * Stop listening for all events. * @param eventListener * Listener for all events. */ void EventManager::removeAllEventsFromListener(EventListenerInterface* eventListener) { for (int32_t i = 0; i < EventTypeEnum::EVENT_COUNT; i++) { removeEventFromListener(eventListener, static_cast(i)); } } /** * Send an event. * * @param event * Event that is sent. */ void EventManager::sendEvent(Event* event) { EventTypeEnum::Enum eventType = event->getEventType(); const AString eventNumberString = AString::number(m_eventIssuedCounter); const AString eventMessagePrefix = ("Event " + eventNumberString + ": " + event->toString() + " from thread: " + AString::number((uint64_t)QThread::currentThread()) + " "); const int32_t eventTypeIndex = static_cast(eventType); CaretAssertVectorIndex(m_eventBlockingCounter, eventTypeIndex); if (m_eventBlockingCounter[eventTypeIndex] > 0) { AString msg = (eventMessagePrefix + " is blocked. Blocking counter=" + AString::number(m_eventBlockingCounter[eventTypeIndex])); CaretLogFiner(msg); } else { if (eventType == EventTypeEnum::EVENT_ALERT_USER) { /* * Only send the ALERT USER event if there is a GUI. * Otherwise, simply log the alert message. */ EventAlertUser* alertEvent = dynamic_cast(event); CaretAssert(alertEvent); if (ApplicationInformation::getApplicationType() != ApplicationTypeEnum::APPLICATION_TYPE_GRAPHICAL_USER_INTERFACE) { CaretLogSevere(alertEvent->getMessage()); return; } } /* * Get listeners for event. */ EVENT_LISTENER_CONTAINER listeners = m_eventListeners[eventType]; 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. */ for (EVENT_LISTENER_CONTAINER_ITERATOR iter = listeners.begin(); 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()) { CaretLogWarning("Event " + eventNumberString + " had error: " + event->toString() + ": " + event->getErrorMessage()); break; } } /* * Verify event was processed. */ if (event->getEventProcessCount() > 0) { /* * Send event to each of the PROCESSED listeners. */ EVENT_LISTENER_CONTAINER processedListeners = m_eventProcessedListeners[eventType]; for (EVENT_LISTENER_CONTAINER_ITERATOR iter = processedListeners.begin(); 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()) { CaretLogWarning("Event " + eventNumberString + " had error: " + event->toString()); break; } } } else { // Too many prints (JWH) CaretLogFine("Event " + eventNumberString + " not processed: " + event->toString()); } m_eventIssuedCounter++; } } /** * Send a "simple" event. A simple event is one for which there is no * specialized subclass of "Event". This method try to prevent sending * a "non-simple" event and it will need to be updated if an event * either has a specialized subclass added or removed. * * @param eventType * Event type that is sent. */ void EventManager::sendSimpleEvent(const EventTypeEnum::Enum eventType) { switch (eventType) { case EventTypeEnum::EVENT_ANNOTATION_TOOLBAR_UPDATE: case EventTypeEnum::EVENT_BROWSER_WINDOW_MENUS_UPDATE: { sendEvent(Event(eventType).getPointer()); } break; case EventTypeEnum::EVENT_INVALID: case EventTypeEnum::EVENT_COUNT: { const AString msg(EventTypeEnum::toName(eventType) + " should never be sent as an event."); CaretAssertMessage(0, msg); CaretLogSevere(msg); } break; case EventTypeEnum::EVENT_ALERT_USER: case EventTypeEnum::EVENT_ANNOTATION_ADD_TO_REMOVE_FROM_FILE: case EventTypeEnum::EVENT_ANNOTATION_COLOR_BAR_GET: case EventTypeEnum::EVENT_ANNOTATION_CREATE_NEW_TYPE: case EventTypeEnum::EVENT_ANNOTATION_GET_DRAWN_IN_WINDOW: case EventTypeEnum::EVENT_ANNOTATION_GROUP_GET_WITH_KEY: case EventTypeEnum::EVENT_ANNOTATION_GROUPING: case EventTypeEnum::EVENT_BRAIN_RESET: case EventTypeEnum::EVENT_BRAIN_STRUCTURE_GET_ALL: case EventTypeEnum::EVENT_BROWSER_TAB_DELETE: case EventTypeEnum::EVENT_BROWSER_TAB_GET: case EventTypeEnum::EVENT_BROWSER_TAB_GET_ALL: 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_CREATE_TABS: case EventTypeEnum::EVENT_BROWSER_WINDOW_GRAPHICS_HAVE_BEEN_REDRAWN: case EventTypeEnum::EVENT_BROWSER_WINDOW_NEW: case EventTypeEnum::EVENT_CARET_MAPPABLE_DATA_FILES_GET: case EventTypeEnum::EVENT_CARET_MAPPABLE_DATA_FILE_MAPS_VIEWED_IN_OVERLAYS: case EventTypeEnum::EVENT_CHART_MATRIX_YOKING_VALIDATION: case EventTypeEnum::EVENT_DATA_FILE_ADD: case EventTypeEnum::EVENT_DATA_FILE_DELETE: case EventTypeEnum::EVENT_DATA_FILE_READ: case EventTypeEnum::EVENT_DATA_FILE_RELOAD: case EventTypeEnum::EVENT_GET_DISPLAYED_DATA_FILES: case EventTypeEnum::EVENT_GET_NODE_DATA_FILES: 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_UPDATE_ALL_WINDOWS: case EventTypeEnum::EVENT_GRAPHICS_UPDATE_ONE_WINDOW: case EventTypeEnum::EVENT_HELP_VIEWER_DISPLAY: case EventTypeEnum::EVENT_IDENTIFICATION_HIGHLIGHT_LOCATION: case EventTypeEnum::EVENT_IDENTIFICATION_SYMBOL_REMOVAL: case EventTypeEnum::EVENT_IDENTIFICATION_REQUEST: case EventTypeEnum::EVENT_IMAGE_CAPTURE: case EventTypeEnum::EVENT_MAC_DOCK_MENU_UPDATE: case EventTypeEnum::EVENT_MAP_YOKING_SELECT_MAP: case EventTypeEnum::EVENT_MAP_YOKING_VALIDATION: case EventTypeEnum::EVENT_MODEL_ADD: case EventTypeEnum::EVENT_MODEL_DELETE: case EventTypeEnum::EVENT_MODEL_GET_ALL: case EventTypeEnum::EVENT_MODEL_SURFACE_GET: case EventTypeEnum::EVENT_NODE_IDENTIFICATION_COLORS_GET_FROM_CHARTS: case EventTypeEnum::EVENT_OPENGL_TEXTURE: 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_SPEC_FILE_READ_DATA_FILES: case EventTypeEnum::EVENT_SURFACE_COLORING_INVALIDATE: case EventTypeEnum::EVENT_SURFACES_GET: case EventTypeEnum::EVENT_SURFACE_STRUCTURES_VALID_GET: case EventTypeEnum::EVENT_TOOLBOX_SELECTION_DISPLAY: 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: { const AString msg(EventTypeEnum::toName(eventType) + " has an special subclass of class Event and should never be sent as an event."); CaretAssertMessage(0, msg); CaretLogSevere(msg); } break; } } /** * Block an event. A counter is used to track blocking of each * event type. Each time a request is made to block an event type, * the counter is incremented for that event type. When a request * is made to un-block the event, the counter is decremented. This * allows multiple requests for blocking an event to come from * different sections of the source code. Thus, anytime the * blocking counter is greater than zero for an event, the event * is blocked. * * @param eventType * Type of event to block. * @param blockStatus * Blocking status (true increments blocking counter, * false decrements blocking counter. */ void EventManager::blockEvent(const EventTypeEnum::Enum eventType, const bool blockStatus) { const int32_t eventTypeIndex = static_cast(eventType); CaretAssertVectorIndex(m_eventBlockingCounter, eventTypeIndex); const AString eventName = EventTypeEnum::toName(eventType); if (blockStatus) { m_eventBlockingCounter[eventTypeIndex]++; CaretLogFiner("Blocking event " + eventName + " blocking counter is now " + AString::number(m_eventBlockingCounter[eventTypeIndex])); } else { if (m_eventBlockingCounter[eventTypeIndex] > 0) { m_eventBlockingCounter[eventTypeIndex]--; CaretLogFiner("Unblocking event " + eventName + " blocking counter is now " + AString::number(m_eventBlockingCounter[eventTypeIndex])); } else { const AString message("Trying to unblock event " + eventName + " but it is not blocked"); CaretAssertMessage(0, message); CaretLogWarning(message); } } } /** * @return The cumulative number of events that have been sent. */ int64_t EventManager::getEventIssuedCounter() const { return m_eventIssuedCounter; } connectome-workbench-1.2.3+git41-gc4c6c90/src/Common/EventManager.h000066400000000000000000000110341300200146000245200ustar00rootroot00000000000000#ifndef __EVENT_MANAGER_H__ #define __EVENT_MANAGER_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 "CaretObject.h" #include "EventTypeEnum.h" //#define CONTAINER_VECTOR 1 //#define CONTAINER_HASH_SET 1 #define CONTAINER_SET 1 #ifdef CONTAINER_VECTOR #include #elif CONTAINER_HASH_SET #include #include "CaretHashSet.h" #elif CONTAINER_SET #include #else INTENTIONAL_COMPILER_ERROR_MISSING_CONTAINER_TYPE #endif namespace caret { class Event; class EventListenerInterface; #ifdef CONTAINER_HASH_SET class EventListenerCompareHash { public: bool operator()(const EventListenerInterface* e1, const EventListenerInterface* e2) const { return (e1 == e2); } }; class EventListenerInterfaceHash { public: size_t operator()(const EventListenerInterface* p) const { EventListenerInterface* el = const_cast(p); return reinterpret_cast((void*)el); } }; #endif // CONTAINER_HASH_SET class EventManager : public CaretObject { public: static void createEventManager(); static void deleteEventManager(); static EventManager* get(); void addEventListener(EventListenerInterface* eventListener, const EventTypeEnum::Enum listenForEventType); void addProcessedEventListener(EventListenerInterface* eventListener, const EventTypeEnum::Enum listenForEventType); void removeEventFromListener(EventListenerInterface* eventListener, const EventTypeEnum::Enum listenForEventType); void removeAllEventsFromListener(EventListenerInterface* eventListener); void sendEvent(Event* event); void sendSimpleEvent(const EventTypeEnum::Enum eventType); void blockEvent(const EventTypeEnum::Enum eventToBlock, const bool blockStatus); int64_t getEventIssuedCounter() const; private: EventManager(); virtual ~EventManager(); /** * Define the container */ #ifdef CONTAINER_VECTOR typedef std::vector EVENT_LISTENER_CONTAINER; #elif CONTAINER_HASH_SET typedef caret::hash_set EVENT_LISTENER_CONTAINER; #elif CONTAINER_SET typedef std::set EVENT_LISTENER_CONTAINER; #else INTENTIONAL_COMPILER_ERROR_MISSING_CONTAINER_TYPE #endif /** * Iterator for the container */ typedef EVENT_LISTENER_CONTAINER::iterator EVENT_LISTENER_CONTAINER_ITERATOR; /** * The event listeners */ EVENT_LISTENER_CONTAINER m_eventListeners[EventTypeEnum::EVENT_COUNT]; /** * Special listeners that are notified AFTER the eventListeners */ EVENT_LISTENER_CONTAINER m_eventProcessedListeners[EventTypeEnum::EVENT_COUNT]; /** Counter that is incremented each time an event is issued */ int64_t m_eventIssuedCounter; /** A counter for blocking events of each type */ std::vector m_eventBlockingCounter; static EventManager* s_singletonEventManager; }; #ifdef __EVENT_MANAGER_MAIN__ EventManager* EventManager::s_singletonEventManager = NULL; #endif // __EVENT_MANAGER_MAIN__ } // namespace #endif // __EVENT_MANAGER_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Common/EventOpenGLTexture.cxx000066400000000000000000000060401300200146000262270ustar00rootroot00000000000000 /*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; } connectome-workbench-1.2.3+git41-gc4c6c90/src/Common/EventOpenGLTexture.h000066400000000000000000000042601300200146000256560ustar00rootroot00000000000000#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__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Common/EventPaletteGetByName.cxx000066400000000000000000000041741300200146000266620ustar00rootroot00000000000000 /*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_PALETTE_GET_BY_NAME_DECLARE__ #include "EventPaletteGetByName.h" #undef __EVENT_PALETTE_GET_BY_NAME_DECLARE__ #include "CaretAssert.h" #include "CaretLogger.h" #include "EventTypeEnum.h" using namespace caret; /** * \class caret::EventPaletteGetByName * \brief Find a palette by name * \ingroup Common */ /** * Constructor. */ EventPaletteGetByName::EventPaletteGetByName(const AString& paletteName) : Event(EventTypeEnum::EVENT_PALETTE_GET_BY_NAME), m_paletteName(paletteName), m_palette(NULL) { } /** * Destructor. */ EventPaletteGetByName::~EventPaletteGetByName() { } /** * @return Name of desired palette. */ AString EventPaletteGetByName::getPaletteName() const { return m_paletteName; } /** * @return Palette that was found (NULL if no matching palette was found). */ Palette* EventPaletteGetByName::getPalette() const { return m_palette; } /** * Set the palette that matches by name. * * @param palette * Palette that matches name of desired palette. */ void EventPaletteGetByName::setPalette(Palette* palette) { CaretAssert(palette); if (m_palette != NULL) { CaretLogWarning("There appears to be more than one palette with name \"" + m_paletteName + "\""); } m_palette = palette; } connectome-workbench-1.2.3+git41-gc4c6c90/src/Common/EventPaletteGetByName.h000066400000000000000000000034501300200146000263030ustar00rootroot00000000000000#ifndef __EVENT_PALETTE_GET_BY_NAME_H__ #define __EVENT_PALETTE_GET_BY_NAME_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 Palette; class EventPaletteGetByName : public Event { public: EventPaletteGetByName(const AString& paletteName); virtual ~EventPaletteGetByName(); AString getPaletteName() const; Palette* getPalette() const; void setPalette(Palette* palette); // ADD_NEW_METHODS_HERE private: EventPaletteGetByName(const EventPaletteGetByName&); EventPaletteGetByName& operator=(const EventPaletteGetByName&); const AString m_paletteName; Palette* m_palette; // ADD_NEW_MEMBERS_HERE }; #ifdef __EVENT_PALETTE_GET_BY_NAME_DECLARE__ // #endif // __EVENT_PALETTE_GET_BY_NAME_DECLARE__ } // namespace #endif //__EVENT_PALETTE_GET_BY_NAME_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Common/EventProgressUpdate.cxx000066400000000000000000000075041300200146000264770ustar00rootroot00000000000000/*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 "EventProgressUpdate.h" using namespace caret; /** * \class caret::EventProgressUpdate * \brief Event for updating progress of a task in a progress dialog. */ EventProgressUpdate::EventProgressUpdate(ProgressObject* myObject): Event(EventTypeEnum::EVENT_PROGRESS_UPDATE), m_minimumProgressValue(0), m_maximumProgressValue(100), m_progressValue(0), m_progressMessage(""), m_cancelled(false) { m_amountUpdate = false; m_finished = false; m_textUpdate = false; m_starting = false; m_whichObject = myObject; } /** * Default constructor. * * If this instance is not modified and sent to a progress dialog * no updates will be made to the progress dialog. However, if the * user has cancelled the task, the cancel status will be set in * this instance after returning from the dialog. */ EventProgressUpdate::EventProgressUpdate() : Event(EventTypeEnum::EVENT_PROGRESS_UPDATE), m_minimumProgressValue(-1), m_maximumProgressValue(-1), m_progressValue(-1), m_progressMessage(""), m_cancelled(false) { /* nothing */ } /* * Constructor for display of progress in a progress dialog. * * @param minimumProgressValue * Minimum Progress. * @param maximumProgressValue * Maximum progress. * @param progressValue * Current progress (min <= current <= maximum) * @param progressMessage * Message for display in progress dialog. * */ EventProgressUpdate::EventProgressUpdate(const int minimumProgressValue, const int maximumProgressValue, const int progressValue, const QString& progressMessage) : Event(EventTypeEnum::EVENT_PROGRESS_UPDATE), m_minimumProgressValue(minimumProgressValue), m_maximumProgressValue(maximumProgressValue), m_progressValue(progressValue), m_progressMessage(progressMessage), m_cancelled(false) { } EventProgressUpdate::EventProgressUpdate(const QString& progressMessage) : Event(EventTypeEnum::EVENT_PROGRESS_UPDATE), m_minimumProgressValue(-1), m_maximumProgressValue(-1), m_progressValue(-1), m_progressMessage(progressMessage), m_cancelled(false) { } /** * Destructor. */ EventProgressUpdate::~EventProgressUpdate() { } /* * Set values for display of progress in a progress dialog. This method * allows a progress event to be reused and avoid reallocation, particularly * when updates are frequent. * * @param progressValue * Current progress (min <= current <= maximum) * @param progressMessage * Message for display in progress dialog. * */ void EventProgressUpdate::setProgress(const int progressValue, const QString& progressMessage) { m_progressValue = progressValue; m_progressMessage = progressMessage; } /* * Update only the progress message in the progress dialog. * * @param progressMessage * Message for display in progress dialog. * */ void EventProgressUpdate::setProgressMessage(const QString& progressMessage) { m_progressMessage = progressMessage; } connectome-workbench-1.2.3+git41-gc4c6c90/src/Common/EventProgressUpdate.h000066400000000000000000000057321300200146000261250ustar00rootroot00000000000000#ifndef __EVENT_PROGRESS_UPDATE_H__ #define __EVENT_PROGRESS_UPDATE_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 ProgressObject; /// Event for updating the user-interface class EventProgressUpdate : public Event { public: EventProgressUpdate(); EventProgressUpdate(ProgressObject* myObject); EventProgressUpdate(const int minimumProgressValue, const int maximumProgressValue, const int progressValue, const QString& progressMessage); EventProgressUpdate(const QString& progressMessage); virtual ~EventProgressUpdate(); void setProgress(const int progressValue, const QString& progressMessage); void setProgressMessage(const QString& progressMessage); bool m_textUpdate, m_amountUpdate, m_finished, m_starting; ProgressObject* m_whichObject;//idea is for progress elements to check whether their object emitted this event or not, if not, ignore /** @return Did the user request cancellation of the task */ bool isCancelled() const { return m_cancelled; } /** @return Minimum progress value. */ int getMinimumProgressValue() const { return m_minimumProgressValue; } /** @return Maximum progress value */ int getMaximumProgressValue() const { return m_maximumProgressValue; } /** @return Current value of progress */ int getProgressValue() const { return m_progressValue; } /** @return Message displayed describing progress */ QString getProgressMessage() const { return m_progressMessage; } /** Request cancellation of the task */ void setCancelled() { m_cancelled = true; } private: EventProgressUpdate(const EventProgressUpdate&); EventProgressUpdate& operator=(const EventProgressUpdate&); int m_minimumProgressValue; int m_maximumProgressValue; int m_progressValue; QString m_progressMessage; bool m_cancelled; }; } // namespace #endif // __EVENT_PROGRESS_UPDATE_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Common/EventTypeEnum.cxx000066400000000000000000000476731300200146000253110ustar00rootroot00000000000000/*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_TYPE_ENUM_DECLARE__ #include "EventTypeEnum.h" #undef __EVENT_TYPE_ENUM_DECLARE__ #include "CaretAssert.h" using namespace caret; /** * Constructor. * * @param enumValue * An enumerated value. * @param name * Name of enumberated value. */ EventTypeEnum::EventTypeEnum(const Enum enumValue, const AString& name, const AString& guiName) { this->enumValue = enumValue; this->name = name; this->guiName = guiName; } /** * Destructor. */ EventTypeEnum::~EventTypeEnum() { } /** * Initialize the enumerated metadata. */ void EventTypeEnum::initialize() { if (initializedFlag) { return; } initializedFlag = true; enumData.push_back(EventTypeEnum(EVENT_INVALID, "EVENT_INVALID", "Invalid Event")); enumData.push_back(EventTypeEnum(EVENT_ALERT_USER, "EVENT_ALERT_USER", "Alert user about something (if gui, a pop is displayed, otherwise logged at severe level")); 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_COLOR_BAR_GET, "EVENT_ANNOTATION_COLOR_BAR_GET", "Event to get annotation color bars from tab(s)")); enumData.push_back(EventTypeEnum(EVENT_ANNOTATION_CREATE_NEW_TYPE, "EVENT_ANNOTATION_CREATE_NEW_TYPE", "Event to create a new annotation of a particular type")); enumData.push_back(EventTypeEnum(EVENT_ANNOTATION_GET_DRAWN_IN_WINDOW, "EVENT_ANNOTATION_GET_DRAWN_IN_WINDOW", "Event for getting annotations drawn in a window")); enumData.push_back(EventTypeEnum(EVENT_ANNOTATION_GROUP_GET_WITH_KEY, "EVENT_ANNOTATION_GROUP_GET_WITH_KEY", "Event for getting an annotation group using its key")); enumData.push_back(EventTypeEnum(EVENT_ANNOTATION_GROUPING, "EVENT_ANNOTATION_GROUPING", "Event for annotation grouping")); enumData.push_back(EventTypeEnum(EVENT_ANNOTATION_TOOLBAR_UPDATE, "EVENT_ANNOTATION_TOOLBAR_UPDATE", "Event to update annotation toolbar")); enumData.push_back(EventTypeEnum(EVENT_BRAIN_RESET, "EVENT_BRAIN_RESET", "Brain has been reset")); enumData.push_back(EventTypeEnum(EVENT_BRAIN_STRUCTURE_GET_ALL, "EVENT_BRAIN_STRUCTURE_GET_ALL", "Get all brain structures")); enumData.push_back(EventTypeEnum(EVENT_BROWSER_TAB_DELETE, "EVENT_BROWSER_TAB_DELETE", "Delete a browser tab")); enumData.push_back(EventTypeEnum(EVENT_BROWSER_TAB_GET, "EVENT_BROWSER_TAB_GET", "Get a browser tab by number")); enumData.push_back(EventTypeEnum(EVENT_BROWSER_TAB_GET_ALL, "EVENT_BROWSER_TAB_GET_ALL", "Get ALL browser tabs")); enumData.push_back(EventTypeEnum(EVENT_BROWSER_TAB_GET_ALL_VIEWED, "EVENT_BROWSER_TAB_GET_ALL_VIEWED", "Get ALL Viewed browser tabs")); enumData.push_back(EventTypeEnum(EVENT_BROWSER_TAB_INDICES_GET_ALL, "EVENT_BROWSER_TAB_INDICES_GET_ALL", "Browser Tab Indices Get All")); enumData.push_back(EventTypeEnum(EVENT_BROWSER_TAB_NEW, "EVENT_BROWSER_TAB_NEW", "Create a browser tab")); enumData.push_back(EventTypeEnum(EVENT_BROWSER_WINDOW_CONTENT_GET, "EVENT_BROWSER_WINDOW_CONTENT_GET", "Get the content in a browser window")); enumData.push_back(EventTypeEnum(EVENT_BROWSER_WINDOW_CREATE_TABS, "EVENT_BROWSER_WINDOW_CREATE_TABS", "Create tabs (if needed) after loading data files")); enumData.push_back(EventTypeEnum(EVENT_BROWSER_WINDOW_GRAPHICS_HAVE_BEEN_REDRAWN, "EVENT_BROWSER_WINDOW_GRAPHICS_HAVE_BEEN_REDRAWN", "A Browser Window's graphics have been redrawn")); enumData.push_back(EventTypeEnum(EVENT_BROWSER_WINDOW_MENUS_UPDATE, "EVENT_BROWSER_WINDOW_MENUS_UPDATE", "Update the browser windows menus")); enumData.push_back(EventTypeEnum(EVENT_BROWSER_WINDOW_NEW, "EVENT_BROWSER_WINDOW_NEW", "Create a new browser window")); 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_CHART_MATRIX_YOKING_VALIDATION, "EVENT_CHART_MATRIX_YOKING_VALIDATION", "Validate Yoking of matrix chart's rows/columns")); enumData.push_back(EventTypeEnum(EVENT_DATA_FILE_ADD, "EVENT_DATA_FILE_ADD", "Add a data file to the Brain")); enumData.push_back(EventTypeEnum(EVENT_DATA_FILE_DELETE, "EVENT_DATA_FILE_DELETE", "Delete a data file from the Brain")); enumData.push_back(EventTypeEnum(EVENT_DATA_FILE_READ, "EVENT_DATA_FILE_READ", "Read a data file into the Brain")); enumData.push_back(EventTypeEnum(EVENT_DATA_FILE_RELOAD, "EVENT_DATA_FILE_RELOAD", "Reopen a data file (replace it with saved version) in the Brain")); enumData.push_back(EventTypeEnum(EVENT_GET_DISPLAYED_DATA_FILES, "EVENT_GET_DISPLAYED_DATA_FILES", "Get data files displayed in windows/tabs")); enumData.push_back(EventTypeEnum(EVENT_GET_NODE_DATA_FILES, "EVENT_GET_NODE_DATA_FILES", "Get node data files")); enumData.push_back(EventTypeEnum(EVENT_GET_OR_SET_USER_INPUT_MODE, "EVENT_GET_OR_SET_USER_INPUT_MODE", "Get or set the user input mode")); enumData.push_back(EventTypeEnum(EVENT_GET_TEXT_RENDERER_FOR_WINDOW, "EVENT_GET_TEXT_RENDERER_FOR_WINDOW", "Get the text renderer for a window")); enumData.push_back(EventTypeEnum(EVENT_GET_VIEWPORT_SIZE, "EVENT_GET_VIEWPORT_SIZE", "Get the viewport size")); enumData.push_back(EventTypeEnum(EVENT_GRAPHICS_UPDATE_ALL_WINDOWS, "EVENT_GRAPHICS_UPDATE_ALL_WINDOWS", "Update all graphics windows")); enumData.push_back(EventTypeEnum(EVENT_GRAPHICS_UPDATE_ONE_WINDOW, "EVENT_GRAPHICS_UPDATE_ONE_WINDOW", "Update graphics in one window")); enumData.push_back(EventTypeEnum(EVENT_HELP_VIEWER_DISPLAY, "EVENT_HELP_VIEWER_DISPLAY", "Display the help viewer")); enumData.push_back(EventTypeEnum(EVENT_IDENTIFICATION_HIGHLIGHT_LOCATION, "EVENT_IDENTIFICATION_HIGHLIGHT_LOCATION", "Highlight the location when identification takes place")); enumData.push_back(EventTypeEnum(EVENT_IDENTIFICATION_REQUEST, "EVENT_IDENTIFICATION_REQUEST", "Request an identification operation")); enumData.push_back(EventTypeEnum(EVENT_IDENTIFICATION_SYMBOL_REMOVAL, "EVENT_IDENTIFICATION_SYMBOL_REMOVAL", "Remove all identification symbols")); enumData.push_back(EventTypeEnum(EVENT_IMAGE_CAPTURE, "EVENT_IMAGE_CAPTURE", "Capture an Image of Browser Window Graphics Region")); enumData.push_back(EventTypeEnum(EVENT_MAC_DOCK_MENU_UPDATE, "EVENT_MAC_DOCK_MENU_UPDATE", "Update the Mac Dock Menu")); enumData.push_back(EventTypeEnum(EVENT_MAP_YOKING_SELECT_MAP, "EVENT_MAP_YOKING_SELECT_MAP", "Map Yoking Select Map")); enumData.push_back(EventTypeEnum(EVENT_MAP_YOKING_VALIDATION, "EVENT_MAP_YOKING_VALIDATION", "Map Yoking Validation")); enumData.push_back(EventTypeEnum(EVENT_MODEL_ADD, "EVENT_MODEL_ADD", "Add a model")); enumData.push_back(EventTypeEnum(EVENT_MODEL_DELETE, "EVENT_MODEL_DELETE", "Delete a model")); enumData.push_back(EventTypeEnum(EVENT_MODEL_GET_ALL, "EVENT_MODEL_GET_ALL", "Get all models")); enumData.push_back(EventTypeEnum(EVENT_MODEL_SURFACE_GET, "EVENT_MODEL_SURFACE_GET", "Get a specific model surface")); enumData.push_back(EventTypeEnum(EVENT_NODE_IDENTIFICATION_COLORS_GET_FROM_CHARTS, "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_OPERATING_SYSTEM_REQUEST_OPEN_DATA_FILE, "EVENT_OPERATING_SYSTEM_REQUEST_OPEN_DATA_FILE", "Operating system requests open data file (Mac only)")); enumData.push_back(EventTypeEnum(EVENT_OVERLAY_SETTINGS_EDITOR_SHOW, "EVENT_OVERLAY_SETTINGS_EDITOR_SHOW", "Request display of overlay settings editor")); enumData.push_back(EventTypeEnum(EVENT_OVERLAY_VALIDATE, "EVENT_OVERLAY_VALIDATE", "Validate an overlay for validity (it exists)")); enumData.push_back(EventTypeEnum(EVENT_PALETTE_COLOR_MAPPING_EDITOR_SHOW, "EVENT_PALETTE_COLOR_MAPPING_EDITOR_SHOW", "Request display of palette color mapping editor")); enumData.push_back(EventTypeEnum(EVENT_PALETTE_GET_BY_NAME, "EVENT_PALETTE_GET_BY_NAME", "Read the selected files in a spec file")); 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")); enumData.push_back(EventTypeEnum(EVENT_SURFACE_COLORING_INVALIDATE, "EVENT_SURFACE_COLORING_INVALIDATE", "Invalidate surface coloring")); enumData.push_back(EventTypeEnum(EVENT_SURFACES_GET, "EVENT_SURFACES_GET", "Get Surfaces")); enumData.push_back(EventTypeEnum(EVENT_SURFACE_STRUCTURES_VALID_GET, "EVENT_SURFACE_STRUCTURES_VALID_GET", "GGet valid surface strucutures and their number of node")); enumData.push_back(EventTypeEnum(EVENT_TOOLBOX_SELECTION_DISPLAY, "EVENT_TOOLBOX_SELECTION_DISPLAY", "Display or hide the selection toolbox")); enumData.push_back(EventTypeEnum(EVENT_USER_INTERFACE_UPDATE, "EVENT_USER_INTERFACE_UPDATE", "Update the user-interface")); enumData.push_back(EventTypeEnum(EVENT_PROGRESS_UPDATE, "EVENT_PROGRESS_UPDATE", "Update the progress amount, text, or finished status")); enumData.push_back(EventTypeEnum(EVENT_UPDATE_INFORMATION_WINDOWS, "EVENT_UPDATE_INFORMATION_WINDOWS", "Update the information windows")); enumData.push_back(EventTypeEnum(EVENT_UPDATE_YOKED_WINDOWS, "EVENT_UPDATE_YOKED_WINDOWS", "Update yoked windows graphics and toolbar")); enumData.push_back(EventTypeEnum(EVENT_UPDATE_VOLUME_EDITING_TOOLBAR, "EVENT_UPDATE_VOLUME_EDITING_TOOLBAR", "Update the volume editing toolbar")); enumData.push_back(EventTypeEnum(EVENT_COUNT, "EVENT_COUNT", "Count of events")); CaretAssertMessage((enumData.size() == static_cast(EVENT_COUNT + 1)), ("Number of EventTypeEnum::Enum values is incorrect.\n" "Have enumerated type been added?\n" "enumData.size()=" + AString::number(enumData.size()) + " EVENT_COUNT+1=" + AString::number(EVENT_COUNT + 1))); } /** * 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 EventTypeEnum* EventTypeEnum::findData(const Enum enumValue) { if (initializedFlag == false) initialize(); size_t num = enumData.size(); for (size_t i = 0; i < num; i++) { const EventTypeEnum* 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 EventTypeEnum::toName(Enum enumValue) { if (initializedFlag == false) initialize(); const EventTypeEnum* 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. */ EventTypeEnum::Enum EventTypeEnum::fromName(const AString& name, bool* isValidOut) { if (initializedFlag == false) initialize(); bool validFlag = false; Enum enumValue = EVENT_INVALID; for (std::vector::iterator iter = enumData.begin(); iter != enumData.end(); iter++) { const EventTypeEnum& 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 EventTypeEnum")); } return enumValue; } /** * Get a GUI string representation of the enumerated type. * @param enumValue * Enumerated value. * @return * String representing enumerated value. */ AString EventTypeEnum::toGuiName(Enum enumValue) { if (initializedFlag == false) initialize(); const EventTypeEnum* 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. */ EventTypeEnum::Enum EventTypeEnum::fromGuiName(const AString& guiName, bool* isValidOut) { if (initializedFlag == false) initialize(); bool validFlag = false; Enum enumValue = EVENT_INVALID; for (std::vector::iterator iter = enumData.begin(); iter != enumData.end(); iter++) { const EventTypeEnum& 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 EventTypeEnum")); } 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 EventTypeEnum::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); } } connectome-workbench-1.2.3+git41-gc4c6c90/src/Common/EventTypeEnum.h000066400000000000000000000204451300200146000247220ustar00rootroot00000000000000#ifndef __EVENT_TYPE_ENUM__H_ #define __EVENT_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 { /// Enumerated type for events. class EventTypeEnum { public: /** * Enumerated values. */ enum Enum { /** Invalid event */ EVENT_INVALID, /** Alert user about something */ EVENT_ALERT_USER, /** Add annotation to or remove from a file */ EVENT_ANNOTATION_ADD_TO_REMOVE_FROM_FILE, /** Get color bars from tab(s) */ EVENT_ANNOTATION_COLOR_BAR_GET, /** Annotation create new of a particular type */ EVENT_ANNOTATION_CREATE_NEW_TYPE, /** Get the annotations drawn in a window */ EVENT_ANNOTATION_GET_DRAWN_IN_WINDOW, /** Get an annotation group */ EVENT_ANNOTATION_GROUP_GET_WITH_KEY, /** Annotation grouping (group, regroup, ungroup) operation */ EVENT_ANNOTATION_GROUPING, /** Annotation toolbar update */ EVENT_ANNOTATION_TOOLBAR_UPDATE, /** Inform that Brain has been reset (new spec or scene loaded) */ EVENT_BRAIN_RESET, /** Get all brain structures */ EVENT_BRAIN_STRUCTURE_GET_ALL, /** Delete a browser tab. */ EVENT_BROWSER_TAB_DELETE, /** Get a browser tab by tab number */ EVENT_BROWSER_TAB_GET, /** Get indices of all valid browser tabs */ EVENT_BROWSER_TAB_INDICES_GET_ALL, /** Get ALL (both viewed and not viewed) browser tabs */ EVENT_BROWSER_TAB_GET_ALL, /** Get ALL VIEWED browser tabs (tabs that are viewed in windows) */ EVENT_BROWSER_TAB_GET_ALL_VIEWED, /** Create a new browser tab */ EVENT_BROWSER_TAB_NEW, /** Get the content of a browser window */ EVENT_BROWSER_WINDOW_CONTENT_GET, /** Create tabs after loading a file */ EVENT_BROWSER_WINDOW_CREATE_TABS, /** Issued after a browser window's graphicshave been redrawn */ EVENT_BROWSER_WINDOW_GRAPHICS_HAVE_BEEN_REDRAWN, /** Issued when displayed browser window menu's may change */ EVENT_BROWSER_WINDOW_MENUS_UPDATE, /** Create a new browser window */ EVENT_BROWSER_WINDOW_NEW, /** 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 for yoking the loading of matrix chart rows/columns */ EVENT_CHART_MATRIX_YOKING_VALIDATION, /** Add a data file into the Brain*/ EVENT_DATA_FILE_ADD, /** Delete a data file from the brain */ EVENT_DATA_FILE_DELETE, /** Read a data file into the Brain */ EVENT_DATA_FILE_READ, /** Reload (replace) a data file with its saved version in the brain*/ EVENT_DATA_FILE_RELOAD, /** Get data files that are display in windows/tabs */ EVENT_GET_DISPLAYED_DATA_FILES, /** Get node data files */ EVENT_GET_NODE_DATA_FILES, /** get or set the user input mode */ EVENT_GET_OR_SET_USER_INPUT_MODE, /** Get the text renderer for a window */ EVENT_GET_TEXT_RENDERER_FOR_WINDOW, /** Get the viewport size for model, tab, window */ EVENT_GET_VIEWPORT_SIZE, /** Update all graphics windows */ EVENT_GRAPHICS_UPDATE_ALL_WINDOWS, /** Update graphics in a window */ EVENT_GRAPHICS_UPDATE_ONE_WINDOW, /** Display the help viewer */ EVENT_HELP_VIEWER_DISPLAY, /** Highlight location when an identification occurs */ EVENT_IDENTIFICATION_HIGHLIGHT_LOCATION, /** Perform an identification operation */ EVENT_IDENTIFICATION_REQUEST, /** Remove all identification symbols */ EVENT_IDENTIFICATION_SYMBOL_REMOVAL, /** Browser window image capture */ EVENT_IMAGE_CAPTURE, /** Update the Mac Dock Menu */ EVENT_MAC_DOCK_MENU_UPDATE, /** Validate when adding a mapped file to mapped yoking */ EVENT_MAP_YOKING_SELECT_MAP, /** Select a map for mapped yoked files */ EVENT_MAP_YOKING_VALIDATION, /** model - ADD */ EVENT_MODEL_ADD, /** model - DELETE */ EVENT_MODEL_DELETE, /** model - get all*/ EVENT_MODEL_GET_ALL, /** 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, /** open file request from the operating system (Mac only) for now */ EVENT_OPERATING_SYSTEM_REQUEST_OPEN_DATA_FILE, /** request display of overlay settings editor */ EVENT_OVERLAY_SETTINGS_EDITOR_SHOW, /** Validate that overlay is valid (it exists). */ EVENT_OVERLAY_VALIDATE, /** request display of palette color mapping editor */ EVENT_PALETTE_COLOR_MAPPING_EDITOR_SHOW, /** Get a palette by name from a palette file */ EVENT_PALETTE_GET_BY_NAME, /** Read the selected files in a spec file */ EVENT_SPEC_FILE_READ_DATA_FILES, /** Invalidate surface coloring */ EVENT_SURFACE_COLORING_INVALIDATE, /** Get surfaces */ EVENT_SURFACES_GET, /** Get valid surface strucutures and their number of nodes */ EVENT_SURFACE_STRUCTURES_VALID_GET, /** Display/Hide the selection toolbox */ EVENT_TOOLBOX_SELECTION_DISPLAY, /** Update the User-Interface */ EVENT_USER_INTERFACE_UPDATE, /** Update the progress amount, text, or finished status */ 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, /* THIS MUST ALWAYS BE LAST - NOT an event type but is number of event types */ EVENT_COUNT }; ~EventTypeEnum(); 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 void getAllEnums(std::vector& allEnums); private: EventTypeEnum(const Enum enumValue, const AString& name, const AString& guiName); static const EventTypeEnum* 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; /** The enumerated type value for an instance */ Enum enumValue; /** 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 __EVENT_TYPE_ENUM_DECLARE__ std::vector EventTypeEnum::enumData; bool EventTypeEnum::initializedFlag = false; #endif // __EVENT_TYPE_ENUM_DECLARE__ } // namespace #endif //__EVENT_TYPE_ENUM__H_ connectome-workbench-1.2.3+git41-gc4c6c90/src/Common/FastStatistics.cxx000066400000000000000000000515451300200146000255020ustar00rootroot00000000000000 /*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 "FastStatistics.h" #include "CaretPointer.h" #include #include #include using namespace caret; using namespace std; const int64_t NUM_BUCKETS_PERCENTILE_HIST = 10000;//10,000 maximum to deal with some outliers outliers until I think of a better fix FastStatistics::FastStatistics() { reset(); } FastStatistics::FastStatistics(const float* data, const int64_t& dataCount) { update(data, dataCount); } void FastStatistics::reset() { m_posCount = 0; m_zeroCount = 0; m_negCount = 0; m_infCount = 0; m_negInfCount = 0; m_nanCount = 0; m_absCount = 0; m_mean = 0.0f; m_stdDevPop = 0.0f; m_stdDevSample = 0.0f; m_mostNeg = 0.0f; m_leastNeg = -numeric_limits::max(); m_leastPos = numeric_limits::max(); m_mostPos = 0.0f; m_leastAbs = numeric_limits::max(); m_mostAbs = 0.0; m_min = 0.0f; m_max = 0.0f; } void FastStatistics::update(const float* data, const int64_t& dataCount) { reset(); CaretArray positives(dataCount), negatives(dataCount), absolutes(dataCount); double sum = 0.0;//for numerical stability bool first = true;//so min can be positive and max can be negative for (int64_t i = 0; i < dataCount; ++i) { if (data[i] != data[i]) { ++m_nanCount; continue;//skip NaNs } if (data[i] == 0.0f)//test exactly zero (negative zero also tests equal), in case someone wants stats on something with miniscule values (percent of surface area per node?) { ++m_zeroCount; } else { if (data[i] < 0.0f) { if (data[i] * 2.0f == data[i]) { ++m_negInfCount; continue;//skip neg infs } else { negatives[m_negCount] = data[i]; ++m_negCount; if (data[i] > m_leastNeg) m_leastNeg = data[i]; if (data[i] < m_mostNeg) m_mostNeg = data[i]; absolutes[m_absCount] = -data[i]; if (absolutes[m_absCount] > m_mostAbs) m_mostAbs = absolutes[m_absCount]; if (absolutes[m_absCount] < m_leastAbs) m_leastAbs = absolutes[m_absCount]; ++m_absCount; } } else { if (data[i] * 2.0f == data[i]) { ++m_infCount; continue;//skip infs } else { positives[m_posCount] = data[i]; ++m_posCount; if (data[i] > m_mostPos) m_mostPos = data[i]; if (data[i] < m_leastPos) m_leastPos = data[i]; absolutes[m_absCount] = data[i]; if (absolutes[m_absCount] > m_mostAbs) m_mostAbs = absolutes[m_absCount]; if (absolutes[m_absCount] < m_leastAbs) m_leastAbs = absolutes[m_absCount]; ++m_absCount; } } } if (data[i] > m_max || first) m_max = data[i]; if (data[i] < m_min || first) m_min = data[i]; sum += data[i];//use a two-pass method for stability, only do mean this pass first = false; } int64_t totalGood = (m_negCount + m_zeroCount + m_posCount); m_mean = sum / totalGood; float tempf; double sum2 = 0.0; for (int64_t i = 0; i < dataCount; ++i) { if (data[i] != data[i]) continue;//skip NaNs if (data[i] < -1.0f && (data[i] * 2.0f == data[i])) continue;//exclude -inf if (data[i] > 1.0f && (data[i] * 2.0f == data[i])) continue;//exclude inf tempf = data[i] - m_mean; sum2 += tempf * tempf; } if (totalGood > 0) { m_stdDevPop = sqrt(sum2 / totalGood); if (totalGood > 1) { m_stdDevSample = sqrt(sum2 / (totalGood - 1)); } } int usebuckets = min(NUM_BUCKETS_PERCENTILE_HIST, dataCount); m_negPercentHist.update(usebuckets, negatives, m_negCount); m_posPercentHist.update(usebuckets, positives, m_posCount); m_absPercentHist.update(usebuckets, absolutes, m_absCount); if (m_negCount <= 0) { m_leastNeg = 0.0; m_mostNeg = 0.0; } if (m_posCount <= 0) { m_leastPos = 0.0; m_mostPos = 0.0; } if (m_absCount <= 0) { m_leastAbs = 0.0; m_mostAbs = 0.0; } } void FastStatistics::update(const float* data, const int64_t& dataCount, const float& minThreshInclusive, const float& maxThreshInclusive) { reset(); CaretArray positives(dataCount), negatives(dataCount), absolutes(dataCount); double sum = 0.0;//for numerical stability bool first = true;//so min can be positive and max can be negative for (int64_t i = 0; i < dataCount; ++i) { if (data[i] != data[i]) { ++m_nanCount; continue;//skip NaNs } if (data[i] < -1.0f && (data[i] * 2.0f == data[i])) { ++m_negInfCount; continue;//skip and count all infs, ignoring the range for now } if (data[i] > 1.0f && (data[i] * 2.0f == data[i])) { ++m_infCount; continue;//ditto } if (data[i] < minThreshInclusive || data[i] > maxThreshInclusive) {//we now have only numerical values continue;//skip them if they are outside the range } if (data[i] == 0.0f)//test exactly zero (negative zero also tests equal), in case someone wants stats on something with miniscule values (percent of surface area per node?) { ++m_zeroCount; } else { if (data[i] < 0.0f) { negatives[m_negCount] = data[i]; ++m_negCount; if (data[i] > m_leastNeg) m_leastNeg = data[i]; if (data[i] < m_mostNeg) m_mostNeg = data[i]; absolutes[m_absCount] = -data[i]; ++m_absCount; } else { positives[m_posCount] = data[i]; ++m_posCount; if (data[i] > m_mostPos) m_mostPos = data[i]; if (data[i] < m_leastPos) m_leastPos = data[i]; absolutes[m_absCount] = data[i]; ++m_absCount; } } if (data[i] > m_max || first) m_max = data[i]; if (data[i] < m_min || first) m_min = data[i]; sum += data[i];//use a two-pass method for stability, only do mean this pass first = false; } int64_t totalGood = (m_negCount + m_zeroCount + m_posCount); m_mean = sum / totalGood; float tempf; double sum2 = 0.0; for (int64_t i = 0; i < dataCount; ++i) { if (data[i] != data[i]) continue;//skip NaNs if (data[i] < -1.0f && (data[i] * 2.0f == data[i])) continue;//exclude -inf if (data[i] > 1.0f && (data[i] * 2.0f == data[i])) continue;//exclude inf tempf = data[i] - m_mean; sum2 += tempf * tempf; } if (totalGood > 0) { m_stdDevPop = sqrt(sum2 / totalGood); if (totalGood > 1) { m_stdDevSample = sqrt(sum2 / (totalGood - 1)); } } int usebuckets = min(NUM_BUCKETS_PERCENTILE_HIST, dataCount); m_negPercentHist.update(usebuckets, negatives, m_negCount);//10,000 will probably allow us to approximate the percentiles pretty closely, and eats only 80K of memory each m_posPercentHist.update(usebuckets, positives, m_posCount); m_absPercentHist.update(usebuckets, absolutes, m_absCount); if (m_negCount <= 0) { m_leastNeg = 0.0; m_mostNeg = 0.0; } if (m_posCount <= 0) { m_leastPos = 0.0; m_mostPos = 0.0; } if (m_absCount <= 0) { m_leastAbs = 0.0; m_mostAbs = 0.0; } } float FastStatistics::getApproxNegativePercentile(const float& percent) const { float rank = percent / 100.0f * m_negCount;//translate to rank rank = m_negCount - rank;//reverse it because negatives go the other direction, histogram is strictly directional towards positive if (rank <= 0) return m_mostNeg; if (rank >= m_negCount) return m_leastNeg; float histMin, histMax; m_negPercentHist.getRange(histMin, histMax); const vector& cumulative = m_negPercentHist.getHistogramCumulativeCounts(); int numBuckets = (int)cumulative.size(); int lowBound = -1, highBound = numBuckets, guess;//bisection search, "index" -1 is implicitly valued zero while (highBound - lowBound > 1) { guess = (lowBound + highBound) / 2; if (cumulative[guess] <= rank) { lowBound = guess; } else { highBound = guess; } } if (highBound == numBuckets) return m_mostPos;//the count mismatched the histogram somehow float bucketsize = (histMax - histMin) / numBuckets; int64_t curLower, curUpper = cumulative[highBound]; if (lowBound > -1) { curLower = cumulative[lowBound]; } else { curLower = 0; } if (highBound > 0 && ((highBound == 1 && cumulative[0] == 0) || (highBound > 1 && cumulative[highBound - 1] == cumulative[highBound - 2]))) {//tweak the function a bit if there is a bin that collected zero to the immediate left, to reduce discontinuities if (rank - curLower >= 1.0f) { ++curLower;//tweak the low end to start from one higher, to make it continuous with the tweak below } else { --highBound;//move left, because this interpolated rank doesn't fall within the highBound bucket --lowBound; curUpper = curLower + 1;//add one to the right end of the flat spot to give it nonzero slope while (lowBound > 0 && cumulative[highBound] == cumulative[lowBound - 1]) { --lowBound;//slide left boundary over the flat spot } if (lowBound == 0 && cumulative[lowBound] == 0) { --lowBound;//including if first bucket is zero, this shouldn't happen unless all valid values are equal (and a low count of values) } if (lowBound > -1) { curLower = cumulative[lowBound]; } else { curLower = 0; } } } float lowValue = histMin + (lowBound + 1) * bucketsize, highValue = histMin + (highBound + 1) * bucketsize; return lowValue + (highValue - lowValue) * (rank - curLower) / (curUpper - curLower); } float FastStatistics::getApproxPositivePercentile(const float& percent) const { float rank = percent / 100.0f * m_posCount;//translate to rank if (rank <= 0.0f) return m_leastPos; if (rank >= m_posCount) return m_mostPos; float histMin, histMax; m_posPercentHist.getRange(histMin, histMax); const vector& cumulative = m_posPercentHist.getHistogramCumulativeCounts(); int numBuckets = (int)cumulative.size(); int lowBound = -1, highBound = numBuckets, guess;//bisection search, "index" -1 is implicitly valued zero while (highBound - lowBound > 1) { guess = (lowBound + highBound) / 2; if (cumulative[guess] <= rank) { lowBound = guess; } else { highBound = guess; } } if (highBound == numBuckets) return m_mostPos;//the count mismatched the histogram somehow float bucketsize = (histMax - histMin) / numBuckets; int64_t curLower, curUpper = cumulative[highBound]; if (lowBound > -1) { curLower = cumulative[lowBound]; } else { curLower = 0; } if (highBound > 0 && ((highBound == 1 && cumulative[0] == 0) || (highBound > 1 && cumulative[highBound - 1] == cumulative[highBound - 2]))) {//tweak the function a bit if there is a bin that collected zero to the immediate left, to reduce discontinuities if (rank - curLower >= 1.0f) { ++curLower;//tweak the low end to start from one higher, to make it continuous with the tweak below } else { --highBound;//move left, because this interpolated rank doesn't fall within the highBound bucket --lowBound; curUpper = curLower + 1;//add one to the right end of the flat spot to give it nonzero slope while (lowBound > 0 && cumulative[highBound] == cumulative[lowBound - 1]) { --lowBound;//slide left boundary over the flat spot } if (lowBound == 0 && cumulative[lowBound] == 0) { --lowBound;//including if first bucket is zero, this shouldn't happen unless all valid values are equal (and a low count of values) } if (lowBound > -1) { curLower = cumulative[lowBound]; } else { curLower = 0; } } } float lowValue = histMin + (lowBound + 1) * bucketsize, highValue = histMin + (highBound + 1) * bucketsize; return lowValue + (highValue - lowValue) * (rank - curLower) / (curUpper - curLower); } float FastStatistics::getApproxAbsolutePercentile(const float& percent) const { float rank = percent / 100.0f * m_absCount;//translate to rank if (rank <= 0.0f) return m_leastAbs; if (rank >= m_absCount) return m_mostAbs; float histMin, histMax; m_absPercentHist.getRange(histMin, histMax); const vector& cumulative = m_absPercentHist.getHistogramCumulativeCounts(); int numBuckets = (int)cumulative.size(); int lowBound = -1, highBound = numBuckets, guess;//bisection search, "index" -1 is implicitly valued zero while (highBound - lowBound > 1) { guess = (lowBound + highBound) / 2; if (cumulative[guess] <= rank) { lowBound = guess; } else { highBound = guess; } } if (highBound == numBuckets) return m_mostAbs;//the count mismatched the histogram somehow float bucketsize = (histMax - histMin) / numBuckets; int64_t curLower, curUpper = cumulative[highBound]; if (lowBound > -1) { curLower = cumulative[lowBound]; } else { curLower = 0; } if (highBound > 0 && ((highBound == 1 && cumulative[0] == 0) || (highBound > 1 && cumulative[highBound - 1] == cumulative[highBound - 2]))) {//tweak the function a bit if there is a bin that collected zero to the immediate left, to reduce discontinuities if (rank - curLower >= 1.0f) { ++curLower;//tweak the low end to start from one higher, to make it continuous with the tweak below } else { --highBound;//move left, because this interpolated rank doesn't fall within the highBound bucket --lowBound; curUpper = curLower + 1;//add one to the right end of the flat spot to give it nonzero slope while (lowBound > 0 && cumulative[highBound] == cumulative[lowBound - 1]) { --lowBound;//slide left boundary over the flat spot } if (lowBound == 0 && cumulative[lowBound] == 0) { --lowBound;//including if first bucket is zero, this shouldn't happen unless all valid values are equal (and a low count of values) } if (lowBound > -1) { curLower = cumulative[lowBound]; } else { curLower = 0; } } } float lowValue = histMin + (lowBound + 1) * bucketsize, highValue = histMin + (highBound + 1) * bucketsize; return lowValue + (highValue - lowValue) * (rank - curLower) / (curUpper - curLower); } float FastStatistics::getApproximateMedian() const { int64_t totalGood = m_negCount + m_zeroCount + m_posCount; if (m_negCount > m_posCount) { if (m_zeroCount > (m_negCount - m_posCount)) { return 0.0f; } else { return getApproxNegativePercentile((m_negCount - m_posCount - m_zeroCount) * 50.0f / totalGood); } } else { if (m_zeroCount > (m_posCount - m_negCount)) { return 0.0f; } else { return getApproxNegativePercentile((m_posCount - m_negCount - m_zeroCount) * 50.0f / totalGood); } } } float FastStatistics::getValuePercentileHelper(const Histogram& histogram, const float numberOfDataValues, const bool negativeDataFlag, const float value) { float percentile = 0.0; const std::vector& cumulativeBuckets = histogram.getHistogramCumulativeCounts(); if ((numberOfDataValues > 0) && (! cumulativeBuckets.empty())) { float minValue = 0.0; float maxValue = 0.0; histogram.getRange(minValue, maxValue); if (value < minValue) { if (negativeDataFlag) { percentile = 100.0; } else { percentile = 0.0; } } else if (value > maxValue) { if (negativeDataFlag) { percentile = 0; } else { percentile = 100.0; } } else { const float histoRange = maxValue - minValue; if (histoRange > 0.0) { const int64_t numBuckets = cumulativeBuckets.size(); int64_t bucketIndex = static_cast(((value - minValue) / histoRange) * numBuckets); if (bucketIndex < 0) { bucketIndex = 0; } else if (bucketIndex >= numBuckets) { bucketIndex = numBuckets - 1; } CaretAssertVectorIndex(cumulativeBuckets, bucketIndex); float cumulativeValue = cumulativeBuckets[bucketIndex]; if (negativeDataFlag) { CaretAssertVectorIndex(cumulativeBuckets, numBuckets - 1); cumulativeValue = cumulativeBuckets[numBuckets - 1] - cumulativeBuckets[bucketIndex]; } percentile = (cumulativeValue / numberOfDataValues) * 100.0; } } } return percentile; } float FastStatistics::getNegativeValuePercentile(const float value) const { return getValuePercentileHelper(m_negPercentHist, m_negCount, true, value); } float FastStatistics::getAbsoluteValuePercentile(const float value) const { float dataValue = value; if (dataValue < 0.0) { dataValue = -dataValue; } return getValuePercentileHelper(m_absPercentHist, m_absCount, false, dataValue); } float FastStatistics::getPositiveValuePercentile(const float value) const { return getValuePercentileHelper(m_posPercentHist, m_posCount, false, value); // float percentile = 0.0; // // const std::vector& cumulativeBuckets = m_posPercentHist.getHistogramCumulativeCounts(); // if ( ! cumulativeBuckets.empty()) { // float minValue = 0.0; // float maxValue = 0.0; // m_posPercentHist.getRange(minValue, // maxValue); // if (value < minValue) { // percentile = 0.0; // } // else if (value > maxValue) { // percentile = 100.0; // } // else { // const float histoRange = maxValue - minValue; // if (histoRange > 0.0) { // const int64_t numBuckets = cumulativeBuckets.size(); // int64_t bucketIndex = static_cast(((value - minValue) / histoRange) * numBuckets); // if (bucketIndex < 0) { // bucketIndex = 0; // } // else if (bucketIndex >= numBuckets) { // bucketIndex = numBuckets - 1; // } // // CaretAssertVectorIndex(cumulativeBuckets, // bucketIndex); // const float cumulativeValue = cumulativeBuckets[bucketIndex]; // percentile = (cumulativeValue / m_posCount) * 100.0; // } // } // } // // return percentile; } connectome-workbench-1.2.3+git41-gc4c6c90/src/Common/FastStatistics.h000066400000000000000000000076511300200146000251260ustar00rootroot00000000000000#ifndef __FAST_STATISTICS_H__ #define __FAST_STATISTICS_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 "Histogram.h" namespace caret { ///this class does statistics that are linear in complexity only, NO SORTING, this means its percentiles are approximate, using interpolation from a histogram class FastStatistics { Histogram m_posPercentHist, m_negPercentHist, m_absPercentHist; float m_min, m_max, m_mean, m_stdDevPop, m_stdDevSample; float m_mostPos, m_leastPos, m_leastNeg, m_mostNeg, m_leastAbs, m_mostAbs; ///counts of each class of number int64_t m_posCount, m_zeroCount, m_negCount, m_infCount, m_negInfCount, m_nanCount, m_absCount; void reset(); static float getValuePercentileHelper(const Histogram& histogram, const float numberOfDataValues, const bool negativeDataFlag, const float value); public: FastStatistics(); FastStatistics(const float* data, const int64_t& dataCount); void update(const float* data, const int64_t& dataCount); ///statistics and display are really not that related, so for now, only include a continuous clipping range, excluding the middle from data will do weird things to standard deviation void update(const float* data, const int64_t& dataCount, const float& minThreshInclusive, const float& maxThreshInclusive); float getApproxPositivePercentile(const float& percent) const; float getApproxNegativePercentile(const float& percent) const; float getApproxAbsolutePercentile(const float& percent) const; void getCounts(int64_t& posCount, int64_t& zeroCount, int64_t& negCount, int64_t& infCount, int64_t& negInfCount, int64_t& nanCount) const { posCount = m_posCount; zeroCount = m_zeroCount; negCount = m_negCount; infCount = m_infCount; negInfCount = m_negInfCount; nanCount = m_nanCount; } void getNonzeroRanges(float& mostNegative, float& leastNegative, float& leastPositive, float& mostPositive) const { mostNegative = m_mostNeg; leastNegative = m_leastNeg; leastPositive = m_leastPos; mostPositive = m_mostPos; } float getMostNegativeValue() const { return m_mostNeg; } float getMostPositiveValue() const { return m_mostPos; } float getMin() const { return m_min; } float getMax() const { return m_max; } float getMean() const { return m_mean; } float getApproximateMedian() const; float getSampleStdDev() const { return m_stdDevSample; } float getPopulationStdDev() const { return m_stdDevPop; } float getPositiveValuePercentile(const float value) const; float getNegativeValuePercentile(const float value) const; float getAbsoluteValuePercentile(const float value) const; }; } #endif //__FAST_STATISTICS_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Common/FileAdapter.cxx000066400000000000000000000101441300200146000247000ustar00rootroot00000000000000 /*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 #define __FILE_ADAPTER_DECLARE__ #include "FileAdapter.h" #undef __FILE_ADAPTER_DECLARE__ #include "CaretAssert.h" #include "FileInformation.h" using namespace caret; /** * \class caret::FileAdapter * \brief Simplifies opening of files and streams. * * When an open method is called, the file is opened * in the appropriate mode, a stream is created, and * the stream is returned. When the destructor is * called it will take care of cleaning up any resources * and closing the file (if it has not already been * done by calling close(). * * If an instance of this class is created in a * try/catch block, create an instance of the class * statically (not NEW) so that if an exception is * thrown, the instance of this class will go out * of scope which results in the destructor being * called which cleans up resources and closes the * file. */ /** * Constructor. */ FileAdapter::FileAdapter() { m_file = NULL; m_textStream = NULL; } /** * */ FileAdapter::~FileAdapter() { close(); } /** * Open a file with the given name, create a QTextStream for * the file, and return the QTextStream. * * @filename * Name of file. * @errorMessagesOut * Contains information about errors if any occur. * @return * A pointer to the QTextStream that was created. DO NOT * destroy it or else disaster will likely occur. If this * returned value is NULL, it indicates that an error has * occurred and its description will be in the errorMessageOut * parameter. */ QTextStream* FileAdapter::openQTextStreamForWritingFile(const AString& filename, AString& errorMessageOut) { errorMessageOut = ""; if (m_file != NULL) { errorMessageOut = ("A file named " + m_file->fileName() + " is currently open with this FileAdapter"); return NULL; } if (m_file != NULL) { errorMessageOut = "This file is already open and has not been closed."; return NULL; } if (filename.isEmpty()) { errorMessageOut = "Filename contains no characters."; return NULL; } FileInformation fileInfo(filename); if (fileInfo.exists()) { if (fileInfo.isWritable() == false) { errorMessageOut = (filename + " exists but does not have writable permission."); return NULL; } } m_file = new QFile(filename); if (m_file->open(QFile::WriteOnly) == false) { errorMessageOut = ("Unable to open " + filename + " for writing: " + m_file->errorString()); delete m_file; m_file = NULL; return NULL; } m_textStream = new QTextStream(m_file); return m_textStream; } /** * If any streams are valid, they are deleted. * If the file is valid, its is flushed, closed * and deleted. */ void FileAdapter::close() { if (m_textStream != NULL) { m_textStream->flush(); delete m_textStream; m_textStream = NULL; } if (m_file != NULL) { m_file->flush(); m_file->close(); delete m_file; m_file = NULL; } } connectome-workbench-1.2.3+git41-gc4c6c90/src/Common/FileAdapter.h000066400000000000000000000030321300200146000243230ustar00rootroot00000000000000#ifndef __FILE_ADAPTER__H_ #define __FILE_ADAPTER__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" class QFile; class QTextStream; namespace caret { class FileAdapter : public CaretObject { public: FileAdapter(); ~FileAdapter(); QTextStream* openQTextStreamForWritingFile(const AString& filename, AString& errorMessageOut); void close(); private: QFile* m_file; QTextStream* m_textStream; }; #ifdef __FILE_ADAPTER_DECLARE__ // #endif // __FILE_ADAPTER_DECLARE__ } // namespace #endif //__FILE_ADAPTER__H_ connectome-workbench-1.2.3+git41-gc4c6c90/src/Common/FileInformation.cxx000066400000000000000000000455001300200146000256110ustar00rootroot00000000000000 /*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 __FILE_INFORMATION_DECLARE__ #include "FileInformation.h" #undef __FILE_INFORMATION_DECLARE__ #include "CaretLogger.h" #include "DataFile.h" #include "DataFileTypeEnum.h" using namespace caret; /** * \class caret::FileInformation * \brief Information about a file path. * \ingroup Common * * Provides information about a path (file, directory, etc). Support for * remote files is provided. Some of the methods are not appropriate for * remote files and the method's documentation indicates any limitations. */ /** * Constructor. * @param file * Name of path for which information is obtained. */ FileInformation::FileInformation(const AString& file) : CaretObject() { m_isLocalFile = false; m_isRemoteFile = false; if (DataFile::isFileOnNetwork(file)) { m_urlInfo.setUrl(file); m_isRemoteFile = true; } else { m_fileInfo.setFile(file); m_isLocalFile = true; } } /** * Constructor. * @param path * Directory containing the file. * @param file * Name of path for which information is obtained. */ FileInformation::FileInformation(const AString& path, const AString& file) : CaretObject() { m_isLocalFile = false; m_isRemoteFile = false; if (DataFile::isFileOnNetwork(file) || DataFile::isFileOnNetwork(path)) { AString pathCopy = path; if (pathCopy.endsWith("/") == false) { /* * With adding a trailing slash: * path: http://brainvis.wustl.edu/john/workbench * file: ParcellationPilot_AverageT1w.nii.gz * CORRECT: http://brainvis.wustl.edu/john/workbench/ParcellationPilot_AverageT1w.nii.gz * * Without adding the trailing slash "workbench" is chopped off * INCORRECT: http://brainvis.wustl.edu/john/ParcellationPilot_AverageT1w.nii.gz */ pathCopy += "/"; } QUrl baseUrl(pathCopy); QUrl relativeUrl(file); m_urlInfo = baseUrl.resolved(relativeUrl); m_isRemoteFile = true; CaretLogFine("Path: " + path + "\n File: " + file + "\n Becomes " + m_urlInfo.toString()); } else { m_fileInfo.setFile(path, file); /* * Clean up path to remove any ".." (up a directory level). * Note that canonicalFilePath() will return an empty string * if the path does not point to a valid file. * * TSC: NEVER USE CANONICAL FOR THIS! canonical resolves symlinks, which is NOT DESIRED unless explicitly asked for (by calling a function including the word "canonical"). * */ if (getAbsoluteFilePath().contains("..")) { AString cleanedPath = QDir::cleanPath(m_fileInfo.absolutePath()); if (!cleanedPath.endsWith('/')) cleanedPath += "/";//because "/" and "/usr" are both possible results cleanedPath += m_fileInfo.fileName(); if (cleanedPath.isEmpty() == false) { m_fileInfo.setFile(cleanedPath); } } m_isLocalFile = true; } } /** * Destructor. */ FileInformation::~FileInformation() { } /** * @return True if the file is a local file, else false. */ bool FileInformation::isLocalFile() const { return m_isLocalFile; } /** * @return True if the file is a remote file, else false. */ bool FileInformation::isRemoteFile() const { return m_isRemoteFile; } /** * @return Absolute path including the name of the file. * * some logic that seems to be missing from QFileInfo: if absolute, * return path() + file() rather than using system call. * * Note: A remote file returns the original, full URL. */ AString FileInformation::getAbsoluteFilePath() const { if (m_isRemoteFile) { return m_urlInfo.toString(); } if (m_fileInfo.isAbsolute()) { return m_fileInfo.filePath(); } else { return m_fileInfo.absoluteFilePath(); } } /** * Removes the file. * Remove files cannot be removed. * * @return * true if file deleted successfully. */ bool FileInformation::remove() { if (m_isRemoteFile) { CaretLogSevere("Deleting remote file is not allowed: " + m_urlInfo.toString()); return false; } bool result = false; if (m_fileInfo.exists()) { result = QFile::remove(m_fileInfo.absoluteFilePath()); } return result; } /** * @return true if it exists, else false. * * A remote file always returns true. */ bool FileInformation::exists() const { if (m_isRemoteFile) { return true; } return m_fileInfo.exists(); } /** * @return true if it is file, else false. * * A remote file always returns true. */ bool FileInformation::isFile() const { if (m_isRemoteFile) { return true; } return m_fileInfo.isFile(); } /** * @return true if it is directory, else false. * * A remote file always returns false. */ bool FileInformation::isDirectory() const { if (m_isRemoteFile) { return false; } return m_fileInfo.isDir(); } /** * @return true if it is symbolic link, else false.0 * * A remote file always returns false. */ bool FileInformation::isSymbolicLink() const { if (m_isRemoteFile) { return false; } return m_fileInfo.isSymLink(); } /** * @return true if it is readable, else false. * * A remote file always returns true. */ bool FileInformation::isReadable() const { if (m_isRemoteFile) { return true; } return m_fileInfo.isReadable(); } /** * @return true if it is writable, else false. * * A remote file always returns false. */ bool FileInformation::isWritable() const { if (m_isRemoteFile) { return false; } return m_fileInfo.isWritable(); } /** * @return true if it is absolute path, else false. * * A remote file always returns true. */ bool FileInformation::isAbsolute() const { if (m_isRemoteFile) { return true; } return m_fileInfo.isAbsolute(); } /** * @return true if it is relative path, else false (remote file is never relative) * * A remote file always returns false. */ bool FileInformation::isRelative() const { if (m_isRemoteFile) { return false; } return m_fileInfo.isRelative(); } /** * @return true if it is hidden, else false. * * A remote file always returns false. */ bool FileInformation::isHidden() const { if (m_isRemoteFile) { return false; } return m_fileInfo.isHidden(); } /** * @return Size of the file in bytes. * * A remote file always returns 0. */ int64_t FileInformation::size() const { if (m_isRemoteFile) { return 0; } return m_fileInfo.size(); } /** * @return name of file followed by path in parenthesis. * * For example: /usr/local/file.txt * returns: file.txt (/usr/local) */ AString FileInformation::getFileNameFollowedByPathNameForGUI() const { AString name = getFileName(); const AString pn = getPathName(); if ( ! pn.isEmpty()) { name += (" (" + pn + ")"); } return name; } /** * @return Name of the file excluding any path. * * A remote file always anything after the last slash (/). If there is * no slash, an emtpy string is returned. */ AString FileInformation::getFileName() const { if (m_isRemoteFile) { QString name = m_urlInfo.toString(); const int indx = name.lastIndexOf('/'); if ((indx >= 0) && (indx < name.length())) { name = name.mid(indx + 1); return name; } else { return ""; } } return m_fileInfo.fileName(); } /** * @return Name of the file excluding any path and WITHOUT any extension. * * A remote file always anything after the last slash (/). If there is * no slash, an emtpy string is returned. */ AString FileInformation::getFileNameNoExtension() const { AString name = getFileName(); const AString ext = getFileExtension(); if ( ! ext.isEmpty()) { const int32_t extStartIndex = name.indexOf(ext); if (extStartIndex > 0) { name = name.left(extStartIndex - 1); } } return name; } /** * @return The file's path excluding the file's name. * * A remote file always everything before the last slash (/). If there is * no slash, the URL is returned. */ AString FileInformation::getPathName() const { if (m_isRemoteFile) { QString path = m_urlInfo.toString(); const int indx = path.lastIndexOf('/'); if (indx >= 1) { path = path.left(indx); return path; } else { return path; } } return m_fileInfo.path(); } /** * @return The full path to the file including the file name, resolving * any symlinks or ".." or "." components. This should give exactly one * string per file, no matter how many ways to get to a file there are * (except for hardlinks). * * Note: A remote file returns the original, full URL. */ AString FileInformation::getCanonicalFilePath() const { if (m_isRemoteFile) { return m_urlInfo.toString(); } return m_fileInfo.canonicalFilePath(); } /** * @return The full path to the file (excluding the file name), * resolving any symlinks or ".." or "." components. * * For a remote file, this returns getPathName(). */ AString FileInformation::getCanonicalPath() const { if (m_isRemoteFile) { return getPathName(); } return m_fileInfo.canonicalPath(); } /** * @return The file name's extension. * * Many Workbench files have filename extensions that include a * dot (nii.gz, pconn.nii, etc) AND users sometimes include dots * in the names of the files (Glasser_PilotIII.L.20k_fs_LR.shape.gii) * so FileInfo::suffix and FileInfo::completeSuffix methods will * not provide the correct file extension. * * This method will compare the end of the file's name to every Workbench * file extension. If there is a match, this extension is returned. * Otherwise FileInfo::suffix is called and that will return anything * after but not including the last dot. */ AString FileInformation::getFileExtension() const { const std::vector workbenchExtensions = DataFileTypeEnum::getFilesExtensionsForEveryFile(); for (std::vector::const_iterator extIter = workbenchExtensions.begin(); extIter != workbenchExtensions.end(); extIter++) { const AString extension = *extIter; if ( ! extension.isEmpty()) { if (getFileName().endsWith(extension)) { return extension; } } } // if (m_isRemoteFile) { // AString ext = getFileName(); // const int indx = ext.lastIndexOf('.'); // if ((indx >= 0) && (indx < ext.length())) { // ext = ext.mid(indx + 1); // return ext; // } // else { // return ""; // } // } return m_fileInfo.suffix(); } /** * Get the components for a filename. * * Example: /Volumes/myelin1/caret7_gui_design/data/HCP_demo/areas.border * Returns * absolutePathOut => /Volumes/myelin1/caret7_gui_design/data/HCP_demo * fileNameWithoutExtensionOut => areas * extensionWithoutDotOut => border * * @param absolutePathOut * Absolute path of file file. Could be empty if this instance * was created using a filename without a path. * @param fileNameWithoutExtensionOut * Name of the file without path and without extention. * @param extensionWithoutDotOut * Extension without the dot. Could be empty if filename does * not have an extension. */ void FileInformation::getFileComponents(AString& absolutePathOut, AString& fileNameWithoutExtensionOut, AString& extensionWithoutDotOut) const { absolutePathOut = getAbsolutePath(); fileNameWithoutExtensionOut = getFileNameNoExtension(); extensionWithoutDotOut = getFileExtension(); } /** * Assemble the file components into a file path and name. * * @param pathName * Path for file (may be absolute, relative, or empty). * @param fileNameWithoutExtension * Name of file without extension. * @param extensionWithoutDot * The file extension without the leading dot. */ AString FileInformation::assembleFileComponents(const AString& pathName, const AString& fileNameWithoutExtension, const AString& extensionWithoutDot) { AString name; if ( ! pathName.isEmpty()) { name += (pathName + "/"); } name += fileNameWithoutExtension; if ( ! extensionWithoutDot.isEmpty()) { name += ("." + extensionWithoutDot); } return name; } /** * Convert, if needed, the file information to a local, absolute path. * * If the file is a remote file, the file name is added to the given current directory. * * If the file is local but a relative path, the filename is added to the given * current directory. * * If the file is local but an absolute path, the result of getAbsoluteFilePath() * is returned. * * @param currentDirectory * The current directory used by remote and relative file paths. * @param dataFileType * The type of data file. If the type is not UNKNOWN, and the * the file name does not end in the proper extension, the extension * is added to the file. @ @return * The local absolute file path. */ AString FileInformation::getAsLocalAbsoluteFilePath(const AString& currentDirectory, const DataFileTypeEnum::Enum dataFileType) const { AString thePath, theName, theExtension; getFileComponents(thePath, theName, theExtension); if (m_isLocalFile) { if (m_fileInfo.isRelative()) { thePath = currentDirectory; } } else if (m_isRemoteFile) { thePath = currentDirectory; theName = theName.replace("?", "_"); theName = theName.replace(":", "_"); theName = theName.replace("=", "_"); theName = theName.replace("@", "_"); } if (dataFileType != DataFileTypeEnum::UNKNOWN) { AString validExtension = ""; const std::vector validExtensions = DataFileTypeEnum::getAllFileExtensions(dataFileType); for (std::vector::const_iterator iter = validExtensions.begin(); iter != validExtensions.end(); iter++) { const AString ext = *iter; if (theExtension == ext) { validExtension = ext; break; } } if (validExtension.isEmpty()) { validExtension = DataFileTypeEnum::toFileExtension(dataFileType); } theExtension = validExtension; } const AString nameOut = assembleFileComponents(thePath, theName, theExtension); return nameOut; } /** * @return The file's absolute path. It DOES NOT include the file's name. * Note: A remote file returns everything up to the last slash. */ AString FileInformation::getAbsolutePath() const { if (m_isRemoteFile) { const AString fullName = m_urlInfo.toString(); const int lastSlashIndex = fullName.lastIndexOf("/"); if (lastSlashIndex > 0) { const AString thePath = fullName.left(lastSlashIndex); return thePath; } return fullName; } return m_fileInfo.absolutePath(); } AString FileInformation::getLastDirectory() const { QStringList myList = getPathName().split('/', QString::SkipEmptyParts);//QT always uses /, even on windows return myList[myList.size() - 1]; } /** * Get a description of this object's content. * @return String describing this object's content. */ AString FileInformation::toString() const { if (m_isRemoteFile) { return ("FileInformation for " + m_urlInfo.toString()); } return ("FileInformation for " + m_fileInfo.absoluteFilePath()); } /** * For a remote file, strip the username and password from the URL * and return the URL (without username and password), the username, * and the password. If the file is local or does not contain * username/password, the equivalent of getFilePath() is the output URL. * * @param urlOut * The URL with the username and password removed. * @param * The username that was in the URL. * @param * The password that was in the URL. */ void FileInformation::getRemoteUrlUsernameAndPassword(AString& urlOut, AString& usernameOut, AString& passwordOut) const { urlOut = ""; usernameOut = ""; passwordOut = ""; if (m_isRemoteFile) { urlOut = m_urlInfo.toString(QUrl::RemoveUserInfo); usernameOut = m_urlInfo.userName(); passwordOut = m_urlInfo.password(); return; } urlOut = getAbsoluteFilePath(); } /** * Convert the number of bytes to a string that includes standard units * (ie: Bytes, Kilobytes, Megabytes, Gigabytes, etc.) * * @param numberOfBytes * The number of bytes. * @return * String with the size in standard units. */ AString FileInformation::fileSizeToStandardUnits(const int64_t numberOfBytes) { double bytes = numberOfBytes; short index = 0; static const char *labels[9] = {" Bytes", " Kilobytes", " Megabytes", " Gigabytes", " Terabytes", " Petabytes", " Exabytes", " Zettabytes", " Yottabytes"}; while (index < 8 && bytes > 1000.0f) { ++index; bytes = bytes / 1000.0f;//using 1024 would make it Kibibytes, etc } AString sizeString = AString::number(bytes, 'f', 2) + labels[index];//2 digits after decimal point return sizeString; } connectome-workbench-1.2.3+git41-gc4c6c90/src/Common/FileInformation.h000066400000000000000000000073511300200146000252400ustar00rootroot00000000000000#ifndef __FILE_INFORMATION__H_ #define __FILE_INFORMATION__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 "DataFileTypeEnum.h" #include #include namespace caret { class FileInformation : public CaretObject { public: FileInformation(const AString& file); FileInformation(const AString& path, const AString& file); virtual ~FileInformation(); bool isLocalFile() const; bool isRemoteFile() const; bool exists() const; bool isFile() const; bool isDirectory() const; bool isSymbolicLink() const; bool isReadable() const; bool isWritable() const; bool isAbsolute() const; bool isRelative() const; bool isHidden() const; int64_t size() const; AString getAsLocalAbsoluteFilePath(const AString& currentDirectory, const DataFileTypeEnum::Enum dataFileType) const; AString getFileNameFollowedByPathNameForGUI() const; AString getFileName() const; AString getFileNameNoExtension() const; AString getPathName() const; AString getAbsoluteFilePath() const; AString getCanonicalFilePath() const; AString getCanonicalPath() const; AString getFileExtension() const; AString getAbsolutePath() const; AString getLastDirectory() const; void getFileComponents(AString& absolutePathOut, AString& fileNameWithoutExtensionOut, AString& extensionWithoutDotOut) const; static AString assembleFileComponents(const AString& pathName, const AString& fileNameWithoutExtension, const AString& extensionWithoutDot); bool remove(); void getRemoteUrlUsernameAndPassword(AString& urlOut, AString& usernameOut, AString& passwordOut) const; static AString fileSizeToStandardUnits(const int64_t numberOfBytes); private: FileInformation(const FileInformation&); FileInformation& operator=(const FileInformation&); public: virtual AString toString() const; private: QFileInfo m_fileInfo; QUrl m_urlInfo; bool m_isRemoteFile; bool m_isLocalFile; }; #ifdef __FILE_INFORMATION_DECLARE__ // #endif // __FILE_INFORMATION_DECLARE__ } // namespace #endif //__FILE_INFORMATION__H_ connectome-workbench-1.2.3+git41-gc4c6c90/src/Common/FloatMatrix.cxx000066400000000000000000000210741300200146000247560ustar00rootroot00000000000000 /*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 "CaretAssert.h" #include "CaretException.h" #include "FloatMatrix.h" #include "MatrixFunctions.h" using namespace caret; using namespace std; bool FloatMatrix::checkDimensions() const { uint64_t rows = m_matrix.size(), cols; if (rows == 0) return true;//treat it as fine for now cols = m_matrix[0].size(); for (uint64_t i = 1; i < rows; ++i) { if (m_matrix[i].size() != cols) { return false; } } return true; } FloatMatrix::FloatMatrix(const vector >& matrixIn) { m_matrix = matrixIn; CaretAssert(checkDimensions()); } FloatMatrix::FloatMatrix(const int64_t& rows, const int64_t& cols) { resize(rows, cols, true); } bool FloatMatrix::operator!=(const FloatMatrix& right) const { return !(*this == right); } FloatMatrix FloatMatrix::operator*(const FloatMatrix& right) const { FloatMatrix ret; MatrixFunctions::multiply(m_matrix, right.m_matrix, ret.m_matrix); return ret; } FloatMatrix& FloatMatrix::operator*=(const FloatMatrix& right) { MatrixFunctions::multiply(m_matrix, right.m_matrix, m_matrix);//would need a copy anyway, so let it make the copy internally return *this; } FloatMatrix FloatMatrix::concatHoriz(const FloatMatrix& right) const { FloatMatrix ret; MatrixFunctions::horizCat(m_matrix, right.m_matrix, ret.m_matrix); return ret; } FloatMatrix FloatMatrix::concatVert(const FloatMatrix& bottom) const { FloatMatrix ret; MatrixFunctions::vertCat(m_matrix, bottom.m_matrix, ret.m_matrix); return ret; } FloatMatrix FloatMatrix::getRange(const int64_t firstRow, const int64_t afterLastRow, const int64_t firstCol, const int64_t afterLastCol) const { FloatMatrix ret; MatrixFunctions::getChunk(firstRow, afterLastRow, firstCol, afterLastCol, m_matrix, ret.m_matrix); return ret; } FloatMatrix FloatMatrix::identity(const int64_t rows) { FloatMatrix ret; MatrixFunctions::identity(rows, ret.m_matrix); return ret; } FloatMatrix FloatMatrix::inverse() const { FloatMatrix ret; MatrixFunctions::inverse(m_matrix, ret.m_matrix); return ret; } FloatMatrix& FloatMatrix::operator*=(const float& right) { MatrixFunctions::multiply(m_matrix, right, m_matrix);//internally makes a copy return *this; } FloatMatrix FloatMatrix::operator+(const FloatMatrix& right) const { FloatMatrix ret; MatrixFunctions::add(m_matrix, right.m_matrix, ret.m_matrix); return ret; } FloatMatrix& FloatMatrix::operator+=(const FloatMatrix& right) { MatrixFunctions::add(m_matrix, right.m_matrix, m_matrix); return *this; } FloatMatrix& FloatMatrix::operator+=(const float& right) { MatrixFunctions::add(m_matrix, right, m_matrix); return *this; } FloatMatrix FloatMatrix::operator-(const FloatMatrix& right) const { FloatMatrix ret; MatrixFunctions::subtract(m_matrix, right.m_matrix, ret.m_matrix); return ret; } FloatMatrix& FloatMatrix::operator-=(const FloatMatrix& right) { MatrixFunctions::subtract(m_matrix, right.m_matrix, m_matrix); return *this; } FloatMatrix& FloatMatrix::operator-=(const float& right) { MatrixFunctions::add(m_matrix, (-right), m_matrix); return *this; } FloatMatrix& FloatMatrix::operator/=(const float& right) { return ((*this) *= 1.0f / right); } bool FloatMatrix::operator==(const FloatMatrix& right) const { if (this == &right) { return true;//short circuit true on pointer equivalence } int64_t i, j, rows = (int64_t)m_matrix.size(), cols; if (rows != (int64_t)right.m_matrix.size()) { return false; } if (rows == 0) { return true;//don't try to get the second dimension } cols = (int64_t)m_matrix[0].size(); if (cols != (int64_t)right.m_matrix[0].size()) { return false; } for (i = 0; i < rows; ++i) { for (j = 0; j < cols; ++j) { if (m_matrix[i][j] != right.m_matrix[i][j]) { return false; } } } return true; } void FloatMatrix::getDimensions(int64_t& rows, int64_t& cols) const { rows = (int64_t)m_matrix.size(); if (rows == 0) { cols = 0; } else { cols = (int64_t)m_matrix[0].size(); } } FloatMatrixRowRef FloatMatrix::operator[](const int64_t& index) { CaretAssert(index > -1 && index < (int64_t)m_matrix.size()); FloatMatrixRowRef ret(m_matrix[index]); return ret; } ConstFloatMatrixRowRef FloatMatrix::operator[](const int64_t& index) const { CaretAssert(index > -1 && index < (int64_t)m_matrix.size()); ConstFloatMatrixRowRef ret(m_matrix[index]); return ret; } FloatMatrix FloatMatrix::reducedRowEchelon() const { FloatMatrix ret(*this); MatrixFunctions::rref(ret.m_matrix); return ret; } void FloatMatrix::resize(const int64_t rows, const int64_t cols, const bool destructive) { MatrixFunctions::resize(rows, cols, m_matrix, destructive); } FloatMatrix FloatMatrix::transpose() const { FloatMatrix ret; MatrixFunctions::transpose(m_matrix, ret.m_matrix); return ret; } FloatMatrix FloatMatrix::zeros(const int64_t rows, const int64_t cols) { FloatMatrix ret; MatrixFunctions::zeros(rows, cols, ret.m_matrix); return ret; } FloatMatrix FloatMatrix::ones(const int64_t rows, const int64_t cols) { FloatMatrix ret; MatrixFunctions::ones(rows, cols, ret.m_matrix); return ret; } const vector >& FloatMatrix::getMatrix() const { return m_matrix; } void FloatMatrix::getAffineVectors(Vector3D& xvec, Vector3D& yvec, Vector3D& zvec, Vector3D& offset) const { if (m_matrix.size() < 3 || m_matrix.size() > 4 || m_matrix[0].size() != 4) { throw CaretException("getAffineVectors called on incorrectly sized matrix"); } xvec[0] = m_matrix[0][0]; xvec[1] = m_matrix[1][0]; xvec[2] = m_matrix[2][0]; yvec[0] = m_matrix[0][1]; yvec[1] = m_matrix[1][1]; yvec[2] = m_matrix[2][1]; zvec[0] = m_matrix[0][2]; zvec[1] = m_matrix[1][2]; zvec[2] = m_matrix[2][2]; offset[0] = m_matrix[0][3]; offset[1] = m_matrix[1][3]; offset[2] = m_matrix[2][3]; } FloatMatrix FloatMatrix::operator-() const { int64_t rows, cols; getDimensions(rows, cols); FloatMatrix ret = zeros(rows, cols); ret -= *this; return ret; } FloatMatrixRowRef::FloatMatrixRowRef(vector& therow) : m_row(therow) { } FloatMatrixRowRef& FloatMatrixRowRef::operator=(const FloatMatrixRowRef& right) { if (&m_row == &(right.m_row)) {//just in case vector isn't smart enough to check self assignment return *this; } CaretAssert(m_row.size() == right.m_row.size());//maybe this should be an exception, not an assertion? m_row = right.m_row; return *this; } FloatMatrixRowRef& FloatMatrixRowRef::operator=(const float& right) { for (int64_t i = 0; i < (int64_t)m_row.size(); ++i) { m_row[i] = right; } return *this; } float& FloatMatrixRowRef::operator[](const int64_t& index) { CaretAssert(index > -1 && index < (int64_t)m_row.size());//instead of segfaulting, explicitly check in debug return m_row[index]; } FloatMatrixRowRef::FloatMatrixRowRef(FloatMatrixRowRef& right) : m_row(right.m_row) { } FloatMatrixRowRef& FloatMatrixRowRef::operator=(const ConstFloatMatrixRowRef& right) { if (&m_row == &(right.m_row)) {//just in case vector isn't smart enough to check self assignment return *this; } CaretAssert(m_row.size() == right.m_row.size()); m_row = right.m_row; return *this; } const float& ConstFloatMatrixRowRef::operator[](const int64_t& index) { CaretAssert(index > -1 && index < (int64_t)m_row.size());//instead of segfaulting, explicitly check in debug return m_row[index]; } ConstFloatMatrixRowRef::ConstFloatMatrixRowRef(const ConstFloatMatrixRowRef& right) : m_row(right.m_row) { } ConstFloatMatrixRowRef::ConstFloatMatrixRowRef(const vector& therow) : m_row(therow) { } connectome-workbench-1.2.3+git41-gc4c6c90/src/Common/FloatMatrix.h000066400000000000000000000134751300200146000244110ustar00rootroot00000000000000 #ifndef __FLOAT_MATRIX_H__ #define __FLOAT_MATRIX_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 "stdint.h" #include "Vector3D.h" namespace caret { class ConstFloatMatrixRowRef {//needed to do [][] on a const FloatMatrix const std::vector& m_row; ConstFloatMatrixRowRef();//disallow default construction, this contains a reference public: ConstFloatMatrixRowRef(const ConstFloatMatrixRowRef& right);//copy constructor ConstFloatMatrixRowRef(const std::vector& therow); const float& operator[](const int64_t& index);//access element friend class FloatMatrixRowRef;//so it can check if it points to the same row }; class FloatMatrixRowRef {//needed to ensure some joker doesn't call mymatrix[1].resize();, while still allowing mymatrix[1][2] = 5; and mymatrix[1] = mymatrix[2]; std::vector& m_row; FloatMatrixRowRef();//disallow default construction, this contains a reference public: FloatMatrixRowRef(FloatMatrixRowRef& right);//copy constructor FloatMatrixRowRef(std::vector& therow); FloatMatrixRowRef& operator=(const FloatMatrixRowRef& right);//NOTE: copy row contents! FloatMatrixRowRef& operator=(const ConstFloatMatrixRowRef& right);//NOTE: copy row contents! FloatMatrixRowRef& operator=(const float& right);//NOTE: set all row values! float& operator[](const int64_t& index);//access element }; ///class for using single precision matrices (insulates other code from the MatrixFunctions templated header) ///errors will result in a matrix of size 0x0, or an assertion failure if the underlying vector isn't rectangular class FloatMatrix { std::vector > m_matrix; bool checkDimensions() const;//put this inside asserts at the end of functions public: FloatMatrix() { };//to make the compiler happy ///construct from a simple vector > FloatMatrix(const std::vector >& matrixIn); ///construct uninitialized with given size FloatMatrix(const int64_t& rows, const int64_t& cols); FloatMatrixRowRef operator[](const int64_t& index);//allow direct indexing to rows ConstFloatMatrixRowRef operator[](const int64_t& index) const;//allow direct indexing to rows while const FloatMatrix& operator+=(const FloatMatrix& right);//add to FloatMatrix& operator-=(const FloatMatrix& right);//subtract from FloatMatrix& operator*=(const FloatMatrix& right);//multiply by FloatMatrix& operator+=(const float& right);//add scalar to FloatMatrix& operator-=(const float& right);//subtract scalar from FloatMatrix& operator*=(const float& right);//multiply by scalar FloatMatrix& operator/=(const float& right);//divide by scalar FloatMatrix operator+(const FloatMatrix& right) const;//add FloatMatrix operator-(const FloatMatrix& right) const;//subtract FloatMatrix operator-() const;//negate FloatMatrix operator*(const FloatMatrix& right) const;//multiply bool operator==(const FloatMatrix& right) const;//compare bool operator!=(const FloatMatrix& right) const;//anti-compare ///return the inverse FloatMatrix inverse() const; ///return the reduced row echelon form FloatMatrix reducedRowEchelon() const; ///return the transpose FloatMatrix transpose() 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 FloatMatrix concatHoriz(const FloatMatrix& right) const; ///returns a matrix formed by concatenating bottom to the bottom of this FloatMatrix concatVert(const FloatMatrix& bottom) const; ///get the dimensions void getDimensions(int64_t& rows, int64_t& cols) const; ///get the matrix as a vector const std::vector >& getMatrix() const; ///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(); } ///get number of columns int64_t getNumberOfColumns() { if (m_matrix.size() == 0) return 0; return (int64_t)m_matrix[0].size(); } }; } #endif //__FLOAT_MATRIX_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Common/Histogram.cxx000066400000000000000000000256371300200146000244720ustar00rootroot00000000000000 /*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 "Histogram.h" #include "CaretAssert.h" #include using namespace caret; using namespace std; Histogram::Histogram(const int& numBuckets) { resize(numBuckets); reset(); } Histogram::Histogram(const float* data, const int64_t& dataCount) { CaretAssert(dataCount > 0); const int MIN_BUCKET_COUNT = 10; const int MAX_BUCKET_COUNT = 10000;//hits this max at 100 million int numBuckets = (int)sqrt((float)dataCount); if (numBuckets < MIN_BUCKET_COUNT) numBuckets = MIN_BUCKET_COUNT; if (numBuckets > MAX_BUCKET_COUNT) numBuckets = MAX_BUCKET_COUNT; resize(numBuckets); update(data, dataCount); } Histogram::Histogram(const int& numBuckets, const float* data, const int64_t& dataCount) { resize(numBuckets); update(data, dataCount); } void Histogram::resize(const int& buckets) { CaretAssert(buckets > 0); m_buckets.resize(buckets); m_cumulative.resize(buckets); m_display.resize(buckets); } void Histogram::reset() { m_posCount = 0; m_zeroCount = 0; m_negCount = 0; m_infCount = 0; m_negInfCount = 0; m_nanCount = 0; int numBuckets = (int)m_buckets.size(); for (int i = 0; i < numBuckets; ++i) { m_buckets[i] = 0; m_cumulative[i] = 0; m_display[i] = 0.0f; } } void Histogram::update(const int& numBuckets, const float* data, const int64_t& dataCount) { resize(numBuckets); update(data, dataCount); } void Histogram::update(const float* data, const int64_t& dataCount) { int numBuckets = (int)m_buckets.size(); reset(); bool first = true; for (int64_t i = 0; i < dataCount; ++i) {//count value classes if (data[i] != data[i]) { ++m_nanCount; continue;//skip NaNs } if (data[i] == 0.0f)//test exactly zero (negative zero also tests equal), in case someone wants stats on something with miniscule values (percent of surface area per node?) { ++m_zeroCount; } else { if (data[i] < 0.0f) { if (data[i] * 2.0f == data[i]) { ++m_negInfCount; continue;//skip neg infs } else { ++m_negCount; } } else { if (data[i] * 2.0f == data[i]) { ++m_infCount; continue;//skip infs } else { ++m_posCount; } } } if (first) { first = false; m_bucketMin = data[i]; m_bucketMax = data[i]; } else { if (data[i] > m_bucketMax) { m_bucketMax = data[i]; } else if (data[i] < m_bucketMin) {//skip testing for new minimum if we found a new maximum m_bucketMin = data[i]; } } } if (first) { m_bucketMin = m_bucketMax = 0.0f; return;//our arrays are already zeroed, so just return if no valid data } if (m_bucketMin == m_bucketMax) { int64_t totalValid = m_negCount + m_posCount + m_zeroCount; for (int i = 0; i < numBuckets - 1; ++i) { m_cumulative[i] = (i + 1) * totalValid / numBuckets;//so, its not particularly useful if our range is zero, but split them evenly among buckets just for kicks if (i == 0) { m_buckets[i] = m_cumulative[i]; } else { m_buckets[i] = m_cumulative[i] - m_cumulative[i - 1]; } }//display is already zeroed, so just return m_cumulative[numBuckets - 1] = totalValid;//make sure the last one has all of them if (numBuckets > 1) { m_buckets[numBuckets - 1] = m_cumulative[numBuckets - 1] - m_cumulative[numBuckets - 2]; } else { m_buckets[numBuckets - 1] = m_cumulative[numBuckets - 1]; } return; } float bucketsize = (m_bucketMax - m_bucketMin) / numBuckets; for (int64_t i = 0; i < dataCount; ++i) {//determine histogram if (data[i] != data[i]) continue;//exclude NaN if (data[i] < -1.0f && (data[i] * 2.0f == data[i])) continue;//exclude -inf if (data[i] > 1.0f && (data[i] * 2.0f == data[i])) continue;//exclude inf int bucket = (int)((data[i] - m_bucketMin) / bucketsize);//doesn't really matter whether small negative floats truncate to a 0 integer if (bucket < 0) bucket = 0;//because of this if (bucket >= numBuckets) bucket = numBuckets - 1; CaretAssertVectorIndex(m_buckets, bucket); ++m_buckets[bucket]; } computeCumulative(); for (int i = 0; i < numBuckets; ++i) {//compute display values by normalizing by bucket size m_display[i] = m_buckets[i] / bucketsize; } } void Histogram::update(const float* data, const int64_t& dataCount, float mostPositiveValueInclusive, float leastPositiveValueInclusive, float leastNegativeValueInclusive, float mostNegativeValueInclusive, const bool& includeZeroValues) { int numBuckets = (int)m_buckets.size(); reset(); if (mostNegativeValueInclusive > 0.0f) mostNegativeValueInclusive = 0.0f;//sanity check the inputs without asserting if (mostPositiveValueInclusive < 0.0f) mostPositiveValueInclusive = 0.0f; if (leastNegativeValueInclusive > 0.0f) leastNegativeValueInclusive = 0.0f; if (leastPositiveValueInclusive < 0.0f) leastPositiveValueInclusive = 0.0f; if ((mostPositiveValueInclusive >= leastPositiveValueInclusive && mostPositiveValueInclusive != 0.0f) || includeZeroValues) { m_bucketMax = mostPositiveValueInclusive; } else { m_bucketMax = leastNegativeValueInclusive; } if (mostNegativeValueInclusive != 0.0f || includeZeroValues) { m_bucketMin = mostNegativeValueInclusive; } else { m_bucketMin = leastPositiveValueInclusive; } float sanity = m_bucketMax + m_bucketMin; if (m_bucketMax <= m_bucketMin || sanity != sanity) {//bad input ranges, so collect counts, make a mock histogram if equal, and return (display values will be zeros) int64_t equalCount = 0; for (int64_t i = 0; i < dataCount; ++i) { if (data[i] != data[i]) { ++m_nanCount; continue; } if (data[i] < -1.0f && (data[i] * 2.0f == data[i])) { ++m_negInfCount; continue; } if (data[i] > 1.0f && (data[i] * 2.0f == data[i])) { ++m_infCount; continue; } if (data[i] == m_bucketMax) { ++equalCount; } } if (m_bucketMax == m_bucketMin) { if (m_bucketMax == 0.0f) { m_zeroCount = equalCount; } else { if (m_bucketMax < 0.0f) { m_negCount = equalCount; } else { m_posCount = equalCount; } } for (int i = 0; i < numBuckets - 1; ++i) { m_cumulative[i] = (i + 1) * equalCount / numBuckets;//so, its not particularly useful if our range is zero, but split them evenly among buckets just for kicks if (i == 0) { m_buckets[i] = m_cumulative[i]; } else { m_buckets[i] = m_cumulative[i] - m_cumulative[i - 1]; } }//display is already zeroed, so just return m_cumulative[numBuckets - 1] = equalCount;//make sure the last one has all of them if (numBuckets > 1) { m_buckets[numBuckets - 1] = m_cumulative[numBuckets - 1] - m_cumulative[numBuckets - 2]; } else { m_buckets[numBuckets - 1] = m_cumulative[numBuckets - 1]; } } return; } float bucketsize = (m_bucketMax - m_bucketMin) / numBuckets; for (int64_t i = 0; i < dataCount; ++i)//do the histogram {//count value classes if (data[i] != data[i]) { ++m_nanCount; continue;//skip NaNs } if (data[i] == 0.0f)//test exactly zero (negative zero also tests equal), in case someone wants stats on something with miniscule values (percent of surface area per node?) { if (!includeZeroValues) continue;//don't count what is excluded ++m_zeroCount; } else { if (data[i] < 0.0f) { if (data[i] * 2.0f == data[i]) { ++m_negInfCount; continue;//skip neg infs } else { if (data[i] > leastNegativeValueInclusive || data[i] < mostNegativeValueInclusive) continue;//exclude negatives outside range ++m_negCount; } } else { if (data[i] * 2.0f == data[i]) { ++m_infCount; continue;//skip infs } else { if (data[i] > mostPositiveValueInclusive || data[i] < leastPositiveValueInclusive) continue;//exclude negatives outside range ++m_posCount; } } } int bucket = (int)((data[i] - m_bucketMin) / bucketsize);//doesn't really matter whether small negative floats truncate to a 0 integer if (bucket < 0) bucket = 0;//because of this if (bucket >= numBuckets) bucket = numBuckets - 1; CaretAssertVectorIndex(m_buckets, bucket); ++m_buckets[bucket]; } computeCumulative(); for (int i = 0; i < numBuckets; ++i) {//compute display values by normalizing by bucket size m_display[i] = m_buckets[i] / bucketsize; } } void Histogram::computeCumulative() { int numBuckets = (int)m_buckets.size(); int64_t accum = 0; for (int i = 0; i < numBuckets; ++i) { accum += m_buckets[i]; m_cumulative[i] = accum; } } connectome-workbench-1.2.3+git41-gc4c6c90/src/Common/Histogram.h000066400000000000000000000066451300200146000241150ustar00rootroot00000000000000#ifndef __HISTOGRAM_H__ #define __HISTOGRAM_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 "stdint.h" namespace caret { class Histogram { std::vector m_buckets, m_cumulative; std::vector m_display; float m_bucketMin, m_bucketMax; ///counts of each class of number int64_t m_posCount, m_zeroCount, m_negCount, m_infCount, m_negInfCount, m_nanCount; void resize(const int& buckets); void reset(); void computeCumulative(); public: Histogram(const int& numBuckets = 100); Histogram(const float* data, const int64_t& dataCount);//NOTE: automatically determines number of buckets by square root of dataCount, but with set minimum and maximum 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, const int64_t& dataCount, float mostPositiveValueInclusive, float leastPositiveValueInclusive, float leastNegativeValueInclusive, float mostNegativeValueInclusive, const bool& includeZeroValues); ///get raw counts (useful mathematically) const std::vector& getHistogramCounts() const { return m_buckets; } const std::vector& getHistogramCumulativeCounts() const { return m_cumulative; } ///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; } 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 { posCount = m_posCount; zeroCount = m_zeroCount; negCount = m_negCount; infCount = m_infCount; negInfCount = m_negInfCount; nanCount = m_nanCount; } ///returns the low edge of the low bucket, and the high edge of the high bucket void getRange(float& histMin, float& histMax) const { histMin = m_bucketMin; histMax = m_bucketMax; } }; } #endif //__HISTOGRAM_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Common/HtmlStringBuilder.cxx000066400000000000000000000121101300200146000261150ustar00rootroot00000000000000/*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 "HtmlStringBuilder.h" using namespace caret; /** * \class caret::HtmlStringBuilder * \brief Assists with constructing HTML text. */ /** * Constructor.mp * */ HtmlStringBuilder::HtmlStringBuilder() : CaretObject() { this->clear(); } /** * Destructor */ HtmlStringBuilder::~HtmlStringBuilder() { } /** * Append plain text (same as addText()). * * @param text - Plain text to add. * */ void HtmlStringBuilder::append(const AString& text) { this->stringBuilder.append(text); } /** * Clear the text. * */ void HtmlStringBuilder::clear() { this->stringBuilder.reserve(4096); this->stringBuilder = ""; } /** * Add plain text. * * @param text - Plain text to add. * */ void HtmlStringBuilder::add(const AString& text) { this->stringBuilder.append(text); } /** * Add plain text followed by a line break. * @param text - Plain text to add. * */ void HtmlStringBuilder::addLine(const AString& text) { this->stringBuilder.append(text); this->addLineBreak(); } /** * Append an integer. * * @param num Integer that is to be appended. * */ void HtmlStringBuilder::add(const int32_t num) { this->stringBuilder.append(AString::number(num)); } /** * Append a float. * * @param num Float that is to be appended. * */ void HtmlStringBuilder::add(const float num) { this->stringBuilder.append(AString::number(num)); } /** * Add bold text. * * @param text - Text that is made bold. * */ void HtmlStringBuilder::addBold(const AString& text) { this->stringBuilder.append(""); this->stringBuilder.append(text); this->stringBuilder.append(""); } /** * Add bold float. * * @param num - Number that is made bold. * */ void HtmlStringBuilder::addBold(const float num) { this->stringBuilder.append(""); this->stringBuilder.append(AString::number(num)); this->stringBuilder.append(""); } /** * Add bold int. * * @param num - Number that is made bold. * */ void HtmlStringBuilder::addBold(const int32_t num) { this->stringBuilder.append(""); this->stringBuilder.append(AString::number(num)); this->stringBuilder.append(""); } /** * Add a hyperlink to the text. * @param urlText - URL that is target. * @param linkText - Text displayed as hyperlink. * */ void HtmlStringBuilder::addHyperlink( const AString& urlText, const AString& linkText) { this->stringBuilder.append("
stringBuilder.append("http://"); this->stringBuilder.append(urlText); this->stringBuilder.append("/"); this->stringBuilder.append(linkText); this->stringBuilder.append("\">"); this->stringBuilder.append(linkText); this->stringBuilder.append(""); } /** * Ends a paragraph. * */ void HtmlStringBuilder::addParagraph() { this->stringBuilder.append("

"); } /** * Add a line break. * */ void HtmlStringBuilder::addLineBreak() { this->stringBuilder.append("

"); } /** * Add a line breaks. * @param numLineBreaks - Number of line breaks to add. * */ void HtmlStringBuilder::addLineBreaks(const int32_t numLineBreaks) { for (int i = 0; i < numLineBreaks; i++) { this->addLineBreak(); } } /** * Add a space. * */ void HtmlStringBuilder::addSpace() { this->stringBuilder.append(" "); } /** * Add a spaces. * @param numSpaces - Number of spaces. * */ void HtmlStringBuilder::addSpaces(const int32_t numSpaces) { for (int i = 0; i < numSpaces; i++) { this->addSpace(); } } /** * Get the total text length (includes HTML markup). * @return Length of text. * */ int32_t HtmlStringBuilder::length() { return this->stringBuilder.length(); } /** * Convert to a string in HTML format WITHOUT leading and trailing * HTML and BODY tags. * * @return String containing text. * */ AString HtmlStringBuilder::toString() const { return this->stringBuilder; } /** * Convert to a string in HTML format WITH leading and trailing * HTML and BODY tags. * * @return String containing text. * */ AString HtmlStringBuilder::toStringWithHtmlBody() { AString sb; sb.reserve(this->stringBuilder.length() + 100); sb.append(""); sb.append(this->stringBuilder); sb.append(""); return sb; } connectome-workbench-1.2.3+git41-gc4c6c90/src/Common/HtmlStringBuilder.h000066400000000000000000000044631300200146000255560ustar00rootroot00000000000000#ifndef __HTMLSTRINGBUILDER_H__ #define __HTMLSTRINGBUILDER_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 class StringBuilder; namespace caret { class HtmlStringBuilder : public CaretObject { public: HtmlStringBuilder(); virtual ~HtmlStringBuilder(); private: HtmlStringBuilder(const HtmlStringBuilder& o); HtmlStringBuilder& operator=(const HtmlStringBuilder& o); public: void append(const AString& text); void clear(); void add(const AString& text); void addLine(const AString& text); void add(const int32_t num); void add(const float num); void addBold(const AString& text); void addBold(const float num); void addBold(const int32_t num); void addHyperlink( const AString& urlText, const AString& linkText); void addParagraph(); void addLineBreak(); void addLineBreaks(const int32_t numLineBreaks); void addSpace(); void addSpaces(const int32_t numSpaces); int32_t length(); AString toString() const; AString toStringWithHtmlBody(); private: AString stringBuilder; }; } // namespace #endif // __HTMLSTRINGBUILDER_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Common/ImageCaptureMethodEnum.cxx000066400000000000000000000252061300200146000270610ustar00rootroot00000000000000 /*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 __IMAGE_CAPTURE_METHOD_ENUM_DECLARE__ #include "ImageCaptureMethodEnum.h" #undef __IMAGE_CAPTURE_METHOD_ENUM_DECLARE__ #include "CaretAssert.h" using namespace caret; /** * \class caret::ImageCaptureMethodEnum * \brief Enumerated type for image capture method used in QGLWidget * * 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_imageCaptureMethodEnumComboBox; * * Declare a slot that is called when user changes selection * private slots: * void imageCaptureMethodEnumComboBoxItemActivated(); * * Implementation File (.cxx) * Include the header files * #include "EnumComboBoxTemplate.h" * #include "ImageCaptureMethodEnum.h" * * Instatiate: * m_imageCaptureMethodEnumComboBox = new EnumComboBoxTemplate(this); * m_imageCaptureMethodEnumComboBox->setup(); * * Get notified when the user changes the selection: * QObject::connect(m_imageCaptureMethodEnumComboBox, SIGNAL(itemActivated()), * this, SLOT(imageCaptureMethodEnumComboBoxItemActivated())); * * Update the selection: * m_imageCaptureMethodEnumComboBox->setSelectedItem(NEW_VALUE); * * Read the selection: * const ImageCaptureMethodEnum::Enum VARIABLE = m_imageCaptureMethodEnumComboBox->getSelectedItem(); * */ /** * Constructor. * * @param enumValue * An enumerated value. * @param name * Name of enumerated value. * * @param guiName * User-friendly name for use in user-interface. */ ImageCaptureMethodEnum::ImageCaptureMethodEnum(const Enum enumValue, const AString& name, const AString& guiName) { this->enumValue = enumValue; this->integerCode = integerCodeCounter++; this->name = name; this->guiName = guiName; } /** * Destructor. */ ImageCaptureMethodEnum::~ImageCaptureMethodEnum() { } /** * Initialize the enumerated metadata. */ void ImageCaptureMethodEnum::initialize() { if (initializedFlag) { return; } initializedFlag = true; enumData.push_back(ImageCaptureMethodEnum(IMAGE_CAPTURE_WITH_RENDER_PIXMAP, "IMAGE_CAPTURE_WITH_RENDER_PIXMAP", "Render Pixmap")); enumData.push_back(ImageCaptureMethodEnum(IMAGE_CAPTURE_WITH_GRAB_FRAME_BUFFER, "IMAGE_CAPTURE_WITH_GRAB_FRAME_BUFFER", "Grab Frame Buffer")); } /** * 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 ImageCaptureMethodEnum* ImageCaptureMethodEnum::findData(const Enum enumValue) { if (initializedFlag == false) initialize(); size_t num = enumData.size(); for (size_t i = 0; i < num; i++) { const ImageCaptureMethodEnum* 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 ImageCaptureMethodEnum::toName(Enum enumValue) { if (initializedFlag == false) initialize(); const ImageCaptureMethodEnum* 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. */ ImageCaptureMethodEnum::Enum ImageCaptureMethodEnum::fromName(const AString& name, bool* isValidOut) { if (initializedFlag == false) initialize(); bool validFlag = false; Enum enumValue = ImageCaptureMethodEnum::enumData[0].enumValue; for (std::vector::iterator iter = enumData.begin(); iter != enumData.end(); iter++) { const ImageCaptureMethodEnum& 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 ImageCaptureMethodEnum")); } return enumValue; } /** * Get a GUI string representation of the enumerated type. * @param enumValue * Enumerated value. * @return * String representing enumerated value. */ AString ImageCaptureMethodEnum::toGuiName(Enum enumValue) { if (initializedFlag == false) initialize(); const ImageCaptureMethodEnum* 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. */ ImageCaptureMethodEnum::Enum ImageCaptureMethodEnum::fromGuiName(const AString& guiName, bool* isValidOut) { if (initializedFlag == false) initialize(); bool validFlag = false; Enum enumValue = ImageCaptureMethodEnum::enumData[0].enumValue; for (std::vector::iterator iter = enumData.begin(); iter != enumData.end(); iter++) { const ImageCaptureMethodEnum& 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 ImageCaptureMethodEnum")); } return enumValue; } /** * Get the integer code for a data type. * * @return * Integer code for data type. */ int32_t ImageCaptureMethodEnum::toIntegerCode(Enum enumValue) { if (initializedFlag == false) initialize(); const ImageCaptureMethodEnum* 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. */ ImageCaptureMethodEnum::Enum ImageCaptureMethodEnum::fromIntegerCode(const int32_t integerCode, bool* isValidOut) { if (initializedFlag == false) initialize(); bool validFlag = false; Enum enumValue = ImageCaptureMethodEnum::enumData[0].enumValue; for (std::vector::iterator iter = enumData.begin(); iter != enumData.end(); iter++) { const ImageCaptureMethodEnum& 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 ImageCaptureMethodEnum")); } 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 ImageCaptureMethodEnum::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 ImageCaptureMethodEnum::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(ImageCaptureMethodEnum::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 ImageCaptureMethodEnum::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(ImageCaptureMethodEnum::toGuiName(iter->enumValue)); } if (isSorted) { std::sort(allGuiNames.begin(), allGuiNames.end()); } } connectome-workbench-1.2.3+git41-gc4c6c90/src/Common/ImageCaptureMethodEnum.h000066400000000000000000000063401300200146000265040ustar00rootroot00000000000000#ifndef __IMAGE_CAPTURE_METHOD_ENUM_H__ #define __IMAGE_CAPTURE_METHOD_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 ImageCaptureMethodEnum { public: /** * Enumerated values. */ enum Enum { /** Capture the image with QGLWidget::renderPixmap() */ IMAGE_CAPTURE_WITH_RENDER_PIXMAP, /** Capture the image with QGLWidget::grabFrameBuffer() */ IMAGE_CAPTURE_WITH_GRAB_FRAME_BUFFER }; ~ImageCaptureMethodEnum(); 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: ImageCaptureMethodEnum(const Enum enumValue, const AString& name, const AString& guiName); static const ImageCaptureMethodEnum* 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 __IMAGE_CAPTURE_METHOD_ENUM_DECLARE__ std::vector ImageCaptureMethodEnum::enumData; bool ImageCaptureMethodEnum::initializedFlag = false; int32_t ImageCaptureMethodEnum::integerCodeCounter = 0; #endif // __IMAGE_CAPTURE_METHOD_ENUM_DECLARE__ } // namespace #endif //__IMAGE_CAPTURE_METHOD_ENUM_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Common/LogHandler.cxx000066400000000000000000000023541300200146000245430ustar00rootroot00000000000000 /*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 __LOG_HANDLER_DECLARE__ #include "LogHandler.h" #undef __LOG_HANDLER_DECLARE__ using namespace caret; /** * Constructor. */ LogHandler::LogHandler() : CaretObject() { } /** * Destructor. */ LogHandler::~LogHandler() { } /** * Get a description of this object's content. * @return String describing this object's content. */ AString LogHandler::toString() const { return "LogHandler"; } connectome-workbench-1.2.3+git41-gc4c6c90/src/Common/LogHandler.h000066400000000000000000000037621300200146000241740ustar00rootroot00000000000000#ifndef __LOG_HANDLER__H_ #define __LOG_HANDLER__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" namespace caret { class LogRecord; /** * \brief Processes log record sent by a Logger. * * A Handler takes log record messages and * does something with them. This is an abstract * class and must be subclassed. * * This class emulates Java's java.util.logging.Handler. */ class LogHandler : public CaretObject { public: /// close the handler and free resources virtual void close() = 0; /// flush any buffered output virtual void flush() = 0; /// Publish a log record virtual void publish(const LogRecord& logRecord) = 0; virtual ~LogHandler(); protected: LogHandler(); private: LogHandler(const LogHandler&); LogHandler& operator=(const LogHandler&); public: virtual AString toString() const; private: }; #ifdef __LOG_HANDLER_DECLARE__ // #endif // __LOG_HANDLER_DECLARE__ } // namespace #endif //__LOG_HANDLER__H_ connectome-workbench-1.2.3+git41-gc4c6c90/src/Common/LogHandlerStandardError.cxx000066400000000000000000000054651300200146000272440ustar00rootroot00000000000000 /*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 __LOG_HANDLER_STANDARD_ERROR_DECLARE__ #include "LogHandlerStandardError.h" #undef __LOG_HANDLER_STANDARD_ERROR_DECLARE__ #include "FileInformation.h" #include "LogRecord.h" using namespace caret; using namespace std; /** * Constructor. */ LogHandlerStandardError::LogHandlerStandardError() : LogHandler() { } /** * Destructor. */ LogHandlerStandardError::~LogHandlerStandardError() { } /** * Get a description of this object's content. * @return String describing this object's content. */ AString LogHandlerStandardError::toString() const { return "LogHandlerStandardError"; } /** * close the handler and free resources. */ void LogHandlerStandardError::close() { // nothing to close } /** * Flush any buffered output. */ void LogHandlerStandardError::flush() { std::cerr.flush(); } /** * Publish a log record. * * @param logRecord * Logging record that is sent to standard error. */ void LogHandlerStandardError::publish(const LogRecord& logRecord) { cerr << endl; cerr << LogLevelEnum::toHintedName(logRecord.getLevel()) << ": " << logRecord.getText().toLocal8Bit().constData() << endl; #ifndef NDEBUG if (logRecord.getMethodName().isEmpty() == false) {//in debug, also give method name and source location cerr << "Method: " << logRecord.getMethodName() << endl; } cerr << "Location: " << logRecord.getFilename() << ":" << AString::number(logRecord.getLineNumber()) << endl; #else if (LogLevelEnum::toIntegerCode(logRecord.getLevel()) >= LogLevelEnum::toIntegerCode(LogLevelEnum::SEVERE))//in release, give method and simplified source location only for severe or worse { if (logRecord.getMethodName().isEmpty() == false) { cerr << "Method: " << logRecord.getMethodName() << endl; } cerr << "Location: " << FileInformation(logRecord.getFilename()).getFileName() << ":" << AString::number(logRecord.getLineNumber()) << endl; } #endif cerr << endl; } connectome-workbench-1.2.3+git41-gc4c6c90/src/Common/LogHandlerStandardError.h000066400000000000000000000035341300200146000266640ustar00rootroot00000000000000#ifndef __LOG_HANDLER_STANDARD_ERROR__H_ #define __LOG_HANDLER_STANDARD_ERROR__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 "LogHandler.h" namespace caret { class LogRecord; /** * \brief * * */ class LogHandlerStandardError : public LogHandler { public: LogHandlerStandardError(); virtual ~LogHandlerStandardError(); virtual void close(); virtual void flush(); virtual void publish(const LogRecord& logRecord); private: LogHandlerStandardError(const LogHandlerStandardError&); LogHandlerStandardError& operator=(const LogHandlerStandardError&); public: virtual AString toString() const; private: }; #ifdef __LOG_HANDLER_STANDARD_ERROR_DECLARE__ // #endif // __LOG_HANDLER_STANDARD_ERROR_DECLARE__ } // namespace #endif //__LOG_HANDLER_STANDARD_ERROR__H_ connectome-workbench-1.2.3+git41-gc4c6c90/src/Common/LogLevelEnum.cxx000066400000000000000000000242571300200146000250700ustar00rootroot00000000000000 /*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 __LOG_LEVEL_ENUM_DECLARE__ #include "LogLevelEnum.h" #undef __LOG_LEVEL_ENUM_DECLARE__ #include "CaretAssert.h" using namespace caret; /** * Constructor. * * @param enumValue * An enumerated value. * @param integerCode * Integer code for this enumerated value. * * @param name * Name of enumerated value. * * @param guiName * User-friendly name for use in user-interface. */ LogLevelEnum::LogLevelEnum(const Enum enumValue, const int32_t integerCode, const AString& name, const AString& guiName, const AString& hintedName) { this->enumValue = enumValue; this->integerCode = integerCode; this->name = name; this->guiName = guiName; this->hintedName = hintedName; } /** * Destructor. */ LogLevelEnum::~LogLevelEnum() { } /** * Initialize the enumerated metadata. */ void LogLevelEnum::initialize() { if (initializedFlag) { return; } initializedFlag = true; enumData.push_back(LogLevelEnum(SEVERE, 800, "SEVERE", "Severe", "SEVERE")); enumData.push_back(LogLevelEnum(WARNING, 700, "WARNING", "Warning", "WARNING")); enumData.push_back(LogLevelEnum(INFO, 600, "INFO", "Information", "Info")); enumData.push_back(LogLevelEnum(CONFIG, 500, "CONFIG", "Configuration", "Config")); enumData.push_back(LogLevelEnum(FINE, 400, "FINE", "Fine (Tracing)", "Fine")); enumData.push_back(LogLevelEnum(FINER, 300, "FINER", "Finer (Detailed Tracing)", "Finer")); enumData.push_back(LogLevelEnum(FINEST, 200, "FINEST", "Finest (Very Detailed Tracing)", "Finest")); enumData.push_back(LogLevelEnum(ALL, 100, "ALL", "All", "ALL"));//shouldn't get used in messages - do we even need this? FINEST should show everything enumData.push_back(LogLevelEnum(OFF, 0, "OFF", "Off", "Off"));//also shouldn't get used in messages } /** * 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 LogLevelEnum* LogLevelEnum::findData(const Enum enumValue) { if (initializedFlag == false) initialize(); size_t num = enumData.size(); for (size_t i = 0; i < num; i++) { const LogLevelEnum* 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 LogLevelEnum::toName(Enum enumValue) { if (initializedFlag == false) initialize(); const LogLevelEnum* 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. */ LogLevelEnum::Enum LogLevelEnum::fromName(const AString& name, bool* isValidOut) { if (initializedFlag == false) initialize(); bool validFlag = false; Enum enumValue = OFF; for (std::vector::iterator iter = enumData.begin(); iter != enumData.end(); iter++) { const LogLevelEnum& 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 LogLevelEnum")); } return enumValue; } /** * Get a GUI string representation of the enumerated type. * @param enumValue * Enumerated value. * @return * String representing enumerated value. */ AString LogLevelEnum::toGuiName(Enum enumValue) { if (initializedFlag == false) initialize(); const LogLevelEnum* 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. */ LogLevelEnum::Enum LogLevelEnum::fromGuiName(const AString& guiName, bool* isValidOut) { if (initializedFlag == false) initialize(); bool validFlag = false; Enum enumValue = OFF; for (std::vector::iterator iter = enumData.begin(); iter != enumData.end(); iter++) { const LogLevelEnum& 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 LogLevelEnum")); } return enumValue; } AString LogLevelEnum::toHintedName(LogLevelEnum::Enum enumValue) { if (initializedFlag == false) initialize(); const LogLevelEnum* enumInstance = findData(enumValue); CaretAssert(enumInstance != NULL); return enumInstance->hintedName; } LogLevelEnum::Enum LogLevelEnum::fromHintedName(const AString& hintedName, bool* isValidOut) { if (initializedFlag == false) initialize(); bool validFlag = false; Enum enumValue = OFF; for (std::vector::iterator iter = enumData.begin(); iter != enumData.end(); iter++) { const LogLevelEnum& d = *iter; if (d.hintedName == hintedName) { enumValue = d.enumValue; validFlag = true; break; } } if (isValidOut != 0) { *isValidOut = validFlag; } else if (validFlag == false) { CaretAssertMessage(0, AString("hintedName " + hintedName + " failed to match enumerated value for type LogLevelEnum")); } return enumValue; } /** * Get the integer code for a data type. * * @return * Integer code for data type. */ int32_t LogLevelEnum::toIntegerCode(Enum enumValue) { if (initializedFlag == false) initialize(); const LogLevelEnum* 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. */ LogLevelEnum::Enum LogLevelEnum::fromIntegerCode(const int32_t integerCode, bool* isValidOut) { if (initializedFlag == false) initialize(); bool validFlag = false; Enum enumValue = OFF; for (std::vector::iterator iter = enumData.begin(); iter != enumData.end(); iter++) { const LogLevelEnum& 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 LogLevelEnum")); } 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 LogLevelEnum::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); } } connectome-workbench-1.2.3+git41-gc4c6c90/src/Common/LogLevelEnum.h000066400000000000000000000073021300200146000245050ustar00rootroot00000000000000#ifndef __LOG_LEVEL_ENUM__H_ #define __LOG_LEVEL_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 { /** * \brief Log level. * * The log level defines a standard logging levels that are used to * control logging output. The levels are ordered and enabling * logging at a given level enables logging at all higher levels. * This class emulates the Java class java.util.logging.Level. */ class LogLevelEnum { public: /** * Enumerated values. */ enum Enum { /** serious failure */ SEVERE, /** potential problem */ WARNING, /** informational messages */ INFO, /** configuration messages, versions of libraries etc. */ CONFIG, /** Detailed information */ FINE, /** More detailed information */ FINER, /** Very detailed information, all Events are logged at this level */ FINEST, /** Log all records */ ALL, /** */ OFF }; ~LogLevelEnum(); 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 AString toHintedName(Enum enumValue); static Enum fromHintedName(const AString& hintedName, bool* isValidOut); static int32_t toIntegerCode(Enum enumValue); static Enum fromIntegerCode(const int32_t integerCode, bool* isValidOut); static void getAllEnums(std::vector& allEnums); private: LogLevelEnum(const Enum enumValue, const int32_t integerCode, const AString& name, const AString& guiName, const AString& hintedName); static const LogLevelEnum* 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; /** 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; /** A name that emphasizes important levels with all-caps */ AString hintedName; }; #ifdef __LOG_LEVEL_ENUM_DECLARE__ std::vector LogLevelEnum::enumData; bool LogLevelEnum::initializedFlag = false; #endif // __LOG_LEVEL_ENUM_DECLARE__ } // namespace #endif //__LOG_LEVEL_ENUM__H_ connectome-workbench-1.2.3+git41-gc4c6c90/src/Common/LogManager.cxx000066400000000000000000000076311300200146000245430ustar00rootroot00000000000000 /*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 __LOG_MANAGER_DECLARE__ #include "CaretLogger.h" #include "LogManager.h" #undef __LOG_MANAGER_DECLARE__ #include "CaretAssert.h" #include "Logger.h" #include "LogHandlerStandardError.h" using namespace caret; /** * Constructor. */ LogManager::LogManager() : CaretObject() { } /** * Destructor. */ LogManager::~LogManager() { /* * Delete all of the loggers. */ for (std::vector::iterator iter = this->loggers.begin(); iter != this->loggers.end(); iter++) { delete (*iter); } } /** * Add a named logger. This does nothing an returns false * if a logger with the same name is already registered. * The Logger factory methods call this method to register * newly created loggers. The LogManager will 'delete' * any registed loggers when the LogManager is deleted. * * @param logger * Logger that is added. * @return * true if logger was successfully registered, false if * a logger with the same name already exists. */ bool LogManager::addLogger(Logger* logger) { CaretAssert(logger); Logger* existingLogger = this->getLogger(logger->getName()); if (existingLogger != NULL) { return false; } this->loggers.push_back(logger); return true; } /** * Find a named logger. * * @param name * Name of the logger. * @return * Matching logger or NULL if none found. */ Logger* LogManager::getLogger(const AString& name) { /* * Find logger with matching name. */ for (std::vector::iterator iter = this->loggers.begin(); iter != this->loggers.end(); iter++) { Logger* logger = *iter; if (logger->getName() == name) { return logger; } } return NULL; } /** * Return the global LogManager object. * * @return The global LogManager object. */ LogManager* LogManager::getLogManager() { return LogManager::singletonLogManager; } /** * Get a description of this object's content. * @return String describing this object's content. */ AString LogManager::toString() const { return "LogManager"; } /** * Create the log manager. * This must be called one AND ONLY one time. */ void LogManager::createLogManager() { CaretAssertMessage((LogManager::singletonLogManager == NULL), "Log manager has already been created."); LogManager::singletonLogManager = new LogManager(); Logger* caretLoggerInstance = Logger::getLogger("CaretLogger"); caretLoggerInstance->setLevel(LogLevelEnum::CONFIG); //caretLoggerInstance->setLevel(LogLevelEnum::FINEST); caretLoggerInstance->addLogHandler(new LogHandlerStandardError()); CaretLogger::setLogger(caretLoggerInstance); } /** * Delete the log manager. * This may only be called one time after log manager is created. */ void LogManager::deleteLogManager() { CaretAssertMessage((LogManager::singletonLogManager != NULL), "Log manager does not exist, cannot delete it."); delete LogManager::singletonLogManager; LogManager::singletonLogManager = NULL; } connectome-workbench-1.2.3+git41-gc4c6c90/src/Common/LogManager.h000066400000000000000000000042611300200146000241640ustar00rootroot00000000000000#ifndef __LOG_MANAGER__H_ #define __LOG_MANAGER__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" namespace caret { class Logger; /** * \brief The logging manager. * * There is one global LogManager that keeps track of all * Loggers. The LogManager must be initialized prior to * creating any Loggers. Whn the LogManager is deleted * all loggers are also deleted. * This class emulates Java's java.util.logging.LogManager. */ class LogManager : public CaretObject { public: static void createLogManager(); static void deleteLogManager(); bool addLogger(Logger* logger); Logger* getLogger(const AString& name); static LogManager* getLogManager(); private: LogManager(); virtual ~LogManager(); LogManager(const LogManager&); LogManager& operator=(const LogManager&); /** All loggers */ std::vector loggers; /** Global log manager */ static LogManager* singletonLogManager; public: virtual AString toString() const; private: }; #ifdef __LOG_MANAGER_DECLARE__ LogManager* LogManager::singletonLogManager = NULL; #endif // __LOG_MANAGER_DECLARE__ } // namespace #endif //__LOG_MANAGER__H_ connectome-workbench-1.2.3+git41-gc4c6c90/src/Common/LogRecord.cxx000066400000000000000000000043621300200146000244050ustar00rootroot00000000000000 /*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 __LOG_RECORD_DECLARE__ #include "LogRecord.h" #undef __LOG_RECORD_DECLARE__ using namespace caret; /** * Constructor. * * @param level * Logging level for message. * @param methodName * Method that logged the message. * @param filename * Name of file that originated the message. * @param lineNumber * Line number of message. * @param text * Text description. */ LogRecord::LogRecord(const LogLevelEnum::Enum level, const AString& methodName, const AString& filename, const int32_t lineNumber, const AString& text) : CaretObject() { this->level = level; this->methodName = methodName; this->filename = filename; this->lineNumber = lineNumber; this->text = text; } /** * Destructor. */ LogRecord::~LogRecord() { } /** * Get a description of this log record. * @return String describing this log record. */ AString LogRecord::toString() const { AString s = "Level=" + LogLevelEnum::toName(this->level); if (this->methodName.isEmpty() == false) { s += " Method=" + this->methodName; } if (this->filename.isEmpty() == false) { s += " File=" + this->filename; } if (this->lineNumber >= 0) { s += " Line=" + AString::number(this->lineNumber); } if (this->text.isEmpty() == false) { s += " Text=" + this->text; } return s; } connectome-workbench-1.2.3+git41-gc4c6c90/src/Common/LogRecord.h000066400000000000000000000050701300200146000240270ustar00rootroot00000000000000#ifndef __LOG_RECORD__H_ #define __LOG_RECORD__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 "LogLevelEnum.h" namespace caret { /** * \brief Log record. * * A record for a single logging message * This class emulates the Java calss java.util.logging. */ class LogRecord : public CaretObject { public: LogRecord(const LogLevelEnum::Enum level, const AString& methodName, const AString& filename, const int32_t lineNumber, const AString& text); virtual ~LogRecord(); LogLevelEnum::Enum getLevel() const { return level; } /** * @return Text message. */ AString getText() const { return text; } /** * @return Method name that logged message. */ AString getMethodName() const { return methodName; } /** * @return Filename in which message was logged. */ AString getFilename() const { return filename; } /** * @return Line number at which message was logged. */ int32_t getLineNumber() const { return lineNumber; } private: LogRecord(const LogRecord&); LogRecord& operator=(const LogRecord&); public: virtual AString toString() const; private: LogLevelEnum::Enum level; AString text; AString methodName; AString filename; int32_t lineNumber; }; #ifdef __LOG_RECORD_DECLARE__ // #endif // __LOG_RECORD_DECLARE__ } // namespace #endif //__LOG_RECORD__H_ connectome-workbench-1.2.3+git41-gc4c6c90/src/Common/Logger.cxx000066400000000000000000000201371300200146000237420ustar00rootroot00000000000000 /*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 __LOGGER_DECLARE__ #include "Logger.h" #undef __LOGGER_DECLARE__ #include "CaretAssert.h" #include "LogHandler.h" #include "LogManager.h" #include "LogRecord.h" using namespace caret; /** * Constructor. */ Logger::Logger(const AString& name) : CaretObject() { this->name = name; this->setLevel(LogLevelEnum::ALL); } /** * Destructor. */ Logger::~Logger() { for (std::vector::iterator iter = this->logHandlers.begin(); iter != this->logHandlers.end(); iter++) { LogHandler* lh = *iter; delete lh; } } /** * Find or create a logger with the specified name. * If a logger exists with the given name, it is returned. * Otherwise, a new Logger is created and returned. * Never delete the returned Logger as it will be * deleted when the LogManager is deleted. */ Logger* Logger::getLogger(const AString& name) { CaretAssertMessage((name.isEmpty() == false), "Logger name must not be empty string."); Logger* existingLogger = LogManager::getLogManager()->getLogger(name); if (existingLogger != NULL) { return existingLogger; } Logger* logger = new Logger(name); bool exists = LogManager::getLogManager()->addLogger(logger); CaretAssertMessage(exists, "Trying to add logger and logger with name exists."); return logger; } /** * Get a description of this object's content. * @return String describing this object's content. */ AString Logger::toString() const { return ("Logger " + this->name); } /** * Log a message. * * @param logLevel * Logging level for message. The levels OFF and ALL * are not permitted. * @param methodName * Name of method. * @param filename * Name of file that originated the message. * @param lineNumber * Line number of message. * @param text * Text description. * */ void Logger::log(const LogLevelEnum::Enum logLevel, const AString& methodName, const AString& filename, const int32_t lineNumber, const AString& text) { switch (logLevel) { case LogLevelEnum::OFF: case LogLevelEnum::ALL: Logger::log(LogLevelEnum::SEVERE, methodName, filename, lineNumber, "Cannot log record with level = OFF or ALL for: " + text); break; case LogLevelEnum::FINEST: if (this->finestLoggingEnabled == false) return; break; case LogLevelEnum::FINER: if (this->finerLoggingEnabled == false) return; break; case LogLevelEnum::FINE: if (this->fineLoggingEnabled == false) return; break; case LogLevelEnum::CONFIG: if (this->configLoggingEnabled == false) return; break; case LogLevelEnum::INFO: if (this->infoLoggingEnabled == false) return; break; case LogLevelEnum::WARNING: if (this->warningLoggingEnabled == false) return; break; case LogLevelEnum::SEVERE: if (this->severeLoggingEnabled == false) return; break; } const LogRecord logRecord(logLevel, methodName, filename, lineNumber, text); /* * Send to all of the handlers. */ for (std::vector::iterator iter = this->logHandlers.begin(); iter != this->logHandlers.end(); iter++) { LogHandler* lh = *iter; lh->publish(logRecord); } } /** * Log throwing of a CaretException derived class. * A log record with the message THROW at the level * FINER is logged. * @param methodName * Name of method. * @param filename * Name of file that originated the message. * @param lineNumber * Line number of message. * */ void Logger::throwingCaretException(const AString& methodName, const AString& filename, const int32_t lineNumber, CaretException& caretException) { Logger::log(LogLevelEnum::FINER, methodName, filename, lineNumber, "THROW " + AString(typeid(caretException).name()) + ": " + caretException.whatString()); } /** * Log a method entry. A log record with * the message ENTRY at the level FINER * is logged. * @param methodName * Name of method. * @param filename * Name of file that originated the message. * @param lineNumber * Line number of message. */ void Logger::entering(const AString& methodName, const AString& filename, const int32_t lineNumber) { Logger::log(LogLevelEnum::FINER, "", filename, lineNumber, "ENTRY: " + methodName); } /** * Log a method return. A log record with * the message RETURN at the level FINER * is logged. * @param methodName * Name of method. * @param filename * Name of file that originated the message. * @param lineNumber * Line number of message. */ void Logger::exiting(const AString& methodName, const AString& filename, const int32_t lineNumber) { Logger::log(LogLevelEnum::FINER, "", filename, lineNumber, "RETURN: " + methodName); } /** * Get the current logging level. * * @return Current logging level. */ LogLevelEnum::Enum Logger::getLevel() const { return this->level; } /** * Set the logging level. * * @param level * New level for logging. */ void Logger::setLevel(const LogLevelEnum::Enum level) { this->level = level; this->severeLoggingEnabled = false; this->warningLoggingEnabled = false; this->infoLoggingEnabled = false; this->configLoggingEnabled = false; this->fineLoggingEnabled = false; this->finerLoggingEnabled = false; this->finestLoggingEnabled = false; /* * Notice that levels are arranged from * from LOWEST to HIGHEST and that most * have not break statements. Thus * 'falling through' the 'case' statements * sets the higher levels of logging. */ switch (this->level) { case LogLevelEnum::OFF: break; case LogLevelEnum::ALL: case LogLevelEnum::FINEST: this->finestLoggingEnabled = true; case LogLevelEnum::FINER: this->finerLoggingEnabled = true; case LogLevelEnum::FINE: this->fineLoggingEnabled = true; case LogLevelEnum::CONFIG: this->configLoggingEnabled = true; case LogLevelEnum::INFO: this->infoLoggingEnabled = true; case LogLevelEnum::WARNING: this->warningLoggingEnabled = true; case LogLevelEnum::SEVERE: this->severeLoggingEnabled = true; break; } } /** * Add a log handler to a logger. This object * will take care deleting the handlers that * it uses. * * @param logHandler * Handler that is added. */ void Logger::addLogHandler(LogHandler* logHandler) { this->logHandlers.push_back(logHandler); } connectome-workbench-1.2.3+git41-gc4c6c90/src/Common/Logger.h000066400000000000000000000104651300200146000233720ustar00rootroot00000000000000#ifndef __LOGGER_H__ #define __LOGGER_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 "CaretException.h" #include "LogLevelEnum.h" namespace caret { class LogHandler; /*! * \brief Logs messages. * * Logger logs messages that are sent to registered handlers. * Each logger has an associated level. Only messages with * a level equal to or 'above' the level are forwarded to * handlers. If the message is to be forwarded, a LogRecord * is created and then forwarded to the registered handlers. * This class emulates the Java class java.util.logging. */ class Logger : public CaretObject { public: virtual ~Logger(); static Logger* getLogger(const AString& name); void log(const LogLevelEnum::Enum logLevel, const AString& methodName, const AString& filename, const int32_t lineNumber, const AString& text); void entering(const AString& methodName, const AString& filename, const int32_t lineNumber); void exiting(const AString& methodName, const AString& filename, const int32_t lineNumber); void throwingCaretException(const AString& methodName, const AString& filename, const int32_t lineNumber, CaretException& caretException); LogLevelEnum::Enum getLevel() const; void setLevel(const LogLevelEnum::Enum level); void addLogHandler(LogHandler* logHandler); /** @return Is severe logging enabled? */ inline bool isSevere() const { return this->severeLoggingEnabled; } /** @return Is warning logging enabled? */ inline bool isWarning() const { return this->warningLoggingEnabled; } /** @return Is info logging enabled? */ inline bool isInfo() const { return this->infoLoggingEnabled; } /** @return Is config logging enabled? */ inline bool isConfig() const { return this->configLoggingEnabled; } /** @return Is fine logging enabled? */ inline bool isFine() const { return this->fineLoggingEnabled; } /** @return Is finer logging enabled? */ inline bool isFiner() const { return this->finerLoggingEnabled; } /** @return Is finest logging enabled? */ inline bool isFinest() const { return this->finestLoggingEnabled; } /** @return Name of this logger. */ AString getName() const { return this->name; } private: Logger(const AString& name); Logger(const Logger&); Logger& operator=(const Logger&); AString name; bool severeLoggingEnabled; bool warningLoggingEnabled; bool infoLoggingEnabled; bool configLoggingEnabled; bool fineLoggingEnabled; bool finerLoggingEnabled; bool finestLoggingEnabled; LogLevelEnum::Enum level; std::vector logHandlers; public: virtual AString toString() const; private: }; #ifdef __LOGGER_DECLARE__ #endif // __LOGGER_DECLARE__ } // namespace #endif //__LOGGER_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Common/MathFunctionEnum.cxx000066400000000000000000000172501300200146000257510ustar00rootroot00000000000000 /*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 __MATH_FUNCTION_ENUM_DECLARE__ #include "MathFunctionEnum.h" #undef __MATH_FUNCTION_ENUM_DECLARE__ #include "CaretAssert.h" using namespace caret; /** * Constructor. * * @param enumValue * An enumerated value. * @param integerCode * Integer code for this enumerated value. * * @param name * Name of enumerated value. * * @param guiName * User-friendly name for use in user-interface. */ MathFunctionEnum::MathFunctionEnum(const Enum enumValue, const AString& name, const AString& explanation) { this->enumValue = enumValue; this->name = name; this->explanation = explanation; } /** * Destructor. */ MathFunctionEnum::~MathFunctionEnum() { } /** * Initialize the enumerated metadata. */ void MathFunctionEnum::initialize() { if (initializedFlag) { return; } initializedFlag = true; //enumData.push_back(MathFunctionEnum(INVALID, "INVALID"));//should this be in the data? I don't think it should, it is a placeholder for "no matching enum value" enumData.push_back(MathFunctionEnum(SIN, "sin", "1 argument, the sine of the argument (units are radians)")); enumData.push_back(MathFunctionEnum(COS, "cos", "1 argument, the cosine of the argument (units are radians)")); enumData.push_back(MathFunctionEnum(TAN, "tan", "1 argument, the tangent of the argument (units are radians)")); enumData.push_back(MathFunctionEnum(ASIN, "asin", "1 argument, the inverse of sine of the argument, in radians")); enumData.push_back(MathFunctionEnum(ACOS, "acos", "1 argument, the inverse of cosine of the argument, in radians")); enumData.push_back(MathFunctionEnum(ATAN, "atan", "1 argument, the inverse of tangent of the argument, in radians")); enumData.push_back(MathFunctionEnum(ATAN2, "atan2", "2 arguments, atan2(y, x) returns the inverse of tangent of (y/x), in radians, determining quadrant by the sign of both arguments")); enumData.push_back(MathFunctionEnum(SINH, "sinh", "1 argument, the hyperbolic sine of the argument")); enumData.push_back(MathFunctionEnum(COSH, "cosh", "1 argument, the hyperbolic cosine of the argument")); enumData.push_back(MathFunctionEnum(TANH, "tanh", "1 argument, the hyperboloc tangent of the argument")); enumData.push_back(MathFunctionEnum(ASINH, "asinh", "1 argument, the inverse hyperbolic sine of the argument")); enumData.push_back(MathFunctionEnum(ACOSH, "acosh", "1 argument, the inverse hyperbolic cosine of the argument")); enumData.push_back(MathFunctionEnum(ATANH, "atanh", "1 argument, the inverse hyperboloc tangent of the argument")); enumData.push_back(MathFunctionEnum(LN, "ln", "1 argument, the natural logarithm of the argument")); enumData.push_back(MathFunctionEnum(EXP, "exp", "1 argument, the constant e raised to the power of the argument")); enumData.push_back(MathFunctionEnum(LOG, "log", "1 argument, the base 10 logarithm of the argument")); enumData.push_back(MathFunctionEnum(SQRT, "sqrt", "1 argument, the square root of the argument")); enumData.push_back(MathFunctionEnum(ABS, "abs", "1 argument, the absolute value of the argument")); enumData.push_back(MathFunctionEnum(FLOOR, "floor", "1 argument, the largest integer not greater than the argument")); enumData.push_back(MathFunctionEnum(ROUND, "round", "1 argument, the nearest integer, with ties rounded away from zero")); enumData.push_back(MathFunctionEnum(CEIL, "ceil", "1 argument, the smallest integer not less than the argument")); enumData.push_back(MathFunctionEnum(MIN, "min", "2 arguments, min(x, y) returns y if (x > y), x otherwise")); enumData.push_back(MathFunctionEnum(MAX, "max", "2 arguments, max(x, y) returns y if (x < y), x otherwise")); enumData.push_back(MathFunctionEnum(MOD, "mod", "2 arguments, mod(x, y) = x - y * floor(x / y), or 0 if y == 0")); enumData.push_back(MathFunctionEnum(CLAMP, "clamp", "3 arguments, clamp(x, low, high) = min(max(x, low), high)")); } /** * 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 MathFunctionEnum* MathFunctionEnum::findData(const Enum enumValue) { if (initializedFlag == false) initialize(); size_t num = enumData.size(); for (size_t i = 0; i < num; i++) { const MathFunctionEnum* 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 MathFunctionEnum::toName(Enum enumValue) { if (initializedFlag == false) initialize(); const MathFunctionEnum* enumInstance = findData(enumValue); if (enumInstance == NULL) return ""; return enumInstance->name; } /** * Get a string explaining the enumerated type. * @param enumValue * Enumerated value. * @return * String representing enumerated value. */ AString MathFunctionEnum::toExplanation(Enum enumValue) { if (initializedFlag == false) initialize(); const MathFunctionEnum* enumInstance = findData(enumValue); if (enumInstance == NULL) return ""; return enumInstance->explanation; } /** * 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. */ MathFunctionEnum::Enum MathFunctionEnum::fromName(const AString& name, bool* isValidOut) { if (initializedFlag == false) initialize(); bool validFlag = false; Enum enumValue = INVALID; for (std::vector::iterator iter = enumData.begin(); iter != enumData.end(); iter++) { const MathFunctionEnum& 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 MathFunctionEnum")); } 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 * except ALL. */ void MathFunctionEnum::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); } } connectome-workbench-1.2.3+git41-gc4c6c90/src/Common/MathFunctionEnum.h000066400000000000000000000056011300200146000253730ustar00rootroot00000000000000#ifndef __MATH_FUNCTION_ENUM__H_ #define __MATH_FUNCTION_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 { /** * \brief Enumerated type for a structure in a brain. * * Enumerated types for math functions. */ class MathFunctionEnum { public: /** * Enumerated values. */ enum Enum { INVALID, SIN, COS, TAN, ASIN, ACOS, ATAN, ATAN2, SINH, COSH, TANH, ASINH, ACOSH, ATANH, LN, EXP, LOG, SQRT, ABS, FLOOR, ROUND, CEIL, MIN, MAX, MOD, CLAMP }; ~MathFunctionEnum(); static AString toName(Enum enumValue); static AString toExplanation(Enum enumValue); static Enum fromName(const AString& name, bool* isValidOut); static void getAllEnums(std::vector& allEnums); private: MathFunctionEnum(const Enum enumValue, const AString& name, const AString& explanation); static const MathFunctionEnum* 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; /** The enumerated type value for an instance */ Enum enumValue; /** The name, a text string that is identical to the enumerated value */ AString name; /** An explanation of the function */ AString explanation; }; #ifdef __MATH_FUNCTION_ENUM_DECLARE__ std::vector MathFunctionEnum::enumData; bool MathFunctionEnum::initializedFlag = false; #endif // __MATH_FUNCTION_ENUM_DECLARE__ } // namespace #endif //__MATH_FUNCTION_ENUM__H_ connectome-workbench-1.2.3+git41-gc4c6c90/src/Common/MathFunctions.cxx000066400000000000000000002026621300200146000253120ustar00rootroot00000000000000/*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*/ /*========================================================================= Program: Visualization Toolkit Module: $RCSfile: vtkBase64Utilities.h,v $ Copyright (c) Ken Martin, Will Schroeder, Bill Lorensen All rights reserved. See Copyright.txt or http://www.kitware.com/Copyright.htm for details. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the above copyright notice for more information. =========================================================================*/ #include #include #include "MathFunctions.h" #include "CaretAssert.h" #include "CaretLogger.h" using namespace caret; using namespace std; MathFunctions::MathFunctions() : CaretObject() { } /** * Destructor */ MathFunctions::~MathFunctions() { } /** * Calulate the number of combinations for choosing "k" elements * from a total of "n" elements. * * Formula: [n!/(k!*(n-k!)) * If k > (n-k): [n(n-1)(n-2)...(k+1)] / (n-k)! * If k < (n-k): [n(n-1)(n-2)...(n-k+1)] / k! * * @param n - total number of elements. * @param k - number of elements to choose. * @return - number of combinations. * */ int64_t MathFunctions::combinations( const int64_t n, const int64_t k) { int64_t denominator = 1; int64_t nmk = n - k; int64_t iStart = 1; if (k > nmk) { iStart = k + 1; denominator = MathFunctions::factorial(nmk); } else { iStart = nmk + 1; denominator = MathFunctions::factorial(k); } int64_t numerator = 1; for (int64_t i = iStart; i <= n; i++) { numerator *= i; } int64_t numCombosLong = numerator / denominator; int64_t numCombos = (int)numCombosLong; return numCombos; } /** * Calulate the number of permuations for choosing "k" elements * from a total of "n" elements. * * Formula: [n!/(n-m)!] = n(n-1)(n-2)...(n-m+1). * * @param n - total number of elements. * @param k - number of elements to choose. * @return - number of combinations. * */ int64_t MathFunctions::permutations( const int64_t n, const int64_t k) { int64_t iStart = n - k + 1; int numPerms = 1; for (int i = iStart; i <= n; i++) { numPerms *= i; } return numPerms; } /** * Calculate the factorial for a number. * * @param n - the number. * @return - its factiorial * */ int64_t MathFunctions::factorial(const int64_t n) { int64_t num = 1; for (int64_t i = 1; i <= n; i++) { num *= i; } return num; } /** * Compute a normal vector from three vertices and make it a unit vector. * * @param v1 the first vertex, an array of three floats. * @param v2 the first vertex, an array of three floats. * @param v3 the first vertex, an array of three floats. * @param normalVectorOut A three-dimensional array passed into which * the normal vector is loaded. * @return true if vector is valid (non-zero length). * */ bool MathFunctions::normalVector( const float v1[3], const float v2[3], const float v3[3], float normalVectorOut[3]) { /* * DOUBLE PRECISION is needed when points are a small or sliver triangle. */ double a0 = v3[0] - v2[0]; double a1 = v3[1] - v2[1]; double a2 = v3[2] - v2[2]; double b0 = v1[0] - v2[0]; double b1 = v1[1] - v2[1]; double b2 = v1[2] - v2[2]; double nv0 = (a1 * b2 - a2 * b1); double nv1 = (a2 * b0 - a0 * b2); double nv2 = (a0 * b1 - a1 * b0); double length = std::sqrt(nv0*nv0 + nv1*nv1 + nv2*nv2); bool valid = false; if (length != 0.0) { nv0 /= length; nv1 /= length; nv2 /= length; valid = true; } normalVectorOut[0] = (float)nv0; normalVectorOut[1] = (float)nv1; normalVectorOut[2] = (float)nv2; return valid; } /** * Compute a normal vector from three vertices and make it a unit vector. * * @param v1 the first vertex, an array of three floats. * @param v2 the first vertex, an array of three floats. * @param v3 the first vertex, an array of three floats. * @param normalVectorOut A three-dimensional array passed into which * the normal vector is loaded. * @return true if vector is valid (non-zero length). * */ bool MathFunctions::normalVector( const double v1[3], const double v2[3], const double v3[3], double normalVectorOut[3]) { double a0 = v3[0] - v2[0]; double a1 = v3[1] - v2[1]; double a2 = v3[2] - v2[2]; double b0 = v1[0] - v2[0]; double b1 = v1[1] - v2[1]; double b2 = v1[2] - v2[2]; double nv0 = (a1 * b2 - a2 * b1); double nv1 = (a2 * b0 - a0 * b2); double nv2 = (a0 * b1 - a1 * b0); double length = std::sqrt(nv0*nv0 + nv1*nv1 + nv2*nv2); bool valid = false; if (length != 0.0) { nv0 /= length; nv1 /= length; nv2 /= length; valid = true; } normalVectorOut[0] = nv0; normalVectorOut[1] = nv1; normalVectorOut[2] = nv2; return valid; } /** * Compute a normal vector from three vertices and but the returned * vector IS NOT a unit vector. * * @param v1 the first vertex, an array of three floats. * @param v2 the first vertex, an array of three floats. * @param v3 the first vertex, an array of three floats. * @return The normal vector, an array of three floats. * */ void MathFunctions::normalVectorDirection( const float v1[3], const float v2[3], const float v3[3], float directionOut[3]) { float a[] = { v3[0] - v2[0], v3[1] - v2[1], v3[2] - v2[2] }; float b[] = { v1[0] - v2[0], v1[1] - v2[1], v1[2] - v2[2] }; directionOut[0] = (a[1] * b[2] - a[2] * b[1]); directionOut[1] = (a[2] * b[0] - a[0] * b[2]); directionOut[2] = (a[0] * b[1] - a[1] * b[0]); } /** * Cross product of two 3D vectors. * @param v1 The first vector, an array of three floats. * @param v2 The first vector, an array of three floats. * @param resultOut Output containing the cross product. * */ void MathFunctions::crossProduct( const float v1[], const float v2[], float resultOut[]) { resultOut[0] = v1[1] * v2[2] - v1[2] * v2[1]; resultOut[1] = v1[2] * v2[0] - v1[0] * v2[2]; resultOut[2] = v1[0] * v2[1] - v1[1] * v2[0]; } /** * Cross product of two 3D vectors. * @param v1 The first vector, an array of three floats. * @param v2 The first vector, an array of three floats. * @param resultOut Output containing the cross product. * */ void MathFunctions::crossProduct( const double v1[], const double v2[], double resultOut[]) { resultOut[0] = v1[1] * v2[2] - v1[2] * v2[1]; resultOut[1] = v1[2] * v2[0] - v1[0] * v2[2]; resultOut[2] = v1[0] * v2[1] - v1[1] * v2[0]; } /** * Cross product of two 3D vectors with normalizing both the * input and output vectors. * * @param x1 The first vector, an array of three floats. * @param x2 The first vector, an array of three floats. * @return The cross product, an array of three floats. * */ void MathFunctions::normalizedCrossProduct( const float x1[], const float x2[], float resultOut[]) { float v1[3] = { x1[0], x1[1], x1[2] }; MathFunctions::normalizeVector(v1); float v2[3] = { x2[0], x2[1], x2[2] }; MathFunctions::normalizeVector(v2); MathFunctions::crossProduct(v1, v2, resultOut); MathFunctions::normalizeVector(resultOut); } /** * Normalize a 3D vector (make its length 1.0). * * @param vectorsAll Array containing the XYZ components * of the vectors. * @param offset Offset of the vector's X-component in the * vectorsAll array. * @return The length of the vector prior to normalization. * */ float MathFunctions::normalizeVector( float vectorsAll[], const int32_t offset) { float len = MathFunctions::vectorLength(vectorsAll, offset); if (len != 0.0) { vectorsAll[offset] /= len; vectorsAll[offset+1] /= len; vectorsAll[offset+2] /= len; } return len; } /** * Normalize a 3D vector (make its length 1.0). * * @param vectorInOut vector that is normalized. * @return The length of the vector prior to normalization. * */ float MathFunctions::normalizeVector(float vector[3]) { float len = vectorLength(vector); if (len != 0.0) { vector[0] /= len; vector[1] /= len; vector[2] /= len; } return len; } /** * Normalize a 3D vector (make its length 1.0). * * @param vectorInOut vector that is normalized. * @return The length of the vector prior to normalization. * */ double MathFunctions::normalizeVector(double vector[3]) { double len = vectorLength(vector); if (len != 0.0) { vector[0] /= len; vector[1] /= len; vector[2] /= len; } return len; } /** * Get length of vector. * * @param vector Vector whose length is computed. * * @return The length of the vector. * */ float MathFunctions::vectorLength(const float vector[3]) { float len = (float)std::sqrt(vector[0]*vector[0] + vector[1]*vector[1] + vector[2]*vector[2]); return len; } /** * Get length of vector. * * @param vectorsAll Array containing three-dimensional vectors. * @param offset Offset of vector's X-component in vectorsAll array. * * @return The length of the vector. * */ float MathFunctions::vectorLength( const float vectorsAll[], const int32_t offset) { float len = (float)std::sqrt(vectorsAll[offset]*vectorsAll[offset] + vectorsAll[offset+1]*vectorsAll[offset+1] + vectorsAll[offset+2]*vectorsAll[offset+2]); return len; } /** * Get length of vector. * * @param vector Vector whose length is computed. * * @return The length of the vector. * */ double MathFunctions::vectorLength(const double vector[3]) { double len = std::sqrt(vector[0]*vector[0] + vector[1]*vector[1] + vector[2]*vector[2]); return len; } /** * Get the squared distance between two 3D points. * * @param p1 Point 1 (3 element array) * @param p2 Point 2 (3 element array) * @return Distance squared between the two points. * */ float MathFunctions::distanceSquared3D( const float p1[3], const float p2[3]) { float dx = p1[0] - p2[0]; float dy = p1[1] - p2[1]; float dz = p1[2] - p2[2]; float distSQ = dx*dx + dy*dy + dz*dz; return distSQ; } /** * Get the squared distance between two 3D coordinates. * * @param xyzAll Array containing all of the XYZ coordinates. * @param offsetCoord1 Offset of the first coordinates X-coordinate. * @param offsetCoord2 Offset of the second coordinates X-coordinate. * @return Distance squared between the two coordinates. * */ float MathFunctions::distanceSquared3D( const float xyzAll[], const int32_t offsetCoord1, const int32_t offsetCoord2) { float dx = xyzAll[offsetCoord1] - xyzAll[offsetCoord2]; float dy = xyzAll[offsetCoord1+1] - xyzAll[offsetCoord2+1]; float dz = xyzAll[offsetCoord1+2] - xyzAll[offsetCoord2+2]; float distSQ = dx*dx + dy*dy + dz*dz; return distSQ; } /** * Get the distance between two 3D points. * * @param p1 Point 1 (3 element array) * @param p2 Point 2 (3 element array) * @return Distance between the two points. * */ float MathFunctions::distance3D( const float p1[3], const float p2[3]) { float dist = distanceSquared3D(p1, p2); if (dist != 0.0f) { dist = (float)std::sqrt(dist); } return dist; } /** * Get the squared distance between two 3D points. * * @param p1 Point 1 (3 element array) * @param p2 Point 2 (3 element array) * @return Distance squared between the two points. * */ double MathFunctions::distanceSquared3D( const double p1[3], const double p2[3]) { double dx = p1[0] - p2[0]; double dy = p1[1] - p2[1]; double dz = p1[2] - p2[2]; double distSQ = dx*dx + dy*dy + dz*dz; return distSQ; // double dist = distanceSquared3D(p1, p2); // if (dist != 0.0f) { // dist = std::sqrt(dist); // } // return dist; } /** * Get the distance between two 3D points. * * @param p1 Point 1 (3 element array) * @param p2 Point 2 (3 element array) * @return Distance between the two points. * */ double MathFunctions::distance3D( const double p1[3], const double p2[3]) { double dist = distanceSquared3D(p1, p2); if (dist != 0.0f) { dist = std::sqrt(dist); } return dist; } /** * subtract vectors (3d) result = v1 - v2. * @param v1 1st vector input * @param v2 2nd vector input * @return 3D vector containing result of subtraction. * */ void MathFunctions::subtractVectors( const float v1[3], const float v2[3], float resultOut[3]) { resultOut[0] = v1[0] - v2[0]; resultOut[1] = v1[1] - v2[1]; resultOut[2] = v1[2] - v2[2]; } /** * subtract vectors (3d) result = v1 - v2. * @param v1 1st vector input * @param v2 2nd vector input * @return 3D vector containing result of subtraction. * */ void MathFunctions::subtractVectors(const double v1[3], const double v2[3], double resultOut[3]) { resultOut[0] = v1[0] - v2[0]; resultOut[1] = v1[1] - v2[1]; resultOut[2] = v1[2] - v2[2]; } void MathFunctions::addVectors(const float v1[3], const float v2[3], float resultOut[3]) { resultOut[0] = v1[0] + v2[0]; resultOut[1] = v1[1] + v2[1]; resultOut[2] = v1[2] + v2[2]; } /** * Create the unit vector for a vector that starts at startXYZ and * ends at endXYZ. * * @param startXYZ - Starting position of vector. * @param endXYZ - Ending position of vector. * @param Unit vector starting at startXYZ and pointing to endXYZ. * */ void MathFunctions::createUnitVector( const float startXYZ[3], const float endXYZ[3], float resultOut[3]) { resultOut[0] = endXYZ[0] - startXYZ[0]; resultOut[1] = endXYZ[1] - startXYZ[1]; resultOut[2] = endXYZ[2] - startXYZ[2]; MathFunctions::normalizeVector(resultOut); } /** * Create the unit vector for a vector that starts at startXYZ and * ends at endXYZ. * * @param startXYZ - Starting position of vector. * @param endXYZ - Ending position of vector. * @param Unit vector starting at startXYZ and pointing to endXYZ. * */ void MathFunctions::createUnitVector( const double startXYZ[3], const double endXYZ[3], double resultOut[3]) { resultOut[0] = endXYZ[0] - startXYZ[0]; resultOut[1] = endXYZ[1] - startXYZ[1]; resultOut[2] = endXYZ[2] - startXYZ[2]; MathFunctions::normalizeVector(resultOut); } /** * Dot produce of three dimensional vectors. * @param p1 vector 1 * @param p2 vector 2 * @return Dot product of the two vectors. * */ float MathFunctions::dotProduct( const float p1[3], const float p2[3]) { float dot = p1[0]*p2[0] + p1[1]*p2[1] + p1[2]*p2[2]; return dot; } /** * Dot produce of three dimensional vectors. * @param p1 vector 1 * @param p2 vector 2 * @return Dot product of the two vectors. * */ double MathFunctions::dotProduct( const double p1[3], const double p2[3]) { return p1[0]*p2[0] + p1[1]*p2[1] + p1[2]*p2[2]; } /** * Calculate the area for a triangle. * @param v1 - XYZ coordinates for vertex 1 * @param v2 - XYZ coordinates for vertex 2 * @param v3 - XYZ coordinates for vertex 3 * * @return Area of triangle. * */ float MathFunctions::triangleArea( const float v1[3], const float v2[3], const float v3[3]) { /* * Using doubles for the intermediate calculations * produces results different from that if floats * were used in the "area" equation. I'm * assuming double is more accurate (JWH). */ double a = MathFunctions::distanceSquared3D(v1,v2); double b = MathFunctions::distanceSquared3D(v2,v3); double c = MathFunctions::distanceSquared3D(v3,v1); float area = (float)(0.25f* std::sqrt(std::abs(4.0*a*c - (a-b+c)*(a-b+c)))); return area; } /** * Calculate the area for a triangle (with doubles) * @param v1 - XYZ coordinates for vertex 1 * @param v2 - XYZ coordinates for vertex 2 * @param v3 - XYZ coordinates for vertex 3 * * @return Area of triangle. * */ float MathFunctions::triangleArea(const double v1[3], const double v2[3], const double v3[3]) { /* * Using doubles for the intermediate calculations * produces results different from that if floats * were used in the "area" equation. I'm * assuming double is more accurate (JWH). */ double a = MathFunctions::distanceSquared3D(v1,v2); double b = MathFunctions::distanceSquared3D(v2,v3); double c = MathFunctions::distanceSquared3D(v3,v1); float area = (float)(0.25f* std::sqrt(std::abs(4.0*a*c - (a-b+c)*(a-b+c)))); return area; } /** * Calculate the area of a triangle formed by 3 coordinates. * @param xyzAll One-dimensional array containing the XYZ coordinates. * @param offsetCoord1 Offset of node 1's X-coordinate which is * followed by the Y- and Z-coordinates. * @param offsetCoord2 Offset of node 2's X-coordinate which is * followed by the Y- and Z-coordinates. * @param offsetCoord3 Offset of node 3's X-coordinate which is * followed by the Y- and Z-coordinates. * @return Area of the triangle formed by the coordinates. * */ float MathFunctions::triangleArea( const float xyzAll[], const int32_t offsetCoord1, const int32_t offsetCoord2, const int32_t offsetCoord3) { /* * Using doubles for the intermediate calculations * produces results different from that if floats * were used in the "area" equation. I'm * assuming double is more accurate (JWH). */ double a = MathFunctions::distanceSquared3D(xyzAll, offsetCoord1, offsetCoord2); double b = MathFunctions::distanceSquared3D(xyzAll, offsetCoord2, offsetCoord3); double c = MathFunctions::distanceSquared3D(xyzAll, offsetCoord3, offsetCoord1); float area = (float)(0.25f* std::sqrt(std::abs(4.0*a*c - (a-b+c)*(a-b+c)))); return area; } /** * Compute the signed area of a triangle in 2D. * @param p1 - 1st coordinate of triangle * @param p2 - 2nd coordinate of triangle * @param p3 - 3rd coordinate of triangle * @return Signed area of triangle which is positive if the vertices * are in counter-clockwise orientation or negative if the vertices * are in clockwise orientation. * */ float MathFunctions::triangleAreaSigned2D( const float p1[2], 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; return area; } /** * Compute the signed area of a triangle in 3D. * @param referenceNormal - Normal vector. * @param p1 - 1st coordinate of triangle * @param p2 - 2nd coordinate of triangle * @param p3 - 3rd coordinate of triangle * @return Signed area of triangle which is positive if the vertices * are in counter-clockwise orientation or negative if the vertices * are in clockwise orientation. * */ float MathFunctions::triangleAreaSigned3D( const float referenceNormal[3], const float p1[3], const float p2[3], const float p3[3]) { // // Area of the triangle formed by the three points // float area = triangleArea(p1, p2, p3); // // Normal for the three points // float triangleNormal[3]; MathFunctions::normalVector(p1, p2, p3, triangleNormal); // // Dot Product is the cosine of the angle between the two normals. When this value is less // than zero, the absolute angle between the normals is greater than 90 degrees. // float dot = MathFunctions::dotProduct(referenceNormal, triangleNormal); if (dot < 0.0) { area = -area; } return area; } /** * this method solves Ay = x for y. * From vtkMath. * * @param A * 3x3 matrix. * @param x * Input vector. * @param y * Output vector. */ void MathFunctions::vtkLinearSolve3x3( const float A[3][3], const float x[3], float y[3]) { int index[3]; float B[3][3]; for (int i = 0; i < 3; i++) { B[i][0] = A[i][0]; B[i][1] = A[i][1]; B[i][2] = A[i][2]; y[i] = x[i]; } vtkLUFactor3x3(B,index); vtkLUSolve3x3(B,index,y); } /** * Backsubstitution with an LU-decomposed matrix. * From vtkMath. * * @param A * 3x3 matrix. * @param index * * @param x * Output vector. */ void MathFunctions::vtkLUSolve3x3( const float A[3][3], const int32_t index[3], float x[3]) { float sum; // forward substitution sum = x[index[0]]; x[index[0]] = x[0]; x[0] = sum; sum = x[index[1]]; x[index[1]] = x[1]; x[1] = sum - A[1][0]*x[0]; sum = x[index[2]]; x[index[2]] = x[2]; x[2] = sum - A[2][0]*x[0] - A[2][1]*x[1]; // back substitution x[2] = x[2]*A[2][2]; x[1] = (x[1] - A[1][2]*x[2])*A[1][1]; x[0] = (x[0] - A[0][1]*x[1] - A[0][2]*x[2])*A[0][0]; } /** * Unrolled LU factorization of a 3x3 matrix with pivoting. * From vtkMath. * * @param A * 3x3 matrix. * @param index * */ void MathFunctions::vtkLUFactor3x3( float A[3][3], int32_t index[3]) { int i,maxI; float tmp,largest; float scale[3]; // Loop over rows to get implicit scaling information for ( i = 0; i < 3; i++ ) { largest = std::abs(A[i][0]); if ((tmp = std::abs(A[i][1])) > largest) { largest = tmp; } if ((tmp = std::abs(A[i][2])) > largest) { largest = tmp; } scale[i] = (1.0f)/largest; } // Loop over all columns using Crout's method // first column largest = scale[0]*std::abs(A[0][0]); maxI = 0; if ((tmp = scale[1]*std::abs(A[1][0])) >= largest) { largest = tmp; maxI = 1; } if ((tmp = scale[2]*std::abs(A[2][0])) >= largest) { maxI = 2; } if (maxI != 0) { //vtkSwapVectors3(A[maxI],A[0]); float tmpSwap[3] = { A[maxI][0], A[maxI][1], A[maxI][2] }; A[maxI][0] = A[0][0]; A[maxI][1] = A[0][1]; A[maxI][2] = A[0][2]; A[0][0] = tmpSwap[0]; A[0][1] = tmpSwap[1]; A[0][2] = tmpSwap[2]; scale[maxI] = scale[0]; } index[0] = maxI; A[0][0] = (1.0f)/A[0][0]; A[1][0] *= A[0][0]; A[2][0] *= A[0][0]; // second column A[1][1] -= A[1][0]*A[0][1]; A[2][1] -= A[2][0]*A[0][1]; largest = scale[1]*std::abs(A[1][1]); maxI = 1; if ((tmp = scale[2]*std::abs(A[2][1])) >= largest) { maxI = 2; //vtkSwapVectors3(A[2],A[1]); float tmpSwap[3] = { A[2][0], A[2][1], A[2][2] }; A[2][0] = A[1][0]; A[2][1] = A[1][1]; A[2][2] = A[1][2]; A[1][0] = tmpSwap[0]; A[1][1] = tmpSwap[1]; A[1][2] = tmpSwap[2]; scale[2] = scale[1]; } index[1] = maxI; A[1][1] = (1.0f)/A[1][1]; A[2][1] *= A[1][1]; // third column A[1][2] -= A[1][0]*A[0][2]; A[2][2] -= A[2][0]*A[0][2] + A[2][1]*A[1][2]; //largest = scale[2]*std::abs(A[2][2]); index[2] = 2; A[2][2] = (1.0f)/A[2][2]; } /** * 2x2 Determinant. * From vtkMath. * * @param a * element top left. * @param b * element top right. * @param c * element bottom left. * @param d * element bottom right. * @return * The determinant. */ double MathFunctions::vtkDeterminant2x2(double a, double b, double c, double d) { return (a * d - b * c); } /** * Invert 3x3 Matrix. * From vtkMath. * * @param A * Input 3x3 matrix. * @param AI * Output 3x3 matrix. */ void MathFunctions::vtkInvert3x3(const double A[3][3], double AI[3][3]) { double a1 = A[0][0]; double b1 = A[0][1]; double c1 = A[0][2]; double a2 = A[1][0]; double b2 = A[1][1]; double c2 = A[1][2]; double a3 = A[2][0]; double b3 = A[2][1]; double c3 = A[2][2]; // Compute the adjoint double d1 = vtkDeterminant2x2( b2, b3, c2, c3); double d2 = - vtkDeterminant2x2( a2, a3, c2, c3); double d3 = vtkDeterminant2x2( a2, a3, b2, b3); double e1 = - vtkDeterminant2x2( b1, b3, c1, c3); double e2 = vtkDeterminant2x2( a1, a3, c1, c3); double e3 = - vtkDeterminant2x2( a1, a3, b1, b3); double f1 = vtkDeterminant2x2( b1, b2, c1, c2); double f2 = - vtkDeterminant2x2( a1, a2, c1, c2); double f3 = vtkDeterminant2x2( a1, a2, b1, b2); // Divide by the determinant double det = a1*d1 + b1*d2 + c1*d3; AI[0][0] = d1/det; AI[1][0] = d2/det; AI[2][0] = d3/det; AI[0][1] = e1/det; AI[1][1] = e2/det; AI[2][1] = e3/det; AI[0][2] = f1/det; AI[1][2] = f2/det; AI[2][2] = f3/det; } /** * Multipley 3x3 matrices C = A * B. * From vtkMath. * * @param A * 3x3 matrix. * @param B * 3x3 matrix. * @param C * Output 3x3 matrix. */ void MathFunctions::vtkMultiply3x3(const double A[3][3], const double B[3][3], double C[3][3]) { double D[3][3]; for (int i = 0; i < 3; i++) { D[0][i] = A[0][0]*B[0][i] + A[0][1]*B[1][i] + A[0][2]*B[2][i]; D[1][i] = A[1][0]*B[0][i] + A[1][1]*B[1][i] + A[1][2]*B[2][i]; D[2][i] = A[2][0]*B[0][i] + A[2][1]*B[1][i] + A[2][2]*B[2][i]; } for (int j = 0; j < 3; j++) { C[j][0] = D[j][0]; C[j][1] = D[j][1]; C[j][2] = D[j][2]; } } #define VTK_ROTATE(a,i,j,k,l) g=a[i][j];h=a[k][l];a[i][j]=g-s*(h+g*tau);\ a[k][l]=h+s*(g-h*tau) #define VTK_MAX_ROTATIONS 20 //#undef VTK_MAX_ROTATIONS //#define VTK_MAX_ROTATIONS 50 // Jacobi iteration for the solution of eigenvectors/eigenvalues of a nxn // real symmetric matrix. Square nxn matrix a; size of matrix in n; // output eigenvalues in w; and output eigenvectors in v. Resulting // eigenvalues/vectors are sorted in decreasing order; eigenvectors are // normalized. int MathFunctions::vtkJacobiN(double **a, int n, double *w, double **v) { int i, j, k, iq, ip, numPos; double tresh, theta, tau, t, sm, s, h, g, c, tmp; double bspace[4], zspace[4]; double *b = bspace; double *z = zspace; // only allocate memory if the matrix is large if (n > 4) { b = new double[n]; z = new double[n]; } // initialize for (ip=0; ip 3 && (fabs(w[ip])+g) == fabs(w[ip]) && (fabs(w[iq])+g) == fabs(w[iq])) { a[ip][iq] = 0.0; } else if (fabs(a[ip][iq]) > tresh) { h = w[iq] - w[ip]; if ( (fabs(h)+g) == fabs(h)) { t = (a[ip][iq]) / h; } else { theta = 0.5*h / (a[ip][iq]); t = 1.0 / (fabs(theta)+sqrt(1.0+theta*theta)); if (theta < 0.0) { t = -t; } } c = 1.0 / sqrt(1+t*t); s = t*c; tau = s/(1.0+c); h = t*a[ip][iq]; z[ip] -= h; z[iq] += h; w[ip] -= h; w[iq] += h; a[ip][iq]=0.0; // ip already shifted left by 1 unit for (j = 0;j <= ip-1;j++) { VTK_ROTATE(a,j,ip,j,iq); } // ip and iq already shifted left by 1 unit for (j = ip+1;j <= iq-1;j++) { VTK_ROTATE(a,ip,j,j,iq); } // iq already shifted left by 1 unit for (j=iq+1; j= VTK_MAX_ROTATIONS ) { CaretLogWarning("vtkMath::Jacobi: Error extracting eigenfunctions"); return 0; } // sort eigenfunctions these changes do not affect accuracy for (j=0; j= tmp) // why exchage if same? { k = i; tmp = w[k]; } } if (k != j) { w[k] = w[j]; w[j] = tmp; for (i=0; i> 1) + (n & 1); for (j=0; j= 0.0 ) { numPos++; } } // if ( numPos < ceil(double(n)/double(2.0)) ) if ( numPos < ceil_half_n) { for(i=0; i 4) { delete [] b; delete [] z; } return 1; } #undef VTK_ROTATE #undef VTK_MAX_ROTATIONS /** * Given a unit vector 'x', find two other unit vectors 'y' and 'z' which * which form an orthonormal set. * From vtkMath. * * @param x * Unit vector. * @param y * Unit vector. * @param z * Unit vector. * @param theta * Angle? */ void MathFunctions::vtkPerpendiculars(const double x[3], double y[3], double z[3], double theta) { int dx,dy,dz; double x2 = x[0]*x[0]; double y2 = x[1]*x[1]; double z2 = x[2]*x[2]; double r = sqrt(x2 + y2 + z2); // transpose the vector to avoid divide-by-zero error if (x2 > y2 && x2 > z2) { dx = 0; dy = 1; dz = 2; } else if (y2 > z2) { dx = 1; dy = 2; dz = 0; } else { dx = 2; dy = 0; dz = 1; } double a = x[dx]/r; double b = x[dy]/r; double c = x[dz]/r; double tmp = sqrt(a*a+c*c); if (theta != 0) { double sintheta = sin(theta); double costheta = cos(theta); if (y) { y[dx] = (c*costheta - a*b*sintheta)/tmp; y[dy] = sintheta*tmp; y[dz] = (-a*costheta - b*c*sintheta)/tmp; } if (z) { z[dx] = (-c*sintheta - a*b*costheta)/tmp; z[dy] = costheta*tmp; z[dz] = (a*sintheta - b*c*costheta)/tmp; } } else { if (y) { y[dx] = c/tmp; y[dy] = 0; y[dz] = -a/tmp; } if (z) { z[dx] = -a*b/tmp; z[dy] = tmp; z[dz] = -b*c/tmp; } } } /** * Determine if 2D line segments intersect. * Algorithm from http://mathworld.wolfram.com/Line-LineIntersection.html * * @param p1 Line 1 end point 1. * @param p2 Line 1 end point 2. * @param q1 Line 2 end point 1. * @param q2 Line 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 line segments intersect else false. * */ 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]) { 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; double pxMax = std::max(x1, x2) + tol; double pxMin = std::min(x1, x2) - tol; double pyMax = std::max(y1, y2) + tol; double pyMin = std::min(y1, y2) - tol; double qxMax = std::max(x3, x4) + tol; double qxMin = std::min(x3, x4) - tol; double qyMax = std::max(y3, y4) + tol; double qyMin = std::min(y3, y4) - tol; intersectionOut[0] = (float)x; intersectionOut[1] = (float)y; if ((x >= pxMin) && (x <= pxMax) && (x >= qxMin) && (x <= qxMax) && (y >= pyMin) && (y <= pyMax) && (y >= qyMin) && (y <= qyMax)) { return true; } } return false; } /** * Determine if a ray intersects a plane. * @param p1 - 1st point defining the plane * @param p2 - 2nd point defining the plane * @param p3 - 3rd point defining the plane * @param rayOrigin - origin of the ray * @param rayVector - vector defining the ray * @param intersectionXYZandDistance - An array of four that will contain * the XYZ or the intersection point and the distance from the plane. * @return true if the ray intersects the plane, else false. * */ bool MathFunctions::rayIntersectPlane( const float p1[3], const float p2[3], const float p3[3], const float rayOrigin[3], const float rayVector[3], float intersectionXYZandDistance[4]) { // Convert the ray into a unit vector // double ray[3] = { rayVector[0], rayVector[1], rayVector[2] }; MathFunctions::normalizeVector(ray); // // Normal of plane // float normal[3]; MathFunctions::normalVector(p1, p2, p3, normal); // // Compute the plane equation // double A = normal[0]; double B = normal[1]; double C = normal[2]; double D = -(A*p1[0] + B*p1[1] + C*p1[2]); // // Parametric coordinate of where ray intersects plane // double denom = A * ray[0] + B * ray[1] + C * ray[2]; if (denom != 0) { const double t = -(A * rayOrigin[0] + B * rayOrigin[1] + C * rayOrigin[2] + D) / denom; intersectionXYZandDistance[0] = (float)(rayOrigin[0] + ray[0] * t); intersectionXYZandDistance[1] = (float)(rayOrigin[1] + ray[1] * t); intersectionXYZandDistance[2] = (float)(rayOrigin[2] + ray[2] * t); intersectionXYZandDistance[3] = (float)t; return true; } return false; } /** * Project a point to a plane. * @param pt - the point to project. * @param origin - point in the plane. * @param normal - normal vector of plane. * @return The projected position of "pt" on the plane. * */ void MathFunctions::projectPoint( const float pt[3], const float origin[3], const float normal[3], float projectedPointOut[3]) { float xo[3] = { pt[0] - origin[0], pt[1] - origin[1], pt[2] - origin[2] }; float t = MathFunctions::dotProduct(normal, xo); projectedPointOut[0] = pt[0] - t * normal[0]; projectedPointOut[1] = pt[1] - t * normal[1]; projectedPointOut[2] = pt[2] - t * normal[2]; } /** * Get the signed distance from the plane to the "queryPoint". * A positive distance indicates "queryPoint" is above the plane * and a negative distance indicates "queryPoint" is below * the plane. * * @param planeNormal - plane's normal vector. * @param pointInPlane - point on the plane. * @param queryPoint - the query point for which distance to plane is sought. * * @return Distance from the plane to the query point. * */ float MathFunctions::signedDistanceFromPlane( const float planeNormal[3], const float pointInPlane[3], const float queryPoint[3]) { // Find out where query point projects on the plane // float queryPointProjectedOntoPlane[3]; MathFunctions::projectPoint(queryPoint, pointInPlane, planeNormal, queryPointProjectedOntoPlane); float dx = planeNormal[0] * (queryPoint[0] - queryPointProjectedOntoPlane[0]); float dy = planeNormal[1] * (queryPoint[1] - queryPointProjectedOntoPlane[1]); float dz = planeNormal[2] * (queryPoint[2] - queryPointProjectedOntoPlane[2]); float dist = dx + dy + dz; return dist; } /** * Limit the "value" to be inclusively between the minimum and maximum. * @param value - Value for testing. * @param minimumValue - Minimum inclusive value. * @param maximumValue - Maximum inclusive value. * @return Value limited inclusively to the minimum and maximum values. * */ int32_t MathFunctions::limitRange( const int32_t value, const int32_t minimumValue, const int32_t maximumValue) { if (value < minimumValue) { return minimumValue; } if (value > maximumValue) { return maximumValue; } return value; } /** * Limit the "value" to be inclusively between the minimum and maximum. * @param value - Value for testing. * @param minimumValue - Minimum inclusive value. * @param maximumValue - Maximum inclusive value. * @return Value limited inclusively to the minimum and maximum values. * */ float MathFunctions::limitRange( const float value, const float minimumValue, const float maximumValue) { if (value < minimumValue) { return minimumValue; } if (value > maximumValue) { return maximumValue; } return value; } /** * Limit the "value" to be inclusively between the minimum and maximum. * @param value - Value for testing. * @param minimumValue - Minimum inclusive value. * @param maximumValue - Maximum inclusive value. * @return Value limited inclusively to the minimum and maximum values. * */ double MathFunctions::limitRange( const double value, const double minimumValue, const double maximumValue) { if (value < minimumValue) { return minimumValue; } if (value > maximumValue) { return maximumValue; } return value; } /** * Find the distance from the point to the line defined by p1 and p2. * Formula is from * "http://mathworld.wolfram.com/Point-LineDistance3-Dimensional.html". * * @param p1 - First coordinate in line. * @param p2 - Second coordinate in line. * @param point - coordinate for which distance to line is sought. * @return Distance from point to the line (p1, p2). * */ float MathFunctions::distanceToLine3D( const float p1[3], const float p2[3], const float point[3]) { float dv2v1[3]; MathFunctions::subtractVectors(p2, p1, dv2v1); float dv1pt[3]; MathFunctions::subtractVectors(p1, point, dv1pt); float crossed[3]; MathFunctions::crossProduct(dv2v1, dv1pt, crossed); float numerator = MathFunctions::vectorLength(crossed); float denomenator = MathFunctions::vectorLength(dv2v1); float dist = numerator / denomenator; return dist; } /** * Determine if two arrays are equal, same number of elements and * corresponding elements equal. * * @param a - first array. * @param b - second array. * @return true if arrays are equal, else false. * */ bool MathFunctions::arraysEqual( const float a[], const float b[], const int numElements) { for (int i = 0; i < numElements; i++) { if (a[i] != b[i]) { return false; } } return true; } /** * Get the average of two coordinates. * @param c1 - coordinate 1 * @param c2 - coordinate 2 * @param outputAverage A three-dimensional array into * which the average of the two coordinates is * placed. * */ void MathFunctions::averageOfTwoCoordinates(const float c1[3], const float c2[3], float outputAverage[3]) { outputAverage[0] = (c1[0] + c2[0]) / 2.0f; outputAverage[1] = (c1[1] + c2[1]) / 2.0f; outputAverage[2] = (c1[2] + c2[2]) / 2.0f; } /** * Get the average of three coordinates. * @param c1 - coordinate 1 * @param c2 - coordinate 2 * @param c3 - coordinate 3 * @param outputAverage A three-dimensional array into * which the average of the three coordinates is * placed. * */ void MathFunctions::averageOfThreeCoordinates( const float c1[3], const float c2[3], const float c3[3], float outputAverage[3]) { outputAverage[0] = (c1[0] + c2[0] + c3[0]) / 3.0f; outputAverage[1] = (c1[1] + c2[1] + c3[1]) / 3.0f; outputAverage[2] = (c1[2] + c2[2] + c3[2]) / 3.0f; } /** * Calculate the average of 3 coordinates. * @param xyzAll One-dimensional array containing the XYZ coordinates. * @param offsetCoord1 Offset of node 1's X-coordinate which is * followed by the Y- and Z-coordinates. * @param offsetCoord2 Offset of node 2's X-coordinate which is * followed by the Y- and Z-coordinates. * @param offsetCoord3 Offset of node 3's X-coordinate which is * followed by the Y- and Z-coordinates. * @param outputAverage 3 dimensional array passed in, into which * the average is placed. * @param outputOffset Offset of average into outputAverage array. * */ void MathFunctions::averageOfThreeCoordinates( const float xyzAll[], const int32_t offsetCoord1, const int32_t offsetCoord2, const int32_t offsetCoord3, float outputAverage[], const int32_t outputOffset) { outputAverage[outputOffset] = (xyzAll[offsetCoord1] + xyzAll[offsetCoord2] + xyzAll[offsetCoord3]) / 3.0f; outputAverage[outputOffset+1] = (xyzAll[offsetCoord1+1] + xyzAll[offsetCoord2+1] + xyzAll[offsetCoord3+1]) / 3.0f; outputAverage[outputOffset+2] = (xyzAll[offsetCoord1+2] + xyzAll[offsetCoord2+2] + xyzAll[offsetCoord3+2]) / 3.0f; } /** * Get the average of four coordinates. * @param c1 - coordinate 1 * @param c2 - coordinate 2 * @param c3 - coordinate 1 * @param c4 - coordinate 2 * @param outputAverage A three-dimensional array into * which the average of the four coordinates is * placed. * */ void MathFunctions::averageOfFourCoordinates(const float c1[3], const float c2[3], const float c3[3], const float c4[3], float outputAverage[3]) { outputAverage[0] = (c1[0] + c2[0] + c3[0] + c4[0]) / 4.0f; outputAverage[1] = (c1[1] + c2[1] + c3[1] + c4[1]) / 4.0f; outputAverage[2] = (c1[2] + c2[2] + c3[2] + c4[2]) / 4.0f; } /** * Angle formed by p1, p2, p3 (angle at p2). Returned angle is in radians. * This method uses Java Math.acos() and produces highly accurate results. * @param p1 - point. * @param p2 - point. * @param p3 - point. * @return Angle formed by points. * */ float MathFunctions::angle( const float p1[3], const float p2[3], const float p3[3]) { // // Vector from P2 to P1 // float v21[3] = { p1[0] - p2[0], p1[1] - p2[1], p1[2] - p2[2] }; // // Vector from P2 to P3 // float v23[3] = { p3[0] - p2[0], p3[1] - p2[1], p3[2] - p2[2] }; // // Normalize the vectors // float v21len = MathFunctions::normalizeVector(v21); float v23len = MathFunctions::normalizeVector(v23); float angleOut = 0.0f; if ((v21len > 0.0) && (v23len > 0.0)) { // // angle is inverse cosine of the dot product // and be sure to handle numerical errors. // float dot = MathFunctions::dotProduct(v21, v23); if (dot > 1.0f) dot = 1.0f; else if (dot < -1.0f) dot = -1.0f; angleOut = (float)std::acos(dot); } return angleOut; } /** * Signed angle for "jik". * @param pi - point. * @param pj - point. * @param pk - point. * @param n - normal * @return signed angle formed by the points. * */ float MathFunctions::signedAngle( const float pi[3], const float pj[3], const float pk[3], const float n[3]) { float x1 = pj[0] - pi[0]; float y1 = pj[1] - pi[1]; float z1 = pj[2] - pi[2]; float x2 = pk[0] - pi[0]; float y2 = pk[1] - pi[1]; float z2 = pk[2] - pi[2]; /* s = |(ji)||(ki)| sin(phi) by cross product */ float dx = y1*z2 - y2*z1; float dy = x2*z1 - x1*z2; float dz = x1*y2 - x2*y1; float t = (dx*n[0]) + (dy*n[1]) + (dz*n[2]); float s = (float)std::sqrt((dx*dx) + (dy*dy) + (dz*dz)); if (t < 0.0f) { s = -s; } /* c = |(ji)||(ki)| cos(phi) by inner product */ float c = x1*x2 + y1*y2 + z1*z2; float phi = (float)std::atan2(s,c); return phi; } /** * Determine if an integer is an odd number. * @param number Integer to test. * @return true if integer is odd, else false. * */ bool MathFunctions::isOddNumber(const int32_t number) { bool result = ((number & 1) != 0); return result; } /** * Determine if an integer is an odd number. * @param number Integer to test. * @return true if integer is odd, else false. * */ bool MathFunctions::isEvenNumber(const int32_t number) { bool result = ((number & 1) == 0); return result; } /** * Determine if two arrays are equal. * @param a1 First array. * @param a2 Second array. * @param tolerance Allowable difference in elements at same index. * @return true if arrays are of same length and corresponding * elements have a difference less than tolerance. * */ bool MathFunctions::compareArrays( const float a1[], const float a2[], const int numElements, const float tolerance) { for (int i = 0; i < numElements; i++) { float diff = a1[i] - a2[i]; if (diff < 0.0f) diff = -diff; if (diff > tolerance) { return false; } } return true; } /** * Clamp a value to the range minimum to maximum. * @param value Value for clamping. * @param minimum Minimum allowed value. * @param maximum Maximum allowed value. * @return Value clamped to minimum and maximum. * */ int32_t MathFunctions::clamp( const int32_t value, const int32_t minimum, const int32_t maximum) { return MathFunctions::limitRange(value, minimum, maximum); } /** * Clamp a value to the range minimum to maximum. * @param value Value for clamping. * @param minimum Minimum allowed value. * @param maximum Maximum allowed value. * @return Value clamped to minimum and maximum. * */ float MathFunctions::clamp( const float value, const float minimum, const float maximum) { return MathFunctions::limitRange(value, minimum, maximum); } /** * convert degrees to radians. * @param * degrees value converted to radians. * @return * the corresponding radians value. */ float MathFunctions::toRadians(float degrees) { float radians = degrees * (M_PI / 180.0f); return radians; } /** * convert radians to degrees. * @param * degrees value converted to degrees. * @return * the corresponding degrees value. */ float MathFunctions::toDegrees(float radians) { float degrees = radians * (180.0f / M_PI); return degrees; } /** * Distance SQUARED from (x1, y1) to (x2, y2) * @param X-coordinate of first point. * @param Y-coordinate of first point. * @param X-coordinate of second point. * @param Y-coordinate of second point. * @return Distance squared between the points. */ double MathFunctions::distanceSquared2D(const double x1, const double y1, const double x2, const double y2) { const double dx = x2 - x1; const double dy = y2 - y1; const double d = (dx*dx) + (dy*dy); return d; } uint32_t MathFunctions::gcd(uint32_t num1, uint32_t num2) { if (num1 == 0 || num2 == 0) {//catch zeros return 0;//gcd(0,x)=gcd(x,0)=0, seems less confusing than returning x } //modulus method for good worst-case asymptotic performance uint32_t temp; if (num2 > num1)//num1 kept as the larger number to simplify the code { temp = num1; num1 = num2; num2 = temp; } while (num2) {//maintain num2 as the smaller number temp = num1 % num2;//modulus to reduce the larger as much as possible, result will be smaller than num2 num1 = num2;//so, we need to swap them num2 = temp;//when result becomes zero, num1 is our gcd } return num1; } bool MathFunctions::isInf(const float number) { return (abs(number) == numeric_limits::infinity()); } bool MathFunctions::isNaN(const float number) { return (number != number); } bool MathFunctions::isNegInf(const float number) { return (number == -numeric_limits::infinity()); } bool MathFunctions::isNumeric(const float number) { return (!isNaN(number) && !isInf(number)); } bool MathFunctions::isPosInf(const float number) { return (number > 1.0f && number * 2.0f == number); } void MathFunctions::quaternToMatrix(const float cijk[4], float matrix[3][3]) {//formula from http://en.wikipedia.org/wiki/Rotation_matrix#Quaternion double qlengthsqr = cijk[0] * cijk[0] + cijk[1] * cijk[1] + cijk[2] * cijk[2] + cijk[3] * cijk[3]; double mult = 0.0; if (qlengthsqr > 0.0f) { mult = 2.0f / qlengthsqr; } double ijkmult[4] = { cijk[1] * mult, cijk[2] * mult, cijk[3] * mult }; double wX = cijk[0] * ijkmult[0], wY = cijk[0] * ijkmult[1], wZ = cijk[0] * ijkmult[2]; double xX = cijk[1] * ijkmult[0], xY = cijk[1] * ijkmult[1], xZ = cijk[1] * ijkmult[2]; double yY = cijk[2] * ijkmult[1], yZ = cijk[2] * ijkmult[2]; double zZ = cijk[3] * ijkmult[2]; matrix[0][0] = 1.0 - (yY + zZ);//equals nifti1 formula because for unit quaternion, a*a + b*b + c*c + d*d = 1, and yY = 2 * c*c matrix[0][1] = xY - wZ; matrix[0][2] = xZ + wY; matrix[1][0] = xY + wZ; matrix[1][1] = 1.0 - (xX + zZ); matrix[1][2] = yZ - wX; matrix[2][0] = xZ - wY; matrix[2][1] = yZ + wX; matrix[2][2] = 1.0 - (xX + yY); } void MathFunctions::quaternToMatrix(const double cijk[4], double matrix[3][3]) {//formula from http://en.wikipedia.org/wiki/Rotation_matrix#Quaternion double qlengthsqr = cijk[0] * cijk[0] + cijk[1] * cijk[1] + cijk[2] * cijk[2] + cijk[3] * cijk[3]; double mult = 0.0; if (qlengthsqr > 0.0f) { mult = 2.0f / qlengthsqr; } double ijkmult[4] = { cijk[1] * mult, cijk[2] * mult, cijk[3] * mult }; double wX = cijk[0] * ijkmult[0], wY = cijk[0] * ijkmult[1], wZ = cijk[0] * ijkmult[2]; double xX = cijk[1] * ijkmult[0], xY = cijk[1] * ijkmult[1], xZ = cijk[1] * ijkmult[2]; double yY = cijk[2] * ijkmult[1], yZ = cijk[2] * ijkmult[2]; double zZ = cijk[3] * ijkmult[2]; matrix[0][0] = 1.0 - (yY + zZ);//equals nifti1 formula because for unit quaternion, a*a + b*b + c*c + d*d = 1, and yY = 2 * c*c matrix[0][1] = xY - wZ; matrix[0][2] = xZ + wY; matrix[1][0] = xY + wZ; matrix[1][1] = 1.0 - (xX + zZ); matrix[1][2] = yZ - wX; matrix[2][0] = xZ - wY; matrix[2][1] = yZ + wX; matrix[2][2] = 1.0 - (xX + yY); } bool MathFunctions::matrixToQuatern(const float matrix[3][3], float cijk[4]) {//formulas from http://en.wikipedia.org/wiki/Rotation_matrix#Quaternion const float toler = 0.0001f; float ivec[3] = { matrix[0][0], matrix[1][0], matrix[2][0] }; float jvec[3] = { matrix[0][1], matrix[1][1], matrix[2][1] }; float kvec[3] = { matrix[0][2], matrix[1][2], matrix[2][2] }; if (!(std::abs(1.0f - normalizeVector(ivec)) <= toler)) return false;//use the "not less than or equal to" trick to catch NaNs if (!(std::abs(1.0f - normalizeVector(jvec)) <= toler)) return false; if (!(std::abs(1.0f - normalizeVector(kvec)) <= toler)) return false; if (!(dotProduct(ivec, jvec) <= toler)) return false; if (!(dotProduct(ivec, kvec) <= toler)) return false; if (!(dotProduct(jvec, kvec) <= toler)) return false; float tempvec[3]; crossProduct(ivec, jvec, tempvec); if (!(dotProduct(tempvec, kvec) >= 0.9f)) return false;//i cross j must be k, otherwise it contains a flip int method = 0; double trace = matrix[0][0] + matrix[1][1] + matrix[2][2]; if (trace < 0.0) { method = 1; float tempf = matrix[0][0]; if (matrix[1][1] > tempf) { method = 2; tempf = matrix[1][1]; } if (matrix[2][2] > tempf) { method = 3; } } switch (method) { case 0: { double r = std::sqrt(1.0 + trace); double s = 0.5 / r; cijk[0] = 0.5 * r; cijk[1] = (matrix[2][1] - matrix[1][2]) * s; cijk[2] = (matrix[0][2] - matrix[2][0]) * s; cijk[3] = (matrix[1][0] - matrix[0][1]) * s; } break; case 1: { double r = std::sqrt(1.0 + matrix[0][0] - matrix[1][1] - matrix[2][2]); double s = 0.5 / r; cijk[0] = (matrix[2][1] - matrix[1][2]) * s; cijk[1] = 0.5 * r; cijk[2] = (matrix[0][1] + matrix[1][0]) * s; cijk[3] = (matrix[2][0] + matrix[0][2]) * s; } break; case 2: {//DISCLAIMER: these last two were worked out by pattern since they aren't on wikipedia double r = std::sqrt(1.0 - matrix[0][0] + matrix[1][1] - matrix[2][2]); double s = 0.5 / r; cijk[0] = (matrix[0][2] - matrix[2][0]) * s; cijk[1] = (matrix[0][1] + matrix[1][0]) * s; cijk[2] = 0.5 * r; cijk[3] = (matrix[1][2] + matrix[2][1]) * s; } break; case 3: { double r = std::sqrt(1.0 - matrix[0][0] - matrix[1][1] + matrix[2][2]); double s = 0.5 / r; cijk[0] = (matrix[1][0] - matrix[0][1]) * s; cijk[1] = (matrix[2][0] + matrix[0][2]) * s; cijk[2] = (matrix[1][2] + matrix[2][1]) * s; cijk[3] = 0.5 * r; } break; default: return false; } if (cijk[0] < 0.0f) { cijk[0] = -cijk[0]; cijk[1] = -cijk[1]; cijk[2] = -cijk[2]; cijk[3] = -cijk[3]; } return true; } bool MathFunctions::matrixToQuatern(const double matrix[3][3], double cijk[4]) {//formulas from http://en.wikipedia.org/wiki/Rotation_matrix#Quaternion const float toler = 0.0001f; double ivec[3] = { matrix[0][0], matrix[1][0], matrix[2][0] }; double jvec[3] = { matrix[0][1], matrix[1][1], matrix[2][1] }; double kvec[3] = { matrix[0][2], matrix[1][2], matrix[2][2] }; if (!(std::abs(1.0f - normalizeVector(ivec)) <= toler)) return false;//use the "not less than or equal to" trick to catch NaNs if (!(std::abs(1.0f - normalizeVector(jvec)) <= toler)) return false; if (!(std::abs(1.0f - normalizeVector(kvec)) <= toler)) return false; if (!(dotProduct(ivec, jvec) <= toler)) return false; if (!(dotProduct(ivec, kvec) <= toler)) return false; if (!(dotProduct(jvec, kvec) <= toler)) return false; double tempvec[3]; crossProduct(ivec, jvec, tempvec); if (!(dotProduct(tempvec, kvec) >= 0.9f)) return false;//i cross j must be k, otherwise it contains a flip int method = 0; double trace = matrix[0][0] + matrix[1][1] + matrix[2][2]; if (trace < 0.0) { method = 1; float tempf = matrix[0][0]; if (matrix[1][1] > tempf) { method = 2; tempf = matrix[1][1]; } if (matrix[2][2] > tempf) { method = 3; } } switch (method) { case 0: { double r = std::sqrt(1.0 + trace); double s = 0.5 / r; cijk[0] = 0.5 * r; cijk[1] = (matrix[2][1] - matrix[1][2]) * s; cijk[2] = (matrix[0][2] - matrix[2][0]) * s; cijk[3] = (matrix[1][0] - matrix[0][1]) * s; } break; case 1: { double r = std::sqrt(1.0 + matrix[0][0] - matrix[1][1] - matrix[2][2]); double s = 0.5 / r; cijk[0] = (matrix[2][1] - matrix[1][2]) * s; cijk[1] = 0.5 * r; cijk[2] = (matrix[0][1] + matrix[1][0]) * s; cijk[3] = (matrix[2][0] + matrix[0][2]) * s; } break; case 2: {//DISCLAIMER: these last two were worked out by pattern since they aren't on wikipedia double r = std::sqrt(1.0 - matrix[0][0] + matrix[1][1] - matrix[2][2]); double s = 0.5 / r; cijk[0] = (matrix[0][2] - matrix[2][0]) * s; cijk[1] = (matrix[0][1] + matrix[1][0]) * s; cijk[2] = 0.5 * r; cijk[3] = (matrix[1][2] + matrix[2][1]) * s; } break; case 3: { double r = std::sqrt(1.0 - matrix[0][0] - matrix[1][1] + matrix[2][2]); double s = 0.5 / r; cijk[0] = (matrix[1][0] - matrix[0][1]) * s; cijk[1] = (matrix[2][0] + matrix[0][2]) * s; cijk[2] = (matrix[1][2] + matrix[2][1]) * s; cijk[3] = 0.5 * r; } break; default: return false; } if (cijk[0] < 0.0f) { cijk[0] = -cijk[0]; cijk[1] = -cijk[1]; cijk[2] = -cijk[2]; cijk[3] = -cijk[3]; } return true; } /** * Return the remainder from the resulting division using the given values. * * This method is written to match the result produced by the remainder() * function that is part of C99 but not supported on all platforms. * * Code may appear verbose but it avoid functions calls to fabs(), ceil(), * and floor(). * * Note: X is the numerator. * Y is the denominator. * * The remainder() functions compute the value r such that r = x - n*y, * where n is the integer nearest the exact value of x/y. * * If there are two integers closest to x/y, n shall be the even one. * * @param numerator * The numerator. * @param denominator * The denominator. * @return * The remainder from numerator divided by denominator. */ double MathFunctions::remainder(const double numerator, const double denominator) { if (denominator == 0.0) { return 0.0; } const double quotient = numerator / denominator; /* * Integer value greater than or equal to the quotient * and its difference with the quotient (ceiling) */ const int64_t nearestIntegerOne = static_cast(quotient + 0.5); double diffOne = quotient - nearestIntegerOne; if (diffOne < 0.0) diffOne = -diffOne; /* * Integer value less than or equal to the quotient * and its difference with the quotient (floor) */ const int64_t nearestIntegerTwo = static_cast(quotient - 0.5); double diffTwo = quotient - nearestIntegerTwo; if (diffTwo < 0.0) diffTwo = -diffTwo; /* * Helps determine if the two integer value are the same * distance from the quotient (value will be very close * to zero). */ double diffOneTwo = diffOne - diffTwo; if (diffOneTwo < 0.0) diffOneTwo = -diffOneTwo; int64_t nearestInteger = 0; /* * If the two integer values are the same distance from zero */ if (diffOneTwo < 0.000001) { /* * Use the integer that is even. * Note that if an integer is even, first bit is zero. */ if ((nearestIntegerOne & 1) == 0) { nearestInteger = nearestIntegerOne; } else { nearestInteger = nearestIntegerTwo; } } else if (diffOne < diffTwo) { nearestInteger = nearestIntegerOne; } else { nearestInteger = nearestIntegerTwo; } const double remainderValue = numerator - nearestInteger * denominator; return remainderValue; } /** * Return the value rounded to the nearest integral (integer) value. * * @param value * Value that is rounded. * @return * Value rounded to nearest integral value. */ double MathFunctions::round(const double value) { if (value < 0.0) { return std::ceil(value - 0.5f); } return std::floor(value + 0.5f); } float MathFunctions::q_func(const float& x) {//when using c++11 or later, could use erfc instead of this approximation if (x == 0.0f) return 0.5f;//below approximation is NaN for 0! if (isInf(x)) { if (x > 0.0f) return 0;//inf return 1;//-inf } float ret; if (x < 0.0f) { ret = 1.0f - (1.0f - exp(1.4f * x)) * exp(-x * x / 2) / (x * -1.135f * sqrt(2 * 3.1415926f)); } else { ret = (1.0f - exp(-1.4f * x)) * exp(-x * x / 2) / (x * 1.135f * sqrt(2 * 3.1415926f)); } //formula from http://en.wikipedia.org/wiki/Q-function //references http://users.auth.gr/users/9/3/028239/public_html/pdf/Q_Approxim.pdf //which gives formula and constants for erfc, need to substitute and simplify //however, is wrong for negatives, so we substitute -x and subtract from 1 if (!isNumeric(ret)) { CaretAssert(abs(x) < 0.00001f);//should only be possible for very small inputs, so check before returning the answer for 0 return 0.5f; } return ret; } connectome-workbench-1.2.3+git41-gc4c6c90/src/Common/MathFunctions.h000066400000000000000000000313661300200146000247400ustar00rootroot00000000000000#ifndef __MATHFUNCTIONS_H__ #define __MATHFUNCTIONS_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*/ /*========================================================================= Program: Visualization Toolkit Module: $RCSfile: vtkBase64Utilities.h,v $ Copyright (c) Ken Martin, Will Schroeder, Bill Lorensen All rights reserved. See Copyright.txt or http://www.kitware.com/Copyright.htm for details. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the above copyright notice for more information. =========================================================================*/ #include "CaretObject.h" #include namespace caret { /** * Various mathematical functions. */ class MathFunctions : public CaretObject { private: MathFunctions(); public: virtual ~MathFunctions(); public: static int64_t combinations( const int64_t n, const int64_t k); static int64_t permutations( const int64_t n, const int64_t k); static int64_t factorial(const int64_t n); static bool normalVector( const float v1[3], const float v2[3], const float v3[3], float normalVectorOut[3]); static bool normalVector( const double v1[3], const double v2[3], const double v3[3], double normalVectorOut[3]); static void normalVectorDirection( const float v1[3], const float v2[3], const float v3[3], float directionOut[3]); static void crossProduct( const float v1[], const float v2[], float resultOut[]); static void crossProduct( const double v1[], const double v2[], double resultOut[]); static void normalizedCrossProduct( const float x1[], const float x2[], float resultOut[]); static float normalizeVector( float vectorsAll[], const int32_t offset); static float normalizeVector(float vectorInOut[3]); static double normalizeVector(double vectorInOut[3]); static float vectorLength(const float vector[3]); static float vectorLength( const float vectorsAll[], const int32_t offset); static double vectorLength(const double vector[3]); static float distanceSquared3D( const float p1[3], const float p2[3]); static float distanceSquared3D( const float xyzAll[], const int32_t offsetCoord1, const int32_t offsetCoord2); static float distance3D( const float p1[3], const float p2[3]); static double distanceSquared3D( const double p1[3], const double p2[3]); static double distance3D( const double p1[3], const double p2[3]); static double distanceSquared2D(const double x1, const double y1, const double x2, const double y2); static void subtractVectors(const float v1[3], const float v2[3], float resultOut[3]); static void subtractVectors(const double v1[3], const double v2[3], double resultOut[3]); static void addVectors( const float v1[3], const float v2[3], float resultOut[3]); static void createUnitVector( const float startXYZ[3], const float endXYZ[3], float unitVectorOut[3]); static void createUnitVector( const double startXYZ[3], const double endXYZ[3], double unitVectorOut[3]); static float dotProduct( const float p1[3], const float p2[3]); static double dotProduct( const double p1[3], const double p2[3]); static float triangleArea( const float v1[3], const float v2[3], const float v3[3]); static float triangleArea(const double v1[3], const double v2[3], const double v3[3]); static float triangleArea( const float xyzAll[], const int32_t offsetCoord1, const int32_t offsetCoord2, const int32_t offsetCoord3); static float triangleAreaSigned2D( const float p1[3], const float p2[3], const float p3[3]); static float triangleAreaSigned3D( const float referenceNormal[3], const float p1[3], const float p2[3], const float p3[3]); static void vtkLinearSolve3x3( const float A[3][3], const float x[], float y[]); static void vtkLUSolve3x3( const float A[3][3], const int32_t index[], float x[]); static void vtkLUFactor3x3( float A[3][3], int32_t index[]); static void vtkInvert3x3(const double A[3][3], double AI[3][3]); static void vtkMultiply3x3(const double A[3][3], const double B[3][3], double C[3][3]); static int vtkJacobiN(double **a, int n, double *w, double **v); static void vtkPerpendiculars(const double x[3], double y[3], double z[3], double theta); static double vtkDeterminant2x2(double a, double b, double c, double d); static bool lineIntersection2D( 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], const float p3[3], const float rayOrigin[3], const float rayVector[3], float intersectionXYZandDistance[3]); static void projectPoint( const float pt[3], const float origin[3], const float normal[3], float projectedPointOut[3]); static float signedDistanceFromPlane( const float planeNormal[3], const float pointInPlane[3], const float queryPoint[3]); static int32_t limitRange( const int32_t value, const int32_t minimumValue, const int32_t maximumValue); static float limitRange( const float value, const float minimumValue, const float maximumValue); static double limitRange( const double value, const double minimumValue, const double maximumValue); static float distanceToLine3D( const float p1[3], const float p2[3], const float point[3]); static bool arraysEqual( const float a[], const float b[], const int32_t numElements); static void averageOfTwoCoordinates( const float c1[3], const float c2[3], float outputAverage[3]); static void averageOfThreeCoordinates( const float c1[3], const float c2[3], const float c3[3], float outputAverage[3]); static void averageOfThreeCoordinates( const float xyzAll[], const int32_t offsetCoord1, const int32_t offsetCoord2, const int32_t offsetCoord3, float outputAverage[], const int32_t outputOffset); static void averageOfFourCoordinates(const float c1[3], const float c2[3], const float c3[3], const float c4[3], float outputAverage[3]); static float angle( const float p1[3], const float p2[3], const float p3[3]); static float signedAngle( const float pi[3], const float pj[3], const float pk[3], const float n[3]); static bool isOddNumber(const int32_t number); static bool isEvenNumber(const int32_t number); static bool isNaN(const float number); static bool isPosInf(const float number); static bool isNegInf(const float number); ///true if either inf or -inf static bool isInf(const float number); ///true only if not NaN, inf, or -inf static bool isNumeric(const float number); static bool compareArrays( const float a1[], const float a2[], const int32_t numElemets, const float tolerance); static int32_t clamp( const int32_t value, const int32_t minimum, const int32_t maximum); static float clamp( const float value, const float minimum, const float maximum); static float toRadians(float angle); static float toDegrees(float radians); ///greatest common divisor static uint32_t gcd(uint32_t num1, uint32_t num2); /** * Is the value very, very close to zero? * @param value * Value to test. * @return true if approximately zero, else false. */ static inline bool isZero(const float value) { if (value > 0.00001) return false; if (value < -0.00001) return false; return true; } ///convert quaternion to rotation matrix static void quaternToMatrix(const float cijk[4], float matrix[3][3]); ///convert quaternion to rotation matrix static void quaternToMatrix(const double cijk[4], double matrix[3][3]); ///try to convert 3x3 matrix to quaternion (return false if not a rotation matrix) static bool matrixToQuatern(const float matrix[3][3], float cijk[4]); ///try to convert 3x3 matrix to quaternion (return false if not a rotation matrix) static bool matrixToQuatern(const double matrix[3][3], double cijk[4]); static double remainder(const double numerator, const double denominator); static double round(const double value); ///one minus cdf of standard normal distribution static float q_func(const float& x); }; } // namespace #endif // __MATHFUNCTIONS_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Common/MatrixFunctions.h000066400000000000000000000571401300200146000253110ustar00rootroot00000000000000#ifndef __MATRIX_UTILITIES_H__ #define __MATRIX_UTILITIES_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 "stdint.h" using namespace std; //because I don't want to type std:: every other line //NOTICE: this is not intended to be used outisde of FloatMatrix.cxx and DoubleMatrix.cxx, use at your own risk //NOTICE: this is NOT meant to be as error friendly as matlab, it will check some things, error condition is a 0x0 matrix result //if a matrix has a row shorter than the first row, expect a segfault. Calling checkDim will look for this, but it is a relatively slow operation to do on every input, so it is not used internally. namespace caret { class MatrixFunctions { typedef int64_t msize_t;//NOTE: must be signed due to using -1 as a sentinel public: /// /// matrix multiplication /// template static void multiply(const vector > &left, const vector > &right, vector > &result); /// /// scalar multiplication /// template static void multiply(const vector > &left, const T2 right, vector > &result); /// /// reduced row echelon form /// template static void rref(vector > &inout); /// /// matrix inversion - wrapper to rref for now /// template static void inverse(const vector > &in, vector > &result); /// /// matrix addition - for simple code /// template static void add(const vector > &left, const vector > &right, vector > &result); /// /// scalar addition - for simple code /// template static void add(const vector > &left, const T2 right, vector > &result); /// /// matrix subtraction - for simple code /// template static void subtract(const vector > &left, const vector > &right, vector > &result); /// /// transpose - for simple code /// template static void transpose(const vector > &in, vector > &result); /// /// debugging - verify matrix is rectangular and show its dimensions - returns true if rectangular /// template static bool checkDim(const vector > &in); /// /// allocate a matrix, don't initialize /// template static void resize(const msize_t rows, const msize_t columns, vector > &result, bool destructive = false); /// /// allocate a matrix of specified size /// template static void zeros(const msize_t rows, const msize_t columns, vector > &result); /// /// allocate a matrix of specified size /// template static void ones(const msize_t rows, const msize_t columns, vector > &result); /// /// make an identity matrix /// template static void identity(const msize_t size, vector > &result); /// /// horizontally concatenate matrices /// template static void horizCat(const vector > &left, const vector > &right, vector > &result); /// /// vertically concatenate matrices /// template static void vertCat(const vector > &top, const vector > &bottom, vector > &result); /// /// grab a piece of a matrix /// template static void getChunk(const msize_t firstrow, const msize_t lastrow, const msize_t firstcol, const msize_t lastcol, const vector > &in, vector > &result); private: /// /// reduced row echelon form that is faster on larger matrices, is called by rref() if the matrix is big enough /// template static void rref_big(vector > &inout); }; template void MatrixFunctions::multiply(const vector >& left, const vector >& right, vector >& result) {//the stupid multiply O(n^3) - the O(n^2.78) version might not be that hard to implement with the other functions here, but not as stable msize_t leftrows = (msize_t)left.size(), rightrows = (msize_t)right.size(), leftcols, rightcols; vector > tempstorage, *tresult = &result;//pointer because you can't change a reference bool copyout = false; if (&left == &result || &right == &result) { copyout = true; tresult = &tempstorage; } if (leftrows && rightrows) { leftcols = (msize_t)left[0].size(); rightcols = (msize_t)right[0].size(); if (leftcols && rightcols && (rightrows == leftcols)) { resize(leftrows, rightcols, (*tresult), true);//could use zeros(), but common index last lets us zero at the same time msize_t i, j, k; for (i = 0; i < leftrows; ++i) { for (j = 0; j < rightcols; ++j) { A accum = 0; for (k = 0; k < leftcols; ++k) { accum += left[i][k] * right[k][j]; } (*tresult)[i][j] = accum; } } } else { result.resize(0); return; } } else { result.resize(0); return; } if (copyout) { result = tempstorage; } } template void MatrixFunctions::multiply(const vector > &left, const T2 right, vector > &result) { msize_t leftrows = (msize_t)left.size(), leftcols; bool doresize = true; if (&left == &result) { doresize = false;//don't resize if an input is an output } if (leftrows) { leftcols = (msize_t)left[0].size(); if (leftcols) { if (doresize) resize(leftrows, leftcols, result, true); msize_t i, j; for (i = 0; i < leftrows; ++i) { for (j = 0; j < leftcols; ++j) { result[i][j] = left[i][j] * right; } } } else { result.resize(0); return; } } else { result.resize(0); return; } } template void MatrixFunctions::rref_big(vector > &inout) { msize_t rows = (msize_t)inout.size(), cols; if (rows > 0) { cols = (msize_t)inout[0].size(); if (cols > 0) { vector pivots(rows, -1), missingPivots; msize_t i, j, k, myrow = 0; msize_t pivotrow; T tempval; for (i = 0; i < cols; ++i) { if (myrow >= rows) break;//no pivots left tempval = 0; pivotrow = -1; for (j = myrow; j < rows; ++j) {//only search below for new pivot if (abs(inout[j][i]) > tempval) { pivotrow = (msize_t)j; tempval = abs(inout[j][i]); } } if (pivotrow == -1) {//naively expect linearly dependence to show as an exact zero missingPivots.push_back(i);//record the missing pivot continue;//move to the next column } inout[pivotrow].swap(inout[myrow]);//STL swap via pointers for constant time row swap pivots[myrow] = i;//save the pivot location for back substitution tempval = inout[myrow][i]; inout[myrow][i] = (T)1; for (j = i + 1; j < cols; ++j) { inout[myrow][j] /= tempval;//divide row by pivot } for (j = myrow + 1; j < rows; ++j) {//zero ONLY below pivot for now tempval = inout[j][i]; inout[j][i] = (T)0; for (k = i + 1; k < cols; ++k) { inout[j][k] -= tempval * inout[myrow][k]; } } ++myrow;//increment row on successful pivot } msize_t numMissing = (msize_t)missingPivots.size(); if (myrow > 1)//if there is only 1 pivot, there is no back substitution to do { msize_t lastPivotCol = pivots[myrow - 1]; for (i = myrow - 1; i > 0; --i)//loop through pivots, can't zero above the top pivot so exclude it { msize_t pivotCol = pivots[i]; for (j = i - 1; j >= 0; --j)//loop through rows above pivot { tempval = inout[j][pivotCol]; inout[j][pivotCol] = (T)0;//flat zero the entry above the pivot for (k = numMissing - 1; k >= 0; --k)//back substitute within pivot range where pivots are missing { msize_t missingCol = missingPivots[k]; if (missingCol <= pivotCol) break;//equals will never trip, but whatever inout[j][missingCol] -= tempval * inout[i][missingCol]; } for (k = lastPivotCol + 1; k < cols; ++k)//loop through elements that are outside the pivot area { inout[j][k] -= tempval * inout[i][k]; } } } } } else { inout.resize(0); return; } } else { inout.resize(0); return; } } template void MatrixFunctions::rref(vector > &inout) { msize_t rows = (msize_t)inout.size(), cols; if (rows) { cols = (msize_t)inout[0].size(); if (cols) { if (rows > 7 || cols > 7)//when the matrix has this many rows/columns, it is faster to allocate storage for tracking pivots, and back substitute { rref_big(inout); return; } msize_t i, j, k, myrow = 0; msize_t pivotrow; T tempval; for (i = 0; i < cols; ++i) { if (myrow >= rows) break;//no pivots left tempval = 0; pivotrow = -1; for (j = myrow; j < rows; ++j) {//only search below for new pivot if (abs(inout[j][i]) > tempval) { pivotrow = (msize_t)j; tempval = abs(inout[j][i]); } } if (pivotrow == -1)//it may be a good idea to include a "very small value" check here, but it could mess up if used on a matrix with all values very small {//naively expect linearly dependence to show as an exact zero continue;//move to the next column } inout[pivotrow].swap(inout[myrow]);//STL swap via pointers for constant time row swap tempval = inout[myrow][i]; inout[myrow][i] = 1; for (j = i + 1; j < cols; ++j) { inout[myrow][j] /= tempval;//divide row by pivot } for (j = 0; j < myrow; ++j) {//zero above pivot tempval = inout[j][i]; inout[j][i] = 0; for (k = i + 1; k < cols; ++k) { inout[j][k] -= tempval * inout[myrow][k]; } } for (j = myrow + 1; j < rows; ++j) {//zero below pivot tempval = inout[j][i]; inout[j][i] = 0; for (k = i + 1; k < cols; ++k) { inout[j][k] -= tempval * inout[myrow][k]; } } ++myrow;//increment row on successful pivot } } else { inout.resize(0); return; } } else { inout.resize(0); return; } } template void MatrixFunctions::inverse(const vector > &in, vector > &result) {//rref implementation, there are faster (more complicated) ways - if it isn't invertible, it will hand back something strange msize_t inrows = (msize_t)in.size(), incols; if (inrows) { incols = (msize_t)in[0].size(); if (incols == inrows) { vector > inter, inter2; identity(incols, inter2); horizCat(in, inter2, inter); rref(inter); getChunk(0, inrows, incols, incols * 2, inter, result);//already using a local variable, doesn't need to check for reference duplicity } else { result.resize(0); return; } } else { result.resize(0); return; } } template void MatrixFunctions::add(const vector >& left, const vector >& right, vector >& result) { msize_t inrows = (msize_t)left.size(), incols; bool doresize = true; if (&left == &result || &right == &result) { doresize = false;//don't resize if an input is an output - this is ok for addition, don't need a copy } if (inrows) { incols = (msize_t)left[0].size(); if (inrows == (msize_t)right.size() && incols == (msize_t)right[0].size())//short circuit evaluation will protect against segfault { if (doresize) resize(inrows, incols, result, true); for (msize_t i = 0; i < inrows; ++i) { for (msize_t j = 0; j < incols; ++j) { result[i][j] = left[i][j] + right[i][j]; } } } else { result.resize(0);//use empty matrix for error condition return; } } else { result.resize(0); return; } } template void MatrixFunctions::add(const vector >& left, const T2 right, vector >& result) { msize_t inrows = (msize_t)left.size(), incols; bool doresize = true; if (&left == &result) { doresize = false;//don't resize if an input is an output - this is ok for addition, don't need a copy } if (inrows) { incols = (msize_t)left[0].size(); if (doresize) resize(inrows, incols, result, true); for (msize_t i = 0; i < inrows; ++i) { for (msize_t j = 0; j < incols; ++j) { result[i][j] = left[i][j] + right; } } } else { result.resize(0); return; } } template void MatrixFunctions::subtract(const vector >& left, const vector >& right, vector >& result) { msize_t inrows = (msize_t)left.size(), incols; bool doresize = true; if (&left == &result || &right == &result) { doresize = false;//don't resize if an input is an output } if (inrows) { incols = (msize_t)left[0].size(); if (inrows == (msize_t)right.size() && incols == (msize_t)right[0].size())//short circuit evaluation will protect against segfault { if (doresize) resize(inrows, incols, result, true); for (msize_t i = 0; i < inrows; ++i) { for (msize_t j = 0; j < incols; ++j) { result[i][j] = left[i][j] - right[i][j]; } } } else { result.resize(0); return; } } else { result.resize(0); return; } } template void MatrixFunctions::transpose(const vector > &in, vector > &result) { msize_t inrows = (msize_t)in.size(), incols; vector > tempstorage, *tresult = &result; bool copyout = false; if (&in == &result) { copyout = true; tresult = &tempstorage; } if (inrows) { incols = (msize_t)in[0].size(); resize(incols, inrows, (*tresult), true); for (msize_t i = 0; i < inrows; ++i) { for (msize_t j = 0; j < incols; ++j) { (*tresult)[j][i] = in[i][j]; } } } else { result.resize(0); } if (copyout) { result = tempstorage; } } template bool MatrixFunctions::checkDim(const vector > &in) { bool ret = true; msize_t rows = (msize_t)in.size(), columns; if (rows) { columns = (msize_t)in[0].size(); for (msize_t i = 1; i < rows; ++i) { if (in[i].size() != columns) { ret = false; } } } return ret; } template void MatrixFunctions::resize(const msize_t rows, const msize_t columns, vector >& result, bool destructive) { if (destructive && result.size() && ((msize_t)result.capacity() < rows || (msize_t)result[0].capacity() < columns)) {//for large matrices, copying to preserve contents is slow result.resize(0);//not intended to dealloc, just to set number of items to copy to zero }//default is nondestructive resize, copies everything result.resize(rows); for (msize_t i = 0; i < (const msize_t)rows; ++i) {//naive method, may end up copying everything twice if both row and col resizes require realloc result[i].resize(columns); } } template void MatrixFunctions::zeros(const msize_t rows, const msize_t columns, vector >& result) { resize(rows, columns, result, true); for (msize_t i = 0; i < rows; ++i) { for (msize_t j = 0; j < columns; ++j) { result[i][j] = 0;//should cast to float or double fine } } } template void MatrixFunctions::ones(const msize_t rows, const msize_t columns, vector >& result) { resize(rows, columns, result, true); for (msize_t i = 0; i < rows; ++i) { for (msize_t j = 0; j < columns; ++j) { result[i][j] = 1;//should cast to float or double fine } } } template void MatrixFunctions::identity(const msize_t size, vector >& result) { resize(size, size, result, true); for (msize_t i = 0; i < (const msize_t)size; ++i) { for (msize_t j = 0; j < (const msize_t)size; ++j) { result[i][j] = ((i == j) ? 1 : 0);//ditto, forgive the ternary } } } template 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()) { leftcols = (msize_t)left[0].size(); rightcols = (msize_t)right[0].size(); (*tresult) = left;//use STL copy to start resize(inrows, leftcols + rightcols, (*tresult));//values survive nondestructive resize for (msize_t i = 0; i < inrows; ++i) { for (msize_t j = 0; j < rightcols; ++j) { (*tresult)[i][j + leftcols] = right[i][j]; } } } else { result.resize(0); return; } if (copyout) { result = tempstorage; } } template 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()) { (*tresult) = top; resize(toprows + botrows, incols, (*tresult));//nondestructive resize for (msize_t i = 0; i < botrows; ++i) { for (msize_t j = 0; j < incols; ++j) { (*tresult)[i + toprows][j] = bottom[i][j]; } } } else { result.resize(0); return; } } else { result.resize(0); return; } if (copyout) { result = tempstorage; } } template void MatrixFunctions::getChunk(const msize_t firstrow, const msize_t lastrow, const msize_t firstcol, const msize_t lastcol, const vector >& in, vector >& result) { msize_t outrows = lastrow - firstrow; msize_t outcols = lastcol - firstcol; if (lastrow <= firstrow || lastcol <= firstcol || firstrow < 0 || firstcol < 0 || lastrow > (msize_t)in.size() || lastcol > (msize_t)in[0].size()) { result.resize(0); return; } vector > tempstorage, *tresult = &result; bool copyout = false; if (&in == &result) { copyout = true; tresult = &tempstorage; } resize(outrows, outcols, (*tresult), true); for (msize_t i = 0; i < outrows; ++i) { for (msize_t j = 0; j < outcols; ++j) { (*tresult)[i][j] = in[i + firstrow][j + firstcol]; } } if (copyout) { result = tempstorage; } } } #endif connectome-workbench-1.2.3+git41-gc4c6c90/src/Common/ModelTransform.cxx000066400000000000000000000407341300200146000254640ustar00rootroot00000000000000 /*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 __MODEL_TRANSFORM_DECLARE__ #include "ModelTransform.h" #undef __MODEL_TRANSFORM_DECLARE__ #include #include "CaretLogger.h" using namespace caret; /** * \class caret::ModelTransform * \brief Translation, Rotation, and Scaling for a model. * * Translation, Rotation, and Scaling for a model. */ /** * Constructor. */ ModelTransform::ModelTransform() : CaretObject() { this->name = ""; this->comment = ""; setToIdentity(); } /** * Destructor. */ ModelTransform::~ModelTransform() { } /** * Set the view to the identity matrix. */ void ModelTransform::setToIdentity() { this->translation[0] = 0.0; this->translation[1] = 0.0; this->translation[2] = 0.0; for (int32_t i = 0; i < 4; i++) { for (int32_t j = 0; j < 4; j++) { if (i == j) { this->rotation[i][j] = 1.0; this->obliqueRotation[i][j] = 1.0; } else { this->rotation[i][j] = 0.0; this->obliqueRotation[i][j] = 0.0; } } } this->rightCortexFlatMapOffsetXY[0] = 0.0; this->rightCortexFlatMapOffsetXY[1] = 0.0; this->rightCortexFlatMapZoomFactor = 1.0; this->scaling = 1.0; } /** * Copy constructor. * @param ModelTransform * View that is copied. */ ModelTransform::ModelTransform(const ModelTransform& ModelTransform) : CaretObject(ModelTransform) { this->copyHelper(ModelTransform); } /** * Assignment operator. * @param ModelTransform * View that is copied to this view. * @return * Reference to this object. */ ModelTransform& ModelTransform::operator=(const ModelTransform& ModelTransform) { if (this != &ModelTransform) { CaretObject::operator=(ModelTransform); this->copyHelper(ModelTransform); } return *this; } /** * Less than operator. * @param view * View compared to this view. * @return * Returns result of a name comparison. */ bool ModelTransform::operator<(const ModelTransform& view) const { return (this->name < view.name); } /** * Equality operator. * @param view * View compared to this view. * @return * Returns true if views have same name. */ bool ModelTransform::operator==(const ModelTransform& view) const { return (this->name == view.name); } /** * Get the translation * @param translation * Output translation. */ void ModelTransform::getTranslation(float translation[3]) const { translation[0] = this->translation[0]; translation[1] = this->translation[1]; translation[2] = this->translation[2]; } /** * Get the rotation matrix. * @param rotation * Output rotation matrix. */ void ModelTransform::getRotation(float rotation[4][4]) const { for (int32_t i = 0; i < 4; i++) { for (int32_t j = 0; j < 4; j++) { rotation[i][j] = this->rotation[i][j]; } } } /** * Get the offset for drawing the right cortex flat map. * * @param rightCortexFlatMapOffsetX * Output contining offset X. * @param rightCortexFlatMapOffsetX * Output contining offset Y. */ void ModelTransform::getRightCortexFlatMapOffset(float& rightCortexFlatMapOffsetX, float& rightCortexFlatMapOffsetY) const { rightCortexFlatMapOffsetX = this->rightCortexFlatMapOffsetXY[0]; rightCortexFlatMapOffsetY = this->rightCortexFlatMapOffsetXY[1]; } /** * @return Zoom factor for right cortex flat map. */ float ModelTransform::getRightCortexFlatMapZoomFactor() const { return this->rightCortexFlatMapZoomFactor; } /** * Get the oblique rotation matrix. * @param obliqueRotation * Output oblique rotation matrix. */ void ModelTransform::getObliqueRotation(float obliqueRotation[4][4]) const { for (int32_t i = 0; i < 4; i++) { for (int32_t j = 0; j < 4; j++) { obliqueRotation[i][j] = this->obliqueRotation[i][j]; } } } /** * @return The scaling. */ float ModelTransform::getScaling() const { return this->scaling; } /** * @return Name of the view. */ AString ModelTransform::getName() const { return this->name; } /** * Set the name of the view. * @param name * New name for view. */ void ModelTransform::setName(const AString& name) { this->name = name; } /** * @return Comment of the view. */ AString ModelTransform::getComment() const { return this->comment; } /** * Set the comment of the view. * @param comment * New comment for view. */ void ModelTransform::setComment(const AString& comment) { this->comment = comment; } /** * Set the translation * @param translation * New translation. */ void ModelTransform::setTranslation(const float translation[3]) { this->translation[0] = translation[0]; this->translation[1] = translation[1]; this->translation[2] = translation[2]; } /** * Set the translation * @param translationX * New translation X-value. * @param translationY * New translation Y-value. * @param translationZ * New translation Z-value. */ void ModelTransform::setTranslation(const float translationX, const float translationY, const float translationZ) { this->translation[0] = translationX; this->translation[1] = translationY; this->translation[2] = translationZ; } /** * Set the rotation matrix. * @param rotation * New rotation matrix. */ void ModelTransform::setRotation(const float rotation[4][4]) { for (int32_t i = 0; i < 4; i++) { for (int32_t j = 0; j < 4; j++) { this->rotation[i][j] = rotation[i][j]; } } } /** * Set the oblique rotation matrix. * @param obliqueRotation * New oblique rotation matrix. */ void ModelTransform::setObliqueRotation(const float obliqueRotation[4][4]) { for (int32_t i = 0; i < 4; i++) { for (int32_t j = 0; j < 4; j++) { this->obliqueRotation[i][j] = obliqueRotation[i][j]; } } } /** * Set the offset for drawing the right cortex flat map. * * @param rightCortexFlatMapOffsetX * Input contining offset X. * @param rightCortexFlatMapOffsetY * Input contining offset Y. */ void ModelTransform::setRightCortexFlatMapOffset(const float rightCortexFlatMapOffsetX, const float rightCortexFlatMapOffsetY) { this->rightCortexFlatMapOffsetXY[0] = rightCortexFlatMapOffsetX; this->rightCortexFlatMapOffsetXY[1] = rightCortexFlatMapOffsetY; } /** * Set the right cortex flat map zoom factor. * * @param rightCortexFlatMapZoomFactor * Zoom factor for right cortex flat map. */ void ModelTransform::setRightCortexFlatMapZoomFactor(const float rightCortexFlatMapZoomFactor) { this->rightCortexFlatMapZoomFactor = rightCortexFlatMapZoomFactor; } /** * Set the scaling * @param scaling * New value for scaling. */ void ModelTransform::setScaling(const float scaling) { this->scaling = scaling; } /** * Returns the user view in a string that contains, * separated by commas: View Name, translation[3], * rotation[4][4], scaling, obliqueRotation[4][4], * and rightCortexFlatMapOffset[2]; */ AString ModelTransform::getAsString() const { AString s = (this->name + s_separatorInPreferences + this->comment + s_separatorInPreferences + AString::number(this->translation[0]) + s_separatorInPreferences + AString::number(this->translation[1]) + s_separatorInPreferences + AString::number(this->translation[2])); for (int32_t i = 0; i < 4; i++) { for (int32_t j = 0; j < 4; j++) { s += (s_separatorInPreferences + AString::number(this->rotation[i][j])); } } s += (s_separatorInPreferences + AString::number(this->scaling)); for (int32_t i = 0; i < 4; i++) { for (int32_t j = 0; j < 4; j++) { s += (s_separatorInPreferences + AString::number(this->obliqueRotation[i][j])); } } s += (s_separatorInPreferences + AString::number(this->rightCortexFlatMapOffsetXY[0])); s += (s_separatorInPreferences + AString::number(this->rightCortexFlatMapOffsetXY[1])); s += (s_separatorInPreferences + AString::number(this->rightCortexFlatMapZoomFactor)); return s; } /** * Set the user view from a string that contains, * separated by commas: View Name, translation[3], * rotation[4][4], scaling, obliqueRotation[4][4], * and rightCortexFlatMapOffset[2]; */ bool ModelTransform::setFromString(const AString& s) { bool hasComment = false; bool hasObliqueRotation = false; bool hasRightFlatMapOffset = false; bool hasRightFlatMapZoomFactor = false; QStringList sl; if (s.contains(s_separatorInPreferences)) { sl = s.split(s_separatorInPreferences, QString::KeepEmptyParts); const int numElements = sl.count(); if (numElements == 41) { hasComment = true; hasObliqueRotation = true; hasRightFlatMapOffset = true; hasRightFlatMapZoomFactor = true; } else if (numElements == 40) { hasComment = true; hasObliqueRotation = true; hasRightFlatMapOffset = true; } else if (numElements == 38) { hasComment = true; hasObliqueRotation = true; } else if (numElements == 22) { hasComment = true; } else { CaretLogSevere("User view string does not contain 22, 38, or 40 elements"); return false; } } else { sl = s.split(",", QString::KeepEmptyParts); const int numElements = sl.count(); if (numElements != 21) { CaretLogSevere("User view string does not contain 21 elements"); return false; } } int ctr = 0; this->name = sl.at(ctr++); if (hasComment) { this->comment = sl.at(ctr++); } else { this->comment = ""; } this->translation[0] = sl.at(ctr++).toFloat(); this->translation[1] = sl.at(ctr++).toFloat(); this->translation[2] = sl.at(ctr++).toFloat(); for (int32_t i = 0; i < 4; i++) { for (int32_t j = 0; j < 4; j++) { this->rotation[i][j] = sl.at(ctr++).toFloat(); } } this->scaling = sl.at(ctr++).toFloat(); if (hasObliqueRotation) { for (int32_t i = 0; i < 4; i++) { for (int32_t j = 0; j < 4; j++) { this->obliqueRotation[i][j] = sl.at(ctr++).toFloat(); } } } else { for (int32_t i = 0; i < 4; i++) { for (int32_t j = 0; j < 4; j++) { if (i == j) { this->obliqueRotation[i][j] = 1.0; } else { this->obliqueRotation[i][j] = 0.0; } } } } if (hasRightFlatMapOffset) { this->rightCortexFlatMapOffsetXY[0] = sl.at(ctr++).toFloat(); this->rightCortexFlatMapOffsetXY[1] = sl.at(ctr++).toFloat(); } else { this->rightCortexFlatMapOffsetXY[0] = 0; this->rightCortexFlatMapOffsetXY[1] = 0; } if (hasRightFlatMapZoomFactor) { this->rightCortexFlatMapZoomFactor = sl.at(ctr++).toFloat(); } else { this->rightCortexFlatMapZoomFactor = 1.0; } return true; } /** * Copy all data from the given user view to this user view. * @param ModelTransform * View from which data is copied. */ void ModelTransform::copyHelper(const ModelTransform& modelTransform) { this->name = modelTransform.name; this->comment = modelTransform.comment; this->translation[0] = modelTransform.translation[0]; this->translation[1] = modelTransform.translation[1]; this->translation[2] = modelTransform.translation[2]; for (int32_t i = 0; i < 4; i++) { for (int32_t j = 0; j < 4; j++) { this->rotation[i][j] = modelTransform.rotation[i][j]; this->obliqueRotation[i][j] = modelTransform.obliqueRotation[i][j]; } } this->scaling = modelTransform.scaling; this->rightCortexFlatMapOffsetXY[0] = modelTransform.rightCortexFlatMapOffsetXY[0]; this->rightCortexFlatMapOffsetXY[1] = modelTransform.rightCortexFlatMapOffsetXY[1]; this->rightCortexFlatMapZoomFactor = modelTransform.rightCortexFlatMapZoomFactor; } /** * Set panning, rotation, oblique rotation, and zoom. * * @param panX * X-Panning. * @param panY * Y-Panning. * @param panZ * Z-Panning. * @param rotationMatrix * 4x4 rotation matrix. * @param obliqueRotationMatrix * 4x4 oblique rotation matrix. * @param zoom * Zooming. * @param rightCortexFlatMapOffsetX * Offset X for right cortex flat map. * @param rightCortexFlatMapOffsetY * Offset Y for right cortex flat map. * @param rightCortexFlatMapZoomFactor * Zoom factor for right cortex flat map. */ void ModelTransform::setPanningRotationMatrixAndZoom(const float panX, const float panY, const float panZ, const float rotationMatrix[4][4], const float obliqueRotationMatrix[4][4], const float zoom, const float rightCortexFlatMapOffsetX, const float rightCortexFlatMapOffsetY, const float rightCortexFlatMapZoomFactor) { this->setTranslation(panX, panY, panZ); setRotation(rotationMatrix); setObliqueRotation(obliqueRotationMatrix); this->setScaling(zoom); this->setRightCortexFlatMapOffset(rightCortexFlatMapOffsetX, rightCortexFlatMapOffsetY); this->setRightCortexFlatMapZoomFactor(rightCortexFlatMapZoomFactor); } /** * Get pan, rotation, oblique rotation, and zoom. * * @param panX * X-Panning. * @param panY * Y-Panning. * @param rotationMatrix * 4x4 rotation matrix. * @param obliqueRotationMatrix * 4x4 oblique rotation matrix. * @param zoom * Zooming. * @param rightCortexFlatMapOffsetX * Offset X for right cortex flat map. * @param rightCortexFlatMapOffsetY * Offset Y for right cortex flat map. * @param rightCortexFlatMapZoomFactor * Zoom factor for right cortex flat map. */ void ModelTransform::getPanningRotationMatrixAndZoom(float& panX, float& panY, float& panZ, float rotationMatrix[4][4], float obliqueRotationMatrix[4][4], float& zoom, float& rightCortexFlatMapOffsetX, float& rightCortexFlatMapOffsetY, float& rightCortexFlatMapZoomFactor) const { panX = this->translation[0]; panY = this->translation[1]; panZ = this->translation[2]; getRotation(rotationMatrix); getObliqueRotation(obliqueRotationMatrix); zoom = getScaling(); getRightCortexFlatMapOffset(rightCortexFlatMapOffsetX, rightCortexFlatMapOffsetY); rightCortexFlatMapZoomFactor = this->getRightCortexFlatMapZoomFactor(); } /** * Get a description of this object's content. * @return String describing this object's content. */ AString ModelTransform::toString() const { return ("ModelTransform: " + this->getAsString()); } connectome-workbench-1.2.3+git41-gc4c6c90/src/Common/ModelTransform.h000066400000000000000000000114731300200146000251070ustar00rootroot00000000000000#ifndef __MODEL_TRANSFORM_H__ #define __MODEL_TRANSFORM_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" namespace caret { class ModelTransform : public CaretObject { public: ModelTransform(); virtual ~ModelTransform(); ModelTransform(const ModelTransform&); ModelTransform& operator=(const ModelTransform&); AString getName() const; void getTranslation(float translation[3]) const; void getRotation(float rotation[4][4]) const; void getObliqueRotation(float obliqueRotation[4][4]) const; void getRightCortexFlatMapOffset(float& rightCortexFlatMapOffsetX, float& rightCortexFlatMapOffsetY) const; float getRightCortexFlatMapZoomFactor() const; float getScaling() const; void setName(const AString& name); AString getComment() const; void setComment(const AString& comment); void setTranslation(const float translation[3]); void setTranslation(const float translationX, const float translationY, const float translationZ); void setRotation(const float rotation[4][4]); void setObliqueRotation(const float obliqueRotation[4][4]); void setRightCortexFlatMapOffset(const float rightCortexFlatMapOffsetX, const float rightCortexFlatMapOffsetY); void setRightCortexFlatMapZoomFactor(const float rightCortexFlatMapZoomFactor); void setScaling(const float scaling); void setPanningRotationMatrixAndZoom(const float panX, const float panY, const float panZ, const float rotationMatrix[4][4], const float obliqueRotationMatrix[4][4], const float zoom, const float rightCortexFlatMapOffsetX, const float rightCortexFlatMapOffsetY, const float rightCortexFlatMapZoomFactor); void getPanningRotationMatrixAndZoom(float& panX, float& panY, float& panZ, float rotationMatrix[4][4], float obliqueRotationMatrix[4][4], float& zoom, float& rightCortexFlatMapOffsetX, float& rightCortexFlatMapOffsetY, float& rightCortexFlatMapZoomFactor) const; AString getAsString() const; bool setFromString(const AString& s); void setToIdentity(); bool operator<(const ModelTransform& view) const; bool operator==(const ModelTransform& view) const; public: virtual AString toString() const; private: void copyHelper(const ModelTransform& ModelTransform); AString name; AString comment; float translation[3]; float rotation[4][4]; float obliqueRotation[4][4]; float scaling; float rightCortexFlatMapOffsetXY[2]; float rightCortexFlatMapZoomFactor; static const QString s_separatorInPreferences; }; #ifdef __MODEL_TRANSFORM_DECLARE__ const QString ModelTransform::s_separatorInPreferences = "::::"; #endif // __MODEL_TRANSFORM_DECLARE__ } // namespace #endif //__MODEL_TRANSFORM_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Common/MultiDimArray.h000066400000000000000000000073411300200146000246750ustar00rootroot00000000000000#ifndef __MULTI_DIM_ARRAY_H__ #define __MULTI_DIM_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 "CaretAssert.h" #include "stdint.h" #include namespace caret { template class MultiDimArray { std::vector m_dims, m_skip;//always use int64_t for indexes internally std::vector m_data; template int64_t index(const int& fullDims, const std::vector& indexSelect) const;//assume we never need over 2 billion dimensions public: const std::vector& getDimensions() const { return m_dims; } template void resize(const std::vector& dims);//destructive resize template T& at(const std::vector& pos); template const T& at(const std::vector& pos) const; template T* get(const int& fullDims, const std::vector& indexSelect);//subarray reference selection template const T* get(const int& fullDims, const std::vector& indexSelect) const; }; template template void MultiDimArray::resize(const std::vector& dims) { m_dims = std::vector(dims.begin(), dims.end()); m_skip.resize(m_dims.size()); if (dims.size() == 0) { m_data.clear(); return; } int64_t numElems = 1; for (int i = 0; i < (int)m_dims.size(); ++i) { CaretAssert(m_dims[i] > 0); m_skip[i] = numElems; numElems *= m_dims[i]; } m_data.resize(numElems); } template template int64_t MultiDimArray::index(const int& fullDims, const std::vector& indexSelect) const { CaretAssert(fullDims + indexSelect.size() == m_dims.size()); int64_t ret = 0; for (int i = fullDims; i < (int)m_dims.size(); ++i) { CaretAssert(indexSelect[i - fullDims] >= 0 && indexSelect[i - fullDims] < m_dims[i]); ret += m_skip[i] * indexSelect[i - fullDims]; } return ret; } template template T& MultiDimArray::at(const std::vector& pos) { return m_data[index(0, pos)]; } template template const T& MultiDimArray::at(const std::vector& pos) const { return m_data[index(0, pos)]; } template template T* MultiDimArray::get(const int& fullDims, const std::vector& indexSelect) { return m_data.data() + index(fullDims, indexSelect); } template template const T* MultiDimArray::get(const int& fullDims, const std::vector& indexSelect) const { return m_data.data() + index(fullDims, indexSelect); } } #endif //__MULTI_DIM_ARRAY_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Common/MultiDimIterator.h000066400000000000000000000076621300200146000254160ustar00rootroot00000000000000#ifndef __MULTI_DIM_ITERATOR_H__ #define __MULTI_DIM_ITERATOR_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 "stdint.h" #include namespace caret { template class MultiDimIterator { std::vector m_dims, m_pos; bool m_atEnd; void gotoBegin(); void gotoLast(); public: explicit MultiDimIterator(const std::vector& dimensions); void operator++(); void operator++(int); void operator--(); void operator--(int); const std::vector& operator*() const { return m_pos; } bool atEnd() const { return m_atEnd; } }; template MultiDimIterator::MultiDimIterator(const std::vector& dimensions) { m_dims = dimensions; gotoBegin(); } template void MultiDimIterator::gotoBegin() { m_pos = std::vector(m_dims.size(), 0); m_atEnd = false; size_t numDims = m_dims.size(); for (size_t i = 0; i < numDims; ++i) { if (m_dims[i] < 1) { m_atEnd = true; break; } } } template void MultiDimIterator::gotoLast() { m_pos = std::vector(m_dims.size()); m_atEnd = false; size_t numDims = m_dims.size(); for (size_t i = 0; i < numDims; ++i) { m_pos[i] = m_dims[i] - 1; if (m_dims[i] < 1) { m_atEnd = true; } } } template void MultiDimIterator::operator++() { if (atEnd())//wrap around { gotoBegin(); return; } if (m_dims.size() == 0) { m_atEnd = true;//special case: no dimensions works the same as 1 dimension of length 1 return; } size_t numDims = m_dims.size(); for (size_t i = 0; i < numDims; ++i) { ++m_pos[i]; if (m_pos[i] < m_dims[i]) return; m_pos[i] = 0; } m_atEnd = true;//if we didn't return already, all of them wrapped, so we are at the end } template void MultiDimIterator::operator++(int) { ++(*this); } template void MultiDimIterator::operator--() { if (atEnd())//wrap around { gotoLast(); return; } if (m_dims.size() == 0) { m_atEnd = true;//special case: no dimensions works the same as 1 dimension of length 1 return; } size_t numDims = m_dims.size(); for (size_t i = 0; i < numDims; ++i) { if (m_pos[i] > 0) { --m_pos[i]; return; } else { m_pos[i] = m_dims[i] - 1; } } m_atEnd = true;//if we didn't return already, all of them wrapped, so we are at the end } template void MultiDimIterator::operator--(int) { --(*this); } } #endif //__MULTI_DIM_ITERATOR_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Common/NetworkException.cxx000066400000000000000000000026201300200146000260300ustar00rootroot00000000000000/*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 "NetworkException.h" using namespace caret; /** * Constructor. * */ NetworkException::NetworkException() : CaretException() { } /** * Constructor that uses stack trace from the exception * passed in as a parameter. * * @param e Any exception whose stack trace becomes * this exception's stack trace. * */ NetworkException::NetworkException( const CaretException& e) : CaretException(e) { } /** * Constructor. * * @param s Description of the exception. * */ NetworkException::NetworkException(const AString& s) : CaretException(s) { } connectome-workbench-1.2.3+git41-gc4c6c90/src/Common/NetworkException.h000066400000000000000000000023641300200146000254620ustar00rootroot00000000000000#ifndef __NETWORK_EXCEPTION_H__ #define __NETWORK_EXCEPTION_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 "CaretException.h" namespace caret { /** * An exception thrown during network operations */ class NetworkException : public CaretException { public: NetworkException(); NetworkException(const CaretException& e); NetworkException(const AString& s); }; } // namespace #endif // __NETWORK_EXCEPTION_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Common/NumericFormatModeEnum.cxx000066400000000000000000000251351300200146000267330ustar00rootroot00000000000000 /*LICENSE_START*/ /* * Copyright (C) 2015 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 __NUMERIC_FORMAT_MODE_ENUM_DECLARE__ #include "NumericFormatModeEnum.h" #undef __NUMERIC_FORMAT_MODE_ENUM_DECLARE__ #include "CaretAssert.h" using namespace caret; /** * \class caret::NumericFormatModeEnum * \brief Format mode for floating point numbers into text * * 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_numericFormatModeEnumComboBox; * * Declare a slot that is called when user changes selection * private slots: * void numericFormatModeEnumComboBoxItemActivated(); * * Implementation File (.cxx) * Include the header files * #include "EnumComboBoxTemplate.h" * #include "NumericFormatModeEnum.h" * * Instatiate: * m_numericFormatModeEnumComboBox = new EnumComboBoxTemplate(this); * m_numericFormatModeEnumComboBox->setup(); * * Get notified when the user changes the selection: * QObject::connect(m_numericFormatModeEnumComboBox, SIGNAL(itemActivated()), * this, SLOT(numericFormatModeEnumComboBoxItemActivated())); * * Update the selection: * m_numericFormatModeEnumComboBox->setSelectedItem(NEW_VALUE); * * Read the selection: * const NumericFormatModeEnum::Enum VARIABLE = m_numericFormatModeEnumComboBox->getSelectedItem(); * */ /** * Constructor. * * @param enumValue * An enumerated value. * @param name * Name of enumerated value. * * @param guiName * User-friendly name for use in user-interface. */ NumericFormatModeEnum::NumericFormatModeEnum(const Enum enumValue, const AString& name, const AString& guiName) { this->enumValue = enumValue; this->integerCode = integerCodeCounter++; this->name = name; this->guiName = guiName; } /** * Destructor. */ NumericFormatModeEnum::~NumericFormatModeEnum() { } /** * Initialize the enumerated metadata. */ void NumericFormatModeEnum::initialize() { if (initializedFlag) { return; } initializedFlag = true; enumData.push_back(NumericFormatModeEnum(AUTO, "AUTO", "Auto")); enumData.push_back(NumericFormatModeEnum(DECIMAL, "DECIMAL", "Decimal")); enumData.push_back(NumericFormatModeEnum(SCIENTIFIC, "SCIENTIFIC", "Scientific")); } /** * 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 NumericFormatModeEnum* NumericFormatModeEnum::findData(const Enum enumValue) { if (initializedFlag == false) initialize(); size_t num = enumData.size(); for (size_t i = 0; i < num; i++) { const NumericFormatModeEnum* 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 NumericFormatModeEnum::toName(Enum enumValue) { if (initializedFlag == false) initialize(); const NumericFormatModeEnum* 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. */ NumericFormatModeEnum::Enum NumericFormatModeEnum::fromName(const AString& name, bool* isValidOut) { if (initializedFlag == false) initialize(); bool validFlag = false; Enum enumValue = NumericFormatModeEnum::enumData[0].enumValue; for (std::vector::iterator iter = enumData.begin(); iter != enumData.end(); iter++) { const NumericFormatModeEnum& 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 NumericFormatModeEnum")); } return enumValue; } /** * Get a GUI string representation of the enumerated type. * @param enumValue * Enumerated value. * @return * String representing enumerated value. */ AString NumericFormatModeEnum::toGuiName(Enum enumValue) { if (initializedFlag == false) initialize(); const NumericFormatModeEnum* 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. */ NumericFormatModeEnum::Enum NumericFormatModeEnum::fromGuiName(const AString& guiName, bool* isValidOut) { if (initializedFlag == false) initialize(); bool validFlag = false; Enum enumValue = NumericFormatModeEnum::enumData[0].enumValue; for (std::vector::iterator iter = enumData.begin(); iter != enumData.end(); iter++) { const NumericFormatModeEnum& 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 NumericFormatModeEnum")); } return enumValue; } /** * Get the integer code for a data type. * * @return * Integer code for data type. */ int32_t NumericFormatModeEnum::toIntegerCode(Enum enumValue) { if (initializedFlag == false) initialize(); const NumericFormatModeEnum* 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. */ NumericFormatModeEnum::Enum NumericFormatModeEnum::fromIntegerCode(const int32_t integerCode, bool* isValidOut) { if (initializedFlag == false) initialize(); bool validFlag = false; Enum enumValue = NumericFormatModeEnum::enumData[0].enumValue; for (std::vector::iterator iter = enumData.begin(); iter != enumData.end(); iter++) { const NumericFormatModeEnum& 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 NumericFormatModeEnum")); } 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 NumericFormatModeEnum::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 NumericFormatModeEnum::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(NumericFormatModeEnum::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 NumericFormatModeEnum::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(NumericFormatModeEnum::toGuiName(iter->enumValue)); } if (isSorted) { std::sort(allGuiNames.begin(), allGuiNames.end()); } } connectome-workbench-1.2.3+git41-gc4c6c90/src/Common/NumericFormatModeEnum.h000066400000000000000000000061561300200146000263620ustar00rootroot00000000000000#ifndef __NUMERIC_FORMAT_MODE_ENUM_H__ #define __NUMERIC_FORMAT_MODE_ENUM_H__ /*LICENSE_START*/ /* * Copyright (C) 2015 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 NumericFormatModeEnum { public: /** * Enumerated values. */ enum Enum { /** Auto */ AUTO, /** Decimal */ DECIMAL, /** Scientific */ SCIENTIFIC }; ~NumericFormatModeEnum(); 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: NumericFormatModeEnum(const Enum enumValue, const AString& name, const AString& guiName); static const NumericFormatModeEnum* 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 __NUMERIC_FORMAT_MODE_ENUM_DECLARE__ std::vector NumericFormatModeEnum::enumData; bool NumericFormatModeEnum::initializedFlag = false; int32_t NumericFormatModeEnum::integerCodeCounter = 0; #endif // __NUMERIC_FORMAT_MODE_ENUM_DECLARE__ } // namespace #endif //__NUMERIC_FORMAT_MODE_ENUM_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Common/NumericTextFormatting.cxx000066400000000000000000000330021300200146000270200ustar00rootroot00000000000000 /*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 __NUMERIC_TEXT_FORMATTING_DECLARE__ #include "NumericTextFormatting.h" #undef __NUMERIC_TEXT_FORMATTING_DECLARE__ #include "CaretAssert.h" #include "CaretLogger.h" using namespace caret; #include "MathFunctions.h" /** * \class caret::NumericTextFormatting * \brief Formats numeric values for display as text. * \ingroup Common */ /** * Constructor. */ NumericTextFormatting::NumericTextFormatting() : CaretObject() { } /** * Destructor. */ NumericTextFormatting::~NumericTextFormatting() { } /** * If there are trailing zeros after the decimal point, remove * any after the first zero. Change a negative zero (-0) to zero (0); * * @param textValueIn * Text representation of the value. * @reutrn * Value after cleanup of zeros. */ AString NumericTextFormatting::cleanZerosInValueText(const AString& textValueIn) { AString textValue = textValueIn; const int dotIndex = textValue.indexOf('.'); if (dotIndex > 0) { /* * Remove any trailing zeros */ for (int32_t i = (textValue.length() - 1); i >= (dotIndex + 2); --i) { if (textValue[i] == '0') { textValue[i] = ' '; } else { break; } } } textValue = textValue.trimmed(); if (textValue == "-0.0") { textValue = "0"; } else if (textValue == "-0") { textValue = "0"; } else if (textValue == "0.0") { textValue = "0"; } textValue = textValue.trimmed(); return textValue; } /** * If there is a leading zero in the exponent, remove it * to save space (-2.1e+03 => -2.1e+3) * * @param numericFormat * How to format the numbers when converted to text. * @param textValueIn * Text representation of the value. * @reutrn * Value after cleanup of zeros. */ AString NumericTextFormatting::removeLeadingZeroFromExponent(const NumericFormatModeEnum::Enum numericFormat, const AString& textValueIn) { AString textValue = textValueIn; AString ePlusMinus = ""; int eIndex = textValue.indexOf("e+"); if (eIndex < 0) { eIndex = textValue.indexOf("e-"); } /* * Regular expression that matches something like 0.000e+00 (zero !!!) */ static QRegExp zeroRegExp("^0\\.0+e[\\+-]0+$"); if (eIndex > 0) { if (textValue.indexOf(zeroRegExp) >= 0) { textValue = "0"; } else { AString eText = textValue.mid(eIndex, 2); AString mantissaText = textValue.left(eIndex); AString exponentText = textValue.mid(eIndex + 2); switch (numericFormat) { case NumericFormatModeEnum::AUTO: /* * Remove trailing zeros from mantissa */ mantissaText = cleanZerosInValueText(mantissaText); break; case NumericFormatModeEnum::DECIMAL: break; case NumericFormatModeEnum::SCIENTIFIC: break; } /* * Remove leading zeros from exponent and * keep last zero */ for (int32_t i = 0; i < (exponentText.length() - 1); i++) { if (exponentText[i] == '0') { exponentText[i] = ' '; } else { break; } } exponentText = exponentText.trimmed(); textValue = (mantissaText + eText + exponentText); // std::cout << " Sci Value " // << textValueIn // << " Mantissa: " << mantissaText // << " e: " << eText // << " Exponent: " << exponentText << std::endl; } } return textValue; } /** * Format a number for display. * * @param numericFormat * How to format the numbers when converted to text. * @param value * The value for formatting. * @param format * Formatting to use for number ('f' fixed, 'e' scientific). * @param fieldWidth * * @param precision * Digits right of the decimal point * @return * Text representation of number with formatting applied. */ AString NumericTextFormatting::formatNumberForDisplay(const NumericFormatModeEnum::Enum numericFormat, const double value, const char format, const int fieldWidth, const int precision) { if (MathFunctions::isNaN(value)) { return "NaN"; } else if (MathFunctions::isInf(value)) { return "Inf"; } else if (MathFunctions::isNegInf(value)) { return "-Inf"; } else if (MathFunctions::isPosInf(value)) { return "Inf"; } const double absValue = ((value < 0.0) ? -value : value); AString numberValue = QString("%1").arg(absValue, fieldWidth, format, precision); numberValue = removeLeadingZeroFromExponent(numericFormat, numberValue); AString textValue; if (value < 0.0) { textValue = "-"; } textValue += numberValue; if ( ! textValue.contains('e')) { switch (numericFormat) { case NumericFormatModeEnum::AUTO: textValue = cleanZerosInValueText(textValue); break; case NumericFormatModeEnum::DECIMAL: break; case NumericFormatModeEnum::SCIENTIFIC: break; } } return textValue; } /** * Get format and precision based upon value of the number. * * @param valueIn * The number. * @param formatOut * Output format (fixed 'f' or scientific 'e') * @param precisionOut * Number of digits right of decimal point. */ void NumericTextFormatting::getFormatAndPrecision(const float valueIn, char& formatOut, int& precisionOut) { const float value = ((valueIn < 0.0) ? -valueIn : valueIn); if (value >= 10000) { formatOut = 'e'; precisionOut = 3; } else if (value >= 100) { formatOut = 'f'; precisionOut = 0; } else if (value >= 10) { formatOut = 'f'; precisionOut = 1; } else if (value >= 1) { formatOut = 'f'; precisionOut = 2; } else if (value >= 0.01) { formatOut = 'f'; precisionOut = 3; } else { formatOut = 'e'; precisionOut = 3; } } /** * Format the values by using the value of the range (min to max) * to set format and precision for all values. * * @param numericFormat * How to format the numbers when converted to text. * @param numericFormatPrecision * Precision used the numeric format is not automatic. * @param valuesIn * The input values array. Must be at least two elements and * the values must be sorted from smallest to largest. * @param formattedValuesOut * Output containing values formatted as text. * @param numberOfValues * Number of values in the arrays and both the input and output * arrays must sized to this value. */ void NumericTextFormatting::formatValueRange(const NumericFormatModeEnum::Enum numericFormat, const int32_t numericFormatPrecision, const float valuesIn[], AString formattedValuesOut[], const int32_t numberOfValues) { for (int32_t i = 0; i < numberOfValues; i++) { formattedValuesOut[i] = ""; } if (numberOfValues < 2) { CaretLogSevere("NumericTextFormatting::formatValueRange requires at least two values."); return; } const double range = valuesIn[numberOfValues - 1] - valuesIn[0]; if (MathFunctions::isNaN(range)) { for (int32_t i = 0; i < numberOfValues; i++) { formattedValuesOut[i] = "0"; } formattedValuesOut[0] = "NaN"; formattedValuesOut[numberOfValues - 1] = "NaN"; return; } char format = 'f'; int precision = 0; switch (numericFormat) { case NumericFormatModeEnum::AUTO: getFormatAndPrecision(range, format, precision); break; case NumericFormatModeEnum::DECIMAL: format = 'f'; precision = numericFormatPrecision; break; case NumericFormatModeEnum::SCIENTIFIC: format = 'e'; precision = numericFormatPrecision; break; } const int FIELD_WIDTH = 0; for (int32_t i = 0; i < numberOfValues; i++) { const double value = valuesIn[i]; AString textValue = formatNumberForDisplay(numericFormat, value, format, FIELD_WIDTH, precision); formattedValuesOut[i] = textValue; } } /** * Format the values by using the value of the range (min to max) * to set format and precision for all values. * * @param valuesIn * The input values array. Must be at least two elements and * the values must be sorted from smallest to largest. * @param formattedValuesOut * Output containing values formatted as text. * @param numberOfValues * Number of values in the arrays and both the input and output * arrays must sized to this value. */ void NumericTextFormatting::formatValueRange(const float valuesIn[], AString formattedValuesOut[], const int32_t numberOfValues) { NumericTextFormatting::formatValueRange(NumericFormatModeEnum::AUTO, 0, valuesIn, formattedValuesOut, numberOfValues); } /** * Format the values by formatting the negative and positive * ranges separately. * * @param negMaxNegMinPosMinPosMaxValuesIn * [0] => most negative value * [1] => least negative value * [2] => least positive value * [3] => most positive value * [0] <= [1] <= 0.0 <= [2] <= [3] * @param formattedValuesOut * Output containing values formatted as text. */ void NumericTextFormatting::formatValueRangeNegativeAndPositive(const float negMaxNegMinPosMinPosMaxValuesIn[4], AString formattedValuesOut[4]) { formatValueRange(&negMaxNegMinPosMinPosMaxValuesIn[0], &formattedValuesOut[0], 2); formatValueRange(&negMaxNegMinPosMinPosMaxValuesIn[2], &formattedValuesOut[2], 2); } /** * Format the value. * * @param valuesn * The input values. * @return * Value formatted as text. */ AString NumericTextFormatting::formatValue(const float value) { char format = 'f'; int precision = 0; getFormatAndPrecision(value, format, precision); const int FIELD_WIDTH = 0; AString textValue = formatNumberForDisplay(NumericFormatModeEnum::AUTO, value, format, FIELD_WIDTH, precision); return textValue; } /** * Format each of the values. * * @param valuesIn * The input values array. * @param formattedValuesOut * Output containing values formatted as text. * @param numberOfValues * Number of values in the arrays and both the input and output * arrays must sized to this value. */ void NumericTextFormatting::formatValuesIndividually(const float valuesIn[], AString formattedValuesOut[], const int32_t numberOfValues) { for (int32_t i = 0; i < numberOfValues; i++) { formattedValuesOut[i] = formatValue(valuesIn[i]); } } connectome-workbench-1.2.3+git41-gc4c6c90/src/Common/NumericTextFormatting.h000066400000000000000000000066651300200146000264640ustar00rootroot00000000000000#ifndef __NUMERIC_TEXT_FORMATTING_H__ #define __NUMERIC_TEXT_FORMATTING_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 "NumericFormatModeEnum.h" namespace caret { class NumericTextFormatting : public CaretObject { public: static void formatValueRangeNegativeAndPositive(const float negMaxNegMinPosMinPosMaxValuesIn[4], AString formattedValuesOut[4]); static void formatValueRange(const float valuesIn[], AString formattedValuesOut[], const int32_t numberOfValues); static void formatValueRange(const NumericFormatModeEnum::Enum numericFormat, const int32_t numericFormatPrecision, const float valuesIn[], AString formattedValuesOut[], const int32_t numberOfValues); static AString formatValue(const float valueIn); static void formatValuesIndividually(const float valuesIn[], AString formattedValuesOut[], const int32_t numberOfValues); // ADD_NEW_METHODS_HERE private: NumericTextFormatting(); virtual ~NumericTextFormatting(); NumericTextFormatting(const NumericTextFormatting&); NumericTextFormatting& operator=(const NumericTextFormatting&); static AString cleanZerosInValueText(const AString& textValueIn); static AString removeLeadingZeroFromExponent(const NumericFormatModeEnum::Enum numericFormat, const AString& textValueIn); static AString formatNumberForDisplay(const NumericFormatModeEnum::Enum numericFormat, const double value, const char format, const int fieldWidth, const int precision); static void getFormatAndPrecision(const float valueIn, char& formatOut, int& precisionOut); // ADD_NEW_MEMBERS_HERE }; #ifdef __NUMERIC_TEXT_FORMATTING_DECLARE__ // #endif // __NUMERIC_TEXT_FORMATTING_DECLARE__ } // namespace #endif //__NUMERIC_TEXT_FORMATTING_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Common/OctTree.h000066400000000000000000000356721300200146000235270ustar00rootroot00000000000000#ifndef __OCT_TREE_H__ #define __OCT_TREE_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 "MathFunctions.h" #include namespace caret { ///low level Oct structure with a bunch of helper members, use it to build your own tree of Octs, possibly by extension template struct Oct { //data T m_data; //children Oct* m_children[2][2][2]; Oct* m_parent; bool m_leaf; float m_bounds[3][3]; Oct(); Oct(const float minCoords[3], const float maxCoords[3]); ~Oct(); void makeChildren(); void makeChildrenExcept(const int octant[3]); void deleteChildren(); ///makes an Oct with this node as the child specified by octant Oct* makeParent(const int octant[3]); Oct* makeContains(const float pointToContain[3]); float distToPoint(const float point[3]); float distSquaredToPoint(const float point[3]); bool lineIntersects(const float p1[3], const float p2[3]); bool rayIntersects(const float start[3], const float p2[3]); bool lineSegmentIntersects(const float start[3], const float end[3]); bool pointInside(const float point[3]); bool boundsOverlaps(const float minCoords[3], const float maxCoords[3]); ///returns which child Oct the point would be contained in if the point were inside this Oct Oct* containingChild(const float point[3], int* whichOct = NULL); }; ///simple templated vector pointer that can be deleted, since you shouldn't rely on any method for actually deleting a vector's memory, for convenience template struct LeafVector { std::vector* m_vector; LeafVector() { m_vector = new std::vector(); } ~LeafVector() { freeData(); } void freeData() { if (m_vector != NULL) { delete m_vector; m_vector = NULL; } } T& operator[](const int64_t index) { return (*m_vector)[index]; } const T& operator[](const int64_t index) const { return (*m_vector)[index]; } }; template Oct::Oct() { for (int i = 0; i < 2; ++i) { m_children[i][0][0] = NULL; m_children[i][0][1] = NULL; m_children[i][1][0] = NULL; m_children[i][1][1] = NULL; } m_parent = NULL; m_leaf = true; } template Oct::Oct(const float minCoords[3], const float maxCoords[3]) { for (int i = 0; i < 2; ++i) { m_children[i][0][0] = NULL; m_children[i][0][1] = NULL; m_children[i][1][0] = NULL; m_children[i][1][1] = NULL; } m_parent = NULL; m_leaf = true; for (int i = 0; i < 3; ++i) { m_bounds[i][0] = minCoords[i]; m_bounds[i][2] = maxCoords[i]; m_bounds[i][1] = (m_bounds[i][0] + m_bounds[i][2]) * 0.5f; } } template Oct::~Oct() { deleteChildren(); } template void Oct::makeChildren() { m_leaf = false; int ijk[3]; for (ijk[0] = 0; ijk[0] < 2; ++ijk[0]) { for (ijk[1] = 0; ijk[1] < 2; ++ijk[1]) { for (ijk[2] = 0; ijk[2] < 2; ++ijk[2]) { Oct* temp = new Oct(); m_children[ijk[0]][ijk[1]][ijk[2]] = temp; temp->m_parent = this; for (int m = 0; m < 3; ++m) { temp->m_bounds[m][0] = m_bounds[m][ijk[m]]; temp->m_bounds[m][2] = m_bounds[m][ijk[m] + 1]; temp->m_bounds[m][1] = (temp->m_bounds[m][0] + temp->m_bounds[m][2]) * 0.5f; } } } } } template void Oct::makeChildrenExcept(const int octant[3]) { m_leaf = false; int ijk[3]; for (ijk[0] = 0; ijk[0] < 2; ++ijk[0]) { for (ijk[1] = 0; ijk[1] < 2; ++ijk[1]) { for (ijk[2] = 0; ijk[2] < 2; ++ijk[2]) { if (ijk[0] != octant[0] && ijk[1] != octant[1] && ijk[2] != octant[2]) {//avoiding one new/delete pair should be worth 8 times this conditional Oct* temp = new Oct(); m_children[ijk[0]][ijk[1]][ijk[2]] = temp; temp->m_parent = this; for (int m = 0; m < 3; ++m) { temp->m_bounds[m][0] = m_bounds[m][ijk[m]]; temp->m_bounds[m][2] = m_bounds[m][ijk[m] + 1]; temp->m_bounds[m][1] = (temp->m_bounds[m][0] + temp->m_bounds[m][2]) * 0.5f; } } } } } } template void Oct::deleteChildren() { m_leaf = true; for (int i = 0; i < 2; ++i) { for (int j = 0; j < 2; ++j) { for (int k = 0; k < 2; ++k) { if (m_children[i][j][k] != NULL) { delete m_children[i][j][k]; m_children[i][j][k] = NULL; } } } } } template Oct* Oct::makeParent(const int octant[3]) { Oct* ret = new Oct(); for (int i = 0; i < 3; ++i) { ret->m_bounds[i][octant[i]] = m_bounds[i][0]; ret->m_bounds[i][octant[i] + 1] = m_bounds[i][2]; ret->m_bounds[i][(octant[i] + 2) % 3] = (octant[i] ? (2.0f * m_bounds[i][0] - m_bounds[i][2]) : (2.0f * m_bounds[i][2] - m_bounds[i][0])); } ret->makeChildrenExcept(octant); ret->m_children[octant[0]][octant[1]][octant[2]] = this; m_parent = ret; return ret; } template Oct* Oct::makeContains(const float pointToContain[3]) { Oct* ret = this; while (!ret->pointInside(pointToContain)) { int octant[3]; octant[0] = (pointToContain[0] < m_bounds[0][1] ? 1 : 0);//use midpoint to intelligently pick best division when more than one division would contain it octant[1] = (pointToContain[1] < m_bounds[1][1] ? 1 : 0); octant[2] = (pointToContain[2] < m_bounds[2][1] ? 1 : 0); ret = ret->makeParent(octant); } return ret; } template float Oct::distToPoint(const float point[3]) { float temp[3]; for (int i = 0; i < 3; ++i) { if (point[i] < m_bounds[i][0]) { temp[i] = m_bounds[i][0] - point[i]; } else { if (point[i] > m_bounds[i][2]) { temp[i] = m_bounds[i][2] - point[i]; } else { temp[i] = 0.0f; } } } return MathFunctions::vectorLength(temp); } template float Oct::distSquaredToPoint(const float point[3]) { float temp[3]; for (int i = 0; i < 3; ++i) { if (point[i] < m_bounds[i][0]) { temp[i] = m_bounds[i][0] - point[i]; } else { if (point[i] > m_bounds[i][2]) { temp[i] = m_bounds[i][2] - point[i]; } else { temp[i] = 0.0f; } } } return temp[0] * temp[0] + temp[1] * temp[1] + temp[2] * temp[2]; } template bool Oct::lineIntersects(const float p1[3], const float p2[3]) { float direction[3]; float curlow = 1.0f, curhigh = -1.0f;//quiet compiler, make default say "false", but we use pointInside logic on zero length queries MathFunctions::subtractVectors(p2, p1, direction); bool first = true; for (int i = 0; i < 3; ++i) { if (direction[i] != 0.0f) { float templow; float temphigh; if (direction[i] > 0.0f) { templow = (m_bounds[i][0] - p1[i]) / direction[i];//compute the range of t over which this line lies between the planes for this axis temphigh = (m_bounds[i][2] - p1[i]) / direction[i]; } else { templow = (m_bounds[i][2] - p1[i]) / direction[i];//compute the range of t over which this line lies between the planes for this axis temphigh = (m_bounds[i][0] - p1[i]) / direction[i]; } if (first) { first = false; curlow = templow; curhigh = temphigh; } else { if (templow > curlow) curlow = templow;//intersect the ranges if (temphigh < curhigh) curhigh = temphigh; } if (curhigh < curlow) return false;//if intersection is null, false } else { if (p1[i] < m_bounds[i][0] || p1[i] > m_bounds[i][2]) return false; } } return true; } template bool Oct::rayIntersects(const float start[3], const float p2[3]) { float direction[3]; float curlow = 1.0f, curhigh = -1.0f;//quiet compiler, make default say "false", but we use pointInside logic on zero length queries MathFunctions::subtractVectors(p2, start, direction); bool first = true; for (int i = 0; i < 3; ++i) { if (direction[i] != 0.0f) { float templow; float temphigh; if (direction[i] > 0.0f) { templow = (m_bounds[i][0] - start[i]) / direction[i];//compute the range of t over which this line lies between the planes for this axis temphigh = (m_bounds[i][2] - start[i]) / direction[i]; } else { templow = (m_bounds[i][2] - start[i]) / direction[i];//compute the range of t over which this line lies between the planes for this axis temphigh = (m_bounds[i][0] - start[i]) / direction[i]; } if (first) { first = false; curlow = templow; curhigh = temphigh; } else { if (templow > curlow) curlow = templow;//intersect the ranges if (temphigh < curhigh) curhigh = temphigh; } if (curhigh < curlow || curhigh < 0.0f) return false;//if intersection is null or has no positive range, false } else { if (start[i] < m_bounds[i][0] || start[i] > m_bounds[i][2]) return false; } } return true; } template bool Oct::lineSegmentIntersects(const float start[3], const float end[3]) { float direction[3]; float curlow = 1.0f, curhigh = -1.0f;//quiet compiler, make default say "false", but we use pointInside logic on zero length queries MathFunctions::subtractVectors(end, start, direction);//parameterize the line segment to the range [0, 1] of t bool first = true; for (int i = 0; i < 3; ++i) { if (direction[i] != 0.0f) { float templow; float temphigh; if (direction[i] > 0.0f) { templow = (m_bounds[i][0] - start[i]) / direction[i];//compute the range of t over which this line lies between the planes for this axis temphigh = (m_bounds[i][2] - start[i]) / direction[i]; } else { templow = (m_bounds[i][2] - start[i]) / direction[i];//compute the range of t over which this line lies between the planes for this axis temphigh = (m_bounds[i][0] - start[i]) / direction[i]; } if (first) { first = false; curlow = templow; curhigh = temphigh; } else { if (templow > curlow) curlow = templow;//intersect the ranges if (temphigh < curhigh) curhigh = temphigh; } if (curhigh < curlow || curhigh < 0.0f || curlow > 1.0f) return false;//if intersection is null or has no positive range, or has no range less than 1, false } else { if (start[i] < m_bounds[i][0] || start[i] > m_bounds[i][2]) return false; } } return true; } template bool Oct::pointInside(const float point[3]) { for (int i = 0; i < 3; ++i) { if (point[i] < m_bounds[i][0] || point[i] > m_bounds[i][2]) return false;//be permissive, equal to boundary falls into both, though for traversal, strictly less than the boundary is the test condition } return true; } template bool Oct::boundsOverlaps(const float minCoords[3], const float maxCoords[3]) { for (int i = 0; i < 3; ++i) { if (maxCoords[i] < m_bounds[i][0] || minCoords[i] > m_bounds[i][2]) return false;//be permissive, equal to boundary falls into both } return true; } template Oct* Oct::containingChild(const float point[3], int* whichOct) { int myOct[3]; for (int i = 0; i < 3; ++i) { myOct[i] = (point[i] < m_bounds[i][1] ? 0 : 1);//strictly less than, using only the midpoint is how traversal works, even if the point isn't inside the Oct if (whichOct != NULL) whichOct[i] = myOct[i]; } return m_children[myOct[0]][myOct[1]][myOct[2]]; } } #endif //__OCT_TREE_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Common/OpenGLDrawingMethodEnum.cxx000066400000000000000000000252501300200146000271520ustar00rootroot00000000000000 /*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 __OPEN_G_L_DRAWING_METHOD_ENUM_DECLARE__ #include "OpenGLDrawingMethodEnum.h" #undef __OPEN_G_L_DRAWING_METHOD_ENUM_DECLARE__ #include "CaretAssert.h" using namespace caret; /** * \class caret::OpenGLDrawingMethodEnum * \brief * * * * 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_openGLDrawingMethodEnumComboBox; * * Declare a slot that is called when user changes selection * private slots: * void openGLDrawingMethodEnumComboBoxItemActivated(); * * Implementation File (.cxx) * Include the header files * #include "EnumComboBoxTemplate.h" * #include "OpenGLDrawingMethodEnum.h" * * Instatiate: * m_openGLDrawingMethodEnumComboBox = new EnumComboBoxTemplate(this); * m_openGLDrawingMethodEnumComboBox->setup(); * * Get notified when the user changes the selection: * QObject::connect(m_openGLDrawingMethodEnumComboBox, SIGNAL(itemActivated()), * this, SLOT(openGLDrawingMethodEnumComboBoxItemActivated())); * * Update the selection: * m_openGLDrawingMethodEnumComboBox->setSelectedItem(NEW_VALUE); * * Read the selection: * const OpenGLDrawingMethodEnum::Enum VARIABLE = m_openGLDrawingMethodEnumComboBox->getSelectedItem(); * */ /** * Constructor. * * @param enumValue * An enumerated value. * @param name * Name of enumerated value. * * @param guiName * User-friendly name for use in user-interface. */ OpenGLDrawingMethodEnum::OpenGLDrawingMethodEnum(const Enum enumValue, const AString& name, const AString& guiName) { this->enumValue = enumValue; this->integerCode = integerCodeCounter++; this->name = name; this->guiName = guiName; } /** * Destructor. */ OpenGLDrawingMethodEnum::~OpenGLDrawingMethodEnum() { } /** * Initialize the enumerated metadata. */ void OpenGLDrawingMethodEnum::initialize() { if (initializedFlag) { return; } initializedFlag = true; enumData.push_back(OpenGLDrawingMethodEnum(DRAW_WITH_VERTEX_BUFFERS_OFF, "DRAW_WITH_VERTEX_BUFFERS_OFF", "Off")); enumData.push_back(OpenGLDrawingMethodEnum(DRAW_WITH_VERTEX_BUFFERS_ON, "DRAW_WITH_VERTEX_BUFFERS_ON", "On")); } /** * 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 OpenGLDrawingMethodEnum* OpenGLDrawingMethodEnum::findData(const Enum enumValue) { if (initializedFlag == false) initialize(); size_t num = enumData.size(); for (size_t i = 0; i < num; i++) { const OpenGLDrawingMethodEnum* 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 OpenGLDrawingMethodEnum::toName(Enum enumValue) { if (initializedFlag == false) initialize(); const OpenGLDrawingMethodEnum* 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. */ OpenGLDrawingMethodEnum::Enum OpenGLDrawingMethodEnum::fromName(const AString& name, bool* isValidOut) { if (initializedFlag == false) initialize(); bool validFlag = false; Enum enumValue = OpenGLDrawingMethodEnum::enumData[0].enumValue; for (std::vector::iterator iter = enumData.begin(); iter != enumData.end(); iter++) { const OpenGLDrawingMethodEnum& 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 OpenGLDrawingMethodEnum")); } return enumValue; } /** * Get a GUI string representation of the enumerated type. * @param enumValue * Enumerated value. * @return * String representing enumerated value. */ AString OpenGLDrawingMethodEnum::toGuiName(Enum enumValue) { if (initializedFlag == false) initialize(); const OpenGLDrawingMethodEnum* 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. */ OpenGLDrawingMethodEnum::Enum OpenGLDrawingMethodEnum::fromGuiName(const AString& guiName, bool* isValidOut) { if (initializedFlag == false) initialize(); bool validFlag = false; Enum enumValue = OpenGLDrawingMethodEnum::enumData[0].enumValue; for (std::vector::iterator iter = enumData.begin(); iter != enumData.end(); iter++) { const OpenGLDrawingMethodEnum& 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 OpenGLDrawingMethodEnum")); } return enumValue; } /** * Get the integer code for a data type. * * @return * Integer code for data type. */ int32_t OpenGLDrawingMethodEnum::toIntegerCode(Enum enumValue) { if (initializedFlag == false) initialize(); const OpenGLDrawingMethodEnum* 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. */ OpenGLDrawingMethodEnum::Enum OpenGLDrawingMethodEnum::fromIntegerCode(const int32_t integerCode, bool* isValidOut) { if (initializedFlag == false) initialize(); bool validFlag = false; Enum enumValue = OpenGLDrawingMethodEnum::enumData[0].enumValue; for (std::vector::iterator iter = enumData.begin(); iter != enumData.end(); iter++) { const OpenGLDrawingMethodEnum& 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 OpenGLDrawingMethodEnum")); } 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 OpenGLDrawingMethodEnum::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 OpenGLDrawingMethodEnum::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(OpenGLDrawingMethodEnum::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 OpenGLDrawingMethodEnum::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(OpenGLDrawingMethodEnum::toGuiName(iter->enumValue)); } if (isSorted) { std::sort(allGuiNames.begin(), allGuiNames.end()); } } connectome-workbench-1.2.3+git41-gc4c6c90/src/Common/OpenGLDrawingMethodEnum.h000066400000000000000000000062711300200146000266010ustar00rootroot00000000000000#ifndef __OPEN_G_L_DRAWING_METHOD_ENUM_H__ #define __OPEN_G_L_DRAWING_METHOD_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 OpenGLDrawingMethodEnum { public: /** * Enumerated values. */ enum Enum { /** OpenGL Vertex Buffers Off */ DRAW_WITH_VERTEX_BUFFERS_OFF, /** OpenGL Vertex Buffers On */ DRAW_WITH_VERTEX_BUFFERS_ON }; ~OpenGLDrawingMethodEnum(); 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: OpenGLDrawingMethodEnum(const Enum enumValue, const AString& name, const AString& guiName); static const OpenGLDrawingMethodEnum* 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 __OPEN_G_L_DRAWING_METHOD_ENUM_DECLARE__ std::vector OpenGLDrawingMethodEnum::enumData; bool OpenGLDrawingMethodEnum::initializedFlag = false; int32_t OpenGLDrawingMethodEnum::integerCodeCounter = 0; #endif // __OPEN_G_L_DRAWING_METHOD_ENUM_DECLARE__ } // namespace #endif //__OPEN_G_L_DRAWING_METHOD_ENUM_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Common/PlainTextStringBuilder.cxx000066400000000000000000000051451300200146000271330ustar00rootroot00000000000000 /*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 __PLAIN_TEXT_STRING_BUILDER_DECLARE__ #include "PlainTextStringBuilder.h" #undef __PLAIN_TEXT_STRING_BUILDER_DECLARE__ #include "CaretAssert.h" #include "CaretLogger.h" using namespace caret; /** * \class caret::PlainTextStringBuilder * \brief * \ingroup Common * * */ /** * Constructor. */ PlainTextStringBuilder::PlainTextStringBuilder() : CaretObject() { m_currentIndentationCount = 0; m_indentationNumberOfSpaces = 3; } /** * Constructor. */ PlainTextStringBuilder::PlainTextStringBuilder(const int32_t indentationNumberOfSpaces) : CaretObject() { m_currentIndentationCount = 0; m_indentationNumberOfSpaces = indentationNumberOfSpaces; } /** * Destructor. */ PlainTextStringBuilder::~PlainTextStringBuilder() { } /** * Clear the text. Does not change amount of indentation. */ void PlainTextStringBuilder::clear() { m_text = ""; } /** * Increase the indentation. */ void PlainTextStringBuilder::pushIndentation() { m_currentIndentationCount += m_indentationNumberOfSpaces; } /** * Decrease the indentation. */ void PlainTextStringBuilder::popIndentation() { m_currentIndentationCount -= m_indentationNumberOfSpaces; if (m_currentIndentationCount < 0) { CaretLogSevere("Indentation pops exceeds pushes (indent < 0)"); m_currentIndentationCount = 0; } } /** * Add the text preceded by indentation and followed by a newline. * * @param text * Text that is added. */ void PlainTextStringBuilder::addLine(const AString& text) { m_text += (AString().fill(' ', m_currentIndentationCount) + text + "\n"); } /** * @return The text */ AString PlainTextStringBuilder::getText() const { return m_text; } connectome-workbench-1.2.3+git41-gc4c6c90/src/Common/PlainTextStringBuilder.h000066400000000000000000000040371300200146000265570ustar00rootroot00000000000000#ifndef __PLAIN_TEXT_STRING_BUILDER_H__ #define __PLAIN_TEXT_STRING_BUILDER_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" namespace caret { class PlainTextStringBuilder : public CaretObject { public: PlainTextStringBuilder(); PlainTextStringBuilder(const int32_t indentationNumberOfSpaces); virtual ~PlainTextStringBuilder(); void clear(); void pushIndentation(); void popIndentation(); /** Add the text preceded by indentation and followed by a newline */ void addLine(const AString& text); /** Get the text */ AString getText() const; private: PlainTextStringBuilder(const PlainTextStringBuilder&); PlainTextStringBuilder& operator=(const PlainTextStringBuilder&); AString m_text; int32_t m_indentationNumberOfSpaces; int32_t m_currentIndentationCount; // ADD_NEW_MEMBERS_HERE }; #ifdef __PLAIN_TEXT_STRING_BUILDER_DECLARE__ // #endif // __PLAIN_TEXT_STRING_BUILDER_DECLARE__ } // namespace #endif //__PLAIN_TEXT_STRING_BUILDER_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Common/Plane.cxx000066400000000000000000000353571300200146000235740ustar00rootroot00000000000000/*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 "MathFunctions.h" #include "Plane.h" using namespace caret; /** * Construct an invalid plane. * Intended for use by the assignement operator. */ Plane::Plane() : CaretObject() { m_pointOnPlane[0] = 0.0; m_pointOnPlane[1] = 0.0; m_pointOnPlane[2] = 0.0; m_normalVector[0] = 0.0; m_normalVector[1] = 0.0; m_normalVector[2] = 0.0; m_validPlaneFlag = false; m_A = m_normalVector[0]; m_B = m_normalVector[1]; m_C = m_normalVector[2]; m_D = 0.0; } /** * Construct a plane from three points that are on the plane. * These points should be in counter-clockwise order. * * @param p1 Point on plane. * @param p2 Point on plane. * @param p3 Point on plane. * */ Plane::Plane(const float p1[3], const float p2[3], const float p3[3]) : CaretObject() { m_pointOnPlane[0] = p1[0]; m_pointOnPlane[1] = p1[1]; m_pointOnPlane[2] = p1[2]; double p2d[3] = { p2[0], p2[1], p2[2] }; double p3d[3] = { p3[0], p3[1], p3[2] }; m_validPlaneFlag = MathFunctions::normalVector(m_pointOnPlane, p2d, p3d, m_normalVector); // // Compute the plane equation // m_A = m_normalVector[0]; m_B = m_normalVector[1]; m_C = m_normalVector[2]; m_D = -(m_A*p1[0] + m_B*p1[1] + m_C*p1[2]); } /** * Construct a plane from a unit normal vector (length = 1) and a point on the plane. * * @param normalVector * The normal vector of the plane. * @param pointOnPlane * A point on the plane. */ Plane::Plane(const float unitNormalVector[3], const float pointOnPlane[3]) { m_pointOnPlane[0] = pointOnPlane[0]; m_pointOnPlane[1] = pointOnPlane[1]; m_pointOnPlane[2] = pointOnPlane[2]; m_normalVector[0] = unitNormalVector[0]; m_normalVector[1] = unitNormalVector[1]; m_normalVector[2] = unitNormalVector[2]; m_A = m_normalVector[0]; m_B = m_normalVector[1]; m_C = m_normalVector[2]; m_D = (-m_A * m_pointOnPlane[0] -m_B * m_pointOnPlane[1] -m_C * m_pointOnPlane[2]); m_validPlaneFlag = (MathFunctions::vectorLength(m_normalVector) > 0.0); } /** * Copy constructor. * @param p * Object that is copied. */ Plane::Plane(const Plane& p) : CaretObject(p) { this->copyHelperPlane(p); } /** * Assignment operator. * @param p * Data copied from obj to this. * @return * Reference to this object. */ Plane& Plane::operator=(const Plane& p) { if (this != &p) { CaretObject::operator=(p); this->copyHelperPlane(p); } return *this; } /** * Helps with copying an object of this type. * @param p * Object that is copied. */ void Plane::copyHelperPlane(const Plane& p) { m_A = p.m_A; m_B = p.m_B; m_C = p.m_C; m_D = p.m_D; m_normalVector[0] = p.m_normalVector[0]; m_normalVector[1] = p.m_normalVector[1]; m_normalVector[2] = p.m_normalVector[2]; m_pointOnPlane[0] = p.m_pointOnPlane[0]; m_pointOnPlane[1] = p.m_pointOnPlane[1]; m_pointOnPlane[2] = p.m_pointOnPlane[2]; m_validPlaneFlag = p.m_validPlaneFlag; } /** * Destructor */ Plane::~Plane() { } /** * Is the plane valid? * @return true if plane is valid, else false. * */ bool Plane::isValidPlane() const { return m_validPlaneFlag; } /** * Find the points where a triangle intersects the plane. * @param t1 First point in triangle. * @param t2 Second point in triangle. * @param t3 Third point in triangle. * @param intersectionPointOut1 If there is intersection, * this will be loaded with one intersection point. * @param intersectionPointOut2 If there is intersection, * this will be loaded with other intersection point. * @return true if any two edges of the triangle intersect * the plane, else false. * */ bool Plane::triangleIntersectPlane( const float t1[3], const float t2[3], const float t3[3], float intersectionPointOut1[3], float intersectionPointOut2[3]) const { float* intersection = intersectionPointOut1; int count = 0; if (lineSegmentIntersectPlane(t1, t2, intersection)) { count++; intersection = intersectionPointOut2; } if (lineSegmentIntersectPlane(t2, t3, intersection)) { count++; if (count == 2) { return true; } intersection = intersectionPointOut2; } if (lineSegmentIntersectPlane(t3, t1, intersection)) { count++; if (count == 2) { return true; } } return false; } /** * Get the plane. * * @param aOut * The value of 'A' * @param bOut * The value of 'B' * @param cOut * The value of 'C' * @param dOut * The value of 'D' */ void Plane::getPlane(double& aOut, double& bOut, double& cOut, double& dOut) const { aOut = m_A; bOut = m_B; cOut = m_C; dOut = m_D; } /** * Get the plane's normal vector. * * @param normalVectorOut * On exit, contains the plane's normal vector. */ void Plane::getNormalVector(double normalVectorOut[3]) const { normalVectorOut[0] = m_normalVector[0]; normalVectorOut[1] = m_normalVector[1]; normalVectorOut[2] = m_normalVector[2]; } /** * Get the plane's normal vector. * * @param normalVectorOut * On exit, contains the plane's normal vector. */ void Plane::getNormalVector(float normalVectorOut[3]) const { normalVectorOut[0] = m_normalVector[0]; normalVectorOut[1] = m_normalVector[1]; normalVectorOut[2] = m_normalVector[2]; } /** * Get absolute distance of point from the plane. * @param p Point. * @return Absolute distance of "p" from plane. * */ double Plane::absoluteDistanceToPlane(const float p[3]) const { double dist = (m_A * p[0] + m_B * p[1] + m_C * p[2] + m_D); if (dist < 0.0f) dist = -dist; return dist; } /** * Get signed distance of point from the plane. * @param p Point. * @return Signed distance of "p" from plane. * */ double Plane::signedDistanceToPlane(const float p[3]) const { double dist = (m_A * p[0] + m_B * p[1] + m_C * p[2] + m_D); return dist; } /** * Find the intersection of a line segment with the plane. * @param lp1 start of line. * @param lp2 end of line. * @param intersectionOut If the line intersects the plane, * the point of intersection will be loaded into this. If * there is no intersection, the values of this array will * not be altered. * @return true if the line segment intersects the plane, * else false. * */ bool Plane::lineSegmentIntersectPlane( const float lp1[3], const float lp2[3], float intersectionOut[3]) const { /* * Ray formed by lp1 ==> lp2 (NOT a unit vector; do not normalize) */ double r0 = lp2[0] - lp1[0]; double r1 = lp2[1] - lp1[1]; double r2 = lp2[2] - lp1[2]; /* * Denominator is dot product of the plane's normal * and the ray. If it is zero, that means the ray * is parallel to the plane. * * Perhaps should test for value near zero, and, * if it is, see if line end points are very * close to plane, and, if so, use the line * endpoints, projected to the plane, as the * intersection. */ double denominator = (m_A * r0) + (m_B * r1) + (m_C * r2); if (denominator == 0.0) { return false; } /* * "t" is the normalized distance of plane from the line origin to * the endpoint of the line. * * (0 <= t <= 1): The line intersects the plane with one point * above the plane and one point below the plane. * * (t > 1): Vector (P1, P2) point towards plane but both are * on same side of the plane. * * (t < 0): Vector (P1, P2) point away from plane but both are * on same side of the plane. */ double numerator = -((m_A * lp1[0]) + (m_B * lp1[1]) + (m_C * lp1[2]) + m_D); double t = numerator / denominator; if ((t >= 0.0f) && (t <= 1.0f)) { intersectionOut[0] = (float)(lp1[0] + r0 * t); intersectionOut[1] = (float)(lp1[1] + r1 * t); intersectionOut[2] = (float)(lp1[2] + r2 * t); return true; } return false; } /** * Project the given point to the plane. * @param pointIn * Point that will be projected. * @param pointProjectedOut * Coordinates of point after projection to the plane. */ void Plane::projectPointToPlane(const float pointIn[3], float pointProjectedOut[3]) const { double xo[3] = { pointIn[0] - m_pointOnPlane[0], pointIn[1] - m_pointOnPlane[1], pointIn[2] - m_pointOnPlane[2] }; float t = MathFunctions::dotProduct(m_normalVector, xo); pointProjectedOut[0] = pointIn[0] - (t * m_normalVector[0]); pointProjectedOut[1] = pointIn[1] - (t * m_normalVector[1]); pointProjectedOut[2] = pointIn[2] - (t * m_normalVector[2]); } /** * Determine if and where a ray intersects the plane. * * @param rayOrigin * Origin of the ray * @param rayVector * Vector defining the ray. * @param intersectionXYZandDistance * Coordinate of where the ray intersects the plane (XYZ) and the * distance of the ray origin from the plane. * @return * True if the ray intersects the plane, else false. */ bool Plane::rayIntersection(const float rayOrigin[3], const float rayVector[3], float intersectionXYZandDistance[4]) { /* Convert the ray into a unit vector * */ double ray[3] = { rayVector[0], rayVector[1], rayVector[2] }; MathFunctions::normalizeVector(ray); /* * Parametric coordinate of where ray intersects plane */ double denom = m_A * ray[0] + m_B * ray[1] + m_C * ray[2]; if (denom != 0) { const double t = -(m_A * rayOrigin[0] + m_B * rayOrigin[1] + m_C * rayOrigin[2] + m_D) / denom; intersectionXYZandDistance[0] = (float)(rayOrigin[0] + ray[0] * t); intersectionXYZandDistance[1] = (float)(rayOrigin[1] + ray[1] * t); intersectionXYZandDistance[2] = (float)(rayOrigin[2] + ray[2] * t); intersectionXYZandDistance[3] = (float)t; return true; } return false; } /** * @return String describing the plane. * */ AString Plane::toString() const { AString s; if (isValidPlane()) { s = (AString::number(m_A) + "x + " + AString::number(m_B) + "y + " + AString::number(m_C) + "z + " + AString::number(m_D)); } else { s = "invalid"; } return s; } /** * Unit test the class. * */ void Plane::unitTest(std::ostream& stream, const bool /*isVerbose*/) { stream << "Plane::unitTest is starting" << std::endl; Plane::unitTest1(stream); Plane::unitTest2(stream); Plane::unitTest3(stream); stream << "Plane::unitTest has ended" << std::endl; } /** * Unit test a plane and line intersection. * @param testName Name of test. * @param p1 Plane Point 1. * @param p2 Plane Point 2. * @param p3 Plane Point 3. * @param l1 Line End Point 1. * @param l2 Line End Point 2. * @param correctIntersectionPoint * @param intersectionValid * */ void Plane::unitTestLineIntersectPlane(std::ostream& stream, const AString& testName, const float p1[3], const float p2[3], const float p3[3], const float l1[3], const float l2[3], const float correctIntersectionPoint[3], const bool intersectionValid) { float intersection[3]; Plane p(p1, p2, p3); bool result = p.lineSegmentIntersectPlane(l1, l2, intersection); AString sb; if (intersectionValid && result) { if (MathFunctions::compareArrays(correctIntersectionPoint, intersection, 3, 0.001f)) { return; } sb.append("Intersection should be " + AString::fromNumbers(correctIntersectionPoint, 3, ",")); sb.append(" but is " + AString::fromNumbers(intersection, 3, ",")); } else if (intersectionValid != result) { sb.append("intersection " + AString::fromBool(result) + " but should be " + AString::fromBool(intersectionValid)); } if (sb.length() > 0) { sb = ("Line/Plane Intersection Test " + testName + " FAILED: " + sb); stream << sb.toStdString() << std::endl; } } void Plane::unitTest1(std::ostream& stream) { float p1[] = { -50.0f, -60.0f, 2.0f }; float p2[] = { 50.0f, -60.0f, 2.0f }; float p3[] = { 50.0f, 60.0f, 2.0f }; float l1[] = { 25.0f, 10.0f, -3.0f }; float l2[] = { 25.0f, 10.0f, 7.0f }; float correctIntersectionPoint[] = { 25.0f, 10.0f, 2.0f }; Plane::unitTestLineIntersectPlane(stream, "1", p1, p2, p3, l1, l2, correctIntersectionPoint, true); } void Plane::unitTest2(std::ostream& stream) { float p1[] = { -50.0f, -60.0f, 2.0f }; float p2[] = { 50.0f, -60.0f, 2.0f }; float p3[] = { 50.0f, 60.0f, 2.0f }; float l1[] = { 25.0f, 10.0f, 7.0f }; float l2[] = { 25.0f, 10.0f, 9.0f }; float correctIntersectionPoint[3]; Plane::unitTestLineIntersectPlane(stream, "2", p1, p2, p3, l1, l2, correctIntersectionPoint, false);} void Plane::unitTest3(std::ostream& stream) { float p1[] = { -50.0f, -60.0f, 2.0f }; float p2[] = { 50.0f, -60.0f, 2.0f }; float p3[] = { 50.0f, 60.0f, 2.0f }; float l1[] = { 25.0f, 10.0f, -5.0f }; float l2[] = { 25.0f, 10.0f, -3.0f }; float correctIntersectionPoint[3]; Plane::unitTestLineIntersectPlane(stream, "3", p1, p2, p3, l1, l2, correctIntersectionPoint, false); } connectome-workbench-1.2.3+git41-gc4c6c90/src/Common/Plane.h000066400000000000000000000077451300200146000232210ustar00rootroot00000000000000#ifndef __PLANE_H__ #define __PLANE_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" namespace caret { ///Operations on a plane. class Plane : public CaretObject { public: Plane(); Plane(const float p1[3], const float p2[3], const float p3[3]); Plane(const float unitNormalVector[3], const float pointOnPlane[3]); virtual ~Plane(); Plane(const Plane& p); Plane& operator=(const Plane& p); bool isValidPlane() const; bool triangleIntersectPlane( const float t1[3], const float t2[3], const float t3[3], float intersectionPointOut1[3], float intersectionPointOut2[3]) const; double absoluteDistanceToPlane(const float p[3]) const; double signedDistanceToPlane(const float p[3]) const; bool lineSegmentIntersectPlane( const float lp1[3], const float lp2[3], float intersectionOut[3]) const; void projectPointToPlane(const float pointIn[3], float pointProjectedOut[3]) const; void getPlane(double& aOut, double& bOut, double& cOut, double& dOut) const; void getNormalVector(double normalVectorOut[3]) const; void getNormalVector(float normalVectorOut[3]) const; bool rayIntersection(const float rayOrigin[3], const float rayVector[3], float intersectionXYZandDistance[4]); virtual AString toString() const; static void unitTest(std::ostream& stream, const bool isVerbose); private: static void unitTestLineIntersectPlane(std::ostream& stream, const AString& testName, const float p1[3], const float p2[3], const float p3[3], const float l1[3], const float l2[3], const float correctIntersectionPoint[3], const bool intersectionValid); static void unitTest1(std::ostream& stream); static void unitTest2(std::ostream& stream); static void unitTest3(std::ostream& stream); void copyHelperPlane(const Plane& p); double m_pointOnPlane[3]; double m_normalVector[3]; bool m_validPlaneFlag; double m_A; double m_B; double m_C; double m_D; }; } // namespace #endif // __PLANE_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Common/ProgramParameters.cxx000066400000000000000000000216071300200146000261610ustar00rootroot00000000000000/*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 "CaretAssert.h" #include "ProgramParameters.h" using namespace caret; /** * Constructor. * * @param argc * Number of parameters. * @param argv * Array containing the parameters. */ ProgramParameters::ProgramParameters(int argc, const char *const argv[]) : CaretObject() { this->initializeMembersProgramParameters(); if (argc > 0) { this->programName = AString::fromLocal8Bit(argv[0]); } for (int32_t i = 1; i < argc; i++) { this->addParameter(AString::fromLocal8Bit(argv[i])); } } /** * Constructor. */ ProgramParameters::ProgramParameters() : CaretObject() { this->initializeMembersProgramParameters(); } /** * Destructor */ ProgramParameters::~ProgramParameters() { this->initializeMembersProgramParameters(); } void ProgramParameters::initializeMembersProgramParameters() { this->parameterIndex = 0; this->parameters.clear(); } /** * Add a parameter. * @param p New parameter. * */ void ProgramParameters::addParameter(const AString& p) { this->parameters.push_back(p); } /** * See if there are more parameters available. * * @return true if more parameters available else false. * */ bool ProgramParameters::hasNext() const { return (this->parameterIndex < static_cast(this->parameters.size())); } /** * Verifies that all parameters have been processed. * * @throws ProgramParametersException If there are more parameters. * */ void ProgramParameters::verifyAllParametersProcessed() { if (this->hasNext()) { throw ProgramParametersException("Unexpected parameter: " + nextString("unexpected")); } } /** * Get the next parameter as a string. * * @param parameterName - name of the parameter. * @return The parameter. * @throws ProgramParametersException If there are no more parameters. * */ AString ProgramParameters::nextString(const AString& parameterName) { if (this->hasNext() == false) { throw ProgramParametersException(parameterName + " is missing (No more parameters)."); } CaretAssertVectorIndex(this->parameters, this->parameterIndex); AString s = this->parameters[this->parameterIndex]; this->parameterIndex++; return s; } /** * Get the next parameter as a boolean * * @param parameterName - name of the parameter. * @return boolean value of parameter. * * @throws ProgramParametersException If there are no more parameters or the * next parameter does not represent a boolean value. * */ bool ProgramParameters::nextBoolean(const AString& parameterName) { AString s = this->nextString(parameterName).toLower(); if ((s == "true") || (s == "t")) { return true; } else if ((s == "false") || (s == "f")) { return false; } throw ProgramParametersException(parameterName + " is not a boolean value (true/false) but is \"" + s + "\"."); return false; } /** * Get the next parameter as a int. * * @param parameterName - name of the parameter. * @return int value of parameter. * @throws ProgramParametersException If there are no more parameters or the * next parameter does not represent an integer value. * */ int32_t ProgramParameters::nextInt(const AString& parameterName) { AString s = this->nextString(parameterName); bool ok = false; int32_t i = s.toInt(&ok); if (!ok) { throw ProgramParametersException(parameterName + " needs an integer, got \"" + s + "\"."); } return i; } /** * Get the next parameter as a long. * * @param parameterName - name of the parameter. * @return inlongt value of parameter. * @throws ProgramParametersException If there are no more parameters or the * next parameter does not represent an integer value. * */ int64_t ProgramParameters::nextLong(const AString& parameterName) { AString s = this->nextString(parameterName); bool ok = false; int64_t i = s.toLong(&ok); if (!ok) { throw ProgramParametersException(parameterName + " needs an integer, got \"" + s + "\"."); } return i; } /** * Get the next parameter as a float. * * @param parameterName - name of the parameter. * @return float value of parameter. * @throws ProgramParametersException If there are no more parameters or the * next parameter does not represent an float value. * */ float ProgramParameters::nextFloat(const AString& parameterName) { AString s = this->nextString(parameterName); bool ok = false; float f = s.toFloat(&ok); if (!ok) { throw ProgramParametersException(parameterName + " needs a floating point, got \"" + s + "\"."); } return f; } /** * Get the next parameter as a double. * * @param parameterName - name of the parameter. * @return double value of parameter. * @throws ProgramParametersException If there are no more parameters or the * next parameter does not represent an double value. * */ double ProgramParameters::nextDouble(const AString& parameterName) { AString s = this->nextString(parameterName); bool ok = false; double d = s.toDouble(&ok); if (!ok) { throw ProgramParametersException(parameterName + " needs a floating point, got \"" + s + "\"."); } return d; } /** * Backup to the previous parameter. If currently at the first * parameter, this command does nothing. * */ void ProgramParameters::backup() { if (this->parameterIndex > 0) { this->parameterIndex--; } } /** * Remove the parameter that was last retrieved with one of the * "next()" methods. */ void ProgramParameters::remove() { this->backup(); CaretAssertVectorIndex(this->parameters, this->parameterIndex); this->parameters.erase(this->parameters.begin() + this->parameterIndex); } /** * Get the current index of the parameter iterator. * @return Index of parameter iterator. * */ int32_t ProgramParameters::getParameterIndex() const { return this->parameterIndex; } /** * Set the current index of the parameter iterator. * @param indx New index of parameter iterator. * @throws ParameterException If index is not valid for parameters * unless the index is zero and there are no parameters. * */ void ProgramParameters::setParameterIndex(const int32_t indx) { CaretAssert(indx >= 0 && indx <= (int32_t)this->parameters.size());//less or equal to size is intentional, having an index of one past the end is a valid state, notably needed by setParameterIndex(0) on empty ProgramParameters this->parameterIndex = indx; } /** * Get the number of parameters. * * @return The number of parameters. * */ int32_t ProgramParameters::getNumberOfParameters() const { return this->parameters.size(); } /** * Get a parameter at the specified index. * * @param index - index of parameter. * @return The parameter at the specified index. * */ AString ProgramParameters::getParameter(const int32_t indx) const { CaretAssertVectorIndex(this->parameters, indx); return this->parameters[indx]; } /** * Get all of the parameters, separated by a space. * @return All parameters in a String. * */ AString ProgramParameters::getAllParametersInString() const { AString str; int num = this->getNumberOfParameters(); for (int i = 0; i < num; i++) { if (i > 0) { str += " "; } str += this->getParameter(i); } return str; } /** * Get all of the parameters, separated by a space. * @return All parameters in a String. * */ AString ProgramParameters::getAllParametersQuotedInString() const { AString str; int num = this->getNumberOfParameters(); for (int i = 0; i < num; i++) { if (i > 0) { str += " "; } str += "\"" + this->getParameter(i) + "\""; } return str; } /** * Get the name of the program. * @return * Name of the program. */ AString ProgramParameters::getProgramName() const { return this->programName; } connectome-workbench-1.2.3+git41-gc4c6c90/src/Common/ProgramParameters.h000066400000000000000000000046641300200146000256120ustar00rootroot00000000000000#ifndef __PROGRAMPARAMETERS_H__ #define __PROGRAMPARAMETERS_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 "ProgramParametersException.h" #include #include #include namespace caret { /** * Simplifies access to program parameters. */ class ProgramParameters : public CaretObject { public: ProgramParameters(int argc, const char *const argv[]); ProgramParameters(); virtual ~ProgramParameters(); private: ProgramParameters(const ProgramParameters&); ProgramParameters& operator=(const ProgramParameters&); private: void initializeMembersProgramParameters(); public: void addParameter(const AString& p); bool hasNext() const; void verifyAllParametersProcessed(); AString nextString(const AString& parameterName); bool nextBoolean(const AString& parameterName); int32_t nextInt(const AString& parameterName); int64_t nextLong(const AString& parameterName); float nextFloat(const AString& parameterName); double nextDouble(const AString& parameterName); void backup(); void remove(); int32_t getParameterIndex() const; void setParameterIndex(const int32_t index); int32_t getNumberOfParameters() const; AString getParameter(const int32_t index) const; AString getAllParametersInString() const; AString getAllParametersQuotedInString() const; AString getProgramName() const; private: /**The parameters. */ std::vector parameters; /**Current index in parameters. */ int32_t parameterIndex; AString programName; }; } // namespace #endif // __PROGRAMPARAMETERS_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Common/ProgramParametersException.cxx000066400000000000000000000045031300200146000300340ustar00rootroot00000000000000/*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 "ProgramParametersException.h" #include using namespace caret; /** * Constructor. * */ ProgramParametersException::ProgramParametersException() : CaretException() { this->initializeMembersProgramParametersException(); } /** * Constructor that uses stack trace from the exception * passed in as a parameter. * * @param e Any exception whose stack trace becomes * this exception's stack trace. * */ ProgramParametersException::ProgramParametersException( const CaretException& e) : CaretException(e) { this->initializeMembersProgramParametersException(); } /** * Constructor. * * @param s Description of the exception. * */ ProgramParametersException::ProgramParametersException(const AString& s) : CaretException(s) { this->initializeMembersProgramParametersException(); } /** * Copy Constructor. * @param e * Exception that is copied. */ ProgramParametersException::ProgramParametersException(const ProgramParametersException& e) : CaretException(e) { } /** * Assignment operator. * @param e * Exception that is copied. * @return * Copy of the exception. */ ProgramParametersException& ProgramParametersException::operator=(const ProgramParametersException& e) { if (this != &e) { CaretException::operator=(e); } return *this; } /** * Destructor */ ProgramParametersException::~ProgramParametersException() throw() { } void ProgramParametersException::initializeMembersProgramParametersException() { } connectome-workbench-1.2.3+git41-gc4c6c90/src/Common/ProgramParametersException.h000066400000000000000000000031501300200146000274560ustar00rootroot00000000000000#ifndef __PROGRAM_PARAMETERS_EXCEPTION_H__ #define __PROGRAM_PARAMETERS_EXCEPTION_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 "CaretException.h" namespace caret { /** * An exception thrown during parameter processing. */ class ProgramParametersException : public CaretException { public: ProgramParametersException(); ProgramParametersException(const CaretException& e); ProgramParametersException(const AString& s); ProgramParametersException(const ProgramParametersException& e); ProgramParametersException& operator=(const ProgramParametersException& e); virtual ~ProgramParametersException() throw(); private: void initializeMembersProgramParametersException(); }; } // namespace #endif // __PROGRAM_PARAMETERS_EXCEPTION_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Common/ProgressObject.cxx000066400000000000000000000202121300200146000254500ustar00rootroot00000000000000/*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 "ProgressObject.h" #include "CaretAssert.h" #include "EventProgressUpdate.h" #include "EventManager.h" #include using namespace std; using namespace caret; ///decrease these values to reduce progress bar overhead everywhere ///or, manually set them for any fast algorithm that calls reportProgress a lot (or change it so it calls reportProgress less often) const float ProgressObject::MAX_CHILD_RESOLUTION = 0.01f;//up to 100 calls per child algorithm const float ProgressObject::MAX_INTERNAL_RESOLUTION = 0.001f;//up to 1000 calls during internal processing ProgressObject* ProgressObject::addAlgorithm(const float weight, const float childResolution) { CaretAssertMessage(weight > 0.0f, "nonpositive weight in ProgressObject::addAlgorithm"); if (m_disabled) return this;//disabled short circuits everything, can't track progress if an algorithm ignores and forwards the pointer ProgressInfo newInfo; newInfo.completed = false; newInfo.curProgress = 0.0f; newInfo.weight = weight; newInfo.progObjRef = new ProgressObject(weight, childResolution); newInfo.progObjRef->m_parent = this; newInfo.progObjRef->m_parentIndex = m_children.size(); m_children.push_back(newInfo); float childWeight = 0.0f; vector::iterator myend = m_children.end(); for (vector::iterator iter = m_children.begin(); iter != myend; ++iter) { childWeight += iter->weight; } m_totalWeight = childWeight + m_nonChildWeight; return newInfo.progObjRef; } void ProgressObject::algorithmStartSentinel() { if (m_sentinelPassed) { m_disabled = true;//if it hits start twice (passed through an algorithm without interaction), disable it } else { m_sentinelPassed = true; } } void ProgressObject::finishLevel() { if (m_finished) return;//don't finish twice m_currentProgress = m_totalWeight; m_finished = true; if (m_parent != NULL) { m_parent->m_children[m_parentIndex].completed = true; m_parent->updateProgress(); } EventProgressUpdate myUpdate(this); myUpdate.m_finished = true; EventManager::get()->sendEvent(myUpdate.getPointer()); } void ProgressObject::forceFinish() { finishLevel();//this function is mostly to insert the word "force" so people think twice about using it } float ProgressObject::getCurrentProgressFraction() { if (m_totalWeight <= 0.0f) return 0.0f; return m_currentProgress / m_totalWeight; } float ProgressObject::getCurrentProgressPercent() { return getCurrentProgressFraction() * 100.0f; } const AString& ProgressObject::getTaskDescription() { return m_description; } ProgressObject::ProgressObject(const float weight, const float childResolution) { m_currentProgress = 0.0f; m_disabled = false; m_finished = false; m_lastReported = 0.0f; m_nonChildProgress = 0.0f; m_nonChildWeight = weight; m_parent = NULL; m_sentinelPassed = false; m_totalWeight = weight; m_childResolution = childResolution; } LevelProgress::LevelProgress(ProgressObject* myProgObj, const float finishedProgress, const float internalWeight, const float internalResolution) { CaretAssertMessage(internalWeight > 0.0f, "nonpositive weight in ProgressObject::startLevel"); m_lastReported = 0.0f; m_maximum = finishedProgress; m_progObjRef = myProgObj; m_internalResolution = max(internalResolution, ProgressObject::MAX_INTERNAL_RESOLUTION);//the lower the value, the more often it updates if (m_progObjRef != NULL) { m_progObjRef->setInternalWeight(internalWeight); EventProgressUpdate myUpdate(myProgObj); myUpdate.m_starting = true; EventManager::get()->sendEvent(myUpdate.getPointer()); } } void ProgressObject::setInternalWeight(const float& myInternalWeight) { m_nonChildWeight = myInternalWeight; float childWeight = 0.0f; vector::iterator myend = m_children.end(); for (vector::iterator iter = m_children.begin(); iter != myend; ++iter) { childWeight += iter->weight; } m_totalWeight = childWeight + m_nonChildWeight; } void ProgressObject::updateProgress() { if (m_disabled) return; if (m_finished) { return;//nothing to do, finishLevel() should have taken care of everything } float totalWeightComplete = 0.0f;//recalculating the sum is thread-safe, as long as float assignment is atomic vector::iterator myend = m_children.end(); for (vector::iterator iter = m_children.end(); iter < myend; ++iter) { if (iter->completed) { totalWeightComplete += iter->weight; } else { totalWeightComplete += iter->curProgress * iter->weight; } } if (m_nonChildProgress > 0.0f && m_nonChildWeight > 0.0f) { totalWeightComplete += m_nonChildProgress * m_nonChildWeight; } if (totalWeightComplete < m_totalWeight) { if (totalWeightComplete < 0.0f) { m_currentProgress = 0.0f; } else { m_currentProgress = totalWeightComplete / m_totalWeight; } } else { m_currentProgress = 1.0f; } if (m_parent != NULL) { m_parent->m_children[m_parentIndex].curProgress = m_currentProgress; if (m_currentProgress - m_lastReported > m_childResolution) {//don't recurse unless progress has changed more than the resolution specified m_lastReported = m_currentProgress; m_parent->updateProgress(); } } EventProgressUpdate myUpdate(this);//just send the event, LevelProgress should already have checked if the amount of change was significant myUpdate.m_amountUpdate = true; EventManager::get()->sendEvent(myUpdate.getPointer()); } bool ProgressObject::isDisabled() { return m_disabled; } ProgressObject::~ProgressObject() { finishLevel();//so that things listening for progress events are kept consistent vector::iterator myend = m_children.end(); for (vector::iterator iter = m_children.begin(); iter != myend; ++iter) { if (iter->progObjRef != NULL) { delete iter->progObjRef; iter->progObjRef = NULL; } } } void LevelProgress::reportProgress(const float currentTotal) { if (m_progObjRef == NULL || m_progObjRef->m_disabled) return; float curProgress = currentTotal / m_maximum; if (curProgress > 1.0f) { curProgress = 1.0f; } if (curProgress < m_lastReported) { curProgress = m_lastReported; } m_progObjRef->m_nonChildProgress = curProgress; if (curProgress - m_lastReported > m_internalResolution) { m_lastReported = curProgress; m_progObjRef->updateProgress(); } } void LevelProgress::setTask(const AString& taskDescription) {//maybe this should be in a setter in m_progObjRef, here for coherence with progress reporting if (m_progObjRef == NULL) return; m_progObjRef->m_description = taskDescription; EventProgressUpdate myUpdate(m_progObjRef); myUpdate.m_textUpdate = true; EventManager::get()->sendEvent(myUpdate.getPointer()); } LevelProgress::~LevelProgress() { if (m_progObjRef == NULL) return; m_progObjRef->finishLevel();//finish level on destruction of the object, for automatic detection of algorithm finishing } connectome-workbench-1.2.3+git41-gc4c6c90/src/Common/ProgressObject.h000066400000000000000000000121161300200146000251010ustar00rootroot00000000000000#ifndef __PROGRESS_OBJECT_H__ #define __PROGRESS_OBJECT_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 "stdint.h" #include #include "AString.h" namespace caret { class LevelProgress; //NOTE: this tries to intelligently avoid doing recursive progress updates when the value doesn't change much class ProgressObject { struct ProgressInfo { ProgressObject* progObjRef;//used to clean up the memory on finish() float weight; float curProgress; bool completed; }; std::vector m_children; float m_totalWeight; float m_nonChildWeight; float m_nonChildProgress; float m_currentProgress; float m_lastReported; float m_childResolution; AString m_description; ProgressObject* m_parent; int32_t m_parentIndex;//which index in parent's vector this object is bool m_sentinelPassed; bool m_disabled;//disables itself if sentinel called twice bool m_finished; void updateProgress();//used by LevelProgress to report changes void finishLevel();//moves this progress object to 100%, then updates parent if not NULL void setInternalWeight(const float& myInternalWeight);//used by LevelProgress when you start a level ProgressObject(); public: //don't always report progress, in case someone uses this in an inner loop const static float MAX_CHILD_RESOLUTION; const static float MAX_INTERNAL_RESOLUTION; ///fill in weight with the ...Algorithm::getAlgorithmWeight() function at the root level (before starting the algorithm) ///if using multiple algorithms at the root level (shame!), sum their weights first, then use addAlgorithm to get objects for each ProgressObject(const float weight, const float childResolution = MAX_CHILD_RESOLUTION); ~ProgressObject(); ///call this on progress objects you make after an algorithm returns to ensure it finishes (in case it ignores the object) void forceFinish(); ///add an algorithm to this algorithm's progress status, and get a pointer to give to that algorithm ///fill in weight with the ...Algorithm::getAlgorithmWeight() function ProgressObject* addAlgorithm(const float weight, const float childResolution = MAX_CHILD_RESOLUTION); ///DO NOT USE: used by AbstractAlgorithm constructor to check for algorithms that ignore the object void algorithmStartSentinel(); ///get progress as a fraction of 1 (in range [0, 1]) float getCurrentProgressFraction(); ///get progress as percent (in range [0, 100]) float getCurrentProgressPercent(); ///get the description of the current task const AString& getTaskDescription(); ///true if algorithmStartSentinel disabled the object bool isDisabled(); //TODO: make something to return the statuses of all in-progress (nonzero curProgress) tasks for the entire tree, for detailed progress info //TODO: set up callbacks so progress changes don't have to be polled for friend class LevelProgress;//so that LevelProgress can report progress, but nothing else can }; class LevelProgress {//reports progress on processing done in this level float m_maximum; float m_lastReported; float m_internalResolution; ProgressObject* m_progObjRef; LevelProgress(); public: LevelProgress(ProgressObject* myProgObj, const float finishedProgress = 1.0f, const float internalWeight = 1.0f, const float internalResolution = ProgressObject::MAX_INTERNAL_RESOLUTION); ///call with the fraction of finishedProgress passed to ProgressObject::startLevel (default 1.0) that this algorithm has done internally ///work done by subalgorithms is automatically added and updated as progress is made, you do not need to call this unless the current algorithm does direct processing void reportProgress(const float currentTotal); ///set a description for current task, like the name of the subalgorithm you are about to call void setTask(const AString& taskDescription);//yes, this reaches through the class, but it is better to have both reporting functions on the same object ~LevelProgress();//automatically finishes level }; } #endif //__PROGRESS_OBJECT_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Common/ProgressReportingInterface.h000066400000000000000000000071671300200146000274770ustar00rootroot00000000000000#ifndef __PROGRESS_REPORTING_INTERFACE_H__ #define __PROGRESS_REPORTING_INTERFACE_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 caret::ProgressReportingInterface * \brief Interface for any process that may display progress in the GUI * * This interface is intended for any task that takes a non-trivial * amount of time to run. It allows display of the task's progress, * usually in the user-interface's progress dialog and provides feedback * to the user about how much work has been and still needs to be completed. * The interface also provides a mechanism for the user to cancel the * task and the task to detect that the user has requested the task * be cancelled. */ #include "AString.h" namespace caret { class ProgressReportingInterface { public: ProgressReportingInterface() { /* nothing */ } virtual ~ProgressReportingInterface() { /* nothing */ } /** * Used by task to set the range of progress reporting. * * @param minimumProgress * The minimum amount reported (typically zero). * @param maximumProgress * The maximum amount of progress. */ virtual void setProgressRange(const int minimumProgress, const int maximumProgress) = 0; /** * Used by task to set the current progress. * * @param progress * The current progress within range of the minimum and maximum. */ virtual void setProgressValue(const int progress) = 0; /** * Used by task to set the message describing the task's activity. * * @param message * Message that is displayed. */ virtual void setProgressMessage(const AString& message) = 0; /** * Used by the user-interface to request that the task end as * soon as possible. */ virtual void setCancelRequested() = 0; /** * @return "true" if a request been made to cancel the task, else false. * * Used by the task to see if the task should end as soon as * possible. If so, the task should clean up after itself * (release resources); */ virtual bool isCancelRequested() const = 0; private: ProgressReportingInterface(const ProgressReportingInterface&); ProgressReportingInterface& operator=(const ProgressReportingInterface&); public: // ADD_NEW_METHODS_HERE private: // ADD_NEW_MEMBERS_HERE }; #ifdef __PROGRESS_REPORTING_INTERFACE_DECLARE__ // #endif // __PROGRESS_REPORTING_INTERFACE_DECLARE__ } // namespace #endif //__PROGRESS_REPORTING_INTERFACE_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Common/ReductionEnum.cxx000066400000000000000000000137051300200146000253070ustar00rootroot00000000000000 /*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 "ReductionEnum.h" #include "CaretAssert.h" using namespace caret; using namespace std; vector ReductionEnum::enumData; bool ReductionEnum::initializedFlag = false; /** * Constructor. * * @param enumValue * An enumerated value. * @param integerCode * Integer code for this enumerated value. * * @param name * Name of enumerated value. * * @param guiName * User-friendly name for use in user-interface. */ ReductionEnum::ReductionEnum(const Enum enumValue, const AString& name, const AString& explanation) { this->enumValue = enumValue; this->name = name; this->explanation = explanation; } /** * Destructor. */ ReductionEnum::~ReductionEnum() { } /** * Initialize the enumerated metadata. */ void ReductionEnum::initialize() { if (initializedFlag) { return; } initializedFlag = true; enumData.push_back(ReductionEnum(MAX, "MAX", "the maximum value")); enumData.push_back(ReductionEnum(MIN, "MIN", "the minimum value")); enumData.push_back(ReductionEnum(INDEXMAX, "INDEXMAX", "the 1-based index of the maximum value")); enumData.push_back(ReductionEnum(INDEXMIN, "INDEXMIN", "the 1-based index of the minimum value")); enumData.push_back(ReductionEnum(SUM, "SUM", "add all values")); enumData.push_back(ReductionEnum(PRODUCT, "PRODUCT", "multiply all values")); enumData.push_back(ReductionEnum(MEAN, "MEAN", "the mean of the data")); enumData.push_back(ReductionEnum(STDEV, "STDEV", "the standard deviation (N denominator)")); enumData.push_back(ReductionEnum(SAMPSTDEV, "SAMPSTDEV", "the sample standard deviation (N-1 denominator)")); enumData.push_back(ReductionEnum(VARIANCE, "VARIANCE", "the variance of the data")); enumData.push_back(ReductionEnum(TSNR, "TSNR", "mean divided by sample standard deviation (N-1 denominator)")); enumData.push_back(ReductionEnum(COV, "COV", "sample standard deviation (N-1 denominator) divided by mean")); enumData.push_back(ReductionEnum(MEDIAN, "MEDIAN", "the median of the data")); enumData.push_back(ReductionEnum(MODE, "MODE", "the mode of the data")); enumData.push_back(ReductionEnum(COUNT_NONZERO, "COUNT_NONZERO", "the number of nonzero elements in the 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 ReductionEnum* ReductionEnum::findData(const Enum enumValue) { if (initializedFlag == false) initialize(); size_t num = enumData.size(); for (size_t i = 0; i < num; i++) { const ReductionEnum* 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 ReductionEnum::toName(Enum enumValue) { if (initializedFlag == false) initialize(); const ReductionEnum* enumInstance = findData(enumValue); if (enumInstance == NULL) return ""; return enumInstance->name; } /** * Get a string explaining the enumerated type. * @param enumValue * Enumerated value. * @return * String representing enumerated value. */ AString ReductionEnum::toExplanation(Enum enumValue) { if (initializedFlag == false) initialize(); const ReductionEnum* enumInstance = findData(enumValue); if (enumInstance == NULL) return ""; return enumInstance->explanation; } /** * 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. */ ReductionEnum::Enum ReductionEnum::fromName(const AString& name, bool* isValidOut) { if (initializedFlag == false) initialize(); bool validFlag = false; Enum enumValue = INVALID; for (std::vector::iterator iter = enumData.begin(); iter != enumData.end(); iter++) { const ReductionEnum& 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 ReductionEnum")); } 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 * except ALL. */ void ReductionEnum::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); } } connectome-workbench-1.2.3+git41-gc4c6c90/src/Common/ReductionEnum.h000066400000000000000000000050351300200146000247310ustar00rootroot00000000000000#ifndef __REDUCTION_ENUM_H__ #define __REDUCTION_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 { /** * \brief Enumerated type for a structure in a brain. * * Enumerated types for reduction operators. */ class ReductionEnum { public: /** * Enumerated values. */ enum Enum { INVALID, MAX, MIN, INDEXMAX, INDEXMIN, SUM, MEAN, STDEV, SAMPSTDEV, VARIANCE, TSNR, COV, PRODUCT, MEDIAN, MODE, COUNT_NONZERO }; ~ReductionEnum(); static AString toName(Enum enumValue); static AString toExplanation(Enum enumValue); static Enum fromName(const AString& name, bool* isValidOut); static void getAllEnums(std::vector& allEnums); private: ReductionEnum(const Enum enumValue, const AString& name, const AString& explanation); static const ReductionEnum* 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; /** The enumerated type value for an instance */ Enum enumValue; /** The name, a text string that is identical to the enumerated value */ AString name; /** An explanation of the function */ AString explanation; }; } // namespace #endif //__REDUCTION_ENUM_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Common/ReductionOperation.cxx000066400000000000000000000504611300200146000263430ustar00rootroot00000000000000/*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 "ReductionOperation.h" #include "CaretAssert.h" #include "CaretException.h" #include "MathFunctions.h" #include #include #include #include using namespace caret; using namespace std; float ReductionOperation::reduce(const float* data, const int64_t& numElems, const ReductionEnum::Enum& type) { CaretAssert(numElems > 0); switch (type) { case ReductionEnum::INVALID: throw CaretException("reduction requested with 'INVALID' method"); case ReductionEnum::SAMPSTDEV://all of these start by taking the average, for stability case ReductionEnum::TSNR: case ReductionEnum::COV: if (numElems < 2) throw CaretException("taking the sample standard deviation of 1 element would require dividing by zero"); case ReductionEnum::MEAN: case ReductionEnum::STDEV: case ReductionEnum::VARIANCE: case ReductionEnum::SUM: { double sum = 0.0; for (int64_t i = 0; i < numElems; ++i) sum += data[i]; switch (type) { case ReductionEnum::SUM: return sum; case ReductionEnum::MEAN: return sum / numElems; default: { float mean = sum / numElems; double residsqr = 0.0; for (int64_t i = 0; i < numElems; ++i) { float tempf = data[i] - mean; residsqr += tempf * tempf; } switch(type) { case ReductionEnum::STDEV: return sqrt(residsqr / numElems); case ReductionEnum::SAMPSTDEV: return sqrt(residsqr / (numElems - 1)); case ReductionEnum::VARIANCE: return residsqr / numElems; case ReductionEnum::TSNR: return mean / sqrt(residsqr / (numElems - 1)); case ReductionEnum::COV: return sqrt(residsqr / (numElems - 1)) / mean; default: CaretAssertMessage(0, "unhandled type in sum-based reduction"); return 0.0f; } } } } case ReductionEnum::PRODUCT: { double prod = 1.0; for (int64_t i = 0; i < numElems; ++i) prod *= data[i]; return prod; } case ReductionEnum::MAX: { float max = data[0]; for (int64_t i = 1; i < numElems; ++i) if (data[i] > max) max = data[i]; return max; } case ReductionEnum::MIN: { float min = data[0]; for (int64_t i = 1; i < numElems; ++i) if (data[i] < min) min = data[i]; return min; } case ReductionEnum::INDEXMAX: { float max = data[0]; int64_t index = 0; for (int64_t i = 1; i < numElems; ++i) { if (data[i] > max) { max = data[i]; index = i; } } return index + 1;//1-based, to match gui and column arguments } case ReductionEnum::INDEXMIN: { float min = data[0]; int64_t index = 0; for (int64_t i = 1; i < numElems; ++i) { if (data[i] < min) { min = data[i]; index = i; } } return index + 1; } case ReductionEnum::MEDIAN: { vector dataCopy(numElems); for (int64_t i = 0; i < numElems; ++i) dataCopy[i] = data[i]; sort(dataCopy.begin(), dataCopy.end()); if ((numElems & 1) == 0)//if even, average middle two { return (dataCopy[numElems / 2 - 1] + dataCopy[numElems / 2]) / 2.0f; } else { return dataCopy[numElems / 2];//otherwise, take the center } } case ReductionEnum::MODE: { vector dataCopy(numElems); for (int64_t i = 0; i < numElems; ++i) dataCopy[i] = data[i]; sort(dataCopy.begin(), dataCopy.end());//sort to put same-value next to each other, a hash based map could be faster for large arrays, but oh well int bestCount = 0, curCount = 1; float bestval = -1.0f, curval = dataCopy[0]; for (int64_t i = 1; i < numElems; ++i)//search for largest contiguous region { if (dataCopy[i] == curval) { ++curCount; } else { if (curCount > bestCount) { bestval = curval; bestCount = curCount; } curval = dataCopy[i]; curCount = 1; } } if (curCount > bestCount) { bestval = curval; bestCount = curCount; } return bestval; } case ReductionEnum::COUNT_NONZERO: { int64_t count = 0; for (int64_t i = 0; i < numElems; ++i) { if (data[i] != 0.0f) { ++count; } } return count; } } return 0.0f; } float ReductionOperation::reduceExcludeDev(const float* data, const int64_t& numElems, const ReductionEnum::Enum& type, const float& numDevBelow, const float& numDevAbove) { CaretAssert(numElems > 0); double sum = 0.0; int64_t validNum = 0; for (int64_t i = 0; i < numElems; ++i) { if (MathFunctions::isNumeric(data[i])) { ++validNum; sum += data[i]; } } if (validNum == 0) throw CaretException("all input values to reduceExcludeDev were non-numeric"); float mean = sum / validNum; double residsqr = 0.0; for (int64_t i = 0; i < numElems; ++i) { if (MathFunctions::isNumeric(data[i])) { float tempf = data[i] - mean; residsqr += tempf * tempf; } } float stdev = sqrt(residsqr / validNum); float low = mean - numDevBelow * stdev, high = mean + numDevAbove * stdev; switch (type)//special case things that use indices { case ReductionEnum::INDEXMAX: { float max = 0.0f; int64_t index = -1; bool first = true; for (int64_t i = 0; i < numElems; ++i) { if (MathFunctions::isNumeric(data[i]) && data[i] >= low && data[i] <= high && (first || data[i] > max)) { first = false; max = data[i]; index = i; } } return index + 1;//1-based, to match gui and column arguments } case ReductionEnum::INDEXMIN: { float min = 0.0f; int64_t index = -1; bool first = true; for (int64_t i = 0; i < numElems; ++i) { if (MathFunctions::isNumeric(data[i]) && data[i] >= low && data[i] <= high && (first || data[i] < min)) { first = false; min = data[i]; index = i; } } return index + 1;//1-based, to match gui and column arguments } default: break; } vector excluded; excluded.reserve(validNum); for (int64_t i = 0; i < numElems; ++i) { if (MathFunctions::isNumeric(data[i]) && data[i] >= low && data[i] <= high) excluded.push_back(data[i]); } if (excluded.size() == 0) throw CaretException("exclusion parameters to reduceExcludeDev resulted in no usable data"); if (type == ReductionEnum::SAMPSTDEV && excluded.size() < 2) throw CaretException("SAMPSTDEV requested in reduceExcludeDev when only 1 element passed the exclusion parameters"); return reduce(excluded.data(), excluded.size(), type); } float ReductionOperation::reduceOnlyNumeric(const float* data, const int64_t& numElems, const ReductionEnum::Enum& type) { CaretAssert(numElems > 0); switch (type)//special case things that use indices { case ReductionEnum::INDEXMAX: { float max = 0.0f; int64_t index = -1; bool first = true; for (int64_t i = 0; i < numElems; ++i) { if (MathFunctions::isNumeric(data[i]) && (first || data[i] > max)) { first = false; max = data[i]; index = i; } } if (first) throw CaretException("all input values to reduceOnlyNumeric were non-numeric"); return index + 1;//1-based, to match gui and column arguments } case ReductionEnum::INDEXMIN: { float min = 0.0f; int64_t index = -1; bool first = true; for (int64_t i = 0; i < numElems; ++i) { if (MathFunctions::isNumeric(data[i]) && (first || data[i] < min)) { first = false; min = data[i]; index = i; } } if (first) throw CaretException("all input values to reduceOnlyNumeric were non-numeric"); return index + 1;//1-based, to match gui and column arguments } default: break; } vector excluded; excluded.reserve(numElems); for (int64_t i = 0; i < numElems; ++i) { if (MathFunctions::isNumeric(data[i])) excluded.push_back(data[i]); } if (excluded.size() < 1) throw CaretException("all input values to reduceOnlyNumeric were non-numeric"); if (type == ReductionEnum::SAMPSTDEV && excluded.size() < 2) throw CaretException("SAMPSTDEV requested in reduceOnlyNumeric when only 1 element is numeric"); return reduce(excluded.data(), excluded.size(), type); } namespace { struct ValWeight {//for sorting based only on value, but keeping weight associated float value, weight; ValWeight(float v, float w) { value = v; weight = w; } inline bool operator<(const ValWeight& rhs) const { return value < rhs.value; } }; } float ReductionOperation::reduceWeighted(const float* data, const float* weights, const int64_t& numElems, const ReductionEnum::Enum& type) { CaretAssert(numElems > 0); switch (type) { case ReductionEnum::INVALID: throw CaretException("weighted reduction requested with 'INVALID' method"); case ReductionEnum::INDEXMAX: case ReductionEnum::INDEXMIN: case ReductionEnum::MIN: case ReductionEnum::MAX: case ReductionEnum::PRODUCT: case ReductionEnum::COUNT_NONZERO: throw CaretException("weighted reduction not supported for '" + ReductionEnum::toName(type) + "' method"); case ReductionEnum::SAMPSTDEV://all of these start by taking the average, for stability case ReductionEnum::TSNR: case ReductionEnum::COV: if (numElems < 2) throw CaretException("'SAMPSTDEV' weighted reduction on 1 element would require dividing by zero"); case ReductionEnum::MEAN: case ReductionEnum::STDEV: case ReductionEnum::VARIANCE: case ReductionEnum::SUM: { double accum = 0.0, weightsum = 0.0f; for (int i = 0; i < numElems; ++i) { accum += data[i] * weights[i]; weightsum += weights[i]; } if (type == ReductionEnum::SUM) return accum; const float mean = accum / weightsum; if (type == ReductionEnum::MEAN) return mean; accum = 0.0; double weightsum2 = 0.0;//for weighted sample stdev for (int i = 0; i < numElems; ++i) { float tempf = data[i] - mean; accum += weights[i] * tempf * tempf; weightsum2 += weights[i] * weights[i]; } switch (type) { case ReductionEnum::STDEV: return sqrt(accum / weightsum); case ReductionEnum::VARIANCE: return accum / weightsum; case ReductionEnum::SAMPSTDEV: return sqrt(accum / (weightsum - weightsum2 / weightsum));//http://en.wikipedia.org/wiki/Weighted_arithmetic_mean#Weighted_sample_variance case ReductionEnum::TSNR: return mean / sqrt(accum / (weightsum - weightsum2 / weightsum)); case ReductionEnum::COV: return sqrt(accum / (weightsum - weightsum2 / weightsum)) / mean; default: CaretAssertMessage(0, "unhandled type in sum-based reduction"); return 0.0f; } } case ReductionEnum::MEDIAN: { vector toSort; toSort.reserve(numElems); for (int i = 0; i < numElems; ++i) { toSort.push_back(ValWeight(data[i], weights[i])); } stable_sort(toSort.begin(), toSort.end()); vector weightaccum(numElems); weightaccum[0] = toSort[0].weight; for (int i = 1; i < numElems; ++i) { weightaccum[i] = weightaccum[i - 1] + toSort[i].weight; } double target = weightaccum.back() / 2; int64_t index = (int64_t)(lower_bound(weightaccum.begin(), weightaccum.end(), target) - weightaccum.begin()); if (index == numElems) --index;//deal with edge cases from things like negative weights if (numElems > 1 && index < (numElems - 1) && weightaccum[index] == target)//only average on exact equals, according to https://en.wikipedia.org/wiki/Weighted_median {//could instead always interpolate return (toSort[index].value + toSort[index + 1].value) / 2; } else { return toSort[index].value; } } case ReductionEnum::MODE: { vector toSort; toSort.reserve(numElems); for (int i = 0; i < numElems; ++i) { toSort.push_back(ValWeight(data[i], weights[i])); } stable_sort(toSort.begin(), toSort.end()); float bestweight = -numeric_limits::infinity(), curweight = toSort[0].weight; float bestval = toSort[0].value, curval = toSort[0].value; for (int i = 1; i < numElems; ++i) { if (toSort[i].value == curval) { curweight += toSort[i].weight; } else { if (curweight > bestweight) { bestval = curval; bestweight = curweight; } curval = toSort[i].value; curweight = toSort[i].weight; } } if (curweight > bestweight) { bestval = curval; bestweight = curweight; } return bestval; } } return 0.0f; } float ReductionOperation::reduceWeightedOnlyNumeric(const float* data, const float* weights, const int64_t& numElems, const ReductionEnum::Enum& type) { CaretAssert(numElems > 0); switch (type) { case ReductionEnum::INVALID: throw CaretException("weighted reduction requested with 'INVALID' method"); case ReductionEnum::INDEXMAX: case ReductionEnum::INDEXMIN: case ReductionEnum::MIN: case ReductionEnum::MAX: case ReductionEnum::PRODUCT: case ReductionEnum::COUNT_NONZERO: throw CaretException("weighted reduction not supported for '" + ReductionEnum::toName(type) + "' method"); default: break; } vector excluded, exweights; excluded.reserve(numElems); exweights.reserve(numElems); for (int64_t i = 0; i < numElems; ++i) { if (MathFunctions::isNumeric(data[i])) { excluded.push_back(data[i]); exweights.push_back(weights[i]); } } if (excluded.size() < 1) throw CaretException("all input values to reduceWeightedOnlyNumeric were non-numeric"); if (type == ReductionEnum::SAMPSTDEV && excluded.size() < 2) throw CaretException("SAMPSTDEV requested in reduceWeightedOnlyNumeric when only 1 element is numeric"); return reduceWeighted(excluded.data(), exweights.data(), excluded.size(), type); } float ReductionOperation::reduceWeightedExcludeDev(const float* data, const float* weights, const int64_t& numElems, const ReductionEnum::Enum& type, const float& numDevBelow, const float& numDevAbove) { CaretAssert(numElems > 0); switch (type) { case ReductionEnum::INVALID: throw CaretException("weighted reduction requested with 'INVALID' method"); case ReductionEnum::INDEXMAX: case ReductionEnum::INDEXMIN: case ReductionEnum::MIN: case ReductionEnum::MAX: case ReductionEnum::PRODUCT: case ReductionEnum::COUNT_NONZERO: throw CaretException("weighted reduction not supported for '" + ReductionEnum::toName(type) + "' method"); default: break; } double accum = 0.0, weightsum = 0.0f;//compute weighted stdev int64_t numValid = 0; for (int i = 0; i < numElems; ++i) { if (MathFunctions::isNumeric(data[i])) { accum += data[i] * weights[i]; weightsum += weights[i]; ++numValid; } } const float mean = accum / weightsum; accum = 0.0; for (int i = 0; i < numElems; ++i) { if (MathFunctions::isNumeric(data[i])) { float tempf = data[i] - mean; accum += weights[i] * tempf * tempf; } } float stdev = sqrt(accum / weightsum); float low = mean - stdev * numDevBelow, high = mean + stdev * numDevAbove; vector excluded, exweights; excluded.reserve(numValid); exweights.reserve(numValid); for (int64_t i = 0; i < numElems; ++i) { if (MathFunctions::isNumeric(data[i]) && data[i] >= low && data[i] <= high) { excluded.push_back(data[i]); exweights.push_back(weights[i]); } } if (excluded.size() < 1) throw CaretException("all input values to reduceWeightedExcludeDev were non-numeric"); if (type == ReductionEnum::SAMPSTDEV && excluded.size() < 2) throw CaretException("SAMPSTDEV requested in reduceWeightedExcludeDev when only 1 element is numeric"); return reduceWeighted(excluded.data(), exweights.data(), excluded.size(), type); } AString ReductionOperation::getHelpInfo() { AString ret; vector myEnums; ReductionEnum::getAllEnums(myEnums); int numEnums = (int)myEnums.size(); for (int i = 0; i < numEnums; ++i) { ret += ReductionEnum::toName(myEnums[i]) + ": " + ReductionEnum::toExplanation(myEnums[i]) + "\n"; } return ret; } connectome-workbench-1.2.3+git41-gc4c6c90/src/Common/ReductionOperation.h000066400000000000000000000041131300200146000257610ustar00rootroot00000000000000#ifndef __REDUCTION_OPERATION_H__ #define __REDUCTION_OPERATION_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 "AString.h" #include "ReductionEnum.h" namespace caret { class ReductionOperation { public: static float reduce(const float* data, const int64_t& numElems, const ReductionEnum::Enum& type); ///reduce, with exclusion based on number of standard deviations static float reduceExcludeDev(const float* data, const int64_t& numElems, const ReductionEnum::Enum& type, const float& numDevBelow, const float& numDevAbove); static float reduceOnlyNumeric(const float* data, const int64_t& numElems, const ReductionEnum::Enum& type); ///weighted versions, do not accept all reduction types static float reduceWeighted(const float* data, const float* weights, const int64_t& numElems, const ReductionEnum::Enum& type); static float reduceWeightedExcludeDev(const float* data, const float* weights, const int64_t& numElems, const ReductionEnum::Enum& type, const float& numDevBelow, const float& numDevAbove); static float reduceWeightedOnlyNumeric(const float* data, const float* weights, const int64_t& numElems, const ReductionEnum::Enum& type); static AString getHelpInfo(); }; } #endif //__REDUCTION_OPERATION_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Common/SpecFileDialogViewFilesTypeEnum.cxx000066400000000000000000000273411300200146000306460ustar00rootroot00000000000000 /*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 __SPEC_FILE_DIALOG_VIEW_FILES_TYPE_ENUM_DECLARE__ #include "SpecFileDialogViewFilesTypeEnum.h" #undef __SPEC_FILE_DIALOG_VIEW_FILES_TYPE_ENUM_DECLARE__ #include "CaretAssert.h" using namespace caret; /** * \class caret::SpecFileDialogViewFilesTypeEnum * \brief Enumerated type for view files selection in Spec File Dialog * * 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_specFileDialogViewFilesTypeEnumComboBox; * * Declare a slot that is called when user changes selection * private slots: * void specFileDialogViewFilesTypeEnumComboBoxItemActivated(); * * Implementation File (.cxx) * Include the header files * #include "EnumComboBoxTemplate.h" * #include "SpecFileDialogViewFilesTypeEnum.h" * * Instatiate: * m_specFileDialogViewFilesTypeEnumComboBox = new EnumComboBoxTemplate(this); * m_specFileDialogViewFilesTypeEnumComboBox->setup(); * * Get notified when the user changes the selection: * QObject::connect(m_specFileDialogViewFilesTypeEnumComboBox, SIGNAL(itemActivated()), * this, SLOT(specFileDialogViewFilesTypeEnumComboBoxItemActivated())); * * Update the selection: * m_specFileDialogViewFilesTypeEnumComboBox->setSelectedItem(NEW_VALUE); * * Read the selection: * const SpecFileDialogViewFilesTypeEnum::Enum VARIABLE = m_specFileDialogViewFilesTypeEnumComboBox->getSelectedItem(); * */ /** * Constructor. * * @param enumValue * An enumerated value. * @param name * Name of enumerated value. * * @param guiName * User-friendly name for use in user-interface. */ SpecFileDialogViewFilesTypeEnum::SpecFileDialogViewFilesTypeEnum(const Enum enumValue, const AString& name, const AString& guiName) { this->enumValue = enumValue; this->integerCode = integerCodeCounter++; this->name = name; this->guiName = guiName; } /** * Destructor. */ SpecFileDialogViewFilesTypeEnum::~SpecFileDialogViewFilesTypeEnum() { } /** * Initialize the enumerated metadata. */ void SpecFileDialogViewFilesTypeEnum::initialize() { if (initializedFlag) { return; } initializedFlag = true; enumData.push_back(SpecFileDialogViewFilesTypeEnum(VIEW_FILES_ALL, "VIEW_FILES_ALL", "All")); enumData.push_back(SpecFileDialogViewFilesTypeEnum(VIEW_FILES_LOADED, "VIEW_FILES_LOADED", "Loaded")); enumData.push_back(SpecFileDialogViewFilesTypeEnum(VIEW_FILES_LOADED_MODIFIED, "VIEW_FILES_LOADED_MODIFIED", "Loaded:Modified")); enumData.push_back(SpecFileDialogViewFilesTypeEnum(VIEW_FILES_LOADED_NOT_MODIFIED, "VIEW_FILES_LOADED_NOT_MODIFIED", "Loaded:Not Modified")); enumData.push_back(SpecFileDialogViewFilesTypeEnum(VIEW_FILES_NOT_LOADED, "VIEW_FILES_NOT_LOADED", "Not Loaded")); } /** * 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 SpecFileDialogViewFilesTypeEnum* SpecFileDialogViewFilesTypeEnum::findData(const Enum enumValue) { if (initializedFlag == false) initialize(); size_t num = enumData.size(); for (size_t i = 0; i < num; i++) { const SpecFileDialogViewFilesTypeEnum* 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 SpecFileDialogViewFilesTypeEnum::toName(Enum enumValue) { if (initializedFlag == false) initialize(); const SpecFileDialogViewFilesTypeEnum* 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. */ SpecFileDialogViewFilesTypeEnum::Enum SpecFileDialogViewFilesTypeEnum::fromName(const AString& name, bool* isValidOut) { if (initializedFlag == false) initialize(); bool validFlag = false; Enum enumValue = SpecFileDialogViewFilesTypeEnum::enumData[0].enumValue; for (std::vector::iterator iter = enumData.begin(); iter != enumData.end(); iter++) { const SpecFileDialogViewFilesTypeEnum& 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 SpecFileDialogViewFilesTypeEnum")); } return enumValue; } /** * Get a GUI string representation of the enumerated type. * @param enumValue * Enumerated value. * @return * String representing enumerated value. */ AString SpecFileDialogViewFilesTypeEnum::toGuiName(Enum enumValue) { if (initializedFlag == false) initialize(); const SpecFileDialogViewFilesTypeEnum* 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. */ SpecFileDialogViewFilesTypeEnum::Enum SpecFileDialogViewFilesTypeEnum::fromGuiName(const AString& guiName, bool* isValidOut) { if (initializedFlag == false) initialize(); bool validFlag = false; Enum enumValue = SpecFileDialogViewFilesTypeEnum::enumData[0].enumValue; for (std::vector::iterator iter = enumData.begin(); iter != enumData.end(); iter++) { const SpecFileDialogViewFilesTypeEnum& 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 SpecFileDialogViewFilesTypeEnum")); } return enumValue; } /** * Get the integer code for a data type. * * @return * Integer code for data type. */ int32_t SpecFileDialogViewFilesTypeEnum::toIntegerCode(Enum enumValue) { if (initializedFlag == false) initialize(); const SpecFileDialogViewFilesTypeEnum* 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. */ SpecFileDialogViewFilesTypeEnum::Enum SpecFileDialogViewFilesTypeEnum::fromIntegerCode(const int32_t integerCode, bool* isValidOut) { if (initializedFlag == false) initialize(); bool validFlag = false; Enum enumValue = SpecFileDialogViewFilesTypeEnum::enumData[0].enumValue; for (std::vector::iterator iter = enumData.begin(); iter != enumData.end(); iter++) { const SpecFileDialogViewFilesTypeEnum& 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 SpecFileDialogViewFilesTypeEnum")); } 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 SpecFileDialogViewFilesTypeEnum::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 SpecFileDialogViewFilesTypeEnum::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(SpecFileDialogViewFilesTypeEnum::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 SpecFileDialogViewFilesTypeEnum::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(SpecFileDialogViewFilesTypeEnum::toGuiName(iter->enumValue)); } if (isSorted) { std::sort(allGuiNames.begin(), allGuiNames.end()); } } connectome-workbench-1.2.3+git41-gc4c6c90/src/Common/SpecFileDialogViewFilesTypeEnum.h000066400000000000000000000065721300200146000302760ustar00rootroot00000000000000#ifndef __SPEC_FILE_DIALOG_VIEW_FILES_TYPE_ENUM_H__ #define __SPEC_FILE_DIALOG_VIEW_FILES_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 SpecFileDialogViewFilesTypeEnum { public: /** * Enumerated values. */ enum Enum { /** */ VIEW_FILES_ALL, /** */ VIEW_FILES_LOADED, /** */ VIEW_FILES_LOADED_MODIFIED, /** */ VIEW_FILES_LOADED_NOT_MODIFIED, /** */ VIEW_FILES_NOT_LOADED }; ~SpecFileDialogViewFilesTypeEnum(); 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: SpecFileDialogViewFilesTypeEnum(const Enum enumValue, const AString& name, const AString& guiName); static const SpecFileDialogViewFilesTypeEnum* 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 __SPEC_FILE_DIALOG_VIEW_FILES_TYPE_ENUM_DECLARE__ std::vector SpecFileDialogViewFilesTypeEnum::enumData; bool SpecFileDialogViewFilesTypeEnum::initializedFlag = false; int32_t SpecFileDialogViewFilesTypeEnum::integerCodeCounter = 0; #endif // __SPEC_FILE_DIALOG_VIEW_FILES_TYPE_ENUM_DECLARE__ } // namespace #endif //__SPEC_FILE_DIALOG_VIEW_FILES_TYPE_ENUM_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Common/SpeciesEnum.cxx000066400000000000000000000254411300200146000247460ustar00rootroot00000000000000 /*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 __SPECIES_ENUM_DECLARE__ #include "SpeciesEnum.h" #undef __SPECIES_ENUM_DECLARE__ #include "CaretAssert.h" using namespace caret; /** * Constructor. * * @param enumValue * An enumerated value. * @param integerCode * Integer code for this enumerated value. * * @param name * Name of enumerated value. * * @param guiName * User-friendly name for use in user-interface. */ SpeciesEnum::SpeciesEnum(const Enum enumValue, const int32_t integerCode, const AString& name, const AString& guiName) { this->enumValue = enumValue; this->integerCode = integerCode; this->name = name; this->guiName = guiName; } /** * Destructor. */ SpeciesEnum::~SpeciesEnum() { } /** * Initialize the enumerated metadata. */ void SpeciesEnum::initialize() { if (initializedFlag) { return; } initializedFlag = true; enumData.push_back(SpeciesEnum(TYPE_UNKNOWN, 0, "TYPE_UNKNOWN", "Unknown")); enumData.push_back(SpeciesEnum(TYPE_BABOON, 1, "TYPE_BABOON", "Baboon")); enumData.push_back(SpeciesEnum(TYPE_CHIMPANZEE, 2, "TYPE_CHIMPANZEE", "Chimpanzee")); enumData.push_back(SpeciesEnum(TYPE_FERRET, 3, "TYPE_FERRET", "Ferret")); enumData.push_back(SpeciesEnum(TYPE_GALAGO, 4, "TYPE_GALAGO", "Galago")); enumData.push_back(SpeciesEnum(TYPE_GIBBON, 5, "TYPE_GIBBON", "Gibbon")); enumData.push_back(SpeciesEnum(TYPE_GORILLA, 6, "TYPE_GORILLA", "Gorilla")); enumData.push_back(SpeciesEnum(TYPE_HUMAN, 7, "TYPE_HUMAN", "Human")); enumData.push_back(SpeciesEnum(TYPE_MACAQUE, 8, "TYPE_MACAQUE", "Macaque")); enumData.push_back(SpeciesEnum(TYPE_MOUSE, 9, "TYPE_MOUSE", "Mouse")); enumData.push_back(SpeciesEnum(TYPE_ORANGUTAN, 10, "TYPE_ORANGUTAN", "Orangutan")); enumData.push_back(SpeciesEnum(TYPE_RAT, 11, "TYPE_RAT", "Rat")); enumData.push_back(SpeciesEnum(TYPE_OTHER, 12, "TYPE_OTHER", "Other not specified")); } /** * 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 SpeciesEnum* SpeciesEnum::findData(const Enum enumValue) { if (initializedFlag == false) initialize(); size_t num = enumData.size(); for (size_t i = 0; i < num; i++) { const SpeciesEnum* 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 SpeciesEnum::toName(Enum enumValue) { if (initializedFlag == false) initialize(); const SpeciesEnum* 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. */ SpeciesEnum::Enum SpeciesEnum::fromName(const AString& name, bool* isValidOut) { if (initializedFlag == false) initialize(); bool validFlag = false; Enum enumValue = TYPE_UNKNOWN; for (std::vector::iterator iter = enumData.begin(); iter != enumData.end(); iter++) { const SpeciesEnum& 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 SpeciesEnum")); } return enumValue; } /** * Get a GUI string representation of the enumerated type. * @param enumValue * Enumerated value. * @return * String representing enumerated value. */ AString SpeciesEnum::toGuiName(Enum enumValue) { if (initializedFlag == false) initialize(); const SpeciesEnum* 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. */ SpeciesEnum::Enum SpeciesEnum::fromGuiName(const AString& guiName, bool* isValidOut) { if (initializedFlag == false) initialize(); bool validFlag = false; Enum enumValue = TYPE_UNKNOWN; for (std::vector::iterator iter = enumData.begin(); iter != enumData.end(); iter++) { const SpeciesEnum& 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 SpeciesEnum")); } return enumValue; } /** * Get the integer code for a data type. * * @return * Integer code for data type. */ int32_t SpeciesEnum::toIntegerCode(Enum enumValue) { if (initializedFlag == false) initialize(); const SpeciesEnum* 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. */ SpeciesEnum::Enum SpeciesEnum::fromIntegerCode(const int32_t integerCode, bool* isValidOut) { if (initializedFlag == false) initialize(); bool validFlag = false; Enum enumValue = TYPE_UNKNOWN; for (std::vector::iterator iter = enumData.begin(); iter != enumData.end(); iter++) { const SpeciesEnum& 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 SpeciesEnum")); } 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 SpeciesEnum::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 SpeciesEnum::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(SpeciesEnum::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 SpeciesEnum::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(SpeciesEnum::toGuiName(iter->enumValue)); } if (isSorted) { std::sort(allGuiNames.begin(), allGuiNames.end()); } } connectome-workbench-1.2.3+git41-gc4c6c90/src/Common/SpeciesEnum.h000066400000000000000000000065611300200146000243750ustar00rootroot00000000000000#ifndef __SPECIES_ENUM__H_ #define __SPECIES_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 { /** * \brief Enumerated type for species. * * Species enumerated type. */ class SpeciesEnum { public: /** * Enumerated values. */ enum Enum { /// unknown TYPE_UNKNOWN, /// baboon TYPE_BABOON, /// chimpanzee TYPE_CHIMPANZEE, /// ferret TYPE_FERRET, /// galago TYPE_GALAGO, /// gibbon TYPE_GIBBON, /// gorilla TYPE_GORILLA, /// human TYPE_HUMAN, /// macaque monkey TYPE_MACAQUE, /// mouse TYPE_MOUSE, /// orangutan TYPE_ORANGUTAN, /// rat TYPE_RAT, /// other TYPE_OTHER }; ~SpeciesEnum(); 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: SpeciesEnum(const Enum enumValue, const int32_t integerCode, const AString& name, const AString& guiName); static const SpeciesEnum* 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; /** 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 __SPECIES_ENUM_DECLARE__ std::vector SpeciesEnum::enumData; bool SpeciesEnum::initializedFlag = false; #endif // __SPECIES_ENUM_DECLARE__ } // namespace #endif //__SPECIES_ENUM__H_ connectome-workbench-1.2.3+git41-gc4c6c90/src/Common/StereotaxicSpaceEnum.cxx000066400000000000000000000555761300200146000266350ustar00rootroot00000000000000 /*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 __STEREOTAXIC_SPACE_ENUM_DECLARE__ #include "StereotaxicSpaceEnum.h" #undef __STEREOTAXIC_SPACE_ENUM_DECLARE__ #include "CaretAssert.h" #include "CaretLogger.h" using namespace caret; /** * Constructor. * * @param enumValue * An enumerated value. * @param name * Name of enumerated value. * * @param guiName * User-friendly name for use in user-interface. */ StereotaxicSpaceEnum::StereotaxicSpaceEnum(const Enum enumValue, const AString& name, const AString& guiName, const int32_t dimI, const int32_t dimJ, const int32_t dimK, const float voxelSizeX, const float voxelSizeY, const float voxelSizeZ, const float originX, const float originY, const float originZ) { this->enumValue = enumValue; this->name = name; this->guiName = guiName; this->volumeDimensions[0] = dimI; this->volumeDimensions[1] = dimJ; this->volumeDimensions[2] = dimK; this->volumeVoxelSizes[0] = voxelSizeX; this->volumeVoxelSizes[1] = voxelSizeY; this->volumeVoxelSizes[2] = voxelSizeZ; this->volumeOrigin[0] = originX; this->volumeOrigin[1] = originY; this->volumeOrigin[2] = originZ; } /** * Destructor. */ StereotaxicSpaceEnum::~StereotaxicSpaceEnum() { } /** * Get the dimensions for a stereotaxic space. * * @param enumValue * Input - Enumerated type for stereotaxic space. * @param dimensionsOut * Output - Dimensions for the stereotaxic space. */ void StereotaxicSpaceEnum::toDimensions(const Enum enumValue, int32_t dimensionsOut[3]) { const StereotaxicSpaceEnum* enumInstance = findData(enumValue); dimensionsOut[0] = enumInstance->volumeDimensions[0]; dimensionsOut[1] = enumInstance->volumeDimensions[1]; dimensionsOut[2] = enumInstance->volumeDimensions[2]; } /** * Get the voxel sizes for a stereotaxic space. * * @param enumValue * Input - Enumerated type for stereotaxic space. * @param voxelSizesOut * Output - Voxel sizes for the stereotaxic space. */ void StereotaxicSpaceEnum::toVoxelSizes(const Enum enumValue, float voxelSizesOut[3]) { const StereotaxicSpaceEnum* enumInstance = findData(enumValue); voxelSizesOut[0] = enumInstance->volumeVoxelSizes[0]; voxelSizesOut[1] = enumInstance->volumeVoxelSizes[1]; voxelSizesOut[2] = enumInstance->volumeVoxelSizes[2]; } /** * Get the origin for a stereotaxic space. * * @param enumValue * Input - Enumerated type for stereotaxic space. * @param originOut * Output - Origin for the stereotaxic space. */ void StereotaxicSpaceEnum::toOrigin(const Enum enumValue, float originOut[3]) { const StereotaxicSpaceEnum* enumInstance = findData(enumValue); originOut[0] = enumInstance->volumeOrigin[0]; originOut[1] = enumInstance->volumeOrigin[1]; originOut[2] = enumInstance->volumeOrigin[2]; } /** * Initialize the enumerated metadata. */ void StereotaxicSpaceEnum::initialize() { if (initializedFlag) { return; } initializedFlag = true; enumData.push_back(StereotaxicSpaceEnum(SPACE_UNKNOWN, "SPACE_UNKNOWN", "Unknown", 0, 0, 0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0)); enumData.push_back(StereotaxicSpaceEnum(SPACE_OTHER, "SPACE_OTHER", "Other not specified", 0, 0, 0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0)); enumData.push_back(StereotaxicSpaceEnum(SPACE_AFNI_TALAIRACH, "SPACE_AFNI_TALAIRACH", "AFNI", 161, 191, 151, 1.0, 1.0, 1.0, -80.0, -110.0, -65.0)); enumData.push_back(StereotaxicSpaceEnum(SPACE_FLIRT, "SPACE_FLIRT", "FLIRT", 182, 217, 182, 1.0, 1.0, 1.0, -90.0, -126.0, -72.0)); enumData.push_back(StereotaxicSpaceEnum(SPACE_FLIRT_222, "SPACE_FLIRT_222", "FLIRT-222", 91, 109, 91, 2.0, 2.0, 2.0, -90.0, -126.0, -72.0)); enumData.push_back(StereotaxicSpaceEnum(SPACE_MACAQUE_F6, "SPACE_MACAQUE_F6", "MACAQUE-F6", 143, 187, 118, 0.5, 0.5, 0.5, -35.75, -54.75, -30.25)); enumData.push_back(StereotaxicSpaceEnum(SPACE_MACAQUE_F99, "SPACE_MACAQUE_F99", "MACAQUE-F99", 143, 187, 118, 0.5, 0.5, 0.5, -35.75, -54.75, -30.25)); enumData.push_back(StereotaxicSpaceEnum(SPACE_MRITOTAL, "SPACE_MRITOTAL", "MRITOTAL", 182, 217, 182, 1.0, 1.0, 1.0, -90.0, -126.0, -72.0)); enumData.push_back(StereotaxicSpaceEnum(SPACE_SPM, "SPACE_SPM", "SPM", 182, 217, 182, 1.0, 1.0, 1.0, -90.0, -126.0, -72.0)); enumData.push_back(StereotaxicSpaceEnum(SPACE_SPM_95, "SPACE_SPM_95", "SPM95", 182, 217, 182, 1.0, 1.0, 1.0, -90.0, -126.0, -72.0)); enumData.push_back(StereotaxicSpaceEnum(SPACE_SPM_96, "SPACE_SPM_96", "SPM96", 182, 217, 182, 1.0, 1.0, 1.0, -90.0, -126.0, -72.0)); enumData.push_back(StereotaxicSpaceEnum(SPACE_SPM_99, "SPACE_SPM_99", "SPM99", 182, 217, 182, 1.0, 1.0, 1.0, -90.0, -126.0, -72.0)); enumData.push_back(StereotaxicSpaceEnum(SPACE_SPM_2, "SPACE_SPM_2", "SPM2", 182, 217, 182, 1.0, 1.0, 1.0, -90.0, -126.0, -72.0)); enumData.push_back(StereotaxicSpaceEnum(SPACE_SPM_5, "SPACE_SPM_5", "SPM5", 182, 217, 182, 1.0, 1.0, 1.0, -90.0, -126.0, -72.0)); enumData.push_back(StereotaxicSpaceEnum(SPACE_T88, "SPACE_T88", "T88", 161, 191, 151, 1.0, 1.0, 1.0, -80.0, -110.0, -65.0)); enumData.push_back(StereotaxicSpaceEnum(SPACE_WU_7112B, "SPACE_WU_7112B", "711-2B", 176, 208, 176, 1.0, 1.0, 1.0, -88.5, -123.5, -75.5)); enumData.push_back(StereotaxicSpaceEnum(SPACE_WU_7112B_111, "SPACE_WU_7112B_111", "711-2B-111", 176, 208, 176, 1.0, 1.0, 1.0, -88.5, -123.5, -75.5)); enumData.push_back(StereotaxicSpaceEnum(SPACE_WU_7112B_222, "SPACE_WU_7112B_222", "711-2B-222", 128, 128, 75, 2.0, 2.0, 2.0, -128, -128, -69)); enumData.push_back(StereotaxicSpaceEnum(SPACE_WU_7112B_333, "SPACE_WU_7112B_333", "711-2B-333", 48, 64, 48, 3.0, 3.0, 3.0, -72.0, -106.5, -61.5)); enumData.push_back(StereotaxicSpaceEnum(SPACE_WU_7112C, "SPACE_WU_7112C", "711-2C", 176, 208, 176, 1.0, 1.0, 1.0, -88.5, -123.5, -75.5)); enumData.push_back(StereotaxicSpaceEnum(SPACE_WU_7112C_111, "SPACE_WU_7112C_111", "711-2C-111", 176, 208, 176, 1.0, 1.0, 1.0, -88.5, -123.5, -75.5)); enumData.push_back(StereotaxicSpaceEnum(SPACE_WU_7112C_222, "SPACE_WU_7112C_222", "711-2C-222", 128, 128, 75, 2.0, 2.0, 2.0, -128, -128, -69)); enumData.push_back(StereotaxicSpaceEnum(SPACE_WU_7112C_333, "SPACE_WU_7112C_333", "711-2C-333", 48, 64, 48, 3.0, 3.0, 3.0, -72.0, -106.5, -61.5)); enumData.push_back(StereotaxicSpaceEnum(SPACE_WU_7112O, "SPACE_WU_7112O", "711-2O", 176, 208, 176, 1.0, 1.0, 1.0, -88.5, -123.5, -75.5)); enumData.push_back(StereotaxicSpaceEnum(SPACE_WU_7112O_111, "SPACE_WU_7112O_111", "711-2O-111", 176, 208, 176, 1.0, 1.0, 1.0, -88.5, -123.5, -75.5)); enumData.push_back(StereotaxicSpaceEnum(SPACE_WU_7112O_222, "SPACE_WU_7112O_222", "711-2O-222", 128, 128, 75, 2.0, 2.0, 2.0, -128, -128, -69)); enumData.push_back(StereotaxicSpaceEnum(SPACE_WU_7112O_333, "SPACE_WU_7112O_333", "711-2O-333", 48, 64, 48, 3.0, 3.0, 3.0, -72.0, -106.5, -61.5)); enumData.push_back(StereotaxicSpaceEnum(SPACE_WU_7112Y, "SPACE_WU_7112Y", "711-2Y", 176, 208, 176, 1.0, 1.0, 1.0, -88.5, -123.5, -75.5)); enumData.push_back(StereotaxicSpaceEnum(SPACE_WU_7112Y_111, "SPACE_WU_7112Y_111", "711-2Y-111", 176, 208, 176, 1.0, 1.0, 1.0, -88.5, -123.5, -75.5)); enumData.push_back(StereotaxicSpaceEnum(SPACE_WU_7112Y_222, "SPACE_WU_7112Y_222", "711-2Y-222", 128, 128, 75, 2.0, 2.0, 2.0, -128, -128, -69)); enumData.push_back(StereotaxicSpaceEnum(SPACE_WU_7112Y_333, "SPACE_WU_7112Y_333", "711-2Y-333", 48, 64, 48, 3.0, 3.0, 3.0, -72.0, -106.5, -61.5)); } /** * 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 StereotaxicSpaceEnum* StereotaxicSpaceEnum::findData(const Enum enumValue) { if (initializedFlag == false) initialize(); size_t num = enumData.size(); for (size_t i = 0; i < num; i++) { const StereotaxicSpaceEnum* 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 StereotaxicSpaceEnum::toName(Enum enumValue) { if (initializedFlag == false) initialize(); const StereotaxicSpaceEnum* 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. */ StereotaxicSpaceEnum::Enum StereotaxicSpaceEnum::fromName(const AString& name, bool* isValidOut) { if (initializedFlag == false) initialize(); bool validFlag = false; Enum enumValue = SPACE_UNKNOWN; for (std::vector::iterator iter = enumData.begin(); iter != enumData.end(); iter++) { const StereotaxicSpaceEnum& 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 StereotaxicSpaceEnum")); } return enumValue; } /** * Get a GUI string representation of the enumerated type. * @param enumValue * Enumerated value. * @return * String representing enumerated value. */ AString StereotaxicSpaceEnum::toGuiName(Enum enumValue) { if (initializedFlag == false) initialize(); const StereotaxicSpaceEnum* 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. */ StereotaxicSpaceEnum::Enum StereotaxicSpaceEnum::fromGuiName(const AString& guiName, bool* isValidOut) { if (initializedFlag == false) initialize(); bool validFlag = false; Enum enumValue = SPACE_UNKNOWN; for (std::vector::iterator iter = enumData.begin(); iter != enumData.end(); iter++) { const StereotaxicSpaceEnum& 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 StereotaxicSpaceEnum")); } return enumValue; } /** * Get the integer code for a data type. * * @return * Integer code for data type. */ int32_t StereotaxicSpaceEnum::toIntegerCode(Enum enumValue) { if (initializedFlag == false) initialize(); const StereotaxicSpaceEnum* 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. */ StereotaxicSpaceEnum::Enum StereotaxicSpaceEnum::fromIntegerCode(const int32_t integerCode, bool* isValidOut) { if (initializedFlag == false) initialize(); bool validFlag = false; Enum enumValue = SPACE_UNKNOWN; for (std::vector::iterator iter = enumData.begin(); iter != enumData.end(); iter++) { const StereotaxicSpaceEnum& 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 StereotaxicSpaceEnum")); } 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 StereotaxicSpaceEnum::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 StereotaxicSpaceEnum::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(StereotaxicSpaceEnum::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 StereotaxicSpaceEnum::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(StereotaxicSpaceEnum::toGuiName(iter->enumValue)); } if (isSorted) { std::sort(allGuiNames.begin(), allGuiNames.end()); } } connectome-workbench-1.2.3+git41-gc4c6c90/src/Common/StereotaxicSpaceEnum.h000066400000000000000000000136531300200146000262500ustar00rootroot00000000000000#ifndef __STEREOTAXIC_SPACE_ENUM__H_ #define __STEREOTAXIC_SPACE_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 { /** * \brief * * */ class StereotaxicSpaceEnum { public: /** * Enumerated values. */ enum Enum { /// unknown space SPACE_UNKNOWN, /// other space SPACE_OTHER, /// AFNI Talairach space SPACE_AFNI_TALAIRACH, /// FLIRT space SPACE_FLIRT, /// FLIRT 222 space SPACE_FLIRT_222, /// macaque atlas SPACE_MACAQUE_F6, /// Macaque F99 SPACE_MACAQUE_F99, /// MRITOTAL space SPACE_MRITOTAL, /// SPM space SPACE_SPM, /// SPM 95 space SPACE_SPM_95, /// SPM 95 space SPACE_SPM_96, /// SPM 99 Template space SPACE_SPM_99, /// SPM 2 Template space SPACE_SPM_2, /// SPM 5 space SPACE_SPM_5, /// Talairach 88 space (same as AFNI) SPACE_T88, /// Washington University 711-2B space SPACE_WU_7112B, /// Washington University 711-2B 1mm voxelspace SPACE_WU_7112B_111, /// Washington University 711-2B 2mm voxelspace SPACE_WU_7112B_222, /// Washington University 711-2B 3mm voxelspace SPACE_WU_7112B_333, /// Washington University 711-2C space SPACE_WU_7112C, /// Washington University 711-2C 1mm voxelspace SPACE_WU_7112C_111, /// Washington University 711-2C 2mm voxelspace SPACE_WU_7112C_222, /// Washington University 711-2C 3mm voxelspace SPACE_WU_7112C_333, /// Washington University 711-2O space SPACE_WU_7112O, /// Washington University 711-2O 1mm voxelspace SPACE_WU_7112O_111, /// Washington University 711-2O 2mm voxelspace SPACE_WU_7112O_222, /// Washington University 711-2O 3mm voxelspace SPACE_WU_7112O_333, /// Washington University 711-2Y space SPACE_WU_7112Y, /// Washington University 711-2Y 1mm voxelspace SPACE_WU_7112Y_111, /// Washington University 711-2Y 2mm voxelspace SPACE_WU_7112Y_222, /// Washington University 711-2Y 3mm voxelspace SPACE_WU_7112Y_333 }; ~StereotaxicSpaceEnum(); 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 void toDimensions(const Enum enumValue, int32_t dimensionsOut[3]); static void toVoxelSizes(const Enum enumValue, float voxelSizesOut[3]); static void toOrigin(const Enum enumValue, float originOut[3]); private: StereotaxicSpaceEnum(const Enum enumValue, const AString& name, const AString& guiName, const int32_t dimI, const int32_t dimJ, const int32_t dimK, const float voxelSizeX, const float voxelSizeY, const float voxelSizeZ, const float originX, const float originY, const float originZ); static const StereotaxicSpaceEnum* 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; /** Volume Dimensions */ int32_t volumeDimensions[3]; /** Volume Origin */ float volumeOrigin[3]; /** Volume Voxel Sizes */ float volumeVoxelSizes[3]; }; #ifdef __STEREOTAXIC_SPACE_ENUM_DECLARE__ std::vector StereotaxicSpaceEnum::enumData; bool StereotaxicSpaceEnum::initializedFlag = false; int32_t StereotaxicSpaceEnum::integerCodeCounter = 0; #endif // __STEREOTAXIC_SPACE_ENUM_DECLARE__ } // namespace #endif //__STEREOTAXIC_SPACE_ENUM__H_ connectome-workbench-1.2.3+git41-gc4c6c90/src/Common/StringTableModel.cxx000066400000000000000000000157201300200146000257240ustar00rootroot00000000000000 /*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 __STRING_TABLE_MODEL_DECLARE__ #include "StringTableModel.h" #undef __STRING_TABLE_MODEL_DECLARE__ #include "CaretAssert.h" using namespace caret; /** * \class caret::StringTableModel * \brief Creates a two-dimension table of strings. * \ingroup Common */ /** * Constructor that creates a table of the specified dimensions * with all column alignment set to right. * * @param numberOfRows * Number of rows. * @param numColumns * Number of columns. * @param floatingPointPrecision * Precision for floating point data. */ StringTableModel::StringTableModel(const int32_t numberOfRows, const int32_t numberOfColumns, const int32_t floatingPointPrecision) : CaretObject(), m_numberOfRows(numberOfRows), m_numberOfColumns(numberOfColumns), m_floatingPointPrecsion(floatingPointPrecision) { CaretAssert(m_numberOfRows > 0); CaretAssert(m_numberOfColumns > 0); m_stringTable.resize(m_numberOfRows * m_numberOfColumns); m_columnAlignment.resize(m_numberOfColumns, ALIGN_RIGHT); } /** * Destructor. */ StringTableModel::~StringTableModel() { } /** * Set column alignment. * * @param column * Column for which alignment is set. * @param alignment * New alignment value. */ void StringTableModel::setColumnAlignment(const int32_t column, const Alignment alignment) { CaretAssertVectorIndex(m_columnAlignment, column); m_columnAlignment[column] = alignment; } /** * Set an element in the table with a string. * * @param row * Row index. * @param column * Column. * @param value * String value for given row/column. */ void StringTableModel::setElement(const int32_t row, const int32_t column, const AString& value) { const int32_t offset = getOffset(row, column); CaretAssertVectorIndex(m_stringTable, offset); m_stringTable[offset] = value; } /** * Set an element in the table with a C-style string. * * @param row * Row index. * @param column * Column. * @param value * C-Style string value for given row/column. */ void StringTableModel::setElement(const int32_t row, const int32_t column, const char* value) { setElement(row, column, AString(value)); } /** * Set an element in the table with a boolean value. * * @param row * Row index. * @param column * Column. * @param value * Boolean value for given row/column. */ void StringTableModel::setElement(const int32_t row, const int32_t column, const bool value) { setElement(row, column, AString::fromBool(value)); } /** * Set an element in the table with a 32-bit integer. * * @param row * Row index. * @param column * Column. * @param value * Integer value for given row/column. */ void StringTableModel::setElement(const int32_t row, const int32_t column, const int32_t value) { setElement(row, column, AString::number(value)); } /** * Set an element in the table with a 64-bit integer. * * @param row * Row index. * @param column * Column. * @param value * 64-Bit integer value for given row/column. */ void StringTableModel::setElement(const int32_t row, const int32_t column, const int64_t value) { setElement(row, column, AString::number(value)); } /** * Set an element in the table with floating point value. * * @param row * Row index. * @param column * Column. * @param value * Floating point value for given row/column. */ void StringTableModel::setElement(const int32_t row, const int32_t column, const double value) { setElement(row, column, AString::number(value, 'f', m_floatingPointPrecsion)); } /** * Get the offset to an element in the table. * * @param row * Row index. * @param column * Column. * @return * Offset of element and given row and column. */ int32_t StringTableModel::getOffset(const int32_t row, const int32_t column) const { CaretAssertArrayIndex(m_stringTable, m_numberOfRows, row); CaretAssertArrayIndex(m_stringTable, m_numberOfColumns, column); const int32_t offset = ((row * m_numberOfColumns) + column); return offset; } /** * @return The table formatted into a text string. */ AString StringTableModel::getInString() const { std::vector columnWidths(m_numberOfColumns, 0); for (int32_t j = 0; j < m_numberOfColumns; j++) { int32_t width = 0; for (int32_t i = 0; i < m_numberOfRows; i++) { width = std::max(m_stringTable[getOffset(i, j)].length(), width); } // if (width > 0) { // width += 3; // } columnWidths[j] = width; } AString textOut; for (int32_t i = 0; i < m_numberOfRows; i++) { for (int32_t j = 0; j < m_numberOfColumns; j++) { const int32_t offset = getOffset(i, j); CaretAssertVectorIndex(m_stringTable, offset); AString txt = m_stringTable[offset]; CaretAssertVectorIndex(m_columnAlignment, j); switch (m_columnAlignment[j]) { case ALIGN_LEFT: textOut.append(txt.leftJustified(columnWidths[j])); break; case ALIGN_RIGHT: textOut.append(txt.rightJustified(columnWidths[j])); break; } textOut.append(" "); } textOut.append("\n"); } return textOut; } connectome-workbench-1.2.3+git41-gc4c6c90/src/Common/StringTableModel.h000066400000000000000000000060371300200146000253520ustar00rootroot00000000000000#ifndef __STRING_TABLE_MODEL_H__ #define __STRING_TABLE_MODEL_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" namespace caret { class StringTableModel : public CaretObject { public: enum Alignment { ALIGN_LEFT, ALIGN_RIGHT }; StringTableModel(const int32_t numberOfRows, const int32_t numberOfColumns, const int32_t floatingPointPrecision = 3); virtual ~StringTableModel(); void setElement(const int32_t row, const int32_t column, const AString& value); void setElement(const int32_t row, const int32_t column, const char* value); void setElement(const int32_t row, const int32_t column, const int32_t value); void setElement(const int32_t row, const int32_t column, const int64_t value); void setElement(const int32_t row, const int32_t column, const double value); void setElement(const int32_t row, const int32_t column, const bool value); void setColumnAlignment(const int32_t column, const Alignment alignment); AString getInString() const; private: StringTableModel(const StringTableModel&); StringTableModel& operator=(const StringTableModel&); int32_t getOffset(const int32_t row, const int32_t column) const; const int32_t m_numberOfRows; const int32_t m_numberOfColumns; const int32_t m_floatingPointPrecsion; std::vector m_stringTable; std::vector m_columnAlignment; // ADD_NEW_MEMBERS_HERE }; #ifdef __STRING_TABLE_MODEL_DECLARE__ // #endif // __STRING_TABLE_MODEL_DECLARE__ } // namespace #endif //__STRING_TABLE_MODEL_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Common/StructureEnum.cxx000066400000000000000000000617431300200146000253600ustar00rootroot00000000000000 /*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 __STRUCTURE_ENUM_DECLARE__ #include "StructureEnum.h" #undef __STRUCTURE_ENUM_DECLARE__ #include "CaretAssert.h" using namespace caret; /** * Constructor. * * @param enumValue * An enumerated value. * @param integerCode * Integer code for this enumerated value. * * @param name * Name of enumerated value. * * @param guiName * User-friendly name for use in user-interface. */ StructureEnum::StructureEnum(const Enum enumValue, const AString& name, const AString& guiName) { this->enumValue = enumValue; this->integerCode = StructureEnum::integerCodeGenerator++; this->name = name; this->guiName = guiName; } /** * Destructor. */ StructureEnum::~StructureEnum() { } /** * Initialize the enumerated metadata. */ void StructureEnum::initialize() { if (initializedFlag) { return; } initializedFlag = true; enumData.push_back(StructureEnum(CORTEX_LEFT, "CORTEX_LEFT", "CortexLeft")); enumData.push_back(StructureEnum(CORTEX_RIGHT, "CORTEX_RIGHT", "CortexRight")); enumData.push_back(StructureEnum(CEREBELLUM, "CEREBELLUM", "Cerebellum")); enumData.push_back(StructureEnum(ACCUMBENS_LEFT, "ACCUMBENS_LEFT", "AccumbensLeft")); enumData.push_back(StructureEnum(ACCUMBENS_RIGHT, "ACCUMBENS_RIGHT", "AccumbensRight")); enumData.push_back(StructureEnum(ALL, "ALL", "All")); enumData.push_back(StructureEnum(ALL_GREY_MATTER, "ALL_GREY_MATTER", "AllGreyMatter")); enumData.push_back(StructureEnum(ALL_WHITE_MATTER, "ALL_WHITE_MATTER", "AllWhiteMatter")); enumData.push_back(StructureEnum(AMYGDALA_LEFT, "AMYGDALA_LEFT", "AmygdalaLeft")); enumData.push_back(StructureEnum(AMYGDALA_RIGHT, "AMYGDALA_RIGHT", "AmygdalaRight")); enumData.push_back(StructureEnum(BRAIN_STEM, "BRAIN_STEM", "BrainStem")); enumData.push_back(StructureEnum(CAUDATE_LEFT, "CAUDATE_LEFT", "CaudateLeft")); enumData.push_back(StructureEnum(CAUDATE_RIGHT, "CAUDATE_RIGHT", "CaudateRight")); enumData.push_back(StructureEnum(CEREBELLAR_WHITE_MATTER_LEFT, "CEREBELLAR_WHITE_MATTER_LEFT", "CerebellarWhiteMatterLeft")); enumData.push_back(StructureEnum(CEREBELLAR_WHITE_MATTER_RIGHT, "CEREBELLAR_WHITE_MATTER_RIGHT", "CerebellarWhiteMatterRight")); enumData.push_back(StructureEnum(CEREBELLUM_LEFT, "CEREBELLUM_LEFT", "CerebellumLeft")); enumData.push_back(StructureEnum(CEREBELLUM_RIGHT, "CEREBELLUM_RIGHT", "CerebellumRight")); enumData.push_back(StructureEnum(CEREBRAL_WHITE_MATTER_LEFT, "CEREBRAL_WHITE_MATTER_LEFT", "CerebralWhiteMatterLeft")); enumData.push_back(StructureEnum(CEREBRAL_WHITE_MATTER_RIGHT, "CEREBRAL_WHITE_MATTER_RIGHT", "CerebralWhiteMatterRight")); enumData.push_back(StructureEnum(CORTEX, "CORTEX", "Cortex")); enumData.push_back(StructureEnum(DIENCEPHALON_VENTRAL_LEFT, "DIENCEPHALON_VENTRAL_LEFT", "DiencephalonVentralLeft")); enumData.push_back(StructureEnum(DIENCEPHALON_VENTRAL_RIGHT, "DIENCEPHALON_VENTRAL_RIGHT", "DiencephalonVentralRight")); enumData.push_back(StructureEnum(HIPPOCAMPUS_LEFT, "HIPPOCAMPUS_LEFT", "HippocampusLeft")); enumData.push_back(StructureEnum(HIPPOCAMPUS_RIGHT, "HIPPOCAMPUS_RIGHT", "HippocampusRight")); enumData.push_back(StructureEnum(INVALID, "INVALID", "Invalid")); enumData.push_back(StructureEnum(OTHER, "OTHER", "Other")); enumData.push_back(StructureEnum(OTHER_GREY_MATTER, "OTHER_GREY_MATTER", "OtherGreyMatter")); enumData.push_back(StructureEnum(OTHER_WHITE_MATTER, "OTHER_WHITE_MATTER", "OtherWhiteMatter")); enumData.push_back(StructureEnum(PALLIDUM_LEFT, "PALLIDUM_LEFT", "PallidumLeft")); enumData.push_back(StructureEnum(PALLIDUM_RIGHT, "PALLIDUM_RIGHT", "PallidumRight")); enumData.push_back(StructureEnum(PUTAMEN_LEFT, "PUTAMEN_LEFT", "PutamenLeft")); enumData.push_back(StructureEnum(PUTAMEN_RIGHT, "PUTAMEN_RIGHT", "PutamenRight")); // enumData.push_back(StructureEnum(SUBCORTICAL_WHITE_MATTER_LEFT, // "SUBCORTICAL_WHITE_MATTER_LEFT", // "SubcorticalWhiteMatterLeft")); // // enumData.push_back(StructureEnum(SUBCORTICAL_WHITE_MATTER_RIGHT, // "SUBCORTICAL_WHITE_MATTER_RIGHT", // "SubcorticalWhiteMatterRight")); enumData.push_back(StructureEnum(THALAMUS_LEFT, "THALAMUS_LEFT", "ThalamusLeft")); enumData.push_back(StructureEnum(THALAMUS_RIGHT, "THALAMUS_RIGHT", "ThalamusRight")); } /** * 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 StructureEnum* StructureEnum::findData(const Enum enumValue) { if (initializedFlag == false) initialize(); size_t num = enumData.size(); for (size_t i = 0; i < num; i++) { const StructureEnum* 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 StructureEnum::toName(Enum enumValue) { if (initializedFlag == false) initialize(); const StructureEnum* 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. */ StructureEnum::Enum StructureEnum::fromName(const AString& name, bool* isValidOut) { if (initializedFlag == false) initialize(); bool validFlag = false; Enum enumValue = INVALID; for (std::vector::iterator iter = enumData.begin(); iter != enumData.end(); iter++) { const StructureEnum& 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 StructureEnum")); } return enumValue; } /** * Get a GUI string representation of the enumerated type. * @param enumValue * Enumerated value. * @return * String representing enumerated value. */ AString StructureEnum::toGuiName(Enum enumValue) { if (initializedFlag == false) initialize(); const StructureEnum* 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. */ StructureEnum::Enum StructureEnum::fromGuiName(const AString& guiName, bool* isValidOut) { if (initializedFlag == false) initialize(); bool validFlag = false; Enum enumValue = INVALID; for (std::vector::iterator iter = enumData.begin(); iter != enumData.end(); iter++) { const StructureEnum& 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 StructureEnum")); } return enumValue; } /** * Get a GUI string representation of the enumerated type. * @param enumValue * Enumerated value. * @return * String representing enumerated value. */ AString StructureEnum::toCiftiName(Enum enumValue) { if (initializedFlag == false) initialize(); const StructureEnum* enumInstance = findData(enumValue); return "CIFTI_STRUCTURE_" + enumInstance->name; } /** * 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. */ StructureEnum::Enum StructureEnum::fromCiftiName(const AString& ciftiName, bool* isValidOut) { if (initializedFlag == false) initialize(); bool validFlag = false; Enum enumValue = INVALID; if (ciftiName.startsWith("CIFTI_STRUCTURE_")) { QString toMatch = ciftiName.mid(16); for (std::vector::iterator iter = enumData.begin(); iter != enumData.end(); iter++) { const StructureEnum& d = *iter; if (toMatch == d.name) { enumValue = d.enumValue; validFlag = true; break; } } } if (isValidOut != 0) { *isValidOut = validFlag; } else if (validFlag == false) { CaretAssertMessage(0, AString("guiName " + ciftiName + "failed to match enumerated value for type StructureEnum")); } return enumValue; } /** * Get the integer code for a data type. * * @return * Integer code for data type. */ int32_t StructureEnum::toIntegerCode(Enum enumValue) { if (initializedFlag == false) initialize(); const StructureEnum* 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. */ StructureEnum::Enum StructureEnum::fromIntegerCode(const int32_t integerCode, bool* isValidOut) { if (initializedFlag == false) initialize(); bool validFlag = false; Enum enumValue = INVALID; for (std::vector::iterator iter = enumData.begin(); iter != enumData.end(); iter++) { const StructureEnum& 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 StructureEnum")); } 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 * except ALL. */ void StructureEnum::getAllEnums(std::vector& allEnums) { if (initializedFlag == false) initialize(); allEnums.clear(); for (std::vector::iterator iter = enumData.begin(); iter != enumData.end(); iter++) { StructureEnum::Enum value =iter->enumValue; if (value == ALL) { // nothing } else { allEnums.push_back(iter->enumValue); } } } /** * Is this 'right' structure? * @param enumValue * The enumerated type. * @return * true if the enumerated value represents a 'right' structure, else false. */ bool StructureEnum::isRight(const Enum enumValue) { switch (enumValue) { case ACCUMBENS_RIGHT: case AMYGDALA_RIGHT: case CAUDATE_RIGHT: case CEREBELLAR_WHITE_MATTER_RIGHT: case CEREBELLUM_RIGHT: case CEREBRAL_WHITE_MATTER_RIGHT: case CORTEX_RIGHT: case DIENCEPHALON_VENTRAL_RIGHT: case HIPPOCAMPUS_RIGHT: case PALLIDUM_RIGHT: case PUTAMEN_RIGHT: case THALAMUS_RIGHT: return true; case ALL://avoid default so smart compilers can warn when a new structure isn't added here case ALL_GREY_MATTER: case ALL_WHITE_MATTER: case BRAIN_STEM: case CEREBELLUM: case CORTEX: case INVALID: case OTHER: case OTHER_GREY_MATTER: case OTHER_WHITE_MATTER: return false;//visually separate none/both from opposite cases case ACCUMBENS_LEFT: case AMYGDALA_LEFT: case CAUDATE_LEFT: case CEREBELLAR_WHITE_MATTER_LEFT: case CEREBELLUM_LEFT: case CEREBRAL_WHITE_MATTER_LEFT: case CORTEX_LEFT: case DIENCEPHALON_VENTRAL_LEFT: case HIPPOCAMPUS_LEFT: case PALLIDUM_LEFT: case PUTAMEN_LEFT: case THALAMUS_LEFT: return false; } CaretAssert(false); return false; } /** * Is this 'left' structure? * @param enumValue * The enumerated type. * @return * true if the enumerated value represents a 'left' structure, else false. */ bool StructureEnum::isLeft(const Enum enumValue) { switch (enumValue) { case ACCUMBENS_LEFT: case AMYGDALA_LEFT: case CAUDATE_LEFT: case CEREBELLAR_WHITE_MATTER_LEFT: case CEREBELLUM_LEFT: case CEREBRAL_WHITE_MATTER_LEFT: case CORTEX_LEFT: case DIENCEPHALON_VENTRAL_LEFT: case HIPPOCAMPUS_LEFT: case PALLIDUM_LEFT: case PUTAMEN_LEFT: case THALAMUS_LEFT: return true; case ALL://avoid default so smart compilers can warn when a new structure isn't added here case ALL_GREY_MATTER: case ALL_WHITE_MATTER: case BRAIN_STEM: case CEREBELLUM: case CORTEX: case INVALID: case OTHER: case OTHER_GREY_MATTER: case OTHER_WHITE_MATTER: return false;//visually separate none/both from opposite cases case ACCUMBENS_RIGHT: case AMYGDALA_RIGHT: case CAUDATE_RIGHT: case CEREBELLAR_WHITE_MATTER_RIGHT: case CEREBELLUM_RIGHT: case CEREBRAL_WHITE_MATTER_RIGHT: case CORTEX_RIGHT: case DIENCEPHALON_VENTRAL_RIGHT: case HIPPOCAMPUS_RIGHT: case PALLIDUM_RIGHT: case PUTAMEN_RIGHT: case THALAMUS_RIGHT: return false; } CaretAssert(false); return false; } /** * Is this a 'single' structure? The structure must be a valid type. * * @param enumValue * The enumerated type. * @return * true if the enumerated value represents a 'single' structure, else false. */ bool StructureEnum::isSingleStructure(const Enum enumValue) { bool singleStructureFlag = true; switch (enumValue) { case ACCUMBENS_LEFT: break; case ACCUMBENS_RIGHT: break; case ALL: singleStructureFlag = false; break; case ALL_GREY_MATTER: singleStructureFlag = false; break; case ALL_WHITE_MATTER: singleStructureFlag = false; break; case AMYGDALA_LEFT: break; case AMYGDALA_RIGHT: break; case BRAIN_STEM: break; case CAUDATE_LEFT: break; case CAUDATE_RIGHT: break; case CEREBELLAR_WHITE_MATTER_LEFT: break; case CEREBELLAR_WHITE_MATTER_RIGHT: break; case CEREBELLUM: break; case CEREBELLUM_LEFT: break; case CEREBELLUM_RIGHT: break; case CEREBRAL_WHITE_MATTER_LEFT: break; case CEREBRAL_WHITE_MATTER_RIGHT: break; case CORTEX: break; case CORTEX_LEFT: break; case CORTEX_RIGHT: break; case DIENCEPHALON_VENTRAL_LEFT: break; case DIENCEPHALON_VENTRAL_RIGHT: break; case HIPPOCAMPUS_LEFT: break; case HIPPOCAMPUS_RIGHT: break; case INVALID: singleStructureFlag = false; break; case PALLIDUM_LEFT: break; case PALLIDUM_RIGHT: break; case OTHER: singleStructureFlag = false; break; case OTHER_GREY_MATTER: singleStructureFlag = false; break; case OTHER_WHITE_MATTER: singleStructureFlag = false; break; case PUTAMEN_LEFT: break; case PUTAMEN_RIGHT: break; // case SUBCORTICAL_WHITE_MATTER_LEFT: // contralateralStructure = SUBCORTICAL_WHITE_MATTER_RIGHT; // break; // case SUBCORTICAL_WHITE_MATTER_RIGHT: // contralateralStructure = SUBCORTICAL_WHITE_MATTER_LEFT; // break; case THALAMUS_LEFT: break; case THALAMUS_RIGHT: break; } return singleStructureFlag; } /** * Are the two structure's cortices and contralateral (is one CortexLeft * and one CortexRight)? * * @param enumValueA * First structure enumerated type. * @param enumValueB * Second structure enumerated type. * @return * True if one is CORTEX_LEFT and one is CORTEX_LEFT. */ bool StructureEnum::isCortexContralateral(const Enum enumValueA, const Enum enumValueB) { if ((enumValueA == CORTEX_LEFT) && (enumValueB == CORTEX_RIGHT)) { return true; } if ((enumValueA == CORTEX_RIGHT) && (enumValueB == CORTEX_LEFT)) { return true; } return false; } /** * For the given structure return its contralateral structure. * Thats is, if this is a left/right structure return its * corresponding structure from the other side. * * @param enumValue * Structure for which contralateral structure is desired. * @return The contralateral structure or NULL if it does * not have a contralateral structure. */ StructureEnum::Enum StructureEnum::getContralateralStructure(const Enum enumValue) { StructureEnum::Enum contralateralStructure = INVALID; switch (enumValue) { case ACCUMBENS_LEFT: contralateralStructure = ACCUMBENS_RIGHT; break; case ACCUMBENS_RIGHT: contralateralStructure = ACCUMBENS_LEFT; break; case ALL: contralateralStructure = INVALID; break; case ALL_GREY_MATTER: break; case ALL_WHITE_MATTER: break; case AMYGDALA_LEFT: contralateralStructure = AMYGDALA_RIGHT; break; case AMYGDALA_RIGHT: contralateralStructure = AMYGDALA_LEFT; break; case BRAIN_STEM: contralateralStructure = INVALID; break; case CAUDATE_LEFT: contralateralStructure = CAUDATE_RIGHT; break; case CAUDATE_RIGHT: contralateralStructure = CAUDATE_LEFT; break; case CEREBELLAR_WHITE_MATTER_LEFT: contralateralStructure= CEREBELLAR_WHITE_MATTER_RIGHT; break; case CEREBELLAR_WHITE_MATTER_RIGHT: contralateralStructure = CEREBELLAR_WHITE_MATTER_LEFT; break; case CEREBELLUM: contralateralStructure = INVALID; break; case CEREBELLUM_LEFT: contralateralStructure = CEREBELLUM_RIGHT; break; case CEREBELLUM_RIGHT: contralateralStructure = CEREBELLUM_LEFT; break; case CEREBRAL_WHITE_MATTER_LEFT: contralateralStructure = CEREBELLAR_WHITE_MATTER_RIGHT; break; case CEREBRAL_WHITE_MATTER_RIGHT: contralateralStructure = CEREBELLAR_WHITE_MATTER_LEFT; break; case CORTEX: break; case CORTEX_LEFT: contralateralStructure = CORTEX_RIGHT; break; case CORTEX_RIGHT: contralateralStructure = CORTEX_LEFT; break; case DIENCEPHALON_VENTRAL_LEFT: contralateralStructure = DIENCEPHALON_VENTRAL_RIGHT; break; case DIENCEPHALON_VENTRAL_RIGHT: contralateralStructure = DIENCEPHALON_VENTRAL_LEFT; break; case HIPPOCAMPUS_LEFT: contralateralStructure = HIPPOCAMPUS_RIGHT; break; case HIPPOCAMPUS_RIGHT: contralateralStructure = HIPPOCAMPUS_LEFT; break; case INVALID: contralateralStructure = INVALID; break; case PALLIDUM_LEFT: contralateralStructure = PALLIDUM_RIGHT; break; case PALLIDUM_RIGHT: contralateralStructure = PALLIDUM_LEFT; break; case OTHER: contralateralStructure = INVALID; break; case OTHER_GREY_MATTER: break; case OTHER_WHITE_MATTER: break; case PUTAMEN_LEFT: contralateralStructure = PUTAMEN_RIGHT; break; case PUTAMEN_RIGHT: contralateralStructure = PUTAMEN_LEFT; break; // case SUBCORTICAL_WHITE_MATTER_LEFT: // contralateralStructure = SUBCORTICAL_WHITE_MATTER_RIGHT; // break; // case SUBCORTICAL_WHITE_MATTER_RIGHT: // contralateralStructure = SUBCORTICAL_WHITE_MATTER_LEFT; // break; case THALAMUS_LEFT: contralateralStructure = THALAMUS_RIGHT; break; case THALAMUS_RIGHT: contralateralStructure = THALAMUS_LEFT; break; } return contralateralStructure; } connectome-workbench-1.2.3+git41-gc4c6c90/src/Common/StructureEnum.h000066400000000000000000000126241300200146000247770ustar00rootroot00000000000000#ifndef __STRUCTURE_ENUM__H_ #define __STRUCTURE_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 { /** * \brief Enumerated type for a structure in a brain. * * Enumerated types for the individual structures in a brain. */ class StructureEnum { public: /** * Enumerated values. */ enum Enum { /** Invalid */ INVALID, /** All Strucures */ ALL, /** All white matter */ ALL_WHITE_MATTER, /** All grey matter */ ALL_GREY_MATTER, /** Left Nucleus Accumbens */ ACCUMBENS_LEFT, /** Right Nucleus Accumbens */ ACCUMBENS_RIGHT, /** Left Amygdala */ AMYGDALA_LEFT, /** Right Amygdala */ AMYGDALA_RIGHT, /** Brain Stem */ BRAIN_STEM, /** Left Caudate */ CAUDATE_LEFT, /** Right Caudate */ CAUDATE_RIGHT, /** Cerebellar white matter left */ CEREBELLAR_WHITE_MATTER_LEFT, /** Cerebellar white matter right */ CEREBELLAR_WHITE_MATTER_RIGHT, /** Cerebellum */ CEREBELLUM, /** Left Cerebellum */ CEREBELLUM_LEFT, /** Right Cerebellum */ CEREBELLUM_RIGHT, /** Cerebral white matter left */ CEREBRAL_WHITE_MATTER_LEFT, /** Cerebral white matter right */ CEREBRAL_WHITE_MATTER_RIGHT, /** Cortex not specified */ CORTEX, /** Left Cerebral Cortex */ CORTEX_LEFT, /** Right Cerebral Cortex*/ CORTEX_RIGHT, /** Left Ventral Diencephalon */ DIENCEPHALON_VENTRAL_LEFT, /** Right Ventral Diencephalon */ DIENCEPHALON_VENTRAL_RIGHT, /** Left Hippocampus */ HIPPOCAMPUS_LEFT, /** Right Hippocampus */ HIPPOCAMPUS_RIGHT, /** Left Pallidum */ PALLIDUM_LEFT, /** Right Pallidum */ PALLIDUM_RIGHT, /** Other structure not specified */ OTHER, /** Other grey matter */ OTHER_GREY_MATTER, /** Other white matter */ OTHER_WHITE_MATTER, /** Left Putamen */ PUTAMEN_LEFT, /** Right Putamen */ PUTAMEN_RIGHT, // /** Left Subcortical White Matter */ // SUBCORTICAL_WHITE_MATTER_LEFT, // /** Right Subcortical White Matter */ // SUBCORTICAL_WHITE_MATTER_RIGHT, /** Left Thalamus */ THALAMUS_LEFT, /** Right Thalamus */ THALAMUS_RIGHT }; ~StructureEnum(); 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 AString toCiftiName(Enum enumValue); static Enum fromCiftiName(const AString& ciftiName, bool* isValidOut); static int32_t toIntegerCode(Enum enumValue); static Enum fromIntegerCode(const int32_t integerCode, bool* isValidOut); static void getAllEnums(std::vector& allEnums); static bool isRight(const Enum enumValue); static bool isLeft(const Enum enumValue); static bool isSingleStructure(const Enum enumValue); static bool isCortexContralateral(const Enum enumValueA, const Enum enumValueB); static Enum getContralateralStructure(const Enum enumValue); private: StructureEnum(const Enum enumValue, const AString& name, const AString& guiName); static const StructureEnum* 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; static int32_t integerCodeGenerator; /** 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 __STRUCTURE_ENUM_DECLARE__ std::vector StructureEnum::enumData; bool StructureEnum::initializedFlag = false; int32_t StructureEnum::integerCodeGenerator = 0; #endif // __STRUCTURE_ENUM_DECLARE__ } // namespace #endif //__STRUCTURE_ENUM__H_ connectome-workbench-1.2.3+git41-gc4c6c90/src/Common/SystemUtilities.cxx000066400000000000000000000407031300200146000257040ustar00rootroot00000000000000/*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 "CaretOMP.h" #ifndef _WIN32 #include "execinfo.h" #else #include "Windows.h" #endif #include "CaretCommandLine.h" #include "CaretLogger.h" #include "SystemUtilities.h" using namespace caret; AString commandLine;//used to store the command line for output by unexpected handler /** * Constructor. */ SystemUtilities::SystemUtilities() { } /** * Destructor */ SystemUtilities::~SystemUtilities() { } #include /** * Get the backtrace in a string with each frame * separated by a newline character. * * @return * String containing the backtrace. */ AString SystemUtilities::getBackTrace() { /* #ifdef CARET_OS_WINDOWS return ""; #else // CARET_OS_WINDOWS std::stringstream str; void* callstack[1024]; int numFrames = backtrace(callstack, 1024); char** symbols = backtrace_symbols(callstack, numFrames); for (int i = 0; i < numFrames; i++) { str << symbols[i] << std::endl; } return AString::fromStdString(str.str()); #endif // CARET_OS_WINDOWS */ std::vector backTrace; SystemUtilities::getBackTrace(backTrace); std::stringstream str; for (std::vector::const_iterator iter = backTrace.begin(); iter != backTrace.end(); iter++) { str << *iter << std::endl; } return AString::fromStdString(str.str()); } /** * Get the backtrace with the frames in a vector of strings. * * @param backTraceOut * Vector of string containg the call stack. */ void SystemUtilities::getBackTrace(std::vector& backTraceOut) { backTraceOut.clear(); #ifdef CARET_OS_WINDOWS #else // CARET_OS_WINDOWS void* callstack[1024]; int numFrames = backtrace(callstack, 1024); char** symbols = backtrace_symbols(callstack, numFrames); for (int i = 0; i < numFrames; i++) { backTraceOut.push_back(symbols[i]); } free(symbols); #endif // CARET_OS_WINDOWS } void SystemUtilities::getBackTrace(SystemBacktrace& backTraceOut) { #ifdef CARET_OS_WINDOWS #else // CARET_OS_WINDOWS backTraceOut.m_numFrames = backtrace(backTraceOut.m_callstack, 1024); #endif // CARET_OS_WINDOWS } SystemBacktrace::SystemBacktrace() { #ifndef CARET_OS_WINDOWS m_numFrames = 0; #endif } /** * @return String containing the backtrace symbols. */ AString SystemBacktrace::toSymbolString() const { std::stringstream str; #ifdef CARET_OS_WINDOWS #else // CARET_OS_WINDOWS char** symbols = backtrace_symbols(m_callstack, m_numFrames); for (int i = 0; i < m_numFrames; ++i) { str << symbols[i] << std::endl; } free(symbols); #endif // CARET_OS_WINDOWS return AString::fromStdString(str.str()); } /** * Get the temporary directory. * * @return Path of temporary directory. * */ AString SystemUtilities::getTempDirectory() { return QDir::tempPath(); } /** * Get the user's name. * * @return Name of user. * */ AString SystemUtilities::getUserName() { #ifdef CARET_OS_WINDOWS const QString name(getenv("USERNAME")); #else // CARET_OS_WINDOWS QString name(getlogin()); if (name.isEmpty()) { name = getenv("USERNAME"); } #endif // CARET_OS_WINDOWS return name; } /** * @return The four digit year. */ AString SystemUtilities::getYear() { QDateTime dateTime = QDateTime::currentDateTime(); AString s = dateTime.toString("yyyy"); return s; } /** * Get the date as ISO format yyyy-mm-dd (2009-12-09) * * @return A string containing the date. * */ AString SystemUtilities::getDate() { QDateTime dateTime = QDateTime::currentDateTime(); AString s = dateTime.toString("yyyy:MM:dd"); return s; } /** * Get the time as ISO format hh:mm:ss (11:42:28) * * @return A string containing the time. * */ AString SystemUtilities::getTime() { QDateTime dateTime = QDateTime::currentDateTime(); AString s = dateTime.toString("hh:mm:ss"); return s; } /** * Get the date and time as month, day, year, hour:min:sec AM/PM * * @return Data and Time. * */ AString SystemUtilities::getDateAndTime() { AString s = (SystemUtilities::getDate() + "T" + SystemUtilities::getTime()); return s; } /** * Is the operating system the Microsoft Windows operating system? * * @return true if the operating system is Microsoft Windows. * */ bool SystemUtilities::isWindowsOperatingSystem() { #ifdef CARET_OS_WINDOWS return true; #endif return false; } /** * Is the operating system the Mac OSX operating system? * * @return true if the operating system is Mac OSX. * */ bool SystemUtilities::isMacOperatingSystem() { #ifdef CARET_OS_MACOSX return true; #endif return false; } /** * Get the number of processors in the computer. * * @return The number of processors. * */ int32_t SystemUtilities::getNumberOfProcessors() { #ifdef CARET_OMP return omp_get_num_procs(); #endif return 1; } /** * Unit testing of assertions. * * @param stream * Stream to which messages are written. * @param isVerbose * Print detailed messages. */ void SystemUtilities::unitTest(std::ostream& stream, const bool /*isVerbose*/) { #ifdef NDEBUG stream << "Unit testing of CaretAssertion will not take place since software is not compiled with debug on." << std::endl; return; #endif stream << "SystemUtilities::unitTest is starting" << std::endl; /* * Redirect std::err to the string stream. */ std::ostringstream str; std::streambuf* cerrSave = std::cerr.rdbuf(); std::cerr.rdbuf(str.rdbuf()); testRelativePath("/surface02/john/caret_data/HUMAN.COLIN.ATLAS", "/surface02/john/caret_data/HUMAN.COLIN.ATLAS/RIGHT_HEM", ".."); testRelativePath("/surface02/john/caret_data/HUMAN.COLIN.ATLAS/RIGHT_HEM", "/surface02/john/caret_data/HUMAN.COLIN.ATLAS", "RIGHT_HEM"); testRelativePath("/surface02/john/caret_data/HUMAN.COLIN.ATLAS/RIGHT_HEM/subdir", "/surface02/john/caret_data/HUMAN.COLIN.ATLAS/RIGHT_HEM", "subdir"); testRelativePath("/surface02/john/caret_data/HUMAN.COLIN.ATLAS/LEFT_HEM/subdir", "/surface02/john/caret_data/HUMAN.COLIN.ATLAS/RIGHT_HEM", "../LEFT_HEM/subdir"); testRelativePath("root:/var/etc", "remove:/usr/local", "root:/var/etc"); // testRelativePath("/Volumes/DS4600/caret7_gui_design/data/HCP_demo/Glasser_PilotIII.L.very_inflated.20k_fs_LR.surf.gii", // "/Volumes/DS4600/caret7_gui_design/data/HCP_demo/border.spec", // "border.spec"); /* * Restore std::err */ std::cerr.rdbuf(cerrSave); stream << "SystemUtilities::unitTest has ended" << std::endl << std::endl;; } /** * Test the relative path method. * @param otherPath - determine relative path to otherpath * @param myPath - from myPath * @param correctResult - The correct result * @return true if test passes. * */ bool SystemUtilities::testRelativePath( const AString& otherPath, const AString& myPath, const AString& correctResult) { bool correctFlag = false; AString result = relativePath(otherPath, myPath); if (result == correctResult) { correctFlag = true; } else { std::cerr << "SystemUtilities.relativePath() failed:" << std::endl; std::cerr << " otherPath: " + otherPath << std::endl; std::cerr << " myPath: " + myPath << std::endl; std::cerr << " result: " + result << std::endl; std::cerr << " correct: " + correctResult << std::endl; } return correctFlag; } /** * @return A Universally Unique Identifier (UUID). */ AString SystemUtilities::createUniqueID() { const AString uuid = QUuid::createUuid().toString(); return uuid; } /** * Given the directory "mypath", determine the relative path to "otherpath". * Both input paths must be absolute paths, otherwise, otherPathIn is * returned. * * Examples: * otherpath - "/surface02/john/caret_data/HUMAN.COLIN.ATLAS" * mypath - "/surface02/john/caret_data/HUMAN.COLIN.ATLAS/RIGHT_HEM" * result - ".."; * * otherpath - "/surface02/john/caret_data/HUMAN.COLIN.ATLAS/RIGHT_HEM/subdir" * mypath - "/surface02/john/caret_data/HUMAN.COLIN.ATLAS/RIGHT_HEM" * result - "subdir"; * * otherpath - "/surface02/john/caret_data/HUMAN.COLIN.ATLAS/LEFT_HEM/subdir" * mypath - "/surface02/john/caret_data/HUMAN.COLIN.ATLAS/RIGHT_HEM" * result - "../LEFT_HEM/subdir"; * * @param otherPathIn - The path for which relative path is sought. * @param myPathIn - Get the path from this * @return The relative path * */ AString SystemUtilities::relativePath( const AString& otherPathIn, const AString& myPathIn) { AString result = otherPathIn; // // Check for either path being empty // if (otherPathIn.isEmpty() || myPathIn.isEmpty()) { return result; } #ifdef CARET_OS_WINDOWS // // Both paths must be absolute paths // if (otherPathIn.indexOf(":") < 0) { return result; } if (myPathIn.indexOf(":") < 0) { return result; } #else // // Both paths must be absolute paths // if ((otherPathIn[0] != '/') || (myPathIn[0] != '/')) { return result; } #endif QStringList otherPath = QDir::cleanPath(otherPathIn).split(QRegExp("[/\\\\]"), QString::SkipEmptyParts); QStringList myPath = QDir::cleanPath(myPathIn).split(QRegExp("[/\\\\]"), QString::SkipEmptyParts); const unsigned int minLength = std::min(myPath.size(), otherPath.size()); int sameCount = 0; for (unsigned int i = 0; i < minLength; i++) { if (myPath[i] == otherPath[i]) { //cout << "Match: |" << myPath[i] << "|" << std::endl; sameCount++; } else { break; } } //cout << "same count: " << sameCount << std::endl; // // Is root of both paths different // if (sameCount == 0) { result = otherPathIn; } //const char separator[2] = { QDir::separator(), '\0' }; // // Is other path a subdirectory of mypath // if (sameCount == myPath.size()) { result = ""; for (int j = sameCount; j < otherPath.size(); j++) { result.append(otherPath[j]); if (j < (otherPath.size() - 1)) { result.append(QDir::separator()); } } } // // otherpath is above this one // result = ""; for (int j = sameCount; j < myPath.size(); j++) { result.append(".."); if (j < (myPath.size() - 1)) { result.append(QDir::separator()); } } for (int k = sameCount; k < otherPath.size(); k++) { if (result.isEmpty() == false) { result.append(QDir::separator()); } result.append(otherPath[k]); } return result; } /** * Unexpected handler */ static void unexpectedHandler() { std::cerr << "While running '" << caret_global_commandLine << "':" << std::endl; std::cerr << std::endl; std::cerr << "ERROR: unhandled exception." << std::endl; //if (theMainWindow != NULL) { const AString msg("Workbench will be terminating due to an unexpected exception.\n" "abort() will be called and a core file may be created."); std::cerr << msg << std::endl; //QMessageBox::critical(theMainWindow, "ERROR", msg); //} std::cerr << SystemUtilities::getBackTrace() << std::endl; abort(); } /** * New handler */ static void newHandler() { std::cerr << "While running '" << caret_global_commandLine << "':" << std::endl; std::ostringstream str; str << "\n" << "OUT OF MEMORY\n" << "\n" << "This means that Workbench is unable to get memory that it needs.\n" << "Possible causes:\n" << " (1) Your computer lacks sufficient RAM.\n" << " (2) Swap space is too small (you might increase it).\n" << " (3) Your computer may be using an non-English character \n" << " set. Try switching to the English character set.\n" << "\n"; std::cerr << str.str().c_str() << std::endl; std::cerr << SystemUtilities::getBackTrace() << std::endl; abort(); //if (theMainWindow != NULL) { // QMessageBox::critical(theMainWindow, "OUT OF MEMORY", // "Out of memory, Caret terminating"); // std::exit(-1); //} } /** * Set the handler for an unexpected (uncaught) exception. */ void SystemUtilities::setUnexpectedHandler() { std::set_unexpected(unexpectedHandler); } /** * Set the handler for when "operator new" is unable to allocate memory. * This new handler will print a message to the terminal containing a * backtrace and then calls abort to end the program. * * NOTE: If this new handler is set, "operator new" WILL NOT * throw a std::bad_alloc exception. */ void SystemUtilities::setNewHandler() { std::set_new_handler(newHandler); } /** * Return the current directory as indicated * by the system. In most cases, use the * methods in Brain to get and set the current * directory since it may be possible to have * multiple Brains each of which has its current * directory set to the directory containing the * SpecFile that was read. * * @return The path of the current directory. */ AString SystemUtilities::systemCurrentDirectory() { return QDir::currentPath(); } /* * From http://developer.qt.nokia.com/wiki/How_to_create_a_splash_screen_with_an_induced_delay */ class Sleeper : public QThread { public: static void sleepSeconds(const float seconds) { if (seconds > 0.0) { const unsigned long milliseconds = seconds * 1000.0; QThread::msleep(milliseconds); } } }; /** * Sleep for the specified number of seconds. The minimum * is one millisecond (0.001). * @param numberOfSeconds * Number of seconds to sleep. */ void SystemUtilities::sleepSeconds(const float numberOfSeconds) { Sleeper::sleepSeconds(numberOfSeconds); } /** * Get the workbench home directory * @return workbenchHomeDirectory * The path to the workbench installation */ AString SystemUtilities::getWorkbenchHome() { static QString workbenchHomeDirectory; if(workbenchHomeDirectory.isEmpty() == false) { return workbenchHomeDirectory; } QProcessEnvironment env = QProcessEnvironment::systemEnvironment(); workbenchHomeDirectory = env.value ( AString("WORKBENCH_HOME") ); if (workbenchHomeDirectory.isEmpty()) { workbenchHomeDirectory = QCoreApplication::applicationDirPath(); if (workbenchHomeDirectory.isEmpty() == false) { #ifdef CARET_OS_MACOSX const bool appFlag = (workbenchHomeDirectory.indexOf(".app/") > 0); if (appFlag) { QDir dir(workbenchHomeDirectory); dir.cdUp(); dir.cdUp(); dir.cdUp(); workbenchHomeDirectory = dir.absolutePath(); } #endif } CaretLogFine("Workbench Home Directory: " + workbenchHomeDirectory); } return workbenchHomeDirectory = QDir::toNativeSeparators(workbenchHomeDirectory); } connectome-workbench-1.2.3+git41-gc4c6c90/src/Common/SystemUtilities.h000066400000000000000000000050731300200146000253320ustar00rootroot00000000000000#ifndef __SYSTEMUTILITIES_H__ #define __SYSTEMUTILITIES_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 namespace caret { class SystemBacktrace { #ifdef CARET_OS_WINDOWS #else // CARET_OS_WINDOWS void* m_callstack[1024]; int m_numFrames; #endif // CARET_OS_WINDOWS public: SystemBacktrace(); AString toSymbolString() const; friend class SystemUtilities; }; /** * Methods to help out with files and directories. */ class SystemUtilities { private: SystemUtilities(); public: virtual ~SystemUtilities(); public: static AString getBackTrace(); static void getBackTrace(std::vector& backTraceOut); static void getBackTrace(SystemBacktrace& backTraceOut); static AString getTempDirectory(); static AString getUserName(); static AString getYear(); static AString getDate(); static AString getTime(); static AString getDateAndTime(); static bool isWindowsOperatingSystem(); static bool isMacOperatingSystem(); static int32_t getNumberOfProcessors(); static AString createUniqueID(); static void unitTest(std::ostream& stream, const bool isVerbose); static bool testRelativePath( const AString& otherPath, const AString& myPath, const AString& correctResult); static AString relativePath( const AString& otherPathIn, const AString& myPathIn); static void setUnexpectedHandler(); static void setNewHandler(); static AString systemCurrentDirectory(); static void sleepSeconds(const float numberOfSeconds); static AString getWorkbenchHome(); }; } // namespace #endif // __SYSTEMUTILITIES_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Common/TileTabsConfiguration.cxx000066400000000000000000000564571300200146000270000ustar00rootroot00000000000000 /*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 __TILE_TABS_CONFIGURATION_DECLARE__ #include "TileTabsConfiguration.h" #undef __TILE_TABS_CONFIGURATION_DECLARE__ #include #include "CaretAssert.h" #include "CaretLogger.h" #include "SystemUtilities.h" using namespace caret; /** * \class caret::TileTabsConfiguration * \brief Defines a tile tabs configuration * \ingroup Common */ /** * Constructor that creates a 2 by 2 configuration. */ TileTabsConfiguration::TileTabsConfiguration() : CaretObject() { initialize(); } /** * Destructor. */ TileTabsConfiguration::~TileTabsConfiguration() { } /** * Copy constructor. * * NOTE: Unique identifier remains the same ! See also: newCopyWithNewUniqueIdentifier() * * @param obj * Object that is copied. */ TileTabsConfiguration::TileTabsConfiguration(const TileTabsConfiguration& obj) : CaretObject(obj) { this->copyHelperTileTabsConfiguration(obj); } /** * Assignment operator. * * NOTE: Unique identifier remains the same ! See also: newCopyWithNewUniqueIdentifier() * * @param obj * Data copied from obj to this. * @return * Reference to this object. */ TileTabsConfiguration& TileTabsConfiguration::operator=(const TileTabsConfiguration& obj) { if (this != &obj) { CaretObject::operator=(obj); this->copyHelperTileTabsConfiguration(obj); } return *this; } /** * Copy this instance and give it a new unique identifier. * Note that copy constructor does not create a new unique identifier. * * @return The new Copy. */ TileTabsConfiguration* TileTabsConfiguration::newCopyWithNewUniqueIdentifier() const { TileTabsConfiguration* newCopy = new TileTabsConfiguration(*this); CaretAssert(newCopy); newCopy->m_uniqueIdentifier = SystemUtilities::createUniqueID(); return newCopy; } /** * Initialize an instance of a tile tabs configuration. */ void TileTabsConfiguration::initialize() { m_rowStretchFactors.resize(getMaximumNumberOfRows(), 1.0); m_columnStretchFactors.resize(getMaximumNumberOfColumns(), 1.0); setNumberOfRows(2); setNumberOfColumns(2); m_defaultConfigurationFlag = false; m_uniqueIdentifier = SystemUtilities::createUniqueID(); } /** * Helps with copying an object of this type. * @param obj * Object that is copied. */ void TileTabsConfiguration::copyHelperTileTabsConfiguration(const TileTabsConfiguration& obj) { m_numberOfColumns = obj.m_numberOfColumns; m_numberOfRows = obj.m_numberOfRows; m_rowStretchFactors = obj.m_rowStretchFactors; m_columnStretchFactors = obj.m_columnStretchFactors; m_name = obj.m_name; //DO NOT CHANGE THE UNIQUE IDENTIFIER: m_uniqueIdentifier } /** * Get the row heights and column widths for this tile tabs configuration using the * given window width and height. * * @param windowWidth * Width of window. * @param windowHeight * Height of window. * @param numberOfModelsToDraw * Number of models to draw. * @param rowHeightsOut * Output containing height of each row. * @param columnWidthsOut * Output containing width of each column. * @return * True if the ouput is valid, else false. */ bool TileTabsConfiguration::getRowHeightsAndColumnWidthsForWindowSize(const int32_t windowWidth, const int32_t windowHeight, const int32_t numberOfModelsToDraw, std::vector& rowHeightsOut, std::vector& columnWidthsOut) { /* * NOTE: When computing widths and heights, do not round. * Rounding may cause the bottom most row or column to extend * outside the graphics region. Shrinking the last row or * column is not desired since it might cause the last model * to be drawn slightly smaller than the others. */ int32_t numRows = 0; int32_t numCols = 0; rowHeightsOut.clear(); columnWidthsOut.clear(); if (isDefaultConfiguration()) { /* * Update number of rows/columns in the default configuration * so that if a scene is saved, the correct number of rows * and columns are saved to the scene. */ updateDefaultConfigurationRowsAndColumns(numberOfModelsToDraw); numRows = getNumberOfRows(); numCols = getNumberOfColumns(); for (int32_t i = 0; i < numRows; i++) { rowHeightsOut.push_back(windowHeight / numRows); } for (int32_t i = 0; i < numCols; i++) { columnWidthsOut.push_back(windowWidth / numCols); } } else { /* * Rows/columns from user configuration */ numRows = getNumberOfRows(); numCols = getNumberOfColumns(); /* * Determine height of each row */ float rowStretchTotal = 0.0; for (int32_t i = 0; i < numRows; i++) { rowStretchTotal += getRowStretchFactor(i); } CaretAssert(rowStretchTotal > 0.0); for (int32_t i = 0; i < numRows; i++) { const int32_t h = static_cast((getRowStretchFactor(i) / rowStretchTotal) * windowHeight); rowHeightsOut.push_back(h); } /* * Determine width of each column */ float columnStretchTotal = 0.0; for (int32_t i = 0; i < numCols; i++) { columnStretchTotal += getColumnStretchFactor(i); } CaretAssert(columnStretchTotal > 0.0); for (int32_t i = 0; i < numCols; i++) { const int32_t w = static_cast((getColumnStretchFactor(i) / columnStretchTotal) * windowWidth); columnWidthsOut.push_back(w); } } if ((numRows == static_cast(rowHeightsOut.size())) && (numCols == static_cast(columnWidthsOut.size()))) { /* * Verify all rows fit within the window */ int32_t rowHeightsSum = 0; for (int32_t i = 0; i < numRows; i++) { rowHeightsSum += rowHeightsOut[i]; } if (rowHeightsSum > windowHeight) { CaretLogSevere("PROGRAM ERROR: Tile Tabs total row heights exceed window height"); rowHeightsOut[numRows - 1] -= (rowHeightsSum - windowHeight); } /* * Adjust width of last column so that it does not extend beyond viewport */ int32_t columnWidthsSum = 0; for (int32_t i = 0; i < numCols; i++) { columnWidthsSum += columnWidthsOut[i]; } if (columnWidthsSum > windowWidth) { CaretLogSevere("PROGRAM ERROR: Tile Tabs total row heights exceed window height"); columnWidthsOut[numCols - 1] = columnWidthsSum - windowWidth; } CaretLogFiner("Tile Tabs Row Heights: " + AString::fromNumbers(rowHeightsOut, ", ")); CaretLogFiner("Tile Tabs Column Widths: " + AString::fromNumbers(columnWidthsOut, ", ")); return true; } const QString msg("Row and heights failed rows=" + AString::number(numRows) + " rowHeights=" + AString::number(rowHeightsOut.size()) + " cols=" + AString::number(numCols) + " rowHeights=" + AString::number(columnWidthsOut.size())); CaretAssertMessage(0, msg); CaretLogSevere(msg); return false; } /** * @return the name of the tile tabs configuration. */ AString TileTabsConfiguration::getName() const { return m_name; } /** * @return Get the unique identifier that uniquely identifies each configuration. */ AString TileTabsConfiguration::getUniqueIdentifier() const { return m_uniqueIdentifier; } /** * Set the name of the tile tabs configuration. * * @param name * New name for configuration. */ void TileTabsConfiguration::setName(const AString& name) { m_name = name; } /** * @return Number of rows. */ int32_t TileTabsConfiguration::getNumberOfRows() const { return m_numberOfRows; } /** * Set number of rows. * * @param numberOfRows * New number of rows. */ void TileTabsConfiguration::setNumberOfRows(const int32_t numberOfRows) { CaretAssert(numberOfRows >= 1); m_numberOfRows = numberOfRows; if (m_numberOfRows > getMaximumNumberOfRows()) { CaretLogSevere("Requested number of rows is " + AString::number(m_numberOfRows) + " but maximum is " + getMaximumNumberOfRows()); m_numberOfRows = getMaximumNumberOfRows(); } } /** * @return Number of columns. */ int32_t TileTabsConfiguration::getNumberOfColumns() const { return m_numberOfColumns; } /** * Set number of rows. * * @param numberOfColumns * New number of rows. */ void TileTabsConfiguration::setNumberOfColumns(const int32_t numberOfColumns) { CaretAssert(numberOfColumns >= 1); m_numberOfColumns = numberOfColumns; if (m_numberOfColumns > getMaximumNumberOfColumns()) { CaretLogSevere("Requested number of columns is " + AString::number(m_numberOfColumns) + " but maximum is " + getMaximumNumberOfColumns()); m_numberOfColumns = getMaximumNumberOfColumns(); } } /** * Get stretch factor for a column. * * @param columnIndex * Index of the column. * @return * Stretch factor for the column. */ float TileTabsConfiguration::getColumnStretchFactor(const int32_t columnIndex) const { CaretAssertVectorIndex(m_columnStretchFactors, columnIndex); return m_columnStretchFactors[columnIndex]; } /** * Set stretch factor for a column. * * @param columnIndex * Index of the column. * @param stretchFactor * Stretch factor for the column. */ void TileTabsConfiguration::setColumnStretchFactor(const int32_t columnIndex, const float stretchFactor) { CaretAssertVectorIndex(m_columnStretchFactors, columnIndex); m_columnStretchFactors[columnIndex] = stretchFactor; } /** * Get stretch factor for a column. * * @param columnIndex * Index of the column. * @return * Stretch factor for the column. */ float TileTabsConfiguration::getRowStretchFactor(const int32_t rowIndex) const { CaretAssertVectorIndex(m_rowStretchFactors, rowIndex); return m_rowStretchFactors[rowIndex]; } /** * Set stretch factor for a column. * * @param rowIndex * Index of the row. * @param stretchFactor * Stretch factor for the column. */ void TileTabsConfiguration::setRowStretchFactor(const int32_t rowIndex, const float stretchFactor) { CaretAssertVectorIndex(m_rowStretchFactors, rowIndex); m_rowStretchFactors[rowIndex] = stretchFactor; } /** * @return Is this the default configuration? Each browser window * always has ONE default configuration which displays all tabs. */ bool TileTabsConfiguration::isDefaultConfiguration() const { return m_defaultConfigurationFlag; } /** * Set the default configuration status. This should only be called by * the browser window which contains one tile tabs configuration that is * used by the browser window for display of all tabs. */ void TileTabsConfiguration::setDefaultConfiguration(const bool defaultConfiguration) { m_defaultConfigurationFlag = defaultConfiguration; } /** * Updates the number of rows and columns in this default configuration * based upon the number of tabs. If this is NOT the default configuration * (isDefaultConfiguration() returns false), no action is taken. * * Since screen width typically exceeds height, ensure the number of * columns is always greater than the number of rows. */ void TileTabsConfiguration::updateDefaultConfigurationRowsAndColumns(const int32_t numberOfTabs) { if (isDefaultConfiguration()) { int32_t numRows = (int)std::sqrt((double)numberOfTabs); int32_t numCols = numRows; int32_t row2 = numRows * numRows; if (row2 < numberOfTabs) { numCols++; } if ((numRows * numCols) < numberOfTabs) { numRows++; } setNumberOfRows(numRows); setNumberOfColumns(numCols); } } /** * @return Encoded tile tabs configuration in XML */ AString TileTabsConfiguration::encodeInXML() const { QDomDocument doc(s_rootTagName); QDomElement root = doc.createElement(s_rootTagName); doc.appendChild(root); QDomElement versionTag = doc.createElement(s_versionTagName); versionTag.setAttribute(s_versionNumberAttributeName, (int)1); root.appendChild(versionTag); QDomElement nameTag = doc.createElement(s_nameTagName); nameTag.appendChild(doc.createTextNode(m_name)); root.appendChild(nameTag); QDomElement uniqueIdentifierTag = doc.createElement(s_uniqueIdentifierTagName); uniqueIdentifierTag.appendChild(doc.createTextNode(m_uniqueIdentifier)); root.appendChild(uniqueIdentifierTag); QDomElement rowStretchFactorsTag = doc.createElement(s_rowStretchFactorsTagName); rowStretchFactorsTag.setAttribute(s_rowStretchFactorsTotalCountAttributeName, static_cast(m_rowStretchFactors.size())); rowStretchFactorsTag.setAttribute(s_rowStretchFactorsSelectedCountAttributeName, static_cast(m_numberOfRows)); rowStretchFactorsTag.appendChild(doc.createTextNode(AString::fromNumbers(m_rowStretchFactors, " "))); root.appendChild(rowStretchFactorsTag); QDomElement columnStretchFactorsTag = doc.createElement(s_columnStretchFactorsTagName); columnStretchFactorsTag.setAttribute(s_columnStretchFactorsTotalCountAttributeName, static_cast(m_columnStretchFactors.size())); columnStretchFactorsTag.setAttribute(s_columnStretchFactorsSelectedCountAttributeName, static_cast(m_numberOfColumns)); columnStretchFactorsTag.appendChild(doc.createTextNode(AString::fromNumbers(m_columnStretchFactors, " "))); root.appendChild(columnStretchFactorsTag); const AString xmlString = doc.toString(); return xmlString; } /** * Decode the tile tabs configuration from XML. * * @param xmlString * String containing XML. * @return * True if configuration was successfully read from the XML, else false. */ bool TileTabsConfiguration::decodeFromXML(const AString& xmlString) { m_defaultConfigurationFlag = false; setNumberOfRows(2); setNumberOfColumns(2); try { QDomDocument doc(s_rootTagName); if (doc.setContent(xmlString) == false) { throw CaretException("Error parsing DomDocument"); } QDomNodeList nodeList = doc.elementsByTagName(s_versionTagName); if (nodeList.isEmpty()) { throw CaretException("Error finding version tag"); } QDomElement versionElement = nodeList.at(0).toElement(); if (versionElement.isNull()) { throw CaretException("Error finding version element"); } const AString versionNumberString = versionElement.attribute(s_versionNumberAttributeName, ""); if (versionNumberString.isEmpty()) { throw CaretException("Error finding version number attribute"); } const int versionNumber = versionNumberString.toInt(); if (versionNumber == 1) { parseVersionOneXML(doc); } else { throw CaretException("Invalid version number attribute " + versionNumberString); } } catch (const CaretException& e) { CaretLogSevere("Error parsing tile tabs configuration XML:\n" + e.whatString() + "\n\n" + xmlString); return false; } return true; } /** * Parse XML for Version One. * * @param doc * XML DOM document. */ void TileTabsConfiguration::parseVersionOneXML(QDomDocument& doc) { QDomNodeList nameNodeList = doc.elementsByTagName(s_nameTagName); if (nameNodeList.isEmpty()) { throw CaretException("Error finding name tag"); } QDomElement nameElement = nameNodeList.at(0).toElement(); if (nameElement.isNull()) { throw CaretException("Error finding name element"); } m_name = nameElement.text(); QDomNodeList uniqueIdNodeList = doc.elementsByTagName(s_uniqueIdentifierTagName); if (uniqueIdNodeList.isEmpty()) { CaretLogWarning("Tile Tabs Configuration " + m_name + " is missing its unique identifier"); m_uniqueIdentifier = SystemUtilities::createUniqueID(); } else { QDomElement uniqueIdElement = uniqueIdNodeList.at(0).toElement(); if (uniqueIdElement.isNull()) { throw CaretException("Error finding unique identifier element"); } m_uniqueIdentifier = uniqueIdElement.text(); } QDomNodeList rowNodeList = doc.elementsByTagName(s_rowStretchFactorsTagName); if (rowNodeList.isEmpty()) { throw CaretException("Error finding row stretch factors tag"); } QDomElement rowElement = rowNodeList.at(0).toElement(); if (rowElement.isNull()) { throw CaretException("Error finding row element"); } const AString numberOfRowsString = rowElement.attribute(s_rowStretchFactorsSelectedCountAttributeName, ""); if (numberOfRowsString.isEmpty()) { throw CaretException("Error finding number of rows attribute"); } const int32_t selectedNumberOfRows = numberOfRowsString.toInt(); if (selectedNumberOfRows <= 0) { throw CaretException("Invalid number of rows attribute " + numberOfRowsString); } const AString totalNumberOfRowsString = rowElement.attribute(s_rowStretchFactorsTotalCountAttributeName, ""); int32_t totalNumberOfRows = 0; if (totalNumberOfRowsString.isEmpty()) { CaretLogWarning("Total number of rows attribute is missing."); } else { totalNumberOfRows = totalNumberOfRowsString.toInt(); } const AString rowStretchFactorsText = rowElement.text(); std::vector rowStretchFactors; AString::toNumbers(rowStretchFactorsText, rowStretchFactors); if (static_cast(rowStretchFactors.size()) != totalNumberOfRows) { throw CaretException("Stretch factor number of rows is " + AString::number(totalNumberOfRows) + " but have " + AString::number(static_cast(rowStretchFactors.size())) + " stretch factors."); } QDomNodeList columnNodeList = doc.elementsByTagName(s_columnStretchFactorsTagName); if (columnNodeList.isEmpty()) { throw CaretException("Error finding column stretch factors tag"); } QDomElement columnElement = columnNodeList.at(0).toElement(); if (columnElement.isNull()) { throw CaretException("Error finding column element"); } const AString numberOfColumnsString = columnElement.attribute(s_columnStretchFactorsSelectedCountAttributeName, ""); if (numberOfColumnsString.isEmpty()) { throw CaretException("Error finding number of columns attribute"); } const int32_t selectedNumberOfColumns = numberOfColumnsString.toInt(); if (selectedNumberOfColumns <= 0) { throw CaretException("Invalid number of columns attribute " + numberOfColumnsString); } const AString totalNumberOfColumnsString = columnElement.attribute(s_columnStretchFactorsTotalCountAttributeName, ""); int32_t totalNumberOfColumns = 0; if (totalNumberOfColumnsString.isEmpty()) { CaretLogWarning("Total number of columns attribute is missing."); } else { totalNumberOfColumns = totalNumberOfColumnsString.toInt(); } const AString columnStretchFactorsText = columnElement.text(); std::vector columnStretchFactors; AString::toNumbers(columnStretchFactorsText, columnStretchFactors); if (static_cast(columnStretchFactors.size()) != totalNumberOfColumns) { throw CaretException("Stretch factor number of columns is " + AString::number(totalNumberOfColumns) + " but have " + AString::number(static_cast(columnStretchFactors.size())) + " stretch factors."); } setNumberOfRows(selectedNumberOfRows); setNumberOfColumns(selectedNumberOfColumns); const int32_t maxRowStretchFactors = std::min(totalNumberOfRows, static_cast(m_rowStretchFactors.size())); for (int32_t i = 0; i < maxRowStretchFactors; i++) { m_rowStretchFactors[i] = rowStretchFactors[i]; } const int32_t maxColumnStretchFactors = std::min(totalNumberOfColumns, static_cast(m_columnStretchFactors.size())); for (int32_t i = 0; i < maxColumnStretchFactors; i++) { m_columnStretchFactors[i] = columnStretchFactors[i]; } } /** * Compare two tile tabs configurations by name. * * @param ttc1 * First tile tab configuration. * @param ttc2 * Second tile tab configuration. * @return * True if ttc1 is "less than" when compared by name, else false. */ bool TileTabsConfiguration::lessThanComparisonByName(const TileTabsConfiguration* ttc1, const TileTabsConfiguration* ttc2) { if (ttc1->getName() < ttc2->getName()) { return true; } return false; } connectome-workbench-1.2.3+git41-gc4c6c90/src/Common/TileTabsConfiguration.h000066400000000000000000000136041300200146000264100ustar00rootroot00000000000000#ifndef __TILE_TABS_CONFIGURATION_H__ #define __TILE_TABS_CONFIGURATION_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 "CaretException.h" #include "CaretObject.h" class QDomDocument; namespace caret { class TileTabsConfiguration : public CaretObject { public: TileTabsConfiguration(); virtual ~TileTabsConfiguration(); TileTabsConfiguration(const TileTabsConfiguration& obj); TileTabsConfiguration& operator=(const TileTabsConfiguration& obj); TileTabsConfiguration* newCopyWithNewUniqueIdentifier() const; bool getRowHeightsAndColumnWidthsForWindowSize(const int32_t windowWidth, const int32_t windowHeight, const int32_t numberOfModelsToDraw, std::vector& rowHeightsOut, std::vector& columnWidthsOut); AString getName() const; void setName(const AString& name); AString getUniqueIdentifier() const; int32_t getNumberOfRows() const; void setNumberOfRows(const int32_t numberOfRows); int32_t getNumberOfColumns() const; void setNumberOfColumns(const int32_t numberOfColumns); float getColumnStretchFactor(const int32_t columnIndex) const; void setColumnStretchFactor(const int32_t columnIndex, const float stretchFactor); float getRowStretchFactor(const int32_t rowIndex) const; void setRowStretchFactor(const int32_t rowIndex, const float stretchFactor); bool isDefaultConfiguration() const; void setDefaultConfiguration(const bool defaultConfiguration); AString encodeInXML() const; bool decodeFromXML(const AString& xmlString); static bool lessThanComparisonByName(const TileTabsConfiguration* ttc1, const TileTabsConfiguration* ttc2); /** * @return Maximum number of rows in a tile tabs configuration */ static inline int32_t getMaximumNumberOfRows() { return 20; } /** * @return Maximum number of columns in a tile tabs configuration */ static inline int32_t getMaximumNumberOfColumns() { return 20; } void updateDefaultConfigurationRowsAndColumns(const int32_t numberOfTabs); // ADD_NEW_METHODS_HERE private: void copyHelperTileTabsConfiguration(const TileTabsConfiguration& obj); void parseVersionOneXML(QDomDocument& doc); void initialize(); // ADD_NEW_MEMBERS_HERE AString m_name; bool m_defaultConfigurationFlag; /** Unique identifier does not get copied */ AString m_uniqueIdentifier; int32_t m_numberOfRows; int32_t m_numberOfColumns; std::vector m_rowStretchFactors; std::vector m_columnStretchFactors; static const AString s_rootTagName; static const AString s_versionTagName; static const AString s_nameTagName; static const AString s_uniqueIdentifierTagName; static const AString s_versionNumberAttributeName; static const AString s_columnStretchFactorsTagName; static const AString s_columnStretchFactorsSelectedCountAttributeName; static const AString s_columnStretchFactorsTotalCountAttributeName; static const AString s_rowStretchFactorsTagName; static const AString s_rowStretchFactorsSelectedCountAttributeName; static const AString s_rowStretchFactorsTotalCountAttributeName; }; #ifdef __TILE_TABS_CONFIGURATION_DECLARE__ const AString TileTabsConfiguration::s_rootTagName = "TileTabsConfiguration"; const AString TileTabsConfiguration::s_versionTagName = "Version"; const AString TileTabsConfiguration::s_versionNumberAttributeName = "Number"; const AString TileTabsConfiguration::s_nameTagName = "Name"; const AString TileTabsConfiguration::s_uniqueIdentifierTagName = "UniqueIdentifier"; const AString TileTabsConfiguration::s_columnStretchFactorsTagName = "ColumnStretchFactors"; const AString TileTabsConfiguration::s_columnStretchFactorsSelectedCountAttributeName = "SelectedRowCount"; const AString TileTabsConfiguration::s_columnStretchFactorsTotalCountAttributeName = "TotalRowCount"; const AString TileTabsConfiguration::s_rowStretchFactorsTagName = "RowStretchFactors"; const AString TileTabsConfiguration::s_rowStretchFactorsSelectedCountAttributeName = "SelectedColumnCount"; const AString TileTabsConfiguration::s_rowStretchFactorsTotalCountAttributeName = "TotalColumnCount"; #endif // __TILE_TABS_CONFIGURATION_DECLARE__ } // namespace #endif //__TILE_TABS_CONFIGURATION_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Common/TracksModificationInterface.h000066400000000000000000000073751300200146000275570ustar00rootroot00000000000000#ifndef __TRACKSMODIFICATION_H__ #define __TRACKSMODIFICATION_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*/ namespace caret { /** * \brief Interface for tracking object modification status. * * Interface for tracking an objects modification status. *
* setModified() should set a boolean that indicates * the modified status. *
* clearModified() should clear the boolean that indicates * the modified status AND should call clearModified() * on any members that implement this interface. *
* isModified() should return the boolean that indicates * the modified status. *
*
* When a class DOES extend a class that implements this * interface, it must implement the clearModified() * and the isModified() ONLY IF it contains members * that implement this interface. *
* The clearMethod() must call the parent's clearModified() * method and the clearModified() method on any member * classes that implement this interface. *
* The isModified() method must first call the parent's * isModified() method, and, if true, true true. Otherwise, * return true if a member is modified. * *
* An alternative model of this interface is to add * methods such as addTrackable(TracksModificationInterface) * and removeTrackable(TracksModificationInterface) that * could be used to query and reset the modification status * of any members in the implementing class and subclasses. * In this case, the isModified(), setModified(), and * clearModified() methods would only need to be implemented * in the top-level class. In this case the * addTrackable() and removeTrackable() classes may be * better off in a separate interface. */ class TracksModificationInterface { protected: /** * Constructor. */ TracksModificationInterface() { } /** * Destructor. */ virtual ~TracksModificationInterface() { } private: TracksModificationInterface(const TracksModificationInterface&); TracksModificationInterface& operator=(const TracksModificationInterface&); public: /** * Set the status to modified. */ virtual void setModified() = 0; /** * Set the status to unmodified. */ virtual void clearModified() = 0; /** * Is the object modified? * @return true if modified, else false. */ virtual bool isModified() const = 0; }; class TracksModification : public TracksModificationInterface { /** modification status */ bool modifiedFlag; protected: TracksModification() { modifiedFlag = false; } public: /** * Set the status to modified. */ virtual void setModified() { modifiedFlag = true; } /** * Set the status to unmodified. */ virtual void clearModified() { modifiedFlag = false; } /** * Is the object modified? * @return true if modified, else false. */ virtual bool isModified() const { return modifiedFlag; } }; } // namespace #endif // __TRACKSMODIFICATION_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Common/TriStateSelectionStatusEnum.cxx000066400000000000000000000260511300200146000301620ustar00rootroot00000000000000 /*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 __TRI_STATE_SELECTION_STATUS_ENUM_DECLARE__ #include "TriStateSelectionStatusEnum.h" #undef __TRI_STATE_SELECTION_STATUS_ENUM_DECLARE__ #include "CaretAssert.h" using namespace caret; /** * \class caret::TriStateSelectionStatusEnum * \brief * * * * 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_triStateSelectionStatusEnumComboBox; * * Declare a slot that is called when user changes selection * private slots: * void triStateSelectionStatusEnumComboBoxItemActivated(); * * Implementation File (.cxx) * Include the header files * #include "EnumComboBoxTemplate.h" * #include "TriStateSelectionStatusEnum.h" * * Instatiate: * m_triStateSelectionStatusEnumComboBox = new EnumComboBoxTemplate(this); * m_triStateSelectionStatusEnumComboBox->setup(); * * Get notified when the user changes the selection: * QObject::connect(m_triStateSelectionStatusEnumComboBox, SIGNAL(itemActivated()), * this, SLOT(triStateSelectionStatusEnumComboBoxItemActivated())); * * Update the selection: * m_triStateSelectionStatusEnumComboBox->setSelectedItem(NEW_VALUE); * * Read the selection: * const TriStateSelectionStatusEnum::Enum VARIABLE = m_triStateSelectionStatusEnumComboBox->getSelectedItem(); * */ /** * Constructor. * * @param enumValue * An enumerated value. * @param name * Name of enumerated value. * * @param guiName * User-friendly name for use in user-interface. */ TriStateSelectionStatusEnum::TriStateSelectionStatusEnum(const Enum enumValue, const AString& name, const AString& guiName) { this->enumValue = enumValue; this->integerCode = integerCodeCounter++; this->name = name; this->guiName = guiName; } /** * Destructor. */ TriStateSelectionStatusEnum::~TriStateSelectionStatusEnum() { } /** * Initialize the enumerated metadata. */ void TriStateSelectionStatusEnum::initialize() { if (initializedFlag) { return; } initializedFlag = true; enumData.push_back(TriStateSelectionStatusEnum(UNSELECTED, "UNSELECTED", "Unselected")); enumData.push_back(TriStateSelectionStatusEnum(PARTIALLY_SELECTED, "PARTIALLY_SELECTED", "Partially Selected")); enumData.push_back(TriStateSelectionStatusEnum(SELECTED, "SELECTED", "Selected")); } /** * 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 TriStateSelectionStatusEnum* TriStateSelectionStatusEnum::findData(const Enum enumValue) { if (initializedFlag == false) initialize(); size_t num = enumData.size(); for (size_t i = 0; i < num; i++) { const TriStateSelectionStatusEnum* 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 TriStateSelectionStatusEnum::toName(Enum enumValue) { if (initializedFlag == false) initialize(); const TriStateSelectionStatusEnum* 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. */ TriStateSelectionStatusEnum::Enum TriStateSelectionStatusEnum::fromName(const AString& name, bool* isValidOut) { if (initializedFlag == false) initialize(); bool validFlag = false; Enum enumValue = TriStateSelectionStatusEnum::enumData[0].enumValue; for (std::vector::iterator iter = enumData.begin(); iter != enumData.end(); iter++) { const TriStateSelectionStatusEnum& 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 TriStateSelectionStatusEnum")); } return enumValue; } /** * Get a GUI string representation of the enumerated type. * @param enumValue * Enumerated value. * @return * String representing enumerated value. */ AString TriStateSelectionStatusEnum::toGuiName(Enum enumValue) { if (initializedFlag == false) initialize(); const TriStateSelectionStatusEnum* 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. */ TriStateSelectionStatusEnum::Enum TriStateSelectionStatusEnum::fromGuiName(const AString& guiName, bool* isValidOut) { if (initializedFlag == false) initialize(); bool validFlag = false; Enum enumValue = TriStateSelectionStatusEnum::enumData[0].enumValue; for (std::vector::iterator iter = enumData.begin(); iter != enumData.end(); iter++) { const TriStateSelectionStatusEnum& 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 TriStateSelectionStatusEnum")); } return enumValue; } /** * Get the integer code for a data type. * * @return * Integer code for data type. */ int32_t TriStateSelectionStatusEnum::toIntegerCode(Enum enumValue) { if (initializedFlag == false) initialize(); const TriStateSelectionStatusEnum* 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. */ TriStateSelectionStatusEnum::Enum TriStateSelectionStatusEnum::fromIntegerCode(const int32_t integerCode, bool* isValidOut) { if (initializedFlag == false) initialize(); bool validFlag = false; Enum enumValue = TriStateSelectionStatusEnum::enumData[0].enumValue; for (std::vector::iterator iter = enumData.begin(); iter != enumData.end(); iter++) { const TriStateSelectionStatusEnum& 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 TriStateSelectionStatusEnum")); } 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 TriStateSelectionStatusEnum::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 TriStateSelectionStatusEnum::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(TriStateSelectionStatusEnum::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 TriStateSelectionStatusEnum::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(TriStateSelectionStatusEnum::toGuiName(iter->enumValue)); } if (isSorted) { std::sort(allGuiNames.begin(), allGuiNames.end()); } } connectome-workbench-1.2.3+git41-gc4c6c90/src/Common/TriStateSelectionStatusEnum.h000066400000000000000000000062761300200146000276160ustar00rootroot00000000000000#ifndef __TRI_STATE_SELECTION_STATUS_ENUM_H__ #define __TRI_STATE_SELECTION_STATUS_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 TriStateSelectionStatusEnum { public: /** * Enumerated values. */ enum Enum { /** */ UNSELECTED, /** */ PARTIALLY_SELECTED, /** */ SELECTED }; ~TriStateSelectionStatusEnum(); 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: TriStateSelectionStatusEnum(const Enum enumValue, const AString& name, const AString& guiName); static const TriStateSelectionStatusEnum* 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 __TRI_STATE_SELECTION_STATUS_ENUM_DECLARE__ std::vector TriStateSelectionStatusEnum::enumData; bool TriStateSelectionStatusEnum::initializedFlag = false; int32_t TriStateSelectionStatusEnum::integerCodeCounter = 0; #endif // __TRI_STATE_SELECTION_STATUS_ENUM_DECLARE__ } // namespace #endif //__TRI_STATE_SELECTION_STATUS_ENUM_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Common/Vector3D.cxx000066400000000000000000000124001300200146000241460ustar00rootroot00000000000000/*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 "MathFunctions.h" #include "Vector3D.h" using namespace std; using namespace caret; Vector3D Vector3D::cross(const Vector3D& right) const { Vector3D ret; MathFunctions::crossProduct(m_vec, right.m_vec, ret.m_vec); return ret; } float Vector3D::dot(const Vector3D& right) const { return MathFunctions::dotProduct(m_vec, right.m_vec); } float Vector3D::length() const { return MathFunctions::vectorLength(m_vec); } float Vector3D::lengthsquared() const { return m_vec[0] * m_vec[0] + m_vec[1] * m_vec[1] + m_vec[2] * m_vec[2]; } Vector3D Vector3D::normal(float* origLength) const { Vector3D ret = *this; if (origLength != NULL) { *origLength = MathFunctions::normalizeVector(ret.m_vec); } else { MathFunctions::normalizeVector(ret.m_vec); } return ret; } float Vector3D::distToLine(const Vector3D& p1, const Vector3D& p2, Vector3D* closePointOut) const { Vector3D diff = p2 - p1; float origLength; Vector3D diffHat = diff.normal(&origLength); if (origLength == 0.0f)//line is degenerate, return distance to point - we could return zero, but this seems like it would be better behaved { if (closePointOut != NULL) *closePointOut = p1; return (*this - p1).length(); } float distAlong = diffHat.dot(*this - p1); Vector3D closePoint = p1 + diffHat * distAlong; if (closePointOut != NULL) *closePointOut = closePoint; return (*this - closePoint).length(); } float Vector3D::distToLineSegment(const Vector3D& p1, const Vector3D& p2, Vector3D* closePointOut) const { float origLength; Vector3D diffHat = (p2 - p1).normal(&origLength); if (origLength == 0.0f)//line segment is degenerate, return distance to point { if (closePointOut != NULL) *closePointOut = p1; return (*this - p1).length(); } float distAlong = diffHat.dot(*this - p1); if (distAlong < 0.0f) distAlong = 0.0f; if (distAlong > origLength) distAlong = origLength; Vector3D closePoint = p1 + diffHat * distAlong; if (closePointOut != NULL) *closePointOut = closePoint; return (*this - closePoint).length(); } Vector3D::Vector3D() { m_vec[0] = 0.0f; m_vec[1] = 0.0f; m_vec[2] = 0.0f; } Vector3D::Vector3D(const float& x, const float& y, const float& z) { m_vec[0] = x; m_vec[1] = y; m_vec[2] = z; } Vector3D::Vector3D(const float* right) { m_vec[0] = right[0]; m_vec[1] = right[1]; m_vec[2] = right[2]; } float& Vector3D::operator[](const int64_t& index) { CaretAssert(index > -1 && index < 3); return m_vec[index]; } const float& Vector3D::operator[](const int64_t& index) const { CaretAssert(index > -1 && index < 3); return m_vec[index]; } float& Vector3D::operator[](const int32_t& index) { CaretAssert(index > -1 && index < 3); return m_vec[index]; } const float& Vector3D::operator[](const int32_t& index) const { CaretAssert(index > -1 && index < 3); return m_vec[index]; } Vector3D Vector3D::operator*(const float& right) const { Vector3D ret = *this; ret *= right; return ret; } Vector3D& Vector3D::operator*=(const float& right) { m_vec[0] *= right; m_vec[1] *= right; m_vec[2] *= right; return *this; } Vector3D caret::operator*(const float& left, const Vector3D& right) { return right * left; } Vector3D Vector3D::operator+(const Vector3D& right) const { Vector3D ret = *this; ret += right; return ret; } Vector3D& Vector3D::operator+=(const Vector3D& right) { m_vec[0] += right.m_vec[0]; m_vec[1] += right.m_vec[1]; m_vec[2] += right.m_vec[2]; return *this; } Vector3D Vector3D::operator-(const Vector3D& right) const { Vector3D ret = *this; ret -= right; return ret; } Vector3D Vector3D::operator-() const { Vector3D ret; ret.m_vec[0] = -m_vec[0]; ret.m_vec[1] = -m_vec[1]; ret.m_vec[2] = -m_vec[2]; return ret; } Vector3D& Vector3D::operator-=(const Vector3D& right) { m_vec[0] -= right.m_vec[0]; m_vec[1] -= right.m_vec[1]; m_vec[2] -= right.m_vec[2]; return *this; } Vector3D Vector3D::operator/(const float& right) const { Vector3D ret = *this; ret /= right; return ret; } Vector3D& Vector3D::operator/=(const float& right) { m_vec[0] /= right; m_vec[1] /= right; m_vec[2] /= right; return *this; } Vector3D& Vector3D::operator=(const float* right) { m_vec[0] = right[0]; m_vec[1] = right[1]; m_vec[2] = right[2]; return *this; } connectome-workbench-1.2.3+git41-gc4c6c90/src/Common/Vector3D.h000066400000000000000000000051651300200146000236050ustar00rootroot00000000000000#ifndef __VECTOR_3D_H__ #define __VECTOR_3D_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 "CaretAssert.h" namespace caret { class Vector3D { float m_vec[3]; public: //vector functions float dot(const Vector3D& right) const; Vector3D cross(const Vector3D& right) const; Vector3D normal(float* origLength = NULL) const; float length() const; float lengthsquared() const; //geometry functions float distToLine(const Vector3D& p1, const Vector3D& p2, Vector3D* closePointOut = NULL) const; float distToLineSegment(const Vector3D& p1, const Vector3D& p2, Vector3D* closePointOut = NULL) const; //constructors Vector3D(); Vector3D(const float& x, const float& y, const float& z); Vector3D(const float* right); //compatibility operators float& operator[](const int64_t& index); const float& operator[](const int64_t& index) const; float& operator[](const int32_t& index); const float& operator[](const int32_t& index) const; Vector3D& operator=(const float* right); //numerical operators Vector3D& operator+=(const Vector3D& right); Vector3D& operator-=(const Vector3D& right); Vector3D& operator*=(const float& right); Vector3D& operator/=(const float& right); Vector3D operator+(const Vector3D& right) const; Vector3D operator-(const Vector3D& right) const; Vector3D operator-() const; Vector3D operator*(const float& right) const; Vector3D operator/(const float& right) const;//NOTE: doesn't really make sense to have the other division, unlike multiplication inline operator float*() { return m_vec; } }; Vector3D operator*(const float& left, const Vector3D& right); } #endif //__VECTOR_3D_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Common/VectorOperation.cxx000066400000000000000000000053441300200146000256510ustar00rootroot00000000000000/*LICENSE_START*/ /* * Copyright (C) 2015 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 "VectorOperation.h" #include "CaretAssert.h" using namespace std; using namespace caret; VectorOperation::Operation VectorOperation::stringToOperation(const AString& string, bool& ok) { ok = true; if (string == "DOT") { return DOT; } else if (string == "CROSS") { return CROSS; } else if (string == "ADD") { return ADD; } else if (string == "SUBTRACT") { return SUBTRACT; } ok = false; return DOT;//have to return something } AString VectorOperation::operationToString(const VectorOperation::Operation& myOp) { switch (myOp) { case DOT: return "DOT"; case CROSS: return "CROSS"; case ADD: return "ADD"; case SUBTRACT: return "SUBTRACT"; } return ""; } vector VectorOperation::getAllOperations() { vector ret; ret.push_back(DOT); ret.push_back(CROSS); ret.push_back(ADD); ret.push_back(SUBTRACT); return ret; } bool VectorOperation::operationReturnsScalar(const VectorOperation::Operation& myOp) { switch (myOp) { case DOT: return true; default: return false; } } Vector3D VectorOperation::doVectorOperation(const Vector3D& first, const Vector3D& second, const Operation& myOp) { switch (myOp) { case CROSS: return first.cross(second); case ADD: return first + second; case SUBTRACT: return first - second; default: CaretAssert(false); return Vector3D(); } } float VectorOperation::doScalarOperation(const Vector3D& first, const Vector3D& second, const VectorOperation::Operation& myOp) { switch (myOp) { case DOT: return first.dot(second); default: CaretAssert(false); return 0.0f; } } connectome-workbench-1.2.3+git41-gc4c6c90/src/Common/VectorOperation.h000066400000000000000000000033341300200146000252730ustar00rootroot00000000000000#ifndef __VECTOR_OPERATION_H__ #define __VECTOR_OPERATION_H__ /*LICENSE_START*/ /* * Copyright (C) 2015 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 "AString.h" #include "Vector3D.h" #include namespace caret { ///helper class for doing user-specified operations on 3D vectors class VectorOperation { public: enum Operation { DOT, CROSS, ADD, SUBTRACT }; static Operation stringToOperation(const AString& string, bool& ok); static AString operationToString(const Operation& myOp); static std::vector getAllOperations(); static bool operationReturnsScalar(const Operation& myOp); static Vector3D doVectorOperation(const Vector3D& first, const Vector3D& second, const Operation& myOp); static float doScalarOperation(const Vector3D& first, const Vector3D& second, const Operation& myOp); }; } #endif //__VECTOR_OPERATION_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Common/VoxelIJK.h000066400000000000000000000037501300200146000236050ustar00rootroot00000000000000#ifndef __VOXEL_IJK_H__ #define __VOXEL_IJK_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 "stdint.h" namespace caret { struct VoxelIJK { int64_t m_ijk[3]; VoxelIJK() { } VoxelIJK(int64_t i, int64_t j, int64_t k) { m_ijk[0] = i; m_ijk[1] = j; m_ijk[2] = k; } template VoxelIJK(const T ijk[3]) { m_ijk[0] = ijk[0]; m_ijk[1] = ijk[1]; m_ijk[2] = ijk[2]; } bool operator<(const VoxelIJK& rhs) const//so it kan be the key of a map { if (m_ijk[2] < rhs.m_ijk[2]) return true;//compare such that when sorted, m_ijk[0] moves fastest if (m_ijk[2] > rhs.m_ijk[2]) return false; if (m_ijk[1] < rhs.m_ijk[1]) return true; if (m_ijk[1] > rhs.m_ijk[1]) return false; return (m_ijk[0] < rhs.m_ijk[0]); } bool operator==(const VoxelIJK& rhs) const { return (m_ijk[0] == rhs.m_ijk[0] && m_ijk[1] == rhs.m_ijk[1] && m_ijk[2] == rhs.m_ijk[2]); } bool operator!=(const VoxelIJK& rhs) const { return !((*this) == rhs); } }; } #endif //__VOXEL_IJK_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Common/YokingGroupEnum.cxx000066400000000000000000000236661300200146000256370ustar00rootroot00000000000000 /*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 __YOKING_GROUP_ENUM_DECLARE__ #include "YokingGroupEnum.h" #undef __YOKING_GROUP_ENUM_DECLARE__ #include "CaretAssert.h" using namespace caret; /** * \class caret::YokingGroupEnum * \brief Enumerated type for yoking views of model. */ /** * Constructor. * * @param enumValue * An enumerated value. * @param name * Name of enumerated value. * * @param guiName * User-friendly name for use in user-interface. */ YokingGroupEnum::YokingGroupEnum(const Enum enumValue, const AString& name, const AString& guiName) { this->enumValue = enumValue; this->integerCode = integerCodeCounter++; this->name = name; this->guiName = guiName; } /** * Destructor. */ YokingGroupEnum::~YokingGroupEnum() { } /** * Initialize the enumerated metadata. */ void YokingGroupEnum::initialize() { if (initializedFlag) { return; } initializedFlag = true; enumData.push_back(YokingGroupEnum(YOKING_GROUP_OFF, "YOKING_GROUP_OFF", "Off")); enumData.push_back(YokingGroupEnum(YOKING_GROUP_A, "YOKING_GROUP_A", "Group A")); enumData.push_back(YokingGroupEnum(YOKING_GROUP_B, "YOKING_GROUP_B", "Group B")); enumData.push_back(YokingGroupEnum(YOKING_GROUP_C, "YOKING_GROUP_C", "Group C")); enumData.push_back(YokingGroupEnum(YOKING_GROUP_D, "YOKING_GROUP_D", "Group D")); enumData.push_back(YokingGroupEnum(YOKING_GROUP_E, "YOKING_GROUP_E", "Group E")); enumData.push_back(YokingGroupEnum(YOKING_GROUP_F, "YOKING_GROUP_F", "Group F")); enumData.push_back(YokingGroupEnum(YOKING_GROUP_G, "YOKING_GROUP_G", "Group G")); enumData.push_back(YokingGroupEnum(YOKING_GROUP_H, "YOKING_GROUP_H", "Group H")); } /** * 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 YokingGroupEnum* YokingGroupEnum::findData(const Enum enumValue) { if (initializedFlag == false) initialize(); size_t num = enumData.size(); for (size_t i = 0; i < num; i++) { const YokingGroupEnum* 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 YokingGroupEnum::toName(Enum enumValue) { if (initializedFlag == false) initialize(); const YokingGroupEnum* 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. */ YokingGroupEnum::Enum YokingGroupEnum::fromName(const AString& name, bool* isValidOut) { if (initializedFlag == false) initialize(); bool validFlag = false; Enum enumValue = YOKING_GROUP_OFF; for (std::vector::iterator iter = enumData.begin(); iter != enumData.end(); iter++) { const YokingGroupEnum& 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 YokingGroupEnum")); } return enumValue; } /** * Get a GUI string representation of the enumerated type. * @param enumValue * Enumerated value. * @return * String representing enumerated value. */ AString YokingGroupEnum::toGuiName(Enum enumValue) { if (initializedFlag == false) initialize(); const YokingGroupEnum* 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. */ YokingGroupEnum::Enum YokingGroupEnum::fromGuiName(const AString& guiName, bool* isValidOut) { if (initializedFlag == false) initialize(); bool validFlag = false; Enum enumValue = YOKING_GROUP_OFF; for (std::vector::iterator iter = enumData.begin(); iter != enumData.end(); iter++) { const YokingGroupEnum& 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 YokingGroupEnum")); } return enumValue; } /** * Get the integer code for a data type. * * @return * Integer code for data type. */ int32_t YokingGroupEnum::toIntegerCode(Enum enumValue) { if (initializedFlag == false) initialize(); const YokingGroupEnum* 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. */ YokingGroupEnum::Enum YokingGroupEnum::fromIntegerCode(const int32_t integerCode, bool* isValidOut) { if (initializedFlag == false) initialize(); bool validFlag = false; Enum enumValue = YOKING_GROUP_OFF; for (std::vector::iterator iter = enumData.begin(); iter != enumData.end(); iter++) { const YokingGroupEnum& 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 YokingGroupEnum")); } 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 YokingGroupEnum::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 YokingGroupEnum::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(YokingGroupEnum::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 YokingGroupEnum::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(YokingGroupEnum::toGuiName(iter->enumValue)); } if (isSorted) { std::sort(allGuiNames.begin(), allGuiNames.end()); } } connectome-workbench-1.2.3+git41-gc4c6c90/src/Common/YokingGroupEnum.h000066400000000000000000000065031300200146000252530ustar00rootroot00000000000000#ifndef __YOKING_GROUP_ENUM_H__ #define __YOKING_GROUP_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 YokingGroupEnum { public: /** * Enumerated values. */ enum Enum { /** Off */ YOKING_GROUP_OFF, /** Group A */ YOKING_GROUP_A, /** Group B */ YOKING_GROUP_B, /** Group C */ YOKING_GROUP_C, /** Group D */ YOKING_GROUP_D, /** Group E */ YOKING_GROUP_E, /** Group F */ YOKING_GROUP_F, /** Group G */ YOKING_GROUP_G, /** Group H */ YOKING_GROUP_H }; ~YokingGroupEnum(); 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: YokingGroupEnum(const Enum enumValue, const AString& name, const AString& guiName); static const YokingGroupEnum* 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 __YOKING_GROUP_ENUM_DECLARE__ std::vector YokingGroupEnum::enumData; bool YokingGroupEnum::initializedFlag = false; int32_t YokingGroupEnum::integerCodeCounter = 0; #endif // __YOKING_GROUP_ENUM_DECLARE__ } // namespace #endif //__YOKING_GROUP_ENUM_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Common/dot_wrapper.h000066400000000000000000000055421300200146000245010ustar00rootroot00000000000000#ifndef DOT_WRAPPER_H #define DOT_WRAPPER_H //workbench is strictly c++, so we don't actually need ifdef guards on this //we also don't expose any libraries, so it doesn't really matter whether it switches to c++ name mangling when we disable SIMD //but hey, whatever #ifdef __cplusplus extern "C" { #endif #ifdef _WIN32 #define inline __inline #endif #ifdef CARET_DOTFCN #include "dot.h" #else inline double sddot (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() //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_flags; //and dummy implementation of dot_set_impl inline dot_flags dot_set_impl (dot_flags) { return DOT_NAIVE; } #endif #ifdef __cplusplus } #endif //convenience helpers for the enum #include "AString.h" #include "CaretAssert.h" #include namespace caret { class DotSIMDEnum { public: typedef dot_flags Enum; static inline std::vector getAllEnums() { std::vector ret; ret.push_back(DOT_NAIVE); ret.push_back(DOT_SSE2); ret.push_back(DOT_AVX); ret.push_back(DOT_AVXFMA); ret.push_back(DOT_AUTO); return ret; } static inline Enum fromName(const AString& name, bool* isValidOut = NULL) { bool valid = false; Enum ret = DOT_NAIVE; if (name == "NAIVE") { ret = DOT_NAIVE; valid = true; } else if (name == "SSE2") { ret = DOT_SSE2; valid = true; } else if (name == "AVX") { ret = DOT_AVX; valid = true; } else if (name == "AVXFMA") { ret = DOT_AVXFMA; valid = true; } else if (name == "AUTO") { ret = DOT_AUTO; valid = true; } if (isValidOut == NULL) { CaretAssert(valid); } else { *isValidOut = valid; } return ret; } static inline AString toName(const Enum& value) { switch (value) { case DOT_NAIVE: return "NAIVE"; case DOT_SSE2: return "SSE2"; case DOT_AVX: return "AVX"; case DOT_AVXFMA: return "AVXFMA"; case DOT_AUTO: return "AUTO"; default: CaretAssert(0); } return ""; } }; } #endif connectome-workbench-1.2.3+git41-gc4c6c90/src/Desktop/000077500000000000000000000000001300200146000221555ustar00rootroot00000000000000connectome-workbench-1.2.3+git41-gc4c6c90/src/Desktop/CMakeLists.txt000066400000000000000000000112561300200146000247220ustar00rootroot00000000000000# # Name of Project # PROJECT(Desktop) # # Name of executable # 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}) # # Added by JWH to eliminate OpenGL linking errors in Ubuntu Linux # IF (UNIX) IF (NOT APPLE) FIND_PACKAGE(OpenGL) ENDIF (NOT APPLE) ENDIF (UNIX) # # Resources # QT4_ADD_RESOURCES(IMAGE_RCS_SRCS ../Resources/GeneralResources/general_resources.qrc ../Resources/GuiResources/gui_resources.qrc) # # Create the executable # Apple creates a bundle # IF (APPLE) ADD_EXECUTABLE(${EXE_NAME} MACOSX_BUNDLE desktop.cxx ${IMAGE_RCS_SRCS} ) # # This is a customized Info.Plist for Mac so that a spec # file can be opened using Finder # SET_TARGET_PROPERTIES( ${EXE_NAME} PROPERTIES MACOSX_BUNDLE_INFO_PLIST ${CMAKE_SOURCE_DIR}/../mac_plist/MacOSXBundleInfo.plist.in ) # SET_TARGET_PROPERTIES( # ${EXE_NAME} # PROPERTIES # RESOURCE # ${QT_BINARY_DIR}/../src/gui/mac/qt_menu.nib # ) ENDIF (APPLE) IF (WIN32) ADD_EXECUTABLE(${EXE_NAME} desktop.cxx ${IMAGE_RCS_SRCS} ${CMAKE_SOURCE_DIR}/../icons/windows/workbench.rc ) ENDIF (WIN32) IF (NOT APPLE) IF (UNIX) ADD_EXECUTABLE(${EXE_NAME} desktop.cxx ${IMAGE_RCS_SRCS} ) ENDIF (UNIX) ENDIF (NOT APPLE) # # Libraries that are linked # TARGET_LINK_LIBRARIES(${EXE_NAME} GuiQt Commands Operations Algorithms OperationsBase ${Qwt_LIBRARIES} OSMesaDummy Brain ${FTGL_LIBRARIES} Files Annotations Charting Palette Cifti Gifti Nifti FilesBase Scenes Xml Common ${QUAZIP_LIBRARIES} ${FREETYPE_LIBRARIES} ${QT_LIBRARIES} ${ZLIB_LIBRARIES} #${LIBS} ) INSTALL(TARGETS ${EXE_NAME} DESTINATION bin) IF(WIN32) TARGET_LINK_LIBRARIES(${EXE_NAME} opengl32 glu32 ) ENDIF(WIN32) IF (UNIX) IF (NOT APPLE) TARGET_LINK_LIBRARIES(${EXE_NAME} ${OPENGL_LIBRARIES} gobject-2.0 ) ENDIF (NOT APPLE) # EXECUTE_PROCESS(COMMAND uname -n OUTPUT_VARIABLE MACHINE_NAME) # MESSAGE("MACHINE_NAME: ${MACHINE_NAME}") # IF (${MACHINE_NAME} MATCHES "linuxbuild") # MESSAGE("is linuxbuild") # SET_TARGET_PROPERTIES(${EXE_NAME} # PROPERTIES # LINK_FLAGS "-Wl,-E" # LINK_FLAGS_DEBUG "-Wl,-E" # LINK_FLAGS_RELEASE "-Wl,-E") # ENDIF() ENDIF (UNIX) # # At this time, Cocoa needs to be explicitly added for Apple Mac # IF (APPLE) #SET (QT_MAC_USE_COCOA TRUE) TARGET_LINK_LIBRARIES(${EXE_NAME} "-framework Cocoa" "-framework OpenGL" ) ENDIF (APPLE) # # Find Headers # INCLUDE_DIRECTORIES( ${CMAKE_SOURCE_DIR}/Desktop ${CMAKE_SOURCE_DIR}/Algorithms ${CMAKE_SOURCE_DIR}/Commands ${CMAKE_SOURCE_DIR}/GuiQt ${CMAKE_SOURCE_DIR}/Brain ${CMAKE_SOURCE_DIR}/FilesBase ${CMAKE_SOURCE_DIR}/Files ${CMAKE_SOURCE_DIR}/Palette ${CMAKE_SOURCE_DIR}/Cifti ${CMAKE_SOURCE_DIR}/Gifti ${CMAKE_SOURCE_DIR}/Nifti ${Qwt_INCLUDE_DIRS} ${CMAKE_SOURCE_DIR}/Scenes ${CMAKE_SOURCE_DIR}/Xml ${CMAKE_SOURCE_DIR}/Common ) # # Apple needs qt_menu.nib directory copied # into application's Resources directory # # Apple needs framework # IF (APPLE) MESSAGE("EXE: " ${CMAKE_BUILD_DIR} " " ${EXE_NAME}) ADD_CUSTOM_COMMAND( TARGET ${EXE_NAME} POST_BUILD COMMAND ${CMAKE_SOURCE_DIR}/CMakeScripts/copy_mac_nib.sh ${EXE_NAME} ###COMMAND ${CMAKE_SOURCE_DIR}/CMakeScripts/copy_mac_frameworks.sh ${EXE_NAME} ) ENDIF (APPLE) IF (APPLE) SET (MACOSX_BUNDLE_INFO_STRING wb_view Copyright 2015 ) SET (MACOSX_BUNDLE_ICON_FILE workbench.icns ) ##SET (MACOSX_BUNDLE_GUI_IDENTIFIER wb_view ) ## Underscore is not valid in MACOSX_BUNDLE_GUI_IDENTIFIER SET (MACOSX_BUNDLE_GUI_IDENTIFIER workbench ) SET (MACOSX_BUNDLE_LONG_VERSION_STRING wb_view) SET (MACOSX_BUNDLE_BUNDLE_NAME wb_view) SET (MACOSX_BUNDLE_SHORT_VERSION_STRING 1.1.1) SET (MACOSX_BUNDLE_BUNDLE_VERSION 1.1.1) SET (MACOSX_BUNDLE_COPYRIGHT 2015 ) ADD_CUSTOM_COMMAND( TARGET ${EXE_NAME} POST_BUILD COMMAND ${CMAKE_SOURCE_DIR}/CMakeScripts/copy_mac_icon.sh ${EXE_NAME} ${CMAKE_SOURCE_DIR}/../icons/mac/wb_view.icns COMMAND ${CMAKE_SOURCE_DIR}/CMakeScripts/copy_mac_icon.sh ${EXE_NAME} ${CMAKE_SOURCE_DIR}/../icons/mac/workbench.icns COMMAND ${CMAKE_SOURCE_DIR}/CMakeScripts/copy_mac_icon.sh ${EXE_NAME} ${CMAKE_SOURCE_DIR}/../icons/mac/spec_file.icns COMMAND ${CMAKE_SOURCE_DIR}/CMakeScripts/copy_mac_icon.sh ${EXE_NAME} ${CMAKE_SOURCE_DIR}/../icons/mac/data_file.icns ) ENDIF (APPLE) connectome-workbench-1.2.3+git41-gc4c6c90/src/Desktop/desktop.cxx000066400000000000000000000742021300200146000243570ustar00rootroot00000000000000/*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 #include #include "ApplicationInformation.h" #include "BrainBrowserWindow.h" #include "BrainOpenGLWidget.h" #include "CaretAssert.h" #include "CaretCommandLine.h" #include "CaretHttpManager.h" #include "CaretLogger.h" #include "CaretPreferences.h" #include "CommandOperationManager.h" #include "EventBrowserWindowNew.h" #include "EventManager.h" #include "FileInformation.h" #include "GuiManager.h" #include "MacApplication.h" #include "ProgramParameters.h" #include "SessionManager.h" #include "SplashScreen.h" #include "SystemUtilities.h" #include "WuQMessageBox.h" #include "WuQtUtilities.h" static bool caretLoggerIsValid = false; using namespace caret; using namespace std; /** * Handles message produced by Qt. */ static void messageHandlerForQt(QtMsgType type, const char* msg) { const AString backtrace = SystemUtilities::getBackTrace(); const AString message = (AString(msg) + "\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; } /* * 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; } 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; } } } //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 sceneFileName; AString sceneNameOrNumber; 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); 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; { /* * Handle uncaught exceptions */ SystemUtilities::setUnexpectedHandler(); /* * Create the session manager. */ SessionManager::createSessionManager(ApplicationTypeEnum::APPLICATION_TYPE_GRAPHICAL_USER_INTERFACE); caretLoggerIsValid = true; /* * Parameters for the program. */ ProgramParameters parameters(argc, argv); caret_global_commandLine_init(parameters); //begin parsing command line ProgramState myState; FileInformation progInfo(argv[0]); AString progName = progInfo.getFileName(); parseCommandLine(progName, ¶meters, myState); /* * Log the command parameters. */ CaretLogFine("Running: " + caret_global_commandLine); //change the default graphics system on mac to avoid rendering performance issues with qwtplotter #ifdef CARET_OS_MACOSX QApplication::setGraphicsSystem("raster"); MacApplication app(argc, argv); #else //CARET_OS_MACOSX QApplication app(argc, argv); #endif //CARET_OS_MACOSX ApplicationInformation applicationInformation; QApplication::addLibraryPath( QApplication::applicationDirPath() + QDir::separator() + "plugins"); QApplication::setApplicationName(applicationInformation.getName()); QApplication::setApplicationVersion(applicationInformation.getVersion()); QApplication::setOrganizationDomain("brainvis.wustl.edu"); QApplication::setOrganizationName("Van Essen Lab"); /* * Override the system local to US - English */ QLocale::setDefault(QLocale(QLocale::English, QLocale::UnitedStates)); /* * 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."; app.processEvents(); WuQMessageBox::errorOk(NULL, msg); app.processEvents(); return -1; } /* * Setup OpenGL */ BrainOpenGLWidget::initializeDefaultGLFormat(); qInstallMsgHandler(messageHandlerForQt);//this handler uses CaretLogger and GuiManager, so we must install it after the logger is available and the application is created /* * 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 * and command line options. Do not show selection splash screen * if there has listed files on the command line. */ 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) { showSelectionSplashScreen = false; showImageSplashScreen = false; } /* * DISABLE IMAGE SPLASH SCREEN */ showImageSplashScreen = false; /* * Splash Screen */ QPixmap splashPixmap; QSplashScreen splashScreen; if (showImageSplashScreen) { if (WuQtUtilities::loadPixmap(":/Splash/hcp.png", splashPixmap)) { splashScreen.setPixmap(splashPixmap); splashScreen.showMessage("Starting Workbench..."); splashScreen.show(); app.processEvents(); SystemUtilities::sleepSeconds(2); } } /* * Create the GUI Manager. */ GuiManager::createGuiManager(); /* * Letting the App process events will allow the message for a * double-clicked spec file in Mac OSX to get processed. */ app.processEvents(); /* * Now that events have processed, see if there was a request for * a data file to open */ const AString dataFileNameFromOS = GuiManager::get()->getNameOfDataFileToOpenAfterStartup(); if (dataFileNameFromOS.isEmpty() == false) { myState.fileList.push_back(dataFileNameFromOS); showSelectionSplashScreen = false; if (dataFileNameFromOS.endsWith(DataFileTypeEnum::toFileExtension(DataFileTypeEnum::SPECIFICATION))) { haveSpec = true; haveFiles = false; myState.specLoadType = 0; } else { haveSpec = false; haveFiles = true; } } /* * Show file selection splash screen if enabled via user's preferences */ if (showSelectionSplashScreen) { /* * Show selection splash screen. * Need to process events since QApplication::exec() has not * been called. */ SplashScreen splashScreen(NULL); app.processEvents(); if (splashScreen.exec()) { 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; } else { haveSpec = false; haveFiles = true; } } } } /* * Create and display a main window. * If not done as pointer, the event object is listed as an * object that was not deleted by CaretObject::printListOfObjectsNotDeleted * since it does not go out of scope. */ EventBrowserWindowNew newBrowserWindow(NULL, NULL); EventManager::get()->sendEvent(newBrowserWindow.getPointer()); splashScreen.close(); BrainBrowserWindow* myWindow = GuiManager::get()->getBrowserWindowByWindowIndex(0); if ((myState.windowSizeXY[0] > 0) && (myState.windowSizeXY[1] > 0)) { if (myState.windowPosXY[0] > 0 && myState.windowPosXY[1] > 0) { myWindow->setGeometry(myState.windowPosXY[0], myState.windowPosXY[1], myState.windowSizeXY[0], myState.windowSizeXY[1]); } else { myWindow->setFixedSize(myState.windowSizeXY[0], myState.windowSizeXY[1]); } } else { if (myState.windowPosXY[0] > 0 && myState.windowPosXY[1] > 0) { myState.windowSizeXY[0] = myWindow->width(); myState.windowSizeXY[1] = myWindow->height(); myWindow->setGeometry(myState.windowPosXY[0], myState.windowPosXY[1], myState.windowSizeXY[0], myState.windowSizeXY[1]); } } if (myState.graphicsSizeXY[0] > 0 && myState.graphicsSizeXY[1] > 0) { 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 (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); } } if (myState.sceneFileName.isEmpty() == false) { myWindow->loadSceneFromCommandLine(myState.sceneFileName, myState.sceneNameOrNumber); } if (QGLPixelBuffer::hasOpenGLPbuffers()) { CaretLogConfig("OpenGL PBuffers are supported"); } else { CaretLogConfig("OpenGL PBuffers are NOT supported"); } /* * Log local (language, country) */ QLocale sytemLocale = QLocale::system(); CaretLogConfig("Local Language=" + QLocale::languageToString(sytemLocale.language()) + " Country=" + QLocale::countryToString(sytemLocale.country())); /* * Resolution of screens */ AString screenSizeText = "Screen Sizes: "; QDesktopWidget* dw = QApplication::desktop(); const int numScreens = dw->screenCount(); for (int i = 0; i < numScreens; i++) { const QRect rect = dw->screenGeometry(i); const int x = rect.x(); const int y = rect.y(); const int w = rect.width(); const int h = rect.height(); screenSizeText.appendWithNewLine("Screen index=" + AString::number(i) + ", x=" + AString::number(x) + ", y=" + AString::number(y) + ", w=" + AString::number(w) + ", h=" + AString::number(h)); } screenSizeText.appendWithNewLine("Primary Screen=" + AString::number(dw->primaryScreen())); if (dw->isVirtualDesktop()) { screenSizeText.appendWithNewLine("Virtual Desktop=YES"); } else { screenSizeText.appendWithNewLine("Virtual Desktop=NO"); } QWidget* screenWidget = dw->screen(); QRect screenWidgetRect = screenWidget->geometry(); screenSizeText.appendWithNewLine("Desktop: x=" + AString::number(screenWidgetRect.x()) + ", y=" + AString::number(screenWidgetRect.y()) + ", w=" + AString::number(screenWidgetRect.width()) + ", h=" + AString::number(screenWidgetRect.height())); screenSizeText.appendWithNewLine("Logical DPI: x=" + AString::number(dw->logicalDpiX()) + ", y=" + AString::number(dw->logicalDpiY())); screenSizeText.appendWithNewLine("Physical DPI: x=" + AString::number(dw->physicalDpiX()) + ", y=" + AString::number(dw->physicalDpiY())); screenSizeText.appendWithNewLine("Width/height (mm): x=" + AString::number(dw->widthMM()) + ", y=" + AString::number(dw->heightMM())); CaretLogConfig(screenSizeText); /* * Start the app which will launch the main window. */ result = app.exec(); /* * Hiding the window removes it from the event loop on Windows, which is necessary to * prevent paint events from causing assertion errors when the Window is destroyed * Although this is a Window's only bug, it's probably good practice to do on all platforms */ //theMainWindow->hide(); /* * Delete the GUI Manager. */ GuiManager::deleteGuiManager(); /* * Delete the command manager */ CommandOperationManager::deleteCommandOperationManager(); /* * Delete the session manager. */ SessionManager::deleteSessionManager(); CaretHttpManager::deleteHttpManager(); } /* * See if any objects were not deleted. */ CaretObject::printListOfObjectsNotDeleted(true); //FreeConsole(); return result; } void printHelp(const AString& progName) { cout << "Usage: " << progName << " [options] [files]" << endl << endl << " [files], if present, can be a single spec file, or multiple data files" << endl << endl << "Options:" << endl << " -help" << endl << " display this usage text" << endl << endl << " -graphics-size " << endl << " Set the size of the graphics region." << endl << " If this option is used you WILL NOT be able" << endl << " to change the size of the graphic region. It" << endl << " may be useful when image captures of a particular" << endl << " size are desired." << endl << endl << " -logging " << endl << " Set the logging level." << endl << " Valid Levels are:" << endl; std::vector logLevels; LogLevelEnum::getAllEnums(logLevels); for (std::vector::iterator iter = logLevels.begin(); iter != logLevels.end(); 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 << " -no-splash" << endl << " disable all splash screens" << endl << endl << " -scene-load " << endl << " load the specified scene file and display the scene " << endl << " in the file that matches by name or number. Name" << endl << " takes precedence over number. The scene numbers " << endl << " start at one." << endl << " " << endl << endl << " -style " << endl << " change the window style to the specified style" << endl << " the following styles are valid on this system:" << endl; QStringList styleList = QStyleFactory::keys(); QStringListIterator styleListIterator(styleList); while (styleListIterator.hasNext()) { cout << " " << qPrintable(styleListIterator.next()) << endl; } cout << " The selected style is listed on the About wb_view dialog" << endl << " available from the File Menu (On Macs: wb_view Menu). " << endl << " Press the \"More\" button to see the selected style." << endl << " Other styles may be available on other systems." << endl << endl << " -spec-load-all" << endl << " load all files in the given spec file, don't show spec file dialog" << endl << endl << " -window-size " << endl << " Set the size of the browser window" << endl << endl << " -window-pos " << endl << " Set the position of the browser window" << endl << endl; } void parseCommandLine(const AString& progName, ProgramParameters* myParams, ProgramState& myState) { bool hasFatalError = false; try { while (myParams->hasNext()) { AString thisParam = myParams->nextString("option"); if (thisParam[0] == '-') { if (thisParam == "-style") { myParams->nextString("style");//discard, QApplication handles this } else if (thisParam == "-help") { printHelp(progName); exit(0); } else if (thisParam == "-logging") { if (myParams->hasNext()) { const AString logLevelName = myParams->nextString("Logging Level").toUpper(); bool valid = false; const LogLevelEnum::Enum level = LogLevelEnum::fromName(logLevelName, &valid); if (valid) { /* * Note settings logging level in preferences will also * set logging level in the caret logger. */ CaretPreferences* prefs = SessionManager::get()->getCaretPreferences(); prefs->setLoggingLevel(level); } else { cerr << "Invalid logging level \"" << qPrintable(logLevelName) << "\" for \"-logging\" option" << std::endl; hasFatalError = true; } } } else if (thisParam == "-no-splash") { myState.showSplash = false; } else if (thisParam == "-scene-load") { if (myParams->hasNext()) { myState.sceneFileName = myParams->nextString("Scene File Name"); if (myParams->hasNext()) { myState.sceneNameOrNumber = myParams->nextString("Scene Name or Number"); } else { cerr << "Missing scene name/number for \"-scene\" option" << std::endl; hasFatalError = true; } } else { cerr << "Missing scene file name for \"-scene\" option" << std::endl; hasFatalError = true; } } else if (thisParam == "-spec-load-all") { myState.specLoadType = 1; } else if (thisParam == "-graphics-size") { if (myParams->hasNext()) { myState.graphicsSizeXY[0] = myParams->nextInt("Graphics Size X"); } else { cerr << "Missing X & Y sizes for graphics" << endl; hasFatalError = true; } if (myParams->hasNext()) { myState.graphicsSizeXY[1] = myParams->nextInt("Graphics Size Y"); } else { cerr << "Missing Y sizes for graphics" << endl; hasFatalError = true; } } else if (thisParam == "-window-size") { if (myParams->hasNext()) { myState.windowSizeXY[0] = myParams->nextInt("Window Size X"); } else { cerr << "Missing X & Y sizes for window" << endl; hasFatalError = true; } if (myParams->hasNext()) { myState.windowSizeXY[1] = myParams->nextInt("Window Size Y"); } else { cerr << "Missing Y sizes for window" << endl; hasFatalError = true; } } else if (thisParam == "-window-pos") { if (myParams->hasNext()) { myState.windowPosXY[0] = myParams->nextInt("Window Position X"); } else { cerr << "Missing X & Y position for window" << endl; hasFatalError = true; } if (myParams->hasNext()) { myState.windowPosXY[1] = myParams->nextInt("Window Position Y"); } else { cerr << "Missing Y position for window" << endl; hasFatalError = true; } } else if (thisParam.startsWith("-psn")) { /* * 21 April 2014 (Did not have this problem before this date) * * IGNORE this parameter. For some reason, when a Mac * version is started from Finder, a "-psn" parameter * is being added to the parameters. If this parameter * is not ignored, Workbench starts, the icon bounces * a few times, and then Workbench quits (due to * "unrecognized option", below), and the user is * not given any error message. * * http://stackoverflow.com/questions/10242115/os-x-strange-psn-command-line-parameter-when-launched-from-finder * http://trac.wxwidgets.org/ticket/15432 */ } else { cerr << "unrecognized option \"" << thisParam << "\"" << endl; printHelp(progName); hasFatalError = true; } } else { myState.fileList.push_back(thisParam); } } } catch (const ProgramParametersException& e) { cerr << e.whatString() << std::endl; hasFatalError = true; } if (hasFatalError) { exit(-1); } } ProgramState::ProgramState() { sceneFileName = ""; sceneNameOrNumber = ""; specLoadType = 0;//0: use spec window, 1: all windowSizeXY[0] = -1; windowSizeXY[1] = -1; windowPosXY[0] = -1; windowPosXY[1] = -1; graphicsSizeXY[0] = -1; graphicsSizeXY[1] = -1; showSplash = true; } connectome-workbench-1.2.3+git41-gc4c6c90/src/Files/000077500000000000000000000000001300200146000216065ustar00rootroot00000000000000connectome-workbench-1.2.3+git41-gc4c6c90/src/Files/AffineFile.cxx000066400000000000000000000107361300200146000243310ustar00rootroot00000000000000/*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 "AffineFile.h" #include "CaretAssert.h" #include "DataFileException.h" #include "FileInformation.h" #include "NiftiIO.h" #include #include using namespace caret; using namespace std; AffineFile::AffineFile() { m_matrix = FloatMatrix::identity(4);//use 4x4 convention for in-memory } void AffineFile::setMatrix(const FloatMatrix& matrix) { int64_t inRows, inCols; matrix.getDimensions(inRows, inCols); CaretAssert(inCols == 4 && inRows > 2 && inRows < 5); m_matrix = FloatMatrix::identity(4);//set the final 0 0 0 1 row m_matrix[0] = matrix[0];//copy ONLY the rows that should change m_matrix[1] = matrix[1]; m_matrix[2] = matrix[2]; } FloatMatrix AffineFile::read34(const AString& filename) { FileInformation affineInfo(filename); if (!affineInfo.exists()) throw DataFileException("affine file '" + filename + "' does not exist"); fstream affineFile(filename.toLocal8Bit().constData(), fstream::in); if (!affineFile.good()) throw DataFileException("error opening file '" + filename + "' for reading"); FloatMatrix ret = FloatMatrix::identity(4);//to ensure the right size and the fourth 0 0 0 1 row for (int i = 0; i < 3; ++i)//DO NOT read the fourth row from the file into the matrix { for (int j = 0; j < 4; ++j) { affineFile >> ret[i][j]; if (!affineFile) throw DataFileException("error while reading file '" + filename + "'"); } } return ret; } void AffineFile::write44(const FloatMatrix& out, const AString& filename) { fstream affineFile(filename.toLocal8Bit().constData(), fstream::out); if (!affineFile.good()) { throw DataFileException("error opening file '" + filename + "' for writing"); } affineFile.setf(ios::fixed, ios::floatfield); affineFile.precision(10);//flirt appears to use 10, so do the same for (int i = 0; i < 4; ++i) { for (int j = 0; j < 4; ++j) { affineFile << out[i][j]; if (j < 3) affineFile << " ";//double space like flirt if (!affineFile) throw DataFileException("error while writing file '" + filename + "'"); } affineFile << endl; } } void AffineFile::readWorld(const AString& filename) { m_matrix = read34(filename);//and that is it, no quirks } void AffineFile::writeWorld(const AString& filename) { write44(m_matrix, filename);//ditto } void AffineFile::readFlirt(const AString& filename, const AString& sourceName, const AString& targetName) { FloatMatrix flirtMat = read34(filename); FloatMatrix sourceMat, sourceScale, targetMat, targetScale; getFSLQuirks(sourceName, sourceMat, sourceScale); getFSLQuirks(targetName, targetMat, targetScale); //via aff_conv : world = targmat * trgscale^-1 * input * srcscale * sourcemat^-1 m_matrix = targetMat * targetScale.inverse() * flirtMat * sourceScale * sourceMat.inverse(); } void AffineFile::writeFlirt(const AString& filename, const AString& sourceName, const AString& targetName) const { FloatMatrix sourceMat, sourceScale, targetMat, targetScale; getFSLQuirks(sourceName, sourceMat, sourceScale); getFSLQuirks(targetName, targetMat, targetScale); FloatMatrix flirtMat = targetScale * targetMat.inverse() * m_matrix * sourceMat * sourceScale.inverse(); write44(flirtMat, filename); } void AffineFile::getFSLQuirks(const AString& niftiName, FloatMatrix& outSform, FloatMatrix& outScale) { NiftiIO myIO; myIO.openRead(niftiName); 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()); } connectome-workbench-1.2.3+git41-gc4c6c90/src/Files/AffineFile.h000066400000000000000000000037701300200146000237560ustar00rootroot00000000000000#ifndef __AFFINE_FILE_H__ #define __AFFINE_FILE_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 "AString.h" #include "FloatMatrix.h" namespace caret { class AffineFile { FloatMatrix m_matrix; static FloatMatrix read34(const AString& filename);//helper to read a simple text affine static void write44(const FloatMatrix& out, const AString& filename);//helper for writing 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 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 setMatrix(const FloatMatrix& matrix);//needs to do sanity checking, so don't inline }; } #endif //__AFFINE_FILE_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Files/AnnotationFile.cxx000066400000000000000000001707331300200146000252570ustar00rootroot00000000000000 /*LICENSE_START*/ /* * Copyright (C) 2015 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 __ANNOTATION_FILE_DECLARE__ #include "AnnotationFile.h" #undef __ANNOTATION_FILE_DECLARE__ #include "AnnotationBox.h" #include "AnnotationCoordinate.h" #include "AnnotationFileXmlReader.h" #include "AnnotationFileXmlWriter.h" #include "AnnotationGroup.h" #include "AnnotationLine.h" #include "AnnotationOval.h" #include "AnnotationPercentSizeText.h" #include "AnnotationPointSizeText.h" #include "BrainConstants.h" #include "CaretAssert.h" #include "CaretColorEnum.h" #include "CaretLogger.h" #include "DataFileContentCopyMoveParameters.h" #include "DataFileException.h" #include "DisplayGroupAndTabItemHelper.h" #include "EventAnnotationAddToRemoveFromFile.h" #include "EventAnnotationGroupGetWithKey.h" #include "EventAnnotationGrouping.h" #include "EventManager.h" #include "GiftiMetaData.h" #include "SceneClass.h" #include "SceneClassAssistant.h" using namespace caret; /** * \class caret::AnnotationFile * \brief File containing annotations. * \ingroup Files */ /** * Constructor for annotation file that saves annotations to a file. */ AnnotationFile::AnnotationFile() : CaretDataFile(DataFileTypeEnum::ANNOTATION), EventListenerInterface(), DataFileContentCopyMoveInterface(), DisplayGroupAndTabItemInterface(), m_fileSubType(ANNOTATION_FILE_SAVE_TO_FILE) { initializeAnnotationFile(); } /** * Constructor for annotation file that accepts a sub file type * so that annotations may be saved to a file or a scene. * * This method is intended for use by the "Brain" for its * scene annotation file. * * @param fileSubType * Type of saving of annotations. */ AnnotationFile::AnnotationFile(const AnnotationFileSubType fileSubType) : CaretDataFile(DataFileTypeEnum::ANNOTATION), m_fileSubType(fileSubType) { initializeAnnotationFile(); } /** * Destructor. */ AnnotationFile::~AnnotationFile() { clearPrivate(); EventManager::get()->removeAllEventsFromListener(this); delete m_displayGroupAndTabItemHelper; delete m_sceneAssistant; } /** * Clear the content of this file. * This method is virtual so do not call from constructor/destructor. */ void AnnotationFile::clear() { const AString nameOfFile = getFileName(); CaretDataFile::clear(); clearPrivate(); switch (m_fileSubType) { case ANNOTATION_FILE_SAVE_TO_FILE: break; case ANNOTATION_FILE_SAVE_TO_SCENE: { /* * Do not clear the name of the scene annotation file. */ const bool modStatus = isModified(); setFileName(nameOfFile); if ( ! modStatus) { clearModified(); } } break; } } /** * Clear the content of this file. */ void AnnotationFile::clearPrivate() { m_metadata->clear(); m_annotationGroups.clear(); m_removedAnnotations.clear(); } /** * Set the selection for editing status of all annotations. * * @param windowIndex * Index of window for annotation selection. * @param selectedStatus * New selection status for all annotations. */ void AnnotationFile::setAllAnnotationsSelectedForEditing(const int32_t windowIndex, const bool selectedStatus) { std::vector allAnnotations; getAllAnnotations(allAnnotations); for (std::vector::iterator iter = allAnnotations.begin(); iter != allAnnotations.end(); iter++) { (*iter)->setSelectedForEditing(windowIndex, selectedStatus); } } /** * Copy constructor. * @param obj * Object that is copied. */ AnnotationFile::AnnotationFile(const AnnotationFile& obj) : CaretDataFile(obj), EventListenerInterface(), DataFileContentCopyMoveInterface(), DisplayGroupAndTabItemInterface(obj), m_fileSubType(obj.m_fileSubType) { initializeAnnotationFile(); this->copyHelperAnnotationFile(obj); } /** * Assignment operator. * @param obj * Data copied from obj to this. * @return * Reference to this object. */ AnnotationFile& AnnotationFile::operator=(const AnnotationFile& obj) { if (this != &obj) { CaretDataFile::operator=(obj); this->copyHelperAnnotationFile(obj); } return *this; } /** * Initialize an instance of an annotation file. */ void AnnotationFile::initializeAnnotationFile() { m_uniqueKeyGenerator = 0; m_metadata.grabNew(new GiftiMetaData()); m_displayGroupAndTabItemHelper = new DisplayGroupAndTabItemHelper(); m_sceneAssistant = new SceneClassAssistant(); m_sceneAssistant->add("m_displayGroupAndTabItemHelper", "DisplayGroupAndTabItemHelper", m_displayGroupAndTabItemHelper); 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); } /** * Get the coordinate space annotation group with the given annotation's space and for tab/window * spaces, the tab/window index. If the group does not exist, it will be created. * * @param annotation * Annotation whose group is needed. * @return * Group for the coordinateSpace. */ AnnotationGroup* AnnotationFile::getSpaceAnnotationGroup(const Annotation* annotation) { const AnnotationCoordinateSpaceEnum::Enum annotationSpace = annotation->getCoordinateSpace(); int32_t annotationTabOrWindowIndex = -1; switch (annotationSpace) { case AnnotationCoordinateSpaceEnum::PIXELS: break; case AnnotationCoordinateSpaceEnum::STEREOTAXIC: break; case AnnotationCoordinateSpaceEnum::SURFACE: break; case AnnotationCoordinateSpaceEnum::TAB: annotationTabOrWindowIndex = annotation->getTabIndex(); break; case AnnotationCoordinateSpaceEnum::WINDOW: annotationTabOrWindowIndex = annotation->getWindowIndex(); break; } for (AnnotationGroupIterator groupIter = m_annotationGroups.begin(); groupIter != m_annotationGroups.end(); groupIter++) { AnnotationGroup* group = (*groupIter).data(); if (group->getGroupType() == AnnotationGroupTypeEnum::SPACE) { if (group->getCoordinateSpace() == annotationSpace) { switch (annotationSpace) { case AnnotationCoordinateSpaceEnum::PIXELS: CaretAssert(0); break; case AnnotationCoordinateSpaceEnum::STEREOTAXIC: case AnnotationCoordinateSpaceEnum::SURFACE: return group; break; case AnnotationCoordinateSpaceEnum::TAB: case AnnotationCoordinateSpaceEnum::WINDOW: if (annotationTabOrWindowIndex == group->getTabOrWindowIndex()) { return group; } break; } } } } switch (annotationSpace) { case AnnotationCoordinateSpaceEnum::PIXELS: CaretAssert(0); break; case AnnotationCoordinateSpaceEnum::STEREOTAXIC: case AnnotationCoordinateSpaceEnum::SURFACE: break; case AnnotationCoordinateSpaceEnum::TAB: CaretAssert((annotationTabOrWindowIndex >= 0) && (annotationTabOrWindowIndex < BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS)); break; case AnnotationCoordinateSpaceEnum::WINDOW: CaretAssert((annotationTabOrWindowIndex >= 0) && (annotationTabOrWindowIndex < BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_WINDOWS)); break; } AnnotationGroup* group = new AnnotationGroup(this, AnnotationGroupTypeEnum::SPACE, generateUniqueKey(), annotationSpace, annotationTabOrWindowIndex); group->setItemParent(this); m_annotationGroups.push_back(QSharedPointer(group)); return group; } /** * Helps with copying an object of this type. * @param obj * Object that is copied. */ void AnnotationFile::copyHelperAnnotationFile(const AnnotationFile& obj) { CaretAssertMessage(0, "Copying of annotation file not implemented. " "Will need to check subtype or have a 'clone' method' that each " "subclass (AnnotationText) implements."); *m_displayGroupAndTabItemHelper = *obj.m_displayGroupAndTabItemHelper; } /** * Receive an event. * * @param event * An event for which this instance is listening. */ void AnnotationFile::receiveEvent(Event* event) { if (event->getEventType() == EventTypeEnum::EVENT_ANNOTATION_ADD_TO_REMOVE_FROM_FILE) { EventAnnotationAddToRemoveFromFile* annEvent = dynamic_cast(event); CaretAssert(annEvent); AnnotationFile* annotationFile = annEvent->getAnnotationFile(); Annotation* annotation = annEvent->getAnnotation(); switch (annEvent->getMode()) { case EventAnnotationAddToRemoveFromFile::MODE_CREATE: if (annotationFile == this) { if (restoreAnnotationAddIfNotFound(annotation)) { annEvent->setSuccessful(true); } } break; case EventAnnotationAddToRemoveFromFile::MODE_CUT: if (removeAnnotation(annotation)) { annEvent->setSuccessful(true); } break; case EventAnnotationAddToRemoveFromFile::MODE_DELETE: if (removeAnnotation(annotation)) { annEvent->setSuccessful(true); } break; case EventAnnotationAddToRemoveFromFile::MODE_PASTE: if (annotationFile == this) { if (restoreAnnotationAddIfNotFound(annotation)) { annEvent->setSuccessful(true); } } break; case EventAnnotationAddToRemoveFromFile::MODE_UNCREATE: if (annotationFile == this) { if (removeAnnotation(annotation)) { annEvent->setSuccessful(true); } } break; case EventAnnotationAddToRemoveFromFile::MODE_UNCUT: if (restoreAnnotation(annotation)) { annEvent->setSuccessful(true); } break; case EventAnnotationAddToRemoveFromFile::MODE_UNDELETE: if (restoreAnnotation(annotation)) { annEvent->setSuccessful(true); } break; case EventAnnotationAddToRemoveFromFile::MODE_UNPASTE: if (annotationFile == this) { if (removeAnnotation(annotation)) { annEvent->setSuccessful(true); } } } } else if (event->getEventType() == EventTypeEnum::EVENT_ANNOTATION_GROUP_GET_WITH_KEY) { EventAnnotationGroupGetWithKey* getGroupEvent = dynamic_cast(event); CaretAssert(getGroupEvent); const AnnotationGroupKey groupKey = getGroupEvent->getGroupKey(); for (AnnotationGroupIterator groupIter = m_annotationGroups.begin(); groupIter != m_annotationGroups.end(); groupIter++) { AnnotationGroup* group = (*groupIter).data(); if (groupKey == group->getAnnotationGroupKey()) { getGroupEvent->setAnnotationGroup(group); getGroupEvent->setEventProcessed(); return; } } } else if (event->getEventType() == EventTypeEnum::EVENT_ANNOTATION_GROUPING) { EventAnnotationGrouping* groupEvent = dynamic_cast(event); CaretAssert(event); if (groupEvent->getAnnotationGroupKey().getAnnotationFile() == this) { switch (groupEvent->getMode()) { case EventAnnotationGrouping::MODE_INVALID: break; case EventAnnotationGrouping::MODE_GROUP: processGroupingAnnotations(groupEvent); break; case EventAnnotationGrouping::MODE_REGROUP: processRegroupingAnnotations(groupEvent); break; case EventAnnotationGrouping::MODE_UNGROUP: processUngroupingAnnotations(groupEvent); break; } } } } /** * @return True if this file is empty, else false. */ bool AnnotationFile::isEmpty() const { for (AnnotationGroupConstIterator groupIter = m_annotationGroups.begin(); groupIter != m_annotationGroups.end(); groupIter++) { if ( ! (*groupIter)->isEmpty()) { return false; } } return true; } /** * @return The structure for this file. */ StructureEnum::Enum AnnotationFile::getStructure() const { return StructureEnum::ALL; } /** * Set the structure for this file. * @param structure * New structure for this file. */ void AnnotationFile::setStructure(const StructureEnum::Enum /*structure*/) { /* nothing */ } /** * @return Get access to the file's metadata. */ GiftiMetaData* AnnotationFile::getFileMetaData() { return m_metadata; } /** * @return Get access to unmodifiable file's metadata. */ const GiftiMetaData* AnnotationFile::getFileMetaData() const { return m_metadata; } /** * Add information about the content of this file. * * @param dataFileInformation * Will contain information about this file. */ void AnnotationFile::addToDataFileContentInformation(DataFileContentInformation& dataFileInformation) { CaretDataFile::addToDataFileContentInformation(dataFileInformation); } /** * Private method for adding annotations to this file. * * In the GUI, annotations are added using the AnnotationRedoUndoCommand * which allows undo/redo operations. * * @param annotation * Annotation that is added. * @param uniqueKey * Unique key for the annotation. */ void AnnotationFile::addAnnotationPrivate(Annotation* annotation, const int32_t uniqueKey) { if (annotation->getType() == AnnotationTypeEnum::TEXT) { AnnotationPointSizeText* pointSizeAnnotation = dynamic_cast(annotation); if (pointSizeAnnotation != NULL) { CaretLogWarning("Point size text annotations are not supported in AnnotationFile. " "The annotation has been discarded."); delete annotation; return; } } CaretAssert(uniqueKey > 0); if (uniqueKey <= 0) { CaretLogSevere("invalid key less than zero."); } AnnotationGroup* group = getSpaceAnnotationGroup(annotation); CaretAssert(group); annotation->setUniqueKey(uniqueKey); group->addAnnotationPrivate(annotation); setModified(); } void AnnotationFile::addAnnotationPrivateSharedPointer(QSharedPointer& annotation, const int32_t uniqueKey) { if (annotation->getType() == AnnotationTypeEnum::TEXT) { AnnotationPointSizeText* pointSizeAnnotation = dynamic_cast(annotation.data()); if (pointSizeAnnotation != NULL) { CaretLogWarning("Point size text annotations are not supported in AnnotationFile. " "The annotation has been discarded."); return; } } CaretAssert(uniqueKey > 0); if (uniqueKey <= 0) { CaretLogSevere("invalid key less than zero."); } AnnotationGroup* group = getSpaceAnnotationGroup(annotation.data()); CaretAssert(group); annotation->setUniqueKey(uniqueKey); group->addAnnotationPrivateSharedPointer(annotation); setModified(); } /** * Add an annotation to this file while the file is being read. * File will take ownership of the annotation. * * In the GUI, annotations are added using the AnnotationRedoUndoCommand * which allows undo/redo operations. * * @param annotation * Annotation that is added. */ void AnnotationFile::addAnnotationDuringFileVersionOneReading(Annotation* annotation) { addAnnotationPrivate(annotation, generateUniqueKey()); } /** * Add a group while reading an annotation file. * * @param groupType * Type of annotation group. * @param coordinateSpace * Coordinate space of the group's annotaitons. * @param tabOrWindowIndex * Tab or window index for groups in tab or window space. * @param uniqueKey * Unique key for the annotation group. * @param annotations * Annotation that are members of the group. * @throw DataFileException * If there is an error. */ void AnnotationFile::addAnnotationGroupDuringFileReading(const AnnotationGroupTypeEnum::Enum groupType, const AnnotationCoordinateSpaceEnum::Enum coordinateSpace, const int32_t tabOrWindowIndex, const int32_t uniqueKey, const std::vector& annotations) { switch (groupType) { case AnnotationGroupTypeEnum::INVALID: throw DataFileException("INVALID group type is not allowed while annotation file."); break; case AnnotationGroupTypeEnum::SPACE: break; case AnnotationGroupTypeEnum::USER: break; } switch (coordinateSpace) { case AnnotationCoordinateSpaceEnum::PIXELS: throw DataFileException("PIXELS coordinate space is not allowed for group while annotation file."); break; case AnnotationCoordinateSpaceEnum::STEREOTAXIC: break; case AnnotationCoordinateSpaceEnum::SURFACE: break; case AnnotationCoordinateSpaceEnum::TAB: case AnnotationCoordinateSpaceEnum::WINDOW: if ((tabOrWindowIndex < 0) || (tabOrWindowIndex >= BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS)) { throw DataFileException("Invalid tab/window index for group while reading annotation file: " + QString::number(tabOrWindowIndex)); } break; } if (uniqueKey <= 0) { throw DataFileException("Invalid unique key for group while reading annotation file: " + AString::number(uniqueKey)); } for (AnnotationGroupIterator groupIter = m_annotationGroups.begin(); groupIter != m_annotationGroups.end(); groupIter++) { QSharedPointer group = *groupIter; if (group->getUniqueKey() == uniqueKey) { throw DataFileException("More than one group using unique key " + AString::number(uniqueKey) + " while reading annotation file."); } /* * For default groups (NOT user groups), there may not be * more than one group of each type. * * There is: * - One group for stereotaxic * - One group for surface * - One group for each Tab * - One group for each Window */ if (group->getGroupType() == groupType) { if (groupType == AnnotationGroupTypeEnum::SPACE) { if (group->getCoordinateSpace() == coordinateSpace) { switch (coordinateSpace) { case AnnotationCoordinateSpaceEnum::PIXELS: CaretAssert(0); break; case AnnotationCoordinateSpaceEnum::STEREOTAXIC: case AnnotationCoordinateSpaceEnum::SURFACE: throw DataFileException("There is more than one annotation space group with space " + AnnotationCoordinateSpaceEnum::toGuiName(coordinateSpace) + ". Only one space group for each space is allowed."); break; case AnnotationCoordinateSpaceEnum::TAB: if (tabOrWindowIndex == group->getTabOrWindowIndex()) { throw DataFileException("There is more than one annotation space group with space " + AnnotationCoordinateSpaceEnum::toGuiName(coordinateSpace) + " for tab " + AString::number(tabOrWindowIndex) + ". Only one space group for each space is allowed."); } break; case AnnotationCoordinateSpaceEnum::WINDOW: if (tabOrWindowIndex == group->getTabOrWindowIndex()) { throw DataFileException("There is more than one annotation space group with space " + AnnotationCoordinateSpaceEnum::toGuiName(coordinateSpace) + " for window " + AString::number(tabOrWindowIndex) + ". Only one space group for each space is allowed."); } break; } } } } } AnnotationGroup* group = new AnnotationGroup(this, groupType, uniqueKey, coordinateSpace, tabOrWindowIndex); for (std::vector::const_iterator annIter = annotations.begin(); annIter != annotations.end(); annIter++) { group->addAnnotationPrivate(*annIter); } group->setItemParent(this); m_annotationGroups.push_back(QSharedPointer(group)); } /** * Restore an annotation that had been removed and possibly * add if it was not restored (probably creating a new * annotation). * * @param annotation * Annotation that is restored. * @return * True if the annotation was restored or added, * otherwise false. */ bool AnnotationFile::restoreAnnotationAddIfNotFound(Annotation* annotation) { if (restoreAnnotation(annotation)) { return true; } addAnnotationPrivate(annotation, generateUniqueKey()); return true; } /** * Restore an annotation that had been removed. * * @param annotation * Annotation that is restored. * @return * True if the annotation was restored otherwise false. */ bool AnnotationFile::restoreAnnotation(Annotation* annotation) { for (std::set >::iterator iter = m_removedAnnotations.begin(); iter != m_removedAnnotations.end(); iter++) { QSharedPointer annotationPointer = *iter; if (annotationPointer.data() == annotation) { addAnnotationPrivateSharedPointer(annotationPointer, annotationPointer->getUniqueKey()); m_removedAnnotations.erase(iter); setModified(); /* * Successfully restored */ return true; } } return false; } /** * Remove the annotation. 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. * @return * True if the annotation was removed, otherwise false. */ bool AnnotationFile::removeAnnotation(Annotation* annotation) { 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; } } // 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 */ return false; } /** * Get all annotations in this file. * * @param annotationsOut * Output containing all annotations. */ void AnnotationFile::getAllAnnotations(std::vector& annotationsOut) const { annotationsOut.clear(); for (AnnotationGroupConstIterator groupIter = m_annotationGroups.begin(); groupIter != m_annotationGroups.end(); groupIter++) { std::vector groupAnnotations; (*groupIter)->getAllAnnotations(groupAnnotations); annotationsOut.insert(annotationsOut.end(), groupAnnotations.begin(), groupAnnotations.end()); } } /** * Get all annotation groups in this file. * * @param annotationGroupsOut * Output containing all annotation groups. */ void AnnotationFile::getAllAnnotationGroups(std::vector& annotationGroupsOut) const { annotationGroupsOut.clear(); for (AnnotationGroupConstIterator groupIter = m_annotationGroups.begin(); groupIter != m_annotationGroups.end(); groupIter++) { annotationGroupsOut.push_back((*groupIter).data()); } } /** * Group annotations. * * @param groupingEvent * The grouping event. */ void AnnotationFile::processGroupingAnnotations(EventAnnotationGrouping* groupingEvent) { CaretAssert(groupingEvent); AnnotationGroupKey spaceGroupKey = groupingEvent->getAnnotationGroupKey(); std::vector annotationsToGroup = groupingEvent->getAnnotations(); if (annotationsToGroup.size() < 2) { groupingEvent->setErrorMessage("PROGRAM ERROR: Trying to group annotations less than two annotations"); CaretAssert(0); return; } AnnotationGroupIterator spaceGroupIter = m_annotationGroups.end(); for (spaceGroupIter = m_annotationGroups.begin(); spaceGroupIter != m_annotationGroups.end(); spaceGroupIter++) { AnnotationGroup* group = (*spaceGroupIter).data(); if (group->getAnnotationGroupKey().getGroupType() == AnnotationGroupTypeEnum::SPACE) { if (group->containsAllAnnotation(annotationsToGroup)) { break; } } } if (spaceGroupIter == m_annotationGroups.end()) { groupingEvent->setErrorMessage("Did not find annotations in a space group. This may occur when " "annotations have been ungrouped, an annotation is modified that moves " "it to a different group or the annotation is deleted, and there " "is an attempt to regroup the annotations."); return; } AnnotationGroup* spaceGroup = (*spaceGroupIter).data(); CaretAssert(spaceGroup); // AnnotationGroupKey spaceGroupKey = groupingEvent->getAnnotationGroupKey(); // // AnnotationGroup* spaceGroup = NULL; // AnnotationGroupIterator spaceGroupIter = m_annotationGroups.end(); // for (spaceGroupIter = m_annotationGroups.begin(); // spaceGroupIter != m_annotationGroups.end(); // spaceGroupIter++) { // QSharedPointer groupPointer = *spaceGroupIter; // if (spaceGroupKey == groupPointer->getAnnotationGroupKey()) { // spaceGroup = groupPointer.data(); // break; // } // } // // if (spaceGroup == NULL) { // groupingEvent->setErrorMessage("PROGRAM ERROR: Did not find space group for source of grouping annotations"); // return; // } groupingEvent->setEventProcessed(); if (spaceGroup->getGroupType() != AnnotationGroupTypeEnum::SPACE) { groupingEvent->setErrorMessage("PROGRAM ERROR: Trying to group annotations in a NON-space group"); CaretAssert(0); return; } bool allValidFlag = true; std::vector > movedAnnotations; for (std::vector::iterator annIter = annotationsToGroup.begin(); annIter != annotationsToGroup.end(); annIter++) { QSharedPointer annPtr; if (spaceGroup->removeAnnotation(*annIter, annPtr)) { movedAnnotations.push_back(annPtr); } else { allValidFlag = false; break; } } 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; } AnnotationGroup* group = new AnnotationGroup(this, AnnotationGroupTypeEnum::USER, generateUniqueKey(), spaceGroup->getCoordinateSpace(), spaceGroup->getTabOrWindowIndex()); for (std::vector >::iterator annPtrIter = movedAnnotations.begin(); annPtrIter != movedAnnotations.end(); annPtrIter++) { group->addAnnotationPrivateSharedPointer(*annPtrIter); } group->setItemParent(this); groupingEvent->setGroupKeyToWhichAnnotationsWereMoved(group->getAnnotationGroupKey()); /* * If space group becomes empty, remove it. * Need to remove it before adding new group, otherwise * iterator will become invalid */ if (spaceGroup->isEmpty()) { m_annotationGroups.erase(spaceGroupIter); } /* * Add new user group */ m_annotationGroups.push_back(QSharedPointer(group)); setModified(); } /** * Group annotations. * * @param groupingEvent * The grouping event. */ void AnnotationFile::processUngroupingAnnotations(EventAnnotationGrouping* groupingEvent) { CaretAssert(groupingEvent); AnnotationGroupKey userGroupKey = groupingEvent->getAnnotationGroupKey(); AnnotationGroupIterator userGroupIter = m_annotationGroups.end(); for (userGroupIter = m_annotationGroups.begin(); userGroupIter != m_annotationGroups.end(); userGroupIter++) { if (userGroupKey == (*userGroupIter)->getAnnotationGroupKey()) { break; } } if (userGroupIter == m_annotationGroups.end()) { groupingEvent->setErrorMessage("PROGRAM ERROR: Did not find group for ungrouping annotations"); return; } AnnotationGroup* userGroup = (*userGroupIter).data(); CaretAssert(userGroup); if (userGroup->getGroupType() != AnnotationGroupTypeEnum::USER) { groupingEvent->setErrorMessage("PROGRAM ERROR: Trying to ungroup annotations in a NON-user group"); CaretAssert(0); return; } std::vector > allGroupAnnotations; userGroup->removeAllAnnotations(allGroupAnnotations); /** * Remove the group since it is empty. Must be done before adding annotations back to * space group since the space group may not exist and if a new * space group is created the iterator will be invalid. */ m_annotationGroups.erase(userGroupIter); for (std::vector >::iterator annIter = allGroupAnnotations.begin(); annIter != allGroupAnnotations.end(); annIter++) { QSharedPointer annPtr = *annIter; AnnotationGroup* spaceGroup = getSpaceAnnotationGroup(annPtr.data()); spaceGroup->addAnnotationPrivateSharedPointer(annPtr); } groupingEvent->setEventProcessed(); setModified(); } /** * Group annotations. * * @param groupingEvent * The grouping event. */ void AnnotationFile::processRegroupingAnnotations(EventAnnotationGrouping* groupingEvent) { CaretAssert(groupingEvent); /* * Unique key of group that annotations were once a member of */ const int32_t userGroupUniqueKey = groupingEvent->getAnnotationGroupKey().getUserGroupUniqueKey(); CaretAssert(userGroupUniqueKey > 0); std::vector annotations; std::set groups; /* * Find annotations in ONE space group that were * previously assigned to the previous user group. */ //AnnotationGroupIterator spaceGroupIter = m_annotationGroups.end(); for (AnnotationGroupIterator spaceGroupIter = m_annotationGroups.begin(); spaceGroupIter != m_annotationGroups.end(); spaceGroupIter++) { AnnotationGroup* group = (*spaceGroupIter).data(); switch (group->getGroupType()) { case AnnotationGroupTypeEnum::INVALID: break; case AnnotationGroupTypeEnum::SPACE: { std::vector groupAnnotations; group->getAllAnnotations(groupAnnotations); for (std::vector::iterator annIter = groupAnnotations.begin(); annIter != groupAnnotations.end(); annIter++) { Annotation* ann = *annIter; if (ann->getAnnotationGroupKey().getUserGroupUniqueKey() == userGroupUniqueKey) { groups.insert(group); annotations.push_back(ann); } } } break; case AnnotationGroupTypeEnum::USER: break; } } const int32_t windowIndex = groupingEvent->getWindowIndex(); /* * Annotations must be in one space group (same space!) */ if (groups.size() == 1) { AnnotationGroup* spaceGroup = *(groups.begin()); const int32_t numAnn = static_cast(annotations.size()); if (numAnn > 1) { AnnotationGroup* group = new AnnotationGroup(this, AnnotationGroupTypeEnum::USER, reuseUniqueKeyOrGenerateNewUniqueKey(userGroupUniqueKey), spaceGroup->getCoordinateSpace(), spaceGroup->getTabOrWindowIndex()); bool allValidFlag = true; std::vector > movedAnnotations; for (std::vector::iterator annIter = annotations.begin(); annIter != annotations.end(); annIter++) { QSharedPointer annPtr; if (spaceGroup->removeAnnotation(*annIter, annPtr)) { movedAnnotations.push_back(annPtr); } else { allValidFlag = false; break; } } for (std::vector >::iterator annPtrIter = movedAnnotations.begin(); annPtrIter != movedAnnotations.end(); annPtrIter++) { if ((windowIndex >= 0) && (windowIndex < BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_WINDOWS)) { (*annPtrIter)->setSelectedForEditing(windowIndex, true); } group->addAnnotationPrivateSharedPointer(*annPtrIter); } group->setItemParent(this); m_annotationGroups.push_back(QSharedPointer(group)); /* * If the space group is empty it is no longer needed * so delete it. */ if (spaceGroup->isEmpty()) { for (AnnotationGroupIterator spaceGroupIter = m_annotationGroups.begin(); spaceGroupIter != m_annotationGroups.end(); spaceGroupIter++) { if (spaceGroup == (*spaceGroupIter).data()) { m_annotationGroups.erase(spaceGroupIter); break; } } } groupingEvent->setGroupKeyToWhichAnnotationsWereMoved(group->getAnnotationGroupKey()); setModified(); } else { groupingEvent->setErrorMessage("ERROR: Unable to regroup annotations due to only one annotation for regrouping."); } } else { groupingEvent->setErrorMessage("ERROR: Annotations that were in a previous group are no longer " "in the same space group."); } } /** * Clear drawn in window status for all annotations. */ void AnnotationFile::clearAllAnnotationsDrawnInWindowStatus() { std::vector allAnnotations; getAllAnnotations(allAnnotations); for (std::vector::iterator annIter = allAnnotations.begin(); annIter != allAnnotations.end(); annIter++) { (*annIter)->clearDrawnInWindowStatusForAllWindows(); } } /** * Get annotations drawin in the given window. * * @param windowIndex * Index of the window. * @param annotationsOut * Output containing annotations with draw in window status set for * the given window index. */ void AnnotationFile::getAllAnnotationWithDrawnInWindowStatusSet(const int32_t windowIndex, std::vector& annotationsOut) { std::vector allAnnotations; getAllAnnotations(allAnnotations); for (std::vector::iterator annIter = allAnnotations.begin(); annIter != allAnnotations.end(); annIter++) { if ((*annIter)->isDrawnInWindowStatus(windowIndex)) { annotationsOut.push_back(*annIter); } } } /** * @return true if file is modified, else false. */ bool AnnotationFile::isModified() const { if (CaretDataFile::isModified()) { return true; } if (m_metadata->isModified()) { return true; } for (AnnotationGroupConstIterator iter = m_annotationGroups.begin(); iter != m_annotationGroups.end(); iter++) { if ((*iter)->isModified()) { return true; } } return false; } /** * Clear the modified status of this file. */ void AnnotationFile::clearModified() { CaretDataFile::clearModified(); m_metadata->clearModified(); for (AnnotationGroupIterator groupIter = m_annotationGroups.begin(); groupIter != m_annotationGroups.end(); groupIter++) { (*groupIter)->clearModified(); } } /** * Reuse the given unique key (if it is not used in a group or annotation * and is less than the current value of the unique key generator. Otherwise, * generate a new unique key. * * @param reuseUniqueKey * Key that is requested and tested to see if it can be used. * @return * The input key if it can be reused, otherwise, a new key. */ int32_t AnnotationFile::reuseUniqueKeyOrGenerateNewUniqueKey(const int32_t reuseUniqueKey) { bool canBeReusedFlag = true; /* * Search the groups and the annotations within the groups to * see if the desired unique key is already used. */ for (AnnotationGroupIterator groupIter = m_annotationGroups.begin(); groupIter != m_annotationGroups.end(); groupIter++) { AnnotationGroup* group = (*groupIter).data(); if (group->getUniqueKey() == reuseUniqueKey) { canBeReusedFlag = false; break; } else { std::vector groupAnnotations; group->getAllAnnotations(groupAnnotations); for (std::vector::iterator annIter = groupAnnotations.begin(); annIter != groupAnnotations.end(); annIter++) { if ((*annIter)->getUniqueKey() == reuseUniqueKey) { canBeReusedFlag = false; break; } } } } /* * Check annotations that were deleted since they can be undeleted */ if (canBeReusedFlag) { for (std::set >::iterator removedAnnIter = m_removedAnnotations.begin(); removedAnnIter != m_removedAnnotations.end(); removedAnnIter++) { if ((*removedAnnIter)->getUniqueKey() == reuseUniqueKey) { canBeReusedFlag = false; } } } int32_t outputUniqueKey = -1; if (canBeReusedFlag) { outputUniqueKey = reuseUniqueKey; } else { outputUniqueKey = generateUniqueKey(); } return outputUniqueKey; } /** * @return A new unique key (each annotation and * annotation group in the file contains a unique key. */ int32_t AnnotationFile::generateUniqueKey() { if (m_uniqueKeyGenerator < 0) { m_uniqueKeyGenerator = 0; } m_uniqueKeyGenerator++; return m_uniqueKeyGenerator; } /** * Update the unqiue keys after reading the file * Older files did not have unique keys */ void AnnotationFile::updateUniqueKeysAfterReadingFile() { /* * Find maximum unique identifier from annotations AND annotation groups */ int32_t maximumKeyFound = 0; for (AnnotationGroupIterator groupIter = m_annotationGroups.begin(); groupIter != m_annotationGroups.end(); groupIter++) { maximumKeyFound = std::max(maximumKeyFound, (*groupIter)->getMaximumUniqueKey()); } m_uniqueKeyGenerator = maximumKeyFound; } /** * Read the data file. * * @param filename * Name of the data file. * @throws DataFileException * If the file was not successfully read. */ void AnnotationFile::readFile(const AString& filename) { clear(); checkFileReadability(filename); AnnotationFileXmlReader reader; reader.readFile(filename, this); updateUniqueKeysAfterReadingFile(); setFileName(filename); clearModified(); } /** * Write the data file. * * @param filename * Name of the data file. * @throws DataFileException * If the file was not successfully written. */ void AnnotationFile::writeFile(const AString& filename) { if (!(filename.endsWith(".annot") || filename.endsWith(".wb_annot"))) { CaretLogWarning("annotation file '" + filename + "' should be saved ending in .annot"); } checkFileWritability(filename); setFileName(filename); AnnotationFileXmlWriter writer; writer.writeFile(this); clearModified(); } /** * 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 AnnotationFile::saveFileDataToScene(const SceneAttributes* sceneAttributes, SceneClass* sceneClass) { m_sceneAssistant->saveMembers(sceneAttributes, sceneClass); switch (m_fileSubType) { case ANNOTATION_FILE_SAVE_TO_FILE: break; case ANNOTATION_FILE_SAVE_TO_SCENE: if ( ! isEmpty()) { try { AnnotationFileXmlWriter writer; QString fileContentInString; writer.writeFileToString(this, fileContentInString); sceneClass->addString("AnnotationFileContent", fileContentInString); } catch (const DataFileException& dfe) { sceneAttributes->addToErrorMessage(dfe.whatString()); } } break; } /* * Save groups to scene */ for (AnnotationGroupIterator groupIter = m_annotationGroups.begin(); groupIter != m_annotationGroups.end(); groupIter++) { AnnotationGroup* group = (*groupIter).data(); const int32_t uniqueKey = group->getUniqueKey(); SceneClass* groupClass = group->saveToScene(sceneAttributes, AnnotationGroup::getSceneClassNameForAnnotationUniqueKey(uniqueKey)); sceneClass->addClass(groupClass); } } /** * 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 AnnotationFile::restoreFileDataFromScene(const SceneAttributes* sceneAttributes, const SceneClass* sceneClass) { m_sceneAssistant->restoreMembers(sceneAttributes, sceneClass); switch (m_fileSubType) { case ANNOTATION_FILE_SAVE_TO_FILE: break; case ANNOTATION_FILE_SAVE_TO_SCENE: QString fileContentInString = sceneClass->getStringValue("AnnotationFileContent"); if ( ! fileContentInString.isEmpty()) { try { AnnotationFileXmlReader reader; reader.readFileFromString(fileContentInString, this); updateUniqueKeysAfterReadingFile(); clearModified(); } catch (const DataFileException& dfe) { sceneAttributes->addToErrorMessage(dfe.whatString()); } } break; } /* * Restore groups from scene */ for (AnnotationGroupIterator groupIter = m_annotationGroups.begin(); groupIter != m_annotationGroups.end(); groupIter++) { AnnotationGroup* group = (*groupIter).data(); const int32_t uniqueKey = group->getUniqueKey(); const SceneClass* annClass = sceneClass->getClass(AnnotationGroup::getSceneClassNameForAnnotationUniqueKey(uniqueKey)); if (sceneClass != NULL) { group->restoreFromScene(sceneAttributes, annClass); } } } /** * @return Pointer to DataFile that implements this interface */ DataFile* AnnotationFile::getAsDataFile() { DataFile* dataFile = dynamic_cast(this); CaretAssert(this); return dataFile; } /** * Append content from the given data file copy/move interface to this instance * * @param copyMoveParameters * Parameters used for copy/move. * @throws DataFileException * If there is an error. */ void AnnotationFile::appendContentFromDataFile(const DataFileContentCopyMoveParameters& copyMoveParameters) { const AnnotationFile* copyFromFile = dynamic_cast(copyMoveParameters.getDataFileCopyMoveInterfaceToCopy()); if (copyFromFile == NULL) { throw DataFileException("Trying to copy content to annotation file from a file that is not an " "annotation file."); } const bool selectedAnnotationsFlag = copyMoveParameters.isOptionSelectedItems(); const int32_t windowIndex = copyMoveParameters.getWindowIndex(); if (selectedAnnotationsFlag) { if (windowIndex < 0) { throw DataFileException("Requested copying of selected annotations but window index is invalid."); } } std::vector annotationGroups; copyFromFile->getAllAnnotationGroups(annotationGroups); for (std::vector::iterator groupIter = annotationGroups.begin(); groupIter != annotationGroups.end(); groupIter++) { const AnnotationGroup* groupToCopy = *groupIter; CaretAssert(groupToCopy); /* * Find that annotations that are to be copied. */ const int32_t numAnnInGroup = groupToCopy->getNumberOfAnnotations(); std::vector annotationsToCopy; for (int32_t ia = 0; ia < numAnnInGroup; ia++) { const Annotation* annToCopy = groupToCopy->getAnnotation(ia); CaretAssert(annToCopy); bool copyFlag = true; if (selectedAnnotationsFlag) { if (annToCopy->isSelectedForEditing(windowIndex)) { /* Nothing */ } else { copyFlag = false; } } if (copyFlag) { annotationsToCopy.push_back(annToCopy); } } /* * If there are are annotations to copy, * clone them and add to the proper group. */ if ( ! annotationsToCopy.empty()) { AnnotationGroup* group = NULL; switch (groupToCopy->getGroupType()) { case AnnotationGroupTypeEnum::INVALID: break; case AnnotationGroupTypeEnum::SPACE: /* * All annotations in a group are in identical spaces * so use the first annotation to find the space group. */ group = getSpaceAnnotationGroup(groupToCopy->getAnnotation(0)); break; case AnnotationGroupTypeEnum::USER: /* * Create a new user group */ group = new AnnotationGroup(this, AnnotationGroupTypeEnum::USER, generateUniqueKey(), groupToCopy->getCoordinateSpace(), groupToCopy->getTabOrWindowIndex()); group->setItemParent(this); m_annotationGroups.push_back(QSharedPointer(group)); break; } CaretAssert(group); /* * Copy annotations and add them to the group. */ for (std::vector::const_iterator iter = annotationsToCopy.begin(); iter != annotationsToCopy.end(); iter++) { const Annotation* annToCopy = *iter; Annotation* clonedAnn = annToCopy->clone(); clonedAnn->setUniqueKey(generateUniqueKey()); group->addAnnotationPrivate(clonedAnn); } } } } /** * @return A new instance of the same file type. File is empty. */ DataFileContentCopyMoveInterface* AnnotationFile::newInstanceOfDataFile() const { return new AnnotationFile(); } /** * @return Number of children. */ int32_t AnnotationFile::getNumberOfItemChildren() const { return m_annotationGroups.size(); } /** * Get child at the given index. * * @param index * Index of the child. * @return * Child at the given index. */ DisplayGroupAndTabItemInterface* AnnotationFile::getItemChild(const int32_t index) const { CaretAssertVectorIndex(m_annotationGroups, index); return m_annotationGroups[index].data(); } /** * @return Children of this item. */ std::vector AnnotationFile::getItemChildren() const { std::vector children; for (AnnotationGroupConstIterator groupIter = m_annotationGroups.begin(); groupIter != m_annotationGroups.end(); groupIter++) { children.push_back((*groupIter).data()); } return children; } /** * @return Parent of this item. */ DisplayGroupAndTabItemInterface* AnnotationFile::getItemParent() const { return m_displayGroupAndTabItemHelper->getParent(); } /** * Set the parent of this item. * * @param itemParent * Parent of this item. */ void AnnotationFile::setItemParent(DisplayGroupAndTabItemInterface* itemParent) { m_displayGroupAndTabItemHelper->setParent(itemParent); } /** * @return Name of this item. */ AString AnnotationFile::getItemName() const { return getFileNameNoPath(); } /** * Get the icon color for this item. Icon is filled with background * color, outline color is drawn around edges, and text color is small * square in center. For any colors that do not apply, use an alpha * value (last element) of zero. * * @param backgroundRgbaOut * Red, green, blue, alpha components for background ranging [0, 1]. * @param outlineRgbaOut * Red, green, blue, alpha components for outline ranging [0, 1]. * @param textRgbaOut * Red, green, blue, alpha components for text ranging [0, 1]. */ void AnnotationFile::getItemIconColorsRGBA(float backgroundRgbaOut[4], float outlineRgbaOut[4], float textRgbaOut[4]) const { backgroundRgbaOut[0] = 0.0; backgroundRgbaOut[1] = 0.0; backgroundRgbaOut[2] = 0.0; backgroundRgbaOut[3] = 0.0; outlineRgbaOut[0] = 0.0; outlineRgbaOut[1] = 0.0; outlineRgbaOut[2] = 0.0; outlineRgbaOut[3] = 0.0; textRgbaOut[0] = 0.0; textRgbaOut[1] = 0.0; textRgbaOut[2] = 0.0; textRgbaOut[3] = 0.0; } /** * @return This item can be expanded. */ bool AnnotationFile::isItemExpandable() const { return true; } /** * @return Is this item expanded in the given display group/tab? * * @param displayGroup * The display group. * @param tabIndex * Index of the tab. */ bool AnnotationFile::isItemExpanded(const DisplayGroupEnum::Enum displayGroup, const int32_t tabIndex) const { return m_displayGroupAndTabItemHelper->isExpanded(displayGroup, tabIndex); } /** * Set this item's expanded status in the given display group/tab. * * @param displayGroup * The display group. * @param tabIndex * Index of the tab. * @param status * New expanded status. */ void AnnotationFile::setItemExpanded(const DisplayGroupEnum::Enum displayGroup, const int32_t tabIndex, const bool status) { m_displayGroupAndTabItemHelper->setExpanded(displayGroup, tabIndex, status); } /** * Get display selection status in the given display group/tab? * * @param displayGroup * The display group. * @param tabIndex * Index of the tab. */ TriStateSelectionStatusEnum::Enum AnnotationFile::getItemDisplaySelected(const DisplayGroupEnum::Enum displayGroup, const int32_t tabIndex) const { TriStateSelectionStatusEnum::Enum status = TriStateSelectionStatusEnum::UNSELECTED; const int numChildren = static_cast(m_annotationGroups.size()); if (numChildren > 0) { int32_t selectedCount = 0; int32_t partialSelectedCount = 0; for (int32_t i = 0; i < numChildren; i++) { CaretAssertVectorIndex(m_annotationGroups, i); switch (m_annotationGroups[i]->getItemDisplaySelected(displayGroup, tabIndex)) { case TriStateSelectionStatusEnum::PARTIALLY_SELECTED: partialSelectedCount++; break; case TriStateSelectionStatusEnum::SELECTED: selectedCount++; break; case TriStateSelectionStatusEnum::UNSELECTED: break; } } if (selectedCount == numChildren) { status = TriStateSelectionStatusEnum::SELECTED; } else if ((selectedCount > 0) || (partialSelectedCount > 0)) { status = TriStateSelectionStatusEnum::PARTIALLY_SELECTED; } } return status; } /** * Set display this item selected in the given display group/tab. * * @param displayGroup * The display group. * @param tabIndex * Index of the tab. * @param status * New selection status. */ void AnnotationFile::setItemDisplaySelected(const DisplayGroupEnum::Enum displayGroup, const int32_t tabIndex, const TriStateSelectionStatusEnum::Enum status) { // m_displayGroupAndTabItemHelper->setSelected(displayGroup, // tabIndex, // status); /* * Note: An annotation file's selection status is based * of the the file's annotation groups so we do not need to set * an explicit selection status for the file. */ DisplayGroupAndTabItemInterface::setChildrenDisplaySelectedHelper(this, displayGroup, tabIndex, status); DisplayGroupAndTabItemInterface::setChildrenDisplaySelectedHelper(this, displayGroup, tabIndex, status); } /** * Is this item selected for editing in the given window? * * @param windowIndex * Index of the window. * @return * Selection status. */ bool AnnotationFile::isItemSelectedForEditingInWindow(const int32_t /*windowIndex*/) { /* * The annotation file is never selected for editing. */ return false; } connectome-workbench-1.2.3+git41-gc4c6c90/src/Files/AnnotationFile.h000066400000000000000000000215051300200146000246740ustar00rootroot00000000000000#ifndef __ANNOTATION_FILE_H__ #define __ANNOTATION_FILE_H__ /*LICENSE_START*/ /* * Copyright (C) 2015 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 "AnnotationCoordinateSpaceEnum.h" #include "AnnotationGroupTypeEnum.h" #include "CaretDataFile.h" #include "CaretPointer.h" #include "DataFileContentCopyMoveInterface.h" #include "DisplayGroupAndTabItemInterface.h" #include "EventAnnotationGrouping.h" #include "EventListenerInterface.h" namespace caret { class Annotation; class AnnotationGroup; class DisplayGroupAndTabItemHelper; class SceneClassAssistant; class AnnotationFile : public CaretDataFile, public EventListenerInterface, public DataFileContentCopyMoveInterface, public DisplayGroupAndTabItemInterface { public: /** * Sub-type of the annotation file */ enum AnnotationFileSubType { /** * Normal annotation file which saves its annotation * to a file */ ANNOTATION_FILE_SAVE_TO_FILE, /** * Special variant of annotation file that restores and saves the * annotations with the scene methods. This is used by the * "Brain" for the scene annotation file that is only saved * to scenes and never saved to a file. */ ANNOTATION_FILE_SAVE_TO_SCENE }; AnnotationFile(); AnnotationFile(const AnnotationFileSubType fileSubType); virtual ~AnnotationFile(); AnnotationFile(const AnnotationFile& obj); AnnotationFile& operator=(const AnnotationFile& obj); bool isEmpty() const; virtual void clear(); virtual StructureEnum::Enum getStructure() const; virtual void setStructure(const StructureEnum::Enum structure); virtual GiftiMetaData* getFileMetaData(); virtual const GiftiMetaData* getFileMetaData() const; void getAllAnnotations(std::vector& annotationsOut) const; void getAllAnnotationGroups(std::vector& annotationGroupsOut) const; void clearAllAnnotationsDrawnInWindowStatus(); void getAllAnnotationWithDrawnInWindowStatusSet(const int32_t windowIndex, std::vector& annotationsOut); virtual void addToDataFileContentInformation(DataFileContentInformation& dataFileInformation); void setAllAnnotationsSelectedForEditing(const int32_t windowIndex, const bool selectedStatus); virtual void receiveEvent(Event* event); virtual void readFile(const AString& filename); virtual void writeFile(const AString& filename); virtual bool isModified() const; virtual void clearModified(); virtual DataFile* getAsDataFile(); virtual void appendContentFromDataFile(const DataFileContentCopyMoveParameters& copyMoveParameters); virtual DataFileContentCopyMoveInterface* newInstanceOfDataFile() const; // ADD_NEW_METHODS_HERE virtual int32_t getNumberOfItemChildren() const; virtual DisplayGroupAndTabItemInterface* getItemChild(const int32_t index) const; virtual std::vector getItemChildren() const; virtual DisplayGroupAndTabItemInterface* getItemParent() const; virtual void setItemParent(DisplayGroupAndTabItemInterface* itemParent); virtual AString getItemName() const; virtual void getItemIconColorsRGBA(float backgroundRgbaOut[4], float outlineRgbaOut[4], float textRgbaOut[4]) const; virtual bool isItemExpandable() const; virtual bool isItemExpanded(const DisplayGroupEnum::Enum displayGroup, const int32_t tabIndex) const; virtual void setItemExpanded(const DisplayGroupEnum::Enum displayGroup, const int32_t tabIndex, const bool status); virtual TriStateSelectionStatusEnum::Enum getItemDisplaySelected(const DisplayGroupEnum::Enum displayGroup, const int32_t tabIndex) const; virtual void setItemDisplaySelected(const DisplayGroupEnum::Enum displayGroup, const int32_t tabIndex, const TriStateSelectionStatusEnum::Enum status); virtual bool isItemSelectedForEditingInWindow(const int32_t windowIndex); protected: virtual void saveFileDataToScene(const SceneAttributes* sceneAttributes, SceneClass* sceneClass); virtual void restoreFileDataFromScene(const SceneAttributes* sceneAttributes, const SceneClass* sceneClass); private: void copyHelperAnnotationFile(const AnnotationFile& obj); void clearPrivate(); void initializeAnnotationFile(); void addAnnotationPrivate(Annotation* annotation, const int32_t uniqueKey); void addAnnotationPrivateSharedPointer(QSharedPointer& annotation, const int32_t uniqueKey); void addAnnotationDuringFileVersionOneReading(Annotation* annotation); void addAnnotationGroupDuringFileReading(const AnnotationGroupTypeEnum::Enum groupType, const AnnotationCoordinateSpaceEnum::Enum coordinateSpace, const int32_t tabOrWindowIndex, const int32_t uniqueKey, const std::vector& annotations); void processGroupingAnnotations(EventAnnotationGrouping* groupingEvent); void processUngroupingAnnotations(EventAnnotationGrouping* groupingEvent); void processRegroupingAnnotations(EventAnnotationGrouping* groupingEvent); bool restoreAnnotation(Annotation* annotation); bool restoreAnnotationAddIfNotFound(Annotation* annotation); bool removeAnnotation(Annotation* annotation); int32_t generateUniqueKey(); int32_t reuseUniqueKeyOrGenerateNewUniqueKey(const int32_t reuseUniqueKey); void updateUniqueKeysAfterReadingFile(); AnnotationGroup* getSpaceAnnotationGroup(const Annotation* annotation); const AnnotationFileSubType m_fileSubType; SceneClassAssistant* m_sceneAssistant; CaretPointer m_metadata; DisplayGroupAndTabItemHelper* m_displayGroupAndTabItemHelper; int32_t m_uniqueKeyGenerator; std::vector > m_annotationGroups; /** * Contains annotation that have been delete/removed so that * they can be 'undeleted' or 're-pasted'. */ std::set > m_removedAnnotations; typedef std::vector >::iterator AnnotationGroupIterator; typedef std::vector >::const_iterator AnnotationGroupConstIterator; // ADD_NEW_MEMBERS_HERE friend class AnnotationFileXmlReader; }; #ifdef __ANNOTATION_FILE_DECLARE__ // #endif // __ANNOTATION_FILE_DECLARE__ } // namespace #endif //__ANNOTATION_FILE_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Files/AnnotationFileXmlFormatBase.cxx000066400000000000000000000032761300200146000277010ustar00rootroot00000000000000 /*LICENSE_START*/ /* * Copyright (C) 2015 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 __ANNOTATION_FILE_XML_FORMAT_BASE_DECLARE__ #include "AnnotationFileXmlFormatBase.h" #undef __ANNOTATION_FILE_XML_FORMAT_BASE_DECLARE__ #include "CaretAssert.h" using namespace caret; /** * \class caret::AnnotationFileXmlFormatBase * \brief Base class for Annotation File XML reader/writer. * \ingroup Files * * This class is the base class for the Annotation File XML format * reader and writer. It contains XML tags used by the reader and * writer. */ /** * Constructor. */ AnnotationFileXmlFormatBase::AnnotationFileXmlFormatBase() : CaretObject() { } /** * Destructor. */ AnnotationFileXmlFormatBase::~AnnotationFileXmlFormatBase() { } /** * Get a description of this object's content. * @return String describing this object's content. */ AString AnnotationFileXmlFormatBase::toString() const { return "AnnotationFileXmlFormatBase"; } connectome-workbench-1.2.3+git41-gc4c6c90/src/Files/AnnotationFileXmlFormatBase.h000066400000000000000000000242401300200146000273200ustar00rootroot00000000000000#ifndef __ANNOTATION_FILE_XML_FORMAT_BASE_H__ #define __ANNOTATION_FILE_XML_FORMAT_BASE_H__ /*LICENSE_START*/ /* * Copyright (C) 2015 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" namespace caret { class AnnotationFileXmlFormatBase : public CaretObject { protected: AnnotationFileXmlFormatBase(); public: virtual ~AnnotationFileXmlFormatBase(); // ADD_NEW_METHODS_HERE virtual AString toString() const; private: AnnotationFileXmlFormatBase(const AnnotationFileXmlFormatBase&); AnnotationFileXmlFormatBase& operator=(const AnnotationFileXmlFormatBase&); protected: static const QString ATTRIBUTE_BACKGROUND_CARET_COLOR; static const QString ATTRIBUTE_BACKGROUND_CUSTOM_RGBA; static const QString ATTRIBUTE_COORDINATE_SPACE; static const QString ATTRIBUTE_COORD_X; static const QString ATTRIBUTE_COORD_Y; static const QString ATTRIBUTE_COORD_Z; static const QString ATTRIBUTE_COORD_SURFACE_STRUCTURE; static const QString ATTRIBUTE_COORD_SURFACE_NUMBER_OF_NODES; static const QString ATTRIBUTE_COORD_SURFACE_NODE_INDEX; static const QString ATTRIBUTE_COORD_SURFACE_NODE_OFFSET; static const QString ATTRIBUTE_COORD_SURFACE_NODE_OFFSET_VECTOR_TYPE; static const QString ATTRIBUTE_FOREGROUND_CARET_COLOR; static const QString ATTRIBUTE_FOREGROUND_CUSTOM_RGBA; static const QString ATTRIBUTE_FOREGROUND_LINE_WIDTH; static const QString ATTRIBUTE_GROUP_TYPE; static const QString ATTRIBUTE_HEIGHT; static const QString ATTRIBUTE_IMAGE_WIDTH; static const QString ATTRIBUTE_IMAGE_HEIGHT; static const QString ATTRIBUTE_LINE_END_ARROW; static const QString ATTRIBUTE_LINE_START_ARROW; static const QString ATTRIBUTE_ROTATION_ANGLE; static const QString ATTRIBUTE_TAB_INDEX; static const QString ATTRIBUTE_TAB_OR_WINDOW_INDEX; static const QString ATTRIBUTE_TEXT_CARET_COLOR; static const QString ATTRIBUTE_TEXT_CUSTOM_RGBA; static const QString ATTRIBUTE_TEXT_CONNECT_BRAINORDINATE; static const QString ATTRIBUTE_TEXT_FONT_BOLD; static const QString ATTRIBUTE_TEXT_FONT_ITALIC; static const QString ATTRIBUTE_TEXT_FONT_NAME; static const QString ATTRIBUTE_TEXT_FONT_POINT_SIZE; static const QString ATTRIBUTE_TEXT_FONT_PERCENT_VIEWPORT_SIZE; static const QString ATTRIBUTE_TEXT_FONT_UNDERLINE; static const QString ATTRIBUTE_TEXT_HORIZONTAL_ALIGNMENT; static const QString ATTRIBUTE_TEXT_ORIENTATION; static const QString ATTRIBUTE_TEXT_VERTICAL_ALIGNMENT; static const QString ATTRIBUTE_UNIQUE_KEY; static const QString ATTRIBUTE_VERSION; static const QString ATTRIBUTE_WIDTH; static const QString ATTRIBUTE_WINDOW_INDEX; static const QString ELEMENT_ANNOTATION_FILE; static const QString ELEMENT_ARROW; static const QString ELEMENT_BOX; static const QString ELEMENT_COORDINATE_ONE; static const QString ELEMENT_COORDINATE_TWO; static const QString ELEMENT_GROUP; static const QString ELEMENT_IMAGE; static const QString ELEMENT_IMAGE_RGBA_BYTES_IN_BASE64; static const QString ELEMENT_LINE; static const QString ELEMENT_OVAL; static const QString ELEMENT_PERCENT_SIZE_TEXT; static const QString ELEMENT_POINT_SIZE_TEXT; static const QString ELEMENT_TEXT_OBSOLETE; static const QString ELEMENT_TEXT_DATA; static const int32_t XML_VERSION_ONE; static const int32_t XML_VERSION_TWO; // ADD_NEW_MEMBERS_HERE }; #ifdef __ANNOTATION_FILE_XML_FORMAT_BASE_DECLARE__ const QString AnnotationFileXmlFormatBase::ATTRIBUTE_BACKGROUND_CARET_COLOR = "backgroundCaretColor"; const QString AnnotationFileXmlFormatBase::ATTRIBUTE_BACKGROUND_CUSTOM_RGBA = "backgroundCustomRGBA"; const QString AnnotationFileXmlFormatBase::ATTRIBUTE_COORDINATE_SPACE = "coordinateSpace"; const QString AnnotationFileXmlFormatBase::ATTRIBUTE_COORD_X = "x"; const QString AnnotationFileXmlFormatBase::ATTRIBUTE_COORD_Y = "y"; const QString AnnotationFileXmlFormatBase::ATTRIBUTE_COORD_Z = "z"; const QString AnnotationFileXmlFormatBase::ATTRIBUTE_COORD_SURFACE_STRUCTURE = "structure"; const QString AnnotationFileXmlFormatBase::ATTRIBUTE_COORD_SURFACE_NUMBER_OF_NODES = "numberOfNodes"; const QString AnnotationFileXmlFormatBase::ATTRIBUTE_COORD_SURFACE_NODE_INDEX = "nodeIndex"; const QString AnnotationFileXmlFormatBase::ATTRIBUTE_COORD_SURFACE_NODE_OFFSET = "nodeOffset"; const QString AnnotationFileXmlFormatBase::ATTRIBUTE_COORD_SURFACE_NODE_OFFSET_VECTOR_TYPE = "nodeOffsetVectorType"; const QString AnnotationFileXmlFormatBase::ATTRIBUTE_FOREGROUND_CARET_COLOR = "foregroundCaretColor"; const QString AnnotationFileXmlFormatBase::ATTRIBUTE_FOREGROUND_CUSTOM_RGBA = "foregroundCustomRGBA"; const QString AnnotationFileXmlFormatBase::ATTRIBUTE_FOREGROUND_LINE_WIDTH = "foregroundLineWidth"; const QString AnnotationFileXmlFormatBase::ATTRIBUTE_GROUP_TYPE = "groupType"; const QString AnnotationFileXmlFormatBase::ATTRIBUTE_HEIGHT = "height"; const QString AnnotationFileXmlFormatBase::ATTRIBUTE_IMAGE_WIDTH = "imageWidth"; const QString AnnotationFileXmlFormatBase::ATTRIBUTE_IMAGE_HEIGHT = "imageHeight"; const QString AnnotationFileXmlFormatBase::ATTRIBUTE_LINE_END_ARROW = "endArrow"; const QString AnnotationFileXmlFormatBase::ATTRIBUTE_LINE_START_ARROW = "startArrow"; const QString AnnotationFileXmlFormatBase::ATTRIBUTE_ROTATION_ANGLE = "rotationAngle"; const QString AnnotationFileXmlFormatBase::ATTRIBUTE_TAB_INDEX = "tabIndex"; const QString AnnotationFileXmlFormatBase::ATTRIBUTE_TAB_OR_WINDOW_INDEX = "tabOrWindowIndex"; const QString AnnotationFileXmlFormatBase::ATTRIBUTE_TEXT_CARET_COLOR = "textCaretColor"; const QString AnnotationFileXmlFormatBase::ATTRIBUTE_TEXT_CUSTOM_RGBA = "textCustomRGBA"; const QString AnnotationFileXmlFormatBase::ATTRIBUTE_TEXT_CONNECT_BRAINORDINATE = "connectBrainordinate"; const QString AnnotationFileXmlFormatBase::ATTRIBUTE_TEXT_FONT_BOLD = "fontBold"; const QString AnnotationFileXmlFormatBase::ATTRIBUTE_TEXT_FONT_ITALIC = "fontItalic"; const QString AnnotationFileXmlFormatBase::ATTRIBUTE_TEXT_FONT_NAME = "fontName"; const QString AnnotationFileXmlFormatBase::ATTRIBUTE_TEXT_FONT_POINT_SIZE = "fontPointSize"; const QString AnnotationFileXmlFormatBase::ATTRIBUTE_TEXT_FONT_PERCENT_VIEWPORT_SIZE = "fontPercentViewportSize"; const QString AnnotationFileXmlFormatBase::ATTRIBUTE_TEXT_FONT_UNDERLINE = "fontUnderline"; const QString AnnotationFileXmlFormatBase::ATTRIBUTE_TEXT_HORIZONTAL_ALIGNMENT = "horizontalAlignment"; const QString AnnotationFileXmlFormatBase::ATTRIBUTE_TEXT_ORIENTATION = "orientation"; const QString AnnotationFileXmlFormatBase::ATTRIBUTE_TEXT_VERTICAL_ALIGNMENT = "verticalAlignment"; const QString AnnotationFileXmlFormatBase::ATTRIBUTE_UNIQUE_KEY = "uniqueKey"; const QString AnnotationFileXmlFormatBase::ATTRIBUTE_VERSION = "version"; const QString AnnotationFileXmlFormatBase::ATTRIBUTE_WIDTH = "width"; const QString AnnotationFileXmlFormatBase::ATTRIBUTE_WINDOW_INDEX = "windowIndex"; const QString AnnotationFileXmlFormatBase::ELEMENT_ANNOTATION_FILE = "AnnotationFile"; const QString AnnotationFileXmlFormatBase::ELEMENT_ARROW = "arrow"; const QString AnnotationFileXmlFormatBase::ELEMENT_BOX = "box"; const QString AnnotationFileXmlFormatBase::ELEMENT_COORDINATE_ONE = "coordOne"; const QString AnnotationFileXmlFormatBase::ELEMENT_COORDINATE_TWO = "coordTwo"; const QString AnnotationFileXmlFormatBase::ELEMENT_GROUP = "group"; const QString AnnotationFileXmlFormatBase::ELEMENT_IMAGE = "image"; const QString AnnotationFileXmlFormatBase::ELEMENT_IMAGE_RGBA_BYTES_IN_BASE64 = "imageRgbaBytesInBase64"; const QString AnnotationFileXmlFormatBase::ELEMENT_LINE = "line"; const QString AnnotationFileXmlFormatBase::ELEMENT_OVAL = "oval"; const QString AnnotationFileXmlFormatBase::ELEMENT_PERCENT_SIZE_TEXT = "percentSizeText"; const QString AnnotationFileXmlFormatBase::ELEMENT_POINT_SIZE_TEXT = "pointSizeText"; const QString AnnotationFileXmlFormatBase::ELEMENT_TEXT_OBSOLETE = "text"; const QString AnnotationFileXmlFormatBase::ELEMENT_TEXT_DATA = "textData"; const int32_t AnnotationFileXmlFormatBase::XML_VERSION_ONE = 1; const int32_t AnnotationFileXmlFormatBase::XML_VERSION_TWO = 2; #endif // __ANNOTATION_FILE_XML_FORMAT_BASE_DECLARE__ } // namespace #endif //__ANNOTATION_FILE_XML_FORMAT_BASE_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Files/AnnotationFileXmlReader.cxx000066400000000000000000001370211300200146000270540ustar00rootroot00000000000000 /*LICENSE_START*/ /* * Copyright (C) 2015 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 #define __ANNOTATION_FILE_XML_READER_DECLARE__ #include "AnnotationFileXmlReader.h" #undef __ANNOTATION_FILE_XML_READER_DECLARE__ #include "AnnotationBox.h" #include "AnnotationCoordinate.h" #include "AnnotationFile.h" #include "AnnotationGroup.h" #include "AnnotationImage.h" #include "AnnotationLine.h" #include "AnnotationOval.h" #include "AnnotationPercentSizeText.h" #include "AnnotationPointSizeText.h" #include "CaretAssert.h" #include "DataFileException.h" #include "GiftiXmlElements.h" #include "XmlStreamReaderHelper.h" using namespace caret; /** * \class caret::AnnotationFileXmlReader * \brief Read Annotation File from XML format. * \ingroup Files */ /** * Constructor. */ AnnotationFileXmlReader::AnnotationFileXmlReader() : AnnotationFileXmlFormatBase() { m_stream.grabNew(new QXmlStreamReader()); m_streamHelper.grabNew(NULL); } /** * Destructor. */ AnnotationFileXmlReader::~AnnotationFileXmlReader() { } /** * Read the given annotation file in XML format. * * @param filename * Name of file. * @param annotationFile * Read into this annotation file. * @throws * DataFileException if there is an error reading the file. */ void AnnotationFileXmlReader::readFile(const QString& filename, AnnotationFile* annotationFile) { m_fileVersionNumber = -1; CaretAssert(annotationFile); m_filename = filename; /* * Open the file */ QFile file(m_filename); if ( ! file.open(QFile::ReadOnly)) { const QString msg("Error opening for reading." + file.errorString()); throw DataFileException(m_filename, msg); } /* * Create an XML stream writer */ m_stream.grabNew(new QXmlStreamReader(&file)); readFileContentFromXmlStreamReader(filename, annotationFile); file.close(); if (m_stream->hasError()) { m_streamHelper->throwDataFileException("There was an error reading the annotation file in XML format (reported by QXmlStreamReader): " + m_stream->errorString()); } } /** * Read the given annotation file contained in a string in XML format. * * @param fileInString * String containing the file. * @param annotationFile * Read into this annotation file. * @throws * DataFileException if there is an error reading the file. */ void AnnotationFileXmlReader::readFileFromString(const QString& fileInString, AnnotationFile* annotationFile) { /* * Create an XML stream writer */ m_stream.grabNew(new QXmlStreamReader(fileInString)); readFileContentFromXmlStreamReader("SceneFileName", annotationFile); if (m_stream->hasError()) { m_streamHelper->throwDataFileException("There was an error reading the annotation file in XML format (reported by QXmlStreamReader): " + m_stream->errorString()); } } /** * Read content of the file from the XML stream reader. * * @param filename * Name of the file. * @param annotationFile * Read into this annotation file. * @throws * DataFileException if there is an error reading the file. */ void AnnotationFileXmlReader::readFileContentFromXmlStreamReader(const QString& filename, AnnotationFile* annotationFile) { /* * Create the helper for reading XML */ if (m_streamHelper != NULL) { m_streamHelper.grabNew(NULL); } m_streamHelper.grabNew(new XmlStreamReaderHelper(filename, m_stream)); if (m_stream->atEnd()) { m_streamHelper->throwDataFileException("Error reading. File appears to have no XML content."); } const bool fileElementValid = m_stream->readNextStartElement(); if ( ! fileElementValid) { m_streamHelper->throwDataFileException("Appears to have no XML elements."); } const QStringRef fileElementName = m_stream->name(); if (fileElementName != ELEMENT_ANNOTATION_FILE) { m_streamHelper->throwDataFileException("First element is " + fileElementName.toString() + " but should be " + ELEMENT_ANNOTATION_FILE); } QXmlStreamAttributes fileAttributes = m_stream->attributes(); const QStringRef versionText = fileAttributes.value(ATTRIBUTE_VERSION); if (versionText.isEmpty()) { m_streamHelper->throwDataFileException("Version attribute (" + ATTRIBUTE_VERSION + ") is missing from the file element " + ELEMENT_ANNOTATION_FILE); } m_fileVersionNumber = versionText.toString().toInt(); if (m_fileVersionNumber == XML_VERSION_ONE) { readVersionOne(annotationFile); } else if (m_fileVersionNumber == XML_VERSION_TWO) { readVersionTwo(annotationFile); } else { m_streamHelper->throwDataFileException("File version number " + versionText.toString() + " is not supported by this version of the software."); } } /** * Read a version one Annotation XML file. * * @param annotationFile * Add annotations to this file. */ void AnnotationFileXmlReader::readVersionOne(AnnotationFile* annotationFile) { while (m_stream->readNextStartElement()) { bool skipCurrentElementFlag = true; const QString elementName = m_stream->name().toString(); if (elementName == GiftiXmlElements::TAG_METADATA) { m_streamHelper->readMetaData(annotationFile->getFileMetaData()); skipCurrentElementFlag = false; } else if (elementName == ELEMENT_BOX) { CaretPointer annotation(new AnnotationBox(AnnotationAttributesDefaultTypeEnum::NORMAL)); readTwoDimensionalAnnotation(ELEMENT_BOX, annotation); annotationFile->addAnnotationDuringFileVersionOneReading(annotation.releasePointer()); } else if (elementName == ELEMENT_IMAGE) { CaretPointer annotation(new AnnotationImage(AnnotationAttributesDefaultTypeEnum::NORMAL)); readTwoDimensionalAnnotation(ELEMENT_IMAGE, annotation); annotationFile->addAnnotationDuringFileVersionOneReading(annotation.releasePointer()); } else if (elementName == ELEMENT_LINE) { CaretPointer annotation(new AnnotationLine(AnnotationAttributesDefaultTypeEnum::NORMAL)); readOneDimensionalAnnotation(ELEMENT_LINE, annotation); annotationFile->addAnnotationDuringFileVersionOneReading(annotation.releasePointer()); } else if (elementName == ELEMENT_OVAL) { CaretPointer annotation(new AnnotationOval(AnnotationAttributesDefaultTypeEnum::NORMAL)); readTwoDimensionalAnnotation(ELEMENT_OVAL, annotation); annotationFile->addAnnotationDuringFileVersionOneReading(annotation.releasePointer()); } else if (elementName == ELEMENT_PERCENT_SIZE_TEXT) { CaretPointer annotation(new AnnotationPercentSizeText(AnnotationAttributesDefaultTypeEnum::NORMAL)); readTwoDimensionalAnnotation(ELEMENT_PERCENT_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, annotation); annotationFile->addAnnotationDuringFileVersionOneReading(annotation.releasePointer()); } else if (elementName == ELEMENT_TEXT_OBSOLETE) { CaretPointer annotation(new AnnotationPercentSizeText(AnnotationAttributesDefaultTypeEnum::NORMAL)); readTwoDimensionalAnnotation(ELEMENT_TEXT_OBSOLETE, annotation); annotationFile->addAnnotationDuringFileVersionOneReading(annotation.releasePointer()); } else { m_streamHelper->throwDataFileException("Unexpected XML element " + elementName); } /* * These elements have no other child elements so move on */ if (skipCurrentElementFlag) { m_stream->skipCurrentElement(); } } } /** * Read a version two Annotation XML file. * * @param annotationFile * Add annotations to this file. */ void AnnotationFileXmlReader::readVersionTwo(AnnotationFile* annotationFile) { while (m_stream->readNextStartElement()) { bool skipCurrentElementFlag = true; const QString elementName = m_stream->name().toString(); if (elementName == GiftiXmlElements::TAG_METADATA) { m_streamHelper->readMetaData(annotationFile->getFileMetaData()); skipCurrentElementFlag = false; } else if (elementName == ELEMENT_GROUP) { readGroup(annotationFile); skipCurrentElementFlag = false; } else { m_streamHelper->throwDataFileException("Unexpected XML element " + elementName); } /* * These elements have no other child elements so move on */ if (skipCurrentElementFlag) { m_stream->skipCurrentElement(); } } } /** * Read the next start element which should be a coordinate * with the given element name. * * @param annotation * One-dimensional annotation that has its data read. * @throw * DataFileException */ void AnnotationFileXmlReader::readCoordinate(const QString& coordinateElementName, AnnotationCoordinate* coordinate) { CaretAssert(coordinate); const bool elementValid = m_stream->readNextStartElement(); if ( ! elementValid) { m_streamHelper->throwDataFileException("Failed to read element " + coordinateElementName); } if (m_stream->name() != coordinateElementName) { m_streamHelper->throwDataFileException("Expected elment " + coordinateElementName + " but read element " + m_stream->name().toString()); } const QXmlStreamAttributes attributes = m_stream->attributes(); /* * XYZ coordinate */ const float xyz[3] = { m_streamHelper->getRequiredAttributeFloatValue(attributes, coordinateElementName, ATTRIBUTE_COORD_X), m_streamHelper->getRequiredAttributeFloatValue(attributes, coordinateElementName, ATTRIBUTE_COORD_Y), m_streamHelper->getRequiredAttributeFloatValue(attributes, coordinateElementName, ATTRIBUTE_COORD_Z) }; coordinate->setXYZ(xyz); /* * Surface coordinate */ const int32_t numberOfNodes = m_streamHelper->getRequiredAttributeIntValue(attributes, coordinateElementName, ATTRIBUTE_COORD_SURFACE_NUMBER_OF_NODES); const int32_t nodeIndex = m_streamHelper->getRequiredAttributeIntValue(attributes, coordinateElementName, ATTRIBUTE_COORD_SURFACE_NODE_INDEX); const QString structureValueString = m_streamHelper->getRequiredAttributeStringValue(attributes, coordinateElementName, ATTRIBUTE_COORD_SURFACE_STRUCTURE); const float offsetDistance = m_streamHelper->getRequiredAttributeFloatValue(attributes, coordinateElementName, ATTRIBUTE_COORD_SURFACE_NODE_OFFSET); const QString offsetVectorString = m_streamHelper->getOptionalAttributeStringValue(attributes, coordinateElementName, ATTRIBUTE_COORD_SURFACE_NODE_OFFSET_VECTOR_TYPE, "CENTROID_THRU_VERTEX"); bool structureValid = false; StructureEnum::Enum structure = StructureEnum::fromName(structureValueString, &structureValid); bool offsetVectorValid = false; AnnotationSurfaceOffsetVectorTypeEnum::Enum offsetVector = AnnotationSurfaceOffsetVectorTypeEnum::fromName(offsetVectorString, &offsetVectorValid); if ( ! offsetVectorValid) { offsetVector = AnnotationSurfaceOffsetVectorTypeEnum::CENTROID_THRU_VERTEX; } if (structureValid) { coordinate->setSurfaceSpace(structure, numberOfNodes, nodeIndex, offsetDistance, offsetVector); } else { m_streamHelper->throwDataFileException("Invalid value " + structureValueString + " for attribute " + ATTRIBUTE_COORD_SURFACE_STRUCTURE); } /* * No other child elements so move on */ m_stream->skipCurrentElement(); } /** * Read the attributes common to all annotation elements. * * @param annotation * The annotation. * @param annotationElementName * Name of element of annotation. * @param attributes * The XML attributes. * @throw * DataFileException if there is an error reading the attributes. */ void AnnotationFileXmlReader::readAnnotationAttributes(Annotation* annotation, const QString& annotationElementName, const QXmlStreamAttributes& attributes) { CaretAssert(annotation); { /* * Coordinate space */ const QString valueString = m_streamHelper->getRequiredAttributeStringValue(attributes, annotationElementName, ATTRIBUTE_COORDINATE_SPACE); bool valid = false; AnnotationCoordinateSpaceEnum::Enum value = AnnotationCoordinateSpaceEnum::fromName(valueString, &valid); if (valid) { annotation->setCoordinateSpace(value); } else { m_streamHelper->throwDataFileException("Invalid value " + valueString + " for attribute " + ATTRIBUTE_COORDINATE_SPACE); } } { /* * Background color */ const QString valueString = m_streamHelper->getRequiredAttributeStringValue(attributes, annotationElementName, ATTRIBUTE_BACKGROUND_CARET_COLOR); bool valid = false; CaretColorEnum::Enum value = CaretColorEnum::fromName(valueString, &valid); if (valid) { annotation->setBackgroundColor(value); } else { m_streamHelper->throwDataFileException("Invalid value " + valueString + " for attribute " + ATTRIBUTE_BACKGROUND_CARET_COLOR); } } { /* * Background custom color */ const QString valueString = m_streamHelper->getRequiredAttributeStringValue(attributes, annotationElementName, ATTRIBUTE_BACKGROUND_CUSTOM_RGBA); std::vector rgba; AString::toNumbers(valueString, rgba); if (rgba.size() == 4) { annotation->setCustomBackgroundColor(&rgba[0]); } else { m_streamHelper->throwDataFileException(ATTRIBUTE_BACKGROUND_CUSTOM_RGBA + " must contain 4 elements but " + valueString + " contains " + QString::number(rgba.size()) + " elements"); } } { /* * Foreground color */ const QString valueString = m_streamHelper->getRequiredAttributeStringValue(attributes, annotationElementName, ATTRIBUTE_FOREGROUND_CARET_COLOR); bool valid = false; CaretColorEnum::Enum value = CaretColorEnum::fromName(valueString, &valid); if (valid) { annotation->setLineColor(value); } else { m_streamHelper->throwDataFileException("Invalid value " + valueString + " for attribute " + ATTRIBUTE_FOREGROUND_CARET_COLOR); } } { /* * Foreground custom color */ const QString valueString = m_streamHelper->getRequiredAttributeStringValue(attributes, annotationElementName, ATTRIBUTE_FOREGROUND_CUSTOM_RGBA); std::vector rgba; AString::toNumbers(valueString, rgba); if (rgba.size() == 4) { annotation->setCustomLineColor(&rgba[0]); } else { m_streamHelper->throwDataFileException(ATTRIBUTE_FOREGROUND_CUSTOM_RGBA + " must contain 4 elements but " + valueString + " contains " + QString::number(rgba.size()) + " elements"); } } /* * Foreground line width */ annotation->setLineWidth(m_streamHelper->getRequiredAttributeFloatValue(attributes, annotationElementName, ATTRIBUTE_FOREGROUND_LINE_WIDTH)); /* * Tab Index */ annotation->setTabIndex(m_streamHelper->getRequiredAttributeIntValue(attributes, annotationElementName, ATTRIBUTE_TAB_INDEX)); /* * Window Index */ annotation->setWindowIndex(m_streamHelper->getRequiredAttributeIntValue(attributes, annotationElementName, ATTRIBUTE_WINDOW_INDEX)); /* * Unique Key */ if (m_fileVersionNumber >= XML_VERSION_TWO) { annotation->setUniqueKey(m_streamHelper->getRequiredAttributeIntValue(attributes, annotationElementName, ATTRIBUTE_UNIQUE_KEY)); } } /** * Read a one dimensional annotation. * * @param annotationElementName * Name of one-dimensional attribute. * @param annotation * One-dimensional annotation that has its data read. */ void AnnotationFileXmlReader::readOneDimensionalAnnotation(const QString& annotationElementName, AnnotationOneDimensionalShape* annotation) { CaretAssert(annotation); const QXmlStreamAttributes attributes = m_stream->attributes(); readAnnotationAttributes(annotation, annotationElementName, attributes); AnnotationLine* line = dynamic_cast(annotation); if (line != NULL) { line->setDisplayEndArrow(m_streamHelper->getOptionalAttributeBoolValue(attributes, annotationElementName, ATTRIBUTE_LINE_END_ARROW, false)); line->setDisplayStartArrow(m_streamHelper->getOptionalAttributeBoolValue(attributes, annotationElementName, ATTRIBUTE_LINE_START_ARROW, false)); } readCoordinate(ELEMENT_COORDINATE_ONE, annotation->getStartCoordinate()); readCoordinate(ELEMENT_COORDINATE_TWO, annotation->getEndCoordinate()); } /** * Read an annotation group. * * @param annotationFile * File that is being read. */ void AnnotationFileXmlReader::readGroup(AnnotationFile* annotationFile) { const QXmlStreamAttributes attributes = m_stream->attributes(); /* * Coordinate space */ AnnotationCoordinateSpaceEnum::Enum coordSpace = AnnotationCoordinateSpaceEnum::PIXELS; { const QString valueString = m_streamHelper->getRequiredAttributeStringValue(attributes, ELEMENT_GROUP, ATTRIBUTE_COORDINATE_SPACE); bool valid = false; coordSpace = AnnotationCoordinateSpaceEnum::fromName(valueString, &valid); if ( ! valid) { m_streamHelper->throwDataFileException("While reading annotation group, invalid value " + valueString + " for attribute " + ATTRIBUTE_COORDINATE_SPACE); } } /* * Group tyoe */ AnnotationGroupTypeEnum::Enum groupType = AnnotationGroupTypeEnum::INVALID; { const QString valueString = m_streamHelper->getRequiredAttributeStringValue(attributes, ELEMENT_GROUP, ATTRIBUTE_GROUP_TYPE); bool valid = false; groupType = AnnotationGroupTypeEnum::fromName(valueString, &valid); if ( ! valid) { m_streamHelper->throwDataFileException("While reading annotation group, invalid value " + valueString + " for attribute " + ATTRIBUTE_GROUP_TYPE); } } const int32_t tabOrWindowIndex = m_streamHelper->getRequiredAttributeIntValue(attributes, ELEMENT_GROUP, ATTRIBUTE_TAB_OR_WINDOW_INDEX); const int32_t uniqueKey = m_streamHelper->getRequiredAttributeIntValue(attributes, ELEMENT_GROUP, ATTRIBUTE_UNIQUE_KEY); std::vector annotations; while (m_stream->readNextStartElement()) { bool skipCurrentElementFlag = true; const QString elementName = m_stream->name().toString(); if (elementName == ELEMENT_BOX) { CaretPointer annotation(new AnnotationBox(AnnotationAttributesDefaultTypeEnum::NORMAL)); readTwoDimensionalAnnotation(ELEMENT_BOX, annotation); annotations.push_back(annotation.releasePointer()); } else if (elementName == ELEMENT_IMAGE) { CaretPointer annotation(new AnnotationImage(AnnotationAttributesDefaultTypeEnum::NORMAL)); readTwoDimensionalAnnotation(ELEMENT_IMAGE, annotation); annotations.push_back(annotation.releasePointer()); } else if (elementName == ELEMENT_LINE) { CaretPointer annotation(new AnnotationLine(AnnotationAttributesDefaultTypeEnum::NORMAL)); readOneDimensionalAnnotation(ELEMENT_LINE, annotation); annotations.push_back(annotation.releasePointer()); } else if (elementName == ELEMENT_OVAL) { CaretPointer annotation(new AnnotationOval(AnnotationAttributesDefaultTypeEnum::NORMAL)); readTwoDimensionalAnnotation(ELEMENT_OVAL, annotation); annotations.push_back(annotation.releasePointer()); } else if (elementName == ELEMENT_PERCENT_SIZE_TEXT) { CaretPointer annotation(new AnnotationPercentSizeText(AnnotationAttributesDefaultTypeEnum::NORMAL)); readTwoDimensionalAnnotation(ELEMENT_PERCENT_SIZE_TEXT, annotation); annotations.push_back(annotation.releasePointer()); } else if (elementName == ELEMENT_POINT_SIZE_TEXT) { CaretPointer annotation(new AnnotationPointSizeText(AnnotationAttributesDefaultTypeEnum::NORMAL)); readTwoDimensionalAnnotation(ELEMENT_POINT_SIZE_TEXT, annotation); annotations.push_back(annotation.releasePointer()); } else if (elementName == ELEMENT_TEXT_OBSOLETE) { CaretPointer annotation(new AnnotationPercentSizeText(AnnotationAttributesDefaultTypeEnum::NORMAL)); readTwoDimensionalAnnotation(ELEMENT_TEXT_OBSOLETE, annotation); annotations.push_back(annotation.releasePointer()); } else { m_streamHelper->throwDataFileException("Unexpected XML element " + elementName); } /* * These elements have no other child elements so move on */ if (skipCurrentElementFlag) { m_stream->skipCurrentElement(); } } if ( ! annotations.empty()) { annotationFile->addAnnotationGroupDuringFileReading(groupType, coordSpace, tabOrWindowIndex, uniqueKey, annotations); } } /** * Read a two dimensional annotation. * * @param annotationElementName * Name of two-dimensional attribute. * @param annotation * Two-dimensional annotation that has its data read. */ void AnnotationFileXmlReader::readTwoDimensionalAnnotation(const QString& annotationElementName, AnnotationTwoDimensionalShape* annotation) { CaretAssert(annotation); const QXmlStreamAttributes attributes = m_stream->attributes(); readAnnotationAttributes(annotation, annotationElementName, attributes); /* * Shape width */ annotation->setWidth(m_streamHelper->getRequiredAttributeFloatValue(attributes, annotationElementName, ATTRIBUTE_WIDTH)); /* * Shape height */ annotation->setHeight(m_streamHelper->getRequiredAttributeFloatValue(attributes, annotationElementName, ATTRIBUTE_HEIGHT)); /* * Shape rotation angle */ annotation->setRotationAngle(m_streamHelper->getRequiredAttributeFloatValue(attributes, annotationElementName, ATTRIBUTE_ROTATION_ANGLE)); /* * Read the coordinate */ readCoordinate(ELEMENT_COORDINATE_ONE, annotation->getCoordinate()); /* * Is this an image annotation? */ AnnotationImage* imageAnn = dynamic_cast(annotation); if (imageAnn != NULL) { readImageDataElement(imageAnn); } /* * Is this a text annotation? */ AnnotationText* textAnn = dynamic_cast(annotation); if (textAnn != NULL) { readTextDataElement(textAnn, annotationElementName); } } /** * Read the image annotation element. * * @param imageAnnotation * Image annotation that has its element data read. * @throw * DataFileException */ void AnnotationFileXmlReader::readImageDataElement(AnnotationImage* imageAnnotation) { CaretAssert(imageAnnotation); const bool elementValid = m_stream->readNextStartElement(); if ( ! elementValid) { m_streamHelper->throwDataFileException("Failed to read element " + ELEMENT_IMAGE_RGBA_BYTES_IN_BASE64); } if (m_stream->name() != ELEMENT_IMAGE_RGBA_BYTES_IN_BASE64) { m_streamHelper->throwDataFileException("Expected elment " + ELEMENT_IMAGE_RGBA_BYTES_IN_BASE64 + " but read element " + m_stream->name().toString()); } const QXmlStreamAttributes attributes = m_stream->attributes(); const int32_t imageWidth = m_streamHelper->getRequiredAttributeIntValue(attributes, ELEMENT_IMAGE_RGBA_BYTES_IN_BASE64, ATTRIBUTE_IMAGE_WIDTH); const int32_t imageHeight = m_streamHelper->getRequiredAttributeIntValue(attributes, ELEMENT_IMAGE_RGBA_BYTES_IN_BASE64, ATTRIBUTE_IMAGE_HEIGHT); const int32_t numberOfBytes = imageWidth * imageHeight * 4; /* * Read the image bytes in base64 encoding which will also finish reading through * the closing element. */ const QString imageChars = m_stream->readElementText(QXmlStreamReader::ErrorOnUnexpectedElement); if (m_stream->hasError()) { m_streamHelper->throwDataFileException("There was an error reading the image annotation's image bytes: " + m_stream->errorString()); } QByteArray imageBytes = QByteArray::fromBase64(imageChars.toAscii()); if (imageBytes.size() == numberOfBytes) { const uint8_t* imageBytesPointer = (const uint8_t*)(imageBytes.data()); imageAnnotation->setImageBytesRGBA(imageBytesPointer, imageWidth, imageHeight); } else { m_streamHelper->throwDataFileException("There was an error reading the image annotations image bytes. " "The number of bytes read was " + AString::number(imageBytes.size()) + " but the number of bytes expected was " + AString::number(numberOfBytes)); } } /** * Read the text annotation element. * * @param textAnnotation * Text annotation that has its element data read. * @param annotationTextElementName * Name of the annotation element. * @throw * DataFileException */ void AnnotationFileXmlReader::readTextDataElement(AnnotationText *textAnnotation, const QString& annotationTextElementName) { CaretAssert(textAnnotation); const bool elementValid = m_stream->readNextStartElement(); if ( ! elementValid) { m_streamHelper->throwDataFileException("Failed to read element " + ELEMENT_TEXT_DATA); } if (m_stream->name() != ELEMENT_TEXT_DATA) { m_streamHelper->throwDataFileException("Expected elment " + ELEMENT_TEXT_DATA + " but read element " + m_stream->name().toString()); } const QXmlStreamAttributes attributes = m_stream->attributes(); bool haveTextColorFlag = false; { /* * Background color */ const QString valueString = m_streamHelper->getOptionalAttributeStringValue(attributes, annotationTextElementName, ATTRIBUTE_TEXT_CARET_COLOR, ""); if ( ! valueString.isEmpty()) { bool valid = false; CaretColorEnum::Enum value = CaretColorEnum::fromName(valueString, &valid); if (valid) { textAnnotation->setTextColor(value); haveTextColorFlag = true; } else { m_streamHelper->throwDataFileException("Invalid value " + valueString + " for attribute " + ATTRIBUTE_TEXT_CARET_COLOR); } } } bool haveCustomTextColorFlag = false; { /* * Background custom color */ const QString valueString = m_streamHelper->getOptionalAttributeStringValue(attributes, annotationTextElementName, ATTRIBUTE_TEXT_CUSTOM_RGBA, ""); if ( ! valueString.isEmpty()) { std::vector rgba; AString::toNumbers(valueString, rgba); if (rgba.size() == 4) { textAnnotation->setCustomTextColor(&rgba[0]); haveCustomTextColorFlag = true; } else { m_streamHelper->throwDataFileException(ATTRIBUTE_TEXT_CUSTOM_RGBA + " must contain 4 elements but " + valueString + " contains " + QString::number(rgba.size()) + " elements"); } } } if (haveTextColorFlag && haveCustomTextColorFlag) { /* nothing */ } else { /* * Older (pre Workbench 1.2) annotations did not have a text color * and the text was drawn using the foreground color. * So, copy the foreground color to the text color and set * the foreground color to none. */ textAnnotation->setTextColor(textAnnotation->getLineColor()); float rgba[4]; textAnnotation->getCustomLineColor(rgba); textAnnotation->setCustomTextColor(rgba); textAnnotation->setLineColor(CaretColorEnum::NONE); } textAnnotation->setBoldStyleEnabled(m_streamHelper->getRequiredAttributeBoolValue(attributes, ELEMENT_TEXT_DATA, ATTRIBUTE_TEXT_FONT_BOLD)); textAnnotation->setItalicStyleEnabled(m_streamHelper->getRequiredAttributeBoolValue(attributes, ELEMENT_TEXT_DATA, ATTRIBUTE_TEXT_FONT_ITALIC)); textAnnotation->setUnderlineStyleEnabled(m_streamHelper->getRequiredAttributeBoolValue(attributes, ELEMENT_TEXT_DATA, ATTRIBUTE_TEXT_FONT_UNDERLINE)); { const QString defaultValue = AnnotationTextConnectTypeEnum::toName(AnnotationTextConnectTypeEnum::ANNOTATION_TEXT_CONNECT_NONE); const QString valueString = m_streamHelper->getOptionalAttributeStringValue(attributes, ELEMENT_TEXT_DATA, ATTRIBUTE_TEXT_CONNECT_BRAINORDINATE, defaultValue); bool valid = false; AnnotationTextConnectTypeEnum::Enum connectValue = AnnotationTextConnectTypeEnum::fromName(valueString, &valid); if (valid) { textAnnotation->setConnectToBrainordinate(connectValue); } else { m_streamHelper->throwDataFileException("Invalid value " + valueString + " for attribute " + ATTRIBUTE_TEXT_CONNECT_BRAINORDINATE); } } { const QString valueString = m_streamHelper->getRequiredAttributeStringValue(attributes, ELEMENT_TEXT_DATA, ATTRIBUTE_TEXT_FONT_NAME); bool valid = false; AnnotationTextFontNameEnum::Enum fontName = AnnotationTextFontNameEnum::fromName(valueString, &valid); if (valid) { textAnnotation->setFont(fontName); } else { m_streamHelper->throwDataFileException("Invalid value " + valueString + " for attribute " + ATTRIBUTE_TEXT_FONT_NAME); } } if (annotationTextElementName != ELEMENT_PERCENT_SIZE_TEXT) { const QString valueString = m_streamHelper->getRequiredAttributeStringValue(attributes, ELEMENT_TEXT_DATA, ATTRIBUTE_TEXT_FONT_POINT_SIZE, "fontSize"); bool valid = false; AnnotationTextFontPointSizeEnum::Enum fontPointSize = AnnotationTextFontPointSizeEnum::fromName(valueString, &valid); if (valid) { textAnnotation->setFontPointSizeProtected(fontPointSize); } else { m_streamHelper->throwDataFileException("Invalid value " + valueString + " for attribute " + ATTRIBUTE_TEXT_FONT_POINT_SIZE); } } { const QString valueString = m_streamHelper->getRequiredAttributeStringValue(attributes, ELEMENT_TEXT_DATA, ATTRIBUTE_TEXT_HORIZONTAL_ALIGNMENT); bool valid = false; AnnotationTextAlignHorizontalEnum::Enum alignment = AnnotationTextAlignHorizontalEnum::fromName(valueString, &valid); if (valid) { textAnnotation->setHorizontalAlignment(alignment); } else { m_streamHelper->throwDataFileException("Invalid value " + valueString + " for attribute " + ATTRIBUTE_TEXT_HORIZONTAL_ALIGNMENT); } } { const QString valueString = m_streamHelper->getRequiredAttributeStringValue(attributes, ELEMENT_TEXT_DATA, ATTRIBUTE_TEXT_VERTICAL_ALIGNMENT); bool valid = false; AnnotationTextAlignVerticalEnum::Enum alignment = AnnotationTextAlignVerticalEnum::fromName(valueString, &valid); if (valid) { textAnnotation->setVerticalAlignment(alignment); } else { m_streamHelper->throwDataFileException("Invalid value " + valueString + " for attribute " + ATTRIBUTE_TEXT_VERTICAL_ALIGNMENT); } } { const QString valueString = m_streamHelper->getRequiredAttributeStringValue(attributes, ELEMENT_TEXT_DATA, ATTRIBUTE_TEXT_ORIENTATION); bool valid = false; AnnotationTextOrientationEnum::Enum orientation = AnnotationTextOrientationEnum::fromName(valueString, &valid); if (valid) { textAnnotation->setOrientation(orientation); } else { m_streamHelper->throwDataFileException("Invalid value " + valueString + " for attribute " + ATTRIBUTE_TEXT_ORIENTATION); } } if (annotationTextElementName == ELEMENT_PERCENT_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, ATTRIBUTE_TEXT_FONT_PERCENT_VIEWPORT_SIZE, 5.0)); } if (annotationTextElementName == ELEMENT_TEXT_OBSOLETE) { } else if (annotationTextElementName == ELEMENT_PERCENT_SIZE_TEXT) { /* * Will cause warning or assertion failure if invalid value */ textAnnotation->setFontPercentViewportSizeProtected(textAnnotation->getFontPercentViewportSizeProtected()); } else if (annotationTextElementName == ELEMENT_POINT_SIZE_TEXT) { } else { m_streamHelper->throwDataFileException("Unrecognized text element name \"" + annotationTextElementName + "\" expected " + ELEMENT_PERCENT_SIZE_TEXT + " or " + ELEMENT_POINT_SIZE_TEXT); } /* * Read the annotation's text which will also finish reading through * the closing element. */ const QString textChars = m_stream->readElementText(QXmlStreamReader::ErrorOnUnexpectedElement); if (m_stream->hasError()) { m_streamHelper->throwDataFileException("There was an error reading the text annotation's characters: " + m_stream->errorString()); } textAnnotation->setText(textChars); if (m_stream->hasError()) { m_streamHelper->throwDataFileException("There was an error reading the annotation file in XML format (reported by QXmlStreamReader): " + m_stream->errorString()); } } connectome-workbench-1.2.3+git41-gc4c6c90/src/Files/AnnotationFileXmlReader.h000066400000000000000000000072111300200146000264760ustar00rootroot00000000000000#ifndef __ANNOTATION_FILE_XML_READER_H__ #define __ANNOTATION_FILE_XML_READER_H__ /*LICENSE_START*/ /* * Copyright (C) 2015 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 "AnnotationFileXmlFormatBase.h" #include "CaretPointer.h" class QXmlStreamAttributes; class QXmlStreamReader; namespace caret { class Annotation; class AnnotationCoordinate; class AnnotationFile; class AnnotationGroup; class AnnotationImage; class AnnotationOneDimensionalShape; class AnnotationText; class AnnotationTwoDimensionalShape; class XmlStreamReaderHelper; class AnnotationFileXmlReader : public AnnotationFileXmlFormatBase { public: AnnotationFileXmlReader(); virtual ~AnnotationFileXmlReader(); void readFile(const QString& filename, AnnotationFile* annotationFile); void readFileFromString(const QString& fileInString, AnnotationFile* annotationFile); // ADD_NEW_METHODS_HERE private: AnnotationFileXmlReader(const AnnotationFileXmlReader&); AnnotationFileXmlReader& operator=(const AnnotationFileXmlReader&); void readFileContentFromXmlStreamReader(const QString& filename, AnnotationFile* annotationFile); void readVersionOne(AnnotationFile* annotationFile); void readVersionTwo(AnnotationFile* annotationFile); void readGroup(AnnotationFile* annotationFile); void readOneDimensionalAnnotation(const QString& annotationElementName, AnnotationOneDimensionalShape* annotation); void readTwoDimensionalAnnotation(const QString& annotationElementName, AnnotationTwoDimensionalShape* annotation); void readCoordinate(const QString& coordinateElementName, AnnotationCoordinate* coordinate); void readAnnotationAttributes(Annotation* annotation, const QString& annotationElementName, const QXmlStreamAttributes& attributes); void readImageDataElement(AnnotationImage* imageAnnotation); void readTextDataElement(AnnotationText* textAnnotation, const QString& annotationTextElementName); CaretPointer m_stream; CaretPointer m_streamHelper; QString m_filename; int32_t m_fileVersionNumber; // ADD_NEW_MEMBERS_HERE }; #ifdef __ANNOTATION_FILE_XML_READER_DECLARE__ // #endif // __ANNOTATION_FILE_XML_READER_DECLARE__ } // namespace #endif //__ANNOTATION_FILE_XML_READER_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Files/AnnotationFileXmlWriter.cxx000066400000000000000000000536241300200146000271340ustar00rootroot00000000000000 /*LICENSE_START*/ /* * Copyright (C) 2015 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 #define __ANNOTATION_FILE_XML_WRITER_DECLARE__ #include "AnnotationFileXmlWriter.h" #undef __ANNOTATION_FILE_XML_WRITER_DECLARE__ #include "AnnotationBox.h" #include "AnnotationCoordinate.h" #include "AnnotationFile.h" #include "AnnotationGroup.h" #include "AnnotationImage.h" #include "AnnotationLine.h" #include "AnnotationOval.h" #include "AnnotationText.h" #include "CaretAssert.h" #include "DataFileException.h" #include "XmlStreamWriterHelper.h" #include "XmlUtilities.h" using namespace caret; /** * \class caret::AnnotationFileXmlWriter * \brief Writes an Annotation File in XML format. * \ingroup Files */ /** * Constructor. */ AnnotationFileXmlWriter::AnnotationFileXmlWriter() : AnnotationFileXmlFormatBase() { m_stream.grabNew(NULL); m_streamHelper.grabNew(NULL); } /** * Destructor. */ AnnotationFileXmlWriter::~AnnotationFileXmlWriter() { } /** * Write the given Annotation File in XML format. Name of the file * is obtained from the file. * * @param annotationFile * Annotation File written in XML format. * @throws * DataFileException if there is an error writing the file. */ void AnnotationFileXmlWriter::writeFile(const AnnotationFile* annotationFile) { CaretAssert(annotationFile); const QString filename = annotationFile->getFileName(); if (filename.isEmpty()) { throw DataFileException("Name for writing annotation file is empty."); } /* * Open the file */ QFile file(filename); if ( ! file.open(QFile::WriteOnly)) { throw DataFileException(filename, "Error opening for writing: " + file.errorString()); } /* * Create an XML stream writer */ m_stream.grabNew(new QXmlStreamWriter(&file)); writeFileContentToXmlStreamWriter(annotationFile, filename); file.close(); if (m_stream->hasError()) { throw DataFileException(filename, "There was an error writing the annotation file in XML format (reported by QXmlStreamWriter)."); } } /** * Write the given Annotation File in XML format into the given string. * * @param annotationFile * The annotation file written to the stream writer. * @param fileContentString * Will contain XML version of the file on exit. * @throws * DataFileException if there is an error writing the file. */ void AnnotationFileXmlWriter::writeFileToString(const AnnotationFile* annotationFile, QString& fileContentString) { fileContentString.clear(); m_stream.grabNew(new QXmlStreamWriter(&fileContentString)); writeFileContentToXmlStreamWriter(annotationFile, "Scene Annotation File"); if (m_stream->hasError()) { throw DataFileException("There was an error writing the scene annotation file in XML format (reported by QXmlStreamWriter)."); } } /** * Write the content of the file to the XML Stream Writer. * * @param annotationFile * The annotation file written to the stream writer. * @param filename * Name of the file. */ void AnnotationFileXmlWriter::writeFileContentToXmlStreamWriter(const AnnotationFile* annotationFile, const QString& filename) { CaretAssert(m_stream); m_stream->setAutoFormatting(true); /* * Create the helper for reading XML */ if (m_streamHelper != NULL) { m_streamHelper.grabNew(NULL); } m_streamHelper.grabNew(new XmlStreamWriterHelper(filename, m_stream)); m_stream->writeStartDocument(); m_stream->writeStartElement(ELEMENT_ANNOTATION_FILE); m_stream->writeAttribute(ATTRIBUTE_VERSION, AString::number(XML_VERSION_TWO)); m_streamHelper->writeMetaData(annotationFile->getFileMetaData()); // std::vector annotations; // annotationFile->getAllAnnotations(annotations); // const int32_t numberOfAnnotations = static_cast(annotations.size()); // for (int32_t i = 0; i < numberOfAnnotations; i++) { // CaretAssertVectorIndex(annotations, i); // const Annotation* annotation = annotations[i]; // CaretAssert(annotation); // // switch (annotation->getType()) { // case AnnotationTypeEnum::BOX: // writeBox(dynamic_cast(annotation)); // break; // case AnnotationTypeEnum::COLOR_BAR: // CaretAssertMessage(0, "Color bar is NEVER written to an annotation file"); // break; // case AnnotationTypeEnum::IMAGE: // writeImage(dynamic_cast(annotation)); // break; // case AnnotationTypeEnum::LINE: // writeLine(dynamic_cast(annotation)); // break; // case AnnotationTypeEnum::OVAL: // writeOval(dynamic_cast(annotation)); // break; // case AnnotationTypeEnum::TEXT: // writeText(dynamic_cast(annotation)); // break; // } // } std::vector annotationGroups; annotationFile->getAllAnnotationGroups(annotationGroups); for (std::vector::iterator groupIter = annotationGroups.begin(); groupIter != annotationGroups.end(); groupIter++) { writeGroup(*groupIter); } m_stream->writeEndElement(); // ELEMENT_ANNOTATION_FILE m_stream->writeEndDocument(); } /** * Write the given annotation group in XML. * * @param group * The annotation group. */ void AnnotationFileXmlWriter::writeGroup(const AnnotationGroup* group) { CaretAssert(group); if (group->isEmpty()) { return; } m_stream->writeStartElement(ELEMENT_GROUP); m_stream->writeAttribute(ATTRIBUTE_COORDINATE_SPACE, AnnotationCoordinateSpaceEnum::toName(group->getCoordinateSpace())); m_stream->writeAttribute(ATTRIBUTE_GROUP_TYPE, AnnotationGroupTypeEnum::toName(group->getGroupType())); m_stream->writeAttribute(ATTRIBUTE_TAB_OR_WINDOW_INDEX, QString::number(group->getTabOrWindowIndex())); m_stream->writeAttribute(ATTRIBUTE_UNIQUE_KEY, QString::number(group->getUniqueKey())); std::vector annotations; group->getAllAnnotations(annotations); for (std::vector::iterator annIter = annotations.begin(); annIter != annotations.end(); annIter++) { const Annotation* annotation = *annIter; CaretAssert(annotation); switch (annotation->getType()) { case AnnotationTypeEnum::BOX: writeBox(dynamic_cast(annotation)); break; case AnnotationTypeEnum::COLOR_BAR: CaretAssertMessage(0, "Color bar is NEVER written to an annotation file"); break; case AnnotationTypeEnum::IMAGE: writeImage(dynamic_cast(annotation)); break; case AnnotationTypeEnum::LINE: writeLine(dynamic_cast(annotation)); break; case AnnotationTypeEnum::OVAL: writeOval(dynamic_cast(annotation)); break; case AnnotationTypeEnum::TEXT: writeText(dynamic_cast(annotation)); break; } } // const AString indicesString = AString::fromNumbers(group->getAnnotationUniqueIdentifiers(), " "); // m_stream->writeCharacters(indicesString); m_stream->writeEndElement(); } /** * Write the given annotation box in XML. * * @param box * The annotation box. */ void AnnotationFileXmlWriter::writeBox(const AnnotationBox* box) { CaretAssert(box); writeTwoDimensionalAnnotation(box, ELEMENT_BOX); } /** * Write the given annotation image in XML. * * @param image * The annotation image. */ void AnnotationFileXmlWriter::writeImage(const AnnotationImage* image) { CaretAssert(image); const int32_t imageWidth = image->getImageWidth(); const int32_t imageHeight = image->getImageHeight(); const int32_t numberOfBytes = imageWidth * imageHeight * 4; if (numberOfBytes <= 0) { return; } const uint8_t* imageBytesRGBA = image->getImageBytesRGBA(); QByteArray byteArray((const char*)imageBytesRGBA, numberOfBytes); QByteArray byteArrayBase64(byteArray.toBase64()); const QString stringBase64(QString::fromAscii(byteArrayBase64.constData(), byteArrayBase64.size())); QXmlStreamAttributes attributes; getTwoDimAnnotationPropertiesAsAttributes(image, attributes); QXmlStreamAttributes imageDataAttributes; imageDataAttributes.append(ATTRIBUTE_IMAGE_WIDTH, AString::number(image->getImageWidth())); imageDataAttributes.append(ATTRIBUTE_IMAGE_HEIGHT, AString::number(image->getImageHeight())); m_stream->writeStartElement(ELEMENT_IMAGE); m_stream->writeAttributes(attributes); writeCoordinate(image->getCoordinate(), ELEMENT_COORDINATE_ONE); m_stream->writeStartElement(ELEMENT_IMAGE_RGBA_BYTES_IN_BASE64); m_stream->writeAttributes(imageDataAttributes); m_stream->writeCharacters(stringBase64); m_stream->writeEndElement(); m_stream->writeEndElement(); } /** * Write the given annotation line in XML. * * @param line * The annotation line. */ void AnnotationFileXmlWriter::writeLine(const AnnotationLine* line) { CaretAssert(line); writeOneDimensionalAnnotation(line, ELEMENT_LINE); } /** * Write the given annotation oval in XML. * * @param oval * The annotation oval. */ void AnnotationFileXmlWriter::writeOval(const AnnotationOval* oval) { CaretAssert(oval); writeTwoDimensionalAnnotation(oval, ELEMENT_OVAL); } /** * Write the given annotation text in XML. * * @param text * The annotation text. */ void AnnotationFileXmlWriter::writeText(const AnnotationText* text) { CaretAssert(text); QXmlStreamAttributes attributes; getTwoDimAnnotationPropertiesAsAttributes(text, attributes); QXmlStreamAttributes textDataAttributes; textDataAttributes.append(ATTRIBUTE_TEXT_CARET_COLOR, CaretColorEnum::toName(text->getTextColor())); float rgba[4]; text->getCustomTextColor(rgba); textDataAttributes.append(ATTRIBUTE_TEXT_CUSTOM_RGBA, realArrayToString(rgba, 4)); textDataAttributes.append(ATTRIBUTE_TEXT_FONT_BOLD, AString::fromBool(text->isBoldStyleEnabled())); textDataAttributes.append(ATTRIBUTE_TEXT_FONT_ITALIC, AString::fromBool(text->isItalicStyleEnabled())); textDataAttributes.append(ATTRIBUTE_TEXT_FONT_NAME, AnnotationTextFontNameEnum::toName(text->getFont())); textDataAttributes.append(ATTRIBUTE_TEXT_FONT_POINT_SIZE, AnnotationTextFontPointSizeEnum::toName(text->getFontPointSizeProtected())); textDataAttributes.append(ATTRIBUTE_TEXT_FONT_UNDERLINE, AString::fromBool(text->isUnderlineStyleEnabled())); textDataAttributes.append(ATTRIBUTE_TEXT_HORIZONTAL_ALIGNMENT, AnnotationTextAlignHorizontalEnum::toName(text->getHorizontalAlignment())); textDataAttributes.append(ATTRIBUTE_TEXT_ORIENTATION, AnnotationTextOrientationEnum::toName(text->getOrientation())); textDataAttributes.append(ATTRIBUTE_TEXT_VERTICAL_ALIGNMENT, AnnotationTextAlignVerticalEnum::toName(text->getVerticalAlignment())); textDataAttributes.append(ATTRIBUTE_TEXT_FONT_PERCENT_VIEWPORT_SIZE, AString::number(text->getFontPercentViewportSizeProtected())); textDataAttributes.append(ATTRIBUTE_TEXT_CONNECT_BRAINORDINATE, AnnotationTextConnectTypeEnum::toName(text->getConnectToBrainordinate())); QString annotationElementName; switch (text->getFontSizeType()) { case AnnotationTextFontSizeTypeEnum::PERCENTAGE_OF_VIEWPORT_HEIGHT: annotationElementName = ELEMENT_PERCENT_SIZE_TEXT; break; case AnnotationTextFontSizeTypeEnum::POINTS: annotationElementName = ELEMENT_POINT_SIZE_TEXT; break; } CaretAssert( ! annotationElementName.isEmpty()); m_stream->writeStartElement(annotationElementName); m_stream->writeAttributes(attributes); writeCoordinate(text->getCoordinate(), ELEMENT_COORDINATE_ONE); /* * Write the text data. * Note: QXmlStreamWriter::writeCharacters() will replace special * characters (& " ' < >) with escape sequences */ m_stream->writeStartElement(ELEMENT_TEXT_DATA); m_stream->writeAttributes(textDataAttributes); m_stream->writeCharacters(text->getText()); m_stream->writeEndElement(); m_stream->writeEndElement(); } /* * Get the annotation's properties in XML attributes. * * @param annotation * The Annotation. * @param attributes * XML attributes to which properties are appended. */ void AnnotationFileXmlWriter::getAnnotationPropertiesAsAttributes(const Annotation* annotation, QXmlStreamAttributes& attributes) { CaretAssert(annotation); attributes.append(ATTRIBUTE_COORDINATE_SPACE, AnnotationCoordinateSpaceEnum::toName(annotation->getCoordinateSpace())); attributes.append(ATTRIBUTE_BACKGROUND_CARET_COLOR, CaretColorEnum::toName(annotation->getBackgroundColor())); float rgba[4]; annotation->getCustomBackgroundColor(rgba); attributes.append(ATTRIBUTE_BACKGROUND_CUSTOM_RGBA, realArrayToString(rgba, 4)); attributes.append(ATTRIBUTE_FOREGROUND_CARET_COLOR, CaretColorEnum::toName(annotation->getLineColor())); annotation->getCustomLineColor(rgba); attributes.append(ATTRIBUTE_FOREGROUND_CUSTOM_RGBA, realArrayToString(rgba, 4)); attributes.append(ATTRIBUTE_FOREGROUND_LINE_WIDTH, QString::number(annotation->getLineWidth())); attributes.append(ATTRIBUTE_TAB_INDEX, QString::number(annotation->getTabIndex())); attributes.append(ATTRIBUTE_WINDOW_INDEX, QString::number(annotation->getWindowIndex())); attributes.append(ATTRIBUTE_UNIQUE_KEY, QString::number(annotation->getUniqueKey())); } /* * Get the two-dim annotation's properties in XML attributes. * * @param shape * The two-dim annotation. * @param attributes * XML attributes to which properties are appended. */ void AnnotationFileXmlWriter::getTwoDimAnnotationPropertiesAsAttributes(const AnnotationTwoDimensionalShape* shape, QXmlStreamAttributes& attributes) { CaretAssert(shape); getAnnotationPropertiesAsAttributes(shape, attributes); attributes.append(ATTRIBUTE_WIDTH, QString::number(shape->getWidth())); attributes.append(ATTRIBUTE_HEIGHT, QString::number(shape->getHeight())); attributes.append(ATTRIBUTE_ROTATION_ANGLE, QString::number(shape->getRotationAngle())); } /** * Write the given two dimensional annotation in XML. * * @param shape * The two-dimensional annotation. * @param annotationXmlElement * The XML element for the annotation. */ void AnnotationFileXmlWriter::writeTwoDimensionalAnnotation(const AnnotationTwoDimensionalShape* shape, const QString& annotationXmlElement) { CaretAssert(shape); QXmlStreamAttributes attributes; getTwoDimAnnotationPropertiesAsAttributes(shape, attributes); m_stream->writeStartElement(annotationXmlElement); m_stream->writeAttributes(attributes); writeCoordinate(shape->getCoordinate(), ELEMENT_COORDINATE_ONE); m_stream->writeEndElement(); } /** * Write the given one dimensional annotation in XML. * * @param shape * The one-dimensional annotation. * @param annotationXmlElement * The XML element for the annotation. */ void AnnotationFileXmlWriter::writeOneDimensionalAnnotation(const AnnotationOneDimensionalShape* shape, const QString& annotationXmlElement) { CaretAssert(shape); QXmlStreamAttributes attributes; getAnnotationPropertiesAsAttributes(shape, attributes); const AnnotationLine* line = dynamic_cast(shape); if (line != NULL) { attributes.append(ATTRIBUTE_LINE_END_ARROW, AString::fromBool(line->isDisplayEndArrow())); attributes.append(ATTRIBUTE_LINE_START_ARROW, AString::fromBool(line->isDisplayStartArrow())); } m_stream->writeStartElement(annotationXmlElement); m_stream->writeAttributes(attributes); writeCoordinate(shape->getStartCoordinate(), ELEMENT_COORDINATE_ONE); writeCoordinate(shape->getEndCoordinate(), ELEMENT_COORDINATE_TWO); m_stream->writeEndElement(); } /** * Write the given annotation coordinate in XML. * * @param coordinate * The annotation coordinate. * @param coordinateXmlElement * The XML element for the annotation coordinate. */ void AnnotationFileXmlWriter::writeCoordinate(const AnnotationCoordinate* coordinate, const QString& coordinateXmlElement) { CaretAssert(coordinate); float xyz[3]; coordinate->getXYZ(xyz); StructureEnum::Enum structure = StructureEnum::INVALID; int32_t numberOfNodes = -1; int32_t nodeIndex = -1; float nodeOffset = AnnotationCoordinate::getDefaultSurfaceOffsetLength(); AnnotationSurfaceOffsetVectorTypeEnum::Enum surfaceOffsetVectorType = AnnotationSurfaceOffsetVectorTypeEnum::CENTROID_THRU_VERTEX; coordinate->getSurfaceSpace(structure, numberOfNodes, nodeIndex, nodeOffset, surfaceOffsetVectorType); m_stream->writeStartElement(coordinateXmlElement); m_stream->writeAttribute(ATTRIBUTE_COORD_X, realToString(xyz[0])); m_stream->writeAttribute(ATTRIBUTE_COORD_Y, realToString(xyz[1])); m_stream->writeAttribute(ATTRIBUTE_COORD_Z, realToString(xyz[2])); m_stream->writeAttribute(ATTRIBUTE_COORD_SURFACE_STRUCTURE, StructureEnum::toName(structure)); m_stream->writeAttribute(ATTRIBUTE_COORD_SURFACE_NUMBER_OF_NODES, QString::number(numberOfNodes)); m_stream->writeAttribute(ATTRIBUTE_COORD_SURFACE_NODE_INDEX, QString::number(nodeIndex)); m_stream->writeAttribute(ATTRIBUTE_COORD_SURFACE_NODE_OFFSET, realToString(nodeOffset)); m_stream->writeAttribute(ATTRIBUTE_COORD_SURFACE_NODE_OFFSET_VECTOR_TYPE, AnnotationSurfaceOffsetVectorTypeEnum::toName(surfaceOffsetVectorType)); m_stream->writeEndElement(); } /** * Convert a real value to a string format. * * @param value * Value that is converted * @return * String format for value. */ QString AnnotationFileXmlWriter::realToString(const float value) const { return QString::number(value, 'f', 6); } /** * Convert an array of real values to string format. * * @param values * Array of values that are converted * @return * String format for values */ QString AnnotationFileXmlWriter::realArrayToString(const float values[], const int32_t numberOfValues) const { QString text; for (int32_t i = 0; i < numberOfValues; i++) { if (i > 0) { text.append(";"); } text.append(realToString(values[i])); } return text; } connectome-workbench-1.2.3+git41-gc4c6c90/src/Files/AnnotationFileXmlWriter.h000066400000000000000000000075401300200146000265550ustar00rootroot00000000000000#ifndef __ANNOTATION_FILE_XML_WRITER_H__ #define __ANNOTATION_FILE_XML_WRITER_H__ /*LICENSE_START*/ /* * Copyright (C) 2015 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 "AnnotationFileXmlFormatBase.h" #include "CaretPointer.h" class QXmlStreamAttributes; class QXmlStreamWriter; namespace caret { class Annotation; class AnnotationBox; class AnnotationCoordinate; class AnnotationFile; class AnnotationGroup; class AnnotationImage; class AnnotationLine; class AnnotationOneDimensionalShape; class AnnotationOval; class AnnotationText; class AnnotationTwoDimensionalShape; class XmlStreamWriterHelper; class AnnotationFileXmlWriter : public AnnotationFileXmlFormatBase { public: AnnotationFileXmlWriter(); virtual ~AnnotationFileXmlWriter(); void writeFile(const AnnotationFile* annotationFile); void writeFileToString(const AnnotationFile* annotationFile, QString& fileContentString); // ADD_NEW_METHODS_HERE private: AnnotationFileXmlWriter(const AnnotationFileXmlWriter&); AnnotationFileXmlWriter& operator=(const AnnotationFileXmlWriter&); QString realToString(const float value) const; QString realArrayToString(const float values[], const int32_t numberOfValues) const; void getAnnotationPropertiesAsAttributes(const Annotation* annotation, QXmlStreamAttributes& attributes); void getTwoDimAnnotationPropertiesAsAttributes(const AnnotationTwoDimensionalShape* shape, QXmlStreamAttributes& attributes); void writeFileContentToXmlStreamWriter(const AnnotationFile* annotationFile, const QString& filename); void writeBox(const AnnotationBox* box); void writeCoordinate(const AnnotationCoordinate* coordinate, const QString& coordinateXmlElement); void writeGroup(const AnnotationGroup* group); void writeImage(const AnnotationImage* image); void writeLine(const AnnotationLine* line); void writeOneDimensionalAnnotation(const AnnotationOneDimensionalShape* shape, const QString& annotationXmlElement); void writeTwoDimensionalAnnotation(const AnnotationTwoDimensionalShape* shape, const QString& annotationXmlElement); void writeOval(const AnnotationOval* oval); void writeText(const AnnotationText* text); CaretPointer m_stream; CaretPointer m_streamHelper; // ADD_NEW_MEMBERS_HERE }; #ifdef __ANNOTATION_FILE_XML_WRITER_DECLARE__ #endif // __ANNOTATION_FILE_XML_WRITER_DECLARE__ } // namespace #endif //__ANNOTATION_FILE_XML_WRITER_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Files/Border.cxx000066400000000000000000001573021300200146000235570ustar00rootroot00000000000000 /*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 __BORDER_DECLARE__ #include "Border.h" #undef __BORDER_DECLARE__ #include #include #include #include #include #include #include #include "CaretAssert.h" #include "CaretLogger.h" #include "CaretPointer.h" #include "DataFileException.h" #include "GeodesicHelper.h" #include "MathFunctions.h" #include "SurfaceFile.h" #include "SurfaceProjectedItem.h" #include "SurfaceProjectionBarycentric.h" #include "XmlWriter.h" using namespace caret; using namespace std; /** * \class caret::Border * \brief A border is a connected line segment on a source or volume. */ /** * Constructor. */ Border::Border() : CaretObjectTracksModification() { m_copyOfBorderPriorToLastEditing = NULL; clear(); // m_color = CaretColorEnum::BLACK; // m_selectionClassNameModificationStatus = true; // name/class is new!! } /** * Create a new border using the given surface's node indices. * * @param borderName * Name for border. * @param surfaceFile * The surface file. * @param nodeIndices * Indices of the surface nodes. * @return * Pointer to the newly created border. */ Border* Border::newInstanceFromSurfaceNodes(const AString& borderName, const SurfaceFile* surfaceFile, std::vector& nodeIndices) { CaretAssert(surfaceFile); Border* border = new Border(); border->setName(borderName); const int32_t numNodes = static_cast(nodeIndices.size()); for (int32_t i = 0; i < numNodes; i++) { const int32_t nodeIndex = nodeIndices[i]; const float* xyz = surfaceFile->getCoordinate(nodeIndex); SurfaceProjectedItem* spi = new SurfaceProjectedItem(); spi->setStereotaxicXYZ(xyz); spi->setStructure(surfaceFile->getStructure()); border->addPoint(spi); } return border; } /** * Destructor. */ Border::~Border() { clear(); } /** * Copy constructor. * @param obj * Object that is copied. */ Border::Border(const Border& obj) : CaretObjectTracksModification(obj) { m_copyOfBorderPriorToLastEditing = NULL; copyHelperBorder(obj); } /** * Assignment operator. * @param obj * Data copied from obj to this. * @return * Reference to this object. */ Border& Border::operator=(const Border& obj) { if (this != &obj) { CaretObjectTracksModification::operator=(obj); copyHelperBorder(obj); } return *this; } /** * Helps with copying an object of this type. * @param obj * Object that is copied. */ void Border::copyHelperBorder(const Border& obj) { clear(); const int32_t numPoints = obj.getNumberOfPoints(); for (int32_t i = 0; i < numPoints; i++) { SurfaceProjectedItem* spi = new SurfaceProjectedItem(*obj.m_points[i]); addPoint(spi); } m_name = obj.m_name; m_className = obj.m_className; m_closed = obj.m_closed; clearModified(); setNameOrClassModified(); // new name/class so modified } /** * Clear the border. * Removes all points, resets names, etc. */ void Border::clear() { removeAllPoints(); m_closed = false; m_groupNameSelectionItem = NULL; m_classRgbaColor[0] = 0.0; m_classRgbaColor[1] = 0.0; m_classRgbaColor[2] = 0.0; m_classRgbaColor[3] = 1.0; m_classRgbaColorValid = false; m_nameRgbaColor[0] = 0.0; m_nameRgbaColor[1] = 0.0; m_nameRgbaColor[2] = 0.0; m_nameRgbaColor[3] = 1.0; m_nameRgbaColorValid = false; if (m_copyOfBorderPriorToLastEditing != NULL) { delete m_copyOfBorderPriorToLastEditing; m_copyOfBorderPriorToLastEditing = NULL; } m_name = ""; m_className = ""; setNameOrClassModified(); // new name/class so modified } /** * @return Structure to which this border is assigned. */ StructureEnum::Enum Border::getStructure() const { StructureEnum::Enum structure = StructureEnum::INVALID; if (m_points.empty() == false) { structure = m_points[0]->getStructure(); } return structure; } void Border::setStructure(const StructureEnum::Enum& structure) { int numPoints = getNumberOfPoints(); for (int i = 0; i < numPoints; ++i) { getPoint(i)->setStructure(structure); } } /** * @return Is the class RGBA color valid? */ bool Border::isClassRgbaValid() const { return m_classRgbaColorValid; } /** * Set then class RGBA color invalid. */ void Border::setClassRgbaInvalid() { m_classRgbaColorValid = false; } /** * @return The class RGBA color components * ranging zero to one. */ const float* Border::getClassRgba() const { return m_classRgbaColor; } /** * Get the class RGBA color components * ranging zero to one. */ void Border::getClassRgba(float rgba[4]) const { rgba[0] = m_classRgbaColor[0]; rgba[1] = m_classRgbaColor[1]; rgba[2] = m_classRgbaColor[2]; rgba[3] = m_classRgbaColor[3]; } /** * Set the RGBA color components assigned to the class. * @param rgba * Red, green, blue, alpha ranging zero to one. */ void Border::setClassRgba(const float rgba[3]) { m_classRgbaColor[0] = rgba[0]; m_classRgbaColor[1] = rgba[1]; m_classRgbaColor[2] = rgba[2]; m_classRgbaColor[3] = rgba[3]; m_classRgbaColorValid = true; } /** * @return Is the name RGBA color valid? */ bool Border::isNameRgbaValid() const { return m_nameRgbaColorValid; } /** * Set then name RGBA color invalid. */ void Border::setNameRgbaInvalid() { m_nameRgbaColorValid = false; } /** * @return The name RGBA color components * ranging zero to one. */ const float* Border::getNameRgba() const { return m_nameRgbaColor; } /** * Get the name RGBA color components * ranging zero to one. */ void Border::getNameRgba(float rgba[4]) const { rgba[0] = m_nameRgbaColor[0]; rgba[1] = m_nameRgbaColor[1]; rgba[2] = m_nameRgbaColor[2]; rgba[3] = m_nameRgbaColor[3]; } /** * Set the RGBA color components assigned to the name. * @param rgba * Red, green, blue, alpha ranging zero to one. */ void Border::setNameRgba(const float rgba[4]) { m_nameRgbaColor[0] = rgba[0]; m_nameRgbaColor[1] = rgba[1]; m_nameRgbaColor[2] = rgba[2]; m_nameRgbaColor[3] = rgba[3]; m_nameRgbaColorValid = true; } /** * @return True if all points are on the * same structure, else false. */ bool Border::verifyAllPointsOnSameStructure() const { const int32_t numPoints = getNumberOfPoints(); if (numPoints <= 1) { return true; } StructureEnum::Enum structure = m_points[0]->getStructure(); for (int32_t i = 1; i < numPoints; i++) { if (m_points[i]->getStructure() != structure) { return false; } } return true; } bool Border::verifyForSurfaceNumberOfNodes(const int32_t& numNodes) const { int32_t numPoints = getNumberOfPoints(); for (int j = 0; j < numPoints; ++j) { const SurfaceProjectionBarycentric* thisProj = getPoint(j)->getBarycentricProjection();//addPoint makes sure these are always valid const int32_t* nodes = thisProj->getTriangleNodes(); for (int k = 0; k < 3; ++k) { if (nodes[k] >= numNodes) { return false; } } } return true; } /** * Remove all points in this border. */ void Border::removeAllPoints() { const int32_t numPoints =getNumberOfPoints(); for (int32_t i = 0; i < numPoints; i++) { delete m_points[i]; } m_points.clear(); setModified(); } /** * @return the name of the border. */ AString Border::getName() const { return m_name; } /** * Set the name of the border. * @param name * New name for border. */ void Border::setName(const AString& name) { if (m_name != name) { m_name = name; setModified(); setNameOrClassModified(); } } /** * @return the class name of the border. */ AString Border::getClassName() const { return m_className; } /** * Set the class name of the border. * @param className * New class name for border. */ void Border::setClassName(const AString& className) { if (m_className != className) { m_className = className; setModified(); setNameOrClassModified(); } } /** * @return Number of points in the border. */ int32_t Border::getNumberOfPoints() const { return m_points.size(); } /** * Get the border point at the given index. * @param indx * Index of desired border point. * @return * Pointer to border point. */ const SurfaceProjectedItem* Border::getPoint(const int32_t indx) const { CaretAssertVectorIndex(m_points, indx); return m_points[indx]; } /** * Get the border point at the given index. * @param indx * Index of desired border point. * @return * Pointer to border point. */ SurfaceProjectedItem* Border::getPoint(const int32_t indx) { CaretAssertVectorIndex(m_points, indx); return m_points[indx]; } /** * Returns the index of the border point nearest * the given XYZ coordinate and within the * given maximum distance. * * @param surfaceFile * Surface file used for unprojecting border points * and producing XYZ coordinates. * @param xyz * The XYZ coordinates for which nearest border * point is desired. * @param maximumDistance * Border points searched are limited to those * within this distance from the XYZ coordinate. * @param distanceToNearestPointOut * If a point is found within the maximum distance * @return * Index of nearest border point or negative if * no border points is within the maximum distance. */ int32_t Border::findPointIndexNearestXYZ(const SurfaceFile* surfaceFile, const float xyz[3], const float maximumDistance, float& distanceToNearestPointOut) const { CaretAssert(surfaceFile); const int32_t numPoints = getNumberOfPoints(); if (numPoints <= 0) { return -1; } if (surfaceFile->getStructure() != getStructure()) { return -1; } int32_t nearestIndex = -1; float nearestDistanceSQ = maximumDistance * maximumDistance; float pointXYZ[3]; for (int32_t i = 0; i < numPoints; i++) { if (m_points[i]->getProjectedPosition(*surfaceFile, pointXYZ, true)) { const float distSQ = MathFunctions::distanceSquared3D(xyz, pointXYZ); if (distSQ <= nearestDistanceSQ) { nearestDistanceSQ = distSQ; nearestIndex = i; } } } if (nearestIndex >= 0) { distanceToNearestPointOut = std::sqrt(nearestDistanceSQ); } return nearestIndex; } /** * Add a point to the border. NOTE: the border * takes ownership of the point and will delete * it. After calling this method DO NOT ever * use the point passed to this method. * * @param point * Point that is added to the border. */ void Border::addPoint(SurfaceProjectedItem* point) { if (m_points.size() != 0 && m_points[0]->getStructure() != point->getStructure()) { delete point;//keep our word and handle deleting the argument throw DataFileException("attempt to add point of different structure to a border"); } if (!point->getBarycentricProjection()->isValid()) { delete point; throw DataFileException("attempt to add point without valid barycentric projection to border"); } const int32_t* nodes = point->getBarycentricProjection()->getTriangleNodes(); for (int k = 0; k < 3; ++k) { if (nodes[k] < 0) { delete point; throw DataFileException("attempt to add point using negative node number"); } } m_points.push_back(point); setModified(); } /** * Add copies of points from the given border starting * at startPointIndex and adding a total of pointCount * points. * * @param border * Border from which points are copied. * @param startPointIndex * Index of first point that is copied from border. * If this value is negative, points will be copied * starting from the first point in the border. * @param pointCount * Number of points that are copied. If this value * is negative all of the remaining points in the * border are copied. If zero, none are copied. */ void Border::addPoints(const Border* border, const int32_t startPointIndex, const int32_t pointCount) { CaretAssert(border); const int32_t startIndex = (startPointIndex >= 0) ? startPointIndex : 0; const int32_t endIndex = (pointCount >= 0) ? (startIndex + pointCount) : border->getNumberOfPoints(); for (int32_t i = startIndex; i < endIndex; i++) { SurfaceProjectedItem* spi = new SurfaceProjectedItem(*border->getPoint(i)); addPoint(spi); } } bool Border::isClosed() const { return m_closed; } void Border::setClosed(const bool& closed) { m_closed = closed; } /** * Add points to the border so that the last point * connects to the first point. */ void Border::addPointsToCloseBorderWithGeodesic(const SurfaceFile* surfaceFile) { const int32_t numberOfPoints = getNumberOfPoints(); if (numberOfPoints < 3) { return; } /* * Index of surface node nearest first border point */ float firstBorderPointXYZ[3]; m_points[0]->getProjectedPosition(*surfaceFile, firstBorderPointXYZ, true); const int firstNodeIndex = surfaceFile->closestNode(firstBorderPointXYZ); if (firstNodeIndex < 0) { return; } /* * Index of surface node nearest last border point */ float lastBorderPointXYZ[3]; m_points[numberOfPoints - 1]->getProjectedPosition(*surfaceFile, lastBorderPointXYZ, true); const int lastNodeIndex = surfaceFile->closestNode(lastBorderPointXYZ); if (lastNodeIndex < 0) { return; } /* * Geodesics from node nearest last border point */ std::vector nodeParents; std::vector nodeDistances; CaretPointer geoHelp = surfaceFile->getGeodesicHelper(); geoHelp->getGeoFromNode(lastNodeIndex, nodeDistances, nodeParents, true); /* * Get path along border points */ const int32_t numberOfSurfaceNodes = surfaceFile->getNumberOfNodes(); std::vector pathFromFirstNodeToLastNode; int32_t geoNodeIndex = firstNodeIndex; int32_t failCounter = 0; while (geoNodeIndex >= 0) { geoNodeIndex = nodeParents[geoNodeIndex]; if (geoNodeIndex == lastNodeIndex) { geoNodeIndex = -1; } else if (geoNodeIndex >= 0) { pathFromFirstNodeToLastNode.push_back(geoNodeIndex); } failCounter ++; if (failCounter > numberOfSurfaceNodes) { CaretLogWarning("Geodesic path for closing border failed."); pathFromFirstNodeToLastNode.clear(); } } /* * Add points to border. */ const float triangleAreas[3] = { 1.0, 0.0, 0.0 }; const StructureEnum::Enum structure = surfaceFile->getStructure(); const int32_t numNewPoints = static_cast(pathFromFirstNodeToLastNode.size()); for (int32_t i = (numNewPoints - 1); i >= 0; i--) { const int32_t nodeIndex = pathFromFirstNodeToLastNode[i]; const float* xyz = surfaceFile->getCoordinate(nodeIndex); SurfaceProjectedItem* spi = new SurfaceProjectedItem(); spi->setStereotaxicXYZ(xyz); spi->setStructure(structure); SurfaceProjectionBarycentric* bp = spi->getBarycentricProjection(); bp->setTriangleAreas(triangleAreas); const int32_t triangleNodes[3] = { nodeIndex, nodeIndex, nodeIndex }; bp->setTriangleNodes(triangleNodes); bp->setValid(true); addPoint(spi); } } /** * Remove the point at the given index. * @param indx * Index of point for removal. */ void Border::removePoint(const int32_t indx) { CaretAssertVectorIndex(m_points, indx); delete m_points[indx]; m_points.erase(m_points.begin() + indx); setModified(); } /** * Remove the first point from the border. */ void Border::removeFirstPoint() { const int numPoints = getNumberOfPoints(); if (numPoints > 0) { removePoint(0); } } /** * Remove the last point from the border. */ void Border::removeLastPoint() { const int numPoints = getNumberOfPoints(); if (numPoints > 0) { removePoint(numPoints - 1); } } /** * Reverse the order of points in a border. */ void Border::reverse() { std::reverse(m_points.begin(), m_points.end()); setModified(); } /** * Revise a border by extending from a of point border. * * @param surfaceFile * Surface on which border extension is performed. * @param pointIndex * Point nearest the first point in the segment. * @param segment * A border segment containing the extension. * @throws BorderException * If there is an error revising the border. */ void Border::reviseExtendFromPointIndex(SurfaceFile* surfaceFile, const int32_t pointIndex, const Border* segment) { const int32_t numPoints = getNumberOfPoints(); if (numPoints <= 2) { throw BorderException("Border being update contains less than two points"); } const int numberOfSegmentPoints = segment->getNumberOfPoints(); if (numberOfSegmentPoints <= 0) { throw BorderException("Border segment for extending contains no points"); } if ((pointIndex < 0) || (pointIndex >= numPoints)) { throw BorderException("Point index for extending border is invalid."); } /* * Copy the border just in case something goes wrong */ /* * Lengths from point index to start and end points */ const float distToStart = getSegmentLength(surfaceFile, 0, pointIndex); const float distToEnd = getSegmentLength(surfaceFile, pointIndex, numPoints - 1); /* * Create a temporary border */ Border tempBorder; /* * Add on to start or ending end of border */ if (distToStart < distToEnd) { /* * Reverse the new segment and it becomes the first part of the border */ Border segmentCopy = *segment; segmentCopy.reverse(); tempBorder.addPoints(&segmentCopy); /* * Add points from this border starting AFTER pointIndex * to the last point) */ const int32_t startPointIndex = pointIndex + 1; if (startPointIndex < numPoints) { tempBorder.addPoints(this, startPointIndex); } } else { /* * Add points from this border from the first point to * the point BEFORE pointIndex */ const int32_t pointCount = pointIndex; if (pointCount > 0) { tempBorder.addPoints(this, 0, pointCount); } /* * Add the new segment */ tempBorder.addPoints(segment); } replacePointsWithUndoSaving(&tempBorder); } /** * Revise a border by extending from the end of a border. * * @param surfaceFile * Surface on which border extension is performed. * @param segment * A border segment containing the extension. * @throws BorderException * If there is an error revising the border. */ void Border::reviseExtendFromEnd(SurfaceFile* surfaceFile, const Border* segment) { const int32_t numPoints = getNumberOfPoints(); if (numPoints <= 0) { throw BorderException("Border being update contains no points"); } const int numberOfSegmentPoints = segment->getNumberOfPoints(); if (numberOfSegmentPoints <= 0) { throw BorderException("Border segment for extending contains no points"); } /* * Get coordinate of first point in new segment */ float segmentStartXYZ[3]; if (segment->getPoint(0)->getProjectedPosition(*surfaceFile, segmentStartXYZ, true) == false) { throw BorderException("First point in extending segment has invalid coordinate. Redraw."); } /* * Find point in this border nearest start of new segment */ const float distanceTolerance = 5.0; float distanceToStartOfNewSegment = 0.0; const int32_t borderPointNearestNewSegmentStart = findPointIndexNearestXYZ(surfaceFile, segmentStartXYZ, distanceTolerance, distanceToStartOfNewSegment); if (borderPointNearestNewSegmentStart < 0) { throw BorderException("New segment does not start near an existing border"); } /* * Get distance from both ends of existing border to first * point in new segment */ float borderStartXYZ[3]; getPoint(0)->getProjectedPosition(*surfaceFile, borderStartXYZ, true); const float distToStart = MathFunctions::distance3D(borderStartXYZ, segmentStartXYZ); float borderEndXYZ[3]; getPoint(numPoints - 1)->getProjectedPosition(*surfaceFile, borderEndXYZ, true); const float distToEnd = MathFunctions::distance3D(borderEndXYZ, segmentStartXYZ); /* * Add on to start or ending end of border */ int32_t startPointIndex = -1; int32_t endPointIndex = -1; bool reverseOrderFlag = false; if (distToStart < distToEnd) { if (distToStart > distanceTolerance) { throw BorderException("New segment does not start near the end of a border."); } endPointIndex = borderPointNearestNewSegmentStart; reverseOrderFlag = true; } else { if (distToEnd > distanceTolerance) { throw BorderException("New segment does not start near the end of a border."); } startPointIndex = borderPointNearestNewSegmentStart; } /* * If needed, swap point indices * if (reverseOrderFlag) { std::swap(startPointIndex, endPointIndex); } */ /* * Create a temporary border */ Border tempBorder; /* * Add in points prior to updated points */ if (startPointIndex >= 0) { tempBorder.addPoints(this, 0, startPointIndex); } /* * Add new points */ Border segmentCopy = *segment; if (reverseOrderFlag) { segmentCopy.reverse(); } tempBorder.addPoints(&segmentCopy); /* * Add in points after updated points */ if (endPointIndex >= 0) { tempBorder.addPoints(this, (endPointIndex + 1)); } replacePointsWithUndoSaving(&tempBorder); } /** * Revise a border by erasing from the end of a border. * * @param surfaceFile * Surface on which border erasing is performed. * @param segment * A border segment containing the erasing. * @throws BorderException * If there is an error revising the border. */ void Border::reviseEraseFromEnd(SurfaceFile* surfaceFile, const Border* segment) { /* * Get coordinate of first and last points in the segment */ const int numberOfSegmentPoints = segment->getNumberOfPoints(); if (numberOfSegmentPoints <= 0) { throw BorderException("Border segment for erasing contains no points"); } float segmentStartXYZ[3]; if (segment->getPoint(0)->getProjectedPosition(*surfaceFile, segmentStartXYZ, true) == false) { throw BorderException("First point in erase segment has invalid coordinate. Redraw."); } float segmentEndXYZ[3]; if (segment->getPoint(numberOfSegmentPoints - 1)->getProjectedPosition(*surfaceFile, segmentEndXYZ, true) == false) { throw BorderException("End point in erase segment has invalid coordinate. Redraw."); } const float tolerance = 10.0; /* * Find points in this border nearest the first and * last points in the erase segment */ float distanceToStartPoint = 0.0; int32_t startPointIndex = findPointIndexNearestXYZ(surfaceFile, segmentStartXYZ, tolerance, distanceToStartPoint); if (startPointIndex < 0) { throw BorderException("Start of segment drawn for erasing is not close enough to existing border"); } float distanceToEndPoint = 0.0; int32_t endPointIndex = findPointIndexNearestXYZ(surfaceFile, segmentEndXYZ, tolerance, distanceToEndPoint); if (endPointIndex < 0) { throw BorderException("End of segment drawn for erasing is not close enough to existing border"); } /* * If needed, swap point indices */ const bool reverseOrderFlag = (startPointIndex > endPointIndex); if (reverseOrderFlag) { std::swap(startPointIndex, endPointIndex); } /* * Create a temporary border */ Border tempBorder; /* * Add in points prior to updated points */ if (startPointIndex >= 0) { tempBorder.addPoints(this, 0, startPointIndex); } /* * Add in points after updated points */ if (endPointIndex >= 0) { tempBorder.addPoints(this, (endPointIndex + 1)); } saveBorderForUndoEditing(); replacePoints(&tempBorder); } /** * Revise a border by replacing a segment in a border. * * @param surfaceFile * Surface on which border segment replacement is performed. * @param segment * A border containing the new segment. * @throws BorderException * If there is an error replacing the segment in the border. */ void Border::reviseReplaceSegment(SurfaceFile* surfaceFile, const Border* segment) { /* * Get coordinate of first and last points in the segment */ const int numberOfSegmentPoints = segment->getNumberOfPoints(); if (numberOfSegmentPoints < 2) { throw BorderException("Border segment for replacing contains less than 2 points"); } float segmentStartXYZ[3]; if (segment->getPoint(0)->getProjectedPosition(*surfaceFile, segmentStartXYZ, true) == false) { throw BorderException("First point in replace segment has invalid coordinate. Redraw."); } float segmentEndXYZ[3]; if (segment->getPoint(numberOfSegmentPoints - 1)->getProjectedPosition(*surfaceFile, segmentEndXYZ, true) == false) { throw BorderException("End point in replace segment has invalid coordinate. Redraw."); } const float tolerance = 10.0; /* * Locate points in this border that are nearest the start * and end points in the new border segment. */ float distanceOfFirstSegmentPointToThisBorder = 0.0; int32_t lowestPointIndex = findPointIndexNearestXYZ(surfaceFile, segmentStartXYZ, tolerance, distanceOfFirstSegmentPointToThisBorder); if (lowestPointIndex < 0) { throw BorderException("Start of segment drawn for replacing is not close enough to existing border"); } float distanceOfLastSegmentPointToThisBorder = 0.0; int32_t highestPointIndex = findPointIndexNearestXYZ(surfaceFile, segmentEndXYZ, tolerance, distanceOfLastSegmentPointToThisBorder); if (highestPointIndex < 0) { throw BorderException("End of segment drawn for replacing is not close enough to existing border"); } /* * Swap lowest and highest point indexes so that lowest < highest */ if (lowestPointIndex > highestPointIndex) { std::swap(lowestPointIndex, highestPointIndex); } /* * Length in this border from lowest to highest point index */ const float lowestToHighestLength = getSegmentLength(surfaceFile, lowestPointIndex, highestPointIndex); /* * Length in this border from highest to lowest point index (assumes border * is closed. */ const float highestToLowestLength = getSegmentLength(surfaceFile, highestPointIndex, lowestPointIndex); /* * Create a temporary border */ Border newBorder; Border newBorderSecondSegment; /* * Keep part of this border that between low and high * indices that is the LONGEST */ if (lowestToHighestLength > highestToLowestLength) { /* * Keep part of this border from lowest index to highest index */ const int32_t lowToHighCount = highestPointIndex - lowestPointIndex + 1; newBorder.addPoints(this, lowestPointIndex, lowToHighCount); } else { /* * Keep segment from highest index to end */ const int32_t highToEndCount = getNumberOfPoints() - highestPointIndex; newBorderSecondSegment.addPoints(this, highestPointIndex, highToEndCount); /* * Keep segment from start to lowest point index * NOTE: Segments need to be separate otherwise * linear border will become closed. */ const int32_t startToLowCount = lowestPointIndex + 1; newBorder.addPoints(this, 0, startToLowCount); } const int32_t newBorderNumberOfPoints = newBorder.getNumberOfPoints(); if (newBorderNumberOfPoints > 0) { float newBorderLastXYZ[3]; if (newBorder.getPoint(newBorderNumberOfPoints - 1)->getProjectedPosition(*surfaceFile, newBorderLastXYZ, true)) { /* * Get position of first and last points in the new segment. */ float segmentFirstPointXYZ[3]; const bool validFirstPoint = segment->getPoint(0)->getProjectedPosition(*surfaceFile, segmentFirstPointXYZ, true); float segmentLastPointXYZ[3]; const bool validLastPoint = segment->getPoint(numberOfSegmentPoints - 1)->getProjectedPosition(*surfaceFile, segmentLastPointXYZ, true); if (validFirstPoint && validLastPoint) { /* * Distance to last point in border being created * to first and last point in new segment */ const float firstDistance = MathFunctions::distance3D(newBorderLastXYZ, segmentFirstPointXYZ); const float lastDistance = MathFunctions::distance3D(newBorderLastXYZ, segmentLastPointXYZ); /* * Remove endpoint(s) of new segment if they are very * close to a point in this border that is being updated */ Border trimmedSegment(*segment); const float distanceTolerance = 1.0; if (distanceOfFirstSegmentPointToThisBorder < distanceTolerance) { trimmedSegment.removeLastPoint(); } if (distanceOfLastSegmentPointToThisBorder < distanceTolerance) { trimmedSegment.removeFirstPoint(); } const int32_t numTrimmedSegmentPoints = trimmedSegment.getNumberOfPoints(); if (numTrimmedSegmentPoints > 0) { if (firstDistance < lastDistance) { /* * Add new segment onto the end of the existing border piece */ newBorder.addPoints(&trimmedSegment, 0, numTrimmedSegmentPoints); } else { /* * New segment is probably opposite orientation * (clockwise/counter-clockwise) that border that is * being edited. */ Border reversedSegment(trimmedSegment); reversedSegment.reverse(); newBorder.addPoints(&reversedSegment, 0, reversedSegment.getNumberOfPoints()); } } if (newBorderSecondSegment.getNumberOfPoints() > 0) { newBorder.addPoints(&newBorderSecondSegment, 0, newBorderSecondSegment.getNumberOfPoints()); } /* * Replace this border with the newly created border */ replacePointsWithUndoSaving(&newBorder); } else { throw BorderException("Border replacement failed: First or last point in new segment failed to project."); } } else { throw BorderException("Border replacement failed: Failed to project original border segment."); } } else { throw BorderException("Border replacement failed: No points were kept from original border."); } } ///** // * Revise a border by replacing a segment in a border. // * // * @param surfaceFile // * Surface on which border segment replacement is performed. // * @param segment // * A border containing the new segment. // * @throws BorderException // * If there is an error replacing the segment in the border. // */ //void //Border::reviseReplaceSegment(SurfaceFile* surfaceFile, // const Border* segment) //{ // /* // * Get coordinate of first and last points in the segment // */ // const int numberOfSegmentPoints = segment->getNumberOfPoints(); // if (numberOfSegmentPoints <= 0) { // throw BorderException("Border segment for erasing contains no points"); // } // float segmentStartXYZ[3]; // if (segment->getPoint(0)->getProjectedPosition(*surfaceFile, // segmentStartXYZ, // true) == false) { // throw BorderException("First point in erase segment has invalid coordinate. Redraw."); // } // // float segmentEndXYZ[3]; // if (segment->getPoint(numberOfSegmentPoints - 1)->getProjectedPosition(*surfaceFile, // segmentEndXYZ, // true) == false) { // throw BorderException("End point in erase segment has invalid coordinate. Redraw."); // } // // const float tolerance = 10.0; // // /* // * Find points in this border nearest the first and // * last points in the erase segment // */ // float distanceToStartPoint = 0.0; // int32_t segmentStartPointIndex = findPointIndexNearestXYZ(surfaceFile, // segmentStartXYZ, // tolerance, // distanceToStartPoint); // if (segmentStartPointIndex < 0) { // throw BorderException("Start of segment drawn for erasing is not close enough to existing border"); // } // float distanceToEndPoint = 0.0; // int32_t segmentEndPointIndex = findPointIndexNearestXYZ(surfaceFile, // segmentEndXYZ, // tolerance, // distanceToEndPoint); // if (segmentEndPointIndex < 0) { // throw BorderException("End of segment drawn for erasing is not close enough to existing border"); // } // // /* // * Make copy of segment // */ // Border replacementSegment(*segment); // // /* // * If needed, swap point indices and reverse order in segment // */ // const bool reverseOrderFlag = (segmentStartPointIndex > segmentEndPointIndex); // if (reverseOrderFlag) { // std::swap(segmentStartPointIndex, segmentEndPointIndex); // } // // /* // * Determine which segment in border to replace by using the // * segment with the minimal length. // */ // const float startToEndLength = getSegmentLength(surfaceFile, // segmentStartPointIndex, // segmentEndPointIndex); // const float endToStartLength = getSegmentLength(surfaceFile, // segmentEndPointIndex, // segmentStartPointIndex); // // /* // * Create a temporary border // */ // Border tempBorder; // // if (startToEndLength < endToStartLength) { // /* // * Add in points from start of border // */ // if (segmentStartPointIndex >= 0) { // tempBorder.addPoints(this, // 0, // segmentStartPointIndex); // } // // /* // * Add new points // */ // if (reverseOrderFlag) { // replacementSegment.reverse(); // } // tempBorder.addPoints(&replacementSegment); // // /* // * Add in points from end of border // */ // if (segmentEndPointIndex >= 0) { // tempBorder.addPoints(this, // (segmentEndPointIndex + 1)); // } // } // else { // /* // * Add new points // */ // tempBorder.addPoints(&replacementSegment); // // /* // * Add points from start to end // */ // const int32_t numSegmentPoints = (segmentEndPointIndex // - segmentStartPointIndex // + 1); // tempBorder.addPoints(this, // segmentStartPointIndex, // numSegmentPoints); // } // // replacePoints(&tempBorder); //} /** * Get the length of the border segment formed by the all of the points * using the starting and ending point indices (inclusively). * * When (segmentStartPointIndex < endPointIndex), the segment length is that * formed by the points from startPointIndex to endPointIndex. * * When (segmentStartPointIndex > endPointIndex), the segment length is that * formed by startPointIndex to the last point, last point to first point * and first point to endPointIndex. (Assumes border is a circular border). * * @param segmentStartPointIndex * Index of first border point in the border segement. * @param segmentEndPointIndex * Index of last border point in the border segment. * @param surfaceFile * Surface on which straightline distance between border points * is calculated. */ float Border::getSegmentLength(SurfaceFile* surfaceFile, const int32_t segmentStartPointIndex, const int32_t segmentEndPointIndex) { CaretAssert(surfaceFile); const int32_t numPoints = getNumberOfPoints(); if (numPoints <= 1) { return 0.0; } const int32_t lastPointIndex = numPoints - 1; CaretAssert((segmentStartPointIndex >= 0) && (segmentStartPointIndex < numPoints)); CaretAssert((segmentEndPointIndex >= 0) && (segmentEndPointIndex < numPoints)); float segmentLength = 0.0; if (segmentStartPointIndex > segmentEndPointIndex) { CaretAssert(segmentStartPointIndex <= lastPointIndex); const float d1 = getSegmentLength(surfaceFile, segmentStartPointIndex, lastPointIndex); float d2 = 0.0; { float xyz[3], xyzNext[3]; if (m_points[lastPointIndex]->getProjectedPosition(*surfaceFile, xyz, true) && m_points[0]->getProjectedPosition(*surfaceFile, xyzNext, true)) { d2 = MathFunctions::distance3D(xyz, xyzNext); } } CaretAssert(0 <= segmentEndPointIndex); const float d3 = getSegmentLength(surfaceFile, 0, segmentEndPointIndex); segmentLength = (d1 + d2 + d3); } else { for (int32_t i = segmentStartPointIndex; i < segmentEndPointIndex; i++) { float xyz[3], xyzNext[3]; const int32_t iNext = i + 1; CaretAssert(iNext < numPoints); if (m_points[i]->getProjectedPosition(*surfaceFile, xyz, true) && m_points[iNext]->getProjectedPosition(*surfaceFile, xyzNext, true)) { segmentLength += MathFunctions::distance3D(xyz, xyzNext); } } } return segmentLength; } /** * Replace the points in this border with points from the given border. * An "undo" copy of the border is also created. * * @param border Border whose points are copied into this border. */ void Border::replacePointsWithUndoSaving(const Border* border) { saveBorderForUndoEditing(); replacePoints(border); } /** * Replace the points in this border with * the given border. * @param border Border whose points are copied * into this border. */ void Border::replacePoints(const Border* border) { removeAllPoints(); const int32_t numPoints = border->getNumberOfPoints(); for (int i = 0; i < numPoints; i++) { SurfaceProjectedItem* spi = new SurfaceProjectedItem(*border->getPoint(i)); addPoint(spi); } } /** * Set modification status of name/class to modified. * * Name/Class modification status is used * by the selection controls that display * borders based upon selected classes and * names. */ void Border::setNameOrClassModified() { m_groupNameSelectionItem = NULL; m_nameRgbaColorValid = false; m_classRgbaColorValid = false; } /** * Set the selection item for the group/name hierarchy. * * @param item * The selection item from the group/name hierarchy. */ void Border::setGroupNameSelectionItem(GroupAndNameHierarchyItem* item) { m_groupNameSelectionItem = item; } /** * @return The selection item for the Group/Name selection hierarchy. * May be NULL in some circumstances. */ const GroupAndNameHierarchyItem* Border::getGroupNameSelectionItem() const { return m_groupNameSelectionItem; } /** * Save the border for undo editing. */ void Border::saveBorderForUndoEditing() { if (m_copyOfBorderPriorToLastEditing == NULL) { m_copyOfBorderPriorToLastEditing = new Border(*this); } else { m_copyOfBorderPriorToLastEditing->replacePoints(this); } } /** * @return True if last editing of border can be "undone". */ bool Border::isUndoBorderValid() const { if (m_copyOfBorderPriorToLastEditing != NULL) { return true; } return false; } /** * Undo the last editing of this border. */ void Border::undoLastBorderEditing() { if (m_copyOfBorderPriorToLastEditing != NULL) { replacePoints(m_copyOfBorderPriorToLastEditing); delete m_copyOfBorderPriorToLastEditing; m_copyOfBorderPriorToLastEditing = NULL; } } /** * Get a description of this object's content. * @return String describing this object's content. */ AString Border::toString() const { return "Border " + m_name; } /** * Write the border to the XML Writer. * @param xmlWriter * Writer for XML output. */ void Border::writeAsXML(XmlWriter& xmlWriter) { xmlWriter.writeStartElement(XML_TAG_BORDER); xmlWriter.writeElementCharacters(XML_TAG_NAME, m_name); if (m_className.isEmpty() == false) { xmlWriter.writeElementCharacters(XML_TAG_CLASS_NAME, m_className); } const int32_t numPoints = getNumberOfPoints(); for (int32_t i = 0; i < numPoints; i++) { m_points[i]->writeAsXML(xmlWriter); } xmlWriter.writeEndElement(); } void Border::writeXML3(QXmlStreamWriter& xml) const { xml.writeStartElement("BorderPart"); xml.writeAttribute("Closed", (isClosed() ? "True" : "False")); int numPoints = getNumberOfPoints(); xml.writeStartElement("Vertices"); for (int p = 0; p < numPoints; ++p) { const SurfaceProjectionBarycentric* thisBary = getPoint(p)->getBarycentricProjection(); const int32_t* nodes = thisBary->getTriangleNodes(); xml.writeCharacters(AString::number(nodes[0]) + " " + AString::number(nodes[1]) + " " + AString::number(nodes[2]) + "\n"); } xml.writeEndElement(); xml.writeStartElement("Weights"); for (int p = 0; p < numPoints; ++p) { const SurfaceProjectionBarycentric* thisBary = getPoint(p)->getBarycentricProjection(); const float* weights = thisBary->getTriangleAreas(); xml.writeCharacters(AString::number(weights[0]) + " " + AString::number(weights[1]) + " " + AString::number(weights[2]) + "\n"); } xml.writeEndElement(); xml.writeEndElement();//BorderPart } void Border::readXML1(QXmlStreamReader& xml) { clear(); CaretAssert(xml.isStartElement() && xml.name() == "Border"); bool haveName = false, haveClass = false, haveColorType = false; for (xml.readNext(); !xml.atEnd() && !xml.isEndElement(); xml.readNext()) { if (xml.isStartElement()) { QStringRef name = xml.name(); if (name == "Name") { if (haveName) throw DataFileException("multiple Name elements in one Border element"); m_name = xml.readElementText();//sets error on unexpected child element if (xml.hasError()) throw DataFileException("XML parsing error in Name: " + xml.errorString()); haveName = true; } else if (name == "ClassName") { if (haveClass) throw DataFileException("multiple ClassName elements in one Border element"); m_className = xml.readElementText();//sets error on unexpected child element if (xml.hasError()) throw DataFileException("XML parsing error in ClassName: " + xml.errorString()); haveClass = true; } else if (name == "ColorName") {//a gui setting that caret5 wrote into the border file, so ignore it if (haveColorType) throw DataFileException("multiple ColorName elements in one Border element"); xml.readElementText();//errors on unexpected element if (xml.hasError()) throw DataFileException("XML parsing error in ColorName: " + xml.errorString()); haveColorType = true; } else if (name == "SurfaceProjectedItem") { CaretPointer myItem(new SurfaceProjectedItem());//again, because current interface requires ownership passing of pointer myItem->readBorderFileXML1(xml); addPoint(myItem.releasePointer()); } else { throw DataFileException("unexpected element in Border: " + name.toString()); } } } if (xml.hasError()) throw DataFileException("XML parsing error in Border: " + xml.errorString()); CaretAssert(xml.isEndElement() && xml.name() == "Border"); if (getNumberOfPoints() > 1 && (*getPoint(0) == *getPoint(getNumberOfPoints() - 1))) { m_closed = true; removeLastPoint(); } } void Border::readXML3(QXmlStreamReader& xml) { clear(); CaretAssert(xml.isStartElement() && xml.name() == "BorderPart"); QXmlStreamAttributes myAttrs = xml.attributes(); if (!myAttrs.hasAttribute("Closed")) throw DataFileException("BorderPart element missing required attribute Closed"); QStringRef closedStr = myAttrs.value("Closed"); if (closedStr == "True") { setClosed(true); } else if (closedStr == "False") { setClosed(false); } else { throw DataFileException("unrecognized value for Closed attribute in BorderPart: " + closedStr.toString()); } vector vertices; vector weights; bool haveVertices = false, haveWeights = false; for (xml.readNext(); !xml.atEnd() && !xml.isEndElement(); xml.readNext()) { if (xml.isStartElement()) { QStringRef name = xml.name(); if (name == "Vertices") { if (haveVertices) throw DataFileException("multiple Vertices elements in one BorderPart element"); QString vertexText = xml.readElementText();//errors on unexpected element if (xml.hasError()) throw DataFileException("XML parsing error in Vertices: " + xml.errorString()); QStringList vertexStrings = vertexText.split(QRegExp("\\s+"), QString::SkipEmptyParts); int numItems = (int)vertexStrings.size(); if (numItems % 3 != 0) throw DataFileException("number of items in Vertices element text is not a multiple of 3"); for (int i = 0; i < numItems; ++i) { bool ok = false; int tempVal = vertexStrings[i].toInt(&ok); if (!ok) throw DataFileException("non-integer item in Vertices text: " + vertexStrings[i]); if (tempVal < 0) throw DataFileException("negative value in Vertices"); vertices.push_back(tempVal); } haveVertices = true; } else if (name == "Weights") { if (haveWeights) throw DataFileException("multiple Weights elements in one BorderPart element"); QString vertexText = xml.readElementText();//errors on unexpected element if (xml.hasError()) throw DataFileException("XML parsing error in Weights: " + xml.errorString()); QStringList vertexStrings = vertexText.split(QRegExp("\\s+"), QString::SkipEmptyParts); int numItems = (int)vertexStrings.size(); if (numItems % 3 != 0) throw DataFileException("number of items in Weights element text is not a multiple of 3"); for (int i = 0; i < numItems; ++i) { bool ok = false; float tempVal = vertexStrings[i].toFloat(&ok); if (!ok) throw DataFileException("non-numeric item in Weights text: " + vertexStrings[i]); if (tempVal < 0.0f) { CaretLogWarning("negative value in Weights, set to zero"); tempVal = 0.0f; } weights.push_back(tempVal); } haveWeights = true; } else { throw DataFileException("unexpected element in BorderPart: " + name.toString()); } } } if (xml.hasError()) throw DataFileException("XML parsing error in BorderPart: " + xml.errorString()); CaretAssert(xml.isEndElement() && xml.name() == "BorderPart"); if (!haveVertices || !haveWeights) throw DataFileException("BorderPart missing required Vertices or Weights element"); if (vertices.size() != weights.size()) throw DataFileException("Vertices and Weights don't contain the same number of elements"); int numPoints = (int)vertices.size() / 3; for (int i = 0; i < numPoints; ++i) { int i3 = i * 3; CaretPointer myItem(new SurfaceProjectedItem());//because addPoint takes ownership of a raw pointer myItem->setStructure(StructureEnum::ALL);//HACK: placeholder because structure is a file attribute in v3, not a border attribute SurfaceProjectionBarycentric* myBary = myItem->getBarycentricProjection(); myBary->setTriangleNodes(vertices.data() + i3); myBary->setTriangleAreas(weights.data() + i3); myBary->setValid(true);//signed distance from surface iniializes to 0 addPoint(myItem.releasePointer()); } } connectome-workbench-1.2.3+git41-gc4c6c90/src/Files/Border.h000066400000000000000000000153211300200146000231760ustar00rootroot00000000000000#ifndef __BORDER__H_ #define __BORDER__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 "BorderException.h" #include "CaretObjectTracksModification.h" #include "StructureEnum.h" #include "XmlException.h" class QXmlStreamReader; class QXmlStreamWriter; namespace caret { class GroupAndNameHierarchyItem; class SurfaceFile; class SurfaceProjectedItem; class XmlWriter; class Border : public CaretObjectTracksModification { public: Border(); virtual ~Border(); Border(const Border& obj); Border& operator=(const Border& obj); static Border* newInstanceFromSurfaceNodes(const AString& borderName, const SurfaceFile* surfaceFile, std::vector& nodeIndices); virtual AString toString() const; void clear(); AString getName() const; void setName(const AString& name); AString getClassName() const; void setClassName(const AString& name); StructureEnum::Enum getStructure() const; void setStructure(const StructureEnum::Enum& structure); bool verifyAllPointsOnSameStructure() const; bool verifyForSurfaceNumberOfNodes(const int32_t& numNodes) const; int32_t getNumberOfPoints() const; const SurfaceProjectedItem* getPoint(const int32_t indx) const; SurfaceProjectedItem* getPoint(const int32_t indx); int32_t findPointIndexNearestXYZ(const SurfaceFile* surfaceFile, const float xyz[3], const float maximumDistance, float& distanceToNearestPointOut) const; void addPoint(SurfaceProjectedItem* point); void addPoints(const Border* border, const int32_t startPointIndex = -1, const int32_t pointCount = -1); bool isClosed() const; void setClosed(const bool& closed); void addPointsToCloseBorderWithGeodesic(const SurfaceFile* surfaceFile); void removeAllPoints(); void removePoint(const int32_t indx); void removeFirstPoint(); void removeLastPoint(); void replacePointsWithUndoSaving(const Border* border); void reverse(); void reviseExtendFromPointIndex(SurfaceFile* surfaceFile, const int32_t pointIndex, const Border* segment); void reviseExtendFromEnd(SurfaceFile* surfaceFile, const Border* segment); void reviseEraseFromEnd(SurfaceFile* surfaceFile, const Border* segment); void reviseReplaceSegment(SurfaceFile* surfaceFile, const Border* segment); void writeAsXML(XmlWriter& xmlWriter); void writeXML3(QXmlStreamWriter& xml) const; void readXML1(QXmlStreamReader& xml); void readXML3(QXmlStreamReader& xml); void setGroupNameSelectionItem(GroupAndNameHierarchyItem* item); const GroupAndNameHierarchyItem* getGroupNameSelectionItem() const; bool isClassRgbaValid() const; void setClassRgbaInvalid(); const float* getClassRgba() const; void getClassRgba(float rgba[4]) const; void setClassRgba(const float rgba[4]); bool isNameRgbaValid() const; void setNameRgbaInvalid(); const float* getNameRgba() const; void getNameRgba(float rgba[4]) const; void setNameRgba(const float rgba[4]); bool isUndoBorderValid() const; void undoLastBorderEditing(); static const AString XML_TAG_BORDER; static const AString XML_TAG_NAME; static const AString XML_TAG_CLASS_NAME; private: void copyHelperBorder(const Border& obj); void setNameOrClassModified(); float getSegmentLength(SurfaceFile* surfaceFile, const int32_t startPointIndex, const int32_t endPointIndex); void replacePoints(const Border* border); void saveBorderForUndoEditing(); AString m_name; AString m_className; std::vector m_points; bool m_closed; /** RGBA color component assigned to border's class name */ float m_classRgbaColor[4]; /** RGBA color components assigned to border's class name validity */ bool m_classRgbaColorValid; /** RGBA color components assigned to focus' name */ float m_nameRgbaColor[4]; /** RGBA color components assigned to focus' name validity */ bool m_nameRgbaColorValid; /** Selection status of this border in the group/name hierarchy */ GroupAndNameHierarchyItem* m_groupNameSelectionItem; /** * When "this" border is edited, a copy of "this" is placed into * this member so that the border can be restored if the editing * was not satisfactory. */ Border* m_copyOfBorderPriorToLastEditing; }; #ifdef __BORDER_DECLARE__ const AString Border::XML_TAG_BORDER = "Border"; const AString Border::XML_TAG_CLASS_NAME = "ClassName"; const AString Border::XML_TAG_NAME = "Name"; #endif // __BORDER_DECLARE__ } // namespace #endif //__BORDER__H_ connectome-workbench-1.2.3+git41-gc4c6c90/src/Files/BorderException.cxx000066400000000000000000000034541300200146000254340ustar00rootroot00000000000000/*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 "BorderException.h" #include using namespace caret; /** * Constructor. * */ BorderException::BorderException() : CaretException() { this->initializeMembersBorderException(); } /** * Constructor. * * @param s Description of the exception. * */ BorderException::BorderException( const AString& s) : CaretException(s) { this->initializeMembersBorderException(); } /** * Copy Constructor. * @param e * Exception that is copied. */ BorderException::BorderException(const BorderException& e) : CaretException(e) { } /** * Assignment operator. * @param e * Exception that is copied. * @return * Copy of the exception. */ BorderException& BorderException::operator=(const BorderException& e) { if (this != &e) { CaretException::operator=(e); } return *this; } /** * Destructor */ BorderException::~BorderException() throw() { } void BorderException::initializeMembersBorderException() { } connectome-workbench-1.2.3+git41-gc4c6c90/src/Files/BorderException.h000066400000000000000000000026461300200146000250630ustar00rootroot00000000000000#ifndef __BORDER_EXCEPTION_H__ #define __BORDER_EXCEPTION_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 "CaretException.h" namespace caret { /** * An exception thrown during Border processing. */ class BorderException : public CaretException { public: BorderException(); BorderException(const AString& s); BorderException(const BorderException& e); BorderException& operator=(const BorderException& e); virtual ~BorderException() throw(); private: void initializeMembersBorderException(); }; } // namespace #endif // __BORDER_EXCEPTION_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Files/BorderFile.cxx000066400000000000000000002751741300200146000243670ustar00rootroot00000000000000 /*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 #include #define __BORDER_FILE_DECLARE__ #include "BorderFile.h" #undef __BORDER_FILE_DECLARE__ #include "Border.h" #include "BorderPointFromSearch.h" #include "CaretAssert.h" #include "CaretLogger.h" #include "DataFileContentInformation.h" #include "DataFileException.h" #include "GroupAndNameHierarchyModel.h" #include "FileAdapter.h" #include "FileInformation.h" #include "GiftiLabel.h" #include "GiftiLabelTable.h" #include "GiftiMetaData.h" #include "MathFunctions.h" #include "SurfaceFile.h" #include "SurfaceProjectedItem.h" #include "SurfaceProjectionBarycentric.h" #include "TextFile.h" #include "XmlAttributes.h" #include "XmlSaxParser.h" #include "XmlWriter.h" using namespace caret; using namespace std; /** * \class caret::BorderFile * \brief File containing borders. */ /** * Constructor. */ BorderFile::BorderFile() : CaretDataFile(DataFileTypeEnum::BORDER) { initializeBorderFile(); } /** * Destructor. */ BorderFile::~BorderFile() { delete m_classColorTable; delete m_nameColorTable; delete m_metadata; for (std::vector::iterator iter = m_borders.begin(); iter != m_borders.end(); iter++) { delete *iter; } m_borders.clear(); delete m_classNameHierarchy; } /** * Initialize members of a border file. */ void BorderFile::initializeBorderFile() { m_classColorTable = new GiftiLabelTable(); m_nameColorTable = new GiftiLabelTable(); m_classNameHierarchy = new GroupAndNameHierarchyModel(); m_metadata = new GiftiMetaData(); m_forceUpdateOfGroupAndNameHierarchy = true; m_structure = StructureEnum::ALL; m_numNodes = -1; } /** * Copy constructor. * @param obj * Object that is copied. */ BorderFile::BorderFile(const BorderFile& obj) : CaretDataFile(obj) { initializeBorderFile(); copyHelperBorderFile(obj); } /** * Assignment operator. * @param obj * Data copied from obj to this. * @return * Reference to this object. */ BorderFile& BorderFile::operator=(const BorderFile& obj) { if (this != &obj) { clear(); CaretDataFile::operator=(obj); copyHelperBorderFile(obj); } return *this; } /** * Helps with copying an object of this type. * @param obj * Object that is copied. */ void BorderFile::copyHelperBorderFile(const BorderFile& obj) { *m_classColorTable = *obj.m_classColorTable; *m_nameColorTable = *obj.m_nameColorTable; if (m_classNameHierarchy != NULL) { delete m_classNameHierarchy; } m_classNameHierarchy = new GroupAndNameHierarchyModel(); *m_metadata = *obj.m_metadata; const int32_t numBorders = obj.getNumberOfBorders(); for (int32_t i = 0; i < numBorders; i++) { m_borders.push_back(new Border(*obj.getBorder(i))); } m_forceUpdateOfGroupAndNameHierarchy = true; setModified(); } /** * @return Is this border file empty (contains zero borders)? */ bool BorderFile::isEmpty() const { return m_borders.empty(); } /** * @return The structure for this file. */ StructureEnum::Enum BorderFile::getStructure() const { return m_structure; } /** * @return Structures of all borders. In most cases, this method * is equivalent to getStructure(). However, there are some older, * obsolete border files that contain borders for multiple structures * in which case this returns the structures for those borders such as * CORTEX_LEFT and CORTEX_RIGHT. */ std::vector BorderFile::getAllBorderStructures() const { std::set uniqueStructures; const int32_t numBorders = getNumberOfBorders(); for (int32_t i = 0; i < numBorders; i++) { uniqueStructures.insert(getBorder(i)->getStructure()); } std::vector structures(uniqueStructures.begin(), uniqueStructures.end()); return structures; } /** * Create new border files each of which contains a border for a single structure. * If there is not a border file name for for a structure, no file will be * produced for that file and it is not considered an error. * * @param singleStructureFileNames * Each 'pair' is the filename for a structure. * @param structureNumberOfNodes * Each pair is the number of nodes for a structure. This parameter is * optional and when available, is used to validate the barycentric * node indices in a border. * @param singleStructureBorderFilesOut * On output contains border files for each of the structures. * @param errorMessageOut * If unsuccessful will contain description of error(s). * @return * True if successful and all files were created, else false. */ bool BorderFile::splitIntoSingleStructureFiles(const std::map& singleStructureFileNames, const std::map& structureNumberOfNodes, std::vector& singleStructureBorderFilesOut, AString& errorMessageOut) const { singleStructureBorderFilesOut.clear(); errorMessageOut.clear(); /* * Create single structure border files. */ std::map structureBorderFiles; for (std::map::const_iterator nameIter = singleStructureFileNames.begin(); nameIter != singleStructureFileNames.end(); nameIter++) { const StructureEnum::Enum structure = nameIter->first; if (StructureEnum::isSingleStructure(structure)) { /* * Create the border file and set the structure */ BorderFile* borderFile = new BorderFile(); borderFile->setFileName(nameIter->second); borderFile->setStructure(structure); /* * Set number of nodes for border file (if available) */ const std::map::const_iterator structNumNodesIter = structureNumberOfNodes.find(structure); if (structNumNodesIter != structureNumberOfNodes.end()) { const int32_t numNodes = structNumNodesIter->second; borderFile->setNumberOfNodes(numNodes); } /* * Add to border file for each structure. */ structureBorderFiles.insert(std::make_pair(structure, borderFile)); } else { errorMessageOut.appendWithNewLine("Structure " + StructureEnum::toGuiName(structure) + " is not a 'single' type structure."); } } /* * Copy borders to the appropriate single-structure files. */ const int32_t numBorders = getNumberOfBorders(); for (int32_t i = 0; i < numBorders; i++) { const Border* border = getBorder(i); CaretAssert(border); const StructureEnum::Enum structure = border->getStructure(); /* * Find output file with same structure as this border */ std::map::iterator fileIter = structureBorderFiles.find(structure); if (fileIter != structureBorderFiles.end()) { if (border->verifyAllPointsOnSameStructure()) { /* * Surface number of nodes may be available */ int32_t surfaceNumberOfNodes = 0; const std::map::const_iterator structNumNodesIter = structureNumberOfNodes.find(structure); if (structNumNodesIter != structureNumberOfNodes.end()) { surfaceNumberOfNodes = structNumNodesIter->second; } /* * If possible verify border node indices valid for structure */ bool copyBorderFlag = false; if (surfaceNumberOfNodes > 0) { if (border->verifyForSurfaceNumberOfNodes(surfaceNumberOfNodes)) { copyBorderFlag = true; } else { errorMessageOut.appendWithNewLine("Border index=" + AString::number(i) + " name=" + border->getName() + " contains an incompatible number of nodes for structure " + StructureEnum::toGuiName(structure)); } } else { /* * Unable to verify number of nodes so assume okay */ copyBorderFlag = true; } if (copyBorderFlag) { Border* borderCopy = new Border(*border); fileIter->second->addBorder(borderCopy); const GiftiLabel* nameLabel = m_nameColorTable->getLabelBestMatching(border->getName()); fileIter->second->getNameColorTable()->addLabel(nameLabel); const GiftiLabel* classLabel = m_classColorTable->getLabelBestMatching(border->getClassName()); fileIter->second->getClassColorTable()->addLabel(classLabel); } } else { errorMessageOut.appendWithNewLine("Border index=" + AString::number(i) + " name=" + border->getName() + " is an invalid border that contains points on multiple structures"); } } } if (errorMessageOut.isEmpty()) { /* * Success: return the single structure border files that were created. */ for (std::map::iterator fileIter = structureBorderFiles.begin(); fileIter != structureBorderFiles.end(); fileIter++) { singleStructureBorderFilesOut.push_back(fileIter->second); } return true; } /* * Had an error so delete any single structure border files that were created. */ for (std::map::iterator fileIter = structureBorderFiles.begin(); fileIter != structureBorderFiles.end(); fileIter++) { delete fileIter->second; } return false; } /** * Set the structure for this file. * @param structure * New structure for this file. */ void BorderFile::setStructure(const StructureEnum::Enum structure) { if (m_structure == StructureEnum::ALL && m_borders.size() != 0) {//not really sure what this should do, so throw an error for now throw DataFileException(getFileName(), "attempt to set structure on multi-structure border file"); } int numBorders = (int)m_borders.size(); for (int i = 0; i < numBorders; ++i) { Border* thisBorder = m_borders[i]; int numPoints = thisBorder->getNumberOfPoints(); for (int j = 0; j < numPoints; ++j) { thisBorder->getPoint(j)->setStructure(structure); } } m_structure = structure; setModified(); } /** * @return Get access to the file's metadata. */ GiftiMetaData* BorderFile::getFileMetaData() { return m_metadata; } /** * @return Get access to unmodifiable file's metadata. */ const GiftiMetaData* BorderFile::getFileMetaData() const { return m_metadata; } /** * Clear the border file. */ void BorderFile::clear() { CaretDataFile::clear(); m_classNameHierarchy->clear(); m_classColorTable->clear(); m_nameColorTable->clear(); m_metadata->clear(); const int32_t numBorders = getNumberOfBorders(); for (int32_t i = 0; i < numBorders; i++) { delete m_borders[i]; } m_borders.clear(); m_structure = StructureEnum::ALL; m_numNodes = -1; m_borderMDKeys.clear(); m_borderMDValues.clear(); } int32_t BorderFile::getNumberOfNodes() const { if (m_structure == StructureEnum::ALL) return -1;//TSC: i think multi-structure should always return -1 return m_numNodes; } void BorderFile::setNumberOfNodes(const int32_t& numNodes) { if (numNodes < 1) { throw DataFileException(getFileName(), "attempt to set non-positive number of vertices on border file"); } int numBorders = (int)m_borders.size(); for (int i = 0; i < numBorders; ++i) { if (!m_borders[i]->verifyForSurfaceNumberOfNodes(numNodes)) { throw DataFileException(getFileName(), "cannot set border file number of vertices less than the vertices used by its borders"); } } m_numNodes = numNodes;//even if we are currently multi-structure, remember the number of nodes that was set setModified(); } /** * If the number of nodes is not valid, it may indicate an old border file * that allows borders from multiple structures. So, examine all of the * borders and if ALL of the borders resized on the same structure, set the * number of nodes. * * @param structureToNodeCount * A map containing the number of nodes for each valid structure. * @throw DataFileException * If a border is not valid for its corresponding structure * (meaning, a node index used by a border is greater than the * number of nodes in the corresponding structure). */ void BorderFile::updateNumberOfNodesIfSingleStructure(const std::map& structureToNodeCount) { if (getNumberOfNodes() > 0) { return; } const int32_t numBorders = getNumberOfBorders(); /* * Verify that all borders in this file are for the same structure */ bool allStructuresMatchFlag = true; StructureEnum::Enum firstBorderStructure = StructureEnum::INVALID; for (int32_t i = 0; i < numBorders; i++) { const StructureEnum::Enum structure = m_borders[i]->getStructure(); if (i == 0) { firstBorderStructure = structure; } else { if (structure != firstBorderStructure) { allStructuresMatchFlag = false; break; } } } /* * If all of the borders in this file are for the same structure, * see if the number of nodes is available for the structure, and if so, * set the number of borders for the file. */ if (allStructuresMatchFlag) { if ((firstBorderStructure != StructureEnum::INVALID) && (firstBorderStructure != StructureEnum::ALL)) { const std::map::const_iterator iter = structureToNodeCount.find(firstBorderStructure); if (iter != structureToNodeCount.end()) { const int32_t numNodes = iter->second; setNumberOfNodes(numNodes); setStructure(firstBorderStructure); CaretLogInfo("Updated border file: " + getFileNameNoPath() + " structure=" + StructureEnum::toGuiName(firstBorderStructure) + " number-of-nodes=" + AString::number(numNodes)); } } } } /** * @return the number of borders. */ int32_t BorderFile::getNumberOfBorders() const { return m_borders.size(); } /** * Get the border at the given index. * @param indx * Index of the border. * @return * Border at the given index. */ Border* BorderFile::getBorder(const int32_t indx) { CaretAssertVectorIndex(m_borders, indx); return m_borders[indx]; } /** * Is the given border in this border file? * * @param border * The border being queried * @return * True if the border is in this file, else false. */ bool BorderFile::containsBorder(const Border* border) const { if (std::find(m_borders.begin(), m_borders.end(), border) != m_borders.end()) { return true; } return false; } /** * Get the border at the given index. * @param indx * Index of the border. * @return * Border at the given index. */ const Border* BorderFile::getBorder(const int32_t indx) const { CaretAssertVectorIndex(m_borders, indx); return m_borders[indx]; } /** * Find ALL borders that have one endpoint within the given distance * of the first point of the given border segment. * * @param displayGroup * Display group in which border is tested for display. * @param browserTabIndex * Tab index in which border is displayed. * @param surfaceFile * Surface file used for unprojection of border points. * @param borderSegment * The border segment. * @param maximumDistance * Maximum distance coordinate can be from a border point. * @param borderPointsOut * Contains result of search. */ void BorderFile::findAllBordersWithEndPointNearSegmentFirstPoint(const DisplayGroupEnum::Enum displayGroup, const int32_t browserTabIndex, const SurfaceFile* surfaceFile, const Border* borderSegment, const float maximumDistance, std::vector& borderPointsOut) const { CaretAssert(surfaceFile); CaretAssert(borderSegment); borderPointsOut.clear(); if (borderSegment->getNumberOfPoints() < 2) { return; } float segFirstXYZ[3]; if (! borderSegment->getPoint(0)->getProjectedPosition(*surfaceFile, segFirstXYZ, false)) { /* * Point did not unproject */ return; } BorderFile* nonConstBorderFile = const_cast(this); const int32_t numBorders = getNumberOfBorders(); for (int32_t borderIndex = 0; borderIndex < numBorders; borderIndex++) { Border* border = m_borders[borderIndex]; if (nonConstBorderFile->isBorderDisplayed(displayGroup, browserTabIndex, border) == false) { continue; } if (border->getStructure() == surfaceFile->getStructure()) { /* * Test first endpoint */ const int32_t numPoints = border->getNumberOfPoints(); float nearestPointDistanceSquared = -1.0; int32_t neartestPointIndex = -1; if (numPoints > 0) { float pointXYZ[3]; if (border->getPoint(0)->getProjectedPosition(*surfaceFile, pointXYZ, false)) { nearestPointDistanceSquared = MathFunctions::distanceSquared3D(pointXYZ, segFirstXYZ); neartestPointIndex = 0; } } /* * Test last endpoint */ if (numPoints > 1) { const int32_t lastPointIndex = numPoints - 1; float pointXYZ[3]; if (border->getPoint(lastPointIndex)->getProjectedPosition(*surfaceFile, pointXYZ, false)) { const float dist2 = MathFunctions::distanceSquared3D(pointXYZ, segFirstXYZ); if (nearestPointDistanceSquared >= 0.0) { if (dist2 < nearestPointDistanceSquared) { neartestPointIndex = lastPointIndex; nearestPointDistanceSquared = dist2; } } else { neartestPointIndex = lastPointIndex; nearestPointDistanceSquared = dist2; } } } if (neartestPointIndex >= 0) { const float nearestPointDistance = std::sqrt(nearestPointDistanceSquared); if (nearestPointDistance <= maximumDistance) { BorderPointFromSearch bpo; bpo.setData(const_cast(this), border, borderIndex, neartestPointIndex, nearestPointDistance); borderPointsOut.push_back(bpo); } } } } } /** * Find ALL borders that have any point within the given distance * of the first point of the given border segment. * * @param displayGroup * Display group in which border is tested for display. * @param browserTabIndex * Tab index in which border is displayed. * @param surfaceFile * Surface file used for unprojection of border points. * @param borderSegment * The border segment. * @param maximumDistance * Maximum distance coordinate can be from a border point. * @param borderPointsOut * Contains result of search. */ void BorderFile::findAllBordersWithAnyPointNearSegmentFirstPoint(const DisplayGroupEnum::Enum displayGroup, const int32_t browserTabIndex, const SurfaceFile* surfaceFile, const Border* borderSegment, const float maximumDistance, std::vector& borderPointsOut) const { CaretAssert(surfaceFile); CaretAssert(borderSegment); borderPointsOut.clear(); if (borderSegment->getNumberOfPoints() < 2) { return; } float segFirstXYZ[3]; if (! borderSegment->getPoint(0)->getProjectedPosition(*surfaceFile, segFirstXYZ, false)) { /* * Point did not unproject */ return; } BorderFile* nonConstBorderFile = const_cast(this); const int32_t numBorders = getNumberOfBorders(); for (int32_t borderIndex = 0; borderIndex < numBorders; borderIndex++) { Border* border = m_borders[borderIndex]; if (nonConstBorderFile->isBorderDisplayed(displayGroup, browserTabIndex, border) == false) { continue; } if (border->getStructure() == surfaceFile->getStructure()) { /* * Test all points */ float nearestPointDistanceSquared = std::numeric_limits::max(); int32_t nearestPointIndex = -1; const int32_t numPoints = border->getNumberOfPoints(); for (int32_t pointIndex = 0; pointIndex < numPoints; pointIndex++) { float pointXYZ[3]; if (border->getPoint(pointIndex)->getProjectedPosition(*surfaceFile, pointXYZ, false)) { const float distSQ = MathFunctions::distanceSquared3D(pointXYZ, segFirstXYZ); if (distSQ < nearestPointDistanceSquared) { nearestPointDistanceSquared = distSQ; nearestPointIndex = pointIndex; } } } if (nearestPointIndex >= 0) { const float nearestPointDistance = std::sqrt(nearestPointDistanceSquared); if (nearestPointDistance <= maximumDistance) { BorderPointFromSearch bpo; bpo.setData(const_cast(this), border, borderIndex, nearestPointIndex, nearestPointDistance); borderPointsOut.push_back(bpo); } } } } } /** * Find ALL borders that have ANY points within the given distance * of the two given coordinates. * * @param displayGroup * Display group in which border is tested for display. * @param browserTabIndex * Tab index in which border is displayed. * @param surfaceFile * Surface file used for unprojection of border points. * @param borderSegment * The border segment. * @param maximumDistance * Maximum distance coordinate can be from a border point. * @param borderPointsOut * Contains result of search. */ void BorderFile::findAllBordersWithPointsNearBothSegmentEndPoints(const DisplayGroupEnum::Enum displayGroup, const int32_t browserTabIndex, const SurfaceFile* surfaceFile, const Border* borderSegment, const float maximumDistance, std::vector& borderPointsOut) const { CaretAssert(surfaceFile); CaretAssert(borderSegment); borderPointsOut.clear(); if (borderSegment->getNumberOfPoints() < 2) { return; } float segFirstXYZ[3], segLastXYZ[3]; const int32_t segLpIndex = borderSegment->getNumberOfPoints() - 1; if (borderSegment->getPoint(0)->getProjectedPosition(*surfaceFile, segFirstXYZ, false) && borderSegment->getPoint(segLpIndex)->getProjectedPosition(*surfaceFile, segLastXYZ, false)) { /* OK - both points have valid coordinates */ } else { /* One or both points failed to project */ return; } BorderFile* nonConstBorderFile = const_cast(this); const int32_t numBorders = getNumberOfBorders(); for (int32_t borderIndex = 0; borderIndex < numBorders; borderIndex++) { Border* border = m_borders[borderIndex]; if (nonConstBorderFile->isBorderDisplayed(displayGroup, browserTabIndex, border) == false) { continue; } if (border->getStructure() == surfaceFile->getStructure()) { /* * Test first query point */ float distance1 = 0.0; const int32_t nearestIndex1 = border->findPointIndexNearestXYZ(surfaceFile, segFirstXYZ, maximumDistance, distance1); float distance2 = 0.0; const int32_t nearestIndex2 = border->findPointIndexNearestXYZ(surfaceFile, segLastXYZ, maximumDistance, distance2); if ((nearestIndex1 >= 0) && (nearestIndex2 >= 0)) { const float averageDistance = (distance1 + distance2) / 2.0; BorderPointFromSearch bpo; bpo.setData(const_cast(this), border, borderIndex, nearestIndex1, averageDistance); borderPointsOut.push_back(bpo); } } } } /** * Find ALL borders that have ANY points within the given * region of interest. Since all borders are projected to * the surface, * * @param displayGroup * Display group in which border is tested for display. * @param browserTabIndex * Tab index in which border is displayed. * @param surfaceFile * Surface file used for unprojection of border points. * @param nodesInROI * Indices if a node is inside (true) or outside (false) the ROI. * Number of elements MUST BE the number of nodes in the surface. * @param insideCountAndBorderOut * Output vector pair with first being number of border points inside the ROI and * and second is the corresponding border. */ void BorderFile::findBordersInsideRegionOfInterest(const DisplayGroupEnum::Enum displayGroup, const int32_t browserTabIndex, const SurfaceFile* surfaceFile, const std::vector& nodesInROI, std::vector >& insideCountAndBorderOut) const { CaretAssert(surfaceFile); const int32_t surfaceNumberOfNodes = surfaceFile->getNumberOfNodes(); CaretAssert(surfaceNumberOfNodes == static_cast(nodesInROI.size())); insideCountAndBorderOut.clear(); const StructureEnum::Enum surfaceStructure = surfaceFile->getStructure(); BorderFile* nonConstBorderFile = const_cast(this); const int32_t numBorders = getNumberOfBorders(); for (int32_t borderIndex = 0; borderIndex < numBorders; borderIndex++) { Border* border = m_borders[borderIndex]; if (nonConstBorderFile->isBorderDisplayed(displayGroup, browserTabIndex, border) == false) { continue; } if (border->getStructure() == surfaceStructure) { // const int32_t numberOfPoints = border->getNumberOfPoints(); // for (int32_t iPoint = 0; iPoint < numberOfPoints; iPoint++) { // const SurfaceProjectedItem* spi = border->getPoint(iPoint); // CaretAssert(spi); // const SurfaceProjectionBarycentric* bary = spi->getBarycentricProjection(); // if (bary != NULL) { // const int32_t* pointNodes = bary->getTriangleNodes(); // const int32_t p1 = pointNodes[0]; // const int32_t p2 = pointNodes[1]; // const int32_t p3 = pointNodes[2]; // if ((p1 < surfaceNumberOfNodes) // && (p2 < surfaceNumberOfNodes) // && (p3 < surfaceNumberOfNodes)) { // if (nodesInROI[p1] // || nodesInROI[p2] // || nodesInROI[p3]) { // bordersOut.push_back(border); // break; // } // } // } // } /* * Get all node indices from the border * Note that the node indices may be used by more than one border point barycentric projection * so first get all UNIQUE node indices that are used by the border points */ std::set borderNodeIndicesInsideROI; const int32_t numberOfPoints = border->getNumberOfPoints(); for (int32_t iPoint = 0; iPoint < numberOfPoints; iPoint++) { const SurfaceProjectedItem* spi = border->getPoint(iPoint); CaretAssert(spi); const SurfaceProjectionBarycentric* bary = spi->getBarycentricProjection(); if (bary != NULL) { const int32_t* pointNodes = bary->getTriangleNodes(); borderNodeIndicesInsideROI.insert(pointNodes[0]); borderNodeIndicesInsideROI.insert(pointNodes[1]); borderNodeIndicesInsideROI.insert(pointNodes[2]); } } int32_t borderNodesInsideROICount = 0; for (std::set::iterator iter = borderNodeIndicesInsideROI.begin(); iter != borderNodeIndicesInsideROI.end(); iter++) { const int32_t nodeIndex = *iter; CaretAssertVectorIndex(nodesInROI, nodeIndex); if (nodesInROI[nodeIndex]) { borderNodesInsideROICount++; } } if (borderNodesInsideROICount > 0) { insideCountAndBorderOut.push_back(std::make_pair(borderNodesInsideROICount, border)); } } } } /** * Add a border. NOTE: This border file * takes ownership of the 'border' and * will handle deleting it. After calling * this method, the caller must never * do anything with the border that was passed * to this method. * * @param border * Border added to this border file. */ void BorderFile::addBorder(Border* border) { int numPoints = border->getNumberOfPoints(); if (numPoints == 0) { delete border;//keep our word and handle deleting the argument throw DataFileException(getFileName(), "attempt to add border with zero points"); }//NOTE: Border itself makes sure all points are on one structure, and have barycentric projections if (m_borders.empty())//TSC: i'm not actually sure if we want border files to automatically set their structure from the borders { m_structure = border->getStructure(); } else { if (m_structure != StructureEnum::ALL && m_structure != border->getPoint(0)->getStructure()) { m_structure = StructureEnum::ALL; } } if (m_numNodes != -1) { if (!border->verifyForSurfaceNumberOfNodes(m_numNodes)) { delete border; throw DataFileException(getFileName(), "attempt to add border that has too large vertex indices for surface"); } } m_borders.push_back(border); // DO NOT WANT to add entries to name and class tables when the names are // not an exact match as partial matches are acceptable. // const AString name = border->getName(); // if (name.isEmpty() == false) { // const int32_t nameColorKey = m_nameColorTable->getLabelKeyFromName(name); // if (nameColorKey < 0) { // m_nameColorTable->addLabel(name, 0.0f, 0.0f, 0.0f, 1.0f); // } // } // const AString className = border->getClassName(); // if (className.isEmpty() == false) { // const int32_t classColorKey = m_classColorTable->getLabelKeyFromName(className); // if (classColorKey < 0) { // m_classColorTable->addLabel(className, 0.0f, 0.0f, 0.0f, 1.0f); // } // } m_forceUpdateOfGroupAndNameHierarchy = true; setModified(); } /** * Remove the border at the given index. * @param indx * Index of border for removal. */ void BorderFile::removeBorder(const int32_t indx) { CaretAssertVectorIndex(m_borders, indx); Border* border = getBorder(indx); m_borders.erase(m_borders.begin() + indx); delete border; if (m_structure == StructureEnum::ALL) { int numBorders = (int)m_borders.size(); bool singleStructure = true; for (int i = 1; i < numBorders; ++i) { if (m_borders[i]->getStructure() != m_borders[0]->getStructure()) { singleStructure = false; break; } } if (singleStructure) { if (numBorders != 0)//have it remember its structure when the last border is removed { m_structure = m_borders[0]->getStructure(); } } } m_forceUpdateOfGroupAndNameHierarchy = true; setModified(); } /** * Remove the border. * @param border * Border that will be removed and DELETED. */ void BorderFile::removeBorder(Border* border) { const int32_t numBorders = getNumberOfBorders(); for (int32_t i = 0;i < numBorders; i++) { if (m_borders[i] == border) { removeBorder(i); return; } } CaretLogWarning("Attempting to delete border not in border file with name: " + border->getName()); } /** * Is the given border, that MUST be in this file, displayed? * @param displayGroup * Display group in which border is tested for display. * @param browserTabIndex * Tab index in which border is displayed. * @param border * Border that is tested to see if it is displayed. * @return * true if border is displayed, else false. */ bool BorderFile::isBorderDisplayed(const DisplayGroupEnum::Enum displayGroup, const int32_t browserTabIndex, const Border* border) { const GroupAndNameHierarchyItem* selectionItem = border->getGroupNameSelectionItem(); if (selectionItem != NULL) { if (selectionItem->isSelected(displayGroup, browserTabIndex) == false) { return false; } } return true; } /** * @return The class and name hierarchy. */ GroupAndNameHierarchyModel* BorderFile::getGroupAndNameHierarchyModel() { m_classNameHierarchy->update(this, m_forceUpdateOfGroupAndNameHierarchy); m_forceUpdateOfGroupAndNameHierarchy = false; return m_classNameHierarchy; } /** * @return The class color table. */ GiftiLabelTable* BorderFile::getClassColorTable() { return m_classColorTable; } /** * @return The class color table. */ const GiftiLabelTable* BorderFile::getClassColorTable() const { return m_classColorTable; } /** * @return The name color table. */ GiftiLabelTable* BorderFile::getNameColorTable() { return m_nameColorTable; } /** * @return The name color table. */ const GiftiLabelTable* BorderFile::getNameColorTable() const { return m_nameColorTable; } int BorderFile::getIndexForBorderMetadataKey(const AString& key) const { for (int i = 0; i < (int)m_borderMDKeys.size(); ++i)//for now, the dumb way, we don't expect hundreds of keys - can speed it up with a secondary map if needed { if (m_borderMDKeys[i] == key) return i;//NOTE: if we use a second structure to speed this up, make sure parseBorderMDNames3 also updates it! } return -1; } const AString& BorderFile::getBorderMetadataKey(const int& index) const { CaretAssert(index >= 0 && index < (int)m_borderMDKeys.size()); return m_borderMDKeys[index]; } int BorderFile::addBorderMetadataKey(const AString& key) { int checkKey = getIndexForBorderMetadataKey(key); if (checkKey != -1) return checkKey; m_borderMDKeys.push_back(key);//NOTE: if we use a second structure to speed this up, make sure parseBorderMDNames3 also updates it! for (map, vector >::iterator iter = m_borderMDValues.begin(); iter != m_borderMDValues.end(); ++iter) { iter->second.push_back(AString()); CaretAssert(iter->second.size() == m_borderMDKeys.size()); } setModified(); return m_borderMDKeys.size() - 1; } void BorderFile::removeBorderMetadataKey(const int& index) { CaretAssert(index >= 0 && index < (int)m_borderMDKeys.size()); m_borderMDKeys.erase(m_borderMDKeys.begin() + index); for (map, vector >::iterator iter = m_borderMDValues.begin(); iter != m_borderMDValues.end(); ++iter) { iter->second.erase(iter->second.begin() + index); CaretAssert(iter->second.size() == m_borderMDKeys.size()); } setModified(); } void BorderFile::clearBorderMetaData() { m_borderMDKeys.clear(); m_borderMDValues.clear(); } AString BorderFile::getBorderMetadataValue(const AString& name, const AString& className, const int& index) const { CaretAssert(index >= 0 && index < (int)m_borderMDKeys.size()); map, vector >::const_iterator iter = m_borderMDValues.find(make_pair(name, className)); if (iter == m_borderMDValues.end()) return AString(); CaretAssert(iter->second.size() == m_borderMDKeys.size()); return iter->second[index]; } void BorderFile::setBorderMetadataValue(const AString& name, const AString& className, const int& index, const AString& value) { CaretAssert(index >= 0 && index < (int)m_borderMDKeys.size()); //TODO: check if such a border actually exists? maybe not needed map, vector >::iterator iter = m_borderMDValues.find(make_pair(name, className)); if (iter == m_borderMDValues.end()) { if (value == "") return;//don't create the metadata values array if we only store the empty string iter = m_borderMDValues.insert(make_pair(make_pair(name, className), vector(m_borderMDKeys.size()))).first; } CaretAssert(iter->second.size() == m_borderMDKeys.size()); iter->second[index] = value; setModified(); } /** * Version 1 foci files contained one color table for both names * and classes. Newer versions of the foci file keep them in * separate tables. * * @param oldColorTable * Old color table that is split into name and class color tables. */ void BorderFile::createNameAndClassColorTables(const GiftiLabelTable* oldColorTable) { CaretAssert(oldColorTable); m_classColorTable->clear(); m_nameColorTable->clear(); std::set nameSet; std::set classSet; const int numBorders = getNumberOfBorders(); for (int32_t i = 0; i < numBorders; i++) { const Border* border = getBorder(i); nameSet.insert(border->getName()); classSet.insert(border->getClassName()); } /* * Create colors for only the "best matching" color. */ for (std::set::iterator iter = nameSet.begin(); iter != nameSet.end(); iter++) { const AString colorName = *iter; const GiftiLabel* oldLabel = oldColorTable->getLabelBestMatching(colorName); if (oldLabel != NULL) { const AString bestMatchingName = oldLabel->getName(); const int32_t labelKey = m_nameColorTable->getLabelKeyFromName(bestMatchingName); if (labelKey < 0) { float rgba[4] = { 0.0, 0.0, 0.0, 1.0 }; oldLabel->getColor(rgba); m_nameColorTable->addLabel(bestMatchingName, rgba[0], rgba[1], rgba[2], rgba[3]); } } } /* * Create a color for each class name using the best matching color */ for (std::set::iterator iter = classSet.begin(); iter != classSet.end(); iter++) { const AString colorName = *iter; const GiftiLabel* label = oldColorTable->getLabelBestMatching(colorName); float rgba[4] = { 0.0, 0.0, 0.0, 1.0 }; if (label != NULL) { label->getColor(rgba); } m_classColorTable->addLabel(colorName, rgba[0], rgba[1], rgba[2], rgba[3]); } } /** * @return A string list containing all border names * sorted in alphabetical order. */ QStringList BorderFile::getAllBorderNamesSorted() const { std::set nameSet; const int32_t numFoci = getNumberOfBorders(); for (int32_t i = 0;i < numFoci; i++) { nameSet.insert(m_borders[i]->getName()); } QStringList sl; for (std::set::iterator iter = nameSet.begin(); iter != nameSet.end(); iter++) { sl += *iter; } return sl; } /** * Invalidate all assigned colors. */ void BorderFile::invalidateAllAssignedColors() { const int32_t numBorders = getNumberOfBorders(); for (int32_t i = 0; i < numBorders; i++) { m_borders[i]->setClassRgbaInvalid(); m_borders[i]->setNameRgbaInvalid(); } m_forceUpdateOfGroupAndNameHierarchy = true; } /** * @return The version of the file as a number. */ int32_t BorderFile::getFileVersion() { return s_borderFileVersion; } /** * @return The version of the file as a string. */ AString BorderFile::getFileVersionAsString() { return AString::number(s_borderFileVersion); } /** * Read the data file. * * @param filename * Name of the data file. * @throws DataFileException * If the file was not successfully read. */ void BorderFile::readFile(const AString& filename) { clear(); checkFileReadability(filename); setFileName(filename); { QFile inFile(filename); if (!inFile.open(QIODevice::ReadOnly)) throw DataFileException(filename, "failed to open file for reading"); QXmlStreamReader myReader(&inFile); readXML(myReader); } /*BorderFileSaxReader saxReader(this); std::auto_ptr parser(XmlSaxParser::createXmlParser()); try { parser->parseFile(filename, &saxReader); } catch (const XmlSaxParserException& e) { clear(); setFileName(""); int lineNum = e.getLineNumber(); int colNum = e.getColumnNumber(); AString msg = "Parse Error while reading " + filename; if ((lineNum >= 0) && (colNum >= 0)) { msg += (" line/col (" + AString::number(e.getLineNumber()) + "/" + AString::number(e.getColumnNumber()) + ")"); } msg += (": " + e.whatString()); DataFileException dfe(msg); CaretLogThrowing(dfe); throw dfe; }//*/ setFileName(filename); m_classNameHierarchy->update(this, true); m_forceUpdateOfGroupAndNameHierarchy = false; m_classNameHierarchy->setAllSelected(true); CaretLogFiner("CLASS/NAME Table for : " + getFileNameNoPath() + "\n" + m_classNameHierarchy->toString()); clearModified(); } /** * Write the data file. * * @param filename * Name of the data file. * @throws DataFileException * If the file was not successfully written. */ void BorderFile::writeFile(const AString& filename) { if (canWriteAsVersion(3)) { writeFile(filename, 3); } else { CaretLogWarning("border file missing information required for writing as version 3, falling back to older format"); writeFile(filename, 1); } } /** * @return Error message for attempting a gui operation on an obsolete, multi-structure border file. */ AString BorderFile::getObsoleteMultiStructureFormatMessage() { return ("This border file (" + getFileNameNoPath() + ") contains borders for multiple structures and must be split into single-structure border files. " "This can be done in the gui, using a selection in the Data menu, " "or on the command line using -file-convert with the -border-version-convert option."); } void BorderFile::writeFile(const AString& filename, const int& version) { if ( ! isSingleStructure()) { throw DataFileException(filename, "Writing multi-structure border files is no longer supported. " "Any existing multi-structure border files should be split into single-structure border files. " "This can be done on the command line using -file-convert with the -border-version-convert option, " "or in the gui, using a selection in the Data menu."); } if (!canWriteAsVersion(version)) throw DataFileException(filename, "cannot write border file as version '" + AString::number(version) + "'"); if (!(filename.endsWith(".border") || filename.endsWith(".wb_border"))) { CaretLogWarning("border file '" + filename + "' should be saved ending in .border"); } checkFileWritability(filename); setFileName(filename); switch (version) { case 3: { QFile myFile(filename); if (!myFile.open(QIODevice::WriteOnly | QIODevice::Truncate)) throw DataFileException(filename, "could not open for writing"); QXmlStreamWriter myXML(&myFile); myXML.setAutoFormatting(true); writeVersion3(myXML); break; } case 1: { // // Format the version string so that it ends with at most one zero // const AString versionString = AString::number(1.0); // // Open the file // FileAdapter file; AString errorMessage; QTextStream* textStream = file.openQTextStreamForWritingFile(getFileName(), errorMessage); if (textStream == NULL) { throw DataFileException(getFileName(), errorMessage); } // // Create the xml writer // XmlWriter xmlWriter(*textStream); // // Write header info // xmlWriter.writeStartDocument("1.0"); // // Write GIFTI root element // XmlAttributes attributes; //attributes.addAttribute("xmlns:xsi", // "http://www.w3.org/2001/XMLSchema-instance"); //attributes.addAttribute("xsi:noNamespaceSchemaLocation", // "http://brainvis.wustl.edu/caret6/xml_schemas/GIFTI_Caret.xsd"); attributes.addAttribute(BorderFile::XML_ATTRIBUTE_VERSION, versionString); xmlWriter.writeStartElement(BorderFile::XML_TAG_BORDER_FILE, attributes); // // Write Metadata // if (m_metadata != NULL) { m_metadata->writeAsXML(xmlWriter); } // // Write the class color table // xmlWriter.writeStartElement(XML_TAG_CLASS_COLOR_TABLE); m_classColorTable->writeAsXML(xmlWriter); xmlWriter.writeEndElement(); // // Write the name color table // xmlWriter.writeStartElement(XML_TAG_NAME_COLOR_TABLE); m_nameColorTable->writeAsXML(xmlWriter); xmlWriter.writeEndElement(); // // Write borders // const int32_t numBorders = getNumberOfBorders(); for (int32_t i = 0; i < numBorders; i++) { if (m_borders[i]->getNumberOfPoints() < 1) { CaretLogWarning("skipped writing zero-point border: '" + m_borders[i]->getName() + "'"); continue; } m_borders[i]->writeAsXML(xmlWriter); } xmlWriter.writeEndElement(); xmlWriter.writeEndDocument(); file.close(); break; } default: CaretAssertMessage(0, "unimplemented writer for claimed supported version"); break; } clearModified(); } bool BorderFile::canWriteAsVersion(const int& version) const { switch (version) { case 1: return true; case 3: if (!StructureEnum::isSingleStructure(m_structure)) return false; if (m_numNodes == -1) return false; return true; default: return false; } } void BorderFile::writeVersion3(QXmlStreamWriter& output) const { CaretAssert(canWriteAsVersion(3));//if this function is made public, this should also throw output.writeStartDocument(); output.writeStartElement("BorderFile"); output.writeAttribute("Version", "3"); output.writeAttribute("Structure", StructureEnum::toName(m_structure)); output.writeAttribute("SurfaceNumberOfVertices", AString::number(m_numNodes)); m_metadata->writeBorderFileXML3(output); int numBorderMDKeys = (int)m_borderMDKeys.size(); if (numBorderMDKeys > 0) { output.writeStartElement("BorderMetaDataNames"); for (int i = 0; i < numBorderMDKeys; ++i) { output.writeStartElement("Name"); output.writeCharacters(m_borderMDKeys[i]);//CDATA? output.writeEndElement(); } output.writeEndElement(); } int numBorders = getNumberOfBorders(); vector used(numBorders, false);//multi-part border behavior for (int i = 0; i < numBorders; ++i) { if (used[i]) continue; const Border* classBorder = getBorder(i); AString thisClass = classBorder->getClassName();//hierarchical representation in file output.writeStartElement("Class"); output.writeAttribute("Name", thisClass); writeColorHelper(output, getClassColorTable()->getLabelBestMatching(thisClass)); //writeColorHelper(output, getClassColorTable()->getLabel(thisClass)); for (int j = i; j < numBorders; ++j) { if (used[j]) continue; const Border* nameBorder = getBorder(j); if (nameBorder->getClassName() == thisClass) { AString thisName = nameBorder->getName();//multipart borders output.writeStartElement("Border"); output.writeAttribute("Name", thisName); writeColorHelper(output, getNameColorTable()->getLabelBestMatching(thisName)); //writeColorHelper(output, getNameColorTable()->getLabel(thisName)); map, vector >::const_iterator iter = m_borderMDValues.find(make_pair(thisName, thisClass)); if (iter != m_borderMDValues.end()) { CaretAssert(iter->second.size() == m_borderMDKeys.size()); output.writeStartElement("BorderMetaDataValues"); for (int k = 0; k < (int)m_borderMDKeys.size(); ++k) { output.writeStartElement("Value"); output.writeCharacters(iter->second[k]);//CDATA? output.writeEndElement(); } output.writeEndElement(); } for (int k = j; k < numBorders; ++k) { if (used[k]) continue; const Border* thisBorder = getBorder(k); if (thisBorder->getNumberOfPoints() < 1) { used[k] = true; CaretLogWarning("skipped writing zero-point border: '" + thisBorder->getName() + "'"); continue; } if (thisBorder->getName() == thisName && thisBorder->getClassName() == thisClass) { used[k] = true; thisBorder->writeXML3(output); } } output.writeEndElement();//Border } } output.writeEndElement();//Class } output.writeEndElement();//BorderFile } void BorderFile::writeColorHelper(QXmlStreamWriter& output, const GiftiLabel* colorLabel) const { if (colorLabel == NULL)//default to black if we somehow have no color { output.writeAttribute("Red", AString::number(0.0f)); output.writeAttribute("Green", AString::number(0.0f)); output.writeAttribute("Blue", AString::number(0.0f)); } else { output.writeAttribute("Red", AString::number(colorLabel->getRed())); output.writeAttribute("Green", AString::number(colorLabel->getGreen())); output.writeAttribute("Blue", AString::number(colorLabel->getBlue())); } } void BorderFile::readXML(QXmlStreamReader& xml) { clear(); bool haveRoot = false; while (!xml.atEnd()) { switch(xml.tokenType()) { case QXmlStreamReader::StartElement: { if (xml.name() != "BorderFile") throw DataFileException(getFileName(), "unexpected root element: " + xml.name().toString()); if (haveRoot) throw DataFileException(getFileName(), "multiple BorderFile elements in one file"); QXmlStreamAttributes myAttrs = xml.attributes(); if (!myAttrs.hasAttribute("Version")) throw DataFileException(getFileName(), "missing required attribute Version of element BorderFile"); QStringRef versionStr = myAttrs.value("Version"); if (versionStr == "1" || versionStr == "1.0") { parseBorderFile1(xml); } else if (versionStr == "3") { parseBorderFile3(xml); } else { throw DataFileException(getFileName(), "unrecognized border file version: " + versionStr.toString()); } haveRoot = true; break; } default: break; } xml.readNext(); } if (xml.hasError()) throw DataFileException(getFileName(), "XML parsing error in root of border file: " + xml.errorString()); if (!haveRoot) throw DataFileException(getFileName(), "BorderFile root element not found"); clearModified(); } void BorderFile::parseBorderFile1(QXmlStreamReader& xml) { CaretAssert(xml.isStartElement() && xml.name() == "BorderFile"); bool haveSingleTable = false, haveClassTable = false, haveNameTable = false; GiftiLabelTable singleTable; for (xml.readNext(); !xml.atEnd() && !xml.isEndElement(); xml.readNext()) { switch(xml.tokenType()) { case QXmlStreamReader::StartElement: { QStringRef name = xml.name(); if (name == "MetaData") { m_metadata->readBorderFileXML1(xml); if (xml.hasError()) throw DataFileException(getFileName(), "XML parsing error in MetaData: " + xml.errorString()); } else if (name == "BorderClassColorTable") { if (haveSingleTable) throw DataFileException(getFileName(), "file has both single-table and split-table coloring information"); if (haveClassTable) throw DataFileException(getFileName(), "file has multiple BorderClassColorTable elements"); if (!xml.readNextStartElement()) { if (xml.hasError()) throw DataFileException(getFileName(), "XML parsing error in BorderClassColorTable: " + xml.errorString()); throw DataFileException(getFileName(), "empty BorderClassColorTable found"); } m_classColorTable->readFromQXmlStreamReader(xml); xml.readNextStartElement();//find the end element of BorderClassColorTable if (xml.hasError()) throw DataFileException(getFileName(), "XML parsing error in BorderClassColorTable: " + xml.errorString()); haveClassTable = true; } else if (name == "BorderNameColorTable") { if (haveSingleTable) throw DataFileException(getFileName(), "file has both single-table and split-table coloring information"); if (haveNameTable) throw DataFileException(getFileName(), "file has multiple BorderNameColorTable elements"); if (!xml.readNextStartElement()) { if (xml.hasError()) throw DataFileException(getFileName(), "XML parsing error in BorderNameColorTable: " + xml.errorString()); throw DataFileException(getFileName(), "empty BorderNameColorTable found"); } m_nameColorTable->readFromQXmlStreamReader(xml); xml.readNextStartElement();//find the end element of BorderNameColorTable if (xml.hasError()) throw DataFileException(getFileName(), "XML parsing error in BorderNameColorTable: " + xml.errorString()); haveNameTable = true; } else if (name == "LabelTable") { if (haveNameTable || haveClassTable) throw DataFileException(getFileName(), "file has both single-table and split-table coloring information"); if (haveSingleTable) throw DataFileException(getFileName(), "file has multiple LabelTable elements"); singleTable.readFromQXmlStreamReader(xml); if (xml.hasError()) throw DataFileException(getFileName(), "XML parsing error in LabelTable: " + xml.errorString()); haveSingleTable = true; } else if (name == "Border") { CaretPointer toParse(new Border());//so throw can clean up, but we can also release the Border pointer toParse->readXML1(xml); if (toParse->getNumberOfPoints() > 0) { addBorder(toParse.releasePointer()); } else { CaretLogWarning("ignored border with zero points: '" + toParse->getName() + "'"); } } else { throw DataFileException(getFileName(), "unexpected element in BorderFile: " + name.toString()); } break; } default: break; } } if (haveSingleTable) { createNameAndClassColorTables(&singleTable); } if (xml.hasError()) throw DataFileException(getFileName(), "XML parsing error in BorderFile: " + xml.errorString()); CaretAssert(xml.isEndElement() && xml.name() == "BorderFile"); if (!haveSingleTable && (!haveClassTable || !haveNameTable)) { throw DataFileException(getFileName(), "border file is missing a required color table"); } } void BorderFile::parseBorderFile3(QXmlStreamReader& xml) { CaretAssert(xml.isStartElement() && xml.name() == "BorderFile"); QXmlStreamAttributes myAttrs = xml.attributes(); bool ok = false; if (!myAttrs.hasAttribute("Structure")) throw DataFileException(getFileName(), "BorderFile is missing required attribute Structure"); StructureEnum::Enum myStructure = StructureEnum::fromName(myAttrs.value("Structure").toString(), &ok); if (!ok) throw DataFileException(getFileName(), "unrecognized structure: " + myAttrs.value("Structure").toString()); setStructure(myStructure); if (!myAttrs.hasAttribute("SurfaceNumberOfVertices")) throw DataFileException(getFileName(), "BorderFile is missing required attribute SurfaceNumberOfVertices"); int myNumNodes = myAttrs.value("SurfaceNumberOfVertices").toString().toInt(&ok); if (!ok) throw DataFileException(getFileName(), "non-integer number of vertices: " + myAttrs.value("SurfaceNumberOfVertices").toString()); if (myNumNodes < 1) throw DataFileException(getFileName(), "number of vertices too small: "); setNumberOfNodes(myNumNodes); bool haveFileMD = false, haveBorderMDNames = false; set classNames; for (xml.readNext(); !xml.atEnd() && !xml.isEndElement(); xml.readNext()) { if (xml.isStartElement()) { QStringRef name = xml.name(); if (name == "MetaData") { if (haveFileMD) throw DataFileException(getFileName(), "file has multiple MetaData elements"); m_metadata->readBorderFileXML3(xml); if (xml.hasError()) throw DataFileException(getFileName(), "XML parsing error in MetaData: " + xml.errorString()); haveFileMD = true; } else if (name == "BorderMetaDataNames") { if (haveBorderMDNames) throw DataFileException(getFileName(), "file has multiple BorderMetaDataNames elements"); parseBorderMDNames3(xml); haveBorderMDNames = true; } else if (name == "Class") { AString className = parseClass3(xml); if (!classNames.insert(className).second) throw DataFileException(getFileName(), "multiple classes using same name: " + className); } else { throw DataFileException(getFileName(), "unexpected element in BorderFile: " + name.toString()); } } } if (xml.hasError()) throw DataFileException(getFileName(), "XML parsing error in BorderFile: " + xml.errorString()); CaretAssert(xml.isEndElement() && xml.name() == "BorderFile"); for (map, vector >::const_iterator iter = m_borderMDValues.begin(); iter != m_borderMDValues.end(); ++iter) {//someone could put the BorderMetaDataNames after a class, so check at the very end if (iter->second.size() != m_borderMDKeys.size()) { throw DataFileException(getFileName(), "wrong number of border metadata values for border " + iter->first.first + ", class " + iter->first.second); } } } void BorderFile::parseBorderMDNames3(QXmlStreamReader& xml) { CaretAssert(xml.isStartElement() && xml.name() == "BorderMetaDataNames"); for (xml.readNext(); !xml.atEnd() && !xml.isEndElement(); xml.readNext()) { if (xml.isStartElement()) { QStringRef name = xml.name(); if (name == "Name") { QString mdName = xml.readElementText();//errors on unexpected element if (xml.hasError()) throw DataFileException(getFileName(), "XML parsing error in Name: " + xml.errorString()); int checkIndex = getIndexForBorderMetadataKey(mdName); if (checkIndex != -1) throw DataFileException(getFileName(), "duplicate border metadata name: " + mdName); m_borderMDKeys.push_back(mdName);//NOTE: do NOT use addBorderMetadataKey, as if there are borders with metadata before this, it will mess things up } else { throw DataFileException(getFileName(), "unexpected element in BorderMetaDataNames: " + name.toString()); } } } if (xml.hasError()) throw DataFileException(getFileName(), "XML parsing error in BorderMetaDataNames: " + xml.errorString()); CaretAssert(xml.isEndElement() && xml.name() == "BorderMetaDataNames"); } AString BorderFile::parseClass3(QXmlStreamReader& xml) { CaretAssert(xml.isStartElement() && xml.name() == "Class"); QXmlStreamAttributes myAttrs = xml.attributes(); if (!myAttrs.hasAttribute("Name")) throw DataFileException(getFileName(), "Class is missing required attribute Name"); AString className = myAttrs.value("Name").toString(); float colorRGB[3]; colorAttribHelper3(getFileName(), xml, colorRGB); m_classColorTable->addLabel(className, colorRGB[0], colorRGB[1], colorRGB[2]); set borderNames; for (xml.readNext(); !xml.atEnd() && !xml.isEndElement(); xml.readNext()) { if (xml.isStartElement()) { QStringRef name = xml.name(); if (name == "Border") { AString borderName = parseBorder3(xml, className); if (!borderNames.insert(borderName).second) throw DataFileException(getFileName(), "multiple borders in one class using same name: " + borderName); } else { throw DataFileException(getFileName(), "unexpected element in Class: " + name.toString()); } } } if (xml.hasError()) throw DataFileException(getFileName(), "XML parsing error in Class: " + xml.errorString()); CaretAssert(xml.isEndElement() && xml.name() == "Class"); if (borderNames.size() == 0) throw DataFileException(getFileName(), "Class " + className + " has no Border elements"); return className; } AString BorderFile::parseBorder3(QXmlStreamReader& xml, const AString& className) { CaretAssert(xml.isStartElement() && xml.name() == "Border"); bool haveMDValues = false; int numBorderParts = 0; QXmlStreamAttributes myAttrs = xml.attributes(); if (!myAttrs.hasAttribute("Name")) throw DataFileException(getFileName(), "Class is missing required attribute Name"); AString borderName = myAttrs.value("Name").toString(); float colorRGB[3]; colorAttribHelper3(getFileName(), xml, colorRGB); m_nameColorTable->addLabel(borderName, colorRGB[0], colorRGB[1], colorRGB[2]); for (xml.readNext(); !xml.atEnd() && !xml.isEndElement(); xml.readNext()) { if (xml.isStartElement()) { QStringRef name = xml.name(); if (name == "BorderPart") { CaretPointer thisBorder(new Border());//again, because the current interface/internals take ownership of raw pointers thisBorder->readXML3(xml); thisBorder->setStructure(getStructure()); thisBorder->setClassName(className); thisBorder->setName(borderName); if (!thisBorder->verifyForSurfaceNumberOfNodes(getNumberOfNodes())) throw DataFileException(getFileName(), "BorderPart uses node numbers larger than are valid for its surface"); if (thisBorder->getNumberOfPoints() > 0) { addBorder(thisBorder.releasePointer()); } else { CaretLogWarning("ignored border with zero points: '" + thisBorder->getName() + "'"); } ++numBorderParts; } else if (name == "BorderMetaDataValues") { if (haveMDValues) throw DataFileException(getFileName(), "Border has multiple BorderMetaDataValues elements"); m_borderMDValues[make_pair(borderName, className)] = parseBorderMDValues3(getFileName(), xml); haveMDValues = true; } else { throw DataFileException(getFileName(), "unexpected element in Border: " + name.toString()); } } } if (xml.hasError()) throw DataFileException(getFileName(), "XML parsing error in Border: " + xml.errorString()); CaretAssert(xml.isEndElement() && xml.name() == "Border"); if (numBorderParts == 0) throw DataFileException(getFileName(), "Border has no BorderPart elements"); return borderName; } void BorderFile::colorAttribHelper3(const AString& filename, QXmlStreamReader& xml, float rgbOut[3]) { QXmlStreamAttributes myAttrs = xml.attributes(); bool ok = false; if (!myAttrs.hasAttribute("Red")) throw DataFileException(filename, xml.name().toString() + " element missing required attribute Red"); rgbOut[0] = myAttrs.value("Red").toString().toFloat(&ok); if (!ok) throw DataFileException(filename, "non-numeric Red attribute of " + xml.name().toString() + ": " + myAttrs.value("Red").toString()); if (!myAttrs.hasAttribute("Green")) throw DataFileException(filename, xml.name().toString() + " element missing required attribute Green"); rgbOut[1] = myAttrs.value("Green").toString().toFloat(&ok); if (!ok) throw DataFileException(filename, "non-numeric Green attribute of " + xml.name().toString() + ": " + myAttrs.value("Green").toString()); if (!myAttrs.hasAttribute("Blue")) throw DataFileException(filename, xml.name().toString() + " element missing required attribute Blue"); rgbOut[2] = myAttrs.value("Blue").toString().toFloat(&ok); if (!ok) throw DataFileException(filename, "non-numeric Blue attribute of " + xml.name().toString() + ": " + myAttrs.value("Blue").toString()); } vector BorderFile::parseBorderMDValues3(const AString& filename, QXmlStreamReader& xml) { CaretAssert(xml.isStartElement() && xml.name() == "BorderMetaDataValues"); vector ret; for (xml.readNext(); !xml.atEnd() && !xml.isEndElement(); xml.readNext()) { if (xml.isStartElement()) { QStringRef name = xml.name(); if (name == "Value") { ret.push_back(xml.readElementText());//errors on unexpected element if (xml.hasError()) throw DataFileException(filename, "XML parsing error in BorderMetaDataValues: " + xml.errorString()); } else { throw DataFileException(filename, "unexpected element in BorderMetaDataValues: " + name.toString()); } } } if (xml.hasError()) throw DataFileException(filename, "XML parsing error in BorderMetaDataValues: " + xml.errorString()); CaretAssert(xml.isEndElement() && xml.name() == "BorderMetaDataValues"); return ret; } /** * @return Is this border file modified? */ bool BorderFile::isModified() const { if (CaretDataFile::isModified()) { return true; } if (m_metadata->isModified()) { return true; } if (m_classColorTable->isModified()) { return true; } if (m_nameColorTable->isModified()) { return true; } /* * Note, these members do not affect modification status: * classNameHierarchy */ const int32_t numBorders = getNumberOfBorders(); for (int32_t i = 0; i < numBorders; i++) { if (m_borders[i]->isModified()) { return true; } } return false; } /** * Clear the modification status of this border file. */ void BorderFile::clearModified() { CaretDataFile::clearModified(); m_metadata->clearModified(); m_classColorTable->clearModified(); m_nameColorTable->clearModified(); const int32_t numBorders = getNumberOfBorders(); for (int32_t i = 0; i < numBorders; i++) { m_borders[i]->clearModified(); } } /** * Add information about the file to the data file information. * * @param dataFileInformation * Consolidates information about a data file. */ void BorderFile::addToDataFileContentInformation(DataFileContentInformation& dataFileInformation) { CaretDataFile::addToDataFileContentInformation(dataFileInformation); const std::vector allStructures = getAllBorderStructures(); if (allStructures.size() >= 2) { AString structuresText; for (std::vector::const_iterator iter = allStructures.begin(); iter != allStructures.end(); iter++) { structuresText += (StructureEnum::toGuiName(*iter) + " "); } dataFileInformation.addNameAndValue("Border Structures", structuresText); } int nameSize = 4, classSize = 0;//reserve space for headings, but classSize is only to know how much to reserve() BorderMultiPartHelper myHelp(this); for (int i = 0; i < (int)myHelp.borderPieceList.size(); ++i) { CaretAssert(myHelp.borderPieceList[i].size() > 0); const Border* thisPart = getBorder(myHelp.borderPieceList[i][0]); nameSize = max(nameSize, thisPart->getName().length()); classSize = max(classSize, thisPart->getClassName().length()); } nameSize += 3;//minimum number spaces between fields int numberSize = max(8, AString::number(myHelp.borderPieceList.size()).length() + 3);//spacing for border index AString header = AString("INDEX").leftJustified(numberSize) + AString("NAME").leftJustified(nameSize) + "CLASS"; dataFileInformation.addText(header); for (int i = 0; i < (int)myHelp.borderPieceList.size(); ++i) { CaretAssert(myHelp.borderPieceList[i].size() > 0); const Border* thisPart = getBorder(myHelp.borderPieceList[i][0]); AString line; line.reserve(numberSize + nameSize + classSize + 1); line = "\n" + AString::number(i + 1).leftJustified(numberSize); line += thisPart->getName().leftJustified(nameSize); line += thisPart->getClassName(); dataFileInformation.addText(line); } } /** * Export this border file to Caret5 formatted border color and * border projection files. * * @param surfaceFiles * Surface files for unprojection of borders. * @param outputCaret5FilesPrefix * Prefix for Caret5 output files. */ void BorderFile::exportToCaret5Format(const std::vector& surfaceFiles, const AString& outputCaret5FilesPrefix) { AString errorMessage; if (getNumberOfBorders() <= 0) { errorMessage.appendWithNewLine("This border file contains zero borders."); } if (outputCaret5FilesPrefix.isEmpty()) { errorMessage.appendWithNewLine("Caret5 output file prefix is empty."); } if ( ! errorMessage.isEmpty()) { throw DataFileException(getFileName(), errorMessage); } /* * In Caret7, each border contains a Structure attribute and a Caret7 * border file may contain borders from more than one structure. However, * Caret5 borders do not contain a structure attribute and so each * Caret5 border file contains borders for one structure only. * * So, group borders by structure. * */ std::map > bordersPerStructuresMap; for (std::vector::iterator borderIter = m_borders.begin(); borderIter != m_borders.end(); borderIter++) { Border* border = *borderIter; if (border->getNumberOfPoints() > 0) { const StructureEnum::Enum structure = border->getStructure(); std::map >::iterator iter = bordersPerStructuresMap.find(structure); if (iter != bordersPerStructuresMap.end()) { iter->second.push_back(border); } else { std::vector borderVector; borderVector.push_back(border); bordersPerStructuresMap.insert(std::pair >(structure, borderVector)); } } } int32_t filesWrittenCount = 0; /* * Surface are needed for unprojecting to create border files. * This will track missing surface structure types. */ std::set missingSurfaceStructures; /* * Place borders for each structure in separate files. */ for (std::map >::iterator iter = bordersPerStructuresMap.begin(); iter != bordersPerStructuresMap.end(); iter++) { const StructureEnum::Enum structure = iter->first; const std::vector& borderVector = iter->second; const int32_t numberOfBorders = static_cast(borderVector.size()); AString structureName = "unknown"; if (structure == StructureEnum::CEREBELLUM) { structureName = "cerebellum"; } else if (StructureEnum::isLeft(structure)) { structureName = "left"; } else if (StructureEnum::isRight(structure)) { structureName = "right"; } AString headerText; headerText.appendWithNewLine("BeginHeader"); headerText.appendWithNewLine("comment exported from wb_view file " + getFileNameNoPath()); headerText.appendWithNewLine("encoding ASCII"); headerText.appendWithNewLine("structure " + structureName); headerText.appendWithNewLine("EndHeader"); if (numberOfBorders > 0) { bool allBorderProjectionsValid = true; AString borderProjFileText; borderProjFileText.appendWithNewLine(headerText); borderProjFileText.appendWithNewLine(AString::number(numberOfBorders)); bool allBordersValid = true; AString borderFileText; borderFileText.appendWithNewLine(headerText); borderFileText.appendWithNewLine(AString::number(numberOfBorders)); for (int32_t iBorder = 0; iBorder < numberOfBorders; iBorder++) { const Border* border = borderVector[iBorder]; const int32_t numPoints = border->getNumberOfPoints(); const AString name = border->getName(); /* * Border Projection * Write index, number of points, name, * sampling density/variance/topography/uncertainty * * Center (0, 0, 0) */ borderProjFileText.appendWithNewLine(AString::number(iBorder) + " " + AString::number(numPoints) + " " + name + " 20.0 1.0 0.0 1.0"); borderProjFileText.appendWithNewLine("0.0 0.0 0.0"); /* * Border * Write index, number of points, name, * sampling density/variance/topography/uncertainty * * Center (0, 0, 0) */ borderFileText.appendWithNewLine(AString::number(iBorder) + " " + AString::number(numPoints) + " " + name + " 20.0 1.0 0.0 1.0"); borderFileText.appendWithNewLine("0.0 0.0 0.0"); for (int32_t jPoint = 0; jPoint < numPoints; jPoint++) { const SurfaceProjectedItem* spi = border->getPoint(jPoint); const SurfaceProjectionBarycentric* baryProj = spi->getBarycentricProjection(); if (baryProj->isValid()) { const float* triangleAreas = baryProj->getTriangleAreas(); const int32_t* triangleNodes = baryProj->getTriangleNodes(); /* * Add points nodes, section, areas, and radius */ borderProjFileText.appendWithNewLine(AString::number(triangleNodes[0]) + " " + AString::number(triangleNodes[1]) + " " + AString::number(triangleNodes[2]) + " 0 " + AString::number(triangleAreas[0], 'f', 6) + " " + AString::number(triangleAreas[1], 'f', 6) + " " + AString::number(triangleAreas[2], 'f', 6) + " 0.0"); } else { allBorderProjectionsValid = false; } if ( ! surfaceFiles.empty()) { SurfaceFile* surface = NULL; for (std::vector::const_iterator surfaceIter = surfaceFiles.begin(); surfaceIter != surfaceFiles.end(); surfaceIter++) { SurfaceFile* sf = *surfaceIter; if (sf->getStructure() == structure) { surface = sf; break; } } if (surface != NULL) { float xyz[3]; if (spi->getProjectedPosition(*surface, xyz, false)) { /* * Add point index, section, xyz, and radius */ borderFileText.appendWithNewLine(AString::number(jPoint) + " 0 " + AString::number(xyz[0], 'f', 3) + " " + AString::number(xyz[1], 'f', 3) + " " + AString::number(xyz[2], 'f', 3) + " 0.0"); } else { allBordersValid = false; } } else { missingSurfaceStructures.insert(structure); allBordersValid = false; } } else { missingSurfaceStructures.insert(structure); allBordersValid = false; } } } if (allBorderProjectionsValid) { try { const AString filename = (outputCaret5FilesPrefix + "_" + StructureEnum::toName(structure) + ".borderproj"); TextFile borderProjectionFile; borderProjectionFile.addText(borderProjFileText); borderProjectionFile.writeFile(filename); filesWrittenCount++; } catch (const DataFileException& dfe) { errorMessage.appendWithNewLine(dfe.whatString()); } } else { errorMessage.appendWithNewLine("There were failures creating at least one border projection for structure: " + StructureEnum::toName(structure)); } if (allBordersValid) { try { const AString filename = (outputCaret5FilesPrefix + "_" + StructureEnum::toName(structure) + ".border"); TextFile borderFile; borderFile.addText(borderFileText); borderFile.writeFile(filename); filesWrittenCount++; } catch (const DataFileException& dfe) { errorMessage.appendWithNewLine(dfe.whatString()); } } else { errorMessage.appendWithNewLine("There were failures creating at least one border for structure: " + StructureEnum::toName(structure)); } } } for (std::set::iterator missingStructureIter = missingSurfaceStructures.begin(); missingStructureIter != missingSurfaceStructures.end(); missingStructureIter++) { errorMessage.appendWithNewLine("No surface was available for structure: " + StructureEnum::toName(*missingStructureIter)); } if (filesWrittenCount > 0) { try { const AString filename = (outputCaret5FilesPrefix + ".bordercolor"); GiftiLabelTable* colorTable = getNameColorTable(); colorTable->exportToCaret5ColorFile(filename); } catch (const GiftiException& ge) { errorMessage.appendWithNewLine(ge.whatString()); } } if ( ! errorMessage.isEmpty()) { throw DataFileException(getFileName(), errorMessage); } } BorderMultiPartHelper::BorderMultiPartHelper(const BorderFile* bf) { int numBorderParts = bf->getNumberOfBorders(); for (int i = 0; i < numBorderParts; ++i) { const Border* thisPart = bf->getBorder(i); map, int>::const_iterator iter = stringLookup.find(make_pair(thisPart->getName(), thisPart->getClassName())); if (iter == stringLookup.end()) { stringLookup.insert(make_pair(make_pair(thisPart->getName(), thisPart->getClassName()), (int)borderPieceList.size())); borderPieceList.push_back(vector(1, i)); } else { borderPieceList[iter->second].push_back(i); } } } int BorderMultiPartHelper::fromNumberOrName(const AString& ident) const { bool ok = false; int whichBorder = ident.toInt(&ok) - 1;//first border is "1" if (ok) { if (whichBorder < 0 || whichBorder >= (int)borderPieceList.size()) return -1; return whichBorder; } else {//only search for name if the string isn't a number, to prevent surprises for (std::map, int>::const_iterator iter = stringLookup.begin(); iter != stringLookup.end(); ++iter) { if (iter->first.first == ident) { return iter->second; } } return -1; } } connectome-workbench-1.2.3+git41-gc4c6c90/src/Files/BorderFile.h000066400000000000000000000251351300200146000240020ustar00rootroot00000000000000#ifndef __BORDER_FILE__H_ #define __BORDER_FILE__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 "CaretDataFile.h" #include "CaretPointer.h" #include "DisplayGroupEnum.h" #include #include class QXmlStreamReader; class QXmlStreamWriter; namespace caret { class Border; class BorderPointFromSearch; class GroupAndNameHierarchyModel; class GiftiLabel; class GiftiLabelTable; class SurfaceFile; class SurfaceProjectedItem; class BorderFile : public CaretDataFile { public: BorderFile(); virtual ~BorderFile(); BorderFile(const BorderFile& obj); BorderFile& operator=(const BorderFile& obj); virtual void addToDataFileContentInformation(DataFileContentInformation& dataFileInformation); StructureEnum::Enum getStructure() const; void setStructure(const StructureEnum::Enum structure); std::vector getAllBorderStructures() const; bool splitIntoSingleStructureFiles(const std::map& singleStructureFileNames, const std::map& structureNumberOfNodes, std::vector& singleStructureBorderFilesOut, AString& errorMessageOut) const; GiftiMetaData* getFileMetaData(); const GiftiMetaData* getFileMetaData() const; void readFile(const AString& filename); void writeFile(const AString& filename); void writeFile(const AString& filename, const int& version); void clear(); bool isEmpty() const; int32_t getNumberOfNodes() const; void setNumberOfNodes(const int32_t& numNodes); void updateNumberOfNodesIfSingleStructure(const std::map& structureToNodeCount); int32_t getNumberOfBorders() const; Border* getBorder(const int32_t indx); const Border* getBorder(const int32_t indx) const; bool containsBorder(const Border* border) const; void findAllBordersWithEndPointNearSegmentFirstPoint(const DisplayGroupEnum::Enum displayGroup, const int32_t browserTabIndex, const SurfaceFile* surfaceFile, const Border* borderSegment, const float maximumDistance, std::vector& borderPointsOut) const; void findAllBordersWithAnyPointNearSegmentFirstPoint(const DisplayGroupEnum::Enum displayGroup, const int32_t browserTabIndex, const SurfaceFile* surfaceFile, const Border* borderSegment, const float maximumDistance, std::vector& borderPointsOut) const; void findAllBordersWithPointsNearBothSegmentEndPoints(const DisplayGroupEnum::Enum displayGroup, const int32_t browserTabIndex, const SurfaceFile* surfaceFile, const Border* borderSegment, const float maximumDistance, std::vector& borderPointsOut) const; void findBordersInsideRegionOfInterest(const DisplayGroupEnum::Enum displayGroup, const int32_t browserTabIndex, const SurfaceFile* surfaceFile, const std::vector& nodesInROI, std::vector >& insideCountAndBorderOut) const; void addBorder(Border* border); void removeBorder(const int32_t indx); void removeBorder(Border* border); bool isBorderDisplayed(const DisplayGroupEnum::Enum displayGroup, const int32_t browserTabIndex, const Border* border); GiftiLabelTable* getClassColorTable(); const GiftiLabelTable* getClassColorTable() const; GiftiLabelTable* getNameColorTable(); const GiftiLabelTable* getNameColorTable() const; int getNumberOfBorderMetadataKeys() const { return (int)m_borderMDKeys.size(); } int getIndexForBorderMetadataKey(const AString& key) const; const AString& getBorderMetadataKey(const int& index) const; //only adds if it doesn't already exist, and returns index int addBorderMetadataKey(const AString& key); void removeBorderMetadataKey(const int& index); void clearBorderMetaData(); AString getBorderMetadataValue(const AString& name, const AString& className, const int& index) const; void setBorderMetadataValue(const AString& name, const AString& className, const int& index, const AString& value); void createNameAndClassColorTables(const GiftiLabelTable* oldColorTable); GroupAndNameHierarchyModel* getGroupAndNameHierarchyModel(); const GroupAndNameHierarchyModel* getGroupAndNameHierarchyModel() const; QStringList getAllBorderNamesSorted() const; static int32_t getFileVersion(); static AString getFileVersionAsString(); void exportToCaret5Format(const std::vector& surfaceFiles, const AString& outputCaret5FilesPrefix); AString getObsoleteMultiStructureFormatMessage(); /** XML Tag for BorderFile element */ static const AString XML_TAG_BORDER_FILE; /** XML Tag for Version attribute */ static const AString XML_ATTRIBUTE_VERSION; /** XML Tag for Name Color Table */ static const AString XML_TAG_NAME_COLOR_TABLE; /** XML Tag for Class Color Table */ static const AString XML_TAG_CLASS_COLOR_TABLE; virtual bool isModified() const; virtual void clearModified(); void invalidateAllAssignedColors(); private: void copyHelperBorderFile(const BorderFile& obj); void initializeBorderFile(); bool canWriteAsVersion(const int& version) const; void writeVersion3(QXmlStreamWriter& output) const; void writeColorHelper(QXmlStreamWriter& output, const GiftiLabel* colorLabel) const; void readXML(QXmlStreamReader& xml); void parseBorderFile1(QXmlStreamReader& xml);//there is no version 2, because the SAX parser pretended to support version 2 when it didn't exist void parseBorderFile3(QXmlStreamReader& xml);//so, to make the new format give reasonable error messages in old releases, make the new format version 3 void parseBorderMDNames3(QXmlStreamReader& xml); AString parseClass3(QXmlStreamReader& xml); AString parseBorder3(QXmlStreamReader& xml, const AString& className); static void colorAttribHelper3(const AString& filename, QXmlStreamReader& xml, float rgbOut[3]); static std::vector parseBorderMDValues3(const AString& filename, QXmlStreamReader& xml); GiftiMetaData* m_metadata; std::vector m_borders; /** Holds colors assigned to classes */ GiftiLabelTable* m_classColorTable; /** Holds colors assigned to names */ GiftiLabelTable* m_nameColorTable; /** Holds class and name hierarchy used for display selection */ mutable GroupAndNameHierarchyModel* m_classNameHierarchy; /** force an update of the class and name hierarchy */ bool m_forceUpdateOfGroupAndNameHierarchy; StructureEnum::Enum m_structure; int32_t m_numNodes; std::vector m_borderMDKeys; std::map, std::vector > m_borderMDValues;//because each "Border" is really just a part of a border /** Version of this BorderFile */ static const int32_t s_borderFileVersion; }; struct BorderMultiPartHelper { std::map, int> stringLookup; std::vector > borderPieceList; BorderMultiPartHelper(const BorderFile* bf); int fromNumberOrName(const AString& ident) const; }; #ifdef __BORDER_FILE_DECLARE__ const AString BorderFile::XML_TAG_BORDER_FILE = "BorderFile"; const AString BorderFile::XML_ATTRIBUTE_VERSION = "Version"; const AString BorderFile::XML_TAG_NAME_COLOR_TABLE = "BorderNameColorTable"; const AString BorderFile::XML_TAG_CLASS_COLOR_TABLE = "BorderClassColorTable"; const int32_t BorderFile::s_borderFileVersion = 2; #endif // __BORDER_FILE_DECLARE__ } // namespace #endif //__BORDER_FILE__H_ connectome-workbench-1.2.3+git41-gc4c6c90/src/Files/BorderLengthHelper.cxx000066400000000000000000000241141300200146000260530ustar00rootroot00000000000000/*LICENSE_START*/ /* * Copyright (C) 2015 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 "Border.h" #include "BorderLengthHelper.h" #include "CaretAssert.h" #include "CaretException.h" #include "GeodesicHelper.h" #include "SurfaceFile.h" #include "SurfaceProjectedItem.h" #include "SurfaceProjectionBarycentric.h" #include "Vector3D.h" #include #include using namespace caret; using namespace std; BorderLengthHelper::BorderLengthHelper(const SurfaceFile* mySurf, const float* correctedAreas) { m_surface = mySurf; m_corrAreas = correctedAreas; mySurf->computeNodeAreas(m_currAreas); if (correctedAreas == NULL) { m_geoHelp = mySurf->getGeodesicHelper(); } else { CaretPointer myBase(new GeodesicHelperBase(mySurf, correctedAreas)); m_geoHelp.grabNew(new GeodesicHelper(myBase)); } } namespace { float segmentLength(const SurfaceProjectedItem* start, const SurfaceProjectedItem* end, GeodesicHelper* myGeoHelp, const SurfaceFile* mySurf, const float* correctedAreas, const float* currAreas) { const SurfaceProjectionBarycentric* startBary = start->getBarycentricProjection(), *endBary = end->getBarycentricProjection(); CaretAssert(startBary->isValid() && endBary->isValid()); const int32_t* startNodes = startBary->getTriangleNodes(), *endNodes = endBary->getTriangleNodes(); const float* nodeCoords = mySurf->getCoordinateData(); Vector3D startPos, endPos; startBary->unprojectToSurface(*mySurf, startPos, 0.0f, true);//in case something silly is in the signed distance endBary->unprojectToSurface(*mySurf, endPos, 0.0f, true); int numSame = 0; int sameVerts[3] = {-1, -1, -1}; set startSet(startNodes, startNodes + 3), endSet(endNodes, endNodes + 3);//use sets in case a strange border file repeats vertices in a projection for (set::iterator iter = startSet.begin(); iter != startSet.end(); ++iter) { if (endSet.find(*iter) != endSet.end()) { sameVerts[numSame] = *iter; ++numSame; } } if (numSame == (int)startSet.size() || numSame == (int)endSet.size()) {//if vertices are repeated, and all of one set match, count it as same triangle numSame = 3; } switch (numSame) { case 3://same triangle if (correctedAreas != NULL)//apply area correction approximation - currently, just average the vertex areas and sqrt {//we could integrate along the path, but expansion should be fairly smooth anyway float correctionFactor = sqrt(correctedAreas[startNodes[0]] + correctedAreas[startNodes[1]] + correctedAreas[startNodes[2]]) / sqrt(currAreas[startNodes[0]] + currAreas[startNodes[1]] + currAreas[startNodes[2]]); return (startPos - endPos).length() * correctionFactor; } else { return (startPos - endPos).length(); } case 2://use unfolding of triangle { Vector3D neigh1Coord = nodeCoords + sameVerts[0] * 3;//neigh1 and neigh2 are the shared vertices Vector3D neigh2Coord = nodeCoords + sameVerts[1] * 3; float abmag, efmag; Vector3D abhat = (neigh2Coord - neigh1Coord).normal(&abmag);//a is neigh1, b is neigh2, b - a = (vector)ab Vector3D ac = endPos - neigh1Coord;//c is endpoint, c - a = (vector)ac Vector3D ad = abhat * abhat.dot(ac);//d is the point on the shared edge that endpoint (c) is closest to Vector3D d = neigh1Coord + ad;//this way we can "unfold" the triangles by projecting the distance of cd, from point d, along the unit vector of the startpoint to closest point on shared edge Vector3D ea = neigh1Coord - startPos;//e is the startpoint, a - e = (vector)ea Vector3D tempvec = abhat * abhat.dot(ea);//find vector fa, f being the point on shared edge closest to e, the startpoint Vector3D efhat = (ea - tempvec).normal(&efmag);//and subtract it to obtain only the perpendicular, normalize to get unit vector float cdmag = (d - endPos).length();//get the length from shared edge to endpoint Vector3D g = d + efhat * cdmag;//get point g, the unfolded position of endpoint Vector3D eg = g - startPos;//this is the vector from base (e) to endpoint after unfolding (g), this is our distance, as long as the tetralateral is convex float tempf = efmag / (efmag + cdmag);//now we need to check that the path stays inside the tetralateral (ie, that it is convex) Vector3D eh = eg * tempf;//this is a vector from e (startpoint) to the point on the shared edge that the full path (eg) crosses Vector3D ah = eh - ea;//eh - ea = eh + ae = ae + eh = ah, vector from neigh1 to the point on shared edge the path goes through tempf = ah.dot(abhat);//get the component along ab so we can test that it is positive and less than |ab| if (tempf <= 0.0f) { tempf = 0.0f; } else if (tempf >= abmag) {//tetralateral is concave or triangular (degenerate), so clamp the point through the shared edge to an endpoint tempf = abmag; } Vector3D crosspoint = neigh1Coord + abhat * tempf; if (correctedAreas != NULL)//apply area correction approximation - currently, just average the vertex areas on the shared edge and sqrt {//we could compute the amount of the path in each triangle exactly, and integrate along the path, but expansion should be fairly smooth anyway float correctionFactor = sqrt(correctedAreas[sameVerts[0]] + correctedAreas[sameVerts[1]]) / sqrt(currAreas[sameVerts[0]] + currAreas[sameVerts[1]]); return ((crosspoint - startPos).length() + (endPos - crosspoint).length()) * correctionFactor; } else { return ((crosspoint - startPos).length() + (endPos - crosspoint).length()); } } case 1://brute force tests of to vertex, geodesic, to end case 0: { float bestLength = -1.0f; for (int i = 0; i < 3; ++i) { for (int j = 0; j < 3; ++j) { vector pathNodes; vector pathDists; myGeoHelp->getPathToNode(startNodes[i], endNodes[j], pathNodes, pathDists, true);//we want the distance, not the path, so use the more accurate distances float startLength, endLength; if (correctedAreas != NULL)//apply area correction approximation - currently, just average the vertex areas and sqrt {//we could integrate along the path, but expansion should be fairly smooth anyway float startCorrFactor = sqrt(correctedAreas[startNodes[0]] + correctedAreas[startNodes[1]] + correctedAreas[startNodes[2]]) / sqrt(currAreas[startNodes[0]] + currAreas[startNodes[1]] + currAreas[startNodes[2]]); float endCorrFactor = sqrt(correctedAreas[endNodes[0]] + correctedAreas[endNodes[1]] + correctedAreas[endNodes[2]]) / sqrt(currAreas[endNodes[0]] + currAreas[endNodes[1]] + currAreas[endNodes[2]]); startLength = (Vector3D(nodeCoords + startNodes[i] * 3) - startPos).length() * startCorrFactor; endLength = (endPos - Vector3D(nodeCoords + endNodes[j] * 3)).length() * endCorrFactor; } else { startLength = (Vector3D(nodeCoords + startNodes[i] * 3) - startPos).length(); endLength = (endPos - Vector3D(nodeCoords + endNodes[j] * 3)).length(); } float totalLength = pathDists.back() + startLength + endLength; if (totalLength < bestLength || bestLength == -1.0f) { bestLength = totalLength; } } } return bestLength; } default://should never be other values CaretAssert(false); throw CaretException("internal error, tell the developers what you just tried to do"); } } } float BorderLengthHelper::length(const Border* myBorder) { int numPoints = myBorder->getNumberOfPoints(); double totalLength = 0.0f; for (int i = 1; i < numPoints; ++i) { totalLength += segmentLength(myBorder->getPoint(i - 1), myBorder->getPoint(i), m_geoHelp, m_surface, m_corrAreas, m_currAreas.data()); } if (myBorder->isClosed() && numPoints > 1) { totalLength += segmentLength(myBorder->getPoint(numPoints - 1), myBorder->getPoint(0), m_geoHelp, m_surface, m_corrAreas, m_currAreas.data()); } return totalLength; } BorderLengthHelper::~BorderLengthHelper() {//for CaretPointer destructor to not be instantiated on a forward declaration } connectome-workbench-1.2.3+git41-gc4c6c90/src/Files/BorderLengthHelper.h000066400000000000000000000027001300200146000254750ustar00rootroot00000000000000#ifndef __BORDER_LENGTH_HELPER_H__ #define __BORDER_LENGTH_HELPER_H__ /*LICENSE_START*/ /* * Copyright (C) 2015 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 "CaretPointer.h" #include namespace caret { class Border; class GeodesicHelper; class SurfaceFile; class BorderLengthHelper { const SurfaceFile* m_surface; CaretPointer m_geoHelp; const float* m_corrAreas; std::vector m_currAreas; public: BorderLengthHelper(const SurfaceFile* mySurf, const float* correctedAreas = NULL); float length(const Border* myBorder); ~BorderLengthHelper(); }; } #endif //__BORDER_LENGTH_HELPER_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Files/BorderPointFromSearch.h000066400000000000000000000151401300200146000261610ustar00rootroot00000000000000#ifndef __BORDER_POINT_FROM_SEARCH_H__ #define __BORDER_POINT_FROM_SEARCH_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 "CaretAssert.h" #include "CaretObject.h" /** * \class caret::BorderPointFromSearch * \brief Contains result of searching for a border point. * \ingroup Files */ namespace caret { class BorderPointFromSearch : public CaretObject { public: /** * Constructor. */ BorderPointFromSearch() { reset(); } /** * Copy constructor. * * @param rhs * Instance copied to this instance. */ BorderPointFromSearch(const BorderPointFromSearch& rhs) : CaretObject(rhs) { this->copyHelperBorderPointFromSearch(rhs); } /** * Destructor. */ virtual ~BorderPointFromSearch() { } /** * Copy constructor. * * @param rhs * Instance copied to this instance. * @return * Reference to this instance. */ BorderPointFromSearch& operator=(const BorderPointFromSearch& rhs) { if (this != &rhs) { CaretObject::operator=(rhs); this->copyHelperBorderPointFromSearch(rhs); } return *this; } /** * Comparison operator using distance. * * @param rhs * Compared to rhs * @param * True if 'this' is less than 'rhs'. */ bool operator<(const BorderPointFromSearch& rhs) const { if (isValid() && rhs.isValid()) { return (m_distance < rhs.m_distance); } else if (isValid()) { return true; } return false; } /* * Reset the data to invalid. */ void reset() { m_borderFile = NULL; m_border = NULL; m_borderIndex = -1; m_borderPointIndex = -1; m_distance = -1.0; } /** * Replace this instance with the given instance if the given * instance's distance is less than this instance's distance * or if this instance is invalid. * * @param rhs * The other instance. */ void replaceWithNearerDistance(const BorderPointFromSearch& rhs) { if ( ! rhs.isValid()) { return; } bool replaceMeFlag = false; if (isValid()) { if (rhs.m_distance < m_distance) { replaceMeFlag = true; } } else { replaceMeFlag = true; } if (replaceMeFlag) { copyHelperBorderPointFromSearch(rhs); } } /** * Set the data. * * @param borderFile * The border file. * @param border * The border * @param borderIndex * Index of border in the border file * @param borderPointIndex * Index of point in the border * @param distance * Distance of border point from the search point. */ void setData(BorderFile* borderFile, Border* border, const int32_t borderIndex, const int32_t borderPointIndex, const float distance) { m_borderFile = borderFile; m_border = border; m_borderIndex = borderIndex; m_borderPointIndex = borderPointIndex; m_distance = distance; CaretAssert(m_borderFile); CaretAssert(m_border); CaretAssert(m_borderIndex >= 0); CaretAssert(m_borderPointIndex >= 0); CaretAssert(m_distance >= 0.0); } /** @return Is this item valid */ inline bool isValid() const { return ((m_borderFile != NULL) && (m_border != NULL) && (m_borderIndex >= 0) && (m_borderPointIndex >= 0) && (m_distance >= 0.0)); } /** @return Border file containing the point */ inline BorderFile* borderFile() { return m_borderFile; } /** @return The border */ inline Border* border() { return m_border; } /** @return Index of the border in the border file */ inline int32_t borderIndex() { return m_borderIndex; } /** @return Index of the border point */ inline int32_t borderPointIndex() { return m_borderPointIndex; } /** @return Distance to the border point from the search point */ inline float distance() { return m_distance; } private: /** * Helps with copying an object of this type. * @param rhs * Object that is copied. */ void copyHelperBorderPointFromSearch(const BorderPointFromSearch& rhs) { m_borderFile = rhs.m_borderFile; m_border = rhs.m_border; m_borderIndex = rhs.m_borderIndex; m_borderPointIndex = rhs.m_borderPointIndex; m_distance = rhs.m_distance; } BorderFile* m_borderFile; Border* m_border; int32_t m_borderIndex; int32_t m_borderPointIndex; float m_distance; }; #ifdef __BORDER_POINT_FROM_SEARCH_DECLARE__ // #endif // __BORDER_POINT_FROM_SEARCH_DECLARE__ } // namespace #endif //__BORDER_POINT_FROM_SEARCH_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Files/BorderTracingHelper.cxx000066400000000000000000000132541300200146000262240ustar00rootroot00000000000000/*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 "BorderTracingHelper.h" #include "Border.h" #include "BorderFile.h" #include "SurfaceProjectedItem.h" #include "SurfaceProjectionBarycentric.h" #include "SurfaceFile.h" #include "TopologyHelper.h" using namespace caret; using namespace std; BorderTracingHelper::BorderTracingHelper(const SurfaceFile* surfIn) { m_numNodes = surfIn->getNumberOfNodes(); m_structure = surfIn->getStructure(); m_topoHelp = surfIn->getTopologyHelper();//doesn't need sorted, because we may need to walk in "reverse", so don't rely on sorting } vector > BorderTracingHelper::tracePrivate(vector& marked, const float& placement) { vector > ret; const vector& myEdgeInfo = m_topoHelp->getEdgeInfo(); const vector& myTileInfo = m_topoHelp->getTileInfo(); vector edgeUsed(myEdgeInfo.size(), 0); float nodeWeights[3] = { 1.0f - placement, placement, 0.0f }; while (true) { bool foundStart = false, closed = true; int curInNode = -1, curEdge = -1, curOutNode = -1; for (int i = 0; i < m_numNodes; ++i) { if (marked[i] != 0) { const vector& edges = m_topoHelp->getNodeEdges(i); int numEdges = (int)edges.size(); for (int j = 0; j < numEdges; ++j) { const TopologyEdgeInfo& thisEdge = myEdgeInfo[edges[j]]; int testNode = (thisEdge.node2 == i ? thisEdge.node1 : thisEdge.node2); if (marked[testNode] == 0 && edgeUsed[edges[j]] == 0) { if (!foundStart || thisEdge.numTiles == 1) { foundStart = true; curInNode = i; curEdge = edges[j]; curOutNode = testNode; if (thisEdge.numTiles == 1) { closed = false; break;//if we found the end of an open border, stop searching } } } } } } if (!foundStart) break; CaretPointer newBorder(new Border());//in case something throws newBorder->setClosed(closed); int startInNode = curInNode, startOutNode = curOutNode; int prevThirdNode = -1; do { edgeUsed[curEdge] = 1;//don't start another border from this edge const TopologyEdgeInfo& myEdge = myEdgeInfo[curEdge];//to remove some redundant indexing CaretPointer newPoint(new SurfaceProjectedItem());//ditto newPoint->setStructure(m_structure); newPoint->getBarycentricProjection()->setProjectionSurfaceNumberOfNodes(m_numNodes); int32_t triNodes[3] = { curInNode, curOutNode, myEdge.tiles[0].node3 };//always use the first tile, as it will always exist newPoint->getBarycentricProjection()->setTriangleNodes(triNodes); newPoint->getBarycentricProjection()->setTriangleAreas(nodeWeights); newPoint->getBarycentricProjection()->setValid(true); newBorder->addPoint(newPoint.releasePointer());//NOTE: addPoint takes ownership of a RAW POINTER - shared_ptr won't release a pointer int useTile = 0; if (myEdge.tiles[0].node3 == prevThirdNode) { if (myEdge.numTiles == 1) break; useTile = 1; } int nextNode = myEdge.tiles[useTile].node3; int edgeMove = 0; if (marked[nextNode] == 0) { edgeMove = (myEdge.tiles[useTile].edgeReversed == (myEdge.node1 == curInNode) ? 1 : 2);//some magic to find the next edge via the lookups prevThirdNode = curOutNode; curOutNode = nextNode; } else { edgeMove = (myEdge.tiles[useTile].edgeReversed == (myEdge.node1 == curInNode) ? 2 : 1);//rather than searching the edges on the tile or such prevThirdNode = curInNode; curInNode = nextNode; } int edgeIndex = (myEdge.tiles[useTile].whichEdge + edgeMove) % 3;//modify the edge index within the tile by the magic offset curEdge = myTileInfo[myEdge.tiles[useTile].tile].edges[edgeIndex].edge;//and pull out the global index of the edge CaretAssert(myEdgeInfo[curEdge].node1 == curInNode || myEdgeInfo[curEdge].node1 == curOutNode);//assert to make sure the magic worked CaretAssert(myEdgeInfo[curEdge].node2 == curInNode || myEdgeInfo[curEdge].node2 == curOutNode); } while (curInNode != startInNode || curOutNode != startOutNode); ret.push_back(newBorder); } return ret; } connectome-workbench-1.2.3+git41-gc4c6c90/src/Files/BorderTracingHelper.h000066400000000000000000000072461300200146000256550ustar00rootroot00000000000000#ifndef __BORDER_TRACING_HELPER_H__ #define __BORDER_TRACING_HELPER_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 "CaretAssert.h" #include "CaretPointer.h" #include "StructureEnum.h" #include "TopologyHelper.h" #include namespace caret { class Border; class SurfaceFile; class BorderTracingHelper { int m_numNodes; StructureEnum::Enum m_structure; CaretPointer m_topoHelp; BorderTracingHelper();//no default BorderTracingHelper(const BorderTracingHelper&);//no copy BorderTracingHelper& operator=(const BorderTracingHelper&);//no assign std::vector > tracePrivate(std::vector& marked, const float& placement); public: BorderTracingHelper(const SurfaceFile* surfIn); template std::vector > traceData(T* data, const Test& myTester, const float& placement = 0.33f); //some useful selection objects class LabelSelect { int32_t m_select; LabelSelect(); public: LabelSelect(const int32_t& value) : m_select(value) { } bool operator() (const int32_t& toTest) const { return toTest == m_select; } }; template class LessThan { T m_threshold; bool m_inclusive; LessThan(); public: LessThan(const T& value, const bool& inclusive) : m_threshold(value), m_inclusive(inclusive) { } bool operator() (const T& toTest) const { if (m_inclusive) { return toTest <= m_threshold; } else { return toTest < m_threshold; } } }; template class GreaterThan { T m_threshold; bool m_inclusive; GreaterThan(); public: GreaterThan(const T& value, const bool& inclusive) : m_threshold(value), m_inclusive(inclusive) { } bool operator() (const T& toTest) const { if (m_inclusive) { return toTest >= m_threshold; } else { return toTest > m_threshold; } } }; }; template std::vector > BorderTracingHelper::traceData(T* data, const Test& myTester, const float& placement) { CaretAssert(placement >= 0.0f && placement <= 1.0f); std::vector marked(m_numNodes); for (int i = 0; i < m_numNodes; ++i) { marked[i] = (myTester(data[i]) ? 1 : 0); } return tracePrivate(marked, placement); } }//namespace #endif //__BORDER_TRACING_HELPER_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Files/BrainordinateRegionOfInterest.cxx000066400000000000000000000625041300200146000302710ustar00rootroot00000000000000 /*LICENSE_START*/ /* * Copyright (C) 2015 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 __BRAINORDINATE_REGION_OF_INTEREST_DECLARE__ #include "BrainordinateRegionOfInterest.h" #undef __BRAINORDINATE_REGION_OF_INTEREST_DECLARE__ #include "BoundingBox.h" #include "CaretAssert.h" #include "CiftiBrainordinateLabelFile.h" #include "CiftiMappableDataFile.h" #include "CiftiParcelsMap.h" #include "EventSurfaceStructuresValidGet.h" #include "EventManager.h" #include "GiftiLabelTable.h" #include "LabelFile.h" #include "SceneClass.h" #include "SceneClassAssistant.h" using namespace caret; /** * \class caret::BrainordinateRegionOfInterest * \brief Contains a region of interest * \ingroup Files * * Contains a region of interest that may include nodes from multiple * surfaces and voxels within a volume space. */ /** * Constructor. */ BrainordinateRegionOfInterest::BrainordinateRegionOfInterest() : CaretObject() { clear(); m_sceneAssistant = new SceneClassAssistant(); } /** * Destructor. */ BrainordinateRegionOfInterest::~BrainordinateRegionOfInterest() { clear(); delete m_sceneAssistant; } /** * Copy constructor. * @param obj * Object that is copied. */ BrainordinateRegionOfInterest::BrainordinateRegionOfInterest(const BrainordinateRegionOfInterest& obj) : CaretObject(obj), SceneableInterface(obj) { this->copyHelperBrainordinateRegionOfInterest(obj); } /** * Assignment operator. * @param obj * Data copied from obj to this. * @return * Reference to this object. */ BrainordinateRegionOfInterest& BrainordinateRegionOfInterest::operator=(const BrainordinateRegionOfInterest& obj) { if (this != &obj) { CaretObject::operator=(obj); this->copyHelperBrainordinateRegionOfInterest(obj); } return *this; } /** * Helps with copying an object of this type. * @param obj * Object that is copied. */ void BrainordinateRegionOfInterest::copyHelperBrainordinateRegionOfInterest(const BrainordinateRegionOfInterest& obj) { clear(); m_highlighingEnabled = obj.m_highlighingEnabled; for (std::vector::const_iterator iter = obj.m_surfaceNodesInfo.begin(); iter != obj.m_surfaceNodesInfo.end(); iter++) { const SurfaceNodesInfo* sni = *iter; m_surfaceNodesInfo.push_back(new SurfaceNodesInfo(*sni)); } m_voxelSize[0] = obj.m_voxelSize[0]; m_voxelSize[1] = obj.m_voxelSize[1]; m_voxelSize[2] = obj.m_voxelSize[2]; /* * Voxel sizes must be non-negative */ CaretAssert(m_voxelSize[0] >= 0.0); CaretAssert(m_voxelSize[1] >= 0.0); CaretAssert(m_voxelSize[2] >= 0.0); m_voxelXYZ = obj.m_voxelXYZ; } /** * Clear this region of interest. */ void BrainordinateRegionOfInterest::clear() { for (std::vector::iterator iter = m_surfaceNodesInfo.begin(); iter != m_surfaceNodesInfo.end(); iter++) { SurfaceNodesInfo* sni = *iter; CaretAssert(sni); delete sni; } m_surfaceNodesInfo.clear(); m_voxelSize[0] = 0.0; m_voxelSize[1] = 0.0; m_voxelSize[2] = 0.0; m_voxelXYZ.clear(); m_highlighingEnabled = false; } /** * @return True if this region of interest contains a surface with * valid nodes, else false. */ bool BrainordinateRegionOfInterest::hasSurfaceNodes() const { for (std::vector::const_iterator iter = m_surfaceNodesInfo.begin(); iter != m_surfaceNodesInfo.end(); iter++) { const SurfaceNodesInfo* sni = *iter; CaretAssert(sni); if ( ! sni->m_surfaceNodeIndices.empty()) { return true; } } return false; } /** * Does this region of interest contains surface nodes for the surface * with the given structure and number of nodes? * * @param structure * The surface's structure. * @param surfaceNumberOfNodes * Number of nodes in surface. * @return * True if found, else false. */ bool BrainordinateRegionOfInterest::hasNodesForSurfaceStructure(const StructureEnum::Enum structure, const int64_t surfaceNumberOfNodes) const { for (std::vector::const_iterator iter = m_surfaceNodesInfo.begin(); iter != m_surfaceNodesInfo.end(); iter++) { const SurfaceNodesInfo* sni = *iter; CaretAssert(sni); if ( ! sni->m_surfaceNodeIndices.empty()) { if ((sni->m_structure == structure) && (sni->m_surfaceNumberOfNodes == surfaceNumberOfNodes)) { return true; } } } return false; } /** * Get the nodes in the region of interest for the surface * with the given structure and number of nodes? * * @param structure * The surface's structure. * @param surfaceNumberOfNodes * Number of nodes in surface. * @return * Vector containing node indices. Empty if surface node found. */ const std::vector& BrainordinateRegionOfInterest::getNodesForSurfaceStructure(const StructureEnum::Enum structure, const int64_t surfaceNumberOfNodes) const { for (std::vector::const_iterator iter = m_surfaceNodesInfo.begin(); iter != m_surfaceNodesInfo.end(); iter++) { const SurfaceNodesInfo* sni = *iter; CaretAssert(sni); if ( ! sni->m_surfaceNodeIndices.empty()) { if ((sni->m_structure == structure) && (sni->m_surfaceNumberOfNodes == surfaceNumberOfNodes)) { return sni->m_surfaceNodeIndices; } } } return s_emptySurfaceNodes; } /** * @return True if the region of interest contains volume voxels. */ bool BrainordinateRegionOfInterest::hasVolumeVoxels() const { if ( ! m_voxelXYZ.empty()) { return true; } return false; } /** * Get the volume voxel size. * * @param voxelSizeOut * Size of the volume voxels. */ void BrainordinateRegionOfInterest::getVolumeVoxelSize(float voxelSizeOut[3]) const { /* * Voxel sizes must be non-negative */ CaretAssert(m_voxelSize[0] >= 0.0); CaretAssert(m_voxelSize[1] >= 0.0); CaretAssert(m_voxelSize[2] >= 0.0); voxelSizeOut[0] = m_voxelSize[0]; voxelSizeOut[1] = m_voxelSize[1]; voxelSizeOut[2] = m_voxelSize[2]; } /** * Get the volume voxels in the region-of-interest. * * @return * XYZ coordinates of the voxels. (3 per voxel ==> XYZ) * Empty if no voxels ni region of interest. */ const std::vector& BrainordinateRegionOfInterest::getVolumeVoxelsXYZ() const { return m_voxelXYZ; } /** * Set the nodes in the region of interest for the surface * with the given structure and number of nodes. If there is an * ROI for the struture and number of nodes, it is replaced. * * @param structure * The surface's structure. * @param surfaceNumberOfNodes * Number of nodes in surface. * @param surfaceNodeIndices * The surface node indices. */ void BrainordinateRegionOfInterest::setSurfaceNodes(const StructureEnum::Enum structure, const int64_t surfaceNumberOfNodes, const std::vector& surfaceNodeIndices) { int64_t replaceAtIndex = -1; const int64_t numItems = static_cast(m_surfaceNodesInfo.size()); for (int64_t i = 0; i < numItems; i++) { const SurfaceNodesInfo* sni = m_surfaceNodesInfo[i]; CaretAssert(sni); if ((sni->m_structure == structure) && (sni->m_surfaceNumberOfNodes == surfaceNumberOfNodes)) { delete sni; m_surfaceNodesInfo[i] = NULL; replaceAtIndex = i; break; } } SurfaceNodesInfo* sni = new SurfaceNodesInfo(structure, surfaceNumberOfNodes, surfaceNodeIndices); if (replaceAtIndex >= 0) { CaretAssertVectorIndex(m_surfaceNodesInfo, replaceAtIndex); CaretAssert(m_surfaceNodesInfo[replaceAtIndex] = NULL); m_surfaceNodesInfo[replaceAtIndex] = sni; } else { m_surfaceNodesInfo.push_back(sni); } } /** * Set the volume voxels in the region of interest. Replaces all * voxels in the region of interest. * * @param voxelSize * Size of the voxels. * @param voxelsXYZ * Coordinates of the voxels (3 per voxel ==> XYZ) */ void BrainordinateRegionOfInterest::setVolumeVoxels(const float voxelSize[3], const std::vector& voxelsXYZ) { m_voxelSize[0] = std::fabs(voxelSize[0]); m_voxelSize[1] = std::fabs(voxelSize[1]); m_voxelSize[2] = std::fabs(voxelSize[2]); m_voxelXYZ = voxelsXYZ; /* * Voxel sizes must be non-negative */ CaretAssert(m_voxelSize[0] >= 0.0); CaretAssert(m_voxelSize[1] >= 0.0); CaretAssert(m_voxelSize[2] >= 0.0); } /** * @return Is brainordinate highlighting enabled * */ bool BrainordinateRegionOfInterest::isBrainordinateHighlightingEnabled() const { return m_highlighingEnabled; } /** * Set brainordinate highlighting. * * @param enabled * New status for highlighting. */ void BrainordinateRegionOfInterest::setBrainordinateHighlightingEnabled(const bool enabled) { m_highlighingEnabled = enabled; } /** * Set the region of interest to the brainordinates in the given named label * from the labels used for loading data in the given CIFTI file. * * @param ciftiMappableDataFile * The CIFTI file. * @param mapIndex * Index of the map. * @param labelName * Name of the parcel. * @param errorMessageOut * Will contain error message upon exit. * @return * True if successful. If failure, false is returned and errorMessageOut * will contain cause of failure. */ bool BrainordinateRegionOfInterest::setWithLabelFileLabel(const CaretMappableDataFile* caretMappableDataFile, const int32_t mapIndex, const AString& labelName, AString& errorMessageOut) { clear(); errorMessageOut.clear(); if (caretMappableDataFile == NULL) { errorMessageOut = "File is not valid (NULL)"; return false; } if ( ! caretMappableDataFile->isMappedWithLabelTable()) { errorMessageOut = "File is not mapped with a label table."; return false; } if ((mapIndex < 0) || (mapIndex >= caretMappableDataFile->getNumberOfMaps())) { errorMessageOut = ("Map index=" + AString::number(mapIndex) + " is invalid for file " + caretMappableDataFile->getFileNameNoPath()); return false; } if (labelName.isEmpty()) { errorMessageOut = "Label name is empty."; return false; } const GiftiLabelTable* labelTable = caretMappableDataFile->getMapLabelTable(mapIndex); CaretAssert(labelTable); if (labelTable != NULL) { const int64_t labelKey = labelTable->getLabelKeyFromName(labelName); if (labelKey < 0) { errorMessageOut = ("Label " + labelName + " not found in label table."); return false; } const LabelFile* labelFile = dynamic_cast(caretMappableDataFile); const CiftiBrainordinateLabelFile* ciftiLabelFile = dynamic_cast(caretMappableDataFile); if (labelFile != NULL) { const StructureEnum::Enum structure = labelFile->getStructure(); int64_t surfaceNumberOfNodes = labelFile->getNumberOfNodes(); std::vector nodeIndices; labelFile->getNodeIndicesWithLabelKey(mapIndex, labelKey, nodeIndices); if (nodeIndices.empty()) { errorMessageOut = ("No vertices found for label " + labelName); return false; } std::vector nodeIndices64(nodeIndices.begin(), nodeIndices.end()); setSurfaceNodes(structure, surfaceNumberOfNodes, nodeIndices64); return true; } else if (ciftiLabelFile != NULL) { bool haveBrainordinatesFlag = false; EventSurfaceStructuresValidGet structureNumberOfNodesEvent; EventManager::get()->sendEvent(structureNumberOfNodesEvent.getPointer()); std::map structNodes = structureNumberOfNodesEvent.getStructuresAndNumberOfNodes(); for (std::map::iterator structNodeIter = structNodes.begin(); structNodeIter != structNodes.end(); structNodeIter++) { const StructureEnum::Enum structure = structNodeIter->first; const int32_t surfaceNumberOfNodes = structNodeIter->second; std::vector nodeIndices; ciftiLabelFile->getNodeIndicesWithLabelKey(structure, surfaceNumberOfNodes, mapIndex, labelKey, nodeIndices); if ( ! nodeIndices.empty()) { std::vector nodeIndices64(nodeIndices.begin(), nodeIndices.end()); setSurfaceNodes(structure, surfaceNumberOfNodes, nodeIndices64); haveBrainordinatesFlag = true; } } std::vector voxelsXYZ; ciftiLabelFile->getVoxelCoordinatesWithLabelKey(mapIndex, labelKey, voxelsXYZ); if ( ! voxelsXYZ.empty()) { m_voxelXYZ.insert(m_voxelXYZ.end(), voxelsXYZ.begin(), voxelsXYZ.end()); ciftiLabelFile->getVoxelSpacing(m_voxelSize[0], m_voxelSize[1], m_voxelSize[2]); m_voxelSize[0] = std::fabs(m_voxelSize[0]); m_voxelSize[1] = std::fabs(m_voxelSize[1]); m_voxelSize[2] = std::fabs(m_voxelSize[2]); haveBrainordinatesFlag = true; } if (haveBrainordinatesFlag) { return true; } errorMessageOut = ("No brainordinates found for label " + labelName); return false; } } errorMessageOut = (caretMappableDataFile->getFileNameNoPath() + " is not a label type file or not recognized as label file (programming error)."); return false; } /** * Set the region of interest to the brainordinates in the given named parcel * from the parcels used for loading data in the given CIFTI file. * * @param ciftiMappableDataFile * The CIFTI file. * @param mapIndex * Index of the map. * @param parcelName * Name of the parcel. * @param errorMessageOut * Will contain error message upon exit. * @return * True if successful. If failure, false is returned and errorMessageOut * will contain cause of failure. */ bool BrainordinateRegionOfInterest::setWithCiftiParcelLoadingBrainordinates(const CiftiMappableDataFile* ciftiMappableDataFile, const int32_t mapIndex, const AString& parcelName, AString& errorMessageOut) { clear(); errorMessageOut.clear(); if (ciftiMappableDataFile == NULL) { errorMessageOut = "File is not valid (NULL)"; return false; } if ((mapIndex < 0) || (mapIndex >= ciftiMappableDataFile->getNumberOfMaps())) { errorMessageOut = ("Map index=" + AString::number(mapIndex) + " is invalid for file " + ciftiMappableDataFile->getFileNameNoPath()); return false; } const CiftiParcelsMap* ciftiParcelsMap = ciftiMappableDataFile->getCiftiParcelsMapForLoading(); return setWithCiftiParcelBrainordinates(ciftiMappableDataFile, ciftiParcelsMap, parcelName, errorMessageOut); } /** * Set the region of interest to the brainordinates in the given named parcel * from the parcels used for mapping data in the given CIFTI file. * * @param ciftiMappableDataFile * The CIFTI file. * @param mapIndex * Index of the map. * @param parcelName * Name of the parcel. * @param errorMessageOut * Will contain error message upon exit. * @return * True if successful. If failure, false is returned and errorMessageOut * will contain cause of failure. */ bool BrainordinateRegionOfInterest::setWithCiftiParcelMappingBrainordinates(const CiftiMappableDataFile* ciftiMappableDataFile, const int32_t mapIndex, const AString& parcelName, AString& errorMessageOut) { clear(); errorMessageOut.clear(); if (ciftiMappableDataFile == NULL) { errorMessageOut = "File is not valid (NULL)"; return false; } if ((mapIndex < 0) || (mapIndex >= ciftiMappableDataFile->getNumberOfMaps())) { errorMessageOut = ("Map index=" + AString::number(mapIndex) + " is invalid for file " + ciftiMappableDataFile->getFileNameNoPath()); return false; } const CiftiParcelsMap* ciftiParcelsMap = ciftiMappableDataFile->getCiftiParcelsMapForBrainordinateMapping(); return setWithCiftiParcelBrainordinates(ciftiMappableDataFile, ciftiParcelsMap, parcelName, errorMessageOut); } /** * Set the region of interest to the brainordinates in the given named parcel * from the given CIFTI parcels map in the given CIFTI file. * * @param ciftiMappableDataFile * The CIFTI file. * @param ciftiParcelsMap * The CIFTI Parcels Map. * @param parcelName * Name of the parcel. * @param errorMessageOut * Will contain error message upon exit. * @return * True if successful. If failure, false is returned and errorMessageOut * will contain cause of failure. */ bool BrainordinateRegionOfInterest::setWithCiftiParcelBrainordinates(const CiftiMappableDataFile* ciftiMappableDataFile, const CiftiParcelsMap* ciftiParcelsMap, const AString& parcelName, AString& errorMessageOut) { if (parcelName.isEmpty()) { errorMessageOut = "Parcel name is empty."; return false; } if (ciftiParcelsMap == NULL) { errorMessageOut = ("No parcels map in " + ciftiMappableDataFile->getFileNameNoPath()); return false; } const int64_t parcelIndex = ciftiParcelsMap->getIndexFromNumberOrName(parcelName); if (parcelIndex < 0) { errorMessageOut = ("Parcel name=" + parcelName + " not found in parcels map for file " + ciftiMappableDataFile->getFileNameNoPath()); return false; } const std::vector& allParcels = ciftiParcelsMap->getParcels(); CaretAssertVectorIndex(allParcels, parcelIndex); const CiftiParcelsMap::Parcel& parcel = allParcels[parcelIndex]; for (std::map >::const_iterator iter = parcel.m_surfaceNodes.begin(); iter != parcel.m_surfaceNodes.end(); iter++) { const StructureEnum::Enum structure = iter->first; const std::set& nodeSet = iter->second; int64_t surfaceNumberOfNodes = ciftiParcelsMap->getSurfaceNumberOfNodes(structure); std::vector nodeVector(nodeSet.begin(), nodeSet.end()); setSurfaceNodes(structure, surfaceNumberOfNodes, nodeVector); } if (ciftiParcelsMap->hasVolumeData()) { const VolumeSpace& volumeSpace = ciftiParcelsMap->getVolumeSpace(); const std::set& voxelSetIJK = parcel.m_voxelIndices; for (std::set::iterator voxelIter = voxelSetIJK.begin(); voxelIter != voxelSetIJK.end(); voxelIter++) { const VoxelIJK& voxelIJK = *voxelIter; float xyz[3]; volumeSpace.indexToSpace(voxelIJK.m_ijk, xyz); m_voxelXYZ.push_back(xyz[0]); m_voxelXYZ.push_back(xyz[1]); m_voxelXYZ.push_back(xyz[2]); } if ( ! m_voxelXYZ.empty()) { VolumeSpace::OrientTypes orientation[3]; float origin[3]; volumeSpace.getOrientAndSpacingForPlumb(orientation, m_voxelSize, origin); m_voxelSize[0] = std::fabs(m_voxelSize[0]); m_voxelSize[1] = std::fabs(m_voxelSize[1]); m_voxelSize[2] = std::fabs(m_voxelSize[2]); /* * Voxel sizes must be non-negative */ CaretAssert(m_voxelSize[0] >= 0.0); CaretAssert(m_voxelSize[1] >= 0.0); CaretAssert(m_voxelSize[2] >= 0.0); } } return true; } /** * Get a description of this object's content. * @return String describing this object's content. */ AString BrainordinateRegionOfInterest::toString() const { return "BrainordinateRegionOfInterest"; } /** * 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* BrainordinateRegionOfInterest::saveToScene(const SceneAttributes* sceneAttributes, const AString& instanceName) { SceneClass* sceneClass = new SceneClass(instanceName, "BrainordinateRegionOfInterest", 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 BrainordinateRegionOfInterest::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); } connectome-workbench-1.2.3+git41-gc4c6c90/src/Files/BrainordinateRegionOfInterest.h000066400000000000000000000145031300200146000277120ustar00rootroot00000000000000#ifndef __BRAINORDINATE_REGION_OF_INTEREST_H__ #define __BRAINORDINATE_REGION_OF_INTEREST_H__ /*LICENSE_START*/ /* * Copyright (C) 2015 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 "SceneableInterface.h" #include "StructureEnum.h" namespace caret { class CaretMappableDataFile; class CiftiMappableDataFile; class CiftiParcelsMap; class SceneClassAssistant; class BrainordinateRegionOfInterest : public CaretObject, public SceneableInterface { public: BrainordinateRegionOfInterest(); virtual ~BrainordinateRegionOfInterest(); BrainordinateRegionOfInterest(const BrainordinateRegionOfInterest& obj); BrainordinateRegionOfInterest& operator=(const BrainordinateRegionOfInterest& obj); void clear(); bool hasSurfaceNodes() const; bool hasNodesForSurfaceStructure(const StructureEnum::Enum structure, const int64_t surfaceNumberOfNodes) const; const std::vector& getNodesForSurfaceStructure(const StructureEnum::Enum structure, const int64_t surfaceNumberOfNodes) const; bool hasVolumeVoxels() const; void getVolumeVoxelSize(float voxelSizeOut[3]) const; const std::vector& getVolumeVoxelsXYZ() const; void setSurfaceNodes(const StructureEnum::Enum structure, const int64_t surfaceNumberOfNodes, const std::vector& surfaceNodeIndices); void setVolumeVoxels(const float voxelSize[3], const std::vector& voxelsXYZ); bool isBrainordinateHighlightingEnabled() const; void setBrainordinateHighlightingEnabled(const bool highlighting); bool setWithLabelFileLabel(const CaretMappableDataFile* caretMappableDataFile, const int32_t mapIndex, const AString& labelName, AString& errorMessageOut); bool setWithCiftiParcelLoadingBrainordinates(const CiftiMappableDataFile* ciftiMappableDataFile, const int32_t mapIndex, const AString& parcelName, AString& errorMessageOut); bool setWithCiftiParcelMappingBrainordinates(const CiftiMappableDataFile* ciftiMappableDataFile, const int32_t mapIndex, const AString& parcelName, AString& errorMessageOut); // 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 implemetation 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 copyHelperBrainordinateRegionOfInterest(const BrainordinateRegionOfInterest& obj); bool setWithCiftiParcelBrainordinates(const CiftiMappableDataFile* ciftiMappableDataFile, const CiftiParcelsMap* ciftiParcelsMap, const AString& parcelName, AString& errorMessageOut); SceneClassAssistant* m_sceneAssistant; class SurfaceNodesInfo { public: SurfaceNodesInfo(const StructureEnum::Enum structure, const int64_t surfaceNumberOfNodes, const std::vector& surfaceNodeIndices) : m_structure(structure), m_surfaceNumberOfNodes(surfaceNumberOfNodes), m_surfaceNodeIndices(surfaceNodeIndices) { /* nothing */ } ~SurfaceNodesInfo() { } const StructureEnum::Enum m_structure; const int64_t m_surfaceNumberOfNodes; const std::vector m_surfaceNodeIndices; }; std::vector m_surfaceNodesInfo; float m_voxelSize[3]; std::vector m_voxelXYZ; static std::vector s_emptySurfaceNodes; bool m_highlighingEnabled; // ADD_NEW_MEMBERS_HERE }; #ifdef __BRAINORDINATE_REGION_OF_INTEREST_DECLARE__ std::vector BrainordinateRegionOfInterest::s_emptySurfaceNodes; #endif // __BRAINORDINATE_REGION_OF_INTEREST_DECLARE__ } // namespace #endif //__BRAINORDINATE_REGION_OF_INTEREST_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Files/CMakeLists.txt000077500000000000000000000146351300200146000243620ustar00rootroot00000000000000# # Name of project # PROJECT (Files) # # Need XML from Qt # SET(QT_DONT_USE_QTGUI) SET(QT_USE_QTNETWORK TRUE) # # Add QT for includes # INCLUDE (${QT_USE_FILE}) # # Files Library # ADD_LIBRARY(Files AffineFile.h AnnotationFile.h AnnotationFileXmlFormatBase.h AnnotationFileXmlReader.h AnnotationFileXmlWriter.h Border.h BorderException.h BorderFile.h BorderLengthHelper.h BorderPointFromSearch.h BorderTracingHelper.h BrainordinateRegionOfInterest.h CaretDataFile.h CaretDataFileHelper.h CaretMappableDataFile.h CaretSparseFile.h CaretVolumeExtension.h ChartableLineSeriesBrainordinateInterface.h ChartableLineSeriesInterface.h ChartableLineSeriesRowColumnInterface.h ChartableMatrixInterface.h ChartableMatrixParcelInterface.h ChartableMatrixSeriesInterface.h CiftiBrainordinateDataSeriesFile.h CiftiBrainordinateLabelFile.h CiftiBrainordinateScalarFile.h CiftiConnectivityMatrixDenseFile.h CiftiConnectivityMatrixDenseDynamicFile.h CiftiConnectivityMatrixDenseParcelFile.h CiftiConnectivityMatrixParcelFile.h CiftiConnectivityMatrixParcelDenseFile.h CiftiFiberOrientationFile.h CiftiFiberTrajectoryFile.h CiftiMappableDataFile.h CiftiMappableConnectivityMatrixDataFile.h CiftiParcelColoringModeEnum.h CiftiParcelLabelFile.h CiftiParcelReordering.h CiftiParcelReorderingModel.h CiftiParcelSeriesFile.h CiftiParcelScalarFile.h CiftiScalarDataSeriesFile.h ConnectivityDataLoaded.h ControlPointFile.h EventCaretMappableDataFilesGet.h EventChartMatrixParcelYokingValidation.h EventGetDisplayedDataFiles.h EventMapYokingSelectMap.h EventMapYokingValidation.h EventSurfaceColoringInvalidate.h EventSurfaceStructuresValidGet.h Fiber.h FiberOrientation.h FiberOrientationColoringTypeEnum.h FiberOrientationTrajectory.h FiberTrajectoryColorModel.h FiberTrajectoryMapProperties.h FiberTrajectoryDisplayModeEnum.h FilePathNamePrefixCompactor.h FociFile.h FociFileSaxReader.h Focus.h GeodesicHelper.h GiftiTypeFile.h GroupAndNameCheckStateEnum.h GroupAndNameHierarchyGroup.h GroupAndNameHierarchyItem.h GroupAndNameHierarchyModel.h GroupAndNameHierarchyName.h ImageCaptureDimensionsModeEnum.h ImageCaptureSettings.h ImageFile.h ImageResolutionUnitsEnum.h ImageSpatialUnitsEnum.h LabelDrawingProperties.h LabelDrawingTypeEnum.h LabelFile.h MapYokingGroupEnum.h MetricFile.h MetricSmoothingObject.h NodeAndVoxelColoring.h OxfordSparseThreeFile.h PaletteFile.h RgbaFile.h RibbonMappingHelper.h SceneFile.h SceneFileSaxReader.h SignedDistanceHelper.h SparseVolumeIndexer.h SpecFile.h SpecFileDataFileTypeGroup.h SpecFileDataFile.h SpecFileSaxReader.h StudyMetaDataLink.h StudyMetaDataLinkSet.h StudyMetaDataLinkSetSaxReader.h SurfaceFile.h SurfaceProjectedItem.h SurfaceProjectedItemSaxReader.h SurfaceProjection.h SurfaceProjectionBarycentric.h SurfaceProjectionVanEssen.h SurfaceProjector.h SurfaceProjectorException.h SurfaceResamplingHelper.h SurfaceResamplingMethodEnum.h SurfaceTypeEnum.h TextFile.h TopologyHelper.h VolumeEditingModeEnum.h VolumeFile.h VolumeFileEditorDelegate.h VolumeFileVoxelColorizer.h VolumeMapUndoCommand.h VolumePaddingHelper.h VolumeSliceProjectionTypeEnum.h VolumeSpline.h VtkFileExporter.h WarpfieldFile.h XmlStreamReaderHelper.h XmlStreamWriterHelper.h AffineFile.cxx AnnotationFile.cxx AnnotationFileXmlFormatBase.cxx AnnotationFileXmlReader.cxx AnnotationFileXmlWriter.cxx Border.cxx BorderException.cxx BorderFile.cxx BorderLengthHelper.cxx BorderTracingHelper.cxx BrainordinateRegionOfInterest.cxx CaretDataFile.cxx CaretDataFileHelper.cxx CaretMappableDataFile.cxx CaretSparseFile.cxx CaretVolumeExtension.cxx ChartableLineSeriesInterface.cxx ChartableMatrixInterface.cxx CiftiBrainordinateDataSeriesFile.cxx CiftiBrainordinateLabelFile.cxx CiftiBrainordinateScalarFile.cxx CiftiConnectivityMatrixDenseFile.cxx CiftiConnectivityMatrixDenseDynamicFile.cxx CiftiConnectivityMatrixDenseParcelFile.cxx CiftiConnectivityMatrixParcelFile.cxx CiftiConnectivityMatrixParcelDenseFile.cxx CiftiFiberOrientationFile.cxx CiftiFiberTrajectoryFile.cxx CiftiMappableDataFile.cxx CiftiMappableConnectivityMatrixDataFile.cxx CiftiParcelColoringModeEnum.cxx CiftiParcelLabelFile.cxx CiftiParcelReordering.cxx CiftiParcelReorderingModel.cxx CiftiParcelSeriesFile.cxx CiftiParcelScalarFile.cxx CiftiScalarDataSeriesFile.cxx ConnectivityDataLoaded.cxx ControlPointFile.cxx EventCaretMappableDataFilesGet.cxx EventChartMatrixParcelYokingValidation.cxx EventGetDisplayedDataFiles.cxx EventMapYokingSelectMap.cxx EventMapYokingValidation.cxx EventSurfaceColoringInvalidate.cxx EventSurfaceStructuresValidGet.cxx Fiber.cxx FiberOrientation.cxx FiberOrientationColoringTypeEnum.cxx FiberOrientationTrajectory.cxx FiberTrajectoryColorModel.cxx FiberTrajectoryDisplayModeEnum.cxx FiberTrajectoryMapProperties.cxx FilePathNamePrefixCompactor.cxx FociFile.cxx FociFileSaxReader.cxx Focus.cxx GeodesicHelper.cxx GiftiTypeFile.cxx GroupAndNameCheckStateEnum.cxx GroupAndNameHierarchyGroup.cxx GroupAndNameHierarchyItem.cxx GroupAndNameHierarchyModel.cxx GroupAndNameHierarchyName.cxx ImageCaptureDimensionsModeEnum.cxx ImageCaptureSettings.cxx ImageFile.cxx ImageResolutionUnitsEnum.cxx ImageSpatialUnitsEnum.cxx LabelDrawingProperties.cxx LabelDrawingTypeEnum.cxx LabelFile.cxx MapYokingGroupEnum.cxx MetricFile.cxx MetricSmoothingObject.cxx NodeAndVoxelColoring.cxx OxfordSparseThreeFile.cxx PaletteFile.cxx RgbaFile.cxx RibbonMappingHelper.cxx SceneFile.cxx SceneFileSaxReader.cxx SignedDistanceHelper.cxx SparseVolumeIndexer.cxx SpecFile.cxx SpecFileDataFileTypeGroup.cxx SpecFileDataFile.cxx SpecFileSaxReader.cxx StudyMetaDataLink.cxx StudyMetaDataLinkSet.cxx StudyMetaDataLinkSetSaxReader.cxx SurfaceFile.cxx SurfaceProjectedItem.cxx SurfaceProjectedItemSaxReader.cxx SurfaceProjection.cxx SurfaceProjectionBarycentric.cxx SurfaceProjectionVanEssen.cxx SurfaceProjector.cxx SurfaceProjectorException.cxx SurfaceResamplingHelper.cxx SurfaceResamplingMethodEnum.cxx SurfaceTypeEnum.cxx TextFile.cxx TopologyHelper.cxx VolumeEditingModeEnum.cxx VolumeFile.cxx VolumeFileEditorDelegate.cxx VolumeFileVoxelColorizer.cxx VolumeMapUndoCommand.cxx VolumePaddingHelper.cxx VolumeSliceProjectionTypeEnum.cxx VolumeSpline.cxx VtkFileExporter.cxx WarpfieldFile.cxx XmlStreamReaderHelper.cxx XmlStreamWriterHelper.cxx ) # # Find Headers # INCLUDE_DIRECTORIES( ${CMAKE_SOURCE_DIR}/Annotations ${CMAKE_SOURCE_DIR}/Charting ${CMAKE_SOURCE_DIR}/Files ${CMAKE_SOURCE_DIR}/FilesBase ${CMAKE_SOURCE_DIR}/Cifti ${CMAKE_SOURCE_DIR}/Palette ${CMAKE_SOURCE_DIR}/Gifti ${CMAKE_SOURCE_DIR}/Nifti ${CMAKE_SOURCE_DIR}/Scenes ${CMAKE_SOURCE_DIR}/Xml ${CMAKE_SOURCE_DIR}/Common ) connectome-workbench-1.2.3+git41-gc4c6c90/src/Files/CaretDataFile.cxx000066400000000000000000000330061300200146000247640ustar00rootroot00000000000000 /*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 __CARET_DATA_FILE_DECLARE__ #include "CaretDataFile.h" #undef __CARET_DATA_FILE_DECLARE__ #include "CaretMappableDataFile.h" #include "DataFileContentInformation.h" #include "SceneClass.h" using namespace caret; /** * \class caret::CaretDataFile * \brief A data file with abstract methods for caret data * * This class is essentially an interface that defines methods * that are supported by most Caret Data Files. */ /** * Constructor. */ CaretDataFile::CaretDataFile(const DataFileTypeEnum::Enum dataFileType) : DataFile(), SceneableInterface() { m_dataFileType = dataFileType; AString name = (DataFileTypeEnum::toName(m_dataFileType).toLower() + "_file_" + AString::number(s_defaultFileNameCounter) + "." + DataFileTypeEnum::toFileExtension(m_dataFileType)); s_defaultFileNameCounter++; setFileName(name); } /** * Destructor. */ CaretDataFile::~CaretDataFile() { } /** * @return The type of this data file. */ DataFileTypeEnum::Enum CaretDataFile::getDataFileType() const { return m_dataFileType; } /** * Override the default data type for the file. * Use this with extreme caution as using a type invalid * with the file may cause disaster. * * @param dataFileType * New value for file's data type. */ void CaretDataFile::setDataFileType(const DataFileTypeEnum::Enum dataFileType) { m_dataFileType = dataFileType; } /** * Copy constructor. * @param cdf * Instance that is copied to this. */ CaretDataFile::CaretDataFile(const CaretDataFile& cdf) : DataFile(cdf), SceneableInterface(cdf) { copyDataCaretDataFile(cdf); } /** * Assignment operator. * @param cdf * Instance that is assigned to this. */ CaretDataFile& CaretDataFile::operator=(const CaretDataFile& cdf) { if (this != &cdf) { DataFile::operator=(cdf); copyDataCaretDataFile(cdf); } return *this; } /** * Assists with copying instances of this class. * @param cdf * Instance that is copied to this. */ void CaretDataFile::copyDataCaretDataFile(const CaretDataFile& cdf) { m_dataFileType = cdf.m_dataFileType; } /** * @return Is this file mapped to a valid single structure ? */ bool CaretDataFile::isSingleStructure() const { return StructureEnum::isSingleStructure(getStructure()); } /** * Add information about the file to the data file information. * * @param dataFileInformation * Consolidates information about a data file. */ void CaretDataFile::addToDataFileContentInformation(DataFileContentInformation& dataFileInformation) { DataFile::addToDataFileContentInformation(dataFileInformation); const DataFileTypeEnum::Enum dataFileType = getDataFileType(); bool validExtensionFlag = false; const DataFileTypeEnum::Enum extensionFileType = DataFileTypeEnum::fromFileExtension(getFileName(), &validExtensionFlag); if (dataFileType != extensionFileType) { const AString msg("Incorrect filename extension. It should be \"" + DataFileTypeEnum::toFileExtension(dataFileType) + "\""); dataFileInformation.addNameAndValue("WARNING EXTENSION", msg); } dataFileInformation.addNameAndValue("Type", DataFileTypeEnum::toGuiName(m_dataFileType)); std::vector allStructures; StructureEnum::getAllEnums(allStructures); CaretMappableDataFile* cmdf = dynamic_cast(this); if (cmdf != NULL) { AString structureNames; for (std::vector::iterator iter = allStructures.begin(); iter != allStructures.end(); iter++) { if (cmdf->isMappableToSurfaceStructure(*iter)) { structureNames.append(StructureEnum::toGuiName(*iter) + " "); } } dataFileInformation.addNameAndValue("Structure", structureNames); } else { dataFileInformation.addNameAndValue("Structure", StructureEnum::toGuiName(getStructure())); } } /** * Set the username and password for reading files, typically from * a database or website. * * @param username * Account's username. * @param password * Account's password. */ void CaretDataFile::setFileReadingUsernameAndPassword(const AString& username, const AString& password) { s_fileReadingUsername = username; s_fileReadingPassword = password; } /** * @return The username for file reading from database or website. */ AString CaretDataFile::getFileReadingUsername() { return s_fileReadingUsername; } /** * @return The password for file reading from database or website. */ AString CaretDataFile::getFileReadingPassword() { return s_fileReadingPassword; } /** * Create a scene for an instance of a class. * * NOTE: In most cases, subclasses should not override this method but * instead override saveFileDataToScene() use it to add 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 * saving the scene. * * @return Pointer to SceneClass object representing the state of * this object. If there is no data for the scene, a NULL pointer * will be returned. */ SceneClass* CaretDataFile::saveToScene(const SceneAttributes* sceneAttributes, const AString& instanceName) { SceneClass* sceneClass = new SceneClass(instanceName, "CaretDataFile", 1); saveFileDataToScene(sceneAttributes, sceneClass); if (sceneClass->getNumberOfObjects() <= 0) { delete sceneClass; sceneClass = NULL; } // const int32_t numMaps = getNumberOfMaps(); // if (numMaps > 0) { // bool* mapEnabledArray = new bool[numMaps]; // for (int32_t i = 0; i < numMaps; i++) { // mapEnabledArray[i] = m_mapContent[i]->m_dataLoadingEnabled; // } // // sceneClass->addBooleanArray("mapEnabled", // mapEnabledArray, // numMaps); // delete[] mapEnabledArray; // } return sceneClass; } /** * Restore the state of an instance of a class. * * * NOTE: In most cases, subclasses should not override this method but * instead override restoreFileDataFromScene() use it to access * 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. May be NULL for some types of scenes. */ void CaretDataFile::restoreFromScene(const SceneAttributes* sceneAttributes, const SceneClass* sceneClass) { if (sceneClass == NULL) { return; } /* * Resoration of file data from the scene may cause * a modified palette status if the palette color * mapping was saved to the scene. We want to * keep this modified status so that if the scene * is resaved, the modified palette information * is added to the scene. */ restoreFileDataFromScene(sceneAttributes, sceneClass); } /** * Save file data from the scene. For subclasses that need to * save to a scene, this method should be overriden. sceneClass * will be valid and any scene data should be added to it. * * @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. */ void CaretDataFile::saveFileDataToScene(const SceneAttributes* /*sceneAttributes*/, SceneClass* /*sceneClass*/) { /* Nothing as subclasses needing to save to scenes will override. */ } /** * Restore file data from the scene. For subclasses that need to * restore from a scene, this method should be overridden. The scene class * will be valid and any scene data may be obtained from it. * * @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 CaretDataFile::restoreFileDataFromScene(const SceneAttributes* /*sceneAttributes*/, const SceneClass* /*sceneClass*/) { /* Nothing as subclasses needing to restore from scenes will override. */ } /** * @return True if this file type supports writing, else false. * * By default, this method returns true. Files that do not support * writing should override this method and return false. */ bool CaretDataFile::supportsWriting() const { return true; } /** * @return The name of the file with both the file's path and * filename's extension removed. * * First, the filename is tested to see if it ends with any of the valid * file extensions for the DataFileTypeEnum. If any of the extensions match, * the filename, with the extension removed, is returned. The valud * file extensions are used since many GIFTI and CIFTI files contain * a "." in their extensions (eg: .surf.gii .dconn.nii). * * /mnt/path/anatomical.surf.gii "returns" anatomical * * Second, the last "/" (directory separator) is found to locate where * the name of the file, excluding the path, is located. Using just * the name of the file, anything before the last "." is returned. * removed. * * Third, if there is not "." in the name of the file * the equivalent of getFileName() is returned. */ AString CaretDataFile::getFileNameNoPathNoExtension() const { AString nameNoExt = getFileNameNoExtension(); const int lastSlashIndex = std::max(nameNoExt.lastIndexOf("/"), nameNoExt.lastIndexOf("\\")); if (lastSlashIndex >= 0) { if ((lastSlashIndex + 1) < nameNoExt.length()) { nameNoExt = nameNoExt.mid(lastSlashIndex + 1); } } return nameNoExt; } /** * @return The name (and path if present) of the file with the * filename's extension removed. * * First, the filename is tested to see if it ends with any of the valid * file extensions for the DataFileTypeEnum. If any of the extensions match, * the filename, with the extension removed, is returned. The valud * file extensions are used since many GIFTI and CIFTI files contain * a "." in their extensions (eg: .surf.gii .dconn.nii). * * /mnt/path/anatomical.surf.gii "returns" /mnt/path/anatomical * * Second, the last "/" (directory separator) is found to locate where * the name of the file, excluding the path, is located. Using just * the name of the file, anything before the last "." is returned. * removed. * * Third, if there is not "." in the name of the file * the equivalent of getFileName() is returned. */ AString CaretDataFile::getFileNameNoExtension() const { AString name = getFileName(); std::vector dataFileTypeExtensions = DataFileTypeEnum::getAllFileExtensions(getDataFileType()); /* * Test using file type extensions */ for (std::vector::iterator iter = dataFileTypeExtensions.begin(); iter != dataFileTypeExtensions.end(); iter++) { const AString ext = *iter; const int offset = name.lastIndexOf(ext); if (offset > 0) { name.resize(offset - 1); return name; } } /* * Look for the last "." and the last forward slash or back slash * * Dont' was to chop off a "." in the path (eg: /mnt/back.up/file) */ const int lastSlashIndex = std::max(name.lastIndexOf("/"), name.lastIndexOf("\\")); const int dotIndex = name.lastIndexOf("."); if (lastSlashIndex > 0) { if (dotIndex > lastSlashIndex) { name.resize(dotIndex); } } else { if (dotIndex > 0) { name.resize(dotIndex); } } return name; } connectome-workbench-1.2.3+git41-gc4c6c90/src/Files/CaretDataFile.h000066400000000000000000000100711300200146000244060ustar00rootroot00000000000000#ifndef __CARET_DATA_FILE__H_ #define __CARET_DATA_FILE__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 "DataFile.h" #include "DataFileTypeEnum.h" #include "SceneableInterface.h" #include "StructureEnum.h" namespace caret { class GiftiMetaData; class CaretDataFile : public DataFile, public SceneableInterface { public: CaretDataFile(const DataFileTypeEnum::Enum dataFileType); virtual ~CaretDataFile(); virtual bool isSingleStructure() const; /** * @return The structure for this file. */ virtual StructureEnum::Enum getStructure() const = 0; /** * Set the structure for this file. * @param structure * New structure for this file. */ virtual void setStructure(const StructureEnum::Enum structure) = 0; DataFileTypeEnum::Enum getDataFileType() const; /** * @return Get access to the file's metadata. */ virtual GiftiMetaData* getFileMetaData() = 0; /** * @return Get access to unmodifiable file's metadata. */ virtual const GiftiMetaData* getFileMetaData() const = 0; virtual AString getFileNameNoExtension() const; virtual AString getFileNameNoPathNoExtension() const; virtual void addToDataFileContentInformation(DataFileContentInformation& dataFileInformation); virtual bool supportsWriting() const; virtual SceneClass* saveToScene(const SceneAttributes* sceneAttributes, const AString& instanceName); virtual void restoreFromScene(const SceneAttributes* sceneAttributes, const SceneClass* sceneClass); static void setFileReadingUsernameAndPassword(const AString& username, const AString& password); static AString getFileReadingUsername(); static AString getFileReadingPassword(); protected: CaretDataFile(const CaretDataFile& cdf); CaretDataFile& operator=(const CaretDataFile& cdf); void setDataFileType(const DataFileTypeEnum::Enum dataFileType); virtual void saveFileDataToScene(const SceneAttributes* sceneAttributes, SceneClass* sceneClass); virtual void restoreFileDataFromScene(const SceneAttributes* sceneAttributes, const SceneClass* sceneClass); private: void copyDataCaretDataFile(const CaretDataFile& cdf); DataFileTypeEnum::Enum m_dataFileType; /** A counter that is used when creating default file names */ static int64_t s_defaultFileNameCounter; static AString s_fileReadingUsername; static AString s_fileReadingPassword; }; #ifdef __CARET_DATA_FILE_DECLARE__ int64_t CaretDataFile::s_defaultFileNameCounter = 1; AString CaretDataFile::s_fileReadingUsername = ""; AString CaretDataFile::s_fileReadingPassword = ""; #endif // __CARET_DATA_FILE_DECLARE__ } // namespace #endif //__CARET_DATA_FILE__H_ connectome-workbench-1.2.3+git41-gc4c6c90/src/Files/CaretDataFileHelper.cxx000066400000000000000000000412551300200146000261310ustar00rootroot00000000000000 /*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 __CARET_DATA_FILE_HELPER_DECLARE__ #include "CaretDataFileHelper.h" #undef __CARET_DATA_FILE_HELPER_DECLARE__ #include "AnnotationFile.h" #include "BorderFile.h" #include "CaretAssert.h" #include "CaretLogger.h" #include "CiftiBrainordinateDataSeriesFile.h" #include "CiftiBrainordinateLabelFile.h" #include "CiftiBrainordinateScalarFile.h" #include "CiftiConnectivityMatrixDenseFile.h" #include "CiftiConnectivityMatrixDenseDynamicFile.h" #include "CiftiConnectivityMatrixDenseParcelFile.h" #include "CiftiConnectivityMatrixParcelDenseFile.h" #include "CiftiConnectivityMatrixParcelFile.h" #include "CiftiFile.h" #include "CiftiParcelLabelFile.h" #include "CiftiParcelScalarFile.h" #include "CiftiParcelSeriesFile.h" #include "CiftiFiberOrientationFile.h" #include "CiftiFiberTrajectoryFile.h" #include "CiftiScalarDataSeriesFile.h" #include "FileInformation.h" #include "FociFile.h" #include "ImageFile.h" #include "LabelFile.h" #include "MetricFile.h" #include "PaletteFile.h" #include "RgbaFile.h" #include "SceneFile.h" #include "SpecFile.h" #include "SurfaceFile.h" #include "VolumeFile.h" #include "nifti2.h" using namespace caret; /** * \class caret::CaretDataFileHelper * \brief Contains static methods to help out with CaretDataFile subclasses. * \ingroup Files */ /** * Constructor. */ CaretDataFileHelper::CaretDataFileHelper() { } /** * Destructor. */ CaretDataFileHelper::~CaretDataFileHelper() { } /** * Read any CaretDataFile subclass. * * @param filename * Name of file. * @return * Pointer to the data file after reading the file. * @throw * DataFileException if unable to read the file for any reason. */ CaretDataFile* CaretDataFileHelper::readAnyCaretDataFile(const AString& filename, const bool& preferOnDisk) { bool isValid = false; const DataFileTypeEnum::Enum dataFileType = DataFileTypeEnum::fromFileExtension(filename, &isValid); CaretDataFile* caretDataFile = NULL; DataFileException dataFileException; bool dataFileExceptionValid = false; /* * If filename's extension is valid, try to read * the file using the matching file type. */ if (isValid && (dataFileType != DataFileTypeEnum::UNKNOWN)) { caretDataFile = createCaretDataFileForFileType(dataFileType); CaretAssert(caretDataFile); try { readCaretDataFile(caretDataFile, filename, preferOnDisk); } catch (const DataFileException& dfe) { delete caretDataFile; caretDataFile = NULL; dataFileException = dfe; dataFileExceptionValid = true; } } else { dataFileException = DataFileException(filename, "Filename extension does not match any supported data file."); dataFileExceptionValid = true; } if (caretDataFile == NULL) { /* * Examine contents of file to determine file type. * Filename could have wrong extension. */ const DataFileTypeEnum::Enum dataFileType = getDataFileTypeFromFileContent(filename); if (dataFileType != DataFileTypeEnum::UNKNOWN) { caretDataFile = createCaretDataFileForFileType(dataFileType); CaretAssert(dataFileType); CaretLogInfo("File " + filename + " appears to be of type " + DataFileTypeEnum::toGuiName(dataFileType) + " from examination of file content."); try { readCaretDataFile(caretDataFile, filename, preferOnDisk); /* * File was read successfully, exception no longer valid */ dataFileExceptionValid = false; } catch (const DataFileException& dfe) { delete caretDataFile; caretDataFile = NULL; /* * Do not override exception from reading * using the file's extension */ if ( ! dataFileExceptionValid) { dataFileException = dfe; dataFileExceptionValid = true; } } } else { CaretAssert(dataFileExceptionValid); } } if (dataFileExceptionValid) { throw dataFileException; } CaretAssert(caretDataFile); // if (( ! isValid) // || (dataFileType == DataFileTypeEnum::UNKNOWN)) { // throw DataFileException(filename, // "Filename extension does not match any supported data file."); // } // try { // if (preferOnDisk) caretDataFile->setPreferOnDiskReading(true);//NOTE: because Dense Connectivity also pays attention to this, never change default behaviors away from on disk // try { // caretDataFile->readFile(filename); // } // catch (const std::bad_alloc& badAlloc) { // /* // * This DataFileException will be caught // * in the outer try/catch and it will // * clean up to avoid memory leaks. // */ // throw DataFileException(filename, // createBadAllocExceptionMessage(filename)); // } // } // catch (const DataFileException& dfe) { // delete caretDataFile; // caretDataFile = NULL; // // DataFileTypeEnum::Enum dft = getDataFileTypeFromFileContent(filename); // std::cout << "File " << filename << " may be " << DataFileTypeEnum::toGuiName(dft) << std::endl; // // throw dfe; // // } return caretDataFile; } /** * Read the given data file using the given filename. * * @param caretDataFile * Caret data file instance into which data is read. * @param filename * Name of the file. * @throw * DataFileException if unable to read the file for any reason. */ void CaretDataFileHelper::readCaretDataFile(CaretDataFile* caretDataFile, const AString& filename, const bool& preferOnDisk) { try { if (preferOnDisk) caretDataFile->setPreferOnDiskReading(true);//NOTE: because Dense Connectivity also pays attention to this, never change default behaviors away from on disk try { caretDataFile->readFile(filename); } catch (const std::bad_alloc& badAlloc) { /* * This DataFileException will be caught * in the outer try/catch and it will * clean up to avoid memory leaks. */ throw DataFileException(filename, createBadAllocExceptionMessage(filename)); } } catch (const DataFileException& dfe) { throw dfe; } } /** * Creates a useful error message when a std::bad_alloc exception occurs. * * @param filename * Name of file that caused the std::bad_alloc exception. * @return * Message with info about the file. */ AString CaretDataFileHelper::createBadAllocExceptionMessage(const AString& filename) { FileInformation fileInfo(filename); AString message("Unable to allocate memory for reading the file."); if (fileInfo.exists()) { // float bytes = (float)fileInfo.size(); // short index = 0; // static const char *labels[9] = {" Bytes", " Kilobytes", " Megabytes", " Gigabytes", " Terabytes", " Petabytes", " Exabytes", " Zettabytes", " Yottabytes"}; // while (index < 8 && bytes > 1000.0f) // { // ++index; // bytes = bytes / 1000.0f;//using 1024 would make it Kibibytes, etc // } // AString sizeString = AString::number(bytes, 'f', 2) + labels[index];//2 digits after decimal point const AString sizeString = FileInformation::fileSizeToStandardUnits(fileInfo.size()); message.appendWithNewLine("File Size: " + sizeString); message.appendWithNewLine(""); message.appendWithNewLine("Note: The amount of memory required to read a data file may be " "substantially larger than the size of the file due to the way the " "file's data is organized in memory or compression of data within the file."); } return message; } /** * Create an instance of a caret data file matching the given data file type. * * @param dataFileType * Type of data file. * @return * New instance of file except if type is UNKNOWN which returns NULL. */ CaretDataFile* CaretDataFileHelper::createCaretDataFileForFileType(const DataFileTypeEnum::Enum dataFileType) { CaretDataFile* caretDataFile = NULL; switch (dataFileType) { case DataFileTypeEnum::ANNOTATION: caretDataFile = new AnnotationFile(); break; case DataFileTypeEnum::BORDER: caretDataFile = new BorderFile(); break; case DataFileTypeEnum::CONNECTIVITY_DENSE: caretDataFile = new CiftiConnectivityMatrixDenseFile(); break; case DataFileTypeEnum::CONNECTIVITY_DENSE_DYNAMIC: CaretAssertMessage(0, "Never create a dense dynamic file."); break; case DataFileTypeEnum::CONNECTIVITY_DENSE_LABEL: caretDataFile = new CiftiBrainordinateLabelFile(); break; case DataFileTypeEnum::CONNECTIVITY_DENSE_PARCEL: caretDataFile = new CiftiConnectivityMatrixDenseParcelFile(); break; case DataFileTypeEnum::CONNECTIVITY_DENSE_SCALAR: caretDataFile = new CiftiBrainordinateScalarFile(); break; case DataFileTypeEnum::CONNECTIVITY_DENSE_TIME_SERIES: caretDataFile = new CiftiBrainordinateDataSeriesFile(); break; case DataFileTypeEnum::CONNECTIVITY_FIBER_ORIENTATIONS_TEMPORARY: caretDataFile = new CiftiFiberOrientationFile(); break; case DataFileTypeEnum::CONNECTIVITY_FIBER_TRAJECTORY_TEMPORARY: caretDataFile = new CiftiFiberTrajectoryFile(); break; case DataFileTypeEnum::CONNECTIVITY_PARCEL: caretDataFile = new CiftiConnectivityMatrixParcelFile(); break; case DataFileTypeEnum::CONNECTIVITY_PARCEL_DENSE: caretDataFile = new CiftiConnectivityMatrixParcelDenseFile(); break; case DataFileTypeEnum::CONNECTIVITY_PARCEL_LABEL: caretDataFile = new CiftiParcelLabelFile(); break; case DataFileTypeEnum::CONNECTIVITY_PARCEL_SCALAR: caretDataFile = new CiftiParcelScalarFile(); break; case DataFileTypeEnum::CONNECTIVITY_PARCEL_SERIES: caretDataFile = new CiftiParcelSeriesFile(); break; case DataFileTypeEnum::CONNECTIVITY_SCALAR_DATA_SERIES: caretDataFile = new CiftiScalarDataSeriesFile(); break; case DataFileTypeEnum::FOCI: caretDataFile = new FociFile(); break; case DataFileTypeEnum::IMAGE: caretDataFile = new ImageFile(); break; case DataFileTypeEnum::LABEL: caretDataFile = new LabelFile(); break; case DataFileTypeEnum::METRIC: caretDataFile = new MetricFile(); break; case DataFileTypeEnum::PALETTE: caretDataFile = new PaletteFile(); break; case DataFileTypeEnum::RGBA: caretDataFile = new RgbaFile(); break; case DataFileTypeEnum::SCENE: caretDataFile = new SceneFile(); break; case DataFileTypeEnum::SPECIFICATION: caretDataFile = new SpecFile(); break; case DataFileTypeEnum::SURFACE: caretDataFile = new SurfaceFile(); break; case DataFileTypeEnum::UNKNOWN: CaretAssert(0); break; case DataFileTypeEnum::VOLUME: caretDataFile = new VolumeFile(); break; } return caretDataFile; } /** * Examine the content of a file to determine the file type. * * @param filename * Name of file. * @return * Type of file if it can be determined else the unknown file type. */ DataFileTypeEnum::Enum CaretDataFileHelper::getDataFileTypeFromFileContent(const AString& filename) { DataFileTypeEnum::Enum dataFileType = DataFileTypeEnum::UNKNOWN; if (DataFile::isFileOnNetwork(filename)) { return dataFileType; } if (filename.endsWith(".nii")) { try { CiftiFile ciftiFile; ciftiFile.openFile(filename); const CiftiXML ciftiXML = ciftiFile.getCiftiXML(); char intentName[16]; const int32_t intentType = ciftiXML.getIntentInfo(CiftiVersion(), intentName); switch (intentType) { case NIFTI_INTENT_CONNECTIVITY_UNKNOWN: // 3000; break; case NIFTI_INTENT_CONNECTIVITY_DENSE: // 3001; dataFileType = DataFileTypeEnum::CONNECTIVITY_DENSE; break; case NIFTI_INTENT_CONNECTIVITY_DENSE_SERIES: // 3002; dataFileType = DataFileTypeEnum::CONNECTIVITY_DENSE_TIME_SERIES; break; case NIFTI_INTENT_CONNECTIVITY_PARCELLATED: // 3003; dataFileType = DataFileTypeEnum::CONNECTIVITY_DENSE_PARCEL; break; case NIFTI_INTENT_CONNECTIVITY_PARCELLATED_SERIES: // 3004; dataFileType = DataFileTypeEnum::CONNECTIVITY_PARCEL_SERIES; break; case NIFTI_INTENT_CONNECTIVITY_DENSE_TRAJECTORY: // 3005; dataFileType = DataFileTypeEnum::CONNECTIVITY_FIBER_TRAJECTORY_TEMPORARY; break; case NIFTI_INTENT_CONNECTIVITY_DENSE_SCALARS: // 3006; dataFileType = DataFileTypeEnum::CONNECTIVITY_DENSE_SCALAR; break; case NIFTI_INTENT_CONNECTIVITY_DENSE_LABELS: // 3007; dataFileType = DataFileTypeEnum::CONNECTIVITY_DENSE_LABEL; break; case NIFTI_INTENT_CONNECTIVITY_PARCELLATED_SCALAR: // 3008; dataFileType = DataFileTypeEnum::CONNECTIVITY_PARCEL_SCALAR; break; case NIFTI_INTENT_CONNECTIVITY_PARCELLATED_DENSE: // 3009; dataFileType = DataFileTypeEnum::CONNECTIVITY_PARCEL_DENSE; break; case NIFTI_INTENT_CONNECTIVITY_DENSE_PARCELLATED: // 3010; dataFileType = DataFileTypeEnum::CONNECTIVITY_DENSE_PARCEL; break; // case NIFTI_INTENT_CONNECTIVITY_PARCELLATED_PARCELLATED_SERIES: // 3011; // dataFileType = DataFileTypeEnum::CONNECTIVITY_PARCEL_SERIES // break; // case NIFTI_INTENT_CONNECTIVITY_PARCELLATED_PARCELLATED_SCALAR: // 3012; // dataFileType = DataFileTypeEnum::CONNECTIVITY_DENSE; // break; default: CaretLogInfo(filename + " has unrecognized CIFTI intent type=" + AString::number(intentType)); break; } } catch (const DataFileException& dfe) { CaretLogInfo(filename + " could not be read as a CIFTI file"); } } return dataFileType; } connectome-workbench-1.2.3+git41-gc4c6c90/src/Files/CaretDataFileHelper.h000066400000000000000000000042711300200146000255530ustar00rootroot00000000000000#ifndef __CARET_DATA_FILE_HELPER_H__ #define __CARET_DATA_FILE_HELPER_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 "AString.h" #include "DataFileTypeEnum.h" namespace caret { class CaretDataFile; class CaretDataFileHelper { public: static CaretDataFile* readAnyCaretDataFile(const AString& filename, const bool& preferOnDisk = false); static AString createBadAllocExceptionMessage(const AString& filename); static DataFileTypeEnum::Enum getDataFileTypeFromFileContent(const AString& filename); static CaretDataFile* createCaretDataFileForFileType(const DataFileTypeEnum::Enum dataFileType); private: CaretDataFileHelper(); virtual ~CaretDataFileHelper(); CaretDataFileHelper(const CaretDataFileHelper&); CaretDataFileHelper& operator=(const CaretDataFileHelper&); static void readCaretDataFile(CaretDataFile* caretDataFile, const AString& filename, const bool& preferOnDisk); public: // ADD_NEW_METHODS_HERE private: // ADD_NEW_MEMBERS_HERE }; #ifdef __CARET_DATA_FILE_HELPER_DECLARE__ // #endif // __CARET_DATA_FILE_HELPER_DECLARE__ } // namespace #endif //__CARET_DATA_FILE_HELPER_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Files/CaretMappableDataFile.cxx000066400000000000000000001066021300200146000264310ustar00rootroot00000000000000 /*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 __CARET_MAPPABLE_DATA_FILE_DECLARE__ #include "CaretMappableDataFile.h" #undef __CARET_MAPPABLE_DATA_FILE_DECLARE__ #include #include "CaretLogger.h" #include "ChartDataCartesian.h" #include "CiftiMappableConnectivityMatrixDataFile.h" #include "DataFileContentInformation.h" #include "FastStatistics.h" #include "FileInformation.h" #include "GiftiLabelTable.h" #include "Histogram.h" #include "LabelDrawingProperties.h" #include "PaletteColorMapping.h" #include "SceneClass.h" #include "SceneClassArray.h" #include "SceneAttributes.h" #include "StringTableModel.h" #include "VolumeFile.h" using namespace caret; /** * Constructor. */ CaretMappableDataFile::CaretMappableDataFile(const DataFileTypeEnum::Enum dataFileType) : CaretDataFile(dataFileType) { m_labelDrawingProperties.grabNew(new LabelDrawingProperties()); m_paletteNormalizationMode = PaletteNormalizationModeEnum::NORMALIZATION_SELECTED_MAP_DATA; } /** * Destructor. */ CaretMappableDataFile::~CaretMappableDataFile() { } /** * Constructor. * @param cmdf * Instance that is copied. */ CaretMappableDataFile::CaretMappableDataFile(const CaretMappableDataFile& cmdf) : CaretDataFile(cmdf) { this->copyCaretMappableDataFile(cmdf); } /** * Constructor. * @param cmdf * Instance that is assigned to this. * @return * Reference to this instance. */ CaretMappableDataFile& CaretMappableDataFile::operator=(const CaretMappableDataFile& cmdf) { if (this != &cmdf) { CaretDataFile::operator=(cmdf); this->copyCaretMappableDataFile(cmdf); } return *this; } /** * Assists with copying instances of this class. */ void CaretMappableDataFile::copyCaretMappableDataFile(const CaretMappableDataFile& cmdf) { m_paletteNormalizationMode = cmdf.m_paletteNormalizationMode; } // note: method is documented in header file bool CaretMappableDataFile::hasMapAttributes() const { return true; } /** * Is this file able to map to the given structure? Some data files, such * as CIFTI files, are able to map to multiple surface structure. The default * implementation of this method simply compares the given structure to * getStructure() and returns true if they are the same value, else false. * * @param structure * Structure for testing mappability status. * @return True if this file is able to map to the given structure, else false. */ bool CaretMappableDataFile::isMappableToSurfaceStructure(const StructureEnum::Enum structure) const { if (getStructure() == StructureEnum::ALL) { return true; } if (structure == getStructure()) { return true; } return false; } // note: method is documented in header file int32_t CaretMappableDataFile::getMapIndexFromNameOrNumber(const AString& mapName) const { bool ok = false; int32_t ret = mapName.toInt(&ok) - 1;//compensate for 1-indexing that command line parsing uses if (ok) { if (ret < 0 || ret >= getNumberOfMaps()) { ret = -1; } } else {//DO NOT search by name if the string was parsed as an integer correctly, or some idiot who names their maps as integers will get confused //when getting map "12" out of a file after the file expands to more than 12 elements suddenly does something different ret = getMapIndexFromName(mapName); } return ret; } // note: method is documented in header file int32_t CaretMappableDataFile::getMapIndexFromName(const AString& mapName) const { int32_t numMaps = getNumberOfMaps(); for (int32_t i = 0; i < numMaps; ++i) { if (mapName == getMapName(i)) { return i; } } return -1; } // note: method is documented in header file int32_t CaretMappableDataFile::getMapIndexFromUniqueID(const AString& uniqueID) const { int32_t numMaps = getNumberOfMaps(); for (int32_t i = 0; i < numMaps; ++i) { if (uniqueID == getMapUniqueID(i)) { return i; } } return -1; } // note: method is documented in header file void CaretMappableDataFile::updateScalarColoringForAllMaps(const PaletteFile* paletteFile) { const int32_t numMaps = getNumberOfMaps(); for (int32_t iMap = 0; iMap < numMaps; iMap++) { updateScalarColoringForMap(iMap, paletteFile); } } /** * @return True if this file is mapped using a palette and all palettes * are equal, otherwise false. */ bool CaretMappableDataFile::isPaletteColorMappingEqualForAllMaps() const { if ( ! isMappedWithPalette()) { return false; } const int32_t numMaps = getNumberOfMaps(); if (numMaps <= 0) { return false; } /* * Some files use one palette color mapping for all maps * and this can be detected if the pointer to palette * color mapping is the same for all maps. */ bool pointerTheSameFlag = true; const PaletteColorMapping* firstPCM = getMapPaletteColorMapping(0); for (int32_t iMap = 1; iMap < numMaps; iMap++) { if (firstPCM != getMapPaletteColorMapping(iMap)) { pointerTheSameFlag = false; break; } } if (pointerTheSameFlag) { return true; } /* * Compare each palette color mapping to the first palette color mapping */ for (int32_t iMap = 1; iMap < numMaps; iMap++) { if (*firstPCM != *getMapPaletteColorMapping(iMap)) { return false; } } return true; } // note: method is documented in header file NiftiTimeUnitsEnum::Enum CaretMappableDataFile::getMapIntervalUnits() const { return NiftiTimeUnitsEnum::NIFTI_UNITS_UNKNOWN; } // note: method is documented in header file void CaretMappableDataFile::getMapIntervalStartAndStep(float& firstMapUnitsValueOut, float& mapIntervalStepValueOut) const { firstMapUnitsValueOut = 1.0; mapIntervalStepValueOut = 1.0; } /** * Get the minimum and maximum values from ALL maps in this file. * Note that not all files (due to size of file) are able to provide * the minimum and maximum values from the file. The return value * indicates success/failure. If the failure (false) is returned * the returned values are likely +/- the maximum float values. * * @param dataRangeMinimumOut * Minimum data value found. * @param dataRangeMaximumOut * Maximum data value found. * @return * True if the values are valid, else false. */ bool CaretMappableDataFile::getDataRangeFromAllMaps(float& dataRangeMinimumOut, float& dataRangeMaximumOut) const { dataRangeMaximumOut = std::numeric_limits::max(); dataRangeMinimumOut = -dataRangeMaximumOut; return false; } /** * Save file data from the scene. For subclasses that need to * save to a scene, this method should be overriden. sceneClass * will be valid and any scene data should be added to it. * * @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. */ void CaretMappableDataFile::saveFileDataToScene(const SceneAttributes* sceneAttributes, SceneClass* sceneClass) { CaretDataFile::saveFileDataToScene(sceneAttributes, sceneClass); sceneClass->addClass(m_labelDrawingProperties->saveToScene(sceneAttributes, "m_labelDrawingProperties")); if (isMappedWithPalette()) { sceneClass->addEnumeratedType("m_paletteNormalizationMode", m_paletteNormalizationMode); if (sceneAttributes->isModifiedPaletteSettingsSavedToScene()) { std::vector pcmClassVector; const int32_t numMaps = getNumberOfMaps(); for (int32_t i = 0; i < numMaps; i++) { const PaletteColorMapping* pcmConst = getMapPaletteColorMapping(i); if (pcmConst->isModified()) { PaletteColorMapping* pcm = const_cast(pcmConst); try { const AString xml = pcm->encodeInXML(); SceneClass* pcmClass = new SceneClass("savedPaletteColorMapping", "SavedPaletteColorMapping", 1); pcmClass->addString("mapName", getMapName(i)); pcmClass->addInteger("mapIndex", i); pcmClass->addInteger("mapCount", numMaps); pcmClass->addString("mapColorMapping", xml); pcmClassVector.push_back(pcmClass); } catch (const XmlException& e) { sceneAttributes->addToErrorMessage("Failed to encode palette color mapping for file: " + getFileNameNoPath() + " Map Name: " + getMapName(i) + " Map Index: " + AString::number(i) + ". " + e.whatString()); } } } if ( ! pcmClassVector.empty()) { SceneClassArray* pcmArray = new SceneClassArray("savedPaletteColorMappingArray", pcmClassVector); sceneClass->addChild(pcmArray); } } } } /** * Restore file data from the scene. For subclasses that need to * restore from a scene, this method should be overridden. The scene class * will be valid and any scene data may be obtained from it. * * @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 CaretMappableDataFile::restoreFileDataFromScene(const SceneAttributes* sceneAttributes, const SceneClass* sceneClass) { CaretDataFile::restoreFileDataFromScene(sceneAttributes, sceneClass); m_labelDrawingProperties->restoreFromScene(sceneAttributes, sceneClass->getClass("m_labelDrawingProperties")); if (isMappedWithPalette()) { std::vector paletteNormalizationModes; getPaletteNormalizationModesSupported(paletteNormalizationModes); if ( ! paletteNormalizationModes.empty()) { const PaletteNormalizationModeEnum::Enum defValue = paletteNormalizationModes[0]; m_paletteNormalizationMode = sceneClass->getEnumeratedTypeValue("m_paletteNormalizationMode", defValue); } const int32_t numMaps = getNumberOfMaps(); const SceneClassArray* pcmArray = sceneClass->getClassArray("savedPaletteColorMappingArray"); if (pcmArray != NULL) { const int32_t numElements = pcmArray->getNumberOfArrayElements(); for (int32_t i = 0; i < numElements; i++) { const SceneClass* pcmClass = pcmArray->getClassAtIndex(i); const AString mapName = pcmClass->getStringValue("mapName"); const int32_t mapIndex = pcmClass->getIntegerValue("mapIndex", -1); const int32_t mapCount = pcmClass->getIntegerValue("mapCount", -1); const AString pcmString = pcmClass->getStringValue("mapColorMapping"); int32_t restoreMapIndex = -1; /* * Try to find map that has the saved name AND index. */ if (restoreMapIndex < 0) { if ((mapIndex >= 0) && (mapIndex < numMaps)) { if (getMapName(mapIndex) == mapName) { restoreMapIndex = mapIndex; } } } /* * If map count has not changed, give preference to * map index over map name */ if (mapCount == numMaps) { /* * Try to find map that has the saved map index. */ if (restoreMapIndex < 0) { if ((mapIndex >= 0) && (mapIndex < numMaps)) { restoreMapIndex = mapIndex; } } /* * Try to find map that has the saved map name. */ if (restoreMapIndex < 0) { if ( ! mapName.isEmpty()) { restoreMapIndex = getMapIndexFromName(mapName); } } } else { /* * Try to find map that has the saved map name. */ if (restoreMapIndex < 0) { if ( ! mapName.isEmpty()) { restoreMapIndex = getMapIndexFromName(mapName); } } /* * Try to find map that has the saved map index. */ if (restoreMapIndex < 0) { if ((mapIndex >= 0) && (mapIndex < numMaps)) { restoreMapIndex = mapIndex; } } } if (restoreMapIndex >= 0) { try { PaletteColorMapping pcm; pcm.decodeFromStringXML(pcmString); PaletteColorMapping* pcmMap = getMapPaletteColorMapping(restoreMapIndex); pcmMap->copy(pcm); pcmMap->clearModified(); /* * WB-522 When palette loaded from scene, * mark it as modified. */ pcmMap->setModified(); /* * Volume file needs it's map coloring updated since * palette has changed. */ VolumeFile* volumeFile = dynamic_cast(this); if (volumeFile != NULL) { volumeFile->updateScalarColoringForMap(restoreMapIndex, NULL); } } catch (const XmlException& e) { sceneAttributes->addToErrorMessage("Failed to decode palette color mapping for file: " + getFileNameNoPath() + " Map Name: " + getMapName(i) + " Map Index: " + AString::number(i) + ". " + e.whatString()); } } else { const AString msg = ("Unable to find map for restoring palette settings for file: " + getFileNameNoPath() + " Map Name: " + mapName + " Map Index: " + AString::number(mapIndex)); sceneAttributes->addToErrorMessage(msg); } } } } } /** * Add information about the file to the data file information. * * @param dataFileInformation * Consolidates information about a data file. */ void CaretMappableDataFile::addToDataFileContentInformation(DataFileContentInformation& dataFileInformation) { CaretDataFile::addToDataFileContentInformation(dataFileInformation); const int64_t dataSizeInBytes = getDataSizeUncompressedInBytes(); if (dataSizeInBytes >= 0) { dataFileInformation.addNameAndValue("Data Size", FileInformation::fileSizeToStandardUnits(dataSizeInBytes)); } dataFileInformation.addNameAndValue("Maps to Surface", isSurfaceMappable()); dataFileInformation.addNameAndValue("Maps to Volume", isVolumeMappable()); dataFileInformation.addNameAndValue("Maps with LabelTable", isMappedWithLabelTable()); dataFileInformation.addNameAndValue("Maps with Palette", isMappedWithPalette()); if (isMappedWithPalette()) { dataFileInformation.addNameAndValue("All Map Palettes Equal", isPaletteColorMappingEqualForAllMaps()); NiftiTimeUnitsEnum::Enum timeUnits = getMapIntervalUnits(); switch (timeUnits) { case NiftiTimeUnitsEnum::NIFTI_UNITS_HZ: break; case NiftiTimeUnitsEnum::NIFTI_UNITS_MSEC: break; case NiftiTimeUnitsEnum::NIFTI_UNITS_PPM: break; case NiftiTimeUnitsEnum::NIFTI_UNITS_SEC: break; case NiftiTimeUnitsEnum::NIFTI_UNITS_UNKNOWN: break; case NiftiTimeUnitsEnum::NIFTI_UNITS_USEC: break; } dataFileInformation.addNameAndValue("Map Interval Units", NiftiTimeUnitsEnum::toName(timeUnits)); if (timeUnits != NiftiTimeUnitsEnum::NIFTI_UNITS_UNKNOWN) { float mapIntervalStart, mapIntervalStep; getMapIntervalStartAndStep(mapIntervalStart, mapIntervalStep); dataFileInformation.addNameAndValue("Map Interval Start", mapIntervalStart); dataFileInformation.addNameAndValue("Map Interval Step", mapIntervalStep); } } bool showMapFlag = (isMappedWithLabelTable() || isMappedWithPalette()); /* * Do not show maps on CIFTI connectivity matrix data because * they do not have maps, they have a matrix. */ const bool ciftiMatrixFlag = (dynamic_cast(this) != NULL); if (ciftiMatrixFlag) { showMapFlag = false; } /* * Did user override display of map information? */ if ( ! dataFileInformation.isOptionFlag(DataFileContentInformation::OPTION_SHOW_MAP_INFORMATION)) { showMapFlag = false; } if (showMapFlag) { const int32_t numMaps = getNumberOfMaps(); dataFileInformation.addNameAndValue("Number of Maps", numMaps); if (numMaps > 0) { int columnCount = 0; const int COL_INDEX = columnCount++; int32_t COL_MIN = -1; int32_t COL_MAX = -1; int32_t COL_MEAN = -1; int32_t COL_DEV = -1; int32_t COL_PCT_POS = -1; int32_t COL_PCT_NEG = -1; int32_t COL_INF_NAN = -1; if (isMappedWithPalette()) { COL_MIN = columnCount++; COL_MAX = columnCount++; COL_MEAN = columnCount++; COL_DEV = columnCount++; COL_PCT_POS = columnCount++; COL_PCT_NEG = columnCount++; COL_INF_NAN = columnCount++; } const int COL_NAME = columnCount++; /* * Include a row for the column titles */ const int32_t tableRowCount = numMaps + 1; StringTableModel stringTable(tableRowCount, columnCount); stringTable.setElement(0, COL_INDEX, "Map"); if (COL_MIN >= 0) { stringTable.setElement(0, COL_MIN, "Minimum"); } if (COL_MAX >= 0) { stringTable.setElement(0, COL_MAX, "Maximum"); } if (COL_MEAN >= 0) { stringTable.setElement(0, COL_MEAN, "Mean"); } if (COL_DEV >= 0) { stringTable.setElement(0, COL_DEV, "Sample Dev"); } if (COL_PCT_POS >= 0) { stringTable.setElement(0, COL_PCT_POS, "% Positive"); } if (COL_PCT_NEG >= 0) { stringTable.setElement(0, COL_PCT_NEG, "% Negative"); } if (COL_INF_NAN >= 0) { stringTable.setElement(0, COL_INF_NAN, "Inf/NaN"); } stringTable.setElement(0, COL_NAME, "Map Name"); stringTable.setColumnAlignment(COL_NAME, StringTableModel::ALIGN_LEFT); for (int32_t mapIndex = 0; mapIndex < numMaps; mapIndex++) { const int32_t tableRow = mapIndex + 1; CaretAssert(COL_INDEX >= 0); CaretAssert(COL_NAME >= 0); stringTable.setElement(tableRow, COL_INDEX, (mapIndex + 1)); stringTable.setElement(tableRow, COL_NAME, getMapName(mapIndex)); const FastStatistics* stats = const_cast(this)->getMapFastStatistics(mapIndex); if (isMappedWithPalette() && (stats != NULL)) { const Histogram* histogram = getMapHistogram(mapIndex); int64_t posCount = 0; int64_t zeroCount = 0; int64_t negCount = 0; int64_t infCount = 0; int64_t negInfCount = 0; int64_t nanCount = 0; histogram->getCounts(posCount, zeroCount, negCount, infCount, negInfCount, nanCount); const int64_t numInfinityAndNotANumber = (infCount + negInfCount + nanCount); const double totalCount = (posCount + zeroCount + negCount + numInfinityAndNotANumber); const double pctPositive = (posCount / totalCount) * 100.0; const double pctNegative = (negCount / totalCount) * 100.0; if (COL_MIN >= 0) { stringTable.setElement(tableRow, COL_MIN, stats->getMin()); } if (COL_MAX >= 0) { stringTable.setElement(tableRow, COL_MAX, stats->getMax()); } if (COL_MEAN >= 0) { stringTable.setElement(tableRow, COL_MEAN, stats->getMean()); } if (COL_DEV >= 0) { stringTable.setElement(tableRow, COL_DEV, stats->getSampleStdDev()); } if (COL_PCT_POS >= 0) { stringTable.setElement(tableRow, COL_PCT_POS, pctPositive); } if (COL_PCT_NEG >= 0) { stringTable.setElement(tableRow, COL_PCT_NEG, pctNegative); } if (COL_INF_NAN >= 0) { stringTable.setElement(tableRow, COL_INF_NAN, numInfinityAndNotANumber); } } } dataFileInformation.addText("\n" + stringTable.getInString() + "\n"); } } if (showMapFlag) { if (isMappedWithLabelTable()) { /* * Show label table for each map. * However, some files contain only a single label table used * for all maps and this condition is detected if the first * two label tables use the same pointer. */ const int32_t numMaps = getNumberOfMaps(); bool haveLabelTableForEachMap = false; if (numMaps > 1) { if (getMapLabelTable(0) != getMapLabelTable(1)) { haveLabelTableForEachMap = true; } } for (int32_t mapIndex = 0; mapIndex < numMaps; mapIndex++) { const AString labelTableName = ("Label table for " + (haveLabelTableForEachMap ? ("map " + AString::number(mapIndex + 1) + ": " + getMapName(mapIndex)) : ("ALL maps")) + "\n"); dataFileInformation.addText(labelTableName + getMapLabelTable(mapIndex)->toFormattedString(" ") + "\n"); if ( ! haveLabelTableForEachMap) { break; } } } } } /** * @return True if any of the maps in this file contain a * color mapping that possesses a modified status. */ bool CaretMappableDataFile::isModifiedPaletteColorMapping() const { if (isMappedWithPalette()) { const int32_t numMaps = getNumberOfMaps(); for (int32_t i = 0; i < numMaps; i++) { if (getMapPaletteColorMapping(i)->isModified()) { return true; } } } return false; } /** * @return True if the file is modified in any way EXCEPT for * the palette color mapping. Also see isModified(). */ bool CaretMappableDataFile::isModifiedExcludingPaletteColorMapping() const { if (CaretDataFile::isModified()) { return true; } return false; } /** * @return True if the file is modified in any way including * the palette color mapping. * * NOTE: While this method overrides that in the super class, * it is NOT virtual here. Thus subclasses cannot override * this method and instead, subclasses should overrride * isModifiedExcludingPaletteColorMapping(). */ bool CaretMappableDataFile::isModified() const { if (isModifiedExcludingPaletteColorMapping()) { return true; } if (isModifiedPaletteColorMapping()) { return true; } return false; } /** * Create cartesian chart data from the given data. * * @param * Data for the Y-axis. * @return * Pointer to the ChartDataCartesian instance. */ ChartDataCartesian* CaretMappableDataFile::helpCreateCartesianChartData(const std::vector& data) { const int64_t numData = static_cast(data.size()); /* * Some files may have time data but initially assume data-series */ bool timeSeriesFlag = 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: break; case NiftiTimeUnitsEnum::NIFTI_UNITS_USEC: convertTimeToSeconds = 1000000.0; timeSeriesFlag = true; break; } ChartDataCartesian* chartData = NULL; if (timeSeriesFlag) { chartData = new ChartDataCartesian(ChartDataTypeEnum::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, ChartAxisUnitsEnum::CHART_AXIS_UNITS_NONE, 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) { /* * X-Value is "time" */ xValue = timeStart + (i * timeStep); } else { /* * X-Value is the map index and map indices start at one */ xValue = i + 1; } chartData->addPoint(xValue, data[i]); } } return chartData; } /** * Helper for getting chart data types supported by files that create * charts from brainordinates (multi-map files). * The chart data types are a function of the map interval units. * * @param chartDataTypesOut * Chart types supported by this file. */ void 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); break; case NiftiTimeUnitsEnum::NIFTI_UNITS_MSEC: chartDataTypesOut.push_back(ChartDataTypeEnum::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); break; case NiftiTimeUnitsEnum::NIFTI_UNITS_UNKNOWN: chartDataTypesOut.push_back(ChartDataTypeEnum::CHART_DATA_TYPE_LINE_DATA_SERIES); break; case NiftiTimeUnitsEnum::NIFTI_UNITS_USEC: chartDataTypesOut.push_back(ChartDataTypeEnum::CHART_DATA_TYPE_LINE_TIME_SERIES); break; } } /** * Is a medial wall label in the label table for the given map index? * * NOTE: This does not test to see if a data element in * the map is set to the key of the medial wall label. So, it is * possible that true is returned but no vertices or voxels are * assigned to the medial wall. * * @param mapIndex * Index of the map. * @return * True if map the map's label table contains a medial wall label, * else false. */ bool CaretMappableDataFile::isMedialWallLabelInMapLabelTable(const int32_t mapIndex) const { if (isMappedWithLabelTable()) { const GiftiLabelTable* labelTable = getMapLabelTable(mapIndex); return labelTable->hasMedialWallLabel(); } return false; } /** * @return The label drawing properties for this file. A valid pointer * will always be returned even if the file does not provide label data. */ LabelDrawingProperties* CaretMappableDataFile::getLabelDrawingProperties() { return m_labelDrawingProperties; } /** * @return The label drawing properties for this file. A valid pointer * will always be returned even if the file does not provide label data. */ const LabelDrawingProperties* CaretMappableDataFile::getLabelDrawingProperties() const { return m_labelDrawingProperties; } /** * @return The palette normalization mode for the file. * The default is NORMALIZATION_SELECTED_MAP_DATA. */ PaletteNormalizationModeEnum::Enum CaretMappableDataFile::getPaletteNormalizationMode() const { return m_paletteNormalizationMode; } /** * Set the palette normalization mode for the file. * * @param mode * New value for palette normalization mode. */ void CaretMappableDataFile::setPaletteNormalizationMode(const PaletteNormalizationModeEnum::Enum mode) { m_paletteNormalizationMode = mode; } /** * @return Is the data in the file mapped to colors using * Red, Green, Blue, Alpha values. */ bool CaretMappableDataFile::isMappedWithRGBA() const { return false; } connectome-workbench-1.2.3+git41-gc4c6c90/src/Files/CaretMappableDataFile.h000066400000000000000000000437161300200146000260640ustar00rootroot00000000000000#ifndef __CARET_MAPPABLE_DATA_FILE__H_ #define __CARET_MAPPABLE_DATA_FILE__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 "CaretDataFile.h" #include "ChartDataTypeEnum.h" #include "CaretPointer.h" #include "NiftiEnums.h" #include "PaletteNormalizationModeEnum.h" namespace caret { class ChartDataCartesian; class FastStatistics; class GiftiMetaData; class GiftiLabelTable; class Histogram; class LabelDrawingProperties; class PaletteColorMapping; class PaletteFile; /** * \class caret::CaretMappableDataFile * \brief A Caret data file that is mappable to surfaces and/or volumes. * * This class is essentially an interface that defines methods for * files that are 'mappable', as an overlay, to surfaces and/or volumes. * Use of a common interface simplifies selection and application * of these data files. * * For a GIFTI File, the number of maps is the number of data arrays * in the GIFTI file. For a volume, it may be the number of time points. * * Note that Caret5 used the term 'column'. */ class CaretMappableDataFile : public CaretDataFile { public: CaretMappableDataFile(const DataFileTypeEnum::Enum dataFileType); virtual ~CaretMappableDataFile(); virtual void addToDataFileContentInformation(DataFileContentInformation& dataFileInformation); /** * @return Is the data mappable to a surface? */ virtual bool isSurfaceMappable() const = 0; /** * @return Is the data mappable to a volume? */ virtual bool isVolumeMappable() const = 0; /** * @return The number of maps in the file. * Note: Caret5 used the term 'columns'. */ virtual int32_t getNumberOfMaps() const = 0; virtual bool isMappableToSurfaceStructure(const StructureEnum::Enum structure) const; /** * @return True if the file has map attributes (name and metadata). * For files that do not have map attributes, they should override * this method and return false. If not overriden, this method * returns true. * * Some files (such as CIFTI Connectivity Matrix Files and CIFTI * Data-Series Files) do not have Map Attributes and thus there * is no map name nor map metadata and options to edit these * attributes should not be presented to the user. * * These CIFTI files do contain palette color mapping but it is * associated with the file. To simplify palette color mapping editing * these file will return the file's palette color mapping for any * calls to getMapPaletteColorMapping(). */ virtual bool hasMapAttributes() const; /** * Get the name of the map at the given index. * * @param mapIndex * Index of the map. * @return * Name of the map. */ virtual AString getMapName(const int32_t mapIndex) const = 0; /** * Find the index of the map that uses the given name. * * @param mapName * Name of the desired map. * @return * Index of the map using the given name. If there is more * than one map with the given name, this method is likely * to return the index of the first map with the name. */ virtual int32_t getMapIndexFromName(const AString& mapName) const; /** * Find the index of the map that uses the given name. * * @param mapName * Name of the desired map. * @return * Index of the map using the given name. If there is more * than one map with the given name, this method is likely * to return the index of the first map with the name. */ virtual int32_t getMapIndexFromNameOrNumber(const AString& mapName) const; /** * Set the name of the map at the given index. * * If the file does not have map attributes (hasMapAttributes()) * calling this method will have not change the file. * * @param mapIndex * Index of the map. * @param mapName * New name for the map. */ virtual void setMapName(const int32_t mapIndex, const AString& mapName) = 0; /** * Get the metadata for the map at the given index * * If the file does not have map attributes (hasMapAttributes()) * a valid metadata object will be returned but changing its * content will have no effect on the file. * * @param mapIndex * Index of the map. * @return * Metadata for the map (const value). */ virtual const GiftiMetaData* getMapMetaData(const int32_t mapIndex) const = 0; /** * Get the metadata for the map at the given index * * If the file does not have map attributes (hasMapAttributes()) * a valid metadata object will be returned but changing its * content will have no effect on the file. * * @param mapIndex * Index of the map. * @return * Metadata for the map. */ virtual GiftiMetaData* getMapMetaData(const int32_t mapIndex) = 0; /** * Get the unique ID (UUID) for the map at the given index. * * @param mapIndex * Index of the map. * @return * String containing UUID for the map. */ virtual AString getMapUniqueID(const int32_t mapIndex) const = 0; /** * Find the index of the map that uses the given unique ID (UUID). * * @param uniqueID * Unique ID (UUID) of the desired map. * @return * Index of the map using the given UUID. */ virtual int32_t getMapIndexFromUniqueID(const AString& uniqueID) const; /** * @return Is the data in the file mapped to colors using * a palette. */ virtual bool isMappedWithPalette() const = 0; /** * @return The estimated size of data after it is uncompressed * and loaded into RAM. A negative value indicates that the * file size cannot be computed. */ virtual int64_t getDataSizeUncompressedInBytes() const = 0; /** * Get statistics describing the distribution of data * mapped with a color palette at the given index. * * @param mapIndex * Index of the map. * @return * Fast statistics for data (will be NULL for data * not mapped using a palette). */ virtual const FastStatistics* getMapFastStatistics(const int32_t mapIndex) = 0; /** * Get a histogram for the map at the given index. * * @param mapIndex * Index of the map. * @return * Histogram for data (will be NULL for data * not mapped using a palette). */ virtual const Histogram* getMapHistogram(const int32_t mapIndex) = 0; /** * Get a histogram for the map at the given index of data * mapped with a color palette at the given index for * data within the given ranges. * * @param mapIndex * Index of the map. * @param mostPositiveValueInclusive * Values more positive than this value are excluded. * @param leastPositiveValueInclusive * Values less positive than this value are excluded. * @param leastNegativeValueInclusive * Values less negative than this value are excluded. * @param mostNegativeValueInclusive * Values more negative than this value are excluded. * @param includeZeroValues * If true zero values (very near zero) are included. * @return * Histogram for data (will be NULL for data * not mapped using a palette). */ virtual const Histogram* getMapHistogram(const int32_t mapIndex, const float mostPositiveValueInclusive, const float leastPositiveValueInclusive, const float leastNegativeValueInclusive, const float mostNegativeValueInclusive, const bool includeZeroValues) = 0; /** * Get statistics describing the distribution of data * mapped with a color palette for all data within the file. * * @return * Fast statistics for data (will be NULL for data * 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 * the file. * * @return * Histogram for data (will be NULL for data * not mapped using a palette). */ virtual const Histogram* getFileHistogram() = 0; /** * Get histogram describing the distribution of data * mapped with a color palette for all data in the file * within the given range of values. * * @param mostPositiveValueInclusive * Values more positive than this value are excluded. * @param leastPositiveValueInclusive * Values less positive than this value are excluded. * @param leastNegativeValueInclusive * Values less negative than this value are excluded. * @param mostNegativeValueInclusive * Values more negative than this value are excluded. * @param includeZeroValues * If true zero values (very near zero) are included. * @return * Descriptive statistics for data (will be NULL for data * not mapped using a palette). */ virtual const Histogram* getFileHistogram(const float mostPositiveValueInclusive, const float leastPositiveValueInclusive, const float leastNegativeValueInclusive, const float mostNegativeValueInclusive, const bool includeZeroValues) = 0; /** * Get the palette color mapping for the map at the given index. * * @param mapIndex * Index of the map. * @return * Palette color mapping for the map (will be NULL for data * not mapped using a palette). */ virtual PaletteColorMapping* getMapPaletteColorMapping(const int32_t mapIndex) = 0; /** * Get the palette color mapping for the map at the given index. * * @param mapIndex * Index of the map. * @return * Palette color mapping for the map (constant) (will be NULL for data * not mapped using a palette). */ virtual const PaletteColorMapping* getMapPaletteColorMapping(const int32_t mapIndex) const = 0; /** * @return Is the data in the file mapped to colors using * a label table. */ virtual bool isMappedWithLabelTable() const = 0; /** * @return Is the data in the file mapped to colors using * Red, Green, Blue, Alpha values. */ virtual bool isMappedWithRGBA() const; /** * Get the label table for the map at the given index. * * @param mapIndex * Index of the map. * @return * Label table for the map (will be NULL for data * not mapped using a label table). */ virtual GiftiLabelTable* getMapLabelTable(const int32_t mapIndex) = 0; /** * Get the label table for the map at the given index. * * @param mapIndex * Index of the map. * @return * Label table for the map (constant) (will be NULL for data * not mapped using a label table). */ virtual const GiftiLabelTable* getMapLabelTable(const int32_t mapIndex) const = 0; bool isMedialWallLabelInMapLabelTable(const int32_t mapIndex) const; /** * Get the palette normalization modes that are supported by the file. * * @param modesSupportedOut * Palette normalization modes supported by a file. Will be * empty for files that are not mapped with a palette. If there * 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; /** * @return The palette normalization mode for the file. */ virtual PaletteNormalizationModeEnum::Enum getPaletteNormalizationMode() const; /** * Set the palette normalization mode for the file. * * @param mode * New value for palette normalization mode. */ virtual void setPaletteNormalizationMode(const PaletteNormalizationModeEnum::Enum mode); /** * Update coloring for all maps. * * @param paletteFile * Palette file containing palettes. */ virtual void updateScalarColoringForAllMaps(const PaletteFile* paletteFile); /** * Update coloring for a map. * * @param mapIndex * Index of map. * @param paletteFile * Palette file containing palettes. */ virtual void updateScalarColoringForMap(const int32_t mapIndex, const PaletteFile* paletteFile) = 0; virtual bool isPaletteColorMappingEqualForAllMaps() const; /** * @return The units for the 'interval' between two consecutive maps. */ virtual NiftiTimeUnitsEnum::Enum getMapIntervalUnits() const; /** * Get the units value for the first map and the * quantity of units between consecutive maps. If the * units for the maps is unknown, value of one (1) are * returned for both output values. * * @param firstMapUnitsValueOut * Output containing units value for first map. * @param mapIntervalStepValueOut * Output containing number of units between consecutive maps. */ virtual void getMapIntervalStartAndStep(float& firstMapUnitsValueOut, float& mapIntervalStepValueOut) const; /* documented in cxx file */ virtual bool getDataRangeFromAllMaps(float& dataRangeMinimumOut, float& dataRangeMaximumOut) const; /* documented in cxx file */ virtual bool isModifiedExcludingPaletteColorMapping() const; /* documented in cxx file. */ virtual bool isModifiedPaletteColorMapping() const; /* documented in cxx file. */ bool isModified() const; LabelDrawingProperties* getLabelDrawingProperties(); const LabelDrawingProperties* getLabelDrawingProperties() const; protected: CaretMappableDataFile(const CaretMappableDataFile&); CaretMappableDataFile& operator=(const CaretMappableDataFile&); ChartDataCartesian* helpCreateCartesianChartData(const std::vector& data); void helpGetSupportedLineSeriesChartDataTypes(std::vector& chartDataTypesOut) const; virtual void saveFileDataToScene(const SceneAttributes* sceneAttributes, SceneClass* sceneClass); virtual void restoreFileDataFromScene(const SceneAttributes* sceneAttributes, const SceneClass* sceneClass); private: void copyCaretMappableDataFile(const CaretMappableDataFile&); CaretPointer m_labelDrawingProperties; PaletteNormalizationModeEnum::Enum m_paletteNormalizationMode; }; #ifdef __CARET_MAPPABLE_DATA_FILE_DECLARE__ // #endif // __CARET_MAPPABLE_DATA_FILE_DECLARE__ } // namespace #endif //__CARET_MAPPABLE_DATA_FILE__H_ connectome-workbench-1.2.3+git41-gc4c6c90/src/Files/CaretSparseFile.cxx000066400000000000000000000313701300200146000253520ustar00rootroot00000000000000/*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 "CaretSparseFile.h" #include "ByteOrderEnum.h" #include "ByteSwapping.h" #include "CaretAssert.h" #include "CaretLogger.h" #include "FileInformation.h" #include using namespace caret; using namespace std; const char magic[] = "\0\0\0\0cst\0"; CaretSparseFile::CaretSparseFile(const AString& fileName) { readFile(fileName); } void CaretSparseFile::readFile(const AString& filename) { m_file.close(); if (filename.endsWith(".gz")) { throw DataFileException("wbsparse files cannot be read while compressed"); } m_file.open(filename); FileInformation fileInfo(filename);//useful later for file size, but create it now to reduce the amount of time between file open and size check char buf[8]; m_file.read(buf, 8); for (int i = 0; i < 8; ++i) { if (buf[i] != magic[i]) throw DataFileException("file has the wrong magic string"); } m_file.read(m_dims, 2 * sizeof(int64_t)); if (ByteOrderEnum::isSystemBigEndian()) { ByteSwapping::swapBytes(m_dims, 2); } if (m_dims[0] < 1 || m_dims[1] < 1) throw DataFileException("both dimensions must be positive"); m_indexArray.resize(m_dims[1] + 1); vector lengthArray(m_dims[1]); m_file.read(lengthArray.data(), m_dims[1] * sizeof(int64_t)); if (ByteOrderEnum::isSystemBigEndian()) { ByteSwapping::swapBytes(lengthArray.data(), m_dims[1]); } m_indexArray[0] = 0; for (int64_t i = 0; i < m_dims[1]; ++i) { if (lengthArray[i] > m_dims[0] || lengthArray[i] < 0) throw DataFileException("impossible value found in length array"); m_indexArray[i + 1] = m_indexArray[i] + lengthArray[i]; } m_valuesOffset = 8 + 2 * sizeof(int64_t) + m_dims[1] * sizeof(int64_t); int64_t xml_offset = m_valuesOffset + m_indexArray[m_dims[1]] * 2 * sizeof(int64_t); if (xml_offset >= fileInfo.size()) throw DataFileException("file is truncated"); int64_t xml_length = fileInfo.size() - xml_offset; if (xml_length < 1) throw DataFileException("file is truncated"); m_file.seek(xml_offset); const int64_t seekResult = m_file.pos(); if (seekResult != xml_offset) { const AString msg = ("Tried to seek to " + AString::number(xml_offset) + " but got an offset of " + AString::number(seekResult)); throw DataFileException(msg); } QByteArray myXMLBytes(xml_length, '\0'); m_file.read(myXMLBytes.data(), xml_length); m_xml.readXML(myXMLBytes); if (m_xml.getDimensionLength(CiftiXML::ALONG_ROW) != m_dims[0] || m_xml.getDimensionLength(CiftiXML::ALONG_COLUMN) != m_dims[1]) { throw DataFileException("cifti XML doesn't match dimensions of sparse file"); } } CaretSparseFile::~CaretSparseFile() { } void CaretSparseFile::getRow(const int64_t& index, int64_t* rowOut) { CaretAssert(index >= 0 && index < m_dims[1]); int64_t start = m_indexArray[index], end = m_indexArray[index + 1]; int64_t numToRead = (end - start) * 2; m_scratchArray.resize(numToRead); m_file.seek(m_valuesOffset + start * sizeof(int64_t) * 2); m_file.read(m_scratchArray.data(), numToRead * sizeof(int64_t)); if (ByteOrderEnum::isSystemBigEndian()) { ByteSwapping::swapBytes(m_scratchArray.data(), numToRead); } int64_t curIndex = 0; for (int64_t i = 0; i < numToRead; i += 2) { int64_t index = m_scratchArray[i]; if (index < curIndex || index >= m_dims[0]) throw DataFileException("impossible index value found in file"); while (curIndex < index) { rowOut[curIndex] = 0; ++curIndex; } ++curIndex; rowOut[index] = m_scratchArray[i + 1]; } while (curIndex < m_dims[0]) { rowOut[curIndex] = 0; ++curIndex; } } void CaretSparseFile::getRowSparse(const int64_t& index, vector& indicesOut, vector& valuesOut) { CaretAssert(index >= 0 && index < m_dims[1]); int64_t start = m_indexArray[index], end = m_indexArray[index + 1]; int64_t numToRead = (end - start) * 2, numNonzero = end - start; m_scratchArray.resize(numToRead); m_file.seek(m_valuesOffset + start * sizeof(int64_t) * 2); m_file.read(m_scratchArray.data(), numToRead * sizeof(int64_t)); if (ByteOrderEnum::isSystemBigEndian()) { ByteSwapping::swapBytes(m_scratchArray.data(), numToRead); } indicesOut.resize(numNonzero); valuesOut.resize(numNonzero); int64_t lastIndex = -1; for (int64_t i = 0; i < numNonzero; ++i) { indicesOut[i] = m_scratchArray[i * 2]; valuesOut[i] = m_scratchArray[i * 2 + 1]; if (indicesOut[i] <= lastIndex || indicesOut[i] >= m_dims[0]) throw DataFileException("impossible index value found in file"); lastIndex = indicesOut[i]; } } void CaretSparseFile::getFibersRow(const int64_t& index, FiberFractions* rowOut) { if (m_scratchRow.size() != (size_t)m_dims[0]) m_scratchRow.resize(m_dims[0]); getRow(index, (int64_t*)m_scratchRow.data()); for (int64_t i = 0; i < m_dims[0]; ++i) { if (m_scratchRow[i] == 0) { rowOut[i].zero(); } else { decodeFibers(m_scratchRow[i], rowOut[i]); } } } void CaretSparseFile::getFibersRowSparse(const int64_t& index, vector& indicesOut, vector& valuesOut) { getRowSparse(index, indicesOut, m_scratchSparseRow); size_t numNonzero = m_scratchSparseRow.size(); valuesOut.resize(numNonzero); for (size_t i = 0; i < numNonzero; ++i) { decodeFibers(((uint64_t*)m_scratchSparseRow.data())[i], valuesOut[i]); } } void CaretSparseFile::decodeFibers(const uint64_t& coded, FiberFractions& decoded) { decoded.fiberFractions.resize(3); decoded.totalCount = coded>>32; uint32_t temp = coded & ((1LL<<32) - 1); const static uint32_t MASK = ((1<<10) - 1); decoded.distance = (temp & MASK); decoded.fiberFractions[1] = ((temp>>10) & MASK) / 1000.0f; decoded.fiberFractions[0] = ((temp>>20) & MASK) / 1000.0f; decoded.fiberFractions[2] = 1.0f - decoded.fiberFractions[0] - decoded.fiberFractions[1]; if (decoded.fiberFractions[2] < -0.002f || (temp & (3<<30))) { throw DataFileException("error decoding value '" + AString::number(coded) + "' from workbench sparse trajectory file"); } if (decoded.fiberFractions[2] < 0.0f) decoded.fiberFractions[2] = 0.0f; } void FiberFractions::zero() { totalCount = 0; fiberFractions.clear(); distance = 0.0f; } CaretSparseFileWriter::CaretSparseFileWriter(const AString& fileName, const CiftiXML& xml) { if (!fileName.endsWith(".trajTEMP.wbsparse")) {//for now (and maybe forever), this format is single-purpose CaretLogWarning("sparse trajectory file '" + fileName + "' should be saved ending in .trajTEMP.wbsparse"); } m_finished = false; int64_t dimensions[2] = { xml.getDimensionLength(CiftiXML::ALONG_ROW), xml.getDimensionLength(CiftiXML::ALONG_COLUMN) }; if (dimensions[0] < 1 || dimensions[1] < 1) throw DataFileException("both dimensions must be positive"); m_xml = xml; m_dims[0] = dimensions[0];//CiftiXML doesn't support 3 dimensions yet, so we do this m_dims[1] = dimensions[1]; if (fileName.endsWith(".gz")) { throw DataFileException("wbsparse files cannot be written compressed"); }//because after we finish writing the data, we have to come back and write the lengths array m_file.open(fileName, CaretBinaryFile::WRITE_TRUNCATE); m_file.write(magic, 8); int64_t tempdims[2] = { m_dims[0], m_dims[1] }; if (ByteOrderEnum::isSystemBigEndian()) { ByteSwapping::swapBytes(tempdims, 2); } m_file.write(tempdims, 2 * sizeof(int64_t)); m_lengthArray.resize(m_dims[1], 0);//initialize the memory so that valgrind won't complain m_file.write(m_lengthArray.data(), m_dims[1] * sizeof(uint64_t));//write it to get the file to the correct length m_nextRowIndex = 0; m_valuesOffset = 8 + 2 * sizeof(int64_t) + m_dims[1] * sizeof(int64_t); } void CaretSparseFileWriter::writeRow(const int64_t& index, const int64_t* row) { CaretAssert(index < m_dims[1]); CaretAssert(index >= m_nextRowIndex); while (m_nextRowIndex < index) { m_lengthArray[m_nextRowIndex] = 0; ++m_nextRowIndex; } m_scratchArray.clear(); int64_t count = 0; for (int64_t i = 0; i < m_dims[0]; ++i) { if (row[i] != 0) { m_scratchArray.push_back(i); m_scratchArray.push_back(row[i]); ++count; } } m_lengthArray[index] = count; if (ByteOrderEnum::isSystemBigEndian()) { ByteSwapping::swapBytes(m_scratchArray.data(), m_scratchArray.size()); } m_file.write(m_scratchArray.data(), m_scratchArray.size() * sizeof(int64_t)); m_nextRowIndex = index + 1; if (m_nextRowIndex == m_dims[1]) finish(); } void CaretSparseFileWriter::writeRowSparse(const int64_t& index, const vector& indices, const vector& values) { CaretAssert(index < m_dims[1]); CaretAssert(index >= m_nextRowIndex); CaretAssert(indices.size() == values.size()); while (m_nextRowIndex < index) { m_lengthArray[m_nextRowIndex] = 0; ++m_nextRowIndex; } m_scratchArray.clear(); size_t numNonzero = indices.size();//assume no zeros m_lengthArray[index] = numNonzero; int64_t lastIndex = -1; for (size_t i = 0; i < numNonzero; ++i) { if (indices[i] <= lastIndex || indices[i] >= m_dims[0]) throw DataFileException("indices must be sorted when writing sparse rows"); lastIndex = indices[i]; m_scratchArray.push_back(indices[i]); m_scratchArray.push_back(values[i]); } if (ByteOrderEnum::isSystemBigEndian()) { ByteSwapping::swapBytes(m_scratchArray.data(), m_scratchArray.size()); } m_file.write(m_scratchArray.data(), m_scratchArray.size() * sizeof(int64_t)); m_nextRowIndex = index + 1; if (m_nextRowIndex == m_dims[1]) finish(); } void CaretSparseFileWriter::writeFibersRow(const int64_t& index, const FiberFractions* row) { if (m_scratchRow.size() != (size_t)m_dims[0]) m_scratchRow.resize(m_dims[0]); for (int64_t i = 0; i < m_dims[0]; ++i) { if (row[i].totalCount == 0) { m_scratchRow[i] = 0; } else { encodeFibers(row[i], m_scratchRow[i]); } } writeRow(index, (int64_t*)m_scratchRow.data()); } void CaretSparseFileWriter::writeFibersRowSparse(const int64_t& index, const vector& indices, const vector& values) { size_t numNonzero = values.size();//assume no zeros m_scratchSparseRow.resize(numNonzero); for (size_t i = 0; i < numNonzero; ++i) { encodeFibers(values[i], ((uint64_t*)m_scratchSparseRow.data())[i]); } writeRowSparse(index, indices, m_scratchSparseRow); } void CaretSparseFileWriter::finish() { if (m_finished) return; m_finished = true; while (m_nextRowIndex < m_dims[1]) { m_lengthArray[m_nextRowIndex] = 0; ++m_nextRowIndex; } QByteArray myXMLBytes = m_xml.writeXMLToQByteArray(); m_file.write(myXMLBytes.constData(), myXMLBytes.size()); m_file.seek(8 + 2 * sizeof(int64_t)); if (ByteOrderEnum::isSystemBigEndian()) { ByteSwapping::swapBytes(m_lengthArray.data(), m_lengthArray.size()); } m_file.write(m_lengthArray.data(), m_lengthArray.size() * sizeof(uint64_t)); m_file.close(); } CaretSparseFileWriter::~CaretSparseFileWriter() { finish(); } void CaretSparseFileWriter::encodeFibers(const FiberFractions& orig, uint64_t& coded) { coded = (((uint64_t)orig.totalCount)<<32) | (myclamp(orig.fiberFractions[0] * 1000.0f + 0.5f)<<20) | (myclamp(orig.fiberFractions[1] * 1000.0f + 0.5f)<<10) | (myclamp(orig.distance)); } uint32_t CaretSparseFileWriter::myclamp(const int& x) { if (x >= 1000) return 1000; if (x <= 0) return 0; return x; } connectome-workbench-1.2.3+git41-gc4c6c90/src/Files/CaretSparseFile.h000066400000000000000000000104111300200146000247700ustar00rootroot00000000000000#ifndef __CARET_SPARSE_FILE_H__ #define __CARET_SPARSE_FILE_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 "stdint.h" #include "AString.h" #include "CaretBinaryFile.h" #include "CiftiXML.h" #include "DataFile.h" #include "DataFileException.h" namespace caret { struct FiberFractions { uint32_t totalCount; // total number of streamline that go through the voxel std::vector fiberFractions; // fraction of totalCount for each fiber (3 orientation for now, could be zero) float distance; // average distance from seed across all streamlines void zero(); }; class CaretSparseFile /* : public DataFile */ { static void decodeFibers(const uint64_t& coded, FiberFractions& decoded);//takes a uint because right shift on signed is implementation dependent CaretBinaryFile m_file; int64_t m_dims[2], m_valuesOffset; std::vector m_indexArray, m_scratchRow; std::vector m_scratchArray, m_scratchSparseRow; CaretSparseFile(const CaretSparseFile& rhs); CiftiXML m_xml; public: const int64_t* getDimensions() { return m_dims; } CaretSparseFile() {}; virtual void readFile(const AString& filename); virtual void writeFile(const AString&) { throw DataFileException("writeFile not implemented for CaretSparseFile"); } CaretSparseFile(const AString& fileName); ///get a reference to the XML data const CiftiXML& getCiftiXML() const { return m_xml; } void getRow(const int64_t& index, int64_t* rowOut); void getRowSparse(const int64_t& index, std::vector& indicesOut, std::vector& valuesOut); void getFibersRow(const int64_t& index, FiberFractions* rowOut); void getFibersRowSparse(const int64_t& index, std::vector& indicesOut, std::vector& valuesOut); virtual ~CaretSparseFile(); }; class CaretSparseFileWriter { static void encodeFibers(const FiberFractions& orig, uint64_t& coded); static uint32_t myclamp(const int& x); CaretBinaryFile m_file; int64_t m_dims[2], m_valuesOffset, m_nextRowIndex; bool m_finished; std::vector m_lengthArray, m_scratchRow; std::vector m_scratchArray, m_scratchSparseRow; CaretSparseFileWriter(const CaretSparseFileWriter& rhs); CiftiXML m_xml; public: CaretSparseFileWriter(const AString& fileName, const CiftiXML& xml); ~CaretSparseFileWriter(); ///you must write the rows in order, though you can skip empty rows void writeRow(const int64_t& index, const int64_t* row); ///you must write the rows in order, though you can skip empty rows void writeRowSparse(const int64_t& index, const std::vector& indices, const std::vector& values); ///you must write the rows in order, though you can skip empty rows void writeFibersRow(const int64_t& index, const FiberFractions* row); ///you must write the rows in order, though you can skip empty rows void writeFibersRowSparse(const int64_t& index, const std::vector& indices, const std::vector& values); ///call this if no rows remain to be written void finish(); }; } #endif //__CARET_SPARSE_FILE_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Files/CaretVolumeExtension.cxx000066400000000000000000000343311300200146000264610ustar00rootroot00000000000000/*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 "CaretVolumeExtension.h" #include "XmlSaxParser.h" #include "GiftiXmlElements.h" #include "PaletteColorMapping.h" #include "PaletteColorMappingSaxReader.h" #include "PaletteColorMappingXmlElements.h" #include "CaretLogger.h" #include using namespace caret; using namespace std; //where should these go? static const AString CARET_VOL_EXT_ROOT = "CaretExtension"; static const AString CARET_VOL_EXT_COMMENT = "Comment"; static const AString CARET_VOL_EXT_DATE = "Date"; static const AString CARET_VOL_EXT_VOL_INFO = "VolumeInformation"; static const AString CARET_VOL_EXT_VI_COMMENT = "Comment"; static const AString CARET_VOL_EXT_VI_GUI_LABEL = "GuiLabel"; static const AString CARET_VOL_EXT_VI_STUDY_META_SET = "StudyMetaDataLinkSet"; static const AString CARET_VOL_EXT_VI_STUDY_META_LINK = "StudyMetaDataLink"; static const AString CARET_VOL_EXT_VI_TYPE = "VolumeType"; void CaretVolumeExtension::clear() { m_comment.clear(); m_date.clear(); m_attributes.clear(); } void CaretVolumeExtension::readFromXmlString(const AString& s) { CaretVolumeExtensionXMLReader myReader(this); CaretPointer myParser(XmlSaxParser::createXmlParser()); try { myParser->parseString(s, &myReader); } catch (XmlSaxParserException& e) { CaretLogWarning(AString("Failed to parse caret volume extension: ") + e.whatString()); } } void CaretVolumeExtension::writeAsXML(XmlWriter& xmlWriter) { xmlWriter.writeStartDocument("1.0"); xmlWriter.writeStartElement(CARET_VOL_EXT_ROOT); if (!m_comment.isEmpty()) xmlWriter.writeElementCData(CARET_VOL_EXT_COMMENT, m_comment); time_t mytime = time(NULL);//we don't have a class to deal with ISO 8601 dates, so use some C struct tm* timeinfo = localtime(&mytime);//note: this is a pointer to a static global in C library code, don't try to delete char buf[101];//we actually only need 20 bytes, but hey strftime(buf, 100, "%Y-%m-%dT%H:%M:%S", timeinfo); xmlWriter.writeElementCData(CARET_VOL_EXT_DATE, AString(buf)); int numVols = (int)m_attributes.size(); for (int i = 0; i < numVols; ++i) { m_attributes[i]->writeAsXML(xmlWriter, i); } xmlWriter.writeEndElement();//just to make it clean xmlWriter.writeEndDocument();//so, this just flushes } void SubvolumeAttributes::writeAsXML(XmlWriter& xmlWriter, int index) { XmlAttributes myattrs; myattrs.addAttribute("Index", AString::number(index)); xmlWriter.writeStartElement(CARET_VOL_EXT_VOL_INFO, myattrs); if (!m_comment.isEmpty()) xmlWriter.writeElementCData(CARET_VOL_EXT_VI_COMMENT, m_comment); if (!m_guiLabel.isEmpty()) xmlWriter.writeElementCData(CARET_VOL_EXT_VI_GUI_LABEL, m_guiLabel); if (m_labelTable != NULL) m_labelTable->writeAsXML(xmlWriter);//expect the extension to not have stuff it doesn't need, so just write everything it has m_studyMetadata.writeAsXML(xmlWriter); if (m_palette != NULL) m_palette->writeAsXML(xmlWriter); AString typeString; switch (m_type) { case ANATOMY: typeString = "Anatomy"; break; case FUNCTIONAL: typeString = "Functional"; break; case LABEL: typeString = "Label"; break; case RGB: typeString = "RGB"; break; case SEGMENTATION: typeString = "Segmentation"; break; case VECTOR: typeString = "Vector"; break; default: typeString = "Unknown"; } xmlWriter.writeElementCData(CARET_VOL_EXT_VI_TYPE, typeString); xmlWriter.writeEndElement(); } void StudyMetadataLinkSet::writeAsXML(XmlWriter& xmlWriter) {//TODO: something xmlWriter.writeStartElement(CARET_VOL_EXT_VI_STUDY_META_SET); xmlWriter.writeEndElement(); } CaretVolumeExtensionXMLReader::CaretVolumeExtensionXMLReader(CaretVolumeExtension* toFill): XmlSaxParserHandlerInterface() { CaretAssert(toFill != NULL); m_toFill = toFill; m_viIndex = -1; } void CaretVolumeExtensionXMLReader::characters(const char* ch) { CaretAssert(m_charDataStack.size() != 0); CaretAssert(m_stateStack.size() != 0); switch (m_stateStack.back()) { case LABEL_TABLE: CaretAssert(m_labelReader != NULL); m_labelReader->characters(ch); break; case PALETTE_COLOR_MAPPING: CaretAssert(m_paletteReader != NULL); m_paletteReader->characters(ch); break; default: m_charDataStack.back() += ch; } } void CaretVolumeExtensionXMLReader::endDocument() { if (m_stateStack.size() != 0) { throw XmlSaxParserException("end of document while still in an element state"); } } void CaretVolumeExtensionXMLReader::endElement(const AString& namespaceURI, const AString& localName, const AString& qualifiedName) { CaretAssert(m_charDataStack.size() != 0); CaretAssert(m_stateStack.size() != 0); AString elemCharData = m_charDataStack.back(); State myState = m_stateStack.back(); bool popState = true; switch (myState) { case INVALID: throw XmlSaxParserException("encountered end element in INVALID state"); break; case CARET_EXTENSION: break; case ROOT_COMMENT: m_toFill->m_comment = elemCharData; break; case DATE: m_toFill->m_date = elemCharData; break; case VOLUME_INFORMATION: m_viIndex = -1; break; case VI_COMMENT: CaretAssertVectorIndex(m_toFill->m_attributes, m_viIndex); m_toFill->m_attributes[m_viIndex]->m_comment = elemCharData; break; case GUI_LABEL: CaretAssertVectorIndex(m_toFill->m_attributes, m_viIndex); m_toFill->m_attributes[m_viIndex]->m_guiLabel = elemCharData; break; case LABEL_TABLE: CaretAssert(m_labelReader != NULL); m_labelReader->endElement(namespaceURI, localName, qualifiedName); if (qualifiedName == GiftiXmlElements::TAG_LABEL_TABLE) { m_labelReader->endDocument(); m_labelReader.grabNew(NULL);//make it delete now so that it can't make related bugs more confusing } else { popState = false; } break; case STUDY_META_DATA_LINK_SET: if (qualifiedName == CARET_VOL_EXT_VI_STUDY_META_SET) {//TODO: something } else { popState = false; } break; case PALETTE_COLOR_MAPPING: CaretAssert(m_paletteReader != NULL); m_paletteReader->endElement(namespaceURI, localName, qualifiedName); if (qualifiedName == PaletteColorMappingXmlElements::XML_TAG_PALETTE_COLOR_MAPPING) { m_paletteReader->endDocument(); m_paletteReader.grabNew(NULL);//ditto } else { popState = false; } break; case VOLUME_TYPE: CaretAssertVectorIndex(m_toFill->m_attributes, m_viIndex); if (elemCharData == "Anatomy") { m_toFill->m_attributes[m_viIndex]->m_type = SubvolumeAttributes::ANATOMY; } else if (elemCharData == "Functional") { m_toFill->m_attributes[m_viIndex]->m_type = SubvolumeAttributes::FUNCTIONAL; } else if (elemCharData == "Label") { m_toFill->m_attributes[m_viIndex]->m_type = SubvolumeAttributes::LABEL; } else if (elemCharData == "RGB") { m_toFill->m_attributes[m_viIndex]->m_type = SubvolumeAttributes::RGB; } else if (elemCharData == "Segmentation") { m_toFill->m_attributes[m_viIndex]->m_type = SubvolumeAttributes::SEGMENTATION; } else if (elemCharData == "Vector") { m_toFill->m_attributes[m_viIndex]->m_type = SubvolumeAttributes::VECTOR; } else { m_toFill->m_attributes[m_viIndex]->m_type = SubvolumeAttributes::UNKNOWN; } break; } if (popState) { m_stateStack.pop_back(); m_charDataStack.pop_back(); } } void CaretVolumeExtensionXMLReader::error(const XmlSaxParserException& exception) { CaretLogWarning(AString("encountered non-fatal XML error in CaretVolumeExtension: ") + exception.whatString()); } void CaretVolumeExtensionXMLReader::fatalError(const XmlSaxParserException& exception) {//all of our members are self-deleting, no worries, just throw throw XmlSaxParserException(exception);//throw a copy of it rather than the original reference, not sure if it matters } void CaretVolumeExtensionXMLReader::startDocument() { } void CaretVolumeExtensionXMLReader::startElement(const AString& uri, const AString& localName, const AString& qName, const XmlAttributes& atts) { bool addState = true; State nextState = INVALID; AString invalidInfo = qName; if(m_stateStack.size() == 0) { if (qName != CARET_VOL_EXT_ROOT) { throw XmlSaxParserException(AString("CaretVolumeExtension encountered unexpected root element: ") + qName); } nextState = CARET_EXTENSION; } else { switch (m_stateStack.back()) { case INVALID://should NEVER happen, INVALID throws instead of pushing, so should never be on the stack throw XmlSaxParserException("something has gone wrong in the CaretVolumeExtension parser"); break; case ROOT_COMMENT://these should not have child elements, let INVALID catch them case DATE: case VI_COMMENT: case GUI_LABEL: case VOLUME_TYPE: break; case CARET_EXTENSION: if (qName == CARET_VOL_EXT_COMMENT) { nextState = ROOT_COMMENT; } else if (qName == CARET_VOL_EXT_DATE) { nextState = DATE; } else if (qName == CARET_VOL_EXT_VOL_INFO) { nextState = VOLUME_INFORMATION; m_viIndex = atts.getValueAsInt("Index"); if (m_viIndex < 0) { throw XmlSaxParserException("negative number encountered in VolumeInformation index"); } if ((int)m_toFill->m_attributes.size() <= m_viIndex) { 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()); }//anything else gets caught in INVALID below break; case VOLUME_INFORMATION: if (qName == CARET_VOL_EXT_VI_COMMENT) { nextState = VI_COMMENT; } else if (qName == CARET_VOL_EXT_VI_GUI_LABEL) { nextState = GUI_LABEL; } else if (qName == GiftiXmlElements::TAG_LABEL_TABLE) { nextState = LABEL_TABLE; CaretAssertVectorIndex(m_toFill->m_attributes, m_viIndex); m_toFill->m_attributes[m_viIndex]->m_labelTable.grabNew(new GiftiLabelTable()); m_labelReader.grabNew(new GiftiLabelTableSaxReader(m_toFill->m_attributes[m_viIndex]->m_labelTable)); m_labelReader->startDocument(); m_labelReader->startElement(uri, localName, qName, atts); } else if (qName == CARET_VOL_EXT_VI_STUDY_META_SET) { nextState = STUDY_META_DATA_LINK_SET;//TODO: something } else if (qName == PaletteColorMappingXmlElements::XML_TAG_PALETTE_COLOR_MAPPING) { nextState = PALETTE_COLOR_MAPPING; CaretAssertVectorIndex(m_toFill->m_attributes, m_viIndex); m_toFill->m_attributes[m_viIndex]->m_palette.grabNew(new PaletteColorMapping); 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 == CARET_VOL_EXT_VI_TYPE) { nextState = VOLUME_TYPE; } break; case LABEL_TABLE: addState = false; CaretAssert(m_labelReader != NULL); m_labelReader->startElement(uri, localName, qName, atts); break; case STUDY_META_DATA_LINK_SET: addState = false;//TODO: something break; case PALETTE_COLOR_MAPPING: addState = false; CaretAssert(m_paletteReader != NULL); m_paletteReader->startElement(uri, localName, qName, atts); break; } } if (addState) { if (nextState == INVALID) { throw XmlSaxParserException(AString("CaretVolumeExtension encountered an unexpected element: ") + invalidInfo); } m_stateStack.push_back(nextState); m_charDataStack.push_back(AString()); } } void CaretVolumeExtensionXMLReader::warning(const caret::XmlSaxParserException& exception) { CaretLogWarning(AString("encountered XML warning in CaretVolumeExtension: ") + exception.whatString()); } connectome-workbench-1.2.3+git41-gc4c6c90/src/Files/CaretVolumeExtension.h000066400000000000000000000101271300200146000261030ustar00rootroot00000000000000#ifndef __CARET_VOLUME_EXTENSION__ #define __CARET_VOLUME_EXTENSION__ /*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 "GiftiLabelTable.h" #include "PaletteColorMapping.h" #include "CaretPointer.h" #include #include "XmlWriter.h" #include "XmlSaxParserHandlerInterface.h" #include "PaletteColorMappingSaxReader.h" #include "GiftiLabelTableSaxReader.h" namespace caret { struct StudyMetadataLinkSet { void writeAsXML(XmlWriter& xmlWriter); };//TODO: make this do something useful struct SubvolumeAttributes { enum VolumeType { UNKNOWN, ANATOMY, FUNCTIONAL, LABEL, RGB, SEGMENTATION, VECTOR };//TODO: make this into a caret enum class? AString m_comment; AString m_guiLabel; CaretPointer m_labelTable; StudyMetadataLinkSet m_studyMetadata; CaretPointer m_palette; VolumeType m_type; SubvolumeAttributes() { m_type = UNKNOWN; } void writeAsXML(XmlWriter& xmlWriter, int index); }; struct CaretVolumeExtension { AString m_comment; AString m_date;//TODO: make a class to handle ISO-8601 dates std::vector > m_attributes; void writeAsXML(XmlWriter& xmlWriter); void readFromXmlString(const AString& s); void clear(); }; class CaretVolumeExtensionXMLReader : public XmlSaxParserHandlerInterface { enum State { INVALID, CARET_EXTENSION, ROOT_COMMENT, DATE, VOLUME_INFORMATION, VI_COMMENT, GUI_LABEL, LABEL_TABLE, STUDY_META_DATA_LINK_SET, PALETTE_COLOR_MAPPING, VOLUME_TYPE }; std::vector m_stateStack; CaretVolumeExtension* m_toFill; std::vector m_charDataStack; int m_viIndex; CaretPointer m_paletteReader; CaretPointer m_labelReader; CaretVolumeExtensionXMLReader();//disallow default construction CaretVolumeExtensionXMLReader(const CaretVolumeExtensionXMLReader&);//disallow copy CaretVolumeExtensionXMLReader& operator=(const CaretVolumeExtensionXMLReader&);//disallow assignment public: CaretVolumeExtensionXMLReader(CaretVolumeExtension* toFill); virtual void startElement(const AString& uri, const AString& localName, const AString& qName, const XmlAttributes& atts) ; virtual void endElement(const AString& namespaceURI, const AString& localName, const AString& qualifiedName) ; virtual void characters(const char* ch); virtual void warning(const XmlSaxParserException& exception); virtual void error(const XmlSaxParserException& exception); virtual void fatalError(const XmlSaxParserException& exception); virtual void startDocument(); virtual void endDocument(); }; } #endif //__CARET_VOLUME_EXTENSION__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Files/ChartableLineSeriesBrainordinateInterface.h000066400000000000000000000077011300200146000321570ustar00rootroot00000000000000#ifndef __CHARTABLE_BRAINORDINATE_INTERFACE_H__ #define __CHARTABLE_BRAINORDINATE_INTERFACE_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 "ChartableLineSeriesInterface.h" #include "StructureEnum.h" namespace caret { class ChartDataCartesian; /** * \class caret::ChartableLineSeriesBrainordinateInterface * \brief Interface for files that are able to produce line series brainordinate charts. * \ingroup Files */ class ChartableLineSeriesBrainordinateInterface : public ChartableLineSeriesInterface { protected: ChartableLineSeriesBrainordinateInterface() { } virtual ~ChartableLineSeriesBrainordinateInterface() { } public: /** * Load charting data for the surface with the given structure and node index. * * @param structure * The surface's structure. * @param nodeIndex * Index of the node. * @return * Pointer to the chart data. If the data FAILED to load, * the returned pointer will be NULL. Caller takes ownership * of the pointer and must delete it when no longer needed. */ virtual ChartDataCartesian* loadLineSeriesChartDataForSurfaceNode(const StructureEnum::Enum structure, const int32_t nodeIndex) = 0; /** * Load average charting data for the surface with the given structure and node indices. * * @param structure * The surface's structure. * @param nodeIndices * Indices of the node. * @return * Pointer to the chart data. If the data FAILED to load, * the returned pointer will be NULL. Caller takes ownership * of the pointer and must delete it when no longer needed. */ virtual ChartDataCartesian* loadAverageLineSeriesChartDataForSurfaceNodes(const StructureEnum::Enum structure, const std::vector& nodeIndices) = 0; /** * Load charting data for the voxel enclosing the given coordinate. * * @param xyz * Coordinate of voxel. * @return * Pointer to the chart data. If the data FAILED to load, * the returned pointer will be NULL. Caller takes ownership * of the pointer and must delete it when no longer needed. */ virtual ChartDataCartesian* loadLineSeriesChartDataForVoxelAtCoordinate(const float xyz[3]) = 0; private: // ChartableLineSeriesBrainordinateInterface(const ChartableLineSeriesBrainordinateInterface&); // // ChartableLineSeriesBrainordinateInterface& operator=(const ChartableLineSeriesBrainordinateInterface&); public: // ADD_NEW_METHODS_HERE private: // ADD_NEW_MEMBERS_HERE }; #ifdef __CHARTABLE_BRAINORDINATE_INTERFACE_DECLARE__ // #endif // __CHARTABLE_BRAINORDINATE_INTERFACE_DECLARE__ } // namespace #endif //__CHARTABLE_BRAINORDINATE_INTERFACE_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Files/ChartableLineSeriesInterface.cxx000066400000000000000000000050501300200146000300230ustar00rootroot00000000000000/*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 __CHARTABLE_LINE_SERIES_INTERFACE_DECLARE__ #include "ChartableLineSeriesInterface.h" #undef __CHARTABLE_LINE_SERIES_INTERFACE_DECLARE__ #include "CaretMappableDataFile.h" using namespace caret; /** * \class caret::ChartableLineSeriesInterface * \brief Interface for files that are able to produce line charts charts. * \ingroup Files */ /** * Is the given chart data type supported by this file. * * @param chartDataType * Chart data type for testing support. * @return * True if chart data type is supported by the file, else false. */ bool ChartableLineSeriesInterface::isLineSeriesChartDataTypeSupported(const ChartDataTypeEnum::Enum chartDataType) const { std::vector validTypes; getSupportedLineSeriesChartDataTypes(validTypes); if (std::find(validTypes.begin(), validTypes.end(), chartDataType) != validTypes.end()) { return true; } return false; } /** * @return The CaretMappableDataFile that implements this interface. * Will be NULL if this interface is not implemented by a CaretMappableDataFile. */ CaretMappableDataFile* ChartableLineSeriesInterface::getLineSeriesChartCaretMappableDataFile() { CaretMappableDataFile* cmdf = dynamic_cast(this); CaretAssert(cmdf); return cmdf; } /** * @return The CaretMappableDataFile that implements this interface. * Will be NULL if this interface is not implemented by a CaretMappableDataFile. */ const CaretMappableDataFile* ChartableLineSeriesInterface::getLineSeriesChartCaretMappableDataFile() const { const CaretMappableDataFile* cmdf = dynamic_cast(this); CaretAssert(cmdf); return cmdf; } connectome-workbench-1.2.3+git41-gc4c6c90/src/Files/ChartableLineSeriesInterface.h000066400000000000000000000065341300200146000274600ustar00rootroot00000000000000#ifndef __CHARTABLE_LINE_SERIES_INTERFACE_H__ #define __CHARTABLE_LINE_SERIES_INTERFACE_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 "ChartDataTypeEnum.h" #include "StructureEnum.h" namespace caret { //class CaretMappableDataFile; class ChartDataCartesian; class CaretMappableDataFile; class ChartableLineSeriesInterface { protected: ChartableLineSeriesInterface() { } virtual ~ChartableLineSeriesInterface() { } public: /** * @return The CaretMappableDataFile that implements this interface. */ virtual CaretMappableDataFile* getLineSeriesChartCaretMappableDataFile(); /** * @return The CaretMappableDataFile that implements this interface. */ virtual const CaretMappableDataFile* getLineSeriesChartCaretMappableDataFile() const; /** * @return Is charting enabled for this file in the given tab? */ virtual bool isLineSeriesChartingEnabled(const int32_t tabIndex) const = 0; /** * @return Return true if the file's current state supports * charting data, else false. Typically a brainordinate file * is chartable if it contains more than one map. */ virtual bool isLineSeriesChartingSupported() const = 0; /** * Set charting enabled for this file in the given tab * * @param enabled * New status for charting enabled. */ virtual void setLineSeriesChartingEnabled(const int32_t tabIndex, const bool enabled) = 0; /** * Get chart data types supported by the file. * * @param chartDataTypesOut * Chart types supported by this file. */ virtual void getSupportedLineSeriesChartDataTypes(std::vector& chartDataTypesOut) const = 0; bool isLineSeriesChartDataTypeSupported(const ChartDataTypeEnum::Enum chartDataType) const; private: // ChartableLineSeriesInterface(const ChartableLineSeriesInterface&); // // ChartableLineSeriesInterface& operator=(const ChartableLineSeriesInterface&); public: // ADD_NEW_METHODS_HERE private: // ADD_NEW_MEMBERS_HERE }; #ifdef __CHARTABLE_LINE_SERIES_INTERFACE_DECLARE__ // #endif // __CHARTABLE_LINE_SERIES_INTERFACE_DECLARE__ } // namespace #endif //__CHARTABLE_LINE_SERIES_INTERFACE_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Files/ChartableLineSeriesRowColumnInterface.h000066400000000000000000000056721300200146000313300ustar00rootroot00000000000000#ifndef __CHARTABLE_LINE_SERIES_ROW_COLUMN_INTERFACE_H__ #define __CHARTABLE_LINE_SERIES_ROW_COLUMN_INTERFACE_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 "ChartableLineSeriesInterface.h" namespace caret { class ChartDataCartesian; /** * \class caret::ChartableLineSeriesRowColumnInterface * \brief Interface for charts that load a row and/or column of data. * \ingroup Files */ class ChartableLineSeriesRowColumnInterface : public ChartableLineSeriesInterface { public: ChartableLineSeriesRowColumnInterface() { } virtual ~ChartableLineSeriesRowColumnInterface() { }; /** * Load charting data for the given column index. * * @param columnIndex * Index of the column. * @return * Pointer to the chart data. If the data FAILED to load, * the returned pointer will be NULL. Caller takes ownership * of the pointer and must delete it when no longer needed. */ virtual ChartDataCartesian* loadLineSeriesChartDataForColumn(const int32_t columnIndex) = 0; /** * Load charting data for the given row index. * * @param rowIndex * Index of the row. * @return * Pointer to the chart data. If the data FAILED to load, * the returned pointer will be NULL. Caller takes ownership * of the pointer and must delete it when no longer needed. */ virtual ChartDataCartesian* loadLineSeriesChartDataForRow(const int32_t rowIndex) = 0; // ADD_NEW_METHODS_HERE private: ChartableLineSeriesRowColumnInterface(const ChartableLineSeriesRowColumnInterface&); ChartableLineSeriesRowColumnInterface& operator=(const ChartableLineSeriesRowColumnInterface&); // ADD_NEW_MEMBERS_HERE }; #ifdef __CHARTABLE_LINE_SERIES_ROW_COLUMN_INTERFACE_DECLARE__ // #endif // __CHARTABLE_LINE_SERIES_ROW_COLUMN_INTERFACE_DECLARE__ } // namespace #endif //__CHARTABLE_LINE_SERIES_ROW_COLUMN_INTERFACE_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Files/ChartableMatrixInterface.cxx000066400000000000000000000064121300200146000272300ustar00rootroot00000000000000/*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 __CHARTABLE_MATRIX_INTERFACE_DECLARE__ #include "ChartableMatrixInterface.h" #undef __CHARTABLE_MATRIX_INTERFACE_DECLARE__ #include "CaretMappableDataFile.h" #include "CiftiMappableDataFile.h" using namespace caret; /** * \class caret::ChartableMatrixInterface * \brief Interface for files that are able to produce charts. * \ingroup Files */ /** * Is the given chart data type supported by this file. * * @param chartDataType * Chart data type for testing support. * @return * True if chart data type is supported by the file, else false. */ bool ChartableMatrixInterface::isMatrixChartDataTypeSupported(const ChartDataTypeEnum::Enum chartDataType) const { std::vector validTypes; getSupportedMatrixChartDataTypes(validTypes); if (std::find(validTypes.begin(), validTypes.end(), chartDataType) != validTypes.end()) { return true; } return false; } /** * @return The CaretMappableDataFile that implements this interface. * Will be NULL if this interface is not implemented by a CaretMappableDataFile. */ CaretMappableDataFile* ChartableMatrixInterface::getMatrixChartCaretMappableDataFile() { CaretMappableDataFile* cmdf = dynamic_cast(this); CaretAssert(cmdf); return cmdf; } /** * @return The CaretMappableDataFile that implements this interface. * Will be NULL if this interface is not implemented by a CaretMappableDataFile. */ const CaretMappableDataFile* ChartableMatrixInterface::getMatrixChartCaretMappableDataFile() const { const CaretMappableDataFile* cmdf = dynamic_cast(this); CaretAssert(cmdf); return cmdf; } /** * @return The CaretMappableDataFile that implements this interface. * Will be NULL if this interface is not implemented by a CaretMappableDataFile. */ CiftiMappableDataFile* ChartableMatrixInterface::getMatrixChartCiftiMappableDataFile() { CiftiMappableDataFile* cmdf = dynamic_cast(this); CaretAssert(cmdf); return cmdf; } /** * @return The CaretMappableDataFile that implements this interface. * Will be NULL if this interface is not implemented by a CaretMappableDataFile. */ const CiftiMappableDataFile* ChartableMatrixInterface::getMatrixChartCiftiMappableDataFile() const { const CiftiMappableDataFile* cmdf = dynamic_cast(this); CaretAssert(cmdf); return cmdf; } connectome-workbench-1.2.3+git41-gc4c6c90/src/Files/ChartableMatrixInterface.h000066400000000000000000000147031300200146000266570ustar00rootroot00000000000000#ifndef __CHARTABLE_MATRIX_INTERFACE_H__ #define __CHARTABLE_MATRIX_INTERFACE_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 "ChartDataTypeEnum.h" #include "ChartMatrixLoadingDimensionEnum.h" #include "CiftiParcelColoringModeEnum.h" #include "YokingGroupEnum.h" namespace caret { class CaretMappableDataFile; class ChartMatrixDisplayProperties; class CiftiMappableDataFile; class CiftiParcelsMap; class ChartableMatrixInterface { protected: ChartableMatrixInterface() { } virtual ~ChartableMatrixInterface() { } public: /** * Get the matrix dimensions. * * @param numberOfRowsOut * Number of rows in the matrix. * @param numberOfColumnsOut * Number of rows in the matrix. */ virtual void getMatrixDimensions(int32_t& numberOfRowsOut, int32_t& numberOfColumnsOut) const = 0; /** * 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. */ virtual bool getMatrixDataRGBA(int32_t& numberOfRowsOut, int32_t& numberOfColumnsOut, std::vector& rgbaOut) const = 0; /** * Get the value, row name, and column name for a cell in the matrix. * * @param rowIndex * The row index. * @param columnIndex * The column index. * @param cellValueOut * Output containing value in the cell. * @param rowNameOut * Name of row corresponding to row index. * @param columnNameOut * Name of column corresponding to column index. * @return * True if the output values are valid (valid row/column indices). */ virtual bool getMatrixCellAttributes(const int32_t rowIndex, const int32_t columnIndex, AString& cellValueOut, AString& rowNameOut, AString& columnNameOut) const = 0; /** * @return The matrix display properties for the given tab. * @param tabIndex * Index of tab. */ virtual const ChartMatrixDisplayProperties* getChartMatrixDisplayProperties(const int32_t tabIndex) const = 0; /** * @return The matrix display properties for the given tab. * @param tabIndex * Index of tab. */ virtual ChartMatrixDisplayProperties* getChartMatrixDisplayProperties(const int32_t tabIndex) = 0; /** * @return The CaretMappableDataFile that implements this interface. */ virtual CaretMappableDataFile* getMatrixChartCaretMappableDataFile(); /** * @return The CaretMappableDataFile that implements this interface (const methdod). */ virtual const CaretMappableDataFile* getMatrixChartCaretMappableDataFile() const; /** * @return The CiftiMappableDataFile that implements this interface. * May be NULL ! */ virtual CiftiMappableDataFile* getMatrixChartCiftiMappableDataFile(); /** * @return The CiftiMappableDataFile that implements this interface (const methdod). * May be NULL ! */ virtual const CiftiMappableDataFile* getMatrixChartCiftiMappableDataFile() const; /** * @return Is charting enabled for this file in the given tab? */ virtual bool isMatrixChartingEnabled(const int32_t tabIndex) const = 0; /** * @return Return true if the file's current state supports * charting data, else false. Typically a brainordinate file * is chartable if it contains more than one map. */ virtual bool isMatrixChartingSupported() const = 0; /** * Set charting enabled for this file in the given tab * * @param enabled * New status for charting enabled. */ virtual void setMatrixChartingEnabled(const int32_t tabIndex, const bool enabled) = 0; /** * Get chart data types supported by the file. * * @param chartDataTypesOut * Chart types supported by this file. */ virtual void getSupportedMatrixChartDataTypes(std::vector& chartDataTypesOut) const = 0; bool isMatrixChartDataTypeSupported(const ChartDataTypeEnum::Enum chartDataType) const; // ADD_NEW_METHODS_HERE private: ChartableMatrixInterface(const ChartableMatrixInterface&); ChartableMatrixInterface& operator=(const ChartableMatrixInterface&); // ADD_NEW_MEMBERS_HERE }; #ifdef __CHARTABLE_MATRIX_INTERFACE_DECLARE__ // #endif // __CHARTABLE_MATRIX_INTERFACE_DECLARE__ } // namespace #endif //__CHARTABLE_MATRIX_INTERFACE_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Files/ChartableMatrixParcelInterface.h000066400000000000000000000156541300200146000300140ustar00rootroot00000000000000#ifndef __CHARTABLE_MATRIX_PARCEL_REORDER_INTERFACE_H__ #define __CHARTABLE_MATRIX_PARCEL_REORDER_INTERFACE_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 "ChartableMatrixInterface.h" namespace caret { class CiftiParcelLabelFile; class CiftiParcelReordering; class ChartableMatrixParcelInterface : public ChartableMatrixInterface { protected: ChartableMatrixParcelInterface() { } virtual ~ChartableMatrixParcelInterface() { } public: /** * 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. */ virtual void getSelectedParcelLabelFileAndMapForReordering(std::vector& compatibleParcelLabelFilesOut, CiftiParcelLabelFile* &selectedParcelLabelFileOut, int32_t& selectedParcelLabelFileMapIndexOut, bool& enabledStatusOut) const = 0; /** * 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. */ virtual void setSelectedParcelLabelFileAndMapForReordering(CiftiParcelLabelFile* selectedParcelLabelFile, const int32_t selectedParcelLabelFileMapIndex, const bool enabledStatus) = 0; /** * 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 errorMessageOut * Error message output. Will only be non-empty if NULL is returned. * @return * Pointer to parcel reordering or NULL if not found. */ virtual bool createParcelReordering(const CiftiParcelLabelFile* parcelLabelFile, const int32_t parcelLabelFileMapIndex, AString& errorMessageOut) = 0; /** * 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. */ virtual const CiftiParcelReordering* getParcelReordering(const CiftiParcelLabelFile* parcelLabelFile, const int32_t parcelLabelFileMapIndex) const = 0; /** * @return Coloring mode for selected parcel. */ virtual CiftiParcelColoringModeEnum::Enum getSelectedParcelColoringMode() const = 0; /** * Set the coloring mode for selected parcel. * * @param coloringMode * New value for coloring mode. */ virtual void setSelectedParcelColoringMode(const CiftiParcelColoringModeEnum::Enum coloringMode) = 0; /** * @return Color for selected parcel. */ virtual CaretColorEnum::Enum getSelectedParcelColor() const = 0; /** * @return True if loading attributes (column/row, yoking) are * supported by this file type. */ virtual bool isSupportsLoadingAttributes() = 0; /** * @return The matrix loading type (by row/column). */ virtual ChartMatrixLoadingDimensionEnum::Enum getMatrixLoadingDimension() const = 0; /** * Set the matrix loading type (by row/column). * * @param matrixLoadingType * New value for matrix loading type. */ virtual void setMatrixLoadingDimension(const ChartMatrixLoadingDimensionEnum::Enum matrixLoadingType) = 0; /** * Set color for selected parcel. * * @param color * New color for selected parcel. */ virtual void setSelectedParcelColor(const CaretColorEnum::Enum color) = 0; /** * @return Selected yoking group. */ virtual YokingGroupEnum::Enum getYokingGroup() const = 0; /** * Set the selected yoking group. * * @param yokingGroup * New value for yoking group. */ virtual void setYokingGroup(const YokingGroupEnum::Enum yokingType) = 0; private: ChartableMatrixParcelInterface(const ChartableMatrixParcelInterface&); ChartableMatrixParcelInterface& operator=(const ChartableMatrixParcelInterface&); // ADD_NEW_MEMBERS_HERE }; #ifdef __CHARTABLE_MATRIX_INTERFACE_PARCEL_REORDER_DECLARE__ // #endif // __CHARTABLE_MATRIX_INTERFACE_PARCEL_REORDER_DECLARE__ } // namespace #endif //__CHARTABLE_MATRIX_PARCEL_REORDER_INTERFACE_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Files/ChartableMatrixSeriesInterface.h000066400000000000000000000043141300200146000300270ustar00rootroot00000000000000#ifndef __CHARTABLE_MATRIX_SERIES_INTERFACE_H__ #define __CHARTABLE_MATRIX_SERIES_INTERFACE_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 "ChartableMatrixInterface.h" #include "MapYokingGroupEnum.h" namespace caret { class ChartableMatrixSeriesInterface : public ChartableMatrixInterface { public: ChartableMatrixSeriesInterface() { } virtual ~ChartableMatrixSeriesInterface() { } virtual MapYokingGroupEnum::Enum getMatrixRowColumnMapYokingGroup(const int32_t tabIndex) const = 0; virtual void setMatrixRowColumnMapYokingGroup(const int32_t tabIndex, const MapYokingGroupEnum::Enum yokingType) = 0; virtual int32_t getSelectedMapIndex(const int32_t tabIndex) = 0; virtual void setSelectedMapIndex(const int32_t tabIndex, const int32_t mapIndex) = 0; // ADD_NEW_METHODS_HERE private: ChartableMatrixSeriesInterface(const ChartableMatrixSeriesInterface&); ChartableMatrixSeriesInterface& operator=(const ChartableMatrixSeriesInterface&); // ADD_NEW_MEMBERS_HERE }; #ifdef __CHARTABLE_MATRIX_SERIES_INTERFACE_DECLARE__ // #endif // __CHARTABLE_MATRIX_SERIES_INTERFACE_DECLARE__ } // namespace #endif //__CHARTABLE_MATRIX_SERIES_INTERFACE_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Files/CiftiBrainordinateDataSeriesFile.cxx000066400000000000000000000344131300200146000306440ustar00rootroot00000000000000 /*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 __CIFTI_BRAINORDINATE_DATA_SERIES_FILE_DECLARE__ #include "CiftiBrainordinateDataSeriesFile.h" #undef __CIFTI_BRAINORDINATE_DATA_SERIES_FILE_DECLARE__ #include "CaretLogger.h" #include "ChartDataCartesian.h" #include "CiftiConnectivityMatrixDenseDynamicFile.h" #include "CiftiFile.h" #include "DataFileException.h" #include "ElapsedTimer.h" #include "SceneClass.h" using namespace caret; /** * \class caret::CiftiBrainordinateDataSeriesFile * \brief CIFTI Brainordinate by Data-Series File. * \ingroup Files */ /** * Constructor. */ CiftiBrainordinateDataSeriesFile::CiftiBrainordinateDataSeriesFile() : CiftiMappableDataFile(DataFileTypeEnum::CONNECTIVITY_DENSE_TIME_SERIES) { for (int32_t i = 0; i < BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS; i++) { m_chartingEnabledForTab[i] = false; } m_lazyInitializedDenseDynamicFile = NULL; } /** * Destructor. */ CiftiBrainordinateDataSeriesFile::~CiftiBrainordinateDataSeriesFile() { if (m_lazyInitializedDenseDynamicFile != NULL) { delete m_lazyInitializedDenseDynamicFile; } } /** * Clear the contents of the file. */ void CiftiBrainordinateDataSeriesFile::clear() { CiftiMappableDataFile::clear(); for (int32_t i = 0; i < BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS; i++) { m_chartingEnabledForTab[i] = false; } if (m_lazyInitializedDenseDynamicFile != NULL) { delete m_lazyInitializedDenseDynamicFile; m_lazyInitializedDenseDynamicFile = NULL; //m_lazyInitializedDenseDynamicFile->clear(); } } /** * Initialize the dense dynamic file. */ void CiftiBrainordinateDataSeriesFile::initializeDenseDynamicFile() { if (m_lazyInitializedDenseDynamicFile != NULL) { return; } m_lazyInitializedDenseDynamicFile = new CiftiConnectivityMatrixDenseDynamicFile(this); try { //ElapsedTimer timer; //timer.start(); m_lazyInitializedDenseDynamicFile->readFile(getFileName()); m_lazyInitializedDenseDynamicFile->updateAfterReading(getCiftiFile()); /* * Palette for dynamic file is in my CIFTI file metadata */ GiftiMetaData* fileMetaData = m_ciftiFile->getCiftiXML().getFileMetaData(); const AString encodedPaletteColorMappingString = fileMetaData->get(s_paletteColorMappingNameInMetaData); if ( ! encodedPaletteColorMappingString.isEmpty()) { if (m_lazyInitializedDenseDynamicFile-getNumberOfMaps() > 0) { PaletteColorMapping* pcm = m_lazyInitializedDenseDynamicFile->getMapPaletteColorMapping(0); CaretAssert(pcm); pcm->decodeFromStringXML(encodedPaletteColorMappingString); } } m_lazyInitializedDenseDynamicFile->clearModified(); // /* // * Remove palette color mapping from metadata so not seen by user // */ // fileMetaData->remove(s_paletteColorMappingNameInMetaData); // clearModified(); /*AString msg = ("Time to setup dense dynamic file " + m_lazyInitializedDenseDynamicFile->getFileNameNoPath() + " was " + AString::number(timer.getElapsedTimeSeconds()) + " seconds."); CaretLogInfo(msg);//*/ } catch (const DataFileException& dfe) { m_lazyInitializedDenseDynamicFile->clear(); CaretLogSevere("ERROR initializing dense dynamic file for " + getFileName() + ": " + dfe.whatString()); } // std::cout << "Initializing dense dynamic file" << std::endl; } /** * Read the file. * * @param ciftiMapFileName * Name of the file to read. * @throw * DataFileException if there is an error reading the file. */ void CiftiBrainordinateDataSeriesFile::readFile(const AString& ciftiMapFileName) { CiftiMappableDataFile::readFile(ciftiMapFileName); } /** * Write the file. * * @param ciftiMapFileName * Name of the file to write. * @throw * DataFileException if there is an error writing the file. */ void CiftiBrainordinateDataSeriesFile::writeFile(const AString& ciftiMapFileName) { /* * Put the child dynamic data-series file's palette in the file's metadata. */ if (m_lazyInitializedDenseDynamicFile != NULL) { GiftiMetaData* fileMetaData = m_ciftiFile->getCiftiXML().getFileMetaData(); CaretAssert(fileMetaData); if (m_lazyInitializedDenseDynamicFile-getNumberOfMaps() > 0) { fileMetaData->set(s_paletteColorMappingNameInMetaData, m_lazyInitializedDenseDynamicFile->getMapPaletteColorMapping(0)->encodeInXML()); } else { fileMetaData->remove(s_paletteColorMappingNameInMetaData); } } CiftiMappableDataFile::writeFile(ciftiMapFileName); clearModified(); if (m_lazyInitializedDenseDynamicFile != NULL) { m_lazyInitializedDenseDynamicFile->clearModified(); } } /** * @return Is charting enabled for this file? */ bool CiftiBrainordinateDataSeriesFile::isLineSeriesChartingEnabled(const int32_t tabIndex) const { CaretAssertArrayIndex(m_chartingEnabledForTab, BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS, tabIndex); return m_chartingEnabledForTab[tabIndex]; } /** * @return Return true if the file's current state supports * charting data, else false. Typically a brainordinate file * is chartable if it contains more than one map. */ bool CiftiBrainordinateDataSeriesFile::isLineSeriesChartingSupported() const { if (getNumberOfMaps() > 1) { return true; } return false; } /** * Set charting enabled for this file. * * @param enabled * New status for charting enabled. */ void CiftiBrainordinateDataSeriesFile::setLineSeriesChartingEnabled(const int32_t tabIndex, const bool enabled) { CaretAssertArrayIndex(m_chartingEnabledForTab, BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS, tabIndex); m_chartingEnabledForTab[tabIndex] = enabled; } /** * Load charting data for the surface with the given structure and node index. * * @param structure * The surface's structure. * @param nodeIndex * Index of the node. * @return * Pointer to the chart data. If the data FAILED to load, * the returned pointer will return true. Caller takes ownership * of the pointer and must delete it when no longer needed. */ ChartDataCartesian* CiftiBrainordinateDataSeriesFile::loadLineSeriesChartDataForSurfaceNode(const StructureEnum::Enum structure, const int32_t nodeIndex) { ChartDataCartesian* chartData = helpLoadChartDataForSurfaceNode(structure, nodeIndex); return chartData; } /** * Load average charting data for the surface with the given structure and node indices. * * @param structure * The surface's structure. * @param nodeIndices * Indices of the node. * @return * Pointer to the chart data. If the data FAILED to load, * the returned pointer will be NULL. Caller takes ownership * of the pointer and must delete it when no longer needed. */ ChartDataCartesian* CiftiBrainordinateDataSeriesFile::loadAverageLineSeriesChartDataForSurfaceNodes(const StructureEnum::Enum structure, const std::vector& nodeIndices) { ChartDataCartesian* chartData = helpLoadChartDataForSurfaceNodeAverage(structure, nodeIndices); return chartData; } /** * Load charting data for the voxel enclosing the given coordinate. * * @param xyz * Coordinate of voxel. * @return * Pointer to the chart data. If the data FAILED to load, * the returned pointer will be NULL. Caller takes ownership * of the pointer and must delete it when no longer needed. */ ChartDataCartesian* CiftiBrainordinateDataSeriesFile::loadLineSeriesChartDataForVoxelAtCoordinate(const float xyz[3]) { ChartDataCartesian* chartData = helpLoadChartDataForVoxelAtCoordinate(xyz); return chartData; } /** * Get chart data types supported by the file. * * @param chartDataTypesOut * Chart types supported by this file. */ void CiftiBrainordinateDataSeriesFile::getSupportedLineSeriesChartDataTypes(std::vector& chartDataTypesOut) const { helpGetSupportedLineSeriesChartDataTypes(chartDataTypesOut); } /** * Save file data from the scene. For subclasses that need to * save to a scene, this method should be overriden. sceneClass * will be valid and any scene data should be added to it. * * @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. */ void CiftiBrainordinateDataSeriesFile::saveFileDataToScene(const SceneAttributes* sceneAttributes, SceneClass* sceneClass) { CiftiMappableDataFile::saveFileDataToScene(sceneAttributes, sceneClass); sceneClass->addBooleanArray("m_chartingEnabledForTab", m_chartingEnabledForTab, BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS); if (m_lazyInitializedDenseDynamicFile != NULL) { sceneClass->addClass(m_lazyInitializedDenseDynamicFile->saveToScene(sceneAttributes, "m_lazyInitializedDenseDynamicFile")); } } /** * Restore file data from the scene. For subclasses that need to * restore from a scene, this method should be overridden. The scene class * will be valid and any scene data may be obtained from it. * * @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 CiftiBrainordinateDataSeriesFile::restoreFileDataFromScene(const SceneAttributes* sceneAttributes, const SceneClass* sceneClass) { CiftiMappableDataFile::restoreFileDataFromScene(sceneAttributes, sceneClass); const ScenePrimitiveArray* tabArray = sceneClass->getPrimitiveArray("m_chartingEnabledForTab"); if (tabArray != NULL) { sceneClass->getBooleanArrayValue("m_chartingEnabledForTab", m_chartingEnabledForTab, BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS); } else { /* * Obsolete value when charting was not 'per tab' */ const bool chartingEnabled = sceneClass->getBooleanValue("m_chartingEnabled", false); for (int32_t i = 0; i < BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS; i++) { m_chartingEnabledForTab[i] = chartingEnabled; } } const SceneClass* dynamicFileSceneClass = sceneClass->getClass("m_lazyInitializedDenseDynamicFile"); if (dynamicFileSceneClass != NULL) { CiftiConnectivityMatrixDenseDynamicFile* denseDynamicFile = getConnectivityMatrixDenseDynamicFile(); denseDynamicFile->restoreFromScene(sceneAttributes, dynamicFileSceneClass); } } /** * @return My matrix dense dynamic file representation. */ CiftiConnectivityMatrixDenseDynamicFile* CiftiBrainordinateDataSeriesFile::getConnectivityMatrixDenseDynamicFile() { if (m_lazyInitializedDenseDynamicFile == NULL) { initializeDenseDynamicFile(); } return m_lazyInitializedDenseDynamicFile; } ///** // * @return My matrix dense dynamic file representation (const method). // */ //const CiftiConnectivityMatrixDenseDynamicFile* //CiftiBrainordinateDataSeriesFile::getConnectivityMatrixDenseDynamicFile() const //{ // CiftiBrainordinateDataSeriesFile* nonConstThis = const_cast(this); // if (m_lazyInitializedDenseDynamicFile == NULL) { // nonConstThis->initializeDenseDynamicFile(); // } // return m_lazyInitializedDenseDynamicFile; //} /** * @return True if any of the maps in this file contain a * color mapping that possesses a modified status. */ bool CiftiBrainordinateDataSeriesFile::isModifiedPaletteColorMapping() const { /* * This method is override because we need to know if the * encapsulated dynamic dense file has a modified palette. * When restoring a scene, a file with any type of modification * must be reloaded to remove any modifications. Note that * when a scene is restored, files that are not modified and * are in the new scene are NOT reloaded to save time. */ if (CiftiMappableDataFile::isModifiedPaletteColorMapping()) { return true; } if (m_lazyInitializedDenseDynamicFile != NULL) { if (m_lazyInitializedDenseDynamicFile->isModifiedPaletteColorMapping()) { return true; } } return false; } connectome-workbench-1.2.3+git41-gc4c6c90/src/Files/CiftiBrainordinateDataSeriesFile.h000066400000000000000000000101301300200146000302570ustar00rootroot00000000000000#ifndef __CIFTI_BRAINORDINATE_DATA_SERIES_FILE_H__ #define __CIFTI_BRAINORDINATE_DATA_SERIES_FILE_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 "BrainConstants.h" #include "ChartableLineSeriesBrainordinateInterface.h" #include "CiftiMappableDataFile.h" namespace caret { class CiftiConnectivityMatrixDenseDynamicFile; class PaletteFile; class CiftiBrainordinateDataSeriesFile : public CiftiMappableDataFile, public ChartableLineSeriesBrainordinateInterface { public: CiftiBrainordinateDataSeriesFile(); virtual ~CiftiBrainordinateDataSeriesFile(); virtual bool isLineSeriesChartingEnabled(const int32_t tabIndex) const; virtual void setLineSeriesChartingEnabled(const int32_t tabIndex, const bool enabled); virtual bool isLineSeriesChartingSupported() const; virtual ChartDataCartesian* loadLineSeriesChartDataForSurfaceNode(const StructureEnum::Enum structure, const int32_t nodeIndex); virtual ChartDataCartesian* loadAverageLineSeriesChartDataForSurfaceNodes(const StructureEnum::Enum structure, const std::vector& nodeIndices); virtual ChartDataCartesian* loadLineSeriesChartDataForVoxelAtCoordinate(const float xyz[3]); virtual void getSupportedLineSeriesChartDataTypes(std::vector& chartDataTypesOut) const; CiftiConnectivityMatrixDenseDynamicFile* getConnectivityMatrixDenseDynamicFile(); //const CiftiConnectivityMatrixDenseDynamicFile* getConnectivityMatrixDenseDynamicFile() const; virtual void clear(); virtual void readFile(const AString& ciftiMapFileName); virtual void writeFile(const AString& filename); virtual bool isModifiedPaletteColorMapping() const; private: CiftiBrainordinateDataSeriesFile(const CiftiBrainordinateDataSeriesFile&); CiftiBrainordinateDataSeriesFile& operator=(const CiftiBrainordinateDataSeriesFile&); virtual void saveFileDataToScene(const SceneAttributes* sceneAttributes, SceneClass* sceneClass); virtual void restoreFileDataFromScene(const SceneAttributes* sceneAttributes, const SceneClass* sceneClass); public: // ADD_NEW_METHODS_HERE private: // ADD_NEW_MEMBERS_HERE void initializeDenseDynamicFile(); bool m_chartingEnabledForTab[BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS]; CiftiConnectivityMatrixDenseDynamicFile* m_lazyInitializedDenseDynamicFile; static const AString s_paletteColorMappingNameInMetaData; }; #ifdef __CIFTI_BRAINORDINATE_DATA_SERIES_FILE_DECLARE__ const AString CiftiBrainordinateDataSeriesFile::s_paletteColorMappingNameInMetaData = "__DYNAMIC_FILE_PALETTE_COLOR_MAPPING__"; // #endif // __CIFTI_BRAINORDINATE_DATA_SERIES_FILE_DECLARE__ } // namespace #endif //__CIFTI_BRAINORDINATE_DATA_SERIES_FILE_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Files/CiftiBrainordinateLabelFile.cxx000066400000000000000000000133261300200146000276370ustar00rootroot00000000000000 /*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 __CIFTI_BRAINORDINATE_LABEL_FILE_DECLARE__ #include "CiftiBrainordinateLabelFile.h" #undef __CIFTI_BRAINORDINATE_LABEL_FILE_DECLARE__ #include "CiftiFile.h" #include "CiftiLabelsMap.h" using namespace caret; /** * \class caret::CiftiBrainordinateLabelFile * \brief CIFTI Brainordinate by Label File * \ingroup Files */ /** * Constructor. */ CiftiBrainordinateLabelFile::CiftiBrainordinateLabelFile() : CiftiMappableDataFile(DataFileTypeEnum::CONNECTIVITY_DENSE_LABEL) { } /** * Destructor. */ CiftiBrainordinateLabelFile::~CiftiBrainordinateLabelFile() { } /** * For the given structure with the given number of nodes, get indices of all * nodes in the map that have the same label key (node value). * * @param structure * Structure of surface. * @param surfaceNumberOfNodes * Number of nodes in surface. * @param mapIndex * Index of the map. * @param labelKey * Desired label key. */ void CiftiBrainordinateLabelFile::getNodeIndicesWithLabelKey(const StructureEnum::Enum structure, const int32_t surfaceNumberOfNodes, const int32_t mapIndex, const int32_t labelKey, std::vector& nodeIndicesOut) const { nodeIndicesOut.clear(); std::vector dataIndices; if (getSurfaceDataIndicesForMappingToBrainordinates(structure, surfaceNumberOfNodes, dataIndices)) { std::vector mapData; getMapData(mapIndex, mapData); for (int32_t i = 0; i < surfaceNumberOfNodes; i++) { const int64_t dataIndex = dataIndices[i]; if (dataIndex >= 0) { CaretAssertVectorIndex(mapData, dataIndex); const int32_t dataKey = static_cast(mapData[dataIndex]); if (dataKey == labelKey) { nodeIndicesOut.push_back(i); } } } } } /** * Get the voxel indices of all voxels in the given map with the given label key. * * @param mapIndex * Index of map. * @param labelKey * Key of the label. * @param voxelIndicesOut * Output containing indices of voxels with the given label key. */ void CiftiBrainordinateLabelFile::getVoxelIndicesWithLabelKey(const int32_t mapIndex, const int32_t labelKey, std::vector& voxelIndicesOut) const { voxelIndicesOut.clear(); const CiftiXML& myXML = m_ciftiFile->getCiftiXML(); if (myXML.getMappingType(CiftiXML::ALONG_COLUMN) != CiftiMappingType::BRAIN_MODELS) { return; } std::vector volumeMaps = myXML.getBrainModelsMap(CiftiXML::ALONG_COLUMN).getFullVolumeMap(); std::vector mapData; getMapData(mapIndex, mapData); for (std::vector::iterator iter = volumeMaps.begin(); iter != volumeMaps.end(); iter++) { const CiftiBrainModelsMap::VolumeMap& vm = *iter; const int64_t dataOffset = vm.m_ciftiIndex; CaretAssertVectorIndex(mapData, dataOffset); const int32_t key = static_cast(mapData[dataOffset]); if (key == labelKey) { voxelIndicesOut.push_back(VoxelIJK(vm.m_ijk)); } } } /** * Get the voxel indices of all voxels in the given map with the given label key. * * @param mapIndex * Index of map. * @param labelKey * Key of the label. * @param voxelXyzOut * Output containing coordinates of voxels with the given label key. */ void CiftiBrainordinateLabelFile::getVoxelCoordinatesWithLabelKey(const int32_t mapIndex, const int32_t labelKey, std::vector& voxelXyzOut) const { voxelXyzOut.clear(); std::vector voxelIJK; getVoxelIndicesWithLabelKey(mapIndex, labelKey, voxelIJK); if (voxelIJK.empty()) { return; } const CiftiXML& myXML = m_ciftiFile->getCiftiXML(); if (myXML.getMappingType(CiftiXML::ALONG_COLUMN) != CiftiMappingType::BRAIN_MODELS) { return; } const VolumeSpace volumeSpace = myXML.getBrainModelsMap(CiftiXML::ALONG_COLUMN).getVolumeSpace(); const int64_t numVoxels = static_cast(voxelIJK.size()); for (int64_t i = 0; i < numVoxels; i++) { float xyz[3]; volumeSpace.indexToSpace(voxelIJK[i].m_ijk, xyz); voxelXyzOut.push_back(xyz[0]); voxelXyzOut.push_back(xyz[1]); voxelXyzOut.push_back(xyz[2]); } } connectome-workbench-1.2.3+git41-gc4c6c90/src/Files/CiftiBrainordinateLabelFile.h000066400000000000000000000047501300200146000272650ustar00rootroot00000000000000#ifndef __CIFTI_BRAINORDINATE_LABEL_FILE_H__ #define __CIFTI_BRAINORDINATE_LABEL_FILE_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 "CiftiMappableDataFile.h" #include "VoxelIJK.h" namespace caret { class CiftiBrainordinateLabelFile : public CiftiMappableDataFile { public: CiftiBrainordinateLabelFile(); virtual ~CiftiBrainordinateLabelFile(); void getNodeIndicesWithLabelKey(const StructureEnum::Enum structure, const int32_t surfaceNumberOfNodes, const int32_t mapIndex, const int32_t labelKey, std::vector& nodeIndicesOut) const; void getVoxelIndicesWithLabelKey(const int32_t mapIndex, const int32_t labelKey, std::vector& voxelIndicesOut) const; void getVoxelCoordinatesWithLabelKey(const int32_t mapIndex, const int32_t labelKey, std::vector& voxelXyzOut) const; private: CiftiBrainordinateLabelFile(const CiftiBrainordinateLabelFile&); CiftiBrainordinateLabelFile& operator=(const CiftiBrainordinateLabelFile&); public: // ADD_NEW_METHODS_HERE private: // ADD_NEW_MEMBERS_HERE }; #ifdef __CIFTI_BRAINORDINATE_LABEL_FILE_DECLARE__ // #endif // __CIFTI_BRAINORDINATE_LABEL_FILE_DECLARE__ } // namespace #endif //__CIFTI_BRAINORDINATE_LABEL_FILE_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Files/CiftiBrainordinateScalarFile.cxx000066400000000000000000000356521300200146000300330ustar00rootroot00000000000000 /*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 __CIFTI_BRAINORDINATE_SCALAR_FILE_DECLARE__ #include "CiftiBrainordinateScalarFile.h" #undef __CIFTI_BRAINORDINATE_SCALAR_FILE_DECLARE__ #include "CiftiMappableConnectivityMatrixDataFile.h" #include "CiftiConnectivityMatrixDenseFile.h" #include "CiftiConnectivityMatrixDenseDynamicFile.h" #include "CaretLogger.h" #include "ChartDataCartesian.h" #include "CiftiFile.h" #include "CiftiXML.h" #include "DataFileException.h" #include "FileInformation.h" #include "SceneClass.h" #include "SceneClassArray.h" using namespace caret; /** * \class caret::CiftiBrainordinateScalarFile * \brief CIFTI Brainordinate by Scalar File * \ingroup Files * */ /** * Constructor. */ CiftiBrainordinateScalarFile::CiftiBrainordinateScalarFile() : CiftiMappableDataFile(DataFileTypeEnum::CONNECTIVITY_DENSE_SCALAR) { for (int32_t i = 0; i < BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS; i++) { m_chartingEnabledForTab[i] = false; } } /** * Destructor. */ CiftiBrainordinateScalarFile::~CiftiBrainordinateScalarFile() { } /** * Create a Cifti Scalar File using the currently loaded row in a Cifti * connectivity matrix file. * * @param sourceCiftiMatrixFile * Cifti connectivity matrix file. * @param destinationDirectory * Directory in which file is placed if the input matrix file is not * in a valid local (user's file system) directory. * @param errorMessageOut * Will describe problem if there is an error. * @return * Pointer to the newly created Cifti Scalar File. If there is an error, * NULL will be returned and errorMessageOut will describe the problem. */ CiftiBrainordinateScalarFile* CiftiBrainordinateScalarFile::newInstanceFromRowInCiftiConnectivityMatrixFile(const CiftiMappableConnectivityMatrixDataFile* sourceCiftiMatrixFile, const AString& destinationDirectory, AString& errorMessageOut) { errorMessageOut.clear(); const CiftiConnectivityMatrixDenseFile* denseFile = dynamic_cast(sourceCiftiMatrixFile); const CiftiConnectivityMatrixDenseDynamicFile* dynamicDenseFile = dynamic_cast(sourceCiftiMatrixFile); if ((denseFile != NULL) || (dynamicDenseFile != NULL)) { /* ok, acceptable file type */ } else { errorMessageOut = "Only Cifti Dense Matrix Files and Dynamic Data from Data (time) Series Files are supported for conversion to Cifti Scalar Files."; return NULL; } const CiftiFile* sourceCiftiFile = sourceCiftiMatrixFile->m_ciftiFile; if (sourceCiftiMatrixFile->getNumberOfMaps() <= 0) { errorMessageOut = "No data appears to be loaded in the Cifti Matrix File (No Maps)."; return NULL; } std::vector data; sourceCiftiMatrixFile->getMapData(0, data); if (data.empty()) { errorMessageOut = "No data appears to be loaded in the Cifti Matrix File (mapData empty)."; return NULL; } CiftiBrainordinateScalarFile* scalarFile = NULL; try { CiftiFile* ciftiFile = new CiftiFile(); /* * Copy XML from matrix file * and update to be a scalar file. */ const CiftiXML& ciftiMatrixXML = sourceCiftiFile->getCiftiXML(); CiftiXML ciftiScalarXML = ciftiMatrixXML; CiftiBrainModelsMap brainModelsMap; if (denseFile != NULL) { brainModelsMap = ciftiMatrixXML.getBrainModelsMap(CiftiXML::ALONG_ROW); } else if (dynamicDenseFile != NULL) { brainModelsMap = ciftiMatrixXML.getBrainModelsMap(CiftiXML::ALONG_COLUMN); } else { const AString msg("Invalid file type for create of CIFTI Scalars File: " + sourceCiftiMatrixFile->getFileName()); CaretAssertMessage(0, msg); CaretLogSevere(msg); delete ciftiFile; return NULL; } CiftiScalarsMap scalarsMap; scalarsMap.setLength(1); scalarsMap.setMapName(0, sourceCiftiMatrixFile->getMapName(0)); ciftiScalarXML.setMap(CiftiXML::ALONG_ROW, scalarsMap); ciftiScalarXML.setMap(CiftiXML::ALONG_COLUMN, brainModelsMap); ciftiFile->setCiftiXML(ciftiScalarXML); /* * Add data to the file */ ciftiFile->setColumn(&data[0], 0); /* * Create a scalar file */ CiftiBrainordinateScalarFile* scalarFile = new CiftiBrainordinateScalarFile(); scalarFile->m_ciftiFile.grabNew(ciftiFile); /* * May need to convert a remote path to a local path */ FileInformation initialFileNameInfo(sourceCiftiMatrixFile->getFileName()); const AString scalarFileName = initialFileNameInfo.getAsLocalAbsoluteFilePath(destinationDirectory, scalarFile->getDataFileType()); /* * Create name of scalar file with row/column information */ FileInformation scalarFileInfo(scalarFileName); AString thePath, theName, theExtension; scalarFileInfo.getFileComponents(thePath, theName, theExtension); theName.append("_" + sourceCiftiMatrixFile->getRowLoadedText()); const AString newFileName = FileInformation::assembleFileComponents(thePath, theName, theExtension); scalarFile->setFileName(newFileName); scalarFile->initializeAfterReading(newFileName); /* * Need to copy color palette since it may be the default */ PaletteColorMapping* scalarPalette = scalarFile->getMapPaletteColorMapping(0); CaretAssert(scalarPalette); const PaletteColorMapping* densePalette = sourceCiftiMatrixFile->getMapPaletteColorMapping(0); CaretAssert(densePalette); scalarPalette->copy(*densePalette); scalarFile->setModified(); return scalarFile; } catch (const DataFileException& de) { if (scalarFile != NULL) { delete scalarFile; } errorMessageOut = de.whatString(); } return NULL; } /** * @return Is charting enabled for this file? */ bool CiftiBrainordinateScalarFile::isLineSeriesChartingEnabled(const int32_t tabIndex) const { CaretAssertArrayIndex(m_chartingEnabledForTab, BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS, tabIndex); return m_chartingEnabledForTab[tabIndex]; } /** * @return Return true if the file's current state supports * charting data, else false. Typically a brainordinate file * is chartable if it contains more than one map. */ bool CiftiBrainordinateScalarFile::isLineSeriesChartingSupported() const { if (getNumberOfMaps() > 1) { return true; } return false; } /** * Set charting enabled for this file. * * @param enabled * New status for charting enabled. */ void CiftiBrainordinateScalarFile::setLineSeriesChartingEnabled(const int32_t tabIndex, const bool enabled) { CaretAssertArrayIndex(m_chartingEnabledForTab, BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS, tabIndex); m_chartingEnabledForTab[tabIndex] = enabled; } /** * Get chart data types supported by the file. * * @param chartDataTypesOut * Chart types supported by this file. */ void CiftiBrainordinateScalarFile::getSupportedLineSeriesChartDataTypes(std::vector& chartDataTypesOut) const { helpGetSupportedLineSeriesChartDataTypes(chartDataTypesOut); } /** * Load charting data for the surface with the given structure and node index. * * @param structure * The surface's structure. * @param nodeIndex * Index of the node. * @return * Pointer to the chart data. If the data FAILED to load, * the returned pointer will return true. Caller takes ownership * of the pointer and must delete it when no longer needed. */ ChartDataCartesian* CiftiBrainordinateScalarFile::loadLineSeriesChartDataForSurfaceNode(const StructureEnum::Enum structure, const int32_t nodeIndex) { 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; } /** * Load average charting data for the surface with the given structure and node indices. * * @param structure * The surface's structure. * @param nodeIndices * Indices of the node. * @return * Pointer to the chart data. If the data FAILED to load, * the returned pointer will be NULL. Caller takes ownership * of the pointer and must delete it when no longer needed. */ ChartDataCartesian* CiftiBrainordinateScalarFile::loadAverageLineSeriesChartDataForSurfaceNodes(const StructureEnum::Enum structure, const std::vector& nodeIndices) { ChartDataCartesian* chartData = helpLoadChartDataForSurfaceNodeAverage(structure, nodeIndices); return chartData; } /** * Load charting data for the voxel enclosing the given coordinate. * * @param xyz * Coordinate of voxel. * @return * Pointer to the chart data. If the data FAILED to load, * the returned pointer will be NULL. Caller takes ownership * of the pointer and must delete it when no longer needed. */ ChartDataCartesian* CiftiBrainordinateScalarFile::loadLineSeriesChartDataForVoxelAtCoordinate(const float xyz[3]) { ChartDataCartesian* chartData = helpLoadChartDataForVoxelAtCoordinate(xyz); return chartData; } /** * Save file data from the scene. For subclasses that need to * save to a scene, this method should be overriden. sceneClass * will be valid and any scene data should be added to it. * * @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. */ void CiftiBrainordinateScalarFile::saveFileDataToScene(const SceneAttributes* sceneAttributes, SceneClass* sceneClass) { CiftiMappableDataFile::saveFileDataToScene(sceneAttributes, sceneClass); sceneClass->addBooleanArray("m_chartingEnabledForTab", m_chartingEnabledForTab, BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS); } /** * Restore file data from the scene. For subclasses that need to * restore from a scene, this method should be overridden. The scene class * will be valid and any scene data may be obtained from it. * * @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 CiftiBrainordinateScalarFile::restoreFileDataFromScene(const SceneAttributes* sceneAttributes, const SceneClass* sceneClass) { CiftiMappableDataFile::restoreFileDataFromScene(sceneAttributes, sceneClass); const ScenePrimitiveArray* tabArray = sceneClass->getPrimitiveArray("m_chartingEnabledForTab"); if (tabArray != NULL) { sceneClass->getBooleanArrayValue("m_chartingEnabledForTab", m_chartingEnabledForTab, BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS); } else { /* * Obsolete value when charting was not 'per tab' */ const bool chartingEnabled = sceneClass->getBooleanValue("m_chartingEnabled", false); for (int32_t i = 0; i < BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS; i++) { m_chartingEnabledForTab[i] = chartingEnabled; } } } connectome-workbench-1.2.3+git41-gc4c6c90/src/Files/CiftiBrainordinateScalarFile.h000066400000000000000000000072061300200146000274520ustar00rootroot00000000000000#ifndef __CIFTI_BRAINORDINATE_SCALAR_FILE_H__ #define __CIFTI_BRAINORDINATE_SCALAR_FILE_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 "BrainConstants.h" #include "ChartableLineSeriesBrainordinateInterface.h" #include "CiftiMappableDataFile.h" namespace caret { class CiftiMappableConnectivityMatrixDataFile; class CiftiBrainordinateScalarFile : public CiftiMappableDataFile, public ChartableLineSeriesBrainordinateInterface { public: CiftiBrainordinateScalarFile(); virtual ~CiftiBrainordinateScalarFile(); static CiftiBrainordinateScalarFile* newInstanceFromRowInCiftiConnectivityMatrixFile(const CiftiMappableConnectivityMatrixDataFile* ciftiMatrixFile, const AString& destinationDirectory, AString& errorMessageOut); virtual bool isLineSeriesChartingEnabled(const int32_t tabIndex) const; virtual void setLineSeriesChartingEnabled(const int32_t tabIndex, const bool enabled); virtual bool isLineSeriesChartingSupported() const; virtual ChartDataCartesian* loadLineSeriesChartDataForSurfaceNode(const StructureEnum::Enum structure, const int32_t nodeIndex); virtual ChartDataCartesian* loadAverageLineSeriesChartDataForSurfaceNodes(const StructureEnum::Enum structure, const std::vector& nodeIndices); virtual ChartDataCartesian* loadLineSeriesChartDataForVoxelAtCoordinate(const float xyz[3]); virtual void getSupportedLineSeriesChartDataTypes(std::vector& chartDataTypesOut) const; private: CiftiBrainordinateScalarFile(const CiftiBrainordinateScalarFile&); CiftiBrainordinateScalarFile& operator=(const CiftiBrainordinateScalarFile&); virtual void saveFileDataToScene(const SceneAttributes* sceneAttributes, SceneClass* sceneClass); virtual void restoreFileDataFromScene(const SceneAttributes* sceneAttributes, const SceneClass* sceneClass); public: // ADD_NEW_METHODS_HERE private: bool m_chartingEnabledForTab[BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS]; // ADD_NEW_MEMBERS_HERE }; #ifdef __CIFTI_BRAINORDINATE_SCALAR_FILE_DECLARE__ // #endif // __CIFTI_BRAINORDINATE_SCALAR_FILE_DECLARE__ } // namespace #endif //__CIFTI_BRAINORDINATE_SCALAR_FILE_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Files/CiftiConnectivityMatrixDenseDynamicFile.cxx000066400000000000000000000466571300200146000322620ustar00rootroot00000000000000 /*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 #define __CIFTI_CONNECTIVITY_MATRIX_DENSE_DYNAMIC_FILE_DECLARE__ #include "CiftiConnectivityMatrixDenseDynamicFile.h" #undef __CIFTI_CONNECTIVITY_MATRIX_DENSE_DYNAMIC_FILE_DECLARE__ #include "CaretAssert.h" #include "CaretLogger.h" #include "CaretOMP.h" #include "CiftiBrainordinateDataSeriesFile.h" #include "CiftiFile.h" #include "FileInformation.h" #include "SceneClassAssistant.h" #include "dot_wrapper.h" using namespace caret; /** * \class caret::CiftiConnectivityMatrixDenseDynamicFile * \brief Connectivity Dynamic Dense x Dense File version of data-series * \ingroup Files * * Contains dynamic connectivity from brainordinates to brainordinates. * Internally, the file format is the same as a data series file. When * a row is requested, the row is correlated with all other rows * producing the connectivity from that row to all other rows. */ /** * Constructor. * * @param parentDataSeriesFile * Parent data series file. */ CiftiConnectivityMatrixDenseDynamicFile::CiftiConnectivityMatrixDenseDynamicFile(CiftiBrainordinateDataSeriesFile* parentDataSeriesFile) : CiftiMappableConnectivityMatrixDataFile(DataFileTypeEnum::CONNECTIVITY_DENSE_DYNAMIC), m_parentDataSeriesFile(parentDataSeriesFile), m_parentDataSeriesCiftiFile(NULL), m_numberOfBrainordinates(-1), m_numberOfTimePoints(-1), m_validDataFlag(false), m_enabledAsLayer(true), m_cacheDataFlag(false) { CaretAssert(m_parentDataSeriesFile); m_sceneAssistant.grabNew(new SceneClassAssistant()); m_sceneAssistant->add("m_enabledAsLayer", &m_enabledAsLayer); } /** * Destructor. */ CiftiConnectivityMatrixDenseDynamicFile::~CiftiConnectivityMatrixDenseDynamicFile() { } /** * @return The parent brainordinate data-series file (const method) */ const CiftiBrainordinateDataSeriesFile* CiftiConnectivityMatrixDenseDynamicFile::getParentBrainordinateDataSeriesFile() const { return m_parentDataSeriesFile; } /** * @return The parent brainordinate data-series file. */ CiftiBrainordinateDataSeriesFile* CiftiConnectivityMatrixDenseDynamicFile::getParentBrainordinateDataSeriesFile() { return m_parentDataSeriesFile; } /** * @return True if enabled as a layer. */ bool CiftiConnectivityMatrixDenseDynamicFile::isEnabledAsLayer() const { return m_enabledAsLayer; } /** * Set enabled as a layer. * * @param True if enabled as a layer. */ void CiftiConnectivityMatrixDenseDynamicFile::setEnabledAsLayer(const bool enabled) { m_enabledAsLayer = enabled; } /** * @return True if this file type supports writing, else false. * * Dense files do NOT support writing. */ bool CiftiConnectivityMatrixDenseDynamicFile::supportsWriting() const { return false; } /** * @return Is the data within the file valid? */ bool CiftiConnectivityMatrixDenseDynamicFile::isDataValid() const { return m_validDataFlag; } /** * Update the content of this dense dynamic file after the parent * brainordinate data series file is successfully read. * * @param ciftiFile * Parent's CIFTI file.. */ void CiftiConnectivityMatrixDenseDynamicFile::updateAfterReading(const CiftiFile* ciftiFile) { m_validDataFlag = false; m_parentDataSeriesCiftiFile = const_cast(ciftiFile); AString path, nameNoExt, ext; FileInformation fileInfo(m_parentDataSeriesCiftiFile->getFileName()); fileInfo.getFileComponents(path, nameNoExt, ext); setFileName(FileInformation::assembleFileComponents(path, nameNoExt, DataFileTypeEnum::toFileExtension(DataFileTypeEnum::CONNECTIVITY_DENSE_DYNAMIC))); /* * Need dimensions of data * Note that CIFTI XML in this file is identifical to CIFTI XML in parent data-series file */ const CiftiXML& ciftiXML = getCiftiFile()->getCiftiXML(); m_numberOfBrainordinates = ciftiXML.getBrainModelsMap(CiftiXML::ALONG_COLUMN).getLength(); m_numberOfTimePoints = ciftiXML.getSeriesMap(CiftiXML::ALONG_ROW).getLength(); m_rowData.clear(); if ((m_numberOfBrainordinates > 0) && (m_numberOfTimePoints > 0)) { m_rowData.resize(m_numberOfBrainordinates); if (m_cacheDataFlag) { /* * Read all of the data. Time-series type files are not * too large and by caching the data, it eliminates * numerous calls to read the data when correlation * is performed. * * READ DATA FROM PARENT FILE */ for (int32_t i = 0; i < m_numberOfBrainordinates; i++) { CaretAssertVectorIndex(m_rowData, i); m_rowData[i].m_data.resize(m_numberOfTimePoints); m_parentDataSeriesCiftiFile->getRow(&m_rowData[i].m_data[0], i); } } preComputeRowMeanAndSumSquared(); m_validDataFlag = true; } } /** * Load data for the given column. * * @param dataOut * Output with data. * @param index * Index of the column. */ void CiftiConnectivityMatrixDenseDynamicFile::getDataForColumn(float* /*dataOut*/, const int64_t& /*index*/) const { const AString msg("Should never be called for Dense Dynamic File"); CaretAssertMessage(0, msg); CaretLogSevere(msg); } /** * Load data for the given row. * * @param dataOut * Output with data. * @param index * Index of the row. */ void CiftiConnectivityMatrixDenseDynamicFile::getDataForRow(float* dataOut, const int64_t& index) const { m_parentDataSeriesCiftiFile->getRow(dataOut, index); } /** * Load PROCESSED data for the given column. * * Some file types may have special processing for a column. This method can be * overridden for those types of files. * * @param dataOut * Output with data. * @param index * Index of the column. */ void CiftiConnectivityMatrixDenseDynamicFile::getProcessedDataForColumn(float* /*dataOut*/, const int64_t& /*index*/) const { const AString msg("Should never be called for Dense Dynamic File"); CaretAssertMessage(0, msg); CaretLogSevere(msg); } /** * Load PROCESSED data for the given row. * * Some file types may have special processing for a row. This method can be * overridden for those types of files. * * @param dataOut * Output with data. * @param index * Index of the row. */ void CiftiConnectivityMatrixDenseDynamicFile::getProcessedDataForRow(float* dataOut, const int64_t& index) const { if ((m_numberOfBrainordinates <= 0) || (m_numberOfTimePoints <= 0)) { return; } std::vector rowData(m_numberOfTimePoints); m_parentDataSeriesCiftiFile->getRow(&rowData[0], index); const float mean = m_rowData[index].m_mean; const float ssxx = m_rowData[index].m_sqrt_ssxx; /* * TSC: hyperthreading means some cores end up "faster" than others, so "static" scheduling is generally not as fast * there is almost no overhead to dynamic scheduling */ #pragma omp CARET_PARFOR schedule(dynamic) for (int32_t iRow = 0; iRow < m_numberOfBrainordinates; iRow++) { float coefficient = 1.0; if (iRow != index) { coefficient = correlation(rowData, mean, ssxx, iRow, m_numberOfTimePoints); } dataOut[iRow] = coefficient; } } /** * Some file types may perform additional processing of row average data and * can override this method. * * @param rowAverageDataInOut * The row average data. */ void CiftiConnectivityMatrixDenseDynamicFile::processRowAverageData(std::vector& rowAverageDataInOut) { if ((m_numberOfBrainordinates <= 0) || (m_numberOfTimePoints <= 0)) { return; } const int32_t dataLength = static_cast(rowAverageDataInOut.size()); if (dataLength != m_numberOfTimePoints) { CaretLogWarning("Data length incorrect. Is " + AString::number(dataLength) + " but should be " + AString::number(m_numberOfTimePoints)); return; } if (dataLength <= 0) { return; } float mean = 0.0; float sumSquared = 0.0; computeDataMeanAndSumSquared(&rowAverageDataInOut[0], dataLength, mean, sumSquared); std::vector processedRowAverageData(m_numberOfBrainordinates); /* * TSC: hyperthreading means some cores end up "faster" than others, so "static" scheduling is generally not as fast * there is almost no overhead to dynamic scheduling */ #pragma omp CARET_PARFOR schedule(dynamic) for (int32_t iRow = 0; iRow < m_numberOfBrainordinates; iRow++) { const float coefficient = correlation(rowAverageDataInOut, mean, sumSquared, iRow, dataLength); CaretAssertVectorIndex(processedRowAverageData, iRow); processedRowAverageData[iRow] = coefficient; } rowAverageDataInOut = processedRowAverageData; } /** * Compute the mean and sum-squared for each row so that they * are only calculated once. */ void CiftiConnectivityMatrixDenseDynamicFile::preComputeRowMeanAndSumSquared() { CaretAssert(m_numberOfBrainordinates > 0); CaretAssert(m_numberOfTimePoints > 0); /* * TSC: hyperthreading means some cores end up "faster" than others, so "static" scheduling is generally not as fast * there is almost no overhead to dynamic scheduling */ #pragma omp CARET_PARFOR schedule(dynamic) for (int32_t iRow = 0; iRow < m_numberOfBrainordinates; iRow++) { CaretAssertVectorIndex(m_rowData, iRow); if (m_cacheDataFlag) { CaretAssertVectorIndex(m_rowData[iRow].m_data, (m_numberOfTimePoints - 1)); computeDataMeanAndSumSquared(&m_rowData[iRow].m_data[0], m_numberOfTimePoints, m_rowData[iRow].m_mean, m_rowData[iRow].m_sqrt_ssxx); } else { std::vector data(m_numberOfTimePoints); #pragma omp critical {//TSC: this can do disk access, which is not currently thread-safe m_parentDataSeriesCiftiFile->getRow(&data[0], iRow); } computeDataMeanAndSumSquared(&data[0], m_numberOfTimePoints, m_rowData[iRow].m_mean, m_rowData[iRow].m_sqrt_ssxx); } // double sum = 0.0; // double sumSquared = 0.0; // if (m_cacheDataFlag) { // for (int32_t iPoint = 0; iPoint < m_numberOfTimePoints; iPoint++) { // CaretAssertVectorIndex(m_rowData[iRow].m_data, iPoint); // const float d = m_rowData[iRow].m_data[iPoint]; // sum += d; // sumSquared += (d * d); // } // } // else { // std::vector data(m_numberOfTimePoints); // m_parentDataSeriesCiftiFile->getRow(&data[0], iRow); // for (int32_t iPoint = 0; iPoint < m_numberOfTimePoints; iPoint++) { // CaretAssertVectorIndex(data, iPoint); // const float d = data[iPoint]; // sum += d; // sumSquared += (d * d); // } // } // // const float mean = (sum / numPointsFloat); // const float ssxx = (sumSquared - (numPointsFloat * mean * mean)); // CaretAssert(ssxx >= 0.0); // // const float meanDiff = std::fabs(mean - m_rowData[iRow].m_mean); // const float ssDiff = std::fabs(std::sqrt(ssxx) - m_rowData[iRow].m_sqrt_ssxx); // if ((meanDiff > 0.0001) || (ssDiff > 0.0001)) { // std::cout << "Mean/SS diff" << std::endl; // } // // m_rowData[iRow].m_mean = mean; // m_rowData[iRow].m_sqrt_ssxx = std::sqrt(ssxx); } } /** * Compute data's mean and sum-squared * * @param data * Data on which mean and sum-squared are calculated * @param dataLength * Number of items in data. * @param meanOut * Output with mean of data. * @param sumSquaredOut * Output with sum-squared. */ void CiftiConnectivityMatrixDenseDynamicFile::computeDataMeanAndSumSquared(const float* data, const int32_t dataLength, float& meanOut, float& sumSquaredOut) const { meanOut = 0.0; sumSquaredOut = 0.0; if (dataLength <= 0) { return; } double sum = 0.0; double sumSquared = 0.0; for (int32_t i = 0; i < dataLength; i++) { const float d = data[i]; sum += d; sumSquared += (d * d); } meanOut = (sum / dataLength); const float ssxx = (sumSquared - (dataLength * meanOut * meanOut)); //TSC: do not assert things that depend on input file content (a NaN in the data will trip it), you could print a warning instead //CaretAssert(ssxx >= 0.0); sumSquaredOut = std::sqrt(ssxx); } /** * Correlation from https://en.wikipedia.org/wiki/Pearson_product-moment_correlation_coefficient * * @param data * Data for correlation * @param mean * Mean of data * @param sumSquared * Sum squared of data. * @param otherRowIndex * Index of another row * @param numberOfPoints * Number of points int the two arrays * @return * The correlation coefficient computed on the two arrays. */ float CiftiConnectivityMatrixDenseDynamicFile::correlation(const std::vector& data, const float mean, const float sumSquared, const int32_t otherRowIndex, const int32_t numberOfPoints) const { const double numFloat = numberOfPoints; double xySum = 0.0; CaretAssertVectorIndex(m_rowData, otherRowIndex); const RowData& otherData = m_rowData[otherRowIndex]; if (m_cacheDataFlag) { xySum = sddot(&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); } const double ssxy = xySum - (numFloat * mean * otherData.m_mean); float correlationCoefficient = 0.0; if ((sumSquared > 0.0) && (otherData.m_sqrt_ssxx > 0.0)) { correlationCoefficient = (ssxy / (sumSquared * otherData.m_sqrt_ssxx)); } return correlationCoefficient; } /** * Correlation from https://en.wikipedia.org/wiki/Pearson_product-moment_correlation_coefficient * * @param rowIndex * Index of a row * @param otherRowIndex * Index of another row * @param numberOfPoints * Number of points int the two arrays * @return * The correlation coefficient computed on the two arrays. */ float CiftiConnectivityMatrixDenseDynamicFile::correlation(const int32_t rowIndex, const int32_t otherRowIndex, const int32_t numberOfPoints) const { const double numFloat = numberOfPoints; double xySum = 0.0; CaretAssertVectorIndex(m_rowData, rowIndex); CaretAssertVectorIndex(m_rowData, otherRowIndex); const RowData& data = m_rowData[rowIndex]; const RowData& otherData = m_rowData[otherRowIndex]; if (m_cacheDataFlag) { for (int i = 0; i < numberOfPoints; i++) { CaretAssertVectorIndex(data.m_data, i); CaretAssertVectorIndex(otherData.m_data, i); xySum += data.m_data[i] * otherData.m_data[i]; } } else { std::vector dataVector(m_numberOfTimePoints); std::vector otherDataVector(m_numberOfTimePoints); m_parentDataSeriesCiftiFile->getRow(&dataVector[0], rowIndex); m_parentDataSeriesCiftiFile->getRow(&otherDataVector[0], otherRowIndex); for (int i = 0; i < numberOfPoints; i++) { CaretAssertVectorIndex(dataVector, i); CaretAssertVectorIndex(otherDataVector, i); xySum += dataVector[i] * otherDataVector[i]; } } const double ssxy = xySum - (numFloat * data.m_mean * otherData.m_mean); float correlationCoefficient = 0.0; if ((data.m_sqrt_ssxx > 0.0) && (otherData.m_sqrt_ssxx > 0.0)) { correlationCoefficient = (ssxy / (data.m_sqrt_ssxx * otherData.m_sqrt_ssxx)); } return correlationCoefficient; } /** * 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 CiftiConnectivityMatrixDenseDynamicFile::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 CiftiConnectivityMatrixDenseDynamicFile::restoreSubClassDataFromScene(const SceneAttributes* sceneAttributes, const SceneClass* sceneClass) { m_sceneAssistant->restoreMembers(sceneAttributes, sceneClass); } connectome-workbench-1.2.3+git41-gc4c6c90/src/Files/CiftiConnectivityMatrixDenseDynamicFile.h000066400000000000000000000112421300200146000316650ustar00rootroot00000000000000#ifndef __CIFTI_CONNECTIVITY_MATRIX_DENSE_DYNAMIC_FILE_H__ #define __CIFTI_CONNECTIVITY_MATRIX_DENSE_DYNAMIC_FILE_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 "CaretPointer.h" #include "CiftiMappableConnectivityMatrixDataFile.h" namespace caret { class CiftiBrainordinateDataSeriesFile; class SceneClassAssistant; class CiftiConnectivityMatrixDenseDynamicFile : public CiftiMappableConnectivityMatrixDataFile { public: CiftiConnectivityMatrixDenseDynamicFile(CiftiBrainordinateDataSeriesFile* parentDataSeriesFile); virtual ~CiftiConnectivityMatrixDenseDynamicFile(); bool isDataValid() const; bool isEnabledAsLayer() const; void setEnabledAsLayer(const bool enabled); virtual bool supportsWriting() const; void updateAfterReading(const CiftiFile* ciftiFile); CiftiBrainordinateDataSeriesFile* getParentBrainordinateDataSeriesFile(); const CiftiBrainordinateDataSeriesFile* getParentBrainordinateDataSeriesFile() const; private: CiftiConnectivityMatrixDenseDynamicFile(const CiftiConnectivityMatrixDenseDynamicFile&); CiftiConnectivityMatrixDenseDynamicFile& operator=(const CiftiConnectivityMatrixDenseDynamicFile&); protected: virtual void getDataForColumn(float* dataOut, const int64_t& index) const; virtual void getDataForRow(float* dataOut, const int64_t& index) const; virtual void getProcessedDataForColumn(float* dataOut, const int64_t& index) const; virtual void getProcessedDataForRow(float* dataOut, const int64_t& index) const; virtual void processRowAverageData(std::vector& rowAverageData); virtual void saveSubClassDataToScene(const SceneAttributes* sceneAttributes, SceneClass* sceneClass); virtual void restoreSubClassDataFromScene(const SceneAttributes* sceneAttributes, const SceneClass* sceneClass); private: class RowData { public: RowData() { } ~RowData() { } std::vector m_data; float m_mean; float m_sqrt_ssxx; }; float correlation(const int32_t rowIndex, const int32_t otherRowIndex, const int32_t numberOfPoints) const; float correlation(const std::vector& data, const float mean, const float sumSquared, const int32_t otherRowIndex, const int32_t numberOfPoints) const; void preComputeRowMeanAndSumSquared(); void computeDataMeanAndSumSquared(const float* data, const int32_t dataLength, float& meanOut, float& sumSquaredOut) const; CiftiBrainordinateDataSeriesFile* m_parentDataSeriesFile; CiftiFile* m_parentDataSeriesCiftiFile; int32_t m_numberOfBrainordinates; int32_t m_numberOfTimePoints; std::vector m_rowData; bool m_validDataFlag; bool m_enabledAsLayer; const bool m_cacheDataFlag; CaretPointer m_sceneAssistant; // ADD_NEW_MEMBERS_HERE }; #ifdef __CIFTI_CONNECTIVITY_MATRIX_DENSE_DYNAMIC_FILE_DECLARE__ // #endif // __CIFTI_CONNECTIVITY_MATRIX_DENSE_DYNAMIC_FILE_DECLARE__ } // namespace #endif //__CIFTI_CONNECTIVITY_MATRIX_DENSE_DYNAMIC_FILE_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Files/CiftiConnectivityMatrixDenseFile.cxx000066400000000000000000000033141300200146000307340ustar00rootroot00000000000000 /*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 __CIFTI_CONNECTIVITY_MATRIX_DENSE_FILE_DECLARE__ #include "CiftiConnectivityMatrixDenseFile.h" #undef __CIFTI_CONNECTIVITY_MATRIX_DENSE_FILE_DECLARE__ using namespace caret; /** * \class caret::CiftiConnectivityMatrixDenseFile * \brief Connectivity Dense x Dense File * \ingroup Files * * Contains connectivity matrix that measures connectivity from brainordinates * to brainordinates. */ /** * Constructor. */ CiftiConnectivityMatrixDenseFile::CiftiConnectivityMatrixDenseFile() : CiftiMappableConnectivityMatrixDataFile(DataFileTypeEnum::CONNECTIVITY_DENSE) { } /** * Destructor. */ CiftiConnectivityMatrixDenseFile::~CiftiConnectivityMatrixDenseFile() { } /** * @return True if this file type supports writing, else false. * * Dense files do NOT support writing. */ bool CiftiConnectivityMatrixDenseFile::supportsWriting() const { return false; } connectome-workbench-1.2.3+git41-gc4c6c90/src/Files/CiftiConnectivityMatrixDenseFile.h000066400000000000000000000034601300200146000303630ustar00rootroot00000000000000#ifndef __CIFTI_CONNECTIVITY_MATRIX_DENSE_FILE_H__ #define __CIFTI_CONNECTIVITY_MATRIX_DENSE_FILE_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 "CiftiMappableConnectivityMatrixDataFile.h" namespace caret { class CiftiConnectivityMatrixDenseFile : public CiftiMappableConnectivityMatrixDataFile { public: CiftiConnectivityMatrixDenseFile(); virtual ~CiftiConnectivityMatrixDenseFile(); virtual bool supportsWriting() const; private: CiftiConnectivityMatrixDenseFile(const CiftiConnectivityMatrixDenseFile&); CiftiConnectivityMatrixDenseFile& operator=(const CiftiConnectivityMatrixDenseFile&); public: // ADD_NEW_METHODS_HERE private: // ADD_NEW_MEMBERS_HERE }; #ifdef __CIFTI_CONNECTIVITY_MATRIX_DENSE_FILE_DECLARE__ // #endif // __CIFTI_CONNECTIVITY_MATRIX_DENSE_FILE_DECLARE__ } // namespace #endif //__CIFTI_CONNECTIVITY_MATRIX_DENSE_FILE_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Files/CiftiConnectivityMatrixDenseParcelFile.cxx000066400000000000000000000030661300200146000320670ustar00rootroot00000000000000 /*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 __CIFTI_CONNECTIVITY_MATRIX_DENSE_PARCEL_FILE_DECLARE__ #include "CiftiConnectivityMatrixDenseParcelFile.h" #undef __CIFTI_CONNECTIVITY_MATRIX_DENSE_PARCEL_FILE_DECLARE__ using namespace caret; /** * \class caret::CiftiConnectivityMatrixDenseParcelFile * \brief Connectivity Dense x Parcel File * \ingroup Files * * Contains connectivity matrix that measures connectivity from brainordinates * to parcels. */ /** * Constructor. */ CiftiConnectivityMatrixDenseParcelFile::CiftiConnectivityMatrixDenseParcelFile() : CiftiMappableConnectivityMatrixDataFile(DataFileTypeEnum::CONNECTIVITY_DENSE_PARCEL) { } /** * Destructor. */ CiftiConnectivityMatrixDenseParcelFile::~CiftiConnectivityMatrixDenseParcelFile() { } connectome-workbench-1.2.3+git41-gc4c6c90/src/Files/CiftiConnectivityMatrixDenseParcelFile.h000066400000000000000000000035061300200146000315130ustar00rootroot00000000000000#ifndef __CIFTI_CONNECTIVITY_MATRIX_DENSE_PARCEL_FILE_H__ #define __CIFTI_CONNECTIVITY_MATRIX_DENSE_PARCEL_FILE_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 "CiftiMappableConnectivityMatrixDataFile.h" namespace caret { class CiftiConnectivityMatrixDenseParcelFile : public CiftiMappableConnectivityMatrixDataFile { public: CiftiConnectivityMatrixDenseParcelFile(); virtual ~CiftiConnectivityMatrixDenseParcelFile(); private: CiftiConnectivityMatrixDenseParcelFile(const CiftiConnectivityMatrixDenseParcelFile&); CiftiConnectivityMatrixDenseParcelFile& operator=(const CiftiConnectivityMatrixDenseParcelFile&); public: // ADD_NEW_METHODS_HERE private: // ADD_NEW_MEMBERS_HERE }; #ifdef __CIFTI_CONNECTIVITY_MATRIX_DENSE_PARCEL_FILE_DECLARE__ // #endif // __CIFTI_CONNECTIVITY_MATRIX_DENSE_PARCEL_FILE_DECLARE__ } // namespace #endif //__CIFTI_CONNECTIVITY_MATRIX_DENSE_PARCEL_FILE_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Files/CiftiConnectivityMatrixParcelDenseFile.cxx000066400000000000000000000030661300200146000320670ustar00rootroot00000000000000 /*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 __CIFTI_CONNECTIVITY_MATRIX_PARCEL_DENSE_FILE_DECLARE__ #include "CiftiConnectivityMatrixParcelDenseFile.h" #undef __CIFTI_CONNECTIVITY_MATRIX_PARCEL_DENSE_FILE_DECLARE__ using namespace caret; /** * \class caret::CiftiConnectivityMatrixParcelDenseFile * \brief Connectivity Parcel x Dense File * \ingroup Files * * Contains connectivity matrix that measures connectivity from parcels * to brainordinates. */ /** * Constructor. */ CiftiConnectivityMatrixParcelDenseFile::CiftiConnectivityMatrixParcelDenseFile() : CiftiMappableConnectivityMatrixDataFile(DataFileTypeEnum::CONNECTIVITY_PARCEL_DENSE) { } /** * Destructor. */ CiftiConnectivityMatrixParcelDenseFile::~CiftiConnectivityMatrixParcelDenseFile() { } connectome-workbench-1.2.3+git41-gc4c6c90/src/Files/CiftiConnectivityMatrixParcelDenseFile.h000066400000000000000000000035061300200146000315130ustar00rootroot00000000000000#ifndef __CIFTI_CONNECTIVITY_MATRIX_PARCEL_DENSE_FILE_H__ #define __CIFTI_CONNECTIVITY_MATRIX_PARCEL_DENSE_FILE_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 "CiftiMappableConnectivityMatrixDataFile.h" namespace caret { class CiftiConnectivityMatrixParcelDenseFile : public CiftiMappableConnectivityMatrixDataFile { public: CiftiConnectivityMatrixParcelDenseFile(); virtual ~CiftiConnectivityMatrixParcelDenseFile(); private: CiftiConnectivityMatrixParcelDenseFile(const CiftiConnectivityMatrixParcelDenseFile&); CiftiConnectivityMatrixParcelDenseFile& operator=(const CiftiConnectivityMatrixParcelDenseFile&); public: // ADD_NEW_METHODS_HERE private: // ADD_NEW_MEMBERS_HERE }; #ifdef __CIFTI_CONNECTIVITY_MATRIX_PARCEL_DENSE_FILE_DECLARE__ // #endif // __CIFTI_CONNECTIVITY_MATRIX_PARCEL_DENSE_FILE_DECLARE__ } // namespace #endif //__CIFTI_CONNECTIVITY_MATRIX_PARCEL_DENSE_FILE_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Files/CiftiConnectivityMatrixParcelFile.cxx000066400000000000000000000730631300200146000311140ustar00rootroot00000000000000 /*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 __CIFTI_CONNECTIVITY_MATRIX_PARCEL_FILE_DECLARE__ #include "CiftiConnectivityMatrixParcelFile.h" #undef __CIFTI_CONNECTIVITY_MATRIX_PARCEL_FILE_DECLARE__ #include "CaretLogger.h" #include "ChartMatrixDisplayProperties.h" #include "CiftiFile.h" #include "CiftiParcelReordering.h" #include "CiftiParcelReorderingModel.h" #include "ConnectivityDataLoaded.h" #include "EventChartMatrixParcelYokingValidation.h" #include "EventManager.h" #include "FastStatistics.h" #include "NodeAndVoxelColoring.h" #include "Palette.h" #include "PaletteColorMapping.h" #include "PaletteFile.h" #include "SceneAttributes.h" #include "SceneClass.h" #include "SceneClassArray.h" #include "SceneClassAssistant.h" using namespace caret; /** * \class caret::CiftiConnectivityMatrixParcelFile * \brief Connectivity Parcel x Parcel File * \ingroup Files * * Contains connectivity matrix that measures connectivity from parcels * to parcels. */ /** * Constructor. */ CiftiConnectivityMatrixParcelFile::CiftiConnectivityMatrixParcelFile() : CiftiMappableConnectivityMatrixDataFile(DataFileTypeEnum::CONNECTIVITY_PARCEL) { for (int32_t i = 0; i < BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS; i++) { m_chartingEnabledForTab[i] = false; m_chartMatrixDisplayProperties[i] = new ChartMatrixDisplayProperties(); } m_selectedParcelColoringMode = CiftiParcelColoringModeEnum::CIFTI_PARCEL_COLORING_OUTLINE; m_selectedParcelColor = CaretColorEnum::WHITE; m_parcelReorderingModel = new CiftiParcelReorderingModel(this); m_chartLoadingYokingGroup = YokingGroupEnum::YOKING_GROUP_OFF; m_sceneAssistant = new SceneClassAssistant(); m_sceneAssistant->add("m_selectedParcelColoringMode", &m_selectedParcelColoringMode); m_sceneAssistant->add("m_selectedParcelColor", &m_selectedParcelColor); m_sceneAssistant->add("m_parcelReorderingModel", "CiftiParcelReorderingModel", m_parcelReorderingModel); m_sceneAssistant->add("m_chartLoadingYokingGroup", &m_chartLoadingYokingGroup); EventManager::get()->addEventListener(this, EventTypeEnum::EVENT_CHART_MATRIX_YOKING_VALIDATION); } /** * Destructor. */ CiftiConnectivityMatrixParcelFile::~CiftiConnectivityMatrixParcelFile() { EventManager::get()->removeAllEventsFromListener(this); for (int32_t i = 0; i < BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS; i++) { delete m_chartMatrixDisplayProperties[i]; } delete m_parcelReorderingModel; delete m_sceneAssistant; } /** * Receive an event. * * @param event * The event that the receive can respond to. */ void CiftiConnectivityMatrixParcelFile::receiveEvent(Event* event) { if (event->getEventType() == EventTypeEnum::EVENT_CHART_MATRIX_YOKING_VALIDATION) { EventChartMatrixParcelYokingValidation* yokeEvent = dynamic_cast(event); CaretAssert(yokeEvent); if (yokeEvent->getChartableMatrixParcelInterface() != this) { switch (yokeEvent->getMode()) { case EventChartMatrixParcelYokingValidation::MODE_APPLY_YOKING: { // YokingGroupEnum::Enum yokingGroup = YokingGroupEnum::YOKING_GROUP_OFF; // int32_t rowOrColumnIndex = -1; // yokeEvent->getApplyYokingSelections(yokingGroup, // rowOrColumnIndex); // if ((yokingGroup != YokingGroupEnum::YOKING_GROUP_OFF) // && (rowOrColumnIndex >= 0)) { // int32_t numRows = -1; // int32_t numCols = -1; // getMatrixDimensions(numRows, // numCols); // switch (getMatrixLoadingDimension()) { // case ChartMatrixLoadingDimensionEnum::CHART_MATRIX_LOADING_BY_COLUMN: // if (rowOrColumnIndex < numCols) { // loadDataForColumnIndex(rowOrColumnIndex); // } // else { // } // break; // case ChartMatrixLoadingDimensionEnum::CHART_MATRIX_LOADING_BY_ROW: // if (rowOrColumnIndex < numRows) { // loadDataForRowIndex(rowOrColumnIndex); // } // break; // } // } } break; case EventChartMatrixParcelYokingValidation::MODE_VALIDATE_YOKING: { const ConnectivityDataLoaded* connData = getConnectivityDataLoaded(); int64_t rowIndex = -1; int64_t columnIndex = -1; connData->getRowColumnLoading(rowIndex, columnIndex); int64_t selectedRowColumnIndex = -1; if (rowIndex >= 0) { selectedRowColumnIndex = rowIndex; } else if (columnIndex >= 0) { selectedRowColumnIndex = columnIndex; } yokeEvent->addValidateYokingChartableInterface(this, selectedRowColumnIndex); } break; } } } } /** * Get the matrix dimensions. * * @param numberOfRowsOut * Number of rows in the matrix. * @param numberOfColumnsOut * Number of columns in the matrix. */ void CiftiConnectivityMatrixParcelFile::getMatrixDimensions(int32_t& numberOfRowsOut, int32_t& numberOfColumnsOut) const { 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 CiftiConnectivityMatrixParcelFile::getMatrixDataRGBA(int32_t& numberOfRowsOut, int32_t& numberOfColumnsOut, std::vector& rgbaOut) const { CiftiParcelLabelFile* parcelLabelFile = NULL; int32_t parcelLabelFileMapIndex = -1; bool enabled = false; std::vector parcelLabelFiles; getSelectedParcelLabelFileAndMapForReordering(parcelLabelFiles, parcelLabelFile, parcelLabelFileMapIndex, enabled); std::vector rowIndices; if (enabled) { const CiftiParcelReordering* parcelReordering = getParcelReordering(parcelLabelFile, parcelLabelFileMapIndex); if (parcelReordering != NULL) { rowIndices = parcelReordering->getReorderedParcelIndices(); } } return helpMatrixFileLoadChartDataMatrixRGBA(numberOfRowsOut, numberOfColumnsOut, rowIndices, rgbaOut); } /** * Get the value, row name, and column name for a cell in the matrix. * * @param rowIndex * The row index. * @param columnIndex * The column index. * @param cellValueOut * Output containing value in the cell. * @param rowNameOut * Name of row corresponding to row index. * @param columnNameOut * Name of column corresponding to column index. * @return * True if the output values are valid (valid row/column indices). */ bool CiftiConnectivityMatrixParcelFile::getMatrixCellAttributes(const int32_t rowIndex, const int32_t columnIndex, AString& cellValueOut, AString& rowNameOut, AString& columnNameOut) const { if ((rowIndex >= 0) && (rowIndex < m_ciftiFile->getNumberOfRows()) && (columnIndex >= 0) && (columnIndex < m_ciftiFile->getNumberOfColumns())) { const CiftiXML& xml = m_ciftiFile->getCiftiXML(); const std::vector& rowsParcelsMap = xml.getParcelsMap(CiftiXML::ALONG_COLUMN).getParcels(); CaretAssertVectorIndex(rowsParcelsMap, rowIndex); rowNameOut = rowsParcelsMap[rowIndex].m_name; const std::vector& columnsParcelsMap = xml.getParcelsMap(CiftiXML::ALONG_ROW).getParcels(); CaretAssertVectorIndex(columnsParcelsMap, columnIndex); columnNameOut = columnsParcelsMap[columnIndex].m_name; const int32_t numberOfElementsInRow = m_ciftiFile->getNumberOfColumns(); std::vector rowData(numberOfElementsInRow); m_ciftiFile->getRow(&rowData[0], rowIndex); CaretAssertVectorIndex(rowData, columnIndex); cellValueOut = AString::number(rowData[columnIndex], 'f', 6); return true; } return false; } /** * @return Is charting enabled for this file? */ bool CiftiConnectivityMatrixParcelFile::isMatrixChartingEnabled(const int32_t tabIndex) const { CaretAssertArrayIndex(m_chartingEnabledForTab, BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS, tabIndex); return m_chartingEnabledForTab[tabIndex]; } /** * @return Return true if the file's current state supports * charting data, else false. Typically a brainordinate file * is chartable if it contains more than one map. */ bool CiftiConnectivityMatrixParcelFile::isMatrixChartingSupported() const { return true; } /** * Set charting enabled for this file. * * @param enabled * New status for charting enabled. */ void CiftiConnectivityMatrixParcelFile::setMatrixChartingEnabled(const int32_t tabIndex, const bool enabled) { CaretAssertArrayIndex(m_chartingEnabledForTab, BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS, tabIndex); m_chartingEnabledForTab[tabIndex] = enabled; } /** * Get chart data types supported by the file. * * @param chartDataTypesOut * Chart types supported by this file. */ void CiftiConnectivityMatrixParcelFile::getSupportedMatrixChartDataTypes(std::vector& chartDataTypesOut) const { chartDataTypesOut.clear(); chartDataTypesOut.push_back(ChartDataTypeEnum::CHART_DATA_TYPE_MATRIX_LAYER); } /** * @return Chart matrix display properties (const method). */ const ChartMatrixDisplayProperties* CiftiConnectivityMatrixParcelFile::getChartMatrixDisplayProperties(const int32_t tabIndex) const { CaretAssertArrayIndex(m_chartMatrixDisplayProperties, BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS, tabIndex); return m_chartMatrixDisplayProperties[tabIndex]; } /** * @return Chart matrix display properties. */ ChartMatrixDisplayProperties* CiftiConnectivityMatrixParcelFile::getChartMatrixDisplayProperties(const int32_t tabIndex) { CaretAssertArrayIndex(m_chartMatrixDisplayProperties, BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS, tabIndex); return m_chartMatrixDisplayProperties[tabIndex]; } /** * 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 CiftiConnectivityMatrixParcelFile::saveSubClassDataToScene(const SceneAttributes* sceneAttributes, SceneClass* sceneClass) { m_sceneAssistant->saveMembers(sceneAttributes, sceneClass); sceneClass->addBooleanArray("m_chartingEnabledForTab", m_chartingEnabledForTab, BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS); /* * Save chart matrix properties */ SceneObjectMapIntegerKey* chartMatrixPropertiesMap = new SceneObjectMapIntegerKey("m_chartMatrixDisplayPropertiesMap", SceneObjectDataTypeEnum::SCENE_CLASS); const std::vector tabIndices = sceneAttributes->getIndicesOfTabsForSavingToScene(); for (std::vector::const_iterator tabIter = tabIndices.begin(); tabIter != tabIndices.end(); tabIter++) { const int32_t tabIndex = *tabIter; chartMatrixPropertiesMap->addClass(tabIndex, m_chartMatrixDisplayProperties[tabIndex]->saveToScene(sceneAttributes, "m_chartMatrixDisplayProperties")); } sceneClass->addChild(chartMatrixPropertiesMap); } /** * 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 CiftiConnectivityMatrixParcelFile::restoreSubClassDataFromScene(const SceneAttributes* sceneAttributes, const SceneClass* sceneClass) { m_sceneAssistant->restoreMembers(sceneAttributes, sceneClass); // CiftiMappableConnectivityMatrixDataFile::restoreFileDataFromScene(sceneAttributes, // sceneClass); for (int32_t i = 0; i < BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS; i++) { m_chartingEnabledForTab[i] = false; } const ScenePrimitiveArray* tabArray = sceneClass->getPrimitiveArray("m_chartingEnabledForTab"); if (tabArray != NULL) { sceneClass->getBooleanArrayValue("m_chartingEnabledForTab", m_chartingEnabledForTab, BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS); } else { /* * Obsolete value when charting was not 'per tab' */ const bool chartingEnabled = sceneClass->getBooleanValue("m_chartingEnabled", false); for (int32_t i = 0; i < BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS; i++) { m_chartingEnabledForTab[i] = chartingEnabled; } } /* * Restore chart matrix properties */ const SceneObjectMapIntegerKey* chartMatrixPropertiesMap = sceneClass->getMapIntegerKey("m_chartMatrixDisplayPropertiesMap"); if (chartMatrixPropertiesMap != NULL) { const std::vector tabIndices = chartMatrixPropertiesMap->getKeys(); for (std::vector::const_iterator tabIter = tabIndices.begin(); tabIter != tabIndices.end(); tabIter++) { const int32_t tabIndex = *tabIter; const SceneClass* sceneClass = chartMatrixPropertiesMap->classValue(tabIndex); m_chartMatrixDisplayProperties[tabIndex]->restoreFromScene(sceneAttributes, sceneClass); } } } ///** // * Save file data from the scene. For subclasses that need to // * save to a scene, this method should be overriden. sceneClass // * will be valid and any scene data should be added to it. // * // * @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. // */ //void //CiftiConnectivityMatrixParcelFile::saveFileDataToScene(const SceneAttributes* sceneAttributes, // SceneClass* sceneClass) //{ // CiftiMappableConnectivityMatrixDataFile::saveFileDataToScene(sceneAttributes, // sceneClass); // // m_sceneAssistant->saveMembers(sceneAttributes, // sceneClass); // // sceneClass->addBooleanArray("m_chartingEnabledForTab", // m_chartingEnabledForTab, // BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS); // // /* // * Save chart matrix properties // */ // SceneObjectMapIntegerKey* chartMatrixPropertiesMap = new SceneObjectMapIntegerKey("m_chartMatrixDisplayPropertiesMap", // SceneObjectDataTypeEnum::SCENE_CLASS); // const std::vector tabIndices = sceneAttributes->getIndicesOfTabsForSavingToScene(); // for (std::vector::const_iterator tabIter = tabIndices.begin(); // tabIter != tabIndices.end(); // tabIter++) { // const int32_t tabIndex = *tabIter; // // chartMatrixPropertiesMap->addClass(tabIndex, // m_chartMatrixDisplayProperties[tabIndex]->saveToScene(sceneAttributes, // "m_chartMatrixDisplayProperties")); // } // sceneClass->addChild(chartMatrixPropertiesMap); //} // ///** // * Restore file data from the scene. For subclasses that need to // * restore from a scene, this method should be overridden. The scene class // * will be valid and any scene data may be obtained from it. // * // * @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 //CiftiConnectivityMatrixParcelFile::restoreFileDataFromScene(const SceneAttributes* sceneAttributes, // const SceneClass* sceneClass) //{ // CiftiMappableConnectivityMatrixDataFile::restoreFileDataFromScene(sceneAttributes, // sceneClass); // // m_sceneAssistant->restoreMembers(sceneAttributes, // sceneClass); // // /* // * The chart loading type is restored by the scene assistant. // * Swap its value so that calling setMatrixLoadingDimension requires // * the value to change for it to have any affect including // * setting size of data. // */ // switch (m_chartLoadingType) { // case ChartMatrixLoadingDimensionEnum::CHART_MATRIX_LOADING_BY_COLUMN: // m_chartLoadingType = ChartMatrixLoadingDimensionEnum::CHART_MATRIX_LOADING_BY_ROW; // break; // case ChartMatrixLoadingDimensionEnum::CHART_MATRIX_LOADING_BY_ROW: // m_chartLoadingType = ChartMatrixLoadingDimensionEnum::CHART_MATRIX_LOADING_BY_COLUMN; // break; // } // setMatrixLoadingDimension(m_chartLoadingType); // //// CiftiMappableConnectivityMatrixDataFile::restoreFileDataFromScene(sceneAttributes, //// sceneClass); // // for (int32_t i = 0; i < BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS; i++) { // m_chartingEnabledForTab[i] = false; // } // // const ScenePrimitiveArray* tabArray = sceneClass->getPrimitiveArray("m_chartingEnabledForTab"); // if (tabArray != NULL) { // sceneClass->getBooleanArrayValue("m_chartingEnabledForTab", // m_chartingEnabledForTab, // BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS); // } // else { // /* // * Obsolete value when charting was not 'per tab' // */ // const bool chartingEnabled = sceneClass->getBooleanValue("m_chartingEnabled", // false); // for (int32_t i = 0; i < BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS; i++) { // m_chartingEnabledForTab[i] = chartingEnabled; // } // } // // /* // * Restore chart matrix properties // */ // const SceneObjectMapIntegerKey* chartMatrixPropertiesMap = sceneClass->getMapIntegerKey("m_chartMatrixDisplayPropertiesMap"); // if (chartMatrixPropertiesMap != NULL) { // const std::vector tabIndices = chartMatrixPropertiesMap->getKeys(); // for (std::vector::const_iterator tabIter = tabIndices.begin(); // tabIter != tabIndices.end(); // tabIter++) { // const int32_t tabIndex = *tabIter; // const SceneClass* sceneClass = chartMatrixPropertiesMap->classValue(tabIndex); // m_chartMatrixDisplayProperties[tabIndex]->restoreFromScene(sceneAttributes, // sceneClass); // } // } // //} /** * @return Coloring mode for selected parcel. */ CiftiParcelColoringModeEnum::Enum CiftiConnectivityMatrixParcelFile::getSelectedParcelColoringMode() const { return m_selectedParcelColoringMode; } /** * Set the coloring mode for selected parcel. * * @param coloringMode * New value for coloring mode. */ void CiftiConnectivityMatrixParcelFile::setSelectedParcelColoringMode(const CiftiParcelColoringModeEnum::Enum coloringMode) { m_selectedParcelColoringMode = coloringMode; } /** * @return Color for selected parcel. */ CaretColorEnum::Enum CiftiConnectivityMatrixParcelFile::getSelectedParcelColor() const { return m_selectedParcelColor; } /** * Set color for selected parcel. * * @param color * New color for selected parcel. */ void CiftiConnectivityMatrixParcelFile::setSelectedParcelColor(const CaretColorEnum::Enum color) { m_selectedParcelColor = color; } /** * 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 CiftiConnectivityMatrixParcelFile::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 CiftiConnectivityMatrixParcelFile::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* CiftiConnectivityMatrixParcelFile::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 CiftiConnectivityMatrixParcelFile::createParcelReordering(const CiftiParcelLabelFile* parcelLabelFile, const int32_t parcelLabelFileMapIndex, AString& errorMessageOut) { return m_parcelReorderingModel->createParcelReordering(parcelLabelFile, parcelLabelFileMapIndex, errorMessageOut); } /** * @return True if loading attributes (column/row, yoking) are * supported by this file type. */ bool CiftiConnectivityMatrixParcelFile::isSupportsLoadingAttributes() { return true; } /** * @return The matrix loading type (by row/column). */ ChartMatrixLoadingDimensionEnum::Enum CiftiConnectivityMatrixParcelFile::getMatrixLoadingDimension() const { return getChartMatrixLoadingDimension(); } /** * Set the matrix loading type (by row/column). * * @param matrixLoadingType * New value for matrix loading type. */ void CiftiConnectivityMatrixParcelFile::setMatrixLoadingDimension(const ChartMatrixLoadingDimensionEnum::Enum matrixLoadingType) { /* * Ignore when the loading dimension does not change */ if (matrixLoadingType != getMatrixLoadingDimension()) { setChartMatrixLoadingDimension(matrixLoadingType); } } /** * @return Selected yoking group. */ YokingGroupEnum::Enum CiftiConnectivityMatrixParcelFile::getYokingGroup() const { return m_chartLoadingYokingGroup; } /** * Set the selected yoking group. * * @param yokingGroup * New value for yoking group. */ void CiftiConnectivityMatrixParcelFile::setYokingGroup(const YokingGroupEnum::Enum yokingGroup) { m_chartLoadingYokingGroup = yokingGroup; if (m_chartLoadingYokingGroup == YokingGroupEnum::YOKING_GROUP_OFF) { return; } /* * Updated selected row/column to match yoking. */ } connectome-workbench-1.2.3+git41-gc4c6c90/src/Files/CiftiConnectivityMatrixParcelFile.h000066400000000000000000000153021300200146000305310ustar00rootroot00000000000000#ifndef __CIFTI_CONNECTIVITY_MATRIX_PARCEL_FILE_H__ #define __CIFTI_CONNECTIVITY_MATRIX_PARCEL_FILE_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 "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: CiftiConnectivityMatrixParcelFile(); virtual ~CiftiConnectivityMatrixParcelFile(); private: CiftiConnectivityMatrixParcelFile(const CiftiConnectivityMatrixParcelFile&); CiftiConnectivityMatrixParcelFile& operator=(const CiftiConnectivityMatrixParcelFile&); public: virtual void receiveEvent(Event* event); virtual void getMatrixDimensions(int32_t& numberOfRowsOut, int32_t& numberOfColumnsOut) const; virtual bool getMatrixDataRGBA(int32_t& numberOfRowsOut, int32_t& numberOfColumnsOut, std::vector& rgbaOut) const; virtual bool getMatrixCellAttributes(const int32_t rowIndex, const int32_t columnIndex, AString& cellValueOut, AString& rowNameOut, AString& columnNameOut) const; virtual bool isMatrixChartingEnabled(const int32_t tabIndex) const; virtual bool isMatrixChartingSupported() const; virtual void setMatrixChartingEnabled(const int32_t tabIndex, const bool enabled); virtual void getSupportedMatrixChartDataTypes(std::vector& chartDataTypesOut) const; const ChartMatrixDisplayProperties* getChartMatrixDisplayProperties(const int32_t tabIndex) const; ChartMatrixDisplayProperties* getChartMatrixDisplayProperties(const int32_t tabIndex); virtual CiftiParcelColoringModeEnum::Enum getSelectedParcelColoringMode() const; virtual void setSelectedParcelColoringMode(const CiftiParcelColoringModeEnum::Enum coloringMode); virtual CaretColorEnum::Enum getSelectedParcelColor() const; virtual void setSelectedParcelColor(const CaretColorEnum::Enum color); 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; virtual bool isSupportsLoadingAttributes(); virtual ChartMatrixLoadingDimensionEnum::Enum getMatrixLoadingDimension() const; virtual void setMatrixLoadingDimension(const ChartMatrixLoadingDimensionEnum::Enum matrixLoadingType); virtual YokingGroupEnum::Enum getYokingGroup() const; virtual void setYokingGroup(const YokingGroupEnum::Enum yokingType); public: // ADD_NEW_METHODS_HERE protected: virtual void saveSubClassDataToScene(const SceneAttributes* sceneAttributes, SceneClass* sceneClass); virtual void restoreSubClassDataFromScene(const SceneAttributes* sceneAttributes, const SceneClass* sceneClass); // virtual void saveFileDataToScene(const SceneAttributes* sceneAttributes, // SceneClass* sceneClass); // // virtual void restoreFileDataFromScene(const SceneAttributes* sceneAttributes, // const SceneClass* sceneClass); private: // ADD_NEW_MEMBERS_HERE SceneClassAssistant* m_sceneAssistant; bool m_chartingEnabledForTab[BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS]; ChartMatrixDisplayProperties* m_chartMatrixDisplayProperties[BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS]; CiftiParcelColoringModeEnum::Enum m_selectedParcelColoringMode; CaretColorEnum::Enum m_selectedParcelColor; CiftiParcelReorderingModel* m_parcelReorderingModel; YokingGroupEnum::Enum m_chartLoadingYokingGroup; }; #ifdef __CIFTI_CONNECTIVITY_MATRIX_PARCEL_FILE_DECLARE__ // #endif // __CIFTI_CONNECTIVITY_MATRIX_PARCEL_FILE_DECLARE__ } // namespace #endif //__CIFTI_CONNECTIVITY_MATRIX_PARCEL_FILE_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Files/CiftiFiberOrientationFile.cxx000066400000000000000000000403741300200146000273640ustar00rootroot00000000000000 /*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 __CIFTI_FIBER_ORIENTATION_FILE_DECLARE__ #include "CiftiFiberOrientationFile.h" #undef __CIFTI_FIBER_ORIENTATION_FILE_DECLARE__ #include "CaretAssert.h" #include "CiftiFile.h" #include "CiftiMappableDataFile.h" #include "CaretLogger.h" #include "DataFileException.h" #include "Fiber.h" #include "FiberOrientation.h" #include "GiftiMetaData.h" #include "MathFunctions.h" #include using namespace caret; /** * \class caret::CiftiFiberOrientationFile * \brief Data file for Fiber Orientations * */ /** * Constructor. */ CiftiFiberOrientationFile::CiftiFiberOrientationFile() : CaretDataFile(DataFileTypeEnum::CONNECTIVITY_FIBER_ORIENTATIONS_TEMPORARY) { m_metadata = new GiftiMetaData(); m_ciftiXML = NULL; for (int32_t i = 0; i < DisplayGroupEnum::NUMBER_OF_GROUPS; i++) { m_displayStatusInDisplayGroup[i] = true; } for (int32_t i = 0; i < BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS; i++) { m_displayStatusInTab[i] = true; } } /** * Destructor. */ CiftiFiberOrientationFile::~CiftiFiberOrientationFile() { clearPrivate(); delete m_metadata; } /** * Cleare data in this file. */ void CiftiFiberOrientationFile::clear() { CaretDataFile::clear(); clearPrivate(); } /** * Cleare data in this file but not the parent class. */ void CiftiFiberOrientationFile::clearPrivate() { m_metadata->clear(); if (m_ciftiXML != NULL) { delete m_ciftiXML; m_ciftiXML = NULL; } for (std::vector::iterator iter = m_fiberOrientations.begin(); iter != m_fiberOrientations.end(); iter++) { delete *iter; } m_fiberOrientations.clear(); } /** * @return True if the file is empty. */ bool CiftiFiberOrientationFile::isEmpty() const { return true; } /** * @return The structure for this file. */ StructureEnum::Enum CiftiFiberOrientationFile::getStructure() const { return StructureEnum::ALL; } /** * Set the structure for this file. * @param structure * New structure for this file. */ void CiftiFiberOrientationFile::setStructure(const StructureEnum::Enum /*structure*/) { /* nothing */ } /** * @return Get access to the file's metadata. */ GiftiMetaData* CiftiFiberOrientationFile::getFileMetaData() { return m_metadata; } /** * @return Get access to unmodifiable file's metadata. */ const GiftiMetaData* CiftiFiberOrientationFile::getFileMetaData() const { return m_metadata; } /** * Initialize with test data. */ void CiftiFiberOrientationFile::initializeWithTestData() { const int64_t fiberDataSizeInFloats = (Fiber::NUMBER_OF_ELEMENTS_PER_FIBER_IN_FILE * 3) + 3; { float* fiberData = new float[fiberDataSizeInFloats]; int64_t offset = 0; /* * Coordinate of fiber orientation */ fiberData[offset+0] = 12.8; fiberData[offset+1] = 125.8; fiberData[offset+2] = 2.4; offset += 3; /* * Along Positive X-Axis */ fiberData[offset+0] = 0.3; // meanF fiberData[offset+1] = 2.0; // varF fiberData[offset+2] = MathFunctions::toRadians(90.0); // theta fiberData[offset+3] = 0.0; // phi fiberData[offset+4] = MathFunctions::toRadians(50.0); // k1 fiberData[offset+5] = MathFunctions::toRadians(10.0); // k2 fiberData[offset+6] = 0.0; // psi offset += 7; /* * Along Positive Y-Axis */ fiberData[offset+0] = 0.6; // meanF fiberData[offset+1] = 2.0; // varF fiberData[offset+2] = MathFunctions::toRadians(90.0); // theta fiberData[offset+3] = MathFunctions::toRadians(90.0); // phi fiberData[offset+4] = MathFunctions::toRadians(50.0); // k1 fiberData[offset+5] = MathFunctions::toRadians(10.0); // k2 fiberData[offset+6] = MathFunctions::toRadians(20.0); // psi offset += 7; /* * Along Positive Z-Axis */ fiberData[offset+0] = 1.0; // meanF fiberData[offset+1] = 2.0; // varF fiberData[offset+2] = 0.0; // theta fiberData[offset+3] = 0.0; // phi fiberData[offset+4] = MathFunctions::toRadians(20.0); // k1 fiberData[offset+5] = MathFunctions::toRadians(10.0); // k2 fiberData[offset+6] = MathFunctions::toRadians(70.0); // psi offset += 7; FiberOrientation* fiberOrientation = new FiberOrientation(3, fiberData); m_fiberOrientations.push_back(fiberOrientation); } { float* fiberData = new float[fiberDataSizeInFloats]; int64_t offset = 0; /* * Coordinate of fiber orientation */ fiberData[offset+0] = -60.8; fiberData[offset+1] = -55.8; fiberData[offset+2] = -2.4; offset += 3; /* * Pointing towards forward right and up */ fiberData[offset+0] = 0.3; // meanF fiberData[offset+1] = 2.0; // varF fiberData[offset+2] = MathFunctions::toRadians(45.0); // theta fiberData[offset+3] = MathFunctions::toRadians(45.0); // phi fiberData[offset+4] = MathFunctions::toRadians(40.0); // k1 fiberData[offset+5] = MathFunctions::toRadians(10.0); // k2 fiberData[offset+6] = 0.0; // psi offset += 7; /* * Pointing towards forward left and down */ fiberData[offset+0] = 0.6; // meanF fiberData[offset+1] = 2.0; // varF fiberData[offset+2] = MathFunctions::toRadians(45.0); // theta fiberData[offset+3] = MathFunctions::toRadians(135.0); // phi fiberData[offset+4] = MathFunctions::toRadians(20.0); // k1 fiberData[offset+5] = MathFunctions::toRadians(15.0); // k2 fiberData[offset+6] = 0.0; // psi offset += 7; /* * Pointing towards backward right and up */ fiberData[offset+0] = 1.0; // meanF fiberData[offset+1] = 2.0; // varF fiberData[offset+2] = MathFunctions::toRadians( 45.0); // theta fiberData[offset+3] = MathFunctions::toRadians(-45.0); // phi fiberData[offset+4] = MathFunctions::toRadians(40.0); // k1 fiberData[offset+5] = MathFunctions::toRadians(20.0); // k2 fiberData[offset+6] = MathFunctions::toRadians(25.0); // psi offset += 7; FiberOrientation* fiberOrientation = new FiberOrientation(3, fiberData); m_fiberOrientations.push_back(fiberOrientation); } } /** * @return The number of orientation fiber groups. */ int64_t CiftiFiberOrientationFile::getNumberOfFiberOrientations() const { return m_fiberOrientations.size(); } /** * Get the orientation fiber group at the given index. * @param indx * Index of the desired fiber orientation group. */ FiberOrientation* CiftiFiberOrientationFile::getFiberOrientations(const int64_t indx) { return m_fiberOrientations[indx]; } /** * Get Fiber orientation nearest coordinate and within the maximum * distance. * * @param xyz * The coordinate. * @param maximumDistance * The maximum distance. If negative, any distance is allowed. * * @return Fiber found or NULL if not found. */ FiberOrientation* CiftiFiberOrientationFile::getFiberOrientationNearestCoordinate(const float xyz[3], const float maximumDistance) const { FiberOrientation* nearestFiberOrientation = NULL; float nearestDistance = std::numeric_limits::max(); const int64_t numFiberOrientations = getNumberOfFiberOrientations(); for (int64_t i = 0; i < numFiberOrientations; i++) { const float distance = MathFunctions::distanceSquared3D(xyz, m_fiberOrientations[i]->m_xyz); if (distance < nearestDistance) { if (maximumDistance > 0.0) { if (distance > maximumDistance) { continue; } } nearestDistance = distance; nearestFiberOrientation = m_fiberOrientations[i]; } } return nearestFiberOrientation; } /** * Get the orientation fiber group at the given index. * @param indx * Index of the desired fiber orientation group. */ const FiberOrientation* CiftiFiberOrientationFile::getFiberOrientations(const int64_t indx) const { return m_fiberOrientations[indx]; } /** * @return The display status. */ bool CiftiFiberOrientationFile::isDisplayed(const DisplayGroupEnum::Enum displayGroup, const int32_t tabIndex) const { const int32_t displayIndex = (int32_t)displayGroup; CaretAssertArrayIndex(m_displayStatusInDisplayGroup, DisplayGroupEnum::NUMBER_OF_GROUPS, displayIndex); if (displayGroup == DisplayGroupEnum::DISPLAY_GROUP_TAB) { CaretAssertArrayIndex(m_displayStatusInTab, BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS, tabIndex); return m_displayStatusInTab[tabIndex]; } return m_displayStatusInDisplayGroup[displayIndex]; } /** * Set the display status. * @param displayed * New display status. */ void CiftiFiberOrientationFile::setDisplayed(const DisplayGroupEnum::Enum displayGroup, const int32_t tabIndex, const bool displayed) { const int32_t displayIndex = (int32_t)displayGroup; CaretAssertArrayIndex(m_displayStatusInDisplayGroup, DisplayGroupEnum::NUMBER_OF_GROUPS, displayIndex); if (displayGroup == DisplayGroupEnum::DISPLAY_GROUP_TAB) { CaretAssertArrayIndex(m_displayStatusInTab, BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS, tabIndex); m_displayStatusInTab[tabIndex] = displayed; } else { m_displayStatusInDisplayGroup[displayIndex] = displayed; } } /** * Get the volume spacing. * @param volumeSpacingOut * Will contain volume spacing for (I, J, K) axes upon exit. */ void CiftiFiberOrientationFile::getVolumeSpacing(float volumeSpacingOut[3]) const { volumeSpacingOut[0] = m_volumeSpacing[0]; volumeSpacingOut[1] = m_volumeSpacing[1]; volumeSpacingOut[2] = m_volumeSpacing[2]; } /** * @return a pointer to the CIFTI XML. * May be NULL if a file is not loaded. */ const CiftiXML* CiftiFiberOrientationFile::getCiftiXML() const { return m_ciftiXML; } /** * Read the data file. * * @param filename * Name of the data file. * @throws DataFileException * If the file was not successfully read. */ void CiftiFiberOrientationFile::readFile(const AString& filename) { clear(); checkFileReadability(filename); try { CiftiFile ciftiFile; ciftiFile.openFile(filename); ciftiFile.convertToInMemory(); const int64_t numRows = ciftiFile.getNumberOfRows(); if (numRows <= 0) { throw DataFileException(getFileNameNoPath() + " does not contain any data (no rows)"); } const int64_t numCols = ciftiFile.getNumberOfColumns(); if (numCols <= 0) { throw DataFileException(getFileNameNoPath() + " does not contain any data (no columns)"); } /* * Each set of fibers contains XYZ (3 elements) * plus number of elements per fiber. */ const int64_t numberOfFibers = ((numCols - FiberOrientation::NUMBER_OF_ELEMENTS_IN_FILE) / Fiber::NUMBER_OF_ELEMENTS_PER_FIBER_IN_FILE); const int64_t expectedNumberOfColumns = (numberOfFibers * Fiber::NUMBER_OF_ELEMENTS_PER_FIBER_IN_FILE) + FiberOrientation::NUMBER_OF_ELEMENTS_IN_FILE; if (expectedNumberOfColumns != numCols) { throw DataFileException(filename, "Validation of column count failed: expected " + AString::number(expectedNumberOfColumns) + " but have " + AString::number(numCols) + " columns."); } /* * Create the fiber groups */ std::vector rowData(numCols); float* rowPointer = &rowData[0]; m_fiberOrientations.reserve(numRows); for (int64_t i = 0; i < numRows; i++) { ciftiFile.getRow(rowPointer, i); FiberOrientation* fiberOrient = new FiberOrientation(numberOfFibers, rowPointer); if (fiberOrient->m_valid) { m_fiberOrientations.push_back(fiberOrient); } else { CaretLogSevere("Fiber invalid at row " + QString::number(i) + " is invalid: " + fiberOrient->m_invalidMessage); delete fiberOrient; } } const CiftiXML& ciftiXML = ciftiFile.getCiftiXML(); m_ciftiXML = new CiftiXML(ciftiXML); VolumeSpace::OrientTypes orient[3]; float origin[3]; if (ciftiXML.getMappingType(CiftiXML::ALONG_COLUMN) != CiftiMappingType::BRAIN_MODELS) throw DataFileException(getFileNameNoPath() + " does not have brain models along column"); const CiftiBrainModelsMap& myMap = ciftiXML.getBrainModelsMap(CiftiXML::ALONG_COLUMN); if (!myMap.hasVolumeData()) throw DataFileException(getFileNameNoPath() + " has no volume data, cannot be a fiber orientation file"); myMap.getVolumeSpace().getOrientAndSpacingForPlumb(orient, m_volumeSpacing, origin);//NOTE: will assert/throw if not plumb setFileName(filename); clearModified(); } catch (const DataFileException& dfe) { clear(); throw dfe; } } /** * Add information about the file to the data file information. * * @param dataFileInformation * Consolidates information about a data file. */ void CiftiFiberOrientationFile::addToDataFileContentInformation(DataFileContentInformation& dataFileInformation) { CaretDataFile::addToDataFileContentInformation(dataFileInformation); if (m_ciftiXML != NULL) { CiftiMappableDataFile::addCiftiXmlToDataFileContentInformation(dataFileInformation, *m_ciftiXML); } } /** * Write the data file. * * @param filename * Name of the data file. * @throws DataFileException * If the file was not successfully written. */ void CiftiFiberOrientationFile::writeFile(const AString& filename) { throw DataFileException(filename, "Writing of Cifti Orientation Files not supported."); } /** * @return True if this file type supports writing, else false. * * Fiber orientation files do NOT support writing. */ bool CiftiFiberOrientationFile::supportsWriting() const { return false; } connectome-workbench-1.2.3+git41-gc4c6c90/src/Files/CiftiFiberOrientationFile.h000066400000000000000000000073051300200146000270060ustar00rootroot00000000000000#ifndef __CIFTI_FIBER_ORIENTATION_FILE_H__ #define __CIFTI_FIBER_ORIENTATION_FILE_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 "BrainConstants.h" #include "CaretDataFile.h" #include "DisplayGroupEnum.h" namespace caret { class CiftiXML; class FiberOrientation; class CiftiFiberOrientationFile : public CaretDataFile { public: CiftiFiberOrientationFile(); virtual ~CiftiFiberOrientationFile(); void initializeWithTestData(); int64_t getNumberOfFiberOrientations() const; FiberOrientation* getFiberOrientations(const int64_t indx); const FiberOrientation* getFiberOrientations(const int64_t indx) const; FiberOrientation* getFiberOrientationNearestCoordinate(const float xyz[3], const float maximumDistance) const; void getVolumeSpacing(float volumeSpacingOut[3]) const; void setDisplayed(const DisplayGroupEnum::Enum displayGroup, const int32_t tabIndex, const bool displayed); bool isDisplayed(const DisplayGroupEnum::Enum displayGroup, const int32_t tabIndex) const; virtual void clear(); bool isEmpty() const; const CiftiXML* getCiftiXML() const; virtual StructureEnum::Enum getStructure() const; virtual void setStructure(const StructureEnum::Enum structure); virtual GiftiMetaData* getFileMetaData(); virtual const GiftiMetaData* getFileMetaData() const; virtual void readFile(const AString& filename); virtual void writeFile(const AString& filename); bool supportsWriting() const; void addToDataFileContentInformation(DataFileContentInformation& dataFileInformation); // ADD_NEW_METHODS_HERE private: CiftiFiberOrientationFile(const CiftiFiberOrientationFile&); CiftiFiberOrientationFile& operator=(const CiftiFiberOrientationFile&); private: void clearPrivate(); CiftiXML* m_ciftiXML; GiftiMetaData* m_metadata; std::vector m_fiberOrientations; /** Display status in display group */ bool m_displayStatusInDisplayGroup[DisplayGroupEnum::NUMBER_OF_GROUPS]; /** Display status in tab */ bool m_displayStatusInTab[BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS]; float m_volumeSpacing[3]; // ADD_NEW_MEMBERS_HERE }; #ifdef __CIFTI_FIBER_ORIENTATION_FILE_DECLARE__ // #endif // __CIFTI_FIBER_ORIENTATION_FILE_DECLARE__ } // namespace #endif //__CIFTI_FIBER_ORIENTATION_FILE_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Files/CiftiFiberTrajectoryFile.cxx000066400000000000000000001732331300200146000272200ustar00rootroot00000000000000 /*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 #define __CIFTI_FIBER_TRAJECTORY_FILE_DECLARE__ #include "CiftiFiberTrajectoryFile.h" #undef __CIFTI_FIBER_TRAJECTORY_FILE_DECLARE__ #include "CaretAssert.h" #include "CaretLogger.h" #include "CaretSparseFile.h" #include "CiftiFiberOrientationFile.h" #include "CiftiMappableDataFile.h" #include "ConnectivityDataLoaded.h" #include "DataFileContentInformation.h" #include "EventManager.h" #include "EventProgressUpdate.h" #include "FiberOrientationTrajectory.h" #include "FiberTrajectoryMapProperties.h" #include "FileInformation.h" #include "GiftiMetaData.h" #include "PaletteColorMapping.h" #include "SceneClass.h" #include "SceneClassAssistant.h" using namespace caret; /** * \class caret::CiftiFiberTrajectoryFile * \brief File that contains trajectories */ /** * Constructor. */ CiftiFiberTrajectoryFile::CiftiFiberTrajectoryFile() : CaretMappableDataFile(DataFileTypeEnum::CONNECTIVITY_FIBER_TRAJECTORY_TEMPORARY) { m_connectivityDataLoaded = new ConnectivityDataLoaded(); m_fiberTrajectoryMapProperties = new FiberTrajectoryMapProperties(); m_metadata = new GiftiMetaData(); m_sparseFile = NULL; m_matchingFiberOrientationFile = NULL; m_matchingFiberOrientationFileName = ""; m_dataLoadingEnabled = true; m_fiberTrajectoryFileType = FIBER_TRAJECTORY_LOAD_BY_BRAINORDINATE; m_sceneAssistant = new SceneClassAssistant(); m_sceneAssistant->add("m_dataLoadingEnabled", &m_dataLoadingEnabled); m_sceneAssistant->add("m_matchingFiberOrientationFileName", &m_matchingFiberOrientationFileName); m_sceneAssistant->add("m_fiberTrajectoryMapProperties", "FiberTrajectoryMapProperties", m_fiberTrajectoryMapProperties); m_sceneAssistant->add("m_connectivityDataLoaded", "ConnectivityDataLoaded", m_connectivityDataLoaded); } /** * Destructor. */ CiftiFiberTrajectoryFile::~CiftiFiberTrajectoryFile() { clearPrivate(); delete m_fiberTrajectoryMapProperties; delete m_metadata; // DO NOT DELETE (owned by Brain): m_matchingFiberOrientationFile. delete m_sceneAssistant; delete m_connectivityDataLoaded; } /** * Cleare data in this file. */ void CiftiFiberTrajectoryFile::clear() { CaretMappableDataFile::clear(); clearPrivate(); } /** * Cleare data in this file but not the parent class. */ void CiftiFiberTrajectoryFile::clearPrivate() { m_metadata->clear(); clearLoadedFiberOrientations(); if (m_sparseFile != NULL) { delete m_sparseFile; m_sparseFile = NULL; } m_matchingFiberOrientationFile = NULL; m_matchingFiberOrientationFileName = ""; m_fiberTrajectoryFileType = FIBER_TRAJECTORY_LOAD_BY_BRAINORDINATE; } /** * @return True if the file is empty. */ bool CiftiFiberTrajectoryFile::isEmpty() const { if (m_sparseFile != NULL) { return false; } return true; } /** * @return Is data loading enabled? */ bool CiftiFiberTrajectoryFile::isDataLoadingEnabled() const { return m_dataLoadingEnabled; } /** * Set data loading enabled. * * @param loadingEnabled * New status of data loading. */ void CiftiFiberTrajectoryFile::setDataLoadingEnabled(const bool loadingEnabled) { m_dataLoadingEnabled = loadingEnabled; } /** * @return The selected matching fiber orientation file. May be NULL. */ const CiftiFiberOrientationFile* CiftiFiberTrajectoryFile::getMatchingFiberOrientationFile() const { return m_matchingFiberOrientationFile; } /** * @return The selected matching fiber orientation file. May be NULL. */ CiftiFiberOrientationFile* CiftiFiberTrajectoryFile::getMatchingFiberOrientationFile() { return m_matchingFiberOrientationFile; } /** * Is the given fiber orientation file compatible with this fiber trajectory file * * @param fiberOrientationFile * File tested for compatibilty * @return * True if file is compatible, else false. */ bool CiftiFiberTrajectoryFile::isFiberOrientationFileCombatible(const CiftiFiberOrientationFile* fiberOrientationFile) const { CaretAssert(fiberOrientationFile); const CiftiXML& trajXML = m_sparseFile->getCiftiXML(); const CiftiXML* orientXML = fiberOrientationFile->getCiftiXML(); if (*(trajXML.getMap(CiftiXML::ALONG_ROW)) == *(orientXML->getMap(CiftiXML::ALONG_COLUMN))) { return true; } return false; } /** * Set the selected matching fiber orientation file. No test of compatibility * is made. If this is a "single row" trajectory file, the data for the single * row is loaded. * * @param matchingFiberOrientationFile * New selection for matching fiber orientation file. */ void CiftiFiberTrajectoryFile::setMatchingFiberOrientationFile(CiftiFiberOrientationFile* matchingFiberOrientationFile) { m_matchingFiberOrientationFile = matchingFiberOrientationFile; if (m_matchingFiberOrientationFile != NULL) { // m_matchingFiberOrientationFileName = m_matchingFiberOrientationFile->getFileNameNoPath(); m_matchingFiberOrientationFileName = m_matchingFiberOrientationFile->getFileName(); switch (m_fiberTrajectoryFileType) { case FIBER_TRAJECTORY_LOAD_BY_BRAINORDINATE: break; case FIBER_TRAJECTORY_LOAD_SINGLE_ROW: loadDataForRowIndex(0); const CiftiXML& sparseXML = m_sparseFile->getCiftiXML(); if (sparseXML.getMappingType(CiftiXML::ALONG_COLUMN) == CiftiMappingType::SCALARS) { m_loadedDataDescriptionForMapName = sparseXML.getScalarsMap(CiftiXML::ALONG_COLUMN).getMapName(0); } else { m_loadedDataDescriptionForMapName = ""; } break; } } else { m_matchingFiberOrientationFileName = ""; } } /** * Update the matching fiber orientation file from the first compatible file in the list. * If none are found, the matching file will become NULL. If the current matching file * is valid, no action is taken. * * @param matchingFiberOrientationFiles * The fiber orientation files. */ void CiftiFiberTrajectoryFile::updateMatchingFiberOrientationFileFromList(std::vector matchingFiberOrientationFiles) { /* * If a scene has been restored, we want to match to the fiber orientation * file name that was restored from the scene */ if (m_matchingFiberOrientationFileNameFromRestoredScene.isEmpty() == false) { bool matched = false; CiftiFiberOrientationFile* matchedOrientFile = NULL; int64_t matchedOrientFileCount = 0; const FileInformation fileInfo(m_matchingFiberOrientationFileNameFromRestoredScene); const AString matchingFileNameNoPath = fileInfo.getFileName(); for (std::vector::iterator iter = matchingFiberOrientationFiles.begin(); iter != matchingFiberOrientationFiles.end(); iter++) { /* * Try and see if it matches for this file * (1) Verify compatibility * (2) Match name of file without path * (3) Match path starting at end of path */ CiftiFiberOrientationFile* orientationFile = *iter; CaretAssert(orientationFile); if (isFiberOrientationFileCombatible(orientationFile)) { if (matchingFileNameNoPath == orientationFile->getFileNameNoPath()) { const AString orientationFileName = orientationFile->getFileName(); const int64_t endMatchCount = orientationFileName.countMatchingCharactersFromEnd(m_matchingFiberOrientationFileNameFromRestoredScene); if (endMatchCount > matchedOrientFileCount) { matchedOrientFile = orientationFile; matchedOrientFileCount = endMatchCount; } } } // if (orientationFile->getFileNameNoPath() == m_matchingFiberOrientationFileNameFromRestoredScene) { // if (isFiberOrientationFileCombatible(orientationFile)) { // setMatchingFiberOrientationFile(orientationFile); // matched = true; // } // } } if (matchedOrientFileCount > 0) { setMatchingFiberOrientationFile(matchedOrientFile); } /* * Clear name so no attempt to use again */ m_matchingFiberOrientationFileNameFromRestoredScene = ""; if (matched) { return; } } /* * See if selected orientation file is still valid */ for (std::vector::iterator iter = matchingFiberOrientationFiles.begin(); iter != matchingFiberOrientationFiles.end(); iter++) { if (*iter == m_matchingFiberOrientationFile) { return; } } /* * Invalidate matching file */ m_matchingFiberOrientationFile = NULL; m_matchingFiberOrientationFileName = ""; clearLoadedFiberOrientations(); /* * Try to find a matching file */ for (std::vector::iterator iter = matchingFiberOrientationFiles.begin(); iter != matchingFiberOrientationFiles.end(); iter++) { /* * Try and see if it matches for this file */ if (isFiberOrientationFileCombatible(*iter)) { setMatchingFiberOrientationFile(*iter); return; } } } /** * @return The structure for this file. */ StructureEnum::Enum CiftiFiberTrajectoryFile::getStructure() const { return StructureEnum::ALL; } /** * Set the structure for this file. * @param structure * New structure for this file. */ void CiftiFiberTrajectoryFile::setStructure(const StructureEnum::Enum /*structure*/) { /* nothing */ } /** * @return Get access to the file's metadata. */ GiftiMetaData* CiftiFiberTrajectoryFile::getFileMetaData() { return m_metadata; } /** * @return Get access to unmodifiable file's metadata. */ const GiftiMetaData* CiftiFiberTrajectoryFile::getFileMetaData() const { return m_metadata; } /** * @return Is the data mappable to a surface? */ bool CiftiFiberTrajectoryFile::isSurfaceMappable() const { return false; } /** * @return Is the data mappable to a volume? */ bool CiftiFiberTrajectoryFile::isVolumeMappable() const { return true; } /** * @return The number of maps in the file. * Note: Caret5 used the term 'columns'. */ int32_t CiftiFiberTrajectoryFile::getNumberOfMaps() const { /* * Always return 1. * If zero is returned, it will never appear in the overlays because * zero is interpreted as "nothing available". */ return 1; } /** * @return True if the file has map attributes (name and metadata). * For files that do not have map attributes, they should override * this method and return false. If not overriden, this method * returns true. * * Some files (such as CIFTI Connectivity Matrix Files and CIFTI * Data-Series Files) do not have Map Attributes and thus there * is no map name nor map metadata and options to edit these * attributes should not be presented to the user. * * These CIFTI files do contain palette color mapping but it is * associated with the file. To simplify palette color mapping editing * these file will return the file's palette color mapping for any * calls to getMapPaletteColorMapping(). */ bool CiftiFiberTrajectoryFile::hasMapAttributes() const { return false; } /** * Get the name of the map at the given index. * * @param mapIndex * Index of the map. * @return * Name of the map. */ AString CiftiFiberTrajectoryFile::getMapName(const int32_t /*mapIndex*/) const { return m_loadedDataDescriptionForMapName; } /** * Set the name of the map at the given index. * * @param mapIndex * Index of the map. * @param mapName * New name for the map. */ void CiftiFiberTrajectoryFile::setMapName(const int32_t /*mapIndex*/, const AString& /*mapName*/) { } /** * Get the metadata for the map at the given index * * @param mapIndex * Index of the map. * @return * Metadata for the map (const value). */ const GiftiMetaData* CiftiFiberTrajectoryFile::getMapMetaData(const int32_t /*mapIndex*/) const { return getFileMetaData(); } /** * Get the metadata for the map at the given index * * @param mapIndex * Index of the map. * @return * Metadata for the map. */ GiftiMetaData* CiftiFiberTrajectoryFile::getMapMetaData(const int32_t /*mapIndex*/) { return getFileMetaData(); } /** * Get the unique ID (UUID) for the map at the given index. * * @param mapIndex * Index of the map. * @return * String containing UUID for the map. */ AString CiftiFiberTrajectoryFile::getMapUniqueID(const int32_t mapIndex) const { const GiftiMetaData* md = getMapMetaData(mapIndex); const AString uniqueID = md->getUniqueID(); return uniqueID; } /** * @return Is the data in the file mapped to colors using * a palette. */ bool CiftiFiberTrajectoryFile::isMappedWithPalette() const { return false; } /** * Get statistics describing the distribution of data * mapped with a color palette at the given index. * * @param mapIndex * Index of the map. * @return * Fast statistics for data (will be NULL for data * not mapped using a palette). */ const FastStatistics* CiftiFiberTrajectoryFile::getMapFastStatistics(const int32_t /*mapIndex*/) { return NULL; } /** * Get histogram describing the distribution of data * mapped with a color palette at the given index. * * @param mapIndex * Index of the map. * @return * Histogram for data (will be NULL for data * not mapped using a palette). */ const Histogram* CiftiFiberTrajectoryFile::getMapHistogram(const int32_t /*mapIndex*/) { return NULL; } /** * Get histogram describing the distribution of data * mapped with a color palette at the given index for * data within the given ranges. * * @param mapIndex * Index of the map. * @param mostPositiveValueInclusive * Values more positive than this value are excluded. * @param leastPositiveValueInclusive * Values less positive than this value are excluded. * @param leastNegativeValueInclusive * Values less negative than this value are excluded. * @param mostNegativeValueInclusive * Values more negative than this value are excluded. * @param includeZeroValues * If true zero values (very near zero) are included. * @return * Descriptive statistics for data (will be NULL for data * not mapped using a palette). */ const Histogram* CiftiFiberTrajectoryFile::getMapHistogram(const int32_t /*mapIndex*/, const float /*mostPositiveValueInclusive*/, const float /*leastPositiveValueInclusive*/, const float /*leastNegativeValueInclusive*/, const float /*mostNegativeValueInclusive*/, const bool /*includeZeroValues*/) { return NULL; } /** * @return The estimated size of data after it is uncompressed * and loaded into RAM. A negative value indicates that the * file size cannot be computed. */ int64_t CiftiFiberTrajectoryFile::getDataSizeUncompressedInBytes() const { return -1; } /** * Get statistics describing the distribution of data * mapped with a color palette for all data within the file. * * @return * Fast statistics for data (will be NULL for data * not mapped using a palette). */ const FastStatistics* CiftiFiberTrajectoryFile::getFileFastStatistics() { return NULL; } /** * Get histogram describing the distribution of data * mapped with a color palette for all data within * the file. * * @return * Histogram for data (will be NULL for data * not mapped using a palette). */ const Histogram* CiftiFiberTrajectoryFile::getFileHistogram() { return NULL; } /** * Get histogram describing the distribution of data * mapped with a color palette for all data in the file * within the given range of values. * * @param mostPositiveValueInclusive * Values more positive than this value are excluded. * @param leastPositiveValueInclusive * Values less positive than this value are excluded. * @param leastNegativeValueInclusive * Values less negative than this value are excluded. * @param mostNegativeValueInclusive * Values more negative than this value are excluded. * @param includeZeroValues * If true zero values (very near zero) are included. * @return * Descriptive statistics for data (will be NULL for data * not mapped using a palette). */ const Histogram* CiftiFiberTrajectoryFile::getFileHistogram(const float /*mostPositiveValueInclusive*/, const float /*leastPositiveValueInclusive*/, const float /*leastNegativeValueInclusive*/, const float /*mostNegativeValueInclusive*/, const bool /*includeZeroValues*/) { return NULL; } /** * Get the palette color mapping for the map at the given index. * * @param mapIndex * Index of the map. * @return * Palette color mapping for the map (will be NULL for data * not mapped using a palette). */ PaletteColorMapping* CiftiFiberTrajectoryFile::getMapPaletteColorMapping(const int32_t /*mapIndex*/) { return NULL; } /** * Get the palette color mapping for the map at the given index. * * @param mapIndex * Index of the map. * @return * Palette color mapping for the map (constant) (will be NULL for data * not mapped using a palette). */ const PaletteColorMapping* CiftiFiberTrajectoryFile::getMapPaletteColorMapping(const int32_t /*mapIndex*/) const { return NULL; } /** * @return Is the data in the file mapped to colors using * a label table. */ bool CiftiFiberTrajectoryFile::isMappedWithLabelTable() const { return false; } /** * Get the label table for the map at the given index. * * @param mapIndex * Index of the map. * @return * Label table for the map (will be NULL for data * not mapped using a label table). */ GiftiLabelTable* CiftiFiberTrajectoryFile::getMapLabelTable(const int32_t /*mapIndex*/) { return NULL; } /** * Get the label table for the map at the given index. * * @param mapIndex * Index of the map. * @return * Label table for the map (constant) (will be NULL for data * not mapped using a label table). */ const GiftiLabelTable* CiftiFiberTrajectoryFile::getMapLabelTable(const int32_t /*mapIndex*/) const { return NULL; } /** * Get the palette normalization modes that are supported by the file. * * @param modesSupportedOut * Palette normalization modes supported by a file. Will be * empty for files that are not mapped with a palette. If there * is more than one suppported mode, the first mode in the * vector is assumed to be the default mode. */ void CiftiFiberTrajectoryFile::getPaletteNormalizationModesSupported(std::vector& modesSupportedOut) { modesSupportedOut.clear(); } /** * Update scalar coloring for a map. * * Note that some CIFTI files can be slow to color due to the need to * retrieve data for the map. Use isMapColoringValid() to avoid * unnecessary calls to isMapColoringValid. * * @param mapIndex * Index of map. * @param paletteFile * Palette file containing palettes. */ void CiftiFiberTrajectoryFile::updateScalarColoringForMap(const int32_t /*mapIndex*/, const PaletteFile* /*paletteFile*/) { } /** * @return The fiber trajectory map properties (const method). */ FiberTrajectoryMapProperties* CiftiFiberTrajectoryFile::getFiberTrajectoryMapProperties() { return m_fiberTrajectoryMapProperties; } /** * @return The fiber trajectory map properties. */ const FiberTrajectoryMapProperties* CiftiFiberTrajectoryFile::getFiberTrajectoryMapProperties() const { return m_fiberTrajectoryMapProperties; } /** * Read the data file. * * @param filename * Name of the data file. * @throws DataFileException * If the file was not successfully read. */ void CiftiFiberTrajectoryFile::readFile(const AString& filename) { clear(); checkFileReadability(filename); try { m_sparseFile = new CaretSparseFile(); m_sparseFile->readFile(filename); setFileName(filename); m_fiberTrajectoryFileType = FIBER_TRAJECTORY_LOAD_BY_BRAINORDINATE; const CiftiXML& xml = m_sparseFile->getCiftiXML(); if (xml.getMappingType(CiftiXML::ALONG_COLUMN) == CiftiMappingType::SCALARS) { m_fiberTrajectoryFileType = FIBER_TRAJECTORY_LOAD_SINGLE_ROW; } clearModified(); } catch (const DataFileException& e) { clear(); throw e; } } /** * Write the data file. * * @param filename * Name of the data file. * @throws DataFileException * If the file was not successfully written. */ void CiftiFiberTrajectoryFile::writeFile(const AString& filename) { switch (m_fiberTrajectoryFileType) { case FIBER_TRAJECTORY_LOAD_BY_BRAINORDINATE: throw DataFileException(filename, "Writing of Cifti Trajectory Files that load by brainordinate is not supported."); break; case FIBER_TRAJECTORY_LOAD_SINGLE_ROW: writeLoadedDataToFile(filename); break; } } class FiberFractionAndIndex { public: FiberFractionAndIndex(const FiberFractions& fiberFraction, const int64_t fiberIndex) { m_fiberFraction = fiberFraction; m_fiberIndex = fiberIndex; } bool operator<(const FiberFractionAndIndex& other) const { return (m_fiberIndex < other.m_fiberIndex); } FiberFractions m_fiberFraction; int64_t m_fiberIndex; }; class FiberTrajectoryComparison { public: bool operator() (const FiberOrientationTrajectory* left, const FiberOrientationTrajectory* right) const { return (left->getFiberOrientationIndex() < right->getFiberOrientationIndex()); } }; /** * Create a new fiber trajectory file from the loaded data of this file. * * @param errorMessageOut * Error message if creation of new fiber trajectory file failed. * @param * Pointer to new file that was created or NULL if creation failed. */ CiftiFiberTrajectoryFile* CiftiFiberTrajectoryFile::newFiberTrajectoryFileFromLoadedRowData(const AString& destinationDirectory, AString& errorMessageOut) const { errorMessageOut = ""; const int64_t numTraj = static_cast(m_fiberOrientationTrajectories.size()); if (numTraj <= 0) { errorMessageOut = "No data is loaded so cannot create file."; return NULL; } CiftiFiberTrajectoryFile* newFile = NULL; try { newFile = new CiftiFiberTrajectoryFile(); AString rowInfo = ""; if (m_loadedDataDescriptionForFileCopy.isEmpty() == false) { rowInfo = ("_" + m_loadedDataDescriptionForFileCopy); } /* * May need to convert a remote path to a local path */ FileInformation initialFileNameInfo(getFileName()); const AString scalarFileName = initialFileNameInfo.getAsLocalAbsoluteFilePath(destinationDirectory, getDataFileType()); /* * Create name of scalar file with row/column information */ FileInformation scalarFileInfo(scalarFileName); AString thePath, theName, theExtension; scalarFileInfo.getFileComponents(thePath, theName, theExtension); theName.append(rowInfo); const AString newFileName = FileInformation::assembleFileComponents(thePath, theName, theExtension); const AString tempFileName = (QDir::tempPath() + QDir::separator() + newFile->getFileNameNoPath()); std::cout << "Filename: " << qPrintable(tempFileName) << std::endl; writeLoadedDataToFile(tempFileName); newFile->readFile(tempFileName); newFile->setFileName(newFileName); newFile->setMatchingFiberOrientationFile(const_cast(getMatchingFiberOrientationFile())); newFile->m_fiberTrajectoryMapProperties->copy(*getFiberTrajectoryMapProperties()); newFile->setModified(); return newFile; } catch (const DataFileException& dfe) { if (newFile != NULL) { delete newFile; } errorMessageOut = dfe.whatString(); return NULL; } return NULL; } /** * Write the loaded data to a file. * * @param filename * Name of file to write. * @throw DataFileException * If an error occurs. */ void CiftiFiberTrajectoryFile::writeLoadedDataToFile(const AString& filename) const { CiftiXML xml = m_sparseFile->getCiftiXML(); /* * Copy the pointers to the fiber orientation trajectories and sort * by fiber orientation index. */ std::vector trajectories(m_fiberOrientationTrajectories.begin(), m_fiberOrientationTrajectories.end()); bool isWriteFullRow = false; if (static_cast(trajectories.size()) == xml.getDimensionLength(CiftiXML::ALONG_ROW)) { isWriteFullRow = true; } else { /* * Sort by fiber orientation index. */ std::sort(trajectories.begin(), trajectories.end(), FiberTrajectoryComparison()); } std::vector fiberIndices; std::vector fiberFractions; int ctr = 0; for (std::vector::const_iterator iter = trajectories.begin(); iter != trajectories.end(); iter++) { const FiberOrientationTrajectory* fot = *iter; std::vector proportions = fot->getFiberFractions(); if (proportions.size() < 3) { proportions.resize(3, 0.0); } const float totalCount = fot->getFiberFractionTotalCount(); FiberFractions ff; ff.totalCount = totalCount; //(totalCount + 0.5); ff.distance = fot->getFiberFractionDistance(); ff.fiberFractions = proportions; fiberIndices.push_back(fot->getFiberOrientationIndex()); fiberFractions.push_back(ff); // // for (int64_t i = 0; i < 3; i++) { // if (vec[i] < -0.002f) { // std::cout << "Fiber " << ctr << vec[i] << std::endl; // } // } ctr++; } /* * Write to temp file!!!!! */ CiftiScalarsMap tempMap; tempMap.setLength(1); tempMap.setMapName(0, m_loadedDataDescriptionForMapName); xml.setMap(CiftiXML::ALONG_COLUMN, tempMap); CaretSparseFileWriter sparseWriter(filename, xml); const int64_t rowIndex = 0; if (isWriteFullRow) { sparseWriter.writeFibersRow(rowIndex, &fiberFractions[0]); } else { sparseWriter.writeFibersRowSparse(rowIndex, fiberIndices, fiberFractions); } sparseWriter.finish(); } /** * Clear the loaded fiber orientations. */ void CiftiFiberTrajectoryFile::clearLoadedFiberOrientations() { const int64_t numFibers = static_cast(m_fiberOrientationTrajectories.size()); for (int64_t i = 0; i < numFibers; i++) { delete m_fiberOrientationTrajectories[i]; } m_fiberOrientationTrajectories.clear(); m_loadedDataDescriptionForMapName = ""; m_loadedDataDescriptionForFileCopy = ""; m_connectivityDataLoaded->reset(); } /** * Validate that the assigned matching fiber orientation file is valid * (not NULL and row/column is compatible). * * @throws DataFileException * If fiber orientation file is NULL or incompatible. */ void CiftiFiberTrajectoryFile::validateAssignedMatchingFiberOrientationFile() { if (m_sparseFile == NULL) { throw DataFileException(getFileName(), "No data has been loaded."); } if (m_matchingFiberOrientationFile == NULL) { throw DataFileException(getFileName(), "No fiber orientation file is assigned."); } const CiftiXML& trajXML = m_sparseFile->getCiftiXML(); const CiftiXML* orientXML = m_matchingFiberOrientationFile->getCiftiXML(); if (*(trajXML.getMap(CiftiXML::ALONG_ROW)) != *(orientXML->getMap(CiftiXML::ALONG_COLUMN))) { QString msg = ("Row to Columns do not match: rows=" + QString::number(trajXML.getDimensionLength(CiftiXML::ALONG_COLUMN)) + " cols=" + QString::number(trajXML.getDimensionLength(CiftiXML::ALONG_ROW)) + " " + m_matchingFiberOrientationFile->getFileNameNoPath() + " rows=" + QString::number(orientXML->getDimensionLength(CiftiXML::ALONG_COLUMN)) + " cols=" + QString::number(orientXML->getDimensionLength(CiftiXML::ALONG_ROW))); throw DataFileException(getFileName(), msg); } } /** * Get the brainordinate from the given row. * * @param rowIndex * Index of the row. * @param surfaceStructureOut * Will contain structure of surface if row is a surface node. * @param surfaceNodeIndexOut * Will contain index of surface node if row is a surface node. * @param surfaceNumberOfNodesOut * Will contain surfaces number of nodes if row is a surface node. * @param surfaceNodeValidOut * Will be true upon exit if the row corresponded to a surface node. * @param voxelIJKOut * Will contain the voxel's IJK indices if row is a surface node. * @param voxelXYZOut * Will contain the voxel's XYZ coordinate if row is a surface node. * @param voxelValidOut * Will be true upon exit if the row corresponded to a surface node. * @throw DataFileException * If the rows are not for brainordinates or the row index is invalid. */ void CiftiFiberTrajectoryFile::getBrainordinateFromRowIndex(const int64_t rowIndex, StructureEnum::Enum& surfaceStructureOut, int32_t& surfaceNodeIndexOut, int32_t& surfaceNumberOfNodesOut, bool& surfaceNodeValidOut, int64_t voxelIJKOut[3], float voxelXYZOut[3], bool& voxelValidOut) const { surfaceNodeValidOut = false; voxelValidOut = false; if (m_sparseFile == NULL) { return; } const CiftiXML& ciftiXML = m_sparseFile->getCiftiXML(); if (ciftiXML.getMappingType(CiftiXML::ALONG_COLUMN) != CiftiMappingType::BRAIN_MODELS) { throw DataFileException(getFileName(), "File does not have brainordinate data for rows."); return; } const CiftiBrainModelsMap& brainMap = ciftiXML.getBrainModelsMap(CiftiXML::ALONG_COLUMN); const int numRows = ciftiXML.getDimensionLength(CiftiXML::ALONG_COLUMN); if ((rowIndex < 0) || (rowIndex >= numRows)) { throw DataFileException(getFileName(), "Row index " + AString::number(rowIndex) + " is invalid. Number of rows is " + AString::number(numRows)); } const CiftiBrainModelsMap::IndexInfo indexInfo = brainMap.getInfoForIndex(rowIndex); switch (indexInfo.m_type) { case CiftiBrainModelsMap::SURFACE: surfaceStructureOut = indexInfo.m_structure; surfaceNodeIndexOut = indexInfo.m_surfaceNode; surfaceNumberOfNodesOut = brainMap.getSurfaceNumberOfNodes(surfaceStructureOut); surfaceNodeValidOut = true; break; case CiftiBrainModelsMap::VOXELS: { const VolumeSpace& colSpace = brainMap.getVolumeSpace(); voxelIJKOut[0] = indexInfo.m_ijk[0]; voxelIJKOut[1] = indexInfo.m_ijk[1]; voxelIJKOut[2] = indexInfo.m_ijk[2]; colSpace.indexToSpace(voxelIJKOut, voxelXYZOut); voxelValidOut = true; } break; } } /** * Load data for the given surface node. * @param structure * Structure in which surface node is located. * @param surfaceNumberOfNodes * Number of nodes in the surface. * @param nodeIndex * Index of the surface node. * @return * Index of row that was loaded or -1 if no data was found for node. */ int64_t CiftiFiberTrajectoryFile::loadDataForSurfaceNode(const StructureEnum::Enum structure, const int32_t surfaceNumberOfNodes, const int32_t nodeIndex) { switch (m_fiberTrajectoryFileType) { case FIBER_TRAJECTORY_LOAD_BY_BRAINORDINATE: break; case FIBER_TRAJECTORY_LOAD_SINGLE_ROW: return -1; break; } if (m_dataLoadingEnabled == false) { return -1; } clearLoadedFiberOrientations(); validateAssignedMatchingFiberOrientationFile(); const CiftiXML& trajXML = m_sparseFile->getCiftiXML(); const CiftiBrainModelsMap& colMap = trajXML.getBrainModelsMap(CiftiXML::ALONG_COLUMN); if (colMap.hasSurfaceData(structure) == false) { return -1; } if (colMap.getSurfaceNumberOfNodes(structure) != surfaceNumberOfNodes) { return -1; } const int64_t rowIndex = colMap.getIndexForNode(nodeIndex, structure); if (rowIndex < 0) { return -1; } std::vector fiberIndices; std::vector fiberFractions; bool rowTest = false; if (rowTest) { /* * Test loading a full row instead of sparse. */ const int numCols = trajXML.getDimensionLength(CiftiXML::ALONG_ROW); fiberFractions.resize(numCols); m_sparseFile->getFibersRow(rowIndex, &fiberFractions[0]); for (int64_t i = 0; i < numCols; i++) { fiberIndices.push_back(i); } } else { m_sparseFile->getFibersRowSparse(rowIndex, fiberIndices, fiberFractions); } CaretAssert(fiberIndices.size() == fiberFractions.size()); const int64_t numFibers = static_cast(fiberIndices.size()); CaretLogFine("For node " + AString::number(nodeIndex) + " number of rows loaded: " + AString::number(numFibers)); if (numFibers > 0) { m_fiberOrientationTrajectories.reserve(numFibers); for (int64_t iFiber = 0; iFiber < numFibers; iFiber++) { const int64_t numFiberOrientations = m_matchingFiberOrientationFile->getNumberOfFiberOrientations(); const int64_t fiberIndex = fiberIndices[iFiber]; if (fiberIndex < numFiberOrientations) { const FiberOrientation* fiberOrientation = m_matchingFiberOrientationFile->getFiberOrientations(fiberIndex); FiberOrientationTrajectory* fot = new FiberOrientationTrajectory(fiberIndex, fiberOrientation); fot->setFiberFractions(fiberFractions[iFiber]); m_fiberOrientationTrajectories.push_back(fot); } else{ CaretLogSevere("Invalid index=" + QString::number(fiberIndex) + " into fiber orientations"); } } m_loadedDataDescriptionForMapName = ("Row: " + AString::number(rowIndex) + ", Node Index: " + AString::number(nodeIndex) + ", Structure: " + StructureEnum::toName(structure)); m_loadedDataDescriptionForFileCopy = ("Row_" + AString::number(rowIndex)); m_connectivityDataLoaded->setSurfaceNodeLoading(structure, surfaceNumberOfNodes, nodeIndex, rowIndex, -1); } else { m_connectivityDataLoaded->reset(); return -1; } return rowIndex; } void CiftiFiberTrajectoryFile::finishFiberOrientationTrajectoriesAveraging() { for (std::vector::iterator iter = m_fiberOrientationTrajectories.begin(); iter != m_fiberOrientationTrajectories.end(); iter++) { FiberOrientationTrajectory* fot = *iter; fot->finishAveraging(); } } /** * Load average data for the given surface nodes. * * @param structure * Structure in which surface node is located. * @param surfaceNumberOfNodes * Number of nodes in surface. * @param nodeIndices * Indices of the surface nodes. */ void CiftiFiberTrajectoryFile::loadDataAverageForSurfaceNodes(const StructureEnum::Enum structure, const int32_t surfaceNumberOfNodes, const std::vector& nodeIndices) { switch (m_fiberTrajectoryFileType) { case FIBER_TRAJECTORY_LOAD_BY_BRAINORDINATE: break; case FIBER_TRAJECTORY_LOAD_SINGLE_ROW: return; break; } if (m_dataLoadingEnabled == false) { return; } clearLoadedFiberOrientations(); if (surfaceNumberOfNodes <= 0) { return; } validateAssignedMatchingFiberOrientationFile(); const CiftiXML& trajXML = m_sparseFile->getCiftiXML(); const CiftiBrainModelsMap& colMap = trajXML.getBrainModelsMap(CiftiXML::ALONG_COLUMN); if (colMap.hasSurfaceData(structure) == false) { return; } if (colMap.getSurfaceNumberOfNodes(structure) != surfaceNumberOfNodes) { return; } /* * This map uses the index of a fiber orientation (from the Fiber Orientation File) * to a FiberOrientationTrajectory instance. For averaging, items that have * a matching fiber orientation index are averaged. */ std::map fiberOrientationIndexMapToFiberTrajectory; std::vector rowIndicesToLoad; const int32_t numberOfNodes = static_cast(nodeIndices.size()); for (int32_t i = 0; i < numberOfNodes; i++) { const int32_t nodeIndex = nodeIndices[i]; /* * Get and load row for node */ const int64_t rowIndex = colMap.getIndexForNode(nodeIndex, structure); if (rowIndex >= 0) { rowIndicesToLoad.push_back(rowIndex); } } if (loadRowsForAveraging(rowIndicesToLoad)) { m_connectivityDataLoaded->setSurfaceAverageNodeLoading(structure, surfaceNumberOfNodes, nodeIndices); m_loadedDataDescriptionForMapName = ("Structure: " + StructureEnum::toName(structure) + ", Averaged Node Count: " + AString::number(numberOfNodes)); m_loadedDataDescriptionForFileCopy = ("Averaged_Node_Count_" + AString::number(numberOfNodes)); } } /** * Load the given rows for averaging. * * @param rowIndices * Indices of rows for averaging. * @return * True if data was loaded else false if no data or user cancelled. * @throw * DataFileException if there is an error. */ bool CiftiFiberTrajectoryFile::loadRowsForAveraging(const std::vector& rowIndices) { const CiftiXML& trajXML = m_sparseFile->getCiftiXML(); const int64_t numberOfColumns = trajXML.getDimensionLength(CiftiXML::ALONG_ROW); std::vector fiberFractionsForRowVector(numberOfColumns); FiberFractions* fiberFractionsForRow = &fiberFractionsForRowVector[0]; const int64_t numberOfRowsToLoad = static_cast(rowIndices.size()); if (numberOfRowsToLoad <= 0) { return false; } const int32_t progressUpdateInterval = 1; EventProgressUpdate progressEvent(0, numberOfRowsToLoad, 0, ("Loading data for " + QString::number(numberOfRowsToLoad) + " brainordinates in file ") + getFileNameNoPath()); EventManager::get()->sendEvent(progressEvent.getPointer()); for (int64_t iCol = 0; iCol < numberOfColumns; iCol++) { const FiberOrientation* fiberOrientation = m_matchingFiberOrientationFile->getFiberOrientations(iCol); CaretAssert(fiberOrientation); m_fiberOrientationTrajectories.push_back(new FiberOrientationTrajectory(iCol, fiberOrientation)); } bool userCancelled = false; for (int64_t iRow = 0; iRow < numberOfRowsToLoad; iRow++) { const int64_t rowIndex = rowIndices[iRow]; if ((iRow % progressUpdateInterval) == 0) { progressEvent.setProgress(iRow, ""); EventManager::get()->sendEvent(progressEvent.getPointer()); if (progressEvent.isCancelled()) { userCancelled = true; break; } } m_sparseFile->getFibersRow(rowIndex, fiberFractionsForRow); for (int64_t iCol = 0; iCol < numberOfColumns; iCol++) { FiberOrientationTrajectory* fot = m_fiberOrientationTrajectories[iCol]; fot->addFiberFractionsForAveraging(fiberFractionsForRow[iCol]); } } if (userCancelled) { clearLoadedFiberOrientations(); return false; } finishFiberOrientationTrajectoriesAveraging(); return true; } /** * Load data for a voxel at the given coordinate. * * @param xyz * Coordinate of voxel. * @return * Index of row that was loaded or -1 if no data was found for coordinate. * @throw * DataFileException if there is an error. */ int64_t CiftiFiberTrajectoryFile::loadMapDataForVoxelAtCoordinate(const float xyz[3]) { m_connectivityDataLoaded->reset(); switch (m_fiberTrajectoryFileType) { case FIBER_TRAJECTORY_LOAD_BY_BRAINORDINATE: break; case FIBER_TRAJECTORY_LOAD_SINGLE_ROW: return -1; break; } if (m_dataLoadingEnabled == false) { return -1; } clearLoadedFiberOrientations(); validateAssignedMatchingFiberOrientationFile(); const CiftiXML& trajXML = m_sparseFile->getCiftiXML(); const CiftiBrainModelsMap& colMap = trajXML.getBrainModelsMap(CiftiXML::ALONG_COLUMN); if (!colMap.hasVolumeData()) return -1; const VolumeSpace& colSpace = colMap.getVolumeSpace(); int64_t ijk[3]; colSpace.enclosingVoxel(xyz, ijk); const int64_t rowIndex = colMap.getIndexForVoxel(ijk); if (rowIndex < 0) { return -1; } std::vector fiberIndices; std::vector fiberFractions; m_sparseFile->getFibersRowSparse(rowIndex, fiberIndices, fiberFractions); CaretAssert(fiberIndices.size() == fiberFractions.size()); const int64_t numFibers = static_cast(fiberIndices.size()); CaretLogFine("For voxel at coordinate " + AString::fromNumbers(xyz, 3, ",") + " number of rows loaded: " + AString::number(numFibers)); if (numFibers > 0) { m_fiberOrientationTrajectories.reserve(numFibers); for (int64_t iFiber = 0; iFiber < numFibers; iFiber++) { const int64_t numFiberOrientations = m_matchingFiberOrientationFile->getNumberOfFiberOrientations(); const int64_t fiberIndex = fiberIndices[iFiber]; if (fiberIndex < numFiberOrientations) { const FiberOrientation* fiberOrientation = m_matchingFiberOrientationFile->getFiberOrientations(fiberIndex); FiberOrientationTrajectory* fot = new FiberOrientationTrajectory(fiberIndex, fiberOrientation); fot->setFiberFractions(fiberFractions[iFiber]); m_fiberOrientationTrajectories.push_back(fot); } else{ CaretLogSevere("Invalid index=" + QString::number(fiberIndex) + " into fiber orientations"); } } m_loadedDataDescriptionForMapName = ("Row: " + AString::number(rowIndex) + ", Voxel XYZ: " + AString::fromNumbers(xyz, 3, ",") + ", Structure: "); m_loadedDataDescriptionForFileCopy = ("Row_" + AString::number(rowIndex)); m_connectivityDataLoaded->setVolumeXYZLoading(xyz, rowIndex, -1); } else { return -1; } return rowIndex; } /** * Load connectivity data for the voxel indices and then average the data. * * @param volumeDimensionIJK * Dimensions of the volume. * @param voxelIndices * Indices of voxels. * @throw * DataFileException if there is an error. */ void CiftiFiberTrajectoryFile::loadMapAverageDataForVoxelIndices(const int64_t volumeDimensionIJK[3], const std::vector& voxelIndices) { switch (m_fiberTrajectoryFileType) { case FIBER_TRAJECTORY_LOAD_BY_BRAINORDINATE: break; case FIBER_TRAJECTORY_LOAD_SINGLE_ROW: return; break; } if (m_dataLoadingEnabled == false) { return; } clearLoadedFiberOrientations(); validateAssignedMatchingFiberOrientationFile(); const CiftiXML& trajXML = m_sparseFile->getCiftiXML(); const CiftiBrainModelsMap& colMap = trajXML.getBrainModelsMap(CiftiXML::ALONG_COLUMN); if (colMap.hasVolumeData() == false) { return; } std::vector rowIndicesToLoad; const int32_t numberOfVoxels = static_cast(voxelIndices.size()); for (int32_t i = 0; i < numberOfVoxels; i++) { /* * Get and load row for voxel */ const int64_t rowIndex = colMap.getIndexForVoxel(voxelIndices[i].m_ijk); if (rowIndex >= 0) { rowIndicesToLoad.push_back(rowIndex); } } if (loadRowsForAveraging(rowIndicesToLoad)) { m_connectivityDataLoaded->setVolumeAverageVoxelLoading(volumeDimensionIJK, voxelIndices); m_loadedDataDescriptionForMapName = ("Averaged Voxel Count: " + AString::number(numberOfVoxels)); m_loadedDataDescriptionForFileCopy = ("Average_Voxel_Count_" + AString::number(numberOfVoxels)); } } /** * Load the given row index from the file even if the file is disabled for data loading * * @param rowIndex * Index of row that is loaded. * @throw DataFileException * If an error occurs. */ void CiftiFiberTrajectoryFile::loadDataForRowIndex(const int64_t rowIndex) { clearLoadedFiberOrientations(); validateAssignedMatchingFiberOrientationFile(); std::vector fiberIndices; std::vector fiberFractions; m_sparseFile->getFibersRowSparse(rowIndex, fiberIndices, fiberFractions); CaretAssert(fiberIndices.size() == fiberFractions.size()); const int64_t numFibers = static_cast(fiberIndices.size()); if (numFibers > 0) { m_fiberOrientationTrajectories.reserve(numFibers); for (int64_t iFiber = 0; iFiber < numFibers; iFiber++) { const int64_t numFiberOrientations = m_matchingFiberOrientationFile->getNumberOfFiberOrientations(); const int64_t fiberIndex = fiberIndices[iFiber]; if (fiberIndex < numFiberOrientations) { const FiberOrientation* fiberOrientation = m_matchingFiberOrientationFile->getFiberOrientations(fiberIndex); FiberOrientationTrajectory* fot = new FiberOrientationTrajectory(fiberIndex, fiberOrientation); fot->setFiberFractions(fiberFractions[iFiber]); m_fiberOrientationTrajectories.push_back(fot); } else{ CaretLogSevere("Invalid index=" + QString::number(fiberIndex) + " into fiber orientations"); } } m_loadedDataDescriptionForMapName = ("Row: " + AString::number(rowIndex)); m_loadedDataDescriptionForFileCopy = ("Row_" + AString::number(rowIndex)); m_connectivityDataLoaded->setRowColumnLoading(rowIndex, -1); } else { throw DataFileException(getFileName(), "Row " + AString::number(rowIndex) + " is invalid or contains no data."); } } /** * Finish restoration of scene. * In this file's circumstances, the fiber orientation files were not * available at the time the scene was restored. * * @throws DataFileException * If there was an error restoring the data. */ void CiftiFiberTrajectoryFile::finishRestorationOfScene() { /* * Loading of data may be disabled in the scene * so temporarily enabled loading and then * restore the status. */ const bool loadingEnabledStatus = isDataLoadingEnabled(); setDataLoadingEnabled(true); switch (m_connectivityDataLoaded->getMode()) { case ConnectivityDataLoaded::MODE_NONE: break; case ConnectivityDataLoaded::MODE_ROW: { int64_t rowIndex; int64_t columnIndex; m_connectivityDataLoaded->getRowColumnLoading(rowIndex, columnIndex); loadDataForRowIndex(rowIndex); } break; case ConnectivityDataLoaded::MODE_COLUMN: { /* * Never load by column !!! */ CaretAssertMessage(0, "Fiber Trajectory never loads by column."); } break; case ConnectivityDataLoaded::MODE_SURFACE_NODE: { StructureEnum::Enum structure; int32_t surfaceNumberOfNodes; int32_t surfaceNodeIndex; int64_t rowIndex; int64_t columnIndex; m_connectivityDataLoaded->getSurfaceNodeLoading(structure, surfaceNumberOfNodes, surfaceNodeIndex, rowIndex, columnIndex); loadDataForSurfaceNode(structure, surfaceNumberOfNodes, surfaceNodeIndex); } break; case ConnectivityDataLoaded::MODE_SURFACE_NODE_AVERAGE: { StructureEnum::Enum structure; int32_t surfaceNumberOfNodes; std::vector surfaceNodeIndices; m_connectivityDataLoaded->getSurfaceAverageNodeLoading(structure, surfaceNumberOfNodes, surfaceNodeIndices); loadDataAverageForSurfaceNodes(structure, surfaceNumberOfNodes, surfaceNodeIndices); } break; case ConnectivityDataLoaded::MODE_VOXEL_XYZ: { float volumeXYZ[3]; int64_t rowIndex; int64_t columnIndex; m_connectivityDataLoaded->getVolumeXYZLoading(volumeXYZ, rowIndex, columnIndex); loadMapDataForVoxelAtCoordinate(volumeXYZ); } break; case ConnectivityDataLoaded::MODE_VOXEL_IJK_AVERAGE: { int64_t volumeDimensionsIJK[3]; std::vector voxelIndicesIJK; m_connectivityDataLoaded->getVolumeAverageVoxelLoading(volumeDimensionsIJK, voxelIndicesIJK); loadMapAverageDataForVoxelIndices(volumeDimensionsIJK, voxelIndicesIJK); } break; } setDataLoadingEnabled(loadingEnabledStatus); } /** * @return a REFERENCE to the fiber fractions that were loaded. */ const std::vector& CiftiFiberTrajectoryFile::getLoadedFiberOrientationTrajectories() const { return m_fiberOrientationTrajectories; } /** * Save file data from the scene. For subclasses that need to * save to a scene, this method should be overriden. sceneClass * will be valid and any scene data should be added to it. * * @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. */ void CiftiFiberTrajectoryFile::saveFileDataToScene(const SceneAttributes* sceneAttributes, SceneClass* sceneClass) { CaretMappableDataFile::saveFileDataToScene(sceneAttributes, sceneClass); m_sceneAssistant->saveMembers(sceneAttributes, sceneClass); } /** * Restore file data from the scene. For subclasses that need to * restore from a scene, this method should be overridden. The scene class * will be valid and any scene data may be obtained from it. * * @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 CiftiFiberTrajectoryFile::restoreFileDataFromScene(const SceneAttributes* sceneAttributes, const SceneClass* sceneClass) { m_connectivityDataLoaded->reset(); m_matchingFiberOrientationFile = NULL; m_matchingFiberOrientationFileName = ""; CaretMappableDataFile::restoreFileDataFromScene(sceneAttributes, sceneClass); m_sceneAssistant->restoreMembers(sceneAttributes, sceneClass); m_matchingFiberOrientationFileNameFromRestoredScene = m_matchingFiberOrientationFileName; } /** * @return True if this file type supports writing, else false. * * Fiber trajectory files do NOT support writing. */ bool CiftiFiberTrajectoryFile::supportsWriting() const { switch (m_fiberTrajectoryFileType) { case FIBER_TRAJECTORY_LOAD_BY_BRAINORDINATE: break; case FIBER_TRAJECTORY_LOAD_SINGLE_ROW: return true; break; } return false; } /** * Add information about the file to the data file information. * * @param dataFileInformation * Consolidates information about a data file. */ void CiftiFiberTrajectoryFile::addToDataFileContentInformation(DataFileContentInformation& dataFileInformation) { CaretMappableDataFile::addToDataFileContentInformation(dataFileInformation); if (m_sparseFile != NULL) { const CiftiXML& ciftiXML = m_sparseFile->getCiftiXML(); const CiftiBrainModelsMap& colMap = ciftiXML.getBrainModelsMap(CiftiXML::ALONG_COLUMN); //ciftiXML.getVoxelInfoInDataFileContentInformation(CiftiXML::ALONG_COLUMN, // dataFileInformation); if (colMap.hasVolumeData()) { VolumeSpace volumeSpace = colMap.getVolumeSpace();//TSC: copied/reimplemented from CiftiXML Old - I don't think it belongs in CiftiXML or CiftiBrainModelsMap const int64_t* dims = volumeSpace.getDims(); dataFileInformation.addNameAndValue("Dimensions", AString::fromNumbers(dims, 3, ",")); VolumeSpace::OrientTypes orientation[3]; float spacing[3]; float origin[3]; volumeSpace.getOrientAndSpacingForPlumb(orientation, spacing, origin); dataFileInformation.addNameAndValue("Spacing", AString::fromNumbers(spacing, 3, ",")); dataFileInformation.addNameAndValue("Origin", AString::fromNumbers(origin, 3, ",")); const std::vector >& sform = volumeSpace.getSform(); for (uint32_t i = 0; i < sform.size(); i++) { dataFileInformation.addNameAndValue(("sform row " + AString::number(i)), AString::fromNumbers(sform[i], ",")); } std::vector volStructs = colMap.getVolumeStructureList(); for (int i = 0; i < (int)volStructs.size(); ++i) { std::vector voxels = colMap.getVolumeStructureMap(volStructs[i]); for (int j = 0; j < (int)voxels.size(); ++j) { float xyz[3]; volumeSpace.indexToSpace(voxels[i].m_ijk, xyz); const AString msg = ("ijk=(" + AString::fromNumbers(voxels[j].m_ijk, 3, ", ") + "), xyz=(" + AString::fromNumbers(xyz, 3, ", ") + "), row=" + AString::number(voxels[j].m_ciftiIndex) + " ");//TSC: huh? dataFileInformation.addNameAndValue(StructureEnum::toGuiName(volStructs[i]), msg);//TSC: huh? } } } CiftiMappableDataFile::addCiftiXmlToDataFileContentInformation(dataFileInformation, ciftiXML); } } connectome-workbench-1.2.3+git41-gc4c6c90/src/Files/CiftiFiberTrajectoryFile.h000066400000000000000000000233551300200146000266440ustar00rootroot00000000000000#ifndef __CIFTI_FIBER_TRAJECTORY_FILE__H_ #define __CIFTI_FIBER_TRAJECTORY_FILE__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 "BrainConstants.h" #include "CaretMappableDataFile.h" #include "CaretSparseFile.h" #include "DisplayGroupEnum.h" #include "SceneClassAssistant.h" #include "VoxelIJK.h" namespace caret { class CiftiFiberOrientationFile; class ConnectivityDataLoaded; class FiberOrientationTrajectory; class FiberTrajectoryMapProperties; class GiftiMetaData; class CiftiFiberTrajectoryFile : public CaretMappableDataFile { public: CiftiFiberTrajectoryFile(); virtual ~CiftiFiberTrajectoryFile(); virtual void clear(); bool isEmpty() const; virtual StructureEnum::Enum getStructure() const; virtual void setStructure(const StructureEnum::Enum structure); virtual GiftiMetaData* getFileMetaData(); virtual const GiftiMetaData* getFileMetaData() const; virtual bool isSurfaceMappable() const; virtual bool isVolumeMappable() const; virtual int32_t getNumberOfMaps() const; virtual bool hasMapAttributes() const; virtual AString getMapName(const int32_t mapIndex) const; virtual void setMapName(const int32_t mapIndex, const AString& mapName); virtual const GiftiMetaData* getMapMetaData(const int32_t mapIndex) const; virtual GiftiMetaData* getMapMetaData(const int32_t mapIndex); virtual AString getMapUniqueID(const int32_t mapIndex) const; virtual bool isMappedWithPalette() const; virtual const FastStatistics* getMapFastStatistics(const int32_t mapIndex); virtual const Histogram* getMapHistogram(const int32_t mapIndex); virtual const Histogram* getMapHistogram(const int32_t mapIndex, const float mostPositiveValueInclusive, const float leastPositiveValueInclusive, const float leastNegativeValueInclusive, const float mostNegativeValueInclusive, const bool includeZeroValues); virtual int64_t getDataSizeUncompressedInBytes() const; virtual const FastStatistics* getFileFastStatistics(); virtual const Histogram* getFileHistogram(); virtual const Histogram* getFileHistogram(const float mostPositiveValueInclusive, const float leastPositiveValueInclusive, const float leastNegativeValueInclusive, const float mostNegativeValueInclusive, const bool includeZeroValues); virtual PaletteColorMapping* getMapPaletteColorMapping(const int32_t mapIndex); virtual const PaletteColorMapping* getMapPaletteColorMapping(const int32_t mapIndex) const; virtual bool isMappedWithLabelTable() const; virtual GiftiLabelTable* getMapLabelTable(const int32_t mapIndex); virtual const GiftiLabelTable* getMapLabelTable(const int32_t mapIndex) const; virtual void getPaletteNormalizationModesSupported(std::vector& modesSupportedOut); virtual void updateScalarColoringForMap(const int32_t mapIndex, const PaletteFile* paletteFile); virtual void readFile(const AString& filename); virtual void writeFile(const AString& filename); void getBrainordinateFromRowIndex(const int64_t rowIndex, StructureEnum::Enum& surfaceStructureOut, int32_t& surfaceNodeIndexOut, int32_t& surfaceNumberOfNodesOut, bool& surfaceNodeValidOut, int64_t voxelIJKOut[3], float voxelXYZOut[3], bool& voxelValidOut) const; int64_t loadDataForSurfaceNode(const StructureEnum::Enum structure, const int32_t surfaceNumberOfNodes, const int32_t nodeIndex); void loadDataAverageForSurfaceNodes(const StructureEnum::Enum structure, const int32_t surfaceNumberOfNodes, const std::vector& nodeIndices); virtual int64_t loadMapDataForVoxelAtCoordinate(const float xyz[3]); virtual void loadMapAverageDataForVoxelIndices(const int64_t volumeDimensionIJK[3], const std::vector& voxelIndices); void loadDataForRowIndex(const int64_t rowIndex); const std::vector& getLoadedFiberOrientationTrajectories() const; void clearLoadedFiberOrientations(); FiberTrajectoryMapProperties* getFiberTrajectoryMapProperties(); const FiberTrajectoryMapProperties* getFiberTrajectoryMapProperties() const; bool isDataLoadingEnabled() const; void setDataLoadingEnabled(const bool loadingEnabled); CiftiFiberOrientationFile* getMatchingFiberOrientationFile(); const CiftiFiberOrientationFile* getMatchingFiberOrientationFile() const; bool isFiberOrientationFileCombatible(const CiftiFiberOrientationFile* fiberOrientationFile) const; void setMatchingFiberOrientationFile(CiftiFiberOrientationFile* matchingFiberOrientationFile); void updateMatchingFiberOrientationFileFromList(std::vector matchingFiberOrientationFiles); void finishRestorationOfScene(); bool supportsWriting() const; CiftiFiberTrajectoryFile* newFiberTrajectoryFileFromLoadedRowData(const AString& destinationDirectory, AString& errorMessageOut) const; void addToDataFileContentInformation(DataFileContentInformation& dataFileInformation); // ADD_NEW_METHODS_HERE private: CiftiFiberTrajectoryFile(const CiftiFiberTrajectoryFile&); CiftiFiberTrajectoryFile& operator=(const CiftiFiberTrajectoryFile&); protected: virtual void saveFileDataToScene(const SceneAttributes* sceneAttributes, SceneClass* sceneClass); virtual void restoreFileDataFromScene(const SceneAttributes* sceneAttributes, const SceneClass* sceneClass); private: /** * Type of fiber trajectory file */ enum FiberTrajectoryFileType { /** Load data by brainordinate */ FIBER_TRAJECTORY_LOAD_BY_BRAINORDINATE, /** Load single row (does not map to a brainordinate) */ FIBER_TRAJECTORY_LOAD_SINGLE_ROW }; bool loadRowsForAveraging(const std::vector& rowIndices); void clearPrivate(); void validateAssignedMatchingFiberOrientationFile(); void finishFiberOrientationTrajectoriesAveraging(); void writeLoadedDataToFile(const AString& filename) const; /** True if file supports loading of data by row */ FiberTrajectoryFileType m_fiberTrajectoryFileType; CaretSparseFile* m_sparseFile; GiftiMetaData* m_metadata; CiftiFiberOrientationFile* m_matchingFiberOrientationFile; AString m_matchingFiberOrientationFileName; AString m_matchingFiberOrientationFileNameFromRestoredScene; std::vector m_fiberOrientationTrajectories; FiberTrajectoryMapProperties* m_fiberTrajectoryMapProperties; bool m_dataLoadingEnabled; AString m_loadedDataDescriptionForFileCopy; AString m_loadedDataDescriptionForMapName; ConnectivityDataLoaded* m_connectivityDataLoaded; SceneClassAssistant* m_sceneAssistant; // ADD_NEW_MEMBERS_HERE }; #ifdef __CIFTI_FIBER_TRAJECTORY_FILE_DECLARE__ // #endif // __CIFTI_FIBER_TRAJECTORY_FILE_DECLARE__ } // namespace #endif //__CIFTI_FIBER_TRAJECTORY_FILE__H_ connectome-workbench-1.2.3+git41-gc4c6c90/src/Files/CiftiMappableConnectivityMatrixDataFile.cxx000066400000000000000000001503561300200146000322220ustar00rootroot00000000000000 /*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 __CIFTI_MAPPABLE_CONNECTIVITY_MATRIX_DATA_FILE_DECLARE__ #include "CiftiMappableConnectivityMatrixDataFile.h" #undef __CIFTI_MAPPABLE_CONNECTIVITY_MATRIX_DATA_FILE_DECLARE__ #include "CaretAssert.h" #include "CiftiFile.h" #include "CaretLogger.h" #include "ChartableMatrixParcelInterface.h" #include "ConnectivityDataLoaded.h" #include "DataFileException.h" #include "ElapsedTimer.h" #include "EventManager.h" #include "EventProgressUpdate.h" #include "SceneClass.h" #include "SceneClassAssistant.h" using namespace caret; /** * \class caret::CiftiMappableConnectivityMatrixDataFile * \brief Data file for Cifti Connectivity Matrix Files. * \ingroup Files */ /** * Constructor. */ CiftiMappableConnectivityMatrixDataFile::CiftiMappableConnectivityMatrixDataFile(const DataFileTypeEnum::Enum dataFileType) : CiftiMappableDataFile(dataFileType) { m_connectivityDataLoaded = new ConnectivityDataLoaded(); /* * This method initializes some members */ clearPrivate(); m_sceneAssistant = new SceneClassAssistant(); m_sceneAssistant->add("m_connectivityDataLoaded", "ConnectivityDataLoaded", m_connectivityDataLoaded); m_sceneAssistant->add("m_dataLoadingEnabled", &m_dataLoadingEnabled); } /** * Destructor. */ CiftiMappableConnectivityMatrixDataFile::~CiftiMappableConnectivityMatrixDataFile() { clearPrivate(); delete m_connectivityDataLoaded; delete m_sceneAssistant; } /** * Clear the contents of the file. */ void CiftiMappableConnectivityMatrixDataFile::clear() { CiftiMappableDataFile::clear(); clearPrivate(); } /** * Clear the contents of the file. * Note that "clear()" is virtual and cannot be called from destructor. */ void CiftiMappableConnectivityMatrixDataFile::clearPrivate() { m_loadedRowData.clear(); m_rowLoadedTextForMapName = ""; m_rowLoadedText = ""; m_dataLoadingEnabled = true; m_connectivityDataLoaded->reset(); m_chartLoadingDimension = ChartMatrixLoadingDimensionEnum::CHART_MATRIX_LOADING_BY_ROW; if (getDataFileType() == DataFileTypeEnum::CONNECTIVITY_DENSE_DYNAMIC) { m_chartLoadingDimension = ChartMatrixLoadingDimensionEnum::CHART_MATRIX_LOADING_BY_COLUMN; } } /** * @return Pointer to the information about last loaded connectivity data. */ const ConnectivityDataLoaded* CiftiMappableConnectivityMatrixDataFile::getConnectivityDataLoaded() const { return m_connectivityDataLoaded; } /** * Get the nodes for the parcel for the given structure that corresponds * to the last selected row. * * @param parcelNodesOut * Ouput containing the node indices. * @param structure * The surface structure. */ bool CiftiMappableConnectivityMatrixDataFile::getParcelNodesElementForSelectedParcel(std::set &parcelNodesOut, const StructureEnum::Enum &structure) const { bool validFlag = false; int64_t rowIndex = -1; int64_t columnIndex = -1; m_connectivityDataLoaded->getRowColumnLoading(rowIndex, columnIndex); if (rowIndex >= 0) { validFlag = CiftiMappableDataFile::getParcelNodesElementForSelectedParcel(parcelNodesOut, structure, rowIndex); } return validFlag; } /** * @return Is this file empty? */ bool CiftiMappableConnectivityMatrixDataFile::isEmpty() const { if (CiftiMappableDataFile::isEmpty()) { return true; } return false; } /** * @return Is loading of data enabled. Note that if * disabled, any previously loaded data is NOT removed * so that it can still be displayed but not updated. */ bool CiftiMappableConnectivityMatrixDataFile::isMapDataLoadingEnabled(const int32_t /*mapIndex*/) const { return m_dataLoadingEnabled; } /** * Set loading of data enabled. Note that if * disabled, any previously loaded data is NOT removed * so that it can still be displayed but not updated. * * @param dataLoadingEnabled * New data loading enabled status. */ void CiftiMappableConnectivityMatrixDataFile::setMapDataLoadingEnabled(const int32_t /*mapIndex*/, const bool dataLoadingEnabled) { m_dataLoadingEnabled = dataLoadingEnabled; } /** * Get the data for the given map index. * * @param mapIndex * Index of the map. * @param dataOut * A vector that will contain the data for the map upon exit. */ void CiftiMappableConnectivityMatrixDataFile::getMapData(const int32_t /*mapIndex*/, std::vector& dataOut) const { if (!isEnabledAsLayer()) {//TSC: HACK to make identification show empty string instead of number when dynconn is used, then set to not load, not layer dataOut.clear(); } else { dataOut = m_loadedRowData; } } /** * Get the index of a row or column when loading data for a surface node. * * @param structure * Structure of the surface. * @param surfaceNumberOfNodes * Number of nodes in the surface. * @param nodeIndex * Index of the node. * @param rowIndexOut * Index of row corresponding to node or -1 if no row in the * matrix corresponds to the node. * @param columnIndexOut * Index of column corresponding to node or -1 if no column in the * matrix corresponds to the node. */ void CiftiMappableConnectivityMatrixDataFile::getRowColumnIndexForNodeWhenLoading(const StructureEnum::Enum structure, const int64_t surfaceNumberOfNodes, const int64_t nodeIndex, int64_t& rowIndexOut, int64_t& columnIndexOut) { rowIndexOut = -1; columnIndexOut = -1; if (m_ciftiFile == NULL) { return; } const CiftiXML& ciftiXML = m_ciftiFile->getCiftiXML(); const int32_t ciftiDirection = getCifitDirectionForLoadingRowOrColumn(); /* * Make sure number of nodes matches. */ switch (ciftiXML.getMappingType(ciftiDirection)) { case CiftiMappingType::BRAIN_MODELS: if (ciftiXML.getBrainModelsMap(ciftiDirection).getSurfaceNumberOfNodes(structure) != surfaceNumberOfNodes) return; break; case CiftiMappingType::PARCELS: if (ciftiXML.getParcelsMap(ciftiDirection).getSurfaceNumberOfNodes(structure) != surfaceNumberOfNodes) return; break; default: return; } /* * Get the mapping type */ const CiftiMappingType::MappingType rowMappingType = ciftiXML.getMappingType(ciftiDirection); int64_t rowOrColumnIndex = -1; /* * Get the row/column index for the node. */ switch (rowMappingType) { case CiftiMappingType::BRAIN_MODELS: rowOrColumnIndex = ciftiXML.getBrainModelsMap(ciftiDirection).getIndexForNode(nodeIndex, structure); break; case CiftiMappingType::PARCELS: rowOrColumnIndex = ciftiXML.getParcelsMap(ciftiDirection).getIndexForNode(nodeIndex, structure); break; case CIFTI_INDEX_TYPE_SCALARS: break; case CIFTI_INDEX_TYPE_TIME_POINTS: break; default: CaretAssert(0); CaretLogSevere("Invalid row mapping type for connectivity file " + DataFileTypeEnum::toName(getDataFileType())); break; } switch (ciftiDirection) { case CiftiXML::ALONG_COLUMN: rowIndexOut = rowOrColumnIndex; break; case CiftiXML::ALONG_ROW: columnIndexOut = rowOrColumnIndex; break; default: CaretAssert(0); break; } } /** * Get the indices for rows or columns when loading data for a surface nodes. * * @param structure * Structure of the surface. * @param surfaceNumberOfNodes * Number of nodes in the surface. * @param nodeIndices * Indices of the node. * @param rowIndicesOut * Indices of row corresponding to node or empty if no data for rows. * @param columnIndicesOut * Index of column corresponding to node or empty if no data for columns */ void CiftiMappableConnectivityMatrixDataFile::getRowColumnIndicesForNodesWhenLoading(const StructureEnum::Enum structure, const int64_t surfaceNumberOfNodes, const std::vector& nodeIndices, std::vector& rowIndicesOut, std::vector& columnIndicesOut) { rowIndicesOut.clear(); columnIndicesOut.clear(); for (std::vector::const_iterator iter = nodeIndices.begin(); iter != nodeIndices.end(); iter++) { int64_t columnIndex = -1; int64_t rowIndex = -1; getRowColumnIndexForNodeWhenLoading(structure, surfaceNumberOfNodes, *iter, rowIndex, columnIndex); if (rowIndex >= 0) { rowIndicesOut.push_back(rowIndex); } else if (columnIndex >= 0) { columnIndicesOut.push_back(columnIndex); } } } /** * Get the average for or column for the given row/column indices. * * @param rowIndices * Indices of the row. * @param columnIndices * Indices of the column. * @param rowAverageOut * Average values for rows. * @param columnAverageOut * Average value for columns. */ void CiftiMappableConnectivityMatrixDataFile::getRowColumnAverageForIndices(const std::vector& rowIndices, const std::vector& columnIndices, std::vector& rowAverageOut, std::vector& columnAverageOut) { columnAverageOut.clear(); rowAverageOut.clear(); int64_t dataLength = 0; std::vector indices; bool doRowsFlag = false; if (! rowIndices.empty()) { dataLength = m_ciftiFile->getNumberOfColumns(); indices = rowIndices; doRowsFlag = true; } else if ( ! columnIndices.empty()) { dataLength = m_ciftiFile->getNumberOfRows(); indices = columnIndices; } const int64_t numIndices = static_cast(indices.size()); if (numIndices > 0) { std::vector sum(dataLength, 0.0); std::vector data(dataLength); for (std::vector::const_iterator iter = indices.begin(); iter != indices.end(); iter++) { if (doRowsFlag) { getDataForRow(&data[0], *iter); } else { getDataForColumn(&data[0], *iter); } for (int64_t i = 0; i < dataLength; i++) { CaretAssertVectorIndex(sum, i); CaretAssertVectorIndex(data, i); sum[i] += data[i]; } } CaretAssert(dataLength > 0); std::vector average(dataLength); const float floatNumIndices = numIndices; //dataLength; for (int64_t i = 0; i < dataLength; i++) { CaretAssertVectorIndex(average, i); CaretAssertVectorIndex(sum, i); average[i] = sum[i] / floatNumIndices; } if (doRowsFlag) { rowAverageOut = average; } else { columnAverageOut = average; } } } /** * @return The CIFTI Direction (ALONG_COLUMN, ALONG_ROW) for loading data. */ int32_t CiftiMappableConnectivityMatrixDataFile::getCifitDirectionForLoadingRowOrColumn() { /* * Default to loading by row (ALONG_COLUMN) which is how the files normally load. */ int32_t ciftiDirection = CiftiXML::ALONG_COLUMN; /** * Parcel File is able to load by either and implements the * ChartableMatrixInterface. */ ChartableMatrixParcelInterface* matrixParcelInterface = dynamic_cast(this); if (matrixParcelInterface != NULL) { switch (matrixParcelInterface->getMatrixLoadingDimension()) { case ChartMatrixLoadingDimensionEnum::CHART_MATRIX_LOADING_BY_COLUMN: ciftiDirection = CiftiXML::ALONG_ROW; break; case ChartMatrixLoadingDimensionEnum::CHART_MATRIX_LOADING_BY_ROW: ciftiDirection = CiftiXML::ALONG_COLUMN; break; } } return ciftiDirection; } /** * Get the index of a row or column when loading data for a voxel at a coordinate. * @param xyz * Coordinate of the voxel. * @param rowIndexOut * Index of row corresponding to voxel or -1 if no row in the * matrix corresponds to the voxel. * @param columnIndexOut * Index of column corresponding to voxel or -1 if no column in the * matrix corresponds to the voxel. */ void CiftiMappableConnectivityMatrixDataFile::getRowColumnIndexForVoxelAtCoordinateWhenLoading(const float xyz[3], int64_t& rowIndexOut, int64_t& columnIndexOut) { rowIndexOut = -1; columnIndexOut = -1; if (m_ciftiFile == NULL) { return; } int64_t ijk[3]; enclosingVoxel(xyz[0], xyz[1], xyz[2], ijk[0], ijk[1], ijk[2]); return getRowColumnIndexForVoxelIndexWhenLoading(ijk, rowIndexOut, columnIndexOut); } /** * Get the index of a row or column when loading data for a voxel index. * @param ijk * Indicies of the voxel. * @param rowIndexOut * Index of row corresponding to voxel or -1 if no row in the * matrix corresponds to the voxel. * @param columnIndexOut * Index of column corresponding to voxel or -1 if no column in the * matrix corresponds to the voxel. */ void CiftiMappableConnectivityMatrixDataFile::getRowColumnIndexForVoxelIndexWhenLoading(const int64_t ijk[3], int64_t& rowIndexOut, int64_t& columnIndexOut) { rowIndexOut = -1; columnIndexOut = -1; if (m_ciftiFile == NULL) { return; } const CiftiXML& ciftiXML = m_ciftiFile->getCiftiXML(); const int32_t ciftiDirection = getCifitDirectionForLoadingRowOrColumn(); const CiftiMappingType::MappingType rowMappingType = ciftiXML.getMappingType(CiftiXML::ALONG_COLUMN); int64_t rowOrColumnIndex = -1; /* * Get the mapping type */ if (indexValid(ijk[0], ijk[1], ijk[2])) { switch (rowMappingType) { case CIFTI_INDEX_TYPE_BRAIN_MODELS: rowOrColumnIndex = ciftiXML.getBrainModelsMap(ciftiDirection).getIndexForVoxel(ijk); break; case CIFTI_INDEX_TYPE_PARCELS: rowOrColumnIndex = ciftiXML.getParcelsMap(ciftiDirection).getIndexForVoxel(ijk); break; default: CaretAssert(0); CaretLogSevere("Invalid row mapping type for connectivity file " + DataFileTypeEnum::toName(getDataFileType())); break; } } switch (ciftiDirection) { case CiftiXML::ALONG_COLUMN: rowIndexOut = rowOrColumnIndex; break; case CiftiXML::ALONG_ROW: columnIndexOut = rowOrColumnIndex; break; default: CaretAssert(0); break; } } /** * Set the loaded row data to zeros. */ void CiftiMappableConnectivityMatrixDataFile::setLoadedRowDataToAllZeros() { if ( ! m_loadedRowData.empty()){ std::fill(m_loadedRowData.begin(), m_loadedRowData.end(), 0.0); } updateForChangeInMapDataWithMapIndex(0); m_connectivityDataLoaded->reset(); m_rowLoadedText.clear(); m_rowLoadedTextForMapName.clear(); } /** * Reset the loaded row data to empty. * * Resets the loaded row data to zero elements and also * clears loaded data attributes. * * One usage of this method is when a parcel connectivity file has * its loading direction changed between row and parcel loading. */ void CiftiMappableConnectivityMatrixDataFile::resetLoadedRowDataToEmpty() { m_loadedRowData.clear(); setLoadedRowDataToAllZeros(); } /** * Load raw data for the given column. * * @param dataOut * Output with data. * @param index of the column. */ void CiftiMappableConnectivityMatrixDataFile::getDataForColumn(float* dataOut, const int64_t& index) const { m_ciftiFile->getColumn(dataOut, index); } /** * Load data for the given row. * * @param dataOut * Output with data. * @param index of the row. */ void CiftiMappableConnectivityMatrixDataFile::getDataForRow(float* dataOut, const int64_t& index) const { m_ciftiFile->getRow(dataOut, index); } /** * Load PROCESSED data for the given column. * * Some file types may have special processing for a column. This method can be * overridden for those types of files. * * @param dataOut * Output with data. * @param index of the column. */ void CiftiMappableConnectivityMatrixDataFile::getProcessedDataForColumn(float* dataOut, const int64_t& index) const { m_ciftiFile->getColumn(dataOut, index); } /** * Load PROCESSED data for the given row. * * Some file types may have special processing for a row. This method can be * overridden for those types of files. * * @param dataOut * Output with data. * @param index of the row. */ void CiftiMappableConnectivityMatrixDataFile::getProcessedDataForRow(float* dataOut, const int64_t& index) const { m_ciftiFile->getRow(dataOut, index); } /** * Some file types may perform additional processing of row average data and * can override this method. * * @param rowAverageData * The row average data. */ void CiftiMappableConnectivityMatrixDataFile::processRowAverageData(std::vector& /*rowAverageData*/) { /* This method may be overridden by subclasses */ } /** * Load the given row from the file even if the file is disabled. * * NOTE: Afterwards, it will be necessary to update this file's color mapping * with updateScalarColoringForMap(). * * * @param rowIndex * Index of row that is loaded. * @throw DataFileException * If an error occurs. */ void CiftiMappableConnectivityMatrixDataFile::loadDataForRowIndex(const int64_t rowIndex) { setLoadedRowDataToAllZeros(); const int64_t dataCount = m_ciftiFile->getNumberOfColumns(); if (dataCount > 0) { if ((rowIndex >= 0) && (rowIndex < m_ciftiFile->getNumberOfRows())) { m_rowLoadedTextForMapName = ("Row: " + AString::number(rowIndex)); m_rowLoadedText = ("Row_" + AString::number(rowIndex)); CaretAssert((rowIndex >= 0) && (rowIndex < m_ciftiFile->getNumberOfRows())); m_loadedRowData.resize(dataCount); getProcessedDataForRow(&m_loadedRowData[0], rowIndex); CaretLogFine("Read row " + AString::number(rowIndex)); m_connectivityDataLoaded->setRowColumnLoading(rowIndex, -1); } } updateForChangeInMapDataWithMapIndex(0); } /** * Load the given column from the file even if the file is disabled. * * NOTE: Afterwards, it will be necessary to update this file's color mapping * with updateScalarColoringForMap(). * * * @param columnIndex * Index of row that is loaded. * @throw DataFileException * If an error occurs. */ void CiftiMappableConnectivityMatrixDataFile::loadDataForColumnIndex(const int64_t columnIndex) { setLoadedRowDataToAllZeros(); const int64_t dataCount = m_ciftiFile->getNumberOfRows(); if (dataCount > 0) { if ((columnIndex >= 0) && (columnIndex < m_ciftiFile->getNumberOfColumns())) { m_rowLoadedTextForMapName = ("Column: " + AString::number(columnIndex)); m_rowLoadedText = ("Column_" + AString::number(columnIndex)); CaretAssert((columnIndex >= 0) && (columnIndex < m_ciftiFile->getNumberOfColumns())); m_loadedRowData.resize(dataCount); getProcessedDataForColumn(&m_loadedRowData[0], columnIndex); CaretLogFine("Read column " + AString::number(columnIndex)); m_connectivityDataLoaded->setRowColumnLoading(-1, columnIndex); } } updateForChangeInMapDataWithMapIndex(0); } /** * Load connectivity data for the surface's node. * * @param mapIndex * Index of map. * @param surfaceNumberOfNodes * Number of nodes in surface. * @param structure * Surface's structure. * @param nodeIndex * Index of node number. * @param rowIndexOut * Index of row corresponding to node or -1 if no row in the * matrix corresponds to the node. * @param columnIndexOut * Index of column corresponding to node or -1 if no column in the * matrix corresponds to the node. * @throw * DataFileException if there is an error. */ void CiftiMappableConnectivityMatrixDataFile::loadMapDataForSurfaceNode(const int32_t /*mapIndex*/, const int32_t surfaceNumberOfNodes, const StructureEnum::Enum structure, const int32_t nodeIndex, int64_t& rowIndexOut, int64_t& columnIndexOut) { if (!isEnabledAsLayer()) { return;//TSC: HACK to do nothing when dynconn layer is disabled } ElapsedTimer timer; timer.start(); rowIndexOut = -1; columnIndexOut = -1; if (m_ciftiFile == NULL) { setLoadedRowDataToAllZeros(); return; } /* * Loading of data disabled? */ if (m_dataLoadingEnabled == false) { return; } /* * Zero out here so that data only gets cleared when data * is to be loaded. */ setLoadedRowDataToAllZeros(); int64_t rowIndex = -1; int64_t columnIndex = -1; try { bool dataWasLoaded = false; getRowColumnIndexForNodeWhenLoading(structure, surfaceNumberOfNodes, nodeIndex, rowIndex, columnIndex); if (rowIndex >= 0) { int64_t dataCount = m_ciftiFile->getNumberOfColumns(); if (getDataFileType() == DataFileTypeEnum::CONNECTIVITY_DENSE_DYNAMIC) { /* * Dense dynamic is special case where number of rows equals number of brainordinates. * Number of columns is number of time points */ dataCount = m_ciftiFile->getNumberOfRows(); } if (dataCount > 0) { m_rowLoadedTextForMapName = ("Row: " + AString::number(rowIndex) + ", Node Index: " + AString::number(nodeIndex) + ", Structure: " + StructureEnum::toName(structure)); m_rowLoadedText = ("Row_" + AString::number(rowIndex) + "_Node_Index_" + AString::number(nodeIndex) + "_Structure_" + StructureEnum::toGuiName(structure)); CaretAssert((rowIndex >= 0) && (rowIndex < m_ciftiFile->getNumberOfRows())); m_loadedRowData.resize(dataCount); getProcessedDataForRow(&m_loadedRowData[0], rowIndex); CaretLogFine("Read row for node " + AString::number(nodeIndex)); m_connectivityDataLoaded->setSurfaceNodeLoading(structure, surfaceNumberOfNodes, nodeIndex, rowIndex, -1); rowIndexOut = rowIndex; dataWasLoaded = true; } } else if (columnIndex >= 0) { const int64_t dataCount = m_ciftiFile->getNumberOfRows(); if (dataCount > 0) { m_rowLoadedTextForMapName = ("Column: " + AString::number(columnIndex) + ", Node Index: " + AString::number(nodeIndex) + ", Structure: " + StructureEnum::toName(structure)); m_rowLoadedText = ("Column_" + AString::number(columnIndex) + "_Node_Index_" + AString::number(nodeIndex) + "_Structure_" + StructureEnum::toGuiName(structure)); CaretAssert((columnIndex >= 0) && (columnIndex < m_ciftiFile->getNumberOfColumns())); m_loadedRowData.resize(dataCount); getProcessedDataForColumn(&m_loadedRowData[0], columnIndex); CaretLogFine("Read column for node " + AString::number(nodeIndex)); m_connectivityDataLoaded->setSurfaceNodeLoading(structure, surfaceNumberOfNodes, nodeIndex, -1, columnIndex); columnIndexOut = columnIndex; dataWasLoaded = true; } } if (dataWasLoaded == false) { CaretLogFine("FAILED to read data for node " + AString::number(nodeIndex)); m_connectivityDataLoaded->reset(); } } catch (DataFileException& e) { m_connectivityDataLoaded->reset(); throw e; } updateForChangeInMapDataWithMapIndex(0); AString msg = ("Time load data for surface node in " + getFileNameNoPath() + " was " + AString::number(timer.getElapsedTimeSeconds()) + " seconds."); CaretLogInfo(msg); } /** * Load connectivity data for the surface's nodes and then average the data. * * @param mapIndex * Index of map. * @param surfaceFile * Surface file used for structure. * @param surfaceNumberOfNodes * Number of nodes in surface. * @param structure * Surface's structure. * @param nodeIndices * Indices of nodes. * @throw * DataFileException if there is an error. */ void CiftiMappableConnectivityMatrixDataFile::loadMapAverageDataForSurfaceNodes(const int32_t /*mapIndex*/, const int32_t surfaceNumberOfNodes, const StructureEnum::Enum structure, const std::vector& nodeIndices) { if (m_ciftiFile == NULL) { setLoadedRowDataToAllZeros(); return; } /* * Loading of data disabled? */ if (m_dataLoadingEnabled == false) { return; } /* * Zero out here so that data only gets cleared when data * is to be loaded. */ setLoadedRowDataToAllZeros(); const int32_t numberOfNodeIndices = static_cast(nodeIndices.size()); if (numberOfNodeIndices <= 0) { return; } std::vector rowIndices, columnIndices; getRowColumnIndicesForNodesWhenLoading(structure, surfaceNumberOfNodes, nodeIndices, rowIndices, columnIndices); if (rowIndices.empty() && columnIndices.empty()) { return; } std::vector rowAverage, columnAverage; getRowColumnAverageForIndices(rowIndices, columnIndices, rowAverage, columnAverage); /* * Update the viewed data */ bool dataWasLoaded = false; if ( ! rowAverage.empty()) { processRowAverageData(rowAverage); m_loadedRowData = rowAverage; dataWasLoaded = true; } else if ( ! columnAverage.empty()) { m_loadedRowData = columnAverage; dataWasLoaded = true; } if (dataWasLoaded) { m_rowLoadedTextForMapName = ("Structure: " + StructureEnum::toName(structure) + ", Averaged Vertex Count: " + AString::number(numberOfNodeIndices)); m_rowLoadedText = ("Structure_" + StructureEnum::toGuiName(structure) + "_Averaged_Vertex_Count_" + AString::number(numberOfNodeIndices)); } if (dataWasLoaded == false) { CaretLogFine("FAILED to read data for node average" + AString::fromNumbers(nodeIndices, ",")); } updateForChangeInMapDataWithMapIndex(0); if (dataWasLoaded) { m_connectivityDataLoaded->setSurfaceAverageNodeLoading(structure, surfaceNumberOfNodes, nodeIndices); } } /** * Load data for a voxel at the given coordinate. * * @param mapIndex * Index of map. * @param xyz * Coordinate of voxel. * @param rowIndexOut * Index of row corresponding to voxel or -1 if no row in the * matrix corresponds to the voxel. * @param columnIndexOut * Index of column corresponding to voxel or -1 if no column in the * matrix corresponds to the voxel. * @throw * DataFileException if there is an error. */ void CiftiMappableConnectivityMatrixDataFile::loadMapDataForVoxelAtCoordinate(const int32_t mapIndex, const float xyz[3], int64_t& rowIndexOut, int64_t& columnIndexOut) { rowIndexOut = -1; columnIndexOut = -1; if (mapIndex != 0) { setLoadedRowDataToAllZeros(); CaretAssertMessage(0, "Map index must be zero."); return; } if (m_ciftiFile == NULL) { setLoadedRowDataToAllZeros(); return; } /* * Loading of data disabled? */ if (m_dataLoadingEnabled == false) { return; } /* * Zero out here so that data only gets cleared when data * is to be loaded. */ setLoadedRowDataToAllZeros(); int64_t rowIndex = -1; int64_t columnIndex = -1; bool dataWasLoaded = false; getRowColumnIndexForVoxelAtCoordinateWhenLoading(xyz, rowIndex, columnIndex); if (rowIndex >= 0) { int64_t dataCount = m_ciftiFile->getNumberOfColumns(); if (getDataFileType() == DataFileTypeEnum::CONNECTIVITY_DENSE_DYNAMIC) { /* * Dense dynamic is special case where number of rows equals number of brainordinates. * Number of columns is number of time points */ dataCount = m_ciftiFile->getNumberOfRows(); } if (dataCount > 0) { m_loadedRowData.resize(dataCount); CaretAssert((rowIndex >= 0) && (rowIndex < m_ciftiFile->getNumberOfRows())); getProcessedDataForRow(&m_loadedRowData[0], rowIndex); m_rowLoadedTextForMapName = ("Row: " + AString::number(rowIndex) + ", Voxel XYZ: (" + AString::fromNumbers(xyz, 3, ",") + ")"); m_rowLoadedText = ("Row_" + AString::number(rowIndex) + "_Voxel_XYZ_" + AString::fromNumbers(xyz, 3, "_").replace('-', 'm')); CaretLogFine("Read row for voxel " + AString::fromNumbers(xyz, 3, ",")); rowIndexOut = rowIndex; dataWasLoaded = true; } } else if (columnIndex >= 0) { const int64_t dataCount = m_ciftiFile->getNumberOfRows(); if (dataCount > 0) { m_loadedRowData.resize(dataCount); CaretAssert((columnIndex >= 0) && (columnIndex < m_ciftiFile->getNumberOfColumns())); getProcessedDataForColumn(&m_loadedRowData[0], columnIndex); m_rowLoadedTextForMapName = ("Column: " + AString::number(columnIndex) + ", Voxel XYZ: (" + AString::fromNumbers(xyz, 3, ",") + ")"); m_rowLoadedText = ("Column_" + AString::number(columnIndex) + "_Voxel_XYZ_" + AString::fromNumbers(xyz, 3, "_").replace('-', 'm')); CaretLogFine("Read column for voxel " + AString::fromNumbers(xyz, 3, ",")); columnIndexOut = columnIndex; dataWasLoaded = true; } } if (dataWasLoaded == false) { CaretLogFine("FAILED to read row/column for voxel " + AString::fromNumbers(xyz, 3, ",")); } updateForChangeInMapDataWithMapIndex(0); m_connectivityDataLoaded->setVolumeXYZLoading(xyz, rowIndex, columnIndex); } /** * Get row/column indices for the given voxel indices for the loading of data. * * @param volumeDimensionIJK * Dimensions of the volume. * @param voxelIndices * Indices of voxels. * @param rowIndicesOut * Indices of row corresponding to node or empty if no data for rows. * @param columnIndicesOut * Index of column corresponding to node or empty if no data for columns */ void CiftiMappableConnectivityMatrixDataFile::getRowColumnIndicesForVoxelsWhenLoading(const int64_t volumeDimensionIJK[3], const std::vector& voxelIndices, std::vector& rowIndicesOut, std::vector& columnIndicesOut) { rowIndicesOut.clear(); columnIndicesOut.clear(); /* * Match dimensions */ std::vector volumeDimensions; getDimensions(volumeDimensions); if (volumeDimensions.size() < 3) { return; } if ((volumeDimensions[0] != volumeDimensionIJK[0]) || (volumeDimensions[1] != volumeDimensionIJK[1]) || (volumeDimensions[2] != volumeDimensionIJK[2])) { return; } for (std::vector::const_iterator iter = voxelIndices.begin(); iter != voxelIndices.end(); iter++) { const VoxelIJK& voxelIJK = *iter; int64_t rowIndex; int64_t columnIndex; getRowColumnIndexForVoxelIndexWhenLoading(voxelIJK.m_ijk, rowIndex, columnIndex); if (rowIndex >= 0) { rowIndicesOut.push_back(rowIndex); } else if (columnIndex >= 0) { columnIndicesOut.push_back(columnIndex); } } } /** * Load connectivity data for the voxel indices and then average the data. * * @param mapIndex * Index of map. * @param volumeDimensionIJK * Dimensions of the volume. * @param voxelIndices * Indices of voxels. * @throw * DataFileException if there is an error. */ bool CiftiMappableConnectivityMatrixDataFile::loadMapAverageDataForVoxelIndices(const int32_t mapIndex, const int64_t volumeDimensionIJK[3], const std::vector& voxelIndices) { if (mapIndex != 0) { // eliminates compilation warning when compiled for release CaretAssert(mapIndex == 0); setLoadedRowDataToAllZeros(); } if (m_ciftiFile == NULL) { setLoadedRowDataToAllZeros(); return false; } /* * Loading of data disabled? */ if (m_dataLoadingEnabled == false) { return false; } /* * Zero out here so that data only gets cleared when data * is to be loaded. */ setLoadedRowDataToAllZeros(); std::vector rowIndices, columnIndices; getRowColumnIndicesForVoxelsWhenLoading(volumeDimensionIJK, voxelIndices, rowIndices, columnIndices); if (rowIndices.empty() && columnIndices.empty()) { return false; } std::vector rowAverage, columnAverage; getRowColumnAverageForIndices(rowIndices, columnIndices, rowAverage, columnAverage); bool dataWasLoadedFlag = false; if ( ! rowAverage.empty()) { processRowAverageData(rowAverage); m_loadedRowData = rowAverage; dataWasLoadedFlag = true; } else if ( ! columnAverage.empty()) { m_loadedRowData = columnAverage; dataWasLoadedFlag = true; } // if (userCancelled) { // m_loadedRowData.clear(); // m_loadedRowData.resize(dataCount, 0.0); // } if (dataWasLoadedFlag) { // progressEvent.setProgress(numberOfVoxelIndices - 1, // "Averaging voxel data"); // EventManager::get()->sendEvent(progressEvent.getPointer()); const int32_t numberOfVoxelIndices = static_cast(voxelIndices.size()); m_rowLoadedTextForMapName = ("Averaged Voxel Count: " + AString::number(numberOfVoxelIndices)); m_rowLoadedText = ("Averaged_Voxel_Count_" + AString::number(numberOfVoxelIndices)); m_connectivityDataLoaded->setVolumeAverageVoxelLoading(volumeDimensionIJK, voxelIndices); } updateForChangeInMapDataWithMapIndex(0); return dataWasLoadedFlag; } /** * @return Text describing row loaded that uses * underscores as separators. */ AString CiftiMappableConnectivityMatrixDataFile::getRowLoadedText() const { return m_rowLoadedText; } /** * Get the name of the map at the given index. For connectivity matrix * files this always returns a description of the last data row that * was loaded. * * @param mapIndex * Index of the map. * @return * Name of the map. */ AString CiftiMappableConnectivityMatrixDataFile::getMapName(const int32_t /*mapIndex*/) const { return m_rowLoadedTextForMapName; } AString CiftiMappableConnectivityMatrixDataFile::getRowName(const int32_t rowIndex) const { const CiftiXML& xml = m_ciftiFile->getCiftiXML(); if (xml.getMappingType(CiftiXML::ALONG_COLUMN) != CiftiMappingType::PARCELS) return "";//TSC: this was originally implemented only for parcels, dunno why const std::vector& plist = xml.getParcelsMap(CiftiXML::ALONG_COLUMN).getParcels(); CaretAssertVectorIndex(plist, rowIndex); return plist[rowIndex].m_name; } AString CiftiMappableConnectivityMatrixDataFile::getColumnName(const int32_t columnIndex) const { const CiftiXML& xml = m_ciftiFile->getCiftiXML(); if (xml.getMappingType(CiftiXML::ALONG_ROW) != CiftiMappingType::PARCELS) return "";//ditto const std::vector& plist = xml.getParcelsMap(CiftiXML::ALONG_ROW).getParcels(); CaretAssertVectorIndex(plist, columnIndex); return plist[columnIndex].m_name; } /** * @return The matrix loading type (by row/column). */ ChartMatrixLoadingDimensionEnum::Enum CiftiMappableConnectivityMatrixDataFile::getChartMatrixLoadingDimension() const { return m_chartLoadingDimension; } /** * Set the matrix loading type (by row/column). * * @param matrixLoadingType * New value for matrix loading type. */ void CiftiMappableConnectivityMatrixDataFile::setChartMatrixLoadingDimension(const ChartMatrixLoadingDimensionEnum::Enum matrixLoadingType) { m_chartLoadingDimension = matrixLoadingType; resetDataLoadingMembers(); switch (m_chartLoadingDimension) { case ChartMatrixLoadingDimensionEnum::CHART_MATRIX_LOADING_BY_COLUMN: m_dataReadingAccessMethod = DATA_ACCESS_FILE_COLUMNS_OR_XML_ALONG_ROW; m_dataMappingAccessMethod = DATA_ACCESS_FILE_ROWS_OR_XML_ALONG_COLUMN; break; case ChartMatrixLoadingDimensionEnum::CHART_MATRIX_LOADING_BY_ROW: m_dataReadingAccessMethod = DATA_ACCESS_FILE_ROWS_OR_XML_ALONG_COLUMN; m_dataMappingAccessMethod = DATA_ACCESS_FILE_COLUMNS_OR_XML_ALONG_ROW; break; } initializeAfterReading(getFileName()); resetLoadedRowDataToEmpty(); } /** * Save file data from the scene. For subclasses that need to * save to a scene, this method should be overriden. sceneClass * will be valid and any scene data should be added to it. * * @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. */ void CiftiMappableConnectivityMatrixDataFile::saveFileDataToScene(const SceneAttributes* sceneAttributes, SceneClass* sceneClass) { CiftiMappableDataFile::saveFileDataToScene(sceneAttributes, sceneClass); sceneClass->addEnumeratedType("m_chartLoadingDimension", m_chartLoadingDimension); m_sceneAssistant->saveMembers(sceneAttributes, sceneClass); saveSubClassDataToScene(sceneAttributes, sceneClass); } /** * Restore file data from the scene. For subclasses that need to * restore from a scene, this method should be overridden. The scene class * will be valid and any scene data may be obtained from it. * * @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 CiftiMappableConnectivityMatrixDataFile::restoreFileDataFromScene(const SceneAttributes* sceneAttributes, const SceneClass* sceneClass) { CiftiMappableDataFile::restoreFileDataFromScene(sceneAttributes, sceneClass); m_connectivityDataLoaded->reset(); /* * The chart loading dimension is restored by the scene assistant but * we need to call setChartMatrixLoadingDimension() so that loading * by row or column is properly setup. */ m_chartLoadingDimension = sceneClass->getEnumeratedTypeValue("m_chartLoadingDimension", ChartMatrixLoadingDimensionEnum::CHART_MATRIX_LOADING_BY_ROW); setChartMatrixLoadingDimension(m_chartLoadingDimension); m_sceneAssistant->restoreMembers(sceneAttributes, sceneClass); restoreSubClassDataFromScene(sceneAttributes, sceneClass); /* * Loading of data may be disabled in the scene * so temporarily enabled loading and then * restore the status. */ const int32_t mapIndex = 0; const bool loadingEnabledStatus = isMapDataLoadingEnabled(mapIndex); setMapDataLoadingEnabled(mapIndex, true); switch (m_connectivityDataLoaded->getMode()) { case ConnectivityDataLoaded::MODE_NONE: setLoadedRowDataToAllZeros(); break; case ConnectivityDataLoaded::MODE_ROW: { int64_t rowIndex; int64_t columnIndex; m_connectivityDataLoaded->getRowColumnLoading(rowIndex, columnIndex); loadDataForRowIndex(rowIndex); } break; case ConnectivityDataLoaded::MODE_COLUMN: { int64_t rowIndex; int64_t columnIndex; m_connectivityDataLoaded->getRowColumnLoading(rowIndex, columnIndex); loadDataForColumnIndex(columnIndex); } break; case ConnectivityDataLoaded::MODE_SURFACE_NODE: { StructureEnum::Enum structure; int32_t surfaceNumberOfNodes; int32_t surfaceNodeIndex; int64_t rowIndex; int64_t columnIndex; m_connectivityDataLoaded->getSurfaceNodeLoading(structure, surfaceNumberOfNodes, surfaceNodeIndex, rowIndex, columnIndex); loadMapDataForSurfaceNode(mapIndex, surfaceNumberOfNodes, structure, surfaceNodeIndex, rowIndex, columnIndex); } break; case ConnectivityDataLoaded::MODE_SURFACE_NODE_AVERAGE: { StructureEnum::Enum structure; int32_t surfaceNumberOfNodes; std::vector surfaceNodeIndices; m_connectivityDataLoaded->getSurfaceAverageNodeLoading(structure, surfaceNumberOfNodes, surfaceNodeIndices); loadMapAverageDataForSurfaceNodes(mapIndex, surfaceNumberOfNodes, structure, surfaceNodeIndices); } break; case ConnectivityDataLoaded::MODE_VOXEL_XYZ: { float volumeXYZ[3]; int64_t rowIndex; int64_t columnIndex; m_connectivityDataLoaded->getVolumeXYZLoading(volumeXYZ, rowIndex, columnIndex); loadMapDataForVoxelAtCoordinate(mapIndex, volumeXYZ, rowIndex, columnIndex); } break; case ConnectivityDataLoaded::MODE_VOXEL_IJK_AVERAGE: { int64_t volumeDimensionsIJK[3]; std::vector voxelIndicesIJK; m_connectivityDataLoaded->getVolumeAverageVoxelLoading(volumeDimensionsIJK, voxelIndicesIJK); loadMapAverageDataForVoxelIndices(mapIndex, volumeDimensionsIJK, voxelIndicesIJK); } break; } setMapDataLoadingEnabled(mapIndex, loadingEnabledStatus); } /** * 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 CiftiMappableConnectivityMatrixDataFile::saveSubClassDataToScene(const SceneAttributes* /*sceneAttributes*/, SceneClass* /*sceneClass*/) { /* This method is intended only for subclasses to override */ } /** * 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 CiftiMappableConnectivityMatrixDataFile::restoreSubClassDataFromScene(const SceneAttributes* /*sceneAttributes*/, const SceneClass* /*sceneClass*/) { /* This method is intended only for subclasses to override */ } connectome-workbench-1.2.3+git41-gc4c6c90/src/Files/CiftiMappableConnectivityMatrixDataFile.h000066400000000000000000000215201300200146000316350ustar00rootroot00000000000000#ifndef __CIFTI_MAPPABLE_CONNECTIVITY_MATRIX_DATA_FILE_H__ #define __CIFTI_MAPPABLE_CONNECTIVITY_MATRIX_DATA_FILE_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 "BrainConstants.h" #include "ChartMatrixLoadingDimensionEnum.h" #include "CiftiMappableDataFile.h" #include "VoxelIJK.h" namespace caret { class ConnectivityDataLoaded; class SceneClassAssistant; class CiftiMappableConnectivityMatrixDataFile : public CiftiMappableDataFile { protected: CiftiMappableConnectivityMatrixDataFile(const DataFileTypeEnum::Enum dataFileType); public: virtual ~CiftiMappableConnectivityMatrixDataFile(); bool isMapDataLoadingEnabled(const int32_t mapIndex) const; void setMapDataLoadingEnabled(const int32_t mapIndex, const bool enabled); virtual void loadMapDataForSurfaceNode(const int32_t mapIndex, const int32_t surfaceNumberOfNodes, const StructureEnum::Enum structure, const int32_t nodeIndex, int64_t& rowIndexOut, int64_t& columnIndexOut); virtual void loadMapAverageDataForSurfaceNodes(const int32_t mapIndex, const int32_t surfaceNumberOfNodes, const StructureEnum::Enum structure, const std::vector& nodeIndices); virtual void loadMapDataForVoxelAtCoordinate(const int32_t mapIndex, const float xyz[3], int64_t& rowIndexOut, int64_t& columnIndexOut); virtual bool loadMapAverageDataForVoxelIndices(const int32_t mapIndex, const int64_t volumeDimensionIJK[3], const std::vector& voxelIndices); void loadDataForRowIndex(const int64_t rowIndex); void loadDataForColumnIndex(const int64_t rowIndex); virtual void clear(); virtual bool isEmpty() const; virtual AString getMapName(const int32_t mapIndex) const; AString getRowName(const int32_t rowIndex) const; AString getColumnName(const int32_t rowIndex) const; AString getRowLoadedText() const; virtual void getMapData(const int32_t mapIndex, std::vector& dataOut) const; const ConnectivityDataLoaded* getConnectivityDataLoaded() const; bool getParcelNodesElementForSelectedParcel(std::set &parcelNodesOut, const StructureEnum::Enum &structure) const; ChartMatrixLoadingDimensionEnum::Enum getChartMatrixLoadingDimension() const; //TSC: HACK to expose dynconn enabled as layer status virtual bool isEnabledAsLayer() const { return true; } private: CiftiMappableConnectivityMatrixDataFile(const CiftiMappableConnectivityMatrixDataFile&); CiftiMappableConnectivityMatrixDataFile& operator=(const CiftiMappableConnectivityMatrixDataFile&); public: // ADD_NEW_METHODS_HERE protected: virtual void saveFileDataToScene(const SceneAttributes* sceneAttributes, SceneClass* sceneClass); virtual void restoreFileDataFromScene(const SceneAttributes* sceneAttributes, const SceneClass* sceneClass); virtual void saveSubClassDataToScene(const SceneAttributes* sceneAttributes, SceneClass* sceneClass); virtual void restoreSubClassDataFromScene(const SceneAttributes* sceneAttributes, const SceneClass* sceneClass); void resetLoadedRowDataToEmpty(); void setChartMatrixLoadingDimension(const ChartMatrixLoadingDimensionEnum::Enum matrixLoadingType); virtual void getProcessedDataForColumn(float* dataOut, const int64_t& index) const; virtual void getProcessedDataForRow(float* dataOut, const int64_t& index) const; virtual void getDataForColumn(float* dataOut, const int64_t& index) const; virtual void getDataForRow(float* dataOut, const int64_t& index) const; virtual void processRowAverageData(std::vector& rowAverageData); private: void setLoadedRowDataToAllZeros(); void clearPrivate(); void getRowColumnIndexForNodeWhenLoading(const StructureEnum::Enum structure, const int64_t surfaceNumberOfNodes, const int64_t nodeIndex, int64_t& rowIndexOut, int64_t& columnIndexOut); void getRowColumnIndicesForNodesWhenLoading(const StructureEnum::Enum structure, const int64_t surfaceNumberOfNodes, const std::vector& nodeIndices, std::vector& rowIndicesOut, std::vector& columnIndicesOut); void getRowColumnAverageForIndices(const std::vector& rowIndices, const std::vector& columnIndices, std::vector& rowAverageOut, std::vector& columnAverageOut); void getRowColumnIndicesForVoxelsWhenLoading(const int64_t volumeDimensionIJK[3], const std::vector& voxelIndices, std::vector& rowIndicesOut, std::vector& columnIndicesOut); void getRowColumnIndexForVoxelAtCoordinateWhenLoading(const float xyz[3], int64_t& rowIndexOut, int64_t& columnIndexOut); void getRowColumnIndexForVoxelIndexWhenLoading(const int64_t ijk[3], int64_t& rowIndexOut, int64_t& columnIndexOut); int32_t getCifitDirectionForLoadingRowOrColumn(); // ADD_NEW_MEMBERS_HERE SceneClassAssistant* m_sceneAssistant; bool m_dataLoadingEnabled; std::vector m_loadedRowData; AString m_rowLoadedTextForMapName; AString m_rowLoadedText; ConnectivityDataLoaded* m_connectivityDataLoaded; /* * This is really a member of parcel file since it the parcel * file is the only file that can load by row or column. * However, because of scenes, the chart loading dimension needs * to be restored in this class. */ ChartMatrixLoadingDimensionEnum::Enum m_chartLoadingDimension; friend class CiftiBrainordinateScalarFile; }; #ifdef __CIFTI_MAPPABLE_CONNECTIVITY_MATRIX_DATA_FILE_DECLARE__ // #endif // __CIFTI_MAPPABLE_CONNECTIVITY_MATRIX_DATA_FILE_DECLARE__ } // namespace #endif //__CIFTI_MAPPABLE_CONNECTIVITY_MATRIX_DATA_FILE_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Files/CiftiMappableDataFile.cxx000066400000000000000000007606371300200146000264470ustar00rootroot00000000000000/*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 __CIFTI_MAPPABLE_DATA_FILE_DECLARE__ #include "CiftiMappableDataFile.h" #undef __CIFTI_MAPPABLE_DATA_FILE_DECLARE__ #include "BoundingBox.h" #include "CaretAssert.h" #include "CaretLogger.h" #include "ChartDataCartesian.h" #include "CiftiBrainordinateLabelFile.h" #include "CiftiBrainordinateScalarFile.h" #include "CiftiFiberTrajectoryFile.h" #include "CiftiFile.h" #include "CiftiMappableConnectivityMatrixDataFile.h" #include "CiftiParcelLabelFile.h" #include "CaretTemporaryFile.h" #include "CiftiXML.h" #include "DataFileContentInformation.h" #include "EventManager.h" #include "EventPaletteGetByName.h" #include "FastStatistics.h" #include "FileInformation.h" #include "GiftiLabel.h" #include "GiftiLabelTable.h" #include "GiftiMetaData.h" #include "GroupAndNameHierarchyModel.h" #include "Histogram.h" #include "NodeAndVoxelColoring.h" #include "PaletteColorMapping.h" #include "PaletteFile.h" #include "SparseVolumeIndexer.h" using namespace caret; /** * \class caret::CiftiMappableDataFile * \brief Abstract class for CIFTI files that are mapped to surfaces and volumes * \ingroup Files */ /** * Constructor. * * @param dataFileType * Type of data file. */ CiftiMappableDataFile::CiftiMappableDataFile(const DataFileTypeEnum::Enum dataFileType) : CaretMappableDataFile(dataFileType) { m_ciftiFile.grabNew(NULL); m_voxelIndicesToOffset.grabNew(NULL); m_classNameHierarchy.grabNew(NULL); m_fileDataReadingType = FILE_READ_DATA_ALL; m_containsSurfaceData = false; m_containsVolumeData = false; m_mappingTimeStart = 0.0; m_mappingTimeStep = 0.0; m_mappingTimeUnits = NiftiTimeUnitsEnum::NIFTI_UNITS_UNKNOWN; m_dataReadingAccessMethod = DATA_ACCESS_METHOD_INVALID; m_dataMappingAccessMethod = DATA_ACCESS_METHOD_INVALID; m_colorMappingMethod = COLOR_MAPPING_METHOD_INVALID; m_paletteColorMappingSource = PALETTE_COLOR_MAPPING_SOURCE_INVALID; m_fileMapDataType = FILE_MAP_DATA_TYPE_INVALID; m_dataMappingDirectionForCiftiXML = S_CIFTI_XML_ALONG_INVALID; m_dataReadingDirectionForCiftiXML = S_CIFTI_XML_ALONG_INVALID; m_fileFastStatistics.grabNew(NULL); m_fileHistogram.grabNew(NULL); m_fileHistorgramLimitedValues.grabNew(NULL); /* * Note: The first palette normalization mode is assumed to * be the default mode. */ m_paletteNormalizationModesSupported.clear(); /* * Note: Force an update of the class and name hierarchy */ m_forceUpdateOfGroupAndNameHierarchy = true; switch (dataFileType) { case DataFileTypeEnum::CONNECTIVITY_DENSE: m_dataReadingAccessMethod = DATA_ACCESS_FILE_ROWS_OR_XML_ALONG_COLUMN; m_dataMappingAccessMethod = DATA_ACCESS_FILE_COLUMNS_OR_XML_ALONG_ROW; m_colorMappingMethod = COLOR_MAPPING_METHOD_PALETTE; m_paletteColorMappingSource = PALETTE_COLOR_MAPPING_SOURCE_FROM_FILE; m_paletteNormalizationModesSupported.push_back(PaletteNormalizationModeEnum::NORMALIZATION_SELECTED_MAP_DATA); m_paletteNormalizationModesSupported.push_back(PaletteNormalizationModeEnum::NORMALIZATION_ALL_MAP_DATA); m_fileMapDataType = FILE_MAP_DATA_TYPE_MATRIX; break; case DataFileTypeEnum::CONNECTIVITY_DENSE_DYNAMIC: m_dataReadingAccessMethod = DATA_ACCESS_FILE_COLUMNS_OR_XML_ALONG_ROW; m_dataMappingAccessMethod = DATA_ACCESS_FILE_ROWS_OR_XML_ALONG_COLUMN; m_colorMappingMethod = COLOR_MAPPING_METHOD_PALETTE; m_paletteColorMappingSource = PALETTE_COLOR_MAPPING_SOURCE_FROM_FILE; m_paletteNormalizationModesSupported.push_back(PaletteNormalizationModeEnum::NORMALIZATION_SELECTED_MAP_DATA); m_paletteNormalizationModesSupported.push_back(PaletteNormalizationModeEnum::NORMALIZATION_ALL_MAP_DATA); m_fileMapDataType = FILE_MAP_DATA_TYPE_MATRIX; break; case DataFileTypeEnum::CONNECTIVITY_DENSE_LABEL: m_dataReadingAccessMethod = DATA_ACCESS_FILE_COLUMNS_OR_XML_ALONG_ROW; m_dataMappingAccessMethod = DATA_ACCESS_FILE_ROWS_OR_XML_ALONG_COLUMN; m_colorMappingMethod = COLOR_MAPPING_METHOD_LABEL_TABLE; m_fileMapDataType = FILE_MAP_DATA_TYPE_MULTI_MAP; break; case DataFileTypeEnum::CONNECTIVITY_DENSE_PARCEL: m_dataReadingAccessMethod = DATA_ACCESS_FILE_ROWS_OR_XML_ALONG_COLUMN; m_dataMappingAccessMethod = DATA_ACCESS_FILE_COLUMNS_OR_XML_ALONG_ROW; m_colorMappingMethod = COLOR_MAPPING_METHOD_PALETTE; m_paletteColorMappingSource = PALETTE_COLOR_MAPPING_SOURCE_FROM_FILE; m_paletteNormalizationModesSupported.push_back(PaletteNormalizationModeEnum::NORMALIZATION_ALL_MAP_DATA); m_fileMapDataType = FILE_MAP_DATA_TYPE_MATRIX; break; case DataFileTypeEnum::CONNECTIVITY_DENSE_SCALAR: m_dataReadingAccessMethod = DATA_ACCESS_FILE_COLUMNS_OR_XML_ALONG_ROW; m_dataMappingAccessMethod = DATA_ACCESS_FILE_ROWS_OR_XML_ALONG_COLUMN; m_colorMappingMethod = COLOR_MAPPING_METHOD_PALETTE; m_paletteColorMappingSource = PALETTE_COLOR_MAPPING_SOURCE_FROM_MAP; m_paletteNormalizationModesSupported.push_back(PaletteNormalizationModeEnum::NORMALIZATION_SELECTED_MAP_DATA); m_paletteNormalizationModesSupported.push_back(PaletteNormalizationModeEnum::NORMALIZATION_ALL_MAP_DATA); m_fileMapDataType = FILE_MAP_DATA_TYPE_MULTI_MAP; break; case DataFileTypeEnum::CONNECTIVITY_DENSE_TIME_SERIES: m_dataReadingAccessMethod = DATA_ACCESS_FILE_COLUMNS_OR_XML_ALONG_ROW; m_dataMappingAccessMethod = DATA_ACCESS_FILE_ROWS_OR_XML_ALONG_COLUMN; m_colorMappingMethod = COLOR_MAPPING_METHOD_PALETTE; m_paletteColorMappingSource = PALETTE_COLOR_MAPPING_SOURCE_FROM_FILE; m_paletteNormalizationModesSupported.push_back(PaletteNormalizationModeEnum::NORMALIZATION_SELECTED_MAP_DATA); m_paletteNormalizationModesSupported.push_back(PaletteNormalizationModeEnum::NORMALIZATION_ALL_MAP_DATA); m_fileMapDataType = FILE_MAP_DATA_TYPE_MULTI_MAP; break; case DataFileTypeEnum::CONNECTIVITY_PARCEL: m_dataReadingAccessMethod = DATA_ACCESS_FILE_ROWS_OR_XML_ALONG_COLUMN; m_dataMappingAccessMethod = DATA_ACCESS_FILE_COLUMNS_OR_XML_ALONG_ROW; m_colorMappingMethod = COLOR_MAPPING_METHOD_PALETTE; m_paletteColorMappingSource = PALETTE_COLOR_MAPPING_SOURCE_FROM_FILE; m_paletteNormalizationModesSupported.push_back(PaletteNormalizationModeEnum::NORMALIZATION_ALL_MAP_DATA); m_fileMapDataType = FILE_MAP_DATA_TYPE_MATRIX; break; case DataFileTypeEnum::CONNECTIVITY_PARCEL_DENSE: m_dataReadingAccessMethod = DATA_ACCESS_FILE_ROWS_OR_XML_ALONG_COLUMN; m_dataMappingAccessMethod = DATA_ACCESS_FILE_COLUMNS_OR_XML_ALONG_ROW; m_colorMappingMethod = COLOR_MAPPING_METHOD_PALETTE; m_paletteColorMappingSource = PALETTE_COLOR_MAPPING_SOURCE_FROM_FILE; m_paletteNormalizationModesSupported.push_back(PaletteNormalizationModeEnum::NORMALIZATION_ALL_MAP_DATA); m_fileMapDataType = FILE_MAP_DATA_TYPE_MATRIX; break; case DataFileTypeEnum::CONNECTIVITY_PARCEL_LABEL: m_dataReadingAccessMethod = DATA_ACCESS_FILE_COLUMNS_OR_XML_ALONG_ROW; m_dataMappingAccessMethod = DATA_ACCESS_FILE_ROWS_OR_XML_ALONG_COLUMN; m_colorMappingMethod = COLOR_MAPPING_METHOD_LABEL_TABLE; m_fileMapDataType = FILE_MAP_DATA_TYPE_MULTI_MAP; break; case DataFileTypeEnum::CONNECTIVITY_PARCEL_SCALAR: m_dataReadingAccessMethod = DATA_ACCESS_FILE_COLUMNS_OR_XML_ALONG_ROW; m_dataMappingAccessMethod = DATA_ACCESS_FILE_ROWS_OR_XML_ALONG_COLUMN; m_colorMappingMethod = COLOR_MAPPING_METHOD_PALETTE; m_paletteColorMappingSource = PALETTE_COLOR_MAPPING_SOURCE_FROM_MAP; m_paletteNormalizationModesSupported.push_back(PaletteNormalizationModeEnum::NORMALIZATION_ALL_MAP_DATA); m_paletteNormalizationModesSupported.push_back(PaletteNormalizationModeEnum::NORMALIZATION_SELECTED_MAP_DATA); m_fileMapDataType = FILE_MAP_DATA_TYPE_MULTI_MAP; break; case DataFileTypeEnum::CONNECTIVITY_PARCEL_SERIES: m_dataReadingAccessMethod = DATA_ACCESS_FILE_COLUMNS_OR_XML_ALONG_ROW; m_dataMappingAccessMethod = DATA_ACCESS_FILE_ROWS_OR_XML_ALONG_COLUMN; m_colorMappingMethod = COLOR_MAPPING_METHOD_PALETTE; m_paletteColorMappingSource = PALETTE_COLOR_MAPPING_SOURCE_FROM_FILE; m_paletteNormalizationModesSupported.push_back(PaletteNormalizationModeEnum::NORMALIZATION_ALL_MAP_DATA); m_paletteNormalizationModesSupported.push_back(PaletteNormalizationModeEnum::NORMALIZATION_SELECTED_MAP_DATA); m_fileMapDataType = FILE_MAP_DATA_TYPE_MULTI_MAP; break; case DataFileTypeEnum::CONNECTIVITY_SCALAR_DATA_SERIES: m_dataReadingAccessMethod = DATA_ACCESS_FILE_ROWS_OR_XML_ALONG_COLUMN; m_dataMappingAccessMethod = DATA_ACCESS_NONE; m_colorMappingMethod = COLOR_MAPPING_METHOD_PALETTE; m_paletteColorMappingSource = PALETTE_COLOR_MAPPING_SOURCE_FROM_FILE; m_paletteNormalizationModesSupported.push_back(PaletteNormalizationModeEnum::NORMALIZATION_ALL_MAP_DATA); m_fileMapDataType = FILE_MAP_DATA_TYPE_MULTI_MAP; break; case DataFileTypeEnum::ANNOTATION: case DataFileTypeEnum::BORDER: case DataFileTypeEnum::CONNECTIVITY_FIBER_ORIENTATIONS_TEMPORARY: case DataFileTypeEnum::CONNECTIVITY_FIBER_TRAJECTORY_TEMPORARY: case DataFileTypeEnum::FOCI: case DataFileTypeEnum::IMAGE: case DataFileTypeEnum::LABEL: case DataFileTypeEnum::METRIC: case DataFileTypeEnum::PALETTE: case DataFileTypeEnum::RGBA: case DataFileTypeEnum::SCENE: case DataFileTypeEnum::SPECIFICATION: case DataFileTypeEnum::SURFACE: case DataFileTypeEnum::UNKNOWN: case DataFileTypeEnum::VOLUME: CaretAssertMessage(0, (DataFileTypeEnum::toGuiName(dataFileType) + " is not a CIFTI Mappable Data File.")); break; } /* * Data from matrix files is read as needed since * Dense can be very large */ switch (m_fileMapDataType) { case FILE_MAP_DATA_TYPE_INVALID: break; case FILE_MAP_DATA_TYPE_MATRIX: m_fileDataReadingType = FILE_READ_DATA_AS_NEEDED; break; case FILE_MAP_DATA_TYPE_MULTI_MAP: m_fileDataReadingType = FILE_READ_DATA_ALL; 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); CaretAssert(m_fileMapDataType != FILE_MAP_DATA_TYPE_INVALID); if (m_colorMappingMethod != COLOR_MAPPING_METHOD_LABEL_TABLE) { CaretAssert(m_paletteColorMappingSource != PALETTE_COLOR_MAPPING_SOURCE_INVALID); } setupCiftiReadingMappingDirection(); m_classNameHierarchy.grabNew(new GroupAndNameHierarchyModel()); } /** * Destructor. */ CiftiMappableDataFile::~CiftiMappableDataFile() { clearPrivate(); } /** * Create a new instance of a CIFTI file from the given CIFTI data file type * for the given surface structure and number of nodes. NOT all CIFTI file * types are supported. * * @param ciftiFileType * Data file type for the returned CIFTI file. * @param structure * The surface structure. * @param numberOfNodes * Number of nodes in the surface. * @param errorMessageOut * Will describe problem if there was an error such as an unsupported * CIFTI file type. * @param * Pointer to the newly created CIFTI file. If there is an * error, NULL will be returned and errorMessageOut will describe the * problem. User will need to 'dynamic_cast' the returned pointer to * the class corresponding to the CIFTI file type. */ CiftiMappableDataFile* CiftiMappableDataFile::newInstanceForCiftiFileTypeAndSurface(const DataFileTypeEnum::Enum ciftiFileType, const StructureEnum::Enum structure, const int32_t numberOfNodes, AString& errorMessageOut) { errorMessageOut.clear(); CiftiFile* ciftiFile = NULL; CiftiMappableDataFile* ciftiMappableFile = NULL; try { bool hasLabelsFlag = false; bool hasScalarsFlag = false; /* * Create the appropriate file type. */ switch (ciftiFileType) { case DataFileTypeEnum::CONNECTIVITY_DENSE_LABEL: ciftiMappableFile = new CiftiBrainordinateLabelFile(); hasLabelsFlag = true; break; case DataFileTypeEnum::CONNECTIVITY_DENSE_SCALAR: ciftiMappableFile = new CiftiBrainordinateScalarFile(); hasScalarsFlag = true; break; default: errorMessageOut = ("Creation of " + DataFileTypeEnum::toGuiName(ciftiFileType) + " is not supported."); CaretAssertMessage(0, errorMessageOut); return NULL; break; } /* * Create the XML. */ CiftiXML myXML; myXML.setNumberOfDimensions(2); /* * Add labels or scalars to XML. */ if (hasLabelsFlag) { CiftiLabelsMap labelsMap; labelsMap.setLength(1); myXML.setMap(CiftiXML::ALONG_ROW, labelsMap); } else if (hasScalarsFlag) { CiftiScalarsMap scalarsMap; scalarsMap.setLength(1); myXML.setMap(CiftiXML::ALONG_ROW, scalarsMap); } else { CaretAssert(0); } /* * Add brainordinates to the XML. */ CiftiBrainModelsMap brainModelsMap; brainModelsMap.addSurfaceModel(numberOfNodes, structure); myXML.setMap(CiftiXML::ALONG_COLUMN, brainModelsMap); /* * Add XML to the CIFTI file. */ ciftiFile = new CiftiFile(); ciftiFile->setCiftiXML(myXML); /* * Fill with zeros */ std::vector zeroData(numberOfNodes, 0.0); ciftiFile->setColumn(&zeroData[0], 0); /* * Add the CiftiFile to the Cifti Mappable File */ const AString defaultFileName = ciftiMappableFile->getFileName(); ciftiMappableFile->m_ciftiFile.grabNew(ciftiFile); ciftiMappableFile->setFileName(defaultFileName); ciftiMappableFile->initializeAfterReading(defaultFileName); ciftiMappableFile->setModified(); return ciftiMappableFile; } catch (const DataFileException& de) { errorMessageOut = de.whatString(); } if (ciftiMappableFile != NULL) { delete ciftiMappableFile; ciftiMappableFile = NULL; } if (ciftiFile != NULL) { delete ciftiFile; ciftiFile = NULL; } return NULL; } /** * Clear the contents of the file. */ void CiftiMappableDataFile::clear() { CaretMappableDataFile::clear(); clearPrivate(); } /** * Clear the contents of the file. * Note that "clear()" is virtual and cannot be called from destructor. */ void CiftiMappableDataFile::clearPrivate() { /* * Do not clear this items as they are setup in the constructor. * m_dataReadingAccessMethod * m_dataMappingAccessMethod * m_colorMappingMethod * m_fileMapDataType */ m_ciftiFile.grabNew(NULL); resetDataLoadingMembers(); m_containsSurfaceData = false; m_containsVolumeData = false; m_mappingTimeStart = 0.0; m_mappingTimeStep = 0.0; m_mappingTimeUnits = NiftiTimeUnitsEnum::NIFTI_UNITS_UNKNOWN; } /** * Reset data loading members. * Also used when parcel row/column loading is changed. */ void CiftiMappableDataFile::resetDataLoadingMembers() { const int64_t num = static_cast(m_mapContent.size()); for (int64_t i = 0; i < num; i++) { delete m_mapContent[i]; } m_mapContent.clear(); m_classNameHierarchy->clear(); m_forceUpdateOfGroupAndNameHierarchy = true; } /** * @return Is this file empty? */ bool CiftiMappableDataFile::isEmpty() const { if (getNumberOfMaps() > 0) { return false; } return true; } /** * Set preference for reading. Reading all data from a "matrix" type file * is not supported and if requested, it will be ignored. * * @param prefer * When true, only header is read and no data is read. * When false, both header and all data is read. */ void CiftiMappableDataFile::setPreferOnDiskReading(const bool& prefer) { if (prefer) { m_fileDataReadingType = FILE_READ_DATA_AS_NEEDED; } else { switch (m_fileMapDataType) { case FILE_MAP_DATA_TYPE_INVALID: break; case FILE_MAP_DATA_TYPE_MATRIX: CaretLogSevere("CIFTI Matrix files do not support reading of all data."); break; case FILE_MAP_DATA_TYPE_MULTI_MAP: m_fileDataReadingType = FILE_READ_DATA_ALL; break; } } } /** * @return structure file maps to. */ StructureEnum::Enum CiftiMappableDataFile::getStructure() const { /* * CIFTI files apply to all structures. */ return StructureEnum::ALL; } /** * Set the structure to which file maps. * @param structure * New structure to which file maps. */ void CiftiMappableDataFile::setStructure(const StructureEnum::Enum /*structure*/) { /* CIFTI files may apply to all structures */ } /** * Is this file able to map to the given structure? Some data files, such * as CIFTI files, are able to map to multiple surface structure. The default * implementation of this method simply compares the given structure to * getStructure() and returns true if they are the same value, else false. * * @param structure * Structure for testing mappability status. * @return True if this file is able to map to the given structure, else false. */ bool CiftiMappableDataFile::isMappableToSurfaceStructure(const StructureEnum::Enum structure) const { /* * Validate number of nodes are correct */ int32_t numCiftiNodes = getMappingSurfaceNumberOfNodes(structure); if (numCiftiNodes > 0) { return true; } return false; } /** * @return Metadata for the file. */ GiftiMetaData* CiftiMappableDataFile::getFileMetaData() { CaretAssert(m_ciftiFile); const CiftiXML& ciftiXML = m_ciftiFile->getCiftiXML(); return ciftiXML.getFileMetaData(); } /** * @return Metadata for the file. */ const GiftiMetaData* CiftiMappableDataFile:: getFileMetaData() const { CaretAssert(m_ciftiFile); const CiftiXML& ciftiXML = m_ciftiFile->getCiftiXML(); return ciftiXML.getFileMetaData(); } /** * Read the file. * * @param ciftiMapFileName * Name of the file to read. * @throw * DataFileException if there is an error reading the file. */ void CiftiMappableDataFile::readFile(const AString& ciftiMapFileName) { clear(); try { /* * Is the file on the network (name begins with http, ftp, etc.). */ if (DataFile::isFileOnNetwork(ciftiMapFileName)) { /* * Data in Xnat does not end with a valid file extension * but ends with HTTP search parameters. Thus, if the * filename does not have a valid extension, assume that * the data is in Xnat. */ bool isValidFileExtension = false; DataFileTypeEnum::fromFileExtension(ciftiMapFileName, &isValidFileExtension); if (isValidFileExtension) { switch (m_fileMapDataType) { case FILE_MAP_DATA_TYPE_INVALID: break; case FILE_MAP_DATA_TYPE_MATRIX: throw DataFileException(ciftiMapFileName + " of type " + DataFileTypeEnum::toGuiName(getDataFileType()) + " cannot be read over the network. The file must be" " accessed by reading individual rows and/or columns" " and this cannot be performed over a network."); break; case FILE_MAP_DATA_TYPE_MULTI_MAP: break; } CaretTemporaryFile tempFile; tempFile.readFile(ciftiMapFileName); m_ciftiFile.grabNew(new CiftiFile()); m_ciftiFile->openFile(tempFile.getFileName()); m_ciftiFile->convertToInMemory(); } else { m_ciftiFile.grabNew(new CiftiFile()); AString username = ""; AString password = ""; AString filenameToOpen = ""; /* * Username and password may be embedded in URL, so extract them. */ FileInformation fileInfo(ciftiMapFileName); fileInfo.getRemoteUrlUsernameAndPassword(filenameToOpen, username, password); /* * Always override with a password entered by the user. */ if (CaretDataFile::getFileReadingUsername().isEmpty() == false) { username = CaretDataFile::getFileReadingUsername(); password = CaretDataFile::getFileReadingPassword(); } m_ciftiFile->openURL(filenameToOpen, username, password); } } else { m_ciftiFile.grabNew(new CiftiFile()); switch (m_fileMapDataType) { case FILE_MAP_DATA_TYPE_INVALID: break; case FILE_MAP_DATA_TYPE_MATRIX: m_ciftiFile->openFile(ciftiMapFileName); break; case FILE_MAP_DATA_TYPE_MULTI_MAP: m_ciftiFile->openFile(ciftiMapFileName); switch (m_fileDataReadingType) { case FILE_READ_DATA_ALL: m_ciftiFile->convertToInMemory(); break; case FILE_READ_DATA_AS_NEEDED: break; } break; } } if (m_ciftiFile != NULL) { initializeAfterReading(ciftiMapFileName); } } catch (DataFileException& e) { clear(); throw e; } catch (CaretException& e) { clear(); throw DataFileException(ciftiMapFileName, e.whatString()); } setFileName(ciftiMapFileName); clearModified(); } /** * Validate the mapping types for each dimension. * * @param filename * Name of file. */ void CiftiMappableDataFile::validateMappingTypes(const AString& filename) { CaretAssert(m_ciftiFile); CiftiMappingType::MappingType expectedAlongColumnMapType = CiftiMappingType::BRAIN_MODELS; CiftiMappingType::MappingType expectedAlongRowMapType = CiftiMappingType::BRAIN_MODELS; const DataFileTypeEnum::Enum dataFileType = getDataFileType(); switch (dataFileType) { case DataFileTypeEnum::CONNECTIVITY_DENSE: expectedAlongColumnMapType = CiftiMappingType::BRAIN_MODELS; expectedAlongRowMapType = CiftiMappingType::BRAIN_MODELS; break; case DataFileTypeEnum::CONNECTIVITY_DENSE_DYNAMIC: expectedAlongColumnMapType = CiftiMappingType::BRAIN_MODELS; expectedAlongRowMapType = CiftiMappingType::SERIES; break; case DataFileTypeEnum::CONNECTIVITY_DENSE_LABEL: expectedAlongColumnMapType = CiftiMappingType::BRAIN_MODELS; expectedAlongRowMapType = CiftiMappingType::LABELS; break; case DataFileTypeEnum::CONNECTIVITY_DENSE_PARCEL: expectedAlongColumnMapType = CiftiMappingType::BRAIN_MODELS; expectedAlongRowMapType = CiftiMappingType::PARCELS; break; case DataFileTypeEnum::CONNECTIVITY_DENSE_SCALAR: expectedAlongColumnMapType = CiftiMappingType::BRAIN_MODELS; expectedAlongRowMapType = CiftiMappingType::SCALARS; break; case DataFileTypeEnum::CONNECTIVITY_DENSE_TIME_SERIES: expectedAlongColumnMapType = CiftiMappingType::BRAIN_MODELS; expectedAlongRowMapType = CiftiMappingType::SERIES; break; case DataFileTypeEnum::CONNECTIVITY_PARCEL: expectedAlongColumnMapType = CiftiMappingType::PARCELS; expectedAlongRowMapType = CiftiMappingType::PARCELS; break; case DataFileTypeEnum::CONNECTIVITY_PARCEL_DENSE: expectedAlongColumnMapType = CiftiMappingType::PARCELS; expectedAlongRowMapType = CiftiMappingType::BRAIN_MODELS; break; case DataFileTypeEnum::CONNECTIVITY_PARCEL_LABEL: expectedAlongColumnMapType = CiftiMappingType::PARCELS; expectedAlongRowMapType = CiftiMappingType::LABELS; break; case DataFileTypeEnum::CONNECTIVITY_PARCEL_SCALAR: expectedAlongColumnMapType = CiftiMappingType::PARCELS; expectedAlongRowMapType = CiftiMappingType::SCALARS; break; case DataFileTypeEnum::CONNECTIVITY_PARCEL_SERIES: expectedAlongColumnMapType = CiftiMappingType::PARCELS; expectedAlongRowMapType = CiftiMappingType::SERIES; break; case DataFileTypeEnum::CONNECTIVITY_SCALAR_DATA_SERIES: expectedAlongColumnMapType = CiftiMappingType::SCALARS; expectedAlongRowMapType = CiftiMappingType::SERIES; break; case DataFileTypeEnum::ANNOTATION: case DataFileTypeEnum::BORDER: case DataFileTypeEnum::CONNECTIVITY_FIBER_ORIENTATIONS_TEMPORARY: case DataFileTypeEnum::CONNECTIVITY_FIBER_TRAJECTORY_TEMPORARY: case DataFileTypeEnum::FOCI: case DataFileTypeEnum::IMAGE: case DataFileTypeEnum::LABEL: case DataFileTypeEnum::METRIC: case DataFileTypeEnum::PALETTE: case DataFileTypeEnum::RGBA: case DataFileTypeEnum::SCENE: case DataFileTypeEnum::SPECIFICATION: case DataFileTypeEnum::SURFACE: case DataFileTypeEnum::UNKNOWN: case DataFileTypeEnum::VOLUME: throw DataFileException(filename, DataFileTypeEnum::toGuiName(dataFileType) + " is not a CIFTI Mappable Data File."); break; } const CiftiXML& ciftiXML = m_ciftiFile->getCiftiXML(); const CiftiMappingType::MappingType alongColumnMapType = ciftiXML.getMappingType(CiftiXML::ALONG_COLUMN); const CiftiMappingType::MappingType alongRowMapType = ciftiXML.getMappingType(CiftiXML::ALONG_ROW); AString errorMessage; if (alongColumnMapType != expectedAlongColumnMapType) { errorMessage.appendWithNewLine("Along column mapping type is " + CiftiMappableDataFile::mappingTypeToName(alongColumnMapType) + " but should be " + CiftiMappableDataFile::mappingTypeToName(expectedAlongColumnMapType) + " for file type \"" + DataFileTypeEnum::toGuiName(dataFileType) + "\""); } if (alongRowMapType != expectedAlongRowMapType) { errorMessage.appendWithNewLine("Along row mapping type is " + CiftiMappableDataFile::mappingTypeToName(alongRowMapType) + " but should be " + CiftiMappableDataFile::mappingTypeToName(expectedAlongRowMapType) + " for file type \"" + DataFileTypeEnum::toGuiName(dataFileType) + "\""); } if ( ! errorMessage.isEmpty()) { throw DataFileException(filename, errorMessage); } } /** * Setup the CIFTI mapping and reading directions. */ void CiftiMappableDataFile::setupCiftiReadingMappingDirection() { 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: m_dataMappingDirectionForCiftiXML = CiftiXML::ALONG_COLUMN; break; case DATA_ACCESS_FILE_COLUMNS_OR_XML_ALONG_ROW: m_dataMappingDirectionForCiftiXML = CiftiXML::ALONG_ROW; break; } switch (m_dataReadingAccessMethod) { case DATA_ACCESS_METHOD_INVALID: CaretAssert(0); break; case DATA_ACCESS_NONE: break; case DATA_ACCESS_FILE_ROWS_OR_XML_ALONG_COLUMN: m_dataReadingDirectionForCiftiXML = CiftiXML::ALONG_COLUMN; break; case DATA_ACCESS_FILE_COLUMNS_OR_XML_ALONG_ROW: m_dataReadingDirectionForCiftiXML = CiftiXML::ALONG_ROW; break; } } /** * Initialize the CIFTI file. * * @param filename * Name of file. */ void CiftiMappableDataFile::initializeAfterReading(const AString& filename) { CaretAssert(m_ciftiFile); setupCiftiReadingMappingDirection(); validateMappingTypes(filename); const CiftiXML& ciftiXML = m_ciftiFile->getCiftiXML(); if (m_dataMappingDirectionForCiftiXML != S_CIFTI_XML_ALONG_INVALID) { switch (ciftiXML.getMappingType(m_dataMappingDirectionForCiftiXML)) { case CiftiMappingType::BRAIN_MODELS: { const CiftiBrainModelsMap& map = ciftiXML.getBrainModelsMap(m_dataMappingDirectionForCiftiXML); if (! map.getSurfaceStructureList().empty()) { m_containsSurfaceData = true; } if (map.hasVolumeData()) { m_containsVolumeData = true; } } break; case CiftiMappingType::LABELS: CaretAssertMessage(0, "Mapping type should never be LABELS"); throw DataFileException(filename, "Mapping type should never be LABELS"); break; case CiftiMappingType::PARCELS: { const CiftiParcelsMap& map = ciftiXML.getParcelsMap(m_dataMappingDirectionForCiftiXML); if (! map.getParcelSurfaceStructures().empty()) { m_containsSurfaceData = true; } if (map.hasVolumeData()) { m_containsVolumeData = true; } } break; case CiftiMappingType::SCALARS: CaretAssertMessage(0, "Mapping type should never be SCALARS"); throw DataFileException(filename, "Mapping type should never be SCALARS"); break; case CiftiMappingType::SERIES: CaretAssertMessage(0, "Mapping type should never be SERIES"); throw DataFileException(filename, "Mapping type should never be SERIES"); break; } } switch (ciftiXML.getMappingType(m_dataReadingDirectionForCiftiXML)) { case CiftiMappingType::BRAIN_MODELS: break; case CiftiMappingType::LABELS: break; case CiftiMappingType::PARCELS: break; case CiftiMappingType::SCALARS: break; case CiftiMappingType::SERIES: { const CiftiSeriesMap& map = ciftiXML.getSeriesMap(m_dataReadingDirectionForCiftiXML); 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(); } break; } 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: if (ciftiXML.getMappingType(CiftiXML::ALONG_COLUMN) == CiftiMappingType::BRAIN_MODELS) { m_voxelIndicesToOffset.grabNew(new SparseVolumeIndexer(ciftiXML.getBrainModelsMap(CiftiXML::ALONG_COLUMN))); } else if (ciftiXML.getMappingType(CiftiXML::ALONG_COLUMN) == CiftiMappingType::PARCELS) { m_voxelIndicesToOffset.grabNew(new SparseVolumeIndexer(ciftiXML.getParcelsMap(CiftiXML::ALONG_COLUMN))); } else { CaretAssertMessage(0, "Invalid mapping type for mapping data to brainordinates"); throw DataFileException(filename, "Invalid mapping type for mapping data to brainordinates"); } break; case DATA_ACCESS_FILE_COLUMNS_OR_XML_ALONG_ROW: if (ciftiXML.getMappingType(CiftiXML::ALONG_ROW) == CiftiMappingType::BRAIN_MODELS) { m_voxelIndicesToOffset.grabNew(new SparseVolumeIndexer(ciftiXML.getBrainModelsMap(CiftiXML::ALONG_ROW))); } else if (ciftiXML.getMappingType(CiftiXML::ALONG_ROW) == CiftiMappingType::PARCELS) { m_voxelIndicesToOffset.grabNew(new SparseVolumeIndexer(ciftiXML.getParcelsMap(CiftiXML::ALONG_ROW))); } else { CaretAssertMessage(0, "Invalid mapping type for mapping data to brainordinates"); throw DataFileException(filename, "Invalid mapping type for mapping data to brainordinates"); } break; } /* * May not have mappings to voxels */ if (m_voxelIndicesToOffset == NULL) { m_voxelIndicesToOffset.grabNew(new SparseVolumeIndexer()); } int32_t numberOfMaps = 0; switch (m_fileMapDataType) { case FILE_MAP_DATA_TYPE_INVALID: break; case FILE_MAP_DATA_TYPE_MATRIX: numberOfMaps = 1; break; case FILE_MAP_DATA_TYPE_MULTI_MAP: switch (m_dataReadingAccessMethod) { case DATA_ACCESS_METHOD_INVALID: CaretAssert(0); break; case DATA_ACCESS_NONE: break; case DATA_ACCESS_FILE_COLUMNS_OR_XML_ALONG_ROW: numberOfMaps = m_ciftiFile->getNumberOfColumns(); break; case DATA_ACCESS_FILE_ROWS_OR_XML_ALONG_COLUMN: numberOfMaps = m_ciftiFile->getNumberOfRows(); break; } break; } /* * Get data for maps. */ for (int32_t i = 0; i < numberOfMaps; i++) { MapContent* mc = new MapContent(m_ciftiFile, m_fileMapDataType, m_dataReadingDirectionForCiftiXML, m_dataMappingDirectionForCiftiXML, i); m_mapContent.push_back(mc); } m_classNameHierarchy->update(this, true); m_forceUpdateOfGroupAndNameHierarchy = false; m_classNameHierarchy->setAllSelected(true); m_fileFastStatistics.grabNew(NULL); m_fileHistogram.grabNew(NULL); m_fileHistorgramLimitedValues.grabNew(NULL); CaretLogFiner("CLASS/NAME Table for : " + this->getFileNameNoPath() + "\n" + m_classNameHierarchy->toString()); validateKeysAndLabels(); validateAfterFileReading(); } /** * This method is intended for overriding by subclasess so that they * can examine and verify the data that was read. This method is * called after successfully reading a file. */ void CiftiMappableDataFile::validateAfterFileReading() { /* nothing - see method comment. */ } /** * Write the file. * * @param ciftiMapFileName * Name of the file to write. * @throw * DataFileException if there is an error writing the file. */ void CiftiMappableDataFile::writeFile(const AString& ciftiMapFileName) { if (m_ciftiFile == NULL) { throw DataFileException(ciftiMapFileName + " cannot be written because no file is loaded"); } if (getDataFileType() == DataFileTypeEnum::CONNECTIVITY_DENSE) { throw DataFileException(ciftiMapFileName + " dense connectivity files cannot be written to files due to their large sizes."); } m_ciftiFile->writeFile(ciftiMapFileName); setFileName(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? */ bool CiftiMappableDataFile::isSurfaceMappable() const { return m_containsSurfaceData; } /** * @return Is the data mappable to a volume? */ bool CiftiMappableDataFile::isVolumeMappable() const { return m_containsVolumeData; } /** * @return The number of maps in the file. * Note: Caret5 used the term 'columns'. */ int32_t CiftiMappableDataFile::getNumberOfMaps() const { return m_mapContent.size(); } /** * @return True if the file has map attributes (name and metadata). * For files that do not have map attributes, they should override * this method and return false. If not overriden, this method * returns true. * * Some files (such as CIFTI Connectivity Matrix Files and CIFTI * Data-Series Files) do not have Map Attributes and thus there * is no map name nor map metadata and options to edit these * attributes should not be presented to the user. * * These CIFTI files do contain palette color mapping but it is * associated with the file. To simplify palette color mapping editing * these file will return the file's palette color mapping for any * calls to getMapPaletteColorMapping(). */ bool CiftiMappableDataFile::hasMapAttributes() const { switch (m_fileMapDataType) { case FILE_MAP_DATA_TYPE_INVALID: break; case FILE_MAP_DATA_TYPE_MATRIX: break; case FILE_MAP_DATA_TYPE_MULTI_MAP: return true; break; } return false; } /** * Get the name of the map at the given index. * * @param mapIndex * Index of the map. * @return * Name of the map. */ AString CiftiMappableDataFile::getMapName(const int32_t mapIndex) const { CaretAssertVectorIndex(m_mapContent, mapIndex); return m_mapContent[mapIndex]->getName(); } /** * Set the name of the map at the given index. * * @param mapIndex * Index of the map. * @param mapName * New name for the map. */ void CiftiMappableDataFile::setMapName(const int32_t mapIndex, const AString& mapName) { CaretAssertVectorIndex(m_mapContent, mapIndex); m_mapContent[mapIndex]->setName(mapName); } /** * Get the metadata for the map at the given index * * @param mapIndex * Index of the map. * @return * Metadata for the map (const value). */ const GiftiMetaData* CiftiMappableDataFile::getMapMetaData(const int32_t mapIndex) const { CaretAssertVectorIndex(m_mapContent, mapIndex); return m_mapContent[mapIndex]->m_metadata; } /** * Get the metadata for the map at the given index * * @param mapIndex * Index of the map. * @return * Metadata for the map. */ GiftiMetaData* CiftiMappableDataFile::getMapMetaData(const int32_t mapIndex) { CaretAssertVectorIndex(m_mapContent, mapIndex); return m_mapContent[mapIndex]->m_metadata; } /** * Get the unique ID (UUID) for the map at the given index. * * @param mapIndex * Index of the map. * @return * String containing UUID for the map. */ AString CiftiMappableDataFile::getMapUniqueID(const int32_t mapIndex) const { CaretAssertVectorIndex(m_mapContent, mapIndex); const GiftiMetaData* md = getMapMetaData(mapIndex); const AString uniqueID = md->getUniqueID(); return uniqueID; } /** * @return Is the data in the file mapped to colors using * a palette. */ bool CiftiMappableDataFile::isMappedWithPalette() const { switch (m_colorMappingMethod) { case COLOR_MAPPING_METHOD_INVALID: break; case COLOR_MAPPING_METHOD_LABEL_TABLE: break; case COLOR_MAPPING_METHOD_PALETTE: return true; break; } return false; } /** * Get the data for the given map index. * * @param mapIndex * Index of the map. * @param dataOut * A vector that will contain the data for the map upon exit. */ void CiftiMappableDataFile::getMapData(const int32_t mapIndex, std::vector& dataOut) const { CaretAssertVectorIndex(m_mapContent, mapIndex); CaretAssert(m_ciftiFile); CaretAssert(mapIndex >= 0); switch (m_dataReadingAccessMethod) { case DATA_ACCESS_METHOD_INVALID: CaretAssert(0); break; case DATA_ACCESS_NONE: break; case DATA_ACCESS_FILE_COLUMNS_OR_XML_ALONG_ROW: CaretAssert(mapIndex < m_ciftiFile->getNumberOfColumns()); dataOut.resize(m_ciftiFile->getNumberOfRows()); m_ciftiFile->getColumn(&dataOut[0], mapIndex); break; case DATA_ACCESS_FILE_ROWS_OR_XML_ALONG_COLUMN: CaretAssert(mapIndex < m_ciftiFile->getNumberOfRows()); dataOut.resize(m_ciftiFile->getNumberOfColumns()); m_ciftiFile->getRow(&dataOut[0], mapIndex); break; } } /** * Set the data for the given map index. * * @param mapIndex * Index of the map. * @param dataOut * A vector that contains the data for the map. */ void CiftiMappableDataFile::setMapData(const int32_t mapIndex, const std::vector& data) { CaretAssertVectorIndex(m_mapContent, mapIndex); CaretAssert(m_ciftiFile); CaretAssert(mapIndex >= 0); switch (m_dataReadingAccessMethod) { case DATA_ACCESS_METHOD_INVALID: CaretAssert(0); break; case DATA_ACCESS_NONE: break; case DATA_ACCESS_FILE_COLUMNS_OR_XML_ALONG_ROW: CaretAssert(mapIndex < m_ciftiFile->getNumberOfColumns()); m_ciftiFile->setColumn(&data[0], mapIndex); break; case DATA_ACCESS_FILE_ROWS_OR_XML_ALONG_COLUMN: CaretAssert(mapIndex < m_ciftiFile->getNumberOfRows()); m_ciftiFile->setRow(&data[0], mapIndex); break; } m_forceUpdateOfGroupAndNameHierarchy = true; m_mapContent[mapIndex]->updateForChangeInMapData(); } /** * Update after a change in map data. * * @param mapIndex * Index of map whose data has changed. */ void CiftiMappableDataFile::updateForChangeInMapDataWithMapIndex(const int32_t mapIndex) { CaretAssertVectorIndex(m_mapContent, mapIndex); m_mapContent[mapIndex]->updateForChangeInMapData(); } /** * Invalidate coloring in all maps */ void CiftiMappableDataFile::invalidateColoringInAllMaps() { const int64_t numMaps = static_cast(getNumberOfMaps()); for (int64_t i = 0; i < numMaps; i++) { CaretAssertVectorIndex(m_mapContent, i); m_mapContent[i]->m_rgbaValid = false; } } /** * Get all data within the file. * * @param data * Filled with data and will contain (number-of-rows * number-of-columns) * of data. */ void CiftiMappableDataFile::getFileData(std::vector& data) const { switch (m_fileMapDataType) { case FILE_MAP_DATA_TYPE_INVALID: CaretAssert(0); break; case FILE_MAP_DATA_TYPE_MATRIX: break; case FILE_MAP_DATA_TYPE_MULTI_MAP: break; } CaretAssert(m_ciftiFile); const int64_t numRows = m_ciftiFile->getNumberOfRows(); const int64_t numCols = m_ciftiFile->getNumberOfColumns(); const int64_t dataSize = numRows * numCols; data.resize(dataSize); for (int64_t iRow = 0; iRow < numRows; iRow++) { m_ciftiFile->getRow(&data[iRow * numCols], iRow); } } /** * Get the RGBA mapped version of the file's data matrix. * * @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) { std::vector data; getFileData(data); const CiftiXML& ciftiXML = m_ciftiFile->getCiftiXML(); PaletteColorMapping *paletteColorMapping = ciftiXML.getFilePalette(); CaretAssert(paletteColorMapping); CaretPointer fastStatistics(new FastStatistics()); const AString paletteName = paletteColorMapping->getSelectedPaletteName(); const Palette* palette = paletteFile->getPaletteByName(paletteName); if (( ! data.empty()) && (palette != NULL)) { fastStatistics->update(&data[0], data.size()); rgba.resize(data.size() * 4); NodeAndVoxelColoring::colorScalarsWithPalette(fastStatistics, paletteColorMapping, palette, &data[0], &data[0], data.size(), &rgba[0]); } else { std::fill(rgba.begin(), rgba.end(), 0.0); } } /** * Get the dimensions of the file (rows and columns). * * @param dim * Contains dimensions upon exit. */ void CiftiMappableDataFile::getMapDimensions(std::vector &dim) const { CaretAssert(m_ciftiFile); dim.clear(); dim.push_back(m_ciftiFile->getNumberOfColumns()); dim.push_back(m_ciftiFile->getNumberOfRows()); } /** * Get statistics describing the distribution of data * mapped with a color palette at the given index. * * @param mapIndex * Index of the map. * @return * Fast statistics for data (will be NULL for data * not mapped using a palette). */ const FastStatistics* CiftiMappableDataFile::getMapFastStatistics(const int32_t mapIndex) { FastStatistics* fastStatsOut = NULL; if (isMappedWithPalette()) { CaretAssertVectorIndex(m_mapContent, mapIndex); if ( ! m_mapContent[mapIndex]->isFastStatisticsValid()) { std::vector data; getMapData(mapIndex, data); m_mapContent[mapIndex]->updateFastStatistics(data); } fastStatsOut = m_mapContent[mapIndex]->m_fastStatistics; } return fastStatsOut; } /** * Get histogram describing the distribution of data * mapped with a color palette at the given index. * * @param mapIndex * Index of the map. * @return * Histogram for data (will be NULL for data * not mapped using a palette). */ const Histogram* CiftiMappableDataFile::getMapHistogram(const int32_t mapIndex) { Histogram* histogramOut = NULL; if (isMappedWithPalette()) { CaretAssertVectorIndex(m_mapContent, mapIndex); if ( ! m_mapContent[mapIndex]->isHistogramValid()) { std::vector data; getMapData(mapIndex, data); m_mapContent[mapIndex]->updateHistogram(data); } histogramOut = m_mapContent[mapIndex]->m_histogram; } return histogramOut; } /** * Get histogram describing the distribution of data * mapped with a color palette at the given index for * data within the given ranges. * * @param mapIndex * Index of the map. * @param mostPositiveValueInclusive * Values more positive than this value are excluded. * @param leastPositiveValueInclusive * Values less positive than this value are excluded. * @param leastNegativeValueInclusive * Values less negative than this value are excluded. * @param mostNegativeValueInclusive * Values more negative than this value are excluded. * @param includeZeroValues * If true zero values (very near zero) are included. * @return * Descriptive statistics for data (will be NULL for data * not mapped using a palette). */ const Histogram* CiftiMappableDataFile::getMapHistogram(const int32_t mapIndex, const float mostPositiveValueInclusive, const float leastPositiveValueInclusive, const float leastNegativeValueInclusive, const float mostNegativeValueInclusive, const bool includeZeroValues) { Histogram* histogramOut = NULL; if (isMappedWithPalette()) { CaretAssertVectorIndex(m_mapContent, mapIndex); if ( ! m_mapContent[mapIndex]->isHistogramLimitedValuesValid(mostPositiveValueInclusive, leastPositiveValueInclusive, leastNegativeValueInclusive, mostNegativeValueInclusive, includeZeroValues)) { std::vector data; getMapData(mapIndex, data); m_mapContent[mapIndex]->updateHistogramLimitedValues(data, mostPositiveValueInclusive, leastPositiveValueInclusive, leastNegativeValueInclusive, mostNegativeValueInclusive, includeZeroValues); } histogramOut = m_mapContent[mapIndex]->m_histogramLimitedValues; } return histogramOut; } /** * @return The estimated size of data after it is uncompressed * and loaded into RAM. A negative value indicates that the * file size cannot be computed. */ int64_t CiftiMappableDataFile::getDataSizeUncompressedInBytes() const { int64_t dataSize = 0; if (m_ciftiFile != NULL) { dataSize = (m_ciftiFile->getNumberOfColumns() * m_ciftiFile->getNumberOfRows() * sizeof(float)); } return dataSize; } /** * Get statistics describing the distribution of data * mapped with a color palette for all data within the file. * * @return * Fast statistics for data (will be NULL for data * not mapped using a palette). */ const FastStatistics* CiftiMappableDataFile::getFileFastStatistics() { if (m_fileFastStatistics == NULL) { std::vector fileData; getFileData(fileData); if ( ! fileData.empty()) { m_fileFastStatistics.grabNew(new FastStatistics()); m_fileFastStatistics->update(&fileData[0], fileData.size()); } } return m_fileFastStatistics; } /** * Get histogram describing the distribution of data * mapped with a color palette for all data within * the file. * * @return * Histogram for data (will be NULL for data * not mapped using a palette). */ const Histogram* CiftiMappableDataFile::getFileHistogram() { if (m_fileHistogram == NULL) { std::vector fileData; getFileData(fileData); if ( ! fileData.empty()) { m_fileHistogram.grabNew(new Histogram()); m_fileHistogram->update(&fileData[0], fileData.size()); } } return m_fileHistogram; } /** * Get histogram describing the distribution of data * mapped with a color palette for all data in the file * within the given range of values. * * @param mostPositiveValueInclusive * Values more positive than this value are excluded. * @param leastPositiveValueInclusive * Values less positive than this value are excluded. * @param leastNegativeValueInclusive * Values less negative than this value are excluded. * @param mostNegativeValueInclusive * Values more negative than this value are excluded. * @param includeZeroValues * If true zero values (very near zero) are included. * @return * Descriptive statistics for data (will be NULL for data * not mapped using a palette). */ const Histogram* CiftiMappableDataFile::getFileHistogram(const float mostPositiveValueInclusive, const float leastPositiveValueInclusive, const float leastNegativeValueInclusive, const float mostNegativeValueInclusive, const bool includeZeroValues) { 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; getFileData(fileData); if ( ! fileData.empty()) { if (m_fileHistorgramLimitedValues == NULL) { m_fileHistorgramLimitedValues.grabNew(new Histogram()); } m_fileHistorgramLimitedValues->update(&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; } /** * Get the palette color mapping for the map at the given index. * * @param mapIndex * Index of the map. * @return * Palette color mapping for the map (will be NULL for data * not mapped using a palette). */ PaletteColorMapping* CiftiMappableDataFile::getMapPaletteColorMapping(const int32_t mapIndex) { CaretAssert(m_ciftiFile); const CiftiXML& ciftiXML = m_ciftiFile->getCiftiXML(); PaletteColorMapping* pcm = NULL; 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: pcm = ciftiXML.getFilePalette(); break; case PALETTE_COLOR_MAPPING_SOURCE_FROM_MAP: CaretAssertVectorIndex(m_mapContent, mapIndex); pcm = m_mapContent[mapIndex]->m_paletteColorMapping; break; } break; } return pcm; } /** * Get the palette color mapping for the map at the given index. * * @param mapIndex * Index of the map. * @return * Palette color mapping for the map (constant) (will be NULL for data * not mapped using a palette). */ const PaletteColorMapping* CiftiMappableDataFile::getMapPaletteColorMapping(const int32_t mapIndex) const { CaretAssert(m_ciftiFile); const CiftiXML& ciftiXML = m_ciftiFile->getCiftiXML(); PaletteColorMapping* pcm = NULL; 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: pcm = ciftiXML.getFilePalette(); break; case PALETTE_COLOR_MAPPING_SOURCE_FROM_MAP: CaretAssertVectorIndex(m_mapContent, mapIndex); pcm = m_mapContent[mapIndex]->m_paletteColorMapping; break; } break; } return pcm; } /** * Get the CIFTI parcels map used for brainordinate mapping. * * @return * Pointer to the map's Cifti Parcels Map or NULL if the file is not * mapped using parcels. */ const CiftiParcelsMap* CiftiMappableDataFile::getCiftiParcelsMapForBrainordinateMapping() const { CaretAssert((m_dataMappingDirectionForCiftiXML == CiftiXML::ALONG_ROW) || (m_dataMappingDirectionForCiftiXML == CiftiXML::ALONG_COLUMN)); return getCiftiParcelsMapForDirection(m_dataMappingDirectionForCiftiXML); } /** * Get the CIFTI parcels map used for data loading. * * @return * Pointer to the map's Cifti Parcels Map or NULL if the file is not * loaded using parcels. */ const CiftiParcelsMap* CiftiMappableDataFile::getCiftiParcelsMapForLoading() const { CaretAssert((m_dataReadingDirectionForCiftiXML == CiftiXML::ALONG_ROW) || (m_dataReadingDirectionForCiftiXML == CiftiXML::ALONG_COLUMN)); return getCiftiParcelsMapForDirection(m_dataReadingDirectionForCiftiXML); } /** * Get the CIFTI parcels for the given direction. * * @param direction * Direction of mapping. MUST BE one of CiftiXML::ALONG_ROW or * CiftiXML::ALONG_COLUMN. * @return * Pointer to the map's Cifti Parcels Map or NULL if the file is not * mapped using parcels or NULL if direction is invalid. */ const CiftiParcelsMap* CiftiMappableDataFile::getCiftiParcelsMapForDirection(const int direction) const { if (m_ciftiFile != NULL) { if ((direction != CiftiXML::ALONG_ROW) && (direction != CiftiXML::ALONG_COLUMN)) { CaretAssert(0); return NULL; } const CiftiXML& ciftiXML = m_ciftiFile->getCiftiXML(); const CiftiMappingType* mapping = ciftiXML.getMap(direction); if (mapping->getType() == CiftiMappingType::PARCELS) { const CiftiParcelsMap* cpm = dynamic_cast(mapping); CaretAssert(cpm); return cpm; } } return NULL; } /** * @return Is the data in the file mapped to colors using * a label table. */ bool CiftiMappableDataFile::isMappedWithLabelTable() const { switch (m_colorMappingMethod) { case COLOR_MAPPING_METHOD_INVALID: break; case COLOR_MAPPING_METHOD_LABEL_TABLE: return true; break; case COLOR_MAPPING_METHOD_PALETTE: break; } return false; } /** * Get the label table for the map at the given index. * * @param mapIndex * Index of the map. * @return * Label table for the map (will be NULL for data * not mapped using a label table). */ GiftiLabelTable* CiftiMappableDataFile::getMapLabelTable(const int32_t mapIndex) { CaretAssertVectorIndex(m_mapContent, mapIndex); switch (m_colorMappingMethod) { case COLOR_MAPPING_METHOD_INVALID: break; case COLOR_MAPPING_METHOD_LABEL_TABLE: return m_mapContent[mapIndex]->m_labelTable; break; case COLOR_MAPPING_METHOD_PALETTE: break; } return NULL; } /** * Get the label table for the map at the given index. * * @param mapIndex * Index of the map. * @return * Label table for the map (constant) (will be NULL for data * not mapped using a label table). */ const GiftiLabelTable* CiftiMappableDataFile::getMapLabelTable(const int32_t mapIndex) const { CaretAssertVectorIndex(m_mapContent, mapIndex); switch (m_colorMappingMethod) { case COLOR_MAPPING_METHOD_INVALID: break; case COLOR_MAPPING_METHOD_LABEL_TABLE: return m_mapContent[mapIndex]->m_labelTable; break; case COLOR_MAPPING_METHOD_PALETTE: break; } return NULL; } /** * Get the palette normalization modes that are supported by the file. * * @param modesSupportedOut * Palette normalization modes supported by a file. Will be * empty for files that are not mapped with a palette. If there * is more than one suppported mode, the first mode in the * vector is assumed to be the default mode. */ void CiftiMappableDataFile::getPaletteNormalizationModesSupported(std::vector& modesSupportedOut) { modesSupportedOut = m_paletteNormalizationModesSupported; } /** * Update coloring for all maps. * * Note: Overridden since Data-Series files have one palette that is * applied to ALL maps. For data-series, just invalidate the coloring * for all maps (data points). * * @param paletteFile * Palette file containing palettes. */ void CiftiMappableDataFile::updateScalarColoringForAllMaps(const PaletteFile* /*paletteFile*/) { /* * Just need to invalidate coloring. * Updating coloring for all maps would take time. * Coloring update is triggered by code that colors nodes/voxels * when drawing. */ invalidateColoringInAllMaps(); } /** * Update scalar coloring for a map. * * Note that some CIFTI files can be slow to color due to the need to * retrieve data for the map. Use isMapColoringValid() to avoid * unnecessary calls to isMapColoringValid. * * @param mapIndex * Index of map. * @param paletteFile * Palette file containing palettes. */ void CiftiMappableDataFile::updateScalarColoringForMap(const int32_t mapIndex, const PaletteFile* paletteFile) { CaretAssertVectorIndex(m_mapContent, mapIndex); std::vector data; getMapData(mapIndex, data); m_mapContent[mapIndex]->m_rgbaValid = false; if (isMappedWithPalette()) { FastStatistics* statistics = NULL; switch (getPaletteNormalizationMode()) { case PaletteNormalizationModeEnum::NORMALIZATION_ALL_MAP_DATA: statistics = const_cast(getFileFastStatistics()); break; case PaletteNormalizationModeEnum::NORMALIZATION_SELECTED_MAP_DATA: statistics = const_cast(getMapFastStatistics(mapIndex)); break; } m_mapContent[mapIndex]->updateColoring(data, paletteFile, statistics); } else if (isMappedWithLabelTable()) { m_mapContent[mapIndex]->updateColoring(data, paletteFile, NULL); } else { CaretAssert(0); } } /** * Note that some CIFTI files can be slow to color due to the need to * retrieve data for the map. This method can be used to avoid calls * to updateScalarColoringForMap. * * @param mapIndex * Index of the map. * @return * True if the coloring for the given map index is valid. */ bool CiftiMappableDataFile::isMapColoringValid(const int32_t mapIndex) const { CaretAssertVectorIndex(m_mapContent, mapIndex); return m_mapContent[mapIndex]->m_rgbaValid; } /** * Get the node ins the parcel of the given index. * @param parcelNodes * Output containing the node indices. * @param structure * Structure for which the node indices are requested. * @param selectionIndex * Index of the parcel. * @return true if parcel is valid, else false. */ bool CiftiMappableDataFile::getParcelNodesElementForSelectedParcel(std::set &parcelNodesOut, const StructureEnum::Enum &structure, const int64_t &selectionIndex) const { if (m_ciftiFile->getCiftiXML().getMappingType(CiftiXML::ALONG_COLUMN) != CiftiMappingType::PARCELS) return false; const std::vector& parcels = m_ciftiFile->getCiftiXML().getParcelsMap(CiftiXML::ALONG_COLUMN).getParcels(); if(selectionIndex >= 0 && selectionIndex < (int64_t)parcels.size()) { const CiftiParcelsMap::Parcel& parcelOut = parcels[selectionIndex]; std::map >::const_iterator findStruct = parcelOut.m_surfaceNodes.find(structure); if (findStruct != parcelOut.m_surfaceNodes.end()) { parcelNodesOut = findStruct->second; return true; } } return false; } /** * Get the dimensions of the volume. * * @param dimOut1 * First dimension (i) out. * @param dimOut2 * Second dimension (j) out. * @param dimOut3 * Third dimension (k) out. * @param dimTimeOut * Time dimensions out (number of maps) * @param numComponentsOut * Number of components per voxel. */ void CiftiMappableDataFile::getDimensions(int64_t& dimOut1, int64_t& dimOut2, int64_t& dimOut3, int64_t& dimTimeOut, int64_t& numComponentsOut) const { CaretAssert(m_ciftiFile); dimOut1 = 0; dimOut2 = 0; dimOut3 = 0; dimTimeOut = 0; numComponentsOut = 0; if (m_dataMappingDirectionForCiftiXML != S_CIFTI_XML_ALONG_INVALID) { switch (m_ciftiFile->getCiftiXML().getMappingType(m_dataMappingDirectionForCiftiXML)) { case CiftiMappingType::BRAIN_MODELS: { const CiftiBrainModelsMap& myDenseMap = m_ciftiFile->getCiftiXML().getBrainModelsMap(m_dataMappingDirectionForCiftiXML); if (!myDenseMap.hasVolumeData()) return; const VolumeSpace& mySpace = myDenseMap.getVolumeSpace(); const int64_t* dims = mySpace.getDims(); dimOut1 = dims[0]; dimOut2 = dims[1]; dimOut3 = dims[2]; dimTimeOut = 1;//??? numComponentsOut = 1; break; } case CiftiMappingType::PARCELS: { const CiftiParcelsMap& myParcelMap = m_ciftiFile->getCiftiXML().getParcelsMap(m_dataMappingDirectionForCiftiXML); if (!myParcelMap.hasVolumeData()) return; const VolumeSpace& mySpace = myParcelMap.getVolumeSpace(); const int64_t* dims = mySpace.getDims(); dimOut1 = dims[0]; dimOut2 = dims[1]; dimOut3 = dims[2]; dimTimeOut = 1;//??? numComponentsOut = 1; break; } default://nothing else has volume dimensions break; } } } /** * Get the dimensions of the volume. * * @param dimsOut * Will contain 5 elements: (0) X-dimension, (1) Y-dimension * (2) Z-dimension, (3) time, (4) components. */ void CiftiMappableDataFile::getDimensions(std::vector& dimsOut) const { dimsOut.resize(5); int64_t dimI, dimJ, dimK, dimTime, dimComp; getDimensions(dimI, dimJ, dimK, dimTime, dimComp); dimsOut[0] = dimI; dimsOut[1] = dimJ; dimsOut[2] = dimK; dimsOut[3] = dimTime; dimsOut[4] = dimComp; } /** * @return The number of componenents per voxel in the volume data. */ const int64_t& CiftiMappableDataFile::getNumberOfComponents() const { int64_t dimI, dimJ, dimK, dimTime; static int64_t dimComp = 0; getDimensions(dimI, dimJ, dimK, dimTime, dimComp); return dimComp; } /** * Convert an index to space (coordinates). * * @param indexIn1 * First dimension (i). * @param indexIn2 * Second dimension (j). * @param indexIn3 * Third dimension (k). * @param coordOut1 * Output first (x) coordinate. * @param coordOut2 * Output first (y) coordinate. * @param coordOut3 * Output first (z) coordinate. */ void CiftiMappableDataFile::indexToSpace(const float& indexIn1, const float& indexIn2, const float& indexIn3, float& coordOut1, float& coordOut2, float& coordOut3) const { CaretAssert(m_voxelIndicesToOffset); m_voxelIndicesToOffset->indicesToCoordinate(indexIn1, indexIn2, indexIn3, coordOut1, coordOut2, coordOut3); } /** * Convert an index to space (coordinates). * * @param indexIn1 * First dimension (i). * @param indexIn2 * Second dimension (j). * @param indexIn3 * Third dimension (k). * @param coordOut * Output XYZ coordinates. */ void CiftiMappableDataFile::indexToSpace(const float& indexIn1, const float& indexIn2, const float& indexIn3, float* coordOut) const { CaretAssert(m_voxelIndicesToOffset); m_voxelIndicesToOffset->indicesToCoordinate(indexIn1, indexIn2, indexIn3, coordOut[0], coordOut[1], coordOut[2]); } /** * Convert an index to space (coordinates). * * @param indexIn * IJK indices * @param coordOut * Output XYZ coordinates. */ void CiftiMappableDataFile::indexToSpace(const int64_t* indexIn, float* coordOut) const { CaretAssert(m_voxelIndicesToOffset); m_voxelIndicesToOffset->indicesToCoordinate(indexIn[0], indexIn[1], indexIn[2], coordOut[0], coordOut[1], coordOut[2]); } /** * Convert a coordinate to indices. Note that output indices * MAY NOT BE WITHIN THE VALID VOXEL DIMENSIONS. * * @param coordIn1 * First (x) input coordinate. * @param coordIn2 * Second (y) input coordinate. * @param coordIn3 * Third (z) input coordinate. * @param indexOut1 * First output index (i). * @param indexOut2 * First output index (j). * @param indexOut3 * First output index (k). */ void CiftiMappableDataFile::enclosingVoxel(const float& coordIn1, const float& coordIn2, const float& coordIn3, int64_t& indexOut1, int64_t& indexOut2, int64_t& indexOut3) const { CaretAssert(m_voxelIndicesToOffset); m_voxelIndicesToOffset->coordinateToIndices(coordIn1, coordIn2, coordIn3, indexOut1, indexOut2, indexOut3); } /** * Determine in the given voxel indices are valid (within the volume dimensions). * * @param indexIn1 * First dimension (i). * @param indexIn2 * Second dimension (j). * @param indexIn3 * Third dimension (k). * @param coordOut1 * Output first (x) coordinate. * @param brickIndex * Time/map index (default 0). * @param component * Voxel component (default 0). */ bool CiftiMappableDataFile::indexValid(const int64_t& indexIn1, const int64_t& indexIn2, const int64_t& indexIn3, const int64_t /*brickIndex*/, const int64_t /*component*/) const { std::vector volumeDimensions; getDimensions(volumeDimensions); CaretAssertVectorIndex(volumeDimensions, 2); if ((indexIn1 >= 0) && (indexIn1 < volumeDimensions[0]) && (indexIn2 >= 0) && (indexIn2 < volumeDimensions[1]) && (indexIn3 >= 0) && (indexIn3 < volumeDimensions[2])) { return true; } return false; } const VolumeSpace& CiftiMappableDataFile::getVolumeSpace() const { CaretAssert(m_voxelIndicesToOffset);//because this is where the other space functions get their volume space from, just roll with it for now return m_voxelIndicesToOffset->getVolumeSpace(); } /** * Get a bounding box for the voxel coordinate ranges. * * @param boundingBoxOut * The output bounding box. */ void CiftiMappableDataFile::getVoxelSpaceBoundingBox(BoundingBox& boundingBoxOut) const { CaretAssert(m_voxelIndicesToOffset); boundingBoxOut.resetForUpdate(); std::vector volumeDimensions(5, 0); getDimensions(volumeDimensions); CaretAssertVectorIndex(volumeDimensions, 2); if (m_voxelIndicesToOffset->isValid()) { float xyz[3]; indexToSpace(0, 0, 0, xyz); boundingBoxOut.update(xyz); indexToSpace(volumeDimensions[0] - 1, volumeDimensions[1] - 1, volumeDimensions[2] - 1, xyz); boundingBoxOut.update(xyz); } else { boundingBoxOut.resetZeros(); } } /** * Get the voxel colors for a slice in the map. * * @param paletteFile * The palette file. * @param mapIndex * Index of the map. * @param slicePlane * The slice plane. * @param sliceIndex * Index of the slice. * @param displayGroup * The selected display group. * @param tabIndex * Index of selected tab. * @param rgbaOut * Output containing the rgba values (must have been allocated * by caller to sufficient count of elements in the slice). * @return * Number of voxels with alpha greater than zero */ int64_t CiftiMappableDataFile::getVoxelColorsForSliceInMap(const PaletteFile* paletteFile, const int32_t mapIndex, const VolumeSliceViewPlaneEnum::Enum slicePlane, const int64_t sliceIndex, const DisplayGroupEnum::Enum displayGroup, const int32_t tabIndex, uint8_t* rgbaOut) const { CaretAssertVectorIndex(m_mapContent, mapIndex); CaretAssertMessage((sliceIndex >= 0), "Slice index is invalid."); if (sliceIndex < 0) { return 0; } if (isMapColoringValid(mapIndex) == false) { CiftiMappableDataFile* nonConstThis = const_cast(this); nonConstThis->updateScalarColoringForMap(mapIndex, paletteFile); } int64_t dimI, dimJ, dimK, dimTime, dimComp; getDimensions(dimI, dimJ, dimK, dimTime, dimComp); int64_t voxelCount = 0; switch (slicePlane) { case VolumeSliceViewPlaneEnum::ALL: CaretAssert(0); break; case VolumeSliceViewPlaneEnum::AXIAL: voxelCount = dimI * dimJ; CaretAssert((sliceIndex < dimK)); if (sliceIndex >= dimK) { return 0; } break; case VolumeSliceViewPlaneEnum::CORONAL: voxelCount = dimI * dimK; CaretAssert((sliceIndex < dimJ)); if (sliceIndex >= dimJ) { return 0; } break; case VolumeSliceViewPlaneEnum::PARASAGITTAL: voxelCount = dimJ * dimK; CaretAssert((sliceIndex < dimI)); if (sliceIndex >= dimI) { return 0; } break; } if (voxelCount <= 0) { return 0; } const int64_t componentCount = voxelCount * 4; /* * Clear the slice rgba coloring. */ for (int64_t i = 0; i < componentCount; i++) { rgbaOut[i] = 0; } 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 validVoxelCount = 0; /* * Set the rgba components for the slice. */ switch (slicePlane) { case VolumeSliceViewPlaneEnum::ALL: CaretAssert(0); break; case VolumeSliceViewPlaneEnum::AXIAL: for (int64_t j = 0; j < dimJ; j++) { for (int64_t i = 0; i < dimI; i++) { const int64_t dataOffset = m_voxelIndicesToOffset->getOffsetForIndices(i, j, sliceIndex); if (dataOffset >= 0) { const int64_t dataOffset4 = dataOffset * 4; CaretAssert(dataOffset4 < mapRgbaCount); const int64_t rgbaOffset = ((j * dimI) + i) * 4; CaretAssert(rgbaOffset < componentCount); rgbaOut[rgbaOffset] = mapRGBA[dataOffset4]; rgbaOut[rgbaOffset+1] = mapRGBA[dataOffset4+1]; rgbaOut[rgbaOffset+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; } rgbaOut[rgbaOffset+3] = (alpha * 255.0); } } } break; case VolumeSliceViewPlaneEnum::CORONAL: for (int64_t k = 0; k < dimK; k++) { for (int64_t i = 0; i < dimI; i++) { const int64_t dataOffset = m_voxelIndicesToOffset->getOffsetForIndices(i, sliceIndex, k); if (dataOffset >= 0) { const int64_t dataOffset4 = dataOffset * 4; CaretAssert(dataOffset4 < mapRgbaCount); const int64_t rgbaOffset = ((k * dimI) + i) * 4; CaretAssert(rgbaOffset < componentCount); rgbaOut[rgbaOffset] = mapRGBA[dataOffset4]; rgbaOut[rgbaOffset+1] = mapRGBA[dataOffset4+1]; rgbaOut[rgbaOffset+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; } rgbaOut[rgbaOffset+3] = (alpha * 255.0); } } } break; case VolumeSliceViewPlaneEnum::PARASAGITTAL: for (int64_t k = 0; k < dimK; k++) { for (int64_t j = 0; j < dimJ; j++) { const int64_t dataOffset = m_voxelIndicesToOffset->getOffsetForIndices(sliceIndex, j, k); if (dataOffset >= 0) { const int64_t dataOffset4 = dataOffset * 4; CaretAssert(dataOffset4 < mapRgbaCount); const int64_t rgbaOffset = ((k * dimJ) + j) * 4; CaretAssert(rgbaOffset < componentCount); rgbaOut[rgbaOffset] = mapRGBA[dataOffset4]; rgbaOut[rgbaOffset+1] = mapRGBA[dataOffset4+1]; rgbaOut[rgbaOffset+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; } rgbaOut[rgbaOffset+3] = (alpha * 255.0); } } } break; } return validVoxelCount; } /** * Get voxel coloring for a set of voxels. * * @param mapIndex * Index of map. * @param firstVoxelIJK * IJK Indices of first voxel * @param rowStepIJK * IJK Step for moving to next row. * @param columnStepIJK * IJK Step for moving to next column. * @param numberOfRows * Number of rows. * @param numberOfColumns * Number of columns. * @param displayGroup * The selected display group. * @param tabIndex * Index of selected tab. * @param rgbaOut * RGBA color components out. * @return * 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 { return 0; } /** * Get the voxel colors for a sub slice in the map. * * @param paletteFile * The palette file. * @param mapIndex * Index of the map. * @param slicePlane * The slice plane. * @param sliceIndex * Index of the slice. * @param firstCornerVoxelIndex * Indices of voxel for first corner of sub-slice (inclusive). * @param lastCornerVoxelIndex * Indices of voxel for last corner of sub-slice (inclusive). * @param voxelCountIJK * Voxel counts for each axis. * @param displayGroup * The selected display group. * @param tabIndex * Index of selected tab. * @param rgbaOut * Output containing the rgba values (must have been allocated * by caller to sufficient count of elements in the slice). * @return * Number of voxels with alpha greater than zero */ int64_t CiftiMappableDataFile::getVoxelColorsForSubSliceInMap(const PaletteFile* paletteFile, const int32_t mapIndex, const VolumeSliceViewPlaneEnum::Enum slicePlane, const int64_t sliceIndex, const int64_t firstCornerVoxelIndex[3], const int64_t lastCornerVoxelIndex[3], const int64_t voxelCountIJK[3], const DisplayGroupEnum::Enum displayGroup, const int32_t tabIndex, uint8_t* rgbaOut) const { CaretAssertVectorIndex(m_mapContent, mapIndex); CaretAssertMessage((sliceIndex >= 0), "Slice index is invalid."); if (sliceIndex < 0) { return 0; } if (isMapColoringValid(mapIndex) == false) { CiftiMappableDataFile* nonConstThis = const_cast(this); nonConstThis->updateScalarColoringForMap(mapIndex, paletteFile); } const int64_t iStart = firstCornerVoxelIndex[0]; const int64_t jStart = firstCornerVoxelIndex[1]; const int64_t kStart = firstCornerVoxelIndex[2]; const int64_t iEnd = lastCornerVoxelIndex[0]; const int64_t jEnd = lastCornerVoxelIndex[1]; const int64_t kEnd = lastCornerVoxelIndex[2]; const int64_t voxelCountI = voxelCountIJK[0]; const int64_t voxelCountJ = voxelCountIJK[1]; const int64_t voxelCountK = voxelCountIJK[2]; int64_t dimI, dimJ, dimK, dimTime, dimComp; getDimensions(dimI, dimJ, dimK, dimTime, dimComp); int64_t voxelCount = 0; switch (slicePlane) { case VolumeSliceViewPlaneEnum::ALL: CaretAssert(0); break; case VolumeSliceViewPlaneEnum::AXIAL: voxelCount = voxelCountI * voxelCountJ; CaretAssert((sliceIndex < dimK)); if (sliceIndex >= dimK) { return 0; } break; case VolumeSliceViewPlaneEnum::CORONAL: voxelCount = voxelCountI * voxelCountK; CaretAssert((sliceIndex < dimJ)); if (sliceIndex >= dimJ) { return 0; } break; case VolumeSliceViewPlaneEnum::PARASAGITTAL: voxelCount = voxelCountJ * voxelCountK; CaretAssert((sliceIndex < dimI)); if (sliceIndex >= dimI) { return 0; } break; } if (voxelCount <= 0) { return 0; } const int64_t componentCount = voxelCount * 4; /* * Clear the slice rgba coloring. */ for (int64_t i = 0; i < componentCount; i++) { rgbaOut[i] = 0; } 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); } /* * Note that step indices may be positive or negative */ const int64_t kStep = ((kEnd < kStart) ? -1 : 1); const int64_t jStep = ((jEnd < jStart) ? -1 : 1); const int64_t iStep = ((iEnd < iStart) ? -1 : 1); int64_t validVoxelCount = 0; /* * Set the rgba components for the slice. */ switch (slicePlane) { case VolumeSliceViewPlaneEnum::ALL: CaretAssert(0); break; case VolumeSliceViewPlaneEnum::AXIAL: { int64_t rgbaOffset = 0; int64_t j = jStart; bool jLoopFlag = true; while (jLoopFlag) { int64_t i = iStart; bool iLoopFlag = true; while (iLoopFlag) { const int64_t dataOffset = m_voxelIndicesToOffset->getOffsetForIndices(i, j, sliceIndex); if (dataOffset >= 0) { const int64_t dataOffset4 = dataOffset * 4; CaretAssert(dataOffset4 < mapRgbaCount); CaretAssert(rgbaOffset < componentCount); rgbaOut[rgbaOffset] = mapRGBA[dataOffset4]; rgbaOut[rgbaOffset+1] = mapRGBA[dataOffset4+1]; rgbaOut[rgbaOffset+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; } rgbaOut[rgbaOffset+3] = (alpha * 255.0); } if (i == iEnd) { iLoopFlag = false; } else { i += iStep; } rgbaOffset += 4; } if (j == jEnd) { jLoopFlag = false; } else { j += jStep; } } } break; case VolumeSliceViewPlaneEnum::CORONAL: { int64_t rgbaOffset = 0; int64_t k = kStart; bool kLoopFlag = true; while (kLoopFlag) { int64_t i = iStart; bool iLoopFlag = true; while (iLoopFlag) { const int64_t dataOffset = m_voxelIndicesToOffset->getOffsetForIndices(i, sliceIndex, k); if (dataOffset >= 0) { const int64_t dataOffset4 = dataOffset * 4; CaretAssert(dataOffset4 < mapRgbaCount); CaretAssert(rgbaOffset < componentCount); rgbaOut[rgbaOffset] = mapRGBA[dataOffset4]; rgbaOut[rgbaOffset+1] = mapRGBA[dataOffset4+1]; rgbaOut[rgbaOffset+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; } rgbaOut[rgbaOffset+3] = (alpha * 255.0); } if (i == iEnd) { iLoopFlag = false; } else { i += iStep; } rgbaOffset += 4; } if (k == kEnd) { kLoopFlag = false; } else { k += kStep; } } } break; case VolumeSliceViewPlaneEnum::PARASAGITTAL: { int64_t rgbaOffset = 0; int64_t k = kStart; bool kLoopFlag = true; while (kLoopFlag) { int64_t j = jStart; bool jLoopFlag = true; while (jLoopFlag) { const int64_t dataOffset = m_voxelIndicesToOffset->getOffsetForIndices(sliceIndex, j, k); if (dataOffset >= 0) { const int64_t dataOffset4 = dataOffset * 4; CaretAssert(dataOffset4 < mapRgbaCount); CaretAssert(rgbaOffset < componentCount); rgbaOut[rgbaOffset] = mapRGBA[dataOffset4]; rgbaOut[rgbaOffset+1] = mapRGBA[dataOffset4+1]; rgbaOut[rgbaOffset+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; } rgbaOut[rgbaOffset+3] = (alpha * 255.0); } if (j == jEnd) { jLoopFlag = false; } else { j += jStep; } rgbaOffset += 4; } if (k == kEnd) { kLoopFlag = false; } else { k += kStep; } } } for (int64_t k = kStart; k <= kEnd; k++) { for (int64_t j = jStart; j < jEnd; j++) { const int64_t dataOffset = m_voxelIndicesToOffset->getOffsetForIndices(sliceIndex, j, k); if (dataOffset >= 0) { const int64_t dataOffset4 = dataOffset * 4; CaretAssert(dataOffset4 < mapRgbaCount); const int64_t rgbaOffset = (((k - kStart) * voxelCountJ) + (j - jStart)) * 4; CaretAssert(rgbaOffset < componentCount); rgbaOut[rgbaOffset] = mapRGBA[dataOffset4]; rgbaOut[rgbaOffset+1] = mapRGBA[dataOffset4+1]; rgbaOut[rgbaOffset+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; } rgbaOut[rgbaOffset+3] = (alpha * 255.0); } } } break; } return validVoxelCount; } /** * Get the voxel coloring for the voxel at the given indices. * This method is for label data. Accessing the actual voxel values is * needed for coloring labels. But, one can only access the entire set * of values for a map. Since this method is typically called many times * when coloring slices in ALL view, get the map data value before calling * this and then pass them in. * * This will work for non-label data. * * @param paletteFile * The palette file. * @param dataForMap * Data for the map. * @param indexIn1 * First dimension (i). * @param indexIn2 * Second dimension (j). * @param indexIn3 * Third dimension (k). * @param mapIndex * Time/map index. * @param displayGroup * The selected display group. * @param tabIndex * Index of selected tab. * @param rgbaOut * Output containing RGBA values for voxel at the given indices. */ void CiftiMappableDataFile::getVoxelColorInMapForLabelData(const PaletteFile* paletteFile, const std::vector& dataForMap, 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 { getVoxelColorInMap(paletteFile, indexIn1, indexIn2, indexIn3, mapIndex, displayGroup, tabIndex, rgbaOut); if (isMappedWithLabelTable()) { if (rgbaOut[3] > 0.0) { const GiftiLabelTable* labelTable = getMapLabelTable(mapIndex); CaretAssert(labelTable); const int64_t dataOffset = m_voxelIndicesToOffset->getOffsetForIndices(indexIn1, indexIn2, indexIn3); if (dataOffset >= 0) { /* * If the label is NOT selected for the given display * group and tab, inhibit its display by setting the * alpha component to zero. */ CaretAssertVectorIndex(dataForMap, dataOffset); const int32_t labelKey = dataForMap[dataOffset]; const GiftiLabel* label = labelTable->getLabel(labelKey); if (label != NULL) { const GroupAndNameHierarchyItem* item = label->getGroupNameSelectionItem(); if (item != NULL) { if (item->isSelected(displayGroup, tabIndex) == false) { rgbaOut[3] = 0.0; } } } } } } } /** * Get the voxel coloring for the voxel at the given indices. * * @see getVoxelColorInMapForLabelData * * @param paletteFile * The palette file. * @param indexIn1 * First dimension (i). * @param indexIn2 * Second dimension (j). * @param indexIn3 * Third dimension (k). * @param mapIndex * Time/map index. * @param displayGroup * The selected display group. * @param tabIndex * Index of selected tab. * @param rgbaOut * 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, const DisplayGroupEnum::Enum /*displayGroup*/, const int32_t /*tabIndex*/, uint8_t rgbaOut[4]) const { rgbaOut[0] = 0; rgbaOut[1] = 0; rgbaOut[2] = 0; rgbaOut[3] = 0; if ( ! isMapColoringValid(mapIndex)) { CiftiMappableDataFile* nonConstThis = const_cast(this); nonConstThis->updateScalarColoringForMap(mapIndex, paletteFile); } CaretAssert(m_voxelIndicesToOffset); const int64_t mapRgbaCount = m_mapContent[mapIndex]->m_rgba.size(); if (mapRgbaCount <= 0) { return; } const uint8_t* mapRGBA = &m_mapContent[mapIndex]->m_rgba[0]; const int64_t dataOffset = m_voxelIndicesToOffset->getOffsetForIndices(indexIn1, indexIn2, indexIn3); if (dataOffset >= 0) { const int64_t dataOffset4 = dataOffset * 4; CaretAssert(dataOffset4 < mapRgbaCount); rgbaOut[0] = mapRGBA[dataOffset4]; rgbaOut[1] = mapRGBA[dataOffset4+1]; rgbaOut[2] = mapRGBA[dataOffset4+2]; rgbaOut[3] = mapRGBA[dataOffset4+3]; } } /** * Get the brainordinate from the given row. * * @param rowIndex * Index of the row. * @param surfaceStructureOut * Will contain structure of surface if row is a surface node. * @param surfaceNodeIndexOut * Will contain index of surface node if row is a surface node. * @param surfaceNumberOfNodesOut * Will contain surfaces number of nodes if row is a surface node. * @param surfaceNodeValidOut * Will be true upon exit if the row corresponded to a surface node. * @param voxelIJKOut * Will contain the voxel's IJK indices if row is a surface node. * @param voxelXYZOut * Will contain the voxel's XYZ coordinate if row is a surface node. * @param voxelValidOut * Will be true upon exit if the row corresponded to a surface node. * @throw DataFileException * If the rows are not for brainordinates or the row index is invalid. */ void CiftiMappableDataFile::getBrainordinateFromRowIndex(const int64_t rowIndex, StructureEnum::Enum& surfaceStructureOut, int32_t& surfaceNodeIndexOut, int32_t& surfaceNumberOfNodesOut, bool& surfaceNodeValidOut, int64_t voxelIJKOut[3], float voxelXYZOut[3], bool& voxelValidOut) const { surfaceNodeValidOut = false; voxelValidOut = false; if (m_ciftiFile == NULL) { return; } const CiftiXML& ciftiXML = m_ciftiFile->getCiftiXML(); if (ciftiXML.getMappingType(CiftiXML::ALONG_COLUMN) != CiftiMappingType::BRAIN_MODELS) { throw DataFileException(getFileName(), "File does not have brainordinate data for rows."); return; } const CiftiBrainModelsMap& brainMap = ciftiXML.getBrainModelsMap(CiftiXML::ALONG_COLUMN); if ((rowIndex < 0) || (rowIndex >= m_ciftiFile->getNumberOfRows())) { throw DataFileException(getFileName(), "Row index " + AString::number(rowIndex) + " is out of range [0, " + AString::number(m_ciftiFile->getNumberOfRows() - 1) + "]"); } const CiftiBrainModelsMap::IndexInfo indexInfo = brainMap.getInfoForIndex(rowIndex); switch (indexInfo.m_type) { case CiftiBrainModelsMap::SURFACE: surfaceStructureOut = indexInfo.m_structure; surfaceNodeIndexOut = indexInfo.m_surfaceNode; surfaceNumberOfNodesOut = brainMap.getSurfaceNumberOfNodes(surfaceStructureOut); surfaceNodeValidOut = true; break; case CiftiBrainModelsMap::VOXELS: voxelIJKOut[0] = indexInfo.m_ijk[0]; voxelIJKOut[1] = indexInfo.m_ijk[1]; voxelIJKOut[2] = indexInfo.m_ijk[2]; indexToSpace(voxelIJKOut, voxelXYZOut); voxelValidOut = true; break; } } /** * Get the unique label keys in the given map. * @param mapIndex * Index of the map. * @return * Keys used by the map. */ std::vector CiftiMappableDataFile::getUniqueLabelKeysUsedInMap(const int32_t mapIndex) const { CaretAssertVectorIndex(m_mapContent, mapIndex); std::vector data; getMapData(mapIndex, data); std::set uniqueKeys; const int64_t numItems = static_cast(data.size()); if (numItems > 0) { const float* dataPtr = &data[0]; for (int64_t i = 0; i < numItems; i++) { const int32_t key = static_cast(dataPtr[i]); uniqueKeys.insert(key); } } std::vector keyVector; keyVector.insert(keyVector.end(), uniqueKeys.begin(), uniqueKeys.end()); return keyVector; } /** * @return The class and name hierarchy. */ GroupAndNameHierarchyModel* CiftiMappableDataFile::getGroupAndNameHierarchyModel() { CaretAssert(m_classNameHierarchy); m_classNameHierarchy->update(this, m_forceUpdateOfGroupAndNameHierarchy); m_forceUpdateOfGroupAndNameHierarchy = false; return m_classNameHierarchy; } /** * @return The class and name hierarchy. */ const GroupAndNameHierarchyModel* CiftiMappableDataFile::getGroupAndNameHierarchyModel() const { CaretAssert(m_classNameHierarchy); m_classNameHierarchy->update(const_cast(this), m_forceUpdateOfGroupAndNameHierarchy); m_forceUpdateOfGroupAndNameHierarchy = false; return m_classNameHierarchy; } /** * Validate keys and labels in the file. */ void CiftiMappableDataFile::validateKeysAndLabels() const { /* * Skip if not label file */ if (isMappedWithLabelTable() == false) { return; } /* * Skip if logging is not fine or less. */ if (CaretLogger::getLogger()->isFine() == false) { return; } AString messages; /* * Find the label keys that are in the data */ std::set dataKeys; const int32_t numMaps = getNumberOfMaps(); for (int32_t jMap = 0; jMap < numMaps; jMap++) { AString mapMessage; std::vector data; getMapData(jMap, data); const int64_t numItems = static_cast(data.size()); for (int32_t i = 0; i < numItems; i++) { const int32_t key = static_cast(data[i]); dataKeys.insert(key); } /* * Find any keys that are not in the label table */ const GiftiLabelTable* labelTable = getMapLabelTable(jMap); std::set missingLabelKeys; for (std::set::iterator dataKeyIter = dataKeys.begin(); dataKeyIter != dataKeys.end(); dataKeyIter++) { const int32_t dataKey = *dataKeyIter; const GiftiLabel* label = labelTable->getLabel(dataKey); if (label == NULL) { missingLabelKeys.insert(dataKey); } } if (missingLabelKeys.empty() == false) { for (std::set::iterator missingKeyIter = missingLabelKeys.begin(); missingKeyIter != missingLabelKeys.end(); missingKeyIter++) { const int32_t missingKey = *missingKeyIter; mapMessage.appendWithNewLine(" Missing Label for Key: " + AString::number(missingKey)); } } /* * Find any label table names that are not used */ std::map labelTableKeysAndNames; labelTable->getKeysAndNames(labelTableKeysAndNames); for (std::map::const_iterator ltIter = labelTableKeysAndNames.begin(); ltIter != labelTableKeysAndNames.end(); ltIter++) { const int32_t ltKey = ltIter->first; if (std::find(dataKeys.begin(), dataKeys.end(), ltKey) == dataKeys.end()) { mapMessage.appendWithNewLine(" Label Not Used Key=" + AString::number(ltKey) + ": " + ltIter->second); } } if (mapMessage.isEmpty() == false) { mapMessage = (" Map: " + getMapName(jMap) + ":\n" + mapMessage + "\n" + labelTable->toFormattedString(" ")); messages += mapMessage; } } AString msg = ("File: " + getFileName() + "\n" + messages); CaretLogFine(msg); } /** * Get connectivity value for a surface's node. When the data is mapped * to parcels, the numerical value will not be valid. * * @param mapIndex * Index of the map. * @param structure * Surface's structure. * @param nodeIndex * Index of the node * @param numberOfNodes * Number of nodes in the surface. * @param numericalValueOut * Numerical value out. * @param numericalValueOutValid * Output that indicates the numerical value output is valid. * For label data, this value will be the lable key. * @param textValueOut * Text containing node' value will always be valid if the method * returns true. For parcel data, this will contain the name of the * parcel. For label data, this will contain the name of the label. * For numerical data, this will contain the text representation * of the numerical value. * @return * True if the text value is valid. The numerical value may or may not * also be valid. */ bool CiftiMappableDataFile::getMapSurfaceNodeValue(const int32_t mapIndex, const StructureEnum::Enum structure, const int nodeIndex, const int32_t numberOfNodes, float& numericalValueOut, bool& numericalValueOutValid, AString& textValueOut) const { numericalValueOut = 0.0; numericalValueOutValid = false; CaretAssertVectorIndex(m_mapContent, mapIndex); CaretAssert(m_ciftiFile); const CiftiXML& ciftiXML = m_ciftiFile->getCiftiXML(); CaretAssert((m_dataMappingDirectionForCiftiXML == CiftiXML::ALONG_ROW) || (m_dataMappingDirectionForCiftiXML == CiftiXML::ALONG_COLUMN)); switch (ciftiXML.getMappingType(m_dataMappingDirectionForCiftiXML)) { case CiftiMappingType::BRAIN_MODELS: { const CiftiBrainModelsMap& map = ciftiXML.getBrainModelsMap(m_dataMappingDirectionForCiftiXML); if (map.getSurfaceNumberOfNodes(structure) == numberOfNodes) { const int64_t dataIndex = map.getIndexForNode(nodeIndex, structure); if (dataIndex >= 0) { std::vector mapData; getMapData(mapIndex, mapData); if (dataIndex < static_cast(mapData.size())) { numericalValueOut = mapData[dataIndex]; numericalValueOutValid = true; if (ciftiXML.getMappingType(m_dataReadingDirectionForCiftiXML) == CiftiMappingType::LABELS) { const GiftiLabelTable* glt = getMapLabelTable(mapIndex); const int32_t labelKey = static_cast(numericalValueOut); const GiftiLabel* gl = glt->getLabel(labelKey); if (gl != NULL) { textValueOut += gl->getName(); } else { textValueOut += ("InvalidLabelKey=" + AString::number(labelKey)); } } else { textValueOut = AString::number(numericalValueOut, 'f'); } return true; } } } } break; case CiftiMappingType::LABELS: CaretAssertMessage(0, "Mapping type should never be LABELS"); break; case CiftiMappingType::PARCELS: { int64_t parcelIndex = -1; const CiftiParcelsMap& map = ciftiXML.getParcelsMap(m_dataMappingDirectionForCiftiXML); if (map.getSurfaceNumberOfNodes(structure) == numberOfNodes) { const std::vector& parcels = map.getParcels(); parcelIndex = map.getIndexForNode(nodeIndex, structure); if ((parcelIndex >= 0) && (parcelIndex < static_cast(parcels.size()))) { textValueOut = parcels[parcelIndex].m_name; std::vector mapData; getMapData(mapIndex, mapData); // if (parcelIndex < static_cast(mapData.size())) { // textValueOut += (" " // + AString::number(mapData[parcelIndex])); // } } } if (parcelIndex >= 0) { int64_t itemIndex = -1; switch (ciftiXML.getMappingType(m_dataReadingDirectionForCiftiXML)) { case CiftiMappingType::BRAIN_MODELS: { const CiftiBrainModelsMap& map = ciftiXML.getBrainModelsMap(m_dataReadingDirectionForCiftiXML); if (map.getSurfaceNumberOfNodes(structure) == numberOfNodes) { itemIndex = map.getIndexForNode(nodeIndex, structure); } } break; case CiftiMappingType::LABELS: break; case CiftiMappingType::PARCELS: itemIndex = mapIndex; break; case CiftiMappingType::SCALARS: itemIndex = mapIndex; break; case CiftiMappingType::SERIES: itemIndex = mapIndex; break; } if (itemIndex >= 0) { const int64_t numRows = m_ciftiFile->getNumberOfRows(); const int64_t numCols = m_ciftiFile->getNumberOfColumns(); switch (m_dataReadingDirectionForCiftiXML) { case CiftiXML::ALONG_COLUMN: { std::vector data; data.resize(numRows); CaretAssert(parcelIndex < numCols); m_ciftiFile->getColumn(&data[0], parcelIndex); CaretAssertVectorIndex(data, itemIndex); textValueOut += (" " + AString::number(data[itemIndex])); } break; case CiftiXML::ALONG_ROW: { std::vector data; data.resize(numCols); CaretAssert(parcelIndex < numRows); m_ciftiFile->getRow(&data[0], parcelIndex); CaretAssertVectorIndex(data, itemIndex); textValueOut += (" " + AString::number(data[itemIndex])); } break; } } } } return true; break; case CiftiMappingType::SCALARS: CaretAssertMessage(0, "Mapping type should never be SCALARS"); break; case CiftiMappingType::SERIES: CaretAssertMessage(0, "Mapping type should never be SERIES"); break; } return false; } /** * Get connectivity value for a surface's node. When the data is mapped * to parcels, the numerical value will not be valid. * * @param mapIndices * Index of the map. * @param structure * Surface's structure. * @param nodeIndex * Index of the node * @param numberOfNodes * Number of nodes in the surface. * @param numericalValuesOut * Numerical values out for all map indices * @param numericalValuesOutValid * Output that indicates the numerical value output is valid. * For label data, this value will be the lable key. * @param textValueOut * Text containing node' value will always be valid if the method * returns true. For parcel data, this will contain the name of the * parcel. For label data, this will contain the name of the label. * For numerical data, this will contain the text representation * of the numerical value. * @return * True if the text value is valid. The numerical values may or may not * also be valid. */ bool CiftiMappableDataFile::getMapSurfaceNodeValues(const std::vector& mapIndices, const StructureEnum::Enum structure, const int nodeIndex, const int32_t numberOfNodes, std::vector& numericalValuesOut, std::vector& numericalValuesOutValid, AString& textValueOut) const { numericalValuesOut.clear(); numericalValuesOutValid.clear(); textValueOut.clear(); CaretAssert(m_ciftiFile); const CiftiXML& ciftiXML = m_ciftiFile->getCiftiXML(); CaretAssert((m_dataMappingDirectionForCiftiXML == CiftiXML::ALONG_ROW) || (m_dataMappingDirectionForCiftiXML == CiftiXML::ALONG_COLUMN)); switch (ciftiXML.getMappingType(m_dataMappingDirectionForCiftiXML)) { case CiftiMappingType::BRAIN_MODELS: { const CiftiBrainModelsMap& map = ciftiXML.getBrainModelsMap(m_dataMappingDirectionForCiftiXML); if (map.getSurfaceNumberOfNodes(structure) == numberOfNodes) { const int64_t dataIndex = map.getIndexForNode(nodeIndex, structure); if (dataIndex >= 0) { for (std::vector::const_iterator mapIter = mapIndices.begin(); mapIter != mapIndices.end(); mapIter++) { const int32_t mapIndex = *mapIter; std::vector mapData; CaretAssertVectorIndex(m_mapContent, mapIndex); getMapData(mapIndex, mapData); if (dataIndex < static_cast(mapData.size())) { const float value = mapData[dataIndex]; numericalValuesOut.push_back(mapData[dataIndex]); numericalValuesOutValid.push_back(true); if (ciftiXML.getMappingType(m_dataReadingDirectionForCiftiXML) == CiftiMappingType::LABELS) { const GiftiLabelTable* glt = getMapLabelTable(mapIndex); const int32_t labelKey = static_cast(value); const GiftiLabel* gl = glt->getLabel(labelKey); if (gl != NULL) { textValueOut += (" " + gl->getName()); } else { textValueOut += (" InvalidLabelKey=" + AString::number(labelKey)); } } else { textValueOut += (" " + AString::number(value, 'f')); } } } } } } break; case CiftiMappingType::LABELS: CaretAssertMessage(0, "Mapping type should never be LABELS"); break; case CiftiMappingType::PARCELS: { int64_t parcelIndex = -1; const CiftiParcelsMap& map = ciftiXML.getParcelsMap(m_dataMappingDirectionForCiftiXML); if (map.getSurfaceNumberOfNodes(structure) == numberOfNodes) { const std::vector& parcels = map.getParcels(); parcelIndex = map.getIndexForNode(nodeIndex, structure); if ((parcelIndex >= 0) && (parcelIndex < static_cast(parcels.size()))) { textValueOut = parcels[parcelIndex].m_name; } } for (std::vector::const_iterator mapIter = mapIndices.begin(); mapIter != mapIndices.end(); mapIter++) { const int32_t mapIndex = *mapIter; if (parcelIndex >= 0) { int64_t itemIndex = -1; switch (ciftiXML.getMappingType(m_dataReadingDirectionForCiftiXML)) { case CiftiMappingType::BRAIN_MODELS: { const CiftiBrainModelsMap& map = ciftiXML.getBrainModelsMap(m_dataReadingDirectionForCiftiXML); if (map.getSurfaceNumberOfNodes(structure) == numberOfNodes) { itemIndex = map.getIndexForNode(nodeIndex, structure); } } break; case CiftiMappingType::LABELS: break; case CiftiMappingType::PARCELS: itemIndex = mapIndex; break; case CiftiMappingType::SCALARS: itemIndex = mapIndex; break; case CiftiMappingType::SERIES: itemIndex = mapIndex; break; } if (itemIndex >= 0) { const int64_t numRows = m_ciftiFile->getNumberOfRows(); const int64_t numCols = m_ciftiFile->getNumberOfColumns(); switch (m_dataReadingDirectionForCiftiXML) { case CiftiXML::ALONG_COLUMN: { std::vector data; data.resize(numRows); CaretAssert(parcelIndex < numCols); m_ciftiFile->getColumn(&data[0], parcelIndex); CaretAssertVectorIndex(data, itemIndex); textValueOut += (" " + AString::number(data[itemIndex])); } break; case CiftiXML::ALONG_ROW: { std::vector data; data.resize(numCols); CaretAssert(parcelIndex < numRows); m_ciftiFile->getRow(&data[0], parcelIndex); CaretAssertVectorIndex(data, itemIndex); textValueOut += (" " + AString::number(data[itemIndex])); } break; } } } } } break; case CiftiMappingType::SCALARS: CaretAssertMessage(0, "Mapping type should never be SCALARS"); break; case CiftiMappingType::SERIES: CaretAssertMessage(0, "Mapping type should never be SERIES"); break; } if (textValueOut.isEmpty()) { return false; } /* * Output text is valid */ return true; } /** * Get Parcel Label File value for a surface's node. When the data is mapped * to parcels, the numerical value will not be valid. * * @param mapIndex * Index of the map. * @param structure * Surface's structure. * @param nodeIndex * Index of the node * @param numberOfNodes * Number of nodes in the surface. * @param numericalValueOut * Numerical value out. * @param numericalValueOutValid * Output that indicates the numerical value output is valid. * For label data, this value will be the lable key. * @param textValueOut * Text containing node' value will always be valid if the method * returns true. For parcel data, this will contain the name of the * parcel. For label data, this will contain the name of the label. * For numerical data, this will contain the text representation * of the numerical value. * @return * True if the text value is valid. The numerical value may or may not * also be valid. */ bool CiftiMappableDataFile::getParcelLabelMapSurfaceNodeValue(const int32_t mapIndex, const StructureEnum::Enum structure, const int nodeIndex, const int32_t numberOfNodes, float& numericalValueOut, bool& numericalValueOutValid, AString& textValueOut) const { numericalValueOut = 0.0; numericalValueOutValid = false; textValueOut = ""; CaretAssertVectorIndex(m_mapContent, mapIndex); CaretAssert(m_ciftiFile); const CiftiXML& ciftiXML = m_ciftiFile->getCiftiXML(); AString areaName = ""; CaretAssert((m_dataMappingDirectionForCiftiXML == CiftiXML::ALONG_ROW) || (m_dataMappingDirectionForCiftiXML == CiftiXML::ALONG_COLUMN)); const CiftiParcelsMap& map = ciftiXML.getParcelsMap(m_dataMappingDirectionForCiftiXML); if (map.getSurfaceNumberOfNodes(structure) == numberOfNodes) { const std::vector& parcels = map.getParcels(); const int64_t parcelIndex = map.getIndexForNode(nodeIndex, structure); if ((parcelIndex >= 0) && (parcelIndex < static_cast(parcels.size()))) { areaName = parcels[parcelIndex].m_name; const AString networkName = getMapName(mapIndex); textValueOut = ("Area: " + areaName + ", Network: " + networkName); return true; } } return false; } /** * Get the identification information for a surface node in the given maps. * * @param mapIndices * Indices of maps for which identification information is requested. * @param structure * Structure of the surface. * @param nodeIndex * Index of the node. * @param numberOfNodes * Number of nodes in the surface. * @param textOut * Output containing identification information. */ bool CiftiMappableDataFile::getSurfaceNodeIdentificationForMaps(const std::vector& mapIndices, const StructureEnum::Enum structure, const int nodeIndex, const int32_t numberOfNodes, AString& textOut) const { CaretAssert(m_ciftiFile); if (mapIndices.empty()) { return false; } bool useMapData = false; bool useParcelLabelMapData = false; bool useSeriesData = false; switch (getDataFileType()) { case DataFileTypeEnum::ANNOTATION: CaretAssert(0); break; case DataFileTypeEnum::BORDER: CaretAssert(0); break; case DataFileTypeEnum::CONNECTIVITY_DENSE: useMapData = true; break; case DataFileTypeEnum::CONNECTIVITY_DENSE_DYNAMIC: useMapData = true; break; case DataFileTypeEnum::CONNECTIVITY_DENSE_LABEL: useSeriesData = true; break; case DataFileTypeEnum::CONNECTIVITY_DENSE_PARCEL: useMapData = true; break; case DataFileTypeEnum::CONNECTIVITY_DENSE_SCALAR: useSeriesData = true; break; case DataFileTypeEnum::CONNECTIVITY_DENSE_TIME_SERIES: useSeriesData = true; break; case DataFileTypeEnum::CONNECTIVITY_FIBER_ORIENTATIONS_TEMPORARY: CaretAssert(0); break; case DataFileTypeEnum::CONNECTIVITY_FIBER_TRAJECTORY_TEMPORARY: CaretAssert(0); break; case DataFileTypeEnum::CONNECTIVITY_PARCEL: useMapData = true; break; case DataFileTypeEnum::CONNECTIVITY_PARCEL_DENSE: useMapData = true; break; case DataFileTypeEnum::CONNECTIVITY_PARCEL_LABEL: useParcelLabelMapData = true; break; case DataFileTypeEnum::CONNECTIVITY_PARCEL_SCALAR: useMapData = true; break; case DataFileTypeEnum::CONNECTIVITY_PARCEL_SERIES: useMapData = true; break; case DataFileTypeEnum::CONNECTIVITY_SCALAR_DATA_SERIES: break; case DataFileTypeEnum::FOCI: CaretAssert(0); break; case DataFileTypeEnum::IMAGE: CaretAssert(0); break; case DataFileTypeEnum::LABEL: CaretAssert(0); break; case DataFileTypeEnum::METRIC: CaretAssert(0); break; case DataFileTypeEnum::PALETTE: CaretAssert(0); break; case DataFileTypeEnum::RGBA: CaretAssert(0); break; case DataFileTypeEnum::SCENE: CaretAssert(0); break; case DataFileTypeEnum::SPECIFICATION: CaretAssert(0); break; case DataFileTypeEnum::SURFACE: CaretAssert(0); break; case DataFileTypeEnum::UNKNOWN: CaretAssert(0); break; case DataFileTypeEnum::VOLUME: CaretAssert(0); break; } const int32_t numberOfMapIndices = static_cast(mapIndices.size()); textOut = ""; bool validID = false; if (useMapData) { std::vector numericalValues; std::vector numericalValuesValid; AString textValue; if (getMapSurfaceNodeValues(mapIndices, structure, nodeIndex, numberOfNodes, numericalValues, numericalValuesValid, textValue)) { textOut += textValue; textOut += " "; validID = true; } // for (int32_t i = 0; i < numberOfMapIndices; i++) { // const int32_t mapIndex = mapIndices[i]; // // float numericalValue; // AString textValue; // bool numericalValueValid; // if (getMapSurfaceNodeValue(mapIndex, // structure, // nodeIndex, // numberOfNodes, // numericalValue, // numericalValueValid, // textValue)) { // textOut += textValue; // textOut += " "; // validID = true; // } // } } else if (useSeriesData) { /* * Use series data which contains values for node from all maps. */ std::vector seriesData; if (getSeriesDataForSurfaceNode(structure, nodeIndex, seriesData)) { for (int32_t i = 0; i < numberOfMapIndices; i++) { const int32_t mapIndex = mapIndices[i]; CaretAssertVectorIndex(seriesData, mapIndex); const float value = seriesData[mapIndex]; if (isMappedWithLabelTable()) { const GiftiLabelTable* glt = getMapLabelTable(mapIndex); const int32_t labelKey = static_cast(value); const GiftiLabel* gl = glt->getLabel(labelKey); if (gl != NULL) { textOut += gl->getName(); } else { textOut += ("InvalidLabelKey=" + AString::number(value)); } validID = true; } else if (isMappedWithPalette()) { textOut += AString::number(value); validID = true; } else { CaretAssert(0); } textOut += " "; } } } else if (useParcelLabelMapData) { for (int32_t i = 0; i < numberOfMapIndices; i++) { const int32_t mapIndex = mapIndices[i]; float numericalValue; AString textValue; bool numericalValueValid; if (getParcelLabelMapSurfaceNodeValue(mapIndex, structure, nodeIndex, numberOfNodes, numericalValue, numericalValueValid, textValue)) { textOut += textValue; textOut += " "; validID = true; } } } return validID; } /** * Get the series data (one data value from each map) for a surface node. * * @param structure * Surface's structure. * @param nodeIndex * Index of the node. * @param seriesDataOut * Series data for given node. * @return * True if output data is valid, else false. */ bool CiftiMappableDataFile::getSeriesDataForSurfaceNode(const StructureEnum::Enum structure, const int32_t nodeIndex, std::vector& seriesDataOut) const { CaretAssert(m_ciftiFile); bool valid = false; switch (m_dataMappingAccessMethod) { case DATA_ACCESS_METHOD_INVALID: CaretAssert(0); break; case DATA_ACCESS_NONE: break; case DATA_ACCESS_FILE_COLUMNS_OR_XML_ALONG_ROW: seriesDataOut.resize(m_ciftiFile->getNumberOfRows()); valid = m_ciftiFile->getColumnFromNode(&seriesDataOut[0], nodeIndex, structure); break; case DATA_ACCESS_FILE_ROWS_OR_XML_ALONG_COLUMN: seriesDataOut.resize(m_ciftiFile->getNumberOfColumns()); valid = m_ciftiFile->getRowFromNode(&seriesDataOut[0], nodeIndex, structure); break; } return valid; } /** * Get the series data (oone data value for each map) for a voxel at the * given coordinate. * * @param xyz * Coordinate of the voxel. * @param seriesDataOut * Series data for the given voxel. * @return * True if output data is valid, else false. */ bool CiftiMappableDataFile::getSeriesDataForVoxelAtCoordinate(const float xyz[3], std::vector& seriesDataOut) const { CaretAssert(m_ciftiFile); bool valid = false; switch (m_dataMappingAccessMethod) { case DATA_ACCESS_METHOD_INVALID: CaretAssert(0); break; case DATA_ACCESS_NONE: break; case DATA_ACCESS_FILE_COLUMNS_OR_XML_ALONG_ROW: seriesDataOut.resize(m_ciftiFile->getNumberOfRows()); valid = m_ciftiFile->getColumnFromVoxelCoordinate(&seriesDataOut[0], xyz); break; case DATA_ACCESS_FILE_ROWS_OR_XML_ALONG_COLUMN: seriesDataOut.resize(m_ciftiFile->getNumberOfColumns()); valid = m_ciftiFile->getRowFromVoxelCoordinate(&seriesDataOut[0], xyz); break; } return valid; } /** * Get the node coloring for the surface. * @param surface * Surface whose nodes are colored. * @param surfaceRGBAOut * Filled with RGBA coloring for the surface's nodes. * Contains numberOfNodes * 4 elements. * @param dataValuesOut * Data values for the nodes (elements are valid when the alpha value in * the RGBA colors is valid (greater than zero). * @param surfaceNumberOfNodes * Number of nodes in the surface. * @return * True if coloring is valid, else false. */ bool CiftiMappableDataFile::getMapSurfaceNodeColoring(const PaletteFile* paletteFile, const int32_t mapIndex, const StructureEnum::Enum structure, float* surfaceRGBAOut, float* dataValuesOut, const int32_t surfaceNumberOfNodes) { CaretAssert(m_ciftiFile); CaretAssertVectorIndex(m_mapContent, mapIndex); const int32_t numCiftiNodes = getMappingSurfaceNumberOfNodes(structure); if (numCiftiNodes != surfaceNumberOfNodes) { return false; } std::vector mapData; getMapData(mapIndex, mapData); /* * Map data may be empty for connectivity matrix files with no rows loaded. */ if (mapData.empty()) { return false; } std::vector surfaceMap; switch (m_dataMappingAccessMethod) { case DATA_ACCESS_METHOD_INVALID: CaretAssert(0); break; case DATA_ACCESS_NONE: break; case DATA_ACCESS_FILE_COLUMNS_OR_XML_ALONG_ROW: m_ciftiFile->getSurfaceMapForRows(surfaceMap, structure); break; case DATA_ACCESS_FILE_ROWS_OR_XML_ALONG_COLUMN: m_ciftiFile->getSurfaceMapForColumns(surfaceMap, structure); break; } const MapContent* mc = m_mapContent[mapIndex]; /* * May need to update map coloring */ if ( ! mc->m_rgbaValid) { updateScalarColoringForMap(mapIndex, paletteFile); } std::vector dataIndicesForNodes; bool validColorsFlag = false; if (getSurfaceDataIndicesForMappingToBrainordinates(structure, surfaceNumberOfNodes, dataIndicesForNodes)) { for (int64_t iNode = 0; iNode < surfaceNumberOfNodes; iNode++) { CaretAssertVectorIndex(dataIndicesForNodes, iNode); const int64_t dataIndex = dataIndicesForNodes[iNode]; const int64_t node4 = iNode * 4; CaretAssertArrayIndex(surfaceRGBA, (surfaceNumberOfNodes * 4), node4); if (dataIndex >= 0) { CaretAssert(dataIndex < mc->m_dataCount); const int64_t data4 = dataIndex * 4; CaretAssertArrayIndex(this->dataRGBA, (mc->m_dataCount * 4), data4); surfaceRGBAOut[node4] = mc->m_rgba[data4] / 255.0; surfaceRGBAOut[node4+1] = mc->m_rgba[data4+1] / 255.0; surfaceRGBAOut[node4+2] = mc->m_rgba[data4+2] / 255.0; surfaceRGBAOut[node4+3] = mc->m_rgba[data4+3] / 255.0; dataValuesOut[iNode] = mapData[dataIndex]; validColorsFlag = true; } else { surfaceRGBAOut[node4] = 0.0; surfaceRGBAOut[node4+1] = 0.0; surfaceRGBAOut[node4+2] = 0.0; surfaceRGBAOut[node4+3] = -1.0; dataValuesOut[iNode] = 0.0; } } } return validColorsFlag; } /** * Get the data indices corresponding to all nodes in the given surface. * * @param structure * Surface's structure. * @param surfaceNumberOfNodes * Number of nodes in the surface. * @param dataIndicesForNodes * Will containg "surfaceNumberOfNodes" element where the values are * indices into the CIFTI data. * @return True if valid, else false. */ bool CiftiMappableDataFile::getSurfaceDataIndicesForMappingToBrainordinates(const StructureEnum::Enum structure, const int64_t surfaceNumberOfNodes, std::vector& dataIndicesForNodes) const { CaretAssert(m_ciftiFile); const CiftiXML& ciftiXML = m_ciftiFile->getCiftiXML(); CaretAssert((m_dataMappingDirectionForCiftiXML == CiftiXML::ALONG_ROW) || (m_dataMappingDirectionForCiftiXML == CiftiXML::ALONG_COLUMN)); switch (ciftiXML.getMappingType(m_dataMappingDirectionForCiftiXML)) { case CiftiMappingType::BRAIN_MODELS: { const CiftiBrainModelsMap& map = ciftiXML.getBrainModelsMap(m_dataMappingDirectionForCiftiXML); if (map.getSurfaceNumberOfNodes(structure) == surfaceNumberOfNodes) { const std::vector surfaceMap = map.getSurfaceMap(structure); dataIndicesForNodes.resize(surfaceNumberOfNodes); std::fill(dataIndicesForNodes.begin(), dataIndicesForNodes.end(), -1); for (std::vector::const_iterator iter = surfaceMap.begin(); iter != surfaceMap.end(); iter++) { const CiftiBrainModelsMap::SurfaceMap& nodeMap = *iter; CaretAssertVectorIndex(dataIndicesForNodes, nodeMap.m_surfaceNode); dataIndicesForNodes[nodeMap.m_surfaceNode] = nodeMap.m_ciftiIndex; } return true; } } break; case CiftiMappingType::LABELS: CaretAssertMessage(0, "Mapping type should never be LABELS"); break; case CiftiMappingType::PARCELS: { const CiftiParcelsMap& map = ciftiXML.getParcelsMap(m_dataMappingDirectionForCiftiXML); if (map.getSurfaceNumberOfNodes(structure) == surfaceNumberOfNodes) { dataIndicesForNodes.resize(surfaceNumberOfNodes); for (int64_t i = 0; i < surfaceNumberOfNodes; i++) { dataIndicesForNodes[i] = map.getIndexForNode(i, structure); } return true; } } break; case CiftiMappingType::SCALARS: CaretAssertMessage(0, "Mapping type should never be SCALARS"); break; case CiftiMappingType::SERIES: CaretAssertMessage(0, "Mapping type should never be SERIES"); break; } return false; } /** * Get connectivity value for a voxel. When the data is mapped * to parcels, the numerical value will not be valid. * * @param mapIndex * Index of the map. * @param xyz * Coordinate of voxel. * @param ijkOut * Voxel indices of value. * @param numericalValueOut * Numerical value out. * @param numericalValueOutValid * Output that indicates the numerical value output is valid. * For label data, this value will be the label key. * @param textValueOut * Text containing node' value will always be valid if the method * returns true. For parcel data, this will contain the name of the * parcel. For label data, this will contain the name of the label. * For numerical data, this will contain the text representation * of the numerical value. * @return * True if the text value is valid. The numerical value may or may not * also be valid. */ bool CiftiMappableDataFile::getMapVolumeVoxelValue(const int32_t mapIndex, const float xyz[3], int64_t ijkOut[3], float& numericalValueOut, bool& numericalValueOutValid, AString& textValueOut) const { textValueOut = ""; numericalValueOutValid = false; CaretAssert(m_ciftiFile); /* * Get content for map. */ CaretAssertVectorIndex(m_mapContent, mapIndex); int64_t ijk[3]; enclosingVoxel(xyz[0], xyz[1], xyz[2], ijk[0], ijk[1], ijk[2]); if (indexValid(ijk[0], ijk[1], ijk[2])) { /* * Only set the IJK if the index is valid. */ ijkOut[0] = ijk[0]; ijkOut[1] = ijk[1]; ijkOut[2] = ijk[2]; const int64_t dataOffset = m_voxelIndicesToOffset->getOffsetForIndices(ijk[0], ijk[1], ijk[2]); if (dataOffset >= 0) { CaretAssert((m_dataMappingDirectionForCiftiXML == CiftiXML::ALONG_ROW) || (m_dataMappingDirectionForCiftiXML == CiftiXML::ALONG_COLUMN)); const CiftiXML& ciftiXML = m_ciftiFile->getCiftiXML(); const CiftiParcelLabelFile* parcelLabelFile = dynamic_cast(this); if (parcelLabelFile != NULL) { const CiftiParcelsMap& map = ciftiXML.getParcelsMap(m_dataMappingDirectionForCiftiXML); const int64_t parcelMapIndex = map.getIndexForVoxel(ijk); const std::vector& parcels = map.getParcels(); AString areaName = ""; if ((parcelMapIndex >= 0) && (parcelMapIndex < static_cast(parcels.size()))) { CaretAssertVectorIndex(parcels, parcelMapIndex); areaName = parcels[parcelMapIndex].m_name; } const AString networkName = getMapName(mapIndex); textValueOut = ("Area: " + areaName + ", Network: " + networkName); return true; } else { switch (ciftiXML.getMappingType(m_dataMappingDirectionForCiftiXML)) { case CiftiMappingType::BRAIN_MODELS: { /* * Note: For a Dense connectivity file, it may not have * data loaded since data is loaded upon demand. */ std::vector mapData; getMapData(mapIndex, mapData); if ( ! mapData.empty()) { CaretAssertVectorIndex(mapData, dataOffset); numericalValueOut = mapData[dataOffset]; if (isMappedWithLabelTable()) { textValueOut = "Invalid Label Index"; const GiftiLabelTable* glt = getMapLabelTable(mapIndex); const int32_t labelKey = static_cast(numericalValueOut); const GiftiLabel* gl = glt->getLabel(labelKey); if (gl != NULL) { textValueOut = gl->getName(); } else { textValueOut += ("InvalidLabelKey=" + AString::number(labelKey)); } numericalValueOutValid = true; } else if (isMappedWithPalette()) { numericalValueOutValid = true; textValueOut = AString::number(numericalValueOut); } else { CaretAssert(0); } return true; } } break; case CiftiMappingType::LABELS: CaretAssertMessage(0, "Mapping type should never be LABELS"); break; case CiftiMappingType::PARCELS: { const CiftiParcelsMap& map = ciftiXML.getParcelsMap(m_dataMappingDirectionForCiftiXML); const int64_t parcelMapIndex = map.getIndexForVoxel(ijk); const std::vector& parcels = map.getParcels(); if ((parcelMapIndex >= 0) && (parcelMapIndex < static_cast(parcels.size()))) { 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) { int64_t itemIndex = -1; switch (ciftiXML.getMappingType(m_dataReadingDirectionForCiftiXML)) { case CiftiMappingType::BRAIN_MODELS: { const CiftiBrainModelsMap& map = ciftiXML.getBrainModelsMap(m_dataReadingDirectionForCiftiXML); itemIndex = map.getIndexForVoxel(ijk); } break; case CiftiMappingType::LABELS: break; case CiftiMappingType::PARCELS: break; case CiftiMappingType::SCALARS: itemIndex = mapIndex; break; case CiftiMappingType::SERIES: itemIndex = mapIndex; break; } if (itemIndex >= 0) { const int64_t numRows = m_ciftiFile->getNumberOfRows(); const int64_t numCols = m_ciftiFile->getNumberOfColumns(); switch (m_dataReadingDirectionForCiftiXML) { case CiftiXML::ALONG_COLUMN: { std::vector data; data.resize(numRows); CaretAssert(parcelMapIndex < numCols); m_ciftiFile->getColumn(&data[0], parcelMapIndex); CaretAssertVectorIndex(data, itemIndex); textValueOut += (" " + AString::number(data[itemIndex])); } break; case CiftiXML::ALONG_ROW: { std::vector data; data.resize(numCols); CaretAssert(parcelMapIndex < numRows); m_ciftiFile->getRow(&data[0], parcelMapIndex); CaretAssertVectorIndex(data, itemIndex); textValueOut += (" " + AString::number(data[itemIndex])); } break; } } } } return true; break; case CiftiMappingType::SCALARS: CaretAssertMessage(0, "Mapping type should never be SCALARS"); break; case CiftiMappingType::SERIES: CaretAssertMessage(0, "Mapping type should never be SERIES"); break; } } } } return false; } /** * Get connectivity value for a voxel. When the data is mapped * to parcels, the numerical value will not be valid. * * @param mapIndices * Indices of the maps. * @param xyz * Coordinate of voxel. * @param ijkOut * Voxel indices of value. * @param numericalValuesOut * Numerical values out. * @param numericalValuesOutValid * Output that indicates the numerical values output is valid. * For label data, this value will be the label key. * @param textValueOut * Text containing node' value will always be valid if the method * returns true. For parcel data, this will contain the name of the * parcel. For label data, this will contain the name of the label. * For numerical data, this will contain the text representation * of the numerical value. * @return * True if the text value is valid. The numerical value may or may not * also be valid. */ bool CiftiMappableDataFile::getMapVolumeVoxelValues(const std::vector mapIndices, const float xyz[3], int64_t ijkOut[3], std::vector& numericalValuesOut, std::vector& numericalValuesOutValid, AString& textValueOut) const { textValueOut = ""; numericalValuesOut.clear(); numericalValuesOutValid.clear(); if (mapIndices.empty()) { return false; } CaretAssert(m_ciftiFile); int64_t ijk[3]; enclosingVoxel(xyz[0], xyz[1], xyz[2], ijk[0], ijk[1], ijk[2]); if (indexValid(ijk[0], ijk[1], ijk[2])) { /* * Only set the IJK if the index is valid. */ ijkOut[0] = ijk[0]; ijkOut[1] = ijk[1]; ijkOut[2] = ijk[2]; const int64_t dataOffset = m_voxelIndicesToOffset->getOffsetForIndices(ijk[0], ijk[1], ijk[2]); if (dataOffset >= 0) { CaretAssert((m_dataMappingDirectionForCiftiXML == CiftiXML::ALONG_ROW) || (m_dataMappingDirectionForCiftiXML == CiftiXML::ALONG_COLUMN)); const CiftiXML& ciftiXML = m_ciftiFile->getCiftiXML(); const CiftiParcelLabelFile* parcelLabelFile = dynamic_cast(this); if (parcelLabelFile != NULL) { const CiftiParcelsMap& map = ciftiXML.getParcelsMap(m_dataMappingDirectionForCiftiXML); const int64_t parcelMapIndex = map.getIndexForVoxel(ijk); const std::vector& parcels = map.getParcels(); AString areaName = ""; if ((parcelMapIndex >= 0) && (parcelMapIndex < static_cast(parcels.size()))) { CaretAssertVectorIndex(parcels, parcelMapIndex); areaName = parcels[parcelMapIndex].m_name; } textValueOut = ("Area: " + areaName + ", Network(s): "); for (std::vector::const_iterator mapIter = mapIndices.begin(); mapIter != mapIndices.end(); mapIter++) { textValueOut += (getMapName(*mapIter) + " "); } // const AString networkName = getMapName(mapIndex); // // textValueOut = ("Area: " // + areaName // + ", Network: " // + networkName); return true; } else { switch (ciftiXML.getMappingType(m_dataMappingDirectionForCiftiXML)) { case CiftiMappingType::BRAIN_MODELS: { for (std::vector::const_iterator mapIter = mapIndices.begin(); mapIter != mapIndices.end(); mapIter++) { const int32_t mapIndex = *mapIter; /* * Note: For a Dense connectivity file, it may not have * data loaded since data is loaded upon demand. */ std::vector mapData; getMapData(mapIndex, mapData); if ( ! mapData.empty()) { CaretAssertVectorIndex(mapData, dataOffset); const float value = mapData[dataOffset]; if (isMappedWithLabelTable()) { textValueOut = "Invalid Label Index"; const GiftiLabelTable* glt = getMapLabelTable(mapIndex); const int32_t labelKey = static_cast(value); const GiftiLabel* gl = glt->getLabel(labelKey); if (gl != NULL) { textValueOut = (gl->getName()); } else { textValueOut += ("InvalidLabelKey=" + AString::number(labelKey) + " "); } numericalValuesOut.push_back(value); numericalValuesOutValid.push_back(false); // NOT VALID ! } else if (isMappedWithPalette()) { numericalValuesOut.push_back(value); numericalValuesOutValid.push_back(true); textValueOut = (AString::number(value) + " "); } else { CaretAssert(0); } return true; } } } break; case CiftiMappingType::LABELS: CaretAssertMessage(0, "Mapping type should never be LABELS"); break; case CiftiMappingType::PARCELS: { const CiftiParcelsMap& map = ciftiXML.getParcelsMap(m_dataMappingDirectionForCiftiXML); const int64_t parcelMapIndex = map.getIndexForVoxel(ijk); const std::vector& parcels = map.getParcels(); if ((parcelMapIndex >= 0) && (parcelMapIndex < static_cast(parcels.size()))) { 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(); mapIter != mapIndices.end(); mapIter++) { const int32_t mapIndex = *mapIter; if (parcelMapIndex >= 0) { int64_t itemIndex = -1; switch (ciftiXML.getMappingType(m_dataReadingDirectionForCiftiXML)) { case CiftiMappingType::BRAIN_MODELS: { const CiftiBrainModelsMap& map = ciftiXML.getBrainModelsMap(m_dataReadingDirectionForCiftiXML); itemIndex = map.getIndexForVoxel(ijk); } break; case CiftiMappingType::LABELS: break; case CiftiMappingType::PARCELS: break; case CiftiMappingType::SCALARS: itemIndex = mapIndex; break; case CiftiMappingType::SERIES: itemIndex = mapIndex; break; } if (itemIndex >= 0) { const int64_t numRows = m_ciftiFile->getNumberOfRows(); const int64_t numCols = m_ciftiFile->getNumberOfColumns(); switch (m_dataReadingDirectionForCiftiXML) { case CiftiXML::ALONG_COLUMN: { std::vector data; data.resize(numRows); CaretAssert(parcelMapIndex < numCols); m_ciftiFile->getColumn(&data[0], parcelMapIndex); CaretAssertVectorIndex(data, itemIndex); textValueOut += (" " + AString::number(data[itemIndex])); } break; case CiftiXML::ALONG_ROW: { std::vector data; data.resize(numCols); CaretAssert(parcelMapIndex < numRows); m_ciftiFile->getRow(&data[0], parcelMapIndex); CaretAssertVectorIndex(data, itemIndex); textValueOut += (" " + AString::number(data[itemIndex])); } break; } } } } } return true; break; case CiftiMappingType::SCALARS: CaretAssertMessage(0, "Mapping type should never be SCALARS"); break; case CiftiMappingType::SERIES: CaretAssertMessage(0, "Mapping type should never be SERIES"); break; } } } } return false; } /** * Get the value of the voxel containing the given coordinate. * * @param coordinateIn * The 3D coordinate * @param validOut * If not NULL, will indicate if the coordinate (and hence the * returned value) is valid. * @param mapIndex * Index of map. * @param component * Voxel component. * @return * Value of voxel containing the given coordinate. */ float CiftiMappableDataFile::getVoxelValue(const float* coordinateIn, bool* validOut, const int64_t mapIndex, const int64_t component) const { return getVoxelValue(coordinateIn[0], coordinateIn[1], coordinateIn[2], validOut, mapIndex, component); } /** * Get the value of the voxel containing the given coordinate. * * @param coordinateX * The X coordinate * @param coordinateY * The Y coordinate * @param coordinateZ * The Z coordinate * @param validOut * If not NULL, will indicate if the coordinate (and hence the * returned value) is valid. * @param mapIndex * Index of map. * @param component * Voxel component. * @return * Value of voxel containing the given coordinate. */ float CiftiMappableDataFile::getVoxelValue(const float coordinateX, const float coordinateY, const float coordinateZ, bool* validOut, const int64_t mapIndex, const int64_t component) const { if (validOut != NULL) { *validOut = false; } CaretAssert(m_ciftiFile); /* * Get content for map. */ CaretAssertVectorIndex(m_mapContent, mapIndex); int64_t voxelI, voxelJ, voxelK; enclosingVoxel(coordinateX, coordinateY, coordinateZ, voxelI, voxelJ, voxelK); if (indexValid(voxelI, voxelJ, voxelK, mapIndex, component)) { const int64_t dataOffset = m_voxelIndicesToOffset->getOffsetForIndices(voxelI, voxelJ, voxelK); if (dataOffset >= 0) { std::vector mapData; getMapData(mapIndex, mapData); CaretAssertVectorIndex(mapData, dataOffset); const float value = mapData[dataOffset]; if (validOut != NULL) { *validOut = true; } return value; } } return 0.0; } /** * @param coordinate * Coordinate for which enclosing voxel is located. * @param mapIndex * Index of the map. * @return * Offset in map data for enclosing voxel or NULL if not within a voxel. */ int64_t CiftiMappableDataFile::getMapDataOffsetForVoxelAtCoordinate(const float coordinate[3], const int32_t mapIndex) const { int64_t dataOffset = -1; CaretAssert(m_ciftiFile); /* * Get content for map. */ CaretAssertVectorIndex(m_mapContent, mapIndex); int64_t voxelI, voxelJ, voxelK; enclosingVoxel(coordinate[0], coordinate[1], coordinate[2], voxelI, voxelJ, voxelK); if (indexValid(voxelI, voxelJ, voxelK, mapIndex, 0)) { dataOffset = m_voxelIndicesToOffset->getOffsetForIndices(voxelI, voxelJ, voxelK); } return dataOffset; } /** * Get the identification information for a surface node in the given maps. * * @param mapIndices * Indices of maps for which identification information is requested. * @param xyz * Coordinate of voxel. * @param ijkOut * Voxel indices of value. * @param textOut * Output containing identification information. */ bool CiftiMappableDataFile::getVolumeVoxelIdentificationForMaps(const std::vector& mapIndices, const float xyz[3], int64_t ijkOut[3], AString& textOut) const { CaretAssert(m_ciftiFile); const int32_t numberOfMapIndices = static_cast(mapIndices.size()); if (numberOfMapIndices <= 0) { return false; } textOut = ""; std::vector numericalValues; std::vector numericalValuesValid; AString textValue; if (getMapVolumeVoxelValues(mapIndices, xyz, ijkOut, numericalValues, numericalValuesValid, textValue)) { textOut = textValue; } // for (int32_t i = 0; i < numberOfMapIndices; i++) { // const int32_t mapIndex = mapIndices[i]; // // float numericalValue; // AString textValue; // bool numericalValueValid; // if (getMapVolumeVoxelValue(mapIndex, // xyz, // ijkOut, // numericalValue, // numericalValueValid, // textValue)) { // textOut += textValue; // textOut += " "; // } // } if (textOut.isEmpty() == false) { return true; } return false; } /** * Set the status to unmodified. */ void CiftiMappableDataFile::clearModified() { CaretMappableDataFile::clearModified(); CaretAssert(m_ciftiFile); const CiftiXML& ciftiXML = m_ciftiFile->getCiftiXML(); ciftiXML.getFileMetaData()->clearModified(); const int32_t numMaps = getNumberOfMaps(); for (int32_t i = 0; i < numMaps; i++) { m_mapContent[i]->clearModified(); } } /** * @return True if the file is modified in any way EXCEPT for * the palette color mapping. Also see isModified(). */ bool CiftiMappableDataFile::isModifiedExcludingPaletteColorMapping() const { if (CaretMappableDataFile::isModifiedExcludingPaletteColorMapping()) { return true; } CaretAssert(m_ciftiFile); const CiftiXML& ciftiXML = m_ciftiFile->getCiftiXML(); if (ciftiXML.getFileMetaData()->isModified()) { return true; } const int32_t numMaps = getNumberOfMaps(); for (int32_t i = 0; i < numMaps; i++) { if (m_mapContent[i]->isModified()) { return true; } } return false; } /** * @return The units for the 'interval' between two consecutive maps. */ NiftiTimeUnitsEnum::Enum CiftiMappableDataFile::getMapIntervalUnits() const { return m_mappingTimeUnits; } /** * Get the units value for the first map and the * quantity of units between consecutive maps. If the * units for the maps is unknown, value of one (1) are * returned for both output values. * * @param firstMapUnitsValueOut * Output containing units value for first map. * @param mapIntervalStepValueOut * Output containing number of units between consecutive maps. */ void CiftiMappableDataFile::getMapIntervalStartAndStep(float& firstMapUnitsValueOut, float& mapIntervalStepValueOut) const { firstMapUnitsValueOut = m_mappingTimeStart; mapIntervalStepValueOut = m_mappingTimeStep; } /** * Get the minimum and maximum values from ALL maps in this file. * Note that not all files (due to size of file) are able to provide * the minimum and maximum values from the file. The return value * indicates success/failure. If the failure (false) is returned * the returned values are likely +/- the maximum float values. * * @param dataRangeMinimumOut * Minimum data value found. * @param dataRangeMaximumOut * Maximum data value found. * @return * True if the values are valid, else false. */ bool CiftiMappableDataFile::getDataRangeFromAllMaps(float& dataRangeMinimumOut, float& dataRangeMaximumOut) const { CaretAssert(m_ciftiFile); /* * Dense is very large but at this time is [-1, 1] */ if (getDataFileType() == DataFileTypeEnum::CONNECTIVITY_DENSE) { dataRangeMaximumOut = 1.0; dataRangeMinimumOut = -1; return true; } else { if (m_ciftiFile->getDataRangeFromAllMaps(dataRangeMinimumOut, dataRangeMaximumOut)) { return true; } } /* * Default */ dataRangeMaximumOut = std::numeric_limits::max(); dataRangeMinimumOut = -dataRangeMaximumOut; return false; } /** * Get the number of nodes for the structure for mapping data. * * @param structure * Structure for which number of nodes is requested. * @return * Number of nodes corresponding to structure. If no matching structure * is found, a negative value is returned. */ int32_t CiftiMappableDataFile::getMappingSurfaceNumberOfNodes(const StructureEnum::Enum structure) const { int32_t numCiftiNodes = -1; if (m_dataMappingAccessMethod == DATA_ACCESS_NONE) { return numCiftiNodes; } CaretAssert((m_dataMappingDirectionForCiftiXML == CiftiXML::ALONG_ROW) || (m_dataMappingDirectionForCiftiXML == CiftiXML::ALONG_COLUMN)); const CiftiXML& ciftiXML = m_ciftiFile->getCiftiXML(); switch (ciftiXML.getMappingType(m_dataMappingDirectionForCiftiXML)) { case CiftiMappingType::BRAIN_MODELS: { const CiftiBrainModelsMap& map = ciftiXML.getBrainModelsMap(m_dataMappingDirectionForCiftiXML); if (map.hasSurfaceData(structure)) { numCiftiNodes = map.getSurfaceNumberOfNodes(structure); } } break; case CiftiMappingType::LABELS: CaretAssertMessage(0, "Mapping type should never be LABELS"); break; case CiftiMappingType::PARCELS: { const CiftiParcelsMap& map = ciftiXML.getParcelsMap(m_dataMappingDirectionForCiftiXML); if (map.hasSurfaceData(structure)) { numCiftiNodes = map.getSurfaceNumberOfNodes(structure); } } break; case CiftiMappingType::SCALARS: CaretAssertMessage(0, "Mapping type should never be SCALARS"); break; case CiftiMappingType::SERIES: CaretAssertMessage(0, "Mapping type should never be SERIES"); break; } return numCiftiNodes; } /** * Save file data from the scene. For subclasses that need to * save to a scene, this method should be overriden. sceneClass * will be valid and any scene data should be added to it. * * @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. */ void CiftiMappableDataFile::saveFileDataToScene(const SceneAttributes* sceneAttributes, SceneClass* sceneClass) { CaretMappableDataFile::saveFileDataToScene(sceneAttributes, sceneClass); if (isMappedWithLabelTable()) { sceneClass->addClass(m_classNameHierarchy->saveToScene(sceneAttributes, "m_classNameHierarchy")); } } /** * Restore file data from the scene. For subclasses that need to * restore from a scene, this method should be overridden. The scene class * will be valid and any scene data may be obtained from it. * * @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 CiftiMappableDataFile::restoreFileDataFromScene(const SceneAttributes* sceneAttributes, const SceneClass* sceneClass) { CaretMappableDataFile::restoreFileDataFromScene(sceneAttributes, sceneClass); if (isMappedWithLabelTable()) { m_classNameHierarchy->restoreFromScene(sceneAttributes, sceneClass->getClass("m_classNameHierarchy")); } /* * When a scene is created and there is a modified palette, the user may choose * to save the modified palette to the scene so that the file does not need to * be saved with a changed palette. When scenes are loaded and a file in the * scene is already in memory, the file is NOT reloaded to save time. However, * since the palette may be saved to the scene, the coloring will needd to be * updated. If this is not done, the coloring of the file's data prior to * loading the scene remains and may be incorrect. */ invalidateColoringInAllMaps(); } /** * Add information about the file to the data file information. * * @param dataFileInformation * Consolidates information about a data file. */ void CiftiMappableDataFile::addToDataFileContentInformation(DataFileContentInformation& dataFileInformation) { CaretAssert(m_ciftiFile); CaretMappableDataFile::addToDataFileContentInformation(dataFileInformation); dataFileInformation.addNameAndValue("Number of Rows", m_ciftiFile->getNumberOfRows()); dataFileInformation.addNameAndValue("Number of Columns", m_ciftiFile->getNumberOfColumns()); int64_t dimI, dimJ, dimK, dimTime, dimNumComp; getDimensions(dimI, dimJ, dimK, dimTime, dimNumComp); dataFileInformation.addNameAndValue("Volume Dim[0]", dimI); dataFileInformation.addNameAndValue("Volume Dim[1]", dimJ); dataFileInformation.addNameAndValue("Volume Dim[2]", dimK); AString paletteType; switch (m_paletteColorMappingSource) { case PALETTE_COLOR_MAPPING_SOURCE_INVALID: paletteType = "None"; break; case PALETTE_COLOR_MAPPING_SOURCE_FROM_FILE: paletteType = "File (One for all maps)"; break; case PALETTE_COLOR_MAPPING_SOURCE_FROM_MAP: paletteType = "Map (Unique for each map)"; break; } dataFileInformation.addNameAndValue("Palette Type", paletteType); const CiftiXML& ciftiXML = m_ciftiFile->getCiftiXML(); CiftiMappableDataFile::addCiftiXmlToDataFileContentInformation(dataFileInformation, ciftiXML); } /** * Get a text name for a CIFTI mapping type. * * @param mappingType * The CIFTI mapping type. * @return * String containing text name. */ AString CiftiMappableDataFile::mappingTypeToName(const CiftiMappingType::MappingType mappingType) { AString mapTypeName; switch (mappingType) { case CiftiMappingType::BRAIN_MODELS: mapTypeName = "BRAIN_MODELS"; break; case CiftiMappingType::LABELS: mapTypeName = "LABELS"; break; case CiftiMappingType::PARCELS: mapTypeName = "PARCELS"; break; case CiftiMappingType::SCALARS: mapTypeName = "SCALARS"; break; case CiftiMappingType::SERIES: mapTypeName = "SERIES"; break; } return mapTypeName; } /** * Add information about the file to the data file information. * * @param dataFileInformation * Consolidates information about a data file. * @param ciftiXML * The CIFTI XML. * @param numberOfCiftiDimensions * Number of Dimensions of the CIFTI File. */ void CiftiMappableDataFile::addCiftiXmlToDataFileContentInformation(DataFileContentInformation& dataFileInformation, const CiftiXML& ciftiXML) { std::vector dims = ciftiXML.getDimensions(); const int32_t numDims = static_cast(dims.size()); for (int32_t i = 0; i < numDims; i++) { dataFileInformation.addNameAndValue(("CIFTI Dim[" + AString::number(i) + "]"), dims[i]); } const bool showLabelMappingsFlag = dataFileInformation.isOptionFlag(DataFileContentInformation::OPTION_SHOW_CIFTI_LABEL_MAPPING); for (int32_t alongType = 0; alongType < numDims; alongType++) { AString alongName; CiftiMappingType::MappingType mapType = CiftiMappingType::BRAIN_MODELS; switch (alongType) { case CiftiXML::ALONG_ROW: alongName = "ALONG_ROW"; mapType = ciftiXML.getMappingType(CiftiXML::ALONG_ROW); break; case CiftiXML::ALONG_COLUMN: alongName = "ALONG_COLUMN"; mapType = ciftiXML.getMappingType(CiftiXML::ALONG_COLUMN); break; case CiftiXML::ALONG_STACK: alongName = "ALONG_STACK"; mapType = ciftiXML.getMappingType(CiftiXML::ALONG_STACK); break; } AString mapInfoString; if ( ! alongName.isEmpty()) { AString mapTypeName; switch (mapType) { case CiftiMappingType::BRAIN_MODELS: mapTypeName = "BRAIN_MODELS"; break; case CiftiMappingType::LABELS: mapTypeName = "LABELS"; break; case CiftiMappingType::PARCELS: mapTypeName = "PARCELS"; break; case CiftiMappingType::SCALARS: mapTypeName = "SCALARS"; break; case CiftiMappingType::SERIES: mapTypeName = "SERIES"; break; } dataFileInformation.addNameAndValue((alongName + " map type"), mapTypeName); switch (mapType) { case CiftiMappingType::BRAIN_MODELS: { const CiftiBrainModelsMap& bmm = ciftiXML.getBrainModelsMap(alongType); dataFileInformation.addNameAndValue(" Has Volume Data", bmm.hasVolumeData()); if (bmm.hasVolumeData()) { const VolumeSpace volumeSpace = bmm.getVolumeSpace(); const int64_t* dims = volumeSpace.getDims(); dataFileInformation.addNameAndValue(" Volume Dims", AString::fromNumbers(dims, 3, ",")); const std::vector >& sform = volumeSpace.getSform(); dataFileInformation.addNameAndValue(" Volume Space", AString::fromNumbers(sform[0].data(), 4, ",") + ";" + AString::fromNumbers(sform[1].data(), 4, ",") + ";" + AString::fromNumbers(sform[2].data(), 4, ",")); } std::vector modelInfo = bmm.getModelInfo();//allows us to visit the models in the order they are in the file for (int i = 0; i < (int)modelInfo.size(); ++i) { if (modelInfo[i].m_type == CiftiBrainModelsMap::SURFACE) { dataFileInformation.addNameAndValue((" " + StructureEnum::toGuiName(modelInfo[i].m_structure)), (AString::number(modelInfo[i].m_indexCount) + " out of " + AString::number(bmm.getSurfaceNumberOfNodes(modelInfo[i].m_structure)) + " vertices")); } else { CaretAssert(modelInfo[i].m_type == CiftiBrainModelsMap::VOXELS); dataFileInformation.addNameAndValue((" " + StructureEnum::toGuiName(modelInfo[i].m_structure)), (AString::number(modelInfo[i].m_indexCount) + " voxels")); } } } break; case CiftiMappingType::LABELS: if (showLabelMappingsFlag) { const CiftiLabelsMap& clm = ciftiXML.getLabelsMap(alongType); const int32_t numItems = clm.getLength(); for (int32_t i = 0; i < numItems; i++) { dataFileInformation.addText(" Index=" + AString::number(i) + " Name=" + clm.getMapName(i) + "\n" + clm.getMapLabelTable(i)->toFormattedString(" ") + "\n"); } } break; case CiftiMappingType::PARCELS: { const CiftiParcelsMap& cpm = ciftiXML.getParcelsMap(alongType); dataFileInformation.addNameAndValue(" Has Volume Data", cpm.hasVolumeData()); if (cpm.hasVolumeData()) { const VolumeSpace volumeSpace = cpm.getVolumeSpace(); const int64_t* dims = volumeSpace.getDims(); dataFileInformation.addNameAndValue(" Volume Dims", AString::fromNumbers(dims, 3, ",")); } const std::vector surfaceStructures = cpm.getParcelSurfaceStructures(); for (std::vector::const_iterator surfaceIter = surfaceStructures.begin(); surfaceIter != surfaceStructures.end(); surfaceIter++) { const StructureEnum::Enum structure = *surfaceIter; dataFileInformation.addNameAndValue((" " + StructureEnum::toGuiName(structure)), (AString::number(cpm.getSurfaceNumberOfNodes(structure)) + " vertices")); } const std::vector& parcels = cpm.getParcels(); for (std::vector::const_iterator parcelIter = parcels.begin(); parcelIter != parcels.end(); parcelIter++) { const CiftiParcelsMap::Parcel parcel = *parcelIter; dataFileInformation.addNameAndValue(" Parcel " + AString::number(parcelIter - parcels.begin() + 1), parcel.m_name); for (std::map >::const_iterator surfIter = parcel.m_surfaceNodes.begin(); surfIter != parcel.m_surfaceNodes.end(); surfIter++) { const StructureEnum::Enum structure = surfIter->first; const std::set& nodeIndices = surfIter->second; dataFileInformation.addNameAndValue(" " + StructureEnum::toGuiName(structure), AString::number(nodeIndices.size()) + " vertices"); } if (parcel.m_voxelIndices.size() != 0) { dataFileInformation.addNameAndValue(" ", AString::number(parcel.m_voxelIndices.size()) + " voxels"); } } } break; case CiftiMappingType::SCALARS: break; case CiftiMappingType::SERIES: { const CiftiSeriesMap& csm = ciftiXML.getSeriesMap(alongType); dataFileInformation.addNameAndValue(" Start", csm.getStart()); dataFileInformation.addNameAndValue(" Step", csm.getStep()); QString unitsName = "Unknown"; switch (csm.getUnit()) { case CiftiSeriesMap::HERTZ: unitsName = "Hertz"; break; case CiftiSeriesMap::METER: unitsName = "Meters"; break; case CiftiSeriesMap::RADIAN: unitsName = "Radians"; break; case CiftiSeriesMap::SECOND: unitsName = "Seconds"; break; } dataFileInformation.addNameAndValue(" Units", unitsName); } break; } } } } /** * Get information about the content of a generic CIFTI file that is * not a Workbench supported CIFTI file type. * * @param filename * Name of the file. * @param dataFileInformation * Consolidates information about a data file. */ void CiftiMappableDataFile::getDataFileContentInformationForGenericCiftiFile(const AString& filename, DataFileContentInformation& dataFileInformation) { CiftiFile ciftiFile(filename); const CiftiXML& ciftiXML = ciftiFile.getCiftiXML(); std::vector dims = ciftiXML.getDimensions(); const int32_t numDims = static_cast(dims.size()); int64_t dataSizeInBytes = 1; for (int32_t i = 0; i < numDims; i++) { dataSizeInBytes *= dims[i]; } dataSizeInBytes *= sizeof(float); dataFileInformation.addNameAndValue("Name", filename); dataFileInformation.addNameAndValue("Type", AString("Connectivity Unknown (Could be Unsupported CIFTI File)")); dataFileInformation.addNameAndValue("Data Size", FileInformation::fileSizeToStandardUnits(dataSizeInBytes)); dataFileInformation.setOptionFlag(DataFileContentInformation::OPTION_SHOW_CIFTI_LABEL_MAPPING, true); CiftiMappableDataFile::addCiftiXmlToDataFileContentInformation(dataFileInformation, ciftiXML); } /** * Help load charting data for the surface with the given structure and node average. * * @param structure * The surface's structure. * @param nodeIndices * Indices of nodes for averaging. * @return * Pointer to the chart data. If the data FAILED to load, * the returned pointer will return true. Caller takes ownership * of the pointer and must delete it when no longer needed. */ ChartDataCartesian* CiftiMappableDataFile::helpLoadChartDataForSurfaceNodeAverage(const StructureEnum::Enum structure, const std::vector& nodeIndices) { ChartDataCartesian* chartData = NULL; try { const int32_t numberOfNodes = static_cast(nodeIndices.size()); if (numberOfNodes > 0) { std::vector dataSum; int32_t dataSumSize = 0; int32_t dataAverageCount = 0; std::vector data; bool firstNodeFlag = true; for (int32_t i = 0; i < numberOfNodes; i++) { if (getSeriesDataForSurfaceNode(structure, nodeIndices[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)) { std::vector dataAverage(dataSumSize); for (int32_t k = 0; k < dataSumSize; k++) { dataAverage[k] = dataSum[k] / dataAverageCount; } chartData = helpCreateCartesianChartData(dataAverage); } } } catch (const DataFileException& dfe) { if (chartData != NULL) { delete chartData; chartData = NULL; } throw dfe; } return chartData; } /** * Help load charting data for the voxel at the given coordinate. * * @param xyz * The voxel coordinate. * @return * Pointer to the chart data. If the data FAILED to load, * the returned pointer will return true. Caller takes ownership * of the pointer and must delete it when no longer needed. */ ChartDataCartesian* CiftiMappableDataFile::helpLoadChartDataForVoxelAtCoordinate(const float xyz[3]) { ChartDataCartesian* chartData = NULL; try { std::vector data; if (getSeriesDataForVoxelAtCoordinate(xyz, data)) { chartData = helpCreateCartesianChartData(data); } } catch (const DataFileException& dfe) { if (chartData != NULL) { delete chartData; chartData = NULL; } throw dfe; } return chartData; } /** * Help load charting data for the surface with the given structure and node index. * * @param structure * The surface's structure. * @param nodeIndex * Index of the node. * @return * Pointer to the chart data. If the data FAILED to load, * the returned pointer will return true. Caller takes ownership * of the pointer and must delete it when no longer needed. */ ChartDataCartesian* CiftiMappableDataFile::helpLoadChartDataForSurfaceNode(const StructureEnum::Enum structure, const int32_t nodeIndex) { ChartDataCartesian* chartData = NULL; try { std::vector data; if (getSeriesDataForSurfaceNode(structure, nodeIndex, data)) { chartData = helpCreateCartesianChartData(data); } } catch (const DataFileException& dfe) { if (chartData != NULL) { delete chartData; chartData = NULL; } throw dfe; } return chartData; } bool CiftiMappableDataFile::getMapDataForSurface(const int32_t mapIndex, const StructureEnum::Enum structure, std::vector& surfaceMapData, std::vector* roiData) const { surfaceMapData.clear();//empty data is secondary hint at failure CaretAssert(m_ciftiFile); CaretAssertVectorIndex(m_mapContent, mapIndex); const int32_t surfaceNumNodes = getMappingSurfaceNumberOfNodes(structure); if (surfaceNumNodes < 1) return false; std::vector mapData; getMapData(mapIndex, mapData); /* * Map data may be empty for connectivity matrix files with no rows loaded. */ if (mapData.empty()) { return false; } std::vector dataIndicesForNodes; if (!getSurfaceDataIndicesForMappingToBrainordinates(structure, surfaceNumNodes, dataIndicesForNodes)) { return false;//currently should never happen, this currently works for parcellated files } CaretAssert((int)dataIndicesForNodes.size() == surfaceNumNodes); surfaceMapData.resize(surfaceNumNodes, 0.0f); if (roiData != NULL) { roiData->clear();//make sure all values get initialized before setting the roi nodes roiData->resize(surfaceNumNodes, 0.0f); } for (int32_t iNode = 0; iNode < surfaceNumNodes; iNode++) { CaretAssertVectorIndex(dataIndicesForNodes, iNode); const int64_t dataIndex = dataIndicesForNodes[iNode]; if (dataIndex >= 0) { surfaceMapData[iNode] = mapData[dataIndex]; if (roiData != NULL) { (*roiData)[iNode] = 1.0f; } } } return true; } /** * Set the map data for the given structure. * * @param mapIndex * Index of the map. * @param structure * The surface structure. * @param surfaceMapData * Data for surface map that must contain same number of elements as * in the brain models map for the surface. * @throw * DataFileException if there is an error. * */ void CiftiMappableDataFile::setMapDataForSurface(const int32_t mapIndex, const StructureEnum::Enum structure, const std::vector surfaceMapData) { CaretAssert(m_ciftiFile); CaretAssertVectorIndex(m_mapContent, mapIndex); const int32_t surfaceNumberOfNodes = static_cast(surfaceMapData.size()); const int32_t numCiftiNodes = getMappingSurfaceNumberOfNodes(structure); if (numCiftiNodes != surfaceNumberOfNodes) { return; } std::vector mapData; getMapData(mapIndex, mapData); /* * Map data may be empty for connectivity matrix files with no rows loaded. */ if (mapData.empty()) { return; } std::vector dataIndicesForNodes; if (getSurfaceDataIndicesForMappingToBrainordinates(structure, surfaceNumberOfNodes, dataIndicesForNodes)) { for (int32_t iNode = 0; iNode < surfaceNumberOfNodes; iNode++) { CaretAssertVectorIndex(dataIndicesForNodes, iNode); const int64_t dataIndex = dataIndicesForNodes[iNode]; if (dataIndex >= 0) { mapData[dataIndex] = surfaceMapData[iNode]; } } setMapData(mapIndex, mapData); } } /** * Get the matrix dimensions. * * @param numberOfRowsOut * Output number of rows in rgba matrix. * @param numberOfColumnsOut * Output number of Columns in rgba matrix. */ void CiftiMappableDataFile::helpMapFileGetMatrixDimensions(int32_t& numberOfRowsOut, int32_t& numberOfColumnsOut) const { CaretAssert(m_ciftiFile); /* * Dimensions of matrix. */ numberOfRowsOut = m_ciftiFile->getNumberOfRows(); numberOfColumnsOut = m_ciftiFile->getNumberOfColumns(); } /** * Help load matrix chart data and order in the given row indices * for a file with multi-mapped file that uses a unqiue palette * or label table for each column (row) in the file. * * @param numberOfRowsOut * Output number of rows in rgba matrix. * @param numberOfColumnsOut * Output number of Columns in rgba matrix. * @param rowIndicesIn * Indices of rows inserted into matrix. * @param rgbaOut * RGBA matrix (number of elements is rows * columns * 4). * @return * True if output data is valid, else false. */ bool CiftiMappableDataFile::helpMapFileLoadChartDataMatrixRGBA(int32_t& numberOfRowsOut, int32_t& numberOfColumnsOut, const std::vector& rowIndicesIn, std::vector& rgbaOut) const { CaretAssert(m_ciftiFile); /* * Dimensions of matrix. */ numberOfRowsOut = m_ciftiFile->getNumberOfRows(); numberOfColumnsOut = m_ciftiFile->getNumberOfColumns(); const int32_t numberOfData = numberOfRowsOut * numberOfColumnsOut; if (numberOfData <= 0) { return false; } const bool useLabelTableFlag = isMappedWithLabelTable(); const bool usePaletteFlag = isMappedWithPalette(); if (( ! useLabelTableFlag) && ( ! usePaletteFlag)) { CaretAssert(0); return false; } const CiftiXML& ciftiXML = m_ciftiFile->getCiftiXML(); const AString badMapTypeMessage("Matrix charts supports only maps in columns at this time for LABEL and SCALAR data"); if (useLabelTableFlag) { if (ciftiXML.getMappingType(CiftiXML::ALONG_ROW) != CiftiMappingType::LABELS) { CaretAssertMessage(0, badMapTypeMessage); CaretLogSevere(badMapTypeMessage); return false; } } if (usePaletteFlag) { if (ciftiXML.getMappingType(CiftiXML::ALONG_ROW) != CiftiMappingType::SCALARS) { CaretAssertMessage(0, badMapTypeMessage); CaretLogSevere(badMapTypeMessage); return false; } } std::vector rowIndices = rowIndicesIn; if (rowIndices.empty()) { rowIndices.resize(numberOfRowsOut); for (int32_t i = 0; i < numberOfRowsOut; i++) { rowIndices[i] = i; } } else { if (static_cast(rowIndices.size()) != numberOfRowsOut) { const AString msg = AString("rowIndices size=%1 is different than " "number of rows in the matrix=%2.").arg(rowIndices.size()).arg(numberOfRowsOut); CaretAssertMessage(0, msg); CaretLogSevere(msg); return false; } } /* * Set up Fast Stats for files that use all data for * statistics and color mapping. */ CiftiMappableDataFile* nonConstMapFile = const_cast(this); /* * Allocate rgba output */ const int32_t numberOfRgba = numberOfData * 4; rgbaOut.resize(numberOfRgba); /* * Get each column, color it using its label table, and then * add the column's coloring into the output coloring. */ std::vector columnData(numberOfRowsOut); std::vector columnRGBA(numberOfRowsOut * 4); for (int32_t iCol = 0; iCol < numberOfColumnsOut; iCol++) { CaretAssertVectorIndex(m_mapContent, iCol); m_ciftiFile->getColumn(&columnData[0], iCol); if (useLabelTableFlag) { const GiftiLabelTable* labelTable = getMapLabelTable(iCol); NodeAndVoxelColoring::colorIndicesWithLabelTable(labelTable, &columnData[0], numberOfRowsOut, &columnRGBA[0]); } 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], &columnData[0], numberOfRowsOut, &columnRGBA[0]); } else { CaretAssert(0); } for (int32_t iRow = 0; iRow < numberOfRowsOut; iRow++) { const int32_t rgbaOffset = (((iRow * numberOfColumnsOut) + iCol) * 4); CaretAssertVectorIndex(rgbaOut, rgbaOffset + 3); const int32_t columnRgbaOffset = (iRow * 4); CaretAssertVectorIndex(columnRGBA, columnRgbaOffset + 3); rgbaOut[rgbaOffset] = columnRGBA[columnRgbaOffset]; rgbaOut[rgbaOffset+1] = columnRGBA[columnRgbaOffset+1]; rgbaOut[rgbaOffset+2] = columnRGBA[columnRgbaOffset+2]; rgbaOut[rgbaOffset+3] = columnRGBA[columnRgbaOffset+3]; } } return true; } /** * Help load matrix chart data and order in the given row indices * for a connectivity matrix file where one palette is used * for all data in the file. * * @param numberOfRowsOut * Output number of rows in rgba matrix. * @param numberOfColumnsOut * Output number of Columns in rgba matrix. * @param rowIndicesIn * Indices of rows inserted into matrix. * @param rgbaOut * RGBA matrix (number of elements is rows * columns * 4). * @return * True if output data is valid, else false. */ bool CiftiMappableDataFile::helpMatrixFileLoadChartDataMatrixRGBA(int32_t& numberOfRowsOut, int32_t& numberOfColumnsOut, const std::vector& rowIndicesIn, std::vector& rgbaOut) const { CaretAssert(m_ciftiFile); /* * Dimensions of matrix. */ numberOfRowsOut = m_ciftiFile->getNumberOfRows(); numberOfColumnsOut = m_ciftiFile->getNumberOfColumns(); const int32_t numberOfData = numberOfRowsOut * numberOfColumnsOut; if (numberOfData <= 0) { return false; } std::vector rowIndices = rowIndicesIn; if (rowIndices.empty()) { rowIndices.resize(numberOfRowsOut); for (int32_t i = 0; i < numberOfRowsOut; i++) { rowIndices[i] = i; } } else { if (static_cast(rowIndices.size()) != numberOfRowsOut) { const AString msg = AString("rowIndices size=%1 is different than " "number of rows in the matrix=%2.").arg(rowIndices.size()).arg(numberOfRowsOut); CaretAssertMessage(0, msg); CaretLogSevere(msg); return false; } } /* * Get the data. */ std::vector data(numberOfData); for (int32_t iRow = 0; iRow < numberOfRowsOut; iRow++) { CaretAssertVectorIndex(rowIndices, iRow); const int32_t rowIndex = rowIndices[iRow]; const int32_t rowOffset = rowIndex * numberOfColumnsOut; CaretAssertVectorIndex(data, rowOffset + numberOfColumnsOut - 1); m_ciftiFile->getRow(&data[rowOffset], iRow); } /* * Get palette for color mapping. */ if (isMappedWithPalette()) { 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 * statistics and color mapping. * Map "0" will return the file fast statistics */ CiftiMappableDataFile* nonConstMapFile = const_cast(this); const FastStatistics* fileFastStats = nonConstMapFile->getFileFastStatistics(); /* * Color the data. */ const int32_t numRGBA = numberOfData * 4; rgbaOut.resize(numRGBA); NodeAndVoxelColoring::colorScalarsWithPalette(fileFastStats, pcm, palette, &data[0], &data[0], numberOfData, &rgbaOut[0]); return true; } else { CaretAssertMessage(0, "Only palette mapped files supported at this time."); } return false; } ///* ========================================================================== */ /** * Constructor. * * @param ciftiFile * The CIFTI data file * @param fileMapDataType * Type of CIFTI file (matrix or multi-map). * @param readingDirectionForCiftiXML * Reading direction for CIFTI XML access * @param mappingDirectionForCiftiXML * Mapping direction for CIFTI XML access * @param mapIndex * Index of this map. */ CiftiMappableDataFile::MapContent::MapContent(CiftiFile* ciftiFile, const FileMapDataType fileMapDataType, const int32_t readingDirectionForCiftiXML, const int32_t mappingDirectionForCiftiXML, const int32_t mapIndex) : CaretObjectTracksModification(), m_ciftiFile(ciftiFile), m_fileMapDataType(fileMapDataType), m_readingDirectionForCiftiXML(readingDirectionForCiftiXML), m_mappingDirectionForCiftiXML(mappingDirectionForCiftiXML), m_mapIndex(mapIndex) { CaretAssert(ciftiFile); CaretAssert(mapIndex >= 0); m_fastStatistics.grabNew(NULL); m_histogram.grabNew(NULL); m_histogramLimitedValues.grabNew(NULL); m_metadata = NULL; m_paletteColorMapping = NULL; m_labelTable = NULL; m_dataCount = 0; m_rgbaValid = false; m_dataIsMappedWithLabelTable = false; const CiftiXML& ciftiXML = m_ciftiFile->getCiftiXML(); switch (m_mappingDirectionForCiftiXML) { case CiftiXML::ALONG_COLUMN: m_dataCount = ciftiFile->getNumberOfRows(); break; case CiftiXML::ALONG_ROW: m_dataCount = ciftiFile->getNumberOfColumns(); break; case CiftiXML::ALONG_STACK: break; } switch (m_fileMapDataType) { case FILE_MAP_DATA_TYPE_INVALID: CaretAssert(0); break; case FILE_MAP_DATA_TYPE_MATRIX: m_metadata = ciftiXML.getFileMetaData(); m_paletteColorMapping = ciftiXML.getFilePalette(); break; case FILE_MAP_DATA_TYPE_MULTI_MAP: switch (ciftiXML.getMappingType(m_readingDirectionForCiftiXML)) { case CiftiMappingType::BRAIN_MODELS: { //const CiftiBrainModelsMap& map = ciftiXML.getBrainModelsMap(m_readingDirectionForCiftiXML); CaretAssert(0); } break; case CiftiMappingType::LABELS: { const CiftiLabelsMap& map = ciftiXML.getLabelsMap(m_readingDirectionForCiftiXML); m_dataIsMappedWithLabelTable = true; m_metadata = map.getMapMetadata(mapIndex); CaretAssert(m_metadata); m_labelTable = map.getMapLabelTable(mapIndex); CaretAssert(m_labelTable); m_mapName = map.getMapName(m_mapIndex); } break; case CiftiMappingType::PARCELS: { //const CiftiParcelsMap& map = ciftiXML.getParcelsMap(m_readingDirectionForCiftiXML); CaretAssert(0); } break; case CiftiMappingType::SCALARS: { const CiftiScalarsMap& map = ciftiXML.getScalarsMap(m_readingDirectionForCiftiXML); m_metadata = map.getMapMetadata(mapIndex); CaretAssert(m_metadata); m_paletteColorMapping = map.getMapPalette(mapIndex); CaretAssert(m_paletteColorMapping); m_mapName = map.getMapName(m_mapIndex); } break; case CiftiMappingType::SERIES: { /* * Series Data has no map metadata but still need valid metadata instance */ m_metadataForMapsWithNoMetaData.grabNew(new GiftiMetaData()); m_metadata = m_metadataForMapsWithNoMetaData; /* * Series data usings the file's palette */ m_paletteColorMapping = ciftiXML.getFilePalette(); CaretAssert(m_paletteColorMapping); /* * Data series do not have map names but the map name is * a function of units and map index. */ const CiftiSeriesMap& map = ciftiXML.getSeriesMap(m_readingDirectionForCiftiXML); AString unitsSuffix; switch (map.getUnit()) { case CiftiSeriesMap::HERTZ: unitsSuffix = " hertz"; break; case CiftiSeriesMap::METER: unitsSuffix = " meters"; break; case CiftiSeriesMap::RADIAN: unitsSuffix = " radians"; break; case CiftiSeriesMap::SECOND: unitsSuffix = " seconds"; break; } if (unitsSuffix.isEmpty()) { m_mapName = ("Map Index: " + AString::number(m_mapIndex + 1)); } else { const float value = (map.getStart() + (m_mapIndex * map.getStep())); m_mapName = (AString::number(value) + unitsSuffix); } } break; } break; } } /** * Destructor. */ CiftiMappableDataFile::MapContent::~MapContent() { /** * Do not delete these as they point to data in CIFTI XML: * m_labelTable * m_paletteColorMapping * m_metadata; */ } /** * Clear the modification status of this map. */ void CiftiMappableDataFile::MapContent::clearModified() { CaretObjectTracksModification::clearModified(); if (m_labelTable != NULL) { m_labelTable->clearModified(); } m_metadata->clearModified(); if (m_paletteColorMapping != NULL) { m_paletteColorMapping->clearModified(); } } /** * @return Modification status. * * DOES NOT include modification status of palette. */ bool CiftiMappableDataFile::MapContent::isModified() const { if (CaretObjectTracksModification::isModified()) { return true; } if (m_labelTable != NULL) { if (m_labelTable->isModified()) { return true; } } if (m_metadata->isModified()) { return true; } /* DO NOT include palette color mapping status ! */ return false; } /** * @return Name of the map. */ AString CiftiMappableDataFile::MapContent::getName() const { return m_mapName; } /** * Set the name of the map. * * @param name * New name for map. */ void CiftiMappableDataFile::MapContent::setName(const AString& name) { if (name == m_mapName) { return; } m_mapName = name; const CiftiXML& ciftiXML = m_ciftiFile->getCiftiXML(); switch (ciftiXML.getMappingType(m_readingDirectionForCiftiXML)) { case CiftiMappingType::BRAIN_MODELS: { //const CiftiBrainModelsMap& map = ciftiXML.getBrainModelsMap(m_readingDirectionForCiftiXML); CaretAssert(0); } break; case CiftiMappingType::LABELS: { const CiftiLabelsMap& map = ciftiXML.getLabelsMap(m_readingDirectionForCiftiXML); map.setMapName(m_mapIndex, m_mapName); setModified(); } break; case CiftiMappingType::PARCELS: { //const CiftiParcelsMap& map = ciftiXML.getParcelsMap(m_readingDirectionForCiftiXML); // mapName = map.getMapName(m_mapIndex); CaretAssert(0); } break; case CiftiMappingType::SCALARS: { const CiftiScalarsMap& map = ciftiXML.getScalarsMap(m_readingDirectionForCiftiXML); map.setMapName(m_mapIndex, m_mapName); setModified(); } break; case CiftiMappingType::SERIES: /* * Data series do not have map names but the map name is * a function of units and map index. */ break; } } /** * Invalidate the coloring (usually due to palette or data changes). */ void CiftiMappableDataFile::MapContent::updateForChangeInMapData() { m_fastStatistics.grabNew(NULL); m_histogram.grabNew(NULL); m_histogramLimitedValues.grabNew(NULL); m_rgbaValid = false; } /** * @return True if fast statistics is valid, else false. */ bool CiftiMappableDataFile::MapContent::isFastStatisticsValid() const { return (m_fastStatistics != NULL); } /** * Update the Fast Statistics but only when needed. * * @param data * Data for fast statistics. */ void CiftiMappableDataFile::MapContent::updateFastStatistics(const std::vector& data) { if (data.empty()) { m_fastStatistics.grabNew(NULL); } else { if (m_fastStatistics == NULL) { m_fastStatistics.grabNew(new FastStatistics()); m_fastStatistics->update(&data[0], data.size()); } } } /** * @return True if histogram is valid, else false. */ bool CiftiMappableDataFile::MapContent::isHistogramValid() const { return (m_histogram != NULL); } /** * Update the Histogram but only when needed. * * @param data * Data for histogram. */ void CiftiMappableDataFile::MapContent::updateHistogram(const std::vector& data) { if (data.empty()) { m_histogram.grabNew(NULL); } else { if (m_histogram == NULL) { m_histogram.grabNew(new Histogram()); m_histogram->update(&data[0], data.size()); } } } /** * Is limited values histogram valid? Is is valid when it exists and * the limited value parameters have not changed. * * @param mostPositiveValueInclusive * Values more positive than this value are excluded. * @param leastPositiveValueInclusive * Values less positive than this value are excluded. * @param leastNegativeValueInclusive * Values less negative than this value are excluded. * @param mostNegativeValueInclusive * Values more negative than this value are excluded. * @param includeZeroValues * If true zero values (very near zero) are included. * * @return true if valid, else false. */ bool CiftiMappableDataFile::MapContent::isHistogramLimitedValuesValid(const float mostPositiveValueInclusive, const float leastPositiveValueInclusive, const float leastNegativeValueInclusive, const float mostNegativeValueInclusive, const bool includeZeroValues) const { if (m_histogramLimitedValues == NULL) { return false; } else if ((mostPositiveValueInclusive != m_histogramLimitedValuesMostPositiveValueInclusive) || (leastPositiveValueInclusive != m_histogramLimitedValuesLeastPositiveValueInclusive) || (leastNegativeValueInclusive != m_histogramLimitedValuesLeastNegativeValueInclusive) || (mostNegativeValueInclusive != m_histogramLimitedValuesMostNegativeValueInclusive) || (includeZeroValues != m_histogramLimitedValuesIncludeZeroValues)) { return false; } return true; } /** * Update the Histogram for limited values. * * @param data * Data for histogram. * @param mostPositiveValueInclusive * Values more positive than this value are excluded. * @param leastPositiveValueInclusive * Values less positive than this value are excluded. * @param leastNegativeValueInclusive * Values less negative than this value are excluded. * @param mostNegativeValueInclusive * Values more negative than this value are excluded. * @param includeZeroValues * If true zero values (very near zero) are included. */ void CiftiMappableDataFile::MapContent::updateHistogramLimitedValues(const std::vector& data, const float mostPositiveValueInclusive, const float leastPositiveValueInclusive, const float leastNegativeValueInclusive, const float mostNegativeValueInclusive, const bool includeZeroValues) { if (data.empty()) { m_histogramLimitedValues.grabNew(NULL); } else { if (m_histogramLimitedValues == NULL) { m_histogramLimitedValues.grabNew(new Histogram()); } m_histogramLimitedValues->update(&data[0], data.size(), mostPositiveValueInclusive, leastPositiveValueInclusive, leastNegativeValueInclusive, mostNegativeValueInclusive, includeZeroValues); m_histogramLimitedValuesMostPositiveValueInclusive = mostPositiveValueInclusive; m_histogramLimitedValuesLeastPositiveValueInclusive = leastPositiveValueInclusive; m_histogramLimitedValuesLeastNegativeValueInclusive = leastNegativeValueInclusive; m_histogramLimitedValuesMostNegativeValueInclusive = mostNegativeValueInclusive; m_histogramLimitedValuesIncludeZeroValues = includeZeroValues; } } /** * 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 * 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()) { return; } if (m_rgbaValid) { return; } 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); } if (m_dataIsMappedWithLabelTable) { NodeAndVoxelColoring::colorIndicesWithLabelTable(m_labelTable, &data[0], data.size(), &m_rgba[0]); } else if (paletteFile != NULL) { 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]); } else { std::fill(m_rgba.begin(), m_rgba.end(), 0); } } else { const AString msg("NULL palette for coloring scalar data."); CaretAssertMessage(0, msg); CaretLogSevere(msg); } m_rgbaValid = true; } connectome-workbench-1.2.3+git41-gc4c6c90/src/Files/CiftiMappableDataFile.h000066400000000000000000001026711300200146000260600ustar00rootroot00000000000000#ifndef __CIFTI_MAPPABLE_DATA_FILE_H__ #define __CIFTI_MAPPABLE_DATA_FILE_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 "CaretMappableDataFile.h" #include "CaretPointer.h" #include "CaretObjectTracksModification.h" #include "CiftiMappingType.h" #include "CiftiXMLElements.h" #include "DisplayGroupEnum.h" #include "VolumeMappableInterface.h" #include namespace caret { class ChartData; class ChartDataCartesian; class CiftiFile; class CiftiParcelsMap; class CiftiXML; class FastStatistics; class GroupAndNameHierarchyModel; class Histogram; class SparseVolumeIndexer; class CiftiMappableDataFile : public CaretMappableDataFile, public VolumeMappableInterface { protected: /** * Method for accessing data with CIFTI */ enum DataAccessMethod { /** * Invalid data access method. */ DATA_ACCESS_METHOD_INVALID, /** * No access to data */ DATA_ACCESS_NONE, /** * The data is accessed from CiftiFile using ROW Methods * and from the XML using ALONG_COLUMN */ DATA_ACCESS_FILE_ROWS_OR_XML_ALONG_COLUMN, /** * Use ALONG_ROW to access CIFTI data * which means one is accessing COLUMNS of data */ DATA_ACCESS_FILE_COLUMNS_OR_XML_ALONG_ROW }; /** * Method for color mapping a map */ enum ColorMappingMethod { /** * Invalid color mapping method. */ COLOR_MAPPING_METHOD_INVALID, /** * Color data using a label table */ COLOR_MAPPING_METHOD_LABEL_TABLE, /** * Color data using a color palette */ COLOR_MAPPING_METHOD_PALETTE }; /** * Source of palette color mapping. * Some CIFTI files provide one palette color mapping per file. * Others have one palette color mapping for each map in the file. */ enum PaletteColorMappingSourceType { PALETTE_COLOR_MAPPING_SOURCE_INVALID, /** * Use file's palette color mapping. */ PALETTE_COLOR_MAPPING_SOURCE_FROM_FILE, /** * Use map's palette color mapping. */ PALETTE_COLOR_MAPPING_SOURCE_FROM_MAP }; /** * Type of map data in the file. */ enum FileMapDataType { /** * Invalid file map data type */ FILE_MAP_DATA_TYPE_INVALID, /** * The file contains a connectivity matrix. There is only * 'one map' and the data for the map is selectively read * and replaced based upon user actions. */ FILE_MAP_DATA_TYPE_MATRIX, /** * The file contains one or more maps and all maps are loaded * when the file is read. */ FILE_MAP_DATA_TYPE_MULTI_MAP }; /** How to read the file */ enum FileDataReadingType { /** Read all data in the file */ FILE_READ_DATA_ALL, /** Open the file but only read data as needed */ FILE_READ_DATA_AS_NEEDED }; CiftiMappableDataFile(const DataFileTypeEnum::Enum dataFileType); public: virtual ~CiftiMappableDataFile(); static CiftiMappableDataFile* newInstanceForCiftiFileTypeAndSurface(const DataFileTypeEnum::Enum ciftiFileType, const StructureEnum::Enum structure, const int32_t numberOfNodes, AString& errorMessageOut); virtual void addToDataFileContentInformation(DataFileContentInformation& dataFileInformation); static void addCiftiXmlToDataFileContentInformation(DataFileContentInformation& dataFileInformation, const CiftiXML& ciftiXML); static void getDataFileContentInformationForGenericCiftiFile(const AString& filename, DataFileContentInformation& dataFileInformation); virtual void clear(); virtual bool isEmpty() const; virtual bool isMappableToSurfaceStructure(const StructureEnum::Enum structure) const; virtual StructureEnum::Enum getStructure() const; virtual void setStructure(const StructureEnum::Enum structure); virtual GiftiMetaData* getFileMetaData(); virtual const GiftiMetaData* getFileMetaData() const; virtual void setPreferOnDiskReading(const bool& prefer); virtual void readFile(const AString& ciftiMapFileName); virtual void writeFile(const AString& filename); virtual bool isSurfaceMappable() const; virtual bool isVolumeMappable() const; virtual int32_t getNumberOfMaps() const; virtual bool hasMapAttributes() const; virtual AString getMapName(const int32_t mapIndex) const; virtual void setMapName(const int32_t mapIndex, const AString& mapName); virtual const GiftiMetaData* getMapMetaData(const int32_t mapIndex) const; virtual GiftiMetaData* getMapMetaData(const int32_t mapIndex); virtual AString getMapUniqueID(const int32_t mapIndex) const; virtual bool isMappedWithPalette() const; virtual const FastStatistics* getMapFastStatistics(const int32_t mapIndex); virtual const Histogram* getMapHistogram(const int32_t mapIndex); virtual const Histogram* getMapHistogram(const int32_t mapIndex, const float mostPositiveValueInclusive, const float leastPositiveValueInclusive, const float leastNegativeValueInclusive, const float mostNegativeValueInclusive, const bool includeZeroValues); virtual void getPaletteNormalizationModesSupported(std::vector& modesSupportedOut); virtual int64_t getDataSizeUncompressedInBytes() const; virtual const FastStatistics* getFileFastStatistics(); virtual const Histogram* getFileHistogram(); virtual const Histogram* getFileHistogram(const float mostPositiveValueInclusive, const float leastPositiveValueInclusive, const float leastNegativeValueInclusive, const float mostNegativeValueInclusive, const bool includeZeroValues); virtual PaletteColorMapping* getMapPaletteColorMapping(const int32_t mapIndex); virtual const PaletteColorMapping* getMapPaletteColorMapping(const int32_t mapIndex) const; const CiftiParcelsMap* getCiftiParcelsMapForBrainordinateMapping() const; const CiftiParcelsMap* getCiftiParcelsMapForLoading() const; const CiftiParcelsMap* getCiftiParcelsMapForDirection(const int direction) const; virtual bool isMappedWithLabelTable() const; virtual GiftiLabelTable* getMapLabelTable(const int32_t mapIndex); virtual const GiftiLabelTable* getMapLabelTable(const int32_t mapIndex) const; virtual void updateScalarColoringForAllMaps(const PaletteFile* paletteFile); virtual void updateScalarColoringForMap(const int32_t mapIndex, const PaletteFile* paletteFile); virtual bool isMapColoringValid(const int32_t mapIndex) const; virtual void getDimensions(int64_t& dimOut1, int64_t& dimOut2, int64_t& dimOut3, int64_t& dimTimeOut, int64_t& numComponents) const; virtual void getDimensions(std::vector& dimsOut) const; virtual void getMapDimensions(std::vector &dim) const; virtual const int64_t& getNumberOfComponents() const; virtual void indexToSpace(const float& indexIn1, const float& indexIn2, const float& indexIn3, float& coordOut1, float& coordOut2, float& coordOut3) const; virtual void indexToSpace(const float& indexIn1, const float& indexIn2, const float& indexIn3, float* coordOut) const; virtual void indexToSpace(const int64_t* indexIn, float* coordOut) const; virtual void enclosingVoxel(const float& coordIn1, const float& coordIn2, const float& coordIn3, int64_t& indexOut1, int64_t& indexOut2, int64_t& indexOut3) const; 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; virtual const VolumeSpace& getVolumeSpace() const; virtual void getVoxelSpaceBoundingBox(BoundingBox& boundingBoxOut) const; virtual int64_t getVoxelColorsForSliceInMap(const PaletteFile* paletteFile, const int32_t mapIndex, const VolumeSliceViewPlaneEnum::Enum slicePlane, const int64_t sliceIndex, const DisplayGroupEnum::Enum displayGroup, const int32_t tabIndex, uint8_t* rgbaOut) const; int64_t 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; virtual int64_t getVoxelColorsForSubSliceInMap(const PaletteFile* paletteFile, const int32_t mapIndex, const VolumeSliceViewPlaneEnum::Enum slicePlane, const int64_t sliceIndex, const int64_t firstCornerVoxelIndex[3], const int64_t lastCornerVoxelIndex[3], const int64_t voxelCountIJK[3], const DisplayGroupEnum::Enum displayGroup, const int32_t tabIndex, uint8_t* rgbaOut) const; virtual void getVoxelColorInMap(const PaletteFile* paletteFile, 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; virtual void getVoxelColorInMapForLabelData(const PaletteFile* paletteFile, const std::vector& dataForMap, 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; virtual bool getMapVolumeVoxelValue(const int32_t mapIndex, const float xyz[3], int64_t ijkOut[3], float& numericalValueOut, bool& numericalValueOutValid, AString& textValueOut) const; virtual bool getMapVolumeVoxelValues(const std::vector mapIndices, const float xyz[3], int64_t ijkOut[3], std::vector& numericalValuesOut, std::vector& numericalValuesOutValid, AString& textValueOut) const; int64_t getMapDataOffsetForVoxelAtCoordinate(const float coordinate[3], const int32_t mapIndex) const; virtual float getVoxelValue(const float* coordinateIn, bool* validOut = NULL, const int64_t mapIndex = 0, const int64_t component = 0) const; 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; virtual bool getVolumeVoxelIdentificationForMaps(const std::vector& mapIndices, const float xyz[3], int64_t ijkOut[3], AString& textOut) const; std::vector getUniqueLabelKeysUsedInMap(const int32_t mapIndex) const; GroupAndNameHierarchyModel* getGroupAndNameHierarchyModel(); const GroupAndNameHierarchyModel* getGroupAndNameHierarchyModel() const; virtual bool getMapSurfaceNodeValue(const int32_t mapIndex, const StructureEnum::Enum structure, const int nodeIndex, const int32_t numberOfNodes, float& numericalValueOut, bool& numericalValueOutValid, AString& textValueOut) const; virtual bool getMapSurfaceNodeValues(const std::vector& mapIndices, const StructureEnum::Enum structure, const int nodeIndex, const int32_t numberOfNodes, std::vector& numericalValuesOut, std::vector& numericalValuesOutValid, AString& textValueOut) const; virtual bool getSurfaceNodeIdentificationForMaps(const std::vector& mapIndices, const StructureEnum::Enum structure, const int nodeIndex, const int32_t numberOfNodes, AString& textOut) const; int32_t getMappingSurfaceNumberOfNodes(const StructureEnum::Enum structure) const; bool getSeriesDataForSurfaceNode(const StructureEnum::Enum structure, const int32_t nodeIndex, std::vector& seriesDataOut) const; bool getSeriesDataForVoxelAtCoordinate(const float xyz[3], std::vector& seriesDataOut) const; virtual bool getMapSurfaceNodeColoring(const PaletteFile* paletteFile, const int32_t mapIndex, const StructureEnum::Enum structure, float* surfaceRGBAOut, float* dataValuesOut, const int32_t surfaceNumberOfNodes); virtual void clearModified(); virtual bool isModifiedExcludingPaletteColorMapping() const; virtual NiftiTimeUnitsEnum::Enum getMapIntervalUnits() const; virtual void getMapIntervalStartAndStep(float& firstMapUnitsValueOut, float& mapIntervalStepValueOut) const; virtual bool getDataRangeFromAllMaps(float& dataRangeMinimumOut, float& dataRangeMaximumOut) const; bool getMapDataForSurface(const int32_t mapIndex, const StructureEnum::Enum structure, std::vector& surfaceMapData, std::vector* roiData = NULL) const; void setMapDataForSurface(const int32_t mapIndex, const StructureEnum::Enum structure, const std::vector surfaceMapData); bool getParcelNodesElementForSelectedParcel(std::set &parcelNodesOut, const StructureEnum::Enum &structure, const int64_t &selectionIndex) const; void invalidateColoringInAllMaps(); void getBrainordinateFromRowIndex(const int64_t rowIndex, StructureEnum::Enum& surfaceStructureOut, int32_t& surfaceNodeIndexOut, int32_t& surfaceNumberOfNodesOut, bool& surfaceNodeValidOut, int64_t voxelIJKOut[3], float voxelXYZOut[3], bool& voxelValidOut) const; private: CiftiMappableDataFile(const CiftiMappableDataFile&); CiftiMappableDataFile& operator=(const CiftiMappableDataFile&); public: virtual void getMapData(const int32_t mapIndex, std::vector& dataOut) const; virtual void setMapData(const int32_t mapIndex, const std::vector& data); virtual void getMatrixRGBA(std::vector& rgba, PaletteFile *paletteFile); virtual void getFileData(std::vector& data) const; const CiftiFile* getCiftiFile() const { return m_ciftiFile; } protected: virtual bool getParcelLabelMapSurfaceNodeValue(const int32_t mapIndex, const StructureEnum::Enum structure, const int nodeIndex, const int32_t numberOfNodes, float& numericalValueOut, bool& numericalValueOutValid, AString& textValueOut) const; void updateForChangeInMapDataWithMapIndex(const int32_t mapIndex); ChartDataCartesian* helpLoadChartDataForSurfaceNode(const StructureEnum::Enum structure, const int32_t nodeIndex); ChartDataCartesian* helpLoadChartDataForSurfaceNodeAverage(const StructureEnum::Enum structure, const std::vector& nodeIndices); ChartDataCartesian* helpLoadChartDataForVoxelAtCoordinate(const float xyz[3]); void helpMapFileGetMatrixDimensions(int32_t& numberOfRowsOut, int32_t& numberOfColumnsOut) const; bool helpMapFileLoadChartDataMatrixRGBA(int32_t& numberOfRowsOut, int32_t& numberOfColumnsOut, const std::vector& rowIndicesIn, std::vector& rgbaOut) const; bool helpMatrixFileLoadChartDataMatrixRGBA(int32_t& numberOfRowsOut, int32_t& numberOfColumnsOut, 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, const FileMapDataType fileMapDataType, const int32_t readingDirectionForCiftiXML, const int32_t mappingDirectionForCiftiXML, const int32_t mapIndex); ~MapContent(); virtual void clearModified(); virtual bool isModified() const; 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; void updateHistogram(const std::vector& data); bool isHistogramLimitedValuesValid(const float mostPositiveValueInclusive, const float leastPositiveValueInclusive, const float leastNegativeValueInclusive, const float mostNegativeValueInclusive, const bool includeZeroValues) const; void updateHistogramLimitedValues(const std::vector& data, const float mostPositiveValueInclusive, const float leastPositiveValueInclusive, const float leastNegativeValueInclusive, const float mostNegativeValueInclusive, const bool includeZeroValues); AString getName() const; void setName(const AString& name); /** The CIFTI file pointer */ CiftiFile *m_ciftiFile; /** Maps or Matrix file */ const FileMapDataType m_fileMapDataType; /** Direction for obtaining reading information (CiftiXML::ALONG_ROW, etc) */ const int32_t m_readingDirectionForCiftiXML; /** Direction for obtaining mapping information (CiftiXML::ALONG_ROW, etc) */ const int32_t m_mappingDirectionForCiftiXML; /** Index of this map */ const int32_t m_mapIndex; /** Name of the map */ AString m_mapName; /** Count of data elements in map. */ int64_t m_dataCount; /** Metadata for the map. Points to data in CiftiFile so DO NOT delete */ GiftiMetaData* m_metadata; /** Palette color mapping for map. Points to data in CiftiFile so DO NOT delete */ PaletteColorMapping* m_paletteColorMapping; /** Label table for map. Points to data in CiftiFile so DO NOT delete */ GiftiLabelTable* m_labelTable; /** Indicates data is mapped with a lable table */ bool m_dataIsMappedWithLabelTable; /** RGBA coloring for map */ std::vector m_rgba; /** RGBA coloring is valid */ bool m_rgbaValid; /** fast statistics for map */ CaretPointer m_fastStatistics; /** histogram for all of map map */ CaretPointer m_histogram; /** histogram for limited values from map */ CaretPointer m_histogramLimitedValues; float m_histogramLimitedValuesMostPositiveValueInclusive; float m_histogramLimitedValuesLeastPositiveValueInclusive; float m_histogramLimitedValuesLeastNegativeValueInclusive; float m_histogramLimitedValuesMostNegativeValueInclusive; bool m_histogramLimitedValuesIncludeZeroValues; private: /** Name of map */ AString m_name; /** * For maps that do not have metadata, a metadata instance * is still needed even though it essentially does nothing. */ CaretPointer m_metadataForMapsWithNoMetaData; }; void clearPrivate(); protected: void initializeAfterReading(const AString& filename); void validateMappingTypes(const AString& filename); void resetDataLoadingMembers(); void validateKeysAndLabels() const; virtual void validateAfterFileReading(); virtual void saveFileDataToScene(const SceneAttributes* sceneAttributes, SceneClass* sceneClass); virtual void restoreFileDataFromScene(const SceneAttributes* sceneAttributes, const SceneClass* sceneClass); bool getSurfaceDataIndicesForMappingToBrainordinates(const StructureEnum::Enum structure, const int64_t surfaceNumberOfNodes, std::vector& dataIndicesForNodes) const; void setupCiftiReadingMappingDirection(); static AString mappingTypeToName(const CiftiMappingType::MappingType mappingType); /** * Point to the CIFTI file object. */ CaretPointer m_ciftiFile; /** * How to read data from the file */ FileDataReadingType m_fileDataReadingType; /** * Method used when reading data from the file. */ DataAccessMethod m_dataReadingAccessMethod; /** Direction for obtaining mapping information (CiftiXML::ALONG_ROW, etc) */ int32_t m_dataReadingDirectionForCiftiXML; /** * Method used when mapping loaded data to brainordinates. */ DataAccessMethod m_dataMappingAccessMethod; /** Direction for obtaining mapping information (CiftiXML::ALONG_ROW, etc) */ int32_t m_dataMappingDirectionForCiftiXML; /** * Method used when mapping data to colors (LabelTable or Palette). */ ColorMappingMethod m_colorMappingMethod; /** * Source of color palette (file or per map) */ PaletteColorMappingSourceType m_paletteColorMappingSource; /* * Supported palette normalization modes */ std::vector m_paletteNormalizationModesSupported; /** * Type of data in the file's map(s). */ FileMapDataType m_fileMapDataType; /** Contains data related to each map */ std::vector m_mapContent; /** True if the file contains surface data */ bool m_containsSurfaceData; /** True if the file contains surface data */ bool m_containsVolumeData; float m_mappingTimeStart; float m_mappingTimeStep; NiftiTimeUnitsEnum::Enum m_mappingTimeUnits; /** Fast statistics used when statistics computed on all data in file */ CaretPointer m_fileFastStatistics; /** Histogram used when statistics computed on all data in file */ CaretPointer m_fileHistogram; /** Histogram with limited values used when statistics computed on all data in file */ CaretPointer m_fileHistorgramLimitedValues; float m_fileHistogramLimitedValuesMostPositiveValueInclusive; float m_fileHistogramLimitedValuesLeastPositiveValueInclusive; float m_fileHistogramLimitedValuesLeastNegativeValueInclusive; float m_fileHistogramLimitedValuesMostNegativeValueInclusive; bool m_fileHistogramLimitedValuesIncludeZeroValues; /** Fast conversion of IJK to data offset */ CaretPointer m_voxelIndicesToOffset; /** Holds class and name hierarchy used for display selection */ mutable CaretPointer m_classNameHierarchy; /** 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; // ADD_NEW_MEMBERS_HERE }; #ifdef __CIFTI_MAPPABLE_DATA_FILE_DECLARE__ const int32_t CiftiMappableDataFile::S_CIFTI_XML_ALONG_INVALID = -1; #endif // __CIFTI_MAPPABLE_DATA_FILE_DECLARE__ } // namespace #endif //__CIFTI_MAPPABLE_DATA_FILE_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Files/CiftiParcelColoringModeEnum.cxx000066400000000000000000000262071300200146000276550ustar00rootroot00000000000000 /*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 __CIFTI_PARCEL_COLORING_MODE_ENUM_DECLARE__ #include "CiftiParcelColoringModeEnum.h" #undef __CIFTI_PARCEL_COLORING_MODE_ENUM_DECLARE__ #include "CaretAssert.h" using namespace caret; /** * \class caret::CiftiParcelColoringModeEnum * \brief Enumerated type for selected parcel coloring. * * Enumerated type for coloring of selected CIFTI parcel * * 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_ciftiParcelColoringModeEnumComboBox; * * Declare a slot that is called when user changes selection * private slots: * void ciftiParcelColoringModeEnumComboBoxItemActivated(); * * Implementation File (.cxx) * Include the header files * #include "EnumComboBoxTemplate.h" * #include "CiftiParcelColoringModeEnum.h" * * Instatiate: * m_ciftiParcelColoringModeEnumComboBox = new EnumComboBoxTemplate(this); * m_ciftiParcelColoringModeEnumComboBox->setup(); * * Get notified when the user changes the selection: * QObject::connect(m_ciftiParcelColoringModeEnumComboBox, SIGNAL(itemActivated()), * this, SLOT(ciftiParcelColoringModeEnumComboBoxItemActivated())); * * Update the selection: * m_ciftiParcelColoringModeEnumComboBox->setSelectedItem(NEW_VALUE); * * Read the selection: * const CiftiParcelColoringModeEnum::Enum VARIABLE = m_ciftiParcelColoringModeEnumComboBox->getSelectedItem(); * */ /** * Constructor. * * @param enumValue * An enumerated value. * @param name * Name of enumerated value. * * @param guiName * User-friendly name for use in user-interface. */ CiftiParcelColoringModeEnum::CiftiParcelColoringModeEnum(const Enum enumValue, const AString& name, const AString& guiName) { this->enumValue = enumValue; this->integerCode = integerCodeCounter++; this->name = name; this->guiName = guiName; } /** * Destructor. */ CiftiParcelColoringModeEnum::~CiftiParcelColoringModeEnum() { } /** * Initialize the enumerated metadata. */ void CiftiParcelColoringModeEnum::initialize() { if (initializedFlag) { return; } initializedFlag = true; enumData.push_back(CiftiParcelColoringModeEnum(CIFTI_PARCEL_COLORING_OFF, "CIFTI_PARCEL_COLORING_OFF", "Off")); enumData.push_back(CiftiParcelColoringModeEnum(CIFTI_PARCEL_COLORING_FILL, "CIFTI_PARCEL_COLORING_FILL", "Fill")); enumData.push_back(CiftiParcelColoringModeEnum(CIFTI_PARCEL_COLORING_OUTLINE, "CIFTI_PARCEL_COLORING_OUTLINE", "Outline")); } /** * 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 CiftiParcelColoringModeEnum* CiftiParcelColoringModeEnum::findData(const Enum enumValue) { if (initializedFlag == false) initialize(); size_t num = enumData.size(); for (size_t i = 0; i < num; i++) { const CiftiParcelColoringModeEnum* 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 CiftiParcelColoringModeEnum::toName(Enum enumValue) { if (initializedFlag == false) initialize(); const CiftiParcelColoringModeEnum* 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. */ CiftiParcelColoringModeEnum::Enum CiftiParcelColoringModeEnum::fromName(const AString& name, bool* isValidOut) { if (initializedFlag == false) initialize(); bool validFlag = false; Enum enumValue = CiftiParcelColoringModeEnum::enumData[0].enumValue; for (std::vector::iterator iter = enumData.begin(); iter != enumData.end(); iter++) { const CiftiParcelColoringModeEnum& 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 CiftiParcelColoringModeEnum")); } return enumValue; } /** * Get a GUI string representation of the enumerated type. * @param enumValue * Enumerated value. * @return * String representing enumerated value. */ AString CiftiParcelColoringModeEnum::toGuiName(Enum enumValue) { if (initializedFlag == false) initialize(); const CiftiParcelColoringModeEnum* 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. */ CiftiParcelColoringModeEnum::Enum CiftiParcelColoringModeEnum::fromGuiName(const AString& guiName, bool* isValidOut) { if (initializedFlag == false) initialize(); bool validFlag = false; Enum enumValue = CiftiParcelColoringModeEnum::enumData[0].enumValue; for (std::vector::iterator iter = enumData.begin(); iter != enumData.end(); iter++) { const CiftiParcelColoringModeEnum& 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 CiftiParcelColoringModeEnum")); } return enumValue; } /** * Get the integer code for a data type. * * @return * Integer code for data type. */ int32_t CiftiParcelColoringModeEnum::toIntegerCode(Enum enumValue) { if (initializedFlag == false) initialize(); const CiftiParcelColoringModeEnum* 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. */ CiftiParcelColoringModeEnum::Enum CiftiParcelColoringModeEnum::fromIntegerCode(const int32_t integerCode, bool* isValidOut) { if (initializedFlag == false) initialize(); bool validFlag = false; Enum enumValue = CiftiParcelColoringModeEnum::enumData[0].enumValue; for (std::vector::iterator iter = enumData.begin(); iter != enumData.end(); iter++) { const CiftiParcelColoringModeEnum& 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 CiftiParcelColoringModeEnum")); } 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 CiftiParcelColoringModeEnum::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 CiftiParcelColoringModeEnum::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(CiftiParcelColoringModeEnum::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 CiftiParcelColoringModeEnum::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(CiftiParcelColoringModeEnum::toGuiName(iter->enumValue)); } if (isSorted) { std::sort(allGuiNames.begin(), allGuiNames.end()); } } connectome-workbench-1.2.3+git41-gc4c6c90/src/Files/CiftiParcelColoringModeEnum.h000066400000000000000000000065011300200146000272750ustar00rootroot00000000000000#ifndef __CIFTI_PARCEL_COLORING_MODE_ENUM_H__ #define __CIFTI_PARCEL_COLORING_MODE_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 CiftiParcelColoringModeEnum { public: /** * Enumerated values. */ enum Enum { /** off (no special coloring) */ CIFTI_PARCEL_COLORING_OFF, /** fill with user defined color */ CIFTI_PARCEL_COLORING_FILL, /** outline with user defined color */ CIFTI_PARCEL_COLORING_OUTLINE }; ~CiftiParcelColoringModeEnum(); 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: CiftiParcelColoringModeEnum(const Enum enumValue, const AString& name, const AString& guiName); static const CiftiParcelColoringModeEnum* 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 __CIFTI_PARCEL_COLORING_MODE_ENUM_DECLARE__ std::vector CiftiParcelColoringModeEnum::enumData; bool CiftiParcelColoringModeEnum::initializedFlag = false; int32_t CiftiParcelColoringModeEnum::integerCodeCounter = 0; #endif // __CIFTI_PARCEL_COLORING_MODE_ENUM_DECLARE__ } // namespace #endif //__CIFTI_PARCEL_COLORING_MODE_ENUM_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Files/CiftiParcelLabelFile.cxx000066400000000000000000000614121300200146000262630ustar00rootroot00000000000000 /*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 __CIFTI_PARCEL_LABEL_FILE_DECLARE__ #include "CiftiParcelLabelFile.h" #undef __CIFTI_PARCEL_LABEL_FILE_DECLARE__ #include "CaretAssert.h" #include "CaretLogger.h" #include "ChartMatrixDisplayProperties.h" #include "CiftiFile.h" #include "CiftiParcelReordering.h" #include "CiftiParcelReorderingModel.h" #include "GiftiLabel.h" #include "GiftiLabelTable.h" #include "NodeAndVoxelColoring.h" #include "SceneClass.h" #include "SceneClassAssistant.h" using namespace caret; /** * \class caret::CiftiParcelLabelFile * \brief CIFTI Parcellated Label File * \ingroup Files */ /** * Constructor. */ CiftiParcelLabelFile::CiftiParcelLabelFile() : CiftiMappableDataFile(DataFileTypeEnum::CONNECTIVITY_PARCEL_LABEL) { for (int32_t i = 0; i < BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS; i++) { m_chartingEnabledForTab[i] = false; m_chartMatrixDisplayProperties[i] = new ChartMatrixDisplayProperties(); } m_selectedParcelColoringMode = CiftiParcelColoringModeEnum::CIFTI_PARCEL_COLORING_OUTLINE; m_selectedParcelColor = CaretColorEnum::WHITE; m_parcelReorderingModel = new CiftiParcelReorderingModel(this); m_sceneAssistant = new SceneClassAssistant(); m_sceneAssistant->add("m_selectedParcelColoringMode", &m_selectedParcelColoringMode); m_sceneAssistant->add("m_selectedParcelColor", &m_selectedParcelColor); m_sceneAssistant->add("m_parcelReorderingModel", "CiftiParcelReorderingModel", m_parcelReorderingModel); } /** * Destructor. */ CiftiParcelLabelFile::~CiftiParcelLabelFile() { for (int32_t i = 0; i < BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS; i++) { delete m_chartMatrixDisplayProperties[i]; } delete m_parcelReorderingModel; delete m_sceneAssistant; } /** * Get the matrix dimensions. * * @param numberOfRowsOut * Number of rows in the matrix. * @param numberOfColumnsOut * Number of rows in the matrix. */ void CiftiParcelLabelFile::getMatrixDimensions(int32_t& numberOfRowsOut, int32_t& numberOfColumnsOut) const { 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 CiftiParcelLabelFile::getMatrixDataRGBA(int32_t& numberOfRowsOut, int32_t& numberOfColumnsOut, std::vector& rgbaOut) const { CiftiParcelLabelFile* parcelLabelFile = NULL; int32_t parcelLabelFileMapIndex = -1; bool enabled = false; std::vector parcelLabelFiles; getSelectedParcelLabelFileAndMapForReordering(parcelLabelFiles, parcelLabelFile, parcelLabelFileMapIndex, enabled); std::vector rowIndices; if (enabled) { const CiftiParcelReordering* parcelReordering = getParcelReordering(parcelLabelFile, parcelLabelFileMapIndex); if (parcelReordering != NULL) { rowIndices = parcelReordering->getReorderedParcelIndices(); } } return helpMapFileLoadChartDataMatrixRGBA(numberOfRowsOut, numberOfColumnsOut, rowIndices, rgbaOut); // CaretAssert(m_ciftiFile); // // /* // * Dimensions of matrix. // */ // numberOfRowsOut = m_ciftiFile->getNumberOfRows(); // numberOfColumnsOut = m_ciftiFile->getNumberOfColumns(); // const int32_t numberOfData = numberOfRowsOut * numberOfColumnsOut; // if (numberOfData <= 0) { // return false; // } // // /* // * Allocate rgba output // */ // const int32_t numberOfRgba = numberOfData * 4; // rgbaOut.resize(numberOfRgba); // // /* // * Get each column, color it using its label table, and then // * add the column's coloring into the output coloring. // */ // std::vector columnData(numberOfRowsOut); // std::vector columnRGBA(numberOfRowsOut * 4); // for (int32_t iCol = 0; iCol < numberOfColumnsOut; iCol++) { // CaretAssertVectorIndex(m_mapContent, iCol); // m_ciftiFile->getColumn(&columnData[0], // iCol); // const GiftiLabelTable* labelTable = getMapLabelTable(iCol); // NodeAndVoxelColoring::colorIndicesWithLabelTable(labelTable, // &columnData[0], // numberOfRowsOut, // &columnRGBA[0]); // // for (int32_t iRow = 0; iRow < numberOfRowsOut; iRow++) { // const int32_t rgbaOffset = (((iRow * numberOfColumnsOut) // + iCol) * 4); // CaretAssertVectorIndex(rgbaOut, rgbaOffset + 3); // const int32_t columnRgbaOffset = (iRow * 4); // CaretAssertVectorIndex(columnRGBA, columnRgbaOffset + 3); // rgbaOut[rgbaOffset] = columnRGBA[columnRgbaOffset]; // rgbaOut[rgbaOffset+1] = columnRGBA[columnRgbaOffset+1]; // rgbaOut[rgbaOffset+2] = columnRGBA[columnRgbaOffset+2]; // rgbaOut[rgbaOffset+3] = columnRGBA[columnRgbaOffset+3]; // } // } // // return true; } /** * Get the value, row name, and column name for a cell in the matrix. * * @param rowIndex * The row index. * @param columnIndex * The column index. * @param cellValueOut * Output containing value in the cell. * @param rowNameOut * Name of row corresponding to row index. * @param columnNameOut * Name of column corresponding to column index. * @return * True if the output values are valid (valid row/column indices). */ bool CiftiParcelLabelFile::getMatrixCellAttributes(const int32_t rowIndex, const int32_t columnIndex, AString& cellValueOut, AString& rowNameOut, AString& columnNameOut) const { if ((rowIndex >= 0) && (rowIndex < m_ciftiFile->getNumberOfRows()) && (columnIndex >= 0) && (columnIndex < m_ciftiFile->getNumberOfColumns())) { const CiftiXML& xml = m_ciftiFile->getCiftiXML(); const std::vector& rowsParcelsMap = xml.getParcelsMap(CiftiXML::ALONG_COLUMN).getParcels(); CaretAssertVectorIndex(rowsParcelsMap, rowIndex); rowNameOut = rowsParcelsMap[rowIndex].m_name; CaretAssertVectorIndex(m_mapContent, columnIndex); columnNameOut = getMapName(columnIndex); const int32_t numberOfElementsInRow = m_ciftiFile->getNumberOfColumns(); std::vector rowData(numberOfElementsInRow); m_ciftiFile->getRow(&rowData[0], rowIndex); CaretAssertVectorIndex(rowData, columnIndex); const int32_t labelKey = rowData[columnIndex]; const GiftiLabel* label = getMapLabelTable(columnIndex)->getLabel(labelKey); if (label != NULL) { cellValueOut = ("(key=" + AString::number(labelKey) + ")" + label->getName()); } else { cellValueOut = ("Invalid Key=" + AString::number(labelKey)); } return true; } return false; } /** * @return Is charting enabled for this file? */ bool CiftiParcelLabelFile::isMatrixChartingEnabled(const int32_t tabIndex) const { CaretAssertArrayIndex(m_chartingEnabledForTab, BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS, tabIndex); return m_chartingEnabledForTab[tabIndex]; } /** * @return Return true if the file's current state supports * charting data, else false. Typically a brainordinate file * is chartable if it contains more than one map. */ bool CiftiParcelLabelFile::isMatrixChartingSupported() const { return true; } /** * Set charting enabled for this file. * * @param enabled * New status for charting enabled. */ void CiftiParcelLabelFile::setMatrixChartingEnabled(const int32_t tabIndex, const bool enabled) { CaretAssertArrayIndex(m_chartingEnabledForTab, BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS, tabIndex); m_chartingEnabledForTab[tabIndex] = enabled; } /** * Get chart data types supported by the file. * * @param chartDataTypesOut * Chart types supported by this file. */ void CiftiParcelLabelFile::getSupportedMatrixChartDataTypes(std::vector& chartDataTypesOut) const { chartDataTypesOut.clear(); chartDataTypesOut.push_back(ChartDataTypeEnum::CHART_DATA_TYPE_MATRIX_LAYER); } /** * @return Chart matrix display properties (const method). */ const ChartMatrixDisplayProperties* CiftiParcelLabelFile::getChartMatrixDisplayProperties(const int32_t tabIndex) const { CaretAssertArrayIndex(m_chartMatrixDisplayProperties, BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS, tabIndex); return m_chartMatrixDisplayProperties[tabIndex]; } /** * @return Chart matrix display properties. */ ChartMatrixDisplayProperties* CiftiParcelLabelFile::getChartMatrixDisplayProperties(const int32_t tabIndex) { CaretAssertArrayIndex(m_chartMatrixDisplayProperties, BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS, tabIndex); return m_chartMatrixDisplayProperties[tabIndex]; } /** * Save file data from the scene. For subclasses that need to * save to a scene, this method should be overriden. sceneClass * will be valid and any scene data should be added to it. * * @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. */ void CiftiParcelLabelFile::saveFileDataToScene(const SceneAttributes* sceneAttributes, SceneClass* sceneClass) { CiftiMappableDataFile::saveFileDataToScene(sceneAttributes, sceneClass); m_sceneAssistant->saveMembers(sceneAttributes, sceneClass); sceneClass->addBooleanArray("m_chartingEnabledForTab", m_chartingEnabledForTab, BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS); /* * Save chart matrix properties */ SceneObjectMapIntegerKey* chartMatrixPropertiesMap = new SceneObjectMapIntegerKey("m_chartMatrixDisplayPropertiesMap", SceneObjectDataTypeEnum::SCENE_CLASS); const std::vector tabIndices = sceneAttributes->getIndicesOfTabsForSavingToScene(); for (std::vector::const_iterator tabIter = tabIndices.begin(); tabIter != tabIndices.end(); tabIter++) { const int32_t tabIndex = *tabIter; chartMatrixPropertiesMap->addClass(tabIndex, m_chartMatrixDisplayProperties[tabIndex]->saveToScene(sceneAttributes, "m_chartMatrixDisplayProperties")); } sceneClass->addChild(chartMatrixPropertiesMap); } /** * Restore file data from the scene. For subclasses that need to * restore from a scene, this method should be overridden. The scene class * will be valid and any scene data may be obtained from it. * * @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 CiftiParcelLabelFile::restoreFileDataFromScene(const SceneAttributes* sceneAttributes, const SceneClass* sceneClass) { CiftiMappableDataFile::restoreFileDataFromScene(sceneAttributes, sceneClass); m_sceneAssistant->restoreMembers(sceneAttributes, sceneClass); for (int32_t i = 0; i < BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS; i++) { m_chartingEnabledForTab[i] = false; } const ScenePrimitiveArray* tabArray = sceneClass->getPrimitiveArray("m_chartingEnabledForTab"); if (tabArray != NULL) { sceneClass->getBooleanArrayValue("m_chartingEnabledForTab", m_chartingEnabledForTab, BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS); } else { /* * Obsolete value when charting was not 'per tab' */ const bool chartingEnabled = sceneClass->getBooleanValue("m_chartingEnabled", false); for (int32_t i = 0; i < BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS; i++) { m_chartingEnabledForTab[i] = chartingEnabled; } } /* * Restore chart matrix properties */ const SceneObjectMapIntegerKey* chartMatrixPropertiesMap = sceneClass->getMapIntegerKey("m_chartMatrixDisplayPropertiesMap"); if (chartMatrixPropertiesMap != NULL) { const std::vector tabIndices = chartMatrixPropertiesMap->getKeys(); for (std::vector::const_iterator tabIter = tabIndices.begin(); tabIter != tabIndices.end(); tabIter++) { const int32_t tabIndex = *tabIter; const SceneClass* sceneClass = chartMatrixPropertiesMap->classValue(tabIndex); m_chartMatrixDisplayProperties[tabIndex]->restoreFromScene(sceneAttributes, sceneClass); } } } /** * @return Coloring mode for selected parcel. */ CiftiParcelColoringModeEnum::Enum CiftiParcelLabelFile::getSelectedParcelColoringMode() const { return m_selectedParcelColoringMode; } /** * Set the coloring mode for selected parcel. * * @param coloringMode * New value for coloring mode. */ void CiftiParcelLabelFile::setSelectedParcelColoringMode(const CiftiParcelColoringModeEnum::Enum coloringMode) { m_selectedParcelColoringMode = coloringMode; } /** * @return Color for selected parcel. */ CaretColorEnum::Enum CiftiParcelLabelFile::getSelectedParcelColor() const { return m_selectedParcelColor; } /** * Set color for selected parcel. * * @param color * New color for selected parcel. */ void CiftiParcelLabelFile::setSelectedParcelColor(const CaretColorEnum::Enum color) { m_selectedParcelColor = color; } /** * 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 CiftiParcelLabelFile::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 CiftiParcelLabelFile::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* CiftiParcelLabelFile::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 errorMessageOut * Error message output. Will only be non-empty if NULL is returned. * @return * Pointer to parcel reordering or NULL if not found. */ bool CiftiParcelLabelFile::createParcelReordering(const CiftiParcelLabelFile* parcelLabelFile, const int32_t parcelLabelFileMapIndex, AString& errorMessageOut) { return m_parcelReorderingModel->createParcelReordering(parcelLabelFile, parcelLabelFileMapIndex, errorMessageOut); } /** * @return True if loading attributes (column/row, yoking) are * supported by this file type. */ bool CiftiParcelLabelFile::isSupportsLoadingAttributes() { return false; } /** * @return The matrix loading type (by row/column). */ ChartMatrixLoadingDimensionEnum::Enum CiftiParcelLabelFile::getMatrixLoadingDimension() const { /* * This file supports loading by column only ! */ return ChartMatrixLoadingDimensionEnum::CHART_MATRIX_LOADING_BY_COLUMN; } /** * Set the matrix loading type (by row/column). * * @param matrixLoadingType * New value for matrix loading type. */ void CiftiParcelLabelFile::setMatrixLoadingDimension(const ChartMatrixLoadingDimensionEnum::Enum /* matrixLoadingType */) { CaretLogSevere("Attempting to change matrix loading type for a file that only supports loading by column"); } /** * @return Selected yoking group. */ YokingGroupEnum::Enum CiftiParcelLabelFile::getYokingGroup() const { /* not supported in this file */ return YokingGroupEnum::YOKING_GROUP_OFF; } /** * Set the selected yoking group. * * @param yokingGroup * New value for yoking group. */ void CiftiParcelLabelFile::setYokingGroup(const YokingGroupEnum::Enum /* yokingGroup */) { /* not supported in this file */ } /** * Reorder and map and return the matching parcel indices. * * @param mapIndex * Index of the map. * @param reorderedParcelIndicesOut * The parcel indices with reordering applied * @param errorMessageOut * Contains description of error. * @return * True if reordering successful, otherwise, false is returned * and errorMessageOut will contain a descrption of the error. */ bool CiftiParcelLabelFile::getReorderedParcelIndicesFromMap(const int32_t mapIndex, std::vector& reorderedParcelIndicesOut, AString& errorMessageOut) const { reorderedParcelIndicesOut.clear(); errorMessageOut.clear(); if ((mapIndex < 0) || (mapIndex >= getNumberOfMaps())) { errorMessageOut.appendWithNewLine("Invalid map index=" + AString::number(mapIndex)); } const int32_t numberOfRows = m_ciftiFile->getNumberOfRows(); if (numberOfRows <= 0) { errorMessageOut.appendWithNewLine("File contains no rows."); } if ( ! errorMessageOut.isEmpty()) { return false; } std::vector columnData(numberOfRows); m_ciftiFile->getColumn(&columnData[0], mapIndex); CaretLogFine("Column values size=" + AString::number(columnData.size()) + ": " + AString::fromNumbers(columnData, ",")); std::vector indexProcessed(numberOfRows, false); /* * Reorder row indices so that identical values are grouped together */ for (int32_t iRow = 0; iRow < numberOfRows; iRow++) { if ( ! indexProcessed[iRow]) { reorderedParcelIndicesOut.push_back(iRow); indexProcessed[iRow] = true; const int32_t valueI = columnData[iRow]; for (int32_t jRow = (iRow + 1); jRow < numberOfRows; jRow++) { if ( ! indexProcessed[jRow]) { if (valueI == static_cast(columnData[jRow])) { reorderedParcelIndicesOut.push_back(jRow); indexProcessed[jRow] = true; } } } } } if (reorderedParcelIndicesOut.empty()) { errorMessageOut.appendWithNewLine("No parcel indices for reordering."); return false; } CaretLogFine("Reordered parcel indices (not values!) size=" + AString::number(reorderedParcelIndicesOut.size()) + ": " + AString::fromNumbers(reorderedParcelIndicesOut, ",")); return true; } connectome-workbench-1.2.3+git41-gc4c6c90/src/Files/CiftiParcelLabelFile.h000066400000000000000000000143431300200146000257110ustar00rootroot00000000000000#ifndef __CIFTI_PARCEL_LABEL_FILE_H__ #define __CIFTI_PARCEL_LABEL_FILE_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 "BrainConstants.h" #include "CiftiMappableDataFile.h" #include "ChartableMatrixParcelInterface.h" namespace caret { class CiftiParcelReorderingModel; class SceneClassAssistant; class CiftiParcelLabelFile : public CiftiMappableDataFile, public ChartableMatrixParcelInterface { public: CiftiParcelLabelFile(); virtual ~CiftiParcelLabelFile(); virtual void getMatrixDimensions(int32_t& numberOfRowsOut, int32_t& numberOfColumnsOut) const; virtual bool getMatrixDataRGBA(int32_t& numberOfRowsOut, int32_t& numberOfColumnsOut, std::vector& rgbaOut) const; virtual bool getMatrixCellAttributes(const int32_t rowIndex, const int32_t columnIndex, AString& cellValueOut, AString& rowNameOut, AString& columnNameOut) const; virtual bool isMatrixChartingEnabled(const int32_t tabIndex) const; virtual bool isMatrixChartingSupported() const; virtual void setMatrixChartingEnabled(const int32_t tabIndex, const bool enabled); virtual void getSupportedMatrixChartDataTypes(std::vector& chartDataTypesOut) const; const ChartMatrixDisplayProperties* getChartMatrixDisplayProperties(const int32_t tabIndex) const; ChartMatrixDisplayProperties* getChartMatrixDisplayProperties(const int32_t tabIndex); virtual CiftiParcelColoringModeEnum::Enum getSelectedParcelColoringMode() const; virtual void setSelectedParcelColoringMode(const CiftiParcelColoringModeEnum::Enum coloringMode); virtual CaretColorEnum::Enum getSelectedParcelColor() const; virtual void setSelectedParcelColor(const CaretColorEnum::Enum color); 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; virtual bool isSupportsLoadingAttributes(); virtual ChartMatrixLoadingDimensionEnum::Enum getMatrixLoadingDimension() const; virtual void setMatrixLoadingDimension(const ChartMatrixLoadingDimensionEnum::Enum matrixLoadingType); virtual YokingGroupEnum::Enum getYokingGroup() const; virtual void setYokingGroup(const YokingGroupEnum::Enum yokingType); bool getReorderedParcelIndicesFromMap(const int32_t mapIndex, std::vector& reorderedParcelIndicesOut, AString& errorMessageOut) const; // ADD_NEW_METHODS_HERE protected: virtual void saveFileDataToScene(const SceneAttributes* sceneAttributes, SceneClass* sceneClass); virtual void restoreFileDataFromScene(const SceneAttributes* sceneAttributes, const SceneClass* sceneClass); private: CiftiParcelLabelFile(const CiftiParcelLabelFile&); CiftiParcelLabelFile& operator=(const CiftiParcelLabelFile&); SceneClassAssistant* m_sceneAssistant; bool m_chartingEnabledForTab[BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS]; ChartMatrixDisplayProperties* m_chartMatrixDisplayProperties[BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS]; CiftiParcelColoringModeEnum::Enum m_selectedParcelColoringMode; CaretColorEnum::Enum m_selectedParcelColor; CiftiParcelReorderingModel* m_parcelReorderingModel; // ADD_NEW_MEMBERS_HERE }; #ifdef __CIFTI_PARCEL_LABEL_FILE_DECLARE__ // #endif // __CIFTI_PARCEL_LABEL_FILE_DECLARE__ } // namespace #endif //__CIFTI_PARCEL_LABEL_FILE_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Files/CiftiParcelReordering.cxx000066400000000000000000000426111300200146000265440ustar00rootroot00000000000000 /*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 __CIFTI_PARCEL_REORDERING_DECLARE__ #include "CiftiParcelReordering.h" #undef __CIFTI_PARCEL_REORDERING_DECLARE__ #include "CaretAssert.h" #include "CiftiParcelLabelFile.h" #include "CiftiParcelsMap.h" #include "ElapsedTimer.h" #include "GiftiLabel.h" #include "GiftiLabelTable.h" #include "SceneClass.h" #include "ScenePrimitiveArray.h" #include "SceneClassAssistant.h" using namespace caret; /** * \class caret::CiftiParcelReordering * \brief Contains a parcel reordering. * \ingroup Files */ /** * Constructs an invalid instance. */ CiftiParcelReordering::CiftiParcelReordering() : CaretObject() { m_sourceParcelLabelFile = NULL; m_sourceParcelLabelFileMapIndex = -1; m_sceneAssistant = new SceneClassAssistant(); m_sceneAssistant->add("m_sourceParcelLabelFileMapIndex", &m_sourceParcelLabelFileMapIndex); } /** * Destructor. */ CiftiParcelReordering::~CiftiParcelReordering() { delete m_sceneAssistant; } /** * Copy constructor. * @param obj * Object that is copied. */ CiftiParcelReordering::CiftiParcelReordering(const CiftiParcelReordering& obj) : CaretObject(obj), SceneableInterface(obj) { this->copyHelperCiftiParcelReordering(obj); } /** * Assignment operator. * @param obj * Data copied from obj to this. * @return * Reference to this object. */ CiftiParcelReordering& CiftiParcelReordering::operator=(const CiftiParcelReordering& obj) { if (this != &obj) { CaretObject::operator=(obj); this->copyHelperCiftiParcelReordering(obj); } return *this; } /** * Helps with copying an object of this type. * @param obj * Object that is copied. */ void CiftiParcelReordering::copyHelperCiftiParcelReordering(const CiftiParcelReordering& obj) { m_sourceParcelLabelFile = obj.m_sourceParcelLabelFile; m_sourceParcelLabelFileMapIndex = obj.m_sourceParcelLabelFileMapIndex; m_reorderedParcelIndices = obj.m_reorderedParcelIndices; } /** * Get a description of this object's content. * @return String describing this object's content. */ AString CiftiParcelReordering::toString() const { return "CiftiParcelReordering"; } /** * Equality operator that compares this instance to another instance. * * @param parcelReordering * Compared to "this" * @return * True if this instance and the other instance are "equal", else false. */ bool CiftiParcelReordering::operator==(const CiftiParcelReordering& obj) const { if ((m_sourceParcelLabelFile == obj.m_sourceParcelLabelFile) && (m_sourceParcelLabelFileMapIndex == obj.m_sourceParcelLabelFileMapIndex)) { return true; } return false; } /** * Does this parcel reordering key match? * * @param sourceParcelLabelFile * The source parcel label file for the reordering. * @param sourceParcelLabelFileMapIndex * The map index from the source parcel map file. * @return * True if this parcel reordering is for the given file and map indices, * else false. */ bool CiftiParcelReordering::isMatch(const CiftiParcelLabelFile* sourceParcelLabelFile, const int32_t sourceParcelLabelFileMapIndex) const { if ((sourceParcelLabelFile == m_sourceParcelLabelFile) && (sourceParcelLabelFileMapIndex == m_sourceParcelLabelFileMapIndex)) { return true; } return false; } /** * @ Is this parcel reordering valid? */ bool CiftiParcelReordering::isValid() const { if (m_reorderedParcelIndices.empty()) { return false; } return true; } ///** // * Create the parcel reordering. // * // * @param sourceParcelLabelFile // * Parcel label file used to create the reordering // * @param sourceParcelLabelFileMapIndex // * Index of map in parcel label file used to create reordering // * @param targetParcelsMap // * Parcels map for which a reordering is created. // * @param errorMessageOut // * Error message if there was a problem creating the // * reordering. // * @return // * True if the reordering was successfully created, else false. // */ //bool //CiftiParcelReordering::createReordering(const CiftiParcelLabelFile* sourceParcelLabelFile, // const int32_t sourceParcelLabelFileMapIndex, // const CiftiParcelsMap& targetParcelsMap, // AString& errorMessageOut) //{ // m_reorderedParcelIndices.clear(); // // errorMessageOut = ""; // // if (sourceParcelLabelFile != NULL) { // if ((sourceParcelLabelFileMapIndex < 0) // || (sourceParcelLabelFileMapIndex >= sourceParcelLabelFile->getNumberOfMaps())) { // errorMessageOut.appendWithNewLine("Source Parcel Label File map index=" // + AString::number(sourceParcelLabelFileMapIndex) // + " is invalid."); // } // } // else { // errorMessageOut.appendWithNewLine("Source Parcel Label File is invalid."); // } // // /* // * The target parcels are those whose indices will be reordered // */ // const std::vector& allTargetParcels = targetParcelsMap.getParcels(); // const int32_t numTargetParcels = static_cast(allTargetParcels.size()); // if (numTargetParcels <= 0) { // errorMessageOut.appendWithNewLine("Parcels map that is to be reordered is empty."); // } // // /* // * These source parcels are those used to create the reordering // */ // const CiftiParcelsMap* sourceParcelsMap = sourceParcelLabelFile->getCiftiParcelsMapForBrainordinateMapping(); // CaretAssert(sourceParcelsMap); // const std::vector& allSourceParcels = sourceParcelsMap->getParcels(); // const int32_t numSourceParcels = static_cast(allSourceParcels.size()); // if (numSourceParcels <= 0) { // errorMessageOut.appendWithNewLine("Parcels map from that Parcel Label File is used to reorder the parcels is empty."); // } // // if ( ! errorMessageOut.isEmpty()) { // return false; // } // // /* // * Tracks parcels that are in the reordering so that they can be // * skipped and save time by avoiding parcel comparisons // */ // std::vector targetParcelRemapped(numTargetParcels, // false); // // /* // * Loop through the source parcels used to create the reordering // */ // for (int32_t iSource = 0; // iSource < numSourceParcels; // iSource++) { // CaretAssertVectorIndex(allSourceParcels, // iSource); // const CiftiParcelsMap::Parcel& sourceParcel = allSourceParcels[iSource]; // // /* // * Loop through target parcels to find those that // * have the exact same mapping by using the // * parcel equality operator. // */ // for (int32_t iTarget = 0; // iTarget < numTargetParcels; // iTarget++) { // CaretAssertVectorIndex(targetParcelRemapped, iTarget); // if ( ! targetParcelRemapped[iTarget]) { // CaretAssertVectorIndex(allTargetParcels, iTarget); // if (allTargetParcels[iTarget] == sourceParcel) { // m_reorderedParcelIndices.push_back(iTarget); // // targetParcelRemapped[iTarget] = true; // } // } // } // } // // if (m_reorderedParcelIndices.empty()) { // return false; // } // // m_sourceParcelLabelFile = const_cast(sourceParcelLabelFile); // m_sourceParcelLabelFileMapIndex = sourceParcelLabelFileMapIndex; // // std::cout << "New parcel reorder: "; // for (std::vector::iterator iter = m_reorderedParcelIndices.begin(); // iter != m_reorderedParcelIndices.end(); // iter++) { // std::cout << " " << *iter; // } // std::cout << std::endl; // // return true; //} /** * Create the parcel reordering. * * @param sourceParcelLabelFile * Parcel label file used to create the reordering * @param sourceParcelLabelFileMapIndex * Index of map in parcel label file used to create reordering * @param targetParcelsMap * Parcels map for which a reordering is created. * @param errorMessageOut * Error message if there was a problem creating the * reordering. * @return * True if the reordering was successfully created, else false. */ bool CiftiParcelReordering::createReordering(const CiftiParcelLabelFile* sourceParcelLabelFile, const int32_t sourceParcelLabelFileMapIndex, const CiftiParcelsMap& targetParcelsMap, AString& errorMessageOut) { ElapsedTimer timer; timer.start(); m_reorderedParcelIndices.clear(); errorMessageOut = ""; if (sourceParcelLabelFile != NULL) { if ((sourceParcelLabelFileMapIndex < 0) || (sourceParcelLabelFileMapIndex >= sourceParcelLabelFile->getNumberOfMaps())) { errorMessageOut.appendWithNewLine("Source Parcel Label File map index=" + AString::number(sourceParcelLabelFileMapIndex) + " is invalid."); } } else { errorMessageOut.appendWithNewLine("Source Parcel Label File is invalid."); } /* * The target parcels are those whose indices will be reordered */ const std::vector& allTargetParcels = targetParcelsMap.getParcels(); const int32_t numTargetParcels = static_cast(allTargetParcels.size()); if (numTargetParcels <= 0) { errorMessageOut.appendWithNewLine("Parcels map that is to be reordered is empty."); } /* * These source's parcels map. */ const CiftiParcelsMap* sourceParcelsMap = sourceParcelLabelFile->getCiftiParcelsMapForBrainordinateMapping(); if (sourceParcelsMap == NULL) { errorMessageOut.appendWithNewLine("No parcels map in source Parcel Label File."); } if ( ! errorMessageOut.isEmpty()) { return false; } /* * Reorder the indices for the Parcels Map in the selected map * of the Parcel Labels File. */ std::vector reorderedSourceParcelIndices; if ( ! sourceParcelLabelFile->getReorderedParcelIndicesFromMap(sourceParcelLabelFileMapIndex, reorderedSourceParcelIndices, errorMessageOut)) { return false; } const std::vector& allSourceParcels = sourceParcelsMap->getParcels(); const int32_t numberOfSourceParcels = static_cast(allSourceParcels.size()); if (numberOfSourceParcels <= 0) { errorMessageOut.appendWithNewLine("No parcels in source Parcel Label File."); return false; } /* * One a target parcel is remapped, it no never needs to * be tested again. */ std::vector targetParcelRemapped(numTargetParcels, false); /* * Loop through the source parcels used to create the reordering */ const int32_t numReorderedSourceParcelIndices = static_cast(reorderedSourceParcelIndices.size()); for (int32_t iSource = 0; iSource < numReorderedSourceParcelIndices; iSource++) { CaretAssertVectorIndex(reorderedSourceParcelIndices, iSource); const int32_t sourceParcelIndex = reorderedSourceParcelIndices[iSource]; CaretAssertVectorIndex(allSourceParcels, sourceParcelIndex); const CiftiParcelsMap::Parcel& sourceParcel = allSourceParcels[sourceParcelIndex]; /* * Loop through target parcels to find those that * have the exact same mapping by using the * parcel's approximate match method which * compares the brainordinate mapping of the parcels. */ for (int32_t iTarget = 0; iTarget < numTargetParcels; iTarget++) { CaretAssertVectorIndex(targetParcelRemapped, iTarget); if ( ! targetParcelRemapped[iTarget]) { CaretAssertVectorIndex(allTargetParcels, iTarget); if (allTargetParcels[iTarget].approximateMatch(sourceParcel)) { m_reorderedParcelIndices.push_back(iTarget); targetParcelRemapped[iTarget] = true; } } } } const int32_t matchCount = std::count(targetParcelRemapped.begin(), targetParcelRemapped.end(), true); const int32_t notMatchCount = numTargetParcels - matchCount; if (notMatchCount > 0) { errorMessageOut.appendWithNewLine("Failed to match " + AString::number(notMatchCount) + " of " + AString::number(numTargetParcels)); return false; } //std::cout << "Time to match parcels was: " << timer.getElapsedTimeSeconds() << " seconds." << std::endl; m_sourceParcelLabelFile = const_cast(sourceParcelLabelFile); m_sourceParcelLabelFileMapIndex = sourceParcelLabelFileMapIndex; return true; } /** * @return The reordered indices of the parcels. */ std::vector CiftiParcelReordering::getReorderedParcelIndices() const { return m_reorderedParcelIndices; } /** * 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* CiftiParcelReordering::saveToScene(const SceneAttributes* sceneAttributes, const AString& instanceName) { SceneClass* sceneClass = new SceneClass(instanceName, "CiftiParcelReordering", 1); m_sceneAssistant->saveMembers(sceneAttributes, sceneClass); if (m_sourceParcelLabelFile != NULL) { sceneClass->addPathName("m_sourceParcelLabelFile", m_sourceParcelLabelFile->getFileName()); if ( ! m_reorderedParcelIndices.empty()) { sceneClass->addIntegerArray("m_reorderedParcelIndices", &m_reorderedParcelIndices[0], m_reorderedParcelIndices.size()); } } // 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 CiftiParcelReordering::restoreFromScene(const SceneAttributes* sceneAttributes, const SceneClass* sceneClass) { if (sceneClass == NULL) { return; } m_sourceParcelLabelFile = NULL; m_reorderedParcelIndices.clear(); m_sceneAssistant->restoreMembers(sceneAttributes, sceneClass); const ScenePathName* parcelLabelPathName = sceneClass->getPathName("m_sourceParcelLabelFile"); if (parcelLabelPathName != NULL) { } const ScenePrimitiveArray* reorderArray = sceneClass->getPrimitiveArray("m_reorderedParcelIndices"); if (reorderArray != NULL) { const int32_t numberOfElements = reorderArray->getNumberOfArrayElements(); if (numberOfElements > 0) { m_reorderedParcelIndices.reserve(numberOfElements); for (int32_t i = 0; i < numberOfElements; i++) { m_reorderedParcelIndices.push_back(reorderArray->integerValue(i)); } } } //Uncomment if sub-classes must restore from scene //restoreSubClassDataFromScene(sceneAttributes, // sceneClass); } connectome-workbench-1.2.3+git41-gc4c6c90/src/Files/CiftiParcelReordering.h000066400000000000000000000075101300200146000261700ustar00rootroot00000000000000#ifndef __CIFTI_PARCEL_REORDERING_H__ #define __CIFTI_PARCEL_REORDERING_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 "SceneableInterface.h" namespace caret { class CiftiParcelLabelFile; class CiftiParcelsMap; class SceneClassAssistant; class CiftiParcelReordering : public CaretObject, public SceneableInterface { public: CiftiParcelReordering(); virtual ~CiftiParcelReordering(); CiftiParcelReordering(const CiftiParcelReordering& obj); CiftiParcelReordering& operator=(const CiftiParcelReordering& obj); bool operator==(const CiftiParcelReordering& obj) const; bool isValid() const; bool isMatch(const CiftiParcelLabelFile* sourceParcelLabelFile, const int32_t sourceParcelLabelFileMapIndex) const; bool createReordering(const CiftiParcelLabelFile* sourceParcelLabelFile, const int32_t sourceParcelLabelFileMapIndex, const CiftiParcelsMap& targetParcelsMap, AString& errorMessageOut); std::vector getReorderedParcelIndices() 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 implemetation 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 copyHelperCiftiParcelReordering(const CiftiParcelReordering& obj); SceneClassAssistant* m_sceneAssistant; /** * Parcel label file used to create the reordering */ CiftiParcelLabelFile* m_sourceParcelLabelFile; /** * Index of map in parcel label file used to create reordering */ int32_t m_sourceParcelLabelFileMapIndex; /** * Reordered parcel indices in data file that has its parcel's * reordered. */ std::vector m_reorderedParcelIndices; // ADD_NEW_MEMBERS_HERE }; #ifdef __CIFTI_PARCEL_REORDERING_DECLARE__ // #endif // __CIFTI_PARCEL_REORDERING_DECLARE__ } // namespace #endif //__CIFTI_PARCEL_REORDERING_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Files/CiftiParcelReorderingModel.cxx000066400000000000000000000464601300200146000275330ustar00rootroot00000000000000 /*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 __CIFTI_PARCEL_REORDERING_MODEL_DECLARE__ #include "CiftiParcelReorderingModel.h" #undef __CIFTI_PARCEL_REORDERING_MODEL_DECLARE__ #include "CaretAssert.h" #include "CiftiMappableDataFile.h" #include "CiftiParcelLabelFile.h" #include "CiftiParcelReordering.h" #include "CiftiParcelsMap.h" #include "CiftiXML.h" #include "EventCaretMappableDataFilesGet.h" #include "EventManager.h" #include "SceneClass.h" #include "SceneClassAssistant.h" #include "ScenePathName.h" using namespace caret; /** * \class caret::CiftiParcelReorderingModel * \brief Controls reordering of parcels for a CIFTI data file. * \ingroup Files */ /** * Constructor. */ CiftiParcelReorderingModel::CiftiParcelReorderingModel(const CiftiMappableDataFile* parentCiftiMappableDataFile) : CaretObject(), m_parentCiftiMappableDataFile(parentCiftiMappableDataFile) { CaretAssert(parentCiftiMappableDataFile); switch (parentCiftiMappableDataFile->getDataFileType()) { case DataFileTypeEnum::CONNECTIVITY_PARCEL: case DataFileTypeEnum::CONNECTIVITY_PARCEL_SCALAR: case DataFileTypeEnum::CONNECTIVITY_PARCEL_LABEL: break; default: CaretAssert(0); } m_parcelReorderingEnabledStatus = false; m_selectedParcelLabelFile = NULL; m_selectedParcelLabelFileMapIndex = -1; m_sceneAssistant = new SceneClassAssistant(); m_sceneAssistant->add("m_selectedParcelLabelFileMapIndex", &m_selectedParcelLabelFileMapIndex); m_sceneAssistant->add("m_parcelReorderingEnabledStatus", &m_parcelReorderingEnabledStatus); } /** * Destructor. */ CiftiParcelReorderingModel::~CiftiParcelReorderingModel() { delete m_sceneAssistant; for (std::vector::iterator iter = m_parcelReordering.begin(); iter != m_parcelReordering.end(); iter++) { delete *iter; } m_parcelReordering.clear(); } /** * Copy constructor. * @param obj * Object that is copied. */ CiftiParcelReorderingModel::CiftiParcelReorderingModel(const CiftiParcelReorderingModel& obj) : CaretObject(obj), SceneableInterface(obj) { this->copyHelperCiftiParcelReorderingModel(obj); } /** * Assignment operator. * @param obj * Data copied from obj to this. * @return * Reference to this object. */ CiftiParcelReorderingModel& CiftiParcelReorderingModel::operator=(const CiftiParcelReorderingModel& obj) { if (this != &obj) { CaretObject::operator=(obj); this->copyHelperCiftiParcelReorderingModel(obj); } return *this; } /** * Helps with copying an object of this type. * @param obj * Object that is copied. */ void CiftiParcelReorderingModel::copyHelperCiftiParcelReorderingModel(const CiftiParcelReorderingModel& obj) { std::vector compatibleParcelLabelFiles; CiftiParcelLabelFile* selectedParcelLabelFile; int32_t selectedParcelLabelFileMapIndex; bool enabledStatus; obj.getSelectedParcelLabelFileAndMapForReordering(compatibleParcelLabelFiles, selectedParcelLabelFile, selectedParcelLabelFileMapIndex, enabledStatus); setSelectedParcelLabelFileAndMapForReordering(selectedParcelLabelFile, selectedParcelLabelFileMapIndex, enabledStatus); } /** * (1) Since the user may load/unload files at will, any current * selection requests will need to validate against available * parcel label files. * * (2) The parcel label files must test for compatibility by * matching their CiftiParcelMaps. * * @return Parcel label files that contain at least one map and * contain compatible CiftiParcelMaps. */ std::vector CiftiParcelReorderingModel::getParcelLabelFiles() const { EventCaretMappableDataFilesGet parcelLabelFilesEvent(DataFileTypeEnum::CONNECTIVITY_PARCEL_LABEL); EventManager::get()->sendEvent(parcelLabelFilesEvent.getPointer()); std::vector mappableFiles; parcelLabelFilesEvent.getAllFiles(mappableFiles); std::vector parcelLabelFiles; for (std::vector::iterator iter = mappableFiles.begin(); iter != mappableFiles.end(); iter++) { CaretMappableDataFile* cmdf = *iter; if (cmdf->getNumberOfMaps() > 0) { CiftiParcelLabelFile* parcelLabelFile = dynamic_cast(*iter); CaretAssert(parcelLabelFile); std::map::iterator fileStatusIter = m_parcelLabelFileCompatibilityStatus.find(parcelLabelFile); if (fileStatusIter != m_parcelLabelFileCompatibilityStatus.end()) { const bool compatibleFlag = fileStatusIter->second; if (compatibleFlag) { /* * File was previously verified for parcel compatibility. */ parcelLabelFiles.push_back(parcelLabelFile); } } else { const CiftiParcelsMap* parcelsMap = parcelLabelFile->getCiftiParcelsMapForDirection(CiftiXML::ALONG_COLUMN); CaretAssert(parcelsMap); bool testAlongRow = false; bool testAlongColumn = false; switch (m_parentCiftiMappableDataFile->getDataFileType()) { case DataFileTypeEnum::CONNECTIVITY_PARCEL: testAlongColumn = true; testAlongRow = true; break; case DataFileTypeEnum::CONNECTIVITY_PARCEL_SCALAR: testAlongColumn = true; break; case DataFileTypeEnum::CONNECTIVITY_PARCEL_LABEL: testAlongColumn = true; break; default: CaretAssert(0); } int32_t passCount = 0; int32_t failCount = 0; if (testAlongColumn) { if (m_parentCiftiMappableDataFile->getCiftiParcelsMapForDirection(CiftiXML::ALONG_COLUMN)->approximateMatch(*parcelsMap)) { ++passCount; } else { ++failCount; } } if (testAlongRow) { if (m_parentCiftiMappableDataFile->getCiftiParcelsMapForDirection(CiftiXML::ALONG_ROW)->approximateMatch(*parcelsMap)) { ++passCount; } else { ++failCount; } } bool compatibleFlag = false; if ((passCount > 0) && (failCount <= 0)) { parcelLabelFiles.push_back(parcelLabelFile); /* * NOTE: Since file was found to be compatible it will * always be compatible. */ compatibleFlag = true; } /* * NOTE: Compatiblity status will never change so cache it * to avoid retesting. */ m_parcelLabelFileCompatibilityStatus.insert(std::make_pair(parcelLabelFile, compatibleFlag)); } } } return parcelLabelFiles; } /** * Validate the selected parcel label file and map. * * @param optionalParcelLabelFilesOut * If not NULL, the matching parcel labels files are inserted into this. */ void CiftiParcelReorderingModel::validateSelectedParcelLabelFileAndMap(std::vector* optionalParcelLabelFilesOut) const { std::vector parcelLabelFiles = getParcelLabelFiles(); bool foundFile = false; for (std::vector::iterator iter = parcelLabelFiles.begin(); iter != parcelLabelFiles.end(); iter++) { CiftiParcelLabelFile* plf = *iter; if (m_selectedParcelLabelFile != NULL) { if (m_selectedParcelLabelFile == plf) { foundFile = true; break; } } } if (foundFile) { const int32_t numMaps = m_selectedParcelLabelFile->getNumberOfMaps(); if (m_selectedParcelLabelFileMapIndex >= numMaps) { m_selectedParcelLabelFileMapIndex = numMaps - 1; } else if (m_selectedParcelLabelFileMapIndex < 0) { m_selectedParcelLabelFileMapIndex = 0; } } if ( ! foundFile) { if ( ! parcelLabelFiles.empty()) { m_selectedParcelLabelFile = parcelLabelFiles[0]; m_selectedParcelLabelFileMapIndex = 0; } else { m_selectedParcelLabelFile = NULL; m_selectedParcelLabelFileMapIndex = -1; m_parcelReorderingEnabledStatus = false; } } if (optionalParcelLabelFilesOut != NULL) { *optionalParcelLabelFilesOut = parcelLabelFiles; } } /** * Get a description of this object's content. * @return String describing this object's content. */ AString CiftiParcelReorderingModel::toString() const { return "CiftiParcelReorderingModel"; } /** * Get the selected parcel label file used for reordering of parcels. * * @param parcelLabelFilesOut * The compatible parcel label files. * @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 CiftiParcelReorderingModel::getSelectedParcelLabelFileAndMapForReordering(std::vector& parcelLabelFilesOut, CiftiParcelLabelFile* &selectedParcelLabelFileOut, int32_t& selectedParcelLabelFileMapIndexOut, bool& enabledStatusOut) const { validateSelectedParcelLabelFileAndMap(&parcelLabelFilesOut); selectedParcelLabelFileOut = m_selectedParcelLabelFile; selectedParcelLabelFileMapIndexOut = m_selectedParcelLabelFileMapIndex; enabledStatusOut = m_parcelReorderingEnabledStatus; } /** * 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 CiftiParcelReorderingModel::setSelectedParcelLabelFileAndMapForReordering(CiftiParcelLabelFile* selectedParcelLabelFile, const int32_t selectedParcelLabelFileMapIndex, const bool enabledStatus) { m_selectedParcelLabelFile = selectedParcelLabelFile; m_selectedParcelLabelFileMapIndex = selectedParcelLabelFileMapIndex; m_parcelReorderingEnabledStatus = 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* CiftiParcelReorderingModel::getParcelReordering(const CiftiParcelLabelFile* parcelLabelFile, const int32_t parcelLabelFileMapIndex) const { for (std::vector::const_iterator iter = m_parcelReordering.begin(); iter != m_parcelReordering.end(); iter++) { const CiftiParcelReordering* parcelReordering = *iter; if (parcelReordering->isMatch(parcelLabelFile, parcelLabelFileMapIndex)) { return parcelReordering; } } return NULL; } /** * 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. */ CiftiParcelReordering* CiftiParcelReorderingModel::getParcelReordering(const CiftiParcelLabelFile* parcelLabelFile, const int32_t parcelLabelFileMapIndex) { for (std::vector::iterator iter = m_parcelReordering.begin(); iter != m_parcelReordering.end(); iter++) { CiftiParcelReordering* parcelReordering = *iter; if (parcelReordering->isMatch(parcelLabelFile, parcelLabelFileMapIndex)) { return parcelReordering; } } return NULL; } /** * Create the parcel reordering for the parent file's parcels 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 errorMessageOut * Error message output. Will only be non-empty if NULL is returned. * @return * Pointer to parcel reordering or NULL if not found. */ bool CiftiParcelReorderingModel::createParcelReordering(const CiftiParcelLabelFile* parcelLabelFile, const int32_t parcelLabelFileMapIndex, AString& errorMessageOut) { if (getParcelReordering(parcelLabelFile, parcelLabelFileMapIndex) != NULL) { return true; } const CiftiParcelsMap* ciftiParcelsMap = m_parentCiftiMappableDataFile->getCiftiParcelsMapForBrainordinateMapping(); CaretAssert(ciftiParcelsMap); CiftiParcelReordering* parcelReordering = new CiftiParcelReordering(); if (parcelReordering->createReordering(parcelLabelFile, parcelLabelFileMapIndex, *ciftiParcelsMap, errorMessageOut)) { m_parcelReordering.push_back(parcelReordering); return true; } 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* CiftiParcelReorderingModel::saveToScene(const SceneAttributes* sceneAttributes, const AString& instanceName) { validateSelectedParcelLabelFileAndMap(NULL); SceneClass* sceneClass = new SceneClass(instanceName, "CiftiParcelReorderingModel", 1); m_sceneAssistant->saveMembers(sceneAttributes, sceneClass); if (m_selectedParcelLabelFile != NULL) { sceneClass->addPathName("m_selectedParcelLabelFile", m_selectedParcelLabelFile->getFileName()); } /* * NOTE: the parcel reorderings are not saved to the scene. * When the scene is restored, a parcel reordering is created * for the restored parcel label file and map index. */ // 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 CiftiParcelReorderingModel::restoreFromScene(const SceneAttributes* sceneAttributes, const SceneClass* sceneClass) { if (sceneClass == NULL) { return; } m_sceneAssistant->restoreMembers(sceneAttributes, sceneClass); m_selectedParcelLabelFile = NULL; m_parcelReordering.clear(); const AString parcelLabelFileName = sceneClass->getPathNameValue("m_selectedParcelLabelFile"); if ( ! parcelLabelFileName.isEmpty()) { std::vector parcelLabelFiles = getParcelLabelFiles(); for (std::vector::iterator iter = parcelLabelFiles.begin(); iter != parcelLabelFiles.end(); iter++) { CiftiParcelLabelFile* plf = *iter; if (plf->getFileName() == parcelLabelFileName) { m_selectedParcelLabelFile = plf; break; } } } validateSelectedParcelLabelFileAndMap(NULL); /* * If there is a valid selected parcel label file, * create the reordering for it. */ if (m_selectedParcelLabelFile != NULL) { if (m_selectedParcelLabelFileMapIndex >= 0) { AString errorMessage; if ( ! createParcelReordering(m_selectedParcelLabelFile, m_selectedParcelLabelFileMapIndex, errorMessage)) { sceneAttributes->addToErrorMessage(errorMessage); } } } //Uncomment if sub-classes must restore from scene //restoreSubClassDataFromScene(sceneAttributes, // sceneClass); } connectome-workbench-1.2.3+git41-gc4c6c90/src/Files/CiftiParcelReorderingModel.h000066400000000000000000000117061300200146000271530ustar00rootroot00000000000000#ifndef __CIFTI_PARCEL_REORDERING_MODEL_H__ #define __CIFTI_PARCEL_REORDERING_MODEL_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 "CaretObject.h" #include "SceneableInterface.h" namespace caret { class CiftiMappableDataFile; class CiftiParcelLabelFile; class CiftiParcelReordering; class CiftiParcelsMap; class SceneClassAssistant; class CiftiParcelReorderingModel : public CaretObject, public SceneableInterface { public: CiftiParcelReorderingModel(const CiftiMappableDataFile* ciftiMappableDataFile); virtual ~CiftiParcelReorderingModel(); CiftiParcelReorderingModel(const CiftiParcelReorderingModel& obj); CiftiParcelReorderingModel& operator=(const CiftiParcelReorderingModel& obj); void getSelectedParcelLabelFileAndMapForReordering(std::vector& parcelLabelFilesOut, CiftiParcelLabelFile* &selectedParcelLabelFileOut, int32_t& selectedParcelLabelFileMapIndexOut, bool& enabledStatusOut) const; void setSelectedParcelLabelFileAndMapForReordering(CiftiParcelLabelFile* selectedParcelLabelFile, const int32_t selectedParcelLabelFileMapIndex, const bool enabledStatus); bool createParcelReordering(const CiftiParcelLabelFile* parcelLabelFile, const int32_t parcelLabelFileMapIndex, AString& errorMessageOut); const CiftiParcelReordering* getParcelReordering(const CiftiParcelLabelFile* parcelLabelFile, const int32_t parcelLabelFileMapIndex) const; CiftiParcelReordering* getParcelReordering(const CiftiParcelLabelFile* parcelLabelFile, const int32_t parcelLabelFileMapIndex); // 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 implemetation 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 copyHelperCiftiParcelReorderingModel(const CiftiParcelReorderingModel& obj); void validateSelectedParcelLabelFileAndMap(std::vector* optionalParcelLabelFilesOut) const; std::vector getParcelLabelFiles() const; mutable std::map m_parcelLabelFileCompatibilityStatus; const CiftiMappableDataFile* m_parentCiftiMappableDataFile; SceneClassAssistant* m_sceneAssistant; std::vector m_parcelReordering; mutable CiftiParcelLabelFile* m_selectedParcelLabelFile; mutable int32_t m_selectedParcelLabelFileMapIndex; mutable bool m_parcelReorderingEnabledStatus; // ADD_NEW_MEMBERS_HERE }; #ifdef __CIFTI_PARCEL_REORDERING_MODEL_DECLARE__ // #endif // __CIFTI_PARCEL_REORDERING_MODEL_DECLARE__ } // namespace #endif //__CIFTI_PARCEL_REORDERING_MODEL_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Files/CiftiParcelScalarFile.cxx000066400000000000000000000637741300200146000264660ustar00rootroot00000000000000 /*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 __CIFTI_PARCEL_SCALAR_FILE_DECLARE__ #include "CiftiParcelScalarFile.h" #undef __CIFTI_PARCEL_SCALAR_FILE_DECLARE__ #include "CaretLogger.h" #include "ChartDataCartesian.h" #include "ChartMatrixDisplayProperties.h" #include "CiftiFile.h" #include "CiftiParcelReordering.h" #include "CiftiParcelReorderingModel.h" #include "CiftiXML.h" #include "FastStatistics.h" #include "NodeAndVoxelColoring.h" #include "Palette.h" #include "PaletteColorMapping.h" #include "PaletteFile.h" #include "SceneClass.h" #include "SceneClassArray.h" #include "SceneClassAssistant.h" using namespace caret; /** * \class caret::CiftiParcelScalarFile * \brief CIFTI Parcel by Scalar File * \ingroup Files * */ /** * Constructor. */ CiftiParcelScalarFile::CiftiParcelScalarFile() : CiftiMappableDataFile(DataFileTypeEnum::CONNECTIVITY_PARCEL_SCALAR) { for (int32_t i = 0; i < BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS; i++) { m_brainordinateChartingEnabledForTab[i] = false; m_matrixChartingEnabledForTab[i] = false; m_chartMatrixDisplayProperties[i] = new ChartMatrixDisplayProperties(); } m_selectedParcelColoringMode = CiftiParcelColoringModeEnum::CIFTI_PARCEL_COLORING_OUTLINE; m_selectedParcelColor = CaretColorEnum::WHITE; m_parcelReorderingModel = new CiftiParcelReorderingModel(this); m_sceneAssistant = new SceneClassAssistant(); m_sceneAssistant->add("m_selectedParcelColoringMode", &m_selectedParcelColoringMode); m_sceneAssistant->add("m_selectedParcelColor", &m_selectedParcelColor); m_sceneAssistant->add("m_parcelReorderingModel", "CiftiParcelReorderingModel", m_parcelReorderingModel); } /** * Destructor. */ CiftiParcelScalarFile::~CiftiParcelScalarFile() { for (int32_t i = 0; i < BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS; i++) { delete m_chartMatrixDisplayProperties[i]; } delete m_parcelReorderingModel; delete m_sceneAssistant; } /** * @return Is charting enabled for this file? */ bool CiftiParcelScalarFile::isLineSeriesChartingEnabled(const int32_t tabIndex) const { CaretAssertArrayIndex(m_chartingEnabledForTab, BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS, tabIndex); return m_brainordinateChartingEnabledForTab[tabIndex]; } /** * @return Return true if the file's current state supports * charting data, else false. Typically a brainordinate file * is chartable if it contains more than one map. */ bool CiftiParcelScalarFile::isLineSeriesChartingSupported() const { if (getNumberOfMaps() > 1) { return true; } return false; } /** * Get chart data types supported by the file. * * @param chartDataTypesOut * Chart types supported by this file. */ void CiftiParcelScalarFile::getSupportedLineSeriesChartDataTypes(std::vector& chartDataTypesOut) const { helpGetSupportedLineSeriesChartDataTypes(chartDataTypesOut); } /** * Set charting enabled for this file. * * @param enabled * New status for charting enabled. */ void CiftiParcelScalarFile::setLineSeriesChartingEnabled(const int32_t tabIndex, const bool enabled) { CaretAssertArrayIndex(m_chartingEnabledForTab, BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS, tabIndex); m_brainordinateChartingEnabledForTab[tabIndex] = enabled; } /** * Load charting data for the surface with the given structure and node index. * * @param structure * The surface's structure. * @param nodeIndex * Index of the node. * @return * Pointer to the chart data. If the data FAILED to load, * the returned pointer will return true. Caller takes ownership * of the pointer and must delete it when no longer needed. */ ChartDataCartesian* CiftiParcelScalarFile::loadLineSeriesChartDataForSurfaceNode(const StructureEnum::Enum structure, const int32_t nodeIndex) { 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; } /** * Load average charting data for the surface with the given structure and node indices. * * @param structure * The surface's structure. * @param nodeIndices * Indices of the node. * @return * Pointer to the chart data. If the data FAILED to load, * the returned pointer will be NULL. Caller takes ownership * of the pointer and must delete it when no longer needed. */ ChartDataCartesian* CiftiParcelScalarFile::loadAverageLineSeriesChartDataForSurfaceNodes(const StructureEnum::Enum structure, const std::vector& nodeIndices) { ChartDataCartesian* chartData = helpLoadChartDataForSurfaceNodeAverage(structure, nodeIndices); return chartData; } /** * Load charting data for the voxel enclosing the given coordinate. * * @param xyz * Coordinate of voxel. * @return * Pointer to the chart data. If the data FAILED to load, * the returned pointer will be NULL. Caller takes ownership * of the pointer and must delete it when no longer needed. */ ChartDataCartesian* CiftiParcelScalarFile::loadLineSeriesChartDataForVoxelAtCoordinate(const float xyz[3]) { ChartDataCartesian* chartData = helpLoadChartDataForVoxelAtCoordinate(xyz); return chartData; } /** * Get the matrix dimensions. * * @param numberOfRowsOut * Number of rows in the matrix. * @param numberOfColumnsOut * Number of rows in the matrix. */ void CiftiParcelScalarFile::getMatrixDimensions(int32_t& numberOfRowsOut, int32_t& numberOfColumnsOut) const { 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 CiftiParcelScalarFile::getMatrixDataRGBA(int32_t& numberOfRowsOut, int32_t& numberOfColumnsOut, std::vector& rgbaOut) const { CiftiParcelLabelFile* parcelLabelFile = NULL; int32_t parcelLabelFileMapIndex = -1; bool enabled = false; std::vector parcelLabelFiles; getSelectedParcelLabelFileAndMapForReordering(parcelLabelFiles, parcelLabelFile, parcelLabelFileMapIndex, enabled); std::vector rowIndices; if (enabled) { const CiftiParcelReordering* parcelReordering = getParcelReordering(parcelLabelFile, parcelLabelFileMapIndex); if (parcelReordering != NULL) { rowIndices = parcelReordering->getReorderedParcelIndices(); } } return helpMapFileLoadChartDataMatrixRGBA(numberOfRowsOut, numberOfColumnsOut, rowIndices, rgbaOut); } /** * Get the value, row name, and column name for a cell in the matrix. * * @param rowIndex * The row index. * @param columnIndex * The column index. * @param cellValueOut * Output containing value in the cell. * @param rowNameOut * Name of row corresponding to row index. * @param columnNameOut * Name of column corresponding to column index. * @return * True if the output values are valid (valid row/column indices). */ bool CiftiParcelScalarFile::getMatrixCellAttributes(const int32_t rowIndex, const int32_t columnIndex, AString& cellValueOut, AString& rowNameOut, AString& columnNameOut) const { if ((rowIndex >= 0) && (rowIndex < m_ciftiFile->getNumberOfRows()) && (columnIndex >= 0) && (columnIndex < m_ciftiFile->getNumberOfColumns())) { const CiftiXML& xml = m_ciftiFile->getCiftiXML(); const std::vector& rowsParcelsMap = xml.getParcelsMap(CiftiXML::ALONG_COLUMN).getParcels(); CaretAssertVectorIndex(rowsParcelsMap, rowIndex); rowNameOut = rowsParcelsMap[rowIndex].m_name; columnNameOut = ("Map " + AString::number(columnIndex + 1)); const int32_t numberOfElementsInRow = m_ciftiFile->getNumberOfColumns(); std::vector rowData(numberOfElementsInRow); m_ciftiFile->getRow(&rowData[0], rowIndex); CaretAssertVectorIndex(rowData, columnIndex); cellValueOut = AString::number(rowData[columnIndex], 'f', 6); return true; } return false; } /** * @return Is charting enabled for this file? */ bool CiftiParcelScalarFile::isMatrixChartingEnabled(const int32_t tabIndex) const { CaretAssertArrayIndex(m_matrixChartingEnabledForTab, BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS, tabIndex); return m_matrixChartingEnabledForTab[tabIndex]; } /** * @return Return true if the file's current state supports * charting data, else false. Typically a brainordinate file * is chartable if it contains more than one map. */ bool CiftiParcelScalarFile::isMatrixChartingSupported() const { return true; } /** * Set charting enabled for this file. * * @param enabled * New status for charting enabled. */ void CiftiParcelScalarFile::setMatrixChartingEnabled(const int32_t tabIndex, const bool enabled) { CaretAssertArrayIndex(m_matrixChartingEnabledForTab, BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS, tabIndex); m_matrixChartingEnabledForTab[tabIndex] = enabled; } /** * Get chart data types supported by the file. * * @param chartDataTypesOut * Chart types supported by this file. */ void CiftiParcelScalarFile::getSupportedMatrixChartDataTypes(std::vector& chartDataTypesOut) const { chartDataTypesOut.clear(); chartDataTypesOut.push_back(ChartDataTypeEnum::CHART_DATA_TYPE_MATRIX_LAYER); } /** * @return Chart matrix display properties (const method). */ const ChartMatrixDisplayProperties* CiftiParcelScalarFile::getChartMatrixDisplayProperties(const int32_t tabIndex) const { CaretAssertArrayIndex(m_chartMatrixDisplayProperties, BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS, tabIndex); return m_chartMatrixDisplayProperties[tabIndex]; } /** * @return Chart matrix display properties. */ ChartMatrixDisplayProperties* CiftiParcelScalarFile::getChartMatrixDisplayProperties(const int32_t tabIndex) { CaretAssertArrayIndex(m_chartMatrixDisplayProperties, BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS, tabIndex); return m_chartMatrixDisplayProperties[tabIndex]; } /** * @return Coloring mode for selected parcel. */ CiftiParcelColoringModeEnum::Enum CiftiParcelScalarFile::getSelectedParcelColoringMode() const { return m_selectedParcelColoringMode; } /** * Set the coloring mode for selected parcel. * * @param coloringMode * New value for coloring mode. */ void CiftiParcelScalarFile::setSelectedParcelColoringMode(const CiftiParcelColoringModeEnum::Enum coloringMode) { m_selectedParcelColoringMode = coloringMode; } /** * @return Color for selected parcel. */ CaretColorEnum::Enum CiftiParcelScalarFile::getSelectedParcelColor() const { return m_selectedParcelColor; } /** * Set color for selected parcel. * * @param color * New color for selected parcel. */ void CiftiParcelScalarFile::setSelectedParcelColor(const CaretColorEnum::Enum color) { m_selectedParcelColor = color; } /** * Save file data from the scene. For subclasses that need to * save to a scene, this method should be overriden. sceneClass * will be valid and any scene data should be added to it. * * @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. */ void CiftiParcelScalarFile::saveFileDataToScene(const SceneAttributes* sceneAttributes, SceneClass* sceneClass) { CiftiMappableDataFile::saveFileDataToScene(sceneAttributes, sceneClass); m_sceneAssistant->saveMembers(sceneAttributes, sceneClass); sceneClass->addBooleanArray("m_brainordinateChartingEnabledForTab", m_brainordinateChartingEnabledForTab, BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS); sceneClass->addBooleanArray("m_matrixChartingEnabledForTab", m_matrixChartingEnabledForTab, BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS); /* * Save chart matrix properties */ SceneObjectMapIntegerKey* chartMatrixPropertiesMap = new SceneObjectMapIntegerKey("m_chartMatrixDisplayPropertiesMap", SceneObjectDataTypeEnum::SCENE_CLASS); const std::vector tabIndices = sceneAttributes->getIndicesOfTabsForSavingToScene(); for (std::vector::const_iterator tabIter = tabIndices.begin(); tabIter != tabIndices.end(); tabIter++) { const int32_t tabIndex = *tabIter; chartMatrixPropertiesMap->addClass(tabIndex, m_chartMatrixDisplayProperties[tabIndex]->saveToScene(sceneAttributes, "m_chartMatrixDisplayProperties")); } sceneClass->addChild(chartMatrixPropertiesMap); } /** * Restore file data from the scene. For subclasses that need to * restore from a scene, this method should be overridden. The scene class * will be valid and any scene data may be obtained from it. * * @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 CiftiParcelScalarFile::restoreFileDataFromScene(const SceneAttributes* sceneAttributes, const SceneClass* sceneClass) { CiftiMappableDataFile::restoreFileDataFromScene(sceneAttributes, sceneClass); m_sceneAssistant->restoreMembers(sceneAttributes, sceneClass); /* * Originally, charting was "per file": m_chartingEnabled * Later, charting became "per tab": m_chartingEnabledForTab * Even Later, it needed to clearly be for brainordinates: m_brainordinateChartingEnabledForTab */ const ScenePrimitiveArray* brainChartingForTab = sceneClass->getPrimitiveArray("m_brainordinateChartingEnabledForTab"); const ScenePrimitiveArray* oldChartingEnabledForTab = sceneClass->getPrimitiveArray("m_chartingEnabledForTab"); if (brainChartingForTab != NULL) { sceneClass->getBooleanArrayValue("brainChartingForTab", m_brainordinateChartingEnabledForTab, BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS); } else if (oldChartingEnabledForTab != NULL) { sceneClass->getBooleanArrayValue("m_chartingEnabledForTab", m_brainordinateChartingEnabledForTab, BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS); } else { /* * Obsolete value when charting was not 'per tab' */ const bool chartingEnabled = sceneClass->getBooleanValue("m_chartingEnabled", false); for (int32_t i = 0; i < BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS; i++) { m_brainordinateChartingEnabledForTab[i] = chartingEnabled; } } for (int32_t i = 0; i < BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS; i++) { m_matrixChartingEnabledForTab[i] = false; m_chartMatrixDisplayProperties[i]->resetPropertiesToDefault(); } sceneClass->getBooleanArrayValue("m_matrixChartingEnabledForTab", m_matrixChartingEnabledForTab, BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS); /* * Restore chart matrix properties */ const SceneObjectMapIntegerKey* chartMatrixPropertiesMap = sceneClass->getMapIntegerKey("m_chartMatrixDisplayPropertiesMap"); if (chartMatrixPropertiesMap != NULL) { const std::vector tabIndices = chartMatrixPropertiesMap->getKeys(); for (std::vector::const_iterator tabIter = tabIndices.begin(); tabIter != tabIndices.end(); tabIter++) { const int32_t tabIndex = *tabIter; const SceneClass* sceneClass = chartMatrixPropertiesMap->classValue(tabIndex); m_chartMatrixDisplayProperties[tabIndex]->restoreFromScene(sceneAttributes, sceneClass); } } } /** * 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 CiftiParcelScalarFile::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 CiftiParcelScalarFile::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* CiftiParcelScalarFile::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 CiftiParcelScalarFile::createParcelReordering(const CiftiParcelLabelFile* parcelLabelFile, const int32_t parcelLabelFileMapIndex, AString& errorMessageOut) { return m_parcelReorderingModel->createParcelReordering(parcelLabelFile, parcelLabelFileMapIndex, errorMessageOut); } /** * @return True if loading attributes (column/row, yoking) are * supported by this file type. */ bool CiftiParcelScalarFile::isSupportsLoadingAttributes() { return false; } /** * @return The matrix loading type (by row/column). */ ChartMatrixLoadingDimensionEnum::Enum CiftiParcelScalarFile::getMatrixLoadingDimension() const { /* * This file supports loading by column only ! */ return ChartMatrixLoadingDimensionEnum::CHART_MATRIX_LOADING_BY_COLUMN; } /** * Set the matrix loading type (by row/column). * * @param matrixLoadingType * New value for matrix loading type. */ void CiftiParcelScalarFile::setMatrixLoadingDimension(const ChartMatrixLoadingDimensionEnum::Enum /* matrixLoadingType */) { CaretLogSevere("Attempting to change matrix loading type for a file that only supports loading by column"); } /** * @return Selected yoking group. */ YokingGroupEnum::Enum CiftiParcelScalarFile::getYokingGroup() const { /* not supported in this file */ return YokingGroupEnum::YOKING_GROUP_OFF; } /** * Set the selected yoking group. * * @param yokingGroup * New value for yoking group. */ void CiftiParcelScalarFile::setYokingGroup(const YokingGroupEnum::Enum /* yokingGroup */) { /* not supported in this file */ } connectome-workbench-1.2.3+git41-gc4c6c90/src/Files/CiftiParcelScalarFile.h000066400000000000000000000162071300200146000261000ustar00rootroot00000000000000#ifndef __CIFTI_PARCEL_SCALAR_FILE_H__ #define __CIFTI_PARCEL_SCALAR_FILE_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 "BrainConstants.h" #include "ChartableLineSeriesBrainordinateInterface.h" #include "ChartableMatrixParcelInterface.h" #include "CiftiMappableDataFile.h" namespace caret { class CiftiParcelReorderingModel; class SceneClassAssistant; class CiftiParcelScalarFile : public CiftiMappableDataFile, public ChartableLineSeriesBrainordinateInterface, public ChartableMatrixParcelInterface { public: CiftiParcelScalarFile(); virtual ~CiftiParcelScalarFile(); virtual bool isLineSeriesChartingEnabled(const int32_t tabIndex) const; virtual void setLineSeriesChartingEnabled(const int32_t tabIndex, const bool enabled); virtual bool isLineSeriesChartingSupported() const; virtual ChartDataCartesian* loadLineSeriesChartDataForSurfaceNode(const StructureEnum::Enum structure, const int32_t nodeIndex); virtual ChartDataCartesian* loadAverageLineSeriesChartDataForSurfaceNodes(const StructureEnum::Enum structure, const std::vector& nodeIndices); virtual ChartDataCartesian* loadLineSeriesChartDataForVoxelAtCoordinate(const float xyz[3]); virtual void getSupportedLineSeriesChartDataTypes(std::vector& chartDataTypesOut) const; virtual void getMatrixDimensions(int32_t& numberOfRowsOut, int32_t& numberOfColumnsOut) const; virtual bool getMatrixDataRGBA(int32_t& numberOfRowsOut, int32_t& numberOfColumnsOut, std::vector& rgbaOut) const; virtual bool getMatrixCellAttributes(const int32_t rowIndex, const int32_t columnIndex, AString& cellValueOut, AString& rowNameOut, AString& columnNameOut) const; virtual bool isMatrixChartingEnabled(const int32_t tabIndex) const; virtual bool isMatrixChartingSupported() const; virtual void setMatrixChartingEnabled(const int32_t tabIndex, const bool enabled); virtual void getSupportedMatrixChartDataTypes(std::vector& chartDataTypesOut) const; const ChartMatrixDisplayProperties* getChartMatrixDisplayProperties(const int32_t tabIndex) const; ChartMatrixDisplayProperties* getChartMatrixDisplayProperties(const int32_t tabIndex); virtual CiftiParcelColoringModeEnum::Enum getSelectedParcelColoringMode() const; virtual void setSelectedParcelColoringMode(const CiftiParcelColoringModeEnum::Enum coloringMode); virtual CaretColorEnum::Enum getSelectedParcelColor() const; virtual void setSelectedParcelColor(const CaretColorEnum::Enum color); 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; virtual bool isSupportsLoadingAttributes(); virtual ChartMatrixLoadingDimensionEnum::Enum getMatrixLoadingDimension() const; virtual void setMatrixLoadingDimension(const ChartMatrixLoadingDimensionEnum::Enum matrixLoadingType); virtual YokingGroupEnum::Enum getYokingGroup() const; virtual void setYokingGroup(const YokingGroupEnum::Enum yokingType); private: CiftiParcelScalarFile(const CiftiParcelScalarFile&); CiftiParcelScalarFile& operator=(const CiftiParcelScalarFile&); virtual void saveFileDataToScene(const SceneAttributes* sceneAttributes, SceneClass* sceneClass); virtual void restoreFileDataFromScene(const SceneAttributes* sceneAttributes, const SceneClass* sceneClass); public: // ADD_NEW_METHODS_HERE private: SceneClassAssistant* m_sceneAssistant; bool m_brainordinateChartingEnabledForTab[BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS]; bool m_matrixChartingEnabledForTab[BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS]; ChartMatrixDisplayProperties* m_chartMatrixDisplayProperties[BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS]; CiftiParcelColoringModeEnum::Enum m_selectedParcelColoringMode; CaretColorEnum::Enum m_selectedParcelColor; CiftiParcelReorderingModel* m_parcelReorderingModel; // ADD_NEW_MEMBERS_HERE }; #ifdef __CIFTI_PARCEL_SCALAR_FILE_DECLARE__ // #endif // __CIFTI_PARCEL_SCALAR_FILE_DECLARE__ } // namespace #endif //__CIFTI_PARCEL_SCALAR_FILE_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Files/CiftiParcelSeriesFile.cxx000066400000000000000000000266371300200146000265100ustar00rootroot00000000000000 /*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 __CIFTI_PARCEL_SERIES_FILE_DECLARE__ #include "CiftiParcelSeriesFile.h" #undef __CIFTI_PARCEL_SERIES_FILE_DECLARE__ #include "CaretLogger.h" #include "ChartDataCartesian.h" #include "SceneClass.h" #include "SceneClassArray.h" using namespace caret; /** * \class caret::CiftiParcelSeriesFile * \brief CIFTI Parcel by Data-Series File. * \ingroup Files */ /** * Constructor. */ CiftiParcelSeriesFile::CiftiParcelSeriesFile() : CiftiMappableDataFile(DataFileTypeEnum::CONNECTIVITY_PARCEL_SERIES) { for (int32_t i = 0; i < BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS; i++) { m_chartingEnabledForTab[i] = false; } } /** * Destructor. */ CiftiParcelSeriesFile::~CiftiParcelSeriesFile() { } /** * @return Is charting enabled for this file? */ bool CiftiParcelSeriesFile::isLineSeriesChartingEnabled(const int32_t tabIndex) const { CaretAssertArrayIndex(m_chartingEnabledForTab, BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS, tabIndex); return m_chartingEnabledForTab[tabIndex]; } /** * @return Return true if the file's current state supports * charting data, else false. Typically a brainordinate file * is chartable if it contains more than one map. */ bool CiftiParcelSeriesFile::isLineSeriesChartingSupported() const { if (getNumberOfMaps() > 1) { return true; } return false; } /** * Set charting enabled for this file. * * @param enabled * New status for charting enabled. */ void CiftiParcelSeriesFile::setLineSeriesChartingEnabled(const int32_t tabIndex, const bool enabled) { CaretAssertArrayIndex(m_chartingEnabledForTab, BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS, tabIndex); m_chartingEnabledForTab[tabIndex] = enabled; } /** * Get chart data types supported by the file. * * @param chartDataTypesOut * Chart types supported by this file. */ void CiftiParcelSeriesFile::getSupportedLineSeriesChartDataTypes(std::vector& chartDataTypesOut) const { helpGetSupportedLineSeriesChartDataTypes(chartDataTypesOut); } /** * Load charting data for the surface with the given structure and node index. * * @param structure * The surface's structure. * @param nodeIndex * Index of the node. * @return * Pointer to the chart data. If the data FAILED to load, * the returned pointer will return true. Caller takes ownership * of the pointer and must delete it when no longer needed. */ ChartDataCartesian* CiftiParcelSeriesFile::loadLineSeriesChartDataForSurfaceNode(const StructureEnum::Enum structure, const int32_t nodeIndex) { 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; } /** * Load average charting data for the surface with the given structure and node indices. * * @param structure * The surface's structure. * @param nodeIndices * Indices of the node. * @return * Pointer to the chart data. If the data FAILED to load, * the returned pointer will be NULL. Caller takes ownership * of the pointer and must delete it when no longer needed. */ ChartDataCartesian* CiftiParcelSeriesFile::loadAverageLineSeriesChartDataForSurfaceNodes(const StructureEnum::Enum structure, const std::vector& nodeIndices) { ChartDataCartesian* chartData = helpLoadChartDataForSurfaceNodeAverage(structure, nodeIndices); return chartData; } /** * Load charting data for the voxel enclosing the given coordinate. * * @param xyz * Coordinate of voxel. * @return * Pointer to the chart data. If the data FAILED to load, * the returned pointer will be NULL. Caller takes ownership * of the pointer and must delete it when no longer needed. */ ChartDataCartesian* CiftiParcelSeriesFile::loadLineSeriesChartDataForVoxelAtCoordinate(const float xyz[3]) { ChartDataCartesian* chartData = helpLoadChartDataForVoxelAtCoordinate(xyz); return chartData; } /** * Save file data from the scene. For subclasses that need to * save to a scene, this method should be overriden. sceneClass * will be valid and any scene data should be added to it. * * @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. */ void CiftiParcelSeriesFile::saveFileDataToScene(const SceneAttributes* sceneAttributes, SceneClass* sceneClass) { CiftiMappableDataFile::saveFileDataToScene(sceneAttributes, sceneClass); sceneClass->addBooleanArray("m_chartingEnabledForTab", m_chartingEnabledForTab, BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS); } /** * Restore file data from the scene. For subclasses that need to * restore from a scene, this method should be overridden. The scene class * will be valid and any scene data may be obtained from it. * * @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 CiftiParcelSeriesFile::restoreFileDataFromScene(const SceneAttributes* sceneAttributes, const SceneClass* sceneClass) { CiftiMappableDataFile::restoreFileDataFromScene(sceneAttributes, sceneClass); const ScenePrimitiveArray* tabArray = sceneClass->getPrimitiveArray("m_chartingEnabledForTab"); if (tabArray != NULL) { sceneClass->getBooleanArrayValue("m_chartingEnabledForTab", m_chartingEnabledForTab, BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS); } else { /* * Obsolete value when charting was not 'per tab' */ const bool chartingEnabled = sceneClass->getBooleanValue("m_chartingEnabled", false); for (int32_t i = 0; i < BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS; i++) { m_chartingEnabledForTab[i] = chartingEnabled; } } } connectome-workbench-1.2.3+git41-gc4c6c90/src/Files/CiftiParcelSeriesFile.h000066400000000000000000000061421300200146000261220ustar00rootroot00000000000000#ifndef __CIFTI_PARCEL_SERIES_FILE_H__ #define __CIFTI_PARCEL_SERIES_FILE_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 "BrainConstants.h" #include "ChartableLineSeriesBrainordinateInterface.h" #include "CiftiMappableDataFile.h" namespace caret { class CiftiParcelSeriesFile : public CiftiMappableDataFile, public ChartableLineSeriesBrainordinateInterface { public: CiftiParcelSeriesFile(); virtual ~CiftiParcelSeriesFile(); virtual bool isLineSeriesChartingEnabled(const int32_t tabIndex) const; virtual void setLineSeriesChartingEnabled(const int32_t tabIndex, const bool enabled); virtual bool isLineSeriesChartingSupported() const; virtual ChartDataCartesian* loadLineSeriesChartDataForSurfaceNode(const StructureEnum::Enum structure, const int32_t nodeIndex); virtual ChartDataCartesian* loadAverageLineSeriesChartDataForSurfaceNodes(const StructureEnum::Enum structure, const std::vector& nodeIndices); virtual ChartDataCartesian* loadLineSeriesChartDataForVoxelAtCoordinate(const float xyz[3]); virtual void getSupportedLineSeriesChartDataTypes(std::vector& chartDataTypesOut) const; private: CiftiParcelSeriesFile(const CiftiParcelSeriesFile&); CiftiParcelSeriesFile& operator=(const CiftiParcelSeriesFile&); virtual void saveFileDataToScene(const SceneAttributes* sceneAttributes, SceneClass* sceneClass); virtual void restoreFileDataFromScene(const SceneAttributes* sceneAttributes, const SceneClass* sceneClass); public: // ADD_NEW_METHODS_HERE private: // ADD_NEW_MEMBERS_HERE bool m_chartingEnabledForTab[BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS]; }; #ifdef __CIFTI_PARCEL_SERIES_FILE_DECLARE__ // #endif // __CIFTI_PARCEL_SERIES_FILE_DECLARE__ } // namespace #endif //__CIFTI_PARCEL_SERIES_FILE_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Files/CiftiScalarDataSeriesFile.cxx000066400000000000000000000563241300200146000272750ustar00rootroot00000000000000 /*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 __CIFTI_SCALAR_DATA_SERIES_FILE_DECLARE__ #include "CiftiScalarDataSeriesFile.h" #undef __CIFTI_SCALAR_DATA_SERIES_FILE_DECLARE__ #include "CaretAssert.h" #include "CaretLogger.h" #include "ChartDataCartesian.h" #include "ChartMatrixDisplayProperties.h" #include "CiftiFile.h" #include "CiftiSeriesMap.h" #include "CiftiXML.h" #include "DataFileException.h" #include "EventBrowserTabIndicesGetAll.h" #include "EventManager.h" #include "EventMapYokingSelectMap.h" #include "EventMapYokingValidation.h" #include "FastStatistics.h" #include "NodeAndVoxelColoring.h" #include "SceneClassAssistant.h" using namespace caret; /** * \class caret::CiftiScalarDataSeriesFile * \brief CIFTI Scalar Data Series File * \ingroup Files */ /** * Constructor. */ CiftiScalarDataSeriesFile::CiftiScalarDataSeriesFile() : CiftiMappableDataFile(DataFileTypeEnum::CONNECTIVITY_SCALAR_DATA_SERIES) { for (int32_t i = 0; i < BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS; i++) { m_lineSeriesChartingEnabledForTab[i] = false; m_matrixChartingEnabledForTab[i] = false; m_chartMatrixDisplayPropertiesForTab[i] = new ChartMatrixDisplayProperties(); m_chartMatrixDisplayPropertiesForTab[i]->setGridLinesDisplayed(false); m_yokingGroupForTab[i] = MapYokingGroupEnum::MAP_YOKING_GROUP_OFF; m_selectedMapIndices[i] = 0; } m_sceneAssistant = new SceneClassAssistant(); m_sceneAssistant->addTabIndexedEnumeratedTypeArray("m_yokingGroupForTab", m_yokingGroupForTab); m_sceneAssistant->addTabIndexedBooleanArray("m_lineSeriesChartingEnabledForTab", m_lineSeriesChartingEnabledForTab); m_sceneAssistant->addTabIndexedBooleanArray("m_matrixChartingEnabledForTab", m_matrixChartingEnabledForTab); m_sceneAssistant->addTabIndexedIntegerArray("m_selectedMapIndices", m_selectedMapIndices); EventManager::get()->addEventListener(this, EventTypeEnum::EVENT_MAP_YOKING_SELECT_MAP); EventManager::get()->addEventListener(this, EventTypeEnum::EVENT_MAP_YOKING_VALIDATION); } /** * Destructor. */ CiftiScalarDataSeriesFile::~CiftiScalarDataSeriesFile() { EventManager::get()->removeAllEventsFromListener(this); for (int32_t i = 0; i < BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS; i++) { delete m_chartMatrixDisplayPropertiesForTab[i]; } delete m_sceneAssistant; } /** * Receive an event. * * @param event * The event. */ void CiftiScalarDataSeriesFile::receiveEvent(Event* event) { if (event->getEventType() == EventTypeEnum::EVENT_MAP_YOKING_VALIDATION) { EventMapYokingValidation* yokeMapEvent = dynamic_cast(event); CaretAssert(yokeMapEvent); yokeMapEvent->addMapYokedFileAllTabs(this, m_yokingGroupForTab); yokeMapEvent->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 mapYokingGroup = selectMapEvent->getMapYokingGroup(); if (mapYokingGroup != MapYokingGroupEnum::MAP_YOKING_GROUP_OFF) { const int32_t yokedMapIndex = selectMapEvent->getMapIndex(); EventBrowserTabIndicesGetAll tabIndicesEvent; EventManager::get()->sendEvent(tabIndicesEvent.getPointer()); const std::vector tabIndices = tabIndicesEvent.getAllBrowserTabIndices(); for (std::vector::const_iterator iter = tabIndices.begin(); iter != tabIndices.end(); iter++) { const int32_t tabIndex = *iter; if (getMatrixRowColumnMapYokingGroup(tabIndex) == mapYokingGroup) { setSelectedMapIndex(tabIndex, yokedMapIndex); } } } selectMapEvent->setEventProcessed(); } } /** * @param tabIndex * Index of tab. * @return * Selected yoking group for the given tab. */ MapYokingGroupEnum::Enum CiftiScalarDataSeriesFile::getMatrixRowColumnMapYokingGroup(const int32_t tabIndex) const { CaretAssertArrayIndex(m_yokingGroupForTab, BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS, tabIndex); return m_yokingGroupForTab[tabIndex]; } /** * Set the selected yoking group for the given tab. * * @param tabIndex * Index of tab. * @param yokingGroup * New value for yoking group. */ void CiftiScalarDataSeriesFile::setMatrixRowColumnMapYokingGroup(const int32_t tabIndex, const MapYokingGroupEnum::Enum yokingGroup) { CaretAssertArrayIndex(m_yokingGroupForTab, BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS, tabIndex); m_yokingGroupForTab[tabIndex] = yokingGroup; if (m_yokingGroupForTab[tabIndex] == MapYokingGroupEnum::MAP_YOKING_GROUP_OFF) { return; } } /** * @param tabIndex * Index of tab. * @return * Selected map index in the given tab. */ int32_t CiftiScalarDataSeriesFile::getSelectedMapIndex(const int32_t tabIndex) { CaretAssertArrayIndex(m_selectedMapIndices, BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS, tabIndex); return m_selectedMapIndices[tabIndex]; } /** * Set the selected map index for the given tab. * * @param tabIndex * Index of tab. * @param mapIndex * New value for selected map index. */ void CiftiScalarDataSeriesFile::setSelectedMapIndex(const int32_t tabIndex, const int32_t mapIndex) { CaretAssertArrayIndex(m_selectedMapIndices, BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS, tabIndex); m_selectedMapIndices[tabIndex] = mapIndex; } /** * Get the matrix dimensions. * * @param numberOfRowsOut * Number of rows in the matrix. * @param numberOfColumnsOut * Number of columns in the matrix. */ void CiftiScalarDataSeriesFile::getMatrixDimensions(int32_t& numberOfRowsOut, int32_t& numberOfColumnsOut) const { 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 CiftiScalarDataSeriesFile::getMatrixDataRGBA(int32_t& numberOfRowsOut, int32_t& numberOfColumnsOut, std::vector& rgbaOut) const { std::vector rowIndices; return helpMatrixFileLoadChartDataMatrixRGBA(numberOfRowsOut, numberOfColumnsOut, rowIndices, rgbaOut); } /** * Get the value, row name, and column name for a cell in the matrix. * * @param rowIndex * The row index. * @param columnIndex * The column index. * @param cellValueOut * Output containing value in the cell. * @param rowNameOut * Name of row corresponding to row index. * @param columnNameOut * Name of column corresponding to column index. * @return * True if the output values are valid (valid row/column indices). */ bool CiftiScalarDataSeriesFile::getMatrixCellAttributes(const int32_t rowIndex, const int32_t columnIndex, AString& cellValueOut, AString& rowNameOut, AString& columnNameOut) const { rowNameOut = " "; columnNameOut = " "; if ((rowIndex >= 0) && (rowIndex < m_ciftiFile->getNumberOfRows()) && (columnIndex >= 0) && (columnIndex < m_ciftiFile->getNumberOfColumns())) { const CiftiXML& xml = m_ciftiFile->getCiftiXML(); const CiftiScalarsMap& scalarsMap = xml.getScalarsMap(CiftiXML::ALONG_COLUMN); CaretAssertArrayIndex(scalarsMap, scalarsMap.getLength(), rowIndex); rowNameOut = scalarsMap.getMapName(rowIndex); const CiftiSeriesMap& seriesMap = xml.getSeriesMap(CiftiXML::ALONG_ROW); CaretAssertArrayIndex(seriesMap, seriesMap.getLength(), columnIndex); const float time = seriesMap.getStart() + seriesMap.getStep() * columnIndex; AString timeUnitsString; switch (seriesMap.getUnit()) { case CiftiSeriesMap::HERTZ: timeUnitsString = NiftiTimeUnitsEnum::toGuiName(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: timeUnitsString = NiftiTimeUnitsEnum::toGuiName(NiftiTimeUnitsEnum::NIFTI_UNITS_SEC); break; } 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); m_ciftiFile->getRow(&rowData[0], rowIndex); CaretAssertVectorIndex(rowData, columnIndex); cellValueOut = AString::number(rowData[columnIndex], 'f', 6); return true; } return false; } /** * @return Is charting enabled for this file? */ bool CiftiScalarDataSeriesFile::isMatrixChartingEnabled(const int32_t tabIndex) const { CaretAssertArrayIndex(m_matrixChartingEnabledForTab, BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS, tabIndex); return m_matrixChartingEnabledForTab[tabIndex]; } /** * @return Return true if the file's current state supports * charting data, else false. Typically a brainordinate file * is chartable if it contains more than one map. */ bool CiftiScalarDataSeriesFile::isMatrixChartingSupported() const { return true; } /** * Set charting enabled for this file. * * @param enabled * New status for charting enabled. */ void CiftiScalarDataSeriesFile::setMatrixChartingEnabled(const int32_t tabIndex, const bool enabled) { CaretAssertArrayIndex(m_matrixChartingEnabledForTab, BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS, tabIndex); m_matrixChartingEnabledForTab[tabIndex] = enabled; } /** * Get chart data types supported by the file. * * @param chartDataTypesOut * Chart types supported by this file. */ void CiftiScalarDataSeriesFile::getSupportedMatrixChartDataTypes(std::vector& chartDataTypesOut) const { chartDataTypesOut.clear(); chartDataTypesOut.push_back(ChartDataTypeEnum::CHART_DATA_TYPE_MATRIX_SERIES); } /** * @return Chart matrix display properties (const method). */ const ChartMatrixDisplayProperties* CiftiScalarDataSeriesFile::getChartMatrixDisplayProperties(const int32_t tabIndex) const { CaretAssertArrayIndex(m_chartMatrixDisplayProperties, BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS, tabIndex); return m_chartMatrixDisplayPropertiesForTab[tabIndex]; } /** * @return Chart matrix display properties. */ ChartMatrixDisplayProperties* CiftiScalarDataSeriesFile::getChartMatrixDisplayProperties(const int32_t tabIndex) { CaretAssertArrayIndex(m_chartMatrixDisplayProperties, BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS, tabIndex); return m_chartMatrixDisplayPropertiesForTab[tabIndex]; } /** * Load charting data for the given column index. * * @param columnIndex * Index of the column. * @return * Pointer to the chart data. If the data FAILED to load, * the returned pointer will be NULL. Caller takes ownership * of the pointer and must delete it when no longer needed. */ ChartDataCartesian* CiftiScalarDataSeriesFile::loadLineSeriesChartDataForColumn(const int32_t /*columnIndex*/) { CaretAssertMessage(0, "Loading of columns is not used at this time"); return NULL; } /** * Load charting data for the given row index. * * @param rowIndex * Index of the row. * @return * Pointer to the chart data. If the data FAILED to load, * the returned pointer will be NULL. Caller takes ownership * of the pointer and must delete it when no longer needed. */ ChartDataCartesian* CiftiScalarDataSeriesFile::loadLineSeriesChartDataForRow(const int32_t rowIndex) { ChartDataCartesian* chartData = NULL; try { if ((rowIndex >= 0) && (rowIndex < m_ciftiFile->getNumberOfRows())) { const int32_t numberOfElementsInRow = m_ciftiFile->getNumberOfColumns(); if (numberOfElementsInRow > 0) { std::vector rowData(numberOfElementsInRow); m_ciftiFile->getRow(&rowData[0], rowIndex); const CiftiXML& ciftiXML = m_ciftiFile->getCiftiXML(); const CiftiSeriesMap& seriesMap = ciftiXML.getSeriesMap(CiftiXML::ALONG_ROW); ChartDataTypeEnum::Enum chartDataType = ChartDataTypeEnum::CHART_DATA_TYPE_INVALID; AString timeUnitsString; switch (seriesMap.getUnit()) { case CiftiSeriesMap::HERTZ: chartDataType = ChartDataTypeEnum::CHART_DATA_TYPE_LINE_FREQUENCY_SERIES; break; case CiftiSeriesMap::METER: CaretLogWarning("CIFTI Units METER not implemented"); break; case CiftiSeriesMap::RADIAN: CaretLogWarning("CIFTI Units RADIAN not implemented"); break; case CiftiSeriesMap::SECOND: chartDataType = ChartDataTypeEnum::CHART_DATA_TYPE_LINE_TIME_SERIES; break; } if (chartDataType != ChartDataTypeEnum::CHART_DATA_TYPE_INVALID) { chartData = new ChartDataCartesian(chartDataType, ChartAxisUnitsEnum::CHART_AXIS_UNITS_NONE, ChartAxisUnitsEnum::CHART_AXIS_UNITS_NONE); if (chartData != NULL) { float timeStart = seriesMap.getStart(); float timeStep = seriesMap.getStep(); chartData->setTimeStartInSecondsAxisX(timeStart); chartData->setTimeStepInSecondsAxisX(timeStep); for (int64_t i = 0; i < numberOfElementsInRow; i++) { const float xValue = timeStart + (i * timeStep); chartData->addPoint(xValue, rowData[i]); } } } } } } catch (const DataFileException& dfe) { if (chartData != NULL) { delete chartData; chartData = NULL; } throw dfe; } return chartData; } /** * @return Is charting enabled for this file? */ bool CiftiScalarDataSeriesFile::isLineSeriesChartingEnabled(const int32_t tabIndex) const { CaretAssertArrayIndex(m_lineSeriesChartingEnabledForTab, BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS, tabIndex); return m_lineSeriesChartingEnabledForTab[tabIndex]; } /** * @return Return true if the file's current state supports * charting data, else false. Typically a brainordinate file * is chartable if it contains more than one map. */ bool CiftiScalarDataSeriesFile::isLineSeriesChartingSupported() const { if ((m_ciftiFile->getNumberOfColumns() > 0) && (m_ciftiFile->getNumberOfRows() > 0)) { return true; } return false; } /** * Get chart data types supported by the file. * * @param chartDataTypesOut * Chart types supported by this file. */ void CiftiScalarDataSeriesFile::getSupportedLineSeriesChartDataTypes(std::vector& chartDataTypesOut) const { chartDataTypesOut.clear(); const CiftiXML& ciftiXML = m_ciftiFile->getCiftiXML(); CiftiMappingType::MappingType mapType = ciftiXML.getMappingType(CiftiXML::ALONG_ROW); const AString message("Mapping type should always be SERIES for CIFTI Scalar Data Series File"); CaretAssertMessage(mapType == CiftiMappingType::SERIES, message); if (mapType == CiftiMappingType::SERIES) { const CiftiSeriesMap& seriesMap = ciftiXML.getSeriesMap(CiftiXML::ALONG_ROW); switch (seriesMap.getUnit()) { case CiftiSeriesMap::HERTZ: chartDataTypesOut.push_back(ChartDataTypeEnum::CHART_DATA_TYPE_LINE_FREQUENCY_SERIES); break; case CiftiSeriesMap::METER: CaretLogWarning("CIFTI Units METER not implemented"); break; case CiftiSeriesMap::RADIAN: CaretLogWarning("CIFTI Units RADIAN not implemented"); break; case CiftiSeriesMap::SECOND: chartDataTypesOut.push_back(ChartDataTypeEnum::CHART_DATA_TYPE_LINE_TIME_SERIES); break; } } else { CaretLogSevere(message); } } /** * Set charting enabled for this file. * * @param enabled * New status for charting enabled. */ void CiftiScalarDataSeriesFile::setLineSeriesChartingEnabled(const int32_t tabIndex, const bool enabled) { CaretAssertArrayIndex(m_lineSeriesChartingEnabledForTab, BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS, tabIndex); m_lineSeriesChartingEnabledForTab[tabIndex] = enabled; } /** * Save file data from the scene. For subclasses that need to * save to a scene, this method should be overriden. sceneClass * will be valid and any scene data should be added to it. * * @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. */ void CiftiScalarDataSeriesFile::saveFileDataToScene(const SceneAttributes* sceneAttributes, SceneClass* sceneClass) { m_sceneAssistant->saveMembers(sceneAttributes, sceneClass); /* * Save chart matrix properties */ SceneObjectMapIntegerKey* chartMatrixPropertiesMap = new SceneObjectMapIntegerKey("m_chartMatrixDisplayPropertiesMap", SceneObjectDataTypeEnum::SCENE_CLASS); const std::vector tabIndices = sceneAttributes->getIndicesOfTabsForSavingToScene(); for (std::vector::const_iterator tabIter = tabIndices.begin(); tabIter != tabIndices.end(); tabIter++) { const int32_t tabIndex = *tabIter; chartMatrixPropertiesMap->addClass(tabIndex, m_chartMatrixDisplayPropertiesForTab[tabIndex]->saveToScene(sceneAttributes, "m_chartMatrixDisplayProperties")); } sceneClass->addChild(chartMatrixPropertiesMap); } /** * Restore file data from the scene. For subclasses that need to * restore from a scene, this method should be overridden. The scene class * will be valid and any scene data may be obtained from it. * * @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 CiftiScalarDataSeriesFile::restoreFileDataFromScene(const SceneAttributes* sceneAttributes, const SceneClass* sceneClass) { m_sceneAssistant->restoreMembers(sceneAttributes, sceneClass); /* * Restore chart matrix properties */ const SceneObjectMapIntegerKey* chartMatrixPropertiesMap = sceneClass->getMapIntegerKey("m_chartMatrixDisplayPropertiesMap"); if (chartMatrixPropertiesMap != NULL) { const std::vector tabIndices = chartMatrixPropertiesMap->getKeys(); for (std::vector::const_iterator tabIter = tabIndices.begin(); tabIter != tabIndices.end(); tabIter++) { const int32_t tabIndex = *tabIter; const SceneClass* sceneClass = chartMatrixPropertiesMap->classValue(tabIndex); m_chartMatrixDisplayPropertiesForTab[tabIndex]->restoreFromScene(sceneAttributes, sceneClass); } } } connectome-workbench-1.2.3+git41-gc4c6c90/src/Files/CiftiScalarDataSeriesFile.h000066400000000000000000000126151300200146000267150ustar00rootroot00000000000000#ifndef __CIFTI_SCALAR_DATA_SERIES_FILE_H__ #define __CIFTI_SCALAR_DATA_SERIES_FILE_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 "BrainConstants.h" #include "ChartableLineSeriesRowColumnInterface.h" #include "ChartableMatrixSeriesInterface.h" #include "CiftiMappableDataFile.h" #include "EventListenerInterface.h" namespace caret { class SceneClassAssistant; class CiftiScalarDataSeriesFile : public CiftiMappableDataFile, public ChartableLineSeriesRowColumnInterface, public ChartableMatrixSeriesInterface, public EventListenerInterface { public: CiftiScalarDataSeriesFile(); virtual ~CiftiScalarDataSeriesFile(); virtual MapYokingGroupEnum::Enum getMatrixRowColumnMapYokingGroup(const int32_t tabIndex) const; virtual void setMatrixRowColumnMapYokingGroup(const int32_t tabIndex, const MapYokingGroupEnum::Enum yokingType); virtual int32_t getSelectedMapIndex(const int32_t tabIndex); virtual void setSelectedMapIndex(const int32_t tabIndex, const int32_t mapIndex); virtual void receiveEvent(Event* event); virtual void getMatrixDimensions(int32_t& numberOfRowsOut, int32_t& numberOfColumnsOut) const; virtual bool getMatrixDataRGBA(int32_t& numberOfRowsOut, int32_t& numberOfColumnsOut, std::vector& rgbaOut) const; virtual bool getMatrixCellAttributes(const int32_t rowIndex, const int32_t columnIndex, AString& cellValueOut, AString& rowNameOut, AString& columnNameOut) const; virtual bool isMatrixChartingEnabled(const int32_t tabIndex) const; virtual bool isMatrixChartingSupported() const; virtual void setMatrixChartingEnabled(const int32_t tabIndex, const bool enabled); virtual void getSupportedMatrixChartDataTypes(std::vector& chartDataTypesOut) const; const ChartMatrixDisplayProperties* getChartMatrixDisplayProperties(const int32_t tabIndex) const; ChartMatrixDisplayProperties* getChartMatrixDisplayProperties(const int32_t tabIndex); virtual bool isLineSeriesChartingEnabled(const int32_t tabIndex) const; virtual void setLineSeriesChartingEnabled(const int32_t tabIndex, const bool enabled); virtual bool isLineSeriesChartingSupported() const; virtual ChartDataCartesian* loadLineSeriesChartDataForColumn(const int32_t columnIndex); virtual ChartDataCartesian* loadLineSeriesChartDataForRow(const int32_t rowIndex); virtual void getSupportedLineSeriesChartDataTypes(std::vector& chartDataTypesOut) const; // ADD_NEW_METHODS_HERE protected: virtual void saveFileDataToScene(const SceneAttributes* sceneAttributes, SceneClass* sceneClass); virtual void restoreFileDataFromScene(const SceneAttributes* sceneAttributes, const SceneClass* sceneClass); private: CiftiScalarDataSeriesFile(const CiftiScalarDataSeriesFile&); CiftiScalarDataSeriesFile& operator=(const CiftiScalarDataSeriesFile&); SceneClassAssistant* m_sceneAssistant; /** yoking status */ MapYokingGroupEnum::Enum m_yokingGroupForTab[BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS]; bool m_lineSeriesChartingEnabledForTab[BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS]; bool m_matrixChartingEnabledForTab[BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS]; ChartMatrixDisplayProperties* m_chartMatrixDisplayPropertiesForTab[BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS]; int32_t m_selectedMapIndices[BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS]; // ADD_NEW_MEMBERS_HERE }; #ifdef __CIFTI_SCALAR_DATA_SERIES_FILE_DECLARE__ // #endif // __CIFTI_SCALAR_DATA_SERIES_FILE_DECLARE__ } // namespace #endif //__CIFTI_SCALAR_DATA_SERIES_FILE_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Files/ConnectivityDataLoaded.cxx000066400000000000000000000365711300200146000267270ustar00rootroot00000000000000 /*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 __CONNECTIVITY_DATA_LOADED_DECLARE__ #include "ConnectivityDataLoaded.h" #undef __CONNECTIVITY_DATA_LOADED_DECLARE__ #include "CaretAssert.h" #include "SceneClassAssistant.h" #include "SceneAttributes.h" #include "SceneClass.h" #include "ScenePrimitiveArray.h" #include "SurfaceFile.h" using namespace caret; /** * \class caret::ConnectivityDataLoaded * \brief Maintains information on loaded brainordinate data. * \ingroup Brain */ /** * Constructor. */ ConnectivityDataLoaded::ConnectivityDataLoaded() : CaretObject() { reset(); m_sceneAssistant = new SceneClassAssistant(); m_sceneAssistant->add("m_rowIndex", &m_rowIndex); m_sceneAssistant->add("m_columnIndex", &m_columnIndex); m_sceneAssistant->add("m_surfaceNumberOfNodes", &m_surfaceNumberOfNodes); m_sceneAssistant->add("m_surfaceStructure", &m_surfaceStructure); m_sceneAssistant->addArray("m_volumeDimensionsIJK", m_volumeDimensionsIJK, 3, -1); m_sceneAssistant->addArray("m_volumeXYZ", m_volumeXYZ, 3, 0.0); reset(); } /** * Destructor. */ ConnectivityDataLoaded::~ConnectivityDataLoaded() { reset(); delete m_sceneAssistant; } /** * Reset the data. */ void ConnectivityDataLoaded::reset() { m_mode = MODE_NONE; m_rowIndex = -1; m_columnIndex = -1; m_surfaceNodeIndices.clear(); m_surfaceNumberOfNodes = 0; m_surfaceStructure = StructureEnum::INVALID; m_volumeDimensionsIJK[0] = -1; m_volumeDimensionsIJK[1] = -1; m_volumeDimensionsIJK[2] = -1; m_volumeXYZ[0] = 0.0; m_volumeXYZ[1] = 0.0; m_volumeXYZ[2] = 0.0; m_voxelIndices.clear(); } /** * @return The mode. */ ConnectivityDataLoaded::Mode ConnectivityDataLoaded::getMode() const { return m_mode; } /** * Get the row that were loaded. * * @param rowIndex * Row that was loaded (may be -1 if none). * @param columnIndex * Column that was loaded (may be -1 if none). */ void ConnectivityDataLoaded::getRowColumnLoading(int64_t& rowIndex, int64_t& columnIndex) const { rowIndex = m_rowIndex; columnIndex = m_columnIndex; } /** * Set the row that were loaded. * * @param rowIndex * Row that was loaded (may be -1 if none). * @param columnIndex * Column that was loaded (may be -1 if none) */ void ConnectivityDataLoaded::setRowColumnLoading(const int64_t rowIndex, const int64_t columnIndex) { reset(); if (rowIndex >= 0) { m_mode = MODE_ROW; m_rowIndex = rowIndex; } else if (columnIndex >= 0) { m_mode = MODE_COLUMN; m_columnIndex = columnIndex; } else { CaretAssertMessage(0, "One or row index or column index should be negative indicating that dimension was not loaded."); } } /** * Get the surface loading information (MODE_SURFACE_NODE) * One of rowIndex or columnIndex will be negative. * * @param structure * The surface structure. * @param surfaceNumberOfNodes * Number of nodes in surface. * @param surfaceNodeIndex * Index of the surface node. * @param rowIndex * Index of row corresponding to the surface node (may be -1 if none). * @param columnIndex * Index of row corresponding to the surface node (may be -1 if none). */ void ConnectivityDataLoaded::getSurfaceNodeLoading(StructureEnum::Enum& structure, int32_t& surfaceNumberOfNodes, int32_t& surfaceNodeIndex, int64_t& rowIndex, int64_t& columnIndex) const { structure = m_surfaceStructure; surfaceNumberOfNodes = m_surfaceNumberOfNodes; if (m_surfaceNodeIndices.empty() == false) { surfaceNodeIndex = m_surfaceNodeIndices[0]; } else { surfaceNodeIndex = -1; } rowIndex = m_rowIndex; columnIndex = m_columnIndex; } /** * Set the surface loading information (MODE_SURFACE_NODE) * One of rowIndex or columnIndex must be negative. * * @param structure * The surface structure. * @param surfaceNumberOfNodes * Number of nodes in surface. * @param surfaceNodeIndex * Index of the surface node. * @param rowIndex * Index of row corresponding to the surface node (may be -1 if none). * @param columnIndex * Index of column corresponding to the surface node (may be -1 if none). */ void ConnectivityDataLoaded::setSurfaceNodeLoading(const StructureEnum::Enum structure, const int32_t surfaceNumberOfNodes, const int32_t surfaceNodeIndex, const int64_t rowIndex, const int64_t columnIndex) { reset(); m_mode = MODE_SURFACE_NODE; m_surfaceStructure = structure; m_surfaceNumberOfNodes = surfaceNumberOfNodes; m_surfaceNodeIndices.push_back(surfaceNodeIndex); m_rowIndex = rowIndex; m_columnIndex = columnIndex; if ((rowIndex >= 0) && (columnIndex >= 0)) { CaretAssertMessage(0, "One of row or column index must be negative."); } } /** * Get the surface average node loading information (MODE_SURFACE_NODE_AVERAGE) * * @param structure * The surface structure. * @param surfaceNumberOfNodes * Number of nodes in surface. * @param surfaceNodeIndices * Indices of the surface nodes. */ void ConnectivityDataLoaded::getSurfaceAverageNodeLoading(StructureEnum::Enum& structure, int32_t& surfaceNumberOfNodes, std::vector& surfaceNodeIndices) const { structure = m_surfaceStructure; surfaceNumberOfNodes = m_surfaceNumberOfNodes; surfaceNodeIndices = m_surfaceNodeIndices; } /** * Set the surface average node loading information (MODE_SURFACE_NODE_AVERAGE) * * @param structure * The surface structure. * @param surfaceNumberOfNodes * Number of nodes in surface. * @param surfaceNodeIndices * Indices of the surface nodes. */ void ConnectivityDataLoaded::setSurfaceAverageNodeLoading(const StructureEnum::Enum structure, const int32_t surfaceNumberOfNodes, const std::vector& surfaceNodeIndices) { reset(); m_mode = MODE_SURFACE_NODE_AVERAGE; m_surfaceStructure = structure; m_surfaceNumberOfNodes = surfaceNumberOfNodes; m_surfaceNodeIndices = surfaceNodeIndices; m_rowIndex = -1; m_columnIndex = -1; } /** * Get the volume loading XYZ coordinate (MODE_VOXEL_XYZ). * * @param volumeXYZ * Coordinate of location. * @param rowIndex * Index of row corresponding to the voxel (may be -1 if none). * @param columnIndex * Index of column corresponding to the voxel (may be -1 if none). */ void ConnectivityDataLoaded::getVolumeXYZLoading(float volumeXYZ[3], int64_t& rowIndex, int64_t& columnIndex) const { volumeXYZ[0] = m_volumeXYZ[0]; volumeXYZ[1] = m_volumeXYZ[1]; volumeXYZ[2] = m_volumeXYZ[2]; rowIndex = m_rowIndex; columnIndex = m_columnIndex; } /** * Set the volume loading XYZ coordinate (MODE_VOXEL_XYZ). * * @param volumeXYZ * Coordinate of location. * @param rowIndex * Index of row corresponding to the voxel(may be -1 if none). * @param columnIndex * Index of column corresponding to the voxel (may be -1 if none). */ void ConnectivityDataLoaded::setVolumeXYZLoading(const float volumeXYZ[3], const int64_t rowIndex, const int64_t columnIndex) { reset(); m_mode = MODE_VOXEL_XYZ; m_volumeXYZ[0] = volumeXYZ[0]; m_volumeXYZ[1] = volumeXYZ[1]; m_volumeXYZ[2] = volumeXYZ[2]; m_rowIndex = rowIndex; m_columnIndex = columnIndex; } /** * Get the voxel average loading voxel IJK indices (MODE_VOXEL_IJK_AVERAGE) * * @param voxelIndicesIJK * Indices of the voxels. */ void ConnectivityDataLoaded::getVolumeAverageVoxelLoading(int64_t volumeDimensionsIJK[3], std::vector& voxelIndicesIJK) const { volumeDimensionsIJK[0] = m_volumeDimensionsIJK[0]; volumeDimensionsIJK[1] = m_volumeDimensionsIJK[1]; volumeDimensionsIJK[2] = m_volumeDimensionsIJK[2]; voxelIndicesIJK = m_voxelIndices; } /** * Set the voxel average loading voxel IJK indices (MODE_VOXEL_IJK_AVERAGE) * * @param voxelIndicesIJK * Indices of the voxels. */ void ConnectivityDataLoaded::setVolumeAverageVoxelLoading(const int64_t volumeDimensionsIJK[3], const std::vector& voxelIndicesIJK) { reset(); m_volumeDimensionsIJK[0] = volumeDimensionsIJK[0]; m_volumeDimensionsIJK[1] = volumeDimensionsIJK[1]; m_volumeDimensionsIJK[2] = volumeDimensionsIJK[2]; m_mode = MODE_VOXEL_IJK_AVERAGE; m_voxelIndices = voxelIndicesIJK; m_rowIndex = -1; m_columnIndex = -1; } /** * Restore the state of an instance of a class. * * @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. May be NULL for some types of scenes. */ void ConnectivityDataLoaded::restoreFromScene(const SceneAttributes* sceneAttributes, const SceneClass* sceneClass) { reset(); if (sceneClass == NULL) { return; } m_sceneAssistant->restoreMembers(sceneAttributes, sceneClass); m_mode = MODE_NONE; const AString modeName = sceneClass->getStringValue("m_mode"); if (modeName == "MODE_NONE") { m_mode = MODE_NONE; } else if (modeName == "MODE_ROW") { m_mode = MODE_ROW; } else if (modeName == "MODE_COLUMN") { m_mode = MODE_COLUMN; } else if (modeName == "MODE_SURFACE_NODE_AVERAGE") { m_mode = MODE_SURFACE_NODE_AVERAGE; } else if (modeName == "MODE_SURFACE_NODE") { m_mode = MODE_SURFACE_NODE; } else if (modeName == "MODE_VOXEL_XYZ") { m_mode = MODE_VOXEL_XYZ; } else if (modeName == "MODE_VOXEL_IJK_AVERAGE") { m_mode = MODE_VOXEL_IJK_AVERAGE; } else { sceneAttributes->addToErrorMessage("Unrecognized mode=" + modeName); return; } const ScenePrimitiveArray* surfaceNodeIndicesArray = sceneClass->getPrimitiveArray("m_surfaceNodeIndices"); if (surfaceNodeIndicesArray != NULL) { const int32_t numNodeIndices = surfaceNodeIndicesArray->getNumberOfArrayElements(); m_surfaceNodeIndices.reserve(numNodeIndices); for (int32_t i = 0; i < numNodeIndices; i++) { m_surfaceNodeIndices.push_back(surfaceNodeIndicesArray->integerValue(i)); } } const ScenePrimitiveArray* voxelIndicesArray = sceneClass->getPrimitiveArray("m_voxelIndices"); if (voxelIndicesArray != NULL) { const int64_t numIndices = voxelIndicesArray->getNumberOfArrayElements(); const int64_t numVoxelIndices = numIndices / 3; for (int64_t i = 0; i < numVoxelIndices; i++) { const int64_t i3 = i * 3; VoxelIJK voxelIJK(voxelIndicesArray->integerValue(i3), voxelIndicesArray->integerValue(i3 + 1), voxelIndicesArray->integerValue(i3 + 2)); m_voxelIndices.push_back(voxelIJK); } } } /** * Create a scene for an instance of a class. * * @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. * * @return Pointer to SceneClass object representing the state of * this object. Under some circumstances a NULL pointer may be * returned. Caller will take ownership of returned object. */ SceneClass* ConnectivityDataLoaded::saveToScene(const SceneAttributes* sceneAttributes, const AString& instanceName) { SceneClass* sceneClass = new SceneClass(instanceName, "ConnectivityDataLoaded", 1); m_sceneAssistant->saveMembers(sceneAttributes, sceneClass); AString modeName = "MODE_NONE"; switch (m_mode) { case MODE_NONE: modeName = "MODE_NONE"; break; case MODE_ROW: modeName = "MODE_ROW"; break; case MODE_COLUMN: modeName = "MODE_COLUMN"; break; case MODE_SURFACE_NODE: modeName = "MODE_SURFACE_NODE"; break; case MODE_SURFACE_NODE_AVERAGE: modeName = "MODE_SURFACE_NODE_AVERAGE"; break; case MODE_VOXEL_XYZ: modeName = "MODE_VOXEL_XYZ"; break; case MODE_VOXEL_IJK_AVERAGE: modeName = "MODE_VOXEL_IJK_AVERAGE"; break; } sceneClass->addString("m_mode", modeName); if (m_surfaceNodeIndices.empty() == false) { sceneClass->addIntegerArray("m_surfaceNodeIndices", &m_surfaceNodeIndices[0], m_surfaceNodeIndices.size()); } if (m_voxelIndices.empty() == false) { const int32_t numVoxels = m_voxelIndices.size(); std::vector indices; for (int32_t i = 0; i < numVoxels; i++) { indices.push_back(m_voxelIndices[i].m_ijk[0]); indices.push_back(m_voxelIndices[i].m_ijk[1]); indices.push_back(m_voxelIndices[i].m_ijk[2]); } sceneClass->addIntegerArray("m_voxelIndices", &indices[0], indices.size()); } return sceneClass; } connectome-workbench-1.2.3+git41-gc4c6c90/src/Files/ConnectivityDataLoaded.h000066400000000000000000000115171300200146000263450ustar00rootroot00000000000000#ifndef __CONNECTIVITY_DATA_LOADED_H__ #define __CONNECTIVITY_DATA_LOADED_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 "SceneableInterface.h" #include "StructureEnum.h" #include "VoxelIJK.h" namespace caret { class SceneClassAssistant; class SurfaceFile; class ConnectivityDataLoaded : public CaretObject, public SceneableInterface { public: ConnectivityDataLoaded(); virtual ~ConnectivityDataLoaded(); private: ConnectivityDataLoaded(const ConnectivityDataLoaded&); ConnectivityDataLoaded& operator=(const ConnectivityDataLoaded&); public: enum Mode { MODE_NONE, MODE_ROW, MODE_COLUMN, MODE_SURFACE_NODE, MODE_SURFACE_NODE_AVERAGE, MODE_VOXEL_XYZ, MODE_VOXEL_IJK_AVERAGE }; void reset(); Mode getMode() const; void getRowColumnLoading(int64_t& rowIndex, int64_t& columnIndex) const; void setRowColumnLoading(const int64_t rowIndex, const int64_t columnIndex); void getSurfaceNodeLoading(StructureEnum::Enum& structure, int32_t& surfaceNumberOfNodes, int32_t& surfaceNodeIndex, int64_t& rowIndex, int64_t& columnIndex) const; void setSurfaceNodeLoading(const StructureEnum::Enum structure, const int32_t surfaceNumberOfNodes, const int32_t surfaceNodeIndex, const int64_t rowIndex, const int64_t columnIndex); void getSurfaceAverageNodeLoading(StructureEnum::Enum& structure, int32_t& surfaceNumberOfNode, std::vector& surfaceNodeIndices) const; void setSurfaceAverageNodeLoading(const StructureEnum::Enum structure, const int32_t surfaceNumberOfNodes, const std::vector& surfaceNodeIndices); void getVolumeXYZLoading(float volumeXYZ[3], int64_t& rowIndex, int64_t& columnIndex) const; void setVolumeXYZLoading(const float volumeXYZ[3], const int64_t rowIndex, const int64_t columnIndex); void getVolumeAverageVoxelLoading(int64_t volumeDimensionsIJK[3], std::vector& voxelIndicesIJK) const; void setVolumeAverageVoxelLoading(const int64_t volumeDimensionsIJK[3], const std::vector& voxelIndicesIJK); virtual void restoreFromScene(const SceneAttributes* sceneAttributes, const SceneClass* sceneClass); virtual SceneClass* saveToScene(const SceneAttributes* sceneAttributes, const AString& instanceName); private: // ADD_NEW_MEMBERS_HERE SceneClassAssistant* m_sceneAssistant; Mode m_mode; int32_t m_rowIndex; int32_t m_columnIndex; StructureEnum::Enum m_surfaceStructure; int32_t m_surfaceNumberOfNodes; std::vector m_surfaceNodeIndices; std::vector m_voxelIndices; int32_t m_volumeDimensionsIJK[3]; float m_volumeXYZ[3]; }; #ifdef __CONNECTIVITY_DATA_LOADED_DECLARE__ // #endif // __CONNECTIVITY_DATA_LOADED_DECLARE__ } // namespace #endif // __CONNECTIVITY_DATA_LOADED_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Files/ControlPointFile.cxx000066400000000000000000000261071300200146000255720ustar00rootroot00000000000000 /*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 __CONTROL_POINT_FILE_DECLARE__ #include "ControlPointFile.h" #undef __CONTROL_POINT_FILE_DECLARE__ #include "CaretAssert.h" #include "ControlPoint3D.h" #include "DataFileException.h" #include "GiftiMetaData.h" #include "Matrix4x4.h" #include "SceneClass.h" #include "SceneClassArray.h" #include "SceneClassAssistant.h" using namespace caret; /** * \class caret::ControlPointFile * \brief File containing control points used for data transformations. * \ingroup Files */ /** * Constructor. */ ControlPointFile::ControlPointFile() : CaretDataFile(DataFileTypeEnum::UNKNOWN) { m_metadata.grabNew(new GiftiMetaData()); m_sceneAssistant.grabNew(new SceneClassAssistant()); m_landmarkTransformationMatrix.grabNew(new Matrix4x4()); // addControlPoint(ControlPoint3D(151, 147, 0, -42, -81, 6)); // addControlPoint(ControlPoint3D(263, 126, 0, -10, -87, 6)); // addControlPoint(ControlPoint3D(250, 491, 0, -13, 20, 6)); // addControlPoint(ControlPoint3D(48, 105, 0, -58, -13, 11)); // addControlPoint(ControlPoint3D(140, 106, 0, -12, -13, 13)); // addControlPoint(ControlPoint3D(133, 196, 0, -17, -13, 58)); } /** * Destructor. */ ControlPointFile::~ControlPointFile() { clearPrivate(); } /** * @return True if this file is empty, else false. */ bool ControlPointFile::isEmpty() const { return m_controlPoints.empty(); } /** * @return The structure for this file. */ StructureEnum::Enum ControlPointFile::getStructure() const { return StructureEnum::ALL; } /** * Set the structure for this file. * @param structure * New structure for this file. */ void ControlPointFile::setStructure(const StructureEnum::Enum /*structure*/) { /* nothing */ } /** * @return Get access to the file's metadata. */ GiftiMetaData* ControlPointFile::getFileMetaData() { return m_metadata.getPointer(); } /** * @return Get access to unmodifiable file's metadata. */ const GiftiMetaData* ControlPointFile::getFileMetaData() const { return m_metadata.getPointer(); } /** * Clear the content of this file. * This method is virtual so do not call from constructor/destructor. */ void ControlPointFile::clear() { const AString nameOfFile = getFileName(); CaretDataFile::clear(); clearPrivate(); } /** * Clear the content of this file. */ void ControlPointFile::clearPrivate() { m_metadata->clear(); removeAllControlPoints(); m_landmarkTransformationMatrix->identity(); } /** * Add information about the content of this file. * * @param dataFileInformation * Will contain information about this file. */ void ControlPointFile::addToDataFileContentInformation(DataFileContentInformation& dataFileInformation) { CaretDataFile::addToDataFileContentInformation(dataFileInformation); } /** * @return true if file is modified, else false. */ bool ControlPointFile::isModified() const { if (CaretDataFile::isModified()) { return true; } if (m_metadata->isModified()) { return true; } for (std::vector::const_iterator iter = m_controlPoints.begin(); iter != m_controlPoints.end(); iter++) { if ((*iter)->isModified()) { return true; } } return false; } /** * Clear the modified status of this file. */ void ControlPointFile::clearModified() { CaretDataFile::clearModified(); m_metadata->clearModified(); for (std::vector::const_iterator iter = m_controlPoints.begin(); iter != m_controlPoints.end(); iter++) { (*iter)->clearModified(); } m_landmarkTransformationMatrix->clearModified(); } /** * @return The number of control points. */ int32_t ControlPointFile::getNumberOfControlPoints() const { return m_controlPoints.size(); } /** * Add a control point. * * @param controlPoint * Control point to add. */ void ControlPointFile::addControlPoint(const ControlPoint3D& controlPoint) { m_controlPoints.push_back(new ControlPoint3D(controlPoint)); setModified(); } /** * @return Control point at the given index (const method). * * @param index * Index of the control point. */ const ControlPoint3D* ControlPointFile::getControlPointAtIndex(const int32_t index) const { CaretAssertVectorIndex(m_controlPoints, index); return m_controlPoints[index]; } /** * @return Control point at the given index. * * @param index * Index of the control point. */ ControlPoint3D* ControlPointFile::getControlPointAtIndex(const int32_t index) { CaretAssertVectorIndex(m_controlPoints, index); return m_controlPoints[index]; } /** * Remove all of the control points. */ void ControlPointFile::removeAllControlPoints() { for (std::vector::iterator iter = m_controlPoints.begin(); iter != m_controlPoints.end(); iter++) { delete *iter; } m_controlPoints.clear(); } /** * Remote the control point at the given index. * * @param index * Index of the control point. */ void ControlPointFile::removeControlPointAtIndex(const int32_t index) { CaretAssertVectorIndex(m_controlPoints, index); delete m_controlPoints[index]; m_controlPoints.erase(m_controlPoints.begin() + index); setModified(); } /** * Update the landmark transformation matrix from the control points. * The transformed position will be set in the control points if the * matrix is successfully created. * * @param errorMessageOutk * Contains error information upon exit. * @return True if landmark transformation matrix was successfully update, * else false. */ bool ControlPointFile::updateLandmarkTransformationMatrix(AString& errorMessageOut) { m_landmarkTransformationMatrix->identity(); errorMessageOut.clear(); bool successFlag = m_landmarkTransformationMatrix->createLandmarkTransformMatrix(m_controlPoints, errorMessageOut); const int32_t numCP = getNumberOfControlPoints(); for (int32_t icp = 0; icp < numCP; icp++) { float transformedXYZ[3] = { 0.0, 0.0, 0.0 }; CaretAssertVectorIndex(m_controlPoints, icp); ControlPoint3D* cp = m_controlPoints[icp]; if (successFlag) { cp->getSourceXYZ(transformedXYZ); m_landmarkTransformationMatrix->multiplyPoint3(transformedXYZ); } cp->setTransformedXYZ(transformedXYZ); } return successFlag; } /** * @return The landmark transformation matrix. */ Matrix4x4* ControlPointFile::getLandmarkTransformationMatrix() { return m_landmarkTransformationMatrix; } /** * @return The landmark transformation matrix (const method). */ const Matrix4x4* ControlPointFile::getLandmarkTransformationMatrix() const { return m_landmarkTransformationMatrix; } /** * Read the data file. * * @param filename * Name of the data file. * @throws DataFileException * If the file was not successfully read. */ void ControlPointFile::readFile(const AString& filename) { clear(); checkFileReadability(filename); throw DataFileException("Reading of Control Point files not implemented"); // AnnotationFileXmlReader reader; // reader.readFile(filename, // this); // // updateUniqueKeysAfterReadingFile(); setFileName(filename); clearModified(); } /** * Write the data file. * * @param filename * Name of the data file. * @throws DataFileException * If the file was not successfully written. */ void ControlPointFile::writeFile(const AString& filename) { checkFileWritability(filename); throw DataFileException("Writing of Control Point files not implemented"); setFileName(filename); // AnnotationFileXmlWriter writer; // writer.writeFile(this); clearModified(); } /** * 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 ControlPointFile::saveFileDataToScene(const SceneAttributes* sceneAttributes, SceneClass* sceneClass) { m_sceneAssistant->saveMembers(sceneAttributes, sceneClass); std::vector controlPointsVector; const int32_t numCP = getNumberOfControlPoints(); for (int32_t icp = 0; icp < numCP; icp++) { const QString name("ControlPoint" + QString::number(icp)); controlPointsVector.push_back(m_controlPoints[icp]->saveToScene(sceneAttributes, name)); } SceneClassArray* classArray = new SceneClassArray("controlPointsArray", controlPointsVector); sceneClass->addChild(classArray); } /** * 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 ControlPointFile::restoreFileDataFromScene(const SceneAttributes* sceneAttributes, const SceneClass* sceneClass) { if (sceneClass == NULL) { return; } m_sceneAssistant->restoreMembers(sceneAttributes, sceneClass); const SceneClassArray* controlPointsArray = sceneClass->getClassArray("controlPointsArray"); if (controlPointsArray != NULL) { const int32_t numElements = controlPointsArray->getNumberOfArrayElements(); for (int32_t i = 0; i < numElements; i++) { const SceneClass* controlPointClass = controlPointsArray->getClassAtIndex(i); ControlPoint3D controlPoint(0, 0, 0, 0, 0, 0); controlPoint.restoreFromScene(sceneAttributes, controlPointClass); addControlPoint(controlPoint); } } } connectome-workbench-1.2.3+git41-gc4c6c90/src/Files/ControlPointFile.h000066400000000000000000000070411300200146000252130ustar00rootroot00000000000000#ifndef __CONTROL_POINT_FILE_H__ #define __CONTROL_POINT_FILE_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 "CaretDataFile.h" #include "CaretPointer.h" namespace caret { class ControlPoint3D; class Matrix4x4; class SceneClassAssistant; class ControlPointFile : public CaretDataFile { public: ControlPointFile(); virtual ~ControlPointFile(); bool isEmpty() const; virtual void clear(); virtual StructureEnum::Enum getStructure() const; virtual void setStructure(const StructureEnum::Enum structure); virtual GiftiMetaData* getFileMetaData(); virtual const GiftiMetaData* getFileMetaData() const; virtual void readFile(const AString& filename); virtual void writeFile(const AString& filename); virtual bool isModified() const; virtual void clearModified(); virtual void addToDataFileContentInformation(DataFileContentInformation& dataFileInformation); int32_t getNumberOfControlPoints() const; void addControlPoint(const ControlPoint3D& controlPoint); const ControlPoint3D* getControlPointAtIndex(const int32_t index) const; ControlPoint3D* getControlPointAtIndex(const int32_t index); void removeAllControlPoints(); void removeControlPointAtIndex(const int32_t index); bool updateLandmarkTransformationMatrix(AString& errorMessageOut); Matrix4x4* getLandmarkTransformationMatrix(); const Matrix4x4* getLandmarkTransformationMatrix() const; // ADD_NEW_METHODS_HERE protected: virtual void saveFileDataToScene(const SceneAttributes* sceneAttributes, SceneClass* sceneClass); virtual void restoreFileDataFromScene(const SceneAttributes* sceneAttributes, const SceneClass* sceneClass); private: ControlPointFile(const ControlPointFile&); ControlPointFile& operator=(const ControlPointFile&); void clearPrivate(); CaretPointer m_metadata; CaretPointer m_sceneAssistant; std::vector m_controlPoints; CaretPointer m_landmarkTransformationMatrix; // ADD_NEW_MEMBERS_HERE }; #ifdef __CONTROL_POINT_FILE_DECLARE__ // #endif // __CONTROL_POINT_FILE_DECLARE__ } // namespace #endif //__CONTROL_POINT_FILE_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Files/EventCaretMappableDataFilesGet.cxx000066400000000000000000000054201300200146000302520ustar00rootroot00000000000000/*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 "CaretAssert.h" #include "CaretMappableDataFile.h" #include "EventCaretMappableDataFilesGet.h" using namespace caret; /** * Constructor for ALL map data files. */ EventCaretMappableDataFilesGet::EventCaretMappableDataFilesGet() : Event(EventTypeEnum::EVENT_CARET_MAPPABLE_DATA_FILES_GET), m_mode(MODE_ANY_DATA_FILE_TYPE), m_oneDataFileType(DataFileTypeEnum::UNKNOWN) { } /** * Constructor for map data files of the given file type. * * @param dataFileType * Type of data files requested. */ EventCaretMappableDataFilesGet::EventCaretMappableDataFilesGet(const DataFileTypeEnum::Enum dataFileType) : Event(EventTypeEnum::EVENT_CARET_MAPPABLE_DATA_FILES_GET), m_mode(MODE_ONE_DATA_FILE_TYPE), m_oneDataFileType(dataFileType) { } /** * Destructor. */ EventCaretMappableDataFilesGet::~EventCaretMappableDataFilesGet() { } /** * Add a map data file. * @param mapDataFile * Map data file that is added. */ void EventCaretMappableDataFilesGet::addFile(CaretMappableDataFile* mapDataFile) { CaretAssert(mapDataFile); if (mapDataFile->getNumberOfMaps() <= 0) { return; } const DataFileTypeEnum::Enum mapDataFileType = mapDataFile->getDataFileType(); /* * No surface files */ if (mapDataFileType == DataFileTypeEnum::SURFACE) { return; } /* * Based upon mode, perform additional filtering of file data type */ switch (m_mode) { case MODE_ONE_DATA_FILE_TYPE: if (mapDataFileType != m_oneDataFileType) { return; } break; case MODE_ANY_DATA_FILE_TYPE: break; } m_allCaretMappableDataFiles.push_back(mapDataFile); } /** * Get all map data files. * * @param allFilesOut * All map data files output. */ void EventCaretMappableDataFilesGet::getAllFiles(std::vector& allFilesOut) const { allFilesOut = m_allCaretMappableDataFiles; } connectome-workbench-1.2.3+git41-gc4c6c90/src/Files/EventCaretMappableDataFilesGet.h000066400000000000000000000040431300200146000276770ustar00rootroot00000000000000#ifndef __EVENT_CARET_MAPPABLE_DATA_FILES_GET_H__ #define __EVENT_CARET_MAPPABLE_DATA_FILES_GET_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 CaretMappableDataFile; /// Event that gets all caret mappable data files. class EventCaretMappableDataFilesGet : public Event { public: EventCaretMappableDataFilesGet(); EventCaretMappableDataFilesGet(const DataFileTypeEnum::Enum dataFileType); virtual ~EventCaretMappableDataFilesGet(); void addFile(CaretMappableDataFile* mapDataFile); void getAllFiles(std::vector& allFilesOut) const; private: enum Mode { MODE_ANY_DATA_FILE_TYPE, MODE_ONE_DATA_FILE_TYPE }; EventCaretMappableDataFilesGet(const EventCaretMappableDataFilesGet&); EventCaretMappableDataFilesGet& operator=(const EventCaretMappableDataFilesGet&); const Mode m_mode; const DataFileTypeEnum::Enum m_oneDataFileType; std::vector m_allCaretMappableDataFiles; }; } // namespace #endif // __EVENT_CARET_MAPPABLE_DATA_FILES_GET_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Files/EventChartMatrixParcelYokingValidation.cxx000066400000000000000000000175011300200146000321110ustar00rootroot00000000000000 /*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_MATRIX_PARCEL_LOADING_YOKING_DECLARE__ #include "EventChartMatrixParcelYokingValidation.h" #undef __EVENT_CHART_MATRIX_PARCEL_LOADING_YOKING_DECLARE__ #include "CaretAssert.h" #include "CaretMappableDataFile.h" #include "ChartableMatrixParcelInterface.h" #include "EventTypeEnum.h" using namespace caret; /** * \class caret::EventChartMatrixParcelYokingValidation * \brief Event for coordination of Matrix Chart yoking. * \ingroup Files */ /** * Constructor for validating that the given file is compatible with any * current yoking for the given yoking group. After sending this event, * call isYokingCompatible() to validate the yoking selection. * * @param chartableInterface * The file for which yoking compatibility is verified. * @param yokingGroup * The selected yoking group. */ EventChartMatrixParcelYokingValidation::EventChartMatrixParcelYokingValidation(const ChartableMatrixParcelInterface* chartableInterface, const YokingGroupEnum::Enum yokingGroup) : Event(EventTypeEnum::EVENT_CHART_MATRIX_YOKING_VALIDATION), m_mode(MODE_VALIDATE_YOKING), m_chartableInterface(chartableInterface), m_yokingGroup(yokingGroup) { CaretAssert(chartableInterface); } /** * Destructor. */ EventChartMatrixParcelYokingValidation::~EventChartMatrixParcelYokingValidation() { } /** * @return The mode of this event (apply or validate). */ EventChartMatrixParcelYokingValidation::Mode EventChartMatrixParcelYokingValidation::getMode() const { return m_mode; } /** * Add chartable interface for validating yoking compatibility. * * @param chartableInterface * A file for which yoking compatibility is verified with the file passed * to the constructor. * @param selectedRowOrColumnIndex * Selected row or column index in the file. */ void EventChartMatrixParcelYokingValidation::addValidateYokingChartableInterface(const ChartableMatrixParcelInterface* chartableInterface, const int32_t selectedRowOrColumnIndex) { CaretAssert(chartableInterface); int32_t numberOfRowsColumns = -1; AString loadingDimensionName; int32_t numRows = -1; int32_t numCols = -1; m_chartableInterface->getMatrixDimensions(numRows, numCols); switch (m_chartableInterface->getMatrixLoadingDimension()) { case ChartMatrixLoadingDimensionEnum::CHART_MATRIX_LOADING_BY_COLUMN: numberOfRowsColumns = numCols; loadingDimensionName = "columns"; break; case ChartMatrixLoadingDimensionEnum::CHART_MATRIX_LOADING_BY_ROW: numberOfRowsColumns = numRows; loadingDimensionName = "rows"; break; } if (chartableInterface != m_chartableInterface) { if ((chartableInterface->getYokingGroup() == m_yokingGroup) && (chartableInterface->getYokingGroup() != YokingGroupEnum::YOKING_GROUP_OFF)) { int32_t chartInterNumRowsColumns = -1; AString chartDimensionsName; int32_t chartRows = -1; int32_t chartCols = -1; chartableInterface->getMatrixDimensions(chartRows, chartCols); switch (chartableInterface->getMatrixLoadingDimension()) { case ChartMatrixLoadingDimensionEnum::CHART_MATRIX_LOADING_BY_COLUMN: chartInterNumRowsColumns = chartCols; chartDimensionsName = "columns"; break; case ChartMatrixLoadingDimensionEnum::CHART_MATRIX_LOADING_BY_ROW: chartInterNumRowsColumns = chartRows; chartDimensionsName = "rows"; break; } if (numberOfRowsColumns != chartInterNumRowsColumns) { if (m_incompatibilityMessage.isEmpty()) { m_incompatibilityMessage.appendWithNewLine(m_chartableInterface->getMatrixChartCaretMappableDataFile()->getFileNameNoPath() + " is loading from " + AString::number(numberOfRowsColumns) + " " + loadingDimensionName); m_incompatibilityMessage.appendWithNewLine("and is incompatible with: "); } m_incompatibilityMessage.appendWithNewLine(" " + chartableInterface->getMatrixChartCaretMappableDataFile()->getFileNameNoPath() + " is loading from " + AString::number(chartInterNumRowsColumns) + " " + chartDimensionsName); } /* * For the map key is row/column index and value is number of times that * row column index is used. */ std::map::iterator iter = m_compatibleRowColumnIndicesCount.find(selectedRowOrColumnIndex); if (iter != m_compatibleRowColumnIndicesCount.end()) { iter->second++; } else { m_compatibleRowColumnIndicesCount.insert(std::make_pair(selectedRowOrColumnIndex, 1)); } } } } /** * Is the yoking for the file passed to the constructor validated with * other files yoked to the yoking group? * * @param messageOut * Contains information on compatibility. * @param selectedRowOrColumnIndexOut * Row or column index that should be selected if yoking is compatible. * @return * True if compatible, else false. */ bool EventChartMatrixParcelYokingValidation::isValidateYokingCompatible(AString& messageOut, int32_t& selectedRowOrColumnIndexOut) const { selectedRowOrColumnIndexOut = -1; messageOut = m_incompatibilityMessage; /* * For the map key is row/column index and value is number of times that * row column index is used. */ int32_t maxCount = 0; for (std::map::const_iterator iter = m_compatibleRowColumnIndicesCount.begin(); iter != m_compatibleRowColumnIndicesCount.end(); iter++) { if (iter->second > maxCount) { maxCount = iter->second; selectedRowOrColumnIndexOut = iter->first; } } if (messageOut.isEmpty()) { return true; } return false; } /** * @return Chartable interface for which this event was issued. */ const ChartableMatrixParcelInterface* EventChartMatrixParcelYokingValidation::getChartableMatrixParcelInterface() const { return m_chartableInterface; } connectome-workbench-1.2.3+git41-gc4c6c90/src/Files/EventChartMatrixParcelYokingValidation.h000066400000000000000000000054741300200146000315440ustar00rootroot00000000000000#ifndef __EVENT_CHART_MATRIX_PARCEL_LOADING_YOKING_H__ #define __EVENT_CHART_MATRIX_PARCEL_LOADING_YOKING_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 "Event.h" #include "YokingGroupEnum.h" namespace caret { class ChartableMatrixParcelInterface; class EventChartMatrixParcelYokingValidation : public Event { public: enum Mode { MODE_APPLY_YOKING, MODE_VALIDATE_YOKING }; EventChartMatrixParcelYokingValidation(const ChartableMatrixParcelInterface* chartableInterface, const YokingGroupEnum::Enum yokingGroup); virtual ~EventChartMatrixParcelYokingValidation(); Mode getMode() const; void addValidateYokingChartableInterface(const ChartableMatrixParcelInterface* chartableInterface, const int32_t selectedRowOrColumnIndex); bool isValidateYokingCompatible(AString& messageOut, int32_t& selectedRowOrColumnIndexOut) const; const ChartableMatrixParcelInterface* getChartableMatrixParcelInterface() const; // ADD_NEW_METHODS_HERE private: EventChartMatrixParcelYokingValidation(const EventChartMatrixParcelYokingValidation&); EventChartMatrixParcelYokingValidation& operator=(const EventChartMatrixParcelYokingValidation&); const Mode m_mode; const ChartableMatrixParcelInterface* m_chartableInterface; const YokingGroupEnum::Enum m_yokingGroup; AString m_incompatibilityMessage; std::map m_compatibleRowColumnIndicesCount; // ADD_NEW_MEMBERS_HERE }; #ifdef __EVENT_CHART_MATRIX_PARCEL_LOADING_YOKING_DECLARE__ // #endif // __EVENT_CHART_MATRIX_PARCEL_LOADING_YOKING_DECLARE__ } // namespace #endif //__EVENT_CHART_MATRIX_PARCEL_LOADING_YOKING_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Files/EventGetDisplayedDataFiles.cxx000066400000000000000000000137061300200146000274760ustar00rootroot00000000000000 /*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 __EVENT_GET_DISPLAYED_DATA_FILES_DECLARE__ #include "EventGetDisplayedDataFiles.h" #undef __EVENT_GET_DISPLAYED_DATA_FILES_DECLARE__ #include "CaretAssert.h" #include "EventTypeEnum.h" #include "SurfaceFile.h" using namespace caret; /** * \class caret::EventGetDisplayedDataFiles * \brief Find data files displayed in some or all tabs. * \ingroup Files */ /** * Constructor for finding data files displayed in the given tab indices. * * param windowIndices * Indices of windows for displayed data files. * param tabIndices * Indices of tabs for displayed data files. */ EventGetDisplayedDataFiles::EventGetDisplayedDataFiles(const std::vector& windowIndices, const std::vector& tabIndices) : Event(EventTypeEnum::EVENT_GET_DISPLAYED_DATA_FILES) { m_tabIndices.insert(tabIndices.begin(), tabIndices.end()); m_windowIndices.insert(windowIndices.begin(), windowIndices.end()); } /** * Destructor. */ EventGetDisplayedDataFiles::~EventGetDisplayedDataFiles() { } /** * Is the tab index one for determining displayed data files. * * @param tabIndex * Index for displayed data files. * @return * True if the tab is one determining displayed data files. */ bool EventGetDisplayedDataFiles::isTestForDisplayedDataFileInTabIndex(const int32_t tabIndex) const { if (m_tabIndices.find(tabIndex) != m_tabIndices.end()) { return true; } return false; } /** * Is the window index one for determining displayed data files. * * @param windowIndex * Index for displayed data files. * @return * True if the window is one determining displayed data files. */ bool EventGetDisplayedDataFiles::isTestForDisplayedDataFileInWindowIndex(const int32_t windowIndex) const { if (m_windowIndices.find(windowIndex) != m_windowIndices.end()) { return true; } return false; } /** * Is the given surface structure displayed? * * @param surfaceStructure * The surface structure. * @return * True if the structure is displayed, else false. */ bool EventGetDisplayedDataFiles::isTestForDisplayedSurfaceStructure(const StructureEnum::Enum surfaceStructure) const { setupSurfaceStrucutures(); if (std::find(m_surfaceStructures.begin(), m_surfaceStructures.end(), surfaceStructure) != m_surfaceStructures.end()) { return true; } return false; } /** * Add the given file as a displayed data file. * * @param caretDataFile * Data file that is displayed. */ void EventGetDisplayedDataFiles::addDisplayedDataFile(const CaretDataFile* caretDataFile) { m_displayedDataFiles.insert(caretDataFile); } /* * Is the given data file displayed? * * @param caretDataFile * Caret data file for testing displayed in a tab. * @return * True if the file is displayed, else false. */ bool EventGetDisplayedDataFiles::isDataFileDisplayed(const CaretDataFile* caretDataFile) const { if (m_displayedDataFiles.find(caretDataFile) != m_displayedDataFiles.end()) { return true; } return false; } /** * @return The displayed data files in a set. */ std::set EventGetDisplayedDataFiles::getDisplayedDataFiles() const { return m_displayedDataFiles; } /** * @return The displayed surface structures. Must be called * AFTER event completes. */ std::vector EventGetDisplayedDataFiles::getDisplayedSurfaceStructures() const { setupSurfaceStrucutures(); return m_surfaceStructures; } /** * Setup the surface structures. */ void EventGetDisplayedDataFiles::setupSurfaceStrucutures() const { if ( ! m_surfaceStructuresValid) { std::set structureSet; for (std::set::const_iterator dataFileIter = m_displayedDataFiles.begin(); dataFileIter != m_displayedDataFiles.end(); dataFileIter++) { const CaretDataFile* dataFile = *dataFileIter; CaretAssert(dataFile); if (dataFile->getDataFileType() == DataFileTypeEnum::SURFACE) { const SurfaceFile* surfaceFile = dynamic_cast(dataFile); CaretAssert(surfaceFile); structureSet.insert(surfaceFile->getStructure()); } } m_surfaceStructures.insert(m_surfaceStructures.end(), structureSet.begin(), structureSet.end()); m_surfaceStructuresValid = true; } } /** * @return The tab indices. */ std::vector EventGetDisplayedDataFiles::getTabIndices() const { std::vector tabVector(m_tabIndices.begin(), m_tabIndices.end()); return tabVector; } /** * @return The window indices. */ std::vector EventGetDisplayedDataFiles::getWindowIndices() const { std::vector windowVector(m_windowIndices.begin(), m_windowIndices.end()); return windowVector; } connectome-workbench-1.2.3+git41-gc4c6c90/src/Files/EventGetDisplayedDataFiles.h000066400000000000000000000055611300200146000271230ustar00rootroot00000000000000#ifndef __EVENT_GET_DISPLAYED_DATA_FILES_H__ #define __EVENT_GET_DISPLAYED_DATA_FILES_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 "Event.h" #include "StructureEnum.h" namespace caret { class CaretDataFile; class EventGetDisplayedDataFiles : public Event { public: EventGetDisplayedDataFiles(const std::vector& windowIndices, const std::vector& tabIndices); virtual ~EventGetDisplayedDataFiles(); bool isTestForDisplayedDataFileInTabIndex(const int32_t tabIndex) const; bool isTestForDisplayedDataFileInWindowIndex(const int32_t windowIndex) const; bool isTestForDisplayedSurfaceStructure(const StructureEnum::Enum surfaceStructure) const; void addDisplayedDataFile(const CaretDataFile* caretDataFile); bool isDataFileDisplayed(const CaretDataFile* caretDataFile) const; std::set getDisplayedDataFiles() const; std::vector getTabIndices() const; std::vector getWindowIndices() const; std::vector getDisplayedSurfaceStructures() const; private: EventGetDisplayedDataFiles(const EventGetDisplayedDataFiles&); EventGetDisplayedDataFiles& operator=(const EventGetDisplayedDataFiles&); public: // ADD_NEW_METHODS_HERE private: void setupSurfaceStrucutures() const; // ADD_NEW_MEMBERS_HERE std::set m_windowIndices; std::set m_tabIndices; std::set m_displayedDataFiles; mutable std::vector m_surfaceStructures; mutable bool m_surfaceStructuresValid; }; #ifdef __EVENT_GET_DISPLAYED_DATA_FILES_DECLARE__ // #endif // __EVENT_GET_DISPLAYED_DATA_FILES_DECLARE__ } // namespace #endif //__EVENT_GET_DISPLAYED_DATA_FILES_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Files/EventMapYokingSelectMap.cxx000066400000000000000000000055571300200146000270440ustar00rootroot00000000000000 /*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_MAP_YOKING_SELECT_MAP_DECLARE__ #include "EventMapYokingSelectMap.h" #undef __EVENT_MAP_YOKING_SELECT_MAP_DECLARE__ #include "CaretAssert.h" #include "EventTypeEnum.h" using namespace caret; /** * \class caret::EventMapYokingSelectMap * \brief Event sent when a yoked overlay or file selection changes. * \ingroup Files */ /** * Constructor for change in map yoking. * * @param caretMappableDataFile * Caret mappable file that is causing this event. * @param mapYokingGroup * Map yoking group that has a status change (selected map or enabled status) */ EventMapYokingSelectMap::EventMapYokingSelectMap(const MapYokingGroupEnum::Enum mapYokingGroup, const CaretMappableDataFile* caretMappableDataFile, const int32_t mapIndex, const bool selectionStatus) : Event(EventTypeEnum::EVENT_MAP_YOKING_SELECT_MAP), m_mapYokingGroup(mapYokingGroup), m_caretMappableDataFile(caretMappableDataFile), m_mapIndex(mapIndex), m_selectionStatus(selectionStatus) { if (mapYokingGroup != MapYokingGroupEnum::MAP_YOKING_GROUP_OFF) { MapYokingGroupEnum::setSelectedMapIndex(mapYokingGroup, mapIndex); MapYokingGroupEnum::setEnabled(mapYokingGroup, selectionStatus); } } /** * Destructor. */ EventMapYokingSelectMap::~EventMapYokingSelectMap() { } /** * @return The map yoking group. */ MapYokingGroupEnum::Enum EventMapYokingSelectMap::getMapYokingGroup() const { return m_mapYokingGroup; } /** * @return Caret Mappable Data File for which event was issued. * Might be NULL. */ const CaretMappableDataFile* EventMapYokingSelectMap::getCaretMappableDataFile() const { return m_caretMappableDataFile; } /** * @return Map index selected. */ int32_t EventMapYokingSelectMap::getMapIndex() const { return m_mapIndex; } /** * @return Selection status but ONLY for SAME FILE ! */ bool EventMapYokingSelectMap::getSelectionStatus() const { return m_selectionStatus; } connectome-workbench-1.2.3+git41-gc4c6c90/src/Files/EventMapYokingSelectMap.h000066400000000000000000000044601300200146000264610ustar00rootroot00000000000000#ifndef __EVENT_MAP_YOKING_SELECT_MAP_H__ #define __EVENT_MAP_YOKING_SELECT_MAP_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" #include "MapYokingGroupEnum.h" namespace caret { class CaretMappableDataFile; class EventMapYokingSelectMap : public Event { public: EventMapYokingSelectMap(const MapYokingGroupEnum::Enum mapYokingGroup, const CaretMappableDataFile* caretMappableDataFile, const int32_t mapIndex, const bool selectionStatus); virtual ~EventMapYokingSelectMap(); MapYokingGroupEnum::Enum getMapYokingGroup() const; const CaretMappableDataFile* getCaretMappableDataFile() const; int32_t getMapIndex() const; bool getSelectionStatus() const; // ADD_NEW_METHODS_HERE private: EventMapYokingSelectMap(const EventMapYokingSelectMap&); EventMapYokingSelectMap& operator=(const EventMapYokingSelectMap&); const MapYokingGroupEnum::Enum m_mapYokingGroup; const CaretMappableDataFile* m_caretMappableDataFile; const int32_t m_mapIndex; const bool m_selectionStatus; // ADD_NEW_MEMBERS_HERE }; #ifdef __EVENT_MAP_YOKING_SELECT_MAP_DECLARE__ // #endif // __EVENT_MAP_YOKING_SELECT_MAP_DECLARE__ } // namespace #endif //__EVENT_MAP_YOKING_SELECT_MAP_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Files/EventMapYokingValidation.cxx000066400000000000000000000153231300200146000272510ustar00rootroot00000000000000 /*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 __EVENT_MAP_YOKING_VALIDATION_DECLARE__ #include "EventMapYokingValidation.h" #undef __EVENT_MAP_YOKING_VALIDATION_DECLARE__ #include "BrainConstants.h" #include "CaretAssert.h" #include "CaretMappableDataFile.h" #include "EventBrowserTabIndicesGetAll.h" #include "EventManager.h" #include "EventTypeEnum.h" using namespace caret; /** * \class caret::EventMapYokingValidation * \brief Get map files yoked to a mapping group for validation. * \ingroup Files * * When the user selects map yoking for a file, verify that the new * file contains the same number of maps as any other files using * the same yoking group. If not, warn the user. */ /** * Constructor for validation files with map yoked to the given yoking group. * * @param mapYokingGroup * The map yoking group that will be validated for compatibility. */ EventMapYokingValidation::EventMapYokingValidation(const MapYokingGroupEnum::Enum mapYokingGroup) : Event(EventTypeEnum::EVENT_MAP_YOKING_VALIDATION), m_mapYokingGroup(mapYokingGroup) { EventBrowserTabIndicesGetAll allTabsEvent; EventManager::get()->sendEvent(allTabsEvent.getPointer()); m_validTabIndices = allTabsEvent.getAllBrowserTabIndices(); } /** * Destructor. */ EventMapYokingValidation::~EventMapYokingValidation() { } /** * Add a map file, if it is yoked to the same yoking group, so that it * may be used in the compatibility test. * * @param caretMapFile * The map file. * @param mapYokingGroup * Yoking group status of the file * @param tabIndex * Index of tab in which the file is displayed. */ void EventMapYokingValidation::addMapYokedFile(const CaretMappableDataFile* caretMapFile, const MapYokingGroupEnum::Enum mapYokingGroup, const int32_t tabIndex) { CaretAssert(caretMapFile); if (mapYokingGroup == MapYokingGroupEnum::MAP_YOKING_GROUP_OFF) { return; } if (mapYokingGroup != m_mapYokingGroup) { return; } if (std::find(m_validTabIndices.begin(), m_validTabIndices.end(), tabIndex) == m_validTabIndices.end()) { return; } m_yokedFileInfo.insert(YokedFileInfo(caretMapFile, tabIndex)); } /** * Add a map file, if it is yoked to the same yoking group, so that it * may be used in the compatibility test. * * @param caretMapFile * The map file. * @param mapYokingGroupsForAllTabs * Yoking group status of the file for all tabs. Number of elements * MUST BE BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS. */ void EventMapYokingValidation::addMapYokedFileAllTabs(const CaretMappableDataFile* caretMapFile, const MapYokingGroupEnum::Enum* mapYokingGroupsForAllTabs) { CaretAssert(caretMapFile); for (int32_t iTab = 0; iTab < BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS; iTab++) { if (mapYokingGroupsForAllTabs[iTab] == MapYokingGroupEnum::MAP_YOKING_GROUP_OFF) { continue; } if (mapYokingGroupsForAllTabs[iTab] != m_mapYokingGroup) { continue; } if (std::find(m_validTabIndices.begin(), m_validTabIndices.end(), iTab) == m_validTabIndices.end()) { continue; } m_yokedFileInfo.insert(YokedFileInfo(caretMapFile, iTab)); } } /** * @return The map yoking group. */ MapYokingGroupEnum::Enum EventMapYokingValidation::getMapYokingGroup() const { return m_mapYokingGroup; } /** * Validate the file for compatibility. * * @param caretMapFile * The map file. * @param numberOfYokedFilesOut * Number of files, excluding the given file, currently yoked * to this yoking group. * @param messageOut * Message containing information about any incompatibility. * @return * True if new file is compatible with any existing yoked files, else false. */ bool EventMapYokingValidation::validateCompatibility(const CaretMappableDataFile* caretMapFile, int32_t& numberOfYokedFilesOut, AString& messageOut) const { numberOfYokedFilesOut = 0; messageOut = ""; CaretAssert(caretMapFile); const int32_t numberOfMaps = caretMapFile->getNumberOfMaps(); for (std::set::const_iterator iter = m_yokedFileInfo.begin(); iter != m_yokedFileInfo.end(); iter++) { const YokedFileInfo& yfi = *iter; numberOfYokedFilesOut++; if (yfi.m_numberOfMaps != numberOfMaps) { messageOut.appendWithNewLine(" " + yfi.m_infoText); } } if (messageOut.isEmpty()) { return true; } const AString fileInfo("Incompatible number of map for yoking:\n" + AString::number(numberOfMaps) + " in " + caretMapFile->getFileNameNoPath() + "\n\n"); messageOut.insert(0, fileInfo); return false; } /** * Constructor for yoked file information. * * @param caretMapFile * The map file. * @param tabIndex * Index of tab in which the file is displayed. */ EventMapYokingValidation::YokedFileInfo::YokedFileInfo(const CaretMappableDataFile* caretMapFile, const int32_t tabIndex) : m_mapFile(caretMapFile), m_tabIndex(tabIndex) { CaretAssert(caretMapFile); m_numberOfMaps = caretMapFile->getNumberOfMaps(); m_infoText = (AString::number(m_numberOfMaps) + " maps in tab " + AString::number(tabIndex) + " file: " + caretMapFile->getFileNameNoPath()); } connectome-workbench-1.2.3+git41-gc4c6c90/src/Files/EventMapYokingValidation.h000066400000000000000000000060321300200146000266730ustar00rootroot00000000000000#ifndef __EVENT_MAP_YOKING_VALIDATION_H__ #define __EVENT_MAP_YOKING_VALIDATION_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 "Event.h" #include "MapYokingGroupEnum.h" namespace caret { class CaretMappableDataFile; class EventMapYokingValidation : public Event { public: EventMapYokingValidation(const MapYokingGroupEnum::Enum mapYokingGroup); virtual ~EventMapYokingValidation(); void addMapYokedFile(const CaretMappableDataFile* caretMapFile, const MapYokingGroupEnum::Enum mapYokingGroup, const int32_t tabIndex); void addMapYokedFileAllTabs(const CaretMappableDataFile* caretMapFile, const MapYokingGroupEnum::Enum* mapYokingGroupsForAllTabs); MapYokingGroupEnum::Enum getMapYokingGroup() const; bool validateCompatibility(const CaretMappableDataFile* caretMapFile, int32_t& numberOfYokedFilesOut, AString& messageOut) const; // ADD_NEW_METHODS_HERE private: class YokedFileInfo { public: YokedFileInfo(const CaretMappableDataFile* caretMapFile, const int32_t tabIndex); const CaretMappableDataFile* m_mapFile; const int32_t m_tabIndex; int32_t m_numberOfMaps; AString m_infoText; bool operator<(const YokedFileInfo& rhs) const { return (m_tabIndex < rhs.m_tabIndex); } }; EventMapYokingValidation(const EventMapYokingValidation&); EventMapYokingValidation& operator=(const EventMapYokingValidation&); std::set m_yokedFileInfo; std::vector m_validTabIndices; const MapYokingGroupEnum::Enum m_mapYokingGroup; // ADD_NEW_MEMBERS_HERE }; #ifdef __EVENT_MAP_YOKING_VALIDATION_DECLARE__ // #endif // __EVENT_MAP_YOKING_VALIDATION_DECLARE__ } // namespace #endif //__EVENT_MAP_YOKING_VALIDATION_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Files/EventSurfaceColoringInvalidate.cxx000066400000000000000000000023371300200146000304270ustar00rootroot00000000000000/*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 "CaretAssert.h" #include "EventSurfaceColoringInvalidate.h" using namespace caret; /** * Construct an event for invalidating surface coloring. */ EventSurfaceColoringInvalidate::EventSurfaceColoringInvalidate() : Event(EventTypeEnum::EVENT_SURFACE_COLORING_INVALIDATE) { } /** * Destructore. */ EventSurfaceColoringInvalidate::~EventSurfaceColoringInvalidate() { } connectome-workbench-1.2.3+git41-gc4c6c90/src/Files/EventSurfaceColoringInvalidate.h000066400000000000000000000027421300200146000300540ustar00rootroot00000000000000#ifndef __EVENT_SURFACE_COLORING_INVALIDATE_H__ #define __EVENT_SURFACE_COLORING_INVALIDATE_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 BrainStructure; /// Invalidate all surface coloring class EventSurfaceColoringInvalidate : public Event { public: EventSurfaceColoringInvalidate(); virtual ~EventSurfaceColoringInvalidate(); private: EventSurfaceColoringInvalidate(const EventSurfaceColoringInvalidate&); EventSurfaceColoringInvalidate& operator=(const EventSurfaceColoringInvalidate&); }; } // namespace #endif // __EVENT_SURFACE_COLORING_INVALIDATE_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Files/EventSurfaceStructuresValidGet.cxx000066400000000000000000000060531300200146000304540ustar00rootroot00000000000000 /*LICENSE_START*/ /* * Copyright (C) 2015 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_SURFACE_STRUCTURES_VALID_GET_DECLARE__ #include "EventSurfaceStructuresValidGet.h" #undef __EVENT_SURFACE_STRUCTURES_VALID_GET_DECLARE__ #include "CaretAssert.h" #include "CaretLogger.h" #include "EventTypeEnum.h" using namespace caret; /** * \class caret::EventSurfaceStructuresValidGet * \brief Get valid surface structures and their number of nodes * \ingroup Files */ /** * Constructor. */ EventSurfaceStructuresValidGet::EventSurfaceStructuresValidGet() : Event(EventTypeEnum::EVENT_SURFACE_STRUCTURES_VALID_GET) { } /** * Destructor. */ EventSurfaceStructuresValidGet::~EventSurfaceStructuresValidGet() { } /** * Add a structure and its number of nodes. If the structure already * has been added with the same number of nodes, no action is taken. * If a structure already has been added with a DIFFERENT number of nodes, * a warning will be logged. * * @param structure * The structure. * @param numberOfNodes * Number of nodes associated with the structure. */ void EventSurfaceStructuresValidGet::addStructure(const StructureEnum::Enum structure, const int32_t numberOfNodes) { std::map::iterator iter = m_structureAndNumberOfNodes.find(structure); if (iter != m_structureAndNumberOfNodes.end()) { const int32_t structNumNodes = iter->second; if (structNumNodes != numberOfNodes) { const AString message("Structure " + StructureEnum::toGuiName(structure) + " has different node counts: " + AString::number(numberOfNodes) + " and " + AString::number(structNumNodes)); CaretAssertMessage(0, message); CaretLogSevere(message); } } else { m_structureAndNumberOfNodes.insert(std::make_pair(structure, numberOfNodes)); } } /** * @return A map containing structures and their number of nodes. */ std::map EventSurfaceStructuresValidGet::getStructuresAndNumberOfNodes() const { return m_structureAndNumberOfNodes; } connectome-workbench-1.2.3+git41-gc4c6c90/src/Files/EventSurfaceStructuresValidGet.h000066400000000000000000000037171300200146000301050ustar00rootroot00000000000000#ifndef __EVENT_SURFACE_STRUCTURES_VALID_GET_H__ #define __EVENT_SURFACE_STRUCTURES_VALID_GET_H__ /*LICENSE_START*/ /* * Copyright (C) 2015 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 "StructureEnum.h" namespace caret { class EventSurfaceStructuresValidGet : public Event { public: EventSurfaceStructuresValidGet(); virtual ~EventSurfaceStructuresValidGet(); void addStructure(const StructureEnum::Enum structure, const int32_t numberOfNodes); std::map getStructuresAndNumberOfNodes() const; // ADD_NEW_METHODS_HERE private: EventSurfaceStructuresValidGet(const EventSurfaceStructuresValidGet&); EventSurfaceStructuresValidGet& operator=(const EventSurfaceStructuresValidGet&); std::map m_structureAndNumberOfNodes; // ADD_NEW_MEMBERS_HERE }; #ifdef __EVENT_SURFACE_STRUCTURES_VALID_GET_DECLARE__ // #endif // __EVENT_SURFACE_STRUCTURES_VALID_GET_DECLARE__ } // namespace #endif //__EVENT_SURFACE_STRUCTURES_VALID_GET_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Files/Fiber.cxx000066400000000000000000000132301300200146000233600ustar00rootroot00000000000000 /*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 #define __FIBER_DECLARE__ #include "Fiber.h" #undef __FIBER_DECLARE__ #include "AString.h" #include "CaretAssert.h" #include "CaretLogger.h" #include "MathFunctions.h" using namespace caret; /** * \class caret::Fiber * \brief Data for a single fiber */ /** * Constructor. */ Fiber::Fiber(const float* pointerToData) { m_opacityForDrawing = 1.0; /* * Retrieve values from the file */ m_meanF = pointerToData[0]; m_varF = pointerToData[1]; m_theta = pointerToData[2]; m_phi = pointerToData[3]; m_k1 = pointerToData[4]; m_k2 = pointerToData[5]; m_psi = pointerToData[6]; /* * Validate inputs */ if (m_meanF != m_meanF) { m_invalidMessage += " meanF=NaN"; } if (m_varF != m_varF) { m_invalidMessage += " varF=NaN"; } if (m_theta != m_theta) { m_invalidMessage += " theta=NaN"; } if (m_phi != m_phi) { m_invalidMessage += " phi=NaN"; } if (m_k1 != m_k1) { m_invalidMessage += " k1=NaN"; } else if (m_k1 < 0.0) { m_invalidMessage += " k1=negative=" + QString::number(m_k1); } if (m_k2 != m_k2) { m_invalidMessage += " k2=NaN"; } else if (m_k1 < 0.0) { m_invalidMessage += " k2=negative=" + QString::number(m_k2); } if (m_psi != m_psi) { m_invalidMessage += " psi=NaN"; } m_valid = m_invalidMessage.isEmpty(); if (m_valid) { /* * Set computed values used for visualization */ m_fanningMajorAxisAngle = fanningEigenvalueToAngle(m_k1); m_fanningMinorAxisAngle = fanningEigenvalueToAngle(m_k2); /* * m_theta is angle from Positive-Z Axis rotated about a line * in the XY-Plane * * m_phi is angle from positive X-Axis rotated about Z-Axis * looking to negative Z. * * NOTE: 'X' is in radiological space (positive X is left) * so flip the sign of the X-coordinate. */ m_directionUnitVector[0] = -std::sin(m_theta) * std::cos(m_phi); m_directionUnitVector[1] = std::sin(m_theta) * std::sin(m_phi); m_directionUnitVector[2] = std::cos(m_theta); /* * Use absolute values of directional unit vector as RGB color components. */ m_directionUnitVectorRGB[0] = std::fabs(m_directionUnitVector[0]); m_directionUnitVectorRGB[1] = std::fabs(m_directionUnitVector[1]); m_directionUnitVectorRGB[2] = std::fabs(m_directionUnitVector[2]); // const float nearZero = 0.0001; // float oppositePhi = 0.0; // if (std::fabs(m_directionUnitVector[0]) > nearZero) { // oppositePhi = M_PI - std::atan2(-m_directionUnitVector[1], // -m_directionUnitVector[0]); // } // else { // oppositePhi = M_PI_2; // } // // float oppositeTheta = std::acos(-m_directionUnitVector[2]); // // const float radiansToDegrees = 180.0 / M_PI; // std::cout << "Vector: " << qPrintable(AString::fromNumbers(m_directionUnitVector, 3, ",")) << std::endl; // std::cout << "Theta/OppTheta/Phi/OppPhi: " // << (m_theta * radiansToDegrees) << " " // << (oppositeTheta * radiansToDegrees) << " " // << (m_phi * radiansToDegrees) << " " // << (oppositePhi * radiansToDegrees) << std::endl << std::endl; } } void vectorToAngles(const float vector[3], float& azimuthOut, float& elevationOut) { const float nearZero = 0.001; azimuthOut = 0.0; if (std::fabs(vector[0]) > nearZero) { azimuthOut = std::atan2(vector[1], vector[0]); } else if (vector[1] > nearZero) { azimuthOut = M_PI_2; } else if (vector[1] < -nearZero) { azimuthOut = -M_PI_2; } if (azimuthOut > M_PI_2) azimuthOut -= M_PI_2; else if (azimuthOut < -M_PI_2) azimuthOut += M_PI_2; elevationOut = std::acos(vector[2]); } /** * Destructor. */ Fiber::~Fiber() { } /** * Convert the fanning eigenvalue to an angle. * @param eigenvalue * The eigenvalue * @return * Angle derived from the eigenvalue. */ float Fiber::fanningEigenvalueToAngle(const float eigenvalue) { float angle = 0.0; if (eigenvalue > 0.0) { float sigma = 1.0 / std::sqrt(2.0 * eigenvalue); if (sigma > 1.0) sigma = 1.0; angle = std::asin(sigma); angle = angle / 2; if (angle < 0.0) { angle = -angle; } } else { CaretLogSevere("Have a negative eigenvalue=" + AString::number(eigenvalue) + " for a fiber."); } return angle; } connectome-workbench-1.2.3+git41-gc4c6c90/src/Files/Fiber.h000066400000000000000000000100061300200146000230030ustar00rootroot00000000000000#ifndef __FIBER_H__ #define __FIBER_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 namespace caret { /** * \struct caret::Fiber * \brief Attributes of a single fiber */ class Fiber { public: Fiber(const float* pointerToData); ~Fiber(); /** * Spatial magnitude of distribution/distance from center * This value is from the fiber orientation file. */ float m_meanF; /** * Spatial variance in magnitude of distribution/distance from center * This value is from the fiber orientation file. */ float m_varF; /** * First spatial angle of distribution. * Angle from Positive Z-Axis rotated about a line * in the XY-Plane * Units is radians. * This value is from the fiber orientation file. */ float m_theta; /** * Second spatial angle of distribution. * Aximuthal angle in X-Y Plane, counter-clockwise * around positive Z-Axis starting at positive X-Axis. * Units is radians. * This value is from the fiber orientation file. */ float m_phi; /** * Major fanning eigenvalue * This value is from the fiber orientation file. */ float m_k1; /** * Minor fanning eigenvalue * This value is from the fiber orientation file. */ float m_k2; /** * Angle of anisotropy in uncertainty/fanning distribution on sphere * Units is radians. * This value is from the fiber orientation file. */ float m_psi; /** * Angle of fanning for the major axis. * Units is radians. * This value is computed and is NOT from the fiber orientation file. */ float m_fanningMajorAxisAngle; /** * Angle of fanning for the minor axis. * Units is radians. * This value is computed and is NOT from the fiber orientation file. */ float m_fanningMinorAxisAngle; /** * Directional unit vector of fiber */ float m_directionUnitVector[3]; /** * RGB Color for directional unit vector of fiber */ float m_directionUnitVectorRGB[3]; /** * True if the fiber is valid, else false. */ bool m_valid; /** * Describes why fiber is invalid. */ QString m_invalidMessage; /** * Opacity of fiber drawing used by drawing code. * This value IS NOT stored in the file. */ float m_opacityForDrawing; /** Number of elements per fiber in a fiber orientation's file */ static const int32_t NUMBER_OF_ELEMENTS_PER_FIBER_IN_FILE; private: float fanningEigenvalueToAngle(const float k); }; #ifdef __FIBER_DECLARE__ const int32_t Fiber::NUMBER_OF_ELEMENTS_PER_FIBER_IN_FILE = 7; #endif // __FIBER_DECLARE__ } // namespace #endif //__FIBER_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Files/FiberOrientation.cxx000066400000000000000000000045621300200146000256040ustar00rootroot00000000000000 /*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 __FIBER_ORIENTATION_DECLARE__ #include "FiberOrientation.h" #undef __FIBER_ORIENTATION_DECLARE__ #include "Fiber.h" using namespace caret; /** * \class caret::FiberOrientation * \brief Groups fibers at a particular spatial coordinate */ /** * Constructor. */ FiberOrientation::FiberOrientation(const int32_t numberOfFibers, float* pointerToData) { m_xyz[0] = pointerToData[0]; m_xyz[1] = pointerToData[1]; m_xyz[2] = pointerToData[2]; m_numberOfFibers = numberOfFibers; /* * Point to 1st element after XYZ in CIFTI file row */ float* offset = pointerToData + FiberOrientation::NUMBER_OF_ELEMENTS_IN_FILE; /* * Point to each fiber in the CIFTI file row */ for (int32_t i = 0; i < m_numberOfFibers; i++) { Fiber* fiber = new Fiber(offset); m_fibers.push_back(fiber); offset += Fiber::NUMBER_OF_ELEMENTS_PER_FIBER_IN_FILE; if (fiber->m_valid == false) { if (m_invalidMessage.isEmpty() == false) { m_invalidMessage += "; "; } m_invalidMessage += ("Index=" + AString::number(i) + ": " + fiber->m_invalidMessage); } } m_valid = m_invalidMessage.isEmpty(); } /** * Destructor. */ FiberOrientation::~FiberOrientation() { for (int32_t i = 0; i < m_numberOfFibers; i++) { delete m_fibers[i]; } m_fibers.clear(); } connectome-workbench-1.2.3+git41-gc4c6c90/src/Files/FiberOrientation.h000066400000000000000000000056001300200146000252230ustar00rootroot00000000000000#ifndef __FIBER_ORIENTATION__H__ #define __FIBER_ORIENTATION__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" namespace caret { class Fiber; class FiberOrientation /* : public CaretObject */ { public: FiberOrientation(const int32_t numberOfFibers, float* pointerToData); virtual ~FiberOrientation(); /** * XYZ coordinates at spatial center of distribution. * When valid, points to memory in a CIFTI file. */ float m_xyz[3]; /** * Number of fibers in this group. * (number of elements in member 'fibers'). */ int32_t m_numberOfFibers; /** * Pointers to all fibers in this group. * When valid, points to memory in a CIFTI file */ std::vector m_fibers; /** * Fiber orientations are drawn using blending (alpha values). For * blending to work correctly in OpenGL, items must be drawn in * "depth" order from furthest to nearest. */ mutable float m_drawingDepth; /** * True if the fiber is valid, else false. */ bool m_valid; /** * Describes why fiber is invalid. */ QString m_invalidMessage; /** * Number of elements per fiber in a fiber orientation's file * (excluding the Fibers). * * At this time, this is the XYZ. * The value for this constant MUST be updated if elements are * added to a fiber orientation. */ static const int32_t NUMBER_OF_ELEMENTS_IN_FILE; private: FiberOrientation(const FiberOrientation&); FiberOrientation& operator=(const FiberOrientation&); // ADD_NEW_MEMBERS_HERE }; #ifdef __FIBER_ORIENTATION_DECLARE__ const int32_t FiberOrientation::NUMBER_OF_ELEMENTS_IN_FILE = 3; #endif // __FIBER_ORIENTATION_DECLARE__ } // namespace #endif //__FIBER_ORIENTATION__H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Files/FiberOrientationColoringTypeEnum.cxx000066400000000000000000000232551300200146000307700ustar00rootroot00000000000000 /*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 __FIBER_ORIENTATION_COLORING_TYPE_ENUM_DECLARE__ #include "FiberOrientationColoringTypeEnum.h" #undef __FIBER_ORIENTATION_COLORING_TYPE_ENUM_DECLARE__ #include "CaretAssert.h" using namespace caret; /** * \class caret::FiberOrientationColoringTypeEnum * \brief Coloring type for foci orientations (and trajectory) * \ingroup Brain */ /** * Constructor. * * @param enumValue * An enumerated value. * @param name * Name of enumerated value. * * @param guiName * User-friendly name for use in user-interface. */ FiberOrientationColoringTypeEnum::FiberOrientationColoringTypeEnum(const Enum enumValue, const AString& name, const AString& guiName) { this->enumValue = enumValue; this->integerCode = integerCodeCounter++; this->name = name; this->guiName = guiName; } /** * Destructor. */ FiberOrientationColoringTypeEnum::~FiberOrientationColoringTypeEnum() { } /** * Initialize the enumerated metadata. */ void FiberOrientationColoringTypeEnum::initialize() { if (initializedFlag) { return; } initializedFlag = true; enumData.push_back(FiberOrientationColoringTypeEnum(FIBER_COLORING_FIBER_INDEX_AS_RGB, "FIBER_COLORING_FIBER_INDEX_AS_RGB", "Fiber 1,2,3 as RBG")); enumData.push_back(FiberOrientationColoringTypeEnum(FIBER_COLORING_XYZ_AS_RGB, "FIBER_COLORING_XYZ_AS_RGB", "XYZ as RGB")); } /** * 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 FiberOrientationColoringTypeEnum* FiberOrientationColoringTypeEnum::findData(const Enum enumValue) { if (initializedFlag == false) initialize(); size_t num = enumData.size(); for (size_t i = 0; i < num; i++) { const FiberOrientationColoringTypeEnum* 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 FiberOrientationColoringTypeEnum::toName(Enum enumValue) { if (initializedFlag == false) initialize(); const FiberOrientationColoringTypeEnum* 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. */ FiberOrientationColoringTypeEnum::Enum FiberOrientationColoringTypeEnum::fromName(const AString& name, bool* isValidOut) { if (initializedFlag == false) initialize(); bool validFlag = false; Enum enumValue = FIBER_COLORING_XYZ_AS_RGB; for (std::vector::iterator iter = enumData.begin(); iter != enumData.end(); iter++) { const FiberOrientationColoringTypeEnum& 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 FiberOrientationColoringTypeEnum")); } return enumValue; } /** * Get a GUI string representation of the enumerated type. * @param enumValue * Enumerated value. * @return * String representing enumerated value. */ AString FiberOrientationColoringTypeEnum::toGuiName(Enum enumValue) { if (initializedFlag == false) initialize(); const FiberOrientationColoringTypeEnum* 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. */ FiberOrientationColoringTypeEnum::Enum FiberOrientationColoringTypeEnum::fromGuiName(const AString& guiName, bool* isValidOut) { if (initializedFlag == false) initialize(); bool validFlag = false; Enum enumValue = FIBER_COLORING_XYZ_AS_RGB; for (std::vector::iterator iter = enumData.begin(); iter != enumData.end(); iter++) { const FiberOrientationColoringTypeEnum& 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 FiberOrientationColoringTypeEnum")); } return enumValue; } /** * Get the integer code for a data type. * * @return * Integer code for data type. */ int32_t FiberOrientationColoringTypeEnum::toIntegerCode(Enum enumValue) { if (initializedFlag == false) initialize(); const FiberOrientationColoringTypeEnum* 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. */ FiberOrientationColoringTypeEnum::Enum FiberOrientationColoringTypeEnum::fromIntegerCode(const int32_t integerCode, bool* isValidOut) { if (initializedFlag == false) initialize(); bool validFlag = false; Enum enumValue = FIBER_COLORING_XYZ_AS_RGB; for (std::vector::iterator iter = enumData.begin(); iter != enumData.end(); iter++) { const FiberOrientationColoringTypeEnum& 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 FiberOrientationColoringTypeEnum")); } 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 FiberOrientationColoringTypeEnum::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 FiberOrientationColoringTypeEnum::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(FiberOrientationColoringTypeEnum::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 FiberOrientationColoringTypeEnum::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(FiberOrientationColoringTypeEnum::toGuiName(iter->enumValue)); } if (isSorted) { std::sort(allGuiNames.begin(), allGuiNames.end()); } } connectome-workbench-1.2.3+git41-gc4c6c90/src/Files/FiberOrientationColoringTypeEnum.h000066400000000000000000000064021300200146000304100ustar00rootroot00000000000000#ifndef __FIBER_ORIENTATION_COLORING_TYPE_ENUM__H_ #define __FIBER_ORIENTATION_COLORING_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 FiberOrientationColoringTypeEnum { public: /** * Enumerated values. */ enum Enum { /** */ FIBER_COLORING_XYZ_AS_RGB, /** */ FIBER_COLORING_FIBER_INDEX_AS_RGB }; ~FiberOrientationColoringTypeEnum(); 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: FiberOrientationColoringTypeEnum(const Enum enumValue, const AString& name, const AString& guiName); static const FiberOrientationColoringTypeEnum* 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 __FIBER_ORIENTATION_COLORING_TYPE_ENUM_DECLARE__ std::vector FiberOrientationColoringTypeEnum::enumData; bool FiberOrientationColoringTypeEnum::initializedFlag = false; int32_t FiberOrientationColoringTypeEnum::integerCodeCounter = 0; #endif // __FIBER_ORIENTATION_COLORING_TYPE_ENUM_DECLARE__ } // namespace #endif //__FIBER_ORIENTATION_COLORING_TYPE_ENUM__H_ connectome-workbench-1.2.3+git41-gc4c6c90/src/Files/FiberOrientationTrajectory.cxx000066400000000000000000000134421300200146000276500ustar00rootroot00000000000000 /*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 __FIBER_ORIENTATION_TRAJECTORY_DECLARE__ #include "FiberOrientationTrajectory.h" #undef __FIBER_ORIENTATION_TRAJECTORY_DECLARE__ #include "CaretAssert.h" #include "CaretSparseFile.h" #include "MathFunctions.h" using namespace caret; /** * \class caret::FiberOrientationTrajectory * \brief Containts fiber trajectory and corresponding fiber orientation. */ /** * Constructor that accepts a fiber orientation. * * @param fiberOrientationIndex * Index of the fiber orientation. * @param fiberOrientation * Fiber orientation that is associated with this fiber trajectory. */ FiberOrientationTrajectory::FiberOrientationTrajectory(const int64_t fiberOrientationIndex, const FiberOrientation* fiberOrientation) : m_fiberOrientationIndex(fiberOrientationIndex), m_fiberOrientation(fiberOrientation) { CaretAssert(fiberOrientation); m_fiberFraction = new FiberFractions(); m_fiberFractionTotalCountFloat = 0.0; m_totalCountSum = 0.0; m_fiberCountsSum.clear(); m_distanceSum = 0.0; m_countForAveraging = 0; } /** * Destructor. */ FiberOrientationTrajectory::~FiberOrientationTrajectory() { delete m_fiberFraction; } /** * Add a fiber fraction for averaging. * * @param fiberFraction * Fiber fraction that is added. */ void FiberOrientationTrajectory::addFiberFractionsForAveraging(const FiberFractions& fiberFraction) { const bool includeZeroTotalCountWhenAveraging = true; const int64_t numFractions = fiberFraction.fiberFractions.size(); if ((fiberFraction.totalCount > 0) && (numFractions > 0)) { // const float len = MathFunctions::vectorLength(&fiberFraction.fiberFractions[0]); // if (len < 1.0) { // std::cout << "Fraction len < 1: " << len << std::endl; // } if (m_fiberCountsSum.empty()) { m_fiberCountsSum.resize(numFractions, 0.0); } else if (static_cast(m_fiberCountsSum.size()) != numFractions) { CaretAssertMessage(0, "Sizes should be the same"); return; } m_totalCountSum += fiberFraction.totalCount; for (int64_t i = 0; i < numFractions; i++) { m_fiberCountsSum[i] += fiberFraction.fiberFractions[i] * fiberFraction.totalCount; } m_distanceSum += fiberFraction.distance; m_countForAveraging += 1; } else if (includeZeroTotalCountWhenAveraging) { m_countForAveraging += 1; } } /** * Set a fiber fraction. * * @param fiberFraction * Fiber fraction that is replaced. */ void FiberOrientationTrajectory::setFiberFractions(const FiberFractions& fiberFraction) { *m_fiberFraction = fiberFraction; if (m_fiberFraction->fiberFractions.empty()) { m_fiberFraction->zero(); } m_fiberFractionTotalCountFloat = m_fiberFraction->totalCount; } /** * Finish, which will update the fiber fraction using the average of all * of the fiber fractions that were added. */ void FiberOrientationTrajectory::finishAveraging() { if (m_countForAveraging > 0) { m_fiberFraction->distance = m_distanceSum / m_countForAveraging; m_fiberFraction->totalCount = m_totalCountSum / m_countForAveraging; m_fiberFractionTotalCountFloat = m_totalCountSum / m_countForAveraging; const int64_t numFiberCounts = static_cast(m_fiberCountsSum.size()); m_fiberFraction->fiberFractions.resize(numFiberCounts); if (numFiberCounts > 0) { for (int64_t i = 0; i < numFiberCounts; i++) { if (m_fiberFractionTotalCountFloat > 0.0) { const float averageCount = m_fiberCountsSum[i] / m_countForAveraging; m_fiberFraction->fiberFractions[i] = (averageCount / m_fiberFractionTotalCountFloat); float sum = (m_fiberFraction->fiberFractions[0] + m_fiberFraction->fiberFractions[1] + m_fiberFraction->fiberFractions[2]); if (sum > 1.0) { const float divisor = 1.0 / sum; m_fiberFraction->fiberFractions[0] *= divisor; m_fiberFraction->fiberFractions[1] *= divisor; m_fiberFraction->fiberFractions[2] *= divisor; // float newSum = (m_fiberFraction->fiberFractions[0] // + m_fiberFraction->fiberFractions[1] // + m_fiberFraction->fiberFractions[2]); // std::cout << "Sum too big: " << sum << " new sum: " << newSum << std::endl; } } else { m_fiberFraction->fiberFractions[i] = 0.0; } } } } else { m_fiberFraction->zero(); } } connectome-workbench-1.2.3+git41-gc4c6c90/src/Files/FiberOrientationTrajectory.h000066400000000000000000000073341300200146000273000ustar00rootroot00000000000000#ifndef __FIBER_ORIENTATION_TRAJECTORY__H_ #define __FIBER_ORIENTATION_TRAJECTORY__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 "CaretAssert.h" #include "CaretSparseFile.h" namespace caret { class FiberOrientation; class FiberOrientationTrajectory { public: FiberOrientationTrajectory(const int64_t fiberOrientationIndex, const FiberOrientation* fiberOrientation); virtual ~FiberOrientationTrajectory(); void addFiberFractionsForAveraging(const FiberFractions& fiberFraction); void setFiberFractions(const FiberFractions& fiberFraction); /** * @return the Fiber Orientation. */ inline const FiberOrientation* getFiberOrientation() const { return m_fiberOrientation; } // /** // * Get the fiber fraction. // * // * @param indx // * Index of the fiber fraction. // * @return // * Fiber fraction at the given index. // */ // inline const FiberFractions* getFiberFraction() const { // return m_fiberFraction; // } /** * @return The fiber orientation index. */ inline int64_t getFiberOrientationIndex() const { return m_fiberOrientationIndex; } /** * @return The total count as a float. */ inline float getFiberFractionTotalCount() const { return m_fiberFractionTotalCountFloat; } /** * @return The fiber fractions (proportions). */ inline const std::vector& getFiberFractions() const { return m_fiberFraction->fiberFractions; } /** * @return The fiber fractions distance. */ inline float getFiberFractionDistance() const { return m_fiberFraction->distance; } void finishAveraging(); private: FiberOrientationTrajectory(const FiberOrientationTrajectory&); FiberOrientationTrajectory& operator=(const FiberOrientationTrajectory&); public: // ADD_NEW_METHODS_HERE private: const int64_t m_fiberOrientationIndex; const FiberOrientation* m_fiberOrientation; FiberFractions* m_fiberFraction; float m_fiberFractionTotalCountFloat; double m_totalCountSum; std::vector m_fiberCountsSum; double m_distanceSum; int64_t m_countForAveraging; // ADD_NEW_MEMBERS_HERE friend class CiftiFiberTrajectoryFile; }; #ifdef __FIBER_ORIENTATION_TRAJECTORY_DECLARE__ // #endif // __FIBER_ORIENTATION_TRAJECTORY_DECLARE__ } // namespace #endif //__FIBER_ORIENTATION_TRAJECTORY__H_ connectome-workbench-1.2.3+git41-gc4c6c90/src/Files/FiberTrajectoryColorModel.cxx000066400000000000000000000301261300200146000274120ustar00rootroot00000000000000 /*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 __FIBER_TRAJECTORY_COLOR_MODEL_DECLARE__ #include "FiberTrajectoryColorModel.h" #undef __FIBER_TRAJECTORY_COLOR_MODEL_DECLARE__ #include "CaretAssert.h" #include "CaretLogger.h" #include "SceneClass.h" #include "SceneClassAssistant.h" using namespace caret; /** * \class caret::FiberTrajectoryColorModel * \brief Combines Fiber Coloring Type and Caret Color enumerated types. * \ingroup Files * * Fiber trajectories may be colored using either the fiber coloring * type or a caret color. This model allows these types to be * treated as a single type. */ /** * Constructor. */ FiberTrajectoryColorModel::FiberTrajectoryColorModel() : CaretObject() { m_selectedItem = NULL; Item* item = new Item(); m_allItems.push_back(item); std::vector allColors; CaretColorEnum::getColorEnums(allColors); for (std::vector::iterator iter = allColors.begin(); iter != allColors.end(); iter++) { Item* item = new Item(*iter); m_allItems.push_back(item); } if (m_allItems.empty()) { CaretAssert(0); } else { m_selectedItem = m_allItems[0]; } m_sceneAssistant = new SceneClassAssistant(); } /** * Destructor. */ FiberTrajectoryColorModel::~FiberTrajectoryColorModel() { for (std::vector::iterator iter = m_allItems.begin(); iter != m_allItems.end(); iter++) { Item* item = *iter; delete item; } m_allItems.clear(); delete m_sceneAssistant; } /** * @return All items in this model. */ std::vector FiberTrajectoryColorModel::getValidItems() { std::vector items = m_allItems; return m_allItems; } /** * @return Pointer to selected item. */ FiberTrajectoryColorModel::Item* FiberTrajectoryColorModel::getSelectedItem() { if (m_selectedItem == NULL) { if (m_allItems.empty() == false) { m_selectedItem = m_allItems[0]; } } return m_selectedItem; } /** * @return Pointer to selected item. */ const FiberTrajectoryColorModel::Item* FiberTrajectoryColorModel::getSelectedItem() const { FiberTrajectoryColorModel* nonConstThis = const_cast(this); const Item* item = nonConstThis->getSelectedItem(); return item; } /** * Set the selected item. * @param item * New selected item. */ void FiberTrajectoryColorModel::setSelectedItem(const Item* item) { std::vector allItems = getValidItems(); const int32_t numItems = static_cast(allItems.size()); for (int32_t i = 0; i < numItems; i++) { if (item->equals(*allItems[i])) { m_selectedItem = allItems[i]; break; } } } /** * Set the selection to the given caret color. * @param color * Color that is to be selected. */ void FiberTrajectoryColorModel::setCaretColor(const CaretColorEnum::Enum color) { std::vector allItems = getValidItems(); const int32_t numItems = static_cast(allItems.size()); for (int32_t i = 0; i < numItems; i++) { if (allItems[i]->getItemType() == Item::ITEM_TYPE_CARET_COLOR) { if (allItems[i]->getCaretColor() == color) { setSelectedItem(allItems[i]); break; } } } } /** * @return Is the fiber coloring type selected? */ bool FiberTrajectoryColorModel::isFiberOrientationColoringTypeSelected() const { if (m_selectedItem->getItemType() == Item::ITEM_TYPE_FIBER_ORIENTATION_COLORING_TYPE) { return true; } return false; } /** * Set the selection to the given fiber coloring type.. * @param fiberColorType * Fiber coloring type that is to be selected. */ void FiberTrajectoryColorModel::setFiberOrientationColoringTypeSelected() { std::vector allItems = getValidItems(); const int32_t numItems = static_cast(allItems.size()); for (int32_t i = 0; i < numItems; i++) { if (allItems[i]->getItemType() == Item::ITEM_TYPE_FIBER_ORIENTATION_COLORING_TYPE) { setSelectedItem(allItems[i]); break; } } } /** * Copy the selection from the other model. * * @param other * The other model. */ void FiberTrajectoryColorModel::copy(const FiberTrajectoryColorModel& other) { const Item* otherItem = other.getSelectedItem(); switch (otherItem->getItemType()) { case Item::ITEM_TYPE_CARET_COLOR: setCaretColor(otherItem->getCaretColor()); break; case Item::ITEM_TYPE_FIBER_ORIENTATION_COLORING_TYPE: setFiberOrientationColoringTypeSelected(); break; } } /** * 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* FiberTrajectoryColorModel::saveToScene(const SceneAttributes* sceneAttributes, const AString& instanceName) { SceneClass* sceneClass = new SceneClass(instanceName, "FiberTrajectoryColorModel", 1); m_sceneAssistant->saveMembers(sceneAttributes, sceneClass); if (m_selectedItem != NULL) { sceneClass->addChild(m_selectedItem->saveToScene(sceneAttributes, "m_selectedItem")); } 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 FiberTrajectoryColorModel::restoreFromScene(const SceneAttributes* sceneAttributes, const SceneClass* sceneClass) { if (sceneClass == NULL) { return; } m_sceneAssistant->restoreMembers(sceneAttributes, sceneClass); Item item; item.restoreFromScene(sceneAttributes, sceneClass->getClass("m_selectedItem")); setSelectedItem(&item); } /* ========================================================================== */ /** * Constructor for a caret color. * * @param caretColor * The caret color enum. */ FiberTrajectoryColorModel::Item::Item(const CaretColorEnum::Enum caretColor) { m_caretColor = caretColor; m_itemType = ITEM_TYPE_CARET_COLOR; initializeAtEndOfConstruction(); } /** * Constructor for a fiber coloring type. * * @param fiberColoringType * The fiber coloring type. */ FiberTrajectoryColorModel::Item::Item() { m_caretColor = CaretColorEnum::BLACK; m_itemType = ITEM_TYPE_FIBER_ORIENTATION_COLORING_TYPE; initializeAtEndOfConstruction(); } /** * Destructor. */ FiberTrajectoryColorModel::Item::~Item() { delete m_sceneAssistant; } /** * Initialize at the end of construction. */ void FiberTrajectoryColorModel::Item::initializeAtEndOfConstruction() { m_sceneAssistant = new SceneClassAssistant(); m_sceneAssistant->add("m_caretColor", &m_caretColor); } /** * Equality test for Item. * * @param item * Item for comparison. * @return * True if the same, else false. */ bool FiberTrajectoryColorModel::Item::equals(const Item& item) const { if (m_itemType == item.m_itemType) { switch (m_itemType) { case ITEM_TYPE_CARET_COLOR: if (m_caretColor == item.m_caretColor) { return true; } break; case ITEM_TYPE_FIBER_ORIENTATION_COLORING_TYPE: return true; break; } } return false; } /** * @return Name of the item. */ AString FiberTrajectoryColorModel::Item::getName() const { AString name = "PROGRAM_ERROR"; switch (m_itemType) { case ITEM_TYPE_CARET_COLOR: name = CaretColorEnum::toGuiName(m_caretColor); break; case ITEM_TYPE_FIBER_ORIENTATION_COLORING_TYPE: name = "Fiber"; break; } return name; } /** * @return Type of the item. */ FiberTrajectoryColorModel::Item::ItemType FiberTrajectoryColorModel::Item::getItemType() const { return m_itemType; } /** * @return The caret color for this item. */ CaretColorEnum::Enum FiberTrajectoryColorModel::Item::getCaretColor() const { return m_caretColor; } /** * 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* FiberTrajectoryColorModel::Item::saveToScene(const SceneAttributes* sceneAttributes, const AString& instanceName) { SceneClass* sceneClass = new SceneClass(instanceName, "FiberTrajectoryColorModel::Item", 1); m_sceneAssistant->saveMembers(sceneAttributes, sceneClass); switch (m_itemType) { case ITEM_TYPE_CARET_COLOR: sceneClass->addString("m_itemType", "ITEM_TYPE_CARET_COLOR"); break; case ITEM_TYPE_FIBER_ORIENTATION_COLORING_TYPE: sceneClass->addString("m_itemType", "ITEM_TYPE_FIBER_ORIENTATION_COLORING_TYPE"); break; } 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 FiberTrajectoryColorModel::Item::restoreFromScene(const SceneAttributes* sceneAttributes, const SceneClass* sceneClass) { if (sceneClass == NULL) { return; } m_sceneAssistant->restoreMembers(sceneAttributes, sceneClass); const AString itemTypeName = sceneClass->getStringValue("m_itemType", "ITEM_TYPE_FIBER_ORIENTATION_COLORING_TYPE"); m_itemType = ITEM_TYPE_FIBER_ORIENTATION_COLORING_TYPE; if (itemTypeName == "ITEM_TYPE_CARET_COLOR") { m_itemType = ITEM_TYPE_CARET_COLOR; } else if (itemTypeName == "ITEM_TYPE_FIBER_ORIENTATION_COLORING_TYPE") { m_itemType = ITEM_TYPE_FIBER_ORIENTATION_COLORING_TYPE; } else { CaretLogWarning(("Unrecognized value: " + itemTypeName)); } } connectome-workbench-1.2.3+git41-gc4c6c90/src/Files/FiberTrajectoryColorModel.h000066400000000000000000000075331300200146000270450ustar00rootroot00000000000000#ifndef __FIBER_TRAJECTORY_COLOR_MODEL_H__ #define __FIBER_TRAJECTORY_COLOR_MODEL_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 "CaretObject.h" #include "SceneableInterface.h" namespace caret { class SceneClassAssistant; class FiberTrajectoryColorModel : public CaretObject, public SceneableInterface { public: class Item : public SceneableInterface { public: enum ItemType { /** * Type of item */ ITEM_TYPE_FIBER_ORIENTATION_COLORING_TYPE, ITEM_TYPE_CARET_COLOR }; Item(const CaretColorEnum::Enum caretColor); Item(); ~Item(); bool equals(const Item& item) const; AString getName() const; ItemType getItemType() const; CaretColorEnum::Enum getCaretColor() const; virtual SceneClass* saveToScene(const SceneAttributes* sceneAttributes, const AString& instanceName); virtual void restoreFromScene(const SceneAttributes* sceneAttributes, const SceneClass* sceneClass); private: void initializeAtEndOfConstruction(); SceneClassAssistant* m_sceneAssistant; CaretColorEnum::Enum m_caretColor; ItemType m_itemType; }; FiberTrajectoryColorModel(); virtual ~FiberTrajectoryColorModel(); std::vector getValidItems(); Item* getSelectedItem(); const Item* getSelectedItem() const; void setSelectedItem(const Item* item); void setCaretColor(const CaretColorEnum::Enum color); bool isFiberOrientationColoringTypeSelected() const; void setFiberOrientationColoringTypeSelected(); void copy(const FiberTrajectoryColorModel& other); private: FiberTrajectoryColorModel(const FiberTrajectoryColorModel&); FiberTrajectoryColorModel& operator=(const FiberTrajectoryColorModel&); public: // ADD_NEW_METHODS_HERE virtual SceneClass* saveToScene(const SceneAttributes* sceneAttributes, const AString& instanceName); virtual void restoreFromScene(const SceneAttributes* sceneAttributes, const SceneClass* sceneClass); private: SceneClassAssistant* m_sceneAssistant; std::vector m_allItems; Item* m_selectedItem; // ADD_NEW_MEMBERS_HERE }; #ifdef __FIBER_TRAJECTORY_COLOR_MODEL_DECLARE__ // #endif // __FIBER_TRAJECTORY_COLOR_MODEL_DECLARE__ } // namespace #endif //__FIBER_TRAJECTORY_COLOR_MODEL_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Files/FiberTrajectoryDisplayModeEnum.cxx000066400000000000000000000241341300200146000304140ustar00rootroot00000000000000 /*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 __FIBER_TRAJECTORY_DISPLAY_MODE_ENUM_DECLARE__ #include "FiberTrajectoryDisplayModeEnum.h" #undef __FIBER_TRAJECTORY_DISPLAY_MODE_ENUM_DECLARE__ #include "CaretAssert.h" using namespace caret; /** * \class caret::FiberTrajectoryDisplayModeEnum * \brief Enumerated type for fiber trajectory display mode */ /** * Constructor. * * @param enumValue * An enumerated value. * @param name * Name of enumerated value. * * @param guiName * User-friendly name for use in user-interface. */ FiberTrajectoryDisplayModeEnum::FiberTrajectoryDisplayModeEnum(const Enum enumValue, const AString& name, const AString& guiName) { this->enumValue = enumValue; this->integerCode = integerCodeCounter++; this->name = name; this->guiName = guiName; } /** * Destructor. */ FiberTrajectoryDisplayModeEnum::~FiberTrajectoryDisplayModeEnum() { } /** * Initialize the enumerated metadata. */ void FiberTrajectoryDisplayModeEnum::initialize() { if (initializedFlag) { return; } initializedFlag = true; enumData.push_back(FiberTrajectoryDisplayModeEnum(FIBER_TRAJECTORY_DISPLAY_ABSOLUTE, "FIBER_TRAJECTORY_DISPLAY_ABSOLUTE", "Absolute")); enumData.push_back(FiberTrajectoryDisplayModeEnum(FIBER_TRAJECTORY_DISPLAY_DISTANCE_WEIGHTED, "FIBER_TRAJECTORY_DISPLAY_DISTANCE_WEIGHTED", "Distance Weighted")); enumData.push_back(FiberTrajectoryDisplayModeEnum(FIBER_TRAJECTORY_DISPLAY_DISTANCE_WEIGHTED_LOG, "FIBER_TRAJECTORY_DISPLAY_DISTANCE_WEIGHTED_LOG", "Distance Weighted (Log)")); enumData.push_back(FiberTrajectoryDisplayModeEnum(FIBER_TRAJECTORY_DISPLAY_PROPORTION, "FIBER_TRAJECTORY_DISPLAY_PROPORTION", "Proportion")); } /** * 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 FiberTrajectoryDisplayModeEnum* FiberTrajectoryDisplayModeEnum::findData(const Enum enumValue) { if (initializedFlag == false) initialize(); size_t num = enumData.size(); for (size_t i = 0; i < num; i++) { const FiberTrajectoryDisplayModeEnum* 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 FiberTrajectoryDisplayModeEnum::toName(Enum enumValue) { if (initializedFlag == false) initialize(); const FiberTrajectoryDisplayModeEnum* 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. */ FiberTrajectoryDisplayModeEnum::Enum FiberTrajectoryDisplayModeEnum::fromName(const AString& name, bool* isValidOut) { if (initializedFlag == false) initialize(); bool validFlag = false; Enum enumValue = FIBER_TRAJECTORY_DISPLAY_PROPORTION; for (std::vector::iterator iter = enumData.begin(); iter != enumData.end(); iter++) { const FiberTrajectoryDisplayModeEnum& 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 FiberTrajectoryDisplayModeEnum")); } return enumValue; } /** * Get a GUI string representation of the enumerated type. * @param enumValue * Enumerated value. * @return * String representing enumerated value. */ AString FiberTrajectoryDisplayModeEnum::toGuiName(Enum enumValue) { if (initializedFlag == false) initialize(); const FiberTrajectoryDisplayModeEnum* 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. */ FiberTrajectoryDisplayModeEnum::Enum FiberTrajectoryDisplayModeEnum::fromGuiName(const AString& guiName, bool* isValidOut) { if (initializedFlag == false) initialize(); bool validFlag = false; Enum enumValue = FIBER_TRAJECTORY_DISPLAY_PROPORTION; for (std::vector::iterator iter = enumData.begin(); iter != enumData.end(); iter++) { const FiberTrajectoryDisplayModeEnum& 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 FiberTrajectoryDisplayModeEnum")); } return enumValue; } /** * Get the integer code for a data type. * * @return * Integer code for data type. */ int32_t FiberTrajectoryDisplayModeEnum::toIntegerCode(Enum enumValue) { if (initializedFlag == false) initialize(); const FiberTrajectoryDisplayModeEnum* 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. */ FiberTrajectoryDisplayModeEnum::Enum FiberTrajectoryDisplayModeEnum::fromIntegerCode(const int32_t integerCode, bool* isValidOut) { if (initializedFlag == false) initialize(); bool validFlag = false; Enum enumValue = FIBER_TRAJECTORY_DISPLAY_PROPORTION; for (std::vector::iterator iter = enumData.begin(); iter != enumData.end(); iter++) { const FiberTrajectoryDisplayModeEnum& 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 FiberTrajectoryDisplayModeEnum")); } 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 FiberTrajectoryDisplayModeEnum::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 FiberTrajectoryDisplayModeEnum::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(FiberTrajectoryDisplayModeEnum::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 FiberTrajectoryDisplayModeEnum::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(FiberTrajectoryDisplayModeEnum::toGuiName(iter->enumValue)); } if (isSorted) { std::sort(allGuiNames.begin(), allGuiNames.end()); } } connectome-workbench-1.2.3+git41-gc4c6c90/src/Files/FiberTrajectoryDisplayModeEnum.h000066400000000000000000000067511300200146000300460ustar00rootroot00000000000000#ifndef __FIBER_TRAJECTORY_DISPLAY_MODE_ENUM__H_ #define __FIBER_TRAJECTORY_DISPLAY_MODE_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 FiberTrajectoryDisplayModeEnum { public: /** * Enumerated values. */ enum Enum { /** Absolute display mode */ FIBER_TRAJECTORY_DISPLAY_ABSOLUTE, /** Distance weighted display mode */ FIBER_TRAJECTORY_DISPLAY_DISTANCE_WEIGHTED, /** Distance weighted Log display mode */ FIBER_TRAJECTORY_DISPLAY_DISTANCE_WEIGHTED_LOG, /** Proportion display mode*/ FIBER_TRAJECTORY_DISPLAY_PROPORTION }; ~FiberTrajectoryDisplayModeEnum(); 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: FiberTrajectoryDisplayModeEnum(const Enum enumValue, const AString& name, const AString& guiName); static const FiberTrajectoryDisplayModeEnum* 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 __FIBER_TRAJECTORY_DISPLAY_MODE_ENUM_DECLARE__ std::vector FiberTrajectoryDisplayModeEnum::enumData; bool FiberTrajectoryDisplayModeEnum::initializedFlag = false; int32_t FiberTrajectoryDisplayModeEnum::integerCodeCounter = 0; #endif // __FIBER_TRAJECTORY_DISPLAY_MODE_ENUM_DECLARE__ } // namespace #endif //__FIBER_TRAJECTORY_DISPLAY_MODE_ENUM__H_ connectome-workbench-1.2.3+git41-gc4c6c90/src/Files/FiberTrajectoryMapProperties.cxx000066400000000000000000000270711300200146000301520ustar00rootroot00000000000000 /*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 __FIBER_TRAJECTORY_MAP_PROPERTIES_DECLARE__ #include "FiberTrajectoryMapProperties.h" #undef __FIBER_TRAJECTORY_MAP_PROPERTIES_DECLARE__ #include "CaretAssert.h" #include "FiberTrajectoryColorModel.h" #include "SceneAttributes.h" #include "SceneClass.h" #include "SceneClassAssistant.h" using namespace caret; /** * \class caret::FiberTrajectoryMapProperties * \brief Contains display properties for a fiber trajectory file. */ /** * Constructor. */ FiberTrajectoryMapProperties::FiberTrajectoryMapProperties() { m_sceneAssistant = new SceneClassAssistant(); m_fiberTrajectoryColoringModel = new FiberTrajectoryColorModel(); const float thresholdStreamline = 5; const float maximumProportionOpacity = 0.80; const float minimumProportionOpacity = 0.05; const FiberTrajectoryDisplayModeEnum::Enum displayMode = FiberTrajectoryDisplayModeEnum::FIBER_TRAJECTORY_DISPLAY_ABSOLUTE; const float countMaximum = 50; const float countMinimum = 5; m_displayStatus = false; m_displayMode = displayMode; m_proportionStreamline = thresholdStreamline; m_maximumProportionOpacity = maximumProportionOpacity; m_minimumProportionOpacity = minimumProportionOpacity; m_countStreamline = thresholdStreamline; m_countMaximumOpacity = countMaximum; m_countMinimumOpacity = countMinimum; m_distanceStreamline = thresholdStreamline; m_distanceMaximumOpacity = countMaximum; m_distanceMinimumOpacity = countMinimum; m_sceneAssistant->add("m_displayStatus", &m_displayStatus); m_sceneAssistant->add("m_displayMode", &m_displayMode); m_sceneAssistant->add("m_proportionStreamline", &m_proportionStreamline); m_sceneAssistant->add("m_maximumProportionOpacity", &m_maximumProportionOpacity); m_sceneAssistant->add("m_minimumProportionOpacity", &m_minimumProportionOpacity); m_sceneAssistant->add("m_countStreamline", &m_countStreamline); m_sceneAssistant->add("m_countMaximumOpacity", &m_countMaximumOpacity); m_sceneAssistant->add("m_countMinimumOpacity", &m_countMinimumOpacity); m_sceneAssistant->add("m_distanceStreamline", &m_distanceStreamline); m_sceneAssistant->add("m_distanceMaximumOpacity", &m_distanceMaximumOpacity); m_sceneAssistant->add("m_distanceMinimumOpacity", &m_distanceMinimumOpacity); m_sceneAssistant->add("m_fiberTrajectoryColoringModel", "FiberTrajectoryColorModel", m_fiberTrajectoryColoringModel); } /** * Destructor. */ FiberTrajectoryMapProperties::~FiberTrajectoryMapProperties() { delete m_fiberTrajectoryColoringModel; delete m_sceneAssistant; } /** * @return Display status of trajectory. */ bool FiberTrajectoryMapProperties::isDisplayed() const { return m_displayStatus; } /** * Set the display status for trajectory for the given display group. * @param displayStatus * New status. */ void FiberTrajectoryMapProperties::setDisplayed(const bool displayStatus) { m_displayStatus = displayStatus; } /** * @return The display mode. */ FiberTrajectoryDisplayModeEnum::Enum FiberTrajectoryMapProperties::getDisplayMode() const { return m_displayMode; } /** * Set the display mode to the given value. * @param displayMode * New value for display mode. */ void FiberTrajectoryMapProperties::setDisplayMode(const FiberTrajectoryDisplayModeEnum::Enum displayMode) { m_displayMode = displayMode; } /** * @return The proportion streamline count */ float FiberTrajectoryMapProperties::getProportionStreamline() const { return m_proportionStreamline; } /** * Set the proportion streamline count. * @param pointSize * New value for below limit. */ void FiberTrajectoryMapProperties::setProportionStreamline(const float proportionStreamline) { m_proportionStreamline = proportionStreamline; } /** * @return The proporation maximum opacity. */ float FiberTrajectoryMapProperties::getProportionMaximumOpacity() const { return m_maximumProportionOpacity; } /** * Set the proporation maximum opacity. * @param minimumMagnitude * New value for minimum magnitude. */ void FiberTrajectoryMapProperties::setProportionMaximumOpacity(const float maximumMagnitude) { m_maximumProportionOpacity = maximumMagnitude; } /** * @return The proporation minimum opacity. */ float FiberTrajectoryMapProperties::getProportionMinimumOpacity() const { return m_minimumProportionOpacity; } /** * Set the proporation minimum opacity. * @param minimumOpacity * New value for minimum opacity */ void FiberTrajectoryMapProperties::setProportionMinimumOpacity(const float minimumOpacity) { m_minimumProportionOpacity = minimumOpacity; } /** * @return The count streamline threshold. */ float FiberTrajectoryMapProperties::getCountStreamline() const { return m_countStreamline; } /** * Set the count streamline threshold. * @param countStreamline * New value for count streamline threshold */ void FiberTrajectoryMapProperties::setCountStreamline(const float countStreamline) { m_countStreamline = countStreamline; } /** * @return The count value mapped to maximum opacity. */ float FiberTrajectoryMapProperties::getCountMaximumOpacity() const { return m_countMaximumOpacity; } /** * Set the count maximum opacity. * @param countMaximumOpacity * New value for count mapped to maximum opacity */ void FiberTrajectoryMapProperties::setCountMaximumOpacity(const float countMaximumOpacity) { m_countMaximumOpacity = countMaximumOpacity; } /** * @return The count value mapped to minimum opacity. */ float FiberTrajectoryMapProperties::getCountMinimumOpacity() const { return m_countMinimumOpacity; } /** * Set the count minimum opacity. * @param countMinimumOpacity * New value for count mapped to minimum opacity */ void FiberTrajectoryMapProperties::setCountMinimumOpacity(const float countMinimumOpacity) { m_countMinimumOpacity = countMinimumOpacity; } /** * @return The distance streamline threshold. */ float FiberTrajectoryMapProperties::getDistanceStreamline() const { return m_distanceStreamline; } /** * Set the distance streamline threshold. * @param distanceStreamline * New value for distance streamline threshold */ void FiberTrajectoryMapProperties::setDistanceStreamline(const float distanceStreamline) { m_distanceStreamline = distanceStreamline; } /** * @return The distance value mapped to maximum opacity. */ float FiberTrajectoryMapProperties::getDistanceMaximumOpacity() const { return m_distanceMaximumOpacity; } /** * Set the distance maximum opacity. * @param distanceMaximumOpacity * New value for distance mapped to maximum opacity */ void FiberTrajectoryMapProperties::setDistanceMaximumOpacity(const float distanceMaximumOpacity) { m_distanceMaximumOpacity = distanceMaximumOpacity; } /** * @return The distance value mapped to minimum opacity. */ float FiberTrajectoryMapProperties::getDistanceMinimumOpacity() const { return m_distanceMinimumOpacity; } /** * Set the distance minimum opacity. * @param distanceMinimumOpacity * New value for distance mapped to minimum opacity */ void FiberTrajectoryMapProperties::setDistanceMinimumOpacity(const float distanceMinimumOpacity) { m_distanceMinimumOpacity = distanceMinimumOpacity; } /** * @return the fiber trajectory coloring model. */ FiberTrajectoryColorModel* FiberTrajectoryMapProperties::getFiberTrajectoryColorModel() { return m_fiberTrajectoryColoringModel; } /** * @return the fiber trajectory coloring model. */ const FiberTrajectoryColorModel* FiberTrajectoryMapProperties::getFiberTrajectoryColorModel() const { return m_fiberTrajectoryColoringModel; } /** * Copy the other map properties. * * @param other * The map properties that are copied. */ void FiberTrajectoryMapProperties::copy(const FiberTrajectoryMapProperties& other) { m_displayMode = other.m_displayMode; m_displayStatus = other.m_displayStatus; m_proportionStreamline = other.m_proportionStreamline; m_maximumProportionOpacity = other.m_maximumProportionOpacity; m_minimumProportionOpacity = other.m_minimumProportionOpacity; m_countStreamline = other.m_countStreamline; m_countMaximumOpacity = other.m_countMaximumOpacity; m_countMinimumOpacity = other.m_countMinimumOpacity; m_distanceStreamline = other.m_distanceStreamline; m_distanceMaximumOpacity = other.m_distanceMaximumOpacity; m_distanceMinimumOpacity = other.m_distanceMinimumOpacity; m_fiberTrajectoryColoringModel->copy(*other.getFiberTrajectoryColorModel()); } /** * Create a scene for an instance of a class. * * @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. * * @return Pointer to SceneClass object representing the state of * this object. Under some circumstances a NULL pointer may be * returned. Caller will take ownership of returned object. */ SceneClass* FiberTrajectoryMapProperties::saveToScene(const SceneAttributes* sceneAttributes, const AString& instanceName) { SceneClass* sceneClass = new SceneClass(instanceName, "FiberTrajectoryMapProperties", 1); m_sceneAssistant->saveMembers(sceneAttributes, sceneClass); return sceneClass; } /** * Restore the state of an instance of a class. * * @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 containing the state that was previously * saved and should be restored. */ void FiberTrajectoryMapProperties::restoreFromScene(const SceneAttributes* sceneAttributes, const SceneClass* sceneClass) { if (sceneClass == NULL) { return; } m_sceneAssistant->restoreMembers(sceneAttributes, sceneClass); switch (sceneAttributes->getSceneType()) { case SceneTypeEnum::SCENE_TYPE_FULL: break; case SceneTypeEnum::SCENE_TYPE_GENERIC: break; } } connectome-workbench-1.2.3+git41-gc4c6c90/src/Files/FiberTrajectoryMapProperties.h000066400000000000000000000105501300200146000275710ustar00rootroot00000000000000#ifndef __FIBER_TRAJECTORY_MAP_PROPERTIES_H__ #define __FIBER_TRAJECTORY_MAP_PROPERTIES_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 "BrainConstants.h" #include "FiberTrajectoryDisplayModeEnum.h" #include "SceneableInterface.h" namespace caret { class FiberTrajectoryColorModel; class SceneClassAssistant; class FiberTrajectoryMapProperties : public SceneableInterface { public: FiberTrajectoryMapProperties(); virtual ~FiberTrajectoryMapProperties(); bool isDisplayed() const; void setDisplayed(const bool displayStatus); FiberTrajectoryDisplayModeEnum::Enum getDisplayMode() const; void setDisplayMode(const FiberTrajectoryDisplayModeEnum::Enum displayMode); float getProportionStreamline() const; void setProportionStreamline(const float thresholdStreamline); float getProportionMaximumOpacity() const; void setProportionMaximumOpacity(const float maximumProportionOpacity); float getProportionMinimumOpacity() const; void setProportionMinimumOpacity(const float minimumProportionOpacity); float getCountStreamline() const; void setCountStreamline(const float countStreamline); float getCountMaximumOpacity() const; void setCountMaximumOpacity(const float countMaximumOpacity); float getCountMinimumOpacity() const; void setCountMinimumOpacity(const float countMinimumOpacity); float getDistanceStreamline() const; void setDistanceStreamline(const float distanceStreamline); float getDistanceMaximumOpacity() const; void setDistanceMaximumOpacity(const float distanceMaximumOpacity); float getDistanceMinimumOpacity() const; void setDistanceMinimumOpacity(const float distanceMinimumOpacity); FiberTrajectoryColorModel* getFiberTrajectoryColorModel(); const FiberTrajectoryColorModel* getFiberTrajectoryColorModel() const; virtual SceneClass* saveToScene(const SceneAttributes* sceneAttributes, const AString& instanceName); virtual void restoreFromScene(const SceneAttributes* sceneAttributes, const SceneClass* sceneClass); void copy(const FiberTrajectoryMapProperties& other); private: FiberTrajectoryMapProperties(const FiberTrajectoryMapProperties&); FiberTrajectoryMapProperties& operator=(const FiberTrajectoryMapProperties&); SceneClassAssistant* m_sceneAssistant; FiberTrajectoryDisplayModeEnum::Enum m_displayMode; bool m_displayStatus; float m_proportionStreamline; float m_maximumProportionOpacity; float m_minimumProportionOpacity; float m_countStreamline; float m_countMaximumOpacity; float m_countMinimumOpacity; float m_distanceStreamline; float m_distanceMaximumOpacity; float m_distanceMinimumOpacity; FiberTrajectoryColorModel* m_fiberTrajectoryColoringModel; }; #ifdef __FIBER_TRAJECTORY_MAP_PROPERTIES_DECLARE__ // #endif // __FIBER_TRAJECTORY_MAP_PROPERTIES_DECLARE__ } // namespace #endif //__FIBER_TRAJECTORY_MAP_PROPERTIES_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Files/FilePathNamePrefixCompactor.cxx000066400000000000000000000275511300200146000276670ustar00rootroot00000000000000 /*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 __FILE_PATH_NAME_PREFIX_COMPACTOR_DECLARE__ #include "FilePathNamePrefixCompactor.h" #undef __FILE_PATH_NAME_PREFIX_COMPACTOR_DECLARE__ #include #include #include #include "CaretAssert.h" #include "CaretMappableDataFile.h" #include "CiftiBrainordinateDataSeriesFile.h" #include "CiftiConnectivityMatrixDenseDynamicFile.h" #include "FileInformation.h" using namespace caret; /** * \class caret::FilePathNamePrefixCompactor * \brief Remove matching prefixes from a group of file names. * \ingroup Files * * In the graphical user-interface names of files with a full path * are frequently presented to the user. In some instances, these * paths may be very long, too long to display in the user-interface. * One solution is to place the path after the name of the file. * However, there are instance in which there are file with identical * names but different paths and when the paths are long, the user * may not see the entire path and is unable to fully identify * a file. * * The static methods in this class are used to remove the matching * prefixes (first of characters) from the path * of each file. Do so, allows the user to see the name of the file * followed by the unique portion of the file's path. */ /** * Constructor. */ FilePathNamePrefixCompactor::FilePathNamePrefixCompactor() : CaretObject() { } /** * Destructor. */ FilePathNamePrefixCompactor::~FilePathNamePrefixCompactor() { } /** * Create names that show the filename followed by the path BUT remove * any matching prefix from the paths for a group of CaretDataFiles. * * Example Input File Names: * /mnt/myelin/data/subject2/rsfmri/activity.dscalar.nii * /mnt/myelin/data/subject1/rsfmri/activity.dscalar.nii * Output: * actitivity.dscalar.nii (../subject2/rsfmri) * actitivity.dscalar.nii (../subject1/rsfmri) * * @param caretMappableDataFiles * The caret mappable data files from which names are obtained. * @param prefixRemovedNamesOut * Names of files with matching prefixes removed. Number of elements * will match the number of elements in caretDataFiles. */ void FilePathNamePrefixCompactor::removeMatchingPathPrefixFromCaretDataFiles(const std::vector& caretMappableDataFiles, std::vector& prefixRemovedNamesOut) { std::vector caretDataFiles; for (std::vector::const_iterator iter = caretMappableDataFiles.begin(); iter != caretMappableDataFiles.end(); iter++) { caretDataFiles.push_back(*iter); } removeMatchingPathPrefixFromCaretDataFiles(caretDataFiles, prefixRemovedNamesOut); // std::vector fileNames; // for (std::vector::const_iterator iter = caretMappableDataFiles.begin(); // iter != caretMappableDataFiles.end(); // iter++) { // const CaretDataFile* cdf = *iter; // CaretAssert(cdf); // fileNames.push_back(cdf->getFileName()); // } // // removeMatchingPathPrefixFromFileNames(fileNames, // prefixRemovedNamesOut); } /** * Create names that show the filename followed by the path BUT remove * any matching prefix from the paths for a group of CaretDataFiles. * * Example Input File Names: * /mnt/myelin/data/subject2/rsfmri/activity.dscalar.nii * /mnt/myelin/data/subject1/rsfmri/activity.dscalar.nii * Output: * actitivity.dscalar.nii (../subject2/rsfmri) * actitivity.dscalar.nii (../subject1/rsfmri) * * @param caretDataFiles * The caret data files from which names are obtained. * @param prefixRemovedNamesOut * Names of files with matching prefixes removed. Number of elements * will match the number of elements in caretDataFiles. */ void FilePathNamePrefixCompactor::removeMatchingPathPrefixFromCaretDataFiles(const std::vector& caretDataFiles, std::vector& prefixRemovedNamesOut) { std::vector fileNames; std::vector specialPrefixes; for (std::vector::const_iterator iter = caretDataFiles.begin(); iter != caretDataFiles.end(); iter++) { CaretDataFile* cdf = *iter; CaretAssert(cdf); if (cdf->getDataFileType() == DataFileTypeEnum::CONNECTIVITY_DENSE_DYNAMIC) { // CiftiConnectivityMatrixDenseDynamicFile* denseDynFile = dynamic_cast(cdf); // cdf = denseDynFile->getParentBrainordinateDataSeriesFile(); specialPrefixes.push_back("dynconn - "); } else { specialPrefixes.push_back(""); } fileNames.push_back(cdf->getFileName()); } removeMatchingPathPrefixFromFileNames(fileNames, prefixRemovedNamesOut); const int32_t numFiles = static_cast(prefixRemovedNamesOut.size()); CaretAssert(numFiles == static_cast(specialPrefixes.size())); for (int32_t i = 0; i < numFiles; i++) { prefixRemovedNamesOut[i].insert(0, specialPrefixes[i]); } } /** * Create names that show the filename followed by the path BUT remove * any matching prefix from the paths for a group of CaretDataFiles. * * Example Input File Name: * /mnt/myelin/data/subject2/rsfmri/activity.dscalar.nii * /mnt/myelin/data/subject1/rsfmri/activity.dscalar.nii * Output: * actitivity.dscalar.nii (../subject2/rsfmri) * actitivity.dscalar.nii (../subject1/rsfmri) * * @param caretDataFiles * The caret data files from which names are obtained. * @param prefixRemovedNamesOut * Names of files with matching prefixes removed. Number of elements * will match the number of elements in caretDataFiles. */ void FilePathNamePrefixCompactor::removeMatchingPathPrefixFromCaretDataFile(const CaretDataFile* caretDataFile, AString& prefixRemovedNameOut) { std::vector nameVector; nameVector.push_back(caretDataFile->getFileName()); std::vector prefixVector; removeMatchingPathPrefixFromFileNames(nameVector, prefixVector); CaretAssert(nameVector.size() == prefixVector.size()); CaretAssert(prefixVector.size() == 1); prefixRemovedNameOut = prefixVector[0]; } /** * Create names that show the filename followed by the path BUT remove * any matching prefix from the paths for a group of file names. * * Example Input File Names: * /mnt/myelin/data/subject2/rsfmri/activity.dscalar.nii * /mnt/myelin/data/subject1/rsfmri/activity.dscalar.nii * Output: * actitivity.dscalar.nii (../subject2/rsfmri) * actitivity.dscalar.nii (../subject1/rsfmri) * * @param caretDataFiles * The caret data files from which names are obtained. * @param prefixRemovedNamesOut * Names of files with matching prefixes removed. Number of elements * will match the number of elements in caretDataFiles. */ void FilePathNamePrefixCompactor::removeMatchingPathPrefixFromFileNames(const std::vector& fileNames, std::vector& prefixRemovedNamesOut) { prefixRemovedNamesOut.clear(); const int32_t numFiles = static_cast(fileNames.size()); if (numFiles == 1) { FileInformation fileInfo(fileNames[0]); prefixRemovedNamesOut.push_back(fileInfo.getFileName()); return; } else if (numFiles < 1){ return; } std::vector > pathComponentEachFile; int32_t mininumComponentCount = std::numeric_limits::max(); /* * For each file, split its path components (parts between '/') and * place them into a vector. Also find the minimum number from * all of the paths as that will be the maximum number of matching * components. */ for (std::vector::const_iterator iter = fileNames.begin(); iter != fileNames.end(); iter++) { FileInformation fileInfo(*iter); const QString path = fileInfo.getPathName(); QStringList pathComponentsList = path.split('/'); mininumComponentCount = std::min(mininumComponentCount, pathComponentsList.size()); std::vector pathComponents; for (int32_t ip = 0; ip < pathComponentsList.size(); ip++) { pathComponents.push_back(pathComponentsList[ip]); } pathComponentEachFile.push_back(pathComponents); } CaretAssert(static_cast(pathComponentEachFile.size()) == numFiles); /* * For each of the file names, examine and compare its path * components to all of the other file path components to * determine the matching prefix. */ int32_t numMatchingLeadingComponents = 0; for (int32_t iComp = 0; iComp < mininumComponentCount; iComp++) { CaretAssertVectorIndex(pathComponentEachFile, 0); const std::vector& firstFileCompontents = pathComponentEachFile[0]; const AString component = firstFileCompontents[iComp]; bool doneFlag = false; for (int32_t jFile = 1; jFile < numFiles; jFile++) { CaretAssertVectorIndex(pathComponentEachFile, jFile); const std::vector& fileComps = pathComponentEachFile[jFile]; CaretAssertVectorIndex(fileComps, iComp); if (fileComps[iComp] != component) { doneFlag = true; break; } } if (doneFlag) { break; } else { numMatchingLeadingComponents++; } } /* * For each of the file names, create a name in the form of: * filename (unique-suffix-of-path). */ for (int32_t iFile = 0; iFile < numFiles; iFile++) { CaretAssertVectorIndex(fileNames, iFile); FileInformation fileInfo(fileNames[iFile]); AString name = fileInfo.getFileName() + " "; CaretAssertVectorIndex(pathComponentEachFile, iFile); const std::vector& fileComps = pathComponentEachFile[iFile]; const int32_t numComps = static_cast(fileComps.size()); bool addedComponentsFlag = false; for (int32_t iComp = numMatchingLeadingComponents; iComp < numComps; iComp++) { if (iComp == numMatchingLeadingComponents) { name.append("(..../"); } CaretAssertVectorIndex(fileComps, iComp); name.append(fileComps[iComp] + "/"); addedComponentsFlag = true; } if (addedComponentsFlag) { name.append(")"); } prefixRemovedNamesOut.push_back(name); } CaretAssert(fileNames.size() == prefixRemovedNamesOut.size()); } connectome-workbench-1.2.3+git41-gc4c6c90/src/Files/FilePathNamePrefixCompactor.h000066400000000000000000000047351300200146000273130ustar00rootroot00000000000000#ifndef __FILE_PATH_NAME_PREFIX_COMPACTOR_H__ #define __FILE_PATH_NAME_PREFIX_COMPACTOR_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" namespace caret { class CaretDataFile; class CaretMappableDataFile; class FilePathNamePrefixCompactor : public CaretObject { public: static void removeMatchingPathPrefixFromCaretDataFiles(const std::vector& caretMappableDataFiles, std::vector& prefixRemovedNamesOut); static void removeMatchingPathPrefixFromCaretDataFiles(const std::vector& caretDataFiles, std::vector& prefixRemovedNamesOut); static void removeMatchingPathPrefixFromCaretDataFile(const CaretDataFile* caretDataFile, AString& prefixRemovedNameOut); private: FilePathNamePrefixCompactor(); virtual ~FilePathNamePrefixCompactor(); FilePathNamePrefixCompactor(const FilePathNamePrefixCompactor&); FilePathNamePrefixCompactor& operator=(const FilePathNamePrefixCompactor&); static void removeMatchingPathPrefixFromFileNames(const std::vector& fileNames, std::vector& prefixRemovedNamesOut); }; #ifdef __FILE_PATH_NAME_PREFIX_COMPACTOR_DECLARE__ // #endif // __FILE_PATH_NAME_PREFIX_COMPACTOR_DECLARE__ } // namespace #endif //__FILE_PATH_NAME_PREFIX_COMPACTOR_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Files/FociFile.cxx000066400000000000000000000424751300200146000240260ustar00rootroot00000000000000 /*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 #define __FOCI_FILE_DECLARE__ #include "FociFile.h" #undef __FOCI_FILE_DECLARE__ #include "CaretAssert.h" #include "CaretLogger.h" #include "DataFileContentInformation.h" #include "DataFileException.h" #include "GroupAndNameHierarchyModel.h" #include "FileAdapter.h" #include "FociFileSaxReader.h" #include "Focus.h" #include "GiftiLabel.h" #include "GiftiLabelTable.h" #include "GiftiMetaData.h" #include "SurfaceProjectedItem.h" #include "XmlAttributes.h" #include "XmlSaxParser.h" #include "XmlWriter.h" using namespace caret; /** * \class caret::FociFile * \brief A foci (plural of focus) file. */ /** * Constructor. */ FociFile::FociFile() : CaretDataFile(DataFileTypeEnum::FOCI) { initializeFociFile(); } /** * Destructor. */ FociFile::~FociFile() { delete m_nameColorTable; delete m_classColorTable; delete m_metadata; for (std::vector::iterator iter = m_foci.begin(); iter != m_foci.end(); iter++) { delete *iter; } m_foci.clear(); delete m_classNameHierarchy; } /** * Copy constructor. * @param obj * Object that is copied. */ FociFile::FociFile(const FociFile& obj) : CaretDataFile(obj) { initializeFociFile(); copyHelperFociFile(obj); } /** * Assignment operator. * @param obj * Data copied from obj to this. * @return * Reference to this object. */ FociFile& FociFile::operator=(const FociFile& obj) { if (this != &obj) { clear(); CaretDataFile::operator=(obj); copyHelperFociFile(obj); } return *this; } void FociFile::initializeFociFile() { m_classColorTable = new GiftiLabelTable(); m_nameColorTable = new GiftiLabelTable(); m_classNameHierarchy = new GroupAndNameHierarchyModel(); m_metadata = new GiftiMetaData(); m_forceUpdateOfGroupAndNameHierarchy = true; } /** * Helps with copying an object of this type. * @param ff * Object that is copied. */ void FociFile::copyHelperFociFile(const FociFile& ff) { *m_classColorTable = *ff.m_classColorTable; *m_nameColorTable = *ff.m_nameColorTable; if (m_classNameHierarchy != NULL) { delete m_classNameHierarchy; } m_classNameHierarchy = new GroupAndNameHierarchyModel(); *m_metadata = *ff.m_metadata; const int32_t numFoci = getNumberOfFoci(); for (int32_t i = 0; i < numFoci; i++) { m_foci.push_back(new Focus(*ff.getFocus(i))); } m_forceUpdateOfGroupAndNameHierarchy = true; setModified(); } /** * @return Is this foci file empty (contains zero focuss)? */ bool FociFile::isEmpty() const { return m_foci.empty(); } /** * @return The structure for this file. */ StructureEnum::Enum FociFile::getStructure() const { return StructureEnum::ALL; } /** * Set the structure for this file. * @param structure * New structure for this file. */ void FociFile::setStructure(const StructureEnum::Enum /*structure*/) { // does nothing since focuss apply to all structures } /** * @return Get access to the file's metadata. */ GiftiMetaData* FociFile::getFileMetaData() { return m_metadata; } /** * @return Get access to unmodifiable file's metadata. */ const GiftiMetaData* FociFile::getFileMetaData() const { return m_metadata; } /** * Clear the focus file. */ void FociFile::clear() { CaretDataFile::clear(); m_classNameHierarchy->clear(); m_classColorTable->clear(); m_nameColorTable->clear(); m_metadata->clear(); const int32_t numFoci = getNumberOfFoci(); for (int32_t i = 0; i < numFoci; i++) { delete m_foci[i]; } m_foci.clear(); } /** * @return the number of foci. */ int32_t FociFile::getNumberOfFoci() const { return m_foci.size(); } /** * Get the focus at the given index. * @param indx * Index of the focus. * @return * focus at the given index. */ Focus* FociFile::getFocus(const int32_t indx) { CaretAssertVectorIndex(m_foci, indx); return m_foci[indx]; } /** * Get the focus at the given index. * @param indx * Index of the focus. * @return * focus at the given index. */ const Focus* FociFile::getFocus(const int32_t indx) const { CaretAssertVectorIndex(m_foci, indx); return m_foci[indx]; } /** * Add a focus. NOTE: This focus file * takes ownership of the 'focus' and * will handle deleting it. After calling * this method, the caller must never * do anything with the focus that was passed * to this method. * * @param focus * Focus added to this focus file. */ void FociFile::addFocus(Focus* focus) { m_foci.push_back(focus); const AString name = focus->getName(); if (name.isEmpty() == false) { const int32_t nameColorKey = m_nameColorTable->getLabelKeyFromName(name); if (nameColorKey < 0) { m_nameColorTable->addLabel(name, 0.0f, 0.0f, 0.0f, 1.0f); } } AString className = focus->getClassName(); if (className.isEmpty() == false) { const int32_t classColorKey = m_classColorTable->getLabelKeyFromName(className); if (classColorKey < 0) { m_classColorTable->addLabel(className, 0.0f, 0.0f, 0.0f, 1.0f); } } m_forceUpdateOfGroupAndNameHierarchy = true; setModified(); } /** * Remove the focus at the given index. * @param indx * Index of focus for removal. */ void FociFile::removeFocus(const int32_t indx) { CaretAssertVectorIndex(m_foci, indx); Focus* focus = getFocus(indx); m_foci.erase(m_foci.begin() + indx); delete focus; m_forceUpdateOfGroupAndNameHierarchy = true; setModified(); } /** * Remove the focus. * @param focus * Focus that will be removed and DELETED. */ void FociFile::removeFocus(Focus* focus) { const int32_t numFoci = getNumberOfFoci(); for (int32_t i = 0;i < numFoci; i++) { if (m_foci[i] == focus) { removeFocus(i); return; } } CaretLogWarning("Attempting to delete focus not in focus file with name: " + focus->getName()); } /** * @return The class and name hierarchy. */ GroupAndNameHierarchyModel* FociFile::getGroupAndNameHierarchyModel() { m_classNameHierarchy->update(this, m_forceUpdateOfGroupAndNameHierarchy); m_forceUpdateOfGroupAndNameHierarchy = false; return m_classNameHierarchy; } /** * @return The class color table. */ GiftiLabelTable* FociFile::getClassColorTable() { return m_classColorTable; } /** * @return The class color table. */ const GiftiLabelTable* FociFile::getClassColorTable() const { return m_classColorTable; } /** * @return The name color table. */ GiftiLabelTable* FociFile::getNameColorTable() { return m_nameColorTable; } /** * @return The name color table. */ const GiftiLabelTable* FociFile::getNameColorTable() const { return m_nameColorTable; } /** * Version 1 foci files contained one color table for both names * and classes. Newer versions of the foci file keep them in * separate tables. * * @param oldColorTable * Old color table that is split into name and class color tables. */ void FociFile::createNameAndClassColorTables(const GiftiLabelTable* oldColorTable) { CaretAssert(oldColorTable); m_classColorTable->clear(); m_nameColorTable->clear(); std::set nameSet; std::set classSet; const int numFoci = getNumberOfFoci(); for (int32_t i = 0; i < numFoci; i++) { const Focus* focus = getFocus(i); nameSet.insert(focus->getName()); classSet.insert(focus->getClassName()); } /* * Create colors for only the "best matching" color. */ for (std::set::iterator iter = nameSet.begin(); iter != nameSet.end(); iter++) { const AString colorName = *iter; const GiftiLabel* oldLabel = oldColorTable->getLabelBestMatching(colorName); if (oldLabel != NULL) { const AString bestMatchingName = oldLabel->getName(); const int32_t labelKey = m_nameColorTable->getLabelKeyFromName(bestMatchingName); if (labelKey < 0) { float rgba[4] = { 0.0, 0.0, 0.0, 1.0 }; oldLabel->getColor(rgba); m_nameColorTable->addLabel(bestMatchingName, rgba[0], rgba[1], rgba[2], rgba[3]); } } } /* * Create a color for each class name using the best matching color */ for (std::set::iterator iter = classSet.begin(); iter != classSet.end(); iter++) { const AString colorName = *iter; const GiftiLabel* label = oldColorTable->getLabelBestMatching(colorName); float rgba[4] = { 0.0, 0.0, 0.0, 1.0 }; if (label != NULL) { label->getColor(rgba); } m_classColorTable->addLabel(colorName, rgba[0], rgba[1], rgba[2], rgba[3]); } } /** * @return A string list containing all foci names * sorted in alphabetical order. */ QStringList FociFile::getAllFociNamesSorted() const { std::set nameSet; const int32_t numFoci = getNumberOfFoci(); for (int32_t i = 0;i < numFoci; i++) { nameSet.insert(m_foci[i]->getName()); } QStringList sl; for (std::set::iterator iter = nameSet.begin(); iter != nameSet.end(); iter++) { sl += *iter; } return sl; } /** * @return The version of the file as a number. */ int32_t FociFile::getFileVersion() { return FociFile::s_fociFileVersion; } /** * @return The version of the file as a string. */ AString FociFile::getFileVersionAsString() { return AString::number(FociFile::getFileVersion()); } /** * Invalidate all assigned colors. */ void FociFile::invalidateAllAssignedColors() { const int32_t numFoci = getNumberOfFoci(); for (int32_t i = 0;i < numFoci; i++) { m_foci[i]->setNameRgbaInvalid(); m_foci[i]->setClassRgbaInvalid(); } m_forceUpdateOfGroupAndNameHierarchy = true; } /** * Read the data file. * * @param filename * Name of the data file. * @throws DataFileException * If the file was not successfully read. */ void FociFile::readFile(const AString& filename) { clear(); checkFileReadability(filename); FociFileSaxReader saxReader(this); std::auto_ptr parser(XmlSaxParser::createXmlParser()); try { parser->parseFile(filename, &saxReader); } catch (const XmlSaxParserException& e) { clear(); setFileName(""); int lineNum = e.getLineNumber(); int colNum = e.getColumnNumber(); AString msg = "Parse Error while reading:"; if ((lineNum >= 0) && (colNum >= 0)) { msg += (" line/col (" + AString::number(e.getLineNumber()) + "/" + AString::number(e.getColumnNumber()) + ")"); } msg += (": " + e.whatString()); DataFileException dfe(filename, msg); CaretLogThrowing(dfe); throw dfe; } setFileName(filename); m_classNameHierarchy->update(this, true); m_forceUpdateOfGroupAndNameHierarchy = false; m_classNameHierarchy->setAllSelected(true); CaretLogFiner("CLASS/NAME Table for : " + getFileNameNoPath() + "\n" + m_classNameHierarchy->toString()); clearModified(); } /** * Write the data file. * * @param filename * Name of the data file. * @throws DataFileException * If the file was not successfully written. */ void FociFile::writeFile(const AString& filename) { if (!(filename.endsWith(".foci") || filename.endsWith(".wb_foci"))) { CaretLogWarning("foci file '" + filename + "'should be saved ending in .foci"); } checkFileWritability(filename); setFileName(filename); try { // // Format the version string so that it ends with at most one zero // const AString versionString = FociFile::getFileVersionAsString(); // // Open the file // FileAdapter file; AString errorMessage; QTextStream* textStream = file.openQTextStreamForWritingFile(getFileName(), errorMessage); if (textStream == NULL) { throw DataFileException(getFileName(), errorMessage); } // // Create the xml writer // XmlWriter xmlWriter(*textStream); // // Write header info // xmlWriter.writeStartDocument("1.0"); // // Write GIFTI root element // XmlAttributes attributes; //attributes.addAttribute("xmlns:xsi", // "http://www.w3.org/2001/XMLSchema-instance"); //attributes.addAttribute("xsi:noNamespaceSchemaLocation", // "http://brainvis.wustl.edu/caret6/xml_schemas/GIFTI_Caret.xsd"); attributes.addAttribute(FociFile::XML_ATTRIBUTE_VERSION, versionString); xmlWriter.writeStartElement(FociFile::XML_TAG_FOCI_FILE, attributes); // // Write Metadata // if (m_metadata != NULL) { m_metadata->writeAsXML(xmlWriter); } // // Write the class color table // xmlWriter.writeStartElement(XML_TAG_CLASS_COLOR_TABLE); m_classColorTable->writeAsXML(xmlWriter); xmlWriter.writeEndElement(); // // Write the name color table // xmlWriter.writeStartElement(XML_TAG_NAME_COLOR_TABLE); m_nameColorTable->writeAsXML(xmlWriter); xmlWriter.writeEndElement(); // // Write foci // const int32_t numFoci = getNumberOfFoci(); for (int32_t i = 0; i < numFoci; i++) { m_foci[i]->writeAsXML(xmlWriter, i); } xmlWriter.writeEndElement(); xmlWriter.writeEndDocument(); file.close(); clearModified(); } catch (const GiftiException& e) { throw DataFileException(e); } catch (const XmlException& e) { throw DataFileException(e); } } /** * @return Is this foci file modified? */ bool FociFile::isModified() const { if (CaretDataFile::isModified()) { return true; } if (m_metadata->isModified()) { return true; } if (m_classColorTable->isModified()) { return true; } if (m_nameColorTable->isModified()) { return true; } /* * Note, these members do not affect modification status: * classNameHierarchy */ const int32_t numFoci = getNumberOfFoci(); for (int32_t i = 0; i < numFoci; i++) { if (m_foci[i]->isModified()) { return true; } } return false; } /** * Clear the modification status of this foci file. */ void FociFile::clearModified() { CaretDataFile::clearModified(); m_metadata->clearModified(); m_classColorTable->clearModified(); m_nameColorTable->clearModified(); const int32_t numFoci = getNumberOfFoci(); for (int32_t i = 0; i < numFoci; i++) { m_foci[i]->clearModified(); } } /** * Add information about the file to the data file information. * * @param dataFileInformation * Consolidates information about a data file. */ void FociFile::addToDataFileContentInformation(DataFileContentInformation& dataFileInformation) { CaretDataFile::addToDataFileContentInformation(dataFileInformation); QStringList fociNames = getAllFociNamesSorted(); const int32_t numNames = fociNames.size(); if (numNames > 0) { AString namesListText = "FOCI NAMES"; for (int32_t i = 0; i < numNames; i++) { namesListText.appendWithNewLine(" " + fociNames.at(i)); } dataFileInformation.addText(namesListText); } } connectome-workbench-1.2.3+git41-gc4c6c90/src/Files/FociFile.h000066400000000000000000000106301300200146000234370ustar00rootroot00000000000000#ifndef __FOCI_FILE__H_ #define __FOCI_FILE__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 "CaretDataFile.h" namespace caret { class GroupAndNameHierarchyModel; class Focus; class GiftiLabelTable; class GiftiMetaData; class FociFile : public CaretDataFile { public: FociFile(); virtual ~FociFile(); FociFile(const FociFile& obj); FociFile& operator=(const FociFile& obj); virtual void addToDataFileContentInformation(DataFileContentInformation& dataFileInformation); GiftiMetaData* getFileMetaData(); const GiftiMetaData* getFileMetaData() const; StructureEnum::Enum getStructure() const; void setStructure(const StructureEnum::Enum structure); void readFile(const AString& filename); void writeFile(const AString& filename); void clear(); bool isEmpty() const; int32_t getNumberOfFoci() const; void addFocus(Focus* focus); Focus* getFocus(const int32_t indx); const Focus* getFocus(const int32_t indx) const; void removeFocus(const int32_t indx); void removeFocus(Focus* focus); GiftiLabelTable* getClassColorTable(); const GiftiLabelTable* getClassColorTable() const; GiftiLabelTable* getNameColorTable(); const GiftiLabelTable* getNameColorTable() const; void createNameAndClassColorTables(const GiftiLabelTable* oldColorTable); GroupAndNameHierarchyModel* getGroupAndNameHierarchyModel(); QStringList getAllFociNamesSorted() const; void invalidateAllAssignedColors(); virtual bool isModified() const; virtual void clearModified(); static int32_t getFileVersion(); static AString getFileVersionAsString(); /** XML Tag for foci file */ static const AString XML_TAG_FOCI_FILE; /** XML Tag for Version attribute */ static const AString XML_ATTRIBUTE_VERSION; /** XML Tag for Name Color Table */ static const AString XML_TAG_NAME_COLOR_TABLE; /** XML Tag for Class Color Table */ static const AString XML_TAG_CLASS_COLOR_TABLE; private: void copyHelperFociFile(const FociFile& obj); void initializeFociFile(); GiftiMetaData* m_metadata; std::vector m_foci; /** Holds colors assigned to classes */ GiftiLabelTable* m_classColorTable; /** Holds colors assigned to names */ GiftiLabelTable* m_nameColorTable; /** Holds class and name hierarchy used for display selection */ mutable GroupAndNameHierarchyModel* m_classNameHierarchy; /** force an update of the class and name hierarchy */ bool m_forceUpdateOfGroupAndNameHierarchy; /** Version of this FociFile */ static const int32_t s_fociFileVersion; }; #ifdef __FOCI_FILE_DECLARE__ const AString FociFile::XML_TAG_FOCI_FILE = "FociFile"; const AString FociFile::XML_ATTRIBUTE_VERSION = "Version"; const AString FociFile::XML_TAG_NAME_COLOR_TABLE = "FociNameColorTable"; const AString FociFile::XML_TAG_CLASS_COLOR_TABLE = "FociClassColorTable"; const int32_t FociFile::s_fociFileVersion = 2; #endif // __FOCI_FILE_DECLARE__ } // namespace #endif //__FOCI_FILE__H_ connectome-workbench-1.2.3+git41-gc4c6c90/src/Files/FociFileSaxReader.cxx000066400000000000000000000352071300200146000256200ustar00rootroot00000000000000/*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 "CaretAssert.h" #include "GiftiLabelTable.h" #include "CaretLogger.h" #include "GiftiXmlElements.h" #include "FociFile.h" #include "FociFileSaxReader.h" #include "Focus.h" #include "GiftiLabelTableSaxReader.h" #include "GiftiMetaDataSaxReader.h" #include "StudyMetaDataLinkSet.h" #include "StudyMetaDataLinkSetSaxReader.h" #include "SurfaceProjectedItem.h" #include "SurfaceProjectedItemSaxReader.h" #include "XmlAttributes.h" #include "XmlException.h" #include "XmlUtilities.h" using namespace caret; /** * \class caret::FociFileSaxReader * \brief Reads a foci file using a SAX XML Parser. */ /** * constructor. */ FociFileSaxReader::FociFileSaxReader(FociFile* fociFile) { CaretAssert(fociFile); m_fociFile = fociFile; m_state = STATE_NONE; m_stateStack.push(m_state); m_elementText = ""; m_metaDataSaxReader = NULL; m_labelTableSaxReader = NULL; m_surfaceProjectedItemSaxReader = NULL; m_focus = NULL; m_surfaceProjectedItem = NULL; m_studyMetaDataLinkSetSaxReader = NULL; m_projectionCounter = 0; m_versionOneColorTable = NULL; } /** * destructor. */ FociFileSaxReader::~FociFileSaxReader() { /* * If reading fails, allocated items need to be deleted. */ if (m_metaDataSaxReader != NULL) { delete m_metaDataSaxReader; } if (m_labelTableSaxReader != NULL) { delete m_labelTableSaxReader; } if (m_surfaceProjectedItemSaxReader != NULL) { delete m_surfaceProjectedItemSaxReader; } if (m_surfaceProjectedItem != NULL) { delete m_surfaceProjectedItem; } if (m_focus != NULL) { delete m_focus; } if (m_studyMetaDataLinkSetSaxReader != NULL) { delete m_studyMetaDataLinkSetSaxReader; } if (m_versionOneColorTable != NULL) { delete m_versionOneColorTable; } } /** * start an element. */ void FociFileSaxReader::startElement(const AString& namespaceURI, const AString& localName, const AString& qName, const XmlAttributes& attributes) { const STATE previousState = m_state; switch (m_state) { case STATE_NONE: if (qName == FociFile::XML_TAG_FOCI_FILE) { m_state = STATE_FOCI_FILE; /* * At one time version was float, but now integer so if * getting the version fails as integer get as float * and convert to integer. */ int32_t versionBeingRead = 0; try { versionBeingRead = attributes.getValueAsInt(FociFile::XML_ATTRIBUTE_VERSION); } catch (const XmlSaxParserException& /*e*/) { const float floatVersion = attributes.getValueAsFloat(FociFile::XML_ATTRIBUTE_VERSION); versionBeingRead = static_cast(floatVersion); } if (versionBeingRead > FociFile::getFileVersion()) { AString msg = XmlUtilities::createInvalidVersionMessage(FociFile::getFileVersion(), versionBeingRead); XmlSaxParserException e(msg); CaretLogThrowing(e); throw e; } } else { const AString msg = XmlUtilities::createInvalidRootElementMessage(FociFile::XML_TAG_FOCI_FILE, qName); XmlSaxParserException e(msg); CaretLogThrowing(e); throw e; } break; case STATE_FOCUS: if (qName == SurfaceProjectedItem::XML_TAG_SURFACE_PROJECTED_ITEM) { m_state = STATE_SURFACE_PROJECTED_ITEM; m_surfaceProjectedItem = new SurfaceProjectedItem(); m_surfaceProjectedItemSaxReader = new SurfaceProjectedItemSaxReader(m_surfaceProjectedItem); m_surfaceProjectedItemSaxReader->startElement(namespaceURI, localName, qName, attributes); } else if (qName == StudyMetaDataLinkSet::XML_TAG_STUDY_META_DATA_LINK_SET) { m_state = STATE_STUDY_META_DATA_LINK_SET; m_studyMetaDataLinkSetSaxReader = new StudyMetaDataLinkSetSaxReader(m_focus->getStudyMetaDataLinkSet()); m_studyMetaDataLinkSetSaxReader->startElement(namespaceURI, localName, qName, attributes); } break; case STATE_FOCI_FILE: if (qName == FociFile::XML_TAG_CLASS_COLOR_TABLE) { m_state = STATE_CLASS_COLOR_TABLE; } else if (qName == FociFile::XML_TAG_NAME_COLOR_TABLE) { m_state = STATE_NAME_COLOR_TABLE; } else if (qName == GiftiXmlElements::TAG_LABEL_TABLE) { m_state = STATE_VERSION_ONE_COLOR_TABLE; m_versionOneColorTable = new GiftiLabelTable(); m_labelTableSaxReader = new GiftiLabelTableSaxReader(m_versionOneColorTable); m_labelTableSaxReader->startElement(namespaceURI, localName, qName, attributes); } else if (qName == GiftiXmlElements::TAG_METADATA) { m_state = STATE_METADATA; m_metaDataSaxReader = new GiftiMetaDataSaxReader(m_fociFile->getFileMetaData()); m_metaDataSaxReader->startElement(namespaceURI, localName, qName, attributes); } else if (qName == Focus::XML_TAG_FOCUS) { m_state = STATE_FOCUS; m_focus = new Focus(); m_projectionCounter = 0; } else { const AString msg = XmlUtilities::createInvalidChildElementMessage(FociFile::XML_TAG_FOCI_FILE, qName); XmlSaxParserException e(msg); CaretLogThrowing(e); throw e; } break; case STATE_METADATA: m_metaDataSaxReader->startElement(namespaceURI, localName, qName, attributes); break; case STATE_VERSION_ONE_COLOR_TABLE: m_labelTableSaxReader->startElement(namespaceURI, localName, qName, attributes); break; case STATE_CLASS_COLOR_TABLE: if (qName == GiftiXmlElements::TAG_LABEL_TABLE) { m_labelTableSaxReader = new GiftiLabelTableSaxReader(m_fociFile->getClassColorTable()); m_labelTableSaxReader->startElement(namespaceURI, localName, qName, attributes); } else if (qName == GiftiXmlElements::TAG_LABEL) { m_labelTableSaxReader->startElement(namespaceURI, localName, qName, attributes); } else { const AString msg = XmlUtilities::createInvalidChildElementMessage(FociFile::XML_TAG_CLASS_COLOR_TABLE, qName); XmlSaxParserException e(msg); CaretLogThrowing(e); throw e; } break; case STATE_NAME_COLOR_TABLE: if (qName == GiftiXmlElements::TAG_LABEL_TABLE) { m_labelTableSaxReader = new GiftiLabelTableSaxReader(m_fociFile->getNameColorTable()); m_labelTableSaxReader->startElement(namespaceURI, localName, qName, attributes); } else if (qName == GiftiXmlElements::TAG_LABEL) { m_labelTableSaxReader->startElement(namespaceURI, localName, qName, attributes); } else { const AString msg = XmlUtilities::createInvalidChildElementMessage(FociFile::XML_TAG_NAME_COLOR_TABLE, qName); XmlSaxParserException e(msg); CaretLogThrowing(e); throw e; } break; case STATE_SURFACE_PROJECTED_ITEM: m_surfaceProjectedItemSaxReader->startElement(namespaceURI, localName, qName, attributes); break; case STATE_STUDY_META_DATA_LINK_SET: m_studyMetaDataLinkSetSaxReader->startElement(namespaceURI, localName, qName, attributes); break; } // // Save previous state // m_stateStack.push(previousState); m_elementText = ""; } /** * end an element. */ void FociFileSaxReader::endElement(const AString& namespaceURI, const AString& localName, const AString& qName) { switch (m_state) { case STATE_NONE: break; case STATE_FOCUS: CaretAssert(m_focus); if (qName == Focus::XML_TAG_FOCUS) { m_fociFile->addFocus(m_focus); m_focus = NULL; // do not delete since added to foci file m_projectionCounter = 0; } else { m_focus->setElementFromText(qName, m_elementText.trimmed()); } break; case STATE_FOCI_FILE: break; case STATE_METADATA: CaretAssert(m_metaDataSaxReader); m_metaDataSaxReader->endElement(namespaceURI, localName, qName); if (qName == GiftiXmlElements::TAG_METADATA) { delete m_metaDataSaxReader; m_metaDataSaxReader = NULL; } break; case STATE_VERSION_ONE_COLOR_TABLE: CaretAssert(m_labelTableSaxReader); m_labelTableSaxReader->endElement(namespaceURI, localName, qName); if (qName == GiftiXmlElements::TAG_LABEL_TABLE) { delete m_labelTableSaxReader; m_labelTableSaxReader = NULL; } break; case STATE_CLASS_COLOR_TABLE: if (qName != FociFile::XML_TAG_CLASS_COLOR_TABLE) { CaretAssert(m_labelTableSaxReader); m_labelTableSaxReader->endElement(namespaceURI, localName, qName); if (qName == GiftiXmlElements::TAG_LABEL_TABLE) { delete m_labelTableSaxReader; m_labelTableSaxReader = NULL; } } break; case STATE_NAME_COLOR_TABLE: if (qName != FociFile::XML_TAG_NAME_COLOR_TABLE) { CaretAssert(m_labelTableSaxReader); m_labelTableSaxReader->endElement(namespaceURI, localName, qName); if (qName == GiftiXmlElements::TAG_LABEL_TABLE) { delete m_labelTableSaxReader; m_labelTableSaxReader = NULL; } } break; case STATE_SURFACE_PROJECTED_ITEM: CaretAssert(m_surfaceProjectedItemSaxReader); m_surfaceProjectedItemSaxReader->endElement(namespaceURI, localName, qName); if (qName == SurfaceProjectedItem::XML_TAG_SURFACE_PROJECTED_ITEM) { if (m_projectionCounter == 0) { /* * Remove all projections when the first projection * is read so that projections can be added as * they are read. */ m_focus->removeAllProjections(); } m_projectionCounter++; m_focus->addProjection(m_surfaceProjectedItem); m_surfaceProjectedItem = NULL; // do not delete since added to focus delete m_surfaceProjectedItemSaxReader; m_surfaceProjectedItemSaxReader = NULL; } break; case STATE_STUDY_META_DATA_LINK_SET: CaretAssert(m_studyMetaDataLinkSetSaxReader); m_studyMetaDataLinkSetSaxReader->endElement(namespaceURI, localName, qName); if (qName == StudyMetaDataLinkSet::XML_TAG_STUDY_META_DATA_LINK_SET) { delete m_studyMetaDataLinkSetSaxReader; m_studyMetaDataLinkSetSaxReader = NULL; } } // // Clear out for new elements // m_elementText = ""; // // Go to previous state // if (m_stateStack.empty()) { throw XmlSaxParserException("State stack is empty while reading XML NiftDataFile."); } m_state = m_stateStack.top(); m_stateStack.pop(); } /** * get characters in an element. */ void FociFileSaxReader::characters(const char* ch) { if (m_metaDataSaxReader != NULL) { m_metaDataSaxReader->characters(ch); } else if (m_labelTableSaxReader != NULL) { m_labelTableSaxReader->characters(ch); } else if (m_surfaceProjectedItemSaxReader != NULL) { m_surfaceProjectedItemSaxReader->characters(ch); } else { m_elementText += ch; } } /** * a fatal error occurs. */ void FociFileSaxReader::fatalError(const XmlSaxParserException& e) { /* std::ostringstream str; str << "Fatal Error at line number: " << e.getLineNumber() << "\n" << "Column number: " << e.getColumnNumber() << "\n" << "Message: " << e.whatString(); if (errorMessage.isEmpty() == false) { str << "\n" << errorMessage; } errorMessage = str.str(); */ // // Stop parsing // throw e; } // a warning occurs void FociFileSaxReader::warning(const XmlSaxParserException& e) { CaretLogWarning("XML Parser Warning: " + e.whatString()); } // an error occurs void FociFileSaxReader::error(const XmlSaxParserException& e) { CaretLogSevere("XML Parser Error: " + e.whatString()); throw e; } void FociFileSaxReader::startDocument() { } void FociFileSaxReader::endDocument() { if (m_versionOneColorTable != NULL) { m_fociFile->createNameAndClassColorTables(m_versionOneColorTable); } } connectome-workbench-1.2.3+git41-gc4c6c90/src/Files/FociFileSaxReader.h000066400000000000000000000105231300200146000252370ustar00rootroot00000000000000 #ifndef __FOCI_FILE_SAX_READER_H__ #define __FOCI_FILE_SAX_READER_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 "XmlSaxParserException.h" #include "XmlSaxParserHandlerInterface.h" namespace caret { class Focus; class FociFile; class GiftiLabelTableSaxReader; class GiftiMetaDataSaxReader; class StudyMetaDataLinkSetSaxReader; class SurfaceProjectedItem; class SurfaceProjectedItemSaxReader; class XmlAttributes; class XmlException; class FociFileSaxReader : public CaretObject, public XmlSaxParserHandlerInterface { public: FociFileSaxReader(FociFile* fociFile); virtual ~FociFileSaxReader(); void startElement(const AString& namespaceURI, const AString& localName, const AString& qName, const XmlAttributes& attributes); void endElement(const AString& namspaceURI, const AString& localName, const AString& qName); void characters(const char* ch); void fatalError(const XmlSaxParserException& e); void warning(const XmlSaxParserException& e); void error(const XmlSaxParserException& e); void startDocument(); void endDocument(); protected: /// file reading states enum STATE { /// no state STATE_NONE, /// processing FociFile tag STATE_FOCI_FILE, /// processing MetaData tag STATE_METADATA, /// processing version one color table tag STATE_VERSION_ONE_COLOR_TABLE, /// processing class color table tag STATE_CLASS_COLOR_TABLE, /// processing name color table tag STATE_NAME_COLOR_TABLE, /// processing focus STATE_FOCUS, /// processing StudyMetaDataLinkSet tag STATE_STUDY_META_DATA_LINK_SET, /// processing SurfaceProjectedItem tag STATE_SURFACE_PROJECTED_ITEM }; /// file reading state STATE m_state; /// the state stack used when reading a file std::stack m_stateStack; /// the error message AString m_errorMessage; /// Foci file that is being read FociFile* m_fociFile; /// Focus that is being read Focus* m_focus; /// Counts projections as they are read int32_t m_projectionCounter; /// surface projected item that is being read SurfaceProjectedItem* m_surfaceProjectedItem; /// Reads a SurfaceProjectedItem SurfaceProjectedItemSaxReader* m_surfaceProjectedItemSaxReader; /// element text AString m_elementText; /// GIFTI meta data sax reader GiftiMetaDataSaxReader* m_metaDataSaxReader; /// GIFTI Label Table SAX Reader; GiftiLabelTableSaxReader* m_labelTableSaxReader; /** * Version 1 had only one color table that contained names and classes * After reading, split into name and class color tables */ GiftiLabelTable* m_versionOneColorTable; /// Study meta data link set reader StudyMetaDataLinkSetSaxReader* m_studyMetaDataLinkSetSaxReader; }; } // namespace #endif // __FOCI_FILE_SAX_READER_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Files/Focus.cxx000066400000000000000000000454461300200146000234260ustar00rootroot00000000000000 /*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 __FOCUS_DECLARE__ #include "Focus.h" #undef __FOCUS_DECLARE__ #include "CaretAssert.h" #include "CaretLogger.h" #include "StudyMetaDataLinkSet.h" #include "SurfaceProjectedItem.h" #include "XmlAttributes.h" #include "XmlWriter.h" using namespace caret; /** * \class caret::Focus * \brief A Focus. */ /** * Constructor. */ Focus::Focus() : CaretObjectTracksModification() { m_studyMetaDataLinkSet = new StudyMetaDataLinkSet(); clear(); } /** * Destructor. */ Focus::~Focus() { clear(); removeAllProjections(); delete m_studyMetaDataLinkSet; } /** * Copy constructor. * @param obj * Object that is copied. */ Focus::Focus(const Focus& obj) : CaretObjectTracksModification(obj) { m_studyMetaDataLinkSet = new StudyMetaDataLinkSet(); clear(); this->copyHelperFocus(obj); } /** * Assignment operator. * @param obj * Data copied from obj to this. * @return * Reference to this object. */ Focus& Focus::operator=(const Focus& obj) { if (this != &obj) { CaretObjectTracksModification::operator=(obj); this->copyHelperFocus(obj); } return *this; } /** * Clear the focus. */ void Focus::clear() { m_area = ""; m_className = ""; m_comment = ""; m_extent = 0.0; m_geography = ""; m_name = ""; m_regionOfInterest = ""; m_searchXYZ[0] = 0.0; m_searchXYZ[1] = 0.0; m_searchXYZ[2] = 0.0; m_statistic = ""; m_studyMetaDataLinkSet->clear(); m_sumsIdNumber = ""; m_sumsRepeatNumber = ""; m_sumsParentFocusBaseId = ""; m_sumsVersionNumber = ""; m_sumsMSLID = ""; m_attributeID = ""; m_groupNameSelectionItem = NULL; m_nameRgbaColor[0] = 0.0; m_nameRgbaColor[1] = 0.0; m_nameRgbaColor[2] = 0.0; m_nameRgbaColor[3] = 1.0; m_nameRgbaColorValid = false; m_classRgbaColor[0] = 0.0; m_classRgbaColor[1] = 0.0; m_classRgbaColor[2] = 0.0; m_classRgbaColor[3] = 1.0; m_classRgbaColorValid = false; setNameOrClassModified(); // new name/class so modified removeAllProjections(); addProjection(new SurfaceProjectedItem()); } /** * Helps with copying an object of this type. * @param obj * Object that is copied. */ void Focus::copyHelperFocus(const Focus& focus) { clear(); m_area = focus.m_area; m_className = focus.m_className; m_comment = focus.m_comment; m_extent = focus.m_extent; m_geography = focus.m_geography; m_name = focus.m_name; m_regionOfInterest = focus.m_regionOfInterest; m_searchXYZ[0] = focus.m_searchXYZ[0]; m_searchXYZ[1] = focus.m_searchXYZ[1]; m_searchXYZ[2] = focus.m_searchXYZ[2]; m_statistic = focus.m_statistic; delete m_studyMetaDataLinkSet; m_studyMetaDataLinkSet = new StudyMetaDataLinkSet(*focus.m_studyMetaDataLinkSet); m_sumsIdNumber = focus.m_sumsIdNumber; m_sumsRepeatNumber = focus.m_sumsRepeatNumber; m_sumsParentFocusBaseId = focus.m_sumsParentFocusBaseId; m_sumsVersionNumber = focus.m_sumsVersionNumber; m_sumsMSLID = focus.m_sumsMSLID; m_attributeID = focus.m_attributeID; this->removeAllProjections(); const int numProj = focus.getNumberOfProjections(); for (int32_t i = 0; i < numProj; i++) { SurfaceProjectedItem* spi = new SurfaceProjectedItem(*focus.getProjection(i)); this->addProjection(spi); } if (m_projections.empty()) { this->addProjection(new SurfaceProjectedItem()); } setNameOrClassModified(); // new name/class so modified } /** * @return Class name */ AString Focus::getClassName() const { return m_className; } /** * Set the class name * @param className */ void Focus::setClassName(const AString& className) { if (m_className != className) { m_className = className; setNameOrClassModified(); setModified(); } } /** * @return Area */ AString Focus::getArea() const { return m_area; } /** * Set the area * @param area */ void Focus::setArea(const AString& area) { if (m_area != area) { m_area = area; setModified(); } } /** * @return Comment */ AString Focus::getComment() const { return m_comment; } /** * Set the comment * @param */ void Focus::setComment(const AString& comment) { if (m_comment != comment) { m_comment = comment; setModified(); } } /** * @return Extent */ float Focus::getExtent() const { return m_extent; } /** * Set the extent * @param extent */ void Focus::setExtent(const float extent) { if (m_extent != extent) { m_extent = extent; setModified(); } } /** * @return Geography */ AString Focus::getGeography() const { return m_geography; } /** * Set the geography. * @param geography */ void Focus::setGeography(const AString& geography) { m_geography = geography; } /** * @return Name */ AString Focus::getName() const { return m_name; } /** * Set the name * @param name */ void Focus::setName(const AString& name) { if (m_name != name) { m_name = name; setNameOrClassModified(); setModified(); } } /** * @return Region of interest */ AString Focus::getRegionOfInterest() const { return m_regionOfInterest; } /** * Set the region of interest * @param regionOfInterest */ void Focus::setRegionOfInterest(const AString& regionOfInterest) { if (m_regionOfInterest != regionOfInterest) { m_regionOfInterest = regionOfInterest; setModified(); } } /** * @return Search coordinate */ const float* Focus::getSearchXYZ() const { /* * If not set, return stereotaxic coordinate */ if ((m_searchXYZ[0] == 0.0) && (m_searchXYZ[1] == 0.0) && (m_searchXYZ[2] == 0.0)) { const float* stereoXYZ = m_projections[0]->getStereotaxicXYZ(); return stereoXYZ; } return m_searchXYZ; } /** * Set the search XYZ * @param searchXYZ */ void Focus::setSearchXYZ(const float searchXYZ[3]) { if ((m_searchXYZ[0] != searchXYZ[0]) || (m_searchXYZ[1] != searchXYZ[1]) || (m_searchXYZ[2] != searchXYZ[2])) { m_searchXYZ[0] = searchXYZ[0]; m_searchXYZ[1] = searchXYZ[1]; m_searchXYZ[2] = searchXYZ[2]; setModified(); } } /** * @return statistic */ AString Focus::getStatistic() const { return m_statistic; } /** * Set the statistic * @param statistic */ void Focus::setStatistic(const AString& statistic) { if (m_statistic != statistic) { m_statistic = statistic; setModified(); } } /** * @return Is the class RGBA color valid? */ bool Focus::isClassRgbaValid() const { return m_classRgbaColorValid; } /** * Set then class RGBA color invalid. */ void Focus::setClassRgbaInvalid() { m_classRgbaColorValid = false; } /** * @return The class RGBA color components * ranging zero to one. */ const float* Focus::getClassRgba() const { return m_classRgbaColor; } /** * Get the class RGBA color components * ranging zero to one. */ void Focus::getClassRgba(float rgba[4]) const { rgba[0] = m_classRgbaColor[0]; rgba[1] = m_classRgbaColor[1]; rgba[2] = m_classRgbaColor[2]; rgba[3] = m_classRgbaColor[3]; } /** * Set the RGBA color components assigned to the class. * @param rgba * Red, green, blue, alpha ranging zero to one. */ void Focus::setClassRgba(const float rgba[3]) { m_classRgbaColor[0] = rgba[0]; m_classRgbaColor[1] = rgba[1]; m_classRgbaColor[2] = rgba[2]; m_classRgbaColor[3] = rgba[3]; m_classRgbaColorValid = true; } /** * @return Is the name RGBA color valid? */ bool Focus::isNameRgbaValid() const { return m_nameRgbaColorValid; } /** * Set then name RGBA color invalid. */ void Focus::setNameRgbaInvalid() { m_nameRgbaColorValid = false; } /** * @return The name RGBA color components * ranging zero to one. */ const float* Focus::getNameRgba() const { return m_nameRgbaColor; } /** * Get the name RGBA color components * ranging zero to one. */ void Focus::getNameRgba(float rgba[4]) const { rgba[0] = m_nameRgbaColor[0]; rgba[1] = m_nameRgbaColor[1]; rgba[2] = m_nameRgbaColor[2]; rgba[3] = m_nameRgbaColor[3]; } /** * Set the RGBA color components assigned to the name. * @param rgba * Red, green, blue, alpha ranging zero to one. */ void Focus::setNameRgba(const float rgba[4]) { m_nameRgbaColor[0] = rgba[0]; m_nameRgbaColor[1] = rgba[1]; m_nameRgbaColor[2] = rgba[2]; m_nameRgbaColor[3] = rgba[3]; m_nameRgbaColorValid = true; } /** * Set the selection item for the group/name hierarchy. * * @param item * The selection item from the group/name hierarchy. */ void Focus::setGroupNameSelectionItem(GroupAndNameHierarchyItem* item) { m_groupNameSelectionItem = item; } /** * @return The selection item for the Group/Name selection hierarchy. * May be NULL in some circumstances. */ const GroupAndNameHierarchyItem* Focus::getGroupNameSelectionItem() const { return m_groupNameSelectionItem; } /** * @return Sums ID Number */ AString Focus::getSumsIdNumber() const { return m_sumsIdNumber; } /** * Set the Sums ID Number * @param sumsIdNumber */ void Focus::setSumsIdNumber(const AString& sumsIdNumber) { if (m_sumsIdNumber != sumsIdNumber) { m_sumsIdNumber = sumsIdNumber; setModified(); } } /** * @return Sums Repeat number */ AString Focus::getSumsRepeatNumber() const { return m_sumsRepeatNumber; } /** * Set the Sums Repeat Number * @param sumsRepeatNumber */ void Focus::setSumsRepeatNumber(const AString& sumsRepeatNumber) { if (m_sumsRepeatNumber != sumsRepeatNumber) { m_sumsRepeatNumber = sumsRepeatNumber; setModified(); } } /** * @return Sums parent focus base id */ AString Focus::getSumsParentFocusBaseId() const { return m_sumsParentFocusBaseId; } /** * Set the Sums Parent Focus Base ID * @param sumsParentFocusBaseId */ void Focus::setSumsParentFocusBaseId(const AString& sumsParentFocusBaseId) { if (m_sumsParentFocusBaseId != sumsParentFocusBaseId) { m_sumsParentFocusBaseId = sumsParentFocusBaseId; setModified(); } } /** * @return Sums version number */ AString Focus::getSumsVersionNumber() const { return m_sumsVersionNumber; } /** * Set the Sums version number * @param sumsVersionNumber */ void Focus::setSumsVersionNumber(const AString& sumsVersionNumber) { if (m_sumsVersionNumber != sumsVersionNumber) { m_sumsVersionNumber = sumsVersionNumber; setModified(); } } /** * @return Sums MSLID */ AString Focus::getSumsMSLID() const { return m_sumsMSLID; } /** * Set the Sums MSLID * @param sumsMSLID */ void Focus::setSumsMSLID(const AString& sumsMSLID) { if (m_sumsMSLID != sumsMSLID) { m_sumsMSLID = sumsMSLID; setModified(); } } /** * @return Sums attribute ID */ AString Focus::getSumsAttributeID() const { return m_attributeID; } /** * Set the Atribute ID * @param attributeID */ void Focus::setSumsAttributeID(const AString& attributeID) { if (m_attributeID != attributeID) { m_attributeID = attributeID; setModified(); } } /** * @return Number of projections */ int32_t Focus::getNumberOfProjections() const { return m_projections.size(); } /** * Get the projection at the given index. * Note: Index 0 will ALWAYS return a valid projection. * * @param indx * Index of projection * @return * Projection at given index. */ const SurfaceProjectedItem* Focus::getProjection(const int32_t indx) const { CaretAssertVectorIndex(m_projections, indx); return m_projections[indx]; } /** * Get the projection at the given index. * Note: Index 0 will ALWAYS return a valid projection. * * @param indx * Index of projection * @return * Projection at given index. */ SurfaceProjectedItem* Focus::getProjection(const int32_t indx) { CaretAssertVectorIndex(m_projections, indx); return m_projections[indx]; } /** * Add the projection. Note: the focus * takes ownership of the projection and will * delete it. After calling this method DO NOT * ever use the projection passed to this method. * * @param projection * Projection that is added. */ void Focus::addProjection(SurfaceProjectedItem* projection) { CaretAssert(projection); m_projections.push_back(projection); } /** * Remove all of the projections. A focus always * has one projection but this method removes all * projections so caller will need to to add a * projection. */ void Focus::removeAllProjections() { const int32_t numProj = getNumberOfProjections(); for (int32_t i = 0; i < numProj; i++) { delete m_projections[i]; } m_projections.clear(); } /** * Remove all but the first projection. */ void Focus::removeExtraProjections() { const int32_t numProj = getNumberOfProjections(); for (int32_t i = 1; i < numProj; i++) { delete m_projections[i]; } m_projections.resize(1); } /** * @return The study meta data link set. */ StudyMetaDataLinkSet* Focus::getStudyMetaDataLinkSet() { return m_studyMetaDataLinkSet; } /** * @return The study meta data link set. */ const StudyMetaDataLinkSet* Focus::getStudyMetaDataLinkSet() const { return m_studyMetaDataLinkSet; } /** * Write the focus to XML. * @param xmlWriter * Writer to which focus is written. * @param focusIndex * Index of the focus. */ void Focus::writeAsXML(XmlWriter& xmlWriter, const int32_t focusIndex) { XmlAttributes atts; atts.addAttribute(XML_ATTRIBUTE_FOCUS_INDEX, focusIndex); xmlWriter.writeStartElement(XML_TAG_FOCUS, atts); xmlWriter.writeElementCData(XML_TAG_AREA, m_area); xmlWriter.writeElementCData(XML_TAG_CLASS_NAME, m_className); xmlWriter.writeElementCData(XML_TAG_COMMENT, m_comment); xmlWriter.writeElementCharacters(XML_TAG_EXTENT, m_extent); xmlWriter.writeElementCData(XML_TAG_GEOGRAPHY, m_geography); xmlWriter.writeElementCData(XML_TAG_NAME, m_name); xmlWriter.writeElementCData(XML_TAG_REGION_OF_INTEREST, m_regionOfInterest); xmlWriter.writeElementCharacters(XML_TAG_SEARCH_XYZ, m_searchXYZ, 3); xmlWriter.writeElementCData(XML_TAG_STATISTIC, m_statistic); xmlWriter.writeElementCData(XML_TAG_SUMS_ID_NUMBER, m_sumsIdNumber); xmlWriter.writeElementCData(XML_TAG_SUMS_REPEAT_NUMBER, m_sumsRepeatNumber); xmlWriter.writeElementCData(XML_TAG_SUMS_PARENT_FOCUS_BASE_ID, m_sumsParentFocusBaseId); xmlWriter.writeElementCData(XML_TAG_SUMS_VERSION_NUMBER, m_sumsVersionNumber); xmlWriter.writeElementCData(XML_TAG_SUMS_MSLID, m_sumsMSLID); xmlWriter.writeElementCData(XML_TAG_SUMS_ATTRIBUTE_ID, m_attributeID); m_studyMetaDataLinkSet->writeXML(xmlWriter); const int32_t numProj = getNumberOfProjections(); for (int32_t i = 0; i < numProj; i++) { m_projections[i]->writeAsXML(xmlWriter); } xmlWriter.writeEndElement(); } /** * Set modification status of name/class to modified. * * Name/Class modification status is used * by the selection controls that display * borders based upon selected classes and * names. */ void Focus::setNameOrClassModified() { m_groupNameSelectionItem = NULL; m_nameRgbaColorValid = false; m_classRgbaColorValid = false; } /** * Set an element from its XML tag name * @param elementName * Name of XML element. * @param textValue * Value of element's text. * @return * True if element was valid, else false. */ bool Focus::setElementFromText(const AString& elementName, const AString& textValue) { if (elementName == Focus::XML_TAG_AREA) { m_area = textValue; } else if (elementName == Focus::XML_TAG_CLASS_NAME) { m_className = textValue; } else if (elementName == "Color") { // obsolete element } else if (elementName == Focus::XML_TAG_COMMENT) { m_comment = textValue; } else if (elementName == Focus::XML_TAG_EXTENT) { m_extent = textValue.toFloat(); } else if (elementName == Focus::XML_TAG_GEOGRAPHY) { m_geography = textValue; } else if (elementName == Focus::XML_TAG_NAME) { m_name = textValue; } else if (elementName == Focus::XML_TAG_REGION_OF_INTEREST) { m_regionOfInterest = textValue; } else if (elementName == Focus::XML_TAG_SEARCH_XYZ) { std::vector xyz; AString::toNumbers(textValue, xyz); if (xyz.size() == 3) { m_searchXYZ[0] = xyz[0]; m_searchXYZ[1] = xyz[1]; m_searchXYZ[2] = xyz[2]; } } else if (elementName == Focus::XML_TAG_STATISTIC) { m_statistic = textValue; } else if (elementName == Focus::XML_TAG_SUMS_ID_NUMBER) { m_sumsIdNumber = textValue; } else if (elementName == Focus::XML_TAG_SUMS_REPEAT_NUMBER) { m_sumsRepeatNumber = textValue; } else if (elementName == Focus::XML_TAG_SUMS_PARENT_FOCUS_BASE_ID) { m_sumsParentFocusBaseId = textValue; } else if (elementName == Focus::XML_TAG_SUMS_VERSION_NUMBER) { m_sumsVersionNumber = textValue; } else if (elementName == Focus::XML_TAG_SUMS_MSLID) { m_sumsMSLID = textValue; } else if (elementName == Focus::XML_TAG_SUMS_ATTRIBUTE_ID) { m_attributeID = textValue; } else { return false; } return true; } /** * Clear the modification status of the focus. */ void Focus::clearModified() { CaretObjectTracksModification::clearModified(); const int numProj = getNumberOfProjections(); for (int32_t i = 0; i < numProj; i++) { SurfaceProjectedItem* spi = getProjection(i); spi->clearModified(); } } /** * @return The modification status. */ bool Focus::isModified() const { if (CaretObjectTracksModification::isModified()) { return true; } const int numProj = getNumberOfProjections(); for (int32_t i = 0; i < numProj; i++) { const SurfaceProjectedItem* spi = getProjection(i); if (spi->isModified()) { return true; } } return false; } connectome-workbench-1.2.3+git41-gc4c6c90/src/Files/Focus.h000066400000000000000000000177771300200146000230610ustar00rootroot00000000000000#ifndef __FOCUS__H_ #define __FOCUS__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 "CaretObjectTracksModification.h" #include "SurfaceProjectedItem.h" namespace caret { class FociFileSaxReader; class GroupAndNameHierarchyItem; class StudyMetaDataLinkSet; class Focus : public CaretObjectTracksModification { public: Focus(); virtual ~Focus(); Focus(const Focus& obj); Focus& operator=(const Focus& obj); void clear(); AString getClassName() const; void setClassName(const AString& name); AString getArea() const; void setArea(const AString& area); AString getComment() const; void setComment(const AString& area); float getExtent() const; void setExtent(const float extent); AString getGeography() const; void setGeography(const AString& geography); AString getName() const; void setName(const AString& name); AString getRegionOfInterest() const; void setRegionOfInterest(const AString& regionOfInterest); const float* getSearchXYZ() const; void setSearchXYZ(const float searchXYZ[3]); AString getStatistic() const; void setStatistic(const AString& statistic); bool isClassRgbaValid() const; void setClassRgbaInvalid(); const float* getClassRgba() const; void getClassRgba(float rgba[4]) const; void setClassRgba(const float rgba[4]); bool isNameRgbaValid() const; void setNameRgbaInvalid(); const float* getNameRgba() const; void getNameRgba(float rgba[4]) const; void setNameRgba(const float rgba[4]); AString getSumsIdNumber() const; void setSumsIdNumber(const AString& sumsIdNumber); AString getSumsRepeatNumber() const; void setSumsRepeatNumber(const AString& sumsRepeatNumber); AString getSumsParentFocusBaseId() const; void setSumsParentFocusBaseId(const AString& sumsParentFocusBaseId); AString getSumsVersionNumber() const; void setSumsVersionNumber(const AString& sumsVersionNumber); AString getSumsMSLID() const; void setSumsMSLID(const AString& sumsMSLID); AString getSumsAttributeID() const; void setSumsAttributeID(const AString& attributeID); int32_t getNumberOfProjections() const; const SurfaceProjectedItem* getProjection(const int32_t indx) const; SurfaceProjectedItem* getProjection(const int32_t indx); void addProjection(SurfaceProjectedItem* projection); void removeExtraProjections(); StudyMetaDataLinkSet* getStudyMetaDataLinkSet(); const StudyMetaDataLinkSet* getStudyMetaDataLinkSet() const; void setGroupNameSelectionItem(GroupAndNameHierarchyItem* item); const GroupAndNameHierarchyItem* getGroupNameSelectionItem() const; void writeAsXML(XmlWriter& xmlWriter, const int32_t focusIndex); bool setElementFromText(const AString& elementName, const AString& textValue); virtual void clearModified(); virtual bool isModified() const; static const AString XML_ATTRIBUTE_FOCUS_INDEX; static const AString XML_TAG_FOCUS; static const AString XML_TAG_AREA; static const AString XML_TAG_CLASS_NAME; static const AString XML_TAG_COMMENT; static const AString XML_TAG_EXTENT; static const AString XML_TAG_GEOGRAPHY; static const AString XML_TAG_NAME; static const AString XML_TAG_REGION_OF_INTEREST; static const AString XML_TAG_SEARCH_XYZ; static const AString XML_TAG_STATISTIC; static const AString XML_TAG_SUMS_ID_NUMBER; static const AString XML_TAG_SUMS_REPEAT_NUMBER; static const AString XML_TAG_SUMS_PARENT_FOCUS_BASE_ID; static const AString XML_TAG_SUMS_VERSION_NUMBER; static const AString XML_TAG_SUMS_MSLID; static const AString XML_TAG_SUMS_ATTRIBUTE_ID; private: void copyHelperFocus(const Focus& obj); void setNameOrClassModified(); void removeAllProjections(); AString m_area; AString m_className; AString m_comment; float m_extent; AString m_geography; AString m_name; AString m_regionOfInterest; float m_searchXYZ[3]; AString m_statistic; StudyMetaDataLinkSet* m_studyMetaDataLinkSet; AString m_sumsIdNumber; AString m_sumsRepeatNumber; AString m_sumsParentFocusBaseId; AString m_sumsVersionNumber; AString m_sumsMSLID; AString m_attributeID; /** RGBA color components assigned to focus' name */ float m_nameRgbaColor[4]; /** RGBA color components assigned to focus' name validity */ bool m_nameRgbaColorValid; /** RGBA color component assigned to focus' class name */ float m_classRgbaColor[4]; /** RGBA color components assigned to focus' name validity */ bool m_classRgbaColorValid; /** May project to more than one surface */ std::vector m_projections; /** Selection status of this border in the group/name hierarchy */ GroupAndNameHierarchyItem* m_groupNameSelectionItem; /** Allow foci file SAX reader to remove all projections */ friend class FociFileSaxReader; }; #ifdef __FOCUS_DECLARE__ const AString Focus::XML_ATTRIBUTE_FOCUS_INDEX = "Index"; const AString Focus::XML_TAG_FOCUS = "Focus"; const AString Focus::XML_TAG_AREA = "Area"; const AString Focus::XML_TAG_CLASS_NAME = "ClassName"; const AString Focus::XML_TAG_COMMENT = "Comment"; const AString Focus::XML_TAG_EXTENT = "Extent"; const AString Focus::XML_TAG_GEOGRAPHY = "Geography"; const AString Focus::XML_TAG_NAME = "Name"; const AString Focus::XML_TAG_REGION_OF_INTEREST = "RegionOfInterest"; const AString Focus::XML_TAG_SEARCH_XYZ = "SearchXYZ"; const AString Focus::XML_TAG_STATISTIC = "Statistic"; const AString Focus::XML_TAG_SUMS_ID_NUMBER = "SumsIDNumber"; const AString Focus::XML_TAG_SUMS_REPEAT_NUMBER = "SumsRepeatNumber"; const AString Focus::XML_TAG_SUMS_PARENT_FOCUS_BASE_ID = "SumsParentFocusBaseID"; const AString Focus::XML_TAG_SUMS_VERSION_NUMBER = "SumsVersionNumber"; const AString Focus::XML_TAG_SUMS_MSLID = "SumsMSLID"; const AString Focus::XML_TAG_SUMS_ATTRIBUTE_ID = "AttributeID"; #endif // __FOCUS_DECLARE__ } // namespace #endif //__FOCUS__H_ connectome-workbench-1.2.3+git41-gc4c6c90/src/Files/GeodesicHelper.cxx000066400000000000000000002046641300200146000252300ustar00rootroot00000000000000/*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 "GeodesicHelper.h" #include "CaretAssert.h" #include "CaretHeap.h" #include "CaretMutex.h" #include "FastStatistics.h" #include "SurfaceFile.h" #include "TopologyHelper.h" #include #include #include using namespace caret; using namespace std; GeodesicHelperBase::GeodesicHelperBase(const SurfaceFile* surfaceIn, const float* correctedAreas) { CaretPointer topoBase(new TopologyHelperBase(surfaceIn)); TopologyHelper topoHelpIn(topoBase);//leave this building one privately, to not introduce even worse dependencies regarding SurfaceFile m_corrAreaSmallestFactor = 1.0f; numNodes = surfaceIn->getNumberOfNodes(); nodeNeighbors.resize(numNodes); distances.resize(numNodes); nodeCoords.resize(numNodes); vector sqrtCorrAreas;//each edge has 2 vertices that influence it - assume that each influences a piece of the edge with a ratio depending on the square roots of the vertex areas vector sqrtVertAreas;//we also assume isometric expansion at each vertex if (correctedAreas != NULL)//this gives an estimated original length of curLength * (sqrt(origA) + sqrt(origB))/(sqrt(curA) + sqrt(curB)) { surfaceIn->computeNodeAreas(sqrtVertAreas); sqrtCorrAreas.resize(numNodes); for (int i = 0; i < numNodes; ++i) { sqrtCorrAreas[i] = sqrt(correctedAreas[i]); sqrtVertAreas[i] = sqrt(sqrtVertAreas[i]); } } Vector3D tempvec; float tempf, abmag, efmag, cdmag; double nodeSpacingAccum = 0.0f;//since we may be using corrected areas, find average node spacing manually int32_t numEdges = 0; bool firstCorrArea = true;//if all corrected vertex areas are significantly larger than 1, we can make A* faster by multiplying all euclidean distances by it, so find the actual smallest for (int32_t i = 0; i < numNodes; ++i) {//get neighbors vector& neighbors = nodeNeighbors[i]; neighbors = topoHelpIn.getNodeNeighbors(i); nodeCoords[i] = surfaceIn->getCoordinate(i); const Vector3D baseCoord = nodeCoords[i]; int numNeigh = (int)neighbors.size(); distances[i].resize(numNeigh); for (int32_t j = 0; j < numNeigh; ++j) { Vector3D neighCoord = surfaceIn->getCoordinate(neighbors[j]); tempvec = baseCoord - neighCoord; distances[i][j] = tempvec.length();//precompute for speed in other calls if (correctedAreas != NULL) { float correctionFactor = (sqrtCorrAreas[i] + sqrtCorrAreas[neighbors[j]]) / (sqrtVertAreas[i] + sqrtVertAreas[neighbors[j]]); if (firstCorrArea || correctionFactor < m_corrAreaSmallestFactor) { m_corrAreaSmallestFactor = correctionFactor;//if this is zero anywhere, it just means that the euclidean part of the heuristic must be ignored (worst case, it does dijkstra) firstCorrArea = false; } distances[i][j] *= correctionFactor; } if (i < neighbors[j]) { nodeSpacingAccum += distances[i][j]; ++numEdges; } }//so few floating point operations, this should turn out symmetric } m_avgNodeSpacing = nodeSpacingAccum / numEdges; std::vector tempneigh2; std::vector tempdist2; nodeNeighbors2.resize(numNodes); distances2.resize(numNodes); neighbors2PathInfo.resize(numNodes); const vector& myEdgeInfo = topoHelpIn.getEdgeInfo(); CaretAssert(numEdges == (int32_t)myEdgeInfo.size());//SurfaceFile checks for triangles with duplicated nodes for (int i = 0; i < numEdges; ++i) { if (myEdgeInfo[i].numTiles < 2) { continue;//skip edges that have only one triangle } int32_t neigh1Node, neigh2Node, baseNode, farNode; neigh1Node = myEdgeInfo[i].node1; neigh2Node = myEdgeInfo[i].node2; baseNode = myEdgeInfo[i].tiles[0].node3; farNode = myEdgeInfo[i].tiles[1].node3; Vector3D neigh1Coord = nodeCoords[neigh1Node]; Vector3D neigh2Coord = nodeCoords[neigh2Node]; Vector3D baseCoord = nodeCoords[baseNode]; Vector3D farCoord = nodeCoords[farNode]; CrawlInfo tempInfo; tempInfo.edgeNodes[0] = neigh1Node; tempInfo.edgeNodes[1] = neigh2Node; const int32_t num_reserve = 8;//uses 8 in case it is used on a mesh with haphazard topology nodeNeighbors2[baseNode].reserve(num_reserve);//reserve should be fast if capacity is already num_reserve, and better than reallocating at 2 and 4, if vector allocation is naive doubling nodeNeighbors2[farNode].reserve(num_reserve);//in the extremely rare case of a node with more than num_reserve neighbors, a second allocation plus copy isn't much of a cost distances2[baseNode].reserve(num_reserve); distances2[farNode].reserve(num_reserve); neighbors2PathInfo[baseNode].reserve(num_reserve); neighbors2PathInfo[farNode].reserve(num_reserve); Vector3D abhat = (neigh2Coord - neigh1Coord).normal(&abmag);//a is neigh1, b is neigh2, b - a = (vector)ab Vector3D ac = farCoord - neigh1Coord;//c is farnode, c - a = (vector)ac Vector3D ad = abhat * abhat.dot(ac);//d is the point on the shared edge that farnode (c) is closest to Vector3D d = neigh1Coord + ad;//this way we can "unfold" the triangles by projecting the distance of cd, from point d, along the unit vector of the base node to closest point on shared edge Vector3D ea = neigh1Coord - baseCoord;//e is the base node, a - e = (vector)ea tempvec = abhat * abhat.dot(ea);//find vector fa, f being the point on shared edge closest to e, the base node Vector3D efhat = (ea - tempvec).normal(&efmag);//and subtract it to obtain only the perpendicular, normalize to get unit vector cdmag = (d - farCoord).length();//get the length from shared edge to far point Vector3D g = d + efhat * cdmag;//get point g, the unfolded position of farnode Vector3D eg = g - baseCoord;//this is the vector from base (e) to far node after unfolding (g), this is our distance, as long as the tetralateral is convex tempf = efmag / (efmag + cdmag);//now we need to check that the path stays inside the tetralateral (ie, that it is convex) Vector3D eh = eg * tempf;//this is a vector from e (base node) to the point on the shared edge that the full path (eg) crosses Vector3D ah = eh - ea;//eh - ea = eh + ae = ae + eh = ah, vector from neigh1 to the point on shared edge the path goes through tempf = ah.dot(abhat);//get the component along ab so we can test that it is positive and less than |ab| if (tempf <= 0.0f || tempf >= abmag) continue;//tetralateral is concave or triangular (degenerate), our path is invalid or not shorter, so consider next edge tempInfo.edgeWeight = 1.0f - tempf / abmag;//if tempf is almost zero, then the weight of point a (neigh1) is almost 1 tempf = eg.length();//this is our path length tempInfo.pieceDists[1] = eh.length();//we currently add things to farNode's neighbor info before baseNode's if (correctedAreas != NULL)//apply area correction approximation { float correctionFactor = (sqrtCorrAreas[baseNode] + sqrtCorrAreas[farNode]) / (sqrtVertAreas[baseNode] + sqrtVertAreas[farNode]); if (correctionFactor < m_corrAreaSmallestFactor) { m_corrAreaSmallestFactor = correctionFactor;//if this is zero anywhere, it just means that the euclidean part of the heuristic must be ignored (worst case, it does dijkstra) } tempf *= correctionFactor; tempInfo.pieceDists[1] *= correctionFactor; }//for now, assume it only depends on the expansion of the endpoints, and affects each part equally tempInfo.pieceDists[0] = tempf - tempInfo.pieceDists[1]; nodeNeighbors2[farNode].push_back(baseNode);//record it at both ends, because we are looping through edges distances2[farNode].push_back(tempf); neighbors2PathInfo[farNode].push_back(tempInfo); float tempf2 = tempInfo.pieceDists[0];//swap the piece distances around for the baseNode info tempInfo.pieceDists[0] = tempInfo.pieceDists[1]; tempInfo.pieceDists[1] = tempf2; nodeNeighbors2[baseNode].push_back(farNode); distances2[baseNode].push_back(tempf); neighbors2PathInfo[baseNode].push_back(tempInfo); } } GeodesicHelper::GeodesicHelper(const CaretPointer& baseIn) { m_myBase = baseIn;//copy the pointer so it doesn't get changed or deleted while we get its members //get references and info from base numNodes = m_myBase->numNodes; m_avgNodeSpacing = m_myBase->m_avgNodeSpacing; m_corrAreaSmallestFactor = m_myBase->m_corrAreaSmallestFactor; distances = m_myBase->distances.data(); distances2 = m_myBase->distances2.data(); nodeNeighbors = m_myBase->nodeNeighbors.data(); nodeNeighbors2 = m_myBase->nodeNeighbors2.data(); nodeCoords = m_myBase->nodeCoords.data(); neighbors2PathInfo = m_myBase->neighbors2PathInfo.data(); //allocate private scratch space marked.resize(numNodes, 0);//initialize once, each internal function (dijkstra methods) tracks elements changed, and resets only those (except in the case of whole surface) m_heapIdent.resize(numNodes);//the idea is to make it faster for the more likely case of small areas of the surface for functions that have limits, by removing the runtime term based solely on surface size outputStore.resize(numNodes); output = outputStore.data();//because we have a function that does a pointer swap to compute distances directly in the output array changed.resize(numNodes); parentStore.resize(numNodes); parent = parentStore.data();//ditto for parents heurVal.resize(numNodes); } void GeodesicHelper::getNodesToGeoDist(const int32_t node, const float maxdist, std::vector& nodesOut, std::vector& distsOut, const bool smoothflag) {//public methods sanity check, private methods process nodesOut.clear(); distsOut.clear(); CaretAssert(node < numNodes && node >= 0); if (node >= numNodes || maxdist < 0.0f || node < 0) return;//check what we asserted so release doesn't do strange things CaretMutexLocker locked(&inUse);//let sanity checks go multithreaded, as if it mattered dijkstra(node, maxdist, nodesOut, distsOut, smoothflag); } void GeodesicHelper::getNodesToGeoDist(const int32_t node, const float maxdist, std::vector& nodesOut, std::vector& distsOut, std::vector& parentsOut, const bool smoothflag) {//public methods sanity check, private methods process nodesOut.clear(); distsOut.clear(); CaretAssert(node < numNodes && node >= 0); if (node >= numNodes || maxdist < 0.0f || node < 0) return; CaretMutexLocker locked(&inUse);//we need the parents array to stay put, so don't scope this dijkstra(node, maxdist, nodesOut, distsOut, smoothflag); int32_t mysize = (int32_t)nodesOut.size(); parentsOut.resize(mysize); for (int32_t i = 0; i < mysize; ++i) { parentsOut[i] = parent[nodesOut[i]]; } } void GeodesicHelper::dijkstra(const int32_t root, const float maxdist, std::vector& nodes, std::vector& dists, bool smooth) { int32_t i, j, whichnode, whichneigh, numNeigh, numChanged = 0; const int32_t* neighbors; float tempf; output[root] = 0.0f; marked[root] |= 4; parent[root] = -1;//idiom for end of path changed[numChanged++] = root; m_active.clear(); m_heapIdent[root] = m_active.push(root, 0.0f); //we keep values greater than maxdist off the heap, so anything pulled from the heap which is unmarked belongs in the list while (!m_active.isEmpty()) { whichnode = m_active.pop(); nodes.push_back(whichnode); dists.push_back(output[whichnode]); marked[whichnode] |= 1;//anything pulled from heap will already be marked as having a valid value (flag 4) neighbors = nodeNeighbors[whichnode].data(); numNeigh = (int32_t)nodeNeighbors[whichnode].size(); for (j = 0; j < numNeigh; ++j) { whichneigh = neighbors[j]; if (!(marked[whichneigh] & 1)) {//skip floating point math if frozen tempf = output[whichnode] + distances[whichnode][j];//isn't precomputation wonderful if (tempf <= maxdist) {//keep it off the heap if it is too far if (!(marked[whichneigh] & 4)) { marked[whichneigh] |= 4; changed[numChanged++] = whichneigh; output[whichneigh] = tempf; parent[whichneigh] = whichnode; m_heapIdent[whichneigh] = m_active.push(whichneigh, tempf); } else if (tempf < output[whichneigh]) { output[whichneigh] = tempf; parent[whichneigh] = whichnode; m_active.changekey(m_heapIdent[whichneigh], tempf); } } } } if (smooth)//repeat with numNeighbors2, nodeNeighbors2, distance2 { neighbors = nodeNeighbors2[whichnode].data(); numNeigh = (int32_t)nodeNeighbors2[whichnode].size(); for (j = 0; j < numNeigh; ++j) { whichneigh = neighbors[j]; if (!(marked[whichneigh] & 1)) {//skip floating point math if frozen tempf = output[whichnode] + distances2[whichnode][j]; if (tempf <= maxdist) {//keep it off the heap if it is too far if (!(marked[whichneigh] & 4)) { marked[whichneigh] |= 4; changed[numChanged++] = whichneigh; output[whichneigh] = tempf; parent[whichneigh] = whichnode; m_heapIdent[whichneigh] = m_active.push(whichneigh, tempf); } else if (tempf < output[whichneigh]) { output[whichneigh] = tempf; parent[whichneigh] = whichnode; m_active.changekey(m_heapIdent[whichneigh], tempf); } } } } } } for (i = 0; i < numChanged; ++i) { marked[changed[i]] = 0;//minimize reinitialization of arrays } } void GeodesicHelper::dijkstra(const int32_t root, bool smooth) {//straightforward dijkstra, no cutoffs, full surface int32_t i, j, whichnode, whichneigh, numNeigh; const int32_t* neighbors; float tempf; output[root] = 0.0f; parent[root] = -1;//idiom for end of path m_active.clear(); m_heapIdent[root] = m_active.push(root, 0.0f); while (!m_active.isEmpty()) { whichnode = m_active.pop(); marked[whichnode] |= 1; neighbors = nodeNeighbors[whichnode].data(); numNeigh = (int32_t)nodeNeighbors[whichnode].size(); for (j = 0; j < numNeigh; ++j) { whichneigh = neighbors[j]; if (!(marked[whichneigh] & 1)) {//skip floating point math if frozen tempf = output[whichnode] + distances[whichnode][j]; if (!(marked[whichneigh] & 4)) { marked[whichneigh] |= 4; output[whichneigh] = tempf; parent[whichneigh] = whichnode; m_heapIdent[whichneigh] = m_active.push(whichneigh, tempf); } else if (tempf < output[whichneigh]) { output[whichneigh] = tempf; parent[whichneigh] = whichnode; m_active.changekey(m_heapIdent[whichneigh], tempf); } } } if (smooth) { neighbors = nodeNeighbors2[whichnode].data(); numNeigh = (int32_t)nodeNeighbors2[whichnode].size(); for (j = 0; j < numNeigh; ++j) { whichneigh = neighbors[j]; if (!(marked[whichneigh] & 1)) {//skip floating point math if frozen tempf = output[whichnode] + distances2[whichnode][j]; if (!(marked[whichneigh] & 4)) { marked[whichneigh] |= 4; output[whichneigh] = tempf; parent[whichneigh] = whichnode; m_heapIdent[whichneigh] = m_active.push(whichneigh, tempf); } else if (tempf < output[whichneigh]) { output[whichneigh] = tempf; parent[whichneigh] = whichnode; m_active.changekey(m_heapIdent[whichneigh], tempf); } } } } } for (i = 0; i < numNodes; ++i) { marked[i] = 0; } } float** GeodesicHelper::getGeoAllToAll(const bool smooth) { float bytes = (float)(((long long)numNodes) * numNodes * (sizeof(float) + sizeof(int32_t)) + numNodes * (sizeof(float*) + sizeof(int32_t*))); short index = 0; static const char *labels[9] = {" Bytes", " Kilobytes", " Megabytes", " Gigabytes", " Terabytes", " Petabytes", " Exabytes", " Zettabytes", " Yottabytes"}; while (index < 8 && bytes > 1000.0f) { ++index; bytes = bytes / 1000.0f;//using 1024 would make it Kibibytes, etc } CaretMutexLocker locked(&inUse);//don't sit there with memory allocated but locked out of computation, lock early - also before status messages std::cout << "attempting to allocate " << AString::number(bytes, 'f', 2) << labels[index] << "..."; std::cout.flush(); int32_t i = -1, j; bool fail = false; float** ret = NULL; int32_t** parents = NULL; try { ret = new float*[numNodes]; if (ret != NULL) { for (i = 0; i < numNodes; ++i) { ret[i] = new float[numNodes]; if (ret[i] == NULL) { fail = true; break;//have to break so it doesn't increment i } } } } catch (std::bad_alloc e) {//should catch if new likes to throw exceptions instead of returning null fail = true; } if (ret == NULL) { std::cout << "failed" << std::endl; return NULL; } if (fail) { std::cout << "failed" << std::endl; for (j = 0; j < i; ++j) delete[] ret[j]; if (i > -1) delete[] ret; return NULL; } i = -1; try { parents = new int32_t*[numNodes]; if (parents != NULL) { for (i = 0; i < numNodes; ++i) { parents[i] = new int32_t[numNodes]; if (parents[i] == NULL) { fail = true; break;//have to break so it doesn't increment i } } } } catch (std::bad_alloc e) {//should catch if new likes to throw exceptions instead of returning null fail = true; } if (parents == NULL) { std::cout << "failed" << std::endl; for (i = 0; i < numNodes; ++i) delete[] ret[i]; delete[] ret; return NULL; } if (fail) { std::cout << "failed" << std::endl; for (j = 0; j < i; ++j) delete[] parents[j]; if (i > -1) delete[] parents; for (i = 0; i < numNodes; ++i) delete[] ret[i]; delete[] ret; return NULL; } std::cout << "success" << std::endl; alltoall(ret, parents, smooth); for (i = 0; i < numNodes; ++i) delete[] parents[i]; delete[] parents; return ret; } void GeodesicHelper::alltoall(float** out, int32_t** parents, bool smooth) {//propagates info about shortest paths not containing root to other roots, hopefully making the problem tractable int32_t root, i, j, whichnode, whichneigh, numNeigh, remain, midpoint, midrevparent, endparent, prevdots = 0, dots; const int32_t* neighbors; float tempf, tempf2; for (i = 0; i < numNodes; ++i) { for (j = 0; j < numNodes; ++j) { out[i][j] = -1.0f; } } remain = numNodes; std::cout << "|0% calculating geodesic distances 100%|" << std::endl; // .................................................. for (root = 0; root < numNodes; ++root) { dots = (50 * root) / numNodes;//simple progress indicator while (prevdots < dots) { std::cout << '.'; std::cout.flush(); ++prevdots; } if (root != 0) { remain = 0; for (i = 0; i < numNodes; ++i)//find known values { if (out[root][i] > 0.0f) { marked[i] = 2;//mark that we already have a value, skip calculation, but not yet added to active list } else { marked[i] = 0; ++remain;//count how many more we need to compute so we can stop early } } }//marking done, dijkstra time m_active.clear(); out[root][root] = 0.0f; parents[root][root] = -1;//idiom for end of path m_heapIdent[root] = m_active.push(root, 0.0f); while (remain && !m_active.isEmpty()) { whichnode = m_active.pop(); if (!(marked[whichnode] & 1)) { if (!(marked[whichnode] & 2)) --remain; marked[whichnode] |= 1; neighbors = nodeNeighbors[whichnode].data(); numNeigh = (int32_t)nodeNeighbors[whichnode].size(); for (j = 0; j < numNeigh; ++j) { whichneigh = neighbors[j]; if (marked[whichneigh] & 2) {//already has a value and parent for this root if (!(marked[whichneigh] & 8)) {//not yet in active list m_heapIdent[whichneigh] = m_active.push(whichneigh, out[root][whichneigh]); marked[whichneigh] |= 8; } } else { if (!(marked[whichneigh] & 1)) {//skip floating point math if marked tempf = out[root][whichnode] + distances[whichnode][j]; if (!(marked[whichneigh] & 4)) { out[root][whichneigh] = tempf; parents[root][whichneigh] = whichnode; marked[whichneigh] |= 4; m_heapIdent[whichneigh] = m_active.push(whichneigh, tempf); } else if (tempf < out[root][whichneigh]) { out[root][whichneigh] = tempf; parents[root][whichneigh] = whichnode; m_active.changekey(m_heapIdent[whichneigh], tempf); } } } } if (smooth) { neighbors = nodeNeighbors2[whichnode].data(); numNeigh = (int32_t)nodeNeighbors2[whichnode].size(); for (j = 0; j < numNeigh; ++j) { whichneigh = neighbors[j]; if (marked[whichneigh] & 2) {//already has a value and parent for this root if (!(marked[whichneigh] & 8)) {//not yet in active list m_heapIdent[whichneigh] = m_active.push(whichneigh, out[root][whichneigh]); marked[whichneigh] |= 8; } } else { if (!(marked[whichneigh] & 1)) {//skip floating point math if marked tempf = out[root][whichnode] + distances2[whichnode][j]; if (!(marked[whichneigh] & 4)) { out[root][whichneigh] = tempf; parents[root][whichneigh] = whichnode; marked[whichneigh] |= 4; m_heapIdent[whichneigh] = m_active.push(whichneigh, tempf); } else if (tempf < out[root][whichneigh]) { out[root][whichneigh] = tempf; parents[root][whichneigh] = whichnode; m_active.changekey(m_heapIdent[whichneigh], tempf); } } } } } } }//dijkstra done...lotsa brackets...now to propagate the information gained to other roots for (i = root + 1; i < numNodes; ++i) {//any node smaller than root already has all distances calculated for entire surface if ((marked[i] & 1) && !(marked[i] & 2))//don't propagate from it if we didn't reach it {//if endpoint already had distance to root precomputed, all available info from this node has been propagated previously midrevparent = i;//if we reached the endpoint, then it has a full parent chain that was also reached, so we don't have to test for the 1 flag further midpoint = parents[root][i];//also, if we ended early from remain, anything not marked with a 1 is marked with a 2 and skipped anyway endparent = midpoint; tempf = out[root][i]; while (midpoint != root) { tempf2 = tempf - out[root][midpoint]; if (midpoint > root) {//try to be swap-friendly by not setting values in finished columns out[midpoint][i] = tempf2;//use midpoint as root, parent of endpoint is endparent parents[midpoint][i] = endparent; } out[i][midpoint] = tempf2;//use endpoint as root (so, reverse the path), parent of midpoint is midrevparent parents[i][midpoint] = midrevparent; midrevparent = midpoint;//step along path midpoint = parents[root][midpoint]; } out[i][root] = out[root][i];//finally, fill the transpose parents[i][root] = midrevparent; } }//propagation of best paths to other roots complete, dijkstra again } while (prevdots < 50) { std::cout << '.'; ++prevdots; } std::cout << std::endl; for (i = 0; i < numNodes; ++i) { marked[i] = 0; } } void GeodesicHelper::getGeoFromNode(const int32_t node, float* valuesOut, const bool smoothflag) { CaretAssert(node >= 0 && node < numNodes && valuesOut != NULL); if (node < 0 || node >= numNodes || !valuesOut) { return; } CaretMutexLocker locked(&inUse);//don't screw with member variables while in use float* temp = output;//swap out the output pointer to avoid allocation output = valuesOut; dijkstra(node, smoothflag); output = temp;//restore the pointer to the original memory } void GeodesicHelper::getGeoFromNode(const int32_t node, float* valuesOut, int32_t* parentsOut, const bool smoothflag) { CaretAssert(node >= 0 && node < numNodes && valuesOut != NULL && parentsOut != NULL); if (node < 0 || node >= numNodes || !valuesOut || !parentsOut) { return; } CaretMutexLocker locked(&inUse);//don't screw with member variables while in use float* temp = output;//swap out the output pointer to avoid allocation int32_t* tempi = parent; output = valuesOut; parent = parentsOut; dijkstra(node, smoothflag); output = temp;//restore the pointers to the original memory parent = tempi; } void GeodesicHelper::getGeoFromNode(const int32_t node, std::vector& valuesOut, const bool smoothflag) { CaretAssert(node >= 0 && node < numNodes); if (node < 0 || node >= numNodes) { valuesOut.clear();//empty array is error condition return; } CaretMutexLocker locked(&inUse); float* temp = output;//swap the output pointer to avoid copy valuesOut.resize(numNodes); output = valuesOut.data(); dijkstra(node, smoothflag); output = temp;//restore } void GeodesicHelper::getGeoFromNode(const int32_t node, std::vector& valuesOut, std::vector& parentsOut, const bool smoothflag) { CaretAssert(node >= 0 && node < numNodes); if (node < 0 || node >= numNodes) { return; } CaretMutexLocker locked(&inUse); float* temp = output;//swap out the output pointer to avoid copying into the vector afterwards int32_t* tempi = parent; valuesOut.resize(numNodes); parentsOut.resize(numNodes); output = valuesOut.data(); parent = parentsOut.data(); dijkstra(node, smoothflag); output = temp;//restore the pointers to the original memory parent = tempi; } void GeodesicHelper::dijkstra(const int32_t root, const std::vector& interested, bool smooth) { int32_t i, j, whichnode, whichneigh, numNeigh, numChanged = 0, remain = 0; const int32_t* neighbors; float tempf; j = interested.size(); for (i = 0; i < j; ++i) { whichnode = interested[i]; if (!marked[whichnode]) { ++remain; marked[whichnode] = 2;//interested, not expanded, no valid value changed[numChanged++] = whichnode; } } output[root] = 0.0f; if (!marked[root]) { changed[numChanged++] = root; } marked[root] |= 4; parent[root] = -1;//idiom for end of path m_active.clear(); m_heapIdent[root] = m_active.push(root, 0.0f); while (remain && !m_active.isEmpty()) { whichnode = m_active.pop(); if (marked[whichnode] & 2) { --remain; } marked[whichnode] |= 1;//anything pulled from heap will already be marked as having a valid value (flag 4), so already in changed list neighbors = nodeNeighbors[whichnode].data(); numNeigh = (int32_t)nodeNeighbors[whichnode].size(); for (j = 0; j < numNeigh; ++j) { whichneigh = neighbors[j]; if (!(marked[whichneigh] & 1)) {//skip floating point math if frozen tempf = output[whichnode] + distances[whichnode][j];//isn't precomputation wonderful if (!(marked[whichneigh] & 4)) { if (!marked[whichneigh]) { changed[numChanged++] = whichneigh; } marked[whichneigh] |= 4; output[whichneigh] = tempf; parent[whichneigh] = whichnode; m_heapIdent[whichneigh] = m_active.push(whichneigh, tempf); } else if (tempf < output[whichneigh]) { output[whichneigh] = tempf; parent[whichneigh] = whichnode; m_active.changekey(m_heapIdent[whichneigh], tempf); } } } if (smooth)//repeat with numNeighbors2, nodeNeighbors2, distance2 { neighbors = nodeNeighbors2[whichnode].data(); numNeigh = (int32_t)nodeNeighbors2[whichnode].size(); for (j = 0; j < numNeigh; ++j) { whichneigh = neighbors[j]; if (!(marked[whichneigh] & 1)) {//skip floating point math if frozen tempf = output[whichnode] + distances2[whichnode][j]; if (!(marked[whichneigh] & 4)) { if (!marked[whichneigh]) { changed[numChanged++] = whichneigh; } marked[whichneigh] |= 4; output[whichneigh] = tempf; parent[whichneigh] = whichnode; m_heapIdent[whichneigh] = m_active.push(whichneigh, tempf); } else if (tempf < output[whichneigh]) { output[whichneigh] = tempf; parent[whichneigh] = whichnode; m_active.changekey(m_heapIdent[whichneigh], tempf); } } } } } for (i = 0; i < numChanged; ++i) { marked[changed[i]] = 0;//minimize reinitialization of arrays } } int32_t GeodesicHelper::dijkstra(const vector& startList, const vector& endList, const float& maxDist, bool smooth) { int32_t i, j, whichnode, whichneigh, numNeigh, numChanged = 0, ret = -1; const int32_t* neighbors; float tempf; m_active.clear(); j = (int32_t)startList.size(); for (i = 0; i < j; ++i) { if (marked[startList[i]] == 0) { output[startList[i]] = 0.0f; changed[numChanged++] = startList[i]; marked[startList[i]] = 4;//has valid value parent[startList[i]] = -1;//idiom for end of path m_heapIdent[startList[i]] = m_active.push(startList[i], 0.0f); } } j = (int32_t)endList.size(); for (i = 0; i < j; ++i) { if (marked[endList[i]] == 0) { changed[numChanged++] = endList[i]; marked[endList[i]] = 8;//stopping point } } while (!m_active.isEmpty()) { whichnode = m_active.pop(); if ((marked[whichnode] & 8) != 0)//we have found the closest node in the endList, we are done { ret = whichnode; break; } marked[whichnode] |= 1;//anything pulled from heap will already be marked as having a valid value (flag 4), so already in changed list neighbors = nodeNeighbors[whichnode].data(); numNeigh = (int32_t)nodeNeighbors[whichnode].size(); for (j = 0; j < numNeigh; ++j) { whichneigh = neighbors[j]; if (!(marked[whichneigh] & 1)) {//skip floating point math if frozen tempf = output[whichnode] + distances[whichnode][j]; if (tempf <= maxDist) { if (!(marked[whichneigh] & 4)) { parent[whichneigh] = whichnode; if (!marked[whichneigh]) { changed[numChanged++] = whichneigh; } marked[whichneigh] |= 4; output[whichneigh] = tempf; m_heapIdent[whichneigh] = m_active.push(whichneigh, tempf); } else if (tempf < output[whichneigh]) { m_active.changekey(m_heapIdent[whichneigh], tempf); output[whichneigh] = tempf; parent[whichneigh] = whichnode; } } } } if (smooth)//repeat with numNeighbors2, nodeNeighbors2, distance2 { neighbors = nodeNeighbors2[whichnode].data(); numNeigh = (int32_t)nodeNeighbors2[whichnode].size(); for (j = 0; j < numNeigh; ++j) { whichneigh = neighbors[j]; if (!(marked[whichneigh] & 1)) {//skip floating point math if frozen tempf = output[whichnode] + distances2[whichnode][j]; if (tempf <= maxDist) { if (!(marked[whichneigh] & 4)) { parent[whichneigh] = whichnode; if (!marked[whichneigh]) { changed[numChanged++] = whichneigh; } marked[whichneigh] |= 4; output[whichneigh] = tempf; m_heapIdent[whichneigh] = m_active.push(whichneigh, tempf); } else if (tempf < output[whichneigh]) { m_active.changekey(m_heapIdent[whichneigh], tempf); output[whichneigh] = tempf; parent[whichneigh] = whichnode; } } } } } } for (i = 0; i < numChanged; ++i) { marked[changed[i]] = 0;//minimize reinitialization of arrays } return ret; } int32_t GeodesicHelper::closest(const int32_t& root, const char* roi, const float& maxdist, float& distOut, bool smooth) { int32_t i, j, whichnode, whichneigh, numNeigh, numChanged = 0, ret = -1; const int32_t* neighbors; float tempf; output[root] = 0.0f; changed[numChanged++] = root; marked[root] |= 4; parent[root] = -1;//idiom for end of path m_active.clear(); m_heapIdent[root] = m_active.push(root, 0.0f); while (!m_active.isEmpty()) { whichnode = m_active.pop(); if (roi[whichnode] != 0)//we have found the closest node in the roi to the root, we are done { distOut = output[whichnode]; ret = whichnode; break; } marked[whichnode] |= 1;//anything pulled from heap will already be marked as having a valid value (flag 4), so already in changed list neighbors = nodeNeighbors[whichnode].data(); numNeigh = (int32_t)nodeNeighbors[whichnode].size(); for (j = 0; j < numNeigh; ++j) { whichneigh = neighbors[j]; if (!(marked[whichneigh] & 1)) {//skip floating point math if frozen tempf = output[whichnode] + distances[whichnode][j];//isn't precomputation wonderful if (tempf <= maxdist) { if (!(marked[whichneigh] & 4)) { parent[whichneigh] = whichnode; if (!marked[whichneigh]) { changed[numChanged++] = whichneigh; } marked[whichneigh] |= 4; output[whichneigh] = tempf; m_heapIdent[whichneigh] = m_active.push(whichneigh, tempf); } else if (tempf < output[whichneigh]) { m_active.changekey(m_heapIdent[whichneigh], tempf); output[whichneigh] = tempf; parent[whichneigh] = whichnode; } } } } if (smooth)//repeat with numNeighbors2, nodeNeighbors2, distance2 { neighbors = nodeNeighbors2[whichnode].data(); numNeigh = (int32_t)nodeNeighbors2[whichnode].size(); for (j = 0; j < numNeigh; ++j) { whichneigh = neighbors[j]; if (!(marked[whichneigh] & 1)) {//skip floating point math if frozen tempf = output[whichnode] + distances2[whichnode][j];//isn't precomputation wonderful if (tempf <= maxdist) { if (!(marked[whichneigh] & 4)) { parent[whichneigh] = whichnode; if (!marked[whichneigh]) { changed[numChanged++] = whichneigh; } marked[whichneigh] |= 4; output[whichneigh] = tempf; m_heapIdent[whichneigh] = m_active.push(whichneigh, tempf); } else if (tempf < output[whichneigh]) { m_active.changekey(m_heapIdent[whichneigh], tempf); output[whichneigh] = tempf; parent[whichneigh] = whichnode; } } } } } } for (i = 0; i < numChanged; ++i) { marked[changed[i]] = 0;//minimize reinitialization of arrays } return ret; } int32_t GeodesicHelper::closest(const int32_t& root, const char* roi, bool smooth) { int32_t i, j, whichnode, whichneigh, numNeigh, numChanged = 0, ret = -1; const int32_t* neighbors; float tempf; output[root] = 0.0f; changed[numChanged++] = root; marked[root] |= 4; parent[root] = -1;//idiom for end of path m_active.clear(); m_heapIdent[root] = m_active.push(root, 0.0f); while (!m_active.isEmpty()) { whichnode = m_active.pop(); if (roi[whichnode] != 0)//we have found the closest node in the roi to the root, we are done { ret = whichnode; break; } marked[whichnode] |= 1;//anything pulled from heap will already be marked as having a valid value (flag 4), so already in changed list neighbors = nodeNeighbors[whichnode].data(); numNeigh = (int32_t)nodeNeighbors[whichnode].size(); for (j = 0; j < numNeigh; ++j) { whichneigh = neighbors[j]; if (!(marked[whichneigh] & 1)) {//skip floating point math if frozen tempf = output[whichnode] + distances[whichnode][j];//isn't precomputation wonderful if (!(marked[whichneigh] & 4)) { parent[whichneigh] = whichnode; if (!marked[whichneigh]) { changed[numChanged++] = whichneigh; } marked[whichneigh] |= 4; output[whichneigh] = tempf; m_heapIdent[whichneigh] = m_active.push(whichneigh, tempf); } else if (tempf < output[whichneigh]) { m_active.changekey(m_heapIdent[whichneigh], tempf); output[whichneigh] = tempf; parent[whichneigh] = whichnode; } } } if (smooth)//repeat with numNeighbors2, nodeNeighbors2, distance2 { neighbors = nodeNeighbors2[whichnode].data(); numNeigh = (int32_t)nodeNeighbors2[whichnode].size(); for (j = 0; j < numNeigh; ++j) { whichneigh = neighbors[j]; if (!(marked[whichneigh] & 1)) {//skip floating point math if frozen tempf = output[whichnode] + distances2[whichnode][j];//isn't precomputation wonderful if (!(marked[whichneigh] & 4)) { parent[whichneigh] = whichnode; if (!marked[whichneigh]) { changed[numChanged++] = whichneigh; } marked[whichneigh] |= 4; output[whichneigh] = tempf; m_heapIdent[whichneigh] = m_active.push(whichneigh, tempf); } else if (tempf < output[whichneigh]) { m_active.changekey(m_heapIdent[whichneigh], tempf); output[whichneigh] = tempf; parent[whichneigh] = whichnode; } } } } } for (i = 0; i < numChanged; ++i) { marked[changed[i]] = 0;//minimize reinitialization of arrays } return ret; } void GeodesicHelper::aStar(const int32_t root, const int32_t endpoint, bool smooth) { int32_t whichnode, whichneigh, numNeigh, numChanged = 0; const int32_t* neighbors; float tempf; output[root] = 0.0f; changed[numChanged++] = root; marked[root] |= 4;//has value in output parent[root] = -1;//idiom for end of path m_active.clear(); float remainEucl = (nodeCoords[root] - nodeCoords[endpoint]).length(); heurVal[root] = remainEucl * m_corrAreaSmallestFactor; m_heapIdent[root] = m_active.push(root, heurVal[root]); while (!m_active.isEmpty()) { whichnode = m_active.pop();//we use a modifiable heap, so we don't need to check for duplicates marked[whichnode] |= 1;//frozen - will already be in changed list, due to being in heap if (whichnode == endpoint) break; neighbors = nodeNeighbors[whichnode].data(); numNeigh = (int32_t)nodeNeighbors[whichnode].size(); for (int32_t j = 0; j < numNeigh; ++j) { whichneigh = neighbors[j]; if (!(marked[whichneigh] & 1)) {//skip floating point math if frozen tempf = output[whichnode] + distances[whichnode][j]; if (!(marked[whichneigh] & 4)) { heurVal[whichneigh] = m_corrAreaSmallestFactor * (nodeCoords[whichneigh] - nodeCoords[endpoint]).length(); output[whichneigh] = tempf; parent[whichneigh] = whichnode; changed[numChanged++] = whichneigh;//having a valid value will be the first marking, so set changed marked[whichneigh] |= 4; m_heapIdent[whichneigh] = m_active.push(whichneigh, tempf + heurVal[whichneigh]); } else if (tempf < output[whichneigh]) { m_active.changekey(m_heapIdent[whichneigh], tempf + heurVal[whichneigh]); output[whichneigh] = tempf; parent[whichneigh] = whichnode; } } } if (smooth)//repeat with numNeighbors2, nodeNeighbors2, distance2 { neighbors = nodeNeighbors2[whichnode].data(); numNeigh = (int32_t)nodeNeighbors2[whichnode].size(); for (int32_t j = 0; j < numNeigh; ++j) { whichneigh = neighbors[j]; if (!(marked[whichneigh] & 1)) {//skip floating point math if frozen tempf = output[whichnode] + distances2[whichnode][j]; if (!(marked[whichneigh] & 4)) { heurVal[whichneigh] = m_corrAreaSmallestFactor * (nodeCoords[whichneigh] - nodeCoords[endpoint]).length(); output[whichneigh] = tempf; parent[whichneigh] = whichnode; changed[numChanged++] = whichneigh;//having a valid value will be the first marking, so set changed marked[whichneigh] |= 4; m_heapIdent[whichneigh] = m_active.push(whichneigh, tempf + heurVal[whichneigh]); } else if (tempf < output[whichneigh]) { m_active.changekey(m_heapIdent[whichneigh], tempf + heurVal[whichneigh]); output[whichneigh] = tempf; parent[whichneigh] = whichnode; } } } } } for (int32_t i = 0; i < numChanged; ++i) { marked[changed[i]] = 0;//minimize reinitialization of marked array } } float GeodesicHelper::linePenalty(const Vector3D& pos, const Vector3D& linep1, const Vector3D& linep2, const bool& segment) { if (segment) { return pos.distToLineSegment(linep1, linep2); } else { return pos.distToLine(linep1, linep2); } } float GeodesicHelper::lineHeuristic(const Vector3D& pos, const Vector3D& linep1, const Vector3D& linep2, const float& remainEucl, const bool& segment) { float tempf;//to make it consistent, assume it minimizes the line penalty by taking a straight line towards the line (segment), encountering the endpoint en route, or on the line (segment) if (segment)//the distance heuristic assumes a different path to make it consistent versus path length, and the sum of consistent heuristics is consistent for the sum of the penalties { tempf = m_corrAreaSmallestFactor * pos.distToLineSegment(linep1, linep2);//all euclidean distances must be modified by the smallest area correction factor } else { tempf = m_corrAreaSmallestFactor * pos.distToLine(linep1, linep2); } if (remainEucl < tempf) { return m_corrAreaSmallestFactor * remainEucl * (2.0f * tempf - remainEucl);//hits the endpoint en route } else { return tempf * tempf;//reaches the line (segment), then finds the endpoint with no additional penalty } } void GeodesicHelper::aStarLine(const int32_t& root, const int32_t& endpoint, const Vector3D& linep1, const Vector3D& linep2, const bool& segment) { int32_t whichnode, whichneigh, numNeigh, numChanged = 0; float penaltyScale = 0.5f / m_avgNodeSpacing;//to prevent change in scale from changing the optimal path - 0.5f is ostensibly for averaging between endpoints, but is largely arbitrary const int32_t* neighbors; float tempf; output[root] = 0.0f; changed[numChanged++] = root; marked[root] |= 4;//has value in output parent[root] = -1;//idiom for end of path m_active.clear(); float remainEucl = (nodeCoords[root] - nodeCoords[endpoint]).length(); heurVal[root] = m_corrAreaSmallestFactor * remainEucl + penaltyScale * lineHeuristic(nodeCoords[root], linep1, linep2, remainEucl, segment); m_heapIdent[root] = m_active.push(root, heurVal[root]);//to get consistent heuristic, assume it can take straight line to endpoint for total distance, and straight line to target line (segment) at the same time while (!m_active.isEmpty()) { whichnode = m_active.pop();//we use a modifiable heap, so we don't need to check for duplicates marked[whichnode] |= 1;//frozen - will already be in changed list, due to being in heap if (whichnode == endpoint) break; neighbors = nodeNeighbors[whichnode].data(); numNeigh = (int32_t)nodeNeighbors[whichnode].size(); for (int32_t j = 0; j < numNeigh; ++j) { whichneigh = neighbors[j]; if (!(marked[whichneigh] & 1)) {//skip floating point math if frozen tempf = output[whichnode] + distances[whichnode][j] + penaltyScale * distances[whichnode][j] * (linePenalty(nodeCoords[whichnode], linep1, linep2, segment) + linePenalty(nodeCoords[whichneigh], linep1, linep2, segment)); if (!(marked[whichneigh] & 4)) { remainEucl = (nodeCoords[whichneigh] - nodeCoords[endpoint]).length(); heurVal[whichneigh] = m_corrAreaSmallestFactor * remainEucl + penaltyScale * lineHeuristic(nodeCoords[whichneigh], linep1, linep2, remainEucl, segment); output[whichneigh] = tempf; parent[whichneigh] = whichnode; changed[numChanged++] = whichneigh;//having a valid value will be the first marking, so set changed marked[whichneigh] |= 4; m_heapIdent[whichneigh] = m_active.push(whichneigh, tempf + heurVal[whichneigh]); } else if (tempf < output[whichneigh]) { m_active.changekey(m_heapIdent[whichneigh], tempf + heurVal[whichneigh]); output[whichneigh] = tempf; parent[whichneigh] = whichnode; } } } } for (int32_t i = 0; i < numChanged; ++i) { marked[changed[i]] = 0;//minimize reinitialization of marked array } } void GeodesicHelper::aStarData(const int32_t& root, const int32_t& endpoint, const float* data, const float& followStrength, const float* roiData, const bool& smooth) {//NOTE: for consistent behavior, data must not contain negatives (or anything non-numeric) int32_t whichnode, whichneigh, numNeigh, numChanged = 0; const int32_t* neighbors; float tempf; output[root] = 0.0f; changed[numChanged++] = root; marked[root] |= 4;//has value in output parent[root] = -1;//idiom for end of path m_active.clear(); heurVal[root] = m_corrAreaSmallestFactor * (nodeCoords[root] - nodeCoords[endpoint]).length();//data could be zero all the way along the optimal path, so euclidean distance is the only consistent heuristic m_heapIdent[root] = m_active.push(root, heurVal[root]); while (!m_active.isEmpty()) { whichnode = m_active.pop();//we use a modifiable heap, so we don't need to check for duplicates marked[whichnode] |= 1;//frozen - will already be in changed list, due to being in heap if (whichnode == endpoint) break; neighbors = nodeNeighbors[whichnode].data(); numNeigh = (int32_t)nodeNeighbors[whichnode].size(); for (int32_t j = 0; j < numNeigh; ++j) { whichneigh = neighbors[j]; if ((roiData == NULL || roiData[whichneigh] > 0.0f) && !(marked[whichneigh] & 1)) {//skip floating point math if frozen or outside roi tempf = output[whichnode] + distances[whichnode][j] * (1.0f + followStrength * (data[whichnode] + data[whichneigh]));//integrate 1 + strength * value to get distance plus path-integrated data if (!(marked[whichneigh] & 4)) { heurVal[whichneigh] = m_corrAreaSmallestFactor * (nodeCoords[whichneigh] - nodeCoords[endpoint]).length(); output[whichneigh] = tempf; parent[whichneigh] = whichnode; changed[numChanged++] = whichneigh;//having a valid value will be the first marking, so set changed marked[whichneigh] |= 4; m_heapIdent[whichneigh] = m_active.push(whichneigh, tempf + heurVal[whichneigh]); } else if (tempf < output[whichneigh]) { m_active.changekey(m_heapIdent[whichneigh], tempf + heurVal[whichneigh]); output[whichneigh] = tempf; parent[whichneigh] = whichnode; } } } if (smooth)//repeat with numNeighbors2, nodeNeighbors2, distance2 { neighbors = nodeNeighbors2[whichnode].data(); numNeigh = (int32_t)nodeNeighbors2[whichnode].size(); const GeodesicHelperBase::CrawlInfo* pathInfo = neighbors2PathInfo[whichnode].data(); for (int32_t j = 0; j < numNeigh; ++j) { whichneigh = neighbors[j]; if ((roiData == NULL || roiData[whichneigh] > 0.0f) && !(marked[whichneigh] & 1)) {//skip floating point math if frozen or outside roi tempf = output[whichnode] + distances2[whichnode][j] + followStrength * (data[whichnode] * pathInfo[j].pieceDists[0] + data[whichneigh] * pathInfo[j].pieceDists[1] + distances2[whichnode][j] * (data[pathInfo[j].edgeNodes[0]] * pathInfo[j].edgeWeight + data[pathInfo[j].edgeNodes[1]] * (1.0f - pathInfo[j].edgeWeight))); if (!(marked[whichneigh] & 4)) { heurVal[whichneigh] = m_corrAreaSmallestFactor * (nodeCoords[whichneigh] - nodeCoords[endpoint]).length(); output[whichneigh] = tempf; parent[whichneigh] = whichnode; changed[numChanged++] = whichneigh;//having a valid value will be the first marking, so set changed marked[whichneigh] |= 4; m_heapIdent[whichneigh] = m_active.push(whichneigh, tempf + heurVal[whichneigh]); } else if (tempf < output[whichneigh]) { m_active.changekey(m_heapIdent[whichneigh], tempf + heurVal[whichneigh]); output[whichneigh] = tempf; parent[whichneigh] = whichnode; } } } } } for (int32_t i = 0; i < numChanged; ++i) { marked[changed[i]] = 0;//minimize reinitialization of marked array } } void GeodesicHelper::getGeoToTheseNodes(const int32_t root, const std::vector& ofInterest, std::vector& distsOut, bool smoothflag) { CaretAssert(root >= 0 && root < numNodes); if (root < 0 || root >= numNodes) { distsOut.clear();//empty array is error condition return; } int32_t i, mysize = ofInterest.size(), node; for (i = 0; i < mysize; ++i) {//needs to do a linear scan of this array later anyway, so lets sanity check it node = ofInterest[i]; if (node < 0 || node >= numNodes) { distsOut.clear();//empty array is error condition return; } } CaretMutexLocker locked(&inUse);//let sanity checks fail without locking dijkstra(root, ofInterest, smoothflag); distsOut.resize(mysize); for (i = 0; i < mysize; ++i) { distsOut[i] = output[ofInterest[i]]; } } void GeodesicHelper::getPathToNode(const int32_t root, const int32_t endpoint, vector& pathNodesOut, vector& pathDistsOut, bool smoothflag) { CaretAssert(root >= 0 && root < numNodes && endpoint >= 0 && endpoint < numNodes); pathNodesOut.clear(); pathDistsOut.clear(); if (root < 0 || root >= numNodes || endpoint < 0 || endpoint >= numNodes) { return; } CaretMutexLocker locked(&inUse);//let sanity checks fail without locking parent[endpoint] = -2;//sentinel value that DOESN'T mean end of path aStar(root, endpoint, smoothflag); if (parent[endpoint] == -2)//check for invalid value { return; } vector tempReverse; int32_t next = endpoint; while (next != root) { tempReverse.push_back(next); next = parent[next]; } tempReverse.push_back(next); int32_t tempSize = (int32_t)tempReverse.size(); for (int32_t i = tempSize - 1; i >= 0; --i) { int32_t tempNode = tempReverse[i]; pathNodesOut.push_back(tempNode); pathDistsOut.push_back(output[tempNode]); } } void GeodesicHelper::getPathBetweenNodeLists(const vector& startList, const vector& endList, const float& maxDist, vector& pathNodesOut, vector& pathDistsOut, bool smoothflag) { pathNodesOut.clear(); pathDistsOut.clear(); for (size_t i = 0; i < startList.size(); ++i) { if (startList[i] < 0 || startList[i] >= numNodes) return; } for (size_t i = 0; i < endList.size(); ++i) { if (endList[i] < 0 || endList[i] >= numNodes) return; } CaretMutexLocker locked(&inUse);//let sanity checks fail without locking int32_t pathEnd = dijkstra(startList, endList, maxDist, smoothflag);//not sure if A* with a PointLocator would be any faster, so don't add a dependency if (pathEnd == -1) return;//no path found vector tempReverse; int32_t next = pathEnd; while (next != -1)//-1 is "parent" of start node { tempReverse.push_back(next); next = parent[next]; } tempReverse.push_back(next); int32_t tempSize = (int32_t)tempReverse.size(); for (int32_t i = tempSize - 1; i >= 0; --i) { int32_t tempNode = tempReverse[i]; pathNodesOut.push_back(tempNode); pathDistsOut.push_back(output[tempNode]); } } void GeodesicHelper::getPathAlongLine(const int32_t root, const int32_t endpoint, const Vector3D& linep1, const Vector3D& linep2, vector& pathNodesOut, vector& pathDistsOut) { CaretAssert(root >= 0 && root < numNodes && endpoint >= 0 && endpoint < numNodes); pathNodesOut.clear(); pathDistsOut.clear(); if (root < 0 || root >= numNodes || endpoint < 0 || endpoint >= numNodes) { return; } CaretMutexLocker locked(&inUse);//let sanity checks fail without locking parent[endpoint] = -2;//sentinel value that DOESN'T mean end of path aStarLine(root, endpoint, linep1, linep2, false); if (parent[endpoint] == -2)//check for invalid value { return; } vector tempReverse; int32_t next = endpoint; while (next != root) { tempReverse.push_back(next); next = parent[next]; } tempReverse.push_back(next); int32_t tempSize = (int32_t)tempReverse.size(); for (int32_t i = tempSize - 1; i >= 0; --i) { int32_t tempNode = tempReverse[i]; pathNodesOut.push_back(tempNode); pathDistsOut.push_back(output[tempNode]); } } void GeodesicHelper::getPathAlongLineSegment(const int32_t root, const int32_t endpoint, const Vector3D& linep1, const Vector3D& linep2, vector& pathNodesOut, vector& pathDistsOut) { CaretAssert(root >= 0 && root < numNodes && endpoint >= 0 && endpoint < numNodes); pathNodesOut.clear(); pathDistsOut.clear(); if (root < 0 || root >= numNodes || endpoint < 0 || endpoint >= numNodes) { return; } vector ofInterest(1, endpoint); CaretMutexLocker locked(&inUse);//let sanity checks fail without locking parent[endpoint] = -2;//sentinel value that DOESN'T mean end of path aStarLine(root, endpoint, linep1, linep2, true); if (parent[endpoint] == -2)//check for invalid value { return; } vector tempReverse; int32_t next = endpoint; while (next != root) { tempReverse.push_back(next); next = parent[next]; } tempReverse.push_back(next); int32_t tempSize = (int32_t)tempReverse.size(); for (int32_t i = tempSize - 1; i >= 0; --i) { int32_t tempNode = tempReverse[i]; pathNodesOut.push_back(tempNode); pathDistsOut.push_back(output[tempNode]); } } void GeodesicHelper::getPathFollowingData(const int32_t root, const int32_t endpoint, const float* data, vector& pathNodesOut, vector& pathDistsOut, const float& followStrength, const float* roiData, const bool& followMaximum, const bool& smoothFlag) { CaretAssert(root >= 0 && root < numNodes && endpoint >= 0 && endpoint < numNodes); pathNodesOut.clear(); pathDistsOut.clear(); if (root < 0 || root >= numNodes || endpoint < 0 || endpoint >= numNodes) { return; } if (roiData != NULL && !(roiData[root] > 0.0f && roiData[endpoint] > 0.0f)) { return; } vector rescaledData(numNodes); float minimum = 0.0f, maximum = 0.0f; bool first = true; for (int i = 0; i < numNodes; ++i)//first normalize contrast in the roi, so we are not sensitive to the absolute values of the data { if (roiData == NULL || roiData[i] > 0.0f) { if (first) { first = false; minimum = data[i]; maximum = data[i]; } else { if (data[i] < minimum) minimum = data[i]; if (data[i] > maximum) maximum = data[i]; } } } if (minimum == maximum)//no contrast, will do simple shortest path { maximum = minimum + 1.0f;//so map it all to minimum value to prevent divide by 0 }//doesn't matter what value it is mapped to if there is no contrast, rescaled data is integrated along path length, and path length is always a factor float range = maximum - minimum; for (int i = 0; i < numNodes; ++i) { if (roiData == NULL || roiData[i] > 0.0f) { if (followMaximum)//also deal with follow minimum vs maximum { rescaledData[i] = (maximum - data[i]) / range; } else { rescaledData[i] = (data[i] - minimum) / range; } } } CaretMutexLocker locked(&inUse);//let sanity checks fail without locking parent[endpoint] = -2;//sentinel value that DOESN'T mean end of path aStarData(root, endpoint, rescaledData.data(), followStrength, roiData, smoothFlag); if (parent[endpoint] == -2)//check for invalid value { return; } vector tempReverse; int32_t next = endpoint; while (next != root) { tempReverse.push_back(next); next = parent[next]; } tempReverse.push_back(next); int32_t tempSize = (int32_t)tempReverse.size(); for (int32_t i = tempSize - 1; i >= 0; --i) { int32_t tempNode = tempReverse[i]; pathNodesOut.push_back(tempNode); pathDistsOut.push_back(output[tempNode]); } } int32_t GeodesicHelper::getClosestNodeInRoi(const int32_t& root, const char* roi, const float& maxdist, float& distOut, bool smoothflag) { CaretAssert(root >= 0 && root < numNodes && maxdist >= 0.0f); if (root < 0 || root >= numNodes || maxdist < 0.0f) { return -1; } CaretMutexLocker locked(&inUse);//let sanity checks fail without locking return closest(root, roi, maxdist, distOut, smoothflag); } int32_t GeodesicHelper::getClosestNodeInRoi(const int32_t& root, const char* roi, vector& pathNodesOut, vector& pathDistsOut, bool smoothflag) { CaretAssert(root >= 0 && root < numNodes); pathNodesOut.clear(); pathDistsOut.clear(); if (root < 0 || root >= numNodes) { return -1; } CaretMutexLocker locked(&inUse);//let sanity checks fail without locking int32_t ret = closest(root, roi, smoothflag); if (ret == -1) return ret; vector tempReverse; int32_t next = ret; while (next != root) { tempReverse.push_back(next); next = parent[next]; } tempReverse.push_back(next); int32_t tempSize = (int32_t)tempReverse.size(); for (int32_t i = tempSize - 1; i >= 0; --i) { int32_t tempNode = tempReverse[i]; pathNodesOut.push_back(tempNode); pathDistsOut.push_back(output[tempNode]); } return ret; } connectome-workbench-1.2.3+git41-gc4c6c90/src/Files/GeodesicHelper.h000066400000000000000000000233711300200146000246470ustar00rootroot00000000000000 #ifndef __GEODESIC_HELPER_H__ #define __GEODESIC_HELPER_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 //for inlining #include "CaretMutex.h" #include "CaretPointer.h" #include "CaretHeap.h" #include "Vector3D.h" namespace caret { class SurfaceFile; //NOTE: this class does NOT stay associated with the coord passed into it, it takes a snapshot of the surface in the constructor //This is because it is designed to be fast on repeated calls on a single surface class GeodesicHelperBase {//This does the neighbor computation, create a GeodesicHelper to contain the temporary arrays and actually do stuff public: struct CrawlInfo { int32_t edgeNodes[2]; float edgeWeight, pieceDists[2]; }; private: GeodesicHelperBase();//can't construct without arguments GeodesicHelperBase& operator=(const GeodesicHelperBase& right);//can't assign GeodesicHelperBase(const GeodesicHelperBase& right);//can't use copy constructor std::vector > distances, distances2; std::vector > nodeNeighbors, nodeNeighbors2; std::vector > neighbors2PathInfo; std::vector nodeCoords;//for line-following and A* int32_t numNodes; float m_avgNodeSpacing;//to use for balancing line following penalty float m_corrAreaSmallestFactor;//so that heuristics can be consistent despite corrected areas public: explicit GeodesicHelperBase(const SurfaceFile* surfaceIn, const float* correctedAreas = NULL);//NOTE: this is only an APPROXIMATE correction, use the real surface whenever possible friend class GeodesicHelper;//let it grab the private variables it needs }; class GeodesicHelper { CaretPointer m_myBase;//mostly just for automatic memory management CaretMutex inUse;//could add a function and a locker pointer to be able to lock to thread once, then call repeatedly without locking, if mutex overhead is actually a factor CaretMinHeap m_active;//save and reuse the allocated space const std::vector* distances, *distances2; const std::vector* nodeNeighbors, *nodeNeighbors2; const std::vector* neighbors2PathInfo; const Vector3D* nodeCoords; float* output; int32_t* parent; std::vector outputStore; std::vector heurVal; std::vector marked, changed, parentStore; std::vector m_heapIdent; int32_t numNodes; float m_avgNodeSpacing; float m_corrAreaSmallestFactor; GeodesicHelper();//Don't allow construction without arguments GeodesicHelper& operator=(const GeodesicHelper& right);//can't assign GeodesicHelper(const GeodesicHelper&);//can't use copy constructor void dijkstra(const int32_t root, const float maxdist, std::vector& nodes, std::vector& dists, bool smooth);//geodesic distance restricted void dijkstra(const int32_t root, bool smooth);//full surface void dijkstra(const int32_t root, const std::vector& interested, bool smooth);//partial surface int32_t dijkstra(const std::vector& startList, const std::vector& endList, const float& maxDist, bool smooth);//one path that connects lists void alltoall(float** out, int32_t** parents, bool smooth);//must be fully allocated int32_t closest(const int32_t& root, const char* roi, const float& maxdist, float& distOut, bool smooth);//just closest node int32_t closest(const int32_t& root, const char* roi, bool smooth);//just closest node void aStar(const int32_t root, const int32_t endpoint, bool smooth);//faster method for path float linePenalty(const Vector3D& pos, const Vector3D& linep1, const Vector3D& linep2, const bool& segment); float lineHeuristic(const Vector3D& pos, const Vector3D& linep1, const Vector3D& linep2, const float& remainEucl, const bool& segment); void aStarLine(const int32_t& root, const int32_t& endpoint, const Vector3D& linep1, const Vector3D& linep2, const bool& segment);//to single endpoint, following line void aStarData(const int32_t& root, const int32_t& endpoint, const float* data, const float& followStrength, const float* roiData, const bool& smooth);//to single endpoint, following data public: explicit GeodesicHelper(const CaretPointer& baseIn); /// Get distances from root node, up to a geodesic distance cutoff (stops computing when no more nodes are within that distance) void getNodesToGeoDist(const int32_t node, const float maxdist, std::vector& neighborsOut, std::vector& distsOut, const bool smoothflag = true); /// Get distances from root node, up to a geodesic distance cutoff, and also return their parents (root node has -1 as parent) void getNodesToGeoDist(const int32_t node, const float maxdist, std::vector& neighborsOut, std::vector& distsOut, std::vector& parentsOut, const bool smoothflag = true); /// Get distances from root node to entire surface - allocate the array first void getGeoFromNode(const int32_t node, float* valuesOut, const bool smoothflag = true);//MUST be already allocated to number of nodes /// Get distances from root node to entire surface, vector method void getGeoFromNode(const int32_t node, std::vector& valuesOut, const bool smoothflag = true); /// Get distances from root node to entire surface and parents - allocate both arrays first (root node has -1 as parent) void getGeoFromNode(const int32_t node, float* valuesOut, int32_t* parentsOut, const bool smoothflag = true); /// Get distances from root node to entire surface, and their parents, vector method (root node has -1 as parent) void getGeoFromNode(const int32_t node, std::vector& valuesOut, std::vector& parentsOut, const bool smoothflag = true); /// Get distances from all nodes to all nodes, passes back NULL if cannot allocate, if successful you must eventually delete the memory float** getGeoAllToAll(const bool smooth = true);//i really don't think this needs an overloaded function that outputs parents /// Get distances to a restricted set of nodes - output vector is in the SAME ORDER and same size as the input vector ofInterest void getGeoToTheseNodes(const int32_t root, const std::vector& ofInterest, std::vector& distsOut, bool smoothflag = true); ///get the distances and nodes along the path to a node - NOTE: default is not smooth distances, so that all nodes in the path are connected in the surface void getPathToNode(const int32_t root, const int32_t endpoint, std::vector& pathNodesOut, std::vector& pathDistsOut, bool smoothflag = false); ///shortest path between two sets of nodes (for instance, clusters), with distance limit void getPathBetweenNodeLists(const std::vector& startList, const std::vector& endList, const float& maxDist, std::vector& pathNodesOut, std::vector& pathDistsOut, bool smoothflag); ///get the distances and nodes along the path to a node - NOTE: does not do smooth distances, so that all nodes in the path are connected in the surface void getPathAlongLine(const int32_t root, const int32_t endpoint, const Vector3D& linep1, const Vector3D& linep2, std::vector& pathNodesOut, std::vector& pathDistsOut); ///get the distances and nodes along the path to a node - NOTE: does not do smooth distances, so that all nodes in the path are connected in the surface void getPathAlongLineSegment(const int32_t root, const int32_t endpoint, const Vector3D& linep1, const Vector3D& linep2, std::vector& pathNodesOut, std::vector& pathDistsOut); ///path drawing by peaks or troughs of supplied data, controlled by followMaximum void getPathFollowingData(const int32_t root, const int32_t endpoint, const float* data, std::vector& pathNodesOut, std::vector& pathDistsOut, const float& followStrength = 5.0f, const float* roiData = NULL, const bool& followMaximum = true, const bool& smoothFlag = false); ///get just the closest node in the region and max distance given, returns -1 if no such node found - roi value of 0 means not in region, anything else is in region int32_t getClosestNodeInRoi(const int32_t& root, const char* roi, const float& maxdist, float& distOut, bool smoothflag = true); int32_t getClosestNodeInRoi(const int32_t& root, const char* roi, std::vector& pathNodesOut, std::vector& pathDistsOut, bool smoothflag); }; } //namespace caret #endif connectome-workbench-1.2.3+git41-gc4c6c90/src/Files/GiftiTypeFile.cxx000066400000000000000000000631661300200146000250520ustar00rootroot00000000000000/*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 "CaretLogger.h" #include "DataFileContentInformation.h" #include "DataFileException.h" #include "FastStatistics.h" #include "GiftiDataArray.h" #include "GiftiFile.h" #include "GiftiMetaData.h" #include "GiftiTypeFile.h" #include "GiftiMetaDataXmlElements.h" #include "Histogram.h" #include "PaletteColorMapping.h" #include "PaletteColorMappingSaxReader.h" #include "SurfaceFile.h" using namespace caret; /** * Constructor. */ GiftiTypeFile::GiftiTypeFile(const DataFileTypeEnum::Enum dataFileType) : CaretMappableDataFile(dataFileType) { this->initializeMembersGiftiTypeFile(); } /** * Destructor. */ GiftiTypeFile::~GiftiTypeFile() { if (this->giftiFile != NULL) { delete this->giftiFile; this->giftiFile = NULL; } } /** * Copy Constructor. * * @param gtf * File that is copied. */ GiftiTypeFile::GiftiTypeFile(const GiftiTypeFile& gtf) : CaretMappableDataFile(gtf) { this->giftiFile = new GiftiFile(*gtf.giftiFile);//NOTE: while CONSTRUCTING, this has virtual type GiftiTypeFile*, NOT MetricFile*, or whatever }//so, validateDataArraysAfterReading will ABORT due to pure virtual /** * Assignment operator. * * @param gtf * File whose contents are copied to this file. */ GiftiTypeFile& GiftiTypeFile::operator=(const GiftiTypeFile& gtf) { if (this != >f) { CaretMappableDataFile::operator=(gtf); this->copyHelperGiftiTypeFile(gtf); } return *this; } /** * Clear the contents of this file. */ void GiftiTypeFile::clear() { DataFile::clear(); this->giftiFile->clear(); } /** * Clear modified status. */ void GiftiTypeFile::clearModified() { CaretDataFile::clearModified(); this->giftiFile->clearModified(); } /** * @return True if any of the maps in this file contain a * color mapping that possesses a modified status. */ bool GiftiTypeFile::isModifiedExcludingPaletteColorMapping() const { if (CaretMappableDataFile::isModifiedExcludingPaletteColorMapping()) { return true; } if (this->giftiFile->isModified()) { return true; } return false; } /** * Is this file empty? * * @return true if file is empty, else false. */ bool GiftiTypeFile::isEmpty() const { return this->giftiFile->isEmpty(); } /** * Read the file. * * @param filename * Name of file to read. * * @throws DataFileException * If there is an error reading the file. */ void GiftiTypeFile::readFile(const AString& filename) { clear(); checkFileReadability(filename); this->setFileName(filename); this->giftiFile->readFile(filename); this->validateDataArraysAfterReading(); this->clearModified(); } /** * Write the file. * * @param filename * Name of file to read. * * @throws DataFileException * If there is an error writing the file. */ void GiftiTypeFile::writeFile(const AString& filename) { checkFileWritability(filename); this->giftiFile->writeFile(filename); this->clearModified(); } /** * Helps with file copying. * * @param gtf * File that is copied. */ void GiftiTypeFile::copyHelperGiftiTypeFile(const GiftiTypeFile& gtf) { if (this->giftiFile != NULL) { delete this->giftiFile; } this->giftiFile = new GiftiFile(*gtf.giftiFile); this->validateDataArraysAfterReading(); } /** * Initialize members of this class. */ void GiftiTypeFile::initializeMembersGiftiTypeFile() { this->giftiFile = new GiftiFile(); } /** * Get information about this file's contents. * @return * Information about the file's contents. */ AString GiftiTypeFile::toString() const { return this->giftiFile->toString(); } StructureEnum::Enum GiftiTypeFile::getStructure() const { AString structurePrimaryName; /* * Surface contains anatomical structure in pointset array. */ const SurfaceFile* surfaceFile = dynamic_cast(this); if (surfaceFile != NULL) { const GiftiDataArray* gda = this->giftiFile->getDataArrayWithIntent(NiftiIntentEnum::NIFTI_INTENT_POINTSET); const GiftiMetaData* metadata = gda->getMetaData(); structurePrimaryName = metadata->get(GiftiMetaDataXmlElements::METADATA_NAME_ANATOMICAL_STRUCTURE_PRIMARY); } else { const GiftiMetaData* metadata = this->giftiFile->getMetaData(); structurePrimaryName = metadata->get(GiftiMetaDataXmlElements::METADATA_NAME_ANATOMICAL_STRUCTURE_PRIMARY); } bool isValid = false; StructureEnum::Enum structure = StructureEnum::fromGuiName(structurePrimaryName, &isValid); return structure; } /** * Set the structure. * @param structure * New value for file's structure. */ void GiftiTypeFile::setStructure(const StructureEnum::Enum structure) { const AString structureName = StructureEnum::toGuiName(structure); /* * Surface contains anatomical structure in pointset array. */ SurfaceFile* surfaceFile = dynamic_cast(this); if (surfaceFile != NULL) { GiftiDataArray* gda = this->giftiFile->getDataArrayWithIntent(NiftiIntentEnum::NIFTI_INTENT_POINTSET); GiftiMetaData* metadata = gda->getMetaData(); metadata->set(GiftiMetaDataXmlElements::METADATA_NAME_ANATOMICAL_STRUCTURE_PRIMARY, structureName); } else { GiftiMetaData* metadata = this->giftiFile->getMetaData(); metadata->set(GiftiMetaDataXmlElements::METADATA_NAME_ANATOMICAL_STRUCTURE_PRIMARY, structureName); } } /** * Add map(s) to this GIFTI file. * @param numberOfNodes * Number of nodes. If file is not empty, this value must * match the number of nodes that are in the file. * @param numberOfMaps * Number of maps to add. */ void GiftiTypeFile::addMaps(const int32_t /*numberOfNodes*/, const int32_t /*numberOfMaps*/) { throw DataFileException(getFileName(), "This file does not support adding additional maps"); } /** * @return Get access to the file's metadata. */ GiftiMetaData* GiftiTypeFile::getFileMetaData() { return this->giftiFile->getMetaData(); } /** * @return Get access to unmodifiable file's metadata. */ const GiftiMetaData* GiftiTypeFile::getFileMetaData() const { return this->giftiFile->getMetaData(); } /** * Verify that all of the data arrays have the same number of rows. * @throws DataFileException * If there are data arrays that have a different number of rows. */ void GiftiTypeFile::verifyDataArraysHaveSameNumberOfRows(const int32_t minimumSecondDimension, const int32_t maximumSecondDimension) const { const int32_t numberOfArrays = this->giftiFile->getNumberOfDataArrays(); if (numberOfArrays > 1) { /* * Verify all arrays contain the same number of rows. */ int64_t numberOfRows = this->giftiFile->getDataArray(0)->getNumberOfRows(); for (int32_t i = 1; i < numberOfArrays; i++) { const int32_t arrayNumberOfRows = this->giftiFile->getDataArray(i)->getNumberOfRows(); if (numberOfRows != arrayNumberOfRows) { AString message = "All data arrays (columns) in the file must have the same number of rows."; message += " The first array (column) contains " + AString::number(numberOfRows) + " rows."; message += " Array " + AString::number(i + 1) + " contains " + AString::number(arrayNumberOfRows) + " rows."; DataFileException e(getFileName(), message); CaretLogThrowing(e); throw e; } } /* * Verify that second dimensions is within valid range. */ for (int32_t i = 0; i < numberOfArrays; i++) { const GiftiDataArray* gda = this->giftiFile->getDataArray(i); const int32_t numberOfDimensions = gda->getNumberOfDimensions(); if (numberOfDimensions > 2) { DataFileException e(getFileName(), "Data array " + AString::number(i + 1) + " contains " + AString::number(numberOfDimensions) + " dimensions. Two is the maximum allowed."); CaretLogThrowing(e); throw e; } int32_t secondDimension = 0; if (numberOfDimensions > 1) { secondDimension = gda->getDimension(1); if (secondDimension == 1) { secondDimension = 0; } } if ((secondDimension < minimumSecondDimension) || (secondDimension > maximumSecondDimension)) { DataFileException e(getFileName(), "Data array " + AString::number(i + 1) + " second dimension is " + AString::number(numberOfDimensions) + ". Minimum allowed is " + AString::number(minimumSecondDimension) + ". Maximum allowed is " + AString::number(maximumSecondDimension)); CaretLogThrowing(e); throw e; } } } } /** * Get the name of a file column. * @param columnIndex * Index of column. * @return * Name of column. */ AString GiftiTypeFile::getColumnName(const int columnIndex) const { return this->giftiFile->getDataArrayName(columnIndex); } /** * Find the first column with the given column name. * @param columnName * Name of column. * @return * Index of column with name or negative if no match. */ int32_t GiftiTypeFile::getColumnIndexFromColumnName(const AString& columnName) const { return this->giftiFile->getDataArrayWithNameIndex(columnName); } /** * Set the name of a column. * @param columnIndex * Index of column. * @param columnName * New name for column. */ void GiftiTypeFile::setColumnName(const int32_t columnIndex, const AString& columnName) { this->giftiFile->setDataArrayName(columnIndex, columnName); } /** * @return The palette color mapping for a data column. */ PaletteColorMapping* GiftiTypeFile::getPaletteColorMapping(const int32_t columnIndex) { GiftiDataArray* gda = this->giftiFile->getDataArray(columnIndex); return gda->getPaletteColorMapping(); } /** * @return The palette color mapping for a data column. */ const PaletteColorMapping* GiftiTypeFile::getPaletteColorMapping(const int32_t columnIndex) const { const GiftiDataArray* gda = this->giftiFile->getDataArray(columnIndex); return gda->getPaletteColorMapping(); } /** * @return Is the data mappable to a surface? */ bool GiftiTypeFile::isSurfaceMappable() const { return true; } /** * @return Is the data mappable to a volume? */ bool GiftiTypeFile::isVolumeMappable() const { return false; } /** * @return The number of maps in the file. * Note: Caret5 used the term 'columns'. */ int32_t GiftiTypeFile::getNumberOfMaps() const { return this->giftiFile->getNumberOfDataArrays(); } /** * Get the name of the map at the given index. * * @param mapIndex * Index of the map. * @return * Name of the map. */ AString GiftiTypeFile::getMapName(const int32_t mapIndex) const { return this->giftiFile->getDataArrayName(mapIndex); } /** * Set the name of the map at the given index. * * @param mapIndex * Index of the map. * @param mapName * New name for the map. */ void GiftiTypeFile::setMapName(const int32_t mapIndex, const AString& mapName) { this->giftiFile->setDataArrayName(mapIndex, mapName); } /** * Get the metadata for the map at the given index * * @param mapIndex * Index of the map. * @return * Metadata for the map (const value). */ const GiftiMetaData* GiftiTypeFile::getMapMetaData(const int32_t mapIndex) const { return this->giftiFile->getDataArray(mapIndex)->getMetaData(); } /** * Get the metadata for the map at the given index * * @param mapIndex * Index of the map. * @return * Metadata for the map. */ GiftiMetaData* GiftiTypeFile::getMapMetaData(const int32_t mapIndex) { return this->giftiFile->getDataArray(mapIndex)->getMetaData(); } const FastStatistics* GiftiTypeFile::getMapFastStatistics(const int32_t mapIndex) { const GiftiDataArray* gda = this->giftiFile->getDataArray(mapIndex); return gda->getFastStatistics(); } const Histogram* GiftiTypeFile::getMapHistogram(const int32_t mapIndex) { const GiftiDataArray* gda = this->giftiFile->getDataArray(mapIndex); return gda->getHistogram(); } const Histogram* GiftiTypeFile::getMapHistogram(const int32_t mapIndex, const float mostPositiveValueInclusive, const float leastPositiveValueInclusive, const float leastNegativeValueInclusive, const float mostNegativeValueInclusive, const bool includeZeroValues) { const GiftiDataArray* gda = this->giftiFile->getDataArray(mapIndex); return gda->getHistogram(mostPositiveValueInclusive, leastPositiveValueInclusive, leastNegativeValueInclusive, mostNegativeValueInclusive, includeZeroValues); } /** * @return The estimated size of data after it is uncompressed * and loaded into RAM. A negative value indicates that the * file size cannot be computed. */ int64_t GiftiTypeFile::getDataSizeUncompressedInBytes() const { const int32_t numDataArrays = getNumberOfMaps(); int64_t dataSizeInBytes = 0; for (int32_t iMap = 0; iMap < numDataArrays; iMap++) { const GiftiDataArray* gda = this->giftiFile->getDataArray(iMap); dataSizeInBytes += gda->getDataSizeInBytes(); } return dataSizeInBytes; } /** * Get all data for a file that contains floats. If the file is very * large this method may take a large amount of time! * * @param dataOut * Output with all data for a float file. Empty if no data in file * or data is not float. */ void GiftiTypeFile::getFileDataFloat(std::vector& dataOut) const { int64_t dataSize = 0; /* * Get the size of the data */ const int64_t numberOfDataArrays = this->giftiFile->getNumberOfDataArrays(); for (int64_t i = 0; i < numberOfDataArrays; i++) { const GiftiDataArray* gda = this->giftiFile->getDataArray(i); if (gda->getDataType() == NiftiDataTypeEnum::NIFTI_TYPE_FLOAT32) { dataSize += gda->getTotalNumberOfElements(); } else { dataOut.clear(); return; } } if (dataSize <= 0) { dataOut.clear(); return; } dataOut.resize(dataSize); int64_t dataOffset = 0; /* * Copy the data. */ for (int64_t i = 0; i < numberOfDataArrays; i++) { const GiftiDataArray* gda = this->giftiFile->getDataArray(i); const int64_t arraySize = gda->getTotalNumberOfElements(); const float* arrayPointer = gda->getDataPointerFloat(); for (int64_t j = 0; j < arraySize; j++) { CaretAssertVectorIndex(dataOut, dataOffset); dataOut[dataOffset] = arrayPointer[j]; ++dataOffset; } } CaretAssert(dataOffset == static_cast(dataOut.size())); } /** * Get statistics describing the distribution of data * mapped with a color palette for all data within the file. * * @return * Fast statistics for data (will be NULL for data * not mapped using a palette). */ const FastStatistics* GiftiTypeFile::getFileFastStatistics() { if (m_fileFastStatistics == NULL) { std::vector fileData; getFileDataFloat(fileData); if ( ! fileData.empty()) { m_fileFastStatistics.grabNew(new FastStatistics()); m_fileFastStatistics->update(&fileData[0], fileData.size()); } } return m_fileFastStatistics; } /** * Get histogram describing the distribution of data * mapped with a color palette for all data within * the file. * * @return * Histogram for data (will be NULL for data * not mapped using a palette). */ const Histogram* GiftiTypeFile::getFileHistogram() { if (m_fileHistogram == NULL) { std::vector fileData; getFileDataFloat(fileData); if ( ! fileData.empty()) { m_fileHistogram.grabNew(new Histogram()); m_fileHistogram->update(&fileData[0], fileData.size()); } } return m_fileHistogram; } /** * Get histogram describing the distribution of data * mapped with a color palette for all data in the file * within the given range of values. * * @param mostPositiveValueInclusive * Values more positive than this value are excluded. * @param leastPositiveValueInclusive * Values less positive than this value are excluded. * @param leastNegativeValueInclusive * Values less negative than this value are excluded. * @param mostNegativeValueInclusive * Values more negative than this value are excluded. * @param includeZeroValues * If true zero values (very near zero) are included. * @return * Descriptive statistics for data (will be NULL for data * not mapped using a palette). */ const Histogram* GiftiTypeFile::getFileHistogram(const float mostPositiveValueInclusive, const float leastPositiveValueInclusive, const float leastNegativeValueInclusive, const float mostNegativeValueInclusive, const bool includeZeroValues) { 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(&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; } /** * @return Is the data in the file mapped to colors using * a palette. */ bool GiftiTypeFile::isMappedWithPalette() const { if (this->getDataFileType() == DataFileTypeEnum::METRIC) { return true; } return false; } /** * Get the palette normalization modes that are supported by the file. * * @param modesSupportedOut * Palette normalization modes supported by a file. Will be * empty for files that are not mapped with a palette. If there * is more than one suppported mode, the first mode in the * vector is assumed to be the default mode. */ void GiftiTypeFile::getPaletteNormalizationModesSupported(std::vector& modesSupportedOut) { modesSupportedOut.clear(); if (getDataFileType() == DataFileTypeEnum::METRIC) { modesSupportedOut.push_back(PaletteNormalizationModeEnum::NORMALIZATION_SELECTED_MAP_DATA); modesSupportedOut.push_back(PaletteNormalizationModeEnum::NORMALIZATION_ALL_MAP_DATA); } } /** * Get the palette color mapping for the map at the given index. * * @param mapIndex * Index of the map. * @return * Palette color mapping for the map (will be NULL for data * not mapped using a palette). */ PaletteColorMapping* GiftiTypeFile::getMapPaletteColorMapping(const int32_t mapIndex) { GiftiDataArray* gda = this->giftiFile->getDataArray(mapIndex); return gda->getPaletteColorMapping(); } /** * Get the palette color mapping for the map at the given index. * * @param mapIndex * Index of the map. * @return * Palette color mapping for the map (constant) (will be NULL for data * not mapped using a palette). */ const PaletteColorMapping* GiftiTypeFile::getMapPaletteColorMapping(const int32_t mapIndex) const { const GiftiDataArray* gda = this->giftiFile->getDataArray(mapIndex); return gda->getPaletteColorMapping(); } /** * @return Is the data in the file mapped to colors using * a label table. */ bool GiftiTypeFile::isMappedWithLabelTable() const { if (this->getDataFileType() == DataFileTypeEnum::LABEL) { return true; } return false; } /** * Get the label table for the map at the given index. * * @param mapIndex * Index of the map. * @return * Label table for the map (will be NULL for data * not mapped using a label table). */ GiftiLabelTable* GiftiTypeFile::getMapLabelTable(const int32_t /*mapIndex*/) { /* * Use file's label table since GIFTI uses one * label table for all data arrays. */ return this->giftiFile->getLabelTable(); } /** * Get the label table for the map at the given index. * * @param mapIndex * Index of the map. * @return * Label table for the map (constant) (will be NULL for data * not mapped using a label table). */ const GiftiLabelTable* GiftiTypeFile::getMapLabelTable(const int32_t /*mapIndex*/) const { /* * Use file's label table since GIFTI uses one * label table for all data arrays. */ return this->giftiFile->getLabelTable(); } /** * Get the unique ID (UUID) for the map at the given index. * * @param mapIndex * Index of the map. * @return * String containing UUID for the map. */ AString GiftiTypeFile::getMapUniqueID(const int32_t mapIndex) const { const GiftiMetaData* md = this->giftiFile->getDataArray(mapIndex)->getMetaData(); return md->getUniqueID(); } /** * Find the index of the map that uses the given unique ID (UUID). * * @param uniqueID * Unique ID (UUID) of the desired map. * @return * Index of the map using the given UUID. */ int32_t GiftiTypeFile::getMapIndexFromUniqueID(const AString& uniqueID) const { const int32_t numberOfArrays = this->giftiFile->getNumberOfDataArrays(); for (int32_t i = 0; i < numberOfArrays; i++) { if (this->getMapUniqueID(i) == uniqueID) { return i; } } return -1; } /** * Update coloring for a map. * * @param mapIndex * Index of map. * @param paletteFile * Palette file containing palettes. */ void GiftiTypeFile::updateScalarColoringForMap(const int32_t /*mapIndex*/, const PaletteFile* /*paletteFile*/) { /* no volumes in gifti */ } /** * Add information about the file to the data file information. * * @param dataFileInformation * Consolidates information about a data file. */ void GiftiTypeFile::addToDataFileContentInformation(DataFileContentInformation& dataFileInformation) { CaretMappableDataFile::addToDataFileContentInformation(dataFileInformation); dataFileInformation.addNameAndValue("Number of Vertices", getNumberOfNodes()); } connectome-workbench-1.2.3+git41-gc4c6c90/src/Files/GiftiTypeFile.h000066400000000000000000000171021300200146000244640ustar00rootroot00000000000000#ifndef __GIFTI_TYPE_FILE_H__ #define __GIFTI_TYPE_FILE_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 "CaretMappableDataFile.h" #include "StructureEnum.h" namespace caret { class GiftiFile; class PaletteColorMapping; /// Encapsulates a GiftiFile for use by specific types of GIFTI data files. class GiftiTypeFile : public CaretMappableDataFile { protected: GiftiTypeFile(const DataFileTypeEnum::Enum dataFileType); virtual ~GiftiTypeFile(); GiftiTypeFile(const GiftiTypeFile& s); GiftiTypeFile& operator=(const GiftiTypeFile&); virtual void addToDataFileContentInformation(DataFileContentInformation& dataFileInformation); /** * Validate the contents of the file after it * has been read such as correct number of * data arrays and proper data types/dimensions. * * @throws DataFileException * If the file is not valid. */ virtual void validateDataArraysAfterReading() = 0; void verifyDataArraysHaveSameNumberOfRows(const int32_t minimumSecondDimension, const int32_t maximumSecondDimension) const; public: virtual void clear(); virtual void clearModified(); virtual bool isModifiedExcludingPaletteColorMapping() const; virtual bool isEmpty() const; virtual void readFile(const AString& filename); virtual void writeFile(const AString& filename); virtual AString toString() const; virtual GiftiMetaData* getFileMetaData(); virtual const GiftiMetaData* getFileMetaData() const; virtual StructureEnum::Enum getStructure() const; virtual void setStructure(const StructureEnum::Enum structure); virtual void addMaps(const int32_t numberOfNodes, const int32_t numberOfMaps); /** @return Number of nodes in the file. */ virtual int32_t getNumberOfNodes() const = 0; /** @return Number of columns (data arrays) in the file. */ virtual int32_t getNumberOfColumns() const = 0; //virtual void setNumberOfNodesAndColumns(int32_t nodes, int32_t columns) = 0; virtual AString getColumnName(const int32_t columnIndex) const; int32_t getColumnIndexFromColumnName(const AString& columnName) const; virtual void setColumnName(const int32_t columnIndex, const AString& columnName); PaletteColorMapping* getPaletteColorMapping(const int32_t columnIndex); const PaletteColorMapping* getPaletteColorMapping(const int32_t columnIndex) const; virtual bool isSurfaceMappable() const; virtual bool isVolumeMappable() const; virtual int32_t getNumberOfMaps() const; virtual AString getMapName(const int32_t mapIndex) const; virtual void setMapName(const int32_t mapIndex, const AString& mapName); virtual const GiftiMetaData* getMapMetaData(const int32_t mapIndex) const; virtual GiftiMetaData* getMapMetaData(const int32_t mapIndex); void getFileDataFloat(std::vector& dataOut) const; virtual const FastStatistics* getMapFastStatistics(const int32_t mapIndex); virtual const Histogram* getMapHistogram(const int32_t mapIndex); virtual const Histogram* getMapHistogram(const int32_t mapIndex, const float mostPositiveValueInclusive, const float leastPositiveValueInclusive, const float leastNegativeValueInclusive, const float mostNegativeValueInclusive, const bool includeZeroValues); virtual int64_t getDataSizeUncompressedInBytes() const; virtual const FastStatistics* getFileFastStatistics(); virtual const Histogram* getFileHistogram(); virtual const Histogram* getFileHistogram(const float mostPositiveValueInclusive, const float leastPositiveValueInclusive, const float leastNegativeValueInclusive, const float mostNegativeValueInclusive, const bool includeZeroValues); virtual bool isMappedWithPalette() const; virtual void getPaletteNormalizationModesSupported(std::vector& modesSupportedOut); virtual PaletteColorMapping* getMapPaletteColorMapping(const int32_t mapIndex); virtual const PaletteColorMapping* getMapPaletteColorMapping(const int32_t mapIndex) const; virtual bool isMappedWithLabelTable() const; virtual GiftiLabelTable* getMapLabelTable(const int32_t mapIndex); virtual const GiftiLabelTable* getMapLabelTable(const int32_t mapIndex) const; virtual AString getMapUniqueID(const int32_t mapIndex) const; virtual int32_t getMapIndexFromUniqueID(const AString& uniqueID) const; virtual void updateScalarColoringForMap(const int32_t mapIndex, const PaletteFile* paletteFile); private: void copyHelperGiftiTypeFile(const GiftiTypeFile& gtf); void initializeMembersGiftiTypeFile(); /** Fast statistics used when statistics computed on all data in file */ CaretPointer m_fileFastStatistics; /** Histogram used when statistics computed on all data in file */ CaretPointer m_fileHistogram; /** Histogram with limited values used when statistics computed on all data in file */ CaretPointer m_fileHistorgramLimitedValues; float m_fileHistogramLimitedValuesMostPositiveValueInclusive; float m_fileHistogramLimitedValuesLeastPositiveValueInclusive; float m_fileHistogramLimitedValuesLeastNegativeValueInclusive; float m_fileHistogramLimitedValuesMostNegativeValueInclusive; bool m_fileHistogramLimitedValuesIncludeZeroValues; protected: GiftiFile* giftiFile; }; } // namespace #endif // __GIFTI_TYPE_FILE_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Files/GroupAndNameCheckStateEnum.cxx000066400000000000000000000235001300200146000274360ustar00rootroot00000000000000 /*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 __GROUP_AND_NAME_CHECK_STATE_ENUM_DECLARE__ #include "GroupAndNameCheckStateEnum.h" #undef __GROUP_AND_NAME_CHECK_STATE_ENUM_DECLARE__ #include "CaretAssert.h" using namespace caret; /** * \class caret::GroupAndNameCheckStateEnum * \brief * * */ /** * Constructor. * * @param enumValue * An enumerated value. * @param integerCode * Integer code for this enumerated value. * * @param name * Name of enumerated value. * * @param guiName * User-friendly name for use in user-interface. */ GroupAndNameCheckStateEnum::GroupAndNameCheckStateEnum(const Enum enumValue, const int32_t integerCode, const AString& name, const AString& guiName) { this->enumValue = enumValue; this->integerCode = integerCode; this->name = name; this->guiName = guiName; } /** * Destructor. */ GroupAndNameCheckStateEnum::~GroupAndNameCheckStateEnum() { } /** * Initialize the enumerated metadata. */ void GroupAndNameCheckStateEnum::initialize() { if (initializedFlag) { return; } initializedFlag = true; enumData.push_back(GroupAndNameCheckStateEnum(UNCHECKED, Qt::Unchecked, "UNCHECKED", "Unchecked")); enumData.push_back(GroupAndNameCheckStateEnum(PARTIALLY_CHECKED, Qt::PartiallyChecked, "PARTIALLY_CHECKED", "PartiallyChecked")); enumData.push_back(GroupAndNameCheckStateEnum(CHECKED, Qt::Checked, "CHECKED", "Checked")); } /** * 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 GroupAndNameCheckStateEnum* GroupAndNameCheckStateEnum::findData(const Enum enumValue) { if (initializedFlag == false) initialize(); size_t num = enumData.size(); for (size_t i = 0; i < num; i++) { const GroupAndNameCheckStateEnum* 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 GroupAndNameCheckStateEnum::toName(Enum enumValue) { if (initializedFlag == false) initialize(); const GroupAndNameCheckStateEnum* 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. */ GroupAndNameCheckStateEnum::Enum GroupAndNameCheckStateEnum::fromName(const AString& name, bool* isValidOut) { if (initializedFlag == false) initialize(); bool validFlag = false; Enum enumValue = UNCHECKED; for (std::vector::iterator iter = enumData.begin(); iter != enumData.end(); iter++) { const GroupAndNameCheckStateEnum& 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 GroupAndNameCheckStateEnum")); } return enumValue; } /** * Get a GUI string representation of the enumerated type. * @param enumValue * Enumerated value. * @return * String representing enumerated value. */ AString GroupAndNameCheckStateEnum::toGuiName(Enum enumValue) { if (initializedFlag == false) initialize(); const GroupAndNameCheckStateEnum* 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. */ GroupAndNameCheckStateEnum::Enum GroupAndNameCheckStateEnum::fromGuiName(const AString& guiName, bool* isValidOut) { if (initializedFlag == false) initialize(); bool validFlag = false; Enum enumValue = UNCHECKED; for (std::vector::iterator iter = enumData.begin(); iter != enumData.end(); iter++) { const GroupAndNameCheckStateEnum& 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 GroupAndNameCheckStateEnum")); } return enumValue; } /** * Get the integer code for a data type. * * @return * Integer code for data type. */ int32_t GroupAndNameCheckStateEnum::toIntegerCode(Enum enumValue) { if (initializedFlag == false) initialize(); const GroupAndNameCheckStateEnum* 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. */ GroupAndNameCheckStateEnum::Enum GroupAndNameCheckStateEnum::fromIntegerCode(const int32_t integerCode, bool* isValidOut) { if (initializedFlag == false) initialize(); bool validFlag = false; Enum enumValue = UNCHECKED; for (std::vector::iterator iter = enumData.begin(); iter != enumData.end(); iter++) { const GroupAndNameCheckStateEnum& 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 GroupAndNameCheckStateEnum")); } 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 GroupAndNameCheckStateEnum::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 GroupAndNameCheckStateEnum::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(GroupAndNameCheckStateEnum::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 GroupAndNameCheckStateEnum::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(GroupAndNameCheckStateEnum::toGuiName(iter->enumValue)); } if (isSorted) { std::sort(allGuiNames.begin(), allGuiNames.end()); } } connectome-workbench-1.2.3+git41-gc4c6c90/src/Files/GroupAndNameCheckStateEnum.h000066400000000000000000000063041300200146000270660ustar00rootroot00000000000000#ifndef __GROUP_AND_NAME_CHECK_STATE_ENUM_H__ #define __GROUP_AND_NAME_CHECK_STATE_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 GroupAndNameCheckStateEnum { public: /** * Enumerated values. */ enum Enum { /** Not selected */ UNCHECKED, /** Selected and at least one, but not all children selected */ PARTIALLY_CHECKED, /** Selected and all children (if any) are selected*/ CHECKED }; ~GroupAndNameCheckStateEnum(); 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: GroupAndNameCheckStateEnum(const Enum enumValue, const int32_t integerCode, const AString& name, const AString& guiName); static const GroupAndNameCheckStateEnum* 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; /** 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 __GROUP_AND_NAME_CHECK_STATE_ENUM_DECLARE__ std::vector GroupAndNameCheckStateEnum::enumData; bool GroupAndNameCheckStateEnum::initializedFlag = false; #endif // __GROUP_AND_NAME_CHECK_STATE_ENUM_DECLARE__ } // namespace #endif //__GROUP_AND_NAME_CHECK_STATE_ENUM_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Files/GroupAndNameHierarchyGroup.cxx000066400000000000000000000032511300200146000275270ustar00rootroot00000000000000 /*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 __CLASS_AND_NAME_HIERARCHY_GROUP_DECLARE__ #include "GroupAndNameHierarchyGroup.h" #undef __CLASS_AND_NAME_HIERARCHY_GROUP_DECLARE__ #include "CaretAssert.h" #include "GroupAndNameHierarchyName.h" using namespace caret; /** * \class caret::GroupAndNameHierarchyGroup * \brief Maintains selection of a class and name in each 'DisplayGroupEnum'. */ /** * Constructor. * @param name * The name. * @param idNumber * ID number assigned to the name. */ GroupAndNameHierarchyGroup::GroupAndNameHierarchyGroup(const AString& name, const int32_t idNumber) : GroupAndNameHierarchyItem(GroupAndNameHierarchyItem::ITEM_TYPE_GROUP, name, idNumber) { } /** * Destructor. */ GroupAndNameHierarchyGroup::~GroupAndNameHierarchyGroup() { } connectome-workbench-1.2.3+git41-gc4c6c90/src/Files/GroupAndNameHierarchyGroup.h000066400000000000000000000033711300200146000271570ustar00rootroot00000000000000#ifndef __CLASS_AND_NAME_HIERARCHY_GROUP__H_ #define __CLASS_AND_NAME_HIERARCHY_GROUP__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 "GroupAndNameHierarchyItem.h" namespace caret { class GroupAndNameHierarchyName; class GroupAndNameHierarchyGroup : public GroupAndNameHierarchyItem { public: GroupAndNameHierarchyGroup(const AString& name, const int32_t idNumber); virtual ~GroupAndNameHierarchyGroup(); // ADD_NEW_METHODS_HERE private: GroupAndNameHierarchyGroup(const GroupAndNameHierarchyGroup&); GroupAndNameHierarchyGroup& operator=(const GroupAndNameHierarchyGroup&); // ADD_NEW_MEMBERS_HERE }; #ifdef __CLASS_AND_NAME_HIERARCHY_GROUP_DECLARE__ // #endif // __CLASS_AND_NAME_HIERARCHY_GROUP_DECLARE__ } // namespace #endif //__CLASS_AND_NAME_HIERARCHY_GROUP__H_ connectome-workbench-1.2.3+git41-gc4c6c90/src/Files/GroupAndNameHierarchyItem.cxx000066400000000000000000000672621300200146000273450ustar00rootroot00000000000000 /*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 __GROUP_AND_NAME_HIERARCHY_ITEM_DECLARE__ #include "GroupAndNameHierarchyItem.h" #undef __GROUP_AND_NAME_HIERARCHY_ITEM_DECLARE__ #include "AStringNaturalComparison.h" #include "CaretAssert.h" #include "GroupAndNameHierarchyGroup.h" #include "GroupAndNameHierarchyName.h" #include "SceneAttributes.h" #include "SceneClass.h" #include "SceneClassArray.h" #include "SceneClassAssistant.h" #include using namespace caret; /** * \class caret::GroupAndNameHierarchyItem * \brief Base class for items in a hierarchy tree. */ /** * Constructor. * * @param itemType * Type of this item. * @param name * Name of the item. * @param idNumber * Id number for the item. */ GroupAndNameHierarchyItem::GroupAndNameHierarchyItem(const ItemType itemType, const AString& name, const int32_t idNumber) : CaretObject(), m_itemType(itemType), m_name(name), m_idNumber(idNumber), m_parent(0) { m_sceneAssistant = new SceneClassAssistant(); m_iconRGBA[0] = 0.0; m_iconRGBA[1] = 0.0; m_iconRGBA[2] = 0.0; m_iconRGBA[3] = 0.0; clearPrivate(); m_sceneAssistant->addTabIndexedBooleanArray("m_selectedInTab", m_selectedInTab); m_sceneAssistant->addTabIndexedBooleanArray("m_expandedStatusInTab", m_expandedStatusInTab); m_sceneAssistant->addArray("m_selectedInDisplayGroup", m_selectedInDisplayGroup, DisplayGroupEnum::NUMBER_OF_GROUPS, m_selectedInDisplayGroup[0]); m_sceneAssistant->addArray("m_expandedStatusInDisplayGroup", m_expandedStatusInDisplayGroup, DisplayGroupEnum::NUMBER_OF_GROUPS, m_expandedStatusInDisplayGroup[0]); } /** * Destructor. */ GroupAndNameHierarchyItem::~GroupAndNameHierarchyItem() { delete m_sceneAssistant; clearPrivate(); } /** * Clear the contents of this class selector. */ void GroupAndNameHierarchyItem::clear() { clearPrivate(); } /** * Clear the contents of this class selector. */ void GroupAndNameHierarchyItem::clearPrivate() { for (std::vector::iterator iter = m_children.begin(); iter != m_children.end(); iter++) { GroupAndNameHierarchyItem* item = *iter; item->m_parent = NULL; delete item; } m_children.clear(); m_childrenNameIdMap.clear(); for (int32_t i = 0; i < DisplayGroupEnum::NUMBER_OF_GROUPS; i++) { m_selectedInDisplayGroup[i] = true; } for (int32_t i = 0; i < BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS; i++) { m_selectedInTab[i] = true; } bool defaultExpandStatus = false; switch (m_itemType) { case ITEM_TYPE_NAME: break; case ITEM_TYPE_GROUP: break; case ITEM_TYPE_MODEL: defaultExpandStatus = true; break; } for (int32_t i = 0; i < DisplayGroupEnum::NUMBER_OF_GROUPS; i++) { m_expandedStatusInDisplayGroup[i] = defaultExpandStatus; } for (int32_t i = 0; i < BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS; i++) { m_expandedStatusInTab[i] = defaultExpandStatus; } m_counter = 0; } /** * @return The type of the item. */ GroupAndNameHierarchyItem::ItemType GroupAndNameHierarchyItem::getItemType() const { return m_itemType; } /** * @return The name of the item. */ AString GroupAndNameHierarchyItem::getName() const { return m_name; } /** * Set the name of the item. This should only be used * to set the name of an item without a parent as names * are used when creating a hierarchy. * * @param name * Name of item. */ void GroupAndNameHierarchyItem::setName(const AString& name) { m_name = name; } /** * @return The parent of this item. */ GroupAndNameHierarchyItem* GroupAndNameHierarchyItem::getParent() { return m_parent; } /** * @return The parent of this item. */ const GroupAndNameHierarchyItem* GroupAndNameHierarchyItem::getParent() const { return m_parent; } /** * @return The ancestors of this item. */ std::vector GroupAndNameHierarchyItem::getAncestors() const { std::vector ancestors; if (m_parent != NULL) { ancestors.push_back(m_parent); std::vector parentsAncestors = m_parent->getAncestors(); ancestors.insert(ancestors.end(), parentsAncestors.begin(), parentsAncestors.end()); } return ancestors; } /** * @return The descendants of this item. */ std::vector GroupAndNameHierarchyItem::getDescendants() const { std::vector descendants; if (m_parent != NULL) { for (std::vector::const_iterator iter = m_children.begin(); iter != m_children.end(); iter++) { GroupAndNameHierarchyItem* child = *iter; descendants.push_back(child); std::vector childDescendants = child->getDescendants(); descendants.insert(descendants.end(), childDescendants.begin(), childDescendants.end()); } } return descendants; } /** * @return The children of this item. */ std::vector GroupAndNameHierarchyItem::getChildren() const { return m_children; } static bool lessName(const GroupAndNameHierarchyItem* itemOne, const GroupAndNameHierarchyItem* itemTwo) { const int32_t result = AStringNaturalComparison::compare(itemOne->getName(), itemTwo->getName()); if (result < 0) { return true; } return false; // return (itemOne->getName() < itemTwo->getName()); } /** * Sort the descendants by name */ void GroupAndNameHierarchyItem::sortDescendantsByName() { std::sort(m_children.begin(), m_children.end(), lessName); for (std::vector::iterator iter = m_children.begin(); iter != m_children.end(); iter++) { GroupAndNameHierarchyItem* child = *iter; child->sortDescendantsByName(); } } /** * Get the child with the given name and ID number. * * @param name * Name of child. * @return Child with the given name and ID number or NULL if no * child with the name and ID number. */ GroupAndNameHierarchyItem* GroupAndNameHierarchyItem::getChildWithNameAndIdNumber(const AString& name, const int32_t idNumber) { ChildMapKey childMapKey(idNumber, name); std::map::iterator iter; iter = m_childrenNameIdMap.find(childMapKey); if (iter != m_childrenNameIdMap.end()) { GroupAndNameHierarchyItem* item = iter->second; return item; } return NULL; } /** * Add the given child to this item. * * @param child * Child to add. */ void GroupAndNameHierarchyItem::addChild(GroupAndNameHierarchyItem* child) { CaretAssertMessage((getChildWithNameAndIdNumber(child->getName(), child->getIdNumber()) != NULL), ("Child with name=" + child->getName() + ", idNumber=" + AString::number(child->getIdNumber()) + " already is in item named=" + getName())); addChildPrivate(child); } /** * Add the given child to this item. * * @param child * Child to add. */ void GroupAndNameHierarchyItem::addChildPrivate(GroupAndNameHierarchyItem* child) { child->m_parent = this; m_children.push_back(child); ChildMapKey childMapKey(child->getIdNumber(), child->getName()); m_childrenNameIdMap.insert(std::make_pair(childMapKey, child)); } /** * Add a child with the given name and id number. * * @param itemType * Type of the item. * @param name * Name of item. * @param idNumber * ID Number for item. * @return If a child with the given name and id number exists, * it is returned. Otherwise, a new child with the given * name and id number is created and returned. */ GroupAndNameHierarchyItem* GroupAndNameHierarchyItem::addChild(const ItemType itemType, const AString& name, const int32_t idNumber) { GroupAndNameHierarchyItem* child = getChildWithNameAndIdNumber(name, idNumber); if (child != NULL) { child->incrementCounter(); return child; } switch (itemType) { case ITEM_TYPE_GROUP: child = new GroupAndNameHierarchyGroup(name, idNumber); break; case ITEM_TYPE_NAME: child = new GroupAndNameHierarchyName(name, idNumber); break; case ITEM_TYPE_MODEL: CaretAssertMessage(0, "Model should never be a child"); break; } child->incrementCounter(); addChildPrivate(child); return child; } /** * Remove the given child from this item. The child IS NOT deleted. * * @param child * Child to remove. */ void GroupAndNameHierarchyItem::removeChild(GroupAndNameHierarchyItem* child) { std::vector::iterator iter = std::find(m_children.begin(), m_children.end(), child); if (iter != m_children.end()) { child->m_parent = NULL; m_children.erase(iter); } for (std::map::iterator mapIter = m_childrenNameIdMap.begin(); mapIter != m_childrenNameIdMap.end(); mapIter++) { GroupAndNameHierarchyItem* item = mapIter->second; if (item == child) { m_childrenNameIdMap.erase(mapIter); break; } } } /** * Is this item selected? * * @param displayGroup * The display group in which the item is controlled/viewed. * @param tabIndex * Index of browser tab in which item is controlled/viewed. * @return * True if item is selected, else false. */ bool GroupAndNameHierarchyItem::isSelected(const DisplayGroupEnum::Enum displayGroup, const int32_t tabIndex) const { const int32_t displayIndex = (int32_t)displayGroup; CaretAssertArrayIndex(m_selectedInDisplayGroup, DisplayGroupEnum::NUMBER_OF_GROUPS, displayIndex); if (displayGroup == DisplayGroupEnum::DISPLAY_GROUP_TAB) { CaretAssertArrayIndex(m_selectedInTab, BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS, tabIndex); return m_selectedInTab[tabIndex]; } return m_selectedInDisplayGroup[displayIndex]; } /** * Get the "check state" of an item. * * @param displayGroup * The display group in which the item is controlled/viewed. * @param tabIndex * Index of browser tab in which item is controlled/viewed. * @return * CHECKED if this item and ALL of its children are selected. * PARTIALLY_CHECKED if this item is selected and any of its * children, but not all of its children, are selected. * UNCHECKED if this item is not selected. */ GroupAndNameCheckStateEnum::Enum GroupAndNameHierarchyItem::getCheckState(const DisplayGroupEnum::Enum displayGroup, const int32_t tabIndex) const { if (isSelected(displayGroup, tabIndex)) { int64_t numChildren = 0; int64_t numChildrenChecked = 0; for (std::vector::const_iterator iter = m_children.begin(); iter != m_children.end(); iter++) { numChildren++; GroupAndNameHierarchyItem* child = *iter; const GroupAndNameCheckStateEnum::Enum childStatus = child->getCheckState(displayGroup, tabIndex); switch (childStatus) { case GroupAndNameCheckStateEnum::CHECKED: numChildrenChecked++; break; case GroupAndNameCheckStateEnum::PARTIALLY_CHECKED: return GroupAndNameCheckStateEnum::PARTIALLY_CHECKED; break; case GroupAndNameCheckStateEnum::UNCHECKED: break; } } if (numChildrenChecked == numChildren) { return GroupAndNameCheckStateEnum::CHECKED; } else if (numChildrenChecked > 0) { return GroupAndNameCheckStateEnum::PARTIALLY_CHECKED; } } return GroupAndNameCheckStateEnum::UNCHECKED; } /** * Set the selected status of this item only. It does not alter * the status of ancestors and children. * * @param displayGroup * The display group in which the item is controlled/viewed. * @param tabIndex * Index of browser tab in which item is controlled/viewed. * @param status * True if item is selected, else false. */ void GroupAndNameHierarchyItem::setSelected(const DisplayGroupEnum::Enum displayGroup, const int32_t tabIndex, const bool status) { const int32_t displayIndex = (int32_t)displayGroup; CaretAssertArrayIndex(m_selectedInDisplayGroup, DisplayGroupEnum::NUMBER_OF_GROUPS, displayIndex); if (displayGroup == DisplayGroupEnum::DISPLAY_GROUP_TAB) { CaretAssertArrayIndex(m_selectedInTab, BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS, tabIndex); m_selectedInTab[tabIndex] = status; } else { m_selectedInDisplayGroup[displayIndex] = status; } } /** * Set the selected status for all of this item's descendants. * * @param displayGroup * The display group in which the item is controlled/viewed. * @param tabIndex * Index of browser tab in which item is controlled/viewed. * @param status * True if item is selected, else false. */ void GroupAndNameHierarchyItem::setDescendantsSelected(const DisplayGroupEnum::Enum displayGroup, const int32_t tabIndex, const bool status) { for (std::vector::const_iterator iter = m_children.begin(); iter != m_children.end(); iter++) { GroupAndNameHierarchyItem* child = *iter; child->setSelected(displayGroup, tabIndex, status); child->setDescendantsSelected(displayGroup, tabIndex, status); } } /** * Set the selected status of this item's ancestor's (parent, * its parent, etc). * * @param displayGroup * The display group in which the item is controlled/viewed. * @param tabIndex * Index of browser tab in which item is controlled/viewed. * @param status * True if item is selected, else false. */ void GroupAndNameHierarchyItem::setAncestorsSelected(const DisplayGroupEnum::Enum displayGroup, const int32_t tabIndex, const bool status) { if (m_parent != NULL) { m_parent->setSelected(displayGroup, tabIndex, status); m_parent->setAncestorsSelected(displayGroup, tabIndex, status); } } /** * Set the selected status of this item, its ancestors, and all * of its children. * * @param displayGroup * The display group in which the item is controlled/viewed. * @param tabIndex * Index of browser tab in which item is controlled/viewed. * @param status * True if item is selected, else false. */ void GroupAndNameHierarchyItem::setSelfAncestorsAndDescendantsSelected(const DisplayGroupEnum::Enum displayGroup, const int32_t tabIndex, const bool status) { setSelected(displayGroup, tabIndex, status); setAncestorsSelected(displayGroup, tabIndex, status); setDescendantsSelected(displayGroup, tabIndex, status); } /** * Get the RGBA color for an Icon that is displayed * in the selection control for this item. * * @return * Pointer to the Red, Green, Blue, and Alpha * color components for this item's icon. * If no icon is to be displayed, the alpha component is zero. */ const float* GroupAndNameHierarchyItem::getIconColorRGBA() const { return m_iconRGBA; } /** * Set the RGBA color components for an icon displayed in the * selection control for this item. * * @param rgba * The Red, Green, Blue, and Alpha color components. * If no icon is to be displayed, the alpha component is zero. */ void GroupAndNameHierarchyItem::setIconColorRGBA(const float rgba[4]) { m_iconRGBA[0] = rgba[0]; m_iconRGBA[1] = rgba[1]; m_iconRGBA[2] = rgba[2]; m_iconRGBA[3] = rgba[3]; } /** * Is this item expanded to display its children in the * selection controls? * * @param displayGroup * The display group in which the item is controlled/viewed. * @param tabIndex * Index of browser tab in which item is controlled/viewed. * @return * True if children should be visible, else false. */ bool GroupAndNameHierarchyItem::isExpandedToDisplayChildren(const DisplayGroupEnum::Enum displayGroup, const int32_t tabIndex) const { const int32_t displayIndex = (int32_t)displayGroup; CaretAssertArrayIndex(m_expandedStatusInDisplayGroup, DisplayGroupEnum::NUMBER_OF_GROUPS, displayIndex); if (displayGroup == DisplayGroupEnum::DISPLAY_GROUP_TAB) { CaretAssertArrayIndex(m_expandedStatusInTab, BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS, tabIndex); return m_expandedStatusInTab[tabIndex]; } return m_expandedStatusInDisplayGroup[displayIndex]; } /** * Set this item expanded to display its children in the * selection controls. * * @param displayGroup * The display group in which the item is controlled/viewed. * @param tabIndex * Index of browser tab in which item is controlled/viewed. * @param expanded * True if children should be visible, else false. */ void GroupAndNameHierarchyItem::setExpandedToDisplayChildren(const DisplayGroupEnum::Enum displayGroup, const int32_t tabIndex, const bool expanded) { const int32_t displayIndex = (int32_t)displayGroup; CaretAssertArrayIndex(m_expandedStatusInDisplayGroup, DisplayGroupEnum::NUMBER_OF_GROUPS, displayIndex); if (displayGroup == DisplayGroupEnum::DISPLAY_GROUP_TAB) { CaretAssertArrayIndex(m_expandedStatusInTab, BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS, tabIndex); m_expandedStatusInTab[tabIndex] = expanded; } else { m_expandedStatusInDisplayGroup[displayIndex] = expanded; } } /** * Copy the selections from one tab to another tab. * Also copies selections in all descendants. * * @param sourceTabIndex * Index of source tab (copy "from") * @param targetTabIndex * Index of target tab (copy "to") */ void GroupAndNameHierarchyItem::copySelections(const int32_t sourceTabIndex, const int32_t targetTabIndex) { m_selectedInTab[targetTabIndex] = m_selectedInTab[sourceTabIndex]; m_expandedStatusInTab[targetTabIndex] = m_expandedStatusInTab[sourceTabIndex]; for (std::vector::const_iterator iter = m_children.begin(); iter != m_children.end(); iter++) { GroupAndNameHierarchyItem* child = *iter; child->copySelections(sourceTabIndex, targetTabIndex); } } /** * @return The Id Number. */ int32_t GroupAndNameHierarchyItem::getIdNumber() const { return m_idNumber; } /** * Clear the counter. Also clears counters in descendants. */ void GroupAndNameHierarchyItem::clearCounters() { m_counter = 0; for (std::vector::const_iterator iter = m_children.begin(); iter != m_children.end(); iter++) { GroupAndNameHierarchyItem* child = *iter; child->clearCounters(); } } /** * Increment the counter. */ void GroupAndNameHierarchyItem::incrementCounter() { m_counter++; } /** * @return The value of the counter. */ int32_t GroupAndNameHierarchyItem::getCounter() const { return m_counter; } /** * Remove all descendants with counters equal to zero. */ void GroupAndNameHierarchyItem::removeDescendantsWithCountersEqualToZeros() { /* * Process all descendants. */ for (std::vector::const_iterator iter = m_children.begin(); iter != m_children.end(); iter++) { GroupAndNameHierarchyItem* child = *iter; child->removeDescendantsWithCountersEqualToZeros(); } /* * Find children with zero counters indicating item not used. */ std::vector childrenWithCountGreaterThanZero; std::vector childrenWithCountEqualToZero; for (std::vector::const_iterator iter = m_children.begin(); iter != m_children.end(); iter++) { GroupAndNameHierarchyItem* child = *iter; if (child->getCounter() > 0) { childrenWithCountGreaterThanZero.push_back(child); } else { childrenWithCountEqualToZero.push_back(child); } } /* * If no children with zero counter, return. */ if (childrenWithCountEqualToZero.empty()) { return; } /* * Remove children with zero counters */ for (std::vector::iterator iter = childrenWithCountEqualToZero.begin(); iter != childrenWithCountEqualToZero.end(); iter++) { GroupAndNameHierarchyItem* item = *iter; item->m_parent = NULL; delete item; } /* * Clear children */ m_children.clear(); m_childrenNameIdMap.clear(); /* * Read children so maps properly created. */ for (std::vector::iterator iter = childrenWithCountGreaterThanZero.begin(); iter != childrenWithCountGreaterThanZero.end(); iter++) { addChildPrivate(*iter); } } /** * Get a description of this object's content. * @return String describing this object's content. */ AString GroupAndNameHierarchyItem::toString() const { AString info = (CaretObject::toString() + "\n name=" + m_name + ", type" + AString::number(m_itemType) + ", idNumber=" + AString::number(m_idNumber) + ", counter=" + AString::number(m_counter) + "\n"); AString childInfo; for (std::vector::const_iterator iter = m_children.begin(); iter != m_children.end(); iter++) { GroupAndNameHierarchyItem* child = *iter; childInfo += child->toString(); } if (childInfo.isEmpty() == false) { childInfo = childInfo.replace("\n", "\n "); } info += childInfo; return info; } /** * Create a scene for an instance of a class. * * @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 the class' instance. * * @return Pointer to SceneClass object representing the state of * this object. Under some circumstances a NULL pointer may be * returned. Caller will take ownership of returned object. */ SceneClass* GroupAndNameHierarchyItem::saveToScene(const SceneAttributes* sceneAttributes, const AString& instanceName) { SceneClass* sceneClass = new SceneClass(instanceName, "GroupAndNameHierarchyItem", 1); sceneClass->addString("m_name", m_name); m_sceneAssistant->saveMembers(sceneAttributes, sceneClass); for (std::vector::iterator iter = m_children.begin(); iter != m_children.end(); iter++) { GroupAndNameHierarchyItem* child = *iter; sceneClass->addClass(child->saveToScene(sceneAttributes, child->getName())); } return sceneClass; } /** * Restore the state of an instance of a class. * * @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 containing the state that was previously * saved and should be restored. */ void GroupAndNameHierarchyItem::restoreFromScene(const SceneAttributes* sceneAttributes, const SceneClass* sceneClass) { if (sceneClass == NULL) { return; } m_sceneAssistant->restoreMembers(sceneAttributes, sceneClass); for (std::vector::iterator iter = m_children.begin(); iter != m_children.end(); iter++) { GroupAndNameHierarchyItem* child = *iter; child->restoreFromScene(sceneAttributes, sceneClass->getClass(child->getName())); } } connectome-workbench-1.2.3+git41-gc4c6c90/src/Files/GroupAndNameHierarchyItem.h000066400000000000000000000213431300200146000267600ustar00rootroot00000000000000#ifndef __GROUP_AND_NAME_HIERARCHY_ITEM_H__ #define __GROUP_AND_NAME_HIERARCHY_ITEM_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 "BrainConstants.h" #include "CaretObject.h" #include "DisplayGroupEnum.h" #include "GroupAndNameCheckStateEnum.h" #include "SceneableInterface.h" namespace caret { class SceneClassAssistant; class GroupAndNameHierarchyItem : public CaretObject, SceneableInterface { public: /** * Type of item. */ enum ItemType { /** Model (eg: file) */ ITEM_TYPE_MODEL, /** Group (eg: class or map) */ ITEM_TYPE_GROUP, /** Name (eg: border, focus, or label) */ ITEM_TYPE_NAME }; protected: GroupAndNameHierarchyItem(const ItemType itemType, const AString& name, const int32_t idNumber); public: virtual ~GroupAndNameHierarchyItem(); virtual void clear(); ItemType getItemType() const; AString getName() const; GroupAndNameHierarchyItem* getParent(); const GroupAndNameHierarchyItem* getParent() const; std::vector getAncestors() const; std::vector getChildren() const; void sortDescendantsByName(); GroupAndNameHierarchyItem* getChildWithNameAndIdNumber(const AString& name, const int32_t idNumber); std::vector getDescendants() const; GroupAndNameHierarchyItem* addChild(const ItemType itemType, const AString& name, const int32_t idNumber); void addChild(GroupAndNameHierarchyItem* child); void removeChild(GroupAndNameHierarchyItem* child); bool isSelected(const DisplayGroupEnum::Enum displayGroup, const int32_t tabIndex) const; GroupAndNameCheckStateEnum::Enum getCheckState(const DisplayGroupEnum::Enum displayGroup, const int32_t tabIndex) const; void setSelected(const DisplayGroupEnum::Enum displayGroup, const int32_t tabIndex, const bool status); void setDescendantsSelected(const DisplayGroupEnum::Enum displayGroup, const int32_t tabIndex, const bool status); void setAncestorsSelected(const DisplayGroupEnum::Enum displayGroup, const int32_t tabIndex, const bool status); void setSelfAncestorsAndDescendantsSelected(const DisplayGroupEnum::Enum displayGroup, const int32_t tabIndex, const bool status); const float* getIconColorRGBA() const; void setIconColorRGBA(const float rgba[4]); bool isExpandedToDisplayChildren(const DisplayGroupEnum::Enum displayGroup, const int32_t tabIndex) const; void setExpandedToDisplayChildren(const DisplayGroupEnum::Enum displayGroup, const int32_t tabIndex, const bool expanded); virtual void copySelections(const int32_t sourceTabIndex, const int32_t targetTabIndex); int32_t getIdNumber() const; void clearCounters(); void incrementCounter(); int32_t getCounter() const; void removeDescendantsWithCountersEqualToZeros(); // 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); protected: void setName(const AString& name); private: GroupAndNameHierarchyItem(const GroupAndNameHierarchyItem&); GroupAndNameHierarchyItem& operator=(const GroupAndNameHierarchyItem&); void addChildPrivate(GroupAndNameHierarchyItem* child); void clearPrivate(); class ChildMapKey { public: ChildMapKey(const int32_t idNumber, const AString& name) : m_idNumber(idNumber), m_name(name) { } const int32_t m_idNumber; const AString m_name; bool operator==(const ChildMapKey& childMapKey) const { if (m_idNumber == childMapKey.m_idNumber) { if (m_name == childMapKey.m_name) { return true; } } return false; } bool operator<(const ChildMapKey& childMapKey) const { if (m_idNumber < childMapKey.m_idNumber) { return true; } else if (m_idNumber == childMapKey.m_idNumber) { if (m_name < childMapKey.m_name) { return true; } // else if (m_name == childMapKey.m_name) { // return 0; // } } // return 1; return false; } }; // bool operator<(const ChildMapKey& a, // const ChildMapKey& b) { // if (a.m_idNumber < b.m_idNumber) { // return true; // } // else if (a.m_idNumber == b.m_idNumber) { // if (a.m_name < b.m_name) { // return true; // } // } // return false; // } /** Type of item */ const ItemType m_itemType; /** Name of this item */ AString m_name; /** ID Number */ const int32_t m_idNumber; /** Parent of this item */ GroupAndNameHierarchyItem* m_parent; /** Children of this item */ std::vector m_children; /** For fast access to children by name and id number of child */ std::map m_childrenNameIdMap; /** Selection for each display group */ bool m_selectedInDisplayGroup[DisplayGroupEnum::NUMBER_OF_GROUPS]; /** Selection for each tab */ bool m_selectedInTab[BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS]; /** Color for icon, valid when (iconRGBA[3] > 0.0) */ float m_iconRGBA[4]; /** Expanded (collapsed) status in display group */ bool m_expandedStatusInDisplayGroup[DisplayGroupEnum::NUMBER_OF_GROUPS]; /** Expanded (collapsed) status in tab */ bool m_expandedStatusInTab[BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS]; /** Counter for tracking usage of item */ int32_t m_counter; /** Assists with scenes */ SceneClassAssistant* m_sceneAssistant; // ADD_NEW_MEMBERS_HERE }; #ifdef __GROUP_AND_NAME_HIERARCHY_ITEM_DECLARE__ #endif // __GROUP_AND_NAME_HIERARCHY_ITEM_DECLARE__ } // namespace #endif //__GROUP_AND_NAME_HIERARCHY_ITEM_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Files/GroupAndNameHierarchyModel.cxx000066400000000000000000001071111300200146000274730ustar00rootroot00000000000000 /*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 __CLASS_AND_NAME_HIERARCHY_MODEL_DECLARE__ #include "GroupAndNameHierarchyModel.h" #undef __CLASS_AND_NAME_HIERARCHY_MODEL_DECLARE__ #include "Border.h" #include "BorderFile.h" #include "CaretAssert.h" #include "CaretLogger.h" #include "CiftiMappableDataFile.h" #include "GroupAndNameHierarchyGroup.h" #include "GroupAndNameHierarchyName.h" #include "FociFile.h" #include "Focus.h" #include "GiftiLabel.h" #include "GiftiLabelTable.h" #include "LabelFile.h" #include "VolumeFile.h" using namespace caret; /** * \class caret::GroupAndNameHierarchySelection * \brief Maintains a 'group' and 'name' hierarchy for selection. * * Each group maps to one or more names. Identical names * may be children of more than one group. However, each * group holds its child names independently from the children * of all other groups. * * Note: Two containers are used to hold the data. One, * a vector, maps group keys to groups and a second, a map * group names to groups. * use 'maps'. A map is typically constructed using a * balanced tree so retrieval can be fast. However, adding * or removing items may be slow due to tree rebalancing. * As a result, unused groups and children names are only * removed when the entire instance is cleared (via clear()) * or by calling removeUnusedNamesAndGroupes(). * * Each group or name supports a 'count'. If the for a group * or name is zero, that indicates that the item is unused. * * Attributes are available for every tab and also a * few 'display groups'. A number of methods in this group accept * both display group and tab index parameters. When the display * group is set to 'Tab', the tab index is used meaning that the * attribute requeted/sent is for use with a specifc tab. For an * other display group value, the attribute is for a display group * and the tab index is ignored. */ /** * Constructor. */ GroupAndNameHierarchyModel::GroupAndNameHierarchyModel() : GroupAndNameHierarchyItem(GroupAndNameHierarchyItem::ITEM_TYPE_MODEL, "", -1) { this->clearModelPrivate(); } /** * Destructor. */ GroupAndNameHierarchyModel::~GroupAndNameHierarchyModel() { this->clearModelPrivate(); } /** * Clear the group/name hierarchy. */ void GroupAndNameHierarchyModel::clear() { GroupAndNameHierarchyItem::clear(); clearModelPrivate(); } /** * Clear the group/name hierarchy. */ void GroupAndNameHierarchyModel::clearModelPrivate() { setUserInterfaceUpdateNeeded(); } /** * Set the selected status for self and all children. * @param status * The selection status. */ void GroupAndNameHierarchyModel::setAllSelected(const bool status) { for (int32_t i = 0; i < BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS; i++) { setAllSelected(DisplayGroupEnum::DISPLAY_GROUP_TAB, i, status); } for (int32_t i = 0; i < DisplayGroupEnum::NUMBER_OF_GROUPS; i++) { const DisplayGroupEnum::Enum displayGroup = DisplayGroupEnum::fromIntegerCode(i, NULL); if (displayGroup != DisplayGroupEnum::DISPLAY_GROUP_TAB) { setAllSelected(displayGroup, -1, status); } } } /** * Set the selection status of this hierarchy model for the display group/tab. * @param displayGroup * Display group selected. * @param tabIndex * Index of tab used when displayGroup is DisplayGroupEnum::DISPLAY_GROUP_TAB. * @param selectionStatus * New selection status. */ void GroupAndNameHierarchyModel::setAllSelected(const DisplayGroupEnum::Enum displayGroup, const int32_t tabIndex, const bool selectionStatus) { setSelfAncestorsAndDescendantsSelected(displayGroup, tabIndex, selectionStatus); } /** * Update this group hierarchy with the border names * and classes. * * @param borderFile * The border file from which classes and names are from. * @parm forceUpdate * If true, force an update. */ void GroupAndNameHierarchyModel::update(BorderFile* borderFile, const bool forceUpdate) { bool needToGenerateKeys = forceUpdate; setName(borderFile->getFileNameNoPath()); const int32_t numBorders = borderFile->getNumberOfBorders(); if (needToGenerateKeys == false) { for (int32_t i = 0; i < numBorders; i++) { const Border* border = borderFile->getBorder(i); if (border->getGroupNameSelectionItem() == NULL) { needToGenerateKeys = true; } } } /* * ID for groups and names is not used */ const int32_t ID_NOT_USED = 0; if (needToGenerateKeys) { /* * Clear the counters */ clearCounters(); /* * Names for missing group names or border names. */ const AString missingGroupName = "NoGroup"; const AString missingBorderName = "NoName"; /* * For icons */ const GiftiLabelTable* classLabelTable = borderFile->getClassColorTable(); const GiftiLabelTable* nameLabelTable = borderFile->getNameColorTable(); const float rgbaBlack[4] = { 0.0, 0.0, 0.0, 1.0 }; /* * Update with all borders. */ for (int32_t i = 0; i < numBorders; i++) { Border* border = borderFile->getBorder(i); /* * Get the group. If it is empty, use the default name. */ AString theGroupName = border->getClassName(); if (theGroupName.isEmpty()) { theGroupName = missingGroupName; } /* * Get the name. */ AString name = border->getName(); if (name.isEmpty()) { name = missingBorderName; } /* * Class */ GroupAndNameHierarchyItem* groupItem = addChild(GroupAndNameHierarchyItem::ITEM_TYPE_GROUP, theGroupName, ID_NOT_USED); CaretAssert(groupItem); const GiftiLabel* groupLabel = classLabelTable->getLabelBestMatching(theGroupName); if (groupLabel != NULL) { float tempcolor[4]; groupLabel->getColor(tempcolor); groupItem->setIconColorRGBA(tempcolor); } else { groupItem->setIconColorRGBA(rgbaBlack); } /* * Name */ GroupAndNameHierarchyItem* nameItem = groupItem->addChild(GroupAndNameHierarchyItem::ITEM_TYPE_NAME, name, ID_NOT_USED); const GiftiLabel* nameLabel = nameLabelTable->getLabelBestMatching(name); if (nameLabel != NULL) { float tempcolor[4]; nameLabel->getColor(tempcolor); nameItem->setIconColorRGBA(tempcolor); } else { nameItem->setIconColorRGBA(rgbaBlack); } /* * Place the name selector into the border. */ border->setGroupNameSelectionItem(nameItem); } removeDescendantsWithCountersEqualToZeros(); sortDescendantsByName(); setUserInterfaceUpdateNeeded(); CaretLogFine("BORDER HIERARCHY:" + toString()); } } /** * Update this group hierarchy with the label names * and maps (as group). Groupes and names are done * differently for LabelFiles. Use the map index as * the group key and the label index as the name key. * * @param labelFile * The label file from which groups (map) and names are from. * @parm forceUpdate * If true, force an update. */ void GroupAndNameHierarchyModel::update(LabelFile* labelFile, const bool forceUpdate) { bool needToGenerateKeys = forceUpdate; setName(labelFile->getFileNameNoPath()); /* * Names for missing group names or foci names. */ const AString missingGroupName = "NoGroup"; const AString missingName = "NoName"; /* * The label table */ GiftiLabelTable* labelTable = labelFile->getLabelTable(); std::map labelKeysAndNames; if (needToGenerateKeys == false) { /* * Check to see if any group (map) names have changed. */ const std::vector groups = getChildren(); const int numGroups = static_cast(groups.size()); if (numGroups != labelFile->getNumberOfMaps()) { needToGenerateKeys = true; } else { for (int32_t i = 0; i < numGroups; i++) { AString mapName = labelFile->getMapName(i); if (mapName.isEmpty()) { mapName = missingGroupName; } if (groups[i]->getName() != mapName) { needToGenerateKeys = true; break; } } if (needToGenerateKeys == false) { labelTable->getKeysAndNames(labelKeysAndNames); if (m_previousLabelFileKeysAndNames.size() != labelKeysAndNames.size()) { needToGenerateKeys = true; } else { std::map::const_iterator prevIter = m_previousLabelFileKeysAndNames.begin(); for (std::map::const_iterator labelIter = labelKeysAndNames.begin(); labelIter != labelKeysAndNames.end(); labelIter++) { if (prevIter->first != labelIter->first) { needToGenerateKeys = true; break; } else if (prevIter->second != labelIter->second) { needToGenerateKeys = true; break; } prevIter++; } // if (std::equal(labelKeysAndNames.begin(), // labelKeysAndNames.end(), // m_previousLabelKeysAndNames) == false) { // needToGenerateKeys = true; // } } // if (labelTable->hasLabelsWithInvalidGroupNameHierarchy()) { // needToGenerateKeys = true; // } } } } if (needToGenerateKeys) { //const int32_t ID_NOT_USED = 0; /* * Save keys and names for comparison in next update test */ m_previousLabelFileKeysAndNames = labelKeysAndNames; if (m_previousLabelFileKeysAndNames.empty()) { labelTable->getKeysAndNames(m_previousLabelFileKeysAndNames); } /* * Clear everything */ this->clear(); /* * Update with labels from maps */ const int32_t numMaps = labelFile->getNumberOfMaps(); for (int32_t iMap = 0; iMap < numMaps; iMap++) { /* * Get the group. If it is empty, use the default name. */ AString theGroupName = labelFile->getMapName(iMap); if (theGroupName.isEmpty()) { theGroupName = missingGroupName; } /* * Find/create group */ GroupAndNameHierarchyItem* groupItem = addChild(GroupAndNameHierarchyItem::ITEM_TYPE_GROUP, theGroupName, iMap); CaretAssert(groupItem); /* * Get indices of labels used in this map */ std::vector labelKeys = labelFile->getUniqueLabelKeysUsedInMap(iMap); const int32_t numLabelKeys = static_cast(labelKeys.size()); for (int32_t iLabel = 0; iLabel < numLabelKeys; iLabel++) { const int32_t labelKey = labelKeys[iLabel]; GiftiLabel* label = labelTable->getLabel(labelKey); if (label == NULL) { continue; } AString labelName = label->getName(); if (labelName.isEmpty()) { labelName = missingName; } float rgba[4]; label->getColor(rgba); /* * Adding focus to class */ GroupAndNameHierarchyItem* nameItem = groupItem->addChild(GroupAndNameHierarchyItem::ITEM_TYPE_NAME, labelName, labelKey); nameItem->setIconColorRGBA(rgba); /* * Place the name selector into the label. */ label->setGroupNameSelectionItem(nameItem); } } /* * Sort names in each group */ std::vector groups = getChildren(); for (std::vector::iterator iter = groups.begin(); iter != groups.end(); iter++) { GroupAndNameHierarchyItem* nameItem = *iter; nameItem->sortDescendantsByName(); } setUserInterfaceUpdateNeeded(); CaretLogFine("LABEL HIERARCHY:" + toString()); } } /** * Update this group hierarchy with the label names * and groups. * * @param ciftiMappableDataFile * The cifti mappable data file from which classes and names are from. * @parm forceUpdate * If true, force an update. */ void GroupAndNameHierarchyModel::update(CiftiMappableDataFile* ciftiMappableDataFile, const bool forceUpdate) { /* * If it is not a label file, there is nothing to do. */ if (ciftiMappableDataFile->isMappedWithLabelTable() == false) { this->clear(); return; } /* * Names for missing group names or foci names. */ const AString missingGroupName = "NoGroup"; const AString missingName = "NoName"; bool needToGenerateKeys = forceUpdate; setName(ciftiMappableDataFile->getFileNameNoPath()); std::vector > labelMapKeysAndNames; if (needToGenerateKeys == false) { /* * Check to see if any group (map) names have changed. */ const std::vector groups = getChildren(); const int numGroups = static_cast(groups.size()); if (numGroups != ciftiMappableDataFile->getNumberOfMaps()) { /* * Number of maps has changed. */ needToGenerateKeys = true; } else { for (int32_t i = 0; i < numGroups; i++) { AString mapName = ciftiMappableDataFile->getMapName(i); if (mapName.isEmpty()) { mapName = missingGroupName; } if (groups[i]->getName() != mapName) { needToGenerateKeys = true; break; } } if (needToGenerateKeys == false) { if (static_cast(m_previousCiftiLabelFileMapKeysAndNames.size()) != ciftiMappableDataFile->getNumberOfMaps()) { needToGenerateKeys = true; } } if (needToGenerateKeys == false) { for (int32_t i = 0; i < numGroups; i++) { const GiftiLabelTable* labelTable = ciftiMappableDataFile->getMapLabelTable(i); std::map labelKeysAndNames; labelTable->getKeysAndNames(labelKeysAndNames); labelMapKeysAndNames.push_back(labelKeysAndNames); } for (int32_t i = 0; i < numGroups; i++) { const std::map& labelKeysAndNames = labelMapKeysAndNames[i]; const std::map& previousLabelKeysAndNames = m_previousCiftiLabelFileMapKeysAndNames[i]; if (previousLabelKeysAndNames.size() != labelKeysAndNames.size()) { needToGenerateKeys = true; break; } else { std::map::const_iterator prevIter = previousLabelKeysAndNames.begin(); for (std::map::const_iterator labelIter = labelKeysAndNames.begin(); labelIter != labelKeysAndNames.end(); labelIter++) { if (prevIter->first != labelIter->first) { needToGenerateKeys = true; break; } else if (prevIter->second != labelIter->second) { needToGenerateKeys = true; break; } prevIter++; } } } } } } if (needToGenerateKeys) { //const int32_t ID_NOT_USED = 0; /* * Save keys and names for comparison in next update test */ const int numMaps = ciftiMappableDataFile->getNumberOfMaps(); m_previousCiftiLabelFileMapKeysAndNames = labelMapKeysAndNames; if (m_previousCiftiLabelFileMapKeysAndNames.empty()) { for (int32_t i = 0; i < numMaps; i++) { const GiftiLabelTable* labelTable = ciftiMappableDataFile->getMapLabelTable(i); std::map labelKeysAndNames; labelTable->getKeysAndNames(labelKeysAndNames); m_previousCiftiLabelFileMapKeysAndNames.push_back(labelKeysAndNames); } } /* * Clear everything */ this->clear(); /* * Update with labels from maps */ for (int32_t iMap = 0; iMap < numMaps; iMap++) { /* * The label table */ GiftiLabelTable* labelTable = ciftiMappableDataFile->getMapLabelTable(iMap); /* * Get the group. If it is empty, use the default name. */ AString theGroupName = ciftiMappableDataFile->getMapName(iMap); if (theGroupName.isEmpty()) { theGroupName = missingGroupName; } /* * Find/create group */ GroupAndNameHierarchyItem* groupItem = addChild(GroupAndNameHierarchyItem::ITEM_TYPE_GROUP, theGroupName, iMap); CaretAssert(groupItem); /* * Get indices of labels used in this map */ std::vector labelKeys = ciftiMappableDataFile->getUniqueLabelKeysUsedInMap(iMap); const int32_t numLabelKeys = static_cast(labelKeys.size()); for (int32_t iLabel = 0; iLabel < numLabelKeys; iLabel++) { const int32_t labelKey = labelKeys[iLabel]; GiftiLabel* label = labelTable->getLabel(labelKey); if (label == NULL) { continue; } AString labelName = label->getName(); if (labelName.isEmpty()) { labelName = missingName; } float rgba[4]; label->getColor(rgba); /* * Adding focus to class */ GroupAndNameHierarchyItem* nameItem = groupItem->addChild(GroupAndNameHierarchyItem::ITEM_TYPE_NAME, labelName, labelKey); nameItem->setIconColorRGBA(rgba); /* * Place the name selector into the label. */ label->setGroupNameSelectionItem(nameItem); } } /* * Sort names in each group */ std::vector groups = getChildren(); for (std::vector::iterator iter = groups.begin(); iter != groups.end(); iter++) { GroupAndNameHierarchyItem* nameItem = *iter; nameItem->sortDescendantsByName(); } setUserInterfaceUpdateNeeded(); CaretLogFine("LABEL HIERARCHY:" + toString()); } } /** * Update this group hierarchy with the label names * and groups. * * @param ciftiMappableDataFile * The volume file from which classes and names are from. * @parm forceUpdate * If true, force an update. */ void GroupAndNameHierarchyModel::update(VolumeFile* volumeFile, const bool forceUpdate) { /* * If it is not a label file, there is nothing to do. */ if (volumeFile->isMappedWithLabelTable() == false) { this->clear(); return; } /* * Names for missing group names or foci names. */ const AString missingGroupName = "NoGroup"; const AString missingName = "NoName"; bool needToGenerateKeys = forceUpdate; setName(volumeFile->getFileNameNoPath()); std::vector > labelMapKeysAndNames; if (needToGenerateKeys == false) { /* * Check to see if any group (map) names have changed. */ const std::vector groups = getChildren(); const int numGroups = static_cast(groups.size()); if (numGroups != volumeFile->getNumberOfMaps()) { /* * Number of maps has changed. */ needToGenerateKeys = true; } else { for (int32_t i = 0; i < numGroups; i++) { AString mapName = volumeFile->getMapName(i); if (mapName.isEmpty()) { mapName = missingGroupName; } if (groups[i]->getName() != mapName) { needToGenerateKeys = true; break; } } if (needToGenerateKeys == false) { if (static_cast(m_previousCiftiLabelFileMapKeysAndNames.size()) != volumeFile->getNumberOfMaps()) { needToGenerateKeys = true; } } if (needToGenerateKeys == false) { for (int32_t i = 0; i < numGroups; i++) { const GiftiLabelTable* labelTable = volumeFile->getMapLabelTable(i); std::map labelKeysAndNames; labelTable->getKeysAndNames(labelKeysAndNames); labelMapKeysAndNames.push_back(labelKeysAndNames); } for (int32_t i = 0; i < numGroups; i++) { const std::map& labelKeysAndNames = labelMapKeysAndNames[i]; const std::map& previousLabelKeysAndNames = m_previousCiftiLabelFileMapKeysAndNames[i]; if (previousLabelKeysAndNames.size() != labelKeysAndNames.size()) { needToGenerateKeys = true; break; } else { std::map::const_iterator prevIter = previousLabelKeysAndNames.begin(); for (std::map::const_iterator labelIter = labelKeysAndNames.begin(); labelIter != labelKeysAndNames.end(); labelIter++) { if (prevIter->first != labelIter->first) { needToGenerateKeys = true; break; } else if (prevIter->second != labelIter->second) { needToGenerateKeys = true; break; } prevIter++; } } } } } } if (needToGenerateKeys) { //const int32_t ID_NOT_USED = 0; /* * Save keys and names for comparison in next update test */ const int numMaps = volumeFile->getNumberOfMaps(); m_previousCiftiLabelFileMapKeysAndNames = labelMapKeysAndNames; if (m_previousCiftiLabelFileMapKeysAndNames.empty()) { for (int32_t i = 0; i < numMaps; i++) { const GiftiLabelTable* labelTable = volumeFile->getMapLabelTable(i); std::map labelKeysAndNames; labelTable->getKeysAndNames(labelKeysAndNames); m_previousCiftiLabelFileMapKeysAndNames.push_back(labelKeysAndNames); } } /* * Clear everything */ this->clear(); /* * Update with labels from maps */ for (int32_t iMap = 0; iMap < numMaps; iMap++) { /* * The label table */ GiftiLabelTable* labelTable = volumeFile->getMapLabelTable(iMap); /* * Get the group. If it is empty, use the default name. */ AString theGroupName = volumeFile->getMapName(iMap); if (theGroupName.isEmpty()) { theGroupName = missingGroupName; } /* * Find/create group */ GroupAndNameHierarchyItem* groupItem = addChild(GroupAndNameHierarchyItem::ITEM_TYPE_GROUP, theGroupName, iMap); CaretAssert(groupItem); /* * Get indices of labels used in this map */ std::vector labelKeys = volumeFile->getUniqueLabelKeysUsedInMap(iMap); const int32_t numLabelKeys = static_cast(labelKeys.size()); for (int32_t iLabel = 0; iLabel < numLabelKeys; iLabel++) { const int32_t labelKey = labelKeys[iLabel]; GiftiLabel* label = labelTable->getLabel(labelKey); if (label == NULL) { continue; } AString labelName = label->getName(); if (labelName.isEmpty()) { labelName = missingName; } float rgba[4]; label->getColor(rgba); /* * Adding focus to class */ GroupAndNameHierarchyItem* nameItem = groupItem->addChild(GroupAndNameHierarchyItem::ITEM_TYPE_NAME, labelName, labelKey); nameItem->setIconColorRGBA(rgba); /* * Place the name selector into the label. */ label->setGroupNameSelectionItem(nameItem); } } /* * Sort names in each group */ std::vector groups = getChildren(); for (std::vector::iterator iter = groups.begin(); iter != groups.end(); iter++) { GroupAndNameHierarchyItem* nameItem = *iter; nameItem->sortDescendantsByName(); } setUserInterfaceUpdateNeeded(); CaretLogFine("LABEL HIERARCHY:" + toString()); } } /** * Update this group hierarchy with the foci names * and groups. * * @param fociFile * The foci file from which classes and names are from. * @parm forceUpdate * If true, force an update. */ void GroupAndNameHierarchyModel::update(FociFile* fociFile, const bool forceUpdate) { bool needToGenerateKeys = forceUpdate; setName(fociFile->getFileNameNoPath()); const int32_t numFoci = fociFile->getNumberOfFoci(); if (needToGenerateKeys == false) { for (int32_t i = 0; i < numFoci; i++) { const Focus* focus = fociFile->getFocus(i); if (focus->getGroupNameSelectionItem() == NULL) { needToGenerateKeys = true; } } } /* * ID for groups and names is not used */ const int32_t ID_NOT_USED = 0; if (needToGenerateKeys) { /* * Names for missing group names or foci names. */ const AString missingGroupName = "NoGroup"; const AString missingName = "NoName"; /* * Reset the counts for all group and children names. */ clearCounters(); /* * For icons */ const GiftiLabelTable* classLabelTable = fociFile->getClassColorTable(); const GiftiLabelTable* nameLabelTable = fociFile->getNameColorTable(); const float rgbaBlack[4] = { 0.0, 0.0, 0.0, 1.0 }; /* * Update with all foci. */ for (int32_t i = 0; i < numFoci; i++) { Focus* focus = fociFile->getFocus(i); /* * Get the group. If it is empty, use the default name. */ AString theGroupName = focus->getClassName(); if (theGroupName.isEmpty()) { theGroupName = missingGroupName; } /* * Get the name. */ AString name = focus->getName(); if (name.isEmpty()) { name = missingName; } /* * Class */ GroupAndNameHierarchyItem* groupItem = addChild(GroupAndNameHierarchyItem::ITEM_TYPE_GROUP, theGroupName, ID_NOT_USED); CaretAssert(groupItem); CaretAssert(groupItem); const GiftiLabel* groupLabel = classLabelTable->getLabelBestMatching(theGroupName); if (groupLabel != NULL) { float rgba[4]; groupLabel->getColor(rgba); groupItem->setIconColorRGBA(rgba); } else { groupItem->setIconColorRGBA(rgbaBlack); } /* * Name */ GroupAndNameHierarchyItem* nameItem = groupItem->addChild(GroupAndNameHierarchyItem::ITEM_TYPE_NAME, name, ID_NOT_USED); const GiftiLabel* nameLabel = nameLabelTable->getLabelBestMatching(name); if (nameLabel != NULL) { float rgba[4]; nameLabel->getColor(rgba); nameItem->setIconColorRGBA(rgba); } else { nameItem->setIconColorRGBA(rgbaBlack); } /* * Place the name selector into the border. */ focus->setGroupNameSelectionItem(nameItem); } removeDescendantsWithCountersEqualToZeros(); sortDescendantsByName(); setUserInterfaceUpdateNeeded(); CaretLogFine("FOCI HIERARCHY:" + toString()); } } /** * Is a User-Interface needed in the given display group and tab? * This occurs when the hierarchy has changed (group and name). * After calling this method, the status for the display group/tab is cleared. * * @param displayGroup * Display group. * @param tabIndex * Index of tab. * @return True if update needed, else false. */ bool GroupAndNameHierarchyModel::needsUserInterfaceUpdate(const DisplayGroupEnum::Enum displayGroup, const int32_t tabIndex) const { bool needUpdate = false; const int32_t displayIndex = (int32_t)displayGroup; CaretAssertArrayIndex(this->expandedStatusInDisplayGroup, DisplayGroupEnum::NUMBER_OF_GROUPS, displayIndex); if (displayGroup == DisplayGroupEnum::DISPLAY_GROUP_TAB) { CaretAssertArrayIndex(this->expandedStatusInTab, BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS, tabIndex); needUpdate = m_updateNeededInTab[tabIndex]; m_updateNeededInTab[tabIndex] = false; } else { needUpdate = m_updateNeededInDisplayGroupAndTab[displayIndex][tabIndex]; m_updateNeededInDisplayGroupAndTab[displayIndex][tabIndex] = false; } return needUpdate; } /** * Set user interface updates needed. */ void GroupAndNameHierarchyModel::setUserInterfaceUpdateNeeded() { for (int32_t i = 0; i < DisplayGroupEnum::NUMBER_OF_GROUPS; i++) { for (int32_t j = 0; j < BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS; j++) { m_updateNeededInDisplayGroupAndTab[i][j] = true; } } for (int32_t i = 0; i < BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS; i++) { m_updateNeededInTab[i] = true; } } connectome-workbench-1.2.3+git41-gc4c6c90/src/Files/GroupAndNameHierarchyModel.h000066400000000000000000000073201300200146000271210ustar00rootroot00000000000000#ifndef __CLASS_AND_NAME_HIERARCHY_MODEL_H_ #define __CLASS_AND_NAME_HIERARCHY_MODEL_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 "GroupAndNameHierarchyItem.h" #include "GroupAndNameCheckStateEnum.h" namespace caret { class BorderFile; class CiftiMappableDataFile; class FociFile; class LabelFile; class VolumeFile; class GroupAndNameHierarchyModel : public GroupAndNameHierarchyItem { public: GroupAndNameHierarchyModel(); virtual ~GroupAndNameHierarchyModel(); virtual void clear(); bool isGroupValid(const int32_t groupKey) const; void setAllSelected(const bool status); void setAllSelected(const DisplayGroupEnum::Enum displayGroup, const int32_t tabIndex, const bool status); void update(BorderFile* borderFile, const bool forceUpdate); void update(FociFile* fociFile, const bool forceUpdate); void update(LabelFile* labelFile, const bool forceUpdate); void update(CiftiMappableDataFile* ciftiMappableDataFile, const bool forceUpdate); void update(VolumeFile* volumeFile, const bool forceUpdate); bool needsUserInterfaceUpdate(const DisplayGroupEnum::Enum displayGroup, const int32_t tabIndex) const; private: GroupAndNameHierarchyModel(const GroupAndNameHierarchyModel&); GroupAndNameHierarchyModel& operator=(const GroupAndNameHierarchyModel&); void clearModelPrivate(); void setUserInterfaceUpdateNeeded(); /** * Contains label keys and names from previous update with Label File. */ std::map m_previousLabelFileKeysAndNames; /** * Contains label keys and names from previous update with CIFTI label file. */ std::vector > m_previousCiftiLabelFileMapKeysAndNames; /** * Update needed status of DISPLAY GROUP in EACH TAB. * Used when user has set to a display group. * Indicates that an update is needed for the given display group in the given tab. */ mutable bool m_updateNeededInDisplayGroupAndTab[DisplayGroupEnum::NUMBER_OF_GROUPS][BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS]; /** * Update needed in TAB. */ mutable bool m_updateNeededInTab[BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS]; }; #ifdef __CLASS_AND_NAME_HIERARCHY_MODEL_DECLARE__ // #endif // __CLASS_AND_NAME_HIERARCHY_MODEL_DECLARE__ } // namespace #endif //__CLASS_AND_NAME_HIERARCHY_MODEL_H_ connectome-workbench-1.2.3+git41-gc4c6c90/src/Files/GroupAndNameHierarchyName.cxx000066400000000000000000000035621300200146000273200ustar00rootroot00000000000000 /*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 __CLASS_AND_NAME_HIERARCHY_NAME_DECLARE__ #include "GroupAndNameHierarchyName.h" #undef __CLASS_AND_NAME_HIERARCHY_NAME_DECLARE__ #include "CaretAssert.h" using namespace caret; /** * \class caret::GroupAndNameHierarchyName * \brief Maintains selection of a name in each 'DisplayGroupEnum'. * * Methods that operate on a boolean value are used to query and set * the selected status. A separate method is provided to query * the 'check state'. The 'check state' may be unchecked, checked, or * partially checked (some children checked but not all). */ /** * Constructor. * @param name * The name. * @param idNumber * ID number assigned to the name. */ GroupAndNameHierarchyName::GroupAndNameHierarchyName(const AString& name, const int32_t idNumber) : GroupAndNameHierarchyItem(GroupAndNameHierarchyItem::ITEM_TYPE_NAME, name, idNumber) { } /** * Destructor. */ GroupAndNameHierarchyName::~GroupAndNameHierarchyName() { } connectome-workbench-1.2.3+git41-gc4c6c90/src/Files/GroupAndNameHierarchyName.h000066400000000000000000000032651300200146000267450ustar00rootroot00000000000000#ifndef __CLASS_AND_NAME_HIERARCHY_NAME__H_ #define __CLASS_AND_NAME_HIERARCHY_NAME__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 "GroupAndNameHierarchyItem.h" namespace caret { class GroupAndNameHierarchyName : public GroupAndNameHierarchyItem { public: GroupAndNameHierarchyName(const AString& name, const int32_t idNumber); ~GroupAndNameHierarchyName(); // ADD_NEW_METHODS_HERE private: GroupAndNameHierarchyName(const GroupAndNameHierarchyName&); GroupAndNameHierarchyName& operator=(const GroupAndNameHierarchyName&); // ADD_NEW_MEMBERS_HERE }; #ifdef __CLASS_AND_NAME_HIERARCHY_NAME_DECLARE__ // #endif // __CLASS_AND_NAME_HIERARCHY_NAME_DECLARE__ } // namespace #endif //__CLASS_AND_NAME_HIERARCHY_NAME__H_ connectome-workbench-1.2.3+git41-gc4c6c90/src/Files/ImageCaptureDimensionsModeEnum.cxx000066400000000000000000000262061300200146000303710ustar00rootroot00000000000000 /*LICENSE_START*/ /* * Copyright (C) 2015 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 __IMAGE_CAPTURE_DIMENSIONS_MODE_ENUM_DECLARE__ #include "ImageCaptureDimensionsModeEnum.h" #undef __IMAGE_CAPTURE_DIMENSIONS_MODE_ENUM_DECLARE__ #include "CaretAssert.h" using namespace caret; /** * \class caret::ImageCaptureDimensionsModeEnum * \brief * * * * 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_imageCaptureDimensionsModeEnumComboBox; * * Declare a slot that is called when user changes selection * private slots: * void imageCaptureDimensionsModeEnumComboBoxItemActivated(); * * Implementation File (.cxx) * Include the header files * #include "EnumComboBoxTemplate.h" * #include "ImageCaptureDimensionsModeEnum.h" * * Instatiate: * m_imageCaptureDimensionsModeEnumComboBox = new EnumComboBoxTemplate(this); * m_imageCaptureDimensionsModeEnumComboBox->setup(); * * Get notified when the user changes the selection: * QObject::connect(m_imageCaptureDimensionsModeEnumComboBox, SIGNAL(itemActivated()), * this, SLOT(imageCaptureDimensionsModeEnumComboBoxItemActivated())); * * Update the selection: * m_imageCaptureDimensionsModeEnumComboBox->setSelectedItem(NEW_VALUE); * * Read the selection: * const ImageCaptureDimensionsModeEnum::Enum VARIABLE = m_imageCaptureDimensionsModeEnumComboBox->getSelectedItem(); * */ /** * Constructor. * * @param enumValue * An enumerated value. * @param name * Name of enumerated value. * * @param guiName * User-friendly name for use in user-interface. */ ImageCaptureDimensionsModeEnum::ImageCaptureDimensionsModeEnum(const Enum enumValue, const AString& name, const AString& guiName) { this->enumValue = enumValue; this->integerCode = integerCodeCounter++; this->name = name; this->guiName = guiName; } /** * Destructor. */ ImageCaptureDimensionsModeEnum::~ImageCaptureDimensionsModeEnum() { } /** * Initialize the enumerated metadata. */ void ImageCaptureDimensionsModeEnum::initialize() { if (initializedFlag) { return; } initializedFlag = true; enumData.push_back(ImageCaptureDimensionsModeEnum(IMAGE_CAPTURE_DIMENSIONS_MODE_CUSTOM, "IMAGE_CAPTURE_DIMENSIONS_MODE_CUSTOM", "")); enumData.push_back(ImageCaptureDimensionsModeEnum(IMAGE_CAPTURE_DIMENSIONS_MODE_WINDOW_SIZE, "IMAGE_CAPTURE_DIMENSIONS_MODE_WINDOW_SIZE", "")); } /** * 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 ImageCaptureDimensionsModeEnum* ImageCaptureDimensionsModeEnum::findData(const Enum enumValue) { if (initializedFlag == false) initialize(); size_t num = enumData.size(); for (size_t i = 0; i < num; i++) { const ImageCaptureDimensionsModeEnum* 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 ImageCaptureDimensionsModeEnum::toName(Enum enumValue) { if (initializedFlag == false) initialize(); const ImageCaptureDimensionsModeEnum* 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. */ ImageCaptureDimensionsModeEnum::Enum ImageCaptureDimensionsModeEnum::fromName(const AString& name, bool* isValidOut) { if (initializedFlag == false) initialize(); bool validFlag = false; Enum enumValue = ImageCaptureDimensionsModeEnum::enumData[0].enumValue; for (std::vector::iterator iter = enumData.begin(); iter != enumData.end(); iter++) { const ImageCaptureDimensionsModeEnum& 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 ImageCaptureDimensionsModeEnum")); } return enumValue; } /** * Get a GUI string representation of the enumerated type. * @param enumValue * Enumerated value. * @return * String representing enumerated value. */ AString ImageCaptureDimensionsModeEnum::toGuiName(Enum enumValue) { if (initializedFlag == false) initialize(); const ImageCaptureDimensionsModeEnum* 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. */ ImageCaptureDimensionsModeEnum::Enum ImageCaptureDimensionsModeEnum::fromGuiName(const AString& guiName, bool* isValidOut) { if (initializedFlag == false) initialize(); bool validFlag = false; Enum enumValue = ImageCaptureDimensionsModeEnum::enumData[0].enumValue; for (std::vector::iterator iter = enumData.begin(); iter != enumData.end(); iter++) { const ImageCaptureDimensionsModeEnum& 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 ImageCaptureDimensionsModeEnum")); } return enumValue; } /** * Get the integer code for a data type. * * @return * Integer code for data type. */ int32_t ImageCaptureDimensionsModeEnum::toIntegerCode(Enum enumValue) { if (initializedFlag == false) initialize(); const ImageCaptureDimensionsModeEnum* 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. */ ImageCaptureDimensionsModeEnum::Enum ImageCaptureDimensionsModeEnum::fromIntegerCode(const int32_t integerCode, bool* isValidOut) { if (initializedFlag == false) initialize(); bool validFlag = false; Enum enumValue = ImageCaptureDimensionsModeEnum::enumData[0].enumValue; for (std::vector::iterator iter = enumData.begin(); iter != enumData.end(); iter++) { const ImageCaptureDimensionsModeEnum& 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 ImageCaptureDimensionsModeEnum")); } 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 ImageCaptureDimensionsModeEnum::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 ImageCaptureDimensionsModeEnum::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(ImageCaptureDimensionsModeEnum::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 ImageCaptureDimensionsModeEnum::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(ImageCaptureDimensionsModeEnum::toGuiName(iter->enumValue)); } if (isSorted) { std::sort(allGuiNames.begin(), allGuiNames.end()); } } connectome-workbench-1.2.3+git41-gc4c6c90/src/Files/ImageCaptureDimensionsModeEnum.h000066400000000000000000000063701300200146000300160ustar00rootroot00000000000000#ifndef __IMAGE_CAPTURE_DIMENSIONS_MODE_ENUM_H__ #define __IMAGE_CAPTURE_DIMENSIONS_MODE_ENUM_H__ /*LICENSE_START*/ /* * Copyright (C) 2015 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 ImageCaptureDimensionsModeEnum { public: /** * Enumerated values. */ enum Enum { /** */ IMAGE_CAPTURE_DIMENSIONS_MODE_CUSTOM, /** */ IMAGE_CAPTURE_DIMENSIONS_MODE_WINDOW_SIZE }; ~ImageCaptureDimensionsModeEnum(); 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: ImageCaptureDimensionsModeEnum(const Enum enumValue, const AString& name, const AString& guiName); static const ImageCaptureDimensionsModeEnum* 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 __IMAGE_CAPTURE_DIMENSIONS_MODE_ENUM_DECLARE__ std::vector ImageCaptureDimensionsModeEnum::enumData; bool ImageCaptureDimensionsModeEnum::initializedFlag = false; int32_t ImageCaptureDimensionsModeEnum::integerCodeCounter = 0; #endif // __IMAGE_CAPTURE_DIMENSIONS_MODE_ENUM_DECLARE__ } // namespace #endif //__IMAGE_CAPTURE_DIMENSIONS_MODE_ENUM_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Files/ImageCaptureSettings.cxx000066400000000000000000000470741300200146000264350ustar00rootroot00000000000000 /*LICENSE_START*/ /* * Copyright (C) 2015 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 __IMAGE_CAPTURE_SETTINGS_DECLARE__ #include "ImageCaptureSettings.h" #undef __IMAGE_CAPTURE_SETTINGS_DECLARE__ #include "CaretAssert.h" #include "SceneClass.h" #include "SceneClassAssistant.h" using namespace caret; /** * \class caret::ImageCaptureSettings * \brief Image capture properties * \ingroup Files * * Allows one to adjust dimensions for both pixels and spatial units. * Internally, spatial processing is in centimeters. */ /** * Constructor. */ ImageCaptureSettings::ImageCaptureSettings() : CaretObject(), SceneableInterface() { m_aspectRatio = 1.0; m_pixelsPerCentimeter = 72.0 / CENTIMETERS_PER_INCH; m_croppingEnabled = false; m_croppingMargin = 10; m_copyToClipboardEnabled = true; m_saveToFileEnabled = false; m_scaleProportionatelyEnabled = true; m_imageFileName = "untitled.png"; m_cropToTabWindowLockAspectRegionEnabled = true; m_dimensionsMode = ImageCaptureDimensionsModeEnum::IMAGE_CAPTURE_DIMENSIONS_MODE_WINDOW_SIZE; m_imageResolutionUnits = ImageResolutionUnitsEnum::PIXELS_PER_INCH; m_spatialUnits = ImageSpatialUnitsEnum::INCHES; setPixelWidthAndHeight(512, 512); m_sceneAssistant = new SceneClassAssistant(); m_sceneAssistant->add("m_pixelWidth", &m_pixelWidth); m_sceneAssistant->add("m_pixelHeight", &m_pixelHeight); m_sceneAssistant->add("m_centimetersWidth", &m_centimetersWidth); m_sceneAssistant->add("m_centimetersHeight", &m_centimetersHeight); m_sceneAssistant->add("m_pixelsPerCentimeter", &m_pixelsPerCentimeter); m_sceneAssistant->add("m_aspectRatio", &m_aspectRatio); m_sceneAssistant->add("m_croppingEnabled", &m_croppingEnabled); m_sceneAssistant->add("m_croppingMargin", &m_croppingMargin); m_sceneAssistant->add("m_copyToClipboardEnabled", &m_copyToClipboardEnabled); m_sceneAssistant->add("m_saveToFileEnabled", &m_saveToFileEnabled); m_sceneAssistant->add("m_scaleProportionatelyEnabled", &m_scaleProportionatelyEnabled); m_sceneAssistant->add("m_cropToTabWindowLockAspectRegionEnabled", &m_cropToTabWindowLockAspectRegionEnabled); m_sceneAssistant->add("m_dimensionsMode", &m_dimensionsMode); m_sceneAssistant->add("m_imageResolutionUnits", &m_imageResolutionUnits); m_sceneAssistant->add("m_spatialUnits", &m_spatialUnits); } /** * Destructor. */ ImageCaptureSettings::~ImageCaptureSettings() { delete m_sceneAssistant; } /** * Copy constructor. * @param obj * Object that is copied. */ ImageCaptureSettings::ImageCaptureSettings(const ImageCaptureSettings& obj) : CaretObject(obj), SceneableInterface(obj) { this->copyHelperImageDimensionsModel(obj); } /** * Assignment operator. * @param obj * Data copied from obj to this. * @return * Reference to this object. */ ImageCaptureSettings& ImageCaptureSettings::operator=(const ImageCaptureSettings& obj) { if (this != &obj) { CaretObject::operator=(obj); this->copyHelperImageDimensionsModel(obj); } return *this; } /** * Helps with copying an object of this type. * @param obj * Object that is copied. */ void ImageCaptureSettings::copyHelperImageDimensionsModel(const ImageCaptureSettings& obj) { m_pixelWidth = obj.m_pixelWidth; m_pixelHeight = obj.m_pixelHeight; m_centimetersWidth = obj.m_centimetersWidth; m_centimetersHeight = obj.m_centimetersHeight; m_pixelsPerCentimeter = obj.m_pixelsPerCentimeter; m_aspectRatio = obj.m_aspectRatio; m_scaleProportionatelyEnabled = obj.m_scaleProportionatelyEnabled; m_croppingMargin = obj.m_croppingMargin; m_croppingEnabled = obj.m_croppingEnabled; m_copyToClipboardEnabled = obj.m_copyToClipboardEnabled; m_saveToFileEnabled = obj.m_saveToFileEnabled; m_imageFileName = obj.m_imageFileName; m_dimensionsMode = obj.m_dimensionsMode; m_imageResolutionUnits = obj.m_imageResolutionUnits; m_spatialUnits = obj.m_spatialUnits; } /** * Get a description of this object's content. * @return String describing this object's content. */ AString ImageCaptureSettings::toString() const { return "ImageCaptureSettings"; } /** * @return The width in pixels. */ int32_t ImageCaptureSettings::getPixelWidth() const { /* * Internally, pixels are float, so round (by adding 0.5) * and return as integer. */ return static_cast(m_pixelWidth + 0.5); } /** * @return The height in pixels. */ int32_t ImageCaptureSettings::getPixelHeight() const { /* * Internally, pixels are float, so round (by adding 0.5) * and return as integer. */ return static_cast(m_pixelHeight + 0.5); } /** * Get the spatial width. * * @return * Width in spatial units. */ float ImageCaptureSettings::getSpatialWidth() const { float width = 1.0; switch (m_spatialUnits) { case ImageSpatialUnitsEnum::CENTIMETERS: width = m_centimetersWidth; break; case ImageSpatialUnitsEnum::INCHES: width = m_centimetersWidth / CENTIMETERS_PER_INCH; break; case ImageSpatialUnitsEnum::MILLIMETERS: width = m_centimetersWidth * MILLIMETERS_PER_CENTIMETER; break; } return width; } /** * Get the spatial height. * * @return * Height in spatial units. */ float ImageCaptureSettings::getSpatialHeight() const { float height = 1.0; switch (m_spatialUnits) { case ImageSpatialUnitsEnum::CENTIMETERS: height = m_centimetersHeight; break; case ImageSpatialUnitsEnum::INCHES: height = m_centimetersHeight / CENTIMETERS_PER_INCH; break; case ImageSpatialUnitsEnum::MILLIMETERS: height = m_centimetersHeight * MILLIMETERS_PER_CENTIMETER; break; } return height; } /** * Get the image resolution (number of pixels per given spatial unit). * * @return * Number of pixels per spatial unit. */ float ImageCaptureSettings::getImageResolutionInCentimeters() const { return m_pixelsPerCentimeter; } /** * Get the image resolution in the selected units * * @return * Number of pixels per selected resolution unit. */ float ImageCaptureSettings::getImageResolutionInSelectedUnits() const { float pixelsPerUnit = 1.0; switch (m_imageResolutionUnits) { case ImageResolutionUnitsEnum::PIXEL_PER_CENTIMETER: pixelsPerUnit = m_pixelsPerCentimeter; break; case ImageResolutionUnitsEnum::PIXELS_PER_INCH: pixelsPerUnit = m_pixelsPerCentimeter * CENTIMETERS_PER_INCH; break; } return pixelsPerUnit; } /** * Set the width and height of the image in pixels. * * @param pixelWidth * New pixel width. * @param pixelHeight * New pixel height. */ void ImageCaptureSettings::setPixelWidthAndHeight(const int32_t pixelWidth, const int32_t pixelHeight) { CaretAssert(pixelWidth > 0); CaretAssert(pixelHeight > 0); m_pixelWidth = pixelWidth; m_pixelHeight = pixelHeight; updateSpatialWidthAndHeightFromPixelWidthAndHeight(); } /** * Set the pixel width to the given width. * Width may change if proportionate scaling is enabled. * * @param pixelWidth * New pixel width. */ void ImageCaptureSettings::setPixelWidth(const int32_t pixelWidth) { CaretAssert(pixelWidth > 0); const float aspectRatio = getAspectRatio(); m_pixelWidth = pixelWidth; if (m_scaleProportionatelyEnabled) { m_pixelHeight = m_pixelWidth * aspectRatio; } updateSpatialWidthAndHeightFromPixelWidthAndHeight(); } /** * Set the pixel height to the given height. * Width may change if proportionate scaling is enabled. * * @param pixelHeight * New pixel height. */ void ImageCaptureSettings::setPixelHeight(const int32_t pixelHeight) { CaretAssert(pixelHeight > 0); const float aspectRatio = getAspectRatio(); m_pixelHeight = pixelHeight; if (m_scaleProportionatelyEnabled) { m_pixelWidth = m_pixelHeight / aspectRatio; } updateSpatialWidthAndHeightFromPixelWidthAndHeight(); } /** * Set the spatial width to the given width. * Height may change if proportionate scaling is enabled. * * @param spatialWidth * New spatial width. */ void ImageCaptureSettings::setSpatialWidth(const float spatialWidth) { CaretAssert(spatialWidth > 0); const float aspectRatio = getAspectRatio(); switch (m_spatialUnits) { case ImageSpatialUnitsEnum::MILLIMETERS: m_centimetersWidth = spatialWidth / MILLIMETERS_PER_CENTIMETER; break; case ImageSpatialUnitsEnum::INCHES: m_centimetersWidth = spatialWidth * CENTIMETERS_PER_INCH; break; case ImageSpatialUnitsEnum::CENTIMETERS: m_centimetersWidth = spatialWidth; break; } if (m_scaleProportionatelyEnabled) { m_centimetersHeight = m_centimetersWidth * aspectRatio; } updatePixelWidthAndHeightFromSpatialWidthAndHeight(); } /** * Set the spatial height to the given height. * Width may change if proportionate scaling is enabled. * * @param spatialHeight * New spatial height. */ void ImageCaptureSettings::setSpatialHeight(const float spatialHeight) { CaretAssert(spatialHeight > 0); const float aspectRatio = getAspectRatio(); switch (m_spatialUnits) { case ImageSpatialUnitsEnum::MILLIMETERS: m_centimetersHeight = spatialHeight / MILLIMETERS_PER_CENTIMETER; break; case ImageSpatialUnitsEnum::INCHES: m_centimetersHeight = spatialHeight * CENTIMETERS_PER_INCH; break; case ImageSpatialUnitsEnum::CENTIMETERS: m_centimetersHeight = spatialHeight; break; } if (m_scaleProportionatelyEnabled) { m_centimetersWidth = m_centimetersHeight / aspectRatio; } updatePixelWidthAndHeightFromSpatialWidthAndHeight(); } /** * Set the image resolution (number of pixels per spatial unit to the given value). * * @param imageResolutionInSelectedUnits * New value for number of pixels per selected resolution unit */ void ImageCaptureSettings::setImageResolutionInSelectedUnits(const float imageResolutionInSelectedUnits) { CaretAssert(imageResolutionInSelectedUnits > 0); switch (m_imageResolutionUnits) { case ImageResolutionUnitsEnum::PIXELS_PER_INCH: m_pixelsPerCentimeter = imageResolutionInSelectedUnits / CENTIMETERS_PER_INCH; break; case ImageResolutionUnitsEnum::PIXEL_PER_CENTIMETER: m_pixelsPerCentimeter = imageResolutionInSelectedUnits; break; } updatePixelWidthAndHeightFromSpatialWidthAndHeight(); } /** * Update the pixel width and height after a change to the spatial width * and/or height. */ void ImageCaptureSettings::updatePixelWidthAndHeightFromSpatialWidthAndHeight() { m_pixelWidth = m_centimetersWidth * m_pixelsPerCentimeter; m_pixelHeight = m_centimetersHeight * m_pixelsPerCentimeter; } /** * Update the spatial width and height after a change to the pixel width * and/or height. */ void ImageCaptureSettings::updateSpatialWidthAndHeightFromPixelWidthAndHeight() { m_centimetersWidth = m_pixelWidth / m_pixelsPerCentimeter; m_centimetersHeight = m_pixelHeight / m_pixelsPerCentimeter; } /** * @return the aspect ration (height / width). */ float ImageCaptureSettings::getAspectRatio() const { return m_aspectRatio; } /** * Update the pixel and image dimensions for the aspect ratio * determined by the given width and height. Aspect ratio * is (height / width) and the height will be changed using * the aspect ratio. * * @param width * Width used in denominator of aspect ratio. * @param height * Height used in numerator of aspect ratio. */ void ImageCaptureSettings::updateForAspectRatio(const float width, const float height) { if ((width > 0.0) && (height > 0.0)) { m_aspectRatio = height / width; m_pixelHeight = m_aspectRatio * m_pixelWidth; updateSpatialWidthAndHeightFromPixelWidthAndHeight(); } } /** * @return The cropping margin. */ int32_t ImageCaptureSettings::getCroppingMargin() const { return m_croppingMargin; } /** * Set the cropping margin. * * @param croppingMargin * New cropping margin. */ void ImageCaptureSettings::setCroppingMargin(const int32_t croppingMargin) { m_croppingMargin = croppingMargin; } /** * @return Is cropping enabled? */ bool ImageCaptureSettings::isCroppingEnabled() const { return m_croppingEnabled; } /** * Set cropping enabled. * * @param enabled * New enabled status. */ void ImageCaptureSettings::setCroppingEnabled(const bool enabled) { m_croppingEnabled = enabled; } /** * @return Is copy to clipboard enabled? */ bool ImageCaptureSettings::isCopyToClipboardEnabled() const { return m_copyToClipboardEnabled; } /** * Set copy to clipboard enabled. * * @param enabled * New enabled status. */ void ImageCaptureSettings::setCopyToClipboardEnabled(const bool enabled) { m_copyToClipboardEnabled = enabled; } /** * @return Is save to file enabled? */ bool ImageCaptureSettings::isSaveToFileEnabled() const { return m_saveToFileEnabled; } /** * Set save to file enabled. * * @param enabled * New enabled status. */ void ImageCaptureSettings::setSaveToFileEnabled(const bool enabled) { m_saveToFileEnabled = enabled; } /** * @return Image file name */ AString ImageCaptureSettings::getImageFileName() const { return m_imageFileName; } /** * @return Is scale proportionately enabled? */ bool ImageCaptureSettings::isScaleProportionately() const { return m_scaleProportionatelyEnabled; } /** * Set scale proportionately enabled. * * @param enabled * New enabled status. */ void ImageCaptureSettings::setScaleProportionately(const bool enabled) { m_scaleProportionatelyEnabled = enabled; } /** * Set the image file name. * * @param filename * Name for image file. */ void ImageCaptureSettings::setImageFileName(const AString& filename) { m_imageFileName = filename; } /** * @return Image dimensions mode */ ImageCaptureDimensionsModeEnum::Enum ImageCaptureSettings::getImageCaptureDimensionsMode() const { return m_dimensionsMode; } /** * @return Image resolution units */ ImageResolutionUnitsEnum::Enum ImageCaptureSettings::getImageResolutionUnits() const { return m_imageResolutionUnits; } /** * Set the image resolution units * * @param imageResolutionUnits * New value for image resolution units */ void ImageCaptureSettings::setImageResolutionUnits(const ImageResolutionUnitsEnum::Enum imageResolutionUnits) { m_imageResolutionUnits = imageResolutionUnits; updateSpatialWidthAndHeightFromPixelWidthAndHeight(); } /** * @return The spatial units. */ ImageSpatialUnitsEnum::Enum ImageCaptureSettings::getSpatialUnits() const { return m_spatialUnits; } /** * Set the spatial units. * * @param spatialUnits * New value for spatial units. */ void ImageCaptureSettings::setSpatialUnits(const ImageSpatialUnitsEnum::Enum spatialUnits) { m_spatialUnits = spatialUnits; updatePixelWidthAndHeightFromSpatialWidthAndHeight(); } /** * Set the image dimensions mode. * * @param mode * New mode. */ void ImageCaptureSettings::setImageCaptureDimensionsMode(const ImageCaptureDimensionsModeEnum::Enum mode) { m_dimensionsMode = mode; } /** * @param Is crop to tab/window lock aspect ratio enabled? */ bool ImageCaptureSettings::isCropToTabWindowLockAspectRegionEnabled() const { return m_cropToTabWindowLockAspectRegionEnabled; } /** * Set crop to tab/window lock aspect ratio enabled. * * @param enabled * New status. */ void ImageCaptureSettings::setCropToTabWindowLockAspectRegionEnabled(const bool enabled) { m_cropToTabWindowLockAspectRegionEnabled = enabled; } /** * 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* ImageCaptureSettings::saveToScene(const SceneAttributes* sceneAttributes, const AString& instanceName) { SceneClass* sceneClass = new SceneClass(instanceName, "ImageCaptureSettings", 1); m_sceneAssistant->saveMembers(sceneAttributes, sceneClass); sceneClass->addPathName("m_imageFileName", m_imageFileName); // 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 ImageCaptureSettings::restoreFromScene(const SceneAttributes* sceneAttributes, const SceneClass* sceneClass) { if (sceneClass == NULL) { return; } m_sceneAssistant->restoreMembers(sceneAttributes, sceneClass); m_imageFileName = sceneClass->getPathNameValue("m_imageFileName", "untitled.png"); //Uncomment if sub-classes must restore from scene //restoreSubClassDataFromScene(sceneAttributes, // sceneClass); } connectome-workbench-1.2.3+git41-gc4c6c90/src/Files/ImageCaptureSettings.h000066400000000000000000000145201300200146000260500ustar00rootroot00000000000000#ifndef __IMAGE_CAPTURE_SETTINGS_H__ #define __IMAGE_CAPTURE_SETTINGS_H__ /*LICENSE_START*/ /* * Copyright (C) 2015 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 "ImageCaptureDimensionsModeEnum.h" #include "ImageResolutionUnitsEnum.h" #include "ImageSpatialUnitsEnum.h" #include "SceneableInterface.h" namespace caret { class SceneClassAssistant; class ImageCaptureSettings : public CaretObject, public SceneableInterface { public: ImageCaptureSettings(); virtual ~ImageCaptureSettings(); ImageCaptureSettings(const ImageCaptureSettings& obj); ImageCaptureSettings& operator=(const ImageCaptureSettings& obj); int32_t getPixelWidth() const; int32_t getPixelHeight() const; float getSpatialWidth() const; float getSpatialHeight() const; float getImageResolutionInCentimeters() const; float getImageResolutionInSelectedUnits() const; void setPixelWidthAndHeight(const int32_t pixelWidth, const int32_t pixelHeight); void setPixelWidth(const int32_t pixelWidth); void setPixelHeight(const int32_t pixelHeight); void setSpatialWidth(const float spatialWidth); void setSpatialHeight(const float spatialHeight); void setImageResolutionInSelectedUnits(const float imageResolutionInSelectedUnits); void updateForAspectRatio(const float width, const float height); int32_t getCroppingMargin() const; void setCroppingMargin(const int32_t croppingMargin); bool isCroppingEnabled() const; void setCroppingEnabled(const bool enabled); bool isCopyToClipboardEnabled() const; void setCopyToClipboardEnabled(const bool enabled); bool isSaveToFileEnabled() const; void setSaveToFileEnabled(const bool enabled); AString getImageFileName() const; void setImageFileName(const AString& filename); ImageCaptureDimensionsModeEnum::Enum getImageCaptureDimensionsMode() const; void setImageCaptureDimensionsMode(const ImageCaptureDimensionsModeEnum::Enum mode); ImageResolutionUnitsEnum::Enum getImageResolutionUnits() const; void setImageResolutionUnits(const ImageResolutionUnitsEnum::Enum imageResolutionUnits); ImageSpatialUnitsEnum::Enum getSpatialUnits() const; void setSpatialUnits(const ImageSpatialUnitsEnum::Enum spatialUnits); bool isScaleProportionately() const; void setScaleProportionately(const bool enabled); bool isCropToTabWindowLockAspectRegionEnabled() const; void setCropToTabWindowLockAspectRegionEnabled(const bool enabled); // 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 implemetation 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 copyHelperImageDimensionsModel(const ImageCaptureSettings& obj); SceneClassAssistant* m_sceneAssistant; void updatePixelWidthAndHeightFromSpatialWidthAndHeight(); void updateSpatialWidthAndHeightFromPixelWidthAndHeight(); float getAspectRatio() const; // ADD_NEW_MEMBERS_HERE /** * Width/height of pixels are float. Otherwise, small changes in * pixel dimensions will get lost (truncated). */ float m_pixelWidth; float m_pixelHeight; float m_centimetersWidth; float m_centimetersHeight; float m_pixelsPerCentimeter; float m_aspectRatio; bool m_scaleProportionatelyEnabled; int32_t m_croppingMargin; bool m_croppingEnabled; bool m_copyToClipboardEnabled; bool m_saveToFileEnabled; AString m_imageFileName; bool m_cropToTabWindowLockAspectRegionEnabled; ImageCaptureDimensionsModeEnum::Enum m_dimensionsMode; ImageResolutionUnitsEnum::Enum m_imageResolutionUnits; ImageSpatialUnitsEnum::Enum m_spatialUnits; static const float CENTIMETERS_PER_INCH; static const float MILLIMETERS_PER_CENTIMETER; }; #ifdef __IMAGE_CAPTURE_SETTINGS_DECLARE__ const float ImageCaptureSettings::CENTIMETERS_PER_INCH = 2.54; const float ImageCaptureSettings::MILLIMETERS_PER_CENTIMETER = 10.0; #endif // __IMAGE_CAPTURE_SETTINGS_DECLARE__ } // namespace #endif //__IMAGE_CAPTURE_SETTINGS_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Files/ImageFile.cxx000066400000000000000000001400411300200146000241540ustar00rootroot00000000000000/*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 "CaretAssert.h" #include "CaretLogger.h" #include "ControlPointFile.h" #include "ControlPoint3D.h" #include "DataFileException.h" #include "FileInformation.h" #include "GiftiMetaData.h" #include "ImageCaptureSettings.h" #include "ImageFile.h" #include "Matrix4x4.h" #include "MathFunctions.h" #include "PaletteFile.h" #include "SceneClass.h" #include "VolumeFile.h" using namespace caret; const float ImageFile::s_defaultWindowDepthPercentage = 990; /** * Constructor. */ ImageFile::ImageFile() : CaretDataFile(DataFileTypeEnum::IMAGE) { m_controlPointFile.grabNew(new ControlPointFile()); m_fileMetaData.grabNew(new GiftiMetaData()); m_image = new QImage(); } /** * Constructor * @param qimage * QImage that is copied to this image file. */ ImageFile::ImageFile(const QImage& qimage) : CaretDataFile(DataFileTypeEnum::IMAGE) { m_controlPointFile.grabNew(new ControlPointFile()); m_fileMetaData.grabNew(new GiftiMetaData()); m_image = new QImage(qimage); } /** * Constructs an image file from image data. * * @param imageDataRGBA * Image data unsigned bytes with one byte for each * red, green, blue, alpha. * @param imageWidth * Width of image. * @param imageHeight * Height of image. * @param imageOrigin * Location of first pixel in the image data. */ ImageFile::ImageFile(const unsigned char* imageDataRGBA, const int imageWidth, const int imageHeight, const IMAGE_DATA_ORIGIN_LOCATION imageOrigin) : CaretDataFile(DataFileTypeEnum::IMAGE) { m_controlPointFile.grabNew(new ControlPointFile()); m_fileMetaData.grabNew(new GiftiMetaData()); m_image = new QImage(imageWidth, imageHeight, QImage::Format_RGB32); bool isOriginAtTop = false; switch (imageOrigin) { case IMAGE_DATA_ORIGIN_AT_BOTTOM: isOriginAtTop = false; break; case IMAGE_DATA_ORIGIN_AT_TOP: isOriginAtTop = true; break; } /* * Documentation for QImage states that setPixel may be very costly * and recommends using the scanLine() method to access pixel data. */ for (int y = 0; y < imageHeight; y++) { const int scanLineIndex = (isOriginAtTop ? y : imageHeight -y - 1); QRgb* rgbScanLine = (QRgb*)m_image->scanLine(scanLineIndex); for (int x = 0; x < imageWidth; x++) { const int32_t contentOffset = (((y * imageWidth) * 4) + (x * 4)); const int red = imageDataRGBA[contentOffset]; const int green = imageDataRGBA[contentOffset+1]; const int blue = imageDataRGBA[contentOffset+2]; const int alpha = imageDataRGBA[contentOffset+3]; QColor color(red, green, blue, alpha); QRgb* pixel = &rgbScanLine[x]; *pixel = color.rgba(); } } } /** * Destructor. */ ImageFile::~ImageFile() { if (m_image != NULL) { delete m_image; m_image = NULL; } } /** * Clears current file data in memory. */ void ImageFile::clear() { if (m_image != NULL) { delete m_image; } m_image = new QImage(); this->clearModified(); } /** * @return The structure for this file. */ StructureEnum::Enum ImageFile::getStructure() const { return StructureEnum::INVALID; } /** * Set the structure for this file. * @param structure * New structure for this file. */ void ImageFile::setStructure(const StructureEnum::Enum /*structure */) { /* File does not support structures */ } /** * @return Get access to the file's metadata. */ GiftiMetaData* ImageFile::getFileMetaData() { return m_fileMetaData; } /** * @return Get access to unmodifiable file's metadata. */ const GiftiMetaData* ImageFile::getFileMetaData() const { return m_fileMetaData; } /** * @return true if the file is is empty (image contains no pixels). */ bool ImageFile::isEmpty() const { return (m_image->width() <= 0); } ///** // * @return A pointer to the QImage in this file. // * Note that manipulating the pointer's data will // * alter the contents of this file. // */ //QImage* //ImageFile::getAsQImage() //{ // return m_image; //} /** * @return A pointer to the QImage in this file. */ const QImage* ImageFile::getAsQImage() const { return m_image; } /** * Set the image in this file from a QImage. * @param qimage * Image that is copied to this file. */ void ImageFile::setFromQImage(const QImage& qimage) { if (m_image != NULL) { delete m_image; } m_image = new QImage(qimage); this->setModified(); } /** * Set the dots per meter. * * @param x * Dots per meter for X dimension. * @param y * Dots per meter for Y dimension. */ void ImageFile::setDotsPerMeter(const int x, const int y) { m_image->setDotsPerMeterX(x); m_image->setDotsPerMeterY(y); } /** * Examines the image to find the rectangular of the object in the image * by examining pixels in the background color. * @param backgroundColor * RGB components range 0-255. * @param objectBoundsOut * 4-dimensional array containing the region that excludes * the backround around the image's object. */ void ImageFile::findImageObject(const uint8_t backgroundColor[3], int objectBoundsOut[4]) const { // // Dimensions of image // const int numX = m_image->width(); const int numY = m_image->height(); // // Initialize output // objectBoundsOut[0] = 0; objectBoundsOut[1] = 0; objectBoundsOut[2] = numX - 1; objectBoundsOut[3] = numY - 1; // // Find left // bool gotPixelFlag = false; for (int i = 0; i < numX; i++) { for (int j = 0; j < numY; j++) { const QRgb pixel = m_image->pixel(i, j); if ((qRed(pixel) != backgroundColor[0]) || (qGreen(pixel) != backgroundColor[1]) || (qBlue(pixel) != backgroundColor[2])) { objectBoundsOut[0] = i; gotPixelFlag = true; break; } } if (gotPixelFlag) { break; } } // // Find right // gotPixelFlag = false; for (int i = (numX - 1); i >= 0; i--) { for (int j = 0; j < numY; j++) { const QRgb pixel = m_image->pixel(i, j); if ((qRed(pixel) != backgroundColor[0]) || (qGreen(pixel) != backgroundColor[1]) || (qBlue(pixel) != backgroundColor[2])) { objectBoundsOut[2] = i; gotPixelFlag = true; break; } } if (gotPixelFlag) { break; } } // // Find top // gotPixelFlag = false; for (int j = 0; j < numY; j++) { for (int i = 0; i < numX; i++) { const QRgb pixel = m_image->pixel(i, j); if ((qRed(pixel) != backgroundColor[0]) || (qGreen(pixel) != backgroundColor[1]) || (qBlue(pixel) != backgroundColor[2])) { objectBoundsOut[1] = j; gotPixelFlag = true; break; } } if (gotPixelFlag) { break; } } // // Find bottom // gotPixelFlag = false; for (int j = (numY - 1); j >= 0; j--) { for (int i = 0; i < numX; i++) { const QRgb pixel = m_image->pixel(i, j); if ((qRed(pixel) != backgroundColor[0]) || (qGreen(pixel) != backgroundColor[1]) || (qBlue(pixel) != backgroundColor[2])) { objectBoundsOut[3] = j; gotPixelFlag = true; break; } } if (gotPixelFlag) { break; } } } /** * Add a margin to this image. * @param marginSize * Number of pixels in the margin. * @param backgroundColor used for the added pixels. * RGB components range 0-255. */ void ImageFile::addMargin(const int marginSize, const uint8_t backgroundColor[3]) { this->addMargin(marginSize, marginSize, backgroundColor); /* if (marginSize <= 0) { return; } // // Add margin // const int width = image.width(); const int height = image.height(); const int newWidth = width + marginSize * 2; const int newHeight = height + marginSize * 2; QRgb backgroundColorRGB = qRgba(backgroundColor[0], backgroundColor[1], backgroundColor[2], 0); // // Insert image // ImageFile imageFile; imageFile.setImage(QImage(newWidth, newHeight, image.format())); imageFile.getImage()->fill(backgroundColorRGB); try { imageFile.insertImage(image, marginSize, marginSize); image = (*imageFile.getImage()); } catch (DataFileException&) { } */ } /** * Add a margin to this image. * @param image * Image to which margin is added. * @param marginSizeX * Number of pixels in the margin along x-axis. * @param marginSizeY * Number of pixels in the margin along y-axis. * @param backgroundColor used for the added pixels. * RGB components range 0-255. */ void ImageFile::addMargin(const int marginSizeX, const int marginSizeY, const uint8_t backgroundColor[3]) { if ((marginSizeX <= 0) && (marginSizeY <= 0)) { return; } // // Add margin // const int width = m_image->width(); const int height = m_image->height(); const int newWidth = width + marginSizeX * 2; const int newHeight = height + marginSizeY * 2; QRgb backgroundColorRGB = qRgba(backgroundColor[0], backgroundColor[1], backgroundColor[2], 0); // // Insert image // ImageFile imageFile; imageFile.setFromQImage(QImage(newWidth, newHeight, m_image->format())); imageFile.m_image->fill(backgroundColorRGB); try { imageFile.insertImage(*m_image, marginSizeX, marginSizeY); this->setFromQImage(*imageFile.getAsQImage()); } catch (DataFileException& e) { CaretLogWarning(e.whatString()); } this->setModified(); } /** * Crop an image by removing the background from the object in the image * but keeping a margin of the given size around the image. * @param marginSize * Number of pixels in the margin around the image's object. * @param backgroundColor * Color of background that ranges 0-255. */ void ImageFile::cropImageRemoveBackground(const int marginSize, const uint8_t backgroundColor[3]) { // // Get cropping bounds // int leftTopRightBottom[4]; this->findImageObject(backgroundColor, leftTopRightBottom); CaretLogFine("cropping: " + AString::fromNumbers(leftTopRightBottom, 4, " ")); const int currentWidth = m_image->width(); const int currentHeight = m_image->height(); // // If cropping is valid // const int width = leftTopRightBottom[2] - leftTopRightBottom[0] + 1; const int height = leftTopRightBottom[3] - leftTopRightBottom[1] + 1; if ((width != currentWidth) || (height != currentHeight)) { if ((width > 1) && (height > 1)) { QImage copyImage = this->getAsQImage()->copy(leftTopRightBottom[0], leftTopRightBottom[1], width, height); if (copyImage.isNull() == false) { if ((copyImage.width() > 0) && (copyImage.height() > 0)) { this->setFromQImage(copyImage); } } this->setModified(); } } // // Process margin // if (marginSize > 0) { this->addMargin(marginSize, backgroundColor); } } /** * Replace the contents of this image by combining the input images and * retaining aspect and stretching and filling if needed. * @param imageFiles * Images that are combined. * @param numImagesPerRow * Number of images in each row. * @param backgroundColor * Color of background that ranges 0-255. */ void ImageFile::combinePreservingAspectAndFillIfNeeded(const std::vector& imageFiles, const int numImagesPerRow, const uint8_t backgroundColor[3]) { const int numImages = static_cast(imageFiles.size()); if (numImages <= 0) { return; } if (numImages == 1) { this->setFromQImage(*imageFiles[0]->m_image); return; } QRgb backgroundColorRGB = qRgba(backgroundColor[0], backgroundColor[1], backgroundColor[2], 0); // // Resize all images but do not stretch // need to retain aspect ratio but all must // be the same size in X & Y // // // Find max width and height of input images // int maxImageWidth = 0; int maxImageHeight = 0; for (int i = 0; i < numImages; i++) { // // Track max width/height // maxImageWidth = std::max(maxImageWidth, imageFiles[i]->m_image->width()); maxImageHeight = std::max(maxImageHeight, imageFiles[i]->m_image->height()); } // // Compute size of output image and create it // const int outputImageSizeX = maxImageWidth * numImagesPerRow; const int numberOfRows = (numImages / numImagesPerRow) + (((numImages % numImagesPerRow) != 0) ? 1 : 0); const int outputImageSizeY = maxImageHeight * numberOfRows; QImage combinedImage(outputImageSizeX, outputImageSizeY, imageFiles[0]->m_image->format()); combinedImage.fill(backgroundColorRGB); // // Loop through the images // int rowCounter = 0; int columnCounter = 0; for (int i = 0; i < numImages; i++) { // // Scale image // const QImage imageScaled = imageFiles[i]->m_image->scaled(maxImageWidth, maxImageHeight, Qt::KeepAspectRatio, Qt::SmoothTransformation); // // Compute position of where image should be inserted // const int marginX = (maxImageWidth - imageScaled.width()) / 2; const int marginY = (maxImageHeight - imageScaled.height()) / 2; const int positionX = columnCounter * maxImageWidth + marginX; const int positionY = rowCounter * maxImageHeight + marginY; // // Insert into output image // try { ImageFile::insertImage(imageScaled, combinedImage, positionX, positionY); } catch (DataFileException& e) { CaretLogWarning("QImageFile::insertImage() error: " + e.whatString()); } // // Update row and column counters // columnCounter++; if (columnCounter >= numImagesPerRow) { columnCounter = 0; rowCounter++; } } this->setFromQImage(combinedImage); } /** * Read the image file. * @param filename * Name of image file. * @throws DataFileException * If error reading image. */ void ImageFile::readFile(const AString& filename) { clear(); checkFileReadability(filename); if (filename.isEmpty()) { throw DataFileException(filename + "Filename for reading is isEmpty"); } this->setFileName(filename); if (m_image->load(filename) == false) { clear(); throw DataFileException(filename + "Unable to load file."); } this->clearModified(); } /** * Append an image file to the bottom of this image file. * @param img * Image that is appended. */ void ImageFile::appendImageAtBottom(const ImageFile& img) { // // Determine size of new image // const QImage* otherImage = img.getAsQImage(); const int newWidth = std::max(m_image->width(), otherImage->width()); const int newHeight = m_image->height() + otherImage->height(); const int oldHeight = m_image->height(); // // Copy the current image // const QImage currentImage = *m_image; // std::cout << "cw: " << currentImage.width() << std::endl; // std::cout << "ch: " << currentImage.height() << std::endl; // // Create the new image and make it "this" image // QImage newImage(newWidth, newHeight, QImage::Format_RGB32); // std::cout << "nw: " << newImage.width() << std::endl; // std::cout << "nh: " << newImage.height() << std::endl; setFromQImage(newImage); // std::cout << "iw2: " << image.width() << std::endl; // std::cout << "ih2: " << image.height() << std::endl; // // Insert current image into new image // insertImage(currentImage, 0, 0); // // Insert other image into new image // insertImage(*otherImage, 0, oldHeight); this->setModified(); } /** * Insert an image into this image which must be large enough for insertion of image. * @param otherImage * Image that is inserted into this image. * @param x * X position of where image is inserted. * @param y * Y position of where image is inserted. * @throws DataFileException * If error inserting image. */ void ImageFile::insertImage(const QImage& otherImage, const int x, const int y) { ImageFile::insertImage(otherImage, *m_image, x, y); this->setModified(); } /** * insert an image into another image. * * Insert an image into another image which must be large enough for insertion of image. * @param insertThisImage * Image that is inserted into other image. * @param intoThisImage * Image that receives insertions of other image. * @param x * X position of where image is inserted. * @param y * Y position of where image is inserted. * @throws DataFileException * If error inserting image. */ void ImageFile::insertImage(const QImage& insertThisImage, QImage& intoThisImage, const int positionX, const int positionY) { if (positionX < 0) { throw DataFileException("X position is less than zero."); } if (positionY < 0) { throw DataFileException("Y position is less than zero."); } const int otherWidth = insertThisImage.width(); const int otherHeight = insertThisImage.height(); const int myWidth = intoThisImage.width(); const int myHeight = intoThisImage.height(); if ((otherWidth + positionX) > myWidth) { throw DataFileException("This image is not large enough to insert other image."); } if ((otherHeight + positionY) > myHeight) { throw DataFileException("This image is not large enough to insert other image."); } for (int i = 0; i < otherWidth; i++) { for (int j = 0; j < otherHeight; j++) { intoThisImage.setPixel(positionX + i, positionY + j, insertThisImage.pixel(i, j)); } } } /** * Compare a file for unit testing (tolerance ignored). * * @param dataFile * Data files that is compared to this data file. * @param tolerance * Allowable difference at each pixel. * @param messageOut * Message describing differences. * @return * True if files are within tolerance, else false. */ bool ImageFile::compareFileForUnitTesting(const DataFile* dataFile, const float tolerance, AString& messageOut) const { // // Cast to an image file // const ImageFile* img = dynamic_cast(dataFile); if (img == NULL) { messageOut = ("ERROR: File for comparison (" + dataFile->getFileName() + " does not appear to be an image file."); return false; } // // Get the image from the other file // const QImage* otherImage = img->getAsQImage(); // // Confirm width/height // const int width = m_image->width(); const int height = m_image->height(); if ((width != otherImage->width()) || (height != otherImage->height())) { messageOut = "The images are of different height and/or width."; return false; } // // compare pixels // int pixelCount = 0; for (int i = 0; i < width; i++) { for (int j = 0; j < height; j++) { QColor im1 = m_image->pixel(i, j); QColor im2 = otherImage->pixel(i, j); if ((abs(im1.red() - im2.red()) > tolerance) || (abs(im1.green() - im2.green()) > tolerance) || (abs(im1.blue() - im2.blue()) > tolerance)) { pixelCount++; } } } if (pixelCount > 0) { const float pct = static_cast(pixelCount * 100.0) / static_cast(width * height); messageOut = QString::number(pct, 'f', 2) + "% pixels in the image do not match."; return false; } return true; } /** * Write the image file. * @param filename * Name of image file. * @throws DataFileException * If error writing image. */ void ImageFile::writeFile(const AString& filename) { checkFileWritability(filename); this->setFileName(filename); AString errorMessage; if (m_image->width() <= 0) { errorMessage = "Image width is zero."; } if (m_image->height() <= 0) { if (errorMessage.isEmpty() == false) errorMessage += "\n"; errorMessage = "Image height is zero."; } if (errorMessage.isEmpty() == false) { throw DataFileException(filename + " " + errorMessage); } FileInformation fileInfo(this->getFileName()); AString format = fileInfo.getFileExtension().toUpper(); if (format == "JPG") { format = "JPEG"; } QImageWriter writer(filename, format.toAscii()); if (writer.supportsOption(QImageIOHandler::Quality)) { if (format.compare("png", Qt::CaseInsensitive) == 0) { const int quality = 1; writer.setQuality(quality); } else { const int quality = 100; writer.setQuality(quality); } } if (writer.supportsOption(QImageIOHandler::CompressionRatio)) { writer.setCompression(1); } if (writer.write(*m_image) == false) { throw DataFileException(writer.errorString()); } this->clearModified(); } /** * Get the image file extensions for the supported image types. * The extensions do not include the leading period. * * @param imageFileExtensions * Output filled with extensions for supported image types. * @param defaultExtension * The default extension (preference is png, jpg, jpeg) */ void ImageFile::getImageFileExtensions(std::vector& imageFileExtensions, AString& defaultExtension) { imageFileExtensions.clear(); defaultExtension = ""; QString firstExtension; QString pngExtension; QString jpegExtension; QString jpgExtension; QString tifExtension; QString tiffExtension; QList imageFormats = QImageWriter::supportedImageFormats(); const int numFormats = imageFormats.count(); for (int i = 0; i < numFormats; i++) { AString extension = QString(imageFormats.at(i)).toLower(); imageFileExtensions.push_back(extension); if (i == 0) { firstExtension = extension; } if (extension == "png") { pngExtension = extension; } else if (extension == "jpg") { jpgExtension = extension; } else if (extension == "jpeg") { jpegExtension = extension; } else if (extension == "tif") { tifExtension = extension; } else if (extension == "tiff") { tiffExtension = extension; } } if (pngExtension.isEmpty() == false) { defaultExtension = pngExtension; } else if (jpgExtension.isEmpty() == false) { defaultExtension = jpgExtension; } else if (jpegExtension.isEmpty() == false) { defaultExtension = jpegExtension; } else if (tifExtension.isEmpty() == false) { defaultExtension = tifExtension; } else if (tiffExtension.isEmpty() == false) { defaultExtension = tiffExtension; } else { defaultExtension = firstExtension; } } /** * Get the image file filters for the supported image types. * * @param imageFileFilters * Output filled with the filters for supported image types. * @param defaultFilter * Filter for the preferred image type. */ void ImageFile::getImageFileFilters(std::vector& imageFileFilters, AString& defaultFilter) { imageFileFilters.clear(); defaultFilter.clear(); std::vector imageFileExtensions; AString defaultExtension; ImageFile::getImageFileExtensions(imageFileExtensions, defaultExtension); const int32_t numExtensions = static_cast(imageFileExtensions.size()); for (int32_t i = 0; i < numExtensions; i++) { const AString ext = imageFileExtensions[i]; const AString filter = (ext.toUpper() + " Image File (*." + ext + ")"); imageFileFilters.push_back(filter); if (ext == defaultExtension) { defaultFilter = filter; } } if (defaultFilter.isEmpty()) { if (imageFileFilters.empty() == false) { defaultFilter = imageFileFilters[0]; } } } /** * Resize the image to the given width while preserving the aspect ratio * of the image. * * @param width * Width for image. */ void ImageFile::resizeToWidth(const int32_t width) { CaretAssert(m_image); *m_image = m_image->scaledToWidth(width, Qt::SmoothTransformation); } /** * Resize the image to the given height while preserving the aspect ratio * of the image. * * @param height * Height for image. */ void ImageFile::resizeToHeight(const int32_t height) { CaretAssert(m_image); *m_image = m_image->scaledToHeight(height, Qt::SmoothTransformation); } /** * Resize the image so that its width is no larger than the given value. * If the image's current width is less than the given value, no * resizing takes place. * * @param maximumWidth * Maximum width for the image. */ void ImageFile::resizeToMaximumWidth(const int32_t maximumWidth) { CaretAssert(m_image); const int32_t width = m_image->width(); if (width > maximumWidth) { *m_image = m_image->scaledToWidth(maximumWidth, Qt::SmoothTransformation); } } /** * Resize the image so that its height is no larger than the given value. * If the image's current height is less than the given value, no * resizing takes place. * * @param maximumHeight * Maximum height for the image. */ void ImageFile::resizeToMaximumHeight(const int32_t maximumHeight) { CaretAssert(m_image); const int32_t height = m_image->height(); if (height > maximumHeight) { *m_image = m_image->scaledToHeight(maximumHeight, Qt::SmoothTransformation); } } /** * Resize the image so that its maximum dimension is the given value * yet preserves the aspect ratio of the image. If the maximum dimension * is less than the given value, no resizing takes place. * * @param maximumWidthOrHeight * Maximum dimension for the image. */ void ImageFile::resizeToMaximumWidthOrHeight(const int32_t maximumWidthOrHeight) { CaretAssert(m_image); const int32_t width = m_image->width(); const int32_t height = m_image->height(); if ((width > 0) && (height > 0)) { if (width > height) { resizeToMaximumWidth(maximumWidthOrHeight); } else { if (height > maximumWidthOrHeight) { resizeToMaximumWidth(maximumWidthOrHeight); } } } } /** * Get the RGBA bytes from the image. * * @param bytesRGBA * The RGBA bytes in the image. * @param widthOut * Width of the image. * @param heightOut * Height of the image. * @param imageOrigin * Location of first pixel in the image data. * @return * True if the bytes, width, and height are valid, else false. */ bool ImageFile::getImageBytesRGBA(const IMAGE_DATA_ORIGIN_LOCATION imageOrigin, std::vector& bytesRGBA, int32_t& widthOut, int32_t& heightOut) const { bytesRGBA.clear(); widthOut = 0; heightOut = 0; if (m_image != NULL) { widthOut = m_image->width(); heightOut = m_image->height(); if ((widthOut > 0) && (heightOut > 0)) { bytesRGBA.resize(widthOut * heightOut * 4); bool isOriginAtTop = false; switch (imageOrigin) { case IMAGE_DATA_ORIGIN_AT_BOTTOM: isOriginAtTop = false; break; case IMAGE_DATA_ORIGIN_AT_TOP: isOriginAtTop = true; break; } /* * Documentation for QImage states that setPixel may be very costly * and recommends using the scanLine() method to access pixel data. */ for (int y = 0; y < heightOut; y++) { const int scanLineIndex = (isOriginAtTop ? y : heightOut -y - 1); const uchar* scanLine = m_image->scanLine(scanLineIndex); QRgb* rgbScanLine = (QRgb*)scanLine; for (int x = 0; x < widthOut; x++) { const int32_t contentOffset = (((y * widthOut) * 4) + (x * 4)); QRgb& rgb = rgbScanLine[x]; bytesRGBA[contentOffset] = static_cast(qRed(rgb)); bytesRGBA[contentOffset+1] = static_cast(qGreen(rgb)); bytesRGBA[contentOffset+2] = static_cast(qBlue(rgb)); bytesRGBA[contentOffset+3] = 255; } } return true; } } return false; } /** * Get the pixel RGBA at the given pixel I and J. * * @param imageOrigin * Location of first pixel in the image data. * @param pixelI * Image I index * @param pixelJ * Image J index * @param pixelRGBAOut * RGBA at Pixel I, J * @return * True if valid, else false. */ bool ImageFile::getImagePixelRGBA(const IMAGE_DATA_ORIGIN_LOCATION imageOrigin, const int32_t pixelI, const int32_t pixelJ, uint8_t pixelRGBAOut[4]) const { if (m_image != NULL) { const int32_t w = m_image->width(); const int32_t h = m_image->height(); if ((pixelI >= 0) && (pixelI < w) && (pixelJ >= 0) && (pixelJ < h)) { int32_t imageJ = pixelJ; switch (imageOrigin) { case IMAGE_DATA_ORIGIN_AT_BOTTOM: imageJ = h - pixelJ - 1; break; case IMAGE_DATA_ORIGIN_AT_TOP: break; } if ((imageJ >= 0) && (imageJ < h)) { const QRgb rgb = m_image->pixel(pixelI, imageJ); pixelRGBAOut[0] = static_cast(qRed(rgb)); pixelRGBAOut[1] = static_cast(qGreen(rgb)); pixelRGBAOut[2] = static_cast(qBlue(rgb)); pixelRGBAOut[3] = 255; return true; } else { CaretLogSevere("Invalid image J"); } } } return false; } /** * Get the RGBA bytes from the image resized into the given width and height. * * @param imageOrigin * Location of first pixel in the image data. * @param resizeToWidth * New width for image. * @param resizeToHeight * New height of the image. * @param bytesRGBAOut * The RGBA bytes in the image. * @return * True if the bytes, width, and height are valid, else false. */ bool ImageFile::getImageResizedBytes(const IMAGE_DATA_ORIGIN_LOCATION imageOrigin, const int32_t resizeToWidth, const int32_t resizeToHeight, std::vector& bytesRGBAOut) const { bytesRGBAOut.clear(); if (m_image == NULL) { return false; } const int32_t numBytes = resizeToWidth * resizeToHeight; if (numBytes <= 0) { return false; } const int32_t colorComponentsPerByte = 4; bytesRGBAOut.resize(numBytes * colorComponentsPerByte); QImage scaledImage = m_image->scaled(resizeToWidth, resizeToHeight, Qt::IgnoreAspectRatio, Qt::SmoothTransformation); /* * QImage::scaled() failed. */ if (scaledImage.isNull()) { return false; } bool isOriginAtTop = false; switch (imageOrigin) { case IMAGE_DATA_ORIGIN_AT_BOTTOM: isOriginAtTop = false; break; case IMAGE_DATA_ORIGIN_AT_TOP: isOriginAtTop = true; break; } /* * Documentation for QImage states that setPixel may be very costly * and recommends using the scanLine() method to access pixel data. */ for (int y = 0; y < resizeToHeight; y++) { const int scanLineIndex = (isOriginAtTop ? y : resizeToHeight -y - 1); const uchar* scanLine = scaledImage.scanLine(scanLineIndex); QRgb* rgbScanLine = (QRgb*)scanLine; for (int x = 0; x < resizeToWidth; x++) { const int32_t contentOffset = (((y * resizeToWidth) * 4) + (x * 4)); QRgb& rgb = rgbScanLine[x]; CaretAssertVectorIndex(bytesRGBAOut, contentOffset + 3); bytesRGBAOut[contentOffset] = static_cast(qRed(rgb)); bytesRGBAOut[contentOffset+1] = static_cast(qGreen(rgb)); bytesRGBAOut[contentOffset+2] = static_cast(qBlue(rgb)); bytesRGBAOut[contentOffset+3] = 255; } } return true; } /** * @return width of image (zero if image is invalid) */ int32_t ImageFile::getWidth() const { int32_t w = 0; if (m_image != NULL) { w = m_image->width(); } return w; } /** * @return height of image (zero if image is invalid) */ int32_t ImageFile::getHeight() const { int32_t h = 0; if (m_image != NULL) { h = m_image->height(); } return h; } /** * Essentially writes the image file to a byte array using the given format. * * @param byteArrayOut * Byte array into which the image is written. * @param format * Format for the image (jpg, ppm, etc.). */ void ImageFile::getImageInByteArray(QByteArray& byteArrayOut, const AString& format) const { byteArrayOut.clear(); if (m_image != NULL) { QBuffer buffer(&byteArrayOut); if ( ! buffer.open(QIODevice::WriteOnly)) { throw DataFileException(getFileName(), "PROGRAM ERROR: Unable to open byte array for output of image."); } bool successFlag = false; if (format.isEmpty()) { successFlag = m_image->save(&buffer); } else { successFlag = m_image->save(&buffer, format.toAscii().data()); } if ( ! successFlag) { throw DataFileException(getFileName(), "Failed to write image to byte array. " + buffer.errorString()); } } } /** * Essentially reads the image file from a byte array using the given format. * * @param byteArray * Byte array from which the image is read. * @param format * Format for the image (jpg, ppm, etc.) or empty if unknown. */ void ImageFile::setImageFromByteArray(const QByteArray& byteArray, const AString& format) { bool successFlag = false; if (format.isEmpty()) { successFlag = m_image->loadFromData(byteArray); } else { successFlag = m_image->loadFromData(byteArray, format.toAscii().data()); } if ( ! successFlag) { throw DataFileException(getFileName(), "Failed to create image from byte array."); } } /** * Convert this image into a Volume File using the * encapsulated Control Point File whose matrix * must have been updated. * * @param colorMode * Color mode for conversion. * @param paletteFile * Palette file used for coloring the voxels. * @param errorMessageOut * Contains error message if conversion fails. * @return * Pointer to volume file or NULL if there is an error. * Name of volume file is the name of the image but * the file extension is changed to a volume file extension. * */ VolumeFile* ImageFile::convertToVolumeFile(const CONVERT_TO_VOLUME_COLOR_MODE colorMode, const PaletteFile* paletteFile, AString& errorMessageOut) const { errorMessageOut.clear(); std::vector rgbaBytes; int32_t width = 0; int32_t height = 0; getImageBytesRGBA(ImageFile::IMAGE_DATA_ORIGIN_AT_BOTTOM, rgbaBytes, width, height); if ((width <= 0) || (height <= 0)) { errorMessageOut = "Image width and/or height is invalid."; return NULL; } const Matrix4x4* transformationMatrix = m_controlPointFile->getLandmarkTransformationMatrix(); float firstPixel[3] = { 0, 0, 0 }; transformationMatrix->multiplyPoint3(firstPixel); std::cout << "First pixel coord: " << AString::fromNumbers(firstPixel, 3, ",") << std::endl; float lastPixel[3] = { width - 1, height - 1, 0 }; transformationMatrix->multiplyPoint3(lastPixel); std::cout << "Last pixel coord: " << AString::fromNumbers(lastPixel, 3, ",") << std::endl; { float bl[3] = { 0.0, 0.0, 0.0 }; transformationMatrix->multiplyPoint3(bl); ControlPoint3D bottomLeft(0, 0, 0, bl[0], bl[1], bl[2]); float br[3] = { 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 }; transformationMatrix->multiplyPoint3(tr); ControlPoint3D topRight(width - 1.0, height - 1.0, 0.0, tr[0], tr[1], tr[2]); ControlPointFile volumeControlPointFile; volumeControlPointFile.addControlPoint(bottomLeft); volumeControlPointFile.addControlPoint(bottomRight); volumeControlPointFile.addControlPoint(topRight); if ( ! volumeControlPointFile.updateLandmarkTransformationMatrix(errorMessageOut)) { errorMessageOut.insert(0, "Volume Matrix: "); return NULL; } } std::vector dimensions; dimensions.push_back(width); // I dimensions.push_back(height); // J dimensions.push_back(1); // K /* * Convert matrix4x4 to volume file vector of vectors. */ std::vector row1; std::vector row2; std::vector row3; std::vector row4; for (int j = 0; j < 4; j++) { row1.push_back(transformationMatrix->getMatrixElement(0, j)); row2.push_back(transformationMatrix->getMatrixElement(1, j)); row3.push_back(transformationMatrix->getMatrixElement(2, j)); row4.push_back(transformationMatrix->getMatrixElement(3, j)); } std::vector > indexToSpace; indexToSpace.push_back(row1); indexToSpace.push_back(row2); indexToSpace.push_back(row3); indexToSpace.push_back(row4); int64_t numComponents = 1; SubvolumeAttributes::VolumeType whatType = SubvolumeAttributes::FUNCTIONAL; switch (colorMode) { case CONVERT_TO_VOLUME_COLOR_GRAYSCALE: break; case CONVERT_TO_VOLUME_COLOR_RGB: numComponents = 3; whatType = SubvolumeAttributes::RGB; break; } VolumeFile* volumeFile = new VolumeFile(dimensions, indexToSpace, numComponents, whatType); FileInformation fileInfo(getFileName()); const AString volumeFileName = FileInformation::assembleFileComponents(fileInfo.getAbsolutePath(), fileInfo.getFileNameNoExtension(), DataFileTypeEnum::toFileExtension(DataFileTypeEnum::VOLUME)); volumeFile->setFileName(volumeFileName); int32_t rgbaIndex = 0; const int64_t k = 0; const int64_t mapIndex = 0; for (int64_t j = 0; j < height; j++) { for (int64_t i = 0; i < width; i++) { switch (colorMode) { case CONVERT_TO_VOLUME_COLOR_GRAYSCALE: { /* * Luminosity conversion from GIMP * http://docs.gimp.org/2.6/en/gimp-tool-desaturate.html */ float intensity = ((rgbaBytes[rgbaIndex] * 0.21) + (rgbaBytes[rgbaIndex + 1] * 0.72) + (rgbaBytes[rgbaIndex + 2] * 0.07)); if (intensity > 255.0) intensity = 255.0; else if (intensity < 0.0) intensity = 0.0; if (rgbaBytes[rgbaIndex + 3] <= 0.0) { intensity = 0.0; } volumeFile->setValue(intensity, i, j, k, mapIndex, 0); rgbaIndex += 4; } break; case CONVERT_TO_VOLUME_COLOR_RGB: { CaretAssertVectorIndex(rgbaBytes, rgbaIndex); volumeFile->setValue(rgbaBytes[rgbaIndex], i, j, k, mapIndex, 0); CaretAssertVectorIndex(rgbaBytes, rgbaIndex); volumeFile->setValue(rgbaBytes[rgbaIndex+1], i, j, k, mapIndex, 1); CaretAssertVectorIndex(rgbaBytes, rgbaIndex); volumeFile->setValue(rgbaBytes[rgbaIndex+2], i, j, k, mapIndex, 2); if (numComponents == 4) { CaretAssertVectorIndex(rgbaBytes, rgbaIndex); volumeFile->setValue(rgbaBytes[rgbaIndex+3], i, j, k, mapIndex, 3); } rgbaIndex += 4; } break; } } } switch (colorMode) { case CONVERT_TO_VOLUME_COLOR_GRAYSCALE: { PaletteColorMapping* pcm = volumeFile->getMapPaletteColorMapping(mapIndex); pcm->setSelectedPaletteToGrayInterpolated(); pcm->setDisplayNegativeDataFlag(false); pcm->setDisplayZeroDataFlag(false); pcm->setDisplayPositiveDataFlag(true); pcm->setScaleMode(PaletteScaleModeEnum::MODE_AUTO_SCALE); } break; case CONVERT_TO_VOLUME_COLOR_RGB: break; } volumeFile->clearVoxelColoringForMap(mapIndex); volumeFile->updateScalarColoringForMap(mapIndex, paletteFile); return volumeFile; } /** * @return The control point file. */ ControlPointFile* ImageFile::getControlPointFile() { return m_controlPointFile; } /** * @return The control point file. */ const ControlPointFile* ImageFile::getControlPointFile() const { return m_controlPointFile; } /** * Save file data from the scene. For subclasses that need to * save to a scene, this method should be overriden. sceneClass * will be valid and any scene data should be added to it. * * @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. */ void ImageFile::saveFileDataToScene(const SceneAttributes* sceneAttributes, SceneClass* sceneClass) { if (m_controlPointFile != NULL) { sceneClass->addClass(m_controlPointFile->saveToScene(sceneAttributes, "m_controlPointFile")); } } /** * Restore file data from the scene. For subclasses that need to * restore from a scene, this method should be overridden. The scene class * will be valid and any scene data may be obtained from it. * * @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 ImageFile::restoreFileDataFromScene(const SceneAttributes* sceneAttributes, const SceneClass* sceneClass) { m_controlPointFile->restoreFromScene(sceneAttributes, sceneClass->getClass("m_controlPointFile")); } connectome-workbench-1.2.3+git41-gc4c6c90/src/Files/ImageFile.h000066400000000000000000000171141300200146000236050ustar00rootroot00000000000000/*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*/ #ifndef __IMAGE_FILE_H__ #define __IMAGE_FILE_H__ #include "CaretDataFile.h" #include "CaretPointer.h" class QImage; namespace caret { class ControlPointFile; class ControlPoint3D; class PaletteFile; class VolumeFile; /// File for images class ImageFile : public CaretDataFile { public: // class ControlPoint { // public: // ControlPoint(const float i, // const float j, // const float k, // const float x, // const float y, // const float z) // : i(i), j(j), k(k), x(x), y(y), z(z) { } // // void getSource(double pt[3]) const { pt[0] = i; pt[1] = j; pt[2] = k; } // // void getTarget(double pt[3]) const { pt[0] = x; pt[1] = y; pt[2] = z; } // // const float i; // const float j; // const float k; // const float x; // const float y; // const float z; // }; /** * Location of origin in image data. */ enum IMAGE_DATA_ORIGIN_LOCATION { /** Origin at bottom (OpenGL has origin at bottom) */ IMAGE_DATA_ORIGIN_AT_BOTTOM, /** Origin at top (most image formats have origin at top) */ IMAGE_DATA_ORIGIN_AT_TOP }; /** * Convert to volume color mode */ enum CONVERT_TO_VOLUME_COLOR_MODE { /** Create single component grayscale volume */ CONVERT_TO_VOLUME_COLOR_GRAYSCALE, /** Create three component RGB volume */ CONVERT_TO_VOLUME_COLOR_RGB }; // enum LANDMARK_MODE { // VTK_LANDMARK_AFFINE, // VTK_LANDMARK_RIGIDBODY, // VTK_LANDMARK_SIMILARITY // }; ImageFile(); ImageFile(const unsigned char* imageDataRGBA, const int imageWidth, const int imageHeight, const IMAGE_DATA_ORIGIN_LOCATION imageOrigin); ImageFile(const QImage& img); ~ImageFile(); void appendImageAtBottom(const ImageFile& img); void clear(); /** * @return The structure for this file. */ virtual StructureEnum::Enum getStructure() const; /** * Set the structure for this file. * @param structure * New structure for this file. */ virtual void setStructure(const StructureEnum::Enum structure); /** * @return Get access to the file's metadata. */ virtual GiftiMetaData* getFileMetaData(); /** * @return Get access to unmodifiable file's metadata. */ virtual const GiftiMetaData* getFileMetaData() const; virtual bool compareFileForUnitTesting(const DataFile* df, const float tolerance, AString& messageOut) const; bool isEmpty() const; //QImage* getAsQImage(); const QImage* getAsQImage() const; void setFromQImage(const QImage& img); bool getImageBytesRGBA(const IMAGE_DATA_ORIGIN_LOCATION imageOrigin, std::vector& bytesRGBA, int32_t& widthOut, int32_t& heightOut) const; bool getImageResizedBytes(const IMAGE_DATA_ORIGIN_LOCATION imageOrigin, const int32_t resizeToWidth, const int32_t resizeToHeight, std::vector& bytesRGBAOut) const; bool getImagePixelRGBA(const IMAGE_DATA_ORIGIN_LOCATION imageOrigin, const int32_t pixelI, const int32_t pixelJ, uint8_t pixelRGBAOut[4]) const; int32_t getWidth() const; int32_t getHeight() const; virtual void readFile(const AString& filename); virtual void writeFile(const AString& filename); void cropImageRemoveBackground(const int marginSize, const uint8_t backgroundColor[3]); void findImageObject(const uint8_t backgroundColor[3], int objectBoundsOut[4]) const; void addMargin(const int marginSize, const uint8_t backgroundColor[3]); void addMargin(const int marginSizeX, const int marginSizeY, const uint8_t backgroundColor[3]); void setDotsPerMeter(const int x, const int y); void resizeToMaximumWidthOrHeight(const int32_t maximumWidthOrHeight); void resizeToMaximumWidth(const int32_t maximumWidth); void resizeToMaximumHeight(const int32_t maximumHeight); void resizeToHeight(const int32_t height); void resizeToWidth(const int32_t width); void getImageInByteArray(QByteArray& byteArrayOut, const AString& format) const; void setImageFromByteArray(const QByteArray& byteArray, const AString& format); void combinePreservingAspectAndFillIfNeeded(const std::vector& imageFiles, const int numImagesPerRow, const uint8_t backgroundColor[3]); static void getImageFileExtensions(std::vector& imageFileExtensions, AString& defaultExtension); static void getImageFileFilters(std::vector& imageFileFilters, AString& defaultFilter); VolumeFile* convertToVolumeFile(const CONVERT_TO_VOLUME_COLOR_MODE colorMode, const PaletteFile* paletteFile, AString& errorMessageOut) const; ControlPointFile* getControlPointFile(); const ControlPointFile* getControlPointFile() const; virtual void saveFileDataToScene(const SceneAttributes* sceneAttributes, SceneClass* sceneClass); virtual void restoreFileDataFromScene(const SceneAttributes* sceneAttributes, const SceneClass* sceneClass); private: ImageFile(const ImageFile&); ImageFile& operator=(const ImageFile&); void insertImage(const QImage& otherImage, const int x, const int y); static void insertImage(const QImage& insertThisImage, QImage& intoThisImage, const int positionX, const int positionY); QImage* m_image; CaretPointer m_fileMetaData; CaretPointer m_controlPointFile; static const float s_defaultWindowDepthPercentage; }; } // namespace #endif // __IMAGE_FILE_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Files/ImageResolutionUnitsEnum.cxx000066400000000000000000000253701300200146000273170ustar00rootroot00000000000000 /*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 __IMAGE_RESOLUTION_UNITS_ENUM_DECLARE__ #include "ImageResolutionUnitsEnum.h" #undef __IMAGE_RESOLUTION_UNITS_ENUM_DECLARE__ #include "CaretAssert.h" using namespace caret; /** * \class caret::ImageResolutionUnitsEnum * \brief Pixel per spatial unit (inches, cm) * * 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_imagePixelsPerSpatialUnitsEnumComboBox; * * Declare a slot that is called when user changes selection * private slots: * void imagePixelsPerSpatialUnitsEnumComboBoxItemActivated(); * * Implementation File (.cxx) * Include the header files * #include "EnumComboBoxTemplate.h" * #include "ImageResolutionUnitsEnum.h" * * Instatiate: * m_imagePixelsPerSpatialUnitsEnumComboBox = new EnumComboBoxTemplate(this); * m_imagePixelsPerSpatialUnitsEnumComboBox->setup(); * * Get notified when the user changes the selection: * QObject::connect(m_imagePixelsPerSpatialUnitsEnumComboBox, SIGNAL(itemActivated()), * this, SLOT(imagePixelsPerSpatialUnitsEnumComboBoxItemActivated())); * * Update the selection: * m_imagePixelsPerSpatialUnitsEnumComboBox->setSelectedItem(NEW_VALUE); * * Read the selection: * const ImageResolutionUnitsEnum::Enum VARIABLE = m_imagePixelsPerSpatialUnitsEnumComboBox->getSelectedItem(); * */ /** * Constructor. * * @param enumValue * An enumerated value. * @param name * Name of enumerated value. * * @param guiName * User-friendly name for use in user-interface. */ ImageResolutionUnitsEnum::ImageResolutionUnitsEnum(const Enum enumValue, const AString& name, const AString& guiName) { this->enumValue = enumValue; this->integerCode = integerCodeCounter++; this->name = name; this->guiName = guiName; } /** * Destructor. */ ImageResolutionUnitsEnum::~ImageResolutionUnitsEnum() { } /** * Initialize the enumerated metadata. */ void ImageResolutionUnitsEnum::initialize() { if (initializedFlag) { return; } initializedFlag = true; enumData.push_back(ImageResolutionUnitsEnum(PIXELS_PER_INCH, "PIXELS_PER_INCH", "pixels/inch")); enumData.push_back(ImageResolutionUnitsEnum(PIXEL_PER_CENTIMETER, "PIXEL_PER_CENTIMETER", "pixels/cm")); } /** * 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 ImageResolutionUnitsEnum* ImageResolutionUnitsEnum::findData(const Enum enumValue) { if (initializedFlag == false) initialize(); size_t num = enumData.size(); for (size_t i = 0; i < num; i++) { const ImageResolutionUnitsEnum* 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 ImageResolutionUnitsEnum::toName(Enum enumValue) { if (initializedFlag == false) initialize(); const ImageResolutionUnitsEnum* 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. */ ImageResolutionUnitsEnum::Enum ImageResolutionUnitsEnum::fromName(const AString& name, bool* isValidOut) { if (initializedFlag == false) initialize(); bool validFlag = false; Enum enumValue = ImageResolutionUnitsEnum::enumData[0].enumValue; for (std::vector::iterator iter = enumData.begin(); iter != enumData.end(); iter++) { const ImageResolutionUnitsEnum& 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 ImageResolutionUnitsEnum")); } return enumValue; } /** * Get a GUI string representation of the enumerated type. * @param enumValue * Enumerated value. * @return * String representing enumerated value. */ AString ImageResolutionUnitsEnum::toGuiName(Enum enumValue) { if (initializedFlag == false) initialize(); const ImageResolutionUnitsEnum* 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. */ ImageResolutionUnitsEnum::Enum ImageResolutionUnitsEnum::fromGuiName(const AString& guiName, bool* isValidOut) { if (initializedFlag == false) initialize(); bool validFlag = false; Enum enumValue = ImageResolutionUnitsEnum::enumData[0].enumValue; for (std::vector::iterator iter = enumData.begin(); iter != enumData.end(); iter++) { const ImageResolutionUnitsEnum& 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 ImageResolutionUnitsEnum")); } return enumValue; } /** * Get the integer code for a data type. * * @return * Integer code for data type. */ int32_t ImageResolutionUnitsEnum::toIntegerCode(Enum enumValue) { if (initializedFlag == false) initialize(); const ImageResolutionUnitsEnum* 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. */ ImageResolutionUnitsEnum::Enum ImageResolutionUnitsEnum::fromIntegerCode(const int32_t integerCode, bool* isValidOut) { if (initializedFlag == false) initialize(); bool validFlag = false; Enum enumValue = ImageResolutionUnitsEnum::enumData[0].enumValue; for (std::vector::iterator iter = enumData.begin(); iter != enumData.end(); iter++) { const ImageResolutionUnitsEnum& 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 ImageResolutionUnitsEnum")); } 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 ImageResolutionUnitsEnum::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 ImageResolutionUnitsEnum::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(ImageResolutionUnitsEnum::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 ImageResolutionUnitsEnum::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(ImageResolutionUnitsEnum::toGuiName(iter->enumValue)); } if (isSorted) { std::sort(allGuiNames.begin(), allGuiNames.end()); } } connectome-workbench-1.2.3+git41-gc4c6c90/src/Files/ImageResolutionUnitsEnum.h000066400000000000000000000062341300200146000267420ustar00rootroot00000000000000#ifndef __IMAGE_RESOLUTION_UNITS_ENUM_H__ #define __IMAGE_RESOLUTION_UNITS_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 ImageResolutionUnitsEnum { public: /** * Enumerated values. */ enum Enum { /** Pixels per inch */ PIXELS_PER_INCH, /** Pixels per centimeter */ PIXEL_PER_CENTIMETER }; ~ImageResolutionUnitsEnum(); 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: ImageResolutionUnitsEnum(const Enum enumValue, const AString& name, const AString& guiName); static const ImageResolutionUnitsEnum* 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 __IMAGE_RESOLUTION_UNITS_ENUM_DECLARE__ std::vector ImageResolutionUnitsEnum::enumData; bool ImageResolutionUnitsEnum::initializedFlag = false; int32_t ImageResolutionUnitsEnum::integerCodeCounter = 0; #endif // __IMAGE_RESOLUTION_UNITS_ENUM_DECLARE__ } // namespace #endif //__IMAGE_RESOLUTION_UNITS_ENUM_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Files/ImageSpatialUnitsEnum.cxx000066400000000000000000000251651300200146000265530ustar00rootroot00000000000000 /*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 __IMAGE_SPATIAL_UNITS_ENUM_DECLARE__ #include "ImageSpatialUnitsEnum.h" #undef __IMAGE_SPATIAL_UNITS_ENUM_DECLARE__ #include "CaretAssert.h" using namespace caret; /** * \class caret::ImageSpatialUnitsEnum * \brief Units of image resolution * * 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_imageSpatialUnitsEnumComboBox; * * Declare a slot that is called when user changes selection * private slots: * void imageSpatialUnitsEnumComboBoxItemActivated(); * * Implementation File (.cxx) * Include the header files * #include "EnumComboBoxTemplate.h" * #include "ImageSpatialUnitsEnum.h" * * Instatiate: * m_imageSpatialUnitsEnumComboBox = new EnumComboBoxTemplate(this); * m_imageSpatialUnitsEnumComboBox->setup(); * * Get notified when the user changes the selection: * QObject::connect(m_imageSpatialUnitsEnumComboBox, SIGNAL(itemActivated()), * this, SLOT(imageSpatialUnitsEnumComboBoxItemActivated())); * * Update the selection: * m_imageSpatialUnitsEnumComboBox->setSelectedItem(NEW_VALUE); * * Read the selection: * const ImageSpatialUnitsEnum::Enum VARIABLE = m_imageSpatialUnitsEnumComboBox->getSelectedItem(); * */ /** * Constructor. * * @param enumValue * An enumerated value. * @param name * Name of enumerated value. * * @param guiName * User-friendly name for use in user-interface. */ ImageSpatialUnitsEnum::ImageSpatialUnitsEnum(const Enum enumValue, const AString& name, const AString& guiName) { this->enumValue = enumValue; this->integerCode = integerCodeCounter++; this->name = name; this->guiName = guiName; } /** * Destructor. */ ImageSpatialUnitsEnum::~ImageSpatialUnitsEnum() { } /** * Initialize the enumerated metadata. */ void ImageSpatialUnitsEnum::initialize() { if (initializedFlag) { return; } initializedFlag = true; enumData.push_back(ImageSpatialUnitsEnum(INCHES, "INCHES", "inches")); enumData.push_back(ImageSpatialUnitsEnum(CENTIMETERS, "CENTIMETERS", "cm")); enumData.push_back(ImageSpatialUnitsEnum(MILLIMETERS, "MILLIMETERS", "mm")); } /** * 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 ImageSpatialUnitsEnum* ImageSpatialUnitsEnum::findData(const Enum enumValue) { if (initializedFlag == false) initialize(); size_t num = enumData.size(); for (size_t i = 0; i < num; i++) { const ImageSpatialUnitsEnum* 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 ImageSpatialUnitsEnum::toName(Enum enumValue) { if (initializedFlag == false) initialize(); const ImageSpatialUnitsEnum* 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. */ ImageSpatialUnitsEnum::Enum ImageSpatialUnitsEnum::fromName(const AString& name, bool* isValidOut) { if (initializedFlag == false) initialize(); bool validFlag = false; Enum enumValue = ImageSpatialUnitsEnum::enumData[0].enumValue; for (std::vector::iterator iter = enumData.begin(); iter != enumData.end(); iter++) { const ImageSpatialUnitsEnum& 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 ImageSpatialUnitsEnum")); } return enumValue; } /** * Get a GUI string representation of the enumerated type. * @param enumValue * Enumerated value. * @return * String representing enumerated value. */ AString ImageSpatialUnitsEnum::toGuiName(Enum enumValue) { if (initializedFlag == false) initialize(); const ImageSpatialUnitsEnum* 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. */ ImageSpatialUnitsEnum::Enum ImageSpatialUnitsEnum::fromGuiName(const AString& guiName, bool* isValidOut) { if (initializedFlag == false) initialize(); bool validFlag = false; Enum enumValue = ImageSpatialUnitsEnum::enumData[0].enumValue; for (std::vector::iterator iter = enumData.begin(); iter != enumData.end(); iter++) { const ImageSpatialUnitsEnum& 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 ImageSpatialUnitsEnum")); } return enumValue; } /** * Get the integer code for a data type. * * @return * Integer code for data type. */ int32_t ImageSpatialUnitsEnum::toIntegerCode(Enum enumValue) { if (initializedFlag == false) initialize(); const ImageSpatialUnitsEnum* 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. */ ImageSpatialUnitsEnum::Enum ImageSpatialUnitsEnum::fromIntegerCode(const int32_t integerCode, bool* isValidOut) { if (initializedFlag == false) initialize(); bool validFlag = false; Enum enumValue = ImageSpatialUnitsEnum::enumData[0].enumValue; for (std::vector::iterator iter = enumData.begin(); iter != enumData.end(); iter++) { const ImageSpatialUnitsEnum& 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 ImageSpatialUnitsEnum")); } 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 ImageSpatialUnitsEnum::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 ImageSpatialUnitsEnum::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(ImageSpatialUnitsEnum::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 ImageSpatialUnitsEnum::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(ImageSpatialUnitsEnum::toGuiName(iter->enumValue)); } if (isSorted) { std::sort(allGuiNames.begin(), allGuiNames.end()); } } connectome-workbench-1.2.3+git41-gc4c6c90/src/Files/ImageSpatialUnitsEnum.h000066400000000000000000000061751300200146000262000ustar00rootroot00000000000000#ifndef __IMAGE_SPATIAL_UNITS_ENUM_H__ #define __IMAGE_SPATIAL_UNITS_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 ImageSpatialUnitsEnum { public: /** * Enumerated values. */ enum Enum { /** Inches */ INCHES, /** Centimeters */ CENTIMETERS, /** Millimeters */ MILLIMETERS }; ~ImageSpatialUnitsEnum(); 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: ImageSpatialUnitsEnum(const Enum enumValue, const AString& name, const AString& guiName); static const ImageSpatialUnitsEnum* 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 __IMAGE_SPATIAL_UNITS_ENUM_DECLARE__ std::vector ImageSpatialUnitsEnum::enumData; bool ImageSpatialUnitsEnum::initializedFlag = false; int32_t ImageSpatialUnitsEnum::integerCodeCounter = 0; #endif // __IMAGE_SPATIAL_UNITS_ENUM_DECLARE__ } // namespace #endif //__IMAGE_SPATIAL_UNITS_ENUM_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Files/LabelDrawingProperties.cxx000066400000000000000000000116521300200146000267470ustar00rootroot00000000000000 /*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 __LABEL_DRAWING_PROPERTIES_DECLARE__ #include "LabelDrawingProperties.h" #undef __LABEL_DRAWING_PROPERTIES_DECLARE__ #include "CaretAssert.h" #include "SceneClass.h" #include "SceneClassAssistant.h" using namespace caret; /** * \class caret::LabelDrawingProperties * \brief File properties for labels. * \ingroup Files */ /** * Constructor. */ LabelDrawingProperties::LabelDrawingProperties() : CaretObject() { m_drawingType = LabelDrawingTypeEnum::DRAW_FILLED; m_outlineColor = CaretColorEnum::BLACK; m_drawMedialWallFilled = true; m_sceneAssistant = new SceneClassAssistant(); m_sceneAssistant->add("m_drawingType", &m_drawingType); m_sceneAssistant->add("m_outlineColor", &m_outlineColor); m_sceneAssistant->add("m_drawMedialWallFilled", &m_drawMedialWallFilled); } /** * Destructor. */ LabelDrawingProperties::~LabelDrawingProperties() { delete m_sceneAssistant; } /** * @return The drawing type. */ LabelDrawingTypeEnum::Enum LabelDrawingProperties::getDrawingType() const { return m_drawingType; } /** * Set the drawing type to the given value. * @param drawingType * New value for drawing type. */ void LabelDrawingProperties::setDrawingType(const LabelDrawingTypeEnum::Enum drawingType) { m_drawingType = drawingType; } /** * @param displayGroup * Display group. * @return The outline color. */ CaretColorEnum::Enum LabelDrawingProperties::getOutlineColor() const { return m_outlineColor; } /** * Set the outline color to the given value. * @param outlineColor * New value for outline color. */ void LabelDrawingProperties::setOutlineColor(const CaretColorEnum::Enum outlineColor) { m_outlineColor = outlineColor; } /** * @return medial wall is drawn filled */ bool LabelDrawingProperties::isDrawMedialWallFilled() const { return m_drawMedialWallFilled; } /** * Set medial wall is drawn filled * @param drawMedialWallFilled * New value for medial wall is drawn filled */ void LabelDrawingProperties::setDrawMedialWallFilled(const bool drawMedialWallFilled) { m_drawMedialWallFilled = drawMedialWallFilled; } /** * 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* LabelDrawingProperties::saveToScene(const SceneAttributes* sceneAttributes, const AString& instanceName) { SceneClass* sceneClass = new SceneClass(instanceName, "LabelDrawingProperties", 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 LabelDrawingProperties::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); } connectome-workbench-1.2.3+git41-gc4c6c90/src/Files/LabelDrawingProperties.h000066400000000000000000000063501300200146000263730ustar00rootroot00000000000000#ifndef __LABEL_DRAWING_PROPERTIES_H__ #define __LABEL_DRAWING_PROPERTIES_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 "CaretColorEnum.h" #include "LabelDrawingTypeEnum.h" #include "SceneableInterface.h" namespace caret { class SceneClassAssistant; class LabelDrawingProperties : public CaretObject, public SceneableInterface { public: LabelDrawingProperties(); virtual ~LabelDrawingProperties(); LabelDrawingTypeEnum::Enum getDrawingType() const; void setDrawingType(const LabelDrawingTypeEnum::Enum drawingType); CaretColorEnum::Enum getOutlineColor() const; void setOutlineColor(const CaretColorEnum::Enum outlineColor); bool isDrawMedialWallFilled() const; void setDrawMedialWallFilled(const bool drawMedialWallFilled); // 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 implemetation by sub-classes. // protected: // virtual void saveSubClassDataToScene(const SceneAttributes* sceneAttributes, // SceneClass* sceneClass) = 0; // // virtual void restoreSubClassDataFromScene(const SceneAttributes* sceneAttributes, // const SceneClass* sceneClass) = 0; private: LabelDrawingProperties(const LabelDrawingProperties&); LabelDrawingProperties& operator=(const LabelDrawingProperties&); SceneClassAssistant* m_sceneAssistant; LabelDrawingTypeEnum::Enum m_drawingType; CaretColorEnum::Enum m_outlineColor; /** medial wall is drawn filled*/ bool m_drawMedialWallFilled; // ADD_NEW_MEMBERS_HERE }; #ifdef __LABEL_DRAWING_PROPERTIES_DECLARE__ // #endif // __LABEL_DRAWING_PROPERTIES_DECLARE__ } // namespace #endif //__LABEL_DRAWING_PROPERTIES_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Files/LabelDrawingTypeEnum.cxx000066400000000000000000000227371300200146000263670ustar00rootroot00000000000000 /*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 __LABEL_DRAWING_TYPE_ENUM_DECLARE__ #include "LabelDrawingTypeEnum.h" #undef __LABEL_DRAWING_TYPE_ENUM_DECLARE__ #include "CaretAssert.h" using namespace caret; /** * \class caret::LabelDrawingTypeEnum * \brief Drawing type for labels. * \ingroup Files */ /** * Constructor. * * @param enumValue * An enumerated value. * @param name * Name of enumerated value. * * @param guiName * User-friendly name for use in user-interface. */ LabelDrawingTypeEnum::LabelDrawingTypeEnum(const Enum enumValue, const AString& name, const AString& guiName) { this->enumValue = enumValue; this->integerCode = integerCodeCounter++; this->name = name; this->guiName = guiName; } /** * Destructor. */ LabelDrawingTypeEnum::~LabelDrawingTypeEnum() { } /** * Initialize the enumerated metadata. */ void LabelDrawingTypeEnum::initialize() { if (initializedFlag) { return; } initializedFlag = true; enumData.push_back(LabelDrawingTypeEnum(DRAW_FILLED, "DRAW_FILLED", "Filled")); enumData.push_back(LabelDrawingTypeEnum(DRAW_FILLED_WITH_OUTLINE_COLOR, "DRAW_FILLED_WITH_OUTLINE_COLOR", "Filled and Outline Color")); enumData.push_back(LabelDrawingTypeEnum(DRAW_OUTLINE_COLOR, "DRAW_OUTLINE_COLOR", "Outline Color")); enumData.push_back(LabelDrawingTypeEnum(DRAW_OUTLINE_LABEL_COLOR, "DRAW_OUTLINE_LABEL_COLOR", "Outline Label Color")); } /** * 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 LabelDrawingTypeEnum* LabelDrawingTypeEnum::findData(const Enum enumValue) { if (initializedFlag == false) initialize(); size_t num = enumData.size(); for (size_t i = 0; i < num; i++) { const LabelDrawingTypeEnum* 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 LabelDrawingTypeEnum::toName(Enum enumValue) { if (initializedFlag == false) initialize(); const LabelDrawingTypeEnum* 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. */ LabelDrawingTypeEnum::Enum LabelDrawingTypeEnum::fromName(const AString& nameIn, bool* isValidOut) { if (initializedFlag == false) initialize(); bool validFlag = false; Enum enumValue = DRAW_FILLED; AString name = nameIn; for (std::vector::iterator iter = enumData.begin(); iter != enumData.end(); iter++) { const LabelDrawingTypeEnum& 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 LabelDrawingTypeEnum")); } return enumValue; } /** * Get a GUI string representation of the enumerated type. * @param enumValue * Enumerated value. * @return * String representing enumerated value. */ AString LabelDrawingTypeEnum::toGuiName(Enum enumValue) { if (initializedFlag == false) initialize(); const LabelDrawingTypeEnum* 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. */ LabelDrawingTypeEnum::Enum LabelDrawingTypeEnum::fromGuiName(const AString& guiName, bool* isValidOut) { if (initializedFlag == false) initialize(); bool validFlag = false; Enum enumValue = DRAW_FILLED; for (std::vector::iterator iter = enumData.begin(); iter != enumData.end(); iter++) { const LabelDrawingTypeEnum& 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 LabelDrawingTypeEnum")); } return enumValue; } /** * Get the integer code for a data type. * * @return * Integer code for data type. */ int32_t LabelDrawingTypeEnum::toIntegerCode(Enum enumValue) { if (initializedFlag == false) initialize(); const LabelDrawingTypeEnum* 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. */ LabelDrawingTypeEnum::Enum LabelDrawingTypeEnum::fromIntegerCode(const int32_t integerCode, bool* isValidOut) { if (initializedFlag == false) initialize(); bool validFlag = false; Enum enumValue = DRAW_FILLED; for (std::vector::iterator iter = enumData.begin(); iter != enumData.end(); iter++) { const LabelDrawingTypeEnum& 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 LabelDrawingTypeEnum")); } 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 LabelDrawingTypeEnum::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 LabelDrawingTypeEnum::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(LabelDrawingTypeEnum::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 LabelDrawingTypeEnum::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(LabelDrawingTypeEnum::toGuiName(iter->enumValue)); } if (isSorted) { std::sort(allGuiNames.begin(), allGuiNames.end()); } } connectome-workbench-1.2.3+git41-gc4c6c90/src/Files/LabelDrawingTypeEnum.h000066400000000000000000000064271300200146000260120ustar00rootroot00000000000000#ifndef __LABEL_DRAWING_TYPE_ENUM__H_ #define __LABEL_DRAWING_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 LabelDrawingTypeEnum { public: /** * Enumerated values. */ enum Enum { /** Filled in label's color */ DRAW_FILLED, /** Filled in label's color with outline color */ DRAW_FILLED_WITH_OUTLINE_COLOR, /** Outline with Outline Color */ DRAW_OUTLINE_COLOR, /** Outline with label color */ DRAW_OUTLINE_LABEL_COLOR }; ~LabelDrawingTypeEnum(); 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: LabelDrawingTypeEnum(const Enum enumValue, const AString& name, const AString& guiName); static const LabelDrawingTypeEnum* 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 __LABEL_DRAWING_TYPE_ENUM_DECLARE__ std::vector LabelDrawingTypeEnum::enumData; bool LabelDrawingTypeEnum::initializedFlag = false; int32_t LabelDrawingTypeEnum::integerCodeCounter = 0; #endif // __LABEL_DRAWING_TYPE_ENUM_DECLARE__ } // namespace #endif //__LABEL_DRAWING_TYPE_ENUM__H_ connectome-workbench-1.2.3+git41-gc4c6c90/src/Files/LabelFile.cxx000066400000000000000000000403501300200146000241530ustar00rootroot00000000000000/*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 "CaretAssert.h" #include "CaretLogger.h" #include "GroupAndNameHierarchyModel.h" #include "DataFileException.h" #include "DataFileTypeEnum.h" #include "GiftiFile.h" #include "GiftiLabel.h" #include "MathFunctions.h" #include "LabelFile.h" using namespace caret; /** * Constructor. */ LabelFile::LabelFile() : GiftiTypeFile(DataFileTypeEnum::LABEL) { m_classNameHierarchy = NULL; this->initializeMembersLabelFile(); } /** * Copy constructor. * * @param sf * Surface file that is copied. */ LabelFile::LabelFile(const LabelFile& sf) : GiftiTypeFile(sf) { m_classNameHierarchy = NULL; this->copyHelperLabelFile(sf); } /** * Assignment operator. * * @param sf * Surface file that is copied. * @return * This surface file with content replaced * by the LabelFile parameter. */ LabelFile& LabelFile::operator=(const LabelFile& sf) { if (this != &sf) { GiftiTypeFile::operator=(sf); this->copyHelperLabelFile(sf); } return *this; } /** * Destructor. */ LabelFile::~LabelFile() { this->columnDataPointers.clear(); delete m_classNameHierarchy; } void LabelFile::writeFile(const AString& filename) { if (!filename.endsWith(".label.gii")) { CaretLogWarning("label file '" + filename + "' should be saved ending in .label.gii, see wb_command -gifti-help"); } caret::GiftiTypeFile::writeFile(filename); } /** * Clear the surface file. */ void LabelFile::clear() { GiftiTypeFile::clear(); this->columnDataPointers.clear(); m_classNameHierarchy->clear(); } /** * @return Return the GIFTI Label Table. */ GiftiLabelTable* LabelFile::getLabelTable() { return this->giftiFile->getLabelTable(); } /** * @return Return the GIFTI Label Table. */ const GiftiLabelTable* LabelFile::getLabelTable() const { return this->giftiFile->getLabelTable(); } /** * @return The class and name hierarchy. */ GroupAndNameHierarchyModel* LabelFile::getGroupAndNameHierarchyModel() { m_classNameHierarchy->update(this, m_forceUpdateOfGroupAndNameHierarchy); m_forceUpdateOfGroupAndNameHierarchy = false; return m_classNameHierarchy; } /** * @return The class and name hierarchy. */ const GroupAndNameHierarchyModel* LabelFile::getGroupAndNameHierarchyModel() const { m_classNameHierarchy->update(const_cast(this), m_forceUpdateOfGroupAndNameHierarchy); m_forceUpdateOfGroupAndNameHierarchy = false; return m_classNameHierarchy; } /** * Validate the contents of the file after it * has been read such as correct number of * data arrays and proper data types/dimensions. */ void LabelFile::validateDataArraysAfterReading() { this->columnDataPointers.clear(); this->initializeMembersLabelFile(); this->verifyDataArraysHaveSameNumberOfRows(0, 0); bool haveWarned = false; const int32_t numberOfDataArrays = this->giftiFile->getNumberOfDataArrays(); for (int32_t i = 0; i < numberOfDataArrays; i++) { GiftiDataArray* thisArray = this->giftiFile->getDataArray(i); if (thisArray->getDataType() != NiftiDataTypeEnum::NIFTI_TYPE_INT32) { thisArray->convertToDataType(NiftiDataTypeEnum::NIFTI_TYPE_INT32); if (!haveWarned) { CaretLogWarning("label file '" + getFileName() + "' contains data array with data type other than int32"); haveWarned = true; } } int32_t* tempPointer = thisArray->getDataPointerInt(); CaretAssert(tempPointer != NULL); if (tempPointer == NULL) throw DataFileException(getFileName(), "failed to convert data array to int32.");//shouldn't happen, can probably be removed this->columnDataPointers.push_back(tempPointer); } validateKeysAndLabels(); m_classNameHierarchy->update(this, true); m_forceUpdateOfGroupAndNameHierarchy = false; m_classNameHierarchy->setAllSelected(true); CaretLogFiner("CLASS/NAME Table for : " + this->getFileNameNoPath() + "\n" + m_classNameHierarchy->toString()); } /** * Validate keys and labels in the file. */ void LabelFile::validateKeysAndLabels() const { /* * Skip if logging is not fine or less. */ if (CaretLogger::getLogger()->isFine() == false) { return; } AString message; /* * Find the label keys that are in the data */ std::set dataKeys; const int32_t numNodes = getNumberOfNodes(); const int32_t numMaps = getNumberOfMaps(); for (int32_t iNode = 0; iNode < numNodes; iNode++) { for (int32_t jMap = 0; jMap < numMaps; jMap++) { const int32_t key = getLabelKey(iNode, jMap); dataKeys.insert(key); } } /* * Find any keys that are not in the label table */ const GiftiLabelTable* labelTable = getLabelTable(); std::set missingLabelKeys; for (std::set::iterator dataKeyIter = dataKeys.begin(); dataKeyIter != dataKeys.end(); dataKeyIter++) { const int32_t dataKey = *dataKeyIter; const GiftiLabel* label = labelTable->getLabel(dataKey); if (label == NULL) { missingLabelKeys.insert(dataKey); } } if (missingLabelKeys.empty() == false) { for (std::set::iterator missingKeyIter = missingLabelKeys.begin(); missingKeyIter != missingLabelKeys.end(); missingKeyIter++) { const int32_t missingKey = *missingKeyIter; message.appendWithNewLine(" Missing Label for Key: " + AString::number(missingKey)); } } /* * Find any label table names that are not used */ std::map labelTableKeysAndNames; labelTable->getKeysAndNames(labelTableKeysAndNames); for (std::map::const_iterator ltIter = labelTableKeysAndNames.begin(); ltIter != labelTableKeysAndNames.end(); ltIter++) { const int32_t ltKey = ltIter->first; if (std::find(dataKeys.begin(), dataKeys.end(), ltKey) == dataKeys.end()) { message.appendWithNewLine(" Label Not Used Key=" + AString::number(ltKey) + ": " + ltIter->second); } } AString msg = ("File: " + getFileName() + "\n" + labelTable->toFormattedString(" ") + message); CaretLogFine(msg); } /** * Get the number of nodes. * * @return * The number of nodes. */ int32_t LabelFile::getNumberOfNodes() const { int32_t numNodes = 0; int32_t numDataArrays = this->giftiFile->getNumberOfDataArrays(); if (numDataArrays > 0) { numNodes = this->giftiFile->getDataArray(0)->getNumberOfRows(); } return numNodes; } /** * Get the number of columns. * * @return * The number of columns. */ int32_t LabelFile::getNumberOfColumns() const { const int32_t numCols = this->giftiFile->getNumberOfDataArrays(); return numCols; } /** * Initialize members of this class. */ void LabelFile::initializeMembersLabelFile() { if (m_classNameHierarchy != NULL) { delete m_classNameHierarchy; } m_classNameHierarchy = new GroupAndNameHierarchyModel(); m_forceUpdateOfGroupAndNameHierarchy = true; } /** * Helps copying files. * * @param sf * File that is copied. */ void LabelFile::copyHelperLabelFile(const LabelFile& /*sf*/) { if (m_classNameHierarchy != NULL) { delete m_classNameHierarchy; } m_classNameHierarchy = new GroupAndNameHierarchyModel(); m_forceUpdateOfGroupAndNameHierarchy = true; this->validateDataArraysAfterReading(); } /** * Get label name for a node. * * @param nodeIndex * Node index. * @param columnIndex * Column index. * @return * Label name at the given node and column indices * Empty string if label is not available. */ AString LabelFile::getLabelName(const int32_t nodeIndex, const int32_t columnIndex) const { const int32_t labelKey = this->getLabelKey(nodeIndex, columnIndex); AString label = this->giftiFile->getLabelTable()->getLabelName(labelKey); return label; } /** * Get label key for a node. * * @param nodeIndex * Node index. * @param columnIndex * Column index. * @return * Label key at the given node and column indices. */ int32_t LabelFile::getLabelKey(const int32_t nodeIndex, const int32_t columnIndex) const { CaretAssertVectorIndex(this->columnDataPointers, columnIndex); CaretAssertMessage((nodeIndex >= 0) && (nodeIndex < this->getNumberOfNodes()), "Node Index out of range."); return this->columnDataPointers[columnIndex][nodeIndex]; } /** * set label key for a node. * * @param nodeIndex * Node index. * @param columnIndex * Column index. * param labelKey * Label key inserted at the given node and column indices. */ void LabelFile::setLabelKey(const int32_t nodeIndex, const int32_t columnIndex, const int32_t labelKey) { CaretAssertVectorIndex(this->columnDataPointers, columnIndex); CaretAssertMessage((nodeIndex >= 0) && (nodeIndex < this->getNumberOfNodes()), "Node Index out of range."); this->columnDataPointers[columnIndex][nodeIndex] = labelKey; this->setModified(); m_forceUpdateOfGroupAndNameHierarchy = true; } /** * Get nodes in the given column with the given lable key. * @param columnIndex * Index of column * @param labelKey * Key of label that is desired. * @param nodeIndicesOut * On exit, will contain indices of nodes that have the * given label key in the given column. */ void LabelFile::getNodeIndicesWithLabelKey(const int32_t columnIndex, const int32_t labelKey, std::vector& nodeIndicesOut) const { const int32_t numberOfNodes = this->getNumberOfNodes(); nodeIndicesOut.clear(); nodeIndicesOut.reserve(numberOfNodes); for (int32_t i = 0; i < numberOfNodes; i++) { if (this->getLabelKey(i, columnIndex) == labelKey) { nodeIndicesOut.push_back(i); } } } /** * Get a pointer to the keys for a label file column. * @param columnIndex * Index of the column. * @return * Pointer to keys for the given column. */ const int32_t* LabelFile::getLabelKeyPointerForColumn(const int32_t columnIndex) const { CaretAssertVectorIndex(this->columnDataPointers, columnIndex); return this->columnDataPointers[columnIndex]; } void LabelFile::setNumberOfNodesAndColumns(int32_t nodes, int32_t columns) { giftiFile->clearAndKeepMetadata(); columnDataPointers.clear(); const int32_t unassignedKey = this->getLabelTable()->getUnassignedLabelKey(); std::vector dimensions; dimensions.push_back(nodes); for (int32_t i = 0; i < columns; ++i) { giftiFile->addDataArray(new GiftiDataArray(NiftiIntentEnum::NIFTI_INTENT_LABEL, NiftiDataTypeEnum::NIFTI_TYPE_INT32, dimensions, GiftiEncodingEnum::GZIP_BASE64_BINARY)); columnDataPointers.push_back(giftiFile->getDataArray(i)->getDataPointerInt()); int32_t* ptr = giftiFile->getDataArray(i)->getDataPointerInt(); for (int32_t j = 0; j < nodes; j++) { ptr[j] = unassignedKey; } } setModified(); m_forceUpdateOfGroupAndNameHierarchy = true; } /** * Add map(s) to this GIFTI file. * @param numberOfNodes * Number of nodes. If file is not empty, this value must * match the number of nodes that are in the file. * @param numberOfMaps * Number of maps to add. */ void LabelFile::addMaps(const int32_t numberOfNodes, const int32_t numberOfMaps) { if (numberOfNodes <= 0) { throw DataFileException(getFileName(), "When adding maps the number of nodes must be greater than zero"); } if (this->getNumberOfNodes() > 0) { if (numberOfNodes != this->getNumberOfNodes()) { throw DataFileException(getFileName(), "When adding maps the requested number of nodes is " + AString::number(numberOfNodes) + " but the file contains " + AString::number(this->getNumberOfNodes()) + " nodes."); } } if (numberOfMaps <= 0) { throw DataFileException(getFileName(), "When adding maps, the number of maps must be greater than zero."); } const int32_t unassignedKey = this->getLabelTable()->getUnassignedLabelKey(); if ((this->getNumberOfNodes() > 0) && (this->getNumberOfMaps() > 0)) { std::vector dimensions; dimensions.push_back(numberOfNodes); for (int32_t i = 0; i < numberOfMaps; ++i) { this->giftiFile->addDataArray(new GiftiDataArray(NiftiIntentEnum::NIFTI_INTENT_LABEL, NiftiDataTypeEnum::NIFTI_TYPE_INT32, dimensions, GiftiEncodingEnum::GZIP_BASE64_BINARY)); const int32_t mapIndex = giftiFile->getNumberOfDataArrays() - 1; this->columnDataPointers.push_back(giftiFile->getDataArray(mapIndex)->getDataPointerInt()); int32_t* ptr = giftiFile->getDataArray(mapIndex)->getDataPointerInt(); for (int32_t j = 0; j < numberOfNodes; j++) { ptr[j] = unassignedKey; } } } else { this->setNumberOfNodesAndColumns(numberOfNodes, numberOfMaps); } m_forceUpdateOfGroupAndNameHierarchy = true; this->setModified(); } void LabelFile::setLabelKeysForColumn(const int32_t columnIndex, const int32_t* valuesIn) { CaretAssertVectorIndex(this->columnDataPointers, columnIndex); int32_t* myColumn = columnDataPointers[columnIndex]; int numNodes = (int)getNumberOfNodes(); for (int i = 0; i < numNodes; ++i) { myColumn[i] = valuesIn[i]; } m_forceUpdateOfGroupAndNameHierarchy = true; setModified(); } /** * Return a vector containing the keys used in a map. Each key is listed * once and the keys will be in ascending order. * @param mapIndex * Index of map. * @return * Vector containing the keys. */ std::vector LabelFile::getUniqueLabelKeysUsedInMap(const int32_t mapIndex) const { CaretAssertVectorIndex(this->columnDataPointers, mapIndex); std::set uniqueKeys; const int32_t numNodes = getNumberOfNodes(); for (int32_t i = 0; i < numNodes; i++) { const int32_t key = getLabelKey(i, mapIndex); uniqueKeys.insert(key); } std::vector keyVector; keyVector.insert(keyVector.end(), uniqueKeys.begin(), uniqueKeys.end()); return keyVector; } connectome-workbench-1.2.3+git41-gc4c6c90/src/Files/LabelFile.h000066400000000000000000000075431300200146000236070ustar00rootroot00000000000000 #ifndef __LABEL_FILE_H__ #define __LABEL_FILE_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 "GiftiTypeFile.h" namespace caret { class GroupAndNameHierarchyModel; class GiftiDataArray; class GiftiLabelTable; /** * \brief A Label data file. */ class LabelFile : public GiftiTypeFile { public: LabelFile(); LabelFile(const LabelFile& sf); LabelFile& operator=(const LabelFile& sf); virtual ~LabelFile(); void clear(); int32_t getNumberOfNodes() const; int32_t getNumberOfColumns() const; void setNumberOfNodesAndColumns(int32_t nodes, int32_t columns); virtual void addMaps(const int32_t numberOfNodes, const int32_t numberOfMaps); GiftiLabelTable* getLabelTable(); const GiftiLabelTable* getLabelTable() const; int32_t getLabelKey(const int32_t nodeIndex, const int32_t columnIndex) const; AString getLabelName(const int32_t nodeIndex, const int32_t columnIndex) const; void setLabelKey(const int32_t nodeIndex, const int32_t columnIndex, const int32_t labelIndex); void getNodeIndicesWithLabelKey(const int32_t columnIndex, const int32_t labelKey, std::vector& nodeIndicesOut) const; const int32_t* getLabelKeyPointerForColumn(const int32_t columnIndex) const; void setLabelKeysForColumn(const int32_t columnIndex, const int32_t* keysIn); std::vector getUniqueLabelKeysUsedInMap(const int32_t mapIndex) const; GroupAndNameHierarchyModel* getGroupAndNameHierarchyModel(); const GroupAndNameHierarchyModel* getGroupAndNameHierarchyModel() const; //override writeFile in order to check filename against type of file virtual void writeFile(const AString& filename); protected: /** * Validate the contents of the file after it * has been read such as correct number of * data arrays and proper data types/dimensions. */ virtual void validateDataArraysAfterReading(); void copyHelperLabelFile(const LabelFile& sf); void initializeMembersLabelFile(); private: void validateKeysAndLabels() const; /** Points to actual data in each Gifti Data Array */ std::vector columnDataPointers; /** Holds class and name hierarchy used for display selection */ mutable GroupAndNameHierarchyModel* m_classNameHierarchy; /** force an update of the class and name hierarchy */ mutable bool m_forceUpdateOfGroupAndNameHierarchy; }; } // namespace #endif // __LABEL_FILE_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Files/MapYokingGroupEnum.cxx000066400000000000000000000357061300200146000261050ustar00rootroot00000000000000 /*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 __MAP_YOKING_GROUP_ENUM_DECLARE__ #include "MapYokingGroupEnum.h" #undef __MAP_YOKING_GROUP_ENUM_DECLARE__ #include "CaretAssert.h" #include "EventManager.h" using namespace caret; /** * \class caret::MapYokingGroupEnum * \brief Enumerated types for map yoking selection. */ /** * Constructor. * * @param enumValue * An enumerated value. * @param name * Name of enumerated value. * * @param guiName * User-friendly name for use in user-interface. */ MapYokingGroupEnum::MapYokingGroupEnum(const Enum enumValue, const AString& name, const AString& guiName) { this->enumValue = enumValue; this->integerCode = integerCodeCounter++; this->name = name; this->guiName = guiName; this->mapIndex = 0; this->enabledStatus = false; } /** * Destructor. */ MapYokingGroupEnum::~MapYokingGroupEnum() { } /** * Initialize the enumerated metadata. */ void MapYokingGroupEnum::initialize() { if (initializedFlag) { return; } initializedFlag = true; enumData.push_back(MapYokingGroupEnum(MAP_YOKING_GROUP_OFF, "MAP_YOKING_GROUP_OFF", "Off")); enumData.push_back(MapYokingGroupEnum(MAP_YOKING_GROUP_1, "MAP_YOKING_GROUP_1", "I")); enumData.push_back(MapYokingGroupEnum(MAP_YOKING_GROUP_2, "MAP_YOKING_GROUP_2", "II")); enumData.push_back(MapYokingGroupEnum(MAP_YOKING_GROUP_3, "MAP_YOKING_GROUP_3", "III")); enumData.push_back(MapYokingGroupEnum(MAP_YOKING_GROUP_4, "MAP_YOKING_GROUP_4", "IV")); enumData.push_back(MapYokingGroupEnum(MAP_YOKING_GROUP_5, "MAP_YOKING_GROUP_5", "V")); enumData.push_back(MapYokingGroupEnum(MAP_YOKING_GROUP_6, "MAP_YOKING_GROUP_6", "VI")); enumData.push_back(MapYokingGroupEnum(MAP_YOKING_GROUP_7, "MAP_YOKING_GROUP_7", "VII")); enumData.push_back(MapYokingGroupEnum(MAP_YOKING_GROUP_8, "MAP_YOKING_GROUP_8", "VIII")); enumData.push_back(MapYokingGroupEnum(MAP_YOKING_GROUP_9, "MAP_YOKING_GROUP_9", "IX")); enumData.push_back(MapYokingGroupEnum(MAP_YOKING_GROUP_10, "MAP_YOKING_GROUP_10", "X")); } /** * 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. */ MapYokingGroupEnum* MapYokingGroupEnum::findData(const Enum enumValue) { if (initializedFlag == false) initialize(); size_t num = enumData.size(); for (size_t i = 0; i < num; i++) { MapYokingGroupEnum* 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 MapYokingGroupEnum::toName(Enum enumValue) { if (initializedFlag == false) initialize(); const MapYokingGroupEnum* 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. */ MapYokingGroupEnum::Enum MapYokingGroupEnum::fromName(const AString& name, bool* isValidOut) { if (initializedFlag == false) initialize(); bool validFlag = false; Enum enumValue = MAP_YOKING_GROUP_OFF; for (std::vector::iterator iter = enumData.begin(); iter != enumData.end(); iter++) { const MapYokingGroupEnum& 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 MapYokingGroupEnum")); } return enumValue; } /** * Get an enumerated value corresponding to the * obsolete OverlayYokingGroupEnum that has been replaced * with MapYokingGroupEnum. * * @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. */ MapYokingGroupEnum::Enum MapYokingGroupEnum::fromOverlayYokingGroupEnumName(const AString& name, bool* isValidOut) { if (initializedFlag == false) initialize(); bool validFlag = false; Enum enumValue = MAP_YOKING_GROUP_OFF; if (name == "OVERLAY_YOKING_GROUP_OFF") { enumValue = MAP_YOKING_GROUP_OFF; validFlag = true; } else if (name == "OVERLAY_YOKING_GROUP_1") { enumValue = MAP_YOKING_GROUP_1; validFlag = true; } else if (name == "OVERLAY_YOKING_GROUP_2") { enumValue = MAP_YOKING_GROUP_2; validFlag = true; } else if (name == "OVERLAY_YOKING_GROUP_3") { enumValue = MAP_YOKING_GROUP_3; validFlag = true; } else if (name == "OVERLAY_YOKING_GROUP_4") { enumValue = MAP_YOKING_GROUP_4; validFlag = true; } else if (name == "OVERLAY_YOKING_GROUP_5") { enumValue = MAP_YOKING_GROUP_5; validFlag = true; } else if (name == "OVERLAY_YOKING_GROUP_6") { enumValue = MAP_YOKING_GROUP_6; validFlag = true; } else if (name == "OVERLAY_YOKING_GROUP_7") { enumValue = MAP_YOKING_GROUP_7; validFlag = true; } else if (name == "OVERLAY_YOKING_GROUP_8") { enumValue = MAP_YOKING_GROUP_8; validFlag = true; } else if (name == "OVERLAY_YOKING_GROUP_9") { enumValue = MAP_YOKING_GROUP_9; validFlag = true; } else if (name == "OVERLAY_YOKING_GROUP_10") { enumValue = MAP_YOKING_GROUP_10; validFlag = true; } if (isValidOut != 0) { *isValidOut = validFlag; } else if (validFlag == false) { CaretAssertMessage(0, AString("Name " + name + "failed to match enumerated value for type MapYokingGroupEnum")); } return enumValue; } /** * Get a GUI string representation of the enumerated type. * @param enumValue * Enumerated value. * @return * String representing enumerated value. */ AString MapYokingGroupEnum::toGuiName(Enum enumValue) { if (initializedFlag == false) initialize(); const MapYokingGroupEnum* 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. */ MapYokingGroupEnum::Enum MapYokingGroupEnum::fromGuiName(const AString& guiName, bool* isValidOut) { if (initializedFlag == false) initialize(); bool validFlag = false; Enum enumValue = MAP_YOKING_GROUP_OFF; for (std::vector::iterator iter = enumData.begin(); iter != enumData.end(); iter++) { const MapYokingGroupEnum& 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 MapYokingGroupEnum")); } return enumValue; } /** * Get the integer code for a data type. * * @return * Integer code for data type. */ int32_t MapYokingGroupEnum::toIntegerCode(Enum enumValue) { if (initializedFlag == false) initialize(); const MapYokingGroupEnum* 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. */ MapYokingGroupEnum::Enum MapYokingGroupEnum::fromIntegerCode(const int32_t integerCode, bool* isValidOut) { if (initializedFlag == false) initialize(); bool validFlag = false; Enum enumValue = MAP_YOKING_GROUP_OFF; for (std::vector::iterator iter = enumData.begin(); iter != enumData.end(); iter++) { const MapYokingGroupEnum& 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 MapYokingGroupEnum")); } 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 MapYokingGroupEnum::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 MapYokingGroupEnum::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(MapYokingGroupEnum::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 MapYokingGroupEnum::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(MapYokingGroupEnum::toGuiName(iter->enumValue)); } if (isSorted) { std::sort(allGuiNames.begin(), allGuiNames.end()); } } /** * @return The selected map index associated with the given value. * * @param enumValue * Value for which map index is requested. */ int32_t MapYokingGroupEnum::getSelectedMapIndex(const Enum enumValue) { CaretAssertMessage(enumValue != MAP_YOKING_GROUP_OFF, "Never should be called with MAP_YOKING_GROUP_OFF"); if (initializedFlag == false) initialize(); MapYokingGroupEnum* enumInstance = findData(enumValue); return enumInstance->mapIndex; } /** * Set the map index for the given enum value. * * @param enumValue * Value for which map index is requested. * @param mapIndex * New value for map index. */ void MapYokingGroupEnum::setSelectedMapIndex(const Enum enumValue, const int32_t mapIndex) { CaretAssertMessage(enumValue != MAP_YOKING_GROUP_OFF, "Never should be called with MAP_YOKING_GROUP_OFF"); if (initializedFlag == false) initialize(); MapYokingGroupEnum* enumInstance = findData(enumValue); enumInstance->mapIndex = mapIndex; } /** * @return The enabled status associated with the given value. * * @param enumValue * Value for which map index is requested. */ bool MapYokingGroupEnum::isEnabled(const Enum enumValue) { CaretAssertMessage(enumValue != MAP_YOKING_GROUP_OFF, "Never should be called with MAP_YOKING_GROUP_OFF"); if (initializedFlag == false) initialize(); MapYokingGroupEnum* enumInstance = findData(enumValue); return enumInstance->enabledStatus; } /** * Set the enabled status for the given enum value. * * @param enumValue * Value for which map index is requested. * @param enabled * New value for enabled status. */ void MapYokingGroupEnum::setEnabled(const Enum enumValue, const bool enabled) { CaretAssertMessage(enumValue != MAP_YOKING_GROUP_OFF, "Never should be called with MAP_YOKING_GROUP_OFF"); if (initializedFlag == false) initialize(); MapYokingGroupEnum* enumInstance = findData(enumValue); enumInstance->enabledStatus = enabled; } connectome-workbench-1.2.3+git41-gc4c6c90/src/Files/MapYokingGroupEnum.h000066400000000000000000000101421300200146000255150ustar00rootroot00000000000000#ifndef __MAP_YOKING_GROUP_ENUM_H__ #define __MAP_YOKING_GROUP_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 MapYokingGroupEnum { public: /** * Enumerated values. */ enum Enum { /** Off */ MAP_YOKING_GROUP_OFF, /** Group 1 */ MAP_YOKING_GROUP_1, /** Group 2 */ MAP_YOKING_GROUP_2, /** Group 3*/ MAP_YOKING_GROUP_3, /** Group 4 */ MAP_YOKING_GROUP_4, /** Group 5 */ MAP_YOKING_GROUP_5, /** Group 6 */ MAP_YOKING_GROUP_6, /** Group 7 */ MAP_YOKING_GROUP_7, /** Group 8 */ MAP_YOKING_GROUP_8, /** Group 9 */ MAP_YOKING_GROUP_9, /** Group 10 */ MAP_YOKING_GROUP_10 }; ~MapYokingGroupEnum(); static Enum fromOverlayYokingGroupEnumName(const AString& name, bool* isValidOut); 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 int32_t getSelectedMapIndex(const Enum enumValue); static void setSelectedMapIndex( const Enum enumValue, const int32_t mapIndex); static bool isEnabled(const Enum enumValue); static void setEnabled(const Enum enumValue, const bool enabled); private: MapYokingGroupEnum(const Enum enumValue, const AString& name, const AString& guiName); static MapYokingGroupEnum* 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; /** Index of the selected map */ int32_t mapIndex; /** Enabled status of the map */ bool enabledStatus; }; #ifdef __MAP_YOKING_GROUP_ENUM_DECLARE__ std::vector MapYokingGroupEnum::enumData; bool MapYokingGroupEnum::initializedFlag = false; int32_t MapYokingGroupEnum::integerCodeCounter = 0; #endif // __MAP_YOKING_GROUP_ENUM_DECLARE__ } // namespace #endif //__MAP_YOKING_GROUP_ENUM_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Files/MetricFile.cxx000066400000000000000000000525431300200146000243660ustar00rootroot00000000000000/*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 "CaretAssert.h" #include "CaretLogger.h" #include "ChartDataCartesian.h" #include "ChartDataSource.h" #include "DataFileException.h" #include "DataFileTypeEnum.h" #include "GiftiFile.h" #include "MathFunctions.h" #include "MetricFile.h" #include "NiftiEnums.h" #include "PaletteColorMapping.h" #include "SceneClass.h" #include using namespace caret; /** * Constructor. */ MetricFile::MetricFile() : GiftiTypeFile(DataFileTypeEnum::METRIC) { this->initializeMembersMetricFile(); } /** * Copy constructor. * * @param sf * Surface file that is copied. */ MetricFile::MetricFile(const MetricFile& sf) : GiftiTypeFile(sf), ChartableLineSeriesBrainordinateInterface() { this->copyHelperMetricFile(sf); } /** * Assignment operator. * * @param sf * Surface file that is copied. * @return * This surface file with content replaced * by the MetricFile parameter. */ MetricFile& MetricFile::operator=(const MetricFile& sf) { if (this != &sf) { GiftiTypeFile::operator=(sf); this->copyHelperMetricFile(sf); } return *this; } /** * Destructor. */ MetricFile::~MetricFile() { this->columnDataPointers.clear(); } void MetricFile::writeFile(const AString& filename) { if (!(filename.endsWith(".func.gii") || filename.endsWith(".shape.gii"))) { CaretLogWarning("metric file '" + filename + "' should be saved ending in .func.gii or .shape.gii, see wb_command -gifti-help"); } caret::GiftiTypeFile::writeFile(filename); } /** * Clear the surface file. */ void MetricFile::clear() { GiftiTypeFile::clear(); this->columnDataPointers.clear(); } /** * Validate the contents of the file after it * has been read such as correct number of * data arrays and proper data types/dimensions. */ void MetricFile::validateDataArraysAfterReading() { this->columnDataPointers.clear(); this->initializeMembersMetricFile(); this->verifyDataArraysHaveSameNumberOfRows(0, 0); bool isLabelData = false; const int32_t numberOfDataArrays = this->giftiFile->getNumberOfDataArrays(); for (int32_t i = 0; i < numberOfDataArrays; i++) { GiftiDataArray* gda = this->giftiFile->getDataArray(i); if (gda->getDataType() != NiftiDataTypeEnum::NIFTI_TYPE_FLOAT32) { if (gda->getIntent() == NiftiIntentEnum::NIFTI_INTENT_LABEL) { isLabelData = true; } gda->convertToDataType(NiftiDataTypeEnum::NIFTI_TYPE_FLOAT32); } int numDims = gda->getNumberOfDimensions(); std::vector dims = gda->getDimensions(); if (numDims == 1 || (numDims == 2 && dims[1] == 1)) { this->columnDataPointers.push_back(gda->getDataPointerFloat()); } else { if (numDims != 2) { throw DataFileException(getFileName(), "Invalid number of dimensions in metric file: " + AString::number(numDims)); } if (numberOfDataArrays != 1) { throw DataFileException(getFileName(), "Two dimensional data arrays are not allowed in metric files with multiple data arrays"); } std::vector newdims = dims; newdims[1] = 1; GiftiFile* newFile = new GiftiFile();//convert to multiple 1-d arrays on the fly *(newFile->getMetaData()) = *(giftiFile->getMetaData()); int32_t indices[2], newindices[2] = {0, 0}; for (indices[1] = 0; indices[1] < dims[1]; ++indices[1]) { GiftiDataArray* tempArray = new GiftiDataArray(NiftiIntentEnum::NIFTI_INTENT_NORMAL, NiftiDataTypeEnum::NIFTI_TYPE_FLOAT32, newdims, GiftiEncodingEnum::GZIP_BASE64_BINARY); for (indices[0] = 0; indices[0] < dims[0]; ++indices[0]) { newindices[0] = indices[0]; tempArray->setDataFloat32(newindices, gda->getDataFloat32(indices)); } newFile->addDataArray(tempArray); newFile->setDataArrayName(indices[1], "#" + AString::number(indices[1] + 1)); columnDataPointers.push_back(tempArray->getDataPointerFloat()); } delete giftiFile;//delete old 2D file giftiFile = newFile;//drop new 1D file in } } if (isLabelData) { CaretLogWarning("Metric File: " + this->getFileName() + " contains data array with NIFTI_INTENT_LABEL !!!"); } } /** * Get the number of nodes. * * @return * The number of nodes. */ int32_t MetricFile::getNumberOfNodes() const { int32_t numNodes = 0; int32_t numDataArrays = this->giftiFile->getNumberOfDataArrays(); if (numDataArrays > 0) { numNodes = this->giftiFile->getDataArray(0)->getNumberOfRows(); } return numNodes; } /** * Get the number of columns. * * @return * The number of columns. */ int32_t MetricFile::getNumberOfColumns() const { const int32_t numCols = this->giftiFile->getNumberOfDataArrays(); return numCols; } /** * Initialize members of this class. */ void MetricFile::initializeMembersMetricFile() { for (int32_t i = 0; i < BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS; i++) { m_chartingEnabledForTab[i] = false; } } /** * Helps copying files. * * @param sf * File that is copied. */ void MetricFile::copyHelperMetricFile(const MetricFile& mf) { this->validateDataArraysAfterReading(); for (int32_t i = 0; i < BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS; i++) { m_chartingEnabledForTab[i] = mf.m_chartingEnabledForTab[i]; } } /** * Get value for a node. * * @param nodeIndex * Node index. * @param columnIndex * Column index. * @return * Value at the given node and column indices. */ float MetricFile::getValue(const int32_t nodeIndex, const int32_t columnIndex) const { CaretAssertVectorIndex(this->columnDataPointers, columnIndex); CaretAssertMessage((nodeIndex >= 0) && (nodeIndex < this->getNumberOfNodes()), "Node Index out of range."); return this->columnDataPointers[columnIndex][nodeIndex]; } /** * set label key for a node. * * @param nodeIndex * Node index. * @param columnIndex * Column index. * param value * Value inserted at the given node and column indices. */ void MetricFile::setValue(const int32_t nodeIndex, const int32_t columnIndex, const float value) { CaretAssertVectorIndex(this->columnDataPointers, columnIndex); CaretAssertMessage((nodeIndex >= 0) && (nodeIndex < this->getNumberOfNodes()), "Node Index out of range."); this->columnDataPointers[columnIndex][nodeIndex] = value; setModified(); } const float* MetricFile::getValuePointerForColumn(const int32_t columnIndex) const { CaretAssertVectorIndex(this->columnDataPointers, columnIndex); return this->columnDataPointers[columnIndex]; } void MetricFile::setNumberOfNodesAndColumns(int32_t nodes, int32_t columns) { giftiFile->clearAndKeepMetadata(); columnDataPointers.clear(); std::vector dimensions; dimensions.push_back(nodes); for (int32_t i = 0; i < columns; ++i) { giftiFile->addDataArray(new GiftiDataArray(NiftiIntentEnum::NIFTI_INTENT_NORMAL, NiftiDataTypeEnum::NIFTI_TYPE_FLOAT32, dimensions, GiftiEncodingEnum::GZIP_BASE64_BINARY)); columnDataPointers.push_back(giftiFile->getDataArray(i)->getDataPointerFloat()); } setModified(); } /** * Add map(s) to this GIFTI file. * @param numberOfNodes * Number of nodes. If file is not empty, this value must * match the number of nodes that are in the file. * @param numberOfMaps * Number of maps to add. */ void MetricFile::addMaps(const int32_t numberOfNodes, const int32_t numberOfMaps) { if (numberOfNodes <= 0) { throw DataFileException(getFileName(), "When adding maps the number of nodes must be greater than zero"); } if (this->getNumberOfNodes() > 0) { if (numberOfNodes != this->getNumberOfNodes()) { throw DataFileException(getFileName(), "When adding maps the requested number of nodes is " + AString::number(numberOfNodes) + " but the file contains " + AString::number(this->getNumberOfNodes()) + " nodes."); } } if (numberOfMaps <= 0) { throw DataFileException(getFileName(), "When adding maps, the number of maps must be greater than zero."); } if ((this->getNumberOfNodes() > 0) && (this->getNumberOfMaps() > 0)) { std::vector dimensions; dimensions.push_back(numberOfNodes); for (int32_t i = 0; i < numberOfMaps; ++i) { this->giftiFile->addDataArray(new GiftiDataArray(NiftiIntentEnum::NIFTI_INTENT_NORMAL, NiftiDataTypeEnum::NIFTI_TYPE_FLOAT32, dimensions, GiftiEncodingEnum::GZIP_BASE64_BINARY)); const int32_t mapIndex = giftiFile->getNumberOfDataArrays() - 1; this->columnDataPointers.push_back(giftiFile->getDataArray(mapIndex)->getDataPointerFloat()); } } else { this->setNumberOfNodesAndColumns(numberOfNodes, numberOfMaps); } this->setModified(); } void MetricFile::setValuesForColumn(const int32_t columnIndex, const float* valuesIn) { CaretAssertVectorIndex(this->columnDataPointers, columnIndex); float* myColumn = columnDataPointers[columnIndex]; int numNodes = (int)getNumberOfNodes(); for (int i = 0; i < numNodes; ++i) { myColumn[i] = valuesIn[i]; } setModified(); } void MetricFile::initializeColumn(const int32_t columnIndex, const float& value) { CaretAssertVectorIndex(this->columnDataPointers, columnIndex); float* myColumn = columnDataPointers[columnIndex]; int numNodes = (int)getNumberOfNodes(); for (int i = 0; i < numNodes; ++i) { myColumn[i] = value; } setModified(); } /** * Get the minimum and maximum values from ALL maps in this file. * Note that not all files (due to size of file) are able to provide * the minimum and maximum values from the file. The return value * indicates success/failure. If the failure (false) is returned * the returned values are likely +/- the maximum float values. * * @param dataRangeMinimumOut * Minimum data value found. * @param dataRangeMaximumOut * Maximum data value found. * @return * True if the values are valid, else false. */ bool MetricFile::getDataRangeFromAllMaps(float& dataRangeMinimumOut, float& dataRangeMaximumOut) const { const int32_t numberOfMaps = getNumberOfMaps(); if (numberOfMaps > 0) { dataRangeMaximumOut = -std::numeric_limits::max(); dataRangeMinimumOut = std::numeric_limits::max(); for (int32_t i = 0; i < numberOfMaps; ++i) { GiftiDataArray* gda = this->giftiFile->getDataArray(i); float mapMin, mapMax; gda->getMinMaxValuesFloat(mapMin, mapMax); if (mapMin < dataRangeMinimumOut) { dataRangeMinimumOut = mapMin; } if (mapMax > dataRangeMaximumOut) { dataRangeMaximumOut = mapMax; } } } else { dataRangeMaximumOut = std::numeric_limits::max(); dataRangeMinimumOut = -dataRangeMaximumOut; } return true; } /** * @return Is charting enabled for this file? */ bool MetricFile::isLineSeriesChartingEnabled(const int32_t tabIndex) const { CaretAssertArrayIndex(m_chartingEnabledForTab, BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS, tabIndex); return m_chartingEnabledForTab[tabIndex]; } /** * @return Return true if the file's current state supports * charting data, else false. Typically a brainordinate file * is chartable if it contains more than one map. */ bool MetricFile::isLineSeriesChartingSupported() const { if (getNumberOfMaps() > 1) { return true; } return false; } /** * Set charting enabled for this file. * * @param enabled * New status for charting enabled. */ void MetricFile::setLineSeriesChartingEnabled(const int32_t tabIndex, const bool enabled) { CaretAssertArrayIndex(m_chartingEnabledForTab, BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS, tabIndex); m_chartingEnabledForTab[tabIndex] = enabled; } /** * Get chart data types supported by the file. * * @param chartDataTypesOut * Chart types supported by this file. */ void MetricFile::getSupportedLineSeriesChartDataTypes(std::vector& chartDataTypesOut) const { helpGetSupportedLineSeriesChartDataTypes(chartDataTypesOut); } /** * Load charting data for the surface with the given structure and node index. * * @param structure * The surface's structure. * @param nodeIndex * Index of the node. * @return * Pointer to the chart data. If the data FAILED to load, * the returned pointer will be NULL. Caller takes ownership * of the pointer and must delete it when no longer needed. */ ChartDataCartesian* MetricFile::loadLineSeriesChartDataForSurfaceNode(const StructureEnum::Enum structure, const int32_t nodeIndex) { ChartDataCartesian* chartData = NULL; if (getStructure() == structure) { try { const int32_t numMaps = getNumberOfMaps(); std::vector data; for (int64_t iMap = 0; iMap < numMaps; iMap++) { data.push_back(getValue(nodeIndex, iMap)); } chartData = helpCreateCartesianChartData(data); ChartDataSource* dataSource = chartData->getChartDataSource(); dataSource->setSurfaceNode(getFileName(), StructureEnum::toName(structure), getNumberOfNodes(), nodeIndex); } catch (const DataFileException& dfe) { if (chartData != NULL) { delete chartData; chartData = NULL; } throw dfe; } } return chartData; } /** * Load average charting data for the surface with the given structure and node indices. * * @param structure * The surface's structure. * @param nodeIndices * Indices of the node. * @return * Pointer to the chart data. If the data FAILED to load, * the returned pointer will be NULL. Caller takes ownership * of the pointer and must delete it when no longer needed. */ ChartDataCartesian* MetricFile::loadAverageLineSeriesChartDataForSurfaceNodes(const StructureEnum::Enum structure, const std::vector& nodeIndices) { ChartDataCartesian* chartData = NULL; if (getStructure() == structure) { ChartDataCartesian* chartData = NULL; try { const int32_t numberOfNodeIndices = static_cast(nodeIndices.size()); const int32_t numberOfMaps = getNumberOfMaps(); if ((numberOfNodeIndices > 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 < numberOfNodeIndices; iNode++) { const int32_t nodeIndex = nodeIndices[iNode]; dataSum[iMap] += getValue(nodeIndex, iMap); } } std::vector data; for (int32_t iMap = 0; iMap < numberOfMaps; iMap++) { CaretAssertVectorIndex(dataSum, iMap); const float mapAverageValue = dataSum[iMap] / numberOfNodeIndices; data.push_back(mapAverageValue); } chartData = helpCreateCartesianChartData(data); ChartDataSource* dataSource = chartData->getChartDataSource(); dataSource->setSurfaceNodeAverage(getFileName(), StructureEnum::toName(structure), numberOfNodeIndices, nodeIndices); } } catch (const DataFileException& dfe) { if (chartData != NULL) { delete chartData; chartData = NULL; } throw dfe; } return chartData; } return chartData; } /** * Load charting data for the voxel enclosing the given coordinate. * * @param xyz * Coordinate of voxel. * @return * Pointer to the chart data. If the data FAILED to load, * the returned pointer will be NULL. Caller takes ownership * of the pointer and must delete it when no longer needed. */ ChartDataCartesian* MetricFile::loadLineSeriesChartDataForVoxelAtCoordinate(const float * /*xyz[3]*/) { ChartDataCartesian* chartData = NULL; //helpLoadChartDataForVoxelAtCoordinate(xyz); return chartData; } /** * Save file data from the scene. For subclasses that need to * save to a scene, this method should be overriden. sceneClass * will be valid and any scene data should be added to it. * * @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. */ void MetricFile::saveFileDataToScene(const SceneAttributes* sceneAttributes, SceneClass* sceneClass) { GiftiTypeFile::saveFileDataToScene(sceneAttributes, sceneClass); sceneClass->addBooleanArray("m_chartingEnabledForTab", m_chartingEnabledForTab, BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS); } /** * Restore file data from the scene. For subclasses that need to * restore from a scene, this method should be overridden. The scene class * will be valid and any scene data may be obtained from it. * * @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 MetricFile::restoreFileDataFromScene(const SceneAttributes* sceneAttributes, const SceneClass* sceneClass) { GiftiTypeFile::restoreFileDataFromScene(sceneAttributes, sceneClass); const ScenePrimitiveArray* tabArray = sceneClass->getPrimitiveArray("m_chartingEnabledForTab"); if (tabArray != NULL) { sceneClass->getBooleanArrayValue("m_chartingEnabledForTab", m_chartingEnabledForTab, BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS); } else { for (int32_t i = 0; i < BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS; i++) { m_chartingEnabledForTab[i] = false; } } } connectome-workbench-1.2.3+git41-gc4c6c90/src/Files/MetricFile.h000066400000000000000000000110151300200146000240000ustar00rootroot00000000000000 #ifndef __METRIC_FILE_H__ #define __METRIC_FILE_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 "ChartableLineSeriesBrainordinateInterface.h" #include "BrainConstants.h" #include "GiftiTypeFile.h" namespace caret { class GiftiDataArray; /** * \brief A Metric data file. */ class MetricFile : public GiftiTypeFile, public ChartableLineSeriesBrainordinateInterface { public: MetricFile(); MetricFile(const MetricFile& sf); MetricFile& operator=(const MetricFile& sf); virtual ~MetricFile(); virtual void clear(); virtual int32_t getNumberOfNodes() const; virtual int32_t getNumberOfColumns() const; virtual void setNumberOfNodesAndColumns(int32_t nodes, int32_t columns); virtual void addMaps(const int32_t numberOfNodes, const int32_t numberOfMaps); float getValue(const int32_t nodeIndex, const int32_t columnIndex) const; void setValue(const int32_t nodeIndex, const int32_t columnIndex, const float value); const float* getValuePointerForColumn(const int32_t columnIndex) const; void setValuesForColumn(const int32_t columnIndex, const float* valuesIn); void initializeColumn(const int32_t columnIndex, const float& value = 0.0f); virtual bool getDataRangeFromAllMaps(float& dataRangeMinimumOut, float& dataRangeMaximumOut) const; virtual bool isLineSeriesChartingEnabled(const int32_t tabIndex) const; virtual void setLineSeriesChartingEnabled(const int32_t tabIndex, const bool enabled); virtual bool isLineSeriesChartingSupported() const; virtual ChartDataCartesian* loadLineSeriesChartDataForSurfaceNode(const StructureEnum::Enum structure, const int32_t nodeIndex); virtual ChartDataCartesian* loadAverageLineSeriesChartDataForSurfaceNodes(const StructureEnum::Enum structure, const std::vector& nodeIndices); virtual ChartDataCartesian* loadLineSeriesChartDataForVoxelAtCoordinate(const float xyz[3]); virtual void getSupportedLineSeriesChartDataTypes(std::vector& chartDataTypesOut) const; //override writeFile in order to check filename against type of file virtual void writeFile(const AString& filename); protected: /** * Validate the contents of the file after it * has been read such as correct number of * data arrays and proper data types/dimensions. */ virtual void validateDataArraysAfterReading(); void copyHelperMetricFile(const MetricFile& sf); void initializeMembersMetricFile(); virtual void saveFileDataToScene(const SceneAttributes* sceneAttributes, SceneClass* sceneClass); virtual void restoreFileDataFromScene(const SceneAttributes* sceneAttributes, const SceneClass* sceneClass); private: /** Points to actual data in each Gifti Data Array */ std::vector columnDataPointers; bool m_chartingEnabledForTab[BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS]; }; } // namespace #endif // __METRIC_FILE_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Files/MetricSmoothingObject.cxx000066400000000000000000001002061300200146000265730ustar00rootroot00000000000000/*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 "MetricSmoothingObject.h" #include "CaretAssert.h" #include "CaretException.h" #include "SurfaceFile.h" #include "MetricFile.h" #include "GeodesicHelper.h" #include "TopologyHelper.h" #include "CaretOMP.h" #include using namespace std; using namespace caret; MetricSmoothingObject::MetricSmoothingObject(const SurfaceFile* mySurf, const float& kernel, const MetricFile* myRoi, Method myMethod, const float* nodeAreas) { CaretAssert(mySurf != NULL); if (myRoi != NULL && mySurf->getNumberOfNodes() != myRoi->getNumberOfNodes()) { throw CaretException("roi number of nodes doesn't match the surface"); } precomputeWeights(mySurf, kernel, myRoi, myMethod, nodeAreas); } void MetricSmoothingObject::smoothColumn(const MetricFile* metricIn, const int& whichColumn, MetricFile* columnOut, const MetricFile* roi, const bool& fixZeros) const { CaretAssert(metricIn != NULL); CaretAssert(columnOut != NULL); if (metricIn->getNumberOfNodes() != (int32_t)m_weightLists.size()) { throw CaretException("metric does not match surface number of nodes"); } if (whichColumn < -1 || whichColumn >= metricIn->getNumberOfColumns()) { throw CaretException("invalid column number"); } if (columnOut->getNumberOfNodes() != (int32_t)m_weightLists.size() || columnOut->getNumberOfColumns() != 1) { columnOut->setNumberOfNodesAndColumns(m_weightLists.size(), 1); } vector scratch(metricIn->getNumberOfNodes()); if (roi != NULL) { if (roi->getNumberOfNodes() != (int32_t)m_weightLists.size()) { throw CaretException("roi does not match surface number of nodes"); } smoothColumnInternal(scratch.data(), metricIn, whichColumn, columnOut, 0, roi, 0, fixZeros); } else { smoothColumnInternal(scratch.data(), metricIn, whichColumn, columnOut, 0, fixZeros); } } void MetricSmoothingObject::smoothColumn(const MetricFile* metricIn, const int& whichColumn, MetricFile* metricOut, const int& whichOutColumn, const MetricFile* roi, const int& whichRoiColumn, const bool& fixZeros) const { CaretAssert(metricIn != NULL); CaretAssert(metricOut != NULL); if (metricIn->getNumberOfNodes() != (int32_t)m_weightLists.size()) { throw CaretException("metric does not match surface number of nodes"); } if (metricOut->getNumberOfNodes() != (int32_t)m_weightLists.size()) { throw CaretException("output metric does not match surface number of nodes"); } if (roi != NULL && (roi->getNumberOfNodes() != (int32_t)m_weightLists.size())) { throw CaretException("roi does not match surface number of nodes"); } if (whichColumn < -1 || whichColumn >= metricIn->getNumberOfColumns()) { throw CaretException("invalid input column number"); } if (whichOutColumn < -1 || whichOutColumn >= metricOut->getNumberOfColumns()) { throw CaretException("invalid output column number"); } if (roi != NULL && (whichRoiColumn < -1 || whichRoiColumn >= roi->getNumberOfColumns())) { throw CaretException("invalid input column number"); } vector scratch(metricIn->getNumberOfNodes()); if (roi != NULL) { smoothColumnInternal(scratch.data(), metricIn, whichColumn, metricOut, whichOutColumn, roi, whichRoiColumn, fixZeros); } else { smoothColumnInternal(scratch.data(), metricIn, whichColumn, metricOut, whichOutColumn, fixZeros); } } void MetricSmoothingObject::smoothMetric(const MetricFile* metricIn, MetricFile* metricOut, const MetricFile* roi, const bool& fixZeros) const { CaretAssert(metricIn != NULL); CaretAssert(metricOut != NULL); int32_t numCols = metricIn->getNumberOfColumns(); if (metricIn->getNumberOfNodes() != (int32_t)m_weightLists.size()) { throw CaretException("metric does not match surface number of nodes"); } if (metricOut->getNumberOfNodes() != (int32_t)m_weightLists.size() || metricOut->getNumberOfColumns() != numCols) { metricOut->setNumberOfNodesAndColumns(m_weightLists.size(), numCols); } vector scratch(metricIn->getNumberOfNodes()); if (roi != NULL) { if (roi->getNumberOfNodes() != (int32_t)m_weightLists.size()) { throw CaretException("roi does not match surface number of nodes"); } for (int32_t i = 0; i < numCols; ++i) { smoothColumnInternal(scratch.data(), metricIn, i, metricOut, i, roi, 0, fixZeros); } } else { for (int32_t i = 0; i < numCols; ++i) { smoothColumnInternal(scratch.data(), metricIn, i, metricOut, i, fixZeros); } } } void MetricSmoothingObject::smoothColumnInternal(float* scratch, const MetricFile* metricIn, const int& whichColumn, MetricFile* metricOut, const int& whichOutColumn, const bool& fixZeros) const { CaretAssert(metricIn != NULL);//asserts only, and only basic checks, these functions are private CaretAssert(metricOut != NULL); CaretAssert(scratch != NULL); CaretAssert(whichColumn >= 0 && whichColumn < metricIn->getNumberOfColumns()); CaretAssert(whichOutColumn >= 0 && whichOutColumn < metricOut->getNumberOfColumns()); const float* myColumn = metricIn->getValuePointerForColumn(whichColumn); int32_t numNodes = metricIn->getNumberOfNodes(); if (fixZeros)//special case early to keep branching down { #pragma omp CARET_PARFOR schedule(dynamic) for (int32_t i = 0; i < numNodes; ++i) { const WeightList& myWeightRef = m_weightLists[i]; if (myWeightRef.m_weightSum != 0.0f)//skip nodes with no neighbors quickly { float sum = 0.0f, weightsum = 0.0f; int32_t numWeights = myWeightRef.m_nodes.size(); for (int32_t j = 0; j < numWeights; ++j) { float value = myColumn[myWeightRef.m_nodes[j]]; if (value != 0.0f) { float weight = myWeightRef.m_weights[j]; sum += weight * value; weightsum += weight; } } if (weightsum != 0.0f) { scratch[i] = sum / weightsum; } else { scratch[i] = 0.0f; } } else { scratch[i] = 0.0f;//but we do need to zero what we skip, so a list of nodes to check may not help } } } else { #pragma omp CARET_PARFOR schedule(dynamic) for (int32_t i = 0; i < numNodes; ++i) { const WeightList& myWeightRef = m_weightLists[i]; if (myWeightRef.m_weightSum != 0.0f) { float sum = 0.0f; int32_t numWeights = myWeightRef.m_nodes.size(); for (int32_t j = 0; j < numWeights; ++j) { sum += myWeightRef.m_weights[j] * myColumn[myWeightRef.m_nodes[j]]; } scratch[i] = sum / myWeightRef.m_weightSum; } else { scratch[i] = 0.0f; } } } metricOut->setValuesForColumn(whichOutColumn, scratch); } void MetricSmoothingObject::smoothColumnInternal(float* scratch, const MetricFile* metricIn, const int& whichColumn, MetricFile* metricOut, const int& whichOutColumn, const MetricFile* roi, const int& whichRoiColumn, const bool& fixZeros) const { CaretAssert(metricIn != NULL);//asserts only, and only basic checks, these functions are private CaretAssert(metricOut != NULL); CaretAssert(scratch != NULL); CaretAssert(roi != NULL); CaretAssert(whichColumn >= 0 && whichColumn < metricIn->getNumberOfColumns()); CaretAssert(whichOutColumn >= 0 && whichOutColumn < metricOut->getNumberOfColumns()); CaretAssert(whichRoiColumn >= 0 && whichRoiColumn < roi->getNumberOfColumns()); const float* myColumn = metricIn->getValuePointerForColumn(whichColumn); const float* roiColumn = roi->getValuePointerForColumn(whichRoiColumn); int32_t numNodes = metricIn->getNumberOfNodes(); if (fixZeros)//special case early to keep branching down { #pragma omp CARET_PARFOR schedule(dynamic) for (int32_t i = 0; i < numNodes; ++i) { const WeightList& myWeightRef = m_weightLists[i]; if (roiColumn[i] > 0.0f && myWeightRef.m_weightSum != 0.0f)//skip nodes with no neighbors quickly { float sum = 0.0f, weightsum = 0.0f; int32_t numWeights = myWeightRef.m_nodes.size(); for (int32_t j = 0; j < numWeights; ++j) { int32_t neighbor = myWeightRef.m_nodes[j]; float value = myColumn[neighbor]; if (roiColumn[neighbor] > 0.0f && value != 0.0f) { float weight = myWeightRef.m_weights[j]; sum += weight * value; weightsum += weight; } } if (weightsum != 0.0f) { scratch[i] = sum / weightsum; } else { scratch[i] = 0.0f; } } else { scratch[i] = 0.0f;//but we do need to zero what we skip, so a list of nodes to check may not help } } } else { #pragma omp CARET_PARFOR schedule(dynamic) for (int32_t i = 0; i < numNodes; ++i) { const WeightList& myWeightRef = m_weightLists[i]; if (roiColumn[i] > 0.0f && myWeightRef.m_weightSum != 0.0f) { float sum = 0.0f, weightsum = 0.0f; int32_t numWeights = myWeightRef.m_nodes.size(); for (int32_t j = 0; j < numWeights; ++j) { int32_t neighbor = myWeightRef.m_nodes[j]; if (roiColumn[neighbor] > 0.0f) { float weight = myWeightRef.m_weights[j]; sum += weight * myColumn[neighbor]; weightsum += weight; } } if (weightsum != 0.0f) { scratch[i] = sum / weightsum; } else { scratch[i] = 0.0f; } } else { scratch[i] = 0.0f; } } } metricOut->setValuesForColumn(whichOutColumn, scratch); } void MetricSmoothingObject::precomputeWeightsGeoGauss(const SurfaceFile* mySurf, float myKernel) { int32_t numNodes = mySurf->getNumberOfNodes(); float myGeoDist = myKernel * 3.0f; float gaussianDenom = -0.5f / myKernel / myKernel; m_weightLists.resize(numNodes); #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(); vector distances; #pragma omp CARET_FOR schedule(dynamic) for (int32_t i = 0; i < numNodes; ++i) { myGeoHelp->getNodesToGeoDist(i, myGeoDist, m_weightLists[i].m_nodes, distances, true); if (distances.size() < 7) { m_weightLists[i].m_nodes = myTopoHelp->getNodeNeighbors(i); m_weightLists[i].m_nodes.push_back(i); myGeoHelp->getGeoToTheseNodes(i, m_weightLists[i].m_nodes, distances, true); } int32_t numNeigh = (int32_t)distances.size(); m_weightLists[i].m_weights.resize(numNeigh); m_weightLists[i].m_weightSum = 0.0f; for (int32_t j = 0; j < numNeigh; ++j) { float weight = exp(distances[j] * distances[j] * gaussianDenom);//exp(- dist ^ 2 / (2 * sigma ^ 2)) m_weightLists[i].m_weights[j] = weight; m_weightLists[i].m_weightSum += weight; } } } } void MetricSmoothingObject::precomputeWeightsROIGeoGauss(const SurfaceFile* mySurf, float myKernel, const MetricFile* theRoi) { 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); #pragma omp CARET_PAR { CaretPointer myTopoHelp = mySurf->getTopologyHelper(); CaretPointer myGeoHelp = mySurf->getGeodesicHelper(); vector distances; vector nodes; #pragma omp CARET_FOR schedule(dynamic) for (int32_t i = 0; i < numNodes; ++i) { if (myRoiColumn[i] > 0.0f) { myGeoHelp->getNodesToGeoDist(i, myGeoDist, nodes, distances, true); if (distances.size() < 7) { nodes = myTopoHelp->getNodeNeighbors(i); nodes.push_back(i); myGeoHelp->getGeoToTheseNodes(i, nodes, distances, true); } int32_t numNeigh = (int32_t)distances.size(); m_weightLists[i].m_weights.reserve(numNeigh); m_weightLists[i].m_nodes.reserve(numNeigh); m_weightLists[i].m_weightSum = 0.0f; for (int32_t j = 0; j < numNeigh; ++j) { if (myRoiColumn[nodes[j]] > 0.0f) { float weight = exp(distances[j] * distances[j] * gaussianDenom);//exp(- dist ^ 2 / (2 * sigma ^ 2)) m_weightLists[i].m_weights.push_back(weight); m_weightLists[i].m_nodes.push_back(nodes[j]); m_weightLists[i].m_weightSum += weight; } } } } } } void MetricSmoothingObject::precomputeWeightsGeoGaussArea(const SurfaceFile* mySurf, float myKernel, const float* nodeAreas) {//this method is normalized in two ways to provide evenly diffusing smoothing with equivalent sum of areas * values as input 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(new GeodesicHelper(myGeoBase)); vector distances; #pragma omp CARET_FOR schedule(dynamic) for (int32_t i = 0; i < numNodes; ++i) { myGeoHelp->getNodesToGeoDist(i, myGeoDist, tempList[i].m_nodes, distances, true); const vector& tempneighbors = myTopoHelp->getNodeNeighbors(i); if (distances.size() <= tempneighbors.size())//because neighbors doesn't include center, so if they are equal, geo is missing a neighbor { tempList[i].m_nodes = tempneighbors; tempList[i].m_nodes.push_back(i); myGeoHelp->getGeoToTheseNodes(i, tempList[i].m_nodes, distances, true); } int32_t numNeigh = (int32_t)distances.size(); tempList[i].m_weights.resize(numNeigh); tempList[i].m_weightSum = 0.0f; for (int32_t j = 0; j < numNeigh; ++j) { float weight = exp(distances[j] * distances[j] * gaussianDenom) * nodeAreas[tempList[i].m_nodes[j]];//exp(- dist ^ 2 / (2 * sigma ^ 2)) * area tempList[i].m_weights[j] = weight;//we multiply by area so that a node scattering to a dense region on one side and a sparse region on the other tempList[i].m_weightSum += weight;//gives similar areal influence to each direction rather than giving a more influence on the dense region (simply because nodes are more numerous) } float myFactor = nodeAreas[i] / tempList[i].m_weightSum;//make each scattering kernel sum to the area of the node it scatters from for (int32_t j = 0; j < numNeigh; ++j) { tempList[i].m_weights[j] *= myFactor; } tempList[i].m_weightSum = nodeAreas[i]; } } m_weightLists.resize(numNodes);//now convert it to gathering kernels for (int32_t i = 0; i < numNodes; ++i)//sadly, this is VERY hard to parallelize in a manner that is efficient, since it needs random access modification { m_weightLists[i].m_weightSum = 0.0f;//memory initialization may not go much faster in parallel size_t neighborCount = tempList[i].m_nodes.size(); m_weightLists[i].m_nodes.reserve(neighborCount);//also preallocate the expected number of nodes (geodesic distance should be symmetric except for rounding errors, so it should usually be exact) m_weightLists[i].m_weights.reserve(neighborCount); } for (int32_t i = 0; i < numNodes; ++i)//and this needs to push onto random vectors in the weight list { int32_t numNeigh = tempList[i].m_nodes.size(); for (int32_t j = 0; j < numNeigh; ++j) { int32_t node = tempList[i].m_nodes[j]; float weight = tempList[i].m_weights[j]; m_weightLists[node].m_nodes.push_back(i); m_weightLists[node].m_weights.push_back(weight); m_weightLists[node].m_weightSum += weight; } } } void MetricSmoothingObject::precomputeWeightsROIGeoGaussArea(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; 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(new GeodesicHelper(myGeoBase)); vector distances; vector nodes; #pragma omp CARET_FOR schedule(dynamic) for (int32_t i = 0; i < numNodes; ++i) { if (myRoiColumn[i] > 0.0f)//we don't need to scatter from things outside the ROI { myGeoHelp->getNodesToGeoDist(i, myGeoDist, nodes, distances, true); const vector& tempneighbors = myTopoHelp->getNodeNeighbors(i); if (distances.size() <= tempneighbors.size())//because neighbors doesn't include center, so if they are equal, geo is missing a neighbor { nodes = tempneighbors; nodes.push_back(i); myGeoHelp->getGeoToTheseNodes(i, nodes, distances, true); } int32_t numNeigh = (int32_t)distances.size(); tempList[i].m_weightSum = 0.0f; for (int32_t j = 0; j < numNeigh; ++j) {//but we DO need to compute scattering TO things outside the ROI, so that our normalization doesn't increase the in-ROI influence of edge nodes float weight = exp(distances[j] * distances[j] * gaussianDenom) * nodeAreas[nodes[j]];//exp(- dist ^ 2 / (2 * sigma ^ 2)) * area tempList[i].m_weightSum += weight;//add it to the total weight in order to normalize correctly if (myRoiColumn[nodes[j]] > 0.0f) {//BUT, don't add it to the list if it is outside the ROI tempList[i].m_nodes.push_back(nodes[j]); tempList[i].m_weights.push_back(weight); } } float myFactor = nodeAreas[i] / tempList[i].m_weightSum;//make each scattering kernel sum to the area of the node it scatters from int32_t numUsed = (int32_t)tempList[i].m_nodes.size(); for (int32_t j = 0; j < numUsed; ++j) { tempList[i].m_weights[j] *= myFactor; } tempList[i].m_weightSum = 0.0f;//this is never actually used again, but make sure it is wrong in case anything tries to use it } } } m_weightLists.resize(numNodes);//now convert it to gathering kernels for (int32_t i = 0; i < numNodes; ++i)//sadly, this is VERY hard to parallelize in a manner that is efficient, since it needs random access modification { m_weightLists[i].m_weightSum = 0.0f;//memory initialization may not go much faster in parallel size_t neighborCount = tempList[i].m_nodes.size(); m_weightLists[i].m_nodes.reserve(neighborCount);//also preallocate the expected number of nodes, again, should be exact except for rounding errors in geodesic distance m_weightLists[i].m_weights.reserve(neighborCount); } for (int32_t i = 0; i < numNodes; ++i)//and this needs to push onto random vectors in the weight list { int32_t numNeigh = tempList[i].m_nodes.size(); for (int32_t j = 0; j < numNeigh; ++j) { int32_t node = tempList[i].m_nodes[j]; float weight = tempList[i].m_weights[j]; m_weightLists[node].m_nodes.push_back(i); m_weightLists[node].m_weights.push_back(weight); m_weightLists[node].m_weightSum += weight; } } } void MetricSmoothingObject::precomputeWeightsGeoGaussEqual(const SurfaceFile* mySurf, float myKernel) {//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); #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(); vector distances; #pragma omp CARET_FOR schedule(dynamic) for (int32_t i = 0; i < numNodes; ++i) { myGeoHelp->getNodesToGeoDist(i, myGeoDist, tempList[i].m_nodes, distances, true); const vector& tempneighbors = myTopoHelp->getNodeNeighbors(i); if (distances.size() <= tempneighbors.size())//because neighbors doesn't include center, so if they are equal, geo is missing a neighbor { tempList[i].m_nodes = tempneighbors; tempList[i].m_nodes.push_back(i); myGeoHelp->getGeoToTheseNodes(i, tempList[i].m_nodes, distances, true); } int32_t numNeigh = (int32_t)distances.size(); tempList[i].m_weights.resize(numNeigh); tempList[i].m_weightSum = 0.0f; for (int32_t j = 0; j < numNeigh; ++j) { float weight = exp(distances[j] * distances[j] * gaussianDenom);//exp(- dist ^ 2 / (2 * sigma ^ 2)) tempList[i].m_weights[j] = weight;//we multiply by area so that a node scattering to a dense region on one side and a sparse region on the other tempList[i].m_weightSum += weight;//gives similar areal influence to each direction rather than giving a more influence on the dense region (simply because nodes are more numerous) } float myFactor = 1.0f / tempList[i].m_weightSum;//make each scattering kernel sum to 1 for (int32_t j = 0; j < numNeigh; ++j) { tempList[i].m_weights[j] *= myFactor; } tempList[i].m_weightSum = 1.0f; } } m_weightLists.resize(numNodes);//now convert it to gathering kernels for (int32_t i = 0; i < numNodes; ++i)//sadly, this is VERY hard to parallelize in a manner that is efficient, since it needs random access modification { m_weightLists[i].m_weightSum = 0.0f;//memory initialization may not go much faster in parallel size_t neighborCount = tempList[i].m_nodes.size(); m_weightLists[i].m_nodes.reserve(neighborCount);//also preallocate the expected number of nodes (geodesic distance should be symmetric except for rounding errors, so it should usually be exact) m_weightLists[i].m_weights.reserve(neighborCount); } for (int32_t i = 0; i < numNodes; ++i)//and this needs to push onto random vectors in the weight list { int32_t numNeigh = tempList[i].m_nodes.size(); for (int32_t j = 0; j < numNeigh; ++j) { int32_t node = tempList[i].m_nodes[j]; float weight = tempList[i].m_weights[j]; m_weightLists[node].m_nodes.push_back(i); m_weightLists[node].m_weights.push_back(weight); m_weightLists[node].m_weightSum += weight; } } } void MetricSmoothingObject::precomputeWeightsROIGeoGaussEqual(const SurfaceFile* mySurf, float myKernel, const MetricFile* theRoi) { 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); const float* myRoiColumn = theRoi->getValuePointerForColumn(0); #pragma omp CARET_PAR { CaretPointer myTopoHelp = mySurf->getTopologyHelper(); CaretPointer myGeoHelp = mySurf->getGeodesicHelper(); vector distances; vector nodes; #pragma omp CARET_FOR schedule(dynamic) for (int32_t i = 0; i < numNodes; ++i) { if (myRoiColumn[i] > 0.0f)//we don't need to scatter from things outside the ROI { myGeoHelp->getNodesToGeoDist(i, myGeoDist, nodes, distances, true); const vector& tempneighbors = myTopoHelp->getNodeNeighbors(i); if (distances.size() <= tempneighbors.size())//because neighbors doesn't include center, so if they are equal, geo is missing a neighbor { nodes = tempneighbors; nodes.push_back(i); myGeoHelp->getGeoToTheseNodes(i, nodes, distances, true); } int32_t numNeigh = (int32_t)distances.size(); tempList[i].m_weightSum = 0.0f; for (int32_t j = 0; j < numNeigh; ++j) {//but we DO need to compute scattering TO things outside the ROI, so that our normalization doesn't increase the in-ROI influence of edge nodes float weight = exp(distances[j] * distances[j] * gaussianDenom);//exp(- dist ^ 2 / (2 * sigma ^ 2)) tempList[i].m_weightSum += weight;//add it to the total weight in order to normalize correctly if (myRoiColumn[nodes[j]] > 0.0f) {//BUT, don't add it to the list if it is outside the ROI tempList[i].m_nodes.push_back(nodes[j]); tempList[i].m_weights.push_back(weight); } } float myFactor = 1.0f / tempList[i].m_weightSum;//make each scattering kernel sum to 1 int32_t numUsed = (int32_t)tempList[i].m_nodes.size(); for (int32_t j = 0; j < numUsed; ++j) { tempList[i].m_weights[j] *= myFactor; } tempList[i].m_weightSum = 0.0f;//this is never actually used again, but make sure it is wrong in case anything tries to use it } } } m_weightLists.resize(numNodes);//now convert it to gathering kernels for (int32_t i = 0; i < numNodes; ++i)//sadly, this is VERY hard to parallelize in a manner that is efficient, since it needs random access modification { m_weightLists[i].m_weightSum = 0.0f;//memory initialization may not go much faster in parallel size_t neighborCount = tempList[i].m_nodes.size(); m_weightLists[i].m_nodes.reserve(neighborCount);//also preallocate the expected number of nodes, again, should be exact except for rounding errors in geodesic distance m_weightLists[i].m_weights.reserve(neighborCount); } for (int32_t i = 0; i < numNodes; ++i)//and this needs to push onto random vectors in the weight list { int32_t numNeigh = tempList[i].m_nodes.size(); for (int32_t j = 0; j < numNeigh; ++j) { int32_t node = tempList[i].m_nodes[j]; float weight = tempList[i].m_weights[j]; m_weightLists[node].m_nodes.push_back(i); m_weightLists[node].m_weights.push_back(weight); m_weightLists[node].m_weightSum += weight; } } } void MetricSmoothingObject::precomputeWeights(const SurfaceFile* mySurf, float myKernel, const MetricFile* theRoi, Method myMethod, const float* nodeAreas) { 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 (theRoi != NULL) { switch (myMethod) { case GEO_GAUSS_AREA: precomputeWeightsROIGeoGaussArea(mySurf, myKernel, theRoi, passAreas); break; case GEO_GAUSS_EQUAL: precomputeWeightsROIGeoGaussEqual(mySurf, myKernel, theRoi); break; case GEO_GAUSS: precomputeWeightsROIGeoGauss(mySurf, myKernel, theRoi); break; default: throw CaretException("unknown smoothing method specified"); }; } else { switch (myMethod) { case GEO_GAUSS_AREA: precomputeWeightsGeoGaussArea(mySurf, myKernel, passAreas); break; case GEO_GAUSS_EQUAL: precomputeWeightsGeoGaussEqual(mySurf, myKernel); break; case GEO_GAUSS: precomputeWeightsGeoGauss(mySurf, myKernel); break; default: throw CaretException("unknown smoothing method specified"); }; } } connectome-workbench-1.2.3+git41-gc4c6c90/src/Files/MetricSmoothingObject.h000066400000000000000000000104451300200146000262250ustar00rootroot00000000000000#ifndef __METRIC_SMOOTHING_OBJECT_H__ #define __METRIC_SMOOTHING_OBJECT_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*/ //NOTE: this is largely for special-purpose smoothing by providing greater efficiency and flexibility, since it can be reused with different rois or on different metric objects // after being constructed once (constructor takes a while). If you just want to smooth one metric object with one (or no) ROI, you probably want AlgorithmMetricSmoothing. // //NOTE: this object contains no mutable members, multiple threads can call the same function on the same instance and expect consistent behavior, while running concurrently, // as long as they don't call it with output arguments that overlap (same instance, same row, or one row plus full metric, etc) // //NOTE: for a static ROI, it is (sometimes much) more efficient to use it in the constructor, and provide no ROI (NULL) to the functions, using both an ROI in constructor and in method // will result in the effective ROI being the logical AND of the two (intersection). #include "stdint.h" #include "stddef.h" #include namespace caret { class SurfaceFile; class MetricFile; class MetricSmoothingObject { public: enum Method { GEO_GAUSS_AREA, GEO_GAUSS_EQUAL, GEO_GAUSS }; MetricSmoothingObject(const SurfaceFile* mySurf, const float& kernel, const MetricFile* myRoi = NULL, Method myMethod = GEO_GAUSS_AREA, const float* nodeAreas = NULL); void smoothColumn(const MetricFile* metricIn, const int& whichColumn, MetricFile* columnOut, const MetricFile* roi = NULL, const bool& fixZeros = false) const; void smoothColumn(const MetricFile* metricIn, const int& whichColumn, MetricFile* metricOut, const int& whichOutColumn, const MetricFile* roi = NULL, const int& whichRoiColumn = 0, const bool& fixZeros = false) const; void smoothMetric(const MetricFile* metricIn, MetricFile* metricOut, const MetricFile* roi = NULL, const bool& fixZeros = false) const; private: struct WeightList { std::vector m_nodes; std::vector m_weights; float m_weightSum; }; std::vector m_weightLists; 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 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); MetricSmoothingObject(); }; } #endif //__METRIC_SMOOTHING_OBJECT_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Files/NodeAndVoxelColoring.cxx000066400000000000000000001111561300200146000263620ustar00rootroot00000000000000 /*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 #define __NODE_AND_VOXEL_COLORING_DECLARE__ #include "NodeAndVoxelColoring.h" #undef __NODE_AND_VOXEL_COLORING_DECLARE__ #include "CaretAssert.h" #include "CaretLogger.h" #include "CaretOMP.h" #include "GiftiLabel.h" #include "GiftiLabelTable.h" #include "GroupAndNameHierarchyItem.h" #include "Palette.h" #include "PaletteColorMapping.h" #include "CaretOMP.h" using namespace caret; static const float positiveThresholdGreenColor[4] = { 115.0f / 255.0f, 255.0f / 255.0f, 180.0f / 255.0f, 255.0f / 255.0f }; static const float negativeThresholdGreenColor[] = { 180.0f / 255.0f, 255.0f / 255.0f, 115.0f / 255.0f, 255.0f / 255.0f }; /** * \class NodeAndVoxelColoring * \brief Static methods for coloring nodes and voxels. * * Provides methods for coloring nodes and voxels. */ /** * Color scalars using a palette that accepts a void* type for the * color array to that multiple data types are supported without * having to allocate memory for conversion to one data type or * the other nor duplicate lots of code for each data type. * * @param 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 thresholdValues * Thresholds for inhibiting coloring. * Number of elements is 'numberOfScalars'. * @param numberOfScalars * Number of scalars and thresholds. * @param colorDataType * Data type of the rgbaOut parameter * @param rgbaOutPointer * RGBA Colors that are output. This is a VOID type and its * true type is provided by the previous parameter colorDataType. * @param ignoreThresholding * If true, skip all threshold testing */ 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) { if (numberOfScalars <= 0) { return; } CaretAssert(statistics); CaretAssert(paletteColorMapping); CaretAssert(palette); CaretAssert(scalarValues); CaretAssert(thresholdValues); CaretAssert(rgbaOutPointer); /* * Cast to data type for rgba coloring */ float* rgbaFloat = NULL; uint8_t* rgbaUnsignedByte = NULL; switch (colorDataType) { case COLOR_TYPE_FLOAT: rgbaFloat = (float*)rgbaOutPointer; break; case COLOR_TYPE_UNSIGNED_BTYE: rgbaUnsignedByte = (uint8_t*)rgbaOutPointer; break; } /* * Type of threshold testing */ bool showOutsideFlag = false; const PaletteThresholdTestEnum::Enum thresholdTest = paletteColorMapping->getThresholdTest(); switch (thresholdTest) { case PaletteThresholdTestEnum::THRESHOLD_TEST_SHOW_OUTSIDE: showOutsideFlag = true; break; case PaletteThresholdTestEnum::THRESHOLD_TEST_SHOW_INSIDE: showOutsideFlag = false; break; } /* * 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(); /* * Skip threshold testing? */ const bool skipThresholdTesting = (ignoreThresholding || (thresholdType == PaletteThresholdTypeEnum::THRESHOLD_TYPE_OFF)); /* * Display of negative, zero, and positive values allowed. */ const bool hidePositiveValues = (paletteColorMapping->isDisplayPositiveDataFlag() == false); const bool hideNegativeValues = (paletteColorMapping->isDisplayNegativeDataFlag() == false); const bool hideZeroValues = (paletteColorMapping->isDisplayZeroDataFlag() == false); const bool interpolateFlag = paletteColorMapping->isInterpolatePaletteFlag(); /* * Convert data values to normalized palette values. */ std::vector normalizedValues(numberOfScalars); paletteColorMapping->mapDataToPaletteNormalizedValues(statistics, scalarValues, &normalizedValues[0], numberOfScalars); /* * Get color for normalized values of -1.0 and 1.0. * Since there may be a large number of values that are -1.0 or 1.0 * we can compute the color only once for these values and save time. */ float rgbaPositiveOne[4], rgbaNegativeOne[4]; palette->getPaletteColor(1.0, interpolateFlag, rgbaPositiveOne); const bool rgbaPositiveOneValid = (rgbaPositiveOne[3] > 0.0); palette->getPaletteColor(-1.0, interpolateFlag, rgbaNegativeOne); const bool rgbaNegativeOneValid = (rgbaNegativeOne[3] > 0.0); /* * Color all scalars. */ #pragma omp CARET_PARFOR schedule(dynamic, 4096) for (int64_t i = 0; i < numberOfScalars; i++) { const int64_t i4 = i * 4; /* * Initialize coloring for node since one of the * continue statements below may cause moving * on to next node */ switch (colorDataType) { case COLOR_TYPE_FLOAT: rgbaFloat[i4] = 0.0; rgbaFloat[i4+1] = 0.0; rgbaFloat[i4+2] = 0.0; rgbaFloat[i4+3] = 0.0; break; case COLOR_TYPE_UNSIGNED_BTYE: rgbaUnsignedByte[i4] = 0; rgbaUnsignedByte[i4+1] = 0; rgbaUnsignedByte[i4+2] = 0; rgbaUnsignedByte[i4+3] = 0; break; } float scalar = scalarValues[i]; const float threshold = thresholdValues[i]; /* * Positive/Zero/Negative Test */ if (scalar > PaletteColorMapping::SMALL_POSITIVE) { // JWH 24 April 2015 NodeAndVoxelColoring::SMALL_POSITIVE) { if (hidePositiveValues) { continue; } } else if (scalar < PaletteColorMapping::SMALL_NEGATIVE) { // JWH 24 April 2015 NodeAndVoxelColoring::SMALL_NEGATIVE) { if (hideNegativeValues) { continue; } } else { /* * May be very near zero so force to zero. */ normalizedValues[i] = 0.0; if (hideZeroValues) { continue; } } /* * Temporary for rgba coloring now that past possible * continue statements */ float rgbaOut[4] = { 0.0, 0.0, 0.0, 0.0 }; const float normalValue = normalizedValues[i]; /* * RGBA colors have been mapped for extreme values */ if (normalValue >= 1.0) { if (rgbaPositiveOneValid) { rgbaOut[0] = rgbaPositiveOne[0]; rgbaOut[1] = rgbaPositiveOne[1]; rgbaOut[2] = rgbaPositiveOne[2]; rgbaOut[3] = rgbaPositiveOne[3]; } } else if (normalValue <= -1.0) { if (rgbaNegativeOneValid) { rgbaOut[0] = rgbaNegativeOne[0]; rgbaOut[1] = rgbaNegativeOne[1]; rgbaOut[2] = rgbaNegativeOne[2]; rgbaOut[3] = rgbaNegativeOne[3]; } } else { /* * Color scalar using palette */ float rgba[4]; palette->getPaletteColor(normalValue, interpolateFlag, rgba); if (rgba[3] > 0.0f) { rgbaOut[0] = rgba[0]; rgbaOut[1] = rgba[1]; rgbaOut[2] = rgba[2]; rgbaOut[3] = rgba[3]; } } /* * Threshold Test * Threshold is done last so colors are still set * but if threshold test fails, alpha is set invalid. */ bool thresholdPassedFlag = false; if (skipThresholdTesting) { thresholdPassedFlag = true; } else if (showOutsideFlag) { if (threshold > thresholdMaximum) { thresholdPassedFlag = true; } else if (threshold < thresholdMinimum) { thresholdPassedFlag = true; } } else { if ((threshold >= thresholdMinimum) && (threshold <= thresholdMaximum)) { thresholdPassedFlag = true; } } if (thresholdPassedFlag == false) { rgbaOut[3] = 0.0; if (showMappedThresholdFailuresInGreen) { if (thresholdType == PaletteThresholdTypeEnum::THRESHOLD_TYPE_MAPPED) { if (threshold > 0.0f) { if ((threshold < thresholdMappedPositive) && (threshold > thresholdMappedPositiveAverageArea)) { rgbaOut[0] = positiveThresholdGreenColor[0]; rgbaOut[1] = positiveThresholdGreenColor[1]; rgbaOut[2] = positiveThresholdGreenColor[2]; rgbaOut[3] = positiveThresholdGreenColor[3]; } } else if (threshold < 0.0f) { if ((threshold > thresholdMappedNegative) && (threshold < thresholdMappedNegativeAverageArea)) { rgbaOut[0] = negativeThresholdGreenColor[0]; rgbaOut[1] = negativeThresholdGreenColor[1]; rgbaOut[2] = negativeThresholdGreenColor[2]; rgbaOut[3] = negativeThresholdGreenColor[3]; } } } } } switch (colorDataType) { case COLOR_TYPE_FLOAT: CaretAssertArrayIndex(rgbaFloat, numberOfScalars * 4, i*4+3); rgbaFloat[i4] = rgbaOut[0]; rgbaFloat[i4+1] = rgbaOut[1]; rgbaFloat[i4+2] = rgbaOut[2]; rgbaFloat[i4+3] = rgbaOut[3]; break; case COLOR_TYPE_UNSIGNED_BTYE: CaretAssertArrayIndex(rgbaUnsignedByte, numberOfScalars * 4, i*4+3); rgbaUnsignedByte[i4] = rgbaOut[0] * 255.0; rgbaUnsignedByte[i4+1] = rgbaOut[1] * 255.0; rgbaUnsignedByte[i4+2] = rgbaOut[2] * 255.0; if (rgbaOut[3] > 0.0) { rgbaUnsignedByte[i4+3] = rgbaOut[3] * 255.0; } else { rgbaUnsignedByte[i4+3] = 0; } break; } } } /** * Color scalars using a palette. * * @param 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 thresholdValues * Thresholds for inhibiting coloring. * Number of elements is 'numberOfScalars'. * @param numberOfScalars * Number of scalars and thresholds. * @param rgbaOut * RGBA Colors that are output. The alpha * value will be negative if the scalar does * not receive any coloring. * Number of elements is 'numberOfScalars' * 4. * @param ignoreThresholding * If true, skip all threshold testing */ void NodeAndVoxelColoring::colorScalarsWithPalette(const FastStatistics* statistics, const PaletteColorMapping* paletteColorMapping, const Palette* palette, const float* scalarValues, const float* thresholdValues, const int64_t numberOfScalars, float* rgbaOut, const bool ignoreThresholding) { colorScalarsWithPalettePrivate(statistics, paletteColorMapping, palette, scalarValues, thresholdValues, numberOfScalars, COLOR_TYPE_FLOAT, (void*)rgbaOut, ignoreThresholding); } /** * Color scalars using a palette. * * @param 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 thresholdValues * Thresholds for inhibiting coloring. * Number of elements is 'numberOfScalars'. * @param numberOfScalars * Number of scalars and thresholds. * @param rgbaOut * RGBA Colors that are output. The alpha * value will be negative if the scalar does * not receive any coloring. * Number of elements is 'numberOfScalars' * 4. * @param ignoreThresholding * If true, skip all threshold testing */ void NodeAndVoxelColoring::colorScalarsWithPalette(const FastStatistics* statistics, const PaletteColorMapping* paletteColorMapping, const Palette* palette, const float* scalarValues, const float* thresholdValues, const int64_t numberOfScalars, uint8_t* rgbaOut, const bool ignoreThresholding) { colorScalarsWithPalettePrivate(statistics, paletteColorMapping, palette, scalarValues, thresholdValues, numberOfScalars, COLOR_TYPE_UNSIGNED_BTYE, (void*)rgbaOut, ignoreThresholding); } /** * Color RGBA data. * * @param redComponents * Values for red components. Range [0, 255]. * @param greenComponents * Values for green components. Range [0, 255]. * @param blueComponents * Values for blue components. Range [0, 255]. * @param alphaComponents * Values for alpha components (NULL if alpha not valid) * @param colorDataType * Data type of the rgbaOut parameter * @param rgbThreshold * Threshold RGB (voxel not drawn if voxel RGB components LESS THAN these values). * Range [0, 255]. * @param rgbaOutPointer * RGBA Colors that are output. The alpha * value will be negative if the scalar does * not receive any coloring. * Number of elements is 'numberOfScalars' * 4. */ void NodeAndVoxelColoring::colorScalarsWithRGBAPrivate(const float* redComponents, const float* greenComponents, const float* blueComponents, const float* alphaComponents, const int64_t numberOfComponents, const ColorDataType colorDataType, const uint8_t* rgbThreshold, uint8_t* rgbaOutPointer) { /* * Cast to data type for rgba coloring */ float* rgbaFloat = NULL; uint8_t* rgbaUnsignedByte = NULL; float thresholdRed = -1.0; float thresholdGreen = -1.0; float thresholdBlue = -1.0; switch (colorDataType) { case COLOR_TYPE_FLOAT: { rgbaFloat = (float*)rgbaOutPointer; const float* threshFloat = (float*)rgbThreshold; thresholdRed = threshFloat[0]; thresholdGreen = threshFloat[1]; thresholdBlue = threshFloat[2]; } break; case COLOR_TYPE_UNSIGNED_BTYE: { rgbaUnsignedByte = (uint8_t*)rgbaOutPointer; const uint8_t* threshByte = (uint8_t*)rgbThreshold; thresholdRed = threshByte[0]; thresholdGreen = threshByte[1]; thresholdBlue = threshByte[2]; } break; } for (int64_t i = 0; i < numberOfComponents; i++) { const float red = redComponents[i]; const float green = greenComponents[i]; const float blue = blueComponents[i]; float alpha = 0.0; if ((red >= thresholdRed) && (green >= thresholdGreen) && (blue >= thresholdBlue)) { alpha = ((alphaComponents == NULL) ? 255.0 : alphaComponents[i]); } const int64_t i4 = i * 4; switch (colorDataType) { case COLOR_TYPE_FLOAT: rgbaFloat[i4] = red / 255.0; rgbaFloat[i4+1] = green / 255.0; rgbaFloat[i4+2] = blue / 255.0; rgbaFloat[i4+3] = alpha / 255.0; break; case COLOR_TYPE_UNSIGNED_BTYE: rgbaUnsignedByte[i4] = static_cast(red); rgbaUnsignedByte[i4+1] = static_cast(green); rgbaUnsignedByte[i4+2] = static_cast(blue); rgbaUnsignedByte[i4+3] = static_cast(alpha); break; } } } /** * Color RGBA data. * * @param redComponents * Values for red components. * @param greenComponents * Values for green components. * @param blueComponents * Values for blue components. * @param alphaComponents * Values for alpha components (NULL if alpha not valid) * @param numberOfComponents * Number of components (each color component contains this number of values). * @param rgbThreshold * Threshold RGB (voxel not drawn if voxel RGB components LESS THAN these values). * Range is [0, 255]. * @param rgbaOut * RGBA Colors that are output. The alpha * value will be negative if the scalar does * not receive any coloring. * Number of elements is 'numberOfComponents' * 4. */ void NodeAndVoxelColoring::colorScalarsWithRGBA(const float* redComponents, const float* greenComponents, const float* blueComponents, const float* alphaComponents, const int64_t numberOfComponents, const uint8_t rgbThreshold[3], uint8_t* rgbaOut) { colorScalarsWithRGBAPrivate(redComponents, greenComponents, blueComponents, alphaComponents, numberOfComponents, COLOR_TYPE_UNSIGNED_BTYE, rgbThreshold, rgbaOut); } /** * Assign colors to label indices using a GIFTI label table. * * @param labelTabl * Label table used for coloring and indexing with label indices. * @param labelIndices * The indices are are used to access colors in the label table. * @param numberOfIndices * Number of indices. * @param displayGroup * The selected display group. * @param tabIndex * Index of selected tab. * @param rgbv * Output with assigned colors. Number of elements is (numberOfIndices * 4). */ void NodeAndVoxelColoring::colorIndicesWithLabelTableForDisplayGroupTab(const GiftiLabelTable* labelTable, const float* labelIndices, const int64_t numberOfIndices, const DisplayGroupEnum::Enum displayGroup, const int32_t tabIndex, float* rgbv) { NodeAndVoxelColoring::colorIndicesWithLabelTableForDisplayGroupTabPrivate(labelTable, labelIndices, numberOfIndices, displayGroup, tabIndex, COLOR_TYPE_FLOAT, (void*)rgbv); } /** * Assign colors to label indices using a GIFTI label table. * * @param labelTabl * Label table used for coloring and indexing with label indices. * @param labelIndices * The indices are are used to access colors in the label table. * @param numberOfIndices * Number of indices. * @param displayGroup * The selected display group. * @param tabIndex * Index of selected tab. * @param rgbv * Output with assigned colors. Number of elements is (numberOfIndices * 4). */ void NodeAndVoxelColoring::colorIndicesWithLabelTableForDisplayGroupTab(const GiftiLabelTable* labelTable, const float* labelIndices, const int64_t numberOfIndices, const DisplayGroupEnum::Enum displayGroup, const int32_t tabIndex, uint8_t* rgbv) { NodeAndVoxelColoring::colorIndicesWithLabelTableForDisplayGroupTabPrivate(labelTable, labelIndices, numberOfIndices, displayGroup, tabIndex, COLOR_TYPE_UNSIGNED_BTYE, (void*)rgbv); } /** * Assign colors to label indices using a GIFTI label table. * * @param labelTabl * Label table used for coloring and indexing with label indices. * @param labelIndices * The indices are are used to access colors in the label table. * @param numberOfIndices * Number of indices. * @param displayGroup * The selected display group. * @param tabIndex * Index of selected tab. * @param colorDataType * Data type of the rgbaOut parameter * @param rgbaOutPointer * RGBA Colors that are output. This is a VOID type and its * true type is provided by the previous parameter colorDataType. * @param rgbv * Output with assigned colors. Number of elements is (numberOfIndices * 4). */ void NodeAndVoxelColoring::colorIndicesWithLabelTableForDisplayGroupTabPrivate(const GiftiLabelTable* labelTable, const float* labelIndices, const int64_t numberOfIndices, const DisplayGroupEnum::Enum displayGroup, const int32_t tabIndex, const ColorDataType colorDataType, void* rgbaOutPointer) { /* * Cast to data type for rgba coloring */ float* rgbaFloat = NULL; uint8_t* rgbaUnsignedByte = NULL; switch (colorDataType) { case COLOR_TYPE_FLOAT: rgbaFloat = (float*)rgbaOutPointer; break; case COLOR_TYPE_UNSIGNED_BTYE: rgbaUnsignedByte = (uint8_t*)rgbaOutPointer; break; } /* * Invalidate all coloring. */ switch (colorDataType) { case COLOR_TYPE_FLOAT: for (int64_t i = 0; i < numberOfIndices; i++) { rgbaFloat[i*4+3] = 0.0; } break; case COLOR_TYPE_UNSIGNED_BTYE: for (int64_t i = 0; i < numberOfIndices; i++) { rgbaUnsignedByte[i*4+3] = 0; } break; } /* * Assign colors from labels to nodes */ float labelRGBA[4]; for (int64_t i = 0; i < numberOfIndices; i++) { const int64_t labelKey = static_cast(labelIndices[i]); const GiftiLabel* gl = labelTable->getLabel(labelKey); if (gl != NULL) { const GroupAndNameHierarchyItem* item = gl->getGroupNameSelectionItem(); bool colorDataFlag = false; if (item != NULL) { if (tabIndex == NodeAndVoxelColoring::INVALID_TAB_INDEX) { colorDataFlag = true; } else if (item->isSelected(displayGroup, tabIndex)) { colorDataFlag = true; } } else { colorDataFlag = true; } if (colorDataFlag) { gl->getColor(labelRGBA); if (labelRGBA[3] > 0.0) { const int64_t i4 = i * 4; switch (colorDataType) { case COLOR_TYPE_FLOAT: CaretAssertArrayIndex(rgbaFloat, numberOfIndices * 4, i*4+3); rgbaFloat[i*4] = labelRGBA[0]; rgbaFloat[i*4+1] = labelRGBA[1]; rgbaFloat[i*4+2] = labelRGBA[2]; rgbaFloat[i*4+3] = labelRGBA[3]; break; case COLOR_TYPE_UNSIGNED_BTYE: CaretAssertArrayIndex(rgbaUnsignedByte, numberOfIndices * 4, i*4+3); rgbaUnsignedByte[i4] = labelRGBA[0] * 255.0; rgbaUnsignedByte[i4+1] = labelRGBA[1] * 255.0; rgbaUnsignedByte[i4+2] = labelRGBA[2] * 255.0; if (labelRGBA[3] > 0.0) { rgbaUnsignedByte[i4+3] = labelRGBA[3] * 255.0; } else { rgbaUnsignedByte[i4+3] = 0; } break; } } } } } } /** * Assign colors to label indices using a GIFTI label table. * * @param labelTabl * Label table used for coloring and indexing with label indices. * @param labelIndices * The indices are are used to access colors in the label table. * @param numberOfIndices * Number of indices. * @param displayGroup * The selected display group. * @param tabIndex * Index of selected tab. * @param rgbv * Output with assigned colors. Number of elements is (numberOfIndices * 4). */ void NodeAndVoxelColoring::colorIndicesWithLabelTable(const GiftiLabelTable* labelTable, const float* labelIndices, const int64_t numberOfIndices, float* rgbv) { NodeAndVoxelColoring::colorIndicesWithLabelTableForDisplayGroupTabPrivate(labelTable, labelIndices, numberOfIndices, DisplayGroupEnum::DISPLAY_GROUP_TAB, NodeAndVoxelColoring::INVALID_TAB_INDEX, COLOR_TYPE_FLOAT, (void*)rgbv); } /** * Assign colors to label indices using a GIFTI label table. * * @param labelTabl * Label table used for coloring and indexing with label indices. * @param labelIndices * The indices are are used to access colors in the label table. * @param numberOfIndices * Number of indices. * @param displayGroup * The selected display group. * @param tabIndex * Index of selected tab. * @param rgbv * Output with assigned colors. Number of elements is (numberOfIndices * 4). */ void NodeAndVoxelColoring::colorIndicesWithLabelTable(const GiftiLabelTable* labelTable, const float* labelIndices, const int64_t numberOfIndices, uint8_t* rgbv) { NodeAndVoxelColoring::colorIndicesWithLabelTableForDisplayGroupTabPrivate(labelTable, labelIndices, numberOfIndices, DisplayGroupEnum::DISPLAY_GROUP_TAB, NodeAndVoxelColoring::INVALID_TAB_INDEX, COLOR_TYPE_UNSIGNED_BTYE, (void*)rgbv); } /** * Convert the slice coloring to outline mode. * * @param rgbaInOut * Coloring for the slice (input and output) * @param labelDrawingType * Type of drawing for label filling and outline. * @param labelOutlineColor * Outline color of label. * @param xdim * X-dimension of slice (number of columns) * @param ydim * Y-dimension of slice (number of rows). */ void NodeAndVoxelColoring::convertSliceColoringToOutlineMode(uint8_t* rgbaInOut, const LabelDrawingTypeEnum::Enum labelDrawingType, const CaretColorEnum::Enum labelOutlineColor, const int64_t xdim, const int64_t ydim) { /* * Copy the rgba colors */ const int64_t numRGBA = xdim * ydim * 4; if (numRGBA <= 0) { return; } std::vector sliceCopyVector(numRGBA); uint8_t* rgba = &sliceCopyVector[0]; for (int64_t i = 0; i < numRGBA; i++) { rgba[i] = rgbaInOut[i]; } uint8_t outlineRGBA[4]; CaretColorEnum::toRGBByte(labelOutlineColor, outlineRGBA); outlineRGBA[3] = 255; /* * Examine coloring for all voxels except those along the edge */ const int64_t lastX = xdim - 1; const int64_t lastY = ydim - 1; for (int64_t i = 1; i < lastX; i++) { for (int64_t j = 1; j < lastY; j++) { const int iStart = i - 1; const int iEnd = i + 1; const int jStart = j - 1; const int jEnd = j + 1; const int64_t myOffset = (i + (xdim * j)) * 4; CaretAssert(myOffset < numRGBA); const uint8_t* myRGBA = &rgba[myOffset]; if (myRGBA[3] <= 0) { continue; } /* * Determine if voxel colors match voxel coloring * of ALL immediate neighbors (8-connected). */ bool isLabelBoundaryVoxel = false; for (int64_t iNeigh = iStart; iNeigh <= iEnd; iNeigh++) { for (int64_t jNeigh = jStart; jNeigh <= jEnd; jNeigh++) { const int64_t neighOffset = (iNeigh + (xdim * jNeigh)) * 4; CaretAssert(neighOffset < numRGBA); const uint8_t* neighRGBA = &rgba[neighOffset]; for (int64_t k = 0; k < 4; k++) { if (myRGBA[k] != neighRGBA[k]) { isLabelBoundaryVoxel = true; break; } } if (isLabelBoundaryVoxel) { break; } } if (isLabelBoundaryVoxel) { break; } } /* * Override the coloring as needed. */ switch (labelDrawingType) { case LabelDrawingTypeEnum::DRAW_FILLED: break; case LabelDrawingTypeEnum::DRAW_FILLED_WITH_OUTLINE_COLOR: if (isLabelBoundaryVoxel) { rgbaInOut[myOffset] = outlineRGBA[0]; rgbaInOut[myOffset + 1] = outlineRGBA[1]; rgbaInOut[myOffset + 2] = outlineRGBA[2]; rgbaInOut[myOffset + 3] = outlineRGBA[3]; } break; case LabelDrawingTypeEnum::DRAW_OUTLINE_COLOR: if (isLabelBoundaryVoxel) { rgbaInOut[myOffset] = outlineRGBA[0]; rgbaInOut[myOffset + 1] = outlineRGBA[1]; rgbaInOut[myOffset + 2] = outlineRGBA[2]; rgbaInOut[myOffset + 3] = outlineRGBA[3]; } else { rgbaInOut[myOffset + 3] = 0; } break; case LabelDrawingTypeEnum::DRAW_OUTLINE_LABEL_COLOR: if ( ! isLabelBoundaryVoxel) { rgbaInOut[myOffset + 3] = 0; } break; } } } } connectome-workbench-1.2.3+git41-gc4c6c90/src/Files/NodeAndVoxelColoring.h000066400000000000000000000173071300200146000260120ustar00rootroot00000000000000#ifndef __NODE_AND_VOXEL_COLORING__H_ #define __NODE_AND_VOXEL_COLORING__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 "CaretColorEnum.h" #include "DisplayGroupEnum.h" #include "LabelDrawingTypeEnum.h" namespace caret { class FastStatistics; class GiftiLabelTable; class Palette; class PaletteColorMapping; class NodeAndVoxelColoring { public: static void colorScalarsWithPalette(const FastStatistics* statistics, const PaletteColorMapping* paletteColorMapping, const Palette* palette, const float* scalars, const float* scalarThresholds, const int64_t numberOfScalars, float* rgbaOut, const bool ignoreThresholding = false); static void colorScalarsWithPalette(const FastStatistics* statistics, const PaletteColorMapping* paletteColorMapping, const Palette* palette, const float* scalars, const float* scalarThresholds, const int64_t numberOfScalars, uint8_t* rgbaOut, const bool ignoreThresholding = false); static void colorScalarsWithRGBA(const float* redComponents, const float* greenComponents, const float* blueComponents, const float* alphaComponents, const int64_t numberOfComponents, const uint8_t rgbThreshold[3], uint8_t* rgbaOut); // JWH 24 April 2015 static const float SMALL_POSITIVE; // JWH 24 April 2015 static const float SMALL_NEGATIVE; static void colorIndicesWithLabelTableForDisplayGroupTab(const GiftiLabelTable* labelTable, const float* labelIndices, const int64_t numberOfIndices, const DisplayGroupEnum::Enum displayGroup, const int32_t tabIndex, float* rgbv); static void colorIndicesWithLabelTableForDisplayGroupTab(const GiftiLabelTable* labelTable, const float* labelIndices, const int64_t numberOfIndices, const DisplayGroupEnum::Enum displayGroup, const int32_t tabIndex, uint8_t* rgbv); static void colorIndicesWithLabelTable(const GiftiLabelTable* labelTable, const float* labelIndices, const int64_t numberOfIndices, float* rgbv); static void colorIndicesWithLabelTable(const GiftiLabelTable* labelTable, const float* labelIndices, const int64_t numberOfIndices, uint8_t* rgbv); static void convertSliceColoringToOutlineMode(uint8_t* rgbaInOut, const LabelDrawingTypeEnum::Enum labelDrawingType, const CaretColorEnum::Enum labelOutlineColor, const int64_t xdim, const int64_t ydim); private: enum ColorDataType { COLOR_TYPE_FLOAT, COLOR_TYPE_UNSIGNED_BTYE }; static void colorScalarsWithPalettePrivate(const FastStatistics* statistics, const PaletteColorMapping* paletteColorMapping, const Palette* palette, const float* scalars, const float* scalarThresholds, const int64_t numberOfScalars, const ColorDataType colorDataType, void* rgbaOutPointer, const bool ignoreThresholding); static void colorIndicesWithLabelTableForDisplayGroupTabPrivate(const GiftiLabelTable* labelTable, const float* labelIndices, const int64_t numberOfIndices, const DisplayGroupEnum::Enum displayGroup, const int32_t tabIndex, const ColorDataType colorDataType, void* rgbaOutPointer); static void colorScalarsWithRGBAPrivate(const float* redComponents, const float* greenComponents, const float* blueComponents, const float* alphaComponents, const int64_t numberOfComponents, const ColorDataType colorDataType, const uint8_t* rgbThreshold, uint8_t* rgbaOutPointer); NodeAndVoxelColoring(); virtual ~NodeAndVoxelColoring(); NodeAndVoxelColoring(const NodeAndVoxelColoring&); NodeAndVoxelColoring& operator=(const NodeAndVoxelColoring&); static const int32_t INVALID_TAB_INDEX; }; #ifdef __NODE_AND_VOXEL_COLORING_DECLARE__ // JWH 24 April 2015 const float NodeAndVoxelColoring::SMALL_POSITIVE = 0.00001; // JWH 24 April 2015 const float NodeAndVoxelColoring::SMALL_NEGATIVE = -0.00001; const int32_t NodeAndVoxelColoring::INVALID_TAB_INDEX = -1; #endif // __NODE_AND_VOXEL_COLORING_DECLARE__ } // namespace #endif //__NODE_AND_VOXEL_COLORING__H_ connectome-workbench-1.2.3+git41-gc4c6c90/src/Files/OxfordSparseThreeFile.cxx000066400000000000000000000161471300200146000265520ustar00rootroot00000000000000/*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 "OxfordSparseThreeFile.h" #include "ByteOrderEnum.h" #include "ByteSwapping.h" #include "CaretAssert.h" #include "FileInformation.h" #include using namespace caret; using namespace std; OxfordSparseThreeFile::OxfordSparseThreeFile(const AString& dimFileName, const AString& indexFileName, const AString& valueFileName) { m_valueFile = NULL; fstream dimFile(dimFileName.toLocal8Bit().constData(), fstream::in); if (!dimFile) throw DataFileException("error opening dimensions file"); dimFile >> m_dims[0]; if (!dimFile) throw DataFileException("error reading dimensions from file"); dimFile >> m_dims[1]; if (!dimFile) throw DataFileException("error reading dimensions from file"); if (m_dims[0] < 1 || m_dims[1] < 1) throw DataFileException("both dimensions must be positive"); m_indexArray.resize(m_dims[1] + 1); vector lengthArray(m_dims[1]); FileInformation indexFileInfo(indexFileName); if (!indexFileInfo.exists()) throw DataFileException("index file doesn't exist"); if (indexFileInfo.size() != 8 * m_dims[1]) throw DataFileException("index file is the wrong size"); FILE* indexFile = fopen(indexFileName.toLocal8Bit().constData(), "rb"); if (indexFile == NULL) throw DataFileException("error opening index file"); if (fread(lengthArray.data(), sizeof(int64_t), m_dims[1], indexFile) != (size_t)m_dims[1]) throw DataFileException("error reading index file"); if (ByteOrderEnum::isSystemBigEndian()) { ByteSwapping::swapBytes(lengthArray.data(), m_dims[1]); } m_indexArray[0] = 0; for (int64_t i = 0; i < m_dims[1]; ++i) { if (lengthArray[i] > m_dims[0] || lengthArray[i] < 0) throw DataFileException("impossible value found in length array"); m_indexArray[i + 1] = m_indexArray[i] + lengthArray[i]; } FileInformation valueFileInfo(valueFileName); if (!valueFileInfo.exists()) throw DataFileException("value file doesn't exist"); if (valueFileInfo.size() != (int64_t)(2 * sizeof(uint64_t) * m_indexArray[m_dims[1]])) throw DataFileException("value file is the wrong size"); m_valueFile = fopen(valueFileName.toLocal8Bit().constData(), "rb"); if (m_valueFile == NULL) throw DataFileException("error opening value file"); } OxfordSparseThreeFile::~OxfordSparseThreeFile() { if (m_valueFile != NULL) fclose(m_valueFile); } void OxfordSparseThreeFile::getRow(const int64_t& index, int64_t* rowOut) { CaretAssert(index >= 0 && index < m_dims[1]); int64_t start = m_indexArray[index], end = m_indexArray[index + 1]; int64_t numToRead = (end - start) * 2; m_scratchArray.resize(numToRead); if (fseek(m_valueFile, start * sizeof(int64_t) * 2, SEEK_SET) != 0) throw DataFileException("failed to seek in value file"); if (fread(m_scratchArray.data(), sizeof(int64_t), numToRead, m_valueFile) != (size_t)numToRead) throw DataFileException("error reading from value file"); if (ByteOrderEnum::isSystemBigEndian()) { ByteSwapping::swapBytes(m_scratchArray.data(), numToRead); } int64_t curIndex = 0; for (int64_t i = 0; i < numToRead; i += 2) { int64_t index = m_scratchArray[i]; if (index < curIndex || index >= m_dims[0]) throw DataFileException("impossible index value found in value file"); while (curIndex < index) { rowOut[curIndex] = 0; ++curIndex; } ++curIndex; rowOut[index] = m_scratchArray[i + 1]; } while (curIndex < m_dims[0]) { rowOut[curIndex] = 0; ++curIndex; } } void OxfordSparseThreeFile::getRowSparse(const int64_t& index, vector& indicesOut, vector& valuesOut) { CaretAssert(index >= 0 && index < m_dims[1]); int64_t start = m_indexArray[index], end = m_indexArray[index + 1]; int64_t numToRead = (end - start) * 2, numNonzero = end - start; m_scratchArray.resize(numToRead); if (fseek(m_valueFile, start * sizeof(int64_t) * 2, SEEK_SET) != 0) throw DataFileException("failed to seek in value file"); if (fread(m_scratchArray.data(), sizeof(int64_t), numToRead, m_valueFile) != (size_t)numToRead) throw DataFileException("error reading from value file"); if (ByteOrderEnum::isSystemBigEndian()) { ByteSwapping::swapBytes(m_scratchArray.data(), numToRead); } indicesOut.resize(numNonzero); valuesOut.resize(numNonzero); int64_t lastIndex = -1; for (int64_t i = 0; i < numNonzero; ++i) { indicesOut[i] = m_scratchArray[i * 2]; valuesOut[i] = m_scratchArray[i * 2 + 1]; if (indicesOut[i] <= lastIndex || indicesOut[i] >= m_dims[0]) { throw DataFileException("impossible index value found in file"); } lastIndex = indicesOut[i]; } } void OxfordSparseThreeFile::getFibersRow(const int64_t& index, FiberFractions* rowOut) { if (m_scratchRow.size() != (size_t)m_dims[0]) m_scratchRow.resize(m_dims[0]); getRow(index, (int64_t*)m_scratchRow.data()); for (int64_t i = 0; i < m_dims[0]; ++i) { if (m_scratchRow[i] == 0) { rowOut[i].zero(); } else { decodeFibers(m_scratchRow[i], rowOut[i]); } } } void OxfordSparseThreeFile::getFibersRowSparse(const int64_t& index, vector& indicesOut, vector& valuesOut) { getRowSparse(index, indicesOut, m_scratchSparseRow); size_t numNonzero = m_scratchSparseRow.size(); valuesOut.resize(numNonzero); for (size_t i = 0; i < numNonzero; ++i) { decodeFibers(((uint64_t*)m_scratchSparseRow.data())[i], valuesOut[i]); } } void OxfordSparseThreeFile::decodeFibers(const uint64_t& coded, FiberFractions& decoded) { decoded.fiberFractions.resize(3); decoded.totalCount = coded>>32; uint32_t temp = coded & ((1LL<<32) - 1); decoded.distance = (temp % 1001); temp = temp / 1001; decoded.fiberFractions[1] = (temp % 1001) / 1000.0f; temp = temp / 1001; decoded.fiberFractions[0] = temp / 1000.0f; decoded.fiberFractions[2] = 1.0f - decoded.fiberFractions[0] - decoded.fiberFractions[1]; if (decoded.fiberFractions[2] < -0.002f || temp > 1000) { throw DataFileException("error decoding value '" + AString::number(coded) + "' from oxford 3-file"); } if (decoded.fiberFractions[2] < 0.0f) decoded.fiberFractions[2] = 0.0f; } connectome-workbench-1.2.3+git41-gc4c6c90/src/Files/OxfordSparseThreeFile.h000066400000000000000000000042041300200146000261660ustar00rootroot00000000000000#ifndef __OXFORD_SPARSE_THREE_FILE_H__ #define __OXFORD_SPARSE_THREE_FILE_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 "CaretSparseFile.h" //for the Fibers struct namespace caret { class OxfordSparseThreeFile { static void decodeFibers(const uint64_t& coded, FiberFractions& decoded);//takes a uint because right shift on signed is implementation dependent FILE* m_valueFile;//we read the other two into memory, so we only need one handle int64_t m_dims[2]; std::vector m_indexArray, m_scratchRow; std::vector m_scratchArray, m_scratchSparseRow; OxfordSparseThreeFile(); OxfordSparseThreeFile(const OxfordSparseThreeFile& rhs); public: const int64_t* getDimensions() { return m_dims; } OxfordSparseThreeFile(const AString& dimFileName, const AString& indexFileName, const AString& valueFileName); void getRow(const int64_t& index, int64_t* rowOut); void getRowSparse(const int64_t& index, std::vector& indicesOut, std::vector& valuesOut); void getFibersRow(const int64_t& index, FiberFractions* rowOut); void getFibersRowSparse(const int64_t& index, std::vector& indicesOut, std::vector& valuesOut); ~OxfordSparseThreeFile(); }; } #endif //__OXFORD_SPARSE_THREE_FILE_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Files/PaletteFile.cxx000066400000000000000000001556301300200146000245420ustar00rootroot00000000000000/*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 "CaretLogger.h" #include "DataFileException.h" #include "GiftiLabel.h" #include "GiftiMetaData.h" #include "Palette.h" #include "PaletteColorMapping.h" #include "PaletteFile.h" #include "PaletteScalarAndColor.h" #include using namespace caret; /** * Constructor. * */ PaletteFile::PaletteFile() : CaretDataFile(DataFileTypeEnum::PALETTE) { this->metadata = new GiftiMetaData(); this->initializeMembersPaletteFile(); this->addDefaultPalettes(); this->clearModified(); } /** * Destructor */ PaletteFile::~PaletteFile() { this->clearAll(); delete this->metadata; } void PaletteFile::initializeMembersPaletteFile() { } /** * Get the label table used for color storage. * @return LabelTable used for color storage. * */ GiftiLabelTable* PaletteFile::getLabelTable() { return &this->labelTable; } /** * Clear everything. */ void PaletteFile::clearAll() { int64_t numberOfPalettes = this->palettes.size(); for (int64_t i = 0; i < numberOfPalettes; i++) { delete this->palettes[i]; } this->palettes.clear(); this->labelTable.clear(); this->metadata->clear(); } /** * Clear the file but add default palettes. */ void PaletteFile::clear() { this->clearAll(); this->addDefaultPalettes(); } /** * Add a palette color. * * @param pc - color to add. * */ void PaletteFile::addColor(const GiftiLabel& pc) { this->labelTable.addLabel(&pc); } /** * Add a palette color. * * @param name - name of color. * @param red - red component. * @param green - red component. * @param blue - red component. * */ void PaletteFile::addColor( const AString& name, const int32_t red, const int32_t green, const int32_t blue) { this->labelTable.addLabel(name, red, green, blue); } /** * Add a palette color. * * @param name - Name of color. * @param rgb - RGB components of color. * */ void PaletteFile::addColor( const AString& name, const int32_t rgb[]) { this->addColor(name, rgb[0], rgb[1], rgb[2]); } /** * Get a color via its index. * * @param index - index of color. * @return Reference to color at index or the default color * if the index is invalid. * */ const GiftiLabel* PaletteFile::getColor(const int32_t indx) const { return this->labelTable.getLabel(indx); } /** * Get a color via its index. * * @param colorName - Name of color. * @return Reference to color with name or the default color * if the name does not match any colors. * */ const GiftiLabel* PaletteFile::getColorByName(const AString& colorName) const { const GiftiLabel* gl = this->labelTable.getLabel(colorName); return gl; } /** * Get index for a color. * * @param colorName - Name of color. * @return Index to color or -1 if not found. * */ int32_t PaletteFile::getColorIndex(const AString& colorName) const { return this->labelTable.getLabelKeyFromName(colorName); } /** * Get the number of palettes. * * @return The number of palettes. * */ int32_t PaletteFile::getNumberOfPalettes() const { return this->palettes.size(); } /** * Add a palette. * * @param p - palette to add. * */ void PaletteFile::addPalette(const Palette& p) { Palette* pal = new Palette(p); this->assignColorsToPalette(*pal); this->palettes.push_back(pal); this->setModified(); } /** * Get a palette. * * @param index - index of palette. * @return Reference to palette or null if invalid index. * */ Palette* PaletteFile::getPalette(const int32_t indx) const { return this->palettes[indx]; } /** * Find a palette by the specified name. * * @param name Name of palette to search for. * @return Reference to palette with name or null if not found. * */ Palette* PaletteFile::getPaletteByName(const AString& name) const { int64_t numberOfPalettes = this->palettes.size(); for (int64_t i = 0; i < numberOfPalettes; i++) { if (this->palettes[i]->getName() == name) { return this->palettes[i]; } } return NULL; } /** * Remove a palette. * * @param index - index of palette to remove. * */ void PaletteFile::removePalette(const int32_t indx) { this->palettes.erase(this->palettes.begin() + indx); this->setModified(); } /** * Is this file empty? * * @return true if the file is empty, else false. * */ bool PaletteFile::isEmpty() const { return this->palettes.empty(); } /** * String description of this class. */ AString PaletteFile::toString() const { AString s; int64_t numberOfPalettes = this->palettes.size(); for (int64_t i = 0; i < numberOfPalettes; i++) { s += (this->palettes[i]->toString() + "\n"); } return s; } /** * Is this palette modified? * @return * true if modified, else false. */ bool PaletteFile::isModified() const { if (DataFile::isModified()) { return true; } if (this->labelTable.isModified()) { return true; } const int64_t numberOfPalettes = this->getNumberOfPalettes(); for (int i = 0; i < numberOfPalettes; i++) { if (this->palettes[i]->isModified()) { return true; } } return false; } /** * Set this object as not modified. Object should also * clear the modification status of its children. * */ void PaletteFile::clearModified() { DataFile::clearModified(); const int64_t numberOfPalettes = this->getNumberOfPalettes(); for (int i = 0; i < numberOfPalettes; i++) { this->palettes[i]->clearModified(); } this->labelTable.clearModified(); } /** * Assign colors to the palette. * @param * p Palette to which colors are assigned. */ void PaletteFile::assignColorsToPalette(Palette& p) { int64_t numberOfScalars = p.getNumberOfScalarsAndColors(); for (int64_t i = 0; i < numberOfScalars; i++) { PaletteScalarAndColor* psac = p.getScalarAndColor(i); const AString& colorName = psac->getColorName(); const GiftiLabel* gl = this->getColorByName(colorName); if (gl != NULL) { float rgba[4]; gl->getColor(rgba); psac->setColor(rgba); } else { CaretLogSevere(("Missing color \"" + colorName + "\" in palette \"" + p.getName() + "\"")); } } } /** * Read the data file. * * @param filename * Name of the data file. * @throws DataFileException * If the file was not successfully read. */ void PaletteFile::readFile(const AString& filename) { clear(); // checkFileReadability(filename); throw DataFileException(filename, "Reading of PaletteFile not implemented."); } /** * Write the data file. * * @param filename * Name of the data file. * @throws DataFileException * If the file was not successfully written. */ void PaletteFile::writeFile(const AString& filename) { // checkFileWritability(filename); throw DataFileException(filename, "Reading of PaletteFile not implemented."); } /** * Add the default palettes. * */ void PaletteFile::addDefaultPalettes() { bool modifiedStatus = this->isModified(); this->addColor("none", 0xff, 0xff, 0xff ); this->addColor("_yellow", 0xff, 0xff, 0x00 ); 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) { 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 this->addColor("_RGB_255_0_0", 255, 0, 0 ); //#ff0000 this->addColor("_RGB_200_0_0", 200, 0, 0 ); //#c80000 this->addColor("_RGB_150_0_0", 150, 0, 0 ); //#960000 this->addColor("_RGB_100_0_0", 100, 0, 0 ); //#640000 this->addColor("_RGB_60_0_0", 60, 0, 0 ); //#3c0000 this->addColor("_RGB_0_0_80", 0, 0, 80 ); //#000050 this->addColor("_RGB_0_0_170", 0, 0, 170 ); //#0000aa this->addColor("_RGB_75_0_125", 75, 0, 125 ); //#4b007d this->addColor("_RGB_125_0_160", 125, 0, 160 ); //#7d00a0 this->addColor("_RGB_75_125_0", 75, 125, 0 ); //#4b7d00 this->addColor("_RGB_0_200_0", 0, 200, 0 ); //#00c800 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); royBigBL.addScalarAndColor(1.00f, "_RGB_255_255_0"); royBigBL.addScalarAndColor(0.875f, "_RGB_255_200_0"); royBigBL.addScalarAndColor(0.750f, "_RGB_255_120_0"); royBigBL.addScalarAndColor(0.625f, "_RGB_255_0_0"); royBigBL.addScalarAndColor(0.500f, "_RGB_200_0_0"); royBigBL.addScalarAndColor(0.375f, "_RGB_150_0_0"); royBigBL.addScalarAndColor(0.250f, "_RGB_100_0_0"); royBigBL.addScalarAndColor(0.125f, "_RGB_60_0_0"); royBigBL.addScalarAndColor(0.000f, "_black"); royBigBL.addScalarAndColor(-0.125f, "_RGB_0_0_80"); royBigBL.addScalarAndColor(-0.250f, "_RGB_0_0_170"); royBigBL.addScalarAndColor(-0.375f, "_RGB_75_0_125"); royBigBL.addScalarAndColor(-0.500f, "_RGB_125_0_160"); royBigBL.addScalarAndColor(-0.625f, "_RGB_75_125_0"); royBigBL.addScalarAndColor(-0.750f, "_RGB_0_200_0"); royBigBL.addScalarAndColor(-0.875f, "_RGB_0_255_0"); royBigBL.addScalarAndColor(-0.990f, "_RGB_0_255_255"); royBigBL.addScalarAndColor(-1.00f, "_RGB_0_255_255"); 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 // int oran_yell[3] = { 0xff, 0x99, 0x00 }; this->addColor("_oran-yell", oran_yell); int red[3] = { 0xff, 0x00, 0x00 }; this->addColor("_red", red); int cyan[3] = { 0x00, 0xff, 0xff }; this->addColor("_cyan", cyan); int green[3] = { 0x00, 0xff, 0x00 }; this->addColor("_green", green); int limegreen[3] = { 0x10, 0xb0, 0x10 }; this->addColor("_limegreen", limegreen); int violet[3] = { 0xe2, 0x51, 0xe2 }; this->addColor("_violet", violet); int hotpink[3] = { 0xff, 0x38, 0x8d }; this->addColor("_hotpink", hotpink); int white[3] = { 0xff, 0xff, 0xff }; this->addColor("_white", white); int gry_dd[3] = { 0xdd, 0xdd, 0xdd }; this->addColor("_gry-dd", gry_dd ); int gry_bb[3] = { 0xbb, 0xbb, 0xbb }; this->addColor("_gry-bb", gry_bb); int purple2[3] = { 0x66, 0x00, 0x33 }; this->addColor("_purple2", purple2); int blue_videen11[3] = { 0x33, 0x33, 0x4c }; this->addColor("_blue_videen11", blue_videen11); int blue_videen9[3] = { 0x4c, 0x4c, 0x7f }; this->addColor("_blue_videen9", blue_videen9); 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"); videenStyle.addScalarAndColor(1.0f, "_red"); videenStyle.addScalarAndColor(0.9f, "_orange"); videenStyle.addScalarAndColor(0.8f, "_oran-yell"); videenStyle.addScalarAndColor(0.7f, "_yellow"); videenStyle.addScalarAndColor(0.6f, "_limegreen"); videenStyle.addScalarAndColor(0.5f, "_green"); videenStyle.addScalarAndColor(0.4f, "_blue_videen7"); videenStyle.addScalarAndColor(0.3f, "_blue_videen9"); videenStyle.addScalarAndColor(0.2f, "_blue_videen11"); videenStyle.addScalarAndColor(0.1f, "_purple2"); videenStyle.addScalarAndColor(0.0f, "_black"); videenStyle.addScalarAndColor(-0.1f, "_cyan"); videenStyle.addScalarAndColor(-0.2f, "_green"); videenStyle.addScalarAndColor(-0.3f, "_limegreen"); videenStyle.addScalarAndColor(-0.4f, "_violet"); videenStyle.addScalarAndColor(-0.5f, "_hotpink"); videenStyle.addScalarAndColor(-0.6f, "_white"); videenStyle.addScalarAndColor(-0.7f, "_gry-dd"); videenStyle.addScalarAndColor(-0.8f, "_gry-bb"); videenStyle.addScalarAndColor(-0.9f, "_black"); 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); 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); } //------------------------------------------------------------------------ // // Palette by Jon Wieser @ mcw // int rbgyr20_01[3] = { 0xCC, 0x10, 0x33 }; this->addColor("_rbgyr20_01", rbgyr20_01); int rbgyr20_02[3] = { 0x99, 0x20, 0x66 }; this->addColor("_rbgyr20_02", rbgyr20_02); int rbgyr20_03[3] = { 0x66, 0x31, 0x99 }; this->addColor("_rbgyr20_03", rbgyr20_03); int rbgyr20_04[3] = { 0x34, 0x41, 0xCC }; this->addColor("_rbgyr20_04", rbgyr20_04); int rbgyr20_05[3] = { 0x00, 0x51, 0xFF }; this->addColor("_rbgyr20_05", rbgyr20_05); int rbgyr20_06[3] = { 0x00, 0x74, 0xCC }; this->addColor("_rbgyr20_06", rbgyr20_06); int rbgyr20_07[3] = { 0x00, 0x97, 0x99 }; this->addColor("_rbgyr20_07", rbgyr20_07); int rbgyr20_08[3] = { 0x00, 0xB9, 0x66 }; this->addColor("_rbgyr20_08", rbgyr20_08); int rbgyr20_09[3] = { 0x00, 0xDC, 0x33 }; this->addColor("_rbgyr20_09", rbgyr20_09); int rbgyr20_10[3] = { 0x00, 0xFF, 0x00 }; this->addColor("_rbgyr20_10", rbgyr20_10); int rbgyr20_11[3] = { 0x33, 0xFF, 0x00 }; this->addColor("_rbgyr20_11", rbgyr20_11); int rbgyr20_12[3] = { 0x66, 0xFF, 0x00 }; this->addColor("_rbgyr20_12", rbgyr20_12); int rbgyr20_13[3] = { 0x99, 0xFF, 0x00 }; this->addColor("_rbgyr20_13", rbgyr20_13); int rbgyr20_14[3] = { 0xCC, 0xFF, 0x00 }; this->addColor("_rbgyr20_14", rbgyr20_14); int rbgyr20_15[3] = { 0xFF, 0xFF, 0x00 }; this->addColor("_rbgyr20_15", rbgyr20_15); int rbgyr20_16[3] = { 0xFF, 0xCC, 0x00 }; this->addColor("_rbgyr20_16", rbgyr20_16); int rbgyr20_17[3] = { 0xFF, 0x99, 0x00 }; this->addColor("_rbgyr20_17", rbgyr20_17); int rbgyr20_18[3] = { 0xFF, 0x66, 0x00 }; this->addColor("_rbgyr20_18", rbgyr20_18); int rbgyr20_19[3] = { 0xFF, 0x33, 0x00 }; this->addColor("_rbgyr20_19", rbgyr20_19); int rbgyr20_20[3] = { 0xFF, 0x00, 0x00 }; this->addColor("_rbgyr20_20", rbgyr20_20); if (this->getPaletteByName("RBGYR20") == NULL) { Palette pal2; pal2.setName("RBGYR20"); pal2.addScalarAndColor( 1.0f, "_rbgyr20_01"); pal2.addScalarAndColor( 0.9f, "_rbgyr20_02"); pal2.addScalarAndColor( 0.8f, "_rbgyr20_03"); pal2.addScalarAndColor( 0.7f, "_rbgyr20_04"); pal2.addScalarAndColor( 0.6f, "_rbgyr20_05"); pal2.addScalarAndColor( 0.5f, "_rbgyr20_06"); pal2.addScalarAndColor( 0.4f, "_rbgyr20_07"); pal2.addScalarAndColor( 0.3f, "_rbgyr20_08"); pal2.addScalarAndColor( 0.2f, "_rbgyr20_09"); pal2.addScalarAndColor( 0.1f, "_rbgyr20_10"); pal2.addScalarAndColor( 0.0f, "_rbgyr20_11"); pal2.addScalarAndColor(-0.1f, "_rbgyr20_12"); pal2.addScalarAndColor(-0.2f, "_rbgyr20_13"); pal2.addScalarAndColor(-0.3f, "_rbgyr20_14"); pal2.addScalarAndColor(-0.4f, "_rbgyr20_15"); pal2.addScalarAndColor(-0.5f, "_rbgyr20_16"); pal2.addScalarAndColor(-0.6f, "_rbgyr20_17"); pal2.addScalarAndColor(-0.7f, "_rbgyr20_18"); pal2.addScalarAndColor(-0.8f, "_rbgyr20_19"); pal2.addScalarAndColor(-0.9f, "_rbgyr20_20"); addPalette(pal2); Palette pal3; pal3.setName("RBGYR20P"); pal3.addScalarAndColor(1.00f, "_rbgyr20_01"); pal3.addScalarAndColor(0.95f, "_rbgyr20_02"); pal3.addScalarAndColor(0.90f, "_rbgyr20_03"); pal3.addScalarAndColor(0.85f, "_rbgyr20_04"); pal3.addScalarAndColor(0.80f, "_rbgyr20_05"); pal3.addScalarAndColor(0.75f, "_rbgyr20_06"); pal3.addScalarAndColor(0.70f, "_rbgyr20_07"); pal3.addScalarAndColor(0.65f, "_rbgyr20_08"); pal3.addScalarAndColor(0.60f, "_rbgyr20_09"); pal3.addScalarAndColor(0.55f, "_rbgyr20_10"); pal3.addScalarAndColor(0.50f, "_rbgyr20_11"); pal3.addScalarAndColor(0.45f, "_rbgyr20_12"); pal3.addScalarAndColor(0.40f, "_rbgyr20_13"); pal3.addScalarAndColor(0.35f, "_rbgyr20_14"); pal3.addScalarAndColor(0.30f, "_rbgyr20_15"); pal3.addScalarAndColor(0.25f, "_rbgyr20_16"); pal3.addScalarAndColor(0.20f, "_rbgyr20_17"); pal3.addScalarAndColor(0.15f, "_rbgyr20_18"); pal3.addScalarAndColor(0.10f, "_rbgyr20_19"); pal3.addScalarAndColor(0.05f, "_rbgyr20_20"); pal3.addScalarAndColor(0.0f, "none"); addPalette(pal3); } //---------------------------------------------------------------------- // 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 (this->getPaletteByName("red-yellow") == NULL) { this->addColor("_red_yellow_interp_red", 255, 0, 0 ); this->addColor("_red_yellow_interp_yellow", 255, 255, 0 ); this->addColor("_blue_lightblue_interp_blue", 0, 0, 255 ); this->addColor("_blue_lightblue_interp_lightblue", 0, 255, 255 ); this->addColor("_fslview_zero", 0, 0, 0); Palette palRedYellowInterp; palRedYellowInterp.setName("red-yellow"); palRedYellowInterp.addScalarAndColor(1.0f, "_red_yellow_interp_yellow"); palRedYellowInterp.addScalarAndColor(0.0f, "_red_yellow_interp_red"); addPalette(palRedYellowInterp); Palette palBlueLightblueInterp; palBlueLightblueInterp.setName("blue-lightblue"); palBlueLightblueInterp.addScalarAndColor(1.0f, "_blue_lightblue_interp_lightblue"); palBlueLightblueInterp.addScalarAndColor(0.0f, "_blue_lightblue_interp_blue"); addPalette(palBlueLightblueInterp); Palette palFSLView; palFSLView.setName("FSL"); palFSLView.addScalarAndColor( 1.0f, "_red_yellow_interp_yellow"); palFSLView.addScalarAndColor( 0.00001f, "_red_yellow_interp_red"); palFSLView.addScalarAndColor( 0.0000099f, "_fslview_zero"); palFSLView.addScalarAndColor(-0.0000099f, "_fslview_zero"); palFSLView.addScalarAndColor(-0.00001f, "_blue_lightblue_interp_blue"); palFSLView.addScalarAndColor(-1.0f, "_blue_lightblue_interp_lightblue"); addPalette(palFSLView); } if (this->getPaletteByName("power_surf") == NULL) { this->addColor("_ps_0", 1.0 *255.0, 0.0 * 255.0, 0.0 * 255.0 ); this->addColor("_ps_059", 0.0 * 255.0, 0.0 * 255.0, 0.6 * 255.0 ); this->addColor("_ps_118", 1.0 * 255.0, 1.0 * 255.0, 0.0 * 255.0 ); this->addColor("_ps_176", 1.0 * 255.0, 0.7 * 255.0, 0.4 * 255.0); this->addColor("_ps_235", 0.0 * 255.0, 0.8 * 255.0, 0.0 * 255.0 ); this->addColor("_ps_294", 1.0 * 255.0, 0.6 * 255.0, 1.0 * 255.0 ); this->addColor("_ps_353", 0.0 * 255.0, 0.6 * 255.0, 0.6 * 255.0 ); this->addColor("_ps_412", 0.0 * 255.0, 0.0 * 255.0, 0.0 * 255.0 ); this->addColor("_ps_471", 0.3 * 255.0, 0.0 * 255.0, 0.6 * 255.0 ); this->addColor("_ps_529", 0.2 * 255.0, 1.0 * 255.0, 1.0 * 255.0 ); this->addColor("_ps_588", 1.0 * 255.0, 0.5 * 255.0, 0.0 * 255.0 ); this->addColor("_ps_647", 0.6 * 255.0, 0.2 * 255.0, 1.0 * 255.0 ); this->addColor("_ps_706", 0.0 * 255.0, 0.2 * 255.0, 0.4 * 255.0 ); this->addColor("_ps_765", 0.2 * 255.0, 1.0 * 255.0, 0.2 * 255.0 ); this->addColor("_ps_824", 0.0 * 255.0, 0.0 * 255.0, 1.0 * 255.0 ); this->addColor("_ps_882", 1.0 * 255.0, 1.0 * 255.0, 0.8 * 255.0 ); this->addColor("_ps_941", 0.0 * 255.0, 0.4 * 255.0, 0.0 * 255.0 ); this->addColor("_ps_1000", 0.25 * 255.0, 0.25 * 255.0, 0.25 * 255.0 ); Palette powerSurf; powerSurf.setName("power_surf"); powerSurf.addScalarAndColor( 1.0, "_ps_1000"); powerSurf.addScalarAndColor( 0.941, "_ps_941"); powerSurf.addScalarAndColor( 0.882, "_ps_882"); powerSurf.addScalarAndColor( 0.824, "_ps_824"); powerSurf.addScalarAndColor( 0.765, "_ps_765"); powerSurf.addScalarAndColor( 0.706, "_ps_706"); powerSurf.addScalarAndColor( 0.647, "_ps_647"); powerSurf.addScalarAndColor( 0.588, "_ps_588"); powerSurf.addScalarAndColor( 0.529, "_ps_529"); powerSurf.addScalarAndColor( 0.471, "_ps_471"); powerSurf.addScalarAndColor( 0.412, "_ps_412"); powerSurf.addScalarAndColor( 0.353, "_ps_353"); powerSurf.addScalarAndColor( 0.294, "_ps_294"); powerSurf.addScalarAndColor( 0.235, "_ps_235"); powerSurf.addScalarAndColor( 0.176, "_ps_176"); powerSurf.addScalarAndColor( 0.118, "_ps_118"); powerSurf.addScalarAndColor( 0.059, "_ps_059"); powerSurf.addScalarAndColor( 0.0, "_ps_0"); addPalette(powerSurf); } /* * FSL Red palette from WB-289 * * float offset = 100.0; * float step = (255.0 - offset) / 255.0; * for(unsigned char i = 0; i < 255; ++i) * { int red = int(((i + 1) * step) + offset); lut->pushValue(red, 0, 0, i); } * * lut->m_lutName = std::string("Red"); */ //TSC: no "lookup tables" for purely interpolated palettes! bad for performance. if (this->getPaletteByName("fsl_red") == NULL) { Palette fslRed; fslRed.setName("fsl_red"); this->addColor("fsl_red_0", 100, 0, 0); this->addColor("fsl_red_1", 255, 0, 0); fslRed.addScalarAndColor(1.0f, "fsl_red_1"); fslRed.addScalarAndColor(0.0f, "fsl_red_0"); addPalette(fslRed); } if (this->getPaletteByName("fsl_green") == NULL) { Palette fslGreen; fslGreen.setName("fsl_green"); this->addColor("fsl_green_0", 0, 100, 0); this->addColor("fsl_green_1", 0, 255, 0); fslGreen.addScalarAndColor(1.0f, "fsl_green_1"); fslGreen.addScalarAndColor(0.0f, "fsl_green_0"); addPalette(fslGreen); } if (this->getPaletteByName("fsl_blue") == NULL) { Palette fslBlue; fslBlue.setName("fsl_blue"); this->addColor("fsl_blue_0", 0, 0, 100); this->addColor("fsl_blue_1", 0, 0, 255); fslBlue.addScalarAndColor(1.0f, "fsl_blue_1"); fslBlue.addScalarAndColor(0.0f, "fsl_blue_0"); addPalette(fslBlue); } if (this->getPaletteByName("fsl_yellow") == NULL) { Palette fslYellow; fslYellow.setName("fsl_yellow"); this->addColor("fsl_yellow_0", 100, 100, 0); this->addColor("fsl_yellow_1", 255, 255, 0); fslYellow.addScalarAndColor(1.0f, "fsl_yellow_1"); fslYellow.addScalarAndColor(0.0f, "fsl_yellow_0"); addPalette(fslYellow); } if (this->getPaletteByName("JET256") == NULL) { Palette JET256; JET256.setName("JET256"); //summary of original slow "lookup table" (if closer to previous implementation is desired): //start: 0 -> (0 0 132) //change: 0.121 -> (0 0 255) //change: 0.372 -> (0 255 255) //change: 0.623 -> (255 255 0) //change: 0.874 -> (255 0 0) //end: 1 -> (127 0 0) //alternative round-valued version via https://gist.github.com/bagrow/805122 /*(0 0.0 0.0 0.5, \ 1 0.0 0.0 1.0, \ 2 0.0 0.5 1.0, \ <- redundant 3 0.0 1.0 1.0, \ 4 0.5 1.0 0.5, \ <- redundant 5 1.0 1.0 0.0, \ 6 1.0 0.5 0.0, \ <- redundant 7 1.0 0.0 0.0, \ 8 0.5 0.0 0.0 )*/ this->addColor("_J0", 0, 0, 127);//rounding to probably-intended colors this->addColor("_J1", 0, 0, 255); this->addColor("_J3", 0, 255, 255);//skipping redundant points this->addColor("_J5", 255, 255, 0); this->addColor("_J7", 255, 0, 0); this->addColor("_J8", 127, 0, 0); JET256.addScalarAndColor(1.0f, "_J8"); JET256.addScalarAndColor(0.875f, "_J7");//also rounding to probably-intended control points JET256.addScalarAndColor(0.625f, "_J5"); JET256.addScalarAndColor(0.375f, "_J3"); JET256.addScalarAndColor(0.125f, "_J1"); JET256.addScalarAndColor(0.0f, "_J0"); addPalette(JET256); } if (modifiedStatus == false) { this->clearModified(); } } /** * @return The structure for this file. */ StructureEnum::Enum PaletteFile::getStructure() const { // palette files do not have structure return StructureEnum::INVALID; } /** * Set the structure for this file. * @param structure * New structure for this file. */ void PaletteFile::setStructure(const StructureEnum::Enum /*structure*/) { // palette files do not have structure } /** * @return Get access to the file's metadata. */ GiftiMetaData* PaletteFile::getFileMetaData() { return this->metadata; } /** * @return Get access to unmodifiable file's metadata. */ const GiftiMetaData* PaletteFile::getFileMetaData() const { return this->metadata; } /** * Set the palette mapping based upon the given file type, * file name, data name, and data. * * @param paletteColorMapping * Palette color mapping that is setup. * @param dataFileType * Type of data file. * @param fileName * Name of file. * @param dataName * Name of data. * @param data * The data. * @param numberOfDataElements * Number of elements in data. */ void PaletteFile::setDefaultPaletteColorMapping(PaletteColorMapping* paletteColorMapping, const DataFileTypeEnum::Enum& dataFileType, const AString& fileNameIn, const AString& dataNameIn, const float* data, const int32_t numberOfDataElements) { bool isShapeCurvatureData = false; bool isShapeDepthData = false; bool isShapeData = false; bool isVolumeAnatomyData = false; const AString fileName = fileNameIn.toLower(); const AString dataName = dataNameIn.toLower(); bool invalid = false; bool checkShapeFile = false; bool checkVolume = false; switch (dataFileType) { case DataFileTypeEnum::ANNOTATION: invalid = true; break; case DataFileTypeEnum::BORDER: invalid = true; break; case DataFileTypeEnum::CONNECTIVITY_DENSE: break; case DataFileTypeEnum::CONNECTIVITY_DENSE_DYNAMIC: break; case DataFileTypeEnum::CONNECTIVITY_DENSE_LABEL: invalid = true; break; case DataFileTypeEnum::CONNECTIVITY_DENSE_PARCEL: 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_DENSE_SCALAR: checkShapeFile = true; break; case DataFileTypeEnum::CONNECTIVITY_DENSE_TIME_SERIES: break; case DataFileTypeEnum::CONNECTIVITY_FIBER_ORIENTATIONS_TEMPORARY: invalid = true; break; case DataFileTypeEnum::CONNECTIVITY_FIBER_TRAJECTORY_TEMPORARY: invalid = true; break; case DataFileTypeEnum::CONNECTIVITY_SCALAR_DATA_SERIES: break; case DataFileTypeEnum::FOCI: invalid = true; break; case DataFileTypeEnum::IMAGE: invalid = true; break; case DataFileTypeEnum::LABEL: invalid = true; break; case DataFileTypeEnum::METRIC: checkShapeFile = true; break; case DataFileTypeEnum::PALETTE: invalid = true; break; case DataFileTypeEnum::RGBA: invalid = true; break; case DataFileTypeEnum::SCENE: invalid = true; break; case DataFileTypeEnum::SPECIFICATION: invalid = true; break; case DataFileTypeEnum::SURFACE: invalid = true; break; case DataFileTypeEnum::UNKNOWN: invalid = true; break; case DataFileTypeEnum::VOLUME: checkVolume = true; break; } if (invalid) { return; } if (checkShapeFile) { if (dataName.contains("curv")) { isShapeData = true; isShapeCurvatureData = true; } else if (dataName.contains("depth")) { isShapeData = true; isShapeDepthData = true; } else if (dataName.contains("shape")) { isShapeData = true; } else if (fileName.contains("curv")) { isShapeData = true; isShapeCurvatureData = true; } else if (fileName.contains("depth")) { isShapeData = true; isShapeDepthData = true; } else if (fileName.contains("shape")) { isShapeData = true; } } float minValue = std::numeric_limits::max(); float maxValue = -minValue; for (int32_t i = 0; i < numberOfDataElements; i++) { const float d = data[i]; if (d > maxValue) { maxValue = d; } if (d < minValue) { minValue = d; } } //bool havePositiveData = (maxValue > 0);//unused, commenting out to prevent compiler warning bool haveNegativeData = (minValue < 0); if (checkVolume) { if ((minValue >= 0) && (maxValue <= 255.0)) { isVolumeAnatomyData = true; } } if (isVolumeAnatomyData) { paletteColorMapping->setThresholdType(PaletteThresholdTypeEnum::THRESHOLD_TYPE_OFF); paletteColorMapping->setSelectedPaletteName("Gray_Interp_Positive"); paletteColorMapping->setInterpolatePaletteFlag(true); paletteColorMapping->setScaleMode(PaletteScaleModeEnum::MODE_AUTO_SCALE_PERCENTAGE); paletteColorMapping->setAutoScalePercentageNegativeMaximum(98.0); paletteColorMapping->setAutoScalePercentageNegativeMinimum(2.0); paletteColorMapping->setAutoScalePercentagePositiveMinimum(2.0); paletteColorMapping->setAutoScalePercentagePositiveMaximum(98.0); } else if (isShapeData) { paletteColorMapping->setThresholdType(PaletteThresholdTypeEnum::THRESHOLD_TYPE_OFF); paletteColorMapping->setSelectedPaletteName("Gray_Interp"); paletteColorMapping->setInterpolatePaletteFlag(true); if (isShapeDepthData) { paletteColorMapping->setScaleMode(PaletteScaleModeEnum::MODE_USER_SCALE); paletteColorMapping->setUserScaleNegativeMaximum(-30.0); paletteColorMapping->setUserScaleNegativeMinimum(0.0); paletteColorMapping->setUserScalePositiveMinimum(0.0); paletteColorMapping->setUserScalePositiveMaximum(10.0); } else if (isShapeCurvatureData) { // paletteColorMapping->setScaleMode(PaletteScaleModeEnum::MODE_USER_SCALE); // paletteColorMapping->setUserScaleNegativeMaximum(-1.5); // paletteColorMapping->setUserScaleNegativeMinimum(0.0); // paletteColorMapping->setUserScalePositiveMinimum(0.0); // paletteColorMapping->setUserScalePositiveMaximum(1.5); paletteColorMapping->setScaleMode(PaletteScaleModeEnum::MODE_AUTO_SCALE_PERCENTAGE); paletteColorMapping->setAutoScalePercentageNegativeMaximum(98.0); paletteColorMapping->setAutoScalePercentageNegativeMinimum(2.0); paletteColorMapping->setAutoScalePercentagePositiveMinimum(2.0); paletteColorMapping->setAutoScalePercentagePositiveMaximum(98.0); } else { paletteColorMapping->setScaleMode(PaletteScaleModeEnum::MODE_AUTO_SCALE_PERCENTAGE); paletteColorMapping->setAutoScalePercentageNegativeMaximum(98.0); paletteColorMapping->setAutoScalePercentageNegativeMinimum(2.0); paletteColorMapping->setAutoScalePercentagePositiveMinimum(2.0); paletteColorMapping->setAutoScalePercentagePositiveMaximum(98.0); } paletteColorMapping->setDisplayNegativeDataFlag(true); paletteColorMapping->setDisplayPositiveDataFlag(true); paletteColorMapping->setDisplayZeroDataFlag(true); } else { if (haveNegativeData) { paletteColorMapping->setThresholdType(PaletteThresholdTypeEnum::THRESHOLD_TYPE_OFF); paletteColorMapping->setSelectedPaletteName("videen-style"); paletteColorMapping->setSelectedPaletteName("ROY-BIG-BL"); paletteColorMapping->setInterpolatePaletteFlag(true); paletteColorMapping->setScaleMode(PaletteScaleModeEnum::MODE_AUTO_SCALE_PERCENTAGE); paletteColorMapping->setAutoScalePercentageNegativeMaximum(98.0); paletteColorMapping->setAutoScalePercentageNegativeMinimum(2.0); paletteColorMapping->setAutoScalePercentagePositiveMinimum(2.0); paletteColorMapping->setAutoScalePercentagePositiveMaximum(98.0); paletteColorMapping->setDisplayNegativeDataFlag(true); paletteColorMapping->setDisplayPositiveDataFlag(true); paletteColorMapping->setDisplayZeroDataFlag(false); } else { paletteColorMapping->setThresholdType(PaletteThresholdTypeEnum::THRESHOLD_TYPE_OFF); paletteColorMapping->setSelectedPaletteName("videen-style"); paletteColorMapping->setSelectedPaletteName("ROY-BIG-BL"); paletteColorMapping->setInterpolatePaletteFlag(true); paletteColorMapping->setScaleMode(PaletteScaleModeEnum::MODE_AUTO_SCALE_PERCENTAGE); paletteColorMapping->setAutoScalePercentageNegativeMaximum(98.0); paletteColorMapping->setAutoScalePercentageNegativeMinimum(2.0); paletteColorMapping->setAutoScalePercentagePositiveMinimum(2.0); paletteColorMapping->setAutoScalePercentagePositiveMaximum(98.0); paletteColorMapping->setDisplayNegativeDataFlag(true); paletteColorMapping->setDisplayPositiveDataFlag(true); paletteColorMapping->setDisplayZeroDataFlag(false); } } paletteColorMapping->clearModified(); } connectome-workbench-1.2.3+git41-gc4c6c90/src/Files/PaletteFile.h000066400000000000000000000100161300200146000241530ustar00rootroot00000000000000#ifndef __PALETTEFILE_H__ #define __PALETTEFILE_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 "CaretDataFile.h" #include #include #include #include "GiftiLabelTable.h" namespace caret { class GiftiLabel; class GiftiLabelTable; class GiftiMetaData; class Palette; class PaletteColorMapping; /** * File for storing color palettes. */ class PaletteFile : public CaretDataFile { public: PaletteFile(); virtual ~PaletteFile(); public: PaletteFile(const PaletteFile& o); PaletteFile& operator=(const PaletteFile& o); private: void initializeMembersPaletteFile(); public: GiftiLabelTable* getLabelTable(); void clear(); void addColor(const GiftiLabel& pc); void addColor( const AString& name, const int32_t red, const int32_t green, const int32_t blue); void addColor( const AString& name, const int32_t rgb[]); const GiftiLabel* getColor(const int32_t index) const; const GiftiLabel* getColorByName(const AString& colorName) const; int32_t getColorIndex(const AString& colorName) const; int32_t getNumberOfPalettes() const; void addPalette(const Palette& p); Palette* getPalette(const int32_t index) const; Palette* getPaletteByName(const AString& name) const; void removePalette(const int32_t index); virtual bool isEmpty() const; AString toString() const; bool isModified() const; void clearModified(); virtual void readFile(const AString& filename); virtual void writeFile(const AString& filename); virtual StructureEnum::Enum getStructure() const; virtual void setStructure(const StructureEnum::Enum structure); DataFileTypeEnum::Enum getDataFileType() const; virtual GiftiMetaData* getFileMetaData(); virtual const GiftiMetaData* getFileMetaData() const; static void setDefaultPaletteColorMapping(PaletteColorMapping* paletteColorMapping, const DataFileTypeEnum::Enum& dataFileType, const AString& fileName, const AString& dataName, const float* data, const int32_t numberOfDataElements); private: void assignColorsToPalette(Palette& p); void addDefaultPalettes(); void clearAll(); private: /**the colors for the palettes */ GiftiLabelTable labelTable; /**the palettes */ std::vector palettes; GiftiMetaData* metadata; }; } // namespace #endif // __PALETTEFILE_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Files/RgbaFile.cxx000066400000000000000000000056051300200146000240130ustar00rootroot00000000000000/*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 "CaretAssert.h" #include "DataFileTypeEnum.h" #include "GiftiFile.h" #include "MathFunctions.h" #include "RgbaFile.h" using namespace caret; /** * Constructor. */ RgbaFile::RgbaFile() : GiftiTypeFile(DataFileTypeEnum::RGBA) { this->initializeMembersRgbaFile(); } /** * Copy constructor. * * @param sf * Surface file that is copied. */ RgbaFile::RgbaFile(const RgbaFile& sf) : GiftiTypeFile(sf) { this->copyHelperRgbaFile(sf); } /** * Assignment operator. * * @param sf * Surface file that is copied. * @return * This surface file with content replaced * by the RgbaFile parameter. */ RgbaFile& RgbaFile::operator=(const RgbaFile& sf) { if (this != &sf) { GiftiTypeFile::operator=(sf); this->copyHelperRgbaFile(sf); } return *this; } /** * Destructor. */ RgbaFile::~RgbaFile() { } /** * Clear the surface file. */ void RgbaFile::clear() { GiftiTypeFile::clear(); } /** * Validate the contents of the file after it * has been read such as correct number of * data arrays and proper data types/dimensions. */ void RgbaFile::validateDataArraysAfterReading() { this->initializeMembersRgbaFile(); this->verifyDataArraysHaveSameNumberOfRows(3, 4); } /** * Get the number of nodes. * * @return * The number of nodes. */ 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; } /** * Get the number of columns. * * @return * The number of columns. */ int32_t RgbaFile::getNumberOfColumns() const { const int32_t numCols = this->giftiFile->getNumberOfDataArrays(); return numCols; } /** * Initialize members of this class. */ void RgbaFile::initializeMembersRgbaFile() { } /** * Helps copying files. * * @param sf * File that is copied. */ void RgbaFile::copyHelperRgbaFile(const RgbaFile& /*sf*/) { this->validateDataArraysAfterReading(); } connectome-workbench-1.2.3+git41-gc4c6c90/src/Files/RgbaFile.h000066400000000000000000000035441300200146000234400ustar00rootroot00000000000000 #ifndef __RGBA_FILE_H__ #define __RGBA_FILE_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 "GiftiTypeFile.h" namespace caret { class GiftiDataArray; /** * \brief An RGBA data file. */ class RgbaFile : public GiftiTypeFile { public: RgbaFile(); RgbaFile(const RgbaFile& sf); RgbaFile& operator=(const RgbaFile& sf); virtual ~RgbaFile(); virtual void clear(); virtual int32_t getNumberOfNodes() const; virtual int32_t getNumberOfColumns() const; protected: /** * Validate the contents of the file after it * has been read such as correct number of * data arrays and proper data types/dimensions. */ virtual void validateDataArraysAfterReading(); void copyHelperRgbaFile(const RgbaFile& sf); void initializeMembersRgbaFile(); private: }; } // namespace #endif // __RGBA_FILE_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Files/RibbonMappingHelper.cxx000066400000000000000000000352271300200146000262320ustar00rootroot00000000000000/*LICENSE_START*/ /* * Copyright (C) 2015 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*/ /* * For the function TriInfi::vertRayHit(): * Original copyright for PNPOLY, though my version is entirely rewritten and modified * Source: http://www.ecse.rpi.edu/Homepages/wrf/Research/Short_Notes/pnpoly.html */ /* Copyright (c) 1970-2003, Wm. Randolph Franklin Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimers. 2. Redistributions in binary form must reproduce the above copyright notice in the documentation and/or other materials provided with the distribution. 3. The name of W. Randolph Franklin may not be used to endorse or promote products derived from this Software without specific prior written permission. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #include "RibbonMappingHelper.h" #include "CaretException.h" #include "FloatMatrix.h" #include "MathFunctions.h" #include "SurfaceFile.h" #include "TopologyHelper.h" #include "VolumeSpace.h" #include using namespace caret; using namespace std; //private namespace for implementation details namespace { struct TriInfo { Vector3D m_xyz[3]; float m_planeEq[3];//x coef, y coef, const : z = [0] * x + [1] * y + [2] bool vertRayHit(const float* xyz);//true if a +z ray from point hits this triangle TriInfo(const float* xyz1, const float* xyz2, const float* xyz3); TriInfo() {}; }; struct QuadInfo { TriInfo m_tris[2][2]; int vertRayHit(const float* xyz);//+z ray intersect: 0 if never, 1 if only 1 of the 2 triangulations, 2 if both QuadInfo(const float* xyz1, const float* xyz2, const float* xyz3, const float* xyz4); QuadInfo() {}; }; struct PolyInfo { 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() {}; 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 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))); } int PolyInfo::isInside(const float* xyz) { int i, temp, numQuads = (int)m_quads.size(); bool toggle = false; for (i = 0; i < numQuads; ++i) { temp = m_quads[i].vertRayHit(xyz); if (temp == 1) return 1;//means only one of the two triangulations has a hit, therefore, we can return early if (temp) toggle = !toggle;//even/odd winding rule } int numTris = (int)m_tris.size(); for (i = 0; i < numTris; ++i) { if (m_tris[i].vertRayHit(xyz)) toggle = !toggle; } if (toggle) return 2; return 0; } PolyInfo::PolyInfo(const caret::SurfaceFile* innerSurf, const caret::SurfaceFile* outerSurf, const int32_t node) { CaretPointer myTopoHelp = innerSurf->getTopologyHelper(); int numTiles; const int* myTiles = myTopoHelp->getNodeTiles(node, numTiles); for (int i = 0; i < numTiles; ++i) { const int32_t* myTri = innerSurf->getTriangle(myTiles[i]); if (myTri[0] == node) { addTri(innerSurf, outerSurf, myTri[0], myTri[1], myTri[2]); } else { if (myTri[1] == node) { addTri(innerSurf, outerSurf, myTri[1], myTri[2], myTri[0]); } else { addTri(innerSurf, outerSurf, myTri[2], myTri[0], myTri[1]); } } } } QuadInfo::QuadInfo(const float* xyz1, const float* xyz2, const float* xyz3, const float* xyz4) { m_tris[0][0] = TriInfo(xyz1, xyz2, xyz3); m_tris[0][1] = TriInfo(xyz1, xyz3, xyz4); m_tris[1][0] = TriInfo(xyz1, xyz2, xyz4); m_tris[1][1] = TriInfo(xyz2, xyz3, xyz4); } int QuadInfo::vertRayHit(const float* xyz) { int ret = 0; if (m_tris[0][0].vertRayHit(xyz) != m_tris[0][1].vertRayHit(xyz)) ++ret; if (m_tris[1][0].vertRayHit(xyz) != m_tris[1][1].vertRayHit(xyz)) ++ret; return ret; } TriInfo::TriInfo(const float* xyz1, const float* xyz2, const float* xyz3) { m_xyz[0] = xyz1; m_xyz[1] = xyz2; m_xyz[2] = xyz3; FloatMatrix myRref; myRref.resize(3, 4); for (int i = 0; i < 3; ++i)//ax + by + c = z { myRref[i][0] = m_xyz[i][0];//coefficient of a myRref[i][1] = m_xyz[i][1];//coefficient of b myRref[i][2] = 1;//coefficient of c myRref[i][3] = m_xyz[i][2];//what it equals } FloatMatrix myResult = myRref.reducedRowEchelon(); m_planeEq[0] = myResult[0][3];//a m_planeEq[1] = myResult[1][3];//b m_planeEq[2] = myResult[2][3];//c float sanity = m_planeEq[0] + m_planeEq[1] + m_planeEq[2]; if (!MathFunctions::isNumeric(sanity)) { m_planeEq[0] = sanity;//make sure the first element easily identifies vertical triangles } } bool TriInfo::vertRayHit(const float* xyz) { if (!MathFunctions::isNumeric(m_planeEq[0])) {//plane is vertical, nothing can hit it return false; } float planeZ = xyz[0] * m_planeEq[0] + xyz[1] * m_planeEq[1] + m_planeEq[2];//ax + by + c = z if (xyz[2] >= planeZ) {//point is above the plane return false; }//test if the x, y projection has the point inside the triangle //below logic copied from PNPOLY by Wm. Randolph Franklin, swapped x for y, and slightly rewritten, for the special case of 3 vertices bool inside = false; for (int j = 2, i = 0; i < 3; ++i)//start with the wraparound case { if ((m_xyz[i][0] < xyz[0]) != (m_xyz[j][0] < xyz[0])) {//if one vertex is on one side of the point in the x direction, and the other is on the other side (equal case is treated as greater) int ti, tj; if (m_xyz[i][0] < m_xyz[j][0])//reorient the segment consistently to get a consistent answer { ti = i; tj = j; } else { ti = j; tj = i; } if ((m_xyz[ti][1] - m_xyz[tj][1]) / (m_xyz[ti][0] - m_xyz[tj][0]) * (xyz[0] - m_xyz[tj][0]) + m_xyz[tj][1] > xyz[1]) {//if the point on the line described by the two vertices with the same x coordinate is above (greater y) than the test point inside = !inside;//even/odd winding rule again } } j = i;//consecutive vertices, does 2,0 then 0,1 then 1,2 } return inside; } float computeVoxelFraction(const VolumeSpace& myVolSpace, const int64_t* ijk, PolyInfo& myPoly, const int divisions, 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); int inside = 0; Vector3D istep = ivec / divisions; Vector3D jstep = jvec / divisions; Vector3D kstep = kvec / divisions; myLowCorner += istep * 0.5f + jstep * 0.5f + kstep * 0.5f; for (int i = 0; i < divisions; ++i) { Vector3D tempVeci = myLowCorner + istep * i; for (int j = 0; j < divisions; ++j) { Vector3D tempVecj = tempVeci + jstep * j; for (int k = 0; k < divisions; ++k) { Vector3D thisPoint = tempVecj + kstep * k; inside += myPoly.isInside(thisPoint); } } } return ((float)inside) / (divisions * divisions * divisions * 2); } } void RibbonMappingHelper::computeWeightsRibbon(vector >& myWeightsOut, const VolumeSpace& myVolSpace, const SurfaceFile* innerSurf, const SurfaceFile* outerSurf, const float* roiFrame, const int& numDivisions) { if (!innerSurf->hasNodeCorrespondence(*outerSurf)) { throw CaretException("input surfaces to ribbon mapping do not have vertex correspondence"); } if (numDivisions < 1) { throw CaretException("number of voxel subdivisions must be positive for ribbon mapping"); } int64_t numNodes = outerSurf->getNumberOfNodes(); myWeightsOut.resize(numNodes); Vector3D origin, ivec, jvec, kvec;//these are the spatial projections of the ijk unit vectors (also, the offset that specifies the origin) myVolSpace.getSpacingVectors(ivec, jvec, kvec, origin); const float* outerCoords = outerSurf->getCoordinateData(); const float* innerCoords = innerSurf->getCoordinateData(); const int64_t* myDims = myVolSpace.getDims(); #pragma omp CARET_PAR { int maxVoxelCount = 10;//guess for preallocating vectors CaretPointer myTopoHelp = innerSurf->getTopologyHelper(); #pragma omp CARET_FOR schedule(dynamic) for (int64_t node = 0; node < numNodes; ++node) { myWeightsOut[node].clear(); myWeightsOut[node].reserve(maxVoxelCount); float tempf; int64_t node3 = node * 3; PolyInfo myPoly(innerSurf, outerSurf, node);//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; myVolSpace.spaceToIndex(outerCoords + node3, tempvec); for (int i = 0; i < 3; ++i) { if (tempvec[i] < minIndex[i]) minIndex[i] = tempvec[i]; if (tempvec[i] > maxIndex[i]) maxIndex[i] = tempvec[i]; } int numNeigh; const int* myNeighList = myTopoHelp->getNodeNeighbors(node, numNeigh);//and now the neighbors for (int j = 0; j < numNeigh; ++j) { int neigh3 = myNeighList[j] * 3; myVolSpace.spaceToIndex(outerCoords + neigh3, tempvec); for (int i = 0; i < 3; ++i) { if (tempvec[i] < minIndex[i]) minIndex[i] = tempvec[i]; if (tempvec[i] > maxIndex[i]) maxIndex[i] = tempvec[i]; } myVolSpace.spaceToIndex(innerCoords + neigh3, tempvec); for (int i = 0; i < 3; ++i) { if (tempvec[i] < minIndex[i]) minIndex[i] = tempvec[i]; if (tempvec[i] > maxIndex[i]) maxIndex[i] = tempvec[i]; } } int startIndex[3], endIndex[3]; for (int i = 0; i < 3; ++i) { startIndex[i] = (int)ceil(minIndex[i] - 0.5f);//give an extra half voxel in order to get anything which could have some polygon in it endIndex[i] = (int)floor(maxIndex[i] + 0.5f) + 1;//ditto, plus the one-after end convention if (startIndex[i] < 0) startIndex[i] = 0;//keep it inside the volume boundaries if (endIndex[i] > myDims[i]) endIndex[i] = myDims[i]; } int64_t ijk[3]; for (ijk[0] = startIndex[0]; ijk[0] < endIndex[0]; ++ijk[0]) { for (ijk[1] = startIndex[1]; ijk[1] < endIndex[1]; ++ijk[1]) { for (ijk[2] = startIndex[2]; ijk[2] < endIndex[2]; ++ijk[2]) { if (roiFrame == NULL || roiFrame[myVolSpace.getIndex(ijk)] > 0.0f) { tempf = computeVoxelFraction(myVolSpace, ijk, myPoly, numDivisions, ivec, jvec, kvec); if (tempf != 0.0f) { myWeightsOut[node].push_back(VoxelWeight(tempf, ijk)); } } } } } if ((int)myWeightsOut[node].size() > maxVoxelCount) {//capacity() would use more memory maxVoxelCount = myWeightsOut[node].size(); } } } } connectome-workbench-1.2.3+git41-gc4c6c90/src/Files/RibbonMappingHelper.h000066400000000000000000000036241300200146000256530ustar00rootroot00000000000000#ifndef __RIBBON_MAPPING_HELPER_H__ #define __RIBBON_MAPPING_HELPER_H__ /*LICENSE_START*/ /* * Copyright (C) 2015 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 "stdint.h" #include #include namespace caret { class SurfaceFile; class VolumeSpace; struct VoxelWeight {//for precomputation in ribbon/myelin style volume to surface mapping float weight; int64_t ijk[3]; VoxelWeight() { }; VoxelWeight(const float weightIn, const int64_t* ijkIn) { weight = weightIn; ijk[0] = ijkIn[0]; ijk[1] = ijkIn[1]; ijk[2] = ijkIn[2]; } }; class RibbonMappingHelper { 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); }; } #endif //__RIBBON_MAPPING_HELPER_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Files/SceneFile.cxx000066400000000000000000000464731300200146000242050ustar00rootroot00000000000000 /*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 #define __SCENE_FILE_DECLARE__ #include "SceneFile.h" #undef __SCENE_FILE_DECLARE__ #include "CaretAssert.h" #include "CaretLogger.h" #include "DataFileContentInformation.h" #include "DataFileException.h" #include "FileAdapter.h" #include "FileInformation.h" #include "GiftiMetaData.h" #include "Scene.h" #include "SceneAttributes.h" #include "SceneClass.h" #include "SceneClassArray.h" #include "SceneFileSaxReader.h" #include "SceneInfo.h" #include "SceneXmlElements.h" #include "SceneWriterXml.h" #include "SpecFile.h" #include "XmlSaxParser.h" #include "XmlWriter.h" using namespace caret; /** * \class caret::SceneFile * \brief Contains scenes that reproduce Workbench state */ /** * Constructor. */ SceneFile::SceneFile() : CaretDataFile(DataFileTypeEnum::SCENE) { m_balsaStudyID = ""; m_baseDirectory = ""; m_metadata = new GiftiMetaData(); } /** * Destructor. */ SceneFile::~SceneFile() { delete m_metadata; for (std::vector::iterator iter = m_scenes.begin(); iter != m_scenes.end(); iter++) { delete *iter; } m_scenes.clear(); } /** * Clear the contents of this file. */ void SceneFile::clear() { CaretDataFile::clear(); m_metadata->clear(); m_balsaStudyID = ""; m_baseDirectory = ""; for (std::vector::iterator iter = m_scenes.begin(); iter != m_scenes.end(); iter++) { delete *iter; } m_scenes.clear(); } /** * @return true if the file is empty (no scenes) else false. */ bool SceneFile::isEmpty() const { return m_scenes.empty(); } /** * Add the given scene to the file. The file then * takes ownership of the scene. * * @param scene * Scene that is added. */ void SceneFile::addScene(Scene* scene) { CaretAssert(scene); m_scenes.push_back(scene); setModified(); } /** * Insert a scene above the given scene. The file then * takes ownership of the scene. * * @param newScene * New scene that is inserted. * @param insertAboveThisScene * The new scene is inserted above (before) this scene. */ void SceneFile::insertScene(Scene* newScene, const Scene* insertAboveThisScene) { CaretAssert(newScene); CaretAssert(insertAboveThisScene); std::vector tempSceneVector; bool newSceneInsertedFlag = false; for (std::vector::iterator iter = m_scenes.begin(); iter != m_scenes.end(); iter++) { Scene* scene = *iter; if (scene == insertAboveThisScene) { newSceneInsertedFlag = true; tempSceneVector.push_back(newScene); } tempSceneVector.push_back(scene); } if ( ! newSceneInsertedFlag) { m_scenes.push_back(newScene); CaretLogSevere("Scene insertion did not find \"insert above scene\""); } m_scenes = tempSceneVector; setModified(); } /** * Replace a scene. * @param newScene * New scene * @param sceneThatIsReplacedAndDeleted * Scene that is replaced and delete so DO NOT * reference this scene after calling this method. */ void SceneFile::replaceScene(Scene* newScene, Scene* sceneThatIsReplacedAndDeleted) { CaretAssert(newScene); CaretAssert(sceneThatIsReplacedAndDeleted); const int32_t numScenes = getNumberOfScenes(); for (int32_t i = 0; i < numScenes; i++) { if (m_scenes[i] == sceneThatIsReplacedAndDeleted) { delete m_scenes[i]; m_scenes[i] = newScene; setModified(); return; } } CaretAssertMessage(0, "Replacing scene failed due to scene not found."); CaretLogSevere("Replacing scene failed due to scene not found."); } /** * @return The number of scenes. */ int32_t SceneFile::getNumberOfScenes() const { return m_scenes.size(); } /** * Get the scene at the given index. * @param indx * Index of the scene. * @return * Scene at the given index. */ Scene* SceneFile::getSceneAtIndex(const int32_t indx) const { CaretAssertVectorIndex(m_scenes, indx); return m_scenes[indx]; } /** * Get the index of the given scene. * * @param scene * Scene for which index is requested. * @return * Index of the scene or negative if scene not found. */ int32_t SceneFile::getIndexOfScene(const Scene* scene) const { const int32_t numScenes = getNumberOfScenes(); for (int32_t i = 0; i < numScenes; i++) { if (scene == getSceneAtIndex(i)) { return i; } } return -1; } /** * Get the scene with the given name. * @param sceneName * Name of scene. * @return * Scene with given name or NULL if no scene with * the given name. */ Scene* SceneFile::getSceneWithName(const AString& sceneName) { const int32_t numScenes = getNumberOfScenes(); for (int32_t i = 0; i < numScenes; i++) { Scene* scene = getSceneAtIndex(i); if (scene->getName() == sceneName) { return scene; } } return NULL; } /** * Remove the given scene. * @param scene * Scene that should be removed. */ void SceneFile::removeScene(Scene* scene) { CaretAssert(scene); std::vector::iterator iter = std::find(m_scenes.begin(), m_scenes.end(), scene); if (iter != m_scenes.end()) { m_scenes.erase(iter); delete scene; setModified(); } } /** * Remove the scene at the given index. * @param indx * Index of the scene. */ void SceneFile::removeSceneAtIndex(const int32_t indx) { CaretAssertVectorIndex(m_scenes, indx); Scene* scene = getSceneAtIndex(indx); removeScene(scene); } /** * Remove the scene at the given index, but return its pointer rather than deleting it. * @param index * Index of the scene. */ Scene* SceneFile::releaseScene(const int32_t& index) { CaretAssertVectorIndex(m_scenes, index); Scene* scene = m_scenes[index]; m_scenes.erase(m_scenes.begin() + index); return scene; } /** * Move the scene in the file by the given change in index. * * @param scene * Scene that is moved. * @param indexDelta * Change of index of scene in the file. */ void SceneFile::moveScene(Scene* scene, const int32_t indexDelta) { if (indexDelta == 0) { return; } const int32_t numberOfScenes = getNumberOfScenes(); if (numberOfScenes == 1) { return; } const int32_t sceneIndex = getIndexOfScene(scene); const int32_t newSceneIndex = sceneIndex + indexDelta; if ((sceneIndex >= 0) && (sceneIndex < numberOfScenes)) { if (newSceneIndex < sceneIndex) { if (newSceneIndex >= 0) { /* * Remove scene from its index * insert scene into its new index */ m_scenes.erase(m_scenes.begin() + sceneIndex); m_scenes.insert(m_scenes.begin() + newSceneIndex, scene); setModified(); } } else { CaretAssert(newSceneIndex > sceneIndex); if (newSceneIndex < numberOfScenes) { /* * Innsert scene into its new index * Remove scene from its index */ m_scenes.insert(m_scenes.begin() + newSceneIndex + 1, scene); m_scenes.erase(m_scenes.begin() + sceneIndex); setModified(); } } } } /** * Reorder the scenes given the newly ordered scenes. * Any existing scenes not in the newly ordered scenes are * removed. * * @param orderedScenes * Newly ordered scenes. */ void SceneFile::reorderScenes(std::vector& orderedScenes) { /* * Make copy of pointers to existing scenes */ std::vector oldSceneVector = m_scenes; /* * Replace scenes with newly ordered scenes */ m_scenes = orderedScenes; /* * If an existing scene is not in the newly ordered scenes, * remove it. */ for (std::vector::iterator iter = oldSceneVector.begin(); iter != oldSceneVector.end(); iter++) { Scene* scene = *iter; if (std::find(m_scenes.begin(), m_scenes.end(), scene) == m_scenes.end()) { delete scene; } } setModified(); } int32_t SceneFile::getSceneIndexFromNumberOrName(const AString& numberOrName) { bool ok = false; int32_t ret = numberOrName.toInt(&ok) - 1;//compensate for 1-indexing that command line parsing uses if (ok) { if (ret < 0 || ret >= getNumberOfScenes()) { return -1; } return ret; } else {//DO NOT search by name if the string was parsed as an integer correctly, or some idiot who names their maps as integers will get confused //when getting map "12" out of a file after the file expands to more than 12 elements suddenly does something different const int32_t numScenes = getNumberOfScenes(); for (int32_t i = 0; i < numScenes; i++) { if (numberOrName == getSceneAtIndex(i)->getName()) { return i; } } } return -1; } /** * @return The structure for this file. */ StructureEnum::Enum SceneFile::getStructure() const { return StructureEnum::ALL; } /** * Set the structure for this file. * @param structure * New structure for this file. */ void SceneFile::setStructure(const StructureEnum::Enum /*structure*/) { /* ignore, not a structure related file */ } /** * @return Get access to the file's metadata. */ GiftiMetaData* SceneFile::getFileMetaData() { return m_metadata; } /** * @return Get access to unmodifiable file's metadata. */ const GiftiMetaData* SceneFile::getFileMetaData() const { return m_metadata; } /** * @return The BALSA Study ID. */ AString SceneFile::getBalsaStudyID() const { return m_balsaStudyID; } /** * Set the BALSA Study ID. * * @param balsaStudyID * New value for BALSA Study ID. */ void SceneFile::setBalsaStudyID(const AString& balsaStudyID) { if (balsaStudyID != m_balsaStudyID) { m_balsaStudyID = balsaStudyID; setModified(); } } /** * @return The Base Directory */ AString SceneFile::getBaseDirectory() const { return m_baseDirectory; } /** * Set the Base Directory. * * @param baseDirectory * New value for Base Directory. */ void SceneFile::setBaseDirectory(const AString& baseDirectory) { if (baseDirectory != m_baseDirectory) { m_baseDirectory = baseDirectory; setModified(); } } /** * Read the scene file. * @param filenameIn * Name of scene file. * @throws DataFileException * If there is an error reading the file. */ void SceneFile::readFile(const AString& filenameIn) { clear(); AString filename = filenameIn; if (DataFile::isFileOnNetwork(filename) == false) { FileInformation specInfo(filename); filename = specInfo.getAbsoluteFilePath(); } checkFileReadability(filename); this->setFileName(filename); SceneFileSaxReader saxReader(this); std::auto_ptr parser(XmlSaxParser::createXmlParser()); try { parser->parseFile(filename, &saxReader); } catch (const XmlSaxParserException& e) { clear(); this->setFileName(""); int lineNum = e.getLineNumber(); int colNum = e.getColumnNumber(); AString msg = "Parse Error while reading:"; if ((lineNum >= 0) && (colNum >= 0)) { msg += (" line/col (" + AString::number(e.getLineNumber()) + "/" + AString::number(e.getColumnNumber()) + ")"); } msg += (": " + e.whatString()); DataFileException dfe(filenameIn, msg); CaretLogThrowing(dfe); throw dfe; } this->setFileName(filename); this->clearModified(); } /** * Write the scene file. * @param filename * Name of scene file. * @throws DataFileException * If there is an error writing the file. */ void SceneFile::writeFile(const AString& filename) { if (!(filename.endsWith(".scene") || filename.endsWith(".wb_scene"))) { CaretLogWarning("scene file '" + filename + "' should be saved ending in .scene"); } checkFileWritability(filename); this->setFileName(filename); try { // // Format the version string so that it ends with at most one zero // const AString versionString = AString::number(SceneFile::getFileVersion(), 'f', 1); // // Open the file // FileAdapter file; AString errorMessage; QTextStream* textStream = file.openQTextStreamForWritingFile(this->getFileName(), errorMessage); if (textStream == NULL) { throw DataFileException(filename, errorMessage); } // // Create the xml writer // XmlWriter xmlWriter(*textStream); // // Write header info // xmlWriter.writeStartDocument("1.0"); // // Write root element // XmlAttributes attributes; //attributes.addAttribute("xmlns:xsi", // "http://www.w3.org/2001/XMLSchema-instance"); //attributes.addAttribute("xsi:noNamespaceSchemaLocation", // "http://brainvis.wustl.edu/caret6/xml_schemas/GIFTI_Caret.xsd"); attributes.addAttribute(SceneFile::XML_ATTRIBUTE_VERSION, versionString); xmlWriter.writeStartElement(SceneFile::XML_TAG_SCENE_FILE, attributes); // // Write Metadata // if (m_metadata != NULL) { m_metadata->writeAsXML(xmlWriter); } const int32_t numScenes = this->getNumberOfScenes(); /* * Write the scene info directory */ 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()); for (int32_t i = 0; i < numScenes; i++) { m_scenes[i]->getSceneInfo()->writeSceneInfo(xmlWriter, i); } xmlWriter.writeEndElement(); // // Write scenes // SceneWriterXml sceneWriter(xmlWriter, this->getFileName()); for (int32_t i = 0; i < numScenes; i++) { sceneWriter.writeScene(*m_scenes[i], i); } xmlWriter.writeEndElement(); xmlWriter.writeEndDocument(); file.close(); this->clearModified(); } catch (const GiftiException& e) { throw DataFileException(e); } catch (const XmlException& e) { throw DataFileException(e); } } /** * Add information about the file to the data file information. * * @param dataFileInformation * Consolidates information about a data file. */ void SceneFile::addToDataFileContentInformation(DataFileContentInformation& dataFileInformation) { CaretDataFile::addToDataFileContentInformation(dataFileInformation); const int32_t numScenes = getNumberOfScenes(); if (numScenes > 0) { AString sceneNamesText = "Scenes:"; for (int32_t i = 0; i < numScenes; i++) { const Scene* scene = getSceneAtIndex(i); sceneNamesText.appendWithNewLine("#" + AString::number(i + 1) + " " + scene->getName()); if (dataFileInformation.isOptionFlag(DataFileContentInformation::OPTION_SHOW_MAP_INFORMATION)) { sceneNamesText += ":"; const SceneAttributes* myAttrs = scene->getAttributes(); const SceneClass* guiMgrClass = scene->getClassWithName("guiManager"); if (guiMgrClass == NULL) { sceneNamesText.appendWithNewLine("missing guiManager class"); continue; } const SceneClass* sessMgrClass = guiMgrClass->getClass("m_sessionManager"); if (sessMgrClass == NULL) { sceneNamesText.appendWithNewLine("missing m_sessionManager class"); continue; } const SceneClassArray* brainArray = sessMgrClass->getClassArray("m_brains"); if (brainArray == NULL) { sceneNamesText.appendWithNewLine("missing m_brains class array"); continue; } 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) { sceneNamesText.appendWithNewLine("missing specFile class in m_brains element " + AString::number(j)); continue; } SpecFile tempSpec; tempSpec.restoreFromScene(myAttrs, specClass); std::vector tempNames = tempSpec.getAllDataFileNamesSelectedForLoading(); int numNames = (int)tempNames.size(); for (int k = 0; k < numNames; ++k) { sceneNamesText.appendWithNewLine(" " + tempNames[k]); } } } } dataFileInformation.addText(sceneNamesText); } } connectome-workbench-1.2.3+git41-gc4c6c90/src/Files/SceneFile.h000066400000000000000000000102031300200146000236100ustar00rootroot00000000000000#ifndef __SCENE_FILE__H_ #define __SCENE_FILE__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 "CaretDataFile.h" namespace caret { class Scene; class SceneFile : public CaretDataFile { public: SceneFile(); virtual ~SceneFile(); private: SceneFile(const SceneFile&); SceneFile& operator=(const SceneFile&); public: virtual void addToDataFileContentInformation(DataFileContentInformation& dataFileInformation); void clear(); void readFile(const AString& filename); void writeFile(const AString& filename); bool isEmpty() const; void addScene(Scene* scene); int32_t getIndexOfScene(const Scene* scene) const; void insertScene(Scene* newScene, const Scene* insertAboveThisScene); void replaceScene(Scene* newScene, Scene* sceneThatIsReplacedAndDeleted); int32_t getNumberOfScenes() const; Scene* getSceneAtIndex(const int32_t indx) const; Scene* getSceneWithName(const AString& sceneName); void moveScene(Scene* scene, const int32_t indexDelta); void removeScene(Scene* scene); void removeSceneAtIndex(const int32_t indx); StructureEnum::Enum getStructure() const; void setStructure(const StructureEnum::Enum structure); GiftiMetaData* getFileMetaData(); const GiftiMetaData* getFileMetaData() const; AString getBalsaStudyID() const; void setBalsaStudyID(const AString& balsaStudyID); AString getBaseDirectory() const; void setBaseDirectory(const AString& baseDirectory); void reorderScenes(std::vector& orderedScenes); int32_t getSceneIndexFromNumberOrName(const AString& numberOrName); Scene* releaseScene(const int32_t& index); // ADD_NEW_METHODS_HERE /** Version of file */ static float getFileVersion() { return s_sceneFileVersion; } /** XML Tag for scene file */ static const AString XML_TAG_SCENE_FILE; /** * XML Tag for Scene Info Directory element. */ static const AString XML_TAG_SCENE_INFO_DIRECTORY_TAG; /** XML Tag for Version attribute */ static const AString XML_ATTRIBUTE_VERSION; private: /** the scenes*/ std::vector m_scenes; /** the metadata */ GiftiMetaData* m_metadata; /** the BALSA Study ID */ AString m_balsaStudyID; /** the Base Directory */ AString m_baseDirectory; // ADD_NEW_MEMBERS_HERE /** Version of this SceneFile */ static const float s_sceneFileVersion; }; #ifdef __SCENE_FILE_DECLARE__ 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; #endif // __SCENE_FILE_DECLARE__ } // namespace #endif //__SCENE_FILE__H_ connectome-workbench-1.2.3+git41-gc4c6c90/src/Files/SceneFileSaxReader.cxx000066400000000000000000000277761300200146000260110ustar00rootroot00000000000000/*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 "CaretAssert.h" #include "CaretLogger.h" #include "GiftiMetaDataSaxReader.h" #include "GiftiXmlElements.h" #include "Scene.h" #include "SceneFile.h" #include "SceneFileSaxReader.h" #include "SceneInfo.h" #include "SceneInfoSaxReader.h" #include "SceneXmlElements.h" #include "XmlAttributes.h" #include "XmlException.h" #include "XmlUtilities.h" using namespace caret; /** * \class caret::SceneFileSaxReader * \brief Reads a scene file using a SAX XML Parser. */ /** * constructor. */ SceneFileSaxReader::SceneFileSaxReader(SceneFile* sceneFile) { CaretAssert(sceneFile); m_sceneFile = sceneFile; m_state = STATE_NONE; m_stateStack.push(m_state); m_elementText = ""; m_metaDataSaxReader = NULL; m_sceneSaxReader = NULL; m_sceneInfoSaxReader = NULL; m_scene = NULL; } /** * destructor. */ SceneFileSaxReader::~SceneFileSaxReader() { /* * If reading fails, allocated items need to be deleted. */ if (m_metaDataSaxReader != NULL) { delete m_metaDataSaxReader; } if (m_sceneSaxReader != NULL) { delete m_sceneSaxReader; } if (m_sceneInfoSaxReader != NULL) { delete m_sceneInfoSaxReader; } if (m_scene != NULL) { delete m_scene; } } /** * start an element. */ void SceneFileSaxReader::startElement(const AString& namespaceURI, const AString& localName, const AString& qName, const XmlAttributes& attributes) { const STATE previousState = m_state; switch (m_state) { case STATE_NONE: if (qName == SceneFile::XML_TAG_SCENE_FILE) { m_state = STATE_SCENE_FILE; // // Check version of file being read // const float version = attributes.getValueAsFloat(SceneFile::XML_ATTRIBUTE_VERSION); if (version > SceneFile::getFileVersion()) { AString msg = XmlUtilities::createInvalidVersionMessage(SceneFile::getFileVersion(), version); XmlSaxParserException e(msg); CaretLogThrowing(e); throw e; } } else { const AString msg = XmlUtilities::createInvalidRootElementMessage(SceneFile::XML_TAG_SCENE_FILE, qName); XmlSaxParserException e(msg); CaretLogThrowing(e); throw e; } break; case STATE_SCENE_INFO_DIRECTORY: if (qName == SceneXmlElements::SCENE_INFO_TAG) { m_state = STATE_SCENE_INFO; m_sceneInfoIndex = attributes.getValueAsIntRequired(SceneXmlElements::SCENE_INFO_INDEX_ATTRIBUTE); m_sceneInfo = new SceneInfo(); m_sceneInfoSaxReader = new SceneInfoSaxReader(m_sceneFile->getFileName(), m_sceneInfo); m_sceneInfoSaxReader->startElement(namespaceURI, localName, qName, attributes); } 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 { const AString msg = XmlUtilities::createInvalidChildElementMessage(SceneXmlElements::SCENE_INFO_TAG, qName); XmlSaxParserException e(msg); warning(e); // CaretLogThrowing(e); // throw e; } break; case STATE_SCENE_INFO_BALSA_STUDY_ID: break; case STATE_SCENE_INFO_BASE_DIRECTORY: break; case STATE_SCENE_INFO: m_sceneInfoSaxReader->startElement(namespaceURI, localName, qName, attributes); break; case STATE_SCENE: m_sceneSaxReader->startElement(namespaceURI, localName, qName, attributes); break; case STATE_SCENE_FILE: if (qName == GiftiXmlElements::TAG_METADATA) { m_state = STATE_METADATA; m_metaDataSaxReader = new GiftiMetaDataSaxReader(m_sceneFile->getFileMetaData()); m_metaDataSaxReader->startElement(namespaceURI, localName, qName, attributes); } else if (qName == SceneFile::XML_TAG_SCENE_INFO_DIRECTORY_TAG) { m_state = STATE_SCENE_INFO_DIRECTORY; break; } else if (qName == SceneXmlElements::SCENE_TAG) { m_state = STATE_SCENE; const AString sceneTypeName = attributes.getValue(SceneXmlElements::SCENE_TYPE_ATTRIBUTE); bool validName = false; const SceneTypeEnum::Enum sceneType = SceneTypeEnum::fromName(sceneTypeName, &validName); if (validName == false) { const AString msg = XmlUtilities::createInvalidAttributeMessage(SceneXmlElements::SCENE_TAG, SceneXmlElements::SCENE_TYPE_ATTRIBUTE, sceneTypeName); XmlSaxParserException e(msg); CaretLogThrowing(e); throw e; } m_scene = new Scene(sceneType); m_sceneSaxReader = new SceneSaxReader(m_sceneFile->getFileName(), m_scene); m_sceneSaxReader->startElement(namespaceURI, localName, qName, attributes); } else { const AString msg = XmlUtilities::createInvalidChildElementMessage(SceneXmlElements::SCENE_TAG, qName); XmlSaxParserException e(msg); warning(e); // CaretLogThrowing(e); // throw e; } break; case STATE_METADATA: m_metaDataSaxReader->startElement(namespaceURI, localName, qName, attributes); break; } // // Save previous state // m_stateStack.push(previousState); m_elementText = ""; } /** * end an element. */ void SceneFileSaxReader::endElement(const AString& namespaceURI, const AString& localName, const AString& qName) { switch (m_state) { case STATE_NONE: break; case STATE_SCENE: CaretAssert(m_scene); CaretAssert(m_sceneSaxReader); m_sceneSaxReader->endElement(namespaceURI, localName, qName); if (qName == SceneXmlElements::SCENE_TAG) { m_sceneFile->addScene(m_scene); m_scene = NULL; // do not delete since added to border file delete m_sceneSaxReader; m_sceneSaxReader = NULL; } break; case STATE_SCENE_INFO_DIRECTORY: break; case STATE_SCENE_INFO_BALSA_STUDY_ID: m_sceneFile->setBalsaStudyID(m_elementText); break; case STATE_SCENE_INFO_BASE_DIRECTORY: m_sceneFile->setBaseDirectory(m_elementText); break; case STATE_SCENE_INFO: CaretAssert(m_sceneInfo); CaretAssert(m_sceneInfoSaxReader); m_sceneInfoSaxReader->endElement(namespaceURI, localName, qName); if (qName == SceneXmlElements::SCENE_INFO_TAG) { m_sceneInfoMap.insert(std::make_pair(m_sceneInfoIndex, m_sceneInfo)); delete m_sceneInfoSaxReader; m_sceneInfoSaxReader = NULL; } break; case STATE_SCENE_FILE: { for (std::map::iterator iter = m_sceneInfoMap.begin(); iter != m_sceneInfoMap.end(); iter++) { const int32_t sceneIndex = iter->first; SceneInfo* sceneInfo = iter->second; CaretAssert(sceneInfo); if ((sceneIndex >= 0) && (sceneIndex < m_sceneFile->getNumberOfScenes())) { Scene* scene = m_sceneFile->getSceneAtIndex(sceneIndex); scene->setSceneInfo(sceneInfo); } else { const AString msg = ("SceneInfo has bad index=" + AString::number(sceneIndex) + " in file " + m_sceneFile->getFileName()); CaretAssertMessage(0, msg); CaretLogSevere(msg); } } } break; case STATE_METADATA: CaretAssert(m_metaDataSaxReader); m_metaDataSaxReader->endElement(namespaceURI, localName, qName); if (qName == GiftiXmlElements::TAG_METADATA) { delete m_metaDataSaxReader; m_metaDataSaxReader = NULL; } break; } // // Clear out for new elements // m_elementText = ""; // // Go to previous state // if (m_stateStack.empty()) { throw XmlSaxParserException("State stack is empty while reading XML NiftDataFile."); } m_state = m_stateStack.top(); m_stateStack.pop(); } /** * get characters in an element. */ void SceneFileSaxReader::characters(const char* ch) { if (m_metaDataSaxReader != NULL) { m_metaDataSaxReader->characters(ch); } else if (m_sceneSaxReader != NULL) { m_sceneSaxReader->characters(ch); } else if (m_sceneInfoSaxReader != NULL) { m_sceneInfoSaxReader->characters(ch); } else { m_elementText += ch; } } /** * a fatal error occurs. */ void SceneFileSaxReader::fatalError(const XmlSaxParserException& e) { /* std::ostringstream str; str << "Fatal Error at line number: " << e.getLineNumber() << "\n" << "Column number: " << e.getColumnNumber() << "\n" << "Message: " << e.whatString(); if (errorMessage.isEmpty() == false) { str << "\n" << errorMessage; } errorMessage = str.str(); */ // // Stop parsing // throw e; } // a warning occurs void SceneFileSaxReader::warning(const XmlSaxParserException& e) { CaretLogWarning("XML Parser Warning: " + e.whatString()); } // an error occurs void SceneFileSaxReader::error(const XmlSaxParserException& e) { CaretLogSevere("XML Parser Error: " + e.whatString()); throw e; } void SceneFileSaxReader::startDocument() { } void SceneFileSaxReader::endDocument() { } connectome-workbench-1.2.3+git41-gc4c6c90/src/Files/SceneFileSaxReader.h000066400000000000000000000076151300200146000254240ustar00rootroot00000000000000 #ifndef __SCENE_FILE_SAX_READER_H__ #define __SCENE_FILE_SAX_READER_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 "AString.h" #include "SceneSaxReader.h" #include "XmlSaxParserException.h" #include "XmlSaxParserHandlerInterface.h" namespace caret { class Scene; class SceneFile; class SceneInfo; class SceneInfoSaxReader; class GiftiMetaDataSaxReader; class XmlAttributes; class XmlException; class SceneFileSaxReader : public CaretObject, public XmlSaxParserHandlerInterface { public: SceneFileSaxReader(SceneFile* sceneFile); virtual ~SceneFileSaxReader(); void startElement(const AString& namespaceURI, const AString& localName, const AString& qName, const XmlAttributes& attributes); void endElement(const AString& namspaceURI, const AString& localName, const AString& qName); void characters(const char* ch); void fatalError(const XmlSaxParserException& e); void warning(const XmlSaxParserException& e); void error(const XmlSaxParserException& e); void startDocument(); void endDocument(); protected: /// file reading states enum STATE { /// no state STATE_NONE, /// processing SceneFile tag STATE_SCENE_FILE, /// processing MetaData tag STATE_METADATA, /// processing SceneInfoDirectory tag STATE_SCENE_INFO_DIRECTORY, /// processing scene info Balsa Study ID tag STATE_SCENE_INFO_BALSA_STUDY_ID, /// processing scene info Base Directory tag STATE_SCENE_INFO_BASE_DIRECTORY, /// processing SceneInfo tag STATE_SCENE_INFO, /// processing Scene tag STATE_SCENE }; /// file reading state STATE m_state; /// the state stack used when reading a file std::stack m_stateStack; /// the error message AString m_errorMessage; /// scene file that is being read SceneFile* m_sceneFile; /// scene that is being read Scene* m_scene; /// scene info that is being read SceneInfo* m_sceneInfo; /// index attribute of scene info being read int32_t m_sceneInfoIndex; /// element text AString m_elementText; /// GIFTI meta data sax reader GiftiMetaDataSaxReader* m_metaDataSaxReader; /// Scene sax reader SceneSaxReader* m_sceneSaxReader; /// Scene info sax reader SceneInfoSaxReader* m_sceneInfoSaxReader; /// map that stores scene info by index std::map m_sceneInfoMap; }; } // namespace #endif // __SCENE_FILE_SAX_READER_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Files/SignedDistanceHelper.cxx000066400000000000000000001213241300200146000263610ustar00rootroot00000000000000/*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*/ /* * For the function pointInTri(): * Original copyright for PNPOLY, though my version is entirely rewritten and modified * Source: http://www.ecse.rpi.edu/Homepages/wrf/Research/Short_Notes/pnpoly.html */ /* Copyright (c) 1970-2003, Wm. Randolph Franklin Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimers. 2. Redistributions in binary form must reproduce the above copyright notice in the documentation and/or other materials provided with the distribution. 3. The name of W. Randolph Franklin may not be used to endorse or promote products derived from this Software without specific prior written permission. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #include "BoundingBox.h" #include "CaretHeap.h" #include "SignedDistanceHelper.h" #include "SurfaceFile.h" #include "TopologyHelper.h" #include using namespace std; using namespace caret; float SignedDistanceHelper::dist(const float coord[3], WindingLogic myWinding) { CaretMutexLocker locked(&m_mutex); CaretSimpleMinHeap*, float> myHeap; myHeap.push(m_base->m_indexRoot, m_base->m_indexRoot->distToPoint(coord)); ClosestPointInfo tempInfo, bestInfo; float tempf = -1.0f, bestTriDist = -1.0f; bool first = true; int numChanged = 0; while (!myHeap.isEmpty()) { Oct* curOct = myHeap.pop(&tempf); if (first || tempf < bestTriDist) { if (curOct->m_leaf) { vector& myVecRef = *(curOct->m_data.m_triList); int numTris = (int)myVecRef.size(); for (int i = 0; i < numTris; ++i) { if (m_triMarked[myVecRef[i]] != 1) { m_triMarked[myVecRef[i]] = 1; m_triMarkChanged[numChanged++] = myVecRef[i]; tempf = unsignedDistToTri(coord, myVecRef[i], tempInfo); if (first || tempf < bestTriDist) { bestInfo = tempInfo; bestTriDist = tempf; first = false; } } } } else { for (int ci = 0; ci < 2; ++ci) { for (int cj = 0; cj < 2; ++cj) { for (int ck = 0; ck < 2; ++ck) { tempf = curOct->m_children[ci][cj][ck]->distToPoint(coord); if (first || tempf < bestTriDist) { myHeap.push(curOct->m_children[ci][cj][ck], tempf); } } } } } } } while (numChanged) { m_triMarked[m_triMarkChanged[--numChanged]] = 0;//need to do this before computeSign } return bestTriDist * computeSign(coord, bestInfo, myWinding); } void SignedDistanceHelper::barycentricWeights(const float coord[3], BarycentricInfo& baryInfoOut) { CaretMutexLocker locked(&m_mutex); CaretSimpleMinHeap*, float> myHeap; myHeap.push(m_base->m_indexRoot, m_base->m_indexRoot->distToPoint(coord)); ClosestPointInfo tempInfo, bestInfo; float tempf = -1.0f, bestTriDist = -1.0f; bool first = true; int numChanged = 0; while (!myHeap.isEmpty()) { Oct* curOct = myHeap.pop(&tempf); if (first || tempf < bestTriDist) { if (curOct->m_leaf) { vector& myVecRef = *(curOct->m_data.m_triList); int numTris = (int)myVecRef.size(); for (int i = 0; i < numTris; ++i) { if (m_triMarked[myVecRef[i]] != 1) { m_triMarked[myVecRef[i]] = 1; m_triMarkChanged[numChanged++] = myVecRef[i]; tempf = unsignedDistToTri(coord, myVecRef[i], tempInfo); if (first || tempf < bestTriDist) { bestInfo = tempInfo; bestTriDist = tempf; first = false; } } } } else { for (int ci = 0; ci < 2; ++ci) { for (int cj = 0; cj < 2; ++cj) { for (int ck = 0; ck < 2; ++ck) { tempf = curOct->m_children[ci][cj][ck]->distToPoint(coord); if (first || tempf < bestTriDist) { myHeap.push(curOct->m_children[ci][cj][ck], tempf); } } } } } } } while (numChanged) { m_triMarked[m_triMarkChanged[--numChanged]] = 0;//clean up } baryInfoOut.triangle = bestInfo.triangle; baryInfoOut.point = bestInfo.tempPoint; baryInfoOut.absDistance = bestTriDist; const int32_t* triNodes = m_base->getTriangle(bestInfo.triangle); baryInfoOut.nodes[0] = triNodes[0]; baryInfoOut.nodes[1] = triNodes[1]; baryInfoOut.nodes[2] = triNodes[2]; switch (bestInfo.type) { case 2: { baryInfoOut.type = BarycentricInfo::TRIANGLE; Vector3D vert1 = m_base->getCoordinate(triNodes[0]); Vector3D vert2 = m_base->getCoordinate(triNodes[1]); Vector3D vert3 = m_base->getCoordinate(triNodes[2]); Vector3D vp1 = vert1 - bestInfo.tempPoint; Vector3D vp2 = vert2 - bestInfo.tempPoint; Vector3D vp3 = vert3 - bestInfo.tempPoint; float weight1 = vp2.cross(vp3).length(); float weight2 = vp1.cross(vp3).length(); float weight3 = vp1.cross(vp2).length(); float weightsum = weight1 + weight2 + weight3; baryInfoOut.baryWeights[0] = weight1 / weightsum; baryInfoOut.baryWeights[1] = weight2 / weightsum; baryInfoOut.baryWeights[2] = weight3 / weightsum; } break; case 1: { baryInfoOut.type = BarycentricInfo::EDGE; Vector3D vert1 = m_base->getCoordinate(bestInfo.node1); Vector3D vert2 = m_base->getCoordinate(bestInfo.node2); Vector3D v21hat = vert2 - vert1; float origLength; v21hat = v21hat.normal(&origLength); float tempf = v21hat.dot(bestInfo.tempPoint - vert1); float weight2 = tempf / origLength; float weight1 = 1.0f - weight2; for (int i = 0; i < 3; ++i) { if (triNodes[i] == bestInfo.node1) { baryInfoOut.baryWeights[i] = weight1; } else if (triNodes[i] == bestInfo.node2) { baryInfoOut.baryWeights[i] = weight2; } else { baryInfoOut.baryWeights[i] = 0.0f; } } } break; case 0: baryInfoOut.type = BarycentricInfo::NODE; for (int i = 0; i < 3; ++i) { if (triNodes[i] == bestInfo.node1) { baryInfoOut.baryWeights[i] = 1.0f; } else { baryInfoOut.baryWeights[i] = 0.0f; } } break; }; for (int i = 0; i < 3; ++i) { if (baryInfoOut.baryWeights[i] < 0.0f) { baryInfoOut.baryWeights[i] = 0.0f; } } } int SignedDistanceHelper::computeSign(const float coord[3], SignedDistanceHelper::ClosestPointInfo myInfo, WindingLogic myWinding) { Vector3D point = coord; Vector3D result = point - myInfo.tempPoint; float tempf; switch (myWinding) { case EVEN_ODD: case NEGATIVE: case NONZERO: { int numChanged = 0; float positiveZ[3] = {0, 0, 1}; Vector3D point2 = point + positiveZ; int crossCount = 0; vector*> myStack; myStack.push_back(m_base->m_indexRoot); while (!myStack.empty()) { Oct* curOct = myStack[myStack.size() - 1]; myStack.pop_back(); if (curOct->m_leaf) { vector& myVecRef = *(curOct->m_data.m_triList); int numTris = (int)myVecRef.size(); for (int i = 0; i < numTris; ++i) { if (m_triMarked[myVecRef[i]] != 1) { m_triMarked[myVecRef[i]] = 1; m_triMarkChanged[numChanged++] = myVecRef[i]; const int32_t* myTileNodes = m_base->getTriangle(myVecRef[i]); Vector3D verts[3]; verts[0] = m_base->getCoordinate(myTileNodes[0]); verts[1] = m_base->getCoordinate(myTileNodes[1]); verts[2] = m_base->getCoordinate(myTileNodes[2]); Vector3D triNormal; MathFunctions::normalVector(verts[0], verts[1], verts[2], triNormal); float factor = triNormal[2];//equivalent to dot product with positiveZ if (factor != 0.0f) { if (triNormal.dot(verts[0] - point) / factor > 0.0f && pointInTri(verts, point, 0, 1)) { if (triNormal[2] < 0.0f) { ++crossCount; } else { --crossCount; } } } } } } else { for (int ci = 0; ci < 2; ++ci) { for (int cj = 0; cj < 2; ++cj) { for (int ck = 0; ck < 2; ++ck) { if (curOct->m_children[ci][cj][ck]->rayIntersects(coord, point2)) { myStack.push_back(curOct->m_children[ci][cj][ck]); } } } } } } while (numChanged) { m_triMarked[m_triMarkChanged[--numChanged]] = 0; } switch (myWinding) { case EVEN_ODD: if ((abs(crossCount) & 1) == 1) return -1;//& 1 instead of % 2 return 1; break; case NEGATIVE: if (crossCount < 0) return -1; return 1; break; case NONZERO: if (crossCount != 0) return -1; return 1; break; default: return 1;//because compiler can't handle when a switch doesn't accound for an enum value... } } break; case NORMALS: switch (myInfo.type) { case 0://node { int curSign = 0; int numChanged = 0; const vector& myTiles = m_base->m_topoHelp->getNodeTiles(myInfo.node1); bool first = true; float bestNorm = 0; Vector3D tempvec, tempvec2, bestCent; for (int i = 0; i < (int)myTiles.size(); ++i)//find the tile of the node with the normal most parallel to the line segment between centroid and point {//should be least likely to have an intervening triangle const int32_t* myTileNodes = m_base->getTriangle(myTiles[i]); Vector3D vert1 = m_base->getCoordinate(myTileNodes[0]); Vector3D vert2 = m_base->getCoordinate(myTileNodes[1]); Vector3D vert3 = m_base->getCoordinate(myTileNodes[2]); Vector3D centroid = (vert1 + vert2 + vert3) / 3.0f; if (MathFunctions::normalVector(vert1, vert2, vert3, tempvec))//make sure the triangle has a valid normal { tempvec2 = point - centroid; tempf = tempvec.dot(tempvec2.normal()); if (first || abs(tempf) > abs(bestNorm)) { if (tempf > 0.0f) { curSign = 1; } else { curSign = -1; } first = false; bestNorm = tempf; bestCent = centroid; } } } Vector3D mySeg = point - bestCent; float bestDist = mySeg.length(); Vector3D segNormal = mySeg.normal();//from the surface to the point, to match the convention of triangles with normals oriented outwards int majAxis = 0, midAxis = 1;//find the axes to use for projecting the triangles, discard the one most aligned with the line segment if (abs(mySeg[1]) < abs(mySeg[0])) { majAxis = 1; midAxis = 0; } if (abs(mySeg[2]) < abs(mySeg[midAxis])) { midAxis = 2; } vector*> myStack; myStack.push_back(m_base->m_indexRoot); while (!myStack.empty()) { Oct* curOct = myStack[myStack.size() - 1]; myStack.pop_back(); if (curOct->m_leaf) { vector& myVecRef = *(curOct->m_data.m_triList); int numTris = (int)myVecRef.size(); for (int i = 0; i < numTris; ++i) { if (m_triMarked[myVecRef[i]] != 1) { m_triMarked[myVecRef[i]] = 1; m_triMarkChanged[numChanged++] = myVecRef[i]; const int32_t* myTileNodes = m_base->getTriangle(myVecRef[i]); Vector3D verts[3]; verts[0] = m_base->getCoordinate(myTileNodes[0]); verts[1] = m_base->getCoordinate(myTileNodes[1]); verts[2] = m_base->getCoordinate(myTileNodes[2]); Vector3D triNormal; MathFunctions::normalVector(verts[0], verts[1], verts[2], triNormal); float factor = triNormal.dot(segNormal); if (factor == 0.0f) { continue;//skip triangles parallel to the line segment } float intersectDist = triNormal.dot(point - verts[0]) / factor; if (intersectDist > 0.0f && intersectDist < bestDist) { Vector3D inPlane = point - intersectDist * segNormal; if (pointInTri(verts, inPlane, majAxis, midAxis)) { bestDist = intersectDist; if (triNormal.dot(mySeg) > 0.0f) { curSign = 1; } else { curSign = -1; } } } } } } else { for (int ci = 0; ci < 2; ++ci) { for (int cj = 0; cj < 2; ++cj) { for (int ck = 0; ck < 2; ++ck) { if (curOct->m_children[ci][cj][ck]->lineSegmentIntersects(coord, bestCent)) { myStack.push_back(curOct->m_children[ci][cj][ck]); } } } } } } while (numChanged) { m_triMarked[m_triMarkChanged[--numChanged]] = 0; } return curSign; } break; case 1://edge { const vector& edgeInfo = m_base->m_topoHelp->getEdgeInfo(); const vector& edges = m_base->m_topoHelp->getNodeEdges(myInfo.node1); int whichEdge = -1, numEdges = (int)edges.size(); for (int i = 0; i < numEdges; ++i) { if (edgeInfo[edges[i]].node1 == myInfo.node2 || edgeInfo[edges[i]].node2 == myInfo.node2) { whichEdge = edges[i]; } } CaretAssert(whichEdge != -1); int tile1 = edgeInfo[whichEdge].tiles[0].tile, tile2 = edgeInfo[whichEdge].tiles[1].tile; Vector3D normalaccum, tempvec;//default constructor initializes it to the zero vector if (tile1 > -1) { const int32_t* tile1nodes = m_base->getTriangle(tile1); MathFunctions::normalVector(m_base->getCoordinate(tile1nodes[0]), m_base->getCoordinate(tile1nodes[1]), m_base->getCoordinate(tile1nodes[2]), tempvec); normalaccum += tempvec; } if (tile2 > -1) { const int32_t *tile2nodes = m_base->getTriangle(tile2); MathFunctions::normalVector(m_base->getCoordinate(tile2nodes[0]), m_base->getCoordinate(tile2nodes[1]), m_base->getCoordinate(tile2nodes[2]), tempvec); normalaccum += tempvec; } if (normalaccum.dot(result) < 0.0f) { return -1; } } break; case 2://face { Vector3D triNormal; const int32_t* triNodes = m_base->getTriangle(myInfo.triangle); Vector3D vert1 = m_base->getCoordinate(triNodes[0]); Vector3D vert2 = m_base->getCoordinate(triNodes[1]); Vector3D vert3 = m_base->getCoordinate(triNodes[2]); MathFunctions::normalVector(vert1, vert2, vert3, triNormal); if (triNormal.dot(result) < 0.0f) { return -1; } } break; } break; } return 1; } bool SignedDistanceHelper::pointInTri(Vector3D verts[3], Vector3D inPlane, int majAxis, int midAxis) { bool inside = false; for (int j = 2, i = 0; i < 3; ++i)//start with the wraparound case { if ((verts[i][majAxis] < inPlane[majAxis]) != (verts[j][majAxis] < inPlane[majAxis])) {//if one vertex is on one side of the point in the x direction, and the other is on the other side (equal case is treated as greater) int ti, tj; if (verts[i][majAxis] < verts[j][majAxis])//reorient the segment consistently to get a consistent answer { ti = i; tj = j; } else { ti = j; tj = i; } if ((verts[ti][midAxis] - verts[tj][midAxis]) / (verts[ti][majAxis] - verts[tj][majAxis]) * (inPlane[majAxis] - verts[tj][majAxis]) + verts[tj][midAxis] > inPlane[midAxis]) {//if the point on the line described by the two vertices with the same x coordinate is above (greater y) than the test point inside = !inside;//even/odd winding rule } } j = i;//consecutive vertices, does 2,0 then 0,1 then 1,2 } return inside; } ///"dumb" implementation, projects to plane, test if inside while finding closest point on each edge ///there are faster implementations out there, but this is easier to follow float SignedDistanceHelper::unsignedDistToTri(const float coord[3], int32_t triangle, ClosestPointInfo& myInfo) { const int32_t* triNodes = m_base->getTriangle(triangle); Vector3D point = coord; Vector3D verts[3]; int type = 0;//tracks whether it is closest to a node, an edge, or the face int32_t node1 = -1, node2 = -1;//tracks which nodes are involved Vector3D bestPoint; verts[0] = m_base->getCoordinate(triNodes[0]); verts[1] = m_base->getCoordinate(triNodes[1]); verts[2] = m_base->getCoordinate(triNodes[2]); Vector3D v10 = verts[1] - verts[0]; Vector3D xhat = v10.normal(); Vector3D v20 = verts[2] - verts[0]; float sanity; Vector3D yhat = (v20 - xhat * xhat.dot(v20)).normal(&sanity);//now we have our orthogonal basis vectors for projection if (sanity == 0.0f || abs(xhat.dot(yhat)) > 0.01f)//if our triangle is (mostly) degenerate, find the closest point on its edges instead of trying to project to the NaN plane { bool first = true; float bestLengthSqr = -1.0f;//track best squared length from edge to original point for (int j = 2, i = 0; i < 3; ++i)//start with the wraparound case { float length; Vector3D norm = (verts[j] - verts[i]).normal(&length); Vector3D mypoint; int temptype = 0, tempnode1, tempnode2 = -1; if (length > 0.0f) { Vector3D diff = point - verts[i]; float dot = norm.dot(diff); if (dot <= 0.0f) { mypoint = verts[i]; tempnode1 = triNodes[i]; } else if (dot >= length) { mypoint = verts[j]; tempnode1 = triNodes[j]; } else { mypoint = verts[i] + dot * norm; temptype = 1; tempnode1 = triNodes[i]; tempnode2 = triNodes[j]; } } else { temptype = 0; tempnode1 = triNodes[i]; mypoint = verts[i]; } float tempdistsqr = (point - mypoint).lengthsquared(); if (first || tempdistsqr < bestLengthSqr) { first = false; type = temptype; bestLengthSqr = tempdistsqr; bestPoint = mypoint; node1 = tempnode1; node2 = tempnode2; } j = i;//consecutive vertices, does 2,0 then 0,1 then 1,2 } } else { float vertxy[3][2]; for (int i = 0; i < 3; ++i)//project everything to the new plane with basis vectors xhat, yhat { vertxy[i][0] = xhat.dot(verts[i] - verts[0]); vertxy[i][1] = yhat.dot(verts[i] - verts[0]); } bool inside = true; float p[2] = { xhat.dot(point - verts[0]), yhat.dot(point - verts[0]) }; float bestxy[2]; float bestDist = -1.0f; for (int i = 0, j = 2, k = 1; i < 3; ++i)//start with the wraparound case { float norm[2] = { vertxy[j][0] - vertxy[i][0], vertxy[j][1] - vertxy[i][1] }; float diff[2] = { p[0] - vertxy[i][0], p[1] - vertxy[i][1] }; float direction[2] = { vertxy[k][0] - vertxy[i][0], vertxy[k][1] - vertxy[i][1] }; float edgelen = sqrt(norm[0] * norm[0] + norm[1] * norm[1]); if (edgelen != 0.0f) { norm[0] /= edgelen; norm[1] /= edgelen; float dot = direction[0] * norm[0] + direction[1] * norm[1]; direction[0] -= dot * norm[0];//direction is orthogonal to norm, in the direction of the third vertex direction[1] -= dot * norm[1]; if (diff[0] * direction[0] + diff[1] * direction[1] < 0.0f)//if dot product with (projected point - vert[i]) is negative {//we are outside the triangle, find the projection to this edge and break if it is the second time or otherwise known to be finished if (bestDist < 0.0f) { inside = false; dot = diff[0] * norm[0] + diff[1] * norm[1]; if (dot <= 0.0f)//if closest point on this edge is an endpoint, it is possible for another edge that we count as outside of to have a closer point { type = 0; node1 = triNodes[i]; bestPoint = verts[i]; bestxy[0] = vertxy[i][0]; bestxy[1] = vertxy[i][1]; } else if (dot >= edgelen) { type = 0; node1 = triNodes[j]; bestPoint = verts[j]; bestxy[0] = vertxy[j][0]; bestxy[1] = vertxy[j][1]; } else {//if closest point on the edge is in the middle of the edge, nothing can be closer, break type = 1; node1 = triNodes[i]; node2 = triNodes[j]; bestxy[0] = vertxy[i][0] + dot * norm[0]; bestxy[1] = vertxy[i][1] + dot * norm[1]; break; } diff[0] = p[0] - bestxy[0]; diff[1] = p[1] - bestxy[1]; bestDist = diff[0] * diff[0] + diff[1] * diff[1]; } else { int tempnode1; Vector3D tempbestPoint; float tempxy[2]; inside = false; dot = diff[0] * norm[0] + diff[1] * norm[1]; if (dot <= 0.0f) { tempnode1 = triNodes[i]; tempbestPoint = verts[i]; tempxy[0] = vertxy[i][0]; tempxy[1] = vertxy[i][1]; } else if (dot >= edgelen) { tempnode1 = triNodes[j]; tempbestPoint = verts[j]; tempxy[0] = vertxy[j][0]; tempxy[1] = vertxy[j][1]; } else {//again, middle of edge always wins, don't bother with the extra test type = 1; node1 = triNodes[i]; node2 = triNodes[j]; bestxy[0] = vertxy[i][0] + dot * norm[0]; bestxy[1] = vertxy[i][1] + dot * norm[1]; break; } diff[0] = p[0] - tempxy[0]; diff[1] = p[1] - tempxy[1]; float tempdist = diff[0] * diff[0] + diff[1] * diff[1]; if (tempdist < bestDist) { type = 0;//if it were in the middle of the edge, we wouldn't be here node1 = tempnode1; bestPoint = tempbestPoint; bestxy[0] = tempxy[0]; bestxy[0] = tempxy[0]; } break;//this is our second time outside an edge, we have now covered all 3 possible endpoints, so break } } } else { if (diff[0] * direction[0] + diff[1] * direction[1] < 0.0f)//since we don't have an edge, we don't need to othrogonalize direction, or project to the edge { inside = false; type = 0; node1 = triNodes[i]; bestPoint = verts[i]; break; } } k = j; j = i;//consecutive vertices, does 2,0 then 0,1 then 1,2 } if (inside) { bestxy[0] = p[0]; bestxy[1] = p[1]; type = 2; } if (type != 0) { bestPoint = bestxy[0] * xhat + bestxy[1] * yhat + verts[0]; } } Vector3D result = point - bestPoint; myInfo.type = type; myInfo.node1 = node1; myInfo.node2 = node2; myInfo.triangle = triangle; myInfo.tempPoint = bestPoint; return result.length(); } SignedDistanceHelper::SignedDistanceHelper(CaretPointer myBase) { m_base = myBase; int32_t numTris = m_base->m_numTris; m_triMarked = CaretArray(numTris); m_triMarkChanged = CaretArray(numTris); for (int32_t i = 0; i < numTris; ++i) { m_triMarked[i] = 0; } } SignedDistanceHelperBase::SignedDistanceHelperBase(const SurfaceFile* mySurf) { m_topoHelp = mySurf->getTopologyHelper(); const float* myBB = mySurf->getBoundingBox()->getBounds(); Vector3D minCoord, maxCoord; minCoord[0] = myBB[0]; maxCoord[0] = myBB[1]; minCoord[1] = myBB[2]; maxCoord[1] = myBB[3]; minCoord[2] = myBB[4]; maxCoord[2] = myBB[5]; m_indexRoot.grabNew(new Oct(minCoord, maxCoord)); const float* myCoordData = mySurf->getCoordinateData(); m_numNodes = mySurf->getNumberOfNodes(); int32_t numNodes3 = m_numNodes * 3; m_coordList.resize(numNodes3); for (int32_t i = 0; i < numNodes3; ++i) { m_coordList[i] = myCoordData[i]; } m_numTris = mySurf->getNumberOfTriangles(); m_triangleList.resize(m_numTris * 3); for (int32_t i = 0; i < m_numTris; ++i) { int32_t i3 = i * 3; const int32_t* thisTri = mySurf->getTriangle(i); m_triangleList[i3] = thisTri[0]; m_triangleList[i3 + 1] = thisTri[1]; m_triangleList[i3 + 2] = thisTri[2]; maxCoord = minCoord = myCoordData + thisTri[0] * 3;//set both to the coordinates of the first node in the triangle for (int j = 1; j < 3; ++j) { int32_t thisNode3 = thisTri[j] * 3; if (myCoordData[thisNode3] < minCoord[0]) minCoord[0] = myCoordData[thisNode3]; if (myCoordData[thisNode3 + 1] < minCoord[1]) minCoord[1] = myCoordData[thisNode3 + 1]; if (myCoordData[thisNode3 + 2] < minCoord[2]) minCoord[2] = myCoordData[thisNode3 + 2]; if (myCoordData[thisNode3] > maxCoord[0]) maxCoord[0] = myCoordData[thisNode3]; if (myCoordData[thisNode3 + 1] > maxCoord[1]) maxCoord[1] = myCoordData[thisNode3 + 1]; if (myCoordData[thisNode3 + 2] > maxCoord[2]) maxCoord[2] = myCoordData[thisNode3 + 2]; } addTriangle(m_indexRoot, i, minCoord, maxCoord);//use bounding box for now as an easy test to capture any chance of the triangle intersecting the Oct } } void SignedDistanceHelperBase::addTriangle(Oct* thisOct, int32_t triangle, float minCoord[3], float maxCoord[3]) { if (thisOct->m_leaf) { thisOct->m_data.m_triList->push_back(triangle); int numTris = (int)thisOct->m_data.m_triList->size(); if (numTris >= NUM_TRIS_TO_TEST && numTris % NUM_TRIS_TEST_INCR == NUM_TRIS_TO_TEST % NUM_TRIS_TEST_INCR)//the second modulus should const out { Vector3D tempMinCoord, tempMaxCoord; const float* myCoordData = m_coordList.data(); int totalSize = 0; int numSplit = 0; for (int i = 0; i < numTris; ++i)//gather data on how it would end up splitting { const int32_t* tempTri = getTriangle((*(thisOct->m_data.m_triList))[i]); tempMaxCoord = tempMinCoord = myCoordData + tempTri[0] * 3;//set both to the coordinates of the first node in the triangle for (int j = 1; j < 3; ++j) { int32_t thisNode3 = tempTri[j] * 3; if (myCoordData[thisNode3] < tempMinCoord[0]) tempMinCoord[0] = myCoordData[thisNode3]; if (myCoordData[thisNode3 + 1] < tempMinCoord[1]) tempMinCoord[1] = myCoordData[thisNode3 + 1]; if (myCoordData[thisNode3 + 2] < tempMinCoord[2]) tempMinCoord[2] = myCoordData[thisNode3 + 2]; if (myCoordData[thisNode3] > tempMaxCoord[0]) tempMaxCoord[0] = myCoordData[thisNode3]; if (myCoordData[thisNode3 + 1] > tempMaxCoord[1]) tempMaxCoord[1] = myCoordData[thisNode3 + 1]; if (myCoordData[thisNode3 + 2] > tempMaxCoord[2]) tempMaxCoord[2] = myCoordData[thisNode3 + 2]; } int minOct[3], maxOct[3]; thisOct->containingChild(tempMinCoord, minOct); thisOct->containingChild(tempMaxCoord, maxOct); int splitSize = 8; if (minOct[0] == maxOct[0]) splitSize >>= 1; if (minOct[1] == maxOct[1]) splitSize >>= 1; if (minOct[2] == maxOct[2]) splitSize >>= 1; totalSize += splitSize; if (splitSize != 8) ++numSplit; } if (numSplit > 0 && totalSize < 3.0f * numTris)//don't split if all triangles end up in all child octs, and try to balance speedup with memory usage { thisOct->makeChildren();//do the split for (int i = 0; i < numTris; ++i)//gather data on how it would end up splitting { const int32_t* tempTri = getTriangle((*(thisOct->m_data.m_triList))[i]); tempMaxCoord = tempMinCoord = myCoordData + tempTri[0] * 3;//set both to the coordinates of the first node in the triangle for (int j = 1; j < 3; ++j) { int32_t thisNode3 = tempTri[j] * 3; if (myCoordData[thisNode3] < tempMinCoord[0]) tempMinCoord[0] = myCoordData[thisNode3]; if (myCoordData[thisNode3 + 1] < tempMinCoord[1]) tempMinCoord[1] = myCoordData[thisNode3 + 1]; if (myCoordData[thisNode3 + 2] < tempMinCoord[2]) tempMinCoord[2] = myCoordData[thisNode3 + 2]; if (myCoordData[thisNode3] > tempMaxCoord[0]) tempMaxCoord[0] = myCoordData[thisNode3]; if (myCoordData[thisNode3 + 1] > tempMaxCoord[1]) tempMaxCoord[1] = myCoordData[thisNode3 + 1]; if (myCoordData[thisNode3 + 2] > tempMaxCoord[2]) tempMaxCoord[2] = myCoordData[thisNode3 + 2]; } for (int ci = 0; ci < 2; ++ci) { for (int cj = 0; cj < 2; ++cj) { for (int ck = 0; ck < 2; ++ck) { if (thisOct->m_children[ci][cj][ck]->boundsOverlaps(tempMinCoord, tempMaxCoord)) { addTriangle(thisOct->m_children[ci][cj][ck], (*(thisOct->m_data.m_triList))[i], tempMinCoord, tempMaxCoord); } } } } } thisOct->m_data.freeData();//and free up some memory } } } else { for (int ci = 0; ci < 2; ++ci) { for (int cj = 0; cj < 2; ++cj) { for (int ck = 0; ck < 2; ++ck) { if (thisOct->m_children[ci][cj][ck]->boundsOverlaps(minCoord, maxCoord)) { addTriangle(thisOct->m_children[ci][cj][ck], triangle, minCoord, maxCoord); } } } } } } const float* SignedDistanceHelperBase::getCoordinate(const int32_t nodeIndex) const { CaretAssert(nodeIndex >= 0 && nodeIndex < m_numNodes); return m_coordList.data() + (nodeIndex * 3); } const int32_t* SignedDistanceHelperBase::getTriangle(const int32_t tileIndex) const { CaretAssert(tileIndex >= 0 && tileIndex < m_numTris); return m_triangleList.data() + (tileIndex * 3); } SignedDistanceHelperBase::~SignedDistanceHelperBase() {//so we don't need to put TopologyHelper.h in our header, due to the smart pointer destructor } connectome-workbench-1.2.3+git41-gc4c6c90/src/Files/SignedDistanceHelper.h000066400000000000000000000105411300200146000260040ustar00rootroot00000000000000#ifndef __SIGNED_DISTANCE_HELPER_H__ #define __SIGNED_DISTANCE_HELPER_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 "Vector3D.h" #include "CaretMutex.h" #include "CaretPointer.h" #include "OctTree.h" #include namespace caret { class SurfaceFile; class TopologyHelper; class SignedDistanceHelperBase { struct TriVector {//specifically so we can cleanly deallocate the vector from non-leaf nodes when they split std::vector* m_triList; TriVector() { m_triList = new std::vector(); } ~TriVector() { freeData(); } void freeData() { if (m_triList != NULL) { delete m_triList; m_triList = NULL; } } }; static const int NUM_TRIS_TO_TEST = 50;//test for whether to split leaf at this number static const int NUM_TRIS_TEST_INCR = 50;//and again at further multiples of this CaretPointer > m_indexRoot; int32_t m_numTris, m_numNodes; std::vector m_coordList;//make a copy of what we need from SurfaceFile so that if the SurfaceFile gets destroyed, we don't crash std::vector m_triangleList; CaretPointer m_topoHelp; SignedDistanceHelperBase(); void addTriangle(Oct* thisOct, int32_t triangle, float minCoord[3], float maxCoord[3]); const float* getCoordinate(const int32_t nodeIndex) const;//make these public? probably don't want them to be widely used, that is what SurfaceFile is for (but we don't want to store a SurfaceFile pointer) const int32_t* getTriangle(const int32_t tileIndex) const; public: ~SignedDistanceHelperBase();//in order to let us not include TopologyHelper SignedDistanceHelperBase(const SurfaceFile* mySurf); friend class SignedDistanceHelper; }; struct BarycentricInfo { enum POINT_TYPE { NODE, EDGE, TRIANGLE }; int32_t triangle; Vector3D point; POINT_TYPE type; float absDistance; int32_t nodes[3]; float baryWeights[3]; }; class SignedDistanceHelper { public: enum WindingLogic { EVEN_ODD, NEGATIVE, NONZERO, NORMALS }; private: CaretMutex m_mutex; CaretPointer m_base; CaretArray m_triMarked; CaretArray m_triMarkChanged; SignedDistanceHelper(); struct ClosestPointInfo { int type; int32_t node1, node2, triangle; Vector3D tempPoint; }; float unsignedDistToTri(const float coord[3], int32_t triangle, ClosestPointInfo& myInfo); int computeSign(const float coord[3], ClosestPointInfo myInfo, WindingLogic myWinding); bool pointInTri(Vector3D verts[3], Vector3D inPlane, int majAxis, int midAxis); public: SignedDistanceHelper(CaretPointer myBase); ///return the signed distance value at the point float dist(const float coord[3], WindingLogic myWinding); ///find the closest point ON the surface, and return information about it ///will never have negative barycentric weights, or a point outside the triangle void barycentricWeights(const float coordIn[3], BarycentricInfo& baryInfoOut); }; } #endif //__SIGNED_DISTANCE_HELPER_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Files/SparseVolumeIndexer.cxx000066400000000000000000000262731300200146000263100ustar00rootroot00000000000000 /*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 __SPARSE_VOLUME_INDEXER_DECLARE__ #include "SparseVolumeIndexer.h" #undef __SPARSE_VOLUME_INDEXER_DECLARE__ #include "CaretLogger.h" using namespace caret; /** * \class caret::SparseVolumeIndexer * \brief Maps IJK indices or XYZ coordinates to a sparse volume data * \ingroup Files * * Some data sources (such as CIFTI files) contain data for a subset * (or sparse representation) of volume data. In a CIFTI file, there * is a list of IJK indices that indicate the offset of each voxel * in the data (and note that the data may be for both surface vertices * and volume voxels). Finding a particular voxel by its IJK indices * requires a sequential search through the CIFTI's list of voxels. * This class permits a much faster location of a particular CIFTI file's * voxel by creating what is essentially a lookup table that maps the * full non-sparse volume IJK indices to an offset in the CIFTI data * or a negative value if the voxel is not present in the CIFTI data. */ /** * Default constructor with invalid state. */ SparseVolumeIndexer::SparseVolumeIndexer() : CaretObject() { m_dataValid = false; } /** * Constructs instance with the given CIFTI Brain Models Map. * * @param ciftiBrainModelsMap * The CIFTI brain models map. */ SparseVolumeIndexer::SparseVolumeIndexer(const CiftiBrainModelsMap& ciftiBrainModelsMap) : CaretObject() { m_dataValid = false; if ( ! ciftiBrainModelsMap.hasVolumeData()) { return; } m_volumeSpace = ciftiBrainModelsMap.getVolumeSpace(); std::vector ciftiVoxelMapping = ciftiBrainModelsMap.getFullVolumeMap(); const int32_t numberOfCiftiVolumeVoxels = static_cast(ciftiVoxelMapping.size()); if (numberOfCiftiVolumeVoxels <= 0) { return; } /* * Make sure orthogonal */ if ( ! m_volumeSpace.isPlumb()) { CaretLogWarning("CIFTI Volume is not Plumb!"); return; } const int64_t* dimIJK = m_volumeSpace.getDims(); const int64_t numberOfVoxels = (dimIJK[0] * dimIJK[1] * dimIJK[2]); if (numberOfVoxels <= 0) { return; } for (std::vector::const_iterator iter = ciftiVoxelMapping.begin(); iter != ciftiVoxelMapping.end(); iter++) { const CiftiBrainModelsMap::VolumeMap& vm = *iter; m_voxelIndexLookup.at(vm.m_ijk) = vm.m_ciftiIndex; } bool validateFlag = true; if (validateFlag) { AString validateString; for (std::vector::const_iterator iter = ciftiVoxelMapping.begin(); iter != ciftiVoxelMapping.end(); iter++) { const CiftiBrainModelsMap::VolumeMap& vm = *iter; const int64_t* foundOffset = m_voxelIndexLookup.find(vm.m_ijk); if (foundOffset != NULL) { if (*foundOffset != vm.m_ciftiIndex) { validateString.appendWithNewLine("IJK (" + AString::fromNumbers(vm.m_ijk, 3, ",") + " should have lookup value " + AString::number(vm.m_ciftiIndex) + " but has value " + AString::number(*foundOffset)); } } else { validateString.appendWithNewLine("IJK (" + AString::fromNumbers(vm.m_ijk, 3, ",") + " should have lookup value " + AString::number(vm.m_ciftiIndex) + " but was not found."); } } if (validateString.isEmpty() == false) { CaretLogSevere("Sparse Indexer Errors:\n" + validateString); } } m_dataValid = true; } /** * Constructs instance with the given CIFTI Parcel Map. * * @param parcel * The CIFTI parcel map. */ SparseVolumeIndexer::SparseVolumeIndexer(const CiftiParcelsMap& ciftiParcelsMap) : CaretObject() { m_dataValid = false; if ( ! ciftiParcelsMap.hasVolumeData()) { return; } m_volumeSpace = ciftiParcelsMap.getVolumeSpace(); /* * Make sure orthogonal */ if ( ! m_volumeSpace.isPlumb()) { CaretLogWarning("CIFTI Volume is not Plumb!"); return; } const std::vector allParcels = ciftiParcelsMap.getParcels(); for (std::vector::const_iterator parcelIter = allParcels.begin(); parcelIter != allParcels.end(); parcelIter++) { const CiftiParcelsMap::Parcel& parcel = *parcelIter; // const int32_t numberOfCiftiVolumeVoxels = static_cast(parcel.m_voxelIndices.size()); // if (numberOfCiftiVolumeVoxels <= 0) { // return; // } // // const int64_t* dimIJK = m_volumeSpace.getDims(); // const int64_t numberOfVoxels = (dimIJK[0] * dimIJK[1] * dimIJK[2]); // if (numberOfVoxels <= 0) { // return; // } for (std::set::const_iterator iter = parcel.m_voxelIndices.begin(); iter != parcel.m_voxelIndices.end(); iter++) { const VoxelIJK& vm = *iter; m_voxelIndexLookup.at(vm.m_ijk) = ciftiParcelsMap.getIndexForVoxel(vm.m_ijk); } } // bool validateFlag = true; // if (validateFlag) { // AString validateString; // for (std::set::const_iterator iter = ciftiParcel.m_voxelIndices.begin(); // iter != ciftiParcel.m_voxelIndices.end(); // iter++) { // const VoxelIJK& vm = *iter; // const int64_t* foundOffset = m_voxelIndexLookup.find(vm.m_ijk); // const int64_t voxelOffset = ciftiParcelsMap.getIndexForVoxel(vm.m_ijk); // if (foundOffset != NULL) { // if (*foundOffset != voxelOffset) { // validateString.appendWithNewLine("IJK (" // + AString::fromNumbers(vm.m_ijk, 3, ",") // + " should have lookup value " // + AString::number(voxelOffset) // + " but has value " // + AString::number(*foundOffset)); // } // } // else { // validateString.appendWithNewLine("IJK (" // + AString::fromNumbers(vm.m_ijk, 3, ",") // + " should have lookup value " // + AString::number(voxelOffset) // + " but was not found."); // } // } // if (validateString.isEmpty() == false) { // CaretLogSevere("Sparse Indexer Errors:\n" // + validateString); // } // } m_dataValid = true; } /** * Destructor. */ SparseVolumeIndexer::~SparseVolumeIndexer() { } /** * @return True if this instance is valid. */ bool SparseVolumeIndexer::isValid() const { return m_dataValid; } /** * Get the offset for the given IJK indices. * * @param i * I index. * @param j * J index. * @param k * K index. * @return * Offset for given indices or -1 if no data for the given indices. */ int64_t SparseVolumeIndexer::getOffsetForIndices(const int64_t i, const int64_t j, const int64_t k) const { if (m_dataValid) { const int64_t* offset = m_voxelIndexLookup.find(i, j, k); if (offset != NULL) { return *offset; } } return -1; } /** * Convert the coordinates to volume indices. Any coordinates are accepted * and output indices are not necessarily within the volume. * * @param x * X coordinate. * @param y * y coordinate. * @param z * z coordinate. * @param iOut * I index. * @param jOut * J index. * @param kOut * K index. * @return * True if volume attributes (origin/spacing/dimensions) are valid. */ bool SparseVolumeIndexer::coordinateToIndices(const float x, const float y, const float z, int64_t& iOut, int64_t& jOut, int64_t& kOut) const { if (m_dataValid) { m_volumeSpace.enclosingVoxel(x, y, z, iOut, jOut, kOut); return true; } return false; } /** * Get the offset for the given XYZ coordinates. * * @param x * X coordinate. * @param y * y coordinate. * @param z * z coordinate. * @return * Offset for given coordinates or -1 if no data for the given coordinates. */ int64_t SparseVolumeIndexer::getOffsetForCoordinate(const float x, const float y, const float z) const { if (m_dataValid) { int64_t i, j, k; m_volumeSpace.enclosingVoxel(x, y, z, i, j, k); return getOffsetForIndices(i, j, k); } return -1; } /** * Get the XYZ coordinate for the given indices. * Any indices are accepted. * * @param i * I index. * @param j * J index. * @param k * K index. * @param xOut * X coordinate. * @param yOut * y coordinate. * @param zOut * z coordinate. * @return * True if volume attributes (origin/spacing/dimensions) are valid. */ bool SparseVolumeIndexer::indicesToCoordinate(const int64_t i, const int64_t j, const int64_t k, float& xOut, float& yOut, float& zOut) const { if (m_dataValid) { m_volumeSpace.indexToSpace(i, j, k, xOut, yOut, zOut); return true; } return false; } connectome-workbench-1.2.3+git41-gc4c6c90/src/Files/SparseVolumeIndexer.h000066400000000000000000000060101300200146000257200ustar00rootroot00000000000000#ifndef __SPARSE_VOLUME_INDEXER_H__ #define __SPARSE_VOLUME_INDEXER_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 "CaretCompact3DLookup.h" #include "CaretObject.h" #include "CiftiBrainModelsMap.h" #include "CiftiParcelsMap.h" #include "VolumeSpace.h" namespace caret { class SparseVolumeIndexer : public CaretObject { public: SparseVolumeIndexer(); SparseVolumeIndexer(const CiftiBrainModelsMap& ciftiBrainModelsMap); SparseVolumeIndexer(const CiftiParcelsMap& ciftiParcelsMap); virtual ~SparseVolumeIndexer(); bool isValid() const; bool coordinateToIndices(const float x, const float y, const float z, int64_t& iOut, int64_t& jOut, int64_t& kOut) const; bool indicesToCoordinate(const int64_t i, const int64_t j, const int64_t k, float& xOut, float& yOut, float& zOut) const; int64_t getOffsetForIndices(const int64_t i, const int64_t j, const int64_t k) const; int64_t getOffsetForCoordinate(const float x, const float y, const float z) const; inline const VolumeSpace& getVolumeSpace() const { return m_volumeSpace; } private: SparseVolumeIndexer(const SparseVolumeIndexer&); SparseVolumeIndexer& operator=(const SparseVolumeIndexer&); public: // ADD_NEW_METHODS_HERE private: // ADD_NEW_MEMBERS_HERE bool m_dataValid; CaretCompact3DLookup m_voxelIndexLookup; VolumeSpace m_volumeSpace; }; #ifdef __SPARSE_VOLUME_INDEXER_DECLARE__ // #endif // __SPARSE_VOLUME_INDEXER_DECLARE__ } // namespace #endif //__SPARSE_VOLUME_INDEXER_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Files/SpecFile.cxx000066400000000000000000001613031300200146000240300ustar00rootroot00000000000000/*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 "CaretAssert.h" #include "CaretLogger.h" #include "CaretPointer.h" #include "DataFileContentInformation.h" #include "DataFileException.h" #include "EventGetDisplayedDataFiles.h" #include "EventManager.h" #include "FileAdapter.h" #include "FileInformation.h" #include "GiftiMetaData.h" #include "SceneAttributes.h" #include "SceneClass.h" #include "SceneClassArray.h" #define __SPEC_FILE_DEFINE__ #include "SpecFile.h" #undef __SPEC_FILE_DEFINE__ #include "SpecFileDataFile.h" #include "SpecFileDataFileTypeGroup.h" #include "SpecFileSaxReader.h" #include "StringTableModel.h" #include "SystemUtilities.h" #include "XmlSaxParser.h" #include "XmlWriter.h" using namespace caret; /** * \class caret::SpecFile * \brief A spec file groups caret data files. * \ingroup Files */ /** * Constructor. */ SpecFile::SpecFile() : CaretDataFile(DataFileTypeEnum::SPECIFICATION) { this->initializeSpecFile(); } /** * Destructor. */ SpecFile::~SpecFile() { for (std::vector::const_iterator iter = dataFileTypeGroups.begin(); iter != dataFileTypeGroups.end(); iter++) { delete *iter; } this->dataFileTypeGroups.clear(); delete this->metadata; this->metadata = NULL; } /** * Copy constructor. * @param sf * Spec file whose data is copied. */ SpecFile::SpecFile(const SpecFile& sf) : CaretDataFile(sf) { this->initializeSpecFile(); this->copyHelperSpecFile(sf); } /** * Assignment operator. * @param sf * Spec file whose data is copied. * @return * Reference to this file. */ SpecFile& SpecFile::operator=(const SpecFile& sf) { if (this != &sf) { CaretDataFile::operator=(sf); this->copyHelperSpecFile(sf); } return *this; } /** * @return The structure for this file. */ StructureEnum::Enum SpecFile::getStructure() const { return StructureEnum::ALL; } /** * Set the structure for this file. * @param structure * New structure for this file. */ void SpecFile::setStructure(const StructureEnum::Enum /* structure */) { /* nothing since spec file not structure type file */ } /** * @return Get access to the file's metadata. */ GiftiMetaData* SpecFile::getFileMetaData() { return metadata; } /** * @return Get access to unmodifiable file's metadata. */ const GiftiMetaData* SpecFile::getFileMetaData() const { return metadata; } /** * Initialize this spec file. */ void SpecFile::initializeSpecFile() { this->metadata = new GiftiMetaData(); std::vector allEnums; DataFileTypeEnum::getAllEnums(allEnums, DataFileTypeEnum::OPTIONS_NONE); /* * Do surface files first since they need to be loaded before other files */ for (std::vector::iterator iter = allEnums.begin(); iter != allEnums.end(); iter++) { DataFileTypeEnum::Enum dataFileType = *iter; const AString typeName = DataFileTypeEnum::toName(dataFileType); if (typeName.startsWith("SURFACE")) { SpecFileDataFileTypeGroup* dftg = new SpecFileDataFileTypeGroup(dataFileType); this->dataFileTypeGroups.push_back(dftg); } } /* * Do remaining file types excluding surfaces */ for (std::vector::iterator iter = allEnums.begin(); iter != allEnums.end(); iter++) { DataFileTypeEnum::Enum dataFileType = *iter; if (dataFileType == DataFileTypeEnum::UNKNOWN) { // ignore } else { const AString typeName = DataFileTypeEnum::toName(dataFileType); if (typeName.startsWith("SURFACE") == false) { SpecFileDataFileTypeGroup* dftg = new SpecFileDataFileTypeGroup(dataFileType); this->dataFileTypeGroups.push_back(dftg); } } } } /** * Copy helper. * @param sf * Spec file whose data is copied. */ void SpecFile::copyHelperSpecFile(const SpecFile& sf) { this->clearData(); if (this->metadata != NULL) { delete this->metadata; } this->metadata = new GiftiMetaData(*sf.metadata); for (std::vector::const_iterator iter = sf.dataFileTypeGroups.begin(); iter != sf.dataFileTypeGroups.end(); iter++) { SpecFileDataFileTypeGroup* group = *iter; const int numFiles = group->getNumberOfFiles(); for (int32_t i = 0; i < numFiles; i++) { SpecFileDataFile* file = group->getFileInformation(i); this->addDataFile(group->getDataFileType(), file->getStructure(), file->getFileName(), file->isLoadingSelected(), file->isSavingSelected(), file->isSpecFileMember()); } } this->setFileName(sf.getFileName()); this->clearModified(); } /** * Change the name of a file. The existing SpecFileDataFile remains with * its CaretDataFile removed and a new SpecFileDataFile is created with * the CaretDataFile. * * @param specFileDataFile * The SpecFileDataFile that has its name changed. * @param newFileName * New name for file. * @return New SpecFileDataFile that is created or NULL if filename did not * change of there is an error. */ SpecFileDataFile* SpecFile::changeFileName(SpecFileDataFile* specFileDataFile, const AString& newFileName) { CaretAssert(specFileDataFile); /* * Make sure name changed. */ if (specFileDataFile->getFileName() == newFileName) { return NULL; } /* * Create a new SpecFileDataFile */ SpecFileDataFile* newSpecFileDataFile = new SpecFileDataFile(*specFileDataFile); newSpecFileDataFile->setFileName(newFileName); newSpecFileDataFile->setCaretDataFile(specFileDataFile->getCaretDataFile()); /* * Remove CaretDataFile from previous SpecFileDataFile */ specFileDataFile->setCaretDataFile(NULL); specFileDataFile->setSavingSelected(false); /* * Add new SpecFileDataFile to appropriate group. */ for (std::vector::const_iterator iter = dataFileTypeGroups.begin(); iter != dataFileTypeGroups.end(); iter++) { SpecFileDataFileTypeGroup* group = *iter; if (group->getDataFileType() == newSpecFileDataFile->getDataFileType()) { group->addFileInformation(newSpecFileDataFile); return newSpecFileDataFile; } } CaretAssert(0); CaretLogSevere("PROGRAM ERROR: Failed to match DataFileType"); return NULL; } /** * Add a Caret Data File. * * @param If there is a a spec file entry with the same name as the Caret * Data File, the caret data file is added to the spec file entry. * Otherwise, a new Spec File Entry is created with the given Caret * Data File. * * @param caretDataFile * Caret data file that is added to, or creates, a spec file entry. */ void SpecFile::addCaretDataFile(CaretDataFile* caretDataFile) { CaretAssert(caretDataFile); if ( ! SpecFile::isDataFileTypeAllowedInSpecFile(caretDataFile->getDataFileType())) { return; } /* * Matches to first file found that has matching name and data file type */ SpecFileDataFile* matchedSpecFileDataFile = NULL; /* * Matches to first file found that has matching name and data file type * AND has its caret data file with a NULL value. */ SpecFileDataFile* matchedSpecFileDataFileWithNULL = NULL; /* * Group that matches data file type */ SpecFileDataFileTypeGroup* matchedDataFileTypeGroup = NULL; /* * Find matches */ for (std::vector::const_iterator iter = dataFileTypeGroups.begin(); iter != dataFileTypeGroups.end(); iter++) { SpecFileDataFileTypeGroup* dataFileTypeGroup = *iter; if (dataFileTypeGroup->getDataFileType() == caretDataFile->getDataFileType()) { matchedDataFileTypeGroup = dataFileTypeGroup; const int32_t numFiles = dataFileTypeGroup->getNumberOfFiles(); for (int32_t i = 0; i < numFiles; i++) { SpecFileDataFile* sfdf = dataFileTypeGroup->getFileInformation(i); if (sfdf->getCaretDataFile() == caretDataFile) { return; } if (sfdf->getFileName() == caretDataFile->getFileName()) { matchedSpecFileDataFile = sfdf; if (sfdf->getCaretDataFile() == NULL) { matchedSpecFileDataFileWithNULL = sfdf; break; } } } } if (matchedSpecFileDataFileWithNULL != NULL) { break; } } CaretAssert(matchedDataFileTypeGroup); SpecFileDataFile* specFileDataFileToUpdate = NULL; if (matchedSpecFileDataFileWithNULL != NULL) { /* * Found item that matched with a NULL value for caret data file */ specFileDataFileToUpdate = matchedSpecFileDataFileWithNULL; } else if (matchedSpecFileDataFile != NULL) { /* * Found item that matched but had non-NULL value for caret data file. * This means that there is a copy of a file loaded (two files same name) */ specFileDataFileToUpdate = new SpecFileDataFile(*matchedSpecFileDataFile); matchedDataFileTypeGroup->addFileInformation(specFileDataFileToUpdate); } else { /* * No matches found, file is not in spec file */ specFileDataFileToUpdate = new SpecFileDataFile(caretDataFile->getFileName(), caretDataFile->getDataFileType(), caretDataFile->getStructure(), false); matchedDataFileTypeGroup->addFileInformation(specFileDataFileToUpdate); } specFileDataFileToUpdate->setStructure(caretDataFile->getStructure()); specFileDataFileToUpdate->setCaretDataFile(caretDataFile); } /** * Remove a Caret Data File. * * @param If there is a a spec file entry with the given caret data file * remove it. Note: file has likely already been deleted so use only the * the caret data file pointer but to not deference it. * * @param caretDataFile * Caret data file that is removed from a spec file entry. */ void SpecFile::removeCaretDataFile(const CaretDataFile* caretDataFile) { CaretAssert(caretDataFile); /* * Get the entry */ for (std::vector::const_iterator iter = dataFileTypeGroups.begin(); iter != dataFileTypeGroups.end(); iter++) { SpecFileDataFileTypeGroup* dataFileTypeGroup = *iter; const int32_t numFiles = dataFileTypeGroup->getNumberOfFiles(); for (int32_t i = 0; i < numFiles; i++) { SpecFileDataFile* sfdf = dataFileTypeGroup->getFileInformation(i); if (sfdf->getCaretDataFile() == caretDataFile) { sfdf->setCaretDataFile(NULL); return; } } } CaretLogSevere("Failed to remove CaretDataFile at address " + AString::number((qulonglong)caretDataFile) + " from SpecFile: " + getFileName()); } /** * Add a data file to this spec file. * * @param dataFileType * Type of data file. * @param structure * Structure of data file (not all files use structure). * @param filename * Name of the file. * @param fileLoadingSelectionStatus * Selection status for loading of the file. * @param fileSavingSelectionStatus * Selection status for saving of the file. * @param specFileMemberStatus * True if the file is a member of the spec file and is written * into the spec file. * * @throws DataFileException * If data file type is UNKNOWN. */ void SpecFile::addDataFile(const DataFileTypeEnum::Enum dataFileType, const StructureEnum::Enum structure, const AString& filename, const bool fileLoadingSelectionStatus, const bool fileSavingSelectionStatus, const bool specFileMemberStatus) { addDataFilePrivate(dataFileType, structure, filename, fileLoadingSelectionStatus, fileSavingSelectionStatus, specFileMemberStatus); } /** * Add a data file to this spec file. * * @param dataFileType * Type of data file. * @param structure * Structure of data file (not all files use structure). * @param filename * Name of the file. * @param fileLoadingSelectionStatus * Selection status for loading of the file. * @param fileSavingSelectionStatus * Selection status for saving of the file. * @param specFileMemberStatus * True if the file is a member of the spec file and is written * into the spec file. * @return * SpecFileDataFile that was created or matched. NULL if error or file type not allowed. * * @throws DataFileException * If data file type is UNKNOWN. */ SpecFileDataFile* SpecFile::addDataFilePrivate(const DataFileTypeEnum::Enum dataFileType, const StructureEnum::Enum structure, const AString& filename, const bool fileLoadingSelectionStatus, const bool fileSavingSelectionStatus, const bool specFileMemberStatus) { if ( ! SpecFile::isDataFileTypeAllowedInSpecFile(dataFileType)) { return NULL; } AString name = filename; const bool dataFileOnNetwork = DataFile::isFileOnNetwork(name); //NOTE: the spec file's location is completely irrelevant to this operation! if (!dataFileOnNetwork) { FileInformation fileInfo(name); if (fileInfo.isRelative()) { name = fileInfo.getAbsoluteFilePath(); } }//if it is on the network, don't modify it // const AString message = ("After adding, " // + filename // + " becomes " // + name); // CaretLogFine(message); // if (this->getFileName().isEmpty() == false) { // name = SystemUtilities::relativePath(name, FileInformation(this->getFileName()).getPathName()); // } for (std::vector::const_iterator iter = dataFileTypeGroups.begin(); iter != dataFileTypeGroups.end(); iter++) { SpecFileDataFileTypeGroup* dataFileTypeGroup = *iter; if (dataFileTypeGroup->getDataFileType() == dataFileType) { /* * If already in file, no need to add it a second time but do update * its selection status if new entry has file selected */ const int32_t numFiles = dataFileTypeGroup->getNumberOfFiles(); for (int32_t i = 0; i < numFiles; i++) { SpecFileDataFile* sfdf = dataFileTypeGroup->getFileInformation(i); if (sfdf->getFileName() == name) { if (fileLoadingSelectionStatus) { sfdf->setLoadingSelected(fileLoadingSelectionStatus); } if (fileSavingSelectionStatus) { sfdf->setSavingSelected(fileSavingSelectionStatus); } return sfdf; } } SpecFileDataFile* sfdf = new SpecFileDataFile(name, dataFileType, structure, specFileMemberStatus); sfdf->setLoadingSelected(fileLoadingSelectionStatus); sfdf->setSavingSelected(fileSavingSelectionStatus); dataFileTypeGroup->addFileInformation(sfdf); return sfdf; } } DataFileException e(getFileName(), "Data File Type: " + DataFileTypeEnum::toName(dataFileType) + " not allowed " + " for file " + filename); CaretLogThrowing(e); throw e; return NULL; // will never get here since exception thrown } ///** // * @return ALL of the connectivity file types (NEVER delete contents of returned vector. // */ //void //SpecFile::getAllConnectivityFileTypes(std::vector& connectivityDataFilesOut) //{ // connectivityDataFilesOut.clear(); // // for (std::vector::const_iterator iter = dataFileTypeGroups.begin(); // iter != dataFileTypeGroups.end(); // iter++) { // SpecFileDataFileTypeGroup* dataFileTypeGroup = *iter; // if (DataFileTypeEnum::isConnectivityDataType(dataFileTypeGroup->getDataFileType())) { // const int32_t numFiles = dataFileTypeGroup->getNumberOfFiles(); // for (int32_t i = 0; i < numFiles; i++) { // connectivityDataFilesOut.push_back(dataFileTypeGroup->getFileInformation(i)); // } // } // } //} /** * Set the selection status of a data file. * @param dataFileTypeName * Name of type of data file. * @param structure * Name of Structure of data file (not all files use structure). * @param filename * Name of the file. * @param fileSelectionStatus * Selection status of file. */ void SpecFile::setFileLoadingSelectionStatus(const DataFileTypeEnum::Enum dataFileType, const StructureEnum::Enum structure, const AString& filename, const bool fileSelectionStatus) { for (std::vector::const_iterator iter = dataFileTypeGroups.begin(); iter != dataFileTypeGroups.end(); iter++) { SpecFileDataFileTypeGroup* dataFileTypeGroup = *iter; if (dataFileTypeGroup->getDataFileType() == dataFileType) { const int32_t numFiles = dataFileTypeGroup->getNumberOfFiles(); for (int32_t i = 0; i < numFiles; i++) { SpecFileDataFile* sfdf = dataFileTypeGroup->getFileInformation(i); if (sfdf->getStructure() == structure) { if (sfdf->getFileName().endsWith(filename)) { sfdf->setLoadingSelected(fileSelectionStatus); } } } } } } /** * Add a data file to this spec file. * @param dataFileTypeName * Name of type of data file. * @param structure * Name of Structure of data file (not all files use structure). * @param filename * Name of the file. * @param fileLoadingSelectionStatus * Selection status for loading of file. * @param fileSavingSelectionStatus * Selection status for saving of file. * @param specFileMemberStatus * True if the file is a member of the spec file and is written * into the spec file. * * @throws DataFileException * If data file type is UNKNOWN. */ void SpecFile::addDataFile(const AString& dataFileTypeName, const AString& structureName, const AString& filename, const bool fileLoadingSelectionStatus, const bool fileSavingSelectionStatus, const bool specFileMemberStatus) { bool validType = false; DataFileTypeEnum::Enum dataFileType = DataFileTypeEnum::fromName(dataFileTypeName, &validType); bool validStructure = false; StructureEnum::Enum structure = StructureEnum::fromGuiName(structureName, &validStructure); this->addDataFilePrivate(dataFileType, structure, filename, fileLoadingSelectionStatus, fileSavingSelectionStatus, specFileMemberStatus); } /** * Clear the file.. */ void SpecFile::clear() { DataFile::clear(); this->clearData(); } /** * Clear the spec file's data as if there were no files loaded. */ void SpecFile::clearData() { this->metadata->clear(); /* * Do not clear the vector, just remove file information from all types */ for (std::vector::const_iterator iter = dataFileTypeGroups.begin(); iter != dataFileTypeGroups.end(); iter++) { SpecFileDataFileTypeGroup* dataFileTypeGroup = *iter; dataFileTypeGroup->removeAllFileInformation(); } } /** * Is this file empty? * * @return true if file is empty, else false. */ bool SpecFile::isEmpty() const { return (this->getNumberOfFiles() <= 0); } /** * @return The number of files. */ int32_t SpecFile::getNumberOfFiles() const { int count = 0; for (std::vector::const_iterator iter = dataFileTypeGroups.begin(); iter != dataFileTypeGroups.end(); iter++) { SpecFileDataFileTypeGroup* dataFileTypeGroup = *iter; count += dataFileTypeGroup->getNumberOfFiles(); } return count; } /** * @return The number of files selected for loading. */ int32_t SpecFile::getNumberOfFilesSelectedForLoading() const { int count = 0; for (std::vector::const_iterator iter = dataFileTypeGroups.begin(); iter != dataFileTypeGroups.end(); iter++) { SpecFileDataFileTypeGroup* dataFileTypeGroup = *iter; count += dataFileTypeGroup->getNumberOfFilesSelectedForLoading(); } return count; } /** * @return The number of files selected for saving. */ int32_t SpecFile::getNumberOfFilesSelectedForSaving() const { int count = 0; for (std::vector::const_iterator iter = dataFileTypeGroups.begin(); iter != dataFileTypeGroups.end(); iter++) { SpecFileDataFileTypeGroup* dataFileTypeGroup = *iter; count += dataFileTypeGroup->getNumberOfFilesSelectedForSaving(); } return count; } /** * @return True if there is at least one file with a remote path * (http...) AND the file is selected for loading. */ bool SpecFile::hasFilesWithRemotePathSelectedForLoading() const { for (std::vector::const_iterator iter = dataFileTypeGroups.begin(); iter != dataFileTypeGroups.end(); iter++) { const SpecFileDataFileTypeGroup* dataFileTypeGroup = *iter; const int32_t numFiles = dataFileTypeGroup->getNumberOfFiles(); for (int32_t i = 0; i < numFiles; i++) { const SpecFileDataFile* sfdf = dataFileTypeGroup->getFileInformation(i); if (sfdf->isLoadingSelected()) { if (DataFile::isFileOnNetwork(sfdf->getFileName())) { return true; } } } } return false; } /** * @return A vector containing all file names selected for loading. */ std::vector SpecFile::getAllDataFileNamesSelectedForLoading() const { std::vector allFileNames; for (std::vector::const_iterator iter = dataFileTypeGroups.begin(); iter != dataFileTypeGroups.end(); iter++) { SpecFileDataFileTypeGroup* dataFileTypeGroup = *iter; const int32_t numFiles = dataFileTypeGroup->getNumberOfFiles(); for (int32_t i = 0; i < numFiles; i++) { if (dataFileTypeGroup->getFileInformation(i)->isLoadingSelected()) { const AString filename = dataFileTypeGroup->getFileInformation(i)->getFileName(); allFileNames.push_back(filename); } } } return allFileNames; } /** * @return A vector containing all file names. */ std::vector SpecFile::getAllDataFileNames() const { std::vector allFileNames; for (std::vector::const_iterator iter = dataFileTypeGroups.begin(); iter != dataFileTypeGroups.end(); iter++) { SpecFileDataFileTypeGroup* dataFileTypeGroup = *iter; const int32_t numFiles = dataFileTypeGroup->getNumberOfFiles(); for (int32_t i = 0; i < numFiles; i++) { const AString filename = dataFileTypeGroup->getFileInformation(i)->getFileName(); allFileNames.push_back(filename); } } return allFileNames; } /** * @return True if the only files selected for loading are scene files. */ bool SpecFile::areAllFilesSelectedForLoadingSceneFiles() const { int32_t sceneFileCount = 0; int32_t allFilesCount = 0; for (std::vector::const_iterator iter = dataFileTypeGroups.begin(); iter != dataFileTypeGroups.end(); iter++) { SpecFileDataFileTypeGroup* dataFileTypeGroup = *iter; allFilesCount += dataFileTypeGroup->getNumberOfFilesSelectedForLoading(); if (dataFileTypeGroup->getDataFileType() == DataFileTypeEnum::SCENE) { sceneFileCount += dataFileTypeGroup->getNumberOfFilesSelectedForLoading(); } } if (sceneFileCount > 0) { if (sceneFileCount == allFilesCount) { return true; } } return false; } /** * Remove any files that are not "in spec" and do not have an * associated caret data file. */ void SpecFile::removeAnyFileInformationIfNotInSpecAndNoCaretDataFile() { for (std::vector::const_iterator iter = dataFileTypeGroups.begin(); iter != dataFileTypeGroups.end(); iter++) { SpecFileDataFileTypeGroup* dataFileTypeGroup = *iter; dataFileTypeGroup->removeFileInformationIfNotInSpecAndNoCaretDataFile(); } setModified(); } /** * Read the file. * * @param filenameIn * Name of file to read. * * @throws DataFileException * If there is an error reading the file. */ void SpecFile::readFile(const AString& filenameIn) { clear(); AString filename = filenameIn; if (DataFile::isFileOnNetwork(filename) == false) { FileInformation specInfo(filename); filename = specInfo.getAbsoluteFilePath(); } this->setFileName(filename); checkFileReadability(filename); SpecFileSaxReader saxReader(this); std::auto_ptr parser(XmlSaxParser::createXmlParser()); try { parser->parseFile(filename, &saxReader); } catch (const XmlSaxParserException& e) { clear(); this->setFileName(""); int lineNum = e.getLineNumber(); int colNum = e.getColumnNumber(); AString msg = "Parse Error while reading:"; if ((lineNum >= 0) && (colNum >= 0)) { msg += (" line/col (" + AString::number(e.getLineNumber()) + "/" + AString::number(e.getColumnNumber()) + ")"); } msg += (": " + e.whatString()); DataFileException dfe(filename, msg); CaretLogThrowing(dfe); throw dfe; } this->setFileName(filename); this->setAllFilesSelectedForLoading(true); this->setAllFilesSelectedForSaving(false); this->clearModified(); } /** * Read the spec file from a string containing the files content. * @param string * String containing the file's content. * @throws DataFileException * If there is an error reading the file from the string. */ void SpecFile::readFileFromString(const AString& string) { SpecFileSaxReader saxReader(this); std::auto_ptr parser(XmlSaxParser::createXmlParser()); try { parser->parseString(string, &saxReader); } catch (const XmlSaxParserException& e) { clear(); this->setFileName(""); int lineNum = e.getLineNumber(); int colNum = e.getColumnNumber(); AString msg = "Parse Error while reading Spec File from string."; if ((lineNum >= 0) && (colNum >= 0)) { msg += (" line/col (" + AString::number(e.getLineNumber()) + "/" + AString::number(e.getColumnNumber()) + ")"); } msg += (": " + e.whatString()); DataFileException dfe(getFileName(), msg); CaretLogThrowing(dfe); throw dfe; } this->clearModified(); } /** * Write the file. * * @param filename * Name of file to read. * * @throws DataFileException * If there is an error writing the file. */ void SpecFile::writeFile(const AString& filename) { if (!(filename.endsWith(".spec") || filename.endsWith(".wb_spec"))) { CaretLogWarning("spec file '" + filename + "' should be saved ending in .spec"); } checkFileWritability(filename); FileInformation specInfo(filename); AString absFileName = specInfo.getAbsoluteFilePath(); this->setFileName(absFileName); try { // // Format the version string so that it ends with at most one zero // const AString versionString = AString::number(1.0); // // Open the file // FileAdapter file; AString errorMessage; QTextStream* textStream = file.openQTextStreamForWritingFile(this->getFileName(), errorMessage); if (textStream == NULL) { throw DataFileException(getFileName(), errorMessage); } // // Create the xml writer // XmlWriter xmlWriter(*textStream); /* * Write the XML and include metadata */ this->writeFileContentToXML(xmlWriter, WRITE_META_DATA_YES, WRITE_IN_SPEC_FILES); file.close(); this->clearModified(); } catch (const GiftiException& e) { throw DataFileException(getFileName(), e.whatString()); } catch (const XmlException& e) { throw DataFileException(getFileName(), e.whatString()); } } /** * Write the file's content to the XML Writer. * @param xmlWriter * XML Writer to which file content is written. * @param writeMetaDataStatus * Yes of no to write metadata. * @throws DataFileException * If there is an error writing to the XML writer. */ void SpecFile::writeFileContentToXML(XmlWriter& xmlWriter, const WriteMetaDataType writeMetaDataStatus, const WriteFilesSelectedType writeFilesSelectedStatus) { // // Write header info // xmlWriter.writeStartDocument("1.0"); // // Write GIFTI root element // XmlAttributes attributes; //attributes.addAttribute("xmlns:xsi", // "http://www.w3.org/2001/XMLSchema-instance"); //attributes.addAttribute("xsi:noNamespaceSchemaLocation", // "http://brainvis.wustl.edu/caret6/xml_schemas/GIFTI_Caret.xsd"); attributes.addAttribute(SpecFile::XML_ATTRIBUTE_VERSION, SpecFile::getFileVersionAsString()); xmlWriter.writeStartElement(SpecFile::XML_TAG_SPEC_FILE, attributes); // // Write Metadata // if (writeMetaDataStatus == WRITE_META_DATA_YES) { if (metadata != NULL) { metadata->writeAsXML(xmlWriter); } } // // Write files // const int32_t numGroups = this->getNumberOfDataFileTypeGroups(); for (int32_t i = 0; i < numGroups; i++) { SpecFileDataFileTypeGroup* group = this->getDataFileTypeGroupByIndex(i); const int32_t numFiles = group->getNumberOfFiles(); for (int32_t j = 0; j < numFiles; j++) { SpecFileDataFile* file = group->getFileInformation(j); bool writeIt = true; switch (writeFilesSelectedStatus) { case WRITE_ALL_FILES: break; case WRITE_IN_SPEC_FILES: writeIt = file->isSpecFileMember(); break; } if (writeIt) { const AString name = updateFileNameAndPathForWriting(file->getFileName()); XmlAttributes atts; atts.addAttribute(SpecFile::XML_ATTRIBUTE_STRUCTURE, StructureEnum::toGuiName(file->getStructure())); atts.addAttribute(SpecFile::XML_ATTRIBUTE_DATA_FILE_TYPE, DataFileTypeEnum::toName(group->getDataFileType())); atts.addAttribute(SpecFile::XML_ATTRIBUTE_SELECTED, file->isLoadingSelected()); xmlWriter.writeStartElement(SpecFile::XML_TAG_DATA_FILE, atts); xmlWriter.writeCharacters(" " + name + "\n"); xmlWriter.writeEndElement(); } } } xmlWriter.writeEndElement(); xmlWriter.writeEndDocument(); } /** * Update the file name for writing to a spec file * (makes file relative to spec file location). */ AString SpecFile::updateFileNameAndPathForWriting(const AString& dataFileNameIn) { AString dataFileName = dataFileNameIn; FileInformation fileInfo(dataFileName); if (fileInfo.isAbsolute()) { const AString specFileName = getFileName(); FileInformation specFileInfo(specFileName); if (specFileInfo.isAbsolute()) { const AString newPath = SystemUtilities::relativePath(fileInfo.getPathName(), specFileInfo.getPathName()); if (newPath.isEmpty()) { dataFileName = fileInfo.getFileName(); } else { dataFileName = (newPath + "/" + fileInfo.getFileName()); } } } AString message = ("When writing, " + dataFileNameIn + " becomes " + dataFileName); CaretLogFine(message); return dataFileName; } /** * Write the file to a XML string. * @param writeMetaDataStatus * Write the metadata to the file. * @return * String containing XML. * @throws DataFileException * If error writing to XML. */ //AString //SpecFile::writeFileToString(const WriteMetaDataType writeMetaDataStatus, // const WriteFilesSelectedType writeFilesSelectedStatus) //{ // /* // * Create a TextStream that writes to a string. // */ // AString xmlString; // QTextStream textStream(&xmlString); // // /* // * Create the xml writer // */ // XmlWriter xmlWriter(textStream); // // /* // * Write file to XML. // */ // this->writeFileContentToXML(xmlWriter, // writeMetaDataStatus, // writeFilesSelectedStatus); // // return xmlString; //} /** * Get information about this file's contents. * @return * Information about the file's contents. */ AString SpecFile::toString() const { AString info = "name=" + this->getFileName() + "\n"; info += this->metadata->toString() + "\n"; for (std::vector::const_iterator iter = dataFileTypeGroups.begin(); iter != dataFileTypeGroups.end(); iter++) { SpecFileDataFileTypeGroup* dataFileTypeGroup = *iter; info += (dataFileTypeGroup->toString() + "\n"); } return info; } /** * @return The number of data file type groups. */ int32_t SpecFile::getNumberOfDataFileTypeGroups() const { return this->dataFileTypeGroups.size(); } /** * Get the data file type group for the given index. * @param dataFileTypeGroupIndex * Index of data file type group. * @return Data file type group at given index. */ SpecFileDataFileTypeGroup* SpecFile::getDataFileTypeGroupByIndex(const int32_t dataFileTypeGroupIndex) { CaretAssertVectorIndex(this->dataFileTypeGroups, dataFileTypeGroupIndex); return this->dataFileTypeGroups[dataFileTypeGroupIndex]; } /** * Get the data file type group for the given index. * @param dataFileTypeGroupIndex * Index of data file type group. * @return Data file type group at given index. */ const SpecFileDataFileTypeGroup* SpecFile::getDataFileTypeGroupByIndex(const int32_t dataFileTypeGroupIndex) const { CaretAssertVectorIndex(this->dataFileTypeGroups, dataFileTypeGroupIndex); return this->dataFileTypeGroups[dataFileTypeGroupIndex]; } /** * Get the data file type group for the given data file type. * @param dataFileType * Data file type requested. * @return Data file type group for requested data file type or * NULL if no matching item found. */ SpecFileDataFileTypeGroup* SpecFile::getDataFileTypeGroupByType(const DataFileTypeEnum::Enum dataFileType) const { for (std::vector::const_iterator iter = dataFileTypeGroups.begin(); iter != dataFileTypeGroups.end(); iter++) { SpecFileDataFileTypeGroup* dataFileTypeGroup = *iter; if (dataFileTypeGroup->getDataFileType() == dataFileType) { return dataFileTypeGroup; } } return NULL; } /** * Set all file's selection status for loading. * @param selected * New selection status for loading. */ void SpecFile::setAllFilesSelectedForLoading(bool selectionStatus) { for (std::vector::iterator iter = dataFileTypeGroups.begin(); iter != dataFileTypeGroups.end(); iter++) { SpecFileDataFileTypeGroup* dataFileTypeGroup = *iter; dataFileTypeGroup->setAllFilesSelectedForLoading(selectionStatus); } } /** * Set all file's selection status for saving. * @param selected * New selection status for saving. */ void SpecFile::setAllFilesSelectedForSaving(bool selectionStatus) { for (std::vector::iterator iter = dataFileTypeGroups.begin(); iter != dataFileTypeGroups.end(); iter++) { SpecFileDataFileTypeGroup* dataFileTypeGroup = *iter; dataFileTypeGroup->setAllFilesSelectedForSaving(selectionStatus); } } /** * Set all scene files selected and all other files not selected. */ void SpecFile::setAllSceneFilesSelectedForLoadingAndAllOtherFilesNotSelected() { for (std::vector::iterator iter = dataFileTypeGroups.begin(); iter != dataFileTypeGroups.end(); iter++) { SpecFileDataFileTypeGroup* dataFileTypeGroup = *iter; const bool selectionStatus = (dataFileTypeGroup->getDataFileType() == DataFileTypeEnum::SCENE); dataFileTypeGroup->setAllFilesSelectedForLoading(selectionStatus); } } /** * Set the save status to on for any files that are modified. */ void SpecFile::setModifiedFilesSelectedForSaving() { for (std::vector::iterator iter = dataFileTypeGroups.begin(); iter != dataFileTypeGroups.end(); iter++) { SpecFileDataFileTypeGroup* dataFileTypeGroup = *iter; dataFileTypeGroup->setModifiedFilesSelectedForSaving(); } } /** * Get the file information for the file with the given name. * * @param fileName * Name of the data file. * @return * File information for the file or NULL if not found. */ const SpecFileDataFile* SpecFile::getFileInfoFromFileName(const AString& fileName) const { for (std::vector::const_iterator iter = dataFileTypeGroups.begin(); iter != dataFileTypeGroups.end(); iter++) { SpecFileDataFileTypeGroup* dataFileTypeGroup = *iter; const int32_t numFiles = dataFileTypeGroup->getNumberOfFiles(); for (int32_t i = 0; i < numFiles; i++) { SpecFileDataFile* dataFileInfo = dataFileTypeGroup->getFileInformation(i); if (fileName == dataFileInfo->getFileName()) { return dataFileInfo; } } } return NULL; } /** * Transfer the "in spec" status for all data files from the * given spec file to this spec file. Files are matched by * data file type and absolute path file name. * * The spec file names MUST match, else no action is taken. * * @param specFile * Spec file from which in spec statuses are copied. */ void SpecFile::transferDataFilesInSpecStatus(const SpecFile& specFile) { if (getFileName() != specFile.getFileName()) { return; } for (std::vector::const_iterator iter = dataFileTypeGroups.begin(); iter != dataFileTypeGroups.end(); iter++) { SpecFileDataFileTypeGroup* dataFileTypeGroup = *iter; const int32_t numFiles = dataFileTypeGroup->getNumberOfFiles(); for (int32_t i = 0; i < numFiles; i++) { SpecFileDataFile* dataFileInfo = dataFileTypeGroup->getFileInformation(i); const AString fileName = dataFileInfo->getFileName(); FileInformation fileInfo(fileName); if (fileInfo.exists() && (fileInfo.isAbsolute())) { const SpecFileDataFile* copyFromDataFileInfo = specFile.getFileInfoFromFileName(fileName); if (copyFromDataFileInfo != NULL) { dataFileInfo->setSpecFileMember(copyFromDataFileInfo->isSpecFileMember()); } } } } } /** * @return The version of the file as a number. */ float SpecFile::getFileVersion() { return SpecFile::specFileVersion; } /** * @return The version of the file as a string. */ AString SpecFile::getFileVersionAsString() { return AString::number(SpecFile::specFileVersion, 'f', 1); } /** * Create a scene for an instance of a class. * * @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 the class' instance. * * @return Pointer to SceneClass object representing the state of * this object. Under some circumstances a NULL pointer may be * returned. Caller will take ownership of returned object. */ SceneClass* SpecFile::saveToScene(const SceneAttributes* sceneAttributes, const AString& instanceName) { SceneClass* sceneClass = new SceneClass(instanceName, "SpecFile", 1); AString specFileNameForScene; if (sceneAttributes->isSpecFileNameSavedToScene()) { specFileNameForScene = getFileName(); } const bool allLoadedFilesFlag = sceneAttributes->isAllLoadedFilesSavedToScene(); std::set displayedDataFiles; if ( ! allLoadedFilesFlag) { const std::vector tabIndicesForScene = sceneAttributes->getIndicesOfTabsForSavingToScene(); const std::vector windowIndicesForScene = sceneAttributes->getIndicesOfWindowsForSavingToScene(); EventGetDisplayedDataFiles displayedFilesEvent(windowIndicesForScene, tabIndicesForScene); EventManager::get()->sendEvent(displayedFilesEvent.getPointer()); displayedDataFiles = displayedFilesEvent.getDisplayedDataFiles(); } sceneClass->addPathName("specFileName", specFileNameForScene); std::vector dataFileClasses; // // Write files (except Scene and Palette files) // const int32_t numGroups = this->getNumberOfDataFileTypeGroups(); for (int32_t i = 0; i < numGroups; i++) { SpecFileDataFileTypeGroup* group = this->getDataFileTypeGroupByIndex(i); const DataFileTypeEnum::Enum dataFileType = group->getDataFileType(); if (dataFileType == DataFileTypeEnum::SCENE) { //CaretLogInfo("Note: Scene files not added to scene at this time"); } else if (dataFileType == DataFileTypeEnum::PALETTE) { CaretLogInfo("Note: Palette files not added to scene at this time"); } else { const int32_t numFiles = group->getNumberOfFiles(); for (int32_t j = 0; j < numFiles; j++) { SpecFileDataFile* file = group->getFileInformation(j); /* * Only write files that are loaded (indicated by its * "caretDataFile" not NULL. */ const CaretDataFile* caretDataFile = file->getCaretDataFile(); if (caretDataFile != NULL) { bool addFileToSceneFlag = false; if (allLoadedFilesFlag) { addFileToSceneFlag = true; } else { if (displayedDataFiles.find(caretDataFile) != displayedDataFiles.end()) { addFileToSceneFlag = true; } } if (addFileToSceneFlag) { SceneClass* fileClass = new SceneClass("specFileDataFile", "SpecFileDataFile", 1); fileClass->addEnumeratedType("dataFileType", dataFileType); fileClass->addEnumeratedType("structure", file->getStructure()); const AString name = updateFileNameAndPathForWriting(file->getFileName()); fileClass->addPathName("fileName", file->getFileName()); fileClass->addBoolean("selected", file->isLoadingSelected()); dataFileClasses.push_back(fileClass); } } } } } sceneClass->addChild(new SceneClassArray("dataFilesArray", dataFileClasses)); return sceneClass; } /** * Restore the state of an instance of a class. * * @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 containing the state that was previously * saved and should be restored. */ void SpecFile::restoreFromScene(const SceneAttributes* sceneAttributes, const SceneClass* sceneClass) { if (sceneClass == NULL) { return; } this->clear(); const AString specFileName = sceneClass->getPathNameValue("specFileName", ""); this->setFileName(specFileName); /* * If spec file name is path to a valid file, * load the spec file and then deselect all * of the files in the spec file. Since the * scene may contain a subset of the files in * the spec file, not doing this would result * in the spec file missing data file if the * user saves files after loading the scene. */ if (specFileName.isEmpty() == false) { FileInformation specFileInfo(specFileName); if (specFileInfo.exists()) { try { readFile(specFileName); } catch (const DataFileException& e) { sceneAttributes->addToErrorMessage("Error reading spec file " + specFileName + " for displaying scene: " + e.whatString()); } setAllFilesSelectedForLoading(false); setAllFilesSelectedForSaving(false); } } const SceneClassArray* dataFileClassArray = sceneClass->getClassArray("dataFilesArray"); if (dataFileClassArray != NULL) { const int32_t numberOfFiles = dataFileClassArray->getNumberOfArrayElements(); for (int32_t i = 0; i < numberOfFiles; i++) { const SceneClass* dataFileClass = dataFileClassArray->getClassAtIndex(i); const bool selectedForLoading = dataFileClass->getBooleanValue("selected"); const AString dataFileName = dataFileClass->getPathNameValue("fileName"); const DataFileTypeEnum::Enum dataFileType = dataFileClass->getEnumeratedTypeValue("dataFileType", DataFileTypeEnum::UNKNOWN); const StructureEnum::Enum structure = dataFileClass->getEnumeratedTypeValue("structure", StructureEnum::INVALID); this->addDataFile(dataFileType, structure, dataFileName, selectedForLoading, false, // not selected for saving true); } } } /** * Append content of a spec file to this spec file. * @param toAppend * Spec file that is appended. */ void SpecFile::appendSpecFile(const SpecFile& toAppend) { int numOtherGroups = (int)toAppend.dataFileTypeGroups.size(); AString otherDirectory = FileInformation(toAppend.getFileName()).getAbsolutePath();//hopefully the filename is already absolute, if it isn't and we changed directory, we can't recover the correct path if (!otherDirectory.endsWith('/'))//deal with the root directory { otherDirectory += "/"; } for (int i = 0; i < numOtherGroups; ++i) { const SpecFileDataFileTypeGroup* thisGroup = toAppend.dataFileTypeGroups[i]; int numOtherFiles = thisGroup->getNumberOfFiles(); for (int j = 0; j < numOtherFiles; ++j) { const SpecFileDataFile* fileData = thisGroup->getFileInformation(j); AString fileName = fileData->getFileName(); FileInformation fileInfo(fileName);//do not trust exists, we don't have the right working directory, this is ONLY to check whether the path is absolute if (fileInfo.isRelative()) { fileName = otherDirectory + fileName;//don't trust the file to exist from current directory, use string manipulation only } addDataFile(thisGroup->getDataFileType(), fileData->getStructure(), fileName, fileData->isLoadingSelected(), fileData->isSavingSelected(), fileData->isSpecFileMember());//absolute paths should get converted to relative on writing } } } /** * Set this file modified. */ void SpecFile::setModified() { CaretDataFile::setModified(); } /** * @return true if this file has been modified. */ bool SpecFile::isModified() const { if (CaretDataFile::isModified()) { return true; } for (std::vector::const_iterator iter = dataFileTypeGroups.begin(); iter != dataFileTypeGroups.end(); iter++) { const SpecFileDataFileTypeGroup* dataFileTypeGroup = *iter; if (dataFileTypeGroup->isModified()) { return true; } } return false; } /** * Clear the modification status. */ void SpecFile::clearModified() { CaretDataFile::clearModified(); for (std::vector::iterator iter = dataFileTypeGroups.begin(); iter != dataFileTypeGroups.end(); iter++) { SpecFileDataFileTypeGroup* dataFileTypeGroup = *iter; dataFileTypeGroup->clearModified(); } } /** * Add information about the file to the data file information. * * @param dataFileInformation * Consolidates information about a data file. */ void SpecFile::addToDataFileContentInformation(DataFileContentInformation& dataFileInformation) { CaretDataFile::addToDataFileContentInformation(dataFileInformation); const int32_t numberOfFiles = getNumberOfFiles(); if (numberOfFiles <= 0) { return; } int32_t columnCounter = 0; const int32_t COL_TYPE = columnCounter++; const int32_t COL_STRUCTURE = columnCounter++; const int32_t COL_NAME = columnCounter++; const int32_t COL_PATH = columnCounter++; StringTableModel table((numberOfFiles + 1), columnCounter); int32_t rowIndex = 0; table.setElement(rowIndex, COL_TYPE, "TYPE"); table.setElement(rowIndex, COL_STRUCTURE, "STRUCTURE"); table.setElement(rowIndex, COL_NAME, "NAME"); table.setElement(rowIndex, COL_PATH, "PATH"); rowIndex++; table.setColumnAlignment(COL_TYPE, StringTableModel::ALIGN_LEFT); table.setColumnAlignment(COL_STRUCTURE, StringTableModel::ALIGN_LEFT); table.setColumnAlignment(COL_NAME, StringTableModel::ALIGN_LEFT); table.setColumnAlignment(COL_PATH, StringTableModel::ALIGN_LEFT); for (std::vector::const_iterator iter = dataFileTypeGroups.begin(); iter != dataFileTypeGroups.end(); iter++) { SpecFileDataFileTypeGroup* dataFileTypeGroup = *iter; const DataFileTypeEnum::Enum dataFileType = dataFileTypeGroup->getDataFileType(); const int32_t numFiles = dataFileTypeGroup->getNumberOfFiles(); for (int32_t i = 0; i < numFiles; i++) { SpecFileDataFile* sfdf = dataFileTypeGroup->getFileInformation(i); FileInformation fileInfo(sfdf->getFileName()); table.setElement(rowIndex, COL_TYPE, DataFileTypeEnum::toGuiName(dataFileType)); table.setElement(rowIndex, COL_STRUCTURE, StructureEnum::toGuiName(sfdf->getStructure())); table.setElement(rowIndex, COL_NAME, fileInfo.getFileName()); table.setElement(rowIndex, COL_PATH, fileInfo.getPathName()); rowIndex++; } } dataFileInformation.addText("\n" + table.getInString()); } /** * Is the given file type allowed in the spec file? * * @param dataFileType * Type of data file. * @return * True if allowed in spec file, else false. */ bool SpecFile::isDataFileTypeAllowedInSpecFile(const DataFileTypeEnum::Enum dataFileType) { if (dataFileType == DataFileTypeEnum::CONNECTIVITY_DENSE_DYNAMIC) { return false; } return true; } connectome-workbench-1.2.3+git41-gc4c6c90/src/Files/SpecFile.h000066400000000000000000000206311300200146000234530ustar00rootroot00000000000000#ifndef __SPEC_FILE_H__ #define __SPEC_FILE_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 "CaretDataFile.h" #include "DataFileTypeEnum.h" #include "StructureEnum.h" namespace caret { class GiftiMetaData; class SpecFileDataFile; class SpecFileDataFileTypeGroup; class XmlWriter; class SpecFile : public CaretDataFile { public: SpecFile(); virtual ~SpecFile(); SpecFile(const SpecFile&); SpecFile& operator=(const SpecFile&); public: virtual void clear(); virtual bool isEmpty() const; virtual StructureEnum::Enum getStructure() const; virtual void setStructure(const StructureEnum::Enum structure); virtual GiftiMetaData* getFileMetaData(); virtual const GiftiMetaData* getFileMetaData() const; virtual void addToDataFileContentInformation(DataFileContentInformation& dataFileInformation); void addCaretDataFile(CaretDataFile* caretDataFile); void removeCaretDataFile(const CaretDataFile* caretDataFile); SpecFileDataFile* changeFileName(SpecFileDataFile* specFileDataFile, const AString& newFileName); void addDataFile(const DataFileTypeEnum::Enum dataFileType, const StructureEnum::Enum structure, const AString& filename, const bool fileLoadingSelectionStatus, const bool fileSavingSelectionStatus, const bool specFileMemberStatus); void addDataFile(const AString& dataFileTypeName, const AString& structureName, const AString& filename, const bool fileLoadingSelectionStatus, const bool fileSavingSelectionStatus, const bool specFileMemberStatus); void setFileLoadingSelectionStatus(const DataFileTypeEnum::Enum dataFileType, const StructureEnum::Enum structure, const AString& filename, const bool fileSelectionStatus); bool hasFilesWithRemotePathSelectedForLoading() const; int32_t getNumberOfFiles() const; int32_t getNumberOfFilesSelectedForLoading() const; int32_t getNumberOfFilesSelectedForSaving() const; std::vector getAllDataFileNames() const; std::vector getAllDataFileNamesSelectedForLoading() const; bool areAllFilesSelectedForLoadingSceneFiles() const; void removeAnyFileInformationIfNotInSpecAndNoCaretDataFile(); virtual void readFile(const AString& filename); virtual void writeFile(const AString& filename); virtual AString toString() const; AString getText() const; void transferDataFilesInSpecStatus(const SpecFile& specFile); int32_t getNumberOfDataFileTypeGroups() const; SpecFileDataFileTypeGroup* getDataFileTypeGroupByIndex(const int32_t dataFileTypeGroupIndex); const SpecFileDataFileTypeGroup* getDataFileTypeGroupByIndex(const int32_t dataFileTypeGroupIndex) const; SpecFileDataFileTypeGroup* getDataFileTypeGroupByType(const DataFileTypeEnum::Enum dataFileType) const; const SpecFileDataFile* getFileInfoFromFileName(const AString& fileName) const; // void getAllConnectivityFileTypes(std::vector& connectivityDataFiles); void setAllFilesSelectedForLoading(bool selectionStatus); void setAllFilesSelectedForSaving(bool selectionStatus); void setAllSceneFilesSelectedForLoadingAndAllOtherFilesNotSelected(); void setModifiedFilesSelectedForSaving(); static float getFileVersion(); static AString getFileVersionAsString(); virtual SceneClass* saveToScene(const SceneAttributes* sceneAttributes, const AString& instanceName); virtual void restoreFromScene(const SceneAttributes* sceneAttributes, const SceneClass* sceneClass); void appendSpecFile(const SpecFile& toAppend); virtual void setModified(); virtual bool isModified() const; virtual void clearModified(); static bool isDataFileTypeAllowedInSpecFile(const DataFileTypeEnum::Enum dataFileType); /** XML Tag for SpecFile element */ static const AString XML_TAG_SPEC_FILE; /** XML Tag for DataFile element */ static const AString XML_TAG_DATA_FILE; /** XML Tag for Structure attribute */ static const AString XML_ATTRIBUTE_STRUCTURE; /** XML Tag for DataFileType attribute */ static const AString XML_ATTRIBUTE_DATA_FILE_TYPE; /** XML Tag for data file selection status */ static const AString XML_ATTRIBUTE_SELECTED; /** XML Tag for Version attribute */ static const AString XML_ATTRIBUTE_VERSION; /** Version of this SpecFile */ static const float specFileVersion; private: enum WriteMetaDataType { WRITE_META_DATA_YES, WRITE_META_DATA_NO }; enum WriteFilesSelectedType { WRITE_ALL_FILES, WRITE_IN_SPEC_FILES }; void copyHelperSpecFile(const SpecFile& sf); void initializeSpecFile(); void clearData(); SpecFileDataFile* addDataFilePrivate(const DataFileTypeEnum::Enum dataFileType, const StructureEnum::Enum structure, const AString& filename, const bool fileLoadingSelectionStatus, const bool fileSavingSelectionStatus, const bool specFileMemberStatus); void readFileFromString(const AString& string); AString updateFileNameAndPathForWriting(const AString& dataFileName); // AString writeFileToString(const WriteMetaDataType writeMetaDataStatus, // const WriteFilesSelectedType writeFilesSelectedStatus); void writeFileContentToXML(XmlWriter& xmlWriter, const WriteMetaDataType writeMetaDataStatus, const WriteFilesSelectedType writeFilesSelectedStatus); std::vector dataFileTypeGroups; GiftiMetaData* metadata; }; #ifdef __SPEC_FILE_DEFINE__ const AString SpecFile::XML_TAG_SPEC_FILE = "CaretSpecFile"; const AString SpecFile::XML_TAG_DATA_FILE = "DataFile"; const AString SpecFile::XML_ATTRIBUTE_STRUCTURE = "Structure"; const AString SpecFile::XML_ATTRIBUTE_DATA_FILE_TYPE = "DataFileType"; const AString SpecFile::XML_ATTRIBUTE_SELECTED = "Selected"; const AString SpecFile::XML_ATTRIBUTE_VERSION = "Version"; const float SpecFile::specFileVersion = 1.0; #endif // __SPEC_FILE_DEFINE__ } // namespace #endif // __SPEC_FILE_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Files/SpecFileDataFile.cxx000066400000000000000000000156021300200146000254220ustar00rootroot00000000000000 /*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 __SPEC_FILE_GROUP_FILE_DECLARE__ #include "SpecFileDataFile.h" #undef __SPEC_FILE_GROUP_FILE_DECLARE__ #include "FileInformation.h" using namespace caret; /** * \class caret::SpecFileDataFile * \brief Name of file and its attributes including structure. * \ingroup Files * * Contains the name of a file and is attributes including * the file's structure. Note that not all files use a * structure. */ /** * Constructor. * * @param filename * Name of file. * @param dataFileType * Type of data file. * @param structure * Structure file represents (not all files have structure). * @param specFileMember * True if this file is a member of the spec file and is thus * written to the spec file. */ SpecFileDataFile::SpecFileDataFile(const AString& filename, const DataFileTypeEnum::Enum dataFileType, const StructureEnum::Enum structure, const bool specFileMember) : CaretObjectTracksModification() { m_caretDataFile = NULL; m_filename = filename; m_dataFileType = dataFileType; m_structure = structure; m_loadingSelected = true; m_savingSelected = false; m_specFileMember = specFileMember; } /** * Copy constructor. * @param sfdf * Object of this type that is copied. */ SpecFileDataFile::SpecFileDataFile(const SpecFileDataFile& sfdf) : CaretObjectTracksModification(sfdf) { copyHelper(sfdf); } /** * Assignment operator. * @parm sfdf * Object that is assigned to this object. * @return * Reference to this object. */ SpecFileDataFile& SpecFileDataFile::operator=(const SpecFileDataFile& sfdf) { if (this != &sfdf) { CaretObjectTracksModification::operator=(sfdf); copyHelper(sfdf); } return *this; } /** * Copy from the given object to this object. * @param sfdf * Object from which data is copied. */ void SpecFileDataFile::copyHelper(const SpecFileDataFile& sfdf) { m_caretDataFile = sfdf.m_caretDataFile; m_filename = sfdf.m_filename; m_dataFileType = sfdf.m_dataFileType; m_structure = sfdf.m_structure; m_loadingSelected = sfdf.m_loadingSelected; m_savingSelected = sfdf.m_savingSelected; m_specFileMember = sfdf.m_specFileMember; } /** * Destructor. */ SpecFileDataFile::~SpecFileDataFile() { } /** * @return The file's name; */ AString SpecFileDataFile::getFileName() const { if (m_caretDataFile != NULL) { m_filename = m_caretDataFile->getFileName(); } return m_filename; } void SpecFileDataFile::setFileName(const AString& fileName) { m_filename = fileName; if (m_caretDataFile != NULL) { m_caretDataFile->setFileName(fileName); } setModified(); } /** * @return The caret data file that is loaded for this spec file entry. * Will return NULL if the file has not been loaded. */ CaretDataFile* SpecFileDataFile::getCaretDataFile() { return m_caretDataFile; } /** * Set the caret data file for this spec file entry. * @param caretDataFile * The caret data file. */ void SpecFileDataFile::setCaretDataFile(CaretDataFile* caretDataFile) { m_caretDataFile = caretDataFile; } /** * @return The file's structure. */ StructureEnum::Enum SpecFileDataFile::getStructure() const { if (m_caretDataFile != NULL) { m_structure = m_caretDataFile->getStructure(); } return m_structure; } /** * Set the structure. * @param structure * New value for structure. */ void SpecFileDataFile::setStructure(const StructureEnum::Enum structure) { if (m_structure != structure) { m_structure = structure; setModified(); } } /** * @return The data file type for this group. */ DataFileTypeEnum::Enum SpecFileDataFile::getDataFileType() const { return m_dataFileType; } /** * @return The file's saving selection status. */ bool SpecFileDataFile::isSavingSelected() const { return m_savingSelected; } /** * Set the file's saving selection status. * DOES NOT alter modification status. * @param selected * New selection status. */ void SpecFileDataFile::setSavingSelected(const bool selected) { if (m_savingSelected != selected) { m_savingSelected = selected; } } /** * @return The file's loading selection status. */ bool SpecFileDataFile::isLoadingSelected() const { return m_loadingSelected; } /** * Set the file's loading selection status. * @param selected * New selection status. */ void SpecFileDataFile::setLoadingSelected(const bool selected) { // note: does not cause modification status to change m_loadingSelected = selected; } /** * Get a description of this object's content. * @return String describing this object's content. */ AString SpecFileDataFile::toString() const { const AString info = "name=" + m_filename + ", structure=" + StructureEnum::toGuiName(m_structure) + ", dataFileType=" + DataFileTypeEnum::toGuiName(m_dataFileType); //+ ", selected=" + AString::fromBool(m_selected); return info; } /** * @return True if this file is a selected as a member of the spec file. * If true, then the spec file is written, this file will be listed in * the spec file. */ bool SpecFileDataFile::isSpecFileMember() const { return m_specFileMember; } /** * Set this file is a selected as a member of the spec file using the given * status. * * @param status If true, then the spec file is written, this file will * be listed in the spec file. */ void SpecFileDataFile::setSpecFileMember(const bool status) { if (m_specFileMember != status) { m_specFileMember = status; setModified(); } } /** * @return True if the file "exists". * * If the file is on the file system, true is returned if the file exists, * else false. * * If the file is remote (http, ftp, etc), true is ALWAYS returned. */ bool SpecFileDataFile::exists() const { if (DataFile::isFileOnNetwork(getFileName())) { return true; } FileInformation fileInfo(getFileName()); if (fileInfo.exists()) { return true; } return false; } connectome-workbench-1.2.3+git41-gc4c6c90/src/Files/SpecFileDataFile.h000066400000000000000000000060161300200146000250460ustar00rootroot00000000000000#ifndef __SPEC_FILE_DATA_FILE_H__ #define __SPEC_FILE_DATA_FILE_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 "CaretDataFile.h" #include "CaretObjectTracksModification.h" #include "DataFileTypeEnum.h" #include "StructureEnum.h" namespace caret { class SpecFileDataFile : public CaretObjectTracksModification { public: SpecFileDataFile(const AString& filename, const DataFileTypeEnum::Enum dataFileType, const StructureEnum::Enum structure, const bool specFileMember); virtual ~SpecFileDataFile(); SpecFileDataFile(const SpecFileDataFile& sfdf); SpecFileDataFile& operator=(const SpecFileDataFile& sfdf); AString getFileName() const; void setFileName(const AString& fileName); CaretDataFile* getCaretDataFile(); void setCaretDataFile(CaretDataFile* caretDataFile); DataFileTypeEnum::Enum getDataFileType() const; StructureEnum::Enum getStructure() const; void setStructure(const StructureEnum::Enum structure); bool isLoadingSelected() const; void setLoadingSelected(const bool selected); bool isSavingSelected() const; void setSavingSelected(const bool selected); bool isSpecFileMember() const; void setSpecFileMember(const bool status); bool exists() const; public: virtual AString toString() const; private: SpecFileDataFile(); // not implemented void copyHelper(const SpecFileDataFile& sfdf); mutable AString m_filename; CaretDataFile* m_caretDataFile; mutable StructureEnum::Enum m_structure; DataFileTypeEnum::Enum m_dataFileType; bool m_loadingSelected; bool m_savingSelected; bool m_specFileMember; }; #ifdef __SPEC_FILE_GROUP_FILE_DECLARE__ // #endif // __SPEC_FILE_GROUP_FILE_DECLARE__ } // namespace #endif // __SPEC_FILE_DATA_FILE_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Files/SpecFileDataFileTypeGroup.cxx000066400000000000000000000207221300200146000273000ustar00rootroot00000000000000 /*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 __SPEC_FILE_GROUP_DECLARE__ #include "SpecFileDataFileTypeGroup.h" #undef __SPEC_FILE_GROUP_DECLARE__ #include "CaretAssert.h" #include "CaretMappableDataFile.h" #include "SpecFileDataFile.h" using namespace caret; /** * \class caret::SpecFileDataFileTypeGroup * \brief Groups files of the same DataFileType. * * Groups files of the same data type in a SpecFile. */ /** * Constructor. * @param dataFileType * Type of data file. */ SpecFileDataFileTypeGroup::SpecFileDataFileTypeGroup(const DataFileTypeEnum::Enum dataFileType) : CaretObjectTracksModification() { this->dataFileType = dataFileType; } /** * Destructor. */ SpecFileDataFileTypeGroup::~SpecFileDataFileTypeGroup() { this->removeAllFileInformation(); } /** * @return The data file type for this group. */ DataFileTypeEnum::Enum SpecFileDataFileTypeGroup::getDataFileType() const { return this->dataFileType; } /** * @return The number of files. */ int32_t SpecFileDataFileTypeGroup::getNumberOfFiles() const { return this->files.size(); } /** * @return The number of files selected for loading. */ int32_t SpecFileDataFileTypeGroup::getNumberOfFilesSelectedForLoading() const { int count = 0; for (std::vector::const_iterator iter = this->files.begin(); iter != this->files.end(); iter++) { SpecFileDataFile* file = *iter; if (file->isLoadingSelected()) { count++; } } return count; } /** * @return The number of files selected for saving. */ int32_t SpecFileDataFileTypeGroup::getNumberOfFilesSelectedForSaving() const { int count = 0; for (std::vector::const_iterator iter = this->files.begin(); iter != this->files.end(); iter++) { SpecFileDataFile* file = *iter; if (file->isSavingSelected()) { count++; } } return count; } /** * Add a file. * @param fileInformation * New file information. */ void SpecFileDataFileTypeGroup::addFileInformation(SpecFileDataFile* fileInformation) { CaretAssert(fileInformation); this->files.push_back(fileInformation); /* * Do not set the modfication status when a data file is added */ } /** * Remove file at given index. * @param fileIndex * Index of file for removal. */ void SpecFileDataFileTypeGroup::removeFileInformation(const int32_t fileIndex) { CaretAssertVectorIndex(this->files, fileIndex); delete this->files[fileIndex]; this->files.erase(this->files.begin() + fileIndex); setModified(); } /** * Remove any files that are not "in spec" and do not have an * associated caret data file. */ void SpecFileDataFileTypeGroup::removeFileInformationIfNotInSpecAndNoCaretDataFile() { const int32_t numFiles = getNumberOfFiles(); for (int32_t i = (numFiles - 1); i >= 0; i--) { SpecFileDataFile* sfdf = getFileInformation(i); if (sfdf->isSpecFileMember() == false) { if (sfdf->getCaretDataFile() == NULL) { removeFileInformation(i); } } } } /** * Remove all files. */ void SpecFileDataFileTypeGroup::removeAllFileInformation() { for (std::vector::iterator iter = this->files.begin(); iter != this->files.end(); iter++) { delete *iter; } this->files.clear(); setModified(); } /** * Get information for a file. * @param fileIndex * Index of file for which information is requested. * @return * Information for file. */ SpecFileDataFile* SpecFileDataFileTypeGroup::getFileInformation(const int32_t fileIndex) { CaretAssertVectorIndex(this->files, fileIndex); return this->files[fileIndex]; } /** * Get information for a file. * @param fileIndex * Index of file for which information is requested. * @return * Information for file. */ const SpecFileDataFile* SpecFileDataFileTypeGroup::getFileInformation(const int32_t fileIndex) const { CaretAssertVectorIndex(this->files, fileIndex); return this->files[fileIndex]; } /** * Set the loading selection status of all files. * @param selectionStatus * New loading selection status for all files. */ void SpecFileDataFileTypeGroup::setAllFilesSelectedForLoading(bool selectionStatus) { for (std::vector::iterator iter = this->files.begin(); iter != this->files.end(); iter++) { SpecFileDataFile* file = *iter; file->setLoadingSelected(selectionStatus); } } /** * Set the saving selection status of all files. * @param selectionStatus * New saving selection status for all files. */ void SpecFileDataFileTypeGroup::setAllFilesSelectedForSaving(bool selectionStatus) { for (std::vector::iterator iter = this->files.begin(); iter != this->files.end(); iter++) { SpecFileDataFile* file = *iter; file->setSavingSelected(selectionStatus); } } /** * Set the save status to on for any files that are modified. */ void SpecFileDataFileTypeGroup::setModifiedFilesSelectedForSaving() { for (std::vector::iterator iter = this->files.begin(); iter != this->files.end(); iter++) { SpecFileDataFile* file = *iter; CaretDataFile* cdf = file->getCaretDataFile(); if (cdf != NULL) { if (cdf->isModified()) { CaretMappableDataFile* mapFile = dynamic_cast(cdf); if (mapFile != NULL) { /* * For mappable data file, we only default saving to on when the modification * does not involve the color palette. Color palettes can be saved to a * scene so it not necessary to save the file when the palette is modified. * This prevents unintentional saving of the mappable data files, * especially when creating scenes. */ if (mapFile->isModifiedExcludingPaletteColorMapping()) { file->setSavingSelected(true); } } else { file->setSavingSelected(true); } } } } } /** * Get a description of this object's content. * @return String describing this object's content. */ AString SpecFileDataFileTypeGroup::toString() const { AString info = "DataFileType=" + DataFileTypeEnum::toName(this->dataFileType) + "\n"; for (std::vector::const_iterator iter = this->files.begin(); iter != this->files.end(); iter++) { SpecFileDataFile* file = *iter; info += (" " + file->toString() + "\n"); } return info; } /** * Set the status to unmodified. */ void SpecFileDataFileTypeGroup::clearModified() { CaretObjectTracksModification::clearModified(); for (std::vector::iterator iter = this->files.begin(); iter != this->files.end(); iter++) { SpecFileDataFile* sfdf = *iter; sfdf->clearModified(); } } /** * Is the object modified? * @return true if modified, else false. */ bool SpecFileDataFileTypeGroup::isModified() const { if (CaretObjectTracksModification::isModified()) { return true; } for (std::vector::const_iterator iter = this->files.begin(); iter != this->files.end(); iter++) { const SpecFileDataFile* sfdf = *iter; if (sfdf->isModified()) { return true; } } return false; } connectome-workbench-1.2.3+git41-gc4c6c90/src/Files/SpecFileDataFileTypeGroup.h000066400000000000000000000054631300200146000267320ustar00rootroot00000000000000#ifndef __SPEC_FILE_DATA_FILE_TYPE_GROUP_H__ #define __SPEC_FILE_DATA_FILE_TYPE_GROUP_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 "CaretObjectTracksModification.h" #include "DataFileTypeEnum.h" namespace caret { class SpecFileDataFile; class SpecFileDataFileTypeGroup : public CaretObjectTracksModification { public: SpecFileDataFileTypeGroup(const DataFileTypeEnum::Enum dataFileType); virtual ~SpecFileDataFileTypeGroup(); DataFileTypeEnum::Enum getDataFileType() const; int32_t getNumberOfFiles() const; int32_t getNumberOfFilesSelectedForLoading() const; int32_t getNumberOfFilesSelectedForSaving() const; void addFileInformation(SpecFileDataFile* fileInformation); void removeFileInformation(const int32_t fileIndex); void removeAllFileInformation(); void removeFileInformationIfNotInSpecAndNoCaretDataFile(); SpecFileDataFile* getFileInformation(const int32_t fileIndex); const SpecFileDataFile* getFileInformation(const int32_t fileIndex) const; void setAllFilesSelectedForLoading(bool selectionStatus); void setAllFilesSelectedForSaving(bool selectionStatus); void setModifiedFilesSelectedForSaving(); virtual void clearModified(); virtual bool isModified() const; private: SpecFileDataFileTypeGroup(const SpecFileDataFileTypeGroup&); SpecFileDataFileTypeGroup& operator=(const SpecFileDataFileTypeGroup&); public: virtual AString toString() const; private: std::vector files; DataFileTypeEnum::Enum dataFileType; }; #ifdef __SPEC_FILE_GROUP_DECLARE__ // #endif // __SPEC_FILE_GROUP_DECLARE__ } // namespace #endif // __SPEC_FILE_DATA_FILE_TYPE_GROUP_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Files/SpecFileSaxReader.cxx000066400000000000000000000223721300200146000256310ustar00rootroot00000000000000/*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 "CaretAssert.h" #include "CaretLogger.h" #include "DataFileException.h" #include "GiftiXmlElements.h" #include "SpecFile.h" #include "SpecFileSaxReader.h" #include "GiftiMetaDataSaxReader.h" #include "FileInformation.h" #include "XmlAttributes.h" #include "XmlException.h" using namespace caret; /** * constructor. */ SpecFileSaxReader::SpecFileSaxReader(SpecFile* specFileIn) { CaretAssert(specFileIn); this->specFile = specFileIn; this->state = STATE_NONE; this->stateStack.push(this->state); this->elementText = ""; this->metaDataSaxReader = NULL; } /** * destructor. */ SpecFileSaxReader::~SpecFileSaxReader() { } /** * start an element. */ void SpecFileSaxReader::startElement(const AString& namespaceURI, const AString& localName, const AString& qName, const XmlAttributes& attributes) { const STATE previousState = this->state; switch (this->state) { case STATE_NONE: if (qName == SpecFile::XML_TAG_SPEC_FILE) { this->state = STATE_SPEC_FILE; // // Check version of file being read // const float version = attributes.getValueAsFloat(SpecFile::XML_ATTRIBUTE_VERSION); if (version > SpecFile::getFileVersion()) { AString msg = "File version is " + AString::number(version) + " but versions newer than " + SpecFile::getFileVersionAsString() + " are not supported. Update your software."; XmlSaxParserException e(msg); CaretLogThrowing(e); throw e; } else if (version < 1.0) { AString msg = "File version is " + AString::number(version) + " but versions before" + SpecFile::getFileVersionAsString() + " are not supported. Update your software."; XmlSaxParserException e(msg); CaretLogThrowing(e); throw e; } } else if (qName == "Spec_File") { const AString msg = ("You are trying to read a Spec File from Caret5 " "that is incompatible with Workbench. Use the " "program wb_import (included in the " "Workbench distribution) to convert files from " "Caret5 formats to Workbench formats"); XmlSaxParserException e(msg); CaretLogThrowing(e); throw e; } else { const AString msg = "Root elements is " + qName + " but should be " + SpecFile::XML_TAG_SPEC_FILE; XmlSaxParserException e(msg); CaretLogThrowing(e); throw e; } break; case STATE_SPEC_FILE: if (qName == GiftiXmlElements::TAG_METADATA) { this->state = STATE_METADATA; this->metaDataSaxReader = new GiftiMetaDataSaxReader(this->specFile->getFileMetaData()); this->metaDataSaxReader->startElement(namespaceURI, localName, qName, attributes); } else if (qName == SpecFile::XML_TAG_DATA_FILE) { this->state = STATE_DATA_FILE; this->fileAttributeStructureName = attributes.getValue(SpecFile::XML_ATTRIBUTE_STRUCTURE); this->fileAttributeTypeName = attributes.getValue(SpecFile::XML_ATTRIBUTE_DATA_FILE_TYPE); this->fileAttributeSelectionStatus = attributes.getValueAsBoolean(SpecFile::XML_ATTRIBUTE_SELECTED, false); } else { const AString msg = "Invalid child of " + SpecFile::XML_TAG_SPEC_FILE + " is " + qName; XmlSaxParserException e(msg); CaretLogThrowing(e); throw e; } break; case STATE_METADATA: this->metaDataSaxReader->startElement(namespaceURI, localName, qName, attributes); break; case STATE_DATA_FILE: break; } // // Save previous state // stateStack.push(previousState); elementText = ""; } /** * end an element. */ void SpecFileSaxReader::endElement(const AString& namespaceURI, const AString& localName, const AString& qName) { switch (this->state) { case STATE_NONE: break; case STATE_SPEC_FILE: break; case STATE_METADATA: this->metaDataSaxReader->endElement(namespaceURI, localName, qName); if (qName == GiftiXmlElements::TAG_METADATA) { delete this->metaDataSaxReader; this->metaDataSaxReader = NULL; } break; case STATE_DATA_FILE: { try { const AString filename = this->elementText.trimmed(); AString fileNameForAdd; if (DataFile::isFileOnNetwork(filename))//keep on-network names as-is { fileNameForAdd = filename; } else {//otherwise, resolve relative to spec file if (DataFile::isFileOnNetwork(specFile->getFileName())) { const int32_t lastSlashIndex = specFile->getFileName().lastIndexOf("/"); if (lastSlashIndex >= 0) { fileNameForAdd = (specFile->getFileName().left(lastSlashIndex) + "/" + filename); } else { CaretAssert(0); } } else { FileInformation resolvePath(FileInformation(specFile->getFileName()).getAbsolutePath(), filename); fileNameForAdd = resolvePath.getAbsoluteFilePath(); } } this->specFile->addDataFile(this->fileAttributeTypeName, this->fileAttributeStructureName, fileNameForAdd, this->fileAttributeSelectionStatus, false, // not selected for saving true); // is a member of spec file since read from spec file this->fileAttributeTypeName = ""; this->fileAttributeStructureName = ""; this->fileAttributeSelectionStatus = false; } catch (const DataFileException& e) { throw XmlSaxParserException(e); } } break; } // // Clear out for new elements // this->elementText = ""; // // Go to previous state // if (this->stateStack.empty()) { throw XmlSaxParserException("State stack is empty while reading XML NiftDataFile."); } this->state = stateStack.top(); this->stateStack.pop(); } /** * get characters in an element. */ void SpecFileSaxReader::characters(const char* ch) { if (this->metaDataSaxReader != NULL) { this->metaDataSaxReader->characters(ch); } else { elementText += ch; } } /** * a fatal error occurs. */ void SpecFileSaxReader::fatalError(const XmlSaxParserException& e) { /* std::ostringstream str; str << "Fatal Error at line number: " << e.getLineNumber() << "\n" << "Column number: " << e.getColumnNumber() << "\n" << "Message: " << e.whatString(); if (errorMessage.isEmpty() == false) { str << "\n" << errorMessage; } errorMessage = str.str(); */ // // Stop parsing // throw e; } // a warning occurs void SpecFileSaxReader::warning(const XmlSaxParserException& e) { CaretLogWarning("XML Parser Warning: " + e.whatString()); } // an error occurs void SpecFileSaxReader::error(const XmlSaxParserException& e) { CaretLogSevere("XML Parser Error: " + e.whatString()); throw e; } void SpecFileSaxReader::startDocument() { } void SpecFileSaxReader::endDocument() { } connectome-workbench-1.2.3+git41-gc4c6c90/src/Files/SpecFileSaxReader.h000066400000000000000000000064111300200146000252520ustar00rootroot00000000000000 #ifndef __SPEC_FILE_SAX_READER_H__ #define __SPEC_FILE_SAX_READER_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 "XmlSaxParserException.h" #include "XmlSaxParserHandlerInterface.h" namespace caret { class SpecFile; class SpecFileDataFileTypeGroup; class GiftiMetaDataSaxReader; class XmlAttributes; class XmlException; /// class for reading a Spec File with a SAX Parser class SpecFileSaxReader : public CaretObject, public XmlSaxParserHandlerInterface { public: SpecFileSaxReader(SpecFile* specFileIn); virtual ~SpecFileSaxReader(); void startElement(const AString& namespaceURI, const AString& localName, const AString& qName, const XmlAttributes& attributes); void endElement(const AString& namspaceURI, const AString& localName, const AString& qName); void characters(const char* ch); void fatalError(const XmlSaxParserException& e); void warning(const XmlSaxParserException& e); void error(const XmlSaxParserException& e); void startDocument(); void endDocument(); protected: /// file reading states enum STATE { /// no state STATE_NONE, /// processing SpecFile tag STATE_SPEC_FILE, /// processing MetaData tag STATE_METADATA, /// processing DataFile tag STATE_DATA_FILE }; /// file reading state STATE state; /// the state stack used when reading a file std::stack stateStack; /// the error message AString errorMessage; /// Spec file that is being read SpecFile* specFile; /// element text AString elementText; /// GIFTI meta data sax reader GiftiMetaDataSaxReader* metaDataSaxReader; /** value of File Structure attribute */ AString fileAttributeStructureName; /** value of File type attribute */ AString fileAttributeTypeName; /** value of File selection status */ bool fileAttributeSelectionStatus; }; } // namespace #endif // __SPEC_FILE_SAX_READER_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Files/StudyMetaDataLink.cxx000066400000000000000000000205611300200146000256650ustar00rootroot00000000000000/*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 #define __STUDY_META_DATA_LINK_MAIN__ #include "StudyMetaDataLink.h" #undef __STUDY_META_DATA_LINK_MAIN__ #include "CaretLogger.h" #include "XmlWriter.h" using namespace caret; /** * \class caret::StudyMetaDataLink * \brief A link to study data. */ /** * constructor. */ StudyMetaDataLink::StudyMetaDataLink() { clear(); } /** * destructor. */ StudyMetaDataLink::~StudyMetaDataLink() { } /** * copy constructor. */ StudyMetaDataLink::StudyMetaDataLink(const StudyMetaDataLink& smdl) : CaretObject(smdl){ copyHelper(smdl); } /** * assignment opertator. */ StudyMetaDataLink& StudyMetaDataLink::operator=(const StudyMetaDataLink& smdl) { if (this != &smdl) { CaretObject::operator=(smdl); copyHelper(smdl); } return *this; } /** * copy helper. */ void StudyMetaDataLink::copyHelper(const StudyMetaDataLink& smdl) { m_pubMedID = smdl.m_pubMedID; m_tableNumber = smdl.m_tableNumber; m_tableSubHeaderNumber = smdl.m_tableSubHeaderNumber; m_figureNumber = smdl.m_figureNumber; m_panelNumberOrLetter = smdl.m_panelNumberOrLetter; m_pageReferencePageNumber = smdl.m_pageReferencePageNumber; m_pageReferenceSubHeaderNumber = smdl.m_pageReferenceSubHeaderNumber; } /** * equality operator. */ bool StudyMetaDataLink::operator==(const StudyMetaDataLink& smdl) const { const bool theSame = ((m_pubMedID == smdl.m_pubMedID) && (m_tableNumber == smdl.m_tableNumber) && (m_tableSubHeaderNumber == smdl.m_tableSubHeaderNumber) && (m_figureNumber == smdl.m_figureNumber) && (m_panelNumberOrLetter == smdl.m_panelNumberOrLetter) && (m_pageReferencePageNumber == smdl.m_pageReferencePageNumber) && (m_pageReferenceSubHeaderNumber == smdl.m_pageReferenceSubHeaderNumber)); return theSame; } /** * clear the link. */ void StudyMetaDataLink::clear() { m_pubMedID = "0"; m_tableNumber = ""; m_tableSubHeaderNumber = ""; m_figureNumber = ""; m_panelNumberOrLetter = ""; m_pageReferencePageNumber = ""; m_pageReferenceSubHeaderNumber = ""; } /** * set the table number (blank if invalid). */ void StudyMetaDataLink::setTableNumber(const AString& tn) { if (tn == "-1") { m_tableNumber = ""; } else { m_tableNumber = tn; } } /** * set the table sub header number (blank if invalid). */ void StudyMetaDataLink::setTableSubHeaderNumber(const AString& tshn) { if (tshn == "-1") { m_tableSubHeaderNumber = ""; } else { m_tableSubHeaderNumber = tshn; } } /** * set the panel letter/number (blank if invalid). */ void StudyMetaDataLink::setFigurePanelNumberOrLetter(const AString& pnl) { if (pnl == "-1") { m_panelNumberOrLetter = ""; } else { m_panelNumberOrLetter = pnl; } } /** * set the figure number (blank if invalid). */ void StudyMetaDataLink::setFigureNumber(const AString& fn) { if (fn == "-1") { m_figureNumber = ""; } else { m_figureNumber = fn; } } /** * set the page reference page number (blank if invalid). */ void StudyMetaDataLink::setPageReferencePageNumber(const AString& prpn) { if (prpn == "-1") { m_pageReferencePageNumber = ""; } else { m_pageReferencePageNumber = prpn; } } /** * set the page reference sub header number (blank if invalid). */ void StudyMetaDataLink::setPageReferenceSubHeaderNumber(const AString& tshn) { if (tshn == "-1") { m_pageReferenceSubHeaderNumber = ""; } else { m_pageReferenceSubHeaderNumber = tshn; } } /** * set element from text (used by SAX XML parser). */ void StudyMetaDataLink::setElementFromText(const AString& elementName, const AString& textValue) { if (elementName == XML_TAG_PUBMED_ID) { setPubMedID(textValue); } else if (elementName == XML_TAG_TABLE_NUMBER) { setTableNumber(textValue); } else if (elementName == XML_TAG_TABLE_SUB_HEADER_NUMBER) { setTableSubHeaderNumber(textValue); } else if (elementName == XML_TAG_FIGURE_NUMBER) { setFigureNumber(textValue); } else if (elementName == XML_TAG_PANEL_NUMBER_OR_LETTER) { setFigurePanelNumberOrLetter(textValue); } else if (elementName == XML_TAG_PAGE_REFERENCE_PAGE_NUMBER) { setPageReferencePageNumber(textValue); } else if (elementName == XML_TAG_PAGE_REFERENCE_SUB_HEADER_NUMBER) { setPageReferenceSubHeaderNumber(textValue); } else { CaretLogWarning("Unrecognized StudyMetaDataLink element ignored: " + elementName); } } /** * called to write XML. */ void StudyMetaDataLink::writeXML(XmlWriter& xmlWriter) const { xmlWriter.writeStartElement(XML_TAG_STUDY_META_DATA_LINK); xmlWriter.writeElementCData(XML_TAG_PUBMED_ID, m_pubMedID); xmlWriter.writeElementCData(XML_TAG_TABLE_NUMBER, m_tableNumber); xmlWriter.writeElementCData(XML_TAG_TABLE_SUB_HEADER_NUMBER, m_tableSubHeaderNumber); xmlWriter.writeElementCData(XML_TAG_FIGURE_NUMBER, m_figureNumber); xmlWriter.writeElementCData(XML_TAG_PANEL_NUMBER_OR_LETTER, m_panelNumberOrLetter); xmlWriter.writeElementCData(XML_TAG_PAGE_REFERENCE_PAGE_NUMBER, m_pageReferencePageNumber); xmlWriter.writeElementCData(XML_TAG_PAGE_REFERENCE_SUB_HEADER_NUMBER, m_pageReferenceSubHeaderNumber); xmlWriter.writeEndElement(); } /** * get the entire link in an "coded" text form. */ AString StudyMetaDataLink::getLinkAsCodedText() const { // // Assemble into one string containing key/value pairs separated by a semi-colon // QStringList sl; sl << ("pubMedID=" + m_pubMedID) << ("tableNumber=" + m_tableNumber) << ("tableSubHeaderNumber=" + m_tableSubHeaderNumber) << ("figureNumber=" + m_figureNumber) << ("panelNumberOrLetter=" + m_panelNumberOrLetter) << ("pageReferencePageNumber=" + m_pageReferencePageNumber) << ("pageReferenceSubHeaderNumber=" + m_pageReferenceSubHeaderNumber); const AString s = sl.join(";"); return s; } /** * set the link from "coded" text form. */ void StudyMetaDataLink::setLinkFromCodedText(const AString& txt) { // // Clear this link // clear(); // // Extract the key/value pairs that are separated by a semi-colon // const QStringList sl = txt.split(";", AString::SkipEmptyParts); for (int i = 0; i < sl.size(); i++) { const AString keyValueString = sl.at(i); // // Split with "=" into key/value pairs // const QStringList keyValueList = keyValueString.split("=", AString::SkipEmptyParts); if (keyValueList.size() == 2) { const AString key = keyValueList.at(0); const AString value = keyValueList.at(1).trimmed(); if (key == "pubMedID") { setPubMedID(value); } else if (key == "tableNumber") { setTableNumber(value); } else if (key == "tableSubHeaderNumber") { setTableSubHeaderNumber(value); } else if (key == "figureNumber") { setFigureNumber(value); } else if (key == "panelNumberOrLetter") { setFigurePanelNumberOrLetter(value); } else if (key == "pageReferencePageNumber") { setPageReferencePageNumber(value); } else if (key == "pageReferenceSubHeaderNumber") { setPageReferenceSubHeaderNumber(value); } else { std::cout << "Unrecognized StudyMetaDataLink key: " << key.toAscii().constData() << std::endl; } } } } connectome-workbench-1.2.3+git41-gc4c6c90/src/Files/StudyMetaDataLink.h000066400000000000000000000154621300200146000253160ustar00rootroot00000000000000 #ifndef __STUDY_META_DATA_LINK_H__ #define __STUDY_META_DATA_LINK_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 "XmlException.h" namespace caret { class XmlWriter; class StudyMetaDataLink : public CaretObject { public: // constructor StudyMetaDataLink(); // destructor ~StudyMetaDataLink(); // copy constructor StudyMetaDataLink(const StudyMetaDataLink& smdl); // assignment opertator StudyMetaDataLink& operator=(const StudyMetaDataLink& smdl); // equality operator bool operator==(const StudyMetaDataLink& smdl) const; // clear the link void clear(); /// get the PubMed ID (negative if project ID, zero if invalid) AString getPubMedID() const { return m_pubMedID; } /// set the PubMed ID (negative if project ID) void setPubMedID(const AString& pmid) { m_pubMedID = pmid; } /// get the table number (blank if invalid) AString getTableNumber() const { return m_tableNumber; } /// set the table number (blank if invalid) void setTableNumber(const AString& tn); /// get the table sub header number (blank if invalid) AString getTableSubHeaderNumber() const { return m_tableSubHeaderNumber; } /// set the table sub header number (blank if invalid) void setTableSubHeaderNumber(const AString& tshn); /// get the figure number (blank if invalid) AString getFigureNumber() const { return m_figureNumber; } /// set the figure number (blank if invalid) void setFigureNumber(const AString& fn); /// get the panel letter/number (blank if invalid) AString getFigurePanelNumberOrLetter() const { return m_panelNumberOrLetter; } /// set the panel letter/number (blank if invalid) void setFigurePanelNumberOrLetter(const AString& pnl); /// get the page reference page number (blank if invalid) AString getPageReferencePageNumber() const { return m_pageReferencePageNumber; } /// set the page reference page number (blank if invalid) void setPageReferencePageNumber(const AString& prpn); /// get the page reference sub header number (blank if invalid) AString getPageReferenceSubHeaderNumber() const { return m_pageReferenceSubHeaderNumber; } /// set the page reference sub header number (blank if invalid) void setPageReferenceSubHeaderNumber(const AString& tshn); /// get the entire link in an "coded" text form AString getLinkAsCodedText() const; /// set the link from "coded" text form void setLinkFromCodedText(const AString& txt); // called to write XML void writeXML(XmlWriter& xmlWriter) const; /// set element from text (used by SAX XML parser) void setElementFromText(const AString& elementName, const AString& textValue); // //----- tags for reading and writing // /// tag for reading and writing study metadata static const AString XML_TAG_STUDY_META_DATA_LINK; /// tag for reading and writing study metadata static const AString XML_TAG_PUBMED_ID; /// tag for reading and writing study metadata static const AString XML_TAG_TABLE_NUMBER; /// tag for reading and writing study metadata static const AString XML_TAG_TABLE_SUB_HEADER_NUMBER; /// tag for reading and writing study metadata static const AString XML_TAG_FIGURE_NUMBER; /// tag for reading and writing study metadata static const AString XML_TAG_PANEL_NUMBER_OR_LETTER; /// tag for reading and writing study metadata //static const AString tagPageNumber; /// tag for reading and writing study metadata static const AString XML_TAG_PAGE_REFERENCE_PAGE_NUMBER; /// tag for reading and writing study metadata static const AString XML_TAG_PAGE_REFERENCE_SUB_HEADER_NUMBER; protected: /// copy helper void copyHelper(const StudyMetaDataLink& smdl); /// the PubMed ID (negative if project ID, 0 if invalid) AString m_pubMedID; /// the table number (blank if invalid) AString m_tableNumber; /// the table sub header number (blank if invalid) AString m_tableSubHeaderNumber; /// the figure number (blank if invalid) AString m_figureNumber; /// the panel letter/number (blank if invalid) AString m_panelNumberOrLetter; /// page reference page number (blank if invalid) AString m_pageReferencePageNumber; /// page reference sub header number (blank if invalid) AString m_pageReferenceSubHeaderNumber; // NOTE: IF MEMBERS ADDED UPDATE THE COPY HELPER friend class StudyMetaDataLinkSet; }; #ifdef __STUDY_META_DATA_LINK_MAIN__ const AString StudyMetaDataLink::XML_TAG_STUDY_META_DATA_LINK = "StudyMetaDataLink"; const AString StudyMetaDataLink::XML_TAG_PUBMED_ID = "pubMedID"; const AString StudyMetaDataLink::XML_TAG_TABLE_NUMBER = "tableNumber"; const AString StudyMetaDataLink::XML_TAG_TABLE_SUB_HEADER_NUMBER = "tableSubHeaderNumber"; const AString StudyMetaDataLink::XML_TAG_FIGURE_NUMBER = "figureNumber"; const AString StudyMetaDataLink::XML_TAG_PANEL_NUMBER_OR_LETTER = "panelNumberOrLetter"; const AString StudyMetaDataLink::XML_TAG_PAGE_REFERENCE_PAGE_NUMBER = "pageReferencePageNumber"; const AString StudyMetaDataLink::XML_TAG_PAGE_REFERENCE_SUB_HEADER_NUMBER = "pageReferenceSubHeaderNumber"; #endif // __STUDY_META_DATA_LINK_MAIN__ } // namespace caret #endif // __STUDY_META_DATA_LINK_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Files/StudyMetaDataLinkSet.cxx000066400000000000000000000073561300200146000263500ustar00rootroot00000000000000 /*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 #define __STUDY_META_DATA_LINK_SET_MAIN__ #include "StudyMetaDataLinkSet.h" #undef __STUDY_META_DATA_LINK_SET_MAIN__ #include "XmlWriter.h" using namespace caret; /** * constructor. */ StudyMetaDataLinkSet::StudyMetaDataLinkSet() { clear(); } /** * destructor. */ StudyMetaDataLinkSet::~StudyMetaDataLinkSet() { clear(); } /** * add a StudyMetaDataLink. */ void StudyMetaDataLinkSet::addStudyMetaDataLink(const StudyMetaDataLink& smdl) { m_links.push_back(smdl); } /** * remove all links. */ void StudyMetaDataLinkSet::clear() { m_links.clear(); } /** * get a StudyMetaDataLink. */ StudyMetaDataLink StudyMetaDataLinkSet::getStudyMetaDataLink(const int indx) const { return m_links[indx]; } /** * get a pointer to a StudyMetaDataLink. */ StudyMetaDataLink* StudyMetaDataLinkSet::getStudyMetaDataLinkPointer(const int indx) { if ((indx >= 0) && (indx < getNumberOfStudyMetaDataLinks())) { return &m_links[indx]; } return NULL; } /** * set a study meta data link. */ void StudyMetaDataLinkSet::setStudyMetaDataLink(const int indx, const StudyMetaDataLink& smdl) { m_links[indx] = smdl; } /** * get all linked PubMed IDs. */ void StudyMetaDataLinkSet::getAllLinkedPubMedIDs(std::vector& pmidsOut) const { std::set pmidSet; const int num = getNumberOfStudyMetaDataLinks(); for (int i = 0; i < num; i++) { const AString pmid = getStudyMetaDataLink(i).getPubMedID(); pmidSet.insert(pmid); } pmidsOut.clear(); pmidsOut.insert(pmidsOut.end(), pmidSet.begin(), pmidSet.end()); } /** * remove a study meta data link. */ void StudyMetaDataLinkSet::removeStudyMetaDataLink(const int indx) { m_links.erase(m_links.begin() + indx); } /** * get the entire link set in an "coded" text form. */ AString StudyMetaDataLinkSet::getLinkSetAsCodedText() const { QStringList sl; const int num = getNumberOfStudyMetaDataLinks(); for (int i = 0; i < num; i++) { sl << getStudyMetaDataLink(i).getLinkAsCodedText(); } const AString s = sl.join(encodedTextLinkSeparator); return s; } /** * set the link set from "coded" text form. */ void StudyMetaDataLinkSet::setLinkSetFromCodedText(const AString& txt) { clear(); const QStringList sl = txt.split(encodedTextLinkSeparator, AString::SkipEmptyParts); for (int i = 0; i < sl.count(); i++) { StudyMetaDataLink smdl; smdl.setLinkFromCodedText(sl.at(i)); m_links.push_back(smdl); } } /** * called to write XML. */ void StudyMetaDataLinkSet::writeXML(XmlWriter& xmlWriter) const { xmlWriter.writeStartElement(XML_TAG_STUDY_META_DATA_LINK_SET); const int num = getNumberOfStudyMetaDataLinks(); for (int i = 0; i < num; i++) { StudyMetaDataLink smdl = getStudyMetaDataLink(i); smdl.writeXML(xmlWriter); } xmlWriter.writeEndElement(); } connectome-workbench-1.2.3+git41-gc4c6c90/src/Files/StudyMetaDataLinkSet.h000066400000000000000000000064331300200146000257700ustar00rootroot00000000000000 #ifndef __STUDY_META_DATA_LINK_SET_H__ #define __STUDY_META_DATA_LINK_SET_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 "CaretObject.h" #include "StudyMetaDataLink.h" #include "XmlException.h" namespace caret { class XmlWriter; /// class for accessing and storing a group of StudyMetaDataLink class StudyMetaDataLinkSet : public CaretObject { public: // constructor StudyMetaDataLinkSet(); // destructor ~StudyMetaDataLinkSet(); // add a StudyMetaDataLink void addStudyMetaDataLink(const StudyMetaDataLink& smdl); // remove all links void clear(); /// get the number of study meta data links int getNumberOfStudyMetaDataLinks() const { return m_links.size(); } // get a StudyMetaDataLink StudyMetaDataLink getStudyMetaDataLink(const int indx) const; // get a pointer to a StudyMetaDataLink StudyMetaDataLink* getStudyMetaDataLinkPointer(const int indx); // get all linked PubMed IDs void getAllLinkedPubMedIDs(std::vector& pmidsOut) const; // remove a study meta data link void removeStudyMetaDataLink(const int indx); // set a study meta data link void setStudyMetaDataLink(const int indx, const StudyMetaDataLink& smdl); /// get the entire link set in an "coded" text form AString getLinkSetAsCodedText() const; /// set the link set from "coded" text form void setLinkSetFromCodedText(const AString& txt); // called to write XML void writeXML(XmlWriter& xmlWriter) const; // //----- tags for reading and writing // /// tag for reading and writing study metadata static const AString XML_TAG_STUDY_META_DATA_LINK_SET; /// get the link separator for when stored as a string static const AString encodedTextLinkSeparator; protected: /// the StudyMetaDataLink std::vector m_links; friend class CellBase; }; #ifdef __STUDY_META_DATA_LINK_SET_MAIN__ const AString StudyMetaDataLinkSet::XML_TAG_STUDY_META_DATA_LINK_SET = "StudyMetaDataLinkSet"; const AString StudyMetaDataLinkSet::encodedTextLinkSeparator = ":::::"; #endif // __STUDY_META_DATA_LINK_SET_MAIN__ } // namespace #endif // __STUDY_META_DATA_LINK_SET_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Files/StudyMetaDataLinkSetSaxReader.cxx000066400000000000000000000123621300200146000301400ustar00rootroot00000000000000/*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 "CaretLogger.h" #include "StudyMetaDataLink.h" #include "StudyMetaDataLinkSet.h" #include "StudyMetaDataLinkSetSaxReader.h" #include "XmlAttributes.h" #include "XmlException.h" #include "XmlUtilities.h" using namespace caret; /** * constructor. */ StudyMetaDataLinkSetSaxReader::StudyMetaDataLinkSetSaxReader(StudyMetaDataLinkSet* studyMetaDataLinkSet) { m_state = STATE_NONE; m_stateStack.push(this->m_state); m_elementText = ""; m_studyMetaDataLinkSet = studyMetaDataLinkSet; m_studyMetaDataLinkBeingRead = NULL; } /** * destructor. */ StudyMetaDataLinkSetSaxReader::~StudyMetaDataLinkSetSaxReader() { } /** * start an element. */ void StudyMetaDataLinkSetSaxReader::startElement(const AString& /* namespaceURI */, const AString& /* localName */, const AString& qName, const XmlAttributes& /*attributes*/) { const STATE previousState = m_state; switch (m_state) { case STATE_NONE: if (qName == StudyMetaDataLinkSet::XML_TAG_STUDY_META_DATA_LINK_SET) { m_state = STATE_STUDY_META_DATA_LINK_SET; } else { AString txt = XmlUtilities::createInvalidRootElementMessage(StudyMetaDataLinkSet::XML_TAG_STUDY_META_DATA_LINK_SET, qName); XmlSaxParserException e(txt); CaretLogThrowing(e); throw e; } break; case STATE_STUDY_META_DATA_LINK_SET: if (qName == StudyMetaDataLink::XML_TAG_STUDY_META_DATA_LINK) { m_state = STATE_STUDY_META_DATA_LINK; m_studyMetaDataLinkBeingRead = new StudyMetaDataLink(); } else { AString txt = XmlUtilities::createInvalidChildElementMessage(StudyMetaDataLink::XML_TAG_STUDY_META_DATA_LINK, qName); XmlSaxParserException e(txt); CaretLogThrowing(e); throw e; } break; case STATE_STUDY_META_DATA_LINK: break; } // // Save previous state // m_stateStack.push(previousState); m_elementText = ""; } /** * end an element. */ void StudyMetaDataLinkSetSaxReader::endElement(const AString& /* namspaceURI */, const AString& /* localName */, const AString& qName) { const AString text = m_elementText.trimmed(); switch (m_state) { case STATE_NONE: break; case STATE_STUDY_META_DATA_LINK_SET: if (m_studyMetaDataLinkBeingRead != NULL) { this->m_studyMetaDataLinkSet->addStudyMetaDataLink(*m_studyMetaDataLinkBeingRead); delete m_studyMetaDataLinkBeingRead; m_studyMetaDataLinkBeingRead = NULL; } break; case STATE_STUDY_META_DATA_LINK: if (qName != StudyMetaDataLink::XML_TAG_STUDY_META_DATA_LINK) { m_studyMetaDataLinkBeingRead->setElementFromText(qName, text); } break; } // // Clear out for new elements // m_elementText = ""; // // Go to previous state // if (m_stateStack.empty()) { throw XmlSaxParserException("State stack is empty while reading XML MetaData."); } m_state = m_stateStack.top(); m_stateStack.pop(); } /** * get characters in an element. */ void StudyMetaDataLinkSetSaxReader::characters(const char* ch) { m_elementText += ch; } /** * a fatal error occurs. */ void StudyMetaDataLinkSetSaxReader::fatalError(const XmlSaxParserException& e) { // // Stop parsing // CaretLogSevere("XML Parser Fatal Error: " + e.whatString()); throw e; } // a warning occurs void StudyMetaDataLinkSetSaxReader::warning(const XmlSaxParserException& e) { CaretLogWarning("XML Parser Warning: " + e.whatString()); } // an error occurs void StudyMetaDataLinkSetSaxReader::error(const XmlSaxParserException& e) { throw e; } void StudyMetaDataLinkSetSaxReader::startDocument() { } void StudyMetaDataLinkSetSaxReader::endDocument() { } connectome-workbench-1.2.3+git41-gc4c6c90/src/Files/StudyMetaDataLinkSetSaxReader.h000066400000000000000000000065551300200146000275740ustar00rootroot00000000000000 #ifndef __STUDY_META_DATA_LINK_SET_SAX_READER_H__ #define __STUDY_META_DATA_LINK_SET_SAX_READER_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" #include "XmlSaxParserException.h" #include "XmlSaxParserHandlerInterface.h" namespace caret { class StudyMetaDataLink; class StudyMetaDataLinkSet; class XmlAttributes; /** * class for reading StudyMetaDataLinkSet with a SAX Parser */ class StudyMetaDataLinkSetSaxReader : public CaretObject, public XmlSaxParserHandlerInterface { public: StudyMetaDataLinkSetSaxReader(StudyMetaDataLinkSet* studyMetaDataLinKSet); virtual ~StudyMetaDataLinkSetSaxReader(); void startElement(const AString& namespaceURI, const AString& localName, const AString& qName, const XmlAttributes& attributes); void endElement(const AString& namspaceURI, const AString& localName, const AString& qName); void characters(const char* ch); void fatalError(const XmlSaxParserException& e); void warning(const XmlSaxParserException& e); void error(const XmlSaxParserException& e); void startDocument(); void endDocument(); private: StudyMetaDataLinkSetSaxReader(const StudyMetaDataLinkSetSaxReader&); StudyMetaDataLinkSetSaxReader& operator=(const StudyMetaDataLinkSetSaxReader&); protected: /// file reading states enum STATE { /// no state STATE_NONE, /// processing SurfaceProjectedItem tags STATE_STUDY_META_DATA_LINK_SET, /// processing StudyMetaDataLink tags STATE_STUDY_META_DATA_LINK, }; /// file reading state STATE m_state; /// the state stack used when reading a file std::stack m_stateStack; /// the error message AString m_errorMessage; /// meta data name AString m_metaDataName; /// meta data value AString m_metaDataValue; /// element text AString m_elementText; /// GIFTI meta data being read StudyMetaDataLinkSet* m_studyMetaDataLinkSet; StudyMetaDataLink* m_studyMetaDataLinkBeingRead; }; } // namespace #endif // __STUDY_META_DATA_LINK_SET_SAX_READER_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Files/SurfaceFile.cxx000066400000000000000000001552611300200146000245340ustar00rootroot00000000000000/*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 "BoundingBox.h" #include "DataFileException.h" #include "DataFileTypeEnum.h" #include "SurfaceFile.h" #include "CaretAssert.h" #include "CaretLogger.h" #include "CaretOMP.h" #include "DataFileContentInformation.h" #include "DescriptiveStatistics.h" #include "FastStatistics.h" #include "EventSurfaceColoringInvalidate.h" #include "GiftiFile.h" #include "GiftiMetaDataXmlElements.h" #include "MathFunctions.h" #include "Matrix4x4.h" #include "Vector3D.h" #include "CaretPointLocator.h" #include "GeodesicHelper.h" #include "PlainTextStringBuilder.h" #include "SignedDistanceHelper.h" #include "TopologyHelper.h" using namespace caret; /** * Constructor. */ SurfaceFile::SurfaceFile() : GiftiTypeFile(DataFileTypeEnum::SURFACE) { m_skipSanityCheck = false;//NOTE: this is NOT in the initializeMembersSurfaceFile method, because that method gets used at the top of the validate function, //which is used by setNumberOfNodesAndTriangles, which temporarily puts it the triangles into an invalid state, which is why this flag exists this->initializeMembersSurfaceFile(); EventManager::get()->addEventListener(this, EventTypeEnum::EVENT_SURFACE_COLORING_INVALIDATE); } /** * Copy constructor. * * @param sf * Surface file that is copied. */ SurfaceFile::SurfaceFile(const SurfaceFile& sf) : GiftiTypeFile(sf), EventListenerInterface() { m_skipSanityCheck = false;//see above this->initializeMembersSurfaceFile(); this->copyHelperSurfaceFile(sf); EventManager::get()->addEventListener(this, EventTypeEnum::EVENT_SURFACE_COLORING_INVALIDATE); } /** * Assignment operator. * * @param sf * Surface file that is copied. * @return * This surface file with content replaced * by the SurfaceFile parameter. */ SurfaceFile& SurfaceFile::operator=(const SurfaceFile& sf) { if (this != &sf) { GiftiTypeFile::operator=(sf); this->copyHelperSurfaceFile(sf); } return *this; } /** * Destructor. */ SurfaceFile::~SurfaceFile() { EventManager::get()->removeAllEventsFromListener(this); if (this->boundingBox != NULL) { delete this->boundingBox; this->boundingBox = NULL; } this->invalidateNodeColoringForBrowserTabs(); } void SurfaceFile::writeFile(const AString& filename) { if (!filename.endsWith(".surf.gii")) { CaretLogWarning("surface file '" + filename + "' should be saved ending in .surf.gii, see wb_command -gifti-help"); } caret::GiftiTypeFile::writeFile(filename); } /** * Clear the surface file. */ void SurfaceFile::clear() { if (this->boundingBox != NULL) { delete this->boundingBox; this->boundingBox = NULL; } coordinateDataArray = NULL; coordinatePointer = NULL; triangleDataArray = NULL; trianglePointer = NULL; GiftiTypeFile::clear(); invalidateHelpers(); this->invalidateNodeColoringForBrowserTabs(); } /** * Runs topology helper creation in a thread */ class CreateTopologyHelperThread : public QThread { public: CreateTopologyHelperThread(SurfaceFile* surfaceFile) { this->surfaceFile = surfaceFile; } ~CreateTopologyHelperThread() { std::cout << "Delete topology helper for " << this->surfaceFile->getFileNameNoPath() << std::endl; } void run() { /* * NEED to prevent SurfaceFile destructor from completing until this is done * Perhaps before starting this thread, set a variable in SurfaceFile and * then have this method clear the variable after topology helper is created. */ this->surfaceFile->getTopologyHelper(); this->deleteLater(); } SurfaceFile* surfaceFile; }; /** * Validate the contents of the file after it * has been read such as correct number of * data arrays and proper data types/dimensions. */ void SurfaceFile::validateDataArraysAfterReading() { this->initializeMembersSurfaceFile(); int numDataArrays = this->giftiFile->getNumberOfDataArrays(); if (numDataArrays != 2) { throw DataFileException(getFileName(), "Number of data arrays MUST be two in a SurfaceFile."); } /* * Find the coordinate and topology data arrays. */ for (int i = 0; i < numDataArrays; i++) { GiftiDataArray* gda = this->giftiFile->getDataArray(i); if (gda->getIntent() == NiftiIntentEnum::NIFTI_INTENT_POINTSET) { if (gda->getDataType() == NiftiDataTypeEnum::NIFTI_TYPE_FLOAT32) { if (gda->getNumberOfDimensions() == 2) { int64_t dim0 = gda->getDimension(0); int64_t dim1 = gda->getDimension(1); if ((dim0 > 0) && (dim1 == 3)) { this->coordinateDataArray = gda; this->coordinatePointer = gda->getDataPointerFloat(); } } } } else if (gda->getIntent() == NiftiIntentEnum::NIFTI_INTENT_TRIANGLE) { if (gda->getDataType() == NiftiDataTypeEnum::NIFTI_TYPE_INT32) { if (gda->getNumberOfDimensions() == 2) { int64_t dim0 = gda->getDimension(0); int64_t dim1 = gda->getDimension(1); if ((dim0 > 0) && (dim1 == 3)) { this->triangleDataArray = gda; this->trianglePointer = gda->getDataPointerInt(); } } } } } AString errorMessage; if (this->coordinateDataArray == NULL) { errorMessage += "Unable to find coordinate data array which " " contains data type FLOAT32, Intent POINTSET, and two " " dimensions with the second dimension set to three. "; } if (this->triangleDataArray == NULL) { errorMessage += "Unable to find topology data array which " " contains data type INT32, Intent TRIANGLE, and two " " dimensions with the second dimension set to three."; } const int32_t numNodes = this->getNumberOfNodes(); if (!m_skipSanityCheck) { const int numTris = getNumberOfTriangles();//sanity check the triangle data array for (int i = 0; i < numTris; ++i) { const int32_t* thisTri = getTriangle(i); for (int j = 0; j < 3; ++j) { if (thisTri[j] < 0 || thisTri[j] >= numNodes) { errorMessage += "Invalid vertex in triangle array: triangle " + AString::number(i) + ", vertex " + AString::number(thisTri[j]); break; } for (int k = j + 1; k < 3; ++k) { if (thisTri[j] == thisTri[k]) { errorMessage += "Vertex used twice in one triangle: triangle " + AString::number(i) + ", vertex " + AString::number(thisTri[j]); break; } } } } if (errorMessage.isEmpty() == false) { throw DataFileException(getFileName(), errorMessage); } } this->computeNormals(); /* * Apply the first transformation matrix that transforms to * Talairach space. */ // Disable as FreeSurfer is inserting a matrix that causes issues in // HCP Pipeline // const AString talairachName = NiftiTransformEnum::toName(NiftiTransformEnum::NIFTI_XFORM_TALAIRACH); // for (int32_t im = 0; im < this->coordinateDataArray->getNumberOfMatrices(); im++) { // const Matrix4x4* m = this->coordinateDataArray->getMatrix(im); // if (m->getTransformedSpaceName() == talairachName) { // applyMatrix(*m); // break; // } // } /* * Create the topology helper in a thread so files dont' take * too long to read. */ // (new CreateTopologyHelperThread(this))->start(); } /** * Get the number of nodes. * * @return * The number of nodes. */ int32_t SurfaceFile::getNumberOfNodes() const { if (this->coordinatePointer == NULL) { return 0; } CaretAssert(this->coordinateDataArray); return this->coordinateDataArray->getDimension(0); } /** * Get the number of columns. * * @return * The number of columns. */ int32_t SurfaceFile::getNumberOfColumns() const { if (this->getNumberOfNodes() > 0) { return 1; } return 0; } void SurfaceFile::setNumberOfNodesAndTriangles(const int32_t& nodes, const int32_t& triangles) { if (this->boundingBox != NULL) { delete this->boundingBox; this->boundingBox = NULL; } coordinateDataArray = NULL; coordinatePointer = NULL; triangleDataArray = NULL; trianglePointer = NULL; giftiFile->clearAndKeepMetadata(); invalidateHelpers(); this->invalidateNodeColoringForBrowserTabs(); std::vector dims(2); dims[1] = 3; dims[0] = nodes; giftiFile->addDataArray(new GiftiDataArray(NiftiIntentEnum::NIFTI_INTENT_POINTSET, NiftiDataTypeEnum::NIFTI_TYPE_FLOAT32, dims, GiftiEncodingEnum::GZIP_BASE64_BINARY)); dims[0] = triangles; giftiFile->addDataArray(new GiftiDataArray(NiftiIntentEnum::NIFTI_INTENT_TRIANGLE, NiftiDataTypeEnum::NIFTI_TYPE_INT32, dims, GiftiEncodingEnum::GZIP_BASE64_BINARY)); m_skipSanityCheck = true; validateDataArraysAfterReading(); m_skipSanityCheck = false; setModified(); } /** * Get a coordinate. * * @param * nodeIndex of coordinate. * * @return * Pointer to memory containing the XYZ coordinate. */ const float* SurfaceFile::getCoordinate(const int32_t nodeIndex) const { CaretAssert(this->coordinatePointer); const int32_t offset = nodeIndex * 3; CaretAssert((offset >= 0) && (offset < (this->getNumberOfNodes() * 3))); return &(this->coordinatePointer[offset]); } /** * Get a coordinate. * * @param * nodeIndex of coordinate. * @param xyzOut * Will contain coordinate upon exit. */ void SurfaceFile::getCoordinate(const int32_t nodeIndex, float xyzOut[3]) const { CaretAssert(this->coordinatePointer); const int32_t offset = nodeIndex * 3; CaretAssert((offset >= 0) && (offset < (this->getNumberOfNodes() * 3))); xyzOut[0] = this->coordinatePointer[offset]; xyzOut[1] = this->coordinatePointer[offset+1]; xyzOut[2] = this->coordinatePointer[offset+2]; } const float* SurfaceFile::getCoordinateData() const { CaretAssert(this->coordinatePointer); return this->coordinatePointer; } void SurfaceFile::setCoordinate(const int32_t nodeIndex, const float xyzIn[3]) { setCoordinate(nodeIndex, xyzIn[0], xyzIn[1], xyzIn[2]); } void SurfaceFile::setCoordinate(const int32_t nodeIndex, const float xIn, const float yIn, const float zIn) { CaretAssert(this->coordinatePointer); const int32_t offset = nodeIndex * 3; CaretAssert((offset >= 0) && (offset < (this->getNumberOfNodes() * 3))); this->coordinatePointer[offset] = xIn; this->coordinatePointer[offset+1] = yIn; this->coordinatePointer[offset+2] = zIn; invalidateNormals(); invalidateHelpers(); setModified(); } void SurfaceFile::setCoordinates(const float *coordinates) { CaretAssert(this->coordinatePointer); memcpy(this->coordinatePointer, coordinates, 3 * sizeof(float) * getNumberOfNodes()); invalidateHelpers(); invalidateNormals(); //setModified(); } /** * Get the number of triangles. * * @return * Number of triangles. */ int SurfaceFile::getNumberOfTriangles() const { if (this->trianglePointer == NULL) { return 0; } CaretAssert(this->triangleDataArray); return this->triangleDataArray->getDimension(0); } /** * Get a triangle. * * @param indx * Index of triangle. * * @return * Pointer to memory containing the three nodes * in the triangle. */ const int32_t* SurfaceFile::getTriangle(const int32_t indx) const { CaretAssert(this->trianglePointer != NULL); CaretAssert((indx >= 0) && (indx < this->getNumberOfTriangles())); const int32_t offset = indx * 3; return &(this->trianglePointer[offset]); } void SurfaceFile::setTriangle(const int32_t& index, const int32_t* nodes) { setTriangle(index, nodes[0], nodes[1], nodes[2]); } void SurfaceFile::setTriangle(const int32_t& index, const int32_t& node1, const int32_t& node2, const int32_t& node3) { CaretAssert(trianglePointer != NULL); CaretAssert((index >= 0) && (index < getNumberOfTriangles())); const int32_t offset = index * 3; trianglePointer[offset] = node1; trianglePointer[offset + 1] = node2; trianglePointer[offset + 2] = node3; invalidateHelpers(); invalidateNormals(); setModified(); } /** * Find the triangle that has an edge formed by "n1" and "n2" * but its not "oppositeTriangle". * * @param n1 * First node in the edge. * @param n2 * Second node in the edge. * @param oppositeTriangle * Triangle that on opposite side of the edge. * @return * Index of triangle or -1 if not found. If not * found surface must be open/cut. */ int32_t SurfaceFile::getTriangleThatSharesEdge(const int32_t n1, const int32_t n2, const int32_t oppositeTriangle) const { CaretPointer topoHelp = getTopologyHelper(); /* * Get the triangles used by one of the nodes */ int32_t numTriangles = 0; const int32_t* triangles = topoHelp->getNodeTiles(n1, numTriangles); for (int32_t i = 0; i < numTriangles; i++) { const int32_t t = triangles[i]; if (t != oppositeTriangle) { const int* nodes = this->getTriangle(t); if ((n1 == nodes[0]) || (n1 == nodes[1]) || (n1 == nodes[2])) { if ((n2 == nodes[0]) || (n2 == nodes[1]) || (n2 == nodes[2])) { return t; } } } } return -1; } /** * Initialize members of this class. */ void SurfaceFile::initializeMembersSurfaceFile() { this->coordinateDataArray = NULL; this->coordinatePointer = NULL; this->triangleDataArray = NULL; this->trianglePointer = NULL; this->boundingBox = NULL; m_distHelperIndex = 0; m_geoHelperIndex = 0; m_topoHelperIndex = 0; m_normalsComputed = false; } /** * Helps copying files. * * @param sf * File that is copied. */ void SurfaceFile::copyHelperSurfaceFile(const SurfaceFile& /*sf*/) { this->validateDataArraysAfterReading(); } /** * Get a normal vector for a coordinate. * * @param * Index of coordinate. * * @return * Pointer to memory containing the normal vector. */ const float* SurfaceFile::getNormalVector(const int32_t nodeIndex) const { const int32_t offset = nodeIndex * 3; CaretAssert((offset >= 0) && (offset < static_cast(this->normalVectors.size()))); return &(this->normalVectors[offset]); } const float* SurfaceFile::getNormalData() const { return normalVectors.data(); } void SurfaceFile::invalidateNormals() { m_normalsComputed = false; } /** * Compute surface normals. */ void SurfaceFile::computeNormals() { if (m_normalsComputed)//don't recompute when not needed { return; } m_normalsComputed = true; int32_t numCoords = this->getNumberOfNodes(); if (numCoords > 0) { this->normalVectors.resize(numCoords * 3); } else { this->normalVectors.clear(); } const int32_t numTriangles = this->getNumberOfTriangles(); if ((numCoords > 0) && (numTriangles > 0)) { float* normalPointer = &this->normalVectors[0]; std::vector numContribute(numCoords, 0); float triangleNormal[3]; for (int32_t i = 0; i < numTriangles; i++) { const int32_t it3 = i * 3; const int n1 = this->trianglePointer[it3]; const int n2 = this->trianglePointer[it3 + 1]; const int n3 = this->trianglePointer[it3 + 2]; const int32_t c1 = n1 * 3; const int32_t c2 = n2 * 3; const int32_t c3 = n3 * 3; if ((n1 >= 0) && (n2 >= 0) && (n3 >= 0)) { MathFunctions::normalVector(&this->coordinatePointer[c1], &this->coordinatePointer[c2], &this->coordinatePointer[c3], triangleNormal); normalPointer[c1 + 0] += triangleNormal[0];//+= is not guaranteed to be atomic, do not parallelize normalPointer[c1 + 1] += triangleNormal[1]; normalPointer[c1 + 2] += triangleNormal[2]; numContribute[n1] += 1; normalPointer[c2 + 0] += triangleNormal[0]; normalPointer[c2 + 1] += triangleNormal[1]; normalPointer[c2 + 2] += triangleNormal[2]; numContribute[n2] += 1; normalPointer[c3 + 0] += triangleNormal[0]; normalPointer[c3 + 1] += triangleNormal[1]; normalPointer[c3 + 2] += triangleNormal[2]; numContribute[n3] += 1; } } for (int i = 0; i < numCoords; i++) { const int i3 = i * 3; if (numContribute[i] > 0) { MathFunctions::normalizeVector(normalPointer + i3); } else { normalPointer[i3 + 0] = 0.0f;//zero the normals for unconnected nodes normalPointer[i3 + 1] = 0.0f; normalPointer[i3 + 2] = 0.0f; } } } } std::vector SurfaceFile::computeAverageNormals() { computeNormals(); const float* normalPointer = getNormalData(); int numCoords = getNumberOfNodes(); std::vector ret(numCoords * 3); CaretPointer myTopoHelp = getTopologyHelper();//TODO: make this not circular - separate base that doesn't handle helpers (and is used by helpers) from file that handles helpers and normals? for (int i = 0; i < numCoords; ++i) { int i3 = i * 3; Vector3D accum; const std::vector& neighbors = myTopoHelp->getNodeNeighbors(i); int numNeigh = (int)neighbors.size(); for (int j = 0; j < numNeigh; ++j) { accum += normalPointer + neighbors[j] * 3; } Vector3D outVec = accum.normal(); ret[i3] = outVec[0]; ret[i3 + 1] = outVec[1]; ret[i3 + 2] = outVec[2]; } return ret; } /** * Get the normal vector for a triangle. * @param triangleIndex * Index of the triangle. * @param normalOut * Output containing the normal for the triangle. */ void SurfaceFile::getTriangleNormalVector(const int32_t triangleIndex, float normalOut[3]) const { const int32_t it3 = triangleIndex * 3; const int n1 = this->trianglePointer[it3]; const int n2 = this->trianglePointer[it3 + 1]; const int n3 = this->trianglePointer[it3 + 2]; const int32_t c1 = n1 * 3; const int32_t c2 = n2 * 3; const int32_t c3 = n3 * 3; MathFunctions::normalVector(&this->coordinatePointer[c1], &this->coordinatePointer[c2], &this->coordinatePointer[c3], normalOut); } /** * @return The type of this surface. */ SurfaceTypeEnum::Enum SurfaceFile::getSurfaceType() const { if (this->coordinateDataArray == NULL) { return SurfaceTypeEnum::UNKNOWN; } const AString geometricTypeName = this->coordinateDataArray->getMetaData()->get(GiftiMetaDataXmlElements::METADATA_NAME_GEOMETRIC_TYPE); SurfaceTypeEnum::Enum surfaceType = SurfaceTypeEnum::fromGiftiName(geometricTypeName, NULL); return surfaceType; } /** * Sets the type of this surface. * @param surfaceType * New type for surface. */ void SurfaceFile::setSurfaceType(const SurfaceTypeEnum::Enum surfaceType) { if (this->coordinateDataArray == NULL) { return; } const AString geometricTypeName = SurfaceTypeEnum::toGiftiName(surfaceType); this->coordinateDataArray->getMetaData()->set(GiftiMetaDataXmlElements::METADATA_NAME_GEOMETRIC_TYPE, geometricTypeName); } /** * @return The secondary type of this surface. */ SecondarySurfaceTypeEnum::Enum SurfaceFile::getSecondaryType() const { if (this->coordinateDataArray == NULL) { return SecondarySurfaceTypeEnum::INVALID; } const AString secondaryTypeName = this->coordinateDataArray->getMetaData()->get(GiftiMetaDataXmlElements::METADATA_NAME_ANATOMICAL_STRUCTURE_SECONDARY); SecondarySurfaceTypeEnum::Enum surfaceType = SecondarySurfaceTypeEnum::fromGiftiName(secondaryTypeName, NULL); return surfaceType; } /** * Sets the type of this surface. * @param surfaceType * New type for surface. */ void SurfaceFile::setSecondaryType(const SecondarySurfaceTypeEnum::Enum secondaryType) { if (this->coordinateDataArray == NULL) { return; } const AString secondaryTypeName = SecondarySurfaceTypeEnum::toGiftiName(secondaryType); this->coordinateDataArray->getMetaData()->set(GiftiMetaDataXmlElements::METADATA_NAME_ANATOMICAL_STRUCTURE_SECONDARY, secondaryTypeName); } void SurfaceFile::getGeodesicHelper(CaretPointer& helpOut) const { {//lock before modifying member (base) CaretMutexLocker myLock(&m_geoHelperMutex); if (m_geoBase == NULL) { m_geoHelpers.clear();//just to be sure m_geoHelperIndex = 0; m_geoBase.grabNew(new GeodesicHelperBase(this));//yes, this takes some time, and is single threaded at the moment }//keep locked while searching int32_t& myIndex = m_geoHelperIndex; int32_t myEnd = m_geoHelpers.size(); for (int32_t i = 0; i < myEnd; ++i) { if (myIndex >= myEnd) myIndex = 0; if (m_geoHelpers[myIndex].getReferenceCount() == 1)//1 reference: in this class, so unused elsewhere { helpOut = m_geoHelpers[myIndex]; ++myIndex; return; } ++myIndex; } }//UNLOCK before building a new one, so they can be built in parallel - this actually just involves initializing the marked array CaretPointer ret(new GeodesicHelper(m_geoBase)); CaretMutexLocker myLock(&m_geoHelperMutex);//relock before modifying the array m_geoHelpers.push_back(ret); helpOut = ret; } CaretPointer SurfaceFile::getGeodesicHelper() const {//this convenience function is here because in order to guarantee thread safety, the real function explicitly copies to a reference argument before letting the mutex unlock CaretPointer ret;//the copy of a return should take place before destructors (including the locker for the helper mutex), but just to be safe getGeodesicHelper(ret);//this call is therefore thread safe and guaranteed to modify reference count before it unlocks the mutex for helpers return ret;//so we are already safe by here, at the expense of a second copy constructor/operator= of a CaretPointer } void SurfaceFile::getTopologyHelper(CaretPointer& helpOut, bool infoSorted) const { { CaretMutexLocker myLock(&m_topoHelperMutex);//lock before searching with the shared index int32_t& myIndex = m_topoHelperIndex; int32_t myEnd = m_topoHelpers.size(); for (int32_t i = 0; i < myEnd; ++i) { if (myIndex >= myEnd) myIndex = 0; if (m_topoHelpers[myIndex].getReferenceCount() == 1 && (!infoSorted || m_topoHelpers[myIndex]->isNodeInfoSorted()))//1 reference: in this class, so unused elsewhere { helpOut = m_topoHelpers[myIndex];//NOTE: can give sorted info to something that doesn't ask for sorted, if it already exists ++myIndex; return; } ++myIndex; } if (m_topoBase == NULL || (infoSorted && !m_topoBase->isNodeInfoSorted())) { m_topoBase.grabNew(new TopologyHelperBase(this, infoSorted)); } } CaretPointer ret(new TopologyHelper(m_topoBase)); CaretMutexLocker myLock(&m_topoHelperMutex);//lock before modifying the array m_topoHelpers.push_back(ret); helpOut = ret; } CaretPointer SurfaceFile::getTopologyHelper(bool infoSorted) const {//see convenience function for geo helper for explanation CaretPointer ret; getTopologyHelper(ret, infoSorted); return ret; } void SurfaceFile::getSignedDistanceHelper(CaretPointer& helpOut) const { { CaretMutexLocker myLock(&m_distHelperMutex);//lock before searching with the shared index int32_t& myIndex = m_distHelperIndex; int32_t myEnd = m_distHelpers.size(); for (int32_t i = 0; i < myEnd; ++i) { if (myIndex >= myEnd) myIndex = 0; if (m_distHelpers[myIndex].getReferenceCount() == 1)//1 reference: in this class, so unused elsewhere { helpOut = m_distHelpers[myIndex]; ++myIndex; return; } ++myIndex; } if (m_distBase == NULL) { m_distBase.grabNew(new SignedDistanceHelperBase(this)); } } CaretPointer ret(new SignedDistanceHelper(m_distBase)); CaretMutexLocker myLock(&m_distHelperMutex);//lock before modifying the array m_distHelpers.push_back(ret); helpOut = ret; } CaretPointer SurfaceFile::getSignedDistanceHelper() const {//see convenience function for geo helper for explanation CaretPointer ret; getSignedDistanceHelper(ret); return ret; } void SurfaceFile::invalidateHelpers() { if (m_geoBase != NULL) { CaretMutexLocker myLock(&m_geoHelperMutex);//make this function threadsafe m_geoHelperIndex = 0; m_geoHelpers.clear();//CaretPointers make this nice, if they are still in use elsewhere, they don't vanish, even though this class is supposed to "control" them to some extent m_geoBase.grabNew(NULL); } if (m_topoBase != NULL) { CaretMutexLocker myLock2(&m_topoHelperMutex); m_topoHelperIndex = 0; m_topoHelpers.clear(); m_topoBase.grabNew(NULL); } if (m_distBase != NULL) { CaretMutexLocker myLock4(&m_distHelperMutex); m_distHelperIndex = 0; m_distHelpers.clear(); m_distBase.grabNew(NULL); } if (m_locator != NULL) { CaretMutexLocker myLock3(&m_locatorMutex); m_locator.grabNew(NULL); } } /** * @return A bounding box for this surface. */ const BoundingBox* SurfaceFile::getBoundingBox() const { if (this->boundingBox == NULL) { this->boundingBox = new BoundingBox(); /* * For bounding box, must make sure each node is connected. */ CaretPointer th = this->getTopologyHelper(); const int32_t numberOfNodes = this->getNumberOfNodes(); for (int32_t i = 0; i < numberOfNodes; i++) { if (th->getNodeHasNeighbors(i)) { this->boundingBox->update(&this->coordinatePointer[i*3]); } } } return this->boundingBox; } /** * Match this surface to the given surface. That is, after this * method is called, this surface and the given surface will * fit within the same bounding box. * @param surfaceFile * Match to this surface file. */ void SurfaceFile::matchSurfaceBoundingBox(const SurfaceFile* surfaceFile) { CaretAssert(surfaceFile); const BoundingBox* targetBoundingBox = surfaceFile->getBoundingBox(); const BoundingBox* myBoundingBox = getBoundingBox(); Matrix4x4 matrix; /* * Translate min x/y/z to origin */ matrix.translate(-myBoundingBox->getMinX(), -myBoundingBox->getMinY(), -myBoundingBox->getMinZ()); /* * Scale to match size of match surface */ float scaleX = (targetBoundingBox->getDifferenceX() / myBoundingBox->getDifferenceX()); float scaleY = (targetBoundingBox->getDifferenceY() / myBoundingBox->getDifferenceY()); float scaleZ = (targetBoundingBox->getDifferenceZ() / myBoundingBox->getDifferenceZ()); if (getSurfaceType() == SurfaceTypeEnum::FLAT) { /* * For a flat surface, need to retain overall shape. * Thus, just one scale factor that matches flat X-range * to 3D Y-range. */ const float scale = (targetBoundingBox->getDifferenceY() / myBoundingBox->getDifferenceX()); scaleX = scale; scaleY = scale; scaleZ = scale; } matrix.scale(scaleX, scaleY, scaleZ); /* * Translate to min x/y/z of match surface so that * the two surfaces are now within the same "shoebox". */ matrix.translate(targetBoundingBox->getMinX(), targetBoundingBox->getMinY(), targetBoundingBox->getMinZ()); applyMatrix(matrix); } /** * Apply the given matrix to the coordinates of this surface. * * @param matrix * Transformation matrix that is used. */ void SurfaceFile::applyMatrix(const Matrix4x4& matrix) { CaretPointer th = this->getTopologyHelper(); const int32_t numberOfNodes = getNumberOfNodes(); for (int32_t i = 0; i < numberOfNodes; i++) { if (th->getNodeHasNeighbors(i)) { matrix.multiplyPoint3(&coordinatePointer[i*3]); } } computeNormals(); setModified(); } /** * Translate to the surface center of mass. */ void SurfaceFile::translateToCenterOfMass() { const int32_t numberOfNodes = getNumberOfNodes(); CaretPointer th = this->getTopologyHelper(); double cx = 0.0; double cy = 0.0; double cz = 0.0; double numberOfNodesWithNeighbors = 0.0; for (int32_t i = 0; i < numberOfNodes; i++) { if (th->getNodeHasNeighbors(i)) { const float* xyz = getCoordinate(i); cx += xyz[0]; cy += xyz[1]; cz += xyz[2]; numberOfNodesWithNeighbors += 1.0; } } if (numberOfNodesWithNeighbors > 0.0) { cx /= numberOfNodesWithNeighbors; cy /= numberOfNodesWithNeighbors; cz /= numberOfNodesWithNeighbors; Matrix4x4 matrix; matrix.setTranslation(-cx, -cy, -cz); applyMatrix(matrix); } } /** * Flip the normal vectors. */ void SurfaceFile::flipNormals() { if (trianglePointer == NULL) return; const int numTiles = getNumberOfTriangles(); int32_t tempvert; for (int i = 0; i < numTiles; ++i)//swap first and second verts of all triangles { int offset = i * 3; tempvert = trianglePointer[offset]; trianglePointer[offset] = trianglePointer[offset + 1]; trianglePointer[offset + 1] = tempvert; } invalidateNormals(); invalidateHelpers();//sorted topology helpers would change, so just for completeness setModified(); } /** * @return True if normal vectors are correct, else false. * * Find the node with the greatest Z-coordinate. If the Z-component * of the this node's normal vector is positive, then the normal vectors * point out of the surface and they are correct. Otherwise, the normal * vectors are pointing into the surface. */ bool SurfaceFile::areNormalVectorsCorrect() const { CaretPointer th = this->getTopologyHelper(); float maxZ = -std::numeric_limits::max(); int32_t indxMaxZ = -1; const int32_t numberOfNodes = getNumberOfNodes(); for (int32_t i = 0; i < numberOfNodes; i++) { if (th->getNodeHasNeighbors(i)) { const float* xyz = getCoordinate(i); if (xyz[2] > maxZ) { maxZ = xyz[2]; indxMaxZ = i; } } } if (indxMaxZ >= 0) { const float* normal = getNormalVector(indxMaxZ); if (normal[2] > 0.0) { return true; } } return false; } /** * @return The radius of the spherical surface. * Surface is assumed spherical. */ float SurfaceFile::getSphericalRadius() const { const BoundingBox* bb = getBoundingBox(); const float radius = bb->getMaxX() - bb->getCenterX(); return radius; } /** * @return Area of the surface. */ float SurfaceFile::getSurfaceArea() const { float areaOut = 0.0; CaretAssert(this->trianglePointer); const int32_t numberOfTriangles = getNumberOfTriangles(); for (int32_t i = 0; i < numberOfTriangles; ++i) { const int32_t* triangleNodeIndices = getTriangle(i); const float* node1 = getCoordinate(triangleNodeIndices[0]); const float* node2 = getCoordinate(triangleNodeIndices[1]); const float* node3 = getCoordinate(triangleNodeIndices[2]); areaOut += MathFunctions::triangleArea(node1, node2, node3); } return areaOut; } void SurfaceFile::computeNodeAreas(std::vector& areasOut) const { CaretAssert(this->trianglePointer); int32_t triEnd = getNumberOfTriangles(); int32_t numNodes = getNumberOfNodes(); areasOut.resize(numNodes); for (int32_t i = 0; i < numNodes; ++i) { areasOut[i] = 0.0f; } for (int32_t i = 0; i < triEnd; ++i) { const int32_t* thisTri = getTriangle(i); const float* node1 = getCoordinate(thisTri[0]); const float* node2 = getCoordinate(thisTri[1]); const float* node3 = getCoordinate(thisTri[2]); float area3 = MathFunctions::triangleArea(node1, node2, node3) / 3.0f; areasOut[thisTri[0]] += area3; areasOut[thisTri[1]] += area3; areasOut[thisTri[2]] += area3; } } /** * Is the object modified? * @return true if modified, else false. */ void SurfaceFile::setModified() { if (this->boundingBox != NULL) { delete this->boundingBox; this->boundingBox = NULL; } GiftiTypeFile::setModified(); } int32_t SurfaceFile::closestNode(const float target[3], const float maxDist) const { if (maxDist > 0.0f) { return getPointLocator()->closestPointLimited(target, maxDist); } else { return getPointLocator()->closestPoint(target); } } CaretPointer SurfaceFile::getPointLocator() const { if (m_locator == NULL)//try to avoid locking even once { CaretMutexLocker myLock(&m_locatorMutex); if (m_locator == NULL)//test again AFTER lock to avoid race conditions { m_locator.grabNew(new CaretPointLocator(getCoordinateData(), getNumberOfNodes())); } } return m_locator; } void SurfaceFile::clearCachedHelpers() const { { CaretMutexLocker locked(&m_topoHelperMutex); m_topoHelperIndex = 0; m_topoHelpers.clear(); m_topoBase.grabNew(NULL); } { CaretMutexLocker locked(&m_geoHelperMutex); m_geoHelperIndex = 0; m_geoHelpers.clear(); m_geoBase.grabNew(NULL); } { CaretMutexLocker locked(&m_distHelperMutex); m_distHelperIndex = 0; m_distHelpers.clear(); m_distBase.grabNew(NULL); } { CaretMutexLocker locked(&m_locatorMutex); m_locator.grabNew(NULL); } } /** * @return Information about the surface. */ AString SurfaceFile::getInformation() const { AString txt; txt += ("Name: " + this->getFileNameNoPath() + "\n"); txt += ("Type: " + SurfaceTypeEnum::toGuiName(this->getSurfaceType()) + "\n"); const int32_t numberOfNodes = this->getNumberOfNodes(); txt += ("Number of Vertices: " + AString::number(numberOfNodes) + "\n"); txt += ("Number of Triangles: " + AString::number(this->getNumberOfTriangles()) + "\n"); txt += ("Bounds: (" + AString::fromNumbers(this->getBoundingBox()->getBounds(), 6, ", ") + ")\n"); if (numberOfNodes > 0) { // std::vector nodeSpacing; // nodeSpacing.reserve(numberOfNodes * 10); // CaretPointer th = this->getTopologyHelper(); // int numberOfNeighbors; // for (int32_t i = 0; i < numberOfNodes; i++) { // const int* neighbors = th->getNodeNeighbors(i, numberOfNeighbors); // for (int32_t j = 0; j < numberOfNeighbors; j++) { // const int n = neighbors[j]; // if (n > i) { // const float dist = MathFunctions::distance3D(this->getCoordinate(i), // this->getCoordinate(n)); // nodeSpacing.push_back(dist); // } // } // } // // DescriptiveStatistics stats; // stats.update(nodeSpacing); DescriptiveStatistics stats; getNodesSpacingStatistics(stats); const float mean = stats.getMean(); const float stdDev = stats.getStandardDeviationSample(); const float minValue = stats.getMinimumValue(); const float maxValue = stats.getMaximumValue(); txt += ("Spacing:\n"); txt += (" Mean: " + AString::number(mean, 'f', 6) + "\n"); txt += (" Std Dev: " + AString::number(stdDev, 'f', 6) + "\n"); txt += (" Minimum: " + AString::number(minValue, 'f', 6) + "\n"); txt += (" Maximum: " + AString::number(maxValue, 'f', 6) + "\n"); } return txt; } /** * Get statistics on node spacing. * @param statsOut * Upon exit, contains node spacing descriptive statistics. */ void SurfaceFile::getNodesSpacingStatistics(DescriptiveStatistics& statsOut) const { const int32_t numberOfNodes = this->getNumberOfNodes(); std::vector nodeSpacing; nodeSpacing.reserve(numberOfNodes * 10); CaretPointer th = this->getTopologyHelper(); int numberOfNeighbors; for (int32_t i = 0; i < numberOfNodes; i++) { const int* neighbors = th->getNodeNeighbors(i, numberOfNeighbors); for (int32_t j = 0; j < numberOfNeighbors; j++) { const int n = neighbors[j]; if (n > i) { const float dist = MathFunctions::distance3D(this->getCoordinate(i), this->getCoordinate(n)); nodeSpacing.push_back(dist); } } } statsOut.update(nodeSpacing); } void SurfaceFile::getNodesSpacingStatistics(FastStatistics& statsOut) const { const int32_t numberOfNodes = this->getNumberOfNodes(); std::vector nodeSpacing; nodeSpacing.reserve(numberOfNodes * 10); CaretPointer th = this->getTopologyHelper(); int numberOfNeighbors; for (int32_t i = 0; i < numberOfNodes; i++) { const int* neighbors = th->getNodeNeighbors(i, numberOfNeighbors); for (int32_t j = 0; j < numberOfNeighbors; j++) { const int n = neighbors[j]; if (n > i) { const float dist = MathFunctions::distance3D(this->getCoordinate(i), this->getCoordinate(n)); nodeSpacing.push_back(dist); } } } statsOut.update(nodeSpacing.data(), nodeSpacing.size()); } /** * Invalidate surface coloring. */ void SurfaceFile::invalidateNodeColoringForBrowserTabs() { /* * Free memory since could have many tabs and many surfaces equals lots of memory */ for (int32_t i = 0; i < BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS; i++) { this->surfaceNodeColoringForBrowserTabs[i].clear(); this->surfaceMontageNodeColoringForBrowserTabs[i].clear(); this->wholeBrainNodeColoringForBrowserTabs[i].clear(); } } /** * Allocate node coloring for a single surface in a browser tab. * @param browserTabIndex * Index of browser tab. * @param zeroizeColorsFlag * If true and memory is allocated for colors, the color components * are set to all zeros, otherwise the memory may be left unitialized. */ void SurfaceFile::allocateSurfaceNodeColoringForBrowserTab(const int32_t browserTabIndex, const bool zeroizeColorsFlag) { CaretAssertArrayIndex(this->surfaceNodeColoringForBrowserTabs, BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS, browserTabIndex); const uint64_t numberOfComponentsRGBA = this->getNumberOfNodes() * 4; if (this->surfaceNodeColoringForBrowserTabs[browserTabIndex].size() != numberOfComponentsRGBA) { if (zeroizeColorsFlag) { this->surfaceNodeColoringForBrowserTabs[browserTabIndex].resize(numberOfComponentsRGBA, 0.0); } else { this->surfaceNodeColoringForBrowserTabs[browserTabIndex].resize(numberOfComponentsRGBA); } } } /** * Allocate node coloring for a surface montage in a browser tab. * @param browserTabIndex * Index of browser tab. * @param zeroizeColorsFlag * If true and memory is allocated for colors, the color components * are set to all zeros, otherwise the memory may be left unitialized. */ void SurfaceFile::allocateSurfaceMontageNodeColoringForBrowserTab(const int32_t browserTabIndex, const bool zeroizeColorsFlag) { CaretAssertArrayIndex(this->surfaceMontageNodeColoringForBrowserTabs, BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS, browserTabIndex); const uint64_t numberOfComponentsRGBA = this->getNumberOfNodes() * 4; if (this->surfaceMontageNodeColoringForBrowserTabs[browserTabIndex].size() != numberOfComponentsRGBA) { if (zeroizeColorsFlag) { this->surfaceMontageNodeColoringForBrowserTabs[browserTabIndex].resize(numberOfComponentsRGBA, 0.0); } else { this->surfaceMontageNodeColoringForBrowserTabs[browserTabIndex].resize(numberOfComponentsRGBA); } } } /** * Allocate node coloring for a whole brain surface in a browser tab. * @param browserTabIndex * Index of browser tab. * @param zeroizeColorsFlag * If true and memory is allocated for colors, the color components * are set to all zeros, otherwise the memory may be left unitialized. */ void SurfaceFile::allocateWholeBrainNodeColoringForBrowserTab(const int32_t browserTabIndex, const bool zeroizeColorsFlag) { CaretAssertArrayIndex(this->wholeBrainNodeColoringForBrowserTabs, BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS, browserTabIndex); const uint64_t numberOfComponentsRGBA = this->getNumberOfNodes() * 4; if (this->wholeBrainNodeColoringForBrowserTabs[browserTabIndex].size() != numberOfComponentsRGBA) { if (zeroizeColorsFlag) { this->wholeBrainNodeColoringForBrowserTabs[browserTabIndex].resize(numberOfComponentsRGBA, 0.0); } else { this->wholeBrainNodeColoringForBrowserTabs[browserTabIndex].resize(numberOfComponentsRGBA); } } } /** * Get the RGBA color components for this single surface in the given tab. * @param browserTabIndex * Index of browser tab. * @return * Coloring for the tab or NULL if coloring is invalid and needs to be * set. */ float* SurfaceFile::getSurfaceNodeColoringRgbaForBrowserTab(const int32_t browserTabIndex) { CaretAssertArrayIndex(this->surfaceNodeColoringForBrowserTabs, BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS, browserTabIndex); std::vector& rgba = this->surfaceNodeColoringForBrowserTabs[browserTabIndex]; if (rgba.empty()) { return NULL; } return &rgba[0]; } /** * Set the RGBA color components for this a single surface in the given tab. * @param browserTabIndex * Index of browser tab. * @param rgbaNodeColorComponents * RGBA color components for this surface in the given tab. */ void SurfaceFile::setSurfaceNodeColoringRgbaForBrowserTab(const int32_t browserTabIndex, const float* rgbaNodeColorComponents) { CaretAssertArrayIndex(this->surfaceNodeColoringForBrowserTabs, BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS, browserTabIndex); this->allocateSurfaceNodeColoringForBrowserTab(browserTabIndex, false); const int numberOfComponentsRGBA = this->getNumberOfNodes() * 4; std::vector& rgba = this->surfaceNodeColoringForBrowserTabs[browserTabIndex]; for (int32_t i = 0; i < numberOfComponentsRGBA; i++) { rgba[i] = rgbaNodeColorComponents[i]; } } /** * Get the RGBA color components for this surface montage in the given tab. * @param browserTabIndex * Index of browser tab. * @return * Coloring for the tab or NULL if coloring is invalid and needs to be * set. */ float* SurfaceFile::getSurfaceMontageNodeColoringRgbaForBrowserTab(const int32_t browserTabIndex) { CaretAssertArrayIndex(this->surfaceMontageNodeColoringForBrowserTabs, BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS, browserTabIndex); std::vector& rgba = this->surfaceMontageNodeColoringForBrowserTabs[browserTabIndex]; if (rgba.empty()) { return NULL; } return &rgba[0]; } /** * Set the RGBA color components for this a surface montage in the given tab. * @param browserTabIndex * Index of browser tab. * @param rgbaNodeColorComponents * RGBA color components for this surface montage in the given tab. */ void SurfaceFile::setSurfaceMontageNodeColoringRgbaForBrowserTab(const int32_t browserTabIndex, const float* rgbaNodeColorComponents) { CaretAssertArrayIndex(this->surfaceMontageNodeColoringForBrowserTabs, BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS, browserTabIndex); this->allocateSurfaceMontageNodeColoringForBrowserTab(browserTabIndex, false); const int numberOfComponentsRGBA = this->getNumberOfNodes() * 4; std::vector& rgba = this->surfaceMontageNodeColoringForBrowserTabs[browserTabIndex]; for (int32_t i = 0; i < numberOfComponentsRGBA; i++) { rgba[i] = rgbaNodeColorComponents[i]; } } /** * Get the RGBA color components for this whole brain surface in the given tab. * @param browserTabIndex * Index of browser tab. * @return * Coloring for the tab or NULL if coloring is invalid and needs to be set. */ float* SurfaceFile::getWholeBrainNodeColoringRgbaForBrowserTab(const int32_t browserTabIndex) { CaretAssertArrayIndex(this->wholeBrainNodeColoringForBrowserTabs, BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS, browserTabIndex); std::vector& rgba = this->wholeBrainNodeColoringForBrowserTabs[browserTabIndex]; if (rgba.empty()) { return NULL; } return &rgba[0]; } /** * Set the RGBA color components for this a whole brain surface in the given tab. * @param browserTabIndex * Index of browser tab. * @param rgbaNodeColorComponents * RGBA color components for this surface in the given tab. */ void SurfaceFile::setWholeBrainNodeColoringRgbaForBrowserTab(const int32_t browserTabIndex, const float* rgbaNodeColorComponents) { CaretAssertArrayIndex(this->wholeBrainNodeColoringForBrowserTabs, BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS, browserTabIndex); this->allocateWholeBrainNodeColoringForBrowserTab(browserTabIndex, false); const int numberOfComponentsRGBA = this->getNumberOfNodes() * 4; std::vector& rgba = this->wholeBrainNodeColoringForBrowserTabs[browserTabIndex]; for (int32_t i = 0; i < numberOfComponentsRGBA; i++) { rgba[i] = rgbaNodeColorComponents[i]; } } /** * Receive an event. * * @param event * The event that the receive can respond to. */ void SurfaceFile::receiveEvent(Event* event) { if (event->getEventType() == EventTypeEnum::EVENT_SURFACE_COLORING_INVALIDATE) { EventSurfaceColoringInvalidate* invalidateEvent = dynamic_cast(event); CaretAssert(invalidateEvent); invalidateEvent->setEventProcessed(); this->invalidateNodeColoringForBrowserTabs(); } } bool SurfaceFile::matchesTopology(const SurfaceFile& rhs) const { if (getNumberOfNodes() != rhs.getNumberOfNodes()) return false; int numTriangles = getNumberOfTriangles(); if (numTriangles != rhs.getNumberOfTriangles()) return false; for (int i = 0; i < numTriangles; ++i) { int i3 = i * 3; if (trianglePointer[i3] != rhs.trianglePointer[i3]) return false;//exactly same order of triangles and nodes, for strictest topology equivalence if (trianglePointer[i3 + 1] != rhs.trianglePointer[i3 + 1]) return false;//also, is a faster test if (trianglePointer[i3 + 2] != rhs.trianglePointer[i3 + 2]) return false; } return true; } bool SurfaceFile::hasNodeCorrespondence(const SurfaceFile& rhs) const { int numNodes = getNumberOfNodes(); if (numNodes != rhs.getNumberOfNodes()) return false; if (getNumberOfTriangles() != rhs.getNumberOfTriangles()) return false; if (matchesTopology(rhs)) return true;//short circuit the common, faster to check case, should fail very early if it fails at all CaretPointer myHelp = getTopologyHelper(), rightHelp = rhs.getTopologyHelper(); for (int i = 0; i < numNodes; ++i) { const std::vector& myNeigh = myHelp->getNodeNeighbors(i); const std::vector& rightNeigh = rightHelp->getNodeNeighbors(i); int mySize = (int)myNeigh.size(); if (mySize != (int)rightNeigh.size()) return false; std::set myUsed; for (int j = 0; j < mySize; ++j) { myUsed.insert(myNeigh[j]); } for (int j = 0; j < mySize; ++j) { if (myUsed.find(rightNeigh[j]) == myUsed.end()) return false; } } return true; } /** * Add information about the file to the data file information. * * @param dataFileInformation * Consolidates information about a data file. */ void SurfaceFile::addToDataFileContentInformation(DataFileContentInformation& dataFileInformation) { GiftiTypeFile::addToDataFileContentInformation(dataFileInformation); dataFileInformation.addNameAndValue("Number of Triangles", getNumberOfTriangles()); dataFileInformation.addNameAndValue("Normal Vectors Correct", areNormalVectorsCorrect()); dataFileInformation.addNameAndValue("Surface Type (Primary)", SurfaceTypeEnum::toGuiName(getSurfaceType())); dataFileInformation.addNameAndValue("Surface Type (Secondary)", SecondarySurfaceTypeEnum::toGuiName(getSecondaryType())); const BoundingBox* boundingBox = getBoundingBox(); dataFileInformation.addNameAndValue("X-minimum", boundingBox->getMinX()); dataFileInformation.addNameAndValue("X-maximum", boundingBox->getMaxX()); dataFileInformation.addNameAndValue("Y-minimum", boundingBox->getMinY()); dataFileInformation.addNameAndValue("Y-maximum", boundingBox->getMaxY()); dataFileInformation.addNameAndValue("Z-minimum", boundingBox->getMinZ()); dataFileInformation.addNameAndValue("Z-maximum", boundingBox->getMaxZ()); if (getSurfaceType() == SurfaceTypeEnum::SPHERICAL) { dataFileInformation.addNameAndValue("Spherical Radius", getSphericalRadius()); } dataFileInformation.addNameAndValue("Surface Area", getSurfaceArea()); DescriptiveStatistics stats; getNodesSpacingStatistics(stats); dataFileInformation.addNameAndValue("Spacing Mean", stats.getMean()); dataFileInformation.addNameAndValue("Spacing Std Dev", stats.getStandardDeviationSample()); dataFileInformation.addNameAndValue("Spacing Minimum", stats.getMinimumValue()); dataFileInformation.addNameAndValue("Spacing Maximum", stats.getMaximumValue()); } /** * @return A String describing the content of this object. */ AString SurfaceFile::toString() const { PlainTextStringBuilder tb; getDescriptionOfContent(tb); return tb.getText(); } /** * Get a text description of the instance's content. * * @param descriptionOut * Description of the instance's content. */ void SurfaceFile::getDescriptionOfContent(PlainTextStringBuilder& descriptionOut) const { descriptionOut.addLine("Surface: " + getFileNameNoPath()); descriptionOut.addLine(" Structure: " + StructureEnum::toGuiName(getStructure())); descriptionOut.addLine(" Primary Type: " + SurfaceTypeEnum::toGuiName(getSurfaceType())); descriptionOut.addLine(" Secondary Type: " + SecondarySurfaceTypeEnum::toGuiName(getSecondaryType())); } connectome-workbench-1.2.3+git41-gc4c6c90/src/Files/SurfaceFile.h000066400000000000000000000266661300200146000241670ustar00rootroot00000000000000 #ifndef __SURFACE_FILE_H__ #define __SURFACE_FILE_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 "BrainConstants.h" #include "CaretMutex.h" #include "CaretPointer.h" #include "EventManager.h" #include "EventListenerInterface.h" #include "GiftiTypeFile.h" #include "SurfaceTypeEnum.h" namespace caret { class BoundingBox; class CaretPointLocator; class DescriptiveStatistics; class FastStatistics; class GeodesicHelper; class GeodesicHelperBase; class GiftiDataArray; class Matrix4x4; class PlainTextStringBuilder; class SignedDistanceHelper; class SignedDistanceHelperBase; class TopologyHelper; class TopologyHelperBase; /** * A surface data file. */ class SurfaceFile : public GiftiTypeFile, EventListenerInterface { public: SurfaceFile(); SurfaceFile(const SurfaceFile& sf); SurfaceFile& operator=(const SurfaceFile& sf); virtual ~SurfaceFile(); virtual void addToDataFileContentInformation(DataFileContentInformation& dataFileInformation); virtual void receiveEvent(Event* event); virtual void clear(); virtual int32_t getNumberOfNodes() const; virtual int32_t getNumberOfColumns() const; void setNumberOfNodesAndTriangles(const int32_t& nodes, const int32_t& triangles); const float* getCoordinate(const int32_t nodeIndex) const; void getCoordinate(const int32_t nodeIndex, float xyzOut[3]) const; void setCoordinate(const int32_t nodeIndex, const float xyzIn[3]); void setCoordinate(const int32_t nodeIndex, const float xIn, const float yIn, const float zIn); void setCoordinates(const float *coordinates); const float* getCoordinateData() const; const float* getNormalVector(const int32_t nodeIndex) const; const float* getNormalData() const; int getNumberOfTriangles() const; const int32_t* getTriangle(const int32_t index) const; void setTriangle(const int32_t& index, const int32_t* nodes); void setTriangle(const int32_t& index, const int32_t& node1, const int32_t& node2, const int32_t& node3); int32_t getTriangleThatSharesEdge(const int32_t n1, const int32_t n2, const int32_t oppositeTriangle) const; void computeNormals(); std::vector computeAverageNormals(); void getTriangleNormalVector(const int32_t triangleIndex, float normalOut[3]) const; SurfaceTypeEnum::Enum getSurfaceType() const; void setSurfaceType(const SurfaceTypeEnum::Enum surfaceType); SecondarySurfaceTypeEnum::Enum getSecondaryType() const; void setSecondaryType(const SecondarySurfaceTypeEnum::Enum secondaryType); float getSphericalRadius() const; float getSurfaceArea() const; CaretPointer getTopologyHelper(bool infoSorted = false) const; void getTopologyHelper(CaretPointer& helpOut, bool infoSorted = false) const; CaretPointer getGeodesicHelper() const; void getGeodesicHelper(CaretPointer& helpOut) const; CaretPointer getSignedDistanceHelper() const; void getSignedDistanceHelper(CaretPointer& helpOut) const; CaretPointer getPointLocator() const; void clearCachedHelpers() const; const BoundingBox* getBoundingBox() const; void matchSurfaceBoundingBox(const SurfaceFile* surfaceFile); void applyMatrix(const Matrix4x4& matrix); void getNodesSpacingStatistics(DescriptiveStatistics& statsOut) const; void getNodesSpacingStatistics(FastStatistics& statsOut) const; void computeNodeAreas(std::vector& areasOut) const; ///find the closest node on the surface, within maxDist if maxDist is positive int32_t closestNode(const float target[3], const float maxDist = -1.0f) const; virtual void setModified(); AString getInformation() const; float* getSurfaceNodeColoringRgbaForBrowserTab(const int32_t browserTabIndex); void setSurfaceNodeColoringRgbaForBrowserTab(const int32_t browserTabIndex, const float* rgbaNodeColorComponents); float* getSurfaceMontageNodeColoringRgbaForBrowserTab(const int32_t browserTabIndex); void setSurfaceMontageNodeColoringRgbaForBrowserTab(const int32_t browserTabIndex, const float* rgbaNodeColorComponents); float* getWholeBrainNodeColoringRgbaForBrowserTab(const int32_t browserTabIndex); void setWholeBrainNodeColoringRgbaForBrowserTab(const int32_t browserTabIndex, const float* rgbaNodeColorComponents); void invalidateNormals(); void translateToCenterOfMass(); void flipNormals(); bool areNormalVectorsCorrect() const; ///check that it has EXACTLY the same topology, with no flipped normals or rotated or reordered triangles bool matchesTopology(const SurfaceFile& rhs) const; ///check only that each node is connected to the same set of other nodes, allow any other form of mischief bool hasNodeCorrespondence(const SurfaceFile& rhs) const; virtual AString toString() const; virtual void getDescriptionOfContent(PlainTextStringBuilder& descriptionOut) const; //override writeFile in order to check filename against type of file virtual void writeFile(const AString& filename); protected: /** * Validate the contents of the file after it * has been read such as correct number of * data arrays and proper data types/dimensions. */ virtual void validateDataArraysAfterReading(); void copyHelperSurfaceFile(const SurfaceFile& sf); void initializeMembersSurfaceFile(); private: void invalidateNodeColoringForBrowserTabs(); void allocateSurfaceNodeColoringForBrowserTab(const int32_t browserTabIndex, const bool zeroizeColorsFlag); void allocateSurfaceMontageNodeColoringForBrowserTab(const int32_t browserTabIndex, const bool zeroizeColorsFlag); void allocateWholeBrainNodeColoringForBrowserTab(const int32_t browserTabIndex, const bool zeroizeColorsFlag); /** Data array containing the coordinates. */ GiftiDataArray* coordinateDataArray; /** * This coloring is used when a ONE surface is displayed. * Node color components Red, Green, Blue, Alpha for each browser tab. * Each element of the vector points to the coloring * for a browser tab with the corresponding index. */ std::vector surfaceNodeColoringForBrowserTabs[BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS]; /** * This coloring is used when a surface montage is displayed. * Node color components Red, Green, Blue, Alpha for each browser tab. * Each element of the vector points to the coloring * for a browser tab with the corresponding index. */ std::vector surfaceMontageNodeColoringForBrowserTabs[BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS]; /** * This coloring is used when a Whole Brain is displayed. * Node color components Red, Green, Blue, Alpha for each browser tab. * Each element of the vector points to the coloring * for a browser tab with the corresponding index. */ std::vector wholeBrainNodeColoringForBrowserTabs[BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS]; /** Points to memory containing the coordinates. */ float* coordinatePointer; /** Data array containing the triangles. */ GiftiDataArray* triangleDataArray; /** Points to memory containing the triangles. */ int32_t* trianglePointer; /** surface normal vectors. */ std::vector normalVectors; bool m_normalsComputed; bool m_skipSanityCheck; ///topology base for surface mutable CaretPointer m_topoBase; ///tracks allocated TopologyHelpers for this class mutable std::vector > m_topoHelpers; ///used to search through topology helpers without starting from 0 every time, wraps around mutable int32_t m_topoHelperIndex; ///the geodesic base for this surface mutable CaretPointer m_geoBase; ///tracks allocated geodesic helpers for this class mutable std::vector > m_geoHelpers; ///used to search through geodesic helpers without starting from 0 every time, wraps around mutable int32_t m_geoHelperIndex; ///the geodesic base for this surface mutable CaretPointer m_distBase; ///tracks allocated geodesic helpers for this class mutable std::vector > m_distHelpers; ///used to search through geodesic helpers without starting from 0 every time, wraps around mutable int32_t m_distHelperIndex; ///used to search for the closest point in the surface mutable CaretPointer m_locator; ///used to track when the surface file gets changed void invalidateHelpers(); mutable BoundingBox* boundingBox; mutable CaretMutex m_topoHelperMutex, m_geoHelperMutex, m_locatorMutex, m_distHelperMutex; }; } // namespace #endif // __SURFACE_FILE_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Files/SurfaceProjectedItem.cxx000066400000000000000000000437321300200146000264120ustar00rootroot00000000000000/*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 __SURFACE_PROJECTED_ITEM_DEFINE__ #include "SurfaceProjectedItem.h" #undef __SURFACE_PROJECTED_ITEM_DEFINE__ #include "CaretAssert.h" #include "CaretLogger.h" #include "DataFileException.h" #include "SurfaceProjectionBarycentric.h" #include "SurfaceProjectionVanEssen.h" #include "XmlWriter.h" #include using namespace caret; /** * \class caret::SurfaceProjectedItem * \brief Maintains position of an item projected to a surface. * * Multiple projections are supported and may be valid at one time. * (1) Barycentric projects to a surface triangle. (2) VanEssen * projects to an edget of a triangle. (3) Stereotaxic is a * three-dimensional coordinate. A volume coordinate is also * available. */ /** * Constructor. * */ SurfaceProjectedItem::SurfaceProjectedItem() : CaretObjectTracksModification() { this->initializeMembersSurfaceProjectedItem(); } /** * Destructor */ SurfaceProjectedItem::~SurfaceProjectedItem() { delete this->barycentricProjection; delete this->vanEssenProjection; } /** * Copy Constructor * @param Object that is copied. */ SurfaceProjectedItem::SurfaceProjectedItem(const SurfaceProjectedItem& o) : CaretObjectTracksModification(o) { this->initializeMembersSurfaceProjectedItem(); this->copyHelper(o); } /** * Assignment operator. */ SurfaceProjectedItem& SurfaceProjectedItem::operator=(const SurfaceProjectedItem& o) { if (this != &o) { CaretObject::operator=(o); this->copyHelper(o); }; return *this; } bool SurfaceProjectedItem::operator==(const SurfaceProjectedItem& rhs) const { if (structure != rhs.structure) return false; if (stereotaxicXYZValid != rhs.stereotaxicXYZValid) return false; if (volumeXYZValid != rhs.volumeXYZValid) return false; if (stereotaxicXYZValid) { for (int i = 0; i < 3; ++i) { if (stereotaxicXYZ[i] != rhs.stereotaxicXYZ[i]) return false; } } if (volumeXYZValid) { for (int i = 0; i < 3; ++i) { if (volumeXYZ[i] != rhs.volumeXYZ[i]) return false; } } if (*barycentricProjection != *rhs.barycentricProjection) return false; return (*vanEssenProjection == *rhs.vanEssenProjection); } /** * Helps with copy constructor and assignment operator. */ void SurfaceProjectedItem::copyHelper(const SurfaceProjectedItem& spi) { this->setStereotaxicXYZ(spi.getStereotaxicXYZ()); this->stereotaxicXYZValid = spi.stereotaxicXYZValid; this->setVolumeXYZ(spi.getVolumeXYZ()); this->volumeXYZValid = spi.volumeXYZValid; this->structure = spi.structure; *this->barycentricProjection = *spi.barycentricProjection; *this->vanEssenProjection = *spi.vanEssenProjection; } /** * Reset to default values as if no projection of any type. */ void SurfaceProjectedItem::reset() { this->stereotaxicXYZ[0] = 0.0; this->stereotaxicXYZ[1] = 0.0; this->stereotaxicXYZ[2] = 0.0; this->stereotaxicXYZValid = false; this->volumeXYZ[0] = 0.0; this->volumeXYZ[1] = 0.0; this->volumeXYZ[2] = 0.0; this->volumeXYZValid = false; this->structure = StructureEnum::INVALID; this->barycentricProjection->reset(); this->vanEssenProjection->reset(); } void SurfaceProjectedItem::initializeMembersSurfaceProjectedItem() { this->barycentricProjection = new SurfaceProjectionBarycentric(); this->vanEssenProjection = new SurfaceProjectionVanEssen(); this->reset(); } /** * Unproject the item to the stereotaxic XYZ coordinates. * * @param sf - Surface on which unprojection takes place. * @param pasteOntoSurfaceFlag - place item directly on surface. * */ void SurfaceProjectedItem::unprojectToStereotaxicXYZ(const SurfaceFile& sf, const bool isUnprojectedOntoSurface) { float xyz[3]; if (getProjectedPosition(sf, xyz, isUnprojectedOntoSurface)) { this->setStereotaxicXYZ(xyz); } } /** * Unproject the item to the volume XYZ coordinates. * * @param sf - Surface on which unprojection takes place. * @param pasteOntoSurfaceFlag - place item directly on surface. * */ void SurfaceProjectedItem::unprojectToVolumeXYZ(const SurfaceFile& sf, const bool isUnprojectedOntoSurface) { float xyz[3]; if (getProjectedPosition(sf, xyz, isUnprojectedOntoSurface)) { this->setVolumeXYZ(xyz); } } /** * Get the projected position of this item. * The first valid of this positions is used: (1) Barycentric, * (2) VanEssen, (3) Stereotaxic. * * @param surfaceFile * Surface File for positioning. * @param xyzOut * Output containing the projected position. * @param pasteOntoSurfaceFlag * Place directly on the surface. * @return true if the position is valid, else false. * */ bool SurfaceProjectedItem::getProjectedPosition(const SurfaceFile& surfaceFile, float xyzOut[3], const bool isUnprojectedOntoSurface) const { bool valid = false; if (valid == false) { if (this->barycentricProjection->isValid()) { valid = this->barycentricProjection->unprojectToSurface(surfaceFile, xyzOut, 0.0, isUnprojectedOntoSurface); } } if (valid == false) { if (this->vanEssenProjection->isValid()) { valid = this->vanEssenProjection->unprojectToSurface(surfaceFile, xyzOut, 0.0, isUnprojectedOntoSurface); } } if (valid == false) { if (this->stereotaxicXYZValid) { this->getStereotaxicXYZ(xyzOut); valid = true; } } return valid; } /** * Get the projected position of this item. The item is unprojected * to the surface and then it is placed above (below if negative) * the surface by the amount specified by 'distanceAboveSurface'. * * The first valid of this positions is used: (1) Barycentric, * (2) VanEssen, (3) Stereotaxic. * * @param surfaceFile * Surface File for positioning. * @param xyzOut * Output containing the projected position. * @param distanceAboveSurface * Unproje * @return true if the position is valid, else false. * */ bool SurfaceProjectedItem::getProjectedPositionAboveSurface(const SurfaceFile& surfaceFile, float xyzOut[3], const float distanceAboveSurface) const { bool valid = false; if (valid == false) { if (this->barycentricProjection->isValid()) { valid = this->barycentricProjection->unprojectToSurface(surfaceFile, xyzOut, distanceAboveSurface, true); } } if (valid == false) { if (this->vanEssenProjection->isValid()) { valid = this->vanEssenProjection->unprojectToSurface(surfaceFile, xyzOut, distanceAboveSurface, true); } } if (valid == false) { if (this->stereotaxicXYZValid) { this->getStereotaxicXYZ(xyzOut); valid = true; } } return valid; } /** * Get the stereotaxic position. * * @return Stereotaxic position. * */ const float* SurfaceProjectedItem::getStereotaxicXYZ() const { return this->stereotaxicXYZ; } /** * Get the Stereotaxic XYZ position. * @param stereotaxicXYZOut Position placed into here. * */ void SurfaceProjectedItem::getStereotaxicXYZ(float stereotaxicXYZOut[3]) const { stereotaxicXYZOut[0] = this->stereotaxicXYZ[0]; stereotaxicXYZOut[1] = this->stereotaxicXYZ[1]; stereotaxicXYZOut[2] = this->stereotaxicXYZ[2]; } /** * Get the validity of the Stereotaxic XYZ coordinate. * @return Validity of Stereotaxic XYZ coordinate. * */ bool SurfaceProjectedItem::isStereotaxicXYZValid() const { return this->stereotaxicXYZValid; } /** * Set the items stereotaxic coordinates and sets the validity * of the stereotaxic coordinates to true. * * @param stereotaxicXYZ New position. * */ void SurfaceProjectedItem::setStereotaxicXYZ(const float stereotaxicXYZ[3]) { if (this->stereotaxicXYZValid) { } this->stereotaxicXYZ[0] = stereotaxicXYZ[0]; this->stereotaxicXYZ[1] = stereotaxicXYZ[1]; this->stereotaxicXYZ[2] = stereotaxicXYZ[2]; this->stereotaxicXYZValid = true; if (this->volumeXYZValid == false) { this->setVolumeXYZ(stereotaxicXYZ); } this->setModified(); } /** * Get the value of volumeXYZ * * @return the value of volumeXYZ * */ const float* SurfaceProjectedItem::getVolumeXYZ() const { /* * If not set, return stereotaxic coordinate */ if ((volumeXYZ[0] == 0.0) && (volumeXYZ[1] == 0.0) && (volumeXYZ[2] == 0.0)) { const float* stereoXYZ = getStereotaxicXYZ(); return stereoXYZ; } return this->volumeXYZ; } /** * Get the volume XYZ coordinates. * @param xyzOut Volume XYZ coordinates. * */ void SurfaceProjectedItem::getVolumeXYZ(float xyzOut[3]) const { /* * If not set, return stereotaxic coordinate */ if ((volumeXYZ[0] == 0.0) && (volumeXYZ[1] == 0.0) && (volumeXYZ[2] == 0.0)) { getStereotaxicXYZ(xyzOut); return; } xyzOut[0] = this->volumeXYZ[0]; xyzOut[1] = this->volumeXYZ[1]; xyzOut[2] = this->volumeXYZ[2]; } /** * Get the validity of the volume XYZ coordinate. * @return Validity of volume XYZ coordinate. * */ bool SurfaceProjectedItem::isVolumeXYZValid() const { return this->volumeXYZValid; } /** * Set the item's volume coordinates and sets the validity * of the volume coordinates to true. * * @param volumeXYZ new value of volumeXYZ * */ void SurfaceProjectedItem::setVolumeXYZ(const float volumeXYZ[3]) { this->volumeXYZ[0] = volumeXYZ[0]; this->volumeXYZ[1] = volumeXYZ[1]; this->volumeXYZ[2] = volumeXYZ[2]; this->volumeXYZValid = true; this->setModified(); } /** * Get the structure of this projected item. * @return The structure. * */ StructureEnum::Enum SurfaceProjectedItem::getStructure() const { return this->structure; } /** * Set the structure of this projected item. * @param s - new structure. * */ void SurfaceProjectedItem::setStructure(const StructureEnum::Enum structure) { this->structure = structure; this->setModified(); } /** * @return the barycentric projection */ SurfaceProjectionBarycentric* SurfaceProjectedItem::getBarycentricProjection() { return this->barycentricProjection; } /** * @return the barycentric projection */ const SurfaceProjectionBarycentric* SurfaceProjectedItem::getBarycentricProjection() const { return this->barycentricProjection; } /** * @return the Van Essen projection */ SurfaceProjectionVanEssen* SurfaceProjectedItem::getVanEssenProjection() { return this->vanEssenProjection; } /** * @return the Van Essen projection */ const SurfaceProjectionVanEssen* SurfaceProjectedItem::getVanEssenProjection() const { return this->vanEssenProjection; } /** * Write the border to the XML Writer. * @param xmlWriter * Writer for XML output. */ void SurfaceProjectedItem::writeAsXML(XmlWriter& xmlWriter) { xmlWriter.writeStartElement(XML_TAG_SURFACE_PROJECTED_ITEM); xmlWriter.writeElementCharacters(XML_TAG_STRUCTURE, StructureEnum::toName(this->structure)); if (this->stereotaxicXYZValid) { xmlWriter.writeElementCharacters(XML_TAG_STEREOTAXIC_XYZ, this->stereotaxicXYZ, 3); } if (this->volumeXYZValid) { xmlWriter.writeElementCharacters(XML_TAG_VOLUME_XYZ, this->volumeXYZ, 3); } this->barycentricProjection->writeAsXML(xmlWriter); this->vanEssenProjection->writeAsXML(xmlWriter); xmlWriter.writeEndElement(); } void SurfaceProjectedItem::readBorderFileXML1(QXmlStreamReader& xml) { reset(); CaretAssert(xml.isStartElement() && xml.name() == "SurfaceProjectedItem"); bool haveStructure = false, haveVanEssen = false, haveStereo = false, haveVolume = false;//track the barycentric projection for being specified more than once by its valid flag for (xml.readNext(); !xml.atEnd() && !xml.isEndElement(); xml.readNext()) { switch (xml.tokenType()) { case QXmlStreamReader::StartElement: { QStringRef name = xml.name(); if (name == "Structure") { if (haveStructure) throw DataFileException("multiple Structure elements in one SurfaceProjectedItem element"); QString structString = xml.readElementText();//sets error on unexpected child element if (xml.hasError()) throw DataFileException("XML parsing error in Structure: " + xml.errorString()); bool ok = false; structure = StructureEnum::fromName(structString, &ok); if (!ok) CaretLogWarning("unrecognized string in Structure: " + structString);//HACK: this is what the SAX reader did, don't look at me haveStructure = true; } else if (name == "ProjectionBarycentric") { if (barycentricProjection->isValid()) throw DataFileException("multiple ProjectionBarycentric elements in one SurfaceProjectedItem element"); barycentricProjection->readBorderFileXML1(xml); } else if (name == "VanEssenProjection") { if (haveVanEssen) throw DataFileException("multiple VanEssenProjection elements in one SurfaceProjectedItem element"); CaretLogFine("found Van Essen projection in border file, ignoring"); xml.readElementText(QXmlStreamReader::SkipChildElements);//HACK: border files never use this projection type, so don't try to parse it if (xml.hasError()) throw DataFileException("XML parsing error in VanEssenProjection: " + xml.errorString()); haveVanEssen = true; } else if (name == "StereotaxicXYZ") { if (haveStereo) throw DataFileException("multiple StereotaxicXYZ elements in one SurfaceProjectedItem element"); CaretLogFine("found stereotaxic coordinates in border file, ignoring"); xml.readElementText(QXmlStreamReader::SkipChildElements);//HACK: ditto if (xml.hasError()) throw DataFileException("XML parsing error in StereotaxicXYZ: " + xml.errorString()); haveStereo = true; } else if (name == "VolumeXYZ") { if (haveVolume) throw DataFileException("multiple VolumeXYZ elements in one SurfaceProjectedItem element"); CaretLogFine("found volume coordinates in border file, ignoring"); xml.readElementText(QXmlStreamReader::SkipChildElements);//HACK: ditto if (xml.hasError()) throw DataFileException("XML parsing error in VolumeXYZ: " + xml.errorString()); haveVolume = true; } else { throw DataFileException("unexpected element in SurfaceProjectedItem: " + name.toString()); } break; } default: break; } } if (xml.hasError()) throw DataFileException("XML parsing error in SurfaceProjectedItem: " + xml.errorString()); CaretAssert(xml.isEndElement() && xml.name() == "SurfaceProjectedItem"); if (!haveStructure) throw DataFileException("SurfaceProjectedItem is missing Structure element"); } /** * Set the status to unmodified. */ void SurfaceProjectedItem::clearModified() { CaretObjectTracksModification::clearModified(); this->barycentricProjection->clearModified(); this->vanEssenProjection->clearModified(); } /** * Is the object modified? * @return true if modified, else false. */ bool SurfaceProjectedItem::isModified() const { if (CaretObjectTracksModification::isModified()) { return true; } if (this->barycentricProjection->isModified()) { return true; } if (this->vanEssenProjection->isModified()) { return true; } return false; } /** * @return True if a projection (barycentric, vanessen) * is valid. Otherwise, false. */ bool SurfaceProjectedItem::hasValidProjection() const { if (barycentricProjection->isValid()) { return true; } if (vanEssenProjection->isValid()) { return true; } return false; } connectome-workbench-1.2.3+git41-gc4c6c90/src/Files/SurfaceProjectedItem.h000066400000000000000000000120121300200146000260220ustar00rootroot00000000000000#ifndef __SURFACE_PROJECTED_ITEM_H__ #define __SURFACE_PROJECTED_ITEM_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 "CaretObjectTracksModification.h" #include "StructureEnum.h" #include "XmlException.h" class QXmlStreamReader; namespace caret { class SurfaceFile; class SurfaceProjectionBarycentric; class SurfaceProjectionVanEssen; class XmlWriter; class SurfaceProjectedItem : public CaretObjectTracksModification { public: SurfaceProjectedItem(); SurfaceProjectedItem(const SurfaceProjectedItem& o); SurfaceProjectedItem& operator=(const SurfaceProjectedItem& o); bool operator==(const SurfaceProjectedItem& rhs) const; bool operator!=(const SurfaceProjectedItem& rhs) const { return !(*this == rhs); } virtual ~SurfaceProjectedItem(); private: void copyHelper(const SurfaceProjectedItem& o); void initializeMembersSurfaceProjectedItem(); public: void unprojectToStereotaxicXYZ(const SurfaceFile& sf, const bool isUnprojectedOntoSurface); void unprojectToVolumeXYZ(const SurfaceFile& sf, const bool isUnprojectedOntoSurface); bool getProjectedPosition(const SurfaceFile& sf, float xyzOut[3], const bool isUnprojectedOntoSurface) const; bool getProjectedPositionAboveSurface(const SurfaceFile& sf, float xyzOut[3], const float distanceAboveSurface) const; const float* getStereotaxicXYZ() const; void getStereotaxicXYZ(float stereotaxicXYZOut[3]) const; bool isStereotaxicXYZValid() const; void setStereotaxicXYZ(const float stereotaxicXYZ[3]); const float* getVolumeXYZ() const; void getVolumeXYZ(float xyzOut[3]) const; bool isVolumeXYZValid() const; void setVolumeXYZ(const float volumeXYZ[3]); StructureEnum::Enum getStructure() const; void setStructure(const StructureEnum::Enum structure); const SurfaceProjectionBarycentric* getBarycentricProjection() const; SurfaceProjectionBarycentric* getBarycentricProjection(); const SurfaceProjectionVanEssen* getVanEssenProjection() const; SurfaceProjectionVanEssen* getVanEssenProjection(); bool hasValidProjection() const; void reset(); void writeAsXML(XmlWriter& xmlWriter); void readBorderFileXML1(QXmlStreamReader& xml); virtual void clearModified(); virtual bool isModified() const; static AString XML_TAG_SURFACE_PROJECTED_ITEM; static AString XML_TAG_STEREOTAXIC_XYZ; static AString XML_TAG_VOLUME_XYZ; static AString XML_TAG_STRUCTURE; protected: /** stereotaxic position of projected item. */ float stereotaxicXYZ[3]; /** stereotaxic position of projected item valid */ bool stereotaxicXYZValid; /** position in volume */ float volumeXYZ[3]; /** position in volume valid */ bool volumeXYZValid; /** Structure to which projected. */ StructureEnum::Enum structure; /** The barycentric projection */ SurfaceProjectionBarycentric* barycentricProjection; /** The Van Essen projection */ SurfaceProjectionVanEssen* vanEssenProjection; }; #ifdef __SURFACE_PROJECTED_ITEM_DEFINE__ AString SurfaceProjectedItem::XML_TAG_SURFACE_PROJECTED_ITEM = "SurfaceProjectedItem"; AString SurfaceProjectedItem::XML_TAG_STEREOTAXIC_XYZ = "StereotaxicXYZ"; AString SurfaceProjectedItem::XML_TAG_VOLUME_XYZ = "VolumeXYZ"; AString SurfaceProjectedItem::XML_TAG_STRUCTURE = "Structure"; #endif // __SURFACE_PROJECTED_ITEM_DEFINE__ } // namespace #endif // __SURFACE_PROJECTED_ITEM_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Files/SurfaceProjectedItemSaxReader.cxx000066400000000000000000000334151300200146000302060ustar00rootroot00000000000000/*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 "CaretLogger.h" #include "SurfaceProjectedItem.h" #include "SurfaceProjectedItemSaxReader.h" #include "SurfaceProjectionBarycentric.h" #include "SurfaceProjectionVanEssen.h" #include "XmlAttributes.h" #include "XmlException.h" #include "XmlUtilities.h" using namespace caret; /** * constructor. */ SurfaceProjectedItemSaxReader::SurfaceProjectedItemSaxReader(SurfaceProjectedItem* surfaceProjectedItem) { this->state = STATE_NONE; this->stateStack.push(this->state); this->elementText = ""; this->surfaceProjectedItem = surfaceProjectedItem; } /** * destructor. */ SurfaceProjectedItemSaxReader::~SurfaceProjectedItemSaxReader() { } /** * start an element. */ void SurfaceProjectedItemSaxReader::startElement(const AString& /* namespaceURI */, const AString& /* localName */, const AString& qName, const XmlAttributes& /*attributes*/) { const STATE previousState = this->state; switch (state) { case STATE_NONE: if (qName == SurfaceProjectedItem::XML_TAG_SURFACE_PROJECTED_ITEM) { this->state = STATE_SURFACE_PROJECTED_ITEM; } else { AString txt = XmlUtilities::createInvalidRootElementMessage(SurfaceProjectedItem::XML_TAG_SURFACE_PROJECTED_ITEM, qName); XmlSaxParserException e(txt); CaretLogThrowing(e); throw e; } break; case STATE_SURFACE_PROJECTED_ITEM: if (qName == SurfaceProjectionBarycentric::XML_TAG_PROJECTION_BARYCENTRIC) { state = STATE_BARYCENTRIC; } else if (qName == SurfaceProjectionVanEssen::XML_TAG_PROJECTION_VAN_ESSEN) { state = STATE_VAN_ESSEN; } else if ((qName == SurfaceProjectedItem::XML_TAG_STEREOTAXIC_XYZ) || (qName == SurfaceProjectedItem::XML_TAG_STRUCTURE) || (qName == SurfaceProjectedItem::XML_TAG_VOLUME_XYZ)) { // nothing } else { AString txt = XmlUtilities::createInvalidChildElementMessage(SurfaceProjectedItem::XML_TAG_SURFACE_PROJECTED_ITEM, qName); XmlSaxParserException e(txt); CaretLogThrowing(e); throw e; } break; case STATE_BARYCENTRIC: if ((qName == SurfaceProjectionBarycentric::XML_TAG_SIGNED_DISTANCE_ABOVE_SURFACE) || (qName == SurfaceProjectionBarycentric::XML_TAG_TRIANGLE_AREAS) || (qName == SurfaceProjectionBarycentric::XML_TAG_TRIANGLE_NODES)) { // nothing } else { AString txt = XmlUtilities::createInvalidChildElementMessage(SurfaceProjectionBarycentric::XML_TAG_PROJECTION_BARYCENTRIC, qName); XmlSaxParserException e(txt); CaretLogThrowing(e); throw e; } break; case STATE_VAN_ESSEN: if ((qName == SurfaceProjectionVanEssen::XML_TAG_DR) || (qName == SurfaceProjectionVanEssen::XML_TAG_FRAC_RI) || (qName == SurfaceProjectionVanEssen::XML_TAG_FRAC_RJ) || (qName == SurfaceProjectionVanEssen::XML_TAG_PHI_R) || (qName == SurfaceProjectionVanEssen::XML_TAG_POS_ANATOMICAL) || (qName == SurfaceProjectionVanEssen::XML_TAG_PROJECTION_VAN_ESSEN) || (qName == SurfaceProjectionVanEssen::XML_TAG_THETA_R) || (qName == SurfaceProjectionVanEssen::XML_TAG_TRI_ANATOMICAL) || (qName == SurfaceProjectionVanEssen::XML_TAG_TRI_VERTICES) || (qName == SurfaceProjectionVanEssen::XML_TAG_VERTEX) || (qName == SurfaceProjectionVanEssen::XML_TAG_VERTEX_ANATOMICAL)) { // nothing } else { AString txt = XmlUtilities::createInvalidChildElementMessage(SurfaceProjectionVanEssen::XML_TAG_PROJECTION_VAN_ESSEN, qName); XmlSaxParserException e(txt); CaretLogThrowing(e); throw e; } break; } // // Save previous state // this->stateStack.push(previousState); this->elementText = ""; } /** * end an element. */ void SurfaceProjectedItemSaxReader::endElement(const AString& /* namspaceURI */, const AString& /* localName */, const AString& qName) { const AString text = this->elementText.trimmed(); switch (state) { case STATE_NONE: break; case STATE_SURFACE_PROJECTED_ITEM: if (qName == SurfaceProjectedItem::XML_TAG_STEREOTAXIC_XYZ) { std::vector xyz; XmlUtilities::getArrayOfNumbersFromText(qName, text, 3, xyz); this->surfaceProjectedItem->setStereotaxicXYZ(xyz.data()); } else if (qName == SurfaceProjectedItem::XML_TAG_STRUCTURE) { bool isValid = false; this->surfaceProjectedItem->setStructure(StructureEnum::fromName(text, &isValid)); if (isValid == false) { CaretLogWarning("Invalid structure name: " + text); throw XmlSaxParserException("Invalid structure name: " + text); } } else if (qName == SurfaceProjectedItem::XML_TAG_VOLUME_XYZ) { std::vector xyz; XmlUtilities::getArrayOfNumbersFromText(qName, text, 3, xyz); this->surfaceProjectedItem->setVolumeXYZ(xyz.data()); } break; case STATE_BARYCENTRIC: { SurfaceProjectionBarycentric* bp = this->surfaceProjectedItem->getBarycentricProjection(); if (qName == SurfaceProjectionBarycentric::XML_TAG_SIGNED_DISTANCE_ABOVE_SURFACE) { bp->setSignedDistanceAboveSurface(text.toFloat()); } else if (qName == SurfaceProjectionBarycentric::XML_TAG_TRIANGLE_AREAS) { std::vector areas; XmlUtilities::getArrayOfNumbersFromText(qName, text, 3, areas); bp->setTriangleAreas(areas.data()); } else if (qName == SurfaceProjectionBarycentric::XML_TAG_TRIANGLE_NODES) { std::vector nodes; XmlUtilities::getArrayOfNumbersFromText(qName, text, 3, nodes); bp->setTriangleNodes(nodes.data()); } bp->setValid(true); } break; case STATE_VAN_ESSEN: { SurfaceProjectionVanEssen* ve = this->surfaceProjectedItem->getVanEssenProjection(); if (qName == SurfaceProjectionVanEssen::XML_TAG_DR) { ve->setDR(text.toFloat()); } else if (qName == SurfaceProjectionVanEssen::XML_TAG_FRAC_RI) { ve->setFracRI(text.toFloat()); } else if (qName == SurfaceProjectionVanEssen::XML_TAG_FRAC_RJ) { ve->setFracRJ(text.toFloat()); } else if (qName == SurfaceProjectionVanEssen::XML_TAG_PHI_R) { ve->setPhiR(text.toFloat()); } else if (qName == SurfaceProjectionVanEssen::XML_TAG_POS_ANATOMICAL) { std::vector xyz; XmlUtilities::getArrayOfNumbersFromText(qName, text, 3, xyz); ve->setPosAnatomical(xyz.data()); } else if (qName == SurfaceProjectionVanEssen::XML_TAG_THETA_R) { ve->setThetaR(text.toFloat()); } else if (qName == SurfaceProjectionVanEssen::XML_TAG_TRI_ANATOMICAL) { std::vector data; XmlUtilities::getArrayOfNumbersFromText(qName, text, 18, data); float ta[2][3][3]; int32_t ctr = 0; for (int32_t i = 0; i < 2; i++) { for (int32_t j = 0; j < 3; j++) { for (int32_t k = 0; k < 3; k++) { ta[i][j][k] = data[ctr]; ctr++; } } } ve->setTriAnatomical(ta); } else if (qName == SurfaceProjectionVanEssen::XML_TAG_TRI_VERTICES) { std::vector data; XmlUtilities::getArrayOfNumbersFromText(qName, text, 6, data); int32_t tv[2][3]; int32_t ctr = 0; for (int32_t i = 0; i < 2; i++) { for (int32_t j = 0; j < 3; j++) { tv[i][j] = data[ctr]; ctr++; } } ve->setTriVertices(tv); } else if (qName == SurfaceProjectionVanEssen::XML_TAG_VERTEX) { std::vector data; XmlUtilities::getArrayOfNumbersFromText(qName, text, 2, data); int32_t tv[2]; int32_t ctr = 0; for (int32_t i = 0; i < 2; i++) { tv[i] = data[ctr]; ctr++; } ve->setVertex(tv); } else if (qName == SurfaceProjectionVanEssen::XML_TAG_VERTEX_ANATOMICAL) { std::vector data; XmlUtilities::getArrayOfNumbersFromText(qName, text, 6, data); float va[2][3]; int32_t ctr = 0; for (int32_t i = 0; i < 2; i++) { for (int32_t j = 0; j < 3; j++) { va[i][j] = data[ctr]; ctr++; } } ve->setVertexAnatomical(va); } ve->setValid(true); } break; } // // Clear out for new elements // this->elementText = ""; // // Go to previous state // if (this->stateStack.empty()) { throw XmlSaxParserException("State stack is empty while reading XML MetaData."); } this->state = this->stateStack.top(); this->stateStack.pop(); } /** * get characters in an element. */ void SurfaceProjectedItemSaxReader::characters(const char* ch) { this->elementText += ch; } /** * a fatal error occurs. */ void SurfaceProjectedItemSaxReader::fatalError(const XmlSaxParserException& e) { // // Stop parsing // CaretLogSevere("XML Parser Fatal Error: " + e.whatString()); throw e; } // a warning occurs void SurfaceProjectedItemSaxReader::warning(const XmlSaxParserException& e) { CaretLogWarning("XML Parser Warning: " + e.whatString()); } // an error occurs void SurfaceProjectedItemSaxReader::error(const XmlSaxParserException& e) { throw e; } void SurfaceProjectedItemSaxReader::startDocument() { } void SurfaceProjectedItemSaxReader::endDocument() { } connectome-workbench-1.2.3+git41-gc4c6c90/src/Files/SurfaceProjectedItemSaxReader.h000066400000000000000000000064571300200146000276410ustar00rootroot00000000000000 #ifndef __SURFACE_PROJECTED_ITEM_SAX_READER_H__ #define __SURFACE_PROJECTED_ITEM_SAX_READER_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 "XmlSaxParserException.h" #include "XmlSaxParserHandlerInterface.h" namespace caret { class SurfaceProjectedItem; class XmlAttributes; /** * class for reading SurfaceProjectedItem with a SAX Parser */ class SurfaceProjectedItemSaxReader : public CaretObject, public XmlSaxParserHandlerInterface { public: SurfaceProjectedItemSaxReader(SurfaceProjectedItem* surfaceProjectedItem); virtual ~SurfaceProjectedItemSaxReader(); void startElement(const AString& namespaceURI, const AString& localName, const AString& qName, const XmlAttributes& attributes); void endElement(const AString& namspaceURI, const AString& localName, const AString& qName); void characters(const char* ch); void fatalError(const XmlSaxParserException& e); void warning(const XmlSaxParserException& e); void error(const XmlSaxParserException& e); void startDocument(); void endDocument(); private: SurfaceProjectedItemSaxReader(const SurfaceProjectedItemSaxReader&); SurfaceProjectedItemSaxReader& operator=(const SurfaceProjectedItemSaxReader&); protected: /// file reading states enum STATE { /// no state STATE_NONE, /// processing SurfaceProjectedItem tags STATE_SURFACE_PROJECTED_ITEM, /// processing Barycentric tags STATE_BARYCENTRIC, /// processing Van Essen tags STATE_VAN_ESSEN }; /// file reading state STATE state; /// the state stack used when reading a file std::stack stateStack; /// the error message AString errorMessage; /// meta data name AString metaDataName; /// meta data value AString metaDataValue; /// element text AString elementText; /// GIFTI meta data being read SurfaceProjectedItem* surfaceProjectedItem; }; } // namespace #endif // __SURFACE_PROJECTED_ITEM_SAX_READER_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Files/SurfaceProjection.cxx000066400000000000000000000054571300200146000257720ustar00rootroot00000000000000 /*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 __SURFACE_PROJECTION_DECLARE__ #include "SurfaceProjection.h" #undef __SURFACE_PROJECTION_DECLARE__ using namespace caret; /** * \class caret::SurfaceProjection * \brief Abstract class for Surface Projections. */ /** * Constructor. */ SurfaceProjection::SurfaceProjection() : CaretObjectTracksModification() { this->projectionSurfaceNumberOfNodes = 0; } /** * Destructor. */ SurfaceProjection::~SurfaceProjection() { } /** * Copy constructor. * @param obj * Object that is copied. */ SurfaceProjection::SurfaceProjection(const SurfaceProjection& obj) : CaretObjectTracksModification(obj) { this->copyHelperSurfaceProjection(obj); } /** * Assignment operator. * @param obj * Data copied from obj to this. * @return * Reference to this object. */ SurfaceProjection& SurfaceProjection::operator=(const SurfaceProjection& obj) { if (this != &obj) { CaretObjectTracksModification::operator=(obj); this->copyHelperSurfaceProjection(obj); } return *this; } /** * Helps with copying an object of this type. * @param obj * Object that is copied. */ void SurfaceProjection::copyHelperSurfaceProjection(const SurfaceProjection& obj) { this->projectionSurfaceNumberOfNodes = obj.projectionSurfaceNumberOfNodes; } /** * @return The number of nodes in the surface to which this projection is made. */ int32_t SurfaceProjection::getProjectionSurfaceNumberOfNodes() const { return this->projectionSurfaceNumberOfNodes; } /** * Set the number of nodes in the surface to which this projection is made. * @param projectionSurfaceNumberOfNodes * Number of nodes in the surface. */ void SurfaceProjection::setProjectionSurfaceNumberOfNodes(const int projectionSurfaceNumberOfNodes) { this->projectionSurfaceNumberOfNodes = projectionSurfaceNumberOfNodes; } /** * @return a string describing the projection */ AString SurfaceProjection::toString() const { return CaretObjectTracksModification::toString(); } connectome-workbench-1.2.3+git41-gc4c6c90/src/Files/SurfaceProjection.h000066400000000000000000000072531300200146000254130ustar00rootroot00000000000000#ifndef __SURFACE_PROJECTION__H_ #define __SURFACE_PROJECTION__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 "CaretObjectTracksModification.h" #include "XmlException.h" namespace caret { class SurfaceFile; class XmlWriter; class SurfaceProjection : public CaretObjectTracksModification { public: SurfaceProjection(); virtual ~SurfaceProjection(); SurfaceProjection(const SurfaceProjection& obj); SurfaceProjection& operator=(const SurfaceProjection& obj); /** * @return Is the projection valid? */ virtual bool isValid() const = 0; /** * Set the validity of the projection. * @param valid * New validity status. */ virtual void setValid(const bool valid) = 0; /** * Reset the surface projection to its initial state. */ virtual void reset() = 0; /** * 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. * @return * True if unprojection is successful, else false. */ virtual bool unprojectToSurface(const SurfaceFile& surfaceFile, float xyzOut[3], const float offsetFromSurface, const bool unprojectWithOffsetFromSurface) const = 0; int32_t getProjectionSurfaceNumberOfNodes() const; void setProjectionSurfaceNumberOfNodes(const int surfaceNumberOfNodes); /** * Write the projection to XML. * @param xmlWriter * The XML Writer. * @throw XmlException * If an error occurs. */ virtual void writeAsXML(XmlWriter& xmlWriter) = 0; /* @return a string describing the projection */ virtual AString toString() const; protected: /** Number of nodes in surface to which item is projected. */ int32_t projectionSurfaceNumberOfNodes; private: void copyHelperSurfaceProjection(const SurfaceProjection& obj); }; #ifdef __SURFACE_PROJECTION_DECLARE__ // #endif // __SURFACE_PROJECTION_DECLARE__ } // namespace #endif //__SURFACE_PROJECTION__H_ connectome-workbench-1.2.3+git41-gc4c6c90/src/Files/SurfaceProjectionBarycentric.cxx000066400000000000000000000366771300200146000301700ustar00rootroot00000000000000 /*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 __SURFACE_PROJECTION_BARYCENTRIC_DECLARE__ #include "SurfaceProjectionBarycentric.h" #undef __SURFACE_PROJECTION_BARYCENTRIC_DECLARE__ #include "CaretAssert.h" #include "DataFileException.h" #include "MathFunctions.h" #include "SurfaceFile.h" #include "TopologyHelper.h" #include "XmlWriter.h" #include #include using namespace caret; /** * \class caret::SurfaceProjectionBarycentric * \brief Maintains a barycentric projection. * */ /** * Constructor. */ SurfaceProjectionBarycentric::SurfaceProjectionBarycentric() : SurfaceProjection() { this->resetAllValues(); } /** * Destructor. */ SurfaceProjectionBarycentric::~SurfaceProjectionBarycentric() { } /** * Copy constructor. * @param obj * Object that is copied. */ SurfaceProjectionBarycentric::SurfaceProjectionBarycentric(const SurfaceProjectionBarycentric& obj) : SurfaceProjection(obj) { this->copyHelperSurfaceProjectionBarycentric(obj); } /** * Assignment operator. * @param obj * Data copied from obj to this. * @return * Reference to this object. */ SurfaceProjectionBarycentric& SurfaceProjectionBarycentric::operator=(const SurfaceProjectionBarycentric& obj) { if (this != &obj) { SurfaceProjection::operator=(obj); this->copyHelperSurfaceProjectionBarycentric(obj); } return *this; } bool SurfaceProjectionBarycentric::operator==(const SurfaceProjectionBarycentric& rhs) { if (projectionValid != rhs.projectionValid) return false; if (projectionValid) { for (int i = 0; i < 3; ++i) { if (triangleAreas[i] != rhs.triangleAreas[i]) return false; if (triangleNodes[i] != rhs.triangleNodes[i]) return false; } if (m_degenerate != rhs.m_degenerate) return false; return (signedDistanceAboveSurface == rhs.signedDistanceAboveSurface); } return true; } /** * Helps with copying an object of this type. * @param obj * Object that is copied. */ void SurfaceProjectionBarycentric::copyHelperSurfaceProjectionBarycentric(const SurfaceProjectionBarycentric& obj) { this->setTriangleAreas(obj.getTriangleAreas()); this->setTriangleNodes(obj.getTriangleNodes()); this->signedDistanceAboveSurface = obj.signedDistanceAboveSurface; this->projectionValid = obj.projectionValid; this->m_degenerate = obj.m_degenerate; } /** * Get a description of this object's content. * @return String describing this object's content. */ AString SurfaceProjectionBarycentric::toString() const { AString txt = SurfaceProjection::toString(); if (txt.isEmpty() == false) { txt += ", "; } txt += ("projectionValid=" + AString::fromBool(projectionValid) + ", triangleAreas=(" + AString::fromNumbers(this->triangleAreas, 3, ",") + "), triangleNodes=(" + AString::fromNumbers(this->triangleNodes, 3, ",") + "), signedDistanceAboveSurface=" + AString::number(this->signedDistanceAboveSurface)); return txt; } /** * @return The signed distance above the surface. */ float SurfaceProjectionBarycentric::getSignedDistanceAboveSurface() const { return this->signedDistanceAboveSurface; } /** * Set the signed distance above the surface. * * @param signedDistanceAboveSurface * New value. */ void SurfaceProjectionBarycentric::setSignedDistanceAboveSurface(const float signedDistanceAboveSurface) { this->signedDistanceAboveSurface = signedDistanceAboveSurface; this->setModified(); } /** * @return The triangle nodes (3 elements). */ const int32_t* SurfaceProjectionBarycentric::getTriangleNodes() const { return this->triangleNodes; } /** * Set the triangle nodes. * * @param triangleNodes * New values for nodes. */ void SurfaceProjectionBarycentric::setTriangleNodes(const int32_t triangleNodes[3]) { this->triangleNodes[0] = triangleNodes[0]; this->triangleNodes[1] = triangleNodes[1]; this->triangleNodes[2] = triangleNodes[2]; this->setModified(); } /** * @return The triangle areas (3 elements). */ const float* SurfaceProjectionBarycentric::getTriangleAreas() const { return this->triangleAreas; } int32_t SurfaceProjectionBarycentric::getNodeWithLargestWeight() const { int32_t ret = -1; float largestWeight = 0.0f;//there must be a positive weight for (int i = 0; i < 3; ++i) { if (triangleAreas[i] > largestWeight) { ret = triangleNodes[i]; largestWeight = triangleAreas[i]; } } return ret; } /** * Set the triangle areas. * * @param triangleAreas * New values for triangle areas. */ void SurfaceProjectionBarycentric::setTriangleAreas(const float triangleAreas[3]) { this->triangleAreas[0] = triangleAreas[0]; this->triangleAreas[1] = triangleAreas[1]; this->triangleAreas[2] = triangleAreas[2]; this->setModified(); } /** * 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. * @return * True if unprojection was successful. */ bool SurfaceProjectionBarycentric::unprojectToSurface(const SurfaceFile& surfaceFile, 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 = 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; } /** * Reset the surface projection to its initial state. */ void SurfaceProjectionBarycentric::reset() { this->resetAllValues(); } /** * @return Is the projection valid? */ bool SurfaceProjectionBarycentric::isValid() const { return this->projectionValid; } /** * Set the validity of the projection. * @param valid * New validity status. */ void SurfaceProjectionBarycentric::setValid(const bool valid) { this->projectionValid = valid; } /** * Set the projection is degenerate (on * an edge or just outside the edge). * @param degenerate * New status. */ void SurfaceProjectionBarycentric::setDegenerate(const bool degenerate) { m_degenerate = degenerate; } /** * @return Is the projection degenerate (on * an edge or just outside the edge). */ bool SurfaceProjectionBarycentric::isDegenerate() const { return m_degenerate; } /** * Since reset overrides the 'super' class it should * never be called from a constructor. So, this * method does the actual reset, and since it does * not override a method from the 'super' class, it * may be called from this class' constructor. */ void SurfaceProjectionBarycentric::resetAllValues() { this->projectionValid = false; m_degenerate = false; this->triangleAreas[0] = -1.0; this->triangleAreas[1] = -1.0; this->triangleAreas[2] = -1.0; this->triangleNodes[0] = -1; this->triangleNodes[1] = -1; this->triangleNodes[2] = -1; this->signedDistanceAboveSurface = 0.0; } /** * Write the projection to XML. * @param xmlWriter * The XML Writer. * @throw XmlException * If an error occurs. */ void SurfaceProjectionBarycentric::writeAsXML(XmlWriter& xmlWriter) { /* * Note: Degenerate status is not saved! */ if (this->projectionValid) { xmlWriter.writeStartElement(XML_TAG_PROJECTION_BARYCENTRIC); xmlWriter.writeElementCharacters(XML_TAG_TRIANGLE_AREAS, this->triangleAreas, 3); xmlWriter.writeElementCharacters(XML_TAG_TRIANGLE_NODES, this->triangleNodes, 3); xmlWriter.writeElementCharacters(XML_TAG_SIGNED_DISTANCE_ABOVE_SURFACE, this->signedDistanceAboveSurface); xmlWriter.writeEndElement(); } } void SurfaceProjectionBarycentric::readBorderFileXML1(QXmlStreamReader& xml) { reset(); CaretAssert(xml.isStartElement() && xml.name() == "ProjectionBarycentric"); bool haveAreas = false, haveNodes = false, haveDist = false; for (xml.readNext(); !xml.atEnd() && !xml.isEndElement(); xml.readNext()) { switch (xml.tokenType()) { case QXmlStreamReader::StartElement: { QStringRef name = xml.name(); if (name == "TriangleAreas") { if (haveAreas) throw DataFileException("multiple TriangleAreas elements in one ProjectionBarycentric element"); QString text = xml.readElementText();//errors on unexpected element if (xml.hasError()) throw DataFileException("XML parsing error in TriangleAreas: " + xml.errorString()); QStringList areaStrings = text.split(QRegExp("\\s+"), QString::SkipEmptyParts); if (areaStrings.size() != 3) throw DataFileException("TriangleAreas element must contain 3 numbers separated by whitespace"); bool ok = false; for (int i = 0; i < 3; ++i) { triangleAreas[i] = areaStrings[i].toFloat(&ok); if (!ok) throw DataFileException("found non-numeric string in TriangleAreas: " + areaStrings[i]); } haveAreas = true; } else if (name == "TriangleNodes") { if (haveNodes) throw DataFileException("multiple TriangleNodes elements in one ProjectionBarycentric element"); QString text = xml.readElementText();//errors on unexpected element if (xml.hasError()) throw DataFileException("XML parsing error in TriangleNodes: " + xml.errorString()); QStringList nodeStrings = text.split(QRegExp("\\s+"), QString::SkipEmptyParts); if (nodeStrings.size() != 3) throw DataFileException("TriangleNodes element must contain 3 integers separated by whitespace"); bool ok = false; for (int i = 0; i < 3; ++i) { triangleNodes[i] = nodeStrings[i].toInt(&ok); if (!ok) throw DataFileException("found non-integer string in TriangleNodes: " + nodeStrings[i]); } haveNodes = true; } else if (name == "SignedDistanceAboveSurface") { if (haveDist) throw DataFileException("multiple SignedDistanceAboveSurface elements in one ProjectionBarycentric element"); QString text = xml.readElementText();//errors on unexpected element if (xml.hasError()) throw DataFileException("XML parsing error in SignedDistanceAboveSurface: " + xml.errorString()); bool ok = false; signedDistanceAboveSurface = text.toFloat(&ok); if (!ok) throw DataFileException("found non-numeric string in SignedDistanceAboveSurface: " + text); haveDist = true; } else { throw DataFileException("unexpected element in ProjectionBarycentric: " + name.toString()); } break; } default: break; } } if (xml.hasError()) throw DataFileException("XML parsing error in ProjectionBarycentric: " + xml.errorString()); CaretAssert(xml.isEndElement() && xml.name() == "ProjectionBarycentric"); if (!haveAreas || !haveNodes)//ignore missing distance? should always be zero for BorderFile anyway { throw DataFileException("SurfaceProjectionBarycentric element missing TriangleNodes and/or TriangleAreas"); } projectionValid = true; } connectome-workbench-1.2.3+git41-gc4c6c90/src/Files/SurfaceProjectionBarycentric.h000066400000000000000000000075301300200146000275770ustar00rootroot00000000000000#ifndef __SURFACE_PROJECTION_BARYCENTRIC__H_ #define __SURFACE_PROJECTION_BARYCENTRIC__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 "SurfaceProjection.h" class QXmlStreamReader; namespace caret { class SurfaceProjectionBarycentric : public SurfaceProjection { public: SurfaceProjectionBarycentric(); virtual ~SurfaceProjectionBarycentric(); SurfaceProjectionBarycentric(const SurfaceProjectionBarycentric& obj); SurfaceProjectionBarycentric& operator=(const SurfaceProjectionBarycentric& obj); bool operator==(const SurfaceProjectionBarycentric& rhs); bool operator!=(const SurfaceProjectionBarycentric& rhs) { return !(*this == rhs); } virtual AString toString() const; float getSignedDistanceAboveSurface() const; void setSignedDistanceAboveSurface(const float signedDistanceAboveSurface); const int32_t* getTriangleNodes() const; void setTriangleNodes(const int32_t triangleNodes[3]); const float* getTriangleAreas() const; void setTriangleAreas(const float triangleAreas[3]); int32_t getNodeWithLargestWeight() const; bool unprojectToSurface(const SurfaceFile& surfaceFile, float xyzOut[3], const float offsetFromSurface, const bool unprojectWithOffsetFromSurface) const; void reset(); bool isValid() const; void setDegenerate(const bool degenerate); bool isDegenerate() const; void setValid(const bool valid); void writeAsXML(XmlWriter& xmlWriter); void readBorderFileXML1(QXmlStreamReader& xml); static const AString XML_TAG_PROJECTION_BARYCENTRIC; static const AString XML_TAG_TRIANGLE_NODES; static const AString XML_TAG_TRIANGLE_AREAS; static const AString XML_TAG_SIGNED_DISTANCE_ABOVE_SURFACE; private: void copyHelperSurfaceProjectionBarycentric(const SurfaceProjectionBarycentric& obj); void resetAllValues(); int32_t triangleNodes[3]; float triangleAreas[3]; float signedDistanceAboveSurface; bool projectionValid; bool m_degenerate; }; #ifdef __SURFACE_PROJECTION_BARYCENTRIC_DECLARE__ const AString SurfaceProjectionBarycentric::XML_TAG_PROJECTION_BARYCENTRIC = "ProjectionBarycentric"; const AString SurfaceProjectionBarycentric::XML_TAG_TRIANGLE_NODES = "TriangleNodes"; const AString SurfaceProjectionBarycentric::XML_TAG_TRIANGLE_AREAS = "TriangleAreas"; const AString SurfaceProjectionBarycentric::XML_TAG_SIGNED_DISTANCE_ABOVE_SURFACE = "SignedDistanceAboveSurface"; #endif // __SURFACE_PROJECTION_BARYCENTRIC_DECLARE__ } // namespace #endif //__SURFACE_PROJECTION_BARYCENTRIC__H_ connectome-workbench-1.2.3+git41-gc4c6c90/src/Files/SurfaceProjectionVanEssen.cxx000066400000000000000000000570131300200146000274300ustar00rootroot00000000000000 /*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 #define __SURFACE_PROJECTION_VAN_ESSEN_DECLARE__ #include "SurfaceProjectionVanEssen.h" #undef __SURFACE_PROJECTION_VAN_ESSEN_DECLARE__ #include "CaretAssert.h" #include "CaretLogger.h" #include "MathFunctions.h" #include "SurfaceFile.h" #include "TopologyHelper.h" #include "XmlWriter.h" using namespace caret; /** * \class caret::SurfaceProjectionVanEssen * \brief Maintains a VanEssen Projection that projects to an edge with offset. * */ /** * Constructor. */ SurfaceProjectionVanEssen::SurfaceProjectionVanEssen() : SurfaceProjection() { this->resetAllValues(); } /** * Destructor. */ SurfaceProjectionVanEssen::~SurfaceProjectionVanEssen() { } /** * Copy constructor. * @param obj * Object that is copied. */ SurfaceProjectionVanEssen::SurfaceProjectionVanEssen(const SurfaceProjectionVanEssen& obj) : SurfaceProjection(obj) { this->copyHelperSurfaceProjectionVanEssen(obj); } /** * Assignment operator. * @param obj * Data copied from obj to this. * @return * Reference to this object. */ SurfaceProjectionVanEssen& SurfaceProjectionVanEssen::operator=(const SurfaceProjectionVanEssen& obj) { if (this != &obj) { SurfaceProjection::operator=(obj); this->copyHelperSurfaceProjectionVanEssen(obj); } return *this; } bool SurfaceProjectionVanEssen::operator==(const SurfaceProjectionVanEssen& rhs) { if (projectionValid != rhs.projectionValid) return false; if (projectionValid) { for (int i = 0; i < 2; i++) { for (int j = 0; j < 3; j++) { for (int k = 0; k < 3; k++) { if (triAnatomical[i][j][k] != rhs.triAnatomical[i][j][k]) return false; } if (triVertices[i][j] != rhs.triVertices[i][j]) return false; if (vertexAnatomical[i][j] != rhs.vertexAnatomical[i][j]) return false; } if (vertex[i] != rhs.vertex[i]) return false; } for (int i = 0; i < 3; ++i) { if (posAnatomical[i] != rhs.posAnatomical[i]) return false; } if (thetaR != rhs.thetaR) return false; if (phiR != rhs.phiR) return false; if (fracRI != rhs.fracRI) return false; if (fracRJ != rhs.fracRJ) return false; } return true; } /** * Helps with copying an object of this type. * @param obj * Object that is copied. */ void SurfaceProjectionVanEssen::copyHelperSurfaceProjectionVanEssen(const SurfaceProjectionVanEssen& obj) { this->dR = obj.dR; for (int32_t i = 0; i < 2; i++) { for (int32_t j = 0; j < 3; j++) { for (int32_t k = 0; k < 3; k++) { this->triAnatomical[i][j][k] = obj.triAnatomical[i][j][k]; } this->triVertices[i][j] = obj.triVertices[i][j]; } this->vertex[i] = obj.vertex[i]; } this->vertexAnatomical[0][0] = this->vertexAnatomical[0][0]; this->vertexAnatomical[0][1] = this->vertexAnatomical[0][1]; this->vertexAnatomical[0][2] = this->vertexAnatomical[0][2]; this->vertexAnatomical[1][0] = this->vertexAnatomical[1][0]; this->vertexAnatomical[1][1] = this->vertexAnatomical[1][1]; this->vertexAnatomical[1][2] = this->vertexAnatomical[1][2]; this->posAnatomical[0] = this->posAnatomical[0]; this->posAnatomical[1] = this->posAnatomical[1]; this->posAnatomical[2] = this->posAnatomical[2]; this->thetaR = obj.thetaR; this->phiR = obj.phiR; this->fracRI = obj.fracRI; this->fracRJ = obj.fracRJ; 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 { /* * Make sure projection surface number of nodes matches surface. */ if (this->projectionSurfaceNumberOfNodes > 0) { if (surfaceFile.getNumberOfNodes() != this->projectionSurfaceNumberOfNodes) { return false; } } const int is = 0; const int js = 1; const int32_t n1 = this->vertex[is]; const int32_t n2 = this->vertex[js]; CaretAssert(n1 < surfaceFile.getNumberOfNodes()); CaretAssert(n2 < surfaceFile.getNumberOfNodes()); /* * All nodes MUST have neighbors (connected) */ const TopologyHelper* topologyHelper = surfaceFile.getTopologyHelper().getPointer(); if ((topologyHelper->getNodeHasNeighbors(n1) == false) || (topologyHelper->getNodeHasNeighbors(n2) == false)) { return false; } float v[3]; float v_t1[3]; MathFunctions::subtractVectors(this->vertexAnatomical[js], this->vertexAnatomical[is], v); MathFunctions::subtractVectors(this->posAnatomical, this->vertexAnatomical[is], v_t1); float s_t2 = MathFunctions::dotProduct(v, v); float s_t3 = MathFunctions::dotProduct(v_t1, v); float QR[3]; for (int j = 0; j < 3; j++) { QR[j] = this->vertexAnatomical[is][j] + ((s_t3/s_t2) * v[j]); } const int pis = this->vertex[0]; const int pjs = this->vertex[1]; const float* posPIS = surfaceFile.getCoordinate(pis); const float* posPJS = surfaceFile.getCoordinate(pjs); if (unprojectWithOffsetFromSurface) { xyzOut[0] = (posPIS[0] + posPJS[0]) / 2.0f; xyzOut[1] = (posPIS[1] + posPJS[1]) / 2.0f; xyzOut[2] = (posPIS[2] + posPJS[2]) / 2.0f; if (offsetFromSurface != 0.0) { const float* normalI = surfaceFile.getNormalVector(pis); 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), }; MathFunctions::normalizeVector(avgNormal); const float offsetX = avgNormal[0] * offsetFromSurface; const float offsetY = avgNormal[1] * offsetFromSurface; const float offsetZ = avgNormal[2] * offsetFromSurface; xyzOut[0] += offsetX; xyzOut[1] += offsetY; xyzOut[2] += offsetZ; } return true; } MathFunctions::subtractVectors(posPJS, posPIS, v); float QS[3]; if ((this->fracRI <= 1.0) && (this->fracRJ <= 1.0)) { for (int j = 0; j < 3; j++) { QS[j] = posPIS[j] + this->fracRI * v[j]; } } else if ((this->fracRI > 1.0) && (this->fracRI > this->fracRJ)) { MathFunctions::subtractVectors(QR, this->vertexAnatomical[js], v_t1); s_t2 = MathFunctions::vectorLength(v_t1); MathFunctions::subtractVectors(posPJS, posPIS, v); s_t3 = MathFunctions::vectorLength(v); for (int j = 0; j < 3; j++) { QS[j] = posPJS[j] + s_t2 * (v[j]/s_t3); } } else if ((this->fracRJ > 1.0) && (this->fracRJ > this->fracRI)) { MathFunctions::subtractVectors(QR, this->vertexAnatomical[is], v_t1); s_t2 = MathFunctions::vectorLength(v_t1); MathFunctions::subtractVectors(posPIS, posPJS, v); s_t3 = MathFunctions::vectorLength(v); for (int j = 0; j < 3; j++) { QS[j] = posPIS[j] + s_t2 * (v[j]/s_t3); } } else { CaretLogWarning(("VanEssen Projection: Unrecognized case for fracRI and fracRJ: " + AString::number(this->fracRI) + ", " + AString::number(this->fracRJ))); return false; } if ((this->triVertices[0][0] < 0) || (this->triVertices[1][0] < 0)) { return false; } float normalB[3]; MathFunctions::normalVector( surfaceFile.getCoordinate(this->triVertices[1][0]), surfaceFile.getCoordinate(this->triVertices[1][1]), surfaceFile.getCoordinate(this->triVertices[1][2]), normalB); float normalA[3]; MathFunctions::normalVector( surfaceFile.getCoordinate(this->triVertices[0][0]), surfaceFile.getCoordinate(this->triVertices[0][1]), surfaceFile.getCoordinate(this->triVertices[0][2]), normalA); s_t2 = MathFunctions::dotProduct(normalA, normalB); s_t2 = std::min(s_t2, 1.0f); // limit to <= 1.0 float phiS = (float)std::acos(s_t2); float thetaS = 0.0f; if (this->phiR > 0.0f) { thetaS = (this->thetaR / this->phiR) * phiS; } else { thetaS = 0.5f * phiS; } /* * Fixes unprojection when thetaR is zero NOT ALL CASES YET */ if (thetaR == 0.0) { // thetaS = M_PI / 2.0; // if (this->phiR > 0.0) { // thetaS = ((M_PI / 2.0) / this->phiR) * phiS; // } } MathFunctions::subtractVectors(posPJS, posPIS, v); MathFunctions::normalizeVector(v); float projection[3] = { 0.0f, 0.0f, 0.0f }; this->computeProjectionPoint(projection); MathFunctions::subtractVectors(projection, QR, v_t1); MathFunctions::normalizeVector(v_t1); MathFunctions::subtractVectors(this->vertexAnatomical[js], this->vertexAnatomical[is], v); MathFunctions::normalizeVector(v); float normalA_3D[3]; MathFunctions::normalVector(this->triAnatomical[0][0], this->triAnatomical[0][1], this->triAnatomical[0][2], normalA_3D); float v_t2[3]; MathFunctions::crossProduct(normalA_3D, v, v_t2); s_t3 = MathFunctions::dotProduct(v_t1, v_t2); float TS[3]; for (int k = 0; k < 3; k++) { TS[k] = QS[k] + (s_t3 * (this->dR * (float)std::sin(thetaS)) * v_t2[k]); } MathFunctions::subtractVectors(this->posAnatomical, projection, v); MathFunctions::normalizeVector(v); s_t3 = MathFunctions::dotProduct(normalA_3D, v); for (int i = 0; i < 3; i++) { xyzOut[i] = TS[i] + (this->dR * s_t3 * (float)std::cos(thetaS)) * normalA[i]; } return true; } /** * Compute a projection point?? * @param * Projection that is computed and set by this method. */ void SurfaceProjectionVanEssen::computeProjectionPoint(float projection[3]) const { float v[3]; MathFunctions::subtractVectors(this->triAnatomical[0][1], this->triAnatomical[0][0], v); float w[3]; MathFunctions::subtractVectors(this->triAnatomical[0][1], this->triAnatomical[0][2], w); float tnormal[3]; MathFunctions::crossProduct(w, v, tnormal); float a[3][3]; for (int k = 0; k < 3; k++) { a[0][k] = v[k]; a[1][k] = w[k]; a[2][k] = tnormal[k]; } float b[3]; b[0] = MathFunctions::dotProduct(v, this->posAnatomical); b[1] = MathFunctions::dotProduct(w, this->posAnatomical); b[2] = MathFunctions::dotProduct(tnormal, this->triAnatomical[0][2]); MathFunctions::vtkLinearSolve3x3(a, b, projection); } /** * @return dR */ float SurfaceProjectionVanEssen::getDR() const { return this->dR; } /** * Set dR * @param dR * New value. */ void SurfaceProjectionVanEssen::setDR(const float dR) { this->dR = dR; this->setModified(); } /** * @return thetaR */ float SurfaceProjectionVanEssen::getThetaR() const { return this->thetaR; } /** * Set thetaR * @param thetaR * New value. */ void SurfaceProjectionVanEssen::setThetaR(const float thetaR) { this->thetaR = thetaR; this->setModified(); } /** * @return phiR */ float SurfaceProjectionVanEssen::getPhiR() const { return this->phiR; } /** * Set phiR * @param phiR * New value. */ void SurfaceProjectionVanEssen::setPhiR(const float phiR) { this->phiR = phiR; this->setModified(); } /** * @return fracRI */ float SurfaceProjectionVanEssen::getFracRI() const { return this->fracRI; } /** * Set fracRI * @param fracRI * New value. */ void SurfaceProjectionVanEssen::setFracRI(const float fracRI) { this->fracRI = fracRI; this->setModified(); } /** * @return fracRJ */ float SurfaceProjectionVanEssen::getFracRJ() const { return this->fracRJ; } /** * Set fracRJ * @param fracRJ * New value. */ void SurfaceProjectionVanEssen::setFracRJ(const float fracRJ) { this->fracRJ = fracRJ; this->setModified(); } /** * Set triVertices * @param triVertices * New values. */ void SurfaceProjectionVanEssen::setTriVertices(const int32_t triVertices[2][3]) { for (int32_t i = 0; i < 2; i++) { for (int32_t j = 0; j < 3; j++) { this->triVertices[i][j] = triVertices[i][j]; } } this->setModified(); } /** * Set triVertices * @param indx1 * Index of vertices being set. * @param triVertices * New values. */ void SurfaceProjectionVanEssen::setTriVertices(const int32_t indx1, const int32_t vertices[3]) { CaretAssertArrayIndex(this->triVertices, 2, indx1); for (int32_t j = 0; j < 3; j++) { this->triVertices[indx1][j] = vertices[j]; } setModified(); } /** * Get triVertices * @param triVertices * Output values. */ void SurfaceProjectionVanEssen::getTriVertices(int32_t triVertices[2][3]) const { for (int32_t i = 0; i < 2; i++) { for (int32_t j = 0; j < 3; j++) { triVertices[i][j] = this->triVertices[i][j]; } } } /** * Set vertex * @param vertex * New values. */ void SurfaceProjectionVanEssen::setVertex(const int32_t vertex[2]) { this->vertex[0] = vertex[0]; this->vertex[1] = vertex[1]; this->setModified(); } /** * Set vertex * @param vertex * New values. */ void SurfaceProjectionVanEssen::setVertex(const int32_t indx1, const int32_t vertex) { CaretAssertArrayIndex(this->vertex, 2, indx1); this->vertex[indx1] = vertex; this->setModified(); } /** * Get vertex * @param vertex * Output values. */ void SurfaceProjectionVanEssen::getVertex(int32_t vertex[2]) const { vertex[0] = this->vertex[0]; vertex[1] = this->vertex[1]; } /** * Set triAnatomical * @param triAnatomical * New values. */ void SurfaceProjectionVanEssen::setTriAnatomical(const float triAnatomical[2][3][3]) { for (int32_t i = 0; i < 2; i++) { for (int32_t j = 0; j < 3; j++) { for (int32_t k = 0; k < 3; k++) { this->triAnatomical[i][j][k] = triAnatomical[i][j][k]; } } } this->setModified(); } /** * Set triAnatomical * @param triAnatomical * New values. */ void SurfaceProjectionVanEssen::setTriAnatomical(const int32_t indx1, const int32_t indx2, const float triAnatomical[3]) { CaretAssertArrayIndex(this->triAnatomical, 2, indx1); CaretAssertArrayIndex(this->triAnatomical, 3, indx2); for (int32_t k = 0; k < 3; k++) { this->triAnatomical[indx1][indx2][k] = triAnatomical[k]; } this->setModified(); } /** * Get triAnatomical * @param triAnatomical * Output values. */ void SurfaceProjectionVanEssen::getTriAnatomical(float triAnatomical[2][3][3]) const { for (int32_t i = 0; i < 2; i++) { for (int32_t j = 0; j < 3; j++) { for (int32_t k = 0; k < 3; k++) { triAnatomical[i][j][k] = this->triAnatomical[i][j][k]; } } } } /** * Set vertexAnatomical * @param vertexAnatomical * New values. */ void SurfaceProjectionVanEssen::setVertexAnatomical(const float vertexAnatomical[2][3]) { this->vertexAnatomical[0][0] = vertexAnatomical[0][0]; this->vertexAnatomical[0][1] = vertexAnatomical[0][1]; this->vertexAnatomical[0][2] = vertexAnatomical[0][2]; this->vertexAnatomical[1][0] = vertexAnatomical[1][0]; this->vertexAnatomical[1][1] = vertexAnatomical[1][1]; this->vertexAnatomical[1][2] = vertexAnatomical[1][2]; this->setModified(); } /** * Set vertexAnatomical * @param vertexAnatomical * New values. */ void SurfaceProjectionVanEssen::setVertexAnatomical(const int32_t indx1, const float vertexAnatomical[3]) { CaretAssertArrayIndex(this->vertexAnatomical, 2, indx1); this->vertexAnatomical[indx1][0] = vertexAnatomical[0]; this->vertexAnatomical[indx1][1] = vertexAnatomical[1]; this->vertexAnatomical[indx1][2] = vertexAnatomical[2]; this->setModified(); } /** * Get vertexAnatomical * @param vertexAnatomical * Output values. */ void SurfaceProjectionVanEssen::getVertexAnatomical(float vertexAnatomical[2][3]) const { vertexAnatomical[0][0] = this->vertexAnatomical[0][0]; vertexAnatomical[0][1] = this->vertexAnatomical[0][1]; vertexAnatomical[0][2] = this->vertexAnatomical[0][2]; vertexAnatomical[1][0] = this->vertexAnatomical[1][0]; vertexAnatomical[1][1] = this->vertexAnatomical[1][1]; vertexAnatomical[1][2] = this->vertexAnatomical[1][2]; } /** * Get posAnatomical * @param posAnatomical * Output values. */ void SurfaceProjectionVanEssen::getPosAnatomical(float posAnatomical[3]) const { posAnatomical[0] = this->posAnatomical[0]; posAnatomical[1] = this->posAnatomical[1]; posAnatomical[2] = this->posAnatomical[2]; } /** * Set posAnatomical * @param posAnatomical * New values. */ void SurfaceProjectionVanEssen::setPosAnatomical(const float posAnatomical[3]) { this->posAnatomical[0] = posAnatomical[0]; this->posAnatomical[1] = posAnatomical[1]; this->posAnatomical[2] = posAnatomical[2]; this->setModified(); } /** * Reset the surface projection to its initial state. */ void SurfaceProjectionVanEssen::reset() { this->resetAllValues(); } /** * Since reset overrides the 'super' class it should * never be called from a constructor. So, this * method does the actual reset, and since it does * not override a method from the 'super' class, it * may be called from this class' constructor. */ void SurfaceProjectionVanEssen::resetAllValues() { this->projectionValid = false; this->dR = 0.0; for (int32_t i = 0; i < 2; i++) { for (int32_t j = 0; j < 3; j++) { for (int32_t k = 0; k < 3; k++) { this->triAnatomical[i][j][k] = 0; } this->triVertices[i][j] = 0.0; } this->vertex[i] = 0.0; } this->vertexAnatomical[0][0] = 0.0; this->vertexAnatomical[0][1] = 0.0; this->vertexAnatomical[0][2] = 0.0; this->vertexAnatomical[1][0] = 0.0; this->vertexAnatomical[1][1] = 0.0; this->vertexAnatomical[1][2] = 0.0; this->posAnatomical[0] = 0.0; this->posAnatomical[1] = 0.0; this->posAnatomical[2] = 0.0; this->thetaR = 0.0; this->phiR = 0.0; this->fracRI = 0.0; this->fracRJ = 0.0; } /** * @return Is the projection valid? */ bool SurfaceProjectionVanEssen::isValid() const { return this->projectionValid; } /** * Set the validity of the projection. * @param valid * New validity status. */ void SurfaceProjectionVanEssen::setValid(const bool valid) { this->projectionValid = valid; setModified(); } /** * Write the projection to XML. * @param xmlWriter * The XML Writer. * @throw XmlException * If an error occurs. */ void SurfaceProjectionVanEssen::writeAsXML(XmlWriter& xmlWriter) { if (this->projectionValid) { xmlWriter.writeStartElement(XML_TAG_PROJECTION_VAN_ESSEN); xmlWriter.writeElementCharacters(XML_TAG_DR, this->dR); xmlWriter.writeElementCharacters(XML_TAG_TRI_ANATOMICAL, (float*)this->triAnatomical, 18); xmlWriter.writeElementCharacters(XML_TAG_THETA_R, this->thetaR); xmlWriter.writeElementCharacters(XML_TAG_PHI_R, this->phiR); xmlWriter.writeElementCharacters(XML_TAG_TRI_VERTICES, (int32_t*)this->triVertices, 6); xmlWriter.writeElementCharacters(XML_TAG_VERTEX, (int32_t*)this->vertex, 2); xmlWriter.writeElementCharacters(XML_TAG_VERTEX_ANATOMICAL, (float*)this->vertexAnatomical, 6); xmlWriter.writeElementCharacters(XML_TAG_POS_ANATOMICAL, this->posAnatomical, 3); xmlWriter.writeElementCharacters(XML_TAG_FRAC_RI, this->fracRI); xmlWriter.writeElementCharacters(XML_TAG_FRAC_RJ, this->fracRJ); xmlWriter.writeEndElement(); } } /** * Get a description of this object's content. * @return String describing this object's content. */ AString SurfaceProjectionVanEssen::toString() const { AString txt = SurfaceProjection::toString(); if (txt.isEmpty() == false) { txt += ", "; } txt += ("dR=" + AString::number(dR) + ", thetaR=" + AString::number(thetaR) + ", phiR=" + AString::number(phiR) + ", fracRI=" + AString::number(fracRI) + ", fracRJ=" + AString::number(fracRJ) + ", triVertices=" + AString::fromNumbers((int32_t*)triVertices, 6, ",") + ", vertex=" + AString::fromNumbers(vertex, 2, ",") + ", triAnatomical=" + AString::fromNumbers((float*)triAnatomical, 18, ",") + ", vertexAnatomical=" + AString::fromNumbers((float*)vertexAnatomical, 6, ",") + ", posAnatomical=" + AString::fromNumbers(posAnatomical, 3, ",") ); return txt; } connectome-workbench-1.2.3+git41-gc4c6c90/src/Files/SurfaceProjectionVanEssen.h000066400000000000000000000136531300200146000270570ustar00rootroot00000000000000#ifndef __SURFACE_PROJECTION_VAN_ESSEN__H_ #define __SURFACE_PROJECTION_VAN_ESSEN__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 "SurfaceProjection.h" namespace caret { class SurfaceProjectionVanEssen : public SurfaceProjection { public: SurfaceProjectionVanEssen(); virtual ~SurfaceProjectionVanEssen(); SurfaceProjectionVanEssen(const SurfaceProjectionVanEssen& obj); SurfaceProjectionVanEssen& operator=(const SurfaceProjectionVanEssen& obj); bool operator==(const SurfaceProjectionVanEssen& rhs); bool operator!=(const SurfaceProjectionVanEssen& rhs) { return !(*this == rhs); } bool unprojectToSurface(const SurfaceFile& surfaceFile, float xyzOut[3], const float offsetFromSurface, const bool unprojectWithOffsetFromSurface) const; float getDR() const; void setDR(const float dR); float getThetaR() const; void setThetaR(const float thetaR); float getPhiR() const; void setPhiR(const float phiR); float getFracRI() const; void setFracRI(const float fracRI); float getFracRJ() const; void setFracRJ(const float fracRJ); void setTriVertices(const int32_t triVertices[2][3]); void setTriVertices(const int32_t indx1, const int32_t vertices[3]); void getTriVertices(int32_t triVertices[2][3]) const; void setVertex(const int32_t vertex[2]); void setVertex(const int32_t indx1, const int32_t vertex); void getVertex(int32_t vertex[2]) const; void setTriAnatomical(const float triAnatomical[2][3][3]); void setTriAnatomical(const int32_t indx1, const int32_t indx2, const float anatomical[3]); void getTriAnatomical(float triAnatomical[2][3][3]) const; void setVertexAnatomical(const float vertexAnatomical[2][3]); void setVertexAnatomical(const int32_t indx1, const float anatomical[3]); void getVertexAnatomical(float vertexAnatomical[2][3]) const; void getPosAnatomical(float posAnatomical[3]) const; void setPosAnatomical(const float posAnatomical[3]); void reset(); bool isValid() const; void setValid(const bool valid); virtual AString toString() const; void writeAsXML(XmlWriter& xmlWriter); static const AString XML_TAG_PROJECTION_VAN_ESSEN; static const AString XML_TAG_DR; static const AString XML_TAG_TRI_ANATOMICAL; static const AString XML_TAG_THETA_R; static const AString XML_TAG_PHI_R; static const AString XML_TAG_TRI_VERTICES; static const AString XML_TAG_VERTEX; static const AString XML_TAG_VERTEX_ANATOMICAL; static const AString XML_TAG_POS_ANATOMICAL; static const AString XML_TAG_FRAC_RI; static const AString XML_TAG_FRAC_RJ; private: void copyHelperSurfaceProjectionVanEssen(const SurfaceProjectionVanEssen& obj); void computeProjectionPoint(float projection[3]) const; void resetAllValues(); float dR; float thetaR; float phiR; float fracRI; float fracRJ; int32_t triVertices[2][3]; int32_t vertex[2]; float triAnatomical[2][3][3]; float vertexAnatomical[2][3]; float posAnatomical[3]; bool projectionValid; }; #ifdef __SURFACE_PROJECTION_VAN_ESSEN_DECLARE__ const AString SurfaceProjectionVanEssen::XML_TAG_PROJECTION_VAN_ESSEN = "VanEssenProjection"; const AString SurfaceProjectionVanEssen::XML_TAG_DR = "DR"; const AString SurfaceProjectionVanEssen::XML_TAG_TRI_ANATOMICAL = "TriAnatomical"; const AString SurfaceProjectionVanEssen::XML_TAG_THETA_R = "ThetaR"; const AString SurfaceProjectionVanEssen::XML_TAG_PHI_R = "PhiR"; const AString SurfaceProjectionVanEssen::XML_TAG_TRI_VERTICES = "TriVertices"; const AString SurfaceProjectionVanEssen::XML_TAG_VERTEX = "Vertex"; const AString SurfaceProjectionVanEssen::XML_TAG_VERTEX_ANATOMICAL = "VertexAnatomical"; const AString SurfaceProjectionVanEssen::XML_TAG_POS_ANATOMICAL = "PosAnatomical"; const AString SurfaceProjectionVanEssen::XML_TAG_FRAC_RI = "FracRI"; const AString SurfaceProjectionVanEssen::XML_TAG_FRAC_RJ = "FracRJ"; #endif // __SURFACE_PROJECTION_VAN_ESSEN_DECLARE__ } // namespace #endif //__SURFACE_PROJECTION_VAN_ESSEN__H_ connectome-workbench-1.2.3+git41-gc4c6c90/src/Files/SurfaceProjector.cxx000066400000000000000000001642031300200146000256200ustar00rootroot00000000000000/*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 #define __SURFACE_PROJECTOR_DEFINE__ #include "SurfaceProjector.h" #undef __SURFACE_PROJECTOR_DEFINE__ #include "CaretLogger.h" #include "FociFile.h" #include "Focus.h" #include "MathFunctions.h" #include "SignedDistanceHelper.h" #include "SurfaceFile.h" #include "SurfaceProjectedItem.h" #include "SurfaceProjectionBarycentric.h" #include "SurfaceProjectionVanEssen.h" #include "TopologyHelper.h" using namespace caret; /** * \class caret::SurfaceProjector * \brief Project points to a surface. */ /** * Constructor for projection to a given surface. * * @param surfaceFile * Surface to which projection takes place. For proper * projection (particularly flat and spherical) surfaces, it is important * that the surface's type is correctly set. */ SurfaceProjector::SurfaceProjector(const SurfaceFile* surfaceFile) : CaretObject(), m_surfaceFileLeft(NULL), m_surfaceFileRight(NULL), m_surfaceFileCerebellum(NULL), m_mode(MODE_SURFACES) { CaretAssert(surfaceFile); m_surfaceFiles.push_back(surfaceFile); initializeMembersSurfaceProjector(); } /** * Constructor for projection to closest of a group of surfaces. * * @param surfaceFiles * Vector of Surfaces to which projection takes place. For proper * projection (particularly flat and spherical) surfaces, it is important * that the surface's type is correctly set. */ SurfaceProjector::SurfaceProjector(const std::vector& surfaceFiles) : CaretObject(), m_surfaceFileLeft(NULL), m_surfaceFileRight(NULL), m_surfaceFileCerebellum(NULL), m_mode(MODE_SURFACES) { const int32_t numberOfSurfaces = static_cast(surfaceFiles.size()); for (int32_t i = 0; i < numberOfSurfaces; i++) { m_surfaceFiles.push_back(surfaceFiles[i]); } initializeMembersSurfaceProjector(); } /** * Constructor that allows ambiguous projections for items between a cortical * surface and the cerebellum. Items that have a positive X-coordinate * are projected to the right cortex or the cerebellum. Items that have * a negative X-coordinate are projected to the left cortex or the cerebellum. * Some of the surface files may be NULL but at least one must be valid. * * @param leftSurfaceFile * Surface file for left cortex. * @param rightSurfaceFile * Surface file for right cortex. * @param cerebellumSurfaceFile * Surface file for cerebellum cortex. */ SurfaceProjector::SurfaceProjector(const SurfaceFile* leftSurfaceFile, const SurfaceFile* rightSurfaceFile, const SurfaceFile* cerebellumSurfaceFile) : CaretObject(), m_surfaceFileLeft(leftSurfaceFile), m_surfaceFileRight(rightSurfaceFile), m_surfaceFileCerebellum(cerebellumSurfaceFile), m_mode(MODE_LEFT_RIGHT_CEREBELLUM) { } /** * Destructor */ SurfaceProjector::~SurfaceProjector() { } /** * Initialize members of this instance. */ void SurfaceProjector::initializeMembersSurfaceProjector() { m_surfaceOffset = 0.0; m_surfaceOffsetValid = false; /* * Validate when logger is set at a specified level * If the level is changed, all need to change level * where validation message is logged. */ m_validateFlag = CaretLogger::getLogger()->isFine(); m_validateItemName = ""; } /** * Set the desired offset of projected items from the surface-> * * @param surfaceOffset - distance above the surface-> * */ void SurfaceProjector::setSurfaceOffset(const float surfaceOffset) { m_surfaceOffset = surfaceOffset; m_surfaceOffsetValid = true; } /** * Project all foci in a foci file. * @param fociFile * The foci file. * @throws SurfaceProjectorException * If projecting an item failed. */ void SurfaceProjector::projectFociFile(FociFile* fociFile) { CaretAssert(fociFile); const int32_t numberOfFoci = fociFile->getNumberOfFoci(); AString errorMessage = ""; for (int32_t i = 0; i < numberOfFoci; i++) { Focus* focus = fociFile->getFocus(i); try { if (m_validateFlag) { m_validateItemName = ("Focus " + AString::number(i) + ", " + focus->getName()); } projectFocus(i, focus); } catch (const SurfaceProjectorException& spe) { if (errorMessage.isEmpty() == false) { errorMessage += "\n"; } errorMessage += (focus->getName() + ", index=" + AString::number(i) + ": " + spe.whatString()); } } if (errorMessage.isEmpty() == false) { throw SurfaceProjectorException(errorMessage); } } /** * Project a focus. * @param focusIndex * Index of the focus (negative indicates no index) * @param focus * The focus. * @throws SurfaceProjectorException * If projecting an item failed. */ void SurfaceProjector::projectFocus(const int32_t focusIndex, Focus* focus) { const int32_t numberOfProjections = focus->getNumberOfProjections(); CaretAssert(numberOfProjections > 0); if (numberOfProjections < 0) { throw SurfaceProjectorException("Focus has no projections, no stereotaxic coordinate."); } focus->removeExtraProjections(); SurfaceProjectedItem* spi = focus->getProjection(0); SurfaceProjectedItem* spiSecond = NULL; if (m_surfaceFileCerebellum != NULL) { spiSecond = new SurfaceProjectedItem(); } m_allowEdgeProjection = true; projectItem(spi, spiSecond); if (spiSecond != NULL) { if (spiSecond->hasValidProjection()) { focus->addProjection(spiSecond); } else { delete spiSecond; spiSecond = NULL; } } if (m_projectionWarning.isEmpty() == false) { AString msg = ("Focus: Name=" + focus->getName()); if (focusIndex >= 0) { msg += (", Index=" + AString::number(focusIndex)); } msg += (": " + m_projectionWarning); CaretLogWarning(msg);; } } /** * Project to the surface(s) triangles (barycentric projection) * * @param spi * Item that is to be projected. Its contents will be * updated to reflect the projection. This item's stereotaxic coordinate * is used for the projection point. * * @throws SurfaceProjectorException * If projecting an item failed. */ void SurfaceProjector::projectItemToTriangle(SurfaceProjectedItem* spi) { CaretAssert(spi); m_allowEdgeProjection = false; projectItem(spi, NULL); } /** * Project to the surface(s) triangles (barycentric projection) * or edges (van-essen projection). * * @param spi * Item that is to be projected. Its contents will be * updated to reflect the projection. This item's stereotaxic coordinate * is used for the projection point. * * @throws SurfaceProjectorException * If projecting an item failed. */ void SurfaceProjector::projectItemToTriangleOrEdge(SurfaceProjectedItem* spi) { CaretAssert(spi); m_allowEdgeProjection = true; projectItem(spi, NULL); } /** * Project to the appropriate surface(s). * * @param spi * Item that is to be projected. Its contents will be * updated to reflect the projection. This item's stereotaxic coordinate * is used for the projection point. * @param secondSpi * For left/right/cerebellum projections, if there is ambiguity for an * item (between cortex and cerebellum), this projection will be set if * it is not NULL. * @throws SurfaceProjectorException * If projecting an item failed. */ void SurfaceProjector::projectItem(SurfaceProjectedItem* spi, SurfaceProjectedItem* secondSpi) { m_projectionWarning = ""; /* * Get position of item. */ float xyz[3]; if (spi->isStereotaxicXYZValid() == false) { throw SurfaceProjectorException( "Stereotaxic position is invalid, cannot project."); } spi->getStereotaxicXYZ(xyz); if (secondSpi != NULL) { secondSpi->setStereotaxicXYZ(xyz); } switch (m_mode) { case MODE_LEFT_RIGHT_CEREBELLUM: { if (xyz[0] < 0.0) { if (m_surfaceFileLeft != NULL) { if (m_surfaceFileCerebellum != NULL) { const float leftDist = m_surfaceFileLeft->getSignedDistanceHelper()->dist(xyz, SignedDistanceHelper::NORMALS); const float cerebellumDist = m_surfaceFileCerebellum->getSignedDistanceHelper()->dist(xyz, SignedDistanceHelper::NORMALS); float ratio = 1000000.0; if (cerebellumDist != 0.0) { ratio = leftDist / cerebellumDist; } if (ratio > s_cerebellumSurfaceCutoff) { projectItemToSurfaceFile(m_surfaceFileCerebellum, spi); } else if (ratio < s_corticalSurfaceCutoff) { projectItemToSurfaceFile(m_surfaceFileLeft, spi); } else { projectItemToSurfaceFile(m_surfaceFileLeft, spi); if (secondSpi != NULL) { projectItemToSurfaceFile(m_surfaceFileCerebellum, secondSpi); } } } else { projectItemToSurfaceFile(m_surfaceFileLeft, spi); } } else if (m_surfaceFileCerebellum != NULL) { projectItemToSurfaceFile(m_surfaceFileCerebellum, spi); } } else { if (m_surfaceFileRight != NULL) { if (m_surfaceFileCerebellum != NULL) { const float rightDist = m_surfaceFileRight->getSignedDistanceHelper()->dist(xyz, SignedDistanceHelper::NORMALS); const float cerebellumDist = m_surfaceFileCerebellum->getSignedDistanceHelper()->dist(xyz, SignedDistanceHelper::NORMALS); float ratio = 1000000.0; if (cerebellumDist != 0.0) { ratio = rightDist / cerebellumDist; } if (ratio > s_cerebellumSurfaceCutoff) { projectItemToSurfaceFile(m_surfaceFileCerebellum, spi); } else if (ratio < s_corticalSurfaceCutoff) { projectItemToSurfaceFile(m_surfaceFileRight, spi); } else { projectItemToSurfaceFile(m_surfaceFileRight, spi); if (secondSpi != NULL) { projectItemToSurfaceFile(m_surfaceFileCerebellum, secondSpi); } } } else { projectItemToSurfaceFile(m_surfaceFileRight, spi); } } else if (m_surfaceFileCerebellum != NULL) { projectItemToSurfaceFile(m_surfaceFileCerebellum, spi); } } } break; case MODE_SURFACES: { const int32_t numberOfSurfaceFiles = static_cast(m_surfaceFiles.size()); if (numberOfSurfaceFiles <= 0) { throw SurfaceProjectorException("No surface for projection!"); } int32_t nearestSurfaceIndex = -1; if (numberOfSurfaceFiles == 1) { nearestSurfaceIndex = 0; } else { /* * Find surface closest to node. */ float nearestDistance = std::numeric_limits::max(); for (int32_t i = 0; i < numberOfSurfaceFiles; i++) { const SurfaceFile* sf = m_surfaceFiles[i]; CaretPointer sdh = sf->getSignedDistanceHelper(); const float absDist = std::fabs(sdh->dist(xyz, SignedDistanceHelper::NORMALS)); if (absDist < nearestDistance) { nearestDistance = absDist; nearestSurfaceIndex = i; } } } if (nearestSurfaceIndex < 0) { throw SurfaceProjectorException("Failed to find surface for projection."); } const SurfaceFile* projectionSurfaceFile = m_surfaceFiles[nearestSurfaceIndex]; projectItemToSurfaceFile(projectionSurfaceFile, spi); } break; } } /** * Project to the given surface * @param surfaceFile * Surface to which triangle projection is made. * @param spi * Item that is to be projected. Its contents will be * updated to reflect the projection. This items XYZ coordinate * is used for the projection point. * * @throws SurfaceProjectorException If projecting an item * failed. */ void SurfaceProjector::projectItemToSurfaceFile(const SurfaceFile* surfaceFile, SurfaceProjectedItem* spi) { float originalXYZ[3]; spi->getStereotaxicXYZ(originalXYZ); float xyz[3] = { originalXYZ[0], originalXYZ[1], originalXYZ[2] }; float projXYZ[3]; float stereoXYZ[3]; float distanceError = 0.0; bool projectionValid = false; projectToSurface(surfaceFile, xyz, spi); if (spi->getBarycentricProjection()->isValid() || spi->getVanEssenProjection()->isValid()) { spi->getProjectedPosition(*surfaceFile, projXYZ, false); spi->getStereotaxicXYZ(stereoXYZ); distanceError = MathFunctions::distance3D(projXYZ, stereoXYZ); projectionValid = true; } if (distanceError > s_projectionDistanceError) { bool perturbIfError = true; if (perturbIfError) { /* * Initialize with first try */ float bestXYZ[3] = { xyz[0], xyz[1], xyz[2] }; float bestDistance = distanceError; bool bestValid = true; const float originalDistanceError = distanceError; for (int32_t iTry = 0; iTry < 10; iTry++) { const float randomZeroToOne = ((float)std::rand()) / ((float)RAND_MAX); const float randomPlusMinusOneHalf = randomZeroToOne - 0.5; const float moveLittleBit = randomPlusMinusOneHalf * 0.5; xyz[0] = originalXYZ[0] + moveLittleBit; xyz[1] = originalXYZ[1] + moveLittleBit; xyz[2] = originalXYZ[2] + moveLittleBit; SurfaceProjectedItem spiTest; spiTest.setStereotaxicXYZ(originalXYZ); projectToSurface(surfaceFile, xyz, &spiTest); if (spiTest.getBarycentricProjection()->isValid() || spiTest.getVanEssenProjection()->isValid()) { spiTest.getProjectedPosition(*surfaceFile, projXYZ, false); spiTest.getStereotaxicXYZ(stereoXYZ); distanceError = MathFunctions::distance3D(projXYZ, stereoXYZ); projectionValid = true; // std::cout << "Moved from original (" // << AString::fromNumbers(originalXYZ, 3, ",") // << ") to (" // << AString::fromNumbers(xyz, 3, ",") // << ") distance error now=" // << distanceError // << std::endl; if (distanceError < bestDistance) { bestXYZ[0] = xyz[0]; bestXYZ[1] = xyz[1]; bestXYZ[2] = xyz[2]; bestDistance = distanceError; bestValid = true; } } } if (bestValid) { projectToSurface(surfaceFile, bestXYZ, spi); if (spi->getBarycentricProjection()->isValid() || spi->getVanEssenProjection()->isValid()) { spi->getProjectedPosition(*surfaceFile, projXYZ, false); spi->getStereotaxicXYZ(stereoXYZ); distanceError = MathFunctions::distance3D(projXYZ, stereoXYZ); m_projectionWarning += ("Was moved due to projection error from (" + AString::fromNumbers(originalXYZ, 3, ",") + ") to (" + AString::fromNumbers(bestXYZ, 3, ",") + ") with distance error reduced from " + AString::number(originalDistanceError) + " to " + AString::number(distanceError)); } } } } if (m_validateFlag == false) { if (distanceError > s_projectionDistanceError) { m_projectionWarning += ("Projection Warning: Error=" + AString::number(distanceError) + "mm, Stereotaxic=(" + AString::fromNumbers(stereoXYZ, 3, ",") + "), Projected=(" + AString::fromNumbers(projXYZ, 3, ",") + ")"); } } if (m_validateFlag) { bool errorFlag = false; AString validateString; if (projectionValid) { AString projTypeString = "Unprojected"; AString projInfo; if (spi->getBarycentricProjection()->isValid()) { projTypeString = "Triangle"; if (spi->getBarycentricProjection()->isDegenerate()) { projTypeString = "-degenerate"; } projInfo = spi->getBarycentricProjection()->toString(); } else if (spi->getVanEssenProjection()->isValid()) { projTypeString = "Edge"; projInfo = spi->getVanEssenProjection()->toString(); } AString matchString = ""; if (distanceError > 0.001) { matchString = " FAILED *************************"; errorFlag = true; } if (validateString.isEmpty() == false) { validateString += "\n"; } if (spi->getStructure() == StructureEnum::CEREBELLUM) { if (spi->getVanEssenProjection()->isValid()) { //errorFlag = true; } } validateString += (m_validateItemName + ": projType=" + projTypeString + ": structure=" + StructureEnum::toName(spi->getStructure()) + ", stereoPos=(" + AString::fromNumbers(stereoXYZ, 3, ",") + "), projPos=(" + AString::fromNumbers(projXYZ, 3, ",") + "): stereo/proj positions differ by " + AString::number(distanceError, 'f', 3) + matchString + "\n"); if (projInfo.isEmpty() == false) { validateString += (projInfo + "\n"); } } else { validateString += (m_validateItemName + ": failed to project\n"); errorFlag = true; } if (errorFlag && (validateString.isEmpty() == false)) { CaretLogFine(validateString); } } } /** * Project to the surface * @param surfaceFile * Surface to which triangle projection is made. * @param xyz * Coordinate that is being projected. * @param spi * Item that is to be projected. Its contents will be * updated to reflect the projection. This items XYZ coordinate * is used for the projection point. * * @throws SurfaceProjectorException If projecting an item * failed. */ void SurfaceProjector::projectToSurface(const SurfaceFile* surfaceFile, const float xyz[3], SurfaceProjectedItem* spi) { // // If needed, create node locator // if (surfaceFile->getNumberOfNodes() <= 0) { throw SurfaceProjectorException("Surface file contains no nodes: " + surfaceFile->getFileNameNoPath()); } if (surfaceFile->getNumberOfTriangles() <= 0) { throw SurfaceProjectorException("Surface topology contains no triangles: " + surfaceFile->getFileNameNoPath()); } m_sphericalSurfaceRadius = 0.0; m_surfaceTypeHint = SURFACE_HINT_THREE_DIMENSIONAL; switch (surfaceFile->getSurfaceType()) { case SurfaceTypeEnum::FLAT: m_surfaceTypeHint = SURFACE_HINT_FLAT; break; case SurfaceTypeEnum::SPHERICAL: m_surfaceTypeHint = SURFACE_HINT_SPHERE; m_sphericalSurfaceRadius = surfaceFile->getSphericalRadius(); break; default: m_surfaceTypeHint = SURFACE_HINT_THREE_DIMENSIONAL; break; } // // Default to invalid projection // SurfaceProjectionBarycentric* baryProj = spi->getBarycentricProjection(); baryProj->setValid(false); SurfaceProjectionVanEssen* vanEssenProj = spi->getVanEssenProjection(); vanEssenProj->setValid(false); /* * Determine if projected to node/edge/triangle */ ProjectionLocation projectionLocation; getProjectionLocation(surfaceFile, xyz, projectionLocation); if (m_validateFlag) { if (m_validateItemName.isEmpty() == false) { m_validateItemName += "\n"; } m_validateItemName += ("ORIGINAL: " + projectionLocation.toString(surfaceFile)); } /* * If projected to edge and edge projection allowed */ if (m_allowEdgeProjection && (projectionLocation.m_type == ProjectionLocation::EDGE)) { vanEssenProj->setPosAnatomical(xyz); projectWithVanEssenAlgorithm(surfaceFile, projectionLocation, vanEssenProj); if (vanEssenProj->isValid() == false) { throw SurfaceProjectorException("Edge projection failed."); } } else { /* * Convert the projection to a triangle projection. */ if ((projectionLocation.m_type == ProjectionLocation::EDGE) || (projectionLocation.m_type == ProjectionLocation::NODE)) { convertToTriangleProjection(surfaceFile, projectionLocation); if (m_validateFlag) { if (m_validateItemName.isEmpty() == false) { m_validateItemName += "\n"; } m_validateItemName += ("ALTERED: " + projectionLocation.toString(surfaceFile)); } } projectToSurfaceTriangle(surfaceFile, projectionLocation, spi->getBarycentricProjection()); if (baryProj->isValid() == false) { throw SurfaceProjectorException("Triangle projection failed."); } } spi->setStructure(surfaceFile->getStructure()); } /** * Convert an edge or node projection to a triangle projection which * may become a degenerate triangle projection. * @param surfaceFile * Surface to which triangle projection is made. * @param projectionLocation * Contains informaiton about item on the surface file. * * @throws SurfaceProjectorException If projecting an item * failed. */ void SurfaceProjector::convertToTriangleProjection(const SurfaceFile* surfaceFile, ProjectionLocation& projectionLocation) { bool doIt = false; switch (projectionLocation.m_type) { case ProjectionLocation::EDGE: doIt = true; break; case ProjectionLocation::INVALID: break; case ProjectionLocation::NODE: doIt = true; break; case ProjectionLocation::TRIANGLE: break; } if (doIt) { SurfaceProjectionBarycentric baryProj; checkItemInTriangle(surfaceFile, projectionLocation.m_triangleIndices[0], projectionLocation.m_pointXYZ, s_extremeTriangleAreaTolerance, &baryProj); if (baryProj.isValid()) { projectionLocation.m_type = ProjectionLocation::TRIANGLE; const int32_t* nodes = baryProj.getTriangleNodes(); projectionLocation.m_nodes[0] = nodes[0]; projectionLocation.m_nodes[1] = nodes[1]; projectionLocation.m_nodes[2] = nodes[2]; const float* areas = baryProj.getTriangleAreas(); projectionLocation.m_weights[0] = areas[0]; projectionLocation.m_weights[1] = areas[1]; projectionLocation.m_weights[2] = areas[2]; projectionLocation.m_signedDistance = baryProj.getSignedDistanceAboveSurface(); projectionLocation.m_absoluteDistance = std::fabs(projectionLocation.m_signedDistance); } else { throw SurfaceProjectorException("Failed to convert from edge/node projection to triangle projection"); } } } /** * Project a coordinate to the surface using a barycentric projection. * @param surfaceFile * Surface to which triangle projection is made. * @param projectionLocation * Contains informaiton about item on the surface file. * @param baryProj * The barycentric projection that will be updated. * * @throws SurfaceProjectorException If projecting an item * failed. * */ void SurfaceProjector::projectToSurfaceTriangle(const SurfaceFile* surfaceFile, const ProjectionLocation& projectionLocation, SurfaceProjectionBarycentric* baryProj) { /* * At one time, there was a need to 'perturb' (slightly move) the * surface, probably for registration. */ projectToSurfaceTriangleAux(surfaceFile, projectionLocation, baryProj); if (baryProj->isValid()) { if (m_surfaceOffsetValid) { baryProj->setSignedDistanceAboveSurface(m_surfaceOffset); } } } /** * Get the location on the surface nearest the given coordinate. * @param surfaceFile * Surface for location. * @param xyz * The coordinate. * @param projectionLocation * Output containing location on surface information. */ void SurfaceProjector::getProjectionLocation(const SurfaceFile* surfaceFile, const float xyz[3], ProjectionLocation& projectionLocation) const { /* * Find nearest point on the surface */ CaretPointer sdh = surfaceFile->getSignedDistanceHelper(); BarycentricInfo baryInfo; sdh->barycentricWeights(xyz, baryInfo); int32_t nearestNode = -1; float maxWeight = -1; std::vector nodes; std::vector weights; for (int32_t i = 0; i < 3; i++) { if (baryInfo.baryWeights[i] > 0.0) { nodes.push_back(baryInfo.nodes[i]); const float w = baryInfo.baryWeights[i]; weights.push_back(w); if (w > maxWeight) { nearestNode = baryInfo.nodes[i]; maxWeight = w; } } } if (nearestNode < 0) { throw SurfaceProjectorException("ERROR: Nearest node is invalid"); } float signedDistance = 0.0; switch (baryInfo.type) { case BarycentricInfo::NODE: { if (nodes.size() != 1) { throw SurfaceProjectorException("ERROR: project to node number of weights incorrect=" + AString::number(nodes.size())); } else { const float* nodeNormal = surfaceFile->getNormalVector(nodes[0]); const float* c1 = surfaceFile->getCoordinate(nodes[0]); const float aboveBelowPlane = MathFunctions::signedDistanceFromPlane(nodeNormal, c1, xyz); const float signValue = ((aboveBelowPlane > 0.0) ? 1.0 : -1.0); signedDistance = (MathFunctions::distance3D(xyz, c1) * signValue); } } break; case BarycentricInfo::EDGE: { if (nodes.size() != 2) { throw SurfaceProjectorException("ERROR: project to edge number weights incorrect=" + AString::number(nodes.size())); } else { const float* n1 = surfaceFile->getNormalVector(nodes[0]); const float* n2 = surfaceFile->getNormalVector(nodes[1]); float avgNormal[3]; MathFunctions::addVectors(n1, n2, avgNormal); MathFunctions::normalizeVector(avgNormal); const float* c1 = surfaceFile->getCoordinate(nodes[0]); const float* c2 = surfaceFile->getCoordinate(nodes[1]); MathFunctions::distanceToLine3D(c1, c2, xyz); const float aboveBelowPlane = MathFunctions::signedDistanceFromPlane(avgNormal, baryInfo.point, xyz); const float signValue = ((aboveBelowPlane > 0.0) ? 1.0 : -1.0); signedDistance = (MathFunctions::distance3D(xyz, baryInfo.point) * signValue); } } break; case BarycentricInfo::TRIANGLE: { if (nodes.size() != 3) { throw SurfaceProjectorException("ERROR: project to triangle number of weights incorrect=" + AString::number(nodes.size())); } else { float triangleNormal[3]; surfaceFile->getTriangleNormalVector(baryInfo.triangle, triangleNormal); const float* c1 = surfaceFile->getCoordinate(nodes[0]); signedDistance = MathFunctions::signedDistanceFromPlane(triangleNormal, c1, xyz); } } break; } /* * Topology helper */ CaretPointer topologyHelper = surfaceFile->getTopologyHelper(); /* * Triangle(s) near projection point on surface */ std::vector nearbyTriangles; /* * Load up the projection information. */ projectionLocation.m_type = ProjectionLocation::INVALID; switch (baryInfo.type) { case BarycentricInfo::NODE: { projectionLocation.m_type = ProjectionLocation::NODE; int32_t numTriangles = 0; const int32_t* nodesTriangles = topologyHelper->getNodeTiles(nodes[0], numTriangles); /* * Make sure nearest triangle is first and * keep triangles ordering */ int32_t iStart = 0; for (int32_t i = 0; i < numTriangles; i++) { if (nodesTriangles[i] == baryInfo.triangle) { iStart = i; break; } } if (iStart < 0) { throw SurfaceProjectorException("PROGRAM ERROR: Nearest triangle node found to be associated with nearest node"); } for (int32_t i = iStart; i < numTriangles; i++) { nearbyTriangles.push_back(nodesTriangles[i]); } for (int32_t i = 0; i < iStart; i++) { nearbyTriangles.push_back(nodesTriangles[i]); } } break; case BarycentricInfo::EDGE: { projectionLocation.m_type = ProjectionLocation::EDGE; const int32_t oppositeTriangle = surfaceFile->getTriangleThatSharesEdge(nodes[0], nodes[1], baryInfo.triangle); nearbyTriangles.push_back(baryInfo.triangle); nearbyTriangles.push_back(oppositeTriangle); } break; case BarycentricInfo::TRIANGLE: projectionLocation.m_type = ProjectionLocation::TRIANGLE; nearbyTriangles.push_back(baryInfo.triangle); break; } for (int32_t i = 0; i < 3; i++) { projectionLocation.m_pointXYZ[i] = xyz[i]; projectionLocation.m_surfaceXYZ[i] = baryInfo.point[i]; if (i < static_cast(nodes.size())) { projectionLocation.m_nodes[i] = nodes[i]; projectionLocation.m_weights[i] = weights[i]; } else { projectionLocation.m_nodes[i] = -1; projectionLocation.m_weights[i] = 0.0; } } projectionLocation.m_numberOfTriangles = static_cast(nearbyTriangles.size()); projectionLocation.m_triangleIndices = new int32_t[projectionLocation.m_numberOfTriangles]; for (int32_t i = 0; i < projectionLocation.m_numberOfTriangles; i++) { projectionLocation.m_triangleIndices[i] = nearbyTriangles[i]; } projectionLocation.m_absoluteDistance = baryInfo.absDistance; projectionLocation.m_signedDistance = signedDistance; projectionLocation.m_nearestNode = nearestNode; AString distErrorMessage = ""; float distError = std::fabs(signedDistance) - baryInfo.absDistance; if (distError > 0.01) { throw SurfaceProjectorException("ERROR: signed/abs distance mismatch: " + projectionLocation.toString(surfaceFile)); } } /** * Project a coordinate to the surface * @param surfaceFile * Surface file to which item is projected. * @param projectionLocation * Contains informaiton about item on the surface file. * @param baryProj * The barycentric projection that will be set. * @return * The node nearest the coordinate. * @throws SurfaceProjectorException * If projecting an item failed. */ int32_t SurfaceProjector::projectToSurfaceTriangleAux(const SurfaceFile* surfaceFile, const ProjectionLocation& projectionLocation, SurfaceProjectionBarycentric* baryProj) { /* * Set the projection. */ baryProj->setTriangleAreas(projectionLocation.m_weights); baryProj->setTriangleNodes(projectionLocation.m_nodes); baryProj->setProjectionSurfaceNumberOfNodes(surfaceFile->getNumberOfNodes()); if (m_surfaceOffsetValid) { baryProj->setSignedDistanceAboveSurface(m_surfaceOffset); } else { baryProj->setSignedDistanceAboveSurface(projectionLocation.m_signedDistance); } baryProj->setValid(true); return projectionLocation.m_nearestNode; } /** * See if the coordinate is within the triangle. * @param surfaceFile * Surface file to which item is projected. * @param triangleNumber * Triangle to check. * @param xyz * The coordinate * @param degenerateTolerance * If the point is outside the triangle, an output area will be negative. * In most cases use zero or a negative value (-0.01) very near zero. A very * negative value can be used to allow degenerate cases. * @param baryProj * Barycentric projection into triangle. */ void SurfaceProjector::checkItemInTriangle(const SurfaceFile* surfaceFile, const int32_t triangleNumber, const float xyz[3], const float degenerateTolerance, SurfaceProjectionBarycentric* baryProj) { // // Vertices of the triangle // const int32_t* tn = surfaceFile->getTriangle(triangleNumber); const float* v1 = surfaceFile->getCoordinate(tn[0]); const float* v2 = surfaceFile->getCoordinate(tn[1]); const float* v3 = surfaceFile->getCoordinate(tn[2]); // // coordinate that may be pushed to a plane depending upon surfac type // float queryXYZ[3] = { xyz[0], xyz[1], xyz[2] }; // // Initialize normal vector to normal of triangle // float normal[3]; MathFunctions::normalVector(v1, v2, v3, normal); // // Adjust the query coordinate based upon the surface type // switch (m_surfaceTypeHint) { case SURFACE_HINT_FLAT: // // Override normal with flat surface normal // normal[0] = 0.0f; normal[1] = 0.0f; normal[2] = 1.0f; queryXYZ[2] = 0.0f; // place on plane break; case SURFACE_HINT_SPHERE: { if (m_sphericalSurfaceRadius > 0.0) { MathFunctions::normalizeVector(queryXYZ); queryXYZ[0] *= m_sphericalSurfaceRadius; queryXYZ[1] *= m_sphericalSurfaceRadius; queryXYZ[2] *= m_sphericalSurfaceRadius; } float origin[3] = { 0.0f, 0.0f, 0.0f }; float xyzDistance[4] = { 0.0f, 0.0f, 0.0f, 0.0f }; if (MathFunctions::rayIntersectPlane(v1, v2, v3, origin, queryXYZ, xyzDistance) == false) { // // Ray does not intersect, must be parallel to plane // return; } // // Use intersection point // queryXYZ[0] = xyzDistance[0]; queryXYZ[1] = xyzDistance[1]; queryXYZ[2] = xyzDistance[2]; } break; case SURFACE_HINT_THREE_DIMENSIONAL: { // // Project point to the triangle // float xyzOnPlane[3]; MathFunctions::projectPoint(queryXYZ, v1, normal, xyzOnPlane); queryXYZ[0] = xyzOnPlane[0]; queryXYZ[1] = xyzOnPlane[1]; queryXYZ[2] = xyzOnPlane[2]; } break; } // // Note that if tolerance is a small negative number (which is done to handle // degenerate cases - projected point on vertex or edge of triangle) an area may // be negative and we continue searching tiles. If all areas are positive // then there is no need to continue searching. // float areas[3] = { 0.0f, 0.0f, 0.0f }; int result = triangleAreas(v1, v2, v3, normal, queryXYZ, degenerateTolerance, areas); if (result != 0) { baryProj->setValid(true); if (result < 0) { baryProj->setDegenerate(true); } float signedDistanceToTriangle = MathFunctions::signedDistanceFromPlane(normal, v1, xyz); baryProj->setTriangleAreas(areas); baryProj->setTriangleNodes(tn); baryProj->setSignedDistanceAboveSurface(signedDistanceToTriangle); baryProj->setProjectionSurfaceNumberOfNodes(surfaceFile->getNumberOfNodes()); } } /** * Compute the signed areas formed by assuming "xyz" is contained in the triangle formed * by the points "p1, p2, p3". "area2" and "area3" may not be set if "xyz" is not * within the triangle. * * @param p1 * Coordinate of triangle node1. * @param p2 * Coordinate of triangle node2. * @param p3 * Coordinate of triangle node3. * @param normal * Triangle's normal vector. * @param xyz * The coordinate being examined. * @param degenerateTolerance * If the point is outside the triangle, an output area will be negative. * In most cases use zero or a negative value (-0.01) very near zero. A very * negative value can be used to allow degenerate cases. * @param areasOut * Output barycentric areas of xyz in the triangle OUTPUT. * @return * Returns 1 if all areas are positive (point32_t inside the triangle). * Returns -1 if all areas are greater than the tolerance * (point32_t may be on edge or vertex) * Returns 0 if not in the triangle. * */ int32_t SurfaceProjector::triangleAreas( const float p1[3], const float p2[3], const float p3[3], const float normal[3], const float xyz[3], const float degenerateTolerance, float areasOut[3]) { float area1 = 0.0f; float area2 = 0.0f; float area3 = 0.0f; int32_t result = 0; float triangleArea = 0.0f; bool inside = false; switch (m_surfaceTypeHint) { case SURFACE_HINT_FLAT: area1 = MathFunctions::triangleAreaSigned2D(p2, p3, xyz); if (area1 > degenerateTolerance) { area2 = MathFunctions::triangleAreaSigned2D(p3, p1, xyz); if (area2 > degenerateTolerance) { area3 = MathFunctions::triangleAreaSigned2D(p1, p2, xyz); if (area3 > degenerateTolerance) { inside = true; triangleArea = MathFunctions::triangleAreaSigned2D(p1, p2, p3); } } } break; case SURFACE_HINT_SPHERE: case SURFACE_HINT_THREE_DIMENSIONAL: area1 = MathFunctions::triangleAreaSigned3D(normal, p2, p3, xyz); if (area1 >= degenerateTolerance) { area2 = MathFunctions::triangleAreaSigned3D(normal, p3, p1, xyz); if (area2 >= degenerateTolerance) { area3 = MathFunctions::triangleAreaSigned3D(normal, p1,p2,xyz); if (area3 >= degenerateTolerance) { inside = true; triangleArea = MathFunctions::triangleArea(p1, p2, p3); } } } break; } if (inside) { if ((area1 > 0.0) && (area2 > 0.0) && (area3 > 0.0)) { result = 1; } else { result = -1; } // if (area1 < 0.0) area1 = -area1; // if (area2 < 0.0) area2 = -area2; // if (area3 < 0.0) area3 = -area3; if (triangleArea > 0.0) { //area1 /= triangleArea; //area2 /= triangleArea; //area3 /= triangleArea; } else { area1 = 1.0f; area2 = 0.0f; area3 = 0.0f; } } areasOut[0] = area1; areasOut[1] = area2; areasOut[2] = area3; return result; } /** * Perform a VanEssen Projection that projects to the edge of two triangles. * * @param surfaceFile * File to which item is projected. * @param projectionLocation * Contains informaiton about item on the surface file. * @param spve * The Van Essen Projection that is setup. * @throws SurfaceProjectorException If projection failure. * */ void SurfaceProjector::projectWithVanEssenAlgorithm(const SurfaceFile* surfaceFile, const ProjectionLocation& projectionLocation, SurfaceProjectionVanEssen* spve) { float xyz[3] = { projectionLocation.m_pointXYZ[0], projectionLocation.m_pointXYZ[1], projectionLocation.m_pointXYZ[2] }; const bool pointIsUnderSurface = (projectionLocation.m_signedDistance < 0.0); // // Find nearest triangle to coordinate // const int32_t nearestTriangle = projectionLocation.m_triangleIndices[0]; if (nearestTriangle < 0) { throw SurfaceProjectorException( "Unable to find nearest triangle for VanEssen projection."); } // // Get triangle's nodes and their coordinates // const int32_t* tn = surfaceFile->getTriangle(nearestTriangle); int32_t n1 = tn[0]; int32_t n2 = tn[1]; int32_t n3 = tn[2]; const float* p1 = surfaceFile->getCoordinate(n1); const float* p2 = surfaceFile->getCoordinate(n2); const float* p3 = surfaceFile->getCoordinate(n3); // // Project the coordinate to the plane of nearest triangle // float planeNormal[3]; MathFunctions::normalVector(p1, p2, p3, planeNormal); float xyzOnPlane[3]; MathFunctions::projectPoint(xyz, p1, planeNormal, xyzOnPlane); // // Adjust for surface offset // if (m_surfaceOffsetValid) { for (int32_t i = 0; i < 3; i++) { xyz[i] = xyzOnPlane[i] + planeNormal[i] * m_surfaceOffset; } } /* * With the nearest triangle, determine which edge is closest * to the coordinate */ const int32_t closestVertices[2] = { projectionLocation.m_nodes[0], projectionLocation.m_nodes[1] }; /* * Nodes and triangles using the edge */ int32_t iR = closestVertices[0]; int32_t jR = closestVertices[1]; int32_t triA = nearestTriangle; int32_t triB = projectionLocation.m_triangleIndices[1]; const float* coordJR = surfaceFile->getCoordinate(jR); const float* coordIR = surfaceFile->getCoordinate(iR); /* * Normal vector for triangle nearest the coordinate */ float normalA[3]; surfaceFile->getTriangleNormalVector(triA, normalA); /* * When point is under surface, need to flip the normal vector */ if (pointIsUnderSurface) { for (int32_t i = 0; i < 3; i++) { normalA[i] *= -1.0; } } /* * Second triangle might not be found if topology is open or cut. */ float normalB[3] = { 0.0f, 0.0f, 0.0f }; if (triB >= 0) { /* * Normal vector for triangle sharing edge with nearest triangle */ surfaceFile->getTriangleNormalVector(triB, normalB); /* * When point is under surface, need to flip the normal vector */ if (pointIsUnderSurface) { for (int32_t i = 0; i < 3; i++) { normalB[i] *= -1.0; } } } else { float dR = (float)std::sqrt(MathFunctions::distance3D(xyzOnPlane, xyz)); float v[3]; MathFunctions::subtractVectors(coordJR, coordIR, v); float t1[3]; MathFunctions::subtractVectors(xyz, coordIR, t1); float t2 = MathFunctions::dotProduct(v, v); float t3 = MathFunctions::dotProduct(t1, v); float QR[3] = { 0.0f, 0.0f, 0.0f }; for (int32_t j = 0; j < 3; j++) { QR[j] = coordIR[j] + ((t3/t2) * v[j]); } MathFunctions::subtractVectors(coordJR, coordIR, v); t2 = MathFunctions::vectorLength(v); MathFunctions::subtractVectors(QR, coordIR, t1); t3 = MathFunctions::vectorLength(t1); float fracRI = 0.0f; if (t2 > 0.0f) { fracRI = t3/t2; } MathFunctions::subtractVectors(coordIR, coordJR, v); t2 = MathFunctions::vectorLength(v); MathFunctions::subtractVectors(QR, coordJR, t1); t3 = MathFunctions::vectorLength(t1); float fracRJ = 0.0f; if (t2 > 0.0f) { fracRJ = t3/t2; } else { fracRI = 0.0f; // uses fracRI seems wrong but like this in OLD code } if (fracRI > 1.0f) { for (int32_t j = 0; j < 3; j++) { QR[j] = coordJR[j]; } } if (fracRJ > 1.0f) { for (int32_t j = 0; j < 3; j++) { QR[j] = coordIR[j]; } } MathFunctions::subtractVectors(xyz, xyzOnPlane, t1); t2 = MathFunctions::vectorLength(t1); if (t2 > 0.0f) { for (int32_t j = 0; j < 3; j++) { t1[j] = t1[j]/t2; } } t3 = MathFunctions::dotProduct(t1, normalA); for (int32_t j = 0; j < 3; j++) { xyz[j] = QR[j] + (dR * t3 * normalA[j]); } } /* * Vector from "IR" to "JR" */ float v[3]; MathFunctions::subtractVectors(coordJR, coordIR, v); /* * Vector from "IR" to "xyz" */ float t1[3]; MathFunctions::subtractVectors(xyz, coordIR, t1); float t2 = MathFunctions::dotProduct(v, v); float t3 = MathFunctions::dotProduct(t1, v); float QR[3] = { 0.0f, 0.0f, 0.0f }; for (int32_t j = 0; j < 3; j++) { QR[j] = coordIR[j] + ((t3/t2) * v[j]); } if ((triA >= 0) && (triB >= 0)) { /* * t2 is arccos of angle between the normal vectors of the two triangles */ t2 = MathFunctions::dotProduct(normalA, normalB); t2 = std::min(t2, 1.0f); /* * Angle formed by the normal vectors of the two triangles */ spve->setPhiR((float)std::acos(t2)); if (m_validateFlag) { m_validateItemName += (", t2=" + AString::number(t2) + ", angleDegrees=" + AString::number(MathFunctions::toDegrees(spve->getPhiR()))); } } else { spve->setPhiR(0.0f); } /* * Vector from "QR" to "xyz" */ MathFunctions::subtractVectors(xyz, QR, t1); MathFunctions::normalizeVector(t1); /* * t3 is arccos of nearest triangle and "t1" */ t3 = MathFunctions::dotProduct(normalA, t1); if (t3 > 0.0f) { spve->setThetaR((float)std::acos(t3 * (t3/std::fabs(t3)))); if ((spve->getThetaR() > -0.001) && (spve->getThetaR() < 0.001)) { const float oneDegreeRadians = (1.0 * M_PI) / 180.0; if (spve->getPhiR() < oneDegreeRadians) { m_validateItemName += (",t3=" + AString::number(t3) + ",thetaR=" + AString::number(spve->getThetaR(), 'f', 10) + "THETAR=NEAR0,t2=SMALL-POSITIVE***"); } } } else { spve->setThetaR(0.0f); if (m_validateFlag) { if (t2 < 0.0) { m_validateItemName += (" ***THETAR=0,t2=NEG***"); } } } MathFunctions::subtractVectors(coordJR, coordIR, v); t2 = MathFunctions::vectorLength(v); MathFunctions::subtractVectors(QR, coordIR, t1); t3 = MathFunctions::vectorLength(t1); if (t2 > 0.0f) { spve->setFracRI(t3/t2); } else { spve->setFracRI(0.0f); } MathFunctions::subtractVectors(coordIR, coordJR, v); t2 = MathFunctions::vectorLength(v); MathFunctions::subtractVectors(QR, coordJR, t1); t3 = MathFunctions::vectorLength(t1); if (t2 > 0.0f) { spve->setFracRJ(t3/t2); } else { spve->setFracRJ(0.0f); } spve->setDR(MathFunctions::distance3D(QR, xyz)); const int32_t* triANodes = surfaceFile->getTriangle(triA); int32_t nodesA[3] = { triANodes[0], triANodes[1], triANodes[2] }; int32_t swapA = nodesA[0]; nodesA[0] = nodesA[2]; nodesA[2] = swapA; spve->setTriVertices(0, nodesA); spve->setTriAnatomical(0,0,surfaceFile->getCoordinate(nodesA[0])); spve->setTriAnatomical(0,1,surfaceFile->getCoordinate(nodesA[1])); spve->setTriAnatomical(0,2,surfaceFile->getCoordinate(nodesA[2])); if (triB >= 0) { const int32_t* triBNodes = surfaceFile->getTriangle(triB); int32_t nodesB[3] = { triBNodes[0], triBNodes[1], triBNodes[2] }; int32_t swapB = nodesB[0]; nodesB[0] = nodesB[2]; nodesB[2] = swapB; spve->setTriVertices(1, nodesB); spve->setTriAnatomical(1,0,surfaceFile->getCoordinate(nodesB[0])); spve->setTriAnatomical(1,1,surfaceFile->getCoordinate(nodesB[1])); spve->setTriAnatomical(1,2,surfaceFile->getCoordinate(nodesB[2])); } else { int32_t intZeros[3] = { 0, 0, 0 }; spve->setTriVertices(1, intZeros); float zeros[3] = { 0.0f, 0.0f, 0.0f }; spve->setTriAnatomical(1, 0, zeros); spve->setTriAnatomical(1, 1, zeros); spve->setTriAnatomical(1, 2, zeros); } spve->setVertexAnatomical(0, coordIR); spve->setVertexAnatomical(1, coordJR); spve->setVertex(0, iR); spve->setVertex(1, jR); spve->setProjectionSurfaceNumberOfNodes(surfaceFile->getNumberOfNodes()); spve->setValid(true); } /* ========================================================================== */ /** * \class caret::SurfaceProjector::ProjectionLocation * \brief Contains information about nearby point on surface */ /** * Constructor. */ SurfaceProjector::ProjectionLocation::ProjectionLocation() { m_type = INVALID; m_triangleIndices = NULL; m_numberOfTriangles = 0; } /** * Destructor. */ SurfaceProjector::ProjectionLocation::~ProjectionLocation() { if (m_triangleIndices != NULL) { delete[] m_triangleIndices; } } /** * Get String describing content. * @param surfaceFile * Surface file used for projection * @return * Description. */ AString SurfaceProjector::ProjectionLocation::toString(const SurfaceFile* surfaceFile) const { AString typeString; switch (m_type) { case EDGE: typeString = "EDGE"; break; case INVALID: typeString = "INVALID"; break; case NODE: typeString = "NODE"; break; case TRIANGLE: typeString = "TRIANGLE"; break; } AString msg = (" Type=" + typeString + " Pos=(" + AString::fromNumbers(m_pointXYZ, 3, ",") + ") SurfacePos=(" + AString::fromNumbers(m_surfaceXYZ, 3, ",") + ") Triangles=(" + AString::fromNumbers(m_triangleIndices, m_numberOfTriangles, ",") + ") AbsDistance=" + AString::number(m_absoluteDistance) + " SignedDistance=" + AString::number(m_signedDistance) + " Nodes=(" + AString::fromNumbers(m_nodes, 3, ",") + ") Weights=(" + AString::fromNumbers(m_weights, 3, ",") + ") NearestNode=" + AString::number(m_nearestNode) + " Node-XYZ=(" + AString::fromNumbers(surfaceFile->getCoordinate(m_nearestNode), 3,",") + ")"); return msg; } connectome-workbench-1.2.3+git41-gc4c6c90/src/Files/SurfaceProjector.h000066400000000000000000000200031300200146000252320ustar00rootroot00000000000000#ifndef __SURFACE_PROJECTOR_H__ #define __SURFACE_PROJECTOR_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 "SurfaceProjectorException.h" #include #include namespace caret { class FociFile; class Focus; class SurfaceFile; class SurfaceProjectedItem; class SurfaceProjectionBarycentric; class SurfaceProjectionVanEssen; class TopologyHelper; /** * Class for projecting items to the surface. */ class SurfaceProjector : public CaretObject { public: SurfaceProjector(const SurfaceFile* surfaceFile); SurfaceProjector(const std::vector& surfaceFiles); SurfaceProjector(const SurfaceFile* leftSurfaceFile, const SurfaceFile* rightSurfaceFile, const SurfaceFile* cerebellumSurfaceFile); virtual ~SurfaceProjector(); void projectItemToTriangle(SurfaceProjectedItem* spi); void projectItemToTriangleOrEdge(SurfaceProjectedItem* spi); void projectFociFile(FociFile* fociFile); void projectFocus(const int32_t focusIndex, Focus* focus); void setSurfaceOffset(const float surfaceOffset); private: enum SurfaceHintType { SURFACE_HINT_FLAT, SURFACE_HINT_SPHERE, SURFACE_HINT_THREE_DIMENSIONAL }; enum Mode { MODE_LEFT_RIGHT_CEREBELLUM, MODE_SURFACES }; class ProjectionLocation { public: enum Type { EDGE, INVALID, NODE, TRIANGLE }; ProjectionLocation(); ~ProjectionLocation(); AString toString(const SurfaceFile* surfaceFile) const; /** Type of surface item projected to */ Type m_type; /** Coordinate that was projected */ float m_pointXYZ[3]; /** Nearest coordinate on surface */ float m_surfaceXYZ[3]; /** Nearest triangle(s) indices (closest triangle always first) */ int32_t* m_triangleIndices; /** Number of triangles */ int32_t m_numberOfTriangles; /** Absolute distance to the surface */ float m_absoluteDistance; /** Signed distance to surface (positive=>above, negative=>below) */ float m_signedDistance; /** Nodes of node/edge/triangle (node has 1 element, edge 2, triangle 3) */ int32_t m_nodes[3]; /** Weights cooresponding to nodes (node has 1 element, edge 2, triangle 3) */ float m_weights[3]; /** Node nearest to the coordinate that was projected */ int32_t m_nearestNode; }; SurfaceProjector(const SurfaceProjector& o); SurfaceProjector& operator=(const SurfaceProjector& o); void initializeMembersSurfaceProjector(); void getProjectionLocation(const SurfaceFile* surfaceFile, const float xyz[3], ProjectionLocation& projectionLocation) const; void projectItem(SurfaceProjectedItem* spi, SurfaceProjectedItem* secondSpi); void projectItemToSurfaceFile(const SurfaceFile* surfaceFile, SurfaceProjectedItem* spi); void projectToSurface(const SurfaceFile* surfaceFile, const float xyz[3], SurfaceProjectedItem* spi) ; void projectToSurfaceTriangle(const SurfaceFile* surfaceFile, const ProjectionLocation& projectionLocation, SurfaceProjectionBarycentric* baryProj) ; int32_t projectToSurfaceTriangleAux(const SurfaceFile* surfaceFile, const ProjectionLocation& projectionLocation, SurfaceProjectionBarycentric* baryProj) ; void checkItemInTriangle(const SurfaceFile* surfaceFile, const int32_t triangleNumber, const float xyz[3], const float degenerateTolerance, SurfaceProjectionBarycentric* baryProj); int32_t triangleAreas( const float p1[3], const float p2[3], const float p3[3], const float normal[3], const float xyz[3], const float degenerateTolerance, float areasOut[3]); void convertToTriangleProjection(const SurfaceFile* surfaceFile, ProjectionLocation& projectionLocation); void projectWithVanEssenAlgorithm(const SurfaceFile* surfaceFile, const ProjectionLocation& projectionLocation, SurfaceProjectionVanEssen* spve) ; std::vector m_surfaceFiles; const SurfaceFile* m_surfaceFileLeft; const SurfaceFile* m_surfaceFileRight; const SurfaceFile* m_surfaceFileCerebellum; const Mode m_mode; SurfaceHintType m_surfaceTypeHint; float m_sphericalSurfaceRadius; float m_surfaceOffset; bool m_surfaceOffsetValid; bool m_allowEdgeProjection; bool m_validateFlag; AString m_validateItemName; AString m_projectionWarning; /** Point in triangle test tolerance that requires point inside triangle */ static float s_normalTriangleAreaTolerance; /** Point in triangle test tolerance that allows point outside triangle (degenerate case) */ static float s_extremeTriangleAreaTolerance; /** Projection distance error used to compare original and after projection/unprojection */ static float s_projectionDistanceError; /** Cutoff for item that that is projected to cortex and not cerebellum */ static float s_corticalSurfaceCutoff; /** Cutoff for item that that is projected to cerebellum and not cortex */ static float s_cerebellumSurfaceCutoff; }; #ifdef __SURFACE_PROJECTOR_DEFINE__ float SurfaceProjector::s_normalTriangleAreaTolerance = -0.01; float SurfaceProjector::s_extremeTriangleAreaTolerance = -10000000.0; float SurfaceProjector::s_projectionDistanceError = 0.5; float SurfaceProjector::s_corticalSurfaceCutoff = 2.0; float SurfaceProjector::s_cerebellumSurfaceCutoff = 4.0; #endif // __SURFACE_PROJECTOR_DEFINE__ } // namespace #endif // __SURFACE_PROJECTOR_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Files/SurfaceProjectorException.cxx000066400000000000000000000037261300200146000275010ustar00rootroot00000000000000/*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 "SurfaceProjectorException.h" #include using namespace caret; /** * Constructor. * */ SurfaceProjectorException::SurfaceProjectorException() : CaretException() { this->initializeMembersSurfaceProjectorException(); } /** * Constructor. * * @param s Description of the exception. * */ SurfaceProjectorException::SurfaceProjectorException( const AString& s) : CaretException(s) { this->initializeMembersSurfaceProjectorException(); } /** * Copy Constructor. * @param e * Exception that is copied. */ SurfaceProjectorException::SurfaceProjectorException(const SurfaceProjectorException& e) : CaretException(e) { } /** * Assignment operator. * @param e * Exception that is copied. * @return * Copy of the exception. */ SurfaceProjectorException& SurfaceProjectorException::operator=(const SurfaceProjectorException& e) { if (this != &e) { CaretException::operator=(e); } return *this; } /** * Destructor */ SurfaceProjectorException::~SurfaceProjectorException() throw() { } void SurfaceProjectorException::initializeMembersSurfaceProjectorException() { } connectome-workbench-1.2.3+git41-gc4c6c90/src/Files/SurfaceProjectorException.h000066400000000000000000000030541300200146000271200ustar00rootroot00000000000000#ifndef __SURFACE_PROJECTOR_EXCEPTION_H__ #define __SURFACE_PROJECTOR_EXCEPTION_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 "CaretException.h" namespace caret { /** * An exception thrown during surface projector processing. */ class SurfaceProjectorException : public CaretException { public: SurfaceProjectorException(); SurfaceProjectorException(const AString& s); SurfaceProjectorException(const SurfaceProjectorException& e); SurfaceProjectorException& operator=(const SurfaceProjectorException& e); virtual ~SurfaceProjectorException() throw(); private: void initializeMembersSurfaceProjectorException(); }; } // namespace #endif // __SURFACE_PROJECTOR_EXCEPTION_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Files/SurfaceResamplingHelper.cxx000066400000000000000000000641161300200146000271140ustar00rootroot00000000000000/*LICENSE_START*/ /* * Copyright (C) 2014 Washington University School of Medicine * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be included * in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*LICENSE_END*/ #include "SurfaceResamplingHelper.h" #include "CaretAssert.h" #include "CaretException.h" #include "CaretOMP.h" #include "GeodesicHelper.h" #include "SignedDistanceHelper.h" #include "SurfaceFile.h" #include "TopologyHelper.h" #include "Vector3D.h" #include #include using namespace std; using namespace caret; SurfaceResamplingHelper::SurfaceResamplingHelper(const SurfaceResamplingMethodEnum::Enum& myMethod, const SurfaceFile* currentSphere, const SurfaceFile* newSphere, const float* currentAreas, const float* newAreas, const float* currentRoi) { if (!checkSphere(currentSphere) || !checkSphere(newSphere)) throw CaretException("input surfaces to SurfaceResamplingHelper must be spheres"); SurfaceFile currentSphereMod, newSphereMod; changeRadius(100.0f, currentSphere, ¤tSphereMod); changeRadius(100.0f, newSphere, &newSphereMod); switch (myMethod) { case SurfaceResamplingMethodEnum::ADAP_BARY_AREA: CaretAssert(currentAreas != NULL && newAreas != NULL); if (currentAreas == NULL || newAreas == NULL) throw CaretException("ADAP_BARY_AREA method requires area surfaces"); computeWeightsAdapBaryArea(¤tSphereMod, &newSphereMod, currentAreas, newAreas, currentRoi); break; case SurfaceResamplingMethodEnum::BARYCENTRIC: computeWeightsBarycentric(¤tSphereMod, &newSphereMod, currentRoi); break; } } void SurfaceResamplingHelper::resampleNormal(const float* input, float* output, const float& invalidVal) const { int numNodes = (int)m_weights.size() - 1; #pragma omp CARET_PARFOR schedule(dynamic) for (int i = 0; i < numNodes; ++i) { WeightElem* end = m_weights[i + 1], *elem = m_weights[i]; if (elem != end) { double accum = 0.0; for (; elem != end; ++elem) { accum += input[elem->node] * elem->weight;//don't need to divide afterwards, because the weights already sum to 1 } output[i] = accum; } else { output[i] = invalidVal; } } } void SurfaceResamplingHelper::resample3DCoord(const float* input, float* output) const { int numNodes = (int)m_weights.size() - 1; #pragma omp CARET_PARFOR schedule(dynamic) for (int i = 0; i < numNodes; ++i) { double tempvec[3] = { 0.0, 0.0, 0.0 }; WeightElem* end = m_weights[i + 1]; for (WeightElem* elem = m_weights[i]; elem != end; ++elem) { const float* coord = input + elem->node * 3; tempvec[0] += coord[0] * elem->weight;//don't need to divide afterwards, because the weights already sum to 1 tempvec[1] += coord[1] * elem->weight; tempvec[2] += coord[2] * elem->weight; } int i3 = i * 3; output[i3] = tempvec[0]; output[i3 + 1] = tempvec[1]; output[i3 + 2] = tempvec[2]; } } void SurfaceResamplingHelper::resamplePopular(const int32_t* input, int32_t* output, const int32_t& invalidVal) const { int numNodes = (int)m_weights.size() - 1; #pragma omp CARET_PARFOR schedule(dynamic) for (int i = 0; i < numNodes; ++i) { map accum; float maxweight = -1.0f; int32_t bestlabel = invalidVal; WeightElem* end = m_weights[i + 1]; for (WeightElem* elem = m_weights[i]; elem != end; ++elem) { int32_t label = input[elem->node]; map::iterator iter = accum.find(label); if (iter == accum.end()) { accum[label] = elem->weight; if (elem->weight > maxweight) { maxweight = elem->weight; bestlabel = label; } } else { iter->second += elem->weight; if (iter->second > maxweight) { maxweight = iter->second; bestlabel = label; } } } output[i] = bestlabel; } } void SurfaceResamplingHelper::resampleLargest(const float* input, float* output, const float& invalidVal) const { int numNodes = (int)m_weights.size() - 1; #pragma omp CARET_PARFOR schedule(dynamic) for (int i = 0; i < numNodes; ++i) { WeightElem* end = m_weights[i + 1]; float largest = -1.0f; int largestNode = -1; for (WeightElem* elem = m_weights[i]; elem != end; ++elem) { if (elem->weight > largest) { largest = elem->weight; largestNode = elem->node; } } if (largestNode != -1) { output[i] = input[largestNode]; } else { output[i] = invalidVal; } } } void SurfaceResamplingHelper::resampleLargest(const int32_t* input, int32_t* output, const int32_t& invalidVal) const { int numNodes = (int)m_weights.size() - 1; #pragma omp CARET_PARFOR schedule(dynamic) for (int i = 0; i < numNodes; ++i) { WeightElem* end = m_weights[i + 1]; float largest = -1.0f; int largestNode = -1; for (WeightElem* elem = m_weights[i]; elem != end; ++elem) { if (elem->weight > largest) { largest = elem->weight; largestNode = elem->node; } } if (largestNode != -1) { output[i] = input[largestNode]; } else { output[i] = invalidVal; } } } void SurfaceResamplingHelper::getResampleValidROI(float* output) const { int numNodes = (int)m_weights.size() - 1; for (int i = 0; i < numNodes; ++i) { if (m_weights[i] != m_weights[i + 1]) { output[i] = 1.0f; } else { output[i] = 0.0f; } } } void SurfaceResamplingHelper::resampleCutSurface(const SurfaceFile* cutSurfaceIn, const SurfaceFile* currentSphere, const SurfaceFile* newSphere, SurfaceFile* surfaceOut) { if (cutSurfaceIn->getNumberOfNodes() != currentSphere->getNumberOfNodes()) throw CaretException("input surface has different number of nodes than input sphere"); if (!checkSphere(currentSphere) || !checkSphere(newSphere)) throw CaretException("input surfaces to SurfaceResamplingHelper must be spheres"); SurfaceFile currentSphereMod, newSphereMod; changeRadius(100.0f, currentSphere, ¤tSphereMod); changeRadius(100.0f, newSphere, &newSphereMod); SurfaceFile cutCurSphere = *cutSurfaceIn; cutCurSphere.setCoordinates(currentSphereMod.getCoordinateData()); int newNodes = newSphere->getNumberOfNodes(); vector newInfo(newSphere->getNumberOfNodes()); #pragma omp CARET_PAR { CaretPointer mySignedHelp = cutCurSphere.getSignedDistanceHelper(); #pragma omp CARET_FOR schedule(dynamic) for (int i = 0; i < newNodes; ++i) { mySignedHelp->barycentricWeights(newSphereMod.getCoordinate(i), newInfo[i]); } } vector isOnEdge(newNodes, 0);//really used as bool, but avoid bitpacking so it can be modified in parallel CaretPointer cutTopoHelp = cutSurfaceIn->getTopologyHelper();//because topology didn't change, and it might have one already - also, don't need separate helpers per thread, not using neighbors to depth CaretPointer closedTopoHelp = currentSphere->getTopologyHelper();//ditto CaretPointer newTopoHelp = newSphere->getTopologyHelper();//tritto? const vector& cutEdgeInfo = cutTopoHelp->getEdgeInfo(); vector largestNode(newNodes, -1); #pragma omp CARET_PARFOR for (int i = 0; i < newNodes; ++i) { float largestWeight = 0.0f, secondWeight = 0.0f;//locate the two nodes with largest barycentric weights largestNode[i] = -1; int secondNode = -1; for (int j = 0; j < 3; ++j) { if (newInfo[i].baryWeights[j] > largestWeight) { secondNode = largestNode[i];//shift largest to second secondWeight = largestWeight; largestWeight = newInfo[i].baryWeights[j];//update largest largestNode[i] = newInfo[i].nodes[j]; } else if (newInfo[i].baryWeights[j] > secondWeight) { secondWeight = newInfo[i].baryWeights[j]; secondNode = newInfo[i].nodes[j]; } } switch (newInfo[i].type) { case BarycentricInfo::NODE: if (cutTopoHelp->getNodeTiles(largestNode[i]).size() != closedTopoHelp->getNodeTiles(largestNode[i]).size()) { isOnEdge[i] = 1; } break; case BarycentricInfo::EDGE: { const vector& cutEdges = cutTopoHelp->getNodeEdges(largestNode[i]); for (int j = 0; j < (int)cutEdges.size(); ++j) { const TopologyEdgeInfo& myInfo = cutEdgeInfo[cutEdges[j]]; if (myInfo.node1 == largestNode[i]) { if (myInfo.node2 == secondNode) { if (myInfo.numTiles == 1)//NOTE: assumes 1 tile means that it is an edge of a cut, could compare to closed instead, but need to search it separately { isOnEdge[i] = 1; } break; } } else { if (myInfo.node1 == secondNode && myInfo.node2 == largestNode[i]) { if (myInfo.numTiles == 1)//ditto { isOnEdge[i] = 1; } break; } } } break; } case BarycentricInfo::TRIANGLE://is never on a cut edge, do nothing break; } } int numNewTris = newSphere->getNumberOfTriangles(); vector triRemove(numNewTris, 0), nodeDisconnect(newNodes, 0);//again, avoid bitpacking #pragma omp CARET_PAR { CaretPointer closedGeoHelp = currentSphereMod.getGeodesicHelper(); CaretPointer cutGeoHelp = cutCurSphere.getGeodesicHelper(); #pragma omp CARET_FOR schedule(dynamic) for (int32_t i = 0; i < newNodes; ++i) { const vector& neighbors = newTopoHelp->getNodeNeighbors(i); if (isOnEdge[i]) { bool hasInteriorNeighbor = false; for (int j = 0; j < (int)neighbors.size(); ++j) { if (!isOnEdge[neighbors[j]]) { hasInteriorNeighbor = true; break; } } if (hasInteriorNeighbor) { for (int j = 0; j < (int)neighbors.size(); ++j) { vector closedPath, cutPath; vector closedPathDists, cutPathDists; closedGeoHelp->getPathToNode(largestNode[i], largestNode[neighbors[j]], closedPath, closedPathDists); cutGeoHelp->getPathToNode(largestNode[i], largestNode[neighbors[j]], cutPath, cutPathDists); if (cutPathDists.size() == 0 || cutPathDists.back() > 2.0f * closedPathDists.back())//maybe this cutoff should be tunable { const vector& myTiles = newTopoHelp->getNodeTiles(i);//find tiles on new mesh that share this edge, remove them for (int k = 0; k < (int)myTiles.size(); ++k) { const int32_t* thisTile = newSphere->getTriangle(myTiles[k]); if (thisTile[0] == neighbors[j] || thisTile[1] == neighbors[j] || thisTile[2] == neighbors[j]) { triRemove[myTiles[k]] = 1; } } } } } else { nodeDisconnect[i] = 1;//disconnect it completely if it has no interior neighbors const vector& nodeTiles = newTopoHelp->getNodeTiles(i); for (int j = 0; j < (int)nodeTiles.size(); ++j) { triRemove[nodeTiles[j]] = 1; } } } else {//interior nodes also have to be checked for crossing the cut, since there may not be a node that falls inside the cut for (int j = 0; j < (int)neighbors.size(); ++j) { vector closedPath, cutPath; vector closedPathDists, cutPathDists; closedGeoHelp->getPathToNode(largestNode[i], largestNode[neighbors[j]], closedPath, closedPathDists); cutGeoHelp->getPathToNode(largestNode[i], largestNode[neighbors[j]], cutPath, cutPathDists);//note: path length of zero means no connection if (cutPathDists.size() == 0 || cutPathDists.back() > 2.0f * closedPathDists.back())//maybe this cutoff should be tunable { const vector& myTiles = newTopoHelp->getNodeTiles(i);//find tiles on new mesh that share this edge, remove them for (int k = 0; k < (int)myTiles.size(); ++k) { const int32_t* thisTile = newSphere->getTriangle(myTiles[k]); if (thisTile[0] == neighbors[j] || thisTile[1] == neighbors[j] || thisTile[2] == neighbors[j]) { triRemove[myTiles[k]] = 1; } } } } } } } int triRemoveCount = 0; for (int i = 0; i < numNewTris; ++i) { if (triRemove[i] != 0) ++triRemoveCount;//parallelizing counting would be silly } surfaceOut->setNumberOfNodesAndTriangles(newNodes, numNewTris - triRemoveCount); surfaceOut->setStructure(cutSurfaceIn->getStructure()); surfaceOut->setSurfaceType(cutSurfaceIn->getSurfaceType()); surfaceOut->setSecondaryType(cutSurfaceIn->getSecondaryType()); int outTri = 0; for (int i = 0; i < numNewTris; ++i) { if (triRemove[i] == 0) { surfaceOut->setTriangle(outTri, newSphere->getTriangle(i)); ++outTri;//means this can't be parallel, but it would also be silly } } CaretAssert(outTri == numNewTris - triRemoveCount); Vector3D origin(0.0f, 0.0f, 0.0f);//where we move disconnected nodes to for (int i = 0; i < newNodes; ++i)//could be parallel, but probably not needed { if (nodeDisconnect[i] != 0) { surfaceOut->setCoordinate(i, origin); } else { Vector3D coord1 = cutSurfaceIn->getCoordinate(newInfo[i].nodes[0]); Vector3D coord2 = cutSurfaceIn->getCoordinate(newInfo[i].nodes[1]); Vector3D coord3 = cutSurfaceIn->getCoordinate(newInfo[i].nodes[2]); Vector3D outCoord = coord1 * newInfo[i].baryWeights[0] + coord2 * newInfo[i].baryWeights[1] + coord3 * newInfo[i].baryWeights[2]; surfaceOut->setCoordinate(i, outCoord); } } } void SurfaceResamplingHelper::computeWeightsAdapBaryArea(const SurfaceFile* currentSphere, const SurfaceFile* newSphere, const float* currentAreas, const float* newAreas, const float* currentRoi) { vector > forward, reverse, reverse_gather; makeBarycentricWeights(currentSphere, newSphere, forward, NULL);//don't use an roi until after we have done area correction, because area correction MUST ignore ROI makeBarycentricWeights(newSphere, currentSphere, reverse, NULL); int numNewNodes = (int)forward.size(), numOldNodes = currentSphere->getNumberOfNodes(); reverse_gather.resize(numNewNodes); for (int oldNode = 0; oldNode < numOldNodes; ++oldNode)//this loop can't be parallelized { for (map::iterator iter = reverse[oldNode].begin(); iter != reverse[oldNode].end(); ++iter)//convert scattering weights to gathering weights { reverse_gather[iter->first][oldNode] = iter->second; } } vector > adap_gather(numNewNodes); #pragma omp CARET_PARFOR schedule(dynamic) for (int newNode = 0; newNode < numNewNodes; ++newNode) { set forwardused;//build set of all nodes used by forward weights (really only 3, but this is a bit cleaner and more generic) for (map::iterator iter = forward[newNode].begin(); iter != forward[newNode].end(); ++iter) { forwardused.insert(iter->first); } bool useforward = true; for (map::iterator iter = reverse_gather[newNode].begin(); iter != reverse_gather[newNode].end(); ++iter) { if (forwardused.find(iter->first) == forwardused.end()) { useforward = false;//if the reverse scatter weights include something the forward gather weights don't, use reverse scatter break; } } if (useforward) { adap_gather[newNode] = forward[newNode]; } else { adap_gather[newNode] = reverse_gather[newNode]; } for (map::iterator iter = adap_gather[newNode].begin(); iter != adap_gather[newNode].end(); ++iter)//begin area correction by multiplying by target node area { iter->second *= newAreas[newNode];//begin the process of area correction by multiplying by gathering node areas } } vector correctionSum(numOldNodes, 0.0f); for (int newNode = 0; newNode < numNewNodes; ++newNode)//this loop is separate because it can't be parallelized { for (map::iterator iter = adap_gather[newNode].begin(); iter != adap_gather[newNode].end(); ++iter) { correctionSum[iter->first] += iter->second;//now, sum the scattering weights to prepare for first normalization } } #pragma omp CARET_PARFOR schedule(dynamic) for (int newNode = 0; newNode < numNewNodes; ++newNode) { double weightsum = 0.0f; vector::iterator> toRemove; for (map::iterator iter = adap_gather[newNode].begin(); iter != adap_gather[newNode].end(); ++iter) { if (currentRoi == NULL || currentRoi[iter->first] > 0.0f) { iter->second *= currentAreas[iter->first] / correctionSum[iter->first];//divide the weights by their scatter sum, then multiply by current areas weightsum += iter->second;//and compute the sum } else { toRemove.push_back(iter); } } int numToRemove = (int)toRemove.size(); for (int i = 0; i < numToRemove; ++i) { adap_gather[newNode].erase(toRemove[i]); } if (weightsum != 0.0f)//this shouldn't happen unless no nodes remain due to roi, or node areas can be zero { for (map::iterator iter = adap_gather[newNode].begin(); iter != adap_gather[newNode].end(); ++iter) { iter->second /= weightsum;//and normalize to a sum of 1 } } } compactWeights(adap_gather);//and compact them into the internal weight storage } void SurfaceResamplingHelper::computeWeightsBarycentric(const SurfaceFile* currentSphere, const SurfaceFile* newSphere, const float* currentRoi) { vector > forward; makeBarycentricWeights(currentSphere, newSphere, forward, currentRoi);//this should ensure they sum to 1, so we are done compactWeights(forward); } bool SurfaceResamplingHelper::checkSphere(const SurfaceFile* surface) { int numNodes = surface->getNumberOfNodes(); CaretAssert(numNodes > 1); int numNodes3 = numNodes * 3; const float* coordData = surface->getCoordinateData(); float mindist = Vector3D(coordData).length(); if (mindist != mindist) throw CaretException("found NaN coordinate in an input sphere"); float maxdist = mindist; const float TOLERANCE = 1.001f; for (int i = 3; i < numNodes3; i += 3) { float tempf = Vector3D(coordData + i).length(); if (tempf != tempf) throw CaretException("found NaN coordinate in an input sphere"); if (tempf < mindist) { mindist = tempf; } if (tempf > maxdist) { maxdist = tempf; } } return (mindist * TOLERANCE > maxdist); } void SurfaceResamplingHelper::changeRadius(const float& radius, const SurfaceFile* input, SurfaceFile* output) { *output = *input; int numNodes = output->getNumberOfNodes(); int numNodes3 = numNodes * 3; vector newCoordData(numNodes3); const float* oldCoordData = output->getCoordinateData(); #pragma omp CARET_PARFOR schedule(dynamic) for (int i = 0; i < numNodes3; i += 3) { Vector3D tempvec1 = oldCoordData + i, tempvec2; tempvec2 = tempvec1 * (radius / tempvec1.length()); newCoordData[i] = tempvec2[0]; newCoordData[i + 1] = tempvec2[1]; newCoordData[i + 2] = tempvec2[2]; } output->setCoordinates(newCoordData.data()); } void SurfaceResamplingHelper::compactWeights(const vector >& weights) { int compactsize = 0; int numNodes = (int)weights.size(); m_weights = CaretArray(numNodes + 1);//include a "one-after" pointer for (int i = 0; i < numNodes; ++i) { compactsize += (int)weights[i].size(); } m_storagechunk = CaretArray(compactsize); int curpos = 0; for (int i = 0; i < numNodes; ++i) { m_weights[i] = m_storagechunk + curpos; for (map::const_iterator iter = weights[i].begin(); iter != weights[i].end(); ++iter) { m_storagechunk[curpos] = WeightElem(iter->first, iter->second); ++curpos; } } CaretAssert(curpos == compactsize); m_weights[numNodes] = m_storagechunk + compactsize; } void SurfaceResamplingHelper::makeBarycentricWeights(const SurfaceFile* from, const SurfaceFile* to, vector >& weights, const float* currentRoi) { int numToNodes = to->getNumberOfNodes(); weights.resize(numToNodes); const float* toCoordData = to->getCoordinateData(); if (currentRoi == NULL) { #pragma omp CARET_PAR { CaretPointer mySignedHelp = from->getSignedDistanceHelper(); #pragma omp CARET_FOR schedule(dynamic) for (int i = 0; i < numToNodes; ++i) { BarycentricInfo myInfo; mySignedHelp->barycentricWeights(toCoordData + i * 3, myInfo); if (myInfo.baryWeights[0] != 0.0f) weights[i][myInfo.nodes[0]] = myInfo.baryWeights[0]; if (myInfo.baryWeights[1] != 0.0f) weights[i][myInfo.nodes[1]] = myInfo.baryWeights[1]; if (myInfo.baryWeights[2] != 0.0f) weights[i][myInfo.nodes[2]] = myInfo.baryWeights[2]; } } } else { #pragma omp CARET_PAR { CaretPointer mySignedHelp = from->getSignedDistanceHelper(); #pragma omp CARET_FOR schedule(dynamic) for (int i = 0; i < numToNodes; ++i) { BarycentricInfo myInfo; float weightsum = 0.0f;//there are only 3 weights, so don't bother with double precision mySignedHelp->barycentricWeights(toCoordData + i * 3, myInfo); if (myInfo.baryWeights[0] != 0.0f && currentRoi[myInfo.nodes[0]] > 0.0f) { weights[i][myInfo.nodes[0]] = myInfo.baryWeights[0]; weightsum += myInfo.baryWeights[0]; } if (myInfo.baryWeights[1] != 0.0f && currentRoi[myInfo.nodes[1]] > 0.0f) { weights[i][myInfo.nodes[1]] = myInfo.baryWeights[1]; weightsum += myInfo.baryWeights[1]; } if (myInfo.baryWeights[2] != 0.0f && currentRoi[myInfo.nodes[2]] > 0.0f) { weights[i][myInfo.nodes[2]] = myInfo.baryWeights[2]; weightsum += myInfo.baryWeights[2]; } if (weightsum != 0.0f) { for (map::iterator iter = weights[i].begin(); iter != weights[i].end(); ++iter) { iter->second /= weightsum; } } } } } } connectome-workbench-1.2.3+git41-gc4c6c90/src/Files/SurfaceResamplingHelper.h000066400000000000000000000072211300200146000265330ustar00rootroot00000000000000#ifndef __SURFACE_RESAMPLING_HELPER_H__ #define __SURFACE_RESAMPLING_HELPER_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 "CaretPointer.h" #include "SurfaceResamplingMethodEnum.h" #include #include namespace caret { class SurfaceFile; class SurfaceResamplingHelper { struct WeightElem { int node; float weight; WeightElem() { } WeightElem(const int& nodeIn, const float& weightIn) : node(nodeIn), weight(weightIn) { } }; CaretArray m_storagechunk; CaretArray m_weights; static bool checkSphere(const SurfaceFile* surface); static void changeRadius(const float& radius, const SurfaceFile* input, SurfaceFile* output); void computeWeightsAdapBaryArea(const SurfaceFile* currentSphere, const SurfaceFile* newSphere, const float* currentAreas, const float* newAreas, const float* currentRoi); void computeWeightsBarycentric(const SurfaceFile* currentSphere, const SurfaceFile* newSphere, const float* currentRoi); static void makeBarycentricWeights(const SurfaceFile* from, const SurfaceFile* to, std::vector >& weights, const float* currentRoi); void compactWeights(const std::vector >& weights); public: SurfaceResamplingHelper() { } SurfaceResamplingHelper(const SurfaceResamplingMethodEnum::Enum& myMethod, const SurfaceFile* currentSphere, const SurfaceFile* newSphere, const float* currentAreas = NULL, const float* newAreas = NULL, const float* currentRoi = NULL); ///resample real-valued data by means of weights void resampleNormal(const float* input, float* output, const float& invalidVal = 0.0f) const; ///resample 3D coordinate data by means of weights void resample3DCoord(const float* input, float* output) const; ///resample label-like data according to which value gets the largest weight sum void resamplePopular(const int32_t* input, int32_t* output, const int32_t& invalidVal = 0) const; ///resample float data according to what weight is largest void resampleLargest(const float* input, float* output, const float& invalidVal = 0.0f) const; ///resample int data according to what weight is largest void resampleLargest(const int32_t* input, int32_t* output, const int32_t& invalidVal = 0) const; ///get the ROI of nodes that have data within the input ROI void getResampleValidROI(float* output) const; ///resample a cut surface - not something you will apply multiple times, so static method static void resampleCutSurface(const SurfaceFile* cutSurfaceIn, const SurfaceFile* curSphere, const SurfaceFile* newSphere, SurfaceFile* surfaceOut); }; } #endif //__SURFACE_RESAMPLING_HELPER_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Files/SurfaceResamplingMethodEnum.cxx000066400000000000000000000231201300200146000277300ustar00rootroot00000000000000 /*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 "SurfaceResamplingMethodEnum.h" #include "CaretAssert.h" using namespace caret; std::vector SurfaceResamplingMethodEnum::enumData; bool SurfaceResamplingMethodEnum::initializedFlag = false; /** * \class caret::SurfaceResamplingMethodEnum * \brief * * */ /** * Constructor. * * @param enumValue * An enumerated value. * @param integerCode * Integer code for this enumerated value. * * @param name * Name of enumerated value. * * @param guiName * User-friendly name for use in user-interface. */ SurfaceResamplingMethodEnum::SurfaceResamplingMethodEnum(const Enum enumValue, const int32_t integerCode, const AString& name, const AString& guiName) { this->enumValue = enumValue; this->integerCode = integerCode; this->name = name; this->guiName = guiName; } /** * Destructor. */ SurfaceResamplingMethodEnum::~SurfaceResamplingMethodEnum() { } /** * Initialize the enumerated metadata. */ void SurfaceResamplingMethodEnum::initialize() { if (initializedFlag) { return; } initializedFlag = true; enumData.push_back(SurfaceResamplingMethodEnum(ADAP_BARY_AREA, 0, "ADAP_BARY_AREA", "adaptive barycentric with area correction")); enumData.push_back(SurfaceResamplingMethodEnum(BARYCENTRIC, 1, "BARYCENTRIC", "barycentric")); } /** * 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 SurfaceResamplingMethodEnum* SurfaceResamplingMethodEnum::findData(const Enum enumValue) { if (initializedFlag == false) initialize(); size_t num = enumData.size(); for (size_t i = 0; i < num; i++) { const SurfaceResamplingMethodEnum* 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 SurfaceResamplingMethodEnum::toName(Enum enumValue) { if (initializedFlag == false) initialize(); const SurfaceResamplingMethodEnum* 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. */ SurfaceResamplingMethodEnum::Enum SurfaceResamplingMethodEnum::fromName(const AString& name, bool* isValidOut) { if (initializedFlag == false) initialize(); bool validFlag = false; Enum enumValue = ADAP_BARY_AREA; for (std::vector::iterator iter = enumData.begin(); iter != enumData.end(); iter++) { const SurfaceResamplingMethodEnum& 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 SurfaceResamplingMethodEnum")); } return enumValue; } /** * Get a GUI string representation of the enumerated type. * @param enumValue * Enumerated value. * @return * String representing enumerated value. */ AString SurfaceResamplingMethodEnum::toGuiName(Enum enumValue) { if (initializedFlag == false) initialize(); const SurfaceResamplingMethodEnum* 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. */ SurfaceResamplingMethodEnum::Enum SurfaceResamplingMethodEnum::fromGuiName(const AString& guiName, bool* isValidOut) { if (initializedFlag == false) initialize(); bool validFlag = false; Enum enumValue = ADAP_BARY_AREA; for (std::vector::iterator iter = enumData.begin(); iter != enumData.end(); iter++) { const SurfaceResamplingMethodEnum& 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 SurfaceResamplingMethodEnum")); } return enumValue; } /** * Get the integer code for a data type. * * @return * Integer code for data type. */ int32_t SurfaceResamplingMethodEnum::toIntegerCode(Enum enumValue) { if (initializedFlag == false) initialize(); const SurfaceResamplingMethodEnum* 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. */ SurfaceResamplingMethodEnum::Enum SurfaceResamplingMethodEnum::fromIntegerCode(const int32_t integerCode, bool* isValidOut) { if (initializedFlag == false) initialize(); bool validFlag = false; Enum enumValue = ADAP_BARY_AREA; for (std::vector::iterator iter = enumData.begin(); iter != enumData.end(); iter++) { const SurfaceResamplingMethodEnum& 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 SurfaceResamplingMethodEnum")); } 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 SurfaceResamplingMethodEnum::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 SurfaceResamplingMethodEnum::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(SurfaceResamplingMethodEnum::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 SurfaceResamplingMethodEnum::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(SurfaceResamplingMethodEnum::toGuiName(iter->enumValue)); } if (isSorted) { std::sort(allGuiNames.begin(), allGuiNames.end()); } } connectome-workbench-1.2.3+git41-gc4c6c90/src/Files/SurfaceResamplingMethodEnum.h000066400000000000000000000054411300200146000273630ustar00rootroot00000000000000#ifndef __SURFACE_RESAMPLING_METHOD_ENUM_H__ #define __SURFACE_RESAMPLING_METHOD_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 SurfaceResamplingMethodEnum { public: /** * Enumerated values. */ enum Enum { ADAP_BARY_AREA, BARYCENTRIC }; ~SurfaceResamplingMethodEnum(); 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: SurfaceResamplingMethodEnum(const Enum enumValue, const int32_t integerCode, const AString& name, const AString& guiName); static const SurfaceResamplingMethodEnum* 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; /** 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; }; } // namespace #endif //__SURFACE_RESAMPLING_METHOD_ENUM_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Files/SurfaceTypeEnum.cxx000066400000000000000000000427671300200146000254310ustar00rootroot00000000000000 /*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 __SURFACE_TYPE_ENUM_DECLARE__ #include "SurfaceTypeEnum.h" #undef __SURFACE_TYPE_ENUM_DECLARE__ using namespace caret; /** * Constructor. * * @param e * An enumerated value. * @param name * Name of enumberated value. */ SurfaceTypeEnum::SurfaceTypeEnum(const Enum e, const AString& name, const AString& guiName, const AString& giftiName) { this->e = e; this->integerCode = SurfaceTypeEnum::integerCodeGenerator++; this->name = name; this->guiName = guiName; this->giftiName = giftiName; } /** * Destructor. */ SurfaceTypeEnum::~SurfaceTypeEnum() { } /** * Initialize the enumerated metadata. */ void SurfaceTypeEnum::initialize() { if (initializedFlag) { return; } initializedFlag = true; enumData.push_back(SurfaceTypeEnum(UNKNOWN, "UNKNOWN", "Unknown", "Unknown")); enumData.push_back(SurfaceTypeEnum(RECONSTRUCTION, "RECONSTRUCTION", "Reconstruction", "Reconstruction")); enumData.push_back(SurfaceTypeEnum(ANATOMICAL, "ANATOMICAL", "Anatomical", "Anatomical")); enumData.push_back(SurfaceTypeEnum(INFLATED, "INFLATED", "Inflated", "Inflated")); enumData.push_back(SurfaceTypeEnum(VERY_INFLATED, "VERY_INFLATED", "VeryInflated", "VeryInflated")); enumData.push_back(SurfaceTypeEnum(SPHERICAL, "SPHERICAL", "Spherical", "Spherical")); enumData.push_back(SurfaceTypeEnum(SEMI_SPHERICAL, "SEMI_SPHERICAL", "SemiSpherical", "SemiSpherical")); enumData.push_back(SurfaceTypeEnum(ELLIPSOID, "ELLIPSOID", "Ellipsoid", "Ellipsoid")); enumData.push_back(SurfaceTypeEnum(FLAT, "FLAT", "Flat", "Flat")); enumData.push_back(SurfaceTypeEnum(HULL, "HULL", "Hull", "Hull")); } /** * Find the data for and enumerated value. * @param e * The enumerated value. * @return Pointer to data for this enumerated type * or NULL if no data for type or if type is invalid. */ const SurfaceTypeEnum* SurfaceTypeEnum::findData(const Enum e) { initialize(); size_t num = enumData.size(); for (size_t i = 0; i < num; i++) { const SurfaceTypeEnum* d = &enumData[i]; if (d->e == e) { return d; } } return NULL; } /** * Get a string representation of the enumerated type. * @param e * Enumerated value. * @return * String representing enumerated value. */ AString SurfaceTypeEnum::toName(Enum e) { initialize(); const SurfaceTypeEnum* st = findData(e); return st->name; } /** * Get an enumerated value corresponding to its 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. */ SurfaceTypeEnum::Enum SurfaceTypeEnum::fromName(const AString& s, bool* isValidOut) { initialize(); bool validFlag = false; Enum e = UNKNOWN; for (std::vector::iterator iter = enumData.begin(); iter != enumData.end(); iter++) { const SurfaceTypeEnum& d = *iter; if (d.name == s) { e = d.e; validFlag = true; break; } } if (isValidOut != 0) { *isValidOut = validFlag; } return e; } /** * Get a GUI string representation of the enumerated type. * @param e * Enumerated value. * @return * String representing enumerated value. */ AString SurfaceTypeEnum::toGuiName(Enum e) { initialize(); const SurfaceTypeEnum* st = findData(e); return st->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. */ SurfaceTypeEnum::Enum SurfaceTypeEnum::fromGuiName(const AString& s, bool* isValidOut) { initialize(); bool validFlag = false; Enum e = UNKNOWN; for (std::vector::iterator iter = enumData.begin(); iter != enumData.end(); iter++) { const SurfaceTypeEnum& d = *iter; if (d.guiName == s) { e = d.e; validFlag = true; break; } } if (isValidOut != 0) { *isValidOut = validFlag; } return e; } /** * Get the integer code for a data type. * * @return * Integer code for data type. */ int32_t SurfaceTypeEnum::toIntegerCode(Enum e) { initialize(); const SurfaceTypeEnum* ndt = findData(e); return ndt->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. */ SurfaceTypeEnum::Enum SurfaceTypeEnum::fromIntegerCode(const int32_t integerCode, bool* isValidOut) { initialize(); bool validFlag = false; Enum e = UNKNOWN; for (std::vector::iterator iter = enumData.begin(); iter != enumData.end(); iter++) { const SurfaceTypeEnum& ndt = *iter; if (ndt.integerCode == integerCode) { e = ndt.e; validFlag = true; break; } } if (isValidOut != 0) { *isValidOut = validFlag; } return e; } /** * Get a GIFTI Name string representation of the enumerated type. * @param e * Enumerated value. * @return * String representing enumerated value. */ AString SurfaceTypeEnum::toGiftiName(Enum e) { initialize(); const SurfaceTypeEnum* st = findData(e); return st->giftiName; } /** * Get an enumerated value corresponding to its GIFTI 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. */ SurfaceTypeEnum::Enum SurfaceTypeEnum::fromGiftiName(const AString& s, bool* isValidOut) { initialize(); bool validFlag = false; Enum e = UNKNOWN; for (std::vector::iterator iter = enumData.begin(); iter != enumData.end(); iter++) { const SurfaceTypeEnum& d = *iter; if (d.giftiName == s) { e = d.e; validFlag = true; break; } } if (isValidOut != 0) { *isValidOut = validFlag; } return e; } /** * 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 SurfaceTypeEnum::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->e); } } /** * Get all of the enumerated type values EXCEPT FLAT. The values can be used * as parameters to toXXX() methods to get associated metadata. * * @param enumsOut * A vector that is OUTPUT containing all of the enumerated values EXCEPT FLAT */ void SurfaceTypeEnum::getAllEnumsExceptFlat(std::vector& enumsOut) { std::vector allEnums; SurfaceTypeEnum::getAllEnums(allEnums); enumsOut.clear(); for (std::vector::iterator iter = allEnums.begin(); iter != allEnums.end(); iter++) { const SurfaceTypeEnum::Enum st = *iter; if (st != FLAT) { enumsOut.push_back(st); } } } /** * Get the enumerated type values that are three-dimensional in shape * but still have an anatomical-shaped appearance. * * @param anatomicalEnumsOut * A vector that is OUTPUT containing the anatomically shaped enums. */ void SurfaceTypeEnum::getAllAnatomicallyShapedEnums(std::vector& anatomicalEnumsOut) { anatomicalEnumsOut.clear(); anatomicalEnumsOut.push_back(ANATOMICAL); anatomicalEnumsOut.push_back(INFLATED); anatomicalEnumsOut.push_back(VERY_INFLATED); anatomicalEnumsOut.push_back(RECONSTRUCTION); } /** * Constructor. * * @param e * An enumerated value. * @param name * Name of enumberated value. */ SecondarySurfaceTypeEnum::SecondarySurfaceTypeEnum(const Enum e, const AString& name, const AString& guiName, const AString& giftiName) { this->e = e; this->integerCode = SecondarySurfaceTypeEnum::integerCodeGenerator++; this->name = name; this->guiName = guiName; this->giftiName = giftiName; } /** * Initialize the enumerated metadata. */ void SecondarySurfaceTypeEnum::initialize() { if (initializedFlag) { return; } initializedFlag = true; enumData.push_back(SecondarySurfaceTypeEnum(INVALID, "INVALID", "Invalid", "Invalid")); enumData.push_back(SecondarySurfaceTypeEnum(GRAY_WHITE, "GRAY_WHITE", "GrayWhite", "GrayWhite")); enumData.push_back(SecondarySurfaceTypeEnum(MIDTHICKNESS, "MIDTHICKNESS", "Midthickness",//I'm assuming that middle capital T is not desired in the gui "MidThickness"));//but it is used in the data format for caret6 enumData.push_back(SecondarySurfaceTypeEnum(PIAL, "PIAL", "Pial", "Pial")); } /** * Find the data for and enumerated value. * @param e * The enumerated value. * @return Pointer to data for this enumerated type * or NULL if no data for type or if type is invalid. */ const SecondarySurfaceTypeEnum* SecondarySurfaceTypeEnum::findData(const Enum e) { initialize(); size_t num = enumData.size(); for (size_t i = 0; i < num; i++) { const SecondarySurfaceTypeEnum* d = &enumData[i]; if (d->e == e) { return d; } } return NULL; } /** * Get a string representation of the enumerated type. * @param e * Enumerated value. * @return * String representing enumerated value. */ AString SecondarySurfaceTypeEnum::toName(Enum e) { initialize(); const SecondarySurfaceTypeEnum* st = findData(e); return st->name; } /** * Get an enumerated value corresponding to its 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. */ SecondarySurfaceTypeEnum::Enum SecondarySurfaceTypeEnum::fromName(const AString& s, bool* isValidOut) { initialize(); bool validFlag = false; Enum e = INVALID; for (std::vector::iterator iter = enumData.begin(); iter != enumData.end(); iter++) { const SecondarySurfaceTypeEnum& d = *iter; if (d.name == s) { e = d.e; validFlag = true; break; } } if (isValidOut != 0) { *isValidOut = validFlag; } return e; } /** * Get a GUI string representation of the enumerated type. * @param e * Enumerated value. * @return * String representing enumerated value. */ AString SecondarySurfaceTypeEnum::toGuiName(Enum e) { initialize(); const SecondarySurfaceTypeEnum* st = findData(e); return st->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. */ SecondarySurfaceTypeEnum::Enum SecondarySurfaceTypeEnum::fromGuiName(const AString& s, bool* isValidOut) { initialize(); bool validFlag = false; Enum e = INVALID; for (std::vector::iterator iter = enumData.begin(); iter != enumData.end(); iter++) { const SecondarySurfaceTypeEnum& d = *iter; if (d.guiName == s) { e = d.e; validFlag = true; break; } } if (isValidOut != 0) { *isValidOut = validFlag; } return e; } /** * Get the integer code for a data type. * * @return * Integer code for data type. */ int32_t SecondarySurfaceTypeEnum::toIntegerCode(Enum e) { initialize(); const SecondarySurfaceTypeEnum* ndt = findData(e); return ndt->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. */ SecondarySurfaceTypeEnum::Enum SecondarySurfaceTypeEnum::fromIntegerCode(const int32_t integerCode, bool* isValidOut) { initialize(); bool validFlag = false; Enum e = INVALID; for (std::vector::iterator iter = enumData.begin(); iter != enumData.end(); iter++) { const SecondarySurfaceTypeEnum& ndt = *iter; if (ndt.integerCode == integerCode) { e = ndt.e; validFlag = true; break; } } if (isValidOut != 0) { *isValidOut = validFlag; } return e; } /** * Get a GIFTI Name string representation of the enumerated type. * @param e * Enumerated value. * @return * String representing enumerated value. */ AString SecondarySurfaceTypeEnum::toGiftiName(Enum e) { initialize(); const SecondarySurfaceTypeEnum* st = findData(e); return st->giftiName; } /** * Get an enumerated value corresponding to its GIFTI 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. */ SecondarySurfaceTypeEnum::Enum SecondarySurfaceTypeEnum::fromGiftiName(const AString& s, bool* isValidOut) { initialize(); bool validFlag = false; Enum e = INVALID; for (std::vector::iterator iter = enumData.begin(); iter != enumData.end(); iter++) { const SecondarySurfaceTypeEnum& d = *iter; if (d.giftiName == s) { e = d.e; validFlag = true; break; } } if (isValidOut != 0) { *isValidOut = validFlag; } return e; } /** * 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 SecondarySurfaceTypeEnum::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->e); } } connectome-workbench-1.2.3+git41-gc4c6c90/src/Files/SurfaceTypeEnum.h000066400000000000000000000106141300200146000250400ustar00rootroot00000000000000#ifndef __SURFACE_TYPE_ENUM__H_ #define __SURFACE_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 { /** * Enumerated type for */ class SurfaceTypeEnum { public: /** * Enumerated values. */ enum Enum { /** UNKNOWN */ UNKNOWN, /** Reconstruction (raw) */ RECONSTRUCTION, /** Anatomical */ ANATOMICAL, /** Inflated */ INFLATED, /** Very Inflated */ VERY_INFLATED, /** Spherical */ SPHERICAL, /** Semi-Spherical (CMW) */ SEMI_SPHERICAL, /** Ellipsoid */ ELLIPSOID, /** Flat */ FLAT, /** Hull */ HULL }; ~SurfaceTypeEnum(); static AString toName(Enum e); static Enum fromName(const AString& s, bool* isValidOut); static AString toGuiName(Enum e); static Enum fromGuiName(const AString& s, bool* isValidOut); static AString toGiftiName(Enum e); static Enum fromGiftiName(const AString& s, bool* isValidOut); static int32_t toIntegerCode(Enum e); static Enum fromIntegerCode(const int32_t integerCode, bool* isValidOut); static void getAllEnums(std::vector& allEnums); static void getAllEnumsExceptFlat(std::vector& enumsOut); static void getAllAnatomicallyShapedEnums(std::vector& threeDimEnums); private: SurfaceTypeEnum(const Enum e, const AString& name, const AString& guiName, const AString& giftiName); static const SurfaceTypeEnum* findData(const Enum e); static std::vector enumData; static void initialize(); static bool initializedFlag; static int32_t integerCodeGenerator; Enum e; int32_t integerCode; AString name; AString guiName; AString giftiName; }; class SecondarySurfaceTypeEnum { public: enum Enum { INVALID, GRAY_WHITE, MIDTHICKNESS, PIAL }; static AString toName(Enum e); static Enum fromName(const AString& s, bool* isValidOut); static AString toGuiName(Enum e); static Enum fromGuiName(const AString& s, bool* isValidOut); static AString toGiftiName(Enum e); static Enum fromGiftiName(const AString& s, bool* isValidOut); static int32_t toIntegerCode(Enum e); static Enum fromIntegerCode(const int32_t integerCode, bool* isValidOut); static void getAllEnums(std::vector& allEnums); private: SecondarySurfaceTypeEnum(const Enum e, const AString& name, const AString& guiName, const AString& giftiName); static const SecondarySurfaceTypeEnum* findData(const Enum e); static std::vector enumData; static void initialize(); static bool initializedFlag; static int32_t integerCodeGenerator; Enum e; int32_t integerCode; AString name; AString guiName; AString giftiName; }; #ifdef __SURFACE_TYPE_ENUM_DECLARE__ std::vector SurfaceTypeEnum::enumData; bool SurfaceTypeEnum::initializedFlag = false; int32_t SurfaceTypeEnum::integerCodeGenerator = 0; std::vector SecondarySurfaceTypeEnum::enumData; bool SecondarySurfaceTypeEnum::initializedFlag = false; int32_t SecondarySurfaceTypeEnum::integerCodeGenerator = 0; #endif // __SURFACE_TYPE_ENUM_DECLARE__ } // namespace #endif //__SURFACE_TYPE_ENUM__H_ connectome-workbench-1.2.3+git41-gc4c6c90/src/Files/TextFile.cxx000066400000000000000000000067431300200146000240700ustar00rootroot00000000000000/*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 "CaretLogger.h" #include "DataFileException.h" #include "TextFile.h" using namespace caret; /** * Constructor. */ TextFile::TextFile() : DataFile() { this->text = ""; } /** * Destructor. */ TextFile::~TextFile() { this->text = ""; } /** * Clear the contents of this file. */ void TextFile::clear() { DataFile::clear(); this->text = ""; } /** * Is this file empty? * * @return true if file is empty, else false. */ bool TextFile::isEmpty() const { return this->text.isEmpty(); } /** * Read the file. * * @param filename * Name of file to read. * * @throws DataFileException * If there is an error reading the file. */ void TextFile::readFile(const AString& filename) { clear(); checkFileReadability(filename); QFile file(filename); if (file.open(QFile::ReadOnly) == false) { throw DataFileException(filename, "Unable to open for reading."); } QTextStream textStream(&file); this->text = textStream.readAll(); file.close(); this->setFileName(filename); this->clearModified(); } /** * Write the file. * * @param filename * Name of file to read. * * @throws DataFileException * If there is an error writing the file. */ void TextFile::writeFile(const AString& filename) { checkFileWritability(filename); QFile file(filename); if (file.open(QFile::WriteOnly) == false) { throw DataFileException(filename, "Unable to open for writing."); } QTextStream textStream(&file); textStream << this->text; file.close(); this->setFileName(filename); this->clearModified(); } /** * Get information about this file's contents. * @return * Information about the file's contents. */ AString TextFile::toString() const { return "TextFile"; } /** * Get the file's text. * * @return The file's text. */ AString TextFile::getText() const { return this->text; } /** * Replace the file's text. * * @param text * Replaces text in the file. */ void TextFile::replaceText(const AString& text) { if (text != this->text) { this->text = text; this->setModified(); } } /** * Add to the file's text. * * @param text * Text added. */ void TextFile::addText(const AString& text) { this->text += text; this->setModified(); } /** * Add to the file's text and then add * a newline character. * * @param text * Text added. */ void TextFile::addLine(const AString& text) { this->text += text; this->text += "\n"; this->setModified(); } connectome-workbench-1.2.3+git41-gc4c6c90/src/Files/TextFile.h000066400000000000000000000034211300200146000235030ustar00rootroot00000000000000#ifndef __TEXT_FILE_H__ #define __TEXT_FILE_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 "DataFile.h" namespace caret { /** * A simple text file. */ class TextFile : public DataFile { public: TextFile(); virtual ~TextFile(); private: TextFile(const TextFile&); TextFile& operator=(const TextFile&); public: virtual void clear(); virtual bool isEmpty() const; virtual void readFile(const AString& filename); virtual void writeFile(const AString& filename); virtual AString toString() const; AString getText() const; void replaceText(const AString& text); void addText(const AString& text); void addLine(const AString& text); private: AString text; }; } // namespace #endif // __TEXT_FILE_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Files/TopologyHelper.cxx000066400000000000000000000361671300200146000253230ustar00rootroot00000000000000/*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 "SurfaceFile.h" #include "TopologyHelper.h" #include "CaretAssert.h" #include using namespace caret; using namespace std; TopologyHelperBase::TopologyHelperBase(const SurfaceFile* surfIn, bool sortFlag) { m_numNodes = surfIn->getNumberOfNodes(); m_numTris = surfIn->getNumberOfTriangles(); m_nodeInfo.resize(m_numNodes); m_boundaryCount.resize(m_numNodes); m_tileInfo.resize(m_numTris); vector tempEdgeInfo; tempEdgeInfo.reserve(m_numTris * 3);//worst case, to prevent reallocs, we will copy it over later to the exact right size for (int32_t i = 0; i < m_numNodes; ++i) {//preallocate what should be enough size for most nodes on most surfaces, including freesurfer m_nodeInfo[i].m_edges.reserve(8); m_nodeInfo[i].m_neighbors.reserve(8); m_nodeInfo[i].m_tiles.reserve(8); m_nodeInfo[i].m_whichVertex.reserve(8); } for (int32_t i = 0; i < m_numTris; ++i) { const int32_t* thisTri = surfIn->getTriangle(i); m_nodeInfo[thisTri[0]].addTileInfo(i, 0); m_nodeInfo[thisTri[1]].addTileInfo(i, 1); m_nodeInfo[thisTri[2]].addTileInfo(i, 2); }//node tiles complete, now we can sweep over nodes instead of triangles, making it easier to build node info m_maxNeigh = -1; m_maxTiles = -1; CaretArray scratch(m_numNodes, -1);//mark array for added neighbors for (int32_t i = 0; i < m_numNodes; ++i) { vector& tileList = m_nodeInfo[i].m_tiles; vector& vertexList = m_nodeInfo[i].m_whichVertex; int neighTiles = (int)tileList.size(); if (neighTiles > m_maxTiles) { m_maxTiles = neighTiles; } for (int j = 0; j < neighTiles; ++j) { int32_t myTile = tileList[j]; const int32_t* thisTri = surfIn->getTriangle(myTile); int32_t myVert = vertexList[j]; switch (myVert) { case 0: if (thisTri[1] > i) processTileNeighbor(tempEdgeInfo, scratch, i, thisTri[1], thisTri[2], myTile, 0, false);//boolean signifies if root, neighbor is same ordering as the cycle of tile nodes if (thisTri[2] > i) processTileNeighbor(tempEdgeInfo, scratch, i, thisTri[2], thisTri[1], myTile, 2, true); break;//the if statement is a trick: processTileNeighbor adds neighbor to both nodes, so by checking that root is less, it does every edge exactly once case 1://this allows edge info building in a linear pass if (thisTri[2] > i) processTileNeighbor(tempEdgeInfo, scratch, i, thisTri[2], thisTri[0], myTile, 1, false); if (thisTri[0] > i) processTileNeighbor(tempEdgeInfo, scratch, i, thisTri[0], thisTri[2], myTile, 0, true); break; case 2: if (thisTri[0] > i) processTileNeighbor(tempEdgeInfo, scratch, i, thisTri[0], thisTri[1], myTile, 2, false); if (thisTri[1] > i) processTileNeighbor(tempEdgeInfo, scratch, i, thisTri[1], thisTri[0], myTile, 1, true); } } vector& myNeighList = m_nodeInfo[i].m_neighbors; vector& myEdgeList = m_nodeInfo[i].m_edges; int numNeigh = (int)myNeighList.size(); if (numNeigh > m_maxNeigh) { m_maxNeigh = numNeigh; } m_boundaryCount[i] = 0; for (int j = 0; j < numNeigh; ++j) { if (tempEdgeInfo[myEdgeList[j]].numTiles == 1) ++m_boundaryCount[i]; scratch[myNeighList[j]] = -1;//NOTE: -1 as sentinel because 0 is a valid edge number } }//neighbor, edge and tile info done m_edgeInfo = tempEdgeInfo;//copy edge info into member to get allocation correct CaretArray scratch2(m_numTris, -1); if (sortFlag) { for (int32_t i = 0; i < m_numNodes; ++i) { sortNeighbors(surfIn, i, scratch, scratch2);//not a member function of node info object because I need m_edgeInfo and m_nodeInfo } m_neighborsSorted = true; } else { m_neighborsSorted = false; } } //1) check mark array // a) if marked, find edge, add triangle to edge // b) if unmarked, make edge from triangle, add neighbor, add reverse neighbor void TopologyHelperBase::processTileNeighbor(vector& tempEdgeInfo, CaretArray& scratch, const int32_t& root, const int32_t& neighbor, const int32_t& thirdNode, const int32_t& tile, const int32_t& tileEdge, const bool& reversed) { if (scratch[neighbor] == -1) { TopologyEdgeInfo tempInfo(root, neighbor, thirdNode, tile, tileEdge, reversed); int32_t myEdge = (int32_t)tempEdgeInfo.size(); tempEdgeInfo.push_back(tempInfo); m_nodeInfo[root].addNeighborInfo(neighbor, myEdge); m_nodeInfo[neighbor].addNeighborInfo(root, myEdge); scratch[neighbor] = myEdge;//use mark array both as "have this neighbor" AND "this is this neighbor's edge" m_tileInfo[tile].edges[tileEdge].edge = myEdge; } else { tempEdgeInfo[scratch[neighbor]].addTile(thirdNode, tile, tileEdge, reversed); m_tileInfo[tile].edges[tileEdge].edge = scratch[neighbor]; } m_tileInfo[tile].edges[tileEdge].reversed = reversed; } void TopologyHelperBase::sortNeighbors(const SurfaceFile* mySurf, const int32_t& node, CaretArray& nodeScratch, CaretArray& tileScratch) { TopologyHelperBase::NodeInfo& myNodeInfo = m_nodeInfo[node]; if (myNodeInfo.m_neighbors.size() == 0) return; int firstIndex = 0, numNeigh = (int)myNodeInfo.m_neighbors.size(); for (int i = 0; i < numNeigh; ++i) { int32_t thisEdge = myNodeInfo.m_edges[i]; if (m_edgeInfo[thisEdge].numTiles == 1)//there cannot be edge info with zero tiles, we are looking for the edge of a cut { firstIndex = i; if ((m_edgeInfo[thisEdge].node1 == node) != m_edgeInfo[thisEdge].tiles[0].edgeReversed)//this checks if the edge from center to neighbor is oriented with the tile {//since edge info is always node1 < node2, we have to xor (this center is node1) and (this edge is reversed compared to its only triangle) break;//found one }//the reason the break is in the additional if, is so that if we don't find a correctly oriented edge, we still find an edge if one exists } } vector tempNeigh; vector tempEdges, tempTiles;//why not sort everything? verts get regenerated in place int numTiles = myNodeInfo.m_tiles.size(); tempNeigh.reserve(numNeigh); tempEdges.reserve(numNeigh); tempTiles.reserve(numTiles); int32_t nextNode = myNodeInfo.m_neighbors[firstIndex]; int32_t nextEdge = myNodeInfo.m_edges[firstIndex]; int32_t nextTile; bool foundNext = true; int tileToUse = 0; if (m_edgeInfo[nextEdge].numTiles > 1) { if (m_edgeInfo[nextEdge].tiles[0].edgeReversed != (m_edgeInfo[nextEdge].node1 == nextNode)) { tileToUse = 1; } } do { nextTile = m_edgeInfo[nextEdge].tiles[tileToUse].tile; int32_t node3 = m_edgeInfo[nextEdge].tiles[tileToUse].node3; nextEdge = -1; for (int i = 0; i < 3; ++i) { if (m_edgeInfo[m_tileInfo[nextTile].edges[i].edge].node1 == node) { if (m_edgeInfo[m_tileInfo[nextTile].edges[i].edge].node2 == node3) { nextEdge = m_tileInfo[nextTile].edges[i].edge; break; } } else { if (m_edgeInfo[m_tileInfo[nextTile].edges[i].edge].node1 == node3 && m_edgeInfo[m_tileInfo[nextTile].edges[i].edge].node2 == node) { nextEdge = m_tileInfo[nextTile].edges[i].edge; break; } } } CaretAssert(nextEdge != -1); tempNeigh.push_back(nextNode); tempEdges.push_back(nextEdge); nodeScratch[nextNode] = 0;//remember, -1 is "unused" tempTiles.push_back(nextTile); nextNode = node3;//update the next node we are going to use tileScratch[nextTile] = 0; tileToUse = 0; if (tileScratch[m_edgeInfo[nextEdge].tiles[0].tile] == 0 && m_edgeInfo[nextEdge].numTiles > 1) { tileToUse = 1; } if (tileScratch[m_edgeInfo[nextEdge].tiles[tileToUse].tile] == 0) { foundNext = false; } } while (foundNext); for (int i = 0; i < numNeigh; ++i)//clean up scratch array, find any neighbors that are gap-separated or on third+ tile of an edge { if (nodeScratch[myNodeInfo.m_neighbors[i]] == 0) { nodeScratch[myNodeInfo.m_neighbors[i]] = -1; } else { tempNeigh.push_back(myNodeInfo.m_neighbors[i]); tempEdges.push_back(myNodeInfo.m_edges[i]); } } CaretAssert(tempNeigh.size() == myNodeInfo.m_neighbors.size());//check against original size CaretAssert(tempEdges.size() == myNodeInfo.m_edges.size()); myNodeInfo.m_neighbors = tempNeigh;//copy over myNodeInfo.m_edges = tempEdges; for (int i = 0; i < numTiles; ++i)//and find similar tiles { if (tileScratch[myNodeInfo.m_tiles[i]] == 0) { tileScratch[myNodeInfo.m_tiles[i]] = -1; } else { tempTiles.push_back(myNodeInfo.m_tiles[i]); } } CaretAssert(tempTiles.size() == myNodeInfo.m_tiles.size()); myNodeInfo.m_tiles = tempTiles; for (int i = 0; i < numTiles; ++i)//finally, regenerate verts { const int32_t* myTri = mySurf->getTriangle(myNodeInfo.m_tiles[i]); if (myTri[0] == node) { myNodeInfo.m_whichVertex[i] = 0; } else if (myTri[1] == node) { myNodeInfo.m_whichVertex[i] = 1; } else { myNodeInfo.m_whichVertex[i] = 2; } } } TopologyHelper::TopologyHelper(CaretPointer myBase) : m_base(myBase), m_nodeInfo(myBase->m_nodeInfo), m_edgeInfo(myBase->m_edgeInfo), m_tileInfo(myBase->m_tileInfo), m_boundaryCount(myBase->m_boundaryCount) {//pointer is by-value so that it makes a private copy that can't be pointed elsewhere during this constructor m_maxNeigh = m_base->m_maxNeigh; m_neighborsSorted = m_base->m_neighborsSorted; m_numNodes = m_base->m_numNodes; } const vector& TopologyHelper::getNumberOfBoundaryEdgesForAllNodes() const { return m_boundaryCount; } int32_t TopologyHelper::getMaximumNumberOfNeighbors() const { return m_maxNeigh; } bool TopologyHelper::getNodeHasNeighbors(const int32_t nodeNum) const { CaretAssertVectorIndex(m_nodeInfo, nodeNum); return m_nodeInfo[nodeNum].m_neighbors.size() != 0; } const vector& TopologyHelper::getNodeNeighbors(const int32_t nodeNum) const { CaretAssertVectorIndex(m_nodeInfo, nodeNum); return m_nodeInfo[nodeNum].m_neighbors; } const int32_t* TopologyHelper::getNodeNeighbors(const int32_t nodeNum, int32_t& numNeighborsOut) const { CaretAssertVectorIndex(m_nodeInfo, nodeNum); numNeighborsOut = (int32_t)m_nodeInfo[nodeNum].m_neighbors.size(); return m_nodeInfo[nodeNum].m_neighbors.data(); } int32_t TopologyHelper::getNodeNumberOfNeighbors(const int32_t nodeNum) const { CaretAssertVectorIndex(m_nodeInfo, nodeNum); return m_nodeInfo[nodeNum].m_neighbors.size(); } const vector& TopologyHelper::getNodeTiles(const int32_t nodeNum) const { CaretAssertVectorIndex(m_nodeInfo, nodeNum); return m_nodeInfo[nodeNum].m_tiles; } const int32_t* TopologyHelper::getNodeTiles(const int32_t nodeNum, int32_t& numTilesOut) const { CaretAssertVectorIndex(m_nodeInfo, nodeNum); numTilesOut = (int32_t)m_nodeInfo[nodeNum].m_tiles.size(); return m_nodeInfo[nodeNum].m_tiles.data(); } const vector& TopologyHelper::getNodeEdges(const int32_t nodeNum) const { CaretAssertVectorIndex(m_nodeInfo, nodeNum); return m_nodeInfo[nodeNum].m_edges; } void TopologyHelper::checkArrays() const { if (m_markNodes.size() != m_numNodes) { m_markNodes = CaretArray(m_numNodes, 0); m_nodelist[0] = CaretArray(m_numNodes); m_nodelist[1] = CaretArray(m_numNodes); } } void TopologyHelper::getNodeNeighborsToDepth(const int32_t nodeNum, const int32_t depth, vector& neighborsOut) const { if (depth < 2) { neighborsOut = getNodeNeighbors(nodeNum); return; } int32_t expected = (7 * depth * (depth + 1)) / 2; if (expected > m_numNodes / 2) expected = m_numNodes / 2; neighborsOut.clear(); neighborsOut.reserve(expected); CaretArray* curlist = &(m_nodelist[0]), *nextlist = &(m_nodelist[1]), *templist;//using raw pointers instead of CaretArray::operator= because this is single threaded int32_t curNum = 1, nextNum = 0;//curnum gets initialized to 1 because it starts with the root node CaretMutexLocker locked(&m_usingMarkNodes);//lock before possibly constructing this object's scratch space checkArrays(); m_markNodes[nodeNum] = 1; (*curlist)[0] = nodeNum;//just use iterative, because depth-first recursive does unneeded work, and has very little reason to ever be faster for (int32_t curdepth = 0; curdepth < depth; ++curdepth) { for (int32_t i = 0; i < curNum; ++i) { const vector& nodeNeighbors = m_nodeInfo[(*curlist)[i]].m_neighbors; int numNeigh = (int)nodeNeighbors.size(); for (int j = 0; j < numNeigh; ++j) { int32_t thisNode = nodeNeighbors[j]; if (m_markNodes[thisNode] == 0) { m_markNodes[thisNode] = 1; (*nextlist)[nextNum] = thisNode; ++nextNum; neighborsOut.push_back(thisNode); } } } templist = curlist; curlist = nextlist; nextlist = templist; curNum = nextNum; nextNum = 0;//we restart the list by zeroing the count, and just overwrite the old values } m_markNodes[nodeNum] = 0;//clean up the mark array int32_t numNeigh = (int32_t)neighborsOut.size(); for (int32_t i = 0; i < numNeigh; ++i) { m_markNodes[neighborsOut[i]] = 0; } } connectome-workbench-1.2.3+git41-gc4c6c90/src/Files/TopologyHelper.h000066400000000000000000000201641300200146000247360ustar00rootroot00000000000000#ifndef __TOPOLOGY_HELPER_H__ #define __TOPOLOGY_HELPER_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 "CaretPointer.h" namespace caret { class SurfaceFile; struct TopologyEdgeInfo { struct Tile { int32_t tile; int32_t node3; int32_t whichEdge;//whether this is edge 0 (0-1), 1 (1-2), or 2 (2-0) bool edgeReversed;//whether ordering the nodes as 1, 2, 3 results in a flipped tile compared to topology }; int32_t node1, node2; int32_t numTiles; Tile tiles[2];//should be so amazingly rare (and inherently bad) for an edge to have 3 triangles that it isn't worth making this a vector TopologyEdgeInfo()//also, a vector would have poor data locality, each edge would have its tiles list in an unrelated spot to the previous edge { numTiles = 0; } TopologyEdgeInfo(const int32_t& firstNode, const int32_t& secondNode, const int32_t& thirdNode, const int32_t& tile, const int32_t& whichEdge, const bool& reversed) { node1 = firstNode;//always called with firstNode less than secondNode, so don't need to swap node2 = secondNode; tiles[0].tile = tile; tiles[0].node3 = thirdNode; tiles[0].edgeReversed = reversed; tiles[0].whichEdge = whichEdge; numTiles = 1; } void addTile(const int32_t& thirdNode, const int32_t& tile, const int32_t& whichEdge, const bool& reversed) { if (numTiles < 2) { tiles[numTiles].tile = tile; tiles[numTiles].node3 = thirdNode; tiles[numTiles].edgeReversed = reversed; tiles[numTiles].whichEdge = whichEdge; } ++numTiles; } }; struct TopologyTileInfo { struct Edge { int32_t edge; bool reversed; }; Edge edges[3]; }; class TopologyHelperBase { TopologyHelperBase();//prevent default, copy, assign TopologyHelperBase(const TopologyHelperBase&); TopologyHelperBase& operator=(const TopologyHelperBase&); void processTileNeighbor(std::vector& tempEdgeInfo, CaretArray& scratch, const int32_t& root, const int32_t& neighbor, const int32_t& thirdNode, const int32_t& tile, const int32_t& tileEdge, const bool& reversed); void sortNeighbors(const SurfaceFile* mySurf, const int32_t& node, CaretArray& nodeScratch, CaretArray& tileScratch); struct NodeInfo { std::vector m_neighbors; std::vector m_edges;//index into the topology edges vector, matched with neighbors std::vector m_tiles; std::vector m_whichVertex;//stores which tile vertex this node is, matched to m_tiles void addTileInfo(int32_t tile, int32_t vertexNum)//don't take edge info yet because it is built after neighbor info { m_tiles.push_back(tile); m_whichVertex.push_back(vertexNum); } void addNeighborInfo(int32_t neighbor, int32_t edge)//after we have tile info, we then generate neighbor info with the help of a mark array { m_neighbors.push_back(neighbor); m_edges.push_back(edge); } }; std::vector m_nodeInfo; std::vector m_edgeInfo; std::vector m_tileInfo; std::vector m_boundaryCount; int32_t m_maxNeigh, m_maxTiles, m_numNodes, m_numTris; bool m_neighborsSorted; public: TopologyHelperBase(const SurfaceFile* surfIn, bool sortNeighbors = false); bool isNodeInfoSorted() const { return m_neighborsSorted; } friend class TopologyHelper; }; /// This class is used to determine the node neighbors and edges for a Topology File. class TopologyHelper { CaretPointer m_base; mutable CaretArray m_markNodes, m_nodelist[2];//persistent, never cleared, only initialized once, saving bazillions of nanoseconds mutable CaretMutex m_usingMarkNodes; bool m_neighborsSorted; int32_t m_numNodes, m_maxNeigh; const std::vector& m_nodeInfo;//references for convenience instead of using the m_base pointer const std::vector& m_edgeInfo; const std::vector& m_tileInfo; const std::vector& m_boundaryCount; void checkArrays() const;//used to make the thread arrays for neighbors to depth lazy (not allocated until first needed) TopologyHelper();//prevent default, copy, assign, prolly not needed since there are reference members TopologyHelper(const TopologyHelper& right); TopologyHelper& operator=(const TopologyHelper& right); public: /// Constructor for use with a TopologyHelperBase (the only way, for now) TopologyHelper(CaretPointer myBase); /// Get the number of nodes int32_t getNumberOfNodes() const { return m_numNodes; } /// See if a node has neighbors bool getNodeHasNeighbors(const int32_t nodeNum) const; /// Get the number of neighbors for a node int32_t getNodeNumberOfNeighbors(const int32_t nodeNum) const; /// Get the neighbors of a node const std::vector& getNodeNeighbors(const int32_t nodeNum) const; /// Get the neighboring nodes for a node. Returns a pointer to an array /// containing the neighbors. const int32_t* getNodeNeighbors(const int32_t nodeNum, int32_t& numNeighborsOut) const; ///get the edges of a node const std::vector& getNodeEdges(const int32_t nodeNum) const; /// Get the neighbors to a specified depth void getNodeNeighborsToDepth(const int32_t nodeNum, const int32_t depth, std::vector& neighborsOut) const; /// Get the number of boundary edges used by node const std::vector& getNumberOfBoundaryEdgesForAllNodes() const; /// Get the maximum number of neighbors of all nodes int32_t getMaximumNumberOfNeighbors() const; /// Get the tiles used by a node const std::vector& getNodeTiles(const int32_t nodeNum) const; /// Get the tiles for a node. Returns a pointer to an array /// containing the tiles. const int32_t* getNodeTiles(const int32_t nodeNum, int32_t& numTilesOut) const; /// get node sorted info validity bool isNodeInfoSorted() const { return m_neighborsSorted; } /// Get the edge information const std::vector& getEdgeInfo() const { return m_edgeInfo; } /// Get the tile information const std::vector& getTileInfo() const { return m_tileInfo; } /// Get the number of edges int32_t getNumberOfEdges() const { return m_edgeInfo.size(); } }; } #endif //__TOPOLOGY_HELPER_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Files/VolumeEditingModeEnum.cxx000066400000000000000000000355771300200146000265600ustar00rootroot00000000000000 /*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 __VOLUME_EDITING_MODE_DECLARE__ #include "VolumeEditingModeEnum.h" #undef __VOLUME_EDITING_MODE_DECLARE__ #include "CaretAssert.h" using namespace caret; /** * \class caret::VolumeEditingModeEnum * \brief Enumerated type for volume editing. * * * * 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_volumeEditingModeEnumComboBox; * * Declare a slot that is called when user changes selection * private slots: * void volumeEditingModeEnumComboBoxItemActivated(); * * Implementation File (.cxx) * Include the header files * #include "EnumComboBoxTemplate.h" * #include "VolumeEditingModeEnum.h" * * Instatiate: * m_volumeEditingModeEnumComboBox = new EnumComboBoxTemplate(this); * m_volumeEditingModeEnumComboBox->setup(); * * Get notified when the user changes the selection: * QObject::connect(m_volumeEditingModeEnumComboBox, SIGNAL(itemActivated()), * this, SLOT(volumeEditingModeEnumComboBoxItemActivated())); * * Update the selection: * m_volumeEditingModeEnumComboBox->setSelectedItem(NEW_VALUE); * * Read the selection: * const VolumeEditingModeEnum::Enum VARIABLE = m_volumeEditingModeEnumComboBox->getSelectedItem(); * */ /** * Constructor. * * @param enumValue * An enumerated value. * @param name * Name of enumerated value. * @param guiName * User-friendly name for use in user-interface. * @param toolTipText * Text for the tooltip. */ VolumeEditingModeEnum::VolumeEditingModeEnum(const Enum enumValue, const AString& name, const AString& guiName, const AString& toolTipText) { this->enumValue = enumValue; this->integerCode = integerCodeCounter++; this->name = name; this->guiName = guiName; this->toolTipText = toolTipText; } /** * Destructor. */ VolumeEditingModeEnum::~VolumeEditingModeEnum() { } /** * Initialize the enumerated metadata. */ void VolumeEditingModeEnum::initialize() { if (initializedFlag) { return; } initializedFlag = true; enumData.push_back(VolumeEditingModeEnum(VOLUME_EDITING_MODE_ON, "VOLUME_EDITING_MODE_ON", "On", "Turn voxels on at mouse click. Or move the mouse with the left " "mouse button down while holding down the CTRL (Apple key on Mac) " "and SHIFT keys.")); enumData.push_back(VolumeEditingModeEnum(VOLUME_EDITING_MODE_OFF, "VOLUME_EDITING_MODE_OFF", "Off", "Turn voxels off at mouse click. Or move the mouse with the left " "mouse button down while holding down the CTRL (Apple key on Mac) " "and SHIFT keys.")); enumData.push_back(VolumeEditingModeEnum(VOLUME_EDITING_MODE_DILATE, "VOLUME_EDITING_MODE_DILATE", "Dilate", "Dilate voxels at mouse click")); enumData.push_back(VolumeEditingModeEnum(VOLUME_EDITING_MODE_ERODE, "VOLUME_EDITING_MODE_ERODE", "Erode", "Erode voxels at mouse click")); enumData.push_back(VolumeEditingModeEnum(VOLUME_EDITING_MODE_FLOOD_FILL_2D, "VOLUME_EDITING_MODE_FLOOD_FILL_2D", "Fill 2D", "Fill closed region (2D) at mouse click")); enumData.push_back(VolumeEditingModeEnum(VOLUME_EDITING_MODE_FLOOD_FILL_3D, "VOLUME_EDITING_MODE_FLOOD_FILL_3D", "Fill 3D", "Fill closed region (3D) at mouse click")); enumData.push_back(VolumeEditingModeEnum(VOLUME_EDITING_MODE_REMOVE_CONNECTED_2D, "VOLUME_EDITING_MODE_REMOVE_CONNECTED_2D", "Remove 2D", "Remove connected region (2D) at mouse click")); enumData.push_back(VolumeEditingModeEnum(VOLUME_EDITING_MODE_REMOVE_CONNECTED_3D, "VOLUME_EDITING_MODE_REMOVE_CONNECTED_3D", "Remove 3D", "Remove connected region (3D) at mouse click")); enumData.push_back(VolumeEditingModeEnum(VOLUME_EDITING_MODE_RETAIN_CONNECTED_3D, "VOLUME_EDITING_MODE_RETAIN_CONNECTED_3D", "Retain 3D", "Remove voxel not connected to region (3D) at mouse click")); } /** * 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 VolumeEditingModeEnum* VolumeEditingModeEnum::findData(const Enum enumValue) { if (initializedFlag == false) initialize(); size_t num = enumData.size(); for (size_t i = 0; i < num; i++) { const VolumeEditingModeEnum* d = &enumData[i]; if (d->enumValue == enumValue) { return d; } } return NULL; } /** * Get a tool tip string of the enumerated type. * @param enumValue * Enumerated value. * @return * String representing tooltip. */ AString VolumeEditingModeEnum::toToolTip(Enum enumValue) { if (initializedFlag == false) initialize(); const VolumeEditingModeEnum* enumInstance = findData(enumValue); return enumInstance->toolTipText; } /** * Get a string representation of the enumerated type. * @param enumValue * Enumerated value. * @return * String representing enumerated value. */ AString VolumeEditingModeEnum::toName(Enum enumValue) { if (initializedFlag == false) initialize(); const VolumeEditingModeEnum* 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. */ VolumeEditingModeEnum::Enum VolumeEditingModeEnum::fromName(const AString& name, bool* isValidOut) { if (initializedFlag == false) initialize(); bool validFlag = false; Enum enumValue = VolumeEditingModeEnum::enumData[0].enumValue; for (std::vector::iterator iter = enumData.begin(); iter != enumData.end(); iter++) { const VolumeEditingModeEnum& 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 VolumeEditingModeEnum")); } return enumValue; } /** * Get a GUI string representation of the enumerated type. * @param enumValue * Enumerated value. * @return * String representing enumerated value. */ AString VolumeEditingModeEnum::toGuiName(Enum enumValue) { if (initializedFlag == false) initialize(); const VolumeEditingModeEnum* 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. */ VolumeEditingModeEnum::Enum VolumeEditingModeEnum::fromGuiName(const AString& guiName, bool* isValidOut) { if (initializedFlag == false) initialize(); bool validFlag = false; Enum enumValue = VolumeEditingModeEnum::enumData[0].enumValue; for (std::vector::iterator iter = enumData.begin(); iter != enumData.end(); iter++) { const VolumeEditingModeEnum& 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 VolumeEditingModeEnum")); } return enumValue; } /** * Get the integer code for a data type. * * @return * Integer code for data type. */ int32_t VolumeEditingModeEnum::toIntegerCode(Enum enumValue) { if (initializedFlag == false) initialize(); const VolumeEditingModeEnum* 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. */ VolumeEditingModeEnum::Enum VolumeEditingModeEnum::fromIntegerCode(const int32_t integerCode, bool* isValidOut) { if (initializedFlag == false) initialize(); bool validFlag = false; Enum enumValue = VolumeEditingModeEnum::enumData[0].enumValue; for (std::vector::iterator iter = enumData.begin(); iter != enumData.end(); iter++) { const VolumeEditingModeEnum& 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 VolumeEditingModeEnum")); } 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 VolumeEditingModeEnum::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 VolumeEditingModeEnum::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(VolumeEditingModeEnum::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 VolumeEditingModeEnum::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(VolumeEditingModeEnum::toGuiName(iter->enumValue)); } if (isSorted) { std::sort(allGuiNames.begin(), allGuiNames.end()); } } /** * Does the enum value allow oblique editing? * * @param enumValue * The editing mode. * @return * True if the enum value allows oblique slice editing, else false. */ bool VolumeEditingModeEnum::isObliqueEditingAllowed(const Enum enumValue) { bool obliqueEditingSupportedFlag = false; switch (enumValue) { case VolumeEditingModeEnum::VOLUME_EDITING_MODE_ON: case VolumeEditingModeEnum::VOLUME_EDITING_MODE_OFF: obliqueEditingSupportedFlag = true; break; case VolumeEditingModeEnum::VOLUME_EDITING_MODE_DILATE: case VolumeEditingModeEnum::VOLUME_EDITING_MODE_ERODE: case VolumeEditingModeEnum::VOLUME_EDITING_MODE_FLOOD_FILL_2D: case VolumeEditingModeEnum::VOLUME_EDITING_MODE_FLOOD_FILL_3D: case VolumeEditingModeEnum::VOLUME_EDITING_MODE_REMOVE_CONNECTED_2D: case VolumeEditingModeEnum::VOLUME_EDITING_MODE_REMOVE_CONNECTED_3D: case VolumeEditingModeEnum::VOLUME_EDITING_MODE_RETAIN_CONNECTED_3D: break; } return obliqueEditingSupportedFlag; } connectome-workbench-1.2.3+git41-gc4c6c90/src/Files/VolumeEditingModeEnum.h000066400000000000000000000075271300200146000261770ustar00rootroot00000000000000#ifndef __VOLUME_EDITING_MODE_ENUM_H__ #define __VOLUME_EDITING_MODE_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 VolumeEditingModeEnum { public: /** * Enumerated values. */ enum Enum { /** Turn voxels on */ VOLUME_EDITING_MODE_ON, /** Turn voxels off */ VOLUME_EDITING_MODE_OFF, /** Dilate */ VOLUME_EDITING_MODE_DILATE, /** Erode */ VOLUME_EDITING_MODE_ERODE, /** Flood Fill 2D */ VOLUME_EDITING_MODE_FLOOD_FILL_2D, /** Flood Fill 3D */ VOLUME_EDITING_MODE_FLOOD_FILL_3D, /** Remove connected voxels */ VOLUME_EDITING_MODE_REMOVE_CONNECTED_2D, /** Remove connected voxels 3D */ VOLUME_EDITING_MODE_REMOVE_CONNECTED_3D, /** Retain connected voxels 3D */ VOLUME_EDITING_MODE_RETAIN_CONNECTED_3D }; ~VolumeEditingModeEnum(); 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 AString toToolTip(Enum enumValue); 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 bool isObliqueEditingAllowed(const Enum enumValue); private: VolumeEditingModeEnum(const Enum enumValue, const AString& name, const AString& guiName, const AString& toolTipText); static const VolumeEditingModeEnum* 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; AString toolTipText; }; #ifdef __VOLUME_EDITING_MODE_DECLARE__ std::vector VolumeEditingModeEnum::enumData; bool VolumeEditingModeEnum::initializedFlag = false; int32_t VolumeEditingModeEnum::integerCodeCounter = 0; #endif // __VOLUME_EDITING_MODE_DECLARE__ } // namespace #endif //__VOLUME_EDITING_MODE_ENUM_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Files/VolumeFile.cxx000066400000000000000000002225451300200146000244130ustar00rootroot00000000000000/*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 "CaretHttpManager.h" #include "CaretLogger.h" #include "CaretTemporaryFile.h" #include "ChartDataCartesian.h" #include "ChartDataSource.h" #include "DataFileContentInformation.h" #include "ElapsedTimer.h" #include "EventManager.h" #include "EventPaletteGetByName.h" #include "GroupAndNameHierarchyModel.h" #include "FastStatistics.h" #include "Histogram.h" #include "MultiDimIterator.h" #include "NiftiIO.h" #include "Palette.h" #include "SceneClass.h" #include "VolumeFile.h" #include "VolumeFileEditorDelegate.h" #include "VolumeFileVoxelColorizer.h" #include "VolumeSpline.h" #include using namespace caret; using namespace std; const float VolumeFile::INVALID_INTERP_VALUE = 0.0f;//we may want NaN or something more obvious bool VolumeFile::s_voxelColoringEnabled = true; /** * Static method that sets the status of voxel coloring. Coloring may take * time and is almost never needed during command line operations (wb_command). * * DO NOT CHANGE THIS VALUE if there are any instance of VolumeFile since * the coloring object is created when a VolumeFile instance is created. * * @param enabled * New status for coloring. */ void VolumeFile::setVoxelColoringEnabled(const bool enabled) { s_voxelColoringEnabled = enabled; CaretLogConfig(AString(s_voxelColoringEnabled ? "Volume coloring is enabled." : "Volume coloring is disabled.")); } VolumeFile::VolumeFile() : VolumeBase(), CaretMappableDataFile(DataFileTypeEnum::VOLUME) { m_fileFastStatistics.grabNew(NULL); m_fileHistogram.grabNew(NULL); m_fileHistorgramLimitedValues.grabNew(NULL); m_forceUpdateOfGroupAndNameHierarchy = true; for (int32_t i = 0; i < BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS; i++) { m_chartingEnabledForTab[i] = false; } m_volumeFileEditorDelegate.grabNew(NULL); validateMembers(); } VolumeFile::VolumeFile(const vector& dimensionsIn, const vector >& indexToSpace, const int64_t numComponents, SubvolumeAttributes::VolumeType whatType) : VolumeBase(dimensionsIn, indexToSpace, numComponents), CaretMappableDataFile(DataFileTypeEnum::VOLUME) { m_fileFastStatistics.grabNew(NULL); m_fileHistogram.grabNew(NULL); m_fileHistorgramLimitedValues.grabNew(NULL); m_forceUpdateOfGroupAndNameHierarchy = true; for (int32_t i = 0; i < BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS; i++) { m_chartingEnabledForTab[i] = false; } m_volumeFileEditorDelegate.grabNew(NULL); validateMembers(); setType(whatType); } void VolumeFile::reinitialize(const vector& dimensionsIn, const vector >& indexToSpace, const int64_t numComponents, SubvolumeAttributes::VolumeType whatType) { clear(); VolumeBase::reinitialize(dimensionsIn, indexToSpace, numComponents); validateMembers(); setType(whatType); } void VolumeFile::reinitialize(const VolumeSpace& volSpaceIn, const int64_t numFrames, const int64_t numComponents, SubvolumeAttributes::VolumeType whatType) { CaretAssert(numFrames > 0); const int64_t* dimsPtr = volSpaceIn.getDims(); vector dims(dimsPtr, dimsPtr + 3); if (numFrames > 1) { dims.push_back(numFrames); } reinitialize(dims, volSpaceIn.getSform(), numComponents, whatType); } void VolumeFile::addSubvolumes(const int64_t& numToAdd) { VolumeBase::addSubvolumes(numToAdd); validateMembers(); } SubvolumeAttributes::VolumeType VolumeFile::getType() const { CaretAssertVectorIndex(m_caretVolExt.m_attributes, 0); CaretAssert(m_caretVolExt.m_attributes[0] != NULL); return m_caretVolExt.m_attributes[0]->m_type; } void VolumeFile::setType(SubvolumeAttributes::VolumeType whatType) { int numAttrs = (int)m_caretVolExt.m_attributes.size(); for (int i = 0; i < numAttrs; ++i) { CaretAssert(m_caretVolExt.m_attributes[i] != NULL); if (m_caretVolExt.m_attributes[i]->m_type != whatType) { m_caretVolExt.m_attributes[i]->m_type = whatType; if (whatType == SubvolumeAttributes::LABEL) { m_caretVolExt.m_attributes[i]->m_palette.grabNew(NULL); m_caretVolExt.m_attributes[i]->m_labelTable.grabNew(new GiftiLabelTable()); } else { m_caretVolExt.m_attributes[i]->m_palette.grabNew(new PaletteColorMapping()); m_caretVolExt.m_attributes[i]->m_labelTable.grabNew(NULL); if (whatType == SubvolumeAttributes::ANATOMY) { m_caretVolExt.m_attributes[i]->m_palette->setSelectedPaletteName(Palette::GRAY_INTERP_POSITIVE_PALETTE_NAME); m_caretVolExt.m_attributes[i]->m_palette->setScaleMode(PaletteScaleModeEnum::MODE_AUTO_SCALE_PERCENTAGE); } } } } } VolumeFile::~VolumeFile() { clear(); } /** * Clear the file. */ void VolumeFile::clear() { CaretMappableDataFile::clear(); m_voxelColorizer.grabNew(NULL); m_classNameHierarchy.grabNew(NULL); m_forceUpdateOfGroupAndNameHierarchy = true; m_fileFastStatistics.grabNew(NULL); m_fileHistogram.grabNew(NULL); m_fileHistorgramLimitedValues.grabNew(NULL); m_caretVolExt.clear(); m_brickAttributes.clear(); m_brickStatisticsValid = false; m_splinesValid = false; m_frameSplineValid.clear(); m_frameSplines.clear(); m_dataRangeValid = false; VolumeBase::clear(); m_volumeFileEditorDelegate->clear(); } void VolumeFile::readFile(const AString& filename) { ElapsedTimer timer; timer.start(); clear(); { /* * CaretTemporaryFile must be outside of the "if" statment block of code. * Otherwise, at the end of the "if" statement block, the * CaretTemporaryFile object will go out of scope and the temporary * file will be deleted so there will be no file to read. */ AString fileToRead; CaretTemporaryFile tempFile; if (DataFile::isFileOnNetwork(filename)) { tempFile.readFile(filename); fileToRead = tempFile.getFileName(); setFileName(filename); } else { setFileName(filename); fileToRead = filename; } checkFileReadability(fileToRead); NiftiIO myIO;//begin nifti specific code - should this go somewhere else? myIO.openRead(fileToRead); const NiftiHeader& inHeader = myIO.getHeader(); int numComponents = myIO.getNumComponents(); vector myDims = myIO.getDimensions(); int fullDims = 3;//deal with nifti with less than 3 dimensions if (myDims.size() < 3) fullDims = (int)myDims.size(); vector extraDims;//non-spatial dims if (myDims.size() > 3) { 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") reinitialize(myDims, inHeader.getSForm(), numComponents); setFileName(filename); // must be donw 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) { vector tempFrame(frameSize), readBuffer(frameSize * numComponents); for (MultiDimIterator myiter(extraDims); !myiter.atEnd(); ++myiter) { myIO.readData(readBuffer.data(), fullDims, *myiter); for (int c = 0; c < numComponents; ++c) { for (int64_t i = 0; i < frameSize; ++i) { tempFrame[i] = readBuffer[i * numComponents + c]; } setFrame(tempFrame.data(), getBrickIndexFromNonSpatialIndexes(*myiter), c); } } } else {//avoid the added allocation for separating components vector tempFrame(frameSize); for (MultiDimIterator myiter(extraDims); !myiter.atEnd(); ++myiter) { myIO.readData(tempFrame.data(), fullDims, *myiter); setFrame(tempFrame.data(), getBrickIndexFromNonSpatialIndexes(*myiter)); } } CaretLogFine("Time to read volume data is " + AString::number(timer.getElapsedTimeSeconds(), 'f', 3) + " seconds."); m_header.grabNew(new NiftiHeader(inHeader));//end nifti-specific code parseExtensions(); clearModified(); } m_volumeFileEditorDelegate->updateIfVolumeFileChangedNumberOfMaps(); /* * This will update the map name/label hierarchy */ if (isMappedWithLabelTable()) { m_forceUpdateOfGroupAndNameHierarchy = true; getGroupAndNameHierarchyModel(); } CaretLogFine("Total Time to read and process volume is " + AString::number(timer.getElapsedTimeSeconds(), 'f', 3) + " seconds."); } /** * Write the data file. * * @param filename * Name of the data file. * @throws DataFileException * If the file was not successfully written. */ void VolumeFile::writeFile(const AString& filename) { if (!(filename.endsWith(".nii.gz") || filename.endsWith(".nii"))) { CaretLogWarning("volume file '" + filename + "' should be saved ending in .nii.gz or .nii, other formats are not supported"); } checkFileWritability(filename); if (getNumberOfComponents() != 1) { throw DataFileException(filename, "writing multi-component volumes is not currently supported");//its a hassle, and uncommon, and there is only one 3-component type, restricted to 0-255 } updateCaretExtension(); NiftiHeader outHeader;//begin nifti-specific code if (m_header != NULL && (m_header->getType() == AbstractHeader::NIFTI)) { outHeader = *((NiftiHeader*)m_header.getPointer());//also shallow copies extensions } outHeader.clearDataScaling(); outHeader.setSForm(getVolumeSpace().getSform()); outHeader.setDimensions(getOriginalDimensions()); outHeader.setDataType(NIFTI_TYPE_FLOAT32); NiftiIO myIO; int outVersion = 1; if (!outHeader.canWriteVersion(1)) outVersion = 2; myIO.writeNew(filename, outHeader, outVersion); const vector& origDims = getOriginalDimensions(); vector extraDims;//non-spatial dims if (origDims.size() > 3) { extraDims = vector(origDims.begin() + 3, origDims.end()); } for (MultiDimIterator myiter(extraDims); !myiter.atEnd(); ++myiter) { myIO.writeData(getFrame(getBrickIndexFromNonSpatialIndexes(*myiter)), 3, *myiter);//NOTE: does not deal with multi-component volumes } m_header.grabNew(new NiftiHeader(outHeader));//update header to last written version, end nifti-specific code m_volumeFileEditorDelegate->clear(); m_volumeFileEditorDelegate->updateIfVolumeFileChangedNumberOfMaps(); } float VolumeFile::interpolateValue(const float* coordIn, InterpType interp, bool* validOut, const int64_t brickIndex, const int64_t component) const { return interpolateValue(coordIn[0], coordIn[1], coordIn[2], interp, validOut, brickIndex, component); } float VolumeFile::interpolateValue(const float coordIn1, const float coordIn2, const float coordIn3, InterpType interp, bool* validOut, const int64_t brickIndex, const int64_t component) const { /* * If the volume is a single slice, CUBIC and TRILINEAR will fail they * require and interpolate between adjacent slices. */ if (m_singleSliceFlag) { interp = ENCLOSING_VOXEL; } const int64_t* dimensions = getDimensionsPtr(); switch (interp) { case CUBIC: { float indexSpace[3]; spaceToIndex(coordIn1, coordIn2, coordIn3, indexSpace); int64_t ind1low = floor(indexSpace[0]); int64_t ind2low = floor(indexSpace[1]); int64_t ind3low = floor(indexSpace[2]); int64_t ind1high = ind1low + 1; int64_t ind2high = ind2low + 1; int64_t ind3high = ind3low + 1; if (!indexValid(ind1low, ind2low, ind3low, brickIndex, component) || !indexValid(ind1high, ind2high, ind3high, brickIndex, component)) { if (validOut != NULL) *validOut = false; return INVALID_INTERP_VALUE;//check for valid coord before deconvolving the frame } int64_t whichFrame = component * dimensions[3] + brickIndex; validateSpline(brickIndex, component); if (validOut != NULL) *validOut = true; return m_frameSplines[whichFrame].sample(indexSpace); } case TRILINEAR: { float index1, index2, index3; spaceToIndex(coordIn1, coordIn2, coordIn3, index1, index2, index3); int64_t ind1low = floor(index1); int64_t ind2low = floor(index2); int64_t ind3low = floor(index3); int64_t ind1high = ind1low + 1; int64_t ind2high = ind2low + 1; int64_t ind3high = ind3low + 1; if (!indexValid(ind1low, ind2low, ind3low, brickIndex, component) || !indexValid(ind1high, ind2high, ind3high, brickIndex, component)) { if (validOut != NULL) *validOut = false; return INVALID_INTERP_VALUE; } float xhighWeight = index1 - ind1low; float xlowWeight = 1.0f - xhighWeight; float xinterp[2][2];//manually unrolled, because loops of 2 seem silly xinterp[0][0] = xlowWeight * getValue(ind1low, ind2low, ind3low, brickIndex, component) + xhighWeight * getValue(ind1high, ind2low, ind3low, brickIndex, component); xinterp[1][0] = xlowWeight * getValue(ind1low, ind2high, ind3low, brickIndex, component) + xhighWeight * getValue(ind1high, ind2high, ind3low, brickIndex, component); xinterp[0][1] = xlowWeight * getValue(ind1low, ind2low, ind3high, brickIndex, component) + xhighWeight * getValue(ind1high, ind2low, ind3high, brickIndex, component); xinterp[1][1] = xlowWeight * getValue(ind1low, ind2high, ind3high, brickIndex, component) + xhighWeight * getValue(ind1high, ind2high, ind3high, brickIndex, component); float yhighWeight = index2 - ind2low; float ylowWeight = 1.0f - yhighWeight; float yinterp[2]; yinterp[0] = ylowWeight * xinterp[0][0] + yhighWeight * xinterp[1][0]; yinterp[1] = ylowWeight * xinterp[0][1] + yhighWeight * xinterp[1][1]; float zhighWeight = index3 - ind3low; float zlowWeight = 1.0f - zhighWeight; float ret = zlowWeight * yinterp[0] + zhighWeight * yinterp[1]; if (validOut != NULL) *validOut = true; return ret; } break; case ENCLOSING_VOXEL: { int64_t index1, index2, index3; enclosingVoxel(coordIn1, coordIn2, coordIn3, index1, index2, index3); if (indexValid(index1, index2, index3, brickIndex, component)) { if (validOut != NULL) *validOut = true; return getValue(index1, index2, index3, brickIndex, component); } else { if (validOut != NULL) *validOut = false; return INVALID_INTERP_VALUE; } } break; } if (validOut != NULL) *validOut = false; return INVALID_INTERP_VALUE; } void VolumeFile::validateSpline(const int64_t brickIndex, const int64_t component) const { const int64_t* dimensions = getDimensionsPtr(); CaretAssert(brickIndex >= 0 && brickIndex < dimensions[3]);//function is public, so check inputs CaretAssert(component >= 0 && component < dimensions[4]); int64_t numFrames = dimensions[3] * dimensions[4], whichFrame = component * dimensions[3] + brickIndex; if (!m_splinesValid) { CaretMutexLocker locked(&m_splineMutex);//prevent concurrent modify access to spline state if (!m_splinesValid)//double check { m_frameSplineValid = vector(numFrames, false); m_frameSplines = vector(numFrames);//release the old spline memory m_splinesValid = true;//the only purpose of this flag is for setModified to be fast, don't worry about it becoming false again before the below happens } } CaretAssert((int64_t)m_frameSplineValid.size() == numFrames); CaretAssert((int64_t)m_frameSplines.size() == numFrames); if (!m_frameSplineValid[whichFrame]) { CaretMutexLocker locked(&m_splineMutex);//prevent concurrent modify access to spline state if (!m_frameSplineValid[whichFrame])//double check { m_frameSplines[whichFrame] = VolumeSpline(getFrame(brickIndex, component), dimensions); if (m_frameSplines[whichFrame].ignoredNonNumeric()) { CaretLogWarning("ignored non-numeric input value when calculating cubic splines in volume '" + getFileName() + "', frame #" + AString::number(brickIndex + 1)); } m_frameSplineValid[whichFrame] = true; } } } void VolumeFile::freeSpline(const int64_t brickIndex, const int64_t component) const { const int64_t* dimensions = getDimensionsPtr(); CaretAssert(brickIndex >= 0 && brickIndex < dimensions[3]);//function is public, so check inputs CaretAssert(component >= 0 && component < dimensions[4]); int64_t numFrames = dimensions[3] * dimensions[4], whichFrame = component * dimensions[3] + brickIndex; if (!m_splinesValid) { CaretMutexLocker locked(&m_splineMutex);//prevent concurrent modify access to spline state if (!m_splinesValid)//double check { m_frameSplineValid = vector(numFrames, false); m_frameSplines = vector(numFrames);//release the old spline memory m_splinesValid = true;//the only purpose of this flag is for setModified to be fast } return;//already freed, we are done } CaretAssert((int64_t)m_frameSplineValid.size() == numFrames); CaretAssert((int64_t)m_frameSplines.size() == numFrames); if (m_frameSplineValid[whichFrame]) { CaretMutexLocker locked(&m_splineMutex);//prevent concurrent modify access to spline state if (m_frameSplineValid[whichFrame])//double check { m_frameSplines[whichFrame] = VolumeSpline(); m_frameSplineValid[whichFrame] = false; } } } bool VolumeFile::matchesVolumeSpace(const VolumeFile* right) const { return getVolumeSpace().matches(right->getVolumeSpace()); } bool VolumeFile::matchesVolumeSpace(const VolumeSpace& otherSpace) const { return getVolumeSpace().matches(otherSpace); } bool VolumeFile::matchesVolumeSpace(const int64_t dims[3], const vector >& sform) const { return getVolumeSpace().matches(VolumeSpace(dims, sform)); } void VolumeFile::parseExtensions() { const int NIFTI_ECODE_CARET = 30;//this should probably go in nifti1.h if (m_header != NULL && m_header->getType() == AbstractHeader::NIFTI) { const NiftiHeader& myHeader = *((NiftiHeader*)m_header.getPointer()); int numExtensions = (int)myHeader.m_extensions.size(); int whichExt = -1, whichType = -1;//type will track caret's preference in which extension to read, the greater the type, the more it prefers it for (int i = 0; i < numExtensions; ++i) { const NiftiExtension& myNiftiExtension = *(myHeader.m_extensions[i]); switch (myNiftiExtension.m_ecode) { case NIFTI_ECODE_CARET: if (100 > whichType)//mostly to make it use the first caret extension it finds in the list of extensions { whichExt = i; whichType = 100;//caret extension gets maximum priority } break; default: break; } break; } if (whichExt != -1) { switch (whichType) { case 100://caret extension { QByteArray myByteArray(myHeader.m_extensions[whichExt]->m_bytes.data(), myHeader.m_extensions[whichExt]->m_bytes.size()); AString myString(myByteArray); m_caretVolExt.readFromXmlString(myString); break; } default: break; } } } validateMembers(); } void VolumeFile::updateCaretExtension() { const int NIFTI_ECODE_CARET = 30;//this should probably go in nifti1.h stringstream mystream; XmlWriter myWriter(mystream); m_caretVolExt.writeAsXML(myWriter); string myStr = mystream.str(); if (m_header == NULL) m_header.grabNew(new NiftiHeader()); switch (m_header->getType()) { case AbstractHeader::NIFTI: { NiftiHeader& myHeader = *((NiftiHeader*)m_header.getPointer()); int numExtensions = (int)myHeader.m_extensions.size(); for (int i = 0; i < numExtensions; ++i)//erase all existing caret extensions { NiftiExtension* myNiftiExtension = myHeader.m_extensions[i]; if (myNiftiExtension->m_ecode == NIFTI_ECODE_CARET) { myHeader.m_extensions.erase(myHeader.m_extensions.begin() + i); --i; --numExtensions; } } CaretPointer newExt(new NiftiExtension()); newExt->m_ecode = NIFTI_ECODE_CARET; int length = myStr.length(); newExt->m_bytes.resize(length + 1);//allocate a null byte for safety for (int i = 0; i < length; ++i) { newExt->m_bytes[i] = myStr[i]; } newExt->m_bytes[length] = '\0'; myHeader.m_extensions.push_back(newExt); break; } } } void VolumeFile::validateMembers() { m_dataRangeValid = false; const int64_t* dimensions = getDimensionsPtr(); m_frameSplineValid = vector(dimensions[3] * dimensions[4], false); m_frameSplines = vector(dimensions[3] * dimensions[4]);//release any previous spline memory m_splinesValid = true;//this now indicates only if they need to all be recalculated - the frame vectors will always have the correct length int numMaps = getNumberOfMaps(); m_brickAttributes.resize(numMaps);//only resize, if this was called from reinitialize, it has called clear() beforehand m_brickStatisticsValid = true; int curAttribNum = (int)m_caretVolExt.m_attributes.size(); if (curAttribNum != numMaps) { m_caretVolExt.m_attributes.resize(numMaps); } bool isLabel = false; SubvolumeAttributes::VolumeType theType = SubvolumeAttributes::ANATOMY; if (numMaps > 0 && curAttribNum > 0 && m_caretVolExt.m_attributes[0] != NULL) { theType = m_caretVolExt.m_attributes[0]->m_type; if (theType == SubvolumeAttributes::UNKNOWN) { theType = SubvolumeAttributes::ANATOMY; } if (theType == SubvolumeAttributes::LABEL) { isLabel = true; } } for (int i = 0; i < numMaps; ++i) { if (m_caretVolExt.m_attributes[i] == NULL) { m_caretVolExt.m_attributes[i].grabNew(new SubvolumeAttributes()); } m_caretVolExt.m_attributes[i]->m_type = theType; if (isLabel) { m_caretVolExt.m_attributes[i]->m_palette.grabNew(NULL); if (m_caretVolExt.m_attributes[i]->m_labelTable == NULL) { m_caretVolExt.m_attributes[i]->m_labelTable.grabNew(new GiftiLabelTable());//TODO: populate the label table by means of the frame values? } } else { m_caretVolExt.m_attributes[i]->m_labelTable.grabNew(NULL); if (m_caretVolExt.m_attributes[i]->m_palette == NULL) { m_caretVolExt.m_attributes[i]->m_palette.grabNew(new PaletteColorMapping()); if (theType == SubvolumeAttributes::ANATOMY) { m_caretVolExt.m_attributes[i]->m_palette->setSelectedPaletteName(Palette::GRAY_INTERP_POSITIVE_PALETTE_NAME); m_caretVolExt.m_attributes[i]->m_palette->setScaleMode(PaletteScaleModeEnum::MODE_AUTO_SCALE_PERCENTAGE); } } } } setPaletteNormalizationMode(PaletteNormalizationModeEnum::NORMALIZATION_SELECTED_MAP_DATA); m_singleSliceFlag = false; if ((dimensions[0] == 1) || (dimensions[1] == 1) || (dimensions[2] == 1)) { m_singleSliceFlag = true; } /* * Will handle colorization of voxel data. */ if (m_voxelColorizer != NULL) { m_voxelColorizer.grabNew(NULL); } if (s_voxelColoringEnabled) { m_voxelColorizer.grabNew(new VolumeFileVoxelColorizer(this)); } if (m_classNameHierarchy == NULL) { m_classNameHierarchy.grabNew(new GroupAndNameHierarchyModel()); } m_classNameHierarchy->clear(); m_forceUpdateOfGroupAndNameHierarchy = true; m_volumeFileEditorDelegate.grabNew(new VolumeFileEditorDelegate(this)); m_volumeFileEditorDelegate->updateIfVolumeFileChangedNumberOfMaps(); } /** * Set this file as modified. */ void VolumeFile::setModified() { DataFile::setModified(); VolumeBase::setModified(); m_brickStatisticsValid = false; m_splinesValid = false; m_fileFastStatistics.grabNew(NULL); m_fileHistogram.grabNew(NULL); m_fileHistorgramLimitedValues.grabNew(NULL); } /** * Clear the modified status of this file. */ void VolumeFile::clearModified() { CaretMappableDataFile::clearModified(); clearModifiedVolumeBase(); const int32_t numMaps = getNumberOfMaps(); if (isMappedWithPalette()) { for (int32_t i = 0; i < numMaps; i++) { PaletteColorMapping* pcm = getMapPaletteColorMapping(i); pcm->clearModified(); } } else if (isMappedWithLabelTable()) { for (int32_t i = 0; i < numMaps; i++) { getMapLabelTable(i)->clearModified(); } } } /** * @eturn The modified status of this file. * * NOTE: DOES NOT include palette color mapping modified status. */ bool VolumeFile::isModifiedExcludingPaletteColorMapping() const { if (CaretMappableDataFile::isModifiedExcludingPaletteColorMapping()) { return true; } if (isModifiedVolumeBase()) { return true; } const int32_t numMaps = getNumberOfMaps(); if (isMappedWithLabelTable()) { for (int32_t i = 0; i < numMaps; i++) { if (getMapLabelTable(i)->isModified()) { return true; } } } return false; } /** * @return The structure for this file. */ StructureEnum::Enum VolumeFile::getStructure() const { return StructureEnum::INVALID; } /** * Set the structure for this file. * @param structure * New structure for this file. */ void VolumeFile::setStructure(const StructureEnum::Enum /*structure*/) { /* no structure in volulme file */ } /** * Get the name of the map at the given index. * * @param mapIndex * Index of the map. * @return * Name of the map. */ AString VolumeFile::getMapName(const int32_t mapIndex) const { CaretAssertVectorIndex(m_caretVolExt.m_attributes, mapIndex); CaretAssert(m_caretVolExt.m_attributes[mapIndex] != NULL); AString name = m_caretVolExt.m_attributes[mapIndex]->m_guiLabel; return name; } /** * Set the name of the map at the given index. * * @param mapIndex * Index of the map. * @param mapName * New name for the map. */ void VolumeFile::setMapName(const int32_t mapIndex, const AString& mapName) { CaretAssertVectorIndex(m_caretVolExt.m_attributes, mapIndex); CaretAssert(m_caretVolExt.m_attributes[mapIndex] != NULL); m_caretVolExt.m_attributes[mapIndex]->m_guiLabel = mapName; setModified(); } 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; } 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; } void VolumeFile::checkStatisticsValid() { if (m_brickStatisticsValid == false) { int32_t numMaps = getNumberOfMaps(); for (int i = 0; i < numMaps; ++i) { m_brickAttributes[i].m_fastStatistics.grabNew(NULL); m_brickAttributes[i].m_histogram.grabNew(NULL); m_brickAttributes[i].m_histogramLimitedValues.grabNew(NULL); } m_brickStatisticsValid = true; } } const FastStatistics* VolumeFile::getMapFastStatistics(const int32_t mapIndex) { CaretAssertVectorIndex(m_brickAttributes, mapIndex); checkStatisticsValid(); const int64_t* dimensions = getDimensionsPtr(); if (m_brickAttributes[mapIndex].m_fastStatistics == NULL) { m_brickAttributes[mapIndex].m_fastStatistics.grabNew(new FastStatistics(getFrame(mapIndex), dimensions[0] * dimensions[1] * dimensions[2])); } return m_brickAttributes[mapIndex].m_fastStatistics; } const Histogram* VolumeFile::getMapHistogram(const int32_t mapIndex) { CaretAssertVectorIndex(m_brickAttributes, mapIndex); checkStatisticsValid(); const int64_t* dimensions = getDimensionsPtr(); if (m_brickAttributes[mapIndex].m_histogram == NULL) { m_brickAttributes[mapIndex].m_histogram.grabNew(new Histogram(100, getFrame(mapIndex), dimensions[0] * dimensions[1] * dimensions[2])); } return m_brickAttributes[mapIndex].m_histogram; } /** * Update the Histogram for limited values. * * @param data * Data for histogram. * @param mostPositiveValueInclusive * Values more positive than this value are excluded. * @param leastPositiveValueInclusive * Values less positive than this value are excluded. * @param leastNegativeValueInclusive * Values less negative than this value are excluded. * @param mostNegativeValueInclusive * Values more negative than this value are excluded. * @param includeZeroValues * If true zero values (very near zero) are included. */ const Histogram* VolumeFile::getMapHistogram(const int32_t mapIndex, const float mostPositiveValueInclusive, const float leastPositiveValueInclusive, const float leastNegativeValueInclusive, const float mostNegativeValueInclusive, const bool includeZeroValues) { CaretAssertVectorIndex(m_brickAttributes, mapIndex); checkStatisticsValid(); const int64_t* dimensions = getDimensionsPtr(); bool updateHistogramFlag = false; 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) || (leastPositiveValueInclusive != m_brickAttributes[mapIndex].m_histogramLimitedValuesLeastPositiveValueInclusive) || (leastNegativeValueInclusive != m_brickAttributes[mapIndex].m_histogramLimitedValuesLeastNegativeValueInclusive) || (mostNegativeValueInclusive != m_brickAttributes[mapIndex].m_histogramLimitedValuesMostNegativeValueInclusive) || (includeZeroValues != m_brickAttributes[mapIndex].m_histogramLimitedValuesIncludeZeroValues)) { updateHistogramFlag = true; } if (updateHistogramFlag) { m_brickAttributes[mapIndex].m_histogramLimitedValues->update(getFrame(mapIndex), dimensions[0] * dimensions[1] * dimensions[2], mostPositiveValueInclusive, leastPositiveValueInclusive, leastNegativeValueInclusive, mostNegativeValueInclusive, includeZeroValues); m_brickAttributes[mapIndex].m_histogramLimitedValuesMostPositiveValueInclusive = mostPositiveValueInclusive; m_brickAttributes[mapIndex].m_histogramLimitedValuesLeastPositiveValueInclusive = leastPositiveValueInclusive; m_brickAttributes[mapIndex].m_histogramLimitedValuesLeastNegativeValueInclusive = leastNegativeValueInclusive; m_brickAttributes[mapIndex].m_histogramLimitedValuesMostNegativeValueInclusive = mostNegativeValueInclusive; m_brickAttributes[mapIndex].m_histogramLimitedValuesIncludeZeroValues = includeZeroValues; } return m_brickAttributes[mapIndex].m_histogramLimitedValues; } /** * @return The estimated size of data after it is uncompressed * and loaded into RAM. A negative value indicates that the * file size cannot be computed. */ int64_t VolumeFile::getDataSizeUncompressedInBytes() const { int64_t dimI, dimJ, dimK, dimTime, dimComp; getDimensions(dimI, dimJ, dimK, dimTime, dimComp); const int64_t numBytes = (dimI * dimJ * dimK * dimTime * dimComp * sizeof(float)); return numBytes; } /** * Get statistics describing the distribution of data * mapped with a color palette for all data within the file. * * @return * Fast statistics for data (will be NULL for data * not mapped using a palette). */ const FastStatistics* VolumeFile::getFileFastStatistics() { if (m_fileFastStatistics == NULL) { std::vector fileData; getFileData(fileData); if ( ! fileData.empty()) { m_fileFastStatistics.grabNew(new FastStatistics()); m_fileFastStatistics->update(&fileData[0], fileData.size()); } } return m_fileFastStatistics; } /** * Get histogram describing the distribution of data * mapped with a color palette for all data within * the file. * * @return * Histogram for data (will be NULL for data * not mapped using a palette). */ const Histogram* VolumeFile::getFileHistogram() { if (m_fileHistogram == NULL) { std::vector fileData; getFileData(fileData); if ( ! fileData.empty()) { m_fileHistogram.grabNew(new Histogram()); m_fileHistogram->update(&fileData[0], fileData.size()); } } return m_fileHistogram; } /** * Get histogram describing the distribution of data * mapped with a color palette for all data in the file * within the given range of values. * * @param mostPositiveValueInclusive * Values more positive than this value are excluded. * @param leastPositiveValueInclusive * Values less positive than this value are excluded. * @param leastNegativeValueInclusive * Values less negative than this value are excluded. * @param mostNegativeValueInclusive * Values more negative than this value are excluded. * @param includeZeroValues * If true zero values (very near zero) are included. * @return * Descriptive statistics for data (will be NULL for data * not mapped using a palette). */ const Histogram* VolumeFile::getFileHistogram(const float mostPositiveValueInclusive, const float leastPositiveValueInclusive, const float leastNegativeValueInclusive, const float mostNegativeValueInclusive, const bool includeZeroValues) { 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; getFileData(fileData); if ( ! fileData.empty()) { if (m_fileHistorgramLimitedValues == NULL) { m_fileHistorgramLimitedValues.grabNew(new Histogram()); } m_fileHistorgramLimitedValues->update(&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; } /** * Get all data for a volume file. If the file is very * large this method may take a large amount of time! * * @param dataOut * Output with all data for a file. Empty if no data in file * or data is not float. */ void VolumeFile::getFileData(std::vector& dataOut) const { int64_t dimI, dimJ, dimK, dimTime, dimComp; getDimensions(dimI, dimJ, dimK, dimTime, dimComp); const int64_t mapSize = dimI * dimJ * dimK * dimComp; const int64_t numMaps = dimTime; const int64_t dataSize = mapSize * numMaps; if (dataSize <= 0) { dataOut.clear(); return; } dataOut.resize(dataSize); int64_t dataOffset = 0; for (int iMap = 0; iMap < numMaps; iMap++) { const float* mapData = getFrame(iMap); for (int64_t i = 0; i < mapSize; i++) { CaretAssertVectorIndex(dataOut, dataOffset); dataOut[dataOffset] = mapData[i]; ++dataOffset; } } CaretAssert(dataOffset == static_cast(dataOut.size())); } /** * @return Is the data in the file mapped to colors using * a palette. */ bool VolumeFile::isMappedWithPalette() const { CaretAssertVectorIndex(m_caretVolExt.m_attributes, 0); CaretAssert(m_caretVolExt.m_attributes[0] != NULL); bool mapsWithPaletteFlag = true; switch (m_caretVolExt.m_attributes[0]->m_type) { case SubvolumeAttributes::ANATOMY: break; case SubvolumeAttributes::FUNCTIONAL: break; case SubvolumeAttributes::LABEL: mapsWithPaletteFlag = false; break; case SubvolumeAttributes::RGB: mapsWithPaletteFlag = false; break; case SubvolumeAttributes::SEGMENTATION: break; case SubvolumeAttributes::UNKNOWN: break; case SubvolumeAttributes::VECTOR: break; } return mapsWithPaletteFlag; // return (m_caretVolExt.m_attributes[0]->m_type != SubvolumeAttributes::LABEL); } /** * Get the palette normalization modes that are supported by the file. * * @param modesSupportedOut * Palette normalization modes supported by a file. Will be * empty for files that are not mapped with a palette. If there * is more than one suppported mode, the first mode in the * vector is assumed to be the default mode. */ void VolumeFile::getPaletteNormalizationModesSupported(std::vector& modesSupportedOut) { modesSupportedOut.clear(); modesSupportedOut.push_back(PaletteNormalizationModeEnum::NORMALIZATION_SELECTED_MAP_DATA); modesSupportedOut.push_back(PaletteNormalizationModeEnum::NORMALIZATION_ALL_MAP_DATA); } /** * Get the palette color mapping for the map at the given index. * * @param mapIndex * Index of the map. * @return * Palette color mapping for the map (will be NULL for data * not mapped using a palette). */ PaletteColorMapping* VolumeFile::getMapPaletteColorMapping(const int32_t mapIndex) { CaretAssertVectorIndex(m_caretVolExt.m_attributes, mapIndex); CaretAssert(m_caretVolExt.m_attributes[mapIndex] != NULL); CaretAssert(m_caretVolExt.m_attributes[mapIndex]->m_palette != NULL); return m_caretVolExt.m_attributes[mapIndex]->m_palette; } /** * Get the palette color mapping for the map at the given index. * * @param mapIndex * Index of the map. * @return * Palette color mapping for the map (constant) (will be NULL for data * not mapped using a palette). */ const PaletteColorMapping* VolumeFile::getMapPaletteColorMapping(const int32_t mapIndex) const { CaretAssertVectorIndex(m_caretVolExt.m_attributes, mapIndex); CaretAssert(m_caretVolExt.m_attributes[mapIndex] != NULL); CaretAssert(m_caretVolExt.m_attributes[mapIndex]->m_palette != NULL); return m_caretVolExt.m_attributes[mapIndex]->m_palette; } /** * @return Is the data in the file mapped to colors using * a label table. */ bool VolumeFile::isMappedWithLabelTable() const { CaretAssertVectorIndex(m_caretVolExt.m_attributes, 0); CaretAssert(m_caretVolExt.m_attributes[0] != NULL); return (m_caretVolExt.m_attributes[0]->m_type == SubvolumeAttributes::LABEL); } /** * Get the label table for the map at the given index. * * @param mapIndex * Index of the map. * @return * Label table for the map (will be NULL for data * not mapped using a label table). */ GiftiLabelTable* VolumeFile::getMapLabelTable(const int32_t mapIndex) { CaretAssertVectorIndex(m_caretVolExt.m_attributes, mapIndex); CaretAssert(m_caretVolExt.m_attributes[mapIndex] != NULL); CaretAssert(m_caretVolExt.m_attributes[mapIndex]->m_labelTable != NULL); return m_caretVolExt.m_attributes[mapIndex]->m_labelTable; } /** * Get the label table for the map at the given index. * * @param mapIndex * Index of the map. * @return * Label table for the map (constant) (will be NULL for data * not mapped using a label table). */ const GiftiLabelTable* VolumeFile::getMapLabelTable(const int32_t mapIndex) const { CaretAssertVectorIndex(m_caretVolExt.m_attributes, mapIndex); CaretAssert(m_caretVolExt.m_attributes[mapIndex] != NULL); CaretAssert(m_caretVolExt.m_attributes[mapIndex]->m_labelTable != NULL); return m_caretVolExt.m_attributes[mapIndex]->m_labelTable; } /** * @return Is the data in the file mapped to colors using * Red, Green, Blue, Alpha values. */ bool VolumeFile::isMappedWithRGBA() const { bool mapsWithRgbaFlag = false; switch (m_caretVolExt.m_attributes[0]->m_type) { case SubvolumeAttributes::ANATOMY: break; case SubvolumeAttributes::FUNCTIONAL: break; case SubvolumeAttributes::LABEL: break; case SubvolumeAttributes::RGB: mapsWithRgbaFlag = true; break; case SubvolumeAttributes::SEGMENTATION: break; case SubvolumeAttributes::UNKNOWN: break; case SubvolumeAttributes::VECTOR: break; } return mapsWithRgbaFlag; } /** * Get the unique ID (UUID) for the map at the given index. * * @param mapIndex * Index of the map. * @return * String containing UUID for the map. */ 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(); } /** * @return Bounding box of the volumes spatial coordinates. */ void VolumeFile::getVoxelSpaceBoundingBox(BoundingBox& boundingBoxOut) const { boundingBoxOut.resetForUpdate(); float coordinates[3]; const int64_t* dimensions = getDimensionsPtr(); for (int i = 0; i < 2; ++i)//if the volume isn't plumb, we need to test all corners, so just always test all corners { for (int j = 0; j < 2; ++j) { for (int k = 0; k < 2; ++k) { this->indexToSpace(i * dimensions[0] - 0.5f, j * dimensions[1] - 0.5f, k * dimensions[2] - 0.5f, coordinates);//accounts for extra half voxel on each side of each center boundingBoxOut.update(coordinates); } } } } /** * Update coloring for a map. * Does nothing if coloring is not enabled. * * @param mapIndex * Index of map. * @param paletteFile * File containing the palettes. */ void VolumeFile::updateScalarColoringForMap(const int32_t mapIndex, const PaletteFile* paletteFile) { if (s_voxelColoringEnabled == false) { return; } 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, palette, this, mapIndex); } /** * Get the voxel RGBA coloring for a map. * 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 * Plane for which colors are requested. * @param sliceIndex * Index of the slice. * @param displayGroup * The selected display group. * @param tabIndex * Index of selected tab. * @param rgbaOut * Contains colors upon exit. * @return * 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, const DisplayGroupEnum::Enum displayGroup, const int32_t tabIndex, uint8_t* rgbaOut) const { if (s_voxelColoringEnabled == false) { return 0; } CaretAssert(m_voxelColorizer); return m_voxelColorizer->getVoxelColorsForSliceInMap(mapIndex, slicePlane, sliceIndex, displayGroup, tabIndex, rgbaOut); } /** * Get voxel coloring for a set of voxels. * * @param mapIndex * Index of map. * @param firstVoxelIJK * IJK Indices of first voxel * @param rowStepIJK * IJK Step for moving to next row. * @param columnStepIJK * IJK Step for moving to next column. * @param numberOfRows * Number of rows. * @param numberOfColumns * Number of columns. * @param displayGroup * The selected display group. * @param tabIndex * Index of selected tab. * @param rgbaOut * RGBA color components out. * @return * Number of voxels with alpha greater than zero */ int64_t VolumeFile::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 { if (s_voxelColoringEnabled == false) { return 0; } CaretAssert(m_voxelColorizer); return m_voxelColorizer->getVoxelColorsForSliceInMap(mapIndex, firstVoxelIJK, rowStepIJK, columnStepIJK, numberOfRows, numberOfColumns, displayGroup, tabIndex, rgbaOut); } /** * Get the voxel colors for a sub slice in the map. * * @param paletteFile * The palette file. * @param mapIndex * Index of the map. * @param slicePlane * The slice plane. * @param sliceIndex * Index of the slice. * @param firstCornerVoxelIndex * Indices of voxel for first corner of sub-slice (inclusive). * @param lastCornerVoxelIndex * Indices of voxel for last corner of sub-slice (inclusive). * @param voxelCountIJK * Voxel counts for each axis. * @param displayGroup * The selected display group. * @param tabIndex * Index of selected tab. * @param rgbaOut * Output containing the rgba values (must have been allocated * by caller to sufficient count of elements in the slice). * @return * Number of voxels with alpha greater than zero */ int64_t VolumeFile::getVoxelColorsForSubSliceInMap(const PaletteFile* /*paletteFile*/, const int32_t mapIndex, const VolumeSliceViewPlaneEnum::Enum slicePlane, const int64_t sliceIndex, const int64_t firstCornerVoxelIndex[3], const int64_t lastCornerVoxelIndex[3], const int64_t voxelCountIJK[3], const DisplayGroupEnum::Enum displayGroup, const int32_t tabIndex, uint8_t* rgbaOut) const { if (s_voxelColoringEnabled == false) { return 0; } CaretAssert(m_voxelColorizer); return m_voxelColorizer->getVoxelColorsForSubSliceInMap(mapIndex, slicePlane, sliceIndex, firstCornerVoxelIndex, lastCornerVoxelIndex, voxelCountIJK, displayGroup, tabIndex, rgbaOut); } /** * Get the voxel values for a slice in a map. * * @param mapIndex * Index of the map. * @param slicePlane * Plane for which colors are requested. * @param sliceIndex * Index of the slice. * @param sliceValuesOut * Slice values output must be correct number of elements. */ void VolumeFile::getVoxelValuesForSliceInMap(const int32_t mapIndex, const VolumeSliceViewPlaneEnum::Enum slicePlane, const int64_t sliceIndex, float* sliceValuesOut) const { CaretAssert(sliceValuesOut); if (s_voxelColoringEnabled == false) { return; } int64_t dimI, dimJ, dimK, dimTime, dimMaps; getDimensions(dimI, dimJ, dimK, dimTime, dimMaps); switch (slicePlane) { case VolumeSliceViewPlaneEnum::ALL: return; break; case VolumeSliceViewPlaneEnum::AXIAL: { int64_t counter = 0; for (int64_t j = 0; j < dimJ; j++) { for (int64_t i = 0; i < dimI; i++) { sliceValuesOut[counter] = getValue(i, j, sliceIndex, 0, mapIndex); counter++; } } } break; case VolumeSliceViewPlaneEnum::CORONAL: { int64_t counter = 0; for (int64_t k = 0; k < dimK; k++) { for (int64_t i = 0; i < dimI; i++) { sliceValuesOut[counter] = getValue(i, sliceIndex, k, 0, mapIndex); counter++; } } } break; case VolumeSliceViewPlaneEnum::PARASAGITTAL: { int64_t counter = 0; for (int64_t k = 0; k < dimK; k++) { for (int64_t j = 0; j < dimJ; j++) { sliceValuesOut[counter] = getValue(sliceIndex, j, k, 0, mapIndex); counter++; } } } break; } } /** * Get the RGBA color components for voxel. * 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 * Coronal index * @param k * Axial index * @param mapIndex * Index of map. * @param displayGroup * The selected display group. * @param tabIndex * Index of selected tab. * @param rgbaOut * 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, const DisplayGroupEnum::Enum displayGroup, const int32_t tabIndex, uint8_t rgbaOut[4]) const { if (s_voxelColoringEnabled == false) { return; } CaretAssert(m_voxelColorizer); m_voxelColorizer->getVoxelColorInMap(i, j, k, mapIndex, displayGroup, tabIndex, rgbaOut); } /** * Clear the voxel coloring for the given map. * Does nothing if coloring is not enabled. * * @param mapIndex * Index of map. */ void VolumeFile::clearVoxelColoringForMap(const int64_t mapIndex) { if (s_voxelColoringEnabled == false) { return; } CaretAssert(m_voxelColorizer); m_voxelColorizer->clearVoxelColoringForMap(mapIndex); if (isMappedWithLabelTable()) { m_forceUpdateOfGroupAndNameHierarchy = true; } } /** * Get the minimum and maximum values from ALL maps in this file. * Note that not all files (due to size of file) are able to provide * the minimum and maximum values from the file. The return value * indicates success/failure. If the failure (false) is returned * the returned values are likely +/- the maximum float values. * * @param dataRangeMinimumOut * Minimum data value found. * @param dataRangeMaximumOut * Maximum data value found. * @return * True if the values are valid, else false. */ bool VolumeFile::getDataRangeFromAllMaps(float& dataRangeMinimumOut, float& dataRangeMaximumOut) const { /* * No data? */ if (isEmpty()) { dataRangeMaximumOut = std::numeric_limits::max(); dataRangeMinimumOut = -dataRangeMaximumOut; return false; } /* * If valid, no need to update */ if (m_dataRangeValid) { dataRangeMinimumOut = m_dataRangeMinimum; dataRangeMaximumOut = m_dataRangeMaximum; return true; } /* * Update range. */ m_dataRangeMaximum = -std::numeric_limits::max(); m_dataRangeMinimum = std::numeric_limits::max(); const int64_t* dimensions = getDimensionsPtr(); int64_t m_dataSize = dimensions[0] * dimensions[1] * dimensions[2] * dimensions[3] * dimensions[4]; const float* data = getFrame();//HACK: use first frame knowing all data is contiguous after it for (int64_t i = 0; i < m_dataSize; i++) { if (data[i] > m_dataRangeMaximum) { m_dataRangeMaximum = data[i]; } if (data[i] < m_dataRangeMinimum) { m_dataRangeMinimum = data[i]; } } dataRangeMinimumOut = m_dataRangeMinimum; dataRangeMaximumOut = m_dataRangeMaximum; m_dataRangeValid = true; return true; } /** * Get the voxel indices of all voxels in the given map with the given label key. * * @param mapIndex * Index of map. * @param labelKey * Key of the label. * @param voxelIndicesOut * Output containing indices of voxels with the given label key. */ void VolumeFile::getVoxelIndicesWithLabelKey(const int32_t mapIndex, const int32_t labelKey, std::vector& voxelIndicesOut) const { voxelIndicesOut.clear(); std::vector dims; getDimensions(dims); const int64_t dimI = dims[0]; const int64_t dimJ = dims[1]; const int64_t dimK = dims[2]; for (int64_t i = 0; i < dimI; i++) { for (int64_t j = 0; j < dimJ; j++) { for (int64_t k = 0; k < dimK; k++) { const float keyValue = static_cast(getValue(i, j, k, mapIndex)); if (keyValue == labelKey) { voxelIndicesOut.push_back(VoxelIJK(i, j, k)); } } } } } /** * Get the unique label keys in the given map. * @param mapIndex * Index of the map. * @return * Keys used by the map. */ std::vector VolumeFile::getUniqueLabelKeysUsedInMap(const int32_t mapIndex) const { std::vector dims; getDimensions(dims); const int64_t dimI = dims[0]; const int64_t dimJ = dims[1]; const int64_t dimK = dims[2]; std::set uniqueKeys; for (int64_t i = 0; i < dimI; i++) { for (int64_t j = 0; j < dimJ; j++) { for (int64_t k = 0; k < dimK; k++) { const float keyValue = static_cast(getValue(i, j, k, mapIndex)); uniqueKeys.insert(keyValue); } } } std::vector keyVector; keyVector.insert(keyVector.end(), uniqueKeys.begin(), uniqueKeys.end()); return keyVector; } /** * @return The class and name hierarchy. */ GroupAndNameHierarchyModel* VolumeFile::getGroupAndNameHierarchyModel() { m_classNameHierarchy->update(this, m_forceUpdateOfGroupAndNameHierarchy); m_forceUpdateOfGroupAndNameHierarchy = false; return m_classNameHierarchy; } /** * @return The class and name hierarchy. */ const GroupAndNameHierarchyModel* VolumeFile::getGroupAndNameHierarchyModel() const { m_classNameHierarchy->update(const_cast(this), m_forceUpdateOfGroupAndNameHierarchy); m_forceUpdateOfGroupAndNameHierarchy = false; return m_classNameHierarchy; } /** * @return The volume file editor delegate used for interactive * editing of a volume's voxels. */ VolumeFileEditorDelegate* VolumeFile::getVolumeFileEditorDelegate() { CaretAssert(m_volumeFileEditorDelegate); return m_volumeFileEditorDelegate; } /** * Save file data from the scene. For subclasses that need to * save to a scene, this method should be overriden. sceneClass * will be valid and any scene data should be added to it. * * @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. */ void VolumeFile::saveFileDataToScene(const SceneAttributes* sceneAttributes, SceneClass* sceneClass) { CaretMappableDataFile::saveFileDataToScene(sceneAttributes, sceneClass); sceneClass->addBooleanArray("m_chartingEnabledForTab", m_chartingEnabledForTab, BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS); if (isMappedWithLabelTable()) { sceneClass->addClass(m_classNameHierarchy->saveToScene(sceneAttributes, "m_classNameHierarchy")); } } /** * Restore file data from the scene. For subclasses that need to * restore from a scene, this method should be overridden. The scene class * will be valid and any scene data may be obtained from it. * * @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 VolumeFile::restoreFileDataFromScene(const SceneAttributes* sceneAttributes, const SceneClass* sceneClass) { CaretMappableDataFile::restoreFileDataFromScene(sceneAttributes, sceneClass); const ScenePrimitiveArray* tabArray = sceneClass->getPrimitiveArray("m_chartingEnabledForTab"); if (tabArray != NULL) { sceneClass->getBooleanArrayValue("m_chartingEnabledForTab", m_chartingEnabledForTab, BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS); } else { for (int32_t i = 0; i < BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS; i++) { m_chartingEnabledForTab[i] = false; } } if (isMappedWithLabelTable()) { const SceneClass* sc = sceneClass->getClass("m_classNameHierarchy"); m_classNameHierarchy->restoreFromScene(sceneAttributes, sc); m_forceUpdateOfGroupAndNameHierarchy = false; } } /** * Add information about the file to the data file information. * * @param dataFileInformation * Consolidates information about a data file. */ void VolumeFile::addToDataFileContentInformation(DataFileContentInformation& dataFileInformation) { CaretMappableDataFile::addToDataFileContentInformation(dataFileInformation); dataFileInformation.addNameAndValue("Orthogonal", isPlumb()); if (m_header != NULL && m_header->getType() == AbstractHeader::NIFTI) { const NiftiHeader& myHeader = *((NiftiHeader*)m_header.getPointer()); dataFileInformation.addNameAndValue("NIFTI Version", myHeader.version()); bool ok = false; dataFileInformation.addNameAndValue("NIFTI Data Type", NiftiDataTypeEnum::toName(NiftiDataTypeEnum::fromIntegerCode(myHeader.getDataType(), &ok)));//fromIntegerCode basically just ignores invalid values if (!ok) { CaretLogWarning("found invalid NIFTI datatype code while adding file information"); } } AString dimString; vector dims = getOriginalDimensions(); for (int i = 0; i < (int)dims.size(); ++i) { if (i != 0) dimString += ", "; dimString += AString::number(dims[i]); } dataFileInformation.addNameAndValue("Dimensions", dimString); const int64_t zero64 = 0; if (indexValid(zero64, zero64, zero64)) { float x, y, z; indexToSpace(zero64, zero64, zero64, x, y, z); dataFileInformation.addNameAndValue("IJK = (0,0,0)", ("XYZ = (" + AString::number(x) + ", " + AString::number(y) + ", " + AString::number(z) + ")")); } BoundingBox boundingBox; getVoxelSpaceBoundingBox(boundingBox); dataFileInformation.addNameAndValue("X-minimum", boundingBox.getMinX()); dataFileInformation.addNameAndValue("X-maximum", boundingBox.getMaxX()); dataFileInformation.addNameAndValue("Y-minimum", boundingBox.getMinY()); dataFileInformation.addNameAndValue("Y-maximum", boundingBox.getMaxY()); dataFileInformation.addNameAndValue("Z-minimum", boundingBox.getMinZ()); dataFileInformation.addNameAndValue("Z-maximum", boundingBox.getMaxZ()); VolumeSpace::OrientTypes orientation[3]; getOrientation(orientation); for (int32_t i = 0; i < 3; i++) { AString orientName; switch (orientation[i]) { case VolumeSpace::ANTERIOR_TO_POSTERIOR: orientName = "Anterior to Posterior"; break; case VolumeSpace::INFERIOR_TO_SUPERIOR: orientName = "Inferior to Superior"; break; case VolumeSpace::LEFT_TO_RIGHT: orientName = "Left to Right"; break; case VolumeSpace::POSTERIOR_TO_ANTERIOR: orientName = "Posterior to Anterior"; break; case VolumeSpace::RIGHT_TO_LEFT: orientName = "Right to Left"; break; case VolumeSpace::SUPERIOR_TO_INFERIOR: orientName = "Superior to Inferior"; break; } dataFileInformation.addNameAndValue(("Orientation[" + AString::number(i) + "]"), orientName); } float spacing[3]; getVoxelSpacing(spacing[0], spacing[1], spacing[2]); spacing[0] = std::fabs(spacing[0]); spacing[1] = std::fabs(spacing[1]); spacing[2] = std::fabs(spacing[2]); dataFileInformation.addNameAndValue("Spacing", AString::fromNumbers(spacing, 3, ", ")); } /** * @return Is charting enabled for this file? */ bool VolumeFile::isLineSeriesChartingEnabled(const int32_t tabIndex) const { CaretAssertArrayIndex(m_chartingEnabledForTab, BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS, tabIndex); return m_chartingEnabledForTab[tabIndex]; } /** * @return Return true if the file's current state supports * charting data, else false. Typically a brainordinate file * is chartable if it contains more than one map. */ bool VolumeFile::isLineSeriesChartingSupported() const { if (getNumberOfMaps() > 1) { return true; } return false; } /** * Set charting enabled for this file. * * @param enabled * New status for charting enabled. */ void VolumeFile::setLineSeriesChartingEnabled(const int32_t tabIndex, const bool enabled) { CaretAssertArrayIndex(m_chartingEnabledForTab, BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS, tabIndex); m_chartingEnabledForTab[tabIndex] = enabled; } /** * Get chart data types supported by the file. * * @param chartDataTypesOut * Chart types supported by this file. */ void VolumeFile::getSupportedLineSeriesChartDataTypes(std::vector& chartDataTypesOut) const { helpGetSupportedLineSeriesChartDataTypes(chartDataTypesOut); } /** * Load charting data for the surface with the given structure and node index. * * @param structure * The surface's structure. * @param nodeIndex * Index of the node. * @return * Pointer to the chart data. If the data FAILED to load, * the returned pointer will be NULL. Caller takes ownership * of the pointer and must delete it when no longer needed. */ ChartDataCartesian* VolumeFile::loadLineSeriesChartDataForSurfaceNode(const StructureEnum::Enum /*structure*/, const int32_t /*nodeIndex*/) { ChartDataCartesian* chartData = NULL; return chartData; } /** * Load average charting data for the surface with the given structure and node indices. * * @param structure * The surface's structure. * @param nodeIndices * Indices of the node. * @return * Pointer to the chart data. If the data FAILED to load, * the returned pointer will be NULL. Caller takes ownership * of the pointer and must delete it when no longer needed. */ ChartDataCartesian* VolumeFile::loadAverageLineSeriesChartDataForSurfaceNodes(const StructureEnum::Enum /*structure*/, const std::vector& /*nodeIndices*/) { ChartDataCartesian* chartData = NULL; return chartData; } /** * Load charting data for the voxel enclosing the given coordinate. * * @param xyz * Coordinate of voxel. * @return * Pointer to the chart data. If the data FAILED to load, * the returned pointer will be NULL. Caller takes ownership * of the pointer and must delete it when no longer needed. */ ChartDataCartesian* VolumeFile::loadLineSeriesChartDataForVoxelAtCoordinate(const float xyz[3]) { ChartDataCartesian* chartData = NULL; if (isMappedWithPalette()) { int64_t ijk[3]; enclosingVoxel(xyz, ijk); if (indexValid(ijk)) { std::vector data; const int32_t numMaps = getNumberOfMaps(); for (int32_t iMap = 0; iMap < numMaps; iMap++) { data.push_back(getValue(ijk, iMap)); } try { chartData = helpCreateCartesianChartData(data); ChartDataSource* dataSource = chartData->getChartDataSource(); dataSource->setVolumeVoxel(getFileName(), xyz); } catch (const DataFileException& dfe) { if (chartData != NULL) { delete chartData; chartData = NULL; } throw dfe; } } } return chartData; } connectome-workbench-1.2.3+git41-gc4c6c90/src/Files/VolumeFile.h000066400000000000000000000407271300200146000240400ustar00rootroot00000000000000 #ifndef __VOLUME_FILE_H__ #define __VOLUME_FILE_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 "BrainConstants.h" #include "VolumeBase.h" #include "CaretMappableDataFile.h" #include "CaretMutex.h" #include "CaretVolumeExtension.h" #include "ChartableLineSeriesBrainordinateInterface.h" #include "StructureEnum.h" #include "GiftiMetaData.h" #include "BoundingBox.h" #include "PaletteFile.h" #include "VolumeFileVoxelColorizer.h" #include "VoxelIJK.h" namespace caret { class GroupAndNameHierarchyModel; class VolumeFileEditorDelegate; class VolumeFileVoxelColorizer; class VolumeSpline; class VolumeFile : public VolumeBase, public CaretMappableDataFile, public ChartableLineSeriesBrainordinateInterface { VolumeFile(const VolumeFile&); VolumeFile& operator=(const VolumeFile&); CaretVolumeExtension m_caretVolExt; void parseExtensions();//called after reading a file, in order to populate m_caretVolExt with best guesses void validateMembers();//called to ensure extension agrees with number of subvolumes void updateCaretExtension();//called before writing a file, erases all existing caret extensions from m_extensions, and rebuilds one from m_caretVolExt void checkStatisticsValid(); struct BrickAttributes//for storing ONLY stuff that doesn't get saved to the caret extension {//TODO: prune this once statistics gets straightened out CaretPointer m_fastStatistics; CaretPointer m_histogram; CaretPointer m_histogramLimitedValues; 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 bool m_brickStatisticsValid;//so that setModified() doesn't do something slow /** Fast statistics used when statistics computed on all data in file */ CaretPointer m_fileFastStatistics; /** Histogram used when statistics computed on all data in file */ CaretPointer m_fileHistogram; /** Histogram with limited values used when statistics computed on all data in file */ CaretPointer m_fileHistorgramLimitedValues; float m_fileHistogramLimitedValuesMostPositiveValueInclusive; float m_fileHistogramLimitedValuesLeastPositiveValueInclusive; float m_fileHistogramLimitedValuesLeastNegativeValueInclusive; float m_fileHistogramLimitedValuesMostNegativeValueInclusive; bool m_fileHistogramLimitedValuesIncludeZeroValues; /** Performs coloring of voxels. Will be NULL if coloring is disabled. */ CaretPointer m_voxelColorizer; /** True if the volume is a single slice, needed by interpolateValue() methods */ bool m_singleSliceFlag; mutable CaretMutex m_splineMutex; mutable bool m_splinesValid; mutable std::vector m_frameSplineValid; mutable std::vector m_frameSplines; bool m_chartingEnabledForTab[BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS]; mutable bool m_dataRangeValid; mutable float m_dataRangeMinimum; mutable float m_dataRangeMaximum; /** Holds class and name hierarchy used for display selection */ mutable CaretPointer m_classNameHierarchy; /** force an update of the class and name hierarchy */ mutable bool m_forceUpdateOfGroupAndNameHierarchy; CaretPointer m_volumeFileEditorDelegate; protected: virtual void saveFileDataToScene(const SceneAttributes* sceneAttributes, SceneClass* sceneClass); virtual void restoreFileDataFromScene(const SceneAttributes* sceneAttributes, const SceneClass* sceneClass); public: enum InterpType { ENCLOSING_VOXEL, TRILINEAR, CUBIC }; const static float INVALID_INTERP_VALUE; /** Enables coloring. Coloring is almost always not needed for command line operations */ static bool s_voxelColoringEnabled; static void setVoxelColoringEnabled(const bool enabled); VolumeFile(); VolumeFile(const std::vector& dimensionsIn, const std::vector >& indexToSpace, const int64_t numComponents = 1, SubvolumeAttributes::VolumeType whatType = SubvolumeAttributes::ANATOMY); ~VolumeFile(); virtual void clear(); virtual void addToDataFileContentInformation(DataFileContentInformation& dataFileInformation); ///recreates the volume file storage with new size and spacing void reinitialize(const std::vector& dimensionsIn, const std::vector >& indexToSpace, const int64_t numComponents = 1, SubvolumeAttributes::VolumeType whatType = SubvolumeAttributes::ANATOMY); ///convenient version for 3D or 4D from a VolumeSpace void reinitialize(const VolumeSpace& volSpaceIn, const int64_t numFrames = 1, const int64_t numComponents = 1, SubvolumeAttributes::VolumeType whatType = SubvolumeAttributes::ANATOMY); void addSubvolumes(const int64_t& numToAdd); void setType(SubvolumeAttributes::VolumeType whatType); SubvolumeAttributes::VolumeType getType() const; void validateSpline(const int64_t brickIndex = 0, const int64_t component = 0) const; void freeSpline(const int64_t brickIndex = 0, const int64_t component = 0) const; float interpolateValue(const float* coordIn, InterpType interp = TRILINEAR, bool* validOut = NULL, const int64_t brickIndex = 0, const int64_t component = 0) const; float interpolateValue(const float coordIn1, const float coordIn2, const float coordIn3, InterpType interp = TRILINEAR, bool* validOut = NULL, const int64_t brickIndex = 0, const int64_t component = 0) const; ///returns true if volume space matches in spatial dimensions and sform bool matchesVolumeSpace(const VolumeFile* right) const; ///returns true if volume space matches in spatial dimensions and sform bool matchesVolumeSpace(const VolumeSpace& otherSpace) const; ///returns true if volume space matches in spatial dimensions and sform bool matchesVolumeSpace(const int64_t dims[3], const std::vector >& sform) const; void readFile(const AString& filename); void writeFile(const AString& filename); bool isEmpty() const { return VolumeBase::isEmpty(); } virtual void setModified(); virtual void clearModified(); virtual bool isModifiedExcludingPaletteColorMapping() const; void getVoxelSpaceBoundingBox(BoundingBox& boundingBoxOut) const; /** * @return The structure for this file. */ StructureEnum::Enum getStructure() const; /** * Set the structure for this file. * @param structure * New structure for this file. */ void setStructure(const StructureEnum::Enum structure); /** * @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 /** * @return Get access to unmodifiable file's metadata. */ const GiftiMetaData* getFileMetaData() const { return NULL; } bool isSurfaceMappable() const { return false; } bool isVolumeMappable() const { return true; } int32_t getNumberOfMaps() const { return getDimensionsPtr()[3]; } AString getMapName(const int32_t mapIndex) const; void setMapName(const int32_t mapIndex, const AString& mapName); const GiftiMetaData* getMapMetaData(const int32_t mapIndex) const; GiftiMetaData* getMapMetaData(const int32_t mapIndex); const FastStatistics* getMapFastStatistics(const int32_t mapIndex); const Histogram* getMapHistogram(const int32_t mapIndex); const Histogram* getMapHistogram(const int32_t mapIndex, const float mostPositiveValueInclusive, const float leastPositiveValueInclusive, const float leastNegativeValueInclusive, const float mostNegativeValueInclusive, const bool includeZeroValues); virtual int64_t getDataSizeUncompressedInBytes() const; virtual const FastStatistics* getFileFastStatistics(); virtual const Histogram* getFileHistogram(); virtual const Histogram* getFileHistogram(const float mostPositiveValueInclusive, const float leastPositiveValueInclusive, const float leastNegativeValueInclusive, const float mostNegativeValueInclusive, const bool includeZeroValues); void getFileData(std::vector& dataOut) const; bool isMappedWithPalette() const; virtual void getPaletteNormalizationModesSupported(std::vector& modesSupportedOut); PaletteColorMapping* getMapPaletteColorMapping(const int32_t mapIndex); const PaletteColorMapping* getMapPaletteColorMapping(const int32_t mapIndex) const; bool isMappedWithLabelTable() const; GiftiLabelTable* getMapLabelTable(const int32_t mapIndex); const GiftiLabelTable* getMapLabelTable(const int32_t mapIndex) const; void getVoxelIndicesWithLabelKey(const int32_t mapIndex, const int32_t labelKey, std::vector& voxelIndicesOut) const; std::vector getUniqueLabelKeysUsedInMap(const int32_t mapIndex) const; virtual bool isMappedWithRGBA() const; AString getMapUniqueID(const int32_t mapIndex) const; void updateScalarColoringForMap(const int32_t mapIndex, const PaletteFile* paletteFile); virtual int64_t 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; virtual int64_t getVoxelColorsForSliceInMap(const PaletteFile* paletteFile, const int32_t mapIndex, const VolumeSliceViewPlaneEnum::Enum slicePlane, const int64_t sliceIndex, const DisplayGroupEnum::Enum displayGroup, const int32_t tabIndex, uint8_t* rgbaOut) const; virtual int64_t getVoxelColorsForSubSliceInMap(const PaletteFile* paletteFile, const int32_t mapIndex, const VolumeSliceViewPlaneEnum::Enum slicePlane, const int64_t sliceIndex, const int64_t firstCornerVoxelIndex[3], const int64_t lastCornerVoxelIndex[3], const int64_t voxelCountIJK[3], const DisplayGroupEnum::Enum displayGroup, const int32_t tabIndex, uint8_t* rgbaOut) const; 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, 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; void clearVoxelColoringForMap(const int64_t mapIndex); virtual bool getDataRangeFromAllMaps(float& dataRangeMinimumOut, float& dataRangeMaximumOut) const; GroupAndNameHierarchyModel* getGroupAndNameHierarchyModel(); const GroupAndNameHierarchyModel* getGroupAndNameHierarchyModel() const; VolumeFileEditorDelegate* getVolumeFileEditorDelegate(); virtual bool isLineSeriesChartingEnabled(const int32_t tabIndex) const; virtual void setLineSeriesChartingEnabled(const int32_t tabIndex, const bool enabled); virtual bool isLineSeriesChartingSupported() const; virtual ChartDataCartesian* loadLineSeriesChartDataForSurfaceNode(const StructureEnum::Enum structure, const int32_t nodeIndex); virtual ChartDataCartesian* loadAverageLineSeriesChartDataForSurfaceNodes(const StructureEnum::Enum structure, const std::vector& nodeIndices); virtual ChartDataCartesian* loadLineSeriesChartDataForVoxelAtCoordinate(const float xyz[3]); virtual void getSupportedLineSeriesChartDataTypes(std::vector& chartDataTypesOut) const; }; } #endif //__VOLUME_FILE_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Files/VolumeFileEditorDelegate.cxx000066400000000000000000001265661300200146000272230ustar00rootroot00000000000000 /*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 __VOLUME_FILE_EDITOR_DELEGATE_DECLARE__ #include "VolumeFileEditorDelegate.h" #undef __VOLUME_FILE_EDITOR_DELEGATE_DECLARE__ #include "CaretAssert.h" #include "CaretPointer.h" #include "CaretUndoStack.h" #include "Matrix4x4.h" #include "VolumeFile.h" #include "VolumeMapUndoCommand.h" #include "VoxelIJK.h" using namespace caret; /** * \class caret::VolumeFileEditorDelegate * \brief Delegate for performing editing operations on a volume file's voxels. * \ingroup Files * * Perform interactive editing operations in a GUI on a volume file * including the ability to undo, redo, and reset the editing operations. */ /** * Constructor. * * @param volumeFile * Volume that 'owns' this editor and on which editing is performed. */ VolumeFileEditorDelegate::VolumeFileEditorDelegate(VolumeFile* volumeFile) : CaretObject(), m_volumeFile(volumeFile) { CaretAssert(volumeFile); m_volumeDimensions[0] = 0; m_volumeDimensions[1] = 0; m_volumeDimensions[2] = 0; updateIfVolumeFileChangedNumberOfMaps(); } /** * Destructor. */ VolumeFileEditorDelegate::~VolumeFileEditorDelegate() { clear(); } /** * Clear the instance. */ void VolumeFileEditorDelegate::clear() { /* * The undo stacks are only created when needed so some * entries may be NULL */ for (std::vector::iterator mapIter = m_volumeMapUndoStacks.begin(); mapIter != m_volumeMapUndoStacks.end(); mapIter++) { CaretUndoStack* undoStack = *mapIter; delete undoStack; } m_volumeMapUndoStacks.clear(); m_volumeDimensions[0] = 0; m_volumeDimensions[1] = 0; m_volumeDimensions[2] = 0; m_volumeMapEditingLocked.clear(); } /** * Perform an editing operation. * * @param mapIndex * Index of map (brick) within the volume that is being edited. * @param mode * The editing mode. * @param slicePlane * The selected slice plane. * @param voxelIJK * Indices of voxel selected by the user. * @param brushSize * Size of brush used by some operations. * @param voxelValueOn * Value that is assigned by a "turn on" operation. * @param voxelValueOff * Value that is assigned by a "turn off" operation. * @param errorMessageOut * Will contain error information. * @return * True if successful, else false and errorMessageOut will be set. */ bool VolumeFileEditorDelegate::performEditingOperation(const int64_t mapIndex, const VolumeEditingModeEnum::Enum mode, const VolumeSliceViewPlaneEnum::Enum slicePlane, const VolumeSliceProjectionTypeEnum::Enum sliceProjectionType, const Matrix4x4& obliqueRotationMatrix, const float voxelDiffXYZ[3], const int64_t voxelIJK[3], const int64_t brushSize[3], const float voxelValueOn, const float voxelValueOff, AString& errorMessageOut) { errorMessageOut.clear(); CaretAssert(m_volumeFile); int64_t dimNumComponents = 0; int64_t dimNumMaps = 0; m_volumeFile->getDimensions(m_volumeDimensions[0], m_volumeDimensions[1], m_volumeDimensions[2], dimNumMaps, dimNumComponents); if ((mapIndex < 0) || (mapIndex >= dimNumMaps)) { errorMessageOut = ("Invalid map index=" + AString::number(mapIndex) + ", number of maps=" + AString::number(dimNumMaps)); return false; } if (isLocked(mapIndex)) { errorMessageOut = "Volume must be unlocked (Press \"Lock\" in toolbar) to allow editing."; return false; } switch (sliceProjectionType) { case VolumeSliceProjectionTypeEnum::VOLUME_SLICE_PROJECTION_OBLIQUE: if ( ! VolumeEditingModeEnum::isObliqueEditingAllowed(mode)) { errorMessageOut = (VolumeEditingModeEnum::toGuiName(mode) + " does not support editing voxels when the volume " "is in an oblique view."); return false; } break; case VolumeSliceProjectionTypeEnum::VOLUME_SLICE_PROJECTION_ORTHOGONAL: break; } int64_t iHalf = brushSize[0] / 2; int64_t jHalf = brushSize[1] / 2; int64_t kHalf = brushSize[2] / 2; int64_t ijkMin[3] = { voxelIJK[0] - iHalf, voxelIJK[1] - jHalf, voxelIJK[2] - kHalf }; clampVoxelIndices(ijkMin); int64_t ijkMax[3] = { voxelIJK[0] + iHalf, voxelIJK[1] + jHalf, voxelIJK[2] + kHalf }; clampVoxelIndices(ijkMax); const EditInfo editInfo(mapIndex, mode, slicePlane, sliceProjectionType, obliqueRotationMatrix, voxelDiffXYZ, voxelIJK, ijkMin, ijkMax, brushSize, voxelValueOn, voxelValueOff); bool result = false; switch (mode) { case VolumeEditingModeEnum::VOLUME_EDITING_MODE_ON: case VolumeEditingModeEnum::VOLUME_EDITING_MODE_OFF: switch (sliceProjectionType) { case VolumeSliceProjectionTypeEnum::VOLUME_SLICE_PROJECTION_OBLIQUE: result = performTurnOnOrOffOblique(editInfo, errorMessageOut); break; case VolumeSliceProjectionTypeEnum::VOLUME_SLICE_PROJECTION_ORTHOGONAL: result = performTurnOnOrOffOrthogonal(editInfo, errorMessageOut); break; } break; case VolumeEditingModeEnum::VOLUME_EDITING_MODE_DILATE: result = performDilateOrErode(editInfo, errorMessageOut); break; case VolumeEditingModeEnum::VOLUME_EDITING_MODE_ERODE: result = performDilateOrErode(editInfo, errorMessageOut); break; case VolumeEditingModeEnum::VOLUME_EDITING_MODE_FLOOD_FILL_2D: result = performFloodFill2D(editInfo, errorMessageOut); break; case VolumeEditingModeEnum::VOLUME_EDITING_MODE_FLOOD_FILL_3D: result = performFloodFill3D(editInfo, errorMessageOut); break; case VolumeEditingModeEnum::VOLUME_EDITING_MODE_REMOVE_CONNECTED_2D: result = performRemoveConnected2D(editInfo, errorMessageOut); break; case VolumeEditingModeEnum::VOLUME_EDITING_MODE_REMOVE_CONNECTED_3D: result = performRemoveConnected3D(editInfo, errorMessageOut); break; case VolumeEditingModeEnum::VOLUME_EDITING_MODE_RETAIN_CONNECTED_3D: result = performRetainConnected3D(editInfo, errorMessageOut); break; } return result; } /** * @return Is the volume file locked (does not allow editing)? * * @param mapIndex * Index of map that is tested for locked. */ bool VolumeFileEditorDelegate::isLocked(const int64_t mapIndex) const { CaretAssertVectorIndex(m_volumeMapEditingLocked, mapIndex); return m_volumeMapEditingLocked[mapIndex]; } /** * Set the volume file's lock status (does not allow editing). * * If the locked status transitions to 'locked', * then CLEAR THE UNDO STACK since by locking, * the user is satisfied with changes made to the volume. * * * @param mapIndex * Index of map that has lock status set. * @param locked * New locked status. */ void VolumeFileEditorDelegate::setLocked(const int64_t mapIndex, const bool locked) { CaretAssertVectorIndex(m_volumeMapEditingLocked, mapIndex); if (locked != m_volumeMapEditingLocked[mapIndex]) { m_volumeMapEditingLocked[mapIndex] = locked; if (m_volumeMapEditingLocked[mapIndex]) { CaretAssertVectorIndex(m_volumeMapUndoStacks, mapIndex); m_volumeMapUndoStacks[mapIndex]->clear(); } } } /** * Adjust the voxel indices so that they are within the volume. * * @param ijk * Voxel indices that are adjusted to be in the range * 0 to dimension minus one. */ void VolumeFileEditorDelegate::clampVoxelIndices(int64_t ijk[3]) const { for (int32_t i = 0; i < 3; i++) { ijk[i] = clampDimensionIndex(m_volumeDimensions[i], ijk[i]); } } /** * Clamp a voxel dimensions index (zero to dim-1) * * @param maxDim * Maximum dimension value. * @param dimIndex * Value that is clamped. */ int64_t VolumeFileEditorDelegate::clampDimensionIndex(const int64_t maxDim, int64_t dimIndex) const { if (dimIndex < 0) { dimIndex = 0; } else if (dimIndex >= maxDim) { dimIndex = maxDim - 1; } return dimIndex; } /** * Clamp voxel indices. * * @param i * Index for dimension 0. * @param j * Index for dimension 1. * @param k * Index for dimension 2. */ void VolumeFileEditorDelegate::clampVoxelIndices(int64_t& i, int64_t& j, int64_t& k) const { i = clampDimensionIndex(m_volumeDimensions[0], i); j = clampDimensionIndex(m_volumeDimensions[1], j); k = clampDimensionIndex(m_volumeDimensions[2], k); } /** * Add to the modified undo stacks for the given map. * * @param mapIndex * Index of the map that was modified. * @param modifiedVoxels * Voxels that were modified. */ void VolumeFileEditorDelegate::addToMapUndoStacks(const int32_t mapIndex, VolumeMapUndoCommand* modifiedVoxels) { if (modifiedVoxels->count() <= 0) { delete modifiedVoxels; return; } CaretAssertVectorIndex(m_volumeMapUndoStacks, mapIndex); m_volumeMapUndoStacks[mapIndex]->push(modifiedVoxels); } /** * Update in case number of maps in volume file has changed. */ void VolumeFileEditorDelegate::updateIfVolumeFileChangedNumberOfMaps() { const int32_t oldNumMaps = static_cast(m_volumeMapUndoStacks.size()); const int32_t numMaps = m_volumeFile->getNumberOfMaps(); const int32_t numMapsToAdd = numMaps - oldNumMaps; if (numMapsToAdd > 0) { for (int32_t i = 0; i < numMapsToAdd; i++) { m_volumeMapUndoStacks.push_back(new CaretUndoStack()); m_volumeMapEditingLocked.push_back(true); } } CaretAssert(static_cast(m_volumeMapUndoStacks.size()) == numMaps); CaretAssert(static_cast(m_volumeMapEditingLocked.size()) == numMaps); } /** * Undo the last voxel editing operation for the given map index. * * @param mapIndex * Index of map that has last voxel operation 'undone'. * @param errorMessageOut * Output containing error message. * @return * True if the redo executed successfully, else false. */ bool VolumeFileEditorDelegate::undo(const int64_t mapIndex, AString& errorMessageOut) { CaretAssertVectorIndex(m_volumeMapUndoStacks, mapIndex); return m_volumeMapUndoStacks[mapIndex]->undo(errorMessageOut); } /** * Reset all voxel editing since last lock. * * Note that when the lock status transitions to lock, * the undo stack is cleared. * * @param mapIndex * Index of map that has last voxel operation 'undone'. * @param errorMessageOut * Output containing error message. * @return * True if the redo executed successfully, else false. */ bool VolumeFileEditorDelegate::reset(const int64_t mapIndex, AString& errorMessageOut) { CaretAssertVectorIndex(m_volumeMapUndoStacks, mapIndex); return m_volumeMapUndoStacks[mapIndex]->undoAll(errorMessageOut); } /** * Redo the last voxel editing operation for the given map index. * * @param mapIndex * Index of map that has last voxel operation 'redone'. * @param errorMessageOut * Output containing error message. * @return * True if the redo executed successfully, else false. */ bool VolumeFileEditorDelegate::redo(const int64_t mapIndex, AString& errorMessageOut) { CaretAssertVectorIndex(m_volumeMapUndoStacks, mapIndex); return m_volumeMapUndoStacks[mapIndex]->redo(errorMessageOut); } /** * Perform an editing operation that turns voxels on or off * for orthogonal slice viewing. * * @param editInfo * The editing information. * @param errorMessageOut * Will contain error information. * @return * True if there was an error, else false. */ bool VolumeFileEditorDelegate::performTurnOnOrOffOrthogonal(const EditInfo& editInfo, AString& errorMessageOut) { float redoVoxelValue = 0.0; switch (editInfo.m_mode){ case VolumeEditingModeEnum::VOLUME_EDITING_MODE_OFF: redoVoxelValue = editInfo.m_voxelValueOff; break; case VolumeEditingModeEnum::VOLUME_EDITING_MODE_ON: redoVoxelValue = editInfo.m_voxelValueOn; break; case VolumeEditingModeEnum::VOLUME_EDITING_MODE_DILATE: case VolumeEditingModeEnum::VOLUME_EDITING_MODE_ERODE: case VolumeEditingModeEnum::VOLUME_EDITING_MODE_FLOOD_FILL_2D: case VolumeEditingModeEnum::VOLUME_EDITING_MODE_FLOOD_FILL_3D: case VolumeEditingModeEnum::VOLUME_EDITING_MODE_REMOVE_CONNECTED_2D: case VolumeEditingModeEnum::VOLUME_EDITING_MODE_REMOVE_CONNECTED_3D: case VolumeEditingModeEnum::VOLUME_EDITING_MODE_RETAIN_CONNECTED_3D: CaretAssert(0); errorMessageOut = "Program error in performTurnOnOrOff but mode not valid."; return false; break; } CaretPointer modifiedVoxels; modifiedVoxels.grabNew(new VolumeMapUndoCommand(m_volumeFile, editInfo.m_mapIndex)); for (int64_t i = editInfo.m_ijkMin[0]; i <= editInfo.m_ijkMax[0]; i++) { for (int64_t j = editInfo.m_ijkMin[1]; j <= editInfo.m_ijkMax[1]; j++) { for (int64_t k = editInfo.m_ijkMin[2]; k <= editInfo.m_ijkMax[2]; k++) { const int64_t ijk[3] = { i, j, k }; modifiedVoxels->addVoxelRedoUndo(ijk, redoVoxelValue, m_volumeFile->getValue(ijk, editInfo.m_mapIndex)); m_volumeFile->setValue(redoVoxelValue, ijk, editInfo.m_mapIndex); } } } addToMapUndoStacks(editInfo.m_mapIndex, modifiedVoxels.releasePointer()); return true; } /** * Perform an editing operation that turns voxels on or off * for oblique slice viewing. * * @param editInfo * The editing information. * @param errorMessageOut * Will contain error information. * @return * True if there was an error, else false. */ bool VolumeFileEditorDelegate::performTurnOnOrOffOblique(const EditInfo& editInfo, AString& errorMessageOut) { float redoVoxelValue = 0.0; switch (editInfo.m_mode){ case VolumeEditingModeEnum::VOLUME_EDITING_MODE_OFF: redoVoxelValue = editInfo.m_voxelValueOff; break; case VolumeEditingModeEnum::VOLUME_EDITING_MODE_ON: redoVoxelValue = editInfo.m_voxelValueOn; break; case VolumeEditingModeEnum::VOLUME_EDITING_MODE_DILATE: case VolumeEditingModeEnum::VOLUME_EDITING_MODE_ERODE: case VolumeEditingModeEnum::VOLUME_EDITING_MODE_FLOOD_FILL_2D: case VolumeEditingModeEnum::VOLUME_EDITING_MODE_FLOOD_FILL_3D: case VolumeEditingModeEnum::VOLUME_EDITING_MODE_REMOVE_CONNECTED_2D: case VolumeEditingModeEnum::VOLUME_EDITING_MODE_REMOVE_CONNECTED_3D: case VolumeEditingModeEnum::VOLUME_EDITING_MODE_RETAIN_CONNECTED_3D: CaretAssert(0); errorMessageOut = "Program error in performTurnOnOrOff but mode not valid."; return false; break; } // float voxelDiffXYZ[3]; float voxelXYZ[3]; m_volumeFile->indexToSpace(editInfo.m_voxelIJK, voxelXYZ); // const int64_t planeVoxelsDI = (editInfo.m_ijkMax[0] - editInfo.m_ijkMin[0]) / 2 ; // const int64_t planeVoxelsDJ = (editInfo.m_ijkMax[1] - editInfo.m_ijkMin[1]) / 2 ; // const int64_t planeVoxelsDK = (editInfo.m_ijkMax[2] - editInfo.m_ijkMin[2]) / 2 ; CaretPointer modifiedVoxels; modifiedVoxels.grabNew(new VolumeMapUndoCommand(m_volumeFile, editInfo.m_mapIndex)); // const float planeZ = voxelXYZ[2]; // for (int64_t i = -planeVoxelsDI; i <= planeVoxelsDI; i++) { // const float planeX = voxelXYZ[0] + i * editInfo.m_voxelDiffXYZ[0]; // for (int64_t j = -planeVoxelsDJ; j <= planeVoxelsDJ; j++) { // const float planeY = voxelXYZ[1] + j * editInfo.m_voxelDiffXYZ[1]; // const float xyz[3] = { planeX, planeY, planeZ }; // float ijkFloat[3]; // m_volumeFile->spaceToIndex(xyz, ijkFloat); // int64_t ijk[3] = { ijkFloat[0], ijkFloat[1], ijkFloat[2] }; // modifiedVoxels->addVoxelRedoUndo(ijk, // redoVoxelValue, // m_volumeFile->getValue(ijk, editInfo.m_mapIndex)); // m_volumeFile->setValue(redoVoxelValue, // ijk, // editInfo.m_mapIndex); // } // } const int64_t halfBrushI = editInfo.m_brushSize[0] / 2; const int64_t halfBrushJ = editInfo.m_brushSize[1] / 2; const int64_t halfBrushK = editInfo.m_brushSize[2] / 2; VolumeSpace::OrientTypes orient[3]; float spacing[3]; float origin[3]; m_volumeFile->getVolumeSpace().getOrientAndSpacingForPlumb(orient, spacing, origin); for (int64_t k = -halfBrushK; k <= halfBrushK; k++) { for (int64_t i = -halfBrushI; i <= halfBrushI; i++) { for (int64_t j = -halfBrushJ; j <= halfBrushJ; j++) { float localXYZ[3] = { i * spacing[0], j * spacing[1], k * spacing[2] }; editInfo.m_obliqueRotationMatrix.multiplyPoint3(localXYZ); float brushXYZ[3] = { voxelXYZ[0] + localXYZ[0], voxelXYZ[1] + localXYZ[1], voxelXYZ[2] + localXYZ[2] }; float ijkFloat[3]; m_volumeFile->spaceToIndex(brushXYZ, ijkFloat); int64_t ijk[3] = { ijkFloat[0], ijkFloat[1], ijkFloat[2] }; modifiedVoxels->addVoxelRedoUndo(ijk, redoVoxelValue, m_volumeFile->getValue(ijk, editInfo.m_mapIndex)); m_volumeFile->setValue(redoVoxelValue, ijk, editInfo.m_mapIndex); } } } // for (int64_t k = -planeVoxelsDZ; k <= planeVoxelsDZ; ++k) { // // } // CaretPointer modifiedVoxels; // modifiedVoxels.grabNew(new VolumeMapUndoCommand(m_volumeFile, // editInfo.m_mapIndex)); // for (int64_t i = editInfo.m_ijkMin[0]; i <= editInfo.m_ijkMax[0]; i++) { // for (int64_t j = editInfo.m_ijkMin[1]; j <= editInfo.m_ijkMax[1]; j++) { // for (int64_t k = editInfo.m_ijkMin[2]; k <= editInfo.m_ijkMax[2]; k++) { // const int64_t ijk[3] = { i, j, k }; // modifiedVoxels->addVoxelRedoUndo(ijk, // redoVoxelValue, // m_volumeFile->getValue(ijk, editInfo.m_mapIndex)); // m_volumeFile->setValue(redoVoxelValue, // ijk, // editInfo.m_mapIndex); // } // } // } addToMapUndoStacks(editInfo.m_mapIndex, modifiedVoxels.releasePointer()); return true; } /** * Perform an editing operation that dilates voxels * connected to the selected voxel. * * @param editInfo * The editing information. * @param errorMessageOut * Will contain error information. * @return * True if there was an error, else false. */ bool VolumeFileEditorDelegate::performDilateOrErode(const EditInfo& editInfo, AString& errorMessageOut) { bool dilateFlag = false; switch (editInfo.m_mode){ case VolumeEditingModeEnum::VOLUME_EDITING_MODE_DILATE: dilateFlag = true; break; case VolumeEditingModeEnum::VOLUME_EDITING_MODE_ERODE: dilateFlag = false; break; case VolumeEditingModeEnum::VOLUME_EDITING_MODE_FLOOD_FILL_2D: case VolumeEditingModeEnum::VOLUME_EDITING_MODE_FLOOD_FILL_3D: case VolumeEditingModeEnum::VOLUME_EDITING_MODE_OFF: case VolumeEditingModeEnum::VOLUME_EDITING_MODE_ON: case VolumeEditingModeEnum::VOLUME_EDITING_MODE_REMOVE_CONNECTED_2D: case VolumeEditingModeEnum::VOLUME_EDITING_MODE_REMOVE_CONNECTED_3D: case VolumeEditingModeEnum::VOLUME_EDITING_MODE_RETAIN_CONNECTED_3D: CaretAssert(0); errorMessageOut = "Program error in dilate/erode but mode not valid."; return false; break; } const int64_t STRUCTURE_ELEMENT_SIZE = 1; CaretPointer modifiedVoxels; modifiedVoxels.grabNew(new VolumeMapUndoCommand(m_volumeFile, editInfo.m_mapIndex)); /* * Check each voxel in the desired region */ for (int64_t i = editInfo.m_ijkMin[0]; i <= editInfo.m_ijkMax[0]; i++) { for (int64_t j = editInfo.m_ijkMin[1]; j <= editInfo.m_ijkMax[1]; j++) { for (int64_t k = editInfo.m_ijkMin[2]; k <= editInfo.m_ijkMax[2]; k++) { const int64_t ijk[3] = { i, j, k }; /* * Get the value of the voxel */ float value = m_volumeFile->getValue(ijk, editInfo.m_mapIndex); bool voxelMatches = false; /* * If eroding, look for voxels with "turn on" value */ if (! dilateFlag) { if (value == editInfo.m_voxelValueOn) { voxelMatches = true; } } /* * If dilating, look for "OFF" voxels */ if (dilateFlag) { if (value == editInfo.m_voxelValueOff) { voxelMatches = true; } } /* * Should we continue processing this voxel */ if (voxelMatches) { /* * Create Structuring Element based upon the axis */ int64_t iMin = ijk[0]; int64_t iMax = ijk[0]; int64_t jMin = ijk[1]; int64_t jMax = ijk[1]; int64_t kMin = ijk[2]; int64_t kMax = ijk[2]; switch (editInfo.m_slicePlane) { case VolumeSliceViewPlaneEnum::ALL: CaretAssert(0); errorMessageOut = "Program Error: Cannot edit an ALL slice"; return false; break; case VolumeSliceViewPlaneEnum::AXIAL: iMin -= STRUCTURE_ELEMENT_SIZE; iMax += STRUCTURE_ELEMENT_SIZE; jMin -= STRUCTURE_ELEMENT_SIZE; jMax += STRUCTURE_ELEMENT_SIZE; break; case VolumeSliceViewPlaneEnum::CORONAL: iMin -= STRUCTURE_ELEMENT_SIZE; iMax += STRUCTURE_ELEMENT_SIZE; kMin -= STRUCTURE_ELEMENT_SIZE; kMax += STRUCTURE_ELEMENT_SIZE; break; case VolumeSliceViewPlaneEnum::PARASAGITTAL: jMin -= STRUCTURE_ELEMENT_SIZE; jMax += STRUCTURE_ELEMENT_SIZE; kMin -= STRUCTURE_ELEMENT_SIZE; kMax += STRUCTURE_ELEMENT_SIZE; break; } clampVoxelIndices(iMin, jMin, kMin); clampVoxelIndices(iMax, jMax, kMax); /* * Check all voxels "under" the structuring element */ bool foundVoxelFlag = false; for (int64_t ii = iMin; ii <= iMax; ii++) { for (int64_t jj = jMin; jj <= jMax; jj++) { for (int64_t kk = kMin; kk <= kMax; kk++) { /* * Ignore the voxel under the center of the * structuring element */ if ((ii != i) || (jj != j) || (kk != k)) { /* * Make sure voxel is valid since structuring element * may exceed bounds of the volume */ const int64_t iijjkk[3] = { ii, jj, kk }; float value = m_volumeFile->getValue(iijjkk); /* * If dilating, look for voxels that are the * turn on value under the structuring * element. */ if (dilateFlag) { if (value == editInfo.m_voxelValueOn) { foundVoxelFlag = true; break; } } /* * If eroding look for voxels that are "OFF" * under the structuring element */ if ( ! dilateFlag) { if (value != editInfo.m_voxelValueOn) { foundVoxelFlag = true; break; } } } } if (foundVoxelFlag) { break; } } if (foundVoxelFlag) { break; } } if (foundVoxelFlag) { /* * For now, just note which voxels need to be set since * we do not want to modify the volume until after all voxels * under structuring element have been checked. */ if (dilateFlag) { modifiedVoxels->addVoxelRedoUndo(ijk, editInfo.m_voxelValueOn, m_volumeFile->getValue(ijk, editInfo.m_mapIndex)); } else { modifiedVoxels->addVoxelRedoUndo(ijk, editInfo.m_voxelValueOff, m_volumeFile->getValue(ijk, editInfo.m_mapIndex)); } } } } } } /* * Calling 'redo' will apply the changes to the volume file. */ const bool validFlag = modifiedVoxels->redo(errorMessageOut); addToMapUndoStacks(editInfo.m_mapIndex, modifiedVoxels.releasePointer()); return validFlag; } /** * Perform an editing operation that flood fills the region * containing the selected voxel in the selected slice. * * @param editInfo * The editing information. * @param errorMessageOut * Will contain error information. * @return * True if there was an error, else false. */ bool VolumeFileEditorDelegate::performFloodFill2D(const EditInfo& editInfo, AString& errorMessageOut) { return performFloodFillAndRemoveConnected(editInfo, errorMessageOut); } /** * Perform an editing operation that flood fills the region * containing the selected voxel in all dimensions. * * @param editInfo * The editing information. * @param errorMessageOut * Will contain error information. * @return * True if there was an error, else false. */ bool VolumeFileEditorDelegate::performFloodFill3D(const EditInfo& editInfo, AString& errorMessageOut) { return performFloodFillAndRemoveConnected(editInfo, errorMessageOut); } /** * Perform an editing operation that removes all voxels connected * to the selected voxel in the slice. * * @param editInfo * The editing information. * @param errorMessageOut * Will contain error information. * @return * True if there was an error, else false. */ bool VolumeFileEditorDelegate::performRemoveConnected2D(const EditInfo& editInfo, AString& errorMessageOut) { return performFloodFillAndRemoveConnected(editInfo, errorMessageOut); } /** * Perform an editing operation that removes all voxels connected * to the selected voxel in all dimensions. * * @param editInfo * The editing information. * @param errorMessageOut * Will contain error information. * @return * True if there was an error, else false. */ bool VolumeFileEditorDelegate::performRemoveConnected3D(const EditInfo& editInfo, AString& errorMessageOut) { return performFloodFillAndRemoveConnected(editInfo, errorMessageOut); } /** * Perform an editing operation that rmeoves all voxels that * are not connected to the selected voxel. * * @param editInfo * The editing information. * @param errorMessageOut * Will contain error information. * @return * True if there was an error, else false. */ bool VolumeFileEditorDelegate::performRetainConnected3D(const EditInfo& editInfo, AString& errorMessageOut) { if (m_volumeFile->getValue(editInfo.m_voxelIJK, editInfo.m_mapIndex) == editInfo.m_voxelValueOff) { errorMessageOut = "Voxel value is zero or the unassigned label."; return false; } CaretPointer modifiedVoxels; modifiedVoxels.grabNew(new VolumeMapUndoCommand(m_volumeFile, editInfo.m_mapIndex)); /* * Tracks visited voxels */ const int64_t numVoxels = (m_volumeDimensions[0] * m_volumeDimensions[1] * m_volumeDimensions[2]); std::vector visitedVoxelFlags(numVoxels, false); /* * Tracks voxels that are connected */ std::vector connectedVoxelFlags(numVoxels, false); /* * Initialize to the staring voxel */ std::stack st; st.push(VoxelIJK(editInfo.m_voxelIJK)); /* * While there are voxels to process */ while (st.empty() == false) { /* * Get the next voxel to process */ const VoxelIJK v = st.top(); st.pop(); const int64_t visitedFlagsOffset = (v.m_ijk[0] + (v.m_ijk[1] * (m_volumeDimensions[0])) + (v.m_ijk[2] * m_volumeDimensions[0] * m_volumeDimensions[1])); CaretAssertVectorIndex(visitedVoxelFlags, visitedFlagsOffset); if (visitedVoxelFlags[visitedFlagsOffset]) { continue; } visitedVoxelFlags[visitedFlagsOffset] = true; if (m_volumeFile->getValue(v.m_ijk, editInfo.m_mapIndex) == editInfo.m_voxelValueOff) { continue; } connectedVoxelFlags[visitedFlagsOffset] = true; int64_t ijkMin[3] = { v.m_ijk[0] - 1, v.m_ijk[1] - 1, v.m_ijk[2] - 1 }; clampVoxelIndices(ijkMin); int64_t ijkMax[3] = { v.m_ijk[0] + 1, v.m_ijk[1] + 1, v.m_ijk[2] + 1 }; clampVoxelIndices(ijkMax); /* * Add neighbors to search */ for (int64_t i = ijkMin[0]; i <= ijkMax[0]; i++) { for (int64_t j = ijkMin[1]; j <= ijkMax[1]; j++) { for (int64_t k = ijkMin[2]; k <= ijkMax[2]; k++) { const int64_t flagsOffset = (i + (j * (m_volumeDimensions[0])) + (k * m_volumeDimensions[0] * m_volumeDimensions[1])); if (visitedVoxelFlags[flagsOffset]) { continue; } if (m_volumeFile->getValue(i, j, k, editInfo.m_mapIndex) != editInfo.m_voxelValueOff) { st.push(VoxelIJK(i, j, k)); } } } } } /* * Turn off not connected voxels */ for (int64_t i = 0; i < m_volumeDimensions[0]; i++) { for (int64_t j = 0; j < m_volumeDimensions[1]; j++) { for (int64_t k = 0; k < m_volumeDimensions[2]; k++) { const int64_t flagsOffset = (i + (j * (m_volumeDimensions[0])) + (k * m_volumeDimensions[0] * m_volumeDimensions[1])); if ( ! connectedVoxelFlags[flagsOffset]) { modifiedVoxels->addVoxelRedoUndo(i, j, k, editInfo.m_voxelValueOff, m_volumeFile->getValue(i, j, k, editInfo.m_mapIndex)); m_volumeFile->setValue(editInfo.m_voxelValueOff, i, j, k, editInfo.m_mapIndex); } } } } addToMapUndoStacks(editInfo.m_mapIndex, modifiedVoxels.releasePointer()); return true; } /** * Perform an editing operation that rmeoves all voxels that * are not connected to the selected voxel. * * @param editInfo * The editing information. * @param errorMessageOut * Will contain error information. * @return * True if there was an error, else false. */ bool VolumeFileEditorDelegate::performFloodFillAndRemoveConnected(const EditInfo& editInfo, AString& errorMessageOut) { bool fillingFlag = false; bool threeDimensionalFlag = false; switch (editInfo.m_mode){ case VolumeEditingModeEnum::VOLUME_EDITING_MODE_DILATE: case VolumeEditingModeEnum::VOLUME_EDITING_MODE_ERODE: case VolumeEditingModeEnum::VOLUME_EDITING_MODE_OFF: case VolumeEditingModeEnum::VOLUME_EDITING_MODE_ON: case VolumeEditingModeEnum::VOLUME_EDITING_MODE_RETAIN_CONNECTED_3D: CaretAssert(0); errorMessageOut = "Program error in performFloodFillAndRemoveConnected but mode not valid."; return false; break; case VolumeEditingModeEnum::VOLUME_EDITING_MODE_FLOOD_FILL_2D: fillingFlag = true; break; case VolumeEditingModeEnum::VOLUME_EDITING_MODE_FLOOD_FILL_3D: fillingFlag = true; threeDimensionalFlag = true; break; case VolumeEditingModeEnum::VOLUME_EDITING_MODE_REMOVE_CONNECTED_2D: break; case VolumeEditingModeEnum::VOLUME_EDITING_MODE_REMOVE_CONNECTED_3D: threeDimensionalFlag = true; break; } CaretPointer modifiedVoxels; modifiedVoxels.grabNew(new VolumeMapUndoCommand(m_volumeFile, editInfo.m_mapIndex)); float newVoxelValue = editInfo.m_voxelValueOff; if (fillingFlag) { newVoxelValue = editInfo.m_voxelValueOn; } /* * Initialize to the staring voxel */ std::stack st; st.push(VoxelIJK(editInfo.m_voxelIJK)); /* * While there are voxels to process */ while (st.empty() == false) { /* * Get the next voxel to process */ const VoxelIJK v = st.top(); st.pop(); int64_t i = v.m_ijk[0]; int64_t j = v.m_ijk[1]; int64_t k = v.m_ijk[2]; /* * If the voxel has valid indices */ if ((i >= 0) && (i < m_volumeDimensions[0]) && (j >= 0) && (j < m_volumeDimensions[1]) && (k >= 0) && (k < m_volumeDimensions[2])) { const int64_t ijk[3] = { i, j, k }; float currentValue = m_volumeFile->getValue(ijk, editInfo.m_mapIndex); /* * See if voxel has proper value for operation */ bool matchingVoxel = false; if (fillingFlag) { matchingVoxel = (currentValue == editInfo.m_voxelValueOff); } else { matchingVoxel = (currentValue == editInfo.m_voxelValueOn); } /* * If the voxel should be modified */ if (matchingVoxel) { /* * Update the voxels value */ modifiedVoxels->addVoxelRedoUndo(ijk, newVoxelValue, m_volumeFile->getValue(ijk, editInfo.m_mapIndex)); m_volumeFile->setValue(newVoxelValue, ijk, editInfo.m_mapIndex); /* * Determine neighboring voxels */ int64_t iDelta = 0; int64_t jDelta = 0; int64_t kDelta = 0; switch (editInfo.m_slicePlane) { case VolumeSliceViewPlaneEnum::PARASAGITTAL: if (threeDimensionalFlag) { iDelta = 1; } else { iDelta = 0; } jDelta = 1; kDelta = 1; break; case VolumeSliceViewPlaneEnum::CORONAL: iDelta = 1; if (threeDimensionalFlag) { jDelta = 1; } else { jDelta = 0; } kDelta = 1; break; case VolumeSliceViewPlaneEnum::AXIAL: iDelta = 1; jDelta = 1; if (threeDimensionalFlag) { kDelta = 1; } else { kDelta = 0; } break; case VolumeSliceViewPlaneEnum::ALL: break; } /* * Add neighboring voxels for search */ if (iDelta != 0) { st.push(VoxelIJK(i - iDelta, j, k)); st.push(VoxelIJK(i + iDelta, j, k)); } if (jDelta != 0) { st.push(VoxelIJK(i, j - jDelta, k)); st.push(VoxelIJK(i, j + jDelta, k)); } if (kDelta != 0) { st.push(VoxelIJK(i, j, k - kDelta)); st.push(VoxelIJK(i, j, k + kDelta)); } } } } addToMapUndoStacks(editInfo.m_mapIndex, modifiedVoxels.releasePointer()); return true; } connectome-workbench-1.2.3+git41-gc4c6c90/src/Files/VolumeFileEditorDelegate.h000066400000000000000000000167511300200146000266420ustar00rootroot00000000000000#ifndef __VOLUME_FILE_EDITOR_DELEGATE_H__ #define __VOLUME_FILE_EDITOR_DELEGATE_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 "Matrix4x4.h" #include "VolumeEditingModeEnum.h" #include "VolumeSliceProjectionTypeEnum.h" #include "VolumeSliceViewPlaneEnum.h" namespace caret { class CaretUndoStack; class VolumeFile; class VolumeMapUndoCommand; class VolumeFileEditorDelegate : public CaretObject { public: VolumeFileEditorDelegate(VolumeFile* volumeFile); virtual ~VolumeFileEditorDelegate(); void clear(); void updateIfVolumeFileChangedNumberOfMaps(); bool performEditingOperation(const int64_t mapIndex, const VolumeEditingModeEnum::Enum mode, const VolumeSliceViewPlaneEnum::Enum slicePlane, const VolumeSliceProjectionTypeEnum::Enum sliceProjectionType, const Matrix4x4& obliqueRotationMatrix, const float voxelDiffXYZ[3], const int64_t voxelIJK[3], const int64_t brushSize[3], const float voxelValueOn, const float voxelValueOff, AString& errorMessageOut); bool undo(const int64_t mapIndex, AString& errorMessageOut); bool reset(const int64_t mapIndex, AString& errorMessageOut); bool redo(const int64_t mapIndex, AString& errorMessageOut); bool isLocked(const int64_t mapIndex) const; void setLocked(const int64_t mapIndex, const bool locked); // ADD_NEW_METHODS_HERE private: class EditInfo { public: EditInfo(const int32_t mapIndex, const VolumeEditingModeEnum::Enum mode, const VolumeSliceViewPlaneEnum::Enum slicePlane, const VolumeSliceProjectionTypeEnum::Enum sliceProjectionType, const Matrix4x4& obliqueRotationMatrix, const float voxelDiffXYZ[3], const int64_t voxelIJK[3], const int64_t ijkMin[3], const int64_t ijkMax[3], const int64_t brushSize[3], const float voxelValueOn, const float voxelValueOff) : m_mapIndex(mapIndex), m_mode(mode), m_slicePlane(slicePlane), m_sliceProjectionType(sliceProjectionType), m_obliqueRotationMatrix(obliqueRotationMatrix), m_voxelValueOn(voxelValueOn), m_voxelValueOff(voxelValueOff) { m_voxelDiffXYZ[0] = voxelDiffXYZ[0]; m_voxelDiffXYZ[1] = voxelDiffXYZ[1]; m_voxelDiffXYZ[2] = voxelDiffXYZ[2]; m_voxelIJK[0] = voxelIJK[0]; m_voxelIJK[1] = voxelIJK[1]; m_voxelIJK[2] = voxelIJK[2]; m_brushSize[0] = brushSize[0]; m_brushSize[1] = brushSize[1]; m_brushSize[2] = brushSize[2]; m_ijkMin[0] = ijkMin[0]; m_ijkMin[1] = ijkMin[1]; m_ijkMin[2] = ijkMin[2]; m_ijkMax[0] = ijkMax[0]; m_ijkMax[1] = ijkMax[1]; m_ijkMax[2] = ijkMax[2]; } const int32_t m_mapIndex; const VolumeEditingModeEnum::Enum m_mode; const VolumeSliceViewPlaneEnum::Enum m_slicePlane; const VolumeSliceProjectionTypeEnum::Enum m_sliceProjectionType; const Matrix4x4 m_obliqueRotationMatrix; float m_voxelDiffXYZ[3]; int64_t m_voxelIJK[3]; int64_t m_ijkMin[3]; int64_t m_ijkMax[3]; int64_t m_brushSize[3]; const float m_voxelValueOn; const float m_voxelValueOff; }; VolumeFileEditorDelegate(const VolumeFileEditorDelegate&); VolumeFileEditorDelegate& operator=(const VolumeFileEditorDelegate&); bool performTurnOnOrOffOblique(const EditInfo& editInfo, AString& errorMessageOut); bool performTurnOnOrOffOrthogonal(const EditInfo& editInfo, AString& errorMessageOut); bool performDilateOrErode(const EditInfo& editInfo, AString& errorMessageOut); bool performFloodFill2D(const EditInfo& editInfo, AString& errorMessageOut); bool performFloodFill3D(const EditInfo& editInfo, AString& errorMessageOut); bool performRemoveConnected2D(const EditInfo& editInfo, AString& errorMessageOut); bool performRemoveConnected3D(const EditInfo& editInfo, AString& errorMessageOut); bool performRetainConnected3D(const EditInfo& editInfo, AString& errorMessageOut); bool performFloodFillAndRemoveConnected(const EditInfo& editInfo, AString& errorMessageOut); int64_t clampDimensionIndex(const int64_t maxDim, int64_t dimIndex) const; void clampVoxelIndices(int64_t ijk[3]) const; void clampVoxelIndices(int64_t& i, int64_t& j, int64_t& k) const; void addToMapUndoStacks(const int32_t mapIndex, VolumeMapUndoCommand* modifiedVoxels); VolumeFile* m_volumeFile; /** * Holds modifications for undo/redo operations. * Index into vector is the map index. */ std::vector m_volumeMapUndoStacks; /** * IJK dimensions of the volume. */ int64_t m_volumeDimensions[3]; /** * A "lock" to prevent editing of a volume's map. */ std::vector m_volumeMapEditingLocked; // ADD_NEW_MEMBERS_HERE }; #ifdef __VOLUME_FILE_EDITOR_DELEGATE_DECLARE__ // #endif // __VOLUME_FILE_EDITOR_DELEGATE_DECLARE__ } // namespace #endif //__VOLUME_FILE_EDITOR_DELEGATE_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Files/VolumeFileVoxelColorizer.cxx000066400000000000000000000637761300200146000273330ustar00rootroot00000000000000 /*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 __VOLUME_FILE_VOXEL_COLORIZER_DECLARE__ #include "VolumeFileVoxelColorizer.h" #undef __VOLUME_FILE_VOXEL_COLORIZER_DECLARE__ #include "CaretAssert.h" #include "CaretLogger.h" #include "ElapsedTimer.h" #include "GiftiLabel.h" #include "GroupAndNameHierarchyItem.h" #include "NodeAndVoxelColoring.h" #include "VolumeFile.h" #include using namespace caret; /** * \class caret::VolumeFileVoxelColorizer * \brief Delegate for coloring a volumes voxels. */ /** * Constructor. * * @param volumeFile * Volume file on which this instance colors voxels. */ VolumeFileVoxelColorizer::VolumeFileVoxelColorizer(VolumeFile* volumeFile) : CaretObject() { CaretAssert(volumeFile); m_volumeFile = volumeFile; int64_t dimNumberOfComponents; m_volumeFile->getDimensions(m_dimI, m_dimJ, m_dimK, m_mapCount, dimNumberOfComponents); m_voxelCountPerMap = m_dimI * m_dimJ * m_dimK; m_mapRGBACount = m_voxelCountPerMap * 4; for (int64_t i = 0; i < m_mapCount; i++) { m_mapRGBA.push_back(new uint8_t[m_mapRGBACount]); m_mapColoringValid.push_back(false); } } /** * Destructor. */ VolumeFileVoxelColorizer::~VolumeFileVoxelColorizer() { for (int64_t i = 0; i < m_mapCount; i++) { delete[] m_mapRGBA[i]; } m_mapRGBA.clear(); } /** * Assign voxel coloring for a map. * * @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*/) { CaretAssertVectorIndex(m_mapRGBA, mapIndex); ElapsedTimer timer; timer.start(); /* * Pointer to map's data */ const float* mapDataPointer = m_volumeFile->getFrame(mapIndex); /* * Get access to threshold data */ //float* thresholdDataPointer = NULL; bool ignoreThresholding = true; if (thresholdVolume != NULL) { int64_t threshI, threshJ, threshK, threshMapCount, threshNumberOfComponents; thresholdVolume->getDimensions(threshI, threshJ, threshK, threshMapCount, threshNumberOfComponents); if ((threshI != m_dimI) || (threshJ != m_dimJ) || (threshK != m_dimK)) { CaretLogSevere("Threshold volume (" + thresholdVolume->getFileNameNoPath() + ") dimensions do not match " + m_volumeFile->getFileNameNoPath()); } 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; } } switch (m_volumeFile->getType()) { case SubvolumeAttributes::UNKNOWN: case SubvolumeAttributes::ANATOMY: case SubvolumeAttributes::FUNCTIONAL: { CaretAssert(palette); FastStatistics* statistics = NULL; switch (m_volumeFile->getPaletteNormalizationMode()) { case PaletteNormalizationModeEnum::NORMALIZATION_ALL_MAP_DATA: statistics = const_cast(m_volumeFile->getFileFastStatistics()); break; case PaletteNormalizationModeEnum::NORMALIZATION_SELECTED_MAP_DATA: statistics = const_cast(m_volumeFile->getMapFastStatistics(mapIndex)); break; } CaretAssert(statistics); NodeAndVoxelColoring::colorScalarsWithPalette(statistics, //m_volumeFile->getMapFastStatistics(mapIndex), m_volumeFile->getMapPaletteColorMapping(mapIndex), palette, mapDataPointer, mapDataPointer, m_voxelCountPerMap, m_mapRGBA[mapIndex], ignoreThresholding); m_mapColoringValid[mapIndex] = true; } break; case SubvolumeAttributes::LABEL: if (m_voxelCountPerMap > 0) { NodeAndVoxelColoring::colorIndicesWithLabelTable(m_volumeFile->getMapLabelTable(mapIndex), &mapDataPointer[0], m_voxelCountPerMap, m_mapRGBA[mapIndex]); m_mapColoringValid[mapIndex] = true; } break; case SubvolumeAttributes::RGB: { const uint8_t thresholdRGB[3] = { 5, 5, 5 }; const int32_t numberOfComponents = m_volumeFile->getNumberOfComponents(); if ((numberOfComponents == 3) || (numberOfComponents == 4)) { const float* alphaComponents = ((numberOfComponents == 4) ? m_volumeFile->getFrame(mapIndex, 3) : NULL); NodeAndVoxelColoring::colorScalarsWithRGBA(m_volumeFile->getFrame(mapIndex, 0), m_volumeFile->getFrame(mapIndex, 1), m_volumeFile->getFrame(mapIndex, 2), alphaComponents, m_voxelCountPerMap, thresholdRGB, m_mapRGBA[mapIndex]); m_mapColoringValid[mapIndex] = true; } else { CaretLogSevere("An RGB/RGBA volume must contain 3 or 4 components per voxel: " + m_volumeFile->getFileNameNoPath()); } } break; case SubvolumeAttributes::SEGMENTATION: break; case SubvolumeAttributes::VECTOR: break; } CaretLogFine("Time to color map named \"" + m_volumeFile->getMapName(mapIndex) + " in volume file " + m_volumeFile->getFileNameNoPath() + " was " + AString::number(timer.getElapsedTimeMilliseconds()) + " milliseconds"); } /** * Invalidate the RGBA coloring for all maps. */ void VolumeFileVoxelColorizer::invalidateColoring() { std::fill(m_mapColoringValid.begin(), m_mapColoringValid.end(), false); } /** * Get voxel coloring for a slice in a map. If voxel coloring is not ready * (it may be running in a different thread) this method will wait until the * coloring is valid prior to returning the slice's coloring. * * @param mapIndex * Index of map. * @param slicePlane * Plane of the slice. * @param sliceIndex * Index of the slice. * @param displayGroup * The selected display group. * @param tabIndex * Index of selected tab. * @param rgbaOut * RGBA color components out. * @return * Number of voxels with alpha greater than zero */ int64_t VolumeFileVoxelColorizer::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 { CaretAssertVectorIndex(m_mapRGBA, mapIndex); CaretAssert(sliceIndex >= 0); CaretAssert(rgbaOut); int64_t iStart = 0; int64_t iEnd = m_dimI - 1; int64_t jStart = 0; int64_t jEnd = m_dimJ - 1; int64_t kStart = 0; int64_t kEnd = m_dimK - 1; switch (slicePlane) { case VolumeSliceViewPlaneEnum::ALL: CaretAssert(0); break; case VolumeSliceViewPlaneEnum::AXIAL: kStart = sliceIndex; kEnd = sliceIndex; break; case VolumeSliceViewPlaneEnum::CORONAL: jStart = sliceIndex; jEnd = sliceIndex; break; case VolumeSliceViewPlaneEnum::PARASAGITTAL: iStart = sliceIndex; iEnd = sliceIndex; break; } /* * Pointer to maps RGBA values */ const uint8_t* mapRGBA = m_mapRGBA[mapIndex]; const GiftiLabelTable* labelTable = (m_volumeFile->isMappedWithLabelTable() ? m_volumeFile->getMapLabelTable(mapIndex) : NULL); if (m_volumeFile->isMappedWithLabelTable()) { CaretAssert(labelTable); } int64_t validVoxelCount = 0; /* * Output RGBA values for slice */ int64_t rgbaOutIndex = 0; for (int64_t k = kStart; k <= kEnd; k++) { for (int64_t j = jStart; j <= jEnd; j++) { for (int64_t i = iStart; i <= iEnd; i++) { const int64_t rgbaOffset = getRgbaOffsetForVoxelIndex(i, j, k); CaretAssertArrayIndex(mapRGBA, m_mapRGBACount, rgbaOffset); rgbaOut[rgbaOutIndex] = mapRGBA[rgbaOffset]; rgbaOut[rgbaOutIndex+1] = mapRGBA[rgbaOffset+1]; rgbaOut[rgbaOutIndex+2] = mapRGBA[rgbaOffset+2]; uint8_t alpha = mapRGBA[rgbaOffset+3]; if (alpha > 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. */ const int32_t dataValue = static_cast(m_volumeFile->getValue(i, j, k, mapIndex)); const GiftiLabel* label = labelTable->getLabel(dataValue); if (label != NULL) { const GroupAndNameHierarchyItem* item = label->getGroupNameSelectionItem(); if (item != NULL) { if (item->isSelected(displayGroup, tabIndex) == false) { alpha = 0; } } } } } if (alpha > 0.0) { ++validVoxelCount; } rgbaOut[rgbaOutIndex+3] = alpha; rgbaOutIndex += 4; } } } return validVoxelCount; } /** * Get voxel coloring for a set of voxels. * * @param mapIndex * Index of map. * @param firstVoxelIJK * IJK Indices of first voxel * @param rowStepIJK * IJK Step for moving to next row. * @param columnStepIJK * IJK Step for moving to next column. * @param numberOfRows * Number of rows. * @param numberOfColumns * Number of columns. * @param displayGroup * The selected display group. * @param tabIndex * Index of selected tab. * @param rgbaOut * RGBA color components out. * @return * Number of voxels with alpha greater than zero */ int64_t VolumeFileVoxelColorizer::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 { /* * Pointer to maps RGBA values */ const uint8_t* mapRGBA = m_mapRGBA[mapIndex]; const GiftiLabelTable* labelTable = (m_volumeFile->isMappedWithLabelTable() ? m_volumeFile->getMapLabelTable(mapIndex) : NULL); if (m_volumeFile->isMappedWithLabelTable()) { CaretAssert(labelTable); } int64_t validVoxelCount = 0; int64_t rgbaOutIndex = 0; int64_t rowIJK[3] = { firstVoxelIJK[0], firstVoxelIJK[1], firstVoxelIJK[2] }; for (int64_t iRow = 0; iRow < numberOfRows; iRow++) { int64_t ijk[3] = { rowIJK[0], rowIJK[1], rowIJK[2] }; for (int64_t iCol = 0; iCol < numberOfColumns; iCol++) { const int64_t rgbaOffset = getRgbaOffsetForVoxelIndex(ijk); CaretAssertArrayIndex(mapRGBA, m_mapRGBACount, rgbaOffset); rgbaOut[rgbaOutIndex] = mapRGBA[rgbaOffset]; rgbaOut[rgbaOutIndex+1] = mapRGBA[rgbaOffset+1]; rgbaOut[rgbaOutIndex+2] = mapRGBA[rgbaOffset+2]; uint8_t alpha = mapRGBA[rgbaOffset+3]; if (alpha > 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. */ const int32_t dataValue = static_cast(m_volumeFile->getValue(ijk, mapIndex)); const GiftiLabel* label = labelTable->getLabel(dataValue); if (label != NULL) { const GroupAndNameHierarchyItem* item = label->getGroupNameSelectionItem(); if (item != NULL) { if (item->isSelected(displayGroup, tabIndex) == false) { alpha = 0; } } } } } if (alpha > 0.0) { ++validVoxelCount; } rgbaOut[rgbaOutIndex+3] = alpha; rgbaOutIndex += 4; 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 voxel coloring for a sub-slice in a map. If voxel coloring is not ready * (it may be running in a different thread) this method will wait until the * coloring is valid prior to returning the slice's coloring. * * @param mapIndex * Index of map. * @param slicePlane * Plane of the slice. * @param sliceIndex * Index of the slice. * @param firstCornerVoxelIndex * Indices of voxel for first corner of sub-slice (inclusive). * @param lastCornerVoxelIndex * Indices of voxel for last corner of sub-slice (inclusive). * @param voxelCountIJK * Voxel counts for each axis. * @param displayGroup * The selected display group. * @param tabIndex * Index of selected tab. * @param rgbaOut * RGBA color components out. * @return * Number of voxels with alpha greater than zero */ int64_t VolumeFileVoxelColorizer::getVoxelColorsForSubSliceInMap(const int32_t mapIndex, const VolumeSliceViewPlaneEnum::Enum slicePlane, const int64_t sliceIndex, const int64_t firstCornerVoxelIndex[3], const int64_t lastCornerVoxelIndex[3], const int64_t voxelCountIJK[3], const DisplayGroupEnum::Enum displayGroup, const int32_t tabIndex, uint8_t* rgbaOut) const { CaretAssertVectorIndex(m_mapRGBA, mapIndex); CaretAssert(sliceIndex >= 0); CaretAssert(rgbaOut); VolumeSpace::OrientTypes orient[3]; m_volumeFile->getOrientation(orient); int orient2dim[3]; int64_t incrementijk[3]; for (int i = 0; i < 3; ++i) { incrementijk[i] = (lastCornerVoxelIndex[i] > firstCornerVoxelIndex[i]) ? 1 : -1; switch (orient[i])//easier to read than indexing by (orient[i] & 3) { case VolumeSpace::LEFT_TO_RIGHT: case VolumeSpace::RIGHT_TO_LEFT: orient2dim[0] = i; break; case VolumeSpace::POSTERIOR_TO_ANTERIOR: case VolumeSpace::ANTERIOR_TO_POSTERIOR: orient2dim[1] = i; break; case VolumeSpace::INFERIOR_TO_SUPERIOR: case VolumeSpace::SUPERIOR_TO_INFERIOR: orient2dim[2] = i; break; } } int outerLoop = -1, innerLoop = -1; int64_t iterijk[3]; switch (slicePlane) { case VolumeSliceViewPlaneEnum::PARASAGITTAL: outerLoop = orient2dim[2]; innerLoop = orient2dim[1]; iterijk[orient2dim[0]] = sliceIndex; break; case VolumeSliceViewPlaneEnum::CORONAL: outerLoop = orient2dim[2]; innerLoop = orient2dim[0]; iterijk[orient2dim[1]] = sliceIndex; break; case VolumeSliceViewPlaneEnum::AXIAL: outerLoop = orient2dim[1]; innerLoop = orient2dim[0]; iterijk[orient2dim[2]] = sliceIndex; break; default: CaretAssert(false); } const int64_t voxelCount = (voxelCountIJK[0] * voxelCountIJK[1] * voxelCountIJK[2]); const int64_t rgbaCount = voxelCount * 4; /* * Pointer to maps RGBA values */ const uint8_t* mapRGBA = m_mapRGBA[mapIndex]; const GiftiLabelTable* labelTable = (m_volumeFile->isMappedWithLabelTable() ? m_volumeFile->getMapLabelTable(mapIndex) : NULL); if (m_volumeFile->isMappedWithLabelTable()) { CaretAssert(labelTable); } int64_t validVoxelCount = 0; int64_t innerCount = std::abs(lastCornerVoxelIndex[innerLoop] - firstCornerVoxelIndex[innerLoop]) + 1;//to check validity of index int64_t rgbaOutIndex = 0; for (iterijk[outerLoop] = firstCornerVoxelIndex[outerLoop]; iterijk[outerLoop] != lastCornerVoxelIndex[outerLoop] + incrementijk[outerLoop]; iterijk[outerLoop] += incrementijk[outerLoop]) { for (iterijk[innerLoop] = firstCornerVoxelIndex[innerLoop]; iterijk[innerLoop] != lastCornerVoxelIndex[innerLoop] + incrementijk[innerLoop]; iterijk[innerLoop] += incrementijk[innerLoop]) { CaretAssert(rgbaOutIndex == 4 * (innerCount * std::abs(iterijk[outerLoop] - firstCornerVoxelIndex[outerLoop]) + std::abs(iterijk[innerLoop] - firstCornerVoxelIndex[innerLoop]))); CaretAssertArrayIndex(rgbaOut, rgbaCount, rgbaOutIndex + 3); const int64_t rgbaOffset = getRgbaOffsetForVoxelIndex(iterijk[0], iterijk[1], iterijk[2]); CaretAssertArrayIndex(mapRGBA, m_mapRGBACount, rgbaOffset); rgbaOut[rgbaOutIndex] = mapRGBA[rgbaOffset]; rgbaOut[rgbaOutIndex+1] = mapRGBA[rgbaOffset+1]; rgbaOut[rgbaOutIndex+2] = mapRGBA[rgbaOffset+2]; uint8_t alpha = mapRGBA[rgbaOffset+3]; if (alpha > 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. const int32_t dataValue = static_cast(m_volumeFile->getValue(iterijk, mapIndex)); const GiftiLabel* label = labelTable->getLabel(dataValue); if (label != NULL) { const GroupAndNameHierarchyItem* item = label->getGroupNameSelectionItem(); if (item != NULL) { if (item->isSelected(displayGroup, tabIndex) == false) { alpha = 0; } } } } } if (alpha > 0.0) { ++validVoxelCount; } rgbaOut[rgbaOutIndex+3] = alpha; rgbaOutIndex += 4; } } return validVoxelCount; } /** * Get the RGBA color components for voxel. * * @param i * Parasaggital index * @param j * Coronal index * @param k * Axial index * @param mapIndex * Index of map. * @param displayGroup * The selected display group. * @param tabIndex * Index of selected tab. * @param rgbaOut * Contains voxel coloring on exit. */ void VolumeFileVoxelColorizer::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 { /* * Pointer to maps RGBA values */ CaretAssertVectorIndex(m_mapRGBA, mapIndex); const uint8_t* mapRGBA = m_mapRGBA[mapIndex]; const int64_t rgbaOffset = getRgbaOffsetForVoxelIndex(i, j, k); CaretAssertArrayIndex(mapRGBA, m_mapRGBACount, rgbaOffset); rgbaOut[0] = mapRGBA[rgbaOffset]; rgbaOut[1] = mapRGBA[rgbaOffset+1]; rgbaOut[2] = mapRGBA[rgbaOffset+2]; uint8_t alpha = mapRGBA[rgbaOffset+3]; if (alpha > 0) { if (m_volumeFile->isMappedWithLabelTable()) { const GiftiLabelTable* labelTable = m_volumeFile->getMapLabelTable(mapIndex); CaretAssert(labelTable); /* * For label data, verify that the label is displayed. * If NOT displayed, zero out the alpha value to * prevent display of the data. */ const int32_t dataValue = static_cast(m_volumeFile->getValue(i, j, k, mapIndex)); const GiftiLabel* label = labelTable->getLabel(dataValue); if (label != NULL) { const GroupAndNameHierarchyItem* item = label->getGroupNameSelectionItem(); if (item != NULL) { if (item->isSelected(displayGroup, tabIndex) == false) { alpha = 0; } } } } } rgbaOut[3] = alpha; } /** * Clear the voxel coloring for the given map. * @param mapIndex * Index of map. */ void VolumeFileVoxelColorizer::clearVoxelColoringForMap(const int64_t mapIndex) { CaretAssertVectorIndex(m_mapRGBA, mapIndex); uint8_t* mapRGBA = m_mapRGBA[mapIndex]; for (int64_t i = 0; i < m_mapRGBACount; i++) { mapRGBA[i] = 0.0; } CaretAssertVectorIndex(m_mapColoringValid, mapIndex); m_mapColoringValid[mapIndex] = false; } connectome-workbench-1.2.3+git41-gc4c6c90/src/Files/VolumeFileVoxelColorizer.h000066400000000000000000000123431300200146000267400ustar00rootroot00000000000000#ifndef __VOLUME_FILE_VOXEL_COLORIZER_H__ #define __VOLUME_FILE_VOXEL_COLORIZER_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 "DisplayGroupEnum.h" #include "VolumeSliceViewPlaneEnum.h" namespace caret { class Palette; class VolumeFile; class VolumeFileVoxelColorizer : public CaretObject { public: VolumeFileVoxelColorizer(VolumeFile* volumeFile); virtual ~VolumeFileVoxelColorizer(); void assignVoxelColorsForMap(const int32_t mapIndex, const Palette* palette, const VolumeFile* thresholdVolume, const int32_t thresholdVolumeMapIndex); int64_t 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; 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; int64_t getVoxelColorsForSubSliceInMap(const int32_t mapIndex, const VolumeSliceViewPlaneEnum::Enum slicePlane, const int64_t sliceIndex, const int64_t firstCornerVoxelIndex[3], const int64_t lastCornerVoxelIndex[3], const int64_t voxelCountIJK[3], const DisplayGroupEnum::Enum displayGroup, const int32_t tabIndex, uint8_t* rgbaOut) const; 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; void clearVoxelColoringForMap(const int64_t mapIndex); void invalidateColoring(); private: VolumeFileVoxelColorizer(const VolumeFileVoxelColorizer&); VolumeFileVoxelColorizer& operator=(const VolumeFileVoxelColorizer&); /** * Get theRGBA offset for a voxel index */ inline int64_t getRgbaOffsetForVoxelIndex(const int64_t i, const int64_t j, const int64_t k) const { return (4 * (i + (j * m_dimI) + ((k * m_dimI * m_dimJ)))); } /** * Get theRGBA offset for a voxel index */ inline int64_t getRgbaOffsetForVoxelIndex(const int64_t ijk[3]) const { return (4 * (ijk[0] + (ijk[1] * m_dimI) + ((ijk[2] * m_dimI * m_dimJ)))); } // ADD_NEW_MEMBERS_HERE VolumeFile* m_volumeFile; int64_t m_dimI; int64_t m_dimJ; int64_t m_dimK; int64_t m_voxelCountPerMap; int64_t m_mapCount; int64_t m_mapRGBACount; std::vector m_mapColoringValid; std::vector m_mapRGBA; }; #ifdef __VOLUME_FILE_VOXEL_COLORIZER_DECLARE__ // #endif // __VOLUME_FILE_VOXEL_COLORIZER_DECLARE__ } // namespace #endif //__VOLUME_FILE_VOXEL_COLORIZER_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Files/VolumeMapUndoCommand.cxx000066400000000000000000000115631300200146000263720ustar00rootroot00000000000000 /*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 __VOLUME_MAP_UNDO_COMMAND_DECLARE__ #include "VolumeMapUndoCommand.h" #undef __VOLUME_MAP_UNDO_COMMAND_DECLARE__ #include "CaretAssert.h" #include "VolumeFile.h" using namespace caret; /** * \class caret::VolumeMapUndoCommand * \brief Command pattern for volume map modifications that undo and redo. * \ingroup Files */ /** * Constructor. */ VolumeMapUndoCommand::VolumeMapUndoCommand(VolumeFile* volumeFile, const int32_t mapIndex) : CaretUndoCommand(), m_volumeFile(volumeFile), m_mapIndex(mapIndex) { CaretAssert(volumeFile); CaretAssert((mapIndex >= 0) && (mapIndex < volumeFile->getNumberOfMaps())); } /** * Destructor. */ VolumeMapUndoCommand::~VolumeMapUndoCommand() { for (std::vector::iterator iter = m_voxelMementos.begin(); iter != m_voxelMementos.end(); iter++) { delete *iter; } m_voxelMementos.clear(); } /** * Operation that "redoes" the command. * * @param errorMessageOut * Output containing error message. * @return * True if the command executed successfully, else false. */ bool VolumeMapUndoCommand::redo(AString& errorMessageOut) { errorMessageOut.clear(); for (std::vector::iterator iter = m_voxelMementos.begin(); iter != m_voxelMementos.end(); iter++) { const VoxelMemento* voxelMod = *iter; m_volumeFile->setValue(voxelMod->m_redoValue, voxelMod->m_ijk, m_mapIndex); } return true; } /** * Operation that "undoes" the command. */ bool VolumeMapUndoCommand::undo(AString& errorMessageOut) { errorMessageOut.clear(); for (std::vector::iterator iter = m_voxelMementos.begin(); iter != m_voxelMementos.end(); iter++) { const VoxelMemento* voxelMod = *iter; m_volumeFile->setValue(voxelMod->m_undoValue, voxelMod->m_ijk, m_mapIndex); } return true; } /** * @return Number of modified voxels. */ int32_t VolumeMapUndoCommand::count() const { return m_voxelMementos.size(); } /** * Add the redo and undo values for a voxel. * * @param ijk * The voxel's indices. * @param redoValue * Value for redo operation. * @param undoValue * Value for undo operation. */ void VolumeMapUndoCommand::addVoxelRedoUndo(const int64_t ijk[3], const float redoValue, const float undoValue) { m_voxelMementos.push_back(new VoxelMemento(ijk, redoValue, undoValue)); } /** * Add the redo and undo values for a voxel. * * @param i * The voxel's "i" index. * @param j * The voxel's "j" index. * @param k * The voxel's "k" index. * @param redoValue * Value for redo operation. * @param undoValue * Value for undo operation. */ void VolumeMapUndoCommand::addVoxelRedoUndo(const int64_t i, const int64_t j, const int64_t k, const float redoValue, const float undoValue) { const int64_t ijk[3] = { i, j, k }; addVoxelRedoUndo(ijk, redoValue, undoValue); } /* ------------------------------------------------------------------ */ /** * Constructor. * * @param ijk * The voxel's indices. * @param redoValue * Value for redo operation. * @param undoValue * Value for undo operation. */ VolumeMapUndoCommand::VoxelMemento::VoxelMemento(const int64_t ijk[3], const float redoValue, const float undoValue) { m_ijk[0] = ijk[0]; m_ijk[1] = ijk[1]; m_ijk[2] = ijk[2]; m_redoValue = redoValue; m_undoValue = undoValue; } VolumeMapUndoCommand::VoxelMemento::~VoxelMemento() { } connectome-workbench-1.2.3+git41-gc4c6c90/src/Files/VolumeMapUndoCommand.h000066400000000000000000000053401300200146000260130ustar00rootroot00000000000000#ifndef __VOLUME_MAP_UNDO_COMMAND_H__ #define __VOLUME_MAP_UNDO_COMMAND_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 "CaretUndoCommand.h" namespace caret { class VolumeFile; class VolumeMapUndoCommand : public CaretUndoCommand { public: VolumeMapUndoCommand(VolumeFile* volumeFile, const int32_t mapIndex); virtual ~VolumeMapUndoCommand(); virtual bool redo(AString& errorMessageOut); virtual bool undo(AString& errorMessageOut); int32_t count() const; void addVoxelRedoUndo(const int64_t ijk[3], const float redoValue, const float undoValue); void addVoxelRedoUndo(const int64_t i, const int64_t j, const int64_t k, const float redoValue, const float undoValue); // ADD_NEW_METHODS_HERE private: class VoxelMemento { public: VoxelMemento(const int64_t ijk[3], const float redoValue, const float undoValue); ~VoxelMemento(); int64_t m_ijk[3]; float m_redoValue; float m_undoValue; }; VolumeMapUndoCommand(const VolumeMapUndoCommand&); VolumeMapUndoCommand& operator=(const VolumeMapUndoCommand&); VolumeFile* m_volumeFile; const int32_t m_mapIndex; std::vector m_voxelMementos; // ADD_NEW_MEMBERS_HERE }; #ifdef __VOLUME_MAP_UNDO_COMMAND_DECLARE__ // #endif // __VOLUME_MAP_UNDO_COMMAND_DECLARE__ } // namespace #endif //__VOLUME_MAP_UNDO_COMMAND_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Files/VolumePaddingHelper.cxx000066400000000000000000000150431300200146000262330ustar00rootroot00000000000000/*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 "VolumePaddingHelper.h" #include "CaretAssert.h" #include "CaretException.h" #include "FloatMatrix.h" #include "GiftiLabelTable.h" #include "PaletteColorMapping.h" #include "Vector3D.h" #include "VolumeFile.h" #include using namespace caret; using namespace std; VolumePaddingHelper VolumePaddingHelper::padVoxels(const VolumeFile* orig, const int& ipad, const int& jpad, const int& kpad) { VolumePaddingHelper ret; orig->getDimensions(ret.m_origDims); ret.m_ipad = ipad; ret.m_jpad = jpad; ret.m_kpad = kpad; ret.m_origDims.resize(3);//we only care about spatial dimensions ret.m_paddedDims = ret.m_origDims; ret.m_paddedDims[0] += ipad * 2; ret.m_paddedDims[1] += jpad * 2; ret.m_paddedDims[2] += kpad * 2; ret.m_origSform = orig->getSform(); FloatMatrix origSpace(ret.m_origSform); FloatMatrix padSpace = origSpace; for (int i = 0; i < origSpace.getNumberOfRows(); ++i) { padSpace[i][3] -= ipad * origSpace[i][0] + jpad * origSpace[i][1] + kpad * origSpace[i][2]; } ret.m_paddedSform = padSpace.getMatrix(); return ret; } VolumePaddingHelper VolumePaddingHelper::padMM(const VolumeFile* orig, const float& mmpad) { vector > volSpace = orig->getSform(); Vector3D ivec, jvec, kvec, origin, ijorth, jkorth, kiorth; FloatMatrix(volSpace).getAffineVectors(ivec, jvec, kvec, origin); ijorth = ivec.cross(jvec).normal();//conceptually put a sphere on each corner of the volume, find how many voxels are needed to fully enclose them jkorth = jvec.cross(kvec).normal(); kiorth = kvec.cross(ivec).normal(); int ipad = (int)floor(abs(mmpad / ivec.dot(jkorth))) + 1; int jpad = (int)floor(abs(mmpad / jvec.dot(kiorth))) + 1; int kpad = (int)floor(abs(mmpad / kvec.dot(ijorth))) + 1; return padVoxels(orig, ipad, jpad, kpad); } void VolumePaddingHelper::doPadding(const VolumeFile* orig, VolumeFile* padded, const float& padval) { CaretAssert(padded != orig); if (!orig->matchesVolumeSpace(m_origDims.data(), m_origSform)) throw CaretException("attempted to pad a volume that doesn't match the one initialized with"); vector newdims = m_paddedDims, curdims = orig->getOriginalDimensions(); while (newdims.size() < curdims.size()) newdims.push_back(curdims[newdims.size()]);//add the nonspatial dimensions from orig padded->reinitialize(newdims, m_paddedSform, orig->getNumberOfComponents(), orig->getType()); vector padframe(m_paddedDims[0] * m_paddedDims[1] * m_paddedDims[2], padval); vector loopdims; orig->getDimensions(loopdims); for (int c = 0; c < loopdims[4]; ++c) { for (int s = 0; s < loopdims[3]; ++s) { if (c == 0) { if (orig->getType() == SubvolumeAttributes::LABEL) { *(padded->getMapLabelTable(s)) = *(orig->getMapLabelTable(s)); } else { *(padded->getMapPaletteColorMapping(s)) = *(orig->getMapPaletteColorMapping(s)); } padded->setMapName(s, orig->getMapName(s)); } int64_t ijk[3], inIndex = 0;//we scan the frame linearly, so we can do this const float* inFrame = orig->getFrame(s, c); for (ijk[2] = 0; ijk[2] < m_origDims[2]; ++ijk[2]) { for (ijk[1] = 0; ijk[1] < m_origDims[1]; ++ijk[1]) { for (ijk[0] = 0; ijk[0] < m_origDims[0]; ++ijk[0]) { int64_t outIndex = padded->getIndex(ijk[0] + m_ipad, ijk[1] + m_jpad, ijk[2] + m_kpad); padframe[outIndex] = inFrame[inIndex];//I could use pointer math instead, but that is needlessly obtuse ++inIndex; } } } padded->setFrame(padframe.data(), s, c); } } } void VolumePaddingHelper::undoPadding(const VolumeFile* padded, VolumeFile* orig) { CaretAssert(orig != padded); if (!padded->matchesVolumeSpace(m_paddedDims.data(), m_paddedSform)) throw CaretException("attempted to unpad a volume that doesn't match padding"); vector newdims = m_origDims, curdims = padded->getOriginalDimensions(); while (newdims.size() < curdims.size()) newdims.push_back(curdims[newdims.size()]);//add the nonspatial dimensions from padded orig->reinitialize(newdims, m_origSform, padded->getNumberOfComponents(), padded->getType()); vector unpadframe(m_origDims[0] * m_origDims[1] * m_origDims[2]); vector loopdims; padded->getDimensions(loopdims); for (int c = 0; c < loopdims[4]; ++c) { for (int s = 0; s < loopdims[3]; ++s) { if (c == 0) { if (padded->getType() == SubvolumeAttributes::LABEL) { *(orig->getMapLabelTable(s)) = *(padded->getMapLabelTable(s)); } else { *(orig->getMapPaletteColorMapping(s)) = *(padded->getMapPaletteColorMapping(s)); } orig->setMapName(s, padded->getMapName(s)); } int64_t ijk[3], outIndex = 0;//we scan the frame linearly, so we can do this for (ijk[2] = 0; ijk[2] < m_origDims[2]; ++ijk[2]) { for (ijk[1] = 0; ijk[1] < m_origDims[1]; ++ijk[1]) { for (ijk[0] = 0; ijk[0] < m_origDims[0]; ++ijk[0]) { unpadframe[outIndex] = padded->getValue(ijk[0] + m_ipad, ijk[1] + m_jpad, ijk[2] + m_kpad, s, c); ++outIndex; } } } orig->setFrame(unpadframe.data(), s, c); } } } connectome-workbench-1.2.3+git41-gc4c6c90/src/Files/VolumePaddingHelper.h000066400000000000000000000032471300200146000256630ustar00rootroot00000000000000#ifndef __VOLUME_PADDING_HELPER_H__ #define __VOLUME_PADDING_HELPER_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 "stdint.h" #include namespace caret { class VolumeFile; class VolumePaddingHelper { std::vector > m_origSform, m_paddedSform; std::vector m_origDims, m_paddedDims; int64_t m_ipad, m_jpad, m_kpad;//hopefully apple doesn't sue us public: VolumePaddingHelper() { } static VolumePaddingHelper padMM(const VolumeFile* orig, const float& mmpad); static VolumePaddingHelper padVoxels(const VolumeFile* orig, const int& ipad, const int& jpad, const int& kpad); void doPadding(const VolumeFile* orig, VolumeFile* padded, const float& padval = 0.0f); void undoPadding(const VolumeFile* padded, VolumeFile* orig); }; } #endif //__VOLUME_PADDING_HELPER_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Files/VolumeSliceProjectionTypeEnum.cxx000066400000000000000000000261411300200146000303110ustar00rootroot00000000000000 /*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 __VOLUME_SLICE_PROJECTION_TYPE_ENUM_DECLARE__ #include "VolumeSliceProjectionTypeEnum.h" #undef __VOLUME_SLICE_PROJECTION_TYPE_ENUM_DECLARE__ #include "CaretAssert.h" using namespace caret; /** * \class caret::VolumeSliceProjectionTypeEnum * \brief Type of projection for drawing a volume slice * * Draw volume slice with an oblique or orthogonal projection * * 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_volumeSliceProjectionTypeEnumComboBox; * * Declare a slot that is called when user changes selection * private slots: * void volumeSliceProjectionTypeEnumComboBoxItemActivated(); * * Implementation File (.cxx) * Include the header files * #include "EnumComboBoxTemplate.h" * #include "VolumeSliceProjectionTypeEnum.h" * * Instatiate: * m_volumeSliceProjectionTypeEnumComboBox = new EnumComboBoxTemplate(this); * m_volumeSliceProjectionTypeEnumComboBox->setup(); * * Get notified when the user changes the selection: * QObject::connect(m_volumeSliceProjectionTypeEnumComboBox, SIGNAL(itemActivated()), * this, SLOT(volumeSliceProjectionTypeEnumComboBoxItemActivated())); * * Update the selection: * m_volumeSliceProjectionTypeEnumComboBox->setSelectedItem(NEW_VALUE); * * Read the selection: * const VolumeSliceProjectionTypeEnum::Enum VARIABLE = m_volumeSliceProjectionTypeEnumComboBox->getSelectedItem(); * */ /** * Constructor. * * @param enumValue * An enumerated value. * @param name * Name of enumerated value. * * @param guiName * User-friendly name for use in user-interface. */ VolumeSliceProjectionTypeEnum::VolumeSliceProjectionTypeEnum(const Enum enumValue, const AString& name, const AString& guiName) { this->enumValue = enumValue; this->integerCode = integerCodeCounter++; this->name = name; this->guiName = guiName; } /** * Destructor. */ VolumeSliceProjectionTypeEnum::~VolumeSliceProjectionTypeEnum() { } /** * Initialize the enumerated metadata. */ void VolumeSliceProjectionTypeEnum::initialize() { if (initializedFlag) { return; } initializedFlag = true; enumData.push_back(VolumeSliceProjectionTypeEnum(VOLUME_SLICE_PROJECTION_OBLIQUE, "VOLUME_SLICE_PROJECTION_OBLIQUE", "Oblique")); enumData.push_back(VolumeSliceProjectionTypeEnum(VOLUME_SLICE_PROJECTION_ORTHOGONAL, "VOLUME_SLICE_PROJECTION_ORTHOGONAL", "Orthogonal")); } /** * 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 VolumeSliceProjectionTypeEnum* VolumeSliceProjectionTypeEnum::findData(const Enum enumValue) { if (initializedFlag == false) initialize(); size_t num = enumData.size(); for (size_t i = 0; i < num; i++) { const VolumeSliceProjectionTypeEnum* 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 VolumeSliceProjectionTypeEnum::toName(Enum enumValue) { if (initializedFlag == false) initialize(); const VolumeSliceProjectionTypeEnum* 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. */ VolumeSliceProjectionTypeEnum::Enum VolumeSliceProjectionTypeEnum::fromName(const AString& name, bool* isValidOut) { if (initializedFlag == false) initialize(); bool validFlag = false; Enum enumValue = VolumeSliceProjectionTypeEnum::enumData[0].enumValue; for (std::vector::iterator iter = enumData.begin(); iter != enumData.end(); iter++) { const VolumeSliceProjectionTypeEnum& 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 VolumeSliceProjectionTypeEnum")); } return enumValue; } /** * Get a GUI string representation of the enumerated type. * @param enumValue * Enumerated value. * @return * String representing enumerated value. */ AString VolumeSliceProjectionTypeEnum::toGuiName(Enum enumValue) { if (initializedFlag == false) initialize(); const VolumeSliceProjectionTypeEnum* 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. */ VolumeSliceProjectionTypeEnum::Enum VolumeSliceProjectionTypeEnum::fromGuiName(const AString& guiName, bool* isValidOut) { if (initializedFlag == false) initialize(); bool validFlag = false; Enum enumValue = VolumeSliceProjectionTypeEnum::enumData[0].enumValue; for (std::vector::iterator iter = enumData.begin(); iter != enumData.end(); iter++) { const VolumeSliceProjectionTypeEnum& 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 VolumeSliceProjectionTypeEnum")); } return enumValue; } /** * Get the integer code for a data type. * * @return * Integer code for data type. */ int32_t VolumeSliceProjectionTypeEnum::toIntegerCode(Enum enumValue) { if (initializedFlag == false) initialize(); const VolumeSliceProjectionTypeEnum* 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. */ VolumeSliceProjectionTypeEnum::Enum VolumeSliceProjectionTypeEnum::fromIntegerCode(const int32_t integerCode, bool* isValidOut) { if (initializedFlag == false) initialize(); bool validFlag = false; Enum enumValue = VolumeSliceProjectionTypeEnum::enumData[0].enumValue; for (std::vector::iterator iter = enumData.begin(); iter != enumData.end(); iter++) { const VolumeSliceProjectionTypeEnum& 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 VolumeSliceProjectionTypeEnum")); } 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 VolumeSliceProjectionTypeEnum::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 VolumeSliceProjectionTypeEnum::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(VolumeSliceProjectionTypeEnum::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 VolumeSliceProjectionTypeEnum::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(VolumeSliceProjectionTypeEnum::toGuiName(iter->enumValue)); } if (isSorted) { std::sort(allGuiNames.begin(), allGuiNames.end()); } } connectome-workbench-1.2.3+git41-gc4c6c90/src/Files/VolumeSliceProjectionTypeEnum.h000066400000000000000000000064731300200146000277440ustar00rootroot00000000000000#ifndef __VOLUME_SLICE_PROJECTION_TYPE_ENUM_H__ #define __VOLUME_SLICE_PROJECTION_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 VolumeSliceProjectionTypeEnum { public: /** * Enumerated values. */ enum Enum { /** Draw volume slice with an oblique projection */ VOLUME_SLICE_PROJECTION_OBLIQUE, /** Draw volume slice with an orthogonal projection */ VOLUME_SLICE_PROJECTION_ORTHOGONAL }; ~VolumeSliceProjectionTypeEnum(); 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: VolumeSliceProjectionTypeEnum(const Enum enumValue, const AString& name, const AString& guiName); static const VolumeSliceProjectionTypeEnum* 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_PROJECTION_TYPE_ENUM_DECLARE__ std::vector VolumeSliceProjectionTypeEnum::enumData; bool VolumeSliceProjectionTypeEnum::initializedFlag = false; int32_t VolumeSliceProjectionTypeEnum::integerCodeCounter = 0; #endif // __VOLUME_SLICE_PROJECTION_TYPE_ENUM_DECLARE__ } // namespace #endif //__VOLUME_SLICE_PROJECTION_TYPE_ENUM_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Files/VolumeSpline.cxx000066400000000000000000000241611300200146000247600ustar00rootroot00000000000000/*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 "CaretOMP.h" #include "CubicSpline.h" #include "MathFunctions.h" #include "VolumeSpline.h" #include #include #include using namespace std; using namespace caret; VolumeSpline::VolumeSpline() { m_ignoredNonNumeric = false; m_dims[0] = 0; m_dims[1] = 0; m_dims[2] = 0; } VolumeSpline::VolumeSpline(const float* frame, const int64_t framedims[3]) { m_ignoredNonNumeric = false; m_dims[0] = framedims[0]; m_dims[1] = framedims[1]; m_dims[2] = framedims[2]; m_deconv = CaretArray(m_dims[0] * m_dims[1] * m_dims[2]); CaretArray scratchArray(m_dims[0] * max(m_dims[1], m_dims[2])), deconvScratch(max(m_dims[0], max(m_dims[1], m_dims[2])));//allocate as much as we will need, even if we don't use it all yet predeconvolve(deconvScratch, m_dims[0]); for (int k = 0; k < m_dims[2]; ++k) { int64_t index = m_dims[0] * m_dims[1] * k; int64_t index2 = 0; for (int j = 0; j < m_dims[1]; ++j) { for (int i = 0; i < m_dims[0]; ++i) { float tempf = frame[index]; if (MathFunctions::isNumeric(tempf)) { scratchArray[index2] = tempf; } else { scratchArray[index2] = 0.0f; m_ignoredNonNumeric = true; } ++index; ++index2; } } #pragma omp CARET_PARFOR schedule(dynamic) for (int j = 0; j < m_dims[1]; ++j) { int64_t privIndex = j * m_dims[0]; deconvolve(scratchArray.getArray() + privIndex, deconvScratch, m_dims[0]); } index = m_dims[0] * m_dims[1] * k; index2 = 0; for (int j = 0; j < m_dims[1]; ++j) { for (int i = 0; i < m_dims[0]; ++i) { m_deconv[index] = scratchArray[index2]; ++index; ++index2; } } } predeconvolve(deconvScratch, m_dims[1]); for (int k = 0; k < m_dims[2]; ++k) { int64_t indexbase = k * m_dims[1] * m_dims[0]; int64_t index = indexbase; for (int j = 0; j < m_dims[1]; ++j) { int64_t index2 = j; for (int i = 0; i < m_dims[0]; ++i) { scratchArray[index2] = m_deconv[index];//read linearly from frame while writing transposed should be slightly faster, because cache can stay dirty? index2 += m_dims[1]; ++index; } } #pragma omp CARET_PARFOR schedule(dynamic) for (int i = 0; i < m_dims[0]; ++i) { int64_t privindex = i * m_dims[1]; deconvolve(scratchArray.getArray() + privindex, deconvScratch, m_dims[1]); } index = 0; for (int i = 0; i < m_dims[0]; ++i) { int64_t index2 = i + indexbase; for (int j = 0; j < m_dims[1]; ++j) { m_deconv[index2] = scratchArray[index];//even though scratch should be cached now, if writing to frame collides, reading linearly should give better behavior ++index; index2 += m_dims[0]; } } } predeconvolve(deconvScratch, m_dims[2]); for (int j = 0; j < m_dims[1]; ++j)//finally, use a similar strategy to do linear reads instead of widely interleaved reads for k-rows { int64_t indexbase = j * m_dims[0]; int64_t increment = m_dims[1] * m_dims[0]; for (int k = 0; k < m_dims[2]; ++k) { int64_t index = indexbase + k * increment; int64_t index2 = k; for (int i = 0; i < m_dims[0]; ++i) { scratchArray[index2] = m_deconv[index]; index2 += m_dims[2]; ++index; } } #pragma omp CARET_PARFOR schedule(dynamic) for (int i = 0; i < m_dims[0]; ++i) { int64_t privindex = i * m_dims[2]; deconvolve(scratchArray.getArray() + privindex, deconvScratch, m_dims[2]); } for (int i = 0; i < m_dims[0]; ++i) { int64_t index = indexbase + i; int64_t index2 = i * m_dims[2]; for (int k = 0; k < m_dims[2]; ++k) { m_deconv[index] = scratchArray[index2]; index += increment; ++index2; } } } } float VolumeSpline::sample(const float& ifloat, const float& jfloat, const float& kfloat) { if (m_dims[0] < 2 || ifloat < 0.0f || jfloat < 0.0f || kfloat < 0.0f || ifloat > m_dims[0] - 1 || jfloat > m_dims[1] - 1 || kfloat > m_dims[2] - 1) return 0.0f;//yeesh const int64_t zstep = m_dims[0] * m_dims[1]; float iparti, ipartj, ipartk; float fparti = modf(ifloat, &iparti); float fpartj = modf(jfloat, &ipartj); float fpartk = modf(kfloat, &ipartk); int64_t lowi = (int64_t)iparti; int64_t lowj = (int64_t)ipartj; int64_t lowk = (int64_t)ipartk; bool lowedgei = (lowi < 1); bool lowedgej = (lowj < 1); bool lowedgek = (lowk < 1); bool highedgei = (lowi >= m_dims[0] - 2); bool highedgej = (lowj >= m_dims[1] - 2); bool highedgek = (lowk >= m_dims[2] - 2); CubicSpline ispline = CubicSpline::bspline(fparti, lowedgei, highedgei); CubicSpline jspline = CubicSpline::bspline(fpartj, lowedgej, highedgej); CubicSpline kspline = CubicSpline::bspline(fpartk, lowedgek, highedgek); float jtemp[4], ktemp[4];//the weights of the splines are zero for off-the edge values, but zero the data anyway jtemp[0] = 0.0f; jtemp[3] = 0.0f; ktemp[0] = 0.0f; ktemp[3] = 0.0f; if (lowedgei || lowedgej || lowedgek || highedgei || highedgej || highedgek) {//there is an edge nearby, use the generic version with more conditionals int jstart = lowedgej ? 1 : 0; int kstart = lowedgek ? 1 : 0; int jend = highedgej ? 3 : 4; int kend = highedgek ? 3 : 4; for (int k = kstart; k < kend; ++k) { int64_t indexk = (k + lowk - 1) * zstep; for (int j = jstart; j < jend; ++j) { int64_t indexj = indexk + (j + lowj - 1) * m_dims[0] + lowi - 1; if (lowedgei)//have to do these tests for the simple reason that otherwise we might access off the end of the array in two of the 8 corners { if (highedgei) { jtemp[j] = ispline.evalBothEdge(m_deconv[indexj + 1], m_deconv[indexj + 2]); } else { jtemp[j] = ispline.evalLowEdge(m_deconv[indexj + 1], m_deconv[indexj + 2], m_deconv[indexj + 3]); } } else { if (highedgei) { jtemp[j] = ispline.evalHighEdge(m_deconv[indexj], m_deconv[indexj + 1], m_deconv[indexj + 2]); } else { jtemp[j] = ispline.evaluate(m_deconv[indexj], m_deconv[indexj + 1], m_deconv[indexj + 2], m_deconv[indexj + 3]); } } } ktemp[k] = jspline.evaluate(jtemp[0], jtemp[1], jtemp[2], jtemp[3]); } return kspline.evaluate(ktemp[0], ktemp[1], ktemp[2], ktemp[3]); } else {//we are clear of all edges, we can use fewer conditionals int64_t indexbase = lowi - 1 + m_dims[0] * (lowj - 1 + m_dims[1] * (lowk - 1)); const float* basePtr = m_deconv.getArray() + indexbase; int64_t indexk = 0; for (int k = 0; k < 4; ++k) { int64_t indexj = indexk; for (int j = 0; j < 4; ++j) { jtemp[j] = ispline.evaluate(basePtr[indexj], basePtr[indexj + 1], basePtr[indexj + 2], basePtr[indexj + 3]); indexj += m_dims[0]; } ktemp[k] = jspline.evaluate(jtemp[0], jtemp[1], jtemp[2], jtemp[3]); indexk += zstep; } return kspline.evaluate(ktemp[0], ktemp[1], ktemp[2], ktemp[3]); } } void VolumeSpline::deconvolve(float* data, const float* backsubs, const int64_t& length) { if (length < 1) return; const float A = 1.0f / 6.0f, B = 2.0f / 3.0f;//the coefficients of a bspline at center and +/-1 //forward pass simulating gaussian elimination on matrix of bspline kernels and data data[0] /= B + A;//repeat final value for data outside the bounding box, to prevent bright edges for (int i = 1; i < length - 1; ++i)//the first and last rows are handled slightly differently { data[i] = (data[i] - A * data[i - 1]) / (B - A * backsubs[i - 1]); } data[length - 1] = (data[length - 1] - A * data[length - 2]) / (B + A - A * backsubs[length - 2]);//repeat final value for data outside the bounding box, to prevent bright edges //back substitution, making it gauss-jordan for (int i = length - 2; i >= 0; --i)//the last row doesn't need back-substitution { data[i] -= backsubs[i] * data[i + 1]; } } void VolumeSpline::predeconvolve(float* backsubs, const int64_t& length) { if (length < 1) return; const float A = 1.0f / 6.0f, B = 2.0f / 3.0f; backsubs[0] = A / (B + A);//repeat final value for data outside the bounding box, to prevent bright edges for (int i = 1; i < length; ++i) { backsubs[i] = A / (B - A * backsubs[i - 1]); } } connectome-workbench-1.2.3+git41-gc4c6c90/src/Files/VolumeSpline.h000066400000000000000000000036511300200146000244060ustar00rootroot00000000000000#ifndef __VOLUME_SPLINE_H__ #define __VOLUME_SPLINE_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 "stdint.h" #include "CaretPointer.h" namespace caret { class VolumeSpline { bool m_ignoredNonNumeric; int64_t m_dims[3]; CaretArray m_deconv;//don't do lazy deconvolution, it doesn't save much time, and takes more memory and slightly longer if you have to do the whole volume anyway void deconvolve(float* data, const float* backsubs, const int64_t& length);//use CaretArray so that it doesn't reallocate like a vector on copy, and the data is static once computed void predeconvolve(float* backsubs, const int64_t& length);//since the back substitution on the same size array uses the same coefficients, precompute them public: VolumeSpline(); VolumeSpline(const float* frame, const int64_t framedims[3]); float sample(const float& i, const float& j, const float& k); float sample(const float ijk[3]) { return sample(ijk[0], ijk[1], ijk[2]); } bool ignoredNonNumeric() const { return m_ignoredNonNumeric; } }; } #endif //__VOLUME_SPLINE_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Files/VtkFileExporter.cxx000066400000000000000000000366741300200146000254470ustar00rootroot00000000000000 /*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 __VTK_FILE_EXPORTER_DECLARE__ #include "VtkFileExporter.h" #undef __VTK_FILE_EXPORTER_DECLARE__ #include "AString.h" #include "ByteOrderEnum.h" #include "CaretAssert.h" #include "DataFileException.h" #include "FileAdapter.h" #include "SurfaceFile.h" #include "XmlWriter.h" #include using namespace caret; /** * \class caret::VtkFileExporter * \brief Exports data into VTK file formats. * \ingroup Files */ /** * Write the given surface files using the coloring from the given browser tab. * * File format documented at: http://www.vtk.org/VTK/img/file-formats.pdf * * @param surfaceFile * The surface file. * @param surfaceFilesColoring * RGBA coloring for each of the surfaces. * @param vtkFileName * Name of the VTK file. */ void VtkFileExporter::writeSurfaces(const std::vector& surfaceFiles, const std::vector& surfaceFilesColoring, const AString& vtkFileName) { try { const int32_t numberOfSurfaceFiles = static_cast(surfaceFiles.size()); if (numberOfSurfaceFiles <= 0) { throw DataFileException(vtkFileName, "No surfaces provided for export to VTK."); } CaretAssert(surfaceFiles.size() == surfaceFilesColoring.size()); int32_t totalNodes = 0; int32_t totalTriangles = 0; float coordinateMinimum = std::numeric_limits::max(); float coordinateMaximum = -std::numeric_limits::max(); for (int32_t iSurface = 0; iSurface < numberOfSurfaceFiles; iSurface++) { SurfaceFile* surfaceFile = surfaceFiles[iSurface]; const int32_t numberOfNodes = surfaceFile->getNumberOfNodes(); totalNodes += numberOfNodes; const int32_t numberOfTriangles = surfaceFile->getNumberOfTriangles(); totalTriangles += numberOfTriangles; for (int32_t iNode = 0; iNode < numberOfNodes; iNode++) { const float* xyz = surfaceFile->getCoordinate(iNode); for (int32_t i = 0; i < 3; i++) { if (xyz[i] < coordinateMinimum) coordinateMinimum = xyz[i]; if (xyz[i] > coordinateMaximum) coordinateMaximum = xyz[i]; } } } if (totalNodes <= 0) { throw DataFileException(vtkFileName, "Surfaces contain no nodes"); } if (totalNodes <= 0) { throw DataFileException(vtkFileName, "Surfaces contain no triangles"); } /* * Open a text stream */ FileAdapter fileAdapter; AString errorMessage; QTextStream* textStream = fileAdapter.openQTextStreamForWritingFile(vtkFileName, errorMessage); if (textStream == NULL) { throw DataFileException(vtkFileName, errorMessage); } /* * Create the XML Writer */ XmlWriter xmlWriter(*textStream); xmlWriter.writeStartDocument("1.0"); /* * Write the root element */ XmlAttributes rootAttributes; rootAttributes.addAttribute("type", "PolyData"); rootAttributes.addAttribute("version", "0.1"); switch (ByteOrderEnum::getSystemEndian()) { case ByteOrderEnum::ENDIAN_BIG: rootAttributes.addAttribute("byte_order", "BigEndian"); break; case ByteOrderEnum::ENDIAN_LITTLE: rootAttributes.addAttribute("byte_order", "LittleEndian"); break; } rootAttributes.addAttribute("compressor", "vtkZLibDataCompressor"); xmlWriter.writeStartElement("VTKFile", rootAttributes); /* * Start PolyData element */ xmlWriter.writeStartElement("PolyData"); /* * Start Piece element */ XmlAttributes pieceAttributes; pieceAttributes.addAttribute("NumberOfPoints", totalNodes); pieceAttributes.addAttribute("NumberOfVerts", 0); pieceAttributes.addAttribute("NumberOfLines", 0); pieceAttributes.addAttribute("NumberOfStrips", 0); pieceAttributes.addAttribute("NumberOfPolys", totalTriangles); xmlWriter.writeStartElement("Piece", pieceAttributes); /* * Start PointData */ XmlAttributes pointDataAttributes; pointDataAttributes.addAttribute("Scalars", "Scalars_"); pointDataAttributes.addAttribute("Normals", "Normals"); xmlWriter.writeStartElement("PointData", pointDataAttributes); /* * Start Normal Vectors DataArray element */ XmlAttributes dataArrayNormalsAttributes; dataArrayNormalsAttributes.addAttribute("type", "Float32"); dataArrayNormalsAttributes.addAttribute("Name", "Normals"); dataArrayNormalsAttributes.addAttribute("NumberOfComponents", 3); dataArrayNormalsAttributes.addAttribute("format", "ascii"); dataArrayNormalsAttributes.addAttribute("RangeMin", -1.0); dataArrayNormalsAttributes.addAttribute("RangeMax", 1.0); xmlWriter.writeStartElement("DataArray", dataArrayNormalsAttributes); for (int32_t iSurface = 0; iSurface < numberOfSurfaceFiles; iSurface++) { const SurfaceFile* sf = surfaceFiles[iSurface]; const int32_t numberOfNodes = sf->getNumberOfNodes(); for (int32_t iNode = 0; iNode < numberOfNodes; iNode++) { const float* normalVector = sf->getNormalVector(iNode); xmlWriter.writeCharactersWithIndent(AString::number(normalVector[0])); xmlWriter.writeCharacters(" "); xmlWriter.writeCharacters(AString::number(normalVector[1])); xmlWriter.writeCharacters(" "); xmlWriter.writeCharacters(AString::number(normalVector[2])); xmlWriter.writeCharacters("\n"); } } /* * End DataArray Normals element */ xmlWriter.writeEndElement(); /* * Start Scalars DataArray element * that contains the RGB colors */ XmlAttributes dataArrayScalarsAttributes; dataArrayScalarsAttributes.addAttribute("type", "UInt8"); dataArrayScalarsAttributes.addAttribute("Name", "Scalars_"); dataArrayScalarsAttributes.addAttribute("NumberOfComponents", 3); dataArrayScalarsAttributes.addAttribute("format", "ascii"); dataArrayScalarsAttributes.addAttribute("RangeMin", 0); dataArrayScalarsAttributes.addAttribute("RangeMax", 255); xmlWriter.writeStartElement("DataArray", dataArrayScalarsAttributes); for (int32_t iSurface = 0; iSurface < numberOfSurfaceFiles; iSurface++) { SurfaceFile* sf = surfaceFiles[iSurface]; const float* surfaceRGBA = surfaceFilesColoring[iSurface]; const int32_t numberOfNodes = sf->getNumberOfNodes(); for (int32_t iNode = 0; iNode < numberOfNodes; iNode++) { const float* rgbaFloat = &surfaceRGBA[iNode * 4]; uint8_t rgb[3]; for (int32_t k = 0; k < 3; k++) { float value = rgbaFloat[k] * 255.0; if (value > 255.0) value = 255.0; if (value < 0.0) value = 0.0; const uint8_t byteValue = static_cast(value); rgb[k] = byteValue; } xmlWriter.writeCharactersWithIndent(AString::number(rgb[0])); xmlWriter.writeCharacters(" "); xmlWriter.writeCharacters(AString::number(rgb[1])); xmlWriter.writeCharacters(" "); xmlWriter.writeCharacters(AString::number(rgb[2])); xmlWriter.writeCharacters("\n"); } } /* * End DataArray Scalars element */ xmlWriter.writeEndElement(); /* * End PointData */ xmlWriter.writeEndElement(); /* * No Cell Data */ xmlWriter.writeStartElement("CellData"); xmlWriter.writeEndElement(); /* * Start Points element */ xmlWriter.writeStartElement("Points"); /* * Start Coordinates DataArray element */ XmlAttributes dataArrayPointsAttributes; dataArrayPointsAttributes.addAttribute("type", "Float32"); dataArrayPointsAttributes.addAttribute("Name", "Points"); dataArrayPointsAttributes.addAttribute("NumberOfComponents", 3); dataArrayPointsAttributes.addAttribute("format", "ascii"); dataArrayPointsAttributes.addAttribute("RangeMin", coordinateMinimum); dataArrayPointsAttributes.addAttribute("RangeMax", coordinateMaximum); xmlWriter.writeStartElement("DataArray", dataArrayPointsAttributes); for (int32_t iSurface = 0; iSurface < numberOfSurfaceFiles; iSurface++) { const SurfaceFile* sf = surfaceFiles[iSurface]; const int32_t numberOfNodes = sf->getNumberOfNodes(); for (int32_t iNode = 0; iNode < numberOfNodes; iNode++) { const float* normalVector = sf->getCoordinate(iNode); xmlWriter.writeCharactersWithIndent(AString::number(normalVector[0])); xmlWriter.writeCharacters(" "); xmlWriter.writeCharacters(AString::number(normalVector[1])); xmlWriter.writeCharacters(" "); xmlWriter.writeCharacters(AString::number(normalVector[2])); xmlWriter.writeCharacters("\n"); } } /* * End DataArray Coordinates */ xmlWriter.writeEndElement(); /* * End Points element */ xmlWriter.writeEndElement(); /* * Empty Verts Element */ xmlWriter.writeStartElement("Verts"); xmlWriter.writeEndElement(); /* * Empty Lines Element */ xmlWriter.writeStartElement("Lines"); xmlWriter.writeEndElement(); /* * Empty Strips Element */ xmlWriter.writeStartElement("Strips"); xmlWriter.writeEndElement(); /* * Start Polys Element */ xmlWriter.writeStartElement("Polys"); /* * Start DataArray for nodes in every triangle */ XmlAttributes dataArrayTrianglesAttributes; dataArrayTrianglesAttributes.addAttribute("type", "Int64"); dataArrayTrianglesAttributes.addAttribute("Name", "connectivity"); dataArrayTrianglesAttributes.addAttribute("format", "ascii"); dataArrayTrianglesAttributes.addAttribute("RangeMin", 0); dataArrayTrianglesAttributes.addAttribute("RangeMax", totalNodes - 1); xmlWriter.writeStartElement("DataArray", dataArrayTrianglesAttributes); /* * Write the nodes in every triangle */ int32_t triangleNodeOffset = 0; for (int32_t iSurface = 0; iSurface < numberOfSurfaceFiles; iSurface++) { const SurfaceFile* sf = surfaceFiles[iSurface]; const int32_t numberOfTriangles = sf->getNumberOfTriangles(); for (int32_t iTriangle = 0; iTriangle < numberOfTriangles; iTriangle++) { const int32_t* triangleNodes = sf->getTriangle(iTriangle); xmlWriter.writeCharactersWithIndent(AString::number(triangleNodes[0] + triangleNodeOffset)); xmlWriter.writeCharacters(" "); xmlWriter.writeCharacters(AString::number(triangleNodes[1] + triangleNodeOffset)); xmlWriter.writeCharacters(" "); xmlWriter.writeCharacters(AString::number(triangleNodes[2] + triangleNodeOffset)); xmlWriter.writeCharacters("\n"); } /* * All surface nodes are in one list so offset for additional surfaces */ triangleNodeOffset += sf->getNumberOfNodes(); } /* * End DataArray for nodes in every triangle */ xmlWriter.writeEndElement(); /* * Start DataArray for offset of each triangle */ XmlAttributes dataArrayTriangleOffsetAttributes; dataArrayTriangleOffsetAttributes.addAttribute("type", "Int64"); dataArrayTriangleOffsetAttributes.addAttribute("Name", "offsets"); dataArrayTriangleOffsetAttributes.addAttribute("format", "ascii"); dataArrayTriangleOffsetAttributes.addAttribute("RangeMin", 0); dataArrayTriangleOffsetAttributes.addAttribute("RangeMax", totalTriangles - 1); xmlWriter.writeStartElement("DataArray", dataArrayTriangleOffsetAttributes); /* * Write offset of each triangle */ for (int32_t i = 1; i <= totalTriangles; i++) { if ((i % 6) == 0) { if (i > 0) { xmlWriter.writeCharacters("\n"); } xmlWriter.writeCharactersWithIndent(AString::number(i * 3)); } else { xmlWriter.writeCharacters(AString::number(i * 3)); } xmlWriter.writeCharacters(" "); } xmlWriter.writeCharacters("\n"); /* * End DataArray for offset of each triangle */ xmlWriter.writeEndElement(); /* * End Polys Element */ xmlWriter.writeEndElement(); /* * End Piece element */ xmlWriter.writeEndElement(); /* * End PolyData element */ xmlWriter.writeEndElement(); /* * End root element */ xmlWriter.writeEndElement(); /* * Finish XML and close file */ xmlWriter.writeEndDocument(); fileAdapter.close(); } catch (const XmlException& e) { throw DataFileException(vtkFileName, e.whatString()); } } connectome-workbench-1.2.3+git41-gc4c6c90/src/Files/VtkFileExporter.h000066400000000000000000000033621300200146000250600ustar00rootroot00000000000000#ifndef __VTK_FILE_EXPORTER_H__ #define __VTK_FILE_EXPORTER_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 "AString.h" #include namespace caret { class SurfaceFile; class VtkFileExporter { public: static void writeSurfaces(const std::vector& surfaceFiles, const std::vector& surfaceFilesColoring, const AString& vtkFileName); private: VtkFileExporter(); virtual ~VtkFileExporter(); VtkFileExporter(const VtkFileExporter&); VtkFileExporter& operator=(const VtkFileExporter&); public: // ADD_NEW_METHODS_HERE private: // ADD_NEW_MEMBERS_HERE }; #ifdef __VTK_FILE_EXPORTER_DECLARE__ // #endif // __VTK_FILE_EXPORTER_DECLARE__ } // namespace #endif //__VTK_FILE_EXPORTER_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Files/WarpfieldFile.cxx000066400000000000000000000156031300200146000250540ustar00rootroot00000000000000/*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 "WarpfieldFile.h" #include "CaretAssert.h" #include "FileInformation.h" #include "FloatMatrix.h" #include "NiftiIO.h" using namespace caret; using namespace std; void WarpfieldFile::readWorld(const AString& warpname) { CaretPointer newFile(new VolumeFile()); newFile->readFile(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"); } 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::readFnirt(const AString& warpName, const AString& sourceName) { FloatMatrix sourceSform, sourceFSL, refSform, refFSL; NiftiIO myIO; myIO.openRead(warpName); refSform = FloatMatrix(myIO.getHeader().getSForm()); refFSL = FloatMatrix(myIO.getHeader().getFSLSpace()); myIO.openRead(sourceName); sourceSform = FloatMatrix(myIO.getHeader().getSForm()); sourceFSL = FloatMatrix(myIO.getHeader().getFSLSpace()); CaretPointer newFile(new VolumeFile()); newFile->readFile(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 (int j = 0; j < dims[1]; ++j) { for (int i = 0; i < dims[0]; ++i) { Vector3D fslcoord = i * fslX + j * fslY + k * fslZ + fslOff; Vector3D coord, fsldisplacement; 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; Vector3D transAbsolute = fslTransAbsolute[0] * sourceTransX + fslTransAbsolute[1] * sourceTransY + fslTransAbsolute[2] * sourceTransZ + sourceTransOff; Vector3D transdisplace = transAbsolute - coord; 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); } } } 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) { if (m_warpfield == NULL) throw DataFileException("writeWorld called on uninitialized warpfield"); m_warpfield->writeFile(warpname); } void WarpfieldFile::writeFnirt(const AString& warpname, const AString& sourceName) { if (m_warpfield == NULL) throw DataFileException("writeFnirt called on uninitialized warpfield"); FloatMatrix sourceSform, sourceFSL, refSform, refFSL; NiftiHeader myHeader; myHeader.setSForm(m_warpfield->getSform()); myHeader.setDimensions(m_warpfield->getOriginalDimensions()); refSform = FloatMatrix(myHeader.getSForm()); refFSL = FloatMatrix(myHeader.getFSLSpace()); NiftiIO sourceIO; sourceIO.openRead(sourceName); sourceSform = FloatMatrix(sourceIO.getHeader().getSForm()); sourceFSL = FloatMatrix(sourceIO.getHeader().getFSLSpace()); VolumeFile outFile; vector dims; m_warpfield->getDimensions(dims); dims.resize(4);//drop number of components outFile.reinitialize(dims, m_warpfield->getSform()); outFile.setMapName(0, "x displacement"); outFile.setMapName(1, "y displacement"); outFile.setMapName(2, "z displacement"); FloatMatrix FSLTransform = sourceFSL * sourceSform.inverse();//goes from real space to FSL source space Vector3D FSLTransX, FSLTransY, FSLTransZ, FSLTransOff; 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 (int j = 0; j < dims[1]; ++j) { for (int i = 0; i < dims[0]; ++i) { Vector3D fslcoord = i * fslX + j * fslY + k * fslZ + fslOff; Vector3D coord, realdisplacement; m_warpfield->indexToSpace(i, j, k, coord); realdisplacement[0] = m_warpfield->getValue(i, j, k, 0); realdisplacement[1] = m_warpfield->getValue(i, j, k, 1); realdisplacement[2] = m_warpfield->getValue(i, j, k, 2); Vector3D realabsolute = coord + realdisplacement; Vector3D fslabsolute = realabsolute[0] * FSLTransX + realabsolute[1] * FSLTransY + realabsolute[2] * FSLTransZ + FSLTransOff; Vector3D fsldisplace = fslabsolute - fslcoord; outFile.setValue(fsldisplace[0], i, j, k, 0); outFile.setValue(fsldisplace[1], i, j, k, 1); outFile.setValue(fsldisplace[2], i, j, k, 2); } } } outFile.writeFile(warpname); } connectome-workbench-1.2.3+git41-gc4c6c90/src/Files/WarpfieldFile.h000066400000000000000000000027011300200146000244740ustar00rootroot00000000000000#ifndef __WARPFIELD_FILE_H__ #define __WARPFIELD_FILE_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 "AString.h" #include "CaretPointer.h" #include "VolumeFile.h" namespace caret { class WarpfieldFile { CaretPointer m_warpfield; public: const VolumeFile* getWarpfield() { return m_warpfield.getPointer(); } void readFnirt(const AString& warpName, const AString& sourceName); void readWorld(const AString& warpname); void writeFnirt(const AString& warpname, const AString& sourceName);//for completeness void writeWorld(const AString& warpname); }; } #endif //__WARPFIELD_FILE_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Files/XmlStreamReaderHelper.cxx000066400000000000000000000366051300200146000265430ustar00rootroot00000000000000 /*LICENSE_START*/ /* * Copyright (C) 2015 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_STREAM_READER_HELPER_DECLARE__ #include "XmlStreamReaderHelper.h" #undef __XML_STREAM_READER_HELPER_DECLARE__ #include #include #include "CaretAssert.h" #include "DataFileException.h" #include "GiftiMetaData.h" using namespace caret; /** * \class caret::XmlStreamReaderHelper * \brief Helper class for using QXmlStreamReader * \ingroup Files * * Assists with reading a file using QXmlStreamReader. * If there is an error while using any of these methods, * a DataFileException will be thrown containing the * name of the file, a message, and also the line and * column of the error. */ /** * Constructor. * * @param filename * Name of file that is being read using the QXmlStreamReader * @param stream * The QXmlStreamReader */ XmlStreamReaderHelper::XmlStreamReaderHelper(const QString& filename, QXmlStreamReader* stream) : CaretObject(), m_filename(filename), m_stream(stream) { CaretAssert(stream); } /** * Destructor. */ XmlStreamReaderHelper::~XmlStreamReaderHelper() { } /** * Get the string 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 or if the value * is empty text. * @return * String value for the attribute. */ QString XmlStreamReaderHelper::getOptionalAttributeStringValue(const QXmlStreamAttributes& attributes, const QString& /*elementName*/, const QString& attributeName, const QString& defaultValue) { QString valueString; if (attributes.hasAttribute(attributeName)) { valueString = attributes.value(attributeName).toString(); if (valueString.isEmpty()) { valueString = defaultValue; } } else { valueString = defaultValue; } return valueString; } /** * Get the string value for the given attribute name from the * given attributes. If the attribute name is not found or * its value is an empty string, a DataFileException is thrown. * * @param attributes * The XML attributes. * @param elementName * Name of element containing the attributes. * @param attributeName * Name of the attribute. * @param alternateAttributeNameOne * Use if attributeName is not found. * @param alternateAttributeNameTwo * Use if neither attributeName nor alternateAttributeNameOne is not found. * @return * String value for the attribute. * @throw * DataFileException if attribute is missing or value is * an empty string. */ QString XmlStreamReaderHelper::getRequiredAttributeStringValue(const QXmlStreamAttributes& attributes, const QString& elementName, const QString& attributeName, const QString& alternateAttributeNameOne, const QString& alternateAttributeNameTwo) { QString valueString; QString foundAttributeName; if (attributes.hasAttribute(attributeName)) { foundAttributeName = attributeName; } else if (( ! alternateAttributeNameOne.isEmpty()) && attributes.hasAttribute(alternateAttributeNameOne)) { foundAttributeName = alternateAttributeNameOne; } else if (( ! alternateAttributeNameTwo.isEmpty()) && attributes.hasAttribute(alternateAttributeNameTwo)) { foundAttributeName = alternateAttributeNameTwo; } if ( ! foundAttributeName.isEmpty()) { valueString = attributes.value(foundAttributeName).toString(); if (valueString.isEmpty()) { throwDataFileException("Value for attribute " + foundAttributeName + " in element " + elementName + " is empty"); } } else { throwDataFileException(attributeName + " is missing from element " + elementName); } return valueString; } /** * Get the bool 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 * Boolean value for the attribute. * @throw * DataFileException if attribute is missing or value is * an empty string. */ bool XmlStreamReaderHelper::getOptionalAttributeBoolValue(const QXmlStreamAttributes& attributes, const QString& elementName, const QString& attributeName, const bool defaultValue) { const AString stringValue = getOptionalAttributeStringValue(attributes, elementName, attributeName, AString::fromBool(defaultValue)); const bool value = stringValue.toBool(); return value; } /** * Get the bool value for the given attribute name from the * given attributes. If the attribute name is not found or * its value is an empty string, a DataFileException is thrown. * * @param attributes * The XML attributes. * @param elementName * Name of element containing the attributes. * @param attributeName * Name of the attribute. * @return * Boolean value for the attribute. * @throw * DataFileException if attribute is missing or value is * an empty string. */ bool XmlStreamReaderHelper::getRequiredAttributeBoolValue(const QXmlStreamAttributes& attributes, const QString& elementName, const QString& attributeName) { const AString stringValue = getRequiredAttributeStringValue(attributes, elementName, attributeName); const bool value = stringValue.toBool(); return value; } /** * Get the int value for the given attribute name from the * given attributes. If the attribute name is not found or * its value is an empty string, a DataFileException is thrown. * * @param attributes * The XML attributes. * @param elementName * Name of element containing the attributes. * @param attributeName * Name of the attribute. * @return * Integer value for the attribute. * @throw * DataFileException if attribute is missing or value is * an empty string. */ int XmlStreamReaderHelper::getRequiredAttributeIntValue(const QXmlStreamAttributes& attributes, const QString& elementName, const QString& attributeName) { const QString stringValue = getRequiredAttributeStringValue(attributes, elementName, attributeName); bool valid; const int value = stringValue.toInt(&valid); if ( ! valid) { throwDataFileException("Value for attribute " + attributeName + " is not a valid integer value " + stringValue); } return value; } /** * Get the float 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 * float value for the attribute. * @throw * DataFileException if attribute is missing or value is * an empty string. */ float XmlStreamReaderHelper::getOptionalAttributeFloatValue(const QXmlStreamAttributes& attributes, const QString& elementName, const QString& attributeName, const float defaultValue) { const AString stringValue = getOptionalAttributeStringValue(attributes, elementName, attributeName, AString::number(defaultValue)); const float value = stringValue.toFloat(); return value; } /** * Get the int value for the given attribute name from the * given attributes. If the attribute name is not found or * its value is an empty string, a DataFileException is thrown. * * @param attributes * The XML attributes. * @param elementName * Name of element containing the attributes. * @param attributeName * Name of the attribute. * @return * Integer value for the attribute. * @throw * DataFileException if attribute is missing or value is * an empty string. */ float XmlStreamReaderHelper::getRequiredAttributeFloatValue(const QXmlStreamAttributes& attributes, const QString& elementName, const QString& attributeName) { const QString stringValue = getRequiredAttributeStringValue(attributes, elementName, attributeName); bool valid; const float value = stringValue.toFloat(&valid); if ( ! valid) { throwDataFileException("Value for attribute " + attributeName + " is not a valid integer value " + stringValue); } return value; } /** * Read the GIFTI metadata (Copied from CiftiXMLReader::parseMetaData). * * @param metadata * GIFTI metadata to which metadata is added. */ void XmlStreamReaderHelper::readMetaData(GiftiMetaData* metadata) { CaretAssert(metadata); metadata->clear(); while (!(m_stream->isEndElement() && (m_stream->name().toString() == "MetaData")) && !m_stream->hasError()) { m_stream->readNext(); if(m_stream->isStartElement()) { QString elementName = m_stream->name().toString(); if(elementName == "MD") { readMetaDataElement(metadata); } else throwDataFileException("unknown element in MetaData: " + elementName); } } //check for end element if(!m_stream->isEndElement() || (m_stream->name().toString() != "MetaData")) throwDataFileException("MetaData end tag not found."); } /** * Read the GIFTI metadata (Copied from CiftiXMLReader::parseMetaDataElement). * * @param metadata * GIFTI metadata to which metadata is added. */ void XmlStreamReaderHelper::readMetaDataElement(GiftiMetaData* metadata) { QString name; QString value; bool haveName = false; bool haveValue = false; while (!(m_stream->isEndElement() && (m_stream->name().toString() == "MD")) && !m_stream->hasError()) { //test = m_stream->name().toString(); m_stream->readNext(); if(m_stream->isStartElement()) { QString elementName = m_stream->name().toString(); if(elementName == "Name") { m_stream->readNext(); if(m_stream->tokenType() != QXmlStreamReader::Characters) { return; } name = m_stream->text().toString(); haveName = true; m_stream->readNext(); if(!m_stream->isEndElement()) { throwDataFileException("End element for meta data name tag not found."); } } else if(elementName == "Value") { m_stream->readNext(); if(m_stream->tokenType() != QXmlStreamReader::Characters) { return; } value = m_stream->text().toString(); haveValue = true; m_stream->readNext(); if(!m_stream->isEndElement()) { throwDataFileException("End element for meta data value tag not found."); } } else { throwDataFileException("unknown element in MD: " + elementName); } } } if (!haveName || !haveValue) { throwDataFileException("MD element is missing name or value"); } metadata->set(name, value); if(!m_stream->isEndElement() || (m_stream->name().toString() != "MD")) { throwDataFileException("End element for MD tag not found"); } } /** * Throw a data file exception with the given message and add * the line and column numbers to the message. * * @param message * Message included in the exception. * @throw * Always throws a DataFileException. */ void XmlStreamReaderHelper::throwDataFileException(const QString message) { throw DataFileException(m_filename, (message + " at line " + QString::number(m_stream->lineNumber()) + ", column " + QString::number(m_stream->columnNumber()))); } connectome-workbench-1.2.3+git41-gc4c6c90/src/Files/XmlStreamReaderHelper.h000066400000000000000000000100101300200146000261460ustar00rootroot00000000000000#ifndef __XML_STREAM_READER_HELPER_H__ #define __XML_STREAM_READER_HELPER_H__ /*LICENSE_START*/ /* * Copyright (C) 2015 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 QXmlStreamAttributes; class QXmlStreamReader; namespace caret { class GiftiMetaData; class XmlStreamReaderHelper : public CaretObject { public: XmlStreamReaderHelper(const QString& filename, QXmlStreamReader* stream); virtual ~XmlStreamReaderHelper(); QString getOptionalAttributeStringValue(const QXmlStreamAttributes& attributes, const QString& elementName, const QString& attributeName, const QString& defaultValue); QString getRequiredAttributeStringValue(const QXmlStreamAttributes& attributes, const QString& elementName, const QString& attributeName, const QString& alternateAttributeNameOne = "", const QString& alternateAttributeNameTwo = ""); int getRequiredAttributeIntValue(const QXmlStreamAttributes& attributes, const QString& elementName, const QString& attributeName); float getOptionalAttributeFloatValue(const QXmlStreamAttributes& attributes, const QString& elementName, const QString& attributeName, const float defaultValue); float getRequiredAttributeFloatValue(const QXmlStreamAttributes& attributes, const QString& elementName, const QString& attributeName); bool getOptionalAttributeBoolValue(const QXmlStreamAttributes& attributes, const QString& elementName, const QString& attributeName, const bool defaultValue); bool getRequiredAttributeBoolValue(const QXmlStreamAttributes& attributes, const QString& elementName, const QString& attributeName); void readMetaData(GiftiMetaData* metadata); void throwDataFileException(const QString message); private: XmlStreamReaderHelper(const XmlStreamReaderHelper&); XmlStreamReaderHelper& operator=(const XmlStreamReaderHelper&); void readMetaDataElement(GiftiMetaData* metadata); const QString m_filename; QXmlStreamReader* m_stream; // ADD_NEW_MEMBERS_HERE }; #ifdef __XML_STREAM_READER_HELPER_DECLARE__ // #endif // __XML_STREAM_READER_HELPER_DECLARE__ } // namespace #endif //__XML_STREAM_READER_HELPER_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Files/XmlStreamWriterHelper.cxx000066400000000000000000000047301300200146000266070ustar00rootroot00000000000000 /*LICENSE_START*/ /* * Copyright (C) 2015 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_STREAM_WRITER_HELPER_DECLARE__ #include "XmlStreamWriterHelper.h" #undef __XML_STREAM_WRITER_HELPER_DECLARE__ #include #include "CaretAssert.h" #include "GiftiMetaData.h" #include "GiftiXmlElements.h" using namespace caret; /** * \class caret::XmlStreamWriterHelper * \brief Helper class for using QXmlStreamWriter * \ingroup Files */ /** * Constructor. * * @param filename * Name of file that is being read using the QXmlStreamWriter * @param stream * The QXmlStreamWriter */ XmlStreamWriterHelper::XmlStreamWriterHelper(const QString& filename, QXmlStreamWriter* stream) : CaretObject(), m_filename(filename), m_stream(stream) { CaretAssert(stream); } /** * Destructor. */ XmlStreamWriterHelper::~XmlStreamWriterHelper() { } /** * Write the given metadata using the XML stream writer. * * @param metadata * Metadata that will be written to the XML stream. */ void XmlStreamWriterHelper::writeMetaData(const GiftiMetaData* metadata) { CaretAssert(metadata); m_stream->writeStartElement(GiftiXmlElements::TAG_METADATA); const std::map mdMap = metadata->getAsMap(); for (std::map::const_iterator iter = mdMap.begin(); iter != mdMap.end(); iter++) { m_stream->writeStartElement(GiftiXmlElements::TAG_METADATA_ENTRY); m_stream->writeTextElement(GiftiXmlElements::TAG_METADATA_NAME, iter->first); m_stream->writeTextElement(GiftiXmlElements::TAG_METADATA_VALUE, iter->second); m_stream->writeEndElement(); } m_stream->writeEndElement(); } connectome-workbench-1.2.3+git41-gc4c6c90/src/Files/XmlStreamWriterHelper.h000066400000000000000000000035331300200146000262340ustar00rootroot00000000000000#ifndef __XML_STREAM_WRITER_HELPER_H__ #define __XML_STREAM_WRITER_HELPER_H__ /*LICENSE_START*/ /* * Copyright (C) 2015 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 QXmlStreamAttributes; class QXmlStreamWriter; namespace caret { class GiftiMetaData; class XmlStreamWriterHelper : public CaretObject { public: XmlStreamWriterHelper(const QString& filename, QXmlStreamWriter* stream); virtual ~XmlStreamWriterHelper(); void writeMetaData(const GiftiMetaData* metadata); // ADD_NEW_METHODS_HERE private: XmlStreamWriterHelper(const XmlStreamWriterHelper&); XmlStreamWriterHelper& operator=(const XmlStreamWriterHelper&); const QString m_filename; QXmlStreamWriter* m_stream; // ADD_NEW_MEMBERS_HERE }; #ifdef __XML_STREAM_WRITER_HELPER_DECLARE__ // #endif // __XML_STREAM_WRITER_HELPER_DECLARE__ } // namespace #endif //__XML_STREAM_WRITER_HELPER_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/FilesBase/000077500000000000000000000000001300200146000224015ustar00rootroot00000000000000connectome-workbench-1.2.3+git41-gc4c6c90/src/FilesBase/CMakeLists.txt000066400000000000000000000014461300200146000251460ustar00rootroot00000000000000# # The FilesBase Project # project (FilesBase) # # Need XML from Qt # SET(QT_DONT_USE_QTGUI) # # Add QT for includes # INCLUDE (${QT_USE_FILE}) # # Create the NIFTI library # ADD_LIBRARY(FilesBase GiftiException.h GiftiLabel.h GiftiLabelTable.h GiftiMetaData.h GiftiMetaDataXmlElements.h GiftiXmlElements.h nifti1.h nifti2.h NiftiEnums.h VolumeBase.h VolumeMappableInterface.h VolumeSliceViewPlaneEnum.h VolumeSpace.h GiftiException.cxx GiftiLabel.cxx GiftiLabelTable.cxx GiftiMetaData.cxx GiftiXmlElements.cxx NiftiEnums.cxx VolumeBase.cxx VolumeMappableInterface.cxx VolumeSliceViewPlaneEnum.cxx VolumeSpace.cxx ) # # Find Headers # INCLUDE_DIRECTORIES( ${CMAKE_SOURCE_DIR}/Annotations ${CMAKE_SOURCE_DIR}/FilesBase ${CMAKE_SOURCE_DIR}/Common ${CMAKE_SOURCE_DIR}/Palette ${CMAKE_SOURCE_DIR}/Xml ) connectome-workbench-1.2.3+git41-gc4c6c90/src/FilesBase/GiftiException.cxx000066400000000000000000000041501300200146000260460ustar00rootroot00000000000000/*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 "GiftiException.h" #include using namespace caret; /** * Constructor. * */ GiftiException::GiftiException() : CaretException() { this->initializeMembersGiftiException(); } /** * Constructor that uses stack trace from the exception * passed in as a parameter. * * @param e Any exception whose stack trace becomes * this exception's stack trace. * */ GiftiException::GiftiException( const CaretException& e) : CaretException(e) { this->initializeMembersGiftiException(); } /** * Constructor. * * @param s Description of the exception. * */ GiftiException::GiftiException( const AString& s) : CaretException(s) { this->initializeMembersGiftiException(); } /** * Copy Constructor. * @param e * Exception that is copied. */ GiftiException::GiftiException(const GiftiException& e) : CaretException(e) { } /** * Assignment operator. * @param e * Exception that is copied. * @return * Copy of the exception. */ GiftiException& GiftiException::operator=(const GiftiException& e) { if (this != &e) { CaretException::operator=(e); } return *this; } /** * Destructor */ GiftiException::~GiftiException() throw() { } void GiftiException::initializeMembersGiftiException() { } connectome-workbench-1.2.3+git41-gc4c6c90/src/FilesBase/GiftiException.h000066400000000000000000000027111300200146000254740ustar00rootroot00000000000000#ifndef __GIFTIEXCEPTION_H__ #define __GIFTIEXCEPTION_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 "CaretException.h" namespace caret { /** * An exception thrown during GIFTI file processing. */ class GiftiException : public CaretException { public: GiftiException(); GiftiException(const CaretException& e); GiftiException(const AString& s); GiftiException(const GiftiException& e); GiftiException& operator=(const GiftiException& e); virtual ~GiftiException() throw(); private: void initializeMembersGiftiException(); }; } // namespace #endif // __GIFTIEXCEPTION_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/FilesBase/GiftiLabel.cxx000066400000000000000000000407341300200146000251370ustar00rootroot00000000000000/*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 __GIFTI_LABEL_DECLARE__ #include "GiftiLabel.h" #undef __GIFTI_LABEL_DECLARE__ #include "CaretLogger.h" using namespace caret; /** * Constructor. * * @param key - key of the label. * @param name - name of label. * */ GiftiLabel::GiftiLabel( const int32_t key, const AString& name) : CaretObject() { this->initializeMembersGiftiLabel(); this->key = key; setNamePrivate(name); } /** * Constructor. * * @param key - Key of the label. * @param name - name of label. * @param red - red color component, zero to one. * @param green - green color component, zero to one. * @param blue - blue color component, zero to one. * @param alpha - alpha color component, zero to one. * */ GiftiLabel::GiftiLabel( const int32_t key, const AString& name, const float red, const float green, const float blue, const float alpha) : CaretObject() { this->initializeMembersGiftiLabel(); this->key = key; setNamePrivate(name); this->red = colorClamp(red); this->green = colorClamp(green); this->blue = colorClamp(blue); this->alpha = colorClamp(alpha); } /** * Constructor. * * @param key - Key of the label. * @param name - name of label. * @param red - red color component, zero to one. * @param green - green color component, zero to one. * @param blue - blue color component, zero to one. * @param alpha - alpha color component, zero to one. * */ GiftiLabel::GiftiLabel(const int32_t key, const AString& name, const float red, const float green, const float blue, const float alpha, const float x, const float y, const float z) : CaretObject() { this->initializeMembersGiftiLabel(); this->key = key; setNamePrivate(name); this->red = colorClamp(red); this->green = colorClamp(green); this->blue = colorClamp(blue); this->alpha = colorClamp(alpha); this->x = x; this->y = y; this->z = z; } /** * Constructor. * * @param key - Key of the label. * @param name - name of label. * @param red - red color component, zero to one. * @param green - green color component, zero to one. * @param blue - blue color component, zero to one. * @param alpha - alpha color component, zero to one. * */ GiftiLabel::GiftiLabel( const int32_t key, const AString& name, const double red, const double green, const double blue, const double alpha) : CaretObject() { this->initializeMembersGiftiLabel(); this->key = key; setNamePrivate(name); this->red = colorClamp(red); this->green = colorClamp(green); this->blue = colorClamp(blue); this->alpha = colorClamp(alpha); } /** * Constructor. * * @param key - Key of the label. * @param name - name of label. * @param rgba - red, green, blue, alpha color componenents, zero to one. * */ GiftiLabel::GiftiLabel( const int32_t key, const AString& name, const float rgba[]) : CaretObject() { this->initializeMembersGiftiLabel(); this->key = key; setNamePrivate(name); this->red = colorClamp(rgba[0]); this->green = colorClamp(rgba[1]); this->blue = colorClamp(rgba[2]); this->alpha = colorClamp(rgba[3]); } /** * Constructor. * * @param key - Key of the label. * @param name - name of label. * @param red - red color component, zero to two-fifty-five. * @param green - green color component, zero to two-fifty-five. * @param blue - blue color component, zero to two-fifty-five. * @param alpha - alpha color component, zero to two-fifty-five. * */ GiftiLabel::GiftiLabel( const int32_t key, const AString& name, const int32_t red, const int32_t green, const int32_t blue, const int32_t alpha) : CaretObject() { this->initializeMembersGiftiLabel(); this->key = key; setNamePrivate(name); this->red = colorClamp(red / 255.0); this->green = colorClamp(green / 255.0); this->blue = colorClamp(blue / 255.0); this->alpha = colorClamp(alpha / 255.0); } /** * Constructor. * * @param key - Key of the label. * @param name - name of label. * @param rgba - red, green, blue, alpha color componenents, zero to 255. * */ GiftiLabel::GiftiLabel( const int32_t key, const AString& name, const int32_t rgba[]) : CaretObject() { this->initializeMembersGiftiLabel(); this->key = key; setNamePrivate(name); this->red = colorClamp(rgba[0] / 255.0); this->green = colorClamp(rgba[1] / 255.0); this->blue = colorClamp(rgba[2] / 255.0); this->alpha = colorClamp(rgba[3] / 255.0); } /** * Constructor. * * @param key - Key of the label. * */ GiftiLabel::GiftiLabel( const int32_t key) : CaretObject() { this->initializeMembersGiftiLabel(); this->key = key; if (this->key == 0) { setNamePrivate("???"); } else { std::stringstream str; str << "???" << this->key; setNamePrivate(AString::fromStdString(str.str())); } } /** * Destructor */ GiftiLabel::~GiftiLabel() { } float GiftiLabel::colorClamp(const float& in) { if (in < 0.0f) return 0.0f; if (in > 1.0f) return 1.0f; if (in != in) { CaretLogWarning("GiftiLabel was given NaN as a color, changing to 1.0"); return 1.0f; } return in; } /** * Copy Constructor * @param Object that is copied. */ GiftiLabel::GiftiLabel(const GiftiLabel& o) : CaretObject(o), TracksModificationInterface() { this->initializeMembersGiftiLabel(); this->copyHelper(o); } /** * Assignment operator. */ GiftiLabel& GiftiLabel::operator=(const GiftiLabel& o) { if (this != &o) { CaretObject::operator=(o); this->copyHelper(o); }; return *this; } /** * Helps with copy constructor and assignment operator. */ void GiftiLabel::copyHelper(const GiftiLabel& gl) { this->initializeMembersGiftiLabel(); setNamePrivate(gl.name); this->key = gl.key; this->selected = gl.selected; this->red = gl.red; this->green = gl.green; this->blue = gl.blue; this->alpha = gl.alpha; this->x = gl.x; this->y = gl.y; this->z = gl.z; this->count = 0; m_groupNameSelectionItem = gl.m_groupNameSelectionItem; } /** * Initialize data members. */ void GiftiLabel::initializeMembersGiftiLabel() { this->modifiedFlag = false; this->medialWallNameFlag = false; this->name = ""; this->key = s_invalidLabelKey; this->selected = true; this->red = 1.0; this->green = 1.0; this->blue = 1.0; this->alpha = 1.0; this->x = 0.0; this->y = 0.0; this->z = 0.0; this->count = 0; m_groupNameSelectionItem = NULL; } /** * Determine if two objects are equal. Two GiftiLabels are equal if they * have the same "key". * @param obj Object for comparison. * @return true if equal, else false. * */ bool GiftiLabel::equals(const GiftiLabel& gl) { return (this->key == gl.key); } /** * Compare this label to another label using the indices of the labels. * @param gl - Compare to this GiftiLabel. * @return negative if "this" is less, positive if "this" is greater, * else zero. * */ int32_t GiftiLabel::operator<(const GiftiLabel& gl) { return (this->key < gl.key); } /** * Get the key of this label. * @return key of the label. * */ int32_t GiftiLabel::getKey() const { return this->key; } /** * Set the key of this label. DO NOT call this method on a label * retrieved from the label table. * * @param key - New key for this label. * */ void GiftiLabel::setKey(const int32_t key) { this->key = key; this->setModified(); } /** * Get the name. * @return Name of label. * */ AString GiftiLabel::getName() const { return this->name; } /** * Set the name. * @param name - new name for label. * */ void GiftiLabel::setName(const AString& name) { setNamePrivate(name); this->setModified(); } /** * (1) Sets the name of the label. * (2) Examines the name of the label to see if the label * is a "medial wall" name which is defined as a name that * contains substring "medial", followed by zero or more * characters, followed by the substring "wall". * (3) DOES NOT change the modfified * status for this label. * * @param name * New name for label. */ void GiftiLabel::setNamePrivate(const AString& name) { this->name = name; this->medialWallNameFlag = false; const int32_t medialIndex = name.indexOf("medial", 0, Qt::CaseInsensitive); if (medialIndex >= 0) { const int32_t wallIndex = name.indexOf("wall", 6, Qt::CaseInsensitive); if (wallIndex > medialIndex) { this->medialWallNameFlag = true; } } } /** * @return A string that contains both the key and name * for use in the label editor. */ AString GiftiLabel::getNameAndKeyForLabelEditor() const { const AString keyAndNameText(QString::number(this->key).rightJustified(4, ' ', false) + ": " + (this->name)); return keyAndNameText; } /** * Is this label selected (for display)? * * @return true if label selected for display, else false. * */ bool GiftiLabel::isSelected() const { return this->selected; } /** * Set the label selected (for display). * * @param selected - new selection status. * */ void GiftiLabel::setSelected(const bool selected) { this->selected = selected; } /** * Get the color components. * @param rgbaOut four dimensional array into which are loaded, * red, green, blue, and alpha components ranging 0.0. to 1.0. * */ void GiftiLabel::getColor(float rgbaOut[4]) const { rgbaOut[0] = this->red; rgbaOut[1] = this->green; rgbaOut[2] = this->blue; rgbaOut[3] = this->alpha; } /** * Set the color components. * * @param rgba - A four-dimensional array of floats containing the red, * green, blue, and alpha components with values ranging from 0.0 to 1.0. * */ void GiftiLabel::setColor(const float rgba[4]) { this->red = colorClamp(rgba[0]); this->green = colorClamp(rgba[1]); this->blue = colorClamp(rgba[2]); this->alpha = colorClamp(rgba[3]); this->setModified(); } /** * Get the default color. * * @param Output with a four-dimensional array of floats * containing the red, green, blue, and alpha components with values * ranging from 0.0 to 1.0. */ void GiftiLabel::getDefaultColor(float rgbaOut[4]) { rgbaOut[0] = 1.0; rgbaOut[1] = 1.0; rgbaOut[2] = 1.0; rgbaOut[3] = 1.0; } /** * Get the red color component for this label. * @return red color component. * */ float GiftiLabel::getRed() const { return this->red; } /** * Get the green color component for this label. * @return green color component. * */ float GiftiLabel::getGreen() const { return this->green; } /** * Get the blue color component for this label. * @return blue color component. * */ float GiftiLabel::getBlue() const { return this->blue; } /** * Get the alpha color component for this label. * @return alpha color component. * */ float GiftiLabel::getAlpha() const { return this->alpha; } /** * Get the X-Coordinate. * @return * The X-coordinate. */ float GiftiLabel::getX() const { return this->x; } /** * Get the Y-Coordinate. * @return * The Y-coordinate. */ float GiftiLabel::getY() const { return this->y; } /** * Get the Z-Coordinate. * @return * The Z-coordinate. */ float GiftiLabel::getZ() const { return this->z; } /** * Get the XYZ coordiantes. * @param xyz * Array into which coordinates are loaded. */ void GiftiLabel::getXYZ(float xyz[3]) const { xyz[0] = this->x; xyz[1] = this->y; xyz[2] = this->z; } /** * Set the X-coordinate. * @param x * New value for X-coordinate. */ void GiftiLabel::setX(const float x) { this->x = x; this->setModified(); } /** * Set the Y-coordinate. * @param y * New value for Y-coordinate. */ void GiftiLabel::setY(const float y) { this->y = y; this->setModified(); } /** * Set the Z-coordinate. * @param z * New value for Z-coordinate. */ void GiftiLabel::setZ(const float z) { this->z = z; this->setModified(); } /** * Set the XYZ coordinates. * @param xyz * Array containing XYZ coordiantes. */ void GiftiLabel::setXYZ(const float xyz[3]) { this->x = xyz[0]; this->y = xyz[1]; this->z = xyz[2]; this->setModified(); } /** * Set this object has been modified. * */ void GiftiLabel::setModified() { this->modifiedFlag = true; } /** * Set this object as not modified. Object should also * clear the modification status of its children. * */ void GiftiLabel::clearModified() { this->modifiedFlag = false; } /** * Get the modification status. Returns true if this object or * any of its children have been modified. * @return - The modification status. * */ bool GiftiLabel::isModified() const { return this->modifiedFlag; } /** * Get information about this label. * * @return Information about the label. * */ AString GiftiLabel::toString() const { AString s; s += "[GiftiLabel=(key=" + AString::number(this->getKey()) + "," + this->getName() + "," + AString::number(this->getRed()) + "," + AString::number(this->getGreen()) + "," + AString::number(this->getBlue()) + "," + AString::number(this->getAlpha()) + "," + AString::number(this->getX()) + "," + AString::number(this->getY()) + "," + AString::number(this->getZ()) + ") "; return s; } /** * Get the count. * @return Count value. * */ int32_t GiftiLabel::getCount() const { return this->count; } /** * Set the count. * @param count - new value for count. * */ void GiftiLabel::setCount(const int32_t count) { this->count = count; } /** * Increment the count. * */ void GiftiLabel::incrementCount() { this->count++; } bool GiftiLabel::matches(const GiftiLabel& rhs, const bool checkColor, const bool checkCoord) const { if (key != rhs.key) return false; if (name != rhs.name) return false; if (checkColor) { if (red != rhs.red) return false; if (green != rhs.green) return false; if (blue != rhs.blue) return false; if (alpha != rhs.alpha) return false; } if (checkCoord) { if (x != rhs.x) return false; if (y != rhs.y) return false; if (z != rhs.z) return false; } return true; } /** * Set the selection item for the group/name hierarchy. * * @param item * The selection item from the group/name hierarchy. */ void GiftiLabel::setGroupNameSelectionItem(GroupAndNameHierarchyItem* item) { m_groupNameSelectionItem = item; } /** * @return The selection item for the Group/Name selection hierarchy. * May be NULL in some circumstances. */ const GroupAndNameHierarchyItem* GiftiLabel::getGroupNameSelectionItem() const { return m_groupNameSelectionItem; } /** * @return The selection item for the Group/Name selection hierarchy. * May be NULL in some circumstances. */ GroupAndNameHierarchyItem* GiftiLabel::getGroupNameSelectionItem() { return m_groupNameSelectionItem; } connectome-workbench-1.2.3+git41-gc4c6c90/src/FilesBase/GiftiLabel.h000066400000000000000000000154661300200146000245700ustar00rootroot00000000000000#ifndef __GIFTILABEL_H__ #define __GIFTILABEL_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 "TracksModificationInterface.h" #include #include #include namespace caret { class GroupAndNameHierarchyItem; /** * Represents a GIFTI Label. */ class GiftiLabel : public CaretObject, TracksModificationInterface { public: GiftiLabel( const int32_t key, const AString& name); explicit GiftiLabel( const int32_t key, const AString& name, const float red, const float green, const float blue, const float alpha); explicit GiftiLabel( const int32_t key, const AString& name, const float red, const float green, const float blue, const float alpha, const float x, const float y, const float z); explicit GiftiLabel( const int32_t key, const AString& name, const double red, const double green, const double blue, const double alpha); GiftiLabel( const int32_t key, const AString& name, const float rgba[]); explicit GiftiLabel( const int32_t key, const AString& name, const int32_t red, const int32_t green, const int32_t blue, const int32_t alpha); GiftiLabel( const int32_t key, const AString& name, const int32_t rgba[]); GiftiLabel(const int32_t key); GiftiLabel(const GiftiLabel& gl); public: GiftiLabel& operator=(const GiftiLabel& gl); virtual ~GiftiLabel(); private: static float colorClamp(const float& in); void copyHelper(const GiftiLabel& o); void initializeMembersGiftiLabel(); public: int32_t hashCode(); bool equals(const GiftiLabel&); int32_t operator<(const GiftiLabel& gl); int32_t getKey() const; void setKey(const int32_t key); AString getName() const; /** * @return True if the name of the label is detected * to be a "medial wall" name. */ inline bool isMedialWallName() const { return this->medialWallNameFlag; } void setName(const AString& name); AString getNameAndKeyForLabelEditor() const; bool isSelected() const; void setSelected(const bool selected); void getColor(float rgbaOut[4]) const; void setColor(const float rgba[4]); static void getDefaultColor(float rgbaOut[4]); float getRed() const; float getGreen() const; float getBlue() const; float getAlpha() const; float getX() const; float getY() const; float getZ() const; void getXYZ(float xyz[3]) const; void setX(const float x); void setY(const float y); void setZ(const float z); void setXYZ(const float xyz[3]); void setModified(); void clearModified(); bool isModified() const; AString toString() const; int32_t getCount() const; void setCount(const int32_t count); void incrementCount(); bool matches(const GiftiLabel& rhs, const bool checkColor = false, const bool checkCoord = false) const; void setGroupNameSelectionItem(GroupAndNameHierarchyItem* item); const GroupAndNameHierarchyItem* getGroupNameSelectionItem() const; GroupAndNameHierarchyItem* getGroupNameSelectionItem(); /** * @return The invalid label key. */ static inline int32_t getInvalidLabelKey() { return s_invalidLabelKey; } private: void setNamePrivate(const AString& name); /**tracks modification status (DO NOT CLONE) */ bool modifiedFlag; /** * Name of label * DO NOT set directly with assignment operation. * Use setName() or setNamePrivate() so that the * medial wall name flag is updated. */ AString name; int32_t key; bool selected; bool medialWallNameFlag; float red; float green; float blue; float alpha; float x; float y; float z; /**Used to count nodes/voxel using label (not saved in file) */ int32_t count; /** Selection status of this label in the map/label hierarchy */ mutable GroupAndNameHierarchyItem* m_groupNameSelectionItem; /** The invalid label key */ const static int32_t s_invalidLabelKey; }; #ifdef __GIFTI_LABEL_DECLARE__ const int32_t GiftiLabel::s_invalidLabelKey = std::numeric_limits::min(); //const int32_t GiftiLabel::s_invalidLabelKey = -2147483648; #endif // __GIFTI_LABEL_DECLARE__ } // namespace #endif // __GIFTILABEL_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/FilesBase/GiftiLabelTable.cxx000066400000000000000000001446671300200146000261210ustar00rootroot00000000000000/*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 "AStringNaturalComparison.h" #include "CaretAssert.h" #include "CaretLogger.h" #include "GiftiLabel.h" #include "GiftiLabelTable.h" #include "GiftiXmlElements.h" #include "StringTableModel.h" #include "XmlWriter.h" using namespace caret; /** * Constructor. * */ GiftiLabelTable::GiftiLabelTable() : CaretObject() { this->initializeMembersGiftiLabelTable(); clear();//actually adds the 0: ??? label } /** * Destructor */ GiftiLabelTable::~GiftiLabelTable() { for (LABELS_MAP_CONST_ITERATOR iter = this->labelsMap.begin(); iter != labelsMap.end(); iter++) { delete iter->second; } this->labelsMap.clear(); } /** * Copy Constructor * @param Object that is copied. */ GiftiLabelTable::GiftiLabelTable(const GiftiLabelTable& glt) : CaretObject(glt), TracksModificationInterface() { this->initializeMembersGiftiLabelTable(); this->copyHelper(glt); } /** * Assignment operator. */ GiftiLabelTable& GiftiLabelTable::operator=(const GiftiLabelTable& glt) { if (this != &glt) { CaretObject::operator=(glt); this->copyHelper(glt); }; return *this; } /** * Helps with copy constructor and assignment operator. */ void GiftiLabelTable::copyHelper(const GiftiLabelTable& glt) { this->clear(); for (LABELS_MAP_CONST_ITERATOR iter = glt.labelsMap.begin(); iter != glt.labelsMap.end(); iter++) { GiftiLabel* myLabel = this->getLabel(iter->second->getKey()); if (myLabel != NULL) { *myLabel = *(iter->second); } else { addLabel(iter->second); } } } void GiftiLabelTable::initializeMembersGiftiLabelTable() { this->modifiedFlag = false; m_tableModelColumnCount = 0; m_tableModelColumnIndexKey = m_tableModelColumnCount++; m_tableModelColumnIndexName = m_tableModelColumnCount++; m_tableModelColumnIndexColorSwatch = m_tableModelColumnCount++; m_tableModelColumnIndexRed = m_tableModelColumnCount++; m_tableModelColumnIndexGreen = m_tableModelColumnCount++; m_tableModelColumnIndexBlue = m_tableModelColumnCount++; } /** * Clear the labelTable. * */ void GiftiLabelTable::clear() { for (LABELS_MAP_CONST_ITERATOR iter = this->labelsMap.begin(); iter != labelsMap.end(); iter++) { delete iter->second; } this->labelsMap.clear(); GiftiLabel gl(0, "???", 1.0, 1.0, 1.0, 0.0); this->addLabel(&gl); this->modifiedFlag = false; } /** * Append a label table to this label table. Since labels may be * duplicated, the map returned that converts the keys of * the appended LabelTable to keys for "this" label table. * * @param lt Label table that is to be appended. * * @return A map where the keys are the keys in the label table * that is passed as a parameter and the values are the keys * into "this" label table. * */ std::map GiftiLabelTable::append(const GiftiLabelTable& glt) { std::map keyConverterMap; for (LABELS_MAP_CONST_ITERATOR iter = glt.labelsMap.begin(); iter != glt.labelsMap.end(); iter++) { int32_t key = iter->first; int32_t newKey = this->addLabel(iter->second); keyConverterMap.insert(std::make_pair(key, newKey)); } return keyConverterMap; } /** * Add a label. If a label with the name exists, its colors * are replaced with these color components. * @param labelName Name of label. * @param red Red color component ranging 0.0 to 1.0. * @param green Green color component ranging 0.0 to 1.0. * @param blue Blue color component ranging 0.0 to 1.0. * @param alpha Alpha color component ranging 0.0 to 1.0. * @return Index of the existing label, or, if no label * exists with name, index of new label. * */ int32_t GiftiLabelTable::addLabel( const AString& labelName, const float red, const float green, const float blue, const float alpha) { const GiftiLabel gl(GiftiLabel::getInvalidLabelKey(), labelName, red, green, blue, alpha); return this->addLabel(&gl); } /** * Add a label. If a label with the name exists, its colors * are replaced with these color components. * @param labelName Name of label. * @param red Red color component ranging 0.0 to 1.0. * @param green Green color component ranging 0.0 to 1.0. * @param blue Blue color component ranging 0.0 to 1.0. * @return Index of the existing label, or, if no label * exists with name, index of new label. * */ int32_t GiftiLabelTable::addLabel( const AString& labelName, const float red, const float green, const float blue) { return this->addLabel(labelName, red, green, blue, 1.0f); } /** * Add a label. If a label with the name exists, its colors * are replaced with these color components. * @param labelName Name of label. * @param red Red color component ranging 0 to 255. * @param green Green color component ranging 0 to 255. * @param blue Blue color component ranging 0 to 255. * @param alpha Alpha color component ranging 0 to 255. * @return Index of the existing label, or, if no label * exists with name, index of new label. * */ int32_t GiftiLabelTable::addLabel( const AString& labelName, const int32_t red, const int32_t green, const int32_t blue, const int32_t alpha) { const GiftiLabel gl(GiftiLabel::getInvalidLabelKey(), labelName, red, green, blue, alpha); return this->addLabel(&gl); } /** * Add a label. If a label with the name exists, its colors * are replaced with these color components. * @param labelName Name of label. * @param red Red color component ranging 0 to 255. * @param green Green color component ranging 0 to 255. * @param blue Blue color component ranging 0 to 255. * @return Index of the existing label, or, if no label * exists with name, index of new label. * */ int32_t GiftiLabelTable::addLabel( const AString& labelName, const int32_t red, const int32_t green, const int32_t blue) { return this->addLabel(labelName, red, green, blue, 255); } /** * Add a label to the label table. If the label's key is already in * the label table, a new key is created. If a label of the same * name already exists, the key of the existing label is returned * and its color is overridden. * @param glIn - Label to add. * @return Key of the label, possibly different than its original key. * */ int32_t GiftiLabelTable::addLabel(const GiftiLabel* glIn) { /* * First see if a label with the same name already exists */ int32_t key = this->getLabelKeyFromName(glIn->getName()); /* * If no label with the name exists, get the key * (which may be invalid) from the input label, * and check that nothing uses that key */ if (key == GiftiLabel::getInvalidLabelKey()) { int32_t tempkey = glIn->getKey(); LABELS_MAP_ITERATOR iter = this->labelsMap.find(tempkey); if (iter == labelsMap.end()) { key = tempkey; } } /* * Still need a key, find an unused key */ if (key == GiftiLabel::getInvalidLabelKey()) { key = this->generateUnusedKey(); GiftiLabel* gl = new GiftiLabel(*glIn); gl->setKey(key); this->labelsMap.insert(std::make_pair(key, gl)); return key; } if (key == 0) { issueLabelKeyZeroWarning(glIn->getName()); // if (glIn->getName() != "???") { // CaretLogWarning("Label 0 overridden!"); // } } LABELS_MAP_ITERATOR iter = this->labelsMap.find(key); if (iter != this->labelsMap.end()) { /* * Update existing label */ GiftiLabel* gl = iter->second; gl->setName(glIn->getName()); float rgba[4]; glIn->getColor(rgba); gl->setColor(rgba); key = iter->first; } else { /* * Insert a new label */ this->labelsMap.insert(std::make_pair(key, new GiftiLabel(*glIn))); } return key; } /** * Generate an unused key. * @return An unused key. */ int32_t GiftiLabelTable::generateUnusedKey() const { const int32_t numKeys = labelsMap.size(); LABELS_MAP::const_reverse_iterator rbegin = labelsMap.rbegin();//reverse begin is largest key if (numKeys > 0 && rbegin->first > 0)//there is at least one positive key { if (rbegin->first < numKeys) { CaretAssert(labelsMap.find(rbegin->first + 1) == labelsMap.end()); return rbegin->first + 1;//keys are compact unless negatives exist, in which case consider it "compact enough" if positive holes equal number of negative keys } else { LABELS_MAP::const_iterator begin = labelsMap.begin(); if (begin->first == 1 && rbegin->first == numKeys) { CaretAssert(labelsMap.find(rbegin->first + 1) == labelsMap.end()); return rbegin->first + 1;//keys are compact but missing 0, do not return 0, so return next } else {//there aren't enough negatives to make up for the missing, search for a hole in the positives LABELS_MAP::const_iterator iter = labelsMap.upper_bound(0);//start with first positive int32_t curVal = 0;//if it isn't one, we can stop early while (iter != labelsMap.end() && iter->first == curVal + 1)//it should NEVER hit end(), due to above checks, but if it did, it would return rbegin->first + 1 { curVal = iter->first; ++iter; } CaretAssert(labelsMap.find(curVal + 1) == labelsMap.end()); return curVal + 1; } } } else { CaretAssert(labelsMap.find(1) == labelsMap.end()); return 1;//otherwise, no keys exist or all keys are non-positive, return 1 } /*int32_t numKeys = labelsMap.size(); LABELS_MAP_CONST_ITERATOR myend = labelsMap.end(); if (labelsMap.upper_bound(numKeys - 1) == myend) {//returns a valid iterator only if there is no strictly greater key - zero key is assumed to exist, being the ??? special palette, no negatives exist return numKeys;//keys are therefore compact, return the next one } if (labelsMap.find(0) == myend && labelsMap.upper_bound(numKeys) == myend) {//similar check, but in case label 0 doesn't exist (but don't override it) return numKeys + 1; } std::vector scratch; scratch.resize(numKeys);//guaranteed to have at least one missing spot within this range other than zero, or one of above tests would have returned for (int32_t i = 0; i < numKeys; ++i) { scratch[i] = 0; } for (LABELS_MAP_CONST_ITERATOR iter = labelsMap.begin(); iter != myend; ++iter) { if (iter->first >= 0 && iter->first < numKeys) {//dont try to mark above the range scratch[iter->first] = 1; } } for (int32_t i = 1; i < numKeys; ++i) {//NOTE: start at 1! 0 is reserved (sort of) if (scratch[i] == 0) { return i; } } CaretAssertMessage(false, "generateUnusedKey() failed for unknown reasons"); return 0;//should never happen//*/ /*std::set keys = getKeys(); int32_t newKey = 1; bool found = false; while (found == false) { if (std::find(keys.begin(), keys.end(), newKey) == keys.end()) { found = true; } else { newKey++; } } return newKey;//*/ } /** * Remove the label with the specified key. * @param key - key of label. * */ void GiftiLabelTable::deleteLabel(const int32_t key) { if (key == 0) {//key 0 is reserved (sort of) CaretLogWarning("Label 0 DELETED!"); } LABELS_MAP_ITERATOR iter = this->labelsMap.find(key); if (iter != this->labelsMap.end()) { GiftiLabel* gl = iter->second; this->labelsMap.erase(iter); delete gl; setModified(); } } /** * Remove a label from the label table. * This method WILL DELETE the label passed * in so the caller should never use the parameter * passed after this call. * @param label - label to remove. * */ void GiftiLabelTable::deleteLabel(const GiftiLabel* label) { if (label->getKey() == 0) {//key 0 is reserved (sort of) CaretLogWarning("Label 0 DELETED!"); } for (LABELS_MAP_ITERATOR iter = this->labelsMap.begin(); iter != this->labelsMap.end(); iter++) { if (iter->second == label) { this->labelsMap.erase(iter); setModified(); break; } } delete label; } /** * Remove unused labels from the label table. Note that the unassigned * label is not removed, even if it is unused. * * @param usedLabelKeys - Color keys that are in use. * */ void GiftiLabelTable::deleteUnusedLabels(const std::set& usedLabelKeys) { LABELS_MAP newMap; int32_t unassignedKey = getUnassignedLabelKey(); for (LABELS_MAP_ITERATOR iter = this->labelsMap.begin(); iter != this->labelsMap.end(); iter++) { int32_t key = iter->first; GiftiLabel* gl = iter->second; if (key == unassignedKey || usedLabelKeys.find(key) != usedLabelKeys.end()) {//unassigned key gets a free pass newMap.insert(std::make_pair(key, gl)); } else { delete gl; } } this->labelsMap = newMap; this->setModified(); } /** * Insert the label using the labels key. * @param labelIn - Label to insert (replaces an existing label * with the same key). * */ void GiftiLabelTable::insertLabel(const GiftiLabel* labelIn) { GiftiLabel* label = new GiftiLabel(*labelIn); int32_t key = label->getKey(); if (key == GiftiLabel::getInvalidLabelKey()) { key = this->generateUnusedKey(); label->setKey(key); } if (key == 0) {//key 0 is reserved (sort of) issueLabelKeyZeroWarning(label->getName()); } /* * Note: A map DOES NOT replace an existing key, so it * must be deleted and then added. */ LABELS_MAP_ITERATOR keyPos = this->labelsMap.find(label->getKey()); if (keyPos != this->labelsMap.end()) { GiftiLabel* gl = keyPos->second; this->labelsMap.erase(keyPos); delete gl; } this->labelsMap.insert(std::make_pair(label->getKey(), label)); this->setModified(); } /** * Get the key of a lable from its name. * @param name Name to search for. * @return Key of Name or GiftiLabel::getInvalidLabelKey() if not found. * */ int32_t GiftiLabelTable::getLabelKeyFromName(const AString& name) const { LABELS_MAP newMap; for (LABELS_MAP_CONST_ITERATOR iter = this->labelsMap.begin(); iter != this->labelsMap.end(); iter++) { int32_t key = iter->first; GiftiLabel* gl = iter->second; if (gl->getName() == name) { return key; } } return GiftiLabel::getInvalidLabelKey(); } /** * Get a GIFTI Label from its name. * @param labelName - Name of label that is sought. * @return Reference to label with name or null if no matching label. * */ const GiftiLabel* GiftiLabelTable::getLabel(const AString& labelName) const { LABELS_MAP newMap; for (LABELS_MAP_CONST_ITERATOR iter = this->labelsMap.begin(); iter != this->labelsMap.end(); iter++) { GiftiLabel* gl = iter->second; if (gl->getName() == labelName) { return gl; } } return NULL; } /** * Get a GIFTI Label from its name. * @param labelName - Name of label that is sought. * @return Reference to label with name or null if no matching label. * */ GiftiLabel* GiftiLabelTable::getLabel(const AString& labelName) { LABELS_MAP newMap; for (LABELS_MAP_CONST_ITERATOR iter = this->labelsMap.begin(); iter != this->labelsMap.end(); iter++) { GiftiLabel* gl = iter->second; if (gl->getName() == labelName) { return gl; } } return NULL; } /** * Get the label whose name is the longest substring of "name" beginning * at the first character. * * @param name - name for which best matching label is sought. * @return Reference to best matching label or null if not found. * */ const GiftiLabel* GiftiLabelTable::getLabelBestMatching(const AString& name) const { GiftiLabel* bestMatchingLabel = NULL; int32_t bestMatchLength = -1; LABELS_MAP newMap; for (LABELS_MAP_CONST_ITERATOR iter = this->labelsMap.begin(); iter != this->labelsMap.end(); iter++) { GiftiLabel* gl = iter->second; AString labelName = gl->getName(); if (name.startsWith(labelName)) { const int32_t len = labelName.length(); if (len > bestMatchLength) { bestMatchLength = len; bestMatchingLabel = iter->second; } } } return bestMatchingLabel; } /** * Get the GiftiLabel at the specified key. * * @param key - Key of GiftiLabel entry. * @return The GiftiLabel at the specified key or null if the * there is not a label at the specified key. * */ const GiftiLabel* GiftiLabelTable::getLabel(const int32_t key) const { LABELS_MAP_CONST_ITERATOR iter = this->labelsMap.find(key); if (iter != this->labelsMap.end()) { return iter->second; } return NULL; } /** * Get the GiftiLabel at the specified key. * * @param key - Key of GiftiLabel entry. * @return The GiftiLabel at the specified key or null if the * there is not a label at the specified key. */ GiftiLabel* GiftiLabelTable::getLabel(const int32_t key) { LABELS_MAP_ITERATOR iter = this->labelsMap.find(key); if (iter != this->labelsMap.end()) { return iter->second; } return NULL; } /** * Get the key for the unassigned label. * @return Index of key for unassigned label. * A valid key will always be returned. * */ int32_t GiftiLabelTable::getUnassignedLabelKey() const { const GiftiLabel* gl = this->getLabel("???"); if (gl != NULL) { return gl->getKey(); } /* * Remove 'constness' from this object so that the * label can be added. */ GiftiLabelTable* glt = (GiftiLabelTable*)this; const int32_t key = glt->addLabel("???", 0.0f, 0.0f, 0.0f, 0.0f); return key; } /** * Get the number of labels. This value is one greater than the last * label key. Note that not every key may have a label. If there * are no labels this returns 0. * @return Number of labels. * */ int32_t GiftiLabelTable::getNumberOfLabels() const { return this->labelsMap.size(); } /** * Get the name of the label at the key. If there is no label at the * key an empty string is returned. * @param key - key of label. * @return Name of label at inkeydex. * */ AString GiftiLabelTable::getLabelName(const int32_t key) const { LABELS_MAP_CONST_ITERATOR iter = this->labelsMap.find(key); if (iter != this->labelsMap.end()) { const AString name = iter->second->getName(); return name; } return ""; } /** * Set the name of a label. * @param key - key of label. * @param name - new name of label. * */ void GiftiLabelTable::setLabelName( const int32_t key, const AString& name) { if (key == 0) { if (name != "???") { issueLabelKeyZeroWarning(name); // CaretLogWarning("Label 0 modified!"); } } LABELS_MAP_ITERATOR iter = this->labelsMap.find(key); if (iter != this->labelsMap.end()) { iter->second->setName(name); } } /** * Set a label. If a label with the specified key exists, * it is replaced. * * @param key Key for label. * @param name Name of label. * @param red Red color component. * @param green Green color component. * @param blue Blue color component. * @param alpha Alpha color component. * */ void GiftiLabelTable::setLabel( const int32_t key, const AString& name, const float red, const float green, const float blue, const float alpha) { if (key == 0) { if (name != "???") { issueLabelKeyZeroWarning(name); // CaretLogWarning("Label 0 modified!"); } } LABELS_MAP_ITERATOR iter = this->labelsMap.find(key); if (iter != this->labelsMap.end()) { GiftiLabel* gl = iter->second; gl->setName(name); float rgba[4] = { red, green, blue, alpha }; gl->setColor(rgba); } else { GiftiLabel gl(key, name, red, green, blue, alpha); this->addLabel(&gl); } } /** * Set a label. If a label with the specified key exists, * it is replaced. * * @param key Key for label. * @param name Name of label. * @param red Red color component. * @param green Green color component. * @param blue Blue color component. * @param alpha Alpha color component. * @param x The X-coordinate. * @param y The Y-coordinate. * @param z The Z-coordinate. * */ void GiftiLabelTable::setLabel(const int32_t key, const AString& name, const float red, const float green, const float blue, const float alpha, const float x, const float y, const float z) { if (key == 0) { if (name != "???") { issueLabelKeyZeroWarning(name); //CaretLogWarning("Label 0 modified!"); } } LABELS_MAP_ITERATOR iter = this->labelsMap.find(key); if (iter != this->labelsMap.end()) { GiftiLabel* gl = iter->second; gl->setName(name); float rgba[4] = { red, green, blue, alpha }; gl->setColor(rgba); gl->setX(x); gl->setY(y); gl->setZ(z); } else { GiftiLabel gl(key, name, red, green, blue, alpha, x, y, z); this->addLabel(&gl); } } /** * Get the selection status of the label at the specified key. If there * is no label at the key, false is returned. * @param key - key of label * @return selection status of label. * */ bool GiftiLabelTable::isLabelSelected(const int32_t key) const { LABELS_MAP_CONST_ITERATOR iter = this->labelsMap.find(key); if (iter != this->labelsMap.end()) { return iter->second->isSelected(); } return false; } /** * Set the selection status of a label. * @param key - key of label. * @param sel - new selection status. * */ void GiftiLabelTable::setLabelSelected( const int32_t key, const bool sel) { LABELS_MAP_ITERATOR iter = this->labelsMap.find(key); if (iter != this->labelsMap.end()) { iter->second->setSelected(sel); } } /** * Set the selection status for all labels. * @param newStatus New selection status. * */ void GiftiLabelTable::setSelectionStatusForAllLabels(const bool newStatus) { for (LABELS_MAP_ITERATOR iter = this->labelsMap.begin(); iter != this->labelsMap.end(); iter++) { GiftiLabel* gl = iter->second; gl->setSelected(newStatus); } } /** * Get the alpha color component for a label. If the key is not a * valid label, an alpha of zero is returned. * @param key - Key of label. * @return Alpha for label or zero if invalid key. * */ float GiftiLabelTable::getLabelAlpha(const int32_t key) const { const GiftiLabel* gl = this->getLabel(key); if (gl != NULL) { return gl->getAlpha(); } return 0.0; } /** * Get the color for a label. * @param key - key of label. * @return Its color components or null if it is an invalid key. * */ void GiftiLabelTable::getLabelColor(const int32_t key, float rgbaOut[4]) const { const GiftiLabel* gl = this->getLabel(key); if (gl != NULL) { gl->getColor(rgbaOut); } } /** * Set the color of a label. * @param key - key of label. * @param color - new color of label. * */ void GiftiLabelTable::setLabelColor( const int32_t key, const float color[]) { if (key == 0) { CaretLogFiner("Label 0 color changed"); } LABELS_MAP_ITERATOR iter = this->labelsMap.find(key); if (iter != this->labelsMap.end()) { GiftiLabel* gl = iter->second; gl->setColor(color); } } /** * Get the label keys sorted by label name. * * @return Vector containing label keys sorted by name. * */ std::vector GiftiLabelTable::getLabelKeysSortedByName() const { /* * Use map to sort by name * If AStringNaturalComparison crashes, temporarily remove * as the third template parameter. */ std::map nameToKeyMap; for (LABELS_MAP_CONST_ITERATOR iter = this->labelsMap.begin(); iter != this->labelsMap.end(); iter++) { nameToKeyMap.insert(std::make_pair(iter->second->getName(), iter->first)); } std::vector keysSortedByName; for (std::map::iterator iter = nameToKeyMap.begin(); iter != nameToKeyMap.end(); iter++) { keysSortedByName.push_back(iter->second); } return keysSortedByName; } /** * Reset the label counts to zero. * */ void GiftiLabelTable::resetLabelCounts() { for (LABELS_MAP_ITERATOR iter = this->labelsMap.begin(); iter != this->labelsMap.end(); iter++) { if (iter != this->labelsMap.end()) { GiftiLabel* gl = iter->second; gl->setCount(0); } } } /** * @return True if this label table contains a label with the * name of the medial wall, else false. */ bool GiftiLabelTable::hasMedialWallLabel() const { for (LABELS_MAP_CONST_ITERATOR iter = this->labelsMap.begin(); iter != this->labelsMap.end(); iter++) { const GiftiLabel* label = iter->second; CaretAssert(label); if (label->isMedialWallName()) { return true; } } return false; } ///** // * @return Are there any labels that have an invalid group/name // * hierarchy settings. This can be caused by changing the name // * of a label or its color. // */ //bool //GiftiLabelTable::hasLabelsWithInvalidGroupNameHierarchy() const //{ // for (LABELS_MAP_CONST_ITERATOR iter = this->labelsMap.begin(); // iter != this->labelsMap.end(); // iter++) { // if (iter != this->labelsMap.end()) { // GiftiLabel* gl = iter->second; // if (gl->getGroupNameSelectionItem() == NULL) { // return true; // break; // } // } // } // // return false; //} /** * Remove labels that have the 'count' attribute * set to zero. * Note the ??? label is not removed. */ void GiftiLabelTable::removeLabelsWithZeroCounts() { const int32_t unknownKey = getUnassignedLabelKey(); /** * First, iterate through the map to find * labels that have the 'count' attribute * set to zero. Delete the label and save * the key since one cannot erase the map * element without confusing the iterator * and causing a crash. */ std::vector unusedkeys; for (LABELS_MAP_ITERATOR iter = this->labelsMap.begin(); iter != this->labelsMap.end(); iter++) { const GiftiLabel* gl = iter->second; if (gl->getCount() <= 0) { /* * Get key and save it. */ const int32_t key = iter->first; if (key != unknownKey) { unusedkeys.push_back(key); /* * Delete the label. */ delete gl; iter->second = NULL; } } } /* * Now, remove all of the elements in the * map for the keys that were found in the * previous loop. */ bool isLabelRemoved = false; for (std::vector::iterator iter = unusedkeys.begin(); iter != unusedkeys.end(); iter++) { const int32_t key = *iter; this->labelsMap.erase(key); isLabelRemoved = true; } if (isLabelRemoved) { this->setModified(); } } /** * Create labels for the keys with generated names and colors. * @param newKeys - Keys that need labels. * */ void GiftiLabelTable::createLabelsForKeys(const std::set& newKeys) { AString namePrefix = "Name"; int32_t nameCount = 0; int32_t colorCounter = 0; for (std::set::iterator iter = newKeys.begin(); iter != newKeys.end(); iter++) { int32_t key = *iter; if (this->getLabel(key) == NULL) { bool found = false; AString name; while (! found) { std::stringstream str; str << namePrefix.toStdString() << "_" << nameCount; nameCount++; name = AString::fromStdString(str.str()); if (this->getLabel(name) == NULL) { found = true; } float red = 0.0f; float green = 0.0f; float blue = 0.0f; float alpha = 1.0f; switch (colorCounter) { case 0: red = 1.0f; break; case 1: red = 1.0f; blue = 0.5f; break; case 2: red = 1.0f; blue = 1.0f; break; case 3: red = 1.0f; green = 0.5f; break; case 4: red = 1.0f; green = 0.5f; blue = 0.5f; break; case 5: red = 1.0f; green = 0.5f; blue = 1.0f; break; case 6: blue = 0.5f; break; case 7: blue = 1.0f; break; case 8: red = 0.5f; break; case 9: red = 0.5f; blue = 0.5f; break; case 10: red = 0.5f; blue = 1.0f; break; case 11: red = 0.5f; green = 0.5f; break; case 12: red = 0.5f; green = 0.5f; blue = 0.5f; break; case 13: red = 0.5f; green = 0.5f; blue = 1.0f; break; case 14: red = 0.5f; green = 1.0f; break; case 15: red = 0.5f; green = 1.0f; blue = 0.5f; break; case 16: red = 0.5f; green = 1.0f; blue = 1.0f; colorCounter = 0; // start over break; } colorCounter++; GiftiLabel* gl = new GiftiLabel(key, name, red, green, blue, alpha); this->addLabel(gl); } } } } /** * Write the metadata in GIFTI XML format. * * @param xmlWriter - output stream * @throws GiftiException if an error occurs while writing. * */ void GiftiLabelTable::writeAsXML(XmlWriter& xmlWriter) { try { // // Write the label tag // xmlWriter.writeStartElement(GiftiXmlElements::TAG_LABEL_TABLE); // // Write the labels // std::set keys = this->getKeys(); for (std::set::const_iterator iter = keys.begin(); iter != keys.end(); iter++) { int key = *iter; const GiftiLabel* label = this->getLabel(key); if (label != NULL) { XmlAttributes attributes; attributes.addAttribute(GiftiXmlElements::ATTRIBUTE_LABEL_KEY, key); float rgba[4]; label->getColor(rgba); attributes.addAttribute(GiftiXmlElements::ATTRIBUTE_LABEL_RED, rgba[0]); attributes.addAttribute(GiftiXmlElements::ATTRIBUTE_LABEL_GREEN, rgba[1]); attributes.addAttribute(GiftiXmlElements::ATTRIBUTE_LABEL_BLUE, rgba[2]); attributes.addAttribute(GiftiXmlElements::ATTRIBUTE_LABEL_ALPHA, rgba[3]); xmlWriter.writeElementCData(GiftiXmlElements::TAG_LABEL, attributes, label->getName()); } } // // Write the closing label tag // xmlWriter.writeEndElement(); } catch (XmlException& e) { throw GiftiException(e); } } void GiftiLabelTable::writeAsXML(QXmlStreamWriter& xmlWriter) const { try { // // Write the label tag // xmlWriter.writeStartElement(GiftiXmlElements::TAG_LABEL_TABLE); // // Write the labels // std::set keys = this->getKeys(); for (std::set::const_iterator iter = keys.begin(); iter != keys.end(); iter++) { int key = *iter; const GiftiLabel* label = this->getLabel(key); if (label != NULL) { xmlWriter.writeStartElement(GiftiXmlElements::TAG_LABEL); xmlWriter.writeAttribute(GiftiXmlElements::ATTRIBUTE_LABEL_KEY, AString::number(key)); float rgba[4]; label->getColor(rgba); xmlWriter.writeAttribute(GiftiXmlElements::ATTRIBUTE_LABEL_RED, AString::number(rgba[0])); xmlWriter.writeAttribute(GiftiXmlElements::ATTRIBUTE_LABEL_GREEN, AString::number(rgba[1])); xmlWriter.writeAttribute(GiftiXmlElements::ATTRIBUTE_LABEL_BLUE, AString::number(rgba[2])); xmlWriter.writeAttribute(GiftiXmlElements::ATTRIBUTE_LABEL_ALPHA, AString::number(rgba[3])); xmlWriter.writeCharacters(label->getName()); xmlWriter.writeEndElement(); } } // // Write the closing label tag // xmlWriter.writeEndElement(); } catch (XmlException& e) { throw GiftiException(e); } } /** * Convert to a string. * * @return String representation of labelTable. * */ AString GiftiLabelTable::toString() const { AString s = "GiftiLabelTable=["; for (LABELS_MAP_CONST_ITERATOR iter = this->labelsMap.begin(); iter != this->labelsMap.end(); iter++) { s += "key="; s += AString::number(iter->first); s += iter->second->toString(); s += ","; } s += "]"; return s; } /** * Get a nicely formatted string for printing. * * @param indentation - use as indentation. * @return String containing label information. * */ AString GiftiLabelTable::toFormattedString(const AString& indentation) const { std::set allKeys = getKeys(); const int32_t numberOfKeys = allKeys.size(); if (numberOfKeys <= 0) { return "Empty"; } int32_t columnCounter = 0; const int32_t COL_INDENT = columnCounter++; const int32_t COL_KEY = columnCounter++; const int32_t COL_NAME = columnCounter++; const int32_t COL_RED = columnCounter++; const int32_t COL_GREEN = columnCounter++; const int32_t COL_BLUE = columnCounter++; const int32_t COL_ALPHA = columnCounter++; const int32_t numberOfTableRows = numberOfKeys + 1; StringTableModel table(numberOfTableRows, columnCounter); int32_t rowIndex = 0; table.setElement(rowIndex, COL_INDENT, // accomplishes indentation indentation); table.setElement(rowIndex, COL_KEY, "KEY"); table.setElement(rowIndex, COL_NAME, "NAME"); table.setElement(rowIndex, COL_RED, "RED"); table.setElement(rowIndex, COL_GREEN, "GREEN"); table.setElement(rowIndex, COL_BLUE, "BLUE"); table.setElement(rowIndex, COL_ALPHA, "ALPHA"); rowIndex++; table.setColumnAlignment(COL_KEY, StringTableModel::ALIGN_RIGHT); table.setColumnAlignment(COL_NAME, StringTableModel::ALIGN_LEFT); table.setColumnAlignment(COL_RED, StringTableModel::ALIGN_RIGHT); table.setColumnAlignment(COL_GREEN, StringTableModel::ALIGN_RIGHT); table.setColumnAlignment(COL_BLUE, StringTableModel::ALIGN_RIGHT); table.setColumnAlignment(COL_ALPHA, StringTableModel::ALIGN_RIGHT); for (std::set::iterator iter = allKeys.begin(); iter != allKeys.end(); iter++) { const int32_t key = *iter; const GiftiLabel* label = getLabel(key); CaretAssertArrayIndex("table", numberOfTableRows, rowIndex); table.setElement(rowIndex, COL_KEY, key); if (label != NULL) { table.setElement(rowIndex, COL_NAME, label->getName()); table.setElement(rowIndex, COL_RED, label->getRed()); table.setElement(rowIndex, COL_GREEN, label->getGreen()); table.setElement(rowIndex, COL_BLUE, label->getBlue()); table.setElement(rowIndex, COL_ALPHA, label->getAlpha()); } else { table.setElement(rowIndex, COL_NAME, "Label Missing"); } rowIndex++; } return table.getInString(); } /** * Read the label table from XML DOM structures. * @param rootNode - the LabelTable node. * @throws GiftiException if an error occurs. * * void GiftiLabelTable::readFromXMLDOM(const Node* rootNode) { } */ /** * Read a LabelTable from a String. The beginning and ending tags must * be the label table tag GiftiXmlElements.TAG_LABEL_TABLE. * @param s - string containing the label table. * @throws GiftiException If there is an error processing the table. * */ void GiftiLabelTable::readFromXmlString(const AString& /*s*/) { CaretAssertMessage(0, "Not implemented yet!"); } void GiftiLabelTable::readFromQXmlStreamReader(QXmlStreamReader& xml) { clear(); bool haveUnassigned = false;//because clear() creates the default "???" label if (!xml.isStartElement() || xml.name() != GiftiXmlElements::TAG_LABEL_TABLE) {//TODO: try to recover instead of erroring? xml.raiseError("tried to read GiftiLabelTable when current element is not " + GiftiXmlElements::TAG_LABEL_TABLE); return; } while (xml.readNextStartElement() && !xml.atEnd()) { if (xml.name() != GiftiXmlElements::TAG_LABEL) { xml.raiseError("unexpected element '" + xml.name().toString() + "' encountered in " + GiftiXmlElements::TAG_LABEL_TABLE); } int key; float rgba[4]; QXmlStreamAttributes myAttrs = xml.attributes(); bool ok = false; QString temp = myAttrs.value(GiftiXmlElements::ATTRIBUTE_LABEL_KEY).toString(); key = temp.toInt(&ok); if (!ok) xml.raiseError("Key attribute of Label missing or noninteger"); temp = myAttrs.value(GiftiXmlElements::ATTRIBUTE_LABEL_RED).toString(); rgba[0] = temp.toFloat(&ok); if (!ok) xml.raiseError("Red attribute of Label missing or not a number"); temp = myAttrs.value(GiftiXmlElements::ATTRIBUTE_LABEL_GREEN).toString(); rgba[1] = temp.toFloat(&ok); if (!ok) xml.raiseError("Green attribute of Label missing or not a number"); temp = myAttrs.value(GiftiXmlElements::ATTRIBUTE_LABEL_BLUE).toString(); rgba[2] = temp.toFloat(&ok); if (!ok) xml.raiseError("Blue attribute of Label missing or not a number"); temp = myAttrs.value(GiftiXmlElements::ATTRIBUTE_LABEL_ALPHA).toString(); if (temp == "") { rgba[3] = 1.0f; } else { rgba[3] = temp.toFloat(&ok); if (!ok) xml.raiseError("Alpha attribute of Label not a number"); } temp = xml.readElementText(); if (xml.hasError()) return; if ((temp == "unknown" || temp == "Unknown") && rgba[3] == 0.0f) { if (haveUnassigned) { CaretLogWarning("found multiple label elements that should be interpreted as unlabeled"); } else { CaretLogFiner("Using '" + temp + "' label as unlabeled key"); haveUnassigned = true; temp = "???";//pretend they are actually our internal unlabeled name } } else if (temp == "???") { if (haveUnassigned) { CaretLogWarning("found multiple label elements that should be interpreted as unlabeled"); } haveUnassigned = true; } setLabel(key, temp, rgba[0], rgba[1], rgba[2], rgba[3]); } } /** * Set this object has been modified. * */ void GiftiLabelTable::setModified() { this->modifiedFlag = true; } /** * Set this object as not modified. Object should also * clear the modification status of its children. * */ void GiftiLabelTable::clearModified() { this->modifiedFlag = false; for (LABELS_MAP_CONST_ITERATOR iter = this->labelsMap.begin(); iter != this->labelsMap.end(); iter++) { iter->second->clearModified(); } } /** * Get the modification status. Returns true if this object or * any of its children have been modified. * @return - The modification status. * */ bool GiftiLabelTable::isModified() const { if (this->modifiedFlag) return true; for (LABELS_MAP_CONST_ITERATOR iter = this->labelsMap.begin(); iter != this->labelsMap.end(); iter++) { if (iter->second->isModified()) return true; } return false; } /** * Get an iterator that steps thought the label keys in * ascending order. * @return An iterator for stepping through the keys in * ascending order. * * Iterator GiftiLabelTable::getKeysIterator() const { return this-> } */ /** * Get the valid keys of the labels in ascending order. * @return A Set containing the valid keys of the label in * ascending order. * */ std::set GiftiLabelTable::getKeys() const { std::set keys; for (std::map::const_iterator iter = this->labelsMap.begin(); iter != this->labelsMap.end(); iter++) { keys.insert(iter->first); } return keys; } void GiftiLabelTable::getKeys(std::vector& keysOut) const { keysOut.reserve(labelsMap.size()); for (std::map::const_iterator iter = this->labelsMap.begin(); iter != this->labelsMap.end(); iter++) { keysOut.push_back(iter->first); } } /** * Get all keys and names. * * @parm keysAndNamesOut * Map containing the pairs of corresponding keys and names. */ void GiftiLabelTable::getKeysAndNames(std::map& keysAndNamesOut) const { keysAndNamesOut.clear(); for (std::map::const_iterator iter = this->labelsMap.begin(); iter != this->labelsMap.end(); iter++) { const GiftiLabel* gl = iter->second; keysAndNamesOut.insert(std::make_pair(iter->first, gl->getName())); } } /** * Change the key of a label from 'currentKey' to 'newKey'. * If a label exists with 'newKey', the label with 'newKey' is removed. * * @param currentKey * Key currently used by the label. * @param newKey * New key for the label. */ void GiftiLabelTable::changeLabelKey(const int32_t currentKey, const int32_t newKey) { /* * Remove a label that uses 'newKey'. */ if (this->labelsMap.find(newKey) != this->labelsMap.end()) { deleteLabel(newKey); } /* * Get the label with 'currentKey' and remove it from the map. */ LABELS_MAP_ITERATOR currentLabelIter = this->labelsMap.find(currentKey); if (currentLabelIter == this->labelsMap.end()) { CaretLogSevere("Attempting to change label key for non-existent label with key=" + AString::number(currentKey)); return; } GiftiLabel* label = currentLabelIter->second; this->labelsMap.erase(currentKey); /* * Change the lable's key from 'currentKey' to 'newKey' * and add the label into the label's map. */ label->setKey(newKey); this->labelsMap.insert(std::make_pair(newKey, label)); } bool GiftiLabelTable::matches(const GiftiLabelTable& rhs, const bool checkColors, const bool checkCoords) const { if (labelsMap.size() != rhs.labelsMap.size()) return false; for (LABELS_MAP::const_iterator iter = labelsMap.begin(); iter != labelsMap.end(); ++iter) { LABELS_MAP::const_iterator riter = rhs.labelsMap.find(iter->first); if (riter == rhs.labelsMap.end()) return false; if (!iter->second->matches(*(riter->second), checkColors, checkCoords)) return false; } return true; } /** * Called when label key zero's name is changed. * May result in a logger message is name is not a preferred name * for the label with key zero. * * @param name * New name for label with key zero. */ void GiftiLabelTable::issueLabelKeyZeroWarning(const AString& name) const { if ((name != "???") && (name.toLower() != "unknown")) { CaretLogFine("Label with key=0 overridden with name \"" + name + "\". This label is typically \"???\" or \"unknown\"."); } } /** * Export the content of the GIFTI Label Table to a Caret5 Color File. */ void GiftiLabelTable::exportToCaret5ColorFile(const AString& filename) const { if (filename.isEmpty()) { throw GiftiException("Missing filename for export of label table to caret5 color file format."); } QFile file(filename); if ( ! file.open(QFile::WriteOnly)) { const AString msg = ("Unable to open " + filename + " for export of label table to caret5 color file format.\n" + file.errorString()); throw GiftiException(msg); } QXmlStreamWriter xmlWriter(&file); xmlWriter.setAutoFormatting(true); xmlWriter.writeStartDocument("1.0"); xmlWriter.writeStartElement("Border_Color_File"); xmlWriter.writeStartElement("FileHeader"); xmlWriter.writeStartElement("Element"); xmlWriter.writeTextElement("comment", "Exported from Caret7/Workbench"); xmlWriter.writeEndElement(); xmlWriter.writeEndElement(); std::set keys = this->getKeys(); for (std::set::const_iterator iter = keys.begin(); iter != keys.end(); iter++) { int key = *iter; const GiftiLabel* label = this->getLabel(key); if (label != NULL) { xmlWriter.writeStartElement("Color"); xmlWriter.writeTextElement("name", label->getName()); const int32_t red = static_cast(label->getRed() * 255.0); const int32_t green = static_cast(label->getGreen() * 255.0); const int32_t blue = static_cast(label->getBlue() * 255.0); const int32_t alpha = static_cast(label->getAlpha() * 255.0); xmlWriter.writeTextElement("red", AString::number(red)); xmlWriter.writeTextElement("green", AString::number(green)); xmlWriter.writeTextElement("blue", AString::number(blue)); xmlWriter.writeTextElement("alpha", AString::number(alpha)); xmlWriter.writeTextElement("pointSize", "1.5"); xmlWriter.writeTextElement("lineSize", "1.0"); xmlWriter.writeTextElement("symbol", "POINT"); xmlWriter.writeTextElement("sumscolorid", ""); xmlWriter.writeEndElement(); } } xmlWriter.writeEndElement(); xmlWriter.writeEndDocument(); file.close(); } connectome-workbench-1.2.3+git41-gc4c6c90/src/FilesBase/GiftiLabelTable.h000066400000000000000000000151311300200146000255250ustar00rootroot00000000000000#ifndef __GIFTILABELTABLE_H__ #define __GIFTILABELTABLE_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 "AString.h" #include "CaretObject.h" #include "TracksModificationInterface.h" #include "GiftiException.h" #include #include #include #include #include #include namespace caret { class GiftiLabel; class XmlWriter; class XmlException; /** * Maintains a GIFTI Label Table using key/value pairs. */ class GiftiLabelTable : public CaretObject, TracksModificationInterface { public: GiftiLabelTable(); GiftiLabelTable(const GiftiLabelTable& glt); GiftiLabelTable& operator=(const GiftiLabelTable& glt); bool matches(const GiftiLabelTable& rhs, const bool checkColors = false, const bool checkCoords = false) const; bool operator==(const GiftiLabelTable& rhs) const { return matches(rhs, true); } bool operator!=(const GiftiLabelTable& rhs) const { return !((*this) == rhs); } virtual ~GiftiLabelTable(); private: void copyHelper(const GiftiLabelTable& glt); void initializeMembersGiftiLabelTable(); public: void clear(); std::map append(const GiftiLabelTable& glt); int32_t addLabel( const AString& labelName, const float red, const float green, const float blue, const float alpha); int32_t addLabel( const AString& labelName, const float red, const float green, const float blue); int32_t addLabel( const AString& labelName, const int32_t red, const int32_t green, const int32_t blue, const int32_t alpha); int32_t addLabel( const AString& labelName, const int32_t red, const int32_t green, const int32_t blue); int32_t addLabel(const GiftiLabel* glt); void deleteLabel(const int32_t key); void deleteLabel(const GiftiLabel* label); void deleteUnusedLabels(const std::set& usedLabelKeys); void insertLabel(const GiftiLabel* label); int32_t getLabelKeyFromName(const AString& name) const; const GiftiLabel* getLabel(const AString& labelName) const; GiftiLabel* getLabel(const AString& labelName); const GiftiLabel* getLabelBestMatching(const AString& name) const; const GiftiLabel* getLabel(const int32_t key) const; GiftiLabel* getLabel(const int32_t key); int32_t getUnassignedLabelKey() const; int32_t getNumberOfLabels() const; AString getLabelName(const int32_t key) const; void setLabelName( const int32_t key, const AString& name); void setLabel(const int32_t key, const AString& name, const float red, const float green, const float blue, const float alpha); void setLabel(const int32_t key, const AString& name, const float red, const float green, const float blue, const float alpha, const float x, const float y, const float z); bool isLabelSelected(const int32_t key) const; void setLabelSelected( const int32_t key, const bool sel); void setSelectionStatusForAllLabels(const bool newStatus); float getLabelAlpha(const int32_t key) const; void getLabelColor(const int32_t key, float rgbaOut[4]) const; void setLabelColor( const int32_t key, const float color[4]); std::vector getLabelKeysSortedByName() const; void resetLabelCounts(); void removeLabelsWithZeroCounts(); void createLabelsForKeys(const std::set& newKeys); bool hasMedialWallLabel() const; void writeAsXML(XmlWriter& xmlWriter); void writeAsXML(QXmlStreamWriter& xmlWriter) const; AString toString() const; AString toFormattedString(const AString& indentation) const; //void readFromXMLDOM(const Node* rootNode) // ; void readFromXmlString(const AString& s); void readFromQXmlStreamReader(QXmlStreamReader& xml); void setModified(); void clearModified(); bool isModified() const; //Iterator getKeysIterator() const; std::set getKeys() const; void getKeys(std::vector& keysOut) const; void getKeysAndNames(std::map& keysAndNamesOut) const; // bool hasLabelsWithInvalidGroupNameHierarchy() const; int32_t generateUnusedKey() const; void changeLabelKey(const int32_t currentKey, const int32_t newKey); void exportToCaret5ColorFile(const AString& filename) const; private: void issueLabelKeyZeroWarning(const AString& name) const; /** The label table storage. Use a TreeMap since label keys may be sparse. */ typedef std::map LABELS_MAP; typedef std::map::iterator LABELS_MAP_ITERATOR; typedef std::map::const_iterator LABELS_MAP_CONST_ITERATOR; LABELS_MAP labelsMap; /**tracks modification status */ bool modifiedFlag; int32_t m_tableModelColumnIndexKey; int32_t m_tableModelColumnIndexName; int32_t m_tableModelColumnIndexColorSwatch; int32_t m_tableModelColumnIndexRed; int32_t m_tableModelColumnIndexGreen; int32_t m_tableModelColumnIndexBlue; int32_t m_tableModelColumnCount; }; } // namespace #endif // __GIFTILABELTABLE_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/FilesBase/GiftiMetaData.cxx000066400000000000000000000404461300200146000256000ustar00rootroot00000000000000/*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 "CaretAssert.h" #include "GiftiMetaData.h" #include "GiftiMetaDataXmlElements.h" //#include "NiftiUtilities.h" #include "GiftiXmlElements.h" #include "XmlWriter.h" using namespace caret; /** * Constructor. * */ GiftiMetaData::GiftiMetaData() : CaretObject(), TracksModificationInterface() { this->initializeMembersGiftiMetaData(); } /** * Destructor */ GiftiMetaData::~GiftiMetaData() { } /** * Copy Constructor * @param Object that is copied. */ GiftiMetaData::GiftiMetaData(const GiftiMetaData& o) : CaretObject(o), TracksModificationInterface() { this->initializeMembersGiftiMetaData(); this->copyHelper(o); } /** * Assignment operator. */ GiftiMetaData& GiftiMetaData::operator=(const GiftiMetaData& o) { if (this != &o) { CaretObject::operator=(o); this->copyHelper(o); }; return *this; } bool GiftiMetaData::operator==(const GiftiMetaData& rhs) const { return (metadata == rhs.metadata); } /** * Helps with copy constructor and assignment operator. */ void GiftiMetaData::copyHelper(const GiftiMetaData& o) { /* * Preserve this instance's Unique ID, but only if it already has one. */ if (exists(GiftiMetaDataXmlElements::METADATA_NAME_UNIQUE_ID)) { const AString uid = this->getUniqueID(); this->metadata = o.metadata; this->set(GiftiMetaDataXmlElements::METADATA_NAME_UNIQUE_ID, uid); this->clearModified(); } else { this->metadata = o.metadata; } } void GiftiMetaData::initializeMembersGiftiMetaData() { this->modifiedFlag = false; } /** * Get the Unique ID. If there is not a unique ID, one is created. * @return String containing unique ID. * */ AString GiftiMetaData::getUniqueID() const { AString uid; MetaDataConstIterator iter = this->metadata.find(GiftiMetaDataXmlElements::METADATA_NAME_UNIQUE_ID); if (iter != this->metadata.end()) { uid = iter->second; } else { uid = SystemUtilities::createUniqueID(); this->metadata.insert(std::make_pair(GiftiMetaDataXmlElements::METADATA_NAME_UNIQUE_ID, uid)); } return uid; } /** * Reset the unique identifier. * Do not call this method unless you really need to such * as in the caret when multiple data arrays have the * same unique identifier. */ void GiftiMetaData::resetUniqueIdentifier() { this->set(GiftiMetaDataXmlElements::METADATA_NAME_UNIQUE_ID, SystemUtilities::createUniqueID()); } /** * Remove the UniqueID from this metadata. * * void GiftiMetaData::removeUniqueID() { this->remove(GiftiMetaDataXmlElements::METADATA_NAME_UNIQUE_ID); } */ /** * Clear the metadata. * */ void GiftiMetaData::clear(bool keepUUID) { if (keepUUID && exists(GiftiMetaDataXmlElements::METADATA_NAME_UNIQUE_ID)) { /* * Preserve this instance's Unique ID. */ const AString uid = this->getUniqueID(); this->metadata.clear(); this->set(GiftiMetaDataXmlElements::METADATA_NAME_UNIQUE_ID, uid); this->clearModified(); } else { metadata.clear(); } } /** * Append the metadata to this metadata. A comment is always appended. * Other metadata are added only if the name is not in "this" metadata. * * @param smd Metadata that is to be appended to "this". * */ void GiftiMetaData::append(const GiftiMetaData& smd) { for (MetaDataConstIterator iter = smd.metadata.begin(); iter != smd.metadata.end(); iter++) { if (iter->first != GiftiMetaDataXmlElements::METADATA_NAME_UNIQUE_ID) { this->set(iter->first, iter->second); } } this->setModified(); } /** * Clears this metadata and then copies all metadata from "smd" * with the exception of the Unique ID. * * @param smd Metadata that is to be copied to "this". * */ void GiftiMetaData::replace(const GiftiMetaData& smd) { /* * Preserve UniqueID. */ const AString uid = this->getUniqueID(); this->metadata = smd.metadata; this->set(GiftiMetaDataXmlElements::METADATA_NAME_UNIQUE_ID, uid); this->setModified(); } /** * Sets metadata. If a metadata entry named "name" already * exists, it is replaced. * * @param name Name of metadata entry. * @param value Value for metadata entry. * */ void GiftiMetaData::set(const AString& name, const AString& value) { MetaDataIterator namePos = this->metadata.find(name); if (namePos != this->metadata.end()) { if (namePos->second != value) { namePos->second = value; this->setModified(); } } else { this->metadata.insert(std::make_pair(name, value)); this->setModified(); } } /** * Set metadata with an integer value. * @param name - name of metadata. * @param value - value of metadata. * */ void GiftiMetaData::setInt( const AString& name, const int32_t value) { AString s = AString::number(value); this->set(name, s); } /** * Set metadata with an float value. * @param name - name of metadata. * @param value - value of metadata. * */ void GiftiMetaData::setFloat( const AString& name, const float value) { AString s = AString::number(value); this->set(name, s); } /** * Replace ALL of the metadata with the data in the given map. * * @param map * New metadata that replaces all existing metadata. */ void GiftiMetaData::replaceWithMap(const std::map& map) { /* * Save UniqueID if it has one and use it if no unique ID in given map. */ AString uid; if (exists(GiftiMetaDataXmlElements::METADATA_NAME_UNIQUE_ID)) { uid = this->getUniqueID(); } this->metadata = map; /* * If metadata was not in given map, restore the Unique ID if we had one. */ if (this->metadata.find(GiftiMetaDataXmlElements::METADATA_NAME_UNIQUE_ID) == this->metadata.end() && uid != "") { this->set(GiftiMetaDataXmlElements::METADATA_NAME_UNIQUE_ID, uid); } this->setModified(); } /** * @return ALL of the metadata in map. */ std::map GiftiMetaData::getAsMap() const { return this->metadata; } /** * Remove a metadata entry. * * @param name Name of metadata entry that is to be removed. * */ void GiftiMetaData::remove(const AString& name) { this->metadata.erase(name); } /** * See if a metadata entry "name" exists. * * @param name Name of metadata entry. * @return Returns true if the metadata entry "name" exists, else false. * */ bool GiftiMetaData::exists(const AString& name) const { if (this->metadata.find(name) != this->metadata.end()) { return true; } return false; } /** * Get a value for metadata entry. * * @param name Name of metadata entry. * @return The value of the metadata entry "name". If the * metadata entry "name" does not exist an empty * string is returned. * */ AString GiftiMetaData::get(const AString& name) const { if (this->metadata.empty()) { return ""; } MetaDataConstIterator iter = this->metadata.find(name); if (iter != this->metadata.end()) { return iter->second; } return ""; } /** * Get the metadata as an integer value. If the metadata does not exist * of its string representation is not a number, zero is returned. * @param name - name of metadata. * @return Integer value associated with the metadata. * */ int32_t GiftiMetaData::getInt(const AString& name) const { AString s = this->get(name); if (s.length() > 0) { int32_t i = s.toInt(); return i; } return 0; } /** * Get the metadata as an float value. If the metadata does not exist * of its string representation is not a number, zero is returned. * @param name - name of metadata. * @return Float value associated with the metadata. * */ float GiftiMetaData::getFloat(const AString& name) const { AString s = this->get(name); if (s.length() > 0) { float f = s.toFloat(); return f; } return 0.0f; } /** * Get names of all metadata. * * @return List of all metadata names. * */ std::vector GiftiMetaData::getAllMetaDataNames() const { std::vector names; for (MetaDataConstIterator iter = this->metadata.begin(); iter != this->metadata.end(); iter++) { names.push_back(iter->first); } return names; } /** * Update metanames from caret5. * */ void GiftiMetaData::updateFromCaret5Names() { } /** * Replace a metadata name. * @param oldName - old name of metadata. * @param newName - new name of metadata. * */ void GiftiMetaData::replaceName( const AString& oldName, const AString& newName) { MetaDataIterator iter = this->metadata.find(oldName); if (iter != this->metadata.end()) { AString value = iter->second; this->remove(oldName); this->set(newName, value); this->setModified(); } } /** * Convert to a string. * * @return String representation of metadata. * */ AString GiftiMetaData::toString() const { AString s = "GiftiMetaData=["; for (MetaDataConstIterator iter = this->metadata.begin(); iter != this->metadata.end(); iter++) { const AString& name = iter->first; const AString& value = iter->second; s += ("(" + name + "," + value + ")"); } s += "]"; return s; } /** * Get a nicely formatted string for printing. * * @param indentation - use as indentation. * @return String containing label information. * */ AString GiftiMetaData::toFormattedString(const AString& indentation) { return (indentation + this->toString()); } /** * Write the metadata in GIFTI XML format. * * @param xmlWriter - output stream * @throws GiftiException if an error occurs while writing. */ void GiftiMetaData::writeAsXML(XmlWriter& xmlWriter) { try { // // Write the metadata tag // xmlWriter.writeStartElement(GiftiXmlElements::TAG_METADATA); // // Write the metadata // for (MetaDataConstIterator iter = this->metadata.begin(); iter != this->metadata.end(); iter++) { const AString& key = iter->first; const AString& value = iter->second; // // MD Tag // xmlWriter.writeStartElement(GiftiXmlElements::TAG_METADATA_ENTRY); // // Name and value // xmlWriter.writeElementCData(GiftiXmlElements::TAG_METADATA_NAME, key); xmlWriter.writeElementCData(GiftiXmlElements::TAG_METADATA_VALUE, value); // // Closing tag // xmlWriter.writeEndElement(); } // // Write the closing metadata tag // xmlWriter.writeEndElement(); } catch (XmlException& e) { throw GiftiException(e); } } void GiftiMetaData::writeCiftiXML1(QXmlStreamWriter& xmlWriter) const { if (metadata.empty()) return;//don't write an empty tag if we have no metadata xmlWriter.writeStartElement(GiftiXmlElements::TAG_METADATA); for (MetaDataConstIterator iter = metadata.begin(); iter != metadata.end(); ++iter) { xmlWriter.writeStartElement(GiftiXmlElements::TAG_METADATA_ENTRY); xmlWriter.writeTextElement(GiftiXmlElements::TAG_METADATA_NAME, iter->first); xmlWriter.writeTextElement(GiftiXmlElements::TAG_METADATA_VALUE, iter->second); xmlWriter.writeEndElement(); } xmlWriter.writeEndElement(); } void GiftiMetaData::writeCiftiXML2(QXmlStreamWriter& xmlWriter) const { writeCiftiXML1(xmlWriter); } void GiftiMetaData::writeBorderFileXML3(QXmlStreamWriter& xmlWriter) const { writeCiftiXML1(xmlWriter); } void GiftiMetaData::readCiftiXML1(QXmlStreamReader& xml) { clear(false); while (!xml.atEnd())//don't check the current element's name { xml.readNext(); if (xml.isStartElement()) { QStringRef name = xml.name(); if (name == GiftiXmlElements::TAG_METADATA_ENTRY) { readEntry(xml); } else { xml.raiseError("unexpected tag name in " + GiftiXmlElements::TAG_METADATA + ": " + name.toString()); } } else if (xml.isEndElement()) { break; } } } void GiftiMetaData::readCiftiXML2(QXmlStreamReader& xml) { readCiftiXML1(xml); } void GiftiMetaData::readBorderFileXML1(QXmlStreamReader& xml) { readCiftiXML1(xml); } void GiftiMetaData::readBorderFileXML3(QXmlStreamReader& xml) { readCiftiXML1(xml); } void GiftiMetaData::readEntry(QXmlStreamReader& xml) { AString key, value; bool haveKey = false, haveValue = false; while (!xml.atEnd())//don't check the current element's name { xml.readNext(); if (xml.isStartElement()) { QStringRef name = xml.name(); if (name == GiftiXmlElements::TAG_METADATA_NAME) { if (haveKey) throw GiftiException("MD element has multiple Name elements"); key = xml.readElementText(); haveKey = true; } else if (name == GiftiXmlElements::TAG_METADATA_VALUE) { if (haveValue) throw GiftiException("MD element has multiple Value elements"); value = xml.readElementText(); haveValue = true; } else { xml.raiseError("unexpected tag name in " + GiftiXmlElements::TAG_METADATA_ENTRY + ": " + name.toString()); } } else if (xml.isEndElement()) { if (haveKey && haveValue) { if (exists(key)) { xml.raiseError("key '" + key + "' used more than once in " + GiftiXmlElements::TAG_METADATA); } else { set(key, value); } } else { if (haveKey) { xml.raiseError(GiftiXmlElements::TAG_METADATA_ENTRY + " element has no " + GiftiXmlElements::TAG_METADATA_VALUE + " element"); } else { if (haveValue) { xml.raiseError(GiftiXmlElements::TAG_METADATA_ENTRY + " element has no " + GiftiXmlElements::TAG_METADATA_NAME + " element"); } else { xml.raiseError(GiftiXmlElements::TAG_METADATA_ENTRY + " element has no " + GiftiXmlElements::TAG_METADATA_NAME + " or " + GiftiXmlElements::TAG_METADATA_VALUE + " element"); } } } break; } } CaretAssert(xml.hasError() || (xml.isEndElement() && xml.name() == "MD")); } /** * Set this object has been modified. * */ void GiftiMetaData::setModified() { this->modifiedFlag = true; } /** * Set this object as not modified. Object should also * clear the modification status of its children. * */ void GiftiMetaData::clearModified() { this->modifiedFlag = false; } /** * Get the modification status. Returns true if this object or * any of its children have been modified. * @return - The modification status. * */ bool GiftiMetaData::isModified() const { return this->modifiedFlag; } connectome-workbench-1.2.3+git41-gc4c6c90/src/FilesBase/GiftiMetaData.h000066400000000000000000000077221300200146000252250ustar00rootroot00000000000000#ifndef __GIFTIMETADATA_H__ #define __GIFTIMETADATA_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 "GiftiException.h" #include "TracksModificationInterface.h" #include #include #include #include class QXmlStreamReader; class QXmlStreamWriter; namespace caret { class XmlWriter; /** * Maintains GIFTI metadata using name/value pairs. A * metadata object may be associated with the GIFTI * data file and with each GIFTI data array. */ class GiftiMetaData : public CaretObject, TracksModificationInterface { public: GiftiMetaData(); public: GiftiMetaData(const GiftiMetaData& o); GiftiMetaData& operator=(const GiftiMetaData& o); bool operator==(const GiftiMetaData& rhs) const; bool operator!=(const GiftiMetaData& rhs) const { return !((*this) == rhs); } virtual ~GiftiMetaData(); private: void copyHelper(const GiftiMetaData& o); void initializeMembersGiftiMetaData(); public: AString getUniqueID() const; //void removeUniqueID(); void clear(bool keepUUID = true); void append(const GiftiMetaData& smd); void replace(const GiftiMetaData& smd); void set( const AString& name, const AString& value); void setInt( const AString& name, const int32_t value); void setFloat( const AString& name, const float value); void replaceWithMap(const std::map& map); std::map getAsMap() const; void remove(const AString& name); bool exists(const AString& name) const; AString get(const AString& name) const; int32_t getInt(const AString& name) const; float getFloat(const AString& name) const; std::vector getAllMetaDataNames() const; void updateFromCaret5Names(); AString toString() const; AString toFormattedString(const AString& indentation); void writeAsXML(XmlWriter& xmlWriter); void writeCiftiXML1(QXmlStreamWriter& xmlWriter) const; void writeCiftiXML2(QXmlStreamWriter& xmlWriter) const;//extra names for code style, and in case it changes in a future version void writeBorderFileXML3(QXmlStreamWriter& xmlWriter) const; void readCiftiXML1(QXmlStreamReader& xml); void readCiftiXML2(QXmlStreamReader& xml); void readBorderFileXML1(QXmlStreamReader& xml); void readBorderFileXML3(QXmlStreamReader& xml); void setModified(); void clearModified(); bool isModified() const; void resetUniqueIdentifier(); private: void readEntry(QXmlStreamReader& xml); std::map createTreeMap(); void replaceName( const AString& oldName, const AString& newName); public: private: /**the metadata storage. */ mutable std::map metadata; typedef std::map::iterator MetaDataIterator; typedef std::map::const_iterator MetaDataConstIterator; /**has the metadata been modified */ bool modifiedFlag; }; } // namespace #endif // __GIFTIMETADATA_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/FilesBase/GiftiMetaDataXmlElements.h000066400000000000000000000206141300200146000273760ustar00rootroot00000000000000 /*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*/ namespace caret { namespace GiftiMetaDataXmlElements { /** metadata name for primary anatomical structure found in NIFTI_INTENT_POINTSET data arrays. */ static const AString METADATA_NAME_ANATOMICAL_STRUCTURE_PRIMARY = "AnatomicalStructurePrimary"; /** metadata value for NIFTI_INTENT_POINTSET primary anatomical structure representing the left cerebral cortex. */ static const AString METADATA_NAME_ANATOMICAL_STRUCTURE_PRIMARY_VALUE_CORTEX_LEFT = "CortexLeft"; /** metadata value for NIFTI_INTENT_POINTSET primary anatomical structure representing the right cerebral cortex. */ static const AString METADATA_NAME_ANATOMICAL_STRUCTURE_PRIMARY_VALUE_CORTEX_RIGHT = "CortexRight"; /** metadata value for NIFTI_INTENT_POINTSET primary anatomical structure representing the both left and right cerebral cortex. */ static const AString METADATA_NAME_ANATOMICAL_STRUCTURE_PRIMARY_VALUE_CORTEX_BOTH = "CortexRightAndLeft"; /** metadata value for NIFTI_INTENT_POINTSET primary anatomical structure representing the cerebellum. */ static const AString METADATA_NAME_ANATOMICAL_STRUCTURE_PRIMARY_VALUE_CEREBELLUM = "Cerebellum"; /** metadata value for NIFTI_INTENT_POINTSET primary anatomical structure representing the head. */ static const AString METADATA_NAME_ANATOMICAL_STRUCTURE_PRIMARY_VALUE_HEAD = "Head"; /** metadata value for NIFTI_INTENT_POINTSET primary anatomical structure representing the left hippocampus. */ static const AString METADATA_NAME_ANATOMICAL_STRUCTURE_PRIMARY_VALUE_HIPPOCAMPUS_LEFT = "HippocampusLeft"; /** metadata value for NIFTI_INTENT_POINTSET primary anatomical structure representing the right hippocampus. */ static const AString METADATA_NAME_ANATOMICAL_STRUCTURE_PRIMARY_VALUE_HIPPOCAMPUS_RIGHT = "HippocampusRight"; /** metadata name for for secondary anatomical structure found in NIFTI_INTENT_POINTSET data arrays. */ static const AString METADATA_NAME_ANATOMICAL_STRUCTURE_SECONDARY = "AnatomicalStructureSecondary"; /** metadata value for NIFTI_INTENT_POINTSET secondary anatomical structure representing the gray and white boundary. */ static const AString METADATA_NAME_ANATOMICAL_STRUCTURE_SECONDARY_VALUE_GRAY_WHITE = "GrayWhite"; /** metadata value for NIFTI_INTENT_POINTSET secondary anatomical structure representing the pial (gray/CSF boundary). */ static const AString METADATA_NAME_ANATOMICAL_STRUCTURE_SECONDARY_VALUE_PIAL = "Pial"; /** metadata value for NIFTI_INTENT_POINTSET secondary anatomical structure representing the mid thickness (layer 4). */ static const AString METADATA_NAME_ANATOMICAL_STRUCTURE_SECONDARY_VALUE_ = "MidThickness"; /** metadata name found in the file's metadata and indicates the date and time the file was written. */ static const AString METADATA_NAME_DATE = "Date"; /** metadata name found in any metadata and provides a description of the entity's content. */ static const AString METADATA_NAME_DESCRIPTION = "Description"; static const AString METADATA_NAME_COMMENT = "Comment"; /** metadata name for geometric type found in NIFTI_INTENT_POINTSET data arrays. */ static const AString METADATA_NAME_GEOMETRIC_TYPE = "GeometricType"; /** metadata value for NIFTI_INTENT_POINTSET's Geometric Type Reconstruction with a "blocky" appearance. */ static const AString METADATA_NAME_GEOMETRIC_TYPE_VALUE_RECONSTRUCTION = "Reconstruction"; /** metadata value for NIFTI_INTENT_POINTSET's Geometric Type representing true anatomical structure. */ static const AString METADATA_NAME_GEOMETRIC_TYPE_VALUE_ANATOMICAL = "Anatomical"; /** metadata value for NIFTI_INTENT_POINTSET's Geometric Type for inflated surface. */ static const AString METADATA_NAME_GEOMETRIC_TYPE_VALUE_INFLATED = "Inflated"; /** metadata value for NIFTI_INTENT_POINTSET's Geometric Type for very inflated surface. */ static const AString METADATA_NAME_GEOMETRIC_TYPE_VALUE_VERY_INFLATED = "VeryInflated"; /** metadata value for NIFTI_INTENT_POINTSET's Geometric Type spherical surface. */ static const AString METADATA_NAME_GEOMETRIC_TYPE_VALUE_SPHERICAL = "Spherical"; /** metadata value for NIFTI_INTENT_POINTSET's Geometric Type semi-spherical surface with one half flattened. */ static const AString METADATA_NAME_GEOMETRIC_TYPE_VALUE_SEMI_SPHERICAL = "SemiSpherical"; /** metadata value for NIFTI_INTENT_POINTSET's Geometric Type ellipsoid surface. */ static const AString METADATA_NAME_GEOMETRIC_TYPE_VALUE_ELLIPSOID = "Ellipsoid"; /** metadata value for NIFTI_INTENT_POINTSET's Geometric Type flat surface. */ static const AString METADATA_NAME_GEOMETRIC_TYPE_VALUE_FLAT = "Flat"; /** metadata value for NIFTI_INTENT_POINTSET's Geometric Type hull surface (eg: wrapping around cortex with sulci filled but not necessarily convex). */ static const AString METADATA_NAME_GEOMETRIC_TYPE_VALUE_HULL = "Hull"; /**metadata name for NIFTI Intent label found in functional data arrays. */ static const AString METADATA_NAME_INTENT_CODE = "Intent_code"; /**metadata name for NIFTI Intent parameter one in functional data arrays.*/ static const AString METADATA_NAME_INTENT_P1 = "intent_p1"; /**metadata name for NIFTI Intent parameter two in functional data arrays.*/ static const AString METADATA_NAME_INTENT_P2 = "intent_p2"; /**metadata name for NIFTI Intent parameter three in functional data arrays.*/ static const AString METADATA_NAME_INTENT_P3 = "intent_p3"; /**metadata name for name of data, often displayed in the user-interface. */ static const AString METADATA_NAME_NAME = "Name"; /**metadata name for text that identifies a subjet. */ static const AString METADATA_NAME_SUBJECT_ID = "SubjectID"; /**metadata name for text that uniquely defines a surface. */ static const AString METADATA_NAME_SURFACE_ID = "SurfaceID"; /**metadata name for time step (TR) in NIFTI_INTENT_TIME_SERIES arrays. */ static const AString METADATA_NAME_TIME_STEP = "TimeStep"; /**metadata name for topological type in NIFTI_INTENT_TRIANGLE arrays. */ static const AString METADATA_NAME_TOPOLOGICAL_TYPE = "TopologicalType"; /** metadata value for NIFTI_INTENT_TRIANGLE's Topological Type for closed topology. */ static const AString METADATA_NAME_TOPOLOGICAL_TYPE_VALUE_CLOSED = "Closed"; /** metadata value for NIFTI_INTENT_TRIANGLE's Topological Type for open topology (perhaps medial wall removed). */ static const AString METADATA_NAME_TOPOLOGICAL_TYPE_VALUE_OPEN = "Open"; /** metadata value for NIFTI_INTENT_TRIANGLE's Topological Type for cut topology (typically used with flat surfaces with medial wall removed and cuts made to reduce distortion. */ static const AString METADATA_NAME_TOPOLOGICAL_TYPE_VALUE_CUT = "Cut"; /** metadata name for a unique identifiers for the data array. This ID is best generated using a Universal Unique Identifier function such as Java's java.util.uuid or C's uuid_generate(). @see UUID */ static const AString METADATA_NAME_UNIQUE_ID = "UniqueID"; /**metadata name for username of user that wrote the file in file metadata.*/ static const AString METADATA_NAME_USER_NAME = "UserName"; /**name of study metadata link set metadata */ static const AString METADATA_NAME_STUDY_METADATA_LINK_SET = "StudyMetaDataLinkSet"; /**name of palette color mapping stored in metadata */ static const AString METADATA_NAME_PALETTE_COLOR_MAPPING = "PaletteColorMapping"; } // namespace } // namespace connectome-workbench-1.2.3+git41-gc4c6c90/src/FilesBase/GiftiXmlElements.cxx000066400000000000000000000023471300200146000263530ustar00rootroot00000000000000/*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 "GiftiXmlElements.h" using namespace caret; /** * Get an attribute dimension (Dim0, Dim1, etc). * @param Index value for dimension. * @return Dim0, Dim1, etc */ AString GiftiXmlElements::getAttributeDimension(int32_t dimIndex) { std::ostringstream str; str << GiftiXmlElements::ATTRIBUTE_DATA_ARRAY_DIM_PREFIX.toStdString() << dimIndex; return AString::fromStdString(str.str()); } connectome-workbench-1.2.3+git41-gc4c6c90/src/FilesBase/GiftiXmlElements.h000066400000000000000000000165221300200146000260000ustar00rootroot00000000000000#ifndef __GIFTIXMLELEMENTS_H__ #define __GIFTIXMLELEMENTS_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 namespace caret { /** * GIFTI XML Element tags */ namespace GiftiXmlElements { /** tag for coordinate transformation matrix element */ static const AString TAG_COORDINATE_TRANSFORMATION_MATRIX = "CoordinateSystemTransformMatrix"; /** tag for data element */ static const AString TAG_DATA = "Data"; /** tag for data array element */ static const AString TAG_DATA_ARRAY = "DataArray"; /** tag for coordinate transformation data space element */ static const AString TAG_MATRIX_DATA_SPACE = "DataSpace"; /** tag for GIFTI element */ static const AString TAG_GIFTI = "GIFTI"; /** tag for label element */ static const AString TAG_LABEL = "Label"; /** tag for label table element */ static const AString TAG_LABEL_TABLE = "LabelTable"; /** tag for a metadata entry */ static const AString TAG_METADATA_ENTRY = "MD"; /** tag for matrix data */ static const AString TAG_MATRIX_DATA = "MatrixData"; /** tag for metadata */ static const AString TAG_METADATA = "MetaData"; /** tag for metadata name */ static const AString TAG_METADATA_NAME = "Name"; /** tag for coordinate transformation space element */ static const AString TAG_MATRIX_TRANSFORMED_SPACE = "TransformedSpace"; /** tag for metadata value element */ static const AString TAG_METADATA_VALUE = "Value"; /** attribute for data array indexing order */ static const AString ATTRIBUTE_DATA_ARRAY_INDEXING_ORDER = "ArrayIndexingOrder"; /** attribute for data array data type */ static const AString ATTRIBUTE_DATA_ARRAY_DATA_TYPE = "DataType"; /** attribute for data array dimensionality */ static const AString ATTRIBUTE_DATA_ARRAY_DIMENSIONALITY = "Dimensionality"; /** attribute for data array dimensionality */ static const AString ATTRIBUTE_DATA_ARRAY_DIM_PREFIX = "Dim"; /** attribute for data array encoding */ static const AString ATTRIBUTE_DATA_ARRAY_ENCODING = "Encoding"; /** attribute for data array ending */ static const AString ATTRIBUTE_DATA_ARRAY_ENDIAN = "Endian"; /** attribute for data array external file name */ static const AString ATTRIBUTE_DATA_ARRAY_EXTERNAL_FILE_NAME = "ExternalFileName"; /** attribute for data array external file offset */ static const AString ATTRIBUTE_DATA_ARRAY_EXTERNAL_FILE_OFFSET = "ExternalFileOffset"; /** attribute for data array label index REPLACED BY KEY*/ static const AString ATTRIBUTE_LABEL_INDEX_obsolete = "Index"; /** attribute for data array label key */ static const AString ATTRIBUTE_LABEL_KEY = "Key"; /** attribute for data array label red color component */ static const AString ATTRIBUTE_LABEL_RED = "Red"; /** attribute for data array label green color component */ static const AString ATTRIBUTE_LABEL_GREEN = "Green"; /** attribute for data array label blue color component */ static const AString ATTRIBUTE_LABEL_BLUE = "Blue"; /** attribute for data array label alpha color component */ static const AString ATTRIBUTE_LABEL_ALPHA = "Alpha"; /** attribute for data array label X-coordinate */ static const AString ATTRIBUTE_LABEL_X = "X"; /** attribute for data array label X-coordinate */ static const AString ATTRIBUTE_LABEL_Y = "Y"; /** attribute for data array label X-coordinate */ static const AString ATTRIBUTE_LABEL_Z = "Z"; /** attribute for data array intent */ static const AString ATTRIBUTE_DATA_ARRAY_INTENT = "Intent"; /** attribute for data array intent parameter 1 */ static const AString ATTRIBUTE_DATA_ARRAY_INTENT_P1 = "intent_p1"; /** attribute for data array intent parameter 2 */ static const AString ATTRIBUTE_DATA_ARRAY_INTENT_P2 = "intent_p2"; /** attribute for data array intent parameter 3 */ static const AString ATTRIBUTE_DATA_ARRAY_INTENT_P3 = "intent_p3"; /** attribute for GIFTI Number of Data Arrays */ static const AString ATTRIBUTE_GIFTI_NUMBER_OF_DATA_ARRAYS = "NumberOfDataArrays"; /** attribute for GIFTI Version */ static const AString ATTRIBUTE_GIFTI_VERSION = "Version"; AString getAttributeDimension(int32_t dimIndex); /** * Get an attribute dimension (Dim0, Dim1, etc). * @param Index value for dimension. * @return Dim0, Dim1, etc * static AString getAttributeDimension(int32_t dimIndex) { std::ostringstream str; str << ATTRIBUTE_DATA_ARRAY_DIM_PREFIX << dimIndex;; return str.str(); } */ /* static const AString tagGIFTI = "GIFTI"; static const AString tagMetaData = "MetaData"; static const AString tagMD = "MD"; static const AString tagName = "Name"; static const AString tagValue = "Value"; static const AString tagDataArray = "DataArray"; static const AString tagData = "Data"; static const AString tagLabelTable = "LabelTable"; static const AString tagLabel = "Labe"; static const AString tagMatrix = "CoordinateSystemTransformMatrix"; static const AString tagMatrixDataSpace = "DataSpace"; static const AString tagMatrixTransformedSpace = "TransformedSpace"; static const AString tagMatrixData = "MatrixData"; static const AString attVersion = "Version"; static const AString attNumberOfDataArrays = "NumberOfDataArrays"; static const AString attArraySubscriptingOrder = "ArrayIndexingOrder"; static const AString attKey = "Key"; static const AString attRed = "Red"; static const AString attGreen = "Green"; static const AString attBlue = "Blue"; static const AString attAlpha = "Alpha"; static const AString attIntent = "Intent"; static const AString attDataType = "DataType"; static const AString attDimensionality = "Dimensionality"; static const AString attDim = "Dim"; static const AString attEncoding = "Encoding"; static const AString attEndian = "Endian"; static const AString attExternalFileName = "ExternalFileName"; static const AString attExternalFileOffset = "ExternalFileOffset"; */ }; // namespace } // namespace #endif // __GIFTIXMLELEMENTS_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/FilesBase/NiftiEnums.cxx000066400000000000000000001031011300200146000252020ustar00rootroot00000000000000/*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 __NIFTI_ENUMS_DECLARE__ #include "NiftiEnums.h" #undef __NIFTI_ENUMS_DECLARE__ using namespace caret; ///Nifti Data Type Enum NiftiDataTypeEnum::NiftiDataTypeEnum() { } NiftiDataTypeEnum::NiftiDataTypeEnum(Enum e, const AString& name, const int32_t integerCode) { this->e = e; this->name = name; this->integerCode = integerCode; } NiftiDataTypeEnum::~NiftiDataTypeEnum() { } void NiftiDataTypeEnum::createDataTypes() { if (dataTypesCreatedFlag) { return; } dataTypesCreatedFlag = true; dataTypes.push_back(NiftiDataTypeEnum(NIFTI_TYPE_INVALID, "NIFTI_DATA_TYPE_NONE", 0)); dataTypes.push_back(NiftiDataTypeEnum(NIFTI_TYPE_UINT8, "NIFTI_TYPE_UINT8", 2)); dataTypes.push_back(NiftiDataTypeEnum(NIFTI_TYPE_INT16, "NIFTI_TYPE_INT16", 4)); dataTypes.push_back(NiftiDataTypeEnum(NIFTI_TYPE_INT32, "NIFTI_TYPE_INT32", 8)); dataTypes.push_back(NiftiDataTypeEnum(NIFTI_TYPE_FLOAT32, "NIFTI_TYPE_FLOAT32", 16)); dataTypes.push_back(NiftiDataTypeEnum(NIFTI_TYPE_COMPLEX64, "NIFTI_TYPE_COMPLEX64", 32)); dataTypes.push_back(NiftiDataTypeEnum(NIFTI_TYPE_FLOAT64, "NIFTI_TYPE_FLOAT64", 64)); dataTypes.push_back(NiftiDataTypeEnum(NIFTI_TYPE_RGB24, "NIFTI_TYPE_RGB24", 128)); dataTypes.push_back(NiftiDataTypeEnum(NIFTI_TYPE_INT8, "NIFTI_TYPE_INT8", 256)); dataTypes.push_back(NiftiDataTypeEnum(NIFTI_TYPE_UINT16, "NIFTI_TYPE_UINT16", 512)); dataTypes.push_back(NiftiDataTypeEnum(NIFTI_TYPE_UINT32, "NIFTI_TYPE_UINT32", 768)); dataTypes.push_back(NiftiDataTypeEnum(NIFTI_TYPE_INT64, "NIFTI_TYPE_INT64", 1024)); dataTypes.push_back(NiftiDataTypeEnum(NIFTI_TYPE_UINT64, "NIFTI_TYPE_UINT64", 1280)); dataTypes.push_back(NiftiDataTypeEnum(NIFTI_TYPE_FLOAT128, "NIFTI_TYPE_FLOAT128", 1792)); dataTypes.push_back(NiftiDataTypeEnum(NIFTI_TYPE_COMPLEX128, "NIFTI_TYPE_COMPLEX128", 1792)); dataTypes.push_back(NiftiDataTypeEnum(NIFTI_TYPE_COMPLEX256, "NIFTI_TYPE_COMPLEX256", 2048)); } /** * Get a string representition of the enumerated type. * @param e * Enumerated value. * @return * String representing enumerated value. */ AString NiftiDataTypeEnum::toName(Enum e) { createDataTypes(); const NiftiDataTypeEnum* ndt = findData(e); return ndt->name; } /** * Get an enumerated value corresponding to its 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. */ NiftiDataTypeEnum::Enum NiftiDataTypeEnum::fromName(const AString& s, bool* isValidOut) { createDataTypes(); bool validFlag = false; Enum e = NIFTI_TYPE_FLOAT32; for (std::vector::iterator iter = dataTypes.begin(); iter != dataTypes.end(); iter++) { const NiftiDataTypeEnum& ndt = *iter; if (ndt.name == s) { e = ndt.e; validFlag = true; break; } } if (isValidOut != 0) { *isValidOut = validFlag; } return e; } /** * Find the Data Type using the enum. * * @return * Object using enum or NULL if not found. */ const NiftiDataTypeEnum* NiftiDataTypeEnum::findData(Enum e) { createDataTypes(); for (std::vector::iterator iter = dataTypes.begin(); iter != dataTypes.end(); iter++) { const NiftiDataTypeEnum& ndt = *iter; if (ndt.e == e) { return &ndt; } } return NULL; } /** * Get the integer code for a data type. * * @return * Integer code for data type. */ int32_t NiftiDataTypeEnum::toIntegerCode(Enum e) { createDataTypes(); const NiftiDataTypeEnum* ndt = findData(e); return ndt->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. */ NiftiDataTypeEnum::Enum NiftiDataTypeEnum::fromIntegerCode(const int32_t integerCode, bool* isValidOut) { createDataTypes(); bool validFlag = false; Enum e = NIFTI_TYPE_INVALID; for (std::vector::iterator iter = dataTypes.begin(); iter != dataTypes.end(); iter++) { const NiftiDataTypeEnum& ndt = *iter; if (ndt.integerCode == integerCode) { e = ndt.e; validFlag = true; break; } } if (isValidOut != 0) { *isValidOut = validFlag; } return e; } ///Nifti Intent Enum NiftiIntentEnum::NiftiIntentEnum(Enum e, const AString& enumName, const int32_t integerCode, const AString& name, const AString& p1Name, const AString& p2Name, const AString& p3Name) { this->e = e; this->enumName = enumName; this->integerCode = integerCode; this->name = name; this->p1Name = p1Name; this->p2Name = p2Name; this->p3Name = p3Name; } //NiftiIntentEnum::NiftiIntentEnum() //{ // //} NiftiIntentEnum::~NiftiIntentEnum() { } void NiftiIntentEnum::initializeIntents() { if (intentsCreatedFlag) { return; } intentsCreatedFlag = true; //intents.push_back( // NiftiIntentEnum()); intents.push_back(NiftiIntentEnum(NIFTI_INTENT_NONE,"NIFTI_INTENT_NONE", 0,"None","","","")); intents.push_back(NiftiIntentEnum(NIFTI_INTENT_CORREL,"NIFTI_INTENT_CORREL", 2,"Correlation Statistic","DOF","","")); intents.push_back(NiftiIntentEnum(NIFTI_INTENT_TTEST,"NIFTI_INTENT_TTEST", 3,"T-Statistic","DOF","","")); intents.push_back(NiftiIntentEnum(NIFTI_INTENT_FTEST,"NIFTI_INTENT_FTEST", 4,"F-Statistic","Numberator DOF","Denominator DOF","")); intents.push_back(NiftiIntentEnum(NIFTI_INTENT_ZSCORE,"NIFTI_INTENT_ZSCORE", 5,"Z-Score","","","")); intents.push_back(NiftiIntentEnum(NIFTI_INTENT_CHISQ,"NIFTI_INTENT_CHISQ", 6,"Chi-Squared Distribution","DOF","","")); intents.push_back(NiftiIntentEnum(NIFTI_INTENT_BETA,"NIFTI_INTENT_BETA", 7,"Beta Distribution","a","b","")); intents.push_back(NiftiIntentEnum(NIFTI_INTENT_BINOM,"NIFTI_INTENT_BINOM", 8,"Binomial Distribution", "Number of Trials","Probability per Trial","")); intents.push_back(NiftiIntentEnum(NIFTI_INTENT_GAMMA,"NIFTI_INTENT_GAMMA", 9,"Gamma Distribution","Shape","Scale","")); intents.push_back(NiftiIntentEnum(NIFTI_INTENT_POISSON,"NIFTI_INTENT_POISSON", 10,"Poisson Distribution","Mean","","")); intents.push_back(NiftiIntentEnum(NIFTI_INTENT_NORMAL,"NIFTI_INTENT_NORMAL", 11,"Normal Distribution","Mean","Standard Deviation","")); intents.push_back(NiftiIntentEnum(NIFTI_INTENT_FTEST_NONC,"NIFTI_INTENT_FTEST_NONC", 12,"F-Statistic Non-Central", "Numerator DOF", "Denominator DOF", "Numerator Noncentrality Parameter")); intents.push_back(NiftiIntentEnum(NIFTI_INTENT_CHISQ_NONC,"NIFTI_INTENT_CHISQ_NONC", 13,"Chi-Squared Non-Central", "DOF", "Noncentrality Parameter","")); intents.push_back(NiftiIntentEnum(NIFTI_INTENT_LOGISTIC,"NIFTI_INTENT_LOGISTIC", 14,"Logistic Distribution","Location","Scale","")); intents.push_back(NiftiIntentEnum(NIFTI_INTENT_LAPLACE,"NIFTI_INTENT_LAPLACE", 15,"Laplace Distribution","Location","Scale","")); intents.push_back(NiftiIntentEnum(NIFTI_INTENT_UNIFORM,"NIFTI_INTENT_UNIFORM", 16,"Uniform Distribution","Lower End","Upper End","")); intents.push_back(NiftiIntentEnum(NIFTI_INTENT_TTEST_NONC,"NIFTI_INTENT_TTEST_NONC", 17,"T-Statistic Non-Central", "DOF", "Noncentrality Parameter","")); intents.push_back(NiftiIntentEnum(NIFTI_INTENT_WEIBULL,"NIFTI_INTENT_WEIBULL", 18,"Weibull Distribution","Location","Scale","Power")); intents.push_back(NiftiIntentEnum(NIFTI_INTENT_CHI,"NIFTI_INTENT_CHI", 19,"Chi Distribution", "Half Normal Distribution", "Rayleigh Distribution", "Maxwell-Boltzmann Distribution")); intents.push_back(NiftiIntentEnum(NIFTI_INTENT_INVGAUSS,"NIFTI_INTENT_INVGAUSS", 20,"Inverse Gaussian Distribution", "MU", "Lambda","")); intents.push_back(NiftiIntentEnum(NIFTI_INTENT_EXTVAL,"NIFTI_INTENT_EXTVAL", 21,"Extreme Value Distribution", "Location", "Scale","")); intents.push_back(NiftiIntentEnum(NIFTI_INTENT_PVAL,"NIFTI_INTENT_PVAL", 22,"P-Value","","","")); intents.push_back(NiftiIntentEnum(NIFTI_INTENT_LOGPVAL,"NIFTI_INTENT_LOGPVAL", 23,"Log P-Value","","","")); intents.push_back(NiftiIntentEnum(NIFTI_INTENT_LOG10PVAL,"NIFTI_INTENT_LOG10PVAL", 24,"Logn10 P-Value","","","")); intents.push_back(NiftiIntentEnum(NIFTI_INTENT_ESTIMATE,"NIFTI_INTENT_ESTIMATE", 1001,"Estimate","","","")); intents.push_back(NiftiIntentEnum(NIFTI_INTENT_LABEL,"NIFTI_INTENT_LABEL", 1002,"Label Indices","","","")); intents.push_back(NiftiIntentEnum(NIFTI_INTENT_NEURONAME,"NIFTI_INTENT_NEURONAME", 1003,"Neuronames Indices","","","")); intents.push_back(NiftiIntentEnum(NIFTI_INTENT_GENMATRIX,"NIFTI_INTENT_GENMATRIX", 1004,"General Matrix","","","")); intents.push_back(NiftiIntentEnum(NIFTI_INTENT_SYMMATRIX,"NIFTI_INTENT_SYMMATRIX", 1005,"Symmetric Matrix","","","")); intents.push_back(NiftiIntentEnum(NIFTI_INTENT_DISPVECT,"NIFTI_INTENT_DISPVECT", 1006,"Displacement Vector","","","")); intents.push_back(NiftiIntentEnum(NIFTI_INTENT_VECTOR,"NIFTI_INTENT_VECTOR", 1007,"Vector","","","")); intents.push_back(NiftiIntentEnum(NIFTI_INTENT_POINTSET,"NIFTI_INTENT_POINTSET", 1008,"Point Set","","","")); intents.push_back(NiftiIntentEnum(NIFTI_INTENT_TRIANGLE,"NIFTI_INTENT_TRIANGLE", 1009,"Triangle","","","")); intents.push_back(NiftiIntentEnum(NIFTI_INTENT_QUATERNION,"NIFTI_INTENT_QUATERNION", 1010,"Quaternion","","","")); intents.push_back(NiftiIntentEnum(NIFTI_INTENT_DIMLESS,"NIFTI_INTENT_DIMLESS", 1011,"Dimensionless Number","","","")); intents.push_back(NiftiIntentEnum(NIFTI_INTENT_TIME_SERIES,"NIFTI_INTENT_TIME_SERIES", 2001,"Time Series","","","")); intents.push_back(NiftiIntentEnum(NIFTI_INTENT_NODE_INDEX,"NIFTI_INTENT_NODE_INDEX", 2002,"Node Index","","","")); intents.push_back(NiftiIntentEnum(NIFTI_INTENT_RGB_VECTOR,"NIFTI_INTENT_RGB_VECTOR", 2003,"RGB","","","")); intents.push_back(NiftiIntentEnum(NIFTI_INTENT_RGBA_VECTOR,"NIFTI_INTENT_RGBA_VECTOR", 2004,"RGBA","","","")); intents.push_back(NiftiIntentEnum(NIFTI_INTENT_SHAPE,"NIFTI_INTENT_SHAPE", 2005,"Shape","","","")); intents.push_back(NiftiIntentEnum(NIFTI_INTENT_CARET_DEFORMATION_NODE_INDICES,"NIFTI_INTENT_CARET_DEFORMATION_NODE_INDICES", 25000,"Deformation Node Indices","","","")); intents.push_back(NiftiIntentEnum(NIFTI_INTENT_CARET_DEFORMATION_NODE_AREAS,"NIFTI_INTENT_CARET_DEFORMATION_NODE_AREAS", 25001,"Deformation Node Areas","","","")); intents.push_back(NiftiIntentEnum(NIFTI_INTENT_CONNECTIVITY_DENSE,"NIFTI_INTENT_CONNECTIVITY_DENSE", 3001,"Connectivity - Dense","","","")); intents.push_back(NiftiIntentEnum(NIFTI_INTENT_CONNECTIVITY_DENSE_TIME,"NIFTI_INTENT_CONNECTIVITY_DENSE_TIME", 3002,"Connectivity - Dense Time Series","","","")); intents.push_back(NiftiIntentEnum(NIFTI_INTENT_CONNECTIVITY_PARCELLATED,"NIFTI_INTENT_CONNECTIVITY_PARCELLATED", 3003,"Connectivity - Parcellated","","","")); intents.push_back(NiftiIntentEnum(NIFTI_INTENT_CONNECTIVITY_PARCELLATED_TIME,"NIFTI_INTENT_CONNECTIVITY_PARCELLATED_TIME", 3004,"Connectivity - Parcellated Time Series","","","")); intents.push_back(NiftiIntentEnum(NIFTI_INTENT_CONNECTIVITY_TRAJECTORY,"NIFTI_INTENT_CONNECTIVITY_TRAJECTORY", 3005,"Connectivity - Trajectory","","","")); } /** * Get a string representition of the enumerated type. * @param e * Enumerated value. * @return * String representing enumerated value. */ AString NiftiIntentEnum::toName(Enum e) { initializeIntents(); const NiftiIntentEnum* ni = findData(e); return ni->enumName; } /** * Get an enumerated value corresponding to its 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. */ NiftiIntentEnum::Enum NiftiIntentEnum::fromName(const AString& s, bool* isValidOut) { initializeIntents(); bool validFlag = false; Enum e = NIFTI_INTENT_NONE; for (std::vector::const_iterator iter = intents.begin(); iter != intents.end(); iter++) { const NiftiIntentEnum& intent = *iter; if (intent.enumName == s) { e = intent.e; validFlag = true; break; } } if (isValidOut != 0) { *isValidOut = validFlag; } return e; } /** * Find the Intent object corresponding to the enum. * @param e * The enum * @return * The Intent or NULL if enum does not match an intent. */ const NiftiIntentEnum* NiftiIntentEnum::findData(Enum e) { initializeIntents(); for (std::vector::const_iterator iter = intents.begin(); iter != intents.end(); iter++) { const NiftiIntentEnum& intent = *iter; if (intent.e == e) { return &intent; } } CaretAssertMessage(0, "Intent enum failed to match."); return NULL; } /** * Get the "P1" name associated with an intent. * @param e * The enum. * @return * P1 name associated with intent (may be empty string). */ AString NiftiIntentEnum::toNameP1(Enum e) { initializeIntents(); const NiftiIntentEnum* ni = findData(e); return ni->p1Name; } /** * Get the "P2" name associated with an intent. * @param e * The enum. * @return * P2 name associated with intent (may be empty string). */ AString NiftiIntentEnum::toNameP2(Enum e) { initializeIntents(); const NiftiIntentEnum* ni = findData(e); return ni->p2Name; } /** * Get the "P3" name associated with an intent. * @param e * The enum. * @return * P3 name associated with intent (may be empty string). */ AString NiftiIntentEnum::toNameP3(Enum e) { initializeIntents(); const NiftiIntentEnum* ni = findData(e); return ni->p3Name; } /** * Get the integer code associated with an intent. * @param e * The enum. * @return * Integer code associated with intent. */ int32_t NiftiIntentEnum::toIntegerCode(Enum e) { initializeIntents(); const NiftiIntentEnum* ni = findData(e); return ni->integerCode; } /** * Find enum corresponding to integer code. * @param integerCode * The integer code. * @param isValidOut * If not NULL, on exit it indicates valid integer code. * @return * Enum corresponding to integer code. */ NiftiIntentEnum::Enum NiftiIntentEnum::fromIntegerCode(const int32_t integerCode, bool* isValidOut) { initializeIntents(); bool validFlag = false; Enum e = NIFTI_INTENT_NONE; for (std::vector::const_iterator iter = intents.begin(); iter != intents.end(); iter++) { const NiftiIntentEnum& intent = *iter; if (intent.integerCode == integerCode) { e = intent.e; validFlag = true; break; } } if (isValidOut != 0) { *isValidOut = validFlag; } return e; } ///Nifti Spacing Units Enum NiftiSpacingUnitsEnum::NiftiSpacingUnitsEnum(Enum e, const int32_t integerCode, const AString& name) { this->e = e; this->integerCode = integerCode; this->name = name; } NiftiSpacingUnitsEnum::~NiftiSpacingUnitsEnum() { } void NiftiSpacingUnitsEnum::initializeSpacingUnits() { if (initializedFlag) { return; } initializedFlag = true; spacingUnits.push_back(NiftiSpacingUnitsEnum(NIFTI_UNITS_UNKNOWN, 0, "NIFTI_UNITS_UNKNOWN")); spacingUnits.push_back(NiftiSpacingUnitsEnum(NIFTI_UNITS_METER, 1, "NIFTI_UNITS_METER")); spacingUnits.push_back(NiftiSpacingUnitsEnum(NIFTI_UNITS_MM, 2, "NIFTI_UNITS_MM")); spacingUnits.push_back(NiftiSpacingUnitsEnum(NIFTI_UNITS_MICRON, 3, "NIFTI_UNITS_MICRON")); } /** * Get a string representition of the enumerated type. * @param e * Enumerated value. * @return * String representing enumerated value. */ AString NiftiSpacingUnitsEnum::toName(Enum e) { initializeSpacingUnits(); const NiftiSpacingUnitsEnum* nsu = findData(e); return nsu->name; } /** * Get an enumerated value corresponding to its 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. */ NiftiSpacingUnitsEnum::Enum NiftiSpacingUnitsEnum::fromName(const AString& s, bool* isValidOut) { initializeSpacingUnits(); bool validFlag = false; Enum e = NIFTI_UNITS_UNKNOWN; for (std::vector::iterator iter = spacingUnits.begin(); iter != spacingUnits.end(); iter++) { const NiftiSpacingUnitsEnum& ndt = *iter; if (ndt.name == s) { e = ndt.e; validFlag = true; break; } } if (isValidOut != 0) { *isValidOut = validFlag; } return e; } /** * Find the Intent object corresponding to the enum. * @param e * The enum * @return * The Intent or NULL if enum does not match an intent. */ const NiftiSpacingUnitsEnum* NiftiSpacingUnitsEnum::findData(Enum e) { initializeSpacingUnits(); for (std::vector::const_iterator iter = spacingUnits.begin(); iter != spacingUnits.end(); iter++) { const NiftiSpacingUnitsEnum& nsu = *iter; if (nsu.e == e) { return &nsu; } return &nsu; } CaretAssertMessage(0, "Spacing Units enum failed to match."); return NULL; } /** * Get the integer code associated with an spacing units. * @param e * The enum. * @return * Integer code associated with spacing units. */ int32_t NiftiSpacingUnitsEnum::toIntegerCode(Enum e) { initializeSpacingUnits(); const NiftiSpacingUnitsEnum* nsu = findData(e); return nsu->integerCode; } /** * Find enum corresponding to integer code. * @param integerCode * The integer code. * @param isValidOut * If not NULL, on exit it indicates valid integer code. * @return * Enum corresponding to integer code. */ NiftiSpacingUnitsEnum::Enum NiftiSpacingUnitsEnum::fromIntegerCode(const int32_t integerCode, bool* isValidOut) { initializeSpacingUnits(); bool validFlag = false; Enum e = NIFTI_UNITS_UNKNOWN; for (std::vector::const_iterator iter = spacingUnits.begin(); iter != spacingUnits.end(); iter++) { const NiftiSpacingUnitsEnum& nsu = *iter; if (nsu.integerCode == integerCode) { e = nsu.e; validFlag = true; break; } } if (isValidOut != 0) { *isValidOut = validFlag; } return e; } ///Nifti Time Units Enum /** * Constructor. * * @param e * An enumerated value. * @param name * Name of enumberated value. * @param guiName * Name in GUI of enumberated value. */ NiftiTimeUnitsEnum::NiftiTimeUnitsEnum(const Enum e, const int32_t integerCode, const AString& name, const AString& guiName) { this->e = e; this->integerCode = integerCode; this->name = name; this->guiName = guiName; } /** * Destructor. */ NiftiTimeUnitsEnum::~NiftiTimeUnitsEnum() { } void NiftiTimeUnitsEnum::initializeTimeUnits() { if (initializedFlag) { return; } initializedFlag = true; enumData.push_back(NiftiTimeUnitsEnum(NIFTI_UNITS_UNKNOWN, 0,"NIFTI_UNITS_UNKNOWN","Unknown")); enumData.push_back(NiftiTimeUnitsEnum(NIFTI_UNITS_SEC, 8,"NIFTI_UNITS_SEC","Seconds")); enumData.push_back(NiftiTimeUnitsEnum(NIFTI_UNITS_MSEC, 16,"NIFTI_UNITS_MSEC","Milliseconds")); enumData.push_back(NiftiTimeUnitsEnum(NIFTI_UNITS_USEC, 24,"NIFTI_UNITS_USEC","Microseconds")); enumData.push_back(NiftiTimeUnitsEnum(NIFTI_UNITS_HZ, 32,"NIFTI_UNITS_HZ","Hertz")); enumData.push_back(NiftiTimeUnitsEnum(NIFTI_UNITS_PPM, 40,"NIFTI_UNITS_PPM","Parts Per Million")); } /** * Find the data for and enumerated value. * @param e * The enumerated value. * @return Pointer to data for this enumerated type * or NULL if no data for type or if type is invalid. */ const NiftiTimeUnitsEnum* NiftiTimeUnitsEnum::findData(const Enum e) { initializeTimeUnits(); int64_t num = enumData.size(); for (int64_t i = 0; i < num; i++) { const NiftiTimeUnitsEnum* d = &enumData[i]; if (d->e == e) { return d; } } CaretAssertMessage(0, "Time Units enum failed to match."); return NULL; } /** * Get a string representation of the enumerated type. * @param e * Enumerated value. * @param isValidOut * If not NULL, it is set indicating that a * label exists for the input enum value. * @return * String representing enumerated value. */ AString NiftiTimeUnitsEnum::toName(Enum e) { initializeTimeUnits(); const NiftiTimeUnitsEnum* ntu = findData(e); return ntu->name; } /** * Get an enumerated value corresponding to its 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. */ NiftiTimeUnitsEnum::Enum NiftiTimeUnitsEnum::fromName(const AString& s, bool* isValidOut) { initializeTimeUnits(); bool validFlag = false; Enum e = NIFTI_UNITS_UNKNOWN; for (std::vector::iterator iter = enumData.begin(); iter != enumData.end(); iter++) { const NiftiTimeUnitsEnum& d = *iter; if (d.name == s) { e = d.e; validFlag = true; break; } } if (isValidOut != 0) { *isValidOut = validFlag; } return e; } /** * Get the integer code associated with an time units. * @param e * The enum. * @return * Integer code associated with time units. */ int32_t NiftiTimeUnitsEnum::toIntegerCode(Enum e) { initializeTimeUnits(); const NiftiTimeUnitsEnum* nsu = findData(e); return nsu->integerCode; } /** * Find enum corresponding to integer code. * @param integerCode * The integer code. * @param isValidOut * If not NULL, on exit it indicates valid integer code. * @return * Enum corresponding to integer code. */ NiftiTimeUnitsEnum::Enum NiftiTimeUnitsEnum::fromIntegerCode(const int32_t integerCode, bool* isValidOut) { initializeTimeUnits(); bool validFlag = false; Enum e = NIFTI_UNITS_UNKNOWN; for (std::vector::const_iterator iter = enumData.begin(); iter != enumData.end(); iter++) { const NiftiTimeUnitsEnum& nsu = *iter; if (nsu.integerCode == integerCode) { e = nsu.e; validFlag = true; break; } } if (isValidOut != 0) { *isValidOut = validFlag; } return e; } /** * Get a string representation for GUI of the enumerated type. * @param e * Enumerated value. * @param isValidOut * If not NULL, it is set indicating that a * label exists for the input enum value. * @return * String representing enumerated value for GUI. */ AString NiftiTimeUnitsEnum::toGuiName(Enum e) { initializeTimeUnits(); const NiftiTimeUnitsEnum* ntu = findData(e); return ntu->guiName; } /** * Constructor. * * @param e * An enumerated value. * @param name * Name of enumberated value. */ NiftiTransformEnum::NiftiTransformEnum( const Enum e, const int32_t integerCode, const AString& name) { this->e = e; this->integerCode = integerCode; this->name = name; } /** * Destructor. */ NiftiTransformEnum::~NiftiTransformEnum() { } void NiftiTransformEnum::initialize() { if (initializedFlag) { return; } initializedFlag = true; enumData.push_back(NiftiTransformEnum(NIFTI_XFORM_UNKNOWN, 0,"NIFTI_XFORM_UNKNOWN")); enumData.push_back(NiftiTransformEnum(NIFTI_XFORM_SCANNER_ANAT, 1,"NIFTI_XFORM_SCANNER_ANAT")); enumData.push_back(NiftiTransformEnum(NIFTI_XFORM_ALIGNED_ANAT, 2,"NIFTI_XFORM_ALIGNED_ANAT")); enumData.push_back(NiftiTransformEnum(NIFTI_XFORM_TALAIRACH, 3,"NIFTI_XFORM_TALAIRACH")); enumData.push_back(NiftiTransformEnum(NIFTI_XFORM_MNI_152, 4,"NIFTI_XFORM_MNI_152")); } /** * Find the data for and enumerated value. * @param e * The enumerated value. * @return Pointer to data for this enumerated type * or NULL if no data for type or if type is invalid. */ const NiftiTransformEnum* NiftiTransformEnum::findData(const Enum e) { initialize(); int64_t num = enumData.size(); for (int64_t i = 0; i < num; i++) { const NiftiTransformEnum* d = &enumData[i]; if (d->e == e) { return d; } } return NULL; } /** * Get a string representation of the enumerated type. * @param e * Enumerated value. * @param isValidOut * If not NULL, it is set indicating that a * label exists for the input enum value. * @return * String representing enumerated value. */ AString NiftiTransformEnum::toName(Enum e) { initialize(); const NiftiTransformEnum* nt = findData(e); return nt->name; } /** * Get an enumerated value corresponding to its 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. */ NiftiTransformEnum::Enum NiftiTransformEnum::fromName(const AString& s, bool* isValidOut) { initialize(); bool validFlag = false; Enum e = NIFTI_XFORM_UNKNOWN; for (std::vector::iterator iter = enumData.begin(); iter != enumData.end(); iter++) { const NiftiTransformEnum& d = *iter; if (d.name == s) { e = d.e; validFlag = true; break; } } if (isValidOut != 0) { *isValidOut = validFlag; } return e; } /** * Get the integer code associated with a transform. * @param e * The enum. * @return * Integer code associated with a transform. */ int32_t NiftiTransformEnum::toIntegerCode(Enum e) { initialize(); const NiftiTransformEnum* nsu = findData(e); return nsu->integerCode; } /** * Find enum corresponding to integer code. * @param integerCode * The integer code. * @param isValidOut * If not NULL, on exit it indicates valid integer code. * @return * Enum corresponding to integer code. */ NiftiTransformEnum::Enum NiftiTransformEnum::fromIntegerCode(const int32_t integerCode, bool* isValidOut) { initialize(); bool validFlag = false; Enum e = NIFTI_XFORM_UNKNOWN; for (std::vector::const_iterator iter = enumData.begin(); iter != enumData.end(); iter++) { const NiftiTransformEnum& nsu = *iter; if (nsu.integerCode == integerCode) { e = nsu.e; validFlag = true; break; } } if (isValidOut != 0) { *isValidOut = validFlag; } return e; } /** * Constructor. * * @param e * An enumerated value. * @param name * Name of enumberated value. */ NiftiVersionEnum::NiftiVersionEnum( const Enum e, const int32_t integerCode, const AString& name) { this->e = e; this->integerCode = integerCode; this->name = name; } /** * Destructor. */ NiftiVersionEnum::~NiftiVersionEnum() { } void NiftiVersionEnum::initialize() { if (initializedFlag) { return; } initializedFlag = true; enumData.push_back(NiftiVersionEnum(NIFTI_VERSION_1, 348, "NIFTI_VERSION_1")); enumData.push_back(NiftiVersionEnum(NIFTI_VERSION_2, 540, "NIFTI_VERSION_2")); } /** * Find the data for and enumerated value. * @param e * The enumerated value. * @return Pointer to data for this enumerated type * or NULL if no data for type or if type is invalid. */ const NiftiVersionEnum* NiftiVersionEnum::findData(const Enum e) { initialize(); int64_t num = enumData.size(); for (int64_t i = 0; i < num; i++) { const NiftiVersionEnum* d = &enumData[i]; if (d->e == e) { return d; } } CaretAssertMessage(0, "NIFTI Version enum failed to match."); return NULL; } /** * Get a string representation of the enumerated type. * @param e * Enumerated value. * @return * String representing enumerated value. */ AString NiftiVersionEnum::toName(Enum e) { initialize(); const NiftiVersionEnum* nv = findData(e); return nv->name; } /** * Get an enumerated value corresponding to its 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. */ NiftiVersionEnum::Enum NiftiVersionEnum::fromName(const AString& s, bool* isValidOut) { initialize(); bool validFlag = false; Enum e = NIFTI_VERSION_1; for (std::vector::iterator iter = enumData.begin(); iter != enumData.end(); iter++) { const NiftiVersionEnum& d = *iter; if (d.name == s) { e = d.e; validFlag = true; break; } } if (isValidOut != 0) { *isValidOut = validFlag; } return e; } /** * Get the integer code associated with a transform. * @param e * The enum. * @return * Integer code associated with a transform. */ int32_t NiftiVersionEnum::toIntegerCode(Enum e) { initialize(); const NiftiVersionEnum* nsu = findData(e); return nsu->integerCode; } /** * Find enum corresponding to integer code. * @param integerCode * The integer code. * @param isValidOut * If not NULL, on exit it indicates valid integer code. * @return * Enum corresponding to integer code. */ NiftiVersionEnum::Enum NiftiVersionEnum::fromIntegerCode(const int32_t integerCode, bool* isValidOut) { initialize(); bool validFlag = false; Enum e = NIFTI_VERSION_1; for (std::vector::const_iterator iter = enumData.begin(); iter != enumData.end(); iter++) { const NiftiVersionEnum& nsu = *iter; if (nsu.integerCode == integerCode) { e = nsu.e; validFlag = true; break; } } if (isValidOut != 0) { *isValidOut = validFlag; } return e; } connectome-workbench-1.2.3+git41-gc4c6c90/src/FilesBase/NiftiEnums.h000066400000000000000000000323051300200146000246360ustar00rootroot00000000000000#ifndef __NIFTI_ENUMS_H__ #define __NIFTI_ENUMS_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 "nifti2.h" namespace caret { //we specify here whether to write in native byte order, or to honor the original //byte order, we could also get clever and try to determine whether or not we are //a big or little-endian machine, but the key concept to keep track of is whether or //not we want to honor the byte order of the original file (for in place read/write) //not the actual byte order. This should also function portably. enum NIFTI_BYTE_ORDER { NATIVE_BYTE_ORDER, SWAPPED_BYTE_ORDER }; /** * NIFTI Data Types. Note that only a small subset are used * in GIFTI data files. */ class NiftiDataTypeEnum { public: /** NIFTI Data Types. Note that only a small subset are used * in GIFTI data files. */ enum Enum { /** invalid data type. */ NIFTI_TYPE_INVALID = 0, /** unsigned byte. */ NIFTI_TYPE_UINT8 = ::NIFTI_TYPE_UINT8, /** signed short. */ NIFTI_TYPE_INT16 = ::NIFTI_TYPE_INT16, /** signed int. */ NIFTI_TYPE_INT32 = ::NIFTI_TYPE_INT32, /** 32 bit float. */ NIFTI_TYPE_FLOAT32 = ::NIFTI_TYPE_FLOAT32, /** 64 bit complex = 2 32 bit floats. */ NIFTI_TYPE_COMPLEX64 = ::NIFTI_TYPE_COMPLEX64, /** 64 bit float = double. */ NIFTI_TYPE_FLOAT64 = ::NIFTI_TYPE_FLOAT64, /** 3 8 bit bytes. */ NIFTI_TYPE_RGB24 = ::NIFTI_TYPE_RGB24, /** signed char. */ NIFTI_TYPE_INT8 = ::NIFTI_TYPE_INT8, /** unsigned short. */ NIFTI_TYPE_UINT16 = ::NIFTI_TYPE_UINT16, /** unsigned int. */ NIFTI_TYPE_UINT32 = ::NIFTI_TYPE_UINT32, /** signed long long. */ NIFTI_TYPE_INT64 = ::NIFTI_TYPE_INT64, /** unsigned long long. */ NIFTI_TYPE_UINT64 = ::NIFTI_TYPE_UINT64, /** 128 bit float = long double. */ NIFTI_TYPE_FLOAT128 = ::NIFTI_TYPE_FLOAT128, /** 128 bit complex = 2 64 bit floats. */ NIFTI_TYPE_COMPLEX128 = ::NIFTI_TYPE_COMPLEX128, /** 256 bit complex = 2 128 bit floats */ NIFTI_TYPE_COMPLEX256 = ::NIFTI_TYPE_COMPLEX256 }; ~NiftiDataTypeEnum(); private: NiftiDataTypeEnum(); NiftiDataTypeEnum(Enum e, const AString& name, const int32_t integerCode); static const NiftiDataTypeEnum* findData(Enum e); static std::vector dataTypes; static void createDataTypes(); static bool dataTypesCreatedFlag; Enum e; AString name; int32_t integerCode; public: static AString toName(Enum e); static Enum fromName(const AString& s, bool* isValidOut); static int32_t toIntegerCode(Enum e); static Enum fromIntegerCode(const int32_t integerCode, bool* isValidOut); }; /** * NIFTI Intent codes and related parameters. */ class NiftiIntentEnum { public: /** NIFTI Intent codes and related parameters. */ enum Enum { /** */ NIFTI_INTENT_NONE = ::NIFTI_INTENT_NONE, /** */ NIFTI_INTENT_CORREL = ::NIFTI_INTENT_CORREL, /** */ NIFTI_INTENT_TTEST = ::NIFTI_INTENT_FTEST, /** */ NIFTI_INTENT_FTEST = ::NIFTI_INTENT_FTEST, /** */ NIFTI_INTENT_ZSCORE = ::NIFTI_INTENT_ZSCORE, /** */ NIFTI_INTENT_CHISQ = ::NIFTI_INTENT_CHISQ, /** */ NIFTI_INTENT_BETA = ::NIFTI_INTENT_BETA, /** */ NIFTI_INTENT_BINOM = ::NIFTI_INTENT_BINOM, /** */ NIFTI_INTENT_GAMMA = ::NIFTI_INTENT_GAMMA, /** */ NIFTI_INTENT_POISSON = ::NIFTI_INTENT_POISSON, /** */ NIFTI_INTENT_NORMAL = ::NIFTI_INTENT_NORMAL, /** */ NIFTI_INTENT_FTEST_NONC = ::NIFTI_INTENT_FTEST_NONC, /** */ NIFTI_INTENT_CHISQ_NONC = ::NIFTI_INTENT_CHISQ_NONC, /** */ NIFTI_INTENT_LOGISTIC = ::NIFTI_INTENT_LOGISTIC, /** */ NIFTI_INTENT_LAPLACE = ::NIFTI_INTENT_LAPLACE, /** */ NIFTI_INTENT_UNIFORM = ::NIFTI_INTENT_UNIFORM, /** */ NIFTI_INTENT_TTEST_NONC = ::NIFTI_INTENT_TTEST_NONC, /** */ NIFTI_INTENT_WEIBULL = ::NIFTI_INTENT_WEIBULL, /** */ NIFTI_INTENT_CHI = ::NIFTI_INTENT_CHI, /** */ NIFTI_INTENT_INVGAUSS = ::NIFTI_INTENT_INVGAUSS, /** */ NIFTI_INTENT_EXTVAL = ::NIFTI_INTENT_EXTVAL, /** */ NIFTI_INTENT_PVAL = ::NIFTI_INTENT_PVAL, /** */ NIFTI_INTENT_LOGPVAL = ::NIFTI_INTENT_LOGPVAL, /** */ NIFTI_INTENT_LOG10PVAL = ::NIFTI_INTENT_LOG10PVAL, /** */ NIFTI_INTENT_ESTIMATE = ::NIFTI_INTENT_ESTIMATE, /** */ NIFTI_INTENT_LABEL = ::NIFTI_INTENT_LABEL, /** */ NIFTI_INTENT_NEURONAME = ::NIFTI_INTENT_NEURONAME, /** */ NIFTI_INTENT_GENMATRIX = ::NIFTI_INTENT_GENMATRIX, /** */ NIFTI_INTENT_SYMMATRIX = ::NIFTI_INTENT_SYMMATRIX, /** */ NIFTI_INTENT_DISPVECT = ::NIFTI_INTENT_DISPVECT, /** */ NIFTI_INTENT_VECTOR = ::NIFTI_INTENT_VECTOR, /** */ NIFTI_INTENT_POINTSET = ::NIFTI_INTENT_POINTSET, /** */ NIFTI_INTENT_TRIANGLE = ::NIFTI_INTENT_TRIANGLE, /** */ NIFTI_INTENT_QUATERNION = ::NIFTI_INTENT_QUATERNION, /** */ NIFTI_INTENT_DIMLESS = ::NIFTI_INTENT_DIMLESS, /** */ NIFTI_INTENT_TIME_SERIES = 2001, /** */ NIFTI_INTENT_NODE_INDEX = 2002, /** */ NIFTI_INTENT_RGB_VECTOR = 2003, /** */ NIFTI_INTENT_RGBA_VECTOR = 2004, /** */ NIFTI_INTENT_SHAPE = 2005, /** */ NIFTI_INTENT_CARET_DEFORMATION_NODE_INDICES = 25000, /** */ NIFTI_INTENT_CARET_DEFORMATION_NODE_AREAS = 25001, /** */ NIFTI_INTENT_CONNECTIVITY_DENSE = ::NIFTI_INTENT_CONNECTIVITY_DENSE, /** */ NIFTI_INTENT_CONNECTIVITY_DENSE_TIME = ::NIFTI_INTENT_CONNECTIVITY_DENSE_TIME, /** */ NIFTI_INTENT_CONNECTIVITY_PARCELLATED = ::NIFTI_INTENT_CONNECTIVITY_PARCELLATED, /** */ NIFTI_INTENT_CONNECTIVITY_PARCELLATED_TIME = ::NIFTI_INTENT_CONNECTIVITY_PARCELLATED_TIME, /** */ NIFTI_INTENT_CONNECTIVITY_TRAJECTORY = ::NIFTI_INTENT_CONNECTIVITY_DENSE_TRAJECTORY }; ~NiftiIntentEnum(); private: NiftiIntentEnum(const Enum e, const AString& enumName, const int32_t integerCode, const AString& name, const AString& p1Name, const AString& p2Name, const AString& p3Name); //NiftiIntentEnum(); Enum e; AString enumName; int32_t integerCode; AString name; AString p1Name; AString p2Name; AString p3Name; static void initializeIntents(); static const NiftiIntentEnum* findData(Enum e); static std::vector intents; static bool intentsCreatedFlag; public: static AString toName(Enum e); static AString toNameP1(Enum e); static AString toNameP2(Enum e); static AString toNameP3(Enum e); static Enum fromName(const AString& s, bool* isValidOut); static int32_t toIntegerCode(Enum e); static Enum fromIntegerCode(const int32_t integerCode, bool* isValidOut); }; /** * NIFTI Spacing Units */ class NiftiSpacingUnitsEnum { public: /** NIFTI Spacing Units */ enum Enum { /** */ NIFTI_UNITS_UNKNOWN = ::NIFTI_UNITS_UNKNOWN, /** */ NIFTI_UNITS_METER = ::NIFTI_UNITS_METER, /** */ NIFTI_UNITS_MM = ::NIFTI_UNITS_MM, /** */ NIFTI_UNITS_MICRON = ::NIFTI_UNITS_MICRON }; ~NiftiSpacingUnitsEnum(); private: NiftiSpacingUnitsEnum(Enum e, const int32_t integerCode, const AString& name); static const NiftiSpacingUnitsEnum* findData(Enum e); static std::vector spacingUnits; static void initializeSpacingUnits(); static bool initializedFlag; Enum e; int32_t integerCode; AString name; public: static AString toName(Enum e); static Enum fromName(const AString& s, bool* isValidOut); static int32_t toIntegerCode(Enum e); static Enum fromIntegerCode(const int32_t integerCode, bool* isValidOut); }; /** * NIFTI Time Units */ class NiftiTimeUnitsEnum { public: /** NIFTI Time Units */ enum Enum { /** Unknown */ NIFTI_UNITS_UNKNOWN = ::NIFTI_UNITS_UNKNOWN, /** Seconds */ NIFTI_UNITS_SEC = ::NIFTI_UNITS_SEC, /** Milliseconds */ NIFTI_UNITS_MSEC = ::NIFTI_UNITS_MSEC, /** Microseconds */ NIFTI_UNITS_USEC = ::NIFTI_UNITS_USEC, /** Hertz */ NIFTI_UNITS_HZ = ::NIFTI_UNITS_HZ, /** Parts Per Million */ NIFTI_UNITS_PPM = ::NIFTI_UNITS_PPM }; ~NiftiTimeUnitsEnum(); static AString toName(Enum e); static Enum fromName(const AString& s, bool* isValidOut); static int32_t toIntegerCode(Enum e); static Enum fromIntegerCode(const int32_t integerCode, bool* isValidOut); static AString toGuiName(Enum e); private: NiftiTimeUnitsEnum(const Enum e, const int32_t integerCode, const AString& name, const AString& guiName); static const NiftiTimeUnitsEnum* findData(const Enum e); static std::vector enumData; static void initializeTimeUnits(); static bool initializedFlag; Enum e; int32_t integerCode; AString name; AString guiName; }; /** * NIFTI Transform. */ class NiftiTransformEnum { public: /** NIFTI Transform. */ enum Enum { /** Arbitrary Coordinates */ NIFTI_XFORM_UNKNOWN = ::NIFTI_XFORM_UNKNOWN, /** Scanner-base anatomical coordinates */ NIFTI_XFORM_SCANNER_ANAT = ::NIFTI_XFORM_SCANNER_ANAT, /** Coordinates aligned to another file's or anatomial "truth" */ NIFTI_XFORM_ALIGNED_ANAT = ::NIFTI_XFORM_ALIGNED_ANAT, /** Coordinates aligned to Talairach-Tournoux Atlas: (0,0,0) = Anterior Commissure */ NIFTI_XFORM_TALAIRACH = ::NIFTI_XFORM_TALAIRACH, /** MNI 152 Normalize Coordinates */ NIFTI_XFORM_MNI_152 = ::NIFTI_XFORM_MNI_152 }; ~NiftiTransformEnum(); static AString toName(Enum e); static Enum fromName(const AString& s, bool* isValidOut); static int32_t toIntegerCode(Enum e); static Enum fromIntegerCode(const int32_t integerCode, bool* isValidOut); private: NiftiTransformEnum(const Enum e, const int32_t integerCode, const AString& name); static std::vector enumData; static const NiftiTransformEnum* findData(const Enum e); static void initialize(); static bool initializedFlag; Enum e; int32_t integerCode; AString name; }; /** * The NIFTI version */ class NiftiVersionEnum { public: /** The NIFTI version */ enum Enum { /** NIFTI-1 */ NIFTI_VERSION_1, /** NIFTI-2 */ NIFTI_VERSION_2 }; ~NiftiVersionEnum(); static AString toName(Enum e); static Enum fromName(const AString& s, bool* isValidOut); static int32_t toIntegerCode(Enum e); static Enum fromIntegerCode(const int32_t integerCode, bool* isValidOut); private: NiftiVersionEnum(const Enum e, const int32_t integerCode, const AString& name); static std::vector enumData; static void initialize(); static bool initializedFlag; Enum e; int32_t integerCode; AString name; static const NiftiVersionEnum* findData(const Enum e); }; #ifdef __NIFTI_ENUMS_DECLARE__ std::vector NiftiDataTypeEnum::dataTypes; bool NiftiDataTypeEnum::dataTypesCreatedFlag = false; std::vector NiftiIntentEnum::intents; bool NiftiIntentEnum::intentsCreatedFlag = false; std::vector NiftiSpacingUnitsEnum::spacingUnits; bool NiftiSpacingUnitsEnum::initializedFlag = false; std::vector NiftiTimeUnitsEnum::enumData; bool NiftiTimeUnitsEnum::initializedFlag = false; std::vector NiftiTransformEnum::enumData; bool NiftiTransformEnum::initializedFlag = false; std::vector NiftiVersionEnum::enumData; bool NiftiVersionEnum::initializedFlag = false; #endif // __NIFTI_ENUMS_DECLARE__ } // namespace #endif // __NIFTI_ENUMS_H connectome-workbench-1.2.3+git41-gc4c6c90/src/FilesBase/VolumeBase.cxx000066400000000000000000000373061300200146000252000ustar00rootroot00000000000000/*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 "VolumeBase.h" #include "DataFileException.h" #include "FloatMatrix.h" #include "GiftiLabelTable.h" #include "GiftiMetaData.h" #include "GiftiXmlElements.h" #include "Palette.h" #include "PaletteColorMapping.h" #include "Vector3D.h" #include using namespace caret; using namespace std; AbstractHeader::~AbstractHeader() { } void VolumeBase::reinitialize(const vector& dimensionsIn, const vector >& indexToSpace, const int64_t numComponents) { CaretAssert(numComponents > 0); clear(); int numDims = (int)dimensionsIn.size(); if (numDims < 3) { throw DataFileException("volume files must have 3 or more dimensions"); } m_origDims = dimensionsIn;//save the original dimensions int64_t storeDims[5]; storeDims[3] = 1; for (int i = 0; i < numDims; ++i) { if (i > 2) { storeDims[3] *= dimensionsIn[i]; } else { storeDims[i] = dimensionsIn[i]; } } m_volSpace.setSpace(storeDims, indexToSpace); if (storeDims[0] == 1 && storeDims[1] == 1 && storeDims[2] == 1 && storeDims[3] > 10000) {//slight hack, to detect if a cifti file is loaded as a volume file, because many 1x1x1 frames could use a surprisingly large amount of memory (metadata, palette, etc) throw DataFileException("this file doesn't appear to be a volume file"); } storeDims[4] = numComponents; m_storage.reinitialize(storeDims); } void VolumeBase::addSubvolumes(const int64_t& numToAdd) { CaretAssert(numToAdd > 0); vector olddims = getDimensions();//use the already flattened dimensions to start, as the non-spatial dimensions must be flattened to add an arbitrary number of maps CaretAssert(olddims[3] > 0);//can't add volumes when we have no dimensions, stop the debugger here if (olddims[3] < 1) { throw DataFileException("cannot call addSubvolumes on an uninitialized VolumeFile");//release shouldn't allow it either } vector newdims = olddims; newdims[3] += numToAdd;//add to the flattened non-spatial dimensions VolumeStorage newStorage(newdims.data()); newdims.resize(4);//drop the number of components from the dimensions array m_origDims = newdims;//and reset our original dimensions for (int64_t c = 0; c < olddims[4]; ++c) { for (int64_t b = 0; b < olddims[3]; ++b) { newStorage.setFrame(m_storage.getFrame(b, c), b, c); } } m_storage.swap(newStorage); setModified();//NOTE: will invalidate splines, can be made more efficient for certain cases when merging Base and File } void VolumeBase::setVolumeSpace(const vector >& indexToSpace) { m_volSpace.setSpace(getDimensionsPtr(), indexToSpace); setModified(); } VolumeBase::VolumeBase() { m_origDims.push_back(0);//give original dimensions 3 elements, just because m_origDims.push_back(0); m_origDims.push_back(0); m_ModifiedFlag = false; } VolumeBase::VolumeBase(const vector& dimensionsIn, const vector >& indexToSpace, const int64_t numComponents) { reinitialize(dimensionsIn, indexToSpace, numComponents); m_ModifiedFlag = true; } void VolumeBase::getOrientAndSpacingForPlumb(VolumeSpace::OrientTypes* orientOut, float* spacingOut, float* centerOut) const { m_volSpace.getOrientAndSpacingForPlumb(orientOut, spacingOut, centerOut); } void VolumeBase::getOrientation(VolumeSpace::OrientTypes orientOut[3]) const { m_volSpace.getOrientation(orientOut); } void VolumeBase::reorient(const VolumeSpace::OrientTypes newOrient[3]) { VolumeSpace::OrientTypes curOrient[3]; getOrientation(curOrient); int curReverse[3];//for each spatial axis, which index currently goes that direction bool curReverseNeg[3]; int doSomething = false;//check whether newOrient is any different for (int i = 0; i < 3; ++i) { if (curOrient[i] != newOrient[i]) doSomething = true; curReverse[curOrient[i] & 3] = i;//int values of the enum are crafted to make this work curReverseNeg[curOrient[i] & 3] = ((curOrient[i] & 4) != 0); } if (!doSomething) return; bool flip[3]; int fetchFrom[3]; for (int i = 0; i < 3; ++i) { flip[i] = curReverseNeg[newOrient[i] & 3] != ((newOrient[i] & 4) != 0); fetchFrom[i] = curReverse[newOrient[i] & 3]; } const int64_t* dims = getDimensionsPtr(); int64_t rowSize = dims[0]; int64_t sliceSize = rowSize * dims[1]; int64_t frameSize = sliceSize * dims[2]; vector scratchFrame(frameSize); int64_t newDims[5] = {dims[fetchFrom[0]], dims[fetchFrom[1]], dims[fetchFrom[2]], dims[3], dims[4]}; VolumeStorage newStorage(newDims); for (int c = 0; c < dims[4]; ++c) { for (int b = 0; b < dims[3]; ++b) { const float* oldFrame = m_storage.getFrame(b, c); int64_t newIndices[3], oldIndices[3]; for (newIndices[2] = 0; newIndices[2] < newDims[2]; ++newIndices[2]) { if (flip[2]) { oldIndices[fetchFrom[2]] = newDims[2] - newIndices[2] - 1; } else { oldIndices[fetchFrom[2]] = newIndices[2]; } for (newIndices[1] = 0; newIndices[1] < newDims[1]; ++newIndices[1]) { if (flip[1]) { oldIndices[fetchFrom[1]] = newDims[1] - newIndices[1] - 1; } else { oldIndices[fetchFrom[1]] = newIndices[1]; } for (newIndices[0] = 0; newIndices[0] < newDims[0]; ++newIndices[0]) { if (flip[0]) { oldIndices[fetchFrom[0]] = newDims[0] - newIndices[0] - 1; } else { oldIndices[fetchFrom[0]] = newIndices[0]; } scratchFrame[newStorage.getIndex(newIndices, 0, 0)] = oldFrame[getIndex(oldIndices)]; } } } newStorage.setFrame(scratchFrame.data(), b, c); } } const vector >& oldSform = m_volSpace.getSform(); vector > indexToSpace = oldSform;//reorder and flip the sform - this might belong in VolumeSpace for (int i = 0; i < 3; ++i) { for (int j = 0; j < 3; ++j) { if (flip[j]) { indexToSpace[i][j] = -oldSform[i][fetchFrom[j]]; indexToSpace[i][3] += oldSform[i][fetchFrom[j]] * (newDims[j] - 1); } else { indexToSpace[i][j] = oldSform[i][fetchFrom[j]]; } } }//and now generate the inverse for spaceToIndex m_volSpace.setSpace(newDims, indexToSpace); m_origDims[0] = newDims[0];//update m_origDims too m_origDims[1] = newDims[1]; m_origDims[2] = newDims[2]; m_storage.swap(newStorage); setModified(); } void VolumeBase::enclosingVoxel(const float* coordIn, int64_t* indexOut) const { enclosingVoxel(coordIn[0], coordIn[1], coordIn[2], indexOut[0], indexOut[1], indexOut[2]); } void VolumeBase::enclosingVoxel(const float& coordIn1, const float& coordIn2, const float& coordIn3, int64_t* indexOut) const { enclosingVoxel(coordIn1, coordIn2, coordIn3, indexOut[0], indexOut[1], indexOut[2]); } void VolumeBase::enclosingVoxel(const float* coordIn, int64_t& indexOut1, int64_t& indexOut2, int64_t& indexOut3) const { enclosingVoxel(coordIn[0], coordIn[1], coordIn[2], indexOut1, indexOut2, indexOut3); } void VolumeBase::enclosingVoxel(const float& coordIn1, const float& coordIn2, const float& coordIn3, int64_t& indexOut1, int64_t& indexOut2, int64_t& indexOut3) const { float tempInd1, tempInd2, tempInd3; spaceToIndex(coordIn1, coordIn2, coordIn3, tempInd1, tempInd2, tempInd3); indexOut1 = (int64_t)floor(0.5f + tempInd1); indexOut2 = (int64_t)floor(0.5f + tempInd2); indexOut3 = (int64_t)floor(0.5f + tempInd3); } int64_t VolumeBase::getBrickIndexFromNonSpatialIndexes(const vector& extraInds) const { CaretAssert(extraInds.size() == m_origDims.size() - 3); int extraDims = (int)extraInds.size(); if (extraDims == 0) return 0; CaretAssert(extraInds[extraDims - 1] >= 0 && extraInds[extraDims - 1] < m_origDims[extraDims + 2]); int64_t ret = extraInds[extraDims - 1]; for (int i = extraDims - 2; i >= 0; --i)//yes, its supposed to loop starting with the second highest dimension { CaretAssert(extraInds[i] >= 0 && extraInds[i] < m_origDims[i + 3]); ret = ret * m_origDims[i + 3] + extraInds[i];//factored polynomial form } CaretAssert(ret < getDimensionsPtr()[3]);//otherwise, dimensions[3] and m_origDims don't match return ret; } vector VolumeBase::getNonSpatialIndexesFromBrickIndex(const int64_t& brickIndex) const { CaretAssert(brickIndex >= 0 && brickIndex < getDimensionsPtr()[3]); vector ret; int extraDims = (int)m_origDims.size() - 3; if (extraDims <= 0) return ret;//empty vector if there are no extra-spatial dimensions, so we don't call resize(0), even though it should be safe ret.resize(extraDims); int64_t myRemaining = brickIndex, temp; for (int i = 0; i < extraDims; ++i) { temp = myRemaining % m_origDims[i + 3];//modulus myRemaining = (myRemaining - temp) / m_origDims[i + 3];//subtract the remainder even though int divide should truncate correctly, just to make it obvious ret[i] = temp; } CaretAssert(myRemaining == 0);//otherwise, m_dimensions[3] and m_origDims don't match return ret; } void VolumeBase::indexToSpace(const int64_t* indexIn, float* coordOut) const { indexToSpace(indexIn[0], indexIn[1], indexIn[2], coordOut[0], coordOut[1], coordOut[2]); } void VolumeBase::indexToSpace(const int64_t* indexIn, float& coordOut1, float& coordOut2, float& coordOut3) const { indexToSpace(indexIn[0], indexIn[1], indexIn[2], coordOut1, coordOut2, coordOut3); } void VolumeBase::indexToSpace(const float* indexIn, float* coordOut) const { indexToSpace(indexIn[0], indexIn[1], indexIn[2], coordOut[0], coordOut[1], coordOut[2]); } void VolumeBase::indexToSpace(const float& indexIn1, const float& indexIn2, const float& indexIn3, float* coordOut) const { indexToSpace(indexIn1, indexIn2, indexIn3, coordOut[0], coordOut[1], coordOut[2]); } void VolumeBase::indexToSpace(const float* indexIn, float& coordOut1, float& coordOut2, float& coordOut3) const { indexToSpace(indexIn[0], indexIn[1], indexIn[2], coordOut1, coordOut2, coordOut3); } void VolumeBase::indexToSpace(const float& indexIn1, const float& indexIn2, const float& indexIn3, float& coordOut1, float& coordOut2, float& coordOut3) const { m_volSpace.indexToSpace(indexIn1, indexIn2, indexIn3, coordOut1, coordOut2, coordOut3); } bool VolumeBase::isPlumb() const { return m_volSpace.isPlumb(); } void VolumeBase::spaceToIndex(const float* coordIn, float* indexOut) const { spaceToIndex(coordIn[0], coordIn[1], coordIn[2], indexOut[0], indexOut[1], indexOut[2]); } void VolumeBase::spaceToIndex(const float& coordIn1, const float& coordIn2, const float& coordIn3, float* indexOut) const { spaceToIndex(coordIn1, coordIn2, coordIn3, indexOut[0], indexOut[1], indexOut[2]); } void VolumeBase::spaceToIndex(const float* coordIn, float& indexOut1, float& indexOut2, float& indexOut3) const { spaceToIndex(coordIn[0], coordIn[1], coordIn[2], indexOut1, indexOut2, indexOut3); } void VolumeBase::spaceToIndex(const float& coordIn1, const float& coordIn2, const float& coordIn3, float& indexOut1, float& indexOut2, float& indexOut3) const { m_volSpace.spaceToIndex(coordIn1, coordIn2, coordIn3, indexOut1, indexOut2, indexOut3); } void VolumeBase::clear() { m_storage.clear(); m_origDims.clear(); m_origDims.resize(3, 0);//give original dimensions 3 elements, just because m_header.grabNew(NULL); } VolumeBase::~VolumeBase() { } /** * Is the file empty (contains no data)? * * @return * true if the file is empty, else false. */ bool VolumeBase::isEmpty() const { return (getDimensionsPtr()[0] <= 0); } VolumeBase::VolumeStorage::VolumeStorage() { for (int i = 0; i < 5; ++i) { m_dimensions[i] = 0; m_mult[i] = 0; } } void VolumeBase::VolumeStorage::reinitialize(int64_t dims[5]) { for (int i = 0; i < 5; ++i) { CaretAssert(dims[i] > 0);//stop the debugger in the right place if (dims[i] < 1) throw DataFileException("VolumeStorage dimensions must be positive");//release shouldn't allow it either, though m_dimensions[i] = dims[i]; } m_mult[0] = m_dimensions[0]; for (int i = 1; i < 5; ++i) { m_mult[i] = m_mult[i - 1] * m_dimensions[i]; } m_data.resize(m_mult[4]); } VolumeBase::VolumeStorage::VolumeStorage(int64_t dims[5]) { reinitialize(dims); } const float* VolumeBase::VolumeStorage::getFrame(const int64_t brickIndex, const int64_t component) const { return m_data.data() + brickIndex * m_mult[2] + component * m_mult[3];//NOTE: do not use [4] } void VolumeBase::VolumeStorage::setFrame(const float* frameIn, const int64_t brickIndex, const int64_t component) { int64_t start = brickIndex * m_mult[2] + component * m_mult[3]; for (int64_t i = 0; i < m_mult[2]; ++i) { m_data[i + start] = frameIn[i]; } } void VolumeBase::VolumeStorage::setValueAllVoxels(const float value) { for (int64_t i = 0; i < m_mult[4]; ++i) { m_data[i] = value; } } void VolumeBase::VolumeStorage::swap(VolumeStorage& rhs) { m_data.swap(rhs.m_data); for (int i = 0; i < 5; ++i) { std::swap(m_dimensions[i], rhs.m_dimensions[i]); std::swap(m_mult[i], rhs.m_mult[i]); } } void VolumeBase::VolumeStorage::getDimensions(vector& dimOut) const { dimOut.resize(5); for (int i = 0; i < 5; ++i) { dimOut[i] = m_dimensions[i]; } } void VolumeBase::VolumeStorage::getDimensions(int64_t& dimOut1, int64_t& dimOut2, int64_t& dimOut3, int64_t& dimTimeOut, int64_t& numComponents) const { dimOut1 = m_dimensions[0]; dimOut2 = m_dimensions[1]; dimOut3 = m_dimensions[2]; dimTimeOut = m_dimensions[3]; numComponents = m_dimensions[4]; } vector VolumeBase::VolumeStorage::getDimensions() const { vector ret; getDimensions(ret); return ret; } void VolumeBase::VolumeStorage::clear() { m_data.clear(); for (int i = 0; i < 5; ++i) { m_dimensions[i] = 0; m_mult[i] = 0; } } /** * @return Is this instance modified? */ bool VolumeBase::isModifiedVolumeBase() const { if (m_ModifiedFlag) { return true; } return false; } /** * Clear this instance's modified status */ void VolumeBase::clearModifiedVolumeBase() { m_ModifiedFlag = false; } /** * Set this instance's status to modified. */ void VolumeBase::setModified() { m_ModifiedFlag = true; } connectome-workbench-1.2.3+git41-gc4c6c90/src/FilesBase/VolumeBase.h000066400000000000000000000447101300200146000246220ustar00rootroot00000000000000 #ifndef __VOLUME_BASE_H__ #define __VOLUME_BASE_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 "stdint.h" #include #include "CaretAssert.h" #include "CaretPointer.h" #include "VolumeMappableInterface.h" #include "VolumeSpace.h" namespace caret { struct AbstractHeader { enum HeaderType { NIFTI }; virtual HeaderType getType() const = 0; virtual AbstractHeader* clone() const = 0; virtual ~AbstractHeader(); }; class VolumeBase : public VolumeMappableInterface { class VolumeStorage { std::vector m_data; int64_t m_dimensions[5];//store internally as 4d+component int64_t m_mult[5];//precalculated multipliers for getIndex/getValue/setValue - NOTE: [0] is for index[1], [4] is the entire size of the data VolumeStorage(const VolumeStorage& rhs);//deny copy, assignment for now VolumeStorage& operator=(const VolumeStorage& rhs); public: VolumeStorage(); VolumeStorage(int64_t dims[5]); 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; std::vector getDimensions() const; const int64_t* getDimensionsPtr() const { return m_dimensions; } inline const int64_t& getNumberOfComponents() const { return m_dimensions[4]; } void swap(VolumeStorage& rhs); ///get a value at three indexes and optionally timepoint inline const float& getValue(const int64_t& indexIn1, const int64_t& indexIn2, const int64_t& indexIn3, const int64_t brickIndex, const int64_t component) const { CaretAssert(indexValid(indexIn1, indexIn2, indexIn3, brickIndex, component));//assert so release version isn't slowed by checking return m_data[getIndex(indexIn1, indexIn2, indexIn3, brickIndex, component)]; } inline const float& getValue(const int64_t indexIn[3], const int64_t brickIndex, const int64_t component) const { return getValue(indexIn[0], indexIn[1], indexIn[2], brickIndex, component); } ///gets index into data array for three indexes plus time index inline int64_t getIndex(const int64_t& indexIn1, const int64_t& indexIn2, const int64_t& indexIn3, const int64_t brickIndex, const int64_t component) const { CaretAssert(indexValid(indexIn1, indexIn2, indexIn3, brickIndex, component)); return indexIn1 + m_mult[0] * indexIn2 + m_mult[1] * indexIn3 + m_mult[2] * brickIndex + m_mult[3] * component; } inline int64_t getIndex(const int64_t indexIn[3], const int64_t brickIndex, const int64_t component) const { return getIndex(indexIn[0], indexIn[1], indexIn[2], brickIndex, component); } ///checks if an index is within array dimensions inline bool indexValid(const int64_t& indexIn1, const int64_t& indexIn2, const int64_t& indexIn3, const int64_t brickIndex, const int64_t component) const {//inlined so that getValue and setValue can get optimized out entirely if (indexIn1 < 0 || indexIn1 >= m_dimensions[0]) return false; if (indexIn2 < 0 || indexIn2 >= m_dimensions[1]) return false; if (indexIn3 < 0 || indexIn3 >= m_dimensions[2]) return false; if (brickIndex < 0 || brickIndex >= m_dimensions[3]) return false; if (component < 0 || component >= m_dimensions[4]) return false; return true; } inline bool indexValid(const int64_t indexIn[3], const int64_t brickIndex, const int64_t component) const { return indexValid(indexIn[0], indexIn[1], indexIn[2], brickIndex, component); } ///set a value at an index triplet and optionally timepoint inline void setValue(const float& valueIn, const int64_t& indexIn1, const int64_t& indexIn2, const int64_t& indexIn3, const int64_t brickIndex, const int64_t component) { CaretAssert(indexValid(indexIn1, indexIn2, indexIn3, brickIndex, component));//assert so release version isn't slowed by checking m_data[getIndex(indexIn1, indexIn2, indexIn3, brickIndex, component)] = valueIn; } inline void setValue(const float& valueIn, const int64_t indexIn[3], const int64_t brickIndex, const int64_t component) { setValue(valueIn, indexIn[0], indexIn[1], indexIn[2], brickIndex, component); } /// set every voxel to the given value void setValueAllVoxels(const float value); ///get a frame (const) const float* getFrame(const int64_t brickIndex = 0, const int64_t component = 0) const; ///set a frame void setFrame(const float* frameIn, const int64_t brickIndex = 0, const int64_t component = 0); }; VolumeStorage m_storage; VolumeSpace m_volSpace; std::vector m_origDims;//keep track of the original dimensions bool m_ModifiedFlag; protected: VolumeBase(); VolumeBase(const std::vector& dimensionsIn, const std::vector >& indexToSpace, const int64_t numComponents = 1); ///recreates the volume file storage with new size and spacing void reinitialize(const std::vector& dimensionsIn, const std::vector >& indexToSpace, const int64_t numComponents = 1); void addSubvolumes(const int64_t& numToAdd); public: void clear(); virtual ~VolumeBase(); ///there isn't much VolumeFile can do to restrict access to the header, so just have it public CaretPointer m_header; ///get the spacing info inline const std::vector >& getSform() const { return m_volSpace.getSform(); } void setVolumeSpace(const std::vector >& indexToSpace); ///get the originally specified dimensions vector inline const std::vector& getOriginalDimensions() const { return m_origDims; } inline const int64_t& getNumberOfComponents() const { return m_storage.getNumberOfComponents(); } ///translates extraspatial indices into a (flat) brick index int64_t getBrickIndexFromNonSpatialIndexes(const std::vector& extraInds) const; ///translates a (flat) brick index into the original extraspatial indices std::vector getNonSpatialIndexesFromBrickIndex(const int64_t& brickIndex) const; ///returns true if volume space is not skew, and each axis and index is separate bool isPlumb() const; ///returns orientation, spacing, and center (spacing/center can be negative, spacing/center is LPI rearranged to ijk (first dimension uses first element), will assert false if isOblique is true) void getOrientAndSpacingForPlumb(VolumeSpace::OrientTypes* orientOut, float* spacingOut, float* centerOut) const; ///get just orientation, even for non-plumb volumes void getOrientation(VolumeSpace::OrientTypes orientOut[3]) const; ///reorient this volume void reorient(const VolumeSpace::OrientTypes newOrient[3]); //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; ///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; ///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; ///returns floating point index triplet of a given coordinate triplet void spaceToIndex(const float* coordIn, float* indexOut) const; ///returns floating point index triplet of three given coordinates void spaceToIndex(const float& coordIn1, const float& coordIn2, const float& coordIn3, float* indexOut) const; ///returns three floating point indexes of a given coordinate triplet void spaceToIndex(const float* coordIn, float& indexOut1, float& indexOut2, float& indexOut3) const; ///returns three floating point indexes of three given coordinates void spaceToIndex(const float& coordIn1, const float& coordIn2, const float& coordIn3, float& indexOut1, float& indexOut2, float& indexOut3) const; ///returns integer index triplet of voxel whose center is closest to the coordinate triplet void enclosingVoxel(const float* coordIn, int64_t* indexOut) const; ///returns integer index triplet of voxel whose center is closest to the three coordinates void enclosingVoxel(const float& coordIn1, const float& coordIn2, const float& coordIn3, int64_t* indexOut) const; ///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; inline const VolumeSpace& getVolumeSpace() const { return m_volSpace; } ///get a value at an index triplet and optionally timepoint inline const float& getValue(const int64_t* indexIn, const int64_t brickIndex = 0, const int64_t component = 0) const { return m_storage.getValue(indexIn[0], indexIn[1], indexIn[2], brickIndex, component); } ///get a value at three indexes and optionally timepoint inline const float& getValue(const int64_t& indexIn1, const int64_t& indexIn2, const int64_t& indexIn3, const int64_t brickIndex = 0, const int64_t component = 0) const { return m_storage.getValue(indexIn1, indexIn2, indexIn3, brickIndex, component); } /** * Get the value of the voxel containing the given coordinate. * * @param coordinateIn * The 3D coordinate * @param validOut * If not NULL, will indicate if the coordinate (and hence the * returned value) is valid. * @param mapIndex * Index of map. * @param component * Voxel component. * @return * Value of voxel containing the given coordinate. */ inline float getVoxelValue(const float* coordinateIn, bool* validOut = NULL, const int64_t mapIndex = 0, const int64_t component = 0) const { return getVoxelValue(coordinateIn[0], coordinateIn[1], coordinateIn[2], validOut, mapIndex, component); } /** * Get the value of the voxel containing the given coordinate. * * @param coordinateX * The X coordinate * @param coordinateY * The Y coordinate * @param coordinateZ * The Z coordinate * @param validOut * If not NULL, will indicate if the coordinate (and hence the * returned value) is valid. * @param mapIndex * Index of map. * @param component * Voxel component. * @return * Value of voxel containing the given coordinate. */ inline 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 { if (validOut != NULL) { *validOut = false; } int64_t voxelI, voxelJ, voxelK; enclosingVoxel(coordinateX, coordinateY, coordinateZ, voxelI, voxelJ, voxelK); if (indexValid(voxelI, voxelJ, voxelK, mapIndex, component)) { if (validOut != NULL) { *validOut = true; } return getValue(voxelI, voxelJ, voxelK, mapIndex, component); } return 0.0; } ///get a frame (const) const float* getFrame(const int64_t brickIndex = 0, const int64_t component = 0) const { return m_storage.getFrame(brickIndex, component); } ///set a value at an index triplet and optionally timepoint inline void setValue(const float& valueIn, const int64_t* indexIn, const int64_t brickIndex = 0, const int64_t component = 0) { m_storage.setValue(valueIn, indexIn[0], indexIn[1], indexIn[2], brickIndex, component); setModified(); } ///set a value at an index triplet and optionally timepoint inline void setValue(const float& valueIn, const int64_t& indexIn1, const int64_t& indexIn2, const int64_t& indexIn3, const int64_t brickIndex = 0, const int64_t component = 0) { m_storage.setValue(valueIn, indexIn1, indexIn2, indexIn3, brickIndex, component); setModified(); } /// set every voxel to the given value void setValueAllVoxels(const float value) { m_storage.setValueAllVoxels(value); setModified(); } ///set a frame 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); } ///gets dimensions void getDimensions(int64_t& dimOut1, int64_t& dimOut2, int64_t& dimOut3, int64_t& dimTimeOut, int64_t& numComponents) const { m_storage.getDimensions(dimOut1, dimOut2, dimOut3, dimTimeOut, numComponents); } ///gets dimensions std::vector getDimensions() const { return m_storage.getDimensions(); } const int64_t* getDimensionsPtr() const { return m_storage.getDimensionsPtr(); } ///gets index into data array for three indexes plus time index inline int64_t getIndex(const int64_t& indexIn1, const int64_t& indexIn2, const int64_t& indexIn3, const int64_t brickIndex = 0, const int64_t component = 0) const { return m_storage.getIndex(indexIn1, indexIn2, indexIn3, brickIndex, component); } inline int64_t getIndex(const int64_t* indexIn, const int64_t brickIndex = 0, const int64_t component = 0) const { return m_storage.getIndex(indexIn[0], indexIn[1], indexIn[2], brickIndex, component); } inline bool indexValid(const int64_t* indexIn, const int64_t brickIndex = 0, const int64_t component = 0) const { return m_storage.indexValid(indexIn[0], indexIn[1], indexIn[2], brickIndex, component); } ///checks if an index is within array dimensions inline 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 {//inlined so that getValue and setValue can get optimized out entirely return m_storage.indexValid(indexIn1, indexIn2, indexIn3, brickIndex, component); } virtual void setModified();//virtual because we need the functions that change voxels in this class to call the setModified in VolumeFile if it really is a VolumeFile (which it always is) void clearModifiedVolumeBase(); bool isModifiedVolumeBase() const; bool isEmpty() const; }; } #endif //__VOLUME_BASE_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/FilesBase/VolumeMappableInterface.cxx000066400000000000000000000066011300200146000276620ustar00rootroot00000000000000 /*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 __VOLUME_MAPPABLE_INTERFACE_DECLARE__ #include "VolumeMappableInterface.h" #undef __VOLUME_MAPPABLE_INTERFACE_DECLARE__ #include "CaretAssert.h" using namespace caret; /** * Get the voxel spacing for each of the spatial dimensions. * * @param spacingOut1 * Spacing for the first dimension (typically X). * @param spacingOut2 * Spacing for the first dimension (typically Y). * @param spacingOut3 * Spacing for the first dimension (typically Z). */ void VolumeMappableInterface::getVoxelSpacing(float& spacingOut1, float& spacingOut2, float& spacingOut3) const { float originX, originY, originZ; float x1, y1, z1; indexToSpace(0, 0, 0, originX, originY, originZ); indexToSpace(1, 1, 1, x1, y1, z1); spacingOut1 = x1 - originX; spacingOut2 = y1 - originY; spacingOut3 = z1 - originZ; } /** * Does this volume have these spatial dimensions? * * @param dim1 * First dimension. * @param dim2 * Second dimension. * @param dim3 * Third dimension. * @return * True if this volume's spatial dimensions match the * given dimensions, else false. */ bool VolumeMappableInterface::matchesDimensions(const int64_t dim1, const int64_t dim2, const int64_t dim3) const { std::vector dims; getDimensions(dims); if (dims.size() >= 3){ if ((dims[0] == dim1) && (dims[1] == dim2) && (dims[2] == dim3)) { return true; } } return false; } /** * Adjust the given indices so that they are valid * in the range 0 to (volume dimension - 1) * * @param index1 * First index. * @param index2 * Second index. * @param index3 * Third index. */ void VolumeMappableInterface::limitIndicesToValidIndices(int64_t& index1, int64_t& index2, int64_t& index3) const { std::vector dims; getDimensions(dims); if (dims.size() >= 3) { if (index1 >= dims[0]) { index1 = dims[0] - 1; } if (index1 < 0) { index1 = 0; } if (index2 >= dims[1]) { index2 = dims[1] - 1; } if (index2 < 0) { index2 = 0; } if (index3 >= dims[2]) { index3 = dims[2] - 1; } if (index3 < 0) { index3 = 0; } } } connectome-workbench-1.2.3+git41-gc4c6c90/src/FilesBase/VolumeMappableInterface.h000066400000000000000000000366211300200146000273140ustar00rootroot00000000000000#ifndef __VOLUME_MAPPABLE_INTERFACE_H__ #define __VOLUME_MAPPABLE_INTERFACE_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 "DisplayGroupEnum.h" #include "VolumeSliceViewPlaneEnum.h" #include "VolumeSpace.h" namespace caret { class BoundingBox; class PaletteFile; /** * \class caret::VolumeMappableInterface * \brief Interface for data that is mapped to volumes * \ingroup FilesBase * * Defines an interface for data files that are drawn with voxel data. */ class VolumeMappableInterface { protected: VolumeMappableInterface() { } virtual ~VolumeMappableInterface() { } private: VolumeMappableInterface(const VolumeMappableInterface&); VolumeMappableInterface& operator=(const VolumeMappableInterface&); public: /** * Get the dimensions of the volume. * * @param dimOut1 * First dimension (i) out. * @param dimOut2 * Second dimension (j) out. * @param dimOut3 * Third dimension (k) out. * @param dimTimeOut * Time dimensions out (number of maps) * @param numComponents * Number of components per voxel. */ virtual void getDimensions(int64_t& dimOut1, int64_t& dimOut2, int64_t& dimOut3, int64_t& dimTimeOut, int64_t& numComponents) const = 0; /** * Get the dimensions of the volume. * * @param dimsOut * Will contain 5 elements: (0) X-dimension, (1) Y-dimension * (2) Z-dimension, (3) time, (4) components. */ virtual void getDimensions(std::vector& dimsOut) const = 0; bool matchesDimensions(const int64_t dim1, const int64_t dim2, const int64_t dim3) const; void limitIndicesToValidIndices(int64_t& index1, int64_t& index2, int64_t& index3) const; /** * @return The number of componenents per voxel. */ virtual const int64_t& getNumberOfComponents() const = 0; /** * Get the value of the voxel containing the given coordinate. * * @param coordinateIn * The 3D coordinate * @param validOut * If not NULL, will indicate if the coordinate (and hence the * returned value) is valid. * @param mapIndex * Index of map. * @param component * Voxel component. * @return * Value of voxel containing the given coordinate. */ virtual float getVoxelValue(const float* coordinateIn, bool* validOut = NULL, const int64_t mapIndex = 0, const int64_t component = 0) const = 0; /** * Get the value of the voxel containing the given coordinate. * * @param coordinateX * The X coordinate * @param coordinateY * The Y coordinate * @param coordinateZ * The Z coordinate * @param validOut * If not NULL, will indicate if the coordinate (and hence the * returned value) is valid. * @param mapIndex * Index of map. * @param component * Voxel component. * @return * Value of voxel containing the given coordinate. */ 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 = 0; /** * Convert an index to space (coordinates). * * @param indexIn1 * First dimension (i). * @param indexIn2 * Second dimension (j). * @param indexIn3 * Third dimension (k). * @param coordOut1 * Output first (x) coordinate. * @param coordOut2 * Output first (y) coordinate. * @param coordOut3 * Output first (z) coordinate. */ virtual void indexToSpace(const float& indexIn1, const float& indexIn2, const float& indexIn3, float& coordOut1, float& coordOut2, float& coordOut3) const = 0; /** * Convert an index to space (coordinates). * * @param indexIn1 * First dimension (i). * @param indexIn2 * Second dimension (j). * @param indexIn3 * Third dimension (k). * @param coordOut * Output XYZ coordinates. */ virtual void indexToSpace(const float& indexIn1, const float& indexIn2, const float& indexIn3, float* coordOut) const = 0; /** * Convert an index to space (coordinates). * * @param indexIn * IJK indices * @param coordOut * Output XYZ coordinates. */ virtual void indexToSpace(const int64_t* indexIn, float* coordOut) const = 0; /** * Convert a coordinate to indices. Note that output indices * MAY NOT BE WITHING THE VALID VOXEL DIMENSIONS. * * @param coordIn1 * First (x) input coordinate. * @param coordIn2 * Second (y) input coordinate. * @param coordIn3 * Third (z) input coordinate. * @param indexOut1 * First output index (i). * @param indexOut2 * First output index (j). * @param indexOut3 * First output index (k). */ virtual void enclosingVoxel(const float& coordIn1, const float& coordIn2, const float& coordIn3, int64_t& indexOut1, int64_t& indexOut2, int64_t& indexOut3) const = 0; /** * Determine in the given voxel indices are valid (within the volume). * * @param indexIn1 * First dimension (i). * @param indexIn2 * Second dimension (j). * @param indexIn3 * Third dimension (k). * @param coordOut1 * Output first (x) coordinate. * @param brickIndex * Time/map index (default 0). * @param component * Voxel component (default 0). */ 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 = 0; /** * Get a bounding box for the voxel coordinate ranges. * * @param boundingBoxOut * The output bounding box. */ virtual void getVoxelSpaceBoundingBox(BoundingBox& boundingBoxOut) const = 0; /** * Get the voxel spacing for each of the spatial dimensions. * * @param spacingOut1 * Spacing for the first dimension (typically X). * @param spacingOut2 * Spacing for the first dimension (typically Y). * @param spacingOut3 * Spacing for the first dimension (typically Z). */ void getVoxelSpacing(float& spacingOut1, float& spacingOut2, float& spacingOut3) const; /** * Get the voxel colors for a slice in the map. * * @param paletteFile * The palette file. * @param mapIndex * Index of the map. * @param slicePlane * The slice plane. * @param sliceIndex * Index of the slice. * @param displayGroup * The selected display group. * @param tabIndex * Index of selected tab. * @param rgbaOut * Output containing the rgba values (must have been allocated * by caller to sufficient count of elements in the slice). * @return * Number of voxels with alpha greater than zero */ virtual int64_t getVoxelColorsForSliceInMap(const PaletteFile* paletteFile, const int32_t mapIndex, const VolumeSliceViewPlaneEnum::Enum slicePlane, const int64_t sliceIndex, const DisplayGroupEnum::Enum displayGroup, const int32_t tabIndex, uint8_t* rgbaOut) const = 0; /** * Get voxel coloring for a set of voxels. * * @param mapIndex * Index of map. * @param firstVoxelIJK * IJK Indices of first voxel * @param rowStepIJK * IJK Step for moving to next row. * @param columnStepIJK * IJK Step for moving to next column. * @param numberOfRows * Number of rows. * @param numberOfColumns * Number of columns. * @param displayGroup * The selected display group. * @param tabIndex * Index of selected tab. * @param rgbaOut * RGBA color components out. * @return * Number of voxels with alpha greater than zero */ virtual int64_t 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 = 0; /** * Get the voxel colors for a sub slice in the map. * * @param paletteFile * The palette file. * @param mapIndex * Index of the map. * @param slicePlane * The slice plane. * @param sliceIndex * Index of the slice. * @param firstCornerVoxelIndex * Indices of voxel for first corner of sub-slice (inclusive). * @param lastCornerVoxelIndex * Indices of voxel for last corner of sub-slice (inclusive). * @param voxelCountIJK * Voxel counts for each axis. * @param displayGroup * The selected display group. * @param tabIndex * Index of selected tab. * @param rgbaOut * Output containing the rgba values (must have been allocated * by caller to sufficient count of elements in the slice). * @return * Number of voxels with alpha greater than zero */ virtual int64_t getVoxelColorsForSubSliceInMap(const PaletteFile* paletteFile, const int32_t mapIndex, const VolumeSliceViewPlaneEnum::Enum slicePlane, const int64_t sliceIndex, const int64_t firstCornerVoxelIndex[3], const int64_t lastCornerVoxelIndex[3], const int64_t voxelCountIJK[3], const DisplayGroupEnum::Enum displayGroup, const int32_t tabIndex, uint8_t* rgbaOut) const = 0; /** * Get the voxel coloring for the voxel at the given indices. * * @param paletteFile * The palette file. * @param indexIn1 * First dimension (i). * @param indexIn2 * Second dimension (j). * @param indexIn3 * Third dimension (k). * @param brickIndex * Time/map index. * @param displayGroup * The selected display group. * @param tabIndex * Index of selected tab. * @param rgbaOut * Output containing RGBA values for voxel at the given indices. */ virtual void getVoxelColorInMap(const PaletteFile* paletteFile, const int64_t indexIn1, const int64_t indexIn2, const int64_t indexIn3, const int64_t brickIndex, const DisplayGroupEnum::Enum displayGroup, const int32_t tabIndex, uint8_t rgbaOut[4]) const = 0; /** * Get the volume space object, so we have access to all functions associated with volume spaces */ virtual const VolumeSpace& getVolumeSpace() const = 0; }; #ifdef __VOLUME_MAPPABLE_INTERFACE_DECLARE__ // #endif // __VOLUME_MAPPABLE_INTERFACE_DECLARE__ } // namespace #endif //__VOLUME_MAPPABLE_INTERFACE_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/FilesBase/VolumeSliceViewPlaneEnum.cxx000066400000000000000000000270771300200146000300310ustar00rootroot00000000000000 /*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 __VOLUME_SLICE_VIEW_AXIS_ENUM_DECLARE__ #include "VolumeSliceViewPlaneEnum.h" #undef __VOLUME_SLICE_VIEW_AXIS_ENUM_DECLARE__ #include "CaretAssert.h" using namespace caret; /** * \class VolumeSliceViewPlaneEnum * \brief * * */ /** * Constructor. * * @param enumValue * An enumerated value. * @param name * Name of enumerated value. * * @param guiName * User-friendly name for use in user-interface. * @param guiNameAbbreviation * Abbreviated name for user-interface. */ VolumeSliceViewPlaneEnum::VolumeSliceViewPlaneEnum(const Enum enumValue, const AString& name, const AString& guiName, const AString& guiNameAbbreviation) { this->enumValue = enumValue; this->integerCode = integerCodeCounter++; this->name = name; this->guiName = guiName; this->guiNameAbbreviation = guiNameAbbreviation; } /** * Destructor. */ VolumeSliceViewPlaneEnum::~VolumeSliceViewPlaneEnum() { } /** * Initialize the enumerated metadata. */ void VolumeSliceViewPlaneEnum::initialize() { if (initializedFlag) { return; } initializedFlag = true; enumData.push_back(VolumeSliceViewPlaneEnum(ALL, "ALL", "All", "All")); enumData.push_back(VolumeSliceViewPlaneEnum(AXIAL, "AXIAL", "Axial", "A")); enumData.push_back(VolumeSliceViewPlaneEnum(CORONAL, "CORONAL", "Coronal", "C")); enumData.push_back(VolumeSliceViewPlaneEnum(PARASAGITTAL, "PARASAGITTAL", "Parasagittal", "P")); } /** * 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 VolumeSliceViewPlaneEnum* VolumeSliceViewPlaneEnum::findData(const Enum enumValue) { if (initializedFlag == false) initialize(); size_t num = enumData.size(); for (size_t i = 0; i < num; i++) { const VolumeSliceViewPlaneEnum* 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 VolumeSliceViewPlaneEnum::toName(Enum enumValue) { if (initializedFlag == false) initialize(); const VolumeSliceViewPlaneEnum* 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. */ VolumeSliceViewPlaneEnum::Enum VolumeSliceViewPlaneEnum::fromName(const AString& name, bool* isValidOut) { if (initializedFlag == false) initialize(); bool validFlag = false; Enum enumValue = AXIAL; for (std::vector::iterator iter = enumData.begin(); iter != enumData.end(); iter++) { const VolumeSliceViewPlaneEnum& 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 VolumeSliceViewPlaneEnum")); } return enumValue; } /** * Get a GUI string representation of the enumerated type. * @param enumValue * Enumerated value. * @return * String representing enumerated value. */ AString VolumeSliceViewPlaneEnum::toGuiName(Enum enumValue) { if (initializedFlag == false) initialize(); const VolumeSliceViewPlaneEnum* 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. */ VolumeSliceViewPlaneEnum::Enum VolumeSliceViewPlaneEnum::fromGuiName(const AString& guiName, bool* isValidOut) { if (initializedFlag == false) initialize(); bool validFlag = false; Enum enumValue = AXIAL; for (std::vector::iterator iter = enumData.begin(); iter != enumData.end(); iter++) { const VolumeSliceViewPlaneEnum& 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 VolumeSliceViewPlaneEnum")); } return enumValue; } /** * Get a GUI abbreviated string representation of the enumerated type. * @param enumValue * Enumerated value. * @return * Short name representing enumerated value. */ AString VolumeSliceViewPlaneEnum::toGuiNameAbbreviation(Enum enumValue) { if (initializedFlag == false) initialize(); const VolumeSliceViewPlaneEnum* enumInstance = findData(enumValue); return enumInstance->guiNameAbbreviation; } /** * Get an enumerated value corresponding to its abbreviated GUI name. * @param guiNameAbbreviation * 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. */ VolumeSliceViewPlaneEnum::Enum VolumeSliceViewPlaneEnum::fromGuiNameAbbreviation(const AString& guiNameAbbreviation, bool* isValidOut) { if (initializedFlag == false) initialize(); bool validFlag = false; Enum enumValue = AXIAL; for (std::vector::iterator iter = enumData.begin(); iter != enumData.end(); iter++) { const VolumeSliceViewPlaneEnum& d = *iter; if (d.guiNameAbbreviation == guiNameAbbreviation) { enumValue = d.enumValue; validFlag = true; break; } } if (isValidOut != 0) { *isValidOut = validFlag; } else if (validFlag == false) { CaretAssertMessage(0, AString("guiNameAbbreviation " + guiNameAbbreviation + "failed to match enumerated value for type VolumeSliceViewPlaneEnum")); } return enumValue; } /** * Get the integer code for a data type. * * @return * Integer code for data type. */ int32_t VolumeSliceViewPlaneEnum::toIntegerCode(Enum enumValue) { if (initializedFlag == false) initialize(); const VolumeSliceViewPlaneEnum* 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. */ VolumeSliceViewPlaneEnum::Enum VolumeSliceViewPlaneEnum::fromIntegerCode(const int32_t integerCode, bool* isValidOut) { if (initializedFlag == false) initialize(); bool validFlag = false; Enum enumValue = AXIAL; for (std::vector::iterator iter = enumData.begin(); iter != enumData.end(); iter++) { const VolumeSliceViewPlaneEnum& 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 VolumeSliceViewPlaneEnum")); } 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 VolumeSliceViewPlaneEnum::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 VolumeSliceViewPlaneEnum::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(VolumeSliceViewPlaneEnum::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 VolumeSliceViewPlaneEnum::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(VolumeSliceViewPlaneEnum::toGuiName(iter->enumValue)); } if (isSorted) { std::sort(allGuiNames.begin(), allGuiNames.end()); } } connectome-workbench-1.2.3+git41-gc4c6c90/src/FilesBase/VolumeSliceViewPlaneEnum.h000066400000000000000000000070631300200146000274470ustar00rootroot00000000000000#ifndef __VOLUME_SLICE_VIEW_AXIS_ENUM__H_ #define __VOLUME_SLICE_VIEW_AXIS_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 VolumeSliceViewPlaneEnum { public: /** * Enumerated values. */ enum Enum { /** All Axis */ ALL, /** Axial (Horizontal) */ AXIAL, /** Coronal */ CORONAL, /** Parasagittal */ PARASAGITTAL }; ~VolumeSliceViewPlaneEnum(); static AString toName(Enum enumValue); static Enum fromName(const AString& name, bool* isValidOut); static AString toGuiName(Enum enumValue); static Enum fromGuiNameAbbreviation(const AString& guiNameAbbreviation, bool* isValidOut); static AString toGuiNameAbbreviation(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: VolumeSliceViewPlaneEnum(const Enum enumValue, const AString& name, const AString& guiName, const AString& guiNameAbbreviation); static const VolumeSliceViewPlaneEnum* 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; /** A short user-friendly name that is displayed in the GUI */ AString guiNameAbbreviation; }; #ifdef __VOLUME_SLICE_VIEW_AXIS_ENUM_DECLARE__ std::vector VolumeSliceViewPlaneEnum::enumData; bool VolumeSliceViewPlaneEnum::initializedFlag = false; int32_t VolumeSliceViewPlaneEnum::integerCodeCounter = 0; #endif // __VOLUME_SLICE_VIEW_AXIS_ENUM_DECLARE__ } // namespace #endif //__VOLUME_SLICE_VIEW_AXIS_ENUM__H_ connectome-workbench-1.2.3+git41-gc4c6c90/src/FilesBase/VolumeSpace.cxx000066400000000000000000000454151300200146000253610ustar00rootroot00000000000000/*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 "VolumeSpace.h" #include "CaretAssert.h" #include "CaretException.h" #include "CaretLogger.h" #include "FloatMatrix.h" #include #include #include #include using namespace std; using namespace caret; VolumeSpace::VolumeSpace() { m_dims[0] = 0; m_dims[1] = 0; m_dims[2] = 0; m_sform = FloatMatrix::identity(4).getMatrix(); computeInverse(); } VolumeSpace::VolumeSpace(const int64_t dims[3], const vector >& sform) { setSpace(dims, sform); } VolumeSpace::VolumeSpace(const int64_t dims[3], const float sform[12]) { setSpace(dims, sform); } void VolumeSpace::setSpace(const int64_t dims[3], const vector >& sform) { if (sform.size() < 2 || sform.size() > 4) { CaretAssert(false); throw CaretException("VolumeSpace initialized with wrong size sform"); } for (int i = 0; i < (int)sform.size(); ++i) { if (sform[i].size() != 4) { CaretAssert(false); throw CaretException("VolumeSpace initialized with wrong size sform"); } } m_dims[0] = dims[0]; m_dims[1] = dims[1]; m_dims[2] = dims[2]; m_sform = sform; m_sform.resize(4);//make sure its 4x4 m_sform[3].resize(4); m_sform[3][0] = 0.0f;//force the fourth row to be correct m_sform[3][1] = 0.0f; m_sform[3][2] = 0.0f; m_sform[3][3] = 1.0f; computeInverse(); } void VolumeSpace::setSpace(const int64_t dims[3], const float sform[12]) { m_sform = FloatMatrix::identity(4).getMatrix(); for (int i = 0; i < 3; ++i) { for (int j = 0; j < 4; ++j) { m_sform[i][j] = sform[i * 4 + j]; } } m_dims[0] = dims[0]; m_dims[1] = dims[1]; m_dims[2] = dims[2]; computeInverse(); } void VolumeSpace::computeInverse() { m_inverse = FloatMatrix(m_sform).inverse().getMatrix(); } void VolumeSpace::spaceToIndex(const float& coordIn1, const float& coordIn2, const float& coordIn3, float& indexOut1, float& indexOut2, float& indexOut3) const { indexOut1 = coordIn1 * m_inverse[0][0] + coordIn2 * m_inverse[0][1] + coordIn3 * m_inverse[0][2] + m_inverse[0][3]; indexOut2 = coordIn1 * m_inverse[1][0] + coordIn2 * m_inverse[1][1] + coordIn3 * m_inverse[1][2] + m_inverse[1][3]; indexOut3 = coordIn1 * m_inverse[2][0] + coordIn2 * m_inverse[2][1] + coordIn3 * m_inverse[2][2] + m_inverse[2][3]; } void VolumeSpace::enclosingVoxel(const float& coordIn1, const float& coordIn2, const float& coordIn3, int64_t& indexOut1, int64_t& indexOut2, int64_t& indexOut3) const { float tempInd1, tempInd2, tempInd3; spaceToIndex(coordIn1, coordIn2, coordIn3, tempInd1, tempInd2, tempInd3); indexOut1 = (int64_t)floor(0.5f + tempInd1); indexOut2 = (int64_t)floor(0.5f + tempInd2); indexOut3 = (int64_t)floor(0.5f + tempInd3); } bool VolumeSpace::matches(const VolumeSpace& right) const { for (int i = 0; i < 3; ++i) { if (m_dims[i] != right.m_dims[i]) { return false; } } const float TOLER_RATIO = 0.999f;//ratio a spacing element can mismatch by for (int i = 0; i < 3; ++i) { for (int j = 0; j < 4; ++j) { float leftelem = m_sform[i][j]; float rightelem = right.m_sform[i][j]; if ((leftelem != rightelem) && (leftelem == 0.0f || rightelem == 0.0f || (leftelem / rightelem < TOLER_RATIO || rightelem / leftelem < TOLER_RATIO))) { return false; } } } return true; } bool VolumeSpace::operator==(const VolumeSpace& right) const { for (int i = 0; i < 3; ++i) { if (m_dims[i] != right.m_dims[i]) { return false; } } for (int i = 0; i < 3; ++i) { for (int j = 0; j < 4; ++j) { if (m_sform[i][j] != right.m_sform[i][j]) { return false; } } } return true; } void VolumeSpace::getSpacingVectors(Vector3D& iStep, Vector3D& jStep, Vector3D& kStep, Vector3D& origin) const { FloatMatrix(m_sform).getAffineVectors(iStep, jStep, kStep, origin); } float VolumeSpace::getVoxelVolume() const { Vector3D spacingVecs[4]; getSpacingVectors(spacingVecs[0], spacingVecs[1], spacingVecs[2], spacingVecs[3]); return abs(spacingVecs[0].dot(spacingVecs[1].cross(spacingVecs[2]))); } void VolumeSpace::getOrientAndSpacingForPlumb(OrientTypes* orientOut, float* spacingOut, float* originOut) const { CaretAssert(isPlumb()); if (!isPlumb()) { throw CaretException("orientation and spacing asked for on non-plumb volume space");//this will fail MISERABLY on non-plumb volumes, so throw otherwise } for (int i = 0; i < 3; ++i) { for (int j = 0; j < 3; ++j) { if (m_sform[i][j] != 0.0f) { spacingOut[j] = m_sform[i][j]; originOut[j] = m_sform[i][3]; bool negative = (m_sform[i][j] < 0.0f); switch (i) { case 0: //left/right orientOut[j] = (negative ? RIGHT_TO_LEFT : LEFT_TO_RIGHT); break; case 1: //forward/back orientOut[j] = (negative ? ANTERIOR_TO_POSTERIOR : POSTERIOR_TO_ANTERIOR); break; case 2: //up/down orientOut[j] = (negative ? SUPERIOR_TO_INFERIOR : INFERIOR_TO_SUPERIOR); break; default: //will never get called break; }; } } } } void VolumeSpace::getOrientation(OrientTypes orientOut[3]) const { Vector3D ivec, jvec, kvec, origin; getSpacingVectors(ivec, jvec, kvec, origin); int next = 1, bestarray[3] = {0, 0, 0}; float bestVal = -1.0f;//make sure at least the first test trips true, if there is a zero spacing vector it will default to report LPI for (int first = 0; first < 3; ++first)//brute force search for best fit - only 6 to try { int third = 3 - first - next; float testVal = abs(ivec[first] * jvec[next] * kvec[third]); if (testVal > bestVal) { bestVal = testVal; bestarray[0] = first; bestarray[1] = next; } testVal = abs(ivec[first] * jvec[third] * kvec[next]); if (testVal > bestVal) { bestVal = testVal; bestarray[0] = first; bestarray[1] = third; } next = 0; } bestarray[2] = 3 - bestarray[0] - bestarray[1]; Vector3D spaceHats[3];//to translate into enums without casting spaceHats[0] = ivec; spaceHats[1] = jvec; spaceHats[2] = kvec; for (int i = 0; i < 3; ++i) { bool neg = (spaceHats[i][bestarray[i]] < 0.0f); switch (bestarray[i]) { case 0: if (neg) { orientOut[i] = RIGHT_TO_LEFT; } else { orientOut[i] = LEFT_TO_RIGHT; } break; case 1: if (neg) { orientOut[i] = ANTERIOR_TO_POSTERIOR; } else { orientOut[i] = POSTERIOR_TO_ANTERIOR; } break; case 2: if (neg) { orientOut[i] = SUPERIOR_TO_INFERIOR; } else { orientOut[i] = INFERIOR_TO_SUPERIOR; } break; default: CaretAssert(0); } } } bool VolumeSpace::isPlumb() const { char axisUsed = 0; char indexUsed = 0; for (int i = 0; i < 3; ++i) { for (int j = 0; j < 3; ++j) { if (m_sform[i][j] != 0.0f) { if (axisUsed & (1< #include #include "stdint.h" #include namespace caret { class VolumeSpace { int64_t m_dims[3]; std::vector > m_sform, m_inverse; void computeInverse(); public: enum OrientTypes { LEFT_TO_RIGHT = 0, RIGHT_TO_LEFT = 4, POSTERIOR_TO_ANTERIOR = 1, ANTERIOR_TO_POSTERIOR = 5, INFERIOR_TO_SUPERIOR = 2, SUPERIOR_TO_INFERIOR = 6 }; VolumeSpace(); VolumeSpace(const int64_t dims[3], const std::vector >& sform); VolumeSpace(const int64_t dims[3], const float sform[12]); void setSpace(const int64_t dims[3], const std::vector >& sform); void setSpace(const int64_t dims[3], const float sform[12]); const int64_t* getDims() const { return m_dims; } const std::vector >& getSform() const { return m_sform; } void getSpacingVectors(Vector3D& iStep, Vector3D& jStep, Vector3D& kStep, Vector3D& origin) const; float getVoxelVolume() const; bool matches(const VolumeSpace& right) const;//allows slight mismatches bool operator==(const VolumeSpace& right) const;//requires that it be exact bool operator!=(const VolumeSpace& right) const { return !(*this == right); } ///returns true if volume space is not skew, and each axis and index is separate bool isPlumb() const; ///returns orientation, spacing, and center (spacing/center can be negative, spacing/center is LPI rearranged to ijk (first dimension uses first element), will assert false if isOblique is true) void getOrientAndSpacingForPlumb(OrientTypes* orientOut, float* spacingOut, float* originOut) const; ///get just orientation, even for non-plumb volumes void getOrientation(OrientTypes orientOut[3]) const; ///returns coordinate triplet of an index triplet template inline void indexToSpace(const T* indexIn, float* coordOut) const { indexToSpace(indexIn[0], indexIn[1], indexIn[2], coordOut[0], coordOut[1], coordOut[2]); } ///returns coordinate triplet of three indices template inline void indexToSpace(const T& indexIn1, const T& indexIn2, const T& indexIn3, float* coordOut) const { indexToSpace(indexIn1, indexIn2, indexIn3, coordOut[0], coordOut[1], coordOut[2]); } ///returns three coordinates of an index triplet template inline void indexToSpace(const T* indexIn, float& coordOut1, float& coordOut2, float& coordOut3) const { indexToSpace(indexIn[0], indexIn[1], indexIn[2], coordOut1, coordOut2, coordOut3); } ///returns three coordinates of three indices template void indexToSpace(const T& indexIn1, const T& indexIn2, const T& indexIn3, float& coordOut1, float& coordOut2, float& coordOut3) const; ///returns floating point index triplet of a given coordinate triplet inline void spaceToIndex(const float* coordIn, float* indexOut) const { spaceToIndex(coordIn[0], coordIn[1], coordIn[2], indexOut[0], indexOut[1], indexOut[2]); } ///returns floating point index triplet of three given coordinates inline void spaceToIndex(const float& coordIn1, const float& coordIn2, const float& coordIn3, float* indexOut) const { spaceToIndex(coordIn1, coordIn2, coordIn3, indexOut[0], indexOut[1], indexOut[2]); } ///returns three floating point indexes of a given coordinate triplet inline void spaceToIndex(const float* coordIn, float& indexOut1, float& indexOut2, float& indexOut3) const { spaceToIndex(coordIn[0], coordIn[1], coordIn[2], indexOut1, indexOut2, indexOut3); } ///returns three floating point indexes of three given coordinates void spaceToIndex(const float& coordIn1, const float& coordIn2, const float& coordIn3, float& indexOut1, float& indexOut2, float& indexOut3) const; ///returns integer index triplet of voxel whose center is closest to the coordinate triplet inline void enclosingVoxel(const float* coordIn, int64_t* indexOut) const { enclosingVoxel(coordIn[0], coordIn[1], coordIn[2], indexOut[0], indexOut[1], indexOut[2]); } ///returns integer index triplet of voxel whose center is closest to the three coordinates inline void enclosingVoxel(const float& coordIn1, const float& coordIn2, const float& coordIn3, int64_t* indexOut) const { enclosingVoxel(coordIn1, coordIn2, coordIn3, indexOut[0], indexOut[1], indexOut[2]); } ///returns integer indexes of voxel whose center is closest to the coordinate triplet inline void enclosingVoxel(const float* coordIn, int64_t& indexOut1, int64_t& indexOut2, int64_t& indexOut3) const { enclosingVoxel(coordIn[0], coordIn[1], coordIn[2], indexOut1, indexOut2, indexOut3); } ///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; template inline bool indexValid(const T* indexIn) const { return indexValid(indexIn[0], indexIn[1], indexIn[2]);//implicit cast to int64_t } ///checks if an index is within array dimensions inline bool indexValid(const int64_t& indexIn1, const int64_t& indexIn2, const int64_t& indexIn3) const { if (indexIn1 < 0 || indexIn1 >= m_dims[0]) return false; if (indexIn2 < 0 || indexIn2 >= m_dims[1]) return false; if (indexIn3 < 0 || indexIn3 >= m_dims[2]) return false; return true; } inline int64_t getIndex(const int64_t& indexIn1, const int64_t& indexIn2, const int64_t& indexIn3) const { CaretAssert(indexValid(indexIn1, indexIn2, indexIn3)); return indexIn1 + m_dims[0] * (indexIn2 + m_dims[1] * indexIn3); } template inline int64_t getIndex(const T* indexIn) const { return getIndex(indexIn[0], indexIn[1], indexIn[2]);//implicit cast to int64_t } void readCiftiXML1(QXmlStreamReader& xml);//xml functions void readCiftiXML2(QXmlStreamReader& xml); void writeCiftiXML1(QXmlStreamWriter& xml) const; void writeCiftiXML2(QXmlStreamWriter& xml) const; }; template void VolumeSpace::indexToSpace(const T& indexIn1, const T& indexIn2, const T& indexIn3, float& coordOut1, float& coordOut2, float& coordOut3) const { coordOut1 = indexIn1 * m_sform[0][0] + indexIn2 * m_sform[0][1] + indexIn3 * m_sform[0][2] + m_sform[0][3]; coordOut2 = indexIn1 * m_sform[1][0] + indexIn2 * m_sform[1][1] + indexIn3 * m_sform[1][2] + m_sform[1][3]; coordOut3 = indexIn1 * m_sform[2][0] + indexIn2 * m_sform[2][1] + indexIn3 * m_sform[2][2] + m_sform[2][3]; } } #endif //__VOLUME_SPACE_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/FilesBase/nifti1.h000077500000000000000000002054021300200146000237520ustar00rootroot00000000000000#ifndef _NIFTI_HEADER_ #define _NIFTI_HEADER_ /*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*/ /* * NOTE: our version is slightly modified, mainly turning #define into const int32_t */ /***************************************************************************** ** This file defines the "NIFTI-1" header format. ** ** It is derived from 2 meetings at the NIH (31 Mar 2003 and ** ** 02 Sep 2003) of the Data Format Working Group (DFWG), ** ** chartered by the NIfTI (Neuroimaging Informatics Technology ** ** Initiative) at the National Institutes of Health (NIH). ** **--------------------------------------------------------------** ** Neither the National Institutes of Health (NIH), the DFWG, ** ** nor any of the members or employees of these institutions ** ** imply any warranty of usefulness of this material for any ** ** purpose, and do not assume any liability for damages, ** ** incidental or otherwise, caused by any use of this document. ** ** If these conditions are not acceptable, do not use this! ** **--------------------------------------------------------------** ** Author: Robert W Cox (NIMH, Bethesda) ** ** Advisors: John Ashburner (FIL, London), ** ** Stephen Smith (FMRIB, Oxford), ** ** Mark Jenkinson (FMRIB, Oxford) ** ******************************************************************************/ /*---------------------------------------------------------------------------*/ /* Note that the ANALYZE 7.5 file header (dbh.h) is (c) Copyright 1986-1995 Biomedical Imaging Resource Mayo Foundation Incorporation of components of dbh.h are by permission of the Mayo Foundation. Changes from the ANALYZE 7.5 file header in this file are released to the public domain, including the functional comments and any amusing asides. -----------------------------------------------------------------------------*/ /*---------------------------------------------------------------------------*/ /*! INTRODUCTION TO NIFTI-1: ------------------------ The twin (and somewhat conflicting) goals of this modified ANALYZE 7.5 format are: (a) To add information to the header that will be useful for functional neuroimaging data analysis and display. These additions include: - More basic data types. - Two affine transformations to specify voxel coordinates. - "Intent" codes and parameters to describe the meaning of the data. - Affine scaling of the stored data values to their "true" values. - Optional storage of the header and image data in one file (.nii). (b) To maintain compatibility with non-NIFTI-aware ANALYZE 7.5 compatible software (i.e., such a program should be able to do something useful with a NIFTI-1 dataset -- at least, with one stored in a traditional .img/.hdr file pair). Most of the unused fields in the ANALYZE 7.5 header have been taken, and some of the lesser-used fields have been co-opted for other purposes. Notably, most of the data_history substructure has been co-opted for other purposes, since the ANALYZE 7.5 format describes this substructure as "not required". NIFTI-1 FLAG (MAGIC STRINGS): ---------------------------- To flag such a struct as being conformant to the NIFTI-1 spec, the last 4 bytes of the header must be either the C String "ni1" or "n+1"; in hexadecimal, the 4 bytes 6E 69 31 00 or 6E 2B 31 00 (in any future version of this format, the '1' will be upgraded to '2', etc.). Normally, such a "magic number" or flag goes at the start of the file, but trying to avoid clobbering widely-used ANALYZE 7.5 fields led to putting this marker last. However, recall that "the last shall be first" (Matthew 20:16). If a NIFTI-aware program reads a header file that is NOT marked with a NIFTI magic string, then it should treat the header as an ANALYZE 7.5 structure. NIFTI-1 FILE STORAGE: -------------------- "ni1" means that the image data is stored in the ".img" file corresponding to the header file (starting at file offset 0). "n+1" means that the image data is stored in the same file as the header information. We recommend that the combined header+data filename suffix be ".nii". When the dataset is stored in one file, the first byte of image data is stored at byte location (int)vox_offset in this combined file. The minimum allowed value of vox_offset is 352; for compatibility with some software, vox_offset should be an integral multiple of 16. GRACE UNDER FIRE: ---------------- Most NIFTI-aware programs will only be able to handle a subset of the full range of datasets possible with this format. All NIFTI-aware programs should take care to check if an input dataset conforms to the program's needs and expectations (e.g., check datatype, intent_code, etc.). If the input dataset can't be handled by the program, the program should fail gracefully (e.g., print a useful warning; not crash). SAMPLE CODES: ------------ The associated files nifti1_io.h and nifti1_io.c provide a sample implementation in C of a set of functions to read, write, and manipulate NIFTI-1 files. The file nifti1_test.c is a sample program that uses the nifti1_io.c functions. -----------------------------------------------------------------------------*/ /*---------------------------------------------------------------------------*/ /* HEADER STRUCT DECLARATION: ------------------------- In the comments below for each field, only NIFTI-1 specific requirements or changes from the ANALYZE 7.5 format are described. For convenience, the 348 byte header is described as a single struct, rather than as the ANALYZE 7.5 group of 3 substructs. Further comments about the interpretation of various elements of this header are after the data type definition itself. Fields that are marked as ++UNUSED++ have no particular interpretation in this standard. (Also see the UNUSED FIELDS comment section, far below.) The presumption below is that the various C types have particular sizes: sizeof(int) = sizeof(float) = 4 ; sizeof(short) = 2 -----------------------------------------------------------------------------*/ /*=================*/ #ifdef __cplusplus extern "C" { #endif /*=================*/ //hopefully cross-platform solution to byte padding added by some compilers #pragma pack(push) #pragma pack(1) /*! \struct nifti_1_header \brief Data structure defining the fields in the nifti1 header. This binary header should be found at the beginning of a valid NIFTI-1 header file. */ /*************************/ /************************/ struct nifti_1_header { /* NIFTI-1 usage */ /* ANALYZE 7.5 field(s) */ /*************************/ /************************/ /*--- was header_key substruct ---*/ int sizeof_hdr; /*!< MUST be 348 */ /* int sizeof_hdr; */ /* 0 */ char data_type[10]; /*!< ++UNUSED++ */ /* char data_type[10]; */ /* 4 */ char db_name[18]; /*!< ++UNUSED++ */ /* char db_name[18]; */ /* 14 */ int extents; /*!< ++UNUSED++ */ /* int extents; */ /* 32 */ short session_error; /*!< ++UNUSED++ */ /* short session_error; */ /* 36 */ char regular; /*!< ++UNUSED++ */ /* char regular; */ /* 38 */ char dim_info; /*!< MRI slice ordering. */ /* char hkey_un0; */ /* 39 */ /*--- was image_dimension substruct ---*/ short dim[8]; /*!< Data array dimensions.*/ /* short dim[8]; */ /* 40 */ float intent_p1 ; /*!< 1st intent parameter. */ /* short unused8; */ /* 56 */ /* short unused9; */ float intent_p2 ; /*!< 2nd intent parameter. */ /* short unused10; */ /* 60 */ /* short unused11; */ float intent_p3 ; /*!< 3rd intent parameter. */ /* short unused12; */ /* 64 */ /* short unused13; */ short intent_code ; /*!< NIFTI_INTENT_* code. */ /* short unused14; */ /* 68 */ short datatype; /*!< Defines data type! */ /* short datatype; */ /* 70 */ short bitpix; /*!< Number bits/voxel. */ /* short bitpix; */ /* 72 */ short slice_start; /*!< First slice index. */ /* short dim_un0; */ /* 74 */ float pixdim[8]; /*!< Grid spacings. */ /* float pixdim[8]; */ /* 76 */ float vox_offset; /*!< Offset into .nii file */ /* float vox_offset; */ /* 108 */ float scl_slope ; /*!< Data scaling: slope. */ /* float funused1; */ /* 112 */ float scl_inter ; /*!< Data scaling: offset. */ /* float funused2; */ /* 116 */ short slice_end; /*!< Last slice index. */ /* float funused3; */ /* 120 */ char slice_code ; /*!< Slice timing order. */ /* 122 */ char xyzt_units ; /*!< Units of pixdim[1..4] */ /* 123 */ float cal_max; /*!< Max display intensity */ /* float cal_max; */ /* 124 */ float cal_min; /*!< Min display intensity */ /* float cal_min; */ /* 128 */ float slice_duration;/*!< Time for 1 slice. */ /* float compressed; */ /* 132 */ float toffset; /*!< Time axis shift. */ /* float verified; */ /* 136 */ int glmax; /*!< ++UNUSED++ */ /* int glmax; */ /* 140 */ int glmin; /*!< ++UNUSED++ */ /* int glmin; */ /* 144 */ /*--- was data_history substruct ---*/ char descrip[80]; /*!< any text you like. */ /* char descrip[80]; */ /* 148 */ char aux_file[24]; /*!< auxiliary filename. */ /* char aux_file[24]; */ /* 228 */ short qform_code ; /*!< NIFTI_XFORM_* code. */ /*-- all ANALYZE 7.5 ---*/ /* 252 */ short sform_code ; /*!< NIFTI_XFORM_* code. */ /* fields below here */ /* 254 */ /* are replaced */ float quatern_b ; /*!< Quaternion b param. */ /* 256 */ float quatern_c ; /*!< Quaternion c param. */ /* 260 */ float quatern_d ; /*!< Quaternion d param. */ /* 264 */ float qoffset_x ; /*!< Quaternion x shift. */ /* 268 */ float qoffset_y ; /*!< Quaternion y shift. */ /* 272 */ float qoffset_z ; /*!< Quaternion z shift. */ /* 276 */ float srow_x[4] ; /*!< 1st row affine transform. */ /* 280 */ float srow_y[4] ; /*!< 2nd row affine transform. */ /* 296 */ float srow_z[4] ; /*!< 3rd row affine transform. */ /* 312 */ char intent_name[16];/*!< 'name' or meaning of data. */ /* 328 */ char magic[4] ; /*!< MUST be "ni1\0" or "n+1\0". */ /* 344 */ } ; /**** 348 bytes total ****/ typedef struct nifti_1_header nifti_1_header ; /*---------------------------------------------------------------------------*/ /* HEADER EXTENSIONS: ----------------- After the end of the 348 byte header (e.g., after the magic field), the next 4 bytes are a char array field named "extension". By default, all 4 bytes of this array should be set to zero. In a .nii file, these 4 bytes will always be present, since the earliest start point for the image data is byte #352. In a separate .hdr file, these bytes may or may not be present. If not present (i.e., if the length of the .hdr file is 348 bytes), then a NIfTI-1 compliant program should use the default value of extension={0,0,0,0}. The first byte (extension[0]) is the only value of this array that is specified at present. The other 3 bytes are reserved for future use. If extension[0] is nonzero, it indicates that extended header information is present in the bytes following the extension array. In a .nii file, this extended header data is before the image data (and vox_offset must be set correctly to allow for this). In a .hdr file, this extended data follows extension and proceeds (potentially) to the end of the file. The format of extended header data is weakly specified. Each extension must be an integer multiple of 16 bytes long. The first 8 bytes of each extension comprise 2 integers: int esize , ecode ; These values may need to be byte-swapped, as indicated by dim[0] for the rest of the header. * esize is the number of bytes that form the extended header data + esize must be a positive integral multiple of 16 + this length includes the 8 bytes of esize and ecode themselves * ecode is a non-negative integer that indicates the format of the extended header data that follows + different ecode values are assigned to different developer groups + at present, the "registered" values for code are = 0 = unknown private format (not recommended!) = 2 = DICOM format (i.e., attribute tags and values) = 4 = AFNI group (i.e., ASCII XML-ish elements) In the interests of interoperability (a primary rationale for NIfTI), groups developing software that uses this extension mechanism are encouraged to document and publicize the format of their extensions. To this end, the NIfTI DFWG will assign even numbered codes upon request to groups submitting at least rudimentary documentation for the format of their extension; at present, the contact is mailto:rwcox@nih.gov. The assigned codes and documentation will be posted on the NIfTI website. All odd values of ecode (and 0) will remain unassigned; at least, until the even ones are used up, when we get to 2,147,483,646. Note that the other contents of the extended header data section are totally unspecified by the NIfTI-1 standard. In particular, if binary data is stored in such a section, its byte order is not necessarily the same as that given by examining dim[0]; it is incumbent on the programs dealing with such data to determine the byte order of binary extended header data. Multiple extended header sections are allowed, each starting with an esize,ecode value pair. The first esize value, as described above, is at bytes #352-355 in the .hdr or .nii file (files start at byte #0). If this value is positive, then the second (esize2) will be found starting at byte #352+esize1 , the third (esize3) at byte #352+esize1+esize2, et cetera. Of course, in a .nii file, the value of vox_offset must be compatible with these extensions. If a malformed file indicates that an extended header data section would run past vox_offset, then the entire extended header section should be ignored. In a .hdr file, if an extended header data section would run past the end-of-file, that extended header data should also be ignored. With the above scheme, a program can successively examine the esize and ecode values, and skip over each extended header section if the program doesn't know how to interpret the data within. Of course, any program can simply ignore all extended header sections simply by jumping straight to the image data using vox_offset. -----------------------------------------------------------------------------*/ /*! \struct nifti1_extender \brief This structure represents a 4-byte string that should follow the binary nifti_1_header data in a NIFTI-1 header file. If the char values are {1,0,0,0}, the file is expected to contain extensions, values of {0,0,0,0} imply the file does not contain extensions. Other sequences of values are not currently defined. */ struct nifti1_extender { char extension[4] ; } ; typedef struct nifti1_extender nifti1_extender ; /*! \struct nifti1_extension \brief Data structure defining the fields of a header extension. */ struct nifti1_extension { int esize ; /*!< size of extension, in bytes (must be multiple of 16) */ int ecode ; /*!< extension code, one of the NIFTI_ECODE_ values */ char * edata ; /*!< raw data, with no byte swapping */ } ; typedef struct nifti1_extension nifti1_extension ; //and restore packing behavior #pragma pack(pop) /*---------------------------------------------------------------------------*/ /* DATA DIMENSIONALITY (as in ANALYZE 7.5): --------------------------------------- dim[0] = number of dimensions; - if dim[0] is outside range 1..7, then the header information needs to be byte swapped appropriately - ANALYZE supports dim[0] up to 7, but NIFTI-1 reserves dimensions 1,2,3 for space (x,y,z), 4 for time (t), and 5,6,7 for anything else needed. dim[i] = length of dimension #i, for i=1..dim[0] (must be positive) - also see the discussion of intent_code, far below pixdim[i] = voxel width along dimension #i, i=1..dim[0] (positive) - cf. ORIENTATION section below for use of pixdim[0] - the units of pixdim can be specified with the xyzt_units field (also described far below). Number of bits per voxel value is in bitpix, which MUST correspond with the datatype field. The total number of bytes in the image data is dim[1] * ... * dim[dim[0]] * bitpix / 8 In NIFTI-1 files, dimensions 1,2,3 are for space, dimension 4 is for time, and dimension 5 is for storing multiple values at each spatiotemporal voxel. Some examples: - A typical whole-brain FMRI experiment's time series: - dim[0] = 4 - dim[1] = 64 pixdim[1] = 3.75 xyzt_units = NIFTI_UNITS_MM - dim[2] = 64 pixdim[2] = 3.75 | NIFTI_UNITS_SEC - dim[3] = 20 pixdim[3] = 5.0 - dim[4] = 120 pixdim[4] = 2.0 - A typical T1-weighted anatomical volume: - dim[0] = 3 - dim[1] = 256 pixdim[1] = 1.0 xyzt_units = NIFTI_UNITS_MM - dim[2] = 256 pixdim[2] = 1.0 - dim[3] = 128 pixdim[3] = 1.1 - A single slice EPI time series: - dim[0] = 4 - dim[1] = 64 pixdim[1] = 3.75 xyzt_units = NIFTI_UNITS_MM - dim[2] = 64 pixdim[2] = 3.75 | NIFTI_UNITS_SEC - dim[3] = 1 pixdim[3] = 5.0 - dim[4] = 1200 pixdim[4] = 0.2 - A 3-vector stored at each point in a 3D volume: - dim[0] = 5 - dim[1] = 256 pixdim[1] = 1.0 xyzt_units = NIFTI_UNITS_MM - dim[2] = 256 pixdim[2] = 1.0 - dim[3] = 128 pixdim[3] = 1.1 - dim[4] = 1 pixdim[4] = 0.0 - dim[5] = 3 intent_code = NIFTI_INTENT_VECTOR - A single time series with a 3x3 matrix at each point: - dim[0] = 5 - dim[1] = 1 xyzt_units = NIFTI_UNITS_SEC - dim[2] = 1 - dim[3] = 1 - dim[4] = 1200 pixdim[4] = 0.2 - dim[5] = 9 intent_code = NIFTI_INTENT_GENMATRIX - intent_p1 = intent_p2 = 3.0 (indicates matrix dimensions) -----------------------------------------------------------------------------*/ /*---------------------------------------------------------------------------*/ /* DATA STORAGE: ------------ If the magic field is "n+1", then the voxel data is stored in the same file as the header. In this case, the voxel data starts at offset (int)vox_offset into the header file. Thus, vox_offset=352.0 means that the data starts immediately after the NIFTI-1 header. If vox_offset is greater than 352, the NIFTI-1 format does not say much about the contents of the dataset file between the end of the header and the start of the data. FILES: ----- If the magic field is "ni1", then the voxel data is stored in the associated ".img" file, starting at offset 0 (i.e., vox_offset is not used in this case, and should be set to 0.0). When storing NIFTI-1 datasets in pairs of files, it is customary to name the files in the pattern "name.hdr" and "name.img", as in ANALYZE 7.5. When storing in a single file ("n+1"), the file name should be in the form "name.nii" (the ".nft" and ".nif" suffixes are already taken; cf. http://www.icdatamaster.com/n.html ). BYTE ORDERING: ------------- The byte order of the data arrays is presumed to be the same as the byte order of the header (which is determined by examining dim[0]). Floating point types are presumed to be stored in IEEE-754 format. -----------------------------------------------------------------------------*/ /*---------------------------------------------------------------------------*/ /* DETAILS ABOUT vox_offset: ------------------------ In a .nii file, the vox_offset field value is interpreted as the start location of the image data bytes in that file. In a .hdr/.img file pair, the vox_offset field value is the start location of the image data bytes in the .img file. * If vox_offset is less than 352 in a .nii file, it is equivalent to 352 (i.e., image data never starts before byte #352 in a .nii file). * The default value for vox_offset in a .nii file is 352. * In a .hdr file, the default value for vox_offset is 0. * vox_offset should be an integer multiple of 16; otherwise, some programs may not work properly (e.g., SPM). This is to allow memory-mapped input to be properly byte-aligned. Note that since vox_offset is an IEEE-754 32 bit float (for compatibility with the ANALYZE-7.5 format), it effectively has a 24 bit mantissa. All integers from 0 to 2^24 can be represented exactly in this format, but not all larger integers are exactly storable as IEEE-754 32 bit floats. However, unless you plan to have vox_offset be potentially larger than 16 MB, this should not be an issue. (Actually, any integral multiple of 16 up to 2^27 can be represented exactly in this format, which allows for up to 128 MB of random information before the image data. If that isn't enough, then perhaps this format isn't right for you.) In a .img file (i.e., image data stored separately from the NIfTI-1 header), data bytes between #0 and #vox_offset-1 (inclusive) are completely undefined and unregulated by the NIfTI-1 standard. One potential use of having vox_offset > 0 in the .hdr/.img file pair storage method is to make the .img file be a copy of (or link to) a pre-existing image file in some other format, such as DICOM; then vox_offset would be set to the offset of the image data in this file. (It may not be possible to follow the "multiple-of-16 rule" with an arbitrary external file; using the NIfTI-1 format in such a case may lead to a file that is incompatible with software that relies on vox_offset being a multiple of 16.) In a .nii file, data bytes between #348 and #vox_offset-1 (inclusive) may be used to store user-defined extra information; similarly, in a .hdr file, any data bytes after byte #347 are available for user-defined extra information. The (very weak) regulation of this extra header data is described elsewhere. -----------------------------------------------------------------------------*/ /*---------------------------------------------------------------------------*/ /* DATA SCALING: ------------ If the scl_slope field is nonzero, then each voxel value in the dataset should be scaled as y = scl_slope * x + scl_inter where x = voxel value stored y = "true" voxel value Normally, we would expect this scaling to be used to store "true" floating values in a smaller integer datatype, but that is not required. That is, it is legal to use scaling even if the datatype is a float type (crazy, perhaps, but legal). - However, the scaling is to be ignored if datatype is DT_RGB24. - If datatype is a complex type, then the scaling is to be applied to both the real and imaginary parts. The cal_min and cal_max fields (if nonzero) are used for mapping (possibly scaled) dataset values to display colors: - Minimum display intensity (black) corresponds to dataset value cal_min. - Maximum display intensity (white) corresponds to dataset value cal_max. - Dataset values below cal_min should display as black also, and values above cal_max as white. - Colors "black" and "white", of course, may refer to any scalar display scheme (e.g., a color lookup table specified via aux_file). - cal_min and cal_max only make sense when applied to scalar-valued datasets (i.e., dim[0] < 5 or dim[5] = 1). -----------------------------------------------------------------------------*/ /*---------------------------------------------------------------------------*/ /* TYPE OF DATA (acceptable values for datatype field): --------------------------------------------------- Values of datatype smaller than 256 are ANALYZE 7.5 compatible. Larger values are NIFTI-1 additions. These are all multiples of 256, so that no bits below position 8 are set in datatype. But there is no need to use only powers-of-2, as the original ANALYZE 7.5 datatype codes do. The additional codes are intended to include a complete list of basic scalar types, including signed and unsigned integers from 8 to 64 bits, floats from 32 to 128 bits, and complex (float pairs) from 64 to 256 bits. Note that most programs will support only a few of these datatypes! A NIFTI-1 program should fail gracefully (e.g., print a warning message) when it encounters a dataset with a type it doesn't like. -----------------------------------------------------------------------------*/ #undef DT_UNKNOWN /* defined in dirent.h on some Unix systems */ #include /*! \defgroup NIFTI1_DATATYPES \brief nifti1 datatype codes @{ */ /*--- the original ANALYZE 7.5 type codes ---*/ const int32_t DT_NONE =0; const int32_t DT_UNKNOWN =0; /* what it says, dude */ const int32_t DT_BINARY =1; /* binary (1 bit/voxel) */ const int32_t DT_UNSIGNED_CHAR =2; /* unsigned char (8 bits/voxel) */ const int32_t DT_SIGNED_SHORT =4; /* signed short (16 bits/voxel) */ const int32_t DT_SIGNED_INT =8; /* signed int (32 bits/voxel) */ const int32_t DT_FLOAT =16; /* float (32 bits/voxel) */ const int32_t DT_COMPLEX =32; /* complex (64 bits/voxel) */ const int32_t DT_DOUBLE =64; /* double (64 bits/voxel) */ const int32_t DT_RGB =128; /* RGB triple (24 bits/voxel) */ const int32_t DT_ALL =255; /* not very useful (?) */ /*----- another set of names for the same ---*/ const int32_t DT_UINT8 =2; const int32_t DT_INT16 =4; const int32_t DT_INT32 =8; const int32_t DT_FLOAT32 =16; const int32_t DT_COMPLEX64 =32; const int32_t DT_FLOAT64 =64; const int32_t DT_RGB24 =128; /*------------------- new codes for NIFTI ---*/ const int32_t DT_INT8 =256; /* signed char (8 bits) */ const int32_t DT_UINT16 =512; /* unsigned short (16 bits) */ const int32_t DT_UINT32 =768; /* unsigned int (32 bits) */ const int32_t DT_INT64 =1024; /* long long (64 bits) */ const int32_t DT_UINT64 =1280; /* unsigned long long (64 bits) */ const int32_t DT_FLOAT128 =1536; /* long double (128 bits) */ const int32_t DT_COMPLEX128 =1792; /* double pair (128 bits) */ const int32_t DT_COMPLEX256 =2048; /* long double pair (256 bits) */ /* @} */ /*------- aliases for all the above codes ---*/ /*! \defgroup NIFTI1_DATATYPE_ALIASES \brief aliases for the nifti1 datatype codes @{ */ /*! unsigned char. */ const int32_t NIFTI_TYPE_UINT8 =2; /*! signed short. */ const int32_t NIFTI_TYPE_INT16 =4; /*! signed int. */ const int32_t NIFTI_TYPE_INT32 =8; /*! 32 bit float. */ const int32_t NIFTI_TYPE_FLOAT32 =16; /*! 64 bit complex = 2 32 bit floats. */ const int32_t NIFTI_TYPE_COMPLEX64 =32; /*! 64 bit float = double. */ const int32_t NIFTI_TYPE_FLOAT64 =64; /*! 3 8 bit bytes. */ const int32_t NIFTI_TYPE_RGB24 =128; /*! signed char. */ const int32_t NIFTI_TYPE_INT8 =256; /*! unsigned short. */ const int32_t NIFTI_TYPE_UINT16 =512; /*! unsigned int. */ const int32_t NIFTI_TYPE_UINT32 =768; /*! signed long long. */ const int32_t NIFTI_TYPE_INT64 =1024; /*! unsigned long long. */ const int32_t NIFTI_TYPE_UINT64 =1280; /*! 128 bit float = long double. */ const int32_t NIFTI_TYPE_FLOAT128 =1536; /*! 128 bit complex = 2 64 bit floats. */ const int32_t NIFTI_TYPE_COMPLEX128 =1792; /*! 256 bit complex = 2 128 bit floats */ const int32_t NIFTI_TYPE_COMPLEX256 =2048; /* @} */ /*-------- sample typedefs for complicated types ---*/ #if 0 typedef struct { float r,i; } complex_float ; typedef struct { double r,i; } complex_double ; typedef struct { long double r,i; } complex_longdouble ; typedef struct { unsigned char r,g,b; } rgb_byte ; #endif /*---------------------------------------------------------------------------*/ /* INTERPRETATION OF VOXEL DATA: ---------------------------- The intent_code field can be used to indicate that the voxel data has some particular meaning. In particular, a large number of codes is given to indicate that the the voxel data should be interpreted as being drawn from a given probability distribution. VECTOR-VALUED DATASETS: ---------------------- The 5th dimension of the dataset, if present (i.e., dim[0]=5 and dim[5] > 1), contains multiple values (e.g., a vector) to be stored at each spatiotemporal location. For example, the header values - dim[0] = 5 - dim[1] = 64 - dim[2] = 64 - dim[3] = 20 - dim[4] = 1 (indicates no time axis) - dim[5] = 3 - datatype = DT_FLOAT - intent_code = NIFTI_INTENT_VECTOR mean that this dataset should be interpreted as a 3D volume (64x64x20), with a 3-vector of floats defined at each point in the 3D grid. A program reading a dataset with a 5th dimension may want to reformat the image data to store each voxels' set of values together in a struct or array. This programming detail, however, is beyond the scope of the NIFTI-1 file specification! Uses of dimensions 6 and 7 are also not specified here. STATISTICAL PARAMETRIC DATASETS (i.e., SPMs): -------------------------------------------- Values of intent_code from NIFTI_FIRST_STATCODE to NIFTI_LAST_STATCODE (inclusive) indicate that the numbers in the dataset should be interpreted as being drawn from a given distribution. Most such distributions have auxiliary parameters (e.g., NIFTI_INTENT_TTEST has 1 DOF parameter). If the dataset DOES NOT have a 5th dimension, then the auxiliary parameters are the same for each voxel, and are given in header fields intent_p1, intent_p2, and intent_p3. If the dataset DOES have a 5th dimension, then the auxiliary parameters are different for each voxel. For example, the header values - dim[0] = 5 - dim[1] = 128 - dim[2] = 128 - dim[3] = 1 (indicates a single slice) - dim[4] = 1 (indicates no time axis) - dim[5] = 2 - datatype = DT_FLOAT - intent_code = NIFTI_INTENT_TTEST mean that this is a 2D dataset (128x128) of t-statistics, with the t-statistic being in the first "plane" of data and the degrees-of-freedom parameter being in the second "plane" of data. If the dataset 5th dimension is used to store the voxel-wise statistical parameters, then dim[5] must be 1 plus the number of parameters required by that distribution (e.g., intent_code=NIFTI_INTENT_TTEST implies dim[5] must be 2, as in the example just above). Note: intent_code values 2..10 are compatible with AFNI 1.5x (which is why there is no code with value=1, which is obsolescent in AFNI). OTHER INTENTIONS: ---------------- The purpose of the intent_* fields is to help interpret the values stored in the dataset. Some non-statistical values for intent_code and conventions are provided for storing other complex data types. The intent_name field provides space for a 15 character (plus 0 byte) 'name' string for the type of data stored. Examples: - intent_code = NIFTI_INTENT_ESTIMATE; intent_name = "T1"; could be used to signify that the voxel values are estimates of the NMR parameter T1. - intent_code = NIFTI_INTENT_TTEST; intent_name = "House"; could be used to signify that the voxel values are t-statistics for the significance of 'activation' response to a House stimulus. - intent_code = NIFTI_INTENT_DISPVECT; intent_name = "ToMNI152"; could be used to signify that the voxel values are a displacement vector that transforms each voxel (x,y,z) location to the corresponding location in the MNI152 standard brain. - intent_code = NIFTI_INTENT_SYMMATRIX; intent_name = "DTI"; could be used to signify that the voxel values comprise a diffusion tensor image. If no data name is implied or needed, intent_name[0] should be set to 0. -----------------------------------------------------------------------------*/ /*! default: no intention is indicated in the header. */ const int32_t NIFTI_INTENT_NONE =0; /*-------- These codes are for probability distributions ---------------*/ /* Most distributions have a number of parameters, below denoted by p1, p2, and p3, and stored in - intent_p1, intent_p2, intent_p3 if dataset doesn't have 5th dimension - image data array if dataset does have 5th dimension Functions to compute with many of the distributions below can be found in the CDF library from U Texas. Formulas for and discussions of these distributions can be found in the following books: [U] Univariate Discrete Distributions, NL Johnson, S Kotz, AW Kemp. [C1] Continuous Univariate Distributions, vol. 1, NL Johnson, S Kotz, N Balakrishnan. [C2] Continuous Univariate Distributions, vol. 2, NL Johnson, S Kotz, N Balakrishnan. */ /*----------------------------------------------------------------------*/ /*! [C2, chap 32] Correlation coefficient R (1 param): p1 = degrees of freedom R/sqrt(1-R*R) is t-distributed with p1 DOF. */ /*! \defgroup NIFTI1_INTENT_CODES \brief nifti1 intent codes, to describe intended meaning of dataset contents @{ */ const int32_t NIFTI_INTENT_CORREL =2; /*! [C2, chap 28] Student t statistic (1 param): p1 = DOF. */ const int32_t NIFTI_INTENT_TTEST =3; /*! [C2, chap 27] Fisher F statistic (2 params): p1 = numerator DOF, p2 = denominator DOF. */ const int32_t NIFTI_INTENT_FTEST =4; /*! [C1, chap 13] Standard normal (0 params): Density = N(0,1). */ const int32_t NIFTI_INTENT_ZSCORE =5; /*! [C1, chap 18] Chi-squared (1 param): p1 = DOF. Density(x) proportional to exp(-x/2) * x^(p1/2-1). */ const int32_t NIFTI_INTENT_CHISQ =6; /*! [C2, chap 25] Beta distribution (2 params): p1=a, p2=b. Density(x) proportional to x^(a-1) * (1-x)^(b-1). */ const int32_t NIFTI_INTENT_BETA =7; /*! [U, chap 3] Binomial distribution (2 params): p1 = number of trials, p2 = probability per trial. Prob(x) = (p1 choose x) * p2^x * (1-p2)^(p1-x), for x=0,1,...,p1. */ const int32_t NIFTI_INTENT_BINOM =8; /*! [C1, chap 17] Gamma distribution (2 params): p1 = shape, p2 = scale. Density(x) proportional to x^(p1-1) * exp(-p2*x). */ const int32_t NIFTI_INTENT_GAMMA =9; /*! [U, chap 4] Poisson distribution (1 param): p1 = mean. Prob(x) = exp(-p1) * p1^x / x! , for x=0,1,2,.... */ const int32_t NIFTI_INTENT_POISSON =10; /*! [C1, chap 13] Normal distribution (2 params): p1 = mean, p2 = standard deviation. */ const int32_t NIFTI_INTENT_NORMAL =11; /*! [C2, chap 30] Noncentral F statistic (3 params): p1 = numerator DOF, p2 = denominator DOF, p3 = numerator noncentrality parameter. */ const int32_t NIFTI_INTENT_FTEST_NONC=12; /*! [C2, chap 29] Noncentral chi-squared statistic (2 params): p1 = DOF, p2 = noncentrality parameter. */ const int32_t NIFTI_INTENT_CHISQ_NONC=13; /*! [C2, chap 23] Logistic distribution (2 params): p1 = location, p2 = scale. Density(x) proportional to sech^2((x-p1)/(2*p2)). */ const int32_t NIFTI_INTENT_LOGISTIC =14; /*! [C2, chap 24] Laplace distribution (2 params): p1 = location, p2 = scale. Density(x) proportional to exp(-abs(x-p1)/p2). */ const int32_t NIFTI_INTENT_LAPLACE =15; /*! [C2, chap 26] Uniform distribution: p1 = lower end, p2 = upper end. */ const int32_t NIFTI_INTENT_UNIFORM =16; /*! [C2, chap 31] Noncentral t statistic (2 params): p1 = DOF, p2 = noncentrality parameter. */ const int32_t NIFTI_INTENT_TTEST_NONC=17; /*! [C1, chap 21] Weibull distribution (3 params): p1 = location, p2 = scale, p3 = power. Density(x) proportional to ((x-p1)/p2)^(p3-1) * exp(-((x-p1)/p2)^p3) for x > p1. */ const int32_t NIFTI_INTENT_WEIBULL =18; /*! [C1, chap 18] Chi distribution (1 param): p1 = DOF. Density(x) proportional to x^(p1-1) * exp(-x^2/2) for x > 0. p1 = 1 = 'half normal' distribution p1 = 2 = Rayleigh distribution p1 = 3 = Maxwell-Boltzmann distribution. */ const int32_t NIFTI_INTENT_CHI =19; /*! [C1, chap 15] Inverse Gaussian (2 params): p1 = mu, p2 = lambda Density(x) proportional to exp(-p2*(x-p1)^2/(2*p1^2*x)) / x^3 for x > 0. */ const int32_t NIFTI_INTENT_INVGAUSS =20; /*! [C2, chap 22] Extreme value type I (2 params): p1 = location, p2 = scale cdf(x) = exp(-exp(-(x-p1)/p2)). */ const int32_t NIFTI_INTENT_EXTVAL =21; /*! Data is a 'p-value' (no params). */ const int32_t NIFTI_INTENT_PVAL =22; /*! Data is ln(p-value) (no params). To be safe, a program should compute p = exp(-abs(this_value)). The nifti_stats.c library returns this_value as positive, so that this_value = -log(p). */ const int32_t NIFTI_INTENT_LOGPVAL =23; /*! Data is log10(p-value) (no params). To be safe, a program should compute p = pow(10.,-abs(this_value)). The nifti_stats.c library returns this_value as positive, so that this_value = -log10(p). */ const int32_t NIFTI_INTENT_LOG10PVAL =24; /*! Smallest intent_code that indicates a statistic. */ const int32_t NIFTI_FIRST_STATCODE =2; /*! Largest intent_code that indicates a statistic. */ const int32_t NIFTI_LAST_STATCODE =24; /*---------- these values for intent_code aren't for statistics ----------*/ /*! To signify that the value at each voxel is an estimate of some parameter, set intent_code = NIFTI_INTENT_ESTIMATE. The name of the parameter may be stored in intent_name. */ const int32_t NIFTI_INTENT_ESTIMATE =1001; /*! To signify that the value at each voxel is an index into some set of labels, set intent_code = NIFTI_INTENT_LABEL. The filename with the labels may stored in aux_file. */ const int32_t NIFTI_INTENT_LABEL =1002; /*! To signify that the value at each voxel is an index into the NeuroNames labels set, set intent_code = NIFTI_INTENT_NEURONAME. */ const int32_t NIFTI_INTENT_NEURONAME=1003; /*! To store an M x N matrix at each voxel: - dataset must have a 5th dimension (dim[0]=5 and dim[5]>1) - intent_code must be NIFTI_INTENT_GENMATRIX - dim[5] must be M*N - intent_p1 must be M (in float format) - intent_p2 must be N (ditto) - the matrix values A[i][[j] are stored in row-order: - A[0][0] A[0][1] ... A[0][N-1] - A[1][0] A[1][1] ... A[1][N-1] - etc., until - A[M-1][0] A[M-1][1] ... A[M-1][N-1] */ const int32_t NIFTI_INTENT_GENMATRIX=1004; /*! To store an NxN symmetric matrix at each voxel: - dataset must have a 5th dimension - intent_code must be NIFTI_INTENT_SYMMATRIX - dim[5] must be N*(N+1)/2 - intent_p1 must be N (in float format) - the matrix values A[i][[j] are stored in row-order: - A[0][0] - A[1][0] A[1][1] - A[2][0] A[2][1] A[2][2] - etc.: row-by-row */ const int32_t NIFTI_INTENT_SYMMATRIX=1005; /*! To signify that the vector value at each voxel is to be taken as a displacement field or vector: - dataset must have a 5th dimension - intent_code must be NIFTI_INTENT_DISPVECT - dim[5] must be the dimensionality of the displacment vector (e.g., 3 for spatial displacement, 2 for in-plane) */ const int32_t NIFTI_INTENT_DISPVECT =1006; /* specifically for displacements */ const int32_t NIFTI_INTENT_VECTOR =1007; /* for any other type of vector */ /*! To signify that the vector value at each voxel is really a spatial coordinate (e.g., the vertices or nodes of a surface mesh): - dataset must have a 5th dimension - intent_code must be NIFTI_INTENT_POINTSET - dim[0] = 5 - dim[1] = number of points - dim[2] = dim[3] = dim[4] = 1 - dim[5] must be the dimensionality of space (e.g., 3 => 3D space). - intent_name may describe the object these points come from (e.g., "pial", "gray/white" , "EEG", "MEG"). */ const int32_t NIFTI_INTENT_POINTSET =1008; /*! To signify that the vector value at each voxel is really a triple of indexes (e.g., forming a triangle) from a pointset dataset: - dataset must have a 5th dimension - intent_code must be NIFTI_INTENT_TRIANGLE - dim[0] = 5 - dim[1] = number of triangles - dim[2] = dim[3] = dim[4] = 1 - dim[5] = 3 - datatype should be an integer type (preferably DT_INT32) - the data values are indexes (0,1,...) into a pointset dataset. */ const int32_t NIFTI_INTENT_TRIANGLE =1009; /*! To signify that the vector value at each voxel is a quaternion: - dataset must have a 5th dimension - intent_code must be NIFTI_INTENT_QUATERNION - dim[0] = 5 - dim[5] = 4 - datatype should be a floating point type */ const int32_t NIFTI_INTENT_QUATERNION=1010; /*! Dimensionless value - no params - although, as in _ESTIMATE the name of the parameter may be stored in intent_name. */ const int32_t NIFTI_INTENT_DIMLESS =1011; /* @} */ /*---------------------------------------------------------------------------*/ /* 3D IMAGE (VOLUME) ORIENTATION AND LOCATION IN SPACE: --------------------------------------------------- There are 3 different methods by which continuous coordinates can attached to voxels. The discussion below emphasizes 3D volumes, and the continuous coordinates are referred to as (x,y,z). The voxel index coordinates (i.e., the array indexes) are referred to as (i,j,k), with valid ranges: i = 0 .. dim[1]-1 j = 0 .. dim[2]-1 (if dim[0] >= 2) k = 0 .. dim[3]-1 (if dim[0] >= 3) The (x,y,z) coordinates refer to the CENTER of a voxel. In methods 2 and 3, the (x,y,z) axes refer to a subject-based coordinate system, with +x = Right +y = Anterior +z = Superior. This is a right-handed coordinate system. However, the exact direction these axes point with respect to the subject depends on qform_code (Method 2) and sform_code (Method 3). N.B.: The i index varies most rapidly, j index next, k index slowest. Thus, voxel (i,j,k) is stored starting at location (i + j*dim[1] + k*dim[1]*dim[2]) * (bitpix/8) into the dataset array. N.B.: The ANALYZE 7.5 coordinate system is +x = Left +y = Anterior +z = Superior which is a left-handed coordinate system. This backwardness is too difficult to tolerate, so this NIFTI-1 standard specifies the coordinate order which is most common in functional neuroimaging. N.B.: The 3 methods below all give the locations of the voxel centers in the (x,y,z) coordinate system. In many cases, programs will wish to display image data on some other grid. In such a case, the program will need to convert its desired (x,y,z) values into (i,j,k) values in order to extract (or interpolate) the image data. This operation would be done with the inverse transformation to those described below. N.B.: Method 2 uses a factor 'qfac' which is either -1 or 1; qfac is stored in the otherwise unused pixdim[0]. If pixdim[0]=0.0 (which should not occur), we take qfac=1. Of course, pixdim[0] is only used when reading a NIFTI-1 header, not when reading an ANALYZE 7.5 header. N.B.: The units of (x,y,z) can be specified using the xyzt_units field. METHOD 1 (the "old" way, used only when qform_code = 0): ------------------------------------------------------- The coordinate mapping from (i,j,k) to (x,y,z) is the ANALYZE 7.5 way. This is a simple scaling relationship: x = pixdim[1] * i y = pixdim[2] * j z = pixdim[3] * k No particular spatial orientation is attached to these (x,y,z) coordinates. (NIFTI-1 does not have the ANALYZE 7.5 orient field, which is not general and is often not set properly.) This method is not recommended, and is present mainly for compatibility with ANALYZE 7.5 files. METHOD 2 (used when qform_code > 0, which should be the "normal" case): --------------------------------------------------------------------- The (x,y,z) coordinates are given by the pixdim[] scales, a rotation matrix, and a shift. This method is intended to represent "scanner-anatomical" coordinates, which are often embedded in the image header (e.g., DICOM fields (0020,0032), (0020,0037), (0028,0030), and (0018,0050)), and represent the nominal orientation and location of the data. This method can also be used to represent "aligned" coordinates, which would typically result from some post-acquisition alignment of the volume to a standard orientation (e.g., the same subject on another day, or a rigid rotation to true anatomical orientation from the tilted position of the subject in the scanner). The formula for (x,y,z) in terms of header parameters and (i,j,k) is: [ x ] [ R11 R12 R13 ] [ pixdim[1] * i ] [ qoffset_x ] [ y ] = [ R21 R22 R23 ] [ pixdim[2] * j ] + [ qoffset_y ] [ z ] [ R31 R32 R33 ] [ qfac * pixdim[3] * k ] [ qoffset_z ] The qoffset_* shifts are in the NIFTI-1 header. Note that the center of the (i,j,k)=(0,0,0) voxel (first value in the dataset array) is just (x,y,z)=(qoffset_x,qoffset_y,qoffset_z). The rotation matrix R is calculated from the quatern_* parameters. This calculation is described below. The scaling factor qfac is either 1 or -1. The rotation matrix R defined by the quaternion parameters is "proper" (has determinant 1). This may not fit the needs of the data; for example, if the image grid is i increases from Left-to-Right j increases from Anterior-to-Posterior k increases from Inferior-to-Superior Then (i,j,k) is a left-handed triple. In this example, if qfac=1, the R matrix would have to be [ 1 0 0 ] [ 0 -1 0 ] which is "improper" (determinant = -1). [ 0 0 1 ] If we set qfac=-1, then the R matrix would be [ 1 0 0 ] [ 0 -1 0 ] which is proper. [ 0 0 -1 ] This R matrix is represented by quaternion [a,b,c,d] = [0,1,0,0] (which encodes a 180 degree rotation about the x-axis). METHOD 3 (used when sform_code > 0): ----------------------------------- The (x,y,z) coordinates are given by a general affine transformation of the (i,j,k) indexes: x = srow_x[0] * i + srow_x[1] * j + srow_x[2] * k + srow_x[3] y = srow_y[0] * i + srow_y[1] * j + srow_y[2] * k + srow_y[3] z = srow_z[0] * i + srow_z[1] * j + srow_z[2] * k + srow_z[3] The srow_* vectors are in the NIFTI_1 header. Note that no use is made of pixdim[] in this method. WHY 3 METHODS? -------------- Method 1 is provided only for backwards compatibility. The intention is that Method 2 (qform_code > 0) represents the nominal voxel locations as reported by the scanner, or as rotated to some fiducial orientation and location. Method 3, if present (sform_code > 0), is to be used to give the location of the voxels in some standard space. The sform_code indicates which standard space is present. Both methods 2 and 3 can be present, and be useful in different contexts (method 2 for displaying the data on its original grid; method 3 for displaying it on a standard grid). In this scheme, a dataset would originally be set up so that the Method 2 coordinates represent what the scanner reported. Later, a registration to some standard space can be computed and inserted in the header. Image display software can use either transform, depending on its purposes and needs. In Method 2, the origin of coordinates would generally be whatever the scanner origin is; for example, in MRI, (0,0,0) is the center of the gradient coil. In Method 3, the origin of coordinates would depend on the value of sform_code; for example, for the Talairach coordinate system, (0,0,0) corresponds to the Anterior Commissure. QUATERNION REPRESENTATION OF ROTATION MATRIX (METHOD 2) ------------------------------------------------------- The orientation of the (x,y,z) axes relative to the (i,j,k) axes in 3D space is specified using a unit quaternion [a,b,c,d], where a*a+b*b+c*c+d*d=1. The (b,c,d) values are all that is needed, since we require that a = sqrt(1.0-(b*b+c*c+d*d)) be nonnegative. The (b,c,d) values are stored in the (quatern_b,quatern_c,quatern_d) fields. The quaternion representation is chosen for its compactness in representing rotations. The (proper) 3x3 rotation matrix that corresponds to [a,b,c,d] is [ a*a+b*b-c*c-d*d 2*b*c-2*a*d 2*b*d+2*a*c ] R = [ 2*b*c+2*a*d a*a+c*c-b*b-d*d 2*c*d-2*a*b ] [ 2*b*d-2*a*c 2*c*d+2*a*b a*a+d*d-c*c-b*b ] [ R11 R12 R13 ] = [ R21 R22 R23 ] [ R31 R32 R33 ] If (p,q,r) is a unit 3-vector, then rotation of angle h about that direction is represented by the quaternion [a,b,c,d] = [cos(h/2), p*sin(h/2), q*sin(h/2), r*sin(h/2)]. Requiring a >= 0 is equivalent to requiring -Pi <= h <= Pi. (Note that [-a,-b,-c,-d] represents the same rotation as [a,b,c,d]; there are 2 quaternions that can be used to represent a given rotation matrix R.) To rotate a 3-vector (x,y,z) using quaternions, we compute the quaternion product [0,x',y',z'] = [a,b,c,d] * [0,x,y,z] * [a,-b,-c,-d] which is equivalent to the matrix-vector multiply [ x' ] [ x ] [ y' ] = R [ y ] (equivalence depends on a*a+b*b+c*c+d*d=1) [ z' ] [ z ] Multiplication of 2 quaternions is defined by the following: [a,b,c,d] = a*1 + b*I + c*J + d*K where I*I = J*J = K*K = -1 (I,J,K are square roots of -1) I*J = K J*K = I K*I = J J*I = -K K*J = -I I*K = -J (not commutative!) For example [a,b,0,0] * [0,0,0,1] = [0,0,-b,a] since this expands to (a+b*I)*(K) = (a*K+b*I*K) = (a*K-b*J). The above formula shows how to go from quaternion (b,c,d) to rotation matrix and direction cosines. Conversely, given R, we can compute the fields for the NIFTI-1 header by a = 0.5 * sqrt(1+R11+R22+R33) (not stored) b = 0.25 * (R32-R23) / a => quatern_b c = 0.25 * (R13-R31) / a => quatern_c d = 0.25 * (R21-R12) / a => quatern_d If a=0 (a 180 degree rotation), alternative formulas are needed. See the nifti1_io.c function mat44_to_quatern() for an implementation of the various cases in converting R to [a,b,c,d]. Note that R-transpose (= R-inverse) would lead to the quaternion [a,-b,-c,-d]. The choice to specify the qoffset_x (etc.) values in the final coordinate system is partly to make it easy to convert DICOM images to this format. The DICOM attribute "Image Position (Patient)" (0020,0032) stores the (Xd,Yd,Zd) coordinates of the center of the first voxel. Here, (Xd,Yd,Zd) refer to DICOM coordinates, and Xd=-x, Yd=-y, Zd=z, where (x,y,z) refers to the NIFTI coordinate system discussed above. (i.e., DICOM +Xd is Left, +Yd is Posterior, +Zd is Superior, whereas +x is Right, +y is Anterior , +z is Superior. ) Thus, if the (0020,0032) DICOM attribute is extracted into (px,py,pz), then qoffset_x = -px qoffset_y = -py qoffset_z = pz is a reasonable setting when qform_code=NIFTI_XFORM_SCANNER_ANAT. That is, DICOM's coordinate system is 180 degrees rotated about the z-axis from the neuroscience/NIFTI coordinate system. To transform between DICOM and NIFTI, you just have to negate the x- and y-coordinates. The DICOM attribute (0020,0037) "Image Orientation (Patient)" gives the orientation of the x- and y-axes of the image data in terms of 2 3-vectors. The first vector is a unit vector along the x-axis, and the second is along the y-axis. If the (0020,0037) attribute is extracted into the value (xa,xb,xc,ya,yb,yc), then the first two columns of the R matrix would be [ -xa -ya ] [ -xb -yb ] [ xc yc ] The negations are because DICOM's x- and y-axes are reversed relative to NIFTI's. The third column of the R matrix gives the direction of displacement (relative to the subject) along the slice-wise direction. This orientation is not encoded in the DICOM standard in a simple way; DICOM is mostly concerned with 2D images. The third column of R will be either the cross-product of the first 2 columns or its negative. It is possible to infer the sign of the 3rd column by examining the coordinates in DICOM attribute (0020,0032) "Image Position (Patient)" for successive slices. However, this method occasionally fails for reasons that I (RW Cox) do not understand. -----------------------------------------------------------------------------*/ /* [qs]form_code value: */ /* x,y,z coordinate system refers to: */ /*-----------------------*/ /*---------------------------------------*/ /*! \defgroup NIFTI1_XFORM_CODES \brief nifti1 xform codes to describe the "standard" coordinate system @{ */ /*! Arbitrary coordinates (Method 1). */ const int32_t NIFTI_XFORM_UNKNOWN =0; /*! Scanner-based anatomical coordinates */ const int32_t NIFTI_XFORM_SCANNER_ANAT=1; /*! Coordinates aligned to another file's, or to anatomical "truth". */ const int32_t NIFTI_XFORM_ALIGNED_ANAT=2; /*! Coordinates aligned to Talairach- Tournoux Atlas; (0,0,0)=AC, etc. */ const int32_t NIFTI_XFORM_TALAIRACH =3; /*! MNI 152 normalized coordinates. */ const int32_t NIFTI_XFORM_MNI_152 =4; /* @} */ /*---------------------------------------------------------------------------*/ /* UNITS OF SPATIAL AND TEMPORAL DIMENSIONS: ---------------------------------------- The codes below can be used in xyzt_units to indicate the units of pixdim. As noted earlier, dimensions 1,2,3 are for x,y,z; dimension 4 is for time (t). - If dim[4]=1 or dim[0] < 4, there is no time axis. - A single time series (no space) would be specified with - dim[0] = 4 (for scalar data) or dim[0] = 5 (for vector data) - dim[1] = dim[2] = dim[3] = 1 - dim[4] = number of time points - pixdim[4] = time step - xyzt_units indicates units of pixdim[4] - dim[5] = number of values stored at each time point Bits 0..2 of xyzt_units specify the units of pixdim[1..3] (e.g., spatial units are values 1..7). Bits 3..5 of xyzt_units specify the units of pixdim[4] (e.g., temporal units are multiples of 8). This compression of 2 distinct concepts into 1 byte is due to the limited space available in the 348 byte ANALYZE 7.5 header. The macros XYZT_TO_SPACE and XYZT_TO_TIME can be used to mask off the undesired bits from the xyzt_units fields, leaving "pure" space and time codes. Inversely, the macro SPACE_TIME_TO_XYZT can be used to assemble a space code (0,1,2,...,7) with a time code (0,8,16,32,...,56) into the combined value for xyzt_units. Note that codes are provided to indicate the "time" axis units are actually frequency in Hertz (_HZ), in part-per-million (_PPM) or in radians-per-second (_RADS). The toffset field can be used to indicate a nonzero start point for the time axis. That is, time point #m is at t=toffset+m*pixdim[4] for m=0..dim[4]-1. -----------------------------------------------------------------------------*/ /*! \defgroup NIFTI1_UNITS \brief nifti1 units codes to describe the unit of measurement for each dimension of the dataset @{ */ /*! NIFTI code for unspecified units. */ const int32_t NIFTI_UNITS_UNKNOWN=0; /** Space codes are multiples of 1. **/ /*! NIFTI code for meters. */ const int32_t NIFTI_UNITS_METER =1; /*! NIFTI code for millimeters. */ const int32_t NIFTI_UNITS_MM =2; /*! NIFTI code for micrometers. */ const int32_t NIFTI_UNITS_MICRON =3; /** Time codes are multiples of 8. **/ /*! NIFTI code for seconds. */ const int32_t NIFTI_UNITS_SEC =8; /*! NIFTI code for milliseconds. */ const int32_t NIFTI_UNITS_MSEC =16; /*! NIFTI code for microseconds. */ const int32_t NIFTI_UNITS_USEC =24; /*** These units are for spectral data: ***/ /*! NIFTI code for Hertz. */ const int32_t NIFTI_UNITS_HZ =32; /*! NIFTI code for ppm. */ const int32_t NIFTI_UNITS_PPM =40; /*! NIFTI code for radians per second. */ const int32_t NIFTI_UNITS_RADS =48; /* @} */ #undef XYZT_TO_SPACE #undef XYZT_TO_TIME #define XYZT_TO_SPACE(xyzt) ( (xyzt) & 0x07 ) #define XYZT_TO_TIME(xyzt) ( (xyzt) & 0x38 ) #undef SPACE_TIME_TO_XYZT #define SPACE_TIME_TO_XYZT(ss,tt) ( (((char)(ss)) & 0x07) \ | (((char)(tt)) & 0x38) ) /*---------------------------------------------------------------------------*/ /* MRI-SPECIFIC SPATIAL AND TEMPORAL INFORMATION: --------------------------------------------- A few fields are provided to store some extra information that is sometimes important when storing the image data from an FMRI time series experiment. (After processing such data into statistical images, these fields are not likely to be useful.) { freq_dim } = These fields encode which spatial dimension (1,2, or 3) { phase_dim } = corresponds to which acquisition dimension for MRI data. { slice_dim } = Examples: Rectangular scan multi-slice EPI: freq_dim = 1 phase_dim = 2 slice_dim = 3 (or some permutation) Spiral scan multi-slice EPI: freq_dim = phase_dim = 0 slice_dim = 3 since the concepts of frequency- and phase-encoding directions don't apply to spiral scan slice_duration = If this is positive, AND if slice_dim is nonzero, indicates the amount of time used to acquire 1 slice. slice_duration*dim[slice_dim] can be less than pixdim[4] with a clustered acquisition method, for example. slice_code = If this is nonzero, AND if slice_dim is nonzero, AND if slice_duration is positive, indicates the timing pattern of the slice acquisition. The following codes are defined: NIFTI_SLICE_SEQ_INC == sequential increasing NIFTI_SLICE_SEQ_DEC == sequential decreasing NIFTI_SLICE_ALT_INC == alternating increasing NIFTI_SLICE_ALT_DEC == alternating decreasing NIFTI_SLICE_ALT_INC2 == alternating increasing #2 NIFTI_SLICE_ALT_DEC2 == alternating decreasing #2 { slice_start } = Indicates the start and end of the slice acquisition { slice_end } = pattern, when slice_code is nonzero. These values are present to allow for the possible addition of "padded" slices at either end of the volume, which don't fit into the slice timing pattern. If there are no padding slices, then slice_start=0 and slice_end=dim[slice_dim]-1 are the correct values. For these values to be meaningful, slice_start must be non-negative and slice_end must be greater than slice_start. Otherwise, they should be ignored. The following table indicates the slice timing pattern, relative to time=0 for the first slice acquired, for some sample cases. Here, dim[slice_dim]=7 (there are 7 slices, labeled 0..6), slice_duration=0.1, and slice_start=1, slice_end=5 (1 padded slice on each end). slice index SEQ_INC SEQ_DEC ALT_INC ALT_DEC ALT_INC2 ALT_DEC2 6 : n/a n/a n/a n/a n/a n/a n/a = not applicable 5 : 0.4 0.0 0.2 0.0 0.4 0.2 (slice time offset 4 : 0.3 0.1 0.4 0.3 0.1 0.0 doesn't apply to 3 : 0.2 0.2 0.1 0.1 0.3 0.3 slices outside 2 : 0.1 0.3 0.3 0.4 0.0 0.1 the range 1 : 0.0 0.4 0.0 0.2 0.2 0.4 slice_start .. 0 : n/a n/a n/a n/a n/a n/a slice_end) The SEQ slice_codes are sequential ordering (uncommon but not unknown), either increasing in slice number or decreasing (INC or DEC), as illustrated above. The ALT slice codes are alternating ordering. The 'standard' way for these to operate (without the '2' on the end) is for the slice timing to start at the edge of the slice_start .. slice_end group (at slice_start for INC and at slice_end for DEC). For the 'ALT_*2' slice_codes, the slice timing instead starts at the first slice in from the edge (at slice_start+1 for INC2 and at slice_end-1 for DEC2). This latter acquisition scheme is found on some Siemens scanners. The fields freq_dim, phase_dim, slice_dim are all squished into the single byte field dim_info (2 bits each, since the values for each field are limited to the range 0..3). This unpleasantness is due to lack of space in the 348 byte allowance. The macros DIM_INFO_TO_FREQ_DIM, DIM_INFO_TO_PHASE_DIM, and DIM_INFO_TO_SLICE_DIM can be used to extract these values from the dim_info byte. The macro FPS_INTO_DIM_INFO can be used to put these 3 values into the dim_info byte. -----------------------------------------------------------------------------*/ #undef DIM_INFO_TO_FREQ_DIM #undef DIM_INFO_TO_PHASE_DIM #undef DIM_INFO_TO_SLICE_DIM #define DIM_INFO_TO_FREQ_DIM(di) ( ((di) ) & 0x03 ) #define DIM_INFO_TO_PHASE_DIM(di) ( ((di) >> 2) & 0x03 ) #define DIM_INFO_TO_SLICE_DIM(di) ( ((di) >> 4) & 0x03 ) #undef FPS_INTO_DIM_INFO #define FPS_INTO_DIM_INFO(fd,pd,sd) ( ( ( ((char)(fd)) & 0x03) ) | \ ( ( ((char)(pd)) & 0x03) << 2 ) | \ ( ( ((char)(sd)) & 0x03) << 4 ) ) /*! \defgroup NIFTI1_SLICE_ORDER \brief nifti1 slice order codes, describing the acquisition order of the slices @{ */ const int32_t NIFTI_SLICE_UNKNOWN =0; const int32_t NIFTI_SLICE_SEQ_INC =1; const int32_t NIFTI_SLICE_SEQ_DEC =2; const int32_t NIFTI_SLICE_ALT_INC =3; const int32_t NIFTI_SLICE_ALT_DEC =4; const int32_t NIFTI_SLICE_ALT_INC2 =5; /* 05 May 2005: RWCox */ const int32_t NIFTI_SLICE_ALT_DEC2 =6; /* 05 May 2005: RWCox */ /* @} */ /*---------------------------------------------------------------------------*/ /* UNUSED FIELDS: ------------- Some of the ANALYZE 7.5 fields marked as ++UNUSED++ may need to be set to particular values for compatibility with other programs. The issue of interoperability of ANALYZE 7.5 files is a murky one -- not all programs require exactly the same set of fields. (Unobscuring this murkiness is a principal motivation behind NIFTI-1.) Some of the fields that may need to be set for other (non-NIFTI aware) software to be happy are: extents dbh.h says this should be 16384 regular dbh.h says this should be the character 'r' glmin, } dbh.h says these values should be the min and max voxel glmax } values for the entire dataset It is best to initialize ALL fields in the NIFTI-1 header to 0 (e.g., with calloc()), then fill in what is needed. -----------------------------------------------------------------------------*/ /*---------------------------------------------------------------------------*/ /* MISCELLANEOUS C MACROS -----------------------------------------------------------------------------*/ /*.................*/ /*! Given a nifti_1_header struct, check if it has a good magic number. Returns NIFTI version number (1..9) if magic is good, 0 if it is not. */ #define NIFTI_VERSION(h) \ ( ( (h).magic[0]=='n' && (h).magic[3]=='\0' && \ ( (h).magic[1]=='i' || (h).magic[1]=='+' ) && \ ( (h).magic[2]>='1' && (h).magic[2]<='9' ) ) \ ? (h).magic[2]-'0' : 0 ) /*.................*/ /*! Check if a nifti_1_header struct says if the data is stored in the same file or in a separate file. Returns 1 if the data is in the same file as the header, 0 if it is not. */ #define NIFTI_ONEFILE(h) ( (h).magic[1] == '+' ) /*.................*/ /*! Check if a nifti_1_header struct needs to be byte swapped. Returns 1 if it needs to be swapped, 0 if it does not. */ #define NIFTI_NEEDS_SWAP(h) ( (h).dim[0] < 0 || (h).dim[0] > 7 ) /*.................*/ /*! Check if a nifti_1_header struct contains a 5th (vector) dimension. Returns size of 5th dimension if > 1, returns 0 otherwise. */ #define NIFTI_5TH_DIM(h) ( ((h).dim[0]>4 && (h).dim[5]>1) ? (h).dim[5] : 0 ) /*****************************************************************************/ /*=================*/ #ifdef __cplusplus } #endif /*=================*/ #endif /* _NIFTI_HEADER_ */ connectome-workbench-1.2.3+git41-gc4c6c90/src/FilesBase/nifti2.h000066400000000000000000000152551300200146000237550ustar00rootroot00000000000000#ifndef __NIFTI2_HEADER #define __NIFTI2_HEADER /*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 "nifti1.h" /*=================*/ #ifdef __cplusplus extern "C" { #endif /*=================*/ #include /*extended nifti intent codes*/ const int32_t NIFTI_INTENT_CONNECTIVITY_UNKNOWN=3000; const int32_t NIFTI_INTENT_CONNECTIVITY_DENSE=3001; const int32_t NIFTI_INTENT_CONNECTIVITY_DENSE_TIME=3002;//CIFTI-1 name const int32_t NIFTI_INTENT_CONNECTIVITY_DENSE_SERIES=3002;//CIFTI-2 name const int32_t NIFTI_INTENT_CONNECTIVITY_PARCELLATED=3003; const int32_t NIFTI_INTENT_CONNECTIVITY_PARCELLATED_TIME=3004;//ditto const int32_t NIFTI_INTENT_CONNECTIVITY_PARCELLATED_SERIES=3004; const int32_t NIFTI_INTENT_CONNECTIVITY_CONNECTIVITY_TRAJECTORY=3005;//ditto const int32_t NIFTI_INTENT_CONNECTIVITY_DENSE_TRAJECTORY=3005; const int32_t NIFTI_INTENT_CONNECTIVITY_DENSE_SCALARS=3006; const int32_t NIFTI_INTENT_CONNECTIVITY_DENSE_LABELS=3007; const int32_t NIFTI_INTENT_CONNECTIVITY_PARCELLATED_SCALAR=3008; const int32_t NIFTI_INTENT_CONNECTIVITY_PARCELLATED_DENSE=3009; const int32_t NIFTI_INTENT_CONNECTIVITY_DENSE_PARCELLATED=3010; const int32_t NIFTI_INTENT_CONNECTIVITY_PARCELLATED_PARCELLATED_SERIES=3011; const int32_t NIFTI_INTENT_CONNECTIVITY_PARCELLATED_PARCELLATED_SCALAR=3012; const int32_t NIFTI_ECODE_CIFTI=32; #define NIFTI2_VERSION(h) \ (h).sizeof_hdr == 348 ? 1 : (\ (h).sizeof_hdr == 1543569408 ? 1 : (\ (h).sizeof_hdr == 540 ? 2 : (\ (h).sizeof_hdr == 469893120 ? 2 : 0))) #define NIFTI2_NEEDS_SWAP(h) \ (h).sizeof_hdr == 469893120 ? 1 : (\ (h).sizeof_hdr == 1543569408 ? 1 : 0) //hopefully cross-platform solution to byte padding added by some compilers #pragma pack(push) #pragma pack(1) /*! \struct nifti_2_header \brief Data structure defining the fields in the nifti2 header. This binary header should be found at the beginning of a valid NIFTI-2 header file. */ /*************************/ /************************/ /************/ struct nifti_2_header { /* NIFTI-2 usage */ /* NIFTI-1 usage */ /* offset */ /*************************/ /************************/ /************/ int32_t sizeof_hdr; /*!< MUST be 540 */ /* int32_t sizeof_hdr; (348) */ /* 0 */ char magic[8] ; /*!< MUST be valid signature. */ /* char magic[4]; */ /* 4 */ int16_t datatype; /*!< Defines data type! */ /* short datatype; */ /* 12 */ int16_t bitpix; /*!< Number bits/voxel. */ /* short bitpix; */ /* 14 */ int64_t dim[8]; /*!< Data array dimensions.*/ /* short dim[8]; */ /* 16 */ double intent_p1 ; /*!< 1st intent parameter. */ /* float intent_p1; */ /* 80 */ double intent_p2 ; /*!< 2nd intent parameter. */ /* float intent_p2; */ /* 88 */ double intent_p3 ; /*!< 3rd intent parameter. */ /* float intent_p3; */ /* 96 */ double pixdim[8]; /*!< Grid spacings. */ /* float pixdim[8]; */ /* 104 */ int64_t vox_offset; /*!< Offset into .nii file */ /* float vox_offset; */ /* 168 */ double scl_slope ; /*!< Data scaling: slope. */ /* float scl_slope; */ /* 176 */ double scl_inter ; /*!< Data scaling: offset. */ /* float scl_inter; */ /* 184 */ double cal_max; /*!< Max display intensity */ /* float cal_max; */ /* 192 */ double cal_min; /*!< Min display intensity */ /* float cal_min; */ /* 200 */ double slice_duration;/*!< Time for 1 slice. */ /* float slice_duration; */ /* 208 */ double toffset; /*!< Time axis shift. */ /* float toffset; */ /* 216 */ int64_t slice_start;/*!< First slice index. */ /* short slice_start; */ /* 224 */ int64_t slice_end; /*!< Last slice index. */ /* short slice_end; */ /* 232 */ char descrip[80]; /*!< any text you like. */ /* char descrip[80]; */ /* 240 */ char aux_file[24]; /*!< auxiliary filename. */ /* char aux_file[24]; */ /* 320 */ int32_t qform_code ; /*!< NIFTI_XFORM_* code. */ /* short qform_code; */ /* 344 */ int32_t sform_code ; /*!< NIFTI_XFORM_* code. */ /* short sform_code; */ /* 348 */ double quatern_b ; /*!< Quaternion b param. */ /* float quatern_b; */ /* 352 */ double quatern_c ; /*!< Quaternion c param. */ /* float quatern_c; */ /* 360 */ double quatern_d ; /*!< Quaternion d param. */ /* float quatern_d; */ /* 368 */ double qoffset_x ; /*!< Quaternion x shift. */ /* float qoffset_x; */ /* 376 */ double qoffset_y ; /*!< Quaternion y shift. */ /* float qoffset_y; */ /* 384 */ double qoffset_z ; /*!< Quaternion z shift. */ /* float qoffset_z; */ /* 392 */ double srow_x[4] ; /*!< 1st row affine transform. */ /* float srow_x[4]; */ /* 400 */ double srow_y[4] ; /*!< 2nd row affine transform. */ /* float srow_y[4]; */ /* 432 */ double srow_z[4] ; /*!< 3rd row affine transform. */ /* float srow_z[4]; */ /* 464 */ int32_t slice_code ; /*!< Slice timing order. */ /* char slice_code; */ /* 496 */ int32_t xyzt_units ; /*!< Units of pixdim[1..4] */ /* char xyzt_units; */ /* 500 */ int32_t intent_code ; /*!< NIFTI_INTENT_* code. */ /* short intent_code; */ /* 504 */ char intent_name[16]; /*!< 'name' or meaning of data. */ /* char intent_name[16]; */ /* 508 */ char dim_info; /*!< MRI slice ordering. */ /* char dim_info; */ /* 524 */ char unused_str[15]; /*!< unused, filled with \0 */ /* 525 */ } ; /**** 540 bytes total ****/ typedef struct nifti_2_header nifti_2_header ; //and restore packing behavior #pragma pack(pop) /*=================*/ #ifdef __cplusplus } #endif /*=================*/ #endif //__NIFTI2_HEADER connectome-workbench-1.2.3+git41-gc4c6c90/src/FtglFont/000077500000000000000000000000001300200146000222675ustar00rootroot00000000000000connectome-workbench-1.2.3+git41-gc4c6c90/src/FtglFont/AUTHORS000066400000000000000000000021351300200146000233400ustar00rootroot00000000000000 -*- coding: utf-8 -*- Original author: Henry Maddocks http://homepages.paradise.net.nz/henryj/ Contributors: Jed Soane (Bezier curve code) Gérard Lanois (demo, Linux port, extrusion code, gltt maintainance) Matthias Kretz (Linux port) Andrew Ellerton (Windows port) Max Rheiner (Windows port) Sébastien Barré (containers and optimisations) Marcelo E. Magallon (original autoconf, bug fixes) Robert Bell (pixmap font modifications) Sam Hocevar (build system, new maintainer) Éric Beets (C bindings) Christopher Sean Morrison (bug fixes, new maintainer) Jeff Myers (JeffM2501) (Windows fixes) Daniel Remenak (Windows fixes) Portions derived from ConvertUTF.c Copyright (C) 2001-2004 Unicode, Inc. Bug fixes: Robert Osfield Markku Rontu Mark A. Fox Patrick Rogers Kai Huettemann FTGL was inspired by gltt, Copyright (C) 1998-1999 Stephane Rehel (http://gltt.sourceforge.net) connectome-workbench-1.2.3+git41-gc4c6c90/src/FtglFont/CMakeLists.txt000066400000000000000000000056671300200146000250450ustar00rootroot00000000000000# # Name of project # 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) # # Create the brain library # ADD_LIBRARY(FtglFont FTCharToGlyphIndexMap.h FTContour.h FTFace.h FTFont/FTBitmapFontImpl.h FTFont/FTBufferFontImpl.h FTFont/FTExtrudeFontImpl.h FTFont/FTFontImpl.h FTFont/FTOutlineFontImpl.h FTFont/FTPixmapFontImpl.h FTFont/FTPolygonFontImpl.h FTFont/FTTextureFontImpl.h FTGL/FTBBox.h FTGL/FTBitmapGlyph.h FTGL/FTBuffer.h FTGL/FTBufferFont.h FTGL/FTBufferGlyph.h FTGL/FTExtrdGlyph.h FTGL/FTFont.h FTGL/ftgl.h FTGL/FTGLBitmapFont.h FTGL/FTGLExtrdFont.h FTGL/FTGLOutlineFont.h FTGL/FTGLPixmapFont.h FTGL/FTGLPolygonFont.h FTGL/FTGLTextureFont.h FTGL/FTGLGlyph.h FTGL/FTLayout.h FTGL/FTOutlineGlyph.h FTGL/FTPixmapGlyph.h FTGL/FTPoint.h FTGL/FTPolyGlyph.h FTGL/FTSimpleLayout.h FTGL/FTTextureGlyph.h FTGlyph/FTBitmapGlyphImpl.h FTGlyph/FTBufferGlyphImpl.h FTGlyph/FTExtrudeGlyphImpl.h FTGlyph/FTGlyphImpl.h FTGlyph/FTOutlineGlyphImpl.h FTGlyph/FTPixmapGlyphImpl.h FTGlyph/FTPolygonGlyphImpl.h FTGlyph/FTTextureGlyphImpl.h FTGlyphContainer.h FTInternals.h FTLayout/FTLayoutImpl.h FTLayout/FTSimpleLayoutImpl.h FTLibrary.h FTList.h FTSize.h FTUnicode.h FTVector.h FTVectoriser.h FtglConfig.h FTBuffer.cpp FTCharmap.cpp FTContour.cpp FTFace.cpp FTFont/FTBitmapFont.cpp FTFont/FTBufferFont.cpp FTFont/FTExtrudeFont.cpp FTFont/FTFont.cpp FTFont/FTFontGlue.cpp FTFont/FTOutlineFont.cpp FTFont/FTPixmapFont.cpp FTFont/FTPolygonFont.cpp FTFont/FTTextureFont.cpp FTGlyph/FTBitmapGlyph.cpp FTGlyph/FTBufferGlyph.cpp FTGlyph/FTExtrudeGlyph.cpp FTGlyph/FTGlyph.cpp FTGlyph/FTGlyphGlue.cpp FTGlyph/FTOutlineGlyph.cpp FTGlyph/FTPixmapGlyph.cpp FTGlyph/FTPolygonGlyph.cpp FTGlyph/FTTextureGlyph.cpp FTGlyphContainer.cpp FTLayout/FTLayout.cpp FTLayout/FTLayoutGlue.cpp FTLayout/FTSimpleLayout.cpp FTLibrary.cpp FTPoint.cpp FTSize.cpp FTVectoriser.cpp ) # # Find Headers # INCLUDE_DIRECTORIES( ${CMAKE_SOURCE_DIR}/Common ${FREETYPE_INCLUDE_DIR_ft2build} ${FREETYPE_INCLUDE_DIR_freetype2} ${CMAKE_SOURCE_DIR}/FtglFont ${CMAKE_SOURCE_DIR}/FtglFont/FTFont ${CMAKE_SOURCE_DIR}/FtglFont/FTGL ${CMAKE_SOURCE_DIR}/FtglFont/FTGlyph ) SET(FTGL_LIBRARIES FtglFont PARENT_SCOPE) SET(FTGL_INCLUDE_DIRS ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_SOURCE_DIR}/FTGL PARENT_SCOPE) connectome-workbench-1.2.3+git41-gc4c6c90/src/FtglFont/COPYING000066400000000000000000000022671300200146000233310ustar00rootroot00000000000000FTGL Herewith is a license. Basically I want you to use this software and if you think this license is preventing you from doing so let me know. Copyright (C) 2001-3 Henry Maddocks Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. connectome-workbench-1.2.3+git41-gc4c6c90/src/FtglFont/FTBuffer.cpp000066400000000000000000000032461300200146000244430ustar00rootroot00000000000000/* * FTGL - OpenGL font library * * Copyright (c) 2008 Sam Hocevar * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #include "FtglConfig.h" #include "FTGL/ftgl.h" FTBuffer::FTBuffer() : width(0), height(0), pixels(0), pos(FTPoint()) { } FTBuffer::~FTBuffer() { if(pixels) { delete[] pixels; } } void FTBuffer::Size(int w, int h) { if(w == width && h == height) { return; } if(w * h != width * height) { if(pixels) { delete[] pixels; } pixels = new unsigned char[w * h]; } memset(pixels, 0, w * h); width = w; height = h; } connectome-workbench-1.2.3+git41-gc4c6c90/src/FtglFont/FTCharToGlyphIndexMap.h000066400000000000000000000115351300200146000265110ustar00rootroot00000000000000/* * FTGL - OpenGL font library * * Copyright (c) 2001-2004 Henry Maddocks * Copyright (c) 2014 Washington University School of Medicine * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #ifndef __FTCharToGlyphIndexMap__ #define __FTCharToGlyphIndexMap__ #include #include "FTGL/ftgl.h" /** * Provides a non-STL alternative to the STL map * which maps character codes to glyph indices inside FTCharmap. * * Implementation: * - NumberOfBuckets buckets are considered. * - Each bucket has BucketSize entries. * - When the glyph index for the character code C has to be stored, the * bucket this character belongs to is found using 'C div BucketSize'. * If this bucket has not been allocated yet, do it now. * The entry in the bucked is found using 'C mod BucketSize'. * If it is set to IndexNotFound, then the glyph entry has not been set. * - Try to mimic the calls made to the STL map API. * * Caveats: * - The glyph index is now a signed long instead of unsigned long, so * the special value IndexNotFound (= -1) can be used to specify that the * glyph index has not been stored yet. */ class FTCharToGlyphIndexMap { public: typedef unsigned long CharacterCode; typedef signed long GlyphIndex; enum { NumberOfBuckets = 256, BucketSize = 256, IndexNotFound = -1 }; FTCharToGlyphIndexMap() { this->Indices = 0; } virtual ~FTCharToGlyphIndexMap() { if(this->Indices) { // Free all buckets this->clear(); // Free main structure delete [] this->Indices; this->Indices = 0; } } void clear() { if(this->Indices) { for(int i = 0; i < FTCharToGlyphIndexMap::NumberOfBuckets; i++) { if(this->Indices[i]) { delete [] this->Indices[i]; this->Indices[i] = 0; } } } } /*const */GlyphIndex find(CharacterCode c) { if(!this->Indices) { return 0; } // Find position of char code in buckets div_t pos = div(c, FTCharToGlyphIndexMap::BucketSize); if(!this->Indices[pos.quot]) { return 0; } const FTCharToGlyphIndexMap::GlyphIndex *ptr = &this->Indices[pos.quot][pos.rem]; if(*ptr == FTCharToGlyphIndexMap::IndexNotFound) { return 0; } return *ptr; } void insert(CharacterCode c, GlyphIndex g) { if(!this->Indices) { this->Indices = new GlyphIndex* [FTCharToGlyphIndexMap::NumberOfBuckets]; for(int i = 0; i < FTCharToGlyphIndexMap::NumberOfBuckets; i++) { this->Indices[i] = 0; } } // Find position of char code in buckets div_t pos = div(c, FTCharToGlyphIndexMap::BucketSize); // Allocate bucket if does not exist yet if(!this->Indices[pos.quot]) { this->Indices[pos.quot] = new GlyphIndex [FTCharToGlyphIndexMap::BucketSize]; for(int i = 0; i < FTCharToGlyphIndexMap::BucketSize; i++) { this->Indices[pos.quot][i] = FTCharToGlyphIndexMap::IndexNotFound; } } this->Indices[pos.quot][pos.rem] = g; } private: GlyphIndex** Indices; }; #endif // __FTCharToGlyphIndexMap__ connectome-workbench-1.2.3+git41-gc4c6c90/src/FtglFont/FTCharmap.cpp000066400000000000000000000052771300200146000246130ustar00rootroot00000000000000/* * FTGL - OpenGL font library * * Copyright (c) 2001-2004 Henry Maddocks * Copyright (c) 2008 Sam Hocevar * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #include "FtglConfig.h" #include "FTFace.h" #include "FTCharmap.h" FTCharmap::FTCharmap(FTFace* face) : ftFace(*(face->Face())), err(0) { if(!ftFace->charmap) { if(!ftFace->num_charmaps) { // This face doesn't even have one charmap! err = 0x96; // Invalid_CharMap_Format return; } err = FT_Set_Charmap(ftFace, ftFace->charmaps[0]); } ftEncoding = ftFace->charmap->encoding; for(unsigned int i = 0; i < FTCharmap::MAX_PRECOMPUTED; i++) { charIndexCache[i] = FT_Get_Char_Index(ftFace, i); } } FTCharmap::~FTCharmap() { charMap.clear(); } bool FTCharmap::CharMap(FT_Encoding encoding) { if(ftEncoding == encoding) { err = 0; return true; } err = FT_Select_Charmap(ftFace, encoding); if(!err) { ftEncoding = encoding; charMap.clear(); } return !err; } unsigned int FTCharmap::GlyphListIndex(const unsigned int characterCode) { return charMap.find(characterCode); } unsigned int FTCharmap::FontIndex(const unsigned int characterCode) { if(characterCode < FTCharmap::MAX_PRECOMPUTED) { return charIndexCache[characterCode]; } return FT_Get_Char_Index(ftFace, characterCode); } void FTCharmap::InsertIndex(const unsigned int characterCode, const size_t containerIndex) { charMap.insert(characterCode, static_cast(containerIndex)); } connectome-workbench-1.2.3+git41-gc4c6c90/src/FtglFont/FTCharmap.h000066400000000000000000000122031300200146000242430ustar00rootroot00000000000000/* * FTGL - OpenGL font library * * Copyright (c) 2001-2004 Henry Maddocks * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #ifndef __FTCharmap__ #define __FTCharmap__ #include #include FT_FREETYPE_H #include FT_GLYPH_H #include "FTGL/ftgl.h" #include "FTCharToGlyphIndexMap.h" /** * FTCharmap takes care of specifying the encoding for a font and mapping * character codes to glyph indices. * * It doesn't preprocess all indices, only on an as needed basis. This may * seem like a performance penalty but it is quicker than using the 'raw' * freetype calls and will save significant amounts of memory when dealing * with unicode encoding * * @see "Freetype 2 Documentation" * */ class FTFace; class FTCharmap { public: /** * Constructor */ FTCharmap(FTFace* face); /** * Destructor */ virtual ~FTCharmap(); /** * Queries for the current character map code. * * @return The current character map code. */ FT_Encoding Encoding() const { return ftEncoding; } /** * Sets the character map for the face. If an error occurs the object is not modified. * Valid encodings as at Freetype 2.0.4 * ft_encoding_none * ft_encoding_symbol * ft_encoding_unicode * ft_encoding_latin_2 * ft_encoding_sjis * ft_encoding_gb2312 * ft_encoding_big5 * ft_encoding_wansung * ft_encoding_johab * ft_encoding_adobe_standard * ft_encoding_adobe_expert * ft_encoding_adobe_custom * ft_encoding_apple_roman * * @param encoding the Freetype encoding symbol. See above. * @return true if charmap was valid and set * correctly. */ bool CharMap(FT_Encoding encoding); /** * Get the FTGlyphContainer index of the input character. * * @param characterCode The character code of the requested glyph in * the current encoding eg apple roman. * @return The FTGlyphContainer index for the character or zero * if it wasn't found */ unsigned int GlyphListIndex(const unsigned int characterCode); /** * Get the font glyph index of the input character. * * @param characterCode The character code of the requested glyph in * the current encoding eg apple roman. * @return The glyph index for the character. */ unsigned int FontIndex(const unsigned int characterCode); /** * Set the FTGlyphContainer index of the character code. * * @param characterCode The character code of the requested glyph in * the current encoding eg apple roman. * @param containerIndex The index into the FTGlyphContainer of the * character code. */ void InsertIndex(const unsigned int characterCode, const size_t containerIndex); /** * Queries for errors. * * @return The current error code. Zero means no error. */ FT_Error Error() const { return err; } private: /** * Current character map code. */ FT_Encoding ftEncoding; /** * The current Freetype face. */ const FT_Face ftFace; /** * A structure that maps glyph indices to character codes * * < character code, face glyph index> */ typedef FTCharToGlyphIndexMap CharacterMap; CharacterMap charMap; /** * Precomputed font indices. */ static const unsigned int MAX_PRECOMPUTED = 128; unsigned int charIndexCache[MAX_PRECOMPUTED]; /** * Current error code. */ FT_Error err; }; #endif // __FTCharmap__ connectome-workbench-1.2.3+git41-gc4c6c90/src/FtglFont/FTContour.cpp000066400000000000000000000163611300200146000246650ustar00rootroot00000000000000/* * FTGL - OpenGL font library * * Copyright (c) 2001-2004 Henry Maddocks * Copyright (c) 2008 Sam Hocevar * Copyright (c) 2008 Éric Beets * Copyright (c) 2014 Washington University School of Medicine * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #include "FtglConfig.h" #include "FTContour.h" #include static const unsigned int BEZIER_STEPS = 5; void FTContour::AddPoint(FTPoint point) { if(pointList.empty() || (point != pointList[pointList.size() - 1] && point != pointList[0])) { pointList.push_back(point); } } void FTContour::AddOutsetPoint(FTPoint point) { outsetPointList.push_back(point); } void FTContour::AddFrontPoint(FTPoint point) { frontPointList.push_back(point); } void FTContour::AddBackPoint(FTPoint point) { backPointList.push_back(point); } void FTContour::evaluateQuadraticCurve(FTPoint A, FTPoint B, FTPoint C) { for(unsigned int i = 1; i < BEZIER_STEPS; i++) { float t = static_cast(i) / BEZIER_STEPS; FTPoint U = (1.0f - t) * A + t * B; FTPoint V = (1.0f - t) * B + t * C; AddPoint((1.0f - t) * U + t * V); } } void FTContour::evaluateCubicCurve(FTPoint A, FTPoint B, FTPoint C, FTPoint D) { for(unsigned int i = 0; i < BEZIER_STEPS; i++) { float t = static_cast(i) / BEZIER_STEPS; FTPoint U = (1.0f - t) * A + t * B; FTPoint V = (1.0f - t) * B + t * C; FTPoint W = (1.0f - t) * C + t * D; FTPoint M = (1.0f - t) * U + t * V; FTPoint N = (1.0f - t) * V + t * W; AddPoint((1.0f - t) * M + t * N); } } // This function is a bit tricky. Given a path ABC, it returns the // coordinates of the outset point facing B on the left at a distance // of 64.0. // M // - - - - - - X // ^ / ' // | 64.0 / ' // X---->-----X ==> X--v-------X ' // A B \ A B \ .>' // \ \<' 64.0 // \ \ . // \ \ . // C X C X // FTPoint FTContour::ComputeOutsetPoint(FTPoint A, FTPoint B, FTPoint C) { /* Build the rotation matrix from 'ba' vector */ FTPoint ba = (A - B).Normalise(); FTPoint bc = C - B; /* Rotate bc to the left */ FTPoint tmp(bc.X() * -ba.X() + bc.Y() * -ba.Y(), bc.X() * ba.Y() + bc.Y() * -ba.X()); /* Compute the vector bisecting 'abc' */ FTGL_DOUBLE norm = sqrt(tmp.X() * tmp.X() + tmp.Y() * tmp.Y()); FTGL_DOUBLE dist = 64.0 * sqrt((norm - tmp.X()) / (norm + tmp.X())); tmp.X(tmp.Y() < 0.0 ? dist : -dist); tmp.Y(64.0); /* Rotate the new bc to the right */ return FTPoint(tmp.X() * -ba.X() + tmp.Y() * ba.Y(), tmp.X() * -ba.Y() + tmp.Y() * -ba.X()); } void FTContour::SetParity(int parity) { size_t size = PointCount(); FTPoint vOutset; if(((parity & 1) && clockwise) || (!(parity & 1) && !clockwise)) { // Contour orientation is wrong! We must reverse all points. // FIXME: could it be worth writing FTVector::reverse() for this? for(size_t i = 0; i < size / 2; i++) { FTPoint tmp = pointList[i]; pointList[i] = pointList[size - 1 - i]; pointList[size - 1 -i] = tmp; } clockwise = !clockwise; } for(size_t i = 0; i < size; i++) { size_t prev, cur, next; prev = (i + size - 1) % size; cur = i; next = (i + size + 1) % size; vOutset = ComputeOutsetPoint(Point(prev), Point(cur), Point(next)); AddOutsetPoint(vOutset); } } FTContour::FTContour(FT_Vector* contour, char* tags, unsigned int n) { FTPoint prev, cur(contour[(n - 1) % n]), next(contour[0]); FTPoint a;//, b = next - cur; double olddir, dir = atan2((next - cur).Y(), (next - cur).X()); double angle = 0.0; // See http://freetype.sourceforge.net/freetype2/docs/glyphs/glyphs-6.html // for a full description of FreeType tags. for(unsigned int i = 0; i < n; i++) { prev = cur; cur = next; next = FTPoint(contour[(i + 1) % n]); olddir = dir; dir = atan2((next - cur).Y(), (next - cur).X()); // Compute our path's new direction. double t = dir - olddir; if(t < -M_PI) t += 2 * M_PI; if(t > M_PI) t -= 2 * M_PI; angle += t; // Only process point tags we know. if(n < 2 || FT_CURVE_TAG(tags[i]) == FT_Curve_Tag_On) { AddPoint(cur); } else if(FT_CURVE_TAG(tags[i]) == FT_Curve_Tag_Conic) { FTPoint prev2 = prev, next2 = next; // Previous point is either the real previous point (an "on" // point), or the midpoint between the current one and the // previous "conic off" point. if(FT_CURVE_TAG(tags[(i - 1 + n) % n]) == FT_Curve_Tag_Conic) { prev2 = (cur + prev) * 0.5; AddPoint(prev2); } // Next point is either the real next point or the midpoint. if(FT_CURVE_TAG(tags[(i + 1) % n]) == FT_Curve_Tag_Conic) { next2 = (cur + next) * 0.5; } evaluateQuadraticCurve(prev2, cur, next2); } else if(FT_CURVE_TAG(tags[i]) == FT_Curve_Tag_Cubic && FT_CURVE_TAG(tags[(i + 1) % n]) == FT_Curve_Tag_Cubic) { evaluateCubicCurve(prev, cur, next, FTPoint(contour[(i + 2) % n])); } } // If final angle is positive (+2PI), it's an anti-clockwise contour, // otherwise (-2PI) it's clockwise. clockwise = (angle < 0.0); } void FTContour::buildFrontOutset(float outset) { for(size_t i = 0; i < PointCount(); ++i) { AddFrontPoint(Point(i) + Outset(i) * outset); } } void FTContour::buildBackOutset(float outset) { for(size_t i = 0; i < PointCount(); ++i) { AddBackPoint(Point(i) + Outset(i) * outset); } } connectome-workbench-1.2.3+git41-gc4c6c90/src/FtglFont/FTContour.h000066400000000000000000000143151300200146000243270ustar00rootroot00000000000000/* * FTGL - OpenGL font library * * Copyright (c) 2001-2004 Henry Maddocks * Copyright (c) 2008 Éric Beets * Copyright (c) 2008 Sam Hocevar * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #ifndef __FTContour__ #define __FTContour__ #include "FTGL/ftgl.h" #include "FTVector.h" /** * FTContour class is a container of points that describe a vector font * outline. It is used as a container for the output of the bezier curve * evaluator in FTVectoriser. * * @see FTOutlineGlyph * @see FTPolygonGlyph * @see FTPoint */ class FTContour { public: /** * Constructor * * @param contour * @param pointTags * @param numberOfPoints */ FTContour(FT_Vector* contour, char* pointTags, unsigned int numberOfPoints); /** * Destructor */ ~FTContour() { pointList.clear(); outsetPointList.clear(); frontPointList.clear(); backPointList.clear(); } /** * Return a point at index. * * @param index of the point in the curve. * @return const point reference */ const FTPoint& Point(size_t index) const { return pointList[index]; } /** * Return a point at index. * * @param index of the point in the outset curve. * @return const point reference */ const FTPoint& Outset(size_t index) const { return outsetPointList[index]; } /** * Return a point at index of the front outset contour. * * @param index of the point in the curve. * @return const point reference */ const FTPoint& FrontPoint(size_t index) const { if(frontPointList.size() == 0) return Point(index); return frontPointList[index]; } /** * Return a point at index of the back outset contour. * * @param index of the point in the curve. * @return const point reference */ const FTPoint& BackPoint(size_t index) const { if(backPointList.size() == 0) return Point(index); return backPointList[index]; } /** * How many points define this contour * * @return the number of points in this contour */ size_t PointCount() const { return pointList.size(); } /** * Make sure the glyph has the proper parity and create the front/back * outset contour. * * @param parity The contour's parity within the glyph. */ void SetParity(int parity); // FIXME: this should probably go away. void buildFrontOutset(float outset); void buildBackOutset(float outset); private: /** * Add a point to this contour. This function tests for duplicate * points. * * @param point The point to be added to the contour. */ inline void AddPoint(FTPoint point); /** * Add a point to this contour. This function tests for duplicate * points. * * @param point The point to be added to the contour. */ inline void AddOutsetPoint(FTPoint point); /* * Add a point to this outset contour. This function tests for duplicate * points. * * @param point The point to be added to the contour outset. */ inline void AddFrontPoint(FTPoint point); inline void AddBackPoint(FTPoint point); /** * De Casteljau (bezier) algorithm contributed by Jed Soane * Evaluates a quadratic or conic (second degree) curve */ inline void evaluateQuadraticCurve(FTPoint, FTPoint, FTPoint); /** * De Casteljau (bezier) algorithm contributed by Jed Soane * Evaluates a cubic (third degree) curve */ inline void evaluateCubicCurve(FTPoint, FTPoint, FTPoint, FTPoint); /** * Compute the vector norm */ inline FTGL_DOUBLE NormVector(const FTPoint &v); /** * Compute a rotation matrix from a vector */ inline void RotationMatrix(const FTPoint &a, const FTPoint &b, FTGL_DOUBLE *matRot, FTGL_DOUBLE *invRot); /** * Matrix and vector multiplication */ inline void MultMatrixVect(FTGL_DOUBLE *mat, FTPoint &v); /** * Compute the vector bisecting from a vector 'v' and a distance 'd' */ inline void ComputeBisec(FTPoint &v); /** * Compute the outset point coordinates */ inline FTPoint ComputeOutsetPoint(FTPoint a, FTPoint b, FTPoint c); /** * The list of points in this contour */ typedef FTVector PointVector; PointVector pointList; PointVector outsetPointList; PointVector frontPointList; PointVector backPointList; /** * Is this contour clockwise or anti-clockwise? */ bool clockwise; }; #endif // __FTContour__ connectome-workbench-1.2.3+git41-gc4c6c90/src/FtglFont/FTFace.cpp000066400000000000000000000133761300200146000240750ustar00rootroot00000000000000/* * FTGL - OpenGL font library * * Copyright (c) 2001-2004 Henry Maddocks * Copyright (c) 2008 Sam Hocevar * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #include "FtglConfig.h" #include "FTFace.h" #include "FTLibrary.h" #include FT_TRUETYPE_TABLES_H FTFace::FTFace(const char* fontFilePath, bool precomputeKerning) : numGlyphs(0), fontEncodingList(0), kerningCache(0), err(0) { const FT_Long DEFAULT_FACE_INDEX = 0; ftFace = new FT_Face; err = FT_New_Face(*FTLibrary::Instance().GetLibrary(), fontFilePath, DEFAULT_FACE_INDEX, ftFace); if(err) { delete ftFace; ftFace = 0; return; } numGlyphs = (*ftFace)->num_glyphs; hasKerningTable = (FT_HAS_KERNING((*ftFace)) != 0); if(hasKerningTable && precomputeKerning) { BuildKerningCache(); } } FTFace::FTFace(const unsigned char *pBufferBytes, size_t bufferSizeInBytes, bool precomputeKerning) : numGlyphs(0), fontEncodingList(0), kerningCache(0), err(0) { const FT_Long DEFAULT_FACE_INDEX = 0; ftFace = new FT_Face; err = FT_New_Memory_Face(*FTLibrary::Instance().GetLibrary(), (FT_Byte const *)pBufferBytes, (FT_Long)bufferSizeInBytes, DEFAULT_FACE_INDEX, ftFace); if(err) { delete ftFace; ftFace = 0; return; } numGlyphs = (*ftFace)->num_glyphs; hasKerningTable = (FT_HAS_KERNING((*ftFace)) != 0); if(hasKerningTable && precomputeKerning) { BuildKerningCache(); } } FTFace::~FTFace() { if(kerningCache) { delete[] kerningCache; } if(ftFace) { FT_Done_Face(*ftFace); delete ftFace; ftFace = 0; } } bool FTFace::Attach(const char* fontFilePath) { err = FT_Attach_File(*ftFace, fontFilePath); return !err; } bool FTFace::Attach(const unsigned char *pBufferBytes, size_t bufferSizeInBytes) { FT_Open_Args open; open.flags = FT_OPEN_MEMORY; open.memory_base = (FT_Byte const *)pBufferBytes; open.memory_size = (FT_Long)bufferSizeInBytes; err = FT_Attach_Stream(*ftFace, &open); return !err; } const FTSize& FTFace::Size(const unsigned int size, const unsigned int res) { charSize.CharSize(ftFace, size, res, res); err = charSize.Error(); return charSize; } unsigned int FTFace::CharMapCount() const { return (*ftFace)->num_charmaps; } FT_Encoding* FTFace::CharMapList() { if(0 == fontEncodingList) { fontEncodingList = new FT_Encoding[CharMapCount()]; for(size_t i = 0; i < CharMapCount(); ++i) { fontEncodingList[i] = (*ftFace)->charmaps[i]->encoding; } } return fontEncodingList; } FTPoint FTFace::KernAdvance(unsigned int index1, unsigned int index2) { float x, y; if(!hasKerningTable || !index1 || !index2) { return FTPoint(0.0f, 0.0f); } if(kerningCache && index1 < FTFace::MAX_PRECOMPUTED && index2 < FTFace::MAX_PRECOMPUTED) { x = kerningCache[2 * (index2 * FTFace::MAX_PRECOMPUTED + index1)]; y = kerningCache[2 * (index2 * FTFace::MAX_PRECOMPUTED + index1) + 1]; return FTPoint(x, y); } FT_Vector kernAdvance; kernAdvance.x = kernAdvance.y = 0; err = FT_Get_Kerning(*ftFace, index1, index2, ft_kerning_unfitted, &kernAdvance); if(err) { return FTPoint(0.0f, 0.0f); } x = static_cast(kernAdvance.x) / 64.0f; y = static_cast(kernAdvance.y) / 64.0f; return FTPoint(x, y); } FT_GlyphSlot FTFace::Glyph(unsigned int index, FT_Int load_flags) { err = FT_Load_Glyph(*ftFace, index, load_flags); if(err) { return NULL; } return (*ftFace)->glyph; } void FTFace::BuildKerningCache() { FT_Vector kernAdvance; kernAdvance.x = 0; kernAdvance.y = 0; kerningCache = new float[FTFace::MAX_PRECOMPUTED * FTFace::MAX_PRECOMPUTED * 2]; for(unsigned int j = 0; j < FTFace::MAX_PRECOMPUTED; j++) { for(unsigned int i = 0; i < FTFace::MAX_PRECOMPUTED; i++) { err = FT_Get_Kerning(*ftFace, i, j, ft_kerning_unfitted, &kernAdvance); if(err) { delete[] kerningCache; kerningCache = NULL; return; } kerningCache[2 * (j * FTFace::MAX_PRECOMPUTED + i)] = static_cast(kernAdvance.x) / 64.0f; kerningCache[2 * (j * FTFace::MAX_PRECOMPUTED + i) + 1] = static_cast(kernAdvance.y) / 64.0f; } } } connectome-workbench-1.2.3+git41-gc4c6c90/src/FtglFont/FTFace.h000066400000000000000000000122351300200146000235330ustar00rootroot00000000000000/* * FTGL - OpenGL font library * * Copyright (c) 2001-2004 Henry Maddocks * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #ifndef __FTFace__ #define __FTFace__ #include #include FT_FREETYPE_H #include FT_GLYPH_H #include "FTGL/ftgl.h" #include "FTSize.h" /** * FTFace class provides an abstraction layer for the Freetype Face. * * @see "Freetype 2 Documentation" * */ class FTFace { public: /** * Opens and reads a face file. Error is set. * * @param fontFilePath font file path. */ FTFace(const char* fontFilePath, bool precomputeKerning = true); /** * Read face data from an in-memory buffer. Error is set. * * @param pBufferBytes the in-memory buffer * @param bufferSizeInBytes the length of the buffer in bytes */ FTFace(const unsigned char *pBufferBytes, size_t bufferSizeInBytes, bool precomputeKerning = true); /** * Destructor * * Disposes of the current Freetype Face. */ virtual ~FTFace(); /** * Attach auxilliary file to font (e.g., font metrics). * * @param fontFilePath auxilliary font file path. * @return true if file has opened * successfully. */ bool Attach(const char* fontFilePath); /** * Attach auxilliary data to font (e.g., font metrics) from memory * * @param pBufferBytes the in-memory buffer * @param bufferSizeInBytes the length of the buffer in bytes * @return true if file has opened * successfully. */ bool Attach(const unsigned char *pBufferBytes, size_t bufferSizeInBytes); /** * Get the freetype face object.. * * @return pointer to an FT_Face. */ FT_Face* Face() const { return ftFace; } /** * Sets the char size for the current face. * * This doesn't guarantee that the size was set correctly. Clients * should check errors. * * @param size the face size in points (1/72 inch) * @param res the resolution of the target device. * @return FTSize object */ const FTSize& Size(const unsigned int size, const unsigned int res); /** * Get the number of character maps in this face. * * @return character map count. */ unsigned int CharMapCount() const; /** * Get a list of character maps in this face. * * @return pointer to the first encoding. */ FT_Encoding* CharMapList(); /** * Gets the kerning vector between two glyphs */ FTPoint KernAdvance(unsigned int index1, unsigned int index2); /** * Loads and creates a Freetype glyph. */ FT_GlyphSlot Glyph(unsigned int index, FT_Int load_flags); /** * Gets the number of glyphs in the current face. */ unsigned int GlyphCount() const { return numGlyphs; } /** * Queries for errors. * * @return The current error code. */ FT_Error Error() const { return err; } private: /** * The Freetype face */ FT_Face* ftFace; /** * The size object associated with this face */ FTSize charSize; /** * The number of glyphs in this face */ int numGlyphs; FT_Encoding* fontEncodingList; /** * This face has kerning tables */ bool hasKerningTable; /** * If this face has kerning tables, we can cache them. */ void BuildKerningCache(); static const unsigned int MAX_PRECOMPUTED = 128; float *kerningCache; /** * Current error code. Zero means no error. */ FT_Error err; }; #endif // __FTFace__ connectome-workbench-1.2.3+git41-gc4c6c90/src/FtglFont/FTFont/000077500000000000000000000000001300200146000234275ustar00rootroot00000000000000connectome-workbench-1.2.3+git41-gc4c6c90/src/FtglFont/FTFont/FTBitmapFont.cpp000066400000000000000000000060031300200146000264270ustar00rootroot00000000000000/* * FTGL - OpenGL font library * * Copyright (c) 2001-2004 Henry Maddocks * Copyright (c) 2008 Sam Hocevar * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #include "FtglConfig.h" #include "FTGL/ftgl.h" #include "FTInternals.h" #include "FTBitmapFontImpl.h" // // FTBitmapFont // FTBitmapFont::FTBitmapFont(char const *fontFilePath) : FTFont(new FTBitmapFontImpl(this, fontFilePath)) {} FTBitmapFont::FTBitmapFont(unsigned char const *pBufferBytes, size_t bufferSizeInBytes) : FTFont(new FTBitmapFontImpl(this, pBufferBytes, bufferSizeInBytes)) {} FTBitmapFont::~FTBitmapFont() {} FTGlyph* FTBitmapFont::MakeGlyph(FT_GlyphSlot ftGlyph) { return new FTBitmapGlyph(ftGlyph); } // // FTBitmapFontImpl // template inline FTPoint FTBitmapFontImpl::RenderI(const T* string, const int len, FTPoint position, FTPoint spacing, int renderMode) { // Protect GL_BLEND glPushAttrib(GL_COLOR_BUFFER_BIT); // Protect glPixelStorei() calls (also in FTBitmapGlyphImpl::RenderImpl) glPushClientAttrib(GL_CLIENT_PIXEL_STORE_BIT); glPixelStorei(GL_UNPACK_LSB_FIRST, GL_FALSE); glPixelStorei(GL_UNPACK_ALIGNMENT, 1); glDisable(GL_BLEND); FTPoint tmp = FTFontImpl::Render(string, len, position, spacing, renderMode); glPopClientAttrib(); glPopAttrib(); return tmp; } FTPoint FTBitmapFontImpl::Render(const char * string, const int len, FTPoint position, FTPoint spacing, int renderMode) { return RenderI(string, len, position, spacing, renderMode); } FTPoint FTBitmapFontImpl::Render(const wchar_t * string, const int len, FTPoint position, FTPoint spacing, int renderMode) { return RenderI(string, len, position, spacing, renderMode); } connectome-workbench-1.2.3+git41-gc4c6c90/src/FtglFont/FTFont/FTBitmapFontImpl.h000066400000000000000000000044761300200146000267320ustar00rootroot00000000000000/* * FTGL - OpenGL font library * * Copyright (c) 2001-2004 Henry Maddocks * Copyright (c) 2008 Sam Hocevar * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #ifndef __FTBitmapFontImpl__ #define __FTBitmapFontImpl__ #include "FTFontImpl.h" class FTGlyph; class FTBitmapFontImpl : public FTFontImpl { friend class FTBitmapFont; protected: FTBitmapFontImpl(FTFont *ftFont, const char* fontFilePath) : FTFontImpl(ftFont, fontFilePath) {}; FTBitmapFontImpl(FTFont *ftFont, const unsigned char *pBufferBytes, size_t bufferSizeInBytes) : FTFontImpl(ftFont, pBufferBytes, bufferSizeInBytes) {}; virtual FTPoint Render(const char *s, const int len, FTPoint position, FTPoint spacing, int renderMode); virtual FTPoint Render(const wchar_t *s, const int len, FTPoint position, FTPoint spacing, int renderMode); private: /* Internal generic Render() implementation */ template inline FTPoint RenderI(const T *s, const int len, FTPoint position, FTPoint spacing, int mode); }; #endif // __FTBitmapFontImpl__ connectome-workbench-1.2.3+git41-gc4c6c90/src/FtglFont/FTFont/FTBufferFont.cpp000066400000000000000000000226551300200146000264370ustar00rootroot00000000000000/* * FTGL - OpenGL font library * * Copyright (c) 2008 Sam Hocevar * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #include "FtglConfig.h" #include #include "FTGL/ftgl.h" #include "FTInternals.h" #include "FTBufferFontImpl.h" // // FTBufferFont // FTBufferFont::FTBufferFont(char const *fontFilePath) : FTFont(new FTBufferFontImpl(this, fontFilePath)) {} FTBufferFont::FTBufferFont(unsigned char const *pBufferBytes, size_t bufferSizeInBytes) : FTFont(new FTBufferFontImpl(this, pBufferBytes, bufferSizeInBytes)) {} FTBufferFont::~FTBufferFont() {} FTGlyph* FTBufferFont::MakeGlyph(FT_GlyphSlot ftGlyph) { FTBufferFontImpl *myimpl = dynamic_cast(impl); if(!myimpl) { return NULL; } return myimpl->MakeGlyphImpl(ftGlyph); } // // FTBufferFontImpl // FTBufferFontImpl::FTBufferFontImpl(FTFont *ftFont, const char* fontFilePath) : FTFontImpl(ftFont, fontFilePath), buffer(new FTBuffer()) { load_flags = FT_LOAD_NO_HINTING | FT_LOAD_NO_BITMAP; glGenTextures(BUFFER_CACHE_SIZE, idCache); for(int i = 0; i < BUFFER_CACHE_SIZE; i++) { stringCache[i] = NULL; glBindTexture(GL_TEXTURE_2D, idCache[i]); 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); } lastString = 0; } FTBufferFontImpl::FTBufferFontImpl(FTFont *ftFont, const unsigned char *pBufferBytes, size_t bufferSizeInBytes) : FTFontImpl(ftFont, pBufferBytes, bufferSizeInBytes), buffer(new FTBuffer()) { load_flags = FT_LOAD_NO_HINTING | FT_LOAD_NO_BITMAP; glGenTextures(BUFFER_CACHE_SIZE, idCache); for(int i = 0; i < BUFFER_CACHE_SIZE; i++) { stringCache[i] = NULL; glBindTexture(GL_TEXTURE_2D, idCache[i]); 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); } lastString = 0; } FTBufferFontImpl::~FTBufferFontImpl() { glDeleteTextures(BUFFER_CACHE_SIZE, idCache); for(int i = 0; i < BUFFER_CACHE_SIZE; i++) { if(stringCache[i]) { free(stringCache[i]); } } delete buffer; } FTGlyph* FTBufferFontImpl::MakeGlyphImpl(FT_GlyphSlot ftGlyph) { return new FTBufferGlyph(ftGlyph, buffer); } bool FTBufferFontImpl::FaceSize(const unsigned int size, const unsigned int res) { for(int i = 0; i < BUFFER_CACHE_SIZE; i++) { if(stringCache[i]) { free(stringCache[i]); stringCache[i] = NULL; } } return FTFontImpl::FaceSize(size, res); } static inline GLuint NextPowerOf2(GLuint in) { in -= 1; in |= in >> 16; in |= in >> 8; in |= in >> 4; in |= in >> 2; in |= in >> 1; return in + 1; } inline int StringCompare(void const *a, char const *b, int len) { return len < 0 ? strcmp((char const *)a, b) : strncmp((char const *)a, b, len); } inline int StringCompare(void const *a, wchar_t const *b, int len) { return len < 0 ? wcscmp((wchar_t const *)a, b) : wcsncmp((wchar_t const *)a, b, len); } inline char *StringCopy(char const *s, int len) { if(len < 0) { return strdup(s); } else { #ifdef HAVE_STRNDUP return strndup(s, len); #else char *s2 = (char*)malloc(len + 1); memcpy(s2, s, len); s2[len] = 0; return s2; #endif } } inline wchar_t *StringCopy(wchar_t const *s, int len) { if(len < 0) { #if defined HAVE_WCSDUP return wcsdup(s); #else len = (int)wcslen(s); #endif } wchar_t *s2 = (wchar_t *)malloc((len + 1) * sizeof(wchar_t)); memcpy(s2, s, len * sizeof(wchar_t)); s2[len] = 0; return s2; } template inline FTPoint FTBufferFontImpl::RenderI(const T* string, const int len, FTPoint position, FTPoint spacing, int renderMode) { const float padding = 3.0f; int width, height, texWidth, texHeight; int cacheIndex = -1; bool inCache = false; // Protect blending functions, GL_BLEND and GL_TEXTURE_2D glPushAttrib(GL_COLOR_BUFFER_BIT | GL_ENABLE_BIT); // Protect glPixelStorei() calls glPushClientAttrib(GL_CLIENT_PIXEL_STORE_BIT); glEnable(GL_BLEND); glEnable(GL_TEXTURE_2D); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); // GL_ONE // Search whether the string is already in a texture we uploaded for(int n = 0; n < BUFFER_CACHE_SIZE; n++) { int i = (lastString + n + BUFFER_CACHE_SIZE) % BUFFER_CACHE_SIZE; if(stringCache[i] && !StringCompare(stringCache[i], string, len)) { cacheIndex = i; inCache = true; break; } } // If the string was not found, we need to put it in the cache and compute // its new bounding box. if(!inCache) { // FIXME: this cache is not very efficient. We should first expire // strings that are not used very often. cacheIndex = lastString; lastString = (lastString + 1) % BUFFER_CACHE_SIZE; if(stringCache[cacheIndex]) { free(stringCache[cacheIndex]); } // FIXME: only the first N bytes are copied; we want the first N chars. stringCache[cacheIndex] = StringCopy(string, len); bboxCache[cacheIndex] = BBox(string, len, FTPoint(), spacing); } FTBBox bbox = bboxCache[cacheIndex]; width = static_cast(bbox.Upper().X() - bbox.Lower().X() + padding + padding + 0.5); height = static_cast(bbox.Upper().Y() - bbox.Lower().Y() + padding + padding + 0.5); texWidth = NextPowerOf2(width); texHeight = NextPowerOf2(height); glBindTexture(GL_TEXTURE_2D, idCache[cacheIndex]); // If the string was not found, we need to render the text in a new // texture buffer, then upload it to the OpenGL layer. if(!inCache) { buffer->Size(texWidth, texHeight); buffer->Pos(FTPoint(padding, padding) - bbox.Lower()); advanceCache[cacheIndex] = FTFontImpl::Render(string, len, FTPoint(), spacing, renderMode); glBindTexture(GL_TEXTURE_2D, idCache[cacheIndex]); glPixelStorei(GL_UNPACK_LSB_FIRST, GL_FALSE); glPixelStorei(GL_UNPACK_ROW_LENGTH, 0); glPixelStorei(GL_UNPACK_ALIGNMENT, 1); /* TODO: use glTexSubImage2D later? */ glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, texWidth, texHeight, 0, GL_ALPHA, GL_UNSIGNED_BYTE, (GLvoid *)buffer->Pixels()); buffer->Size(0, 0); } FTPoint low = position + bbox.Lower(); FTPoint up = position + bbox.Upper(); glBegin(GL_QUADS); glNormal3f(0.0f, 0.0f, 1.0f); glTexCoord2f(padding / texWidth, (texHeight - height + padding) / texHeight); glVertex2f(low.Xf(), up.Yf()); glTexCoord2f(padding / texWidth, (texHeight - padding) / texHeight); glVertex2f(low.Xf(), low.Yf()); glTexCoord2f((width - padding) / texWidth, (texHeight - padding) / texHeight); glVertex2f(up.Xf(), low.Yf()); glTexCoord2f((width - padding) / texWidth, (texHeight - height + padding) / texHeight); glVertex2f(up.Xf(), up.Yf()); glEnd(); glPopClientAttrib(); glPopAttrib(); return position + advanceCache[cacheIndex]; } FTPoint FTBufferFontImpl::Render(const char * string, const int len, FTPoint position, FTPoint spacing, int renderMode) { return RenderI(string, len, position, spacing, renderMode); } FTPoint FTBufferFontImpl::Render(const wchar_t * string, const int len, FTPoint position, FTPoint spacing, int renderMode) { return RenderI(string, len, position, spacing, renderMode); } connectome-workbench-1.2.3+git41-gc4c6c90/src/FtglFont/FTFont/FTBufferFontImpl.h000066400000000000000000000054121300200146000267160ustar00rootroot00000000000000/* * FTGL - OpenGL font library * * Copyright (c) 2008 Sam Hocevar * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #ifndef __FTBufferFontImpl__ #define __FTBufferFontImpl__ #include "FTFontImpl.h" class FTGlyph; class FTBuffer; class FTBufferFontImpl : public FTFontImpl { friend class FTBufferFont; protected: FTBufferFontImpl(FTFont *ftFont, const char* fontFilePath); FTBufferFontImpl(FTFont *ftFont, const unsigned char *pBufferBytes, size_t bufferSizeInBytes); virtual ~FTBufferFontImpl(); virtual FTPoint Render(const char *s, const int len, FTPoint position, FTPoint spacing, int renderMode); virtual FTPoint Render(const wchar_t *s, const int len, FTPoint position, FTPoint spacing, int renderMode); virtual bool FaceSize(const unsigned int size, const unsigned int res); private: /** * Create an FTBufferGlyph object for the base class. */ FTGlyph* MakeGlyphImpl(FT_GlyphSlot ftGlyph); /* Internal generic Render() implementation */ template inline FTPoint RenderI(const T *s, const int len, FTPoint position, FTPoint spacing, int mode); /* Pixel buffer */ FTBuffer *buffer; static const int BUFFER_CACHE_SIZE = 16; /* Texture IDs */ GLuint idCache[BUFFER_CACHE_SIZE]; void *stringCache[BUFFER_CACHE_SIZE]; FTBBox bboxCache[BUFFER_CACHE_SIZE]; FTPoint advanceCache[BUFFER_CACHE_SIZE]; int lastString; }; #endif // __FTBufferFontImpl__ connectome-workbench-1.2.3+git41-gc4c6c90/src/FtglFont/FTFont/FTExtrudeFont.cpp000066400000000000000000000050371300200146000266410ustar00rootroot00000000000000/* * FTGL - OpenGL font library * * Copyright (c) 2001-2004 Henry Maddocks * Copyright (c) 2008 Sam Hocevar * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #include "FtglConfig.h" #include "FTGL/ftgl.h" #include "FTInternals.h" #include "FTExtrudeFontImpl.h" // // FTExtrudeFont // FTExtrudeFont::FTExtrudeFont(char const *fontFilePath) : FTFont(new FTExtrudeFontImpl(this, fontFilePath)) {} FTExtrudeFont::FTExtrudeFont(const unsigned char *pBufferBytes, size_t bufferSizeInBytes) : FTFont(new FTExtrudeFontImpl(this, pBufferBytes, bufferSizeInBytes)) {} FTExtrudeFont::~FTExtrudeFont() {} FTGlyph* FTExtrudeFont::MakeGlyph(FT_GlyphSlot ftGlyph) { FTExtrudeFontImpl *myimpl = dynamic_cast(impl); if(!myimpl) { return NULL; } return new FTExtrudeGlyph(ftGlyph, myimpl->depth, myimpl->front, myimpl->back, myimpl->useDisplayLists); } // // FTExtrudeFontImpl // FTExtrudeFontImpl::FTExtrudeFontImpl(FTFont *ftFont, const char* fontFilePath) : FTFontImpl(ftFont, fontFilePath), depth(0.0f), front(0.0f), back(0.0f) { load_flags = FT_LOAD_NO_HINTING; } FTExtrudeFontImpl::FTExtrudeFontImpl(FTFont *ftFont, const unsigned char *pBufferBytes, size_t bufferSizeInBytes) : FTFontImpl(ftFont, pBufferBytes, bufferSizeInBytes), depth(0.0f), front(0.0f), back(0.0f) { load_flags = FT_LOAD_NO_HINTING; } connectome-workbench-1.2.3+git41-gc4c6c90/src/FtglFont/FTFont/FTExtrudeFontImpl.h000066400000000000000000000051051300200146000271240ustar00rootroot00000000000000/* * FTGL - OpenGL font library * * Copyright (c) 2001-2004 Henry Maddocks * Copyright (c) 2008 Sam Hocevar * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #ifndef __FTExtrudeFontImpl__ #define __FTExtrudeFontImpl__ #include "FTFontImpl.h" class FTGlyph; class FTExtrudeFontImpl : public FTFontImpl { friend class FTExtrudeFont; protected: FTExtrudeFontImpl(FTFont *ftFont, const char* fontFilePath); FTExtrudeFontImpl(FTFont *ftFont, const unsigned char *pBufferBytes, size_t bufferSizeInBytes); /** * Set the extrusion distance for the font. * * @param d The extrusion distance. */ virtual void Depth(float d) { depth = d; } /** * Set the outset distance for the font. Only implemented by * FTOutlineFont, FTPolygonFont and FTExtrudeFont * * @param o The outset distance. */ virtual void Outset(float o) { front = back = o; } /** * Set the outset distance for the font. Only implemented by * FTExtrudeFont * * @param f The front outset distance. * @param b The back outset distance. */ virtual void Outset(float f, float b) { front = f; back = b; } private: /** * The extrusion distance for the font. */ float depth; /** * The outset distance (front and back) for the font. */ float front, back; }; #endif // __FTExtrudeFontImpl__ connectome-workbench-1.2.3+git41-gc4c6c90/src/FtglFont/FTFont/FTFont.cpp000066400000000000000000000263721300200146000253050ustar00rootroot00000000000000/* * FTGL - OpenGL font library * * Copyright (c) 2001-2004 Henry Maddocks * Copyright (c) 2008 Sam Hocevar * Copyright (c) 2014 Washington University School of Medicine * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #include "FtglConfig.h" #include "FTInternals.h" #include "FTUnicode.h" #include "FTFontImpl.h" #include "FTBitmapFontImpl.h" #include "FTExtrudeFontImpl.h" #include "FTOutlineFontImpl.h" #include "FTPixmapFontImpl.h" #include "FTPolygonFontImpl.h" #include "FTTextureFontImpl.h" #include "FTGlyphContainer.h" #include "FTFace.h" // // FTFont // FTFont::FTFont(char const *fontFilePath) { impl = new FTFontImpl(this, fontFilePath); } FTFont::FTFont(const unsigned char *pBufferBytes, size_t bufferSizeInBytes) { impl = new FTFontImpl(this, pBufferBytes, bufferSizeInBytes); } FTFont::FTFont(FTFontImpl *pImpl) { impl = pImpl; } FTFont::~FTFont() { delete impl; } bool FTFont::Attach(const char* fontFilePath) { return impl->Attach(fontFilePath); } bool FTFont::Attach(const unsigned char *pBufferBytes, size_t bufferSizeInBytes) { return impl->Attach(pBufferBytes, bufferSizeInBytes); } bool FTFont::FaceSize(const unsigned int size, const unsigned int res) { return impl->FaceSize(size, res); } unsigned int FTFont::FaceSize() const { return impl->FaceSize(); } void FTFont::Depth(float depth) { return impl->Depth(depth); } void FTFont::Outset(float outset) { return impl->Outset(outset); } void FTFont::Outset(float front, float back) { return impl->Outset(front, back); } void FTFont::GlyphLoadFlags(FT_Int flags) { return impl->GlyphLoadFlags(flags); } bool FTFont::CharMap(FT_Encoding encoding) { return impl->CharMap(encoding); } unsigned int FTFont::CharMapCount() const { return impl->CharMapCount(); } FT_Encoding* FTFont::CharMapList() { return impl->CharMapList(); } void FTFont::UseDisplayList(bool useList) { return impl->UseDisplayList(useList); } float FTFont::Ascender() const { return impl->Ascender(); } float FTFont::Descender() const { return impl->Descender(); } float FTFont::LineHeight() const { return impl->LineHeight(); } FTPoint FTFont::Render(const char * string, const int len, FTPoint position, FTPoint spacing, int renderMode) { return impl->Render(string, len, position, spacing, renderMode); } FTPoint FTFont::Render(const wchar_t * string, const int len, FTPoint position, FTPoint spacing, int renderMode) { return impl->Render(string, len, position, spacing, renderMode); } float FTFont::Advance(const char * string, const int len, FTPoint spacing) { return impl->Advance(string, len, spacing); } float FTFont::Advance(const wchar_t * string, const int len, FTPoint spacing) { return impl->Advance(string, len, spacing); } float FTFont::Advance(const wchar_t theChar, const wchar_t theNextChar) { return impl->glyphList->Advance(theChar, theNextChar); } FTBBox FTFont::BBox(const char *string, const int len, FTPoint position, FTPoint spacing) { return impl->BBox(string, len, position, spacing); } FTBBox FTFont::BBox(const wchar_t *string, const int len, FTPoint position, FTPoint spacing) { return impl->BBox(string, len, position, spacing); } FT_Error FTFont::Error() const { return impl->err; } // // FTFontImpl // FTFontImpl::FTFontImpl(FTFont *ftFont, char const *fontFilePath) : face(fontFilePath), useDisplayLists(true), load_flags(FT_LOAD_DEFAULT), intf(ftFont), glyphList(0) { err = face.Error(); if(err == 0) { glyphList = new FTGlyphContainer(&face); } } FTFontImpl::FTFontImpl(FTFont *ftFont, const unsigned char *pBufferBytes, size_t bufferSizeInBytes) : face(pBufferBytes, bufferSizeInBytes), useDisplayLists(true), load_flags(FT_LOAD_DEFAULT), intf(ftFont), glyphList(0) { err = face.Error(); if(err == 0) { glyphList = new FTGlyphContainer(&face); } } FTFontImpl::~FTFontImpl() { if(glyphList) { delete glyphList; } } bool FTFontImpl::Attach(const char* fontFilePath) { if(!face.Attach(fontFilePath)) { err = face.Error(); return false; } err = 0; return true; } bool FTFontImpl::Attach(const unsigned char *pBufferBytes, size_t bufferSizeInBytes) { if(!face.Attach(pBufferBytes, bufferSizeInBytes)) { err = face.Error(); return false; } err = 0; return true; } bool FTFontImpl::FaceSize(const unsigned int size, const unsigned int res) { if(glyphList != NULL) { delete glyphList; glyphList = NULL; } charSize = face.Size(size, res); err = face.Error(); if(err != 0) { return false; } glyphList = new FTGlyphContainer(&face); return true; } unsigned int FTFontImpl::FaceSize() const { return charSize.CharSize(); } void FTFontImpl::Depth(float /*depth*/) { ; } void FTFontImpl::Outset(float /*outset*/) { ; } void FTFontImpl::Outset(float /*front*/, float /*back*/) { ; } void FTFontImpl::GlyphLoadFlags(FT_Int flags) { load_flags = flags; } bool FTFontImpl::CharMap(FT_Encoding encoding) { bool result = glyphList->CharMap(encoding); err = glyphList->Error(); return result; } unsigned int FTFontImpl::CharMapCount() const { return face.CharMapCount(); } FT_Encoding* FTFontImpl::CharMapList() { return face.CharMapList(); } void FTFontImpl::UseDisplayList(bool useList) { useDisplayLists = useList; } float FTFontImpl::Ascender() const { return charSize.Ascender(); } float FTFontImpl::Descender() const { return charSize.Descender(); } float FTFontImpl::LineHeight() const { return charSize.Height(); } template inline FTBBox FTFontImpl::BBoxI(const T* string, const int len, FTPoint position, FTPoint spacing) { FTBBox totalBBox; /* Only compute the bounds if string is non-empty. */ if(string && ('\0' != string[0])) { // for multibyte - we can't rely on sizeof(T) == character FTUnicodeStringItr ustr(string); unsigned int thisChar = *ustr++; unsigned int nextChar = *ustr; if(CheckGlyph(thisChar)) { totalBBox = glyphList->BBox(thisChar); totalBBox += position; position += FTPoint(glyphList->Advance(thisChar, nextChar), 0.0); } /* Expand totalBox by each glyph in string */ for(int i = 1; (len < 0 && *ustr) || (len >= 0 && i < len); i++) { thisChar = *ustr++; nextChar = *ustr; if(CheckGlyph(thisChar)) { position += spacing; FTBBox tempBBox = glyphList->BBox(thisChar); tempBBox += position; totalBBox |= tempBBox; position += FTPoint(glyphList->Advance(thisChar, nextChar), 0.0); } } } return totalBBox; } FTBBox FTFontImpl::BBox(const char *string, const int len, FTPoint position, FTPoint spacing) { /* The chars need to be unsigned because they are cast to int later */ return BBoxI((const unsigned char *)string, len, position, spacing); } FTBBox FTFontImpl::BBox(const wchar_t *string, const int len, FTPoint position, FTPoint spacing) { return BBoxI(string, len, position, spacing); } template inline float FTFontImpl::AdvanceI(const T* string, const int len, FTPoint spacing) { float advance = 0.0f; FTUnicodeStringItr ustr(string); for(int i = 0; (len < 0 && *ustr) || (len >= 0 && i < len); i++) { unsigned int thisChar = *ustr++; unsigned int nextChar = *ustr; if(CheckGlyph(thisChar)) { advance += glyphList->Advance(thisChar, nextChar); } if(nextChar) { advance += spacing.Xf(); } } return advance; } float FTFontImpl::Advance(const char* string, const int len, FTPoint spacing) { /* The chars need to be unsigned because they are cast to int later */ return AdvanceI((const unsigned char *)string, len, spacing); } float FTFontImpl::Advance(const wchar_t* string, const int len, FTPoint spacing) { return AdvanceI(string, len, spacing); } template inline FTPoint FTFontImpl::RenderI(const T* string, const int len, FTPoint position, FTPoint spacing, int renderMode) { // for multibyte - we can't rely on sizeof(T) == character FTUnicodeStringItr ustr(string); for(int i = 0; (len < 0 && *ustr) || (len >= 0 && i < len); i++) { unsigned int thisChar = *ustr++; unsigned int nextChar = *ustr; if(CheckGlyph(thisChar)) { position += glyphList->Render(thisChar, nextChar, position, renderMode); } if(nextChar) { position += spacing; } } return position; } FTPoint FTFontImpl::Render(const char * string, const int len, FTPoint position, FTPoint spacing, int renderMode) { return RenderI((const unsigned char *)string, len, position, spacing, renderMode); } FTPoint FTFontImpl::Render(const wchar_t * string, const int len, FTPoint position, FTPoint spacing, int renderMode) { return RenderI(string, len, position, spacing, renderMode); } bool FTFontImpl::CheckGlyph(const unsigned int characterCode) { if(glyphList->Glyph(characterCode)) { return true; } unsigned int glyphIndex = glyphList->FontIndex(characterCode); FT_GlyphSlot ftSlot = face.Glyph(glyphIndex, load_flags); if(!ftSlot) { err = face.Error(); return false; } FTGlyph* tempGlyph = intf->MakeGlyph(ftSlot); if(!tempGlyph) { if(0 == err) { err = 0x13; } return false; } glyphList->Add(tempGlyph, characterCode); return true; } connectome-workbench-1.2.3+git41-gc4c6c90/src/FtglFont/FTFont/FTFontGlue.cpp000066400000000000000000000170141300200146000261130ustar00rootroot00000000000000/* * FTGL - OpenGL font library * * Copyright (c) 2001-2004 Henry Maddocks * Copyright (c) 2008 Éric Beets * Copyright (c) 2008 Sam Hocevar * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #include "FtglConfig.h" #include "FTInternals.h" static const FTPoint static_ftpoint; static const FTBBox static_ftbbox; FTGL_BEGIN_C_DECLS #define C_TOR(cname, cargs, cxxname, cxxarg, cxxtype) \ FTGLfont* cname cargs \ { \ cxxname *f = new cxxname cxxarg; \ if(f->Error()) \ { \ delete f; \ return NULL; \ } \ FTGLfont *ftgl = (FTGLfont *)malloc(sizeof(FTGLfont)); \ ftgl->ptr = f; \ ftgl->type = cxxtype; \ return ftgl; \ } // FTBitmapFont::FTBitmapFont(); C_TOR(ftglCreateBitmapFont, (const char *fontname), FTBitmapFont, (fontname), FONT_BITMAP); // FTBufferFont::FTBufferFont(); C_TOR(ftglCreateBufferFont, (const char *fontname), FTBufferFont, (fontname), FONT_BUFFER); // FTExtrudeFont::FTExtrudeFont(); C_TOR(ftglCreateExtrudeFont, (const char *fontname), FTExtrudeFont, (fontname), FONT_EXTRUDE); // FTOutlineFont::FTOutlineFont(); C_TOR(ftglCreateOutlineFont, (const char *fontname), FTOutlineFont, (fontname), FONT_OUTLINE); // FTPixmapFont::FTPixmapFont(); C_TOR(ftglCreatePixmapFont, (const char *fontname), FTPixmapFont, (fontname), FONT_PIXMAP); // FTPolygonFont::FTPolygonFont(); C_TOR(ftglCreatePolygonFont, (const char *fontname), FTPolygonFont, (fontname), FONT_POLYGON); // FTTextureFont::FTTextureFont(); C_TOR(ftglCreateTextureFont, (const char *fontname), FTTextureFont, (fontname), FONT_TEXTURE); // FTCustomFont::FTCustomFont(); class FTCustomFont : public FTFont { public: FTCustomFont(char const *fontFilePath, void *p, FTGLglyph * (*makeglyph) (FT_GlyphSlot, void *)) : FTFont(fontFilePath), data(p), makeglyphCallback(makeglyph) {} ~FTCustomFont() {} FTGlyph* MakeGlyph(FT_GlyphSlot slot) { FTGLglyph *g = makeglyphCallback(slot, data); FTGlyph *glyph = g->ptr; // XXX: we no longer need g, and no one will free it for us. Not // very elegant, and we need to make sure no one else will try to // use it. free(g); return glyph; } private: void *data; FTGLglyph *(*makeglyphCallback) (FT_GlyphSlot, void *); }; C_TOR(ftglCreateCustomFont, (char const *fontFilePath, void *data, FTGLglyph * (*makeglyphCallback) (FT_GlyphSlot, void *)), FTCustomFont, (fontFilePath, data, makeglyphCallback), FONT_CUSTOM); #define C_FUN(cret, cname, cargs, cxxerr, cxxname, cxxarg) \ cret cname cargs \ { \ if(!f || !f->ptr) \ { \ fprintf(stderr, "FTGL warning: NULL pointer in %s\n", #cname); \ cxxerr; \ } \ return f->ptr->cxxname cxxarg; \ } // FTFont::~FTFont(); void ftglDestroyFont(FTGLfont *f) { if(!f || !f->ptr) { fprintf(stderr, "FTGL warning: NULL pointer in %s\n", __FUNCTION__); return; } delete f->ptr; free(f); } // bool FTFont::Attach(const char* fontFilePath); C_FUN(int, ftglAttachFile, (FTGLfont *f, const char* path), return 0, Attach, (path)); // bool FTFont::Attach(const unsigned char *pBufferBytes, // size_t bufferSizeInBytes); C_FUN(int, ftglAttachData, (FTGLfont *f, const unsigned char *p, size_t s), return 0, Attach, (p, s)); // void FTFont::GlyphLoadFlags(FT_Int flags); C_FUN(void, ftglSetFontGlyphLoadFlags, (FTGLfont *f, FT_Int flags), return, GlyphLoadFlags, (flags)); // bool FTFont::CharMap(FT_Encoding encoding); C_FUN(int, ftglSetFontCharMap, (FTGLfont *f, FT_Encoding enc), return 0, CharMap, (enc)); // unsigned int FTFont::CharMapCount(); C_FUN(unsigned int, ftglGetFontCharMapCount, (FTGLfont *f), return 0, CharMapCount, ()); // FT_Encoding* FTFont::CharMapList(); C_FUN(FT_Encoding *, ftglGetFontCharMapList, (FTGLfont* f), return NULL, CharMapList, ()); // virtual bool FTFont::FaceSize(const unsigned int size, // const unsigned int res = 72); C_FUN(int, ftglSetFontFaceSize, (FTGLfont *f, unsigned int s, unsigned int r), return 0, FaceSize, (s, r > 0 ? r : 72)); // unsigned int FTFont::FaceSize() const; // XXX: need to call FaceSize() as FTFont::FaceSize() because of FTGLTexture C_FUN(unsigned int, ftglGetFontFaceSize, (FTGLfont *f), return 0, FTFont::FaceSize, ()); // virtual void FTFont::Depth(float depth); C_FUN(void, ftglSetFontDepth, (FTGLfont *f, float d), return, Depth, (d)); // virtual void FTFont::Outset(float front, float back); C_FUN(void, ftglSetFontOutset, (FTGLfont *f, float front, float back), return, FTFont::Outset, (front, back)); // void FTFont::UseDisplayList(bool useList); C_FUN(void, ftglSetFontDisplayList, (FTGLfont *f, int l), return, UseDisplayList, (l != 0)); // float FTFont::Ascender() const; C_FUN(float, ftglGetFontAscender, (FTGLfont *f), return 0.f, Ascender, ()); // float FTFont::Descender() const; C_FUN(float, ftglGetFontDescender, (FTGLfont *f), return 0.f, Descender, ()); // float FTFont::LineHeight() const; C_FUN(float, ftglGetFontLineHeight, (FTGLfont *f), return 0.f, LineHeight, ()); // void FTFont::BBox(const char* string, float& llx, float& lly, float& llz, // float& urx, float& ury, float& urz); extern "C++" { C_FUN(static FTBBox, _ftglGetFontBBox, (FTGLfont *f, char const *s, int len), return static_ftbbox, BBox, (s, len)); } void ftglGetFontBBox(FTGLfont *f, const char* s, int len, float c[6]) { FTBBox ret = _ftglGetFontBBox(f, s, len); FTPoint lower = ret.Lower(), upper = ret.Upper(); c[0] = lower.Xf(); c[1] = lower.Yf(); c[2] = lower.Zf(); c[3] = upper.Xf(); c[4] = upper.Yf(); c[5] = upper.Zf(); } // float FTFont::Advance(const char* string); C_FUN(float, ftglGetFontAdvance, (FTGLfont *f, char const *s), return 0.0, Advance, (s)); // virtual void Render(const char* string, int renderMode); extern "C++" { C_FUN(static FTPoint, _ftglRenderFont, (FTGLfont *f, char const *s, int len, FTPoint pos, FTPoint spacing, int mode), return static_ftpoint, Render, (s, len, pos, spacing, mode)); } void ftglRenderFont(FTGLfont *f, const char *s, int mode) { _ftglRenderFont(f, s, -1, FTPoint(), FTPoint(), mode); } // FT_Error FTFont::Error() const; C_FUN(FT_Error, ftglGetFontError, (FTGLfont *f), return -1, Error, ()); FTGL_END_C_DECLS connectome-workbench-1.2.3+git41-gc4c6c90/src/FtglFont/FTFont/FTFontImpl.h000066400000000000000000000113531300200146000255650ustar00rootroot00000000000000/* * FTGL - OpenGL font library * * Copyright (c) 2001-2004 Henry Maddocks * Copyright (c) 2008 Sam Hocevar * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #ifndef __FTFontImpl__ #define __FTFontImpl__ #include "FTGL/ftgl.h" #include "FTFace.h" class FTGlyphContainer; class FTGlyph; class FTFontImpl { friend class FTFont; protected: FTFontImpl(FTFont *ftFont, char const *fontFilePath); FTFontImpl(FTFont *ftFont, const unsigned char *pBufferBytes, size_t bufferSizeInBytes); virtual ~FTFontImpl(); virtual bool Attach(const char* fontFilePath); virtual bool Attach(const unsigned char *pBufferBytes, size_t bufferSizeInBytes); virtual void GlyphLoadFlags(FT_Int flags); virtual bool CharMap(FT_Encoding encoding); virtual unsigned int CharMapCount() const; virtual FT_Encoding* CharMapList(); virtual void UseDisplayList(bool useList); virtual float Ascender() const; virtual float Descender() const; virtual float LineHeight() const; virtual bool FaceSize(const unsigned int size, const unsigned int res); virtual unsigned int FaceSize() const; virtual void Depth(float depth); virtual void Outset(float outset); virtual void Outset(float front, float back); virtual FTBBox BBox(const char *s, const int len, FTPoint, FTPoint); virtual FTBBox BBox(const wchar_t *s, const int len, FTPoint, FTPoint); virtual float Advance(const char *s, const int len, FTPoint); virtual float Advance(const wchar_t *s, const int len, FTPoint); virtual FTPoint Render(const char *s, const int len, FTPoint, FTPoint, int); virtual FTPoint Render(const wchar_t *s, const int len, FTPoint, FTPoint, int); /** * Current face object */ FTFace face; /** * Current size object */ FTSize charSize; /** * Flag to enable or disable the use of Display Lists inside FTGL * true turns ON display lists. * false turns OFF display lists. */ bool useDisplayLists; /** * The default glyph loading flags. */ FT_Int load_flags; /** * Current error code. Zero means no error. */ FT_Error err; private: /** * A link back to the interface of which we are the implementation. */ FTFont *intf; /** * Check that the glyph at chr exist. If not load it. * * @param chr character index * @return true if the glyph can be created. */ bool CheckGlyph(const unsigned int chr); /** * An object that holds a list of glyphs */ FTGlyphContainer* glyphList; /** * Current pen or cursor position; */ FTPoint pen; /* Internal generic BBox() implementation */ template inline FTBBox BBoxI(const T *s, const int len, FTPoint position, FTPoint spacing); /* Internal generic Advance() implementation */ template inline float AdvanceI(const T *s, const int len, FTPoint spacing); /* Internal generic Render() implementation */ template inline FTPoint RenderI(const T *s, const int len, FTPoint position, FTPoint spacing, int mode); }; #endif // __FTFontImpl__ connectome-workbench-1.2.3+git41-gc4c6c90/src/FtglFont/FTFont/FTOutlineFont.cpp000066400000000000000000000073361300200146000266440ustar00rootroot00000000000000/* * FTGL - OpenGL font library * * Copyright (c) 2001-2004 Henry Maddocks * Copyright (c) 2008 Sam Hocevar * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #include "FtglConfig.h" #include "FTGL/ftgl.h" #include "FTInternals.h" #include "FTOutlineFontImpl.h" // // FTOutlineFont // FTOutlineFont::FTOutlineFont(char const *fontFilePath) : FTFont(new FTOutlineFontImpl(this, fontFilePath)) {} FTOutlineFont::FTOutlineFont(const unsigned char *pBufferBytes, size_t bufferSizeInBytes) : FTFont(new FTOutlineFontImpl(this, pBufferBytes, bufferSizeInBytes)) {} FTOutlineFont::~FTOutlineFont() {} FTGlyph* FTOutlineFont::MakeGlyph(FT_GlyphSlot ftGlyph) { FTOutlineFontImpl *myimpl = dynamic_cast(impl); if(!myimpl) { return NULL; } return new FTOutlineGlyph(ftGlyph, myimpl->outset, myimpl->useDisplayLists); } // // FTOutlineFontImpl // FTOutlineFontImpl::FTOutlineFontImpl(FTFont *ftFont, const char* fontFilePath) : FTFontImpl(ftFont, fontFilePath), outset(0.0f) { load_flags = FT_LOAD_NO_HINTING; } FTOutlineFontImpl::FTOutlineFontImpl(FTFont *ftFont, const unsigned char *pBufferBytes, size_t bufferSizeInBytes) : FTFontImpl(ftFont, pBufferBytes, bufferSizeInBytes), outset(0.0f) { load_flags = FT_LOAD_NO_HINTING; } template inline FTPoint FTOutlineFontImpl::RenderI(const T* string, const int len, FTPoint position, FTPoint spacing, int renderMode) { // Protect GL_TEXTURE_2D, glHint(), GL_LINE_SMOOTH and blending functions glPushAttrib(GL_ENABLE_BIT | GL_HINT_BIT | GL_LINE_BIT | GL_COLOR_BUFFER_BIT); glDisable(GL_TEXTURE_2D); glEnable(GL_LINE_SMOOTH); glHint(GL_LINE_SMOOTH_HINT, GL_DONT_CARE); glEnable(GL_BLEND); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); // GL_ONE FTPoint tmp = FTFontImpl::Render(string, len, position, spacing, renderMode); glPopAttrib(); return tmp; } FTPoint FTOutlineFontImpl::Render(const char * string, const int len, FTPoint position, FTPoint spacing, int renderMode) { return RenderI(string, len, position, spacing, renderMode); } FTPoint FTOutlineFontImpl::Render(const wchar_t * string, const int len, FTPoint position, FTPoint spacing, int renderMode) { return RenderI(string, len, position, spacing, renderMode); } connectome-workbench-1.2.3+git41-gc4c6c90/src/FtglFont/FTFont/FTOutlineFontImpl.h000066400000000000000000000050601300200146000271230ustar00rootroot00000000000000/* * FTGL - OpenGL font library * * Copyright (c) 2001-2004 Henry Maddocks * Copyright (c) 2008 Sam Hocevar * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #ifndef __FTOutlineFontImpl__ #define __FTOutlineFontImpl__ #include "FTFontImpl.h" class FTGlyph; class FTOutlineFontImpl : public FTFontImpl { friend class FTOutlineFont; protected: FTOutlineFontImpl(FTFont *ftFont, const char* fontFilePath); FTOutlineFontImpl(FTFont *ftFont, const unsigned char *pBufferBytes, size_t bufferSizeInBytes); /** * Set the outset distance for the font. Only implemented by * FTOutlineFont, FTPolygonFont and FTExtrudeFont * * @param outset The outset distance. */ virtual void Outset(float o) { outset = o; } virtual FTPoint Render(const char *s, const int len, FTPoint position, FTPoint spacing, int renderMode); virtual FTPoint Render(const wchar_t *s, const int len, FTPoint position, FTPoint spacing, int renderMode); private: /** * The outset distance for the font. */ float outset; /* Internal generic Render() implementation */ template inline FTPoint RenderI(const T *s, const int len, FTPoint position, FTPoint spacing, int mode); }; #endif // __FTOutlineFontImpl__ connectome-workbench-1.2.3+git41-gc4c6c90/src/FtglFont/FTFont/FTPixmapFont.cpp000066400000000000000000000075431300200146000264630ustar00rootroot00000000000000/* * FTGL - OpenGL font library * * Copyright (c) 2001-2004 Henry Maddocks * Copyright (c) 2008 Sam Hocevar * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #include "FtglConfig.h" #include "FTGL/ftgl.h" #include "FTInternals.h" #include "FTPixmapFontImpl.h" // // FTPixmapFont // FTPixmapFont::FTPixmapFont(char const *fontFilePath) : FTFont(new FTPixmapFontImpl(this, fontFilePath)) {} FTPixmapFont::FTPixmapFont(const unsigned char *pBufferBytes, size_t bufferSizeInBytes) : FTFont(new FTPixmapFontImpl(this, pBufferBytes, bufferSizeInBytes)) {} FTPixmapFont::~FTPixmapFont() {} FTGlyph* FTPixmapFont::MakeGlyph(FT_GlyphSlot ftGlyph) { return new FTPixmapGlyph(ftGlyph); } // // FTPixmapFontImpl // FTPixmapFontImpl::FTPixmapFontImpl(FTFont *ftFont, const char* fontFilePath) : FTFontImpl(ftFont, fontFilePath) { load_flags = FT_LOAD_NO_HINTING | FT_LOAD_NO_BITMAP; } FTPixmapFontImpl::FTPixmapFontImpl(FTFont *ftFont, const unsigned char *pBufferBytes, size_t bufferSizeInBytes) : FTFontImpl(ftFont, pBufferBytes, bufferSizeInBytes) { load_flags = FT_LOAD_NO_HINTING | FT_LOAD_NO_BITMAP; } template inline FTPoint FTPixmapFontImpl::RenderI(const T* string, const int len, FTPoint position, FTPoint spacing, int renderMode) { // Protect GL_TEXTURE_2D and GL_BLEND, glPixelTransferf(), and blending // functions. glPushAttrib(GL_ENABLE_BIT | GL_PIXEL_MODE_BIT | GL_COLOR_BUFFER_BIT); // Protect glPixelStorei() calls (made by FTPixmapGlyphImpl::RenderImpl). glPushClientAttrib(GL_CLIENT_PIXEL_STORE_BIT); glEnable(GL_BLEND); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); glDisable(GL_TEXTURE_2D); GLfloat ftglColour[4]; glGetFloatv(GL_CURRENT_RASTER_COLOR, ftglColour); glPixelTransferf(GL_RED_SCALE, ftglColour[0]); glPixelTransferf(GL_GREEN_SCALE, ftglColour[1]); glPixelTransferf(GL_BLUE_SCALE, ftglColour[2]); glPixelTransferf(GL_ALPHA_SCALE, ftglColour[3]); FTPoint tmp = FTFontImpl::Render(string, len, position, spacing, renderMode); glPopClientAttrib(); glPopAttrib(); return tmp; } FTPoint FTPixmapFontImpl::Render(const char * string, const int len, FTPoint position, FTPoint spacing, int renderMode) { return RenderI(string, len, position, spacing, renderMode); } FTPoint FTPixmapFontImpl::Render(const wchar_t * string, const int len, FTPoint position, FTPoint spacing, int renderMode) { return RenderI(string, len, position, spacing, renderMode); } connectome-workbench-1.2.3+git41-gc4c6c90/src/FtglFont/FTFont/FTPixmapFontImpl.h000066400000000000000000000043071300200146000267450ustar00rootroot00000000000000/* * FTGL - OpenGL font library * * Copyright (c) 2001-2004 Henry Maddocks * Copyright (c) 2008 Sam Hocevar * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #ifndef __FTPixmapFontImpl__ #define __FTPixmapFontImpl__ #include "FTFontImpl.h" class FTGlyph; class FTPixmapFontImpl : public FTFontImpl { friend class FTPixmapFont; protected: FTPixmapFontImpl(FTFont *ftFont, const char* fontFilePath); FTPixmapFontImpl(FTFont *ftFont, const unsigned char *pBufferBytes, size_t bufferSizeInBytes); virtual FTPoint Render(const char *s, const int len, FTPoint position, FTPoint spacing, int renderMode); virtual FTPoint Render(const wchar_t *s, const int len, FTPoint position, FTPoint spacing, int renderMode); private: /* Internal generic Render() implementation */ template inline FTPoint RenderI(const T *s, const int len, FTPoint position, FTPoint spacing, int mode); }; #endif // __FTPixmapFontImpl__ connectome-workbench-1.2.3+git41-gc4c6c90/src/FtglFont/FTFont/FTPolygonFont.cpp000066400000000000000000000047231300200146000266510ustar00rootroot00000000000000/* * FTGL - OpenGL font library * * Copyright (c) 2001-2004 Henry Maddocks * Copyright (c) 2008 Sam Hocevar * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #include "FtglConfig.h" #include "FTGL/ftgl.h" #include "FTInternals.h" #include "FTPolygonFontImpl.h" // // FTPolygonFont // FTPolygonFont::FTPolygonFont(char const *fontFilePath) : FTFont(new FTPolygonFontImpl(this, fontFilePath)) {} FTPolygonFont::FTPolygonFont(const unsigned char *pBufferBytes, size_t bufferSizeInBytes) : FTFont(new FTPolygonFontImpl(this, pBufferBytes, bufferSizeInBytes)) {} FTPolygonFont::~FTPolygonFont() {} FTGlyph* FTPolygonFont::MakeGlyph(FT_GlyphSlot ftGlyph) { FTPolygonFontImpl *myimpl = dynamic_cast(impl); if(!myimpl) { return NULL; } return new FTPolygonGlyph(ftGlyph, myimpl->outset, myimpl->useDisplayLists); } // // FTPolygonFontImpl // FTPolygonFontImpl::FTPolygonFontImpl(FTFont *ftFont, const char* fontFilePath) : FTFontImpl(ftFont, fontFilePath), outset(0.0f) { load_flags = FT_LOAD_NO_HINTING; } FTPolygonFontImpl::FTPolygonFontImpl(FTFont *ftFont, const unsigned char *pBufferBytes, size_t bufferSizeInBytes) : FTFontImpl(ftFont, pBufferBytes, bufferSizeInBytes), outset(0.0f) { load_flags = FT_LOAD_NO_HINTING; } connectome-workbench-1.2.3+git41-gc4c6c90/src/FtglFont/FTFont/FTPolygonFontImpl.h000066400000000000000000000040021300200146000271260ustar00rootroot00000000000000/* * FTGL - OpenGL font library * * Copyright (c) 2001-2004 Henry Maddocks * Copyright (c) 2008 Sam Hocevar * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #ifndef __FTPolygonFontImpl__ #define __FTPolygonFontImpl__ #include "FTFontImpl.h" class FTGlyph; class FTPolygonFontImpl : public FTFontImpl { friend class FTPolygonFont; protected: FTPolygonFontImpl(FTFont *ftFont, const char* fontFilePath); FTPolygonFontImpl(FTFont *ftFont, const unsigned char *pBufferBytes, size_t bufferSizeInBytes); /** * Set the outset distance for the font. Only implemented by * FTOutlineFont, FTPolygonFont and FTExtrudeFont * * @param depth The outset distance. */ virtual void Outset(float o) { outset = o; } private: /** * The outset distance (front and back) for the font. */ float outset; }; #endif // __FTPolygonFontImpl__ connectome-workbench-1.2.3+git41-gc4c6c90/src/FtglFont/FTFont/FTTextureFont.cpp000066400000000000000000000164531300200146000266650ustar00rootroot00000000000000/* * FTGL - OpenGL font library * * Copyright (c) 2001-2004 Henry Maddocks * Copyright (c) 2008 Sam Hocevar * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #include "FtglConfig.h" #include #include // For memset #include "FTGL/ftgl.h" #include "FTInternals.h" #include "../FTGlyph/FTTextureGlyphImpl.h" #include "./FTTextureFontImpl.h" // // FTTextureFont // FTTextureFont::FTTextureFont(char const *fontFilePath) : FTFont(new FTTextureFontImpl(this, fontFilePath)) {} FTTextureFont::FTTextureFont(const unsigned char *pBufferBytes, size_t bufferSizeInBytes) : FTFont(new FTTextureFontImpl(this, pBufferBytes, bufferSizeInBytes)) {} FTTextureFont::~FTTextureFont() {} FTGlyph* FTTextureFont::MakeGlyph(FT_GlyphSlot ftGlyph) { FTTextureFontImpl *myimpl = dynamic_cast(impl); if(!myimpl) { return NULL; } return myimpl->MakeGlyphImpl(ftGlyph); } // // FTTextureFontImpl // static inline GLuint NextPowerOf2(GLuint in) { in -= 1; in |= in >> 16; in |= in >> 8; in |= in >> 4; in |= in >> 2; in |= in >> 1; return in + 1; } FTTextureFontImpl::FTTextureFontImpl(FTFont *ftFont, const char* fontFilePath) : FTFontImpl(ftFont, fontFilePath), maximumGLTextureSize(0), textureWidth(0), textureHeight(0), glyphHeight(0), glyphWidth(0), padding(3), xOffset(0), yOffset(0) { load_flags = FT_LOAD_NO_HINTING | FT_LOAD_NO_BITMAP; remGlyphs = numGlyphs = face.GlyphCount(); } FTTextureFontImpl::FTTextureFontImpl(FTFont *ftFont, const unsigned char *pBufferBytes, size_t bufferSizeInBytes) : FTFontImpl(ftFont, pBufferBytes, bufferSizeInBytes), maximumGLTextureSize(0), textureWidth(0), textureHeight(0), glyphHeight(0), glyphWidth(0), padding(3), xOffset(0), yOffset(0) { load_flags = FT_LOAD_NO_HINTING | FT_LOAD_NO_BITMAP; remGlyphs = numGlyphs = face.GlyphCount(); } FTTextureFontImpl::~FTTextureFontImpl() { if(textureIDList.size()) { glDeleteTextures((GLsizei)textureIDList.size(), (const GLuint*)&textureIDList[0]); } } FTGlyph* FTTextureFontImpl::MakeGlyphImpl(FT_GlyphSlot ftGlyph) { glyphHeight = static_cast(charSize.Height() + 0.5); glyphWidth = static_cast(charSize.Width() + 0.5); if(glyphHeight < 1) glyphHeight = 1; if(glyphWidth < 1) glyphWidth = 1; if(textureIDList.empty()) { textureIDList.push_back(CreateTexture()); xOffset = yOffset = padding; } if(xOffset > (textureWidth - glyphWidth)) { xOffset = padding; yOffset += glyphHeight; if(yOffset > (textureHeight - glyphHeight)) { textureIDList.push_back(CreateTexture()); yOffset = padding; } } FTTextureGlyph* tempGlyph = new FTTextureGlyph(ftGlyph, textureIDList[textureIDList.size() - 1], xOffset, yOffset, textureWidth, textureHeight); xOffset += static_cast(tempGlyph->BBox().Upper().X() - tempGlyph->BBox().Lower().X() + padding + 0.5); --remGlyphs; return tempGlyph; } void FTTextureFontImpl::CalculateTextureSize() { if(!maximumGLTextureSize) { maximumGLTextureSize = 1024; glGetIntegerv(GL_MAX_TEXTURE_SIZE, (GLint*)&maximumGLTextureSize); assert(maximumGLTextureSize); // If you hit this then you have an invalid OpenGL context. } textureWidth = NextPowerOf2((remGlyphs * glyphWidth) + (padding * 2)); textureWidth = textureWidth > maximumGLTextureSize ? maximumGLTextureSize : textureWidth; int h = static_cast((textureWidth - (padding * 2)) / glyphWidth + 0.5); textureHeight = NextPowerOf2(((numGlyphs / h) + 1) * glyphHeight); textureHeight = textureHeight > maximumGLTextureSize ? maximumGLTextureSize : textureHeight; } GLuint FTTextureFontImpl::CreateTexture() { CalculateTextureSize(); int totalMemory = textureWidth * textureHeight; unsigned char* textureMemory = new unsigned char[totalMemory]; memset(textureMemory, 0, totalMemory); GLuint textID; glGenTextures(1, (GLuint*)&textID); glBindTexture(GL_TEXTURE_2D, textID); 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); glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, textureWidth, textureHeight, 0, GL_ALPHA, GL_UNSIGNED_BYTE, textureMemory); delete [] textureMemory; return textID; } bool FTTextureFontImpl::FaceSize(const unsigned int size, const unsigned int res) { if(!textureIDList.empty()) { glDeleteTextures((GLsizei)textureIDList.size(), (const GLuint*)&textureIDList[0]); textureIDList.clear(); remGlyphs = numGlyphs = face.GlyphCount(); } return FTFontImpl::FaceSize(size, res); } template inline FTPoint FTTextureFontImpl::RenderI(const T* string, const int len, FTPoint position, FTPoint spacing, int renderMode) { // Protect GL_TEXTURE_2D, GL_BLEND and blending functions glPushAttrib(GL_ENABLE_BIT | GL_COLOR_BUFFER_BIT); glEnable(GL_BLEND); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); // GL_ONE glEnable(GL_TEXTURE_2D); FTTextureGlyphImpl::ResetActiveTexture(); FTPoint tmp = FTFontImpl::Render(string, len, position, spacing, renderMode); glPopAttrib(); return tmp; } FTPoint FTTextureFontImpl::Render(const char * string, const int len, FTPoint position, FTPoint spacing, int renderMode) { return RenderI(string, len, position, spacing, renderMode); } FTPoint FTTextureFontImpl::Render(const wchar_t * string, const int len, FTPoint position, FTPoint spacing, int renderMode) { return RenderI(string, len, position, spacing, renderMode); } connectome-workbench-1.2.3+git41-gc4c6c90/src/FtglFont/FTFont/FTTextureFontImpl.h000066400000000000000000000110051300200146000271400ustar00rootroot00000000000000/* * FTGL - OpenGL font library * * Copyright (c) 2001-2004 Henry Maddocks * Copyright (c) 2008 Sam Hocevar * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #ifndef __FTTextureFontImpl__ #define __FTTextureFontImpl__ #include "FTFontImpl.h" #include "FTVector.h" class FTTextureGlyph; class FTTextureFontImpl : public FTFontImpl { friend class FTTextureFont; protected: FTTextureFontImpl(FTFont *ftFont, const char* fontFilePath); FTTextureFontImpl(FTFont *ftFont, const unsigned char *pBufferBytes, size_t bufferSizeInBytes); virtual ~FTTextureFontImpl(); /** * Set the char size for the current face. * * @param size the face size in points (1/72 inch) * @param res the resolution of the target device. * @return true if size was set correctly */ virtual bool FaceSize(const unsigned int size, const unsigned int res = 72); virtual FTPoint Render(const char *s, const int len, FTPoint position, FTPoint spacing, int renderMode); virtual FTPoint Render(const wchar_t *s, const int len, FTPoint position, FTPoint spacing, int renderMode); private: /** * Create an FTTextureGlyph object for the base class. */ FTGlyph* MakeGlyphImpl(FT_GlyphSlot ftGlyph); /** * Get the size of a block of memory required to layout the glyphs * * Calculates a width and height based on the glyph sizes and the * number of glyphs. It over estimates. */ inline void CalculateTextureSize(); /** * Creates a 'blank' OpenGL texture object. * * The format is GL_ALPHA and the params are * GL_TEXTURE_WRAP_S = GL_CLAMP * GL_TEXTURE_WRAP_T = GL_CLAMP * GL_TEXTURE_MAG_FILTER = GL_LINEAR * GL_TEXTURE_MIN_FILTER = GL_LINEAR * Note that mipmapping is NOT used */ inline GLuint CreateTexture(); /** * The maximum texture dimension on this OpenGL implemetation */ GLsizei maximumGLTextureSize; /** * The minimum texture width required to hold the glyphs */ GLsizei textureWidth; /** * The minimum texture height required to hold the glyphs */ GLsizei textureHeight; /** *An array of texture ids */ FTVector textureIDList; /** * The max height for glyphs in the current font */ int glyphHeight; /** * The max width for glyphs in the current font */ int glyphWidth; /** * A value to be added to the height and width to ensure that * glyphs don't overlap in the texture */ unsigned int padding; /** * */ unsigned int numGlyphs; /** */ unsigned int remGlyphs; /** */ int xOffset; /** */ int yOffset; /* Internal generic Render() implementation */ template inline FTPoint RenderI(const T *s, const int len, FTPoint position, FTPoint spacing, int mode); }; #endif // __FTTextureFontImpl__ connectome-workbench-1.2.3+git41-gc4c6c90/src/FtglFont/FTGL/000077500000000000000000000000001300200146000230235ustar00rootroot00000000000000connectome-workbench-1.2.3+git41-gc4c6c90/src/FtglFont/FTGL/FTBBox.h000066400000000000000000000117771300200146000242750ustar00rootroot00000000000000/* * FTGL - OpenGL font library * * Copyright (c) 2001-2004 Henry Maddocks * Copyright (c) 2008 Sam Hocevar * Copyright (c) 2008 Sean Morrison * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #ifndef __ftgl__ # warning This header is deprecated. Please use from now. # include #endif #ifndef __FTBBox__ #define __FTBBox__ #ifdef __cplusplus /** * FTBBox is a convenience class for handling bounding boxes. */ class FTGL_EXPORT FTBBox { public: /** * Default constructor. Bounding box is set to zero. */ FTBBox() : lower(0.0f, 0.0f, 0.0f), upper(0.0f, 0.0f, 0.0f) {} /** * Constructor. */ FTBBox(float lx, float ly, float lz, float ux, float uy, float uz) : lower(lx, ly, lz), upper(ux, uy, uz) {} /** * Constructor. */ FTBBox(FTPoint l, FTPoint u) : lower(l), upper(u) {} /** * Constructor. Extracts a bounding box from a freetype glyph. Uses * the control box for the glyph. FT_Glyph_Get_CBox() * * @param glyph A freetype glyph */ FTBBox(FT_GlyphSlot glyph) : lower(0.0f, 0.0f, 0.0f), upper(0.0f, 0.0f, 0.0f) { FT_BBox bbox; FT_Outline_Get_CBox(&(glyph->outline), &bbox); lower.X(static_cast(bbox.xMin) / 64.0f); lower.Y(static_cast(bbox.yMin) / 64.0f); lower.Z(0.0f); upper.X(static_cast(bbox.xMax) / 64.0f); upper.Y(static_cast(bbox.yMax) / 64.0f); upper.Z(0.0f); } /** * Destructor */ ~FTBBox() {} /** * Mark the bounds invalid by setting all lower dimensions greater * than the upper dimensions. */ void Invalidate() { lower = FTPoint(1.0f, 1.0f, 1.0f); upper = FTPoint(-1.0f, -1.0f, -1.0f); } /** * Determines if this bounding box is valid. * * @return True if all lower values are <= the corresponding * upper values. */ bool IsValid() { return lower.X() <= upper.X() && lower.Y() <= upper.Y() && lower.Z() <= upper.Z(); } /** * Move the Bounding Box by a vector. * * @param vector The vector to move the bbox in 3D space. */ FTBBox& operator += (const FTPoint vector) { lower += vector; upper += vector; return *this; } /** * Combine two bounding boxes. The result is the smallest bounding * box containing the two original boxes. * * @param bbox The bounding box to merge with the second one. */ FTBBox& operator |= (const FTBBox& bbox) { if(bbox.lower.X() < lower.X()) lower.X(bbox.lower.X()); if(bbox.lower.Y() < lower.Y()) lower.Y(bbox.lower.Y()); if(bbox.lower.Z() < lower.Z()) lower.Z(bbox.lower.Z()); if(bbox.upper.X() > upper.X()) upper.X(bbox.upper.X()); if(bbox.upper.Y() > upper.Y()) upper.Y(bbox.upper.Y()); if(bbox.upper.Z() > upper.Z()) upper.Z(bbox.upper.Z()); return *this; } void SetDepth(float depth) { if(depth > 0) upper.Z(lower.Z() + depth); else lower.Z(upper.Z() + depth); } inline FTPoint const Upper() const { return upper; } inline FTPoint const Lower() const { return lower; } private: /** * The bounds of the box */ FTPoint lower, upper; }; #endif //__cplusplus #endif // __FTBBox__ connectome-workbench-1.2.3+git41-gc4c6c90/src/FtglFont/FTGL/FTBitmapGlyph.h000066400000000000000000000047251300200146000256560ustar00rootroot00000000000000/* * FTGL - OpenGL font library * * Copyright (c) 2001-2004 Henry Maddocks * Copyright (c) 2008 Sam Hocevar * Copyright (c) 2008 Sean Morrison * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #ifndef __ftgl__ # warning This header is deprecated. Please use from now. # include #endif #ifndef __FTBitmapGlyph__ #define __FTBitmapGlyph__ #ifdef __cplusplus /** * FTBitmapGlyph is a specialisation of FTGlyph for creating bitmaps. */ class FTGL_EXPORT FTBitmapGlyph : public FTGlyph { public: /** * Constructor * * @param glyph The Freetype glyph to be processed */ FTBitmapGlyph(FT_GlyphSlot glyph); /** * Destructor */ virtual ~FTBitmapGlyph(); /** * Render this glyph at the current pen position. * * @param pen The current pen position. * @param renderMode Render mode to display * @return The advance distance for this glyph. */ virtual const FTPoint& Render(const FTPoint& pen, int renderMode); }; #endif //__cplusplus FTGL_BEGIN_C_DECLS /** * Create a specialisation of FTGLglyph for creating bitmaps. * * @param glyph The Freetype glyph to be processed * @return An FTGLglyph* object. */ FTGL_EXPORT FTGLglyph *ftglCreateBitmapGlyph(FT_GlyphSlot glyph); FTGL_END_C_DECLS #endif // __FTBitmapGlyph__ connectome-workbench-1.2.3+git41-gc4c6c90/src/FtglFont/FTGL/FTBuffer.h000066400000000000000000000063731300200146000246500ustar00rootroot00000000000000/* * FTGL - OpenGL font library * * Copyright (c) 2008 Sam Hocevar * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #ifndef __ftgl__ # warning Please use instead of . # include #endif #ifndef __FTBuffer__ #define __FTBuffer__ #ifdef __cplusplus /** * FTBuffer is a helper class for pixel buffers. * * It provides the interface between FTBufferFont and FTBufferGlyph to * optimise rendering operations. * * @see FTBufferGlyph * @see FTBufferFont */ class FTGL_EXPORT FTBuffer { public: /** * Default constructor. */ FTBuffer(); /** * Destructor */ ~FTBuffer(); /** * Get the pen's position in the buffer. * * @return The pen's position as an FTPoint object. */ inline FTPoint Pos() const { return pos; } /** * Set the pen's position in the buffer. * * @param arg An FTPoint object with the desired pen's position. */ inline void Pos(FTPoint arg) { pos = arg; } /** * Set the buffer's size. * * @param w The buffer's desired width, in pixels. * @param h The buffer's desired height, in pixels. */ void Size(int w, int h); /** * Get the buffer's width. * * @return The buffer's width, in pixels. */ inline int Width() const { return width; } /** * Get the buffer's height. * * @return The buffer's height, in pixels. */ inline int Height() const { return height; } /** * Get the buffer's direct pixel buffer. * * @return A read-write pointer to the buffer's pixels. */ inline unsigned char *Pixels() const { return pixels; } private: /** * Buffer's width and height. */ int width, height; /** * Buffer's pixel buffer. */ unsigned char *pixels; /** * Buffer's internal pen position. */ FTPoint pos; }; #endif //__cplusplus #endif // __FTBuffer__ connectome-workbench-1.2.3+git41-gc4c6c90/src/FtglFont/FTGL/FTBufferFont.h000066400000000000000000000056341300200146000254760ustar00rootroot00000000000000/* * FTGL - OpenGL font library * * Copyright (c) 2008 Sam Hocevar * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #ifndef __ftgl__ # warning Please use instead of . # include #endif #ifndef __FTBufferFont__ #define __FTBufferFont__ #ifdef __cplusplus /** * FTBufferFont is a specialisation of the FTFont class for handling * memory buffer fonts. * * @see FTFont */ class FTGL_EXPORT FTBufferFont : public FTFont { public: /** * Open and read a font file. Sets Error flag. * * @param fontFilePath font file path. */ FTBufferFont(const char* fontFilePath); /** * Open and read a font from a buffer in memory. Sets Error flag. * The buffer is owned by the client and is NOT copied by FTGL. The * pointer must be valid while using FTGL. * * @param pBufferBytes the in-memory buffer * @param bufferSizeInBytes the length of the buffer in bytes */ FTBufferFont(const unsigned char *pBufferBytes, size_t bufferSizeInBytes); /** * Destructor */ ~FTBufferFont(); protected: /** * Construct a glyph of the correct type. * * Clients must override the function and return their specialised * FTGlyph. * * @param slot A FreeType glyph slot. * @return An FT****Glyph or null on failure. */ virtual FTGlyph* MakeGlyph(FT_GlyphSlot slot); }; #endif //__cplusplus FTGL_BEGIN_C_DECLS /** * Create a specialised FTGLfont object for handling memory buffer fonts. * * @param file The font file name. * @return An FTGLfont* object. * * @see FTGLfont */ FTGL_EXPORT FTGLfont *ftglCreateBufferFont(const char *file); FTGL_END_C_DECLS #endif // __FTBufferFont__ connectome-workbench-1.2.3+git41-gc4c6c90/src/FtglFont/FTGL/FTBufferGlyph.h000066400000000000000000000042621300200146000256470ustar00rootroot00000000000000/* * FTGL - OpenGL font library * * Copyright (c) 2008 Sam Hocevar * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #ifndef __ftgl__ # warning Please use instead of . # include #endif #ifndef __FTBufferGlyph__ #define __FTBufferGlyph__ #ifdef __cplusplus /** * FTBufferGlyph is a specialisation of FTGlyph for memory buffer rendering. */ class FTGL_EXPORT FTBufferGlyph : public FTGlyph { public: /** * Constructor * * @param glyph The Freetype glyph to be processed * @param buffer An FTBuffer object in which to render the glyph. */ FTBufferGlyph(FT_GlyphSlot glyph, FTBuffer *buffer); /** * Destructor */ virtual ~FTBufferGlyph(); /** * Render this glyph at the current pen position. * * @param pen The current pen position. * @param renderMode Render mode to display * @return The advance distance for this glyph. */ virtual const FTPoint& Render(const FTPoint& pen, int renderMode); }; #endif //__cplusplus #endif // __FTBufferGlyph__ connectome-workbench-1.2.3+git41-gc4c6c90/src/FtglFont/FTGL/FTExtrdGlyph.h000066400000000000000000000072751300200146000255330ustar00rootroot00000000000000/* * FTGL - OpenGL font library * * Copyright (c) 2001-2004 Henry Maddocks * Copyright (c) 2008 Sam Hocevar * Copyright (c) 2008 Sean Morrison * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #ifndef __ftgl__ # warning This header is deprecated. Please use from now. # include #endif #ifndef __FTExtrudeGlyph__ #define __FTExtrudeGlyph__ #ifdef __cplusplus /** * FTExtrudeGlyph is a specialisation of FTGlyph for creating tessellated * extruded polygon glyphs. */ class FTGL_EXPORT FTExtrudeGlyph : public FTGlyph { public: /** * Constructor. Sets the Error to Invalid_Outline if the glyph isn't * an outline. * * @param glyph The Freetype glyph to be processed * @param depth The distance along the z axis to extrude the glyph * @param frontOutset outset contour size * @param backOutset outset contour size * @param useDisplayList Enable or disable the use of Display Lists * for this glyph * true turns ON display lists. * false turns OFF display lists. */ FTExtrudeGlyph(FT_GlyphSlot glyph, float depth, float frontOutset, float backOutset, bool useDisplayList); /** * Destructor */ virtual ~FTExtrudeGlyph(); /** * Render this glyph at the current pen position. * * @param pen The current pen position. * @param renderMode Render mode to display * @return The advance distance for this glyph. */ virtual const FTPoint& Render(const FTPoint& pen, int renderMode); }; #define FTExtrdGlyph FTExtrudeGlyph #endif //__cplusplus FTGL_BEGIN_C_DECLS /** * Create a specialisation of FTGLglyph for creating tessellated * extruded polygon glyphs. * * @param glyph The Freetype glyph to be processed * @param depth The distance along the z axis to extrude the glyph * @param frontOutset outset contour size * @param backOutset outset contour size * @param useDisplayList Enable or disable the use of Display Lists * for this glyph * true turns ON display lists. * false turns OFF display lists. * @return An FTGLglyph* object. */ FTGL_EXPORT FTGLglyph *ftglCreateExtrudeGlyph(FT_GlyphSlot glyph, float depth, float frontOutset, float backOutset, int useDisplayList); FTGL_END_C_DECLS #endif // __FTExtrudeGlyph__ connectome-workbench-1.2.3+git41-gc4c6c90/src/FtglFont/FTGL/FTFont.h000066400000000000000000000500741300200146000243420ustar00rootroot00000000000000/* * FTGL - OpenGL font library * * Copyright (c) 2001-2004 Henry Maddocks * Copyright (c) 2008 Sam Hocevar * Copyright (c) 2008 Sean Morrison * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #ifndef __ftgl__ # warning This header is deprecated. Please use from now. # include #endif #ifndef __FTFont__ #define __FTFont__ #ifdef __cplusplus class FTFontImpl; /** * FTFont is the public interface for the FTGL library. * * Specific font classes are derived from this class. It uses the helper * classes FTFace and FTSize to access the Freetype library. This class * is abstract and deriving classes must implement the protected * MakeGlyph function to create glyphs of the * appropriate type. * * It is good practice after using these functions to test the error * code returned. FT_Error Error(). Check the freetype file * fterrdef.h for error definitions. * * @see FTFace * @see FTSize */ class FTGL_EXPORT FTFont { protected: /** * Open and read a font file. Sets Error flag. * * @param fontFilePath font file path. */ FTFont(char const *fontFilePath); /** * Open and read a font from a buffer in memory. Sets Error flag. * The buffer is owned by the client and is NOT copied by FTGL. The * pointer must be valid while using FTGL. * * @param pBufferBytes the in-memory buffer * @param bufferSizeInBytes the length of the buffer in bytes */ FTFont(const unsigned char *pBufferBytes, size_t bufferSizeInBytes); private: /* Allow our internal subclasses to access the private constructor */ friend class FTBitmapFont; friend class FTBufferFont; friend class FTExtrudeFont; friend class FTOutlineFont; friend class FTPixmapFont; friend class FTPolygonFont; friend class FTTextureFont; /** * Internal FTGL FTFont constructor. For private use only. * * @param pImpl Internal implementation object. Will be destroyed * upon FTFont deletion. */ FTFont(FTFontImpl *pImpl); public: virtual ~FTFont(); /** * Attach auxilliary file to font e.g font metrics. * * Note: not all font formats implement this function. * * @param fontFilePath auxilliary font file path. * @return true if file has been attached * successfully. */ virtual bool Attach(const char* fontFilePath); /** * Attach auxilliary data to font e.g font metrics, from memory. * * Note: not all font formats implement this function. * * @param pBufferBytes the in-memory buffer. * @param bufferSizeInBytes the length of the buffer in bytes. * @return true if file has been attached * successfully. */ virtual bool Attach(const unsigned char *pBufferBytes, size_t bufferSizeInBytes); /** * Set the glyph loading flags. By default, fonts use the most * sensible flags when loading a font's glyph using FT_Load_Glyph(). * This function allows to override the default flags. * * @param flags The glyph loading flags. */ virtual void GlyphLoadFlags(FT_Int flags); /** * Set the character map for the face. * * @param encoding Freetype enumerate for char map code. * @return true if charmap was valid and * set correctly. */ virtual bool CharMap(FT_Encoding encoding); /** * Get the number of character maps in this face. * * @return character map count. */ virtual unsigned int CharMapCount() const; /** * Get a list of character maps in this face. * * @return pointer to the first encoding. */ virtual FT_Encoding* CharMapList(); /** * Set the char size for the current face. * * @param size the face size in points (1/72 inch) * @param res the resolution of the target device. * @return true if size was set correctly */ virtual bool FaceSize(const unsigned int size, const unsigned int res = 72); /** * Get the current face size in points (1/72 inch). * * @return face size */ virtual unsigned int FaceSize() const; /** * Set the extrusion distance for the font. Only implemented by * FTExtrudeFont * * @param depth The extrusion distance. */ virtual void Depth(float depth); /** * Set the outset distance for the font. Only implemented by * FTOutlineFont, FTPolygonFont and FTExtrudeFont * * @param outset The outset distance. */ virtual void Outset(float outset); /** * Set the front and back outset distances for the font. Only * implemented by FTExtrudeFont * * @param front The front outset distance. * @param back The back outset distance. */ virtual void Outset(float front, float back); /** * Enable or disable the use of Display Lists inside FTGL * * @param useList true turns ON display lists. * false turns OFF display lists. */ virtual void UseDisplayList(bool useList); /** * Get the global ascender height for the face. * * @return Ascender height */ virtual float Ascender() const; /** * Gets the global descender height for the face. * * @return Descender height */ virtual float Descender() const; /** * Gets the line spacing for the font. * * @return Line height */ virtual float LineHeight() const; /** * Get the bounding box for a string. * * @param string A char buffer. * @param len The length of the string. If < 0 then all characters * will be checked until a null character is encountered * (optional). * @param position The pen position of the first character (optional). * @param spacing A displacement vector to add after each character * has been checked (optional). * @return The corresponding bounding box. */ virtual FTBBox BBox(const char *string, const int len = -1, FTPoint position = FTPoint(), FTPoint spacing = FTPoint()); /** * Get the bounding box for a string (deprecated). * * @param string A char buffer. * @param llx Lower left near x coordinate. * @param lly Lower left near y coordinate. * @param llz Lower left near z coordinate. * @param urx Upper right far x coordinate. * @param ury Upper right far y coordinate. * @param urz Upper right far z coordinate. */ void BBox(const char* string, float& llx, float& lly, float& llz, float& urx, float& ury, float& urz) { FTBBox b = BBox(string); llx = b.Lower().Xf(); lly = b.Lower().Yf(); llz = b.Lower().Zf(); urx = b.Upper().Xf(); ury = b.Upper().Yf(); urz = b.Upper().Zf(); } /** * Get the bounding box for a string. * * @param string A wchar_t buffer. * @param len The length of the string. If < 0 then all characters * will be checked until a null character is encountered * (optional). * @param position The pen position of the first character (optional). * @param spacing A displacement vector to add after each character * has been checked (optional). * @return The corresponding bounding box. */ virtual FTBBox BBox(const wchar_t *string, const int len = -1, FTPoint position = FTPoint(), FTPoint spacing = FTPoint()); /** * Get the bounding box for a string (deprecated). * * @param string A wchar_t buffer. * @param llx Lower left near x coordinate. * @param lly Lower left near y coordinate. * @param llz Lower left near z coordinate. * @param urx Upper right far x coordinate. * @param ury Upper right far y coordinate. * @param urz Upper right far z coordinate. */ void BBox(const wchar_t* string, float& llx, float& lly, float& llz, float& urx, float& ury, float& urz) { FTBBox b = BBox(string); llx = b.Lower().Xf(); lly = b.Lower().Yf(); llz = b.Lower().Zf(); urx = b.Upper().Xf(); ury = b.Upper().Yf(); urz = b.Upper().Zf(); } /** * Get the advance from one character to the next. * * Method added by HCP (John Harwell). * * @param theChar 'C' char * @param theNextChar next 'C' char * @return The char's advance width. */ float Advance(const wchar_t theChar, const wchar_t theNextChar); /** * Get the advance for a string. * * @param string 'C' style string to be checked. * @param len The length of the string. If < 0 then all characters * will be checked until a null character is encountered * (optional). * @param spacing A displacement vector to add after each character * has been checked (optional). * @return The string's advance width. */ virtual float Advance(const char* string, const int len = -1, FTPoint spacing = FTPoint()); /** * Get the advance for a string. * * @param string A wchar_t string * @param len The length of the string. If < 0 then all characters * will be checked until a null character is encountered * (optional). * @param spacing A displacement vector to add after each character * has been checked (optional). * @return The string's advance width. */ virtual float Advance(const wchar_t* string, const int len = -1, FTPoint spacing = FTPoint()); /** * Render a string of characters. * * @param string 'C' style string to be output. * @param len The length of the string. If < 0 then all characters * will be displayed until a null character is encountered * (optional). * @param position The pen position of the first character (optional). * @param spacing A displacement vector to add after each character * has been displayed (optional). * @param renderMode Render mode to use for display (optional). * @return The new pen position after the last character was output. */ virtual FTPoint Render(const char* string, const int len = -1, FTPoint position = FTPoint(), FTPoint spacing = FTPoint(), int renderMode = FTGL::RENDER_ALL); /** * Render a string of characters * * @param string wchar_t string to be output. * @param len The length of the string. If < 0 then all characters * will be displayed until a null character is encountered * (optional). * @param position The pen position of the first character (optional). * @param spacing A displacement vector to add after each character * has been displayed (optional). * @param renderMode Render mode to use for display (optional). * @return The new pen position after the last character was output. */ virtual FTPoint Render(const wchar_t *string, const int len = -1, FTPoint position = FTPoint(), FTPoint spacing = FTPoint(), int renderMode = FTGL::RENDER_ALL); /** * Queries the Font for errors. * * @return The current error code. */ virtual FT_Error Error() const; protected: /* Allow impl to access MakeGlyph */ friend class FTFontImpl; /** * Construct a glyph of the correct type. * * Clients must override the function and return their specialised * FTGlyph. * * @param slot A FreeType glyph slot. * @return An FT****Glyph or null on failure. */ virtual FTGlyph* MakeGlyph(FT_GlyphSlot slot) = 0; private: /** * Internal FTGL FTFont implementation object. For private use only. */ FTFontImpl *impl; }; #endif //__cplusplus FTGL_BEGIN_C_DECLS /** * FTGLfont is the public interface for the FTGL library. * * It is good practice after using these functions to test the error * code returned. FT_Error Error(). Check the freetype file * fterrdef.h for error definitions. */ struct _FTGLFont; typedef struct _FTGLfont FTGLfont; /** * Create a custom FTGL font object. * * @param fontFilePath The font file name. * @param data A pointer to private data that will be passed to callbacks. * @param makeglyphCallback A glyph-making callback function. * @return An FTGLfont* object. */ FTGL_EXPORT FTGLfont *ftglCreateCustomFont(char const *fontFilePath, void *data, FTGLglyph * (*makeglyphCallback) (FT_GlyphSlot, void *)); /** * Destroy an FTGL font object. * * @param font An FTGLfont* object. */ FTGL_EXPORT void ftglDestroyFont(FTGLfont* font); /** * Attach auxilliary file to font e.g. font metrics. * * Note: not all font formats implement this function. * * @param font An FTGLfont* object. * @param path Auxilliary font file path. * @return 1 if file has been attached successfully. */ FTGL_EXPORT int ftglAttachFile(FTGLfont* font, const char* path); /** * Attach auxilliary data to font, e.g. font metrics, from memory. * * Note: not all font formats implement this function. * * @param font An FTGLfont* object. * @param data The in-memory buffer. * @param size The length of the buffer in bytes. * @return 1 if file has been attached successfully. */ FTGL_EXPORT int ftglAttachData(FTGLfont* font, const unsigned char * data, size_t size); /** * Set the character map for the face. * * @param font An FTGLfont* object. * @param encoding Freetype enumerate for char map code. * @return 1 if charmap was valid and set correctly. */ FTGL_EXPORT int ftglSetFontCharMap(FTGLfont* font, FT_Encoding encoding); /** * Get the number of character maps in this face. * * @param font An FTGLfont* object. * @return character map count. */ FTGL_EXPORT unsigned int ftglGetFontCharMapCount(FTGLfont* font); /** * Get a list of character maps in this face. * * @param font An FTGLfont* object. * @return pointer to the first encoding. */ FTGL_EXPORT FT_Encoding* ftglGetFontCharMapList(FTGLfont* font); /** * Set the char size for the current face. * * @param font An FTGLfont* object. * @param size The face size in points (1/72 inch). * @param res The resolution of the target device, or 0 to use the default * value of 72. * @return 1 if size was set correctly. */ FTGL_EXPORT int ftglSetFontFaceSize(FTGLfont* font, unsigned int size, unsigned int res); /** * Get the current face size in points (1/72 inch). * * @param font An FTGLfont* object. * @return face size */ FTGL_EXPORT unsigned int ftglGetFontFaceSize(FTGLfont* font); /** * Set the extrusion distance for the font. Only implemented by * FTExtrudeFont. * * @param font An FTGLfont* object. * @param depth The extrusion distance. */ FTGL_EXPORT void ftglSetFontDepth(FTGLfont* font, float depth); /** * Set the outset distance for the font. Only FTOutlineFont, FTPolygonFont * and FTExtrudeFont implement front outset. Only FTExtrudeFont implements * back outset. * * @param font An FTGLfont* object. * @param front The front outset distance. * @param back The back outset distance. */ FTGL_EXPORT void ftglSetFontOutset(FTGLfont* font, float front, float back); /** * Enable or disable the use of Display Lists inside FTGL. * * @param font An FTGLfont* object. * @param useList 1 turns ON display lists. * 0 turns OFF display lists. */ FTGL_EXPORT void ftglSetFontDisplayList(FTGLfont* font, int useList); /** * Get the global ascender height for the face. * * @param font An FTGLfont* object. * @return Ascender height */ FTGL_EXPORT float ftglGetFontAscender(FTGLfont* font); /** * Gets the global descender height for the face. * * @param font An FTGLfont* object. * @return Descender height */ FTGL_EXPORT float ftglGetFontDescender(FTGLfont* font); /** * Gets the line spacing for the font. * * @param font An FTGLfont* object. * @return Line height */ FTGL_EXPORT float ftglGetFontLineHeight(FTGLfont* font); /** * Get the bounding box for a string. * * @param font An FTGLfont* object. * @param string A char buffer * @param len The length of the string. If < 0 then all characters will be * checked until a null character is encountered (optional). * @param bounds An array of 6 float values where the bounding box's lower * left near and upper right far 3D coordinates will be stored. */ FTGL_EXPORT void ftglGetFontBBox(FTGLfont* font, const char *string, int len, float bounds[6]); /** * Get the advance width for a string. * * @param font An FTGLfont* object. * @param string A char string. * @return Advance width */ FTGL_EXPORT float ftglGetFontAdvance(FTGLfont* font, const char *string); /** * Render a string of characters. * * @param font An FTGLfont* object. * @param string Char string to be output. * @param mode Render mode to display. */ FTGL_EXPORT void ftglRenderFont(FTGLfont* font, const char *string, int mode); /** * Query a font for errors. * * @param font An FTGLfont* object. * @return The current error code. */ FTGL_EXPORT FT_Error ftglGetFontError(FTGLfont* font); FTGL_END_C_DECLS #endif // __FTFont__ connectome-workbench-1.2.3+git41-gc4c6c90/src/FtglFont/FTGL/FTGLBitmapFont.h000066400000000000000000000060631300200146000257210ustar00rootroot00000000000000/* * FTGL - OpenGL font library * * Copyright (c) 2001-2004 Henry Maddocks * Copyright (c) 2008 Sam Hocevar * Copyright (c) 2008 Sean Morrison * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #ifndef __ftgl__ # warning This header is deprecated. Please use from now. # include #endif #ifndef __FTBitmapFont__ #define __FTBitmapFont__ #ifdef __cplusplus /** * FTBitmapFont is a specialisation of the FTFont class for handling * Bitmap fonts * * @see FTFont */ class FTGL_EXPORT FTBitmapFont : public FTFont { public: /** * Open and read a font file. Sets Error flag. * * @param fontFilePath font file path. */ FTBitmapFont(const char* fontFilePath); /** * Open and read a font from a buffer in memory. Sets Error flag. * The buffer is owned by the client and is NOT copied by FTGL. The * pointer must be valid while using FTGL. * * @param pBufferBytes the in-memory buffer * @param bufferSizeInBytes the length of the buffer in bytes */ FTBitmapFont(const unsigned char *pBufferBytes, size_t bufferSizeInBytes); /** * Destructor */ ~FTBitmapFont(); protected: /** * Construct a glyph of the correct type. * * Clients must override the function and return their specialised * FTGlyph. * * @param slot A FreeType glyph slot. * @return An FT****Glyph or null on failure. */ virtual FTGlyph* MakeGlyph(FT_GlyphSlot slot); }; #define FTGLBitmapFont FTBitmapFont #endif //__cplusplus FTGL_BEGIN_C_DECLS /** * Create a specialised FTGLfont object for handling bitmap fonts. * * @param file The font file name. * @return An FTGLfont* object. * * @see FTGLfont */ FTGL_EXPORT FTGLfont *ftglCreateBitmapFont(const char *file); FTGL_END_C_DECLS #endif // __FTBitmapFont__ connectome-workbench-1.2.3+git41-gc4c6c90/src/FtglFont/FTGL/FTGLExtrdFont.h000066400000000000000000000061771300200146000256010ustar00rootroot00000000000000/* * FTGL - OpenGL font library * * Copyright (c) 2001-2004 Henry Maddocks * Copyright (c) 2008 Sam Hocevar * Copyright (c) 2008 Sean Morrison * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #ifndef __ftgl__ # warning This header is deprecated. Please use from now. # include #endif #ifndef __FTExtrudeFont__ #define __FTExtrudeFont__ #ifdef __cplusplus /** * FTExtrudeFont is a specialisation of the FTFont class for handling * extruded Polygon fonts * * @see FTFont * @see FTPolygonFont */ class FTGL_EXPORT FTExtrudeFont : public FTFont { public: /** * Open and read a font file. Sets Error flag. * * @param fontFilePath font file path. */ FTExtrudeFont(const char* fontFilePath); /** * Open and read a font from a buffer in memory. Sets Error flag. * The buffer is owned by the client and is NOT copied by FTGL. The * pointer must be valid while using FTGL. * * @param pBufferBytes the in-memory buffer * @param bufferSizeInBytes the length of the buffer in bytes */ FTExtrudeFont(const unsigned char *pBufferBytes, size_t bufferSizeInBytes); /** * Destructor */ ~FTExtrudeFont(); protected: /** * Construct a glyph of the correct type. * * Clients must override the function and return their specialised * FTGlyph. * * @param slot A FreeType glyph slot. * @return An FT****Glyph or null on failure. */ virtual FTGlyph* MakeGlyph(FT_GlyphSlot slot); }; #define FTGLExtrdFont FTExtrudeFont #endif //__cplusplus FTGL_BEGIN_C_DECLS /** * Create a specialised FTGLfont object for handling extruded poygon fonts. * * @param file The font file name. * @return An FTGLfont* object. * * @see FTGLfont * @see ftglCreatePolygonFont */ FTGL_EXPORT FTGLfont *ftglCreateExtrudeFont(const char *file); FTGL_END_C_DECLS #endif // __FTExtrudeFont__ connectome-workbench-1.2.3+git41-gc4c6c90/src/FtglFont/FTGL/FTGLGlyph.h000066400000000000000000000141411300200146000247350ustar00rootroot00000000000000/* * FTGL - OpenGL font library * * Copyright (c) 2001-2004 Henry Maddocks * Copyright (c) 2008 Sam Hocevar * Copyright (c) 2008 Sean Morrison * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #ifndef __ftgl__ # warning This header is deprecated. Please use from now. # include #endif #ifndef __FTGlyph__ #define __FTGlyph__ #ifdef __cplusplus class FTGlyphImpl; /** * FTGlyph is the base class for FTGL glyphs. * * It provides the interface between Freetype glyphs and their openGL * renderable counterparts. This is an abstract class and derived classes * must implement the Render function. * * @see FTBBox * @see FTPoint */ class FTGL_EXPORT FTGlyph { protected: /** * Create a glyph. * * @param glyph The Freetype glyph to be processed */ FTGlyph(FT_GlyphSlot glyph); private: /** * Internal FTGL FTGlyph constructor. For private use only. * * @param pImpl Internal implementation object. Will be destroyed * upon FTGlyph deletion. */ FTGlyph(FTGlyphImpl *pImpl); /* Allow our internal subclasses to access the private constructor */ friend class FTBitmapGlyph; friend class FTBufferGlyph; friend class FTExtrudeGlyph; friend class FTOutlineGlyph; friend class FTPixmapGlyph; friend class FTPolygonGlyph; friend class FTTextureGlyph; public: /** * Destructor */ virtual ~FTGlyph(); /** * Renders this glyph at the current pen position. * * @param pen The current pen position. * @param renderMode Render mode to display * @return The advance distance for this glyph. */ virtual const FTPoint& Render(const FTPoint& pen, int renderMode) = 0; /** * Return the advance width for this glyph. * * @return advance width. */ virtual float Advance() const; /** * Return the bounding box for this glyph. * * @return bounding box. */ virtual const FTBBox& BBox() const; /** * Queries for errors. * * @return The current error code. */ virtual FT_Error Error() const; private: /** * Internal FTGL FTGlyph implementation object. For private use only. */ FTGlyphImpl *impl; }; #endif //__cplusplus FTGL_BEGIN_C_DECLS /** * FTGLglyph is the base class for FTGL glyphs. * * It provides the interface between Freetype glyphs and their openGL * renderable counterparts. This is an abstract class and derived classes * must implement the ftglRenderGlyph() function. */ struct _FTGLGlyph; typedef struct _FTGLglyph FTGLglyph; /** * Create a custom FTGL glyph object. * FIXME: maybe get rid of "base" and have advanceCallback etc. functions * * @param base The base FTGLglyph* to subclass. * @param data A pointer to private data that will be passed to callbacks. * @param renderCallback A rendering callback function. * @param destroyCallback A callback function to be called upon destruction. * @return An FTGLglyph* object. */ FTGL_EXPORT FTGLglyph *ftglCreateCustomGlyph(FTGLglyph *base, void *data, void (*renderCallback) (FTGLglyph *, void *, FTGL_DOUBLE, FTGL_DOUBLE, int, FTGL_DOUBLE *, FTGL_DOUBLE *), void (*destroyCallback) (FTGLglyph *, void *)); /** * Destroy an FTGL glyph object. * * @param glyph An FTGLglyph* object. */ FTGL_EXPORT void ftglDestroyGlyph(FTGLglyph *glyph); /** * Render a glyph at the current pen position and compute the corresponding * advance. * * @param glyph An FTGLglyph* object. * @param penx The current pen's X position. * @param peny The current pen's Y position. * @param renderMode Render mode to display * @param advancex A pointer to an FTGL_DOUBLE where to write the advance's X * component. * @param advancey A pointer to an FTGL_DOUBLE where to write the advance's Y * component. */ FTGL_EXPORT void ftglRenderGlyph(FTGLglyph *glyph, FTGL_DOUBLE penx, FTGL_DOUBLE peny, int renderMode, FTGL_DOUBLE *advancex, FTGL_DOUBLE *advancey); /** * Return the advance for a glyph. * * @param glyph An FTGLglyph* object. * @return The advance's X component. */ FTGL_EXPORT float ftglGetGlyphAdvance(FTGLglyph *glyph); /** * Return the bounding box for a glyph. * * @param glyph An FTGLglyph* object. * @param bounds An array of 6 float values where the bounding box's lower * left near and upper right far 3D coordinates will be stored. */ FTGL_EXPORT void ftglGetGlyphBBox(FTGLglyph *glyph, float bounds[6]); /** * Query a glyph for errors. * * @param glyph An FTGLglyph* object. * @return The current error code. */ FTGL_EXPORT FT_Error ftglGetGlyphError(FTGLglyph* glyph); FTGL_END_C_DECLS #endif // __FTGlyph__ connectome-workbench-1.2.3+git41-gc4c6c90/src/FtglFont/FTGL/FTGLOutlineFont.h000066400000000000000000000061151300200146000261220ustar00rootroot00000000000000/* * FTGL - OpenGL font library * * Copyright (c) 2001-2004 Henry Maddocks * Copyright (c) 2008 Sam Hocevar * Copyright (c) 2008 Sean Morrison * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #ifndef __ftgl__ # warning This header is deprecated. Please use from now. # include #endif #ifndef __FTOutlineFont__ #define __FTOutlineFont__ #ifdef __cplusplus /** * FTOutlineFont is a specialisation of the FTFont class for handling * Vector Outline fonts * * @see FTFont */ class FTGL_EXPORT FTOutlineFont : public FTFont { public: /** * Open and read a font file. Sets Error flag. * * @param fontFilePath font file path. */ FTOutlineFont(const char* fontFilePath); /** * Open and read a font from a buffer in memory. Sets Error flag. * The buffer is owned by the client and is NOT copied by FTGL. The * pointer must be valid while using FTGL. * * @param pBufferBytes the in-memory buffer * @param bufferSizeInBytes the length of the buffer in bytes */ FTOutlineFont(const unsigned char *pBufferBytes, size_t bufferSizeInBytes); /** * Destructor */ ~FTOutlineFont(); protected: /** * Construct a glyph of the correct type. * * Clients must override the function and return their specialised * FTGlyph. * * @param slot A FreeType glyph slot. * @return An FT****Glyph or null on failure. */ virtual FTGlyph* MakeGlyph(FT_GlyphSlot slot); }; #define FTGLOutlineFont FTOutlineFont #endif //__cplusplus FTGL_BEGIN_C_DECLS /** * Create a specialised FTGLfont object for handling vector outline fonts. * * @param file The font file name. * @return An FTGLfont* object. * * @see FTGLfont */ FTGL_EXPORT FTGLfont *ftglCreateOutlineFont(const char *file); FTGL_END_C_DECLS #endif // __FTOutlineFont__ connectome-workbench-1.2.3+git41-gc4c6c90/src/FtglFont/FTGL/FTGLPixmapFont.h000066400000000000000000000061161300200146000257420ustar00rootroot00000000000000/* * FTGL - OpenGL font library * * Copyright (c) 2001-2004 Henry Maddocks * Copyright (c) 2008 Sam Hocevar * Copyright (c) 2008 Sean Morrison * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #ifndef __ftgl__ # warning This header is deprecated. Please use from now. # include #endif #ifndef __FTPixmapFont__ #define __FTPixmapFont__ #ifdef __cplusplus /** * FTPixmapFont is a specialisation of the FTFont class for handling * Pixmap (Grey Scale) fonts * * @see FTFont */ class FTGL_EXPORT FTPixmapFont : public FTFont { public: /** * Open and read a font file. Sets Error flag. * * @param fontFilePath font file path. */ FTPixmapFont(const char* fontFilePath); /** * Open and read a font from a buffer in memory. Sets Error flag. * The buffer is owned by the client and is NOT copied by FTGL. The * pointer must be valid while using FTGL. * * @param pBufferBytes the in-memory buffer * @param bufferSizeInBytes the length of the buffer in bytes */ FTPixmapFont(const unsigned char *pBufferBytes, size_t bufferSizeInBytes); /** * Destructor */ ~FTPixmapFont(); protected: /** * Construct a glyph of the correct type. * * Clients must override the function and return their specialised * FTGlyph. * * @param slot A FreeType glyph slot. * @return An FT****Glyph or null on failure. */ virtual FTGlyph* MakeGlyph(FT_GlyphSlot slot); }; #define FTGLPixmapFont FTPixmapFont #endif // __cplusplus FTGL_BEGIN_C_DECLS /** * Create a specialised FTGLfont object for handling pixmap (grey scale) fonts. * * @param file The font file name. * @return An FTGLfont* object. * * @see FTGLfont */ FTGL_EXPORT FTGLfont *ftglCreatePixmapFont(const char *file); FTGL_END_C_DECLS #endif // __FTPixmapFont__ connectome-workbench-1.2.3+git41-gc4c6c90/src/FtglFont/FTGL/FTGLPolygonFont.h000066400000000000000000000061441300200146000261340ustar00rootroot00000000000000/* * FTGL - OpenGL font library * * Copyright (c) 2001-2004 Henry Maddocks * Copyright (c) 2008 Sam Hocevar * Copyright (c) 2008 Sean Morrison * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #ifndef __ftgl__ # warning This header is deprecated. Please use from now. # include #endif #ifndef __FTPolygonFont__ #define __FTPolygonFont__ #ifdef __cplusplus /** * FTPolygonFont is a specialisation of the FTFont class for handling * tesselated Polygon Mesh fonts * * @see FTFont */ class FTGL_EXPORT FTPolygonFont : public FTFont { public: /** * Open and read a font file. Sets Error flag. * * @param fontFilePath font file path. */ FTPolygonFont(const char* fontFilePath); /** * Open and read a font from a buffer in memory. Sets Error flag. * The buffer is owned by the client and is NOT copied by FTGL. The * pointer must be valid while using FTGL. * * @param pBufferBytes the in-memory buffer * @param bufferSizeInBytes the length of the buffer in bytes */ FTPolygonFont(const unsigned char *pBufferBytes, size_t bufferSizeInBytes); /** * Destructor */ ~FTPolygonFont(); protected: /** * Construct a glyph of the correct type. * * Clients must override the function and return their specialised * FTGlyph. * * @param slot A FreeType glyph slot. * @return An FT****Glyph or null on failure. */ virtual FTGlyph* MakeGlyph(FT_GlyphSlot slot); }; #define FTGLPolygonFont FTPolygonFont #endif //__cplusplus FTGL_BEGIN_C_DECLS /** * Create a specialised FTGLfont object for handling tesselated polygon * mesh fonts. * * @param file The font file name. * @return An FTGLfont* object. * * @see FTGLfont */ FTGL_EXPORT FTGLfont *ftglCreatePolygonFont(const char *file); FTGL_END_C_DECLS #endif // __FTPolygonFont__ connectome-workbench-1.2.3+git41-gc4c6c90/src/FtglFont/FTGL/FTGLTextureFont.h000066400000000000000000000061261300200146000261450ustar00rootroot00000000000000/* * FTGL - OpenGL font library * * Copyright (c) 2001-2004 Henry Maddocks * Copyright (c) 2008 Sam Hocevar * Copyright (c) 2008 Sean Morrison * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #ifndef __ftgl__ # warning This header is deprecated. Please use from now. # include #endif #ifndef __FTTextureFont__ #define __FTTextureFont__ #ifdef __cplusplus /** * FTTextureFont is a specialisation of the FTFont class for handling * Texture mapped fonts * * @see FTFont */ class FTGL_EXPORT FTTextureFont : public FTFont { public: /** * Open and read a font file. Sets Error flag. * * @param fontFilePath font file path. */ FTTextureFont(const char* fontFilePath); /** * Open and read a font from a buffer in memory. Sets Error flag. * The buffer is owned by the client and is NOT copied by FTGL. The * pointer must be valid while using FTGL. * * @param pBufferBytes the in-memory buffer * @param bufferSizeInBytes the length of the buffer in bytes */ FTTextureFont(const unsigned char *pBufferBytes, size_t bufferSizeInBytes); /** * Destructor */ virtual ~FTTextureFont(); protected: /** * Construct a glyph of the correct type. * * Clients must override the function and return their specialised * FTGlyph. * * @param slot A FreeType glyph slot. * @return An FT****Glyph or null on failure. */ virtual FTGlyph* MakeGlyph(FT_GlyphSlot slot); }; #define FTGLTextureFont FTTextureFont #endif //__cplusplus FTGL_BEGIN_C_DECLS /** * Create a specialised FTGLfont object for handling texture-mapped fonts. * * @param file The font file name. * @return An FTGLfont* object. * * @see FTGLfont */ FTGL_EXPORT FTGLfont *ftglCreateTextureFont(const char *file); FTGL_END_C_DECLS #endif // __FTTextureFont__ connectome-workbench-1.2.3+git41-gc4c6c90/src/FtglFont/FTGL/FTLayout.h000066400000000000000000000145451300200146000247140ustar00rootroot00000000000000/* * FTGL - OpenGL font library * * Copyright (c) 2001-2004 Henry Maddocks * Copyright (c) 2008 Sam Hocevar * Copyright (c) 2008 Sean Morrison * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #ifndef __ftgl__ # warning This header is deprecated. Please use from now. # include #endif #ifndef __FTLayout__ #define __FTLayout__ #ifdef __cplusplus class FTLayoutImpl; /** * FTLayout is the interface for layout managers that render text. * * Specific layout manager classes are derived from this class. This class * is abstract and deriving classes must implement the protected * Render methods to render formatted text and * BBox methods to determine the bounding box of output text. * * @see FTFont * @see FTBBox */ class FTGL_EXPORT FTLayout { protected: FTLayout(); private: /** * Internal FTGL FTLayout constructor. For private use only. * * @param pImpl Internal implementation object. Will be destroyed * upon FTLayout deletion. */ FTLayout(FTLayoutImpl *pImpl); /* Allow our internal subclasses to access the private constructor */ friend class FTSimpleLayout; public: /** * Destructor */ virtual ~FTLayout(); /** * Get the bounding box for a formatted string. * * @param string A char string. * @param len The length of the string. If < 0 then all characters * will be checked until a null character is encountered * (optional). * @param position The pen position of the first character (optional). * @return The corresponding bounding box. */ virtual FTBBox BBox(const char* string, const int len = -1, FTPoint position = FTPoint()) = 0; /** * Get the bounding box for a formatted string. * * @param string A wchar_t string. * @param len The length of the string. If < 0 then all characters * will be checked until a null character is encountered * (optional). * @param position The pen position of the first character (optional). * @return The corresponding bounding box. */ virtual FTBBox BBox(const wchar_t* string, const int len = -1, FTPoint position = FTPoint()) = 0; /** * Render a string of characters. * * @param string 'C' style string to be output. * @param len The length of the string. If < 0 then all characters * will be displayed until a null character is encountered * (optional). * @param position The pen position of the first character (optional). * @param renderMode Render mode to display (optional) */ virtual void Render(const char *string, const int len = -1, FTPoint position = FTPoint(), int renderMode = FTGL::RENDER_ALL) = 0; /** * Render a string of characters. * * @param string wchar_t string to be output. * @param len The length of the string. If < 0 then all characters * will be displayed until a null character is encountered * (optional). * @param position The pen position of the first character (optional). * @param renderMode Render mode to display (optional) */ virtual void Render(const wchar_t *string, const int len = -1, FTPoint position = FTPoint(), int renderMode = FTGL::RENDER_ALL) = 0; /** * Queries the Layout for errors. * * @return The current error code. */ virtual FT_Error Error() const; private: /** * Internal FTGL FTLayout implementation object. For private use only. */ FTLayoutImpl *impl; }; #endif //__cplusplus FTGL_BEGIN_C_DECLS /** * FTGLlayout is the interface for layout managers that render text. */ struct _FTGLlayout; typedef struct _FTGLlayout FTGLlayout; /** * Destroy an FTGL layout object. * * @param layout An FTGLlayout* object. */ FTGL_EXPORT void ftglDestroyLayout(FTGLlayout* layout); /** * Get the bounding box for a string. * * @param layout An FTGLlayout* object. * @param string A char buffer * @param bounds An array of 6 float values where the bounding box's lower * left near and upper right far 3D coordinates will be stored. */ FTGL_EXPORT void ftglGetLayoutBBox(FTGLlayout *layout, const char* string, float bounds[6]); /** * Render a string of characters. * * @param layout An FTGLlayout* object. * @param string Char string to be output. * @param mode Render mode to display. */ FTGL_EXPORT void ftglRenderLayout(FTGLlayout *layout, const char *string, int mode); /** * Query a layout for errors. * * @param layout An FTGLlayout* object. * @return The current error code. */ FTGL_EXPORT FT_Error ftglGetLayoutError(FTGLlayout* layout); FTGL_END_C_DECLS #endif /* __FTLayout__ */ connectome-workbench-1.2.3+git41-gc4c6c90/src/FtglFont/FTGL/FTOutlineGlyph.h000066400000000000000000000063511300200146000260560ustar00rootroot00000000000000/* * FTGL - OpenGL font library * * Copyright (c) 2001-2004 Henry Maddocks * Copyright (c) 2008 Sam Hocevar * Copyright (c) 2008 Sean Morrison * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #ifndef __ftgl__ # warning This header is deprecated. Please use from now. # include #endif #ifndef __FTOutlineGlyph__ #define __FTOutlineGlyph__ #ifdef __cplusplus /** * FTOutlineGlyph is a specialisation of FTGlyph for creating outlines. */ class FTGL_EXPORT FTOutlineGlyph : public FTGlyph { public: /** * Constructor. Sets the Error to Invalid_Outline if the glyphs isn't * an outline. * * @param glyph The Freetype glyph to be processed * @param outset outset distance * @param useDisplayList Enable or disable the use of Display Lists * for this glyph * true turns ON display lists. * false turns OFF display lists. */ FTOutlineGlyph(FT_GlyphSlot glyph, float outset, bool useDisplayList); /** * Destructor */ virtual ~FTOutlineGlyph(); /** * Render this glyph at the current pen position. * * @param pen The current pen position. * @param renderMode Render mode to display * @return The advance distance for this glyph. */ virtual const FTPoint& Render(const FTPoint& pen, int renderMode); }; #endif //__cplusplus FTGL_BEGIN_C_DECLS /** * Create a specialisation of FTGLglyph for creating outlines. * * @param glyph The Freetype glyph to be processed * @param outset outset contour size * @param useDisplayList Enable or disable the use of Display Lists * for this glyph * true turns ON display lists. * false turns OFF display lists. * @return An FTGLglyph* object. */ FTGL_EXPORT FTGLglyph *ftglCreateOutlineGlyph(FT_GlyphSlot glyph, float outset, int useDisplayList); FTGL_END_C_DECLS #endif // __FTOutlineGlyph__ connectome-workbench-1.2.3+git41-gc4c6c90/src/FtglFont/FTGL/FTPixmapGlyph.h000066400000000000000000000047261300200146000257010ustar00rootroot00000000000000/* * FTGL - OpenGL font library * * Copyright (c) 2001-2004 Henry Maddocks * Copyright (c) 2008 Sam Hocevar * Copyright (c) 2008 Sean Morrison * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #ifndef __ftgl__ # warning This header is deprecated. Please use from now. # include #endif #ifndef __FTPixmapGlyph__ #define __FTPixmapGlyph__ #ifdef __cplusplus /** * FTPixmapGlyph is a specialisation of FTGlyph for creating pixmaps. */ class FTGL_EXPORT FTPixmapGlyph : public FTGlyph { public: /** * Constructor * * @param glyph The Freetype glyph to be processed */ FTPixmapGlyph(FT_GlyphSlot glyph); /** * Destructor */ virtual ~FTPixmapGlyph(); /** * Render this glyph at the current pen position. * * @param pen The current pen position. * @param renderMode Render mode to display * @return The advance distance for this glyph. */ virtual const FTPoint& Render(const FTPoint& pen, int renderMode); }; #endif //__cplusplus FTGL_BEGIN_C_DECLS /** * Create a specialisation of FTGLglyph for creating pixmaps. * * @param glyph The Freetype glyph to be processed * @return An FTGLglyph* object. */ FTGL_EXPORT FTGLglyph *ftglCreatePixmapGlyph(FT_GlyphSlot glyph); FTGL_END_C_DECLS #endif // __FTPixmapGlyph__ connectome-workbench-1.2.3+git41-gc4c6c90/src/FtglFont/FTGL/FTPoint.h000066400000000000000000000170241300200146000245230ustar00rootroot00000000000000/* * FTGL - OpenGL font library * * Copyright (c) 2001-2004 Henry Maddocks * Copyright (c) 2008 Sam Hocevar * Copyright (c) 2008 Sean Morrison * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #ifndef __ftgl__ # warning This header is deprecated. Please use from now. # include #endif #ifndef __FTPoint__ #define __FTPoint__ #ifdef __cplusplus /** * FTPoint class is a basic 3-dimensional point or vector. */ class FTGL_EXPORT FTPoint { public: /** * Default constructor. Point is set to zero. */ inline FTPoint() { values[0] = 0; values[1] = 0; values[2] = 0; } /** * Constructor. Z coordinate is set to zero if unspecified. * * @param x First component * @param y Second component * @param z Third component */ inline FTPoint(const FTGL_DOUBLE x, const FTGL_DOUBLE y, const FTGL_DOUBLE z = 0) { values[0] = x; values[1] = y; values[2] = z; } /** * Constructor. This converts an FT_Vector to an FTPoint * * @param ft_vector A freetype vector */ inline FTPoint(const FT_Vector& ft_vector) { values[0] = ft_vector.x; values[1] = ft_vector.y; values[2] = 0; } /** * Normalise a point's coordinates. If the coordinates are zero, * the point is left untouched. * * @return A vector of norm one. */ FTPoint Normalise(); /** * Operator += In Place Addition. * * @param point * @return this plus point. */ inline FTPoint& operator += (const FTPoint& point) { values[0] += point.values[0]; values[1] += point.values[1]; values[2] += point.values[2]; return *this; } /** * Operator + * * @param point * @return this plus point. */ inline FTPoint operator + (const FTPoint& point) const { FTPoint temp; temp.values[0] = values[0] + point.values[0]; temp.values[1] = values[1] + point.values[1]; temp.values[2] = values[2] + point.values[2]; return temp; } /** * Operator -= In Place Substraction. * * @param point * @return this minus point. */ inline FTPoint& operator -= (const FTPoint& point) { values[0] -= point.values[0]; values[1] -= point.values[1]; values[2] -= point.values[2]; return *this; } /** * Operator - * * @param point * @return this minus point. */ inline FTPoint operator - (const FTPoint& point) const { FTPoint temp; temp.values[0] = values[0] - point.values[0]; temp.values[1] = values[1] - point.values[1]; temp.values[2] = values[2] - point.values[2]; return temp; } /** * Operator * Scalar multiplication * * @param multiplier * @return this multiplied by multiplier. */ inline FTPoint operator * (double multiplier) const { FTPoint temp; temp.values[0] = values[0] * multiplier; temp.values[1] = values[1] * multiplier; temp.values[2] = values[2] * multiplier; return temp; } /** * Operator * Scalar multiplication * * @param point * @param multiplier * @return multiplier multiplied by point. */ inline friend FTPoint operator * (double multiplier, FTPoint& point) { return point * multiplier; } /** * Operator * Scalar product * * @param a First vector. * @param b Second vector. * @return a.b scalar product. */ inline friend double operator * (FTPoint &a, FTPoint& b) { return a.values[0] * b.values[0] + a.values[1] * b.values[1] + a.values[2] * b.values[2]; } /** * Operator ^ Vector product * * @param point Second point * @return this vector point. */ inline FTPoint operator ^ (const FTPoint& point) { FTPoint temp; temp.values[0] = values[1] * point.values[2] - values[2] * point.values[1]; temp.values[1] = values[2] * point.values[0] - values[0] * point.values[2]; temp.values[2] = values[0] * point.values[1] - values[1] * point.values[0]; return temp; } /** * Operator == Tests for equality * * @param a * @param b * @return true if a & b are equal */ friend bool operator == (const FTPoint &a, const FTPoint &b); /** * Operator != Tests for non equality * * @param a * @param b * @return true if a & b are not equal */ friend bool operator != (const FTPoint &a, const FTPoint &b); /** * Cast to FTGL_DOUBLE* */ inline operator const FTGL_DOUBLE*() const { return values; } /** * Setters */ inline void X(FTGL_DOUBLE x) { values[0] = x; }; inline void Y(FTGL_DOUBLE y) { values[1] = y; }; inline void Z(FTGL_DOUBLE z) { values[2] = z; }; /** * Getters */ inline FTGL_DOUBLE X() const { return values[0]; }; inline FTGL_DOUBLE Y() const { return values[1]; }; inline FTGL_DOUBLE Z() const { return values[2]; }; inline FTGL_FLOAT Xf() const { return static_cast(values[0]); }; inline FTGL_FLOAT Yf() const { return static_cast(values[1]); }; inline FTGL_FLOAT Zf() const { return static_cast(values[2]); }; private: /** * The point data */ FTGL_DOUBLE values[3]; }; #endif //__cplusplus #endif // __FTPoint__ connectome-workbench-1.2.3+git41-gc4c6c90/src/FtglFont/FTGL/FTPolyGlyph.h000066400000000000000000000065111300200146000253600ustar00rootroot00000000000000/* * FTGL - OpenGL font library * * Copyright (c) 2001-2004 Henry Maddocks * Copyright (c) 2008 Sam Hocevar * Copyright (c) 2008 Sean Morrison * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #ifndef __ftgl__ # warning This header is deprecated. Please use from now. # include #endif #ifndef __FTPolygonGlyph__ #define __FTPolygonGlyph__ #ifdef __cplusplus /** * FTPolygonGlyph is a specialisation of FTGlyph for creating tessellated * polygon glyphs. */ class FTGL_EXPORT FTPolygonGlyph : public FTGlyph { public: /** * Constructor. Sets the Error to Invalid_Outline if the glyphs * isn't an outline. * * @param glyph The Freetype glyph to be processed * @param outset The outset distance * @param useDisplayList Enable or disable the use of Display Lists * for this glyph * true turns ON display lists. * false turns OFF display lists. */ FTPolygonGlyph(FT_GlyphSlot glyph, float outset, bool useDisplayList); /** * Destructor */ virtual ~FTPolygonGlyph(); /** * Render this glyph at the current pen position. * * @param pen The current pen position. * @param renderMode Render mode to display * @return The advance distance for this glyph. */ virtual const FTPoint& Render(const FTPoint& pen, int renderMode); }; #define FTPolyGlyph FTPolygonGlyph #endif //__cplusplus FTGL_BEGIN_C_DECLS /** * Create a specialisation of FTGLglyph for creating tessellated * polygon glyphs. * * @param glyph The Freetype glyph to be processed * @param outset outset contour size * @param useDisplayList Enable or disable the use of Display Lists * for this glyph * true turns ON display lists. * false turns OFF display lists. * @return An FTGLglyph* object. */ FTGL_EXPORT FTGLglyph *ftglCreatePolygonGlyph(FT_GlyphSlot glyph, float outset, int useDisplayList); FTGL_END_C_DECLS #endif // __FTPolygonGlyph__ connectome-workbench-1.2.3+git41-gc4c6c90/src/FtglFont/FTGL/FTSimpleLayout.h000066400000000000000000000147201300200146000260610ustar00rootroot00000000000000/* * FTGL - OpenGL font library * * Copyright (c) 2001-2004 Henry Maddocks * Copyright (c) 2008 Sam Hocevar * Copyright (c) 2008 Sean Morrison * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #ifndef __ftgl__ # warning This header is deprecated. Please use from now. # include #endif #ifndef __FTSimpleLayout__ #define __FTSimpleLayout__ #ifdef __cplusplus class FTFont; /** * FTSimpleLayout is a specialisation of FTLayout for simple text boxes. * * This class has basic support for text wrapping, left, right and centered * alignment, and text justification. * * @see FTLayout */ class FTGL_EXPORT FTSimpleLayout : public FTLayout { public: /** * Initializes line spacing to 1.0, alignment to * ALIGN_LEFT and wrap to 100.0 */ FTSimpleLayout(); /** * Destructor */ ~FTSimpleLayout(); /** * Get the bounding box for a formatted string. * * @param string A char string. * @param len The length of the string. If < 0 then all characters * will be checked until a null character is encountered * (optional). * @param position The pen position of the first character (optional). * @return The corresponding bounding box. */ virtual FTBBox BBox(const char* string, const int len = -1, FTPoint position = FTPoint()); /** * Get the bounding box for a formatted string. * * @param string A wchar_t string. * @param len The length of the string. If < 0 then all characters * will be checked until a null character is encountered * (optional). * @param position The pen position of the first character (optional). * @return The corresponding bounding box. */ virtual FTBBox BBox(const wchar_t* string, const int len = -1, FTPoint position = FTPoint()); /** * Render a string of characters. * * @param string 'C' style string to be output. * @param len The length of the string. If < 0 then all characters * will be displayed until a null character is encountered * (optional). * @param position The pen position of the first character (optional). * @param renderMode Render mode to display (optional) */ virtual void Render(const char *string, const int len = -1, FTPoint position = FTPoint(), int renderMode = FTGL::RENDER_ALL); /** * Render a string of characters. * * @param string wchar_t string to be output. * @param len The length of the string. If < 0 then all characters * will be displayed until a null character is encountered * (optional). * @param position The pen position of the first character (optional). * @param renderMode Render mode to display (optional) */ virtual void Render(const wchar_t *string, const int len = -1, FTPoint position = FTPoint(), int renderMode = FTGL::RENDER_ALL); /** * Set the font to use for rendering the text. * * @param fontInit A pointer to the new font. The font is * referenced by this but will not be * disposed of when this is deleted. */ void SetFont(FTFont *fontInit); /** * @return The current font. */ FTFont *GetFont(); /** * The maximum line length for formatting text. * * @param LineLength The new line length. */ void SetLineLength(const float LineLength); /** * @return The current line length. */ float GetLineLength() const; /** * The text alignment mode used to distribute * space within a line or rendered text. * * @param Alignment The new alignment mode. */ void SetAlignment(const FTGL::TextAlignment Alignment); /** * @return The text alignment mode. */ FTGL::TextAlignment GetAlignment() const; /** * Sets the line height. * * @param LineSpacing The height of each line of text expressed as * a percentage of the current fonts line height. */ void SetLineSpacing(const float LineSpacing); /** * @return The line spacing. */ float GetLineSpacing() const; }; #endif //__cplusplus FTGL_BEGIN_C_DECLS FTGL_EXPORT FTGLlayout *ftglCreateSimpleLayout(void); FTGL_EXPORT void ftglSetLayoutFont(FTGLlayout *, FTGLfont*); FTGL_EXPORT FTGLfont *ftglGetLayoutFont(FTGLlayout *); FTGL_EXPORT void ftglSetLayoutLineLength(FTGLlayout *, const float); FTGL_EXPORT float ftglGetLayoutLineLength(FTGLlayout *); FTGL_EXPORT void ftglSetLayoutAlignment(FTGLlayout *, const int); FTGL_EXPORT int ftglGetLayoutAlignement(FTGLlayout *); FTGL_EXPORT void ftglSetLayoutLineSpacing(FTGLlayout *, const float); FTGL_EXPORT float ftglGetLayoutLineSpacing(FTGLlayout *); FTGL_END_C_DECLS #endif /* __FTSimpleLayout__ */ connectome-workbench-1.2.3+git41-gc4c6c90/src/FtglFont/FTGL/FTTextureGlyph.h000066400000000000000000000067771300200146000261130ustar00rootroot00000000000000/* * FTGL - OpenGL font library * * Copyright (c) 2001-2004 Henry Maddocks * Copyright (c) 2008 Sam Hocevar * Copyright (c) 2008 Sean Morrison * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #ifndef __ftgl__ # warning This header is deprecated. Please use from now. # include #endif #ifndef __FTTextureGlyph__ #define __FTTextureGlyph__ #ifdef __cplusplus /** * FTTextureGlyph is a specialisation of FTGlyph for creating texture * glyphs. */ class FTGL_EXPORT FTTextureGlyph : public FTGlyph { public: /** * Constructor * * @param glyph The Freetype glyph to be processed * @param id The id of the texture that this glyph will be * drawn in * @param xOffset The x offset into the parent texture to draw * this glyph * @param yOffset The y offset into the parent texture to draw * this glyph * @param width The width of the parent texture * @param height The height (number of rows) of the parent texture */ FTTextureGlyph(FT_GlyphSlot glyph, int id, int xOffset, int yOffset, int width, int height); /** * Destructor */ virtual ~FTTextureGlyph(); /** * Render this glyph at the current pen position. * * @param pen The current pen position. * @param renderMode Render mode to display * @return The advance distance for this glyph. */ virtual const FTPoint& Render(const FTPoint& pen, int renderMode); }; #endif //__cplusplus FTGL_BEGIN_C_DECLS /** * Create a specialisation of FTGLglyph for creating pixmaps. * * @param glyph The Freetype glyph to be processed. * @param id The id of the texture that this glyph will be drawn in. * @param xOffset The x offset into the parent texture to draw this glyph. * @param yOffset The y offset into the parent texture to draw this glyph. * @param width The width of the parent texture. * @param height The height (number of rows) of the parent texture. * @return An FTGLglyph* object. */ FTGL_EXPORT FTGLglyph *ftglCreateTextureGlyph(FT_GlyphSlot glyph, int id, int xOffset, int yOffset, int width, int height); FTGL_END_C_DECLS #endif // __FTTextureGlyph__ connectome-workbench-1.2.3+git41-gc4c6c90/src/FtglFont/FTGL/ftgl.h000066400000000000000000000102011300200146000241220ustar00rootroot00000000000000/* * FTGL - OpenGL font library * * Copyright (c) 2001-2004 Henry Maddocks * Copyright (c) 2008 Sam Hocevar * Copyright (c) 2008 Sean Morrison * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #ifndef __ftgl__ #define __ftgl__ /* We need the Freetype headers */ #include #include FT_FREETYPE_H #include FT_GLYPH_H #include FT_OUTLINE_H /* Floating point types used by the library */ typedef double FTGL_DOUBLE; typedef float FTGL_FLOAT; /* Macros used to declare C-linkage types and symbols */ #ifdef __cplusplus # define FTGL_BEGIN_C_DECLS extern "C" { namespace FTGL { # define FTGL_END_C_DECLS } } #else # define FTGL_BEGIN_C_DECLS # define FTGL_END_C_DECLS #endif #ifdef __cplusplus namespace FTGL { typedef enum { RENDER_FRONT = 0x0001, RENDER_BACK = 0x0002, RENDER_SIDE = 0x0004, RENDER_ALL = 0xffff } RenderMode; typedef enum { ALIGN_LEFT = 0, ALIGN_CENTER = 1, ALIGN_RIGHT = 2, ALIGN_JUSTIFY = 3 } TextAlignment; } #else # define FTGL_RENDER_FRONT 0x0001 # define FTGL_RENDER_BACK 0x0002 # define FTGL_RENDER_SIDE 0x0004 # define FTGL_RENDER_ALL 0xffff # define FTGL_ALIGN_LEFT 0 # define FTGL_ALIGN_CENTER 1 # define FTGL_ALIGN_RIGHT 2 # define FTGL_ALIGN_JUSTIFY 3 #endif // Compiler-specific conditional compilation #ifdef _MSC_VER // MS Visual C++ // Disable various warning. // 4786: template name too long #pragma warning(disable : 4251) #pragma warning(disable : 4275) #pragma warning(disable : 4786) // The following definitions control how symbols are exported. // If the target is a static library ensure that FTGL_LIBRARY_STATIC // is defined. If building a dynamic library (ie DLL) ensure the // FTGL_LIBRARY macro is defined, as it will mark symbols for // export. If compiling a project to _use_ the _dynamic_ library // version of the library, no definition is required. #ifdef FTGL_LIBRARY_STATIC // static lib - no special export required # define FTGL_EXPORT #elif FTGL_LIBRARY // dynamic lib - must export/import symbols appropriately. # define FTGL_EXPORT __declspec(dllexport) #else # define FTGL_EXPORT __declspec(dllimport) #endif #else // Compiler that is not MS Visual C++. // Ensure that the export symbol is defined (and blank) #define FTGL_EXPORT #endif #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #endif // __ftgl__ connectome-workbench-1.2.3+git41-gc4c6c90/src/FtglFont/FTGlyph/000077500000000000000000000000001300200146000236045ustar00rootroot00000000000000connectome-workbench-1.2.3+git41-gc4c6c90/src/FtglFont/FTGlyph/FTBitmapGlyph.cpp000066400000000000000000000065141300200146000267700ustar00rootroot00000000000000/* * FTGL - OpenGL font library * * Copyright (c) 2001-2004 Henry Maddocks * Copyright (c) 2008 Sam Hocevar * Copyright (c) 2014 Washington University School of Medicine * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #include "FtglConfig.h" #include #include "FTGL/ftgl.h" #include "FTInternals.h" #include "FTBitmapGlyphImpl.h" // // FTGLBitmapGlyph // FTBitmapGlyph::FTBitmapGlyph(FT_GlyphSlot glyph) : FTGlyph(new FTBitmapGlyphImpl(glyph)) {} FTBitmapGlyph::~FTBitmapGlyph() {} const FTPoint& FTBitmapGlyph::Render(const FTPoint& pen, int renderMode) { FTBitmapGlyphImpl *myimpl = dynamic_cast(impl); return myimpl->RenderImpl(pen, renderMode); } // // FTGLBitmapGlyphImpl // FTBitmapGlyphImpl::FTBitmapGlyphImpl(FT_GlyphSlot glyph) : FTGlyphImpl(glyph), destWidth(0), destHeight(0), data(0) { err = FT_Render_Glyph(glyph, FT_RENDER_MODE_MONO); if(err || ft_glyph_format_bitmap != glyph->format) { return; } FT_Bitmap bitmap = glyph->bitmap; unsigned int srcWidth = bitmap.width; unsigned int srcHeight = bitmap.rows; unsigned int srcPitch = bitmap.pitch; destWidth = srcWidth; destHeight = srcHeight; destPitch = srcPitch; if(destWidth && destHeight) { data = new unsigned char[destPitch * destHeight]; unsigned char* dest = data + ((destHeight - 1) * destPitch); unsigned char* src = bitmap.buffer; for(unsigned int y = 0; y < srcHeight; ++y) { memcpy(dest, src, srcPitch); dest -= destPitch; src += srcPitch; } } pos = FTPoint(glyph->bitmap_left, static_cast(srcHeight) - glyph->bitmap_top, 0.0); } FTBitmapGlyphImpl::~FTBitmapGlyphImpl() { delete [] data; } const FTPoint& FTBitmapGlyphImpl::RenderImpl(const FTPoint& pen, int /*renderMode*/) { if(data) { float dx, dy; dx = pen.Xf() + pos.Xf(); dy = pen.Yf() - pos.Yf(); glBitmap(0, 0, 0.0f, 0.0f, dx, dy, (const GLubyte*)0); glPixelStorei(GL_UNPACK_ROW_LENGTH, destPitch * 8); glBitmap(destWidth, destHeight, 0.0f, 0.0, 0.0, 0.0, (const GLubyte*)data); glBitmap(0, 0, 0.0f, 0.0f, -dx, -dy, (const GLubyte*)0); } return advance; } connectome-workbench-1.2.3+git41-gc4c6c90/src/FtglFont/FTGlyph/FTBitmapGlyphImpl.h000066400000000000000000000041411300200146000272510ustar00rootroot00000000000000/* * FTGL - OpenGL font library * * Copyright (c) 2001-2004 Henry Maddocks * Copyright (c) 2008 Sam Hocevar * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #ifndef __FTBitmapGlyphImpl__ #define __FTBitmapGlyphImpl__ #include "FTGlyphImpl.h" class FTBitmapGlyphImpl : public FTGlyphImpl { friend class FTBitmapGlyph; protected: FTBitmapGlyphImpl(FT_GlyphSlot glyph); virtual ~FTBitmapGlyphImpl(); virtual const FTPoint& RenderImpl(const FTPoint& pen, int renderMode); private: /** * The width of the glyph 'image' */ unsigned int destWidth; /** * The height of the glyph 'image' */ unsigned int destHeight; /** * The pitch of the glyph 'image' */ unsigned int destPitch; /** * Vector from the pen position to the topleft corner of the bitmap */ FTPoint pos; /** * Pointer to the 'image' data */ unsigned char* data; }; #endif // __FTBitmapGlyphImpl__ connectome-workbench-1.2.3+git41-gc4c6c90/src/FtglFont/FTGlyph/FTBufferGlyph.cpp000066400000000000000000000063661300200146000267720ustar00rootroot00000000000000/* * FTGL - OpenGL font library * * Copyright (c) 2008 Sam Hocevar * Copyright (c) 2014 Washington University School of Medicine * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #include "FtglConfig.h" #include #include "FTGL/ftgl.h" #include "FTInternals.h" #include "FTBufferGlyphImpl.h" // // FTGLBufferGlyph // FTBufferGlyph::FTBufferGlyph(FT_GlyphSlot glyph, FTBuffer *buffer) : FTGlyph(new FTBufferGlyphImpl(glyph, buffer)) {} FTBufferGlyph::~FTBufferGlyph() {} const FTPoint& FTBufferGlyph::Render(const FTPoint& pen, int renderMode) { FTBufferGlyphImpl *myimpl = dynamic_cast(impl); return myimpl->RenderImpl(pen, renderMode); } // // FTGLBufferGlyphImpl // FTBufferGlyphImpl::FTBufferGlyphImpl(FT_GlyphSlot glyph, FTBuffer *p) : FTGlyphImpl(glyph), has_bitmap(false), buffer(p) { err = FT_Render_Glyph(glyph, FT_RENDER_MODE_NORMAL); if(err || glyph->format != ft_glyph_format_bitmap) { return; } bitmap = glyph->bitmap; pixels = new unsigned char[bitmap.pitch * bitmap.rows]; memcpy(pixels, bitmap.buffer, bitmap.pitch * bitmap.rows); if(bitmap.width && bitmap.rows) { has_bitmap = true; corner = FTPoint(glyph->bitmap_left, glyph->bitmap_top); } } FTBufferGlyphImpl::~FTBufferGlyphImpl() { delete[] pixels; } const FTPoint& FTBufferGlyphImpl::RenderImpl(const FTPoint& pen, int /*renderMode*/) { if(has_bitmap) { FTPoint pos(buffer->Pos() + pen + corner); int dx = (int)(pos.Xf() + 0.5f); 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++) { // 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++) { if(x + dx < 0 || x + dx >= buffer->Width()) continue; unsigned char p = pixels[y * bitmap.pitch + x]; if(p) { dest[y * buffer->Width() + x] = p; } } } } return advance; } connectome-workbench-1.2.3+git41-gc4c6c90/src/FtglFont/FTGlyph/FTBufferGlyphImpl.h000066400000000000000000000032621300200146000272510ustar00rootroot00000000000000/* * FTGL - OpenGL font library * * Copyright (c) 2008 Sam Hocevar * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #ifndef __FTBufferGlyphImpl__ #define __FTBufferGlyphImpl__ #include "FTGlyphImpl.h" class FTBufferGlyphImpl : public FTGlyphImpl { friend class FTBufferGlyph; protected: FTBufferGlyphImpl(FT_GlyphSlot glyph, FTBuffer *p); virtual ~FTBufferGlyphImpl(); virtual const FTPoint& RenderImpl(const FTPoint& pen, int renderMode); private: bool has_bitmap; FT_Bitmap bitmap; unsigned char *pixels; FTPoint corner; FTBuffer *buffer; }; #endif // __FTBufferGlyphImpl__ connectome-workbench-1.2.3+git41-gc4c6c90/src/FtglFont/FTGlyph/FTExtrudeGlyph.cpp000066400000000000000000000164201300200146000271710ustar00rootroot00000000000000/* * FTGL - OpenGL font library * * Copyright (c) 2001-2004 Henry Maddocks * Copyright (c) 2008 Sam Hocevar * Copyright (c) 2014 Washington University School of Medicine * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #include "FtglConfig.h" #include #include "FTGL/ftgl.h" #include "FTInternals.h" #include "FTExtrudeGlyphImpl.h" #include "FTVectoriser.h" // // FTGLExtrudeGlyph // FTExtrudeGlyph::FTExtrudeGlyph(FT_GlyphSlot glyph, float depth, float frontOutset, float backOutset, bool useDisplayList) : FTGlyph(new FTExtrudeGlyphImpl(glyph, depth, frontOutset, backOutset, useDisplayList)) {} FTExtrudeGlyph::~FTExtrudeGlyph() {} const FTPoint& FTExtrudeGlyph::Render(const FTPoint& pen, int renderMode) { FTExtrudeGlyphImpl *myimpl = dynamic_cast(impl); return myimpl->RenderImpl(pen, renderMode); } // // FTGLExtrudeGlyphImpl // FTExtrudeGlyphImpl::FTExtrudeGlyphImpl(FT_GlyphSlot glyph, float _depth, float _frontOutset, float _backOutset, bool useDisplayList) : FTGlyphImpl(glyph), vectoriser(0), glList(0) { bBox.SetDepth(-_depth); if(ft_glyph_format_outline != glyph->format) { err = 0x14; // Invalid_Outline return; } vectoriser = new FTVectoriser(glyph); if((vectoriser->ContourCount() < 1) || (vectoriser->PointCount() < 3)) { delete vectoriser; vectoriser = NULL; return; } hscale = glyph->face->size->metrics.x_ppem * 64; vscale = glyph->face->size->metrics.y_ppem * 64; depth = _depth; frontOutset = _frontOutset; backOutset = _backOutset; if(useDisplayList) { glList = glGenLists(3); /* Front face */ glNewList(glList + 0, GL_COMPILE); RenderFront(); glEndList(); /* Back face */ glNewList(glList + 1, GL_COMPILE); RenderBack(); glEndList(); /* Side face */ glNewList(glList + 2, GL_COMPILE); RenderSide(); glEndList(); delete vectoriser; vectoriser = NULL; } } FTExtrudeGlyphImpl::~FTExtrudeGlyphImpl() { if(glList) { glDeleteLists(glList, 3); } else if(vectoriser) { delete vectoriser; } } const FTPoint& FTExtrudeGlyphImpl::RenderImpl(const FTPoint& pen, int renderMode) { glTranslatef(pen.Xf(), pen.Yf(), pen.Zf()); if(glList) { if(renderMode & FTGL::RENDER_FRONT) glCallList(glList + 0); if(renderMode & FTGL::RENDER_BACK) glCallList(glList + 1); if(renderMode & FTGL::RENDER_SIDE) glCallList(glList + 2); } else if(vectoriser) { if(renderMode & FTGL::RENDER_FRONT) RenderFront(); if(renderMode & FTGL::RENDER_BACK) RenderBack(); if(renderMode & FTGL::RENDER_SIDE) RenderSide(); } glTranslatef(-pen.Xf(), -pen.Yf(), -pen.Zf()); return advance; } void FTExtrudeGlyphImpl::RenderFront() { vectoriser->MakeMesh(1.0, 1, frontOutset); glNormal3d(0.0, 0.0, 1.0); const FTMesh *mesh = vectoriser->GetMesh(); for(unsigned int j = 0; j < mesh->TesselationCount(); ++j) { const FTTesselation* subMesh = mesh->Tesselation(j); unsigned int polygonType = subMesh->PolygonType(); glBegin(polygonType); for(unsigned int i = 0; i < subMesh->PointCount(); ++i) { FTPoint pt = subMesh->Point(i); glTexCoord2f(pt.Xf() / hscale, pt.Yf() / vscale); glVertex3f(pt.Xf() / 64.0f, pt.Yf() / 64.0f, 0.0f); } glEnd(); } } void FTExtrudeGlyphImpl::RenderBack() { vectoriser->MakeMesh(-1.0, 2, backOutset); glNormal3d(0.0, 0.0, -1.0); const FTMesh *mesh = vectoriser->GetMesh(); for(unsigned int j = 0; j < mesh->TesselationCount(); ++j) { const FTTesselation* subMesh = mesh->Tesselation(j); unsigned int polygonType = subMesh->PolygonType(); glBegin(polygonType); for(unsigned int i = 0; i < subMesh->PointCount(); ++i) { //FTPoint pt = subMesh->Point(i); glTexCoord2f(subMesh->Point(i).Xf() / hscale, subMesh->Point(i).Yf() / vscale); glVertex3f(subMesh->Point(i).Xf() / 64.0f, subMesh->Point(i).Yf() / 64.0f, -depth); } glEnd(); } } void FTExtrudeGlyphImpl::RenderSide() { int contourFlag = vectoriser->ContourFlag(); for(size_t c = 0; c < vectoriser->ContourCount(); ++c) { const FTContour* contour = vectoriser->Contour(c); size_t n = contour->PointCount(); if(n < 2) { continue; } glBegin(GL_QUAD_STRIP); for(size_t j = 0; j <= n; ++j) { size_t cur = (j == n) ? 0 : j; size_t next = (cur == n - 1) ? 0 : cur + 1; FTPoint frontPt = contour->FrontPoint(cur); FTPoint nextPt = contour->FrontPoint(next); FTPoint backPt = contour->BackPoint(cur); FTPoint normal = FTPoint(0.f, 0.f, 1.f) ^ (frontPt - nextPt); if(normal != FTPoint(0.0f, 0.0f, 0.0f)) { glNormal3dv(static_cast(normal.Normalise())); } glTexCoord2f(frontPt.Xf() / hscale, frontPt.Yf() / vscale); if(contourFlag & ft_outline_reverse_fill) { glVertex3f(backPt.Xf() / 64.0f, backPt.Yf() / 64.0f, 0.0f); glVertex3f(frontPt.Xf() / 64.0f, frontPt.Yf() / 64.0f, -depth); } else { glVertex3f(backPt.Xf() / 64.0f, backPt.Yf() / 64.0f, -depth); glVertex3f(frontPt.Xf() / 64.0f, frontPt.Yf() / 64.0f, 0.0f); } } glEnd(); } } connectome-workbench-1.2.3+git41-gc4c6c90/src/FtglFont/FTGlyph/FTExtrudeGlyphImpl.h000066400000000000000000000042011300200146000274520ustar00rootroot00000000000000/* * FTGL - OpenGL font library * * Copyright (c) 2001-2004 Henry Maddocks * Copyright (c) 2008 Sam Hocevar * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #ifndef __FTExtrudeGlyphImpl__ #define __FTExtrudeGlyphImpl__ #include "FTGlyphImpl.h" class FTVectoriser; class FTExtrudeGlyphImpl : public FTGlyphImpl { friend class FTExtrudeGlyph; protected: FTExtrudeGlyphImpl(FT_GlyphSlot glyph, float depth, float frontOutset, float backOutset, bool useDisplayList); virtual ~FTExtrudeGlyphImpl(); virtual const FTPoint& RenderImpl(const FTPoint& pen, int renderMode); private: /** * Private rendering methods. */ void RenderFront(); void RenderBack(); void RenderSide(); /** * Private rendering variables. */ unsigned int hscale, vscale; float depth; float frontOutset, backOutset; FTVectoriser *vectoriser; /** * OpenGL display list */ GLuint glList; }; #endif // __FTExtrudeGlyphImpl__ connectome-workbench-1.2.3+git41-gc4c6c90/src/FtglFont/FTGlyph/FTGlyph.cpp000066400000000000000000000043431300200146000256310ustar00rootroot00000000000000/* * FTGL - OpenGL font library * * Copyright (c) 2001-2004 Henry Maddocks * Copyright (c) 2008 Sam Hocevar * Copyright (c) 2014 Washington University School of Medicine * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #include "FtglConfig.h" #include "FTGL/ftgl.h" #include "FTInternals.h" #include "FTGlyphImpl.h" // // FTGlyph // FTGlyph::FTGlyph(FT_GlyphSlot glyph) { impl = new FTGlyphImpl(glyph); } FTGlyph::FTGlyph(FTGlyphImpl *pImpl) { impl = pImpl; } FTGlyph::~FTGlyph() { delete impl; } float FTGlyph::Advance() const { return impl->Advance(); } const FTBBox& FTGlyph::BBox() const { return impl->BBox(); } FT_Error FTGlyph::Error() const { return impl->Error(); } // // FTGlyphImpl // FTGlyphImpl::FTGlyphImpl(FT_GlyphSlot glyph, bool /*useList*/) : err(0) { if(glyph) { bBox = FTBBox(glyph); advance = FTPoint(glyph->advance.x / 64.0f, glyph->advance.y / 64.0f); } } FTGlyphImpl::~FTGlyphImpl() {} float FTGlyphImpl::Advance() const { return advance.Xf(); } const FTBBox& FTGlyphImpl::BBox() const { return bBox; } FT_Error FTGlyphImpl::Error() const { return err; } connectome-workbench-1.2.3+git41-gc4c6c90/src/FtglFont/FTGlyph/FTGlyphGlue.cpp000066400000000000000000000147401300200146000264500ustar00rootroot00000000000000/* * FTGL - OpenGL font library * * Copyright (c) 2001-2004 Henry Maddocks * Copyright (c) 2008 Sam Hocevar * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #include "FtglConfig.h" #include "FTGL/ftgl.h" #include "FTInternals.h" static const FTPoint static_ftpoint; static const FTBBox static_ftbbox; FTGL_BEGIN_C_DECLS #define C_TOR(cname, cargs, cxxname, cxxarg, cxxtype) \ FTGLglyph* cname cargs \ { \ cxxname *g = new cxxname cxxarg; \ if(g->Error()) \ { \ delete g; \ return NULL; \ } \ FTGLglyph *ftgl = (FTGLglyph *)malloc(sizeof(FTGLglyph)); \ ftgl->ptr = g; \ ftgl->type = cxxtype; \ return ftgl; \ } // FTBitmapGlyph::FTBitmapGlyph(); C_TOR(ftglCreateBitmapGlyph, (FT_GlyphSlot glyph), FTBitmapGlyph, (glyph), GLYPH_BITMAP); // FTBufferGlyph::FTBufferGlyph(); // FIXME: not implemented // FTExtrudeGlyph::FTExtrudeGlyph(); C_TOR(ftglCreateExtrudeGlyph, (FT_GlyphSlot glyph, float depth, float frontOutset, float backOutset, int useDisplayList), FTExtrudeGlyph, (glyph, depth, frontOutset, backOutset, (useDisplayList != 0)), GLYPH_EXTRUDE); // FTOutlineGlyph::FTOutlineGlyph(); C_TOR(ftglCreateOutlineGlyph, (FT_GlyphSlot glyph, float outset, int useDisplayList), FTOutlineGlyph, (glyph, outset, (useDisplayList != 0)), GLYPH_OUTLINE); // FTPixmapGlyph::FTPixmapGlyph(); C_TOR(ftglCreatePixmapGlyph, (FT_GlyphSlot glyph), FTPixmapGlyph, (glyph), GLYPH_PIXMAP); // FTPolygonGlyph::FTPolygonGlyph(); C_TOR(ftglCreatePolygonGlyph, (FT_GlyphSlot glyph, float outset, int useDisplayList), FTPolygonGlyph, (glyph, outset, (useDisplayList != 0)), GLYPH_POLYGON); // FTTextureGlyph::FTTextureGlyph(); C_TOR(ftglCreateTextureGlyph, (FT_GlyphSlot glyph, int id, int xOffset, int yOffset, int width, int height), FTTextureGlyph, (glyph, id, xOffset, yOffset, width, height), GLYPH_TEXTURE); // FTCustomGlyph::FTCustomGlyph(); class FTCustomGlyph : public FTGlyph { public: FTCustomGlyph(FTGLglyph *base, void *p, void (*render) (FTGLglyph *, void *, FTGL_DOUBLE, FTGL_DOUBLE, int, FTGL_DOUBLE *, FTGL_DOUBLE *), void (*destroy) (FTGLglyph *, void *)) : FTGlyph((FT_GlyphSlot)0), baseGlyph(base), data(p), renderCallback(render), destroyCallback(destroy) {} ~FTCustomGlyph() { destroyCallback(baseGlyph, data); } float Advance() const { return baseGlyph->ptr->Advance(); } const FTPoint& Render(const FTPoint& pen, int renderMode) { FTGL_DOUBLE advancex, advancey; renderCallback(baseGlyph, data, pen.X(), pen.Y(), renderMode, &advancex, &advancey); advance = FTPoint(advancex, advancey); return advance; } const FTBBox& BBox() const { return baseGlyph->ptr->BBox(); } FT_Error Error() const { return baseGlyph->ptr->Error(); } private: FTPoint advance; FTGLglyph *baseGlyph; void *data; void (*renderCallback) (FTGLglyph *, void *, FTGL_DOUBLE, FTGL_DOUBLE, int, FTGL_DOUBLE *, FTGL_DOUBLE *); void (*destroyCallback) (FTGLglyph *, void *); }; C_TOR(ftglCreateCustomGlyph, (FTGLglyph *base, void *data, void (*renderCallback) (FTGLglyph *, void *, FTGL_DOUBLE, FTGL_DOUBLE, int, FTGL_DOUBLE *, FTGL_DOUBLE *), void (*destroyCallback) (FTGLglyph *, void *)), FTCustomGlyph, (base, data, renderCallback, destroyCallback), GLYPH_CUSTOM); #define C_FUN(cret, cname, cargs, cxxerr, cxxname, cxxarg) \ cret cname cargs \ { \ if(!g || !g->ptr) \ { \ fprintf(stderr, "FTGL warning: NULL pointer in %s\n", #cname); \ cxxerr; \ } \ return g->ptr->cxxname cxxarg; \ } // FTGlyph::~FTGlyph(); void ftglDestroyGlyph(FTGLglyph *g) { if(!g || !g->ptr) { fprintf(stderr, "FTGL warning: NULL pointer in %s\n", __FUNCTION__); return; } delete g->ptr; free(g); } // const FTPoint& FTGlyph::Render(const FTPoint& pen, int renderMode); extern "C++" { C_FUN(static const FTPoint&, _ftglRenderGlyph, (FTGLglyph *g, const FTPoint& pen, int renderMode), return static_ftpoint, Render, (pen, renderMode)); } void ftglRenderGlyph(FTGLglyph *g, FTGL_DOUBLE penx, FTGL_DOUBLE peny, int renderMode, FTGL_DOUBLE *advancex, FTGL_DOUBLE *advancey) { FTPoint pen(penx, peny); FTPoint ret = _ftglRenderGlyph(g, pen, renderMode); *advancex = ret.X(); *advancey = ret.Y(); } // float FTGlyph::Advance() const; C_FUN(float, ftglGetGlyphAdvance, (FTGLglyph *g), return 0.0, Advance, ()); // const FTBBox& FTGlyph::BBox() const; extern "C++" { C_FUN(static const FTBBox&, _ftglGetGlyphBBox, (FTGLglyph *g), return static_ftbbox, BBox, ()); } void ftglGetGlyphBBox(FTGLglyph *g, float bounds[6]) { FTBBox ret = _ftglGetGlyphBBox(g); FTPoint lower = ret.Lower(), upper = ret.Upper(); bounds[0] = lower.Xf(); bounds[1] = lower.Yf(); bounds[2] = lower.Zf(); bounds[3] = upper.Xf(); bounds[4] = upper.Yf(); bounds[5] = upper.Zf(); } // FT_Error FTGlyph::Error() const; C_FUN(FT_Error, ftglGetGlyphError, (FTGLglyph *g), return -1, Error, ()); FTGL_END_C_DECLS connectome-workbench-1.2.3+git41-gc4c6c90/src/FtglFont/FTGlyph/FTGlyphImpl.h000066400000000000000000000035401300200146000261160ustar00rootroot00000000000000/* * FTGL - OpenGL font library * * Copyright (c) 2001-2004 Henry Maddocks * Copyright (c) 2008 Sam Hocevar * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #ifndef __FTGlyphImpl__ #define __FTGlyphImpl__ #include "FTGL/ftgl.h" class FTGlyphImpl { friend class FTGlyph; protected: FTGlyphImpl(FT_GlyphSlot glyph, bool useDisplayList = true); virtual ~FTGlyphImpl(); float Advance() const; const FTBBox& BBox() const; FT_Error Error() const; /** * The advance distance for this glyph */ FTPoint advance; /** * The bounding box of this glyph. */ FTBBox bBox; /** * Current error code. Zero means no error. */ FT_Error err; }; #endif // __FTGlyphImpl__ connectome-workbench-1.2.3+git41-gc4c6c90/src/FtglFont/FTGlyph/FTOutlineGlyph.cpp000066400000000000000000000074351300200146000271760ustar00rootroot00000000000000/* * FTGL - OpenGL font library * * Copyright (c) 2001-2004 Henry Maddocks * Copyright (c) 2008 Éric Beets * Copyright (c) 2008 Sam Hocevar * Copyright (c) 2014 Washington University School of Medicine * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #include "FtglConfig.h" #include "FTGL/ftgl.h" #include "FTInternals.h" #include "FTOutlineGlyphImpl.h" #include "FTVectoriser.h" // // FTGLOutlineGlyph // FTOutlineGlyph::FTOutlineGlyph(FT_GlyphSlot glyph, float outset, bool useDisplayList) : FTGlyph(new FTOutlineGlyphImpl(glyph, outset, useDisplayList)) {} FTOutlineGlyph::~FTOutlineGlyph() {} const FTPoint& FTOutlineGlyph::Render(const FTPoint& pen, int renderMode) { FTOutlineGlyphImpl *myimpl = dynamic_cast(impl); return myimpl->RenderImpl(pen, renderMode); } // // FTGLOutlineGlyphImpl // FTOutlineGlyphImpl::FTOutlineGlyphImpl(FT_GlyphSlot glyph, float _outset, bool useDisplayList) : FTGlyphImpl(glyph), glList(0) { if(ft_glyph_format_outline != glyph->format) { err = 0x14; // Invalid_Outline return; } vectoriser = new FTVectoriser(glyph); if((vectoriser->ContourCount() < 1) || (vectoriser->PointCount() < 3)) { delete vectoriser; vectoriser = NULL; return; } outset = _outset; if(useDisplayList) { glList = glGenLists(1); glNewList(glList, GL_COMPILE); DoRender(); glEndList(); delete vectoriser; vectoriser = NULL; } } FTOutlineGlyphImpl::~FTOutlineGlyphImpl() { if(glList) { glDeleteLists(glList, 1); } else if(vectoriser) { delete vectoriser; } } const FTPoint& FTOutlineGlyphImpl::RenderImpl(const FTPoint& pen, int /*renderMode*/) { glTranslatef(pen.Xf(), pen.Yf(), pen.Zf()); if(glList) { glCallList(glList); } else if(vectoriser) { DoRender(); } glTranslatef(-pen.Xf(), -pen.Yf(), -pen.Zf()); return advance; } void FTOutlineGlyphImpl::DoRender() { for(unsigned int c = 0; c < vectoriser->ContourCount(); ++c) { const FTContour* contour = vectoriser->Contour(c); glBegin(GL_LINE_LOOP); for(unsigned int i = 0; i < contour->PointCount(); ++i) { FTPoint point = FTPoint(contour->Point(i).X() + contour->Outset(i).X() * outset, contour->Point(i).Y() + contour->Outset(i).Y() * outset, 0); glVertex2f(point.Xf() / 64.0f, point.Yf() / 64.0f); } glEnd(); } } connectome-workbench-1.2.3+git41-gc4c6c90/src/FtglFont/FTGlyph/FTOutlineGlyphImpl.h000066400000000000000000000040301300200146000274510ustar00rootroot00000000000000/* * FTGL - OpenGL font library * * Copyright (c) 2001-2004 Henry Maddocks * Copyright (c) 2008 Sam Hocevar * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #ifndef __FTOutlineGlyphImpl__ #define __FTOutlineGlyphImpl__ #include "FTGlyphImpl.h" class FTVectoriser; class FTOutlineGlyphImpl : public FTGlyphImpl { friend class FTOutlineGlyph; protected: FTOutlineGlyphImpl(FT_GlyphSlot glyph, float outset, bool useDisplayList); virtual ~FTOutlineGlyphImpl(); virtual const FTPoint& RenderImpl(const FTPoint& pen, int renderMode); private: /** * Private rendering method. */ void DoRender(); /** * Private rendering variables. */ FTVectoriser *vectoriser; /** * Private rendering variables. */ float outset; /** * OpenGL display list */ GLuint glList; }; #endif // __FTOutlineGlyphImpl__ connectome-workbench-1.2.3+git41-gc4c6c90/src/FtglFont/FTGlyph/FTPixmapGlyph.cpp000066400000000000000000000070761300200146000270160ustar00rootroot00000000000000/* * FTGL - OpenGL font library * * Copyright (c) 2001-2004 Henry Maddocks * Copyright (c) 2008 Sam Hocevar * Copyright (c) 2014 Washington University School of Medicine * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #include "FtglConfig.h" #include #include "FTGL/ftgl.h" #include "FTInternals.h" #include "FTPixmapGlyphImpl.h" // // FTGLPixmapGlyph // FTPixmapGlyph::FTPixmapGlyph(FT_GlyphSlot glyph) : FTGlyph(new FTPixmapGlyphImpl(glyph)) {} FTPixmapGlyph::~FTPixmapGlyph() {} const FTPoint& FTPixmapGlyph::Render(const FTPoint& pen, int renderMode) { FTPixmapGlyphImpl *myimpl = dynamic_cast(impl); return myimpl->RenderImpl(pen, renderMode); } // // FTGLPixmapGlyphImpl // FTPixmapGlyphImpl::FTPixmapGlyphImpl(FT_GlyphSlot glyph) : FTGlyphImpl(glyph), destWidth(0), destHeight(0), data(0) { err = FT_Render_Glyph(glyph, FT_RENDER_MODE_NORMAL); if(err || ft_glyph_format_bitmap != glyph->format) { return; } FT_Bitmap bitmap = glyph->bitmap; //check the pixel mode //ft_pixel_mode_grays int srcWidth = bitmap.width; int srcHeight = bitmap.rows; destWidth = srcWidth; destHeight = srcHeight; if(destWidth && destHeight) { data = new unsigned char[destWidth * destHeight * 2]; unsigned char* src = bitmap.buffer; unsigned char* dest = data + ((destHeight - 1) * destWidth * 2); size_t destStep = destWidth * 2 * 2; for(int y = 0; y < srcHeight; ++y) { for(int x = 0; x < srcWidth; ++x) { *dest++ = static_cast(255); *dest++ = *src++; } dest -= destStep; } destHeight = srcHeight; } pos.X(glyph->bitmap_left); pos.Y(srcHeight - glyph->bitmap_top); } FTPixmapGlyphImpl::~FTPixmapGlyphImpl() { delete [] data; } const FTPoint& FTPixmapGlyphImpl::RenderImpl(const FTPoint& pen, int /*renderMode*/) { if(data) { float dx, dy; dx = floor(pen.Xf() + pos.Xf()); dy = floor(pen.Yf() - pos.Yf()); glBitmap(0, 0, 0.0f, 0.0f, dx, dy, (const GLubyte*)0); glPixelStorei(GL_UNPACK_ROW_LENGTH, 0); glPixelStorei(GL_UNPACK_ALIGNMENT, 2); glDrawPixels(destWidth, destHeight, GL_LUMINANCE_ALPHA, GL_UNSIGNED_BYTE, (const GLvoid*)data); glBitmap(0, 0, 0.0f, 0.0f, -dx, -dy, (const GLubyte*)0); } return advance; } connectome-workbench-1.2.3+git41-gc4c6c90/src/FtglFont/FTGlyph/FTPixmapGlyphImpl.h000066400000000000000000000037551300200146000273050ustar00rootroot00000000000000/* * FTGL - OpenGL font library * * Copyright (c) 2001-2004 Henry Maddocks * Copyright (c) 2008 Sam Hocevar * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #ifndef __FTPixmapGlyphImpl__ #define __FTPixmapGlyphImpl__ #include "FTGlyphImpl.h" class FTPixmapGlyphImpl : public FTGlyphImpl { friend class FTPixmapGlyph; protected: FTPixmapGlyphImpl(FT_GlyphSlot glyph); virtual ~FTPixmapGlyphImpl(); virtual const FTPoint& RenderImpl(const FTPoint& pen, int renderMode); private: /** * The width of the glyph 'image' */ int destWidth; /** * The height of the glyph 'image' */ int destHeight; /** * Vector from the pen position to the topleft corner of the pixmap */ FTPoint pos; /** * Pointer to the 'image' data */ unsigned char* data; }; #endif // __FTPixmapGlyphImpl__ connectome-workbench-1.2.3+git41-gc4c6c90/src/FtglFont/FTGlyph/FTPolygonGlyph.cpp000066400000000000000000000076531300200146000272100ustar00rootroot00000000000000/* * FTGL - OpenGL font library * * Copyright (c) 2001-2004 Henry Maddocks * Copyright (c) 2008 Éric Beets * Copyright (c) 2008 Sam Hocevar * Copyright (c) 2014 Washington University School of Medicine * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #include "FtglConfig.h" #include "FTGL/ftgl.h" #include "FTInternals.h" #include "FTPolygonGlyphImpl.h" #include "FTVectoriser.h" // // FTGLPolyGlyph // FTPolygonGlyph::FTPolygonGlyph(FT_GlyphSlot glyph, float outset, bool useDisplayList) : FTGlyph(new FTPolygonGlyphImpl(glyph, outset, useDisplayList)) {} FTPolygonGlyph::~FTPolygonGlyph() {} const FTPoint& FTPolygonGlyph::Render(const FTPoint& pen, int renderMode) { FTPolygonGlyphImpl *myimpl = dynamic_cast(impl); return myimpl->RenderImpl(pen, renderMode); } // // FTGLPolyGlyphImpl // FTPolygonGlyphImpl::FTPolygonGlyphImpl(FT_GlyphSlot glyph, float _outset, bool useDisplayList) : FTGlyphImpl(glyph), glList(0) { if(ft_glyph_format_outline != glyph->format) { err = 0x14; // Invalid_Outline return; } vectoriser = new FTVectoriser(glyph); if((vectoriser->ContourCount() < 1) || (vectoriser->PointCount() < 3)) { delete vectoriser; vectoriser = NULL; return; } hscale = glyph->face->size->metrics.x_ppem * 64; vscale = glyph->face->size->metrics.y_ppem * 64; outset = _outset; if(useDisplayList) { glList = glGenLists(1); glNewList(glList, GL_COMPILE); DoRender(); glEndList(); delete vectoriser; vectoriser = NULL; } } FTPolygonGlyphImpl::~FTPolygonGlyphImpl() { if(glList) { glDeleteLists(glList, 1); } else if(vectoriser) { delete vectoriser; } } const FTPoint& FTPolygonGlyphImpl::RenderImpl(const FTPoint& pen, int /*renderMode*/) { glTranslatef(pen.Xf(), pen.Yf(), pen.Zf()); if(glList) { glCallList(glList); } else if(vectoriser) { DoRender(); } glTranslatef(-pen.Xf(), -pen.Yf(), -pen.Zf()); return advance; } void FTPolygonGlyphImpl::DoRender() { vectoriser->MakeMesh(1.0, 1, outset); const FTMesh *mesh = vectoriser->GetMesh(); for(unsigned int t = 0; t < mesh->TesselationCount(); ++t) { const FTTesselation* subMesh = mesh->Tesselation(t); unsigned int polygonType = subMesh->PolygonType(); glBegin(polygonType); for(unsigned int i = 0; i < subMesh->PointCount(); ++i) { FTPoint point = subMesh->Point(i); glTexCoord2f(point.Xf() / hscale, point.Yf() / vscale); glVertex3f(point.Xf() / 64.0f, point.Yf() / 64.0f, 0.0f); } glEnd(); } } connectome-workbench-1.2.3+git41-gc4c6c90/src/FtglFont/FTGlyph/FTPolygonGlyphImpl.h000066400000000000000000000037721300200146000274750ustar00rootroot00000000000000/* * FTGL - OpenGL font library * * Copyright (c) 2001-2004 Henry Maddocks * Copyright (c) 2008 Sam Hocevar * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #ifndef __FTPolygonGlyphImpl__ #define __FTPolygonGlyphImpl__ #include "FTGlyphImpl.h" class FTVectoriser; class FTPolygonGlyphImpl : public FTGlyphImpl { friend class FTPolygonGlyph; public: FTPolygonGlyphImpl(FT_GlyphSlot glyph, float outset, bool useDisplayList); virtual ~FTPolygonGlyphImpl(); virtual const FTPoint& RenderImpl(const FTPoint& pen, int renderMode); private: /** * Private rendering method. */ void DoRender(); /** * Private rendering variables. */ unsigned int hscale, vscale; FTVectoriser *vectoriser; float outset; /** * OpenGL display list */ GLuint glList; }; #endif // __FTPolygonGlyphImpl__ connectome-workbench-1.2.3+git41-gc4c6c90/src/FtglFont/FTGlyph/FTTextureGlyph.cpp000066400000000000000000000104551300200146000272130ustar00rootroot00000000000000/* * FTGL - OpenGL font library * * Copyright (c) 2001-2004 Henry Maddocks * Copyright (c) 2008 Sam Hocevar * Copyright (c) 2014 Washington University School of Medicine * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #include "FtglConfig.h" #include #include "FTGL/ftgl.h" #include "FTInternals.h" #include "FTTextureGlyphImpl.h" // // FTGLTextureGlyph // FTTextureGlyph::FTTextureGlyph(FT_GlyphSlot glyph, int id, int xOffset, int yOffset, int width, int height) : FTGlyph(new FTTextureGlyphImpl(glyph, id, xOffset, yOffset, width, height)) {} FTTextureGlyph::~FTTextureGlyph() {} const FTPoint& FTTextureGlyph::Render(const FTPoint& pen, int renderMode) { FTTextureGlyphImpl *myimpl = dynamic_cast(impl); return myimpl->RenderImpl(pen, renderMode); } // // FTGLTextureGlyphImpl // GLint FTTextureGlyphImpl::activeTextureID = 0; FTTextureGlyphImpl::FTTextureGlyphImpl(FT_GlyphSlot glyph, int id, int xOffset, int yOffset, int width, int height) : FTGlyphImpl(glyph), destWidth(0), destHeight(0), glTextureID(id) { /* FIXME: need to propagate the render mode all the way down to * here in order to get FT_RENDER_MODE_MONO aliased fonts. */ err = FT_Render_Glyph(glyph, FT_RENDER_MODE_NORMAL); if(err || glyph->format != ft_glyph_format_bitmap) { return; } FT_Bitmap bitmap = glyph->bitmap; destWidth = bitmap.width; destHeight = bitmap.rows; if(destWidth && destHeight) { glPushClientAttrib(GL_CLIENT_PIXEL_STORE_BIT); glPixelStorei(GL_UNPACK_LSB_FIRST, GL_FALSE); glPixelStorei(GL_UNPACK_ROW_LENGTH, 0); glPixelStorei(GL_UNPACK_ALIGNMENT, 1); glBindTexture(GL_TEXTURE_2D, glTextureID); glTexSubImage2D(GL_TEXTURE_2D, 0, xOffset, yOffset, destWidth, destHeight, GL_ALPHA, GL_UNSIGNED_BYTE, bitmap.buffer); glPopClientAttrib(); } // 0 // +----+ // | | // | | // | | // +----+ // 1 uv[0].X(static_cast(xOffset) / static_cast(width)); uv[0].Y(static_cast(yOffset) / static_cast(height)); uv[1].X(static_cast(xOffset + destWidth) / static_cast(width)); uv[1].Y(static_cast(yOffset + destHeight) / static_cast(height)); corner = FTPoint(glyph->bitmap_left, glyph->bitmap_top); } FTTextureGlyphImpl::~FTTextureGlyphImpl() {} const FTPoint& FTTextureGlyphImpl::RenderImpl(const FTPoint& pen, int /*renderMode*/) { float dx, dy; if(activeTextureID != glTextureID) { glBindTexture(GL_TEXTURE_2D, (GLuint)glTextureID); activeTextureID = glTextureID; } dx = floor(pen.Xf() + corner.Xf()); dy = floor(pen.Yf() + corner.Yf()); glBegin(GL_QUADS); glTexCoord2f(uv[0].Xf(), uv[0].Yf()); glVertex2f(dx, dy); glTexCoord2f(uv[0].Xf(), uv[1].Yf()); glVertex2f(dx, dy - destHeight); glTexCoord2f(uv[1].Xf(), uv[1].Yf()); glVertex2f(dx + destWidth, dy - destHeight); glTexCoord2f(uv[1].Xf(), uv[0].Yf()); glVertex2f(dx + destWidth, dy); glEnd(); return advance; } connectome-workbench-1.2.3+git41-gc4c6c90/src/FtglFont/FTGlyph/FTTextureGlyphImpl.h000066400000000000000000000053771300200146000275110ustar00rootroot00000000000000/* * FTGL - OpenGL font library * * Copyright (c) 2001-2004 Henry Maddocks * Copyright (c) 2008 Sam Hocevar * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #ifndef __FTTextureGlyphImpl__ #define __FTTextureGlyphImpl__ #include "FTGlyphImpl.h" class FTTextureGlyphImpl : public FTGlyphImpl { friend class FTTextureGlyph; friend class FTTextureFontImpl; protected: FTTextureGlyphImpl(FT_GlyphSlot glyph, int id, int xOffset, int yOffset, int width, int height); virtual ~FTTextureGlyphImpl(); virtual const FTPoint& RenderImpl(const FTPoint& pen, int renderMode); private: /** * Reset the currently active texture to zero to get into a known * state before drawing a string. This is to get round possible * threading issues. */ static void ResetActiveTexture() { activeTextureID = 0; } /** * The width of the glyph 'image' */ int destWidth; /** * The height of the glyph 'image' */ int destHeight; /** * Vector from the pen position to the topleft corner of the pixmap */ FTPoint corner; /** * The texture co-ords of this glyph within the texture. */ FTPoint uv[2]; /** * The texture index that this glyph is contained in. */ int glTextureID; /** * The texture index of the currently active texture * * We keep track of the currently active texture to try to reduce the * number of texture bind operations. */ static GLint activeTextureID; }; #endif // __FTTextureGlyphImpl__ connectome-workbench-1.2.3+git41-gc4c6c90/src/FtglFont/FTGlyphContainer.cpp000066400000000000000000000064611300200146000261620ustar00rootroot00000000000000/* * FTGL - OpenGL font library * * Copyright (c) 2001-2004 Henry Maddocks * Copyright (c) 2008 Sam Hocevar * Copyright (c) 2014 Washington University School of Medicine * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #include "FtglConfig.h" #include "FTGL/ftgl.h" #include "FTGlyphContainer.h" #include "FTFace.h" #include "FTCharmap.h" FTGlyphContainer::FTGlyphContainer(FTFace* f) : face(f), err(0) { glyphs.push_back(NULL); charMap = new FTCharmap(face); } FTGlyphContainer::~FTGlyphContainer() { GlyphVector::iterator it; for(it = glyphs.begin(); it != glyphs.end(); ++it) { delete *it; } glyphs.clear(); delete charMap; } bool FTGlyphContainer::CharMap(FT_Encoding encoding) { bool result = charMap->CharMap(encoding); err = charMap->Error(); return result; } unsigned int FTGlyphContainer::FontIndex(const unsigned int charCode) const { return charMap->FontIndex(charCode); } void FTGlyphContainer::Add(FTGlyph* tempGlyph, const unsigned int charCode) { charMap->InsertIndex(charCode, glyphs.size()); glyphs.push_back(tempGlyph); } const FTGlyph* /*const*/ FTGlyphContainer::Glyph(const unsigned int charCode) const { unsigned int index = charMap->GlyphListIndex(charCode); return glyphs[index]; } FTBBox FTGlyphContainer::BBox(const unsigned int charCode) const { return Glyph(charCode)->BBox(); } float FTGlyphContainer::Advance(const unsigned int charCode, const unsigned int nextCharCode) { unsigned int left = charMap->FontIndex(charCode); unsigned int right = charMap->FontIndex(nextCharCode); return face->KernAdvance(left, right).Xf() + Glyph(charCode)->Advance(); } FTPoint FTGlyphContainer::Render(const unsigned int charCode, const unsigned int nextCharCode, FTPoint penPosition, int renderMode) { unsigned int left = charMap->FontIndex(charCode); unsigned int right = charMap->FontIndex(nextCharCode); FTPoint kernAdvance = face->KernAdvance(left, right); if(!face->Error()) { unsigned int index = charMap->GlyphListIndex(charCode); kernAdvance += glyphs[index]->Render(penPosition, renderMode); } return kernAdvance; } connectome-workbench-1.2.3+git41-gc4c6c90/src/FtglFont/FTGlyphContainer.h000066400000000000000000000116201300200146000256200ustar00rootroot00000000000000/* * FTGL - OpenGL font library * * Copyright (c) 2001-2004 Henry Maddocks * Copyright (c) 2014 Washington University School of Medicine * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #ifndef __FTGlyphContainer__ #define __FTGlyphContainer__ #include #include FT_FREETYPE_H #include FT_GLYPH_H #include "FTGL/ftgl.h" #include "FTVector.h" class FTFace; class FTGlyph; class FTCharmap; /** * FTGlyphContainer holds the post processed FTGlyph objects. * * @see FTGlyph */ class FTGlyphContainer { typedef FTVector GlyphVector; public: /** * Constructor * * @param face The Freetype face */ FTGlyphContainer(FTFace* face); /** * Destructor */ ~FTGlyphContainer(); /** * Sets the character map for the face. * * @param encoding the Freetype encoding symbol. See above. * @return true if charmap was valid * and set correctly */ bool CharMap(FT_Encoding encoding); /** * Get the font index of the input character. * * @param characterCode The character code of the requested glyph in the * current encoding eg apple roman. * @return The font index for the character. */ unsigned int FontIndex(const unsigned int characterCode) const; /** * Adds a glyph to this glyph list. * * @param glyph The FTGlyph to be inserted into the container * @param characterCode The char code of the glyph NOT the glyph index. */ void Add(FTGlyph* glyph, const unsigned int characterCode); /** * Get a glyph from the glyph list * * @param characterCode The char code of the glyph NOT the glyph index * @return An FTGlyph or null is it hasn't been * loaded. */ const FTGlyph* /*const*/ Glyph(const unsigned int characterCode) const; /** * Get the bounding box for a character. * @param characterCode The char code of the glyph NOT the glyph index */ FTBBox BBox(const unsigned int characterCode) const; /** * Returns the kerned advance width for a glyph. * * @param characterCode glyph index of the character * @param nextCharacterCode the next glyph in a string * @return advance width */ float Advance(const unsigned int characterCode, const unsigned int nextCharacterCode); /** * Renders a character * @param characterCode the glyph to be Rendered * @param nextCharacterCode the next glyph in the string. Used for kerning. * @param penPosition the position to Render the glyph * @param renderMode Render mode to display * @return The distance to advance the pen position after Rendering */ FTPoint Render(const unsigned int characterCode, const unsigned int nextCharacterCode, FTPoint penPosition, int renderMode); /** * Queries the Font for errors. * * @return The current error code. */ FT_Error Error() const { return err; } private: /** * The FTGL face */ FTFace* face; /** * The Character Map object associated with the current face */ FTCharmap* charMap; /** * A structure to hold the glyphs */ GlyphVector glyphs; /** * Current error code. Zero means no error. */ FT_Error err; }; #endif // __FTGlyphContainer__ connectome-workbench-1.2.3+git41-gc4c6c90/src/FtglFont/FTInternals.h000066400000000000000000000066101300200146000246340ustar00rootroot00000000000000/* * FTGL - OpenGL font library * * Copyright (c) 2001-2004 Henry Maddocks * Copyright (c) 2008 Éric Beets * Copyright (c) 2008 Sam Hocevar * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #ifndef __FTINTERNALS_H__ #define __FTINTERNALS_H__ #include "FTGL/ftgl.h" #include #include // Fixes for deprecated identifiers in 2.1.5 #ifndef FT_OPEN_MEMORY #define FT_OPEN_MEMORY (FT_Open_Flags)1 #endif #ifndef FT_RENDER_MODE_MONO #define FT_RENDER_MODE_MONO ft_render_mode_mono #endif #ifndef FT_RENDER_MODE_NORMAL #define FT_RENDER_MODE_NORMAL ft_render_mode_normal #endif #ifdef WIN32 // Under windows avoid including is overrated. // Sure, it can be avoided and "name space pollution" can be // avoided, but why? It really doesn't make that much difference // these days. #define WIN32_LEAN_AND_MEAN #include #ifndef __gl_h_ #include #include #endif #else // Non windows platforms - don't require nonsense as seen above :-) #ifndef __gl_h_ #ifdef SDL_main #include "SDL_opengl.h" #elif __APPLE_CC__ #include #include #else #include #if defined (__sun__) && !defined (__sparc__) #include #else #include #endif #endif #endif // Required for compatibility with glext.h style function definitions of // OpenGL extensions, such as in src/osg/Point.cpp. #ifndef APIENTRY #define APIENTRY #endif #endif FTGL_BEGIN_C_DECLS typedef enum { GLYPH_CUSTOM, GLYPH_BITMAP, GLYPH_BUFFER, GLYPH_PIXMAP, GLYPH_OUTLINE, GLYPH_POLYGON, GLYPH_EXTRUDE, GLYPH_TEXTURE, } GlyphType; struct _FTGLglyph { FTGlyph *ptr; FTGL::GlyphType type; }; typedef enum { FONT_CUSTOM, FONT_BITMAP, FONT_BUFFER, FONT_PIXMAP, FONT_OUTLINE, FONT_POLYGON, FONT_EXTRUDE, FONT_TEXTURE, } FontType; struct _FTGLfont { FTFont *ptr; FTGL::FontType type; }; typedef enum { LAYOUT_SIMPLE, } LayoutType; struct _FTGLlayout { FTLayout *ptr; FTGLfont *font; FTGL::LayoutType type; }; FTGL_END_C_DECLS #endif //__FTINTERNALS_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/FtglFont/FTLayout/000077500000000000000000000000001300200146000237765ustar00rootroot00000000000000connectome-workbench-1.2.3+git41-gc4c6c90/src/FtglFont/FTLayout/FTLayout.cpp000066400000000000000000000032611300200146000262130ustar00rootroot00000000000000/* * FTGL - OpenGL font library * * Copyright (c) 2001-2004 Henry Maddocks * Copyright (c) 2008 Sam Hocevar * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #include "FtglConfig.h" #include "FTGL/ftgl.h" #include "../FTFont/FTFontImpl.h" #include "./FTLayoutImpl.h" // // FTLayout // FTLayout::FTLayout() { impl = new FTLayoutImpl(); } FTLayout::FTLayout(FTLayoutImpl *pImpl) { impl = pImpl; } FTLayout::~FTLayout() { delete impl; } FT_Error FTLayout::Error() const { return impl->err; } // // FTLayoutImpl // FTLayoutImpl::FTLayoutImpl() : err(0) { ; } FTLayoutImpl::~FTLayoutImpl() { ; } connectome-workbench-1.2.3+git41-gc4c6c90/src/FtglFont/FTLayout/FTLayoutGlue.cpp000066400000000000000000000125551300200146000270360ustar00rootroot00000000000000/* * FTGL - OpenGL font library * * Copyright (c) 2001-2004 Henry Maddocks * Copyright (c) 2008 Éric Beets * Copyright (c) 2008 Sam Hocevar * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #include "FtglConfig.h" #include "FTInternals.h" static const FTBBox static_ftbbox; FTGL_BEGIN_C_DECLS #define C_TOR(cname, cargs, cxxname, cxxarg, cxxtype) \ FTGLlayout* cname cargs \ { \ cxxname *l = new cxxname cxxarg; \ if(l->Error()) \ { \ delete l; \ return NULL; \ } \ FTGLlayout *ftgl = (FTGLlayout *)malloc(sizeof(FTGLlayout)); \ ftgl->ptr = l; \ ftgl->type = cxxtype; \ return ftgl; \ } // FTSimpleLayout::FTSimpleLayout(); C_TOR(ftglCreateSimpleLayout, (), FTSimpleLayout, (), LAYOUT_SIMPLE); #define C_FUN(cret, cname, cargs, cxxerr, cxxname, cxxarg) \ cret cname cargs \ { \ if(!l || !l->ptr) \ { \ fprintf(stderr, "FTGL warning: NULL pointer in %s\n", #cname); \ cxxerr; \ } \ return l->ptr->cxxname cxxarg; \ } // FTLayout::~FTLayout(); void ftglDestroyLayout(FTGLlayout *l) { if(!l || !l->ptr) { fprintf(stderr, "FTGL warning: NULL pointer in %s\n", __FUNCTION__); return; } delete l->ptr; free(l); } // virtual FTBBox FTLayout::BBox(const char* string) extern "C++" { C_FUN(static FTBBox, _ftgGetlLayoutBBox, (FTGLlayout *l, const char *s), return static_ftbbox, BBox, (s)); } void ftgGetlLayoutBBox(FTGLlayout *l, const char * s, float c[6]) { FTBBox ret = _ftgGetlLayoutBBox(l, s); FTPoint lower = ret.Lower(), upper = ret.Upper(); c[0] = lower.Xf(); c[1] = lower.Yf(); c[2] = lower.Zf(); c[3] = upper.Xf(); c[4] = upper.Yf(); c[5] = upper.Zf(); } // virtual void FTLayout::Render(const char* string, int renderMode); C_FUN(void, ftglRenderLayout, (FTGLlayout *l, const char *s, int r), return, Render, (s, r)); // FT_Error FTLayout::Error() const; C_FUN(FT_Error, ftglGetLayoutError, (FTGLlayout *l), return -1, Error, ()); // void FTSimpleLayout::SetFont(FTFont *fontInit) void ftglSetLayoutFont(FTGLlayout *l, FTGLfont *font) { if(!l || !l->ptr) { fprintf(stderr, "FTGL warning: NULL pointer in %s\n", __FUNCTION__); return; } if(l->type != FTGL::LAYOUT_SIMPLE) { fprintf(stderr, "FTGL warning: %s not implemented for %d\n", __FUNCTION__, l->type); } l->font = font; return dynamic_cast(l->ptr)->SetFont(font->ptr); } // FTFont *FTSimpleLayout::GetFont() FTGLfont *ftglGetLayoutFont(FTGLlayout *l) { if(!l || !l->ptr) { fprintf(stderr, "FTGL warning: NULL pointer in %s\n", __FUNCTION__); return NULL; } if(l->type != FTGL::LAYOUT_SIMPLE) { fprintf(stderr, "FTGL warning: %s not implemented for %d\n", __FUNCTION__, l->type); } return l->font; } #undef C_FUN #define C_FUN(cret, cname, cargs, cxxerr, cxxname, cxxarg) \ cret cname cargs \ { \ if(!l || !l->ptr) \ { \ fprintf(stderr, "FTGL warning: NULL pointer in %s\n", #cname); \ cxxerr; \ } \ if(l->type != FTGL::LAYOUT_SIMPLE) \ { \ fprintf(stderr, "FTGL warning: %s not implemented for %d\n", \ __FUNCTION__, l->type); \ cxxerr; \ } \ return dynamic_cast(l->ptr)->cxxname cxxarg; \ } // void FTSimpleLayout::SetLineLength(const float LineLength); C_FUN(void, ftglSetLayoutLineLength, (FTGLlayout *l, const float length), return, SetLineLength, (length)); // float FTSimpleLayout::GetLineLength() const C_FUN(float, ftglGetLayoutLineLength, (FTGLlayout *l), return 0.0f, GetLineLength, ()); // void FTSimpleLayout::SetAlignment(const TextAlignment Alignment) C_FUN(void, ftglSetLayoutAlignment, (FTGLlayout *l, const int a), return, SetAlignment, ((FTGL::TextAlignment)a)); // TextAlignment FTSimpleLayout::GetAlignment() const C_FUN(int, ftglGetLayoutAlignement, (FTGLlayout *l), return FTGL::ALIGN_LEFT, GetAlignment, ()); // void FTSimpleLayout::SetLineSpacing(const float LineSpacing) C_FUN(void, ftglSetLayoutLineSpacing, (FTGLlayout *l, const float f), return, SetLineSpacing, (f)); FTGL_END_C_DECLS connectome-workbench-1.2.3+git41-gc4c6c90/src/FtglFont/FTLayout/FTLayoutImpl.h000066400000000000000000000032411300200146000265000ustar00rootroot00000000000000/* * FTGL - OpenGL font library * * Copyright (c) 2001-2004 Henry Maddocks * Copyright (c) 2008 Sam Hocevar * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #ifndef __FTLayoutImpl__ #define __FTLayoutImpl__ #include "FTSize.h" #include "FTGlyphContainer.h" class FTLayoutImpl { friend class FTLayout; protected: FTLayoutImpl(); virtual ~FTLayoutImpl(); protected: /** * Current pen or cursor position; */ FTPoint pen; /** * Current error code. Zero means no error. */ FT_Error err; }; #endif // __FTLayoutImpl__ connectome-workbench-1.2.3+git41-gc4c6c90/src/FtglFont/FTLayout/FTSimpleLayout.cpp000066400000000000000000000342271300200146000273730ustar00rootroot00000000000000/* * FTGL - OpenGL font library * * Copyright (c) 2001-2004 Henry Maddocks * Copyright (c) 2008 Sam Hocevar * Copyright (c) 2014 Washington University School of Medicine * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #include "FtglConfig.h" #include #include #include "FTInternals.h" #include "FTUnicode.h" #include "FTGlyphContainer.h" #include "FTSimpleLayoutImpl.h" // // FTSimpleLayout // FTSimpleLayout::FTSimpleLayout() : FTLayout(new FTSimpleLayoutImpl()) {} FTSimpleLayout::~FTSimpleLayout() {} FTBBox FTSimpleLayout::BBox(const char *string, const int len, FTPoint pos) { return dynamic_cast(impl)->BBox(string, len, pos); } FTBBox FTSimpleLayout::BBox(const wchar_t *string, const int len, FTPoint pos) { return dynamic_cast(impl)->BBox(string, len, pos); } void FTSimpleLayout::Render(const char *string, const int len, FTPoint pos, int renderMode) { return dynamic_cast(impl)->Render(string, len, pos, renderMode); } void FTSimpleLayout::Render(const wchar_t* string, const int len, FTPoint pos, int renderMode) { return dynamic_cast(impl)->Render(string, len, pos, renderMode); } void FTSimpleLayout::SetFont(FTFont *fontInit) { dynamic_cast(impl)->currentFont = fontInit; } FTFont *FTSimpleLayout::GetFont() { return dynamic_cast(impl)->currentFont; } void FTSimpleLayout::SetLineLength(const float LineLength) { dynamic_cast(impl)->lineLength = LineLength; } float FTSimpleLayout::GetLineLength() const { return dynamic_cast(impl)->lineLength; } void FTSimpleLayout::SetAlignment(const FTGL::TextAlignment Alignment) { dynamic_cast(impl)->alignment = Alignment; } FTGL::TextAlignment FTSimpleLayout::GetAlignment() const { return dynamic_cast(impl)->alignment; } void FTSimpleLayout::SetLineSpacing(const float LineSpacing) { dynamic_cast(impl)->lineSpacing = LineSpacing; } float FTSimpleLayout::GetLineSpacing() const { return dynamic_cast(impl)->lineSpacing; } // // FTSimpleLayoutImpl // FTSimpleLayoutImpl::FTSimpleLayoutImpl() { currentFont = NULL; lineLength = 100.0f; alignment = FTGL::ALIGN_LEFT; lineSpacing = 1.0f; } template inline FTBBox FTSimpleLayoutImpl::BBoxI(const T* string, const int len, FTPoint position) { FTBBox tmp; WrapText(string, len, position, 0, &tmp); return tmp; } FTBBox FTSimpleLayoutImpl::BBox(const char *string, const int len, FTPoint position) { return BBoxI(string, len, position); } FTBBox FTSimpleLayoutImpl::BBox(const wchar_t *string, const int len, FTPoint position) { return BBoxI(string, len, position); } template inline void FTSimpleLayoutImpl::RenderI(const T *string, const int len, FTPoint position, int renderMode) { pen = FTPoint(0.0f, 0.0f); WrapText(string, len, position, renderMode, NULL); } void FTSimpleLayoutImpl::Render(const char *string, const int len, FTPoint position, int renderMode) { RenderI(string, len, position, renderMode); } void FTSimpleLayoutImpl::Render(const wchar_t* string, const int len, FTPoint position, int renderMode) { RenderI(string, len, position, renderMode); } template inline void FTSimpleLayoutImpl::WrapTextI(const T *buf, const int /*len*/, FTPoint position, int renderMode, FTBBox *bounds) { FTUnicodeStringItr breakItr(buf); // points to the last break character FTUnicodeStringItr lineStart(buf); // points to the line start float nextStart = 0.0; // total width of the current line float breakWidth = 0.0; // width of the line up to the last word break float currentWidth = 0.0; // width of all characters on the current line float prevWidth; // width of all characters but the current glyph float wordLength = 0.0; // length of the block since the last break char int charCount = 0; // number of characters so far on the line int breakCharCount = 0; // number of characters before the breakItr float glyphWidth, advance; FTBBox glyphBounds; // Reset the pen position pen.Y(0); // If we have bounds mark them invalid if(bounds) { bounds->Invalidate(); } // Scan the input for all characters that need output FTUnicodeStringItr prevItr(buf); for (FTUnicodeStringItr itr(buf); *itr; prevItr = itr++, charCount++) { // Find the width of the current glyph glyphBounds = currentFont->BBox(itr.getBufferFromHere(), 1); glyphWidth = glyphBounds.Upper().Xf() - glyphBounds.Lower().Xf(); advance = currentFont->Advance(itr.getBufferFromHere(), 1); prevWidth = currentWidth; // Compute the width of all glyphs up to the end of buf[i] currentWidth = nextStart + glyphWidth; // Compute the position of the next glyph nextStart += advance; // See if the current character is a space, a break or a regular character if((currentWidth > lineLength) || (*itr == '\n')) { // A non whitespace character has exceeded the line length. Or a // newline character has forced a line break. Output the last // line and start a new line after the break character. // If we have not yet found a break, break on the last character if(breakItr == lineStart || (*itr == '\n')) { // Break on the previous character breakItr = prevItr; breakCharCount = charCount - 1; breakWidth = prevWidth; // None of the previous words will be carried to the next line wordLength = 0; // If the current character is a newline discard its advance if(*itr == '\n') advance = 0; } float remainingWidth = lineLength - breakWidth; // Render the current substring FTUnicodeStringItr breakChar = breakItr; // move past the break character and don't count it on the next line either ++breakChar; --charCount; // If the break character is a newline do not render it if(*breakChar == '\n') { ++breakChar; --charCount; } OutputWrapped(lineStart.getBufferFromHere(), breakCharCount, //breakItr.getBufferFromHere() - lineStart.getBufferFromHere(), position, renderMode, remainingWidth, bounds); // Store the start of the next line lineStart = breakChar; // TODO: Is Height() the right value here? pen -= FTPoint(0, currentFont->LineHeight() * lineSpacing); // The current width is the width since the last break nextStart = wordLength + advance; wordLength += advance; currentWidth = wordLength + advance; // Reset the safe break for the next line breakItr = lineStart; charCount -= breakCharCount; } else if(iswspace(*itr)) { // This is the last word break position wordLength = 0; breakItr = itr; breakCharCount = charCount; // Check to see if this is the first whitespace character in a run if(buf == itr.getBufferFromHere() || !iswspace(*prevItr)) { // Record the width of the start of the block breakWidth = currentWidth; } } else { wordLength += advance; } } float remainingWidth = lineLength - currentWidth; // Render any remaining text on the last line // Disable justification for the last row if(alignment == FTGL::ALIGN_JUSTIFY) { alignment = FTGL::ALIGN_LEFT; OutputWrapped(lineStart.getBufferFromHere(), -1, position, renderMode, remainingWidth, bounds); alignment = FTGL::ALIGN_JUSTIFY; } else { OutputWrapped(lineStart.getBufferFromHere(), -1, position, renderMode, remainingWidth, bounds); } } void FTSimpleLayoutImpl::WrapText(const char *buf, const int len, FTPoint position, int renderMode, FTBBox *bounds) { WrapTextI(buf, len, position, renderMode, bounds); } void FTSimpleLayoutImpl::WrapText(const wchar_t* buf, const int len, FTPoint position, int renderMode, FTBBox *bounds) { WrapTextI(buf, len, position, renderMode, bounds); } template inline void FTSimpleLayoutImpl::OutputWrappedI(const T *buf, const int len, FTPoint position, int renderMode, const float remaining, FTBBox *bounds) { float distributeWidth = 0.0; // Align the text according as specified by Alignment switch (alignment) { case FTGL::ALIGN_LEFT: pen.X(0); break; case FTGL::ALIGN_CENTER: pen.X(remaining / 2); break; case FTGL::ALIGN_RIGHT: pen.X(remaining); break; case FTGL::ALIGN_JUSTIFY: pen.X(0); distributeWidth = remaining; break; } // If we have bounds expand them by the line's bounds, otherwise render // the line. if(bounds) { FTBBox temp = currentFont->BBox(buf, len); // Add the extra space to the upper x dimension temp = FTBBox(temp.Lower() + pen, temp.Upper() + pen + FTPoint(distributeWidth, 0)); // See if this is the first area to be added to the bounds if(bounds->IsValid()) { *bounds |= temp; } else { *bounds = temp; } } else { RenderSpace(buf, len, position, renderMode, distributeWidth); } } void FTSimpleLayoutImpl::OutputWrapped(const char *buf, const int len, FTPoint position, int renderMode, const float remaining, FTBBox *bounds) { OutputWrappedI(buf, len, position, renderMode, remaining, bounds); } void FTSimpleLayoutImpl::OutputWrapped(const wchar_t *buf, const int len, FTPoint position, int renderMode, const float remaining, FTBBox *bounds) { OutputWrappedI(buf, len, position, renderMode, remaining, bounds); } template inline void FTSimpleLayoutImpl::RenderSpaceI(const T *string, const int len, FTPoint /*position*/, int renderMode, const float extraSpace) { float space = 0.0; // If there is space to distribute, count the number of spaces if(extraSpace > 0.0) { int numSpaces = 0; // Count the number of space blocks in the input FTUnicodeStringItr prevItr(string), itr(string); for(int i = 0; ((len < 0) && *itr) || ((len >= 0) && (i <= len)); ++i, prevItr = itr++) { // If this is the end of a space block, increment the counter if((i > 0) && !iswspace(*itr) && iswspace(*prevItr)) { numSpaces++; } } space = extraSpace/numSpaces; } // Output all characters of the string FTUnicodeStringItr prevItr(string), itr(string); for(int i = 0; ((len < 0) && *itr) || ((len >= 0) && (i <= len)); ++i, prevItr = itr++) { // If this is the end of a space block, distribute the extra space // inside it if((i > 0) && !iswspace(*itr) && iswspace(*prevItr)) { pen += FTPoint(space, 0); } pen = currentFont->Render(itr.getBufferFromHere(), 1, pen, FTPoint(), renderMode); } } void FTSimpleLayoutImpl::RenderSpace(const char *string, const int len, FTPoint position, int renderMode, const float extraSpace) { RenderSpaceI(string, len, position, renderMode, extraSpace); } void FTSimpleLayoutImpl::RenderSpace(const wchar_t *string, const int len, FTPoint position, int renderMode, const float extraSpace) { RenderSpaceI(string, len, position, renderMode, extraSpace); } connectome-workbench-1.2.3+git41-gc4c6c90/src/FtglFont/FTLayout/FTSimpleLayoutImpl.h000066400000000000000000000231131300200146000276520ustar00rootroot00000000000000/* * FTGL - OpenGL font library * * Copyright (c) 2001-2004 Henry Maddocks * Copyright (c) 2008 Sam Hocevar * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #ifndef __FTSimpleLayoutImpl__ #define __FTSimpleLayoutImpl__ #include "FTLayoutImpl.h" class FTFont; class FTSimpleLayoutImpl : public FTLayoutImpl { friend class FTSimpleLayout; protected: FTSimpleLayoutImpl(); virtual ~FTSimpleLayoutImpl() {}; virtual FTBBox BBox(const char* string, const int len, FTPoint position); virtual FTBBox BBox(const wchar_t* string, const int len, FTPoint position); virtual void Render(const char *string, const int len, FTPoint position, int renderMode); virtual void Render(const wchar_t *string, const int len, FTPoint position, int renderMode); /** * Render a string of characters and distribute extra space amongst * the whitespace regions of the string. * * @param string A buffer of wchar_t characters to output. * @param len The length of the string. If < 0 then all characters * will be displayed until a null character is encountered. * @param position TODO * @param renderMode Render mode to display * @param extraSpace The amount of extra space to distribute amongst * the characters. */ virtual void RenderSpace(const char *string, const int len, FTPoint position, int renderMode, const float extraSpace); /** * Render a string of characters and distribute extra space amongst * the whitespace regions of the string. * * @param string A buffer of wchar_t characters to output. * @param len The length of the string. If < 0 then all characters * will be displayed until a null character is encountered. * @param position TODO * @param renderMode Render mode to display * @param extraSpace The amount of extra space to distribute amongst * the characters. */ virtual void RenderSpace(const wchar_t *string, const int len, FTPoint position, int renderMode, const float extraSpace); private: /** * Either render a string of characters and wrap lines * longer than a threshold or compute the bounds * of a string of characters when wrapped. The functionality * of this method is exposed by the BBoxWrapped and * RenderWrapped methods. * * @param buf A char string to output. * @param len The length of the string. If < 0 then all characters * will be displayed until a null character is encountered. * @param position TODO * @param renderMode Render mode to display * @param bounds A pointer to a bounds object. If non null * the bounds of the text when laid out * will be stored in bounds. If null the * text will be rendered. */ virtual void WrapText(const char *buf, const int len, FTPoint position, int renderMode, FTBBox *bounds); /** * Either render a string of characters and wrap lines * longer than a threshold or compute the bounds * of a string of characters when wrapped. The functionality * of this method is exposed by the BBoxWrapped and * RenderWrapped methods. * * @param buf A wchar_t style string to output. * @param len The length of the string. If < 0 then all characters * will be displayed until a null character is encountered. * @param position TODO * @param renderMode Render mode to display * @param bounds A pointer to a bounds object. If non null * the bounds of the text when laid out * will be stored in bounds. If null the * text will be rendered. */ virtual void WrapText(const wchar_t *buf, const int len, FTPoint position, int renderMode, FTBBox *bounds); /** * A helper method used by WrapText to either output the text or * compute it's bounds. * * @param buf A pointer to an array of character data. * @param len The length of the string. If < 0 then all characters * will be displayed until a null character is encountered. * @param position TODO * @param renderMode Render mode to display * @param RemainingWidth The amount of extra space left on the line. * @param bounds A pointer to a bounds object. If non null the * bounds will be initialized or expanded by the * bounds of the line. If null the text will be * rendered. If the bounds are invalid (lower > upper) * they will be initialized. Otherwise they * will be expanded. */ void OutputWrapped(const char *buf, const int len, FTPoint position, int renderMode, const float RemainingWidth, FTBBox *bounds); /** * A helper method used by WrapText to either output the text or * compute it's bounds. * * @param buf A pointer to an array of character data. * @param len The length of the string. If < 0 then all characters * will be displayed until a null character is encountered. * @param position TODO * @param renderMode Render mode to display * @param RemainingWidth The amount of extra space left on the line. * @param bounds A pointer to a bounds object. If non null the * bounds will be initialized or expanded by the * bounds of the line. If null the text will be * rendered. If the bounds are invalid (lower > upper) * they will be initialized. Otherwise they * will be expanded. */ void OutputWrapped(const wchar_t *buf, const int len, FTPoint position, int renderMode, const float RemainingWidth, FTBBox *bounds); /** * The font to use for rendering the text. The font is * referenced by this but will not be disposed of when this * is deleted. */ FTFont *currentFont; /** * The maximum line length for formatting text. */ float lineLength; /** * The text alignment mode used to distribute * space within a line or rendered text. */ FTGL::TextAlignment alignment; /** * The height of each line of text expressed as * a percentage of the font's line height. */ float lineSpacing; /* Internal generic BBox() implementation */ template inline FTBBox BBoxI(const T* string, const int len, FTPoint position); /* Internal generic Render() implementation */ template inline void RenderI(const T* string, const int len, FTPoint position, int renderMode); /* Internal generic RenderSpace() implementation */ template inline void RenderSpaceI(const T* string, const int len, FTPoint position, int renderMode, const float extraSpace); /* Internal generic WrapText() implementation */ template void WrapTextI(const T* buf, const int len, FTPoint position, int renderMode, FTBBox *bounds); /* Internal generic OutputWrapped() implementation */ template void OutputWrappedI(const T* buf, const int len, FTPoint position, int renderMode, const float RemainingWidth, FTBBox *bounds); }; #endif // __FTSimpleLayoutImpl__ connectome-workbench-1.2.3+git41-gc4c6c90/src/FtglFont/FTLibrary.cpp000066400000000000000000000040501300200146000246300ustar00rootroot00000000000000/* * FTGL - OpenGL font library * * Copyright (c) 2001-2004 Henry Maddocks * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #include "FtglConfig.h" #include "FTLibrary.h" const FTLibrary& FTLibrary::Instance() { static FTLibrary ftlib; return ftlib; } FTLibrary::~FTLibrary() { if(library != 0) { FT_Done_FreeType(*library); delete library; library= 0; } // if(manager != 0) // { // FTC_Manager_Done(manager); // // delete manager; // manager= 0; // } } FTLibrary::FTLibrary() : library(0), err(0) { Initialise(); } bool FTLibrary::Initialise() { if(library != 0) return true; library = new FT_Library; err = FT_Init_FreeType(library); if(err) { delete library; library = 0; return false; } // FTC_Manager* manager; // // if(FTC_Manager_New(lib, 0, 0, 0, my_face_requester, 0, manager) // { // delete manager; // manager= 0; // return false; // } return true; } connectome-workbench-1.2.3+git41-gc4c6c90/src/FtglFont/FTLibrary.h000066400000000000000000000076041300200146000243050ustar00rootroot00000000000000/* * FTGL - OpenGL font library * * Copyright (c) 2001-2004 Henry Maddocks * Copyright (c) 2014 Washington University School of Medicine * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #ifndef __FTLibrary__ #define __FTLibrary__ #include #include FT_FREETYPE_H //#include FT_CACHE_H #include "FTGL/ftgl.h" /** * FTLibrary class is the global accessor for the Freetype library. * * This class encapsulates the Freetype Library. This is a singleton class * and ensures that only one FT_Library is in existence at any one time. * All constructors are private therefore clients cannot create or * instantiate this class themselves and must access it's methods via the * static FTLibrary::Instance() function. * * Just because this class returns a valid FTLibrary object * doesn't mean that the Freetype Library has been successfully initialised. * Clients should check for errors. You can initialse the library AND check * for errors using the following code... * err = FTLibrary::Instance().Error(); * * @see "Freetype 2 Documentation" * */ class FTLibrary { public: /** * Global acces point to the single FTLibrary object. * * @return The global FTLibrary object. */ static const FTLibrary& Instance(); /** * Gets a pointer to the native Freetype library. * * @return A handle to a FreeType library instance. */ const FT_Library* /*const*/ GetLibrary() const { return library; } /** * Queries the library for errors. * * @return The current error code. */ FT_Error Error() const { return err; } /** * Destructor * * Disposes of the Freetype library */ ~FTLibrary(); private: /** * Default constructors. * * Made private to stop clients creating there own FTLibrary * objects. */ FTLibrary(); FTLibrary(const FT_Library&){} FTLibrary& operator=(const FT_Library&) { return *this; } /** * Initialises the Freetype library * * Even though this function indicates success via the return value, * clients can't see this so must check the error codes. This function * is only ever called by the default c_stor * * @return true if the Freetype library was * successfully initialised, false * otherwise. */ bool Initialise(); /** * Freetype library handle. */ FT_Library* library; // FTC_Manager* manager; /** * Current error code. Zero means no error. */ FT_Error err; }; #endif // __FTLibrary__ connectome-workbench-1.2.3+git41-gc4c6c90/src/FtglFont/FTList.h000066400000000000000000000063021300200146000236060ustar00rootroot00000000000000/* * FTGL - OpenGL font library * * Copyright (c) 2001-2004 Henry Maddocks * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #ifndef __FTList__ #define __FTList__ #include "FTGL/ftgl.h" /** * Provides a non-STL alternative to the STL list */ template class FTList { public: typedef FT_LIST_ITEM_TYPE value_type; typedef value_type& reference; typedef const value_type& const_reference; typedef size_t size_type; /** * Constructor */ FTList() : listSize(0), tail(0) { tail = NULL; head = new Node; } /** * Destructor */ ~FTList() { Node* next; for(Node *walk = head; walk; walk = next) { next = walk->next; delete walk; } } /** * Get the number of items in the list */ size_type size() const { return listSize; } /** * Add an item to the end of the list */ void push_back(const value_type& item) { Node* node = new Node(item); if(head->next == NULL) { head->next = node; } if(tail) { tail->next = node; } tail = node; ++listSize; } /** * Get the item at the front of the list */ reference front() const { return head->next->payload; } /** * Get the item at the end of the list */ reference back() const { return tail->payload; } private: struct Node { Node() : next(NULL) {} Node(const value_type& item) : next(NULL) { payload = item; } Node* next; value_type payload; }; size_type listSize; Node* head; Node* tail; }; #endif // __FTList__ connectome-workbench-1.2.3+git41-gc4c6c90/src/FtglFont/FTPoint.cpp000066400000000000000000000036361300200146000243260ustar00rootroot00000000000000/* * FTGL - OpenGL font library * * Copyright (c) 2001-2004 Henry Maddocks * Copyright (c) 2008 Sam Hocevar * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #include "FtglConfig.h" #include #include "FTGL/ftgl.h" bool operator == (const FTPoint &a, const FTPoint &b) { return((a.values[0] == b.values[0]) && (a.values[1] == b.values[1]) && (a.values[2] == b.values[2])); } bool operator != (const FTPoint &a, const FTPoint &b) { return((a.values[0] != b.values[0]) || (a.values[1] != b.values[1]) || (a.values[2] != b.values[2])); } FTPoint FTPoint::Normalise() { double norm = sqrt(values[0] * values[0] + values[1] * values[1] + values[2] * values[2]); if(norm == 0.0) { return *this; } FTPoint temp(values[0] / norm, values[1] / norm, values[2] / norm); return temp; } connectome-workbench-1.2.3+git41-gc4c6c90/src/FtglFont/FTSize.cpp000066400000000000000000000056131300200146000241440ustar00rootroot00000000000000/* * FTGL - OpenGL font library * * Copyright (c) 2001-2004 Henry Maddocks * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #include "FtglConfig.h" #include "FTSize.h" FTSize::FTSize() : ftFace(0), ftSize(0), size(0), xResolution(0), yResolution(0), err(0) {} FTSize::~FTSize() {} bool FTSize::CharSize(FT_Face* face, unsigned int pointSize, unsigned int xRes, unsigned int yRes) { if(size != pointSize || xResolution != xRes || yResolution != yRes) { err = FT_Set_Char_Size(*face, 0L, pointSize * 64, xResolution, yResolution); if(!err) { ftFace = face; size = pointSize; xResolution = xRes; yResolution = yRes; ftSize = (*ftFace)->size; } } return !err; } unsigned int FTSize::CharSize() const { return size; } float FTSize::Ascender() const { return ftSize == 0 ? 0.0f : static_cast(ftSize->metrics.ascender) / 64.0f; } float FTSize::Descender() const { return ftSize == 0 ? 0.0f : static_cast(ftSize->metrics.descender) / 64.0f; } float FTSize::Height() const { if(0 == ftSize) { return 0.0f; } if(FT_IS_SCALABLE((*ftFace))) { return ((*ftFace)->bbox.yMax - (*ftFace)->bbox.yMin) * ((float)ftSize->metrics.y_ppem / (float)(*ftFace)->units_per_EM); } else { return static_cast(ftSize->metrics.height) / 64.0f; } } float FTSize::Width() const { if(0 == ftSize) { return 0.0f; } if(FT_IS_SCALABLE((*ftFace))) { return ((*ftFace)->bbox.xMax - (*ftFace)->bbox.xMin) * (static_cast(ftSize->metrics.x_ppem) / static_cast((*ftFace)->units_per_EM)); } else { return static_cast(ftSize->metrics.max_advance) / 64.0f; } } float FTSize::Underline() const { return 0.0f; } connectome-workbench-1.2.3+git41-gc4c6c90/src/FtglFont/FTSize.h000066400000000000000000000112671300200146000236130ustar00rootroot00000000000000/* * FTGL - OpenGL font library * * Copyright (c) 2001-2004 Henry Maddocks * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #ifndef __FTSize__ #define __FTSize__ #include #include FT_FREETYPE_H #include "FTGL/ftgl.h" /** * FTSize class provides an abstraction layer for the Freetype Size. * * @see "Freetype 2 Documentation" * */ class FTSize { public: /** * Default Constructor */ FTSize(); /** * Destructor */ virtual ~FTSize(); /** * Sets the char size for the current face. * * This doesn't guarantee that the size was set correctly. Clients * should check errors. If an error does occur the size object isn't modified. * * @param face Parent face for this size object * @param point_size the face size in points (1/72 inch) * @param x_resolution the horizontal resolution of the target device. * @param y_resolution the vertical resolution of the target device. * @return true if the size has been set. Clients should check Error() for more information if this function returns false() */ bool CharSize(FT_Face* face, unsigned int point_size, unsigned int x_resolution, unsigned int y_resolution); /** * get the char size for the current face. * * @return The char size in points */ unsigned int CharSize() const; /** * Gets the global ascender height for the face in pixels. * * @return Ascender height */ float Ascender() const; /** * Gets the global descender height for the face in pixels. * * @return Ascender height */ float Descender() const; /** * Gets the global face height for the face. * * If the face is scalable this returns the height of the global * bounding box which ensures that any glyph will be less than or * equal to this height. If the font isn't scalable there is no * guarantee that glyphs will not be taller than this value. * * @return height in pixels. */ float Height() const; /** * Gets the global face width for the face. * * If the face is scalable this returns the width of the global * bounding box which ensures that any glyph will be less than or * equal to this width. If the font isn't scalable this value is * the max_advance for the face. * * @return width in pixels. */ float Width() const; /** * Gets the underline position for the face. * * @return underline position in pixels */ float Underline() const; /** * Queries for errors. * * @return The current error code. */ FT_Error Error() const { return err; } private: /** * The current Freetype face that this FTSize object relates to. */ FT_Face* ftFace; /** * The Freetype size. */ FT_Size ftSize; /** * The size in points. */ unsigned int size; /** * The horizontal resolution. */ unsigned int xResolution; /** * The vertical resolution. */ unsigned int yResolution; /** * Current error code. Zero means no error. */ FT_Error err; }; #endif // __FTSize__ connectome-workbench-1.2.3+git41-gc4c6c90/src/FtglFont/FTUnicode.h000066400000000000000000000177341300200146000242740ustar00rootroot00000000000000/* * FTGL - OpenGL font library * * Copyright (c) 2008 Daniel Remenak * * Portions derived from ConvertUTF.c Copyright (C) 2001-2004 Unicode, Inc * Unicode, Inc. hereby grants the right to freely use the information * supplied in this file in the creation of products supporting the * Unicode Standard, and to make copies of this file in any form * for internal or external distribution as long as this notice * remains attached. * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #ifndef __FTUnicode__ #define __FTUnicode__ /** * Provides a way to easily walk multibyte unicode strings in the various * Unicode encodings (UTF-8, UTF-16, UTF-32, UCS-2, and UCS-4). Encodings * with elements larger than one byte must already be in the correct endian * order for the current architecture. */ template class FTUnicodeStringItr { public: /** * Constructor. Also reads the first character and stores it. * * @param string The buffer to iterate. No copy is made. */ FTUnicodeStringItr(const T* string) : curPos(string), nextPos(string) { (*this)++; }; /** * Pre-increment operator. Reads the next unicode character and sets * the state appropriately. * Note - not protected against overruns. */ FTUnicodeStringItr& operator++() { curPos = nextPos; // unicode handling switch (sizeof(T)) { case 1: // UTF-8 // get this character readUTF8(); break; case 2: // UTF-16 readUTF16(); break; case 4: // UTF-32 // fall through default: // error condition really, but give it a shot anyway curChar = *nextPos++; } return *this; } /** * Post-increment operator. Reads the next character and sets * the state appropriately. * Note - not protected against overruns. */ FTUnicodeStringItr operator++(int) { FTUnicodeStringItr temp = *this; ++*this; return temp; } /** * Equality operator. Two FTUnicodeStringItrs are considered equal * if they have the same current buffer and buffer position. */ bool operator==(const FTUnicodeStringItr& right) const { if (curPos == right.getBufferFromHere()) return true; return false; } /** * Dereference operator. * * @return The unicode codepoint of the character currently pointed * to by the FTUnicodeStringItr. */ unsigned int operator*() const { return curChar; } /** * Buffer-fetching getter. You can use this to retreive the buffer * starting at the currently-iterated character for functions which * require a Unicode string as input. */ const T* getBufferFromHere() const { return curPos; } private: /** * Helper function for reading a single UTF8 character from the string. * Updates internal state appropriately. */ void readUTF8(); /** * Helper function for reading a single UTF16 character from the string. * Updates internal state appropriately. */ void readUTF16(); /** * The buffer position of the first element in the current character. */ const T* curPos; /** * The character stored at the current buffer position (prefetched on * increment, so there's no penalty for dereferencing more than once). */ unsigned int curChar; /** * The buffer position of the first element in the next character. */ const T* nextPos; // unicode magic numbers static const char utf8bytes[256]; static const unsigned long offsetsFromUTF8[6]; static const unsigned long highSurrogateStart; static const unsigned long highSurrogateEnd; static const unsigned long lowSurrogateStart; static const unsigned long lowSurrogateEnd; static const unsigned long highSurrogateShift; static const unsigned long lowSurrogateBase; }; /* The first character in a UTF8 sequence indicates how many bytes * to read (among other things) */ template const char FTUnicodeStringItr::utf8bytes[256] = { 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, 3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3, 4,4,4,4,4,4,4,4,5,5,5,5,6,6,6,6 }; /* Magic values subtracted from a buffer value during UTF8 conversion. * This table contains as many values as there might be trailing bytes * in a UTF-8 sequence. */ template const unsigned long FTUnicodeStringItr::offsetsFromUTF8[6] = { 0x00000000UL, 0x00003080UL, 0x000E2080UL, 0x03C82080UL, 0xFA082080UL, 0x82082080UL }; // get a UTF8 character; leave the tracking pointer at the start of the // next character // not protected against invalid UTF8 template inline void FTUnicodeStringItr::readUTF8() { unsigned int ch = 0; unsigned int extraBytesToRead = utf8bytes[(unsigned char)(*nextPos)]; // falls through switch (extraBytesToRead) { case 6: ch += *nextPos++; ch <<= 6; /* remember, illegal UTF-8 */ case 5: ch += *nextPos++; ch <<= 6; /* remember, illegal UTF-8 */ case 4: ch += *nextPos++; ch <<= 6; case 3: ch += *nextPos++; ch <<= 6; case 2: ch += *nextPos++; ch <<= 6; case 1: ch += *nextPos++; } ch -= offsetsFromUTF8[extraBytesToRead-1]; curChar = ch; } // Magic numbers for UTF-16 conversions template const unsigned long FTUnicodeStringItr::highSurrogateStart = 0xD800; template const unsigned long FTUnicodeStringItr::highSurrogateEnd = 0xDBFF; template const unsigned long FTUnicodeStringItr::lowSurrogateStart = 0xDC00; template const unsigned long FTUnicodeStringItr::lowSurrogateEnd = 0xDFFF; template const unsigned long FTUnicodeStringItr::highSurrogateShift = 10; template const unsigned long FTUnicodeStringItr::lowSurrogateBase = 0x0010000UL; template inline void FTUnicodeStringItr::readUTF16() { unsigned int ch = *nextPos++; // if we have the first half of the surrogate pair if (ch >= highSurrogateStart && ch <= highSurrogateEnd) { unsigned int ch2 = *curPos; // complete the surrogate pair if (ch2 >= lowSurrogateStart && ch2 <= lowSurrogateEnd) { ch = ((ch - highSurrogateStart) << highSurrogateShift) + (ch2 - lowSurrogateStart) + lowSurrogateBase; ++nextPos; } } curChar = ch; } #endif connectome-workbench-1.2.3+git41-gc4c6c90/src/FtglFont/FTVector.h000066400000000000000000000116371300200146000241440ustar00rootroot00000000000000/* * FTGL - OpenGL font library * * Copyright (c) 2001-2004 Henry Maddocks * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #ifndef __FTVector__ #define __FTVector__ #include "FTGL/ftgl.h" /** * Provides a non-STL alternative to the STL vector */ template class FTVector { public: typedef FT_VECTOR_ITEM_TYPE value_type; typedef value_type& reference; typedef const value_type& const_reference; typedef value_type* iterator; typedef const value_type* const_iterator; typedef size_t size_type; FTVector() { Capacity = Size = 0; Items = 0; } virtual ~FTVector() { clear(); } FTVector& operator =(const FTVector& v) { reserve(v.capacity()); iterator ptr = begin(); const_iterator vbegin = v.begin(); const_iterator vend = v.end(); while(vbegin != vend) { *ptr++ = *vbegin++; } Size = v.size(); return *this; } size_type size() const { return Size; } size_type capacity() const { return Capacity; } iterator begin() { return Items; } const_iterator begin() const { return Items; } iterator end() { return begin() + size(); } const_iterator end() const { return begin() + size(); } bool empty() const { return size() == 0; } reference operator [](size_type pos) { return(*(begin() + pos)); } const_reference operator [](size_type pos) const { return *(begin() + pos); } void clear() { if(Capacity) { delete [] Items; Capacity = Size = 0; Items = 0; } } void reserve(size_type n) { if(capacity() < n) { expand(n); } } void push_back(const value_type& x) { if(size() == capacity()) { expand(); } (*this)[size()] = x; ++Size; } void resize(size_type n, value_type x) { if(n == size()) { return; } reserve(n); iterator ibegin, iend; if(n >= Size) { ibegin = this->end(); iend = this->begin() + n; } else { ibegin = this->begin() + n; iend = this->end(); } while(ibegin != iend) { *ibegin++ = x; } Size = n; } private: void expand(size_type capacity_hint = 0) { size_type new_capacity = (capacity() == 0) ? 256 : capacity() * 2; if(capacity_hint) { while(new_capacity < capacity_hint) { new_capacity *= 2; } } value_type *new_items = new value_type[new_capacity]; iterator ibegin = this->begin(); iterator iend = this->end(); value_type *ptr = new_items; while(ibegin != iend) { *ptr++ = *ibegin++; } if(Capacity) { delete [] Items; } Items = new_items; Capacity = new_capacity; } size_type Capacity; size_type Size; value_type* Items; }; #endif // __FTVector__ connectome-workbench-1.2.3+git41-gc4c6c90/src/FtglFont/FTVectoriser.cpp000066400000000000000000000216261300200146000253610ustar00rootroot00000000000000/* * FTGL - OpenGL font library * * Copyright (c) 2001-2004 Henry Maddocks * Copyright (c) 2008 Éric Beets * Copyright (c) 2008 Sam Hocevar * Copyright (c) 2014 Washington University School of Medicine * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #include "FtglConfig.h" #include "FTInternals.h" #include "FTVectoriser.h" #ifndef CALLBACK #define CALLBACK #endif #define GCC_VERSION (__GNUC__ * 10000 \ + __GNUC_MINOR__ * 100 \ + __GNUC_PATCHLEVEL__) /* Test for GCC > 4.9.0 on Apple JWH */ #if defined __APPLE_CC__ && GCC_VERSION > 4900 typedef GLvoid (*GLUTesselatorFunction) (); #elif defined __APPLE_CC__ && __APPLE_CC__ < 5465 typedef GLvoid (*GLUTesselatorFunction) (...); #elif defined WIN32 && !defined __CYGWIN__ typedef GLvoid (CALLBACK *GLUTesselatorFunction) (); #else typedef GLvoid (*GLUTesselatorFunction) (); #endif void CALLBACK ftglError(GLenum errCode, FTMesh* mesh) { mesh->Error(errCode); } void CALLBACK ftglVertex(void* data, FTMesh* mesh) { FTGL_DOUBLE* vertex = static_cast(data); mesh->AddPoint(vertex[0], vertex[1], vertex[2]); } void CALLBACK ftglCombine(FTGL_DOUBLE coords[3], void** /*vertex_data[4]*/, GLfloat* /*weight[4]*/, void** outData, FTMesh* mesh) { const FTGL_DOUBLE* vertex = static_cast(coords); *outData = const_cast(mesh->Combine(vertex[0], vertex[1], vertex[2])); } void CALLBACK ftglBegin(GLenum type, FTMesh* mesh) { mesh->Begin(type); } void CALLBACK ftglEnd(FTMesh* mesh) { mesh->End(); } FTMesh::FTMesh() : currentTesselation(0), err(0) { tesselationList.reserve(16); } FTMesh::~FTMesh() { for(size_t t = 0; t < tesselationList.size(); ++t) { delete tesselationList[t]; } tesselationList.clear(); } void FTMesh::AddPoint(const FTGL_DOUBLE x, const FTGL_DOUBLE y, const FTGL_DOUBLE z) { currentTesselation->AddPoint(x, y, z); } const FTGL_DOUBLE* FTMesh::Combine(const FTGL_DOUBLE x, const FTGL_DOUBLE y, const FTGL_DOUBLE z) { tempPointList.push_back(FTPoint(x, y,z)); return static_cast(tempPointList.back()); } void FTMesh::Begin(GLenum meshType) { currentTesselation = new FTTesselation(meshType); } void FTMesh::End() { tesselationList.push_back(currentTesselation); } const FTTesselation* /*const*/ FTMesh::Tesselation(size_t index) const { return (index < tesselationList.size()) ? tesselationList[index] : NULL; } FTVectoriser::FTVectoriser(const FT_GlyphSlot glyph) : contourList(0), mesh(0), ftContourCount(0), contourFlag(0) { if(glyph) { outline = glyph->outline; ftContourCount = outline.n_contours; contourList = 0; contourFlag = outline.flags; ProcessContours(); } } FTVectoriser::~FTVectoriser() { for(size_t c = 0; c < ContourCount(); ++c) { delete contourList[c]; } delete [] contourList; delete mesh; } void FTVectoriser::ProcessContours() { short contourLength = 0; short startIndex = 0; short endIndex = 0; contourList = new FTContour*[ftContourCount]; for(int i = 0; i < ftContourCount; ++i) { FT_Vector* pointList = &outline.points[startIndex]; char* tagList = &outline.tags[startIndex]; endIndex = outline.contours[i]; contourLength = (endIndex - startIndex) + 1; FTContour* contour = new FTContour(pointList, tagList, contourLength); contourList[i] = contour; startIndex = endIndex + 1; } // Compute each contour's parity. FIXME: see if FT_Outline_Get_Orientation // can do it for us. for(int i = 0; i < ftContourCount; i++) { FTContour *c1 = contourList[i]; // 1. Find the leftmost point. FTPoint leftmost(65536.0, 0.0); for(size_t n = 0; n < c1->PointCount(); n++) { FTPoint p = c1->Point(n); if(p.X() < leftmost.X()) { leftmost = p; } } // 2. Count how many other contours we cross when going further to // the left. int parity = 0; for(int j = 0; j < ftContourCount; j++) { if(j == i) { continue; } FTContour *c2 = contourList[j]; for(size_t n = 0; n < c2->PointCount(); n++) { FTPoint p1 = c2->Point(n); FTPoint p2 = c2->Point((n + 1) % c2->PointCount()); /* FIXME: combinations of >= > <= and < do not seem stable */ if((p1.Y() < leftmost.Y() && p2.Y() < leftmost.Y()) || (p1.Y() >= leftmost.Y() && p2.Y() >= leftmost.Y()) || (p1.X() > leftmost.X() && p2.X() > leftmost.X())) { continue; } else if(p1.X() < leftmost.X() && p2.X() < leftmost.X()) { parity++; } else { FTPoint a = p1 - leftmost; FTPoint b = p2 - leftmost; if(b.X() * a.Y() > b.Y() * a.X()) { parity++; } } } } // 3. Make sure the glyph has the proper parity. c1->SetParity(parity); } } size_t FTVectoriser::PointCount() { size_t s = 0; for(size_t c = 0; c < ContourCount(); ++c) { s += contourList[c]->PointCount(); } return s; } const FTContour* /*const*/ FTVectoriser::Contour(size_t index) const { return (index < ContourCount()) ? contourList[index] : NULL; } void FTVectoriser::MakeMesh(FTGL_DOUBLE zNormal, int outsetType, float outsetSize) { if(mesh) { delete mesh; } mesh = new FTMesh; GLUtesselator* tobj = gluNewTess(); gluTessCallback(tobj, GLU_TESS_BEGIN_DATA, (GLUTesselatorFunction)ftglBegin); gluTessCallback(tobj, GLU_TESS_VERTEX_DATA, (GLUTesselatorFunction)ftglVertex); gluTessCallback(tobj, GLU_TESS_COMBINE_DATA, (GLUTesselatorFunction)ftglCombine); gluTessCallback(tobj, GLU_TESS_END_DATA, (GLUTesselatorFunction)ftglEnd); gluTessCallback(tobj, GLU_TESS_ERROR_DATA, (GLUTesselatorFunction)ftglError); if(contourFlag & ft_outline_even_odd_fill) // ft_outline_reverse_fill { gluTessProperty(tobj, GLU_TESS_WINDING_RULE, GLU_TESS_WINDING_ODD); } else { gluTessProperty(tobj, GLU_TESS_WINDING_RULE, GLU_TESS_WINDING_NONZERO); } gluTessProperty(tobj, GLU_TESS_TOLERANCE, 0); gluTessNormal(tobj, 0.0f, 0.0f, zNormal); gluTessBeginPolygon(tobj, mesh); for(size_t c = 0; c < ContourCount(); ++c) { /* Build the */ switch(outsetType) { case 1 : contourList[c]->buildFrontOutset(outsetSize); break; case 2 : contourList[c]->buildBackOutset(outsetSize); break; } const FTContour* contour = contourList[c]; gluTessBeginContour(tobj); for(size_t p = 0; p < contour->PointCount(); ++p) { const FTGL_DOUBLE* d; switch(outsetType) { case 1: d = contour->FrontPoint(p); break; case 2: d = contour->BackPoint(p); break; case 0: default: d = contour->Point(p); break; } // XXX: gluTessVertex doesn't modify the data but does not // specify "const" in its prototype, so we cannot cast to // a const type. gluTessVertex(tobj, (GLdouble *)d, (GLvoid *)d); } gluTessEndContour(tobj); } gluTessEndPolygon(tobj); gluDeleteTess(tobj); } connectome-workbench-1.2.3+git41-gc4c6c90/src/FtglFont/FTVectoriser.h000066400000000000000000000173421300200146000250260ustar00rootroot00000000000000/* * FTGL - OpenGL font library * * Copyright (c) 2001-2004 Henry Maddocks * Copyright (c) 2008 Sam Hocevar * Copyright (c) 2014 Washington University School of Medicine * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #ifndef __FTVectoriser__ #define __FTVectoriser__ #include "FTGL/ftgl.h" #include "FTContour.h" #include "FTList.h" #include "FTVector.h" #ifndef CALLBACK #define CALLBACK #endif /** * FTTesselation captures points that are output by OpenGL's gluTesselator. */ class FTTesselation { public: /** * Default constructor */ FTTesselation(GLenum m) : meshType(m) { pointList.reserve(128); } /** * Destructor */ ~FTTesselation() { pointList.clear(); } /** * Add a point to the mesh. */ void AddPoint(const FTGL_DOUBLE x, const FTGL_DOUBLE y, const FTGL_DOUBLE z) { pointList.push_back(FTPoint(x, y, z)); } /** * The number of points in this mesh */ size_t PointCount() const { return pointList.size(); } /** * */ const FTPoint& Point(unsigned int index) const { return pointList[index]; } /** * Return the OpenGL polygon type. */ GLenum PolygonType() const { return meshType; } private: /** * Points generated by gluTesselator. */ typedef FTVector PointVector; PointVector pointList; /** * OpenGL primitive type from gluTesselator. */ GLenum meshType; }; /** * FTMesh is a container of FTTesselation's that make up a polygon glyph */ class FTMesh { typedef FTVector TesselationVector; typedef FTList PointList; public: /** * Default constructor */ FTMesh(); /** * Destructor */ ~FTMesh(); /** * Add a point to the mesh */ void AddPoint(const FTGL_DOUBLE x, const FTGL_DOUBLE y, const FTGL_DOUBLE z); /** * Create a combine point for the gluTesselator */ const FTGL_DOUBLE* Combine(const FTGL_DOUBLE x, const FTGL_DOUBLE y, const FTGL_DOUBLE z); /** * Begin a new polygon */ void Begin(GLenum meshType); /** * End a polygon */ void End(); /** * Record a gluTesselation error */ void Error(GLenum e) { err = e; } /** * The number of tesselations in the mesh */ size_t TesselationCount() const { return tesselationList.size(); } /** * Get a tesselation by index */ const FTTesselation* /*const*/ Tesselation(size_t index) const; /** * Return the temporary point list. For testing only. */ const PointList& TempPointList() const { return tempPointList; } /** * Get the GL ERROR returned by the glu tesselator */ GLenum Error() const { return err; } private: /** * The current sub mesh that we are constructing. */ FTTesselation* currentTesselation; /** * Holds each sub mesh that comprises this glyph. */ TesselationVector tesselationList; /** * Holds extra points created by gluTesselator. See ftglCombine. */ PointList tempPointList; /** * GL ERROR returned by the glu tesselator */ GLenum err; }; const FTGL_DOUBLE FTGL_FRONT_FACING = 1.0; const FTGL_DOUBLE FTGL_BACK_FACING = -1.0; /** * FTVectoriser class is a helper class that converts font outlines into * point data. * * @see FTExtrudeGlyph * @see FTOutlineGlyph * @see FTPolygonGlyph * @see FTContour * @see FTPoint * */ class FTVectoriser { public: /** * Constructor * * @param glyph The freetype glyph to be processed */ FTVectoriser(const FT_GlyphSlot glyph); /** * Destructor */ virtual ~FTVectoriser(); /** * Build an FTMesh from the vector outline data. * * @param zNormal The direction of the z axis of the normal * for this mesh * FIXME: change the following for a constant * @param outsetType Specify the outset type contour * 0 : Original * 1 : Front * 2 : Back * @param outsetSize Specify the outset size contour */ void MakeMesh(FTGL_DOUBLE zNormal = FTGL_FRONT_FACING, int outsetType = 0, float outsetSize = 0.0f); /** * Get the current mesh. */ const FTMesh* /*const*/ GetMesh() const { return mesh; } /** * Get the total count of points in this outline * * @return the number of points */ size_t PointCount(); /** * Get the count of contours in this outline * * @return the number of contours */ size_t ContourCount() const { return ftContourCount; } /** * Return a contour at index * * @return the number of contours */ const FTContour* /*const*/ Contour(size_t index) const; /** * Get the number of points in a specific contour in this outline * * @param c The contour index * @return the number of points in contour[c] */ size_t ContourSize(int c) const { return contourList[c]->PointCount(); } /** * Get the flag for the tesselation rule for this outline * * @return The contour flag */ int ContourFlag() const { return contourFlag; } private: /** * Process the freetype outline data into contours of points * * @param front front outset distance * @param back back outset distance */ void ProcessContours(); /** * The list of contours in the glyph */ FTContour** contourList; /** * A Mesh for tesselations */ FTMesh* mesh; /** * The number of contours reported by Freetype */ short ftContourCount; /** * A flag indicating the tesselation rule for the glyph */ int contourFlag; /** * A Freetype outline */ FT_Outline outline; }; #endif // __FTVectoriser__ connectome-workbench-1.2.3+git41-gc4c6c90/src/FtglFont/FtglConfig.h000066400000000000000000000046301300200146000244650ustar00rootroot00000000000000/* config.h.in. Generated from configure.ac by autoheader. */ /* Define to the path to a TrueType font */ #define FONT_FILE /usr/X11R6/share/fonts/TTF/VeraSe.ttf /* Define to 1 if you have the header file. */ #undef HAVE_DLFCN_H /* Define to 1 if you have the header file. */ #undef HAVE_GLUT_GLUT_H /* Define to 1 if you have the header file. */ #undef HAVE_GL_GLUT_H /* Define to 1 if you have the header file. */ #define HAVE_INTTYPES_H 1 /* Define to 1 if you have the header file. */ #define HAVE_MEMORY_H 1 /* Define to 1 if you have the header file. */ #define HAVE_STDINT_H 1 /* Define to 1 if you have the header file. */ #define HAVE_STDLIB_H 1 /* Define to 1 if you have the header file. */ #define HAVE_STRINGS_H 1 /* Define to 1 if you have the header file. */ #define HAVE_STRING_H 1 /* Define to 1 if you have the `strndup' function. */ #define HAVE_STRNDUP 1 /* not on mac (hippocampus) */ #undef HAVE_STRNDUP /* Define to 1 if you have the header file. */ #undef HAVE_SYS_STAT_H /* Define to 1 if you have the header file. */ #define HAVE_SYS_TYPES_H 1 /* Define to 1 if you have the header file. */ #undef HAVE_UNISTD_H /* Define to 1 if you have the `wcsdup' function. */ #undef HAVE_WCSDUP /* Define to 1 if your C compiler doesn't accept -c and -o together. */ #undef NO_MINUS_C_MINUS_O /* Define to the address where bug reports for this package should be sent. */ #undef PACKAGE_BUGREPORT /* Define to the full name of this package. */ #undef PACKAGE_NAME /* Define to the full name and version of this package. */ #undef PACKAGE_STRING /* Define to the one symbol short name of this package. */ #undef PACKAGE_TARNAME /* Define to the version of this package. */ #undef PACKAGE_VERSION /* Define to 1 if you have the ANSI C header files. */ #define STDC_HEADERS 1 /* Define to 1 if the X Window System is missing or not being used. */ #define X_DISPLAY_MISSING 1 /* FOR WINDOWS, otherwise __declspec(dllimport) linking errors */ #define FTGL_LIBRARY_STATIC 1 connectome-workbench-1.2.3+git41-gc4c6c90/src/FtglFont/Makefile.am000066400000000000000000000053401300200146000243250ustar00rootroot00000000000000 lib_LTLIBRARIES = libftgl.la libftgl_la_SOURCES = \ FTBuffer.cpp \ FTCharmap.cpp \ FTCharmap.h \ FTCharToGlyphIndexMap.h \ FTContour.cpp \ FTContour.h \ FTFace.cpp \ FTFace.h \ FTGlyphContainer.cpp \ FTGlyphContainer.h \ FTInternals.h \ FTLibrary.cpp \ FTLibrary.h \ FTList.h \ FTPoint.cpp \ FTSize.cpp \ FTSize.h \ FTVector.h \ FTVectoriser.cpp \ FTVectoriser.h \ FTUnicode.h \ $(ftglyph_sources) \ $(ftfont_sources) \ $(ftlayout_sources) \ $(ftgl_headers) \ $(NULL) libftgl_la_CPPFLAGS = -IFTGlyph -IFTFont -IFTLayout libftgl_la_CXXFLAGS = $(FT2_CFLAGS) $(GL_CFLAGS) libftgl_la_LDFLAGS = \ -no-undefined -version-number $(LT_VERSION) libftgl_la_LIBADD = \ $(FT2_LIBS) $(GL_LIBS) ftgldir = $(includedir)/FTGL ftgl_HEADERS = $(ftgl_headers) ftgl_headers = \ FTGL/ftgl.h \ FTGL/FTBBox.h \ FTGL/FTBuffer.h \ FTGL/FTPoint.h \ FTGL/FTGlyph.h \ FTGL/FTBitmapGlyph.h \ FTGL/FTBufferGlyph.h \ FTGL/FTExtrdGlyph.h \ FTGL/FTOutlineGlyph.h \ FTGL/FTPixmapGlyph.h \ FTGL/FTPolyGlyph.h \ FTGL/FTTextureGlyph.h \ FTGL/FTFont.h \ FTGL/FTGLBitmapFont.h \ FTGL/FTBufferFont.h \ FTGL/FTGLExtrdFont.h \ FTGL/FTGLOutlineFont.h \ FTGL/FTGLPixmapFont.h \ FTGL/FTGLPolygonFont.h \ FTGL/FTGLTextureFont.h \ FTGL/FTLayout.h \ FTGL/FTSimpleLayout.h \ ${NULL} ftglyph_sources = \ FTGlyph/FTGlyph.cpp \ FTGlyph/FTGlyphImpl.h \ FTGlyph/FTGlyphGlue.cpp \ FTGlyph/FTBitmapGlyph.cpp \ FTGlyph/FTBitmapGlyphImpl.h \ FTGlyph/FTBufferGlyph.cpp \ FTGlyph/FTBufferGlyphImpl.h \ FTGlyph/FTExtrudeGlyph.cpp \ FTGlyph/FTExtrudeGlyphImpl.h \ FTGlyph/FTOutlineGlyph.cpp \ FTGlyph/FTOutlineGlyphImpl.h \ FTGlyph/FTPixmapGlyph.cpp \ FTGlyph/FTPixmapGlyphImpl.h \ FTGlyph/FTPolygonGlyph.cpp \ FTGlyph/FTPolygonGlyphImpl.h \ FTGlyph/FTTextureGlyph.cpp \ FTGlyph/FTTextureGlyphImpl.h \ $(NULL) ftfont_sources = \ FTFont/FTFont.cpp \ FTFont/FTFontImpl.h \ FTFont/FTFontGlue.cpp \ FTFont/FTBitmapFont.cpp \ FTFont/FTBitmapFontImpl.h \ FTFont/FTBufferFont.cpp \ FTFont/FTBufferFontImpl.h \ FTFont/FTExtrudeFont.cpp \ FTFont/FTExtrudeFontImpl.h \ FTFont/FTOutlineFont.cpp \ FTFont/FTOutlineFontImpl.h \ FTFont/FTPixmapFont.cpp \ FTFont/FTPixmapFontImpl.h \ FTFont/FTPolygonFont.cpp \ FTFont/FTPolygonFontImpl.h \ FTFont/FTTextureFont.cpp \ FTFont/FTTextureFontImpl.h \ $(NULL) ftlayout_sources = \ FTLayout/FTLayout.cpp \ FTLayout/FTLayoutImpl.h \ FTLayout/FTLayoutGlue.cpp \ FTLayout/FTSimpleLayout.cpp \ FTLayout/FTSimpleLayoutImpl.h \ $(NULL) NULL = connectome-workbench-1.2.3+git41-gc4c6c90/src/FtglFont/Makefile.in000066400000000000000000001550131300200146000243410ustar00rootroot00000000000000# Makefile.in generated by automake 1.10.1 from Makefile.am. # @configure_input@ # Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, # 2003, 2004, 2005, 2006, 2007, 2008 Free Software Foundation, Inc. # This Makefile.in is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY, to the extent permitted by law; without # even the implied warranty of MERCHANTABILITY or FITNESS FOR A # PARTICULAR PURPOSE. @SET_MAKE@ VPATH = @srcdir@ pkgdatadir = $(datadir)/@PACKAGE@ pkglibdir = $(libdir)/@PACKAGE@ pkgincludedir = $(includedir)/@PACKAGE@ am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd install_sh_DATA = $(install_sh) -c -m 644 install_sh_PROGRAM = $(install_sh) -c install_sh_SCRIPT = $(install_sh) -c INSTALL_HEADER = $(INSTALL_DATA) transform = $(program_transform_name) NORMAL_INSTALL = : PRE_INSTALL = : POST_INSTALL = : NORMAL_UNINSTALL = : PRE_UNINSTALL = : POST_UNINSTALL = : build_triplet = @build@ host_triplet = @host@ subdir = src DIST_COMMON = $(ftgl_HEADERS) $(srcdir)/Makefile.am \ $(srcdir)/Makefile.in ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 am__aclocal_m4_deps = $(top_srcdir)/m4/cxx.m4 $(top_srcdir)/m4/font.m4 \ $(top_srcdir)/m4/freetype2.m4 $(top_srcdir)/m4/gl.m4 \ $(top_srcdir)/m4/glut.m4 $(top_srcdir)/m4/pkg.m4 \ $(top_srcdir)/configure.ac am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ $(ACLOCAL_M4) mkinstalldirs = $(install_sh) -d CONFIG_HEADER = $(top_builddir)/config.h CONFIG_CLEAN_FILES = am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; am__vpath_adj = case $$p in \ $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \ *) f=$$p;; \ esac; am__strip_dir = `echo $$p | sed -e 's|^.*/||'`; am__installdirs = "$(DESTDIR)$(libdir)" "$(DESTDIR)$(ftgldir)" libLTLIBRARIES_INSTALL = $(INSTALL) LTLIBRARIES = $(lib_LTLIBRARIES) am__DEPENDENCIES_1 = libftgl_la_DEPENDENCIES = $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) am__objects_1 = am__objects_2 = libftgl_la-FTGlyph.lo libftgl_la-FTGlyphGlue.lo \ libftgl_la-FTBitmapGlyph.lo libftgl_la-FTBufferGlyph.lo \ libftgl_la-FTExtrudeGlyph.lo libftgl_la-FTOutlineGlyph.lo \ libftgl_la-FTPixmapGlyph.lo libftgl_la-FTPolygonGlyph.lo \ libftgl_la-FTTextureGlyph.lo $(am__objects_1) am__objects_3 = libftgl_la-FTFont.lo libftgl_la-FTFontGlue.lo \ libftgl_la-FTBitmapFont.lo libftgl_la-FTBufferFont.lo \ libftgl_la-FTExtrudeFont.lo libftgl_la-FTOutlineFont.lo \ libftgl_la-FTPixmapFont.lo libftgl_la-FTPolygonFont.lo \ libftgl_la-FTTextureFont.lo $(am__objects_1) am__objects_4 = libftgl_la-FTLayout.lo libftgl_la-FTLayoutGlue.lo \ libftgl_la-FTSimpleLayout.lo $(am__objects_1) am__objects_5 = $(am__objects_1) am_libftgl_la_OBJECTS = libftgl_la-FTBuffer.lo libftgl_la-FTCharmap.lo \ libftgl_la-FTContour.lo libftgl_la-FTFace.lo \ libftgl_la-FTGlyphContainer.lo libftgl_la-FTLibrary.lo \ libftgl_la-FTPoint.lo libftgl_la-FTSize.lo \ libftgl_la-FTVectoriser.lo $(am__objects_2) $(am__objects_3) \ $(am__objects_4) $(am__objects_5) $(am__objects_1) libftgl_la_OBJECTS = $(am_libftgl_la_OBJECTS) libftgl_la_LINK = $(LIBTOOL) --tag=CXX $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=link $(CXXLD) $(libftgl_la_CXXFLAGS) \ $(CXXFLAGS) $(libftgl_la_LDFLAGS) $(LDFLAGS) -o $@ DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir) depcomp = $(SHELL) $(top_srcdir)/.auto/depcomp am__depfiles_maybe = depfiles CXXCOMPILE = $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) \ $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) LTCXXCOMPILE = $(LIBTOOL) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) \ --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) \ $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) CXXLD = $(CXX) CXXLINK = $(LIBTOOL) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) \ --mode=link $(CXXLD) $(AM_CXXFLAGS) $(CXXFLAGS) $(AM_LDFLAGS) \ $(LDFLAGS) -o $@ COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) LTCOMPILE = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) \ --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) \ $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) CCLD = $(CC) LINK = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) \ --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) $(AM_LDFLAGS) \ $(LDFLAGS) -o $@ SOURCES = $(libftgl_la_SOURCES) DIST_SOURCES = $(libftgl_la_SOURCES) ftglHEADERS_INSTALL = $(INSTALL_HEADER) HEADERS = $(ftgl_HEADERS) ETAGS = etags CTAGS = ctags DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) ACLOCAL = @ACLOCAL@ AMTAR = @AMTAR@ AR = @AR@ AUTOCONF = @AUTOCONF@ AUTOHEADER = @AUTOHEADER@ AUTOMAKE = @AUTOMAKE@ AWK = @AWK@ CC = @CC@ CCDEPMODE = @CCDEPMODE@ CFLAGS = @CFLAGS@ CONVERT = @CONVERT@ CPP = @CPP@ CPPFLAGS = @CPPFLAGS@ CPPUNIT_CFLAGS = @CPPUNIT_CFLAGS@ CPPUNIT_LIBS = @CPPUNIT_LIBS@ CXX = @CXX@ CXXCPP = @CXXCPP@ CXXDEPMODE = @CXXDEPMODE@ CXXFLAGS = @CXXFLAGS@ CYGPATH_W = @CYGPATH_W@ DEFS = @DEFS@ DEPDIR = @DEPDIR@ DOXYGEN = @DOXYGEN@ DSYMUTIL = @DSYMUTIL@ DVIPS = @DVIPS@ ECHO = @ECHO@ ECHO_C = @ECHO_C@ ECHO_N = @ECHO_N@ ECHO_T = @ECHO_T@ EGREP = @EGREP@ EPSTOPDF = @EPSTOPDF@ EXEEXT = @EXEEXT@ F77 = @F77@ FFLAGS = @FFLAGS@ FRAMEWORK_OPENGL = @FRAMEWORK_OPENGL@ FT2_CFLAGS = @FT2_CFLAGS@ FT2_CONFIG = @FT2_CONFIG@ FT2_LIBS = @FT2_LIBS@ GLUT_CFLAGS = @GLUT_CFLAGS@ GLUT_LIBS = @GLUT_LIBS@ GL_CFLAGS = @GL_CFLAGS@ GL_LIBS = @GL_LIBS@ GREP = @GREP@ INSTALL = @INSTALL@ INSTALL_DATA = @INSTALL_DATA@ INSTALL_PROGRAM = @INSTALL_PROGRAM@ INSTALL_SCRIPT = @INSTALL_SCRIPT@ INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ KPSEWHICH = @KPSEWHICH@ LATEX = @LATEX@ LDFLAGS = @LDFLAGS@ LIBOBJS = @LIBOBJS@ LIBS = @LIBS@ LIBTOOL = @LIBTOOL@ LN_S = @LN_S@ LTLIBOBJS = @LTLIBOBJS@ LT_MAJOR = @LT_MAJOR@ LT_MICRO = @LT_MICRO@ LT_MINOR = @LT_MINOR@ LT_VERSION = @LT_VERSION@ MAKEINFO = @MAKEINFO@ MKDIR_P = @MKDIR_P@ NMEDIT = @NMEDIT@ OBJEXT = @OBJEXT@ PACKAGE = @PACKAGE@ PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ PACKAGE_NAME = @PACKAGE_NAME@ PACKAGE_STRING = @PACKAGE_STRING@ PACKAGE_TARNAME = @PACKAGE_TARNAME@ PACKAGE_VERSION = @PACKAGE_VERSION@ PATH_SEPARATOR = @PATH_SEPARATOR@ PKG_CONFIG = @PKG_CONFIG@ RANLIB = @RANLIB@ SED = @SED@ SET_MAKE = @SET_MAKE@ SHELL = @SHELL@ STRIP = @STRIP@ VERSION = @VERSION@ XMKMF = @XMKMF@ X_CFLAGS = @X_CFLAGS@ X_EXTRA_LIBS = @X_EXTRA_LIBS@ X_LIBS = @X_LIBS@ X_PRE_LIBS = @X_PRE_LIBS@ abs_builddir = @abs_builddir@ abs_srcdir = @abs_srcdir@ abs_top_builddir = @abs_top_builddir@ abs_top_srcdir = @abs_top_srcdir@ ac_ct_CC = @ac_ct_CC@ ac_ct_CXX = @ac_ct_CXX@ ac_ct_F77 = @ac_ct_F77@ am__include = @am__include@ am__leading_dot = @am__leading_dot@ am__quote = @am__quote@ am__tar = @am__tar@ am__untar = @am__untar@ bindir = @bindir@ build = @build@ build_alias = @build_alias@ build_cpu = @build_cpu@ build_os = @build_os@ build_vendor = @build_vendor@ builddir = @builddir@ datadir = @datadir@ datarootdir = @datarootdir@ docdir = @docdir@ dvidir = @dvidir@ exec_prefix = @exec_prefix@ host = @host@ host_alias = @host_alias@ host_cpu = @host_cpu@ host_os = @host_os@ host_vendor = @host_vendor@ htmldir = @htmldir@ includedir = @includedir@ infodir = @infodir@ install_sh = @install_sh@ libdir = @libdir@ libexecdir = @libexecdir@ localedir = @localedir@ localstatedir = @localstatedir@ mandir = @mandir@ mkdir_p = @mkdir_p@ oldincludedir = @oldincludedir@ pdfdir = @pdfdir@ prefix = @prefix@ program_transform_name = @program_transform_name@ psdir = @psdir@ sbindir = @sbindir@ sharedstatedir = @sharedstatedir@ srcdir = @srcdir@ sysconfdir = @sysconfdir@ target_alias = @target_alias@ top_builddir = @top_builddir@ top_srcdir = @top_srcdir@ lib_LTLIBRARIES = libftgl.la libftgl_la_SOURCES = \ FTBuffer.cpp \ FTCharmap.cpp \ FTCharmap.h \ FTCharToGlyphIndexMap.h \ FTContour.cpp \ FTContour.h \ FTFace.cpp \ FTFace.h \ FTGlyphContainer.cpp \ FTGlyphContainer.h \ FTInternals.h \ FTLibrary.cpp \ FTLibrary.h \ FTList.h \ FTPoint.cpp \ FTSize.cpp \ FTSize.h \ FTVector.h \ FTVectoriser.cpp \ FTVectoriser.h \ FTUnicode.h \ $(ftglyph_sources) \ $(ftfont_sources) \ $(ftlayout_sources) \ $(ftgl_headers) \ $(NULL) libftgl_la_CPPFLAGS = -IFTGlyph -IFTFont -IFTLayout libftgl_la_CXXFLAGS = $(FT2_CFLAGS) $(GL_CFLAGS) libftgl_la_LDFLAGS = \ -no-undefined -version-number $(LT_VERSION) libftgl_la_LIBADD = \ $(FT2_LIBS) $(GL_LIBS) ftgldir = $(includedir)/FTGL ftgl_HEADERS = $(ftgl_headers) ftgl_headers = \ FTGL/ftgl.h \ FTGL/FTBBox.h \ FTGL/FTBuffer.h \ FTGL/FTPoint.h \ FTGL/FTGlyph.h \ FTGL/FTBitmapGlyph.h \ FTGL/FTBufferGlyph.h \ FTGL/FTExtrdGlyph.h \ FTGL/FTOutlineGlyph.h \ FTGL/FTPixmapGlyph.h \ FTGL/FTPolyGlyph.h \ FTGL/FTTextureGlyph.h \ FTGL/FTFont.h \ FTGL/FTGLBitmapFont.h \ FTGL/FTBufferFont.h \ FTGL/FTGLExtrdFont.h \ FTGL/FTGLOutlineFont.h \ FTGL/FTGLPixmapFont.h \ FTGL/FTGLPolygonFont.h \ FTGL/FTGLTextureFont.h \ FTGL/FTLayout.h \ FTGL/FTSimpleLayout.h \ ${NULL} ftglyph_sources = \ FTGlyph/FTGlyph.cpp \ FTGlyph/FTGlyphImpl.h \ FTGlyph/FTGlyphGlue.cpp \ FTGlyph/FTBitmapGlyph.cpp \ FTGlyph/FTBitmapGlyphImpl.h \ FTGlyph/FTBufferGlyph.cpp \ FTGlyph/FTBufferGlyphImpl.h \ FTGlyph/FTExtrudeGlyph.cpp \ FTGlyph/FTExtrudeGlyphImpl.h \ FTGlyph/FTOutlineGlyph.cpp \ FTGlyph/FTOutlineGlyphImpl.h \ FTGlyph/FTPixmapGlyph.cpp \ FTGlyph/FTPixmapGlyphImpl.h \ FTGlyph/FTPolygonGlyph.cpp \ FTGlyph/FTPolygonGlyphImpl.h \ FTGlyph/FTTextureGlyph.cpp \ FTGlyph/FTTextureGlyphImpl.h \ $(NULL) ftfont_sources = \ FTFont/FTFont.cpp \ FTFont/FTFontImpl.h \ FTFont/FTFontGlue.cpp \ FTFont/FTBitmapFont.cpp \ FTFont/FTBitmapFontImpl.h \ FTFont/FTBufferFont.cpp \ FTFont/FTBufferFontImpl.h \ FTFont/FTExtrudeFont.cpp \ FTFont/FTExtrudeFontImpl.h \ FTFont/FTOutlineFont.cpp \ FTFont/FTOutlineFontImpl.h \ FTFont/FTPixmapFont.cpp \ FTFont/FTPixmapFontImpl.h \ FTFont/FTPolygonFont.cpp \ FTFont/FTPolygonFontImpl.h \ FTFont/FTTextureFont.cpp \ FTFont/FTTextureFontImpl.h \ $(NULL) ftlayout_sources = \ FTLayout/FTLayout.cpp \ FTLayout/FTLayoutImpl.h \ FTLayout/FTLayoutGlue.cpp \ FTLayout/FTSimpleLayout.cpp \ FTLayout/FTSimpleLayoutImpl.h \ $(NULL) NULL = all: all-am .SUFFIXES: .SUFFIXES: .cpp .lo .o .obj $(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(am__configure_deps) @for dep in $?; do \ case '$(am__configure_deps)' in \ *$$dep*) \ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh \ && exit 0; \ exit 1;; \ esac; \ done; \ echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu src/Makefile'; \ cd $(top_srcdir) && \ $(AUTOMAKE) --gnu src/Makefile .PRECIOUS: Makefile Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status @case '$?' in \ *config.status*) \ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ *) \ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ esac; $(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(top_srcdir)/configure: $(am__configure_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(ACLOCAL_M4): $(am__aclocal_m4_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh install-libLTLIBRARIES: $(lib_LTLIBRARIES) @$(NORMAL_INSTALL) test -z "$(libdir)" || $(MKDIR_P) "$(DESTDIR)$(libdir)" @list='$(lib_LTLIBRARIES)'; for p in $$list; do \ if test -f $$p; then \ f=$(am__strip_dir) \ echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(libLTLIBRARIES_INSTALL) $(INSTALL_STRIP_FLAG) '$$p' '$(DESTDIR)$(libdir)/$$f'"; \ $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(libLTLIBRARIES_INSTALL) $(INSTALL_STRIP_FLAG) "$$p" "$(DESTDIR)$(libdir)/$$f"; \ else :; fi; \ done uninstall-libLTLIBRARIES: @$(NORMAL_UNINSTALL) @list='$(lib_LTLIBRARIES)'; for p in $$list; do \ p=$(am__strip_dir) \ echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f '$(DESTDIR)$(libdir)/$$p'"; \ $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f "$(DESTDIR)$(libdir)/$$p"; \ done clean-libLTLIBRARIES: -test -z "$(lib_LTLIBRARIES)" || rm -f $(lib_LTLIBRARIES) @list='$(lib_LTLIBRARIES)'; for p in $$list; do \ dir="`echo $$p | sed -e 's|/[^/]*$$||'`"; \ test "$$dir" != "$$p" || dir=.; \ echo "rm -f \"$${dir}/so_locations\""; \ rm -f "$${dir}/so_locations"; \ done libftgl.la: $(libftgl_la_OBJECTS) $(libftgl_la_DEPENDENCIES) $(libftgl_la_LINK) -rpath $(libdir) $(libftgl_la_OBJECTS) $(libftgl_la_LIBADD) $(LIBS) mostlyclean-compile: -rm -f *.$(OBJEXT) distclean-compile: -rm -f *.tab.c @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libftgl_la-FTBitmapFont.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libftgl_la-FTBitmapGlyph.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libftgl_la-FTBuffer.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libftgl_la-FTBufferFont.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libftgl_la-FTBufferGlyph.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libftgl_la-FTCharmap.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libftgl_la-FTContour.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libftgl_la-FTExtrudeFont.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libftgl_la-FTExtrudeGlyph.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libftgl_la-FTFace.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libftgl_la-FTFont.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libftgl_la-FTFontGlue.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libftgl_la-FTGlyph.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libftgl_la-FTGlyphContainer.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libftgl_la-FTGlyphGlue.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libftgl_la-FTLayout.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libftgl_la-FTLayoutGlue.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libftgl_la-FTLibrary.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libftgl_la-FTOutlineFont.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libftgl_la-FTOutlineGlyph.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libftgl_la-FTPixmapFont.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libftgl_la-FTPixmapGlyph.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libftgl_la-FTPoint.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libftgl_la-FTPolygonFont.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libftgl_la-FTPolygonGlyph.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libftgl_la-FTSimpleLayout.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libftgl_la-FTSize.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libftgl_la-FTTextureFont.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libftgl_la-FTTextureGlyph.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libftgl_la-FTVectoriser.Plo@am__quote@ .cpp.o: @am__fastdepCXX_TRUE@ $(CXXCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< @am__fastdepCXX_TRUE@ mv -f $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po @AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCXX_FALSE@ $(CXXCOMPILE) -c -o $@ $< .cpp.obj: @am__fastdepCXX_TRUE@ $(CXXCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'` @am__fastdepCXX_TRUE@ mv -f $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po @AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCXX_FALSE@ $(CXXCOMPILE) -c -o $@ `$(CYGPATH_W) '$<'` .cpp.lo: @am__fastdepCXX_TRUE@ $(LTCXXCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< @am__fastdepCXX_TRUE@ mv -f $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo @AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCXX_FALSE@ $(LTCXXCOMPILE) -c -o $@ $< libftgl_la-FTBuffer.lo: FTBuffer.cpp @am__fastdepCXX_TRUE@ $(LIBTOOL) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libftgl_la_CPPFLAGS) $(CPPFLAGS) $(libftgl_la_CXXFLAGS) $(CXXFLAGS) -MT libftgl_la-FTBuffer.lo -MD -MP -MF $(DEPDIR)/libftgl_la-FTBuffer.Tpo -c -o libftgl_la-FTBuffer.lo `test -f 'FTBuffer.cpp' || echo '$(srcdir)/'`FTBuffer.cpp @am__fastdepCXX_TRUE@ mv -f $(DEPDIR)/libftgl_la-FTBuffer.Tpo $(DEPDIR)/libftgl_la-FTBuffer.Plo @AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='FTBuffer.cpp' object='libftgl_la-FTBuffer.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCXX_FALSE@ $(LIBTOOL) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libftgl_la_CPPFLAGS) $(CPPFLAGS) $(libftgl_la_CXXFLAGS) $(CXXFLAGS) -c -o libftgl_la-FTBuffer.lo `test -f 'FTBuffer.cpp' || echo '$(srcdir)/'`FTBuffer.cpp libftgl_la-FTCharmap.lo: FTCharmap.cpp @am__fastdepCXX_TRUE@ $(LIBTOOL) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libftgl_la_CPPFLAGS) $(CPPFLAGS) $(libftgl_la_CXXFLAGS) $(CXXFLAGS) -MT libftgl_la-FTCharmap.lo -MD -MP -MF $(DEPDIR)/libftgl_la-FTCharmap.Tpo -c -o libftgl_la-FTCharmap.lo `test -f 'FTCharmap.cpp' || echo '$(srcdir)/'`FTCharmap.cpp @am__fastdepCXX_TRUE@ mv -f $(DEPDIR)/libftgl_la-FTCharmap.Tpo $(DEPDIR)/libftgl_la-FTCharmap.Plo @AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='FTCharmap.cpp' object='libftgl_la-FTCharmap.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCXX_FALSE@ $(LIBTOOL) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libftgl_la_CPPFLAGS) $(CPPFLAGS) $(libftgl_la_CXXFLAGS) $(CXXFLAGS) -c -o libftgl_la-FTCharmap.lo `test -f 'FTCharmap.cpp' || echo '$(srcdir)/'`FTCharmap.cpp libftgl_la-FTContour.lo: FTContour.cpp @am__fastdepCXX_TRUE@ $(LIBTOOL) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libftgl_la_CPPFLAGS) $(CPPFLAGS) $(libftgl_la_CXXFLAGS) $(CXXFLAGS) -MT libftgl_la-FTContour.lo -MD -MP -MF $(DEPDIR)/libftgl_la-FTContour.Tpo -c -o libftgl_la-FTContour.lo `test -f 'FTContour.cpp' || echo '$(srcdir)/'`FTContour.cpp @am__fastdepCXX_TRUE@ mv -f $(DEPDIR)/libftgl_la-FTContour.Tpo $(DEPDIR)/libftgl_la-FTContour.Plo @AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='FTContour.cpp' object='libftgl_la-FTContour.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCXX_FALSE@ $(LIBTOOL) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libftgl_la_CPPFLAGS) $(CPPFLAGS) $(libftgl_la_CXXFLAGS) $(CXXFLAGS) -c -o libftgl_la-FTContour.lo `test -f 'FTContour.cpp' || echo '$(srcdir)/'`FTContour.cpp libftgl_la-FTFace.lo: FTFace.cpp @am__fastdepCXX_TRUE@ $(LIBTOOL) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libftgl_la_CPPFLAGS) $(CPPFLAGS) $(libftgl_la_CXXFLAGS) $(CXXFLAGS) -MT libftgl_la-FTFace.lo -MD -MP -MF $(DEPDIR)/libftgl_la-FTFace.Tpo -c -o libftgl_la-FTFace.lo `test -f 'FTFace.cpp' || echo '$(srcdir)/'`FTFace.cpp @am__fastdepCXX_TRUE@ mv -f $(DEPDIR)/libftgl_la-FTFace.Tpo $(DEPDIR)/libftgl_la-FTFace.Plo @AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='FTFace.cpp' object='libftgl_la-FTFace.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCXX_FALSE@ $(LIBTOOL) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libftgl_la_CPPFLAGS) $(CPPFLAGS) $(libftgl_la_CXXFLAGS) $(CXXFLAGS) -c -o libftgl_la-FTFace.lo `test -f 'FTFace.cpp' || echo '$(srcdir)/'`FTFace.cpp libftgl_la-FTGlyphContainer.lo: FTGlyphContainer.cpp @am__fastdepCXX_TRUE@ $(LIBTOOL) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libftgl_la_CPPFLAGS) $(CPPFLAGS) $(libftgl_la_CXXFLAGS) $(CXXFLAGS) -MT libftgl_la-FTGlyphContainer.lo -MD -MP -MF $(DEPDIR)/libftgl_la-FTGlyphContainer.Tpo -c -o libftgl_la-FTGlyphContainer.lo `test -f 'FTGlyphContainer.cpp' || echo '$(srcdir)/'`FTGlyphContainer.cpp @am__fastdepCXX_TRUE@ mv -f $(DEPDIR)/libftgl_la-FTGlyphContainer.Tpo $(DEPDIR)/libftgl_la-FTGlyphContainer.Plo @AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='FTGlyphContainer.cpp' object='libftgl_la-FTGlyphContainer.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCXX_FALSE@ $(LIBTOOL) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libftgl_la_CPPFLAGS) $(CPPFLAGS) $(libftgl_la_CXXFLAGS) $(CXXFLAGS) -c -o libftgl_la-FTGlyphContainer.lo `test -f 'FTGlyphContainer.cpp' || echo '$(srcdir)/'`FTGlyphContainer.cpp libftgl_la-FTLibrary.lo: FTLibrary.cpp @am__fastdepCXX_TRUE@ $(LIBTOOL) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libftgl_la_CPPFLAGS) $(CPPFLAGS) $(libftgl_la_CXXFLAGS) $(CXXFLAGS) -MT libftgl_la-FTLibrary.lo -MD -MP -MF $(DEPDIR)/libftgl_la-FTLibrary.Tpo -c -o libftgl_la-FTLibrary.lo `test -f 'FTLibrary.cpp' || echo '$(srcdir)/'`FTLibrary.cpp @am__fastdepCXX_TRUE@ mv -f $(DEPDIR)/libftgl_la-FTLibrary.Tpo $(DEPDIR)/libftgl_la-FTLibrary.Plo @AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='FTLibrary.cpp' object='libftgl_la-FTLibrary.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCXX_FALSE@ $(LIBTOOL) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libftgl_la_CPPFLAGS) $(CPPFLAGS) $(libftgl_la_CXXFLAGS) $(CXXFLAGS) -c -o libftgl_la-FTLibrary.lo `test -f 'FTLibrary.cpp' || echo '$(srcdir)/'`FTLibrary.cpp libftgl_la-FTPoint.lo: FTPoint.cpp @am__fastdepCXX_TRUE@ $(LIBTOOL) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libftgl_la_CPPFLAGS) $(CPPFLAGS) $(libftgl_la_CXXFLAGS) $(CXXFLAGS) -MT libftgl_la-FTPoint.lo -MD -MP -MF $(DEPDIR)/libftgl_la-FTPoint.Tpo -c -o libftgl_la-FTPoint.lo `test -f 'FTPoint.cpp' || echo '$(srcdir)/'`FTPoint.cpp @am__fastdepCXX_TRUE@ mv -f $(DEPDIR)/libftgl_la-FTPoint.Tpo $(DEPDIR)/libftgl_la-FTPoint.Plo @AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='FTPoint.cpp' object='libftgl_la-FTPoint.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCXX_FALSE@ $(LIBTOOL) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libftgl_la_CPPFLAGS) $(CPPFLAGS) $(libftgl_la_CXXFLAGS) $(CXXFLAGS) -c -o libftgl_la-FTPoint.lo `test -f 'FTPoint.cpp' || echo '$(srcdir)/'`FTPoint.cpp libftgl_la-FTSize.lo: FTSize.cpp @am__fastdepCXX_TRUE@ $(LIBTOOL) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libftgl_la_CPPFLAGS) $(CPPFLAGS) $(libftgl_la_CXXFLAGS) $(CXXFLAGS) -MT libftgl_la-FTSize.lo -MD -MP -MF $(DEPDIR)/libftgl_la-FTSize.Tpo -c -o libftgl_la-FTSize.lo `test -f 'FTSize.cpp' || echo '$(srcdir)/'`FTSize.cpp @am__fastdepCXX_TRUE@ mv -f $(DEPDIR)/libftgl_la-FTSize.Tpo $(DEPDIR)/libftgl_la-FTSize.Plo @AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='FTSize.cpp' object='libftgl_la-FTSize.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCXX_FALSE@ $(LIBTOOL) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libftgl_la_CPPFLAGS) $(CPPFLAGS) $(libftgl_la_CXXFLAGS) $(CXXFLAGS) -c -o libftgl_la-FTSize.lo `test -f 'FTSize.cpp' || echo '$(srcdir)/'`FTSize.cpp libftgl_la-FTVectoriser.lo: FTVectoriser.cpp @am__fastdepCXX_TRUE@ $(LIBTOOL) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libftgl_la_CPPFLAGS) $(CPPFLAGS) $(libftgl_la_CXXFLAGS) $(CXXFLAGS) -MT libftgl_la-FTVectoriser.lo -MD -MP -MF $(DEPDIR)/libftgl_la-FTVectoriser.Tpo -c -o libftgl_la-FTVectoriser.lo `test -f 'FTVectoriser.cpp' || echo '$(srcdir)/'`FTVectoriser.cpp @am__fastdepCXX_TRUE@ mv -f $(DEPDIR)/libftgl_la-FTVectoriser.Tpo $(DEPDIR)/libftgl_la-FTVectoriser.Plo @AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='FTVectoriser.cpp' object='libftgl_la-FTVectoriser.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCXX_FALSE@ $(LIBTOOL) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libftgl_la_CPPFLAGS) $(CPPFLAGS) $(libftgl_la_CXXFLAGS) $(CXXFLAGS) -c -o libftgl_la-FTVectoriser.lo `test -f 'FTVectoriser.cpp' || echo '$(srcdir)/'`FTVectoriser.cpp libftgl_la-FTGlyph.lo: FTGlyph/FTGlyph.cpp @am__fastdepCXX_TRUE@ $(LIBTOOL) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libftgl_la_CPPFLAGS) $(CPPFLAGS) $(libftgl_la_CXXFLAGS) $(CXXFLAGS) -MT libftgl_la-FTGlyph.lo -MD -MP -MF $(DEPDIR)/libftgl_la-FTGlyph.Tpo -c -o libftgl_la-FTGlyph.lo `test -f 'FTGlyph/FTGlyph.cpp' || echo '$(srcdir)/'`FTGlyph/FTGlyph.cpp @am__fastdepCXX_TRUE@ mv -f $(DEPDIR)/libftgl_la-FTGlyph.Tpo $(DEPDIR)/libftgl_la-FTGlyph.Plo @AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='FTGlyph/FTGlyph.cpp' object='libftgl_la-FTGlyph.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCXX_FALSE@ $(LIBTOOL) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libftgl_la_CPPFLAGS) $(CPPFLAGS) $(libftgl_la_CXXFLAGS) $(CXXFLAGS) -c -o libftgl_la-FTGlyph.lo `test -f 'FTGlyph/FTGlyph.cpp' || echo '$(srcdir)/'`FTGlyph/FTGlyph.cpp libftgl_la-FTGlyphGlue.lo: FTGlyph/FTGlyphGlue.cpp @am__fastdepCXX_TRUE@ $(LIBTOOL) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libftgl_la_CPPFLAGS) $(CPPFLAGS) $(libftgl_la_CXXFLAGS) $(CXXFLAGS) -MT libftgl_la-FTGlyphGlue.lo -MD -MP -MF $(DEPDIR)/libftgl_la-FTGlyphGlue.Tpo -c -o libftgl_la-FTGlyphGlue.lo `test -f 'FTGlyph/FTGlyphGlue.cpp' || echo '$(srcdir)/'`FTGlyph/FTGlyphGlue.cpp @am__fastdepCXX_TRUE@ mv -f $(DEPDIR)/libftgl_la-FTGlyphGlue.Tpo $(DEPDIR)/libftgl_la-FTGlyphGlue.Plo @AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='FTGlyph/FTGlyphGlue.cpp' object='libftgl_la-FTGlyphGlue.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCXX_FALSE@ $(LIBTOOL) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libftgl_la_CPPFLAGS) $(CPPFLAGS) $(libftgl_la_CXXFLAGS) $(CXXFLAGS) -c -o libftgl_la-FTGlyphGlue.lo `test -f 'FTGlyph/FTGlyphGlue.cpp' || echo '$(srcdir)/'`FTGlyph/FTGlyphGlue.cpp libftgl_la-FTBitmapGlyph.lo: FTGlyph/FTBitmapGlyph.cpp @am__fastdepCXX_TRUE@ $(LIBTOOL) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libftgl_la_CPPFLAGS) $(CPPFLAGS) $(libftgl_la_CXXFLAGS) $(CXXFLAGS) -MT libftgl_la-FTBitmapGlyph.lo -MD -MP -MF $(DEPDIR)/libftgl_la-FTBitmapGlyph.Tpo -c -o libftgl_la-FTBitmapGlyph.lo `test -f 'FTGlyph/FTBitmapGlyph.cpp' || echo '$(srcdir)/'`FTGlyph/FTBitmapGlyph.cpp @am__fastdepCXX_TRUE@ mv -f $(DEPDIR)/libftgl_la-FTBitmapGlyph.Tpo $(DEPDIR)/libftgl_la-FTBitmapGlyph.Plo @AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='FTGlyph/FTBitmapGlyph.cpp' object='libftgl_la-FTBitmapGlyph.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCXX_FALSE@ $(LIBTOOL) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libftgl_la_CPPFLAGS) $(CPPFLAGS) $(libftgl_la_CXXFLAGS) $(CXXFLAGS) -c -o libftgl_la-FTBitmapGlyph.lo `test -f 'FTGlyph/FTBitmapGlyph.cpp' || echo '$(srcdir)/'`FTGlyph/FTBitmapGlyph.cpp libftgl_la-FTBufferGlyph.lo: FTGlyph/FTBufferGlyph.cpp @am__fastdepCXX_TRUE@ $(LIBTOOL) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libftgl_la_CPPFLAGS) $(CPPFLAGS) $(libftgl_la_CXXFLAGS) $(CXXFLAGS) -MT libftgl_la-FTBufferGlyph.lo -MD -MP -MF $(DEPDIR)/libftgl_la-FTBufferGlyph.Tpo -c -o libftgl_la-FTBufferGlyph.lo `test -f 'FTGlyph/FTBufferGlyph.cpp' || echo '$(srcdir)/'`FTGlyph/FTBufferGlyph.cpp @am__fastdepCXX_TRUE@ mv -f $(DEPDIR)/libftgl_la-FTBufferGlyph.Tpo $(DEPDIR)/libftgl_la-FTBufferGlyph.Plo @AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='FTGlyph/FTBufferGlyph.cpp' object='libftgl_la-FTBufferGlyph.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCXX_FALSE@ $(LIBTOOL) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libftgl_la_CPPFLAGS) $(CPPFLAGS) $(libftgl_la_CXXFLAGS) $(CXXFLAGS) -c -o libftgl_la-FTBufferGlyph.lo `test -f 'FTGlyph/FTBufferGlyph.cpp' || echo '$(srcdir)/'`FTGlyph/FTBufferGlyph.cpp libftgl_la-FTExtrudeGlyph.lo: FTGlyph/FTExtrudeGlyph.cpp @am__fastdepCXX_TRUE@ $(LIBTOOL) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libftgl_la_CPPFLAGS) $(CPPFLAGS) $(libftgl_la_CXXFLAGS) $(CXXFLAGS) -MT libftgl_la-FTExtrudeGlyph.lo -MD -MP -MF $(DEPDIR)/libftgl_la-FTExtrudeGlyph.Tpo -c -o libftgl_la-FTExtrudeGlyph.lo `test -f 'FTGlyph/FTExtrudeGlyph.cpp' || echo '$(srcdir)/'`FTGlyph/FTExtrudeGlyph.cpp @am__fastdepCXX_TRUE@ mv -f $(DEPDIR)/libftgl_la-FTExtrudeGlyph.Tpo $(DEPDIR)/libftgl_la-FTExtrudeGlyph.Plo @AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='FTGlyph/FTExtrudeGlyph.cpp' object='libftgl_la-FTExtrudeGlyph.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCXX_FALSE@ $(LIBTOOL) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libftgl_la_CPPFLAGS) $(CPPFLAGS) $(libftgl_la_CXXFLAGS) $(CXXFLAGS) -c -o libftgl_la-FTExtrudeGlyph.lo `test -f 'FTGlyph/FTExtrudeGlyph.cpp' || echo '$(srcdir)/'`FTGlyph/FTExtrudeGlyph.cpp libftgl_la-FTOutlineGlyph.lo: FTGlyph/FTOutlineGlyph.cpp @am__fastdepCXX_TRUE@ $(LIBTOOL) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libftgl_la_CPPFLAGS) $(CPPFLAGS) $(libftgl_la_CXXFLAGS) $(CXXFLAGS) -MT libftgl_la-FTOutlineGlyph.lo -MD -MP -MF $(DEPDIR)/libftgl_la-FTOutlineGlyph.Tpo -c -o libftgl_la-FTOutlineGlyph.lo `test -f 'FTGlyph/FTOutlineGlyph.cpp' || echo '$(srcdir)/'`FTGlyph/FTOutlineGlyph.cpp @am__fastdepCXX_TRUE@ mv -f $(DEPDIR)/libftgl_la-FTOutlineGlyph.Tpo $(DEPDIR)/libftgl_la-FTOutlineGlyph.Plo @AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='FTGlyph/FTOutlineGlyph.cpp' object='libftgl_la-FTOutlineGlyph.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCXX_FALSE@ $(LIBTOOL) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libftgl_la_CPPFLAGS) $(CPPFLAGS) $(libftgl_la_CXXFLAGS) $(CXXFLAGS) -c -o libftgl_la-FTOutlineGlyph.lo `test -f 'FTGlyph/FTOutlineGlyph.cpp' || echo '$(srcdir)/'`FTGlyph/FTOutlineGlyph.cpp libftgl_la-FTPixmapGlyph.lo: FTGlyph/FTPixmapGlyph.cpp @am__fastdepCXX_TRUE@ $(LIBTOOL) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libftgl_la_CPPFLAGS) $(CPPFLAGS) $(libftgl_la_CXXFLAGS) $(CXXFLAGS) -MT libftgl_la-FTPixmapGlyph.lo -MD -MP -MF $(DEPDIR)/libftgl_la-FTPixmapGlyph.Tpo -c -o libftgl_la-FTPixmapGlyph.lo `test -f 'FTGlyph/FTPixmapGlyph.cpp' || echo '$(srcdir)/'`FTGlyph/FTPixmapGlyph.cpp @am__fastdepCXX_TRUE@ mv -f $(DEPDIR)/libftgl_la-FTPixmapGlyph.Tpo $(DEPDIR)/libftgl_la-FTPixmapGlyph.Plo @AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='FTGlyph/FTPixmapGlyph.cpp' object='libftgl_la-FTPixmapGlyph.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCXX_FALSE@ $(LIBTOOL) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libftgl_la_CPPFLAGS) $(CPPFLAGS) $(libftgl_la_CXXFLAGS) $(CXXFLAGS) -c -o libftgl_la-FTPixmapGlyph.lo `test -f 'FTGlyph/FTPixmapGlyph.cpp' || echo '$(srcdir)/'`FTGlyph/FTPixmapGlyph.cpp libftgl_la-FTPolygonGlyph.lo: FTGlyph/FTPolygonGlyph.cpp @am__fastdepCXX_TRUE@ $(LIBTOOL) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libftgl_la_CPPFLAGS) $(CPPFLAGS) $(libftgl_la_CXXFLAGS) $(CXXFLAGS) -MT libftgl_la-FTPolygonGlyph.lo -MD -MP -MF $(DEPDIR)/libftgl_la-FTPolygonGlyph.Tpo -c -o libftgl_la-FTPolygonGlyph.lo `test -f 'FTGlyph/FTPolygonGlyph.cpp' || echo '$(srcdir)/'`FTGlyph/FTPolygonGlyph.cpp @am__fastdepCXX_TRUE@ mv -f $(DEPDIR)/libftgl_la-FTPolygonGlyph.Tpo $(DEPDIR)/libftgl_la-FTPolygonGlyph.Plo @AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='FTGlyph/FTPolygonGlyph.cpp' object='libftgl_la-FTPolygonGlyph.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCXX_FALSE@ $(LIBTOOL) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libftgl_la_CPPFLAGS) $(CPPFLAGS) $(libftgl_la_CXXFLAGS) $(CXXFLAGS) -c -o libftgl_la-FTPolygonGlyph.lo `test -f 'FTGlyph/FTPolygonGlyph.cpp' || echo '$(srcdir)/'`FTGlyph/FTPolygonGlyph.cpp libftgl_la-FTTextureGlyph.lo: FTGlyph/FTTextureGlyph.cpp @am__fastdepCXX_TRUE@ $(LIBTOOL) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libftgl_la_CPPFLAGS) $(CPPFLAGS) $(libftgl_la_CXXFLAGS) $(CXXFLAGS) -MT libftgl_la-FTTextureGlyph.lo -MD -MP -MF $(DEPDIR)/libftgl_la-FTTextureGlyph.Tpo -c -o libftgl_la-FTTextureGlyph.lo `test -f 'FTGlyph/FTTextureGlyph.cpp' || echo '$(srcdir)/'`FTGlyph/FTTextureGlyph.cpp @am__fastdepCXX_TRUE@ mv -f $(DEPDIR)/libftgl_la-FTTextureGlyph.Tpo $(DEPDIR)/libftgl_la-FTTextureGlyph.Plo @AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='FTGlyph/FTTextureGlyph.cpp' object='libftgl_la-FTTextureGlyph.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCXX_FALSE@ $(LIBTOOL) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libftgl_la_CPPFLAGS) $(CPPFLAGS) $(libftgl_la_CXXFLAGS) $(CXXFLAGS) -c -o libftgl_la-FTTextureGlyph.lo `test -f 'FTGlyph/FTTextureGlyph.cpp' || echo '$(srcdir)/'`FTGlyph/FTTextureGlyph.cpp libftgl_la-FTFont.lo: FTFont/FTFont.cpp @am__fastdepCXX_TRUE@ $(LIBTOOL) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libftgl_la_CPPFLAGS) $(CPPFLAGS) $(libftgl_la_CXXFLAGS) $(CXXFLAGS) -MT libftgl_la-FTFont.lo -MD -MP -MF $(DEPDIR)/libftgl_la-FTFont.Tpo -c -o libftgl_la-FTFont.lo `test -f 'FTFont/FTFont.cpp' || echo '$(srcdir)/'`FTFont/FTFont.cpp @am__fastdepCXX_TRUE@ mv -f $(DEPDIR)/libftgl_la-FTFont.Tpo $(DEPDIR)/libftgl_la-FTFont.Plo @AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='FTFont/FTFont.cpp' object='libftgl_la-FTFont.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCXX_FALSE@ $(LIBTOOL) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libftgl_la_CPPFLAGS) $(CPPFLAGS) $(libftgl_la_CXXFLAGS) $(CXXFLAGS) -c -o libftgl_la-FTFont.lo `test -f 'FTFont/FTFont.cpp' || echo '$(srcdir)/'`FTFont/FTFont.cpp libftgl_la-FTFontGlue.lo: FTFont/FTFontGlue.cpp @am__fastdepCXX_TRUE@ $(LIBTOOL) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libftgl_la_CPPFLAGS) $(CPPFLAGS) $(libftgl_la_CXXFLAGS) $(CXXFLAGS) -MT libftgl_la-FTFontGlue.lo -MD -MP -MF $(DEPDIR)/libftgl_la-FTFontGlue.Tpo -c -o libftgl_la-FTFontGlue.lo `test -f 'FTFont/FTFontGlue.cpp' || echo '$(srcdir)/'`FTFont/FTFontGlue.cpp @am__fastdepCXX_TRUE@ mv -f $(DEPDIR)/libftgl_la-FTFontGlue.Tpo $(DEPDIR)/libftgl_la-FTFontGlue.Plo @AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='FTFont/FTFontGlue.cpp' object='libftgl_la-FTFontGlue.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCXX_FALSE@ $(LIBTOOL) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libftgl_la_CPPFLAGS) $(CPPFLAGS) $(libftgl_la_CXXFLAGS) $(CXXFLAGS) -c -o libftgl_la-FTFontGlue.lo `test -f 'FTFont/FTFontGlue.cpp' || echo '$(srcdir)/'`FTFont/FTFontGlue.cpp libftgl_la-FTBitmapFont.lo: FTFont/FTBitmapFont.cpp @am__fastdepCXX_TRUE@ $(LIBTOOL) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libftgl_la_CPPFLAGS) $(CPPFLAGS) $(libftgl_la_CXXFLAGS) $(CXXFLAGS) -MT libftgl_la-FTBitmapFont.lo -MD -MP -MF $(DEPDIR)/libftgl_la-FTBitmapFont.Tpo -c -o libftgl_la-FTBitmapFont.lo `test -f 'FTFont/FTBitmapFont.cpp' || echo '$(srcdir)/'`FTFont/FTBitmapFont.cpp @am__fastdepCXX_TRUE@ mv -f $(DEPDIR)/libftgl_la-FTBitmapFont.Tpo $(DEPDIR)/libftgl_la-FTBitmapFont.Plo @AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='FTFont/FTBitmapFont.cpp' object='libftgl_la-FTBitmapFont.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCXX_FALSE@ $(LIBTOOL) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libftgl_la_CPPFLAGS) $(CPPFLAGS) $(libftgl_la_CXXFLAGS) $(CXXFLAGS) -c -o libftgl_la-FTBitmapFont.lo `test -f 'FTFont/FTBitmapFont.cpp' || echo '$(srcdir)/'`FTFont/FTBitmapFont.cpp libftgl_la-FTBufferFont.lo: FTFont/FTBufferFont.cpp @am__fastdepCXX_TRUE@ $(LIBTOOL) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libftgl_la_CPPFLAGS) $(CPPFLAGS) $(libftgl_la_CXXFLAGS) $(CXXFLAGS) -MT libftgl_la-FTBufferFont.lo -MD -MP -MF $(DEPDIR)/libftgl_la-FTBufferFont.Tpo -c -o libftgl_la-FTBufferFont.lo `test -f 'FTFont/FTBufferFont.cpp' || echo '$(srcdir)/'`FTFont/FTBufferFont.cpp @am__fastdepCXX_TRUE@ mv -f $(DEPDIR)/libftgl_la-FTBufferFont.Tpo $(DEPDIR)/libftgl_la-FTBufferFont.Plo @AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='FTFont/FTBufferFont.cpp' object='libftgl_la-FTBufferFont.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCXX_FALSE@ $(LIBTOOL) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libftgl_la_CPPFLAGS) $(CPPFLAGS) $(libftgl_la_CXXFLAGS) $(CXXFLAGS) -c -o libftgl_la-FTBufferFont.lo `test -f 'FTFont/FTBufferFont.cpp' || echo '$(srcdir)/'`FTFont/FTBufferFont.cpp libftgl_la-FTExtrudeFont.lo: FTFont/FTExtrudeFont.cpp @am__fastdepCXX_TRUE@ $(LIBTOOL) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libftgl_la_CPPFLAGS) $(CPPFLAGS) $(libftgl_la_CXXFLAGS) $(CXXFLAGS) -MT libftgl_la-FTExtrudeFont.lo -MD -MP -MF $(DEPDIR)/libftgl_la-FTExtrudeFont.Tpo -c -o libftgl_la-FTExtrudeFont.lo `test -f 'FTFont/FTExtrudeFont.cpp' || echo '$(srcdir)/'`FTFont/FTExtrudeFont.cpp @am__fastdepCXX_TRUE@ mv -f $(DEPDIR)/libftgl_la-FTExtrudeFont.Tpo $(DEPDIR)/libftgl_la-FTExtrudeFont.Plo @AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='FTFont/FTExtrudeFont.cpp' object='libftgl_la-FTExtrudeFont.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCXX_FALSE@ $(LIBTOOL) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libftgl_la_CPPFLAGS) $(CPPFLAGS) $(libftgl_la_CXXFLAGS) $(CXXFLAGS) -c -o libftgl_la-FTExtrudeFont.lo `test -f 'FTFont/FTExtrudeFont.cpp' || echo '$(srcdir)/'`FTFont/FTExtrudeFont.cpp libftgl_la-FTOutlineFont.lo: FTFont/FTOutlineFont.cpp @am__fastdepCXX_TRUE@ $(LIBTOOL) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libftgl_la_CPPFLAGS) $(CPPFLAGS) $(libftgl_la_CXXFLAGS) $(CXXFLAGS) -MT libftgl_la-FTOutlineFont.lo -MD -MP -MF $(DEPDIR)/libftgl_la-FTOutlineFont.Tpo -c -o libftgl_la-FTOutlineFont.lo `test -f 'FTFont/FTOutlineFont.cpp' || echo '$(srcdir)/'`FTFont/FTOutlineFont.cpp @am__fastdepCXX_TRUE@ mv -f $(DEPDIR)/libftgl_la-FTOutlineFont.Tpo $(DEPDIR)/libftgl_la-FTOutlineFont.Plo @AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='FTFont/FTOutlineFont.cpp' object='libftgl_la-FTOutlineFont.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCXX_FALSE@ $(LIBTOOL) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libftgl_la_CPPFLAGS) $(CPPFLAGS) $(libftgl_la_CXXFLAGS) $(CXXFLAGS) -c -o libftgl_la-FTOutlineFont.lo `test -f 'FTFont/FTOutlineFont.cpp' || echo '$(srcdir)/'`FTFont/FTOutlineFont.cpp libftgl_la-FTPixmapFont.lo: FTFont/FTPixmapFont.cpp @am__fastdepCXX_TRUE@ $(LIBTOOL) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libftgl_la_CPPFLAGS) $(CPPFLAGS) $(libftgl_la_CXXFLAGS) $(CXXFLAGS) -MT libftgl_la-FTPixmapFont.lo -MD -MP -MF $(DEPDIR)/libftgl_la-FTPixmapFont.Tpo -c -o libftgl_la-FTPixmapFont.lo `test -f 'FTFont/FTPixmapFont.cpp' || echo '$(srcdir)/'`FTFont/FTPixmapFont.cpp @am__fastdepCXX_TRUE@ mv -f $(DEPDIR)/libftgl_la-FTPixmapFont.Tpo $(DEPDIR)/libftgl_la-FTPixmapFont.Plo @AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='FTFont/FTPixmapFont.cpp' object='libftgl_la-FTPixmapFont.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCXX_FALSE@ $(LIBTOOL) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libftgl_la_CPPFLAGS) $(CPPFLAGS) $(libftgl_la_CXXFLAGS) $(CXXFLAGS) -c -o libftgl_la-FTPixmapFont.lo `test -f 'FTFont/FTPixmapFont.cpp' || echo '$(srcdir)/'`FTFont/FTPixmapFont.cpp libftgl_la-FTPolygonFont.lo: FTFont/FTPolygonFont.cpp @am__fastdepCXX_TRUE@ $(LIBTOOL) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libftgl_la_CPPFLAGS) $(CPPFLAGS) $(libftgl_la_CXXFLAGS) $(CXXFLAGS) -MT libftgl_la-FTPolygonFont.lo -MD -MP -MF $(DEPDIR)/libftgl_la-FTPolygonFont.Tpo -c -o libftgl_la-FTPolygonFont.lo `test -f 'FTFont/FTPolygonFont.cpp' || echo '$(srcdir)/'`FTFont/FTPolygonFont.cpp @am__fastdepCXX_TRUE@ mv -f $(DEPDIR)/libftgl_la-FTPolygonFont.Tpo $(DEPDIR)/libftgl_la-FTPolygonFont.Plo @AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='FTFont/FTPolygonFont.cpp' object='libftgl_la-FTPolygonFont.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCXX_FALSE@ $(LIBTOOL) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libftgl_la_CPPFLAGS) $(CPPFLAGS) $(libftgl_la_CXXFLAGS) $(CXXFLAGS) -c -o libftgl_la-FTPolygonFont.lo `test -f 'FTFont/FTPolygonFont.cpp' || echo '$(srcdir)/'`FTFont/FTPolygonFont.cpp libftgl_la-FTTextureFont.lo: FTFont/FTTextureFont.cpp @am__fastdepCXX_TRUE@ $(LIBTOOL) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libftgl_la_CPPFLAGS) $(CPPFLAGS) $(libftgl_la_CXXFLAGS) $(CXXFLAGS) -MT libftgl_la-FTTextureFont.lo -MD -MP -MF $(DEPDIR)/libftgl_la-FTTextureFont.Tpo -c -o libftgl_la-FTTextureFont.lo `test -f 'FTFont/FTTextureFont.cpp' || echo '$(srcdir)/'`FTFont/FTTextureFont.cpp @am__fastdepCXX_TRUE@ mv -f $(DEPDIR)/libftgl_la-FTTextureFont.Tpo $(DEPDIR)/libftgl_la-FTTextureFont.Plo @AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='FTFont/FTTextureFont.cpp' object='libftgl_la-FTTextureFont.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCXX_FALSE@ $(LIBTOOL) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libftgl_la_CPPFLAGS) $(CPPFLAGS) $(libftgl_la_CXXFLAGS) $(CXXFLAGS) -c -o libftgl_la-FTTextureFont.lo `test -f 'FTFont/FTTextureFont.cpp' || echo '$(srcdir)/'`FTFont/FTTextureFont.cpp libftgl_la-FTLayout.lo: FTLayout/FTLayout.cpp @am__fastdepCXX_TRUE@ $(LIBTOOL) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libftgl_la_CPPFLAGS) $(CPPFLAGS) $(libftgl_la_CXXFLAGS) $(CXXFLAGS) -MT libftgl_la-FTLayout.lo -MD -MP -MF $(DEPDIR)/libftgl_la-FTLayout.Tpo -c -o libftgl_la-FTLayout.lo `test -f 'FTLayout/FTLayout.cpp' || echo '$(srcdir)/'`FTLayout/FTLayout.cpp @am__fastdepCXX_TRUE@ mv -f $(DEPDIR)/libftgl_la-FTLayout.Tpo $(DEPDIR)/libftgl_la-FTLayout.Plo @AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='FTLayout/FTLayout.cpp' object='libftgl_la-FTLayout.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCXX_FALSE@ $(LIBTOOL) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libftgl_la_CPPFLAGS) $(CPPFLAGS) $(libftgl_la_CXXFLAGS) $(CXXFLAGS) -c -o libftgl_la-FTLayout.lo `test -f 'FTLayout/FTLayout.cpp' || echo '$(srcdir)/'`FTLayout/FTLayout.cpp libftgl_la-FTLayoutGlue.lo: FTLayout/FTLayoutGlue.cpp @am__fastdepCXX_TRUE@ $(LIBTOOL) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libftgl_la_CPPFLAGS) $(CPPFLAGS) $(libftgl_la_CXXFLAGS) $(CXXFLAGS) -MT libftgl_la-FTLayoutGlue.lo -MD -MP -MF $(DEPDIR)/libftgl_la-FTLayoutGlue.Tpo -c -o libftgl_la-FTLayoutGlue.lo `test -f 'FTLayout/FTLayoutGlue.cpp' || echo '$(srcdir)/'`FTLayout/FTLayoutGlue.cpp @am__fastdepCXX_TRUE@ mv -f $(DEPDIR)/libftgl_la-FTLayoutGlue.Tpo $(DEPDIR)/libftgl_la-FTLayoutGlue.Plo @AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='FTLayout/FTLayoutGlue.cpp' object='libftgl_la-FTLayoutGlue.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCXX_FALSE@ $(LIBTOOL) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libftgl_la_CPPFLAGS) $(CPPFLAGS) $(libftgl_la_CXXFLAGS) $(CXXFLAGS) -c -o libftgl_la-FTLayoutGlue.lo `test -f 'FTLayout/FTLayoutGlue.cpp' || echo '$(srcdir)/'`FTLayout/FTLayoutGlue.cpp libftgl_la-FTSimpleLayout.lo: FTLayout/FTSimpleLayout.cpp @am__fastdepCXX_TRUE@ $(LIBTOOL) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libftgl_la_CPPFLAGS) $(CPPFLAGS) $(libftgl_la_CXXFLAGS) $(CXXFLAGS) -MT libftgl_la-FTSimpleLayout.lo -MD -MP -MF $(DEPDIR)/libftgl_la-FTSimpleLayout.Tpo -c -o libftgl_la-FTSimpleLayout.lo `test -f 'FTLayout/FTSimpleLayout.cpp' || echo '$(srcdir)/'`FTLayout/FTSimpleLayout.cpp @am__fastdepCXX_TRUE@ mv -f $(DEPDIR)/libftgl_la-FTSimpleLayout.Tpo $(DEPDIR)/libftgl_la-FTSimpleLayout.Plo @AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='FTLayout/FTSimpleLayout.cpp' object='libftgl_la-FTSimpleLayout.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCXX_FALSE@ $(LIBTOOL) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libftgl_la_CPPFLAGS) $(CPPFLAGS) $(libftgl_la_CXXFLAGS) $(CXXFLAGS) -c -o libftgl_la-FTSimpleLayout.lo `test -f 'FTLayout/FTSimpleLayout.cpp' || echo '$(srcdir)/'`FTLayout/FTSimpleLayout.cpp mostlyclean-libtool: -rm -f *.lo clean-libtool: -rm -rf .libs _libs install-ftglHEADERS: $(ftgl_HEADERS) @$(NORMAL_INSTALL) test -z "$(ftgldir)" || $(MKDIR_P) "$(DESTDIR)$(ftgldir)" @list='$(ftgl_HEADERS)'; for p in $$list; do \ if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ f=$(am__strip_dir) \ echo " $(ftglHEADERS_INSTALL) '$$d$$p' '$(DESTDIR)$(ftgldir)/$$f'"; \ $(ftglHEADERS_INSTALL) "$$d$$p" "$(DESTDIR)$(ftgldir)/$$f"; \ done uninstall-ftglHEADERS: @$(NORMAL_UNINSTALL) @list='$(ftgl_HEADERS)'; for p in $$list; do \ f=$(am__strip_dir) \ echo " rm -f '$(DESTDIR)$(ftgldir)/$$f'"; \ rm -f "$(DESTDIR)$(ftgldir)/$$f"; \ done ID: $(HEADERS) $(SOURCES) $(LISP) $(TAGS_FILES) list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ unique=`for i in $$list; do \ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ done | \ $(AWK) '{ files[$$0] = 1; nonemtpy = 1; } \ END { if (nonempty) { for (i in files) print i; }; }'`; \ mkid -fID $$unique tags: TAGS TAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \ $(TAGS_FILES) $(LISP) tags=; \ here=`pwd`; \ list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ unique=`for i in $$list; do \ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ done | \ $(AWK) '{ files[$$0] = 1; nonempty = 1; } \ END { if (nonempty) { for (i in files) print i; }; }'`; \ if test -z "$(ETAGS_ARGS)$$tags$$unique"; then :; else \ test -n "$$unique" || unique=$$empty_fix; \ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ $$tags $$unique; \ fi ctags: CTAGS CTAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \ $(TAGS_FILES) $(LISP) tags=; \ list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ unique=`for i in $$list; do \ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ done | \ $(AWK) '{ files[$$0] = 1; nonempty = 1; } \ END { if (nonempty) { for (i in files) print i; }; }'`; \ test -z "$(CTAGS_ARGS)$$tags$$unique" \ || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ $$tags $$unique GTAGS: here=`$(am__cd) $(top_builddir) && pwd` \ && cd $(top_srcdir) \ && gtags -i $(GTAGS_ARGS) $$here distclean-tags: -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags distdir: $(DISTFILES) @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ list='$(DISTFILES)'; \ dist_files=`for file in $$list; do echo $$file; done | \ sed -e "s|^$$srcdirstrip/||;t" \ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ case $$dist_files in \ */*) $(MKDIR_P) `echo "$$dist_files" | \ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ sort -u` ;; \ esac; \ for file in $$dist_files; do \ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ if test -d $$d/$$file; then \ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ cp -pR $(srcdir)/$$file $(distdir)$$dir || exit 1; \ fi; \ cp -pR $$d/$$file $(distdir)$$dir || exit 1; \ else \ test -f $(distdir)/$$file \ || cp -p $$d/$$file $(distdir)/$$file \ || exit 1; \ fi; \ done check-am: all-am check: check-am all-am: Makefile $(LTLIBRARIES) $(HEADERS) installdirs: for dir in "$(DESTDIR)$(libdir)" "$(DESTDIR)$(ftgldir)"; do \ test -z "$$dir" || $(MKDIR_P) "$$dir"; \ done install: install-am install-exec: install-exec-am install-data: install-data-am uninstall: uninstall-am install-am: all-am @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am installcheck: installcheck-am install-strip: $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ `test -z '$(STRIP)' || \ echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install mostlyclean-generic: clean-generic: distclean-generic: -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) maintainer-clean-generic: @echo "This command is intended for maintainers to use" @echo "it deletes files that may require special tools to rebuild." clean: clean-am clean-am: clean-generic clean-libLTLIBRARIES clean-libtool \ mostlyclean-am distclean: distclean-am -rm -rf ./$(DEPDIR) -rm -f Makefile distclean-am: clean-am distclean-compile distclean-generic \ distclean-tags dvi: dvi-am dvi-am: html: html-am info: info-am info-am: install-data-am: install-ftglHEADERS install-dvi: install-dvi-am install-exec-am: install-libLTLIBRARIES install-html: install-html-am install-info: install-info-am install-man: install-pdf: install-pdf-am install-ps: install-ps-am installcheck-am: maintainer-clean: maintainer-clean-am -rm -rf ./$(DEPDIR) -rm -f Makefile maintainer-clean-am: distclean-am maintainer-clean-generic mostlyclean: mostlyclean-am mostlyclean-am: mostlyclean-compile mostlyclean-generic \ mostlyclean-libtool pdf: pdf-am pdf-am: ps: ps-am ps-am: uninstall-am: uninstall-ftglHEADERS uninstall-libLTLIBRARIES .MAKE: install-am install-strip .PHONY: CTAGS GTAGS all all-am check check-am clean clean-generic \ clean-libLTLIBRARIES clean-libtool ctags distclean \ distclean-compile distclean-generic distclean-libtool \ distclean-tags distdir dvi dvi-am html html-am info info-am \ install install-am install-data install-data-am install-dvi \ install-dvi-am install-exec install-exec-am \ install-ftglHEADERS install-html install-html-am install-info \ install-info-am install-libLTLIBRARIES install-man install-pdf \ install-pdf-am install-ps install-ps-am install-strip \ installcheck installcheck-am installdirs maintainer-clean \ maintainer-clean-generic mostlyclean mostlyclean-compile \ mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \ tags uninstall uninstall-am uninstall-ftglHEADERS \ uninstall-libLTLIBRARIES # Tell versions [3.59,3.63) of GNU make to not export all variables. # Otherwise a system limit (for SysV at least) may be exceeded. .NOEXPORT: connectome-workbench-1.2.3+git41-gc4c6c90/src/FtglFont/NEWS000066400000000000000000000462531300200146000230000ustar00rootroot00000000000000 -*- coding: utf-8 -*- FTGL ==== Included below are release notes for all versions of FTGL that have been released to date. All versions prior to and including version 2.1.2 were exclusively developed by Henry Maddocks. Subsequent versions have changes attributed per contributor. ---------------------------------------------------------------------- --- 2008-06-12 Release 2.1.3~rc5 --- ---------------------------------------------------------------------- * Stable API. Public headers are now frozen. * Fixed several memory corruption and crash bugs - Sam Hocevar * Fixed several memory leaks - Sam Hocevar * Kerning and glyph performance enhancements - Sean Morrison * The library now also exports a pure C interface - Éric Beets * Inset/outset contour support for fonts - Éric Beets * Fix the FTLayout rendering - Éric Beets * Added new FTLayout and FTSimpleLayout support for layout managers - Sam Hocevar * Fixed the paths in the XCode project - Henry Maddocks * Changed the behaviour of some objects so that if there is an error their state isn't changed - Henry Maddocks * New, fast FTBufferFont texture fonts - Sam Hocevar * UTF-8 support - Daniel Remenak ---------------------------------------------------------------------- --- 2004-12-11 Release 2.1.2 --- ---------------------------------------------------------------------- * Changed the way the colour is specified for Pixmap fonts. It can now be done per string rather than at start up as previous. * Fixed a couple of compilation errors caused by the new FTPoint stuff, mostly... * More const correctness. It's like a virus! ---------------------------------------------------------------------- --- 2004-12-05 Release 2.1.1 --- ---------------------------------------------------------------------- * Added the xCode project properly this time. ---------------------------------------------------------------------- --- 2004-12-05 Release 2.1.0 --- ---------------------------------------------------------------------- * Added texture co-ordinates to the geometry based font types. * Added the ability to turn off (or on) glDisplayList creation inside FTGL. * Removed unnecessary translates in the glyph rendering code. * Moved the Mac project to XCode. * Added a line height function to FTFont. * Got rid of the GL_TEXTURE_2D_BINDING_EXT call and replaced it with a static member. * Fixed a bug where resizing FTGLTextureFont caused a GL error. * Refactored FTPoint quite a bit. * More unit tests. Fixed a heap of bugs. ---------------------------------------------------------------------- --- 2004-08-16 Release 2.0.11 --- ---------------------------------------------------------------------- * Updated FTFont( *pBufferBytes, bufferSizeInBytes) documentation. ---------------------------------------------------------------------- --- 2004-08-16 Release 2.0.10 --- ---------------------------------------------------------------------- * Fixed tab problem in unix Makefile. * Added CYGWIN GLUTesselatorFunction define to FTVectoriser. ---------------------------------------------------------------------- --- 2004-04-21 Release 2.0.9 --- ---------------------------------------------------------------------- * Fixed includes for pre 2.1.7 versions of freetype * Changed unix build to create FTGL subdir for includes ---------------------------------------------------------------------- --- 2004-04-09 Release 2.0.8 --- ---------------------------------------------------------------------- * Fixes for deprecated identifiers in 2.1.5 * Changed the internals to use FTGlyphSlot instead of FTGlyph * Added a unit test for FTBitmapGlyph, FTCharToGlyphIndexMap. * Fixed a memory leak in FTGlyphContainer. * Added the ability to get the list of charmaps in the font. * Changed FTGLTextureFont to use FTVector for texture id list. ---------------------------------------------------------------------- --- 2003-08-31 Release 2.07 --- ---------------------------------------------------------------------- * Minor fix for unix build scripts. * Minor fix for unit tests. ---------------------------------------------------------------------- --- 2003-08-25 Release 2.06 --- ---------------------------------------------------------------------- * Updated the unix build scripts. ---------------------------------------------------------------------- --- 2003-08-25 Release 2.05 --- ---------------------------------------------------------------------- * Refactored FTGlyphContainer & FTCharmap. They now store FTGlyphs sequentially rather than by glyph index. This should save a heap of memory and a bit of time at startup. * Changed the Mac font paths in the demos. * Changed the unit tests for new hinter in Freetype 2.1.4. * Added a test for broken contour tags. ---------------------------------------------------------------------- --- 2003-04-12 Release 2.04 --- ---------------------------------------------------------------------- * Fixed resize behavior in FTGLTextureFont. ---------------------------------------------------------------------- --- 2003-04-09 Release 2.03 --- ---------------------------------------------------------------------- * Fix in FTContour to handle broken contours. ---------------------------------------------------------------------- --- 2003-04-03 Release 2.02 --- ---------------------------------------------------------------------- * Fixed memory leaks ---------------------------------------------------------------------- --- 2003-03-14 Release 2.01 --- ---------------------------------------------------------------------- * Minor changes to autoconf to detect glu ---------------------------------------------------------------------- --- 2003-03-11 Release 2.0 --- ---------------------------------------------------------------------- * Fixed some alignment bugs caused by changes to Freetype ( > 2.0.9). * Minor fixes to float declarations. * Moved FTBBox and FTPoint to their own files and added Move() and operator += to FTBBox * Replaced FT_Vector with FTPoint for kerning. * Fixed the glPushAttrib calls. * Changed gluTess callback def. * Rewriting FTGLDemo. * Minor fixes for irix. * Removed a bunch of redundant members and made them function locals. * Removed the Open() & Close() functions from FTFont because there was no way to handle Close correctly which makes Open redundant. * Removed Open() from FTface. * Improved the robustness of some of the error handling. * Removed the FTCharmap Platform/Encoding function. * Added unit tests. * Removed the precache flag. * Unvirtualised functions in FTLibrary and FTGlyphContainer. * Fixed empty string bug in FTFont::BBox. * Refactored FTContour and moved it to it's own file. * Removed unnecessary memory allocations in vector Glyphs. They now access the vector data directly. * Made vectoriser a local variable in vector glyphs. * Fixed a long standing visual bug in FTVectoriser. * Changed size calculations to use floats instead of ints. This includes FTBBox. * Refactored FTGlyph. Now calculates advance (as a float) and bbox. * Changed contourList from FTVector to an array in FTVectoriser. * Made function and member names more consistant. * Moved header files to include directory. * Mesh now uses a list for glCombine points. * Delete the display lists. * Unix AutoConf support. * Attach 'files' from memory. ---------------------------------------------------------------------- --- 2002-10-23 Release 1.4 --- ---------------------------------------------------------------------- * FTGL now requires 2.0.9 or later. See below for reason. * Merged 1.32 branch with main tree * Glyph loading has been optimised for pixel based glyphs. * Removed mmgr * Added FTFont::Attach * Updated API docs * Removed stl map and vector. Replaced by code supplied by Sebastien Barre * Removed work around for Type1 height and width bug in freetype. It seems to be fixed in 2.0.9 * Added a test target to the Mac OSX project * Inline some private functions. ---------------------------------------------------------------------- --- 2002-04-23 Release 1.32 --- ---------------------------------------------------------------------- * Fixed enable state attribute in FTGLBitmapFont * Wrapped tb.h & trackball.h in EXTERN "C" * Renamed FTGLDemo to .cpp Ellers... * New MSVC projects updated to v1.3 * Removed a lot of unnecessary Windows stuff from ftgl.h * Added functions to load font from memory. * Fixed a couple of Windows 'for' scope problems in FTExtrdGlyph * FTGLDemo. Added #define for windows font ---------------------------------------------------------------------- --- 2002-01-30 Release 1.31 --- ---------------------------------------------------------------------- * Forgot to update readme etc for 1.3 ---------------------------------------------------------------------- --- 2002-01-27 Release 1.3b5 --- ---------------------------------------------------------------------- * FTBbox now uses float rather then int * Fixed some more warnings (size_t) * Removed the contour winding function because it didn't fix the problem!! * Fixed up some state settings in fonts. ---------------------------------------------------------------------- --- 2001-12-11 Release 1.3b4 --- ---------------------------------------------------------------------- * Added MAC OSX project (Project Builder) * Added a function for extruded glyphs that calculates the winding order of the glyph contour. * Added FTGL_DEBUG to include memory debugger. * Added a couple of typedefs to FTGL.h, mainly to aid debugging * Cleaned up the includes. ---------------------------------------------------------------------- --- 2001-11-13 Release 1.3b3 --- ---------------------------------------------------------------------- * Texture fonts now behave the same as the others and can be loaded on demand. This made FTGLTextureFont MUCH simpler!!!! It has also improved the grid fitting so less texture mem is needed. * Refactored FTVectoriser... This now builds contours and meshes internally and then passes the raw point data onto the glyphs. The gluTess data is captured in an internal non static data structure fixing a memory Leak in PolyGlyph (glCombine). This has enabled... * Extruded fonts. FTGLExtrdFont & FTExtrdGlyph. * Reversed the winding for polyglyphs, extruded glyphs and texture glyphs to make them CCW * Bounding box function * Fixed the != and == operators in ftPoint * Un-virtualised some functions in FTFont * Added a demo app to dist. ---------------------------------------------------------------------- --- 2001-11-09 Release 1.21 --- ---------------------------------------------------------------------- * Visual Studio projects updated for .cpp source file extensions. * A couple of windows 'cast' warnings have been fixed. ---------------------------------------------------------------------- --- 2001-11-06 Release 1.2 --- ---------------------------------------------------------------------- * Glyphs can now be loaded on the fly instead of being pre-cached. If FTFont::Open() is called with false, FTGlyphContainer will build a list of null pointers. Then when ever a glyph needs to be access eg by FTFont::advance or FTFont::render, it will be built and slotted into the glyphlist in the correct position. * Removed glext.h from FTGL.h and replaced it with a test for GL_EXT_texture_object. * Added padding to texture size calculations. * Fixed a NASTY bug in FTGLTextureFont. Only came to light after changes to the glyph preprocessing. ---------------------------------------------------------------------- --- 2001-10-31 Release 1.1 --- ---------------------------------------------------------------------- * Renamed the source to .cpp * Removed the static activeTextureID from FTTextureGlyph and replaced it with a call to glGetIntegerv( GL_TEXTURE_2D_BINDING_EXT, &activeTextureID); * Added an include for glext.h in FTGL.h * Tidied up the glbegin/glEnd pairs in FTTextureGlyph & FTGLTextureFont * Fixed the problem with doc filenames. * Tidied up some implicit type conversions. * Fixed FTCharMap to ensure that a valid default charmap is always created by the c_stor. ---------------------------------------------------------------------- --- 2001-10-26 Release 1.01 --- ---------------------------------------------------------------------- * Removed the glEnable( GL_TEXTURE_2D) from FTGLTextureFont * Removed the redundant tempGlyph members in the FTGLXXXXFont classes * Made a change in FTGL.h to include correct headers for MAC OSX * FTGL.h now includes glu.h * Minor fixes to get rid of Project Builder warnings (MAC OSX) * Fixed some of the docs ---------------------------------------------------------------------- --- 2001-10-24 Release 1.0 --- ---------------------------------------------------------------------- * Version 1.0 release ---------------------------------------------------------------------- --- 2001-09-29 Release 1.0b7 --- ---------------------------------------------------------------------- * Tesselation winding rules * Fixed bug in FTContour Add point function * Cleaned up disposal of FTCharmap in FTFace * renamed FTVectorGlyph to FTOutlineGlyph * New distribution structure * Minor changes for windows (VC 6) * Windows and Linux ports. ---------------------------------------------------------------------- --- 2001-09-20 Release 1.0b6 --- ---------------------------------------------------------------------- * Implemented the new FTCharmap class. The performance improvement is dramatic. * Tidied up the way the freetype FT_Face object is disposed of by FTFont and FTFace. This was a potential crash. * FTVectorGlyph and FTPolyGlyph now disposes of the freetype glyph correctly after initialsation. This was a potential crash. * Preliminary support for unicode...wchar_t Tested with non european fonts. * Added function to calc the advance width of a string. * Minor tidy ups. ---------------------------------------------------------------------- --- 2001-08-29 Release 1.0b5 --- ---------------------------------------------------------------------- * Settled on integers for FTSize stuff. NOTE the FTGlyph stuff is still up in the air. * Fixed the positional stuff. * Added Java Doc comments. NOT COMPLETE * Fixes for linux, mainly to clear warnings. * changed the return type for FTFace::Glyph() from a reference to a pointer so it can return NULL on failure. * Related to above...better error handling and reporting in FTGLXXXFont::MakeGlyphList() * Fixed a bug in FTVectoriser that was ignoring non printing characters. This meant that the pen wasn't advanced for spaces etc. It affected polygon and outline font rendering. * Minor tidy ups. ---------------------------------------------------------------------- --- 2001-08-21 Release 1.0b4 --- ---------------------------------------------------------------------- * Changed the mode for FT_Load_Glyph to FT_LOAD_NO_HINTING | FT_LOAD_NO_BITMAP for outline and polygon fonts & FT_LOAD_NO_HINTING for texture fonts. Seems to produce better looking glyphs. * FTGLTextureFont can now use multiple textures to render glyphs if they don't fit within one GL_MAX_TEXTURE_SIZE texture. * Changed FTSize to use bbox for global width and height. Needs more work (eg float or int?) and need to check inconsistancies in freetype. * Being more strict with types eg integer indices and sizes are now unsigned. ---------------------------------------------------------------------- --- 2001-08-08 Release 1.0b3 --- ---------------------------------------------------------------------- * I've made fundamental change to the way the glyphlist is built. This is to get round the problems I was having with charmaps. At this stage it is a temporary solution. Previously the glyphList was indexed by char code. Now it's indexed by glyph index and the conversion is done by the freetype function FT_Get_Char_Index(). If this proves to be too slow I'll make my own charmap and use it to index into the glyphlist. This has fixed all the charmap related problems/bugs. * Enabled alpha blend in Pixmap font. * Enabled LINE_SMOOTH in Outline font * Fixed bug that prevented the display of chars >127 * Moved pixel store stuff out of BitmapGlyph into BitmapFont. * Minor changes for IRIX (compiles but isn't tested) * Pixmap fonts can now be in colour. It uses the current colour when the font is CREATED. This isn't ideal but is better than the alternatives. * Tidied up the error handling. * Minor code clean ups. ---------------------------------------------------------------------- --- 2001-08-06 BETA Release 1.0b2 --- ---------------------------------------------------------------------- * Minor tidy ups for first public release. ---------------------------------------------------------------------- --- 2001-08-03 First BETA Release 1.0b1 --- ---------------------------------------------------------------------- * All font types are now working, Bitmaps, Pixmaps, Texture, Outline and Polygons. Quality of output and performance varies wildly. :) ---------------------------------------------------------------------- --- 2001-07-22 First ALPHA Release 1.0a1 --- ---------------------------------------------------------------------- * And so it begins. connectome-workbench-1.2.3+git41-gc4c6c90/src/FtglFont/README000066400000000000000000000016551300200146000231560ustar00rootroot00000000000000FTGL 2.1 5 December 2004 DESCRIPTION: FTGL is a free open source library to enable developers to use arbitrary fonts in their OpenGL (www.opengl.org) applications. Unlike other OpenGL font libraries FTGL uses standard font file formats so doesn't need a preprocessing step to convert the high quality font data into a lesser quality, proprietary format. FTGL uses the Freetype (www.freetype.org) font library to open and 'decode' the fonts. It then takes that output and stores it in a format most efficient for OpenGL rendering. Rendering modes supported are: - Bit maps - Antialiased Pix maps - Outlines - Polygon meshes - Extruded polygon meshes - Texture maps - Buffer maps USAGE: FTGLPixmapFont font("Arial.ttf"); font.FaceSize(72); font.Render("Hello World!"); CONTACT: Please contact us if you have any suggestions, feature requests, or problems. Sam Hocevar Christopher Sean Morrison connectome-workbench-1.2.3+git41-gc4c6c90/src/FtglFont/README_WORKBENCH.txt000066400000000000000000000015701300200146000253720ustar00rootroot00000000000000 How FtglFont was created. * Download and uncompress an FTGL library. * Copy the source only: cp -r /src ./FtglFont * cd FtglFont * Create the CMakeLists.txt file. ** find . -name '*.h' -print ** find . -name '*.cpp' -print ** Add .h and .cpp files to ADD_LIBRARY ** Add all subdirectories to INCLUDE_DIRECTORIES ** Need 'ft2build.h' from FreeType it is in /Developer/SDKs/MacOSX10.7.sdk/usr/X11/include on mac ** Change the include for "config.h" to "FtglConfig.h" so that it does not conflict with other "config.h" files ** Create the FtglConfig.h file by "cp /config.h.in ./FtglConfig.h" ** Add FtglConfig.h to CMakeLists.txt ** Edit FtglConfig.h and define these items: *** HAVE_INTTYPES_H *** HAVE_MEMORY_H *** HAVE_STDINT_H *** HAVE_STDLIB_H *** HAVE_STRINGS_H *** HAVE_STRING_H *** HAVE_STRNDUP *** HAVE_SYS_TYPES_H *** STDC_HEADERS *** X_DISPLAY_MISSING connectome-workbench-1.2.3+git41-gc4c6c90/src/Gifti/000077500000000000000000000000001300200146000216065ustar00rootroot00000000000000connectome-workbench-1.2.3+git41-gc4c6c90/src/Gifti/CMakeLists.txt000066400000000000000000000014611300200146000243500ustar00rootroot00000000000000 # # Name of Project # PROJECT (Gifti) # # Need XML from Qt # SET(QT_DONT_USE_QTGUI) # # Add QT for includes # INCLUDE (${QT_USE_FILE}) # # Create GIFTI Library # ADD_LIBRARY(Gifti GiftiArrayIndexingOrderEnum.h GiftiDataArray.h GiftiEncodingEnum.h GiftiEndianEnum.h GiftiFile.h GiftiFileSaxReader.h GiftiFileWriter.h GiftiLabelTableSaxReader.h GiftiMetaDataSaxReader.h GiftiArrayIndexingOrderEnum.cxx GiftiDataArray.cxx GiftiEncodingEnum.cxx GiftiEndianEnum.cxx GiftiFile.cxx GiftiFileSaxReader.cxx GiftiFileWriter.cxx GiftiLabelTableSaxReader.cxx GiftiMetaDataSaxReader.cxx ) # # Find Headers # INCLUDE_DIRECTORIES( ${CMAKE_SOURCE_DIR}/Annotations ${CMAKE_SOURCE_DIR}/Palette ${CMAKE_SOURCE_DIR}/FilesBase ${CMAKE_SOURCE_DIR}/Gifti ${CMAKE_SOURCE_DIR}/Nifti ${CMAKE_SOURCE_DIR}/Xml ${CMAKE_SOURCE_DIR}/Common ) connectome-workbench-1.2.3+git41-gc4c6c90/src/Gifti/GiftiArrayIndexingOrderEnum.cxx000066400000000000000000000117031300200146000277040ustar00rootroot00000000000000/*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 __GIFTIARRAYINDEXINGORDER_DECLARE__ #include "GiftiArrayIndexingOrderEnum.h" #undef __GIFTIARRAYINDEXINGORDER_DECLARE__ #include "CaretAssert.h" using namespace caret; /** * Constructor. * * @param e * An enumerated value. * @param name * Name of enumberated value. */ GiftiArrayIndexingOrderEnum::GiftiArrayIndexingOrderEnum( const Enum e, const AString& name, const AString& giftiName) { this->e = e; this->name = name; this->giftiName = giftiName; } /** * Destructor. */ GiftiArrayIndexingOrderEnum::~GiftiArrayIndexingOrderEnum() { } void GiftiArrayIndexingOrderEnum::initialize() { if (initializedFlag) { return; } initializedFlag = true; enumData.push_back(GiftiArrayIndexingOrderEnum(COLUMN_MAJOR_ORDER, "COLUMN_MAJOR_ORDER", "ColumnMajorOrder")); enumData.push_back(GiftiArrayIndexingOrderEnum(ROW_MAJOR_ORDER, "ROW_MAJOR_ORDER", "RowMajorOrder")); } /** * Find the data for and enumerated value. * @param e * The enumerated value. * @return Pointer to data for this enumerated type * or NULL if no data for type or if type is invalid. */ const GiftiArrayIndexingOrderEnum* GiftiArrayIndexingOrderEnum::findData(const Enum e) { initialize(); size_t num = enumData.size(); for (size_t i = 0; i < num; i++) { const GiftiArrayIndexingOrderEnum* d = &enumData[i]; if (d->e == e) { return d; } } return NULL; } /** * Get a string representation of the enumerated type. * @param e * Enumerated value. * @return * String representing enumerated value. */ AString GiftiArrayIndexingOrderEnum::toName(Enum e) { initialize(); const GiftiArrayIndexingOrderEnum* gaio = findData(e); return gaio->name; } /** * Get an enumerated value corresponding to its 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. */ GiftiArrayIndexingOrderEnum::Enum GiftiArrayIndexingOrderEnum::fromName(const AString& s, bool* isValidOut) { initialize(); bool validFlag = false; Enum e = ROW_MAJOR_ORDER; for (std::vector::iterator iter = enumData.begin(); iter != enumData.end(); iter++) { const GiftiArrayIndexingOrderEnum& d = *iter; if (d.name == s) { e = d.e; validFlag = true; break; } } if (isValidOut != 0) { *isValidOut = validFlag; } else if (validFlag == false) { CaretAssertMessage(0, AString("name \"" + s + " \"failed to match enumerated value for type GiftiArrayIndexingOrderEnum")); } return e; } /** * Get a string representation of the enumerated type. * @param e * Enumerated value. * @return * String representing enumerated value. */ AString GiftiArrayIndexingOrderEnum::toGiftiName(Enum e) { initialize(); const GiftiArrayIndexingOrderEnum* gaio = findData(e); return gaio->giftiName; } /** * Get an enumerated value corresponding to its 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. */ GiftiArrayIndexingOrderEnum::Enum GiftiArrayIndexingOrderEnum::fromGiftiName(const AString& s, bool* isValidOut) { initialize(); bool validFlag = false; Enum e = ROW_MAJOR_ORDER; for (std::vector::iterator iter = enumData.begin(); iter != enumData.end(); iter++) { const GiftiArrayIndexingOrderEnum& d = *iter; if (d.giftiName == s) { e = d.e; validFlag = true; break; } } if (isValidOut != 0) { *isValidOut = validFlag; } else if (validFlag == false) { CaretAssertMessage(0, AString("giftiName \"" + s + " \"failed to match enumerated value for type GiftiArrayIndexingOrderEnum")); } return e; } connectome-workbench-1.2.3+git41-gc4c6c90/src/Gifti/GiftiArrayIndexingOrderEnum.h000066400000000000000000000044601300200146000273330ustar00rootroot00000000000000#ifndef __GIFTIARRAYINDEXINGORDER_H__ #define __GIFTIARRAYINDEXINGORDER_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 "GiftiException.h" #include #include #include namespace caret { /** * Type for GIFTI Data Array ArrayIndexingOrder Attribute. */ class GiftiArrayIndexingOrderEnum { public: /** Type for GIFTI Data Array ArrayIndexingOrder Attribute. */ enum Enum { /** Column-Major Order (Fortran/Matlab) */ COLUMN_MAJOR_ORDER, /** Row-Major Order (C/C++/Java) */ ROW_MAJOR_ORDER }; ~GiftiArrayIndexingOrderEnum(); static AString toName(Enum e); static Enum fromName(const AString& s, bool* isValidOut); static AString toGiftiName(Enum e); static Enum fromGiftiName(const AString& s, bool* isValidOut); private: GiftiArrayIndexingOrderEnum(const Enum e, const AString& name, const AString& giftiName); static const GiftiArrayIndexingOrderEnum* findData(const Enum e); static std::vector enumData; static void initialize(); static bool initializedFlag; Enum e; AString name; AString giftiName; }; #ifdef __GIFTIARRAYINDEXINGORDER_DECLARE__ std::vector GiftiArrayIndexingOrderEnum::enumData; bool GiftiArrayIndexingOrderEnum::initializedFlag = false; #endif // __GIFTIARRAYINDEXINGORDER_DECLARE__ } // namespace #endif // __GIFTIARRAYINDEXINGORDER_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Gifti/GiftiDataArray.cxx000066400000000000000000001724171300200146000252010ustar00rootroot00000000000000/*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 "Base64.h" #include "ByteOrderEnum.h" #include "ByteSwapping.h" #include "CaretAssert.h" #include "CaretLogger.h" #include "DataCompressZLib.h" //#include "FileUtilities.h" #include "FastStatistics.h" #include "GiftiDataArray.h" #include "GiftiFile.h" #include "GiftiMetaDataXmlElements.h" #include "GiftiXmlElements.h" #include "Histogram.h" #include "NiftiEnums.h" #include "PaletteColorMapping.h" #include "SystemUtilities.h" #include "XmlWriter.h" using namespace caret; /** * constructor. */ GiftiDataArray::GiftiDataArray(const NiftiIntentEnum::Enum intentIn, const NiftiDataTypeEnum::Enum dataTypeIn, const std::vector& dimensionsIn, const GiftiEncodingEnum::Enum encodingIn) { intent = intentIn; dataTypeSize = 0; dataPointerFloat = NULL; dataPointerInt = NULL; dataPointerUByte = NULL; this->paletteColorMapping = NULL; this->descriptiveStatistics = NULL; this->descriptiveStatisticsLimitedValues = NULL; clear(); dataType = dataTypeIn; setDimensions(dimensionsIn); encoding = encodingIn; endian = getSystemEndian(); arraySubscriptingOrder = GiftiArrayIndexingOrderEnum::ROW_MAJOR_ORDER; externalFileName = ""; externalFileOffset = 0; if (intent == NiftiIntentEnum::NIFTI_INTENT_POINTSET) { Matrix4x4 gm; matrices.push_back(gm); } } /** * constructor. */ GiftiDataArray::GiftiDataArray(const NiftiIntentEnum::Enum intentIn) { intent = intentIn; dataTypeSize = 0; dataPointerFloat = NULL; dataPointerInt = NULL; dataPointerUByte = NULL; this->paletteColorMapping = NULL; this->descriptiveStatistics = NULL; this->descriptiveStatisticsLimitedValues = NULL; clear(); dimensions.clear(); encoding = GiftiEncodingEnum::ASCII; endian = getSystemEndian(); arraySubscriptingOrder = GiftiArrayIndexingOrderEnum::ROW_MAJOR_ORDER; externalFileName = ""; externalFileOffset = 0; /*if (intent == NiftiIntentEnum::NIFTI_INTENT_POINTSET) { Matrix4x4 gm; matrices.push_back(gm); }//*///TSC: do not add a fake matrix with no data or transformed space BEFORE knowing if one already exists, instead add one in validate, after reading, if none exists dataType = NiftiDataTypeEnum::NIFTI_TYPE_FLOAT32; getDataTypeAppropriateForIntent(intent, dataType); } /** * destructor. */ GiftiDataArray::~GiftiDataArray() { clear(); } /** * copy constructor. */ GiftiDataArray::GiftiDataArray(const GiftiDataArray& nda) : CaretObject(), TracksModificationInterface() { dataTypeSize = 0; dataPointerFloat = NULL; dataPointerInt = NULL; dataPointerUByte = NULL; this->paletteColorMapping = NULL; this->descriptiveStatistics = NULL; this->descriptiveStatisticsLimitedValues = NULL; copyHelperGiftiDataArray(nda); } /** * assignment operator. */ GiftiDataArray& GiftiDataArray::operator=(const GiftiDataArray& nda) { if (this != &nda) { copyHelperGiftiDataArray(nda); } return *this; } /** * the copy helper (used by copy constructor and assignment operator). */ void GiftiDataArray::copyHelperGiftiDataArray(const GiftiDataArray& nda) { this->paletteColorMapping = NULL; if (nda.paletteColorMapping != NULL) { this->paletteColorMapping = new PaletteColorMapping(*nda.paletteColorMapping); } if (this->descriptiveStatistics != NULL) { delete this->descriptiveStatistics; this->descriptiveStatistics = NULL; } if (this->descriptiveStatisticsLimitedValues != NULL) { delete this->descriptiveStatisticsLimitedValues; this->descriptiveStatisticsLimitedValues = NULL; } intent = nda.intent; encoding = nda.encoding; arraySubscriptingOrder = nda.arraySubscriptingOrder; dataType = nda.dataType; //dataLocation = nda.dataLocation; dataTypeSize = nda.dataTypeSize; endian = nda.endian; dimensions = nda.dimensions; allocateData(); data = nda.data; metaData = nda.metaData; nonWrittenMetaData = nda.nonWrittenMetaData; externalFileName = nda.externalFileName; externalFileOffset = nda.externalFileOffset; minMaxFloatValuesValid = nda.minMaxFloatValuesValid; minValueFloat = nda.minValueFloat; maxValueFloat = nda.maxValueFloat; minMaxFloatValuesValid = nda.minMaxFloatValuesValid; minValueInt = nda.minValueInt; maxValueInt = nda.maxValueInt; minMaxIntValuesValid = nda.minMaxIntValuesValid; minMaxPercentageValuesValid = nda.minMaxPercentageValuesValid; negMaxPct = nda.negMaxPct; negMinPct = nda.negMinPct; posMinPct = nda.posMinPct; posMaxPct = nda.posMaxPct; negMaxPctValue = nda.negMaxPctValue; negMinPctValue = nda.negMinPctValue; posMinPctValue = nda.posMinPctValue; posMaxPctValue = nda.posMaxPctValue; matrices = nda.matrices; setModified(); } /** * get the data type appropriate for the intent (returns true if intent is valid). */ bool GiftiDataArray::getDataTypeAppropriateForIntent(const NiftiIntentEnum::Enum intent, NiftiDataTypeEnum::Enum& dataTypeOut) { // // Default to float // dataTypeOut = NiftiDataTypeEnum::NIFTI_TYPE_FLOAT32; if (intent == NiftiIntentEnum::NIFTI_INTENT_POINTSET) { dataTypeOut = NiftiDataTypeEnum::NIFTI_TYPE_FLOAT32; } else if (intent == NiftiIntentEnum::NIFTI_INTENT_TIME_SERIES) { dataTypeOut = NiftiDataTypeEnum::NIFTI_TYPE_FLOAT32; } else if (intent == NiftiIntentEnum::NIFTI_INTENT_NORMAL) { dataTypeOut = NiftiDataTypeEnum::NIFTI_TYPE_FLOAT32; } else if (intent == NiftiIntentEnum::NIFTI_INTENT_LABEL) { dataTypeOut = NiftiDataTypeEnum::NIFTI_TYPE_INT32; } else if ((intent == NiftiIntentEnum::NIFTI_INTENT_RGB_VECTOR) || (intent == NiftiIntentEnum::NIFTI_INTENT_RGBA_VECTOR)) { dataTypeOut = NiftiDataTypeEnum::NIFTI_TYPE_UINT8; } else if (intent == NiftiIntentEnum::NIFTI_INTENT_SHAPE) { dataTypeOut = NiftiDataTypeEnum::NIFTI_TYPE_FLOAT32; } else if (intent == NiftiIntentEnum::NIFTI_INTENT_SYMMATRIX) { dataTypeOut = NiftiDataTypeEnum::NIFTI_TYPE_FLOAT32; } else if (intent == NiftiIntentEnum::NIFTI_INTENT_TRIANGLE) { dataTypeOut = NiftiDataTypeEnum::NIFTI_TYPE_INT32; } // else { // CaretLogFine("Unrecogized NIFTI intent \"" // + NiftiIntentEnum::toName(intent) // + "\" in GiftiDataArray::getDataTypeAppropriateForIntent()."); // return false; // } return true; } /** * add nodes. */ void GiftiDataArray::addRows(const int32_t numRowsToAdd) { dimensions[0] += numRowsToAdd; allocateData(); } /** * delete rows. */ void GiftiDataArray::deleteRows(const std::vector& rowsToDeleteIn) { if (rowsToDeleteIn.empty()) { return; } // // Sort rows in reverse order // std::vector rowsToDelete = rowsToDeleteIn; std::sort(rowsToDelete.begin(), rowsToDelete.end()); std::unique(rowsToDelete.begin(), rowsToDelete.end()); std::reverse(rowsToDelete.begin(), rowsToDelete.end()); // // size of row in bytes // int64_t numBytesInRow = 1; for (uint32_t i = 1; i < dimensions.size(); i++) { numBytesInRow = dimensions[i]; } numBytesInRow *= dataTypeSize; // // Remove the unneeded rows // for (uint32_t i = 0; i < rowsToDelete.size(); i++) { const int32_t offset = rowsToDelete[i] * numBytesInRow; data.erase(data.begin() + offset, data.begin() + offset + numBytesInRow); } // // Update the dimensions // dimensions[0] -= rowsToDelete.size(); setModified(); } /** * set number of nodes which causes reallocation of data. */ void GiftiDataArray::setDimensions(const std::vector dimensionsIn) { dimensions = dimensionsIn; if (dimensions.size() == 1) { dimensions.push_back(1); } else if (dimensions.empty()) { dimensions.push_back(0); dimensions.push_back(0); } allocateData(); } /** * allocate data for this array. */ void GiftiDataArray::allocateData() { // // Determine the number of items to allocate // int64_t dataSizeInBytes = 1; for (uint32_t i = 0; i < dimensions.size(); i++) { dataSizeInBytes *= dimensions[i]; } // // Bytes required by each data type // dataTypeSize = 0; switch (dataType) { case NiftiDataTypeEnum::NIFTI_TYPE_FLOAT32: dataTypeSize = sizeof(float); break; case NiftiDataTypeEnum::NIFTI_TYPE_INT32: dataTypeSize = sizeof(int32_t); break; case NiftiDataTypeEnum::NIFTI_TYPE_UINT8: dataTypeSize = sizeof(uint8_t); break; default: CaretAssertMessage(0, "Unsupported GIFTI data type."); break; } dataSizeInBytes *= dataTypeSize; // // Does data need to be allocated // if (dataSizeInBytes > 0) { // // Allocate the needed memory // data.resize(dataSizeInBytes); } else { data.clear(); } // // Update the data pointers // updateDataPointers(); setModified(); } /** * update the data pointers. */ void GiftiDataArray::updateDataPointers() { dataPointerFloat = NULL; dataPointerInt = NULL; dataPointerUByte = NULL; if (data.empty() == false) { switch (dataType) { case NiftiDataTypeEnum::NIFTI_TYPE_FLOAT32: dataPointerFloat = (float*)&data[0]; break; case NiftiDataTypeEnum::NIFTI_TYPE_INT32: dataPointerInt = (int32_t*)&data[0]; break; case NiftiDataTypeEnum::NIFTI_TYPE_UINT8: dataPointerUByte = (uint8_t*)&data[0]; break; default: CaretAssertMessage(0, "Unsupported GIFTI Data Type"); break; } } } /** * reset column. */ void GiftiDataArray::clear() { arraySubscriptingOrder = GiftiArrayIndexingOrderEnum::ROW_MAJOR_ORDER; encoding = GiftiEncodingEnum::ASCII; dataType = NiftiDataTypeEnum::NIFTI_TYPE_FLOAT32; endian = getSystemEndian(); dataTypeSize = sizeof(float); metaData.clear(); nonWrittenMetaData.clear(); dimensions.clear(); setDimensions(dimensions); externalFileName = ""; externalFileOffset = 0; minMaxFloatValuesValid = false; minMaxPercentageValuesValid = false; if (this->paletteColorMapping != NULL) { delete this->paletteColorMapping; this->paletteColorMapping = NULL; } if (this->descriptiveStatistics != NULL) { delete this->descriptiveStatistics; this->descriptiveStatistics = NULL; } if (this->descriptiveStatisticsLimitedValues != NULL) { delete this->descriptiveStatisticsLimitedValues; this->descriptiveStatisticsLimitedValues = NULL; } // do not clear // parentGiftiDataFile; // arrayType; // setDimensions will call allocateData() which will set // dataPointer // dataPointerFloat // dataPointerInt // dataPointerUByte; } /** * get the number of nodes (1st dimension). */ int64_t GiftiDataArray::getNumberOfRows() const { if (dimensions.empty() == false) { return dimensions[0]; } return 0; } /** * get the total number of elements. */ int64_t GiftiDataArray::getTotalNumberOfElements() const { if (dimensions.empty()) { return 0; } int64_t num = 1; for (uint32_t i = 0; i < dimensions.size(); i++) { num *= dimensions[i]; } return num; } /** * get number of components per node (2nd dimension). */ int64_t GiftiDataArray::getNumberOfComponents() const { if (dimensions.size() > 1) { return dimensions[1]; } else if (dimensions.size() == 1) { return 1; } return 0; } /** * get data offset. */ /*int64_t GiftiDataArray::getDataOffset(const int64_t nodeNum, const int64_t componentNum) const { const int64_t off = nodeNum * dimensions[1] + componentNum;//TSC: this is WRONG! (assumes 2 dimensions, assumes a particular index order) fix it before uncommenting return off; }//*/ /** * get the system's endian. */ GiftiEndianEnum::Enum GiftiDataArray::getSystemEndian() { GiftiEndianEnum::Enum endian = GiftiEndianEnum::ENDIAN_BIG; if (ByteOrderEnum::isSystemLittleEndian()) { endian = GiftiEndianEnum::ENDIAN_LITTLE; } //if (QSysInfo::ByteOrder == QSysInfo::BigEndian) { // return ENDIAN_BIG; //} return endian; } /** * get external file information. */ void GiftiDataArray::getExternalFileInformation(AString& nameOut, int64_t & offsetOut) const { nameOut = externalFileName; offsetOut = externalFileOffset; } /** * set external file information. */ void GiftiDataArray::setExternalFileInformation(const AString& nameIn, const int64_t offsetIn) { externalFileName = nameIn; externalFileOffset = offsetIn; } /** * remap integer values that are indices to a table. * void GiftiDataArray::remapIntValues(const std::vector& remappingTable) { if (remappingTable.isEmpty()) { return; } if (dataType != NiftiDataType::NIFTI_TYPE_INT32) { return; } const int64_t num = getTotalNumberOfElements(); for (int64_t i = 0; i < num; i++) { dataPointerInt[i] = remappingTable[dataPointerInt[i]]; } } */ /** * Update label indices using the index converter array. Typically, * this is done when appending one label file to another. * * @param indexConverter Converts the label indices to new values. */ void GiftiDataArray::transferLabelIndices(const std::map& indexConverter) { if (this->getDataType() == NiftiDataTypeEnum::NIFTI_TYPE_INT32) { int64_t num = this->getTotalNumberOfElements(); for (int i = 0; i < num; i++) { int oldIndex = this->dataPointerInt[i]; std::map::const_iterator iter = indexConverter.find(oldIndex); if (iter != indexConverter.end()) { this->dataPointerInt[i] = iter->second; //indexConverter.get(newIndex); } else { this->dataPointerInt[i] = 0; } this->setModified(); } } } /** * read a GIFTI data array from text. * Data array should already be initialized and allocated. */ void GiftiDataArray::readFromText(const AString text, const GiftiEndianEnum::Enum dataEndianForReading, const GiftiArrayIndexingOrderEnum::Enum arraySubscriptingOrderForReading, const NiftiDataTypeEnum::Enum dataTypeForReading, const std::vector& dimensionsForReading, const GiftiEncodingEnum::Enum encodingForReading, const AString& externalFileNameForReading, const int64_t externalFileOffsetForReading, const bool isReadOnlyMetaData) { const NiftiDataTypeEnum::Enum requiredDataType = dataType; dataType = dataTypeForReading; encoding = encodingForReading; endian = dataEndianForReading; arraySubscriptingOrder = arraySubscriptingOrderForReading; setDimensions(dimensionsForReading); if (dimensionsForReading.size() == 0) { throw GiftiException("Data array has no dimensions."); } //setExternalFileInformation(externalFileNameForReading, // externalFileOffsetForReading);//TSC: don't set the external filename on the array, because that is what it uses when writing the array // // If NOT metadata only // if (isReadOnlyMetaData == false) { // // Total number of elements in Data Array // const int64_t numElements = getTotalNumberOfElements(); switch (encoding) { case GiftiEncodingEnum::ASCII: { std::istringstream stream(text.toStdString()); switch (dataType) { case NiftiDataTypeEnum::NIFTI_TYPE_FLOAT32: { float* ptr = dataPointerFloat; for (int64_t i = 0; i < numElements; i++) { stream >> *ptr; ptr++; } } break; case NiftiDataTypeEnum::NIFTI_TYPE_INT32: { int32_t* ptr = dataPointerInt; for (int64_t i = 0; i < numElements; i++) { stream >> *ptr; ptr++; } } break; case NiftiDataTypeEnum::NIFTI_TYPE_UINT8: { uint8_t* ptr = dataPointerUByte; int32_t c; for (int64_t i = 0; i < numElements; i++) { stream >> c; *ptr = static_cast(c); ptr++; } } break; default: throw GiftiException("DataType " + NiftiDataTypeEnum::toName(dataType) + " not supported in GIFTI"); } } break; case GiftiEncodingEnum::BASE64_BINARY: { // // Decode the Base64 data using VTK's algorithm // const uint64_t numDecoded = Base64::decode((const unsigned char*)(text.toStdString().c_str()), data.size(), &data[0]); if (numDecoded != data.size()) { std::ostringstream str; str << "Decoding of Base64 Binary data failed.\n" << "Decoded " << AString::number(numDecoded).toStdString() << " bytes but should be " << AString::number(static_cast(data.size())).toStdString() << " bytes."; throw GiftiException(AString::fromStdString(str.str())); } // // Is byte swapping needed ? // if (endian != getSystemEndian()) { byteSwapData(getSystemEndian()); } } break; case GiftiEncodingEnum::GZIP_BASE64_BINARY: { // // Decode the Base64 data using VTK's algorithm // unsigned char* dataBuffer = new unsigned char[data.size()]; //const char* textChars = text.toAscii().constData(); const uint64_t numDecoded = Base64::decode((unsigned char*)text.toStdString().c_str(), data.size(), dataBuffer); if (numDecoded == 0) { std::ostringstream str; str << "Decoding of GZip Base64 Binary data failed." << "Decoded " << AString::number(numDecoded).toStdString() << " bytes but should be " << AString::number(static_cast(data.size())).toStdString() << " bytes."; throw GiftiException(AString::fromStdString(str.str())); } // // Uncompress the data using VTK's algorithm // DataCompressZLib compressor; const uint64_t uncompressedDataLength = compressor.uncompressData(dataBuffer, numDecoded, (unsigned char*)&data[0], data.size()); if (uncompressedDataLength != data.size()) { std::ostringstream str; str << "Decompression of Binary data failed.\n" << "Uncompressed " << AString::number(uncompressedDataLength).toStdString() << " bytes but should be " << AString::number(static_cast(data.size())).toStdString() << " bytes."; throw GiftiException(AString::fromStdString(str.str())); } // // Free memory // delete[] dataBuffer; // // Is byte swapping needed ? // if (endian != getSystemEndian()) { byteSwapData(getSystemEndian()); } } break; case GiftiEncodingEnum::EXTERNAL_FILE_BINARY: { if (externalFileNameForReading.length() <= 0) { throw GiftiException("External file name is empty."); } std::ifstream extBinFile(externalFileNameForReading.toStdString().c_str(), std::ifstream::in | std::ifstream::binary); if (extBinFile.good() == false) { throw GiftiException("Error opening \"" + externalFileNameForReading + "\""); } else { // // Move to the offset of the data // extBinFile.seekg(externalFileOffsetForReading, std::ios::beg); if (extBinFile.good() == false) { throw GiftiException("Error seeking to \"" + AString::number(externalFileOffsetForReading) + "\" in \"" + externalFileNameForReading + "\""); } // // Set the number of bytes that must be read // std::streamsize numberOfBytesToRead = 0; char* pointerToForReadingData = NULL; switch (dataType) { case NiftiDataTypeEnum::NIFTI_TYPE_FLOAT32: numberOfBytesToRead = numElements * sizeof(float); pointerToForReadingData = (char*)dataPointerFloat; break; case NiftiDataTypeEnum::NIFTI_TYPE_INT32: numberOfBytesToRead = numElements * sizeof(int32_t); pointerToForReadingData = (char*)dataPointerInt; break; case NiftiDataTypeEnum::NIFTI_TYPE_UINT8: numberOfBytesToRead = numElements * sizeof(uint8_t); pointerToForReadingData = (char*)dataPointerUByte; break; default: throw GiftiException("DataType " + NiftiDataTypeEnum::toName(dataType) + " not supported in GIFTI"); } // // Read the data // extBinFile.read((char*)pointerToForReadingData, numberOfBytesToRead); if(extBinFile.good() == false) { throw GiftiException("Tried to read " + AString::number((int64_t)numberOfBytesToRead) + " from " + externalFileOffsetForReading + " but failed"); } // // Is byte swapping needed ? // if (endian != getSystemEndian()) { byteSwapData(getSystemEndian()); } } } break; } // // Check if data type needs to be converted // if (requiredDataType != dataType) { if (intent != NiftiIntentEnum::NIFTI_INTENT_POINTSET) { convertToDataType(requiredDataType); } } // // Are array indices in opposite order // if (arraySubscriptingOrderForReading == GiftiArrayIndexingOrderEnum::COLUMN_MAJOR_ORDER) { convertArrayIndexingOrder(); } } // If NOT metadata only setModified(); } /** * convert array indexing order of data. */ void GiftiDataArray::convertArrayIndexingOrder() { const int32_t numDim = static_cast(dimensions.size()); if (numDim > 2) { throw GiftiException("Row/Column Major order conversion unavailable for arrays " "with dimensions greater than two."); } // // Swap data // if (numDim == 2) { int32_t dimI = dimensions[0]; int32_t dimJ = dimensions[1]; /* * If a dimensions is "1", the data does not need to be transposed. */ if ((dimI != 1) && (dimJ != 1)) { // // Is matrix square? // if (dimI == dimJ) { switch (dataType) { case NiftiDataTypeEnum::NIFTI_TYPE_FLOAT32: { for (int64_t i = 1; i < dimI; i++) { for (int64_t j = 0; j < i; j++) { const int64_t indexLowerLeft = (i * dimJ) + j; const int64_t indexUpperRight = (j * dimI) + i; float temp = dataPointerFloat[indexLowerLeft]; dataPointerFloat[indexLowerLeft] = dataPointerFloat[indexUpperRight]; dataPointerFloat[indexUpperRight] = temp; } } } break; case NiftiDataTypeEnum::NIFTI_TYPE_INT32: { for (int64_t i = 1; i < dimI; i++) { for (int64_t j = 0; j < i; j++) { const int64_t indexLowerLeft = (i * dimJ) + j; const int64_t indexUpperRight = (j * dimI) + i; const int32_t temp = dataPointerInt[indexLowerLeft]; dataPointerInt[indexLowerLeft] = dataPointerInt[indexUpperRight]; dataPointerInt[indexUpperRight] = temp; } } } break; case NiftiDataTypeEnum::NIFTI_TYPE_UINT8: { for (int64_t i = 1; i < dimI; i++) { for (int64_t j = 0; j < i; j++) { const int64_t indexLowerLeft = (i * dimJ) + j; const int64_t indexUpperRight = (j * dimI) + i; const uint8_t temp = dataPointerUByte[indexLowerLeft]; dataPointerUByte[indexLowerLeft] = dataPointerUByte[indexUpperRight]; dataPointerUByte[indexUpperRight] = temp; } } } break; default: throw GiftiException("DataType " + NiftiDataTypeEnum::toName(dataType) + " not supported in GIFTI"); break; } } else { // // Copy the data // std::vector dataCopy = data; switch (arraySubscriptingOrder) { case GiftiArrayIndexingOrderEnum::ROW_MAJOR_ORDER: switch (dataType) { case NiftiDataTypeEnum::NIFTI_TYPE_FLOAT32: { float* ptr = (float*)&(dataCopy[0]); for (int64_t i = 0; i < dimI; i++) { for (int64_t j = 0; j < dimJ; j++) { const int64_t indx = (j * dimI) + i; const int64_t ptrIndex = (i * dimJ) + j; dataPointerFloat[indx] = ptr[ptrIndex]; } } } break; case NiftiDataTypeEnum::NIFTI_TYPE_INT32: { uint32_t* ptr = (uint32_t*)&(dataCopy[0]); for (int64_t i = 0; i < dimI; i++) { for (int64_t j = 0; j < dimJ; j++) { const int64_t indx = (j * dimI) + i; const int64_t ptrIndex = (i * dimJ) + j; dataPointerInt[indx] = ptr[ptrIndex]; } } } break; case NiftiDataTypeEnum::NIFTI_TYPE_UINT8: { uint8_t* ptr = (uint8_t*)&(dataCopy[0]); for (int64_t i = 0; i < dimI; i++) { for (int64_t j = 0; j < dimJ; j++) { const int64_t indx = (j * dimI) + i; const int64_t ptrIndex = (i * dimJ) + j; dataPointerUByte[indx] = ptr[ptrIndex]; } } } break; default: throw GiftiException("DataType " + NiftiDataTypeEnum::toName(dataType) + " not supported in GIFTI"); break; } break; case GiftiArrayIndexingOrderEnum::COLUMN_MAJOR_ORDER: switch (dataType) { case NiftiDataTypeEnum::NIFTI_TYPE_FLOAT32: { float* ptr = (float*)&(dataCopy[0]); for (int64_t i = 0; i < dimI; i++) { for (int64_t j = 0; j < dimJ; j++) { const int64_t indx = (i * dimJ) + j; const int64_t ptrIndex = (j * dimI) + i; dataPointerFloat[indx] = ptr[ptrIndex]; } } } break; case NiftiDataTypeEnum::NIFTI_TYPE_INT32: { uint32_t* ptr = (uint32_t*)&(dataCopy[0]); for (int64_t i = 0; i < dimI; i++) { for (int64_t j = 0; j < dimJ; j++) { const int64_t indx = (i * dimJ) + j; const int64_t ptrIndex = (j * dimI) + i; dataPointerInt[indx] = ptr[ptrIndex]; } } } break; case NiftiDataTypeEnum::NIFTI_TYPE_UINT8: { uint8_t* ptr = (uint8_t*)&(dataCopy[0]); for (int64_t i = 0; i < dimI; i++) { for (int64_t j = 0; j < dimJ; j++) { const int64_t indx = (i * dimJ) + j; const int64_t ptrIndex = (j * dimI) + i; dataPointerUByte[indx] = ptr[ptrIndex]; } } } break; default: throw GiftiException("DataType " + NiftiDataTypeEnum::toName(dataType) + " not supported in GIFTI"); break; } break; } } } } switch (arraySubscriptingOrder) { case GiftiArrayIndexingOrderEnum::COLUMN_MAJOR_ORDER: arraySubscriptingOrder = GiftiArrayIndexingOrderEnum::ROW_MAJOR_ORDER; break; case GiftiArrayIndexingOrderEnum::ROW_MAJOR_ORDER: arraySubscriptingOrder = GiftiArrayIndexingOrderEnum::COLUMN_MAJOR_ORDER; break; } } /** * write the data as XML. * @param stream * Stream for XML. * @param externalBinaryOutputStream * Stream for external binary file. * @param encodingForWriting * GIFTI encoding used when writing the data. */ void GiftiDataArray::writeAsXML(std::ostream& stream, std::ostream* externalBinaryOutputStream, GiftiEncodingEnum::Enum encodingForWriting) { this->encoding = encodingForWriting; // // Do not write if data array is isEmpty() // const int64_t numRows = this->dimensions[0]; if (numRows <= 0) { return; } XmlWriter xmlWriter(stream); // // Clean up the dimensions by removing any "last" dimensions that // are one with the exception of the first dimension // e.g.: dimension = [73730, 1] becomes [73730] // int64_t dimensionality = static_cast(dimensions.size()); for (int64_t i = (dimensionality - 1); i >= 1; i--) { if (dimensions[i] <= 1) { dimensions.resize(i); } } dimensionality = static_cast(dimensions.size()); /* * Push the palette color mapping into the metadata. */ if (this->paletteColorMapping != NULL) { const AString paletteXML = this->paletteColorMapping->encodeInXML(); this->getMetaData()->set(GiftiMetaDataXmlElements::METADATA_NAME_PALETTE_COLOR_MAPPING, paletteXML); } // // External file not supported // //const AString externalFileName = ""; //const AString externalFileOffset = "0"; // // Write the opening tag // XmlAttributes dataAtt; dataAtt.addAttribute(GiftiXmlElements::ATTRIBUTE_DATA_ARRAY_INTENT, NiftiIntentEnum::toName(this->intent)); dataAtt.addAttribute(GiftiXmlElements::ATTRIBUTE_DATA_ARRAY_DATA_TYPE, NiftiDataTypeEnum::toName(this->dataType)); dataAtt.addAttribute(GiftiXmlElements::ATTRIBUTE_DATA_ARRAY_INDEXING_ORDER, GiftiArrayIndexingOrderEnum::toGiftiName(this->arraySubscriptingOrder)); dataAtt.addAttribute(GiftiXmlElements::ATTRIBUTE_DATA_ARRAY_DIMENSIONALITY, dimensionality); for (int64_t i = 0; i < dimensionality; i++) { const AString dimName = GiftiXmlElements::ATTRIBUTE_DATA_ARRAY_DIM_PREFIX + AString::number(i); dataAtt.addAttribute(dimName, this->dimensions[i]); } dataAtt.addAttribute(GiftiXmlElements::ATTRIBUTE_DATA_ARRAY_ENCODING, GiftiEncodingEnum::toGiftiName(this->encoding)); dataAtt.addAttribute(GiftiXmlElements::ATTRIBUTE_DATA_ARRAY_ENDIAN, GiftiEndianEnum::toGiftiName(this->endian)); dataAtt.addAttribute(GiftiXmlElements::ATTRIBUTE_DATA_ARRAY_EXTERNAL_FILE_NAME, externalFileName); dataAtt.addAttribute(GiftiXmlElements::ATTRIBUTE_DATA_ARRAY_EXTERNAL_FILE_OFFSET, externalFileOffset); xmlWriter.writeStartElement(GiftiXmlElements::TAG_DATA_ARRAY, dataAtt); // // Write the metadata // this->metaData.writeAsXML(xmlWriter); /* * Write one identity matrix for the transformation matrix. * * We do not want to write the transformation matrices that * were read from the file as the first matrix was applied to * the coordinates when the file was read. To do so would require * applying an inverse of the matrix. However, the user may have * modified the coordinates so the matrix is no longer valid for * the file's coordinates. */ if (getIntent() == NiftiIntentEnum::NIFTI_INTENT_POINTSET) { Matrix4x4 identityMatrix; identityMatrix.identity(); identityMatrix.setDataSpaceName(NiftiTransformEnum::toName(NiftiTransformEnum::NIFTI_XFORM_TALAIRACH)); identityMatrix.setTransformedSpaceName(NiftiTransformEnum::toName(NiftiTransformEnum::NIFTI_XFORM_TALAIRACH)); identityMatrix.writeAsGiftiXML(xmlWriter, GiftiXmlElements::TAG_COORDINATE_TRANSFORMATION_MATRIX, GiftiXmlElements::TAG_MATRIX_DATA_SPACE, GiftiXmlElements::TAG_MATRIX_TRANSFORMED_SPACE, GiftiXmlElements::TAG_MATRIX_DATA); } // // NOTE: for the base64 and ZLIB-Base64 data, it is important that there are // no spaces between the and tags. // switch (encoding) { case GiftiEncodingEnum::ASCII: { // // Write the start element. // xmlWriter.writeStartElement(GiftiXmlElements::TAG_DATA); // // Newline after tag (only do this for ASCII !!!) // //stream << "\n"; // // determine the number of items per row (node) // int64_t numItemsPerRow = 1; for (uint32_t i = 1; i < dimensions.size(); i++) { numItemsPerRow *= dimensions[i]; } // // Write the rows // int32_t offset = 0; for (int32_t i = 0; i < numRows; i++) { xmlWriter.writeCharacters(" "); switch (dataType) { case NiftiDataTypeEnum::NIFTI_TYPE_FLOAT32: for (int64_t j = 0; j < numItemsPerRow; j++) { xmlWriter.writeCharacters(AString::number(this->dataPointerFloat[offset + j])); xmlWriter.writeCharacters(" "); } break; case NiftiDataTypeEnum::NIFTI_TYPE_INT32: for (int64_t j = 0; j < numItemsPerRow; j++) { xmlWriter.writeCharacters(AString::number(this->dataPointerInt[offset + j])); xmlWriter.writeCharacters(" "); } break; case NiftiDataTypeEnum::NIFTI_TYPE_UINT8: for (int64_t j = 0; j < numItemsPerRow; j++) { xmlWriter.writeCharacters(AString::number(this->dataPointerUByte[offset + j])); xmlWriter.writeCharacters(" "); } break; default: throw GiftiException("DataType " + NiftiDataTypeEnum::toName(dataType) + " not supported in GIFTI"); break; } xmlWriter.writeCharacters("\n"); offset += numItemsPerRow; } // // Write the closing Data tag // xmlWriter.writeEndElement(); } break; case GiftiEncodingEnum::BASE64_BINARY: { // // Encode the data with VTK's Base64 algorithm // const uint64_t bufferLength = static_cast(data.size() * 1.5); char* buffer = new char[bufferLength]; const uint64_t compressedLength = Base64::encode(&data[0], data.size(), (unsigned char*)buffer); if (compressedLength >= bufferLength) { throw GiftiException( "Base64 encoding buffer length (" + AString::number(bufferLength) + ") is too small but needs to be " + AString::number(compressedLength)); } buffer[compressedLength] = '\0'; // // Write the data MUST BE NO space around data // xmlWriter.writeElementNoSpace(GiftiXmlElements::TAG_DATA, buffer); // // Free memory // delete[] buffer; } break; case GiftiEncodingEnum::GZIP_BASE64_BINARY: { // // Compress the data with VTK's ZLIB algorithm // DataCompressZLib compressor; unsigned long compressedDataBufferLength = compressor.getMaximumCompressionSpace(data.size()); unsigned char* compressedDataBuffer = new unsigned char[compressedDataBufferLength]; unsigned long compressedDataLength = compressor.compressData(&data[0], data.size(), compressedDataBuffer, compressedDataBufferLength); // // Encode the data with VTK's Base64 algorithm // char* buffer = new char[static_cast(compressedDataLength * 1.5)]; const uint64_t compressedLength = Base64::encode(compressedDataBuffer, compressedDataLength, (unsigned char*)buffer); buffer[compressedLength] = '\0'; // // Write the data MUST BE NO space around data // xmlWriter.writeElementNoSpace(GiftiXmlElements::TAG_DATA, buffer); // // Free memory // delete[] buffer; delete[] compressedDataBuffer; } break; case GiftiEncodingEnum::EXTERNAL_FILE_BINARY: { const int64_t dataLength = data.size(); externalBinaryOutputStream->write((const char*)&data[0], dataLength); if (externalBinaryOutputStream->bad()) { throw GiftiException("Output stream for external file reports its status as bad."); } // // Write the empty data // xmlWriter.writeElementNoSpace(GiftiXmlElements::TAG_DATA, ""); } break; } // // write the closing data array tag // xmlWriter.writeEndElement(); } /** * convert to data type. */ void GiftiDataArray::convertToDataType(const NiftiDataTypeEnum::Enum newDataType) { if (newDataType != dataType) { // // make a copy of myself // GiftiDataArray copyOfMe(*this); // // Set my new data type and reallocate memory // const NiftiDataTypeEnum::Enum oldDataType = dataType; dataType = newDataType; allocateData(); if (data.empty() == false) { // // Get total number of elements // int64_t numElements = 1; for (uint32_t i = 0; i < dimensions.size(); i++) { numElements *= dimensions[i]; } // // copy the data // for (int64_t i = 0; i < numElements; i++) { switch (dataType) { case NiftiDataTypeEnum::NIFTI_TYPE_FLOAT32: switch (oldDataType) { case NiftiDataTypeEnum::NIFTI_TYPE_FLOAT32: dataPointerFloat[i] = static_cast(copyOfMe.dataPointerFloat[i]); break; case NiftiDataTypeEnum::NIFTI_TYPE_INT32: dataPointerFloat[i] = static_cast(copyOfMe.dataPointerInt[i]); break; case NiftiDataTypeEnum::NIFTI_TYPE_UINT8: dataPointerFloat[i] = static_cast(copyOfMe.dataPointerUByte[i]); break; default: throw GiftiException("DataType " + NiftiDataTypeEnum::toName(oldDataType) + " not supported in GIFTI"); break; } break; case NiftiDataTypeEnum::NIFTI_TYPE_INT32: switch (oldDataType) { case NiftiDataTypeEnum::NIFTI_TYPE_FLOAT32: dataPointerInt[i] = static_cast(copyOfMe.dataPointerFloat[i]); break; case NiftiDataTypeEnum::NIFTI_TYPE_INT32: dataPointerInt[i] = static_cast(copyOfMe.dataPointerInt[i]); break; case NiftiDataTypeEnum::NIFTI_TYPE_UINT8: dataPointerInt[i] = static_cast(copyOfMe.dataPointerUByte[i]); break; default: throw GiftiException("DataType " + NiftiDataTypeEnum::toName(oldDataType) + " not supported in GIFTI"); break; } break; case NiftiDataTypeEnum::NIFTI_TYPE_UINT8: switch (oldDataType) { case NiftiDataTypeEnum::NIFTI_TYPE_FLOAT32: dataPointerUByte[i] = static_cast(copyOfMe.dataPointerFloat[i]); break; case NiftiDataTypeEnum::NIFTI_TYPE_INT32: dataPointerUByte[i] = static_cast(copyOfMe.dataPointerInt[i]); break; case NiftiDataTypeEnum::NIFTI_TYPE_UINT8: dataPointerUByte[i] = static_cast(copyOfMe.dataPointerUByte[i]); break; default: throw GiftiException("DataType " + NiftiDataTypeEnum::toName(oldDataType) + " not supported in GIFTI"); break; } break; default: throw GiftiException("DataType " + NiftiDataTypeEnum::toName(dataType) + " not supported in GIFTI"); break; } } } } setModified(); } /** * byte swap the data (data read is different endian than this system). */ void GiftiDataArray::byteSwapData(const GiftiEndianEnum::Enum newEndian) { endian = newEndian; switch (dataType) { case NiftiDataTypeEnum::NIFTI_TYPE_FLOAT32: ByteSwapping::swapBytes(dataPointerFloat, getTotalNumberOfElements()); break; case NiftiDataTypeEnum::NIFTI_TYPE_INT32: ByteSwapping::swapBytes(dataPointerInt, getTotalNumberOfElements()); break; case NiftiDataTypeEnum::NIFTI_TYPE_UINT8: // should not need to swap ?? break; default: CaretAssertMessage(0, "Unsupported GIFTI data Type"); break; } } /** * Set this object has been modified. * */ void GiftiDataArray::setModified() { this->modifiedFlag = true; if (this->descriptiveStatistics != NULL) { delete this->descriptiveStatistics; this->descriptiveStatistics = NULL; } if (this->descriptiveStatisticsLimitedValues != NULL) { delete this->descriptiveStatisticsLimitedValues; this->descriptiveStatisticsLimitedValues = NULL; } } /** * Set this object as not modified. Object should also * clear the modification status of its children. * */ void GiftiDataArray::clearModified() { this->modifiedFlag = false; if (this->paletteColorMapping != NULL) { this->paletteColorMapping->clearModified(); } } /** * Get the modification status. Returns true if this object or * any of its children have been modified. * * DOES NOT include palette color mapping modification status. * * @return - The modification status. * */ bool GiftiDataArray::isModified() const { // if (this->paletteColorMapping != NULL) { // if (this->paletteColorMapping->isModified()) { // return true; // } // } return this->modifiedFlag; } /** * get minimum and maximum values (valid for int data only). */ void GiftiDataArray::getMinMaxValues(int& minValue, int& maxValue) const { if (minMaxIntValuesValid == false) { minValueInt = std::numeric_limits::max(); minValueInt = std::numeric_limits::min(); int64_t numItems = getTotalNumberOfElements(); for (int64_t i = 0; i < numItems; i++) { minValueInt = std::min(minValueInt, dataPointerInt[i]); maxValueInt = std::max(maxValueInt, dataPointerInt[i]); } minMaxIntValuesValid = true; } minValue = minValueInt; maxValue = maxValueInt; } /** * Get the minimum and maximum values for float data. * * @param minValue * Output minimum value. * @param maxValue * Output maximum value. */ void GiftiDataArray::getMinMaxValuesFloat(float& minValue, float& maxValue) const { if (minMaxFloatValuesValid == false) { minValueFloat = std::numeric_limits::max(); maxValueFloat = -std::numeric_limits::max(); int64_t numItems = getTotalNumberOfElements(); for (int64_t i = 0; i < numItems; i++) { minValueFloat = std::min(minValueFloat, dataPointerFloat[i]); maxValueFloat = std::max(maxValueFloat, dataPointerFloat[i]); } minMaxFloatValuesValid = true; } minValue = minValueFloat; maxValue = maxValueFloat; } /** * set all elements of array to zero. */ void GiftiDataArray::zeroize() { if (data.empty() == false) { std::fill(data.begin(), data.end(), 0); } metaData.clear(); nonWrittenMetaData.clear(); } /** * get an offset for indices into data (dimensionality of indices must be same as data). */ int64_t GiftiDataArray::getDataOffset(const int32_t indices[]) const { const int32_t numDim = static_cast(dimensions.size()); int64_t offset = 0; int64_t dimProduct = 1; switch (this->arraySubscriptingOrder) { case GiftiArrayIndexingOrderEnum::ROW_MAJOR_ORDER: for (int32_t d = (numDim - 1); d >= 0; d--) { offset += indices[d] * dimProduct; dimProduct *= dimensions[d]; } break; case GiftiArrayIndexingOrderEnum::COLUMN_MAJOR_ORDER: // correct??? for (int32_t d = 0; d <= (numDim - 1); d++) { offset += indices[d] * dimProduct; dimProduct *= dimensions[d]; } break; } return offset; } /** * get a float value (data type must be float and dimensionality of indices must be same as data). */ float GiftiDataArray::getDataFloat32(const int32_t indices[]) const { const int64_t offset = getDataOffset(indices); return dataPointerFloat[offset]; } /** * get a float value pointer(data type must be float and dimensionality of indices must be same as data). */ const float* GiftiDataArray::getDataFloat32Pointer(const int32_t indices[]) const { const int64_t offset = getDataOffset(indices); return &dataPointerFloat[offset]; } /** * get an int value (data type must be int and dimensionality of indices must be same as data). */ int32_t GiftiDataArray::getDataInt32(const int32_t indices[]) const { const int64_t offset = getDataOffset(indices); return dataPointerInt[offset]; } /** * get an int value pointer (data type must be int and dimensionality of indices must be same as data). */ const int32_t* GiftiDataArray::getDataInt32Pointer(const int32_t indices[]) const { const int64_t offset = getDataOffset(indices); return &dataPointerInt[offset]; } /** * get a byte value (data type must be unsigned char and dimensionality of indices must be same as data). */ uint8_t GiftiDataArray::getDataUInt8(const int32_t indices[]) const { const int64_t offset = getDataOffset(indices); return dataPointerUByte[offset]; } /** * get a byte value pointer(data type must be unsigned char and dimensionality of indices must be same as data). */ const uint8_t* GiftiDataArray::getDataUInt8Pointer(const int32_t indices[]) const { const int64_t offset = getDataOffset(indices); return &dataPointerUByte[offset]; } /** * set a float value (data type must be float and dimensionality of indices must be same as data). */ void GiftiDataArray::setDataFloat32(const int32_t indices[], const float dataValue) const { const int64_t offset = getDataOffset(indices); dataPointerFloat[offset] = dataValue; } /** * set an int value (data type must be int and dimensionality of indices must be same as data). */ void GiftiDataArray::setDataInt32(const int32_t indices[], const int32_t dataValue) const { const int64_t offset = getDataOffset(indices); dataPointerInt[offset] = dataValue; } /** * set a byte value (data type must be unsigned char and dimensionality of indices must be same as data). */ void GiftiDataArray::setDataUInt8(const int32_t indices[], const uint8_t dataValue) const { const int64_t offset = getDataOffset(indices); dataPointerUByte[offset] = dataValue; } /** * valid intent name. */ bool GiftiDataArray::intentNameValid(const AString& intentNameIn) { bool valid = false; NiftiIntentEnum::fromName(intentNameIn, &valid); return valid; } /** * remove all matrices. */ void GiftiDataArray::removeAllMatrices() { matrices.clear(); setModified(); } /** * remove a matrix. */ void GiftiDataArray::removeMatrix(const int32_t indx) { matrices.erase(matrices.begin() + indx); setModified(); } /** * Get the color palette mapping. * * @return * The palette color mapping. */; PaletteColorMapping* GiftiDataArray::getPaletteColorMapping() { if (this->paletteColorMapping == NULL) { this->paletteColorMapping = new PaletteColorMapping(); const AString paletteString = this->getMetaData()->get(GiftiMetaDataXmlElements::METADATA_NAME_PALETTE_COLOR_MAPPING); if (paletteString.isEmpty() == false) { try { this->paletteColorMapping->decodeFromStringXML(paletteString); } catch (const XmlException& e) { this->paletteColorMapping = new PaletteColorMapping(); CaretLogSevere("Failed to parse Palette XML: " + e.whatString()); } } this->paletteColorMapping->clearModified(); } return this->paletteColorMapping; } /** * Get the color palette mapping. * * @return * The palette color mapping. */; const PaletteColorMapping* GiftiDataArray::getPaletteColorMapping() const { if (this->paletteColorMapping == NULL) { this->paletteColorMapping = new PaletteColorMapping(); const AString paletteString = this->getMetaData()->get(GiftiMetaDataXmlElements::METADATA_NAME_PALETTE_COLOR_MAPPING); if (paletteString.isEmpty() == false) { try { this->paletteColorMapping->decodeFromStringXML(paletteString); } catch (const XmlException& e) { this->paletteColorMapping = new PaletteColorMapping(); CaretLogSevere("Failed to parse Palette XML: " + e.whatString()); } } this->paletteColorMapping->clearModified(); } return this->paletteColorMapping; } /** * Get the descriptive statistics for this data array. * * @return Descriptive statistics. */ const DescriptiveStatistics* GiftiDataArray::getDescriptiveStatistics() const { if (this->getDataType() == NiftiDataTypeEnum::NIFTI_TYPE_FLOAT32) { if (this->descriptiveStatistics == NULL) { this->descriptiveStatistics = new DescriptiveStatistics(); } this->descriptiveStatistics->update(this->dataPointerFloat, this->getTotalNumberOfElements()); } return this->descriptiveStatistics; } const FastStatistics* GiftiDataArray::getFastStatistics() const { if (this->getDataType() == NiftiDataTypeEnum::NIFTI_TYPE_FLOAT32) { if (m_fastStatistics == NULL) { m_fastStatistics.grabNew(new FastStatistics()); } m_fastStatistics->update(dataPointerFloat, getTotalNumberOfElements()); } return m_fastStatistics; } const Histogram* GiftiDataArray::getHistogram() const { if (this->getDataType() == NiftiDataTypeEnum::NIFTI_TYPE_FLOAT32) { if (m_histogram == NULL) { m_histogram.grabNew(new Histogram(100)); } m_histogram->update(dataPointerFloat, getTotalNumberOfElements()); } return m_histogram; } /** * Get the descriptive statistics for this data array limited * to values within the given ranges. * * @param mostPositiveValueInclusive * Values more positive than this value are excluded. * @param leastPositiveValueInclusive * Values less positive than this value are excluded. * @param leastNegativeValueInclusive * Values less negative than this value are excluded. * @param mostNegativeValueInclusive * Values more negative than this value are excluded. * @param includeZeroValues * If true zero values (very near zero) are included. * @return Descriptive statistics. */ const DescriptiveStatistics* GiftiDataArray::getDescriptiveStatistics(const float mostPositiveValueInclusive, const float leastPositiveValueInclusive, const float leastNegativeValueInclusive, const float mostNegativeValueInclusive, const bool includeZeroValues) const { if (this->getDataType() == NiftiDataTypeEnum::NIFTI_TYPE_FLOAT32) { if (this->descriptiveStatisticsLimitedValues == NULL) { this->descriptiveStatisticsLimitedValues = new DescriptiveStatistics(); } this->descriptiveStatisticsLimitedValues->update(this->dataPointerFloat, this->getTotalNumberOfElements(), mostPositiveValueInclusive, leastPositiveValueInclusive, leastNegativeValueInclusive, mostNegativeValueInclusive, includeZeroValues); } return this->descriptiveStatisticsLimitedValues; } const Histogram* GiftiDataArray::getHistogram(const float mostPositiveValueInclusive, const float leastPositiveValueInclusive, const float leastNegativeValueInclusive, const float mostNegativeValueInclusive, const bool includeZeroValues) const { if (this->getDataType() == NiftiDataTypeEnum::NIFTI_TYPE_FLOAT32) { if (m_histogramLimitedValues == NULL) { m_histogramLimitedValues.grabNew(new Histogram(100)); } m_histogramLimitedValues->update(dataPointerFloat, getTotalNumberOfElements(), mostPositiveValueInclusive, leastPositiveValueInclusive, leastNegativeValueInclusive, mostNegativeValueInclusive, includeZeroValues); } return m_histogramLimitedValues; } AString GiftiDataArray::toString() const { std::ostringstream str; str << "Data Array" << std::endl; str << " DataType=" << NiftiDataTypeEnum::toName(this->dataType).toStdString() << std::endl; str << " Intent=" << NiftiIntentEnum::toName(this->intent).toStdString() << std::endl; str << " Dimensions=" << AString::fromNumbers(this->dimensions, ",").toStdString(); str << " MetaData=" << this->metaData.toString().toStdString() << std::endl; return AString::fromStdString(str.str()); } void GiftiDataArray::validateArrayAfterReading() { //pointset arrays are mandated to have at least one tranfsormation matrix, for some unknown reason if (intent == NiftiIntentEnum::NIFTI_INTENT_POINTSET && matrices.size() == 0) { CaretLogWarning("pointset gifti array did not include a transformation matrix, adding identity transform"); Matrix4x4 gm; gm.setDataSpaceName("NIFTI_XFORM_TALAIRACH"); gm.setTransformedSpaceName("NIFTI_XFORM_TALAIRACH"); matrices.push_back(gm); } } connectome-workbench-1.2.3+git41-gc4c6c90/src/Gifti/GiftiDataArray.h000066400000000000000000000411451300200146000246170ustar00rootroot00000000000000 #ifndef __GIFTI_DATA_ARRAY_H__ #define __GIFTI_DATA_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 #include #include #include #include #include "CaretObject.h" #include "CaretPointer.h" #include "DescriptiveStatistics.h" #include "FastStatistics.h" #include "GiftiArrayIndexingOrderEnum.h" #include "GiftiEncodingEnum.h" #include "GiftiEndianEnum.h" #include "GiftiLabelTable.h" #include "GiftiMetaData.h" #include "Histogram.h" #include "Matrix4x4.h" #include "NiftiEnums.h" #include "TracksModificationInterface.h" namespace caret { class GiftiFile; class GiftiException; class PaletteColorMapping; /// class GiftiDataArray. class GiftiDataArray : public CaretObject, TracksModificationInterface { public: // constructor GiftiDataArray(const NiftiIntentEnum::Enum intentIn, const NiftiDataTypeEnum::Enum dataTypeIn, const std::vector& dimensionsIn, const GiftiEncodingEnum::Enum encodingIn = GiftiEncodingEnum::ASCII); // constructor used when reading data GiftiDataArray(const NiftiIntentEnum::Enum intentIn); // copy constructor GiftiDataArray(const GiftiDataArray& nda); // assignment operator GiftiDataArray& operator=(const GiftiDataArray& nda); // destructor virtual ~GiftiDataArray(); // add rows (increase 1st dimension) void addRows(const int32_t numRowsToAdd); // delete rows void deleteRows(const std::vector& rowsToDelete); // convert all data arrays to data type void convertToDataType(const NiftiDataTypeEnum::Enum newDataType) ; // set the dimensions void setDimensions(const std::vector dimensionsIn); // reset column virtual void clear(); /// get the number of dimensions int32_t getNumberOfDimensions() const { return dimensions.size(); } /// get the dimensions std::vector getDimensions() const { return dimensions; } /// current size of the data (in bytes) int64_t getDataSizeInBytes() const { return data.size(); } /// get a dimension int32_t getDimension(const int32_t dimIndex) const { return dimensions[dimIndex]; } // get the number of rows (1st dimension) int64_t getNumberOfRows() const; // get number of components per node (2nd dimension) int64_t getNumberOfComponents() const; // get the total number of elements int64_t getTotalNumberOfElements() const; // get data offset //int64_t getDataOffset(const int64_t nodeNum, const int64_t componentNum) const;//TSC: implementation was wrong, commenting out for now // read a data array from text void readFromText(const AString text, const GiftiEndianEnum::Enum dataEndianForReading, const GiftiArrayIndexingOrderEnum::Enum arraySubscriptingOrderForReading, const NiftiDataTypeEnum::Enum dataTypeForReading, const std::vector& dimensionsForReading, const GiftiEncodingEnum::Enum encodingForReading, const AString& externalFileNameForReading, const int64_t externalFileOffsetForReading, const bool isReadOnlyMetaData); // write the data as XML void writeAsXML(std::ostream& stream, std::ostream* externalBinaryOutputStream, GiftiEncodingEnum::Enum encodingForWriting); /// get endian GiftiEndianEnum::Enum getEndian() const { return endian; } // set endian void setEndian(const GiftiEndianEnum::Enum e) { endian = e; } /// get the system's endian static GiftiEndianEnum::Enum getSystemEndian(); // get external file information void getExternalFileInformation(AString& nameOut, int64_t& offsetOut) const; // set external file information void setExternalFileInformation(const AString& nameIn, const int64_t offsetIn); /// get the metadata GiftiMetaData* getMetaData() { return &metaData; } /// get the metadata (const method) const GiftiMetaData* getMetaData() const { return &metaData; } /// set the metadata void setMetaData(const GiftiMetaData* gmd) { metaData = *gmd; setModified(); } /// get the number of matrices int32_t getNumberOfMatrices() const { return matrices.size(); } /// add a matrix void addMatrix(const Matrix4x4& gm) { matrices.push_back(gm); } /// get a matrix Matrix4x4* getMatrix(const int32_t indx) { return &matrices[indx]; } /// remove all matrices void removeAllMatrices(); /// remove a matrix void removeMatrix(const int32_t indx); /// get the matrix (const method) const Matrix4x4* getMatrix(const int32_t indx) const { return &matrices[indx]; } /// get the non-written metadata for values not saved to file //GiftiMetaData* getNonWrittenMetaData() { return &nonWrittenMetaData; } /// get the non-written metadata for values not save to file (const method) //const GiftiMetaData* getNonWrittenMetaData() const { return &nonWrittenMetaData; } /// get the data type NiftiDataTypeEnum::Enum getDataType() const { return dataType; } /// set the data type void setDataType(const NiftiDataTypeEnum::Enum dt) { dataType = dt; setModified(); } /// get the encoding GiftiEncodingEnum::Enum getEncoding() const { return encoding; } /// set the encoding void setEncoding(const GiftiEncodingEnum::Enum e) { encoding = e; setModified(); } /// get the data intent NiftiIntentEnum::Enum getIntent() const { return intent; } /// set the data intent void setIntent(const NiftiIntentEnum::Enum cat) { intent = cat; setModified(); } /// valid intent name static bool intentNameValid(const AString& intentNameIn); /// get array subscripting order GiftiArrayIndexingOrderEnum::Enum getArraySubscriptingOrder() const { return arraySubscriptingOrder; } /// set array subscripting order void setArraySubscriptingOrder(const GiftiArrayIndexingOrderEnum::Enum aso) { arraySubscriptingOrder = aso; } /// get pointer for floating point data (valid only if data type is FLOAT) float* getDataPointerFloat() { return dataPointerFloat; } /// get pointer for floating point data (const method) (valid only if data type is FLOAT) const float* getDataPointerFloat() const { return dataPointerFloat; } /// get pointer for integer data (valid only if data type is INT) int32_t* getDataPointerInt() { return dataPointerInt; } /// get pointer for integer data (const method) (valid only if data type is INT) const int32_t* getDataPointerInt() const { return dataPointerInt; } /// get pointer for unsigned byte data (valid only if data type is UBYTE) uint8_t* getDataPointerUByte() { return dataPointerUByte; } /// get pointer for unsigned byte data (const method) (valid only if data type is UBYTE) const uint8_t* getDataPointerUByte() const { return dataPointerUByte; } // set all elements of array to zero void zeroize(); // get minimum and maximum values (valid for int data only) void getMinMaxValues(int& minValue, int& maxValue) const; void getMinMaxValuesFloat(float& minValue, float& maxValue) const; // remap integer values that are indices to a table //void remapIntValues(const std::vector& remappingTable); void transferLabelIndices(const std::map& indexConverter); // get the data type appropriate for the intent (returns true if valid intent) static bool getDataTypeAppropriateForIntent(const NiftiIntentEnum::Enum intentIn, NiftiDataTypeEnum::Enum& dataTypeOut); // get an offset for indices into data (dimensionality of indices must be same as data) int64_t getDataOffset(const int32_t indices[]) const; // get a float value (data type must be float and dimensionality of indices must be same as data) float getDataFloat32(const int32_t indices[]) const; // get a float value pointer (data type must be float and dimensionality of indices must be same as data) const float* getDataFloat32Pointer(const int32_t indices[]) const; // get an int value (data type must be int and dimensionality of indices must be same as data) int32_t getDataInt32(const int32_t indices[]) const; // get an int value pointer(data type must be int and dimensionality of indices must be same as data) const int32_t* getDataInt32Pointer(const int32_t indices[]) const; // get a byte value (data type must be unsigned char and dimensionality of indices must be same as data) uint8_t getDataUInt8(const int32_t indices[]) const; // get a byte value pointer (data type must be unsigned char and dimensionality of indices must be same as data) const uint8_t* getDataUInt8Pointer(const int32_t indices[]) const; // set a float value (data type must be float and dimensionality of indices must be same as data) void setDataFloat32(const int32_t indices[], const float dataValue) const; // set an int value (data type must be int and dimensionality of indices must be same as data) void setDataInt32(const int32_t indices[], const int32_t dataValue) const; // set a byte value (data type must be unsigned char and dimensionality of indices must be same as data) void setDataUInt8(const int32_t indices[], const uint8_t dataValue) const; void setModified(); void clearModified(); bool isModified() const; virtual AString toString() const; PaletteColorMapping* getPaletteColorMapping(); const PaletteColorMapping* getPaletteColorMapping() const; const DescriptiveStatistics* getDescriptiveStatistics() const; const FastStatistics* getFastStatistics() const; const Histogram* getHistogram() const; const DescriptiveStatistics* getDescriptiveStatistics(const float mostPositiveValueInclusive, const float leastPositiveValueInclusive, const float leastNegativeValueInclusive, const float mostNegativeValueInclusive, const bool includeZeroValues) const; const Histogram* getHistogram(const float mostPositiveValueInclusive, const float leastPositiveValueInclusive, const float leastNegativeValueInclusive, const float mostNegativeValueInclusive, const bool includeZeroValues) const; protected: //validate the array void validateArrayAfterReading(); // allocate data for this column virtual void allocateData(); // the copy helper (used by copy constructor and assignment operator) void copyHelperGiftiDataArray(const GiftiDataArray& nda); // update the data pointers void updateDataPointers(); // byte swap the data (data read is different endian than this system) void byteSwapData(const GiftiEndianEnum::Enum newEndian); /// convert array indexing order of data void convertArrayIndexingOrder(); /// the data std::vector data; /// size of one data type element uint32_t dataTypeSize; /// pointer for floating point data float* dataPointerFloat; /// pointer for integer data int32_t* dataPointerInt; /// pointer for unsigned byte data uint8_t* dataPointerUByte; /// the matrix (typically only used by coordinates) std::vector matrices; /// the metadata GiftiMetaData metaData; /// the metadata not written to file (mainly for file specific data array meta data) GiftiMetaData nonWrittenMetaData; /// dimensions of the data std::vector dimensions; /// data type NiftiDataTypeEnum::Enum dataType; /// encoding of data GiftiEncodingEnum::Enum encoding; // endian of data GiftiEndianEnum::Enum endian; /// intent name NiftiIntentEnum::Enum intent; /// array subscripting order GiftiArrayIndexingOrderEnum::Enum arraySubscriptingOrder; /// external file name AString externalFileName; /// external file offset int64_t externalFileOffset; /// the palette color mapping mutable PaletteColorMapping* paletteColorMapping; /// minimum float value mutable float minValueFloat; /// maximum float value mutable float maxValueFloat; /// min/max float values valid (child class must set this false when an array value is changed) mutable bool minMaxFloatValuesValid; /// minimum int value mutable int32_t minValueInt; /// maximum int value mutable int32_t maxValueInt; /// min/max int values valid (child class must set this false when an array value is changed) mutable bool minMaxIntValuesValid; mutable float negMaxPct; mutable float negMinPct; mutable float posMinPct; mutable float posMaxPct; mutable float negMaxPctValue; mutable float negMinPctValue; mutable float posMinPctValue; mutable float posMaxPctValue; /// min/max percentage values valid mutable bool minMaxPercentageValuesValid; /// statistics about data (DO NOT COPY) mutable DescriptiveStatistics* descriptiveStatistics; mutable CaretPointer m_fastStatistics; mutable CaretPointer m_histogram; /// 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 ****** /// allow NodeDataFile access to protected elements friend class GiftiFile; }; } // namespace #endif // __GIFTI_DATA_ARRAY_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Gifti/GiftiEncodingEnum.cxx000066400000000000000000000116621300200146000256760ustar00rootroot00000000000000/*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 __GIFTIENCODING_DECLARE__ #include "GiftiEncodingEnum.h" #undef __GIFTIENCODING_DECLARE__ #include "CaretAssert.h" using namespace caret; /** * Constructor. * * @param e * An enumerated value. * @param name * Name of enumberated value. */ GiftiEncodingEnum::GiftiEncodingEnum( const Enum e, const int32_t integerCode, const AString& name, const AString& giftiName) { this->e = e; this->integerCode = integerCode; this->name = name; this->giftiName = giftiName; } /** * Destructor. */ GiftiEncodingEnum::~GiftiEncodingEnum() { } void GiftiEncodingEnum::initialize() { if (initializedFlag) { return; } initializedFlag = true; enumData.push_back(GiftiEncodingEnum(ASCII, -1, "ASCII", "ASCII")); enumData.push_back(GiftiEncodingEnum(BASE64_BINARY, -1, "BASE64_BINARY", "Base64Binary")); enumData.push_back(GiftiEncodingEnum(GZIP_BASE64_BINARY, -1, "GZIP_BASE64_BINARY", "GZipBase64Binary")); enumData.push_back(GiftiEncodingEnum(EXTERNAL_FILE_BINARY, -1, "EXTERNAL_FILE_BINARY", "ExternalFileBinary")); } /** * Find the data for and enumerated value. * @param e * The enumerated value. * @return Pointer to data for this enumerated type * or NULL if no data for type or if type is invalid. */ const GiftiEncodingEnum* GiftiEncodingEnum::findData(const Enum e) { initialize(); size_t num = enumData.size(); for (size_t i = 0; i < num; i++) { const GiftiEncodingEnum* d = &enumData[i]; if (d->e == e) { return d; } } return NULL; } /** * Get a string representation of the enumerated type. * @param e * Enumerated value. * @return * String representing enumerated value. */ AString GiftiEncodingEnum::toName(Enum e) { initialize(); const GiftiEncodingEnum* gaio = findData(e); return gaio->name; } /** * Get an enumerated value corresponding to its 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. */ GiftiEncodingEnum::Enum GiftiEncodingEnum::fromName(const AString& s, bool* isValidOut) { initialize(); bool validFlag = false; Enum e = ASCII; for (std::vector::iterator iter = enumData.begin(); iter != enumData.end(); iter++) { const GiftiEncodingEnum& d = *iter; if (d.name == s) { e = d.e; validFlag = true; break; } } if (isValidOut != 0) { *isValidOut = validFlag; } else if (validFlag == false) { CaretAssertMessage(0, AString("name \"" + s + " \"failed to match enumerated value for type GiftiEncodingEnum")); } return e; } /** * Get a string representation of the enumerated type. * @param e * Enumerated value. * @return * String representing enumerated value. */ AString GiftiEncodingEnum::toGiftiName(Enum e) { initialize(); const GiftiEncodingEnum* gaio = findData(e); return gaio->giftiName; } /** * Get an enumerated value corresponding to its 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. */ GiftiEncodingEnum::Enum GiftiEncodingEnum::fromGiftiName(const AString& s, bool* isValidOut) { initialize(); bool validFlag = false; Enum e = ASCII; for (std::vector::iterator iter = enumData.begin(); iter != enumData.end(); iter++) { const GiftiEncodingEnum& d = *iter; if (d.giftiName == s) { e = d.e; validFlag = true; break; } } if (isValidOut != 0) { *isValidOut = validFlag; } else if (validFlag == false) { CaretAssertMessage(0, AString("giftiName \"" + s + " \"failed to match enumerated value for type GiftiEncodingEnum")); } return e; } connectome-workbench-1.2.3+git41-gc4c6c90/src/Gifti/GiftiEncodingEnum.h000066400000000000000000000045031300200146000253170ustar00rootroot00000000000000#ifndef __GIFTIENCODING_H__ #define __GIFTIENCODING_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 namespace caret { /** * GIFTI Encoding Types. */ class GiftiEncodingEnum { public: /** GIFTI Encoding Types. */ enum Enum { /** The data is ASCII Text */ ASCII, /** THe data is binary data that is encoded as text using the Base64 Algorithm */ BASE64_BINARY, /** The data is binary compressed using the GZIP algorithm and then encoded as Base64 */ GZIP_BASE64_BINARY, /** The data is stored in a separate, uncompressed, binary data file */ EXTERNAL_FILE_BINARY }; ~GiftiEncodingEnum(); static AString toName(Enum e); static Enum fromName(const AString& s, bool* isValidOut); static AString toGiftiName(Enum e); static Enum fromGiftiName(const AString& s, bool* isValidOut); private: GiftiEncodingEnum(const Enum e, const int32_t integerCode, const AString& name, const AString& giftiName); static const GiftiEncodingEnum* findData(const Enum e); static std::vector enumData; static void initialize(); static bool initializedFlag; Enum e; int32_t integerCode; AString name; AString giftiName; }; #ifdef __GIFTIENCODING_DECLARE__ std::vector GiftiEncodingEnum::enumData; bool GiftiEncodingEnum::initializedFlag = false; #endif // __GIFTIENCODING_DECLARE__ } // namespace #endif // __GIFTIENCODING_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Gifti/GiftiEndianEnum.cxx000066400000000000000000000112701300200146000253410ustar00rootroot00000000000000/*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 __GIFTIENDIAN_DECLARE__ #include "GiftiEndianEnum.h" #undef __GIFTIENDIAN_DECLARE__ #include "CaretAssert.h" using namespace caret; /** * Constructor. * * @param e * An enumerated value. * @param name * Name of enumberated value. */ GiftiEndianEnum::GiftiEndianEnum( const Enum e, const int32_t integerCode, const AString& name, const AString& giftiName) { this->e = e; this->integerCode = integerCode; this->name = name; this->giftiName = giftiName; } /** * Destructor. */ GiftiEndianEnum::~GiftiEndianEnum() { } void GiftiEndianEnum::initialize() { if (initializedFlag) { return; } initializedFlag = true; enumData.push_back(GiftiEndianEnum(ENDIAN_BIG, 0, "ENDIAN_BIG", "BigEndian")); enumData.push_back(GiftiEndianEnum(ENDIAN_LITTLE, 1, "ENDIAN_LITTLE", "LittleEndian")); } /** * Find the data for and enumerated value. * @param e * The enumerated value. * @return Pointer to data for this enumerated type * or NULL if no data for type or if type is invalid. */ const GiftiEndianEnum* GiftiEndianEnum::findData(const Enum e) { initialize(); size_t num = enumData.size(); for (size_t i = 0; i < num; i++) { const GiftiEndianEnum* d = &enumData[i]; if (d->e == e) { return d; } } return NULL; } /** * Get a string representation of the enumerated type. * @param e * Enumerated value. * @return * String representing enumerated value. */ AString GiftiEndianEnum::toName(Enum e) { initialize(); const GiftiEndianEnum* gaio = findData(e); return gaio->name; } /** * Get an enumerated value corresponding to its 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. */ GiftiEndianEnum::Enum GiftiEndianEnum::fromName(const AString& s, bool* isValidOut) { initialize(); bool validFlag = false; Enum e = ENDIAN_LITTLE; for (std::vector::iterator iter = enumData.begin(); iter != enumData.end(); iter++) { const GiftiEndianEnum& d = *iter; if (d.name == s) { e = d.e; validFlag = true; break; } } if (isValidOut != 0) { *isValidOut = validFlag; } else if (validFlag == false) { CaretAssertMessage(0, AString("name \"" + s + " \"failed to match enumerated value for type GiftiEndianEnum")); } return e; } /** * Get a string representation of the enumerated type. * @param e * Enumerated value. * @return * String representing enumerated value. */ AString GiftiEndianEnum::toGiftiName(Enum e) { initialize(); const GiftiEndianEnum* gaio = findData(e); return gaio->giftiName; } /** * Get an enumerated value corresponding to its 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. */ GiftiEndianEnum::Enum GiftiEndianEnum::fromGiftiName(const AString& s, bool* isValidOut) { initialize(); bool validFlag = false; Enum e = ENDIAN_LITTLE; for (std::vector::iterator iter = enumData.begin(); iter != enumData.end(); iter++) { const GiftiEndianEnum& d = *iter; if (d.giftiName == s) { e = d.e; validFlag = true; break; } } if (isValidOut != 0) { *isValidOut = validFlag; } else if (validFlag == false) { CaretAssertMessage(0, AString("giftiName \"" + s + " \"failed to match enumerated value for type GiftiEndianEnum")); } return e; } connectome-workbench-1.2.3+git41-gc4c6c90/src/Gifti/GiftiEndianEnum.h000066400000000000000000000040761300200146000247740ustar00rootroot00000000000000#ifndef __GIFTIENDIAN_H__ #define __GIFTIENDIAN_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 "GiftiException.h" #include #include #include namespace caret { /** * GIFTI Endian Types. */ class GiftiEndianEnum { public: /** GIFTI Endian Types. */ enum Enum { /** Data is in Big Endian byte order */ ENDIAN_BIG, /** Data is in Little Endian byte order */ ENDIAN_LITTLE }; ~GiftiEndianEnum(); static AString toName(Enum e); static Enum fromName(const AString& s, bool* isValidOut); static AString toGiftiName(Enum e); static Enum fromGiftiName(const AString& s, bool* isValidOut); private: GiftiEndianEnum(const Enum e, const int32_t integerCode, const AString& name, const AString& giftiName); static const GiftiEndianEnum* findData(const Enum e); static std::vector enumData; static void initialize(); static bool initializedFlag; Enum e; int32_t integerCode; AString name; AString giftiName; }; #ifdef __GIFTIENDIAN_DECLARE__ std::vector GiftiEndianEnum::enumData; bool GiftiEndianEnum::initializedFlag = false; #endif // __GIFTIENDIAN_DECLARE__ } // namespace #endif // __GIFTIENDIAN_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Gifti/GiftiFile.cxx000066400000000000000000001111621300200146000241760ustar00rootroot00000000000000/*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 "CaretAssert.h" #include "CaretLogger.h" #include "DataFileException.h" #include "FileInformation.h" #include "GiftiEncodingEnum.h" #define __GIFTI_FILE_MAIN__ #include "GiftiFile.h" #undef __GIFTI_FILE_MAIN__ #include "GiftiFileSaxReader.h" #include "GiftiMetaDataXmlElements.h" #include "GiftiFileWriter.h" #include "GiftiXmlElements.h" #include "XmlSaxParser.h" using namespace caret; /** * Constructor */ GiftiFile::GiftiFile(const AString& descriptiveName, const NiftiIntentEnum::Enum defaultDataArrayIntentIn, const NiftiDataTypeEnum::Enum defaultDataTypeIn, const AString& defaultExtension, const bool dataAreIndicesIntoLabelTableIn) : DataFile() { this->descriptiveName = descriptiveName; defaultDataArrayIntent = defaultDataArrayIntentIn; defaultDataType = defaultDataTypeIn; dataAreIndicesIntoLabelTable = dataAreIndicesIntoLabelTableIn; this->defaultExtension = defaultExtension; numberOfNodesForSparseNodeIndexFile = 0; this->encodingForWriting = GiftiFile::defaultEncodingForWriting; } /** * Constructor for generic gifti data array file */ GiftiFile::GiftiFile() : DataFile() { this->descriptiveName = "GIFTI"; defaultDataArrayIntent = NiftiIntentEnum::NIFTI_INTENT_NONE; defaultDataType = NiftiDataTypeEnum::NIFTI_TYPE_FLOAT32; dataAreIndicesIntoLabelTable = false; numberOfNodesForSparseNodeIndexFile = 0; this->defaultExtension = ".gii"; this->encodingForWriting = GiftiFile::defaultEncodingForWriting; } /** * copy constructor. */ GiftiFile::GiftiFile(const GiftiFile& nndf) : DataFile(nndf) { copyHelperGiftiFile(nndf); } /** * assignment operator. */ GiftiFile& GiftiFile::operator=(const GiftiFile& nndf) { if (this != &nndf) { DataFile::operator=(nndf); this->copyHelperGiftiFile(nndf); } return *this; } /** * copy helper. */ void GiftiFile::copyHelperGiftiFile(const GiftiFile& nndf) { labelTable = nndf.labelTable; metaData = nndf.metaData; defaultDataType = nndf.defaultDataType; defaultDataArrayIntent = nndf.defaultDataArrayIntent; dataAreIndicesIntoLabelTable = nndf.dataAreIndicesIntoLabelTable; numberOfNodesForSparseNodeIndexFile = nndf.numberOfNodesForSparseNodeIndexFile; this->descriptiveName = nndf.descriptiveName; this->defaultExtension = nndf.defaultExtension; int32_t numArrays = this->getNumberOfDataArrays(); for (int32_t i = (numArrays - 1); i >= 0; i--) { this->removeDataArray(i); } for (std::size_t i = 0; i < nndf.dataArrays.size(); i++) { addDataArray(new GiftiDataArray(*nndf.dataArrays[i])); } this->encodingForWriting = nndf.encodingForWriting; } /** * Destructor */ GiftiFile::~GiftiFile() { for (uint64_t i = 0; i < this->dataArrays.size(); i++) { delete this->dataArrays[i]; } } /** * compare a file for unit testing (returns true if "within tolerance"). */ bool GiftiFile::compareFileForUnitTesting(const GiftiFile* gf, const float tolerance, AString& messageOut) const { messageOut = ""; if (gf == NULL) { messageOut += "ERROR: File for comparison is not a GiftiFile or subtype.\n"; return false; } const int32_t numLabels = labelTable.getNumberOfLabels(); if (numLabels != gf->labelTable.getNumberOfLabels()) { messageOut += "ERROR: The files contain a different number of labels.\n"; } else { int32_t labelCount = 0; for (int32_t k = 0; k < numLabels; k++) { if (labelTable.getLabel(k) != gf->getLabelTable()->getLabel(k)) { labelCount++; } } if (labelCount > 0) { messageOut += "ERROR: The files have " + AString::number(labelCount) + " different labels.\n"; } } const int32_t numArrays = getNumberOfDataArrays(); if (numArrays != gf->getNumberOfDataArrays()) { messageOut += "ERROR: The files contain a different number of data arrays (data columns)"; } else { for (int32_t i = 0; i < numArrays; i++) { const GiftiDataArray* gdf1 = getDataArray(i); const GiftiDataArray* gdf2 = gf->getDataArray(i); const std::vector dim1 = gdf1->getDimensions(); const std::vector dim2 = gdf2->getDimensions(); if (dim1 != dim2) { messageOut += "ERROR: Data Array " + AString::number(i) + " have a different number of dimensions.\n"; } else { if (gdf1->getDataType() != gdf2->getDataType()) { messageOut += "ERROR: Data Array " + AString::number(i) + " are different data types.\n"; } else if (gdf1->getTotalNumberOfElements() != gdf2->getTotalNumberOfElements()) { messageOut += "ERROR: Data Array " + AString::number(i) + " have a different number of total elements.\n"; } else { const int32_t numElem = gdf1->getTotalNumberOfElements(); int32_t diffCount = 0; switch (gdf1->getDataType()) { case NiftiDataTypeEnum::NIFTI_TYPE_FLOAT32: { const float* p1 = gdf1->getDataPointerFloat(); const float* p2 = gdf2->getDataPointerFloat(); for (int64_t m = 0; m < numElem; m++) { float diff = p1[m] - p2[m]; if (diff < 0.0) diff = -diff; if (diff > tolerance) { diffCount++; } } } break; case NiftiDataTypeEnum::NIFTI_TYPE_INT32: { const int32_t tol = static_cast(tolerance); const int32_t* p1 = gdf1->getDataPointerInt(); const int32_t* p2 = gdf2->getDataPointerInt(); for (int64_t m = 0; m < numElem; m++) { float diff = p1[m] - p2[m]; if (diff < 0.0) diff = -diff; if (diff > tol) { diffCount++; } } } break; case NiftiDataTypeEnum::NIFTI_TYPE_UINT8: { const uint8_t tol = static_cast(tolerance); const uint8_t* p1 = gdf1->getDataPointerUByte(); const uint8_t* p2 = gdf2->getDataPointerUByte(); for (int64_t m = 0; m < numElem; m++) { float diff = p1[m] - p2[m]; if (diff < 0.0) diff = -diff; if (diff > tol) { diffCount++; } } } break; default: CaretAssertMessage(0, "Invalid Data Type"); break; } if (diffCount > 0) { messageOut += "ERROR: There are " + AString::number(diffCount) + " elements with a difference that are greater than " + AString::number(tolerance) + " in data array " + AString::number(i) + ".\n"; } } } } } return (messageOut.isEmpty()); } /** * Set the name of a data array. */ void GiftiFile::setDataArrayName(const int32_t arrayIndex, const AString& name) { CaretAssertVectorIndex(this->dataArrays, arrayIndex); dataArrays[arrayIndex]->getMetaData()->set(GiftiXmlElements::TAG_METADATA_NAME, name); setModified(); } /** * returns true if the file is isEmpty() (contains no data). */ bool GiftiFile::isEmpty() const { return dataArrays.empty(); } /** * get the data array with the specified name. */ GiftiDataArray* GiftiFile::getDataArrayWithName(const AString& n) { for (int32_t i = 0; i < getNumberOfDataArrays(); i++) { if (getDataArrayName(i) == n) { return getDataArray(i); } } return NULL; } /** * get the data array with the specified name. */ const GiftiDataArray* GiftiFile::getDataArrayWithName(const AString& n) const { for (int32_t i = 0; i < getNumberOfDataArrays(); i++) { if (getDataArrayName(i) == n) { return getDataArray(i); } } return NULL; } /** * Get the data array index for an array with the specified name. If the * name is not found a negative number is returned. */ int GiftiFile::getDataArrayWithNameIndex(const AString& n) const { for (int32_t i = 0; i < getNumberOfDataArrays(); i++) { if (getDataArrayName(i) == n) { return i; } } return -1; } /** * get the data array of the specified intent. */ GiftiDataArray* GiftiFile::getDataArrayWithIntent(const NiftiIntentEnum::Enum intent) { for (int32_t i = 0; i < getNumberOfDataArrays(); i++) { GiftiDataArray* gda = getDataArray(i); if (gda->getIntent() == intent) { return gda; } } return NULL; } /** * get the data array of the specified intent (const method). */ const GiftiDataArray* GiftiFile::getDataArrayWithIntent(const NiftiIntentEnum::Enum intent) const { for (int32_t i = 0; i < getNumberOfDataArrays(); i++) { const GiftiDataArray* gda = getDataArray(i); if (gda->getIntent() == intent) { return gda; } } return NULL; } /** * get the index of the data array of the specified intent. */ int32_t GiftiFile::getDataArrayWithIntentIndex(const NiftiIntentEnum::Enum intent) const { for (int32_t i = 0; i < getNumberOfDataArrays(); i++) { const GiftiDataArray* gda = getDataArray(i); if (gda->getIntent() == intent) { return i; } } return -1; } /** * Get the name for a data array. */ AString GiftiFile::getDataArrayName(const int32_t arrayIndex) const { CaretAssertVectorIndex(this->dataArrays, arrayIndex); AString s = dataArrays[arrayIndex]->getMetaData()->get(GiftiXmlElements::TAG_METADATA_NAME); return s; } /** * Set the comment for a data array. */ void GiftiFile::setDataArrayComment(const int32_t arrayIndex, const AString& comm) { CaretAssertVectorIndex(this->dataArrays, arrayIndex); dataArrays[arrayIndex]->getMetaData()->set(GiftiMetaDataXmlElements::METADATA_NAME_COMMENT, comm); setModified(); } /** * Append to the comment for a data array. */ void GiftiFile::appendToDataArrayComment(const int32_t arrayIndex, const AString& comm) { CaretAssertVectorIndex(this->dataArrays, arrayIndex); if (comm.isEmpty() == false) { AString s(getDataArrayComment(arrayIndex)); s.append(comm); setDataArrayComment(arrayIndex, s); setModified(); } } /** * Prepend to the comment for a data array. */ void GiftiFile::prependToDataArrayComment(const int32_t arrayIndex, const AString& comm) { CaretAssertVectorIndex(this->dataArrays, arrayIndex); if (comm.isEmpty() == false) { AString s(comm); s.append(getDataArrayComment(arrayIndex)); setDataArrayComment(arrayIndex, s); setModified(); } } /** * Get the comment for a data array. */ AString GiftiFile::getDataArrayComment(const int32_t arrayIndex) const { CaretAssertVectorIndex(this->dataArrays, arrayIndex); AString s; (void)dataArrays[arrayIndex]->getMetaData()->get(GiftiMetaDataXmlElements::METADATA_NAME_COMMENT); return s; } /** * get the default data array intent. */ void GiftiFile::setDefaultDataArrayIntent(const NiftiIntentEnum::Enum newIntent) { defaultDataArrayIntent = newIntent; setModified(); } /** * */ void GiftiFile::clear() { DataFile::clear(); for (std::size_t i = 0; i < dataArrays.size(); i++) { if (dataArrays[i] != NULL) { delete dataArrays[i]; dataArrays[i] = NULL; } } dataArrays.clear(); labelTable.clear(); metaData.clear(); // do not clear // giftiElementName // requiredArrayTypeDataTypes } void GiftiFile::clearAndKeepMetadata() {//same as above, minus metaData.clear() DataFile::clear(); for (std::size_t i = 0; i < dataArrays.size(); i++) { if (dataArrays[i] != NULL) { delete dataArrays[i]; dataArrays[i] = NULL; } } dataArrays.clear(); labelTable.clear(); } /** * get all of the data array names. */ void GiftiFile::getAllArrayNames(std::vector& names) const { names.clear(); for (int32_t i = 0; i < getNumberOfDataArrays(); i++) { names.push_back(getDataArrayName(i)); } } /** * check for data arrays with the same name (returns true if there are any). */ bool GiftiFile::checkForDataArraysWithSameName(std::vector& multipleArrayNames) const { multipleArrayNames.clear(); const int32_t numArrays = getNumberOfDataArrays(); if (numArrays > 0) { std::set badNames; for (int32_t i = 0; i < (numArrays - 1); i++) { for (int32_t j = i + 1; j < numArrays; j++) { if (getDataArrayName(i) == getDataArrayName(j)) { badNames.insert(getDataArrayName(i)); } } } if (badNames.empty() == false) { multipleArrayNames.insert(multipleArrayNames.begin(), badNames.begin(), badNames.end()); } } return (multipleArrayNames.size() > 0); } /** * add a data array. */ void GiftiFile::addDataArray(GiftiDataArray* gda) { CaretAssert(gda); // const AString uuid = gda->getMetaData()->getUniqueID(); // const int32_t numDataArrays = this->getNumberOfDataArrays(); // for (int32_t i = 0; i < numDataArrays; i++) { // const AString daUuid = this->getDataArray(i)->getMetaData()->getUniqueID(); // if (daUuid == uuid) { // CaretLogWarning("File " // + this->getFileNameNoPath() // + " contains multiple data arrays with the same unique identifier. " // "Unique ID has been modified to ensure uniqueness within file."); // const bool modStatus = gda->isModified(); // gda->getMetaData()->resetUniqueIdentifier(); // if (modStatus == false) { // gda->clearModified(); // } // } // } dataArrays.push_back(gda); setModified(); } /** * append a gifti array data file to this one. */ void GiftiFile::append(const GiftiFile& gf) { const bool copyDataArrayFlag = true; const int32_t numArrays = gf.getNumberOfDataArrays(); if (numArrays <= 0) { return; } // // Replace filename if "this" file is isEmpty() // if (getNumberOfDataArrays() == 0) { setFileName(gf.getFileName()); } /* * Append metadata. */ this->metaData.append(*gf.getMetaData()); /* * Updates for label table. */ std::map labelIndexConverter = this->labelTable.append(*gf.getLabelTable()); // // Append the data arrays // for (int32_t i = 0; i < gf.getNumberOfDataArrays(); i++) { GiftiDataArray* gda = gf.dataArrays[i]; if (copyDataArrayFlag) { gda = new GiftiDataArray(*(gf.dataArrays[i])); } if (gda->getIntent() == NiftiIntentEnum::NIFTI_INTENT_LABEL) { gda->transferLabelIndices(labelIndexConverter); } this->addDataArray(gda); } setModified(); } /** * append array data file to this one but selectively load/overwrite arrays * indexDestination is where naf's data arrays should be (-1=new, -2=do not load). * "indexDestination" will be updated with the columns actually used. * @param gf * GIFTI file that is appended. * @param indexDestination * How arrays are added/replaced. */ void GiftiFile::append(const GiftiFile& gf, std::vector& indexDestination) { const bool copyDataArrayFlag = true; const int32_t numArrays = gf.getNumberOfDataArrays(); if (numArrays <= 0) { return; } /* * Append metadata. */ this->metaData.append(*gf.getMetaData()); /* * Updates for label table. */ std::map labelIndexConverter = this->labelTable.append(*gf.getLabelTable()); // // Replace file name if this file is isEmpty() // if (getNumberOfDataArrays() == 0) { setFileName(gf.getFileName()); } // // append the data arrays // for (int32_t i = 0; i < numArrays; i++) { const int32_t arrayIndex = indexDestination[i]; if (arrayIndex != 0) { GiftiDataArray* gda = gf.dataArrays[i]; if (copyDataArrayFlag) { gda = new GiftiDataArray(*gda); if (gda->getIntent() == NiftiIntentEnum::NIFTI_INTENT_LABEL) { gda->transferLabelIndices(labelIndexConverter); } this->addDataArray(gda); } // // Replacing existing array ? // if (arrayIndex >= 0) { delete dataArrays[arrayIndex]; dataArrays[arrayIndex] = gda; } // // create new array // else if (arrayIndex == -1) { dataArrays.push_back(gda); // // Lets others know where the array was placed // indexDestination[i] = getNumberOfDataArrays() - 1; } } // // Ignore array // else { // nothing } } this->appendToFileComment(gf.getFileComment()); setModified(); } AString GiftiFile::getFileComment() const { return this->metaData.get(GiftiMetaDataXmlElements::METADATA_NAME_COMMENT); } void GiftiFile::setFileComment(const AString& comment) { if (comment.isEmpty() == false) { AString s = this->getFileName(); s += "\n" + comment; this->setFileComment(s); } } void GiftiFile::appendToFileComment(const AString& comment) { this->metaData.set(GiftiMetaDataXmlElements::METADATA_NAME_COMMENT, comment); } /** * append helper for files where data are label indices. * The table "oldIndicesToNewIndicesTable" maps label indices from * "naf" label indices to the proper indices for "this" file. * void GiftiFile::appendLabelDataHelper(const GiftiFile& naf, const std::vector& arrayWillBeAppended, std::vector& oldIndicesToNewIndicesTable) { oldIndicesToNewIndicesTable.clear(); if ((dataAreIndicesIntoLabelTable == false) || (naf.dataAreIndicesIntoLabelTable == false)) { return; } const int32_t numArrays = naf.getNumberOfDataArrays(); if (numArrays != static_cast(arrayWillBeAppended.size())) { return; } const GiftiLabelTable* nltNew = naf.getLabelTable(); const int32_t numLabelsNew = nltNew->getNumberOfLabels(); if (numLabelsNew <= 0) { return; } oldIndicesToNewIndicesTable.resize(numLabelsNew, -1); // // Determine which labels will be appended // for (int32_t i = 0; i < numArrays; i++) { GiftiDataArray* nda = naf.dataArrays[i]; if (nda->getDataType() == NiftiDataType::NIFTI_TYPE_INT32) { const int32_t numElem = nda->getTotalNumberOfElements(); if (numElem >= 0) { int32_t* dataPtr = nda->getDataPointerInt(); for (int32_t i = 0; i < numElem; i++) { const int32_t indx = dataPtr[i]; if ((indx >= 0) && (indx < numLabelsNew)) { oldIndicesToNewIndicesTable[indx] = -2; } else { std::cout << "WARNING Invalid label index set to zero: " << indx << " of " << numLabelsNew << " labels." << std::endl; dataPtr[i] = 0; } } } } } // // remap the label indices // GiftiLabelTable* myLabelTable = getLabelTable(); for (int32_t i = 0; i < numLabelsNew; i++) { if (oldIndicesToNewIndicesTable[i] == -2) { int32_t indx = myLabelTable->addLabel(nltNew->getLabel(i)); oldIndicesToNewIndicesTable[i] = indx; nltNew->g unsigned char r, g, b, a; nltNew->getColor(i, r, g, b, a); myLabelTable->setColor(indx, r, g, b, a); } } } */ /** * add rows to this file. */ void GiftiFile::addRows(const int32_t numberOfRowsToAdd) { for (int32_t i = 0; i < getNumberOfDataArrays(); i++) { dataArrays[i]->addRows(numberOfRowsToAdd); } setModified(); } /** * reset a data array. */ void GiftiFile::resetDataArray(const int32_t arrayIndex) { CaretAssertVectorIndex(this->dataArrays, arrayIndex); dataArrays[arrayIndex]->zeroize(); } /** * remove a data array. */ void GiftiFile::removeDataArray(const int32_t arrayIndex) { CaretAssertVectorIndex(this->dataArrays, arrayIndex); int32_t numArrays = getNumberOfDataArrays(); if ((arrayIndex >= 0) && (arrayIndex < numArrays)) { delete dataArrays[arrayIndex]; for (int32_t i = arrayIndex; i < (numArrays - 1); i++) { dataArrays[i] = dataArrays[i + 1]; } dataArrays.resize(numArrays - 1); } } /** * remove a data array. */ void GiftiFile::removeDataArray(const GiftiDataArray* arrayPointer) { CaretAssert(arrayPointer); for (int32_t i = 0; i < getNumberOfDataArrays(); i++) { if (getDataArray(i) == arrayPointer) { removeDataArray(i); break; } } } /** * read the file. */ void GiftiFile::readFile(const AString& filename) { this->clear(); this->setFileName(filename); GiftiFileSaxReader saxReader(this); std::auto_ptr parser(XmlSaxParser::createXmlParser()); try { parser->parseFile(filename, &saxReader); } catch (const XmlSaxParserException& e) { clear(); this->setFileName(""); int lineNum = e.getLineNumber(); int colNum = e.getColumnNumber(); std::ostringstream str; str << "Parse error while reading"; if ((lineNum >= 0) && (colNum >= 0)) { str << ", line/col (" << e.getLineNumber() << "/" << e.getColumnNumber() << ")"; } str << ": " << e.whatString().toStdString(); throw DataFileException(filename, AString::fromStdString(str.str())); } /* * If any maps are missing names, give them default names. */ const int32_t numArrays = getNumberOfDataArrays(); for (int32_t i = 0; i < numArrays; i++) { AString arrayName = getDataArrayName(i); if (arrayName.isEmpty()) { arrayName = ("#" + AString::number(i + 1)); setDataArrayName(i, arrayName); } } } /** * write the file. */ void GiftiFile::writeFile(const AString& filename) { try { this->setFileName(filename); FileInformation fileInfo(filename); if (fileInfo.exists()) { //if (GiftiDataArrayFile.isFileOverwriteAllowed() == false) { // throw new GiftiException( // "Overwriting of existing files is currently prohibited"); //} } // // Create a GIFTI Data Array File Writer // GiftiFileWriter giftiFileWriter(filename, this->encodingForWriting); // // Start writing the file // int numberOfDataArrays = this->getNumberOfDataArrays(); giftiFileWriter.start(numberOfDataArrays, &this->metaData, &this->labelTable); // // Write the data arrays // for (int i = 0; i < numberOfDataArrays; i++) { giftiFileWriter.writeDataArray(this->getDataArray(i)); } // // Finish writing the file // giftiFileWriter.finish(); } catch (const GiftiException& e) { throw DataFileException(filename, e.whatString()); } this->clearModified(); /* // // Get how the array data should be encoded // GiftiDataArray::ENCODING encoding = GiftiDataArray::ENCODING_INTERNAL_ASCII; switch (getFileWriteType()) { case FILE_FORMAT_ASCII: break; case FILE_FORMAT_BINARY: break; case FILE_FORMAT_XML: encoding = GiftiDataArray::ENCODING_INTERNAL_ASCII; break; case FILE_FORMAT_XML_BASE64: encoding = GiftiDataArray::ENCODING_INTERNAL_BASE64_BINARY; break; case FILE_FORMAT_XML_GZIP_BASE64: encoding = GiftiDataArray::ENCODING_INTERNAL_COMPRESSED_BASE64_BINARY; break; case FILE_FORMAT_XML_EXTERNAL_BINARY: encoding = GiftiDataArray::ENCODING_EXTERNAL_FILE_BINARY; break; case FILE_FORMAT_OTHER: break; case FILE_FORMAT_COMMA_SEPARATED_VALUE_FILE: break; } AString giftiFileVersionString = AString::number(GiftiFile::getCurrentFileVersion(), 'f', 6); while (giftiFileVersionString.endsWith("00")) { giftiFileVersionString.resize(giftiFileVersionString.size() - 1); } stream << "" << "\n"; stream << "" << "\n"; stream << "<" << GiftiCommon::tagGIFTI << "\n" << " xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n" << " xsi:noNamespaceSchemaLocation=\"http://brainvis.wustl.edu/caret6/xml_schemas/GIFTI_Caret.xsd\"\n" << " " << GiftiCommon::attVersion << "=\"" << giftiFileVersionString << "\"\n" << " " << GiftiCommon::attNumberOfDataArrays << "=\"" << getNumberOfDataArrays() << "\"" << ">" << "\n"; int32_t indent = 0; // // External binary file. // AString externalBinaryFileName; int64_t externalBinaryFileDataOffset = 0; std::ofstream* externalBinaryOutputStream = NULL; if (encoding == GiftiDataArray::ENCODING_EXTERNAL_FILE_BINARY) { externalBinaryFileName = getFileNameNoPath() + ".data"; externalBinaryOutputStream = new std::ofstream(externalBinaryFileName.toAscii().constData(), std::ofstream::binary); if (externalBinaryOutputStream == NULL) { throw GiftiException("Unable to open " + externalBinaryFileName + " for output."); } } #ifdef CARET_FLAG // // copy the Abstract File header into this file's metadata // metaData.clear(); AbstractFileHeaderContainer::iterator iter; for (iter = header.begin(); iter != header.end(); iter++) { // // Get the tag and its value // const AString tag(iter->first); const AString value(iter->second); metaData.set(tag,value); } #endif // CARET_FLAG indent++; metaData.writeAsXML(stream, indent); indent--; // // Write the labels // indent++; labelTable.writeAsXML(stream, indent); indent--; indent++; for (unsigned int32_t i = 0; i < dataArrays.size(); i++) { #ifdef CARET_FLAG dataArrays[i]->setEncoding(encoding); #endif // CARET_FLAG if (externalBinaryOutputStream != NULL) { externalBinaryFileDataOffset = externalBinaryOutputStream->tellp(); } dataArrays[i]->setExternalFileInformation(externalBinaryFileName, externalBinaryFileDataOffset); dataArrays[i]->writeAsXML(stream, indent, externalBinaryOutputStream); } indent--; stream << "" << "\n"; if (externalBinaryOutputStream != NULL) { externalBinaryOutputStream->close(); } */ } /** * set the number of nodes for sparse node index files (NIFTI_INTENT_NODE_INDEX). */ void GiftiFile::setNumberOfNodesForSparseNodeIndexFiles(const int32_t numNodes) { numberOfNodesForSparseNodeIndexFile = numNodes; } /** * process NIFTI_INTENT_NODE_INDEX arrays. */ void GiftiFile::procesNiftiIntentNodeIndexArrays() { // // See if there is a node index array // GiftiDataArray* nodeIndexArray = getDataArrayWithIntent(NiftiIntentEnum::NIFTI_INTENT_NODE_INDEX); if (nodeIndexArray != NULL) { // // Make sure node index array is integer type and one dimensional // if (nodeIndexArray->getDataType() != NiftiDataTypeEnum::NIFTI_TYPE_INT32) { throw GiftiException("Data type other than \"int\" not supported for data intent node index."); } if (nodeIndexArray->getNumberOfDimensions() < 1) { throw GiftiException("Dimensions other than one not supported for data intent node index."); } // // Make node index array integer // nodeIndexArray->convertToDataType(NiftiDataTypeEnum::NIFTI_TYPE_INT32); // // Get the node indices // const int32_t numNodeIndices = nodeIndexArray->getDimension(0); if (numNodeIndices <= 0) { throw GiftiException("Dimension is zero for data intent: " + NiftiIntentEnum::toName(NiftiIntentEnum::NIFTI_INTENT_NODE_INDEX)); } const int32_t zeroIndex[2] = { 0, 0 }; const int32_t* indexData = nodeIndexArray->getDataInt32Pointer(zeroIndex); // // Find the true number of nodes // int32_t numNodes = numberOfNodesForSparseNodeIndexFile; if (numNodes <= 0) { int32_t minNodeIndex = 0; nodeIndexArray->getMinMaxValues(minNodeIndex, numNodes); } // // Check each data array // const int32_t numArrays = getNumberOfDataArrays(); for (int32_t i = 0; i < numArrays; i++) { GiftiDataArray* dataArray = getDataArray(i); if (dataArray->getIntent() != NiftiIntentEnum::NIFTI_INTENT_NODE_INDEX) { if (dataArray->getNumberOfDimensions() < 1) { throw GiftiException("Data Array with intent \"" + NiftiIntentEnum::toName(dataArray->getIntent()) + " is not one-dimensional in sparse node file."); } if (dataArray->getDimension(0) != numNodeIndices) { throw GiftiException("Data Array with intent \"" + NiftiIntentEnum::toName(dataArray->getIntent()) + " has a different number of nodes than the NIFTI_INTENT_NODE_INDEX array in the file."); } switch (dataArray->getDataType()) { case NiftiDataTypeEnum::NIFTI_TYPE_FLOAT32: { std::vector dataFloat(numNodes, 0.0); const float* readPtr = dataArray->getDataFloat32Pointer(zeroIndex); for (int32_t m = 0; m < numNodeIndices; m++) { dataFloat[indexData[m]] = readPtr[m]; } std::vector newDim(1, numNodes); dataArray->setDimensions(newDim); for (int32_t n = 0; n < numNodes; n++) { const int32_t indxs[2] = { n, 0 }; dataArray->setDataFloat32(indxs, dataFloat[n]); } } break; case NiftiDataTypeEnum::NIFTI_TYPE_INT32: { std::vector dataInt(numNodes, 0); const int32_t* readPtr = dataArray->getDataInt32Pointer(zeroIndex); for (int32_t m = 0; m < numNodeIndices; m++) { dataInt[indexData[m]] = readPtr[m]; } std::vector newDim(1, numNodes); dataArray->setDimensions(newDim); for (int32_t n = 0; n < numNodes; n++) { const int32_t indxs[2] = { n, 0 }; dataArray->setDataInt32(indxs, dataInt[n]); } } break; case NiftiDataTypeEnum::NIFTI_TYPE_UINT8: { std::vector dataByte(numNodes, 0); const uint8_t* readPtr = dataArray->getDataUInt8Pointer(zeroIndex); for (int32_t m = 0; m < numNodeIndices; m++) { dataByte[indexData[m]] = readPtr[m]; } std::vector newDim(1, numNodes); dataArray->setDimensions(newDim); for (int32_t n = 0; n < numNodes; n++) { const int32_t indxs[2] = { n, 0 }; dataArray->setDataUInt8(indxs, dataByte[n]); } } break; default: CaretAssertMessage(0, "Invalid Data Type"); break; } } } // // Remove the node index array // removeDataArray(nodeIndexArray); } } /** * Set this object as not modified. Object should also * clear the modification status of its children. * */ void GiftiFile::clearModified() { DataFile::clearModified(); metaData.clearModified(); labelTable.clearModified(); for (int i = 0; i < this->getNumberOfDataArrays(); i++) { this->getDataArray(i)->clearModified(); } } /** * Get the modification status. Returns true if this object or * any of its children have been modified. * @return - The modification status. * */ bool GiftiFile::isModified() const { if (DataFile::isModified()) { return true; } if (metaData.isModified()) { return true; } if (labelTable.isModified()) { return true; } for (int i = 0; i < this->getNumberOfDataArrays(); i++) { if (this->getDataArray(i)->isModified()) { return true; } } return false; } /** * Set the encoding for writing the file. * @param encoding * New encoding. */ void GiftiFile::setEncodingForWriting(const GiftiEncodingEnum::Enum encoding) { this->encodingForWriting = encoding; } /** * Validate the data arrays (optional for subclasses). */ void GiftiFile::validateDataArrays() { // nothing } AString GiftiFile::toString() const { std::ostringstream str; str << "Gifti File: " << this->getFileName().toStdString() << std::endl; str << this->metaData.toString().toStdString() << std::endl; str << this->labelTable.toString().toStdString() << std::endl; for (int32_t i = 0; i < this->getNumberOfDataArrays(); i++) { const GiftiDataArray* gda = this->getDataArray(i); str << gda->toString().toStdString() << std::endl; } return AString::fromStdString(str.str()); } connectome-workbench-1.2.3+git41-gc4c6c90/src/Gifti/GiftiFile.h000066400000000000000000000224331300200146000236250ustar00rootroot00000000000000#ifndef __GIFTI_FILE_H__ #define __GIFTI_FILE_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 "CaretAssert.h" #include "DataFile.h" #include "GiftiDataArray.h" #include "GiftiEncodingEnum.h" #include "GiftiLabelTable.h" #include "NiftiEnums.h" #include "TracksModificationInterface.h" namespace caret { /// This abstract class defines some variables and methods used for gifti data array files. /// While this class may be instantiated, it is best subclassed. class GiftiFile : public DataFile { public: /// append array index values enum APPEND_ARRAY_INDEX { APPEND_ARRAY_NEW = -1, APPEND_ARRAY_DO_NOT_LOAD = -2 }; /// constructor GiftiFile(const AString& descriptiveName, const NiftiIntentEnum::Enum defaultDataArrayIntentIn, const NiftiDataTypeEnum::Enum defaultDataTypeIn, const AString& defaultExt, const bool dataAreIndicesIntoLabelTableIn); /// constructor for generic gifti data array file GiftiFile(); // copy constructor GiftiFile(const GiftiFile& nndf); // destructor virtual ~GiftiFile(); // assignment operator GiftiFile& operator=(const GiftiFile& nndf); // add a data array virtual void addDataArray(GiftiDataArray* nda); // add rows to this file. void addRows(const int32_t numberOfRowsToAdd); // append a data array file to this one virtual void append(const GiftiFile& naf); // append a data array file to this one but selectively load/overwrite arraysumns // arrayDestination is where naf's arrays should be (-1=new, -2=do not load) virtual void append(const GiftiFile& naf, std::vector& indexDestinationg); /// compare a file for unit testing (returns true if "within tolerance") virtual bool compareFileForUnitTesting(const GiftiFile* gf, const float tolerance, AString& messageOut) const; // Clear the gifti array data file. virtual void clear(); virtual void clearAndKeepMetadata(); // returns true if the file is isEmpty() (contains no data) virtual bool isEmpty() const; AString getFileComment() const; void setFileComment(const AString& comment); void appendToFileComment(const AString& comment); /// get the number of data arrays int32_t getNumberOfDataArrays() const { return dataArrays.size() ; } /// get a data array GiftiDataArray* getDataArray(const int32_t arrayNumber) { CaretAssertVectorIndex(dataArrays, arrayNumber); return dataArrays[arrayNumber]; } /// get a data array (const method) const GiftiDataArray* getDataArray(const int32_t arrayNumber) const { CaretAssertVectorIndex(dataArrays, arrayNumber); return dataArrays[arrayNumber]; } /// reset a data array virtual void resetDataArray(const int32_t arrayIndex); /// remove a data array virtual void removeDataArray(const GiftiDataArray* arrayPointer); /// remove a data array virtual void removeDataArray(const int32_t arrayIndex); // get all of the data array names void getAllArrayNames(std::vector& names) const; // get the specified data array's name AString getDataArrayName(const int32_t arrayIndex) const; // get the index of the data array with the specified name int32_t getDataArrayWithNameIndex(const AString& n) const; // get the data array with the specified name GiftiDataArray* getDataArrayWithName(const AString& n); // get the data array with the specified name const GiftiDataArray* getDataArrayWithName(const AString& n) const; // get the index of the data array of the specified intent int32_t getDataArrayWithIntentIndex(const NiftiIntentEnum::Enum intent) const; // get the data array of the specified intent GiftiDataArray* getDataArrayWithIntent(const NiftiIntentEnum::Enum intent); // get the data array of the specified intent (const method) const GiftiDataArray* getDataArrayWithIntent(const NiftiIntentEnum::Enum intent) const; // get the comment for a data array AString getDataArrayComment(const int32_t arrayIndex) const; // set the name of a data array void setDataArrayName(const int32_t arrayIndex, const AString& name); // set the comment for a data array void setDataArrayComment(const int32_t arrayIndex, const AString& comm); // append to the comment for a data array void appendToDataArrayComment(const int32_t arrayIndex, const AString& comm); // prepend to the comment for a data array void prependToDataArrayComment(const int32_t arrayIndex, const AString& comm); // check for data arrays with the same name (returns true if there are any) bool checkForDataArraysWithSameName(std::vector& multipleDataArrayNames) const; // get the metadata GiftiMetaData* getMetaData() { return &metaData; } // get the metadata (const method) const GiftiMetaData* getMetaData() const { return &metaData; } /// get the label table GiftiLabelTable* getLabelTable() { return &labelTable; } /// get the label table const GiftiLabelTable* getLabelTable() const { return &labelTable; } /// get the current version for GiftiFiles static float getCurrentFileVersion() { return 1.0; } /// get the default data array intent NiftiIntentEnum::Enum getDefaultDataArrayIntent() const { return defaultDataArrayIntent; } /// get the default data array intent void setDefaultDataArrayIntent(const NiftiIntentEnum::Enum newIntent); /// set the number of nodes for sparse node index files (NIFTI_INTENT_NODE_INDEX) void setNumberOfNodesForSparseNodeIndexFiles(const int32_t numNodes); // read the XML file virtual void readFile(const AString& filename); // write the XML file virtual void writeFile(const AString& filename); bool getReadMetaDataOnlyFlag() const { return false; } /** @return The encoding used to write the file. */ GiftiEncodingEnum::Enum getEncodingForWriting() const { return this->encodingForWriting; } void setEncodingForWriting(const GiftiEncodingEnum::Enum encoding); virtual void clearModified(); virtual bool isModified() const; virtual AString toString() const; protected: // append helper for files where data are label indices //void appendLabelDataHelper(const GiftiFile& naf, // const std::vector& arrayWillBeAppended, // std::vector& oldIndicesToNewIndicesTable); // copy helper void copyHelperGiftiFile(const GiftiFile& nndf); // process NIFTI_INTENT_NODE_INDEX arrays void procesNiftiIntentNodeIndexArrays(); // validate the data arrays (optional for subclasses) virtual void validateDataArrays(); /// the data arrays std::vector dataArrays; /// the label table GiftiLabelTable labelTable; /// the file's metadata GiftiMetaData metaData; GiftiEncodingEnum::Enum encodingForWriting; /// the default data type NiftiDataTypeEnum::Enum defaultDataType; /// default data array intent for this file NiftiIntentEnum::Enum defaultDataArrayIntent; /// data arrays contain indices into label table bool dataAreIndicesIntoLabelTable; AString descriptiveName; AString defaultExtension; /// number of nodes in sparse node index files (NIFTI_INTENT_NODE_INDEX array) int32_t numberOfNodesForSparseNodeIndexFile; /** The default encoding for writing a GIFTI file. */ static GiftiEncodingEnum::Enum defaultEncodingForWriting; /*!!!! be sure to update copyHelperGiftiFile if new member added !!!!*/ // // friends // }; #ifdef __GIFTI_FILE_MAIN__ GiftiEncodingEnum::Enum GiftiFile::defaultEncodingForWriting = GiftiEncodingEnum::GZIP_BASE64_BINARY; #endif // __GIFTI_FILE_MAIN__ } // namespace #endif // __GIFTI_FILE_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Gifti/GiftiFileSaxReader.cxx000066400000000000000000000515721300200146000260050ustar00rootroot00000000000000/*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 "CaretLogger.h" #include "FileInformation.h" #include "GiftiEndianEnum.h" #include "GiftiLabel.h" #include "GiftiFile.h" #include "GiftiFileSaxReader.h" #include "GiftiLabelTableSaxReader.h" #include "GiftiMetaDataSaxReader.h" #include "GiftiXmlElements.h" #include "NiftiEnums.h" #include "XmlAttributes.h" #include "XmlException.h" using namespace caret; /** * constructor. */ GiftiFileSaxReader::GiftiFileSaxReader(GiftiFile* giftiFileIn) { this->giftiFile = giftiFileIn; this->state = STATE_NONE; this->stateStack.push(this->state); this->elementText = ""; this->dataArray.grabNew(NULL); this->labelTable = NULL; this->labelTableSaxReader = NULL; this->metaDataSaxReader = NULL; this->dataArrayDataHasBeenRead = false; } /** * destructor. */ GiftiFileSaxReader::~GiftiFileSaxReader() { } /** * start an element. */ void GiftiFileSaxReader::startElement(const AString& namespaceURI, const AString& localName, const AString& qName, const XmlAttributes& attributes) { const STATE previousState = this->state; switch (this->state) { case STATE_NONE: if (qName == GiftiXmlElements::TAG_GIFTI) { this->state = STATE_GIFTI; // // Check version of file being read // const float version = attributes.getValueAsFloat(GiftiXmlElements::ATTRIBUTE_GIFTI_VERSION); if (version > GiftiFile::getCurrentFileVersion()) { std::ostringstream str; str << "File version is " << version << " but this Caret" << " does not support versions newer than " << GiftiFile::getCurrentFileVersion() << ".\n" << "You may need a newer version of Caret."; throw XmlSaxParserException(AString::fromStdString(str.str())); } else if (version < 1.0) { throw XmlSaxParserException( "File version is " + AString::number(version) + " but this Caret" " does not support versions before 1.0"); } } else { std::ostringstream str; str << "Root element is \"" << qName << "\" but should be " << GiftiXmlElements::TAG_GIFTI; throw XmlSaxParserException(AString::fromStdString(str.str())); } break; case STATE_GIFTI: if (qName == GiftiXmlElements::TAG_METADATA) { this->state = STATE_METADATA; this->metaDataSaxReader = new GiftiMetaDataSaxReader(giftiFile->getMetaData()); this->metaDataSaxReader->startElement(namespaceURI, localName, qName, attributes); } else if (qName == GiftiXmlElements::TAG_DATA_ARRAY) { this->state = STATE_DATA_ARRAY; this->createDataArray(attributes); } else if (qName == GiftiXmlElements::TAG_LABEL_TABLE) { this->state = STATE_LABEL_TABLE; this->labelTableSaxReader = new GiftiLabelTableSaxReader(giftiFile->getLabelTable()); this->labelTableSaxReader->startElement(namespaceURI, localName, qName, attributes); } else { std::ostringstream str; str << "Child of " << GiftiXmlElements::TAG_GIFTI << " is \"" << qName << "\" but should be one of \n" << " " << GiftiXmlElements::TAG_METADATA << "\n" << " " << GiftiXmlElements::TAG_DATA_ARRAY << "\n" << " " << GiftiXmlElements::TAG_LABEL_TABLE; throw XmlSaxParserException(AString::fromStdString(str.str())); } break; case STATE_METADATA: this->metaDataSaxReader->startElement(namespaceURI, localName, qName, attributes); break; case STATE_LABEL_TABLE: this->labelTableSaxReader->startElement(namespaceURI, localName, qName, attributes); break; case STATE_DATA_ARRAY: if (qName == GiftiXmlElements::TAG_METADATA) { this->state = STATE_METADATA; this->metaDataSaxReader = new GiftiMetaDataSaxReader(dataArray->getMetaData()); this->metaDataSaxReader->startElement(namespaceURI, localName, qName, attributes); } else if (qName == GiftiXmlElements::TAG_DATA) { this->state = STATE_DATA_ARRAY_DATA; } else if (qName == GiftiXmlElements::TAG_COORDINATE_TRANSFORMATION_MATRIX) { this->state = STATE_DATA_ARRAY_MATRIX; this->dataArray->addMatrix(Matrix4x4()); this->matrix = dataArray->getMatrix(dataArray->getNumberOfMatrices() - 1); } else { std::ostringstream str; str << "Child of " << GiftiXmlElements::TAG_DATA_ARRAY << " is \"" << qName << "\" but should be one of \n" << " " << GiftiXmlElements::TAG_METADATA << "\n" << " " << GiftiXmlElements::TAG_COORDINATE_TRANSFORMATION_MATRIX << "\n" << " " << GiftiXmlElements::TAG_DATA; throw XmlSaxParserException(AString::fromStdString(str.str())); } break; case STATE_DATA_ARRAY_DATA: { std::ostringstream str; str << GiftiXmlElements::TAG_DATA << " has child \"" << qName << "\" but should not have any child nodes"; throw XmlSaxParserException(AString::fromStdString(str.str())); } break; case STATE_DATA_ARRAY_MATRIX: if (qName == GiftiXmlElements::TAG_MATRIX_DATA_SPACE) { this->state = STATE_DATA_ARRAY_MATRIX_DATA_SPACE; } else if (qName == GiftiXmlElements::TAG_MATRIX_TRANSFORMED_SPACE) { this->state = STATE_DATA_ARRAY_MATRIX_TRANSFORMED_SPACE; } else if (qName == GiftiXmlElements::TAG_MATRIX_DATA) { this->state = STATE_DATA_ARRAY_MATRIX_DATA; } else { std::ostringstream str; str << "Child of " << GiftiXmlElements::TAG_COORDINATE_TRANSFORMATION_MATRIX << " is \"" << qName << "\" but should be one of \n" << " " << GiftiXmlElements::TAG_MATRIX_DATA_SPACE << "\n" << " " << GiftiXmlElements::TAG_MATRIX_TRANSFORMED_SPACE << "\n" << " " << GiftiXmlElements::TAG_MATRIX_DATA; throw XmlSaxParserException(AString::fromStdString(str.str())); } break; case STATE_DATA_ARRAY_MATRIX_DATA_SPACE: { std::ostringstream str; str << GiftiXmlElements::TAG_MATRIX_DATA_SPACE << " has child \"" << qName << "\" but should not have any child nodes"; throw XmlSaxParserException(AString::fromStdString(str.str())); } break; case STATE_DATA_ARRAY_MATRIX_TRANSFORMED_SPACE: { std::ostringstream str; str << GiftiXmlElements::TAG_MATRIX_TRANSFORMED_SPACE << " has child \"" << qName << "\" but should not have any child nodes"; throw XmlSaxParserException(AString::fromStdString(str.str())); } break; case STATE_DATA_ARRAY_MATRIX_DATA: { std::ostringstream str; str << GiftiXmlElements::TAG_MATRIX_DATA << " has child \"" << qName << "\" but should not have any child nodes"; throw XmlSaxParserException(AString::fromStdString(str.str())); } break; } // // Save previous state // stateStack.push(previousState); elementText = ""; } /** * end an element. */ void GiftiFileSaxReader::endElement(const AString& namespaceURI, const AString& localName, const AString& qName) { switch (this->state) { case STATE_NONE: break; case STATE_GIFTI: break; case STATE_METADATA: this->metaDataSaxReader->endElement(namespaceURI, localName, qName); if (qName == GiftiXmlElements::TAG_METADATA) { delete this->metaDataSaxReader; this->metaDataSaxReader = NULL; } break; case STATE_LABEL_TABLE: this->labelTableSaxReader->endElement(namespaceURI, localName, qName); if (qName == GiftiXmlElements::TAG_LABEL_TABLE) { delete this->labelTableSaxReader; this->labelTableSaxReader = NULL; } break; case STATE_DATA_ARRAY: if (this->dataArray != NULL) { /* * An external binary file may not have a DATA tag * so this will handle reading in that case. */ if (this->encodingForReadingArrayData == GiftiEncodingEnum::EXTERNAL_FILE_BINARY) { if (this->dataArrayDataHasBeenRead == false) { this->processArrayData(); } } this->giftiFile->addDataArray(this->dataArray.releasePointer()); } else { } break; case STATE_DATA_ARRAY_DATA: this->processArrayData(); break; case STATE_DATA_ARRAY_MATRIX: this->matrix = NULL; break; case STATE_DATA_ARRAY_MATRIX_DATA_SPACE: this->matrix->setDataSpaceName(elementText); break; case STATE_DATA_ARRAY_MATRIX_TRANSFORMED_SPACE: this->matrix->setTransformedSpaceName(elementText); break; case STATE_DATA_ARRAY_MATRIX_DATA: { std::istringstream istr(elementText.toStdString()); double m[4][4]; for (int i = 0; i < 4; i++) { for (int j = 0; j < 4; j++) { istr >> m[i][j]; } } matrix->setMatrix(m); /* * In GIFTI file, matrix is row-major order * but Matrix4x4 is column-major order. */ matrix->transpose(); } break; } // // Clear out for new elements // this->elementText = ""; // // Go to previous state // if (this->stateStack.empty()) { throw XmlSaxParserException("State stack is empty while reading XML NiftDataFile."); } this->state = stateStack.top(); this->stateStack.pop(); } /** * create a data array. */ void GiftiFileSaxReader::createDataArray(const XmlAttributes& attributes) { // // Intent // AString intentName = attributes.getValue(GiftiXmlElements::ATTRIBUTE_DATA_ARRAY_INTENT); if (intentName.isEmpty()) { intentName = attributes.getValue("Intent"); } if (intentName.isEmpty()) { throw XmlSaxParserException( "Required attribute " + GiftiXmlElements::ATTRIBUTE_DATA_ARRAY_INTENT + " not found for DataArray"); } bool intentValid = false; NiftiIntentEnum::Enum intent = NiftiIntentEnum::fromName(intentName, &intentValid); if (intentValid == false) { throw XmlSaxParserException("Intent name invalid: " + intentName); } // // Data type name // const AString dataTypeName = attributes.getValue(GiftiXmlElements::ATTRIBUTE_DATA_ARRAY_DATA_TYPE); if (dataTypeName.isEmpty()) { throw XmlSaxParserException("Required attribute " + GiftiXmlElements::ATTRIBUTE_DATA_ARRAY_DATA_TYPE + " not found for DataArray"); } bool dataTypeNameValid = false; this->dataTypeForReadingArrayData = NiftiDataTypeEnum::fromName(dataTypeName, &dataTypeNameValid); if (dataTypeNameValid == false) { throw XmlSaxParserException("Attribute " + GiftiXmlElements::ATTRIBUTE_DATA_ARRAY_DATA_TYPE + "is invalid: " + dataTypeName); } // // Encoding // const AString encodingName = attributes.getValue(GiftiXmlElements::ATTRIBUTE_DATA_ARRAY_ENCODING); if (encodingName.isEmpty()) { throw XmlSaxParserException("Required attribute " + GiftiXmlElements::ATTRIBUTE_DATA_ARRAY_ENCODING + " not found for DataArray"); } bool validEncoding = false; encodingForReadingArrayData = GiftiEncodingEnum::fromGiftiName(encodingName, &validEncoding); if (validEncoding == false) { throw XmlSaxParserException("Attribute " + GiftiXmlElements::ATTRIBUTE_DATA_ARRAY_ENCODING + "is invalid: " + encodingName); } // // External File Name // this->externalFileNameForReadingData = attributes.getValue(GiftiXmlElements::ATTRIBUTE_DATA_ARRAY_EXTERNAL_FILE_NAME); /* * Update external file with path to XML file */ FileInformation extBinInfo(this->externalFileNameForReadingData); if (extBinInfo.isAbsolute() == false) { FileInformation xmlInfo(this->giftiFile->getFileName()); AString path = xmlInfo.getPathName(); if (path.isEmpty() == false) { FileInformation fi(path, this->externalFileNameForReadingData); AString newName = fi.getAbsoluteFilePath(); if (newName.isEmpty() == false) { this->externalFileNameForReadingData = newName; } } } // // External File Offset // this->externalFileOffsetForReadingData = 0; if (encodingForReadingArrayData == GiftiEncodingEnum::EXTERNAL_FILE_BINARY) { const AString offsetString = attributes.getValue(GiftiXmlElements::ATTRIBUTE_DATA_ARRAY_EXTERNAL_FILE_OFFSET); //if (offsetString.isEmpty() == false) { bool validOffsetFlag = false; this->externalFileOffsetForReadingData = offsetString.toLong(&validOffsetFlag); if (validOffsetFlag == false) { XmlSaxParserException e("File Offset is not an integer (" + offsetString + ")"); CaretLogThrowing(e); throw e; } //} } // // Endian // AString endianAttributeNameForReadingArrayData = attributes.getValue(GiftiXmlElements::ATTRIBUTE_DATA_ARRAY_ENDIAN); if (endianAttributeNameForReadingArrayData.isEmpty()) { throw XmlSaxParserException("Required attribute " + GiftiXmlElements::ATTRIBUTE_DATA_ARRAY_ENDIAN + " not found for DataArray"); } bool endianValid = false; this->endianForReadingArrayData = GiftiEndianEnum::fromGiftiName(endianAttributeNameForReadingArrayData, &endianValid); if (endianValid == false) { throw XmlSaxParserException("Attribute " + GiftiXmlElements::ATTRIBUTE_DATA_ARRAY_ENDIAN + "is invalid: " + endianAttributeNameForReadingArrayData); } // // Dimensions // const AString dimString = attributes.getValue(GiftiXmlElements::ATTRIBUTE_DATA_ARRAY_DIMENSIONALITY); if (dimString.isEmpty()) { throw XmlSaxParserException("Required attribute " + GiftiXmlElements::ATTRIBUTE_DATA_ARRAY_DIMENSIONALITY + " not found for DataArray"); } this->dimensionsForReadingArrayData.clear(); const int numDimensions = dimString.toInt(); for (int i = 0; i < numDimensions; i++) { const AString dimNumString = attributes.getValue(GiftiXmlElements::getAttributeDimension(i)); if (dimNumString.isEmpty()) { throw XmlSaxParserException("Required dimension " + GiftiXmlElements::getAttributeDimension(i) + " not found for DataArray"); } const int dim = dimNumString.toInt(); this->dimensionsForReadingArrayData.push_back(dim); } // // Data Location // /* const AString dataLocationString = attributes.getValue(GiftiXmlElements::attDataLocation); if (dataLocationString.isEmpty()) { errorMessage = "Required attribute " + GiftiXmlElements::attDataLocation + " not found for DataArray"; return false; } bool validDataLocation = false; dataLocationForReadingArrayData = GiftiDataArray::getDataLocationFromName(dataLocationString, &validDataLocation); if (validDataLocation == false) { errorMessage = "Attribute " + GiftiXmlElements::attDataLocation + "is invalid: " + dataLocationString; return false; } if (dataLocationForReadingArrayData == GiftiDataArray::DATA_LOCATION_EXTERNAL) { errorMessage = "External data storage not supported."; return false; } */ // // Subscript order // const AString subscriptOrderString = attributes.getValue(GiftiXmlElements::ATTRIBUTE_DATA_ARRAY_INDEXING_ORDER); if (subscriptOrderString.isEmpty()) { throw XmlSaxParserException("Required attribute " + GiftiXmlElements::ATTRIBUTE_DATA_ARRAY_INDEXING_ORDER + " not found for DataArray"); } bool validArraySubscriptingOrder = false; this->arraySubscriptingOrderForReadingArrayData = GiftiArrayIndexingOrderEnum::fromGiftiName( subscriptOrderString, &validArraySubscriptingOrder); if (validArraySubscriptingOrder == false) { throw XmlSaxParserException("Attribute " + GiftiXmlElements::ATTRIBUTE_DATA_ARRAY_INDEXING_ORDER + "is invalid: " + subscriptOrderString); } this->dataArray.grabNew(new GiftiDataArray(intent)); /* * Indicate that data has not been read. */ dataArrayDataHasBeenRead = false; } /** * process the array data into numbers. */ void GiftiFileSaxReader::processArrayData() { this->dataArrayDataHasBeenRead = true; CaretAssert(dataArray); try { dataArray->readFromText(elementText, this->endianForReadingArrayData, arraySubscriptingOrderForReadingArrayData, dataTypeForReadingArrayData, dimensionsForReadingArrayData, encodingForReadingArrayData, externalFileNameForReadingData, externalFileOffsetForReadingData, this->giftiFile->getReadMetaDataOnlyFlag()); } catch (const GiftiException& e) { throw XmlSaxParserException(e.whatString()); } } /** * get characters in an element. */ void GiftiFileSaxReader::characters(const char* ch) { if (this->metaDataSaxReader != NULL) { this->metaDataSaxReader->characters(ch); } else if (this->labelTableSaxReader != NULL) { this->labelTableSaxReader->characters(ch); } else { elementText += ch; } } /** * a fatal error occurs. */ void GiftiFileSaxReader::fatalError(const XmlSaxParserException& e) { /* std::ostringstream str; str << "Fatal Error at line number: " << e.getLineNumber() << "\n" << "Column number: " << e.getColumnNumber() << "\n" << "Message: " << e.whatString(); if (errorMessage.isEmpty() == false) { str << "\n" << errorMessage; } errorMessage = str.str(); */ // // Stop parsing // throw e; } // a warning occurs void GiftiFileSaxReader::warning(const XmlSaxParserException& e) { CaretLogWarning("XML Parser Warning: " + e.whatString()); } // an error occurs void GiftiFileSaxReader::error(const XmlSaxParserException& e) { CaretLogSevere("XML Parser Error: " + e.whatString()); throw e; } void GiftiFileSaxReader::startDocument() { } void GiftiFileSaxReader::endDocument() { } connectome-workbench-1.2.3+git41-gc4c6c90/src/Gifti/GiftiFileSaxReader.h000066400000000000000000000124131300200146000254210ustar00rootroot00000000000000 #ifndef __GIFTI_FILE_SAX_READER_H__ #define __GIFTI_FILE_SAX_READER_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 "CaretPointer.h" #include "GiftiArrayIndexingOrderEnum.h" #include "GiftiEndianEnum.h" #include "GiftiEncodingEnum.h" #include "NiftiEnums.h" #include "XmlSaxParserException.h" #include "XmlSaxParserHandlerInterface.h" namespace caret { class GiftiDataArray; class GiftiFile; class GiftiLabelTableSaxReader; class GiftiMetaDataSaxReader; class GiftiFile; class Matrix4x4; class XmlAttributes; class XmlException; /// class for reading a GIFTI Node Data File with a SAX Parser class GiftiFileSaxReader : public CaretObject, public XmlSaxParserHandlerInterface { public: GiftiFileSaxReader(GiftiFile* giftiFileIn); virtual ~GiftiFileSaxReader(); void startElement(const AString& namespaceURI, const AString& localName, const AString& qName, const XmlAttributes& attributes); void endElement(const AString& namspaceURI, const AString& localName, const AString& qName); void characters(const char* ch); void fatalError(const XmlSaxParserException& e); void warning(const XmlSaxParserException& e); void error(const XmlSaxParserException& e); void startDocument(); void endDocument(); protected: /// file reading states enum STATE { /// no state STATE_NONE, /// processing GIFTI tag STATE_GIFTI, /// processing MetaData tag STATE_METADATA, /// processing LabelTable tag STATE_LABEL_TABLE, /// processing DataArray tag STATE_DATA_ARRAY, /// processing DataArray Data tag STATE_DATA_ARRAY_DATA, /// processing DataArray Matrix Tag STATE_DATA_ARRAY_MATRIX, /// processing DataArray Matrix Data Space Tag STATE_DATA_ARRAY_MATRIX_DATA_SPACE, /// processing DataArray Matrix Transformed Space Tag STATE_DATA_ARRAY_MATRIX_TRANSFORMED_SPACE, /// processing DataArray Matrix Data Tag STATE_DATA_ARRAY_MATRIX_DATA }; // process the array data into numbers void processArrayData(); // create a data array void createDataArray(const XmlAttributes& attributes); /// file reading state STATE state; /// the state stack used when reading a file std::stack stateStack; /// the error message AString errorMessage; /// GIFTI file that is being read GiftiFile* giftiFile; /// element text AString elementText; /// GIFTI data array being read CaretPointer dataArray; /// GIFTI label table being read GiftiLabelTable* labelTable; /// GIFTI label table sax reader GiftiLabelTableSaxReader* labelTableSaxReader; /// GIFTI meta data sax reader GiftiMetaDataSaxReader* metaDataSaxReader; /// GIFTI matrix data being read Matrix4x4* matrix; /// endian attribute data GiftiEndianEnum::Enum endianForReadingArrayData; /// array subscripting order for reading GiftiArrayIndexingOrderEnum::Enum arraySubscriptingOrderForReadingArrayData; /// data type for reading NiftiDataTypeEnum::Enum dataTypeForReadingArrayData; /// dimension for reading std::vector dimensionsForReadingArrayData; /// encoding for reading GiftiEncodingEnum::Enum encodingForReadingArrayData; /// data location for reading //GiftiDataArray::DATA_LOCATION dataLocationForReadingArrayData; /// external file name AString externalFileNameForReadingData; /// external file offset int64_t externalFileOffsetForReadingData; /// tracks if data has been read since external binary may not have DATA tag bool dataArrayDataHasBeenRead; }; } // namespace #endif // __GIFTI_FILE_SAX_READER_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Gifti/GiftiFileWriter.cxx000066400000000000000000000274321300200146000254010ustar00rootroot00000000000000 /*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 #define __GIFTI_FILE_WRITER_DECLARE__ #include "GiftiFileWriter.h" #undef __GIFTI_FILE_WRITER_DECLARE__ #include "FileInformation.h" #include "GiftiDataArray.h" #include "GiftiXmlElements.h" #include "XmlWriter.h" using namespace caret; /** * \class GiftiFileWriter * \brief Writes GIFTI files. * * Writes a GIFTI data array file. In most cases, using the * GiftiDataArrayFile's writeFile() method should be used for writing a file. * This class can be used to incrementally write a file. After constructing * an object of this class, call start(), call writeDataArray() for each * data array, and lastly call finish(). */ /** * Constructor. * * @param filename - name of the file. * @param encoding - encoding of the file. */ GiftiFileWriter::GiftiFileWriter(const AString& filename, const GiftiEncodingEnum::Enum encoding) : CaretObject() { this->xmlFileOutputStream = NULL; this->externalFileOutputStream = NULL; this->maximumExternalFileSize = 1024 * 1024 * 1024; this->filename = filename; this->encoding = encoding; this->xmlWriter = NULL; this->dataArraysWrittenCounter = 0; } /** * Destructor. */ GiftiFileWriter::~GiftiFileWriter() { this->closeFiles(); if (this->xmlWriter != NULL) { delete this->xmlWriter; this->xmlWriter = NULL; } } /** * Start writing the file. * * @param numberOfDataArrays - The number of data arrays that for file * that is being written. * @param metadata - The file's metadata or null if no metadata. * @param labelTable - The file's label table or null if no labels. * @throws GiftiException - If an error occurs. */ void GiftiFileWriter::start(const int numberOfDataArrays, GiftiMetaData* metadata, GiftiLabelTable* labelTable) { this->numberOfDataArrays = numberOfDataArrays; // // Does the file need to be opened? // if (this->xmlFileOutputStream == NULL) { // // Open the file // char* name = this->filename.toCharArray(); this->xmlFileOutputStream = new std::ofstream(name); delete[] name; if (! this->xmlFileOutputStream->good()) { delete this->xmlFileOutputStream; this->xmlFileOutputStream = NULL; AString msg = "Unable to open " + this->filename + " for writing."; throw GiftiException(msg); } // // Remove any existing external files. // this->removeExternalFiles(); } try { // // Format the version string so that it ends with at most one zero // const AString versionString = AString::number(GiftiFile::getCurrentFileVersion()); // // Create the xml writer // this->xmlWriter = new XmlWriter(*this->xmlFileOutputStream); // // Write header info // this->xmlWriter->writeStartDocument("1.0"); //this->xmlWriter.writeDTD(GiftiXmlElements.TAG_GIFTI, // "http://www.nitrc.org/frs/download.php/115/gifti.dtd"); // // Write GIFTI root element // XmlAttributes attributes; attributes.addAttribute("xmlns:xsi", "http://www.w3.org/2001/XMLSchema-instance"); attributes.addAttribute("xsi:noNamespaceSchemaLocation", "http://brainvis.wustl.edu/caret6/xml_schemas/GIFTI_Caret.xsd"); attributes.addAttribute(GiftiXmlElements::ATTRIBUTE_GIFTI_VERSION, versionString); attributes.addAttribute(GiftiXmlElements::ATTRIBUTE_GIFTI_NUMBER_OF_DATA_ARRAYS, this->numberOfDataArrays); this->xmlWriter->writeStartElement(GiftiXmlElements::TAG_GIFTI, attributes); // // Write Metadata // if (metadata != NULL) { metadata->writeAsXML(*this->xmlWriter); } // // Write Labels // if (labelTable != NULL) { labelTable->writeAsXML(*this->xmlWriter); } } catch (const GiftiException& e) { this->closeFiles(); throw e; } } /** * Write a GIFTI Data Array. * * @param gda - The data array. * @throws GiftiException - If an error occurs. */ void GiftiFileWriter::writeDataArray(GiftiDataArray* gda) { this->verifyOpened(); if (this->dataArraysWrittenCounter >= this->numberOfDataArrays) { this->closeFiles(); throw GiftiException("PROGRAMMER ERROR: the number of data arrays " "written exceeds the number of data arrays in the file " "passed to start."); } try { // // // // Get the external file name. // // // String externalFileName = ""; // if (this.encoding == GiftiEncoding.EXTERNAL_FILE_BINARY) { // externalFileName = this.getExternalFileNameForWriting(); // } // // Writing to an external binary data file? // if (this->encoding == GiftiEncodingEnum::EXTERNAL_FILE_BINARY) { if (this->externalFileOutputStream == NULL) { char* name = this->getExternalFileNameForWriting().toCharArray(); this->externalFileOutputStream = new std::ofstream(name,std::fstream::binary); delete[] name; if (! *this->externalFileOutputStream) { this->closeFiles(); const AString msg = ("Unable to open " + this->getExternalFileNameForWriting() + " for writing."); throw GiftiException(msg); } } const int64_t fileOffset = this->externalFileOutputStream->tellp(); FileInformation myInfo(this->getExternalFileNameForWriting());//TODO: get filename only without doing a stat? gda->setExternalFileInformation(myInfo.getFileName(), fileOffset); } // // Write data array // gda->writeAsXML(*this->xmlFileOutputStream, this->externalFileOutputStream, this->encoding); // // Increment counter of data arrays written // this->dataArraysWrittenCounter++; } catch (const GiftiException& e) { this->closeFiles(); throw e; } catch (const XmlException& e) { this->closeFiles(); throw GiftiException(e); } } /** * Finish writing the file. Closes any open files. * @throws GiftiException If file error or number of data arrays written * does not match number of data arrays passed to start() method. */ void GiftiFileWriter::finish() { this->verifyOpened(); try { // // Write GIFTI root end element // this->xmlWriter->writeEndElement(); this->xmlWriter->writeEndDocument(); } catch (const XmlException& e) { this->closeFiles(); throw GiftiException(e); } // // Close the file // this->closeFiles(); } /** * Get the maximum external file size. * * @return The size. */ long GiftiFileWriter::getMaximumExternalFileSize() const { return this->maximumExternalFileSize; } /** * Set the maximum external file size. Starts a new external file when * this size is reached or exceeded. * * @param size Desired maximum size. */ void GiftiFileWriter::setMaximumExternalFileSize(const long size) { this->maximumExternalFileSize = size; } /** * Close any open files. */ void GiftiFileWriter::closeFiles() { if (this->xmlFileOutputStream != NULL) { if (this->xmlFileOutputStream->is_open()) { this->xmlFileOutputStream->close(); } delete this->xmlFileOutputStream; this->xmlFileOutputStream = NULL; } if (this->externalFileOutputStream != NULL) { if (this->externalFileOutputStream->is_open()) { this->externalFileOutputStream->close(); } delete this->externalFileOutputStream; this->externalFileOutputStream = NULL; } } /** * Verify the file is properly opened. * * @throws GiftiException If file was not opened properly. */ void GiftiFileWriter::verifyOpened() { if (this->xmlWriter == NULL) { throw GiftiException("Trying to write to file named \"" + this->filename + "\" but the file was not properly opened " "by calling start() or opening the file " "failed."); } } /** * Remove any existing external data files. * * @throws GiftiException - If unable to delete a file. */ void GiftiFileWriter::removeExternalFiles() { int maxFiles = 50; // more than enough for (int counter = -1; counter <= maxFiles; counter++) { AString name = this->getExternalFileNamePrefix() + AString::number(counter); if (counter < 0) { name = this->getExternalFileNamePrefix(); // old version of ext file } FileInformation fileInfo(name); if (fileInfo.exists()) { if (fileInfo.remove() == false) { throw GiftiException("Unable to delete an existing external " " file named \"" + name + "\"."); } } } } /** * Get the name of the external file prefix. * * @return The XML file name + ".data" */ AString GiftiFileWriter::getExternalFileNamePrefix() const { return this->filename + ".data"; } /** * Get the name of the external file that should be used for writing. * * @return Name of external file. */ AString GiftiFileWriter::getExternalFileNameForWriting() const { return this->getExternalFileNamePrefix(); // COULD use multiple files for external data when large. // boolean done = false; // int counter = 1; // String name = this.getExternalFileNamePrefix() + counter; // while (done == false) { // File file = new File(name); // if (file.exists()) { // if (file.length() > this.maximumExternalFileSize) { // counter++; // name = this.getExternalFileNamePrefix() + counter; // } // else { // done = true; // } // } // else { // done = true; // } // } // // return name; } /** * Get a description of this object's content. * @return String describing this object's content. */ AString GiftiFileWriter::toString() const { return "GiftiFileWriter"; } connectome-workbench-1.2.3+git41-gc4c6c90/src/Gifti/GiftiFileWriter.h000066400000000000000000000061571300200146000250270ustar00rootroot00000000000000#ifndef __GIFTI_FILE_WRITER__H_ #define __GIFTI_FILE_WRITER__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 "CaretObject.h" #include "GiftiFile.h" #include "GiftiEncodingEnum.h" namespace caret { class GiftiDataArray; class GiftiMetaData; class GiftiLabelTable; class XmlWriter; class GiftiFileWriter : public CaretObject { public: GiftiFileWriter(const AString& filename, const GiftiEncodingEnum::Enum encoding); virtual ~GiftiFileWriter(); void start(const int numberOfDataArrays, GiftiMetaData* metadata, GiftiLabelTable* labelTable); void writeDataArray(GiftiDataArray* gda); void finish(); long getMaximumExternalFileSize() const; void setMaximumExternalFileSize(const long size); private: GiftiFileWriter(const GiftiFileWriter&); GiftiFileWriter& operator=(const GiftiFileWriter&); void closeFiles(); void verifyOpened(); void removeExternalFiles(); AString getExternalFileNamePrefix() const; AString getExternalFileNameForWriting() const; public: virtual AString toString() const; private: /** The file output stream for the XML file. */ std::ofstream* xmlFileOutputStream; /** The file output stream for the external data file. */ std::ofstream* externalFileOutputStream; /** The number of data arrays in the file being written. */ int numberOfDataArrays; /** Start a new external file when the external file reaches this size. */ long maximumExternalFileSize; /** name of file to write. */ AString filename; /** encoding of file. */ GiftiEncodingEnum::Enum encoding; /** The XML writer. */ XmlWriter* xmlWriter; /** Counts the number of data arrays that have been written. */ int dataArraysWrittenCounter; }; #ifdef __GIFTI_FILE_WRITER_DECLARE__ // #endif // __GIFTI_FILE_WRITER_DECLARE__ } // namespace #endif //__GIFTI_FILE_WRITER__H_ connectome-workbench-1.2.3+git41-gc4c6c90/src/Gifti/GiftiLabelTableSaxReader.cxx000066400000000000000000000200151300200146000271010ustar00rootroot00000000000000/*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 "CaretLogger.h" #include "GiftiEndianEnum.h" #include "GiftiLabel.h" #include "GiftiFile.h" #include "GiftiLabelTableSaxReader.h" #include "GiftiXmlElements.h" #include "NiftiEnums.h" #include "XmlAttributes.h" #include "XmlException.h" using namespace caret; /** * constructor. */ GiftiLabelTableSaxReader::GiftiLabelTableSaxReader(GiftiLabelTable* labelTable) { this->state = STATE_NONE; this->stateStack.push(state); this->elementText = ""; this->labelTable = labelTable; m_haveUnlabeled = false; } /** * destructor. */ GiftiLabelTableSaxReader::~GiftiLabelTableSaxReader() { } /** * start an element. */ void GiftiLabelTableSaxReader::startElement(const AString& /* namespaceURI */, const AString& /* localName */, const AString& qName, const XmlAttributes& attributes) { const STATE previousState = this->state; switch (this->state) { case STATE_NONE: if (qName == GiftiXmlElements::TAG_LABEL_TABLE) { this->state = STATE_LABEL_TABLE; } else { std::ostringstream str; str << "Root element is \"" << qName.toStdString() << "\" but should be " << GiftiXmlElements::TAG_LABEL_TABLE.toStdString(); throw XmlSaxParserException(AString::fromStdString(str.str())); } break; case STATE_LABEL_TABLE: if (qName == GiftiXmlElements::TAG_LABEL) { this->state = STATE_LABEL_TABLE_LABEL; AString s = attributes.getValue(GiftiXmlElements::ATTRIBUTE_LABEL_KEY); if (s == "") { s = attributes.getValue("Index"); } if (s.isEmpty()) { std::ostringstream str; str << "Tag " << GiftiXmlElements::TAG_LABEL.toStdString() << " is missing its " << GiftiXmlElements::ATTRIBUTE_LABEL_KEY.toStdString(); throw XmlSaxParserException(AString::fromStdString(str.str())); } this->labelIndex = s.toInt(); float defaultRGBA[4]; GiftiLabel::getDefaultColor(defaultRGBA); this->labelRed = defaultRGBA[0]; this->labelGreen = defaultRGBA[1]; this->labelBlue = defaultRGBA[2]; this->labelAlpha = defaultRGBA[3]; const AString redString = attributes.getValue(GiftiXmlElements::ATTRIBUTE_LABEL_RED); if (redString.isEmpty() == false) { this->labelRed = redString.toFloat(); } const AString greenString = attributes.getValue(GiftiXmlElements::ATTRIBUTE_LABEL_GREEN); if (greenString.isEmpty() == false) { this->labelGreen = greenString.toFloat(); } const AString blueString = attributes.getValue(GiftiXmlElements::ATTRIBUTE_LABEL_BLUE); if (blueString.isEmpty() == false) { this->labelBlue = blueString.toFloat(); } const AString alphaString = attributes.getValue(GiftiXmlElements::ATTRIBUTE_LABEL_ALPHA); if (alphaString.isEmpty() == false) { this->labelAlpha = alphaString.toFloat(); } this->labelX = attributes.getValueAsFloat(GiftiXmlElements::ATTRIBUTE_LABEL_X); this->labelY = attributes.getValueAsFloat(GiftiXmlElements::ATTRIBUTE_LABEL_Y); this->labelZ = attributes.getValueAsFloat(GiftiXmlElements::ATTRIBUTE_LABEL_Z); } else { std::ostringstream str; str << "Child of " << GiftiXmlElements::TAG_LABEL_TABLE.toStdString() << " is \"" << qName.toStdString() << "\" but should be " << GiftiXmlElements::TAG_LABEL.toStdString(); throw XmlSaxParserException(AString::fromStdString(str.str())); } break; case STATE_LABEL_TABLE_LABEL: { std::ostringstream str; str << GiftiXmlElements::TAG_LABEL.toStdString() << " has child \"" << qName.toStdString() << "\" but should not have any child nodes"; throw XmlSaxParserException(AString::fromStdString(str.str())); } break; } // // Save previous state // this->stateStack.push(previousState); this->elementText = ""; } /** * end an element. */ void GiftiLabelTableSaxReader::endElement(const AString& /* namspaceURI */, const AString& /* localName */, const AString& /*qName*/) { switch (state) { case STATE_NONE: break; case STATE_LABEL_TABLE: break; case STATE_LABEL_TABLE_LABEL: if ((elementText == "unknown" || elementText == "Unknown") && labelAlpha == 0.0f) { if (!m_haveUnlabeled)//if we already have an unlabeled key, don't change things, and warn { m_haveUnlabeled = true; CaretLogFiner("Using '" + elementText + "' label as unlabeled key"); elementText = "???";//pretend these strings are actually "???" when alpha is 0, as that means it is unlabeled } else { CaretLogWarning("found multiple label elements that should be interpreted as unlabeled"); } } else if (elementText == "???") { if (m_haveUnlabeled) { CaretLogWarning("found multiple label elements that should be interpreted as unlabeled"); } m_haveUnlabeled = true;//that identifier always means unlabeled to us, don't let it slip by without setting m_haveUnlabeled } this->labelTable->setLabel(this->labelIndex, this->elementText, this->labelRed, this->labelGreen, this->labelBlue, this->labelAlpha, this->labelX, this->labelY, this->labelZ); break; } // // Clear out for new elements // this->elementText = ""; // // Go to previous state // if (this->stateStack.empty()) { throw XmlSaxParserException("State stack is empty while reading GiftiLabelTable."); } this->state = stateStack.top(); this->stateStack.pop(); } /** * get characters in an element. */ void GiftiLabelTableSaxReader::characters(const char* ch) { this->elementText += ch; } /** * a fatal error occurs. */ void GiftiLabelTableSaxReader::fatalError(const XmlSaxParserException& e) { throw e; } /** * A warning occurs */ void GiftiLabelTableSaxReader::warning(const XmlSaxParserException& e) { CaretLogWarning("XML Parser Warning: " + e.whatString()); } // an error occurs void GiftiLabelTableSaxReader::error(const XmlSaxParserException& e) { throw e; } void GiftiLabelTableSaxReader::startDocument() { } void GiftiLabelTableSaxReader::endDocument() { } connectome-workbench-1.2.3+git41-gc4c6c90/src/Gifti/GiftiLabelTableSaxReader.h000066400000000000000000000070031300200146000265300ustar00rootroot00000000000000 #ifndef __GIFTI_LABEL_TABLE_SAX_READER_H__ #define __GIFTI_LABEL_TABLE_SAX_READER_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 "XmlSaxParserException.h" #include "XmlSaxParserHandlerInterface.h" namespace caret { class GiftiLabelTable; class XmlAttributes; /** * class for reading a GiftiLabelTable with a SAX Parser */ class GiftiLabelTableSaxReader : public CaretObject, public XmlSaxParserHandlerInterface { public: GiftiLabelTableSaxReader(GiftiLabelTable* labelTable); virtual ~GiftiLabelTableSaxReader(); void startElement(const AString& namespaceURI, const AString& localName, const AString& qName, const XmlAttributes& attributes); void endElement(const AString& namspaceURI, const AString& localName, const AString& qName); void characters(const char* ch); void fatalError(const XmlSaxParserException& e); void warning(const XmlSaxParserException& e); void error(const XmlSaxParserException& e); void startDocument(); void endDocument(); protected: /// file reading states enum STATE { /// no state STATE_NONE, /// processing LabelTable tag STATE_LABEL_TABLE, /// processing LabelTable Label STATE_LABEL_TABLE_LABEL }; /// file reading state STATE state; /// the state stack used when reading a file std::stack stateStack; /// the error message AString errorMessage; /// element text AString elementText; /// GIFTI label table being read GiftiLabelTable* labelTable; /// label index int labelIndex; /// label color component float labelRed; /// label color component float labelGreen; /// label color component float labelBlue; /// label color component float labelAlpha; /// label's X-coordinate float labelX; /// label's Y-coordinate float labelY; /// label's Z-coordinate float labelZ; /// tracks whether we have read an "unused" label, because the GiftiLabelTable constructor and clear() add "???" whether we want it or not bool m_haveUnlabeled; }; } // namespace #endif // __GIFTI_LABEL_TABLE_SAX_READER_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Gifti/GiftiMetaDataSaxReader.cxx000066400000000000000000000144641300200146000266050ustar00rootroot00000000000000/*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 "CaretLogger.h" #include "GiftiMetaData.h" #include "GiftiMetaDataSaxReader.h" #include "GiftiXmlElements.h" #include "XmlAttributes.h" #include "XmlException.h" using namespace caret; /** * constructor. */ GiftiMetaDataSaxReader::GiftiMetaDataSaxReader(GiftiMetaData* metaData) { this->state = STATE_NONE; this->stateStack.push(this->state); this->metaDataName = ""; this->metaDataValue = ""; this->elementText = ""; this->metaData = metaData; } /** * destructor. */ GiftiMetaDataSaxReader::~GiftiMetaDataSaxReader() { } /** * start an element. */ void GiftiMetaDataSaxReader::startElement(const AString& /* namespaceURI */, const AString& /* localName */, const AString& qName, const XmlAttributes& /*attributes*/) { const STATE previousState = this->state; switch (this->state) { case STATE_NONE: if (qName == GiftiXmlElements::TAG_METADATA) { this->state = STATE_METADATA; } else { std::ostringstream str; str << "Root element is \"" << qName.toStdString() << "\" but should be " << GiftiXmlElements::TAG_METADATA.toStdString(); throw XmlSaxParserException(AString::fromStdString(str.str())); } break; case STATE_METADATA: if (qName == GiftiXmlElements::TAG_METADATA_ENTRY) { this->state = STATE_METADATA_MD; } else { std::ostringstream str; str << "Child of " << GiftiXmlElements::TAG_METADATA.toStdString() << " is \"" << qName.toStdString() << "\" but should be " << GiftiXmlElements::TAG_METADATA_ENTRY.toStdString(); throw XmlSaxParserException(AString::fromStdString(str.str())); } break; case STATE_METADATA_MD: if (qName == GiftiXmlElements::TAG_METADATA_NAME) { this->state = STATE_METADATA_MD_NAME; } else if (qName == GiftiXmlElements::TAG_METADATA_VALUE) { this->state = STATE_METADATA_MD_VALUE; } else { std::ostringstream str; str << "Child of " << GiftiXmlElements::TAG_METADATA_ENTRY.toStdString() << " is \"" << qName.toStdString() << "\" but should be one of \n" << " " << GiftiXmlElements::TAG_METADATA_NAME.toStdString() << "\n" << " " << GiftiXmlElements::TAG_METADATA_VALUE.toStdString(); throw XmlSaxParserException(AString::fromStdString(str.str())); } break; case STATE_METADATA_MD_NAME: { std::ostringstream str; str << GiftiXmlElements::TAG_METADATA_NAME.toStdString() << " has child \"" << qName.toStdString() << "\" but should not have any child nodes"; throw XmlSaxParserException(AString::fromStdString(str.str())); } break; case STATE_METADATA_MD_VALUE: { std::ostringstream str; str << GiftiXmlElements::TAG_METADATA_VALUE.toStdString() << " has child \"" << qName.toStdString() << "\" but should not have any child nodes"; throw XmlSaxParserException(AString::fromStdString(str.str())); } break; } // // Save previous state // this->stateStack.push(previousState); this->elementText = ""; } /** * end an element. */ void GiftiMetaDataSaxReader::endElement(const AString& /* namspaceURI */, const AString& /* localName */, const AString& /*qName*/) { switch (state) { case STATE_NONE: break; case STATE_METADATA: break; case STATE_METADATA_MD: if ((this->metaDataName.isEmpty() == false) && (this->metaDataValue.isEmpty() == false)) { if (this->metaData != NULL) { this->metaData->set(metaDataName, metaDataValue); } else { throw XmlSaxParserException("ERROR: Have metadata name/value but no MetaDeta."); } this->metaDataName = ""; this->metaDataValue = ""; } break; case STATE_METADATA_MD_NAME: this->metaDataName = elementText; break; case STATE_METADATA_MD_VALUE: this->metaDataValue = elementText; break; } // // Clear out for new elements // this->elementText = ""; // // Go to previous state // if (this->stateStack.empty()) { throw XmlSaxParserException("State stack is empty while reading XML MetaData."); } this->state = this->stateStack.top(); this->stateStack.pop(); } /** * get characters in an element. */ void GiftiMetaDataSaxReader::characters(const char* ch) { this->elementText += ch; } /** * a fatal error occurs. */ void GiftiMetaDataSaxReader::fatalError(const XmlSaxParserException& e) { // // Stop parsing // CaretLogSevere("XML Parser Fatal Error: " + e.whatString()); throw e; } // a warning occurs void GiftiMetaDataSaxReader::warning(const XmlSaxParserException& e) { CaretLogWarning("XML Parser Warning: " + e.whatString()); } // an error occurs void GiftiMetaDataSaxReader::error(const XmlSaxParserException& e) { throw e; } void GiftiMetaDataSaxReader::startDocument() { } void GiftiMetaDataSaxReader::endDocument() { } connectome-workbench-1.2.3+git41-gc4c6c90/src/Gifti/GiftiMetaDataSaxReader.h000066400000000000000000000064031300200146000262240ustar00rootroot00000000000000 #ifndef __GIFTI_META_DATA_SAX_READER_H__ #define __GIFTI_META_DATA_SAX_READER_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 "XmlSaxParserException.h" #include "XmlSaxParserHandlerInterface.h" namespace caret { class GiftiMetaData; class XmlAttributes; /** * class for reading Gifti Metadata with a SAX Parser */ class GiftiMetaDataSaxReader : public CaretObject, public XmlSaxParserHandlerInterface { public: GiftiMetaDataSaxReader(GiftiMetaData* metaData); virtual ~GiftiMetaDataSaxReader(); void startElement(const AString& namespaceURI, const AString& localName, const AString& qName, const XmlAttributes& attributes); void endElement(const AString& namspaceURI, const AString& localName, const AString& qName); void characters(const char* ch); void fatalError(const XmlSaxParserException& e); void warning(const XmlSaxParserException& e); void error(const XmlSaxParserException& e); void startDocument(); void endDocument(); private: GiftiMetaDataSaxReader(const GiftiMetaDataSaxReader&); GiftiMetaDataSaxReader& operator=(const GiftiMetaDataSaxReader&); protected: /// file reading states enum STATE { /// no state STATE_NONE, /// processing MetaData tag STATE_METADATA, /// processing MetaData MD child tag STATE_METADATA_MD, /// processing MetaData MD Name tag STATE_METADATA_MD_NAME, /// processing MetaData MD Value tag STATE_METADATA_MD_VALUE }; /// file reading state STATE state; /// the state stack used when reading a file std::stack stateStack; /// the error message AString errorMessage; /// meta data name AString metaDataName; /// meta data value AString metaDataValue; /// element text AString elementText; /// GIFTI meta data being read GiftiMetaData* metaData; }; } // namespace #endif // __GIFTI_META_DATA_SAX_READER_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/GuiQt/000077500000000000000000000000001300200146000215755ustar00rootroot00000000000000connectome-workbench-1.2.3+git41-gc4c6c90/src/GuiQt/AboutWorkbenchDialog.cxx000066400000000000000000000141021300200146000263540ustar00rootroot00000000000000 /*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 __ABOUT_WORKBENCH_DIALOG_DECLARE__ #include "AboutWorkbenchDialog.h" #undef __ABOUT_WORKBENCH_DIALOG_DECLARE__ #include #include #include #include #include #include #include #include "ApplicationInformation.h" #include "BrainOpenGLWidget.h" #include "CaretAssert.h" #include "WuQDataEntryDialog.h" #include "WuQtUtilities.h" using namespace caret; /** * \class caret::AboutWorkbenchDialog * \brief Dialog that displays information about workbench. */ /** * Constructor. * * @param openGLParentWidget * The parent OpenGL Widget for which information is provided. */ AboutWorkbenchDialog::AboutWorkbenchDialog(BrainOpenGLWidget* openGLParentWidget) : WuQDialogModal("About Workbench", openGLParentWidget) { m_openGLParentWidget = openGLParentWidget; this->setCancelButtonText(""); ApplicationInformation appInfo; QLabel* imageLabel = NULL; QPixmap pixmap; if (WuQtUtilities::loadPixmap(":/About/hcp-logo.png", pixmap)) { imageLabel = new QLabel(); imageLabel->setPixmap(pixmap); imageLabel->setAlignment(Qt::AlignCenter); } QLabel* workbenchLabel = new QLabel(appInfo.getName()); QFont workbenchFont = workbenchLabel->font(); workbenchFont.setBold(true); workbenchFont.setPointSize(32); workbenchLabel->setFont(workbenchFont); const QString labelStyle = ("QLabel { " " font: 20px bold " "}"); QLabel* hcpWebsiteLabel = new QLabel("" "Visit the Human Connectome Project" ""); hcpWebsiteLabel->setStyleSheet(labelStyle); hcpWebsiteLabel->setAlignment(Qt::AlignCenter); QObject::connect(hcpWebsiteLabel, SIGNAL(linkActivated(const QString&)), this, SLOT(websiteLinkActivated(const QString&))); QLabel* versionLabel = new QLabel("Version " + appInfo.getVersion()); QLabel* copyrightLabel = new QLabel("Copyright " + QString::number(QDate::currentDate().year()) + " Washington University"); m_morePushButton = addUserPushButton("More...", QDialogButtonBox::NoRole); m_openGLPushButton = addUserPushButton("OpenGL...", QDialogButtonBox::NoRole); QWidget* widget = new QWidget(); QVBoxLayout* layout = new QVBoxLayout(widget); WuQtUtilities::setLayoutSpacingAndMargins(layout, 4, 2); if (imageLabel != NULL) { layout->addWidget(imageLabel, 0, Qt::AlignHCenter); layout->addSpacing(15); } layout->addWidget(workbenchLabel, 0, Qt::AlignHCenter); layout->addSpacing(15); layout->addWidget(hcpWebsiteLabel, 0, Qt::AlignHCenter); layout->addSpacing(15); layout->addWidget(versionLabel, 0, Qt::AlignHCenter); layout->addSpacing(15); layout->addWidget(copyrightLabel, 0, Qt::AlignHCenter); layout->addSpacing(15); this->setCentralWidget(widget, WuQDialog::SCROLL_AREA_NEVER); } /** * Destructor. */ AboutWorkbenchDialog::~AboutWorkbenchDialog() { } AboutWorkbenchDialog::DialogUserButtonResult AboutWorkbenchDialog::userButtonPressed(QPushButton* userPushButton) { if (userPushButton == m_openGLPushButton) { displayOpenGLInformation(); } else if (userPushButton == m_morePushButton) { displayMoreInformation(); } else { CaretAssert(0); } return AboutWorkbenchDialog::RESULT_NONE; } void AboutWorkbenchDialog::displayOpenGLInformation() { WuQDataEntryDialog ded("OpenGL Information", this, true); ded.addTextEdit("", m_openGLParentWidget->getOpenGLInformation(), true); ded.resize(600, 600); ded.setCancelButtonText(""); ded.exec(); } void AboutWorkbenchDialog::displayMoreInformation() { ApplicationInformation appInfo; std::vector informationData; appInfo.getAllInformation(informationData); QString styleName("Undefined"); QStyle* appStyle = QApplication::style(); if (appStyle != NULL) { styleName = appStyle->objectName(); } informationData.push_back(QString("Style Name: " + styleName)); WuQDataEntryDialog ded("More " + appInfo.getName() + " Information", this, true); const int32_t numInfo = static_cast(informationData.size()); for (int32_t i = 0; i < numInfo; i++) { ded.addWidget("", new QLabel(informationData[i])); } ded.setCancelButtonText(""); ded.exec(); } /** * Called when a label's hyperlink is selected. * @param link * The URL. */ void AboutWorkbenchDialog::websiteLinkActivated(const QString& link) { if (link.isEmpty() == false) { QDesktopServices::openUrl(QUrl(link)); } } connectome-workbench-1.2.3+git41-gc4c6c90/src/GuiQt/AboutWorkbenchDialog.h000066400000000000000000000037641300200146000260150ustar00rootroot00000000000000#ifndef __ABOUT_WORKBENCH_DIALOG__H_ #define __ABOUT_WORKBENCH_DIALOG__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 "WuQDialogModal.h" class QPushButton; namespace caret { class BrainOpenGLWidget; class AboutWorkbenchDialog : public WuQDialogModal { Q_OBJECT public: AboutWorkbenchDialog(BrainOpenGLWidget* openGLParentWidget); virtual ~AboutWorkbenchDialog(); protected: virtual DialogUserButtonResult userButtonPressed(QPushButton* userPushButton); private slots: void websiteLinkActivated(const QString& link); private: AboutWorkbenchDialog(const AboutWorkbenchDialog&); AboutWorkbenchDialog& operator=(const AboutWorkbenchDialog&); void displayMoreInformation(); void displayOpenGLInformation(); QPushButton* m_morePushButton; QPushButton* m_openGLPushButton; BrainOpenGLWidget* m_openGLParentWidget; }; #ifdef __ABOUT_WORKBENCH_DIALOG_DECLARE__ // #endif // __ABOUT_WORKBENCH_DIALOG_DECLARE__ } // namespace #endif //__ABOUT_WORKBENCH_DIALOG__H_ connectome-workbench-1.2.3+git41-gc4c6c90/src/GuiQt/AnnotationChangeCoordinateDialog.cxx000066400000000000000000000156511300200146000307010ustar00rootroot00000000000000 /*LICENSE_START*/ /* * Copyright (C) 2015 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 __ANNOTATION_CHANGE_COORDINATE_DIALOG_DECLARE__ #include "AnnotationChangeCoordinateDialog.h" #undef __ANNOTATION_CHANGE_COORDINATE_DIALOG_DECLARE__ #include #include #include #include #include "Annotation.h" #include "AnnotationCoordinate.h" #include "AnnotationCoordinateSelectionWidget.h" #include "CaretAssert.h" #include "WuQMessageBox.h" #include "WuQtUtilities.h" using namespace caret; /** * \class caret::AnnotationChangeCoordinateDialog * \brief Dialog for changing an annotation coordinate. * \ingroup GuiQt * * When the user changes a coordinate it may be desirable to * change the coordinate space such as moving from one tab * to another tab. */ /** * Constructor. */ AnnotationChangeCoordinateDialog::AnnotationChangeCoordinateDialog(const AnnotationCoordinateInformation& coordInfo, Annotation* annotation, AnnotationCoordinate* coordinate, AnnotationCoordinate* secondCoordinate, QWidget* parent) : WuQDialogModal("Change Coordinate", parent), m_coordInfo(coordInfo), m_annotation(annotation), m_coordinate(coordinate), m_secondCoordinate(secondCoordinate) { CaretAssert(annotation); CaretAssert(coordinate); QWidget* currentCoordinateWidget = createCurrentCoordinateWidget(); QWidget* newCoordinateWidget = createNewCoordinateWidget(); QWidget* widget = new QWidget(); QVBoxLayout* layout = new QVBoxLayout(widget); layout->addWidget(currentCoordinateWidget); layout->addWidget(newCoordinateWidget); setCentralWidget(widget, SCROLL_AREA_NEVER); } /** * Destructor. */ AnnotationChangeCoordinateDialog::~AnnotationChangeCoordinateDialog() { } /** * @return Widget containing new coordinate selection */ QWidget* AnnotationChangeCoordinateDialog::createNewCoordinateWidget() { m_coordinateSelectionWidget = new AnnotationCoordinateSelectionWidget(m_annotation->getType(), m_coordInfo, NULL); m_coordinateSelectionWidget->selectCoordinateSpace(m_annotation->getCoordinateSpace()); QGroupBox* newCoordGroupBox = new QGroupBox("New Coordinate"); QVBoxLayout* newCoordGroupLayout = new QVBoxLayout(newCoordGroupBox); newCoordGroupLayout->setMargin(0); newCoordGroupLayout->addWidget(m_coordinateSelectionWidget); return newCoordGroupBox; } /** * @return Widget containing current coordinate. */ QWidget* AnnotationChangeCoordinateDialog::createCurrentCoordinateWidget() { QString spaceText(AnnotationCoordinateSpaceEnum::toGuiName(m_annotation->getCoordinateSpace()) + " "); bool useXyzFlag = true; switch (m_annotation->getCoordinateSpace()) { case AnnotationCoordinateSpaceEnum::STEREOTAXIC: break; case AnnotationCoordinateSpaceEnum::PIXELS: break; case AnnotationCoordinateSpaceEnum::SURFACE: { useXyzFlag = false; StructureEnum::Enum structure = StructureEnum::INVALID; int32_t numberOfNodes = -1; int32_t nodeIndex = -1; m_coordinate->getSurfaceSpace(structure, numberOfNodes, nodeIndex); spaceText += ("Structure: " + StructureEnum::toGuiName(structure) + " Node Index=" + QString::number(nodeIndex)); } break; case AnnotationCoordinateSpaceEnum::TAB: spaceText += (AString::number(m_annotation->getTabIndex() + 1) + " "); break; case AnnotationCoordinateSpaceEnum::WINDOW: spaceText += (AString::number(m_annotation->getWindowIndex() + 1) + " "); break; } QGroupBox* groupBox = new QGroupBox("Coordinate"); if (useXyzFlag) { float xyz[3]; m_coordinate->getXYZ(xyz); QGridLayout* gridLayout = new QGridLayout(groupBox); int row = gridLayout->rowCount(); gridLayout->addWidget(new QLabel("Space"), row, 0, Qt::AlignHCenter); gridLayout->addWidget(new QLabel("X"), row, 1, Qt::AlignHCenter); gridLayout->addWidget(new QLabel("Y"), row, 2, Qt::AlignHCenter); gridLayout->addWidget(new QLabel("Z"), row, 3, Qt::AlignHCenter); row++; gridLayout->addWidget(new QLabel(spaceText), row, 0); gridLayout->addWidget(new QLabel(QString::number(xyz[0], 'f', 1)), row, 1, Qt::AlignRight); gridLayout->addWidget(new QLabel(QString::number(xyz[1], 'f', 1)), row, 2, Qt::AlignRight); gridLayout->addWidget(new QLabel(QString::number(xyz[2], 'f', 1)), row, 3, Qt::AlignRight); } else { QLabel* spaceLabel = new QLabel(spaceText); QVBoxLayout* layout = new QVBoxLayout(groupBox); layout->addWidget(spaceLabel); } return groupBox; } /** * Called when the OK button is clicked. */ void AnnotationChangeCoordinateDialog::okButtonClicked() { QString errorMessage; if ( ! m_coordinateSelectionWidget->changeAnnotationCoordinate(m_annotation, errorMessage)) { WuQMessageBox::errorOk(this, errorMessage); return; } WuQDialogModal::okButtonClicked(); } connectome-workbench-1.2.3+git41-gc4c6c90/src/GuiQt/AnnotationChangeCoordinateDialog.h000066400000000000000000000051261300200146000303220ustar00rootroot00000000000000#ifndef __ANNOTATION_CHANGE_COORDINATE_DIALOG_H__ #define __ANNOTATION_CHANGE_COORDINATE_DIALOG_H__ /*LICENSE_START*/ /* * Copyright (C) 2015 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 "UserInputModeAnnotations.h" #include "WuQDialogModal.h" namespace caret { class Annotation; class AnnotationCoordinate; class AnnotationCoordinateSelectionWidget; class AnnotationChangeCoordinateDialog : public WuQDialogModal { Q_OBJECT public: AnnotationChangeCoordinateDialog(const AnnotationCoordinateInformation& coordInfo, Annotation* annotation, AnnotationCoordinate* coordinate, AnnotationCoordinate* secondCoordinate, QWidget* parent); virtual ~AnnotationChangeCoordinateDialog(); virtual void okButtonClicked(); // ADD_NEW_METHODS_HERE private: AnnotationChangeCoordinateDialog(const AnnotationChangeCoordinateDialog&); AnnotationChangeCoordinateDialog& operator=(const AnnotationChangeCoordinateDialog&); QWidget* createCurrentCoordinateWidget(); QWidget* createNewCoordinateWidget(); const AnnotationCoordinateInformation& m_coordInfo; Annotation* m_annotation; AnnotationCoordinate* m_coordinate; AnnotationCoordinate* m_secondCoordinate; AnnotationCoordinateSelectionWidget* m_coordinateSelectionWidget; // ADD_NEW_MEMBERS_HERE }; #ifdef __ANNOTATION_CHANGE_COORDINATE_DIALOG_DECLARE__ // #endif // __ANNOTATION_CHANGE_COORDINATE_DIALOG_DECLARE__ } // namespace #endif //__ANNOTATION_CHANGE_COORDINATE_DIALOG_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/GuiQt/AnnotationColorWidget.cxx000066400000000000000000000622011300200146000265770ustar00rootroot00000000000000 /*LICENSE_START*/ /* * Copyright (C) 2015 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 __ANNOTATION_COLOR_WIDGET_DECLARE__ #include "AnnotationColorWidget.h" #undef __ANNOTATION_COLOR_WIDGET_DECLARE__ #include #include #include #include #include #include #include #include "AnnotationManager.h" #include "AnnotationOneDimensionalShape.h" #include "AnnotationTwoDimensionalShape.h" #include "AnnotationRedoUndoCommand.h" #include "Brain.h" #include "BrainOpenGL.h" #include "CaretAssert.h" #include "CaretColorEnumMenu.h" #include "EventGraphicsUpdateAllWindows.h" #include "EventManager.h" #include "GuiManager.h" #include "WuQFactory.h" #include "WuQMessageBox.h" #include "WuQWidgetObjectGroup.h" #include "WuQtUtilities.h" using namespace caret; /** * \class caret::AnnotationColorWidget * \brief Widget for annotation color selection. * \ingroup GuiQt */ /** * Constructor. * * @param parent * Parent for this widget. */ AnnotationColorWidget::AnnotationColorWidget(const AnnotationWidgetParentEnum::Enum parentWidgetType, const int32_t browserWindowIndex, QWidget* parent) : QWidget(parent), m_parentWidgetType(parentWidgetType), m_browserWindowIndex(browserWindowIndex) { QLabel* backFillLabel = new QLabel("Fill"); QLabel* backFillColorLabel = new QLabel("Color"); 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; } const QSize toolButtonSize(16, 16); /* * Background color menu */ m_backgroundColorMenu = new CaretColorEnumMenu((CaretColorEnum::OPTION_INCLUDE_CUSTOM_COLOR | CaretColorEnum::OPTION_INCLUDE_NONE_COLOR)); QObject::connect(m_backgroundColorMenu, SIGNAL(colorSelected(const CaretColorEnum::Enum)), this, SLOT(backgroundColorSelected(const CaretColorEnum::Enum))); /* * Background action and tool button */ m_backgroundColorAction = new QAction("B", this); m_backgroundColorAction->setToolTip("Adjust the fill color"); m_backgroundColorAction->setMenu(m_backgroundColorMenu); m_backgroundToolButton = new QToolButton(); m_backgroundToolButton->setDefaultAction(m_backgroundColorAction); m_backgroundToolButton->setIconSize(toolButtonSize); /* * Widget/object group for background widgets */ m_backgroundWidgetGroup = new WuQWidgetObjectGroup(this); m_backgroundWidgetGroup->add(backFillLabel); m_backgroundWidgetGroup->add(backFillColorLabel); m_backgroundWidgetGroup->add(m_backgroundToolButton); /* * Line color menu */ m_lineColorMenu = new CaretColorEnumMenu((CaretColorEnum::OPTION_INCLUDE_CUSTOM_COLOR | CaretColorEnum::OPTION_INCLUDE_NONE_COLOR)); QObject::connect(m_lineColorMenu, SIGNAL(colorSelected(const CaretColorEnum::Enum)), this, SLOT(lineColorSelected(const CaretColorEnum::Enum))); /* * Line color action and toolbutton */ m_lineColorAction = new QAction("F", this); m_lineColorAction->setToolTip("Adjust the line color"); m_lineColorAction->setMenu(m_lineColorMenu); m_lineToolButton = new QToolButton(); m_lineToolButton->setDefaultAction(m_lineColorAction); m_lineToolButton->setIconSize(toolButtonSize); /* * 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); } QGridLayout* gridLayout = new QGridLayout(this); WuQtUtilities::setLayoutSpacingAndMargins(gridLayout, 2, 0); switch (m_parentWidgetType) { case AnnotationWidgetParentEnum::ANNOTATION_TOOL_BAR_WIDGET: { CaretAssert(lineWidthLabel); gridLayout->addWidget(foreLineLabel, 0, 0, 1, 2, Qt::AlignHCenter); gridLayout->addWidget(lineWidthLabel, 1, 0, Qt::AlignHCenter); gridLayout->addWidget(foreLineColorLabel, 1, 1, Qt::AlignHCenter); gridLayout->addWidget(m_lineThicknessSpinBox, 2, 0, Qt::AlignHCenter); gridLayout->addWidget(m_lineToolButton, 2, 1, Qt::AlignHCenter); gridLayout->addWidget(backFillLabel, 0, 2, Qt::AlignHCenter); gridLayout->addWidget(backFillColorLabel, 1, 2, Qt::AlignHCenter); gridLayout->addWidget(m_backgroundToolButton, 2, 2, Qt::AlignHCenter); } break; case AnnotationWidgetParentEnum::PARENT_ENUM_FOR_LATER_USE: CaretAssert(0); break; } /* * Layout widgets */ backgroundColorSelected(CaretColorEnum::WHITE); lineColorSelected(CaretColorEnum::BLACK); setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); } /** * Destructor. */ AnnotationColorWidget::~AnnotationColorWidget() { } /** * Update with the given annotation. * * @param annotations */ void AnnotationColorWidget::updateContent(std::vector& annotations) { m_annotations = annotations; if ( ! m_annotations.empty()) { setEnabled(true); } else { setEnabled(false); } updateBackgroundColorButton(); updateLineColorButton(); updateLineThicknessSpinBox(); } /** * Gets called when the background color is changed. * * @param caretColor * Color that was selected. */ void AnnotationColorWidget::backgroundColorSelected(const CaretColorEnum::Enum caretColor) { if (! m_annotations.empty()) { float rgba[4]; m_annotations[0]->getCustomBackgroundColor(rgba); if (caretColor == CaretColorEnum::CUSTOM) { QColor color; color.setRgbF(rgba[0], rgba[1], rgba[2]); QColor newColor = QColorDialog::getColor(color, m_backgroundToolButton, "Background Color"); if (newColor.isValid()) { rgba[0] = newColor.redF(); rgba[1] = newColor.greenF(); rgba[2] = newColor.blueF(); switch (m_parentWidgetType) { case AnnotationWidgetParentEnum::ANNOTATION_TOOL_BAR_WIDGET: Annotation::setUserDefaultCustomBackgroundColor(rgba); break; case AnnotationWidgetParentEnum::PARENT_ENUM_FOR_LATER_USE: CaretAssert(0); break; } } } if ( ! isBothColorsSetToNoneAllowed(this, caretColor, m_lineColorMenu->getSelectedColor(), m_annotations)) { return; } AnnotationRedoUndoCommand* undoCommand = new AnnotationRedoUndoCommand(); undoCommand->setModeColorBackground(caretColor, rgba, m_annotations); AnnotationManager* annMan = GuiManager::get()->getBrain()->getAnnotationManager(); AString errorMessage; if ( ! annMan->applyCommand(undoCommand, errorMessage)) { WuQMessageBox::errorOk(this, errorMessage); } Annotation::setUserDefaultBackgroundColor(caretColor); EventManager::get()->sendSimpleEvent(EventTypeEnum::EVENT_ANNOTATION_TOOLBAR_UPDATE); } updateBackgroundColorButton(); EventManager::get()->sendEvent(EventGraphicsUpdateAllWindows().getPointer()); } /** * Update the background color. */ void AnnotationColorWidget::updateBackgroundColorButton() { CaretColorEnum::Enum colorEnum = CaretColorEnum::NONE; float rgba[4]; CaretColorEnum::toRGBFloat(colorEnum, rgba); rgba[3] = 1.0; const int32_t numAnnotations = static_cast(m_annotations.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) { break; } } } } if (allSameColorFlag) { colorEnum = m_annotations[0]->getBackgroundColor(); m_annotations[0]->getBackgroundColorRGBA(rgba); float customRGBA[4]; m_annotations[0]->getCustomBackgroundColor(customRGBA); m_backgroundColorMenu->setCustomIconColor(customRGBA); switch (m_parentWidgetType) { case AnnotationWidgetParentEnum::ANNOTATION_TOOL_BAR_WIDGET: Annotation::setUserDefaultBackgroundColor(colorEnum); Annotation::setUserDefaultCustomBackgroundColor(customRGBA); break; case AnnotationWidgetParentEnum::PARENT_ENUM_FOR_LATER_USE: CaretAssert(0); break; } } m_backgroundWidgetGroup->setEnabled(enableBackgroundFlag); if ( ! enableBackgroundFlag) { colorEnum = CaretColorEnum::NONE; } } QPixmap pm = WuQtUtilities::createCaretColorEnumPixmap(m_backgroundToolButton, 24, 24, colorEnum, rgba, false); QIcon icon(pm); m_backgroundColorAction->setIcon(icon); m_backgroundColorMenu->setSelectedColor(colorEnum); } /** * Update the line color. */ void AnnotationColorWidget::updateLineColorButton() { CaretColorEnum::Enum colorEnum = CaretColorEnum::NONE; float rgba[4]; CaretColorEnum::toRGBFloat(colorEnum, rgba); rgba[3] = 1.0; const int32_t numAnnotations = static_cast(m_annotations.size()); 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); firstColorSupportFlag = false; enableLineFlag = true; } else { float colorRGBA[4]; m_annotations[i]->getLineColorRGBA(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]->getLineColor(); m_annotations[0]->getLineColorRGBA(rgba); float customRGBA[4]; m_annotations[0]->getCustomLineColor(customRGBA); m_lineColorMenu->setCustomIconColor(customRGBA); switch (m_parentWidgetType) { case AnnotationWidgetParentEnum::ANNOTATION_TOOL_BAR_WIDGET: setUserDefaultLineColor(colorEnum, customRGBA); break; case AnnotationWidgetParentEnum::PARENT_ENUM_FOR_LATER_USE: CaretAssert(0); break; } } if ( ! enableLineFlag) { colorEnum = CaretColorEnum::NONE; } } QPixmap pm = WuQtUtilities::createCaretColorEnumPixmap(m_lineToolButton, 24, 24, colorEnum, rgba, true); m_lineColorAction->setIcon(QIcon(pm)); m_lineColorMenu->setSelectedColor(colorEnum); } /** * Tests for both background and line colors both set to none. This is allowed * for some annotations types such as text, which uses a text color, or images. * * @param widget * Widget on which error dialog is displayed. * @param colorOne * One of the background or line colors. * @param colorTwo * The other one of the background or line colors. * @param annotations * The selected annotations. * @return * True if the colors are acceptable, else false (and a an error * message dialog is displayed). */ bool AnnotationColorWidget::isBothColorsSetToNoneAllowed(QWidget* widget, const CaretColorEnum::Enum colorOne, const CaretColorEnum::Enum colorTwo, const std::vector& annotations) { if ((colorOne == CaretColorEnum::NONE) && (colorTwo == CaretColorEnum::NONE)) { bool allowBothColorsNoneFlag = true; for (std::vector::const_iterator iter = annotations.begin(); iter != annotations.end(); iter++) { const Annotation* ann = *iter; CaretAssert(ann); switch (ann->getType()) { case AnnotationTypeEnum::BOX: allowBothColorsNoneFlag = false; break; case AnnotationTypeEnum::COLOR_BAR: break; case AnnotationTypeEnum::IMAGE: break; case AnnotationTypeEnum::LINE: allowBothColorsNoneFlag = false; break; case AnnotationTypeEnum::OVAL: allowBothColorsNoneFlag = false; break; case AnnotationTypeEnum::TEXT: break; } } if ( ! allowBothColorsNoneFlag) { const AString message("Setting both Line and Fill colors to NONE is not allowed for the selected annotation(s)."); WuQMessageBox::errorOk(widget, message); return false; } } return true; } /** * Gets called when the line color is changed. * * @param caretColor * Color that was selected. */ void AnnotationColorWidget::lineColorSelected(const CaretColorEnum::Enum caretColor) { if ( ! m_annotations.empty()) { CaretAssertVectorIndex(m_annotations, 0); float rgba[4]; m_annotations[0]->getCustomLineColor(rgba); if (caretColor == CaretColorEnum::CUSTOM) { QColor color; color.setRgbF(rgba[0], rgba[1], rgba[2]); QColor newColor = QColorDialog::getColor(color, m_backgroundToolButton, "Line Color"); if (newColor.isValid()) { rgba[0] = newColor.redF(); rgba[1] = newColor.greenF(); rgba[2] = newColor.blueF(); } } if ( ! isBothColorsSetToNoneAllowed(this, caretColor, m_backgroundColorMenu->getSelectedColor(), m_annotations)) { return; } AnnotationRedoUndoCommand* undoCommand = new AnnotationRedoUndoCommand(); undoCommand->setModeColorLine(caretColor, rgba, m_annotations); AnnotationManager* annMan = GuiManager::get()->getBrain()->getAnnotationManager(); AString errorMessage; if ( ! annMan->applyCommand(undoCommand, errorMessage)) { WuQMessageBox::errorOk(this, errorMessage); } setUserDefaultLineColor(caretColor, rgba); } updateLineColorButton(); switch (m_parentWidgetType) { case AnnotationWidgetParentEnum::ANNOTATION_TOOL_BAR_WIDGET: EventManager::get()->sendSimpleEvent(EventTypeEnum::EVENT_ANNOTATION_TOOLBAR_UPDATE); break; case AnnotationWidgetParentEnum::PARENT_ENUM_FOR_LATER_USE: break; } EventManager::get()->sendEvent(EventGraphicsUpdateAllWindows().getPointer()); } /** * Set the user default line color. * * @param caretColor * The line color. * @param customRGBA * The custom line color RGBA components. */ void AnnotationColorWidget::setUserDefaultLineColor(const CaretColorEnum::Enum caretColor, const float customRGBA[4]) { if ( ! m_annotations.empty()) { bool allTextFlag = true; bool someTextFlag = false; for (std::vector::iterator iter = m_annotations.begin(); iter != m_annotations.end(); iter++) { const Annotation* ann = *iter; if (ann->getType() == AnnotationTypeEnum::TEXT) { someTextFlag = true; } else { allTextFlag = false; } } /* * Note: Text has its own default line color. Without it, if the * user creates a text annotation it will get a box around it since * other annotations frequently use a line color. */ if (allTextFlag || someTextFlag) { Annotation::setUserDefaultForTextLineColor(caretColor); if (caretColor == CaretColorEnum::CUSTOM) { Annotation::setUserDefaultForTextCustomLineColor(customRGBA); } } if (! allTextFlag) { Annotation::setUserDefaultLineColor(caretColor); if (caretColor == CaretColorEnum::CUSTOM) { Annotation::setUserDefaultCustomLineColor(customRGBA); } } } } /** * Gets called when the line thickness value changes. * * @param value * New value for line thickness. */ void AnnotationColorWidget::lineThicknessSpinBoxValueChanged(double value) { if ( ! m_lineThicknessSpinBox->specialValueText().isEmpty()) { if (m_lineThicknessSpinBox->specialValueText() == m_lineThicknessSpinBox->text()) { /* * Ignore special text which is available when * there are multiple annotations with different * line thicknesses. */ std::cout << "Ignoring special text " << std::endl; return; } } AnnotationRedoUndoCommand* undoCommand = new AnnotationRedoUndoCommand(); undoCommand->setModeLineWidth(value, m_annotations); AnnotationManager* annMan = GuiManager::get()->getBrain()->getAnnotationManager(); AString errorMessage; if ( ! annMan->applyCommand(undoCommand, errorMessage)) { WuQMessageBox::errorOk(this, errorMessage); } EventManager::get()->sendSimpleEvent(EventTypeEnum::EVENT_ANNOTATION_TOOLBAR_UPDATE); Annotation::setUserDefaultLineWidth(value); EventManager::get()->sendEvent(EventGraphicsUpdateAllWindows().getPointer()); } /** * Update the line thickness spin box. */ void AnnotationColorWidget::updateLineThicknessSpinBox() { if (m_lineThicknessSpinBox == NULL) { return; } float lineWidthValue = 1.0; bool lineWidthValid = false; bool haveMultipleLineWidthValues = false; const int32_t numAnnotations = static_cast(m_annotations.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; } } } if (lineWidthValid) { switch (m_parentWidgetType) { case AnnotationWidgetParentEnum::ANNOTATION_TOOL_BAR_WIDGET: Annotation::setUserDefaultLineWidth(lineWidthValue); break; case AnnotationWidgetParentEnum::PARENT_ENUM_FOR_LATER_USE: CaretAssert(0); break; } } } /* * When the selected annotations have different line * widths, the valid displayed is the minimum line * width with a suffix consisting of a plus symbol. */ m_lineThicknessSpinBox->blockSignals(true); m_lineThicknessSpinBox->setValue(lineWidthValue); m_lineThicknessSpinBox->setEnabled(lineWidthValid); if (haveMultipleLineWidthValues) { m_lineThicknessSpinBox->setSuffix("+"); } else { m_lineThicknessSpinBox->setSuffix(""); } m_lineThicknessSpinBox->blockSignals(false); } connectome-workbench-1.2.3+git41-gc4c6c90/src/GuiQt/AnnotationColorWidget.h000066400000000000000000000067201300200146000262300ustar00rootroot00000000000000#ifndef __ANNOTATION_COLOR_WIDGET_H__ #define __ANNOTATION_COLOR_WIDGET_H__ /*LICENSE_START*/ /* * Copyright (C) 2015 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 "AnnotationWidgetParentEnum.h" #include "CaretColorEnum.h" class QAction; class QDoubleSpinBox; class QToolButton; namespace caret { class Annotation; class CaretColorEnumMenu; class WuQWidgetObjectGroup; class AnnotationColorWidget : public QWidget { Q_OBJECT public: AnnotationColorWidget(const AnnotationWidgetParentEnum::Enum parentWidgetType, const int32_t browserWindowIndex, QWidget* parent = 0); virtual ~AnnotationColorWidget(); void updateContent(std::vector& annotations); // ADD_NEW_METHODS_HERE private slots: void backgroundColorSelected(const CaretColorEnum::Enum); void lineColorSelected(const CaretColorEnum::Enum); private slots: void lineThicknessSpinBoxValueChanged(double value); private: AnnotationColorWidget(const AnnotationColorWidget&); AnnotationColorWidget& operator=(const AnnotationColorWidget&); void setUserDefaultLineColor(const CaretColorEnum::Enum caretColor, const float customRGBA[4]); void updateBackgroundColorButton(); void updateLineColorButton(); void updateLineThicknessSpinBox(); static bool isBothColorsSetToNoneAllowed(QWidget* widget, const CaretColorEnum::Enum colorOne, const CaretColorEnum::Enum colorTwo, const std::vector& annotations); const AnnotationWidgetParentEnum::Enum m_parentWidgetType; const int32_t m_browserWindowIndex; QToolButton* m_backgroundToolButton; QToolButton* m_lineToolButton; QAction* m_lineColorAction; QAction* m_backgroundColorAction; CaretColorEnumMenu* m_lineColorMenu; CaretColorEnumMenu* m_backgroundColorMenu; QDoubleSpinBox* m_lineThicknessSpinBox; WuQWidgetObjectGroup* m_backgroundWidgetGroup; std::vector m_annotations; // ADD_NEW_MEMBERS_HERE }; #ifdef __ANNOTATION_COLOR_WIDGET_DECLARE__ // #endif // __ANNOTATION_COLOR_WIDGET_DECLARE__ } // namespace #endif //__ANNOTATION_COLOR_WIDGET_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/GuiQt/AnnotationCoordinateInformation.cxx000066400000000000000000000766321300200146000306670ustar00rootroot00000000000000 /*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 __ANNOTATION_COORDINATE_INFORMATION_DECLARE__ #include "AnnotationCoordinateInformation.h" #undef __ANNOTATION_COORDINATE_INFORMATION_DECLARE__ #include "Annotation.h" #include "AnnotationCoordinate.h" #include "AnnotationOneDimensionalShape.h" #include "AnnotationTwoDimensionalShape.h" #include "BrainOpenGLWidget.h" #include "BrainOpenGLViewportContent.h" #include "BrowserTabContent.h" #include "CaretAssert.h" #include "EventIdentificationRequest.h" #include "EventManager.h" #include "MouseEvent.h" #include "SelectionItemSurfaceNode.h" #include "SelectionItemVoxel.h" #include "SelectionManager.h" #include "Surface.h" using namespace caret; /** * \class caret::AnnotationCoordinateInformation * \brief Contains annotation coordinate types valid for a window coordinate * \ingroup GuiQt * * Annotations support several different coordinate systems. This class * contains validity for each these different coordinate systems. */ /** * Constructor. */ AnnotationCoordinateInformation::AnnotationCoordinateInformation() { reset(); } /** * Destructor. */ AnnotationCoordinateInformation::~AnnotationCoordinateInformation() { } /** * Reset all coordinate information to invalid. */ void AnnotationCoordinateInformation::reset() { m_modelXYZValid = false; m_surfaceNodeValid = false; m_surfaceStructure = StructureEnum::INVALID; m_surfaceNumberOfNodes = 0; m_surfaceNodeIndex = -1; m_surfaceNodeOffset = 0.0; m_surfaceNodeVector = AnnotationSurfaceOffsetVectorTypeEnum::CENTROID_THRU_VERTEX; m_tabIndex = -1; m_tabWidth = 0; m_tabHeight = 0; m_windowIndex = -1; m_windowWidth = 0; m_windowHeight = 0; m_modelXYZ[0] = 0.0; m_modelXYZ[1] = 0.0; m_modelXYZ[2] = 0.0; m_tabXYZ[0] = 0.0; m_tabXYZ[1] = 0.0; m_tabXYZ[2] = 0.0; m_tabPixelXYZ[0] = 0.0; m_tabPixelXYZ[1] = 0.0; m_tabPixelXYZ[2] = 0.0; m_windowXYZ[0] = 0.0; m_windowXYZ[1] = 0.0; m_windowXYZ[2] = 0.0; m_windowPixelXYZ[0] = 0.0; m_windowPixelXYZ[1] = 0.0; m_windowPixelXYZ[2] = 0.0; } bool AnnotationCoordinateInformation::isCoordinateSpaceValid(const AnnotationCoordinateSpaceEnum::Enum space) const { bool validSpaceFlag = false; switch (space) { case AnnotationCoordinateSpaceEnum::PIXELS: CaretAssertMessage(0, "Space should never be pixels"); break; case AnnotationCoordinateSpaceEnum::STEREOTAXIC: validSpaceFlag = m_modelXYZValid; break; case AnnotationCoordinateSpaceEnum::SURFACE: validSpaceFlag = m_surfaceNodeValid; break; case AnnotationCoordinateSpaceEnum::TAB: validSpaceFlag = (m_tabIndex >= 0); break; case AnnotationCoordinateSpaceEnum::WINDOW: validSpaceFlag = (m_windowIndex >= 0); break; } return validSpaceFlag; } /** * Get the valid coordinate spaces for the two annotation coordinate information. * If both coordinates are valid, the space must be valid for both coordinates, AND, if tab or window * space the tab or window indices must also be the same. * * @param coordInfoOne * First coordinate information. * @param coordInfoTwo * Second coordinate information (optional, NULL if not valid). * @param spacesOut * Output containing spaces valid for both. */ void AnnotationCoordinateInformation::getValidCoordinateSpaces(const AnnotationCoordinateInformation* coordInfoOne, const AnnotationCoordinateInformation* coordInfoTwo, std::vector& spacesOut) { CaretAssert(coordInfoOne); spacesOut.clear(); std::vector allSpaces; AnnotationCoordinateSpaceEnum::getAllEnums(allSpaces); for (std::vector::iterator spaceIter = allSpaces.begin(); spaceIter != allSpaces.end(); spaceIter++) { const AnnotationCoordinateSpaceEnum::Enum space = *spaceIter; switch (space) { case AnnotationCoordinateSpaceEnum::PIXELS: break; case AnnotationCoordinateSpaceEnum::STEREOTAXIC: case AnnotationCoordinateSpaceEnum::SURFACE: case AnnotationCoordinateSpaceEnum::TAB: case AnnotationCoordinateSpaceEnum::WINDOW: /* * See if space for first coord info is valid */ if (coordInfoOne->isCoordinateSpaceValid(space)) { bool addItFlag = true; if (coordInfoTwo != NULL) { /* * See if same space is valid for second coord info */ addItFlag = coordInfoTwo->isCoordinateSpaceValid(space); switch (space) { case AnnotationCoordinateSpaceEnum::PIXELS: case AnnotationCoordinateSpaceEnum::STEREOTAXIC: /* * Both coord info's must be in the SAME TAB */ if (coordInfoOne->m_tabIndex != coordInfoTwo->m_tabIndex) { addItFlag = false; } case AnnotationCoordinateSpaceEnum::SURFACE: /* * Both coord info's must be on same surface and * in the SAME TAB */ if ((coordInfoOne->m_tabIndex != coordInfoTwo->m_tabIndex) || (coordInfoOne->m_surfaceNumberOfNodes != coordInfoTwo->m_surfaceNumberOfNodes) || (coordInfoOne->m_surfaceStructure != coordInfoTwo->m_surfaceStructure)) { addItFlag = false; } break; case AnnotationCoordinateSpaceEnum::TAB: if (coordInfoOne->m_tabIndex != coordInfoTwo->m_tabIndex) { addItFlag = false; } break; case AnnotationCoordinateSpaceEnum::WINDOW: if (coordInfoOne->m_windowIndex != coordInfoTwo->m_windowIndex) { addItFlag = false; } break; } } if (addItFlag) { spacesOut.push_back(space); } } break; } } } /** * Get the different types of coordinates at the given mouse location. * * @param mouseEvent * Mouse event containing mouse location information. * @param coordInfoOut * Output containing coordinate information. */ void AnnotationCoordinateInformation::createCoordinateInformationFromXY(const MouseEvent& mouseEvent, AnnotationCoordinateInformation& coordInfoOut) { createCoordinateInformationFromXY(mouseEvent.getOpenGLWidget(), mouseEvent.getViewportContent(), mouseEvent.getX(), mouseEvent.getY(), coordInfoOut); } /** * Get the different types of coordinates at the given mouse location. * * @param mouseEvent * Mouse event containing mouse location information. * @param windowX * X-coordinate in the window. * @param windowY * Y-coordinate in the window. * @param coordInfoOut * Output containing coordinate information. */ void AnnotationCoordinateInformation::createCoordinateInformationFromXY(const MouseEvent& mouseEvent, const int32_t windowX, const int32_t windowY, AnnotationCoordinateInformation& coordInfoOut) { createCoordinateInformationFromXY(mouseEvent.getOpenGLWidget(), mouseEvent.getViewportContent(), windowX, windowY, coordInfoOut); } /** * Get the different types of coordinates at the given mouse location. * * @param openGLWidget * The OpenGL Widget. * @param viewportContent * The content of the viewport. * @param windowX * X-coordinate in the window. * @param windowY * Y-coordinate in the window. * @param coordInfoOut * Output containing coordinate information. */ void AnnotationCoordinateInformation::createCoordinateInformationFromXY(BrainOpenGLWidget* openGLWidget, BrainOpenGLViewportContent* viewportContent, const int32_t windowX, const int32_t windowY, AnnotationCoordinateInformation& coordInfoOut) { coordInfoOut.reset(); SelectionManager* idManager = openGLWidget->performIdentification(windowX, windowY, false); SelectionItemVoxel* voxelID = idManager->getVoxelIdentification(); SelectionItemSurfaceNode* surfaceNodeIdentification = idManager->getSurfaceNodeIdentification(); if (surfaceNodeIdentification->isValid()) { surfaceNodeIdentification->getModelXYZ(coordInfoOut.m_modelXYZ); coordInfoOut.m_modelXYZValid = true; const Surface* surface = surfaceNodeIdentification->getSurface(); CaretAssert(surface); coordInfoOut.m_surfaceNumberOfNodes = surface->getNumberOfNodes(); coordInfoOut.m_surfaceStructure = surface->getStructure(); coordInfoOut.m_surfaceNodeIndex = surfaceNodeIdentification->getNodeNumber(); coordInfoOut.m_surfaceNodeOffset = AnnotationCoordinate::getDefaultSurfaceOffsetLength(); coordInfoOut.m_surfaceNodeVector = AnnotationSurfaceOffsetVectorTypeEnum::CENTROID_THRU_VERTEX; coordInfoOut.m_surfaceNodeValid = true; } else if (voxelID->isValid()) { voxelID->getModelXYZ(coordInfoOut.m_modelXYZ); coordInfoOut.m_modelXYZValid = true; } /* * In tile tabs, some regions may not contain a tab such * as three tabs in a two-by-two configuration * or if the user has clicked in a margin */ BrowserTabContent* tabContent = viewportContent->getBrowserTabContent(); if (tabContent != NULL) { int tabViewport[4]; viewportContent->getModelViewport(tabViewport); const float tabX = 100.0 * (windowX - tabViewport[0]) / static_cast(tabViewport[2]); const float tabY = 100.0 * (windowY - tabViewport[1]) / static_cast(tabViewport[3]); if ((tabX >= 0.0) && (tabX < 100.0) && (tabY >= 0.0) && (tabY <= 100.0)) { coordInfoOut.m_tabXYZ[0] = tabX; coordInfoOut.m_tabXYZ[1] = tabY; coordInfoOut.m_tabXYZ[2] = 0.0; coordInfoOut.m_tabPixelXYZ[0] = (windowX - tabViewport[0]); coordInfoOut.m_tabPixelXYZ[1] = (windowY - tabViewport[1]); coordInfoOut.m_tabPixelXYZ[2] = 0; coordInfoOut.m_tabIndex = tabContent->getTabNumber(); coordInfoOut.m_tabWidth = tabViewport[2]; coordInfoOut.m_tabHeight = tabViewport[3]; } } int windowViewport[4]; viewportContent->getWindowViewport(windowViewport); coordInfoOut.m_windowPixelXYZ[0] = windowX - windowViewport[0]; coordInfoOut.m_windowPixelXYZ[1] = windowY - windowViewport[1]; coordInfoOut.m_windowPixelXYZ[2] = 0.0; coordInfoOut.m_windowIndex = viewportContent->getWindowIndex(); coordInfoOut.m_windowWidth = windowViewport[2]; coordInfoOut.m_windowHeight = windowViewport[3]; /* * Normalize window coordinates (width and height range [0, 100] */ coordInfoOut.m_windowXYZ[0] = 100.0 * (coordInfoOut.m_windowPixelXYZ[0] / windowViewport[2]); coordInfoOut.m_windowXYZ[1] = 100.0 * (coordInfoOut.m_windowPixelXYZ[1] / windowViewport[3]); coordInfoOut.m_windowXYZ[2] = 0.0; } /** * Set the coordinates for the annotation. * * @param annotation * The annotation. * @param coordinateSpace * The coordinate space. * @parm coordInfoOne * Data for the first coordinate. * @parm coordInfoTwo * Data for the second coordinate. */ bool AnnotationCoordinateInformation::setAnnotationCoordinatesForSpace(Annotation* annotation, const AnnotationCoordinateSpaceEnum::Enum coordinateSpace, const AnnotationCoordinateInformation* coordInfoOne, const AnnotationCoordinateInformation* coordInfoTwo) { CaretAssert(annotation); bool validCoordinateFlag = false; AnnotationOneDimensionalShape* oneDimAnn = dynamic_cast(annotation); AnnotationTwoDimensionalShape* twoDimAnn = dynamic_cast(annotation); if (oneDimAnn != NULL) { validCoordinateFlag = setOneDimAnnotationCoordinatesForSpace(oneDimAnn, coordinateSpace, coordInfoOne, coordInfoTwo); } else if (twoDimAnn != NULL) { validCoordinateFlag = setTwoDimAnnotationCoordinatesForSpace(twoDimAnn, coordinateSpace, coordInfoOne, coordInfoTwo); } return validCoordinateFlag; } /** * Set the coordinates for the one-dimensional annotation. * * @param annotation * The annotation. * @param coordinateSpace * The coordinate space. * @parm coordInfoOne * Data for the first coordinate. * @parm coordInfoTwo * Data for the second coordinate. */ bool AnnotationCoordinateInformation::setOneDimAnnotationCoordinatesForSpace(AnnotationOneDimensionalShape* annotation, const AnnotationCoordinateSpaceEnum::Enum coordinateSpace, const AnnotationCoordinateInformation* coordInfoOne, const AnnotationCoordinateInformation* coordInfoTwo) { bool validCoordinateFlag = false; CaretAssert(annotation); CaretAssert(coordInfoOne); AnnotationCoordinate* startCoordinate = annotation->getStartCoordinate(); CaretAssert(startCoordinate); AnnotationCoordinate* endCoordinate = annotation->getEndCoordinate(); CaretAssert(endCoordinate); switch (coordinateSpace) { case AnnotationCoordinateSpaceEnum::STEREOTAXIC: if (coordInfoOne->m_modelXYZValid) { startCoordinate->setXYZ(coordInfoOne->m_modelXYZ); annotation->setCoordinateSpace(AnnotationCoordinateSpaceEnum::STEREOTAXIC); validCoordinateFlag = true; if (coordInfoTwo != NULL) { if (coordInfoTwo->m_modelXYZValid) { if (endCoordinate != NULL) { endCoordinate->setXYZ(coordInfoTwo->m_modelXYZ); } } } } break; case AnnotationCoordinateSpaceEnum::PIXELS: CaretAssert(0); break; case AnnotationCoordinateSpaceEnum::SURFACE: if (coordInfoOne->m_surfaceNodeValid) { const float surfaceOffsetLength = startCoordinate->getSurfaceOffsetLength(); const AnnotationSurfaceOffsetVectorTypeEnum::Enum surfaceOffsetVector = startCoordinate->getSurfaceOffsetVectorType(); startCoordinate->setSurfaceSpace(coordInfoOne->m_surfaceStructure, coordInfoOne->m_surfaceNumberOfNodes, coordInfoOne->m_surfaceNodeIndex, surfaceOffsetLength, surfaceOffsetVector); annotation->setCoordinateSpace(AnnotationCoordinateSpaceEnum::SURFACE); validCoordinateFlag = true; if (coordInfoTwo != NULL) { if (coordInfoTwo->m_surfaceNodeValid) { if (endCoordinate != NULL) { const float surfaceOffsetLength = endCoordinate->getSurfaceOffsetLength(); endCoordinate->setSurfaceSpace(coordInfoTwo->m_surfaceStructure, coordInfoTwo->m_surfaceNumberOfNodes, coordInfoTwo->m_surfaceNodeIndex, surfaceOffsetLength, surfaceOffsetVector); } } } } break; case AnnotationCoordinateSpaceEnum::TAB: if (coordInfoOne->m_tabIndex >= 0) { startCoordinate->setXYZ(coordInfoOne->m_tabXYZ); annotation->setCoordinateSpace(AnnotationCoordinateSpaceEnum::TAB); annotation->setTabIndex(coordInfoOne->m_tabIndex); validCoordinateFlag = true; if (coordInfoTwo != NULL) { if (coordInfoTwo->m_tabIndex >= 0) { if (endCoordinate != NULL) { endCoordinate->setXYZ(coordInfoTwo->m_tabXYZ); } } } else if (endCoordinate != NULL) { double xyz[3] = { coordInfoOne->m_tabXYZ[0], coordInfoOne->m_tabXYZ[1], coordInfoOne->m_tabXYZ[2] }; if (xyz[1] > 50.0) { xyz[1] -= 25.0; } else { xyz[1] += 25.0; } endCoordinate->setXYZ(xyz); } } break; case AnnotationCoordinateSpaceEnum::WINDOW: if (coordInfoOne->m_windowIndex >= 0) { startCoordinate->setXYZ(coordInfoOne->m_windowXYZ); annotation->setCoordinateSpace(AnnotationCoordinateSpaceEnum::WINDOW); annotation->setWindowIndex(coordInfoOne->m_windowIndex); validCoordinateFlag = true; if (coordInfoTwo != NULL) { if (coordInfoTwo->m_windowIndex >= 0) { if (endCoordinate != NULL) { endCoordinate->setXYZ(coordInfoTwo->m_windowXYZ); } } } else if (endCoordinate != NULL) { double xyz[3] = { coordInfoOne->m_windowXYZ[0], coordInfoOne->m_windowXYZ[1], coordInfoOne->m_windowXYZ[2] }; if (xyz[1] > 50.0) { xyz[1] -= 25.0; } else { xyz[1] += 25.0; } endCoordinate->setXYZ(xyz); } } break; } return validCoordinateFlag; } /** * Set the coordinates for the two-dimensional annotation. * If both coordinates are valid (not NULL), the annotation is * placed at the average of the two coordinates. Otherwise, * the annotation is placed at the first coordinate. * * @param annotation * The annotation. * @param coordinateSpace * The coordinate space. * @parm coordInfoOne * Data for the first coordinate. * @parm optionalCoordInfoTwo * Data for the optional second coordinate. */ bool AnnotationCoordinateInformation::setTwoDimAnnotationCoordinatesForSpace(AnnotationTwoDimensionalShape* annotation, const AnnotationCoordinateSpaceEnum::Enum coordinateSpace, const AnnotationCoordinateInformation* coordInfoOne, const AnnotationCoordinateInformation* optionalCoordInfoTwo) { bool validCoordinateFlag = false; CaretAssert(annotation); CaretAssert(coordInfoOne); bool setWidthHeightWithTabCoordsFlag = false; bool setWidthHeightWithWindowCoordsFlag = false; AnnotationCoordinate* coordinate = annotation->getCoordinate(); switch (coordinateSpace) { case AnnotationCoordinateSpaceEnum::STEREOTAXIC: if (coordInfoOne->m_modelXYZValid) { coordinate->setXYZ(coordInfoOne->m_modelXYZ); annotation->setCoordinateSpace(AnnotationCoordinateSpaceEnum::STEREOTAXIC); validCoordinateFlag = true; 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 }; coordinate->setXYZ(centerXYZ); setWidthHeightWithTabCoordsFlag = true; } } } break; case AnnotationCoordinateSpaceEnum::PIXELS: CaretAssert(0); break; case AnnotationCoordinateSpaceEnum::SURFACE: if (coordInfoOne->m_surfaceNodeValid) { coordinate->setSurfaceSpace(coordInfoOne->m_surfaceStructure, coordInfoOne->m_surfaceNumberOfNodes, coordInfoOne->m_surfaceNodeIndex); annotation->setCoordinateSpace(AnnotationCoordinateSpaceEnum::SURFACE); validCoordinateFlag = true; if (optionalCoordInfoTwo != NULL) { if ((optionalCoordInfoTwo->m_surfaceNodeValid) && (optionalCoordInfoTwo->m_surfaceStructure == coordInfoOne->m_surfaceStructure)) { if ((optionalCoordInfoTwo->m_windowIndex == coordInfoOne->m_windowIndex) && (coordInfoOne->m_windowIndex >= 0)) { const float windowWidth = coordInfoOne->m_windowWidth; const float windowHeight = coordInfoOne->m_windowHeight; const float x1 = coordInfoOne->m_windowXYZ[0] * windowWidth; const float y1 = coordInfoOne->m_windowXYZ[1] * windowHeight; const float x2 = optionalCoordInfoTwo->m_windowXYZ[0] * windowWidth; const float y2 = optionalCoordInfoTwo->m_windowXYZ[1] * windowHeight; const int32_t windowX = static_cast((x1 + x2)) / 2.0; const int32_t windowY = static_cast((y1 + y2)) / 2.0; EventIdentificationRequest idRequest(coordInfoOne->m_windowIndex, static_cast(windowX), static_cast(windowY)); EventManager::get()->sendEvent(idRequest.getPointer()); SelectionManager* sm = idRequest.getSelectionManager(); if (sm != NULL) { const SelectionItemSurfaceNode* nodeID = sm->getSurfaceNodeIdentification(); CaretAssert(nodeID); if (nodeID->isValid()) { if (nodeID->getSurface()->getStructure() == coordInfoOne->m_surfaceStructure) { coordinate->setSurfaceSpace(coordInfoOne->m_surfaceStructure, coordInfoOne->m_surfaceNumberOfNodes, nodeID->getNodeNumber()); setWidthHeightWithTabCoordsFlag = true; } } } } } } } break; case AnnotationCoordinateSpaceEnum::TAB: if (coordInfoOne->m_tabIndex >= 0) { coordinate->setXYZ(coordInfoOne->m_tabXYZ); annotation->setCoordinateSpace(AnnotationCoordinateSpaceEnum::TAB); annotation->setTabIndex(coordInfoOne->m_tabIndex); validCoordinateFlag = true; 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 }; coordinate->setXYZ(centerXYZ); setWidthHeightWithTabCoordsFlag = true; } } } break; case AnnotationCoordinateSpaceEnum::WINDOW: if (coordInfoOne->m_windowIndex >= 0) { coordinate->setXYZ(coordInfoOne->m_windowXYZ); annotation->setCoordinateSpace(AnnotationCoordinateSpaceEnum::WINDOW); annotation->setWindowIndex(coordInfoOne->m_windowIndex); validCoordinateFlag = true; 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 }; coordinate->setXYZ(centerXYZ); setWidthHeightWithWindowCoordsFlag = true; } } } break; } if (setWidthHeightWithTabCoordsFlag) { if (coordInfoOne->m_tabIndex >= 0) { if (optionalCoordInfoTwo != NULL) { if (coordInfoOne->m_tabIndex == optionalCoordInfoTwo->m_tabIndex) { const float tabWidth = coordInfoOne->m_tabWidth; const float tabHeight = coordInfoOne->m_tabHeight; const float oneXYZ[3] = { coordInfoOne->m_tabXYZ[0], coordInfoOne->m_tabXYZ[1], coordInfoOne->m_tabXYZ[2] }; const float twoXYZ[3] = { optionalCoordInfoTwo->m_tabXYZ[0], optionalCoordInfoTwo->m_tabXYZ[1], optionalCoordInfoTwo->m_tabXYZ[2] }; annotation->setWidthAndHeightFromBounds(oneXYZ, twoXYZ, tabWidth, tabHeight); } } } } else if (setWidthHeightWithWindowCoordsFlag) { if (coordInfoOne->m_windowIndex >= 0) { if (optionalCoordInfoTwo != NULL) { if (coordInfoOne->m_windowIndex == optionalCoordInfoTwo->m_windowIndex) { const float windowWidth = coordInfoOne->m_windowWidth; const float windowHeight = coordInfoOne->m_windowHeight; const float oneXYZ[3] = { coordInfoOne->m_windowXYZ[0], coordInfoOne->m_windowXYZ[1], coordInfoOne->m_windowXYZ[2] }; const float twoXYZ[3] = { optionalCoordInfoTwo->m_windowXYZ[0], optionalCoordInfoTwo->m_windowXYZ[1], optionalCoordInfoTwo->m_windowXYZ[2] }; annotation->setWidthAndHeightFromBounds(oneXYZ, twoXYZ, windowWidth, windowHeight); } } } } return validCoordinateFlag; } connectome-workbench-1.2.3+git41-gc4c6c90/src/GuiQt/AnnotationCoordinateInformation.h000066400000000000000000000122621300200146000303010ustar00rootroot00000000000000#ifndef __ANNOTATION_COORDINATE_INFORMATION_H__ #define __ANNOTATION_COORDINATE_INFORMATION_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 "AnnotationCoordinateSpaceEnum.h" #include "AnnotationSurfaceOffsetVectorTypeEnum.h" #include "StructureEnum.h" class QLabel; namespace caret { class Annotation; class AnnotationOneDimensionalShape; class AnnotationTwoDimensionalShape; class BrainOpenGLWidget; class BrainOpenGLViewportContent; class MouseEvent; class AnnotationCoordinateInformation { public: AnnotationCoordinateInformation(); virtual ~AnnotationCoordinateInformation(); bool isCoordinateSpaceValid(const AnnotationCoordinateSpaceEnum::Enum space) const; void reset(); static void getValidCoordinateSpaces(const AnnotationCoordinateInformation* coordInfoOne, const AnnotationCoordinateInformation* coordInfoTwo, std::vector& spacesOut); static void createCoordinateInformationFromXY(const MouseEvent& mouseEvent, AnnotationCoordinateInformation& coordInfoOut); static void createCoordinateInformationFromXY(const MouseEvent& mouseEvent, const int32_t windowX, const int32_t windowY, AnnotationCoordinateInformation& coordInfoOut); static void createCoordinateInformationFromXY(BrainOpenGLWidget* openGLWidget, BrainOpenGLViewportContent* viewportContent, const int32_t windowX, const int32_t windowY, AnnotationCoordinateInformation& coordInfoOut); static bool setAnnotationCoordinatesForSpace(Annotation* annotation, const AnnotationCoordinateSpaceEnum::Enum space, const AnnotationCoordinateInformation* coordInfoOne, const AnnotationCoordinateInformation* coordInfoTwo); double m_modelXYZ[3]; bool m_modelXYZValid; float m_tabWidth; float m_tabHeight; float m_tabXYZ[3]; float m_tabPixelXYZ[3]; int32_t m_tabIndex; float m_windowWidth; float m_windowHeight; float m_windowXYZ[3]; float m_windowPixelXYZ[3]; int32_t m_windowIndex; StructureEnum::Enum m_surfaceStructure; int32_t m_surfaceNumberOfNodes; int32_t m_surfaceNodeIndex; float m_surfaceNodeOffset; AnnotationSurfaceOffsetVectorTypeEnum::Enum m_surfaceNodeVector; bool m_surfaceNodeValid; private: AnnotationCoordinateInformation(const AnnotationCoordinateInformation&); AnnotationCoordinateInformation& operator=(const AnnotationCoordinateInformation&); static bool setOneDimAnnotationCoordinatesForSpace(AnnotationOneDimensionalShape* annotation, const AnnotationCoordinateSpaceEnum::Enum space, const AnnotationCoordinateInformation* coordInfoOne, const AnnotationCoordinateInformation* coordInfoTwo); static bool setTwoDimAnnotationCoordinatesForSpace(AnnotationTwoDimensionalShape* annotation, const AnnotationCoordinateSpaceEnum::Enum space, const AnnotationCoordinateInformation* coordInfoOne, const AnnotationCoordinateInformation* coordInfoTwo); // ADD_NEW_MEMBERS_HERE }; #ifdef __ANNOTATION_COORDINATE_INFORMATION_DECLARE__ // #endif // __ANNOTATION_COORDINATE_INFORMATION_DECLARE__ } // namespace #endif //__ANNOTATION_COORDINATE_INFORMATION_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/GuiQt/AnnotationCoordinateSelectionWidget.cxx000066400000000000000000001004771300200146000314660ustar00rootroot00000000000000 /*LICENSE_START*/ /* * Copyright (C) 2015 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 __ANNOTATION_COORDINATE_SELECTION_WIDGET_DECLARE__ #include "AnnotationCoordinateSelectionWidget.h" #undef __ANNOTATION_COORDINATE_SELECTION_WIDGET_DECLARE__ #include #include #include #include #include #include #include "Annotation.h" #include "AnnotationCoordinate.h" #include "AnnotationCoordinateInformation.h" #include "AnnotationImage.h" #include "AnnotationManager.h" #include "AnnotationOneDimensionalShape.h" #include "AnnotationPercentSizeText.h" #include "AnnotationRedoUndoCommand.h" #include "AnnotationTwoDimensionalShape.h" #include "Brain.h" #include "BrainStructure.h" #include "CaretAssert.h" #include "CaretLogger.h" #include "DisplayPropertiesAnnotation.h" #include "EventIdentificationRequest.h" #include "GuiManager.h" #include "MathFunctions.h" #include "SelectionItemSurfaceNode.h" #include "SelectionManager.h" #include "Surface.h" #include "WuQMessageBox.h" using namespace caret; /** * \class caret::AnnotationCoordinateSelectionWidget * \brief Widget for selection of an annotation coordinate. * \ingroup GuiQt */ /** * Constructor. * * @param annotationType * Type of annotation that is being created. * @param coordInfo * Coordinate information. * @param optionalSecondCoordInfo * Optional second coordinate information (valid if not NULL) * @param parent * The parent widget. */ AnnotationCoordinateSelectionWidget::AnnotationCoordinateSelectionWidget(const AnnotationTypeEnum::Enum annotationType, const AnnotationCoordinateInformation& coordInfo, const AnnotationCoordinateInformation* optionalSecondCoordInfo, QWidget* parent) : QWidget(parent), m_annotationType(annotationType), m_coordInfo(coordInfo), m_optionalSecondCoordInfo(optionalSecondCoordInfo) { bool enableModelSpaceFlag = false; bool enableSurfaceSpaceFlag = false; bool enableTabSpaceFlag = true; bool enableWindowSpaceFlag = true; switch (m_annotationType) { case AnnotationTypeEnum::BOX: enableModelSpaceFlag = true; enableSurfaceSpaceFlag = true; break; case AnnotationTypeEnum::COLOR_BAR: break; case AnnotationTypeEnum::IMAGE: enableModelSpaceFlag = true; enableSurfaceSpaceFlag = true; break; case AnnotationTypeEnum::LINE: enableModelSpaceFlag = true; enableSurfaceSpaceFlag = true; break; case AnnotationTypeEnum::OVAL: enableModelSpaceFlag = true; enableSurfaceSpaceFlag = true; break; case AnnotationTypeEnum::TEXT: enableModelSpaceFlag = true; enableSurfaceSpaceFlag = true; break; } m_spaceButtonGroup = new QButtonGroup(this); int columnIndex = 0; const int COLUMN_RADIO_BUTTON = columnIndex++; const int COLUMN_COORD_X = columnIndex++; const int COLUMN_COORD_Y = columnIndex++; const int COLUMN_COORD_Z = columnIndex++; const int COLUMN_COORD_TWO_X = columnIndex++; const int COLUMN_COORD_TWO_Y = columnIndex++; const int COLUMN_COORD_TWO_Z = columnIndex++; const int COLUMN_EXTRA = columnIndex++; QGridLayout* gridLayout = new QGridLayout(this); gridLayout->setColumnStretch(COLUMN_RADIO_BUTTON, 0); gridLayout->setColumnStretch(COLUMN_COORD_X, 0); gridLayout->setColumnStretch(COLUMN_COORD_Y, 0); gridLayout->setColumnStretch(COLUMN_COORD_Z, 0); gridLayout->setColumnStretch(COLUMN_EXTRA, 100); const int titleRow = gridLayout->rowCount(); gridLayout->addWidget(new QLabel("Space"), titleRow, COLUMN_RADIO_BUTTON, Qt::AlignHCenter); gridLayout->addWidget(new QLabel("X"), titleRow, COLUMN_COORD_X, Qt::AlignHCenter); gridLayout->addWidget(new QLabel("Y"), titleRow, COLUMN_COORD_Y, Qt::AlignHCenter); gridLayout->addWidget(new QLabel("Z"), titleRow, COLUMN_COORD_Z, Qt::AlignHCenter); if (m_optionalSecondCoordInfo != NULL) { gridLayout->addWidget(new QLabel("X2"), titleRow, COLUMN_COORD_TWO_X, Qt::AlignHCenter); gridLayout->addWidget(new QLabel("Y2"), titleRow, COLUMN_COORD_TWO_Y, Qt::AlignHCenter); gridLayout->addWidget(new QLabel("Z2"), titleRow, COLUMN_COORD_TWO_Z, Qt::AlignHCenter); } if (enableTabSpaceFlag) { if (m_coordInfo.m_tabIndex < 0) { enableTabSpaceFlag = false; } if (m_optionalSecondCoordInfo != NULL) { if (m_optionalSecondCoordInfo->m_tabIndex < 0) { enableTabSpaceFlag = false; } } } if (enableModelSpaceFlag) { if ( ! m_coordInfo.m_modelXYZValid) { enableModelSpaceFlag = false; } if (m_optionalSecondCoordInfo != NULL) { if ( ! m_optionalSecondCoordInfo->m_modelXYZValid) { enableModelSpaceFlag = false; } /* * Only allow model space for two coordinates in the SAME tab. * If tab space is not enabled, then the two model coordinates are * in different tabs. */ if ( ! enableTabSpaceFlag) { enableModelSpaceFlag = false; } } } if (enableModelSpaceFlag) { QRadioButton* rb = createRadioButtonForSpace(AnnotationCoordinateSpaceEnum::STEREOTAXIC); m_spaceButtonGroup->addButton(rb, AnnotationCoordinateSpaceEnum::toIntegerCode(AnnotationCoordinateSpaceEnum::STEREOTAXIC)); const int rowNum = gridLayout->rowCount(); gridLayout->addWidget(rb, rowNum, COLUMN_RADIO_BUTTON); gridLayout->addWidget(new QLabel(AString::number(m_coordInfo.m_modelXYZ[0], 'f', 1)), rowNum, COLUMN_COORD_X, Qt::AlignRight); gridLayout->addWidget(new QLabel(AString::number(m_coordInfo.m_modelXYZ[1], 'f', 1)), rowNum, COLUMN_COORD_Y, Qt::AlignRight); gridLayout->addWidget(new QLabel(AString::number(m_coordInfo.m_modelXYZ[2], 'f', 1)), rowNum, COLUMN_COORD_Z, Qt::AlignRight); if (m_optionalSecondCoordInfo != NULL) { gridLayout->addWidget(new QLabel(AString::number(m_optionalSecondCoordInfo->m_modelXYZ[0], 'f', 1)), rowNum, COLUMN_COORD_TWO_X, Qt::AlignRight); gridLayout->addWidget(new QLabel(AString::number(m_optionalSecondCoordInfo->m_modelXYZ[1], 'f', 1)), rowNum, COLUMN_COORD_TWO_Y, Qt::AlignRight); gridLayout->addWidget(new QLabel(AString::number(m_optionalSecondCoordInfo->m_modelXYZ[2], 'f', 1)), rowNum, COLUMN_COORD_TWO_Z, Qt::AlignRight); } } if (enableTabSpaceFlag) { QRadioButton* rb = createRadioButtonForSpace(AnnotationCoordinateSpaceEnum::TAB); rb->setText(rb->text() + " " + AString::number(m_coordInfo.m_tabIndex + 1)); m_spaceButtonGroup->addButton(rb, AnnotationCoordinateSpaceEnum::toIntegerCode(AnnotationCoordinateSpaceEnum::TAB)); const int rowNum = gridLayout->rowCount(); gridLayout->addWidget(rb, rowNum, COLUMN_RADIO_BUTTON); gridLayout->addWidget(new QLabel(AString::number(m_coordInfo.m_tabXYZ[0], 'f', 1)), rowNum, COLUMN_COORD_X, Qt::AlignRight); gridLayout->addWidget(new QLabel(AString::number(m_coordInfo.m_tabXYZ[1], 'f', 1)), rowNum, COLUMN_COORD_Y, Qt::AlignRight); gridLayout->addWidget(new QLabel(AString::number(m_coordInfo.m_tabXYZ[2], 'f', 1)), rowNum, COLUMN_COORD_Z, Qt::AlignRight); if (m_optionalSecondCoordInfo != NULL) { gridLayout->addWidget(new QLabel(AString::number(m_optionalSecondCoordInfo->m_tabXYZ[0], 'f', 1)), rowNum, COLUMN_COORD_TWO_X, Qt::AlignRight); gridLayout->addWidget(new QLabel(AString::number(m_optionalSecondCoordInfo->m_tabXYZ[1], 'f', 1)), rowNum, COLUMN_COORD_TWO_Y, Qt::AlignRight); gridLayout->addWidget(new QLabel(AString::number(m_optionalSecondCoordInfo->m_tabXYZ[2], 'f', 1)), rowNum, COLUMN_COORD_TWO_Z, Qt::AlignRight); } } if (enableWindowSpaceFlag) { if (m_coordInfo.m_windowIndex < 0) { enableWindowSpaceFlag = false; } if (m_optionalSecondCoordInfo != NULL) { if (m_optionalSecondCoordInfo->m_windowIndex < 0) { enableWindowSpaceFlag = false; } } } if (enableWindowSpaceFlag) { QRadioButton* rb = createRadioButtonForSpace(AnnotationCoordinateSpaceEnum::WINDOW); rb->setText(rb->text() + " " + AString::number(m_coordInfo.m_windowIndex + 1)); m_spaceButtonGroup->addButton(rb, AnnotationCoordinateSpaceEnum::toIntegerCode(AnnotationCoordinateSpaceEnum::WINDOW)); const int rowNum = gridLayout->rowCount(); gridLayout->addWidget(rb, rowNum, COLUMN_RADIO_BUTTON); gridLayout->addWidget(new QLabel(AString::number(m_coordInfo.m_windowXYZ[0], 'f', 1)), rowNum, COLUMN_COORD_X, Qt::AlignRight); gridLayout->addWidget(new QLabel(AString::number(m_coordInfo.m_windowXYZ[1], 'f', 1)), rowNum, COLUMN_COORD_Y, Qt::AlignRight); gridLayout->addWidget(new QLabel(AString::number(m_coordInfo.m_windowXYZ[2], 'f', 1)), rowNum, COLUMN_COORD_Z, Qt::AlignRight); if (m_optionalSecondCoordInfo != NULL) { gridLayout->addWidget(new QLabel(AString::number(m_optionalSecondCoordInfo->m_windowXYZ[0], 'f', 1)), rowNum, COLUMN_COORD_TWO_X, Qt::AlignRight); gridLayout->addWidget(new QLabel(AString::number(m_optionalSecondCoordInfo->m_windowXYZ[1], 'f', 1)), rowNum, COLUMN_COORD_TWO_Y, Qt::AlignRight); gridLayout->addWidget(new QLabel(AString::number(m_optionalSecondCoordInfo->m_windowXYZ[2], 'f', 1)), rowNum, COLUMN_COORD_TWO_Z, Qt::AlignRight); } } if (enableSurfaceSpaceFlag) { if ( ! m_coordInfo.m_surfaceNodeValid) { enableSurfaceSpaceFlag = false; } if (m_optionalSecondCoordInfo != NULL) { if ( ! m_optionalSecondCoordInfo->m_surfaceNodeValid) { enableSurfaceSpaceFlag = false; } } /* * Only allow surface space for two coordinates in the SAME tab. * If tab space is not enabled, then the two surface coordinates are * in different tabs. */ if ( ! enableTabSpaceFlag) { enableSurfaceSpaceFlag = false; } } if (enableSurfaceSpaceFlag) { QRadioButton* rb = createRadioButtonForSpace(AnnotationCoordinateSpaceEnum::SURFACE); m_spaceButtonGroup->addButton(rb, AnnotationCoordinateSpaceEnum::toIntegerCode(AnnotationCoordinateSpaceEnum::SURFACE)); const int rowNum = gridLayout->rowCount(); gridLayout->addWidget(rb, rowNum, COLUMN_RADIO_BUTTON); const AString infoText(StructureEnum::toGuiName(m_coordInfo.m_surfaceStructure) + " Vertex: " +AString::number(m_coordInfo.m_surfaceNodeIndex)); gridLayout->addWidget(new QLabel(infoText), rowNum, COLUMN_COORD_X, 1, 4); if (m_optionalSecondCoordInfo != NULL) { const int rowNum = gridLayout->rowCount(); const AString infoText(StructureEnum::toGuiName(m_optionalSecondCoordInfo->m_surfaceStructure) + " Vertex 2: " +AString::number(m_optionalSecondCoordInfo->m_surfaceNodeIndex)); gridLayout->addWidget(new QLabel(infoText), rowNum, COLUMN_COORD_X, 1, 4); } } /* * This switch statment does nothing. But, if a new space is added * the missing enumerated value in the switch statement will cause a * compilation error which may indicate the code in this method * needs to be updated. */ const AnnotationCoordinateSpaceEnum::Enum space = AnnotationCoordinateSpaceEnum::TAB; switch (space) { case AnnotationCoordinateSpaceEnum::STEREOTAXIC: case AnnotationCoordinateSpaceEnum::PIXELS: case AnnotationCoordinateSpaceEnum::SURFACE: case AnnotationCoordinateSpaceEnum::TAB: case AnnotationCoordinateSpaceEnum::WINDOW: break; } setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); } /** * Destructor. */ AnnotationCoordinateSelectionWidget::~AnnotationCoordinateSelectionWidget() { } /** * Create a radio button that displays the text for and contains the * enumerated value in a property. * * @param space * Coordinate space for button. */ QRadioButton* AnnotationCoordinateSelectionWidget::createRadioButtonForSpace(const AnnotationCoordinateSpaceEnum::Enum space) { 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(), spaceEnumName); return rb; } /** * Select the given coordinate space. * * @param coordSpace * The coordinate space. */ void AnnotationCoordinateSelectionWidget::selectCoordinateSpace(const AnnotationCoordinateSpaceEnum::Enum coordSpace) { QList buttons = m_spaceButtonGroup->buttons(); QListIterator buttList(buttons); while (buttList.hasNext()) { QAbstractButton* button = buttList.next(); CaretAssert(button); const int32_t buttonID = m_spaceButtonGroup->id(button); bool valid = false; const AnnotationCoordinateSpaceEnum::Enum buttSpace = AnnotationCoordinateSpaceEnum::fromIntegerCode(buttonID, &valid); if (valid) { if (buttSpace == coordSpace) { button->setChecked(true); break; } } else { CaretLogSevere("Invalid integer code for annotation coordinate space=" + QString::number(buttonID)); } } } /** * Get the selected annotation coordinate space. * * @param validOut * True if user has selected a coordinate space, else false. * @return * Coordinate space selected by the user (valid if validOut is true). */ AnnotationCoordinateSpaceEnum::Enum AnnotationCoordinateSelectionWidget::getSelectedCoordinateSpace(bool& validOut) const { validOut = false; AnnotationCoordinateSpaceEnum::Enum space = AnnotationCoordinateSpaceEnum::PIXELS; QAbstractButton* button = m_spaceButtonGroup->checkedButton(); if (button == NULL) { return space; } CaretAssert(button); const int32_t buttonID = m_spaceButtonGroup->id(button); space = AnnotationCoordinateSpaceEnum::fromIntegerCode(buttonID, &validOut); return space; } /** * Set the coordinate with the current space selection. * * @param annotation * Annotation whose coordinates are changed. * @param errorMessageOut * Contains error information. * @return * True if successful, else false. */ bool AnnotationCoordinateSelectionWidget::changeAnnotationCoordinate(Annotation* annotation, QString& errorMessageOut) { CaretAssert(annotation); errorMessageOut.clear(); bool valid = false; const AnnotationCoordinateSpaceEnum::Enum newSpace = getSelectedCoordinateSpace(valid); if ( ! valid) { errorMessageOut = ("A coordinate space has not been selected."); return false; } CaretPointer redoAnnotation(annotation->clone()); AnnotationOneDimensionalShape* oneDimShape = dynamic_cast(redoAnnotation.getPointer()); AnnotationTwoDimensionalShape* twoDimShape = dynamic_cast(redoAnnotation.getPointer()); AnnotationCoordinate* coordinate = NULL; AnnotationCoordinate* otherCoordinate = NULL; if (oneDimShape != NULL) { coordinate = oneDimShape->getStartCoordinate(); otherCoordinate = oneDimShape->getEndCoordinate(); } else { coordinate = twoDimShape->getCoordinate(); } const AnnotationCoordinateSpaceEnum::Enum oldSpace = redoAnnotation->getCoordinateSpace(); float oldViewportHeight = 0.0; switch (oldSpace) { case AnnotationCoordinateSpaceEnum::STEREOTAXIC: oldViewportHeight = m_coordInfo.m_tabHeight; break; case AnnotationCoordinateSpaceEnum::PIXELS: break; case AnnotationCoordinateSpaceEnum::SURFACE: oldViewportHeight = m_coordInfo.m_tabHeight; break; case AnnotationCoordinateSpaceEnum::TAB: oldViewportHeight = m_coordInfo.m_tabHeight; break; case AnnotationCoordinateSpaceEnum::WINDOW: oldViewportHeight = m_coordInfo.m_windowHeight; break; } /* * If annotation has two coordinates, get the difference of the two coordinates * that will be used if the user changes the coordinate space. This results * in the annotation retaining its relative locations in the new space */ float diffXyz[3] = { 0.0, 0.0, 0.0 }; bool diffXyzValid = false; if ((oneDimShape != NULL) && (otherCoordinate != NULL)) { float xyz[3]; coordinate->getXYZ(xyz); float otherXyz[3]; otherCoordinate->getXYZ(otherXyz); switch (oldSpace) { case AnnotationCoordinateSpaceEnum::STEREOTAXIC: break; case AnnotationCoordinateSpaceEnum::PIXELS: CaretAssert(0); break; case AnnotationCoordinateSpaceEnum::SURFACE: break; case AnnotationCoordinateSpaceEnum::TAB: diffXyzValid = true; break; case AnnotationCoordinateSpaceEnum::WINDOW: diffXyzValid = true; break; } if (diffXyzValid) { diffXyz[0] = otherXyz[0] - xyz[0]; diffXyz[1] = otherXyz[1] - xyz[1]; diffXyz[2] = otherXyz[2] - xyz[2]; } } float newViewportHeight = 0.0; bool setOtherCoordinateFlag = false; switch (newSpace) { case AnnotationCoordinateSpaceEnum::STEREOTAXIC: if (m_coordInfo.m_modelXYZValid) { coordinate->setXYZ(m_coordInfo.m_modelXYZ); redoAnnotation->setCoordinateSpace(AnnotationCoordinateSpaceEnum::STEREOTAXIC); 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, m_coordInfo.m_surfaceNumberOfNodes, m_coordInfo.m_surfaceNodeIndex); redoAnnotation->setCoordinateSpace(AnnotationCoordinateSpaceEnum::SURFACE); newViewportHeight = m_coordInfo.m_tabHeight; } break; case AnnotationCoordinateSpaceEnum::TAB: if (m_coordInfo.m_tabIndex >= 0) { const int32_t oldTabIndex = redoAnnotation->getTabIndex(); const int32_t newTabIndex = m_coordInfo.m_tabIndex; coordinate->setXYZ(m_coordInfo.m_tabXYZ); redoAnnotation->setCoordinateSpace(AnnotationCoordinateSpaceEnum::TAB); redoAnnotation->setTabIndex(newTabIndex); /* * Will need to move 'other' coordinate since it may not * be within the new tab's region. */ if (otherCoordinate != NULL) { if ((newSpace != oldSpace) || (newTabIndex != oldTabIndex)) { setOtherCoordinateFlag = true; } } newViewportHeight = m_coordInfo.m_tabHeight; } break; case AnnotationCoordinateSpaceEnum::WINDOW: if (m_coordInfo.m_windowIndex >= 0) { const int32_t oldWindowIndex = redoAnnotation->getWindowIndex(); const int32_t newWindowIndex = m_coordInfo.m_windowIndex; coordinate->setXYZ(m_coordInfo.m_windowXYZ); redoAnnotation->setCoordinateSpace(AnnotationCoordinateSpaceEnum::WINDOW); redoAnnotation->setWindowIndex(newWindowIndex); /* * VERIFY * Will need to move 'other' coordinate since it may not * be within the new windows's region. */ if (otherCoordinate != NULL) { if ((newSpace != oldSpace) || (newWindowIndex != oldWindowIndex)) { setOtherCoordinateFlag = true; } } newViewportHeight = m_coordInfo.m_windowHeight; } break; } /* * If the space changes, the 'other' coordinate will need to be moved. * For example, if the */ if (setOtherCoordinateFlag) { float xyz[3]; coordinate->getXYZ(xyz); if (diffXyzValid) { xyz[0] += diffXyz[0]; xyz[1] += diffXyz[1]; xyz[2] += diffXyz[2]; xyz[0] = MathFunctions::clamp(xyz[0], 1.0, 99.0); xyz[1] = MathFunctions::clamp(xyz[1], 1.0, 99.0); xyz[2] = MathFunctions::clamp(xyz[2], 1.0, 99.0); } else { if (xyz[1] > 50.0) { xyz[1] -= 25.0; } else { xyz[1] += 25.0; } } otherCoordinate->setXYZ(xyz); } /* * Height of text is based upon viewport height. If the viewport changes, * scale the text so that it maintains the same physical (pixel) height. */ if ((newViewportHeight > 0.0) && (oldViewportHeight > 0.0)) { const float scale = oldViewportHeight / newViewportHeight; std::cout << "Scale is: " << scale << std::endl; AnnotationPercentSizeText* textAnn = dynamic_cast(redoAnnotation.getPointer()); if (textAnn != NULL) { float percentSize = textAnn->getFontPercentViewportSize(); percentSize *= scale; textAnn->setFontPercentViewportSize(percentSize); } } std::vector annotationsBeforeMoveAndResize; annotationsBeforeMoveAndResize.push_back(annotation); std::vector annotationsAfterMoveAndResize; annotationsAfterMoveAndResize.push_back(redoAnnotation); AnnotationRedoUndoCommand* command = new AnnotationRedoUndoCommand(); command->setModeLocationAndSize(annotationsBeforeMoveAndResize, annotationsAfterMoveAndResize); command->setDescription("Change Coordinate"); AnnotationManager* annotationManager = GuiManager::get()->getBrain()->getAnnotationManager(); AString errorMessage; if ( ! annotationManager->applyCommand(command, errorMessage)) { WuQMessageBox::errorOk(this, errorMessage); } updateAnnotationDisplayProperties(redoAnnotation); return true; } /** * Set the coordinate with the current space selection. * * @param coordinate * Coordinate that is set. * @param errorMessageOut * Contains error information. * @return * True if successful, else false. */ bool AnnotationCoordinateSelectionWidget::setCoordinateForNewAnnotation(Annotation* annotation, QString& errorMessageOut) { errorMessageOut.clear(); bool valid = false; const AnnotationCoordinateSpaceEnum::Enum coordinateSpace = getSelectedCoordinateSpace(valid); if ( ! valid) { errorMessageOut = ("A coordinate space has not been selected."); return false; } AnnotationOneDimensionalShape* oneDimAnn = dynamic_cast(annotation); AnnotationTwoDimensionalShape* twoDimAnn = dynamic_cast(annotation); bool validCoordsFlag = false; if (oneDimAnn != NULL) { validCoordsFlag = AnnotationCoordinateInformation::setAnnotationCoordinatesForSpace(oneDimAnn, coordinateSpace, &m_coordInfo, m_optionalSecondCoordInfo); // setOneDimAnnotationCoordinates(oneDimAnn); } else if (twoDimAnn != NULL) { validCoordsFlag = AnnotationCoordinateInformation::setAnnotationCoordinatesForSpace(twoDimAnn, coordinateSpace, &m_coordInfo, m_optionalSecondCoordInfo); // setTwoDimAnnotationCoordinates(twoDimAnn); } else { const QString msg("PROGRAM ERROR: Annotation is neither one nor two dimensional"); CaretAssertMessage(0, msg); CaretLogSevere(msg); errorMessageOut = msg; return false; } if ( ! validCoordsFlag) { errorMessageOut = "Failed to set coordinates for annotatin."; } updateAnnotationDisplayProperties(annotation); AnnotationImage* imageAnn = dynamic_cast(twoDimAnn); if (imageAnn != NULL) { setWidthAndHeightForImage(imageAnn); } return validCoordsFlag; } /** * Set the width and height of the annotation using the viewport. * * @param imageAnn * The annotation image. */ void AnnotationCoordinateSelectionWidget::setWidthAndHeightForImage(AnnotationImage* imageAnn) { CaretAssert(imageAnn); float vpWidth = 0.0; float vpHeight = 0.0; switch (imageAnn->getCoordinateSpace()) { case AnnotationCoordinateSpaceEnum::STEREOTAXIC: vpWidth = m_coordInfo.m_tabWidth; vpHeight = m_coordInfo.m_tabHeight; break; case AnnotationCoordinateSpaceEnum::PIXELS: CaretAssert(0); break; case AnnotationCoordinateSpaceEnum::SURFACE: vpWidth = m_coordInfo.m_tabWidth; vpHeight = m_coordInfo.m_tabHeight; break; case AnnotationCoordinateSpaceEnum::TAB: vpWidth = m_coordInfo.m_tabWidth; vpHeight = m_coordInfo.m_tabHeight; break; case AnnotationCoordinateSpaceEnum::WINDOW: vpWidth = m_coordInfo.m_windowWidth; vpHeight = m_coordInfo.m_windowHeight; break; } if ((vpWidth > 0.0) && (vpHeight > 0.0)) { const float imageWidth = imageAnn->getImageWidth(); const float imageHeight = imageAnn->getImageHeight(); float percentWidth = (imageWidth / vpWidth) * 100.0; float percentHeight = (imageHeight / vpHeight) * 100.0; const float maxPercentageSize = 90.0; if (percentWidth > percentHeight) { if (percentWidth > maxPercentageSize) { const float scaleValue = maxPercentageSize / percentWidth; percentWidth = maxPercentageSize; percentHeight = percentHeight * scaleValue; } } else { if (percentHeight > maxPercentageSize) { const float scaleValue = maxPercentageSize / percentHeight; percentHeight = maxPercentageSize; percentWidth = percentWidth * scaleValue; } } imageAnn->setWidth(percentWidth); imageAnn->setHeight(percentHeight); } } /** * Update the annotation display properties after creating/updating an annotation. * It is possible that the user will create an annotation in a space (tab 4) and tab 4 * anotations are not enabled for display. So, ensure the display property is enabled * for the annotation. Otherwise, the user could create an annotation and not see * the annotation if the display property is disabled. * * @param annotation * Annotation that has been created or updated. */ void AnnotationCoordinateSelectionWidget::updateAnnotationDisplayProperties(const Annotation* annotation) { DisplayPropertiesAnnotation* dpa = GuiManager::get()->getBrain()->getDisplayPropertiesAnnotation(); CaretAssert(annotation); switch (annotation->getCoordinateSpace()) { 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::WINDOW: dpa->setDisplayWindowAnnotationsInSingleTabViews(annotation->getWindowIndex(), true); break; } } connectome-workbench-1.2.3+git41-gc4c6c90/src/GuiQt/AnnotationCoordinateSelectionWidget.h000066400000000000000000000071521300200146000311070ustar00rootroot00000000000000#ifndef __ANNOTATION_COORDINATE_SELECTION_WIDGET_H__ #define __ANNOTATION_COORDINATE_SELECTION_WIDGET_H__ /*LICENSE_START*/ /* * Copyright (C) 2015 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 "AnnotationCoordinateSpaceEnum.h" #include "AnnotationTypeEnum.h" #include "UserInputModeAnnotations.h" class QButtonGroup; class QRadioButton; namespace caret { class Annotation; class AnnotationCoordinate; class AnnotationImage; class AnnotationOneDimensionalShape; class AnnotationTwoDimensionalShape; class AnnotationCoordinateSelectionWidget : public QWidget { Q_OBJECT public: AnnotationCoordinateSelectionWidget(const AnnotationTypeEnum::Enum annotationType, const AnnotationCoordinateInformation& coordInfo, const AnnotationCoordinateInformation* optionalSecondCoordInfo, QWidget* parent = 0); virtual ~AnnotationCoordinateSelectionWidget(); void selectCoordinateSpace(const AnnotationCoordinateSpaceEnum::Enum coordSpace); AnnotationCoordinateSpaceEnum::Enum getSelectedCoordinateSpace(bool& validOut) const; bool changeAnnotationCoordinate(Annotation* annotation, QString& errorMessageOut); bool setCoordinateForNewAnnotation(Annotation* annotation, QString& errorMessageOut); // ADD_NEW_METHODS_HERE private: AnnotationCoordinateSelectionWidget(const AnnotationCoordinateSelectionWidget&); AnnotationCoordinateSelectionWidget& operator=(const AnnotationCoordinateSelectionWidget&); void updateAnnotationDisplayProperties(const Annotation* annotation); void setWidthAndHeightForImage(AnnotationImage* imageAnn); QRadioButton* createRadioButtonForSpace(const AnnotationCoordinateSpaceEnum::Enum space); // void setOneDimAnnotationCoordinates(AnnotationOneDimensionalShape* annotation); // // void setTwoDimAnnotationCoordinates(AnnotationTwoDimensionalShape* annotation); QButtonGroup* m_spaceButtonGroup; const AnnotationTypeEnum::Enum m_annotationType; const AnnotationCoordinateInformation& m_coordInfo; const AnnotationCoordinateInformation* m_optionalSecondCoordInfo; static const QString s_SPACE_PROPERTY_NAME; // ADD_NEW_MEMBERS_HERE }; #ifdef __ANNOTATION_COORDINATE_SELECTION_WIDGET_DECLARE__ const QString AnnotationCoordinateSelectionWidget::s_SPACE_PROPERTY_NAME = "SPACE_NAME"; #endif // __ANNOTATION_COORDINATE_SELECTION_WIDGET_DECLARE__ } // namespace #endif //__ANNOTATION_COORDINATE_SELECTION_WIDGET_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/GuiQt/AnnotationCoordinateSpaceWidget.cxx000066400000000000000000000116771300200146000305770ustar00rootroot00000000000000 /*LICENSE_START*/ /* * Copyright (C) 2015 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 __ANNOTATION_COORDINATE_SPACE_WIDGET_DECLARE__ #include "AnnotationCoordinateSpaceWidget.h" #undef __ANNOTATION_COORDINATE_SPACE_WIDGET_DECLARE__ #include #include #include "Annotation.h" #include "CaretAssert.h" #include "WuQtUtilities.h" using namespace caret; /** * \class caret::AnnotationCoordinateSpaceWidget * \brief Widget that displays the coordinate space of selected annotations * \ingroup GuiQt */ /** * Constructor. * * @param browserWindowIndex * Index of window in which this instance is displayed * @param parent * Parent for this widget. */ AnnotationCoordinateSpaceWidget::AnnotationCoordinateSpaceWidget(const int32_t browserWindowIndex, QWidget* parent) : QWidget(parent), m_browserWindowIndex(browserWindowIndex) { m_spaceLabel = new QLabel(" "); m_spaceLabel->setToolTip("Selection annotation(s) space.\n" "Mouse dragging to move/resize\n" "annotations allowed in Tab or \n" "Window space only.\n" " St : Stereotaxic\n" " Sf : Surface\n" " T : Tab\n" " W : Window\n" " + : Multiple Spaces"); QHBoxLayout* layout = new QHBoxLayout(this); WuQtUtilities::setLayoutSpacingAndMargins(layout, 0, 0); layout->addWidget(m_spaceLabel); } /** * Destructor. */ AnnotationCoordinateSpaceWidget::~AnnotationCoordinateSpaceWidget() { } /** * Update the content of this widget with the given text annotation. * * @param annotationTexts * Text annotations for display (may be NULL). */ void AnnotationCoordinateSpaceWidget::updateContent(std::vector annotations) { QString text; if ( ! annotations.empty()) { CaretAssertVectorIndex(annotations, 0); CaretAssert(annotations[0]); AnnotationCoordinateSpaceEnum::Enum space = annotations[0]->getCoordinateSpace(); std::set tabIndices; bool haveMultipleSpacesFlag = false; for (int32_t i = 0; i < static_cast(annotations.size()); i++) { CaretAssertVectorIndex(annotations, i); CaretAssert(annotations[i]); const Annotation* ann = annotations[i]; const AnnotationCoordinateSpaceEnum::Enum annSpace = ann->getCoordinateSpace(); if (annSpace!= space) { haveMultipleSpacesFlag = true; } if (annotations[i]->getCoordinateSpace() == AnnotationCoordinateSpaceEnum::TAB) { tabIndices.insert(ann->getTabIndex()); } } if (haveMultipleSpacesFlag) { text = "+"; } else { text = AnnotationCoordinateSpaceEnum::toGuiAbbreviatedName(space); switch (space) { case AnnotationCoordinateSpaceEnum::STEREOTAXIC: break; case AnnotationCoordinateSpaceEnum::PIXELS: break; case AnnotationCoordinateSpaceEnum::SURFACE: break; case AnnotationCoordinateSpaceEnum::TAB: { QString tabString; for (std::set::iterator iter = tabIndices.begin(); iter != tabIndices.end(); iter++) { if (tabString.isEmpty()) { tabString.append(":"); } else { tabString.append(","); } tabString.append(AString::number(*iter + 1)); } text.append(tabString); } break; case AnnotationCoordinateSpaceEnum::WINDOW: break; } } } m_spaceLabel->setText(text); } connectome-workbench-1.2.3+git41-gc4c6c90/src/GuiQt/AnnotationCoordinateSpaceWidget.h000066400000000000000000000037501300200146000302150ustar00rootroot00000000000000#ifndef __ANNOTATION_COORDINATE_SPACE_WIDGET_H__ #define __ANNOTATION_COORDINATE_SPACE_WIDGET_H__ /*LICENSE_START*/ /* * Copyright (C) 2015 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 namespace caret { class Annotation; class AnnotationCoordinateSpaceWidget : public QWidget { Q_OBJECT public: AnnotationCoordinateSpaceWidget(const int32_t browserWindowIndex, QWidget* parent = 0); virtual ~AnnotationCoordinateSpaceWidget(); void updateContent(std::vector annotations); // ADD_NEW_METHODS_HERE private: AnnotationCoordinateSpaceWidget(const AnnotationCoordinateSpaceWidget&); AnnotationCoordinateSpaceWidget& operator=(const AnnotationCoordinateSpaceWidget&); const int32_t m_browserWindowIndex; QLabel* m_spaceLabel; // ADD_NEW_MEMBERS_HERE }; #ifdef __ANNOTATION_COORDINATE_SPACE_WIDGET_DECLARE__ // #endif // __ANNOTATION_COORDINATE_SPACE_WIDGET_DECLARE__ } // namespace #endif //__ANNOTATION_COORDINATE_SPACE_WIDGET_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/GuiQt/AnnotationCoordinateWidget.cxx000066400000000000000000000505211300200146000276120ustar00rootroot00000000000000 /*LICENSE_START*/ /* * Copyright (C) 2015 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 __ANNOTATION_COORDINATE_WIDGET_DECLARE__ #include "AnnotationCoordinateWidget.h" #undef __ANNOTATION_COORDINATE_WIDGET_DECLARE__ #include #include #include #include #include #include #include "AnnotationColorBar.h" #include "AnnotationManager.h" #include "AnnotationCoordinate.h" #include "AnnotationOneDimensionalShape.h" #include "AnnotationRedoUndoCommand.h" #include "AnnotationTwoDimensionalShape.h" #include "Brain.h" #include "CaretAssert.h" #include "EnumComboBoxTemplate.h" #include "EventGraphicsUpdateAllWindows.h" #include "EventManager.h" #include "EventOverlaySettingsEditorDialogRequest.h" #include "GuiManager.h" #include "StructureEnumComboBox.h" #include "WuQFactory.h" #include "WuQMessageBox.h" #include "WuQtUtilities.h" using namespace caret; /** * \class caret::AnnotationCoordinateWidget * \brief Widget for editing annotation coordinate * \ingroup GuiQt */ /** * Constructor. * * @param * @param whichCoordinate * Which coordinate, one (or only), or two * @param browserWindowIndex * Index of browser window * @param parent * Parent widget */ AnnotationCoordinateWidget::AnnotationCoordinateWidget(const AnnotationWidgetParentEnum::Enum parentWidgetType, const WhichCoordinate whichCoordinate, const int32_t browserWindowIndex, QWidget* parent) : QWidget(parent), m_parentWidgetType(parentWidgetType), m_whichCoordinate(whichCoordinate), m_browserWindowIndex(browserWindowIndex) { m_annotation = NULL; QString colonString; switch (m_parentWidgetType) { case AnnotationWidgetParentEnum::ANNOTATION_TOOL_BAR_WIDGET: colonString = ":"; break; case AnnotationWidgetParentEnum::PARENT_ENUM_FOR_LATER_USE: CaretAssert(0); break; } QLabel* surfaceVertexLabel = new QLabel("Vertex:"); m_surfaceStructureComboBox = new StructureEnumComboBox(this); m_surfaceStructureComboBox->listOnlyValidStructures(); m_surfaceStructureComboBox->getWidget()->setToolTip("Select surface structure"); QObject::connect(m_surfaceStructureComboBox, SIGNAL(structureSelected(const StructureEnum::Enum)), this, SLOT(valueChanged())); m_surfaceNodeIndexSpinBox = new QSpinBox(); m_surfaceNodeIndexSpinBox->setRange(0, 1000000); m_surfaceNodeIndexSpinBox->setSingleStep(1); m_surfaceNodeIndexSpinBox->setToolTip("Select surface vertex"); QObject::connect(m_surfaceNodeIndexSpinBox, SIGNAL(valueChanged(int)), this, SLOT(valueChanged())); m_surfaceOffsetLengthSpinBox = new QDoubleSpinBox(); m_surfaceOffsetLengthSpinBox->setRange(0.0, 999.0); m_surfaceOffsetLengthSpinBox->setSingleStep(1.0); m_surfaceOffsetLengthSpinBox->setToolTip("Offset of annotation from surface vertex"); QObject::connect(m_surfaceOffsetLengthSpinBox, SIGNAL(valueChanged(double)), this, SLOT(valueChanged())); const int digitsRightOfDecimal = 1; QLabel* xCoordLabel = new QLabel(" X" + colonString); m_xCoordSpinBox = WuQFactory::newDoubleSpinBoxWithMinMaxStepDecimalsSignalDouble(0.0, 100.0, 0.1, digitsRightOfDecimal, this, SLOT(valueChanged())); WuQtUtilities::setWordWrappedToolTip(m_xCoordSpinBox, "X-coordinate of annotation\n" " STEREOTAXIC: Stereotaxic X-Coordinate\n" " TAB and WINDOW X-Range: [0.0%, 100.0%]\n" " 0.0% => Left side of tab/window\n" " 100.0% => Right side of tab/window\n"); QLabel* yCoordLabel = new QLabel(" Y" + colonString); m_yCoordSpinBox = WuQFactory::newDoubleSpinBoxWithMinMaxStepDecimalsSignalDouble(0.0, 100.0, 0.1, digitsRightOfDecimal, this, SLOT(valueChanged())); WuQtUtilities::setWordWrappedToolTip(m_yCoordSpinBox, "Y-coordinate of annotation\n" " STEREOTAXIC: Stereotaxic Y-Coordinate\n" " TAB and WINDOW Y-Range: [0.0%, 100.0%]\n" " 0.0% => Bottom of tab/window\n" " 100.0% => Top of tab/window\n"); QLabel* zCoordLabel = new QLabel(" Z" + colonString); m_zCoordSpinBox = WuQFactory::newDoubleSpinBoxWithMinMaxStepDecimalsSignalDouble(-100.0, 100.0, 0.1, digitsRightOfDecimal, this, SLOT(valueChanged())); WuQtUtilities::setWordWrappedToolTip(m_zCoordSpinBox, "Z-coordinate of annotation\n" " STEREOTAXIC: Stereotaxic Z-Coordinate\n" " TAB and WINDOW DEPTH Range: [0.0%, 100.0%]\n" " 0.0% => Toward's viewer\n" " 100.0% => Away from viewer\n"); m_surfaceOffsetVectorTypeComboBox = new EnumComboBoxTemplate(this); m_surfaceOffsetVectorTypeComboBox->setup(); QObject::connect(m_surfaceOffsetVectorTypeComboBox, SIGNAL(itemActivated()), this, SLOT(valueChanged())); m_surfaceOffsetVectorTypeComboBox->getWidget()->setFixedWidth(45); m_surfaceOffsetVectorTypeComboBox->getWidget()->setToolTip("Vector for surface offset:\n" " C - Centroid thru Vertex\n" " N - Vertex Normal"); m_plusButtonToolTipText = ("Click the mouse to set the new location for the coordinate.\n" "After clicking the mouse, a dialog allows selection of the\n" "coordinate space."); QToolButton* setCoordinateToolButton = NULL; switch (m_parentWidgetType) { case AnnotationWidgetParentEnum::ANNOTATION_TOOL_BAR_WIDGET: // disabled as change space cause grouping problems. setCoordinateToolButton = new QToolButton(); break; case AnnotationWidgetParentEnum::PARENT_ENUM_FOR_LATER_USE: CaretAssert(0); break; } if (setCoordinateToolButton != NULL) { QAction* setCoordinateAction = WuQtUtilities::createAction("+", ("After pressing this button: \n" + m_plusButtonToolTipText), this, this, SLOT(setCoordinateActionTriggered())); setCoordinateToolButton->setDefaultAction(setCoordinateAction); } m_surfaceWidget = new QWidget(); QHBoxLayout* surfaceLayout = new QHBoxLayout(m_surfaceWidget); WuQtUtilities::setLayoutSpacingAndMargins(surfaceLayout, 2, 0); surfaceLayout->addWidget(surfaceVertexLabel); surfaceLayout->addWidget(m_surfaceStructureComboBox->getWidget()); surfaceLayout->addWidget(m_surfaceNodeIndexSpinBox); surfaceLayout->addWidget(m_surfaceOffsetVectorTypeComboBox->getWidget()); surfaceLayout->addWidget(m_surfaceOffsetLengthSpinBox); m_coordinateWidget = new QWidget(); QHBoxLayout* coordinateLayout = new QHBoxLayout(m_coordinateWidget); WuQtUtilities::setLayoutSpacingAndMargins(coordinateLayout, 2, 0); coordinateLayout->addWidget(xCoordLabel); coordinateLayout->addWidget(m_xCoordSpinBox); coordinateLayout->addWidget(yCoordLabel); coordinateLayout->addWidget(m_yCoordSpinBox); coordinateLayout->addWidget(zCoordLabel); coordinateLayout->addWidget(m_zCoordSpinBox); QHBoxLayout* layout = new QHBoxLayout(this); WuQtUtilities::setLayoutSpacingAndMargins(layout, 2, 2); layout->addWidget(m_surfaceWidget); layout->addWidget(m_coordinateWidget); if (setCoordinateToolButton != NULL) { layout->addWidget(setCoordinateToolButton); } setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); } /** * Destructor. */ AnnotationCoordinateWidget::~AnnotationCoordinateWidget() { } AnnotationCoordinate* AnnotationCoordinateWidget::getCoordinate() { AnnotationCoordinate* ac = NULL; if (m_annotation != NULL) { AnnotationOneDimensionalShape* oneDimShape = dynamic_cast(m_annotation); AnnotationTwoDimensionalShape* twoDimShape = dynamic_cast(m_annotation); switch (m_whichCoordinate) { case COORDINATE_ONE: if (oneDimShape != NULL) { ac = oneDimShape->getStartCoordinate(); } else if (twoDimShape != NULL) { ac = twoDimShape->getCoordinate(); } break; case COORDINATE_TWO: if (oneDimShape != NULL) { ac = oneDimShape->getEndCoordinate(); } break; } } return ac; } /** * Update with the given annotation coordinate. * * @param coordinate. */ void AnnotationCoordinateWidget::updateContent(Annotation* annotation) { m_annotation = annotation; m_surfaceStructureComboBox->listOnlyValidStructures(); const AnnotationCoordinate* coordinate = getCoordinate(); 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("%"); switch (m_annotation->getCoordinateSpace()) { case AnnotationCoordinateSpaceEnum::STEREOTAXIC: xyMax = 10000.0; xyMin = -xyMax; zMin = xyMin; zMax = xyMax; xyzStep = 1.0; suffix.clear(); break; case AnnotationCoordinateSpaceEnum::PIXELS: break; case AnnotationCoordinateSpaceEnum::SURFACE: surfaceFlag = true; break; case AnnotationCoordinateSpaceEnum::TAB: zMin = 0.0; break; case AnnotationCoordinateSpaceEnum::WINDOW: zMin = 0.0; break; } float xyz[3]; coordinate->getXYZ(xyz); m_xCoordSpinBox->blockSignals(true); m_xCoordSpinBox->setRange(xyMin, xyMax); m_xCoordSpinBox->setSingleStep(xyzStep); m_xCoordSpinBox->setSuffix(suffix); m_xCoordSpinBox->setValue(xyz[0]); m_xCoordSpinBox->blockSignals(false); m_yCoordSpinBox->blockSignals(true); m_yCoordSpinBox->setRange(xyMin, xyMax); m_yCoordSpinBox->setSingleStep(xyzStep); m_yCoordSpinBox->setSuffix(suffix); m_yCoordSpinBox->setValue(xyz[1]); m_yCoordSpinBox->blockSignals(false); m_zCoordSpinBox->blockSignals(true); m_zCoordSpinBox->setRange(zMin, zMax); m_zCoordSpinBox->setSingleStep(xyzStep); m_zCoordSpinBox->setSuffix(suffix); m_zCoordSpinBox->setValue(xyz[2]); m_zCoordSpinBox->blockSignals(false); if (surfaceFlag) { StructureEnum::Enum structure = StructureEnum::INVALID; int32_t surfaceNumberOfNodes = -1; int32_t surfaceNodeIndex = -1; float surfaceOffsetLength = AnnotationCoordinate::getDefaultSurfaceOffsetLength(); AnnotationSurfaceOffsetVectorTypeEnum::Enum surfaceOffsetVector = AnnotationSurfaceOffsetVectorTypeEnum::CENTROID_THRU_VERTEX; coordinate->getSurfaceSpace(structure, surfaceNumberOfNodes, surfaceNodeIndex, surfaceOffsetLength, surfaceOffsetVector); m_surfaceStructureComboBox->setSelectedStructure(structure); m_surfaceNodeIndexSpinBox->blockSignals(true); m_surfaceNodeIndexSpinBox->setValue(surfaceNodeIndex); m_surfaceNodeIndexSpinBox->blockSignals(false); m_surfaceOffsetVectorTypeComboBox->setSelectedItem(surfaceOffsetVector); m_surfaceOffsetLengthSpinBox->blockSignals(true); m_surfaceOffsetLengthSpinBox->setValue(surfaceOffsetLength); m_surfaceOffsetLengthSpinBox->blockSignals(false); } setEnabled(true); } else { setEnabled(false); } m_surfaceWidget->setVisible(surfaceFlag); m_coordinateWidget->setVisible( ! surfaceFlag); } /** * Gets called when a coordinate value is changed. */ void AnnotationCoordinateWidget::valueChanged() { const AnnotationCoordinate* coordinate = getCoordinate(); if ((m_annotation != NULL) && (coordinate != NULL)) { bool surfaceFlag = false; switch (m_annotation->getCoordinateSpace()) { case AnnotationCoordinateSpaceEnum::STEREOTAXIC: break; case AnnotationCoordinateSpaceEnum::PIXELS: break; case AnnotationCoordinateSpaceEnum::SURFACE: surfaceFlag = true; break; case AnnotationCoordinateSpaceEnum::TAB: break; case AnnotationCoordinateSpaceEnum::WINDOW: break; } switch (m_parentWidgetType) { case AnnotationWidgetParentEnum::ANNOTATION_TOOL_BAR_WIDGET: { AnnotationCoordinate coordinateCopy(*coordinate); if (surfaceFlag) { StructureEnum::Enum structure = StructureEnum::INVALID; int32_t surfaceNumberOfNodes = -1; int32_t surfaceNodeIndex = -1; float surfaceOffsetLength = AnnotationCoordinate::getDefaultSurfaceOffsetLength(); AnnotationSurfaceOffsetVectorTypeEnum::Enum surfaceOffsetVector = AnnotationSurfaceOffsetVectorTypeEnum::CENTROID_THRU_VERTEX; coordinate->getSurfaceSpace(structure, surfaceNumberOfNodes, surfaceNodeIndex, surfaceOffsetLength, surfaceOffsetVector); structure = m_surfaceStructureComboBox->getSelectedStructure(); surfaceNodeIndex = m_surfaceNodeIndexSpinBox->value(); surfaceOffsetLength = m_surfaceOffsetLengthSpinBox->value(); surfaceOffsetVector = m_surfaceOffsetVectorTypeComboBox->getSelectedItem(); coordinateCopy.setSurfaceSpace(structure, surfaceNumberOfNodes, surfaceNodeIndex, surfaceOffsetLength, surfaceOffsetVector); } else { float xyz[3] = { m_xCoordSpinBox->value(), m_yCoordSpinBox->value(), m_zCoordSpinBox->value() }; coordinateCopy.setXYZ(xyz); } std::vector selectedAnnotations; selectedAnnotations.push_back(m_annotation); bool updateUserInterfaceFlag = false; for (std::vector::iterator annIter = selectedAnnotations.begin(); annIter != selectedAnnotations.end(); annIter++) { Annotation* ann = *annIter; if (ann->getType() == AnnotationTypeEnum::COLOR_BAR) { AnnotationColorBar* colorBar = dynamic_cast(ann); CaretAssert(colorBar); colorBar->setPositionMode(AnnotationColorBarPositionModeEnum::MANUAL); updateUserInterfaceFlag = true; } } AnnotationRedoUndoCommand* undoCommand = new AnnotationRedoUndoCommand(); switch (m_whichCoordinate) { case COORDINATE_ONE: undoCommand->setModeCoordinateOne(coordinateCopy, selectedAnnotations); break; case COORDINATE_TWO: undoCommand->setModeCoordinateTwo(coordinateCopy, selectedAnnotations); break; } AnnotationManager* annMan = GuiManager::get()->getBrain()->getAnnotationManager(); AString errorMessage; if ( ! annMan->applyCommand(undoCommand, errorMessage)) { WuQMessageBox::errorOk(this, errorMessage); } EventManager::get()->sendSimpleEvent(EventTypeEnum::EVENT_ANNOTATION_TOOLBAR_UPDATE); if (updateUserInterfaceFlag) { EventManager::get()->sendEvent(EventOverlaySettingsEditorDialogRequest(EventOverlaySettingsEditorDialogRequest::MODE_UPDATE_ALL, m_browserWindowIndex, NULL, NULL, -1).getPointer()); } } break; case AnnotationWidgetParentEnum::PARENT_ENUM_FOR_LATER_USE: CaretAssert(0); break; } EventManager::get()->sendEvent(EventGraphicsUpdateAllWindows().getPointer()); } } /** * Called when the set coordinate button is clicked. */ void AnnotationCoordinateWidget::setCoordinateActionTriggered() { QPoint middlePos(width() / 2, height() / 2); QToolTip::showText(mapToGlobal(middlePos), m_plusButtonToolTipText, this); signalSelectCoordinateWithMouse(); } connectome-workbench-1.2.3+git41-gc4c6c90/src/GuiQt/AnnotationCoordinateWidget.h000066400000000000000000000063471300200146000272460ustar00rootroot00000000000000#ifndef __ANNOTATION_COORDINATE_WIDGET_H__ #define __ANNOTATION_COORDINATE_WIDGET_H__ /*LICENSE_START*/ /* * Copyright (C) 2015 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 "AnnotationCoordinateSpaceEnum.h" #include "AnnotationWidgetParentEnum.h" class QDoubleSpinBox; class QSpinBox; namespace caret { class Annotation; class AnnotationCoordinate; class EnumComboBoxTemplate; class StructureEnumComboBox; class AnnotationCoordinateWidget : public QWidget { Q_OBJECT public: enum WhichCoordinate { COORDINATE_ONE, COORDINATE_TWO }; AnnotationCoordinateWidget(const AnnotationWidgetParentEnum::Enum parentWidgetType, const WhichCoordinate whichCoordinate, const int32_t browserWindowIndex, QWidget* parent = 0); virtual ~AnnotationCoordinateWidget(); // ADD_NEW_METHODS_HERE void updateContent(Annotation* annotation); signals: void signalSelectCoordinateWithMouse(); private slots: void valueChanged(); void setCoordinateActionTriggered(); private: AnnotationCoordinateWidget(const AnnotationCoordinateWidget&); AnnotationCoordinateWidget& operator=(const AnnotationCoordinateWidget&); AnnotationCoordinate* getCoordinate(); // ADD_NEW_MEMBERS_HERE const AnnotationWidgetParentEnum::Enum m_parentWidgetType; const WhichCoordinate m_whichCoordinate; const int32_t m_browserWindowIndex; QWidget* m_surfaceWidget; QWidget* m_coordinateWidget; StructureEnumComboBox* m_surfaceStructureComboBox; QSpinBox* m_surfaceNodeIndexSpinBox; EnumComboBoxTemplate* m_surfaceOffsetVectorTypeComboBox; QDoubleSpinBox* m_surfaceOffsetLengthSpinBox; QDoubleSpinBox* m_xCoordSpinBox; QDoubleSpinBox* m_yCoordSpinBox; QDoubleSpinBox* m_zCoordSpinBox; Annotation* m_annotation; AString m_plusButtonToolTipText; }; #ifdef __ANNOTATION_COORDINATE_WIDGET_DECLARE__ // #endif // __ANNOTATION_COORDINATE_WIDGET_DECLARE__ } // namespace #endif //__ANNOTATION_COORDINATE_WIDGET_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/GuiQt/AnnotationCreateDialog.cxx000066400000000000000000001202431300200146000267010ustar00rootroot00000000000000 /*LICENSE_START*/ /* * Copyright (C) 2015 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 __ANNOTATION_CREATE_DIALOG_DECLARE__ #include "AnnotationCreateDialog.h" #undef __ANNOTATION_CREATE_DIALOG_DECLARE__ #include #include #include #include #include #include #include #include "AnnotationBox.h" #include "AnnotationFile.h" #include "AnnotationImage.h" #include "AnnotationManager.h" #include "AnnotationPercentSizeText.h" #include "AnnotationRedoUndoCommand.h" #include "Brain.h" #include "BrainBrowserWindow.h" #include "BrainOpenGLWidget.h" #include "BrainOpenGLViewportContent.h" #include "BrowserTabContent.h" #include "CaretAssert.h" #include "CaretFileDialog.h" #include "CaretLogger.h" #include "CaretPointer.h" #include "DataFileException.h" #include "DisplayPropertiesAnnotation.h" #include "EnumComboBoxTemplate.h" #include "EventDataFileAdd.h" #include "EventGraphicsUpdateAllWindows.h" #include "EventManager.h" #include "EventUserInterfaceUpdate.h" #include "GuiManager.h" #include "ImageFile.h" #include "ModelSurfaceMontage.h" #include "MouseEvent.h" #include "WuQtUtilities.h" #include "WuQMessageBox.h" using namespace caret; /** * \class caret::AnnotationCreateDialog * \brief Dialog used for creating new annotations. * \ingroup GuiQt */ /** * Creating a new annotation using an annotation space and type. * A dialog may be displayed when the coordinate space is not valid * for the window location or for text and image annotations. * * @param mouseEvent * The mouse event indicating where user clicked in the window * @param annotationSpace * Space of annotation being created. * @param annotationType * Type of annotation that is being created. * @param annotationFile * File to which new annotation is added. * @return * Pointer to annotation that was created or NULL * if annotation not created (user cancelled). */ Annotation* AnnotationCreateDialog::newAnnotationFromSpaceAndType(const MouseEvent& mouseEvent, const AnnotationCoordinateSpaceEnum::Enum annotationSpace, const AnnotationTypeEnum::Enum annotationType, AnnotationFile* annotationFile) { Annotation* newAnnotation = newAnnotationFromSpaceTypeAndCoords(MODE_NEW_ANNOTATION_TYPE_CLICK, mouseEvent, annotationSpace, annotationType, annotationFile); return newAnnotation; } /** * Creating a new annotation using an annotation space and type. * A dialog may be displayed when the coordinate space is not valid * for the window location or for text and image annotations. * * @param mouseEvent * The mouse event indicating where user clicked in the window * @param annotationSpace * Space of annotation being created. * @param annotationType * Type of annotation that is being created. * @param annotationFile * File to which new annotation is added. * @return * Pointer to annotation that was created or NULL * if annotation not created (user cancelled). */ Annotation* AnnotationCreateDialog::newAnnotationFromSpaceTypeAndBounds(const MouseEvent& mouseEvent, const AnnotationCoordinateSpaceEnum::Enum annotationSpace, const AnnotationTypeEnum::Enum annotationType, AnnotationFile* annotationFile) { Annotation* newAnnotation = newAnnotationFromSpaceTypeAndCoords(MODE_NEW_ANNOTATION_TYPE_FROM_BOUNDS, mouseEvent, annotationSpace, annotationType, annotationFile); return newAnnotation; } /** * Creating a new annotation using an annotation space and type. * A dialog may be displayed when the coordinate space is not valid * for the window location or for text and image annotations. * * @param mode * The mode. * @param mouseEvent * The mouse event indicating where user clicked in the window * @param annotationSpace * Space of annotation being created. * @param annotationType * Type of annotation that is being created. * @param coordOne * The first coordinate MUST BE VALID (NOT NULL). * @param coordTwo * Optional second coordinate (NULL if invalid). * @param annotationFile * File to which new annotation is added. * @return * Pointer to annotation that was created or NULL * if annotation not created (user cancelled). */ Annotation* AnnotationCreateDialog::newAnnotationFromSpaceTypeAndCoords(const Mode mode, const MouseEvent& mouseEvent, const AnnotationCoordinateSpaceEnum::Enum annotationSpace, const AnnotationTypeEnum::Enum annotationType, AnnotationFile* annotationFile) { bool useBothFlag = false; switch (mode) { case MODE_NEW_ANNOTATION_TYPE_CLICK: break; case MODE_NEW_ANNOTATION_TYPE_FROM_BOUNDS: useBothFlag = true; break; } NewAnnotationInfo newInfo(mouseEvent, annotationSpace, annotationType, useBothFlag, annotationFile); if ( ! newInfo.isValid()) { CaretAssert(0); WuQMessageBox::errorOk(mouseEvent.getOpenGLWidget(), "PROGRAM ERROR: Failed to create coordinate information."); return NULL; } bool needImageOrTextFlag = false; switch (annotationType) { case AnnotationTypeEnum::BOX: break; case AnnotationTypeEnum::COLOR_BAR: CaretAssertMessage(0, "Colorbars do not get created !!!"); break; case AnnotationTypeEnum::IMAGE: needImageOrTextFlag = true; break; case AnnotationTypeEnum::LINE: break; case AnnotationTypeEnum::OVAL: break; case AnnotationTypeEnum::TEXT: needImageOrTextFlag = true; break; } bool needToLaunchDialogFlag = false; if (newInfo.isSelectedSpaceValid()) { if (needImageOrTextFlag) { needToLaunchDialogFlag = true; } else { Annotation* newAnn = createAnnotation(newInfo, annotationSpace); if (newAnn != NULL) { DisplayPropertiesAnnotation* dpa = GuiManager::get()->getBrain()->getDisplayPropertiesAnnotation(); dpa->updateForNewAnnotation(newAnn); EventManager::get()->sendEvent(EventGraphicsUpdateAllWindows().getPointer()); EventManager::get()->sendEvent(EventUserInterfaceUpdate().getPointer()); return newAnn; } delete newAnn; needToLaunchDialogFlag = true; } } else { needToLaunchDialogFlag = true; } if (needToLaunchDialogFlag) { AnnotationCreateDialog annDialog(mode, newInfo, newInfo.m_selectedSpace, newInfo.isSelectedSpaceValid(), mouseEvent.getOpenGLWidget()); if (annDialog.exec() == AnnotationCreateDialog::Accepted) { return annDialog.getAnnotationThatWasCreated(); } } return NULL; } /** * Create a new annotation. * * @param newAnnotationInfo * Information about the new annotation. * @param annotationSpace * Coordinate space for new annotaiton. */ Annotation* AnnotationCreateDialog::createAnnotation(NewAnnotationInfo& newAnnotationInfo, const AnnotationCoordinateSpaceEnum::Enum annotationSpace) { Annotation* newAnnotation = NULL; if (newAnnotationInfo.m_annotationType == AnnotationTypeEnum::TEXT) { newAnnotation = new AnnotationPercentSizeText(AnnotationAttributesDefaultTypeEnum::USER); /* * In surface montage, percentage size text may need alteration */ bool adjustTextPctSizeFlag = false; switch (annotationSpace) { case AnnotationCoordinateSpaceEnum::PIXELS: break; case AnnotationCoordinateSpaceEnum::STEREOTAXIC: adjustTextPctSizeFlag = true; break; case AnnotationCoordinateSpaceEnum::SURFACE: adjustTextPctSizeFlag = true; break; case AnnotationCoordinateSpaceEnum::TAB: break; case AnnotationCoordinateSpaceEnum::WINDOW: break; } if (adjustTextPctSizeFlag) { AnnotationPercentSizeText* pctText = dynamic_cast(newAnnotation); if (pctText != NULL) { const int32_t browserWindowIndex = newAnnotationInfo.m_mouseEvent.getBrowserWindowIndex(); BrowserTabContent* browserTabContent = GuiManager::get()->getBrowserTabContentForBrowserWindow(browserWindowIndex, false); if (browserTabContent != NULL) { ModelSurfaceMontage* msm = browserTabContent->getDisplayedSurfaceMontageModel(); if (msm != NULL) { const int32_t tabIndex = browserTabContent->getTabNumber(); int32_t rowCount = 1; int32_t columnCount = 1; msm->getSurfaceMontageNumberOfRowsAndColumns(tabIndex, rowCount, columnCount); if (rowCount > 0) { float pctSize = pctText->getFontPercentViewportSize(); pctSize *= static_cast(rowCount); pctText->setFontPercentViewportSize(pctSize); } } } } } } else { newAnnotation = Annotation::newAnnotationOfType(newAnnotationInfo.m_annotationType, AnnotationAttributesDefaultTypeEnum::USER); } AnnotationCoordinateInformation* coordTwo = NULL; if (newAnnotationInfo.m_coordTwoInfoValid) { coordTwo = &newAnnotationInfo.m_coordTwoInfo; } const bool validFlag = AnnotationCoordinateInformation::setAnnotationCoordinatesForSpace(newAnnotation, annotationSpace, &newAnnotationInfo.m_coordOneInfo, coordTwo); if (validFlag) { if ((newAnnotationInfo.m_percentageWidth > 0) && (newAnnotationInfo.m_percentageHeight > 0)) { AnnotationTwoDimensionalShape* twoDimShape = dynamic_cast(newAnnotation); if (twoDimShape != NULL) { twoDimShape->setWidth(newAnnotationInfo.m_percentageWidth); twoDimShape->setHeight(newAnnotationInfo.m_percentageHeight); } } finishAnnotationCreation(newAnnotationInfo.m_annotationFile, newAnnotation, newAnnotationInfo.m_mouseEvent.getBrowserWindowIndex()); return newAnnotation; } else { CaretAssertMessage(0, "Space should have been valid, we should never get here."); delete newAnnotation; return NULL; } } /** * Dialog constructor. * * @param mode * The dialog's mode. * @param newAnnotationInfo * Information for creating new annotation. * @param annotationSpace * Space of annotation that is being created. * @param annotationSpaceValidFlag * True if annotation space is valid (do not need space selection). * @param parent * Optional parent for this dialog. */ AnnotationCreateDialog::AnnotationCreateDialog(const Mode mode, NewAnnotationInfo& newAnnotationInfo, const AnnotationCoordinateSpaceEnum::Enum annotationSpace, const bool annotationSpaceValidFlag, QWidget* parent) : WuQDialogModal("New Annotation", parent), m_mode(mode), m_newAnnotationInfo(newAnnotationInfo), m_annotationSpace(annotationSpace), m_annotationThatWasCreated(NULL), m_imageWidth(0), m_imageHeight(0) { m_annotationSpaceButtonGroup = NULL; m_textEdit = NULL; QGroupBox* coordGroupBox = NULL; if ( ! annotationSpaceValidFlag) { coordGroupBox = new QGroupBox("Coordinate Space"); QVBoxLayout* coordGroupLayout = new QVBoxLayout(coordGroupBox); coordGroupLayout->setMargin(0); m_annotationSpaceButtonGroup = new QButtonGroup(this); for (std::vector::iterator iter = m_newAnnotationInfo.m_validSpaces.begin(); iter != m_newAnnotationInfo.m_validSpaces.end(); iter++) { const AnnotationCoordinateSpaceEnum::Enum space = *iter; QRadioButton* rb = new QRadioButton(AnnotationCoordinateSpaceEnum::toGuiName(space)); m_annotationSpaceButtonGroup->addButton(rb, AnnotationCoordinateSpaceEnum::toIntegerCode(space)); coordGroupLayout->addWidget(rb); } } QWidget* textWidget = ((m_newAnnotationInfo.m_annotationType == AnnotationTypeEnum::TEXT) ? createTextWidget() : NULL); QWidget* imageWidget = ((m_newAnnotationInfo.m_annotationType == AnnotationTypeEnum::IMAGE) ? createImageWidget() : NULL); QWidget* dialogWidget = new QWidget(); QVBoxLayout* layout = new QVBoxLayout(dialogWidget); if (coordGroupBox != NULL) { const QString message("The location for the new annotation is incompatible with the " "coordinate space selected in the toolbar. " "Choose one of the coordinate " "spaces below to create the annotation or press Cancel to cancel creation " "of the annotation."); QLabel* messageLabel = new QLabel(message); messageLabel->setWordWrap(true); QLabel* spaceLabel = new QLabel("Space selected in Toolbar: " + AnnotationCoordinateSpaceEnum::toGuiName(annotationSpace)); spaceLabel->setWordWrap(false); layout->addWidget(spaceLabel); layout->addSpacing(10); layout->addWidget(messageLabel); layout->addSpacing(10); layout->addWidget(coordGroupBox); } if (textWidget != NULL) { layout->addWidget(textWidget); } if (imageWidget != NULL) { layout->addWidget(imageWidget); } dialogWidget->setSizePolicy(dialogWidget->sizePolicy().horizontalPolicy(), QSizePolicy::Fixed); setCentralWidget(dialogWidget, SCROLL_AREA_NEVER); if (m_newAnnotationInfo.m_annotationType == AnnotationTypeEnum::TEXT) { CaretAssert(m_textEdit); m_textEdit->setFocus(); } } /** * Destructor. */ AnnotationCreateDialog::~AnnotationCreateDialog() { } /** * @return Annotation that was created by dialog (NULL if annotation NOT created). */ Annotation* AnnotationCreateDialog::getAnnotationThatWasCreated() { return m_annotationThatWasCreated; } /** * @return New instance of text widget. */ QWidget* AnnotationCreateDialog::createTextWidget() { m_textEdit = new QTextEdit(); m_textEdit->setText(""); m_textEdit->selectAll(); QGroupBox* groupBox = new QGroupBox("Text"); QHBoxLayout* layout = new QHBoxLayout(groupBox); layout->addWidget(m_textEdit, 100); return groupBox; } /** * @return New instance of image widget. */ QWidget* AnnotationCreateDialog::createImageWidget() { QAction* newFileAction = WuQtUtilities::createAction("Choose Image File...", "Choose image file using file selection dialog", this, this, SLOT(selectImageButtonClicked())); QToolButton* newFileToolButton = new QToolButton(); newFileToolButton->setDefaultAction(newFileAction); m_imageFileNameLabel = new QLabel(); m_imageThumbnailLabel = new QLabel(); m_imageThumbnailLabel->setFixedSize(s_MAXIMUM_THUMB_NAIL_SIZE, s_MAXIMUM_THUMB_NAIL_SIZE); QHBoxLayout* nameLayout = new QHBoxLayout(); nameLayout->addWidget(newFileToolButton); nameLayout->addWidget(m_imageFileNameLabel); QGroupBox* groupBox = new QGroupBox("Image File"); QVBoxLayout* layout = new QVBoxLayout(groupBox); layout->addLayout(nameLayout); layout->addWidget(WuQtUtilities::createHorizontalLineWidget()); layout->addWidget(m_imageThumbnailLabel); return groupBox; } /** * Invalidate the image. */ void AnnotationCreateDialog::invalidateImage() { m_imageRgbaBytes.clear(); m_imageWidth = 0; m_imageHeight = 0; m_imageFileNameLabel->setText(""); m_imageThumbnailLabel->setPixmap(QPixmap()); } /** * Called when the select image button is clicked. */ void AnnotationCreateDialog::selectImageButtonClicked() { const AString fileDialogSettingsName("AnnotImageDialog"); /* * Setup file selection dialog. */ CaretFileDialog fd(this); fd.setAcceptMode(CaretFileDialog::AcceptOpen); fd.setNameFilter(DataFileTypeEnum::toQFileDialogFilter(DataFileTypeEnum::IMAGE)); fd.setFileMode(CaretFileDialog::ExistingFile); fd.setViewMode(CaretFileDialog::List); fd.setLabelText(CaretFileDialog::Accept, "Choose"); // OK button shows Insert fd.restoreDialogSettings(fileDialogSettingsName); AString errorMessages; if (fd.exec() == CaretFileDialog::Accepted) { invalidateImage(); fd.saveDialogSettings(fileDialogSettingsName); QStringList selectedFiles = fd.selectedFiles(); if ( ! selectedFiles.empty()) { const AString imageFileName = selectedFiles.at(0); ImageFile imageFile; try { imageFile.readFile(imageFileName); } catch (const DataFileException& dfe) { WuQMessageBox::errorOk(this, dfe.whatString()); return; } imageFile.getImageBytesRGBA(ImageFile::IMAGE_DATA_ORIGIN_AT_BOTTOM, m_imageRgbaBytes, m_imageWidth, m_imageHeight); if ((m_imageWidth <= 0) || (m_imageHeight <= 0)) { WuQMessageBox::errorOk(this, ("Image Width=" + QString::number(m_imageWidth) + " or Height=" + QString::number(m_imageHeight) + " is invalid.")); invalidateImage(); return; } const int32_t expectedNumberOfBytes = (m_imageWidth * m_imageHeight * 4); if (static_cast(m_imageRgbaBytes.size()) != expectedNumberOfBytes) { WuQMessageBox::errorOk(this, "Image bytes size should be " + QString::number(expectedNumberOfBytes) + " but is " + QString::number(m_imageRgbaBytes.size())); invalidateImage(); return; } m_imageFileNameLabel->setText(imageFile.getFileNameNoPath()); imageFile.resizeToMaximumWidthOrHeight(s_MAXIMUM_THUMB_NAIL_SIZE); const QImage* qImage = imageFile.getAsQImage(); m_imageThumbnailLabel->setPixmap(QPixmap::fromImage(*qImage)); } } } /** * Gets called when the OK button is clicked. */ void AnnotationCreateDialog::okButtonClicked() { AString errorMessage; QString userText; if (m_newAnnotationInfo.m_annotationType == AnnotationTypeEnum::TEXT) { userText = m_textEdit->toPlainText(); if (userText.isEmpty()) { errorMessage.appendWithNewLine("Text is missing."); } } if (m_newAnnotationInfo.m_annotationType == AnnotationTypeEnum::IMAGE) { if ((m_imageWidth <= 0) || (m_imageHeight <= 0) || (m_imageRgbaBytes.empty())) { errorMessage = "Image File is invalid. Choose Image File."; } } if ( ! errorMessage.isEmpty()) { WuQMessageBox::errorOk(this, errorMessage); return; } AnnotationCoordinateSpaceEnum::Enum space = m_newAnnotationInfo.m_selectedSpace; if (m_annotationSpaceButtonGroup != NULL) { const int id = m_annotationSpaceButtonGroup->checkedId(); bool validFlag = false; space = AnnotationCoordinateSpaceEnum::fromIntegerCode(id, &validFlag); if ( ! validFlag) { WuQMessageBox::errorOk(this, "No Space is selected."); return; } } CaretAssert(m_newAnnotationInfo.m_annotationFile); CaretPointer annotation; annotation.grabNew(NULL); annotation.grabNew(createAnnotation(m_newAnnotationInfo, space)); if (annotation == NULL) { WuQMessageBox::errorOk(this, "Failed to create annotation in space: " + AnnotationCoordinateSpaceEnum::toGuiName(space)); return; } if (m_newAnnotationInfo.m_annotationType == AnnotationTypeEnum::TEXT) { AnnotationText* text = dynamic_cast(annotation.getPointer()); CaretAssert(text); text->setText(userText); } AnnotationImage* annImage = dynamic_cast(annotation.getPointer()); if (annImage != NULL) { annImage->setImageBytesRGBA(&m_imageRgbaBytes[0], m_imageWidth, m_imageHeight); } /* * Need to release annotation from its CaretPointer since the * annotation file will take ownership of the annotation. */ Annotation* annotationPointer = annotation.releasePointer(); m_annotationThatWasCreated = annotationPointer; DisplayPropertiesAnnotation* dpa = GuiManager::get()->getBrain()->getDisplayPropertiesAnnotation(); dpa->updateForNewAnnotation(m_annotationThatWasCreated); EventManager::get()->sendEvent(EventGraphicsUpdateAllWindows().getPointer()); EventManager::get()->sendEvent(EventUserInterfaceUpdate().getPointer()); WuQDialog::okButtonClicked(); } /** * Finish the creation of an annotation. * * @param annotationFile * File to which annotation is added. * @param annotation * Annotation that was created. * @param browserWindowIndex * Index of window in which annotation was created. */ void AnnotationCreateDialog::finishAnnotationCreation(AnnotationFile* annotationFile, Annotation* annotation, const int32_t browswerWindowIndex) { AnnotationManager* annotationManager = GuiManager::get()->getBrain()->getAnnotationManager(); /* * Add annotation to its file */ AnnotationRedoUndoCommand* undoCommand = new AnnotationRedoUndoCommand(); undoCommand->setModeCreateAnnotation(annotationFile, annotation); AString errorMessage; if ( ! annotationManager->applyCommand(undoCommand, errorMessage)) { WuQMessageBox::errorOk(GuiManager::get()->getBrowserWindowByWindowIndex(browswerWindowIndex), errorMessage); } annotationManager->selectAnnotationForEditing(browswerWindowIndex, AnnotationManager::SELECTION_MODE_SINGLE, false, annotation); } /** * 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); } } /** * When the user drags to create an annotation, two points are * used at opposite corners. For non-linear annotations, we * need a center, width, and height. So for these types, * convert the two points to one point with center, width, * and height. */ void AnnotationCreateDialog::NewAnnotationInfo::processTwoCoordInfo() { if ((m_coordOneInfo.m_windowIndex >= 0) && (m_coordTwoInfo.m_windowIndex >= 0)) { bool useAverageFlag = false; bool useTextAligmentFlag = false; switch (m_annotationType) { case AnnotationTypeEnum::BOX: useAverageFlag = true; break; case AnnotationTypeEnum::COLOR_BAR: useAverageFlag = true; break; case AnnotationTypeEnum::IMAGE: useAverageFlag = true; break; case AnnotationTypeEnum::OVAL: useAverageFlag = true; break; case AnnotationTypeEnum::LINE: useAverageFlag = false; break; case AnnotationTypeEnum::TEXT: useTextAligmentFlag = true; break; } if (useAverageFlag || useTextAligmentFlag) { int32_t windowPixelX = m_coordOneInfo.m_windowPixelXYZ[0]; int32_t windowPixelY = m_coordOneInfo.m_windowPixelXYZ[1]; int32_t windowTwoPixelX = m_coordTwoInfo.m_windowPixelXYZ[0]; int32_t windowTwoPixelY = m_coordTwoInfo.m_windowPixelXYZ[1]; if ((windowPixelX >= 0) && (windowPixelY >= 0) && (windowTwoPixelX >= 0) && (windowTwoPixelY >= 0)) { const float minX = std::min(windowPixelX, windowTwoPixelX); const float minY = std::min(windowPixelY, windowTwoPixelY); const float maxX = std::max(windowPixelX, windowTwoPixelX); const float maxY = std::max(windowPixelY, windowTwoPixelY); const float centerX = (windowPixelX + windowTwoPixelX) / 2.0; const float centerY = (windowPixelY + windowTwoPixelY) / 2.0; if (useAverageFlag) { /* * Width and height in pixels */ const float pixelWidth = maxX - minX; const float pixelHeight = maxY - minY; float viewportWidth = 0.0; float viewportHeight = 0.0; switch (m_selectedSpace) { case AnnotationCoordinateSpaceEnum::PIXELS: break; case AnnotationCoordinateSpaceEnum::STEREOTAXIC: case AnnotationCoordinateSpaceEnum::SURFACE: { int viewport[4]; m_mouseEvent.getViewportContent()->getModelViewport(viewport); viewportWidth = viewport[2]; viewportHeight = viewport[3]; float subWidth = 0.0; float subHeight = 0.0; if (adjustViewportForSurfaceMontage(m_mouseEvent.getViewportContent()->getBrowserTabContent(), viewport, subWidth, subHeight)) { // std::cout << "Changing " // << viewportWidth << ", " << viewportHeight << " to " // << subWidth << ", " << subHeight << std::endl; viewportWidth = subWidth; viewportHeight = subHeight; } break; } case AnnotationCoordinateSpaceEnum::TAB: { int viewport[4]; m_mouseEvent.getViewportContent()->getModelViewport(viewport); viewportWidth = viewport[2]; viewportHeight = viewport[3]; } break; case AnnotationCoordinateSpaceEnum::WINDOW: { int viewport[4]; m_mouseEvent.getViewportContent()->getWindowViewport(viewport); viewportWidth = viewport[2]; viewportHeight = viewport[3]; } break; } if ((viewportWidth > 0.0) && (viewportHeight > 0.0)) { m_percentageWidth = (pixelWidth / viewportWidth) * 100.0; m_percentageHeight = (pixelHeight / viewportHeight) * 100.0; } windowPixelX = centerX; windowPixelY = centerY; windowTwoPixelX = -1; windowTwoPixelY = -1; /* 25 Mar 2016 * Note: windowPixelX,Y has origin in locked viewport * but we need X,Y in window as if there was no locked * viewport. */ int wvp[4]; m_mouseEvent.getViewportContent()->getWindowViewport(wvp); windowPixelX = centerX + wvp[0]; windowPixelY = centerY + wvp[1]; } else if (useTextAligmentFlag) { float textX = windowPixelX; float textY = windowPixelY; AnnotationPercentSizeText textAnn(AnnotationAttributesDefaultTypeEnum::USER); switch (textAnn.getHorizontalAlignment()) { case AnnotationTextAlignHorizontalEnum::CENTER: textX = centerX; break; case AnnotationTextAlignHorizontalEnum::LEFT: textX = minX; break; case AnnotationTextAlignHorizontalEnum::RIGHT: textX = maxX; break; } switch (textAnn.getVerticalAlignment()) { case AnnotationTextAlignVerticalEnum::BOTTOM: textY = minY; break; case AnnotationTextAlignVerticalEnum::MIDDLE: textY = centerY; break; case AnnotationTextAlignVerticalEnum::TOP: textY = maxY; break; } windowPixelX = textX; windowPixelY = textY; windowTwoPixelX = -1; windowTwoPixelY = -1; /* 25 Mar 2016 * Note: windowPixelX,Y has origin in locked viewport * but we need X,Y in window as if there was no locked * viewport. */ int wvp[4]; m_mouseEvent.getViewportContent()->getWindowViewport(wvp); windowPixelX = textX + wvp[0]; windowPixelY = textY + wvp[1]; } AnnotationCoordinateInformation::createCoordinateInformationFromXY(m_mouseEvent, windowPixelX, windowPixelY, m_coordOneInfo); m_coordTwoInfo.reset(); m_coordTwoInfoValid = false; AnnotationCoordinateInformation::getValidCoordinateSpaces(&m_coordOneInfo, NULL, m_validSpaces); } } } } /** * Adjust the viewport for surface montage. A surface montage is a composite of multiple * smaller viewports. For proper annotation sizing in stereotaxic or surface space, * we need to find the with of the model area in the viewport. * * @param browserTabContent * Content of browser tab. * @param viewport * The tab's viewport. * @param widthOut * Output with width. * @param heightOut * Output with height. * @return * True if the width and height are valid, else false. */ bool AnnotationCreateDialog::NewAnnotationInfo::adjustViewportForSurfaceMontage(BrowserTabContent* browserTabContent, const int viewport[4], float& widthOut, float& heightOut) { if (browserTabContent == NULL) { return false; } ModelSurfaceMontage* msm = browserTabContent->getDisplayedSurfaceMontageModel(); if (msm == NULL) { return false; } std::vector montageViewports; msm->getSurfaceMontageViewportsForDrawing(browserTabContent->getTabNumber(), montageViewports); int32_t numRows = -1; int32_t numCols = -1; SurfaceMontageViewport::getNumberOfRowsAndColumns(montageViewports, numRows, numCols); if ((numRows > 0) && (numCols > 0)) { const float viewportWidth = viewport[2]; const float viewportHeight = viewport[3]; widthOut = viewportWidth / static_cast(numCols); heightOut = viewportHeight / static_cast(numRows); if ((widthOut > 0.0) && (heightOut > 0.0)) { return true; } } return false; } /** * @return Is the new annotation information valid (has at least one valid space)? */ bool AnnotationCreateDialog::NewAnnotationInfo::isValid() const { return ( ! m_validSpaces.empty()); } /** * @return Is the selected space valid? */ bool AnnotationCreateDialog::NewAnnotationInfo::isSelectedSpaceValid() const { if (std::find(m_validSpaces.begin(), m_validSpaces.end(), m_selectedSpace) != m_validSpaces.end()) { return true; } return false; } connectome-workbench-1.2.3+git41-gc4c6c90/src/GuiQt/AnnotationCreateDialog.h000066400000000000000000000154651300200146000263370ustar00rootroot00000000000000#ifndef __ANNOTATION_CREATE_DIALOG_H__ #define __ANNOTATION_CREATE_DIALOG_H__ /*LICENSE_START*/ /* * Copyright (C) 2015 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 "AnnotationCoordinateInformation.h" #include "AnnotationCoordinateSpaceEnum.h" #include "AnnotationTypeEnum.h" #include "WuQDialogModal.h" class QButtonGroup; class QTextEdit; namespace caret { class Annotation; class AnnotationFile; class BrowserTabContent; class MouseEvent; class AnnotationCreateDialog : public WuQDialogModal { Q_OBJECT public: static Annotation* newAnnotationFromSpaceAndType(const MouseEvent& mouseEvent, const AnnotationCoordinateSpaceEnum::Enum annotationSpace, const AnnotationTypeEnum::Enum annotationType, AnnotationFile* annotationFile); static Annotation* newAnnotationFromSpaceTypeAndBounds(const MouseEvent& mouseEvent, const AnnotationCoordinateSpaceEnum::Enum annotationSpace, const AnnotationTypeEnum::Enum annotationType, AnnotationFile* annotationFile); virtual ~AnnotationCreateDialog(); virtual void okButtonClicked(); Annotation* getAnnotationThatWasCreated(); // ADD_NEW_METHODS_HERE private slots: void selectImageButtonClicked(); private: /** Dialogs mode */ enum Mode { /** New annotation from annotation type at mouse click */ MODE_NEW_ANNOTATION_TYPE_CLICK, /** New annotation from annotation type at mouse press and release */ MODE_NEW_ANNOTATION_TYPE_FROM_BOUNDS }; class NewAnnotationInfo { public: NewAnnotationInfo(const MouseEvent& mouseEvent, const AnnotationCoordinateSpaceEnum::Enum selectedSpace, const AnnotationTypeEnum::Enum annotationType, const bool useBothCoordinatesFromMouseFlag, AnnotationFile* annotationFile); bool isValid() const; bool isSelectedSpaceValid() const; void processTwoCoordInfo(); bool adjustViewportForSurfaceMontage(BrowserTabContent* browserTabContent, const int viewport[4], float& widthOut, float& heightOut); const MouseEvent& m_mouseEvent; const AnnotationCoordinateSpaceEnum::Enum m_selectedSpace; const AnnotationTypeEnum::Enum m_annotationType; AnnotationFile* m_annotationFile; std::vector m_validSpaces; AnnotationCoordinateInformation m_coordOneInfo; AnnotationCoordinateInformation m_coordTwoInfo; bool m_coordTwoInfoValid; float m_percentageWidth; float m_percentageHeight; }; static Annotation* newAnnotationFromSpaceTypeAndCoords(const Mode mode, const MouseEvent& mouseEvent, const AnnotationCoordinateSpaceEnum::Enum annotationSpace, const AnnotationTypeEnum::Enum annotationType, AnnotationFile* annotationFile); AnnotationCreateDialog(const Mode mode, NewAnnotationInfo& newAnnotationInfo, const AnnotationCoordinateSpaceEnum::Enum annotationSpace, const bool annotationSpaceValidFlag, QWidget* parent = 0); AnnotationCreateDialog(const AnnotationCreateDialog&); AnnotationCreateDialog& operator=(const AnnotationCreateDialog&); QWidget* createTextWidget(); QWidget* createImageWidget(); void invalidateImage(); static Annotation* createAnnotation(NewAnnotationInfo& newAnnotationInfo, const AnnotationCoordinateSpaceEnum::Enum annotationSpace); static void finishAnnotationCreation(AnnotationFile* annotationFile, Annotation* annotation, const int32_t browswerWindowIndex); const Mode m_mode; NewAnnotationInfo& m_newAnnotationInfo; const AnnotationCoordinateSpaceEnum::Enum m_annotationSpace; Annotation* m_annotationThatWasCreated; // float m_annotationFromBoundsWidth; // // float m_annotationFromBoundsHeight; QButtonGroup* m_annotationSpaceButtonGroup; QTextEdit* m_textEdit; QLabel* m_imageFileNameLabel; QLabel* m_imageThumbnailLabel; std::vector m_imageRgbaBytes; int32_t m_imageWidth; int32_t m_imageHeight; // AnnotationCoordinateInformation m_coordInfo; // // AnnotationCoordinateInformation m_coordTwoInfo; static const int s_MAXIMUM_THUMB_NAIL_SIZE; // ADD_NEW_MEMBERS_HERE }; #ifdef __ANNOTATION_CREATE_DIALOG_DECLARE__ const int AnnotationCreateDialog::s_MAXIMUM_THUMB_NAIL_SIZE = 128; #endif // __ANNOTATION_CREATE_DIALOG_DECLARE__; } // namespace #endif //__ANNOTATION_CREATE_DIALOG_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/GuiQt/AnnotationDeleteWidget.cxx000066400000000000000000000126551300200146000267330ustar00rootroot00000000000000 /*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 __ANNOTATION_DELETE_WIDGET_DECLARE__ #include "AnnotationDeleteWidget.h" #undef __ANNOTATION_DELETE_WIDGET_DECLARE__ #include #include #include #include #include #include #include "AnnotationManager.h" #include "AnnotationRedoUndoCommand.h" #include "Brain.h" #include "CaretAssert.h" #include "EventGraphicsUpdateAllWindows.h" #include "EventManager.h" #include "GuiManager.h" #include "WuQMessageBox.h" #include "WuQtUtilities.h" using namespace caret; /** * \class caret::AnnotationDeleteWidget * \brief Widget for deleting annotations. * \ingroup GuiQt */ /** * Constructor. */ AnnotationDeleteWidget::AnnotationDeleteWidget(const int32_t browserWindowIndex, QWidget* parent) : QWidget(parent), m_browserWindowIndex(browserWindowIndex) { QLabel* deleteLabel = new QLabel("Delete"); m_deleteToolButton = createDeleteToolButton(); QVBoxLayout* layout = new QVBoxLayout(this); WuQtUtilities::setLayoutSpacingAndMargins(layout, 2, 2); layout->addWidget(deleteLabel); layout->addWidget(m_deleteToolButton); layout->addStretch(); setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); } /** * Destructor. */ AnnotationDeleteWidget::~AnnotationDeleteWidget() { } /** * Update the content. */ void AnnotationDeleteWidget::updateContent() { AnnotationManager* annotationManager = GuiManager::get()->getBrain()->getAnnotationManager(); m_deleteToolButtonAction->setEnabled(annotationManager->isAnnotationSelectedForEditingDeletable(m_browserWindowIndex)); } /** * @return The delete tool button. */ QToolButton* AnnotationDeleteWidget::createDeleteToolButton() { m_deleteToolButtonAction = WuQtUtilities::createAction("", ("Delete the selected annotation\n" "\n" "Pressing the Delete key while an annotation\n" "is selected will also delete an annotation\n" "\n" "Pressing the arrow will show a menu for\n" "undeleting annotations"), this, this, SLOT(deleteActionTriggered())); QToolButton* toolButton = new QToolButton(); const float width = 24.0; const float height = 24.0; QPixmap pixmap(static_cast(width), static_cast(height)); QSharedPointer painter = WuQtUtilities::createPixmapWidgetPainter(toolButton, pixmap); /* trash can */ painter->drawLine(4, 6, 4, 22); painter->drawLine(4, 22, 20, 22); painter->drawLine(20, 22, 20, 6); /* trash can lines */ painter->drawLine(12, 8, 12, 20); painter->drawLine(8, 8, 8, 20); painter->drawLine(16, 8, 16, 20); /* trash can lid and handle */ painter->drawLine(2, 6, 22, 6); painter->drawLine(8, 6, 8, 2); painter->drawLine(8, 2, 16, 2); painter->drawLine(16, 2, 16, 6); m_deleteToolButtonAction->setIcon(QIcon(pixmap)); toolButton->setIconSize(pixmap.size()); toolButton->setDefaultAction(m_deleteToolButtonAction); return toolButton; } /** * Gets called when the delete action is triggered. */ void AnnotationDeleteWidget::deleteActionTriggered() { AnnotationManager* annotationManager = GuiManager::get()->getBrain()->getAnnotationManager(); std::vector selectedAnnotations = annotationManager->getAnnotationsSelectedForEditing(m_browserWindowIndex); if ( ! selectedAnnotations.empty()) { AnnotationRedoUndoCommand* undoCommand = new AnnotationRedoUndoCommand(); undoCommand->setModeDeleteAnnotations(selectedAnnotations); AString errorMessage; if ( ! annotationManager->applyCommand(undoCommand, errorMessage)) { WuQMessageBox::errorOk(this, errorMessage); } EventManager::get()->sendSimpleEvent(EventTypeEnum::EVENT_ANNOTATION_TOOLBAR_UPDATE); EventManager::get()->sendEvent(EventGraphicsUpdateAllWindows().getPointer()); } } connectome-workbench-1.2.3+git41-gc4c6c90/src/GuiQt/AnnotationDeleteWidget.h000066400000000000000000000037671300200146000263640ustar00rootroot00000000000000#ifndef __ANNOTATION_DELETE_WIDGET_H__ #define __ANNOTATION_DELETE_WIDGET_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 class QAction; class QToolButton; namespace caret { class AnnotationDeleteWidget : public QWidget { Q_OBJECT public: AnnotationDeleteWidget(const int32_t browserWindowIndex, QWidget* parent = 0); virtual ~AnnotationDeleteWidget(); // ADD_NEW_METHODS_HERE void updateContent(); private slots: void deleteActionTriggered(); private: AnnotationDeleteWidget(const AnnotationDeleteWidget&); AnnotationDeleteWidget& operator=(const AnnotationDeleteWidget&); QToolButton* createDeleteToolButton(); const int32_t m_browserWindowIndex; QToolButton* m_deleteToolButton; QAction* m_deleteToolButtonAction; // ADD_NEW_MEMBERS_HERE }; #ifdef __ANNOTATION_DELETE_WIDGET_DECLARE__ // #endif // __ANNOTATION_DELETE_WIDGET_DECLARE__ } // namespace #endif //__ANNOTATION_DELETE_WIDGET_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/GuiQt/AnnotationFontWidget.cxx000066400000000000000000000647731300200146000264470ustar00rootroot00000000000000 /*LICENSE_START*/ /* * Copyright (C) 2015 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 #define __ANNOTATION_FONT_WIDGET_DECLARE__ #include "AnnotationFontWidget.h" #undef __ANNOTATION_FONT_WIDGET_DECLARE__ #include "AnnotationFontAttributesInterface.h" #include "AnnotationManager.h" #include "AnnotationRedoUndoCommand.h" #include "AnnotationTextFontNameEnum.h" #include "AnnotationTextFontPointSizeEnum.h" #include "AnnotationPercentSizeText.h" #include "Brain.h" #include "BrainBrowserWindow.h" #include "BrowserTabContent.h" #include "CaretAssert.h" #include "CaretColorEnumMenu.h" #include "EnumComboBoxTemplate.h" #include "EventGraphicsUpdateAllWindows.h" #include "EventManager.h" #include "GuiManager.h" #include "ModelSurfaceMontage.h" #include "WuQSpecialIncrementDoubleSpinBox.h" #include "WuQMessageBox.h" #include "WuQtUtilities.h" using namespace caret; /** * \class caret::AnnotationFontWidget * \brief Widget for annotation font selection * \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. * * @param browserWindowIndex * Index of window in which this instance is displayed * @param parent * Parent for this widget. */ AnnotationFontWidget::AnnotationFontWidget(const AnnotationWidgetParentEnum::Enum parentWidgetType, const int32_t browserWindowIndex, QWidget* parent) : QWidget(parent), m_parentWidgetType(parentWidgetType), m_browserWindowIndex(browserWindowIndex) { /* * "Font" label */ QLabel* fontLabel = new QLabel("Font "); /* * Combo box for font name selection */ m_fontNameComboBox = new EnumComboBoxTemplate(this); m_fontNameComboBox->setup(); QObject::connect(m_fontNameComboBox, SIGNAL(itemActivated()), this, SLOT(fontNameChanged())); WuQtUtilities::setToolTipAndStatusTip(m_fontNameComboBox->getWidget(), "Change font"); /* * 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("%"); 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"); /* * Text color menu */ m_textColorMenu = new CaretColorEnumMenu((CaretColorEnum::OPTION_INCLUDE_CUSTOM_COLOR)); QObject::connect(m_textColorMenu, SIGNAL(colorSelected(const CaretColorEnum::Enum)), this, SLOT(textColorSelected(const CaretColorEnum::Enum))); /* * Text color action and toolbutton */ QLabel* textColorLabel = new QLabel("Color"); const QSize toolButtonSize(16, 16); m_textColorAction = new QAction("C", this); m_textColorAction->setToolTip("Adjust the text color"); m_textColorAction->setMenu(m_textColorMenu); m_textColorToolButton = new QToolButton(); m_textColorToolButton->setDefaultAction(m_textColorAction); m_textColorToolButton->setIconSize(toolButtonSize); QToolButton* boldFontToolButton = NULL; QToolButton* italicFontToolButton = NULL; QToolButton* underlineFontToolButton = NULL; m_boldFontAction = NULL; m_italicFontAction = NULL; m_underlineFontAction = NULL; switch (m_parentWidgetType) { case AnnotationWidgetParentEnum::ANNOTATION_TOOL_BAR_WIDGET: { /* * Bold Font */ m_boldFontAction = WuQtUtilities::createAction("B", //boldFontText.toStringWithHtmlBody(), "Enable/disable bold styling", this, this, SLOT(fontBoldChanged())); m_boldFontAction->setCheckable(true); boldFontToolButton = new QToolButton(); boldFontToolButton->setDefaultAction(m_boldFontAction); /* * Change the bold toolbutton's font to bold. */ QFont boldFont = boldFontToolButton->font(); boldFont.setBold(true); boldFontToolButton->setFont(boldFont); /* * Italic font toolbutton */ m_italicFontAction = WuQtUtilities::createAction("i", "Enable/disable italic styling", this, this, SLOT(fontItalicChanged())); m_italicFontAction->setCheckable(true); italicFontToolButton = new QToolButton(); italicFontToolButton->setDefaultAction(m_italicFontAction); /* * Change the italic toolbutton's font to italic. */ QFont italicFont = italicFontToolButton->font(); italicFont.setItalic(true); italicFontToolButton->setFont(italicFont); /* * Underline font toolbutton */ m_underlineFontAction = WuQtUtilities::createAction("U", "Enable/disable font underlining", this, this, SLOT(fontUnderlineChanged())); m_underlineFontAction->setCheckable(true); underlineFontToolButton = new QToolButton(); underlineFontToolButton->setDefaultAction(m_underlineFontAction); /* * Change the underline toolbutton's font to underline. */ QFont underlineFont = underlineFontToolButton->font(); underlineFont.setUnderline(true); underlineFontToolButton->setFont(underlineFont); } break; case AnnotationWidgetParentEnum::PARENT_ENUM_FOR_LATER_USE: CaretAssert(0); break; } /* * Layout the widgets */ switch (m_parentWidgetType) { case AnnotationWidgetParentEnum::ANNOTATION_TOOL_BAR_WIDGET: { QLabel* sizeLabel = new QLabel("Size"); QLabel* styleLabel = new QLabel("Style"); QHBoxLayout* stylesLayout = new QHBoxLayout(); WuQtUtilities::setLayoutSpacingAndMargins(stylesLayout, 0, 0); //stylesLayout->addStretch(); stylesLayout->addWidget(boldFontToolButton); stylesLayout->addWidget(italicFontToolButton); stylesLayout->addWidget(underlineFontToolButton); stylesLayout->addStretch(); QGridLayout* fontNameSizeLayout = new QGridLayout(this); WuQtUtilities::setLayoutSpacingAndMargins(fontNameSizeLayout, 2, 0); fontNameSizeLayout->setColumnStretch(0, 0); fontNameSizeLayout->setColumnStretch(1, 0); fontNameSizeLayout->setColumnStretch(2, 0); fontNameSizeLayout->setColumnStretch(3, 100); fontNameSizeLayout->addWidget(fontLabel, 0, 0); fontNameSizeLayout->addWidget(m_fontNameComboBox->getWidget(), 0, 1, 1, 3); fontNameSizeLayout->addWidget(sizeLabel, 1, 0); fontNameSizeLayout->addWidget(m_fontSizeSpinBox, 1, 1); fontNameSizeLayout->addWidget(styleLabel, 2, 0); fontNameSizeLayout->addLayout(stylesLayout, 2, 1); fontNameSizeLayout->addWidget(textColorLabel, 1, 2); fontNameSizeLayout->addWidget(m_textColorToolButton, 2, 2); } break; case AnnotationWidgetParentEnum::PARENT_ENUM_FOR_LATER_USE: CaretAssert(0); break; } setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); } /** * Destructor. */ AnnotationFontWidget::~AnnotationFontWidget() { } /** * Update the content of this widget with the given annotations. * * @param annotations * The selected annotations. */ void AnnotationFontWidget::updateContent(std::vector& annotations) { m_annotationsFontStyle = annotations; m_annotations.clear(); if ( ! m_annotationsFontStyle.empty()) { bool boldOnFlag = true; bool italicOnFlag = true; bool underlineOnFlag = true; int32_t stylesEnabledCount = 0; AnnotationTextFontNameEnum::Enum fontName = AnnotationTextFontNameEnum::VERA; bool fontNameValid = true; float fontSizeValue = 5.0; bool haveMultipleFontSizeValues = false; const float surfaceMontageRowCount = getSurfaceMontageRowCount(); 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); m_annotations.push_back(annotation); float sizeValue = annText->getFontPercentViewportSize(); Annotation* ann = dynamic_cast(annText); CaretAssert(ann); switch (ann->getCoordinateSpace()) { case AnnotationCoordinateSpaceEnum::PIXELS: break; case AnnotationCoordinateSpaceEnum::STEREOTAXIC: sizeValue /= surfaceMontageRowCount; break; case AnnotationCoordinateSpaceEnum::SURFACE: sizeValue /= surfaceMontageRowCount; break; case AnnotationCoordinateSpaceEnum::TAB: break; case AnnotationCoordinateSpaceEnum::WINDOW: break; } if (i == 0) { fontName = annText->getFont(); fontSizeValue = sizeValue; } else { if (annText->getFont() != fontName) { fontNameValid = false; } if (fontSizeValue != sizeValue) { haveMultipleFontSizeValues = true; fontSizeValue = std::min(fontSizeValue, sizeValue); } } if (annText->isStylesSupported()) { if ( ! annText->isBoldStyleEnabled()) { boldOnFlag = false; } if ( ! annText->isItalicStyleEnabled()) { italicOnFlag = false; } if ( ! annText->isUnderlineStyleEnabled()) { underlineOnFlag = false; } ++stylesEnabledCount; } } m_fontNameComboBox->setSelectedItem(fontName); updateFontSizeSpinBox(fontSizeValue, haveMultipleFontSizeValues); /* * Font styles are ON only if all selected * text annotations have the style enabled */ const bool stylesEnabledFlag = (stylesEnabledCount > 0); m_boldFontAction->setEnabled(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. * * @param value * New value for font size spin box. * @param haveMultipleValuesFlag * If true, there are multiple font size values so indicate * this with a '+' sign as a suffix */ void AnnotationFontWidget::updateFontSizeSpinBox(const float value, const bool haveMultipleValuesFlag) { m_fontSizeSpinBox->blockSignals(true); m_fontSizeSpinBox->setValue(value); m_fontSizeSpinBox->blockSignals(false); QString fontSizeSuffix("%"); if (haveMultipleValuesFlag) { fontSizeSuffix = "%+"; } m_fontSizeSpinBox->setSuffix(fontSizeSuffix); } /** * Gets called when the text color is changed. * * @param caretColor * Color that was selected. */ void AnnotationFontWidget::textColorSelected(const CaretColorEnum::Enum caretColor) { if ( ! m_annotationsFontStyle.empty()) { float rgba[4]; m_annotationsFontStyle[0]->getTextColorRGBA(rgba); if (caretColor == CaretColorEnum::CUSTOM) { QColor color; color.setRgbF(rgba[0], rgba[1], rgba[2]); QColor newColor = QColorDialog::getColor(color, m_textColorToolButton, "Text Color"); if (newColor.isValid()) { rgba[0] = newColor.redF(); rgba[1] = newColor.greenF(); rgba[2] = newColor.blueF(); switch (m_parentWidgetType) { case AnnotationWidgetParentEnum::ANNOTATION_TOOL_BAR_WIDGET: AnnotationText::setUserDefaultCustomTextColor(rgba); break; case AnnotationWidgetParentEnum::PARENT_ENUM_FOR_LATER_USE: CaretAssert(0); break; } } } switch (m_parentWidgetType) { case AnnotationWidgetParentEnum::ANNOTATION_TOOL_BAR_WIDGET: { AnnotationRedoUndoCommand* undoCommand = new AnnotationRedoUndoCommand(); undoCommand->setModeTextColor(caretColor, rgba, m_annotations); AnnotationManager* annMan = GuiManager::get()->getBrain()->getAnnotationManager(); AString errorMessage; if ( ! annMan->applyCommand(undoCommand, errorMessage)) { WuQMessageBox::errorOk(this, errorMessage); } AnnotationText::setUserDefaultTextColor(caretColor); } break; case AnnotationWidgetParentEnum::PARENT_ENUM_FOR_LATER_USE: CaretAssert(0); break; } } updateTextColorButton(); switch (m_parentWidgetType) { case AnnotationWidgetParentEnum::ANNOTATION_TOOL_BAR_WIDGET: EventManager::get()->sendSimpleEvent(EventTypeEnum::EVENT_ANNOTATION_TOOLBAR_UPDATE); break; case AnnotationWidgetParentEnum::PARENT_ENUM_FOR_LATER_USE: CaretAssert(0); break; } EventManager::get()->sendEvent(EventGraphicsUpdateAllWindows().getPointer()); } /** * Update the text color button. */ void AnnotationFontWidget::updateTextColorButton() { CaretColorEnum::Enum colorEnum = CaretColorEnum::NONE; float rgba[4]; CaretColorEnum::toRGBFloat(colorEnum, rgba); rgba[3] = 1.0; bool colorButtonValidFlag = false; switch (m_parentWidgetType) { case AnnotationWidgetParentEnum::ANNOTATION_TOOL_BAR_WIDGET: { const int32_t numAnnotations = static_cast(m_annotationsFontStyle.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); firstColorSupportFlag = false; colorButtonValidFlag = true; } else { float colorRGBA[4]; m_annotationsFontStyle[i]->getTextColorRGBA(colorRGBA); for (int32_t iColor = 0; iColor < 4; iColor++) { if (rgba[iColor] != colorRGBA[iColor]) { allSameColorFlag = false; break; } } if ( ! allSameColorFlag) { break; } } } if (allSameColorFlag) { colorEnum = m_annotationsFontStyle[0]->getTextColor(); m_annotationsFontStyle[0]->getTextColorRGBA(rgba); float customRGBA[4]; m_annotationsFontStyle[0]->getCustomTextColor(customRGBA); m_textColorMenu->setCustomIconColor(customRGBA); switch (m_parentWidgetType) { case AnnotationWidgetParentEnum::ANNOTATION_TOOL_BAR_WIDGET: AnnotationText::setUserDefaultTextColor(colorEnum); AnnotationText::setUserDefaultCustomTextColor(customRGBA); break; case AnnotationWidgetParentEnum::PARENT_ENUM_FOR_LATER_USE: CaretAssert(0); break; } } } } break; case AnnotationWidgetParentEnum::PARENT_ENUM_FOR_LATER_USE: CaretAssert(0); break; } QPixmap pm = WuQtUtilities::createCaretColorEnumPixmap(m_textColorToolButton, 24, 24, colorEnum, rgba, true); m_textColorAction->setIcon(QIcon(pm)); m_textColorMenu->setSelectedColor(colorEnum); if (colorButtonValidFlag) { } } /** * Gets called when font bold changed. */ void AnnotationFontWidget::fontBoldChanged() { AnnotationRedoUndoCommand* command = new AnnotationRedoUndoCommand(); command->setModeTextFontBold(m_boldFontAction->isChecked(), m_annotations); AnnotationManager* annMan = GuiManager::get()->getBrain()->getAnnotationManager(); AString errorMessage; if ( ! annMan->applyCommand(command, errorMessage)) { WuQMessageBox::errorOk(this, errorMessage); } EventManager::get()->sendSimpleEvent(EventTypeEnum::EVENT_ANNOTATION_TOOLBAR_UPDATE); EventManager::get()->sendEvent(EventGraphicsUpdateAllWindows().getPointer()); switch (m_parentWidgetType) { case AnnotationWidgetParentEnum::ANNOTATION_TOOL_BAR_WIDGET: AnnotationText::setUserDefaultBoldEnabled(m_boldFontAction->isChecked()); break; case AnnotationWidgetParentEnum::PARENT_ENUM_FOR_LATER_USE: break; } } /** * Gets called when font italic changed. */ void AnnotationFontWidget::fontItalicChanged() { AnnotationRedoUndoCommand* command = new AnnotationRedoUndoCommand(); command->setModeTextFontItalic(m_italicFontAction->isChecked(), m_annotations); AnnotationManager* annMan = GuiManager::get()->getBrain()->getAnnotationManager(); AString errorMessage; if ( ! annMan->applyCommand(command, errorMessage)) { WuQMessageBox::errorOk(this, errorMessage); } EventManager::get()->sendSimpleEvent(EventTypeEnum::EVENT_ANNOTATION_TOOLBAR_UPDATE); EventManager::get()->sendEvent(EventGraphicsUpdateAllWindows().getPointer()); switch (m_parentWidgetType) { case AnnotationWidgetParentEnum::ANNOTATION_TOOL_BAR_WIDGET: AnnotationText::setUserDefaultItalicEnabled(m_italicFontAction->isChecked()); break; case AnnotationWidgetParentEnum::PARENT_ENUM_FOR_LATER_USE: break; } } /** * Gets called when font name changed. */ void AnnotationFontWidget::fontNameChanged() { const AnnotationTextFontNameEnum::Enum fontName = m_fontNameComboBox->getSelectedItem(); AnnotationRedoUndoCommand* command = new AnnotationRedoUndoCommand(); command->setModeTextFontName(fontName, m_annotations); AnnotationManager* annMan = GuiManager::get()->getBrain()->getAnnotationManager(); AString errorMessage; if ( ! annMan->applyCommand(command, errorMessage)) { WuQMessageBox::errorOk(this, errorMessage); } EventManager::get()->sendSimpleEvent(EventTypeEnum::EVENT_ANNOTATION_TOOLBAR_UPDATE); switch (m_parentWidgetType) { case AnnotationWidgetParentEnum::ANNOTATION_TOOL_BAR_WIDGET: AnnotationText::setUserDefaultFont(fontName); break; case AnnotationWidgetParentEnum::PARENT_ENUM_FOR_LATER_USE: break; } EventManager::get()->sendEvent(EventGraphicsUpdateAllWindows().getPointer()); } /** * Gets called when font size changed. */ void AnnotationFontWidget::fontSizeChanged() { const float fontPercentSize = m_fontSizeSpinBox->value(); AnnotationRedoUndoCommand* command = new AnnotationRedoUndoCommand(); command->setModeTextFontPercentSize(fontPercentSize, m_annotations, getSurfaceMontageRowCount()); AnnotationManager* annMan = GuiManager::get()->getBrain()->getAnnotationManager(); AString errorMessage; if ( ! annMan->applyCommand(command, errorMessage)) { WuQMessageBox::errorOk(this, errorMessage); } EventManager::get()->sendSimpleEvent(EventTypeEnum::EVENT_ANNOTATION_TOOLBAR_UPDATE); switch (m_parentWidgetType) { case AnnotationWidgetParentEnum::ANNOTATION_TOOL_BAR_WIDGET: AnnotationText::setUserDefaultFontPercentViewportSize(fontPercentSize); break; case AnnotationWidgetParentEnum::PARENT_ENUM_FOR_LATER_USE: CaretAssert(0); break; } EventManager::get()->sendEvent(EventGraphicsUpdateAllWindows().getPointer()); } /** * @return Number of rows in a surface montage. * If a surface montage is not displayed, one is returned. */ float AnnotationFontWidget::getSurfaceMontageRowCount() const { const BrainBrowserWindow* bbw = GuiManager::get()->getBrowserWindowByWindowIndex(m_browserWindowIndex); CaretAssert(bbw); int32_t surfaceMontageRowCount = 1; const BrowserTabContent* btc = bbw->getBrowserTabContent(); if (btc != NULL) { const ModelSurfaceMontage* msm = btc->getDisplayedSurfaceMontageModel(); if (msm != NULL) { int32_t columnCount = 1; msm->getSurfaceMontageNumberOfRowsAndColumns(btc->getTabNumber(), surfaceMontageRowCount, columnCount); } } return surfaceMontageRowCount; } /** * Gets called when font underline changed. */ void AnnotationFontWidget::fontUnderlineChanged() { AnnotationRedoUndoCommand* command = new AnnotationRedoUndoCommand(); command->setModeTextFontUnderline(m_underlineFontAction->isChecked(), m_annotations); AnnotationManager* annMan = GuiManager::get()->getBrain()->getAnnotationManager(); AString errorMessage; if ( ! annMan->applyCommand(command, errorMessage)) { WuQMessageBox::errorOk(this, errorMessage); } EventManager::get()->sendSimpleEvent(EventTypeEnum::EVENT_ANNOTATION_TOOLBAR_UPDATE); EventManager::get()->sendEvent(EventGraphicsUpdateAllWindows().getPointer()); switch (m_parentWidgetType) { case AnnotationWidgetParentEnum::ANNOTATION_TOOL_BAR_WIDGET: AnnotationText::setUserDefaultUnderlineEnabled(m_underlineFontAction->isChecked()); break; case AnnotationWidgetParentEnum::PARENT_ENUM_FOR_LATER_USE: CaretAssert(0); break; } } connectome-workbench-1.2.3+git41-gc4c6c90/src/GuiQt/AnnotationFontWidget.h000066400000000000000000000066031300200146000260600ustar00rootroot00000000000000#ifndef __ANNOTATION_FONT_WIDGET_H__ #define __ANNOTATION_FONT_WIDGET_H__ /*LICENSE_START*/ /* * Copyright (C) 2015 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 "AnnotationWidgetParentEnum.h" #include "CaretColorEnum.h" class QToolButton; namespace caret { class Annotation; class AnnotationFontAttributesInterface; class AnnotationText; class CaretColorEnumMenu; class EnumComboBoxTemplate; class WuQSpecialIncrementDoubleSpinBox; class AnnotationFontWidget : public QWidget { Q_OBJECT public: AnnotationFontWidget(const AnnotationWidgetParentEnum::Enum parentWidgetType, const int32_t browserWindowIndex, QWidget* parent = 0); virtual ~AnnotationFontWidget(); void updateContent(std::vector& annotations); // ADD_NEW_METHODS_HERE private slots: void textColorSelected(const CaretColorEnum::Enum); void fontBoldChanged(); void fontItalicChanged(); void fontNameChanged(); void fontSizeChanged(); void fontUnderlineChanged(); private: AnnotationFontWidget(const AnnotationFontWidget&); AnnotationFontWidget& operator=(const AnnotationFontWidget&); void updateFontSizeSpinBox(const float value, const bool haveMultipleValuesFlag); void updateTextColorButton(); float getSurfaceMontageRowCount() const; const AnnotationWidgetParentEnum::Enum m_parentWidgetType; const int32_t m_browserWindowIndex; EnumComboBoxTemplate* m_fontNameComboBox; WuQSpecialIncrementDoubleSpinBox* m_fontSizeSpinBox; QToolButton* m_textColorToolButton; QAction* m_textColorAction; CaretColorEnumMenu* m_textColorMenu; QAction* m_boldFontAction; QAction* m_italicFontAction; QAction* m_underlineFontAction; /** Contains annotations */ std::vector m_annotations; /** Contains annotations with type as font style */ std::vector m_annotationsFontStyle; // ADD_NEW_MEMBERS_HERE }; #ifdef __ANNOTATION_FONT_WIDGET_DECLARE__ // #endif // __ANNOTATION_FONT_WIDGET_DECLARE__ } // namespace #endif //__ANNOTATION_FONT_WIDGET_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/GuiQt/AnnotationFormatWidget.cxx000066400000000000000000000053731300200146000267600ustar00rootroot00000000000000 /*LICENSE_START*/ /* * Copyright (C) 2015 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 __ANNOTATION_FORMAT_WIDGET_DECLARE__ #include "AnnotationFormatWidget.h" #undef __ANNOTATION_FORMAT_WIDGET_DECLARE__ #include #include #include #include "AnnotationMenuArrange.h" #include "CaretAssert.h" #include "WuQtUtilities.h" using namespace caret; /** * \class caret::AnnotationFormatWidget * \brief Widget for formatting annotations. * \ingroup GuiQt */ /** * Constructor. * * @param browserWindowIndex * Index of the browser window. * @param parent * Parent of this widget. */ AnnotationFormatWidget::AnnotationFormatWidget(const int32_t browserWindowIndex, QWidget* parent) : QWidget(parent), m_browserWindowIndex(browserWindowIndex) { QLabel* formatLabel = new QLabel("Format"); QWidget* arrangeButton = createArrangeMenuToolButton(); QVBoxLayout* layout = new QVBoxLayout(this); WuQtUtilities::setLayoutSpacingAndMargins(layout, 2, 2); layout->addWidget(formatLabel); layout->addWidget(arrangeButton); layout->addStretch(); setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); } /** * Destructor. */ AnnotationFormatWidget::~AnnotationFormatWidget() { } /** * Update with the given annotation. * * @param annotation. */ void AnnotationFormatWidget::updateContent(Annotation* /*annotation*/) { } /** * @return The arrange tool button. */ QWidget* AnnotationFormatWidget::createArrangeMenuToolButton() { AnnotationMenuArrange* arrangeMenu = new AnnotationMenuArrange(m_browserWindowIndex); QAction* arrangeAction = new QAction("Arrange", this); arrangeAction->setToolTip("Arrange (align) and group annotations"); arrangeAction->setMenu(arrangeMenu); QToolButton* arrangeToolButton = new QToolButton(); arrangeToolButton->setDefaultAction(arrangeAction); return arrangeToolButton; } connectome-workbench-1.2.3+git41-gc4c6c90/src/GuiQt/AnnotationFormatWidget.h000066400000000000000000000035331300200146000264010ustar00rootroot00000000000000#ifndef __ANNOTATION_FORMAT_WIDGET_H__ #define __ANNOTATION_FORMAT_WIDGET_H__ /*LICENSE_START*/ /* * Copyright (C) 2015 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 Annotation; class AnnotationFormatWidget : public QWidget { Q_OBJECT public: AnnotationFormatWidget(const int32_t browserWindowIndex, QWidget* parent = 0); virtual ~AnnotationFormatWidget(); // ADD_NEW_METHODS_HERE void updateContent(Annotation* annotation); private: AnnotationFormatWidget(const AnnotationFormatWidget&); AnnotationFormatWidget& operator=(const AnnotationFormatWidget&); QWidget* createArrangeMenuToolButton(); const int32_t m_browserWindowIndex; // ADD_NEW_MEMBERS_HERE }; #ifdef __ANNOTATION_FORMAT_WIDGET_DECLARE__ // #endif // __ANNOTATION_FORMAT_WIDGET_DECLARE__ } // namespace #endif //__ANNOTATION_FORMAT_WIDGET_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/GuiQt/AnnotationInsertNewWidget.cxx000066400000000000000000000501551300200146000274440ustar00rootroot00000000000000 /*LICENSE_START*/ /* * Copyright (C) 2015 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 __ANNOTATION_INSERT_NEW_WIDGET_DECLARE__ #include "AnnotationInsertNewWidget.h" #undef __ANNOTATION_INSERT_NEW_WIDGET_DECLARE__ #include #include #include #include #include #include #include #include #include "Annotation.h" #include "AnnotationFile.h" #include "AnnotationManager.h" #include "AnnotationMenuFileSelection.h" #include "AnnotationRedoUndoCommand.h" #include "Brain.h" #include "CaretAssert.h" #include "CaretUndoStack.h" #include "DisplayPropertiesAnnotation.h" #include "EventAnnotationCreateNewType.h" #include "EventGraphicsUpdateAllWindows.h" #include "EventUserInterfaceUpdate.h" #include "EventManager.h" #include "GuiManager.h" #include "WuQMessageBox.h" #include "WuQtUtilities.h" using namespace caret; /** * \class caret::AnnotationInsertNewWidget * \brief Widget for creating new annotations. * \ingroup GuiQt */ /** * Constructor. * * @param browserWindowIndex * Index of the browser window. * @param parent * Parent of this widget. */ AnnotationInsertNewWidget::AnnotationInsertNewWidget(const int32_t browserWindowIndex, QWidget* parent) : 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); QToolButton* tabSpaceToolButton = createSpaceToolButton(AnnotationCoordinateSpaceEnum::TAB); QToolButton* stereotaxicSpaceToolButton = createSpaceToolButton(AnnotationCoordinateSpaceEnum::STEREOTAXIC); QToolButton* surfaceSpaceToolButton = createSpaceToolButton(AnnotationCoordinateSpaceEnum::SURFACE); QToolButton* windowSpaceToolButton = createSpaceToolButton(AnnotationCoordinateSpaceEnum::WINDOW); const bool smallButtonsFlag = false; if (smallButtonsFlag) { const int mw = 24; const int mh = 24; shapeBoxToolButton->setMaximumSize(mw, mh); shapeImageToolButton->setMaximumSize(mw, mh); shapeLineToolButton->setMaximumSize(mw, mh); shapeOvalToolButton->setMaximumSize(mw, mh); shapeTextToolButton->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"); QGridLayout* gridLayout = new QGridLayout(this); WuQtUtilities::setLayoutSpacingAndMargins(gridLayout, 2, 2); const bool rowsLayoutFlag = true; if (rowsLayoutFlag) { QVBoxLayout* fileLayout = new QVBoxLayout(); fileLayout->addWidget(fileLabel, 0, Qt::AlignHCenter); fileLayout->addWidget(fileSelectionToolButton, 0, Qt::AlignHCenter); fileLayout->addStretch(); QLabel* insertLabel = new QLabel("Insert New"); gridLayout->addWidget(insertLabel, 0, 0, 1, 8, Qt::AlignHCenter); gridLayout->addLayout(fileLayout, 1, 0, 3, 1, (Qt::AlignTop | Qt::AlignHCenter)); gridLayout->setColumnMinimumWidth(1, 5); gridLayout->addWidget(spaceLabel, 1, 2, Qt::AlignLeft); gridLayout->addWidget(stereotaxicSpaceToolButton, 1, 3); gridLayout->addWidget(surfaceSpaceToolButton, 1, 4); gridLayout->addWidget(tabSpaceToolButton, 1, 5); gridLayout->addWidget(windowSpaceToolButton, 1, 6); gridLayout->setRowMinimumHeight(2, 2); gridLayout->addWidget(typeLabel, 3, 2, Qt::AlignLeft); gridLayout->addWidget(shapeBoxToolButton, 3, 3); gridLayout->addWidget(shapeImageToolButton, 3, 4); gridLayout->addWidget(shapeLineToolButton, 3, 5); gridLayout->addWidget(shapeOvalToolButton, 3, 6); gridLayout->addWidget(shapeTextToolButton, 3, 7); } else { QLabel* insertLabel = new QLabel("Insert New"); gridLayout->addWidget(insertLabel, 0, 0, 1, 8, Qt::AlignHCenter); gridLayout->addWidget(fileLabel, 1, 0, Qt::AlignHCenter); gridLayout->addWidget(fileSelectionToolButton, 2, 0, 2, 1, (Qt::AlignTop | Qt::AlignHCenter)); gridLayout->setColumnMinimumWidth(1, 15); gridLayout->addWidget(WuQtUtilities::createVerticalLineWidget(), 1, 1, 3, 1, Qt::AlignHCenter); gridLayout->addWidget(spaceLabel, 1, 2, Qt::AlignLeft); gridLayout->addWidget(stereotaxicSpaceToolButton, 1, 3); gridLayout->addWidget(surfaceSpaceToolButton, 1, 4); gridLayout->addWidget(tabSpaceToolButton, 1, 5); gridLayout->addWidget(windowSpaceToolButton, 1, 6); QSpacerItem* rowSpaceItem = new QSpacerItem(5, 5, QSizePolicy::Fixed, QSizePolicy::Fixed); gridLayout->addItem(rowSpaceItem, 2, 3, 1, 6); gridLayout->addWidget(typeLabel, 3, 2, Qt::AlignLeft); gridLayout->addWidget(shapeBoxToolButton, 3, 3); gridLayout->addWidget(shapeImageToolButton, 3, 4); gridLayout->addWidget(shapeLineToolButton, 3, 5); gridLayout->addWidget(shapeOvalToolButton, 3, 6); gridLayout->addWidget(shapeTextToolButton, 3, 7); } setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); /* * Default Space to Tab * Default Shape to Text * Do this before creating action groups to avoid * triggering signals. */ m_spaceActionGroup->blockSignals(true); tabSpaceToolButton->defaultAction()->setChecked(true); m_spaceActionGroup->blockSignals(false); m_shapeActionGroup->blockSignals(true); shapeTextToolButton->defaultAction()->setChecked(true); m_shapeActionGroup->blockSignals(false); } /** * Destructor. */ AnnotationInsertNewWidget::~AnnotationInsertNewWidget() { } /** * Update the content. */ void AnnotationInsertNewWidget::updateContent() { itemSelectedFromFileSelectionMenu(); } /** * Gets called when an item is selected from the file selection menu. */ void AnnotationInsertNewWidget::itemSelectedFromFileSelectionMenu() { /* * Add a space so that the arrow is not */ m_fileSelectionToolButtonAction->setText(m_fileSelectionMenu->getSelectedNameForToolButton()); } /** * @return Create a file selection/menu toolbutton. */ QToolButton* AnnotationInsertNewWidget::createFileSelectionToolButton() { m_fileSelectionMenu = new AnnotationMenuFileSelection(); QObject::connect(m_fileSelectionMenu, SIGNAL(menuItemSelected()), this, SLOT(itemSelectedFromFileSelectionMenu())); m_fileSelectionToolButtonAction = new QAction(m_fileSelectionMenu->getSelectedNameForToolButton(), this); m_fileSelectionToolButtonAction->setToolTip("Choose file for new annotation"); m_fileSelectionToolButtonAction->setMenu(m_fileSelectionMenu); QToolButton* fileSelectionToolButton = new QToolButton(); fileSelectionToolButton->setDefaultAction(m_fileSelectionToolButtonAction); fileSelectionToolButton->setFixedWidth(fileSelectionToolButton->sizeHint().width()); return fileSelectionToolButton; } /** * @return Create the shape tool button for the given annotation type. * * @param annotationType * The annotation type. */ QToolButton* AnnotationInsertNewWidget::createShapeToolButton(const AnnotationTypeEnum::Enum annotationType) { const QString typeGuiName = AnnotationTypeEnum::toGuiName(annotationType); QToolButton* toolButton = new QToolButton(); QAction* action = new QAction(createShapePixmap(toolButton, annotationType), typeGuiName, this); action->setData(AnnotationTypeEnum::toIntegerCode(annotationType)); action->setToolTip(typeGuiName + " annotation"); action->setCheckable(true); action->setChecked(false); toolButton->setDefaultAction(action); return toolButton; } /** * Called when a space or shape action triggered. */ void AnnotationInsertNewWidget::spaceOrShapeActionTriggered() { const QAction* spaceAction = m_spaceActionGroup->checkedAction(); if (spaceAction == NULL) { WuQMessageBox::errorOk(this, "No space is selected. Select a space."); return; } const QAction* shapeAction = m_shapeActionGroup->checkedAction(); if (shapeAction == NULL) { WuQMessageBox::errorOk(this, "No shape is selected. Select a shape."); return; } AnnotationFile* annotationFile = m_fileSelectionMenu->getSelectedAnnotationFile(); CaretAssert(annotationFile); CaretAssert(spaceAction); const int spaceInt = spaceAction->data().toInt(); bool spaceValidFlag = false; AnnotationCoordinateSpaceEnum::Enum annSpace = AnnotationCoordinateSpaceEnum::fromIntegerCode(spaceInt, &spaceValidFlag); CaretAssert(spaceValidFlag); CaretAssert(shapeAction); const int shapeInt = shapeAction->data().toInt(); bool shapeValidFlag = false; AnnotationTypeEnum::Enum annShape = AnnotationTypeEnum::fromIntegerCode(shapeInt, &shapeValidFlag); CaretAssert(shapeValidFlag); DisplayPropertiesAnnotation* dpa = GuiManager::get()->getBrain()->getDisplayPropertiesAnnotation(); dpa->setDisplayAnnotations(true); EventManager::get()->sendEvent(EventAnnotationCreateNewType(annotationFile, annSpace, annShape).getPointer()); } /** * Create a pixmap for the given annotation shape type. * * @param widget * To color the pixmap with backround and foreground, * the palette from the given widget is used. * @param annotationType * The annotation type. * @return * Pixmap with icon for the given annotation type. */ QPixmap AnnotationInsertNewWidget::createShapePixmap(const QWidget* widget, const AnnotationTypeEnum::Enum annotationType) { 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::createPixmapWidgetPainter(widget, pixmap); /** * NOTE: ORIGIN is in TOP LEFT corner of pixmap. */ switch (annotationType) { case AnnotationTypeEnum::BOX: painter->drawRect(1, 1, width - 2, height - 2); break; case AnnotationTypeEnum::COLOR_BAR: CaretAssertMessage(0, "No pixmap for colorbar as user does not create them like other annotations"); break; case AnnotationTypeEnum::IMAGE: { const int blueAsGray = qGray(25,25,255); QColor skyColor(blueAsGray, blueAsGray, blueAsGray); /* * Background (sky) */ painter->fillRect(pixmap.rect(), skyColor); const int greenAsGray = qGray(0, 255, 0); QColor terrainColor(greenAsGray, greenAsGray, greenAsGray); /* * Terrain */ painter->setBrush(terrainColor); painter->setPen(terrainColor); const int w14 = width * 0.25; const int h23 = height * 0.667; const int h34 = height * 0.75; QPolygon terrain; terrain.push_back(QPoint(1, height - 1)); terrain.push_back(QPoint(width - 1, height - 1)); terrain.push_back(QPoint(width - 1, h23)); terrain.push_back(QPoint(w14 * 3, h34)); terrain.push_back(QPoint(w14 * 2, h23)); terrain.push_back(QPoint(w14, h34)); terrain.push_back(QPoint(1, h23)); terrain.push_back(QPoint(1, height - 1)); painter->drawPolygon(terrain); const int yellowAsGray = qGray(255, 255, 0); QColor sunColor(yellowAsGray, yellowAsGray, yellowAsGray); /* * Sun */ painter->setBrush(sunColor); painter->setPen(sunColor); const int radius = width * 0.25; painter->drawEllipse(width * 0.33, height * 0.33, radius, radius); } break; case AnnotationTypeEnum::LINE: painter->drawLine(1, height - 1, width - 1, 1); break; case AnnotationTypeEnum::OVAL: painter->drawEllipse(1, 1, width - 1, height - 1); break; case AnnotationTypeEnum::TEXT: { QFont font = painter->font(); font.setPixelSize(20); painter->setFont(font); painter->drawText(pixmap.rect(), (Qt::AlignCenter), "A"); } break; } return pixmap; } /** * @return Create the space tool button for the given annotation space. * * @param annotationSpace * The annotation space */ QToolButton* AnnotationInsertNewWidget::createSpaceToolButton(const AnnotationCoordinateSpaceEnum::Enum annotationSpace) { switch (annotationSpace) { case AnnotationCoordinateSpaceEnum::PIXELS: CaretAssertMessage(0, "Annotations in pixel space not supported."); break; case AnnotationCoordinateSpaceEnum::STEREOTAXIC: break; case AnnotationCoordinateSpaceEnum::SURFACE: break; case AnnotationCoordinateSpaceEnum::TAB: break; case AnnotationCoordinateSpaceEnum::WINDOW: break; } const bool useIconFlag = true; QToolButton* toolButton = new QToolButton(); QAction* action = NULL; if (useIconFlag) { action = new QAction(createSpacePixmap(toolButton, annotationSpace), AnnotationCoordinateSpaceEnum::toGuiAbbreviatedName(annotationSpace), this); } else { action = new QAction(AnnotationCoordinateSpaceEnum::toGuiAbbreviatedName(annotationSpace), this); } action->setData((int)AnnotationCoordinateSpaceEnum::toIntegerCode(annotationSpace)); action->setToolTip(AnnotationCoordinateSpaceEnum::toToolTip(annotationSpace)); toolButton->setDefaultAction(action); action->setCheckable(true); action->setChecked(false); return toolButton; } /** * Create a pixmap for the given annotation coordinate space * * @param widget * To color the pixmap with backround and foreground, * the palette from the given widget is used. * @param annotationSpace * The annotation coordinate space. * @return * Pixmap with icon for the given annotation coordinate space. */ QPixmap AnnotationInsertNewWidget::createSpacePixmap(const QWidget* widget, const AnnotationCoordinateSpaceEnum::Enum annotationSpace) { 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::createPixmapWidgetPainter(widget, pixmap); /** * NOTE: ORIGIN is in TOP LEFT corner of pixmap. */ switch (annotationSpace) { case AnnotationCoordinateSpaceEnum::PIXELS: CaretAssertMessage(0, "Annotations in pixel space not supported."); break; case AnnotationCoordinateSpaceEnum::STEREOTAXIC: break; case AnnotationCoordinateSpaceEnum::SURFACE: break; case AnnotationCoordinateSpaceEnum::TAB: break; case AnnotationCoordinateSpaceEnum::WINDOW: break; } const QString letter = AnnotationCoordinateSpaceEnum::toGuiAbbreviatedName(annotationSpace); QFont font = painter->font(); font.setPixelSize(20); painter->setFont(font); painter->drawText(pixmap.rect(), (Qt::AlignCenter), letter); return pixmap; } connectome-workbench-1.2.3+git41-gc4c6c90/src/GuiQt/AnnotationInsertNewWidget.h000066400000000000000000000056451300200146000270750ustar00rootroot00000000000000#ifndef __ANNOTATION_INSERT_NEW_WIDGET_H__ #define __ANNOTATION_INSERT_NEW_WIDGET_H__ /*LICENSE_START*/ /* * Copyright (C) 2015 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 "AnnotationCoordinateSpaceEnum.h" #include "AnnotationTypeEnum.h" class QActionGroup; class QToolButton; namespace caret { class AnnotationMenuFileSelection; class AnnotationInsertNewWidget : public QWidget { Q_OBJECT public: AnnotationInsertNewWidget(const int32_t browserWindowIndex, QWidget* parent = 0); virtual ~AnnotationInsertNewWidget(); // ADD_NEW_METHODS_HERE void updateContent(); private slots: void itemSelectedFromFileSelectionMenu(); void spaceOrShapeActionTriggered(); private: AnnotationInsertNewWidget(const AnnotationInsertNewWidget&); AnnotationInsertNewWidget& operator=(const AnnotationInsertNewWidget&); QToolButton* createShapeToolButton(const AnnotationTypeEnum::Enum annotationType); QToolButton* createSpaceToolButton(const AnnotationCoordinateSpaceEnum::Enum annotationSpace); QPixmap createShapePixmap(const QWidget* widget, const AnnotationTypeEnum::Enum annotationType); QPixmap createSpacePixmap(const QWidget* widget, const AnnotationCoordinateSpaceEnum::Enum annotationSpace); QToolButton* createFileSelectionToolButton(); const int32_t m_browserWindowIndex; QActionGroup* m_spaceActionGroup; QActionGroup* m_shapeActionGroup; QAction* m_fileSelectionToolButtonAction; AnnotationMenuFileSelection* m_fileSelectionMenu; static AString s_previousImageFileDirectory; // ADD_NEW_MEMBERS_HERE }; #ifdef __ANNOTATION_INSERT_NEW_WIDGET_DECLARE__ AString AnnotationInsertNewWidget::s_previousImageFileDirectory; #endif // __ANNOTATION_INSERT_NEW_WIDGET_DECLARE__ } // namespace #endif //__ANNOTATION_INSERT_NEW_WIDGET_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/GuiQt/AnnotationLineArrowTipsWidget.cxx000066400000000000000000000147101300200146000302650ustar00rootroot00000000000000 /*LICENSE_START*/ /* * Copyright (C) 2015 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 __ANNOTATION_LINE_ARROW_TIPS_WIDGET_DECLARE__ #include "AnnotationLineArrowTipsWidget.h" #undef __ANNOTATION_LINE_ARROW_TIPS_WIDGET_DECLARE__ #include #include #include #include #include "AnnotationLine.h" #include "AnnotationManager.h" #include "AnnotationRedoUndoCommand.h" #include "Brain.h" #include "CaretAssert.h" #include "EventGraphicsUpdateAllWindows.h" #include "EventManager.h" #include "GuiManager.h" #include "WuQMessageBox.h" #include "WuQtUtilities.h" using namespace caret; /** * \class caret::AnnotationLineArrowTipsWidget * \brief Widget for enabling/disabling line arrow tips * \ingroup GuiQt */ /** * Constructor. */ AnnotationLineArrowTipsWidget::AnnotationLineArrowTipsWidget(const int32_t browserWindowIndex, QWidget* parent) : QWidget(parent), m_browserWindowIndex(browserWindowIndex) { QLabel* label = new QLabel("Line"); const QSize toolButtonSize(18, 18); 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())); QGridLayout* gridLayout = new QGridLayout(this); WuQtUtilities::setLayoutSpacingAndMargins(gridLayout, 2, 0); gridLayout->addWidget(label, 0, 0, Qt::AlignHCenter); gridLayout->addWidget(m_startArrowToolButton, 1, 0, Qt::AlignHCenter); gridLayout->addWidget(m_endArrowToolButton, 2, 0, Qt::AlignHCenter); setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); } /** * Destructor. */ AnnotationLineArrowTipsWidget::~AnnotationLineArrowTipsWidget() { } /** * Update with the given line annotation. * * @param annotationLines */ 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]; } bool allStartOnFlag = true; bool allEndOnFlag = true; const int32_t numLines = static_cast(annotationLines.size()); for (int32_t i = 0; i < numLines; i++) { CaretAssertVectorIndex(annotationLines, i); if ( ! annotationLines[i]->isDisplayStartArrow()) { allStartOnFlag = false; } if ( ! annotationLines[i]->isDisplayEndArrow()) { allEndOnFlag = false; } } if (numLines <= 0) { allStartOnFlag = false; allEndOnFlag = false; } m_startArrowToolButton->setChecked(allStartOnFlag); m_endArrowToolButton->setChecked(allEndOnFlag); if (numLines > 0) { setEnabled(true); AnnotationLine::setUserDefaultDisplayStartArrow(m_startArrowToolButton->isChecked()); AnnotationLine::setUserDefaultDisplayEndArrow(m_endArrowToolButton->isChecked()); } else { setEnabled(false); } } /** * Gets called when the line arrow start buttons is toggled. */ void AnnotationLineArrowTipsWidget::startArrowTipActionToggled() { AnnotationRedoUndoCommand* undoCommand = new AnnotationRedoUndoCommand(); undoCommand->setModeLineArrowStart(m_startArrowToolButton->isChecked(), m_annotations); AnnotationManager* annMan = GuiManager::get()->getBrain()->getAnnotationManager(); AString errorMessage; if ( ! annMan->applyCommand(undoCommand, errorMessage)) { WuQMessageBox::errorOk(this, errorMessage); } EventManager::get()->sendSimpleEvent(EventTypeEnum::EVENT_ANNOTATION_TOOLBAR_UPDATE); EventManager::get()->sendEvent(EventGraphicsUpdateAllWindows().getPointer()); AnnotationLine::setUserDefaultDisplayStartArrow(m_startArrowToolButton->isChecked()); } /** * Gets called when the line arrow end buttons is toggled. */ void AnnotationLineArrowTipsWidget::endArrowTipActionToggled() { AnnotationRedoUndoCommand* undoCommand = new AnnotationRedoUndoCommand(); undoCommand->setModeLineArrowEnd(m_endArrowToolButton->isChecked(), m_annotations); AnnotationManager* annMan = GuiManager::get()->getBrain()->getAnnotationManager(); AString errorMessage; if ( ! annMan->applyCommand(undoCommand, errorMessage)) { WuQMessageBox::errorOk(this, errorMessage); } EventManager::get()->sendSimpleEvent(EventTypeEnum::EVENT_ANNOTATION_TOOLBAR_UPDATE); EventManager::get()->sendEvent(EventGraphicsUpdateAllWindows().getPointer()); AnnotationLine::setUserDefaultDisplayEndArrow(m_endArrowToolButton->isChecked()); } connectome-workbench-1.2.3+git41-gc4c6c90/src/GuiQt/AnnotationLineArrowTipsWidget.h000066400000000000000000000043501300200146000277110ustar00rootroot00000000000000#ifndef __ANNOTATION_LINE_ARROW_TIPS_WIDGET_H__ #define __ANNOTATION_LINE_ARROW_TIPS_WIDGET_H__ /*LICENSE_START*/ /* * Copyright (C) 2015 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 QToolButton; namespace caret { class Annotation; class AnnotationLine; class AnnotationLineArrowTipsWidget : public QWidget { Q_OBJECT public: AnnotationLineArrowTipsWidget(const int32_t browserWindowIndex, QWidget* parent = 0); virtual ~AnnotationLineArrowTipsWidget(); void updateContent(std::vector& annotationLines); private slots: void startArrowTipActionToggled(); void endArrowTipActionToggled(); // ADD_NEW_METHODS_HERE private: AnnotationLineArrowTipsWidget(const AnnotationLineArrowTipsWidget&); AnnotationLineArrowTipsWidget& operator=(const AnnotationLineArrowTipsWidget&); const int32_t m_browserWindowIndex; QToolButton* m_startArrowToolButton; QToolButton* m_endArrowToolButton; std::vector m_annotations; // ADD_NEW_MEMBERS_HERE }; #ifdef __ANNOTATION_LINE_ARROW_TIPS_WIDGET_DECLARE__ // #endif // __ANNOTATION_LINE_ARROW_TIPS_WIDGET_DECLARE__ } // namespace #endif //__ANNOTATION_LINE_ARROW_TIPS_WIDGET_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/GuiQt/AnnotationMenuArrange.cxx000066400000000000000000000574361300200146000265770ustar00rootroot00000000000000 /*LICENSE_START*/ /* * Copyright (C) 2015 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 __ANNOTATION_MENU_ARRANGE_DECLARE__ #include "AnnotationMenuArrange.h" #undef __ANNOTATION_MENU_ARRANGE_DECLARE__ #include #include "Annotation.h" #include "AnnotationArrangerInputs.h" #include "AnnotationManager.h" #include "Brain.h" #include "CaretAssert.h" #include "CaretLogger.h" #include "EventAnnotationGrouping.h" #include "EventGetBrainOpenGLTextRenderer.h" #include "EventGraphicsUpdateAllWindows.h" #include "EventManager.h" #include "GuiManager.h" #include "WuQMessageBox.h" #include "WuQtUtilities.h" using namespace caret; /** * \class caret::AnnotationMenuArrange * \brief Menu for arranging annotations. * \ingroup GuiQt */ /** * Constructor. * * @param parent * The parent widget. * @param browserWindowIndex * Index of the browser window. */ AnnotationMenuArrange::AnnotationMenuArrange(const int32_t browserWindowIndex, QWidget* parent) : QMenu(parent), m_browserWindowIndex(browserWindowIndex) { addAlignmentSelections(); addSeparator(); addDistributeSelections(); addSeparator(); addGroupingSelections(); QObject::connect(this, SIGNAL(aboutToShow()), this, SLOT(menuAboutToShow())); QObject::connect(this, SIGNAL(triggered(QAction*)), this, SLOT(menuActionTriggered(QAction*))); } /** * Destructor. */ AnnotationMenuArrange::~AnnotationMenuArrange() { } /** * Add alignment options to the menu. */ void AnnotationMenuArrange::addAlignmentSelections() { std::vector alignments; AnnotationAlignmentEnum::getAllEnums(alignments); for (std::vector::iterator iter = alignments.begin(); iter != alignments.end(); iter++) { const AnnotationAlignmentEnum::Enum annAlign = *iter; const QString enumText = AnnotationAlignmentEnum::toGuiName(annAlign); const QString enumName = AnnotationAlignmentEnum::toName(annAlign); QPixmap pixmap = createAlignmentPixmap(this, annAlign); QAction* action = addAction(enumText); action->setIcon(pixmap); action->setData(enumName); } } /** * Add distribution items to the menu. */ void AnnotationMenuArrange::addDistributeSelections() { std::vector distributes; AnnotationDistributeEnum::getAllEnums(distributes); for (std::vector::iterator iter = distributes.begin(); iter != distributes.end(); iter++) { const AnnotationDistributeEnum::Enum annDist = *iter; const QString enumText = AnnotationDistributeEnum::toGuiName(annDist); const QString enumName = AnnotationDistributeEnum::toName(annDist); QPixmap pixmap = createDistributePixmap(this, annDist); QAction* action = addAction(enumText); action->setIcon(pixmap); action->setData(enumName); } } /** * Add distribution items to the menu. */ void AnnotationMenuArrange::addGroupingSelections() { m_groupAction = NULL; m_regroupAction = NULL; m_ungroupAction = NULL; std::vector groupings; AnnotationGroupingModeEnum::getAllEnums(groupings); for (std::vector::iterator iter = groupings.begin(); iter != groupings.end(); iter++) { const AnnotationGroupingModeEnum::Enum groupingMode = *iter; const QString enumText = AnnotationGroupingModeEnum::toGuiName(groupingMode); const QString enumName = AnnotationGroupingModeEnum::toName(groupingMode); QPixmap pixmap = createGroupingPixmap(this, groupingMode); QAction* action = addAction(enumText); action->setIcon(pixmap); action->setData(enumName); switch (groupingMode) { case AnnotationGroupingModeEnum::GROUP: m_groupAction = action; break; case AnnotationGroupingModeEnum::REGROUP: m_regroupAction = action; break; case AnnotationGroupingModeEnum::UNGROUP: m_ungroupAction = action; break; } } CaretAssert(m_groupAction); CaretAssert(m_regroupAction); CaretAssert(m_ungroupAction); } /* * Gets called when the menu is about to show * so that its menu items can be enabled/disabled. */ void AnnotationMenuArrange::menuAboutToShow() { AnnotationManager* annMan = GuiManager::get()->getBrain()->getAnnotationManager(); m_groupAction->setEnabled(annMan->isGroupingModeValid(m_browserWindowIndex, AnnotationGroupingModeEnum::GROUP)); m_regroupAction->setEnabled(annMan->isGroupingModeValid(m_browserWindowIndex, AnnotationGroupingModeEnum::REGROUP)); m_ungroupAction->setEnabled(annMan->isGroupingModeValid(m_browserWindowIndex, AnnotationGroupingModeEnum::UNGROUP)); } /** * Gets called when the user selects a menu item. */ void AnnotationMenuArrange::menuActionTriggered(QAction* action) { CaretAssert(action); const QString enumName = action->data().toString(); bool validAlignmentFlag = false; const AnnotationAlignmentEnum::Enum annAlign = AnnotationAlignmentEnum::fromName(enumName, &validAlignmentFlag); bool validDistributeFlag = false; const AnnotationDistributeEnum::Enum annDist = AnnotationDistributeEnum::fromName(enumName, &validDistributeFlag); bool validGroupingFlag = false; const AnnotationGroupingModeEnum::Enum annGroup = AnnotationGroupingModeEnum::fromName(enumName, &validGroupingFlag); if (validAlignmentFlag) { applyAlignment(annAlign); } else if (validDistributeFlag) { applyDistribute(annDist); } else if (validGroupingFlag) { applyGrouping(annGroup); } else { const AString msg("Unrecognized Enum name in Annotation Align Menu \"" + enumName + "\""); CaretAssertMessage(0, msg); CaretLogSevere(msg); } EventManager::get()->sendSimpleEvent(EventTypeEnum::EVENT_ANNOTATION_TOOLBAR_UPDATE); EventManager::get()->sendEvent(EventGraphicsUpdateAllWindows().getPointer()); } /** * Apply alignment selection. * * @param alignment * Selected alignment. */ void AnnotationMenuArrange::applyAlignment(const AnnotationAlignmentEnum::Enum alignment) { EventGetBrainOpenGLTextRenderer textRendererEvent(m_browserWindowIndex); EventManager::get()->sendEvent(textRendererEvent.getPointer()); BrainOpenGLTextRenderInterface* textRenderer = textRendererEvent.getTextRenderer(); if (textRenderer == NULL) { WuQMessageBox::errorOk(this, "Failed to get text renderer for window " + QString::number(m_browserWindowIndex)); return; } AnnotationArrangerInputs alignMod(textRenderer, m_browserWindowIndex); AnnotationManager* annMan = GuiManager::get()->getBrain()->getAnnotationManager(); AString errorMessage; if ( ! annMan->alignAnnotations(alignMod, alignment, errorMessage)) { WuQMessageBox::errorOk(this, errorMessage); } EventManager::get()->sendSimpleEvent(EventTypeEnum::EVENT_ANNOTATION_TOOLBAR_UPDATE); EventManager::get()->sendEvent(EventGraphicsUpdateAllWindows().getPointer()); } /** * Apply distribute selection. * * @param distribute * Selected distribute. */ void AnnotationMenuArrange::applyDistribute(const AnnotationDistributeEnum::Enum distribute) { EventGetBrainOpenGLTextRenderer textRendererEvent(m_browserWindowIndex); EventManager::get()->sendEvent(textRendererEvent.getPointer()); BrainOpenGLTextRenderInterface* textRenderer = textRendererEvent.getTextRenderer(); if (textRenderer == NULL) { WuQMessageBox::errorOk(this, "Failed to get text renderer for window " + QString::number(m_browserWindowIndex)); return; } AnnotationArrangerInputs distributeMod(textRenderer, m_browserWindowIndex); AnnotationManager* annMan = GuiManager::get()->getBrain()->getAnnotationManager(); AString errorMessage; if ( ! annMan->distributeAnnotations(distributeMod, distribute, errorMessage)) { WuQMessageBox::errorOk(this, errorMessage); } EventManager::get()->sendSimpleEvent(EventTypeEnum::EVENT_ANNOTATION_TOOLBAR_UPDATE); EventManager::get()->sendEvent(EventGraphicsUpdateAllWindows().getPointer()); } /** * Apply grouping selection. * * @param grouping * Selected grouping. */ void AnnotationMenuArrange::applyGrouping(const AnnotationGroupingModeEnum::Enum grouping) { AnnotationManager* annMan = GuiManager::get()->getBrain()->getAnnotationManager(); AString errorMessage; if ( ! annMan->applyGroupingMode(m_browserWindowIndex, grouping, errorMessage)) { WuQMessageBox::errorOk(this, errorMessage); } EventManager::get()->sendSimpleEvent(EventTypeEnum::EVENT_ANNOTATION_TOOLBAR_UPDATE); EventManager::get()->sendEvent(EventGraphicsUpdateAllWindows().getPointer()); } /** * Create an alignment pixmap. * * @param widget * To color the pixmap with backround and foreground, * the palette from the given widget is used. * @param alignment * The alignment. * @return * Pixmap with icon for the given alignment. */ QPixmap AnnotationMenuArrange::createAlignmentPixmap(const QWidget* widget, const AnnotationAlignmentEnum::Enum alignment) { CaretAssert(widget); const float pixmapSize = 24.0; const float halfPixmapSize = pixmapSize / 2.0; QPixmap pixmap(static_cast(pixmapSize), static_cast(pixmapSize)); QSharedPointer painter = WuQtUtilities::createPixmapWidgetPainterOriginBottomLeft(widget, pixmap); const qreal minInset = 2; const qreal minInset2 = minInset * 2; const qreal minInset3 = minInset * 3; const qreal maxInset = pixmapSize - minInset; const QLineF horizontalLine(minInset, 0.0, maxInset, 0.0); const QLineF verticalLine(0.0, minInset, 0.0, maxInset); const qreal rectangleThickness = pixmapSize * 0.25; const qreal longRectangleLength = pixmapSize - (minInset * 4); const qreal shortRectangleLength = longRectangleLength * 0.60; const QRectF longVerticalRectangle(QPointF(0.0, 0.0), QPointF(rectangleThickness, longRectangleLength)); const QRectF shortVerticalRectangle(QPointF(0.0, 0.0), QPointF(rectangleThickness, shortRectangleLength)); const qreal shortVerticalRectangleOffset = pixmapSize - minInset2 - rectangleThickness; const QRectF longHorizontalRectangle(QPointF(0.0, 0.0), QPointF(longRectangleLength, rectangleThickness)); const QRectF shortHorizontalRectangle(QPointF(0.0, 0.0), QPointF(shortRectangleLength, rectangleThickness)); const qreal longRectangleSpace = pixmapSize - longRectangleLength; const qreal shortRectangleSpace = pixmapSize - shortRectangleLength; QBrush foregroundBrush = widget->palette().brush(widget->foregroundRole()); QPen foregroundPen = painter->pen(); QColor foregroundColor = widget->palette().brush(widget->foregroundRole()).color(); switch (alignment) { case AnnotationAlignmentEnum::ALIGN_BOTTOM: drawLine(painter, horizontalLine, 0.0, minInset); drawRect(painter, foregroundColor, longVerticalRectangle, minInset2, minInset3); drawRect(painter, foregroundColor, shortVerticalRectangle, shortVerticalRectangleOffset, minInset3); break; case AnnotationAlignmentEnum::ALIGN_CENTER: drawLine(painter, verticalLine, halfPixmapSize, 0.0); drawRect(painter, foregroundColor, longHorizontalRectangle, (longRectangleSpace / 2.0), minInset2); drawRect(painter, foregroundColor, shortHorizontalRectangle, (shortRectangleSpace / 2.0), shortVerticalRectangleOffset); break; case AnnotationAlignmentEnum::ALIGN_LEFT: drawLine(painter, verticalLine, minInset, 0.0); drawRect(painter, foregroundColor, longHorizontalRectangle, minInset3, minInset2); drawRect(painter, foregroundColor, shortHorizontalRectangle, minInset3, shortVerticalRectangleOffset); break; case AnnotationAlignmentEnum::ALIGN_MIDDLE: drawLine(painter, horizontalLine, 0.0, halfPixmapSize); drawRect(painter, foregroundColor, longVerticalRectangle, minInset2, (longRectangleSpace / 2.0)); drawRect(painter, foregroundColor, shortVerticalRectangle, shortVerticalRectangleOffset, (shortRectangleSpace / 2.0)); break; case AnnotationAlignmentEnum::ALIGN_RIGHT: drawLine(painter, verticalLine, maxInset, 0.0); drawRect(painter, foregroundColor, longHorizontalRectangle, (longRectangleSpace - minInset3), minInset2); drawRect(painter, foregroundColor, shortHorizontalRectangle, (shortRectangleSpace - minInset3), shortVerticalRectangleOffset); break; case AnnotationAlignmentEnum::ALIGN_TOP: drawLine(painter, horizontalLine, 0.0, maxInset); drawRect(painter, foregroundColor, longVerticalRectangle, minInset2, (longRectangleSpace - minInset3)); drawRect(painter, foregroundColor, shortVerticalRectangle, shortVerticalRectangleOffset, (shortRectangleSpace - minInset3)); break; } return pixmap; } /** * Create a distribute pixmap. * * @param widget * To color the pixmap with backround and foreground, * the palette from the given widget is used. * @param distribute * The distribute type. * @return * Pixmap with icon for the given distribute. */ QPixmap AnnotationMenuArrange::createDistributePixmap(const QWidget* widget, const AnnotationDistributeEnum::Enum distribute) { CaretAssert(widget); const float pixmapSize = 24.0; const float halfPixmapSize = pixmapSize / 2.0; QPixmap pixmap(static_cast(pixmapSize), static_cast(pixmapSize)); QSharedPointer painter = WuQtUtilities::createPixmapWidgetPainterOriginBottomLeft(widget, pixmap); const qreal minInset = 2; const qreal minInset2 = minInset * 2; const qreal minInset3 = minInset * 3; const qreal maxInset = pixmapSize - minInset; const QLineF horizontalLine(minInset, 0.0, maxInset, 0.0); const QLineF verticalLine(0.0, minInset, 0.0, maxInset); const qreal rectangleThickness = pixmapSize * 0.15; const qreal longRectangleLength = pixmapSize - (minInset3 * 2); const qreal shortRectangleLength = longRectangleLength * 0.50; const qreal midSizeRectangleLength = longRectangleLength * 0.75; const QRectF longVerticalRectangle(QPointF(0.0, 0.0), QPointF(rectangleThickness, longRectangleLength)); const QRectF midSizeVerticalRectangle(QPointF(0.0, 0.0), QPointF(rectangleThickness, midSizeRectangleLength)); const QRectF shortVerticalRectangle(QPointF(0.0, 0.0), QPointF(rectangleThickness, shortRectangleLength)); const qreal shortVerticalRectangleOffset = halfPixmapSize - (rectangleThickness / 2.0); const qreal midSizeVerticalRectangleOffset = pixmapSize - minInset2 - rectangleThickness; const QRectF longHorizontalRectangle(QPointF(0.0, 0.0), QPointF(longRectangleLength, rectangleThickness)); const QRectF midSizeHorizontalRectangle(QPointF(0.0, 0.0), QPointF(midSizeRectangleLength, rectangleThickness)); const QRectF shortHorizontalRectangle(QPointF(0.0, 0.0), QPointF(shortRectangleLength, rectangleThickness)); const qreal shortRectangleSpace = pixmapSize - shortRectangleLength; const qreal midSizeRectangleSpace = pixmapSize - midSizeRectangleLength; QBrush foregroundBrush = widget->palette().brush(widget->foregroundRole()); QPen foregroundPen = painter->pen(); QColor foregroundColor = widget->palette().brush(widget->foregroundRole()).color(); switch (distribute) { case AnnotationDistributeEnum::HORIZONTALLY: drawLine(painter, horizontalLine, 0.0, maxInset); drawLine(painter, horizontalLine, 0.0, minInset); drawRect(painter, foregroundColor, longVerticalRectangle, minInset2, minInset3); drawRect(painter, foregroundColor, shortVerticalRectangle, shortVerticalRectangleOffset, (shortRectangleSpace / 2.0)); drawRect(painter, foregroundColor, midSizeVerticalRectangle, midSizeVerticalRectangleOffset, (midSizeRectangleSpace / 2.0)); break; case AnnotationDistributeEnum::VERTICALLY: drawLine(painter, verticalLine, minInset, 0.0); drawLine(painter, verticalLine, maxInset, 0.0); drawRect(painter, foregroundColor, longHorizontalRectangle, minInset3, minInset2); drawRect(painter, foregroundColor, shortHorizontalRectangle, (shortRectangleSpace / 2.0), shortVerticalRectangleOffset); drawRect(painter, foregroundColor, midSizeHorizontalRectangle, (midSizeRectangleSpace / 2.0), midSizeVerticalRectangleOffset); break; } return pixmap; } /** * Create a grouping pixmap. * * @param widget * To color the pixmap with backround and foreground, * the palette from the given widget is used. * @param grouping * The grouping type. * @return * Pixmap with icon for the given grouping. */ QPixmap AnnotationMenuArrange::createGroupingPixmap(const QWidget* widget, const AnnotationGroupingModeEnum::Enum grouping) { CaretAssert(widget); const float pixmapSize = 24.0; QPixmap pixmap(static_cast(pixmapSize), static_cast(pixmapSize)); QSharedPointer painter = WuQtUtilities::createPixmapWidgetPainterOriginCenter100x100(widget, pixmap); const float xyOne = 75.0; const float xyTwo = 25.0; const float boxSize = 100.0; painter->drawRect(-xyOne, -xyTwo, boxSize, boxSize); painter->drawRect(-xyTwo, -xyOne, boxSize, boxSize); QColor foregroundColor = widget->palette().brush(widget->foregroundRole()).color(); painter->setBrush(foregroundColor); const float dotSize = 20.0; switch (grouping) { case AnnotationGroupingModeEnum::GROUP: painter->drawEllipse(QPointF(-xyOne, -xyOne), dotSize, dotSize); painter->drawEllipse(QPointF( xyOne, -xyOne), dotSize, dotSize); painter->drawEllipse(QPointF( xyOne, xyOne), dotSize, dotSize); painter->drawEllipse(QPointF(-xyOne, xyOne), dotSize, dotSize); break; case AnnotationGroupingModeEnum::REGROUP: painter->drawEllipse(QPointF(-xyOne, -xyOne), dotSize, dotSize); painter->drawEllipse(QPointF( xyOne, -xyOne), dotSize, dotSize); painter->drawEllipse(QPointF( xyOne, xyOne), dotSize, dotSize); painter->drawEllipse(QPointF(-xyOne, xyOne), dotSize, dotSize); if (grouping == AnnotationGroupingModeEnum::REGROUP) { QVector points; points.push_back(QPoint(-100, -10)); points.push_back(QPoint( 10, -10)); points.push_back(QPoint( 10, -50)); points.push_back(QPoint( 80, 0)); points.push_back(QPoint( 10, 50)); points.push_back(QPoint( 10, 10)); points.push_back(QPoint(-100, 10)); points.push_back(QPoint(-100, -10)); painter->drawPolygon(QPolygon(points)); } break; case AnnotationGroupingModeEnum::UNGROUP: painter->drawEllipse(QPointF(-xyTwo, -xyOne), dotSize, dotSize); painter->drawEllipse(QPointF(-xyTwo + boxSize, -xyOne), dotSize, dotSize); painter->drawEllipse(QPointF(-xyTwo + boxSize, -xyOne + boxSize), dotSize, dotSize); painter->drawEllipse(QPointF(-xyTwo, -xyOne + boxSize), dotSize, dotSize); painter->drawEllipse(QPointF(-xyOne, -xyTwo), dotSize, dotSize); painter->drawEllipse(QPointF(-xyOne + boxSize, -xyTwo), dotSize, dotSize); painter->drawEllipse(QPointF(-xyOne + boxSize, -xyTwo + boxSize), dotSize, dotSize); painter->drawEllipse(QPointF(-xyOne, -xyTwo + boxSize), dotSize, dotSize); break; } return pixmap; } /** * Draw a line at the given X,Y using the painter. * * @param painter * The painter. * @param line * The line * @param x * Translate to X. * @param y * Translate to Y. */ void AnnotationMenuArrange::drawLine(QSharedPointer& painter, const QLineF& line, const qreal x, const qreal y) { painter->save(); painter->translate(x, y); painter->drawLine(line); painter->restore(); } /** * Draw a filled rectangle at the given X,Y using the painter and in the given color. * * @param painter * The painter. * @param color * Color of filled rectangle. * @param rectangle * The rectangle * @param x * Translate to X. * @param y * Translate to Y. */ void AnnotationMenuArrange::drawRect(QSharedPointer& painter, const QColor& color, const QRectF& rectangle, const qreal x, const qreal y) { painter->save(); painter->translate(x, y); painter->fillRect(rectangle, color); painter->restore(); } connectome-workbench-1.2.3+git41-gc4c6c90/src/GuiQt/AnnotationMenuArrange.h000066400000000000000000000064561300200146000262200ustar00rootroot00000000000000#ifndef __ANNOTATION_MENU_ARRANGE_H__ #define __ANNOTATION_MENU_ARRANGE_H__ /*LICENSE_START*/ /* * Copyright (C) 2015 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 "AnnotationAlignmentEnum.h" #include "AnnotationDistributeEnum.h" #include "AnnotationGroupingModeEnum.h" namespace caret { class AnnotationMenuArrange : public QMenu { Q_OBJECT public: AnnotationMenuArrange(const int32_t browserWindowIndex, QWidget* parent = 0); virtual ~AnnotationMenuArrange(); // ADD_NEW_METHODS_HERE private slots: void menuActionTriggered(QAction* action); void menuAboutToShow(); private: void addAlignmentSelections(); void addDistributeSelections(); void addGroupingSelections(); void applyAlignment(const AnnotationAlignmentEnum::Enum alignment); void applyDistribute(const AnnotationDistributeEnum::Enum distribute); void applyGrouping(const AnnotationGroupingModeEnum::Enum grouping); AnnotationMenuArrange(const AnnotationMenuArrange&); AnnotationMenuArrange& operator=(const AnnotationMenuArrange&); QPixmap createAlignmentPixmap(const QWidget* widget, const AnnotationAlignmentEnum::Enum alignment); QPixmap createDistributePixmap(const QWidget* widget, const AnnotationDistributeEnum::Enum distribute); QPixmap createGroupingPixmap(const QWidget* widget, const AnnotationGroupingModeEnum::Enum grouping); void drawLine(QSharedPointer& painter, const QLineF& line, const qreal x, const qreal y); void drawRect(QSharedPointer& painter, const QColor& color, const QRectF& rectangle, const qreal x, const qreal y); const int32_t m_browserWindowIndex; QAction* m_groupAction; QAction* m_regroupAction; QAction* m_ungroupAction; // ADD_NEW_MEMBERS_HERE }; #ifdef __ANNOTATION_MENU_ARRANGE_DECLARE__ // #endif // __ANNOTATION_MENU_ARRANGE_DECLARE__ } // namespace #endif //__ANNOTATION_MENU_ARRANGE_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/GuiQt/AnnotationMenuFileSelection.cxx000066400000000000000000000144431300200146000277340ustar00rootroot00000000000000 /*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 __ANNOTATION_MENU_FILE_SELECTION_DECLARE__ #include "AnnotationMenuFileSelection.h" #undef __ANNOTATION_MENU_FILE_SELECTION_DECLARE__ #include "AnnotationFile.h" #include "Brain.h" #include "CaretAssert.h" #include "CaretFileDialog.h" #include "EventDataFileAdd.h" #include "EventManager.h" #include "GuiManager.h" using namespace caret; /** * \class caret::AnnotationMenuFileSelection * \brief Menu for selecting an annotation file. * \ingroup GuiQt */ /** * Constructor. * * @param parent * The parent widget. */ AnnotationMenuFileSelection::AnnotationMenuFileSelection(QWidget* parent) : QMenu(parent) { m_selectedAnnotationFile = NULL; QObject::connect(this, SIGNAL(aboutToShow()), this, SLOT(updateMenuContents())); QObject::connect(this, SIGNAL(triggered(QAction*)), this, SLOT(menuActionSelected(QAction*))); } /** * Destructor. */ AnnotationMenuFileSelection::~AnnotationMenuFileSelection() { } /** * @return The selected annotation file or NULL if no * annotation file is selected. */ AnnotationFile* AnnotationMenuFileSelection::getSelectedAnnotationFile() { /* * Ensures selected file is valid */ updateMenuContents(); return m_selectedAnnotationFile; } /** * @return The selected annotation file or NULL if no * annotation file is selected. */ AString AnnotationMenuFileSelection::getSelectedNameForToolButton() { AString name("Scene"); AnnotationFile* annFile = getSelectedAnnotationFile(); if (annFile != NULL) { if (annFile == GuiManager::get()->getBrain()->getSceneAnnotationFile()) { name = "Scene"; } else { name = "Disk "; } } return name; } /** * Called to create a new disk file. */ void AnnotationMenuFileSelection::chooseDiskFile() { const AString fileDialogSettingsName("AnnotDiskFileDialog"); /* * Setup file selection dialog. */ CaretFileDialog fd(this); fd.setAcceptMode(CaretFileDialog::AcceptSave); fd.setNameFilter(DataFileTypeEnum::toQFileDialogFilter(DataFileTypeEnum::ANNOTATION)); fd.setFileMode(CaretFileDialog::AnyFile); fd.setViewMode(CaretFileDialog::List); fd.setLabelText(CaretFileDialog::Accept, "Choose"); // OK button shows Insert fd.restoreDialogSettings(fileDialogSettingsName); fd.selectFile(AnnotationFile().getFileNameNoPath()); AString errorMessages; if (fd.exec() == CaretFileDialog::Accepted) { fd.saveDialogSettings(fileDialogSettingsName); QStringList selectedFiles = fd.selectedFiles(); if ( ! selectedFiles.empty()) { const AString annotationFileName = selectedFiles.at(0); AnnotationFile* newFile = new AnnotationFile(); newFile->setFileName(annotationFileName); EventManager::get()->sendEvent(EventDataFileAdd(newFile).getPointer()); m_selectedAnnotationFile = newFile; } } } /** * Called when a menu action is selected. * * @param action * The action that was selected. */ void AnnotationMenuFileSelection::menuActionSelected(QAction* action) { CaretAssert(action); const int actionID = action->data().toInt(); if (actionID == ACTION_ID_SCENE) { m_selectedAnnotationFile = GuiManager::get()->getBrain()->getSceneAnnotationFile(); } else if (actionID == ACTION_ID_NEW_DISK_FILE) { chooseDiskFile(); } else if (actionID >= ACTION_ID_FIRST_DISK_FILE) { if ((actionID >= 0) && (actionID < static_cast(m_annotationDiskFiles.size()))) { CaretAssertVectorIndex(m_annotationDiskFiles, actionID); m_selectedAnnotationFile = m_annotationDiskFiles[actionID]; } } emit menuItemSelected(); } /** * Update the contents of the menu. */ void AnnotationMenuFileSelection::updateMenuContents() { blockSignals(true); clear(); bool foundSelectedFileFlag = false; Brain* brain = GuiManager::get()->getBrain(); AnnotationFile* sceneAnnotationFile = brain->getSceneAnnotationFile(); QAction* sceneAction = addAction("Scene Annotations"); sceneAction->setData((int)ACTION_ID_SCENE); sceneAction->setCheckable(true); if (sceneAnnotationFile == m_selectedAnnotationFile) { sceneAction->setChecked(true); foundSelectedFileFlag = true; } addSeparator(); QAction* newDiskFileAction = addAction("New Disk File..."); newDiskFileAction->setData((int)ACTION_ID_NEW_DISK_FILE); addSeparator(); m_annotationDiskFiles.clear(); brain->getAllAnnotationFilesExcludingSceneAnnotationFile(m_annotationDiskFiles); const int32_t numDiskFiles = static_cast(m_annotationDiskFiles.size()); for (int32_t i = 0; i < numDiskFiles; i++) { CaretAssertVectorIndex(m_annotationDiskFiles, i); AnnotationFile* annFile = m_annotationDiskFiles[i]; CaretAssert(annFile); QAction* fileAction = addAction(annFile->getFileNameNoPath()); fileAction->setData((int)ACTION_ID_FIRST_DISK_FILE + i); fileAction->setCheckable(true); if (m_selectedAnnotationFile == annFile) { fileAction->setChecked(true); foundSelectedFileFlag = true; } } if ( ! foundSelectedFileFlag) { sceneAction->setChecked(true); m_selectedAnnotationFile = sceneAnnotationFile; } blockSignals(false); } connectome-workbench-1.2.3+git41-gc4c6c90/src/GuiQt/AnnotationMenuFileSelection.h000066400000000000000000000044251300200146000273600ustar00rootroot00000000000000#ifndef __ANNOTATION_MENU_FILE_SELECTION_H__ #define __ANNOTATION_MENU_FILE_SELECTION_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 "AString.h" namespace caret { class AnnotationFile; class AnnotationMenuFileSelection : public QMenu { Q_OBJECT public: AnnotationMenuFileSelection(QWidget* parent = 0); virtual ~AnnotationMenuFileSelection(); AnnotationFile* getSelectedAnnotationFile(); AString getSelectedNameForToolButton(); // ADD_NEW_METHODS_HERE signals: void menuItemSelected(); private slots: void updateMenuContents(); void menuActionSelected(QAction* action); private: enum { ACTION_ID_SCENE = -2, ACTION_ID_NEW_DISK_FILE = -1, ACTION_ID_FIRST_DISK_FILE = 0 }; AnnotationMenuFileSelection(const AnnotationMenuFileSelection&); AnnotationMenuFileSelection& operator=(const AnnotationMenuFileSelection&); void chooseDiskFile(); std::vector m_annotationDiskFiles; AnnotationFile* m_selectedAnnotationFile; // ADD_NEW_MEMBERS_HERE }; #ifdef __ANNOTATION_MENU_FILE_SELECTION_DECLARE__ // #endif // __ANNOTATION_MENU_FILE_SELECTION_DECLARE__ } // namespace #endif //__ANNOTATION_MENU_FILE_SELECTION_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/GuiQt/AnnotationPasteDialog.cxx000066400000000000000000000516311300200146000265560ustar00rootroot00000000000000 /*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 __ANNOTATION_PASTE_DIALOG_DECLARE__ #include "AnnotationPasteDialog.h" #undef __ANNOTATION_PASTE_DIALOG_DECLARE__ #include #include #include #include #include "Annotation.h" #include "AnnotationCoordinateSelectionWidget.h" #include "AnnotationFile.h" #include "AnnotationManager.h" #include "AnnotationOneDimensionalShape.h" #include "AnnotationPercentSizeText.h" #include "AnnotationRedoUndoCommand.h" #include "Brain.h" #include "BrainBrowserWindow.h" #include "BrainOpenGLViewportContent.h" #include "BrainOpenGLWidget.h" #include "BrowserTabContent.h" #include "CaretAssert.h" #include "EventGraphicsUpdateAllWindows.h" #include "EventUserInterfaceUpdate.h" #include "EventManager.h" #include "GuiManager.h" #include "ModelSurfaceMontage.h" #include "MouseEvent.h" #include "WuQMessageBox.h" #include "WuQtUtilities.h" using namespace caret; /** * \class caret::AnnotationPasteDialog * \brief Dialog for pasting annotations. * \ingroup GuiQt */ /** * Paste the annotation on the clipboard using the mouse information. * * @param mouseEvent * Information about where to paste the annotation. * @param windowIndex * Window in which annotation is pasted. * @return * Pointer to annotation that was pasted or NULL if the annotation * on the clipboard was not pasted. */ Annotation* AnnotationPasteDialog::pasteAnnotationOnClipboard(const MouseEvent& mouseEvent, const int32_t windowIndex) { Annotation* newPastedAnnotation = NULL; AnnotationManager* annotationManager = GuiManager::get()->getBrain()->getAnnotationManager(); if (annotationManager->isAnnotationOnClipboardValid()) { AnnotationFile* annotationFile = annotationManager->getAnnotationFileOnClipboard(); Annotation* annotation = annotationManager->getAnnotationOnClipboard()->clone(); BrainOpenGLViewportContent* viewportContent = mouseEvent.getViewportContent(); AnnotationCoordinateInformation coordInfo; AnnotationCoordinateInformation::createCoordinateInformationFromXY(mouseEvent.getOpenGLWidget(), viewportContent, mouseEvent.getX(), mouseEvent.getY(), coordInfo); bool validCoordsFlag = false; AnnotationOneDimensionalShape* oneDimShape = dynamic_cast(annotation); if (oneDimShape != NULL) { /* * Pasting line while preserving its orientation only * works for tab and window spaces. */ validCoordsFlag = pasteOneDimensionalShape(oneDimShape, coordInfo); } if (! validCoordsFlag) { validCoordsFlag = AnnotationCoordinateInformation::setAnnotationCoordinatesForSpace(annotation, annotation->getCoordinateSpace(), &coordInfo, NULL); } if (validCoordsFlag) { AnnotationRedoUndoCommand* undoCommand = new AnnotationRedoUndoCommand(); undoCommand->setModePasteAnnotation(annotationFile, annotation); AString errorMessage; if ( ! annotationManager->applyCommand(undoCommand, errorMessage)) { WuQMessageBox::errorOk(mouseEvent.getOpenGLWidget(), errorMessage); } newPastedAnnotation = annotation; annotationManager->selectAnnotationForEditing(windowIndex, AnnotationManager::SELECTION_MODE_SINGLE, false, annotation); } else { /* * Pasting annotation in its coordinate failed (user may have tried to paste * an annotation in surface space where there is no surface). */ delete annotation; annotation = NULL; const QString message("The location for pasting the annotation is incompatible with the " "coordinate space " "used by the annotation on the clipboard. Choose one of the coordinate " "spaces below to paste the annotation or press Cancel to cancel pasting " "of the annotation."); /* * The annotation dialog will create a new annotation for pasting */ AnnotationPasteDialog pasteDialog(mouseEvent, annotationFile, annotationManager->getAnnotationOnClipboard(), message, mouseEvent.getOpenGLWidget()); if (pasteDialog.exec() == AnnotationPasteDialog::Accepted) { newPastedAnnotation = pasteDialog.getAnnotationThatWasCreated(); } } } return newPastedAnnotation; } /** * Paste the annotation on the clipboard using the mouse information * and allow the user to change the coordinate space. * * @param mouseEvent * Information about where to paste the annotation. * @return * Pointer to annotation that was pasted or NULL if the annotation * on the clipboard was not pasted. */ Annotation* AnnotationPasteDialog::pasteAnnotationOnClipboardChangeSpace(const MouseEvent& mouseEvent) { Annotation* newPastedAnnotation = NULL; AnnotationManager* annotationManager = GuiManager::get()->getBrain()->getAnnotationManager(); if (annotationManager->isAnnotationOnClipboardValid()) { AnnotationFile* annotationFile = annotationManager->getAnnotationFileOnClipboard(); //Annotation* annotation = annotationManager->getAnnotationOnClipboard()->clone(); AString message("Choose one of the coordinate " "spaces below to paste the annotation or press Cancel to cancel pasting " "of the annotation."); AnnotationPasteDialog pasteDialog(mouseEvent, annotationFile, annotationManager->getAnnotationOnClipboard(), //annotation, message, mouseEvent.getOpenGLWidget()); if (pasteDialog.exec() == AnnotationPasteDialog::Accepted) { newPastedAnnotation = pasteDialog.getAnnotationThatWasCreated(); } } return newPastedAnnotation; } /** * Constructor. * * @param mouseEvent * Information about where mouse was clicked. * @param annotationFile * File that contains the annotation. * @param annotation * Annotation that is copied and pasted. * @param informationMessage * Message shown on dialog. * @param parent * Parent widget of dialog. */ AnnotationPasteDialog::AnnotationPasteDialog(const MouseEvent& mouseEvent, AnnotationFile* annotationFile, const Annotation* annotation, const AString& informationMessage, QWidget* parent) : WuQDialogModal("Paste Annotation", parent), m_mouseEvent(mouseEvent), m_annotationFile(annotationFile), m_annotation(annotation), m_annotationThatWasCreated(NULL) { CaretAssert(m_annotationFile); CaretAssert(m_annotation); /* * Get coordinates at the mouse location. */ AnnotationCoordinateInformation::createCoordinateInformationFromXY(mouseEvent, m_coordInfo); m_coordinateSelectionWidget = new AnnotationCoordinateSelectionWidget(m_annotation->getType(), m_coordInfo, NULL); m_coordinateSelectionWidget->selectCoordinateSpace(m_annotation->getCoordinateSpace()); QLabel* messageLabel = new QLabel(informationMessage); messageLabel->setWordWrap(true); QLabel* spaceLabel = new QLabel("Space of Annotation on Clipboard: " + AnnotationCoordinateSpaceEnum::toGuiName(m_annotation->getCoordinateSpace())); spaceLabel->setWordWrap(false); QGroupBox* coordGroupBox = new QGroupBox("Coordinate Space"); QVBoxLayout* coordGroupLayout = new QVBoxLayout(coordGroupBox); coordGroupLayout->setMargin(0); coordGroupLayout->addWidget(m_coordinateSelectionWidget); QWidget* dialogWidget = new QWidget(); QVBoxLayout* layout = new QVBoxLayout(dialogWidget); layout->addWidget(spaceLabel); layout->addSpacing(10); layout->addWidget(messageLabel); layout->addSpacing(10); layout->addWidget(coordGroupBox); setCentralWidget(dialogWidget, SCROLL_AREA_NEVER); } /** * Destructor. */ AnnotationPasteDialog::~AnnotationPasteDialog() { } /** * @return Get the annotation that was created. */ Annotation* AnnotationPasteDialog::getAnnotationThatWasCreated() { return m_annotationThatWasCreated; } /** * Paste a one-dimensional shape (line) keeping its start to end * coordinate orientation. The start coordinate is pasted at * the coordinate in 'coordInfo'. * * @param oneDimShape * One dimensional shape that will be pasted. * @param coordInfo * Coordinate information that will be used for the shape's 'start' coordinate. * @return * True if the shape's coordinate was updated for pasting, else false. */ bool AnnotationPasteDialog::pasteOneDimensionalShape(AnnotationOneDimensionalShape* oneDimShape, AnnotationCoordinateInformation& coordInfo) { bool tabFlag = false; bool windowFlag = false; switch (oneDimShape->getCoordinateSpace()) { case AnnotationCoordinateSpaceEnum::PIXELS: break; case AnnotationCoordinateSpaceEnum::STEREOTAXIC: break; case AnnotationCoordinateSpaceEnum::SURFACE: break; case AnnotationCoordinateSpaceEnum::TAB: tabFlag = true; break; case AnnotationCoordinateSpaceEnum::WINDOW: windowFlag = true; break; } bool validCoordsFlag = false; if (tabFlag || windowFlag) { float startXYZ[3]; float endXYZ[3]; oneDimShape->getStartCoordinate()->getXYZ(startXYZ); oneDimShape->getEndCoordinate()->getXYZ(endXYZ); const float diffXYZ[3] = { endXYZ[0] - startXYZ[0], endXYZ[1] - startXYZ[1], endXYZ[2] - startXYZ[2] }; if (tabFlag && (coordInfo.m_tabIndex >= 0)) { startXYZ[0] = coordInfo.m_tabXYZ[0]; startXYZ[1] = coordInfo.m_tabXYZ[1]; startXYZ[2] = coordInfo.m_tabXYZ[2]; oneDimShape->setTabIndex(coordInfo.m_tabIndex); validCoordsFlag = true; } else if (windowFlag && (coordInfo.m_windowIndex >= 0)) { startXYZ[0] = coordInfo.m_windowXYZ[0]; startXYZ[1] = coordInfo.m_windowXYZ[1]; startXYZ[2] = coordInfo.m_windowXYZ[2]; oneDimShape->setWindowIndex(coordInfo.m_windowIndex); validCoordsFlag = true; } if (validCoordsFlag) { endXYZ[0] = startXYZ[0] + diffXYZ[0]; endXYZ[1] = startXYZ[1] + diffXYZ[1]; endXYZ[2] = startXYZ[2] + diffXYZ[2]; /* * Tab/Window coordinates are percentage ranging [0.0, 100.0] * Need to "clip" lines if they exceed the viewport's edges */ const float minCoord = 1.0; const float maxCoord = 99.0; if (endXYZ[0] < minCoord) { if (diffXYZ[0] != 0.0) { const float xDist = minCoord - startXYZ[0]; const float scaledDistance = std::fabs(xDist / diffXYZ[0]); endXYZ[0] = minCoord; endXYZ[1] = startXYZ[1] + (scaledDistance * diffXYZ[1]); } } else if (endXYZ[0] >= maxCoord) { if (diffXYZ[0] != 0.0) { const float xDist = maxCoord - startXYZ[0]; const float scaledDistance = std::fabs(xDist / diffXYZ[0]); endXYZ[0] = maxCoord; endXYZ[1] = startXYZ[1] + (scaledDistance * diffXYZ[1]); } } if (endXYZ[1] < minCoord) { if (diffXYZ[1] != 0.0) { const float yDist = minCoord - startXYZ[1]; const float scaledDistance = std::fabs(yDist / diffXYZ[1]); endXYZ[1] = minCoord; endXYZ[0] = startXYZ[0] + (scaledDistance * diffXYZ[0]); } } else if (endXYZ[1] > maxCoord) { if (diffXYZ[1] != 0.0) { const float yDist = maxCoord - startXYZ[1]; const float scaledDistance = std::fabs(yDist / diffXYZ[1]); endXYZ[1] = maxCoord; endXYZ[0] = startXYZ[0] + (scaledDistance * diffXYZ[0]); } } oneDimShape->getStartCoordinate()->setXYZ(startXYZ); oneDimShape->getEndCoordinate()->setXYZ(endXYZ); } } return validCoordsFlag; } /** * Gets called when the OK button is clicked. */ void AnnotationPasteDialog::okButtonClicked() { AString errorMessage; bool valid = false; m_coordinateSelectionWidget->getSelectedCoordinateSpace(valid); if ( ! valid) { const QString msg("A coordinate space has not been selected."); WuQMessageBox::errorOk(this, msg); return; } CaretPointer newAnnotation(m_annotation->clone()); const AnnotationCoordinateSpaceEnum::Enum previousSpace = m_annotation->getCoordinateSpace(); if ( ! m_coordinateSelectionWidget->setCoordinateForNewAnnotation(newAnnotation, errorMessage)) { WuQMessageBox::errorOk(this, errorMessage); return; } /* * Need to release annotation from its CaretPointer since the * annotation file will take ownership of the annotation. */ Annotation* annotationPointer = newAnnotation.releasePointer(); adjustTextAnnotationFontHeight(previousSpace, annotationPointer); AnnotationManager* annotationManager = GuiManager::get()->getBrain()->getAnnotationManager(); AnnotationRedoUndoCommand* undoCommand = new AnnotationRedoUndoCommand(); undoCommand->setModePasteAnnotation(m_annotationFile, annotationPointer); if ( ! annotationManager->applyCommand(undoCommand, errorMessage)) { WuQMessageBox::errorOk(this, errorMessage); } annotationManager->selectAnnotationForEditing(m_mouseEvent.getBrowserWindowIndex(), AnnotationManager::SELECTION_MODE_SINGLE, false, annotationPointer); EventManager::get()->sendEvent(EventGraphicsUpdateAllWindows().getPointer()); EventManager::get()->sendEvent(EventUserInterfaceUpdate().getPointer()); m_annotationThatWasCreated = annotationPointer; WuQDialog::okButtonClicked(); } /** * If the given annotation is a text annotation and the space is changed to * surface, we may need to adjust the height if in surface montage. This * results in the surface space pixel height being the same as the tab * space text pixel height when in surface montage. * * Inverse logic is need when converting text annotation from surface * space to another space. * * @param previousSpace * Space of annotation that was on the clipboard. * @param annotation * Annotation that is being pasted. */ void AnnotationPasteDialog::adjustTextAnnotationFontHeight(const AnnotationCoordinateSpaceEnum::Enum previousSpace, Annotation* annotation) { CaretAssert(annotation); BrainOpenGLViewportContent* vpContent = m_mouseEvent.getViewportContent(); CaretAssert(vpContent); BrowserTabContent* btc = vpContent->getBrowserTabContent(); CaretAssert(btc); int32_t surfaceMontageRowCount = 1; const ModelSurfaceMontage* msm = btc->getDisplayedSurfaceMontageModel(); if (msm != NULL) { int32_t columnCount = 1; msm->getSurfaceMontageNumberOfRowsAndColumns(btc->getTabNumber(), surfaceMontageRowCount, columnCount); } const AnnotationCoordinateSpaceEnum::Enum newSpace = annotation->getCoordinateSpace(); const bool previousSpaceStereoOrSurfaceFlag = ((previousSpace == AnnotationCoordinateSpaceEnum::STEREOTAXIC) || (previousSpace == AnnotationCoordinateSpaceEnum::SURFACE)); const bool newSpaceStereoOrSurfaceFlag = ((newSpace == AnnotationCoordinateSpaceEnum::STEREOTAXIC) || (newSpace == AnnotationCoordinateSpaceEnum::SURFACE)); if (surfaceMontageRowCount > 1) { if (annotation->getType() == AnnotationTypeEnum::TEXT) { float heightMultiplier = 0.0; if ( ! previousSpaceStereoOrSurfaceFlag) { if (newSpaceStereoOrSurfaceFlag) { /* * Converting to surface */ heightMultiplier = surfaceMontageRowCount; } } else { if ( ! newSpaceStereoOrSurfaceFlag) { /* * Converting from surface */ heightMultiplier = 1.0 / surfaceMontageRowCount; } } // if (previousSpace != AnnotationCoordinateSpaceEnum::SURFACE) { // if (annotation->getCoordinateSpace() == AnnotationCoordinateSpaceEnum::SURFACE) { // /* // * Converting to surface // */ // heightMultiplier = surfaceMontageRowCount; // } // } // else { // if (annotation->getCoordinateSpace() != AnnotationCoordinateSpaceEnum::SURFACE) { // /* // * Converting from surface // */ // heightMultiplier = 1.0 / surfaceMontageRowCount; // } // } if (heightMultiplier != 0.0) { AnnotationPercentSizeText* textAnn = dynamic_cast(annotation); if (textAnn != NULL) { float percentHeight = textAnn->getFontPercentViewportSize(); percentHeight *= heightMultiplier; textAnn->setFontPercentViewportSize(percentHeight); } } } } } connectome-workbench-1.2.3+git41-gc4c6c90/src/GuiQt/AnnotationPasteDialog.h000066400000000000000000000062001300200146000261730ustar00rootroot00000000000000#ifndef __ANNOTATION_PASTE_DIALOG_H__ #define __ANNOTATION_PASTE_DIALOG_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 "AnnotationCoordinateInformation.h" #include "WuQDialogModal.h" namespace caret { class Annotation; class AnnotationCoordinateInformation; class AnnotationCoordinateSelectionWidget; class AnnotationFile; class AnnotationOneDimensionalShape; class MouseEvent; class AnnotationPasteDialog : public WuQDialogModal { Q_OBJECT public: static Annotation* pasteAnnotationOnClipboard(const MouseEvent& mouseEvent, const int32_t windowIndex); static Annotation* pasteAnnotationOnClipboardChangeSpace(const MouseEvent& mouseEvent); virtual ~AnnotationPasteDialog(); Annotation* getAnnotationThatWasCreated(); // ADD_NEW_METHODS_HERE private: AnnotationPasteDialog(const MouseEvent& mouseEvent, AnnotationFile* annotationFile, const Annotation* annotation, const AString& informationMessage, QWidget* parent = 0); AnnotationPasteDialog(const AnnotationPasteDialog&); AnnotationPasteDialog& operator=(const AnnotationPasteDialog&); static bool pasteOneDimensionalShape(AnnotationOneDimensionalShape* oneDimShape, AnnotationCoordinateInformation& coordInfo); virtual void okButtonClicked(); void adjustTextAnnotationFontHeight(const AnnotationCoordinateSpaceEnum::Enum previousSpace, Annotation* annotation); const MouseEvent& m_mouseEvent; AnnotationFile* m_annotationFile; const Annotation* m_annotation; AnnotationCoordinateSelectionWidget* m_coordinateSelectionWidget; AnnotationCoordinateInformation m_coordInfo; Annotation* m_annotationThatWasCreated; // ADD_NEW_MEMBERS_HERE }; #ifdef __ANNOTATION_PASTE_DIALOG_DECLARE__ // #endif // __ANNOTATION_PASTE_DIALOG_DECLARE__ } // namespace #endif //__ANNOTATION_PASTE_DIALOG_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/GuiQt/AnnotationRedoUndoWidget.cxx000066400000000000000000000115671300200146000272510ustar00rootroot00000000000000 /*LICENSE_START*/ /* * Copyright (C) 2015 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 __ANNOTATION_REDO_UNDO_WIDGET_DECLARE__ #include "AnnotationRedoUndoWidget.h" #undef __ANNOTATION_REDO_UNDO_WIDGET_DECLARE__ #include #include #include #include #include "AnnotationManager.h" #include "Brain.h" #include "CaretAssert.h" #include "CaretUndoStack.h" #include "EventGraphicsUpdateAllWindows.h" #include "EventManager.h" #include "GuiManager.h" #include "WuQMessageBox.h" #include "WuQtUtilities.h" using namespace caret; /** * \class caret::AnnotationRedoUndoWidget * \brief Widget for redo and undo buttons * \ingroup GuiQt */ /** * Constructor. */ AnnotationRedoUndoWidget::AnnotationRedoUndoWidget(const int32_t browserWindowIndex, QWidget* parent) : QWidget(parent), m_browserWindowIndex(browserWindowIndex) { QLabel* titleLabel = new QLabel("Edit"); m_redoAction = WuQtUtilities::createAction("Redo", "Redo ToolTip", this, this, SLOT(redoActionTriggered())); QToolButton* redoToolButton = new QToolButton(); redoToolButton->setDefaultAction(m_redoAction); m_undoAction = WuQtUtilities::createAction("Undo", "Undo ToolTip", this, this, SLOT(undoActionTriggered())); QToolButton* undoToolButton = new QToolButton(); undoToolButton->setDefaultAction(m_undoAction); QGridLayout* gridLayout = new QGridLayout(this); WuQtUtilities::setLayoutSpacingAndMargins(gridLayout, 2, 0); gridLayout->addWidget(titleLabel, 0, 0, 1, 1, Qt::AlignHCenter); gridLayout->addWidget(redoToolButton, 1, 0, Qt::AlignHCenter); gridLayout->addWidget(undoToolButton, 2, 0, Qt::AlignHCenter); setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); } /** * Destructor. */ AnnotationRedoUndoWidget::~AnnotationRedoUndoWidget() { } /** * Update with the given line annotation. * * @param annotationLine */ void AnnotationRedoUndoWidget::updateContent() { AnnotationManager* annMan = GuiManager::get()->getBrain()->getAnnotationManager(); CaretUndoStack* undoStack = annMan->getCommandRedoUndoStack(); m_redoAction->setEnabled(undoStack->canRedo()); m_redoAction->setToolTip(undoStack->redoText()); m_undoAction->setEnabled(undoStack->canUndo()); m_undoAction->setToolTip(undoStack->undoText()); } /** * Gets called when the redo action is triggered */ void AnnotationRedoUndoWidget::redoActionTriggered() { AnnotationManager* annMan = GuiManager::get()->getBrain()->getAnnotationManager(); CaretUndoStack* undoStack = annMan->getCommandRedoUndoStack(); AString errorMessage; if ( ! undoStack->redoInWindow(m_browserWindowIndex, errorMessage)) { WuQMessageBox::errorOk(this, errorMessage); } EventManager::get()->sendSimpleEvent(EventTypeEnum::EVENT_ANNOTATION_TOOLBAR_UPDATE); EventManager::get()->sendEvent(EventGraphicsUpdateAllWindows().getPointer()); } /** * Gets called when the undo action is triggered */ void AnnotationRedoUndoWidget::undoActionTriggered() { AnnotationManager* annMan = GuiManager::get()->getBrain()->getAnnotationManager(); CaretUndoStack* undoStack = annMan->getCommandRedoUndoStack(); AString errorMessage; if ( ! undoStack->undoInWindow(m_browserWindowIndex, errorMessage)) { WuQMessageBox::errorOk(this, errorMessage); } EventManager::get()->sendSimpleEvent(EventTypeEnum::EVENT_ANNOTATION_TOOLBAR_UPDATE); EventManager::get()->sendEvent(EventGraphicsUpdateAllWindows().getPointer()); } connectome-workbench-1.2.3+git41-gc4c6c90/src/GuiQt/AnnotationRedoUndoWidget.h000066400000000000000000000037171300200146000266740ustar00rootroot00000000000000#ifndef __ANNOTATION_REDO_UNDO_WIDGET_H__ #define __ANNOTATION_REDO_UNDO_WIDGET_H__ /*LICENSE_START*/ /* * Copyright (C) 2015 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 AnnotationRedoUndoWidget : public QWidget { Q_OBJECT public: AnnotationRedoUndoWidget(const int32_t browserWindowIndex, QWidget* parent = 0); virtual ~AnnotationRedoUndoWidget(); void updateContent(); private slots: void redoActionTriggered(); void undoActionTriggered(); // ADD_NEW_METHODS_HERE private: AnnotationRedoUndoWidget(const AnnotationRedoUndoWidget&); AnnotationRedoUndoWidget& operator=(const AnnotationRedoUndoWidget&); const int32_t m_browserWindowIndex; QAction* m_redoAction; QAction* m_undoAction; // ADD_NEW_MEMBERS_HERE }; #ifdef __ANNOTATION_REDO_UNDO_WIDGET_DECLARE__ // #endif // __ANNOTATION_REDO_UNDO_WIDGET_DECLARE__ } // namespace #endif //__ANNOTATION_REDO_UNDO_WIDGET_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/GuiQt/AnnotationRotationWidget.cxx000066400000000000000000000254421300200146000273260ustar00rootroot00000000000000 /*LICENSE_START*/ /* * Copyright (C) 2015 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 #define __ANNOTATION_ROTATION_WIDGET_DECLARE__ #include "AnnotationRotationWidget.h" #undef __ANNOTATION_ROTATION_WIDGET_DECLARE__ #include #include #include #include "AnnotationManager.h" #include "AnnotationOneDimensionalShape.h" #include "AnnotationRedoUndoCommand.h" #include "AnnotationTwoDimensionalShape.h" #include "Brain.h" #include "CaretAssert.h" #include "EventGetViewportSize.h" #include "EventGraphicsUpdateAllWindows.h" #include "EventManager.h" #include "GuiManager.h" #include "MathFunctions.h" #include "WuQFactory.h" #include "WuQMessageBox.h" #include "WuQtUtilities.h" using namespace caret; /** * \class caret::AnnotationRotationWidget * \brief Widget for adjusting annotation's rotation angle. * \ingroup GuiQt */ /** * Constructor. * * @param browserWindowIndex * Index of browser window. * @param parent * Parent of this widget. */ AnnotationRotationWidget::AnnotationRotationWidget(const int32_t browserWindowIndex, QWidget* parent) : QWidget(parent), m_browserWindowIndex(browserWindowIndex) { QLabel* rotationLabel = new QLabel(" R:"); m_rotationSpinBox = WuQFactory::newDoubleSpinBoxWithMinMaxStepDecimalsSignalDouble(0.0, 359, 1.0, 0, this, SLOT(rotationValueChanged(double))); m_rotationSpinBox->setWrapping(true); WuQtUtilities::setWordWrappedToolTip(m_rotationSpinBox, "Rotation, clockwise in degrees"); QHBoxLayout* layout = new QHBoxLayout(this); WuQtUtilities::setLayoutSpacingAndMargins(layout, 2, 2); layout->addWidget(rotationLabel); layout->addWidget(m_rotationSpinBox); setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); } /** * Destructor. */ AnnotationRotationWidget::~AnnotationRotationWidget() { } /** * Convert the given annotation to a one-dimensional annotation. * * @param annotation * The annotation. * @return * Non-null if it is a one-dimensional annotation in a compatible * stereotaxic space for rotation angle. * */ AnnotationOneDimensionalShape* AnnotationRotationWidget::getValidOneDimAnnotation(Annotation* annotation) { AnnotationOneDimensionalShape* oneDimAnn = dynamic_cast(annotation); if (oneDimAnn != NULL) { bool validSpaceFlag = false; switch (oneDimAnn->getCoordinateSpace()) { case AnnotationCoordinateSpaceEnum::PIXELS: break; case AnnotationCoordinateSpaceEnum::STEREOTAXIC: break; case AnnotationCoordinateSpaceEnum::SURFACE: break; case AnnotationCoordinateSpaceEnum::TAB: validSpaceFlag = true; break; case AnnotationCoordinateSpaceEnum::WINDOW: validSpaceFlag = true; break; } if ( ! validSpaceFlag) { oneDimAnn = NULL; } } return oneDimAnn; } /** * Update with the given annotation. * * @param annotations. * The annotation. */ void AnnotationRotationWidget::updateContent(std::vector& annotations) { m_annotations.clear(); if ( ! annotations.empty()) { float rotationAngle = 0.0; bool rotationAngleValid = false; bool haveMultipleRotationAnglesFlag = false; const int32_t numAnns = static_cast(annotations.size()); 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 (viewportValidFlag) { angle = oneDimAnn->getRotationAngle(viewport[2], viewport[3]); angleValid = true; } } if (angleValid) { if (angle < 0.0) { angle += 360.0; } 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); } } } if (rotationAngleValid) { m_rotationSpinBox->blockSignals(true); m_rotationSpinBox->setValue(rotationAngle); if (haveMultipleRotationAnglesFlag) { m_rotationSpinBox->setSuffix("+"); } else { m_rotationSpinBox->setSuffix(""); } m_rotationSpinBox->blockSignals(false); } setEnabled(rotationAngleValid); } else { setEnabled(false); } } /** * Gets called when rotation value is changed. * * @param value * */ 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, m_annotations); AnnotationManager* annMan = GuiManager::get()->getBrain()->getAnnotationManager(); AString errorMessage; if ( ! annMan->applyCommand(undoCommand, errorMessage)) { WuQMessageBox::errorOk(this, errorMessage); } 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()); } connectome-workbench-1.2.3+git41-gc4c6c90/src/GuiQt/AnnotationRotationWidget.h000066400000000000000000000043761300200146000267560ustar00rootroot00000000000000#ifndef __ANNOTATION_ROTATION_WIDGET_H__ #define __ANNOTATION_ROTATION_WIDGET_H__ /*LICENSE_START*/ /* * Copyright (C) 2015 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 class QDoubleSpinBox; namespace caret { class Annotation; class AnnotationOneDimensionalShape; class AnnotationRotationWidget : public QWidget { Q_OBJECT public: AnnotationRotationWidget(const int32_t browserWindowIndex, QWidget* parent = 0); virtual ~AnnotationRotationWidget(); // ADD_NEW_METHODS_HERE // void updateContent(std::vector& annotation2D); void updateContent(std::vector& annotations); private slots: void rotationValueChanged(double value); private: AnnotationRotationWidget(const AnnotationRotationWidget&); AnnotationRotationWidget& operator=(const AnnotationRotationWidget&); AnnotationOneDimensionalShape* getValidOneDimAnnotation(Annotation* annotation); std::vector m_annotations; const int32_t m_browserWindowIndex; QDoubleSpinBox* m_rotationSpinBox; // ADD_NEW_MEMBERS_HERE }; #ifdef __ANNOTATION_ROTATION_WIDGET_DECLARE__ // #endif // __ANNOTATION_ROTATION_WIDGET_DECLARE__ } // namespace #endif //__ANNOTATION_ROTATION_WIDGET_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/GuiQt/AnnotationSelectionViewController.cxx000066400000000000000000000275241300200146000312120ustar00rootroot00000000000000 /*LICENSE_START*/ /* * Copyright (C) 2015 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 __ANNOTATION_SELECTION_VIEW_CONTROLLER_DECLARE__ #include "AnnotationSelectionViewController.h" #undef __ANNOTATION_SELECTION_VIEW_CONTROLLER_DECLARE__ #include #include #include #include "AnnotationFile.h" #include "Brain.h" #include "BrowserTabContent.h" #include "CaretAssert.h" #include "DisplayGroupAndTabItemViewController.h" #include "DisplayGroupEnumComboBox.h" #include "DisplayPropertiesAnnotation.h" #include "EventGetOrSetUserInputModeProcessor.h" #include "EventGraphicsUpdateAllWindows.h" #include "EventUserInterfaceUpdate.h" #include "EventManager.h" #include "GuiManager.h" #include "SceneClass.h" #include "SceneClassAssistant.h" #include "WuQtUtilities.h" using namespace caret; /** * \class caret::AnnotationSelectionViewController * \brief View controller for display of annotations. * \ingroup GuiQt */ /** * Constructor. * * @param browserWindowIndex * Index of the browser window. * @param parent * The parent widget. */ AnnotationSelectionViewController::AnnotationSelectionViewController(const int32_t browserWindowIndex, QWidget* parent) : QWidget(parent), m_browserWindowIndex(browserWindowIndex) { QLabel* groupLabel = new QLabel("Group"); m_displayGroupComboBox = new DisplayGroupEnumComboBox(this); QObject::connect(m_displayGroupComboBox, SIGNAL(displayGroupSelected(const DisplayGroupEnum::Enum)), this, SLOT(displayGroupSelected(const DisplayGroupEnum::Enum))); QHBoxLayout* groupSelectionLayout = new QHBoxLayout(); groupSelectionLayout->addWidget(groupLabel); groupSelectionLayout->addWidget(m_displayGroupComboBox->getWidget()); groupSelectionLayout->addStretch(); QMargins groupLayoutMargins = groupSelectionLayout->contentsMargins(); groupLayoutMargins.setBottom(0); groupLayoutMargins.setTop(0); m_displayAnnotationsCheckBox = new QCheckBox("Display Annotations"); m_displayAnnotationsCheckBox->setToolTip("Disables/enables display of annotations in all windows"); QObject::connect(m_displayAnnotationsCheckBox, SIGNAL(clicked(bool)), this, SLOT(checkBoxToggled())); m_displayTextAnnotationsCheckBox = new QCheckBox("Display Text Annotations"); m_displayTextAnnotationsCheckBox->setToolTip("Disables/enables display of text annotations in all windows"); QObject::connect(m_displayTextAnnotationsCheckBox, SIGNAL(clicked(bool)), this, SLOT(checkBoxToggled())); m_displayWindowAnnotationInSingleTabViewsCheckBox = new QCheckBox("Show Window " + QString::number(m_browserWindowIndex + 1) + " Annotations in Single Tab View"); const QString singTT(WuQtUtilities::createWordWrappedToolTipText("When checked, window annotations are always displayed." "When unchecked, window annotations are only displayed when tile tabs is enabled.")); m_displayWindowAnnotationInSingleTabViewsCheckBox->setToolTip(singTT); QObject::connect(m_displayWindowAnnotationInSingleTabViewsCheckBox, SIGNAL(clicked(bool)), this, SLOT(checkBoxToggled())); m_sceneAssistant = new SceneClassAssistant(); QVBoxLayout* layout = new QVBoxLayout(this); layout->addWidget(m_displayAnnotationsCheckBox); layout->addWidget(m_displayTextAnnotationsCheckBox); layout->addWidget(m_displayWindowAnnotationInSingleTabViewsCheckBox); layout->addWidget(WuQtUtilities::createHorizontalLineWidget()); layout->addLayout(groupSelectionLayout); layout->addWidget(createSelectionWidget(), 100); layout->addStretch(); EventManager::get()->addEventListener(this, EventTypeEnum::EVENT_ANNOTATION_TOOLBAR_UPDATE); EventManager::get()->addEventListener(this, EventTypeEnum::EVENT_USER_INTERFACE_UPDATE); s_allAnnotationSelectionViewControllers.insert(this); } /** * Destructor. */ AnnotationSelectionViewController::~AnnotationSelectionViewController() { EventManager::get()->removeAllEventsFromListener(this); delete m_sceneAssistant; s_allAnnotationSelectionViewControllers.erase(this); } /** * Receive an event. * * @param event * An event for which this instance is listening. */ void AnnotationSelectionViewController::receiveEvent(Event* event) { bool doUpdateFlag = false; if (event->getEventType() == EventTypeEnum::EVENT_ANNOTATION_TOOLBAR_UPDATE) { doUpdateFlag = true; } else if (event->getEventType() == EventTypeEnum::EVENT_USER_INTERFACE_UPDATE) { EventUserInterfaceUpdate* eventUI = dynamic_cast(event); CaretAssert(eventUI); doUpdateFlag = true; eventUI->setEventProcessed(); } if (doUpdateFlag) { updateAnnotationSelections(); } } /** * Update other selection annotation selector since they may share properties */ void AnnotationSelectionViewController::updateOtherAnnotationViewControllers() { for (std::set::iterator iter = s_allAnnotationSelectionViewControllers.begin(); iter != s_allAnnotationSelectionViewControllers.end(); iter++) { AnnotationSelectionViewController* avc = *iter; if (avc != this) { avc->updateAnnotationSelections(); } } } /** * Update the annotation selections. */ void AnnotationSelectionViewController::updateAnnotationSelections() { const DisplayPropertiesAnnotation* dpa = GuiManager::get()->getBrain()->getDisplayPropertiesAnnotation(); BrowserTabContent* browserTabContent = GuiManager::get()->getBrowserTabContentForBrowserWindow(m_browserWindowIndex, true); if (browserTabContent == NULL) { return; } const int32_t browserTabIndex = browserTabContent->getTabNumber(); m_displayAnnotationsCheckBox->setChecked(dpa->isDisplayAnnotations()); m_displayTextAnnotationsCheckBox->setChecked(dpa->isDisplayTextAnnotations()); m_displayWindowAnnotationInSingleTabViewsCheckBox->setChecked(dpa->isDisplayWindowAnnotationsInSingleTabViews(m_browserWindowIndex)); Brain* brain = GuiManager::get()->getBrain(); std::vector annotationFiles; brain->getAllAnnotationFilesIncludingSceneAnnotationFile(annotationFiles); std::vector fileItems; for (std::vector::iterator fileIter = annotationFiles.begin(); fileIter != annotationFiles.end(); fileIter++) { AnnotationFile* annFile = *fileIter; fileItems.push_back(annFile); } const DisplayGroupEnum::Enum displayGroup = dpa->getDisplayGroupForTab(browserTabIndex); m_displayGroupComboBox->setSelectedDisplayGroup(displayGroup); EventGetOrSetUserInputModeProcessor inputModeEvent(m_browserWindowIndex); EventManager::get()->sendEvent(inputModeEvent.getPointer()); UserInputModeAbstract::UserInputMode mode = inputModeEvent.getUserInputMode(); const bool annotationsValidFlag = (mode == UserInputModeAbstract::ANNOTATIONS); m_selectionViewController->updateContent(fileItems, displayGroup, browserTabIndex, annotationsValidFlag); } QWidget* AnnotationSelectionViewController::createSelectionWidget() { m_selectionViewController = new DisplayGroupAndTabItemViewController(DataFileTypeEnum::ANNOTATION, m_browserWindowIndex); return m_selectionViewController; } /** * Called when one of the checkboxes is clicked. */ void AnnotationSelectionViewController::checkBoxToggled() { DisplayPropertiesAnnotation* dpa = GuiManager::get()->getBrain()->getDisplayPropertiesAnnotation(); BrowserTabContent* browserTabContent = GuiManager::get()->getBrowserTabContentForBrowserWindow(m_browserWindowIndex, true); if (browserTabContent == NULL) { return; } dpa->setDisplayAnnotations(m_displayAnnotationsCheckBox->isChecked()); dpa->setDisplayTextAnnotations(m_displayTextAnnotationsCheckBox->isChecked()); dpa->setDisplayWindowAnnotationsInSingleTabViews(m_browserWindowIndex, m_displayWindowAnnotationInSingleTabViewsCheckBox->isChecked()); updateOtherAnnotationViewControllers(); EventManager::get()->sendEvent(EventGraphicsUpdateAllWindows().getPointer()); } /** * Called when the display group combo box is changed. */ void AnnotationSelectionViewController::displayGroupSelected(const DisplayGroupEnum::Enum displayGroup) { DisplayPropertiesAnnotation* dpa = GuiManager::get()->getBrain()->getDisplayPropertiesAnnotation(); BrowserTabContent* browserTabContent = GuiManager::get()->getBrowserTabContentForBrowserWindow(m_browserWindowIndex, true); if (browserTabContent == NULL) { return; } const int32_t browserTabIndex = browserTabContent->getTabNumber(); dpa->setDisplayGroupForTab(browserTabIndex, displayGroup); updateAnnotationSelections(); } /** * 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* AnnotationSelectionViewController::saveToScene(const SceneAttributes* sceneAttributes, const AString& instanceName) { SceneClass* sceneClass = new SceneClass(instanceName, "AnnotationSelectionViewController", 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 AnnotationSelectionViewController::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); } connectome-workbench-1.2.3+git41-gc4c6c90/src/GuiQt/AnnotationSelectionViewController.h000066400000000000000000000076241300200146000306360ustar00rootroot00000000000000#ifndef __ANNOTATION_SELECTION_VIEW_CONTROLLER_H__ #define __ANNOTATION_SELECTION_VIEW_CONTROLLER_H__ /*LICENSE_START*/ /* * Copyright (C) 2015 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 "DisplayGroupEnum.h" #include "EventListenerInterface.h" #include "SceneableInterface.h" class QCheckBox; namespace caret { class DisplayGroupAndTabItemViewController; class DisplayGroupEnumComboBox; class SceneClassAssistant; class AnnotationSelectionViewController : public QWidget, public EventListenerInterface, public SceneableInterface { Q_OBJECT public: AnnotationSelectionViewController(const int32_t browserWindowIndex, QWidget* parent); virtual ~AnnotationSelectionViewController(); // ADD_NEW_METHODS_HERE 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 implemetation by sub-classes. // protected: // virtual void saveSubClassDataToScene(const SceneAttributes* sceneAttributes, // SceneClass* sceneClass) = 0; // // virtual void restoreSubClassDataFromScene(const SceneAttributes* sceneAttributes, // const SceneClass* sceneClass) = 0; private slots: void checkBoxToggled(); void displayGroupSelected(const DisplayGroupEnum::Enum); private: AnnotationSelectionViewController(const AnnotationSelectionViewController&); AnnotationSelectionViewController& operator=(const AnnotationSelectionViewController&); QWidget* createSelectionWidget(); void updateAnnotationSelections(); void updateOtherAnnotationViewControllers(); SceneClassAssistant* m_sceneAssistant; int32_t m_browserWindowIndex; DisplayGroupEnumComboBox* m_displayGroupComboBox; DisplayGroupAndTabItemViewController* m_selectionViewController; QCheckBox* m_displayAnnotationsCheckBox; QCheckBox* m_displayTextAnnotationsCheckBox; QCheckBox* m_displayWindowAnnotationInSingleTabViewsCheckBox; static std::set s_allAnnotationSelectionViewControllers; // ADD_NEW_MEMBERS_HERE }; #ifdef __ANNOTATION_SELECTION_VIEW_CONTROLLER_DECLARE__ std::set AnnotationSelectionViewController::s_allAnnotationSelectionViewControllers; #endif // __ANNOTATION_SELECTION_VIEW_CONTROLLER_DECLARE__ } // namespace #endif //__ANNOTATION_SELECTION_VIEW_CONTROLLER_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/GuiQt/AnnotationTextAlignmentWidget.cxx000066400000000000000000000531131300200146000303060ustar00rootroot00000000000000 /*LICENSE_START*/ /* * Copyright (C) 2015 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 __ANNOTATION_TEXT_ALIGNMENT_WIDGET_DECLARE__ #include "AnnotationTextAlignmentWidget.h" #undef __ANNOTATION_TEXT_ALIGNMENT_WIDGET_DECLARE__ #include #include #include #include #include #include #include #include #include #include #include "AnnotationManager.h" #include "AnnotationRedoUndoCommand.h" #include "AnnotationText.h" #include "Brain.h" #include "CaretAssert.h" #include "EventGraphicsUpdateAllWindows.h" #include "EventManager.h" #include "GuiManager.h" #include "MathFunctions.h" #include "WuQMessageBox.h" #include "WuQtUtilities.h" using namespace caret; /** * \class caret::AnnotationTextAlignmentWidget * \brief Widget for adjusting annotation alignment. * \ingroup GuiQt */ /** * Constructor. * * @param browserWindowIndex * Index of browser window. * @param parent * The parent widget. */ AnnotationTextAlignmentWidget::AnnotationTextAlignmentWidget(const int32_t browserWindowIndex, QWidget* parent) : QWidget(parent), m_browserWindowIndex(browserWindowIndex) { m_smallLayoutFlag = true; QToolButton* leftAlignToolButton = createHorizontalAlignmentToolButton(AnnotationTextAlignHorizontalEnum::LEFT); QToolButton* centerAlignToolButton = createHorizontalAlignmentToolButton(AnnotationTextAlignHorizontalEnum::CENTER); 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->addAction(leftAlignToolButton->defaultAction()); m_horizontalAlignActionGroup->addAction(centerAlignToolButton->defaultAction()); m_horizontalAlignActionGroup->addAction(rightAlignToolButton->defaultAction()); QObject::connect(m_horizontalAlignActionGroup, SIGNAL(triggered(QAction*)), this, SLOT(horizontalAlignmentActionSelected(QAction*))); QToolButton* topAlignToolButton = createVerticalAlignmentToolButton(AnnotationTextAlignVerticalEnum::TOP); QToolButton* middleAlignToolButton = createVerticalAlignmentToolButton(AnnotationTextAlignVerticalEnum::MIDDLE); 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->addAction(topAlignToolButton->defaultAction()); m_verticalAlignActionGroup->addAction(middleAlignToolButton->defaultAction()); m_verticalAlignActionGroup->addAction(bottomAlignToolButton->defaultAction()); QObject::connect(m_verticalAlignActionGroup, SIGNAL(triggered(QAction*)), this, SLOT(verticalAlignmentActionSelected(QAction*))); QGridLayout* gridLayout = new QGridLayout(this); WuQtUtilities::setLayoutSpacingAndMargins(gridLayout, 2, 0); if (m_smallLayoutFlag) { QLabel* alignmentLabel = new QLabel("Alignment"); int row = gridLayout->rowCount(); gridLayout->addWidget(alignmentLabel, row, 0, 1, 3, Qt::AlignHCenter); row++; gridLayout->addWidget(leftAlignToolButton, row, 0); gridLayout->addWidget(centerAlignToolButton, row, 1); gridLayout->addWidget(rightAlignToolButton, row, 2); row++; gridLayout->addWidget(topAlignToolButton, row, 0); gridLayout->addWidget(middleAlignToolButton, row, 1); gridLayout->addWidget(bottomAlignToolButton, row, 2); } else { QLabel* horizontalLabel = new QLabel("Text Horizontal"); QHBoxLayout* horizontalAlignLayout = new QHBoxLayout(); WuQtUtilities::setLayoutSpacingAndMargins(horizontalAlignLayout, 2, 0); horizontalAlignLayout->addWidget(leftAlignToolButton); horizontalAlignLayout->addWidget(centerAlignToolButton); horizontalAlignLayout->addWidget(rightAlignToolButton); QLabel* verticalLabel = new QLabel("Text Vertical"); QHBoxLayout* verticalAlignLayout = new QHBoxLayout(); WuQtUtilities::setLayoutSpacingAndMargins(verticalAlignLayout, 2, 0); verticalAlignLayout->addWidget(topAlignToolButton); verticalAlignLayout->addWidget(middleAlignToolButton); verticalAlignLayout->addWidget(bottomAlignToolButton); gridLayout->addWidget(horizontalLabel, 0, 0, Qt::AlignHCenter); gridLayout->addLayout(horizontalAlignLayout, 1, 0); gridLayout->addWidget(WuQtUtilities::createVerticalLineWidget(), 0, 1, 2, 1); gridLayout->addWidget(verticalLabel, 0, 2, Qt::AlignHCenter); gridLayout->addLayout(verticalAlignLayout, 1, 2); } } /** * Destructor. */ AnnotationTextAlignmentWidget::~AnnotationTextAlignmentWidget() { } /** * Update with the given annotation. * * @param annotationTexts. */ void AnnotationTextAlignmentWidget::updateContent(std::vector& annotationTexts) { m_annotations.clear(); m_annotations.insert(m_annotations.end(), annotationTexts.begin(), annotationTexts.end()); { /* * Update horizontal alignment */ m_horizontalAlignActionGroup->blockSignals(true); /* * If multiple annotations are selected, the may have different alignments. */ std::set selectedAlignments; for (std::vector::iterator iter = annotationTexts.begin(); iter != annotationTexts.end(); iter++) { const AnnotationText* annText = *iter; CaretAssert(annText); selectedAlignments.insert(annText->getHorizontalAlignment()); } AnnotationTextAlignHorizontalEnum::Enum alignment = AnnotationTextAlignHorizontalEnum::LEFT; bool alignmentValid = false; if (selectedAlignments.size() == 1) { alignment = *(selectedAlignments.begin()); alignmentValid = true; } /* * Update the status of each action * * An action is "checked" if an only if all selected annotations * have the same alignment. */ QList allActions = m_horizontalAlignActionGroup->actions(); QListIterator iter(allActions); while (iter.hasNext()) { QAction* action = iter.next(); const int intValue = action->data().toInt(); bool valid = false; AnnotationTextAlignHorizontalEnum::Enum actionAlign = AnnotationTextAlignHorizontalEnum::fromIntegerCode(intValue, &valid); bool actionChecked = false; if (valid) { if (alignmentValid) { if (actionAlign == alignment) { actionChecked = true; } } } action->setChecked(actionChecked); } if (alignmentValid) { AnnotationText::setUserDefaultHorizontalAlignment(alignment); } m_horizontalAlignActionGroup->blockSignals(false); } { /* * Update vertical alignment */ m_verticalAlignActionGroup->blockSignals(true); /* * If multiple annotations are selected, the may have different alignments. */ std::set selectedAlignments; for (std::vector::iterator iter = annotationTexts.begin(); iter != annotationTexts.end(); iter++) { const AnnotationText* annText = *iter; CaretAssert(annText); selectedAlignments.insert(annText->getVerticalAlignment()); } AnnotationTextAlignVerticalEnum::Enum alignment = AnnotationTextAlignVerticalEnum::TOP; bool alignmentValid = false; if (selectedAlignments.size() == 1) { alignment = *(selectedAlignments.begin()); alignmentValid = true; } /* * Update the status of each action * * An action is "checked" if an only if all selected annotations * have the same alignment. */ QList allActions = m_verticalAlignActionGroup->actions(); QListIterator iter(allActions); while (iter.hasNext()) { QAction* action = iter.next(); const int intValue = action->data().toInt(); bool valid = false; AnnotationTextAlignVerticalEnum::Enum actionAlign = AnnotationTextAlignVerticalEnum::fromIntegerCode(intValue, &valid); bool actionChecked = false; if (valid) { if (alignmentValid) { if (actionAlign == alignment) { actionChecked = true; } } } action->setChecked(actionChecked); } if (alignmentValid) { AnnotationText::setUserDefaultVerticalAlignment(alignment); } m_verticalAlignActionGroup->blockSignals(false); } setEnabled( ! annotationTexts.empty()); } /** * Gets called when a horizontal alignment selection is made. * * @param action * Action that was selected. */ void AnnotationTextAlignmentWidget::horizontalAlignmentActionSelected(QAction* action) { CaretAssert(action); const int intValue = action->data().toInt(); bool valid = false; AnnotationTextAlignHorizontalEnum::Enum actionAlign = AnnotationTextAlignHorizontalEnum::fromIntegerCode(intValue, &valid); if (valid) { AnnotationRedoUndoCommand* undoCommand = new AnnotationRedoUndoCommand(); undoCommand->setModeTextAlignmentHorizontal(actionAlign, m_annotations); AnnotationManager* annMan = GuiManager::get()->getBrain()->getAnnotationManager(); AString errorMessage; if ( ! annMan->applyCommand(undoCommand, errorMessage)) { WuQMessageBox::errorOk(this, errorMessage); } EventManager::get()->sendSimpleEvent(EventTypeEnum::EVENT_ANNOTATION_TOOLBAR_UPDATE); EventManager::get()->sendEvent(EventGraphicsUpdateAllWindows().getPointer()); AnnotationText::setUserDefaultHorizontalAlignment(actionAlign); } } /** * Gets called when a vertical alignment selection is made. * * @param action * Action that was selected. */ void AnnotationTextAlignmentWidget::verticalAlignmentActionSelected(QAction* action) { CaretAssert(action); const int intValue = action->data().toInt(); bool valid = false; AnnotationTextAlignVerticalEnum::Enum actionAlign = AnnotationTextAlignVerticalEnum::fromIntegerCode(intValue, &valid); if (valid) { AnnotationRedoUndoCommand* undoCommand = new AnnotationRedoUndoCommand(); undoCommand->setModeTextAlignmentVertical(actionAlign, m_annotations); AnnotationManager* annMan = GuiManager::get()->getBrain()->getAnnotationManager(); AString errorMessage; if ( ! annMan->applyCommand(undoCommand, errorMessage)) { WuQMessageBox::errorOk(this, errorMessage); } EventManager::get()->sendSimpleEvent(EventTypeEnum::EVENT_ANNOTATION_TOOLBAR_UPDATE); EventManager::get()->sendEvent(EventGraphicsUpdateAllWindows().getPointer()); AnnotationText::setUserDefaultVerticalAlignment(actionAlign); } } /** * Create a tool button for the given horizontal alignment. * The tool button will contain an action with the appropriate * icon and tooltip. * * @param horizontalAlignment * The horizontal alignment. */ QToolButton* AnnotationTextAlignmentWidget::createHorizontalAlignmentToolButton(const AnnotationTextAlignHorizontalEnum::Enum horizontalAlignment) { QString toolTipText; switch (horizontalAlignment) { case AnnotationTextAlignHorizontalEnum::CENTER: toolTipText = "Align Text Center"; break; case AnnotationTextAlignHorizontalEnum::LEFT: toolTipText = "Align Text Left"; break; case AnnotationTextAlignHorizontalEnum::RIGHT: toolTipText = "Align Text Right"; break; } QToolButton* toolButton = new QToolButton(); QPixmap pixmap = createHorizontalAlignmentPixmap(toolButton, horizontalAlignment); QAction* action = new QAction(this); action->setCheckable(true); action->setData((int)AnnotationTextAlignHorizontalEnum::toIntegerCode(horizontalAlignment)); action->setToolTip(toolTipText); action->setIcon(QIcon(pixmap)); toolButton->setDefaultAction(action); toolButton->setIconSize(pixmap.size()); return toolButton; } /** * Create a tool button for the given vertical alignment. * The tool button will contain an action with the appropriate * icon and tooltip. * * @param verticalAlignment * The vertical alignment. */ QToolButton* AnnotationTextAlignmentWidget::createVerticalAlignmentToolButton(const AnnotationTextAlignVerticalEnum::Enum verticalAlignment) { QString toolTipText; switch (verticalAlignment) { case AnnotationTextAlignVerticalEnum::BOTTOM: toolTipText = "Align Text Bottom"; break; case AnnotationTextAlignVerticalEnum::MIDDLE: toolTipText = "Align Text Middle"; break; case AnnotationTextAlignVerticalEnum::TOP: toolTipText = "Align Text Top"; break; } QToolButton* toolButton = new QToolButton(); QPixmap pixmap = createVerticalAlignmentPixmap(toolButton, verticalAlignment); QAction* action = new QAction(this); action->setCheckable(true); action->setData((int)AnnotationTextAlignVerticalEnum::toIntegerCode(verticalAlignment)); action->setToolTip(toolTipText); action->setIcon(QIcon(pixmap)); toolButton->setDefaultAction(action); toolButton->setIconSize(pixmap.size()); return toolButton; } /** * Create a horizontal alignment pixmap. * * @param widget * To color the pixmap with backround and foreground, * the palette from the given widget is used. * @param horizontalAlignment * The horizontal alignment. * @return * Pixmap with icon for the given horizontal alignment. */ QPixmap AnnotationTextAlignmentWidget::createHorizontalAlignmentPixmap(const QWidget* widget, const AnnotationTextAlignHorizontalEnum::Enum horizontalAlignment) { CaretAssert(widget); /* * Create a small, square pixmap that will contain * the foreground color around the pixmap's perimeter. */ float width = 24.0; float height = 24.0; int32_t numLines = 5; if (m_smallLayoutFlag) { width = 12.0; height = 12.0; numLines = 3; } QPixmap pixmap(static_cast(width), static_cast(height)); QSharedPointer painter = WuQtUtilities::createPixmapWidgetPainter(widget, pixmap); 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); for (int32_t i = 1; i <= numLines; i++) { const qreal lineLength = (((i % 2) == 0) ? shortLineLength : longLineLength); const qreal y = yStep * i; qreal xStart = 0.0; qreal xEnd = width; switch (horizontalAlignment) { case AnnotationTextAlignHorizontalEnum::CENTER: xStart = (width - lineLength) / 2.0; xEnd = xStart + lineLength; break; case AnnotationTextAlignHorizontalEnum::LEFT: xStart = margin; xEnd = xStart + lineLength; break; case AnnotationTextAlignHorizontalEnum::RIGHT: xEnd = width - margin; xStart = xEnd - lineLength; break; } painter->drawLine(QLineF(xStart, y, xEnd, y)); } return pixmap; } /** * Create a vertical alignment pixmap. * * @param widget * To color the pixmap with backround and foreground, * the palette from the given widget is used. * @param verticalAlignment * The vertical alignment. * @return * Pixmap with icon for the given vertical alignment. */ QPixmap AnnotationTextAlignmentWidget::createVerticalAlignmentPixmap(const QWidget* widget, const AnnotationTextAlignVerticalEnum::Enum verticalAlignment) { CaretAssert(widget); /* * Create a small, square pixmap that will contain * the foreground color around the pixmap's perimeter. */ float width = 24.0; float height = 24.0; int32_t numLines = 5; if (m_smallLayoutFlag) { width = 12.0; height = 12.0; numLines = 3; } QPixmap pixmap(static_cast(width), static_cast(height)); QSharedPointer painter = WuQtUtilities::createPixmapWidgetPainter(widget, pixmap); const qreal margin = width * 0.05; if (m_smallLayoutFlag) { float yStep = 3.0; float y1 = 0.0; float y2 = 0.0; switch (verticalAlignment) { case AnnotationTextAlignVerticalEnum::BOTTOM: y1 = (height - 1 - yStep); y2 = y1 + yStep; break; case AnnotationTextAlignVerticalEnum::MIDDLE: y1 = MathFunctions::round((height / 2.0) - (yStep / 2.0)); y2 = y1 + yStep; break; case AnnotationTextAlignVerticalEnum::TOP: y1 = yStep; y2 = y1 + yStep; break; } const float xStart = margin; const float xEnd = width - (margin * 2.0); painter->drawLine(QLineF(xStart, y1, xEnd, y1)); painter->drawLine(QLineF(xStart, y2, xEnd, y2)); } else { const qreal longLineLength = width - (margin * 2.0); const qreal shortLineLength = width / 2.0; const qreal yStep = MathFunctions::round(height / 6.0); for (int32_t i = 1; i <= numLines; i++) { const qreal lineLength = (((i % 2) == 0) ? shortLineLength : longLineLength); int32_t iOffset = i; switch (verticalAlignment) { case AnnotationTextAlignVerticalEnum::BOTTOM: iOffset += 2; break; case AnnotationTextAlignVerticalEnum::MIDDLE: iOffset += 1; break; case AnnotationTextAlignVerticalEnum::TOP: break; } const qreal y = yStep * iOffset; const qreal xStart = margin; const qreal xEnd = xStart + lineLength; painter->drawLine(QLineF(xStart, y, xEnd, y)); } } return pixmap; } connectome-workbench-1.2.3+git41-gc4c6c90/src/GuiQt/AnnotationTextAlignmentWidget.h000066400000000000000000000060321300200146000277310ustar00rootroot00000000000000#ifndef __ANNOTATION_TEXT_ALIGNMENT_WIDGET_H__ #define __ANNOTATION_TEXT_ALIGNMENT_WIDGET_H__ /*LICENSE_START*/ /* * Copyright (C) 2015 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 "AnnotationTextAlignHorizontalEnum.h" #include "AnnotationTextAlignVerticalEnum.h" class QActionGroup; class QToolButton; namespace caret { class Annotation; class AnnotationText; class AnnotationTextAlignmentWidget : public QWidget { Q_OBJECT public: AnnotationTextAlignmentWidget(const int32_t browserWindowIndex, QWidget* parent = 0); virtual ~AnnotationTextAlignmentWidget(); // ADD_NEW_METHODS_HERE void updateContent(std::vector& annotationTexts); private slots: void horizontalAlignmentActionSelected(QAction* action); void verticalAlignmentActionSelected(QAction* action); private: AnnotationTextAlignmentWidget(const AnnotationTextAlignmentWidget&); AnnotationTextAlignmentWidget& operator=(const AnnotationTextAlignmentWidget&); QToolButton* createHorizontalAlignmentToolButton(const AnnotationTextAlignHorizontalEnum::Enum horizontalAlignment); QToolButton* createVerticalAlignmentToolButton(const AnnotationTextAlignVerticalEnum::Enum verticalAlignment); QPixmap createHorizontalAlignmentPixmap(const QWidget* widget, const AnnotationTextAlignHorizontalEnum::Enum horizontalAlignment); QPixmap createVerticalAlignmentPixmap(const QWidget* widget, const AnnotationTextAlignVerticalEnum::Enum verticalAlignment); const int32_t m_browserWindowIndex; QActionGroup* m_horizontalAlignActionGroup; QActionGroup* m_verticalAlignActionGroup; std::vector m_annotations; bool m_smallLayoutFlag; // ADD_NEW_MEMBERS_HERE }; #ifdef __ANNOTATION_TEXT_ALIGNMENT_WIDGET_DECLARE__ // #endif // __ANNOTATION_TEXT_ALIGNMENT_WIDGET_DECLARE__ } // namespace #endif //__ANNOTATION_TEXT_ALIGNMENT_WIDGET_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/GuiQt/AnnotationTextEditorDialog.cxx000066400000000000000000000104041300200146000275660ustar00rootroot00000000000000 /*LICENSE_START*/ /* * Copyright (C) 2015 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 __ANNOTATION_TEXT_EDITOR_DIALOG_DECLARE__ #include "AnnotationTextEditorDialog.h" #undef __ANNOTATION_TEXT_EDITOR_DIALOG_DECLARE__ #include #include #include #include "AnnotationManager.h" #include "AnnotationRedoUndoCommand.h" #include "AnnotationText.h" #include "Brain.h" #include "CaretAssert.h" #include "EventGraphicsUpdateAllWindows.h" #include "EventManager.h" #include "GuiManager.h" #include "WuQMessageBox.h" #include "WuQtUtilities.h" using namespace caret; /** * \class caret::AnnotationTextEditorDialog * \brief Dialog for editing annotation text. * \ingroup GuiQt */ /** * Constructor. * * @param textAnnotation * Text annotation that will be edited. * @param parent * Parent of this dialog. */ AnnotationTextEditorDialog::AnnotationTextEditorDialog(AnnotationText* textAnnotation, QWidget* parent) : QDialog(parent), m_textAnnotation(textAnnotation) { CaretAssert(textAnnotation); Qt::WindowFlags flags = windowFlags(); flags |= (Qt::CustomizeWindowHint); // disables min/max buttons setWindowFlags(flags); setWindowTitle("Edit Annotation Text"); m_uneditedText = textAnnotation->getText(); m_textEdit = new QTextEdit(); m_textEdit->setText(textAnnotation->getText()); m_textEdit->selectAll(); m_textEdit->setToolTip("Press OK to save text changes and close dialog\n" "Press CANCEL to revert changes and close dialog"); QObject::connect(m_textEdit, SIGNAL(textChanged()), this, SLOT(textWasEdited())); QDialogButtonBox* buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel); QObject::connect(buttonBox, SIGNAL(accepted()), this, SLOT(accept())); QObject::connect(buttonBox, SIGNAL(rejected()), this, SLOT(reject())); QVBoxLayout* layout = new QVBoxLayout(this); WuQtUtilities::setLayoutSpacingAndMargins(layout, 0, 2); layout->addWidget(m_textEdit); layout->addWidget(buttonBox); } /** * Destructor. */ AnnotationTextEditorDialog::~AnnotationTextEditorDialog() { } /** * Closes the dialog. * * @param resultCode */ void AnnotationTextEditorDialog::done(int resultCode) { if (resultCode == QDialog::Accepted) { } else { m_textEdit->setText(m_uneditedText); } textWasEdited(); QDialog::done(resultCode); } /** * Called when the user edits text. * * @param text * Text entered by user. */ void AnnotationTextEditorDialog::textWasEdited() { const QString text = m_textEdit->toPlainText(); AnnotationManager* annMan = GuiManager::get()->getBrain()->getAnnotationManager(); AnnotationRedoUndoCommand* undoCommand = new AnnotationRedoUndoCommand(); std::vector annotationVector; annotationVector.push_back(m_textAnnotation); undoCommand->setModeTextCharacters(text, annotationVector); AString errorMessage; if ( ! annMan->applyCommand(undoCommand, errorMessage)) { WuQMessageBox::errorOk(this, errorMessage); } EventManager::get()->sendSimpleEvent(EventTypeEnum::EVENT_ANNOTATION_TOOLBAR_UPDATE); EventManager::get()->sendEvent(EventGraphicsUpdateAllWindows().getPointer()); } connectome-workbench-1.2.3+git41-gc4c6c90/src/GuiQt/AnnotationTextEditorDialog.h000066400000000000000000000040341300200146000272150ustar00rootroot00000000000000#ifndef __ANNOTATION_TEXT_EDITOR_DIALOG_H__ #define __ANNOTATION_TEXT_EDITOR_DIALOG_H__ /*LICENSE_START*/ /* * Copyright (C) 2015 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 QTextEdit; namespace caret { class AnnotationText; class AnnotationTextEditorDialog : public QDialog { Q_OBJECT public: AnnotationTextEditorDialog(AnnotationText* textAnnotation, QWidget* parent); virtual ~AnnotationTextEditorDialog(); virtual void done(int resultCode); signals: void textHasBeenChanged(const QString&); private slots: void textWasEdited(); // ADD_NEW_METHODS_HERE private: AnnotationTextEditorDialog(const AnnotationTextEditorDialog&); AnnotationTextEditorDialog& operator=(const AnnotationTextEditorDialog&); AnnotationText* m_textAnnotation; QString m_uneditedText; QTextEdit* m_textEdit; // ADD_NEW_MEMBERS_HERE }; #ifdef __ANNOTATION_TEXT_EDITOR_DIALOG_DECLARE__ // #endif // __ANNOTATION_TEXT_EDITOR_DIALOG_DECLARE__ } // namespace #endif //__ANNOTATION_TEXT_EDITOR_DIALOG_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/GuiQt/AnnotationTextEditorWidget.cxx000066400000000000000000000177051300200146000276250ustar00rootroot00000000000000 /*LICENSE_START*/ /* * Copyright (C) 2015 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 __ANNOTATION_TEXT_EDITOR_WIDGET_DECLARE__ #include "AnnotationTextEditorWidget.h" #undef __ANNOTATION_TEXT_EDITOR_WIDGET_DECLARE__ #include #include #include #include #include "AnnotationManager.h" #include "AnnotationRedoUndoCommand.h" #include "AnnotationText.h" #include "AnnotationTextEditorDialog.h" #include "Brain.h" #include "CaretAssert.h" #include "EnumComboBoxTemplate.h" #include "EventGraphicsUpdateAllWindows.h" #include "EventManager.h" #include "GuiManager.h" #include "WuQDataEntryDialog.h" #include "WuQMessageBox.h" #include "WuQtUtilities.h" using namespace caret; /** * \class caret::AnnotationTextEditorWidget * \brief Widget for editing annotation text. * \ingroup GuiQt */ /** * Constructor. * * @param browserWindowIndex * Index of browser window. * @param parent * The parent widget. */ AnnotationTextEditorWidget::AnnotationTextEditorWidget(const int32_t browserWindowIndex, QWidget* parent) : QWidget(parent), m_browserWindowIndex(browserWindowIndex) { m_annotationText = NULL; QLabel* textLabel = new QLabel("Text"); m_textLineEdit = new AnnotationLineEdit(); QObject::connect(m_textLineEdit, SIGNAL(textEdited(const QString&)), this, SLOT(annotationTextChanged())); QObject::connect(m_textLineEdit, SIGNAL(doubleClickInLineEdit()), this, SLOT(displayTextEditor())); m_textLineEdit->setToolTip("Insert lines breaks with '\\n' \n" "Double-click text to edit text in a multi-line text dialog"); const QString textConnectToolTip("Connect text to brainordinate with arrow or line"); m_annotationTextConnectTypeEnumComboBox = new EnumComboBoxTemplate(this); m_annotationTextConnectTypeEnumComboBox->getWidget()->setToolTip(textConnectToolTip); m_annotationTextConnectTypeEnumComboBox->setup(); QObject::connect(m_annotationTextConnectTypeEnumComboBox, SIGNAL(itemActivated()), this, SLOT(annotationTextConnectTypeEnumComboBoxItemActivated())); QVBoxLayout* layout = new QVBoxLayout(this); WuQtUtilities::setLayoutSpacingAndMargins(layout, 2, 2); layout->addWidget(textLabel, 0, Qt::AlignHCenter); layout->addWidget(m_textLineEdit); //, 0, Qt::AlignHCenter); layout->addWidget(m_annotationTextConnectTypeEnumComboBox->getWidget(), 0, Qt::AlignHCenter); setSizePolicy(sizePolicy().horizontalPolicy(), QSizePolicy::Fixed); setMaximumWidth(200); } /** * Destructor. */ AnnotationTextEditorWidget::~AnnotationTextEditorWidget() { } /** * Update with the given annotation. * * @param annotation. */ void AnnotationTextEditorWidget::updateContent(std::vector& annotationTexts) { m_annotationText = NULL; if (annotationTexts.size() == 1) { m_annotationText = annotationTexts[0]; } AnnotationTextConnectTypeEnum::Enum connectValue = AnnotationTextConnectTypeEnum::ANNOTATION_TEXT_CONNECT_NONE; if (m_annotationText != NULL) { connectValue = m_annotationText->getConnectToBrainordinate(); } m_annotationTextConnectTypeEnumComboBox->setSelectedItem(connectValue); QString text; if (m_annotationText != NULL) { setEnabled(true); if (m_annotationText->isConnectToBrainordinateValid()) { m_annotationTextConnectTypeEnumComboBox->getComboBox()->setEnabled(true); } text = m_annotationText->getText(); } else { setEnabled(false); } updateLineEditText(text); } /** * Gets called when the edit button is clicked. */ void AnnotationTextEditorWidget::displayTextEditor() { if (m_annotationText != NULL) { AnnotationTextEditorDialog ted(m_annotationText, this); QObject::connect(&ted, SIGNAL(textHasBeenChanged(const QString&)), this, SLOT(textEditorDialogTextChanged(const QString&))); ted.exec(); } } /** * Gets called when text is edited in the text editor dialog. */ void AnnotationTextEditorWidget::textEditorDialogTextChanged(const QString& text) { updateLineEditText(text); annotationTextChanged(); } /** * Gets called when the annotation text is changed. */ void AnnotationTextEditorWidget::annotationTextChanged() { if (m_annotationText == NULL) { return; } /* * The update event will cause the text to be reloaded * into the line edit and that will cause the cursor * position to change so save and later restore the * cursor position. */ const int cursorPos = m_textLineEdit->cursorPosition(); QString s(m_textLineEdit->text()); s.replace("\\n", "\n"); std::vector selectedAnnotations; selectedAnnotations.push_back(m_annotationText); AnnotationRedoUndoCommand* undoCommand = new AnnotationRedoUndoCommand(); undoCommand->setModeTextCharacters(s, selectedAnnotations); AnnotationManager* annMan = GuiManager::get()->getBrain()->getAnnotationManager(); AString errorMessage; if ( ! annMan->applyCommand(undoCommand, errorMessage)) { WuQMessageBox::errorOk(this, errorMessage); } EventManager::get()->sendSimpleEvent(EventTypeEnum::EVENT_ANNOTATION_TOOLBAR_UPDATE); EventManager::get()->sendEvent(EventGraphicsUpdateAllWindows().getPointer()); m_textLineEdit->setCursorPosition(cursorPos); } /** * Update the text in the line edit including * replacing newline characters with "\n" * * @param text * Text for the line edit. */ void AnnotationTextEditorWidget::updateLineEditText(const QString& text) { QString s(text); s.replace("\n", "\\n"); m_textLineEdit->setText(s); } /** * Gets called when the annotation connect to brainordinate is changed. */ void AnnotationTextEditorWidget::annotationTextConnectTypeEnumComboBoxItemActivated() { if (m_annotationText == NULL) { return; } std::vector selectedAnnotations; selectedAnnotations.push_back(m_annotationText); const AnnotationTextConnectTypeEnum::Enum connectType = m_annotationTextConnectTypeEnumComboBox->getSelectedItem(); AnnotationRedoUndoCommand* undoCommand = new AnnotationRedoUndoCommand(); undoCommand->setModeTextConnectToBrainordinate(connectType, selectedAnnotations); AnnotationManager* annMan = GuiManager::get()->getBrain()->getAnnotationManager(); AString errorMessage; if ( ! annMan->applyCommand(undoCommand, errorMessage)) { WuQMessageBox::errorOk(this, errorMessage); } EventManager::get()->sendSimpleEvent(EventTypeEnum::EVENT_ANNOTATION_TOOLBAR_UPDATE); EventManager::get()->sendEvent(EventGraphicsUpdateAllWindows().getPointer()); } connectome-workbench-1.2.3+git41-gc4c6c90/src/GuiQt/AnnotationTextEditorWidget.h000066400000000000000000000056651300200146000272540ustar00rootroot00000000000000#ifndef __ANNOTATION_TEXT_EDITOR_WIDGET_H__ #define __ANNOTATION_TEXT_EDITOR_WIDGET_H__ /*LICENSE_START*/ /* * Copyright (C) 2015 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 namespace caret { class AnnotationLineEdit; class AnnotationText; class EnumComboBoxTemplate; class AnnotationTextEditorWidget : public QWidget { Q_OBJECT public: AnnotationTextEditorWidget(const int32_t browserWindowIndex, QWidget* parent = 0); virtual ~AnnotationTextEditorWidget(); // ADD_NEW_METHODS_HERE void updateContent(std::vector& annotationTexts); private slots: void annotationTextChanged(); void displayTextEditor(); void textEditorDialogTextChanged(const QString&); void annotationTextConnectTypeEnumComboBoxItemActivated(); private: AnnotationTextEditorWidget(const AnnotationTextEditorWidget&); AnnotationTextEditorWidget& operator=(const AnnotationTextEditorWidget&); void updateLineEditText(const QString& text); const int32_t m_browserWindowIndex; AnnotationText* m_annotationText; AnnotationLineEdit* m_textLineEdit; EnumComboBoxTemplate* m_annotationTextConnectTypeEnumComboBox; // ADD_NEW_MEMBERS_HERE }; /** * A line edit that emits a signal if the user double-clicks * in the line edit. */ class AnnotationLineEdit : public QLineEdit { Q_OBJECT public: AnnotationLineEdit(QWidget* parent = 0) : QLineEdit(parent) { } virtual ~AnnotationLineEdit() { } signals: void doubleClickInLineEdit(); protected: void mouseDoubleClickEvent(QMouseEvent*) { emit doubleClickInLineEdit(); } }; #ifdef __ANNOTATION_TEXT_EDITOR_WIDGET_DECLARE__ // #endif // __ANNOTATION_TEXT_EDITOR_WIDGET_DECLARE__ } // namespace #endif //__ANNOTATION_TEXT_EDITOR_WIDGET_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/GuiQt/AnnotationTextOrientationWidget.cxx000066400000000000000000000231741300200146000306670ustar00rootroot00000000000000 /*LICENSE_START*/ /* * Copyright (C) 2015 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 __ANNOTATION_TEXT_ORIENTATION_WIDGET_DECLARE__ #include "AnnotationTextOrientationWidget.h" #undef __ANNOTATION_TEXT_ORIENTATION_WIDGET_DECLARE__ #include #include #include #include #include #include #include #include #include "AnnotationManager.h" #include "AnnotationRedoUndoCommand.h" #include "AnnotationText.h" #include "Brain.h" #include "CaretAssert.h" #include "EventGraphicsUpdateAllWindows.h" #include "EventManager.h" #include "GuiManager.h" #include "WuQMessageBox.h" #include "WuQtUtilities.h" using namespace caret; /** * \class caret::AnnotationTextOrientationWidget * \brief Widget for selection of text orientation. * \ingroup GuiQt */ /** * Constructor. */ AnnotationTextOrientationWidget::AnnotationTextOrientationWidget(const int32_t browserWindowIndex, QWidget* parent) : QWidget(parent), m_browserWindowIndex(browserWindowIndex) { QLabel* orientationLabel = new QLabel("Orientation"); QToolButton* horizontalOrientationToolButton = createOrientationToolButton(AnnotationTextOrientationEnum::HORIZONTAL); QToolButton* verticalOrientationToolButton = createOrientationToolButton(AnnotationTextOrientationEnum::STACKED); m_orientationActionGroup = new QActionGroup(this); m_orientationActionGroup->setExclusive(false); m_orientationActionGroup->addAction(horizontalOrientationToolButton->defaultAction()); m_orientationActionGroup->addAction(verticalOrientationToolButton->defaultAction()); QObject::connect(m_orientationActionGroup, SIGNAL(triggered(QAction*)), this, SLOT(orientationActionSelected(QAction*))); QGridLayout* gridLayout = new QGridLayout(this); WuQtUtilities::setLayoutSpacingAndMargins(gridLayout, 2, 0); int row = gridLayout->rowCount(); gridLayout->addWidget(orientationLabel, row, 0, 1, 2, Qt::AlignHCenter); row++; gridLayout->addWidget(horizontalOrientationToolButton, row, 0); gridLayout->addWidget(verticalOrientationToolButton, row, 1); setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); } /** * Destructor. */ AnnotationTextOrientationWidget::~AnnotationTextOrientationWidget() { } /** * Update with the given annotation. * * @param annotation. */ void AnnotationTextOrientationWidget::updateContent(std::vector& annotationTexts) { m_annotations.clear(); m_annotations.insert(m_annotations.end(), annotationTexts.begin(), annotationTexts.end()); { /* * Update orientation */ m_orientationActionGroup->blockSignals(true); /* * If multiple annotations are selected, the may have different orientation. */ std::set selectedOrientations; for (std::vector::iterator iter = annotationTexts.begin(); iter != annotationTexts.end(); iter++) { const AnnotationText* annText = *iter; CaretAssert(annText); selectedOrientations.insert(annText->getOrientation()); } AnnotationTextOrientationEnum::Enum orientation = AnnotationTextOrientationEnum::HORIZONTAL; bool orientationValid = false; if (selectedOrientations.size() == 1) { orientation = *(selectedOrientations.begin()); orientationValid = true; } /* * Update the status of each action * * An action is "checked" if an only if all selected annotations * have the same orientation. */ QList allActions = m_orientationActionGroup->actions(); QListIterator iter(allActions); while (iter.hasNext()) { QAction* action = iter.next(); const int intValue = action->data().toInt(); bool valid = false; AnnotationTextOrientationEnum::Enum actionOrient = AnnotationTextOrientationEnum::fromIntegerCode(intValue, &valid); bool actionChecked = false; if (valid) { if (orientationValid) { if (actionOrient == orientation) { actionChecked = true; } } } action->setChecked(actionChecked); } if (orientationValid) { AnnotationText::setUserDefaultOrientation(orientation); } m_orientationActionGroup->blockSignals(false); } setEnabled( ! annotationTexts.empty()); } /** * Gets called when a orientation selection is made. * * @param action * Action that was selected. */ void AnnotationTextOrientationWidget::orientationActionSelected(QAction* action) { CaretAssert(action); const int intValue = action->data().toInt(); bool valid = false; AnnotationTextOrientationEnum::Enum actionOrientation = AnnotationTextOrientationEnum::fromIntegerCode(intValue, &valid); if (valid) { AnnotationRedoUndoCommand* undoCommand = new AnnotationRedoUndoCommand(); undoCommand->setModeTextOrientation(actionOrientation, m_annotations); AnnotationManager* annMan = GuiManager::get()->getBrain()->getAnnotationManager(); AString errorMessage; if ( ! annMan->applyCommand(undoCommand, errorMessage)) { WuQMessageBox::errorOk(this, errorMessage); } EventManager::get()->sendSimpleEvent(EventTypeEnum::EVENT_ANNOTATION_TOOLBAR_UPDATE); EventManager::get()->sendEvent(EventGraphicsUpdateAllWindows().getPointer()); AnnotationText::setUserDefaultOrientation(actionOrientation); } } /** * Create a tool button for the given horizontal alignment. * The tool button will contain an action with the appropriate * icon and tooltip. * * @param orientation * The horizontal alignment. */ QToolButton* AnnotationTextOrientationWidget::createOrientationToolButton(const AnnotationTextOrientationEnum::Enum orientation) { QString toolTipText; switch (orientation) { case AnnotationTextOrientationEnum::HORIZONTAL: toolTipText = "Horizontal (left-to-right) text"; break; case AnnotationTextOrientationEnum::STACKED: toolTipText = "Stacked (top-to-bottom) text"; break; } QToolButton* toolButton = new QToolButton(); QPixmap pixmap = createHorizontalAlignmentPixmap(toolButton, orientation); QAction* action = new QAction(this); action->setCheckable(true); action->setData((int)AnnotationTextOrientationEnum::toIntegerCode(orientation)); action->setToolTip(toolTipText); action->setIcon(QIcon(pixmap)); toolButton->setDefaultAction(action); toolButton->setIconSize(pixmap.size()); return toolButton; } /** * Create a horizontal alignment pixmap. * * @param widget * To color the pixmap with backround and foreground, * the palette from the given widget is used. * @param orientation * The horizontal alignment. * @return * Pixmap with icon for the given horizontal alignment. */ QPixmap AnnotationTextOrientationWidget::createHorizontalAlignmentPixmap(const QWidget* widget, const AnnotationTextOrientationEnum::Enum orientation) { CaretAssert(widget); /* * Create a small, square pixmap that will contain * the foreground color around the pixmap's perimeter. */ float width = 24.0; float height = 24.0; QPixmap pixmap(static_cast(width), static_cast(height)); QSharedPointer painter = WuQtUtilities::createPixmapWidgetPainter(widget, pixmap); switch (orientation) { case AnnotationTextOrientationEnum::HORIZONTAL: painter->drawText(pixmap.rect(), (Qt::AlignCenter), "ab"); break; case AnnotationTextOrientationEnum::STACKED: painter->drawText(pixmap.rect(), (Qt::AlignHCenter | Qt::AlignTop), "a\nb"); break; } return pixmap; } connectome-workbench-1.2.3+git41-gc4c6c90/src/GuiQt/AnnotationTextOrientationWidget.h000066400000000000000000000047611300200146000303150ustar00rootroot00000000000000#ifndef __ANNOTATION_TEXT_ORIENTATION_WIDGET_H__ #define __ANNOTATION_TEXT_ORIENTATION_WIDGET_H__ /*LICENSE_START*/ /* * Copyright (C) 2015 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 "AnnotationTextOrientationEnum.h" class QActionGroup; class QToolButton; namespace caret { class Annotation; class AnnotationText; class AnnotationTextOrientationWidget : public QWidget { Q_OBJECT public: AnnotationTextOrientationWidget(const int32_t browserWindowIndex, QWidget* parent = 0); virtual ~AnnotationTextOrientationWidget(); // ADD_NEW_METHODS_HERE void updateContent(std::vector& annotationTexts); private slots: void orientationActionSelected(QAction* action); private: AnnotationTextOrientationWidget(const AnnotationTextOrientationWidget&); AnnotationTextOrientationWidget& operator=(const AnnotationTextOrientationWidget&); QToolButton* createOrientationToolButton(const AnnotationTextOrientationEnum::Enum orientation); QPixmap createHorizontalAlignmentPixmap(const QWidget* widget, const AnnotationTextOrientationEnum::Enum orientation); const int32_t m_browserWindowIndex; std::vector m_annotations; QActionGroup* m_orientationActionGroup; // ADD_NEW_MEMBERS_HERE }; #ifdef __ANNOTATION_TEXT_ORIENTATION_WIDGET_DECLARE__ // #endif // __ANNOTATION_TEXT_ORIENTATION_WIDGET_DECLARE__ } // namespace #endif //__ANNOTATION_TEXT_ORIENTATION_WIDGET_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/GuiQt/AnnotationWidgetParentEnum.cxx000066400000000000000000000255741300200146000276130ustar00rootroot00000000000000 /*LICENSE_START*/ /* * Copyright (C) 2015 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 __ANNOTATION_WIDGET_PARENT_ENUM_DECLARE__ #include "AnnotationWidgetParentEnum.h" #undef __ANNOTATION_WIDGET_PARENT_ENUM_DECLARE__ #include "CaretAssert.h" using namespace caret; /** * \class caret::AnnotationWidgetParentEnum * \brief * * * * 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_annotationWidgetParentEnumComboBox; * * Declare a slot that is called when user changes selection * private slots: * void annotationWidgetParentEnumComboBoxItemActivated(); * * Implementation File (.cxx) * Include the header files * #include "EnumComboBoxTemplate.h" * #include "AnnotationWidgetParentEnum.h" * * Instatiate: * m_annotationWidgetParentEnumComboBox = new EnumComboBoxTemplate(this); * m_annotationWidgetParentEnumComboBox->setup(); * * Get notified when the user changes the selection: * QObject::connect(m_annotationWidgetParentEnumComboBox, SIGNAL(itemActivated()), * this, SLOT(annotationWidgetParentEnumComboBoxItemActivated())); * * Update the selection: * m_annotationWidgetParentEnumComboBox->setSelectedItem(NEW_VALUE); * * Read the selection: * const AnnotationWidgetParentEnum::Enum VARIABLE = m_annotationWidgetParentEnumComboBox->getSelectedItem(); * */ /** * Constructor. * * @param enumValue * An enumerated value. * @param name * Name of enumerated value. * * @param guiName * User-friendly name for use in user-interface. */ AnnotationWidgetParentEnum::AnnotationWidgetParentEnum(const Enum enumValue, const AString& name, const AString& guiName) { this->enumValue = enumValue; this->integerCode = integerCodeCounter++; this->name = name; this->guiName = guiName; } /** * Destructor. */ AnnotationWidgetParentEnum::~AnnotationWidgetParentEnum() { } /** * Initialize the enumerated metadata. */ void AnnotationWidgetParentEnum::initialize() { if (initializedFlag) { return; } initializedFlag = true; enumData.push_back(AnnotationWidgetParentEnum(ANNOTATION_TOOL_BAR_WIDGET, "ANNOTATION_TOOL_BAR_WIDGET", "Annotation Tool Bar Widget")); enumData.push_back(AnnotationWidgetParentEnum(PARENT_ENUM_FOR_LATER_USE, "PARENT_ENUM_FOR_LATER_USE", "For future usage")); } /** * 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 AnnotationWidgetParentEnum* AnnotationWidgetParentEnum::findData(const Enum enumValue) { if (initializedFlag == false) initialize(); size_t num = enumData.size(); for (size_t i = 0; i < num; i++) { const AnnotationWidgetParentEnum* 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 AnnotationWidgetParentEnum::toName(Enum enumValue) { if (initializedFlag == false) initialize(); const AnnotationWidgetParentEnum* 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. */ AnnotationWidgetParentEnum::Enum AnnotationWidgetParentEnum::fromName(const AString& name, bool* isValidOut) { if (initializedFlag == false) initialize(); bool validFlag = false; Enum enumValue = AnnotationWidgetParentEnum::enumData[0].enumValue; for (std::vector::iterator iter = enumData.begin(); iter != enumData.end(); iter++) { const AnnotationWidgetParentEnum& 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 AnnotationWidgetParentEnum")); } return enumValue; } /** * Get a GUI string representation of the enumerated type. * @param enumValue * Enumerated value. * @return * String representing enumerated value. */ AString AnnotationWidgetParentEnum::toGuiName(Enum enumValue) { if (initializedFlag == false) initialize(); const AnnotationWidgetParentEnum* 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. */ AnnotationWidgetParentEnum::Enum AnnotationWidgetParentEnum::fromGuiName(const AString& guiName, bool* isValidOut) { if (initializedFlag == false) initialize(); bool validFlag = false; Enum enumValue = AnnotationWidgetParentEnum::enumData[0].enumValue; for (std::vector::iterator iter = enumData.begin(); iter != enumData.end(); iter++) { const AnnotationWidgetParentEnum& 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 AnnotationWidgetParentEnum")); } return enumValue; } /** * Get the integer code for a data type. * * @return * Integer code for data type. */ int32_t AnnotationWidgetParentEnum::toIntegerCode(Enum enumValue) { if (initializedFlag == false) initialize(); const AnnotationWidgetParentEnum* 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. */ AnnotationWidgetParentEnum::Enum AnnotationWidgetParentEnum::fromIntegerCode(const int32_t integerCode, bool* isValidOut) { if (initializedFlag == false) initialize(); bool validFlag = false; Enum enumValue = AnnotationWidgetParentEnum::enumData[0].enumValue; for (std::vector::iterator iter = enumData.begin(); iter != enumData.end(); iter++) { const AnnotationWidgetParentEnum& 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 AnnotationWidgetParentEnum")); } 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 AnnotationWidgetParentEnum::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 AnnotationWidgetParentEnum::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(AnnotationWidgetParentEnum::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 AnnotationWidgetParentEnum::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(AnnotationWidgetParentEnum::toGuiName(iter->enumValue)); } if (isSorted) { std::sort(allGuiNames.begin(), allGuiNames.end()); } } connectome-workbench-1.2.3+git41-gc4c6c90/src/GuiQt/AnnotationWidgetParentEnum.h000066400000000000000000000071451300200146000272320ustar00rootroot00000000000000#ifndef __ANNOTATION_WIDGET_PARENT_ENUM_H__ #define __ANNOTATION_WIDGET_PARENT_ENUM_H__ /*LICENSE_START*/ /* * Copyright (C) 2015 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 AnnotationWidgetParentEnum { public: /** * Enumerated values. */ enum Enum { /** Parent is the annotation toolbar */ ANNOTATION_TOOL_BAR_WIDGET, /* * There is only one enum at this time. * There was a second enum for use when the color bar was edited * on the Map and Overlay Settings dialog prior to color bar * becoming an annotation. * * This enumerated type is being preserved in the event there * are other parents for editing annotations such as a * right-click pop-up dialog. */ PARENT_ENUM_FOR_LATER_USE }; ~AnnotationWidgetParentEnum(); 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: AnnotationWidgetParentEnum(const Enum enumValue, const AString& name, const AString& guiName); static const AnnotationWidgetParentEnum* 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 __ANNOTATION_WIDGET_PARENT_ENUM_DECLARE__ std::vector AnnotationWidgetParentEnum::enumData; bool AnnotationWidgetParentEnum::initializedFlag = false; int32_t AnnotationWidgetParentEnum::integerCodeCounter = 0; #endif // __ANNOTATION_WIDGET_PARENT_ENUM_DECLARE__ } // namespace #endif //__ANNOTATION_WIDGET_PARENT_ENUM_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/GuiQt/AnnotationWidthHeightWidget.cxx000066400000000000000000000224351300200146000277360ustar00rootroot00000000000000 /*LICENSE_START*/ /* * Copyright (C) 2015 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 __ANNOTATION_WIDTH_HEIGHT_WIDGET_DECLARE__ #include "AnnotationWidthHeightWidget.h" #undef __ANNOTATION_WIDTH_HEIGHT_WIDGET_DECLARE__ #include #include #include #include "AnnotationBox.h" #include "AnnotationManager.h" #include "AnnotationRedoUndoCommand.h" #include "AnnotationTwoDimensionalShape.h" #include "Brain.h" #include "CaretAssert.h" #include "EventGraphicsUpdateAllWindows.h" #include "EventManager.h" #include "GuiManager.h" #include "WuQFactory.h" #include "WuQMessageBox.h" #include "WuQtUtilities.h" using namespace caret; /** * \class caret::AnnotationWidthHeightWidget * \brief Widget for editing annotation coordinate, size, and rotation. * \ingroup GuiQt */ /** * Constructor. * * @param parentWidgetType * Type of parent widget * @param browserWindowIndex * Index of browser window. * @param parent * Parent of this widget. */ AnnotationWidthHeightWidget::AnnotationWidthHeightWidget(const AnnotationWidgetParentEnum::Enum parentWidgetType, const int32_t browserWindowIndex, QWidget* parent) : QWidget(parent), m_parentWidgetType(parentWidgetType), m_browserWindowIndex(browserWindowIndex) { QString widthLabelText; QString heightLabelText; switch (m_parentWidgetType) { case AnnotationWidgetParentEnum::ANNOTATION_TOOL_BAR_WIDGET: widthLabelText = " W:"; heightLabelText = " H:"; break; case AnnotationWidgetParentEnum::PARENT_ENUM_FOR_LATER_USE: CaretAssert(0); break; } QLabel* widthLabel = new QLabel(widthLabelText); m_widthSpinBox = WuQFactory::newDoubleSpinBoxWithMinMaxStepDecimalsSignalDouble(0.0, 100.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, this, SLOT(heightValueChanged(double))); m_heightSpinBox->setSuffix("%"); WuQtUtilities::setWordWrappedToolTip(m_heightSpinBox, "Percentage height of 2D Shapes (Box, Image, Oval)"); QHBoxLayout* layout = new QHBoxLayout(this); WuQtUtilities::setLayoutSpacingAndMargins(layout, 2, 2); layout->addWidget(widthLabel); layout->addWidget(m_widthSpinBox); layout->addWidget(heightLabel); layout->addWidget(m_heightSpinBox); setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); } /** * Destructor. */ AnnotationWidthHeightWidget::~AnnotationWidthHeightWidget() { } /** * Update with the given annotation. * * @param annotations2D. * Two dimensional annotation. */ void AnnotationWidthHeightWidget::updateContent(std::vector& annotations2D) { m_annotations2D = annotations2D; if ( ! m_annotations2D.empty()) { float widthValue = 0.0; bool haveMultipleWidthValuesFlag = false; float heightValue = 0.0; bool haveMultipleHeightValuesFlag = false; bool firstFlag = true; bool haveValuesFlag = false; const int32_t numAnns = static_cast(m_annotations2D.size()); for (int32_t i = 0; i < numAnns; i++) { CaretAssertVectorIndex(m_annotations2D, i); if (m_annotations2D[i]->getType() == AnnotationTypeEnum::TEXT) { continue; } haveValuesFlag = true; const float width = m_annotations2D[i]->getWidth(); const float height = m_annotations2D[i]->getHeight(); if (firstFlag) { widthValue = width; heightValue = height; firstFlag = false; } else { if (width != widthValue) { haveMultipleWidthValuesFlag = true; } if (height != heightValue) { haveMultipleHeightValuesFlag = true; } widthValue = std::min(widthValue, width); heightValue = std::min(heightValue, height); } } if (haveValuesFlag) { m_widthSpinBox->blockSignals(true); m_widthSpinBox->setValue(widthValue); if (haveMultipleWidthValuesFlag) { m_widthSpinBox->setSuffix("%+"); } else { m_widthSpinBox->setSuffix("%"); } m_widthSpinBox->blockSignals(false); m_heightSpinBox->blockSignals(true); m_heightSpinBox->setValue(heightValue); if (haveMultipleHeightValuesFlag) { m_heightSpinBox->setSuffix("%+"); } else { m_heightSpinBox->setSuffix("%"); } m_heightSpinBox->blockSignals(false); switch (m_parentWidgetType) { case AnnotationWidgetParentEnum::ANNOTATION_TOOL_BAR_WIDGET: AnnotationTwoDimensionalShape::setUserDefaultWidth(widthValue); AnnotationTwoDimensionalShape::setUserDefaultHeight(heightValue); break; case AnnotationWidgetParentEnum::PARENT_ENUM_FOR_LATER_USE: CaretAssert(0); break; } setEnabled(true); } else { setEnabled(false); } } else { setEnabled(false); } } /** * Gets called when height value is changed. * * @param value * */ void AnnotationWidthHeightWidget::heightValueChanged(double value) { std::vector annotations(m_annotations2D.begin(), m_annotations2D.end()); AnnotationRedoUndoCommand* undoCommand = new AnnotationRedoUndoCommand(); undoCommand->setModeTwoDimHeight(value, annotations); AnnotationManager* annMan = GuiManager::get()->getBrain()->getAnnotationManager(); AString errorMessage; if ( ! annMan->applyCommand(undoCommand, errorMessage)) { WuQMessageBox::errorOk(this, errorMessage); } EventManager::get()->sendSimpleEvent(EventTypeEnum::EVENT_ANNOTATION_TOOLBAR_UPDATE); switch (m_parentWidgetType) { case AnnotationWidgetParentEnum::ANNOTATION_TOOL_BAR_WIDGET: AnnotationTwoDimensionalShape::setUserDefaultHeight(value); break; case AnnotationWidgetParentEnum::PARENT_ENUM_FOR_LATER_USE: CaretAssert(0); break; } EventManager::get()->sendEvent(EventGraphicsUpdateAllWindows().getPointer()); } /** * Gets called when width value is changed. * * @param value * */ void AnnotationWidthHeightWidget::widthValueChanged(double value) { std::vector annotations(m_annotations2D.begin(), m_annotations2D.end()); AnnotationRedoUndoCommand* undoCommand = new AnnotationRedoUndoCommand(); undoCommand->setModeTwoDimWidth(value, annotations); AnnotationManager* annMan = GuiManager::get()->getBrain()->getAnnotationManager(); AString errorMessage; if ( ! annMan->applyCommand(undoCommand, errorMessage)) { WuQMessageBox::errorOk(this, errorMessage); } EventManager::get()->sendSimpleEvent(EventTypeEnum::EVENT_ANNOTATION_TOOLBAR_UPDATE); switch (m_parentWidgetType) { case AnnotationWidgetParentEnum::ANNOTATION_TOOL_BAR_WIDGET: AnnotationTwoDimensionalShape::setUserDefaultWidth(value); break; case AnnotationWidgetParentEnum::PARENT_ENUM_FOR_LATER_USE: CaretAssert(0); break; } EventManager::get()->sendEvent(EventGraphicsUpdateAllWindows().getPointer()); } connectome-workbench-1.2.3+git41-gc4c6c90/src/GuiQt/AnnotationWidthHeightWidget.h000066400000000000000000000047041300200146000273620ustar00rootroot00000000000000#ifndef __ANNOTATION_WIDTH_HEIGHT_WIDGET_H__ #define __ANNOTATION_WIDTH_HEIGHT_WIDGET_H__ /*LICENSE_START*/ /* * Copyright (C) 2015 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 "AnnotationWidgetParentEnum.h" class QDoubleSpinBox; namespace caret { class AnnotationTwoDimensionalShape; class AnnotationWidthHeightWidget : public QWidget { Q_OBJECT public: AnnotationWidthHeightWidget(const AnnotationWidgetParentEnum::Enum parentWidgetType, const int32_t browserWindowIndex, QWidget* parent = 0); virtual ~AnnotationWidthHeightWidget(); // ADD_NEW_METHODS_HERE void updateContent(std::vector& annotations2D); private slots: void widthValueChanged(double value); void heightValueChanged(double value); private: AnnotationWidthHeightWidget(const AnnotationWidthHeightWidget&); AnnotationWidthHeightWidget& operator=(const AnnotationWidthHeightWidget&); // ADD_NEW_MEMBERS_HERE const AnnotationWidgetParentEnum::Enum m_parentWidgetType; const int32_t m_browserWindowIndex; QDoubleSpinBox* m_widthSpinBox; QDoubleSpinBox* m_heightSpinBox; std::vector m_annotations2D; }; #ifdef __ANNOTATION_WIDTH_HEIGHT_WIDGET_DECLARE__ // #endif // __ANNOTATION_WIDTH_HEIGHT_WIDGET_DECLARE__ } // namespace #endif //__ANNOTATION_WIDTH_HEIGHT_WIDGET_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/GuiQt/BalsaDatabaseManager.cxx000066400000000000000000000541471300200146000262760ustar00rootroot00000000000000 /*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 __BALSA_DATABASE_MANAGER_DECLARE__ #include "BalsaDatabaseManager.h" #undef __BALSA_DATABASE_MANAGER_DECLARE__ #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 "OperationZipSceneFile.h" #include "ProgramParameters.h" #include "SceneFile.h" using namespace caret; /** * \class caret::BalsaDatabaseManager * \brief Manages connection with BALSA Database. * \ingroup GuiQt */ /** * Constructor. */ BalsaDatabaseManager::BalsaDatabaseManager() : CaretObject() { m_debugFlag = false; // EventManager::get()->addEventListener(this, EventTypeEnum::); } /** * Destructor. */ BalsaDatabaseManager::~BalsaDatabaseManager() { EventManager::get()->removeAllEventsFromListener(this); } /** * Get a description of this object's content. * @return String describing this object's content. */ AString BalsaDatabaseManager::toString() const { return "BalsaDatabaseManager"; } /** * Login to the BALSA Database. * * @param loginURL * URL for logging in. * @param username * Name for login. * @param password * Password for login. * @param errorMessageOut * Contains error information if login failed. * @return * True if login is successful, else false. */ bool BalsaDatabaseManager::login(const AString& loginURL, const AString& username, const AString& password, AString& errorMessageOut) { errorMessageOut.clear(); m_username = username.trimmed(); m_password = password.trimmed(); m_jSessionIdCookie = ""; CaretHttpRequest loginRequest; loginRequest.m_method = CaretHttpManager::POST_ARGUMENTS; loginRequest.m_url = loginURL; loginRequest.m_arguments.push_back(std::make_pair("j_username", m_username)); loginRequest.m_arguments.push_back(std::make_pair("j_password", m_password)); CaretHttpResponse loginResponse; CaretHttpManager::httpRequest(loginRequest, loginResponse); if (m_debugFlag) { std::cout << "Response Code: " << loginResponse.m_responseCode << std::endl; } for (std::map::iterator mapIter = loginResponse.m_headers.begin(); mapIter != loginResponse.m_headers.end(); mapIter++) { if (mapIter->first == "Set-Cookie") { const AString value = mapIter->second; const int equalPos = value.indexOf("="); const int semiPos = value.indexOf(";"); if ((equalPos >= 0) && (semiPos > (equalPos + 1))) { const int offset = equalPos + 1; const int length = semiPos - offset; m_jSessionIdCookie = value.mid(offset, length); } } if (m_debugFlag) { std::cout << " Response Header: " << qPrintable(mapIter->first) << " -> " << qPrintable(mapIter->second) << std::endl; } } if (m_debugFlag) { std::cout << "SessionID: " << qPrintable(m_jSessionIdCookie) << std::endl; } if (loginResponse.m_responseCode == 200) { if (m_jSessionIdCookie.isEmpty()) { errorMessageOut = ("Login was successful but BALSA failed to provide a Session ID."); return false; } return true; } 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); return false; } /** * Upload file to the BALSA Database. * * @param uploadURL * URL for uploading file. * @param fileName * Name of 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 errorMessageOut * Contains error information if upload failed. * @return * True if upload is successful, else false. */ bool BalsaDatabaseManager::uploadFile(const AString& uploadURL, const AString& fileName, const AString& httpContentTypeName, AString& responseContentOut, AString& errorMessageOut) { responseContentOut.clear(); errorMessageOut.clear(); const bool successFlag = uploadFileWithCaretHttpManager(uploadURL, fileName, httpContentTypeName, responseContentOut, errorMessageOut); return successFlag; } /** * Upload file to the BALSA Database. * * @param uploadURL * URL for uploading file. * @param fileName * Name of 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 errorMessageOut * Contains error information if upload failed. * @return * True if upload is successful, else false. */ bool BalsaDatabaseManager::uploadFileWithCaretHttpManager(const AString& uploadURL, const AString& fileName, const AString& httpContentTypeName, AString& responseContentOut, AString& errorMessageOut) { responseContentOut.clear(); errorMessageOut.clear(); if (httpContentTypeName.isEmpty()) { errorMessageOut = ("Content Type Name is empty. " "See http://www.freeformatter.com/mime-types-list.html for examples."); return false; } FileInformation fileInfo(fileName); if ( ! fileInfo.exists()) { errorMessageOut = (fileName + " does not exist."); return false; } const int64_t fileSize = fileInfo.size(); if (fileSize <= 0) { errorMessageOut = (fileName + " does not contain any data (size=0)"); return false; } /* * Upload file name must be name of file without path */ const AString uploadFileName(fileInfo.getFileName()); const AString fileSizeString(AString::number(fileSize)); CaretHttpRequest uploadRequest; uploadRequest.m_method = CaretHttpManager::POST_FILE; uploadRequest.m_url = uploadURL; uploadRequest.m_uploadFileName = fileName; uploadRequest.m_headers.insert(std::make_pair("Content-Type", httpContentTypeName)); uploadRequest.m_headers.insert(std::make_pair("Cookie", getJSessionIdCookie())); uploadRequest.m_headers.insert(std::make_pair("X-File-Name", uploadFileName)); uploadRequest.m_headers.insert(std::make_pair("X-File-Size", fileSizeString)); CaretHttpResponse uploadResponse; CaretHttpManager::httpRequest(uploadRequest, uploadResponse); uploadResponse.m_body.push_back('\0'); responseContentOut.append(&uploadResponse.m_body[0]); return processUploadResponse(uploadResponse.m_headers, responseContentOut, uploadResponse.m_responseCode, errorMessageOut); } /** * Process the response from file upload. Content should be JSON. * * @param responseHeaders * Headers from the response. * @param responseContent * Content from the response. * @param responseHttpCode * HTTP code from response. * @param errorMessageOut * Contains description of error. * @return * True if response shows upload was successful, else false. */ bool BalsaDatabaseManager::processUploadResponse(const std::map& responseHeaders, const AString& responseContent, const int32_t responseHttpCode, AString& errorMessageOut) const { if (responseHeaders.empty()) { if (m_debugFlag) std::cout << "Response headers from upload are empty." << std::endl; } bool haveContentTypeFlag = false; AString contentTypeString; for (std::map::const_iterator iter = responseHeaders.begin(); iter != responseHeaders.end(); iter++) { if (m_debugFlag) std::cout << "Response Header: " << iter->first << " -> " << iter->second << std::endl; if (iter->first == "Content-Type") { haveContentTypeFlag = true; contentTypeString = iter->second; } } AString contentErrorMessage; bool haveJsonResponseContentFlag = false; if (haveContentTypeFlag) { if (contentTypeString.startsWith("application/json;")) { haveJsonResponseContentFlag = true; } else { contentErrorMessage = ("Content-Type received from file upload is not JSON but is " + contentTypeString + "\n"); } } else { contentErrorMessage = "No Content-Type header received from file upload.\n"; } CaretJsonObject json(responseContent); if (responseHttpCode != 200) { AString msg = json.value("statusText"); if ( ! msg.isEmpty()) { contentErrorMessage.insert(0, msg + "\n"); } errorMessageOut = ("Upload failed. Http Code=" + AString::number(responseHttpCode) + "\n" " " + contentErrorMessage); return false; } if ( ! haveJsonResponseContentFlag) { errorMessageOut = (contentErrorMessage); return false; } return true; } /** * Zip a scene file and its data files. * * @param sceneFile * Scene file that is zipped. * @param extractDirectory * Directory into which files are extracted. * @param zipFileName * Name for the ZIP file. * @param errorMessageOut * Contains error information if zipping failed. * @return * True if zipping is successful, else false. */ bool BalsaDatabaseManager::zipSceneAndDataFiles(const SceneFile* sceneFile, const AString& extractDirectory, const AString& zipFileName, AString& errorMessageOut) { errorMessageOut.clear(); if (sceneFile == NULL) { errorMessageOut = "Scene file is invalid."; return false; } const QString sceneFileName = sceneFile->getFileName(); if (sceneFileName.isEmpty()) { errorMessageOut = "Scene File does not have a name."; return false; } const AString extractToDirectoryName = extractDirectory; if (extractToDirectoryName.isEmpty()) { errorMessageOut = "Extract to directory is empty."; return false; } if (zipFileName.isEmpty()) { errorMessageOut = "Zip File name is empty"; return false; } AString baseDirectoryName; if ( ! sceneFile->getBaseDirectory().isEmpty()) { /* validate ? */ baseDirectoryName = sceneFile->getBaseDirectory(); } bool successFlag = false; try { OperationZipSceneFile::createZipFile(sceneFileName, extractToDirectoryName, zipFileName, baseDirectoryName, OperationZipSceneFile::PROGRESS_GUI_EVENT, NULL); successFlag = true; } catch (const CaretException& e) { errorMessageOut = e.whatString(); } return successFlag; } /** * @return The JSESSIONID Cookie value (empty if not valid). */ AString BalsaDatabaseManager::getJSessionIdCookie() const { return m_jSessionIdCookie; } /** * Receive an event. * * @param event * An event for which this instance is listening. */ void BalsaDatabaseManager::receiveEvent(Event* /*event*/) { // if (event->getEventType() == EventTypeEnum::) { // eventName = dynamic_cast(event); // CaretAssert(eventName); // // event->setEventProcessed(); // } } /** * Request BALSA database process the 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 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) { responseContentOut.clear(); errorMessageOut.clear(); if (httpContentTypeName.isEmpty()) { errorMessageOut = ("Content Type Name is empty. " "See http://www.freeformatter.com/mime-types-list.html for examples."); return false; } CaretHttpRequest uploadRequest; uploadRequest.m_method = CaretHttpManager::POST_ARGUMENTS; uploadRequest.m_url = processUploadURL; uploadRequest.m_headers.insert(std::make_pair("Content-Type", httpContentTypeName)); uploadRequest.m_headers.insert(std::make_pair("Cookie", getJSessionIdCookie())); CaretHttpResponse uploadResponse; CaretHttpManager::httpRequest(uploadRequest, uploadResponse); if (m_debugFlag) { 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 (m_debugFlag) { std::cout << " Process Upload Response Header: " << qPrintable(mapIter->first) << " -> " << qPrintable(mapIter->second) << std::endl; } } uploadResponse.m_body.push_back('\0'); responseContentOut.append(&uploadResponse.m_body[0]); CaretLogFine("Process Upload to BALSA reply body: " + responseContentOut); if (uploadResponse.m_responseCode == 200) { return true; } errorMessageOut = ("Process Upload failed code: " + QString::number(uploadResponse.m_responseCode) + "\n" + responseContentOut); return false; } /** * 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 * Name for ZIP file. * @param extractToDirectoryName * Directory for extraction of ZIP file. * @param errorMessageOut * Contains description of any error(s). * @return * 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) { errorMessageOut.clear(); /* * Check for errors */ if (sceneFile == NULL) { errorMessageOut = "Scene file is invalid."; return false; } const AString sceneFileName = sceneFile->getFileName(); if (sceneFileName.isEmpty()) { errorMessageOut = "Scene file does not have a name."; return false; } if (sceneFile->getNumberOfScenes() <= 0) { errorMessageOut = "Scene file does not contain any scenes."; return false; } if (zipFileName.isEmpty()) { errorMessageOut = "Zip file does not have a name."; return false; } if ( ! zipFileName.endsWith(".zip")) { errorMessageOut = "Zip file name must end with \".zip\""; return false; } if (extractToDirectoryName.isEmpty()) { errorMessageOut = "The extract directory name is empty."; return false; } enum ProgressEnum { PROGRESS_NONE, PROGRESS_LOGIN, PROGRESS_ZIPPING, PROGRESS_UPLOAD, PROGRESS_PROCESS_UPLOAD, PROGRESS_DONE }; EventProgressUpdate progressUpdate(PROGRESS_NONE, PROGRESS_DONE, PROGRESS_LOGIN, "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()); /* * Zip the scene file and its data files */ if ( ! zipSceneAndDataFiles(sceneFile, extractToDirectoryName, zipFileName, errorMessageOut)) { return false; } if (m_debugFlag) std::cout << "Zip file has been created " << std::endl; progressUpdate.setProgress(PROGRESS_UPLOAD, "Uploading zip file"); EventManager::get()->sendEvent(progressUpdate.getPointer()); /* * Upload the ZIP file */ AString uploadResultText; const AString uploadURL(databaseURL + "/study/handleUpload/" + sceneFile->getBalsaStudyID()); const bool uploadSuccessFlag = uploadFile(uploadURL, zipFileName, "application/zip", uploadResultText, errorMessageOut); if (m_debugFlag) std::cout << "Output of uploading zip file: " << uploadResultText << std::endl; if ( ! uploadSuccessFlag) { return false; } const bool doProcessUploadFlag = true; if (doProcessUploadFlag) { /* * Process the uploaded file */ progressUpdate.setProgress(PROGRESS_PROCESS_UPLOAD, "Processing uploaded zip file"); EventManager::get()->sendEvent(progressUpdate.getPointer()); const AString processUploadURL(databaseURL + "/study/processUpload/" + sceneFile->getBalsaStudyID()); AString processUploadResultText; const bool processUploadSuccessFlag = processUploadedFile(processUploadURL, "application/x-www-form-urlencoded", processUploadResultText, errorMessageOut); if (m_debugFlag) std::cout << "Result of processing the uploaded ZIP file" << AString::fromBool(processUploadSuccessFlag) << std::endl; if ( ! processUploadSuccessFlag) { return false; } } else { CaretLogSevere("PROCESSING OF FILE UPLOADED TO BALSA IS DISABLED"); } if (QFile::exists(zipFileName)) { if ( ! QFile::remove(zipFileName)) { CaretLogWarning("Unable to delete Zip file after uploading: " + zipFileName); } } progressUpdate.setProgress(PROGRESS_DONE, "Finished."); EventManager::get()->sendEvent(progressUpdate.getPointer()); return true; } connectome-workbench-1.2.3+git41-gc4c6c90/src/GuiQt/BalsaDatabaseManager.h000066400000000000000000000102351300200146000257110ustar00rootroot00000000000000#ifndef __BALSA_DATABASE_MANAGER_H__ #define __BALSA_DATABASE_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 "CaretObject.h" #include "EventListenerInterface.h" namespace caret { class SceneFile; class BalsaDatabaseManager : public CaretObject, public EventListenerInterface { public: BalsaDatabaseManager(); virtual ~BalsaDatabaseManager(); bool uploadZippedSceneFile(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; bool uploadFile(const AString& uploadURL, const AString& fileName, const AString& httpContentTypeName, AString& responseContentOut, AString& errorMessageOut); bool processUploadedFile(const AString& processUploadURL, const AString& httpContentTypeName, AString& responseContentOut, AString& errorMessageOut); static bool zipSceneAndDataFiles(const SceneFile* sceneFile, const AString& extractDirectory, const AString& zipFileName, AString& errorMessageOut); // ADD_NEW_METHODS_HERE virtual AString toString() const; virtual void receiveEvent(Event* event); private: BalsaDatabaseManager(const BalsaDatabaseManager&); BalsaDatabaseManager& operator=(const BalsaDatabaseManager&); bool uploadFileWithCaretHttpManager(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 processUploadResponse(const std::map& responseHeaders, const AString& responseContent, const int32_t responseHttpCode, AString& errorMessageOut) const; AString m_username; AString m_password; AString m_jSessionIdCookie; bool m_debugFlag; // ADD_NEW_MEMBERS_HERE }; #ifdef __BALSA_DATABASE_MANAGER_DECLARE__ // #endif // __BALSA_DATABASE_MANAGER_DECLARE__ } // namespace #endif //__BALSA_DATABASE_MANAGER_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/GuiQt/BalsaDatabaseUploadSceneFileDialog.cxx000066400000000000000000000211611300200146000310340ustar00rootroot00000000000000 /*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 __BALSA_DATABASE_UPLOAD_SCENE_FILE_DIALOG_DECLARE__ #include "BalsaDatabaseUploadSceneFileDialog.h" #undef __BALSA_DATABASE_UPLOAD_SCENE_FILE_DIALOG_DECLARE__ #include #include #include #include #include #include "BalsaDatabaseManager.h" #include "CaretAssert.h" #include "CaretLogger.h" #include "CaretPointer.h" #include "CursorDisplayScoped.h" #include "FileInformation.h" #include "ProgressReportingDialog.h" #include "SceneFile.h" #include "SystemUtilities.h" #include "WuQMessageBox.h" #include "WuQtUtilities.h" using namespace caret; /** * \class caret::BalsaDatabaseUploadSceneFileDialog * \brief Dialog for uploading a scene file to the BALSA Database. * \ingroup GuiQt */ /** * Constructor. * * @param sceneFile * Scene file that will be uploaded. * @param parent * Parent of this dialog. */ BalsaDatabaseUploadSceneFileDialog::BalsaDatabaseUploadSceneFileDialog(const SceneFile* sceneFile, QWidget* parent) : WuQDialogModal("Upload Scene File to BALSA", parent), m_sceneFile(sceneFile) { QString defaultUserName = "balsaTest"; QString defaultPassword = "@2password"; #ifdef NDEBUG defaultUserName = ""; defaultPassword = ""; #endif 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"); const int minimumLineEditWidth = 250; QLabel* usernameLabel = new QLabel("Username: "); m_usernameLineEdit = new QLineEdit(); m_usernameLineEdit->setMinimumWidth(minimumLineEditWidth); m_usernameLineEdit->setText(defaultUserName); QLabel* passwordLabel = new QLabel("Password: "); m_passwordLineEdit = new QLineEdit(); m_passwordLineEdit->setMinimumWidth(minimumLineEditWidth); m_passwordLineEdit->setEchoMode(QLineEdit::Password); m_passwordLineEdit->setText(defaultPassword); QLabel* forgotUsernameLabel = new QLabel("" "Forgot Username" ""); QObject::connect(forgotUsernameLabel, SIGNAL(linkActivated(const QString&)), this, SLOT(labelHtmlLinkClicked(const QString&))); QLabel* forgotPasswordLabel = new QLabel("" "Forgot Password" ""); QObject::connect(forgotPasswordLabel, SIGNAL(linkActivated(const QString&)), this, SLOT(labelHtmlLinkClicked(const QString&))); QLabel* registerLabel = new QLabel("" "Register" ""); QObject::connect(registerLabel, SIGNAL(linkActivated(const QString&)), this, SLOT(labelHtmlLinkClicked(const QString&))); const QString zipFileName = FileInformation::assembleFileComponents(SystemUtilities::getTempDirectory(), sceneFile->getFileNameNoPathNoExtension() , "zip"); QLabel* zipFileNameLabel = new QLabel("Zip File Name"); m_zipFileNameLineEdit = new QLineEdit; m_zipFileNameLineEdit->setText(zipFileName); QLabel* extractDirectoryLabel = new QLabel("Extract to Directory"); m_extractDirectoryNameLineEdit = new QLineEdit(); m_extractDirectoryNameLineEdit->setText("ExtDir"); 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); int row = 0; gridLayout->addWidget(databaseNameLabel, row, 0); gridLayout->addWidget(m_databaseComboBox, row, 1); gridLayout->addWidget(registerLabel, row, 2); row++; gridLayout->addWidget(usernameLabel, row, 0); gridLayout->addWidget(m_usernameLineEdit, row, 1); gridLayout->addWidget(forgotUsernameLabel, row, 2); row++; gridLayout->addWidget(passwordLabel, row, 0); gridLayout->addWidget(m_passwordLineEdit, row, 1); gridLayout->addWidget(forgotPasswordLabel, row, 2); row++; gridLayout->addWidget(zipFileNameLabel, row, 0); gridLayout->addWidget(m_zipFileNameLineEdit, row, 1, 1, 2); row++; gridLayout->addWidget(extractDirectoryLabel, row, 0); gridLayout->addWidget(m_extractDirectoryNameLineEdit, row, 1, 1, 2); row++; setCentralWidget(dialogWidget, WuQDialogModal::SCROLL_AREA_NEVER); } /** * Destructor. */ BalsaDatabaseUploadSceneFileDialog::~BalsaDatabaseUploadSceneFileDialog() { } /** * @return Name of selected database. */ AString BalsaDatabaseUploadSceneFileDialog::getDataBaseURL() const { return m_databaseComboBox->currentText().trimmed(); } /** * Gets called when the user clicks a link in the forgot username * or password label. * * @param linkPath * Path relative to database. */ void BalsaDatabaseUploadSceneFileDialog::labelHtmlLinkClicked(const QString& linkPath) { if (linkPath.isEmpty() == false) { const QString dbURL = getDataBaseURL(); const AString linkURL = (getDataBaseURL() + (linkPath.startsWith("/") ? "" : "/") + linkPath); QDesktopServices::openUrl(QUrl(linkURL)); } } /** * Gets called when the OK button is clicked. */ void BalsaDatabaseUploadSceneFileDialog::okButtonClicked() { CursorDisplayScoped cursor; cursor.showWaitCursor(); ProgressReportingDialog progressDialog("Upload Scene File to BALSA", "", this); 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 errorMessage; CaretPointer balsaDatabaseManager(new BalsaDatabaseManager()); const bool successFlag = balsaDatabaseManager->uploadZippedSceneFile(getDataBaseURL(), username, password, m_sceneFile, zipFileName, extractToDirectoryName, errorMessage); cursor.restoreCursor(); progressDialog.setValue(progressDialog.maximum()); if (successFlag) { WuQMessageBox::informationOk(this, "Upload was successful"); } else { WuQMessageBox::errorOk(this, errorMessage); return; } WuQDialogModal::okButtonClicked(); } connectome-workbench-1.2.3+git41-gc4c6c90/src/GuiQt/BalsaDatabaseUploadSceneFileDialog.h000066400000000000000000000045441300200146000304670ustar00rootroot00000000000000#ifndef __BALSA_DATABASE_UPLOAD_SCENE_FILE_DIALOG_H__ #define __BALSA_DATABASE_UPLOAD_SCENE_FILE_DIALOG_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 "WuQDialogModal.h" class QComboBox; class QLabel; class QLineEdit; namespace caret { class SceneFile; class BalsaDatabaseUploadSceneFileDialog : public WuQDialogModal { Q_OBJECT public: BalsaDatabaseUploadSceneFileDialog(const SceneFile* sceneFile, QWidget* parent); virtual ~BalsaDatabaseUploadSceneFileDialog(); // ADD_NEW_METHODS_HERE private slots: void labelHtmlLinkClicked(const QString&); protected: virtual void okButtonClicked(); private: BalsaDatabaseUploadSceneFileDialog(const BalsaDatabaseUploadSceneFileDialog&); BalsaDatabaseUploadSceneFileDialog& operator=(const BalsaDatabaseUploadSceneFileDialog&); AString getDataBaseURL() const; const SceneFile* m_sceneFile; QComboBox* m_databaseComboBox; QLineEdit* m_usernameLineEdit; QLineEdit* m_passwordLineEdit; QLineEdit* m_zipFileNameLineEdit; QLineEdit* m_extractDirectoryNameLineEdit; // ADD_NEW_MEMBERS_HERE }; #ifdef __BALSA_DATABASE_UPLOAD_SCENE_FILE_DIALOG_DECLARE__ // #endif // __BALSA_DATABASE_UPLOAD_SCENE_FILE_DIALOG_DECLARE__ } // namespace #endif //__BALSA_DATABASE_UPLOAD_SCENE_FILE_DIALOG_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/GuiQt/BorderEditingSelectionDialog.cxx000066400000000000000000000117751300200146000300430ustar00rootroot00000000000000 /*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 __BORDER_EDITING_SELECTION_DIALOG_DECLARE__ #include "BorderEditingSelectionDialog.h" #undef __BORDER_EDITING_SELECTION_DIALOG_DECLARE__ #include #include #include #include #include #include "CaretAssert.h" using namespace caret; /** * \class caret::BorderEditingSelectionDialog * \brief Dialog for selecting border when borders are editing. * \ingroup GuiQt */ /** * Constructor. * * @param modeDescription * Description of the mode. * @param borderNames * Names of the borders for selection. * @param parent * Parent of the dialog. */ BorderEditingSelectionDialog::BorderEditingSelectionDialog(const AString& modeDescription, const std::vector& borderNames, QWidget* parent) : WuQDialogModal("Border Editing", parent) { AString modeText(modeDescription + ((borderNames.size() > 1) ? " these borders (ordered by distance): " : " this border: ")); const QString toolTipText = ("ERASE AND REPLACE - Distance is the average of:\n" " * Distance from first segment point to nearest point in border.\n" " * Distance from last segment point to nearest point in border.\n" "\n" "EXTEND - Distance is from first segment point to any point\n" "in border.\n"); QLabel* modeLabel = new QLabel(modeText); QPushButton* allOnPushButton = new QPushButton("All On"); QObject::connect(allOnPushButton, SIGNAL(clicked()), this, SLOT(allOnPushButtonClicked())); QPushButton* allOffPushButton = new QPushButton("All Off"); QObject::connect(allOffPushButton, SIGNAL(clicked()), this, SLOT(allOffPushButtonClicked())); QWidget* allOnOffWidget = new QWidget(); QHBoxLayout* allOnOffLayout = new QHBoxLayout(allOnOffWidget); allOnOffLayout->addWidget(allOnPushButton); allOnOffLayout->addStrut(10); allOnOffLayout->addWidget(allOffPushButton); allOnOffLayout->addStretch(); QWidget* widget = new QWidget; QVBoxLayout* layout = new QVBoxLayout(widget); layout->addWidget(modeLabel); for (std::vector::const_iterator iter = borderNames.begin(); iter != borderNames.end(); iter++) { QCheckBox* cb = new QCheckBox(*iter); cb->setChecked(true); cb->setToolTip(toolTipText); m_borderNameCheckBoxes.push_back(cb); layout->addWidget(cb); } setTopBottomAndCentralWidgets(allOnOffWidget, widget, NULL, WuQDialog::SCROLL_AREA_AS_NEEDED); disableAutoDefaultForAllPushButtons(); } /** * Destructor. */ BorderEditingSelectionDialog::~BorderEditingSelectionDialog() { } /** * Called when ALL ON push button is clicked. */ void BorderEditingSelectionDialog::allOnPushButtonClicked() { setAllCheckBoxesChecked(true); } /** * Called when ALL OFF push button is clicked. */ void BorderEditingSelectionDialog::allOffPushButtonClicked() { setAllCheckBoxesChecked(false); } /** * Set all checkbox check status. * * @param status * New checked status. */ void BorderEditingSelectionDialog::setAllCheckBoxesChecked(const bool status) { for (std::vector::iterator iter = m_borderNameCheckBoxes.begin(); iter != m_borderNameCheckBoxes.end(); iter++) { QCheckBox* cb = *iter; cb->setChecked(status); } } /** * Is the border name with the given index selected ? * * @param borderNameIndex * Index of the border name. * @return * Selection status. */ bool BorderEditingSelectionDialog::isBorderNameSelected(const int32_t borderNameIndex) const { CaretAssertVectorIndex(m_borderNameCheckBoxes, borderNameIndex); return m_borderNameCheckBoxes[borderNameIndex]->isChecked(); } connectome-workbench-1.2.3+git41-gc4c6c90/src/GuiQt/BorderEditingSelectionDialog.h000066400000000000000000000042151300200146000274570ustar00rootroot00000000000000#ifndef __BORDER_EDITING_SELECTION_DIALOG_H__ #define __BORDER_EDITING_SELECTION_DIALOG_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 "WuQDialogModal.h" class QCheckBox; namespace caret { class BorderEditingSelectionDialog : public WuQDialogModal { Q_OBJECT public: BorderEditingSelectionDialog(const AString& modeDescription, const std::vector& borderNames, QWidget* parent); virtual ~BorderEditingSelectionDialog(); // ADD_NEW_METHODS_HERE bool isBorderNameSelected(const int32_t borderNameIndex) const; private slots: void allOnPushButtonClicked(); void allOffPushButtonClicked(); private: BorderEditingSelectionDialog(const BorderEditingSelectionDialog&); BorderEditingSelectionDialog& operator=(const BorderEditingSelectionDialog&); void setAllCheckBoxesChecked(const bool status); std::vector m_borderNameCheckBoxes; // ADD_NEW_MEMBERS_HERE }; #ifdef __BORDER_EDITING_SELECTION_DIALOG_DECLARE__ // #endif // __BORDER_EDITING_SELECTION_DIALOG_DECLARE__ } // namespace #endif //__BORDER_EDITING_SELECTION_DIALOG_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/GuiQt/BorderFileSplitDialog.cxx000066400000000000000000000256111300200146000264770ustar00rootroot00000000000000 /*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 __BORDER_FILE_SPLIT_DIALOG_DECLARE__ #include "BorderFileSplitDialog.h" #undef __BORDER_FILE_SPLIT_DIALOG_DECLARE__ #include #include #include #include #include #include #include "BorderFile.h" #include "Brain.h" #include "BrainStructure.h" #include "CaretAssert.h" #include "CaretDataFileSelectionComboBox.h" #include "CaretDataFileSelectionModel.h" #include "CaretFileDialog.h" #include "EventDataFileAdd.h" #include "EventManager.h" #include "EventUserInterfaceUpdate.h" #include "FileInformation.h" #include "GuiManager.h" #include "WuQMessageBox.h" #include "WuQtUtilities.h" using namespace caret; /** * \class caret::BorderFileSplitDialog * \brief Dialog for splitting multi-structure border file. * \ingroup GuiQt * * Some older border files contained borders for multiple structures * which will not work with wb_command operations. This dialog is * used to split up a multi-structure border file into multiple border * files each containing a single structure. */ /** * Constructor. */ BorderFileSplitDialog::BorderFileSplitDialog(QWidget* parent) : WuQDialogModal("Split Multi-Structure Border File", parent) { m_dialogIsBeingCreatedFlag = true; m_fileNameToolButtonSignalMapper = new QSignalMapper(this); QObject::connect(m_fileNameToolButtonSignalMapper, SIGNAL(mapped(int)), this, SLOT(fileNameToolButtonClicked(int))); Brain* brain = GuiManager::get()->getBrain(); m_fileSelectionModel.grabNew(CaretDataFileSelectionModel::newInstanceForMultiStructureBorderFiles(brain)); QLabel* fileSelectionLabel = new QLabel("Multi Structure Border File: "); m_fileSelectionComboBox = new CaretDataFileSelectionComboBox(this); m_fileSelectionComboBox->updateComboBox(m_fileSelectionModel); QObject::connect(m_fileSelectionComboBox, SIGNAL(fileSelected(CaretDataFile*)), this, SLOT(borderMultiStructureFileSelected(CaretDataFile*))); QHBoxLayout* multiLayout = new QHBoxLayout(); multiLayout->addWidget(fileSelectionLabel); multiLayout->addWidget(m_fileSelectionComboBox->getWidget()); multiLayout->addStretch(); m_gridLayout = new QGridLayout(); WuQtUtilities::setLayoutSpacingAndMargins(m_gridLayout, 3, 3); m_gridLayout->setColumnStretch(0, 0); m_gridLayout->setColumnStretch(1, 100); int rowIndex = 0; m_gridLayout->addWidget(new QLabel("Structures"), rowIndex, 0, Qt::AlignHCenter); m_gridLayout->addWidget(new QLabel("New Single-Structure Border Files"), rowIndex, 1, Qt::AlignHCenter); QWidget* widget = new QWidget(); QVBoxLayout* layout = new QVBoxLayout(widget); layout->addLayout(multiLayout); layout->addWidget(WuQtUtilities::createHorizontalLineWidget()); layout->addLayout(m_gridLayout); layout->addStretch(); setCentralWidget(widget, WuQDialog::SCROLL_AREA_AS_NEEDED); m_dialogIsBeingCreatedFlag = false; CaretDataFile* firstFile = m_fileSelectionModel->getSelectedFile(); if (firstFile != NULL) { borderMultiStructureFileSelected(firstFile); } } /** * Destructor. */ BorderFileSplitDialog::~BorderFileSplitDialog() { } /** * Called when a multi-structure border file is selected. */ void BorderFileSplitDialog::borderMultiStructureFileSelected(CaretDataFile* caretDataFile) { if (m_dialogIsBeingCreatedFlag) { return; } AString pathName; AString fileNameNoExt; AString fileExtension; std::vector structures; if (caretDataFile != NULL) { BorderFile* borderFile = dynamic_cast(caretDataFile); CaretAssert(borderFile); structures = borderFile->getAllBorderStructures(); FileInformation fileInfo(caretDataFile->getFileName()); fileInfo.getFileComponents(pathName, fileNameNoExt, fileExtension); } const int32_t numStructures = static_cast(structures.size()); for (int32_t i = 0; i < numStructures; i++) { QPushButton* pushButton = NULL; QLineEdit* lineEdit = NULL; if (i < static_cast(m_structureRows.size())) { CaretAssertVectorIndex(m_structureRows, i); pushButton = m_structureRows[i].m_fileNamePushButton; lineEdit = m_structureRows[i].m_fileNameLineEdit; if (m_structureRows[i].m_fileNamePushButton->isHidden()) { pushButton->setVisible(true); lineEdit->setVisible(true); } } else { const int MINIMUM_LINE_EDIT_WIDTH = 400; pushButton = new QPushButton(); QObject::connect(pushButton, SIGNAL(clicked()), m_fileNameToolButtonSignalMapper, SLOT(map())); m_fileNameToolButtonSignalMapper->setMapping(pushButton, i); lineEdit = new QLineEdit(); lineEdit->setMinimumWidth(MINIMUM_LINE_EDIT_WIDTH); const int rowIndex = m_gridLayout->rowCount(); m_gridLayout->addWidget(pushButton, rowIndex, 0); m_gridLayout->addWidget(lineEdit, rowIndex, 1); StructureRow structureRow; structureRow.m_fileNamePushButton = pushButton; structureRow.m_fileNameLineEdit = lineEdit; m_structureRows.push_back(structureRow); } CaretAssertVectorIndex(m_structureRows, i); CaretAssert(pushButton); CaretAssert(lineEdit); CaretAssertVectorIndex(structures, i); m_structureRows[i].m_structure = structures[i]; pushButton->setText(StructureEnum::toGuiName(structures[i]) + "..."); AString singleStructureFileName; if ( ! pathName.isEmpty()) { singleStructureFileName += (pathName + "/"); } singleStructureFileName += fileNameNoExt; singleStructureFileName += ("_" + StructureEnum::toGuiName(structures[i])); singleStructureFileName += ("." + fileExtension); lineEdit->setText(singleStructureFileName); lineEdit->end(false); } const int32_t numRows = static_cast(m_structureRows.size()); for (int32_t i = numStructures; i < numRows; i++) { m_structureRows[i].m_fileNamePushButton->hide(); m_structureRows[i].m_fileNameLineEdit->hide(); } } /** * Called when a file name tool button is clicked. Allows user to set * name of new border file with file selection dialog. * * @param buttonIndex * Index of button that was clicked. */ void BorderFileSplitDialog::fileNameToolButtonClicked(int buttonIndex) { CaretAssertVectorIndex(m_structureRows, buttonIndex); QLineEdit* lineEdit = m_structureRows[buttonIndex].m_fileNameLineEdit; AString newBorderFileName = CaretFileDialog::getSaveFileNameDialog(DataFileTypeEnum::BORDER, this, "Choose Border File Name", lineEdit->text().trimmed()); if ( ! newBorderFileName.isEmpty()) { lineEdit->setText(newBorderFileName); lineEdit->end(false); } } /** * Called when the OK button is clicked. */ void BorderFileSplitDialog::okButtonClicked() { const BorderFile* borderFile = m_fileSelectionComboBox->getSelectionModel()->getSelectedFileOfType(); if (borderFile == NULL) { WuQMessageBox::errorOk(this, "No border file is selected."); return; } std::map singleStructureFileNames; const int32_t numSingleStructureFiles = static_cast(m_structureRows.size()); for (int32_t i = 0; i < numSingleStructureFiles; i++) { if (m_structureRows[i].m_fileNameLineEdit->isVisible()) { singleStructureFileNames.insert(std::make_pair(m_structureRows[i].m_structure, m_structureRows[i].m_fileNameLineEdit->text().trimmed())); } } if (singleStructureFileNames.empty()) { WuQMessageBox::errorOk(this, "Border file contains no structures."); return; } std::map structureNumberOfNodes; Brain* brain = GuiManager::get()->getBrain(); const int32_t numStructures = brain->getNumberOfBrainStructures(); for (int32_t iStruct = 0; iStruct < numStructures; iStruct++) { const BrainStructure* bs = brain->getBrainStructure(iStruct); structureNumberOfNodes.insert(std::make_pair(bs->getStructure(), bs->getNumberOfNodes())); } std::vector singleStructureBorderFiles; AString errorMessage; if (borderFile->splitIntoSingleStructureFiles(singleStructureFileNames, structureNumberOfNodes, singleStructureBorderFiles, errorMessage)) { for (std::vector::iterator iter = singleStructureBorderFiles.begin(); iter != singleStructureBorderFiles.end(); iter++) { EventManager::get()->sendEvent(EventDataFileAdd(*iter).getPointer()); } EventManager::get()->sendEvent(EventUserInterfaceUpdate().getPointer()); WuQMessageBox::informationOk(this, ("New border file(s) were created but not saved. " "Use File Menu->Save/Manage Files to save these new files.")); } else { WuQMessageBox::errorOk(this, errorMessage); return; } WuQDialog::okButtonClicked(); } connectome-workbench-1.2.3+git41-gc4c6c90/src/GuiQt/BorderFileSplitDialog.h000066400000000000000000000052301300200146000261170ustar00rootroot00000000000000#ifndef __BORDER_FILE_SPLIT_DIALOG_H__ #define __BORDER_FILE_SPLIT_DIALOG_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 "CaretPointer.h" #include "StructureEnum.h" #include "WuQDialogModal.h" class QGridLayout; class QLineEdit; class QPushButton; class QSignalMapper; namespace caret { class CaretDataFile; class CaretDataFileSelectionComboBox; class CaretDataFileSelectionModel; class BorderFileSplitDialog : public WuQDialogModal { Q_OBJECT public: BorderFileSplitDialog(QWidget* parent = 0); virtual ~BorderFileSplitDialog(); // ADD_NEW_METHODS_HERE protected: virtual void okButtonClicked(); private slots: void fileNameToolButtonClicked(int); void borderMultiStructureFileSelected(CaretDataFile* caretDataFile); private: BorderFileSplitDialog(const BorderFileSplitDialog&); BorderFileSplitDialog& operator=(const BorderFileSplitDialog&); QGridLayout* m_gridLayout; /* Use CaretPointer so that the file selection model gets destroyed */ CaretPointer m_fileSelectionModel; CaretDataFileSelectionComboBox* m_fileSelectionComboBox; struct StructureRow { QPushButton* m_fileNamePushButton; QLineEdit* m_fileNameLineEdit; StructureEnum::Enum m_structure; }; std::vector m_structureRows; QSignalMapper* m_fileNameToolButtonSignalMapper; bool m_dialogIsBeingCreatedFlag; // ADD_NEW_MEMBERS_HERE }; #ifdef __BORDER_FILE_SPLIT_DIALOG_DECLARE__ // #endif // __BORDER_FILE_SPLIT_DIALOG_DECLARE__ } // namespace #endif //__BORDER_FILE_SPLIT_DIALOG_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/GuiQt/BorderOptimizeDialog.cxx000066400000000000000000001504261300200146000264070ustar00rootroot00000000000000 /*LICENSE_START*/ /* * Copyright (C) 2015 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 __BORDER_OPTIMIZE_DIALOG_DECLARE__ #include "BorderOptimizeDialog.h" #undef __BORDER_OPTIMIZE_DIALOG_DECLARE__ #include #include #include #include #include #include #include #include #include #include #include #include "Border.h" #include "BorderFile.h" #include "Brain.h" #include "BrainStructure.h" #include "BrowserTabContent.h" #include "CaretAssert.h" #include "CaretDataFileSelectionComboBox.h" #include "CaretDataFileSelectionModel.h" #include "CaretFileDialog.h" #include "CaretMappableDataFile.h" #include "CaretMappableDataFileAndMapSelectionModel.h" #include "CaretMappableDataFileAndMapSelectorObject.h" #include "CursorDisplayScoped.h" #include "EventBrowserTabGet.h" #include "EventDataFileAdd.h" #include "EventManager.h" #include "EventSurfaceColoringInvalidate.h" #include "EventUpdateInformationWindows.h" #include "EventUserInterfaceUpdate.h" #include "GuiManager.h" #include "MetricFile.h" #include "OverlaySet.h" #include "ProgressReportingDialog.h" #include "Surface.h" #include "SurfaceSelectionModel.h" #include "SurfaceSelectionViewController.h" #include "WuQMessageBox.h" #include "WuQtUtilities.h" using namespace caret; /** * \class caret::BorderOptimizeDialog * \brief Border Optimize Dialog. * \ingroup GuiQt */ static const int STRETCH_NONE = 0; static const int STRETCH_MAX = 100; static const bool DATA_FILES_IN_SCROLL_BARS = false; /** * Constructor. * * @param parent * Parent of the dialog. */ BorderOptimizeDialog::BorderOptimizeDialog(QWidget* parent) : WuQDialogModal("Border Optimize", parent), m_surface(NULL), m_borderEnclosingROI(NULL), m_borderPairFileSelectionModel(NULL), m_surfaceSelectionStructure(StructureEnum::INVALID), m_surfaceSelectionModel(NULL), m_vertexAreasMetricFileSelectionModel(NULL), m_browserTabIndex(-1), m_upsamplingSurfaceSelectionModel(NULL), m_upsamplingSurfaceStructure(StructureEnum::INVALID) { m_optimizeDataFileTypes.push_back(DataFileTypeEnum::CONNECTIVITY_DENSE); m_optimizeDataFileTypes.push_back(DataFileTypeEnum::CONNECTIVITY_DENSE_SCALAR); m_optimizeDataFileTypes.push_back(DataFileTypeEnum::CONNECTIVITY_DENSE_TIME_SERIES); m_optimizeDataFileTypes.push_back(DataFileTypeEnum::METRIC); QWidget* dialogWidget = new QWidget(); QVBoxLayout* dialogLayout = new QVBoxLayout(dialogWidget); dialogLayout->addWidget(createBorderSelectionWidget(), STRETCH_NONE); if (DATA_FILES_IN_SCROLL_BARS) { dialogLayout->addWidget(createDataFilesWidget(), STRETCH_MAX); } else { dialogLayout->addWidget(createDataFilesWidget(), STRETCH_MAX); } dialogLayout->addWidget(createSurfaceSelectionWidget(), STRETCH_NONE); dialogLayout->addWidget(createVertexAreasMetricWidget(), STRETCH_NONE); dialogLayout->addWidget(createSphericalUpsamplingWidget(), STRETCH_NONE); dialogLayout->addWidget(createSavingWidget(), STRETCH_NONE); QWidget* optionsWidget = createOptionsWidget(); if (optionsWidget != NULL) { dialogLayout->addWidget(optionsWidget, STRETCH_NONE); } if (DATA_FILES_IN_SCROLL_BARS) { dialogLayout->addStretch(); setCentralWidget(dialogWidget, SCROLL_AREA_NEVER); } else { setCentralWidget(dialogWidget, SCROLL_AREA_AS_NEEDED_VERT_NO_HORIZ); } if (DATA_FILES_IN_SCROLL_BARS) { if (m_defaultDataFilesWidgetSize.width() > 300) { QSize defaultSize = sizeHint(); defaultSize.setWidth(m_defaultDataFilesWidgetSize.width() + 100); setSizeOfDialogWhenDisplayed(defaultSize); } } m_dialogWidget = dialogWidget; // if ( ! DATA_FILES_IN_SCROLL_BARS) { // setSizePolicy(sizePolicy().horizontalPolicy(), // QSizePolicy::Fixed); // } } /** * Destructor. */ BorderOptimizeDialog::~BorderOptimizeDialog() { if (m_surfaceSelectionModel != NULL) { delete m_surfaceSelectionModel; } if (m_vertexAreasMetricFileSelectionModel != NULL) { delete m_vertexAreasMetricFileSelectionModel; } if (m_borderPairFileSelectionModel != NULL) { delete m_borderPairFileSelectionModel; } if (m_upsamplingSurfaceSelectionModel != NULL) { delete m_upsamplingSurfaceSelectionModel; } } /** * Update the content of the dialog. * * @param browserTabIndex * Index of browser tab in which borders are drawn. * @param surface * Surface on which borders are drawn. * @param bordersInsideROI * Map where value is border and key is number of border * points inside the ROI * @param borderEnclosingROI * Border that encloses the region of interest * @param nodesInsideROI * Nodes inside the region of interest. */ void BorderOptimizeDialog::updateDialog(const int32_t browserTabIndex, Surface* surface, std::vector >& bordersInsideROI, Border* borderEnclosingROI, std::vector& nodesInsideROI) { CaretAssert(surface); CaretAssert(borderEnclosingROI); m_browserTabIndex = browserTabIndex; m_surface = surface; m_borderEnclosingROI = borderEnclosingROI; m_nodesInsideROI = nodesInsideROI; m_borderPointsInsideROICount.clear(); m_bordersInsideROI.clear(); for (std::vector >::reverse_iterator bi = bordersInsideROI.rbegin(); bi != bordersInsideROI.rend(); bi++) { m_borderPointsInsideROICount.push_back(bi->first); m_bordersInsideROI.push_back(bi->second); } /* * Update surface selection. * Will need to recreate model if the structure has changed. */ const StructureEnum::Enum structure = m_surface->getStructure(); if (m_surfaceSelectionModel != NULL) { if (structure != m_surfaceSelectionStructure) { delete m_surfaceSelectionModel; m_surfaceSelectionModel = NULL; m_surfaceSelectionStructure = StructureEnum::INVALID; } } if (m_surfaceSelectionModel == NULL) { m_surfaceSelectionStructure = structure; std::vector allSurfaceTypes; SurfaceTypeEnum::getAllEnums(allSurfaceTypes); m_surfaceSelectionModel = new SurfaceSelectionModel(m_surfaceSelectionStructure, allSurfaceTypes); BrainStructure* bs = GuiManager::get()->getBrain()->getBrainStructure(m_surfaceSelectionStructure, false); if (bs != NULL) { Surface* surface = bs->getPrimaryAnatomicalSurface(); if (surface != NULL) { m_surfaceSelectionModel->updateModel(); m_surfaceSelectionModel->setSurface(const_cast(surface)); } } } else { m_surfaceSelectionModel->updateModel(); } if (m_surfaceSelectionModel != NULL) { m_surfaceSelectionControl->updateControl(m_surfaceSelectionModel); m_surfaceSelectionControl->getWidget()->setEnabled(true); } else { m_surfaceSelectionControl->getWidget()->setEnabled(false); } Surface* defaultSphericalSurface = NULL; if (m_upsamplingSurfaceSelectionModel != NULL) { defaultSphericalSurface = m_upsamplingSurfaceSelectionModel->getSurface(); if (structure != m_upsamplingSurfaceStructure) { delete m_upsamplingSurfaceSelectionModel; m_upsamplingSurfaceSelectionModel = NULL; m_upsamplingSurfaceStructure = StructureEnum::INVALID; } } if (m_upsamplingSurfaceSelectionModel == NULL) { m_upsamplingSurfaceStructure = structure; std::vector sphericalSurfaceTypes; sphericalSurfaceTypes.push_back(SurfaceTypeEnum::SPHERICAL); m_upsamplingSurfaceSelectionModel = new SurfaceSelectionModel(m_upsamplingSurfaceStructure, sphericalSurfaceTypes); m_upsamplingResolutionSpinBox->setValue(0); } if (defaultSphericalSurface != NULL) { m_upsamplingSurfaceSelectionModel->setSurface(defaultSphericalSurface); } m_upsamplingSurfaceSelectionModel->updateModel(); m_upsamplingSurfaceSelectionControl->updateControl(m_upsamplingSurfaceSelectionModel); if (m_upsamplingResolutionSpinBox->value() <= 0) { Surface* surface = m_upsamplingSurfaceSelectionModel->getSurface(); if (surface != NULL) { m_upsamplingResolutionSpinBox->setValue(surface->getNumberOfNodes() * 4); } } /* * Update metric selection model * Will need to recreate if the structure has changed. */ if (m_vertexAreasMetricFileSelectionModel != NULL) { if (structure != m_vertexAreasMetricFileSelectionModel->getStructure()) { delete m_vertexAreasMetricFileSelectionModel; m_vertexAreasMetricFileSelectionModel = NULL; } } if (m_vertexAreasMetricFileSelectionModel == NULL) { std::vector metricDataTypes; metricDataTypes.push_back(DataFileTypeEnum::METRIC); m_vertexAreasMetricFileSelectionModel = CaretDataFileSelectionModel::newInstanceForCaretDataFileTypesInStructure(GuiManager::get()->getBrain(), m_surfaceSelectionStructure, metricDataTypes); m_vertexAreasMetricFileComboBox->updateComboBox(m_vertexAreasMetricFileSelectionModel); } if (m_vertexAreasMetricFileSelectionModel != NULL) { m_vertexAreasMetricFileComboBox->updateComboBox(m_vertexAreasMetricFileSelectionModel); } /* * Update border file selection but limit to border files that are the same structure * as the surface */ m_borderPairFileSelectionModel->setStructure(structure); m_borderPairFileSelectionComboBox->updateComboBox(m_borderPairFileSelectionModel); QString initialBaseName = ""; /* * Update borders inside ROI selections */ CaretAssert(m_bordersInsideROI.size() == m_borderPointsInsideROICount.size()); const int32_t NUM_ROWS = 2; const int32_t numberOfBorders = static_cast(m_bordersInsideROI.size()); for (int32_t i = 0; i < numberOfBorders; i++) { if (i >= static_cast(m_borderCheckBoxes.size())) { QCheckBox* cb = new QCheckBox(""); m_borderCheckBoxes.push_back(cb); /* * Use two rows and then wrap additional columns on right */ const int32_t row = (i % NUM_ROWS); const int32_t col = (i / NUM_ROWS); m_bordersInsideROIGridLayout->addWidget(cb, row, col); } CaretAssertVectorIndex(m_borderCheckBoxes, i); CaretAssertVectorIndex(m_bordersInsideROI, i); CaretAssertVectorIndex(m_borderPointsInsideROICount, i); if (m_borderCheckBoxes[i]->text() != m_bordersInsideROI[i]->getName()) { m_borderCheckBoxes[i]->blockSignals(true); m_borderCheckBoxes[i]->setChecked(true); m_borderCheckBoxes[i]->blockSignals(false); } // m_borderCheckBoxes[i]->setText(m_bordersInsideROI[i]->getName()); m_borderCheckBoxes[i]->setText(m_bordersInsideROI[i]->getName() + " (" + QString::number(m_borderPointsInsideROICount[i]) + ")"); m_borderCheckBoxes[i]->setVisible(true); if (i < 2) { if (initialBaseName != "") initialBaseName += "_"; initialBaseName += m_bordersInsideROI[i]->getName(); } } m_savingBaseNameLineEdit->setText(initialBaseName); /* * Remove border selections not needed. */ for (int32_t i = numberOfBorders; i < static_cast(m_borderCheckBoxes.size()); i++) { m_borderCheckBoxes[i]->setVisible(false); } /* * Update the data file selectors. */ for (std::vector::iterator fileIter = m_optimizeDataFileSelectors.begin(); fileIter != m_optimizeDataFileSelectors.end(); fileIter++) { BorderOptimizeDataFileSelector* selector = *fileIter; selector->updateFileData(); } m_dialogWidget->adjustSize(); } /** * Called when OK button is clicked. */ void BorderOptimizeDialog::okButtonClicked() { preserveDialogSizeAndPositionWhenReOpened(); AString errorMessage; m_selectedBorders.clear(); Surface* gradientComputationSurface = m_surfaceSelectionModel->getSurface(); if (gradientComputationSurface == NULL) { errorMessage.appendWithNewLine("Gradient Computation Surface is not valid."); } if (m_borderEnclosingROI != NULL) { if (m_borderEnclosingROI->getNumberOfPoints() < 3) { errorMessage.appendWithNewLine("Border named " + m_borderEnclosingROI->getName() + " enclosing region is invalid must contain at least three points"); } } else { errorMessage.appendWithNewLine("Border enclosing region is invalid."); } std::vector dataFileSelections; for (std::vector::iterator fileIter = m_optimizeDataFileSelectors.begin(); fileIter != m_optimizeDataFileSelectors.end(); fileIter++) { BorderOptimizeDataFileSelector* selector = *fileIter; CaretPointer fileInfo = selector->getSelections(); if (fileInfo != NULL) { dataFileSelections.push_back(*fileInfo); } } if (dataFileSelections.empty()) { errorMessage.appendWithNewLine("No optimization data files are selected."); } /* * "Pair" border file */ const BorderFile* pairBorderFile = m_borderPairFileSelectionModel->getSelectedFileOfType(); std::vector pairedBorders; /* * Get borders selected by the user. */ AString metricFileMapName; CaretAssert(m_bordersInsideROI.size() <= m_borderCheckBoxes.size()); const int32_t numBorderFileSelections = static_cast(m_bordersInsideROI.size()); for (int32_t iBorder = 0; iBorder < numBorderFileSelections; iBorder++) { if (m_borderCheckBoxes[iBorder]->isVisible()) { if (m_borderCheckBoxes[iBorder]->isChecked()) { CaretAssertVectorIndex(m_bordersInsideROI, iBorder); Border* border = m_bordersInsideROI[iBorder]; m_selectedBorders.push_back(border); metricFileMapName.append(border->getName() + " "); if (pairBorderFile != NULL) { if (pairBorderFile->containsBorder(border)) { pairedBorders.push_back(border); } } } } } const int32_t numPairedBorders = static_cast(pairedBorders.size()); if (m_selectedBorders.empty()) { errorMessage.appendWithNewLine("No optimization border files are selected."); } else if (m_borderPairCheckBox->isChecked()) { if (pairBorderFile == NULL) { errorMessage.appendWithNewLine("Border pair file selection is invalid."); } else if (numPairedBorders != 2) { errorMessage.appendWithNewLine("Two of the selected borders must be in the border pair file."); if (pairedBorders.empty()) { errorMessage.appendWithNewLine("None of the selected borders are in the border pair file."); } else { errorMessage.appendWithNewLine("There are " + AString::number(numPairedBorders) + " selected borders in the border pair file."); for (int32_t i = 0; i < numPairedBorders; i++) { CaretAssertVectorIndex(pairedBorders, i); errorMessage.appendWithNewLine(" " + pairedBorders[i]->getName()); } } } } Surface* upsamplingSphericalSurface = NULL; int32_t upsamplingResolution = 0; if (m_upsamplingGroupBox->isChecked()) { upsamplingSphericalSurface = m_upsamplingSurfaceSelectionModel->getSurface(); if (upsamplingSphericalSurface == NULL) { errorMessage.appendWithNewLine("Upsampling is selected but no spherical surface is available."); } upsamplingResolution = m_upsamplingResolutionSpinBox->value(); if (upsamplingResolution <= 0) { errorMessage.appendWithNewLine("Upsampling resolution is invalid."); } } const QString savingBaseName = m_savingBaseNameLineEdit->text().trimmed(); const QString savingDirectoryName = m_savingDirectoryLineEdit->text().trimmed(); if (m_savingGroupBox->isChecked()) { if (savingDirectoryName.isEmpty()) { errorMessage.appendWithNewLine("Save Results is selected and Saving Directory is empty."); } if (savingBaseName.isEmpty()) { errorMessage.appendWithNewLine("Save Results is selected and Saving Base Filename is empty."); } } if ( ! errorMessage.isEmpty()) { WuQMessageBox::errorOk(this, errorMessage); return; } if (m_borderPairCheckBox->isChecked()) { CaretAssertMessage((pairedBorders.size() == 2), "Must be exactly two borders in the border pair vector"); } else { pairedBorders.clear(); } ProgressReportingDialog progressDialog("Border Optimization", "", this); progressDialog.setMinimum(0); progressDialog.setValue(0); MetricFile* vertexAreasMetricFile = NULL; CaretDataFile* vertexAreaFile = m_vertexAreasMetricFileSelectionModel->getSelectedFile(); if (vertexAreaFile != NULL) { vertexAreasMetricFile = dynamic_cast(vertexAreaFile); } const float gradientFollowingStrength = m_gradientFollowingStrengthSpinBox->value(); CaretPointer resultsMetricFile; resultsMetricFile.grabNew(new MetricFile()); BorderOptimizeExecutor::InputData algInput(m_selectedBorders, pairedBorders, m_borderEnclosingROI, m_nodesInsideROI, gradientComputationSurface, dataFileSelections, vertexAreasMetricFile, gradientFollowingStrength, upsamplingSphericalSurface, upsamplingResolution, resultsMetricFile, m_savingGroupBox->isChecked(), savingDirectoryName, savingBaseName); /* * Run border optimization. */ AString statisticsInformation; const bool algSuccessFlag = BorderOptimizeExecutor::run(algInput, statisticsInformation, errorMessage); /* * The progress dialog normally closes on its own but * may fail to close if the algorithm fails due to * the 'progress value' not reaching 'progress maximum'. * This prevent the error dialog from being shown behind * the progress dialog. */ progressDialog.close(); if (algSuccessFlag) { AString infoMsg; if (m_outputGradientMapCheckBox->isChecked() && ! resultsMetricFile->isEmpty()) { resultsMetricFile->setStructure(gradientComputationSurface->getStructure()); resultsMetricFile->setMapName(0, metricFileMapName); PaletteColorMapping* pcm = resultsMetricFile->getMapPaletteColorMapping(0); pcm->setAutoScalePercentageNegativeMaximum(96.0); pcm->setAutoScalePercentageNegativeMinimum(4.0); pcm->setAutoScalePercentagePositiveMinimum(4.0); pcm->setAutoScalePercentagePositiveMaximum(96.0); pcm->setSelectedPaletteName("videen_style"); pcm->setDisplayNegativeDataFlag(false); pcm->setDisplayZeroDataFlag(false); pcm->setDisplayPositiveDataFlag(true); pcm->setInterpolatePaletteFlag(true); EventDataFileAdd addDataFile(resultsMetricFile.releasePointer()); EventManager::get()->sendEvent(addDataFile.getPointer()); infoMsg.appendWithNewLine("Border Optimization Gradient results in file " + resultsMetricFile->getFileNameNoPath() + " Map Name: " + metricFileMapName); if (m_browserTabIndex >= 0) { /* * Updating user interface will update content of overlay * so that metric file gets added. */ EventManager::get()->sendEvent(EventUserInterfaceUpdate().getPointer()); EventBrowserTabGet browserTabEvent(m_browserTabIndex); EventManager::get()->sendEvent(browserTabEvent.getPointer()); BrowserTabContent* tabContent = browserTabEvent.getBrowserTab(); if (tabContent != NULL) { /* * Create a new overlay at the top */ OverlaySet* overlaySet = tabContent->getOverlaySet(); CaretAssert(overlaySet); overlaySet->insertOverlayAbove(0); /* * Load the gradient metric file into the top-most * (should be the new) overlay. */ Overlay* overlay = overlaySet->getPrimaryOverlay(); CaretAssert(overlay); overlay->setSelectionData(resultsMetricFile, 0); overlay->setEnabled(true); overlay->setMapYokingGroup(MapYokingGroupEnum::MAP_YOKING_GROUP_OFF); overlay->setOpacity(1.0); EventManager::get()->sendEvent(EventSurfaceColoringInvalidate().getPointer()); } } } /* * Display the statistics information. */ if ( ! statisticsInformation.isEmpty()) { infoMsg.appendWithNewLine(""); infoMsg.appendWithNewLine(statisticsInformation); } infoMsg = infoMsg.replaceHtmlSpecialCharactersWithEscapeCharacters(); EventManager::get()->sendEvent(EventUpdateInformationWindows(infoMsg).getPointer()); /* * Success, allow dialog to close. */ WuQDialogModal::okButtonClicked(); return; } WuQMessageBox::errorOk(this, errorMessage); } /** * Called when cancel button clicked. */ void BorderOptimizeDialog::cancelButtonClicked() { preserveDialogSizeAndPositionWhenReOpened(); /* * Allow dialog to close. */ WuQDialogModal::cancelButtonClicked(); } /** * Make dialog appear in same place and size when repopened. */ void BorderOptimizeDialog::preserveDialogSizeAndPositionWhenReOpened() { setSaveWindowPositionForNextTime("BorderOptimizeDialog"); setSizeOfDialogWhenDisplayed(size()); } /** * Get borders that were selected by the user. * * @param selectedBordersOut * Output containing borders selected by the user. */ void BorderOptimizeDialog::getModifiedBorders(std::vector& modifiedBordersOut) const { modifiedBordersOut = m_selectedBorders; } /** * @return The border selection widget. */ QWidget* BorderOptimizeDialog::createBorderSelectionWidget() { m_borderPairCheckBox = new QCheckBox("Border Pair File"); m_borderPairCheckBox->setChecked(true); m_borderPairFileSelectionModel = CaretDataFileSelectionModel::newInstanceForCaretDataFileType(GuiManager::get()->getBrain(), DataFileTypeEnum::BORDER); m_borderPairFileSelectionComboBox = new CaretDataFileSelectionComboBox(this); QHBoxLayout* borderPairLayout = new QHBoxLayout(); borderPairLayout->addWidget(m_borderPairCheckBox, 0); borderPairLayout->addWidget(m_borderPairFileSelectionComboBox->getWidget(), 100); QObject::connect(m_borderPairCheckBox, SIGNAL(clicked(bool)), m_borderPairFileSelectionComboBox->getWidget(), SLOT(setEnabled(bool))); m_borderPairFileSelectionComboBox->getWidget()->setEnabled(m_borderPairCheckBox->isChecked()); m_bordersInsideROIGridLayout = new QGridLayout(); m_bordersInsideROIGridLayout->setMargin(2); m_bordersInsideROIGridLayout->setHorizontalSpacing(25); m_bordersInsideROIGridLayout->setColumnStretch(100, 1000); // force widgets to left QAction* diableAllAction = new QAction("Disable All", this); QObject::connect(diableAllAction, SIGNAL(triggered(bool)), this, SLOT(bordersDisableAllSelected())); QToolButton* disableAllToolButton = new QToolButton(); disableAllToolButton->setDefaultAction(diableAllAction); QAction* enableAllAction = new QAction("Enable All", this); QObject::connect(enableAllAction, SIGNAL(triggered(bool)), this, SLOT(bordersEnableAllSelected())); QToolButton* enableAllToolButton = new QToolButton(); enableAllToolButton->setDefaultAction(enableAllAction); QHBoxLayout* buttonsLayout = new QHBoxLayout(); buttonsLayout->setMargin(2); buttonsLayout->addWidget(enableAllToolButton); buttonsLayout->addWidget(disableAllToolButton); buttonsLayout->addStretch(); QGroupBox* widget = new QGroupBox("Borders"); widget->setSizePolicy(widget->sizePolicy().horizontalPolicy(), QSizePolicy::Fixed); QVBoxLayout* layout = new QVBoxLayout(widget); layout->setMargin(2); layout->addLayout(borderPairLayout); layout->addLayout(m_bordersInsideROIGridLayout, STRETCH_NONE); layout->addLayout(buttonsLayout, STRETCH_NONE); return widget; } /** * Disable all borders. */ void BorderOptimizeDialog::bordersDisableAllSelected() { setAllBorderEnabledSelections(false); } /** * Enable all borders. */ void BorderOptimizeDialog::bordersEnableAllSelected() { setAllBorderEnabledSelections(true); } /** * Set the enable status of all borders to the given value. * * @param status * New status. */ void BorderOptimizeDialog::setAllBorderEnabledSelections(const bool status) { for (std::vector::iterator iter = m_borderCheckBoxes.begin(); iter != m_borderCheckBoxes.end(); iter++) { QCheckBox* cb = *iter; cb->setChecked(status); } } /** * @return The data files widget. */ QWidget* BorderOptimizeDialog::createDataFilesWidget() { QWidget* gridWidget = new QWidget(); if (DATA_FILES_IN_SCROLL_BARS) { gridWidget->setSizePolicy(gridWidget->sizePolicy().horizontalPolicy(), QSizePolicy::Fixed); } else { // gridWidget->setSizePolicy(gridWidget->sizePolicy().horizontalPolicy(), // QSizePolicy::Fixed); } m_borderOptimizeDataFileGridLayout = new QGridLayout(gridWidget); m_borderOptimizeDataFileGridLayout->setMargin(2); std::vector optimizeMapFiles; GuiManager::get()->getBrain()->getAllMappableDataFileWithDataFileTypes(m_optimizeDataFileTypes, optimizeMapFiles); const int32_t numberOfMapFiles = static_cast(optimizeMapFiles.size()); // const int32_t minimumDataFilesToShow = 10; // const int32_t numberOfFilesToShow = std::max(minimumDataFilesToShow, // numberOfMapFiles); const int32_t numberOfFilesToShow = 3; for (int32_t i = 0; i < numberOfFilesToShow; i++) { CaretMappableDataFile* mapFile = NULL; if (i < numberOfMapFiles) { CaretAssertVectorIndex(optimizeMapFiles, i); mapFile = optimizeMapFiles[i]; } addDataFileRow(mapFile); } QWidget* widget = gridWidget; if (DATA_FILES_IN_SCROLL_BARS) { QScrollArea* scrollArea = new QScrollArea(); scrollArea->setWidget(gridWidget); scrollArea->setWidgetResizable(true); scrollArea->setHorizontalScrollBarPolicy(Qt::ScrollBarAsNeeded); scrollArea->setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded); scrollArea->setSizePolicy(gridWidget->sizePolicy().horizontalPolicy(), QSizePolicy::Preferred); widget = scrollArea; } else { widget->setSizePolicy(widget->sizePolicy().horizontalPolicy(), QSizePolicy::Fixed); } QAction* addRowAction = new QAction("Add New Data File Row", this); QObject::connect(addRowAction, SIGNAL(triggered(bool)), this, SLOT(addDataFileRowToolButtonClicked())); QToolButton* addRowToolButton = new QToolButton(); addRowToolButton->setDefaultAction(addRowAction); QAction* diableAllAction = new QAction("Disable All", this); QObject::connect(diableAllAction, SIGNAL(triggered(bool)), this, SLOT(dataFilesDisableAllSelected())); QToolButton* disableAllToolButton = new QToolButton(); disableAllToolButton->setDefaultAction(diableAllAction); QAction* enableAllAction = new QAction("Enable All", this); QObject::connect(enableAllAction, SIGNAL(triggered(bool)), this, SLOT(dataFilesEnableAllSelected())); QToolButton* enableAllToolButton = new QToolButton(); enableAllToolButton->setDefaultAction(enableAllAction); QHBoxLayout* buttonsLayout = new QHBoxLayout(); buttonsLayout->setMargin(2); buttonsLayout->addWidget(addRowToolButton); buttonsLayout->addWidget(enableAllToolButton); buttonsLayout->addWidget(disableAllToolButton); buttonsLayout->addStretch(); QGroupBox* groupBox = new QGroupBox("Data Files"); if ( ! DATA_FILES_IN_SCROLL_BARS) { // groupBox->setSizePolicy(groupBox->sizePolicy().horizontalPolicy(), // QSizePolicy::Minimum); } QVBoxLayout* groupBoxLayout = new QVBoxLayout(groupBox); groupBoxLayout->setMargin(2); if (DATA_FILES_IN_SCROLL_BARS) { groupBox->setMinimumHeight(300); groupBoxLayout->addWidget(widget, STRETCH_NONE); } else { groupBoxLayout->addWidget(widget, STRETCH_NONE); // STRETCH_MAX); } groupBoxLayout->addLayout(buttonsLayout, STRETCH_NONE); m_defaultDataFilesWidgetSize = gridWidget->sizeHint(); if ( ! DATA_FILES_IN_SCROLL_BARS) { groupBox->setSizePolicy(groupBox->sizePolicy().horizontalPolicy(), QSizePolicy::Fixed); //groupBoxLayout->addStretch(); } return groupBox; } /** * Add a data file row * * @param mapFile * If not NULL, the file selector is set to this file. */ void BorderOptimizeDialog::addDataFileRow(CaretMappableDataFile* mapFile) { const int32_t index = static_cast(m_optimizeDataFileSelectors.size()); BorderOptimizeDataFileSelector* selector = new BorderOptimizeDataFileSelector(index, m_optimizeDataFileTypes, mapFile, m_borderOptimizeDataFileGridLayout, this); selector->setSelected(true); m_optimizeDataFileSelectors.push_back(selector); } /** * Called when data file's Add Row button is clicked. */ void BorderOptimizeDialog::addDataFileRowToolButtonClicked() { addDataFileRow(NULL); } /** * Disable all borders. */ void BorderOptimizeDialog::dataFilesDisableAllSelected() { setAllDataFileEnabledSelections(false); } /** * Enable all borders. */ void BorderOptimizeDialog::dataFilesEnableAllSelected() { setAllDataFileEnabledSelections(true); } void BorderOptimizeDialog::saveBrowseButtonClicked() { QString path = CaretFileDialog::getExistingDirectoryDialog(); if (path != "") m_savingDirectoryLineEdit->setText(path); } /** * Set the enable status of all borders to the given value. * * @param status * New status. */ void BorderOptimizeDialog::setAllDataFileEnabledSelections(const bool status) { for (std::vector::iterator iter = m_optimizeDataFileSelectors.begin(); iter != m_optimizeDataFileSelectors.end(); iter++) { BorderOptimizeDataFileSelector* dfs = *iter; dfs->m_selectionCheckBox->setChecked(status); } } /** * @return The metric vertex areas widget. */ QWidget* BorderOptimizeDialog::createVertexAreasMetricWidget() { m_vertexAreasMetricFileComboBox = new CaretDataFileSelectionComboBox(this); QGroupBox* widget = new QGroupBox("Vertex Areas Metric File"); widget->setSizePolicy(widget->sizePolicy().horizontalPolicy(), QSizePolicy::Fixed); QVBoxLayout* layout = new QVBoxLayout(widget); layout->setMargin(2); layout->addWidget(m_vertexAreasMetricFileComboBox->getWidget()); return widget; } /** * @return The options widget. */ QWidget* BorderOptimizeDialog::createOptionsWidget() { m_keepRegionBorderCheckBox = new QCheckBox("Keep Boundary Border"); m_keepRegionBorderCheckBox->setChecked(true); m_outputGradientMapCheckBox = new QCheckBox("Output Gradient as Map"); m_outputGradientMapCheckBox->setChecked(true); QLabel* gradientLabel = new QLabel("Graident Following Strength"); m_gradientFollowingStrengthSpinBox = new QDoubleSpinBox(); m_gradientFollowingStrengthSpinBox->setRange(0.0, 1.0e6); m_gradientFollowingStrengthSpinBox->setDecimals(2); m_gradientFollowingStrengthSpinBox->setSingleStep(1.0); m_gradientFollowingStrengthSpinBox->setValue(5); QHBoxLayout* gradientLayout = new QHBoxLayout(); gradientLayout->addWidget(gradientLabel); gradientLayout->addWidget(m_gradientFollowingStrengthSpinBox); gradientLayout->addStretch(); QGroupBox* widget = new QGroupBox("Options"); widget->setSizePolicy(widget->sizePolicy().horizontalPolicy(), QSizePolicy::Fixed); QVBoxLayout* layout = new QVBoxLayout(widget); layout->setMargin(2); layout->addWidget(m_keepRegionBorderCheckBox); layout->addWidget(m_outputGradientMapCheckBox); layout->addLayout(gradientLayout); return widget; } /** * @return Return keep boundary border selection status. */ bool BorderOptimizeDialog::isKeepBoundaryBorderSelected() const { return m_keepRegionBorderCheckBox->isChecked(); } /** * @return The options widget. */ QWidget* BorderOptimizeDialog::createSurfaceSelectionWidget() { m_surfaceSelectionControl = new SurfaceSelectionViewController(this); QObject::connect(m_surfaceSelectionControl, SIGNAL(surfaceSelected(Surface*)), this, SLOT(gradientComputatonSurfaceSelected(Surface*))); QGroupBox* widget = new QGroupBox("Gradient Computation Surface"); widget->setSizePolicy(widget->sizePolicy().horizontalPolicy(), QSizePolicy::Fixed); QVBoxLayout* layout = new QVBoxLayout(widget); layout->setMargin(2); layout->addWidget(m_surfaceSelectionControl->getWidget()); return widget; } /** * @return The spherical upsampling widget. */ QWidget* BorderOptimizeDialog::createSphericalUpsamplingWidget() { QLabel* surfaceLabel = new QLabel("Current Sphere"); m_upsamplingSurfaceSelectionControl = new SurfaceSelectionViewController(this); QLabel* resolutionLabel = new QLabel("Upsampling Resolution"); m_upsamplingResolutionSpinBox = new QSpinBox(); m_upsamplingResolutionSpinBox->setRange(0, std::numeric_limits::max()); m_upsamplingResolutionSpinBox->setSingleStep(1); m_upsamplingResolutionSpinBox->setToolTip("Number of vertices to use when making the upsampling sphere"); m_upsamplingGroupBox = new QGroupBox(" Upsampling"); m_upsamplingGroupBox->setSizePolicy(m_upsamplingGroupBox->sizePolicy().horizontalPolicy(), QSizePolicy::Fixed); m_upsamplingGroupBox->setCheckable(true); m_upsamplingGroupBox->setChecked(true); QGridLayout* layout = new QGridLayout(m_upsamplingGroupBox); layout->setMargin(2); layout->setColumnStretch(0, 0); layout->setColumnStretch(1, 100); int row = 0; layout->addWidget(surfaceLabel, row, 0); layout->addWidget(m_upsamplingSurfaceSelectionControl->getWidget(), row, 1); row++; layout->addWidget(resolutionLabel, row, 0); layout->addWidget(m_upsamplingResolutionSpinBox, row, 1, Qt::AlignLeft); return m_upsamplingGroupBox; } QWidget* BorderOptimizeDialog::createSavingWidget() { m_savingGroupBox = new QGroupBox(" Save results"); m_savingGroupBox->setSizePolicy(m_savingGroupBox->sizePolicy().horizontalPolicy(), QSizePolicy::Fixed); m_savingGroupBox->setCheckable(true); m_savingGroupBox->setChecked(true); QGridLayout* layout = new QGridLayout(m_savingGroupBox); layout->setMargin(2); layout->setColumnStretch(0, 0);//label layout->setColumnStretch(1, 100);//line edit layout->setColumnStretch(2, 0);//button or extended line edit layout->addWidget(new QLabel("Base Filename"), 0, 0); m_savingBaseNameLineEdit = new QLineEdit(); layout->addWidget(m_savingBaseNameLineEdit, 0, 1, 1, -1);//extend to end of row layout->addWidget(new QLabel("Path"), 1, 0); m_savingDirectoryLineEdit = new QLineEdit(); layout->addWidget(m_savingDirectoryLineEdit, 1, 1); QPushButton* browseButton = new QPushButton("Browse..."); QObject::connect(browseButton, SIGNAL(clicked()), this, SLOT(saveBrowseButtonClicked())); layout->addWidget(browseButton, 1, 2); return m_savingGroupBox; } /** * Called when gradient computational surface is selected * * @param surface * Surface that was selected. */ void BorderOptimizeDialog::gradientComputatonSurfaceSelected(Surface* surface) { if (m_surfaceSelectionModel != NULL) { m_surfaceSelectionModel->setSurface(surface); } } /** * Constructor. * * @param optimizeDataFileTypes * File types for selection * @param defaultFile * The default file (may be NULL) * @param gridLayout * Layout for the widgets. * @param parent * Parent of the selector. */ BorderOptimizeDataFileSelector::BorderOptimizeDataFileSelector(const int32_t itemIndex, const std::vector& optimizeDataFileTypes, CaretMappableDataFile* defaultFile, QGridLayout* gridLayout, QObject* parent) : QObject(parent) { m_mapFileAndIndexSelectorObject = new CaretMappableDataFileAndMapSelectorObject(optimizeDataFileTypes, CaretMappableDataFileAndMapSelectorObject::OPTION_SHOW_MAP_INDEX_SPIN_BOX, this); QObject::connect(m_mapFileAndIndexSelectorObject, SIGNAL(selectionWasPerformed()), this, SLOT(mapFileSelectionChanged())); QWidget* mapFileComboBox; QWidget* mapIndexSpinBox; QWidget* mapNameComboBox; m_mapFileAndIndexSelectorObject->getWidgetsForAddingToLayout(mapFileComboBox, mapIndexSpinBox, mapNameComboBox); CaretAssert(mapFileComboBox); CaretAssert(mapIndexSpinBox); CaretAssert(mapNameComboBox); mapIndexSpinBox->setFixedWidth(80); m_allMapsCheckBox = new QCheckBox(""); QObject::connect(m_allMapsCheckBox, SIGNAL(toggled(bool)), this, SLOT(allMapsCheckBoxToggled(bool))); m_invertGradientCheckBox = new QCheckBox(""); m_skipGradientCheckBox = new QCheckBox(""); m_selectionCheckBox = new QCheckBox(""); m_selectionCheckBox->setChecked(true); QObject::connect(m_selectionCheckBox, SIGNAL(toggled(bool)), this, SLOT(selectionCheckBoxToggled(bool))); m_exclusionDistanceSpinBox = new QDoubleSpinBox(); m_exclusionDistanceSpinBox->setRange(0, 1.0e6); m_exclusionDistanceSpinBox->setDecimals(2); m_exclusionDistanceSpinBox->setSingleStep(1.0); m_exclusionDistanceSpinBox->setValue(2); m_smoothingSpinBox = new QDoubleSpinBox(); m_smoothingSpinBox->setRange(0.0, 1.0e6); m_smoothingSpinBox->setDecimals(2); m_smoothingSpinBox->setSingleStep(1.0); m_smoothingSpinBox->setValue(1.0); m_weightSpinBox = new QDoubleSpinBox(); m_weightSpinBox->setRange(0.0, 1.0); m_weightSpinBox->setDecimals(2); m_weightSpinBox->setSingleStep(0.01); m_weightSpinBox->setValue(0.7); int32_t columnCount = 0; const int32_t COLUMN_SELECT = columnCount++; const int32_t COLUMN_EXCLUDE = columnCount++; const int32_t COLUMN_SMOOTH = columnCount++; const int32_t COLUMN_WEIGHT = columnCount++; const int32_t COLUMN_INVERT = columnCount++; const int32_t COLUMN_SKIP = columnCount++; const int32_t COLUMN_ALL_MAP = columnCount++; const int32_t COLUMN_MAP_INDEX = columnCount++; const int32_t COLUMN_MAP_NAME = columnCount++; if (itemIndex == 0) { const int32_t row = gridLayout->rowCount(); gridLayout->addWidget(new QLabel("Enable"), row, COLUMN_SELECT, 2, 1, Qt::AlignCenter); gridLayout->addWidget(new QLabel("Exclusion"), row, COLUMN_EXCLUDE, Qt::AlignCenter); gridLayout->addWidget(new QLabel("Radius (mm)"), row+1, COLUMN_EXCLUDE, Qt::AlignCenter); gridLayout->addWidget(new QLabel("Smoothing"), row, COLUMN_SMOOTH, Qt::AlignCenter); gridLayout->addWidget(new QLabel("Sigma (mm)"), row+1, COLUMN_SMOOTH, Qt::AlignCenter); gridLayout->addWidget(new QLabel("Weight"), row, COLUMN_WEIGHT, 2, 1, Qt::AlignCenter); gridLayout->addWidget(new QLabel("Invert"), row, COLUMN_INVERT, Qt::AlignHCenter); gridLayout->addWidget(new QLabel("Gradient"), row + 1, COLUMN_INVERT, Qt::AlignHCenter); gridLayout->addWidget(new QLabel("Skip"), row, COLUMN_SKIP, Qt::AlignHCenter); gridLayout->addWidget(new QLabel("Gradient"), row + 1, COLUMN_SKIP, Qt::AlignHCenter); gridLayout->addWidget(new QLabel("All"), row, COLUMN_ALL_MAP, Qt::AlignCenter); gridLayout->addWidget(new QLabel("Maps"), row + 1, COLUMN_ALL_MAP, Qt::AlignCenter); gridLayout->addWidget(new QLabel("File/Map"), row, COLUMN_MAP_INDEX, 2, 2, Qt::AlignCenter); gridLayout->setVerticalSpacing(2); gridLayout->setColumnStretch(COLUMN_SELECT, 0); gridLayout->setColumnStretch(COLUMN_EXCLUDE, 0); gridLayout->setColumnStretch(COLUMN_SMOOTH, 0); gridLayout->setColumnStretch(COLUMN_WEIGHT, 0); gridLayout->setColumnStretch(COLUMN_INVERT, 0); gridLayout->setColumnStretch(COLUMN_SKIP, 0); gridLayout->setColumnStretch(COLUMN_ALL_MAP, 0); gridLayout->setColumnStretch(COLUMN_MAP_INDEX, 0); gridLayout->setColumnStretch(COLUMN_MAP_NAME, 100); } int row = gridLayout->rowCount(); gridLayout->addWidget(m_selectionCheckBox, row, COLUMN_SELECT, 2, 1, Qt::AlignCenter); gridLayout->addWidget(m_exclusionDistanceSpinBox, row, COLUMN_EXCLUDE, 2, 1, Qt::AlignCenter); gridLayout->addWidget(m_smoothingSpinBox, row, COLUMN_SMOOTH, 2, 1, Qt::AlignCenter); gridLayout->addWidget(m_weightSpinBox, row, COLUMN_WEIGHT, 2, 1, Qt::AlignCenter); gridLayout->addWidget(m_invertGradientCheckBox, row, COLUMN_INVERT, 2, 1, Qt::AlignCenter); gridLayout->addWidget(m_skipGradientCheckBox, row, COLUMN_SKIP, 2, 1, Qt::AlignCenter); gridLayout->addWidget(m_allMapsCheckBox, row, COLUMN_ALL_MAP, 2, 1, Qt::AlignCenter); gridLayout->addWidget(mapFileComboBox, row, COLUMN_MAP_INDEX, 1, 2); row++; gridLayout->addWidget(mapIndexSpinBox, row, COLUMN_MAP_INDEX); gridLayout->addWidget(mapNameComboBox, row, COLUMN_MAP_NAME); CaretMappableDataFileAndMapSelectionModel* model = m_mapFileAndIndexSelectorObject->getModel(); if (defaultFile != NULL) { model->setSelectedFile(defaultFile); } else { m_selectionCheckBox->blockSignals(true); m_selectionCheckBox->setChecked(false); m_selectionCheckBox->blockSignals(false); } m_mapFileAndIndexSelectorObject->updateFileAndMapSelector(model); updateFileData(); } /* * Destructor. */ BorderOptimizeDataFileSelector::~BorderOptimizeDataFileSelector() { } /** * Update the file data. */ void BorderOptimizeDataFileSelector::updateFileData() { const bool widgetsEnabled = m_selectionCheckBox->isChecked(); m_allMapsCheckBox->setEnabled(widgetsEnabled); m_exclusionDistanceSpinBox->setEnabled(widgetsEnabled); m_invertGradientCheckBox->setEnabled(widgetsEnabled); m_skipGradientCheckBox->setEnabled(widgetsEnabled); m_smoothingSpinBox->setEnabled(widgetsEnabled); m_weightSpinBox->setEnabled(widgetsEnabled); CaretMappableDataFileAndMapSelectionModel* model = m_mapFileAndIndexSelectorObject->getModel(); m_mapFileAndIndexSelectorObject->updateFileAndMapSelector(model); m_mapFileAndIndexSelectorObject->setEnabled(widgetsEnabled); if (widgetsEnabled) { const CaretMappableDataFile* mapFile = model->getSelectedFile(); const bool denseFileFlag = ((mapFile != NULL) ? (mapFile->getDataFileType() == DataFileTypeEnum::CONNECTIVITY_DENSE) : false); if (denseFileFlag) { m_allMapsCheckBox->setChecked(false); m_allMapsCheckBox->setEnabled(false); m_exclusionDistanceSpinBox->setEnabled(true); m_skipGradientCheckBox->setChecked(false); m_skipGradientCheckBox->setEnabled(false); } else { m_exclusionDistanceSpinBox->setEnabled(false); } m_allMapsCheckBox->setEnabled( ! denseFileFlag); } } /** * Set the selection status. * * @param selectedStatus * New selection status. */ void BorderOptimizeDataFileSelector::setSelected(const bool selectedStatus) { m_selectionCheckBox->setChecked(selectedStatus); } /** * Gets called when the map file selection changes. */ void BorderOptimizeDataFileSelector::mapFileSelectionChanged() { updateFileData(); } /** * @return Selections made for data file. */ CaretPointer BorderOptimizeDataFileSelector::getSelections() const { CaretMappableDataFileAndMapSelectionModel* model = m_mapFileAndIndexSelectorObject->getModel(); CaretMappableDataFile* mapFile = model->getSelectedFile(); const int32_t mapIndex = model->getSelectedMapIndex(); CaretPointer dataOut(NULL); if (mapFile != NULL) { if ((mapIndex >= 0) && (mapIndex < mapFile->getNumberOfMaps())) { if (m_selectionCheckBox->isChecked()) { dataOut.grabNew(new BorderOptimizeExecutor::DataFileInfo(mapFile, mapIndex, m_allMapsCheckBox->isChecked(), m_smoothingSpinBox->value(), m_weightSpinBox->value(), m_invertGradientCheckBox->isChecked(), m_skipGradientCheckBox->isChecked(), m_exclusionDistanceSpinBox->value())); } } } return dataOut; } /* * Called when selection checkbox changes. * @param checked * New status for selection check box. */ void BorderOptimizeDataFileSelector::selectionCheckBoxToggled(bool /*checked*/) { updateFileData(); } /* * Called when all maps check box is toggled. * * @param checked * New status for selection check box. */ void BorderOptimizeDataFileSelector::allMapsCheckBoxToggled(bool /*checked*/) { updateFileData(); } connectome-workbench-1.2.3+git41-gc4c6c90/src/GuiQt/BorderOptimizeDialog.h000066400000000000000000000156231300200146000260330ustar00rootroot00000000000000#ifndef __BORDER_OPTIMIZE_DIALOG_H__ #define __BORDER_OPTIMIZE_DIALOG_H__ /*LICENSE_START*/ /* * Copyright (C) 2015 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 "BorderOptimizeExecutor.h" #include "DataFileTypeEnum.h" #include "StructureEnum.h" #include "WuQDialogModal.h" class QCheckBox; class QDoubleSpinBox; class QGridLayout; class QGroupBox; class QLineEdit; class QSpinBox; class QVBoxLayout; namespace caret { class Border; class BorderOptimizeDataFileSelector; class CaretDataFileSelectionComboBox; class CaretDataFileSelectionModel; class CaretMappableDataFile; class CaretMappableDataFileAndMapSelectorObject; class Surface; class SurfaceSelectionModel; class SurfaceSelectionViewController; class BorderOptimizeDialog : public WuQDialogModal { Q_OBJECT public: BorderOptimizeDialog(QWidget* parent); virtual ~BorderOptimizeDialog(); void getModifiedBorders(std::vector& modifiedBordersOut) const; bool isKeepBoundaryBorderSelected() const; void updateDialog(const int32_t browserTabIndex, Surface* surface, std::vector >& bordersInsideROI, Border* borderEnclosingROI, std::vector& nodesInsideROI); protected: virtual void okButtonClicked(); virtual void cancelButtonClicked(); private slots: void addDataFileRowToolButtonClicked(); void gradientComputatonSurfaceSelected(Surface* surface); void bordersDisableAllSelected(); void bordersEnableAllSelected(); void dataFilesDisableAllSelected(); void dataFilesEnableAllSelected(); void saveBrowseButtonClicked(); private: BorderOptimizeDialog(const BorderOptimizeDialog&); BorderOptimizeDialog& operator=(const BorderOptimizeDialog&); QWidget* createBorderSelectionWidget(); QWidget* createDataFilesWidget(); QWidget* createVertexAreasMetricWidget(); QWidget* createOptionsWidget(); QWidget* createSurfaceSelectionWidget(); QWidget* createSphericalUpsamplingWidget(); QWidget* createSavingWidget(); void addDataFileRow(CaretMappableDataFile* mapFile); void setAllBorderEnabledSelections(const bool status); void setAllDataFileEnabledSelections(const bool status); void preserveDialogSizeAndPositionWhenReOpened(); QWidget* m_dialogWidget; Surface* m_surface; std::vector m_bordersInsideROI; std::vector m_borderPointsInsideROICount; std::vector m_selectedBorders; QGridLayout* m_bordersInsideROIGridLayout; Border* m_borderEnclosingROI; std::vector m_nodesInsideROI; std::vector m_optimizeDataFileSelectors; CaretDataFileSelectionComboBox* m_borderPairFileSelectionComboBox; CaretDataFileSelectionModel* m_borderPairFileSelectionModel; QCheckBox* m_borderPairCheckBox; std::vector m_borderCheckBoxes; std::vector m_optimizeDataFileTypes; QGridLayout* m_borderOptimizeDataFileGridLayout; StructureEnum::Enum m_surfaceSelectionStructure; SurfaceSelectionModel* m_surfaceSelectionModel; SurfaceSelectionViewController* m_surfaceSelectionControl; CaretDataFileSelectionComboBox* m_vertexAreasMetricFileComboBox; CaretDataFileSelectionModel* m_vertexAreasMetricFileSelectionModel; QDoubleSpinBox* m_gradientFollowingStrengthSpinBox; int32_t m_browserTabIndex; QCheckBox* m_keepRegionBorderCheckBox; QCheckBox* m_outputGradientMapCheckBox; QSize m_defaultDataFilesWidgetSize; QGroupBox* m_upsamplingGroupBox; SurfaceSelectionModel* m_upsamplingSurfaceSelectionModel; StructureEnum::Enum m_upsamplingSurfaceStructure; SurfaceSelectionViewController* m_upsamplingSurfaceSelectionControl; QSpinBox* m_upsamplingResolutionSpinBox; QGroupBox* m_savingGroupBox; QLineEdit* m_savingBaseNameLineEdit; QLineEdit* m_savingDirectoryLineEdit; }; class BorderOptimizeDataFileSelector : public QObject { Q_OBJECT public: BorderOptimizeDataFileSelector(const int32_t itemIndex, const std::vector& optimizeDataFileTypes, CaretMappableDataFile* defaultFile, QGridLayout* gridLayout, QObject* parent); ~BorderOptimizeDataFileSelector(); void updateFileData(); CaretPointer getSelections() const; void setSelected(const bool selectedStatus); private slots: void selectionCheckBoxToggled(bool checked); void allMapsCheckBoxToggled(bool checked); void mapFileSelectionChanged(); public: CaretMappableDataFileAndMapSelectorObject* m_mapFileAndIndexSelectorObject; QCheckBox* m_allMapsCheckBox; QDoubleSpinBox* m_exclusionDistanceSpinBox; QCheckBox* m_invertGradientCheckBox; QCheckBox* m_skipGradientCheckBox; QCheckBox* m_selectionCheckBox; QDoubleSpinBox* m_smoothingSpinBox; QDoubleSpinBox* m_weightSpinBox; }; #ifdef __BORDER_OPTIMIZE_DIALOG_DECLARE__ #endif // __BORDER_OPTIMIZE_DIALOG_DECLARE__ } // namespace #endif //__BORDER_OPTIMIZE_DIALOG_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/GuiQt/BorderOptimizeExecutor.cxx000066400000000000000000001613231300200146000270040ustar00rootroot00000000000000 /*LICENSE_START*/ /* * Copyright (C) 2015 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 __BORDER_OPTIMIZE_EXECUTOR_DECLARE__ #include "BorderOptimizeExecutor.h" #undef __BORDER_OPTIMIZE_EXECUTOR_DECLARE__ #include "Border.h" #include "CaretAssert.h" #include "Surface.h" #include "AlgorithmBorderResample.h" #include "AlgorithmBorderToVertices.h" #include "AlgorithmCiftiCorrelationGradient.h" #include "AlgorithmCiftiRestrictDenseMap.h" #include "AlgorithmCiftiSeparate.h" #include "AlgorithmMetricDilate.h" #include "AlgorithmMetricGradient.h" #include "AlgorithmMetricFindClusters.h" #include "AlgorithmNodesInsideBorder.h" #include "AlgorithmSurfaceCreateSphere.h" #include "AlgorithmSurfaceResample.h" #include "BorderFile.h" #include "CaretLogger.h" #include "CaretOMP.h" #include "CiftiFile.h" #include "CiftiMappableDataFile.h" #include "EventProgressUpdate.h" #include "FileInformation.h" #include "GeodesicHelper.h" #include "MathFunctions.h" #include "MetricFile.h" #include "SurfaceFile.h" #include "SurfaceProjectedItem.h" #include "SurfaceProjectionBarycentric.h" #include "SurfaceResamplingHelper.h" #include "TextFile.h" #include "TopologyHelper.h" #include using namespace caret; using namespace std; /** * \class caret::BorderOptimizeExecutor * \brief * \ingroup GuiQt * * */ /** * Constructor. */ BorderOptimizeExecutor::BorderOptimizeExecutor() : CaretObject() { } /** * Destructor. */ BorderOptimizeExecutor::~BorderOptimizeExecutor() { } namespace { void doCombination(const MetricFile& gradient, const vector& roiNodes, const bool& invert, const float& mapStrength, vector& combinedGradData) { const float* gradVals = gradient.getValuePointerForColumn(0); float myMin = 0.0f, myMax = 0.0f; bool first = true; int numSelected = (int)roiNodes.size(); for (int i = 0; i < numSelected; ++i)//for normalizing, gather min/max { float tempVal = gradVals[roiNodes[i]]; if (MathFunctions::isNumeric(tempVal)) { if (first) { first = false; myMin = tempVal; myMax = tempVal; } else { if (tempVal < myMin) myMin = tempVal; if (tempVal > myMax) myMax = tempVal; } } } if (myMin == myMax)//if no contrast, map it all to (post-inversion) minimum - also trips if no numeric data { if (invert) { myMin -= 1.0f; } else { myMax += 1.0f; } } float myRange = myMax - myMin; for (int i = 0; i < numSelected; ++i)//for normalizing, gather min/max { float tempVal = gradVals[roiNodes[i]]; if (MathFunctions::isNumeric(tempVal)) { float toCombine; if (invert) { toCombine = (myMax - tempVal) / myRange; } else { toCombine = (tempVal - myMin) / myRange; } combinedGradData[roiNodes[i]] *= 1.0f + mapStrength * (toCombine - 1.0f);//equals toCombine * mapStrength + 1.0f - mapStrength } else { combinedGradData[roiNodes[i]] *= 1.0f - mapStrength; } } } bool extractGradientData(const CaretMappableDataFile* dataFile, const int32_t& mapIndex, SurfaceFile* surface, const MetricFile* gradRoi, const float& smoothing, const MetricFile* correctedAreasMetric, MetricFile& gradientOut, const bool& skipGradient, const float& excludeDist) { int numNodes = surface->getNumberOfNodes(); MetricFile tempData, tempRoi; const MetricFile* useData = &tempData, *useRoi = gradRoi; switch (dataFile->getDataFileType()) { case DataFileTypeEnum::METRIC: { const MetricFile* metricFile = dynamic_cast(dataFile); CaretAssert(metricFile != NULL); CaretAssert(metricFile->getNumberOfNodes() == numNodes); useData = metricFile; break; } case DataFileTypeEnum::CONNECTIVITY_DENSE_SCALAR: case DataFileTypeEnum::CONNECTIVITY_DENSE_TIME_SERIES: { tempData.setNumberOfNodesAndColumns(numNodes, 1); tempData.setStructure(surface->getStructure()); tempRoi.setNumberOfNodesAndColumns(numNodes, 1); const CiftiMappableDataFile* ciftiMappableFile = dynamic_cast(dataFile); CaretAssert(ciftiMappableFile != NULL); CaretAssert(ciftiMappableFile->getMappingSurfaceNumberOfNodes(surface->getStructure()) == numNodes); vector surfData, ciftiRoi; bool result = ciftiMappableFile->getMapDataForSurface(mapIndex, surface->getStructure(), surfData, &ciftiRoi); CaretAssert(result); if (!result) { CaretLogSevere("failed to get map data for map " + AString::number(mapIndex) + " of data file of type " + DataFileTypeEnum::toName(dataFile->getDataFileType())); return false; } tempData.setValuesForColumn(0, surfData.data()); vector maskedROI(numNodes); const float* gradRoiData = gradRoi->getValuePointerForColumn(0); for (int i = 0; i < numNodes; ++i) { if (gradRoiData[i] > 0.0f) { maskedROI[i] = ciftiRoi[i]; } else { maskedROI[i] = 0.0f; } } tempRoi.setValuesForColumn(0, maskedROI.data()); useRoi = &tempRoi; break; } case DataFileTypeEnum::CONNECTIVITY_DENSE: { const CiftiMappableDataFile* ciftiMappableFile = dynamic_cast(dataFile); CaretAssert(ciftiMappableFile != NULL); CaretAssert(ciftiMappableFile->getMappingSurfaceNumberOfNodes(surface->getStructure()) == numNodes); const CiftiFile* dataCifti = ciftiMappableFile->getCiftiFile(); const MetricFile* leftRoi = NULL, *rightRoi = NULL, *cerebRoi = NULL; const MetricFile* leftCorrAreas = NULL, *rightCorrAreas = NULL, *cerebCorrAreas = NULL; SurfaceFile* leftSurf = NULL, *rightSurf = NULL, *cerebSurf = NULL; switch (surface->getStructure()) { case StructureEnum::CORTEX_LEFT: leftRoi = gradRoi; leftCorrAreas = correctedAreasMetric; leftSurf = surface; break; case StructureEnum::CORTEX_RIGHT: rightRoi = gradRoi; rightCorrAreas = correctedAreasMetric; rightSurf = surface; break; case StructureEnum::CEREBELLUM: cerebRoi = gradRoi; cerebCorrAreas = correctedAreasMetric; cerebSurf = surface; break; default: CaretAssert(false); break; } CiftiFile restrictCifti, corrGradCifti; AlgorithmCiftiRestrictDenseMap(NULL, dataCifti, CiftiXML::ALONG_COLUMN, &restrictCifti, leftRoi, rightRoi, cerebRoi, NULL); AlgorithmCiftiCorrelationGradient(NULL, &restrictCifti, &corrGradCifti, leftSurf, rightSurf, cerebSurf, leftCorrAreas, rightCorrAreas, cerebCorrAreas, smoothing, 0.0f, false, false, excludeDist, -1.0f); AlgorithmCiftiSeparate(NULL, &corrGradCifti, CiftiXML::ALONG_COLUMN, surface->getStructure(), &gradientOut); return true; } default: CaretLogWarning("ignoring map " + AString::number(mapIndex) + " of data file of type " + DataFileTypeEnum::toName(dataFile->getDataFileType())); return false; } if (skipGradient) { gradientOut.setNumberOfNodesAndColumns(numNodes, 1); gradientOut.setStructure(surface->getStructure()); gradientOut.setValuesForColumn(0, useData->getValuePointerForColumn(0)); } else { AlgorithmMetricGradient(NULL, surface, useData, &gradientOut, NULL, smoothing, useRoi, false, 0, correctedAreasMetric); } return true; } bool getStatisticsString(const CaretMappableDataFile* dataFile, const int32_t& mapIndex, const vector nodeLists[2], const SurfaceFile& surface, const MetricFile* correctedAreasMetric, const float& excludeDist, AString& statsOut) { vector tempStatsStore, roiData; StructureEnum::Enum structure = surface.getStructure(); int numNodes = surface.getNumberOfNodes(); const float* statsData = NULL; switch (dataFile->getDataFileType()) { case DataFileTypeEnum::METRIC: { const MetricFile* metricFile = dynamic_cast(dataFile); CaretAssert(metricFile != NULL); statsData = metricFile->getValuePointerForColumn(mapIndex); break; } case DataFileTypeEnum::CONNECTIVITY_DENSE_SCALAR: case DataFileTypeEnum::CONNECTIVITY_DENSE_TIME_SERIES: { const CiftiMappableDataFile* ciftiMappableFile = dynamic_cast(dataFile); CaretAssert(ciftiMappableFile != NULL); bool result = ciftiMappableFile->getMapDataForSurface(mapIndex, structure, tempStatsStore, &roiData); CaretAssert(result); if (!result) { CaretLogSevere("failed to get map data for map " + AString::number(mapIndex) + " of data file of type " + DataFileTypeEnum::toName(dataFile->getDataFileType())); return false; } statsData = tempStatsStore.data(); break; } case DataFileTypeEnum::CONNECTIVITY_DENSE: { const CiftiMappableDataFile* ciftiMappableFile = dynamic_cast(dataFile); CaretAssert(ciftiMappableFile != NULL); const CiftiFile* dataCifti = ciftiMappableFile->getCiftiFile(); const CiftiXML& myXML = dataCifti->getCiftiXML(); const CiftiBrainModelsMap& myDenseMap = myXML.getBrainModelsMap(CiftiXML::ALONG_ROW); CaretAssert(myDenseMap.hasSurfaceData(structure));//the GUI should filter by structure, right? int64_t rowLength = myXML.getDimensionLength(CiftiXML::ALONG_ROW); vector > data[2]; for (int i = 0; i < 2; ++i) { data[i].resize(nodeLists[i].size()); for (int j = 0; j < (int)nodeLists[i].size(); ++j) { int64_t index = myDenseMap.getIndexForNode(nodeLists[i][j], structure); if (index != -1)//roi can go outside the cifti ROI, ignore such vertices { data[i][j].resize(rowLength);//only allocate the ones inside the cifti ROI dataCifti->getRow(data[i][j].data(), index); } } } vector samples[2][2]; CaretPointer myGeoBase; if (correctedAreasMetric != NULL) { myGeoBase.grabNew(new GeodesicHelperBase(&surface, correctedAreasMetric->getValuePointerForColumn(0))); } for (int posSide = 0; posSide < 2; ++posSide) { int listSize = (int)nodeLists[posSide].size(); #pragma omp CARET_PAR { CaretPointer myGeoHelp; if (correctedAreasMetric != NULL) { myGeoHelp.grabNew(new GeodesicHelper(myGeoBase)); } else { myGeoHelp = surface.getGeodesicHelper(); } #pragma omp CARET_FOR schedule(dynamic) for (int i = 0; i < listSize; ++i) { if (!data[posSide][i].empty()) { vector excludeNodes; vector excludeDists; myGeoHelp->getNodesToGeoDist(nodeLists[posSide][i], excludeDist, excludeNodes, excludeDists); vector excludeLookup(numNodes, 0); for (int j = 0; j < (int)excludeNodes.size(); ++j) { excludeLookup[excludeNodes[j]] = 1; } for (int side = 0; side < 2; ++side) { vector averagerow(rowLength, 0.0); int count = 0; for (int j = 0; j < (int)nodeLists[side].size(); ++j) { if (excludeLookup[nodeLists[side][j]] == 0 && !data[side][j].empty()) { ++count; float* dataRef = data[side][j].data(); for (int k = 0; k < rowLength; ++k) { averagerow[k] += dataRef[k]; } } } if (count != 0) { double accum1 = 0.0, accum2 = 0.0; for (int k = 0; k < rowLength; ++k) { averagerow[k] /= count; accum1 += averagerow[k]; accum2 += data[posSide][i][k]; } float mean1 = accum1 / rowLength, mean2 = accum2 / rowLength; accum1 = 0.0, accum2 = 0.0; double accum3 = 0.0; for (int k = 0; k < rowLength; ++k) { float val1 = averagerow[k] - mean1, val2 = data[posSide][i][k] - mean2; accum1 += val1 * val2; accum2 += val1 * val1; accum3 += val2 * val2; } double corrval = accum1 / sqrt(accum2 * accum3); if (corrval >= 0.999999) corrval = 0.999999;//prevent inf if (corrval <= -0.999999) corrval = -0.999999;//prevent -inf #pragma omp critical { samples[posSide][side].push_back(0.5f * log((1 + corrval)/(1 - corrval)));//fisher z transform } } } } } } } float tstats[2], cohen_ds[2], pvals[2]; for (int piece = 0; piece < 2; ++piece) { int count[2]; float mean[2], variance[2]; for (int posSide = 0; posSide < 2; ++posSide) { count[posSide] = (int)samples[posSide][piece].size(); if (count[posSide] < 2) return false; double accum = 0.0; for (int i = 0; i < count[posSide]; ++i) { accum += samples[posSide][piece][i]; } mean[posSide] = accum / count[posSide]; accum = 0.0; for (int i = 0; i < count[posSide]; ++i) { float tempf = samples[posSide][piece][i] - mean[posSide]; accum += tempf * tempf; } variance[posSide] = accum / (count[posSide] - 1); } tstats[piece] = (mean[0] - mean[1]) / sqrt(variance[0] / count[0] + variance[1] / count[1]);//welch's t-test cohen_ds[piece] = (mean[0] - mean[1]) / sqrt(((count[0] - 1) * variance[0] + (count[1] - 1) * variance[1]) / (count[0] + count[1] - 2)); pvals[piece] = 2.0f * MathFunctions::q_func(abs(tstats[piece]));//treat as 2-tailed z-stat, this isn't meant to be rigorous, and the t-test cdf is a pain } statsOut = "p1=" + AString::number(pvals[0], 'g', 3) + ", p2=" + AString::number(pvals[1], 'g', 3) + ", t1=" + AString::number(tstats[0], 'g', 3) + ", t2=" + AString::number(tstats[1], 'g', 3) + ", d1=" + AString::number(cohen_ds[0], 'g', 3) + ", d2=" + AString::number(cohen_ds[1], 'g', 3); return true; } default: CaretLogWarning("statistics: ignoring data file of type " + DataFileTypeEnum::toName(dataFile->getDataFileType())); return false; } float mean[2], variance[2]; int count[2] = {0, 0}; for (int piece = 0; piece < 2; ++piece) { double accum = 0.0; for (int i = 0; i < (int)nodeLists[piece].size(); ++i) { if (roiData.empty() || roiData[nodeLists[piece][i]] > 0.0f) { accum += statsData[nodeLists[piece][i]];//weight by vertex area? weighted welch's t-test, oh joy ++count[piece]; } } mean[piece] = accum / count[piece]; accum = 0.0; for (int i = 0; i < (int)nodeLists[piece].size(); ++i) { if (roiData.empty() || roiData[nodeLists[piece][i]] > 0.0f) { float tempf = (statsData[nodeLists[piece][i]] - mean[piece]); accum += tempf * tempf; } } variance[piece] = accum / (count[piece] - 1); } float tstat = (mean[0] - mean[1]) / sqrt(variance[0] / count[0] + variance[1] / count[1]);//welch's t-test float cohen_d = (mean[0] - mean[1]) / sqrt(((count[0] - 1) * variance[0] + (count[1] - 1) * variance[1]) / (count[0] + count[1] - 2)); float pval = 2.0f * MathFunctions::q_func(abs(tstat));//treat as 2-tailed z-stat, this isn't meant to be rigorous, and the t-test cdf is a pain statsOut = "p=" + AString::number(pval, 'g', 3) + ", t=" + AString::number(tstat, 'g', 3) + ", d=" + AString::number(cohen_d, 'g', 3); return true; } int getBorderPointNode(const Border* theBorder, const int& index) { CaretAssert(index >= 0 && index < theBorder->getNumberOfPoints()); const SurfaceProjectionBarycentric* thisBary = theBorder->getPoint(index)->getBarycentricProjection(); CaretAssert(thisBary != NULL && thisBary->isValid()); int thisNode = thisBary->getNodeWithLargestWeight(); CaretAssert(thisNode >= 0); return thisNode; } struct BorderRedrawInfo { int startpoint, endpoint; }; } /** * Run the border optimization algorithm. * * @param inputData * Input data for the algorithm. * @param statisticsInformationOut * Output containing statistics information. * @param errorMessageOut * Output containing error information when false returned. * @return * True if successful, else false. */ bool BorderOptimizeExecutor::run(const InputData& inputData, AString& statisticsInformationOut, AString& errorMessageOut) { AString stageString = "initializing"; try { statisticsInformationOut.clear(); errorMessageOut.clear(); printInputs(inputData); SurfaceFile* computeSurf = inputData.m_surface; const MetricFile* correctedAreasMetric = inputData.m_vertexAreasMetricFile; int32_t numNodes = computeSurf->getNumberOfNodes(); int numSelected = (int)inputData.m_nodesInsideROI.size(); if (numSelected < 1) { errorMessageOut = "no nodes selected"; return false; } vector roiData(numNodes, 0.0f); vector combinedGradData(numNodes, 0.0f); for (int i = 0; i < numSelected; ++i) { roiData[inputData.m_nodesInsideROI[i]] = 1.0f; combinedGradData[inputData.m_nodesInsideROI[i]] = 1.0f;//also initialize only the inside-roi parts of the gradient combining function, leaving the outside 0 } int numBorders = (int)inputData.m_borders.size(); vector myRedrawInfo(numBorders); const int PROGRESS_MAX = 100; const int SEGMENT_PROGRESS = 10; const int COMPUTE_PROGRESS = 55; const int HELPER_PROGRESS = 5; const int DRAW_PROGRESS = 10; const int STATISTICS_PROGRESS = 20; CaretAssert(SEGMENT_PROGRESS + COMPUTE_PROGRESS + HELPER_PROGRESS + DRAW_PROGRESS + STATISTICS_PROGRESS == PROGRESS_MAX); stageString = "border segment locating"; for (int i = 0; i < numBorders; ++i) {//find pieces of border to redraw, before doing gradient, so it can error early Border* thisBorder = inputData.m_borders[i]; EventProgressUpdate tempEvent(0, PROGRESS_MAX, (SEGMENT_PROGRESS * i) / numBorders, "finding in-roi segment of border '" + thisBorder->getName() + "'"); EventManager::get()->sendEvent(&tempEvent); if (tempEvent.isCancelled()) { errorMessageOut = "cancelled by user"; return false; } int numPoints = thisBorder->getNumberOfPoints(); int start, end; if (thisBorder->isClosed()) { if (numPoints < 3)//one outside, two inside will work { errorMessageOut = "Border '" + thisBorder->getName() + "' has too few points to adjust"; return false; } for (start = numPoints - 1; start >= 0; --start)//search backwards for a point outside the roi { if (!(roiData[getBorderPointNode(thisBorder, start)] > 0.0f)) break; } if (start == -1)//no points outside ROI { errorMessageOut = "Border '" + thisBorder->getName() + "' has no points outside the ROI"; return false; } int added; for (added = 1; added < numPoints; ++added)//search forward from it to find the point inside the roi - if the above loop had to loop, this will end on the first iteration { int pointIndex = (start + added) % numPoints;//closed border requires mod arithmetic if (roiData[getBorderPointNode(thisBorder, pointIndex)] > 0.0f) break; } if (added == numPoints)//no points inside ROI {//NOTE: if multipart borders are used, and passed borders don't check the parts for being in/out, then this name is not unique errorMessageOut = "Border '" + thisBorder->getName() + "' has no points inside the ROI"; return false; } start = (start + added) % numPoints; for (added = 1; added < numPoints; ++added) { int pointIndex = (start + added) % numPoints; if (!(roiData[getBorderPointNode(thisBorder, pointIndex)] > 0.0f)) break; } CaretAssert(added != numPoints);//we have points inside and outside roi, this search should have stopped end = (start + added - 1) % numPoints;//we will check this for being equal to start later, first check for multiple sections for (; added < numPoints; ++added) { int pointIndex = (start + added) % numPoints; if (roiData[getBorderPointNode(thisBorder, pointIndex)] > 0.0f) { errorMessageOut = "Border '" + thisBorder->getName() + "' has multiple sections inside the ROI"; return false; } } if (end == start) { errorMessageOut = "Border '" + thisBorder->getName() + "' has only one point inside the ROI"; return false; } if (getBorderPointNode(thisBorder, start) == getBorderPointNode(thisBorder, end)) { errorMessageOut = "Border '" + thisBorder->getName() + "' enters and exits the ROI too close to the same vertex"; return false; } } else {//open border if (numPoints < 2)//two, both inside, will work { errorMessageOut = "Border '" + thisBorder->getName() + "' has too few points to adjust"; return false; } for (start = 0; start < numPoints; ++start)//search for first point inside { if (roiData[getBorderPointNode(thisBorder, start)] > 0.0f) break; } if (start == numPoints) {//NOTE: if multipart borders are used, and passed borders don't check the parts for being in/out, then this name is not unique errorMessageOut = "Border '" + thisBorder->getName() + "' has no points inside the ROI"; return false; } for (end = start + 1; end < numPoints; ++end)//search for first point outside { if (!(roiData[getBorderPointNode(thisBorder, end)] > 0.0f)) break; } --end;//and subtract one to get inclusive range, also deals with == numPoints for (int check = end + 2; check < numPoints; ++check)//look for a second piece inside { if (roiData[getBorderPointNode(thisBorder, check)] > 0.0f) { errorMessageOut = "Border '" + thisBorder->getName() + "' has multiple sections inside the ROI"; return false; } } } myRedrawInfo[i].startpoint = start; myRedrawInfo[i].endpoint = end; } stageString = "data processing"; int numInputs = (int)inputData.m_dataFileInfo.size(); vector inputRoiData(numNodes, 0.0f); for (int i = 0; i < (int)inputData.m_nodesInsideROI.size(); ++i) { inputRoiData[inputData.m_nodesInsideROI[i]] = 1.0f; } MetricFile inputRoi, dilatedRoi; inputRoi.setNumberOfNodesAndColumns(numNodes, 1); inputRoi.setValuesForColumn(0, inputRoiData.data()); AlgorithmMetricDilate(NULL, &inputRoi, computeSurf, 0.0001f, &dilatedRoi);//dilate roi by 1 neighbor for (int i = 0; i < numInputs; ++i) { EventProgressUpdate tempEvent(0, PROGRESS_MAX, SEGMENT_PROGRESS + (COMPUTE_PROGRESS * i) / numInputs, "processing data file '" + inputData.m_dataFileInfo[i].m_mapFile->getFileNameNoPath() + "'"); EventManager::get()->sendEvent(&tempEvent); if (tempEvent.isCancelled()) { errorMessageOut = "cancelled by user"; return false; } MetricFile tempGradient; if (inputData.m_dataFileInfo[i].m_allMapsFlag) { for (int j = 0; j < inputData.m_dataFileInfo[i].m_mapFile->getNumberOfMaps(); ++j) { if (extractGradientData(inputData.m_dataFileInfo[i].m_mapFile, j, computeSurf, &dilatedRoi, inputData.m_dataFileInfo[i].m_smoothing, correctedAreasMetric, tempGradient, inputData.m_dataFileInfo[i].m_skipGradient, inputData.m_dataFileInfo[i].m_corrGradExcludeDist)) { doCombination(tempGradient, inputData.m_nodesInsideROI, inputData.m_dataFileInfo[i].m_invertGradientFlag, inputData.m_dataFileInfo[i].m_weight, combinedGradData); } } } else { if (extractGradientData(inputData.m_dataFileInfo[i].m_mapFile, inputData.m_dataFileInfo[i].m_mapIndex, computeSurf, &dilatedRoi, inputData.m_dataFileInfo[i].m_smoothing, correctedAreasMetric, tempGradient, inputData.m_dataFileInfo[i].m_skipGradient, inputData.m_dataFileInfo[i].m_corrGradExcludeDist)) { doCombination(tempGradient, inputData.m_nodesInsideROI, inputData.m_dataFileInfo[i].m_invertGradientFlag, inputData.m_dataFileInfo[i].m_weight, combinedGradData); } } } stageString = "border drawing"; if (inputData.m_combinedGradientDataOut != NULL) { inputData.m_combinedGradientDataOut->setNumberOfNodesAndColumns(numNodes, 1); inputData.m_combinedGradientDataOut->setStructure(computeSurf->getStructure()); inputData.m_combinedGradientDataOut->setValuesForColumn(0, combinedGradData.data()); } SurfaceFile* drawSurf = computeSurf, *origSphere = inputData.m_upsamplingSphericalSurface, highresSphere, highresMidthick; vector drawGrad = combinedGradData, drawRoi = roiData; vector origAreasStore, highresAreasStore; const float* origAreas = NULL; if (correctedAreasMetric != NULL) { origAreas = correctedAreasMetric->getValuePointerForColumn(0); } const float* drawAreas = origAreas; if (origSphere != NULL) { if (inputData.m_upsamplingSphericalSurface->getNumberOfNodes() >= inputData.m_upsamplingResolution) { errorMessageOut = "upsampling number of vertices must be greater than current vertex count"; return false; } AlgorithmSurfaceCreateSphere(NULL, inputData.m_upsamplingResolution, &highresSphere); int highresNumNodes = highresSphere.getNumberOfNodes(); highresSphere.setStructure(origSphere->getStructure()); AlgorithmSurfaceResample(NULL, computeSurf, origSphere, &highresSphere, SurfaceResamplingMethodEnum::BARYCENTRIC, &highresMidthick); if (origAreas == NULL) { computeSurf->computeNodeAreas(origAreasStore); origAreas = origAreasStore.data(); highresMidthick.computeNodeAreas(highresAreasStore); } else { vector origRatioStore(origAreas, origAreas + numNodes), highresRatioStore(highresNumNodes), wrongAreas, highresWrongAreas; computeSurf->computeNodeAreas(wrongAreas);//to get high res corrected areas, convert to expansion ratio, highresMidthick.computeNodeAreas(highresWrongAreas);//then resample and multiply by the high res surface areas for (int i = 0; i < numNodes; ++i) { origRatioStore[i] /= wrongAreas[i]; }//we don't have anything high res but the wrong areas yet, so use like area measures - expansion ratio should be fairly smooth anyway, so not as important SurfaceResamplingHelper initialUpsampler(SurfaceResamplingMethodEnum::ADAP_BARY_AREA, origSphere, &highresSphere, wrongAreas.data(), highresWrongAreas.data()); initialUpsampler.resampleNormal(origRatioStore.data(), highresRatioStore.data()); highresAreasStore.resize(highresNumNodes); for (int i = 0; i < highresNumNodes; ++i) { highresAreasStore[i] = highresRatioStore[i] * highresWrongAreas[i]; } } drawSurf = &highresMidthick; drawAreas = highresAreasStore.data(); drawGrad.resize(highresNumNodes); drawRoi.resize(highresNumNodes); SurfaceResamplingHelper finalUpsampler(SurfaceResamplingMethodEnum::ADAP_BARY_AREA, origSphere, &highresSphere, origAreas, highresAreasStore.data(), roiData.data()); finalUpsampler.resampleNormal(combinedGradData.data(), drawGrad.data()); finalUpsampler.getResampleValidROI(drawRoi.data()); } { EventProgressUpdate tempEvent(0, PROGRESS_MAX, SEGMENT_PROGRESS + COMPUTE_PROGRESS, "generating geodesic helper"); EventManager::get()->sendEvent(&tempEvent); if (tempEvent.isCancelled()) { errorMessageOut = "cancelled by user"; return false; } } CaretPointer myGeoHelp; CaretPointer myGeoBase; if (correctedAreasMetric != NULL) { myGeoBase.grabNew(new GeodesicHelperBase(drawSurf, drawAreas)); myGeoHelp.grabNew(new GeodesicHelper(myGeoBase)); } else { myGeoHelp = drawSurf->getGeodesicHelper(); } CaretPointer myTopoHelp = drawSurf->getTopologyHelper(); vector drawOrigBorders = inputData.m_borders; BorderFile highresOrigBorders; if (origSphere != NULL) {//first, copy all borders and resample to get endpoint positions on high res mesh, then draw new segments as borders and downsample just the segments BorderFile origBorders; for (int i = 0; i < numBorders; ++i) { origBorders.addBorder(new Border(*(inputData.m_borders[i]))); } AlgorithmBorderResample(NULL, &origBorders, origSphere, &highresSphere, &highresOrigBorders);//NOTE: this must keep each border and point intact and in the same order, just on the new sphere for (int i = 0; i < numBorders; ++i) { drawOrigBorders[i] = highresOrigBorders.getBorder(i); } } vector roiMinusTracesData = drawRoi; BorderFile redrawnSegments; for (int i = 0; i < numBorders; ++i) { EventProgressUpdate tempEvent(0, PROGRESS_MAX, SEGMENT_PROGRESS + COMPUTE_PROGRESS + HELPER_PROGRESS + (DRAW_PROGRESS * i) / numBorders, "redrawing segment of border '" + inputData.m_borders[i]->getName() + "'");//use non-resampled borders for name, just in case EventManager::get()->sendEvent(&tempEvent); if (tempEvent.isCancelled()) { errorMessageOut = "cancelled by user"; return false; } vector nodes; vector dists; myGeoHelp->getPathFollowingData(getBorderPointNode(drawOrigBorders[i], myRedrawInfo[i].startpoint), getBorderPointNode(drawOrigBorders[i], myRedrawInfo[i].endpoint), drawGrad.data(), nodes, dists, inputData.m_gradientFollowingStrength, drawRoi.data(), true, true); if (nodes.size() < 3)//require at least 1 surviving point after removing endpoints { errorMessageOut = "Unable to redraw border segment for border '" + inputData.m_borders[i]->getName() + "'"; return false; } CaretPointer redrawnSegment(new Border()); for (int j = 1; j < (int)nodes.size() - 1; ++j)//drop the closest node to the start and end points from the redrawn segment { const vector& nodeTiles = myTopoHelp->getNodeTiles(nodes[j]); CaretAssert(!nodeTiles.empty()); const int32_t* tileNodes = drawSurf->getTriangle(nodeTiles[0]); int whichNode; for (whichNode = 0; whichNode < 3; ++whichNode) { if (tileNodes[whichNode] == nodes[j]) break; } CaretAssert(whichNode < 3);//it should always find it float weights[3] = { 0.0f, 0.0f, 0.0f }; weights[whichNode] = 1.0f; SurfaceProjectedItem* myItem = new SurfaceProjectedItem(); myItem->getBarycentricProjection()->setTriangleNodes(tileNodes);//none of these should throw myItem->getBarycentricProjection()->setTriangleAreas(weights); myItem->getBarycentricProjection()->setValid(true); myItem->setStructure(computeSurf->getStructure()); redrawnSegment->addPoint(myItem); } redrawnSegments.addBorder(redrawnSegment.releasePointer()); int numOrigPoints = inputData.m_borders[i]->getNumberOfPoints(); Border fullRedrawn = *(drawOrigBorders[i]);//use the potentially upsampled version for the ROI splitting, but don't re-downsample it as the modified border on the current mesh fullRedrawn.removeAllPoints(); if (!(inputData.m_borders[i]->isClosed()))//if it is a closed border, start with the newly drawn section, for simplicity { for (int j = 0; j <= myRedrawInfo[i].startpoint; ++j)//include the original startpoint { fullRedrawn.addPoint(new SurfaceProjectedItem(*(drawOrigBorders[i]->getPoint(j)))); } } fullRedrawn.addPoints(redrawnSegment); if (inputData.m_borders[i]->isClosed()) {//mod arithmetic for closed borders int numKeep = ((numOrigPoints + myRedrawInfo[i].startpoint - myRedrawInfo[i].endpoint) % numOrigPoints) + 1;//inclusive for (int j = 0; j < numKeep; ++j) { fullRedrawn.addPoint(new SurfaceProjectedItem(*(drawOrigBorders[i]->getPoint((j + myRedrawInfo[i].endpoint) % numOrigPoints)))); } } else { for (int j = myRedrawInfo[i].endpoint; j < numOrigPoints; ++j)//include original endpoint { fullRedrawn.addPoint(new SurfaceProjectedItem(*(drawOrigBorders[i]->getPoint(j)))); } } MetricFile borderTrace; BorderFile tempBorderFile; tempBorderFile.addBorder(new Border(fullRedrawn));//because it takes ownership of a pointer AlgorithmBorderToVertices(NULL, drawSurf, &tempBorderFile, &borderTrace); const float* traceData = borderTrace.getValuePointerForColumn(0); int drawNumNodes = drawSurf->getNumberOfNodes(); for (int j = 0; j < drawNumNodes; ++j) { if (traceData[j] > 0.0f) { roiMinusTracesData[j] = 0.0f; } } } BorderFile* segmentsToUse = &redrawnSegments; BorderFile downsampledSegments; if (origSphere != NULL) { AlgorithmBorderResample(NULL, &redrawnSegments, &highresSphere, origSphere, &downsampledSegments); segmentsToUse = &downsampledSegments; } vector modifiedBorders(numBorders);//modify all without replacing so we can error before changing any for (int i = 0; i < numBorders; ++i) { Border& modifiedBorder = modifiedBorders[i]; modifiedBorder = *(inputData.m_borders[i]); int numOrigPoints = inputData.m_borders[i]->getNumberOfPoints(); modifiedBorder.removeAllPoints();//keep name, color, class, etc if (!(inputData.m_borders[i]->isClosed()))//if it is a closed border, start with the newly drawn section, for simplicity { for (int j = 0; j <= myRedrawInfo[i].startpoint; ++j)//include the original startpoint { modifiedBorder.addPoint(new SurfaceProjectedItem(*(inputData.m_borders[i]->getPoint(j)))); } } modifiedBorder.addPoints(segmentsToUse->getBorder(i)); if (inputData.m_borders[i]->isClosed()) {//mod arithmetic for closed borders int numKeep = ((numOrigPoints + myRedrawInfo[i].startpoint - myRedrawInfo[i].endpoint) % numOrigPoints) + 1;//inclusive for (int j = 0; j < numKeep; ++j) { modifiedBorder.addPoint(new SurfaceProjectedItem(*(inputData.m_borders[i]->getPoint((j + myRedrawInfo[i].endpoint) % numOrigPoints)))); } } else { for (int j = myRedrawInfo[i].endpoint; j < numOrigPoints; ++j)//include original endpoint { modifiedBorder.addPoint(new SurfaceProjectedItem(*(inputData.m_borders[i]->getPoint(j)))); } } } stageString = "roi splitting"; MetricFile roiMinusBorderTraces, roiMetric, drawAreasMetric; roiMinusBorderTraces.setNumberOfNodesAndColumns(roiMinusTracesData.size(), 1); roiMinusBorderTraces.setValuesForColumn(0, roiMinusTracesData.data()); roiMetric.setNumberOfNodesAndColumns(drawRoi.size(), 1); roiMetric.setValuesForColumn(0, drawRoi.data()); drawAreasMetric.setNumberOfNodesAndColumns(drawSurf->getNumberOfNodes(), 1); drawAreasMetric.setValuesForColumn(0, drawAreas); MetricFile clustersMetric; int endVal = 0; AlgorithmMetricFindClusters(NULL, drawSurf, &roiMinusBorderTraces, 0.5f, 10.0f, &clustersMetric, false, &roiMetric, &drawAreasMetric, 0, 1, &endVal); if (endVal != 3) { statisticsInformationOut = AString::number(endVal - 1) + " cluster(s) found after splitting the roi with the redrawn borders, skipping statistics"; } else { const float* clusterData = clustersMetric.getValuePointerForColumn(0); vector downsampledClusters; if (origSphere != NULL) { SurfaceResamplingHelper downsampler(SurfaceResamplingMethodEnum::ADAP_BARY_AREA, &highresSphere, origSphere, highresAreasStore.data(), origAreas, drawRoi.data()); vector highresData(highresSphere.getNumberOfNodes()), downsamledData(numNodes); const float* highresClusters = clustersMetric.getValuePointerForColumn(0); for (int i = 0; i < (int)highresData.size(); ++i) { highresData[i] = floor(highresClusters[i] + 0.5f); } downsampler.resamplePopular(highresData.data(), downsamledData.data()); downsampledClusters.resize(numNodes); for (int i = 0; i < (int)downsamledData.size(); ++i) { downsampledClusters[i] = downsamledData[i]; } clusterData = downsampledClusters.data(); } stageString = "statistics"; vector nodeLists[2]; for (int i = 0; i < numNodes; ++i) { int label = (int)floor(clusterData[i] + 0.5f); CaretAssert(label < 3); if (label > 0) nodeLists[label - 1].push_back(i); } vector orderedNodeLists[2]; if (nodeLists[0].size() >= nodeLists[1].size()) { orderedNodeLists[0] = nodeLists[0]; orderedNodeLists[1] = nodeLists[1]; } else { orderedNodeLists[0] = nodeLists[1]; orderedNodeLists[1] = nodeLists[0]; } if (inputData.m_borderPair.size() == 2) { vector insideBorderNodes; AlgorithmNodesInsideBorder(NULL, computeSurf, inputData.m_borderPair[0], false, insideBorderNodes); vector insideLookup(numNodes, 0); for (int i = 0; i < (int)insideBorderNodes.size(); ++i) { insideLookup[insideBorderNodes[i]] = 1; } int counts[2] = {0, 0}; for (int whichList = 0; whichList < 2; ++whichList) { for (int i = 0; i < (int)orderedNodeLists[whichList].size(); ++i) { if (insideLookup[orderedNodeLists[whichList][i]] != 0) { ++counts[whichList]; } } } if (counts[0] >= counts[1]) { statisticsInformationOut += inputData.m_borderPair[0]->getName() + " n=" + AString::number(orderedNodeLists[0].size()) + ", " + inputData.m_borderPair[1]->getName() + " n=" + AString::number(orderedNodeLists[1].size()) + "\n\n"; } else { statisticsInformationOut += inputData.m_borderPair[1]->getName() + " n=" + AString::number(orderedNodeLists[0].size()) + ", " + inputData.m_borderPair[0]->getName() + " n=" + AString::number(orderedNodeLists[1].size()) + "\n\n"; } if (orderedNodeLists[0].size() < 2 || orderedNodeLists[1].size() < 2) { statisticsInformationOut += "roi pieces are too small for statistics"; } else { for (int i = 0; i < numInputs; ++i) { EventProgressUpdate tempEvent(0, PROGRESS_MAX, SEGMENT_PROGRESS + COMPUTE_PROGRESS + HELPER_PROGRESS + DRAW_PROGRESS + (STATISTICS_PROGRESS * i) / numInputs, "computing statistics on file '" + inputData.m_dataFileInfo[i].m_mapFile->getFileNameNoPath() + "'"); EventManager::get()->sendEvent(&tempEvent); if (tempEvent.isCancelled()) { errorMessageOut = "cancelled by user"; return false; } if (inputData.m_dataFileInfo[i].m_allMapsFlag) { for (int j = 0; j < inputData.m_dataFileInfo[i].m_mapFile->getNumberOfMaps(); ++j) { AString statsOut; if(getStatisticsString(inputData.m_dataFileInfo[i].m_mapFile, j, orderedNodeLists, *computeSurf, correctedAreasMetric, inputData.m_dataFileInfo[i].m_corrGradExcludeDist, statsOut)) { statisticsInformationOut += statsOut + ": " + inputData.m_dataFileInfo[i].m_mapFile->getMapName(inputData.m_dataFileInfo[i].m_mapIndex) + ", " + inputData.m_dataFileInfo[i].m_mapFile->getFileNameNoPath() + ", " + FileInformation(inputData.m_dataFileInfo[i].m_mapFile->getFileName()).getLastDirectory() + "\n"; } } statisticsInformationOut += "\n"; } else { AString statsOut; if(getStatisticsString(inputData.m_dataFileInfo[i].m_mapFile, inputData.m_dataFileInfo[i].m_mapIndex, orderedNodeLists, *computeSurf, correctedAreasMetric, inputData.m_dataFileInfo[i].m_corrGradExcludeDist, statsOut)) { statisticsInformationOut += statsOut + ": " + inputData.m_dataFileInfo[i].m_mapFile->getMapName(inputData.m_dataFileInfo[i].m_mapIndex) + ", " + inputData.m_dataFileInfo[i].m_mapFile->getFileNameNoPath() + ", " + FileInformation(inputData.m_dataFileInfo[i].m_mapFile->getFileName()).getLastDirectory() + "\n\n"; } } } } } } stageString = "border replacing"; for (int i = 0; i < numBorders; ++i)//now replace the borders with the modified ones { inputData.m_borders[i]->replacePointsWithUndoSaving(&modifiedBorders[i]); } if (inputData.m_saveResults) { saveResults(inputData, statisticsInformationOut); } /* * Modifying a border: * * (1) Make a copy of the border * * Border* borderCopy = new Border(*border) * * (2) Modify the 'copied' border * * (3) When modification is complete, calling this method * will first make an 'undo' copy of 'border' that is stored * inside of border and then replace the points in 'border' * with those from 'borderCopy'. This will allow the * user to press the Border ToolBar's 'Undo Finish' button * if the changes are not acceptable. * * border->replacePointsWithUndoSaving(borderCopy) */ } catch (CaretException& e) {//all exceptions are errors errorMessageOut = "Caught exception during " + stageString + " stage: " + e.whatString(); return false; } return true; } void BorderOptimizeExecutor::saveResults(const BorderOptimizeExecutor::InputData& inputData, const AString& statisticsInformation) { AString completeBase = FileInformation(inputData.m_savingPath, inputData.m_savingBaseName).getAbsoluteFilePath(); BorderFile tempOutBorder; tempOutBorder.setNumberOfNodes(inputData.m_surface->getNumberOfNodes()); tempOutBorder.addBorder(new Border(*(inputData.m_borderEnclosingROI))); tempOutBorder.writeFile(completeBase + ".border"); if (inputData.m_combinedGradientDataOut != NULL) { inputData.m_combinedGradientDataOut->writeFile(completeBase + ".func.gii"); } TextFile textOut;//because QT's paths use non-native separators on windows textOut.replaceText(statisticsInformation); textOut.addLine("\n\nBorders chosen to optimize:"); for (std::vector::const_iterator bi = inputData.m_borders.begin(); bi != inputData.m_borders.end(); bi++) { textOut.addLine((*bi)->getName()); } textOut.addLine("\nBorder pair for statistics:"); for (std::vector::const_iterator bi = inputData.m_borderPair.begin(); bi != inputData.m_borderPair.end(); bi++) { textOut.addLine((*bi)->getName()); } textOut.addLine("\nOptimizing Surface: " + inputData.m_surface->getFileName()); textOut.addLine("\nData Files used:"); for (std::vector::const_iterator fi = inputData.m_dataFileInfo.begin(); fi != inputData.m_dataFileInfo.end(); fi++) { const DataFileInfo& dfi = *fi; textOut.addLine("\n" + dfi.m_mapFile->getFileName()); if (dfi.m_allMapsFlag) { textOut.addLine(" Map: All Maps"); } else { textOut.addLine(" Map: " + AString::number(dfi.m_mapIndex) + ", " + dfi.m_mapFile->getMapName(dfi.m_mapIndex)); } textOut.addLine(" Strength: " + AString::number(dfi.m_weight)); textOut.addLine(" Smoothing: " + AString::number(dfi.m_smoothing)); textOut.addLine(" Invert Gradient: " + AString::fromBool(dfi.m_invertGradientFlag)); } if (inputData.m_vertexAreasMetricFile != NULL) textOut.addLine("\nVertex Areas Metric File:\n" + inputData.m_vertexAreasMetricFile->getFileName()); if (inputData.m_upsamplingSphericalSurface != NULL) { textOut.addLine("\nUpsampling Sphere: " + inputData.m_upsamplingSphericalSurface->getFileName()); textOut.addLine("Upsampling Resolution: " + AString::number(inputData.m_upsamplingResolution)); } textOut.addLine("\nGradient Following Strength: " + AString::number(inputData.m_gradientFollowingStrength)); textOut.writeFile(completeBase + ".txt"); } /** * Run the border optimization algorithm. * * @param inputData * Input data for the algorithm. */ void BorderOptimizeExecutor::printInputs(const InputData& inputData) { std::cout << "Optimizing borders: " << std::endl; for (std::vector::const_iterator bi = inputData.m_borders.begin(); bi != inputData.m_borders.end(); bi++) { std::cout << " " << qPrintable((*bi)->getName()) << std::endl; } std::cout << "Border pair: " << std::endl; for (std::vector::const_iterator bi = inputData.m_borderPair.begin(); bi != inputData.m_borderPair.end(); bi++) { std::cout << " " << qPrintable((*bi)->getName()) << std::endl; } std::cout << "Optimizing Surface: " << qPrintable(inputData.m_surface->getFileNameNoPath()) << std::endl; std::cout << "Number of nodes in ROI: " << qPrintable(AString::number(inputData.m_nodesInsideROI.size())) << std::endl; std::cout << "Optimizing Data Files: " << std::endl; for (std::vector::const_iterator fi = inputData.m_dataFileInfo.begin(); fi != inputData.m_dataFileInfo.end(); fi++) { const DataFileInfo& dfi = *fi; std::cout << " Name: " << qPrintable(dfi.m_mapFile->getFileNameNoPath()) << std::endl; if (dfi.m_allMapsFlag) { std::cout << " Map: All Maps" << std::endl; } else { std::cout << " Map: " << qPrintable(AString::number(dfi.m_mapIndex)) << " " << qPrintable(dfi.m_mapFile->getMapName(dfi.m_mapIndex)) << std::endl; } 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 << "Vertex Areas Metric File: " << ((inputData.m_vertexAreasMetricFile != NULL) ? qPrintable(inputData.m_vertexAreasMetricFile->getFileNameNoPath()) : "NULL") << std::endl; if (inputData.m_upsamplingSphericalSurface != NULL) { std::cout << "Upsampling Sphere: " << qPrintable(inputData.m_upsamplingSphericalSurface->getFileNameNoPath()) << std::endl; std::cout << "Upsampling Resolution: " << qPrintable(QString::number(inputData.m_upsamplingResolution)) << std::endl; } std::cout << "Gradient Following Strength: " << inputData.m_gradientFollowingStrength << std::endl; } connectome-workbench-1.2.3+git41-gc4c6c90/src/GuiQt/BorderOptimizeExecutor.h000066400000000000000000000125251300200146000264300ustar00rootroot00000000000000#ifndef __BORDER_OPTIMIZE_EXECUTOR_H__ #define __BORDER_OPTIMIZE_EXECUTOR_H__ /*LICENSE_START*/ /* * Copyright (C) 2015 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 "CaretPointer.h" namespace caret { class Border; class CaretMappableDataFile; class MetricFile; class Surface; class BorderOptimizeExecutor : public CaretObject { public: /** * Info about the data files */ struct DataFileInfo { DataFileInfo(const CaretMappableDataFile* mapFile, const int32_t mapIndex, const bool allMapsFlag, const float smoothing, const float weight, const bool invertGradientFlag, const bool& skipGradient, const float& corrGradExcludeDist) : m_mapFile(mapFile), m_mapIndex(mapIndex), m_allMapsFlag(allMapsFlag), m_smoothing(smoothing), m_weight(weight), m_invertGradientFlag(invertGradientFlag), m_skipGradient(skipGradient), m_corrGradExcludeDist(corrGradExcludeDist) { } const CaretMappableDataFile* m_mapFile; int32_t m_mapIndex; bool m_allMapsFlag; float m_smoothing; float m_weight; bool m_invertGradientFlag; bool m_skipGradient; float m_corrGradExcludeDist; }; /** * Update data for the algorithm */ struct InputData { InputData(std::vector borders, std::vector borderPair, const Border* borderEnclosingROI, const std::vector& nodesInsideROI, Surface* surface, const std::vector& dataFileInfo, const MetricFile* vertexAreasMetricFile, const float& gradientFollowingStrength, Surface* upsamplingSphericalSurface, const int32_t upsamplingResolution, MetricFile* combinedGradientDataOut, bool saveResults, const AString& savingPath, const AString& savingBaseName) : m_borders(borders), m_borderPair(borderPair), m_borderEnclosingROI(borderEnclosingROI), m_nodesInsideROI(nodesInsideROI), m_surface(surface), m_dataFileInfo(dataFileInfo), m_vertexAreasMetricFile(vertexAreasMetricFile), m_gradientFollowingStrength(gradientFollowingStrength), m_upsamplingSphericalSurface(upsamplingSphericalSurface), m_upsamplingResolution(upsamplingResolution), m_combinedGradientDataOut(combinedGradientDataOut), m_saveResults(saveResults), m_savingPath(savingPath), m_savingBaseName(savingBaseName) { } std::vector m_borders; std::vector m_borderPair; const Border* m_borderEnclosingROI; const std::vector& m_nodesInsideROI; Surface* m_surface; const std::vector& m_dataFileInfo; const MetricFile* m_vertexAreasMetricFile; const float m_gradientFollowingStrength; Surface* m_upsamplingSphericalSurface; const int32_t m_upsamplingResolution; MetricFile* m_combinedGradientDataOut; bool m_saveResults; AString m_savingPath, m_savingBaseName; }; BorderOptimizeExecutor(); virtual ~BorderOptimizeExecutor(); static void printInputs(const InputData& inputData); static bool run(const InputData& inputData, AString& statisticsInformationOut, AString& errorMessageOut); static void saveResults(const InputData& inputData, const AString& statisticsInformation); private: BorderOptimizeExecutor(const BorderOptimizeExecutor&); BorderOptimizeExecutor& operator=(const BorderOptimizeExecutor&); // ADD_NEW_MEMBERS_HERE }; #ifdef __BORDER_OPTIMIZE_EXECUTOR_DECLARE__ // #endif // __BORDER_OPTIMIZE_EXECUTOR_DECLARE__ } // namespace #endif //__BORDER_OPTIMIZE_EXECUTOR_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/GuiQt/BorderPropertiesEditorDialog.cxx000066400000000000000000000565241300200146000301160ustar00rootroot00000000000000 /*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 __BORDER_PROPERTIES_EDITOR_DIALOG__DECLARE__ #include "BorderPropertiesEditorDialog.h" #undef __BORDER_PROPERTIES_EDITOR_DIALOG__DECLARE__ #include "Brain.h" #include "Border.h" #include "BorderFile.h" #include "CaretAssert.h" #include "CaretFileDialog.h" #include "EventDataFileAdd.h" #include "EventManager.h" #include "GroupAndNameHierarchyModel.h" #include "GiftiLabel.h" #include "GiftiLabelTable.h" #include "GiftiLabelTableEditor.h" #include "GiftiLabelTableSelectionComboBox.h" #include "GuiManager.h" #include "SurfaceFile.h" #include "WuQDataEntryDialog.h" #include "WuQMessageBox.h" #include "WuQtUtilities.h" using namespace caret; /** * \class caret::BorderPropertiesEditorDialog * \brief Dialog that queries user to finish drawing of a border. * * This dialog is displayed when the user finishes drawing a * border. It allows the user to select the border file, * enter the border name, select the color, enter the class, * set the type of border (open/closed), and * possibly other attributes of the border. */ /** * Create a new instance of the border properties editor for finishing * a border using a drawing mode. * * @param border * Border that was drawn. * @param parent * Parent on which this dialog is shown. * @return * Dialog that will finish the border. * Users MUST DELETE the returned dialog. */ BorderPropertiesEditorDialog* BorderPropertiesEditorDialog::newInstanceFinishBorder(Border* border, SurfaceFile* surfaceFile, QWidget* parent) { CaretAssert(surfaceFile); BorderPropertiesEditorDialog* dialog = new BorderPropertiesEditorDialog("Finish Border", surfaceFile, BorderPropertiesEditorDialog::MODE_FINISH_DRAWING, NULL, border, parent); return dialog; } /** * Create a new instance of the border properties editor for editing * a border properties. * * @param editModeBorderFile * Border file containing the border that is being edited. * @param border * Border that is to be edited. * @param parent * Parent on which this dialog is shown. * @return * Dialog that will finish the border. * Users MUST DELETE the returned dialog. */ BorderPropertiesEditorDialog* BorderPropertiesEditorDialog::newInstanceEditBorder(BorderFile* editModeBorderFile, Border* border, QWidget* parent) { BorderPropertiesEditorDialog* dialog = new BorderPropertiesEditorDialog("Edit Border Properties", NULL, BorderPropertiesEditorDialog::MODE_EDIT, editModeBorderFile, border, parent); return dialog; } /** * Constructor. */ BorderPropertiesEditorDialog::BorderPropertiesEditorDialog(const QString& title, SurfaceFile* finishBorderSurfaceFile, Mode mode, BorderFile* editModeBorderFile, Border* border, QWidget* parent) : WuQDialogModal(title, parent) { CaretAssert(border); m_finishBorderSurfaceFile = finishBorderSurfaceFile; m_editModeBorderFile = editModeBorderFile; m_mode = mode; m_border = border; m_classComboBox = NULL; QString borderName = border->getName(); QString className = border->getClassName(); switch (m_mode) { case MODE_EDIT: break; case MODE_FINISH_DRAWING: if (s_previousDataValid) { borderName = s_previousName; className = s_previousClassName; } break; } /* * File selection combo box */ QLabel* borderFileLabel = new QLabel("Border File"); m_borderFileSelectionComboBox = new QComboBox(); WuQtUtilities::setToolTipAndStatusTip(m_borderFileSelectionComboBox, "Selects an existing border file\n" "to which new borders are added."); QObject::connect(m_borderFileSelectionComboBox, SIGNAL(activated(int)), this, SLOT(borderFileSelected())); QAction* newFileAction = WuQtUtilities::createAction("New", "Create a new border file", this, this, SLOT(newBorderFileButtonClicked())); QToolButton* newFileToolButton = new QToolButton(); newFileToolButton->setDefaultAction(newFileAction); /* * Completer for name */ m_nameCompleterStringListModel = new QStringListModel(this); /* * Name */ QLabel* nameLabel = new QLabel("Name"); m_nameComboBox = new GiftiLabelTableSelectionComboBox(this); m_nameComboBox->setUnassignedLabelTextOverride("Select Name"); // m_nameLineEdit->setText(borderName); QAction* displayNameColorEditorAction = WuQtUtilities::createAction("Add/Edit...", "Add and/or edit name colors", this, this, SLOT(displayNameEditor())); QToolButton* displayNameColorEditorToolButton = new QToolButton(); displayNameColorEditorToolButton->setDefaultAction(displayNameColorEditorAction); /* * Class */ QLabel* classLabel = new QLabel("Class"); m_classComboBox = new GiftiLabelTableSelectionComboBox(this); WuQtUtilities::setToolTipAndStatusTip(m_classComboBox->getWidget(), "The class is used to group borders with similar\n" "characteristics. Controls are available to\n" "display borders by their class attributes."); QAction* displayClassEditorAction = WuQtUtilities::createAction("Add/Edit...", "Add and/or edit classes", this, this, SLOT(displayClassEditor())); QToolButton* displayClassEditorToolButton = new QToolButton(); displayClassEditorToolButton->setDefaultAction(displayClassEditorAction); /* * Closed */ m_closedCheckBox = new QCheckBox("Closed Border"); WuQtUtilities::setToolTipAndStatusTip(m_closedCheckBox, "If checked, additional points will be added\n" "to the border so that the border forms a loop\n" "with the last point adjacent to the first point."); if (s_previousClosedSelected) { m_closedCheckBox->setChecked(true); } else { m_closedCheckBox->setChecked(false); } /* * Reverse point order */ m_reversePointOrderCheckBox = new QCheckBox("Reverse Point Order"); WuQtUtilities::setToolTipAndStatusTip(m_reversePointOrderCheckBox, "If checked, the order of the points in the \n" "border are reversed when the OK button is pressed."); /* * Layout widgets */ QWidget* widget = new QWidget(); QGridLayout* gridLayout = new QGridLayout(widget); int row = 0; gridLayout->addWidget(borderFileLabel, row, 0); gridLayout->addWidget(m_borderFileSelectionComboBox, row, 1); gridLayout->addWidget(newFileToolButton, row, 2); row++; gridLayout->addWidget(nameLabel, row, 0); gridLayout->addWidget(m_nameComboBox->getWidget(), row, 1); gridLayout->addWidget(displayNameColorEditorToolButton, row, 2); row++; gridLayout->addWidget(classLabel, row, 0); gridLayout->addWidget(m_classComboBox->getWidget(), row, 1); gridLayout->addWidget(displayClassEditorToolButton, row, 2); row++; gridLayout->addWidget(WuQtUtilities::createHorizontalLineWidget(), row, 0, 1, 3, Qt::AlignLeft); row++; gridLayout->addWidget(m_closedCheckBox, row, 0, 1, 3, Qt::AlignLeft); row++; gridLayout->addWidget(m_reversePointOrderCheckBox, row, 0, 1, 3, Qt::AlignLeft); /* * Show/Hide options based upon mode */ bool showFileOptionFlag = false; bool showClosedOptionFlag = false; bool showReverseOptionFlag = false; switch (m_mode) { case MODE_EDIT: showReverseOptionFlag = true; break; case MODE_FINISH_DRAWING: showFileOptionFlag = true; showClosedOptionFlag = true; break; } borderFileLabel->setVisible(showFileOptionFlag); m_borderFileSelectionComboBox->setVisible(showFileOptionFlag); newFileToolButton->setVisible(showFileOptionFlag); m_closedCheckBox->setVisible(showClosedOptionFlag); m_reversePointOrderCheckBox->setVisible(showReverseOptionFlag); loadBorderFileComboBox(); loadNameComboBox(borderName); loadClassComboBox(className); /* * Set the widget for the dialog. */ setCentralWidget(widget, WuQDialog::SCROLL_AREA_NEVER); } /** * Destructor. */ BorderPropertiesEditorDialog::~BorderPropertiesEditorDialog() { } /** * Get the selected border file. * @return BorderFile or NULL if no border file. */ BorderFile* BorderPropertiesEditorDialog::getSelectedBorderFile() { if (m_editModeBorderFile != NULL) { return m_editModeBorderFile; } const int fileComboBoxIndex = m_borderFileSelectionComboBox->currentIndex(); void* filePointer = m_borderFileSelectionComboBox->itemData(fileComboBoxIndex).value(); BorderFile* borderFile = (BorderFile*)filePointer; s_previousBorderFile = borderFile; return borderFile; } /** * Load the SINGLE STRUCTURE border files into the border file combo box. */ void BorderPropertiesEditorDialog::loadBorderFileComboBox() { CaretAssert(m_border); const StructureEnum::Enum borderStructure = m_border->getStructure(); Brain* brain = GuiManager::get()->getBrain(); const int32_t numBorderFiles = brain->getNumberOfBorderFiles(); m_borderFileSelectionComboBox->clear(); int defaultFileComboIndex = 0; for (int32_t i = 0; i < numBorderFiles; i++) { BorderFile* borderFile = brain->getBorderFile(i); if (borderFile->isSingleStructure()) { if (borderFile->getStructure() == borderStructure) { const AString name = borderFile->getFileNameNoPath(); m_borderFileSelectionComboBox->addItem(name, qVariantFromValue((void*)borderFile)); if (s_previousBorderFile == borderFile) { defaultFileComboIndex = m_borderFileSelectionComboBox->count() - 1; } } } } if (m_borderFileSelectionComboBox->count() > 0) { m_borderFileSelectionComboBox->setCurrentIndex(defaultFileComboIndex); const BorderFile* borderFile = getSelectedBorderFile(); if (borderFile != NULL) { m_nameCompleterStringList = borderFile->getAllBorderNamesSorted(); m_nameCompleterStringListModel->setStringList(m_nameCompleterStringList); } } } /** * Called to create a new border file. */ void BorderPropertiesEditorDialog::newBorderFileButtonClicked() { CaretAssert(m_border); const StructureEnum::Enum borderStructure = m_border->getStructure(); if (StructureEnum::isSingleStructure(borderStructure)) { CaretAssert(m_finishBorderSurfaceFile); /* * Let user choose a different path/name */ BorderFile* newBorderFile = new BorderFile(); GuiManager::get()->getBrain()->convertDataFilePathNameToAbsolutePathName(newBorderFile); AString newBorderFileName = CaretFileDialog::getSaveFileNameDialog(DataFileTypeEnum::BORDER, this, "Choose Border File Name", newBorderFile->getFileName()); /* * If user cancels, delete the new border file and return */ if (newBorderFileName.isEmpty()) { delete newBorderFile; return; } /* * Set name of new border file along with structure and number of nodes */ newBorderFile->setFileName(newBorderFileName); newBorderFile->setStructure(borderStructure); newBorderFile->setNumberOfNodes(m_finishBorderSurfaceFile->getNumberOfNodes()); EventManager::get()->sendEvent(EventDataFileAdd(newBorderFile).getPointer()); s_previousBorderFile = newBorderFile; loadBorderFileComboBox(); borderFileSelected(); } else { WuQMessageBox::errorOk(this, ("Border must be for a single structure but it is for " + StructureEnum::toGuiName(borderStructure))); } } /** * Called when a border file is selected. */ void BorderPropertiesEditorDialog::borderFileSelected() { loadNameComboBox(); if (m_classComboBox != NULL) { loadClassComboBox(); } } /** * Load the class combo box. * * @param name * If not empty, make this name the selected name. */ void BorderPropertiesEditorDialog::loadClassComboBox(const QString& name) { BorderFile* borderFile = getSelectedBorderFile(); if (borderFile != NULL) { m_classComboBox->updateContent(borderFile->getClassColorTable()); if (name.isEmpty() == false) { m_classComboBox->setSelectedLabelName(name); } } else { m_classComboBox->updateContent(NULL); } } /** * Load the name combo box. * * @param name * If not empty, make this name the selected name. */ void BorderPropertiesEditorDialog::loadNameComboBox(const QString& name) { BorderFile* borderFile = getSelectedBorderFile(); if (borderFile != NULL) { m_nameComboBox->updateContent(borderFile->getNameColorTable()); if (name.isEmpty() == false) { m_nameComboBox->setSelectedLabelName(name); } } else { m_nameComboBox->updateContent(NULL); } } /** * Called when the OK button is pressed. */ void BorderPropertiesEditorDialog::okButtonClicked() { AString errorMessage; /* * Get border file. */ BorderFile* borderFile = getSelectedBorderFile(); if (borderFile == NULL) { WuQMessageBox::errorOk(this, "Border file is not valid, use the New button to create a border file."); return; } /* * Get data entered by the user. */ const AString name = m_nameComboBox->getSelectedLabelName(); if (name.isEmpty()) { errorMessage += ("Name is invalid.\n"); } else { const int32_t unassignedNameKey = borderFile->getNameColorTable()->getUnassignedLabelKey(); const int32_t selectedNameKey = m_nameComboBox->getSelectedLabelKey(); if (selectedNameKey == unassignedNameKey) { errorMessage += "Choose or create a name for the border"; } } const QString className = m_classComboBox->getSelectedLabelName(); /* * Error? */ if (errorMessage.isEmpty() == false) { WuQMessageBox::errorOk(this, errorMessage); return; } Border* borderBeingEdited = NULL; bool finishModeFlag = false; switch (m_mode) { case MODE_EDIT: borderBeingEdited = m_border; break; case MODE_FINISH_DRAWING: borderBeingEdited = new Border(*m_border); finishModeFlag = true; break; } /* * Make a copy of the border being drawn */ borderBeingEdited->setName(name); borderBeingEdited->setClassName(className); if (finishModeFlag) { /* * Close border */ if (m_closedCheckBox->isChecked()) { borderBeingEdited->addPointsToCloseBorderWithGeodesic(m_finishBorderSurfaceFile); borderBeingEdited->setClosed(true); } else { borderBeingEdited->setClosed(false); } /* * Add border to border file */ CaretAssert(borderFile); borderFile->addBorder(borderBeingEdited); /* * Save values entered by the user and * use them to initialize the dialog next * time it is displayed. */ s_previousDataValid = true; s_previousName = name; s_previousClassName = className; s_previousClosedSelected = m_closedCheckBox->isChecked(); s_previousBorderFile = borderFile; } else { if (m_reversePointOrderCheckBox->isChecked()) { m_border->reverse(); } } if (m_nameCompleterStringList.contains(name) == false) { m_nameCompleterStringList.append(name); m_nameCompleterStringList.sort(); m_nameCompleterStringListModel->setStringList(m_nameCompleterStringList); } /* * continue with OK button processing */ WuQDialogModal::okButtonClicked(); } /** * Display the class editor */ void BorderPropertiesEditorDialog::displayClassEditor() { BorderFile* borderFile = getSelectedBorderFile(); if (borderFile == NULL) { WuQMessageBox::errorOk(this, "Border file is not valid, use the New button to create a border file."); return; } /* * Need to detect a change in the name class table. * So: * (1) Save the modified status * (2) Clear the modified status * (3) After editing, see if the modified status is on indicating * that the user has made a change to the table * (4) If user modified table, invalidate border colors so that * the borders get the new color(s). * (5) If user DID NOT modify table, restore the modification * status of the table. */ GiftiLabelTable* classTable = borderFile->getClassColorTable(); const bool modifiedStatus = classTable->isModified(); classTable->clearModified(); GiftiLabelTableEditor editor(borderFile, classTable, "Edit Class Attributes", GiftiLabelTableEditor::OPTION_NONE, this); const QString className = m_classComboBox->getSelectedLabelName(); if (className.isEmpty() == false) { editor.selectLabelWithName(className); } const int dialogResult = editor.exec(); loadClassComboBox(); if (dialogResult == GiftiLabelTableEditor::Accepted) { const QString selectedClassName = editor.getLastSelectedLabelName(); if (selectedClassName.isEmpty() == false) { m_classComboBox->setSelectedLabelName(selectedClassName); } } if (classTable->isModified()) { /* * User changed something in the table. */ borderFile->invalidateAllAssignedColors(); } else { if (modifiedStatus) { /* * User did not change anything but need to restore * modified status. */ classTable->setModified(); } } } /** * Display the name editor */ void BorderPropertiesEditorDialog::displayNameEditor() { BorderFile* borderFile = getSelectedBorderFile(); if (borderFile == NULL) { WuQMessageBox::errorOk(this, "Border file is not valid, use the New button to create a border file."); return; } /* * Need to detect a change in the name color table. * So: * (1) Save the modified status * (2) Clear the modified status * (3) After editing, see if the modified status is on indicating * that the user has made a change to the table * (4) If user modified table, invalidate border colors so that * the borders get the new color(s). * (5) If user DID NOT modify table, restore the modification * status of the table. */ GiftiLabelTable* nameTable = borderFile->getNameColorTable(); const bool modifiedStatus = nameTable->isModified(); nameTable->clearModified(); GiftiLabelTableEditor editor(borderFile, nameTable, "Edit Name Attributes", GiftiLabelTableEditor::OPTION_UNASSIGNED_LABEL_HIDDEN, this); const QString name = m_nameComboBox->getSelectedLabelName(); if (name.isEmpty() == false) { editor.selectLabelWithName(name); } const int dialogResult = editor.exec(); loadNameComboBox(); if (dialogResult == GiftiLabelTableEditor::Accepted) { const QString selectedName = editor.getLastSelectedLabelName(); if (selectedName.isEmpty() == false) { m_nameComboBox->setSelectedLabelName(selectedName); } } if (nameTable->isModified()) { /* * User changed something in the table. */ borderFile->invalidateAllAssignedColors(); } else { if (modifiedStatus) { /* * User did not change anything but need to restore * modified status. */ nameTable->setModified(); } } } connectome-workbench-1.2.3+git41-gc4c6c90/src/GuiQt/BorderPropertiesEditorDialog.h000066400000000000000000000103131300200146000275250ustar00rootroot00000000000000#ifndef __BORDER_PROPERTIES_EDITOR_DIALOG__H_ #define __BORDER_PROPERTIES_EDITOR_DIALOG__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 "WuQDialogModal.h" class QCheckBox; class QComboBox; class QDoubleSpinBox; class QLabel; class QLineEdit; class QStringListModel; namespace caret { class Border; class BorderFile; class GiftiLabelTableSelectionComboBox; class SurfaceFile; class BorderPropertiesEditorDialog : public WuQDialogModal { Q_OBJECT public: static BorderPropertiesEditorDialog* newInstanceFinishBorder(Border* border, SurfaceFile* surfaceFile, QWidget* parent = 0); static BorderPropertiesEditorDialog* newInstanceEditBorder(BorderFile* editModeBorderFile, Border* border, QWidget* parent = 0); virtual ~BorderPropertiesEditorDialog(); protected: virtual void okButtonClicked(); private slots: void displayNameEditor(); void displayClassEditor(); void borderFileSelected(); void newBorderFileButtonClicked(); private: enum Mode { MODE_EDIT, MODE_FINISH_DRAWING }; BorderPropertiesEditorDialog(const QString& title, SurfaceFile* finishBorderSurfaceFile, const Mode mode, BorderFile* editModeBorderFile, Border* border, QWidget* parent = 0); BorderPropertiesEditorDialog(const BorderPropertiesEditorDialog&); BorderPropertiesEditorDialog& operator=(const BorderPropertiesEditorDialog&); BorderFile* getSelectedBorderFile(); void loadBorderFileComboBox(); void loadNameComboBox(const QString& name = ""); void loadClassComboBox(const QString& className = ""); Mode m_mode; BorderFile* m_editModeBorderFile; Border* m_border; QComboBox* m_borderFileSelectionComboBox; GiftiLabelTableSelectionComboBox* m_nameComboBox; QStringList m_nameCompleterStringList; QStringListModel* m_nameCompleterStringListModel; QCheckBox* m_closedCheckBox; GiftiLabelTableSelectionComboBox* m_classComboBox; QCheckBox* m_reversePointOrderCheckBox; SurfaceFile* m_finishBorderSurfaceFile; static bool s_previousDataValid; static AString s_previousName; static BorderFile* s_previousBorderFile; static bool s_previousClosedSelected; static AString s_previousClassName; }; #ifdef __BORDER_PROPERTIES_EDITOR_DIALOG__DECLARE__ bool BorderPropertiesEditorDialog::s_previousDataValid = false; AString BorderPropertiesEditorDialog::s_previousName = "Name"; BorderFile* BorderPropertiesEditorDialog::s_previousBorderFile = NULL; bool BorderPropertiesEditorDialog::s_previousClosedSelected = false; AString BorderPropertiesEditorDialog::s_previousClassName = "None"; #endif // __BORDER_PROPERTIES_EDITOR_DIALOG__DECLARE__ } // namespace #endif //__BORDER_PROPERTIES_EDITOR_DIALOG__H_ connectome-workbench-1.2.3+git41-gc4c6c90/src/GuiQt/BorderSelectionViewController.cxx000066400000000000000000000551571300200146000303200ustar00rootroot00000000000000 /*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 #define __BORDER_SELECTION_VIEW_CONTROLLER_DECLARE__ #include "BorderSelectionViewController.h" #undef __BORDER_SELECTION_VIEW_CONTROLLER_DECLARE__ #include "BorderDrawingTypeEnum.h" #include "Brain.h" #include "BrainOpenGL.h" #include "BrowserTabContent.h" #include "CaretAssert.h" #include "CaretColorEnumComboBox.h" #include "GroupAndNameHierarchyViewController.h" #include "DisplayGroupEnumComboBox.h" #include "DisplayPropertiesBorders.h" #include "EnumComboBoxTemplate.h" #include "EventGraphicsUpdateAllWindows.h" #include "EventManager.h" #include "EventUserInterfaceUpdate.h" #include "GuiManager.h" #include "SceneClass.h" #include "WuQDataEntryDialog.h" #include "WuQFactory.h" #include "WuQTabWidget.h" #include "WuQtUtilities.h" using namespace caret; /** * \class caret::BorderSelectionViewController * \brief Widget for controlling display of borders * * Widget for controlling the display of borders including * different display groups. */ /** * Constructor. */ BorderSelectionViewController::BorderSelectionViewController(const int32_t browserWindowIndex, QWidget* parent) : QWidget(parent) { m_browserWindowIndex = browserWindowIndex; QLabel* groupLabel = new QLabel("Group"); m_bordersDisplayGroupComboBox = new DisplayGroupEnumComboBox(this); QObject::connect(m_bordersDisplayGroupComboBox, SIGNAL(displayGroupSelected(const DisplayGroupEnum::Enum)), this, SLOT(borderDisplayGroupSelected(const DisplayGroupEnum::Enum))); QHBoxLayout* groupLayout = new QHBoxLayout(); groupLayout->addWidget(groupLabel); groupLayout->addWidget(m_bordersDisplayGroupComboBox->getWidget()); groupLayout->addStretch(); m_bordersDisplayCheckBox = new QCheckBox("Display Borders"); QObject::connect(m_bordersDisplayCheckBox, SIGNAL(clicked(bool)), this, SLOT(processAttributesChanges())); QWidget* attributesWidget = this->createAttributesWidget(); QWidget* selectionWidget = this->createSelectionWidget(); m_tabWidget = new WuQTabWidget(WuQTabWidget::TAB_ALIGN_LEFT, this); m_tabWidget->addTab(attributesWidget, "Attributes"); m_tabWidget->addTab(selectionWidget, "Selection"); m_tabWidget->setCurrentWidget(attributesWidget); QVBoxLayout* layout = new QVBoxLayout(this); //WuQtUtilities::setLayoutSpacingAndMargins(layout, 2, 2); layout->addWidget(m_bordersDisplayCheckBox); layout->addWidget(WuQtUtilities::createHorizontalLineWidget()); layout->addLayout(groupLayout); layout->addWidget(m_tabWidget->getWidget(), 0, Qt::AlignLeft); layout->addStretch(); EventManager::get()->addEventListener(this, EventTypeEnum::EVENT_USER_INTERFACE_UPDATE); BorderSelectionViewController::allBorderSelectionViewControllers.insert(this); } /** * Destructor. */ BorderSelectionViewController::~BorderSelectionViewController() { EventManager::get()->removeAllEventsFromListener(this); BorderSelectionViewController::allBorderSelectionViewControllers.erase(this); } QWidget* BorderSelectionViewController::createSelectionWidget() { m_borderClassNameHierarchyViewController = new GroupAndNameHierarchyViewController(m_browserWindowIndex); return m_borderClassNameHierarchyViewController; } /** * @return The attributes widget. */ QWidget* BorderSelectionViewController::createAttributesWidget() { m_bordersContralateralCheckBox = new QCheckBox("Contralateral"); QObject::connect(m_bordersContralateralCheckBox, SIGNAL(clicked(bool)), this, SLOT(processAttributesChanges())); std::vector drawingTypeEnums; BorderDrawingTypeEnum::getAllEnums(drawingTypeEnums); const int32_t numDrawingTypeEnums = static_cast(drawingTypeEnums.size()); QLabel* drawAsLabel = new QLabel("Draw As"); m_drawTypeComboBox = new QComboBox(); for (int32_t i = 0; i < numDrawingTypeEnums; i++) { BorderDrawingTypeEnum::Enum drawType = drawingTypeEnums[i]; m_drawTypeComboBox->addItem(BorderDrawingTypeEnum::toGuiName(drawType), (int)drawType); } m_drawTypeComboBox->setToolTip("Select the drawing style of borders"); QObject::connect(m_drawTypeComboBox, SIGNAL(activated(int)), this, SLOT(processAttributesChanges())); QLabel* coloringLabel = new QLabel("Coloring"); m_coloringTypeComboBox = new EnumComboBoxTemplate(this); m_coloringTypeComboBox->setup(); m_coloringTypeComboBox->getWidget()->setToolTip("Select the coloring assignment for borders"); QObject::connect(m_coloringTypeComboBox, SIGNAL(itemActivated()), this, SLOT(processAttributesChanges())); QLabel* standardColorLabel = new QLabel("Standard Color"); m_standardColorComboBox = new CaretColorEnumComboBox(this); m_standardColorComboBox->getWidget()->setToolTip("Select the standard color"); QObject::connect(m_standardColorComboBox, SIGNAL(colorSelected(const CaretColorEnum::Enum)), this, SLOT(processAttributesChanges())); float minLineWidth = 0; float maxLineWidth = 1000; //BrainOpenGL::getMinMaxLineWidth(minLineWidth, // maxLineWidth); QLabel* lineWidthLabel = new QLabel("Line Diameter"); m_lineWidthSpinBox = WuQFactory::newDoubleSpinBox(); m_lineWidthSpinBox->setFixedWidth(80); m_lineWidthSpinBox->setRange(minLineWidth, maxLineWidth); m_lineWidthSpinBox->setSingleStep(1.0); m_lineWidthSpinBox->setDecimals(1); m_lineWidthSpinBox->setSuffix("px"); m_lineWidthSpinBox->setToolTip("Adjust the width of borders drawn as lines.\n" "Units is pixels\n" "The maximum width is dependent upon the \n" "graphics system. There is no maximum value\n" "for this control and the drawn width of the \n" "lines will stop increasing even though the\n" "value of this control is changing"); QObject::connect(m_lineWidthSpinBox, SIGNAL(valueChanged(double)), this, SLOT(processAttributesChanges())); QLabel* pointSizeLabel = new QLabel("Symbol Diameter"); m_pointSizeSpinBox = WuQFactory::newDoubleSpinBox(); m_pointSizeSpinBox->setFixedWidth(80); m_pointSizeSpinBox->setRange(minLineWidth, maxLineWidth); m_pointSizeSpinBox->setSingleStep(1.0); m_pointSizeSpinBox->setDecimals(1); m_pointSizeSpinBox->setToolTip("Adjust the size of borders drawn as points"); m_pointSizeSpinBox->setSuffix("mm"); QObject::connect(m_pointSizeSpinBox, SIGNAL(valueChanged(double)), this, SLOT(processAttributesChanges())); m_enableUnstretchedLinesCheckBox = new QCheckBox("Unstretched Lines"); QObject::connect(m_enableUnstretchedLinesCheckBox, SIGNAL(clicked(bool)), this, SLOT(processAttributesChanges())); m_unstretchedLinesLengthSpinBox = WuQFactory::newDoubleSpinBox(); m_unstretchedLinesLengthSpinBox->setFixedWidth(80); m_unstretchedLinesLengthSpinBox->setRange(0.0, 10000000.0); m_unstretchedLinesLengthSpinBox->setSingleStep(1.0); m_unstretchedLinesLengthSpinBox->setDecimals(1); m_unstretchedLinesLengthSpinBox->setToolTip(WuQtUtilities::createWordWrappedToolTipText("Ratio = (length of border on flat surface divided by length of border of 3d (primary anatomical) surface. " "When ratio is greater than the unstretched lines value, the border segment is NOT drawn.")); m_unstretchedLinesLengthSpinBox->setSuffix("mm"); QObject::connect(m_unstretchedLinesLengthSpinBox, SIGNAL(valueChanged(double)), this, SLOT(processAttributesChanges())); QLabel* aboveSurfaceLabel = new QLabel("Above Offset"); m_aboveSurfaceOffsetSpinBox =WuQFactory::newDoubleSpinBox(); m_aboveSurfaceOffsetSpinBox->setFixedWidth(80); m_aboveSurfaceOffsetSpinBox->setRange( 0.0, 100.0); m_aboveSurfaceOffsetSpinBox->setSingleStep(0.1); m_aboveSurfaceOffsetSpinBox->setDecimals(1); m_aboveSurfaceOffsetSpinBox->setToolTip(WuQtUtilities::createWordWrappedToolTipText("Moves surface away from borders (in depth) so that borders are above surface. " "Use with caution.")); QObject::connect(m_aboveSurfaceOffsetSpinBox, SIGNAL(valueChanged(double)), this, SLOT(processAttributesChanges())); QWidget* gridWidget = new QWidget(); QGridLayout* gridLayout = new QGridLayout(gridWidget); WuQtUtilities::setLayoutSpacingAndMargins(gridLayout, 8, 2); int row = gridLayout->rowCount(); gridLayout->addWidget(m_bordersContralateralCheckBox, row, 0, 1, 2, Qt::AlignLeft); row++; gridLayout->addWidget(WuQtUtilities::createHorizontalLineWidget(), row, 0, 1, 2); row++; gridLayout->addWidget(drawAsLabel, row, 0); gridLayout->addWidget(m_drawTypeComboBox, row, 1); row++; gridLayout->addWidget(coloringLabel, row, 0); gridLayout->addWidget(m_coloringTypeComboBox->getWidget(), row, 1); row++; gridLayout->addWidget(standardColorLabel, row, 0); gridLayout->addWidget(m_standardColorComboBox->getWidget(), row, 1); row++; gridLayout->addWidget(lineWidthLabel, row, 0); gridLayout->addWidget(m_lineWidthSpinBox, row, 1); row++; gridLayout->addWidget(pointSizeLabel, row, 0); gridLayout->addWidget(m_pointSizeSpinBox, row, 1); row++; gridLayout->addWidget(m_enableUnstretchedLinesCheckBox, row, 0); gridLayout->addWidget(m_unstretchedLinesLengthSpinBox, row, 1); row++; gridLayout->addWidget(aboveSurfaceLabel, row, 0); gridLayout->addWidget(m_aboveSurfaceOffsetSpinBox, row, 1); gridWidget->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); QWidget* widget = new QWidget(); QVBoxLayout* layout = new QVBoxLayout(widget); layout->addWidget(gridWidget); layout->addStretch(); return widget; } /** * Called when a widget on the attributes page has * its value changed. */ void BorderSelectionViewController::processAttributesChanges() { DisplayPropertiesBorders* dpb = GuiManager::get()->getBrain()->getDisplayPropertiesBorders(); const int selectedDrawTypeIndex = m_drawTypeComboBox->currentIndex(); const int drawTypeInteger = m_drawTypeComboBox->itemData(selectedDrawTypeIndex).toInt(); const BorderDrawingTypeEnum::Enum selectedDrawingType = static_cast(drawTypeInteger); const FeatureColoringTypeEnum::Enum selectedColoringType = m_coloringTypeComboBox->getSelectedItem(); const CaretColorEnum::Enum standardColorType = m_standardColorComboBox->getSelectedColor(); BrowserTabContent* browserTabContent = GuiManager::get()->getBrowserTabContentForBrowserWindow(m_browserWindowIndex, true); if (browserTabContent == NULL) { return; } const int32_t browserTabIndex = browserTabContent->getTabNumber(); const DisplayGroupEnum::Enum displayGroup = dpb->getDisplayGroupForTab(browserTabIndex); dpb->setDisplayed(displayGroup, browserTabIndex, m_bordersDisplayCheckBox->isChecked()); dpb->setContralateralDisplayed(displayGroup, browserTabIndex, m_bordersContralateralCheckBox->isChecked()); dpb->setDrawingType(displayGroup, browserTabIndex, selectedDrawingType); dpb->setColoringType(displayGroup, browserTabIndex, selectedColoringType); dpb->setStandardColorType(displayGroup, browserTabIndex, standardColorType); dpb->setLineWidth(displayGroup, browserTabIndex, m_lineWidthSpinBox->value()); dpb->setPointSize(displayGroup, browserTabIndex, m_pointSizeSpinBox->value()); dpb->setUnstretchedLinesEnabled(displayGroup, browserTabIndex, m_enableUnstretchedLinesCheckBox->isChecked()); dpb->setUnstretchedLinesLength(displayGroup, browserTabIndex, m_unstretchedLinesLengthSpinBox->value()); dpb->setAboveSurfaceOffset(m_aboveSurfaceOffsetSpinBox->value()); EventManager::get()->sendEvent(EventGraphicsUpdateAllWindows().getPointer()); updateOtherBorderViewControllers(); } /** * Called when the border display group combo box is changed. */ void BorderSelectionViewController::borderDisplayGroupSelected(const DisplayGroupEnum::Enum displayGroup) { /* * Update selected display group in model. */ BrowserTabContent* browserTabContent = GuiManager::get()->getBrowserTabContentForBrowserWindow(m_browserWindowIndex, false); if (browserTabContent == NULL) { return; } const int32_t browserTabIndex = browserTabContent->getTabNumber(); Brain* brain = GuiManager::get()->getBrain(); DisplayPropertiesBorders* dsb = brain->getDisplayPropertiesBorders(); dsb->setDisplayGroupForTab(browserTabIndex, displayGroup); /* * Since display group has changed, need to update controls */ updateBorderViewController(); /* * Apply the changes. */ processBorderSelectionChanges(); } /** * Update the border selection widget. */ void BorderSelectionViewController::updateBorderViewController() { setWindowTitle("Borders"); BrowserTabContent* browserTabContent = GuiManager::get()->getBrowserTabContentForBrowserWindow(m_browserWindowIndex, true); if (browserTabContent == NULL) { return; } const int32_t browserTabIndex = browserTabContent->getTabNumber(); Brain* brain = GuiManager::get()->getBrain(); DisplayPropertiesBorders* dpb = brain->getDisplayPropertiesBorders(); const DisplayGroupEnum::Enum displayGroup = dpb->getDisplayGroupForTab(browserTabIndex); m_bordersDisplayGroupComboBox->setSelectedDisplayGroup(dpb->getDisplayGroupForTab(browserTabIndex)); /*; * Get all of border files. */ std::vector allBorderFiles; const int32_t numberOfBorderFiles = brain->getNumberOfBorderFiles(); for (int32_t ibf= 0; ibf < numberOfBorderFiles; ibf++) { allBorderFiles.push_back(brain->getBorderFile(ibf)); } /* * Update the class/name hierarchy */ m_borderClassNameHierarchyViewController->updateContents(allBorderFiles, displayGroup); std::vector drawingTypeEnums; BorderDrawingTypeEnum::getAllEnums(drawingTypeEnums); const int32_t numDrawingTypeEnums = static_cast(drawingTypeEnums.size()); m_bordersDisplayCheckBox->setChecked(dpb->isDisplayed(displayGroup, browserTabIndex)); m_bordersContralateralCheckBox->setChecked(dpb->isContralateralDisplayed(displayGroup, browserTabIndex)); const BorderDrawingTypeEnum::Enum selectedDrawingType = dpb->getDrawingType(displayGroup, browserTabIndex); int32_t selectedDrawingTypeIndex = 0; for (int32_t i = 0; i < numDrawingTypeEnums; i++) { BorderDrawingTypeEnum::Enum drawType = drawingTypeEnums[i]; if (drawType == selectedDrawingType) { selectedDrawingTypeIndex = i; } } m_drawTypeComboBox->setCurrentIndex(selectedDrawingTypeIndex); m_coloringTypeComboBox->setSelectedItem(dpb->getColoringType(displayGroup, browserTabIndex)); m_standardColorComboBox->setSelectedColor(dpb->getStandardColorType(displayGroup, browserTabIndex)); m_lineWidthSpinBox->blockSignals(true); m_lineWidthSpinBox->setValue(dpb->getLineWidth(displayGroup, browserTabIndex)); m_lineWidthSpinBox->blockSignals(false); m_pointSizeSpinBox->blockSignals(true); m_pointSizeSpinBox->setValue(dpb->getPointSize(displayGroup, browserTabIndex)); m_pointSizeSpinBox->blockSignals(false); m_enableUnstretchedLinesCheckBox->setChecked(dpb->isUnstretchedLinesEnabled(displayGroup, browserTabIndex)); m_unstretchedLinesLengthSpinBox->blockSignals(true); m_unstretchedLinesLengthSpinBox->setValue(dpb->getUnstretchedLinesLength(displayGroup, browserTabIndex)); m_unstretchedLinesLengthSpinBox->blockSignals(false); m_aboveSurfaceOffsetSpinBox->blockSignals(true); m_aboveSurfaceOffsetSpinBox->setValue(dpb->getAboveSurfaceOffset()); m_aboveSurfaceOffsetSpinBox->blockSignals(false); } /** * Update other selection toolbox since they should all be the same. */ void BorderSelectionViewController::updateOtherBorderViewControllers() { for (std::set::iterator iter = BorderSelectionViewController::allBorderSelectionViewControllers.begin(); iter != BorderSelectionViewController::allBorderSelectionViewControllers.end(); iter++) { BorderSelectionViewController* bsw = *iter; if (bsw != this) { bsw->updateBorderViewController(); } } } /** * Gets called when border selections are changed. */ void BorderSelectionViewController::processBorderSelectionChanges() { BrowserTabContent* browserTabContent = GuiManager::get()->getBrowserTabContentForBrowserWindow(m_browserWindowIndex, false); CaretAssert(browserTabContent); const int32_t browserTabIndex = browserTabContent->getTabNumber(); Brain* brain = GuiManager::get()->getBrain(); DisplayPropertiesBorders* dsb = brain->getDisplayPropertiesBorders(); dsb->setDisplayGroupForTab(browserTabIndex, m_bordersDisplayGroupComboBox->getSelectedDisplayGroup()); processSelectionChanges(); } /** * Issue update events after selections are changed. */ void BorderSelectionViewController::processSelectionChanges() { updateOtherBorderViewControllers(); EventManager::get()->sendEvent(EventGraphicsUpdateAllWindows().getPointer()); } /** * Receive events from the event manager. * * @param event * Event sent by event manager. */ void BorderSelectionViewController::receiveEvent(Event* event) { bool doUpdate = false; if (event->getEventType() == EventTypeEnum::EVENT_USER_INTERFACE_UPDATE) { EventUserInterfaceUpdate* uiEvent = dynamic_cast(event); CaretAssert(uiEvent); if (uiEvent->isUpdateForWindow(m_browserWindowIndex)) { if (uiEvent->isBorderUpdate() || uiEvent->isToolBoxUpdate()) { doUpdate = true; uiEvent->setEventProcessed(); } } } if (doUpdate) { updateBorderViewController(); } } /** * Create a scene for an instance of a class. * * @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. * * @return Pointer to SceneClass object representing the state of * this object. Under some circumstances a NULL pointer may be * returned. Caller will take ownership of returned object. */ SceneClass* BorderSelectionViewController::saveToScene(const SceneAttributes* sceneAttributes, const AString& instanceName) { SceneClass* sceneClass = new SceneClass(instanceName, "BorderSelectionViewController", 1); sceneClass->addClass(m_tabWidget->saveToScene(sceneAttributes, "m_tabWidget")); return sceneClass; } /** * Restore the state of an instance of a class. * * @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 containing the state that was previously * saved and should be restored. */ void BorderSelectionViewController::restoreFromScene(const SceneAttributes* sceneAttributes, const SceneClass* sceneClass) { if (sceneClass == NULL) { return; } m_tabWidget->restoreFromScene(sceneAttributes, sceneClass->getClass("m_tabWidget")); } connectome-workbench-1.2.3+git41-gc4c6c90/src/GuiQt/BorderSelectionViewController.h000066400000000000000000000075301300200146000277350ustar00rootroot00000000000000#ifndef __BORDER_SELECTION_VIEW_CONTROLLER__H_ #define __BORDER_SELECTION_VIEW_CONTROLLER__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 "DisplayGroupEnum.h" #include "EventListenerInterface.h" #include "SceneableInterface.h" class QCheckBox; class QComboBox; class QDoubleSpinBox; namespace caret { class CaretColorEnumComboBox; class GroupAndNameHierarchyViewController; class DisplayGroupEnumComboBox; class EnumComboBoxTemplate; class WuQTabWidget; class BorderSelectionViewController : public QWidget, public EventListenerInterface, public SceneableInterface { Q_OBJECT public: BorderSelectionViewController(const int32_t browserWindowIndex, QWidget* parent = 0); virtual ~BorderSelectionViewController(); void receiveEvent(Event* event); virtual SceneClass* saveToScene(const SceneAttributes* sceneAttributes, const AString& instanceName); virtual void restoreFromScene(const SceneAttributes* sceneAttributes, const SceneClass* sceneClass); private slots: void processBorderSelectionChanges(); void processSelectionChanges(); void borderDisplayGroupSelected(const DisplayGroupEnum::Enum); void processAttributesChanges(); private: BorderSelectionViewController(const BorderSelectionViewController&); BorderSelectionViewController& operator=(const BorderSelectionViewController&); void updateBorderViewController(); void updateOtherBorderViewControllers(); QWidget* createSelectionWidget(); QWidget* createAttributesWidget(); int32_t m_browserWindowIndex; GroupAndNameHierarchyViewController* m_borderClassNameHierarchyViewController; QCheckBox* m_bordersDisplayCheckBox; QCheckBox* m_bordersContralateralCheckBox; DisplayGroupEnumComboBox* m_bordersDisplayGroupComboBox; QComboBox* m_drawTypeComboBox; EnumComboBoxTemplate* m_coloringTypeComboBox; CaretColorEnumComboBox* m_standardColorComboBox; QDoubleSpinBox* m_lineWidthSpinBox; QDoubleSpinBox* m_pointSizeSpinBox; QCheckBox* m_enableUnstretchedLinesCheckBox; QDoubleSpinBox* m_unstretchedLinesLengthSpinBox; QDoubleSpinBox* m_aboveSurfaceOffsetSpinBox; WuQTabWidget* m_tabWidget; static std::set allBorderSelectionViewControllers; }; #ifdef __BORDER_SELECTION_VIEW_CONTROLLER_DECLARE__ std::set BorderSelectionViewController::allBorderSelectionViewControllers; #endif // __BORDER_SELECTION_VIEW_CONTROLLER_DECLARE__ } // namespace #endif //__BORDER_SELECTION_VIEW_CONTROLLER__H_ connectome-workbench-1.2.3+git41-gc4c6c90/src/GuiQt/BrainBrowserWindow.cxx000066400000000000000000004704051300200146000261220ustar00rootroot00000000000000/*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 #include #include #include #include #define __BRAIN_BROWSER_WINDOW_DECLARE__ #include "BrainBrowserWindow.h" #undef __BRAIN_BROWSER_WINDOW_DECLARE__ #include "AboutWorkbenchDialog.h" #include "ApplicationInformation.h" #include "BorderFile.h" #include "BorderFileSplitDialog.h" #include "Brain.h" #include "BrainBrowserWindowEditMenuItemEnum.h" #include "BrainBrowserWindowToolBar.h" #include "BrainBrowserWindowOrientedToolBox.h" #include "BrainOpenGLViewportContent.h" #include "BrainOpenGLWidget.h" #include "BrainStructure.h" #include "BrowserTabContent.h" #include "CaretAssert.h" #include "CaretFileDialog.h" #include "CaretFileRemoteDialog.h" #include "CaretPreferences.h" #include "CursorDisplayScoped.h" #include "DataFileException.h" #include "DeveloperFlagsEnum.h" #include "DisplayPropertiesImages.h" #include "DisplayPropertiesVolume.h" #include "EventBrowserWindowNew.h" #include "CaretLogger.h" #include "ElapsedTimer.h" #include "EventGetViewportSize.h" #include "EventBrowserWindowCreateTabs.h" #include "EventDataFileRead.h" #include "EventMacDockMenuUpdate.h" #include "EventManager.h" #include "EventModelGetAll.h" #include "EventGraphicsUpdateAllWindows.h" #include "EventGraphicsUpdateOneWindow.h" #include "EventSpecFileReadDataFiles.h" #include "EventSurfaceColoringInvalidate.h" #include "EventGetOrSetUserInputModeProcessor.h" #include "EventUserInterfaceUpdate.h" #include "FileInformation.h" #include "FociProjectionDialog.h" #include "GuiManager.h" #include "ModelSurface.h" #include "ModelSurfaceMontage.h" #include "ModelWholeBrain.h" #include "PlainTextStringBuilder.h" #include "ProgressReportingDialog.h" #include "SceneAttributes.h" #include "SceneClass.h" #include "SceneClassArray.h" #include "SceneClassAssistant.h" #include "SceneEnumeratedType.h" #include "SceneFile.h" #include "SceneWindowGeometry.h" #include "SessionManager.h" #include "SpecFile.h" #include "SpecFileManagementDialog.h" #include "StructureEnumComboBox.h" #include "Surface.h" #include "SurfaceMontageConfigurationAbstract.h" #include "SurfaceSelectionViewController.h" #include "TileTabsConfiguration.h" #include "WuQDataEntryDialog.h" #include "WuQMessageBox.h" #include "WuQtUtilities.h" #include "VtkFileExporter.h" using namespace caret; /** * Constructor. * * @param browserWindowIndex * Index for this window. * @param browserTabContent * If not NULL, this is the tab displayed in the window. * If NULL, a new tab is created. * @param parent * Parent of this object * @param flags * Flags for Qt. */ BrainBrowserWindow::BrainBrowserWindow(const int browserWindowIndex, BrowserTabContent* browserTabContent, const CreateDefaultTabsMode createDefaultTabsMode, QWidget* parent, Qt::WindowFlags flags) : QMainWindow(parent, flags) { m_developMenuAction = NULL; 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() + " " + AString::number(m_browserWindowIndex + 1)); setObjectName(windowTitle()); m_openGLWidget = new BrainOpenGLWidget(this, browserWindowIndex); const int openGLSizeX = 500; const int openGLSizeY = (WuQtUtilities::isSmallDisplay() ? 200 : 375); m_openGLWidget->setMinimumSize(openGLSizeX, openGLSizeY); setCentralWidget(m_openGLWidget); m_overlayVerticalToolBox = new BrainBrowserWindowOrientedToolBox(m_browserWindowIndex, "Overlay ToolBox", BrainBrowserWindowOrientedToolBox::TOOL_BOX_OVERLAYS_VERTICAL, this); m_overlayVerticalToolBox->setAllowedAreas(Qt::LeftDockWidgetArea); m_overlayHorizontalToolBox = new BrainBrowserWindowOrientedToolBox(m_browserWindowIndex, "Overlay ToolBox ", BrainBrowserWindowOrientedToolBox::TOOL_BOX_OVERLAYS_HORIZONTAL, this); m_overlayHorizontalToolBox->setAllowedAreas(Qt::BottomDockWidgetArea); if (WuQtUtilities::isSmallDisplay()) { 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)), this, SLOT(processOverlayHorizontalToolBoxVisibilityChanged(bool))); QObject::connect(m_overlayVerticalToolBox, SIGNAL(visibilityChanged(bool)), this, SLOT(processOverlayVerticalToolBoxVisibilityChanged(bool))); m_featuresToolBox = new BrainBrowserWindowOrientedToolBox(m_browserWindowIndex, "Features ToolBox", BrainBrowserWindowOrientedToolBox::TOOL_BOX_FEATURES, this); m_featuresToolBox->setAllowedAreas(Qt::RightDockWidgetArea); addDockWidget(Qt::RightDockWidgetArea, m_featuresToolBox); createActionsUsedByToolBar(); m_overlayToolBoxAction->blockSignals(true); m_overlayToolBoxAction->setChecked(true); m_overlayToolBoxAction->blockSignals(false); m_featuresToolBoxAction->blockSignals(true); m_featuresToolBoxAction->setChecked(true); m_featuresToolBoxAction->blockSignals(false); m_toolbar = new BrainBrowserWindowToolBar(m_browserWindowIndex, browserTabContent, m_overlayToolBoxAction, m_featuresToolBoxAction, m_windowAspectRatioLockedAction, m_tabAspectRatioLockedAction, this); m_showToolBarAction = m_toolbar->toolBarToolButtonAction; addToolBar(m_toolbar); createActions(); createMenus(); m_toolbar->updateToolBar(); processShowOverlayToolBox(m_overlayToolBoxAction->isChecked()); processHideFeaturesToolBox(); if (browserTabContent == NULL) { if (createDefaultTabsMode == CREATE_DEFAULT_TABS_YES) { m_toolbar->addDefaultTabsAfterLoadingSpecFile(); } } m_sceneAssistant = new SceneClassAssistant(); m_sceneAssistant->add("m_aspectRatio", &m_aspectRatio); m_defaultWindowComponentStatus.isFeaturesToolBoxDisplayed = m_featuresToolBoxAction->isChecked(); m_defaultWindowComponentStatus.isOverlayToolBoxDisplayed = m_overlayToolBoxAction->isChecked(); m_defaultWindowComponentStatus.isToolBarDisplayed = m_showToolBarAction->isChecked(); EventManager::get()->addEventListener(this, EventTypeEnum::EVENT_BROWSER_WINDOW_MENUS_UPDATE); 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); } /** * Destructor. */ BrainBrowserWindow::~BrainBrowserWindow() { EventManager::get()->removeAllEventsFromListener(this); delete m_defaultTileTabsConfiguration; delete m_sceneTileTabsConfiguration; delete m_sceneAssistant; } /** * Receive an event. * * @param event * The event that the receive can respond to. */ void BrainBrowserWindow::receiveEvent(Event* event) { if (event->getEventType() == EventTypeEnum::EVENT_BROWSER_WINDOW_MENUS_UPDATE) { const CaretPreferences* prefs = SessionManager::get()->getCaretPreferences(); if (m_developMenuAction != NULL) { m_developMenuAction->setVisible(prefs->isDevelopMenuEnabled()); event->setEventProcessed(); } } else if (event->getEventType() == EventTypeEnum::EVENT_GET_VIEWPORT_SIZE) { EventGetViewportSize* viewportSizeEvent = dynamic_cast(event); CaretAssert(viewportSizeEvent); std::vector allViewportContent = m_openGLWidget->getViewportContent(); int32_t viewport[4] = { 0, 0, 0, 0 }; bool viewportValid = false; int32_t notBestViewport[4] = { 0, 0, 0, 0 }; bool notBestViewportValid = false; switch (viewportSizeEvent->getMode()) { case EventGetViewportSize::MODE_SURFACE_MONTAGE: if (viewportSizeEvent->getIndex() == m_browserWindowIndex) { /* * Find a surface montage in this window */ for (std::vector::iterator vpIter = allViewportContent.begin(); vpIter != allViewportContent.end(); vpIter++) { const BrainOpenGLViewportContent* vpContent = *vpIter; if (vpContent != NULL) { BrowserTabContent* btc = vpContent->getBrowserTabContent(); if (btc != NULL) { const Model* model = btc->getModelForDisplay(); if (model->getModelType() == ModelTypeEnum::MODEL_TYPE_SURFACE_MONTAGE) { vpContent->getTabViewportBeforeApplyingMargins(viewport); viewportValid = true; break; } } } } } break; case EventGetViewportSize::MODE_TAB_BEFORE_MARGINS_INDEX: for (std::vector::iterator vpIter = allViewportContent.begin(); vpIter != allViewportContent.end(); vpIter++) { const BrainOpenGLViewportContent* vpContent = *vpIter; if (vpContent != NULL) { BrowserTabContent* btc = vpContent->getBrowserTabContent(); 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(viewport); viewportValid = true; break; } } } } } break; case EventGetViewportSize::MODE_TAB_AFTER_MARGINS_INDEX: for (std::vector::iterator vpIter = allViewportContent.begin(); vpIter != allViewportContent.end(); vpIter++) { const BrainOpenGLViewportContent* vpContent = *vpIter; if (vpContent != NULL) { BrowserTabContent* btc = vpContent->getBrowserTabContent(); 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->getModelViewport(viewport); viewportValid = true; break; } } } } } break; case EventGetViewportSize::MODE_VOLUME_MONTAGE: if (viewportSizeEvent->getIndex() == m_browserWindowIndex) { /* * Find the viewport content containing the specified tab by index */ for (std::vector::iterator vpIter = allViewportContent.begin(); vpIter != allViewportContent.end(); vpIter++) { const BrainOpenGLViewportContent* vpContent = *vpIter; if (vpContent != NULL) { BrowserTabContent* btc = vpContent->getBrowserTabContent(); if (btc != NULL) { const Model* model = btc->getModelForDisplay(); if (model->getModelType() == ModelTypeEnum::MODEL_TYPE_VOLUME_SLICES) { if (btc->getSliceDrawingType() == VolumeSliceDrawingTypeEnum::VOLUME_SLICE_DRAW_MONTAGE) { vpContent->getTabViewportBeforeApplyingMargins(viewport); viewportValid = true; break; } else { vpContent->getTabViewportBeforeApplyingMargins(notBestViewport); notBestViewportValid = true; } } } } } } break; case EventGetViewportSize::MODE_WINDOW_INDEX: if (m_browserWindowIndex == viewportSizeEvent->getIndex()) { for (std::vector::iterator vpIter = allViewportContent.begin(); vpIter != allViewportContent.end(); vpIter++) { const BrainOpenGLViewportContent* vpContent = *vpIter; if (vpContent != NULL) { vpContent->getWindowViewport(viewport); viewportValid = true; } } } break; } if ( ! viewportValid) { if (notBestViewportValid) { viewport[0] = notBestViewport[0]; viewport[1] = notBestViewport[1]; viewport[2] = notBestViewport[2]; viewport[3] = notBestViewport[3]; viewportValid = 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(viewport); viewportValid = true; } } } if (viewportValid) { 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)) { /* * When in annotations mode, items on the Edit Menu are enabled/disabled * based upon the selected annotations. While we can update the menu items * when the menu is about to show, that is sufficient. The menu items * have shortcut keys so it is possible for the selected annotations to change * and the user to use a short cut key but the item on the edit menu * may still be disabled. Since a change in the selected annotations is * followed by a graphics update event, update the enabled/disabled status * of items in the Edit menu when the graphics are updated so that the * shortcut keys will function. */ processEditMenuAboutToShow(); } } /** * Reset the graphics window size. * When the window is created, the graphics is set to a reasonable size * by setting the minimum size of the graphics region. If the minimum * size is small, */ void BrainBrowserWindow::resetGraphicsWidgetMinimumSize() { m_openGLWidget->setMinimumSize(100, 100); } void BrainBrowserWindow::setGraphicsWidgetFixedSize(const int32_t width, const int32_t height) { m_openGLWidget->setFixedSize(width, height); } /** * 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 applyLockedAspectRatiosFlag * True if locked tab or window aspect ratio should be applied for * graphics region size. */ void BrainBrowserWindow::getGraphicsWidgetSize(int32_t& xOut, int32_t& yOut, int32_t& widthOut, int32_t& heightOut, int32_t& graphicsWidthOut, int32_t& graphicsHeightOut, const bool applyLockedAspectRatiosFlag) const { xOut = 0; yOut = 0; graphicsWidthOut = m_openGLWidget->width(); graphicsHeightOut = m_openGLWidget->height(); widthOut = m_openGLWidget->width(); heightOut = m_openGLWidget->height(); int aspectViewport[4] = { 0, 0, widthOut, heightOut }; if (isTileTabsSelected()) { if (isAspectRatioLocked()) { BrainOpenGLViewportContent::adjustViewportForAspectRatio(aspectViewport, getAspectRatio()); } } else { const BrowserTabContent* btc = getBrowserTabContent(); if (btc != NULL) { if (btc->isAspectRatioLocked()) { BrainOpenGLViewportContent::adjustViewportForAspectRatio(aspectViewport, btc->getAspectRatio()); } else if (isAspectRatioLocked()) { BrainOpenGLViewportContent::adjustViewportForAspectRatio(aspectViewport, getAspectRatio()); } } } if (applyLockedAspectRatiosFlag) { const std::vector allViewportContent = m_openGLWidget->getViewportContent(); if ( ! allViewportContent.empty()) { CaretAssertVectorIndex(allViewportContent, 0); int windowViewport[4]; allViewportContent[0]->getWindowViewport(windowViewport); const float windowWidth = windowViewport[2]; const float windowHeight = windowViewport[3]; if ((windowWidth > 0) && (windowHeight > 0)) { if ((windowWidth != widthOut) || (windowHeight != heightOut)) { xOut = windowViewport[0]; yOut = windowViewport[1]; widthOut = windowWidth; heightOut = 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; // } // } } } /** * @return True if tile tabs is selected, else false. */ bool BrainBrowserWindow::isTileTabsSelected() const { return m_viewTileTabsSelected; } /** * @return the index of this browser window. */ int32_t BrainBrowserWindow::getBrowserWindowIndex() const { return m_browserWindowIndex; } /** * Called when the window is requested to close. * * @param event * CloseEvent that may or may not be accepted * allowing the window to close. */ void BrainBrowserWindow::closeEvent(QCloseEvent* event) { if (m_closeWithoutConfirmationFlag) { m_closeWithoutConfirmationFlag = false; event->accept(); return; } /* * The GuiManager may warn user about closing the * window and the user may cancel closing of the window. */ GuiManager* guiManager = GuiManager::get(); if (guiManager->allowBrainBrowserWindowToClose(this, m_toolbar->tabBar->count())) { event->accept(); } else { event->ignore(); } } /** * Called when a key is pressed. * * @param event * The key event. */ void BrainBrowserWindow::keyPressEvent(QKeyEvent* event) { bool keyEventWasProcessed = false; /* * Pressing the Escape Key exits full screen mode. */ if (event->key() == Qt::Key_Escape) { if (event->modifiers() == Qt::NoModifier) { if (isFullScreen()) { processViewFullScreenSelected(); keyEventWasProcessed = true; } } } /* * According to the documentation, if the key event was not acted upon, * pass it on the base class implementation. */ if ( ! keyEventWasProcessed) { QMainWindow::keyPressEvent(event); } } /** * Create actions for this window. * NOTE: This is called BEFORE the toolbar is created. */ void BrainBrowserWindow::createActionsUsedByToolBar() { QIcon featuresToolBoxIcon; const bool featuresToolBoxIconValid = WuQtUtilities::loadIcon(":/ToolBar/features_toolbox.png", featuresToolBoxIcon); QIcon overlayToolBoxIcon; const bool overlayToolBoxIconValid = WuQtUtilities::loadIcon(":/ToolBar/overlay_toolbox.png", overlayToolBoxIcon); /* * Note: The name of a dock widget becomes its * name in the toggleViewAction(). So, use * a separate action here so that the name in * the menu is as set here. */ m_overlayToolBoxAction = WuQtUtilities::createAction("Overlay ToolBox", "Overlay ToolBox", this, this, SLOT(processShowOverlayToolBox(bool))); m_overlayToolBoxAction->setCheckable(true); if (overlayToolBoxIconValid) { m_overlayToolBoxAction->setIcon(overlayToolBoxIcon); m_overlayToolBoxAction->setIconVisibleInMenu(false); } else { m_overlayToolBoxAction->setIconText("OT"); } /* * Note: The name of a dock widget becomes its * name in the toggleViewAction(). So, use * a separate action here so that the name in * the menu is as set here. */ m_featuresToolBoxAction = m_featuresToolBox->toggleViewAction(); m_featuresToolBoxAction->setCheckable(true); QObject::connect(m_featuresToolBoxAction, SIGNAL(triggered(bool)), this, SLOT(processShowFeaturesToolBox(bool))); if (featuresToolBoxIconValid) { m_featuresToolBoxAction->setIcon(featuresToolBoxIcon); m_featuresToolBoxAction->setIconVisibleInMenu(false); } else { 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())); } /** * Called when Lock Aspect Ratio for All Tabs is selected. */ void BrainBrowserWindow::processLockAllTabsAspectRatioTriggered() { std::vector vpContent = m_openGLWidget->getViewportContent(); if (isTileTabsSelected()) { /* * When tile tabs is on, set the aspect ratio for each tab that does not * already have its aspect ratio locked */ 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. */ 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); } } } } } EventManager::get()->sendEvent(EventGraphicsUpdateOneWindow(getBrowserWindowIndex()).getPointer()); m_toolbar->updateToolBar(); } /** * Called when Unlock Aspect Ratio for All Tabs is selected. */ void BrainBrowserWindow::processUnlockAllTabsAspectRatioTriggered() { 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(); } /** * Called when the Tab aspect ratio action it toggled. * * @param new tab locked status */ 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); BrowserTabContent* tabContent = vp->getBrowserTabContent(); if (selectedTab == tabContent) { int32_t tabViewport[4]; vp->getTabViewportBeforeApplyingMargins(tabViewport); const float width = tabViewport[2]; if (width > 0.0) { const float aspectRatio = tabViewport[3] / width; tabContent->setAspectRatioLocked(checked); if (checked) { tabContent->setAspectRatio(aspectRatio); } } } } } EventManager::get()->sendEvent(EventGraphicsUpdateOneWindow(getBrowserWindowIndex()).getPointer()); } /** * Called when the Window aspect ratio action it toggled. * * @param new tab locked status */ void BrainBrowserWindow::processWindowAspectRatioLockedToggled(bool checked) { setAspectRatioLocked(checked); if (checked) { const float aspectRatio = getOpenGLWidgetAspectRatio(); if (aspectRatio > 0) { setAspectRatio(aspectRatio); } } EventManager::get()->sendEvent(EventGraphicsUpdateOneWindow(getBrowserWindowIndex()).getPointer()); } /** * @return Is the aspect ratio locked? */ bool BrainBrowserWindow::isAspectRatioLocked() const { return m_windowAspectRatioLockedAction->isChecked(); } /** * Set the aspect ratio locked status. * * @param locked * New status. */ void BrainBrowserWindow::setAspectRatioLocked(const bool locked) { m_windowAspectRatioLockedAction->blockSignals(true); m_windowAspectRatioLockedAction->setChecked(locked); m_windowAspectRatioLockedAction->blockSignals(false); } /** * @return The aspect ratio. */ float BrainBrowserWindow::getAspectRatio() const { return m_aspectRatio; } /** * Set the aspect ratio. * * @param aspectRatio * New value for aspect ratio. */ void BrainBrowserWindow::setAspectRatio(const float aspectRatio) { m_aspectRatio = aspectRatio; } /** * Show the surface properties editor dialog. */ void BrainBrowserWindow::processShowSurfacePropertiesDialog() { GuiManager::get()->processShowSurfacePropertiesEditorDialog(this); } /** * Create actions for this window. * NOTE: This is called AFTER the toolbar is created. */ void BrainBrowserWindow::createActions() { CaretAssert(m_toolbar); GuiManager* guiManager = GuiManager::get(); m_aboutWorkbenchAction = WuQtUtilities::createAction("About Workbench...", "Information about Workbench", this, this, SLOT(processAboutWorkbench())); m_newWindowAction = WuQtUtilities::createAction("New Window", "Creates a new window for viewing brain models", Qt::CTRL+Qt::Key_N, this, this, SLOT(processNewWindow())); m_newTabAction = WuQtUtilities::createAction("New Tab", "Create a new tab (window pane) in the window", Qt::CTRL + Qt::Key_T, this, this, SLOT(processNewTab())); m_duplicateTabAction = WuQtUtilities::createAction("Duplicate Tab", "Create a new tab (window pane) that duplicates the selected tab in the window", Qt::CTRL + Qt::Key_D, this, this, SLOT(processDuplicateTab())); m_openFileAction = WuQtUtilities::createAction("Open File...", "Open a data file including a spec file located on the computer", Qt::CTRL+Qt::Key_O, this, this, SLOT(processDataFileOpen())); m_openLocationAction = WuQtUtilities::createAction("Open Location...", "Open a data file including a spec file located on a web server (http)", Qt::CTRL+Qt::Key_L, this, this, SLOT(processDataFileLocationOpen())); m_manageFilesAction = WuQtUtilities::createAction("Save/Manage Files...", "Save and Manage Loaded Files", Qt::CTRL + Qt::Key_S, this, this, SLOT(processManageSaveLoadedFiles())); m_closeSpecFileAction = WuQtUtilities::createAction("Close All Files", "Close all loaded files", this, this, SLOT(processCloseAllFiles())); m_closeTabAction = WuQtUtilities::createAction("Close Tab", "Close the active tab (window pane) in the window", Qt::CTRL + Qt::Key_W, this, m_toolbar, SLOT(closeSelectedTab())); m_closeWithoutConfirmationFlag = false; m_closeWindowActionConfirmTitle = "Close Window..."; m_closeWindowActionNoConfirmTitle = "Close Window"; m_closeWindowAction = WuQtUtilities::createAction(m_closeWindowActionConfirmTitle, "Close the window", Qt::CTRL + Qt::SHIFT + Qt::Key_W, this, this, SLOT(processCloseWindow())); m_captureImageAction = WuQtUtilities::createAction("Capture Image...", "Capture an Image of the windows content", this, this, SLOT(processCaptureImage())); m_recordMovieAction = WuQtUtilities::createAction("Animation Control...", "Animate Brain Surface", this, this, SLOT(processRecordMovie())); m_preferencesAction = WuQtUtilities::createAction("Preferences...", "Edit the preferences", this, this, SLOT(processEditPreferences())); m_exitProgramAction = WuQtUtilities::createAction("Exit", "Exit (quit) the program", Qt::CTRL+Qt::Key_Q, this, this, SLOT(processExitProgram())); m_dataFociProjectAction = WuQtUtilities::createAction("Project Foci...", "Project Foci to Surfaces", this, this, SLOT(processProjectFoci())); m_dataBorderFilesSplitAction = WuQtUtilities::createAction("Split Multi-Structure Border File(s)...", "Split Multi-Structure Border File(s", this, this, SLOT(processSplitBorderFiles())); m_viewFullScreenAction = WuQtUtilities::createAction("Full Screen", "View using all of screen", Qt::CTRL+Qt::Key_F, this); QObject::connect(m_viewFullScreenAction, SIGNAL(triggered()), this, SLOT(processViewFullScreenSelected())); /* * Note: If shortcut key is changed, also change the shortcut key * for the tile tabs configuration dialog menu item to match. */ m_viewTileTabsAction = WuQtUtilities::createAction("Tile Tabs", "View all tabs in a tiled layout", Qt::CTRL+Qt::Key_M, this); QObject::connect(m_viewTileTabsAction, SIGNAL(triggered()), this, SLOT(processViewTileTabs())); m_createAndEditTileTabsAction = WuQtUtilities::createAction("Create and Edit...", "Create, Delete, and Edit Tile Tabs Configurations", Qt::CTRL + Qt::SHIFT + Qt::Key_M, this); m_gapsAndMarginsAction = WuQtUtilities::createAction("Gaps and Margins...", "Adjust the gaps and margins", this, this, SLOT(processGapsAndMargins())); m_nextTabAction = WuQtUtilities::createAction("Next Tab", "Move to the next tab", Qt::CTRL + Qt::Key_Right, this, m_toolbar, SLOT(nextTab())); m_previousTabAction = WuQtUtilities::createAction("Previous Tab", "Move to the previous tab", Qt::CTRL + Qt::Key_Left, this, m_toolbar, SLOT(previousTab())); m_renameSelectedTabAction = WuQtUtilities::createAction("Rename Selected Tab...", "Change the name of the selected tab", this, m_toolbar, SLOT(renameTab())); m_moveTabsInWindowToNewWindowsAction = WuQtUtilities::createAction("Move All Tabs in Current Window to New Windows", "Move all but the left most tab to new windows", this, m_toolbar, SLOT(moveTabsToNewWindows())); m_moveTabsFromAllWindowsToOneWindowAction = WuQtUtilities::createAction("Move All Tabs From All Windows Into Selected Window", "Move all tabs from all windows into selected window", this, this, SLOT(processMoveAllTabsToOneWindow())); m_bringAllToFrontAction = WuQtUtilities::createAction("Bring All To Front", "Move all windows on top of other application windows", this, guiManager, SLOT(processBringAllWindowsToFront())); m_tileWindowsAction = WuQtUtilities::createAction("Tile Windows", "Arrange the windows into grid so that the fill the screen", this, guiManager, SLOT(processTileWindows())); m_informationDialogAction = WuQtUtilities::createAction("Information...", "Show the Informaiton Window", this, guiManager, SLOT(processShowInformationWindow())); m_helpHcpWebsiteAction = WuQtUtilities::createAction("Go to HCP Website...", "Load the HCP Website in your computer's web browser", this, this, SLOT(processHcpWebsiteInBrowser())); m_helpHcpFeatureRequestAction = WuQtUtilities::createAction("Submit HCP Software Feature Request...", "Go to HCP Feature Request Website in your computer's web browser", this, this, SLOT(processHcpFeatureRequestWebsiteInBrowser())); m_helpWorkbenchBugReportAction = WuQtUtilities::createAction("Report a Workbench Bug...", "Send a Workbench Bug Report", this, this, SLOT(processReportWorkbenchBug())); m_connectToAllenDatabaseAction = WuQtUtilities::createAction("Allen Brain Institute Database...", "Open a connection to the Allen Brain Institute Database", this, this, SLOT(processConnectToAllenDataBase())); m_connectToAllenDatabaseAction->setEnabled(false); m_connectToConnectomeDatabaseAction = WuQtUtilities::createAction("Human Connectome Project Database...", "Open a connection to the Human Connectome Project Database", this, this, SLOT(processConnectToConnectomeDataBase())); m_connectToConnectomeDatabaseAction->setEnabled(false); m_developerGraphicsTimingAction = WuQtUtilities::createAction("Time Graphics Update", "Show the average time for updating the windows graphics", this, this, SLOT(processDevelopGraphicsTiming())); m_developerExportVtkFileAction = WuQtUtilities::createAction("Export to VTK File", "Export model(s) to VTK File", this, this, SLOT(processDevelopExportVtkFile())); } /** * Create menus for this window. */ void BrainBrowserWindow::createMenus() { CaretPreferences* prefs = SessionManager::get()->getCaretPreferences(); /* * Create the menu bar and add menus to it. */ QMenuBar* menubar = menuBar(); menubar->addMenu(createMenuFile()); menubar->addMenu(createMenuEdit()); menubar->addMenu(createMenuView()); QMenu* dataMenu = createMenuData(); if (dataMenu != NULL) { menubar->addMenu(dataMenu); } menubar->addMenu(createMenuSurface()); QMenu* volumeMenu = createMenuVolume(); if (volumeMenu != NULL) { menubar->addMenu(volumeMenu); } QMenu* connectMenu = createMenuConnect(); if (connectMenu != NULL) { menubar->addMenu(connectMenu); } QMenu* developMenu = createMenuDevelop(); m_developMenuAction = menubar->addMenu(developMenu); m_developMenuAction->setVisible(prefs->isDevelopMenuEnabled()); menubar->addMenu(createMenuWindow()); menubar->addMenu(createMenuHelp()); } /** * @return Create and return the developer menu. */ QMenu* BrainBrowserWindow::createMenuDevelop() { QMenu* menu = new QMenu("Develop", this); QObject::connect(menu, SIGNAL(aboutToShow()), this, SLOT(developerMenuAboutToShow())); menu->addAction(m_developerExportVtkFileAction); /* * Hide the Export to VTK menu item */ m_developerExportVtkFileAction->setVisible(false); menu->addAction(m_developerGraphicsTimingAction); std::vector developerFlags; DeveloperFlagsEnum::getAllEnums(developerFlags); m_developerFlagsActionGroup = NULL; if ( ! developerFlags.empty()) { menu->addSeparator(); m_developerFlagsActionGroup = new QActionGroup(this); m_developerFlagsActionGroup->setExclusive(false); QObject::connect(m_developerFlagsActionGroup, SIGNAL(triggered(QAction*)), this, SLOT(developerMenuFlagTriggered(QAction*))); for (std::vector::iterator iter = developerFlags.begin(); iter != developerFlags.end(); iter++) { const DeveloperFlagsEnum::Enum flag = *iter; QAction* action = menu->addAction(DeveloperFlagsEnum::toGuiName(flag)); action->setCheckable(true); action->setData(static_cast(DeveloperFlagsEnum::toIntegerCode(flag))); m_developerFlagsActionGroup->addAction(action); } } return menu; } /** * Called when developer menu is about to show. */ void BrainBrowserWindow::developerMenuAboutToShow() { std::vector developerFlags; DeveloperFlagsEnum::getAllEnums(developerFlags); QList actions = m_developerFlagsActionGroup->actions(); QListIterator iter(actions); while (iter.hasNext()) { QAction* action = iter.next(); const int integerCode = action->data().toInt(); bool valid = false; const DeveloperFlagsEnum::Enum enumValue = DeveloperFlagsEnum::fromIntegerCode(integerCode, &valid); if (valid) { const bool flag = DeveloperFlagsEnum::isFlag(enumValue); action->setChecked(flag); } else { CaretLogSevere("Failed to find develper flag for updating menu: " + action->text()); } } } /** * Called when developer flag is checked/unchecked. * * @param action * Action that is checked/unchecked. */ void BrainBrowserWindow::developerMenuFlagTriggered(QAction* action) { const int integerCode = action->data().toInt(); bool valid = false; const DeveloperFlagsEnum::Enum enumValue = DeveloperFlagsEnum::fromIntegerCode(integerCode, &valid); if (valid) { DeveloperFlagsEnum::setFlag(enumValue, action->isChecked()); /* * Update graphics and GUI */ EventManager::get()->sendEvent(EventSurfaceColoringInvalidate().getPointer()); EventManager::get()->sendEvent(EventUserInterfaceUpdate().getPointer()); EventManager::get()->sendEvent(EventGraphicsUpdateAllWindows().getPointer()); } else { CaretLogSevere("Failed to find develper flag for reading menu: " + action->text()); } } /** * Create the file menu. * @return the file menu. */ QMenu* BrainBrowserWindow::createMenuFile() { QMenu* menu = new QMenu("File", this); QObject::connect(menu, SIGNAL(aboutToShow()), this, SLOT(processFileMenuAboutToShow())); menu->addAction(m_aboutWorkbenchAction); menu->addAction(m_preferencesAction); #ifndef CARET_OS_MACOSX menu->addSeparator(); #endif // CARET_OS_MACOSX menu->addAction(m_newWindowAction); menu->addAction(m_newTabAction); menu->addAction(m_duplicateTabAction); menu->addSeparator(); menu->addAction(m_openFileAction); menu->addAction(m_openLocationAction); m_recentSpecFileMenuOpenConfirmTitle = "Open Recent Spec File"; m_recentSpecFileMenuLoadNoConfirmTitle = "Load All Files in Recent Spec File"; m_recentSpecFileMenu = menu->addMenu(m_recentSpecFileMenuOpenConfirmTitle); QObject::connect(m_recentSpecFileMenu, SIGNAL(aboutToShow()), this, SLOT(processRecentSpecFileMenuAboutToBeDisplayed())); QObject::connect(m_recentSpecFileMenu, SIGNAL(triggered(QAction*)), this, SLOT(processRecentSpecFileMenuSelection(QAction*))); m_recentSceneFileMenu = menu->addMenu("Open Recent Scene File"); QObject::connect(m_recentSceneFileMenu, SIGNAL(aboutToShow()), this, SLOT(processRecentSceneFileMenuAboutToBeDisplayed())); 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(); menu->addAction(m_recordMovieAction); menu->addAction(m_captureImageAction); menu->addSeparator(); menu->addAction(m_closeTabAction); menu->addAction(m_closeWindowAction); menu->addSeparator(); #ifndef CARET_OS_MACOSX menu->addSeparator(); #endif // CARET_OS_MACOSX menu->addAction(m_exitProgramAction); return menu; } /** * Create an item for the edit menu. * * @param editMenu * The menu. * @param editMenuItem * Enumerated by that is inserted into the edit menu. * @return * Action that is created by adding item to menu. */ static QAction* addItemToEditMenu(QMenu* editMenu, const BrainBrowserWindowEditMenuItemEnum::Enum editMenuItem) { QAction* action = editMenu->addAction(BrainBrowserWindowEditMenuItemEnum::toGuiName(editMenuItem)); action->setData(static_cast(BrainBrowserWindowEditMenuItemEnum::toIntegerCode(editMenuItem))); action->setShortcut(BrainBrowserWindowEditMenuItemEnum::toShortcut(editMenuItem)); return action; } /** * @return A new instance of the edit menu. */ QMenu* BrainBrowserWindow::createMenuEdit() { /* * Why is there a space after 'Edit'. * If there is not a space, the Mac will see 'Edit' and add * two additional menu items "Start Dictation" and * "Emoji and Symbols". Use of these menu items * will sometimes cause a crash in the FTGL font code. */ m_editMenu = new QMenu("Edit "); m_editMenuUndoAction = addItemToEditMenu(m_editMenu, BrainBrowserWindowEditMenuItemEnum::UNDO); m_editMenuRedoAction = addItemToEditMenu(m_editMenu, BrainBrowserWindowEditMenuItemEnum::REDO); QAction* cutAction = addItemToEditMenu(m_editMenu, BrainBrowserWindowEditMenuItemEnum::CUT); addItemToEditMenu(m_editMenu, BrainBrowserWindowEditMenuItemEnum::COPY); addItemToEditMenu(m_editMenu, BrainBrowserWindowEditMenuItemEnum::PASTE); addItemToEditMenu(m_editMenu, BrainBrowserWindowEditMenuItemEnum::PASTE_SPECIAL); QKeySequence noKeySequence; addItemToEditMenu(m_editMenu, BrainBrowserWindowEditMenuItemEnum::DELETER); QAction* selectAllAction = NULL; const bool addSelectAllFlag = true; if (addSelectAllFlag) { selectAllAction = addItemToEditMenu(m_editMenu, BrainBrowserWindowEditMenuItemEnum::SELECT_ALL); } m_editMenu->insertSeparator(cutAction); if (selectAllAction != NULL) { m_editMenu->insertSeparator(selectAllAction); } QObject::connect(m_editMenu, SIGNAL(aboutToShow()), this, SLOT(processEditMenuAboutToShow())); QObject::connect(m_editMenu, SIGNAL(triggered(QAction*)), this, SLOT(processEditMenuItemTriggered(QAction*))); return m_editMenu; } /** * Gets called when an item is selected from the edit menu. * * @param action * Action (menu items) that was selected. */ void BrainBrowserWindow::processEditMenuItemTriggered(QAction* action) { EventGetOrSetUserInputModeProcessor inputEvent(m_browserWindowIndex); EventManager::get()->sendEvent(inputEvent.getPointer()); UserInputModeAbstract* inputProcessor = inputEvent.getUserInputProcessor(); if (inputProcessor != NULL) { const int actionDataInt = action->data().toInt(); bool validActionDataIntFlag = false; const BrainBrowserWindowEditMenuItemEnum::Enum item = BrainBrowserWindowEditMenuItemEnum::fromIntegerCode(actionDataInt, &validActionDataIntFlag); if (validActionDataIntFlag) { inputProcessor->processEditMenuItemSelection(item); } else { CaretLogSevere("Invalid conversion of integer code " + AString::number(actionDataInt) + " to BrainBrowserWindowEditMenuItemEnum::Enum"); } } /* * If Cut or Copy is selected and successful, * the menu needs to be updated so that * paste will be enabled for selection * via a shortcut key. */ processEditMenuAboutToShow(); } /** * Gets called when the edit menu is about to show. */ void BrainBrowserWindow::processEditMenuAboutToShow() { EventGetOrSetUserInputModeProcessor inputEvent(m_browserWindowIndex); EventManager::get()->sendEvent(inputEvent.getPointer()); /* * Get edit menu items that are enabled by the input processor */ std::vector editMenuItemsEnabled; UserInputModeAbstract* inputProcessor = inputEvent.getUserInputProcessor(); AString redoMenuItemSuffix; AString undoMenuItemSuffix; AString pasteText; AString pasteSpecialText; if (inputProcessor != NULL) { inputProcessor->getEnabledEditMenuItems(editMenuItemsEnabled, redoMenuItemSuffix, undoMenuItemSuffix, pasteText, pasteSpecialText); } /* * Enable/Disable each of the edit menu's actions */ QList menuActions = m_editMenu->actions(); QListIterator menuActionsIterator(menuActions); while (menuActionsIterator.hasNext()) { QAction* action = menuActionsIterator.next(); if ( ! action->isSeparator()) { const int actionDataInt = action->data().toInt(); bool validActionDataIntFlag = false; const BrainBrowserWindowEditMenuItemEnum::Enum editMenuItem = BrainBrowserWindowEditMenuItemEnum::fromIntegerCode(actionDataInt, &validActionDataIntFlag); action->setEnabled(false); if ( ! editMenuItemsEnabled.empty()) { if (validActionDataIntFlag) { if (std::find(editMenuItemsEnabled.begin(), editMenuItemsEnabled.end(), editMenuItem) != editMenuItemsEnabled.end()) { action->setEnabled(true); switch (editMenuItem) { case BrainBrowserWindowEditMenuItemEnum::COPY: break; case BrainBrowserWindowEditMenuItemEnum::CUT: break; case BrainBrowserWindowEditMenuItemEnum::DELETER: break; case BrainBrowserWindowEditMenuItemEnum::PASTE: if (pasteText.isEmpty()) { action->setText(BrainBrowserWindowEditMenuItemEnum::toGuiName(editMenuItem)); } else { action->setText(pasteText); } break; case BrainBrowserWindowEditMenuItemEnum::PASTE_SPECIAL: if (pasteSpecialText.isEmpty()) { action->setText(BrainBrowserWindowEditMenuItemEnum::toGuiName(editMenuItem)); } else { action->setText(pasteSpecialText); } break; case BrainBrowserWindowEditMenuItemEnum::REDO: break; case BrainBrowserWindowEditMenuItemEnum::SELECT_ALL: break; case BrainBrowserWindowEditMenuItemEnum::UNDO: break; } } } else { CaretLogSevere("Invalid conversion of integer code " + AString::number(actionDataInt) + " to BrainBrowserWindowEditMenuItemEnum::Enum"); } } } } /* * Redo menu may have a suffix */ AString redoMenuItemText("Redo"); if (std::find(editMenuItemsEnabled.begin(), editMenuItemsEnabled.end(), BrainBrowserWindowEditMenuItemEnum::REDO) != editMenuItemsEnabled.end()) { if ( ! redoMenuItemSuffix.isEmpty()) { redoMenuItemText.append(" " + redoMenuItemSuffix); } } m_editMenuRedoAction->setText(redoMenuItemText); /* * Undo menu may have a suffix */ AString undoMenuItemText("Undo"); if (std::find(editMenuItemsEnabled.begin(), editMenuItemsEnabled.end(), BrainBrowserWindowEditMenuItemEnum::UNDO) != editMenuItemsEnabled.end()) { if ( ! undoMenuItemSuffix.isEmpty()) { undoMenuItemText.append(" " + undoMenuItemSuffix); } } m_editMenuUndoAction->setText(undoMenuItemText); } /** * Called to display the overlay toolbox. */ void BrainBrowserWindow::processShowOverlayToolBox(bool status) { m_overlayActiveToolBox->setVisible(status); m_overlayToolBoxAction->blockSignals(true); m_overlayToolBoxAction->setChecked(status); m_overlayToolBoxAction->blockSignals(false); if (status) { m_overlayToolBoxAction->setToolTip("Hide Overlay Toolbox"); EventManager::get()->sendEvent(EventUserInterfaceUpdate().setWindowIndex(m_browserWindowIndex).addToolBox().getPointer()); } else { m_overlayToolBoxAction->setToolTip("Show Overlay Toolbox"); } } /** * Called when visibility of horizontal overlay toolbox is changed. * @param visible * New visibility status. */ void BrainBrowserWindow::processOverlayHorizontalToolBoxVisibilityChanged(bool visible) { if (visible == false) { if (m_overlayActiveToolBox == m_overlayHorizontalToolBox) { m_overlayToolBoxAction->blockSignals(true); m_overlayToolBoxAction->setChecked(false); m_overlayToolBoxAction->blockSignals(false); } } } /** * Called when visibility of vertical overlay toolbox is changed. * @param visible * New visibility status. */ void BrainBrowserWindow::processOverlayVerticalToolBoxVisibilityChanged(bool visible) { if (visible == false) { if (m_overlayActiveToolBox == m_overlayVerticalToolBox) { m_overlayToolBoxAction->blockSignals(true); m_overlayToolBoxAction->setChecked(false); m_overlayToolBoxAction->blockSignals(false); } } } /** * Called when File Menu is about to show. */ void BrainBrowserWindow::processFileMenuAboutToShow() { if (isMacOptionKeyDown()) { m_closeWindowAction->setText(m_closeWindowActionNoConfirmTitle); m_recentSpecFileMenu->setTitle(m_recentSpecFileMenuLoadNoConfirmTitle); } else { m_closeWindowAction->setText(m_closeWindowActionConfirmTitle); m_recentSpecFileMenu->setTitle(m_recentSpecFileMenuOpenConfirmTitle); } } void BrainBrowserWindow::processCloseWindow() { if (m_closeWindowAction->text() == m_closeWindowActionConfirmTitle) { /* * When confirming, just use close slot and it will result * in confirmation dialog being displayed. */ m_closeWithoutConfirmationFlag = false; close(); } else if (m_closeWindowAction->text() == m_closeWindowActionNoConfirmTitle) { m_closeWithoutConfirmationFlag = true; close(); } else { CaretAssert(0); } } /** * @return Is the Option Key down on a Mac. * Always returns false if not a Mac. */ bool BrainBrowserWindow::isMacOptionKeyDown() const { bool keyDown = false; #ifdef CARET_OS_MACOSX keyDown = (QApplication::queryKeyboardModifiers() == Qt::AltModifier); #endif // CARET_OS_MACOSX return keyDown; } /** * Called when Open Recent Spec File Menu is about to be displayed * and creates the content of the menu. */ void BrainBrowserWindow::processRecentSpecFileMenuAboutToBeDisplayed() { m_recentSpecFileMenu->clear(); const int32_t numRecentSpecFiles = BrainBrowserWindow::loadRecentSpecFileMenu(m_recentSpecFileMenu); if (numRecentSpecFiles > 0) { m_recentSpecFileMenu->addSeparator(); QAction* action = new QAction("Clear Menu", m_recentSpecFileMenu); action->setData("CLEAR_CLEAR"); m_recentSpecFileMenu->addAction(action); } } /** * Load a menu with recent spec files. This method only ADDS * items to the menu, nothing is removed or cleared. * * @param recentSpecFileMenu * Menu to which recent spec files are added. * @return * Returns the number of recent spec files added to the menu. */ int32_t BrainBrowserWindow::loadRecentSpecFileMenu(QMenu* recentSpecFileMenu) { CaretPreferences* prefs = SessionManager::get()->getCaretPreferences(); std::vector recentSpecFiles; prefs->getPreviousSpecFiles(recentSpecFiles); const int32_t numRecentSpecFiles = static_cast(recentSpecFiles.size()); for (int32_t i = 0; i < numRecentSpecFiles; i++) { AString actionName; AString actionFullPath; if (DataFile::isFileOnNetwork(recentSpecFiles[i])) { actionName = recentSpecFiles[i]; actionFullPath = recentSpecFiles[i]; } else { FileInformation fileInfo(recentSpecFiles[i]); QString path = fileInfo.getPathName(); QString name = fileInfo.getFileName(); if (path.isEmpty() == false) { name += (" (" + path + ")"); } actionName = name; actionFullPath = fileInfo.getAbsoluteFilePath(); } QAction* action = new QAction(actionName, recentSpecFileMenu); /* * If this "setData()" action changes you will need to update: * (1) BrainBrowserWindow::processRecentSpecFileMenuSelection * (2) MacDockMenu::menuActionTriggered */ action->setData(actionFullPath); recentSpecFileMenu->addAction(action); } return numRecentSpecFiles; } /** * Called when an item is selected from the recent spec file * menu. * @param itemAction * Action of the menu item that was selected. */ void BrainBrowserWindow::processRecentSpecFileMenuSelection(QAction* itemAction) { //AString errorMessages; const AString specFileName = itemAction->data().toString(); if (specFileName == "CLEAR_CLEAR") { CaretPreferences* prefs = SessionManager::get()->getCaretPreferences(); prefs->clearPreviousSpecFiles(); return; } if ( ! specFileName.isEmpty()) { SpecFile specFile; try { specFile.readFile(specFileName); if (m_recentSpecFileMenu->title() == m_recentSpecFileMenuOpenConfirmTitle) { if (GuiManager::get()->processShowOpenSpecFileDialog(&specFile, this)) { m_toolbar->addDefaultTabsAfterLoadingSpecFile(); } } else if (m_recentSpecFileMenu->title() == m_recentSpecFileMenuLoadNoConfirmTitle) { std::vector fileNamesToLoad; fileNamesToLoad.push_back(specFileName); loadFilesFromCommandLine(fileNamesToLoad, BrainBrowserWindow::LOAD_SPEC_FILE_CONTENTS_VIA_COMMAND_LINE); m_toolbar->addDefaultTabsAfterLoadingSpecFile(); } else { CaretAssert(0); } } catch (const DataFileException& e) { //errorMessages += e.whatString(); QMessageBox::critical(this, "ERROR", e.whatString()); return; } EventManager::get()->sendEvent(EventSurfaceColoringInvalidate().getPointer()); EventManager::get()->sendEvent(EventUserInterfaceUpdate().getPointer()); EventManager::get()->sendEvent(EventGraphicsUpdateAllWindows().getPointer()); } } /** * Called when Open Recent Scene File Menu is about to be displayed * and creates the content of the menu. */ void BrainBrowserWindow::processRecentSceneFileMenuAboutToBeDisplayed() { m_recentSceneFileMenu->clear(); const int32_t numRecentSceneFiles = BrainBrowserWindow::loadRecentSceneFileMenu(m_recentSceneFileMenu); if (numRecentSceneFiles > 0) { m_recentSceneFileMenu->addSeparator(); QAction* action = new QAction("Clear Menu", m_recentSceneFileMenu); action->setData("CLEAR_CLEAR"); m_recentSceneFileMenu->addAction(action); } } /** * Load a menu with recent scene files. This method only ADDS * items to the menu, nothing is removed or cleared. * * @param recentSceneFileMenu * Menu to which recent scene files are added. * @return * Returns the number of recent scene files added to the menu. */ int32_t BrainBrowserWindow::loadRecentSceneFileMenu(QMenu* recentSceneFileMenu) { CaretPreferences* prefs = SessionManager::get()->getCaretPreferences(); std::vector recentSceneFiles; prefs->getPreviousSceneFiles(recentSceneFiles); const int32_t numRecentSceneFiles = static_cast(recentSceneFiles.size()); for (int32_t i = 0; i < numRecentSceneFiles; i++) { AString actionName; AString actionFullPath; if (DataFile::isFileOnNetwork(recentSceneFiles[i])) { actionName = recentSceneFiles[i]; actionFullPath = recentSceneFiles[i]; } else { FileInformation fileInfo(recentSceneFiles[i]); QString path = fileInfo.getPathName(); QString name = fileInfo.getFileName(); if (path.isEmpty() == false) { name += (" (" + path + ")"); } actionName = name; actionFullPath = fileInfo.getAbsoluteFilePath(); } QAction* action = new QAction(actionName, recentSceneFileMenu); /* * If this "setData()" action changes you will need to update: * (1) BrainBrowserWindow::processRecentSceneFileMenuSelection * (2) MacDockMenu::menuActionTriggered */ action->setData(actionFullPath); recentSceneFileMenu->addAction(action); } return numRecentSceneFiles; } /** * Called when an item is selected from the recent scene file * menu. * @param itemAction * Action of the menu item that was selected. */ void BrainBrowserWindow::processRecentSceneFileMenuSelection(QAction* itemAction) { //AString errorMessages; const AString sceneFileName = itemAction->data().toString(); if (sceneFileName == "CLEAR_CLEAR") { CaretPreferences* prefs = SessionManager::get()->getCaretPreferences(); prefs->clearPreviousSceneFiles(); return; } if ( ! sceneFileName.isEmpty()) { std::vector filenamesVector; filenamesVector.push_back(sceneFileName); std::vector dataFileTypes; dataFileTypes.push_back(DataFileTypeEnum::SCENE); loadFiles(this, filenamesVector, dataFileTypes, LOAD_SPEC_FILE_CONTENTS_VIA_COMMAND_LINE, "", ""); EventManager::get()->sendEvent(EventSurfaceColoringInvalidate().getPointer()); EventManager::get()->sendEvent(EventUserInterfaceUpdate().getPointer()); EventManager::get()->sendEvent(EventGraphicsUpdateAllWindows().getPointer()); } } /** * Called when view menu is about to show. */ void BrainBrowserWindow::processViewMenuAboutToShow() { m_viewMoveFeaturesToolBoxMenu->setEnabled(isFullScreen() == false); m_viewMoveOverlayToolBoxMenu->setEnabled(isFullScreen()== false); if (isFullScreen()) { m_viewFullScreenAction->setText("Exit Full Screen"); } else { m_viewFullScreenAction->setText("Enter Full Screen"); } if (isTileTabsSelected()) { m_viewTileTabsAction->setText("Exit Tile Tabs"); } else { m_viewTileTabsAction->setText("Enter Tile Tabs"); } } /** * Create the view menu. * @return the view menu. */ QMenu* BrainBrowserWindow::createMenuView() { m_tileTabsMenu = new QMenu("Tile Tabs Configuration"); QObject::connect(m_tileTabsMenu, SIGNAL(aboutToShow()), this, SLOT(processTileTabsMenuAboutToBeDisplayed())); QObject::connect(m_tileTabsMenu, SIGNAL(triggered(QAction*)), this, SLOT(processTileTabsMenuSelection(QAction*))); m_tileTabsMenu->addAction(m_createAndEditTileTabsAction); m_tileTabsMenu->addSeparator(); QMenu* menu = new QMenu("View", this); QObject::connect(menu, SIGNAL(aboutToShow()), this, SLOT(processViewMenuAboutToShow())); m_viewMoveFeaturesToolBoxMenu = createMenuViewMoveFeaturesToolBox(); m_viewMoveOverlayToolBoxMenu = createMenuViewMoveOverlayToolBox(); menu->addAction(m_showToolBarAction); menu->addMenu(m_viewMoveFeaturesToolBoxMenu); menu->addMenu(m_viewMoveOverlayToolBoxMenu); menu->addSeparator(); menu->addAction(m_viewFullScreenAction); menu->addAction(m_viewTileTabsAction); menu->addSeparator(); menu->addAction(m_gapsAndMarginsAction); menu->addMenu(m_tileTabsMenu); return menu; } /** * @return Create and return the overlay toolbox menu. */ QMenu* BrainBrowserWindow::createMenuViewMoveFeaturesToolBox() { QMenu* menu = new QMenu("Features Toolbox", this); menu->addAction("Attach to Right", this, SLOT(processMoveFeaturesToolBoxToRight())); menu->addAction("Detach", this, SLOT(processMoveFeaturesToolBoxToFloat())); menu->addAction("Hide", this, SLOT(processHideFeaturesToolBox())); return menu; } /** * @return Create and return the overlay toolbox menu. */ QMenu* BrainBrowserWindow::createMenuViewMoveOverlayToolBox() { QMenu* menu = new QMenu("Overlay Toolbox", this); menu->addAction("Attach to Bottom", this, SLOT(processMoveOverlayToolBoxToBottom())); menu->addAction("Attach to Left", this, SLOT(processMoveOverlayToolBoxToLeft())); menu->addAction("Detach", this, SLOT(processMoveOverlayToolBoxToFloat())); menu->addAction("Hide", this, SLOT(processHideOverlayToolBox())); return menu; } /** * Update the Tile Tabs Configuration Menu just before it is displayed. */ void BrainBrowserWindow::processTileTabsMenuAboutToBeDisplayed() { /* * Remove all actions for user-defined tile tabs configurations */ QList menuActions = m_tileTabsMenu->actions(); QListIterator actionIterator(menuActions); while (actionIterator.hasNext()) { QAction* action = actionIterator.next(); if (action != m_createAndEditTileTabsAction) { if (action->isSeparator() == false) { m_tileTabsMenu->removeAction(action); delete action; } } } CaretPreferences* preferences = SessionManager::get()->getCaretPreferences(); preferences->readTileTabsConfigurations(); std::vector configurations = preferences->getTileTabsConfigurationsSortedByName(); const int32_t numConfigurations = static_cast(configurations.size()); /* * Add All Tabs (Default) tile tabs configuration */ QAction* defaultAction = m_tileTabsMenu->addAction(m_defaultTileTabsConfiguration->getName()); defaultAction->setCheckable(true); defaultAction->setData(QVariant(m_defaultTileTabsConfiguration->getUniqueIdentifier())); /* * Add the scene configuration */ QAction* sceneAction = m_tileTabsMenu->addAction(m_sceneTileTabsConfiguration->getName()); sceneAction->setCheckable(true); sceneAction->setData(QVariant(m_sceneTileTabsConfiguration->getUniqueIdentifier())); //sceneAction->setEnabled(false); bool haveSelectedTileTabsConfiguration = false; /* * Add the user defined Tile Tabs configurations */ if (numConfigurations > 0) { m_tileTabsMenu->addSeparator(); for (int32_t i = 0; i < numConfigurations; i++) { QAction* action = m_tileTabsMenu->addAction(configurations[i]->getName()); action->setCheckable(true); if (m_selectedTileTabsConfigurationUniqueIdentifier == configurations[i]->getUniqueIdentifier()) { action->setChecked(true); haveSelectedTileTabsConfiguration = true; } action->setData(QVariant(configurations[i]->getUniqueIdentifier())); } } if (m_selectedTileTabsConfigurationUniqueIdentifier == m_sceneTileTabsConfiguration->getUniqueIdentifier()) { haveSelectedTileTabsConfiguration = true; sceneAction->setChecked(true); } if ( ! haveSelectedTileTabsConfiguration) { defaultAction->setChecked(true); m_selectedTileTabsConfigurationUniqueIdentifier = m_defaultTileTabsConfiguration->getUniqueIdentifier(); } } /** * Process a selection from the Tile Tab Configuration Menu. * * @param action * Action that was selected. */ void BrainBrowserWindow::processTileTabsMenuSelection(QAction* action) { if (action == m_createAndEditTileTabsAction) { processViewTileTabsConfigurationDialog(); } else { m_selectedTileTabsConfigurationUniqueIdentifier = action->data().toString(); if (isTileTabsSelected()) { EventManager::get()->sendEvent(EventGraphicsUpdateOneWindow(m_browserWindowIndex).getPointer()); } } } /* * Set the selected tile tabs configuration. If requested configuration is * NULL, the default configuration will be selected. * * @param configuration * New selection for tile tabs configuration. */ void BrainBrowserWindow::setSelectedTileTabsConfiguration(TileTabsConfiguration* configuration) { if (configuration != NULL) { m_selectedTileTabsConfigurationUniqueIdentifier = configuration->getUniqueIdentifier(); } else { m_selectedTileTabsConfigurationUniqueIdentifier = m_defaultTileTabsConfiguration->getUniqueIdentifier(); } } /** * @return The selected tile tabs configuration. */ TileTabsConfiguration* BrainBrowserWindow::getSelectedTileTabsConfiguration() { CaretPreferences* preferences = SessionManager::get()->getCaretPreferences(); TileTabsConfiguration* configuration = preferences->getTileTabsConfigurationByUniqueIdentifier(m_selectedTileTabsConfigurationUniqueIdentifier); if (configuration != NULL) { /* * A user configuration is selected */ return configuration; } else if (m_selectedTileTabsConfigurationUniqueIdentifier == m_defaultTileTabsConfiguration->getUniqueIdentifier()) { /* * Default configuration is selected */ return m_defaultTileTabsConfiguration; } else if (m_selectedTileTabsConfigurationUniqueIdentifier == m_sceneTileTabsConfiguration->getUniqueIdentifier()) { /* * Scene's configuration is selected */ return m_sceneTileTabsConfiguration; } else { /* * The selected configuration has been deleted, likely in another browser window * so selecte the default configuration */ m_selectedTileTabsConfigurationUniqueIdentifier = m_defaultTileTabsConfiguration->getUniqueIdentifier(); } return m_defaultTileTabsConfiguration; } /** * Create the connect menu. * @return the connect menu. */ QMenu* BrainBrowserWindow::createMenuConnect() { QMenu* menu = new QMenu("Connect"); if (m_connectToAllenDatabaseAction->isEnabled()) { menu->addAction(m_connectToAllenDatabaseAction); } if (m_connectToConnectomeDatabaseAction->isEnabled()) { menu->addAction(m_connectToConnectomeDatabaseAction); } /* * If none of the actions are enabled, the menu will be * empty so there is no need to display the menu. */ if (menu->isEmpty()) { delete menu; menu = NULL; } return menu; } /** * Create the data menu. * @return the data menu. */ QMenu* BrainBrowserWindow::createMenuData() { QMenu* menu = new QMenu("Data", this); QObject::connect(menu, SIGNAL(aboutToShow()), this, SLOT(processDataMenuAboutToShow())); menu->addAction(m_dataFociProjectAction); menu->addAction(m_dataBorderFilesSplitAction); return menu; } /** * Called when Data Menu is about to show. */ void BrainBrowserWindow::processDataMenuAboutToShow() { Brain* brain = GuiManager::get()->getBrain(); bool haveFociFiles = (GuiManager::get()->getBrain()->getNumberOfFociFiles() > 0); m_dataFociProjectAction->setEnabled(haveFociFiles); bool haveMultiStructureBorderFiles = false; const int32_t numBorderFiles = brain->getNumberOfBorderFiles(); for (int32_t i = 0; i < numBorderFiles; i++) { if ( ! brain->getBorderFile(i)->isSingleStructure()) { haveMultiStructureBorderFiles = true; break; } } m_dataBorderFilesSplitAction->setEnabled(haveMultiStructureBorderFiles); } /** * Create the surface menu. * @return the surface menu. */ QMenu* BrainBrowserWindow::createMenuSurface() { QMenu* menu = new QMenu("Surface", this); menu->addAction("Information...", this, SLOT(processSurfaceMenuInformation())); menu->addAction("Properties...", this, SLOT(processShowSurfacePropertiesDialog())); menu->addAction("Primary Anatomical...", this, SLOT(processSurfaceMenuPrimaryAnatomical())); return menu; } /** * Called when Primary Anatomical is selected from the surface menu. */ void BrainBrowserWindow::processSurfaceMenuPrimaryAnatomical() { Brain* brain = GuiManager::get()->getBrain(); const int32_t numBrainStructures = brain->getNumberOfBrainStructures(); if (numBrainStructures <= 0) { return; } WuQDataEntryDialog ded("Primary Anatomical Surfaces", this); std::vector surfaceSelectionControls; for (int32_t i = 0; i < numBrainStructures; i++) { BrainStructure* bs = brain->getBrainStructure(i); SurfaceSelectionViewController* ssc = ded.addSurfaceSelectionViewController(StructureEnum::toGuiName(bs->getStructure()), bs); ssc->setSurface(bs->getPrimaryAnatomicalSurface()); surfaceSelectionControls.push_back(ssc); } if (ded.exec() == WuQDataEntryDialog::Accepted) { for (int32_t i = 0; i < numBrainStructures; i++) { BrainStructure* bs = brain->getBrainStructure(i); bs->setPrimaryAnatomicalSurface(surfaceSelectionControls[i]->getSurface()); } EventManager::get()->sendEvent(EventGraphicsUpdateAllWindows().getPointer()); } } /** * Called when Information is selected from the surface menu. */ void BrainBrowserWindow::processSurfaceMenuInformation() { BrowserTabContent* btc = getBrowserTabContent(); if (btc != NULL) { AString txt = ""; Model* mdc = btc->getModelForDisplay(); ModelSurface* mdcs = dynamic_cast(mdc); if (mdcs != NULL) { txt += mdcs->getSurface()->getInformation(); } ModelWholeBrain* mdcwb = dynamic_cast(mdc); if (mdcwb != NULL) { std::vector allStructures; StructureEnum::getAllEnums(allStructures); for (std::vector::iterator iter = allStructures.begin(); iter != allStructures.end(); iter++) { const Surface* surface = mdcwb->getSelectedSurface(*iter, btc->getTabNumber()); if (surface != NULL) { txt += surface->getInformation(); txt += "\n"; } } } ModelSurfaceMontage* msm = dynamic_cast(mdc); if (msm != NULL) { std::vector surfaces; msm->getSelectedConfiguration(btc->getTabNumber())->getDisplayedSurfaces(surfaces); for (std::vector::iterator iter = surfaces.begin(); iter != surfaces.end(); iter++) { const Surface* s = *iter; txt += s->getInformation(); txt += "\n"; } } if (txt.isEmpty() == false) { WuQMessageBox::informationOk(this, txt); } } } /** * Create the volume menu. * @return the volume menu. */ QMenu* BrainBrowserWindow::createMenuVolume() { // QMenu* menu = new QMenu("Volume", this); // return menu; return NULL; } /** * Create the window menu. * @return the window menu. */ QMenu* BrainBrowserWindow::createMenuWindow() { m_moveSelectedTabToWindowMenu = new QMenu("Move Selected Tab to Window"); QObject::connect(m_moveSelectedTabToWindowMenu, SIGNAL(aboutToShow()), this, SLOT(processMoveSelectedTabToWindowMenuAboutToBeDisplayed())); QObject::connect(m_moveSelectedTabToWindowMenu, SIGNAL(triggered(QAction*)), 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->addSeparator(); menu->addAction(m_nextTabAction); menu->addAction(m_previousTabAction); menu->addAction(m_renameSelectedTabAction); menu->addSeparator(); menu->addAction(m_moveTabsInWindowToNewWindowsAction); menu->addAction(m_moveTabsFromAllWindowsToOneWindowAction); menu->addMenu(m_moveSelectedTabToWindowMenu); menu->addSeparator(); /* * Cannot use the Identify action since it sets the "checkable" attribute * and this will add checkbox and checkmark to the menu. In addition, * using the identify action would also hide the help viewer if it is * displayed and we don't want that. */ QAction* identifyAction = GuiManager::get()->getIdentifyBrainordinateDialogDisplayAction(); menu->addAction(identifyAction->text(), this, SLOT(processShowIdentifyBrainordinateDialog())); menu->addAction(m_informationDialogAction); menu->addAction(GuiManager::get()->getSceneDialogDisplayAction()); menu->addSeparator(); menu->addAction(m_bringAllToFrontAction); menu->addAction(m_tileWindowsAction); return menu; } /** * Create the help menu. * @return the help menu. */ QMenu* BrainBrowserWindow::createMenuHelp() { QMenu* menu = new QMenu("Help", this); /* * Cannot use the Help action since it sets the "checkable" attribute * and this will add checkbox and checkmark to the menu. In addition, * using the help action would also hide the help viewer if it is * dispalyed and we don't want that. */ QAction* helpAction = GuiManager::get()->getHelpViewerDialogDisplayAction(); menu->addAction(helpAction->text(), this, SLOT(processShowHelpInformation())); menu->addSeparator(); menu->addAction(m_helpHcpWebsiteAction); menu->addAction(m_helpWorkbenchBugReportAction); menu->addAction(m_helpHcpFeatureRequestAction); return menu; } /** * Called to show/hide help content. */ void BrainBrowserWindow::processShowHelpInformation() { /* * Always display the help viewer when selected from the menu. * Even if the help viewer is active it may be under other windows * so triggering it will cause it to display. */ QAction* helpAction = GuiManager::get()->getHelpViewerDialogDisplayAction(); if (helpAction->isChecked()) { helpAction->blockSignals(true); helpAction->setChecked(false); helpAction->blockSignals(false); } helpAction->trigger(); } void BrainBrowserWindow::processShowIdentifyBrainordinateDialog() { /* * Always display the identify dialog when selected from the menu. * Even if the identify dialog is active it may be under other windows * so triggering it will cause it to display. */ GuiManager::get()->showHideIdentfyBrainordinateDialog(true, this); } /** * Time the graphics drawing. */ void BrainBrowserWindow::processDevelopGraphicsTiming() { ElapsedTimer et; et.start(); const int32_t numTimes = 5; for (int32_t i = 0; i < numTimes; i++) { EventManager::get()->sendEvent(EventGraphicsUpdateOneWindow(m_browserWindowIndex).getPointer()); } const float time = et.getElapsedTimeSeconds() / numTimes; const AString timeString = AString::number(time, 'f', 5); const AString msg = ("Time to draw graphics (seconds): " + timeString); WuQMessageBox::informationOk(this, msg); } /** * Export to VTK file. */ void BrainBrowserWindow::processDevelopExportVtkFile() { static QString previousVtkFileName = ""; BrowserTabContent* btc = getBrowserTabContent(); if (btc != NULL) { const int32_t tabIndex = btc->getTabNumber(); std::vector surfaceFiles; std::vector surfaceFilesColoring; ModelSurface* modelSurface = btc->getDisplayedSurfaceModel(); ModelWholeBrain* modelWholeBrain = btc->getDisplayedWholeBrainModel(); if (modelSurface != NULL) { SurfaceFile* sf = modelSurface->getSurface(); surfaceFiles.push_back(sf); surfaceFilesColoring.push_back(sf->getSurfaceNodeColoringRgbaForBrowserTab(tabIndex)); } else if (modelWholeBrain != NULL) { std::vector wholeBrainSurfaces = modelWholeBrain->getSelectedSurfaces(tabIndex); for (std::vector::iterator iter = wholeBrainSurfaces.begin(); iter != wholeBrainSurfaces.end(); iter++) { Surface* surface = *iter; surfaceFiles.push_back(surface); surfaceFilesColoring.push_back(surface->getWholeBrainNodeColoringRgbaForBrowserTab(tabIndex)); } } if (surfaceFiles.empty() == false) { QString vtkSurfaceFileFilter = "VTK Poly Data File (*.vtp)"; CaretFileDialog cfd(this, "Export to VTK File", GuiManager::get()->getBrain()->getCurrentDirectory(), vtkSurfaceFileFilter); cfd.selectFilter(vtkSurfaceFileFilter); cfd.setAcceptMode(QFileDialog::AcceptSave); cfd.setFileMode(CaretFileDialog::AnyFile); if (previousVtkFileName.isEmpty() == false) { cfd.selectFile(previousVtkFileName); } if (cfd.exec() == CaretFileDialog::Accepted) { QStringList selectedFiles = cfd.selectedFiles(); if (selectedFiles.size() > 0) { const QString vtkFileName = selectedFiles[0]; if (vtkFileName.isEmpty() == false) { try { previousVtkFileName = vtkFileName; VtkFileExporter::writeSurfaces(surfaceFiles, surfaceFilesColoring, vtkFileName); } catch (const DataFileException& dfe) { WuQMessageBox::errorOk(this, dfe.whatString()); } } } } } else { WuQMessageBox::errorOk(this, "Displayed model does not support exporting to VTK File at this time."); } } } /** * Project foci. */ void BrainBrowserWindow::processProjectFoci() { FociProjectionDialog fpd(this); fpd.exec(); } /** * Split multi-structure border files. */ void BrainBrowserWindow::processSplitBorderFiles() { BorderFileSplitDialog dialog(this); dialog.exec(); } /** * Called when capture image is selected. */ void BrainBrowserWindow::processCaptureImage() { //std::cout << "Toolbar height " << m_toolbar->height() << std::endl; GuiManager::get()->processShowImageCaptureDialog(this); } /** * Called when capture image is selected. */ void BrainBrowserWindow::processGapsAndMargins() { GuiManager::get()->processShowGapsAndMarginsDialog(this); } /** * Called when record movie is selected. */ void BrainBrowserWindow::processRecordMovie() { GuiManager::get()->processShowMovieDialog(this); } /** * Called when capture image is selected. */ void BrainBrowserWindow::processEditPreferences() { GuiManager::get()->processShowPreferencesDialog(this); } /** * Called when information dialog is selected. */ void BrainBrowserWindow::processInformationDialog() { GuiManager::get()->processShowInformationDisplayDialog(true); } /** * Called when close spec file is selected. */ void BrainBrowserWindow::processCloseAllFiles() { if(!WuQMessageBox::warningYesNo(this, "Are you sure you want to close all files?")) return; Brain* brain = GuiManager::get()->getBrain(); brain->resetBrain(); CaretPreferences* prefs = SessionManager::get()->getCaretPreferences(); prefs->setBackgroundAndForegroundColorsMode(BackgroundAndForegroundColorsModeEnum::USER_PREFERENCES); EventManager::get()->sendEvent(EventUserInterfaceUpdate().getPointer()); EventManager::get()->sendEvent(EventGraphicsUpdateAllWindows().getPointer()); GuiManager::get()->closeAllOtherWindows(this); EventManager::get()->sendEvent(EventUserInterfaceUpdate().getPointer()); } /** * Create a new window. */ void BrainBrowserWindow::processNewWindow() { CursorDisplayScoped cursor; cursor.showWaitCursor(); EventManager::get()->blockEvent(EventTypeEnum::EVENT_GRAPHICS_UPDATE_ONE_WINDOW, true); EventBrowserWindowNew eventNewBrowser(this, NULL); EventManager::get()->sendEvent(eventNewBrowser.getPointer()); if (eventNewBrowser.isError()) { cursor.restoreCursor(); QMessageBox::critical(this, "", eventNewBrowser.getErrorMessage()); return; } const int32_t newWindowIndex = eventNewBrowser.getBrowserWindowCreated()->getBrowserWindowIndex(); EventManager::get()->sendEvent(EventUserInterfaceUpdate().setWindowIndex(newWindowIndex).getPointer()); EventManager::get()->blockEvent(EventTypeEnum::EVENT_GRAPHICS_UPDATE_ONE_WINDOW, false); EventManager::get()->sendEvent(EventGraphicsUpdateOneWindow(newWindowIndex).getPointer()); } /** * Display about workbench dialog. */ void BrainBrowserWindow::processAboutWorkbench() { AboutWorkbenchDialog awd(this->m_openGLWidget); awd.exec(); } /** * Called when open location is selected. */ void BrainBrowserWindow::processDataFileLocationOpen() { CaretFileRemoteDialog fileRemoteDialog(this); fileRemoteDialog.exec(); } /** * Called when open data file is selected. */ void BrainBrowserWindow::processDataFileOpen() { if (s_previousOpenFileNameFilter.isEmpty()) { s_previousOpenFileNameFilter = DataFileTypeEnum::toQFileDialogFilter(DataFileTypeEnum::SPECIFICATION); } /* * Get all file filters. */ std::vector dataFileTypes; DataFileTypeEnum::getAllEnums(dataFileTypes, DataFileTypeEnum::OPTIONS_NONE); QStringList filenameFilterList; filenameFilterList.append("Any File (*)"); for (std::vector::const_iterator iter = dataFileTypes.begin(); iter != dataFileTypes.end(); iter++) { AString filterName = DataFileTypeEnum::toQFileDialogFilter(*iter); filenameFilterList.append(filterName); } /* * Setup file selection dialog. */ CaretFileDialog fd(this); fd.setAcceptMode(CaretFileDialog::AcceptOpen); fd.setNameFilters(filenameFilterList); fd.setFileMode(CaretFileDialog::ExistingFiles); fd.setViewMode(CaretFileDialog::List); fd.selectNameFilter(s_previousOpenFileNameFilter); if (s_previousOpenFileDirectory.isEmpty() == false) { FileInformation fileInfo(s_previousOpenFileDirectory); if (fileInfo.exists()) { fd.setDirectory(s_previousOpenFileDirectory); } } if ( ! s_previousOpenFileGeometry.isEmpty()) { fd.restoreGeometry(s_previousOpenFileGeometry); } AString errorMessages; if (fd.exec() == CaretFileDialog::Accepted) { QStringList selectedFiles = fd.selectedFiles(); if (selectedFiles.empty() == false) { /* * Load the files. */ std::vector filenamesVector; QStringListIterator nameIter(selectedFiles); while (nameIter.hasNext()) { const QString name = nameIter.next(); filenamesVector.push_back(name); } std::vector dataFileTypesDummyNotUsed; loadFiles(this, filenamesVector, dataFileTypesDummyNotUsed, LOAD_SPEC_FILE_WITH_DIALOG, "", ""); } s_previousOpenFileNameFilter = fd.selectedNameFilter(); s_previousOpenFileDirectory = fd.directory().absolutePath(); s_previousOpenFileGeometry = fd.saveGeometry(); } } /** * Load files that are on the network * * @param parentForDialogs, * Parent widget on which dialogs are displayed. * @param filenames * List of filenames to read. * @param dataFileTypes * Type for each data file (optional, if not available type matched from file's extension). * @param username * Username for network file reading * @param password * Password for network file reading */ bool BrainBrowserWindow::loadFilesFromNetwork(QWidget* parentForDialogs, const std::vector& filenames, const std::vector dataFileTypes, const AString& username, const AString& password) { const bool successFlag = loadFiles(parentForDialogs, filenames, dataFileTypes, BrainBrowserWindow::LOAD_SPEC_FILE_WITH_DIALOG_VIA_COMMAND_LINE, username, password); return successFlag; } /** * Load the files that were specified on the command line. * @param filenames * Names of files on the command line. * @param loadSpecFileMode * Specifies handling of SpecFiles */ void BrainBrowserWindow::loadFilesFromCommandLine(const std::vector& filenames, const LoadSpecFileMode loadSpecFileMode) { std::vector dataFileTypesDummyNotUsed; loadFiles(this, filenames, dataFileTypesDummyNotUsed, loadSpecFileMode, "", ""); } /** * Load the scene file and the scene with the given name or number * @param sceneFileName * Name of scene file. * @param sceneNameOrNumber * Name or number of scene. Name takes precedence over number. * Scene numbers start at one. */ void BrainBrowserWindow::loadSceneFromCommandLine(const AString& sceneFileName, const AString& sceneNameOrNumber) { std::vector filenames; filenames.push_back(sceneFileName); loadFilesFromCommandLine(filenames, LOAD_SPEC_FILE_CONTENTS_VIA_COMMAND_LINE); bool haveSceneFileError = true; bool haveSceneError = true; FileInformation fileInfo(sceneFileName); const AString nameNoExt = fileInfo.getFileName(); Brain* brain = GuiManager::get()->getBrain(); const int32_t numSceneFiles = brain->getNumberOfSceneFiles(); for (int32_t i = 0; i < numSceneFiles; i++) { SceneFile* sf = brain->getSceneFile(i); if (sf->getFileName().contains(sceneFileName)) { 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) if ((sceneNumber >= 0) && (sceneNumber < sf->getNumberOfScenes())) { scene = sf->getSceneAtIndex(sceneNumber); } } } if (scene != NULL) { GuiManager::get()->processShowSceneDialogAndScene(this, sf, scene); haveSceneError = false; break; } } } if (haveSceneFileError) { const AString msg = ("No scene file named \"" + sceneFileName + "\" was loaded."); WuQMessageBox::errorOk(this, msg); } else if (haveSceneError) { const AString msg = ("No scene with name/number \"" + sceneNameOrNumber + "\" found in scene file."); WuQMessageBox::errorOk(this, msg); } } /** * Load data files. If there are errors, an error message dialog * will be displayed. * * @param parentForDialogs * Parent widget for any dialogs (if NULL this window is used). * @param filenames * Names of files. * @param dataFileTypes * Type for each filename (optional). If the type is not present for a * filename, the type is inferred from the filename's extension. * @param loadSpecFileMode * Specifies handling of SpecFiles * @param username * Username for network file reading * @param password * Password for network file reading * @return true if there are no errors, else false. */ bool BrainBrowserWindow::loadFiles(QWidget* parentForDialogs, const std::vector& filenames, const std::vector dataFileTypes, const LoadSpecFileMode loadSpecFileMode, const AString& username, const AString& password) { QWidget* parentWidget = parentForDialogs; if (parentWidget == NULL) { parentWidget = this; } /* * Pick out specific file types. */ AString specFileName; std::vector volumeFileNames; std::vector surfaceFileNames; std::vector otherFileNames; std::vector otherDataFileTypes; AString typeErrorMessage = ""; const int32_t numFiles = static_cast(filenames.size()); const int32_t numDataTypes = static_cast(dataFileTypes.size()); for (int32_t i = 0; i < numFiles; i++) { const AString name = filenames[i]; bool isValidType = false; DataFileTypeEnum::Enum fileType = DataFileTypeEnum::UNKNOWN; if (i < numDataTypes) { fileType = dataFileTypes[i]; isValidType = true; } else { fileType = DataFileTypeEnum::fromFileExtension(name, &isValidType); if (isValidType == false) { typeErrorMessage.appendWithNewLine("Extension for " + name + " does not match a suppported file type"); } } switch (fileType) { case DataFileTypeEnum::SPECIFICATION: if (specFileName.isEmpty() == false) { QMessageBox::critical(parentWidget, "ERROR", "More than one spec file cannot be loaded"); return false; } specFileName = name; break; case DataFileTypeEnum::SURFACE: surfaceFileNames.push_back(name); break; case DataFileTypeEnum::VOLUME: volumeFileNames.push_back(name); break; default: otherFileNames.push_back(name); otherDataFileTypes.push_back(fileType); break; } } if (typeErrorMessage.isEmpty() == false) { QMessageBox::critical(parentWidget, "ERROR", typeErrorMessage); return false; } /* * Load files in this order: * (1) Spec File - Limit to one. * (2) Volume File * (3) Surface File * (4) All other files. */ std::vector > filesToLoad; const int32_t numVolumeFiles = static_cast(volumeFileNames.size()); for (int32_t i = 0; i < numVolumeFiles; i++) { filesToLoad.push_back(std::make_pair(volumeFileNames[i], DataFileTypeEnum::VOLUME)); } const int32_t numSurfaceFiles = static_cast(surfaceFileNames.size()); for (int32_t i = 0; i < numSurfaceFiles; i++) { filesToLoad.push_back(std::make_pair(surfaceFileNames[i], DataFileTypeEnum::SURFACE)); } const int32_t numOtherFiles = static_cast(otherFileNames.size()); for (int32_t i = 0; i < numOtherFiles; i++) { filesToLoad.push_back(std::make_pair(otherFileNames[i], otherDataFileTypes[i])); } bool createDefaultTabsFlag = false; /* * If there are no models loaded, will want to create default tabs. */ EventModelGetAll modelGetAllEvent; EventManager::get()->sendEvent(modelGetAllEvent.getPointer()); const int32_t numberOfModels = static_cast(modelGetAllEvent.getModels().size()); if (numberOfModels <= 0) { createDefaultTabsFlag = true; } AString errorMessages; ElapsedTimer timer; timer.start(); float specFileTimeStart = 0.0; float specFileTimeEnd = 0.0; bool sceneFileWasLoaded = false; /* * Load spec file (before data files) */ if (specFileName.isEmpty() == false) { SpecFile specFile; try { specFile.readFile(specFileName); } catch (const DataFileException& e) { errorMessages += e.whatString(); QMessageBox::critical(parentWidget, "ERROR", errorMessages); return false; } switch (loadSpecFileMode) { case LOAD_SPEC_FILE_CONTENTS_VIA_COMMAND_LINE: { timer.reset(); // resets timer specFileTimeStart = timer.getElapsedTimeSeconds(); /* * Load all files listed in spec file */ specFile.setAllFilesSelectedForLoading(true); EventSpecFileReadDataFiles readSpecFileEvent(GuiManager::get()->getBrain(), &specFile); if (username.isEmpty() == false) { readSpecFileEvent.setUsernameAndPassword(username, password); } ProgressReportingDialog::runEvent(&readSpecFileEvent, parentWidget, specFile.getFileNameNoPath()); if (readSpecFileEvent.isError()) { if (errorMessages.isEmpty() == false) { errorMessages += "\n"; } errorMessages += readSpecFileEvent.getErrorMessage(); } specFileTimeEnd = timer.getElapsedTimeSeconds(); createDefaultTabsFlag = true; } break; case LOAD_SPEC_FILE_WITH_DIALOG: case LOAD_SPEC_FILE_WITH_DIALOG_VIA_COMMAND_LINE: { if (GuiManager::get()->processShowOpenSpecFileDialog(&specFile, this)) { m_toolbar->addDefaultTabsAfterLoadingSpecFile(); createDefaultTabsFlag = true; } } break; } sceneFileWasLoaded = specFile.areAllFilesSelectedForLoadingSceneFiles(); } /* * Prepare to load any data files */ EventDataFileRead loadFilesEvent(GuiManager::get()->getBrain()); if (username.isEmpty() == false) { loadFilesEvent.setUsernameAndPassword(username, password); } /* * Add data files to data file loading event (after loading spec file) */ const int32_t numFilesToLoad = static_cast(filesToLoad.size()); for (int32_t i = 0; i < numFilesToLoad; i++) { AString name = filesToLoad[i].first; const DataFileTypeEnum::Enum fileType = filesToLoad[i].second; loadFilesEvent.addDataFile(fileType, name); } /* * Now, load the data files */ const int32_t numberOfValidFiles = loadFilesEvent.getNumberOfDataFilesToRead(); if (numberOfValidFiles > 0) { ProgressReportingDialog::runEvent(&loadFilesEvent, parentWidget, "Loading Data Files"); errorMessages.appendWithNewLine(loadFilesEvent.getErrorMessage()); /* * Check for errors */ for (int32_t i = 0; i < numberOfValidFiles; i++) { const AString& dataFileName = loadFilesEvent.getDataFileName(i); const DataFileTypeEnum::Enum dataFileType = loadFilesEvent.getDataFileType(i); const AString shortName = FileInformation(dataFileName).getFileName(); if (loadFilesEvent.isFileErrorInvalidStructure(i)) { /* * Allow user to specify the structure */ WuQDataEntryDialog ded("Structure", parentWidget); StructureEnumComboBox* ssc = ded.addStructureEnumComboBox(""); ded.setTextAtTop(("File \"" + shortName + "\"\nhas missing or invalid structure, select it's structure." "\nAfter loading, save file with File Menu->Save Manage Files" "\nto prevent this error."), false); if (ded.exec() == WuQDataEntryDialog::Accepted) { EventDataFileRead loadFileEventStructure(GuiManager::get()->getBrain()); loadFileEventStructure.addDataFile(ssc->getSelectedStructure(), dataFileType, dataFileName); if (username.isEmpty() == false) { loadFileEventStructure.setUsernameAndPassword(username, password); } ProgressReportingDialog::runEvent(&loadFileEventStructure, parentWidget, ("Loading " + shortName)); if (loadFileEventStructure.isError()) { errorMessages.appendWithNewLine(loadFileEventStructure.getErrorMessage()); } else { if (loadFileEventStructure.getNumberOfDataFilesToRead() == 1) { CaretDataFile* cdf = loadFileEventStructure.getDataFileRead(0); if (cdf != NULL) { cdf->setModified(); } } } } else { errorMessages.appendWithNewLine("File \"" + shortName + "\" not loaded due to invalid structure."); } } else if (loadFilesEvent.isFileError(i) == false) { if (dataFileType == DataFileTypeEnum::SCENE) { sceneFileWasLoaded = true; } } } } const float specFileTime = specFileTimeEnd - specFileTimeStart; const float createTabsStartTime = timer.getElapsedTimeSeconds(); const EventBrowserWindowCreateTabs::Mode tabMode = (createDefaultTabsFlag ? EventBrowserWindowCreateTabs::MODE_LOADED_SPEC_FILE : EventBrowserWindowCreateTabs::MODE_LOADED_DATA_FILE); EventBrowserWindowCreateTabs createTabsEvent(tabMode); EventManager::get()->sendEvent(createTabsEvent.getPointer()); const float createTabsTime = timer.getElapsedTimeSeconds() - createTabsStartTime; const float guiStartTime = timer.getElapsedTimeSeconds(); EventManager::get()->sendEvent(EventSurfaceColoringInvalidate().getPointer()); EventManager::get()->sendEvent(EventUserInterfaceUpdate().getPointer()); EventManager::get()->sendEvent(EventGraphicsUpdateAllWindows().getPointer()); const float guiTime = timer.getElapsedTimeSeconds() - guiStartTime; if (specFileName.isEmpty() == false) { CaretLogInfo("Time to read files from spec file (in GUI) \"" + FileInformation(specFileName).getFileName() + "\" was " + AString::number(timer.getElapsedTimeSeconds()) + " seconds.\n Time to read spec data files was " + AString::number(specFileTime) + " seconds.\n Time to update GUI was " + AString::number(guiTime) + " seconds.\n Time to create tabs was " + AString::number(createTabsTime)); } bool successFlag = true; if (errorMessages.isEmpty() == false) { successFlag = false; QMessageBox::critical(parentWidget, "ERROR", errorMessages); } if (sceneFileWasLoaded) { GuiManager::get()->processShowSceneDialog(this); } EventManager::get()->sendEvent(EventMacDockMenuUpdate().getPointer()); return successFlag; } /** * Called when manage/save loaded files is selected. */ void BrainBrowserWindow::processManageSaveLoadedFiles() { GuiManager::get()->processShowSaveManageFilesDialog(this); } /** * Exit the program. */ void BrainBrowserWindow::processExitProgram() { GuiManager::get()->exitProgram(this); } /** * Update full screen status. * * @param showFullScreenDisplay * If true, show as full screen, else show as normal screen * @param saveRestoreWindowStatus * If true, save/restore the window status */ void BrainBrowserWindow::processViewFullScreen(bool showFullScreenDisplay, const bool saveRestoreWindowStatus) { if (showFullScreenDisplay == false) { EventManager::get()->blockEvent(EventTypeEnum::EVENT_USER_INTERFACE_UPDATE, true); showNormal(); if (saveRestoreWindowStatus) { restoreWindowComponentStatus(m_normalWindowComponentStatus); } EventManager::get()->blockEvent(EventTypeEnum::EVENT_USER_INTERFACE_UPDATE, false); } else { if (saveRestoreWindowStatus) { saveWindowComponentStatus(m_normalWindowComponentStatus); } /* * Hide and disable Toolbar, Overlay, and Features ToolBox */ m_showToolBarAction->setChecked(true); m_showToolBarAction->trigger(); m_showToolBarAction->setEnabled(false); m_overlayToolBoxAction->setChecked(true); m_overlayToolBoxAction->trigger(); m_overlayToolBoxAction->setEnabled(false); m_featuresToolBoxAction->setChecked(true); m_featuresToolBoxAction->trigger(); m_featuresToolBoxAction->setEnabled(false); showFullScreen(); } EventManager::get()->sendEvent(EventUserInterfaceUpdate().setWindowIndex(m_browserWindowIndex).addToolBar().getPointer()); EventManager::get()->sendEvent(EventGraphicsUpdateOneWindow(m_browserWindowIndex).getPointer()); } /** * Called when view full screen is selected and toggles the status of full screen. */ void BrainBrowserWindow::processViewFullScreenSelected() { const bool toggledStatus = (! isFullScreen()); processViewFullScreen(toggledStatus, true); } /** * Called when tile tabs is selected and toggles the state of tile tabs. */ void BrainBrowserWindow::processViewTileTabs() { const bool toggledStatus = (! m_viewTileTabsSelected); setViewTileTabs(toggledStatus); } /** * Set the status of tile tabs. */ void BrainBrowserWindow::setViewTileTabs(const bool newStatus) { m_viewTileTabsSelected = newStatus; EventManager::get()->sendEvent(EventUserInterfaceUpdate().setWindowIndex(m_browserWindowIndex).addToolBar().getPointer()); EventManager::get()->sendEvent(EventGraphicsUpdateOneWindow(m_browserWindowIndex).getPointer()); } /** * View the Tile Tabs Configuration Dialog. */ void BrainBrowserWindow::processViewTileTabsConfigurationDialog() { GuiManager::get()->processShowTileTabsConfigurationDialog(this); } /** * Restore the status of window components. * @param wcs * Window component status that is restored. */ void BrainBrowserWindow::restoreWindowComponentStatus(const WindowComponentStatus& wcs) { if (wcs.windowGeometry.isEmpty() == false) { restoreGeometry(wcs.windowGeometry); } if (wcs.windowState.isEmpty() == false) { restoreState(wcs.windowState); } m_showToolBarAction->setEnabled(true); if (wcs.isToolBarDisplayed) { m_showToolBarAction->setChecked(false); m_showToolBarAction->trigger(); } else { m_showToolBarAction->setChecked(true); m_showToolBarAction->trigger(); } m_overlayToolBoxAction->setEnabled(true); if (wcs.isOverlayToolBoxDisplayed) { m_overlayToolBoxAction->blockSignals(true); m_overlayToolBoxAction->setChecked(false); m_overlayToolBoxAction->blockSignals(false); m_overlayToolBoxAction->trigger(); } else { m_overlayToolBoxAction->blockSignals(true); m_overlayToolBoxAction->setChecked(true); m_overlayToolBoxAction->blockSignals(false); m_overlayToolBoxAction->trigger(); } m_featuresToolBoxAction->setEnabled(true); if (wcs.isFeaturesToolBoxDisplayed) { m_featuresToolBoxAction->blockSignals(true); m_featuresToolBoxAction->setChecked(false); m_featuresToolBoxAction->blockSignals(false); m_featuresToolBoxAction->trigger(); } else { m_featuresToolBoxAction->blockSignals(true); m_featuresToolBoxAction->setChecked(true); m_featuresToolBoxAction->blockSignals(false); m_featuresToolBoxAction->trigger(); } if (m_featuresToolBox != NULL) { if (wcs.featuresGeometry.isEmpty() == false) { m_featuresToolBox->restoreGeometry(wcs.featuresGeometry); } } } /** * Save the status of window components. * @param wcs * Will contains status after exit. * @param hideComponents * If true, any components (toolbar/toolbox) will be hidden. */ void BrainBrowserWindow::saveWindowComponentStatus(WindowComponentStatus& wcs) { wcs.windowState = saveState(); wcs.windowGeometry = saveGeometry(); if (m_featuresToolBoxAction->isChecked()) { if (m_featuresToolBox != NULL) { wcs.featuresGeometry = m_featuresToolBox->saveGeometry(); } } wcs.isToolBarDisplayed = m_showToolBarAction->isChecked(); wcs.isOverlayToolBoxDisplayed = m_overlayToolBoxAction->isChecked(); wcs.isFeaturesToolBoxDisplayed = m_featuresToolBoxAction->isChecked(); m_showToolBarAction->setEnabled(false); m_overlayToolBoxAction->setEnabled(false); m_featuresToolBoxAction->setEnabled(false); } /** * Adds a new tab to the window. */ void BrainBrowserWindow::processNewTab() { m_toolbar->addNewTab(); } /** * Adds a new tab to the window. */ void BrainBrowserWindow::processDuplicateTab() { BrowserTabContent* previousTabContent = getBrowserTabContent(); m_toolbar->addNewTabCloneContent(previousTabContent); } /** * Called when move all tabs to one window is selected. */ void BrainBrowserWindow::processMoveAllTabsToOneWindow() { /* * Wait cursor */ CursorDisplayScoped cursor; cursor.showWaitCursor(); std::vector otherTabContent; GuiManager::get()->closeOtherWindowsAndReturnTheirTabContent(this, otherTabContent); const int32_t numOtherTabs = static_cast(otherTabContent.size()); for (int32_t i = 0; i < numOtherTabs; i++) { m_toolbar->addNewTabWithContent(otherTabContent[i]); m_toolbar->updateToolBar(); } EventManager::get()->sendEvent(EventSurfaceColoringInvalidate().getPointer()); EventManager::get()->sendEvent(EventUserInterfaceUpdate().getPointer()); EventManager::get()->sendEvent(EventGraphicsUpdateOneWindow(m_browserWindowIndex).getPointer()); } /** * Called when the move tab to window is about to be displayed. */ void BrainBrowserWindow::processMoveSelectedTabToWindowMenuAboutToBeDisplayed() { m_moveSelectedTabToWindowMenu->clear(); if (getBrowserTabContent() == NULL) { return; } /* * Allow movement of tab to new window ONLY if this window * contains more than one tab. */ if (m_toolbar->tabBar->count() > 1) { QAction* toNewWindowAction = new QAction("New Window", m_moveSelectedTabToWindowMenu); toNewWindowAction->setData(qVariantFromValue((void*)NULL)); m_moveSelectedTabToWindowMenu->addAction(toNewWindowAction); } std::vector browserWindows = GuiManager::get()->getAllOpenBrainBrowserWindows(); for (int32_t i = 0; i < static_cast(browserWindows.size()); i++) { if (browserWindows[i] != this) { QAction* action = new QAction(browserWindows[i]->windowTitle(), m_moveSelectedTabToWindowMenu); action->setData(qVariantFromValue((void*)browserWindows[i])); m_moveSelectedTabToWindowMenu->addAction(action); } } } /** * Called when move tab to window menu item is selected. * This window may close if there are no more tabs after * the tab is removed. * @param action * Action from menu item that was selected. */ void BrainBrowserWindow::processMoveSelectedTabToWindowMenuSelection(QAction* action) { if (action != NULL) { /* * Wait cursor */ CursorDisplayScoped cursor; cursor.showWaitCursor(); void* p = action->data().value(); BrainBrowserWindow* moveToBrowserWindow = (BrainBrowserWindow*)p; BrowserTabContent* btc = getBrowserTabContent(); if (moveToBrowserWindow != NULL) { m_toolbar->removeTabWithContent(btc); moveToBrowserWindow->m_toolbar->addNewTabWithContent(btc); } else { EventBrowserWindowNew newWindow(this, btc); EventManager::get()->sendEvent(newWindow.getPointer()); if (newWindow.isError()) { cursor.restoreCursor(); QMessageBox::critical(this, "", newWindow.getErrorMessage()); return; } m_toolbar->removeTabWithContent(btc); } if (m_toolbar->tabBar->count() <= 0) { close(); } EventManager::get()->sendEvent(EventUserInterfaceUpdate().getPointer()); } } /** * Called to move the overlay toolbox to the left side of the window. */ void BrainBrowserWindow::processMoveOverlayToolBoxToLeft() { moveOverlayToolBox(Qt::LeftDockWidgetArea); } /** * Called to move the overlay toolbox to the bottom side of the window. */ void BrainBrowserWindow::processMoveOverlayToolBoxToBottom() { moveOverlayToolBox(Qt::BottomDockWidgetArea); } /** * Called to move the overlay toolbox to float outside of the window. */ void BrainBrowserWindow::processMoveOverlayToolBoxToFloat() { moveOverlayToolBox(Qt::NoDockWidgetArea); } /** * Called to hide the overlay toolbox. */ void BrainBrowserWindow::processHideOverlayToolBox() { processShowOverlayToolBox(false); } /** * Called to move the layers toolbox to the right side of the window. */ void BrainBrowserWindow::processMoveFeaturesToolBoxToRight() { moveFeaturesToolBox(Qt::RightDockWidgetArea); } /** * Called to move the layers toolbox to float outside of the window. */ void BrainBrowserWindow::processMoveFeaturesToolBoxToFloat() { moveFeaturesToolBox(Qt::NoDockWidgetArea); } /** * Called to hide the layers tool box. */ void BrainBrowserWindow::processHideFeaturesToolBox() { if (m_featuresToolBoxAction->isChecked()) { m_featuresToolBoxAction->trigger(); } } /** * Called to display the layers toolbox. */ void BrainBrowserWindow::processShowFeaturesToolBox(bool status) { if (status) { m_featuresToolBoxAction->setToolTip("Hide Features Toolbox"); EventManager::get()->sendEvent(EventUserInterfaceUpdate().setWindowIndex(m_browserWindowIndex).addToolBox().getPointer()); } else { m_featuresToolBoxAction->setToolTip("Show Features Toolbox"); } } /** * Move the overlay toolbox to the desired location. * @param area * DockWidget location. */ void BrainBrowserWindow::moveFeaturesToolBox(Qt::DockWidgetArea area) { switch (area) { case Qt::LeftDockWidgetArea: CaretAssertMessage(0, "Layers toolbox not allowed on left"); break; case Qt::RightDockWidgetArea: m_featuresToolBox->setFloating(false); addDockWidget(Qt::RightDockWidgetArea, m_featuresToolBox); if (m_featuresToolBoxAction->isChecked() == false) { m_featuresToolBoxAction->trigger(); } break; case Qt::TopDockWidgetArea: CaretAssertMessage(0, "Layers toolbox not allowed on top"); break; case Qt::BottomDockWidgetArea: CaretAssertMessage(0, "Layers toolbox not allowed on bottom"); break; default: m_featuresToolBox->setFloating(true); if (m_featuresToolBoxAction->isChecked() == false) { m_featuresToolBoxAction->trigger(); } break; } } /** * Move the overlay toolbox to the desired location. * @param area * DockWidget location. */ void BrainBrowserWindow::moveOverlayToolBox(Qt::DockWidgetArea area) { bool isVisible = false; switch (area) { case Qt::LeftDockWidgetArea: m_overlayHorizontalToolBox->setVisible(false); m_overlayVerticalToolBox->setFloating(false); addDockWidget(Qt::LeftDockWidgetArea, m_overlayVerticalToolBox); m_overlayVerticalToolBox->setVisible(true); m_overlayActiveToolBox = m_overlayVerticalToolBox; isVisible = true; break; case Qt::RightDockWidgetArea: CaretAssertMessage(0, "Overlay toolbox not allowed on right"); break; case Qt::TopDockWidgetArea: CaretAssertMessage(0, "Overlay toolbox not allowed on top"); break; case Qt::BottomDockWidgetArea: m_overlayVerticalToolBox->setVisible(false); m_overlayHorizontalToolBox->setFloating(false); addDockWidget(Qt::BottomDockWidgetArea, m_overlayHorizontalToolBox); m_overlayHorizontalToolBox->setVisible(true); m_overlayActiveToolBox = m_overlayHorizontalToolBox; isVisible = true; break; default: m_overlayActiveToolBox->setVisible(true); m_overlayActiveToolBox->setFloating(true); isVisible = true; break; } processShowOverlayToolBox(isVisible); } /** * Remove and return all tabs from this toolbar. * After this the window containing this toolbar * will contain no tabs! * * @param allTabContent * Will contain the content from the tabs upon return. */ void BrainBrowserWindow::removeAndReturnAllTabs(std::vector& allTabContent) { m_toolbar->removeAndReturnAllTabs(allTabContent); } /** * @return Return the active browser tab content in * this browser window. */ BrowserTabContent* BrainBrowserWindow::getBrowserTabContent() { return m_toolbar->getTabContentFromSelectedTab(); } /** * @return Return the active browser tab content in * this browser window. */ const BrowserTabContent* BrainBrowserWindow::getBrowserTabContent() const { return m_toolbar->getTabContentFromSelectedTab(); } /** * get browser tab content for tab with specified tab Index * @param tabIndex * Desired tabIndex * @return Return the active browser tab content in * this browser window. */ BrowserTabContent* BrainBrowserWindow::getBrowserTabContent(int tabIndex) { return m_toolbar->getTabContentFromTab(tabIndex); } /** * Get content of all tabs * * @param allTabContent * Will contain the tabs in window upon return */ void BrainBrowserWindow::getAllTabContent(std::vector& allTabContent) const { m_toolbar->getAllTabContent(allTabContent); } /** * Get indices of all tabs in window * * @param allTabContentIndices * Will contain the indices from all tabs in window upon return. */ void BrainBrowserWindow::getAllTabContentIndices(std::vector& allTabContentIndices) const { allTabContentIndices.clear(); std::vector allTabContent; getAllTabContent(allTabContent); for (std::vector::iterator iter = allTabContent.begin(); iter != allTabContent.end(); iter++) { const BrowserTabContent* btc = *iter; CaretAssert(btc); allTabContentIndices.push_back(btc->getTabNumber()); } } /** * Returns a popup menu for the main window. * Overrides that in QMainWindow and prevents the * default context menu from appearing. * "O * nothing available. */ QMenu* BrainBrowserWindow::createPopupMenu() { return NULL; } /** * Open a connection to the allen brain institute database. */ void BrainBrowserWindow::processConnectToAllenDataBase() { GuiManager::get()->processShowAllenDataBaseWebView(this); } /** * Open a connection to the human connectome project database. */ void BrainBrowserWindow::processConnectToConnectomeDataBase() { GuiManager::get()->processShowConnectomeDataBaseWebView(this); } /** * Load the HCP Website into the user's web browser. */ void BrainBrowserWindow::processHcpWebsiteInBrowser() { QUrl url("https://humanconnectome.org"); QDesktopServices::openUrl(url); } /** * Report a Workbench bug. */ void BrainBrowserWindow::processReportWorkbenchBug() { GuiManager::get()->processShowBugReportDialog(this, m_openGLWidget->getOpenGLInformation()); } /** * Load the HCP Feature Request Website into the user's web browser. */ void BrainBrowserWindow::processHcpFeatureRequestWebsiteInBrowser() { QUrl url("http://humanconnectome.org/contact/feature-request.php"); QDesktopServices::openUrl(url); } /** * @rerturn Aspect ratio of the OpenGL widget. */ float BrainBrowserWindow::getOpenGLWidgetAspectRatio() const { const float w = this->m_openGLWidget->width(); const float h = this->m_openGLWidget->height(); const float aspectRatio = ((w != 0.0) ? (h / w) : 1.0); return aspectRatio; } /** * Create a scene for an instance of a class. * * @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. * * @return Pointer to SceneClass object representing the state of * this object. Under some circumstances a NULL pointer may be * returned. Caller will take ownership of returned object. */ SceneClass* BrainBrowserWindow::saveToScene(const SceneAttributes* sceneAttributes, const AString& instanceName) { SceneClass* sceneClass = new SceneClass(instanceName, "BrainBrowserWindow", 1); m_sceneAssistant->saveMembers(sceneAttributes, sceneClass); 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 */ sceneClass->addClass(m_toolbar->saveToScene(sceneAttributes, "m_toolbar")); /* * Save overlay toolbox */ { AString orientationName = ""; if (m_overlayActiveToolBox == m_overlayHorizontalToolBox) { orientationName = "horizontal"; } else if (m_overlayActiveToolBox == m_overlayVerticalToolBox) { orientationName = "vertical"; } SceneClass* overlayToolBoxClass = new SceneClass("overlayToolBox", "OverlayToolBox", 1); overlayToolBoxClass->addString("orientation", orientationName); overlayToolBoxClass->addBoolean("floating", m_overlayActiveToolBox->isFloating()); overlayToolBoxClass->addBoolean("visible", m_overlayActiveToolBox->isVisible()); sceneClass->addClass(overlayToolBoxClass); sceneClass->addClass(m_overlayActiveToolBox->saveToScene(sceneAttributes, "m_overlayActiveToolBox")); } switch (sceneAttributes->getSceneType()) { case SceneTypeEnum::SCENE_TYPE_FULL: break; case SceneTypeEnum::SCENE_TYPE_GENERIC: break; } /* * Save features toolbox */ { SceneClass* featureToolBoxClass = new SceneClass("featureToolBox", "FeatureToolBox", 1); featureToolBoxClass->addBoolean("floating", m_featuresToolBox->isFloating()); featureToolBoxClass->addBoolean("visible", m_featuresToolBox->isVisible()); sceneClass->addClass(featureToolBoxClass); sceneClass->addClass(m_featuresToolBox->saveToScene(sceneAttributes, "m_featuresToolBox")); } /* * Position and size */ SceneWindowGeometry swg(this); sceneClass->addClass(swg.saveToScene(sceneAttributes, "geometry")); sceneClass->addBoolean("isFullScreen", isFullScreen()); sceneClass->addBoolean("isMaximized", isMaximized()); sceneClass->addBoolean("m_viewTileTabsAction", m_viewTileTabsSelected); /* * Graphics position and size */ SceneWindowGeometry openGLGeometry(m_openGLWidget); sceneClass->addClass(openGLGeometry.saveToScene(sceneAttributes, "openGLWidgetGeometry")); return sceneClass; } /** * Restore the state of an instance of a class. * * @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 containing the state that was previously * saved and should be restored. */ void BrainBrowserWindow::restoreFromScene(const SceneAttributes* sceneAttributes, const SceneClass* sceneClass) { if (sceneClass == NULL) { return; } /* * Restore toolbar */ const SceneClass* toolbarClass = sceneClass->getClass("m_toolbar"); if (toolbarClass != NULL) { m_toolbar->restoreFromScene(sceneAttributes, toolbarClass); } 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); /* * Restore Unique ID of selected tile tabs configuration. * If not valid, use default configuration */ m_selectedTileTabsConfigurationUniqueIdentifier = sceneClass->getStringValue("m_selectedTileTabsConfigurationUniqueIdentifier", ""); CaretPreferences* caretPreferences = SessionManager::get()->getCaretPreferences(); TileTabsConfiguration* selectedConfiguration = caretPreferences->getTileTabsConfigurationByUniqueIdentifier(m_selectedTileTabsConfigurationUniqueIdentifier); if (selectedConfiguration == NULL) { m_selectedTileTabsConfigurationUniqueIdentifier = m_defaultTileTabsConfiguration->getUniqueIdentifier(); } /* * Restoration status for full screen and tab tiles * * If "m_screenMode" is found, the scene is an older scene that was * created prior to splitting Full Screen and Tile Tabs into * separate functionality. */ bool restoreToFullScreen = false; bool restoreToTabTiles = false; const SceneObject* screenModeObject = sceneClass->getObjectWithName("m_screenMode"); if (screenModeObject != NULL) { const SceneEnumeratedType* screenEnum = dynamic_cast(screenModeObject); if (screenEnum != NULL) { const AString screenModeName = screenEnum->stringValue(); if (screenModeName == "NORMAL") { } else if (screenModeName == "FULL_SCREEN") { restoreToFullScreen = true; } else if (screenModeName == "TAB_MONTAGE") { restoreToTabTiles = true; } else if (screenModeName == "TAB_MONTAGE_FULL_SCREEN") { restoreToTabTiles = true; restoreToFullScreen = true; } else { CaretLogWarning("Unrecognized obsolete screen mode: " + screenModeName); } } } else { restoreToFullScreen = sceneClass->getBooleanValue("isFullScreen", false); restoreToTabTiles = sceneClass->getBooleanValue("m_viewTileTabsAction", false); /* * 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_normalWindowComponentStatus = m_defaultWindowComponentStatus; processViewFullScreen(restoreToFullScreen, false); setViewTileTabs(restoreToTabTiles); /* * Position and size */ SceneWindowGeometry swg(this); swg.restoreFromScene(sceneAttributes, sceneClass->getClass("geometry")); EventManager::get()->sendEvent(EventUserInterfaceUpdate().getPointer()); QApplication::processEvents(); if (restoreToFullScreen == false) { /* * Restore feature toolbox */ const SceneClass* featureToolBoxClass = sceneClass->getClass("featureToolBox"); if (featureToolBoxClass != NULL) { const bool toolBoxVisible = featureToolBoxClass->getBooleanValue("visible", true); const bool toolBoxFloating = featureToolBoxClass->getBooleanValue("floating", false); if (toolBoxVisible) { if (toolBoxFloating) { processMoveFeaturesToolBoxToFloat(); } else { processMoveFeaturesToolBoxToRight(); } } m_featuresToolBoxAction->blockSignals(true); m_featuresToolBoxAction->setChecked(! toolBoxVisible); m_featuresToolBoxAction->blockSignals(false); m_featuresToolBoxAction->trigger(); m_featuresToolBox->restoreFromScene(sceneAttributes, sceneClass->getClass("m_featuresToolBox")); } /* * Restore overlay toolbox */ const SceneClass* overlayToolBoxClass = sceneClass->getClass("overlayToolBox"); if (overlayToolBoxClass != NULL) { const AString orientationName = overlayToolBoxClass->getStringValue("orientation", "horizontal"); const bool toolBoxVisible = overlayToolBoxClass->getBooleanValue("visible", true); const bool toolBoxFloating = overlayToolBoxClass->getBooleanValue("floating", false); if (orientationName == "horizontal") { processMoveOverlayToolBoxToBottom(); } else { processMoveOverlayToolBoxToLeft(); } if (toolBoxFloating) { processMoveOverlayToolBoxToFloat(); } processShowOverlayToolBox(toolBoxVisible); m_overlayActiveToolBox->restoreFromScene(sceneAttributes, sceneClass->getClass("m_overlayActiveToolBox")); } } switch (sceneAttributes->getSceneType()) { case SceneTypeEnum::SCENE_TYPE_FULL: break; case SceneTypeEnum::SCENE_TYPE_GENERIC: break; } const bool maximizedWindow = sceneClass->getBooleanValue("isMaximized", false); if (maximizedWindow) { showMaximized(); } } ///** // * 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); //} /** * @return A string describing the object's content. */ AString BrainBrowserWindow::toString() const { AString msg; msg.appendWithNewLine("Window " + AString::number(getBrowserWindowIndex() + 1) + ":"); const BrowserTabContent* btc = getBrowserTabContent(); if (btc != NULL) { msg.appendWithNewLine(btc->toString()); } return msg; } /** * Get a text description of the window's content. * * @param descriptionOut * Description of the window's content. */ void BrainBrowserWindow::getDescriptionOfContent(PlainTextStringBuilder& descriptionOut) const { descriptionOut.addLine("Window " + AString::number(getBrowserWindowIndex() + 1) + ":"); std::vector tabContent; if (isTileTabsSelected()) { m_toolbar->getAllTabContent(tabContent); } else { BrowserTabContent* btc = m_toolbar->getTabContentFromSelectedTab(); if (btc != NULL) { tabContent.push_back(btc); } } descriptionOut.pushIndentation(); for (std::vector::iterator iter = tabContent.begin(); iter != tabContent.end(); iter++) { const BrowserTabContent* btc = *iter; btc->getDescriptionOfContent(descriptionOut); } descriptionOut.popIndentation(); } connectome-workbench-1.2.3+git41-gc4c6c90/src/GuiQt/BrainBrowserWindow.h000066400000000000000000000373171300200146000255500ustar00rootroot00000000000000 #ifndef __BRAIN_BROWSER_WINDOW_H__ #define __BRAIN_BROWSER_WINDOW_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 "AString.h" #include "DataFileTypeEnum.h" #include "EventListenerInterface.h" #include "SceneableInterface.h" class QAction; class QActionGroup; class QDockWidget; class QMenu; namespace caret { class BrainBrowserWindowToolBar; class BrainBrowserWindowOrientedToolBox; class BrainOpenGLWidget; class BrowserTabContent; class PlainTextStringBuilder; class SceneClassAssistant; class TileTabsConfiguration; /** * The brain browser window is the viewer for * brain models. It may contain multiple tabs * with each tab displaying brain models. */ class BrainBrowserWindow : public QMainWindow, public EventListenerInterface, public SceneableInterface { Q_OBJECT public: virtual ~BrainBrowserWindow(); virtual void receiveEvent(Event* event); BrowserTabContent* getBrowserTabContent(); const BrowserTabContent* getBrowserTabContent() const; BrowserTabContent* getBrowserTabContent(int tabIndex); QMenu* createPopupMenu(); void getAllTabContent(std::vector& allTabContent) const; void getAllTabContentIndices(std::vector& allTabContentIndices) const; void removeAndReturnAllTabs(std::vector& allTabContent); int32_t getBrowserWindowIndex() const; bool isTileTabsSelected() const; /** * Mode for loading spec files */ enum LoadSpecFileMode { /** Do not show spec file dialog, just load all files listed in spec file listed on command line at program startup */ LOAD_SPEC_FILE_CONTENTS_VIA_COMMAND_LINE, /** Show spec file in spec file dialog for user selections */ LOAD_SPEC_FILE_WITH_DIALOG, /** Show spec file in spec file dialog for user selections from spec file listed on command line at program startup */ LOAD_SPEC_FILE_WITH_DIALOG_VIA_COMMAND_LINE }; void loadFilesFromCommandLine(const std::vector& filenames, const LoadSpecFileMode loadSpecFileMode); void loadSceneFromCommandLine(const AString& sceneFileName, const AString& sceneNameOrNumber); bool loadFilesFromNetwork(QWidget* parentForDialogs, const std::vector& filenames, const std::vector dataFileTypes, const AString& username, const AString& password); virtual SceneClass* saveToScene(const SceneAttributes* sceneAttributes, const AString& instanceName); virtual void restoreFromScene(const SceneAttributes* sceneAttributes, const SceneClass* sceneClass); // void getViewportSize(int &w, int &h); TileTabsConfiguration* getSelectedTileTabsConfiguration(); void setSelectedTileTabsConfiguration(TileTabsConfiguration* configuration); void resetGraphicsWidgetMinimumSize(); void setGraphicsWidgetFixedSize(const int32_t width, const int32_t height); void getGraphicsWidgetSize(int32_t& xOut, int32_t& yOut, int32_t& widthOut, int32_t& heightOut, int32_t& graphicsWidthOut, int32_t& graphicsHeightOut, const bool applyLockedAspectRatiosFlag) const; AString toString() const; virtual void getDescriptionOfContent(PlainTextStringBuilder& descriptionOut) const; static int32_t loadRecentSpecFileMenu(QMenu* recentSpecFileMenu); static int32_t loadRecentSceneFileMenu(QMenu* recentSpecFileMenu); float getOpenGLWidgetAspectRatio() const; bool isAspectRatioLocked() const; void setAspectRatioLocked(const bool locked); float getAspectRatio() const; void setAspectRatio(const float aspectRatio); protected: void closeEvent(QCloseEvent* event); void keyPressEvent(QKeyEvent* event); private slots: void processAboutWorkbench(); void processInformationDialog(); void processNewWindow(); void processNewTab(); void processDuplicateTab(); void processDataFileLocationOpen(); void processDataFileOpen(); void processManageSaveLoadedFiles(); void processCaptureImage(); void processRecordMovie(); void processEditPreferences(); void processCloseAllFiles(); void processCloseWindow(); void processExitProgram(); void processMoveAllTabsToOneWindow(); void processViewFullScreenSelected(); void processViewTileTabs(); void processViewTileTabsConfigurationDialog(); void processShowHelpInformation(); void processShowIdentifyBrainordinateDialog(); void processGapsAndMargins(); void processMoveOverlayToolBoxToLeft(); void processMoveOverlayToolBoxToBottom(); void processMoveOverlayToolBoxToFloat(); void processHideOverlayToolBox(); void processMoveFeaturesToolBoxToRight(); void processMoveFeaturesToolBoxToFloat(); void processHideFeaturesToolBox(); void processMoveSelectedTabToWindowMenuAboutToBeDisplayed(); void processMoveSelectedTabToWindowMenuSelection(QAction*); void processRecentSceneFileMenuAboutToBeDisplayed(); void processRecentSceneFileMenuSelection(QAction*); void processRecentSpecFileMenuAboutToBeDisplayed(); void processRecentSpecFileMenuSelection(QAction*); void processShowOverlayToolBox(bool); void processShowFeaturesToolBox(bool); void processOverlayHorizontalToolBoxVisibilityChanged(bool); void processOverlayVerticalToolBoxVisibilityChanged(bool); void processFileMenuAboutToShow(); void processDataMenuAboutToShow(); void processViewMenuAboutToShow(); void processTileTabsMenuAboutToBeDisplayed(); void processTileTabsMenuSelection(QAction*); void processSurfaceMenuInformation(); void processSurfaceMenuPrimaryAnatomical(); void processConnectToAllenDataBase(); void processConnectToConnectomeDataBase(); void processHcpWebsiteInBrowser(); void processHcpFeatureRequestWebsiteInBrowser(); void processReportWorkbenchBug(); void processShowSurfacePropertiesDialog(); void processDevelopGraphicsTiming(); void processDevelopExportVtkFile(); void developerMenuAboutToShow(); void developerMenuFlagTriggered(QAction*); void processProjectFoci(); void processSplitBorderFiles(); void processTabAspectRatioLockedToggled(bool checked); void processWindowAspectRatioLockedToggled(bool checked); void processLockAllTabsAspectRatioTriggered(); void processUnlockAllTabsAspectRatioTriggered(); void processEditMenuItemTriggered(QAction* action); void processEditMenuAboutToShow(); private: // Contains status of components such as enter/exit full screen struct WindowComponentStatus { bool isFeaturesToolBoxDisplayed; bool isOverlayToolBoxDisplayed; bool isToolBarDisplayed; QByteArray windowState; QByteArray windowGeometry; QByteArray featuresGeometry; }; enum CreateDefaultTabsMode { CREATE_DEFAULT_TABS_YES, CREATE_DEFAULT_TABS_NO }; BrainBrowserWindow(const int browserWindowIndex, BrowserTabContent* browserTabContent, const CreateDefaultTabsMode createDefaultTabsMode, QWidget* parent = 0, Qt::WindowFlags flags = 0); BrainBrowserWindow(const BrainBrowserWindow&); BrainBrowserWindow& operator=(const BrainBrowserWindow&); bool loadFiles(QWidget* parentForDialogs, const std::vector& filenames, const std::vector dataFileTypes, const LoadSpecFileMode loadSpecFileMode, const AString& username, const AString& password); void createActions(); void createActionsUsedByToolBar(); void createMenus(); QMenu* createMenuDevelop(); QMenu* createMenuEdit(); QMenu* createMenuFile(); QMenu* createMenuView(); QMenu* createMenuViewMoveOverlayToolBox(); QMenu* createMenuViewMoveFeaturesToolBox(); QMenu* createMenuConnect(); QMenu* createMenuData(); QMenu* createMenuSurface(); QMenu* createMenuVolume(); QMenu* createMenuWindow(); QMenu* createMenuHelp(); void moveOverlayToolBox(Qt::DockWidgetArea area); void moveFeaturesToolBox(Qt::DockWidgetArea area); void restoreWindowComponentStatus(const WindowComponentStatus& wcs); void saveWindowComponentStatus(WindowComponentStatus& wcs); void openSpecFile(const AString& specFileName); void processViewFullScreen(bool showFullScreenDisplay, const bool saveRestoreWindowStatus); void setViewTileTabs(const bool newStatus); bool isMacOptionKeyDown() const; /** Index of this window */ int32_t m_browserWindowIndex; BrainOpenGLWidget* m_openGLWidget; BrainBrowserWindowToolBar* m_toolbar; QAction* m_aboutWorkbenchAction; QAction* m_newWindowAction; QAction* m_newTabAction; QAction* m_duplicateTabAction; QAction* m_openFileAction; QAction* m_openLocationAction; QAction* m_manageFilesAction; QAction* m_closeSpecFileAction; QAction* m_closeTabAction; QAction* m_closeWindowAction; AString m_closeWindowActionConfirmTitle; AString m_closeWindowActionNoConfirmTitle; bool m_closeWithoutConfirmationFlag; QAction* m_captureImageAction; QAction* m_recordMovieAction; QAction* m_preferencesAction; QAction* m_exitProgramAction; QAction* m_showToolBarAction; QMenu* m_viewMoveFeaturesToolBoxMenu; QMenu* m_viewMoveOverlayToolBoxMenu; QAction* m_viewFullScreenAction; QAction* m_viewTileTabsAction; bool m_viewTileTabsSelected; QMenu* m_tileTabsMenu; QAction* m_createAndEditTileTabsAction; QAction* m_gapsAndMarginsAction; QAction* m_nextTabAction; QAction* m_previousTabAction; QAction* m_renameSelectedTabAction; QAction* m_moveTabsInWindowToNewWindowsAction; QAction* m_moveTabsFromAllWindowsToOneWindowAction; QAction* m_bringAllToFrontAction; QAction* m_tileWindowsAction; QAction* m_informationDialogAction; QAction* m_connectToAllenDatabaseAction; QAction* m_connectToConnectomeDatabaseAction; QAction* m_helpHcpWebsiteAction; QAction* m_helpHcpFeatureRequestAction; QAction* m_helpWorkbenchBugReportAction; QAction* m_developMenuAction; QActionGroup* m_developerFlagsActionGroup; QAction* m_developerGraphicsTimingAction; QAction* m_developerExportVtkFileAction; QAction* m_overlayToolBoxAction; QAction* m_lockAllTabsAspectRatioAction; QAction* m_unlockAllTabsAspectRatioAction; QAction* m_tabAspectRatioLockedAction; QAction* m_windowAspectRatioLockedAction; QAction* m_featuresToolBoxAction; QAction* m_dataFociProjectAction; QAction* m_dataBorderFilesSplitAction; QMenu* m_moveSelectedTabToWindowMenu; QMenu* m_recentSpecFileMenu; AString m_recentSpecFileMenuOpenConfirmTitle; AString m_recentSpecFileMenuLoadNoConfirmTitle; QMenu* m_recentSceneFileMenu; QMenu* m_editMenu; QAction* m_editMenuRedoAction; QAction* m_editMenuUndoAction; BrainBrowserWindowOrientedToolBox* m_overlayHorizontalToolBox; BrainBrowserWindowOrientedToolBox* m_overlayVerticalToolBox; BrainBrowserWindowOrientedToolBox* m_overlayActiveToolBox; BrainBrowserWindowOrientedToolBox* m_featuresToolBox; AString m_selectedTileTabsConfigurationUniqueIdentifier; TileTabsConfiguration* m_defaultTileTabsConfiguration; TileTabsConfiguration* m_sceneTileTabsConfiguration; AString m_sceneTileTabsConfigurationText; static AString s_previousOpenFileNameFilter; static AString s_previousOpenFileDirectory; static QByteArray s_previousOpenFileGeometry; WindowComponentStatus m_defaultWindowComponentStatus; WindowComponentStatus m_normalWindowComponentStatus; float m_aspectRatio; static bool s_firstWindowFlag; friend class BrainBrowserWindowToolBar; friend class GuiManager; SceneClassAssistant* m_sceneAssistant; /** X position from scene file for first window */ static int32_t s_sceneFileFirstWindowX; /** Y position from scene file for first window */ static int32_t s_sceneFileFirstWindowY; }; #ifdef __BRAIN_BROWSER_WINDOW_DECLARE__ AString BrainBrowserWindow::s_previousOpenFileNameFilter; AString BrainBrowserWindow::s_previousOpenFileDirectory; QByteArray BrainBrowserWindow::s_previousOpenFileGeometry; bool BrainBrowserWindow::s_firstWindowFlag = true; int32_t BrainBrowserWindow::s_sceneFileFirstWindowX = -1; int32_t BrainBrowserWindow::s_sceneFileFirstWindowY = -1; #endif // __BRAIN_BROWSER_WINDOW_DECLARE__ } #endif // __BRAIN_BROWSER_WINDOW_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/GuiQt/BrainBrowserWindowComboBox.cxx000066400000000000000000000125351300200146000275470ustar00rootroot00000000000000 /*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_COMBO_BOX_DECLARE__ #include "BrainBrowserWindowComboBox.h" #undef __BRAIN_BROWSER_WINDOW_COMBO_BOX_DECLARE__ #include #include "BrainBrowserWindow.h" #include "CaretAssert.h" #include "GuiManager.h" using namespace caret; /** * \class caret::BrainBrowserWindowComboBox * \brief Combo box for selection of browser windows * \ingroup GuiQt */ /** * Constructor. * * @param style * Style of combo box content * @param parent * Parent of this combo box */ BrainBrowserWindowComboBox::BrainBrowserWindowComboBox(const Style style, QObject* parent) : WuQWidget(parent), m_style(style) { m_comboBox = new QComboBox(); QObject::connect(m_comboBox, SIGNAL(currentIndexChanged(int)), this, SLOT(comboBoxIndexSelected(int))); } /** * Destructor. */ BrainBrowserWindowComboBox::~BrainBrowserWindowComboBox() { } /** * Called when the user selects an item from the combo box. * @param indx * Index of item selected. */ void BrainBrowserWindowComboBox::comboBoxIndexSelected(int /*indx*/) { BrainBrowserWindow* bbw = getSelectedBrowserWindow(); if (bbw != NULL) { emit browserWindowIndexSelected(bbw->getBrowserWindowIndex()); emit browserWindowSelected(bbw); } } /** * Update the combo box. */ void BrainBrowserWindowComboBox::updateComboBox() { m_comboBox->blockSignals(true); std::vector browserWindows = GuiManager::get()->getAllOpenBrainBrowserWindows(); BrainBrowserWindow* selectedWindow = getSelectedBrowserWindow(); m_comboBox->clear(); int32_t defaultIndex = 0; const int32_t numWindows = static_cast(browserWindows.size()); for (int32_t i = 0; i < numWindows; i++) { BrainBrowserWindow* bbw = browserWindows[i]; const int32_t browserWindowIndex = bbw->getBrowserWindowIndex(); switch (m_style) { case STYLE_NAME_AND_NUMBER: m_comboBox->addItem("Window " + QString::number(browserWindowIndex + 1)); break; case STYLE_NUMBER: m_comboBox->addItem(QString::number(browserWindowIndex + 1)); break; } m_comboBox->setItemData(i, qVariantFromValue((void*)bbw)); if (bbw == selectedWindow) { defaultIndex = i; } } if (defaultIndex < m_comboBox->count()) { m_comboBox->setCurrentIndex(defaultIndex); } m_comboBox->blockSignals(false); } /** * @return The widget inside this control. */ QWidget* BrainBrowserWindowComboBox::getWidget() { return m_comboBox; } /** * Set the selected browser window using the browser window index. * If the index is invalid selection will not change. * * @param browserWindowIndex * Index of window. */ void BrainBrowserWindowComboBox::setBrowserWindowByIndex(const int32_t browserWindowIndex) { for (int32_t i = 0; i < m_comboBox->count(); i++) { void* pointer = m_comboBox->itemData(i).value(); BrainBrowserWindow* bbw = (BrainBrowserWindow*)pointer; if (bbw->getBrowserWindowIndex() == browserWindowIndex) { m_comboBox->blockSignals(true); m_comboBox->setCurrentIndex(i); m_comboBox->blockSignals(false); return; } } } /** * Set the selected browser window. * If the window is invalid selection will not change. * * @param browserWindow * The window. */ void BrainBrowserWindowComboBox::setBrowserWindow(BrainBrowserWindow* browserWindow) { CaretAssert(browserWindow); setBrowserWindowByIndex(browserWindow->getBrowserWindowIndex()); } /** * @return Index of selected browser window. A negative value * is returned if invalid. */ int32_t BrainBrowserWindowComboBox::getSelectedBrowserWindowIndex() const { int32_t indx = -1; BrainBrowserWindow* bbw = getSelectedBrowserWindow(); if (bbw != NULL) { indx = bbw->getBrowserWindowIndex(); } return indx; } /** * @return Selected browser window. NULL is returned if invalid. */ BrainBrowserWindow* BrainBrowserWindowComboBox::getSelectedBrowserWindow() const { BrainBrowserWindow* bbw = NULL; const int32_t indx = m_comboBox->currentIndex(); if ((indx >= 0) && (indx < m_comboBox->count())) { void* pointer = m_comboBox->itemData(indx).value(); bbw = (BrainBrowserWindow*)pointer; } return bbw; } connectome-workbench-1.2.3+git41-gc4c6c90/src/GuiQt/BrainBrowserWindowComboBox.h000066400000000000000000000053161300200146000271730ustar00rootroot00000000000000#ifndef __BRAIN_BROWSER_WINDOW_COMBO_BOX_H__ #define __BRAIN_BROWSER_WINDOW_COMBO_BOX_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 "WuQWidget.h" class QComboBox; namespace caret { class BrainBrowserWindow; class BrainBrowserWindowComboBox : public WuQWidget { Q_OBJECT public: /** * Style of combo box content */ enum Style { /** Name and number: "Window 3" */ STYLE_NAME_AND_NUMBER, /** Number only: "3" */ STYLE_NUMBER }; BrainBrowserWindowComboBox(const Style style, QObject* parent); virtual ~BrainBrowserWindowComboBox(); void updateComboBox(); virtual QWidget* getWidget(); void setBrowserWindowByIndex(const int32_t browserWindowIndex); void setBrowserWindow(BrainBrowserWindow* browserWindow); int32_t getSelectedBrowserWindowIndex() const; BrainBrowserWindow* getSelectedBrowserWindow() const; signals: void browserWindowIndexSelected(const int32_t browserWindowIndex); void browserWindowSelected(BrainBrowserWindow* browserWindow); private slots: void comboBoxIndexSelected(int indx); private: BrainBrowserWindowComboBox(const BrainBrowserWindowComboBox&); BrainBrowserWindowComboBox& operator=(const BrainBrowserWindowComboBox&); public: // ADD_NEW_METHODS_HERE private: const Style m_style; QComboBox* m_comboBox; // ADD_NEW_MEMBERS_HERE }; #ifdef __BRAIN_BROWSER_WINDOW_COMBO_BOX_DECLARE__ // #endif // __BRAIN_BROWSER_WINDOW_COMBO_BOX_DECLARE__ } // namespace #endif //__BRAIN_BROWSER_WINDOW_COMBO_BOX_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/GuiQt/BrainBrowserWindowEditMenuItemEnum.cxx000066400000000000000000000336001300200146000312110ustar00rootroot00000000000000 /*LICENSE_START*/ /* * Copyright (C) 2015 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_BROWSER_WINDOW_EDIT_MENU_ITEM_ENUM_DECLARE__ #include "BrainBrowserWindowEditMenuItemEnum.h" #undef __BRAIN_BROWSER_WINDOW_EDIT_MENU_ITEM_ENUM_DECLARE__ #include "CaretAssert.h" using namespace caret; /** * \class caret::BrainBrowserWindowEditMenuItemEnum * \brief Enumerated type for items on the Edit Menu * * 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_brainBrowserWindowEditMenuItemEnumComboBox; * * Declare a slot that is called when user changes selection * private slots: * void brainBrowserWindowEditMenuItemEnumComboBoxItemActivated(); * * Implementation File (.cxx) * Include the header files * #include "EnumComboBoxTemplate.h" * #include "BrainBrowserWindowEditMenuItemEnum.h" * * Instatiate: * m_brainBrowserWindowEditMenuItemEnumComboBox = new EnumComboBoxTemplate(this); * m_brainBrowserWindowEditMenuItemEnumComboBox->setup(); * * Get notified when the user changes the selection: * QObject::connect(m_brainBrowserWindowEditMenuItemEnumComboBox, SIGNAL(itemActivated()), * this, SLOT(brainBrowserWindowEditMenuItemEnumComboBoxItemActivated())); * * Update the selection: * m_brainBrowserWindowEditMenuItemEnumComboBox->setSelectedItem(NEW_VALUE); * * Read the selection: * const BrainBrowserWindowEditMenuItemEnum::Enum VARIABLE = m_brainBrowserWindowEditMenuItemEnumComboBox->getSelectedItem(); * */ /** * Constructor. * * @param enumValue * An enumerated value. * @param name * Name of enumerated value. * @param guiName * User-friendly name for use in user-interface. * @param shortCut * Shortcut for menu item. */ BrainBrowserWindowEditMenuItemEnum::BrainBrowserWindowEditMenuItemEnum(const Enum enumValue, const AString& name, const AString& guiName, const QKeySequence& shortCut) { this->enumValue = enumValue; this->integerCode = integerCodeCounter++; this->name = name; this->guiName = guiName; this->shortCut = shortCut; } /** * Destructor. */ BrainBrowserWindowEditMenuItemEnum::~BrainBrowserWindowEditMenuItemEnum() { } /** * Initialize the enumerated metadata. */ void BrainBrowserWindowEditMenuItemEnum::initialize() { if (initializedFlag) { return; } initializedFlag = true; QKeySequence noShortCutKeySequence; enumData.push_back(BrainBrowserWindowEditMenuItemEnum(COPY, "COPY", "Copy", (Qt::CTRL + Qt::Key_C))); enumData.push_back(BrainBrowserWindowEditMenuItemEnum(CUT, "CUT", "Cut", (Qt::CTRL + Qt::Key_X))); enumData.push_back(BrainBrowserWindowEditMenuItemEnum(DELETER, "DELETER", "Delete", noShortCutKeySequence)); enumData.push_back(BrainBrowserWindowEditMenuItemEnum(PASTE, "PASTE", "Paste", (Qt::CTRL + Qt::Key_V))); enumData.push_back(BrainBrowserWindowEditMenuItemEnum(PASTE_SPECIAL, "PASTE", "Paste Special", (Qt::CTRL + Qt::SHIFT + Qt::Key_V))); enumData.push_back(BrainBrowserWindowEditMenuItemEnum(REDO, "REDO", "Redo", (Qt::CTRL + Qt::Key_Y))); //(Qt::CTRL + Qt::SHIFT + Qt::Key_Z))); enumData.push_back(BrainBrowserWindowEditMenuItemEnum(SELECT_ALL, "SELECT_ALL", "Select All", (Qt::CTRL + Qt::Key_A))); enumData.push_back(BrainBrowserWindowEditMenuItemEnum(UNDO, "UNDO", "Undo", (Qt::CTRL + Qt::Key_Z))); } /** * 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 BrainBrowserWindowEditMenuItemEnum* BrainBrowserWindowEditMenuItemEnum::findData(const Enum enumValue) { if (initializedFlag == false) initialize(); size_t num = enumData.size(); for (size_t i = 0; i < num; i++) { const BrainBrowserWindowEditMenuItemEnum* 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 BrainBrowserWindowEditMenuItemEnum::toName(Enum enumValue) { if (initializedFlag == false) initialize(); const BrainBrowserWindowEditMenuItemEnum* 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. */ BrainBrowserWindowEditMenuItemEnum::Enum BrainBrowserWindowEditMenuItemEnum::fromName(const AString& name, bool* isValidOut) { if (initializedFlag == false) initialize(); bool validFlag = false; Enum enumValue = BrainBrowserWindowEditMenuItemEnum::enumData[0].enumValue; for (std::vector::iterator iter = enumData.begin(); iter != enumData.end(); iter++) { const BrainBrowserWindowEditMenuItemEnum& 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 BrainBrowserWindowEditMenuItemEnum")); } return enumValue; } /** * Get a GUI string representation of the enumerated type. * @param enumValue * Enumerated value. * @return * String representing enumerated value. */ AString BrainBrowserWindowEditMenuItemEnum::toGuiName(Enum enumValue) { if (initializedFlag == false) initialize(); const BrainBrowserWindowEditMenuItemEnum* 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. */ BrainBrowserWindowEditMenuItemEnum::Enum BrainBrowserWindowEditMenuItemEnum::fromGuiName(const AString& guiName, bool* isValidOut) { if (initializedFlag == false) initialize(); bool validFlag = false; Enum enumValue = BrainBrowserWindowEditMenuItemEnum::enumData[0].enumValue; for (std::vector::iterator iter = enumData.begin(); iter != enumData.end(); iter++) { const BrainBrowserWindowEditMenuItemEnum& 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 BrainBrowserWindowEditMenuItemEnum")); } return enumValue; } /** * Get the integer code for a data type. * * @return * Integer code for data type. */ int32_t BrainBrowserWindowEditMenuItemEnum::toIntegerCode(Enum enumValue) { if (initializedFlag == false) initialize(); const BrainBrowserWindowEditMenuItemEnum* 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. */ BrainBrowserWindowEditMenuItemEnum::Enum BrainBrowserWindowEditMenuItemEnum::fromIntegerCode(const int32_t integerCode, bool* isValidOut) { if (initializedFlag == false) initialize(); bool validFlag = false; Enum enumValue = BrainBrowserWindowEditMenuItemEnum::enumData[0].enumValue; for (std::vector::iterator iter = enumData.begin(); iter != enumData.end(); iter++) { const BrainBrowserWindowEditMenuItemEnum& 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 BrainBrowserWindowEditMenuItemEnum")); } return enumValue; } /** * Get the shortcut key for a data type. * * @return * Shortcut keys for data type. */ QKeySequence BrainBrowserWindowEditMenuItemEnum::toShortcut(Enum enumValue) { if (initializedFlag == false) initialize(); const BrainBrowserWindowEditMenuItemEnum* enumInstance = findData(enumValue); return enumInstance->shortCut; } /** * 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 BrainBrowserWindowEditMenuItemEnum::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 BrainBrowserWindowEditMenuItemEnum::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(BrainBrowserWindowEditMenuItemEnum::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 BrainBrowserWindowEditMenuItemEnum::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(BrainBrowserWindowEditMenuItemEnum::toGuiName(iter->enumValue)); } if (isSorted) { std::sort(allGuiNames.begin(), allGuiNames.end()); } } connectome-workbench-1.2.3+git41-gc4c6c90/src/GuiQt/BrainBrowserWindowEditMenuItemEnum.h000066400000000000000000000074251300200146000306440ustar00rootroot00000000000000#ifndef __BRAIN_BROWSER_WINDOW_EDIT_MENU_ITEM_ENUM_H__ #define __BRAIN_BROWSER_WINDOW_EDIT_MENU_ITEM_ENUM_H__ /*LICENSE_START*/ /* * Copyright (C) 2015 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 "AString.h" namespace caret { class BrainBrowserWindowEditMenuItemEnum { public: /** * Enumerated values. */ enum Enum { /** Copy */ COPY, /** Cut */ CUT, /** Delete (note: 'DELETE' is reserved word on Windows) */ DELETER, /** Paste */ PASTE, /** Paste Special */ PASTE_SPECIAL, /** Redo */ REDO, /** Select All */ SELECT_ALL, /** Undo */ UNDO }; ~BrainBrowserWindowEditMenuItemEnum(); 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 QKeySequence toShortcut(Enum enumValue); 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: BrainBrowserWindowEditMenuItemEnum(const Enum enumValue, const AString& name, const AString& guiName, const QKeySequence& shortCut); static const BrainBrowserWindowEditMenuItemEnum* 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; /** Shortcut key */ QKeySequence shortCut; }; #ifdef __BRAIN_BROWSER_WINDOW_EDIT_MENU_ITEM_ENUM_DECLARE__ std::vector BrainBrowserWindowEditMenuItemEnum::enumData; bool BrainBrowserWindowEditMenuItemEnum::initializedFlag = false; int32_t BrainBrowserWindowEditMenuItemEnum::integerCodeCounter = 0; #endif // __BRAIN_BROWSER_WINDOW_EDIT_MENU_ITEM_ENUM_DECLARE__ } // namespace #endif //__BRAIN_BROWSER_WINDOW_EDIT_MENU_ITEM_ENUM_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/GuiQt/BrainBrowserWindowOrientedToolBox.cxx000066400000000000000000000675421300200146000311270ustar00rootroot00000000000000/*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 "AnnotationFile.h" #include "AnnotationSelectionViewController.h" #include "BorderSelectionViewController.h" #include "Brain.h" #include "BrainBrowserWindow.h" #include "BrainBrowserWindowOrientedToolBox.h" #include "BrowserTabContent.h" #include "CaretAssert.h" #include "CaretDataFile.h" #include "CaretPreferences.h" #include "ChartableLineSeriesBrainordinateInterface.h" #include "ChartableMatrixInterface.h" #include "ChartToolBoxViewController.h" #include "CiftiConnectivityMatrixViewController.h" #include "EventBrowserWindowContentGet.h" #include "EventManager.h" #include "EventUserInterfaceUpdate.h" #include "FiberOrientationSelectionViewController.h" #include "FociSelectionViewController.h" #include "GuiManager.h" #include "ImageSelectionViewController.h" #include "LabelSelectionViewController.h" #include "OverlaySetViewController.h" #include "SceneClass.h" #include "SceneWindowGeometry.h" #include "SessionManager.h" #include "VolumeFile.h" #include "VolumeSurfaceOutlineSetViewController.h" #include "WuQtUtilities.h" using namespace caret; /** * Construct the toolbox. * * @param browserWindowIndex * Index of browser window that contains this toolbox. * @param title * Title for the toolbox. * @param location * Locations allowed for this toolbox. */ BrainBrowserWindowOrientedToolBox::BrainBrowserWindowOrientedToolBox(const int32_t browserWindowIndex, const QString& title, const ToolBoxType toolBoxType, QWidget* parent) : QDockWidget(parent) { m_browserWindowIndex = browserWindowIndex; toggleViewAction()->setText("Toolbox"); m_toolBoxTitle = title; setWindowTitle(m_toolBoxTitle); bool isFeaturesToolBox = false; bool isOverlayToolBox = false; Qt::Orientation orientation = Qt::Horizontal; AString toolboxTypeName = ""; switch (toolBoxType) { case TOOL_BOX_FEATURES: orientation = Qt::Vertical; isFeaturesToolBox = true; toggleViewAction()->setText("Features Toolbox"); toolboxTypeName = "Features"; break; case TOOL_BOX_OVERLAYS_HORIZONTAL: orientation = Qt::Horizontal; isOverlayToolBox = true; toolboxTypeName = "OverlayHorizontal"; break; case TOOL_BOX_OVERLAYS_VERTICAL: orientation = Qt::Vertical; isOverlayToolBox = true; toolboxTypeName = "OverlayVertical"; break; } /* * Needed for saving and restoring window state in main window */ CaretAssert(toolboxTypeName.length() > 0); setObjectName("BrainBrowserWindowOrientedToolBox_" + toolboxTypeName + "_" + AString::number(browserWindowIndex)); m_annotationViewController = NULL; m_borderSelectionViewController = NULL; m_chartToolBoxViewController = NULL; m_connectivityMatrixViewController = NULL; m_fiberOrientationViewController = NULL; m_fociSelectionViewController = NULL; m_imageSelectionViewController = NULL; m_labelSelectionViewController = NULL; m_overlaySetViewController = NULL; m_volumeSurfaceOutlineSetViewController = NULL; m_tabWidget = new QTabWidget(); m_annotationTabIndex = -1; m_borderTabIndex = -1; m_chartTabIndex = -1; m_connectivityTabIndex = -1; m_fiberOrientationTabIndex = -1; m_fociTabIndex = -1; m_imageTabIndex = -1; m_labelTabIndex = -1; m_overlayTabIndex = -1; m_volumeSurfaceOutlineTabIndex = -1; if (isOverlayToolBox) { m_overlaySetViewController = new OverlaySetViewController(orientation, browserWindowIndex, this); m_overlayTabIndex = addToTabWidget(m_overlaySetViewController, "Layers"); } if (isOverlayToolBox) { m_chartToolBoxViewController = new ChartToolBoxViewController(orientation, browserWindowIndex, this); m_chartTabIndex = addToTabWidget(m_chartToolBoxViewController, "Charting"); } if (isOverlayToolBox) { m_connectivityMatrixViewController = new CiftiConnectivityMatrixViewController(orientation, this); m_connectivityTabIndex = addToTabWidget(m_connectivityMatrixViewController, "Connectivity"); } if (isFeaturesToolBox) { m_annotationViewController = new AnnotationSelectionViewController(browserWindowIndex, this); m_annotationTabIndex = addToTabWidget(m_annotationViewController, "Annot"); } if (isFeaturesToolBox) { m_borderSelectionViewController = new BorderSelectionViewController(browserWindowIndex, this); m_borderTabIndex = addToTabWidget(m_borderSelectionViewController, "Borders"); } if (isFeaturesToolBox) { m_fiberOrientationViewController = new FiberOrientationSelectionViewController(browserWindowIndex, this); m_fiberOrientationTabIndex = addToTabWidget(m_fiberOrientationViewController, "Fibers"); } if (isFeaturesToolBox) { m_fociSelectionViewController = new FociSelectionViewController(browserWindowIndex, this); m_fociTabIndex = addToTabWidget(m_fociSelectionViewController, "Foci"); } if (isFeaturesToolBox) { m_imageSelectionViewController = new ImageSelectionViewController(browserWindowIndex, this); m_imageTabIndex = addToTabWidget(m_imageSelectionViewController, "Images"); } if (isFeaturesToolBox) { m_labelSelectionViewController = new LabelSelectionViewController(browserWindowIndex, this); m_labelTabIndex = addToTabWidget(m_labelSelectionViewController, "Labels"); } if (isOverlayToolBox) { m_volumeSurfaceOutlineSetViewController = new VolumeSurfaceOutlineSetViewController(orientation, m_browserWindowIndex); m_volumeSurfaceOutlineTabIndex = addToTabWidget(m_volumeSurfaceOutlineSetViewController, "Vol/Surf Outline"); } setWidget(m_tabWidget); if (orientation == Qt::Horizontal) { setMinimumHeight(200); setMaximumHeight(800); } else { if (isOverlayToolBox) { setMinimumWidth(300); setMaximumWidth(800); } else { setMinimumWidth(200); setMaximumWidth(800); } } QObject::connect(this, SIGNAL(topLevelChanged(bool)), this, SLOT(floatingStatusChanged(bool))); EventManager::get()->addEventListener(this, EventTypeEnum::EVENT_USER_INTERFACE_UPDATE); } /** * Destructor. */ BrainBrowserWindowOrientedToolBox::~BrainBrowserWindowOrientedToolBox() { EventManager::get()->removeAllEventsFromListener(this); } /** * Place widget into a scroll area and then into the tab widget. * @param page * Widget that is added. * @param label * Name corresponding to widget's tab. */ int BrainBrowserWindowOrientedToolBox::addToTabWidget(QWidget* page, const QString& label) { QScrollArea* scrollArea = new QScrollArea(); scrollArea->setWidget(page); scrollArea->setWidgetResizable(true); scrollArea->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); int indx = m_tabWidget->addTab(scrollArea, label); return indx; } /** * Called when floating status changes. * @param status * New floating status. */ void BrainBrowserWindowOrientedToolBox::floatingStatusChanged(bool /*status*/) { QString title = m_toolBoxTitle; setWindowTitle(title); } /** * Create a scene for an instance of a class. * * @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. * * @return Pointer to SceneClass object representing the state of * this object. Under some circumstances a NULL pointer may be * returned. Caller will take ownership of returned object. */ SceneClass* BrainBrowserWindowOrientedToolBox::saveToScene(const SceneAttributes* sceneAttributes, const AString& instanceName) { SceneClass* sceneClass = new SceneClass(instanceName, "BrainBrowserWindowOrientedToolBox", 1); AString tabName; const int tabIndex = m_tabWidget->currentIndex(); if ((tabIndex >= 0) && tabIndex < m_tabWidget->count()) { tabName = m_tabWidget->tabText(tabIndex); } sceneClass->addString("selectedTabName", tabName); if (m_chartToolBoxViewController != NULL) { sceneClass->addClass(m_chartToolBoxViewController->saveToScene(sceneAttributes, "m_chartToolBoxViewController")); } // if (m_chartTabWidget != NULL) { // const AString chartTabName = m_chartTabWidget->tabText(m_chartTabWidget->currentIndex()); // sceneClass->addString("selectedChartTabName", // chartTabName); // } /* * Save current widget size */ QWidget* childWidget = m_tabWidget->currentWidget(); if (childWidget != NULL) { SceneWindowGeometry swg(childWidget, this); sceneClass->addClass(swg.saveToScene(sceneAttributes, "childWidget")); } /* * Save the toolbox */ SceneWindowGeometry swg(this, GuiManager::get()->getBrowserWindowByWindowIndex(this->m_browserWindowIndex)); sceneClass->addClass(swg.saveToScene(sceneAttributes, "geometry")); /** * Save the size when visible BUT NOT floating */ if (isFloating() == false) { sceneClass->addInteger("toolboxWidth", width()); sceneClass->addInteger("toolboxHeight", height()); } /* * Save controllers in the toolbox */ if (m_annotationViewController != NULL) { sceneClass->addClass(m_annotationViewController->saveToScene(sceneAttributes, "m_annotationViewController")); } if (m_borderSelectionViewController != NULL) { sceneClass->addClass(m_borderSelectionViewController->saveToScene(sceneAttributes, "m_borderSelectionViewController")); } if (m_fiberOrientationViewController != NULL) { sceneClass->addClass(m_fiberOrientationViewController->saveToScene(sceneAttributes, "m_fiberOrientationViewController")); } if (m_fociSelectionViewController != NULL) { sceneClass->addClass(m_fociSelectionViewController->saveToScene(sceneAttributes, "m_fociSelectionViewController")); } if (m_imageSelectionViewController != NULL) { sceneClass->addClass(m_imageSelectionViewController->saveToScene(sceneAttributes, "m_imageSelectionViewController")); } if (m_labelSelectionViewController != NULL) { sceneClass->addClass(m_labelSelectionViewController->saveToScene(sceneAttributes, "m_labelSelectionViewController")); } return sceneClass; } /** * Restore the state of an instance of a class. * * @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 containing the state that was previously * saved and should be restored. */ void BrainBrowserWindowOrientedToolBox::restoreFromScene(const SceneAttributes* sceneAttributes, const SceneClass* sceneClass) { if (sceneClass == NULL) { return; } const AString tabName = sceneClass->getStringValue("selectedTabName", ""); for (int32_t i = 0; i < m_tabWidget->count(); i++) { if (m_tabWidget->tabText(i) == tabName) { m_tabWidget->setCurrentIndex(i); break; } } if (m_chartToolBoxViewController != NULL) { m_chartToolBoxViewController->restoreFromScene(sceneAttributes, sceneClass->getClass("m_chartToolBoxViewController")); } // const AString chartTabName = sceneClass->getStringValue("selectedChartTabName", // ""); // if ( ! chartTabName.isEmpty()) { // for (int32_t i = 0; i < m_chartTabWidget->count(); i++) { // if (m_chartTabWidget->tabText(i) == chartTabName) { // m_chartTabWidget->setCurrentIndex(i); // break; // } // } // } /* * Restore controllers in the toolbox */ if (m_annotationViewController != NULL) { m_annotationViewController->restoreFromScene(sceneAttributes, sceneClass->getClass("m_annotationViewController")); } if (m_borderSelectionViewController != NULL) { m_borderSelectionViewController->restoreFromScene(sceneAttributes, sceneClass->getClass("m_borderSelectionViewController")); } if (m_fiberOrientationViewController != NULL) { m_fiberOrientationViewController->restoreFromScene(sceneAttributes, sceneClass->getClass("m_fiberOrientationViewController")); } if (m_fociSelectionViewController != NULL) { m_fociSelectionViewController->restoreFromScene(sceneAttributes, sceneClass->getClass("m_fociSelectionViewController")); } if (m_imageSelectionViewController != NULL) { m_imageSelectionViewController->restoreFromScene(sceneAttributes, sceneClass->getClass("m_imageSelectionViewController")); } if (m_labelSelectionViewController != NULL) { m_labelSelectionViewController->restoreFromScene(sceneAttributes, sceneClass->getClass("m_labelSelectionViewController")); } /* * Restore current widget size */ QWidget* childWidget = m_tabWidget->currentWidget(); QSize childMinSize; QSize childSize; if (childWidget != NULL) { childMinSize = childWidget->minimumSize(); SceneWindowGeometry swg(childWidget, this); swg.restoreFromScene(sceneAttributes, sceneClass->getClass("childWidget")); childSize = childWidget->size(); } if (isFloating() && isVisible()) { SceneWindowGeometry swg(this, GuiManager::get()->getBrowserWindowByWindowIndex(this->m_browserWindowIndex)); swg.restoreFromScene(sceneAttributes, sceneClass->getClass("geometry")); } else { /* * From http://stackoverflow.com/questions/2722939/c-resize-a-docked-qt-qdockwidget-programmatically * * Set the minimum and maximum sizes and restore them later. * Trying to restore them immediately does not work. So, as * explained in the link above, set the minimum and maximum * sizes to that the toolbox is the correct size and then use * a timer to restore the correct values for the minimum and * maximum sizes after a little delay. */ const int w = sceneClass->getIntegerValue("toolboxWidth", -1); const int h = sceneClass->getIntegerValue("toolboxHeight", -1); if ((w > 0) && (h > 0)) { m_minimumSizeAfterSceneRestored = minimumSize(); m_maximumSizeAfterSceneRestored = maximumSize(); setMaximumWidth(w); setMaximumHeight(h); setMinimumWidth(w); setMinimumHeight(h); QTimer::singleShot(1000, // 1000 ms => 1 second this, SLOT(restoreMinimumAndMaximumSizesAfterSceneRestored())); } } } /** * This slot is called when restoring a scene */ void BrainBrowserWindowOrientedToolBox::restoreMinimumAndMaximumSizesAfterSceneRestored() { setMinimumSize(m_minimumSizeAfterSceneRestored); setMaximumSize(m_maximumSizeAfterSceneRestored); } /** * Receive events from the event manager. * * @param event * Event sent by event manager. */ void BrainBrowserWindowOrientedToolBox::receiveEvent(Event* event) { if (event->getEventType() == EventTypeEnum::EVENT_USER_INTERFACE_UPDATE) { EventUserInterfaceUpdate* uiEvent = dynamic_cast(event); CaretAssert(uiEvent); uiEvent->setEventProcessed(); Brain* brain = GuiManager::get()->getBrain(); /* * Determine types of data this is loaded */ bool haveAnnotation = ( ! brain->getSceneAnnotationFile()->isEmpty()); bool haveBorders = false; bool haveConnFiles = false; bool haveFibers = false; bool haveFoci = false; bool haveImages = false; bool haveLabels = false; bool haveSurfaces = false; bool haveVolumes = false; std::vector allDataFiles; brain->getAllDataFiles(allDataFiles); for (std::vector::iterator iter = allDataFiles.begin(); iter != allDataFiles.end(); iter++) { const CaretDataFile* caretDataFile = *iter; const DataFileTypeEnum::Enum dataFileType = caretDataFile->getDataFileType(); switch (dataFileType) { case DataFileTypeEnum::ANNOTATION: haveAnnotation = true; break; case DataFileTypeEnum::BORDER: haveBorders = true; break; case DataFileTypeEnum::CONNECTIVITY_DENSE: haveConnFiles = true; break; case DataFileTypeEnum::CONNECTIVITY_DENSE_DYNAMIC: haveConnFiles = true; break; case DataFileTypeEnum::CONNECTIVITY_DENSE_LABEL: haveLabels = true; break; case DataFileTypeEnum::CONNECTIVITY_DENSE_PARCEL: haveConnFiles = true; break; case DataFileTypeEnum::CONNECTIVITY_DENSE_SCALAR: break; case DataFileTypeEnum::CONNECTIVITY_DENSE_TIME_SERIES: break; case DataFileTypeEnum::CONNECTIVITY_FIBER_ORIENTATIONS_TEMPORARY: haveFibers = true; break; case DataFileTypeEnum::CONNECTIVITY_FIBER_TRAJECTORY_TEMPORARY: haveConnFiles = true; break; case DataFileTypeEnum::CONNECTIVITY_PARCEL: haveConnFiles = true; break; case DataFileTypeEnum::CONNECTIVITY_PARCEL_DENSE: haveConnFiles = true; break; case DataFileTypeEnum::CONNECTIVITY_PARCEL_LABEL: haveLabels = true; break; case DataFileTypeEnum::CONNECTIVITY_PARCEL_SCALAR: break; case DataFileTypeEnum::CONNECTIVITY_PARCEL_SERIES: break; case DataFileTypeEnum::CONNECTIVITY_SCALAR_DATA_SERIES: break; case DataFileTypeEnum::FOCI: haveFoci = true; break; case DataFileTypeEnum::IMAGE: haveImages = true; break; case DataFileTypeEnum::LABEL: haveLabels = true; break; case DataFileTypeEnum::METRIC: break; case DataFileTypeEnum::PALETTE: break; case DataFileTypeEnum::RGBA: break; case DataFileTypeEnum::SCENE: break; case DataFileTypeEnum::SPECIFICATION: break; case DataFileTypeEnum::SURFACE: haveSurfaces = true; break; case DataFileTypeEnum::UNKNOWN: break; case DataFileTypeEnum::VOLUME: { haveVolumes = true; const VolumeFile* vf = dynamic_cast(caretDataFile); CaretAssert(vf); if (vf->isMappedWithLabelTable()) { haveLabels = true; } } break; } } /* * Enable surface volume outline only if have both surfaces and volumes * loaded and model being viewed is allows drawing of volume surface * outline. */ int defaultTabIndex = -1; bool enableLayers = true; bool enableVolumeSurfaceOutline = false; bool enableCharts = false; EventBrowserWindowContentGet browserContentEvent(m_browserWindowIndex); EventManager::get()->sendEvent(browserContentEvent.getPointer()); BrowserTabContent* windowContent = browserContentEvent.getSelectedBrowserTabContent(); if (windowContent != NULL) { switch (windowContent->getSelectedModelType()) { case ModelTypeEnum::MODEL_TYPE_INVALID: break; case ModelTypeEnum::MODEL_TYPE_SURFACE: defaultTabIndex = m_overlayTabIndex; break; case ModelTypeEnum::MODEL_TYPE_SURFACE_MONTAGE: defaultTabIndex = m_overlayTabIndex; break; case ModelTypeEnum::MODEL_TYPE_VOLUME_SLICES: defaultTabIndex = m_overlayTabIndex; enableVolumeSurfaceOutline = (haveSurfaces & haveVolumes); break; case ModelTypeEnum::MODEL_TYPE_WHOLE_BRAIN: defaultTabIndex = m_overlayTabIndex; enableVolumeSurfaceOutline = (haveSurfaces & haveVolumes); break; case ModelTypeEnum::MODEL_TYPE_CHART: defaultTabIndex = m_chartTabIndex; enableLayers = false; enableVolumeSurfaceOutline = false; haveBorders = false; haveFibers = false; haveFoci = false; haveLabels = false; enableCharts = true; break; } } /* * Get the selected tab BEFORE enabling/disabling tabs. * Otherwise, the enabling/disabling of tabs may cause the selection * to change */ const int32_t tabIndex = m_tabWidget->currentIndex(); /* * Enable/disable Tabs based upon data that is loaded * NOTE: Order is important so that overlay tab is * automatically selected. */ if (m_chartTabIndex >= 0) m_tabWidget->setTabEnabled(m_chartTabIndex, enableCharts); if (m_connectivityTabIndex >= 0) m_tabWidget->setTabEnabled(m_connectivityTabIndex, haveConnFiles); if (m_volumeSurfaceOutlineTabIndex >= 0) m_tabWidget->setTabEnabled(m_volumeSurfaceOutlineTabIndex, enableVolumeSurfaceOutline); if (m_annotationTabIndex >= 0) m_tabWidget->setTabEnabled(m_annotationTabIndex, haveAnnotation); if (m_borderTabIndex >= 0) m_tabWidget->setTabEnabled(m_borderTabIndex, haveBorders); if (m_fiberOrientationTabIndex >= 0) m_tabWidget->setTabEnabled(m_fiberOrientationTabIndex, haveFibers); if (m_fociTabIndex >= 0) m_tabWidget->setTabEnabled(m_fociTabIndex, haveFoci); if (m_imageTabIndex >= 0) m_tabWidget->setTabEnabled(m_imageTabIndex, haveImages); if (m_labelTabIndex >= 0) m_tabWidget->setTabEnabled(m_labelTabIndex, haveLabels); if (m_overlayTabIndex >= 0) m_tabWidget->setTabEnabled(m_overlayTabIndex, enableLayers); /* * Switch selected tab if it is not valid */ bool tabIndexValid = false; if ((tabIndex >= 0) && (tabIndex < m_tabWidget->count())) { if (m_tabWidget->isTabEnabled(tabIndex)) { tabIndexValid = true; } } if ( ! tabIndexValid) { if (m_tabWidget->isTabEnabled(defaultTabIndex)) { m_tabWidget->setCurrentIndex(defaultTabIndex); } else { for (int i = 0; i < m_tabWidget->count(); ++i) { if (m_tabWidget->isTabEnabled(i)) { m_tabWidget->setCurrentIndex(i); break; } } } } } else { } } connectome-workbench-1.2.3+git41-gc4c6c90/src/GuiQt/BrainBrowserWindowOrientedToolBox.h000066400000000000000000000107011300200146000305350ustar00rootroot00000000000000#ifndef __BRAIN_BROWSER_WINDOW_ORIENTED_TOOLBOX_H__ #define __BRAIN_BROWSER_WINDOW_ORIENTED_TOOLBOX_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 "EventListenerInterface.h" #include "SceneableInterface.h" class QTabWidget; namespace caret { class AnnotationSelectionViewController; class BorderSelectionViewController; class ChartToolBoxViewController; class CiftiConnectivityMatrixViewController; class FiberOrientationSelectionViewController; class FociSelectionViewController; class ImageSelectionViewController; class LabelSelectionViewController; class OverlaySetViewController; class VolumeSurfaceOutlineSetViewController; class BrainBrowserWindowOrientedToolBox : public QDockWidget, public EventListenerInterface, public SceneableInterface { Q_OBJECT public: enum ToolBoxType { TOOL_BOX_FEATURES, TOOL_BOX_OVERLAYS_HORIZONTAL, TOOL_BOX_OVERLAYS_VERTICAL, }; BrainBrowserWindowOrientedToolBox(const int32_t browserWindowIndex, const QString& title, const ToolBoxType toolBoxType, QWidget* parent = 0); ~BrainBrowserWindowOrientedToolBox(); void receiveEvent(Event* event); virtual SceneClass* saveToScene(const SceneAttributes* sceneAttributes, const AString& instanceName); virtual void restoreFromScene(const SceneAttributes* sceneAttributes, const SceneClass* sceneClass); private slots: void floatingStatusChanged(bool); void restoreMinimumAndMaximumSizesAfterSceneRestored(); private: BrainBrowserWindowOrientedToolBox(const BrainBrowserWindowOrientedToolBox&); BrainBrowserWindowOrientedToolBox& operator=(const BrainBrowserWindowOrientedToolBox&); int addToTabWidget(QWidget* page, const QString& label); OverlaySetViewController* m_overlaySetViewController; AnnotationSelectionViewController* m_annotationViewController; BorderSelectionViewController* m_borderSelectionViewController; CiftiConnectivityMatrixViewController* m_connectivityMatrixViewController; ChartToolBoxViewController* m_chartToolBoxViewController; FiberOrientationSelectionViewController* m_fiberOrientationViewController; FociSelectionViewController* m_fociSelectionViewController; ImageSelectionViewController* m_imageSelectionViewController; LabelSelectionViewController* m_labelSelectionViewController; VolumeSurfaceOutlineSetViewController* m_volumeSurfaceOutlineSetViewController; QTabWidget* m_tabWidget; QString m_toolBoxTitle; int32_t m_browserWindowIndex; int32_t m_overlayTabIndex; int32_t m_annotationTabIndex; int32_t m_borderTabIndex; int32_t m_connectivityTabIndex; int32_t m_chartTabIndex; int32_t m_fiberOrientationTabIndex; int32_t m_fociTabIndex; int32_t m_imageTabIndex; int32_t m_labelTabIndex; int32_t m_volumeSurfaceOutlineTabIndex; QSize m_minimumSizeAfterSceneRestored; QSize m_maximumSizeAfterSceneRestored; }; } #endif // __BRAIN_BROWSER_WINDOW_ORIENTED_TOOLBOX_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/GuiQt/BrainBrowserWindowToolBar.cxx000066400000000000000000004443001300200146000274000ustar00rootroot00000000000000 /*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 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "AnnotationManager.h" #include "Brain.h" #include "BrainBrowserWindow.h" #include "BrainBrowserWindowToolBar.h" #include "BrainBrowserWindowToolBarChartAttributes.h" #include "BrainBrowserWindowToolBarChartAxes.h" #include "BrainBrowserWindowToolBarChartType.h" #include "BrainBrowserWindowToolBarClipping.h" #include "BrainBrowserWindowToolBarSlicePlane.h" #include "BrainBrowserWindowToolBarSliceSelection.h" #include "BrainBrowserWindowToolBarSurfaceMontage.h" #include "BrainBrowserWindowToolBarTab.h" #include "BrainBrowserWindowToolBarVolumeMontage.h" #include "BrainStructure.h" #include "BrowserTabContent.h" #include "CaretAssert.h" #include "CaretFunctionName.h" #include "CaretLogger.h" #include "CaretPreferences.h" #include "CursorDisplayScoped.h" #include "DisplayPropertiesBorders.h" #include "EventBrowserTabDelete.h" #include "EventBrowserTabGet.h" #include "EventBrowserTabGetAll.h" #include "EventBrowserTabGetAllViewed.h" #include "EventBrowserTabNew.h" #include "EventBrowserWindowContentGet.h" #include "EventBrowserWindowCreateTabs.h" #include "EventBrowserWindowNew.h" #include "EventGetOrSetUserInputModeProcessor.h" #include "EventGraphicsUpdateOneWindow.h" #include "EventGraphicsUpdateAllWindows.h" #include "EventUserInterfaceUpdate.h" #include "EventManager.h" #include "EventModelGetAll.h" #include "EventSurfaceColoringInvalidate.h" #include "EventUpdateYokedWindows.h" #include "GuiManager.h" #include "Model.h" #include "ModelChart.h" #include "ModelSurface.h" #include "ModelSurfaceMontage.h" #include "ModelSurfaceSelector.h" #include "ModelTransform.h" #include "ModelVolume.h" #include "ModelWholeBrain.h" #include "OverlaySet.h" #include "SceneAttributes.h" #include "SceneClass.h" #include "SceneIntegerArray.h" #include "ScenePrimitiveArray.h" #include "SessionManager.h" #include "Surface.h" #include "SurfaceSelectionModel.h" #include "SurfaceSelectionViewController.h" #include "StructureSurfaceSelectionControl.h" #include "UserInputModeAbstract.h" #include "VolumeFile.h" #include "VolumeSliceViewPlaneEnum.h" #include "VolumeSurfaceOutlineSetModel.h" #include "WuQDataEntryDialog.h" #include "WuQFactory.h" #include "WuQMessageBox.h" #include "WuQWidgetObjectGroup.h" #include "WuQtUtilities.h" using namespace caret; /** * Constructor. * * @param browserWindowIndex * Index of the parent browser window. * @param initialBrowserTabContent * Content of default tab (may be NULL in which cast * new content is created). * @param overlayToolBoxAction * 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 parent * Parent for this toolbar. */ BrainBrowserWindowToolBar::BrainBrowserWindowToolBar(const int32_t browserWindowIndex, BrowserTabContent* initialBrowserTabContent, QAction* overlayToolBoxAction, QAction* layersToolBoxAction, QAction* windowAspectRatioLockedAction, QAction* tabAspectRatioLockedAction, BrainBrowserWindow* parentBrainBrowserWindow) : QToolBar(parentBrainBrowserWindow) { this->browserWindowIndex = browserWindowIndex; this->updateCounter = 0; m_tabIndexForTileTabsHighlighting = -1; this->isContructorFinished = false; this->isDestructionInProgress = false; this->viewOrientationLeftIcon = NULL; this->viewOrientationRightIcon = NULL; this->viewOrientationAnteriorIcon = NULL; this->viewOrientationPosteriorIcon = NULL; this->viewOrientationDorsalIcon = NULL; this->viewOrientationVentralIcon = NULL; this->viewOrientationLeftLateralIcon = NULL; this->viewOrientationLeftMedialIcon = NULL; this->viewOrientationRightLateralIcon = NULL; this->viewOrientationRightMedialIcon = NULL; /* * Needed for saving and restoring window state in main window */ setObjectName("BrainBrowserWindowToolBar_" + AString::number(browserWindowIndex)); /* * Create tab bar that displays models. */ this->tabBar = new QTabBar(); if (WuQtUtilities::isSmallDisplay()) { this->tabBar->setStyleSheet("QTabBar::tab:selected {" " font: bold;" "} " "QTabBar::tab {" " font: italic" "}"); } else { this->tabBar->setStyleSheet("QTabBar::tab:selected {" " font: bold 14px;" "} " "QTabBar::tab {" " font: italic" "}"); } this->tabBar->setShape(QTabBar::RoundedNorth); this->tabBar->setMovable(true); #ifdef Q_OS_MACX /* * Adding a parent to the style will result in it * being destroyed when this instance is destroyed. * The style must remain valid until the destruction * of this instance. It cannot be declared statically. */ QCleanlooksStyle* cleanLooksStyle = new QCleanlooksStyle(); cleanLooksStyle->setParent(this); this->tabBar->setStyle(cleanLooksStyle); #endif // Q_OS_MACX QObject::connect(this->tabBar, SIGNAL(currentChanged(int)), this, SLOT(selectedTabChanged(int))); QObject::connect(this->tabBar, SIGNAL(tabCloseRequested(int)), this, SLOT(tabCloseSelected(int))); QObject::connect(this->tabBar, SIGNAL(tabMoved(int,int)), this, SLOT(tabMoved(int,int))); /* * Custom view action */ 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."); this->customViewAction = WuQtUtilities::createAction("Custom", customToolTip, this, this, SLOT(customViewActionTriggered())); /* * Actions at right side of toolbar */ QToolButton* informationDialogToolButton = new QToolButton(); informationDialogToolButton->setDefaultAction(GuiManager::get()->getInformationDisplayDialogEnabledAction()); QToolButton* identifyDialogToolButton = new QToolButton(); identifyDialogToolButton->setDefaultAction(GuiManager::get()->getIdentifyBrainordinateDialogDisplayAction()); QToolButton* helpDialogToolButton = new QToolButton(); helpDialogToolButton->setDefaultAction(GuiManager::get()->getHelpViewerDialogDisplayAction()); QToolButton* sceneDialogToolButton = new QToolButton(); sceneDialogToolButton->setDefaultAction(GuiManager::get()->getSceneDialogDisplayAction()); /* * Toolbar action and tool button at right of the tab bar */ QIcon toolBarIcon; const bool toolBarIconValid = WuQtUtilities::loadIcon(":/ToolBar/toolbar.png", toolBarIcon); this->toolBarToolButtonAction = WuQtUtilities::createAction("Toolbar", "Show or hide the toolbar", this, this, SLOT(showHideToolBar(bool))); if (toolBarIconValid) { this->toolBarToolButtonAction->setIcon(toolBarIcon); this->toolBarToolButtonAction->setIconVisibleInMenu(false); } this->toolBarToolButtonAction->setIconVisibleInMenu(false); this->toolBarToolButtonAction->blockSignals(true); this->toolBarToolButtonAction->setCheckable(true); this->toolBarToolButtonAction->setChecked(true); this->showHideToolBar(this->toolBarToolButtonAction->isChecked()); this->toolBarToolButtonAction->blockSignals(false); QToolButton* toolBarToolButton = new QToolButton(); toolBarToolButton->setDefaultAction(this->toolBarToolButtonAction); /* * Toolbox control at right of the tab bar */ QToolButton* overlayToolBoxToolButton = new QToolButton(); overlayToolBoxToolButton->setDefaultAction(overlayToolBoxAction); QToolButton* layersToolBoxToolButton = new QToolButton(); layersToolBoxToolButton->setDefaultAction(layersToolBoxAction); /* * Make all tool buttons the same height */ WuQtUtilities::matchWidgetHeights(helpDialogToolButton, informationDialogToolButton, identifyDialogToolButton, sceneDialogToolButton, toolBarToolButton, overlayToolBoxToolButton, layersToolBoxToolButton); /* * Tab bar and controls at far right side of toolbar */ this->tabBarWidget = new QWidget(); QHBoxLayout* tabBarLayout = new QHBoxLayout(this->tabBarWidget); WuQtUtilities::setLayoutSpacingAndMargins(tabBarLayout, 2, 1); tabBarLayout->addWidget(this->tabBar, 100); tabBarLayout->addWidget(helpDialogToolButton); tabBarLayout->addWidget(informationDialogToolButton); tabBarLayout->addWidget(identifyDialogToolButton); tabBarLayout->addWidget(sceneDialogToolButton); tabBarLayout->addWidget(toolBarToolButton); tabBarLayout->addWidget(overlayToolBoxToolButton); tabBarLayout->addWidget(layersToolBoxToolButton); /* * Create the toolbar's widgets. */ this->viewWidget = this->createViewWidget(); this->orientationWidget = this->createOrientationWidget(); this->chartAxesWidget = createChartAxesWidget(); this->chartAttributesWidget = createChartAttributesWidget(); this->chartTypeWidget = createChartTypeWidget(); this->wholeBrainSurfaceOptionsWidget = this->createWholeBrainSurfaceOptionsWidget(); this->volumeIndicesWidget = this->createVolumeIndicesWidget(); this->modeWidget = this->createModeWidget(); this->windowWidget = this->createTabOptionsWidget(windowAspectRatioLockedAction, tabAspectRatioLockedAction); this->singleSurfaceSelectionWidget = this->createSingleSurfaceOptionsWidget(); this->surfaceMontageSelectionWidget = this->createSurfaceMontageOptionsWidget(); m_clippingOptionsWidget = createClippingOptionsWidget(); this->volumeMontageWidget = this->createVolumeMontageWidget(); this->volumePlaneWidget = this->createVolumePlaneWidget(); /* * Layout the toolbar's widgets. */ m_toolbarWidget = new QWidget(); this->toolbarWidgetLayout = new QHBoxLayout(m_toolbarWidget); WuQtUtilities::setLayoutSpacingAndMargins(this->toolbarWidgetLayout, 2, 1); this->toolbarWidgetLayout->addWidget(this->viewWidget, 0, Qt::AlignLeft); this->toolbarWidgetLayout->addWidget(this->orientationWidget, 0, Qt::AlignLeft); this->toolbarWidgetLayout->addWidget(this->wholeBrainSurfaceOptionsWidget, 0, Qt::AlignLeft); this->toolbarWidgetLayout->addWidget(this->singleSurfaceSelectionWidget, 0, Qt::AlignLeft); this->toolbarWidgetLayout->addWidget(this->surfaceMontageSelectionWidget, 0, Qt::AlignLeft); this->toolbarWidgetLayout->addWidget(this->volumePlaneWidget, 0, Qt::AlignLeft); this->toolbarWidgetLayout->addWidget(this->volumeMontageWidget, 0, Qt::AlignLeft); this->toolbarWidgetLayout->addWidget(this->volumeIndicesWidget, 0, Qt::AlignLeft); this->toolbarWidgetLayout->addWidget(this->chartTypeWidget, 0, Qt::AlignLeft); this->toolbarWidgetLayout->addWidget(this->chartAxesWidget, 0, Qt::AlignLeft); this->toolbarWidgetLayout->addWidget(this->chartAttributesWidget, 0, Qt::AlignLeft); this->toolbarWidgetLayout->addWidget(this->modeWidget, 0, Qt::AlignLeft); this->toolbarWidgetLayout->addWidget(m_clippingOptionsWidget, 0, Qt::AlignLeft); this->toolbarWidgetLayout->addWidget(this->windowWidget, 0, Qt::AlignLeft); this->toolbarWidgetLayout->addStretch(); /* * Widget below toolbar for user input mode mouse controls */ this->userInputControlsWidgetLayout = new QHBoxLayout(); this->userInputControlsWidgetLayout->addSpacing(5); WuQtUtilities::setLayoutSpacingAndMargins(this->userInputControlsWidgetLayout, 0, 0); this->userInputControlsWidget = new QWidget(); QVBoxLayout* userInputLayout = new QVBoxLayout(this->userInputControlsWidget); WuQtUtilities::setLayoutSpacingAndMargins(userInputLayout, 2, 0); userInputLayout->addWidget(WuQtUtilities::createHorizontalLineWidget()); userInputLayout->addLayout(this->userInputControlsWidgetLayout); userInputControlsWidgetActiveInputWidget = NULL; /* * Arrange the tabbar and the toolbar vertically. */ QWidget* w = new QWidget(); QVBoxLayout* layout = new QVBoxLayout(w); WuQtUtilities::setLayoutSpacingAndMargins(layout, 1, 0); layout->addWidget(this->tabBarWidget); layout->addWidget(m_toolbarWidget); layout->addWidget(this->userInputControlsWidget); this->addWidget(w); if (initialBrowserTabContent != NULL) { this->addNewTabWithContent(initialBrowserTabContent); } else { AString errorMessage; this->createNewTab(errorMessage); } this->updateToolBar(); this->isContructorFinished = 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_CREATE_TABS); EventManager::get()->addEventListener(this, EventTypeEnum::EVENT_UPDATE_YOKED_WINDOWS); EventManager::get()->addEventListener(this, EventTypeEnum::EVENT_USER_INTERFACE_UPDATE); } /** * Destructor. */ BrainBrowserWindowToolBar::~BrainBrowserWindowToolBar() { this->isDestructionInProgress = true; if (this->viewOrientationLeftIcon != NULL) { delete this->viewOrientationLeftIcon; this->viewOrientationLeftIcon = NULL; } if (this->viewOrientationRightIcon != NULL) { delete this->viewOrientationRightIcon; this->viewOrientationRightIcon = NULL; } if (this->viewOrientationAnteriorIcon != NULL) { delete this->viewOrientationAnteriorIcon; this->viewOrientationAnteriorIcon = NULL; } if (this->viewOrientationPosteriorIcon != NULL) { delete this->viewOrientationPosteriorIcon; this->viewOrientationPosteriorIcon = NULL; } if (this->viewOrientationDorsalIcon != NULL) { delete this->viewOrientationDorsalIcon; this->viewOrientationDorsalIcon = NULL; } if (this->viewOrientationVentralIcon != NULL) { delete this->viewOrientationVentralIcon; this->viewOrientationVentralIcon = NULL; } if (this->viewOrientationLeftLateralIcon != NULL) { delete this->viewOrientationLeftLateralIcon; this->viewOrientationLeftLateralIcon = NULL; } if (this->viewOrientationLeftMedialIcon != NULL) { delete this->viewOrientationLeftMedialIcon; this->viewOrientationLeftMedialIcon = NULL; } if (this->viewOrientationRightLateralIcon != NULL) { delete this->viewOrientationRightLateralIcon; this->viewOrientationRightLateralIcon = NULL; } if (this->viewOrientationRightMedialIcon != NULL) { delete this->viewOrientationRightMedialIcon; this->viewOrientationRightMedialIcon = NULL; } EventManager::get()->removeAllEventsFromListener(this); this->viewWidgetGroup->clear(); this->orientationWidgetGroup->clear(); this->wholeBrainSurfaceOptionsWidgetGroup->clear(); this->modeWidgetGroup->clear(); this->singleSurfaceSelectionWidgetGroup->clear(); for (int i = (this->tabBar->count() - 1); i >= 0; i--) { this->tabClosed(i); } this->isDestructionInProgress = false; } /** * Create a new tab. * @param errorMessage * If fails to create new tab, it will contain a message * describing the error. * @return * Pointer to content of new tab or NULL if unable to * create the new tab. */ BrowserTabContent* BrainBrowserWindowToolBar::createNewTab(AString& errorMessage) { errorMessage = ""; EventBrowserTabNew newTabEvent; EventManager::get()->sendEvent(newTabEvent.getPointer()); if (newTabEvent.isError()) { errorMessage = newTabEvent.getErrorMessage(); return NULL; } BrowserTabContent* tabContent = newTabEvent.getBrowserTab(); Brain* brain = GuiManager::get()->getBrain(); tabContent->getVolumeSurfaceOutlineSet()->selectSurfacesAfterSpecFileLoaded(brain, false); this->addNewTabWithContent(tabContent); return tabContent; } /** * Add a new tab and clone the content of the given tab. * @param browserTabContentToBeCloned * Tab Content that is to be cloned into the new tab. */ void BrainBrowserWindowToolBar::addNewTabCloneContent(BrowserTabContent* browserTabContentToBeCloned) { /* * Wait cursor */ CursorDisplayScoped cursor; cursor.showWaitCursor(); AString errorMessage; BrowserTabContent* tabContent = this->createNewTab(errorMessage); if (tabContent == NULL) { cursor.restoreCursor(); QMessageBox::critical(this, "", errorMessage); return; } if (browserTabContentToBeCloned != NULL) { /* * New tab is clone of tab that was displayed when the new tab was created. */ tabContent->cloneBrowserTabContent(browserTabContentToBeCloned); } this->updateToolBar(); EventManager::get()->sendEvent(EventSurfaceColoringInvalidate().getPointer()); EventManager::get()->sendEvent(EventUserInterfaceUpdate().setWindowIndex(this->browserWindowIndex).getPointer()); EventManager::get()->sendEvent(EventGraphicsUpdateOneWindow(this->browserWindowIndex).getPointer()); } /** * Add a new tab containing the given content. * @param tabContent * Content for new tab. */ void BrainBrowserWindowToolBar::addNewTabWithContent(BrowserTabContent* tabContent) { addOrInsertNewTab(tabContent, -1); } /** * Adds a new tab. */ void BrainBrowserWindowToolBar::addNewTab() { /* * Wait cursor */ CursorDisplayScoped cursor; cursor.showWaitCursor(); AString errorMessage; BrowserTabContent* tabContent = this->createNewTab(errorMessage); if (tabContent == NULL) { cursor.restoreCursor(); QMessageBox::critical(this, "", errorMessage); return; } this->updateToolBar(); EventManager::get()->sendEvent(EventSurfaceColoringInvalidate().getPointer()); EventManager::get()->sendEvent(EventUserInterfaceUpdate().setWindowIndex(this->browserWindowIndex).getPointer()); EventManager::get()->sendEvent(EventGraphicsUpdateOneWindow(this->browserWindowIndex).getPointer()); } /** * 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. * * @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. */ void BrainBrowserWindowToolBar::addOrInsertNewTab(BrowserTabContent* browserTabContent, const int32_t insertAtIndex) { 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) { 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; } } if (insertIndex >= numTabs) { newTabIndex = this->tabBar->addTab("NewTab"); } else { this->tabBar->insertTab(insertIndex, "NewTab"); newTabIndex = insertIndex; } } } 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); this->tabBar->blockSignals(false); } /** * Shows/hides the toolbar. */ void BrainBrowserWindowToolBar::showHideToolBar(bool showIt) { if (this->isContructorFinished) { m_toolbarWidget->setVisible(showIt); } this->toolBarToolButtonAction->blockSignals(true); if (showIt) { this->toolBarToolButtonAction->setToolTip("Hide Toolbar"); this->toolBarToolButtonAction->setChecked(true); } else { this->toolBarToolButtonAction->setToolTip("Show Toolbar"); this->toolBarToolButtonAction->setChecked(false); } this->toolBarToolButtonAction->blockSignals(false); } /** * Add the default tabs after loading a spec file. */ void BrainBrowserWindowToolBar::addDefaultTabsAfterLoadingSpecFile() { EventModelGetAll eventAllModels; EventManager::get()->sendEvent(eventAllModels.getPointer()); const std::vector allModels = eventAllModels.getModels(); ModelSurface* leftSurfaceModel = NULL; ModelSurface* leftSurfaceInflated = NULL; ModelSurface* leftSurfaceVeryInflated = NULL; int32_t leftSurfaceTypeCode = 1000000; ModelSurface* rightSurfaceModel = NULL; ModelSurface* rightSurfaceInflated = NULL; ModelSurface* rightSurfaceVeryInflated = NULL; int32_t rightSurfaceTypeCode = 1000000; ModelSurface* cerebellumSurfaceModel = NULL; ModelSurface* cerebellumSurfaceInflated = NULL; ModelSurface* cerebellumSurfaceVeryInflated = NULL; int32_t cerebellumSurfaceTypeCode = 1000000; ModelChart* chartModel = NULL; ModelSurfaceMontage* surfaceMontageModel = NULL; ModelVolume* volumeModel = NULL; ModelWholeBrain* wholeBrainModel = NULL; 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; } } else if (dynamic_cast(*iter) != NULL) { surfaceMontageModel = dynamic_cast(*iter); } else if (dynamic_cast(*iter) != NULL) { volumeModel = dynamic_cast(*iter); } else if (dynamic_cast(*iter) != NULL) { wholeBrainModel = dynamic_cast(*iter); } else if (dynamic_cast(*iter)) { chartModel = dynamic_cast(*iter); } else { CaretAssertMessage(0, AString("Unknow controller type: ") + (*iter)->getNameForGUI(true)); } } if (cerebellumSurfaceInflated != NULL) { cerebellumSurfaceModel = cerebellumSurfaceInflated; } else if (cerebellumSurfaceVeryInflated != NULL) { cerebellumSurfaceModel = cerebellumSurfaceVeryInflated; } if (leftSurfaceInflated != NULL) { leftSurfaceModel = leftSurfaceInflated; } else if (leftSurfaceVeryInflated != NULL) { leftSurfaceModel = leftSurfaceVeryInflated; } if (rightSurfaceInflated != NULL) { rightSurfaceModel = rightSurfaceInflated; } else if (rightSurfaceVeryInflated != NULL) { rightSurfaceModel = rightSurfaceVeryInflated; } int32_t numberOfTabsNeeded = 0; if (surfaceMontageModel != NULL) { numberOfTabsNeeded++; } if (volumeModel != NULL) { numberOfTabsNeeded++; } if (wholeBrainModel != NULL) { numberOfTabsNeeded++; } if (chartModel != NULL) { numberOfTabsNeeded++; } if (leftSurfaceModel != NULL) { numberOfTabsNeeded++; } if (rightSurfaceModel != NULL) { numberOfTabsNeeded++; } if (cerebellumSurfaceModel != NULL) { numberOfTabsNeeded++; } const int32_t numberOfTabsToAdd = numberOfTabsNeeded - this->tabBar->count(); for (int32_t i = 0; i < numberOfTabsToAdd; i++) { AString errorMessage; this->createNewTab(errorMessage); } int32_t tabIndex = 0; tabIndex = loadIntoTab(tabIndex, surfaceMontageModel); tabIndex = loadIntoTab(tabIndex, volumeModel); tabIndex = loadIntoTab(tabIndex, wholeBrainModel); tabIndex = loadIntoTab(tabIndex, chartModel); tabIndex = loadIntoTab(tabIndex, leftSurfaceModel); tabIndex = loadIntoTab(tabIndex, rightSurfaceModel); tabIndex = loadIntoTab(tabIndex, cerebellumSurfaceModel); const int numTabs = this->tabBar->count(); if (numTabs > 0) { this->tabBar->setCurrentIndex(0); Brain* brain = GuiManager::get()->getBrain(); for (int32_t i = 0; i < numTabs; i++) { BrowserTabContent* btc = this->getTabContentFromTab(i); if (btc != NULL) { btc->getVolumeSurfaceOutlineSet()->selectSurfacesAfterSpecFileLoaded(brain, true); } } /* * Set the default tab to whole brain, if present */ int32_t surfaceTabIndex = -1; int32_t montageTabIndex = -1; int32_t wholeBrainTabIndex = -1; int32_t volumeTabIndex = -1; for (int32_t i = 0; i < numTabs; i++) { BrowserTabContent* btc = getTabContentFromTab(i); if (btc != NULL) { switch (btc->getSelectedModelType()) { case ModelTypeEnum::MODEL_TYPE_INVALID: break; case ModelTypeEnum::MODEL_TYPE_SURFACE: if (surfaceTabIndex < 0) { surfaceTabIndex = i; } break; case ModelTypeEnum::MODEL_TYPE_SURFACE_MONTAGE: if (montageTabIndex < 0) { montageTabIndex = i; } break; case ModelTypeEnum::MODEL_TYPE_VOLUME_SLICES: if (volumeTabIndex < 0) { volumeTabIndex = i; } break; case ModelTypeEnum::MODEL_TYPE_WHOLE_BRAIN: if (wholeBrainTabIndex < 0) { wholeBrainTabIndex = i; } break; case ModelTypeEnum::MODEL_TYPE_CHART: break; } } } int32_t defaultTabIndex = 0; if (montageTabIndex >= 0) { defaultTabIndex = montageTabIndex; } else if (surfaceTabIndex >= 0) { defaultTabIndex = surfaceTabIndex; } else if (volumeTabIndex >= 0) { defaultTabIndex = volumeTabIndex; } else if (wholeBrainTabIndex >= 0) { defaultTabIndex = wholeBrainTabIndex; } this->tabBar->setCurrentIndex(defaultTabIndex); } } /** * Load a controller into the tab with the given index. * @param tabIndexIn * Index of tab into which controller is loaded. A * new tab will be created, if needed. * @param controller * Model that is to be displayed in the tab. If * NULL, this method does nothing. * @return * Index of next tab after controller is displayed. * If the input controller was NULL, the returned * value is identical to the input tab index. */ int32_t BrainBrowserWindowToolBar::loadIntoTab(const int32_t tabIndexIn, Model* controller) { if (tabIndexIn < 0) { return -1; } int32_t tabIndex = tabIndexIn; if (controller != NULL) { if (tabIndex >= this->tabBar->count()) { AString errorMessage; if (this->createNewTab(errorMessage) == NULL) { return -1; } tabIndex = this->tabBar->count() - 1; } void* p = this->tabBar->tabData(tabIndex).value(); BrowserTabContent* btc = (BrowserTabContent*)p; btc->setSelectedModelType(controller->getModelType()); ModelSurface* surfaceModel = dynamic_cast(controller); if (surfaceModel != NULL) { btc->getSurfaceModelSelector()->setSelectedStructure(surfaceModel->getSurface()->getStructure()); btc->getSurfaceModelSelector()->setSelectedSurfaceModel(surfaceModel); btc->setSelectedModelType(ModelTypeEnum::MODEL_TYPE_SURFACE); } this->updateTabName(tabIndex); tabIndex++; } return tabIndex; } /** * Move all but the current tab to new windows. */ void BrainBrowserWindowToolBar::moveTabsToNewWindows() { int32_t numTabs = this->tabBar->count(); if (numTabs > 1) { const int32_t currentIndex = this->tabBar->currentIndex(); QWidget* lastParent = this->parentWidget(); if (lastParent == NULL) { lastParent = this; } for (int32_t i = (numTabs - 1); i >= 0; i--) { if (i != currentIndex) { void* p = this->tabBar->tabData(i).value(); BrowserTabContent* btc = (BrowserTabContent*)p; EventBrowserWindowNew eventNewWindow(lastParent, btc); EventManager::get()->sendEvent(eventNewWindow.getPointer()); if (eventNewWindow.isError()) { QMessageBox::critical(this, "", eventNewWindow.getErrorMessage()); break; } else { lastParent = eventNewWindow.getBrowserWindowCreated(); this->tabBar->setTabData(i, qVariantFromValue((void*)NULL)); this->tabClosed(i); } } } } EventManager::get()->sendEvent(EventUserInterfaceUpdate().getPointer()); } /** * Remove and return all tabs from this toolbar. * After this the window containing this toolbar * will contain no tabs! * * @param allTabContent * Will contain the content from the tabs upon return. */ void BrainBrowserWindowToolBar::removeAndReturnAllTabs(std::vector& allTabContent) { allTabContent.clear(); int32_t numTabs = this->tabBar->count(); for (int32_t i = (numTabs - 1); i >= 0; i--) { void* p = this->tabBar->tabData(i).value(); BrowserTabContent* btc = (BrowserTabContent*)p; if (btc != NULL) { allTabContent.push_back(btc); } this->tabBar->setTabData(i, qVariantFromValue((void*)NULL)); this->tabClosed(i); } } /** * Get content of all tabs * * @param allTabContent * Will contain the content from the tabs upon return. */ void BrainBrowserWindowToolBar::getAllTabContent(std::vector& allTabContent) const { allTabContent.clear(); int32_t numTabs = this->tabBar->count(); for (int32_t i = 0; i < numTabs; i++) { void* p = this->tabBar->tabData(i).value(); BrowserTabContent* btc = (BrowserTabContent*)p; if (btc != NULL) { allTabContent.push_back(btc); } } } /** * Remove the tab that contains the given tab content. * Note: The tab content is NOT deleted and the caller must * either delete it or move it into a window. * After this method completes, the windowo may contain no tabs. * * @param browserTabContent */ void BrainBrowserWindowToolBar::removeTabWithContent(BrowserTabContent* browserTabContent) { int32_t numTabs = this->tabBar->count(); for (int32_t i = 0; i < numTabs; i++) { void* p = this->tabBar->tabData(i).value(); BrowserTabContent* btc = (BrowserTabContent*)p; if (btc == browserTabContent) { this->tabBar->setTabData(i, qVariantFromValue((void*)NULL)); this->tabClosed(i); if (this->tabBar->count() <= 0) { EventManager::get()->removeAllEventsFromListener(this); } break; } } } /** * Select the next tab. */ void BrainBrowserWindowToolBar::nextTab() { int32_t numTabs = this->tabBar->count(); if (numTabs > 1) { int32_t tabIndex = this->tabBar->currentIndex(); tabIndex++; if (tabIndex >= numTabs) { tabIndex = 0; } this->tabBar->setCurrentIndex(tabIndex); } } /** * Select the previous tab. */ void BrainBrowserWindowToolBar::previousTab() { int32_t numTabs = this->tabBar->count(); if (numTabs > 1) { int32_t tabIndex = this->tabBar->currentIndex(); tabIndex--; if (tabIndex < 0) { tabIndex = numTabs - 1; } this->tabBar->setCurrentIndex(tabIndex); } } /** * Rename the current tab. */ void BrainBrowserWindowToolBar::renameTab() { const int tabIndex = this->tabBar->currentIndex(); if (tabIndex >= 0) { void* p = this->tabBar->tabData(tabIndex).value(); BrowserTabContent* btc = (BrowserTabContent*)p; AString currentName = btc->getUserName(); bool ok = false; AString newName = QInputDialog::getText(this, "Set Tab Name", "New Name (empty to reset)", QLineEdit::Normal, currentName, &ok); if (ok) { btc->setUserName(newName); this->updateTabName(tabIndex); } } } /** * Update the name of the tab at the given index. The * name is obtained from the tabs browser content. * * @param tabIndex * Index of tab. */ void BrainBrowserWindowToolBar::updateTabName(const int32_t tabIndex) { int32_t tabIndexForUpdate = tabIndex; if (tabIndexForUpdate < 0) { tabIndexForUpdate = this->tabBar->currentIndex(); } void* p = this->tabBar->tabData(tabIndexForUpdate).value(); AString newName = ""; BrowserTabContent* btc = (BrowserTabContent*)p; if (btc != NULL) { newName = btc->getName(); } this->tabBar->setTabText(tabIndexForUpdate, newName); } /** * Close the selected tab. This method is typically * called by the BrowswerWindow's File Menu. */ void BrainBrowserWindowToolBar::closeSelectedTab() { tabCloseSelected(this->tabBar->currentIndex()); // const int tabIndex = this->tabBar->currentIndex(); // if (this->tabBar->count() > 1) { // this->tabClosed(tabIndex); // } } /** * Called when the selected tab is changed. * @param index * Index of selected tab. */ void BrainBrowserWindowToolBar::selectedTabChanged(int indx) { this->updateTabName(indx); this->updateToolBar(); this->updateToolBox(); emit viewedModelChanged(); BrainBrowserWindow* browserWindow = GuiManager::get()->getBrowserWindowByWindowIndex(this->browserWindowIndex); if (browserWindow != NULL) { 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())); } } } this->updateGraphicsWindow(); } /** * Reset the tab index for tab tile highlighting. */ void BrainBrowserWindowToolBar::resetTabIndexForTileTabsHighlighting() { m_tabIndexForTileTabsHighlighting = -1; EventManager::get()->sendEvent(EventGraphicsUpdateOneWindow(this->browserWindowIndex).getPointer()); } /** * Gets called when user closes a tab by clicking the tab's 'X'. * * @param tabIndex * Index of the tab that was closed. */ void BrainBrowserWindowToolBar::tabCloseSelected(int tabIndex) { if ((tabIndex >= 0) && (tabIndex < this->tabBar->count())) { tabClosed(tabIndex); } this->updateGraphicsWindow(); } /** * Close a tab. * * @param tabIndex * Index of tab that was closed. */ void BrainBrowserWindowToolBar::tabClosed(int tabIndex) { CaretAssertArrayIndex(this-tabBar->tabData(), this->tabBar->count(), tabIndex); this->removeTab(tabIndex); if (this->isDestructionInProgress == false) { this->updateToolBar(); this->updateToolBox(); emit viewedModelChanged(); } } /** * Called when a tab has been moved. * * @param from * Previous location of tab. * @param to * New location of tab. */ void BrainBrowserWindowToolBar::tabMoved(int /*from*/, int /*to*/) { this->updateGraphicsWindow(); } /** * Remove the tab at the given index. * @param index */ void BrainBrowserWindowToolBar::removeTab(int tabIndex) { CaretAssertArrayIndex(this-tabBar->tabData(), this->tabBar->count(), tabIndex); void* p = this->tabBar->tabData(tabIndex).value(); if (p != NULL) { BrowserTabContent* btc = (BrowserTabContent*)p; EventBrowserTabDelete deleteTabEvent(btc); EventManager::get()->sendEvent(deleteTabEvent.getPointer()); } this->tabBar->blockSignals(true); this->tabBar->removeTab(tabIndex); this->tabBar->blockSignals(false); const int numOpenTabs = this->tabBar->count(); this->tabBar->setTabsClosable(numOpenTabs > 1); } /** * Update the toolbar. */ void BrainBrowserWindowToolBar::updateToolBar() { if (this->isDestructionInProgress) { return; } /* * If there are no models, close all but the first tab. */ EventModelGetAll getAllModelsEvent; EventManager::get()->sendEvent(getAllModelsEvent.getPointer()); if (getAllModelsEvent.getFirstModel() == NULL) { for (int i = (this->tabBar->count() - 1); i >= 0; i--) { this->removeTab(i); } } this->incrementUpdateCounter(__CARET_FUNCTION_NAME__); BrowserTabContent* browserTabContent = this->getTabContentFromSelectedTab(); const ModelTypeEnum::Enum viewModel = this->updateViewWidget(browserTabContent); bool showOrientationWidget = false; bool showWholeBrainSurfaceOptionsWidget = false; bool showSingleSurfaceOptionsWidget = false; bool showSurfaceMontageOptionsWidget = false; bool showClippingOptionsWidget = true; bool showVolumeIndicesWidget = false; bool showVolumePlaneWidget = false; bool showVolumeMontageWidget = false; bool showChartAxesWidget = false; bool showChartTypeWidget = false; bool showChartAttributesWidget = false; bool showModeWidget = true; bool showWindowWidget = true; switch (viewModel) { case ModelTypeEnum::MODEL_TYPE_INVALID: break; case ModelTypeEnum::MODEL_TYPE_SURFACE: showOrientationWidget = true; showSingleSurfaceOptionsWidget = true; break; case ModelTypeEnum::MODEL_TYPE_SURFACE_MONTAGE: showOrientationWidget = true; showSurfaceMontageOptionsWidget = true; break; case ModelTypeEnum::MODEL_TYPE_VOLUME_SLICES: showVolumeIndicesWidget = true; showVolumePlaneWidget = true; showVolumeMontageWidget = true; break; case ModelTypeEnum::MODEL_TYPE_WHOLE_BRAIN: showOrientationWidget = true; showWholeBrainSurfaceOptionsWidget = true; showVolumeIndicesWidget = true; break; case ModelTypeEnum::MODEL_TYPE_CHART: { showChartTypeWidget = true; showClippingOptionsWidget = false; ModelChart* modelChart = browserTabContent->getDisplayedChartModel(); if (modelChart != NULL) { switch (modelChart->getSelectedChartDataType(browserTabContent->getTabNumber())) { case ChartDataTypeEnum::CHART_DATA_TYPE_INVALID: break; case ChartDataTypeEnum::CHART_DATA_TYPE_MATRIX_LAYER: showChartAttributesWidget = true; break; case ChartDataTypeEnum::CHART_DATA_TYPE_MATRIX_SERIES: showChartAttributesWidget = true; break; case ChartDataTypeEnum::CHART_DATA_TYPE_LINE_TIME_SERIES: showChartAxesWidget = true; showChartAttributesWidget = true; break; case ChartDataTypeEnum::CHART_DATA_TYPE_LINE_FREQUENCY_SERIES: showChartAxesWidget = true; showChartAttributesWidget = true; break; case ChartDataTypeEnum::CHART_DATA_TYPE_LINE_DATA_SERIES: showChartAxesWidget = true; showChartAttributesWidget = true; break; } } } break; } /* * Need to turn off display of all widgets, * otherwise, the toolbar width may be overly * expanded with empty space as other widgets * are turned on and off. */ this->orientationWidget->setVisible(false); this->wholeBrainSurfaceOptionsWidget->setVisible(false); this->singleSurfaceSelectionWidget->setVisible(false); this->surfaceMontageSelectionWidget->setVisible(false); this->chartTypeWidget->setVisible(false); this->chartAxesWidget->setVisible(false); this->chartAttributesWidget->setVisible(false); this->volumeIndicesWidget->setVisible(false); this->volumePlaneWidget->setVisible(false); this->volumeMontageWidget->setVisible(false); this->modeWidget->setVisible(false); this->windowWidget->setVisible(false); m_clippingOptionsWidget->setVisible(false); this->orientationWidget->setVisible(showOrientationWidget); 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->volumeIndicesWidget->setVisible(showVolumeIndicesWidget); this->volumePlaneWidget->setVisible(showVolumePlaneWidget); this->volumeMontageWidget->setVisible(showVolumeMontageWidget); this->modeWidget->setVisible(showModeWidget); m_clippingOptionsWidget->setVisible(showClippingOptionsWidget); this->windowWidget->setVisible(showWindowWidget); updateToolBarComponents(browserTabContent); this->decrementUpdateCounter(__CARET_FUNCTION_NAME__); if (this->updateCounter != 0) { CaretLogSevere("Update counter is non-zero at end of updateToolBar()"); } this->updateTabName(-1); BrainBrowserWindow* browserWindow = GuiManager::get()->getBrowserWindowByWindowIndex(this->browserWindowIndex); if (browserWindow != NULL) { if (browserWindow->isFullScreen()) { this->setVisible(false); } else { this->setVisible(true); } } /* * Try to avoid resizing of Toolbar widget (view, orientation, etc) * height when models are changed. Let it grow but never shrink. */ if (isVisible()) { if (m_toolbarWidget->isVisible()) { const int sizeHintHeight = m_toolbarWidget->sizeHint().height(); const int actualHeight = m_toolbarWidget->height(); if (sizeHintHeight >= actualHeight) { m_toolbarWidget->setFixedHeight(sizeHintHeight); } } } } /** * Update the components in the toolbar with the given tab content. * * @param browserTabContent * The current tab content. */ void BrainBrowserWindowToolBar::updateToolBarComponents(BrowserTabContent* browserTabContent) { if (browserTabContent != NULL) { this->updateOrientationWidget(browserTabContent); this->updateWholeBrainSurfaceOptionsWidget(browserTabContent); this->updateVolumeIndicesWidget(browserTabContent); this->updateSingleSurfaceOptionsWidget(browserTabContent); this->updateSurfaceMontageOptionsWidget(browserTabContent); this->updateChartTypeWidget(browserTabContent); this->updateChartAxesWidget(browserTabContent); this->updateChartAttributesWidget(browserTabContent); this->updateVolumeMontageWidget(browserTabContent); this->updateVolumePlaneWidget(browserTabContent); this->updateModeWidget(browserTabContent); this->updateTabOptionsWidget(browserTabContent); this->updateClippingOptionsWidget(browserTabContent); } } /** * Create the view widget. * * @return The view widget. */ QWidget* BrainBrowserWindowToolBar::createViewWidget() { this->viewModeChartRadioButton = 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); layout->addWidget(this->viewModeSurfaceMontageRadioButton); layout->addWidget(this->viewModeVolumeRadioButton); layout->addWidget(this->viewModeWholeBrainRadioButton); layout->addWidget(this->viewModeSurfaceRadioButton); layout->addStretch(); QButtonGroup* viewModeRadioButtonGroup = new QButtonGroup(this); viewModeRadioButtonGroup->addButton(this->viewModeChartRadioButton); viewModeRadioButtonGroup->addButton(this->viewModeSurfaceRadioButton); viewModeRadioButtonGroup->addButton(this->viewModeSurfaceMontageRadioButton); viewModeRadioButtonGroup->addButton(this->viewModeVolumeRadioButton); viewModeRadioButtonGroup->addButton(this->viewModeWholeBrainRadioButton); QObject::connect(viewModeRadioButtonGroup, SIGNAL(buttonClicked(QAbstractButton*)), this, SLOT(viewModeRadioButtonClicked(QAbstractButton*))); this->viewWidgetGroup = new WuQWidgetObjectGroup(this); this->viewWidgetGroup->add(this->viewModeChartRadioButton); this->viewWidgetGroup->add(this->viewModeSurfaceRadioButton); this->viewWidgetGroup->add(this->viewModeSurfaceMontageRadioButton); this->viewWidgetGroup->add(this->viewModeVolumeRadioButton); this->viewWidgetGroup->add(this->viewModeWholeBrainRadioButton); QWidget* w = this->createToolWidget("View", widget, WIDGET_PLACEMENT_NONE, WIDGET_PLACEMENT_TOP, 0); return w; } /** * Update the view widget. * * @param browserTabContent * Content in the tab. * @return * An enumerated type indicating the type of model being viewed. */ ModelTypeEnum::Enum BrainBrowserWindowToolBar::updateViewWidget(BrowserTabContent* browserTabContent) { ModelTypeEnum::Enum modelType = ModelTypeEnum::MODEL_TYPE_INVALID; if (browserTabContent != NULL) { modelType = browserTabContent->getSelectedModelType(); } this->incrementUpdateCounter(__CARET_FUNCTION_NAME__); this->viewWidgetGroup->blockAllSignals(true); /* * Enable buttons for valid types */ if (browserTabContent != NULL) { this->viewModeSurfaceRadioButton->setEnabled(browserTabContent->isSurfaceModelValid()); this->viewModeSurfaceMontageRadioButton->setEnabled(browserTabContent->isSurfaceMontageModelValid()); this->viewModeVolumeRadioButton->setEnabled(browserTabContent->isVolumeSliceModelValid()); this->viewModeWholeBrainRadioButton->setEnabled(browserTabContent->isWholeBrainModelValid()); this->viewModeChartRadioButton->setEnabled(browserTabContent->isChartModelValid()); } switch (modelType) { case ModelTypeEnum::MODEL_TYPE_INVALID: break; case ModelTypeEnum::MODEL_TYPE_SURFACE: this->viewModeSurfaceRadioButton->setChecked(true); break; case ModelTypeEnum::MODEL_TYPE_SURFACE_MONTAGE: this->viewModeSurfaceMontageRadioButton->setChecked(true); break; case ModelTypeEnum::MODEL_TYPE_VOLUME_SLICES: this->viewModeVolumeRadioButton->setChecked(true); break; case ModelTypeEnum::MODEL_TYPE_WHOLE_BRAIN: this->viewModeWholeBrainRadioButton->setChecked(true); break; case ModelTypeEnum::MODEL_TYPE_CHART: this->viewModeChartRadioButton->setChecked(true); break; } this->viewWidgetGroup->blockAllSignals(false); this->decrementUpdateCounter(__CARET_FUNCTION_NAME__); return modelType; } /** * Create the orientation widget. * * @return The orientation widget. */ QWidget* BrainBrowserWindowToolBar::createOrientationWidget() { this->viewOrientationLeftIcon = WuQtUtilities::loadIcon(":/ToolBar/view-left.png"); this->viewOrientationRightIcon = WuQtUtilities::loadIcon(":/ToolBar/view-right.png"); this->viewOrientationAnteriorIcon = WuQtUtilities::loadIcon(":/ToolBar/view-anterior.png"); this->viewOrientationPosteriorIcon = WuQtUtilities::loadIcon(":/ToolBar/view-posterior.png"); this->viewOrientationDorsalIcon = WuQtUtilities::loadIcon(":/ToolBar/view-dorsal.png"); this->viewOrientationVentralIcon = WuQtUtilities::loadIcon(":/ToolBar/view-ventral.png"); this->viewOrientationLeftLateralIcon = WuQtUtilities::loadIcon(":/ToolBar/view-left-lateral.png"); this->viewOrientationLeftMedialIcon = WuQtUtilities::loadIcon(":/ToolBar/view-left-medial.png"); this->viewOrientationRightLateralIcon = WuQtUtilities::loadIcon(":/ToolBar/view-right-lateral.png"); this->viewOrientationRightMedialIcon = WuQtUtilities::loadIcon(":/ToolBar/view-right-medial.png"); this->orientationLeftOrLateralToolButtonAction = WuQtUtilities::createAction("L", "View from a LEFT perspective", this, this, SLOT(orientationLeftOrLateralToolButtonTriggered(bool))); if (this->viewOrientationLeftIcon != NULL) { this->orientationLeftOrLateralToolButtonAction->setIcon(*this->viewOrientationLeftIcon); } else { this->orientationLeftOrLateralToolButtonAction->setIconText("L"); } this->orientationRightOrMedialToolButtonAction = WuQtUtilities::createAction("R", "View from a RIGHT perspective", this, this, SLOT(orientationRightOrMedialToolButtonTriggered(bool))); if (this->viewOrientationRightIcon != NULL) { this->orientationRightOrMedialToolButtonAction->setIcon(*this->viewOrientationRightIcon); } else { this->orientationRightOrMedialToolButtonAction->setIconText("R"); } this->orientationAnteriorToolButtonAction = WuQtUtilities::createAction("A", "View from an ANTERIOR perspective", this, this, SLOT(orientationAnteriorToolButtonTriggered(bool))); if (this->viewOrientationAnteriorIcon != NULL) { this->orientationAnteriorToolButtonAction->setIcon(*this->viewOrientationAnteriorIcon); } else { this->orientationAnteriorToolButtonAction->setIconText("A"); } this->orientationPosteriorToolButtonAction = WuQtUtilities::createAction("P", "View from a POSTERIOR perspective", this, this, SLOT(orientationPosteriorToolButtonTriggered(bool))); if (this->viewOrientationPosteriorIcon != NULL) { this->orientationPosteriorToolButtonAction->setIcon(*this->viewOrientationPosteriorIcon); } else { this->orientationPosteriorToolButtonAction->setIconText("P"); } this->orientationDorsalToolButtonAction = WuQtUtilities::createAction("D", "View from a DORSAL perspective", this, this, SLOT(orientationDorsalToolButtonTriggered(bool))); if (this->viewOrientationDorsalIcon != NULL) { this->orientationDorsalToolButtonAction->setIcon(*this->viewOrientationDorsalIcon); } else { this->orientationDorsalToolButtonAction->setIconText("D"); } this->orientationVentralToolButtonAction = WuQtUtilities::createAction("V", "View from a VENTRAL perspective", this, this, SLOT(orientationVentralToolButtonTriggered(bool))); if (this->viewOrientationVentralIcon != NULL) { this->orientationVentralToolButtonAction->setIcon(*this->viewOrientationVentralIcon); } else { this->orientationVentralToolButtonAction->setIconText("V"); } this->orientationLateralMedialToolButtonAction = WuQtUtilities::createAction("LM", "View from a Lateral/Medial perspective", this, this, SLOT(orientationLateralMedialToolButtonTriggered(bool))); this->orientationDorsalVentralToolButtonAction = WuQtUtilities::createAction("DV", "View from a Dorsal/Ventral perspective", this, this, SLOT(orientationDorsalVentralToolButtonTriggered(bool))); this->orientationAnteriorPosteriorToolButtonAction = WuQtUtilities::createAction("AP", "View from a Anterior/Posterior perspective", this, this, SLOT(orientationAnteriorPosteriorToolButtonTriggered(bool))); this->orientationResetToolButtonAction = WuQtUtilities::createAction("R\nE\nS\nE\nT", "Reset the view to dorsal and remove any panning or zooming", this, this, SLOT(orientationResetToolButtonTriggered(bool))); this->orientationLeftOrLateralToolButton = new QToolButton(); this->orientationLeftOrLateralToolButton->setDefaultAction(this->orientationLeftOrLateralToolButtonAction); this->orientationRightOrMedialToolButton = new QToolButton(); this->orientationRightOrMedialToolButton->setDefaultAction(this->orientationRightOrMedialToolButtonAction); this->orientationAnteriorToolButton = new QToolButton(); this->orientationAnteriorToolButton->setDefaultAction(this->orientationAnteriorToolButtonAction); this->orientationPosteriorToolButton = new QToolButton(); this->orientationPosteriorToolButton->setDefaultAction(this->orientationPosteriorToolButtonAction); this->orientationDorsalToolButton = new QToolButton(); this->orientationDorsalToolButton->setDefaultAction(this->orientationDorsalToolButtonAction); this->orientationVentralToolButton = new QToolButton(); this->orientationVentralToolButton->setDefaultAction(this->orientationVentralToolButtonAction); this->orientationLateralMedialToolButton = new QToolButton(); this->orientationLateralMedialToolButton->setDefaultAction(this->orientationLateralMedialToolButtonAction); this->orientationDorsalVentralToolButton = new QToolButton(); this->orientationDorsalVentralToolButton->setDefaultAction(this->orientationDorsalVentralToolButtonAction); this->orientationAnteriorPosteriorToolButton = new QToolButton(); this->orientationAnteriorPosteriorToolButton->setDefaultAction(this->orientationAnteriorPosteriorToolButtonAction); WuQtUtilities::matchWidgetWidths(this->orientationLateralMedialToolButton, this->orientationDorsalVentralToolButton, this->orientationAnteriorPosteriorToolButton); QToolButton* orientationResetToolButton = new QToolButton(); orientationResetToolButton->setDefaultAction(this->orientationResetToolButtonAction); this->orientationCustomViewSelectToolButton = new QToolButton(); this->orientationCustomViewSelectToolButton->setDefaultAction(this->customViewAction); this->orientationCustomViewSelectToolButton->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Fixed); QGridLayout* buttonGridLayout = new QGridLayout(); buttonGridLayout->setColumnStretch(3, 100); WuQtUtilities::setLayoutSpacingAndMargins(buttonGridLayout, 0, 0); buttonGridLayout->addWidget(this->orientationLeftOrLateralToolButton, 0, 0); buttonGridLayout->addWidget(this->orientationRightOrMedialToolButton, 0, 1); buttonGridLayout->addWidget(this->orientationDorsalToolButton, 1, 0); buttonGridLayout->addWidget(this->orientationVentralToolButton, 1, 1); buttonGridLayout->addWidget(this->orientationAnteriorToolButton, 2, 0); buttonGridLayout->addWidget(this->orientationPosteriorToolButton, 2, 1); buttonGridLayout->addWidget(this->orientationLateralMedialToolButton, 0, 2); buttonGridLayout->addWidget(this->orientationDorsalVentralToolButton, 1, 2); buttonGridLayout->addWidget(this->orientationAnteriorPosteriorToolButton, 2, 2); buttonGridLayout->addWidget(this->orientationCustomViewSelectToolButton, 3, 0, 1, 5, Qt::AlignHCenter); buttonGridLayout->addWidget(orientationResetToolButton, 0, 4, 3, 1); QWidget* w = new QWidget(); QVBoxLayout* layout = new QVBoxLayout(w); WuQtUtilities::setLayoutSpacingAndMargins(layout, 0, 0); layout->addLayout(buttonGridLayout); this->orientationWidgetGroup = new WuQWidgetObjectGroup(this); this->orientationWidgetGroup->add(this->orientationLeftOrLateralToolButtonAction); this->orientationWidgetGroup->add(this->orientationRightOrMedialToolButtonAction); this->orientationWidgetGroup->add(this->orientationAnteriorToolButtonAction); this->orientationWidgetGroup->add(this->orientationPosteriorToolButtonAction); this->orientationWidgetGroup->add(this->orientationDorsalToolButtonAction); this->orientationWidgetGroup->add(this->orientationVentralToolButtonAction); this->orientationWidgetGroup->add(this->orientationResetToolButtonAction); QWidget* orientWidget = this->createToolWidget("Orientation", w, WIDGET_PLACEMENT_LEFT, WIDGET_PLACEMENT_TOP, 0); orientWidget->setVisible(false); return orientWidget; } /** * Update the orientation widget. * * @param browserTabContent * Content of browser tab. */ void BrainBrowserWindowToolBar::updateOrientationWidget(BrowserTabContent* browserTabContent) { if (this->orientationWidget->isHidden()) { return; } this->incrementUpdateCounter(__CARET_FUNCTION_NAME__); const int32_t tabIndex = browserTabContent->getTabNumber(); this->orientationWidgetGroup->blockAllSignals(true); const Model* mdc = this->getDisplayedModel(); if (mdc != NULL) { const ModelSurface* mdcs = dynamic_cast(mdc); const ModelSurfaceMontage* mdcsm = dynamic_cast(mdc); const ModelVolume* mdcv = dynamic_cast(mdc); const ModelWholeBrain* mdcwb = dynamic_cast(mdc); bool rightFlag = false; bool leftFlag = false; bool leftRightFlag = false; bool enableDualViewOrientationButtons = false; bool showDualViewOrientationButtons = false; bool showSingleViewOrientationButtons = false; if (mdcs != NULL) { const Surface* surface = mdcs->getSurface(); const StructureEnum::Enum structure = surface->getStructure(); if (StructureEnum::isLeft(structure)) { leftFlag = true; } else if (StructureEnum::isRight(structure)) { rightFlag = true; } else { leftRightFlag = true; } showSingleViewOrientationButtons = true; } else if (mdcsm != NULL) { AString latMedLeftRightText = "LM"; AString latMedLeftRightToolTipText = "View from a Lateral/Medial perspective"; switch (mdcsm->getSelectedConfigurationType(tabIndex)) { case SurfaceMontageConfigurationTypeEnum::CEREBELLAR_CORTEX_CONFIGURATION: latMedLeftRightText = "LR"; latMedLeftRightToolTipText = "View from a Right/Left Perspective"; enableDualViewOrientationButtons = true; break; case SurfaceMontageConfigurationTypeEnum::CEREBRAL_CORTEX_CONFIGURATION: enableDualViewOrientationButtons = true; break; case SurfaceMontageConfigurationTypeEnum::FLAT_CONFIGURATION: break; } this->orientationLateralMedialToolButtonAction->setText(latMedLeftRightText); WuQtUtilities::setToolTipAndStatusTip(this->orientationLateralMedialToolButtonAction, latMedLeftRightToolTipText); showDualViewOrientationButtons = true; } else if (mdcv != NULL) { /* nothing */ } else if (mdcwb != NULL) { leftRightFlag = true; showSingleViewOrientationButtons = true; } else { CaretAssertMessage(0, "Unknown model display controller type"); } if (rightFlag || leftFlag) { if (rightFlag) { if (this->viewOrientationRightLateralIcon != NULL) { this->orientationLeftOrLateralToolButtonAction->setIcon(*this->viewOrientationRightLateralIcon); } else { this->orientationLeftOrLateralToolButtonAction->setIconText("L"); } if (this->viewOrientationRightMedialIcon != NULL) { this->orientationRightOrMedialToolButtonAction->setIcon(*this->viewOrientationRightMedialIcon); } else { this->orientationRightOrMedialToolButtonAction->setIconText("M"); } } else if (leftFlag) { if (this->viewOrientationLeftLateralIcon != NULL) { this->orientationLeftOrLateralToolButtonAction->setIcon(*this->viewOrientationLeftLateralIcon); } else { this->orientationLeftOrLateralToolButtonAction->setIconText("L"); } if (this->viewOrientationLeftMedialIcon != NULL) { this->orientationRightOrMedialToolButtonAction->setIcon(*this->viewOrientationLeftMedialIcon); } else { this->orientationRightOrMedialToolButtonAction->setIconText("M"); } } WuQtUtilities::setToolTipAndStatusTip(this->orientationLeftOrLateralToolButtonAction, "View from a LATERAL perspective"); WuQtUtilities::setToolTipAndStatusTip(this->orientationRightOrMedialToolButtonAction, "View from a MEDIAL perspective"); } else if (leftRightFlag) { if (this->viewOrientationLeftIcon != NULL) { this->orientationLeftOrLateralToolButtonAction->setIcon(*this->viewOrientationLeftIcon); } else { this->orientationLeftOrLateralToolButtonAction->setIconText("L"); } if (this->viewOrientationRightIcon != NULL) { this->orientationRightOrMedialToolButtonAction->setIcon(*this->viewOrientationRightIcon); } else { this->orientationRightOrMedialToolButtonAction->setIconText("R"); } WuQtUtilities::setToolTipAndStatusTip(this->orientationLeftOrLateralToolButtonAction, "View from a LEFT perspective"); WuQtUtilities::setToolTipAndStatusTip(this->orientationRightOrMedialToolButtonAction, "View from a RIGHT perspective"); } /* * The dual view buttons are not need for a flat map montage. * However, if they are hidden, their space is not reallocated and the * Reset button remains on the right and it looks weird. So, * display them but disable them when a flat map montage. */ this->orientationLateralMedialToolButton->setVisible(showDualViewOrientationButtons); this->orientationDorsalVentralToolButton->setVisible(showDualViewOrientationButtons); this->orientationAnteriorPosteriorToolButton->setVisible(showDualViewOrientationButtons); this->orientationLateralMedialToolButton->setEnabled(enableDualViewOrientationButtons); this->orientationDorsalVentralToolButton->setEnabled(enableDualViewOrientationButtons); this->orientationAnteriorPosteriorToolButton->setEnabled(enableDualViewOrientationButtons); this->orientationLeftOrLateralToolButton->setVisible(showSingleViewOrientationButtons); this->orientationRightOrMedialToolButton->setVisible(showSingleViewOrientationButtons); this->orientationDorsalToolButton->setVisible(showSingleViewOrientationButtons); this->orientationVentralToolButton->setVisible(showSingleViewOrientationButtons); this->orientationAnteriorToolButton->setVisible(showSingleViewOrientationButtons); this->orientationPosteriorToolButton->setVisible(showSingleViewOrientationButtons); } this->orientationWidgetGroup->blockAllSignals(false); this->decrementUpdateCounter(__CARET_FUNCTION_NAME__); } /** * Create the whole brain surface options widget. * * @return The whole brain surface options widget. */ QWidget* BrainBrowserWindowToolBar::createWholeBrainSurfaceOptionsWidget() { this->wholeBrainSurfaceTypeComboBox = WuQFactory::newComboBox(); WuQtUtilities::setToolTipAndStatusTip(this->wholeBrainSurfaceTypeComboBox, "Select the geometric type of surface for display"); QObject::connect(this->wholeBrainSurfaceTypeComboBox, SIGNAL(currentIndexChanged(int)), this, SLOT(wholeBrainSurfaceTypeComboBoxIndexChanged(int))); /* * Left */ this->wholeBrainSurfaceLeftCheckBox = new QCheckBox(" "); WuQtUtilities::setToolTipAndStatusTip(this->wholeBrainSurfaceLeftCheckBox, "Enable/Disable display of the left cortical surface"); QObject::connect(this->wholeBrainSurfaceLeftCheckBox, SIGNAL(stateChanged(int)), this, SLOT(wholeBrainSurfaceLeftCheckBoxStateChanged(int))); QAction* leftSurfaceAction = WuQtUtilities::createAction("Left", "Select the whole brain left surface", this, this, SLOT(wholeBrainSurfaceLeftToolButtonTriggered(bool))); QToolButton* wholeBrainLeftSurfaceToolButton = new QToolButton(); wholeBrainLeftSurfaceToolButton->setDefaultAction(leftSurfaceAction); /* * Right */ this->wholeBrainSurfaceRightCheckBox = new QCheckBox(" "); WuQtUtilities::setToolTipAndStatusTip(this->wholeBrainSurfaceRightCheckBox, "Enable/Disable display of the right cortical surface"); QObject::connect(this->wholeBrainSurfaceRightCheckBox, SIGNAL(stateChanged(int)), this, SLOT(wholeBrainSurfaceRightCheckBoxStateChanged(int))); QAction* rightSurfaceAction = WuQtUtilities::createAction("Right", "Select the whole brain right surface", this, this, SLOT(wholeBrainSurfaceRightToolButtonTriggered(bool))); QToolButton* wholeBrainRightSurfaceToolButton = new QToolButton(); wholeBrainRightSurfaceToolButton->setDefaultAction(rightSurfaceAction); /* * Cerebellum */ this->wholeBrainSurfaceCerebellumCheckBox = new QCheckBox(" "); WuQtUtilities::setToolTipAndStatusTip(this->wholeBrainSurfaceCerebellumCheckBox, "Enable/Disable display of the cerebellum surface"); QObject::connect(this->wholeBrainSurfaceCerebellumCheckBox, SIGNAL(stateChanged(int)), this, SLOT(wholeBrainSurfaceCerebellumCheckBoxStateChanged(int))); QAction* cerebellumSurfaceAction = WuQtUtilities::createAction("Cerebellum", "Select the whole brain cerebellum surface", this, this, SLOT(wholeBrainSurfaceCerebellumToolButtonTriggered(bool))); QToolButton* wholeBrainCerebellumSurfaceToolButton = new QToolButton(); wholeBrainCerebellumSurfaceToolButton->setDefaultAction(cerebellumSurfaceAction); /* * Left/Right separation */ const int separationSpinngerWidth = 48; this->wholeBrainSurfaceSeparationLeftRightSpinBox = WuQFactory::newDoubleSpinBox(); this->wholeBrainSurfaceSeparationLeftRightSpinBox->setDecimals(0); this->wholeBrainSurfaceSeparationLeftRightSpinBox->setFixedWidth(separationSpinngerWidth); this->wholeBrainSurfaceSeparationLeftRightSpinBox->setMinimum(-100000.0); this->wholeBrainSurfaceSeparationLeftRightSpinBox->setMaximum(100000.0); WuQtUtilities::setToolTipAndStatusTip(this->wholeBrainSurfaceSeparationLeftRightSpinBox, "Adjust the separation of the left and right cortical surfaces"); QObject::connect(this->wholeBrainSurfaceSeparationLeftRightSpinBox, SIGNAL(valueChanged(double)), this, SLOT(wholeBrainSurfaceSeparationLeftRightSpinBoxValueChanged(double))); /* * Cerebellum separation */ this->wholeBrainSurfaceSeparationCerebellumSpinBox = WuQFactory::newDoubleSpinBox(); this->wholeBrainSurfaceSeparationCerebellumSpinBox->setDecimals(0); this->wholeBrainSurfaceSeparationCerebellumSpinBox->setFixedWidth(separationSpinngerWidth); this->wholeBrainSurfaceSeparationCerebellumSpinBox->setMinimum(-100000.0); this->wholeBrainSurfaceSeparationCerebellumSpinBox->setMaximum(100000.0); WuQtUtilities::setToolTipAndStatusTip(this->wholeBrainSurfaceSeparationCerebellumSpinBox, "Adjust the separation of the cerebellum from the left and right cortical surfaces"); QObject::connect(this->wholeBrainSurfaceSeparationCerebellumSpinBox, SIGNAL(valueChanged(double)), this, SLOT(wholeBrainSurfaceSeparationCerebellumSpinBoxSelected(double))); QLabel* columnTwoSpaceLabel = new QLabel(" "); wholeBrainLeftSurfaceToolButton->setText("L"); wholeBrainRightSurfaceToolButton->setText("R"); wholeBrainCerebellumSurfaceToolButton->setText("C"); bool originalFlag = false; QGridLayout* gridLayout = new QGridLayout(); if (originalFlag) { gridLayout->setVerticalSpacing(2); gridLayout->setHorizontalSpacing(2); gridLayout->addWidget(this->wholeBrainSurfaceTypeComboBox, 0, 0, 1, 6); gridLayout->addWidget(this->wholeBrainSurfaceLeftCheckBox, 1, 0); gridLayout->addWidget(wholeBrainLeftSurfaceToolButton, 1, 1); gridLayout->addWidget(columnTwoSpaceLabel, 1, 2); gridLayout->addWidget(this->wholeBrainSurfaceRightCheckBox, 1, 3); gridLayout->addWidget(wholeBrainRightSurfaceToolButton, 1, 4); gridLayout->addWidget(this->wholeBrainSurfaceSeparationLeftRightSpinBox, 1, 5); gridLayout->addWidget(this->wholeBrainSurfaceCerebellumCheckBox, 2, 0); gridLayout->addWidget(wholeBrainCerebellumSurfaceToolButton, 2, 1); gridLayout->addWidget(this->wholeBrainSurfaceSeparationCerebellumSpinBox, 2, 5); } else { gridLayout->setVerticalSpacing(2); gridLayout->setHorizontalSpacing(2); gridLayout->addWidget(this->wholeBrainSurfaceTypeComboBox, 0, 0, 1, 6); gridLayout->addWidget(this->wholeBrainSurfaceLeftCheckBox, 1, 0); gridLayout->addWidget(wholeBrainLeftSurfaceToolButton, 1, 1); gridLayout->addWidget(this->wholeBrainSurfaceRightCheckBox, 2, 0); gridLayout->addWidget(wholeBrainRightSurfaceToolButton, 2, 1); gridLayout->addWidget(this->wholeBrainSurfaceCerebellumCheckBox, 3, 0); gridLayout->addWidget(wholeBrainCerebellumSurfaceToolButton, 3, 1); gridLayout->addWidget(this->wholeBrainSurfaceSeparationLeftRightSpinBox, 1, 2, 2, 1); gridLayout->addWidget(this->wholeBrainSurfaceSeparationCerebellumSpinBox, 3, 2); } QWidget* widget = new QWidget(); QVBoxLayout* layout = new QVBoxLayout(widget); WuQtUtilities::setLayoutSpacingAndMargins(layout, 0, 0); layout->addLayout(gridLayout); this->wholeBrainSurfaceOptionsWidgetGroup = new WuQWidgetObjectGroup(this); this->wholeBrainSurfaceOptionsWidgetGroup->add(this->wholeBrainSurfaceTypeComboBox); this->wholeBrainSurfaceOptionsWidgetGroup->add(this->wholeBrainSurfaceLeftCheckBox); this->wholeBrainSurfaceOptionsWidgetGroup->add(wholeBrainLeftSurfaceToolButton); this->wholeBrainSurfaceOptionsWidgetGroup->add(this->wholeBrainSurfaceRightCheckBox); this->wholeBrainSurfaceOptionsWidgetGroup->add(wholeBrainRightSurfaceToolButton); this->wholeBrainSurfaceOptionsWidgetGroup->add(this->wholeBrainSurfaceCerebellumCheckBox); this->wholeBrainSurfaceOptionsWidgetGroup->add(wholeBrainCerebellumSurfaceToolButton); this->wholeBrainSurfaceOptionsWidgetGroup->add(this->wholeBrainSurfaceSeparationLeftRightSpinBox); this->wholeBrainSurfaceOptionsWidgetGroup->add(this->wholeBrainSurfaceSeparationCerebellumSpinBox); QWidget* w = this->createToolWidget("Surface Viewing", widget, WIDGET_PLACEMENT_LEFT, WIDGET_PLACEMENT_TOP, 0); w->setVisible(false); return w; } /** * Update the whole brain surface options widget. * * @param browserTabContent * Content of browser tab. */ void BrainBrowserWindowToolBar::updateWholeBrainSurfaceOptionsWidget(BrowserTabContent* browserTabContent) { if (this->wholeBrainSurfaceOptionsWidget->isHidden()) { return; } this->incrementUpdateCounter(__CARET_FUNCTION_NAME__); ModelWholeBrain* wholeBrainModel = browserTabContent->getDisplayedWholeBrainModel(); if (wholeBrainModel != NULL) { const int32_t tabNumber = browserTabContent->getTabNumber(); this->wholeBrainSurfaceOptionsWidgetGroup->blockAllSignals(true); std::vector availableSurfaceTypes; wholeBrainModel->getAvailableSurfaceTypes(availableSurfaceTypes); const SurfaceTypeEnum::Enum selectedSurfaceType = wholeBrainModel->getSelectedSurfaceType(tabNumber); int32_t defaultIndex = 0; this->wholeBrainSurfaceTypeComboBox->clear(); int32_t numSurfaceTypes = static_cast(availableSurfaceTypes.size()); for (int32_t i = 0; i < numSurfaceTypes; i++) { const SurfaceTypeEnum::Enum st = availableSurfaceTypes[i]; if (st == selectedSurfaceType) { defaultIndex = this->wholeBrainSurfaceTypeComboBox->count(); } const AString name = SurfaceTypeEnum::toGuiName(st); const int integerCode = SurfaceTypeEnum::toIntegerCode(st); this->wholeBrainSurfaceTypeComboBox->addItem(name, integerCode); } if (defaultIndex < this->wholeBrainSurfaceTypeComboBox->count()) { this->wholeBrainSurfaceTypeComboBox->setCurrentIndex(defaultIndex); } this->wholeBrainSurfaceLeftCheckBox->setChecked(browserTabContent->isWholeBrainLeftEnabled()); this->wholeBrainSurfaceRightCheckBox->setChecked(browserTabContent->isWholeBrainRightEnabled()); this->wholeBrainSurfaceCerebellumCheckBox->setChecked(browserTabContent->isWholeBrainCerebellumEnabled()); this->wholeBrainSurfaceSeparationLeftRightSpinBox->setValue(browserTabContent->getWholeBrainLeftRightSeparation()); this->wholeBrainSurfaceSeparationCerebellumSpinBox->setValue(browserTabContent->getWholeBrainCerebellumSeparation()); this->wholeBrainSurfaceOptionsWidgetGroup->blockAllSignals(false); } this->decrementUpdateCounter(__CARET_FUNCTION_NAME__); } /** * Create the volume indices widget. * * @return The volume indices widget. */ QWidget* BrainBrowserWindowToolBar::createVolumeIndicesWidget() { m_sliceSelectionComponent = new BrainBrowserWindowToolBarSliceSelection(this); QWidget* w = this->createToolWidget("Slice Indices/Coords", m_sliceSelectionComponent, WIDGET_PLACEMENT_LEFT, WIDGET_PLACEMENT_TOP, 0); w->setVisible(false); return w; } /** * Update the volume indices widget. * * @param browserTabContent * Content of browser tab. */ void BrainBrowserWindowToolBar::updateVolumeIndicesWidget(BrowserTabContent* browserTabContent) { if (this->volumeIndicesWidget->isHidden()) { return; } m_sliceSelectionComponent->updateContent(browserTabContent); } /** * Create the mode widget. * * @return The mode widget. */ QWidget* BrainBrowserWindowToolBar::createModeWidget() { /* * Annotations */ this->modeInputModeAnnotationsAction = WuQtUtilities::createAction("Annotate", "Perform annotate operations with mouse", this); this->modeInputModeAnnotationsAction->setCheckable(true); QToolButton* inputModeAnnotationsToolButton = new QToolButton(); inputModeAnnotationsToolButton->setDefaultAction(this->modeInputModeAnnotationsAction); /* * Borders */ this->modeInputModeBordersAction = WuQtUtilities::createAction("Border", "Perform border operations with mouse", this); this->modeInputModeBordersAction->setCheckable(true); QToolButton* inputModeBordersToolButton = new QToolButton(); inputModeBordersToolButton->setDefaultAction(this->modeInputModeBordersAction); /* * Foci */ this->modeInputModeFociAction = WuQtUtilities::createAction("Foci", "Perform foci operations with mouse", this); this->modeInputModeFociAction->setCheckable(true); QToolButton* inputModeFociToolButton = new QToolButton(); inputModeFociToolButton->setDefaultAction(this->modeInputModeFociAction); /* * Image */ this->modeInputModeImageAction = WuQtUtilities::createAction("Image", "Edit Image Control Points", this); this->modeInputModeImageAction->setCheckable(true); QToolButton* inputModeImageToolButton = new QToolButton(); inputModeImageToolButton->setDefaultAction(this->modeInputModeImageAction); /* * Volume Edit */ this->modeInputVolumeEditAction = WuQtUtilities::createAction("Volume", "Edit volume voxels", this); this->modeInputVolumeEditAction->setCheckable(true); QToolButton* inputModeVolumeEditButton = new QToolButton(); inputModeVolumeEditButton->setDefaultAction(this->modeInputVolumeEditAction); /* * View Mode */ this->modeInputModeViewAction = WuQtUtilities::createAction("View", "Perform viewing operations with mouse\n" "\n" "Identify: Click left mouse button (might cause rotation)\n" #ifdef CARET_OS_MACOSX "Identify: Click left mouse button while keyboard shift and apple keys are down (prevents rotation)\n" #else // CARET_OS_MACOSX "Identify: Click left mouse button while keyboard shift and controls keys are down (prevents rotation)\n" #endif // CARET_OS_MACOSX "Pan: Move mouse with left mouse button down and keyboard shift key down\n" "Rotate: Move mouse with left mouse button down\n" #ifdef CARET_OS_MACOSX "Zoom: Move mouse with left mouse button down and keyboard apple key down", #else // CARET_OS_MACOSX "Zoom: Move mouse with left mouse button down and keyboard control key down", #endif // CARET_OS_MACOSX this); this->modeInputModeViewAction->setCheckable(true); QToolButton* inputModeViewToolButton = new QToolButton(); inputModeViewToolButton->setDefaultAction(this->modeInputModeViewAction); 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()); /* * Layout for input modes */ QWidget* inputModeWidget = new QWidget(); QGridLayout* inputModeLayout = new QGridLayout(inputModeWidget); int modeRow = 0; WuQtUtilities::setLayoutSpacingAndMargins(inputModeLayout, 2, 2); inputModeLayout->addWidget(inputModeAnnotationsToolButton, modeRow, 0, 1, 2, Qt::AlignHCenter); modeRow++; inputModeLayout->addWidget(inputModeBordersToolButton, modeRow, 0, 1, 2, Qt::AlignHCenter); modeRow++; inputModeLayout->addWidget(inputModeFociToolButton, modeRow, 0); inputModeLayout->addWidget(inputModeImageToolButton, modeRow, 1); modeRow++; inputModeLayout->addWidget(inputModeViewToolButton, modeRow, 0, 1, 2, Qt::AlignHCenter); modeRow++; inputModeLayout->addWidget(inputModeVolumeEditButton, modeRow, 0, 1, 2, Qt::AlignHCenter); modeRow++; this->modeInputModeActionGroup = new QActionGroup(this); this->modeInputModeActionGroup->addAction(this->modeInputModeAnnotationsAction); this->modeInputModeActionGroup->addAction(this->modeInputModeBordersAction); this->modeInputModeActionGroup->addAction(this->modeInputModeFociAction); this->modeInputModeActionGroup->addAction(this->modeInputModeImageAction); this->modeInputModeActionGroup->addAction(this->modeInputModeViewAction); this->modeInputModeActionGroup->addAction(this->modeInputVolumeEditAction); QObject::connect(this->modeInputModeActionGroup, SIGNAL(triggered(QAction*)), this, SLOT(modeInputModeActionTriggered(QAction*))); this->modeInputModeActionGroup->setExclusive(true); QWidget* widget = new QWidget(); QVBoxLayout* layout = new QVBoxLayout(widget); WuQtUtilities::setLayoutSpacingAndMargins(layout, 0, 0); layout->addWidget(inputModeWidget, 0, Qt::AlignHCenter); layout->addStretch(); this->modeWidgetGroup = new WuQWidgetObjectGroup(this); this->modeWidgetGroup->add(this->modeInputModeActionGroup); QWidget* w = this->createToolWidget("Mode", widget, WIDGET_PLACEMENT_LEFT, WIDGET_PLACEMENT_NONE, 0); return w; } /** * Called when a tools input mode button is clicked. * @param action * Action of tool button that was clicked. */ void BrainBrowserWindowToolBar::modeInputModeActionTriggered(QAction* action) { BrowserTabContent* tabContent = this->getTabContentFromSelectedTab(); if (tabContent == NULL) { return; } UserInputModeAbstract::UserInputMode inputMode = UserInputModeAbstract::INVALID; if (action == this->modeInputModeAnnotationsAction) { inputMode = UserInputModeAbstract::ANNOTATIONS; } else if (action == this->modeInputModeBordersAction) { inputMode = UserInputModeAbstract::BORDERS; /* * If borders are not displayed, display them */ DisplayPropertiesBorders* dpb = GuiManager::get()->getBrain()->getDisplayPropertiesBorders(); const int32_t browserTabIndex = tabContent->getTabNumber(); const DisplayGroupEnum::Enum displayGroup = dpb->getDisplayGroupForTab(browserTabIndex); if (dpb->isDisplayed(displayGroup, browserTabIndex) == false) { dpb->setDisplayed(displayGroup, browserTabIndex, true); this->updateUserInterface(); this->updateGraphicsWindow(); } } else if (action == this->modeInputModeFociAction) { inputMode = UserInputModeAbstract::FOCI; } else if (action == this->modeInputModeImageAction) { inputMode = UserInputModeAbstract::IMAGE; } else if (action == this->modeInputVolumeEditAction) { inputMode = UserInputModeAbstract::VOLUME_EDIT; } else if (action == this->modeInputModeViewAction) { inputMode = UserInputModeAbstract::VIEW; } else { CaretAssertMessage(0, "Tools input mode action is invalid, new action added???"); } EventManager::get()->sendEvent(EventGetOrSetUserInputModeProcessor(this->browserWindowIndex, inputMode).getPointer()); EventManager::get()->sendEvent(EventUserInterfaceUpdate().getPointer()); this->updateModeWidget(tabContent); this->updateDisplayedModeUserInputWidget(); } /** * Update the tools widget. * * @param browserTabContent * Content of browser tab. */ void BrainBrowserWindowToolBar::updateModeWidget(BrowserTabContent* /*browserTabContent*/) { if (this->modeWidget->isHidden()) { return; } this->incrementUpdateCounter(__CARET_FUNCTION_NAME__); this->modeWidgetGroup->blockAllSignals(true); EventGetOrSetUserInputModeProcessor getInputModeEvent(this->browserWindowIndex); EventManager::get()->sendEvent(getInputModeEvent.getPointer()); switch (getInputModeEvent.getUserInputMode()) { case UserInputModeAbstract::INVALID: /* may get here when program is exiting and widgets are being destroyed */ break; case UserInputModeAbstract::ANNOTATIONS: this->modeInputModeAnnotationsAction->setChecked(true); break; case UserInputModeAbstract::BORDERS: this->modeInputModeBordersAction->setChecked(true); break; case UserInputModeAbstract::FOCI: this->modeInputModeFociAction->setChecked(true); break; case UserInputModeAbstract::IMAGE: this->modeInputModeImageAction->setChecked(true); break; case UserInputModeAbstract::VOLUME_EDIT: this->modeInputVolumeEditAction->setChecked(true); break; case UserInputModeAbstract::VIEW: this->modeInputModeViewAction->setChecked(true); break; } this->modeWidgetGroup->blockAllSignals(false); this->updateDisplayedModeUserInputWidget(); this->decrementUpdateCounter(__CARET_FUNCTION_NAME__); } void BrainBrowserWindowToolBar::updateDisplayedModeUserInputWidget() { EventGetOrSetUserInputModeProcessor getInputModeEvent(this->browserWindowIndex); EventManager::get()->sendEvent(getInputModeEvent.getPointer()); UserInputModeAbstract* userInputProcessor = getInputModeEvent.getUserInputProcessor(); QWidget* userInputWidget = userInputProcessor->getWidgetForToolBar(); /* * If a widget is display and needs to change, * remove the old widget. */ if (this->userInputControlsWidgetActiveInputWidget != NULL) { if (userInputWidget != this->userInputControlsWidgetActiveInputWidget) { /* * Remove the current input widget: * (1) Set its visibility to false * (2) Remove the widget from the toolbar's layout * (3) Set its parent to NULL. * * Why is the parent set to NULL? * * When a widget is put into a layout, the widget is put into * a QWidgetItem (subclass of QLayoutItem). * * QLayout::removeWidget() will delete the QWidgetItem but * it does not reset the parent for the widget that was in * QWidgetItem. So the user will need to delete the widget * unless it is placed into a layout. * * After removing the widget, set the widget's parent to NULL. * As a result, when the input receiver (owner of the widget) * is deleted, it can examine the parent, and, if the parent * is NULL, it can delete the widget preventing a memory link * and a possible crash. */ this->userInputControlsWidgetActiveInputWidget->setVisible(false); this->userInputControlsWidgetLayout->removeWidget(this->userInputControlsWidgetActiveInputWidget); this->userInputControlsWidgetActiveInputWidget->setParent(NULL); this->userInputControlsWidgetActiveInputWidget = NULL; } } if (this->userInputControlsWidgetActiveInputWidget == NULL) { if (userInputWidget != NULL) { this->userInputControlsWidgetActiveInputWidget = userInputWidget; this->userInputControlsWidgetActiveInputWidget->setVisible(true); this->userInputControlsWidgetLayout->addWidget(this->userInputControlsWidgetActiveInputWidget); this->userInputControlsWidget->setVisible(true); this->userInputControlsWidgetLayout->update(); } else { this->userInputControlsWidget->setVisible(false); } } if (userInputProcessor->getUserInputMode() != UserInputModeAbstract::ANNOTATIONS) { /* * Delete all selected annotations and update graphics and UI. */ AnnotationManager* annotationManager = GuiManager::get()->getBrain()->getAnnotationManager(); annotationManager->deselectAllAnnotationsForEditing(this->browserWindowIndex); } } /** * 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. * * @return The tab options widget. */ QWidget* BrainBrowserWindowToolBar::createTabOptionsWidget(QAction* windowAspectRatioLockedAction, QAction* tabAspectRatioLockedAction) { m_tabOptionsComponent = new BrainBrowserWindowToolBarTab(this->browserWindowIndex, windowAspectRatioLockedAction, tabAspectRatioLockedAction, this); QWidget* w = this->createToolWidget("Tab", m_tabOptionsComponent, WIDGET_PLACEMENT_LEFT, WIDGET_PLACEMENT_TOP, 0); return w; } /** * Update the tab widget. * * @param browserTabContent * Content of browser tab. */ void BrainBrowserWindowToolBar::updateTabOptionsWidget(BrowserTabContent* browserTabContent) { if (this->windowWidget->isHidden()) { return; } m_tabOptionsComponent->updateContent(browserTabContent); } /** * Create the chart type widget. * * @return * Widget containing the chart options. */ QWidget* BrainBrowserWindowToolBar::createChartTypeWidget() { m_chartTypeToolBarComponent = new BrainBrowserWindowToolBarChartType(this); QWidget* w = this->createToolWidget("Chart Type", m_chartTypeToolBarComponent, 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::updateChartTypeWidget(BrowserTabContent* browserTabContent) { if (this->chartTypeWidget->isHidden()) { return; } m_chartTypeToolBarComponent->updateContent(browserTabContent); } /** * Create the chart axes widget. * * @return * Widget containing the chart axes. */ QWidget* BrainBrowserWindowToolBar::createChartAxesWidget() { m_chartAxisToolBarComponent = new BrainBrowserWindowToolBarChartAxes(this); QWidget* w = this->createToolWidget("Chart Axes", m_chartAxisToolBarComponent, 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::updateChartAxesWidget(BrowserTabContent* browserTabContent) { if (this->chartAxesWidget->isHidden()) { return; } m_chartAxisToolBarComponent->updateContent(browserTabContent); } /** * Create the chart attributes widget. * * @return * Widget containing the chart attributes. */ QWidget* BrainBrowserWindowToolBar::createChartAttributesWidget() { m_chartAttributesToolBarComponent = new BrainBrowserWindowToolBarChartAttributes(this); QWidget* w = this->createToolWidget("Chart Attributes", m_chartAttributesToolBarComponent, 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::updateChartAttributesWidget(BrowserTabContent* browserTabContent) { if (this->chartAttributesWidget->isHidden()) { return; } m_chartAttributesToolBarComponent->updateContent(browserTabContent); } /** * Create the single surface options widget. * * @return The single surface options widget. */ QWidget* BrainBrowserWindowToolBar::createSingleSurfaceOptionsWidget() { QLabel* structureSurfaceLabel = new QLabel("Brain Structure and Surface: "); this->surfaceSurfaceSelectionControl = new StructureSurfaceSelectionControl(false); QObject::connect(this->surfaceSurfaceSelectionControl, SIGNAL(selectionChanged(const StructureEnum::Enum, ModelSurface*)), this, SLOT(surfaceSelectionControlChanged(const StructureEnum::Enum, ModelSurface*))); this->surfaceSurfaceSelectionControl->setMinimumWidth(150); this->surfaceSurfaceSelectionControl->setMaximumWidth(1200); QWidget* widget = new QWidget(); QVBoxLayout* layout = new QVBoxLayout(widget); WuQtUtilities::setLayoutSpacingAndMargins(layout, 2, 2); layout->addWidget(structureSurfaceLabel); layout->addWidget(this->surfaceSurfaceSelectionControl); layout->addStretch(); this->singleSurfaceSelectionWidgetGroup = new WuQWidgetObjectGroup(this); this->singleSurfaceSelectionWidgetGroup->add(this->surfaceSurfaceSelectionControl); QWidget* w = this->createToolWidget("Selection", widget, WIDGET_PLACEMENT_LEFT, WIDGET_PLACEMENT_TOP, 100); w->setVisible(false); return w; } /** * Update the single surface options widget. * * @param browserTabContent * The active model display controller (may be NULL). */ void BrainBrowserWindowToolBar::updateSingleSurfaceOptionsWidget(BrowserTabContent* browserTabContent) { if (this->singleSurfaceSelectionWidget->isHidden()) { return; } this->incrementUpdateCounter(__CARET_FUNCTION_NAME__); this->singleSurfaceSelectionWidgetGroup->blockAllSignals(true); this->surfaceSurfaceSelectionControl->updateControl(browserTabContent->getSurfaceModelSelector()); this->singleSurfaceSelectionWidgetGroup->blockAllSignals(false); this->decrementUpdateCounter(__CARET_FUNCTION_NAME__); } /** * @return Create and return the surface montage options widget. */ QWidget* BrainBrowserWindowToolBar::createSurfaceMontageOptionsWidget() { m_surfaceMontageToolBarComponent = new BrainBrowserWindowToolBarSurfaceMontage(this); QWidget* w = this->createToolWidget("Montage Selection", m_surfaceMontageToolBarComponent, WIDGET_PLACEMENT_LEFT, WIDGET_PLACEMENT_TOP, 100); w->setVisible(false); return w; } /** * Update the surface montage options widget. * * @param browserTabContent * The active model display controller (may be NULL). */ void BrainBrowserWindowToolBar::updateSurfaceMontageOptionsWidget(BrowserTabContent* browserTabContent) { if (this->surfaceMontageSelectionWidget->isHidden()) { return; } m_surfaceMontageToolBarComponent->updateContent(browserTabContent); } /** * @return Create and return the clipping options component. */ QWidget* BrainBrowserWindowToolBar::createClippingOptionsWidget() { m_clippingToolBarComponent = new BrainBrowserWindowToolBarClipping(this->browserWindowIndex, this); QWidget* w = this->createToolWidget("Clipping", m_clippingToolBarComponent, WIDGET_PLACEMENT_LEFT, WIDGET_PLACEMENT_TOP, 100); w->setVisible(false); return w; } /** * Update the clipping options widget. * * @param browserTabContent * The active browser tab. */ void BrainBrowserWindowToolBar::updateClippingOptionsWidget(BrowserTabContent* browserTabContent) { if (m_clippingOptionsWidget->isHidden()) { return; } m_clippingToolBarComponent->updateContent(browserTabContent); } /** * Create the volume montage widget. * * @return The volume montage widget. */ QWidget* BrainBrowserWindowToolBar::createVolumeMontageWidget() { m_volumeMontageComponent = new BrainBrowserWindowToolBarVolumeMontage(this); QWidget* w = this->createToolWidget("Montage", m_volumeMontageComponent, WIDGET_PLACEMENT_LEFT, WIDGET_PLACEMENT_TOP, 0); w->setVisible(false); return w; } /** * Update the volume montage widget. * * @param browserTabContent * Content of browser tab. */ void BrainBrowserWindowToolBar::updateVolumeMontageWidget(BrowserTabContent* browserTabContent) { if (this->volumeMontageWidget->isHidden()) { return; } this->incrementUpdateCounter(__CARET_FUNCTION_NAME__); m_volumeMontageComponent->updateContent(browserTabContent); this->decrementUpdateCounter(__CARET_FUNCTION_NAME__); } /** * Create the volume plane widget. * * @return The volume plane widget. */ QWidget* BrainBrowserWindowToolBar::createVolumePlaneWidget() { m_slicePlaneComponent = new BrainBrowserWindowToolBarSlicePlane(this); QWidget* w = this->createToolWidget("Slice Plane", m_slicePlaneComponent, WIDGET_PLACEMENT_LEFT, WIDGET_PLACEMENT_TOP, 0); w->setVisible(false); return w; } /** * Update the volume plane orientation widget. * * @param browserTabContent * Content of browser tab. */ void BrainBrowserWindowToolBar::updateVolumePlaneWidget(BrowserTabContent* browserTabContent) { if (this->volumePlaneWidget->isHidden()) { return; } m_slicePlaneComponent->updateContent(browserTabContent); } /** * Create a tool widget which is a group of widgets with * a descriptive label added. * * @param name * Name for the descriptive labe. * @param childWidget * Child widget that is in the tool widget. * @param verticalBarPlacement * Where to place a vertical bar. Values other than right or * left are ignored in which case no vertical bar is displayed. * @param contentPlacement * Where to place widget which must be top or bottom. * @return The tool widget. */ QWidget* BrainBrowserWindowToolBar::createToolWidget(const QString& name, QWidget* childWidget, const WidgetPlacement verticalBarPlacement, const WidgetPlacement contentPlacement, const int /*horizontalStretching*/) { QLabel* nameLabel = new QLabel("" + name + ""); QWidget* w = new QWidget(); QGridLayout* layout = new QGridLayout(w); layout->setColumnStretch(0, 100); layout->setColumnStretch(1, 100); WuQtUtilities::setLayoutSpacingAndMargins(layout, 2, 0); switch (contentPlacement) { case WIDGET_PLACEMENT_BOTTOM: layout->setRowStretch(0, 100); layout->setRowStretch(1, 0); layout->addWidget(childWidget, 1, 0, 1, 2); break; case WIDGET_PLACEMENT_TOP: layout->setRowStretch(1, 100); layout->setRowStretch(0, 0); layout->addWidget(childWidget, 0, 0, 1, 2); break; case WIDGET_PLACEMENT_NONE: layout->setRowStretch(0, 0); layout->addWidget(childWidget, 0, 0, 1, 2); break; default: CaretAssert(0); } layout->addWidget(nameLabel, 2, 0, 1, 2, Qt::AlignHCenter); const bool addVerticalBarOnLeftSide = (verticalBarPlacement == WIDGET_PLACEMENT_LEFT); const bool addVerticalBarOnRightSide = (verticalBarPlacement == WIDGET_PLACEMENT_RIGHT); if (addVerticalBarOnLeftSide || addVerticalBarOnRightSide) { QWidget* w2 = new QWidget(); QHBoxLayout* horizLayout = new QHBoxLayout(w2); WuQtUtilities::setLayoutSpacingAndMargins(horizLayout, 0, 0); if (addVerticalBarOnLeftSide) { horizLayout->addWidget(WuQtUtilities::createVerticalLineWidget(), 0); horizLayout->addSpacing(3); } const int widgetStretchFactor = 100; horizLayout->addWidget(w, widgetStretchFactor); if (addVerticalBarOnRightSide) { horizLayout->addSpacing(3); horizLayout->addWidget(WuQtUtilities::createVerticalLineWidget(), 0); } w = w2; } return w; } /** * Update the graphics windows for the selected tab. */ void BrainBrowserWindowToolBar::updateGraphicsWindow() { EventManager::get()->sendEvent( EventGraphicsUpdateOneWindow(this->browserWindowIndex).getPointer()); } /** * Update the user-interface. */ void BrainBrowserWindowToolBar::updateUserInterface() { EventManager::get()->sendEvent(EventUserInterfaceUpdate().setWindowIndex(this->browserWindowIndex).getPointer()); } /** * Update the toolbox for the window */ void BrainBrowserWindowToolBar::updateToolBox() { EventManager::get()->sendEvent(EventUserInterfaceUpdate().setWindowIndex(this->browserWindowIndex).addToolBox().getPointer()); } /** * Called when a view mode is selected. */ void BrainBrowserWindowToolBar::viewModeRadioButtonClicked(QAbstractButton*) { CaretLogEntering(); BrowserTabContent* btc = this->getTabContentFromSelectedTab(); if (btc == NULL) { return; } if (this->viewModeChartRadioButton->isChecked()) { btc->setSelectedModelType(ModelTypeEnum::MODEL_TYPE_CHART); } else if (this->viewModeSurfaceRadioButton->isChecked()) { btc->setSelectedModelType(ModelTypeEnum::MODEL_TYPE_SURFACE); } else if (this->viewModeSurfaceMontageRadioButton->isChecked()) { btc->setSelectedModelType(ModelTypeEnum::MODEL_TYPE_SURFACE_MONTAGE); } else if (this->viewModeVolumeRadioButton->isChecked()) { btc->setSelectedModelType(ModelTypeEnum::MODEL_TYPE_VOLUME_SLICES); } else if (this->viewModeWholeBrainRadioButton->isChecked()) { btc->setSelectedModelType(ModelTypeEnum::MODEL_TYPE_WHOLE_BRAIN); } else { btc->setSelectedModelType(ModelTypeEnum::MODEL_TYPE_INVALID); } this->checkUpdateCounter(); this->updateToolBar(); this->updateTabName(-1); this->updateToolBox(); emit viewedModelChanged(); this->updateGraphicsWindow(); } /** * Called when orientation left or lateral button is pressed. */ void BrainBrowserWindowToolBar::orientationLeftOrLateralToolButtonTriggered(bool /*checked*/) { BrowserTabContent* btc = this->getTabContentFromSelectedTab(); btc->leftView(); this->updateGraphicsWindow(); this->updateOtherYokedWindows(); this->checkUpdateCounter(); } /** * Called when orientation right or medial button is pressed. */ void BrainBrowserWindowToolBar::orientationRightOrMedialToolButtonTriggered(bool /*checked*/) { BrowserTabContent* btc = this->getTabContentFromSelectedTab(); btc->rightView(); this->updateGraphicsWindow(); this->updateOtherYokedWindows(); this->checkUpdateCounter(); } /** * Called when orientation anterior button is pressed. */ void BrainBrowserWindowToolBar::orientationAnteriorToolButtonTriggered(bool /*checked*/) { BrowserTabContent* btc = this->getTabContentFromSelectedTab(); btc->anteriorView(); this->updateGraphicsWindow(); this->updateOtherYokedWindows(); this->checkUpdateCounter(); } /** * Called when orientation posterior button is pressed. */ void BrainBrowserWindowToolBar::orientationPosteriorToolButtonTriggered(bool /*checked*/) { BrowserTabContent* btc = this->getTabContentFromSelectedTab(); btc->posteriorView(); this->updateGraphicsWindow(); this->updateOtherYokedWindows(); this->checkUpdateCounter(); } /** * Called when orientation dorsal button is pressed. */ void BrainBrowserWindowToolBar::orientationDorsalToolButtonTriggered(bool /*checked*/) { BrowserTabContent* btc = this->getTabContentFromSelectedTab(); btc->dorsalView(); this->updateGraphicsWindow(); this->updateOtherYokedWindows(); this->checkUpdateCounter(); } /** * Called when orientation ventral button is pressed. */ void BrainBrowserWindowToolBar::orientationVentralToolButtonTriggered(bool /*checked*/) { BrowserTabContent* btc = this->getTabContentFromSelectedTab(); btc->ventralView(); this->updateGraphicsWindow(); this->updateOtherYokedWindows(); this->checkUpdateCounter(); } /** * Called when orientation reset button is pressed. */ void BrainBrowserWindowToolBar::orientationResetToolButtonTriggered(bool /*checked*/) { BrowserTabContent* btc = this->getTabContentFromSelectedTab(); btc->resetView(); Model* mdc = btc->getModelForDisplay(); if (mdc != NULL) { this->updateVolumeIndicesWidget(btc); this->updateGraphicsWindow(); this->updateOtherYokedWindows(); } this->checkUpdateCounter(); } /** * Called when orientation lateral/medial button is pressed. */ void BrainBrowserWindowToolBar::orientationLateralMedialToolButtonTriggered(bool /*checked*/) { BrowserTabContent* btc = this->getTabContentFromSelectedTab(); btc->leftView(); this->updateGraphicsWindow(); this->updateOtherYokedWindows(); this->checkUpdateCounter(); } /** * Called when orientation dorsal/ventral button is pressed. */ void BrainBrowserWindowToolBar::orientationDorsalVentralToolButtonTriggered(bool /*checked*/) { BrowserTabContent* btc = this->getTabContentFromSelectedTab(); btc->dorsalView(); this->updateGraphicsWindow(); this->updateOtherYokedWindows(); this->checkUpdateCounter(); } /** * Called when orientation anterior/posterior button is pressed. */ void BrainBrowserWindowToolBar::orientationAnteriorPosteriorToolButtonTriggered(bool /*checked*/) { BrowserTabContent* btc = this->getTabContentFromSelectedTab(); btc->anteriorView(); this->updateGraphicsWindow(); this->updateOtherYokedWindows(); this->checkUpdateCounter(); } /** * Called when custom view is triggered and displays Custom View Menu. */ void BrainBrowserWindowToolBar::customViewActionTriggered() { CaretPreferences* prefs = SessionManager::get()->getCaretPreferences(); prefs->readCustomViews(); const std::vector > customViewNameAndComments = prefs->getCustomViewNamesAndComments(); QMenu menu; QAction* editAction = menu.addAction("Create and Edit..."); editAction->setToolTip("Add and delete Custom Views.\n" "Edit model transformations."); const int32_t numViews = static_cast(customViewNameAndComments.size()); if (numViews > 0) { menu.addSeparator(); } for (int32_t i = 0; i < numViews; i++) { QAction* action = menu.addAction(customViewNameAndComments[i].first); action->setToolTip(WuQtUtilities::createWordWrappedToolTipText(customViewNameAndComments[i].second)); } QAction* selectedAction = menu.exec(QCursor::pos()); if (selectedAction != NULL) { if (selectedAction == editAction) { BrainBrowserWindow* bbw = GuiManager::get()->getBrowserWindowByWindowIndex(browserWindowIndex); GuiManager::get()->processShowCustomViewDialog(bbw); } else { const AString customViewName = selectedAction->text(); ModelTransform modelTransform; if (prefs->getCustomView(customViewName, modelTransform)) { BrowserTabContent* btc = this->getTabContentFromSelectedTab(); btc->setTransformationsFromModelTransform(modelTransform); this->updateGraphicsWindow(); this->updateOtherYokedWindows(); } } } } /** * Called when the whole brain surface type combo box is changed. */ void BrainBrowserWindowToolBar::wholeBrainSurfaceTypeComboBoxIndexChanged(int /*indx*/) { CaretLogEntering(); this->checkUpdateCounter(); BrowserTabContent* btc = this->getTabContentFromSelectedTab(); const int32_t tabIndex = btc->getTabNumber(); ModelWholeBrain* wholeBrainModel = btc->getDisplayedWholeBrainModel(); if (wholeBrainModel == NULL) { return; } int32_t comboBoxIndex = this->wholeBrainSurfaceTypeComboBox->currentIndex(); if (comboBoxIndex >= 0) { const int32_t integerCode = this->wholeBrainSurfaceTypeComboBox->itemData(comboBoxIndex).toInt(); bool isValid = false; const SurfaceTypeEnum::Enum surfaceType = SurfaceTypeEnum::fromIntegerCode(integerCode, &isValid); if (isValid) { wholeBrainModel->setSelectedSurfaceType(tabIndex, surfaceType); this->updateVolumeIndicesWidget(btc); /* slices may get deselected */ this->updateGraphicsWindow(); this->updateOtherYokedWindows(); } } } /** * Called when whole brain surface left check box is toggled. */ void BrainBrowserWindowToolBar::wholeBrainSurfaceLeftCheckBoxStateChanged(int /*state*/) { CaretLogEntering(); this->checkUpdateCounter(); BrowserTabContent* btc = this->getTabContentFromSelectedTab(); ModelWholeBrain* wholeBrainModel = btc->getDisplayedWholeBrainModel(); if (wholeBrainModel == NULL) { return; } btc->setWholeBrainLeftEnabled(this->wholeBrainSurfaceLeftCheckBox->isChecked()); this->updateGraphicsWindow(); this->updateOtherYokedWindows(); } /** * Called when the left surface tool button is pressed. */ void BrainBrowserWindowToolBar::wholeBrainSurfaceLeftToolButtonTriggered(bool /*checked*/) { CaretLogEntering(); this->checkUpdateCounter(); BrowserTabContent* btc = this->getTabContentFromSelectedTab(); ModelWholeBrain* wholeBrainModel = btc->getDisplayedWholeBrainModel(); if (wholeBrainModel == NULL) { return; } const int32_t tabIndex = btc->getTabNumber(); Brain* brain = GuiManager::get()->getBrain(); BrainStructure* brainStructure = brain->getBrainStructure(StructureEnum::CORTEX_LEFT, false); if (brainStructure != NULL) { std::vector surfaces; brainStructure->getSurfacesOfType(wholeBrainModel->getSelectedSurfaceType(tabIndex), surfaces); const int32_t numSurfaces = static_cast(surfaces.size()); if (numSurfaces > 0) { Surface* selectedSurface = wholeBrainModel->getSelectedSurface(StructureEnum::CORTEX_LEFT, tabIndex); QMenu menu; QActionGroup* actionGroup = new QActionGroup(&menu); actionGroup->setExclusive(true); for (int32_t i = 0; i < numSurfaces; i++) { QString name = surfaces[i]->getFileNameNoPath(); QAction* action = actionGroup->addAction(name); action->setCheckable(true); if (surfaces[i] == selectedSurface) { action->setChecked(true); } menu.addAction(action); } QAction* result = menu.exec(QCursor::pos()); if (result != NULL) { QList actionList = actionGroup->actions(); for (int32_t i = 0; i < numSurfaces; i++) { if (result == actionList.at(i)) { wholeBrainModel->setSelectedSurface(StructureEnum::CORTEX_LEFT, tabIndex, surfaces[i]); this->updateGraphicsWindow(); this->updateOtherYokedWindows(); break; } } } } } } /** * Called when the right surface tool button is pressed. */ void BrainBrowserWindowToolBar::wholeBrainSurfaceRightToolButtonTriggered(bool /*checked*/) { CaretLogEntering(); this->checkUpdateCounter(); CaretLogEntering(); this->checkUpdateCounter(); BrowserTabContent* btc = this->getTabContentFromSelectedTab(); ModelWholeBrain* wholeBrainModel = btc->getDisplayedWholeBrainModel(); if (wholeBrainModel == NULL) { return; } const int32_t tabIndex = btc->getTabNumber(); Brain* brain = GuiManager::get()->getBrain(); BrainStructure* brainStructure = brain->getBrainStructure(StructureEnum::CORTEX_RIGHT, false); if (brainStructure != NULL) { std::vector surfaces; brainStructure->getSurfacesOfType(wholeBrainModel->getSelectedSurfaceType(tabIndex), surfaces); const int32_t numSurfaces = static_cast(surfaces.size()); if (numSurfaces > 0) { Surface* selectedSurface = wholeBrainModel->getSelectedSurface(StructureEnum::CORTEX_RIGHT, tabIndex); QMenu menu; QActionGroup* actionGroup = new QActionGroup(&menu); actionGroup->setExclusive(true); for (int32_t i = 0; i < numSurfaces; i++) { QString name = surfaces[i]->getFileNameNoPath(); QAction* action = actionGroup->addAction(name); action->setCheckable(true); if (surfaces[i] == selectedSurface) { action->setChecked(true); } menu.addAction(action); } QAction* result = menu.exec(QCursor::pos()); if (result != NULL) { QList actionList = actionGroup->actions(); for (int32_t i = 0; i < numSurfaces; i++) { if (result == actionList.at(i)) { wholeBrainModel->setSelectedSurface(StructureEnum::CORTEX_RIGHT, tabIndex, surfaces[i]); this->updateGraphicsWindow(); this->updateOtherYokedWindows(); break; } } } } } } /** * Called when the cerebellum surface tool button is pressed. */ void BrainBrowserWindowToolBar::wholeBrainSurfaceCerebellumToolButtonTriggered(bool /*checked*/) { CaretLogEntering(); this->checkUpdateCounter(); CaretLogEntering(); this->checkUpdateCounter(); BrowserTabContent* btc = this->getTabContentFromSelectedTab(); ModelWholeBrain* wholeBrainModel = btc->getDisplayedWholeBrainModel(); if (wholeBrainModel == NULL) { return; } const int32_t tabIndex = btc->getTabNumber(); Brain* brain = GuiManager::get()->getBrain(); BrainStructure* brainStructure = brain->getBrainStructure(StructureEnum::CEREBELLUM, false); if (brainStructure != NULL) { std::vector surfaces; brainStructure->getSurfacesOfType(wholeBrainModel->getSelectedSurfaceType(tabIndex), surfaces); const int32_t numSurfaces = static_cast(surfaces.size()); if (numSurfaces > 0) { Surface* selectedSurface = wholeBrainModel->getSelectedSurface(StructureEnum::CEREBELLUM, tabIndex); QMenu menu; QActionGroup* actionGroup = new QActionGroup(&menu); actionGroup->setExclusive(true); for (int32_t i = 0; i < numSurfaces; i++) { QString name = surfaces[i]->getFileNameNoPath(); QAction* action = actionGroup->addAction(name); action->setCheckable(true); if (surfaces[i] == selectedSurface) { action->setChecked(true); } menu.addAction(action); } QAction* result = menu.exec(QCursor::pos()); if (result != NULL) { QList actionList = actionGroup->actions(); for (int32_t i = 0; i < numSurfaces; i++) { if (result == actionList.at(i)) { wholeBrainModel->setSelectedSurface(StructureEnum::CEREBELLUM, tabIndex, surfaces[i]); this->updateGraphicsWindow(); this->updateOtherYokedWindows(); break; } } } } } } /** * Called when whole brain surface right checkbox is toggled. */ void BrainBrowserWindowToolBar::wholeBrainSurfaceRightCheckBoxStateChanged(int /*state*/) { CaretLogEntering(); this->checkUpdateCounter(); BrowserTabContent* btc = this->getTabContentFromSelectedTab(); ModelWholeBrain* wholeBrainModel = btc->getDisplayedWholeBrainModel(); if (wholeBrainModel == NULL) { return; } btc->setWholeBrainRightEnabled(this->wholeBrainSurfaceRightCheckBox->isChecked()); this->updateGraphicsWindow(); this->updateOtherYokedWindows(); } /** * Called when whole brain cerebellum check box is toggled. */ void BrainBrowserWindowToolBar::wholeBrainSurfaceCerebellumCheckBoxStateChanged(int /*state*/) { CaretLogEntering(); this->checkUpdateCounter(); BrowserTabContent* btc = this->getTabContentFromSelectedTab(); ModelWholeBrain* wholeBrainModel = btc->getDisplayedWholeBrainModel(); if (wholeBrainModel == NULL) { return; } btc->setWholeBrainCerebellumEnabled(this->wholeBrainSurfaceCerebellumCheckBox->isChecked()); this->updateGraphicsWindow(); this->updateOtherYokedWindows(); } /** * Called when whole brain separation left/right spin box value is changed. */ void BrainBrowserWindowToolBar::wholeBrainSurfaceSeparationLeftRightSpinBoxValueChanged(double /*d*/) { CaretLogEntering(); this->checkUpdateCounter(); BrowserTabContent* btc = this->getTabContentFromSelectedTab(); ModelWholeBrain* wholeBrainModel = btc->getDisplayedWholeBrainModel(); if (wholeBrainModel == NULL) { return; } btc->setWholeBrainLeftRightSeparation(this->wholeBrainSurfaceSeparationLeftRightSpinBox->value()); this->updateGraphicsWindow(); this->updateOtherYokedWindows(); } /** * Called when whole brain left&right/cerebellum spin box value is changed. */ void BrainBrowserWindowToolBar::wholeBrainSurfaceSeparationCerebellumSpinBoxSelected(double /*d*/) { CaretLogEntering(); this->checkUpdateCounter(); BrowserTabContent* btc = this->getTabContentFromSelectedTab(); ModelWholeBrain* wholeBrainModel = btc->getDisplayedWholeBrainModel(); if (wholeBrainModel == NULL) { return; } btc->setWholeBrainCerebellumSeparation(this->wholeBrainSurfaceSeparationCerebellumSpinBox->value()); this->updateGraphicsWindow(); this->updateOtherYokedWindows(); } /** * Called when a single surface control is changed. * @param structure * Structure that is selected. * @param surfaceModel * Model that is selected. */ void BrainBrowserWindowToolBar::surfaceSelectionControlChanged( const StructureEnum::Enum structure, ModelSurface* surfaceModel) { if (surfaceModel != NULL) { BrowserTabContent* btc = this->getTabContentFromSelectedTab(); ModelSurfaceSelector* surfaceModelSelector = btc->getSurfaceModelSelector(); surfaceModelSelector->setSelectedStructure(structure); surfaceModelSelector->setSelectedSurfaceModel(surfaceModel); EventManager::get()->sendEvent(EventSurfaceColoringInvalidate().getPointer()); this->updateUserInterface(); this->updateGraphicsWindow(); } 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--; } /** * Receive events from the event manager. * * @param event * Event sent by event manager. */ void BrainBrowserWindowToolBar::receiveEvent(Event* event) { if (event->getEventType() == EventTypeEnum::EVENT_BROWSER_WINDOW_CONTENT_GET) { EventBrowserWindowContentGet* getModelEvent = dynamic_cast(event); CaretAssert(getModelEvent); if (getModelEvent->getBrowserWindowIndex() == this->browserWindowIndex) { BrainBrowserWindow* browserWindow = GuiManager::get()->getBrowserWindowByWindowIndex(this->browserWindowIndex); if (browserWindow != NULL) { 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. */ getModelEvent->setTabIndexForTileTabsHighlighting(m_tabIndexForTileTabsHighlighting); getModelEvent->setTileTabsConfiguration(browserWindow->getSelectedTileTabsConfiguration()); } else { BrowserTabContent* btc = this->getTabContentFromSelectedTab(); getModelEvent->addTabContentToDraw(btc); } getModelEvent->setSelectedBrowserTabContent(this->getTabContentFromSelectedTab()); } getModelEvent->setEventProcessed(); } } else if (event->getEventType() == EventTypeEnum::EVENT_USER_INTERFACE_UPDATE) { EventUserInterfaceUpdate* uiEvent = dynamic_cast(event); CaretAssert(uiEvent); if (uiEvent->isToolBarUpdate() && uiEvent->isUpdateForWindow(this->browserWindowIndex)) { this->updateToolBar(); uiEvent->setEventProcessed(); } } else if (event->getEventType() == EventTypeEnum::EVENT_BROWSER_WINDOW_CREATE_TABS) { EventBrowserWindowCreateTabs* tabEvent = dynamic_cast(event); CaretAssert(tabEvent); EventModelGetAll eventAllModels; EventManager::get()->sendEvent(eventAllModels.getPointer()); const bool haveModels = (eventAllModels.getModels().empty() == false); if (haveModels) { switch (tabEvent->getMode()) { case EventBrowserWindowCreateTabs::MODE_LOADED_DATA_FILE: if (tabBar->count() == 0) { AString errorMessage; createNewTab(errorMessage); if (errorMessage.isEmpty() == false) { CaretLogSevere(errorMessage); } } break; case EventBrowserWindowCreateTabs::MODE_LOADED_SPEC_FILE: this->addDefaultTabsAfterLoadingSpecFile(); break; } } tabEvent->setEventProcessed(); } else if (event->getEventType() == EventTypeEnum::EVENT_UPDATE_YOKED_WINDOWS) { EventUpdateYokedWindows* yokeUpdateEvent = dynamic_cast(event); CaretAssert(yokeUpdateEvent); BrowserTabContent* browserTabContent = getTabContentFromSelectedTab(); if (browserTabContent != NULL) { if (this->browserWindowIndex != yokeUpdateEvent->getBrowserWindowIndexThatIssuedEvent()) { if (browserTabContent->getYokingGroup() == yokeUpdateEvent->getYokingGroup()) { this->updateToolBar(); this->updateGraphicsWindow(); } } } } else if (event->getEventType() == EventTypeEnum::EVENT_BROWSER_TAB_GET_ALL_VIEWED) { EventBrowserTabGetAllViewed* viewedTabsEvent = dynamic_cast(event); CaretAssert(viewedTabsEvent); BrainBrowserWindow* browserWindow = GuiManager::get()->getBrowserWindowByWindowIndex(this->browserWindowIndex); if (browserWindow != NULL) { if (browserWindow->isTileTabsSelected()) { const int32_t numTabs = this->tabBar->count(); for (int32_t i = 0; i < numTabs; i++) { BrowserTabContent* btc = this->getTabContentFromTab(i); viewedTabsEvent->addViewedBrowserTab(btc); } } else { BrowserTabContent* btc = getTabContentFromSelectedTab(); if (btc != NULL) { viewedTabsEvent->addViewedBrowserTab(btc); } } } viewedTabsEvent->setEventProcessed(); } else { } // CaretLogFinest("Toolbar width/height: " // + AString::number(width()) // + "/" // + AString::number(height())); } /** * If this windows is yoked, issue an event to update other * windows that are using the same yoking. */ void BrainBrowserWindowToolBar::updateOtherYokedWindows() { BrowserTabContent* browserTabContent = getTabContentFromSelectedTab(); if (browserTabContent != NULL) { if (browserTabContent->isYoked()) { EventManager::get()->sendEvent(EventUpdateYokedWindows(this->browserWindowIndex, browserTabContent->getYokingGroup()).getPointer()); } } } /** * Get the content in the browser tab. * @return * Browser tab contents in the selected tab or NULL if none. */ BrowserTabContent* BrainBrowserWindowToolBar::getTabContentFromSelectedTab() { const int tabIndex = this->tabBar->currentIndex(); BrowserTabContent* btc = this->getTabContentFromTab(tabIndex); return btc; } /** * Get the content in the given browser tab * @param tabIndex * Index of tab. * @return * Browser tab contents in the selected tab or NULL if none. */ BrowserTabContent* BrainBrowserWindowToolBar::getTabContentFromTab(const int tabIndex) { if ((tabIndex >= 0) && (tabIndex < this->tabBar->count())) { void* p = this->tabBar->tabData(tabIndex).value(); BrowserTabContent* btc = (BrowserTabContent*)p; return btc; } return NULL; } /** * Get the model displayed in the selected tab. * @return * Model in the selected tab or NULL if * no model is displayed. */ Model* BrainBrowserWindowToolBar::getDisplayedModel() { Model* mdc = NULL; BrowserTabContent* btc = this->getTabContentFromSelectedTab(); if (btc != NULL) { mdc = btc->getModelForDisplay(); } return mdc; } /** * Create a scene for an instance of a class. * * @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. * * @return Pointer to SceneClass object representing the state of * this object. Under some circumstances a NULL pointer may be * returned. Caller will take ownership of returned object. */ SceneClass* BrainBrowserWindowToolBar::saveToScene(const SceneAttributes* sceneAttributes, const AString& instanceName) { SceneClass* sceneClass = new SceneClass(instanceName, "BrainBrowserWindowToolBar", 1); switch (sceneAttributes->getSceneType()) { case SceneTypeEnum::SCENE_TYPE_FULL: break; case SceneTypeEnum::SCENE_TYPE_GENERIC: break; } /* * Determine tabs in this toolbar that should be saved to scene */ const std::vector tabIndicesForScene = sceneAttributes->getIndicesOfTabsForSavingToScene(); std::vector tabIndicesToSave; const int numTabsInToolbar = this->tabBar->count(); if (numTabsInToolbar > 0) { for (int32_t i = 0; i < numTabsInToolbar; i++) { BrowserTabContent* btc = this->getTabContentFromTab(i); const int32_t tabIndex = btc->getTabNumber(); if (std::find(tabIndicesForScene.begin(), tabIndicesForScene.end(), tabIndex) != tabIndicesForScene.end()) { tabIndicesToSave.push_back(tabIndex); } } } /* * Save the tabs */ const int numTabs = static_cast(tabIndicesToSave.size()); if (numTabs > 0) { SceneIntegerArray* sceneTabIndexArray = new SceneIntegerArray("tabIndices", numTabs); for (int32_t i = 0; i < numTabs; i++) { const int32_t tabIndex = tabIndicesToSave[i]; sceneTabIndexArray->setValue(i, tabIndex); } sceneClass->addChild(sceneTabIndexArray); } /* * Add selected tab to scene */ int32_t selectedTabIndex = -1; BrowserTabContent* selectedTab = getTabContentFromSelectedTab(); if (selectedTab != NULL) { selectedTabIndex = selectedTab->getTabNumber(); } sceneClass->addInteger("selectedTabIndex", selectedTabIndex); /* * Toolbar visible */ sceneClass->addBoolean("toolBarVisible", m_toolbarWidget->isVisible()); return sceneClass; } /** * Restore the state of an instance of a class. * * @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 containing the state that was previously * saved and should be restored. */ void BrainBrowserWindowToolBar::restoreFromScene(const SceneAttributes* sceneAttributes, const SceneClass* sceneClass) { if (sceneClass == NULL) { return; } switch (sceneAttributes->getSceneType()) { case SceneTypeEnum::SCENE_TYPE_FULL: break; case SceneTypeEnum::SCENE_TYPE_GENERIC: break; } /* * Close any tabs */ const int32_t numberOfOpenTabs = this->tabBar->count(); for (int32_t iClose = (numberOfOpenTabs - 1); iClose >= 0; iClose--) { this->tabClosed(iClose); } /* * Index of selected browser tab (NOT the tabBar) */ const int32_t selectedTabIndex = sceneClass->getIntegerValue("selectedTabIndex", -1); /* * Create new tabs */ int32_t defaultTabBarIndex = 0; const ScenePrimitiveArray* sceneTabIndexArray = sceneClass->getPrimitiveArray("tabIndices"); if (sceneTabIndexArray != NULL) { const int32_t numValidTabs = sceneTabIndexArray->getNumberOfArrayElements(); for (int32_t iTab = 0; iTab < numValidTabs; iTab++) { const int32_t tabIndex = sceneTabIndexArray->integerValue(iTab); EventBrowserTabGet getTabContent(tabIndex); EventManager::get()->sendEvent(getTabContent.getPointer()); BrowserTabContent* tabContent = getTabContent.getBrowserTab(); if (tabContent != NULL) { if (tabContent->getTabNumber() == selectedTabIndex) { defaultTabBarIndex = iTab; } this->insertTabAtIndex(tabContent, iTab); } else { sceneAttributes->addToErrorMessage("Toolbar in window " + AString::number(this->browserWindowIndex) + " failed to restore tab " + AString::number(selectedTabIndex)); } } } /* * Select tab */ if ((defaultTabBarIndex >= 0) && (defaultTabBarIndex < tabBar->count())) { tabBar->setCurrentIndex(defaultTabBarIndex); } /* * Show hide toolbar */ const bool showToolBar = sceneClass->getBooleanValue("toolBarVisible", true); showHideToolBar(showToolBar); } connectome-workbench-1.2.3+git41-gc4c6c90/src/GuiQt/BrainBrowserWindowToolBar.h000066400000000000000000000400421300200146000270200ustar00rootroot00000000000000#ifndef __BRAIN_BROWSER_WINDOW_TOOLBAR_H__ #define __BRAIN_BROWSER_WINDOW_TOOLBAR_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 "EnumComboBoxTemplate.h" #include "EventListenerInterface.h" #include "ModelTypeEnum.h" #include "SceneableInterface.h" #include "StructureEnum.h" class QAbstractButton; class QAction; class QActionGroup; class QButtonGroup; class QCheckBox; class QComboBox; class QDoubleSpinBox; class QHBoxLayout; class QIcon; class QLabel; class QMenu; class QRadioButton; class QSpinBox; class QTabBar; class QToolButton; namespace caret { class BrainBrowserWindowToolBarChartAxes; class BrainBrowserWindowToolBarChartAttributes; class BrainBrowserWindowToolBarChartType; class BrainBrowserWindowToolBarClipping; class BrainBrowserWindowToolBarSlicePlane; class BrainBrowserWindowToolBarSliceSelection; class BrainBrowserWindowToolBarSurfaceMontage; class BrainBrowserWindowToolBarTab; class BrainBrowserWindowToolBarVolumeMontage; class BrainBrowserWindow; class BrowserTabContent; class Model; class ModelSurface; class ModelVolumeInterface; class SceneAttributes; class SceneClass; class Surface; class SurfaceSelectionViewController; class StructureSurfaceSelectionControl; class WuQWidgetObjectGroup; class BrainBrowserWindowToolBar : public QToolBar, public EventListenerInterface, public SceneableInterface { Q_OBJECT public: BrainBrowserWindowToolBar(const int32_t browserWindowIndex, BrowserTabContent* initialBrowserTabContent, QAction* overlayToolBoxAction, QAction* layersToolBoxAction, QAction* windowAspectRatioLockedAction, QAction* tabAspectRatioLockedAction, BrainBrowserWindow* parentBrainBrowserWindow); ~BrainBrowserWindowToolBar(); void addNewTab(); void addNewTabCloneContent(BrowserTabContent* browserTabContentToBeCloned); void addNewTabWithContent(BrowserTabContent* browserTabContent); void addDefaultTabsAfterLoadingSpecFile(); void receiveEvent(Event* event); virtual SceneClass* saveToScene(const SceneAttributes* sceneAttributes, const AString& instanceName); virtual void restoreFromScene(const SceneAttributes* sceneAttributes, const SceneClass* sceneClass); signals: void viewedModelChanged(); private: enum WidgetPlacement { WIDGET_PLACEMENT_NONE, WIDGET_PLACEMENT_BOTTOM, WIDGET_PLACEMENT_LEFT, WIDGET_PLACEMENT_RIGHT, WIDGET_PLACEMENT_TOP }; BrainBrowserWindowToolBar(const BrainBrowserWindowToolBar&); BrainBrowserWindowToolBar& operator=(const BrainBrowserWindowToolBar&); BrowserTabContent* getTabContentFromSelectedTab(); BrowserTabContent* getTabContentFromTab(const int tabIndex); Model* getDisplayedModel(); int32_t loadIntoTab(const int32_t tabIndex, Model* controller); void updateGraphicsWindow(); void updateUserInterface(); void updateToolBox(); 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* createChartAxesWidget(); QWidget* createChartAttributesWidget(); QWidget* createChartTypeWidget(); QWidget* createSingleSurfaceOptionsWidget(); QWidget* createSurfaceMontageOptionsWidget(); QWidget* createClippingOptionsWidget(); QWidget* createVolumeMontageWidget(); QWidget* createVolumePlaneWidget(); ModelTypeEnum::Enum updateViewWidget(BrowserTabContent* browserTabContent); void updateOrientationWidget(BrowserTabContent* browserTabContent); void updateWholeBrainSurfaceOptionsWidget(BrowserTabContent* browserTabContent); void updateVolumeIndicesWidget(BrowserTabContent* browserTabContent); void updateModeWidget(BrowserTabContent* browserTabContent); void updateTabOptionsWidget(BrowserTabContent* browserTabContent); void updateSingleSurfaceOptionsWidget(BrowserTabContent* browserTabContent); void updateSurfaceMontageOptionsWidget(BrowserTabContent* browserTabContent); void updateChartAxesWidget(BrowserTabContent* browserTabContent); void updateChartAttributesWidget(BrowserTabContent* browserTabContent); void updateChartTypeWidget(BrowserTabContent* browserTabContent); void updateVolumeMontageWidget(BrowserTabContent* browserTabContent); void updateVolumePlaneWidget(BrowserTabContent* browserTabContent); void updateClippingOptionsWidget(BrowserTabContent* browserTabContent); QWidget* createToolWidget(const QString& name, QWidget* childWidget, const WidgetPlacement verticalBarPlacement, const WidgetPlacement contentPlacement, const int horizontalStretching); QWidget* viewWidget; QWidget* orientationWidget; QWidget* wholeBrainSurfaceOptionsWidget; QWidget* volumeIndicesWidget; QWidget* modeWidget; QWidget* windowWidget; QWidget* singleSurfaceSelectionWidget; QWidget* surfaceMontageSelectionWidget; QWidget* m_clippingOptionsWidget; QWidget* volumeMontageWidget; QWidget* volumePlaneWidget; QWidget* chartTypeWidget; QWidget* chartAxesWidget; QWidget* chartAttributesWidget; WuQWidgetObjectGroup* viewWidgetGroup; WuQWidgetObjectGroup* orientationWidgetGroup; WuQWidgetObjectGroup* wholeBrainSurfaceOptionsWidgetGroup; WuQWidgetObjectGroup* modeWidgetGroup; WuQWidgetObjectGroup* singleSurfaceSelectionWidgetGroup; QWidget* fullToolBarWidget; QWidget* m_toolbarWidget; QHBoxLayout* toolbarWidgetLayout; QWidget* tabBarWidget; QTabBar* tabBar; /** Widget displayed at bottom of toolbar for mouse input controls */ QWidget* userInputControlsWidget; /** Layout for widget displayed at bottom of toolbar for mouse input controls */ QHBoxLayout* userInputControlsWidgetLayout; /** 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; void removeTabWithContent(BrowserTabContent* browserTabContent); public slots: void closeSelectedTab(); void moveTabsToNewWindows(); void nextTab(); void previousTab(); void renameTab(); void updateToolBar(); void updateToolBarComponents(BrowserTabContent* browserTabContent); void showHideToolBar(bool showIt); private slots: void selectedTabChanged(int indx); void tabMoved(int, int); void tabCloseSelected(int); private: void insertTabAtIndex(BrowserTabContent* browserTabContent, const int32_t insertAtIndex); void addOrInsertNewTab(BrowserTabContent* browserTabContent, const int32_t insertAtIndex); void removeTab(int index); void tabClosed(int index); BrowserTabContent* createNewTab(AString& errorMessage); QRadioButton* viewModeSurfaceRadioButton; QRadioButton* viewModeSurfaceMontageRadioButton; QRadioButton* viewModeVolumeRadioButton; QRadioButton* viewModeWholeBrainRadioButton; QRadioButton* viewModeChartRadioButton; QAction* customViewAction; private slots: void viewModeRadioButtonClicked(QAbstractButton*); void customViewActionTriggered(); private: QAction* orientationLateralMedialToolButtonAction; QAction* orientationDorsalVentralToolButtonAction; QAction* orientationAnteriorPosteriorToolButtonAction; QToolButton* orientationLateralMedialToolButton; QToolButton* orientationDorsalVentralToolButton; QToolButton* orientationAnteriorPosteriorToolButton; QAction* orientationLeftOrLateralToolButtonAction; QAction* orientationRightOrMedialToolButtonAction; QAction* orientationAnteriorToolButtonAction; QAction* orientationPosteriorToolButtonAction; QAction* orientationDorsalToolButtonAction; QAction* orientationVentralToolButtonAction; QToolButton* orientationLeftOrLateralToolButton; QToolButton* orientationRightOrMedialToolButton; QToolButton* orientationAnteriorToolButton; QToolButton* orientationPosteriorToolButton; QToolButton* orientationDorsalToolButton; QToolButton* orientationVentralToolButton; QAction* orientationResetToolButtonAction; QToolButton* orientationCustomViewSelectToolButton; QIcon* viewOrientationLeftIcon; QIcon* viewOrientationRightIcon; QIcon* viewOrientationAnteriorIcon; QIcon* viewOrientationPosteriorIcon; QIcon* viewOrientationDorsalIcon; QIcon* viewOrientationVentralIcon; QIcon* viewOrientationLeftLateralIcon; QIcon* viewOrientationLeftMedialIcon; QIcon* viewOrientationRightLateralIcon; QIcon* viewOrientationRightMedialIcon; private slots: void orientationLeftOrLateralToolButtonTriggered(bool checked); void orientationRightOrMedialToolButtonTriggered(bool checked); void orientationAnteriorToolButtonTriggered(bool checked); void orientationPosteriorToolButtonTriggered(bool checked); void orientationDorsalToolButtonTriggered(bool checked); void orientationVentralToolButtonTriggered(bool checked); void orientationResetToolButtonTriggered(bool checked); void orientationLateralMedialToolButtonTriggered(bool checked); void orientationDorsalVentralToolButtonTriggered(bool checked); void orientationAnteriorPosteriorToolButtonTriggered(bool checked); private: QComboBox* wholeBrainSurfaceTypeComboBox; QCheckBox* wholeBrainSurfaceLeftCheckBox; QCheckBox* wholeBrainSurfaceRightCheckBox; QCheckBox* wholeBrainSurfaceCerebellumCheckBox; QDoubleSpinBox* wholeBrainSurfaceSeparationLeftRightSpinBox; QDoubleSpinBox* wholeBrainSurfaceSeparationCerebellumSpinBox; private slots: void wholeBrainSurfaceTypeComboBoxIndexChanged(int indx); void wholeBrainSurfaceLeftCheckBoxStateChanged(int state); void wholeBrainSurfaceRightCheckBoxStateChanged(int state); void wholeBrainSurfaceCerebellumCheckBoxStateChanged(int state); void wholeBrainSurfaceSeparationLeftRightSpinBoxValueChanged(double d); void wholeBrainSurfaceSeparationCerebellumSpinBoxSelected(double d); void wholeBrainSurfaceLeftToolButtonTriggered(bool checked); void wholeBrainSurfaceRightToolButtonTriggered(bool checked); void wholeBrainSurfaceCerebellumToolButtonTriggered(bool checked); private: StructureSurfaceSelectionControl* surfaceSurfaceSelectionControl; private slots: void surfaceSelectionControlChanged(const StructureEnum::Enum, ModelSurface*); private: BrainBrowserWindowToolBarChartAxes* m_chartAxisToolBarComponent; BrainBrowserWindowToolBarChartType* m_chartTypeToolBarComponent; BrainBrowserWindowToolBarChartAttributes* m_chartAttributesToolBarComponent; BrainBrowserWindowToolBarSurfaceMontage* m_surfaceMontageToolBarComponent; BrainBrowserWindowToolBarClipping* m_clippingToolBarComponent; BrainBrowserWindowToolBarSlicePlane* m_slicePlaneComponent; BrainBrowserWindowToolBarSliceSelection* m_sliceSelectionComponent; BrainBrowserWindowToolBarVolumeMontage* m_volumeMontageComponent; BrainBrowserWindowToolBarTab* m_tabOptionsComponent; private slots: void modeInputModeActionTriggered(QAction*); private: void updateDisplayedModeUserInputWidget(); QActionGroup* modeInputModeActionGroup; QAction* modeInputModeAnnotationsAction; QAction* modeInputModeBordersAction; QAction* modeInputModeFociAction; QAction* modeInputModeImageAction; QAction* modeInputModeViewAction; QAction* modeInputVolumeEditAction; private: QAction* toolBarToolButtonAction; QAction* toolBoxToolButtonAction; int32_t browserWindowIndex; private slots: void resetTabIndexForTileTabsHighlighting(); private: friend class BrainBrowserWindow; friend class BrainBrowserWindowToolBarChartAxes; friend class BrainBrowserWindowToolBarChartType; friend class BrainBrowserWindowToolBarClipping; friend class BrainBrowserWindowToolBarComponent; friend class BrainBrowserWindowToolBarSurfaceMontage; friend class BrainBrowserWindowToolBarSlicePlane; friend class BrainBrowserWindowToolBarSliceSelection; friend class BrainBrowserWindowToolBarTab; friend class BrainBrowserWindowToolBarVolumeMontage; /** * When a tab is selected in Tile Tabs viewing, * the graphics window content of the tab is * highlighted for a short time by drawing a * box around it. */ int32_t m_tabIndexForTileTabsHighlighting; bool isContructorFinished; bool isDestructionInProgress; }; } #endif // __BRAIN_BROWSER_WINDOW_TOOLBAR_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/GuiQt/BrainBrowserWindowToolBarChartAttributes.cxx000066400000000000000000000501731300200146000324320ustar00rootroot00000000000000 /*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_ATTRIBUTES_DECLARE__ #include "BrainBrowserWindowToolBarChartAttributes.h" #undef __BRAIN_BROWSER_WINDOW_TOOL_BAR_CHART_ATTRIBUTES_DECLARE__ #include "BrowserTabContent.h" #include "CaretAssert.h" #include "CaretDataFile.h" #include "CaretDataFileSelectionModel.h" #include "EnumComboBoxTemplate.h" #include "CaretMappableDataFile.h" #include "CaretMappableDataFileAndMapSelectionModel.h" #include "ChartMatrixDisplayProperties.h" #include "ChartModelDataSeries.h" #include "ChartModelFrequencySeries.h" #include "ChartModelTimeSeries.h" #include "ChartableMatrixInterface.h" #include "EventBrowserWindowGraphicsRedrawn.h" #include "EventGraphicsUpdateAllWindows.h" #include "EventManager.h" #include "ModelChart.h" #include "WuQFactory.h" #include "WuQWidgetObjectGroup.h" #include "WuQtUtilities.h" using namespace caret; /** * \class caret::BrainBrowserWindowToolBarChartAttributes * \brief Controls for chart attributes. * \ingroup GuiQt */ /** * Constructor. * * @param parentToolBar * The parent toolbar. */ BrainBrowserWindowToolBarChartAttributes::BrainBrowserWindowToolBarChartAttributes(BrainBrowserWindowToolBar* parentToolBar) : BrainBrowserWindowToolBarComponent(parentToolBar) { m_cartesianChartAttributesWidget = new CartesianChartAttributesWidget(this); m_matrixChartAttributesWidget = new MatrixChartAttributesWidget(this); m_stackedWidget = new QStackedWidget(); m_stackedWidget->addWidget(m_cartesianChartAttributesWidget); m_stackedWidget->addWidget(m_matrixChartAttributesWidget); QVBoxLayout* layout = new QVBoxLayout(this); WuQtUtilities::setLayoutSpacingAndMargins(layout, 0, 0); layout->addWidget(m_stackedWidget); layout->addStretch(); } /** * Destructor. */ BrainBrowserWindowToolBarChartAttributes::~BrainBrowserWindowToolBarChartAttributes() { } /** * Update content of this tool bar component. * * @param browserTabContent * Content of the browser tab. */ void BrainBrowserWindowToolBarChartAttributes::updateContent(BrowserTabContent* /*browserTabContent*/) { ChartModelCartesian* cartesianChart = getCartesianChart(); ChartMatrixDisplayProperties* matrixProperties = getChartableMatrixDisplayProperties(); if (cartesianChart != NULL) { m_stackedWidget->setCurrentWidget(m_cartesianChartAttributesWidget); m_cartesianChartAttributesWidget->updateContent(); m_stackedWidget->setEnabled(true); } else if (matrixProperties) { m_stackedWidget->setCurrentWidget(m_matrixChartAttributesWidget); m_matrixChartAttributesWidget->updateContent(); m_stackedWidget->setEnabled(true); } else { m_stackedWidget->setEnabled(false); } } /** * @return Cartesian chart in this widget. Returned value will * be NULL if the chart (or no chart) is not a Cartesian Chart. */ ChartModelCartesian* BrainBrowserWindowToolBarChartAttributes::getCartesianChart() { ChartModelCartesian* cartesianChart = NULL; BrowserTabContent* browserTabContent = getTabContentFromSelectedTab(); if (browserTabContent != NULL) { ModelChart* modelChart = browserTabContent->getDisplayedChartModel(); if (modelChart != NULL) { const int32_t tabIndex = browserTabContent->getTabNumber(); const ChartDataTypeEnum::Enum chartType = modelChart->getSelectedChartDataType(tabIndex); switch (chartType) { 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: cartesianChart = modelChart->getSelectedDataSeriesChartModel(tabIndex); //dynamic_cast(chart); break; case ChartDataTypeEnum::CHART_DATA_TYPE_LINE_FREQUENCY_SERIES: cartesianChart = modelChart->getSelectedFrequencySeriesChartModel(tabIndex); break; case ChartDataTypeEnum::CHART_DATA_TYPE_LINE_TIME_SERIES: cartesianChart = modelChart->getSelectedTimeSeriesChartModel(tabIndex); //dynamic_cast(chart); break; } } } return cartesianChart; } /** * @return Matrix chart interface in this widget. Returned value will * be NULL if the chart (or no chart) is not a Matrix Chart. */ ChartMatrixDisplayProperties* BrainBrowserWindowToolBarChartAttributes::getChartableMatrixDisplayProperties() { ChartMatrixDisplayProperties* matrixDisplayProperties = NULL; BrowserTabContent* browserTabContent = getTabContentFromSelectedTab(); if (browserTabContent != NULL) { ModelChart* modelChart = browserTabContent->getDisplayedChartModel(); if (modelChart != NULL) { const int32_t tabIndex = browserTabContent->getTabNumber(); const ChartDataTypeEnum::Enum chartType = modelChart->getSelectedChartDataType(tabIndex); switch (chartType) { case ChartDataTypeEnum::CHART_DATA_TYPE_INVALID: break; case ChartDataTypeEnum::CHART_DATA_TYPE_MATRIX_LAYER: { ChartableMatrixInterface* matrixInterface = modelChart->getChartableMatrixParcelFileSelectionModel(tabIndex)->getSelectedFileOfType(); if (matrixInterface != NULL) { matrixDisplayProperties = matrixInterface->getChartMatrixDisplayProperties(tabIndex); } } break; case ChartDataTypeEnum::CHART_DATA_TYPE_MATRIX_SERIES: { CaretDataFileSelectionModel* fileModel = modelChart->getChartableMatrixSeriesFileSelectionModel(tabIndex); CaretDataFile* caretFile = fileModel->getSelectedFile(); if (caretFile != NULL) { ChartableMatrixInterface* matrixInterface = dynamic_cast(caretFile); if (matrixInterface != NULL) { matrixDisplayProperties = matrixInterface->getChartMatrixDisplayProperties(tabIndex); } } } break; case ChartDataTypeEnum::CHART_DATA_TYPE_LINE_DATA_SERIES: break; case ChartDataTypeEnum::CHART_DATA_TYPE_LINE_FREQUENCY_SERIES: break; case ChartDataTypeEnum::CHART_DATA_TYPE_LINE_TIME_SERIES: break; } } } return matrixDisplayProperties; } /** * Update the graphics. */ void BrainBrowserWindowToolBarChartAttributes::updateGraphics() { EventManager::get()->sendEvent(EventGraphicsUpdateAllWindows().getPointer()); } /* ===========================================================================*/ /** * \class caret::CartesianChartAttributesWidget * \brief Controls for cartesian chart attributes. * \ingroup GuiQt */ /** * Constructor. * * @param brainBrowserWindowToolBarChartAttributes * The parent attributes widget. */ CartesianChartAttributesWidget::CartesianChartAttributesWidget(BrainBrowserWindowToolBarChartAttributes* 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. */ CartesianChartAttributesWidget::~CartesianChartAttributesWidget() { } /** * Update the content of this widget. */ void CartesianChartAttributesWidget::updateContent() { ChartModelCartesian* chart = m_brainBrowserWindowToolBarChartAttributes->getCartesianChart(); if (chart != NULL) { m_cartesianLineWidthDoubleSpinBox->blockSignals(true); m_cartesianLineWidthDoubleSpinBox->setValue(chart->getLineWidth()); m_cartesianLineWidthDoubleSpinBox->blockSignals(false); } } /** * Called when the cartesian line width is changed. * * @param value * New value for line width. */ void CartesianChartAttributesWidget::cartesianLineWidthValueChanged(double value) { ChartModelCartesian* chart = m_brainBrowserWindowToolBarChartAttributes->getCartesianChart(); if (chart != NULL) { chart->setLineWidth(value); m_brainBrowserWindowToolBarChartAttributes->updateGraphics(); } } /* ===========================================================================*/ /** * \class caret::MatrixChartAttributesWidget * \brief Controls for matrix chart attributes. * \ingroup GuiQt */ /** * Constructor. * * @param brainBrowserWindowToolBarChartAttributes * The parent attributes widget. */ MatrixChartAttributesWidget::MatrixChartAttributesWidget(BrainBrowserWindowToolBarChartAttributes* brainBrowserWindowToolBarChartAttributes) : QWidget(brainBrowserWindowToolBarChartAttributes), EventListenerInterface() { m_brainBrowserWindowToolBarChartAttributes = brainBrowserWindowToolBarChartAttributes; QLabel* cellWidthLabel = new QLabel("Cell Width"); m_cellWidthSpinBox = WuQFactory::newDoubleSpinBoxWithMinMaxStepDecimalsSignalDouble(1.0, 1000.0, 0.1, 2, this, SLOT(cellWidthSpinBoxValueChanged(double))); m_cellWidthSpinBox->setKeyboardTracking(true); QLabel* cellHeightLabel = new QLabel("Cell Height"); m_cellHeightSpinBox = WuQFactory::newDoubleSpinBoxWithMinMaxStepDecimalsSignalDouble(1.0, 1000.0, 0.1, 2, this, SLOT(cellHeightSpinBoxValueChanged(double))); m_cellHeightSpinBox->setKeyboardTracking(true); QAction* resetButtonAction = WuQtUtilities::createAction("Reset", "Reset panning (SHIFT-mouse),zooming (CTRL-mouse), and scale matrix to fit window", this, this, SLOT(resetButtonClicked())); QToolButton* resetToolButton = new QToolButton(); resetToolButton->setDefaultAction(resetButtonAction); WuQtUtilities::matchWidgetWidths(m_cellHeightSpinBox, m_cellWidthSpinBox); m_highlightSelectionCheckBox = new QCheckBox("Highlight Selection"); QObject::connect(m_highlightSelectionCheckBox, SIGNAL(clicked(bool)), this, SLOT(highlightSelectionCheckBoxClicked(bool))); m_displayGridLinesCheckBox = new QCheckBox("Show Grid Outline"); QObject::connect(m_displayGridLinesCheckBox, SIGNAL(clicked(bool)), this, SLOT(displayGridLinesCheckBoxClicked(bool))); m_manualWidgetsGroup = new WuQWidgetObjectGroup(this); m_manualWidgetsGroup->add(m_cellWidthSpinBox); m_manualWidgetsGroup->add(m_cellHeightSpinBox); m_manualWidgetsGroup->add(m_displayGridLinesCheckBox); m_manualWidgetsGroup->add(resetToolButton); 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_cellWidthSpinBox, rowIndex, COLUMN_WIDGET); rowIndex++; gridLayout->addWidget(cellHeightLabel, rowIndex, COLUMN_LABEL); gridLayout->addWidget(m_cellHeightSpinBox, rowIndex, COLUMN_WIDGET); rowIndex++; gridLayout->addWidget(resetToolButton, rowIndex, COLUMN_LABEL, 1, 2, Qt::AlignHCenter); 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()); EventManager::get()->addEventListener(this, EventTypeEnum::EVENT_BROWSER_WINDOW_GRAPHICS_HAVE_BEEN_REDRAWN); } /** * Destructor. */ MatrixChartAttributesWidget::~MatrixChartAttributesWidget() { EventManager::get()->removeAllEventsFromListener(this); } /** * Receive an event. * * @param event * The event. */ void MatrixChartAttributesWidget::receiveEvent(Event* event) { if (event->getEventType() == EventTypeEnum::EVENT_BROWSER_WINDOW_GRAPHICS_HAVE_BEEN_REDRAWN) { EventBrowserWindowGraphicsRedrawn* redrawEvent = dynamic_cast(event); CaretAssert(event); /* * When the matrix view mode is auto, the OpenGL graphics update the size of * the matrix cells for use by manual mode (cell sizes are in pixels). */ ChartMatrixDisplayProperties* matrixDisplayProperties = m_brainBrowserWindowToolBarChartAttributes->getChartableMatrixDisplayProperties(); if (matrixDisplayProperties != NULL) { const BrowserTabContent* tabContent = m_brainBrowserWindowToolBarChartAttributes->getTabContentFromSelectedTab(); if (tabContent->getTabNumber() == redrawEvent->getBrowserWindowIndex()) { updateContent(); } } } } /** * Update the content of this widget. */ void MatrixChartAttributesWidget::updateContent() { ChartMatrixDisplayProperties* matrixDisplayProperties = m_brainBrowserWindowToolBarChartAttributes->getChartableMatrixDisplayProperties(); if (matrixDisplayProperties != NULL) { m_cellWidthSpinBox->blockSignals(true); m_cellWidthSpinBox->setValue(matrixDisplayProperties->getCellWidth()); m_cellWidthSpinBox->blockSignals(false); m_cellHeightSpinBox->blockSignals(true); m_cellHeightSpinBox->setValue(matrixDisplayProperties->getCellHeight()); m_cellHeightSpinBox->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); } } /** * Called when the cell width spin box value is changed. * * @param value * New value for cell width. */ void MatrixChartAttributesWidget::cellWidthSpinBoxValueChanged(double value) { ChartMatrixDisplayProperties* matrixDisplayProperties = m_brainBrowserWindowToolBarChartAttributes->getChartableMatrixDisplayProperties(); if (matrixDisplayProperties != NULL) { matrixDisplayProperties->setScaleMode(ChartMatrixScaleModeEnum::CHART_MATRIX_SCALE_MANUAL); matrixDisplayProperties->setCellWidth(value); m_brainBrowserWindowToolBarChartAttributes->updateGraphics(); } } /** * Called when the cell height spin box value is changed. * * @param value * New value for cell height. */ void MatrixChartAttributesWidget::cellHeightSpinBoxValueChanged(double value) { ChartMatrixDisplayProperties* matrixDisplayProperties = m_brainBrowserWindowToolBarChartAttributes->getChartableMatrixDisplayProperties(); if (matrixDisplayProperties != NULL) { matrixDisplayProperties->setScaleMode(ChartMatrixScaleModeEnum::CHART_MATRIX_SCALE_MANUAL); matrixDisplayProperties->setCellHeight(value); m_brainBrowserWindowToolBarChartAttributes->updateGraphics(); } } /** * Called when the reset button is clicked. */ void MatrixChartAttributesWidget::resetButtonClicked() { ChartMatrixDisplayProperties* matrixDisplayProperties = m_brainBrowserWindowToolBarChartAttributes->getChartableMatrixDisplayProperties(); if (matrixDisplayProperties != NULL) { matrixDisplayProperties->resetPropertiesToDefault(); updateContent(); m_brainBrowserWindowToolBarChartAttributes->updateGraphics(); } } /** * Called when the show selection check box is checked. * * @param checked * New checked status. */ void MatrixChartAttributesWidget::highlightSelectionCheckBoxClicked(bool checked) { ChartMatrixDisplayProperties* matrixDisplayProperties = m_brainBrowserWindowToolBarChartAttributes->getChartableMatrixDisplayProperties(); if (matrixDisplayProperties != NULL) { matrixDisplayProperties->setSelectedRowColumnHighlighted(checked); m_brainBrowserWindowToolBarChartAttributes->updateGraphics(); } } /** * Called when the display grid lines check box is checked. * * @param checked * New checked status. */ void MatrixChartAttributesWidget::displayGridLinesCheckBoxClicked(bool checked) { ChartMatrixDisplayProperties* matrixDisplayProperties = m_brainBrowserWindowToolBarChartAttributes->getChartableMatrixDisplayProperties(); if (matrixDisplayProperties != NULL) { matrixDisplayProperties->setGridLinesDisplayed(checked); m_brainBrowserWindowToolBarChartAttributes->updateGraphics(); } } connectome-workbench-1.2.3+git41-gc4c6c90/src/GuiQt/BrainBrowserWindowToolBarChartAttributes.h000066400000000000000000000113561300200146000320570ustar00rootroot00000000000000#ifndef __BRAIN_BROWSER_WINDOW_TOOL_BAR_CHART_ATTRIBUTES_H__ #define __BRAIN_BROWSER_WINDOW_TOOL_BAR_CHART_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 CartesianChartAttributesWidget; class ChartMatrixDisplayProperties; class ChartModelCartesian; class EnumComboBoxTemplate; class MatrixChartAttributesWidget; class WuQWidgetObjectGroup; class BrainBrowserWindowToolBarChartAttributes : public BrainBrowserWindowToolBarComponent { Q_OBJECT public: BrainBrowserWindowToolBarChartAttributes(BrainBrowserWindowToolBar* parentToolBar); virtual ~BrainBrowserWindowToolBarChartAttributes(); virtual void updateContent(BrowserTabContent* browserTabContent); // ADD_NEW_METHODS_HERE private: BrainBrowserWindowToolBarChartAttributes(const BrainBrowserWindowToolBarChartAttributes&); BrainBrowserWindowToolBarChartAttributes& operator=(const BrainBrowserWindowToolBarChartAttributes&); ChartModelCartesian* getCartesianChart(); ChartMatrixDisplayProperties* getChartableMatrixDisplayProperties(); void updateGraphics(); CartesianChartAttributesWidget* m_cartesianChartAttributesWidget; MatrixChartAttributesWidget* m_matrixChartAttributesWidget; QStackedWidget* m_stackedWidget; // ADD_NEW_MEMBERS_HERE friend class CartesianChartAttributesWidget; friend class MatrixChartAttributesWidget; }; /* * While this should be a private class in the class above * Qt's meta-object compiler (moc) does not allow it to be. */ class CartesianChartAttributesWidget : public QWidget { Q_OBJECT public: CartesianChartAttributesWidget(BrainBrowserWindowToolBarChartAttributes* brainBrowserWindowToolBarChartAttributes); ~CartesianChartAttributesWidget(); virtual void updateContent(); private slots: void cartesianLineWidthValueChanged(double value); private: BrainBrowserWindowToolBarChartAttributes* 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 MatrixChartAttributesWidget : public QWidget, public EventListenerInterface { Q_OBJECT public: MatrixChartAttributesWidget(BrainBrowserWindowToolBarChartAttributes* brainBrowserWindowToolBarChartAttributes); ~MatrixChartAttributesWidget(); virtual void receiveEvent(Event* event); virtual void updateContent(); private slots: void cellWidthSpinBoxValueChanged(double value); void cellHeightSpinBoxValueChanged(double value); void highlightSelectionCheckBoxClicked(bool checked); void displayGridLinesCheckBoxClicked(bool checked); void resetButtonClicked(); private: BrainBrowserWindowToolBarChartAttributes* m_brainBrowserWindowToolBarChartAttributes; QDoubleSpinBox* m_cellWidthSpinBox; QDoubleSpinBox* m_cellHeightSpinBox; WuQWidgetObjectGroup* m_manualWidgetsGroup; QCheckBox* m_highlightSelectionCheckBox; QCheckBox* m_displayGridLinesCheckBox; }; #ifdef __BRAIN_BROWSER_WINDOW_TOOL_BAR_CHART_ATTRIBUTES_DECLARE__ // #endif // __BRAIN_BROWSER_WINDOW_TOOL_BAR_CHART_ATTRIBUTES_DECLARE__ } // namespace #endif //__BRAIN_BROWSER_WINDOW_TOOL_BAR_CHART_ATTRIBUTES_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/GuiQt/BrainBrowserWindowToolBarChartAxes.cxx000066400000000000000000000407701300200146000312060ustar00rootroot00000000000000 /*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_BROWSER_WINDOW_TOOL_BAR_CHART_AXES_DECLARE__ #include "BrainBrowserWindowToolBarChartAxes.h" #undef __BRAIN_BROWSER_WINDOW_TOOL_BAR_CHART_AXES_DECLARE__ #include #include #include #include #include "BrowserTabContent.h" #include "BrainBrowserWindowToolBar.h" #include "CaretAssert.h" #include "ChartAxis.h" #include "ChartAxisCartesian.h" #include "ChartModelDataSeries.h" #include "ChartModelFrequencySeries.h" #include "ChartModelTimeSeries.h" #include "ModelChart.h" #include "WuQWidgetObjectGroup.h" #include "WuQtUtilities.h" using namespace caret; /** * \class caret::BrainBrowserWindowToolBarChartAxes * \brief Component of toolbar for chart axes. * \ingroup GuiQt */ /** * Constructor. * * @param parentToolBar * parent toolbar. */ BrainBrowserWindowToolBarChartAxes::BrainBrowserWindowToolBarChartAxes(BrainBrowserWindowToolBar* parentToolBar) : BrainBrowserWindowToolBarComponent(parentToolBar), m_parentToolBar(parentToolBar) { QGridLayout* gridLayout = new QGridLayout(this); WuQtUtilities::setLayoutSpacingAndMargins(gridLayout, 0, 0); gridLayout->addWidget(new QLabel("Axis"), 0, 0, Qt::AlignHCenter); gridLayout->addWidget(new QLabel("Auto"), 0, 1, Qt::AlignHCenter); gridLayout->addWidget(new QLabel("Min"), 0, 2, Qt::AlignHCenter); gridLayout->addWidget(new QLabel("Max"), 0, 3, Qt::AlignHCenter); createAxisWidgets(gridLayout, m_bottomAxisLabel, m_bottomAxisAutoRangeScaleCheckBox, m_bottomAxisMinimumValueSpinBox, m_bottomAxisMaximumValueSpinBox, m_bottomAxisWidgetGroup); QObject::connect(m_bottomAxisAutoRangeScaleCheckBox, SIGNAL(clicked(bool)), this, SLOT(bottomAxisAutoRangeScaleCheckBoxClicked(bool))); QObject::connect(m_bottomAxisMinimumValueSpinBox, SIGNAL(valueChanged(double)), this, SLOT(bottomAxisValueChanged(double))); QObject::connect(m_bottomAxisMaximumValueSpinBox, SIGNAL(valueChanged(double)), this, SLOT(bottomAxisValueChanged(double))); createAxisWidgets(gridLayout, m_leftAxisLabel, m_leftAxisAutoRangeScaleCheckBox, m_leftAxisMinimumValueSpinBox, m_leftAxisMaximumValueSpinBox, m_leftAxisWidgetGroup); QObject::connect(m_leftAxisAutoRangeScaleCheckBox, SIGNAL(clicked(bool)), this, SLOT(leftAxisAutoRangeScaleCheckBoxClicked(bool))); QObject::connect(m_leftAxisMinimumValueSpinBox, SIGNAL(valueChanged(double)), this, SLOT(leftAxisValueChanged(double))); QObject::connect(m_leftAxisMaximumValueSpinBox, SIGNAL(valueChanged(double)), this, SLOT(leftAxisValueChanged(double))); // createAxisWidgets(gridLayout, // m_topAxisLabel, // m_topAxisAutoRangeScaleCheckBox, // m_topAxisMinimumValueSpinBox, // m_topAxisMaximumValueSpinBox, // m_topAxisWidgetGroup); // // createAxisWidgets(gridLayout, // m_rightAxisLabel, // m_rightAxisAutoRangeScaleCheckBox, // m_rightAxisMinimumValueSpinBox, // m_rightAxisMaximumValueSpinBox, // m_rightAxisWidgetGroup); } /** * Destructor. */ BrainBrowserWindowToolBarChartAxes::~BrainBrowserWindowToolBarChartAxes() { } /** * Create the widgets for an axis. * * @param gridLayout * Layout in which axis widgets are displayed. * @param nameLabel * Label that contains name of axis. * @param autoRangeScaleCheckBox * Checkbox for enabling auto range scale. * @param minimumValueSpinBox * Spin box for minimum value. * @param maximumValueSpinBox * Spin box for maximum value. * @param widgetGroup * Widget group for the axis' widgets. */ void BrainBrowserWindowToolBarChartAxes::createAxisWidgets(QGridLayout* gridLayout, QLabel*& nameLabel, QCheckBox*& autoRangeScaleCheckBox, QDoubleSpinBox*& minimumValueSpinBox, QDoubleSpinBox*& maximumValueSpinBox, WuQWidgetObjectGroup*& widgetGroup) { const int32_t spinBoxWidth = 100; nameLabel = new QLabel(); autoRangeScaleCheckBox = new QCheckBox(" "); WuQtUtilities::setWordWrappedToolTip(autoRangeScaleCheckBox, "Axis Auto Range\n\n" "When checked, the minimum and maximum values are " "automatically adjusted to show the full range " "of values for the axis.\n\n" "When not checked, the user may adjust the minimum " "and maximum values for the axis to show a " "desired range of data for the axis.\n\n" "Changing the status from not checked to checked " "will reset the minimum and maximum values to show " "the full range of data."); minimumValueSpinBox = new QDoubleSpinBox(); minimumValueSpinBox->setFixedWidth(spinBoxWidth); minimumValueSpinBox->setMinimum(-std::numeric_limits::max()); minimumValueSpinBox->setMaximum(std::numeric_limits::max()); minimumValueSpinBox->setSingleStep(1.0); minimumValueSpinBox->setDecimals(2); WuQtUtilities::setWordWrappedToolTip(minimumValueSpinBox, "Minimum value displayed for the axis"); maximumValueSpinBox = new QDoubleSpinBox(); maximumValueSpinBox->setFixedWidth(spinBoxWidth); maximumValueSpinBox->setMinimum(-std::numeric_limits::max()); maximumValueSpinBox->setMaximum(std::numeric_limits::max()); maximumValueSpinBox->setSingleStep(1.0); maximumValueSpinBox->setDecimals(2); WuQtUtilities::setWordWrappedToolTip(maximumValueSpinBox, "Maximum value displayed for the axis"); widgetGroup = new WuQWidgetObjectGroup(this); widgetGroup->add(nameLabel); widgetGroup->add(autoRangeScaleCheckBox); widgetGroup->add(minimumValueSpinBox); widgetGroup->add(maximumValueSpinBox); const int rowIndex = gridLayout->rowCount(); gridLayout->addWidget(nameLabel, rowIndex, 0, Qt::AlignLeft); gridLayout->addWidget(autoRangeScaleCheckBox, rowIndex, 1, Qt::AlignHCenter); gridLayout->addWidget(minimumValueSpinBox, rowIndex, 2, Qt::AlignHCenter); gridLayout->addWidget(maximumValueSpinBox, rowIndex, 3, Qt::AlignHCenter); } /** * Update content of this tool bar component. * * @param browserTabContent * Content of the browser tab. */ void BrainBrowserWindowToolBarChartAxes::updateContent(BrowserTabContent* /*browserTabContent*/) { m_bottomAxisWidgetGroup->blockAllSignals(true); m_leftAxisWidgetGroup->blockAllSignals(true); // m_topAxisWidgetGroup->blockAllSignals(true); // m_rightAxisWidgetGroup->blockAllSignals(true); ChartModelCartesian* cartesianChart = getCartesianChart(); if (cartesianChart != NULL) { if (cartesianChart != NULL) { updateAxisWidgets(cartesianChart->getLeftAxis(), m_leftAxisLabel, m_leftAxisAutoRangeScaleCheckBox, m_leftAxisMinimumValueSpinBox, m_leftAxisMaximumValueSpinBox, m_leftAxisWidgetGroup); updateAxisWidgets(cartesianChart->getBottomAxis(), m_bottomAxisLabel, m_bottomAxisAutoRangeScaleCheckBox, m_bottomAxisMinimumValueSpinBox, m_bottomAxisMaximumValueSpinBox, m_bottomAxisWidgetGroup); // updateAxisWidgets(lineSeriesChart->getRightAxis(), // m_rightAxisLabel, // m_rightAxisAutoRangeScaleCheckBox, // m_rightAxisMinimumValueSpinBox, // m_rightAxisMaximumValueSpinBox, // m_rightAxisWidgetGroup); // // updateAxisWidgets(lineSeriesChart->getTopAxis(), // m_topAxisLabel, // m_topAxisAutoRangeScaleCheckBox, // m_topAxisMinimumValueSpinBox, // m_topAxisMaximumValueSpinBox, // m_topAxisWidgetGroup); } } m_bottomAxisWidgetGroup->blockAllSignals(false); m_leftAxisWidgetGroup->blockAllSignals(false); // m_topAxisWidgetGroup->blockAllSignals(false); // m_rightAxisWidgetGroup->blockAllSignals(false); } /** * @return Cartesian chart in this widget. Returned value will * be NULL if the chart (or no chart) is not a Cartesian Chart. */ ChartModelCartesian* BrainBrowserWindowToolBarChartAxes::getCartesianChart() { ChartModelCartesian* cartesianChart = NULL; BrowserTabContent* browserTabContent = getTabContentFromSelectedTab(); if (browserTabContent != NULL) { ModelChart* modelChart = browserTabContent->getDisplayedChartModel(); if (modelChart != NULL) { const int32_t tabIndex = browserTabContent->getTabNumber(); const ChartDataTypeEnum::Enum chartType = modelChart->getSelectedChartDataType(tabIndex); switch (chartType) { 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: cartesianChart = modelChart->getSelectedDataSeriesChartModel(tabIndex); //dynamic_cast(chart); break; case ChartDataTypeEnum::CHART_DATA_TYPE_LINE_FREQUENCY_SERIES: cartesianChart = modelChart->getSelectedFrequencySeriesChartModel(tabIndex); break; case ChartDataTypeEnum::CHART_DATA_TYPE_LINE_TIME_SERIES: cartesianChart = modelChart->getSelectedTimeSeriesChartModel(tabIndex); //dynamic_cast(chart); break; } } } return cartesianChart; } /** * Update the widgets for an axis. * * @param chartAxis * Source axis containing data. * @param nameLabel * Label that contains name of axis. * @param autoRangeScaleCheckBox * Checkbox for enabling auto range scale. * @param minimumValueSpinBox * Spin box for minimum value. * @param maximumValueSpinBox * Spin box for maximum value. * @param widgetGroup * Widget group for the axis' widgets. */ void BrainBrowserWindowToolBarChartAxes::updateAxisWidgets(const ChartAxis* chartAxis, QLabel* nameLabel, QCheckBox* autoRangeScaleCheckBox, QDoubleSpinBox* minimumValueSpinBox, QDoubleSpinBox* maximumValueSpinBox, WuQWidgetObjectGroup* widgetGroup) { if (chartAxis == NULL) { return; } const ChartAxisCartesian* chartAxisCartesian = dynamic_cast(chartAxis); CaretAssert(chartAxisCartesian); const bool visible = chartAxis->isVisible(); if (visible) { nameLabel->setText(chartAxis->getText()); autoRangeScaleCheckBox->setChecked(chartAxis->isAutoRangeScaleEnabled()); const float minValue = chartAxisCartesian->getMinimumValue(); const float maxValue = chartAxisCartesian->getMaximumValue(); minimumValueSpinBox->setValue(minValue); maximumValueSpinBox->setValue(maxValue); } widgetGroup->setVisible(visible); } /** * Called when left axis auto range scale checkbox is clicked. * * @param checked * New check status. */ void BrainBrowserWindowToolBarChartAxes::leftAxisAutoRangeScaleCheckBoxClicked(bool checked) { ChartModelCartesian* cartesianChart = getCartesianChart(); if (cartesianChart != NULL) { cartesianChart->getLeftAxis()->setAutoRangeScaleEnabled(checked); if (checked) { updateContent(getTabContentFromSelectedTab()); invalidateColoringAndUpdateGraphicsWindow(); } } } /** * Called when left axis minimum or maximum value is changed. * * @param value * New value. */ void BrainBrowserWindowToolBarChartAxes::leftAxisValueChanged(double /*value*/) { ChartModelCartesian* cartesianChart = getCartesianChart(); if (cartesianChart != NULL) { m_leftAxisAutoRangeScaleCheckBox->setChecked(false); ChartAxisCartesian* axis = dynamic_cast(cartesianChart->getLeftAxis()); CaretAssert(axis); axis->setAutoRangeScaleEnabled(false); axis->setMinimumValue(m_leftAxisMinimumValueSpinBox->value()); axis->setMaximumValue(m_leftAxisMaximumValueSpinBox->value()); invalidateColoringAndUpdateGraphicsWindow(); } } /** * Called when bottom axis auto range scale checkbox is clicked. * * @param checked * New check status. */ void BrainBrowserWindowToolBarChartAxes::bottomAxisAutoRangeScaleCheckBoxClicked(bool checked) { ChartModelCartesian* cartesianChart = getCartesianChart(); if (cartesianChart != NULL) { cartesianChart->getBottomAxis()->setAutoRangeScaleEnabled(checked); if (checked) { updateContent(getTabContentFromSelectedTab()); invalidateColoringAndUpdateGraphicsWindow(); } } } /** * Called when bottom axis minimum or maximum value is changed. * * @param value * New value. */ void BrainBrowserWindowToolBarChartAxes::bottomAxisValueChanged(double /*value*/) { ChartModelCartesian* cartesianChart = getCartesianChart(); if (cartesianChart != NULL) { m_bottomAxisAutoRangeScaleCheckBox->setChecked(false); ChartAxisCartesian* axis = dynamic_cast(cartesianChart->getBottomAxis()); CaretAssert(axis); axis->setAutoRangeScaleEnabled(false); axis->setMinimumValue(m_bottomAxisMinimumValueSpinBox->value()); axis->setMaximumValue(m_bottomAxisMaximumValueSpinBox->value()); invalidateColoringAndUpdateGraphicsWindow(); } } connectome-workbench-1.2.3+git41-gc4c6c90/src/GuiQt/BrainBrowserWindowToolBarChartAxes.h000066400000000000000000000107301300200146000306240ustar00rootroot00000000000000#ifndef __BRAIN_BROWSER_WINDOW_TOOL_BAR_CHART_AXES_H__ #define __BRAIN_BROWSER_WINDOW_TOOL_BAR_CHART_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*/ class QCheckBox; class QDoubleSpinBox; class QGridLayout; class QLabel; #include "BrainBrowserWindowToolBarComponent.h" namespace caret { class ChartAxis; class ChartModelCartesian; class BrainBrowserWindowToolBar; class WuQWidgetObjectGroup; class BrainBrowserWindowToolBarChartAxes : public BrainBrowserWindowToolBarComponent { Q_OBJECT public: BrainBrowserWindowToolBarChartAxes(BrainBrowserWindowToolBar* parentToolBar); virtual ~BrainBrowserWindowToolBarChartAxes(); virtual void updateContent(BrowserTabContent* browserTabContent); private slots: void leftAxisAutoRangeScaleCheckBoxClicked(bool checked); void leftAxisValueChanged(double value); void bottomAxisAutoRangeScaleCheckBoxClicked(bool checked); void bottomAxisValueChanged(double value); private: BrainBrowserWindowToolBarChartAxes(const BrainBrowserWindowToolBarChartAxes&); BrainBrowserWindowToolBarChartAxes& operator=(const BrainBrowserWindowToolBarChartAxes&); void createAxisWidgets(QGridLayout* gridLayout, QLabel*& nameLabel, QCheckBox*& autoRangeScaleCheckBox, QDoubleSpinBox*& minimumValueSpinBox, QDoubleSpinBox*& maximumValueSpinBox, WuQWidgetObjectGroup*& widgetGroup); void updateAxisWidgets(const ChartAxis* chartAxis, QLabel* nameLabel, QCheckBox* autoRangeScaleCheckBox, QDoubleSpinBox* minimumValueSpinBox, QDoubleSpinBox* maximumValueSpinBox, WuQWidgetObjectGroup* widgetGroup); ChartModelCartesian* getCartesianChart(); public: // ADD_NEW_METHODS_HERE private: BrainBrowserWindowToolBar* m_parentToolBar; QLabel* m_bottomAxisLabel; QCheckBox* m_bottomAxisAutoRangeScaleCheckBox; QDoubleSpinBox* m_bottomAxisMinimumValueSpinBox; QDoubleSpinBox* m_bottomAxisMaximumValueSpinBox; WuQWidgetObjectGroup* m_bottomAxisWidgetGroup; QLabel* m_leftAxisLabel; QCheckBox* m_leftAxisAutoRangeScaleCheckBox; QDoubleSpinBox* m_leftAxisMinimumValueSpinBox; QDoubleSpinBox* m_leftAxisMaximumValueSpinBox; WuQWidgetObjectGroup* m_leftAxisWidgetGroup; // QLabel* m_topAxisLabel; // // QCheckBox* m_topAxisAutoRangeScaleCheckBox; // // QDoubleSpinBox* m_topAxisMinimumValueSpinBox; // // QDoubleSpinBox* m_topAxisMaximumValueSpinBox; // // WuQWidgetObjectGroup* m_topAxisWidgetGroup; // // QLabel* m_rightAxisLabel; // // QCheckBox* m_rightAxisAutoRangeScaleCheckBox; // // QDoubleSpinBox* m_rightAxisMinimumValueSpinBox; // // QDoubleSpinBox* m_rightAxisMaximumValueSpinBox; // // WuQWidgetObjectGroup* m_rightAxisWidgetGroup; // ADD_NEW_MEMBERS_HERE }; #ifdef __BRAIN_BROWSER_WINDOW_TOOL_BAR_CHART_AXES_DECLARE__ // #endif // __BRAIN_BROWSER_WINDOW_TOOL_BAR_CHART_AXES_DECLARE__ } // namespace #endif //__BRAIN_BROWSER_WINDOW_TOOL_BAR_CHART_AXES_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/GuiQt/BrainBrowserWindowToolBarChartType.cxx000066400000000000000000000232001300200146000312140ustar00rootroot00000000000000 /*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_DECLARE__ #include "BrainBrowserWindowToolBarChartType.h" #undef __BRAIN_BROWSER_WINDOW_TOOL_BAR_CHART_DECLARE__ #include #include #include #include "BrowserTabContent.h" #include "BrainBrowserWindowToolBar.h" #include "CaretAssert.h" #include "ChartModel.h" #include "EnumComboBoxTemplate.h" #include "ModelChart.h" #include "WuQtUtilities.h" using namespace caret; /** * \class caret::BrainBrowserWindowToolBarChartType * \brief Chart Component of Brain Browser Window ToolBar * \ingroup GuiQt */ /** * Constructor. * * @param parentToolBar * parent toolbar. */ BrainBrowserWindowToolBarChartType::BrainBrowserWindowToolBarChartType(BrainBrowserWindowToolBar* parentToolBar) : BrainBrowserWindowToolBarComponent(parentToolBar), m_parentToolBar(parentToolBar) { m_chartTypeButtonGroup = new QButtonGroup(this); QVBoxLayout* radioButtonLayout = new QVBoxLayout(this); std::vector allChartTypes; ChartDataTypeEnum::getAllEnums(allChartTypes); for (std::vector::iterator chartIter = allChartTypes.begin(); chartIter != allChartTypes.end(); chartIter++) { const ChartDataTypeEnum::Enum ct = *chartIter; if (ct == ChartDataTypeEnum::CHART_DATA_TYPE_INVALID) { continue; } QRadioButton* rb = new QRadioButton(ChartDataTypeEnum::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(); // 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(); } /** * Destructor. */ BrainBrowserWindowToolBarChartType::~BrainBrowserWindowToolBarChartType() { } /** * Called when a radio button is clicked. * * @param buttonIndex * Index of button that is clicked. */ void 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?"); // } m_parentToolBar->getTabContentFromSelectedTab(); BrowserTabContent* btc = m_parentToolBar->getTabContentFromSelectedTab(); ModelChart* chartModel = btc->getDisplayedChartModel(); const int32_t tabIndex = btc->getTabNumber(); chartModel->setSelectedChartDataType(tabIndex, chartDataType); //updateContent(btc); invalidateColoringAndUpdateGraphicsWindow(); m_parentToolBar->updateUserInterface(); } /** * Update content of this tool bar component. * * @param browserTabContent * Content of the browser tab. */ void BrainBrowserWindowToolBarChartType::updateContent(BrowserTabContent* browserTabContent) { m_chartTypeButtonGroup->blockSignals(true); const ModelChart* chartModel = browserTabContent->getDisplayedChartModel(); if (chartModel != NULL) { const int32_t tabIndex = browserTabContent->getTabNumber(); const ChartDataTypeEnum::Enum selectedChartType = chartModel->getSelectedChartDataType(tabIndex); std::vector validChartDataTypes; chartModel->getValidChartDataTypes(validChartDataTypes); for (std::vector >::iterator buttIter = m_chartTypeRadioButtons.begin(); buttIter != m_chartTypeRadioButtons.end(); buttIter++) { ChartDataTypeEnum::Enum chartType = buttIter->first; QRadioButton* radioButton = buttIter->second; const bool validTypeFlag = (std::find(validChartDataTypes.begin(), validChartDataTypes.end(), chartType) != validChartDataTypes.end()); radioButton->setEnabled(validTypeFlag); if (chartType == selectedChartType) { 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); } connectome-workbench-1.2.3+git41-gc4c6c90/src/GuiQt/BrainBrowserWindowToolBarChartType.h000066400000000000000000000052371300200146000306530ustar00rootroot00000000000000#ifndef __BRAIN_BROWSER_WINDOW_TOOL_BAR_CHART_H__ #define __BRAIN_BROWSER_WINDOW_TOOL_BAR_CHART_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 "ChartDataTypeEnum.h" namespace caret { class EnumComboBoxTemplate; class BrainBrowserWindowToolBarChartType : public BrainBrowserWindowToolBarComponent { Q_OBJECT public: BrainBrowserWindowToolBarChartType(BrainBrowserWindowToolBar* parentToolBar); virtual ~BrainBrowserWindowToolBarChartType(); virtual void updateContent(BrowserTabContent* browserTabContent); private slots: void chartTypeRadioButtonClicked(int buttonIndex); private: BrainBrowserWindowToolBarChartType(const BrainBrowserWindowToolBarChartType&); BrainBrowserWindowToolBarChartType& operator=(const BrainBrowserWindowToolBarChartType&); public: // ADD_NEW_METHODS_HERE private: // ADD_NEW_MEMBERS_HERE BrainBrowserWindowToolBar* m_parentToolBar; std::vector > m_chartTypeRadioButtons; QButtonGroup* m_chartTypeButtonGroup; // QRadioButton* m_chartMatrixLayerTypeRadioButton; // // QRadioButton* m_chartMatrixSeriesTypeRadioButton; // // QRadioButton* m_chartDataSeriesTypeRadioButton; // // QRadioButton* m_chartFrequencySeriesTypeRadioButton; // // QRadioButton* m_chartTimeSeriesTypeRadioButton; }; #ifdef __BRAIN_BROWSER_WINDOW_TOOL_BAR_CHART_DECLARE__ // #endif // __BRAIN_BROWSER_WINDOW_TOOL_BAR_CHART_DECLARE__ } // namespace #endif //__BRAIN_BROWSER_WINDOW_TOOL_BAR_CHART_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/GuiQt/BrainBrowserWindowToolBarClipping.cxx000066400000000000000000000170231300200146000310640ustar00rootroot00000000000000 /*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 #define __BRAIN_BROWSER_WINDOW_TOOL_BAR_CLIPPING_DECLARE__ #include "BrainBrowserWindowToolBarClipping.h" #undef __BRAIN_BROWSER_WINDOW_TOOL_BAR_CLIPPING_DECLARE__ #include "BrowserTabContent.h" #include "CaretAssert.h" #include "GuiManager.h" #include "WuQtUtilities.h" using namespace caret; /** * \class caret::BrainBrowserWindowToolBarClipping * \brief Clipping group for toolbar. * \ingroup GuiQt */ /** * Constructor. */ BrainBrowserWindowToolBarClipping::BrainBrowserWindowToolBarClipping(const int32_t browserWindowIndex, BrainBrowserWindowToolBar* parentToolBar) : BrainBrowserWindowToolBarComponent(parentToolBar), m_browserWindowIndex(browserWindowIndex), m_parentToolBar(parentToolBar) { m_xClippingEnabledCheckBox = new QCheckBox("X"); QObject::connect(m_xClippingEnabledCheckBox, SIGNAL(clicked(bool)), this, SLOT(clippingCheckBoxCheckStatusChanged())); m_yClippingEnabledCheckBox = new QCheckBox("Y"); QObject::connect(m_yClippingEnabledCheckBox, SIGNAL(clicked(bool)), this, SLOT(clippingCheckBoxCheckStatusChanged())); m_zClippingEnabledCheckBox = new QCheckBox("Z"); QObject::connect(m_zClippingEnabledCheckBox, SIGNAL(clicked(bool)), this, SLOT(clippingCheckBoxCheckStatusChanged())); m_surfaceClippingEnabledCheckBox = new QCheckBox("Surface"); QObject::connect(m_surfaceClippingEnabledCheckBox, SIGNAL(clicked(bool)), this, SLOT(clippingCheckBoxCheckStatusChanged())); m_volumeClippingEnabledCheckBox = new QCheckBox("Volume"); QObject::connect(m_volumeClippingEnabledCheckBox, SIGNAL(clicked(bool)), this, SLOT(clippingCheckBoxCheckStatusChanged())); m_featuresClippingEnabledCheckBox = new QCheckBox("Features"); QObject::connect(m_featuresClippingEnabledCheckBox, SIGNAL(clicked(bool)), this, SLOT(clippingCheckBoxCheckStatusChanged())); QToolButton* setupToolButton = new QToolButton(); setupToolButton->setText("Setup"); QObject::connect(setupToolButton, SIGNAL(clicked()), this, SLOT(setupClippingPushButtonClicked())); QGridLayout* gridLayout = new QGridLayout(this); gridLayout->setHorizontalSpacing(6); gridLayout->setVerticalSpacing(8); gridLayout->setContentsMargins(0, 0, 0, 0); int32_t rowIndex = gridLayout->rowCount(); gridLayout->addWidget(m_xClippingEnabledCheckBox, rowIndex, 0); gridLayout->addWidget(m_yClippingEnabledCheckBox, rowIndex, 1); gridLayout->addWidget(m_zClippingEnabledCheckBox, rowIndex, 2); rowIndex++; gridLayout->addWidget(m_featuresClippingEnabledCheckBox, rowIndex, 0, 1, 3); rowIndex++; gridLayout->addWidget(m_surfaceClippingEnabledCheckBox, rowIndex, 0, 1, 3); rowIndex++; gridLayout->addWidget(m_volumeClippingEnabledCheckBox, rowIndex, 0, 1, 3); rowIndex++; gridLayout->addWidget(setupToolButton, rowIndex, 0, 1, 3, Qt::AlignHCenter); // /* // * Layout: // * Column 1: Clipping X, Y, Z // * Column 2: Nothing but used as space // * Column 3: Type of data clipped. // */ // QGridLayout* checkboxGridLayout = new QGridLayout(); // WuQtUtilities::setLayoutSpacingAndMargins(checkboxGridLayout, 4, 0); // checkboxGridLayout->setColumnMinimumWidth(1, 15); // checkboxGridLayout->setColumnStretch(0, 0); // checkboxGridLayout->setColumnStretch(0, 1); // checkboxGridLayout->setColumnStretch(0, 2); // checkboxGridLayout->addWidget(m_xClippingEnabledCheckBox, 0, 0); // checkboxGridLayout->addWidget(m_yClippingEnabledCheckBox, 1, 0); // checkboxGridLayout->addWidget(m_zClippingEnabledCheckBox, 2, 0); // checkboxGridLayout->addWidget(m_surfaceClippingEnabledCheckBox, 0, 2); // checkboxGridLayout->addWidget(m_volumeClippingEnabledCheckBox, 1, 2); // checkboxGridLayout->addWidget(m_featuresClippingEnabledCheckBox, 2, 2); // // QVBoxLayout* layout = new QVBoxLayout(this); // WuQtUtilities::setLayoutSpacingAndMargins(layout, 0, 0); // layout->addLayout(checkboxGridLayout); // layout->addWidget(setupToolButton, 0, Qt::AlignHCenter); // layout->addStretch(); } /** * Destructor. */ BrainBrowserWindowToolBarClipping::~BrainBrowserWindowToolBarClipping() { } /** * Update the surface montage options widget. * * @param browserTabContent * The active model display controller (may be NULL). */ void BrainBrowserWindowToolBarClipping::updateContent(BrowserTabContent* browserTabContent) { bool xEnabled; bool yEnabled; bool zEnabled; bool surfaceEnabled; bool volumeEnabled; bool featuresEnabled; browserTabContent->getClippingPlaneEnabled(xEnabled, yEnabled, zEnabled, surfaceEnabled, volumeEnabled, featuresEnabled); m_xClippingEnabledCheckBox->setChecked(xEnabled); m_yClippingEnabledCheckBox->setChecked(yEnabled); m_zClippingEnabledCheckBox->setChecked(zEnabled); m_surfaceClippingEnabledCheckBox->setChecked(surfaceEnabled); m_volumeClippingEnabledCheckBox->setChecked(volumeEnabled); m_featuresClippingEnabledCheckBox->setChecked(featuresEnabled); } /** * Called when a clipping checkbox status is changed. */ void BrainBrowserWindowToolBarClipping::clippingCheckBoxCheckStatusChanged() { BrowserTabContent* browserTabContent = getTabContentFromSelectedTab(); if (browserTabContent != NULL) { browserTabContent->setClippingPlaneEnabled(m_xClippingEnabledCheckBox->isChecked(), m_yClippingEnabledCheckBox->isChecked(), m_zClippingEnabledCheckBox->isChecked(), m_surfaceClippingEnabledCheckBox->isChecked(), m_volumeClippingEnabledCheckBox->isChecked(), m_featuresClippingEnabledCheckBox->isChecked()); this->updateGraphicsWindow(); this->updateOtherYokedWindows(); } } /** * Called when the setup clipping push button is clicked. */ void BrainBrowserWindowToolBarClipping::setupClippingPushButtonClicked() { BrainBrowserWindow* browserWindow = GuiManager::get()->getBrowserWindowByWindowIndex(m_browserWindowIndex); GuiManager::get()->processShowClippingPlanesDialog(browserWindow); } connectome-workbench-1.2.3+git41-gc4c6c90/src/GuiQt/BrainBrowserWindowToolBarClipping.h000066400000000000000000000050661300200146000305150ustar00rootroot00000000000000#ifndef __BRAIN_BROWSER_WINDOW_TOOL_BAR_CLIPPING_H__ #define __BRAIN_BROWSER_WINDOW_TOOL_BAR_CLIPPING_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 "BrainBrowserWindowToolBarComponent.h" namespace caret { class BrainBrowserWindowToolBarClipping : public BrainBrowserWindowToolBarComponent { Q_OBJECT public: BrainBrowserWindowToolBarClipping(const int32_t browserWindowIndex, BrainBrowserWindowToolBar* parentToolBar); virtual ~BrainBrowserWindowToolBarClipping(); virtual void updateContent(BrowserTabContent* browserTabContent); // ADD_NEW_METHODS_HERE private slots: void clippingCheckBoxCheckStatusChanged(); void setupClippingPushButtonClicked(); private: BrainBrowserWindowToolBarClipping(const BrainBrowserWindowToolBarClipping&); BrainBrowserWindowToolBarClipping& operator=(const BrainBrowserWindowToolBarClipping&); const int32_t m_browserWindowIndex; BrainBrowserWindowToolBar* m_parentToolBar; QCheckBox* m_xClippingEnabledCheckBox; QCheckBox* m_yClippingEnabledCheckBox; QCheckBox* m_zClippingEnabledCheckBox; QCheckBox* m_surfaceClippingEnabledCheckBox; QCheckBox* m_volumeClippingEnabledCheckBox; QCheckBox* m_featuresClippingEnabledCheckBox; // ADD_NEW_MEMBERS_HERE }; #ifdef __BRAIN_BROWSER_WINDOW_TOOL_BAR_CLIPPING_DECLARE__ // #endif // __BRAIN_BROWSER_WINDOW_TOOL_BAR_CLIPPING_DECLARE__ } // namespace #endif //__BRAIN_BROWSER_WINDOW_TOOL_BAR_CLIPPING_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/GuiQt/BrainBrowserWindowToolBarComponent.cxx000066400000000000000000000074621300200146000312670ustar00rootroot00000000000000 /*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_COMPONENT_DECLARE__ #include "BrainBrowserWindowToolBarComponent.h" #undef __BRAIN_BROWSER_WINDOW_TOOL_BAR_COMPONENT_DECLARE__ #include "BrainBrowserWindowToolBar.h" #include "CaretAssert.h" #include "EventManager.h" #include "EventSurfaceColoringInvalidate.h" #include "WuQWidgetObjectGroup.h" using namespace caret; /** * \class caret::BrainBrowserWindowToolBarComponent * \brief Abstract class for brain browser window tool bar components. * \ingroup GuiQt * */ /** * Constructor. * * @param parent * Parent widget. */ BrainBrowserWindowToolBarComponent::BrainBrowserWindowToolBarComponent(BrainBrowserWindowToolBar* parentToolBar) : QWidget(parentToolBar), m_parentToolBar(parentToolBar) { m_widgetGroup = new WuQWidgetObjectGroup(this); // EventManager::get()->addEventListener(this, EventTypeEnum::); } /** * Destructor. */ BrainBrowserWindowToolBarComponent::~BrainBrowserWindowToolBarComponent() { m_widgetGroup->clear(); EventManager::get()->removeAllEventsFromListener(this); } /** * Receive an event. * * @param event * An event for which this instance is listening. */ void BrainBrowserWindowToolBarComponent::receiveEvent(Event* /*event*/) { // if (event->getEventType() == EventTypeEnum::) { // eventName = dynamic_cast(event); // CaretAssert(eventName); // // event->setEventProcessed(); // } } /** * Add a widget/object to the widget group so that all items can be * enabled or disabled. * * @param qObject * QObject or QWidget subclass added to widget group. */ void BrainBrowserWindowToolBarComponent::addToWidgetGroup(QObject* qObject) { CaretAssert(qObject); m_widgetGroup->add(qObject); } /** * Block/unblock signals for components in the widget group. * * @param blocked * New status for blocked (true/false). */ void BrainBrowserWindowToolBarComponent::blockAllSignals(const bool blocked) { m_widgetGroup->blockAllSignals(blocked); } /** * @return The browser tab content for the selected tab. */ BrowserTabContent* BrainBrowserWindowToolBarComponent::getTabContentFromSelectedTab() { return m_parentToolBar->getTabContentFromSelectedTab(); } /** * Invalidate surface coloring and update the graphics in the * window containing this toolbar. */ void BrainBrowserWindowToolBarComponent::invalidateColoringAndUpdateGraphicsWindow() { EventManager::get()->sendEvent(EventSurfaceColoringInvalidate().getPointer()); updateGraphicsWindow(); } /** * Update the graphics window. */ void BrainBrowserWindowToolBarComponent::updateGraphicsWindow() { m_parentToolBar->updateGraphicsWindow(); } /** * Update toolbars in other yoked windows. */ void BrainBrowserWindowToolBarComponent::updateOtherYokedWindows() { m_parentToolBar->updateOtherYokedWindows(); } /** * Update the user interface. */ void BrainBrowserWindowToolBarComponent::updateUserInterface() { m_parentToolBar->updateUserInterface(); } connectome-workbench-1.2.3+git41-gc4c6c90/src/GuiQt/BrainBrowserWindowToolBarComponent.h000066400000000000000000000051321300200146000307040ustar00rootroot00000000000000#ifndef __BRAIN_BROWSER_WINDOW_TOOL_BAR_COMPONENT_H__ #define __BRAIN_BROWSER_WINDOW_TOOL_BAR_COMPONENT_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 "EventListenerInterface.h" namespace caret { class BrainBrowserWindowToolBar; class BrowserTabContent; class WuQWidgetObjectGroup; class BrainBrowserWindowToolBarComponent : public QWidget, public EventListenerInterface { Q_OBJECT protected: BrainBrowserWindowToolBarComponent(BrainBrowserWindowToolBar* parentToolBar); public: virtual ~BrainBrowserWindowToolBarComponent(); virtual void updateContent(BrowserTabContent* browserTabContent) = 0; BrowserTabContent* getTabContentFromSelectedTab(); void invalidateColoringAndUpdateGraphicsWindow(); void updateGraphicsWindow(); void updateOtherYokedWindows(); void updateUserInterface(); private: BrainBrowserWindowToolBarComponent(const BrainBrowserWindowToolBarComponent&); BrainBrowserWindowToolBarComponent& operator=(const BrainBrowserWindowToolBarComponent&); protected: void addToWidgetGroup(QObject* qObject); void blockAllSignals(const bool status); public: // ADD_NEW_METHODS_HERE virtual void receiveEvent(Event* event); private: BrainBrowserWindowToolBar* m_parentToolBar; WuQWidgetObjectGroup* m_widgetGroup; // ADD_NEW_MEMBERS_HERE }; #ifdef __BRAIN_BROWSER_WINDOW_TOOL_BAR_COMPONENT_DECLARE__ // #endif // __BRAIN_BROWSER_WINDOW_TOOL_BAR_COMPONENT_DECLARE__ } // namespace #endif //__BRAIN_BROWSER_WINDOW_TOOL_BAR_COMPONENT_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/GuiQt/BrainBrowserWindowToolBarSlicePlane.cxx000066400000000000000000000236611300200146000313430ustar00rootroot00000000000000 /*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_SLICE_PLANE_DECLARE__ #include "BrainBrowserWindowToolBarSlicePlane.h" #undef __BRAIN_BROWSER_WINDOW_TOOL_BAR_SLICE_PLANE_DECLARE__ #include #include #include #include #include "BrainBrowserWindowToolBar.h" #include "BrowserTabContent.h" #include "CaretAssert.h" #include "CaretLogger.h" #include "WuQWidgetObjectGroup.h" #include "WuQtUtilities.h" using namespace caret; /** * \class caret::BrainBrowserWindowToolBarSlicePlane * \brief Toolbar component for volume slice plane and projection selection. * \ingroup GuiQt */ /** * Constructor. */ BrainBrowserWindowToolBarSlicePlane::BrainBrowserWindowToolBarSlicePlane(BrainBrowserWindowToolBar* parentToolBar) : BrainBrowserWindowToolBarComponent(parentToolBar), m_parentToolBar(parentToolBar) { QIcon parasagittalIcon; const bool parasagittalIconValid = WuQtUtilities::loadIcon(":/ToolBar/view-plane-parasagittal.png", parasagittalIcon); QIcon coronalIcon; const bool coronalIconValid = WuQtUtilities::loadIcon(":/ToolBar/view-plane-coronal.png", coronalIcon); QIcon axialIcon; const bool axialIconValid = WuQtUtilities::loadIcon(":/ToolBar/view-plane-axial.png", axialIcon); m_volumePlaneParasagittalToolButtonAction = WuQtUtilities::createAction(VolumeSliceViewPlaneEnum::toGuiNameAbbreviation(VolumeSliceViewPlaneEnum::PARASAGITTAL), "View the PARASAGITTAL slice", this); m_volumePlaneParasagittalToolButtonAction->setCheckable(true); if (parasagittalIconValid) { m_volumePlaneParasagittalToolButtonAction->setIcon(parasagittalIcon); } m_volumePlaneCoronalToolButtonAction = WuQtUtilities::createAction(VolumeSliceViewPlaneEnum::toGuiNameAbbreviation(VolumeSliceViewPlaneEnum::CORONAL), "View the CORONAL slice", this); m_volumePlaneCoronalToolButtonAction->setCheckable(true); if (coronalIconValid) { m_volumePlaneCoronalToolButtonAction->setIcon(coronalIcon); } m_volumePlaneAxialToolButtonAction = WuQtUtilities::createAction(VolumeSliceViewPlaneEnum::toGuiNameAbbreviation(VolumeSliceViewPlaneEnum::AXIAL), "View the AXIAL slice", this); m_volumePlaneAxialToolButtonAction->setCheckable(true); if (axialIconValid) { m_volumePlaneAxialToolButtonAction->setIcon(axialIcon); } m_volumePlaneAllToolButtonAction = WuQtUtilities::createAction(VolumeSliceViewPlaneEnum::toGuiNameAbbreviation(VolumeSliceViewPlaneEnum::ALL), "View the PARASAGITTAL, CORONAL, and AXIAL slices", this); m_volumePlaneAllToolButtonAction->setCheckable(true); m_volumePlaneActionGroup = new QActionGroup(this); m_volumePlaneActionGroup->addAction(m_volumePlaneParasagittalToolButtonAction); m_volumePlaneActionGroup->addAction(m_volumePlaneCoronalToolButtonAction); m_volumePlaneActionGroup->addAction(m_volumePlaneAxialToolButtonAction); m_volumePlaneActionGroup->addAction(m_volumePlaneAllToolButtonAction); m_volumePlaneActionGroup->setExclusive(true); QObject::connect(m_volumePlaneActionGroup, SIGNAL(triggered(QAction*)), this, SLOT(volumePlaneActionGroupTriggered(QAction*))); m_volumePlaneResetToolButtonAction = WuQtUtilities::createAction("Reset", "Reset to remove panning, zooming, and/or oblique rotation", this, this, SLOT(volumePlaneResetToolButtonTriggered(bool))); QToolButton* volumePlaneParasagittalToolButton = new QToolButton(); volumePlaneParasagittalToolButton->setDefaultAction(m_volumePlaneParasagittalToolButtonAction); QToolButton* volumePlaneCoronalToolButton = new QToolButton(); volumePlaneCoronalToolButton->setDefaultAction(m_volumePlaneCoronalToolButtonAction); QToolButton* volumePlaneAxialToolButton = new QToolButton(); volumePlaneAxialToolButton->setDefaultAction(m_volumePlaneAxialToolButtonAction); QToolButton* volumePlaneAllToolButton = new QToolButton(); volumePlaneAllToolButton->setDefaultAction(m_volumePlaneAllToolButtonAction); QToolButton* volumePlaneResetToolButton = new QToolButton(); volumePlaneResetToolButton->setDefaultAction(m_volumePlaneResetToolButtonAction); WuQtUtilities::matchWidgetHeights(volumePlaneParasagittalToolButton, volumePlaneCoronalToolButton, volumePlaneAxialToolButton, volumePlaneAllToolButton); WuQtUtilities::matchWidgetWidths(volumePlaneParasagittalToolButton, volumePlaneCoronalToolButton, volumePlaneAxialToolButton, volumePlaneAllToolButton); QToolButton* slicePlaneCustomToolButton = new QToolButton(); slicePlaneCustomToolButton->setDefaultAction(m_parentToolBar->customViewAction); slicePlaneCustomToolButton->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Fixed); QGridLayout* gridLayout = new QGridLayout(this); WuQtUtilities::setLayoutSpacingAndMargins(gridLayout, 0, 0); int32_t rowIndex = gridLayout->rowCount(); gridLayout->addWidget(volumePlaneParasagittalToolButton, rowIndex, 0); gridLayout->addWidget(volumePlaneCoronalToolButton, rowIndex, 1); rowIndex++; gridLayout->addWidget(volumePlaneAxialToolButton, rowIndex, 0); gridLayout->addWidget(volumePlaneAllToolButton, rowIndex, 1); rowIndex++; gridLayout->addWidget(volumePlaneResetToolButton, rowIndex, 0, 1, 2, Qt::AlignHCenter); rowIndex++; gridLayout->addWidget(slicePlaneCustomToolButton, rowIndex, 0, 1, 2, Qt::AlignHCenter); m_volumePlaneWidgetGroup = new WuQWidgetObjectGroup(this); m_volumePlaneWidgetGroup->add(m_volumePlaneActionGroup); m_volumePlaneWidgetGroup->add(m_volumePlaneResetToolButtonAction); } /** * Destructor. */ BrainBrowserWindowToolBarSlicePlane::~BrainBrowserWindowToolBarSlicePlane() { } /** * Update the surface montage options widget. * * @param browserTabContent * The active model display controller (may be NULL). */ void BrainBrowserWindowToolBarSlicePlane::updateContent(BrowserTabContent* browserTabContent) { m_volumePlaneWidgetGroup->blockAllSignals(true); switch (browserTabContent->getSliceViewPlane()) { case VolumeSliceViewPlaneEnum::ALL: m_volumePlaneAllToolButtonAction->setChecked(true); break; case VolumeSliceViewPlaneEnum::AXIAL: m_volumePlaneAxialToolButtonAction->setChecked(true); break; case VolumeSliceViewPlaneEnum::CORONAL: m_volumePlaneCoronalToolButtonAction->setChecked(true); break; case VolumeSliceViewPlaneEnum::PARASAGITTAL: m_volumePlaneParasagittalToolButtonAction->setChecked(true); break; } m_volumePlaneWidgetGroup->blockAllSignals(false); } /** * Called when volume slice plane button is clicked. */ void BrainBrowserWindowToolBarSlicePlane::volumePlaneActionGroupTriggered(QAction* action) { VolumeSliceViewPlaneEnum::Enum plane = VolumeSliceViewPlaneEnum::AXIAL; if (action == m_volumePlaneAllToolButtonAction) { plane = VolumeSliceViewPlaneEnum::ALL; } else if (action == m_volumePlaneAxialToolButtonAction) { plane = VolumeSliceViewPlaneEnum::AXIAL; } else if (action == m_volumePlaneCoronalToolButtonAction) { plane = VolumeSliceViewPlaneEnum::CORONAL; } else if (action == m_volumePlaneParasagittalToolButtonAction) { plane = VolumeSliceViewPlaneEnum::PARASAGITTAL; } else { CaretLogSevere("Invalid volume plane action: " + action->text()); } BrowserTabContent* btc = getTabContentFromSelectedTab(); btc->setSliceViewPlane(plane); m_parentToolBar->updateVolumeIndicesWidget(btc); updateGraphicsWindow(); updateOtherYokedWindows(); } /** * Called when volume reset slice view button is pressed. */ void BrainBrowserWindowToolBarSlicePlane::volumePlaneResetToolButtonTriggered(bool /*checked*/) { BrowserTabContent* btc = getTabContentFromSelectedTab(); btc->resetView(); m_parentToolBar->updateVolumeIndicesWidget(btc); updateGraphicsWindow(); updateOtherYokedWindows(); } connectome-workbench-1.2.3+git41-gc4c6c90/src/GuiQt/BrainBrowserWindowToolBarSlicePlane.h000066400000000000000000000050641300200146000307650ustar00rootroot00000000000000#ifndef __BRAIN_BROWSER_WINDOW_TOOL_BAR_SLICE_PLANE_H__ #define __BRAIN_BROWSER_WINDOW_TOOL_BAR_SLICE_PLANE_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" class QActionGroup; namespace caret { class WuQWidgetObjectGroup; class BrainBrowserWindowToolBarSlicePlane : public BrainBrowserWindowToolBarComponent { Q_OBJECT public: BrainBrowserWindowToolBarSlicePlane(BrainBrowserWindowToolBar* parentToolBar); virtual ~BrainBrowserWindowToolBarSlicePlane(); virtual void updateContent(BrowserTabContent* browserTabContent); // ADD_NEW_METHODS_HERE private slots: void volumePlaneActionGroupTriggered(QAction*); void volumePlaneResetToolButtonTriggered(bool checked); private: BrainBrowserWindowToolBarSlicePlane(const BrainBrowserWindowToolBarSlicePlane&); BrainBrowserWindowToolBarSlicePlane& operator=(const BrainBrowserWindowToolBarSlicePlane&); BrainBrowserWindowToolBar* m_parentToolBar; WuQWidgetObjectGroup* m_volumePlaneWidgetGroup; QAction* m_volumePlaneParasagittalToolButtonAction; QAction* m_volumePlaneCoronalToolButtonAction; QAction* m_volumePlaneAxialToolButtonAction; QAction* m_volumePlaneAllToolButtonAction; QAction* m_volumePlaneResetToolButtonAction; QActionGroup* m_volumePlaneActionGroup; // ADD_NEW_MEMBERS_HERE }; #ifdef __BRAIN_BROWSER_WINDOW_TOOL_BAR_SLICE_PLANE_DECLARE__ // #endif // __BRAIN_BROWSER_WINDOW_TOOL_BAR_SLICE_PLANE_DECLARE__ } // namespace #endif //__BRAIN_BROWSER_WINDOW_TOOL_BAR_SLICE_PLANE_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/GuiQt/BrainBrowserWindowToolBarSliceSelection.cxx000066400000000000000000000614651300200146000322350ustar00rootroot00000000000000 /*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_BROWSER_WINDOW_TOOL_BAR_SLICE_SELECTION_DECLARE__ #include "BrainBrowserWindowToolBarSliceSelection.h" #undef __BRAIN_BROWSER_WINDOW_TOOL_BAR_SLICE_SELECTION_DECLARE__ #include #include #include #include #include #include #include #include "BrainBrowserWindowToolBar.h" #include "BrowserTabContent.h" #include "CaretAssert.h" #include "EnumComboBoxTemplate.h" #include "EventManager.h" #include "EventUpdateVolumeEditingToolBar.h" #include "GuiManager.h" #include "ModelVolume.h" #include "ModelWholeBrain.h" #include "VolumeFile.h" #include "VolumeSliceProjectionTypeEnum.h" #include "WuQFactory.h" #include "WuQWidgetObjectGroup.h" #include "WuQtUtilities.h" using namespace caret; /** * \class caret::BrainBrowserWindowToolBarSliceSelection * \brief Toolbar component for selection of volume slices. * \ingroup GuiQt */ /** * Constructor. */ BrainBrowserWindowToolBarSliceSelection::BrainBrowserWindowToolBarSliceSelection(BrainBrowserWindowToolBar* parentToolBar) : BrainBrowserWindowToolBarComponent(parentToolBar), m_parentToolBar(parentToolBar) { QAction* volumeIndicesOriginToolButtonAction = WuQtUtilities::createAction("O\nR\nI\nG\nI\nN", "Set the slice indices to the origin, \n" "stereotaxic coordinate (0, 0, 0)", this, this, SLOT(volumeIndicesOriginActionTriggered())); QToolButton* volumeIndicesOriginToolButton = new QToolButton; volumeIndicesOriginToolButton->setDefaultAction(volumeIndicesOriginToolButtonAction); QLabel* parasagittalLabel = new QLabel("P:"); QLabel* coronalLabel = new QLabel("C:"); QLabel* axialLabel = new QLabel("A:"); m_volumeIndicesParasagittalCheckBox = new QCheckBox(" "); WuQtUtilities::setToolTipAndStatusTip(m_volumeIndicesParasagittalCheckBox, "Enable/Disable display of PARASAGITTAL slice"); QObject::connect(m_volumeIndicesParasagittalCheckBox, SIGNAL(stateChanged(int)), this, SLOT(volumeIndicesParasagittalCheckBoxStateChanged(int))); m_volumeIndicesCoronalCheckBox = new QCheckBox(" "); WuQtUtilities::setToolTipAndStatusTip(m_volumeIndicesCoronalCheckBox, "Enable/Disable display of CORONAL slice"); QObject::connect(m_volumeIndicesCoronalCheckBox, SIGNAL(stateChanged(int)), this, SLOT(volumeIndicesCoronalCheckBoxStateChanged(int))); m_volumeIndicesAxialCheckBox = new QCheckBox(" "); WuQtUtilities::setToolTipAndStatusTip(m_volumeIndicesAxialCheckBox, "Enable/Disable display of AXIAL slice"); QObject::connect(m_volumeIndicesAxialCheckBox, SIGNAL(stateChanged(int)), this, SLOT(volumeIndicesAxialCheckBoxStateChanged(int))); const int sliceIndexSpinBoxWidth = 55; const int sliceCoordinateSpinBoxWidth = 60; m_volumeIndicesParasagittalSpinBox = WuQFactory::newSpinBox(); m_volumeIndicesParasagittalSpinBox->setFixedWidth(sliceIndexSpinBoxWidth); WuQtUtilities::setToolTipAndStatusTip(m_volumeIndicesParasagittalSpinBox, "Change the selected PARASAGITTAL slice"); QObject::connect(m_volumeIndicesParasagittalSpinBox, SIGNAL(valueChanged(int)), this, SLOT(volumeIndicesParasagittalSpinBoxValueChanged(int))); m_volumeIndicesCoronalSpinBox = WuQFactory::newSpinBox(); m_volumeIndicesCoronalSpinBox->setFixedWidth(sliceIndexSpinBoxWidth); WuQtUtilities::setToolTipAndStatusTip(m_volumeIndicesCoronalSpinBox, "Change the selected CORONAL slice"); QObject::connect(m_volumeIndicesCoronalSpinBox, SIGNAL(valueChanged(int)), this, SLOT(volumeIndicesCoronalSpinBoxValueChanged(int))); m_volumeIndicesAxialSpinBox = WuQFactory::newSpinBox(); m_volumeIndicesAxialSpinBox->setFixedWidth(sliceIndexSpinBoxWidth); WuQtUtilities::setToolTipAndStatusTip(m_volumeIndicesAxialSpinBox, "Change the selected AXIAL slice"); QObject::connect(m_volumeIndicesAxialSpinBox, SIGNAL(valueChanged(int)), this, SLOT(volumeIndicesAxialSpinBoxValueChanged(int))); m_volumeIndicesXcoordSpinBox = WuQFactory::newDoubleSpinBox(); m_volumeIndicesXcoordSpinBox->setDecimals(1); m_volumeIndicesXcoordSpinBox->setFixedWidth(sliceCoordinateSpinBoxWidth); WuQtUtilities::setToolTipAndStatusTip(m_volumeIndicesXcoordSpinBox, "Adjust coordinate to select PARASAGITTAL slice"); QObject::connect(m_volumeIndicesXcoordSpinBox, SIGNAL(valueChanged(double)), this, SLOT(volumeIndicesXcoordSpinBoxValueChanged(double))); m_volumeIndicesYcoordSpinBox = WuQFactory::newDoubleSpinBox(); m_volumeIndicesYcoordSpinBox->setDecimals(1); m_volumeIndicesYcoordSpinBox->setFixedWidth(sliceCoordinateSpinBoxWidth); WuQtUtilities::setToolTipAndStatusTip(m_volumeIndicesYcoordSpinBox, "Adjust coordinate to select CORONAL slice"); QObject::connect(m_volumeIndicesYcoordSpinBox, SIGNAL(valueChanged(double)), this, SLOT(volumeIndicesYcoordSpinBoxValueChanged(double))); m_volumeIndicesZcoordSpinBox = WuQFactory::newDoubleSpinBox(); m_volumeIndicesZcoordSpinBox->setDecimals(1); m_volumeIndicesZcoordSpinBox->setFixedWidth(sliceCoordinateSpinBoxWidth); WuQtUtilities::setToolTipAndStatusTip(m_volumeIndicesZcoordSpinBox, "Adjust coordinate to select AXIAL slice"); QObject::connect(m_volumeIndicesZcoordSpinBox, SIGNAL(valueChanged(double)), this, SLOT(volumeIndicesZcoordSpinBoxValueChanged(double))); const AString idToolTipText = ("When selected: If there is an identification operation " "in ths tab or any other tab with the same yoking status " "(not Off), the volume slices will move to the location " "of the identified brainordinate."); m_volumeIdentificationUpdatesSlicesAction = WuQtUtilities::createAction("", WuQtUtilities::createWordWrappedToolTipText(idToolTipText), this, this, SLOT(volumeIdentificationToggled(bool))); m_volumeIdentificationUpdatesSlicesAction->setCheckable(true); QIcon volumeCrossHairIcon; const bool volumeCrossHairIconValid = WuQtUtilities::loadIcon(":/ToolBar/volume-crosshair-pointer.png", volumeCrossHairIcon); if (volumeCrossHairIconValid) { m_volumeIdentificationUpdatesSlicesAction->setIcon(volumeCrossHairIcon); } else { m_volumeIdentificationUpdatesSlicesAction->setText("ID"); } QToolButton* volumeIDToolButton = new QToolButton; volumeIDToolButton->setDefaultAction(m_volumeIdentificationUpdatesSlicesAction); m_volumeSliceProjectionTypeEnumComboBox = new EnumComboBoxTemplate(this); m_volumeSliceProjectionTypeEnumComboBox->setup(); m_volumeSliceProjectionTypeEnumComboBox->getComboBox()->setSizeAdjustPolicy(QComboBox::AdjustToContentsOnFirstShow); QObject::connect(m_volumeSliceProjectionTypeEnumComboBox, SIGNAL(itemActivated()), this, SLOT(volumeSliceProjectionTypeEnumComboBoxItemActivated())); WuQtUtilities::setToolTipAndStatusTip(m_volumeSliceProjectionTypeEnumComboBox->getWidget(), "Chooses viewing orientation (oblique or orthogonal)"); QGridLayout* gridLayout = new QGridLayout(this); WuQtUtilities::setLayoutSpacingAndMargins(gridLayout, 0, 0); gridLayout->addWidget(m_volumeIndicesParasagittalCheckBox, 0, 0); gridLayout->addWidget(parasagittalLabel, 0, 1); gridLayout->addWidget(m_volumeIndicesCoronalCheckBox, 1, 0); gridLayout->addWidget(coronalLabel, 1, 1); gridLayout->addWidget(m_volumeIndicesAxialCheckBox, 2, 0); gridLayout->addWidget(axialLabel, 2, 1); gridLayout->addWidget(m_volumeIndicesParasagittalSpinBox, 0, 2); gridLayout->addWidget(m_volumeIndicesCoronalSpinBox, 1, 2); gridLayout->addWidget(m_volumeIndicesAxialSpinBox, 2, 2); gridLayout->addWidget(m_volumeIndicesXcoordSpinBox, 0, 3); gridLayout->addWidget(m_volumeIndicesYcoordSpinBox, 1, 3); 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(volumeIndicesOriginToolButton, 0, 4, 3, 1); m_volumeIndicesWidgetGroup = new WuQWidgetObjectGroup(this); m_volumeIndicesWidgetGroup->add(volumeIndicesOriginToolButtonAction); m_volumeIndicesWidgetGroup->add(m_volumeIndicesParasagittalCheckBox); m_volumeIndicesWidgetGroup->add(m_volumeIndicesParasagittalSpinBox); m_volumeIndicesWidgetGroup->add(m_volumeIndicesCoronalCheckBox); m_volumeIndicesWidgetGroup->add(m_volumeIndicesCoronalSpinBox); m_volumeIndicesWidgetGroup->add(m_volumeIndicesAxialCheckBox); m_volumeIndicesWidgetGroup->add(m_volumeIndicesAxialSpinBox); m_volumeIndicesWidgetGroup->add(m_volumeIndicesXcoordSpinBox); m_volumeIndicesWidgetGroup->add(m_volumeIndicesYcoordSpinBox); m_volumeIndicesWidgetGroup->add(m_volumeIndicesZcoordSpinBox); m_volumeIndicesWidgetGroup->add(m_volumeIdentificationUpdatesSlicesAction); } /** * Destructor. */ BrainBrowserWindowToolBarSliceSelection::~BrainBrowserWindowToolBarSliceSelection() { } /** * Update the surface montage options widget. * * @param browserTabContent * The active model display controller (may be NULL). */ void BrainBrowserWindowToolBarSliceSelection::updateContent(BrowserTabContent* browserTabContent) { m_volumeIndicesWidgetGroup->blockAllSignals(true); const int32_t tabIndex = browserTabContent->getTabNumber(); VolumeMappableInterface* vf = NULL; ModelVolume* volumeModel = browserTabContent->getDisplayedVolumeModel(); if (volumeModel != NULL) { if (m_parentToolBar->getDisplayedModel() == volumeModel) { vf = volumeModel->getUnderlayVolumeFile(tabIndex); m_volumeIndicesAxialCheckBox->setVisible(false); m_volumeIndicesCoronalCheckBox->setVisible(false); m_volumeIndicesParasagittalCheckBox->setVisible(false); } } ModelWholeBrain* wholeBrainModel = browserTabContent->getDisplayedWholeBrainModel(); if (wholeBrainModel != NULL) { if (m_parentToolBar->getDisplayedModel() == wholeBrainModel) { vf = wholeBrainModel->getUnderlayVolumeFile(tabIndex); m_volumeIndicesAxialCheckBox->setVisible(true); m_volumeIndicesCoronalCheckBox->setVisible(true); m_volumeIndicesParasagittalCheckBox->setVisible(true); } } if (vf != NULL) { m_volumeIndicesAxialCheckBox->setChecked(browserTabContent->isSliceAxialEnabled()); m_volumeIndicesCoronalCheckBox->setChecked(browserTabContent->isSliceCoronalEnabled()); m_volumeIndicesParasagittalCheckBox->setChecked(browserTabContent->isSliceParasagittalEnabled()); } m_volumeSliceProjectionTypeEnumComboBox->setSelectedItem(browserTabContent->getSliceProjectionType()); m_volumeIdentificationUpdatesSlicesAction->setChecked(browserTabContent->isIdentificationUpdatesVolumeSlices()); this->updateSliceIndicesAndCoordinatesRanges(); m_volumeIndicesWidgetGroup->blockAllSignals(false); } /* * Set the values/minimums/maximums for volume slice indices and coordinate spin controls. */ void BrainBrowserWindowToolBarSliceSelection::updateSliceIndicesAndCoordinatesRanges() { const bool blockedStatus = m_volumeIndicesWidgetGroup->signalsBlocked(); m_volumeIndicesWidgetGroup->blockAllSignals(true); BrowserTabContent* btc = this->getTabContentFromSelectedTab(); const int32_t tabIndex = btc->getTabNumber(); VolumeMappableInterface* vf = NULL; ModelVolume* volumeModel = btc->getDisplayedVolumeModel(); if (volumeModel != NULL) { vf = volumeModel->getUnderlayVolumeFile(tabIndex); } ModelWholeBrain* wholeBrainModel = btc->getDisplayedWholeBrainModel(); if (wholeBrainModel != NULL) { vf = wholeBrainModel->getUnderlayVolumeFile(tabIndex); } if (vf != NULL) { m_volumeIndicesAxialSpinBox->setEnabled(true); m_volumeIndicesCoronalSpinBox->setEnabled(true); m_volumeIndicesParasagittalSpinBox->setEnabled(true); std::vector dimensions; vf->getDimensions(dimensions); /* * Setup minimum and maximum slices for each dimension. * Range is unlimited when Yoked. */ int minAxialDim = 0; int minCoronalDim = 0; int minParasagittalDim = 0; int maxAxialDim = (dimensions[2] > 0) ? (dimensions[2] - 1) : 0; 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); /* * Setup minimum and maximum coordinates for each dimension. * Range is unlimited when Yoked. */ int64_t slicesZero[3] = { 0, 0, 0 }; float sliceZeroCoords[3]; vf->indexToSpace(slicesZero, sliceZeroCoords); int64_t slicesMax[3] = { maxParasagittalDim, maxCoronalDim, maxAxialDim }; float sliceMaxCoords[3]; 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])); 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])); int64_t slicesOne[3] = { 1, 1, 1 }; float slicesOneCoords[3]; vf->indexToSpace(slicesOne, slicesOneCoords); const float dx = std::fabs(slicesOneCoords[0] - sliceZeroCoords[0]); const float dy = std::fabs(slicesOneCoords[1] - sliceZeroCoords[1]); const float dz = std::fabs(slicesOneCoords[2] - sliceZeroCoords[2]); m_volumeIndicesXcoordSpinBox->setSingleStep(dx); m_volumeIndicesYcoordSpinBox->setSingleStep(dy); m_volumeIndicesZcoordSpinBox->setSingleStep(dz); m_volumeIndicesAxialSpinBox->setValue(btc->getSliceIndexAxial(vf)); m_volumeIndicesCoronalSpinBox->setValue(btc->getSliceIndexCoronal(vf)); m_volumeIndicesParasagittalSpinBox->setValue(btc->getSliceIndexParasagittal(vf)); int64_t slices[3] = { btc->getSliceIndexParasagittal(vf), btc->getSliceIndexCoronal(vf), btc->getSliceIndexAxial(vf) }; float sliceCoords[3] = { 0.0, 0.0, 0.0 }; if (vf != NULL) { vf->indexToSpace(slices, sliceCoords); } m_volumeIndicesXcoordSpinBox->setValue(btc->getSliceCoordinateParasagittal()); m_volumeIndicesYcoordSpinBox->setValue(btc->getSliceCoordinateCoronal()); m_volumeIndicesZcoordSpinBox->setValue(btc->getSliceCoordinateAxial()); } m_volumeIndicesWidgetGroup->blockAllSignals(blockedStatus); } /** * Called when volume indices ORIGIN tool button is pressed. */ void BrainBrowserWindowToolBarSliceSelection::volumeIndicesOriginActionTriggered() { BrowserTabContent* btc = this->getTabContentFromSelectedTab(); btc->setSlicesToOrigin(); updateContent(btc); this->updateGraphicsWindow(); this->updateOtherYokedWindows(); } /** * Called when volume indices parasagittal check box is toggled. */ void BrainBrowserWindowToolBarSliceSelection::volumeIndicesParasagittalCheckBoxStateChanged(int /*state*/) { BrowserTabContent* btc = this->getTabContentFromSelectedTab(); btc->setSliceParasagittalEnabled(m_volumeIndicesParasagittalCheckBox->isChecked()); this->updateGraphicsWindow(); this->updateOtherYokedWindows(); } /** * Called when volume indices coronal check box is toggled. */ void BrainBrowserWindowToolBarSliceSelection::volumeIndicesCoronalCheckBoxStateChanged(int /*state*/) { BrowserTabContent* btc = this->getTabContentFromSelectedTab(); btc->setSliceCoronalEnabled(m_volumeIndicesCoronalCheckBox->isChecked()); this->updateGraphicsWindow(); this->updateOtherYokedWindows(); } /** * Called when volume indices axial check box is toggled. */ void BrainBrowserWindowToolBarSliceSelection::volumeIndicesAxialCheckBoxStateChanged(int /*state*/) { BrowserTabContent* btc = this->getTabContentFromSelectedTab(); btc->setSliceAxialEnabled(m_volumeIndicesAxialCheckBox->isChecked()); this->updateGraphicsWindow(); this->updateOtherYokedWindows(); } /** * Called when volume indices parasagittal spin box value is changed. */ void BrainBrowserWindowToolBarSliceSelection::volumeIndicesParasagittalSpinBoxValueChanged(int /*i*/) { this->readVolumeSliceIndicesAndUpdateSliceCoordinates(); } /** * Called when volume indices coronal spin box value is changed. */ void BrainBrowserWindowToolBarSliceSelection::volumeIndicesCoronalSpinBoxValueChanged(int /*i*/) { this->readVolumeSliceIndicesAndUpdateSliceCoordinates(); } /** * Called when volume indices axial spin box value is changed. */ void BrainBrowserWindowToolBarSliceSelection::volumeIndicesAxialSpinBoxValueChanged(int /*i*/) { this->readVolumeSliceIndicesAndUpdateSliceCoordinates(); } /** * Called when X stereotaxic coordinate is changed. * @param d * New value. */ void BrainBrowserWindowToolBarSliceSelection::volumeIndicesXcoordSpinBoxValueChanged(double /*d*/) { this->readVolumeSliceCoordinatesAndUpdateSliceIndices(); } /** * Called when Y stereotaxic coordinate is changed. * @param d * New value. */ void BrainBrowserWindowToolBarSliceSelection::volumeIndicesYcoordSpinBoxValueChanged(double /*d*/) { this->readVolumeSliceCoordinatesAndUpdateSliceIndices(); } /** * Called when Z stereotaxic coordinate is changed. * @param d * New value. */ void BrainBrowserWindowToolBarSliceSelection::volumeIndicesZcoordSpinBoxValueChanged(double /*d*/) { this->readVolumeSliceCoordinatesAndUpdateSliceIndices(); } /** * Read the slice indices and update the slice coordinates. */ void BrainBrowserWindowToolBarSliceSelection::readVolumeSliceIndicesAndUpdateSliceCoordinates() { BrowserTabContent* btc = this->getTabContentFromSelectedTab(); const int32_t tabIndex = btc->getTabNumber(); VolumeMappableInterface* underlayVolumeFile = NULL; ModelWholeBrain* wholeBrainModel = btc->getDisplayedWholeBrainModel(); if (wholeBrainModel != NULL) { if (m_parentToolBar->getDisplayedModel() == wholeBrainModel) { underlayVolumeFile = wholeBrainModel->getUnderlayVolumeFile(tabIndex); } } ModelVolume* volumeModel = btc->getDisplayedVolumeModel(); if (volumeModel != NULL) { if (m_parentToolBar->getDisplayedModel() == volumeModel) { underlayVolumeFile = volumeModel->getUnderlayVolumeFile(tabIndex); } } 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); } this->updateSliceIndicesAndCoordinatesRanges(); this->updateGraphicsWindow(); this->updateOtherYokedWindows(); } /** * Read the slice coordinates and convert to slices indices and then * update the displayed slices. */ void BrainBrowserWindowToolBarSliceSelection::readVolumeSliceCoordinatesAndUpdateSliceIndices() { BrowserTabContent* btc = this->getTabContentFromSelectedTab(); const int32_t tabIndex = btc->getTabNumber(); VolumeMappableInterface* underlayVolumeFile = NULL; ModelWholeBrain* wholeBrainModel = btc->getDisplayedWholeBrainModel(); if (wholeBrainModel != NULL) { if (m_parentToolBar->getDisplayedModel() == wholeBrainModel) { underlayVolumeFile = wholeBrainModel->getUnderlayVolumeFile(tabIndex); } } ModelVolume* volumeModel = btc->getDisplayedVolumeModel(); if (volumeModel != NULL) { if (m_parentToolBar->getDisplayedModel() == volumeModel) { underlayVolumeFile = volumeModel->getUnderlayVolumeFile(tabIndex); } } if (underlayVolumeFile != NULL) { float sliceCoords[3] = { m_volumeIndicesXcoordSpinBox->value(), m_volumeIndicesYcoordSpinBox->value(), m_volumeIndicesZcoordSpinBox->value() }; btc->selectSlicesAtCoordinate(sliceCoords); } this->updateSliceIndicesAndCoordinatesRanges(); this->updateGraphicsWindow(); this->updateOtherYokedWindows(); } /** * Called when projection type is changed. */ void BrainBrowserWindowToolBarSliceSelection::volumeSliceProjectionTypeEnumComboBoxItemActivated() { BrowserTabContent* btc = this->getTabContentFromSelectedTab(); const VolumeSliceProjectionTypeEnum::Enum sliceProjectionType = m_volumeSliceProjectionTypeEnumComboBox->getSelectedItem(); btc->setSliceProjectionType(sliceProjectionType); this->updateGraphicsWindow(); this->updateOtherYokedWindows(); EventManager::get()->sendEvent(EventUpdateVolumeEditingToolBar().getPointer()); } /** * Called when volume identification action toggled. * * @param value * New value. */ void BrainBrowserWindowToolBarSliceSelection::volumeIdentificationToggled(bool value) { BrowserTabContent* browserTabContent = this->getTabContentFromSelectedTab(); if (browserTabContent == NULL) { return; } browserTabContent->setIdentificationUpdatesVolumeSlices(value); } connectome-workbench-1.2.3+git41-gc4c6c90/src/GuiQt/BrainBrowserWindowToolBarSliceSelection.h000066400000000000000000000074041300200146000316530ustar00rootroot00000000000000#ifndef __BRAIN_BROWSER_WINDOW_TOOL_BAR_SLICE_SELECTION_H__ #define __BRAIN_BROWSER_WINDOW_TOOL_BAR_SLICE_SELECTION_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" class QAction; class QCheckBox; class QDoubleSpinBox; class QSpinBox; class WuQWidgetObjectGroup; namespace caret { class EnumComboBoxTemplate; class BrainBrowserWindowToolBarSliceSelection : public BrainBrowserWindowToolBarComponent { Q_OBJECT public: BrainBrowserWindowToolBarSliceSelection(BrainBrowserWindowToolBar* parentToolBar); virtual ~BrainBrowserWindowToolBarSliceSelection(); virtual void updateContent(BrowserTabContent* browserTabContent); // ADD_NEW_METHODS_HERE private slots: void volumeIndicesOriginActionTriggered(); 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 volumeIndicesXcoordSpinBoxValueChanged(double d); void volumeIndicesYcoordSpinBoxValueChanged(double d); void volumeIndicesZcoordSpinBoxValueChanged(double d); void volumeSliceProjectionTypeEnumComboBoxItemActivated(); void volumeIdentificationToggled(bool value); private: BrainBrowserWindowToolBarSliceSelection(const BrainBrowserWindowToolBarSliceSelection&); BrainBrowserWindowToolBarSliceSelection& operator=(const BrainBrowserWindowToolBarSliceSelection&); void readVolumeSliceCoordinatesAndUpdateSliceIndices(); void readVolumeSliceIndicesAndUpdateSliceCoordinates(); void updateSliceIndicesAndCoordinatesRanges(); BrainBrowserWindowToolBar* m_parentToolBar; WuQWidgetObjectGroup* m_volumeIndicesWidgetGroup; QAction* m_volumeIdentificationUpdatesSlicesAction; QCheckBox* m_volumeIndicesParasagittalCheckBox; QCheckBox* m_volumeIndicesCoronalCheckBox; QCheckBox* m_volumeIndicesAxialCheckBox; QSpinBox* m_volumeIndicesParasagittalSpinBox; QSpinBox* m_volumeIndicesCoronalSpinBox; QSpinBox* m_volumeIndicesAxialSpinBox; QDoubleSpinBox* m_volumeIndicesXcoordSpinBox; QDoubleSpinBox* m_volumeIndicesYcoordSpinBox; QDoubleSpinBox* m_volumeIndicesZcoordSpinBox; EnumComboBoxTemplate* m_volumeSliceProjectionTypeEnumComboBox; // ADD_NEW_MEMBERS_HERE }; #ifdef __BRAIN_BROWSER_WINDOW_TOOL_BAR_SLICE_SELECTION_DECLARE__ // #endif // __BRAIN_BROWSER_WINDOW_TOOL_BAR_SLICE_SELECTION_DECLARE__ } // namespace #endif //__BRAIN_BROWSER_WINDOW_TOOL_BAR_SLICE_SELECTION_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/GuiQt/BrainBrowserWindowToolBarSurfaceMontage.cxx000066400000000000000000001050411300200146000322200ustar00rootroot00000000000000 /*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 #define __BRAIN_BROWSER_WINDOW_TOOL_BAR_SURFACE_MONTAGE_DECLARE__ #include "BrainBrowserWindowToolBarSurfaceMontage.h" #undef __BRAIN_BROWSER_WINDOW_TOOL_BAR_SURFACE_MONTAGE_DECLARE__ #include "BrainBrowserWindowToolBar.h" #include "BrowserTabContent.h" #include "CaretAssert.h" #include "EnumComboBoxTemplate.h" #include "EventManager.h" #include "EventSurfaceColoringInvalidate.h" #include "ModelSurfaceMontage.h" #include "SurfaceMontageConfigurationCerebellar.h" #include "SurfaceMontageConfigurationCerebral.h" #include "SurfaceMontageConfigurationFlatMaps.h" #include "SurfaceMontageLayoutOrientationEnum.h" #include "SurfaceSelectionModel.h" #include "SurfaceSelectionViewController.h" #include "WuQtUtilities.h" #include "WuQWidgetObjectGroup.h" using namespace caret; /** * \class caret::BrainBrowserWindowToolBarSurfaceMontage * \brief Surface Montage Component of Brain Browser Window ToolBar * \ingroup GuiQt */ /** * Constructor. * * @param parentToolBar * parent toolbar. */ BrainBrowserWindowToolBarSurfaceMontage::BrainBrowserWindowToolBarSurfaceMontage(BrainBrowserWindowToolBar* parentToolBar) : BrainBrowserWindowToolBarComponent(parentToolBar), m_parentToolBar(parentToolBar) { m_surfaceMontageConfigurationTypeEnumComboBox = new EnumComboBoxTemplate(this); m_surfaceMontageConfigurationTypeEnumComboBox->setup(); QObject::connect(m_surfaceMontageConfigurationTypeEnumComboBox, SIGNAL(itemActivated()), this, SLOT(surfaceMontageConfigurationTypeEnumComboBoxItemActivated())); addToWidgetGroup(m_surfaceMontageConfigurationTypeEnumComboBox->getWidget()); WuQtUtilities::setToolTipAndStatusTip(m_surfaceMontageConfigurationTypeEnumComboBox->getWidget(), ("Selects Surface Montage Configuration:\n" " Cerebellar Cortex\n" " Cerebral Cortex\n" " Flat Maps")); m_surfaceMontageLayoutOrientationEnumComboBox = new EnumComboBoxTemplate(this); m_surfaceMontageLayoutOrientationEnumComboBox->setup(); QObject::connect(m_surfaceMontageLayoutOrientationEnumComboBox, SIGNAL(itemActivated()), this, SLOT(surfaceMontageLayoutOrientationEnumComboBoxItemActivated())); addToWidgetGroup(m_surfaceMontageLayoutOrientationEnumComboBox->getWidget()); WuQtUtilities::setToolTipAndStatusTip(m_surfaceMontageLayoutOrientationEnumComboBox->getWidget(), ("Selects Surface Layout:\n" " Landscape (Layout left-to-right)\n" " Portrait (Layout top-to-bottom)")); m_cerebellarComponent = new SurfaceMontageCerebellarComponent(this); m_cerebralComponent = new SurfaceMontageCerebralComponent(this); m_flatMapsComponent = new SurfaceMontageFlatMapsComponent(this); QHBoxLayout* configOrientationLayout = new QHBoxLayout(); WuQtUtilities::setLayoutSpacingAndMargins(configOrientationLayout, 2, 0); configOrientationLayout->addStretch(); configOrientationLayout->addWidget(m_surfaceMontageConfigurationTypeEnumComboBox->getWidget()); configOrientationLayout->addStretch(); configOrientationLayout->addSpacing(10); configOrientationLayout->addWidget(m_surfaceMontageLayoutOrientationEnumComboBox->getWidget()); configOrientationLayout->addStretch(); m_stackedWidget = new QStackedWidget(); m_stackedWidget->addWidget(m_cerebellarComponent); m_stackedWidget->addWidget(m_cerebralComponent); m_stackedWidget->addWidget(m_flatMapsComponent); QVBoxLayout* layout = new QVBoxLayout(this); WuQtUtilities::setLayoutSpacingAndMargins(layout, 2, 0); layout->addLayout(configOrientationLayout); layout->addWidget(WuQtUtilities::createHorizontalLineWidget()); layout->addWidget(m_stackedWidget); // EventManager::get()->addEventListener(this, EventTypeEnum::); } /** * Destructor. */ BrainBrowserWindowToolBarSurfaceMontage::~BrainBrowserWindowToolBarSurfaceMontage() { EventManager::get()->removeAllEventsFromListener(this); } /** * Called when the montage configuration is changed. */ void BrainBrowserWindowToolBarSurfaceMontage::surfaceMontageConfigurationTypeEnumComboBoxItemActivated() { m_parentToolBar->getTabContentFromSelectedTab(); BrowserTabContent* btc = m_parentToolBar->getTabContentFromSelectedTab(); const SurfaceMontageConfigurationTypeEnum::Enum configType = m_surfaceMontageConfigurationTypeEnumComboBox->getSelectedItem(); ModelSurfaceMontage* msm = btc->getDisplayedSurfaceMontageModel(); const int32_t tabIndex = btc->getTabNumber(); msm->setSelectedConfigurationType(tabIndex, configType); updateContent(btc); invalidateColoringAndUpdateGraphicsWindow(); m_parentToolBar->updateUserInterface(); } /** * Called when the layout orientation value is changed. */ void BrainBrowserWindowToolBarSurfaceMontage::surfaceMontageLayoutOrientationEnumComboBoxItemActivated() { m_parentToolBar->getTabContentFromSelectedTab(); BrowserTabContent* btc = m_parentToolBar->getTabContentFromSelectedTab(); ModelSurfaceMontage* msm = btc->getDisplayedSurfaceMontageModel(); const int32_t tabIndex = btc->getTabNumber(); SurfaceMontageConfigurationAbstract* selectedConfiguration = msm->getSelectedConfiguration(tabIndex); selectedConfiguration->setLayoutOrientation(m_surfaceMontageLayoutOrientationEnumComboBox->getSelectedItem()); invalidateColoringAndUpdateGraphicsWindow(); } /** * Update the surface montage options widget. * * @param browserTabContent * The active model display controller (may be NULL). */ void BrainBrowserWindowToolBarSurfaceMontage::updateContent(BrowserTabContent* browserTabContent) { ModelSurfaceMontage* msm = browserTabContent->getDisplayedSurfaceMontageModel(); if (msm == NULL) { return; } const int32_t tabIndex = browserTabContent->getTabNumber(); std::vector validConfigs; if (msm->getCerebellarConfiguration(tabIndex)->isValid()) { validConfigs.push_back(SurfaceMontageConfigurationTypeEnum::CEREBELLAR_CORTEX_CONFIGURATION); } if (msm->getCerebralConfiguration(tabIndex)->isValid()) { validConfigs.push_back(SurfaceMontageConfigurationTypeEnum::CEREBRAL_CORTEX_CONFIGURATION); } if (msm->getFlatMapsConfiguration(tabIndex)->isValid()) { validConfigs.push_back(SurfaceMontageConfigurationTypeEnum::FLAT_CONFIGURATION); } m_surfaceMontageConfigurationTypeEnumComboBox->setupWithItems(validConfigs); SurfaceMontageConfigurationAbstract* selectedConfiguration = msm->getSelectedConfiguration(tabIndex); m_surfaceMontageConfigurationTypeEnumComboBox->setSelectedItem(msm->getSelectedConfigurationType(tabIndex)); m_surfaceMontageLayoutOrientationEnumComboBox->setSelectedItem(selectedConfiguration->getLayoutOrientation()); switch (msm->getSelectedConfigurationType(tabIndex)) { case SurfaceMontageConfigurationTypeEnum::CEREBELLAR_CORTEX_CONFIGURATION: m_cerebellarComponent->updateContent(browserTabContent); m_stackedWidget->setCurrentWidget(m_cerebellarComponent); break; case SurfaceMontageConfigurationTypeEnum::CEREBRAL_CORTEX_CONFIGURATION: m_cerebralComponent->updateContent(browserTabContent); m_stackedWidget->setCurrentWidget(m_cerebralComponent); break; case SurfaceMontageConfigurationTypeEnum::FLAT_CONFIGURATION: m_flatMapsComponent->updateContent(browserTabContent); m_stackedWidget->setCurrentWidget(m_flatMapsComponent); break; } } /* ******************************************************************************************** */ /** * \class caret::SurfaceMontageCerebralComponent * \brief Cerebral Surface Montage Component of Brain Browser Window ToolBar * \ingroup GuiQt */ /** * Constructor. * * @param parentToolBar * parent toolbar. */ SurfaceMontageCerebralComponent::SurfaceMontageCerebralComponent(BrainBrowserWindowToolBarSurfaceMontage* parentToolBarMontage) : QWidget(parentToolBarMontage) { m_parentToolBarMontage = parentToolBarMontage; m_leftCheckBox = new QCheckBox("Left"); QObject::connect(m_leftCheckBox, SIGNAL(toggled(bool)), this, SLOT(checkBoxSelected(bool))); m_rightCheckBox = new QCheckBox("Right"); QObject::connect(m_rightCheckBox, SIGNAL(toggled(bool)), this, SLOT(checkBoxSelected(bool))); m_lateralCheckBox = new QCheckBox("Lateral"); QObject::connect(m_lateralCheckBox, SIGNAL(toggled(bool)), this, SLOT(checkBoxSelected(bool))); m_medialCheckBox = new QCheckBox("Medial"); QObject::connect(m_medialCheckBox, SIGNAL(toggled(bool)), this, SLOT(checkBoxSelected(bool))); m_surfaceMontageFirstSurfaceCheckBox = new QCheckBox(" "); QObject::connect(m_surfaceMontageFirstSurfaceCheckBox, SIGNAL(toggled(bool)), this, SLOT(checkBoxSelected(bool))); m_surfaceMontageSecondSurfaceCheckBox = new QCheckBox(" "); QObject::connect(m_surfaceMontageSecondSurfaceCheckBox, SIGNAL(toggled(bool)), this, SLOT(checkBoxSelected(bool))); m_leftSurfaceViewController = new SurfaceSelectionViewController(this); QObject::connect(m_leftSurfaceViewController, SIGNAL(surfaceSelected(Surface*)), this, SLOT(leftSurfaceSelected(Surface*))); m_leftSecondSurfaceViewController = new SurfaceSelectionViewController(this); QObject::connect(m_leftSecondSurfaceViewController, SIGNAL(surfaceSelected(Surface*)), this, SLOT(leftSecondSurfaceSelected(Surface*))); m_rightSurfaceViewController = new SurfaceSelectionViewController(this); QObject::connect(m_rightSurfaceViewController, SIGNAL(surfaceSelected(Surface*)), this, SLOT(rightSurfaceSelected(Surface*))); m_rightSecondSurfaceViewController = new SurfaceSelectionViewController(this); QObject::connect(m_rightSecondSurfaceViewController, SIGNAL(surfaceSelected(Surface*)), this, SLOT(rightSecondSurfaceSelected(Surface*))); QHBoxLayout* checkBoxLayout = new QHBoxLayout(); WuQtUtilities::setLayoutSpacingAndMargins(checkBoxLayout, 2, 0); checkBoxLayout->addWidget(m_leftCheckBox); checkBoxLayout->addSpacing(10); checkBoxLayout->addStretch(); checkBoxLayout->addWidget(m_lateralCheckBox); checkBoxLayout->addSpacing(10); checkBoxLayout->addStretch(); checkBoxLayout->addWidget(m_medialCheckBox); checkBoxLayout->addSpacing(10); checkBoxLayout->addStretch(); checkBoxLayout->addWidget(m_rightCheckBox); int32_t columnIndex = 0; const int32_t COLUMN_ONE_TWO = columnIndex++; const int32_t COLUMN_INDEX_LEFT = columnIndex++; const int32_t COLUMN_INDEX_RIGHT = columnIndex++; QGridLayout* layout = new QGridLayout(this); layout->setColumnStretch(0, 0); layout->setColumnStretch(1, 100); layout->setColumnStretch(2, 100); WuQtUtilities::setLayoutSpacingAndMargins(layout, 4, 0); int row = layout->rowCount(); layout->addWidget(m_surfaceMontageFirstSurfaceCheckBox, row, COLUMN_ONE_TWO); layout->addWidget(m_leftSurfaceViewController->getWidget(), row, COLUMN_INDEX_LEFT); layout->addWidget(m_rightSurfaceViewController->getWidget(), row, COLUMN_INDEX_RIGHT); row = layout->rowCount(); layout->addWidget(m_surfaceMontageSecondSurfaceCheckBox, row, COLUMN_ONE_TWO); layout->addWidget(m_leftSecondSurfaceViewController->getWidget(), row, COLUMN_INDEX_LEFT); layout->addWidget(m_rightSecondSurfaceViewController->getWidget(), row, COLUMN_INDEX_RIGHT); row = layout->rowCount(); layout->addLayout(checkBoxLayout, row, COLUMN_INDEX_LEFT, 1, 2); row = layout->rowCount(); m_widgetGroup = new WuQWidgetObjectGroup(this); m_widgetGroup->add(m_leftSurfaceViewController->getWidget()); m_widgetGroup->add(m_leftSecondSurfaceViewController->getWidget()); m_widgetGroup->add(m_rightSurfaceViewController->getWidget()); m_widgetGroup->add(m_rightSecondSurfaceViewController->getWidget()); m_widgetGroup->add(m_leftCheckBox); m_widgetGroup->add(m_rightCheckBox); m_widgetGroup->add(m_surfaceMontageFirstSurfaceCheckBox); m_widgetGroup->add(m_surfaceMontageSecondSurfaceCheckBox); m_widgetGroup->add(m_medialCheckBox); m_widgetGroup->add(m_lateralCheckBox); setFixedHeight(sizeHint().height()); } /** * Destructor. */ SurfaceMontageCerebralComponent::~SurfaceMontageCerebralComponent() { } /** * Update the cerebral montage options widget. * * @param browserTabContent * The active model display controller (may be NULL). */ void SurfaceMontageCerebralComponent::updateContent(BrowserTabContent* browserTabContent) { m_widgetGroup->blockAllSignals(true); const int32_t tabIndex = browserTabContent->getTabNumber(); ModelSurfaceMontage* msm = browserTabContent->getDisplayedSurfaceMontageModel(); SurfaceMontageConfigurationCerebral* smcc = msm->getCerebralConfiguration(tabIndex); m_leftCheckBox->setChecked(smcc->isLeftEnabled()); m_rightCheckBox->setChecked(smcc->isRightEnabled()); m_surfaceMontageFirstSurfaceCheckBox->setChecked(smcc->isFirstSurfaceEnabled()); m_surfaceMontageSecondSurfaceCheckBox->setChecked(smcc->isSecondSurfaceEnabled()); m_lateralCheckBox->setChecked(smcc->isLateralEnabled()); m_medialCheckBox->setChecked(smcc->isMedialEnabled()); m_leftSurfaceViewController->updateControl(smcc->getLeftFirstSurfaceSelectionModel()); m_leftSecondSurfaceViewController->updateControl(smcc->getLeftSecondSurfaceSelectionModel()); m_rightSurfaceViewController->updateControl(smcc->getRightFirstSurfaceSelectionModel()); m_rightSecondSurfaceViewController->updateControl(smcc->getRightSecondSurfaceSelectionModel()); m_widgetGroup->blockAllSignals(false); } /** * Called when montage left first surface is selected. * @param surface * Surface that was selected. */ void SurfaceMontageCerebralComponent::leftSurfaceSelected(Surface* surface) { if (surface != NULL) { BrowserTabContent* btc = m_parentToolBarMontage->getTabContentFromSelectedTab(); const int32_t tabIndex = btc->getTabNumber(); ModelSurfaceMontage* msm = btc->getDisplayedSurfaceMontageModel(); SurfaceMontageConfigurationCerebral* smcc = msm->getCerebralConfiguration(tabIndex); smcc->getLeftFirstSurfaceSelectionModel()->setSurface(surface); m_parentToolBarMontage->invalidateColoringAndUpdateGraphicsWindow(); } } /** * Called when montage left second surface is selected. * @param surface * Surface that was selected. */ void SurfaceMontageCerebralComponent::leftSecondSurfaceSelected(Surface* surface) { if (surface != NULL) { BrowserTabContent* btc = m_parentToolBarMontage->getTabContentFromSelectedTab(); const int32_t tabIndex = btc->getTabNumber(); ModelSurfaceMontage* msm = btc->getDisplayedSurfaceMontageModel(); SurfaceMontageConfigurationCerebral* smcc = msm->getCerebralConfiguration(tabIndex); smcc->getLeftSecondSurfaceSelectionModel()->setSurface(surface); m_parentToolBarMontage->invalidateColoringAndUpdateGraphicsWindow(); } } /** * Called when montage right surface is selected. * @param surface * Surface that was selected. */ void SurfaceMontageCerebralComponent::rightSurfaceSelected(Surface* surface) { if (surface != NULL) { BrowserTabContent* btc = m_parentToolBarMontage->getTabContentFromSelectedTab(); const int32_t tabIndex = btc->getTabNumber(); ModelSurfaceMontage* msm = btc->getDisplayedSurfaceMontageModel(); SurfaceMontageConfigurationCerebral* smcc = msm->getCerebralConfiguration(tabIndex); smcc->getRightFirstSurfaceSelectionModel()->setSurface(surface); m_parentToolBarMontage->invalidateColoringAndUpdateGraphicsWindow(); } } /** * Called when montage right second surface is selected. * @param surface * Surface that was selected. */ void SurfaceMontageCerebralComponent::rightSecondSurfaceSelected(Surface* surface) { if (surface != NULL) { BrowserTabContent* btc = m_parentToolBarMontage->getTabContentFromSelectedTab(); const int32_t tabIndex = btc->getTabNumber(); ModelSurfaceMontage* msm = btc->getDisplayedSurfaceMontageModel(); SurfaceMontageConfigurationCerebral* smcc = msm->getCerebralConfiguration(tabIndex); smcc->getRightSecondSurfaceSelectionModel()->setSurface(surface); m_parentToolBarMontage->invalidateColoringAndUpdateGraphicsWindow(); } } /** * Called when surface montage checkbox is toggled. * @param status * New status of check box. */ void SurfaceMontageCerebralComponent::checkBoxSelected(bool /*status*/) { BrowserTabContent* btc = m_parentToolBarMontage->getTabContentFromSelectedTab(); const int32_t tabIndex = btc->getTabNumber(); ModelSurfaceMontage* msm = btc->getDisplayedSurfaceMontageModel(); SurfaceMontageConfigurationCerebral* smcc = msm->getCerebralConfiguration(tabIndex); smcc->setLeftEnabled(m_leftCheckBox->isChecked()); smcc->setRightEnabled(m_rightCheckBox->isChecked()); smcc->setFirstSurfaceEnabled(m_surfaceMontageFirstSurfaceCheckBox->isChecked()); smcc->setSecondSurfaceEnabled(m_surfaceMontageSecondSurfaceCheckBox->isChecked()); smcc->setLateralEnabled(m_lateralCheckBox->isChecked()); smcc->setMedialEnabled(m_medialCheckBox->isChecked()); m_parentToolBarMontage->updateUserInterface(); m_parentToolBarMontage->invalidateColoringAndUpdateGraphicsWindow(); } /* ******************************************************************************************** */ /** * \class caret::SurfaceMontageCerebellarComponent * \brief Cerebellar Surface Montage Component of Brain Browser Window ToolBar * \ingroup GuiQt */ /** * Constructor. * * @param parentToolBar * parent toolbar. */ SurfaceMontageCerebellarComponent::SurfaceMontageCerebellarComponent(BrainBrowserWindowToolBarSurfaceMontage* parentToolBarMontage) : QWidget(parentToolBarMontage) { m_parentToolBarMontage = parentToolBarMontage; m_dorsalCheckBox = new QCheckBox("Dorsal"); QObject::connect(m_dorsalCheckBox, SIGNAL(toggled(bool)), this, SLOT(checkBoxSelected(bool))); m_ventralCheckBox = new QCheckBox("Ventral"); QObject::connect(m_ventralCheckBox, SIGNAL(toggled(bool)), this, SLOT(checkBoxSelected(bool))); m_anteriorCheckBox = new QCheckBox("Anterior"); QObject::connect(m_anteriorCheckBox, SIGNAL(toggled(bool)), this, SLOT(checkBoxSelected(bool))); m_posteriorCheckBox = new QCheckBox("Posterior"); QObject::connect(m_posteriorCheckBox, SIGNAL(toggled(bool)), this, SLOT(checkBoxSelected(bool))); m_firstSurfaceCheckBox = new QCheckBox(" "); QObject::connect(m_firstSurfaceCheckBox, SIGNAL(toggled(bool)), this, SLOT(checkBoxSelected(bool))); m_secondSurfaceCheckBox = new QCheckBox(" "); QObject::connect(m_secondSurfaceCheckBox, SIGNAL(toggled(bool)), this, SLOT(checkBoxSelected(bool))); m_firstSurfaceViewController = new SurfaceSelectionViewController(this); QObject::connect(m_firstSurfaceViewController, SIGNAL(surfaceSelected(Surface*)), this, SLOT(firstSurfaceSelected(Surface*))); m_secondSurfaceViewController = new SurfaceSelectionViewController(this); QObject::connect(m_secondSurfaceViewController, SIGNAL(surfaceSelected(Surface*)), this, SLOT(secondSurfaceSelected(Surface*))); QHBoxLayout* checkBoxLayout = new QHBoxLayout(); WuQtUtilities::setLayoutSpacingAndMargins(checkBoxLayout, 2, 0); checkBoxLayout->addStretch(); checkBoxLayout->addWidget(m_dorsalCheckBox); checkBoxLayout->addSpacing(5); checkBoxLayout->addStretch(); checkBoxLayout->addWidget(m_ventralCheckBox); checkBoxLayout->addSpacing(5); checkBoxLayout->addStretch(); checkBoxLayout->addWidget(m_anteriorCheckBox); checkBoxLayout->addSpacing(5); checkBoxLayout->addStretch(); checkBoxLayout->addWidget(m_posteriorCheckBox); checkBoxLayout->addStretch(); int32_t columnIndex = 0; const int32_t COLUMN_CHECKBOX = columnIndex++; const int32_t COLUMN_SELECTION = columnIndex++; QGridLayout* layout = new QGridLayout(this); layout->setColumnStretch(COLUMN_CHECKBOX, 0); layout->setColumnStretch(COLUMN_SELECTION, 100); WuQtUtilities::setLayoutSpacingAndMargins(layout, 4, 0); int row = layout->rowCount(); layout->addWidget(m_firstSurfaceCheckBox, row, COLUMN_CHECKBOX); layout->addWidget(m_firstSurfaceViewController->getWidget(), row, COLUMN_SELECTION); row = layout->rowCount(); layout->addWidget(m_secondSurfaceCheckBox, row, COLUMN_CHECKBOX); layout->addWidget(m_secondSurfaceViewController->getWidget(), row, COLUMN_SELECTION); row = layout->rowCount(); layout->addLayout(checkBoxLayout, row, COLUMN_CHECKBOX, 1, 2); row = layout->rowCount(); m_widgetGroup = new WuQWidgetObjectGroup(this); m_widgetGroup->add(m_firstSurfaceViewController->getWidget()); m_widgetGroup->add(m_secondSurfaceViewController->getWidget()); m_widgetGroup->add(m_firstSurfaceCheckBox); m_widgetGroup->add(m_secondSurfaceCheckBox); m_widgetGroup->add(m_dorsalCheckBox); m_widgetGroup->add(m_ventralCheckBox); m_widgetGroup->add(m_anteriorCheckBox); m_widgetGroup->add(m_posteriorCheckBox); setFixedHeight(sizeHint().height()); } /** * Destructor. */ SurfaceMontageCerebellarComponent::~SurfaceMontageCerebellarComponent() { } /** * Update the cerebellar montage options widget. * * @param browserTabContent * The active model display controller (may be NULL). */ void SurfaceMontageCerebellarComponent::updateContent(BrowserTabContent* browserTabContent) { m_widgetGroup->blockAllSignals(true); const int32_t tabIndex = browserTabContent->getTabNumber(); ModelSurfaceMontage* msm = browserTabContent->getDisplayedSurfaceMontageModel(); SurfaceMontageConfigurationCerebellar* smcc = msm->getCerebellarConfiguration(tabIndex); m_firstSurfaceCheckBox->setChecked(smcc->isFirstSurfaceEnabled()); m_secondSurfaceCheckBox->setChecked(smcc->isSecondSurfaceEnabled()); m_dorsalCheckBox->setChecked(smcc->isDorsalEnabled()); m_ventralCheckBox->setChecked(smcc->isVentralEnabled()); m_anteriorCheckBox->setChecked(smcc->isAnteriorEnabled()); m_posteriorCheckBox->setChecked(smcc->isPosteriorEnabled()); m_firstSurfaceViewController->updateControl(smcc->getFirstSurfaceSelectionModel()); m_secondSurfaceViewController->updateControl(smcc->getSecondSurfaceSelectionModel()); m_widgetGroup->blockAllSignals(false); } /** * Called when first cerebellar surface is selected. * @param surface * Surface that was selected. */ void SurfaceMontageCerebellarComponent::firstSurfaceSelected(Surface* surface) { if (surface != NULL) { BrowserTabContent* btc = m_parentToolBarMontage->getTabContentFromSelectedTab(); const int32_t tabIndex = btc->getTabNumber(); ModelSurfaceMontage* msm = btc->getDisplayedSurfaceMontageModel(); SurfaceMontageConfigurationCerebellar* smcc = msm->getCerebellarConfiguration(tabIndex); smcc->getFirstSurfaceSelectionModel()->setSurface(surface); m_parentToolBarMontage->invalidateColoringAndUpdateGraphicsWindow(); } } /** * Called when second cerebellar surface is selected. * @param surface * Surface that was selected. */ void SurfaceMontageCerebellarComponent::secondSurfaceSelected(Surface* surface) { if (surface != NULL) { BrowserTabContent* btc = m_parentToolBarMontage->getTabContentFromSelectedTab(); const int32_t tabIndex = btc->getTabNumber(); ModelSurfaceMontage* msm = btc->getDisplayedSurfaceMontageModel(); SurfaceMontageConfigurationCerebellar* smcc = msm->getCerebellarConfiguration(tabIndex); smcc->getSecondSurfaceSelectionModel()->setSurface(surface); m_parentToolBarMontage->invalidateColoringAndUpdateGraphicsWindow(); } } /** * Called cerebellar surface montage checkbox is toggled. * @param status * New status of check box. */ void SurfaceMontageCerebellarComponent::checkBoxSelected(bool /*status*/) { BrowserTabContent* btc = m_parentToolBarMontage->getTabContentFromSelectedTab(); const int32_t tabIndex = btc->getTabNumber(); ModelSurfaceMontage* msm = btc->getDisplayedSurfaceMontageModel(); SurfaceMontageConfigurationCerebellar* smcc = msm->getCerebellarConfiguration(tabIndex); smcc->setDorsalEnabled(m_dorsalCheckBox->isChecked()); smcc->setVentralEnabled(m_ventralCheckBox->isChecked()); smcc->setAnteriorEnabled(m_anteriorCheckBox->isChecked()); smcc->setPosteriorEnabled(m_posteriorCheckBox->isChecked()); smcc->setFirstSurfaceEnabled(m_firstSurfaceCheckBox->isChecked()); smcc->setSecondSurfaceEnabled(m_secondSurfaceCheckBox->isChecked()); m_parentToolBarMontage->updateUserInterface(); m_parentToolBarMontage->invalidateColoringAndUpdateGraphicsWindow(); } /* ******************************************************************************************** */ /** * \class caret::SurfaceMontageFlatMapsComponent * \brief Flat Surface Montage Component of Brain Browser Window ToolBar * \ingroup GuiQt */ /** * Constructor. * * @param parentToolBar * parent toolbar. */ SurfaceMontageFlatMapsComponent::SurfaceMontageFlatMapsComponent(BrainBrowserWindowToolBarSurfaceMontage* parentToolBarMontage) : QWidget(parentToolBarMontage) { m_parentToolBarMontage = parentToolBarMontage; m_leftSurfaceCheckBox = new QCheckBox("Left"); QObject::connect(m_leftSurfaceCheckBox, SIGNAL(toggled(bool)), this, SLOT(checkBoxSelected(bool))); m_rightSurfaceCheckBox = new QCheckBox("Right"); QObject::connect(m_rightSurfaceCheckBox, SIGNAL(toggled(bool)), this, SLOT(checkBoxSelected(bool))); m_cerebellumSurfaceCheckBox = new QCheckBox("Cerebellum "); QObject::connect(m_cerebellumSurfaceCheckBox, SIGNAL(toggled(bool)), this, SLOT(checkBoxSelected(bool))); m_leftSurfaceViewController = new SurfaceSelectionViewController(this); QObject::connect(m_leftSurfaceViewController, SIGNAL(surfaceSelected(Surface*)), this, SLOT(leftSurfaceSelected(Surface*))); m_rightSurfaceViewController = new SurfaceSelectionViewController(this); QObject::connect(m_rightSurfaceViewController, SIGNAL(surfaceSelected(Surface*)), this, SLOT(rightSurfaceSelected(Surface*))); m_cerebellumSurfaceViewController = new SurfaceSelectionViewController(this); QObject::connect(m_cerebellumSurfaceViewController, SIGNAL(surfaceSelected(Surface*)), this, SLOT(cerebellumSurfaceSelected(Surface*))); // QHBoxLayout* checkBoxLayout = new QHBoxLayout(); // WuQtUtilities::setLayoutSpacingAndMargins(checkBoxLayout, 2, 0); // checkBoxLayout->addStretch(); // checkBoxLayout->addWidget(m_leftSurfaceCheckBox); // checkBoxLayout->addSpacing(5); // checkBoxLayout->addStretch(); // checkBoxLayout->addWidget(m_rightSurfaceCheckBox); // checkBoxLayout->addSpacing(5); // checkBoxLayout->addStretch(); // checkBoxLayout->addWidget(m_cerebellumSurfaceCheckBox); // checkBoxLayout->addStretch(); int32_t columnIndex = 0; const int32_t COLUMN_CHECKBOX = columnIndex++; const int32_t COLUMN_SELECTION = columnIndex++; QGridLayout* layout = new QGridLayout(this); layout->setColumnStretch(COLUMN_CHECKBOX, 0); layout->setColumnStretch(COLUMN_SELECTION, 100); WuQtUtilities::setLayoutSpacingAndMargins(layout, 4, 2); int row = layout->rowCount(); layout->addWidget(m_leftSurfaceCheckBox, row, COLUMN_CHECKBOX); layout->addWidget(m_leftSurfaceViewController->getWidget(), row, COLUMN_SELECTION); row = layout->rowCount(); layout->addWidget(m_rightSurfaceCheckBox, row, COLUMN_CHECKBOX); layout->addWidget(m_rightSurfaceViewController->getWidget(), row, COLUMN_SELECTION); row = layout->rowCount(); layout->addWidget(m_cerebellumSurfaceCheckBox, row, COLUMN_CHECKBOX); layout->addWidget(m_cerebellumSurfaceViewController->getWidget(), row, COLUMN_SELECTION); row = layout->rowCount(); // layout->addLayout(checkBoxLayout, // row, COLUMN_CHECKBOX, // 1, 2); // row = layout->rowCount(); m_widgetGroup = new WuQWidgetObjectGroup(this); m_widgetGroup->add(m_leftSurfaceViewController->getWidget()); m_widgetGroup->add(m_rightSurfaceViewController->getWidget()); m_widgetGroup->add(m_cerebellumSurfaceViewController->getWidget()); m_widgetGroup->add(m_leftSurfaceCheckBox); m_widgetGroup->add(m_rightSurfaceCheckBox); m_widgetGroup->add(m_cerebellumSurfaceCheckBox); setFixedHeight(sizeHint().height()); } /** * Destructor. */ SurfaceMontageFlatMapsComponent::~SurfaceMontageFlatMapsComponent() { } /** * Update the flat maps montage options widget. * * @param browserTabContent * The active model display controller (may be NULL). */ void SurfaceMontageFlatMapsComponent::updateContent(BrowserTabContent* browserTabContent) { m_widgetGroup->blockAllSignals(true); const int32_t tabIndex = browserTabContent->getTabNumber(); ModelSurfaceMontage* msm = browserTabContent->getDisplayedSurfaceMontageModel(); SurfaceMontageConfigurationFlatMaps* smcc = msm->getFlatMapsConfiguration(tabIndex); m_leftSurfaceCheckBox->setChecked(smcc->isLeftEnabled()); m_rightSurfaceCheckBox->setChecked(smcc->isRightEnabled()); m_cerebellumSurfaceCheckBox->setChecked(smcc->isCerebellumEnabled()); m_leftSurfaceViewController->updateControl(smcc->getLeftSurfaceSelectionModel()); m_rightSurfaceViewController->updateControl(smcc->getRightSurfaceSelectionModel()); m_cerebellumSurfaceViewController->updateControl(smcc->getCerebellumSurfaceSelectionModel()); m_widgetGroup->blockAllSignals(false); } /** * Called when flat left surface is selected. * @param surface * Surface that was selected. */ void SurfaceMontageFlatMapsComponent::leftSurfaceSelected(Surface* surface) { if (surface != NULL) { BrowserTabContent* btc = m_parentToolBarMontage->getTabContentFromSelectedTab(); const int32_t tabIndex = btc->getTabNumber(); ModelSurfaceMontage* msm = btc->getDisplayedSurfaceMontageModel(); SurfaceMontageConfigurationFlatMaps* smcc = msm->getFlatMapsConfiguration(tabIndex); smcc->getLeftSurfaceSelectionModel()->setSurface(surface); m_parentToolBarMontage->invalidateColoringAndUpdateGraphicsWindow(); } } /** * Called when flat right surface is selected. * @param surface * Surface that was selected. */ void SurfaceMontageFlatMapsComponent::rightSurfaceSelected(Surface* surface) { if (surface != NULL) { BrowserTabContent* btc = m_parentToolBarMontage->getTabContentFromSelectedTab(); const int32_t tabIndex = btc->getTabNumber(); ModelSurfaceMontage* msm = btc->getDisplayedSurfaceMontageModel(); SurfaceMontageConfigurationFlatMaps* smcc = msm->getFlatMapsConfiguration(tabIndex); smcc->getRightSurfaceSelectionModel()->setSurface(surface); m_parentToolBarMontage->invalidateColoringAndUpdateGraphicsWindow(); } } /** * Called when flat cerebellum surface is selected. * @param surface * Surface that was selected. */ void SurfaceMontageFlatMapsComponent::cerebellumSurfaceSelected(Surface* surface) { if (surface != NULL) { BrowserTabContent* btc = m_parentToolBarMontage->getTabContentFromSelectedTab(); const int32_t tabIndex = btc->getTabNumber(); ModelSurfaceMontage* msm = btc->getDisplayedSurfaceMontageModel(); SurfaceMontageConfigurationFlatMaps* smcc = msm->getFlatMapsConfiguration(tabIndex); smcc->getCerebellumSurfaceSelectionModel()->setSurface(surface); m_parentToolBarMontage->invalidateColoringAndUpdateGraphicsWindow(); } } /** * Called when flat maps surface montage checkbox is toggled. * @param status * New status of check box. */ void SurfaceMontageFlatMapsComponent::checkBoxSelected(bool /*status*/) { BrowserTabContent* btc = m_parentToolBarMontage->getTabContentFromSelectedTab(); const int32_t tabIndex = btc->getTabNumber(); ModelSurfaceMontage* msm = btc->getDisplayedSurfaceMontageModel(); SurfaceMontageConfigurationFlatMaps* smcc = msm->getFlatMapsConfiguration(tabIndex); smcc->setLeftEnabled(m_leftSurfaceCheckBox->isChecked()); smcc->setRightEnabled(m_rightSurfaceCheckBox->isChecked()); smcc->setCerebellumEnabled(m_cerebellumSurfaceCheckBox->isChecked()); m_parentToolBarMontage->updateUserInterface(); m_parentToolBarMontage->invalidateColoringAndUpdateGraphicsWindow(); } connectome-workbench-1.2.3+git41-gc4c6c90/src/GuiQt/BrainBrowserWindowToolBarSurfaceMontage.h000066400000000000000000000147731300200146000316600ustar00rootroot00000000000000#ifndef __BRAIN_BROWSER_WINDOW_TOOL_BAR_SURFACE_MONTAGE_H__ #define __BRAIN_BROWSER_WINDOW_TOOL_BAR_SURFACE_MONTAGE_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" class QCheckBox; class QStackedWidget; namespace caret { class BrowserTabContent; class EnumComboBoxTemplate; class Surface; class SurfaceMontageCerebellarComponent; class SurfaceMontageCerebralComponent; class SurfaceMontageFlatMapsComponent; class SurfaceSelectionViewController; class BrainBrowserWindowToolBarSurfaceMontage : public BrainBrowserWindowToolBarComponent { Q_OBJECT public: BrainBrowserWindowToolBarSurfaceMontage(BrainBrowserWindowToolBar* parentToolBar); virtual ~BrainBrowserWindowToolBarSurfaceMontage(); virtual void updateContent(BrowserTabContent* browserTabContent); private slots: void surfaceMontageConfigurationTypeEnumComboBoxItemActivated(); void surfaceMontageLayoutOrientationEnumComboBoxItemActivated(); private: BrainBrowserWindowToolBarSurfaceMontage(const BrainBrowserWindowToolBarSurfaceMontage&); BrainBrowserWindowToolBarSurfaceMontage& operator=(const BrainBrowserWindowToolBarSurfaceMontage&); BrainBrowserWindowToolBar* m_parentToolBar; SurfaceMontageCerebralComponent* m_cerebralComponent; SurfaceMontageCerebellarComponent* m_cerebellarComponent; SurfaceMontageFlatMapsComponent* m_flatMapsComponent; QStackedWidget* m_stackedWidget; EnumComboBoxTemplate* m_surfaceMontageConfigurationTypeEnumComboBox; EnumComboBoxTemplate* m_surfaceMontageLayoutOrientationEnumComboBox; // ADD_NEW_MEMBERS_HERE private slots: }; /* ===========================================================================*/ class SurfaceMontageCerebralComponent : public QWidget { Q_OBJECT public: SurfaceMontageCerebralComponent(BrainBrowserWindowToolBarSurfaceMontage* parentToolBarMontage); ~SurfaceMontageCerebralComponent(); void updateContent(BrowserTabContent* browserTabContent); private slots: void leftSurfaceSelected(Surface*); void leftSecondSurfaceSelected(Surface*); void rightSurfaceSelected(Surface*); void rightSecondSurfaceSelected(Surface*); void checkBoxSelected(bool); private: BrainBrowserWindowToolBarSurfaceMontage* m_parentToolBarMontage; SurfaceSelectionViewController* m_leftSurfaceViewController; SurfaceSelectionViewController* m_leftSecondSurfaceViewController; SurfaceSelectionViewController* m_rightSurfaceViewController; SurfaceSelectionViewController* m_rightSecondSurfaceViewController; QCheckBox* m_leftCheckBox; QCheckBox* m_rightCheckBox; QCheckBox* m_surfaceMontageFirstSurfaceCheckBox; QCheckBox* m_surfaceMontageSecondSurfaceCheckBox; QCheckBox* m_lateralCheckBox; QCheckBox* m_medialCheckBox; WuQWidgetObjectGroup* m_widgetGroup; }; /* ===========================================================================*/ class SurfaceMontageCerebellarComponent : public QWidget { Q_OBJECT public: SurfaceMontageCerebellarComponent(BrainBrowserWindowToolBarSurfaceMontage* parentToolBarMontage); ~SurfaceMontageCerebellarComponent(); void updateContent(BrowserTabContent* browserTabContent); private slots: void firstSurfaceSelected(Surface*); void secondSurfaceSelected(Surface*); void checkBoxSelected(bool); private: BrainBrowserWindowToolBarSurfaceMontage* m_parentToolBarMontage; SurfaceSelectionViewController* m_firstSurfaceViewController; SurfaceSelectionViewController* m_secondSurfaceViewController; QCheckBox* m_firstSurfaceCheckBox; QCheckBox* m_secondSurfaceCheckBox; QCheckBox* m_dorsalCheckBox; QCheckBox* m_ventralCheckBox; QCheckBox* m_anteriorCheckBox; QCheckBox* m_posteriorCheckBox; WuQWidgetObjectGroup* m_widgetGroup; }; /* ===========================================================================*/ class SurfaceMontageFlatMapsComponent : public QWidget { Q_OBJECT public: SurfaceMontageFlatMapsComponent(BrainBrowserWindowToolBarSurfaceMontage* parentToolBarMontage); ~SurfaceMontageFlatMapsComponent(); void updateContent(BrowserTabContent* browserTabContent); private slots: void leftSurfaceSelected(Surface*); void rightSurfaceSelected(Surface*); void cerebellumSurfaceSelected(Surface*); void checkBoxSelected(bool); private: BrainBrowserWindowToolBarSurfaceMontage* m_parentToolBarMontage; SurfaceSelectionViewController* m_leftSurfaceViewController; SurfaceSelectionViewController* m_rightSurfaceViewController; SurfaceSelectionViewController* m_cerebellumSurfaceViewController; QCheckBox* m_leftSurfaceCheckBox; QCheckBox* m_rightSurfaceCheckBox; QCheckBox* m_cerebellumSurfaceCheckBox; WuQWidgetObjectGroup* m_widgetGroup; }; #ifdef __BRAIN_BROWSER_WINDOW_TOOL_BAR_SURFACE_MONTAGE_DECLARE__ // #endif // __BRAIN_BROWSER_WINDOW_TOOL_BAR_SURFACE_MONTAGE_DECLARE__ } // namespace #endif //__BRAIN_BROWSER_WINDOW_TOOL_BAR_SURFACE_MONTAGE_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/GuiQt/BrainBrowserWindowToolBarTab.cxx000066400000000000000000000123021300200146000300200ustar00rootroot00000000000000 /*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_TAB_DECLARE__ #include "BrainBrowserWindowToolBarTab.h" #undef __BRAIN_BROWSER_WINDOW_TOOL_BAR_TAB_DECLARE__ #include #include #include #include #include #include "BrainBrowserWindow.h" #include "BrainBrowserWindowToolBar.h" #include "BrowserTabContent.h" #include "CaretAssert.h" #include "CaretLogger.h" #include "EnumComboBoxTemplate.h" #include "GuiManager.h" #include "WuQtUtilities.h" #include "WuQWidgetObjectGroup.h" #include "YokingGroupEnum.h" using namespace caret; /** * \class caret::BrainBrowserWindowToolBarTab * \brief Tab section of Browser Window Toolbar. * \ingroup GuiQt */ /** * Constructor. * * @param browserWindowIndex * Index of browser window. * @param windowAspectRatioLockedAction * Action for locking window aspect ratio. * @param tabAspectRatioLockedAction * Action for locking tab aspect ratio. * @param parentToolBar * Parent toolbar. */ BrainBrowserWindowToolBarTab::BrainBrowserWindowToolBarTab(const int32_t browserWindowIndex, QAction* windowAspectRatioLockedAction, QAction* tabAspectRatioLockedAction, BrainBrowserWindowToolBar* parentToolBar) : BrainBrowserWindowToolBarComponent(parentToolBar), m_browserWindowIndex(browserWindowIndex), m_parentToolBar(parentToolBar), m_tabAspectRatioLockedAction(tabAspectRatioLockedAction) { m_yokingGroupComboBox = new EnumComboBoxTemplate(this); m_yokingGroupComboBox->setup(); m_yokingGroupComboBox->getWidget()->setStatusTip("Select a yoking group (linked views)"); m_yokingGroupComboBox->getWidget()->setToolTip(("Select a yoking group (linked views).\n" "Models yoked to a group are displayed in the same view.\n" "Surface Yoking is applied to Surface, Surface Montage\n" "and Whole Brain. Volume Yoking is applied to Volumes.")); QLabel* 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); QVBoxLayout* layout = new QVBoxLayout(this); WuQtUtilities::setLayoutSpacingAndMargins(layout, 4, 0); layout->addWidget(yokeToLabel); layout->addWidget(m_yokingGroupComboBox->getWidget()); layout->addSpacing(15); layout->addWidget(windowAspectRatioLockedToolButton); layout->addWidget(tabAspectRatioLockedToolButton); addToWidgetGroup(yokeToLabel); addToWidgetGroup(m_yokingGroupComboBox->getWidget()); } /** * Destructor. */ BrainBrowserWindowToolBarTab::~BrainBrowserWindowToolBarTab() { } /** * Update the surface montage options widget. * * @param browserTabContent * The active model display controller (may be NULL). */ void BrainBrowserWindowToolBarTab::updateContent(BrowserTabContent* browserTabContent) { blockAllSignals(true); m_yokingGroupComboBox->setSelectedItem(browserTabContent->getYokingGroup()); m_tabAspectRatioLockedAction->blockSignals(true); m_tabAspectRatioLockedAction->setChecked(browserTabContent->isAspectRatioLocked()); m_tabAspectRatioLockedAction->blockSignals(false); blockAllSignals(false); } /** * Called when window yoke to tab combo box is selected. */ void BrainBrowserWindowToolBarTab::yokeToGroupComboBoxIndexChanged() { BrowserTabContent* browserTabContent = this->getTabContentFromSelectedTab(); if (browserTabContent == NULL) { return; } YokingGroupEnum::Enum yokingGroup = m_yokingGroupComboBox->getSelectedItem(); browserTabContent->setYokingGroup(yokingGroup); m_parentToolBar->updateToolBarComponents(browserTabContent); this->updateGraphicsWindow(); } connectome-workbench-1.2.3+git41-gc4c6c90/src/GuiQt/BrainBrowserWindowToolBarTab.h000066400000000000000000000046601300200146000274550ustar00rootroot00000000000000#ifndef __BRAIN_BROWSER_WINDOW_TOOL_BAR_TAB_H__ #define __BRAIN_BROWSER_WINDOW_TOOL_BAR_TAB_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 "BrainBrowserWindowToolBarComponent.h" class QToolButton; namespace caret { class BrainBrowserWindowToolBar; class EnumComboBoxTemplate; class BrainBrowserWindowToolBarTab : public BrainBrowserWindowToolBarComponent { Q_OBJECT public: BrainBrowserWindowToolBarTab(const int32_t browserWindowIndex, QAction* windowAspectRatioLockedAction, QAction* tabAspectRatioLockedAction, BrainBrowserWindowToolBar* parentToolBar); virtual ~BrainBrowserWindowToolBarTab(); virtual void updateContent(BrowserTabContent* browserTabContent); // ADD_NEW_METHODS_HERE private slots: void yokeToGroupComboBoxIndexChanged(); private: BrainBrowserWindowToolBarTab(const BrainBrowserWindowToolBarTab&); BrainBrowserWindowToolBarTab& operator=(const BrainBrowserWindowToolBarTab&); EnumComboBoxTemplate* m_yokingGroupComboBox; const int32_t m_browserWindowIndex; BrainBrowserWindowToolBar* m_parentToolBar; QAction* m_tabAspectRatioLockedAction; // ADD_NEW_MEMBERS_HERE }; #ifdef __BRAIN_BROWSER_WINDOW_TOOL_BAR_TAB_DECLARE__ // #endif // __BRAIN_BROWSER_WINDOW_TOOL_BAR_TAB_DECLARE__ } // namespace #endif //__BRAIN_BROWSER_WINDOW_TOOL_BAR_TAB_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/GuiQt/BrainBrowserWindowToolBarVolumeMontage.cxx000066400000000000000000000166101300200146000321020ustar00rootroot00000000000000 /*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_VOLUME_MONTAGE_DECLARE__ #include "BrainBrowserWindowToolBarVolumeMontage.h" #undef __BRAIN_BROWSER_WINDOW_TOOL_BAR_VOLUME_MONTAGE_DECLARE__ #include #include #include #include #include #include "BrowserTabContent.h" #include "CaretAssert.h" #include "WuQFactory.h" #include "WuQWidgetObjectGroup.h" #include "WuQtUtilities.h" using namespace caret; /** * \class caret::BrainBrowserWindowToolBarVolumeMontage * \brief Toolbar component for volume montage slice selections * \ingroup GuiQt * * */ /** * Constructor. */ BrainBrowserWindowToolBarVolumeMontage::BrainBrowserWindowToolBarVolumeMontage(BrainBrowserWindowToolBar* parentToolBar) : BrainBrowserWindowToolBarComponent(parentToolBar), m_parentToolBar(parentToolBar) { QLabel* rowsLabel = new QLabel("Rows:"); QLabel* columnsLabel = new QLabel("Cols:"); QLabel* spacingLabel = new QLabel("Step:"); const int spinBoxWidth = 48; 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"); QObject::connect(m_montageRowsSpinBox, SIGNAL(valueChanged(int)), this, SLOT(montageRowsSpinBoxValueChanged(int))); 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"); QObject::connect(m_montageColumnsSpinBox, SIGNAL(valueChanged(int)), this, SLOT(montageColumnsSpinBoxValueChanged(int))); 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"); QObject::connect(m_montageSpacingSpinBox, SIGNAL(valueChanged(int)), this, SLOT(montageSpacingSpinBoxValueChanged(int))); m_montageEnabledAction = WuQtUtilities::createAction("On", "View a montage of parallel slices", this, this, SLOT(montageEnabledActionToggled(bool))); m_montageEnabledAction->setCheckable(true); QToolButton* montageEnabledToolButton = new QToolButton(); montageEnabledToolButton->setDefaultAction(m_montageEnabledAction); QGridLayout* gridLayout = new QGridLayout(this); WuQtUtilities::setLayoutSpacingAndMargins(gridLayout, 0, 0); gridLayout->setVerticalSpacing(2); gridLayout->addWidget(rowsLabel, 0, 0); gridLayout->addWidget(m_montageRowsSpinBox, 0, 1); gridLayout->addWidget(columnsLabel, 1, 0); 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); 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); } /** * Destructor. */ BrainBrowserWindowToolBarVolumeMontage::~BrainBrowserWindowToolBarVolumeMontage() { } /** * Update the surface montage options widget. * * @param browserTabContent * The active model display controller (may be NULL). */ void BrainBrowserWindowToolBarVolumeMontage::updateContent(BrowserTabContent* browserTabContent) { m_volumeMontageWidgetGroup->blockAllSignals(true); switch (browserTabContent->getSliceDrawingType()) { case VolumeSliceDrawingTypeEnum::VOLUME_SLICE_DRAW_MONTAGE: m_montageEnabledAction->setChecked(true); break; case VolumeSliceDrawingTypeEnum::VOLUME_SLICE_DRAW_SINGLE: m_montageEnabledAction->setChecked(false); break; } m_montageRowsSpinBox->setValue(browserTabContent->getMontageNumberOfRows()); m_montageColumnsSpinBox->setValue(browserTabContent->getMontageNumberOfColumns()); m_montageSpacingSpinBox->setValue(browserTabContent->getMontageSliceSpacing()); m_volumeMontageWidgetGroup->blockAllSignals(false); } /** * Called when montage enabled button toggled. */ void BrainBrowserWindowToolBarVolumeMontage::montageEnabledActionToggled(bool) { VolumeSliceDrawingTypeEnum::Enum drawingType = VolumeSliceDrawingTypeEnum::VOLUME_SLICE_DRAW_SINGLE; if (m_montageEnabledAction->isChecked()) { drawingType = VolumeSliceDrawingTypeEnum::VOLUME_SLICE_DRAW_MONTAGE; } BrowserTabContent* btc = this->getTabContentFromSelectedTab(); btc->setSliceDrawingType(drawingType); this->updateGraphicsWindow(); this->updateOtherYokedWindows(); } /** * Called when montage rows spin box value is changed. */ void BrainBrowserWindowToolBarVolumeMontage::montageRowsSpinBoxValueChanged(int /*i*/) { BrowserTabContent* btc = this->getTabContentFromSelectedTab(); btc->setMontageNumberOfRows(m_montageRowsSpinBox->value()); this->updateGraphicsWindow(); this->updateOtherYokedWindows(); } /** * Called when montage columns spin box value is changed. */ void BrainBrowserWindowToolBarVolumeMontage::montageColumnsSpinBoxValueChanged(int /*i*/) { BrowserTabContent* btc = this->getTabContentFromSelectedTab(); btc->setMontageNumberOfColumns(m_montageColumnsSpinBox->value()); this->updateGraphicsWindow(); this->updateOtherYokedWindows(); } /** * Called when montage spacing spin box value is changed. */ void BrainBrowserWindowToolBarVolumeMontage::montageSpacingSpinBoxValueChanged(int /*i*/) { BrowserTabContent* btc = this->getTabContentFromSelectedTab(); btc->setMontageSliceSpacing(m_montageSpacingSpinBox->value()); this->updateGraphicsWindow(); this->updateOtherYokedWindows(); } connectome-workbench-1.2.3+git41-gc4c6c90/src/GuiQt/BrainBrowserWindowToolBarVolumeMontage.h000066400000000000000000000050061300200146000315240ustar00rootroot00000000000000#ifndef __BRAIN_BROWSER_WINDOW_TOOL_BAR_VOLUME_MONTAGE_H__ #define __BRAIN_BROWSER_WINDOW_TOOL_BAR_VOLUME_MONTAGE_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" class QSpinBox; namespace caret { class WuQWidgetObjectGroup; class BrainBrowserWindowToolBarVolumeMontage : public BrainBrowserWindowToolBarComponent { Q_OBJECT public: BrainBrowserWindowToolBarVolumeMontage(BrainBrowserWindowToolBar* parentToolBar); virtual ~BrainBrowserWindowToolBarVolumeMontage(); virtual void updateContent(BrowserTabContent* browserTabContent); // ADD_NEW_METHODS_HERE private slots: void montageRowsSpinBoxValueChanged(int i); void montageColumnsSpinBoxValueChanged(int i); void montageSpacingSpinBoxValueChanged(int i); void montageEnabledActionToggled(bool); private: BrainBrowserWindowToolBarVolumeMontage(const BrainBrowserWindowToolBarVolumeMontage&); BrainBrowserWindowToolBarVolumeMontage& operator=(const BrainBrowserWindowToolBarVolumeMontage&); BrainBrowserWindowToolBar* m_parentToolBar; QSpinBox* m_montageRowsSpinBox; QSpinBox* m_montageColumnsSpinBox; QSpinBox* m_montageSpacingSpinBox; QAction* m_montageEnabledAction; WuQWidgetObjectGroup* m_volumeMontageWidgetGroup; // ADD_NEW_MEMBERS_HERE }; #ifdef __BRAIN_BROWSER_WINDOW_TOOL_BAR_VOLUME_MONTAGE_DECLARE__ // #endif // __BRAIN_BROWSER_WINDOW_TOOL_BAR_VOLUME_MONTAGE_DECLARE__ } // namespace #endif //__BRAIN_BROWSER_WINDOW_TOOL_BAR_VOLUME_MONTAGE_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/GuiQt/BrainOpenGLWidget.cxx000066400000000000000000001576611300200146000256050ustar00rootroot00000000000000/*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_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" #include "CaretAssert.h" #include "CaretLogger.h" #include "CaretPreferences.h" #include "CursorManager.h" #include "DummyFontTextRenderer.h" #include "EventBrainReset.h" #include "EventImageCapture.h" #include "EventModelGetAll.h" #include "EventManager.h" #include "EventBrowserWindowContentGet.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" #include "KeyEvent.h" #include "MathFunctions.h" #include "Matrix4x4.h" #include "Model.h" #include "MouseEvent.h" #include "QGLWidgetTextRenderer.h" #include "SelectionManager.h" #include "SelectionItemAnnotation.h" #include "SelectionItemSurfaceNode.h" #include "SelectionItemVoxelEditing.h" #include "SessionManager.h" #include "Surface.h" #include "TileTabsConfiguration.h" #include "UserInputModeAnnotations.h" #include "UserInputModeBorders.h" #include "UserInputModeFoci.h" #include "UserInputModeImage.h" #include "UserInputModeView.h" #include "UserInputModeVolumeEdit.h" using namespace caret; /** * Constructor. * * NOTE: We do not use "sharing" of textures and display lists * between windows. There are some issues with sharing and * recreation of OpenGL contexts such as when renderPixmap() * function is used. * * @param * The parent widget. */ BrainOpenGLWidget::BrainOpenGLWidget(QWidget* parent, const int32_t windowIndex) : QGLWidget(parent), windowIndex(windowIndex), m_textRenderer(NULL) { this->openGL = NULL; this->borderBeingDrawn = new Border(); m_mousePositionValid = false; m_mousePositionEvent.grabNew(new MouseEvent(NULL, NULL, -1, 0, 0, 0, 0, 0, 0, false)); this->userInputAnnotationsModeProcessor = new UserInputModeAnnotations(windowIndex); this->userInputBordersModeProcessor = new UserInputModeBorders(this->borderBeingDrawn, windowIndex); this->userInputFociModeProcessor = new UserInputModeFoci(windowIndex); this->userInputImageModeProcessor = new UserInputModeImage(windowIndex); this->userInputVolumeEditModeProcessor = new UserInputModeVolumeEdit(windowIndex); this->userInputViewModeProcessor = new UserInputModeView(); this->selectedUserInputProcessor = this->userInputViewModeProcessor; this->selectedUserInputProcessor->initialize(); this->mousePressX = -10000; this->mousePressY = -10000; this->mouseNewDraggingStartedFlag = false; m_newKeyPressStartedFlag = true; /* * Mouse tracking must be on to receive mouse move events * when the mouse is NOT down. When this property is false * mouse move events are only received when the at least * one mouse button is down. */ setMouseTracking(true); EventManager::get()->addEventListener(this, EventTypeEnum::EVENT_BRAIN_RESET); 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); } /** * Destructor. */ 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 delete this->borderBeingDrawn; EventManager::get()->removeAllEventsFromListener(this); } /** * Initializes graphics. */ void BrainOpenGLWidget::initializeGL() { if (this->openGL == NULL) { this->openGL = new BrainOpenGLFixedPipeline(this->windowIndex, createTextRenderer()); } else { /* * 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(). */ this->openGL->setTextRenderer(createTextRenderer()); } this->openGL->initializeOpenGL(); this->lastMouseX = 0; this->lastMouseY = 0; this->isMousePressedNearToolBox = false; this->setFocusPolicy(Qt::StrongFocus); CaretLogConfig(getOpenGLInformation()); if (s_defaultGLFormatInitialized == false) { CaretLogSevere("PROGRAM ERROR: The default QGLFormat has not been set.\n" "Need to call BrainOpenGLWidget::initializeDefaultGLFormat() prior to " "instantiating an instance of this class."); } } /** * @return Information about OpenGL. */ QString BrainOpenGLWidget::getOpenGLInformation() { AString info; QGLFormat format = this->format(); info += ("OpenGL Context:" "\n Accum: " + AString::fromBool(format.accum()) + "\n Accum size: " + AString::number(format.accumBufferSize()) + "\n Alpha: " + AString::fromBool(format.alpha()) + "\n Alpha size: " + AString::number(format.alphaBufferSize()) + "\n Depth: " + AString::fromBool(format.depth()) + "\n Depth size: " + AString::number(format.depthBufferSize()) + "\n Direct Rendering: " + AString::fromBool(format.directRendering()) + "\n Red size: " + AString::number(format.redBufferSize()) + "\n Green size: " + AString::number(format.greenBufferSize()) + "\n Blue size: " + AString::number(format.blueBufferSize()) + "\n Double Buffer: " + AString::fromBool(format.doubleBuffer()) + "\n RGBA: " + AString::fromBool(format.rgba()) + "\n Samples: " + AString::fromBool(format.sampleBuffers()) + "\n Samples size: " + AString::number(format.samples()) + "\n Stencil: " + AString::fromBool(format.stencil()) + "\n Stencil size: " + AString::number(format.stencilBufferSize()) + "\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())); info += ("\n\n" + this->openGL->getOpenGLInformation()); #if BRAIN_OPENGL_INFO_SUPPORTS_DISPLAY_LISTS int32_t numDisplayLists = 0; for (GLuint iList = 1; iList < 1000; iList++) { if (glIsList(iList)) { numDisplayLists++; } } info += ("\nAt least " + 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. */ void BrainOpenGLWidget::resizeGL(int w, int h) { this->windowWidth[this->windowIndex] = w; this->windowHeight[this->windowIndex] = h; } /** * @return Pointer to the border that is being drawn. */ Border* BrainOpenGLWidget::getBorderBeingDrawn() { return this->borderBeingDrawn; } /** * Clear the contents for drawing into the viewports. */ 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(); } /** * Update the cursor from the active user input processor. */ void BrainOpenGLWidget::updateCursor() { /* * Set the cursor to that requested by the user input processor */ CursorEnum::Enum cursor = this->selectedUserInputProcessor->getCursor(); GuiManager::get()->getCursorManager()->setCursorForWidget(this, cursor); } /** * Paints the graphics. */ void BrainOpenGLWidget::paintGL() { updateCursor(); this->clearDrawingViewportContents(); int windowViewport[4] = { 0, 0, this->windowWidth[this->windowIndex], this->windowHeight[this->windowIndex] }; BrainBrowserWindow* bbw = GuiManager::get()->getBrowserWindowByWindowIndex(this->windowIndex); EventBrowserWindowContentGet getModelEvent(this->windowIndex); EventManager::get()->sendEvent(getModelEvent.getPointer()); if (getModelEvent.isError()) { return; } if (bbw->isAspectRatioLocked()) { const float aspectRatio = bbw->getAspectRatio(); if (aspectRatio > 0.0) { BrainOpenGLViewportContent::adjustViewportForAspectRatio(windowViewport, aspectRatio); } } /* * Highlighting of border points */ this->openGL->setDrawHighlightedEndPoints(false); if (this->selectedUserInputProcessor == this->userInputBordersModeProcessor) { this->openGL->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); } else if (numToDraw > 1) { const int32_t windowWidth = windowViewport[2]; const int32_t windowHeight = windowViewport[3]; std::vector rowHeights; std::vector columnsWidths; /* * Determine if default configuration for tiles */ TileTabsConfiguration* tileTabsConfiguration = getModelEvent.getTileTabsConfiguration(); CaretAssert(tileTabsConfiguration); /* * 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; } /* * 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)); } this->drawingViewportContents = BrainOpenGLViewportContent::createViewportContentForTileTabs(allTabs, tileTabsConfiguration, gapsAndMargins, windowIndex, windowViewport, getModelEvent.getTabIndexForTileTabsHighlighting()); } if (this->selectedUserInputProcessor == userInputBordersModeProcessor) { this->openGL->setBorderBeingDrawn(this->borderBeingDrawn); } else { this->openGL->setBorderBeingDrawn(NULL); } this->openGL->drawModels(GuiManager::get()->getBrain(), this->drawingViewportContents); /* * Issue browser window redrawn event */ if (bbw != NULL) { EventManager::get()->sendEvent(EventBrowserWindowGraphicsRedrawn(bbw).getPointer()); } } /** * Override of event handling. */ bool BrainOpenGLWidget::event(QEvent* event) { const bool toolTipsEnabled = false; if (toolTipsEnabled) { if (event->type() == QEvent::ToolTip) { QHelpEvent* helpEvent = static_cast(event); CaretAssert(helpEvent); QPoint globalXY = helpEvent->globalPos(); QPoint xy = helpEvent->pos(); static int counter = 0; std::cout << "Displaying tooltip " << counter++ << " at global (" << globalXY.x() << ", " << globalXY.y() << ") at pos (" << xy.x() << ", " << xy.y() << ")" << std::endl; QToolTip::showText(globalXY, "This is the tooltip " + AString::number(counter)); return true; } } return QGLWidget::event(event); } /** * Receive Content Menu events from Qt. * @param contextMenuEvent * The context menu event. */ void BrainOpenGLWidget::contextMenuEvent(QContextMenuEvent* contextMenuEvent) { const int mouseX = contextMenuEvent->x(); const int mouseY = this->height() - contextMenuEvent->y(); BrainOpenGLViewportContent* viewportContent = this->getViewportContentAtXY(mouseX, mouseY); if (viewportContent != NULL) { MouseEvent mouseEvent(viewportContent, this, this->windowIndex, mouseX, mouseY, 0, 0, mouseX, mouseY, false); this->selectedUserInputProcessor->showContextMenu(mouseEvent, contextMenuEvent->globalPos(), this); } } /** * Receive Mouse Wheel events from Qt. A wheel event is that same * as CTRL-LEFT-DRAG. The wheel's change in value is reported as * change in Y. Change in X is reported as zero. * * @param we * The wheel event. */ void BrainOpenGLWidget::wheelEvent(QWheelEvent* we) { const int wheelX = we->x(); const int wheelY = this->windowHeight[this->windowIndex] - we->y(); int delta = we->delta(); delta = MathFunctions::limitRange(delta, -2, 2); /* * Use location of mouse press so that the model * being manipulated does not change if mouse moves * out of its viewport without releasing the mouse * button. */ BrainOpenGLViewportContent* viewportContent = this->getViewportContentAtXY(wheelX, wheelY); if (viewportContent != NULL) { MouseEvent mouseEvent(viewportContent, this, this->windowIndex, wheelX, wheelY, 0, delta, 0, 0, this->mouseNewDraggingStartedFlag); this->selectedUserInputProcessor->mouseLeftDragWithCtrl(mouseEvent); } we->accept(); } /* * A mouse event that is the middle mouse button but with no keys pressed * is reported as a SHIFT-LEFT-DRAG and the mouse event is changed. * * @param mouseButtons * Button state when event was generated * @param button * Button that caused the event. * @param keyModifiers * Keys that are down, may be modified. * @param isMouseMoving * True if mouse is moving, else false. */ void BrainOpenGLWidget::checkForMiddleMouseButton(Qt::MouseButtons& mouseButtons, Qt::MouseButton& button, Qt::KeyboardModifiers& keyModifiers, const bool isMouseMoving) { if (isMouseMoving) { if (button == Qt::NoButton) { if (mouseButtons == Qt::MiddleButton) { if (keyModifiers == Qt::NoModifier) { mouseButtons = Qt::LeftButton; button = Qt::NoButton; keyModifiers = Qt::ShiftModifier; } } } } else { if (button == Qt::MiddleButton) { if (keyModifiers == Qt::NoModifier) { button = Qt::LeftButton; keyModifiers = Qt::ShiftModifier; } } } } /** * Receive key press events from Qt. * @param e * The key event. */ void BrainOpenGLWidget::keyPressEvent(QKeyEvent* e) { Qt::KeyboardModifiers keyModifiers = e->modifiers(); const bool shiftKeyDownFlag = ((keyModifiers & Qt::ShiftModifier) != 0); KeyEvent keyEvent(this, this->windowIndex, e->key(), m_newKeyPressStartedFlag, shiftKeyDownFlag); this->selectedUserInputProcessor->keyPressEvent(keyEvent); e->accept(); m_newKeyPressStartedFlag = false; } /** * Receive key release events from Qt. * @param e * The key event. */ void BrainOpenGLWidget::keyReleaseEvent(QKeyEvent* e) { m_newKeyPressStartedFlag = true; e->accept(); } /** * Receive mouse press events from Qt. * @param me * The mouse event. */ void BrainOpenGLWidget::mousePressEvent(QMouseEvent* me) { Qt::MouseButton button = me->button(); Qt::KeyboardModifiers keyModifiers = me->modifiers(); Qt::MouseButtons mouseButtons = me->buttons(); checkForMiddleMouseButton(mouseButtons, button, keyModifiers, false); /* * When the mouse is dragged, a mouse input receiver may want to * know that a new dragging has started. */ this->mouseNewDraggingStartedFlag = true; this->isMousePressedNearToolBox = false; if (button == Qt::LeftButton) { const int mouseX = me->x(); const int mouseY = this->windowHeight[this->windowIndex] - me->y(); this->mousePressX = mouseX; this->mousePressY = mouseY; this->lastMouseX = mouseX; this->lastMouseY = mouseY; this->mouseMovementMinimumX = mouseX; this->mouseMovementMaximumX = mouseX; this->mouseMovementMinimumY = mouseY; this->mouseMovementMaximumY = mouseY; /* * The user may intend to increase the size of a toolbox * but instead misses the edge of the toolbox when trying * to drag the toolbox and make it larger. So, indicate * when the user is very close to the edge of the graphics * window. */ const int nearToolBoxDistance = 5; if ((mouseX < nearToolBoxDistance) || (mouseX > (this->windowWidth[this->windowIndex] - 5)) || (mouseY < nearToolBoxDistance) || (mouseY > (this->windowHeight[this->windowIndex] - 5))) { this->isMousePressedNearToolBox = true; } BrainOpenGLViewportContent* viewportContent = this->getViewportContentAtXY(mouseX, mouseY); if (viewportContent != NULL) { MouseEvent mouseEvent(viewportContent, this, this->windowIndex, mouseX, mouseY, 0, 0, this->mousePressX, this->mousePressY, this->mouseNewDraggingStartedFlag); if (keyModifiers == Qt::NoModifier) { this->selectedUserInputProcessor->mouseLeftPress(mouseEvent); } else if (keyModifiers == Qt::ShiftModifier) { // not implemented this->selectedUserInputProcessor->mouseLeftPressWithShift(mouseEvent); } } } else { this->mousePressX = -10000; this->mousePressY = -10000; } me->accept(); } /** * Receive mouse button release events from Qt. * @param me * The mouse event. */ void BrainOpenGLWidget::mouseReleaseEvent(QMouseEvent* me) { Qt::MouseButton button = me->button(); Qt::KeyboardModifiers keyModifiers = me->modifiers(); Qt::MouseButtons mouseButtons = me->buttons(); checkForMiddleMouseButton(mouseButtons, button, keyModifiers, false); if (button == Qt::LeftButton) { const int mouseX = me->x(); const int mouseY = this->windowHeight[this->windowIndex] - me->y(); this->mouseMovementMinimumX = std::min(this->mouseMovementMinimumX, mouseX); this->mouseMovementMaximumX = std::max(this->mouseMovementMaximumX, mouseX); this->mouseMovementMinimumY = std::min(this->mouseMovementMinimumY, mouseY); this->mouseMovementMaximumY = std::max(this->mouseMovementMaximumY, mouseY); const int dx = this->mouseMovementMaximumX - this->mouseMovementMinimumX; const int dy = this->mouseMovementMaximumY - this->mouseMovementMinimumY; const int absDX = (dx >= 0) ? dx : -dx; const int absDY = (dy >= 0) ? dy : -dy; { /* * Mouse button RELEASE event */ BrainOpenGLViewportContent* viewportContent = this->getViewportContentAtXY(mouseX, mouseY); if (viewportContent != NULL) { MouseEvent mouseEvent(viewportContent, this, this->windowIndex, mouseX, mouseY, 0, 0, this->mousePressX, this->mousePressY, this->mouseNewDraggingStartedFlag); this->selectedUserInputProcessor->mouseLeftRelease(mouseEvent); } } /* * Use location of mouse press so that the model * being manipulated does not change if mouse moves * out of its viewport without releasing the mouse * button. */ BrainOpenGLViewportContent* viewportContent = this->getViewportContentAtXY(this->mousePressX, this->mousePressY); if (viewportContent != NULL) { if ((absDX <= BrainOpenGLWidget::MOUSE_MOVEMENT_TOLERANCE) && (absDY <= BrainOpenGLWidget::MOUSE_MOVEMENT_TOLERANCE)) { MouseEvent mouseEvent(viewportContent, this, this->windowIndex, mouseX, mouseY, dx, dy, this->mousePressX, this->mousePressY, this->mouseNewDraggingStartedFlag); if (keyModifiers == Qt::NoModifier) { this->selectedUserInputProcessor->mouseLeftClick(mouseEvent); } else if (keyModifiers == Qt::ShiftModifier) { this->selectedUserInputProcessor->mouseLeftClickWithShift(mouseEvent); } else if (keyModifiers == (Qt::ShiftModifier | Qt::ControlModifier)) { this->selectedUserInputProcessor->mouseLeftClickWithCtrlShift(mouseEvent); } } } } this->mousePressX = -10000; this->mousePressY = -10000; this->isMousePressedNearToolBox = false; me->accept(); } /** * Receive mouse button double click events from Qt. * @param me * The mouse event. */ void BrainOpenGLWidget::mouseDoubleClickEvent(QMouseEvent* me) { Qt::MouseButton button = me->button(); Qt::KeyboardModifiers keyModifiers = me->modifiers(); Qt::MouseButtons mouseButtons = me->buttons(); checkForMiddleMouseButton(mouseButtons, button, keyModifiers, false); if (button == Qt::LeftButton) { if (keyModifiers == Qt::NoModifier) { const int mouseX = me->x(); const int mouseY = this->windowHeight[this->windowIndex] - me->y(); /* * Use location of mouse press so that the model * being manipulated does not change if mouse moves * out of its viewport without releasing the mouse * button. */ BrainOpenGLViewportContent* viewportContent = this->getViewportContentAtXY(mouseX, mouseY); if (viewportContent != NULL) { MouseEvent mouseEvent(viewportContent, this, this->windowIndex, mouseX, mouseY, 0, 0, this->mousePressX, this->mousePressY, this->mouseNewDraggingStartedFlag); this->selectedUserInputProcessor->mouseLeftDoubleClick(mouseEvent); } } } this->isMousePressedNearToolBox = false; me->accept(); } void BrainOpenGLWidget::enterEvent(QEvent* /*e*/) { m_mousePositionValid = true; } void BrainOpenGLWidget::leaveEvent(QEvent* /*e*/) { m_mousePositionValid = false; this->selectedUserInputProcessor->setMousePosition(m_mousePositionEvent, m_mousePositionValid); } /** * Get the viewport content at the given location. * @param x * X-coordinate. * @param y * Y-coordinate. */ 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; } } return viewportContent; } /** * Get all viewport content. If tile tabs is ON, the output will contain * viewport content for all tabs. Otherwise, the output will contain viewport * content for only the selected tab. * * @return Viewport content. */ std::vector BrainOpenGLWidget::getViewportContent() const { std::vector contentOut(this->drawingViewportContents.begin(), this->drawingViewportContents.end()); return contentOut; } /** * Perform identification on all items EXCEPT voxel editing. * * @param x * X-coordinate for identification. * @param y * Y-coordinate for identification. * @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. * @return * SelectionManager providing identification information. */ SelectionManager* BrainOpenGLWidget::performIdentification(const int x, const int y, const bool applySelectionBackgroundFiltering) { BrainOpenGLViewportContent* idViewport = this->getViewportContentAtXY(x, y); this->makeCurrent(); CaretLogFine("Performing selection"); SelectionManager* idManager = GuiManager::get()->getBrain()->getSelectionManager(); idManager->reset(); idManager->setAllSelectionsEnabled(true); idManager->getVoxelEditingIdentification()->setEnabledForSelection(false); if (idViewport != NULL) { this->openGL->selectModel(GuiManager::get()->getBrain(), idViewport, x, y, applySelectionBackgroundFiltering); } return idManager; } /** * Perform identification of only annotations. Identification of other * data types is off. * * @param x * X-coordinate for identification. * @param y * Y-coordinate for identification. * @return * A pointer to the annotation selection item. Its * "isValid()" method may be queried to determine * if the selected annotation is valid. */ SelectionItemAnnotation* BrainOpenGLWidget::performIdentificationAnnotations(const int x, const int y) { BrainOpenGLViewportContent* idViewport = this->getViewportContentAtXY(x, y); this->makeCurrent(); CaretLogFine("Performing selection"); SelectionManager* idManager = GuiManager::get()->getBrain()->getSelectionManager(); idManager->reset(); idManager->setAllSelectionsEnabled(false); SelectionItemAnnotation* annotationID = idManager->getAnnotationIdentification(); annotationID->setEnabledForSelection(true); if (idViewport != 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->selectModel(GuiManager::get()->getBrain(), idViewport, x, y, true); } return annotationID; } /** * Perform identification of only voxel editing. Identification of other * data types is off. * * @param editingVolumeFile * Volume file that is being edited. * @param x * X-coordinate for identification. * @param y * Y-coordinate for identification. * @return * SelectionManager providing identification information. */ SelectionManager* BrainOpenGLWidget::performIdentificationVoxelEditing(VolumeFile* editingVolumeFile, const int x, const int y) { BrainOpenGLViewportContent* idViewport = this->getViewportContentAtXY(x, y); this->makeCurrent(); CaretLogFine("Performing selection"); SelectionManager* idManager = GuiManager::get()->getBrain()->getSelectionManager(); idManager->reset(); idManager->setAllSelectionsEnabled(false); SelectionItemVoxelEditing* idVoxelEdit = idManager->getVoxelEditingIdentification(); idVoxelEdit->setEnabledForSelection(true); idVoxelEdit->setVolumeFileForEditing(editingVolumeFile); if (idViewport != 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->selectModel(GuiManager::get()->getBrain(), idViewport, x, y, true); } return idManager; } /** * Project the given item to a model. * * @param x * X-coordinate for projection. * @param y * Y-coordinate for projection. * @param projectionOut * Projection updated for the given x and y coordinates. */ void BrainOpenGLWidget::performProjection(const int x, const int y, SurfaceProjectedItem& projectionOut) { 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(), projectionViewport, x, y, projectionOut); } } /** * Receive mouse move events from Qt. * @param me * The mouse event. */ void BrainOpenGLWidget::mouseMoveEvent(QMouseEvent* me) { Qt::MouseButton button = me->button(); Qt::KeyboardModifiers keyModifiers = me->modifiers(); Qt::MouseButtons mouseButtons = me->buttons(); checkForMiddleMouseButton(mouseButtons, button, keyModifiers, true); const int mouseX = me->x(); const int mouseY = this->windowHeight[this->windowIndex] - me->y(); if (button == Qt::NoButton) { if (mouseButtons == Qt::LeftButton) { this->mouseMovementMinimumX = std::min(this->mouseMovementMinimumX, mouseX); this->mouseMovementMaximumX = std::max(this->mouseMovementMaximumX, mouseX); this->mouseMovementMinimumY = std::min(this->mouseMovementMinimumY, mouseY); this->mouseMovementMaximumY = std::max(this->mouseMovementMaximumY, mouseY); const int dx = mouseX - this->lastMouseX; const int dy = mouseY - this->lastMouseY; const int absDX = (dx >= 0) ? dx : -dx; const int absDY = (dy >= 0) ? dy : -dy; if ((absDX > 0) || (absDY > 0)) { /* * Use location of mouse press so that the model * being manipulated does not change if mouse moves * out of its viewport without releasing the mouse * button. */ BrainOpenGLViewportContent* viewportContent = this->getViewportContentAtXY(this->mousePressX, this->mousePressY); if (viewportContent != NULL) { MouseEvent mouseEvent(viewportContent, this, this->windowIndex, mouseX, mouseY, dx, dy, this->mousePressX, this->mousePressY, this->mouseNewDraggingStartedFlag); if (keyModifiers == Qt::NoModifier) { this->selectedUserInputProcessor->mouseLeftDrag(mouseEvent); } else if (keyModifiers == Qt::ControlModifier) { this->selectedUserInputProcessor->mouseLeftDragWithCtrl(mouseEvent); } else if (keyModifiers == Qt::ShiftModifier) { this->selectedUserInputProcessor->mouseLeftDragWithShift(mouseEvent); } else if (keyModifiers == Qt::AltModifier) { this->selectedUserInputProcessor->mouseLeftDragWithAlt(mouseEvent); } else if (keyModifiers == (Qt::ShiftModifier | Qt::ControlModifier)) { this->selectedUserInputProcessor->mouseLeftDragWithCtrlShift(mouseEvent); } this->mouseNewDraggingStartedFlag = false; } } this->lastMouseX = mouseX; this->lastMouseY = mouseY; } else if (mouseButtons == Qt::NoButton) { BrainOpenGLViewportContent* viewportContent = this->getViewportContentAtXY(mouseX, mouseY); if (viewportContent != NULL) { MouseEvent mouseEvent(viewportContent, this, this->windowIndex, mouseX, mouseY, 0, 0, this->mousePressX, this->mousePressY, this->mouseNewDraggingStartedFlag); if (keyModifiers == Qt::NoModifier) { this->selectedUserInputProcessor->mouseMove(mouseEvent); } else if (keyModifiers == Qt::ShiftModifier) { this->selectedUserInputProcessor->mouseMoveWithShift(mouseEvent); } } } } BrainOpenGLViewportContent* viewportContent = this->getViewportContentAtXY(mouseX, mouseY); if (viewportContent != NULL) { m_mousePositionEvent.grabNew(new MouseEvent(viewportContent, this, this->windowIndex, mouseX, mouseY, 0, 0, this->mousePressX, this->mousePressY, this->mouseNewDraggingStartedFlag)); this->selectedUserInputProcessor->setMousePosition(m_mousePositionEvent, m_mousePositionValid); } else { } me->accept(); } /** * Receive events from the event manager. * * @param event * Event sent by event manager. */ void BrainOpenGLWidget::receiveEvent(Event* event) { if (event->getEventType() == EventTypeEnum::EVENT_BRAIN_RESET) { EventBrainReset* brainResetEvent = dynamic_cast(event); CaretAssert(brainResetEvent); this->borderBeingDrawn->clear(); 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); CaretAssert(updateAllEvent); updateAllEvent->setEventProcessed(); if (updateAllEvent->isRepaint()) { this->repaint(); } else { this->updateGL(); } } else if (event->getEventType() == EventTypeEnum::EVENT_GRAPHICS_UPDATE_ONE_WINDOW) { EventGraphicsUpdateOneWindow* updateOneEvent = dynamic_cast(event); CaretAssert(updateOneEvent); if (updateOneEvent->getWindowIndex() == this->windowIndex) { updateOneEvent->setEventProcessed(); this->updateGL(); } else { /* * If a window is yoked, update its graphics. */ EventBrowserWindowContentGet getModelEvent(this->windowIndex); EventManager::get()->sendEvent(getModelEvent.getPointer()); if (getModelEvent.isError()) { return; } bool needUpdate = false; if (needUpdate) { this->updateGL(); } } } else if (event->getEventType() == EventTypeEnum::EVENT_GET_OR_SET_USER_INPUT_MODE) { EventGetOrSetUserInputModeProcessor* inputModeEvent = dynamic_cast(event); CaretAssert(inputModeEvent); if (inputModeEvent->getWindowIndex() == this->windowIndex) { if (inputModeEvent->isGetUserInputMode()) { inputModeEvent->setUserInputProcessor(this->selectedUserInputProcessor); } else if (inputModeEvent->isSetUserInputMode()) { UserInputModeAbstract* newUserInputProcessor = NULL; switch (inputModeEvent->getUserInputMode()) { case UserInputModeAbstract::INVALID: CaretAssertMessage(0, "INVALID is NOT allowed for user input mode"); break; case UserInputModeAbstract::ANNOTATIONS: newUserInputProcessor = this->userInputAnnotationsModeProcessor; break; case UserInputModeAbstract::BORDERS: newUserInputProcessor = this->userInputBordersModeProcessor; break; case UserInputModeAbstract::FOCI: newUserInputProcessor = this->userInputFociModeProcessor; break; case UserInputModeAbstract::IMAGE: newUserInputProcessor = this->userInputImageModeProcessor; break; case UserInputModeAbstract::VOLUME_EDIT: newUserInputProcessor = this->userInputVolumeEditModeProcessor; break; case UserInputModeAbstract::VIEW: newUserInputProcessor = this->userInputViewModeProcessor; break; } if (newUserInputProcessor != NULL) { if (newUserInputProcessor != this->selectedUserInputProcessor) { this->selectedUserInputProcessor->finish(); this->selectedUserInputProcessor = newUserInputProcessor; this->selectedUserInputProcessor->initialize(); } } } inputModeEvent->setEventProcessed(); } } else if (event->getEventType() == EventTypeEnum::EVENT_IMAGE_CAPTURE) { EventImageCapture* imageCaptureEvent = dynamic_cast(event); CaretAssert(imageCaptureEvent); if (imageCaptureEvent->getBrowserWindowIndex() == this->windowIndex) { captureImage(imageCaptureEvent); imageCaptureEvent->setEventProcessed(); } } else if (event->getEventType() == EventTypeEnum::EVENT_IDENTIFICATION_REQUEST) { EventIdentificationRequest* idRequestEvent = dynamic_cast(event); CaretAssert(idRequestEvent); if (idRequestEvent->getWindowIndex() == this->windowIndex) { SelectionManager* sm = performIdentification(idRequestEvent->getWindowX(), idRequestEvent->getWindowY(), false); idRequestEvent->setSelectionManager(sm); idRequestEvent->setEventProcessed(); } } else if (event->getEventType() == EventTypeEnum::EVENT_USER_INTERFACE_UPDATE) { EventUserInterfaceUpdate* guiUpdateEvent = dynamic_cast(event); CaretAssert(guiUpdateEvent); guiUpdateEvent->setEventProcessed(); this->selectedUserInputProcessor->update(); } else { } } /** * Capture an image using the parameters from the event. * * @param imageCaptureEvent * The image capture event. */ void BrainOpenGLWidget::captureImage(EventImageCapture* imageCaptureEvent) { const int oldSizeX = this->windowWidth[this->windowIndex]; const int oldSizeY = this->windowHeight[this->windowIndex]; /* * 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(); /* * Force immediate mode since problems with display lists * in image capture. */ BrainOpenGLShape::setImmediateModeOverride(true); QImage image; const CaretPreferences* prefs = SessionManager::get()->getCaretPreferences(); const ImageCaptureMethodEnum::Enum imageCaptureMethod = prefs->getImageCaptureMethod(); switch (imageCaptureMethod) { case ImageCaptureMethodEnum::IMAGE_CAPTURE_WITH_GRAB_FRAME_BUFFER: { /* * Grab frame buffer seems to have a bug in that it grabs * the previous buffer on Mac so repaint to ensure frame * buffer is updated. (repaint() updates immediately, * update() is a scheduled update). */ repaint(); image = grabFrameBuffer(); if ((captureOffsetX > 0) || (captureOffsetY > 0)) { 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); } } } } break; case ImageCaptureMethodEnum::IMAGE_CAPTURE_WITH_RENDER_PIXMAP: { /* * 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 * 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 * the rendering takes place, there is no empty * 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); image = pixmap.toImage(); textureManager->deleteAllTexturesForWindow(this->windowIndex); this->openGL->setTextRenderer(createTextRenderer()); } break; } if ((image.size().width() <= 0) || (image.size().height() <= 0)) { imageCaptureEvent->setErrorMessage("Image capture appears to have failed (invalid size)."); } else { imageCaptureEvent->setImage(image); uint8_t backgroundColor[3]; this->openGL->getBackgroundColor(backgroundColor); imageCaptureEvent->setBackgroundColor(backgroundColor); } BrainOpenGLShape::setImmediateModeOverride(false); this->resizeGL(oldSizeX, oldSizeY); } /** * Initialize the OpenGL format. This must be called * prior to initializing an instance of this class so * that the OpenGL is setup properly. */ void BrainOpenGLWidget::initializeDefaultGLFormat() { QGLFormat glfmt; glfmt.setAccum(false); glfmt.setAlpha(true); glfmt.setAlphaBufferSize(8); glfmt.setDepth(true); glfmt.setDepthBufferSize(24); glfmt.setDirectRendering(true); glfmt.setDoubleBuffer(true); glfmt.setOverlay(false); glfmt.setSampleBuffers(false); glfmt.setStencil(false); glfmt.setStereo(false); glfmt.setRgba(true); glfmt.setRedBufferSize(8); glfmt.setGreenBufferSize(8); glfmt.setBlueBufferSize(8); QGLFormat::setDefaultFormat(glfmt); s_defaultGLFormatInitialized = true; } connectome-workbench-1.2.3+git41-gc4c6c90/src/GuiQt/BrainOpenGLWidget.h000066400000000000000000000144371300200146000252230ustar00rootroot00000000000000 #ifndef __BRAIN_OPENGL_WIDGET_H__ #define __BRAIN_OPENGL_WIDGET_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 "CaretOpenGLInclude.h" #include #include #include #include "BrainConstants.h" #include "CaretPointer.h" #include "EventListenerInterface.h" class QMouseEvent; namespace caret { class Border; class BrainOpenGL; class BrainOpenGLTextRenderInterface; class BrainOpenGLViewportContent; class BrowserTabContent; class EventImageCapture; class SelectionItemAnnotation; class SelectionManager; class Model; class MouseEvent; class SurfaceProjectedItem; class UserInputModeAnnotations; class UserInputModeBorders; class UserInputModeFoci; class UserInputModeImage; class UserInputModeView; class UserInputModeVolumeEdit; class UserInputModeAbstract; class VolumeFile; class BrainOpenGLWidget : public QGLWidget, public EventListenerInterface { Q_OBJECT public: BrainOpenGLWidget(QWidget* parent, const int32_t windowIndex); ~BrainOpenGLWidget(); void receiveEvent(Event* event); SelectionManager* performIdentification(const int x, const int y, const bool applySelectionBackgroundFiltering); SelectionItemAnnotation* performIdentificationAnnotations(const int x, const int y); SelectionManager* performIdentificationVoxelEditing(VolumeFile* editingVolumeFile, const int x, const int y); void performProjection(const int x, const int y, SurfaceProjectedItem& projectionOut); Border* getBorderBeingDrawn(); static void initializeDefaultGLFormat(); QString getOpenGLInformation(); void updateCursor(); std::vector getViewportContent() const; protected: virtual void initializeGL(); virtual void resizeGL(int w, int h); virtual void paintGL(); virtual void contextMenuEvent(QContextMenuEvent* contextMenuEvent); virtual bool event(QEvent* event); virtual void keyPressEvent(QKeyEvent* e); virtual void keyReleaseEvent(QKeyEvent* e); virtual void mouseDoubleClickEvent(QMouseEvent* e); virtual void mouseMoveEvent(QMouseEvent* e); virtual void mousePressEvent(QMouseEvent* e); virtual void mouseReleaseEvent(QMouseEvent* e); virtual void wheelEvent(QWheelEvent* e); virtual void enterEvent(QEvent* e); virtual void leaveEvent(QEvent* e); private: BrainOpenGLTextRenderInterface* createTextRenderer(); void clearDrawingViewportContents(); BrainOpenGLViewportContent* getViewportContentAtXY(const int x, const int y); void checkForMiddleMouseButton(Qt::MouseButtons& mouseButtons, Qt::MouseButton& button, Qt::KeyboardModifiers& keyModifiers, const bool isMouseMoving); 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; int32_t windowWidth[BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_WINDOWS]; int32_t windowHeight[BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_WINDOWS]; int32_t mouseMovementMinimumX; int32_t mouseMovementMaximumX; int32_t mouseMovementMinimumY; int32_t mouseMovementMaximumY; bool mouseNewDraggingStartedFlag; static const int32_t MOUSE_MOVEMENT_TOLERANCE; int mousePressX; int mousePressY; bool isMousePressedNearToolBox; int lastMouseX; int lastMouseY; bool m_newKeyPressStartedFlag; UserInputModeAbstract* selectedUserInputProcessor; UserInputModeAnnotations* userInputAnnotationsModeProcessor; UserInputModeView* userInputViewModeProcessor; UserInputModeBorders* userInputBordersModeProcessor; UserInputModeFoci* userInputFociModeProcessor; UserInputModeImage* userInputImageModeProcessor; UserInputModeVolumeEdit* userInputVolumeEditModeProcessor; Border* borderBeingDrawn; bool m_mousePositionValid; CaretPointer m_mousePositionEvent; static bool s_defaultGLFormatInitialized; }; #ifdef __BRAIN_OPENGL_WIDGET_DEFINE__ const int32_t BrainOpenGLWidget::MOUSE_MOVEMENT_TOLERANCE = 0; //10; bool BrainOpenGLWidget::s_defaultGLFormatInitialized = false; #endif // __BRAIN_OPENGL_WIDGET_DEFINE__ } // namespace #endif // __BRAIN_OPENGL_WIDGET_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/GuiQt/BugReportDialog.cxx000066400000000000000000000165451300200146000253650ustar00rootroot00000000000000 /*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 __BUG_REPORT_DIALOG_DECLARE__ #include "BugReportDialog.h" #undef __BUG_REPORT_DIALOG_DECLARE__ #include "ApplicationInformation.h" #include "CaretAssert.h" #include "WuQtUtilities.h" using namespace caret; /** * \class caret::BugReportDialog * \brief Dialog for reporting bugs in Workbench. * \ingroup GuiQt */ /** * Constructor. * * @param parent * Parent on which this dialog is displayed. * @param openGLInformation * Information about OpenGL. */ BugReportDialog::BugReportDialog(QWidget* parent, const AString& openGLInformation) : WuQDialogNonModal("Report Workbench Bug", parent) { const AString emailAddress("workbench_bugs@brainvis.wustl.edu"); m_emailAddressURL = ("mailto:" + emailAddress + "?subject=Workbench Bug Report"); m_uploadWebSite = "http://brainvis.wustl.edu/cgi-bin/upload.cgi"; const AString newLines("\n\n"); AString bugInfo; bugInfo.appendWithNewLine(""); bugInfo.appendWithNewLine("(1) SUMMARY_OF_PROBLEM" + newLines); bugInfo.appendWithNewLine("(2) HOW TO REPRODUCE PROBLEM" + newLines); bugInfo.appendWithNewLine("(3) WHAT HAPPENS" + newLines); bugInfo.appendWithNewLine("(4) WHAT SHOULD HAPPEN" + newLines); bugInfo.appendWithNewLine("(5) NAME OF UPLOADED DATA ZIP FILE (Press \"Upload Data\" button to go to the upload website)" + newLines); bugInfo.appendWithNewLine("(6) SCREEN CAPTURES - After this information is copied into your email client, " "image captures (File Menu->Capture Image) that help describe the bug may be " "copied into the email message." + newLines); bugInfo.appendWithNewLine("------------------------------------------------------------------"); bugInfo.appendWithNewLine("PLEASE DO NOT CHANGE TEXT BELOW THIS LINE.\n" "It is used to help us understand the context of your reported bug.\n"); bugInfo.appendWithNewLine(ApplicationInformation().getAllInformationInString("\n\n")); bugInfo.appendWithNewLine(openGLInformation); const AString hcpURL("http://humanconnectome.org/connectome/get-connectome-workbench.html"); const AString infoMessage = ("Verify that your bug occurs in the latest version of Workbench: " "" + hcpURL + "." + "
" + "You are using version " + ApplicationInformation().getVersion() + "." + "

" + "Please address each of the numbered items in the field below." + " Use the \"Copy to Email\" button to submit your bug report to " + emailAddress + "

" + "Data files that cause the bug may be uploaded (as a ZIP file) at " + "" + m_uploadWebSite + "." + " wb_command -zip-spec-file can be used to zip a data set." + ""); QLabel* versionLabel = new QLabel(infoMessage); versionLabel->setWordWrap(true); versionLabel->setOpenExternalLinks(true); m_textEdit = new QTextEdit(); m_textEdit->setMinimumSize(600, 400); m_textEdit->setText(bugInfo); QPushButton* copyToClipboardPushButton = new QPushButton("Copy to Clipboard"); QObject::connect(copyToClipboardPushButton, SIGNAL(clicked()), this, SLOT(copyToClipboard())); QPushButton* copyToEmailPushButton = new QPushButton("Copy to Email"); QObject::connect(copyToEmailPushButton, SIGNAL(clicked()), this, SLOT(copyToEmail())); QPushButton* uploadWebSitePushButton = new QPushButton("Upload Data"); QObject::connect(uploadWebSitePushButton, SIGNAL(clicked()), this, SLOT(openUploadWebsite())); copyToClipboardPushButton->setToolTip("Copies information to the computer's clipboard"); copyToEmailPushButton->setToolTip("Sends information to the user's email client"); uploadWebSitePushButton->setToolTip("Display upload website in user's web browser"); QHBoxLayout* buttonsLayout = new QHBoxLayout(); buttonsLayout->addStretch(); buttonsLayout->addWidget(uploadWebSitePushButton); buttonsLayout->addStretch(); buttonsLayout->addWidget(copyToClipboardPushButton); buttonsLayout->addStretch(); buttonsLayout->addWidget(copyToEmailPushButton); buttonsLayout->addStretch(); QWidget* contentWidget = new QWidget; QVBoxLayout* layout = new QVBoxLayout(contentWidget); layout->addWidget(versionLabel, 0); layout->addSpacing(8); layout->addWidget(m_textEdit, 1000); layout->addLayout(buttonsLayout, 0); setCentralWidget(contentWidget, WuQDialog::SCROLL_AREA_NEVER); setApplyButtonText(""); disableAutoDefaultForAllPushButtons(); } /** * Destructor. */ BugReportDialog::~BugReportDialog() { } /** * Copy the text to the user's clipboard. */ void BugReportDialog::copyToClipboard() { QApplication::clipboard()->setText(m_textEdit->toPlainText().trimmed(), QClipboard::Clipboard); } /** * Copy the text to the user's email client. */ void BugReportDialog::copyToEmail() { const AString mailURL = (m_emailAddressURL + "&body=" + m_textEdit->toPlainText().trimmed()); QUrl url(mailURL); QDesktopServices::openUrl(url); } /** * Update the dialog. */ void BugReportDialog::updateDialog() { /* nothing to do */ } /** * Open the Upload Website in the user's web browser. */ void BugReportDialog::openUploadWebsite() { QDesktopServices::openUrl(QUrl(m_uploadWebSite)); } connectome-workbench-1.2.3+git41-gc4c6c90/src/GuiQt/BugReportDialog.h000066400000000000000000000036431300200146000250050ustar00rootroot00000000000000#ifndef __BUG_REPORT_DIALOG_H__ #define __BUG_REPORT_DIALOG_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 "WuQDialogNonModal.h" class QTextEdit; namespace caret { class BugReportDialog : public WuQDialogNonModal { Q_OBJECT public: BugReportDialog(QWidget* parent, const AString& openGLInformation); virtual ~BugReportDialog(); virtual void updateDialog(); private: BugReportDialog(const BugReportDialog&); BugReportDialog& operator=(const BugReportDialog&); public: // ADD_NEW_METHODS_HERE private slots: void copyToClipboard(); void copyToEmail(); void openUploadWebsite(); private: QTextEdit* m_textEdit; QString m_emailAddressURL; QString m_uploadWebSite; // ADD_NEW_MEMBERS_HERE }; #ifdef __BUG_REPORT_DIALOG_DECLARE__ // #endif // __BUG_REPORT_DIALOG_DECLARE__ } // namespace #endif //__BUG_REPORT_DIALOG_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/GuiQt/CMakeLists.txt000066400000000000000000000416101300200146000243370ustar00rootroot00000000000000 # # Name of project # PROJECT (GuiQt) # # Use OpenGL from QT # SET(QT_USE_QTNETWORK TRUE) SET(QT_USE_QTOPENGL TRUE) #SET(QT_USE_QTWEBKIT TRUE) # # QT include files # 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. # 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 ) # # 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 AnnotationChangeCoordinateDialog.h AnnotationColorWidget.h AnnotationCoordinateInformation.h AnnotationCoordinateSelectionWidget.h AnnotationCoordinateSpaceWidget.h AnnotationCoordinateWidget.h AnnotationCreateDialog.h AnnotationDeleteWidget.h AnnotationFontWidget.h AnnotationFormatWidget.h AnnotationInsertNewWidget.h AnnotationLineArrowTipsWidget.h AnnotationMenuArrange.h AnnotationMenuFileSelection.h AnnotationPasteDialog.h AnnotationRedoUndoWidget.h AnnotationRotationWidget.h AnnotationSelectionViewController.h AnnotationTextAlignmentWidget.h AnnotationTextEditorDialog.h AnnotationTextEditorWidget.h AnnotationTextOrientationWidget.h AnnotationWidgetParentEnum.h AnnotationWidthHeightWidget.h BalsaDatabaseManager.h BalsaDatabaseUploadSceneFileDialog.h BorderEditingSelectionDialog.h BorderFileSplitDialog.h BorderOptimizeDialog.h BorderOptimizeExecutor.h BorderPropertiesEditorDialog.h BorderSelectionViewController.h BrainBrowserWindow.h BrainBrowserWindowComboBox.h BrainBrowserWindowEditMenuItemEnum.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 CursorDisplayScoped.h CursorEnum.h CursorManager.h CustomViewDialog.h DataFileContentCopyMoveDialog.h DisplayGroupAndTabItemTreeWidgetItem.h DisplayGroupAndTabItemViewController.h DisplayGroupEnumComboBox.h EnumComboBoxTemplate.h EventAnnotationCreateNewType.h EventAnnotationGetDrawnInWindow.h EventBrowserWindowContentGet.h EventBrowserWindowCreateTabs.h EventBrowserWindowGraphicsRedrawn.h EventBrowserWindowNew.h EventGetOrSetUserInputModeProcessor.h EventGraphicsUpdateAllWindows.h EventGraphicsUpdateOneWindow.h EventHelpViewerDisplay.h EventIdentificationRequest.h EventImageCapture.h EventMacDockMenuUpdate.h EventOperatingSystemRequestOpenDataFile.h EventOverlaySettingsEditorDialogRequest.h EventPaletteColorMappingEditorDialogRequest.h EventUpdateInformationWindows.h EventUpdateVolumeEditingToolBar.h EventUpdateYokedWindows.h EventUserInterfaceUpdate.h FiberOrientationSelectionViewController.h FiberSamplesOpenGLWidget.h FociProjectionDialog.h FociPropertiesEditorDialog.h FociSelectionViewController.h GapsAndMarginsDialog.h GiftiLabelTableEditor.h GiftiLabelTableSelectionComboBox.h GroupAndNameHierarchyTreeWidgetItem.h GroupAndNameHierarchyViewController.h GuiManager.h HelpViewerDialog.h HyperLinkTextBrowser.h IdentifyBrainordinateDialog.h ImageCaptureDialog.h ImageFileConvertToVolumeFileDialog.h ImageSelectionViewController.h InformationDisplayDialog.h InformationDisplayPropertiesDialog.h KeyEvent.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 MouseEvent.h MovieDialog.h OverlaySetViewController.h OverlaySettingsEditorDialog.h OverlayViewController.h PaletteColorMappingEditorDialog.h PlotMagnifier.h PlotPanner.h PreferencesDialog.h ProgressReportingDialog.h ProgressReportingFromEvent.h ProgressReportingWithSlots.h QGLWidgetTextRenderer.h RegionOfInterestCreateFromBorderDialog.h SceneCreateReplaceDialog.h SceneDialog.h ScenePreviewDialog.h SceneWindowGeometry.h SpecFileManagementDialog.h SplashScreen.h StructureEnumComboBox.h StructureSurfaceSelectionControl.h SurfacePropertiesEditorDialog.h SurfaceSelectionViewController.h TileTabsConfigurationDialog.h UserInputModeAbstract.h UserInputModeAnnotations.h UserInputModeAnnotationsContextMenu.h UserInputModeAnnotationsWidget.h UserInputModeBorders.h UserInputModeBordersWidget.h UserInputModeFociWidget.h UserInputModeFoci.h UserInputModeImageWidget.h UserInputModeImage.h UserInputModeView.h UserInputModeViewContextMenu.h UserInputModeVolumeEdit.h UserInputModeVolumeEditWidget.h UsernamePasswordWidget.h ViewModeEnum.h VolumeFileCreateDialog.h VolumeSurfaceOutlineColorOrTabViewController.h VolumeSurfaceOutlineSetViewController.h VolumeSurfaceOutlineViewController.h WuQCollapsibleWidget.h WuQDataEntryDialog.h WuQDialog.h WuQDialogModal.h WuQDialogNonModal.h WuQDoubleSlider.h WuQEventBlockingFilter.h WuQFactory.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 WuQtUtilities.h ZipSceneFileDialog.h AboutWorkbenchDialog.cxx AnnotationChangeCoordinateDialog.cxx AnnotationColorWidget.cxx AnnotationCoordinateInformation.cxx AnnotationCoordinateSelectionWidget.cxx AnnotationCoordinateSpaceWidget.cxx AnnotationCoordinateWidget.cxx AnnotationCreateDialog.cxx AnnotationDeleteWidget.cxx AnnotationFontWidget.cxx AnnotationFormatWidget.cxx AnnotationInsertNewWidget.cxx AnnotationLineArrowTipsWidget.cxx AnnotationMenuArrange.cxx AnnotationMenuFileSelection.cxx AnnotationPasteDialog.cxx AnnotationRedoUndoWidget.cxx AnnotationRotationWidget.cxx AnnotationSelectionViewController.cxx AnnotationTextAlignmentWidget.cxx AnnotationTextEditorDialog.cxx AnnotationTextEditorWidget.cxx AnnotationTextOrientationWidget.cxx AnnotationWidgetParentEnum.cxx AnnotationWidthHeightWidget.cxx BalsaDatabaseManager.cxx BalsaDatabaseUploadSceneFileDialog.cxx BorderEditingSelectionDialog.cxx BorderFileSplitDialog.cxx BorderOptimizeDialog.cxx BorderOptimizeExecutor.cxx BorderPropertiesEditorDialog.cxx BorderSelectionViewController.cxx BrainBrowserWindow.cxx BrainBrowserWindowComboBox.cxx BrainBrowserWindowEditMenuItemEnum.cxx BrainBrowserWindowOrientedToolBox.cxx BrainBrowserWindowToolBar.cxx BrainBrowserWindowToolBarChartAttributes.cxx BrainBrowserWindowToolBarChartAxes.cxx BrainBrowserWindowToolBarChartType.cxx BrainBrowserWindowToolBarClipping.cxx BrainBrowserWindowToolBarComponent.cxx BrainBrowserWindowToolBarSlicePlane.cxx BrainBrowserWindowToolBarSliceSelection.cxx BrainBrowserWindowToolBarSurfaceMontage.cxx BrainBrowserWindowToolBarTab.cxx BrainBrowserWindowToolBarVolumeMontage.cxx BrainOpenGLWidget.cxx BugReportDialog.cxx CaretColorEnumComboBox.cxx CaretColorEnumMenu.cxx CaretDataFileSelectionComboBox.cxx CaretFileDialog.cxx CaretFileDialogExtendable.cxx CaretFileRemoteDialog.cxx CaretMappableDataFileAndMapSelector.cxx CaretMappableDataFileAndMapSelectorObject.cxx ChartHistoryViewController.cxx ChartLinesSelectionViewController.cxx ChartMatrixParcelSelectionViewController.cxx ChartMatrixSeriesSelectionViewController.cxx ChartSelectionViewController.cxx ChartToolBoxViewController.cxx CiftiConnectivityMatrixViewController.cxx CiftiParcelSelectionComboBox.cxx ClippingPlanesDialog.cxx ColorEditorWidget.cxx CursorDisplayScoped.cxx CursorEnum.cxx CursorManager.cxx CustomViewDialog.cxx DataFileContentCopyMoveDialog.cxx DisplayGroupAndTabItemTreeWidgetItem.cxx DisplayGroupAndTabItemViewController.cxx DisplayGroupEnumComboBox.cxx EventAnnotationCreateNewType.cxx EventAnnotationGetDrawnInWindow.cxx EventBrowserWindowContentGet.cxx EventBrowserWindowCreateTabs.cxx EventBrowserWindowGraphicsRedrawn.cxx EventBrowserWindowNew.cxx EventGetOrSetUserInputModeProcessor.cxx EventGraphicsUpdateAllWindows.cxx EventGraphicsUpdateOneWindow.cxx EventHelpViewerDisplay.cxx EventIdentificationRequest.cxx EventImageCapture.cxx EventMacDockMenuUpdate.cxx EventOperatingSystemRequestOpenDataFile.cxx EventOverlaySettingsEditorDialogRequest.cxx EventPaletteColorMappingEditorDialogRequest.cxx EventUpdateInformationWindows.cxx EventUpdateVolumeEditingToolBar.cxx EventUpdateYokedWindows.cxx EventUserInterfaceUpdate.cxx FiberOrientationSelectionViewController.cxx FiberSamplesOpenGLWidget.cxx FociProjectionDialog.cxx FociPropertiesEditorDialog.cxx FociSelectionViewController.cxx GapsAndMarginsDialog.cxx GiftiLabelTableEditor.cxx GiftiLabelTableSelectionComboBox.cxx GroupAndNameHierarchyTreeWidgetItem.cxx GroupAndNameHierarchyViewController.cxx GuiManager.cxx HelpViewerDialog.cxx HyperLinkTextBrowser.cxx IdentifyBrainordinateDialog.cxx ImageCaptureDialog.cxx ImageFileConvertToVolumeFileDialog.cxx ImageSelectionViewController.cxx InformationDisplayDialog.cxx InformationDisplayPropertiesDialog.cxx KeyEvent.cxx LabelSelectionViewController.cxx MacApplication.cxx MacDockMenu.cxx MapSettingsColorBarWidget.cxx MapSettingsFiberTrajectoryWidget.cxx MapSettingsLabelsWidget.cxx MapSettingsLayerWidget.cxx MapSettingsPaletteColorMappingWidget.cxx MapSettingsParcelsWidget.cxx MapYokingGroupComboBox.cxx MetaDataEditorDialog.cxx MetaDataEditorWidget.cxx MouseEvent.cxx MovieDialog.cxx OverlaySetViewController.cxx OverlayViewController.cxx OverlaySettingsEditorDialog.cxx PaletteColorMappingEditorDialog.cxx PlotMagnifier.cxx PlotPanner.cxx PreferencesDialog.cxx ProgressReportingDialog.cxx ProgressReportingFromEvent.cxx ProgressReportingWithSlots.cxx QGLWidgetTextRenderer.cxx RegionOfInterestCreateFromBorderDialog.cxx SceneCreateReplaceDialog.cxx SceneDialog.cxx ScenePreviewDialog.cxx SceneWindowGeometry.cxx SpecFileManagementDialog.cxx SplashScreen.cxx StructureEnumComboBox.cxx StructureSurfaceSelectionControl.cxx SurfacePropertiesEditorDialog.cxx SurfaceSelectionViewController.cxx TileTabsConfigurationDialog.cxx UserInputModeAbstract.cxx UserInputModeAnnotations.cxx UserInputModeAnnotationsContextMenu.cxx UserInputModeAnnotationsWidget.cxx UserInputModeBorders.cxx UserInputModeBordersWidget.cxx UserInputModeFoci.cxx UserInputModeFociWidget.cxx UserInputModeImage.cxx UserInputModeImageWidget.cxx UserInputModeView.cxx UserInputModeViewContextMenu.cxx UserInputModeVolumeEdit.cxx UserInputModeVolumeEditWidget.cxx UsernamePasswordWidget.cxx ViewModeEnum.cxx VolumeFileCreateDialog.cxx VolumeSurfaceOutlineColorOrTabViewController.cxx VolumeSurfaceOutlineSetViewController.cxx VolumeSurfaceOutlineViewController.cxx WuQCollapsibleWidget.cxx WuQDataEntryDialog.cxx WuQDialog.cxx WuQDialogModal.cxx WuQDialogNonModal.cxx WuQDoubleSlider.cxx WuQEventBlockingFilter.cxx WuQFactory.cxx WuQGridLayoutGroup.cxx WuQGroupBoxExclusiveWidget.cxx WuQImageLabel.cxx WuQListWidget.cxx WuQMessageBox.cxx WuQwtPlot.cxx WuQSpecialIncrementDoubleSpinBox.cxx WuQSpinBoxGroup.cxx WuQSpinBoxOddValue.cxx WuQTabWidget.cxx WuQTimedMessageDisplay.cxx WuQTreeWidget.cxx WuQTrueFalseComboBox.cxx #WuQWebView.cxx WuQWidget.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} ) INCLUDE_DIRECTORIES( ${CMAKE_SOURCE_DIR}/GuiQt ${Qwt_INCLUDE_DIRS} ${CMAKE_SOURCE_DIR}/Algorithms ${CMAKE_SOURCE_DIR}/Annotations ${CMAKE_SOURCE_DIR}/Brain ${CMAKE_SOURCE_DIR}/Commands ${CMAKE_SOURCE_DIR}/Charting ${CMAKE_SOURCE_DIR}/FilesBase ${CMAKE_SOURCE_DIR}/Files ${CMAKE_SOURCE_DIR}/Cifti ${CMAKE_SOURCE_DIR}/Gifti ${CMAKE_SOURCE_DIR}/Nifti ${CMAKE_SOURCE_DIR}/OSMesaDummy ${CMAKE_SOURCE_DIR}/Operations ${CMAKE_SOURCE_DIR}/OperationsBase ${CMAKE_SOURCE_DIR}/Palette ${CMAKE_SOURCE_DIR}/Scenes ${CMAKE_SOURCE_DIR}/Xml ${CMAKE_SOURCE_DIR}/Common ) connectome-workbench-1.2.3+git41-gc4c6c90/src/GuiQt/CaretColorEnumComboBox.cxx000066400000000000000000000077601300200146000266460ustar00rootroot00000000000000 /*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 __CARET_COLOR_ENUM_COMBOBOX_DECLARE__ #include "CaretColorEnumComboBox.h" #undef __CARET_COLOR_ENUM_COMBOBOX_DECLARE__ #include using namespace caret; /** * \class caret::CaretColorEnumComboBox * \brief Control for selection of Caret Color enumerated types. * * Control for selection of Caret Color enumerated types. */ /** * Constructor. * @param parent * Parent object. */ CaretColorEnumComboBox::CaretColorEnumComboBox(QObject* parent) : WuQWidget(parent) { this->colorComboBox = new QComboBox(); std::vector colors; CaretColorEnum::getColorEnums(colors); 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); this->colorComboBox->addItem(name); 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); } setSelectedColor(CaretColorEnum::BLACK); QObject::connect(this->colorComboBox, SIGNAL(currentIndexChanged(int)), this, SLOT(colorComboBoxIndexChanged(int))); } /** * Destructor. */ CaretColorEnumComboBox::~CaretColorEnumComboBox() { } /** * @return The actual widget. */ QWidget* CaretColorEnumComboBox::getWidget() { return this->colorComboBox; } /** * @return The selected color. */ CaretColorEnum::Enum CaretColorEnumComboBox::getSelectedColor() { const int32_t indx = this->colorComboBox->currentIndex(); const int32_t integerCode = this->colorComboBox->itemData(indx).toInt(); CaretColorEnum::Enum color = CaretColorEnum::fromIntegerCode(integerCode, NULL); return color; } /** * Set the selected color. * @param color * New color for selection. */ void CaretColorEnumComboBox::setSelectedColor(const CaretColorEnum::Enum color) { const int32_t numColors = static_cast(this->colorComboBox->count()); for (int32_t i = 0; i < numColors; i++) { const int32_t integerCode = this->colorComboBox->itemData(i).toInt(); CaretColorEnum::Enum c = CaretColorEnum::fromIntegerCode(integerCode, NULL); if (c == color) { this->colorComboBox->blockSignals(true); this->colorComboBox->setCurrentIndex(i); this->colorComboBox->blockSignals(false); break; } } } /** * Called when a color is selected. * @param indx * Index of item selected. */ void CaretColorEnumComboBox::colorComboBoxIndexChanged(int indx) { const int32_t integerCode = this->colorComboBox->itemData(indx).toInt(); CaretColorEnum::Enum color = CaretColorEnum::fromIntegerCode(integerCode, NULL); emit colorSelected(color); } connectome-workbench-1.2.3+git41-gc4c6c90/src/GuiQt/CaretColorEnumComboBox.h000066400000000000000000000036431300200146000262670ustar00rootroot00000000000000#ifndef __CARET_COLOR_ENUM_COMBOBOX__H_ #define __CARET_COLOR_ENUM_COMBOBOX__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 "WuQWidget.h" class QComboBox; namespace caret { class CaretColorEnumComboBox : public WuQWidget { Q_OBJECT public: CaretColorEnumComboBox(QObject* parent); virtual ~CaretColorEnumComboBox(); CaretColorEnum::Enum getSelectedColor(); void setSelectedColor(const CaretColorEnum::Enum color); QWidget* getWidget(); signals: void colorSelected(const CaretColorEnum::Enum); private slots: void colorComboBoxIndexChanged(int); private: CaretColorEnumComboBox(const CaretColorEnumComboBox&); CaretColorEnumComboBox& operator=(const CaretColorEnumComboBox&); private: QComboBox* colorComboBox; }; #ifdef __CARET_COLOR_ENUM_COMBOBOX_DECLARE__ // #endif // __CARET_COLOR_ENUM_COMBOBOX_DECLARE__ } // namespace #endif //__CARET_COLOR_ENUM_COMBOBOX__H_ connectome-workbench-1.2.3+git41-gc4c6c90/src/GuiQt/CaretColorEnumMenu.cxx000066400000000000000000000145701300200146000260370ustar00rootroot00000000000000 /*LICENSE_START*/ /* * Copyright (C) 2015 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 __CARET_COLOR_ENUM_MENU_DECLARE__ #include "CaretColorEnumMenu.h" #undef __CARET_COLOR_ENUM_MENU_DECLARE__ #include "CaretAssert.h" #include "CaretLogger.h" #include "WuQtUtilities.h" using namespace caret; /** * \class caret::CaretColorEnumMenu * \brief Menu for selection of the standard caret colors * \ingroup GuiQt */ /** * Constructor. */ CaretColorEnumMenu::CaretColorEnumMenu() : QMenu() { initializeCaretColorEnumMenu(CaretColorEnum::OPTION_NO_OPTIONS); } /** * Constructor for menu that may provide optional caret color enums. * * @param caretColorOptions * Options for caret color enums. */ CaretColorEnumMenu::CaretColorEnumMenu(const int64_t caretColorOptions) : QMenu() { initializeCaretColorEnumMenu(caretColorOptions); } /** * Destructor. */ CaretColorEnumMenu::~CaretColorEnumMenu() { } /** * Initialize instance that may have optional caret color enums. * * @param caretColorOptions * Options for caret color enums. */ void CaretColorEnumMenu::initializeCaretColorEnumMenu(const int64_t caretColorOptions) { m_customColorAction = NULL; std::vector colors; 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 int colorIntegerCode = CaretColorEnum::toIntegerCode(colorEnum); const AString name = CaretColorEnum::toGuiName(colorEnum); /* * Create an icon with the color. */ float rgba[4]; CaretColorEnum::toRGBFloat(colorEnum, rgba); if (colorEnum == CaretColorEnum::NONE) { rgba[3] = 0.0; } else if (colorEnum == CaretColorEnum::CUSTOM) { /* * No pixmap for CUSTOM */ rgba[3] = 0.0; } else { rgba[3] = 1.0; } /* * Add color action to menu and make it checkable */ QAction* action = addAction(name); action->setData(colorIntegerCode); action->setIconText(name); QPixmap pm = WuQtUtilities::createCaretColorEnumPixmap(this, 10, 10, colorEnum, rgba, false); action->setIcon(QIcon(pm)); action->setCheckable(true); if (colorEnum == CaretColorEnum::CUSTOM) { m_customColorAction = action; } } QObject::connect(this, SIGNAL(triggered(QAction*)), this, SLOT(colorActionSelected(QAction*))); } /** * Set the selected color. * * @param color * New selected color. */ void CaretColorEnumMenu::setSelectedColor(const CaretColorEnum::Enum color) { blockSignals(true); const int colorIntegerCode = CaretColorEnum::toIntegerCode(color); /* * Set checkbox next to selected color. */ QList actionList = actions(); QListIterator actionIter(actionList); while (actionIter.hasNext()) { QAction* action = actionIter.next(); CaretAssert(action); const int actionIntegerCode = action->data().toInt(); if (actionIntegerCode == colorIntegerCode) { action->setChecked(true); } else { action->setChecked(false); } } blockSignals(false); } /** * Gets called when a color is selected. * * @param action * Action that is selected. */ void CaretColorEnumMenu::colorActionSelected(QAction* action) { const int integerCode = action->data().toInt(); bool valid = false; const CaretColorEnum::Enum color = CaretColorEnum::fromIntegerCode(integerCode, &valid); if (valid) { /* * Update checkboxes */ setSelectedColor(color); /* * Emit color changed signal. */ emit colorSelected(color); } else { CaretLogSevere("Invalid CaretColorEnum integer code=" + AString::number(integerCode)); } } /** * @return The selected color. */ CaretColorEnum::Enum CaretColorEnumMenu::getSelectedColor() { QList actionList = actions(); QListIterator actionIter(actionList); while (actionIter.hasNext()) { QAction* action = actionIter.next(); CaretAssert(action); if (action->isChecked()) { const int actionIntegerCode = action->data().toInt(); bool valid = false; CaretColorEnum::Enum colorSelected = CaretColorEnum::fromIntegerCode(actionIntegerCode, &valid); if (valid) { return colorSelected; } } } CaretAssertMessage(0, "Did not find selected color."); return CaretColorEnum::WHITE; } /** * Set the color for the custom color's icon. * * @param rgb * Red/Green/Blue/Alpha color components [0.0, 1.0] */ void CaretColorEnumMenu::setCustomIconColor(const float rgba[4]) { if (m_customColorAction != NULL) { QPixmap pm = WuQtUtilities::createCaretColorEnumPixmap(this, 10, 10, CaretColorEnum::CUSTOM, rgba, false); m_customColorAction->setIcon(QIcon(pm)); } } connectome-workbench-1.2.3+git41-gc4c6c90/src/GuiQt/CaretColorEnumMenu.h000066400000000000000000000040741300200146000254620ustar00rootroot00000000000000#ifndef __CARET_COLOR_ENUM_MENU_H__ #define __CARET_COLOR_ENUM_MENU_H__ /*LICENSE_START*/ /* * Copyright (C) 2015 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 "CaretColorEnum.h" namespace caret { class CaretColorEnumMenu : public QMenu { Q_OBJECT public: CaretColorEnumMenu(); CaretColorEnumMenu(const int64_t caretColorOptions); virtual ~CaretColorEnumMenu(); CaretColorEnum::Enum getSelectedColor(); void setSelectedColor(const CaretColorEnum::Enum color); void setCustomIconColor(const float rgba[4]); // ADD_NEW_METHODS_HERE signals: void colorSelected(const CaretColorEnum::Enum); private slots: void colorActionSelected(QAction* action); private: CaretColorEnumMenu(const CaretColorEnumMenu&); CaretColorEnumMenu& operator=(const CaretColorEnumMenu&); void initializeCaretColorEnumMenu(const int64_t caretColorOptions); QAction* m_customColorAction; // ADD_NEW_MEMBERS_HERE }; #ifdef __CARET_COLOR_ENUM_MENU_DECLARE__ // #endif // __CARET_COLOR_ENUM_MENU_DECLARE__ } // namespace #endif //__CARET_COLOR_ENUM_MENU_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/GuiQt/CaretDataFileSelectionComboBox.cxx000066400000000000000000000107171300200146000302560ustar00rootroot00000000000000 /*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 __CARET_DATA_FILE_SELECTION_COMBO_BOX_DECLARE__ #include "CaretDataFileSelectionComboBox.h" #undef __CARET_DATA_FILE_SELECTION_COMBO_BOX_DECLARE__ #include "CaretAssert.h" #include "CaretDataFile.h" #include "CaretDataFileSelectionModel.h" #include "FilePathNamePrefixCompactor.h" #include "WuQEventBlockingFilter.h" using namespace caret; /** * \class caret::CaretDataFileSelectionComboBox * \brief Combo box for selection of a CaretDataFile. * \ingroup GuiQt */ /** * Constructor. * * @param parent * Parent of combo box. */ CaretDataFileSelectionComboBox::CaretDataFileSelectionComboBox(QObject* parent) : WuQWidget(parent) { m_selectionModel = NULL; m_comboBox = new QComboBox(); m_comboBox->setSizeAdjustPolicy(QComboBox::AdjustToContents); QObject::connect(m_comboBox, SIGNAL(activated(int)), this, SLOT(slotFileIndexSelected(int))); WuQEventBlockingFilter::blockMouseWheelEventInMacComboBox(m_comboBox); } /** * Destructor. */ CaretDataFileSelectionComboBox::~CaretDataFileSelectionComboBox() { } /** * @return The widget (combobox) for insertion into a layout. */ QWidget* CaretDataFileSelectionComboBox::getWidget() { return m_comboBox; } /** * Gets called when the user makes a selection. Issues the * 'fileSelected()' signal. * * @param indx * Index of the item that was selected. */ void CaretDataFileSelectionComboBox::slotFileIndexSelected(int indx) { CaretDataFile* caretDataFile = NULL; if ((indx >= 0) && (indx < m_comboBox->count())) { void* filePointer = m_comboBox->itemData(indx).value(); caretDataFile = (CaretDataFile*)filePointer; } if (m_selectionModel != NULL) { m_selectionModel->setSelectedFile(caretDataFile); } emit fileSelected(caretDataFile); } /** * Update the content of the combo box. * * @param * Selection model for the combo box. */ void CaretDataFileSelectionComboBox::updateComboBox(CaretDataFileSelectionModel* selectionModel) { m_comboBox->blockSignals(true); m_comboBox->clear(); m_selectionModel = selectionModel; if (m_selectionModel != NULL) { const CaretDataFile* selectedFile = m_selectionModel->getSelectedFile(); int defaultIndex = 0; std::vector caretDataFiles = m_selectionModel->getAvailableFiles(); std::vector displayNames; FilePathNamePrefixCompactor::removeMatchingPathPrefixFromCaretDataFiles(caretDataFiles, displayNames); CaretAssert(caretDataFiles.size() == displayNames.size()); const int32_t numFiles = static_cast(caretDataFiles.size()); for (int32_t iFile = 0; iFile < numFiles; iFile++) { CaretAssertVectorIndex(caretDataFiles, iFile); CaretDataFile* cdf = caretDataFiles[iFile]; CaretAssert(cdf); CaretAssertVectorIndex(displayNames, iFile); m_comboBox->addItem(displayNames[iFile], qVariantFromValue((void*)cdf)); if (cdf == selectedFile) { defaultIndex = m_comboBox->count() - 1; } } if ((defaultIndex >= 0) && (defaultIndex < m_comboBox->count())) { m_comboBox->setCurrentIndex(defaultIndex); } } m_comboBox->blockSignals(false); } /** * @return Selection model in this combo box. */ CaretDataFileSelectionModel* CaretDataFileSelectionComboBox::getSelectionModel() { return m_selectionModel; } connectome-workbench-1.2.3+git41-gc4c6c90/src/GuiQt/CaretDataFileSelectionComboBox.h000066400000000000000000000043741300200146000277050ustar00rootroot00000000000000#ifndef __CARET_DATA_FILE_SELECTION_COMBO_BOX_H__ #define __CARET_DATA_FILE_SELECTION_COMBO_BOX_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 "DataFileTypeEnum.h" #include "WuQWidget.h" class QComboBox; namespace caret { class CaretDataFile; class CaretDataFileSelectionModel; class CaretDataFileSelectionComboBox : public WuQWidget { Q_OBJECT public: CaretDataFileSelectionComboBox(QObject* parent); virtual ~CaretDataFileSelectionComboBox(); void updateComboBox(CaretDataFileSelectionModel* selectionModel); CaretDataFileSelectionModel* getSelectionModel(); virtual QWidget* getWidget(); // ADD_NEW_METHODS_HERE signals: void fileSelected(CaretDataFile* caretDataFile); private slots: void slotFileIndexSelected(int indx); private: CaretDataFileSelectionComboBox(const CaretDataFileSelectionComboBox&); CaretDataFileSelectionComboBox& operator=(const CaretDataFileSelectionComboBox&); QComboBox* m_comboBox; CaretDataFileSelectionModel* m_selectionModel; // ADD_NEW_MEMBERS_HERE }; #ifdef __CARET_DATA_FILE_SELECTION_COMBO_BOX_DECLARE__ // #endif // __CARET_DATA_FILE_SELECTION_COMBO_BOX_DECLARE__ } // namespace #endif //__CARET_DATA_FILE_SELECTION_COMBO_BOX_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/GuiQt/CaretFileDialog.cxx000066400000000000000000000443711300200146000253100ustar00rootroot00000000000000/*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 #define __CARET_FILE_DIALOG_DECLARE__ #include "CaretFileDialog.h" #undef __CARET_FILE_DIALOG_DECLARE__ #include "Brain.h" #include "GuiManager.h" using namespace caret; FilterFilesProxyModel::FilterFilesProxyModel() { m_dataFileType = DataFileTypeEnum::UNKNOWN; } FilterFilesProxyModel::~FilterFilesProxyModel() { } /** * On Macs, Qt shows files that do not match the filter as disabled. This * method looks for disabled files and prevents them from being displayed. * * @return True to display file, else false. */ bool FilterFilesProxyModel::filterAcceptsRow ( int sourceRow, const QModelIndex & sourceParent ) const { /* * See if the 'super' allows file to be displayed. */ bool showIt = QSortFilterProxyModel::filterAcceptsRow(sourceRow, sourceParent); if (showIt) { /* * See if item is disabled, and if so, do not show it. */ QModelIndex modelIndex = sourceModel()->index(sourceRow, 0, sourceParent); QFileSystemModel* fileModel = qobject_cast(sourceModel()); Qt::ItemFlags flags = fileModel->flags(modelIndex); if((flags & Qt::ItemIsEnabled) == 0) { showIt = false; } /* * All CIFTI files use a 'CIFTI prefix' followed by the NIFTI volume * extension. As a result, when the File Dialog's filter is set * to volume files, both volume and CIFTI files are displayed. * So, when the filter is volume files, inhibit the display CIFTI * files in the file selection dialog. */ if (showIt) { if (m_dataFileType == DataFileTypeEnum::VOLUME) { const AString name = fileModel->fileName(modelIndex); bool isValid = false; const DataFileTypeEnum::Enum fileType = DataFileTypeEnum::fromFileExtension(name, &isValid); if (isValid) { if (fileType != DataFileTypeEnum::VOLUME) { showIt = false; } } } } } return showIt; } void FilterFilesProxyModel::setDataFileTypeForFiltering(const DataFileTypeEnum::Enum dataFileType) { m_dataFileType = dataFileType; } /** * \class caret::CaretFileDialog * \brief Adds additional functionality over Qt's QFileDialog. */ /** * Constructor. */ CaretFileDialog::CaretFileDialog(QWidget* parent, Qt::WindowFlags f) : QFileDialog(parent, f) { this->initializeCaretFileDialog(); this->setDirectory(GuiManager::get()->getBrain()->getCurrentDirectory()); } /** * Constructor. */ CaretFileDialog::CaretFileDialog(QWidget* parent, const QString& caption, const QString& directory, const QString& filter) : QFileDialog(parent, caption, directory, filter) { this->initializeCaretFileDialog(); if (directory.isEmpty()) { this->setDirectory(GuiManager::get()->getBrain()->getCurrentDirectory()); } } /** * Destructor. */ CaretFileDialog::~CaretFileDialog() { } /** * Initialize the file dialog. */ 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. */ m_filterFilesProxyModel = new FilterFilesProxyModel(); this->setProxyModel(m_filterFilesProxyModel); #ifdef Q_OS_MACX /* * On Macs, add /Volumes to the sidebar URLs * so that mounted disks can be accessed. */ QList urls = this->sidebarUrls(); urls.append(QUrl::fromLocalFile("/Volumes")); this->setSidebarUrls(urls); #endif // Q_OS_MACX QObject::connect(this, SIGNAL(filterSelected(const QString&)), this, SLOT(fileFilterWasChanged(const QString&))); } /** * Overrides parent's setVisible to ensure file filter is properly set * * @param visible * New visibility status. */ void CaretFileDialog::setVisible(bool visible) { if (visible) { fileFilterWasChanged(selectedFilter()); } QFileDialog::setVisible(visible); } /** * Gets called when the file filter is changed. * * @param filter * Newly selected file filter. */ void CaretFileDialog::fileFilterWasChanged(const QString& filter) { bool isValid = false; DataFileTypeEnum::Enum dataFileType = DataFileTypeEnum::fromQFileDialogFilter(filter, &isValid); if ( ! isValid) { dataFileType = DataFileTypeEnum::UNKNOWN; } m_filterFilesProxyModel->setDataFileTypeForFiltering(dataFileType); } /** * Like QFileDialog::getOpenFileName() except that this * NEVER uses the native file dialog thus providing * a consistent user-interface across platforms. * * @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 filter * Filter for file file selection. * @param selectedFilter * Optional selected filter. * @param options * Options (see QFileDialog). * @return * Name of file selected or empty string if user cancelled. */ QString CaretFileDialog::getOpenFileNameDialog(QWidget *parent, const QString &caption, const QString &dir, const QString &filter, QString *selectedFilter, Options options ) { CaretFileDialog cfd(parent, caption, dir, filter); if (selectedFilter != 0) { cfd.selectFilter(*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::getSaveFileName() except that this * NEVER uses the native file dialog thus providing * a consistent user-interface across platforms. * * @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 filter * Filter for file file selection. * @param selectedFilter * Optional selected filter. * @param options * Options (see QFileDialog). * @return * Name of file selected or empty string if user cancelled. */ QString CaretFileDialog::getSaveFileNameDialog(QWidget *parent, const QString &caption, const QString &dir, const QString &filter, QString *selectedFilter, Options options) { CaretFileDialog cfd(parent, caption, dir, filter); if (selectedFilter != 0) { cfd.selectFilter(*selectedFilter); } cfd.setOptions(options); cfd.setAcceptMode(QFileDialog::AcceptSave); cfd.setFileMode(CaretFileDialog::AnyFile); if (cfd.exec() == CaretFileDialog::Accepted) { QStringList selectedFiles = cfd.selectedFiles(); if (selectedFiles.size() > 0) { return selectedFiles[0]; } } return QString(); } /** * Like QFileDialog::getSaveFileName() except that this * NEVER uses the native file dialog thus providing * a consistent user-interface across platforms. * * The file filter will be from the given data file type. * The returned file will contain a valid extension from the * given data file type. * * @param dataFileType * Type of file being saved. * @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. If there is * no file extension or it is invalid, a valid extension will be added. */ QString CaretFileDialog::getSaveFileNameDialog(const DataFileTypeEnum::Enum dataFileType, QWidget *parent, const QString &caption, const QString &dir, Options options) { CaretFileDialog cfd(parent, caption, dir, DataFileTypeEnum::toQFileDialogFilter(dataFileType)); cfd.selectFilter(DataFileTypeEnum::toQFileDialogFilter(dataFileType)); cfd.setOptions(options); cfd.setAcceptMode(QFileDialog::AcceptSave); cfd.setFileMode(CaretFileDialog::AnyFile); if (cfd.exec() == CaretFileDialog::Accepted) { QStringList selectedFiles = cfd.selectedFiles(); if (selectedFiles.size() > 0) { AString filename = DataFileTypeEnum::addFileExtensionIfMissing(selectedFiles[0], dataFileType); return filename; } } return QString(); } /** * Like QFileDialog::getSaveFileName() except that this * NEVER uses the native file dialog thus providing * a consistent user-interface across platforms. It also will * display "Choose" for the selection button. * * The file filter will be from the given data file type. * The returned file will contain a valid extension from the * given data file type. * * @param dataFileType * Type of file being saved. * @param directoryOrFileName * Name of directory or name of 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. If there is * no file extension or it is invalid, a valid extension will be added. */ QString CaretFileDialog::getChooseFileNameDialog(const DataFileTypeEnum::Enum dataFileType, const QString& directoryOrFileName, QWidget *parent) { CaretFileDialog fd(parent); fd.setFilter(DataFileTypeEnum::toQFileDialogFilter(dataFileType)); fd.selectFilter(DataFileTypeEnum::toQFileDialogFilter(dataFileType)); fd.setAcceptMode(CaretFileDialog::AcceptSave); fd.setFileMode(CaretFileDialog::AnyFile); fd.setViewMode(CaretFileDialog::List); if (directoryOrFileName.isEmpty() == false) { QFileInfo fileInfo(directoryOrFileName); if (fileInfo.isDir()) { fd.setDirectory(directoryOrFileName); } else { fd.selectFile(directoryOrFileName); } } else { fd.setDirectory(GuiManager::get()->getBrain()->getCurrentDirectory()); } fd.setLabelText(CaretFileDialog::Accept, "Choose"); fd.setWindowTitle("Choose File Name"); if (fd.exec() == CaretFileDialog::Accepted) { QStringList selectedFiles = fd.selectedFiles(); if (selectedFiles.size() > 0) { AString filename = DataFileTypeEnum::addFileExtensionIfMissing(selectedFiles[0], dataFileType); return filename; } } return QString(); } /** * Like QFileDialog::getExistingDirectory() except that this * NEVER uses the native file dialog thus providing * a consistent user-interface across platforms. * * @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 filter * Filter for file file selection. * @param selectedFilter * Optional selected filter. * @param options * Options (see QFileDialog). * @return * Name of directory selected or empty string if user cancelled. */ QString CaretFileDialog::getExistingDirectoryDialog(QWidget *parent, const QString &caption, const QString &dir, Options options) { CaretFileDialog cfd(parent, caption, dir, ""); cfd.setOptions(options); cfd.setAcceptMode(CaretFileDialog::AcceptOpen); cfd.setFileMode(CaretFileDialog::Directory); cfd.setOption(CaretFileDialog::ShowDirsOnly, true); if (cfd.exec() == CaretFileDialog::Accepted) { QStringList selectedFiles = cfd.selectedFiles(); if (selectedFiles.size() > 0) { return selectedFiles[0]; } } return QString(); } /** * Like QFileDialog::getOpenFileNames() except that this * NEVER uses the native file dialog thus providing * a consistent user-interface across platforms. * * @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 filter * Filter for file file selection. * @param selectedFilter * Optional selected filter. * @param options * Options (see QFileDialog). * @return * StringList of file(s) selected or empty list if user cancelled. */ QStringList CaretFileDialog::getOpenFileNamesDialog(QWidget *parent, const QString &caption, const QString &dir, const QString &filter, QString *selectedFilter, Options options) { CaretFileDialog cfd(parent, caption, dir, filter); if (selectedFilter != 0) { cfd.selectFilter(*selectedFilter); } cfd.setOptions(options); cfd.setAcceptMode(CaretFileDialog::AcceptOpen); cfd.setFileMode(CaretFileDialog::ExistingFiles); if (cfd.exec() == CaretFileDialog::Accepted) { QStringList selectedFiles = cfd.selectedFiles(); if (selectedFiles.size() > 0) { return selectedFiles; } } QStringList sl; return sl; } /** * Save the dialog settings (directory, filename filter, and geometry) * for next time dialog displayed. * * @param settingsName * Name for settings. */ void CaretFileDialog::saveDialogSettings(const AString& settingsName) { PreviousDialogSettings previousSettings(selectedNameFilter(), directory().absolutePath(), saveGeometry()); std::map::iterator iter = s_previousDialogSettingsMap.find(settingsName); if (iter != s_previousDialogSettingsMap.end()) { iter->second = previousSettings; } else { s_previousDialogSettingsMap.insert(std::make_pair(settingsName, previousSettings)); } } /** * Restore the dialog settings (directory, filename filter, and geometry) * for next time dialog displayed. * * @param settingsName * Name for settings used when settings were saved. */ void CaretFileDialog::restoreDialogSettings(const AString& settingsName) { std::map::iterator iter = s_previousDialogSettingsMap.find(settingsName); if (iter != s_previousDialogSettingsMap.end()) { std::cout << "Found and restoring settings for " << qPrintable(settingsName) << std::endl; const PreviousDialogSettings previousSettings = iter->second; FileInformation fileInfo(previousSettings.m_directoryName); if (fileInfo.exists()) { setDirectory(previousSettings.m_directoryName); } QStringList dialogFilters = filters(); QStringListIterator filterIter(dialogFilters); while (filterIter.hasNext()) { const AString filterName = filterIter.next(); if (filterName == previousSettings.m_fileFilterName) { selectFilter(previousSettings.m_fileFilterName); break; } } restoreGeometry(previousSettings.m_dialogGeometry); } } connectome-workbench-1.2.3+git41-gc4c6c90/src/GuiQt/CaretFileDialog.h000066400000000000000000000137671300200146000247420ustar00rootroot00000000000000#ifndef __CARET_FILE_DIALOG__H_ #define __CARET_FILE_DIALOG__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 "DataFileTypeEnum.h" namespace caret { class FilterFilesProxyModel; class CaretFileDialog : public QFileDialog { Q_OBJECT public: CaretFileDialog(QWidget* parent, Qt::WindowFlags f); CaretFileDialog(QWidget* parent = 0, const QString& caption = QString(), const QString& directory = QString(), const QString& filter = QString()); virtual ~CaretFileDialog(); // modal method to get open file name static QString getOpenFileNameDialog(QWidget *parent = 0, const QString &caption = QString(), const QString &dir = QString(), const QString &filter = QString(), QString *selectedFilter = 0, Options options = 0); // modal method to get save file name static QString getSaveFileNameDialog(QWidget *parent = 0, const QString &caption = QString(), const QString &dir = QString(), const QString &filter = QString(), QString *selectedFilter = 0, Options options = 0); // modal method to get save file name static QString getSaveFileNameDialog(const DataFileTypeEnum::Enum dataFileType, QWidget *parent = 0, const QString &caption = QString(), const QString &dir = QString(), Options options = 0); // modal method to get choose file name static QString getChooseFileNameDialog(const DataFileTypeEnum::Enum dataFileType, const QString& directoryOrFileName, QWidget *parent = 0); // modal method to get directory name static QString getExistingDirectoryDialog(QWidget *parent = 0, const QString &caption = QString(), const QString &dir = QString(), Options options = ShowDirsOnly); // modal method to get open file names static QStringList getOpenFileNamesDialog(QWidget *parent = 0, const QString &caption = QString(), const QString &dir = QString(), const QString &filter = QString(), QString *selectedFilter = 0, Options options = 0); void saveDialogSettings(const AString& settingsName); void restoreDialogSettings(const AString& settingsName); public slots: virtual void setVisible(bool visible); private slots: void fileFilterWasChanged(const QString& filter); private: CaretFileDialog(const CaretFileDialog&); CaretFileDialog& operator=(const CaretFileDialog&); void initializeCaretFileDialog(); FilterFilesProxyModel* m_filterFilesProxyModel; class PreviousDialogSettings { public: PreviousDialogSettings(const AString& fileFilterName, const AString& directoryName, const QByteArray& dialogGeometry) : m_fileFilterName(fileFilterName), m_directoryName(directoryName), m_dialogGeometry(dialogGeometry) { } AString m_fileFilterName; AString m_directoryName; QByteArray m_dialogGeometry; }; static std::map s_previousDialogSettingsMap; }; /** * May be fully implemented to provide additional filtering of files. */ class FilterFilesProxyModel : public QSortFilterProxyModel { public: FilterFilesProxyModel(); virtual ~FilterFilesProxyModel(); void setDataFileTypeForFiltering(const DataFileTypeEnum::Enum dataFileType); protected: bool filterAcceptsRow ( int sourceRow, const QModelIndex & sourceParent ) const; private: DataFileTypeEnum::Enum m_dataFileType; }; #ifdef __CARET_FILE_DIALOG_DECLARE__ std::map CaretFileDialog::s_previousDialogSettingsMap; #endif // __CARET_FILE_DIALOG_DECLARE__ } // namespace #endif //__CARET_FILE_DIALOG__H_ connectome-workbench-1.2.3+git41-gc4c6c90/src/GuiQt/CaretFileDialogExtendable.cxx000066400000000000000000000115751300200146000273040ustar00rootroot00000000000000 /*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 #define __CARET_FILE_DIALOG_EXTENDABLE_DECLARE__ #include "CaretFileDialogExtendable.h" #undef __CARET_FILE_DIALOG_EXTENDABLE_DECLARE__ #include "CaretAssert.h" #include "CaretFileDialog.h" using namespace caret; /** * \class caret::CaretFileDialogExtendable * \brief A File Dialog that can have a widget added to it. * * Embeds a QFileDialog inside a dialog so that a widget * can be inserted below the FileDialog. All public methods * from QFileDialog are duplicated and go straight to * the embedded QFileDialog. */ /** * Constructor. */ CaretFileDialogExtendable::CaretFileDialogExtendable(QWidget* parent, Qt::WindowFlags f) : QDialog(parent, f) { Qt::WindowFlags flags = 0; m_caretFileDialog = new CaretFileDialogPrivate(NULL, flags); createDialog(); } /** * Constructor. */ CaretFileDialogExtendable::CaretFileDialogExtendable(QWidget* parent, const QString& caption, const QString& directory, const QString& filter) : QDialog(parent) { m_caretFileDialog = new CaretFileDialogPrivate(NULL, caption, directory, filter); createDialog(); } /** * Destructor. */ CaretFileDialogExtendable::~CaretFileDialogExtendable() { } /** * Create the dialog. */ void CaretFileDialogExtendable::createDialog() { QObject::connect(m_caretFileDialog, SIGNAL(currentChanged(const QString&)), this, SIGNAL(currentChanged(const QString&))); QObject::connect(m_caretFileDialog, SIGNAL( directoryEntered(const QString&)), this, SIGNAL( directoryEntered(const QString&))); QObject::connect(m_caretFileDialog, SIGNAL(fileSelected(const QString&)), this, SIGNAL(fileSelected(const QString&))); QObject::connect(m_caretFileDialog, SIGNAL(filesSelected(const QStringList&)), this, SIGNAL(filesSelected(const QStringList&))); QObject::connect(m_caretFileDialog, SIGNAL(filterSelected(const QString&)), this, SIGNAL(filterSelected(const QString&))); QObject::connect(m_caretFileDialog, SIGNAL(finished(int)), this, SLOT(fileDialogFinished(int))); m_caretFileDialog->setAttribute(Qt::WA_DeleteOnClose, false); m_caretFileDialog->setSizeGripEnabled(false); m_dialogLayout = new QVBoxLayout(this); m_dialogLayout->addWidget(m_caretFileDialog); } /** * Gets called when child file dialog issues its finished signal. * @param result * Result of dialog. */ void CaretFileDialogExtendable::fileDialogFinished(int result) { this->setResult(result); this->done(result); } /** * Add a widget at the bottom to this file dialog. * @param widget * Widget that is added. */ void CaretFileDialogExtendable::addWidget(QWidget* widget) { CaretAssert(widget); m_dialogLayout->addWidget(widget); } //================================================================================== CaretFileDialogPrivate::CaretFileDialogPrivate(QWidget* parent, Qt::WindowFlags /*f*/) : CaretFileDialog(parent, Qt::Widget) { } CaretFileDialogPrivate::CaretFileDialogPrivate(QWidget* parent, const QString& caption, const QString& directory, const QString& filter) : CaretFileDialog(parent, caption, directory, filter) { } CaretFileDialogPrivate::~CaretFileDialogPrivate() { } void CaretFileDialogPrivate::closeEvent(QCloseEvent* event) { event->ignore(); } void CaretFileDialogPrivate::done(int result) { CaretFileDialog::done(result); } connectome-workbench-1.2.3+git41-gc4c6c90/src/GuiQt/CaretFileDialogExtendable.h000066400000000000000000000174751300200146000267360ustar00rootroot00000000000000#ifndef __CARET_FILE_DIALOG_EXTENDABLE__H_ #define __CARET_FILE_DIALOG_EXTENDABLE__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 "CaretFileDialog.h" class QVBoxLayout; namespace caret { class CaretFileDialogPrivate : public CaretFileDialog { Q_OBJECT public: CaretFileDialogPrivate(QWidget* parent, Qt::WindowFlags f); CaretFileDialogPrivate(QWidget* parent = 0, const QString& caption = QString(), const QString& directory = QString(), const QString& filter = QString()); virtual ~CaretFileDialogPrivate(); protected: virtual void done(int result); virtual void closeEvent(QCloseEvent* event); }; class CaretFileDialogExtendable : public QDialog { Q_OBJECT public: CaretFileDialogExtendable(QWidget* parent, Qt::WindowFlags f); CaretFileDialogExtendable(QWidget* parent = 0, const QString& caption = QString(), const QString& directory = QString(), const QString& filter = QString()); virtual ~CaretFileDialogExtendable(); void addWidget(QWidget* widget); QFileDialog::AcceptMode acceptMode() const { return m_caretFileDialog->acceptMode(); } bool confirmOverwrite() const { return m_caretFileDialog->confirmOverwrite(); } QString defaultSuffix() const { return m_caretFileDialog->defaultSuffix(); } QDir directory() const { return m_caretFileDialog->directory(); } QFileDialog::FileMode fileMode() const { return m_caretFileDialog->fileMode(); } QStringList filters() const { return m_caretFileDialog->filters(); } QStringList history() const { return m_caretFileDialog->history(); } QFileIconProvider* iconProvider() const { return m_caretFileDialog->iconProvider(); } bool isReadOnly() const { return m_caretFileDialog->isReadOnly(); } QAbstractItemDelegate* itemDelegate() const { return m_caretFileDialog->itemDelegate(); } QString labelText(const QFileDialog::DialogLabel label) const { return m_caretFileDialog->labelText(label); } QStringList nameFilters() const { return m_caretFileDialog->nameFilters(); } void open(QObject* receiver, const char* member) { m_caretFileDialog->open(receiver, member); } QFileDialog::Options options() const { return m_caretFileDialog->options(); } QAbstractProxyModel* proxyModel() const { return m_caretFileDialog->proxyModel(); } bool resolveSymlinks() const { return m_caretFileDialog->resolveSymlinks(); } bool restoreState(const QByteArray& state) { return m_caretFileDialog->restoreState(state); } QByteArray saveState() const { return m_caretFileDialog->saveState(); } void selectFile(const QString& name) { m_caretFileDialog->selectFile(name); } void selectNameFilter(const QString& filter) { m_caretFileDialog->selectNameFilter(filter); } QStringList selectedFiles() const { return m_caretFileDialog->selectedFiles(); } QString selectedNameFilter() const { return m_caretFileDialog->selectedNameFilter(); } void setAcceptMode(const QFileDialog::AcceptMode mode) { m_caretFileDialog->setAcceptMode(mode); } void setConfirmOverwrite(const bool enabled) { m_caretFileDialog->setConfirmOverwrite(enabled); } void setDefaultSuffix(const QString& suffix) { m_caretFileDialog->setDefaultSuffix(suffix); } void setDirectory(const QString& dir) { m_caretFileDialog->setDirectory(dir); } void setDirectory(const QDir& dir) { m_caretFileDialog->setDirectory(dir); } void setFileMode(const QFileDialog::FileMode mode) { m_caretFileDialog->setFileMode(mode); } void setFilter(const QString& filter) { m_caretFileDialog->setFilter(filter); } void setHistory(const QStringList& paths) { m_caretFileDialog->setHistory(paths); } void setIconProvider(QFileIconProvider* provider) { m_caretFileDialog->setIconProvider(provider); } void setItemDelegate(QAbstractItemDelegate* delegate) { m_caretFileDialog->setItemDelegate(delegate); } void setLabelText(const QFileDialog::DialogLabel label, const QString& text) { m_caretFileDialog->setLabelText(label, text); } void setNameFilter(const QString& filter) { m_caretFileDialog->setNameFilter(filter); } void setNameFilterDetailsVisible(bool enabled) { m_caretFileDialog->setNameFilterDetailsVisible(enabled); } void setNameFilters(const QStringList& filters) { m_caretFileDialog->setNameFilters(filters); } void setOption(QFileDialog::Option option, bool on = true) { m_caretFileDialog->setOption(option, on); } void setOptions(QFileDialog::Options options) { m_caretFileDialog->setOptions(options); } void setProxyModel(QAbstractProxyModel* proxyModel) { m_caretFileDialog->setProxyModel(proxyModel); } void setReadOnly(const bool enabled) { m_caretFileDialog->setReadOnly(enabled); } void setResolveSymlinks(bool enabled) { m_caretFileDialog->setResolveSymlinks(enabled); } void setSidebarUrls(const QList& urls) { m_caretFileDialog->setSidebarUrls(urls); } void setViewMode(const QFileDialog::ViewMode viewMode) { m_caretFileDialog->setViewMode(viewMode); } QList sidebarUrls() const { return m_caretFileDialog->sidebarUrls(); } bool testOption(QFileDialog::Option option) const { return m_caretFileDialog->testOption(option); } QFileDialog::ViewMode viewMode() const { return m_caretFileDialog->viewMode(); } signals: void currentChanged(const QString& path); void directoryEntered(const QString& directory); void fileSelected(const QString& file); void filesSelected(const QStringList& selected); void filterSelected(const QString& filter); private slots: void fileDialogFinished(int); private: CaretFileDialogExtendable(const CaretFileDialogExtendable&); CaretFileDialogExtendable& operator=(const CaretFileDialogExtendable&); void createDialog(); QVBoxLayout* m_dialogLayout; CaretFileDialogPrivate* m_caretFileDialog; }; #ifdef __CARET_FILE_DIALOG_EXTENDABLE_DECLARE__ // #endif // __CARET_FILE_DIALOG_EXTENDABLE_DECLARE__ } // namespace #endif //__CARET_FILE_DIALOG_EXTENDABLE__H_ connectome-workbench-1.2.3+git41-gc4c6c90/src/GuiQt/CaretFileRemoteDialog.cxx000066400000000000000000000270131300200146000264560ustar00rootroot00000000000000 /*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 #include #include #define __CARET_FILE_REMOTE_DIALOG_DECLARE__ #include "CaretFileRemoteDialog.h" #undef __CARET_FILE_REMOTE_DIALOG_DECLARE__ #include "Brain.h" #include "BrainBrowserWindow.h" #include "CaretAssert.h" #include "CaretPreferences.h" #include "DataFileTypeEnum.h" #include "EnumComboBoxTemplate.h" #include "EventDataFileRead.h" #include "EventGraphicsUpdateAllWindows.h" #include "EventManager.h" #include "EventSurfaceColoringInvalidate.h" #include "EventUserInterfaceUpdate.h" #include "GuiManager.h" #include "ProgressReportingDialog.h" #include "SessionManager.h" #include "SpecFile.h" #include "UsernamePasswordWidget.h" #include "WuQMessageBox.h" #include "WuQWidgetObjectGroup.h" #include "WuQtUtilities.h" using namespace caret; /** * \class caret::CaretFileRemoteDialog * \brief Open a remote data file * */ /** * Constructor. */ CaretFileRemoteDialog::CaretFileRemoteDialog(QWidget* parent) : WuQDialogModal("Open Location", parent) { QWidget* locationWidget = createLocationWidget(); m_usernamePasswordWidget = new UsernamePasswordWidget(); createAndLoadStandardData(); QWidget* controlsWidget = new QWidget(); QVBoxLayout* controlsLayout = new QVBoxLayout(controlsWidget); controlsLayout->addWidget(locationWidget); controlsLayout->addWidget(m_usernamePasswordWidget, 0, Qt::AlignCenter); WuQtUtilities::setLayoutSpacingAndMargins(controlsLayout, 4, 2); setCentralWidget(controlsWidget, WuQDialog::SCROLL_AREA_NEVER); /* * Restore previous selections */ QRadioButton* defaultRadioButton = NULL; if (s_previousSelections.m_firstTime) { s_previousSelections.m_firstTime = false; } m_customUrlLineEdit->setText(s_previousSelections.m_customURL); m_customUrlFileTypeComboBox->setSelectedItem(s_previousSelections.m_customDataFileType); m_standardFileComboBox->setCurrentIndex(s_previousSelections.m_standardFileComboBoxIndex); if (m_locationCustomRadioButton->text() == s_previousSelections.m_radioButtonText) { defaultRadioButton = m_locationCustomRadioButton; } else if (m_locationStandardRadioButton->text() == s_previousSelections.m_radioButtonText) { defaultRadioButton = m_locationStandardRadioButton; } if (defaultRadioButton != NULL) { defaultRadioButton->setChecked(true); } locationSourceRadioButtonClicked(defaultRadioButton); } /** * Destructor. */ CaretFileRemoteDialog::~CaretFileRemoteDialog() { } /** * @return The location widget. */ QWidget* CaretFileRemoteDialog::createLocationWidget() { QStringList filenameFilterList; std::vector dataFileTypes; DataFileTypeEnum::getAllEnums(dataFileTypes, DataFileTypeEnum::OPTIONS_NONE); m_locationCustomRadioButton = new QRadioButton("Custom"); m_locationStandardRadioButton = new QRadioButton("Standard"); QButtonGroup* locationButtonGroup = new QButtonGroup(this); locationButtonGroup->addButton(m_locationCustomRadioButton); locationButtonGroup->addButton(m_locationStandardRadioButton); QObject::connect(locationButtonGroup, SIGNAL(buttonClicked(QAbstractButton*)), this, SLOT(locationSourceRadioButtonClicked(QAbstractButton*))); QLabel* customUrlLabel = new QLabel("URL: "); QLabel* customUrlTypeLabel = new QLabel("Type: "); m_customUrlLineEdit = new QLineEdit(); m_customUrlLineEdit->setMinimumWidth(400); m_customUrlFileTypeComboBox = new EnumComboBoxTemplate(this); m_customUrlFileTypeComboBox->setupWithItems(dataFileTypes); m_customUrlFileTypeComboBox->setSelectedItem(DataFileTypeEnum::CONNECTIVITY_DENSE_SCALAR); m_standardFileComboBox = new QComboBox(); QGroupBox* locationGroupBox = new QGroupBox("Location"); QGridLayout* locationGridLayout = new QGridLayout(locationGroupBox); locationGridLayout->setColumnStretch(0, 0); locationGridLayout->setColumnStretch(1, 0); locationGridLayout->setColumnStretch(2, 100); int row = locationGridLayout->rowCount(); locationGridLayout->addWidget(m_locationCustomRadioButton, row, 0, 2, 1); locationGridLayout->addWidget(customUrlLabel, row, 1); locationGridLayout->addWidget(m_customUrlLineEdit, row, 2); row++; locationGridLayout->addWidget(customUrlTypeLabel, row, 1); locationGridLayout->addWidget(m_customUrlFileTypeComboBox->getWidget(), row, 2); row++; locationGridLayout->addWidget(WuQtUtilities::createHorizontalLineWidget(), row, 0, 1, 3); row++; locationGridLayout->addWidget(m_locationStandardRadioButton, row, 0, 1, 2); locationGridLayout->addWidget(m_standardFileComboBox, row, 2); m_customWidgetGroup = new WuQWidgetObjectGroup(this); m_customWidgetGroup->add(customUrlLabel); m_customWidgetGroup->add(customUrlTypeLabel); m_customWidgetGroup->add(m_customUrlLineEdit); m_customWidgetGroup->add(m_customUrlFileTypeComboBox); m_standardWidgetGroup = new WuQWidgetObjectGroup(this); m_standardWidgetGroup->add(m_standardFileComboBox); QObject::connect(m_customUrlLineEdit, SIGNAL(textEdited(const QString&)), this, SLOT(selectCustomRadioButton())); QObject::connect(m_customUrlFileTypeComboBox, SIGNAL(itemActivated()), this, SLOT(selectCustomRadioButton())); QObject::connect(m_standardFileComboBox, SIGNAL(activated(int)), this, SLOT(selectStandardRadioButton())); return locationGroupBox; } /** * To select standard radio button. */ void CaretFileRemoteDialog::selectStandardRadioButton() { m_locationStandardRadioButton->setChecked(true); locationSourceRadioButtonClicked(m_locationStandardRadioButton); } /** * To select custom radio button. */ void CaretFileRemoteDialog::selectCustomRadioButton() { m_locationCustomRadioButton->setChecked(true); locationSourceRadioButtonClicked(m_locationCustomRadioButton); } /** * Called when a location source radio button is clicked. */ void CaretFileRemoteDialog::locationSourceRadioButtonClicked(QAbstractButton* button) { bool okButtonEnabled = true; if (button == m_locationCustomRadioButton) { /* nothing */ } else if (button == m_locationStandardRadioButton) { /* nothing */ } else { okButtonEnabled = false; } getDialogButtonBox()->button(QDialogButtonBox::Ok)->setEnabled(okButtonEnabled); } /** * Create and load the standard data. */ void CaretFileRemoteDialog::createAndLoadStandardData() { m_standardFileComboBox->blockSignals(true); m_standardData.push_back(StandardData("HCP-Q1 Correlation with Mean Gray Regression (Avg 20)", "https://db.humanconnectome.org/spring/cifti-average?resource=HCP_Q1:Q1:Demo_HCP_unrelated20_FunctionalConnectivity_mgt-regression", DataFileTypeEnum::CONNECTIVITY_DENSE)); m_standardData.push_back(StandardData("HCP-Q1 Full Correlation (Avg 20)", "https://db.humanconnectome.org/spring/cifti-average?resource=HCP_Q1:Q1:Demo_HCP_unrelated20_FunctionalConnectivity_FullCorrel", DataFileTypeEnum::CONNECTIVITY_DENSE)); m_standardData.push_back(StandardData("Pilot1 Average Dense Connectome", "https://db.humanconnectome.org/data/services/cifti-average?searchID=PILOT1_AVG_xnat:subjectData", DataFileTypeEnum::CONNECTIVITY_DENSE)); const int numStandardData = static_cast(m_standardData.size()); for (int i = 0; i < numStandardData; i++) { m_standardFileComboBox->addItem(m_standardData[i].m_userFriendlyName, qVariantFromValue(i)); } m_standardFileComboBox->blockSignals(false); } /** * Called when OK button is pressed. */ void CaretFileRemoteDialog::okButtonClicked() { AString username; AString password; m_usernamePasswordWidget->getUsernameAndPassword(username, password); bool customSelected = false; if (m_locationCustomRadioButton->isChecked()) { customSelected = true; } else if (m_locationStandardRadioButton->isChecked()) { customSelected = false; } else { CaretAssert(0); // OK button only enabled if radio button selected } AString dataFileURL; DataFileTypeEnum::Enum dataFileType = DataFileTypeEnum::UNKNOWN; if (customSelected) { dataFileURL = m_customUrlLineEdit->text().trimmed(); dataFileType = m_customUrlFileTypeComboBox->getSelectedItem(); s_previousSelections.m_customURL = dataFileURL; s_previousSelections.m_customDataFileType = dataFileType; s_previousSelections.m_radioButtonText = m_locationCustomRadioButton->text(); } else { const int indx = m_standardFileComboBox->currentIndex(); if (indx >= 0) { dataFileURL = m_standardData[indx].m_locationUrl; dataFileType = m_standardData[indx].m_dataFileType; } s_previousSelections.m_standardFileComboBoxIndex = indx; s_previousSelections.m_radioButtonText = m_locationStandardRadioButton->text(); } BrainBrowserWindow* browserWindow = GuiManager::get()->getActiveBrowserWindow(); std::vector files; files.push_back(dataFileURL); std::vector dataFileTypes; dataFileTypes.push_back(dataFileType); const bool successFlag = browserWindow->loadFilesFromNetwork(this, files, dataFileTypes, username, password); if (successFlag) { WuQDialogModal::okButtonClicked(); } } connectome-workbench-1.2.3+git41-gc4c6c90/src/GuiQt/CaretFileRemoteDialog.h000066400000000000000000000073541300200146000261110ustar00rootroot00000000000000#ifndef __CARET_FILE_REMOTE_DIALOG__H_ #define __CARET_FILE_REMOTE_DIALOG__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 "DataFileTypeEnum.h" #include "WuQDialogModal.h" class QCheckBox; class QComboBox; class QLineEdit; class QRadioButton; namespace caret { class EnumComboBoxTemplate; class UsernamePasswordWidget; class WuQWidgetObjectGroup; class CaretFileRemoteDialog : public WuQDialogModal { Q_OBJECT public: CaretFileRemoteDialog(QWidget* parent = 0); virtual ~CaretFileRemoteDialog(); protected: void okButtonClicked(); private slots: void locationSourceRadioButtonClicked(QAbstractButton* button); void selectCustomRadioButton(); void selectStandardRadioButton(); private: CaretFileRemoteDialog(const CaretFileRemoteDialog&); CaretFileRemoteDialog& operator=(const CaretFileRemoteDialog&); class StandardData { public: StandardData(const AString& userFriendlyName, const AString locationUrl, const DataFileTypeEnum::Enum dataFileType) : m_userFriendlyName(userFriendlyName), m_locationUrl(locationUrl), m_dataFileType(dataFileType) { } AString m_userFriendlyName; AString m_locationUrl; DataFileTypeEnum::Enum m_dataFileType; }; class PreviousSelections { public: PreviousSelections() { m_customURL = "http://"; m_customDataFileType = DataFileTypeEnum::CONNECTIVITY_DENSE; m_standardFileComboBoxIndex = 0; m_firstTime = true; } AString m_customURL; DataFileTypeEnum::Enum m_customDataFileType; int m_standardFileComboBoxIndex; AString m_radioButtonText; bool m_firstTime; }; QWidget* createLocationWidget(); void createAndLoadStandardData(); UsernamePasswordWidget* m_usernamePasswordWidget; QRadioButton* m_locationCustomRadioButton; QRadioButton* m_locationStandardRadioButton; QComboBox* m_standardFileComboBox; EnumComboBoxTemplate* m_customUrlFileTypeComboBox; QLineEdit* m_customUrlLineEdit; QCheckBox* m_savePasswordToPreferencesCheckBox; WuQWidgetObjectGroup* m_customWidgetGroup; WuQWidgetObjectGroup* m_standardWidgetGroup; std::vector m_standardData; static PreviousSelections s_previousSelections; }; #ifdef __CARET_FILE_REMOTE_DIALOG_DECLARE__ CaretFileRemoteDialog::PreviousSelections CaretFileRemoteDialog::s_previousSelections; #endif // __CARET_FILE_REMOTE_DIALOG_DECLARE__ } // namespace #endif //__CARET_FILE_REMOTE_DIALOG__H_ connectome-workbench-1.2.3+git41-gc4c6c90/src/GuiQt/CaretMappableDataFileAndMapSelector.cxx000066400000000000000000001142421300200146000312010ustar00rootroot00000000000000 /*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 #include #include "WuQDataEntryDialog.h" #include "WuQFactory.h" #include "WuQMessageBox.h" #include "WuQWidgetObjectGroup.h" #include "WuQtUtilities.h" #define __CARET_MAPPABLE_DATA_FILE_AND_MAP_SELECTOR_DECLARE__ #include "CaretMappableDataFileAndMapSelector.h" #undef __CARET_MAPPABLE_DATA_FILE_AND_MAP_SELECTOR_DECLARE__ #include "Brain.h" #include "BrainStructure.h" #include "CaretAssert.h" #include "CiftiBrainordinateLabelFile.h" #include "CiftiBrainordinateScalarFile.h" #include "DataFileException.h" #include "EventDataFileAdd.h" #include "EventManager.h" #include "GiftiLabel.h" #include "GiftiLabelTableEditor.h" #include "GiftiLabelTable.h" #include "GiftiMetaData.h" #include "GuiManager.h" #include "LabelFile.h" #include "MetricFile.h" #include "SpecFile.h" #include "StructureEnumComboBox.h" #include "WuQMessageBox.h" #include "WuQtUtilities.h" using namespace caret; /** * \class caret::CaretMappableDataFileAndMapSelector * \brief Widget for selecting a map file and map. * * Dialog that allows the user to select a map file and * a map within the map file. The dialog also allows * the user to create a new map file and/or map. */ /** * Constructor. * * @param defaultName * Default name for file. * @param brain * Brain structure that contains map files. * @param structure * Type of map file. * @param parent * The parent. */ CaretMappableDataFileAndMapSelector::CaretMappableDataFileAndMapSelector(const AString defaultName, Brain* brain, const std::vector& supportedMapFileTypes, const std::vector& supportedStructures, QObject* parent) : WuQWidget(parent) { m_brain = brain; m_defaultName = defaultName; m_newMapAction = NULL; m_supportedMapFileTypes = supportedMapFileTypes; m_supportedStructures = supportedStructures; m_mapFileTypesThatAllowAddingMaps.push_back(DataFileTypeEnum::LABEL); m_mapFileTypesThatAllowAddingMaps.push_back(DataFileTypeEnum::METRIC); /* * File Type */ QLabel* mapFileTypeLabel = new QLabel("File Type: "); this->mapFileTypeComboBox = new QComboBox(); for (std::vector::iterator dataFileTypeIterator = m_supportedMapFileTypes.begin(); dataFileTypeIterator != m_supportedMapFileTypes.end(); dataFileTypeIterator++) { const DataFileTypeEnum::Enum dataFileType = *dataFileTypeIterator; const AString name = DataFileTypeEnum::toGuiName(dataFileType); this->mapFileTypeComboBox->addItem(name, (int)DataFileTypeEnum::toIntegerCode(dataFileType)); } QObject::connect(this->mapFileTypeComboBox, SIGNAL(activated(int)), this, SLOT(mapFileTypeComboBoxSelected(int))); /* * Structure */ QLabel* mapFileStructureLabel = new QLabel("Structure: "); m_mapFileStructureComboBox = new StructureEnumComboBox(this); m_mapFileStructureComboBox->listOnlyTheseStructures(supportedStructures); if ( ! supportedStructures.empty()) { m_mapFileStructureComboBox->setSelectedStructure(supportedStructures[0]); } QObject::connect(m_mapFileStructureComboBox, SIGNAL(structureSelected(const StructureEnum::Enum)), this, SLOT(mapFileStructureComboBoxSelected(const StructureEnum::Enum))); /* * File Selection */ QLabel* mapFileLabel = new QLabel("File: "); this->mapFileComboBox = new QComboBox(); QObject::connect(this->mapFileComboBox, SIGNAL(activated(int)), this, SLOT(mapFileComboBoxSelected(int))); QAction* newMapFileAction = WuQtUtilities::createAction("New...", "", this, this, SLOT(newMapFileToolButtonSelected())); QToolButton* newMapFileToolButton = new QToolButton(); newMapFileToolButton->setDefaultAction(newMapFileAction); /* * Map Name Selection */ QLabel* mapNameLabel = new QLabel("Map: "); this->mapNameComboBox = new QComboBox(); QObject::connect(this->mapNameComboBox, SIGNAL(activated(int)), this, SLOT(mapNameComboBoxSelected(int))); m_newMapAction = WuQtUtilities::createAction("New...", "", this, this, SLOT(newMapToolButtonSelected())); QToolButton* newMapToolButton = new QToolButton(); newMapToolButton->setDefaultAction(m_newMapAction); /* * Label selection */ QLabel* labelNameLabel = new QLabel("Label Name: "); this->labelSelectionComboBox = new QComboBox(); QObject::connect(this->labelSelectionComboBox, SIGNAL(currentIndexChanged(int)), this, SLOT(labelNameComboBoxSelected(int))); QAction* editLabelsAction = WuQtUtilities::createAction("Edit...", "Add/Edit/Delete Labels\nand edit their colors", this, this, SLOT(showLabelsEditor())); QToolButton* editLabelsToolButton = new QToolButton(); editLabelsToolButton->setDefaultAction(editLabelsAction); this->labelValueControlsGroup = new WuQWidgetObjectGroup(this); this->labelValueControlsGroup->add(labelNameLabel); this->labelValueControlsGroup->add(this->labelSelectionComboBox); this->labelValueControlsGroup->add(editLabelsToolButton); /* * Float Value Entry */ QLabel* floatValueLabel = new QLabel("Value: "); m_floatValueSpinBox = WuQFactory::newDoubleSpinBox(); m_floatValueSpinBox->setMaximumWidth(100); m_floatValueSpinBox->setRange(-std::numeric_limits::max(), std::numeric_limits::max()); m_floatValueSpinBox->setValue(1.0); QObject::connect(m_floatValueSpinBox, SIGNAL(valueChanged(double)), this, SLOT(floatValueChanged(double))); m_floatValueControlsGroup = new WuQWidgetObjectGroup(this); m_floatValueControlsGroup->add(floatValueLabel); m_floatValueControlsGroup->add(m_floatValueSpinBox); /* * Layout widgets */ this->widget = new QWidget(); QGridLayout* gridLayout = new QGridLayout(this->widget); WuQtUtilities::setLayoutSpacingAndMargins(gridLayout, 4, 2); gridLayout->setColumnStretch(0, 0); gridLayout->setColumnStretch(1, 100); gridLayout->setColumnStretch(2, 0); int32_t rowIndex = gridLayout->rowCount(); gridLayout->addWidget(mapFileTypeLabel, rowIndex, 0); gridLayout->addWidget(this->mapFileTypeComboBox, rowIndex, 1); rowIndex++; gridLayout->addWidget(mapFileStructureLabel, rowIndex, 0); gridLayout->addWidget(m_mapFileStructureComboBox->getWidget(), rowIndex, 1); rowIndex++; gridLayout->addWidget(mapFileLabel, rowIndex, 0); gridLayout->addWidget(this->mapFileComboBox, rowIndex, 1); gridLayout->addWidget(newMapFileToolButton, rowIndex, 2); rowIndex++; gridLayout->addWidget(mapNameLabel, rowIndex, 0); gridLayout->addWidget(this->mapNameComboBox, rowIndex, 1); gridLayout->addWidget(newMapToolButton, rowIndex, 2); rowIndex++; gridLayout->addWidget(labelNameLabel, rowIndex, 0); gridLayout->addWidget(this->labelSelectionComboBox, rowIndex, 1); gridLayout->addWidget(editLabelsToolButton, rowIndex, 2); rowIndex++; gridLayout->addWidget(floatValueLabel, rowIndex, 0, Qt::AlignLeft); gridLayout->addWidget(m_floatValueSpinBox, rowIndex, 1); rowIndex++; this->setMapFileTypeComboBoxCurrentIndex(0); loadLastSelectionsForFileType(DataFileTypeEnum::UNKNOWN); } /** * Destructor. */ CaretMappableDataFileAndMapSelector::~CaretMappableDataFileAndMapSelector() { } /** * Load the map file combo box. * * @param selectedFileIndex * Index of selected file. */ void CaretMappableDataFileAndMapSelector::loadMapFileComboBox(const int32_t selectedFileIndex) { /* * Fill widgets */ std::vector matchingMapFiles; const DataFileTypeEnum::Enum selectedDataFileType = this->getSelectedMapFileType(); const StructureEnum::Enum selectedStructure = this->getSelectedMapFileStructure(); std::vector caretMappableDataFiles; m_brain->getAllMappableDataFiles(caretMappableDataFiles); for (std::vector::iterator iter = caretMappableDataFiles.begin(); iter != caretMappableDataFiles.end(); iter++) { CaretMappableDataFile* caretMapFile = *iter; const DataFileTypeEnum::Enum fileType = caretMapFile->getDataFileType(); if (selectedDataFileType == fileType) { if (caretMapFile->isMappableToSurfaceStructure(selectedStructure)) { matchingMapFiles.push_back(caretMapFile); } } } this->mapFileComboBox->clear(); for (std::vector::iterator iter = matchingMapFiles.begin(); iter != matchingMapFiles.end(); iter++) { CaretMappableDataFile* cmdf = *iter; this->mapFileComboBox->addItem(cmdf->getFileNameNoPath(), qVariantFromValue((void*)cmdf)); } if ((selectedFileIndex >= 0) && (selectedFileIndex < this->mapFileComboBox->count())) { this->setMapFileComboBoxCurrentIndex(selectedFileIndex); } this->loadMapNameComboBox(0); } /** * Called when the user makes a selection from the map file type combo box. * A signal indicating that the selections have changed will be emitted. * * @param indx * Index of item selected. */ void CaretMappableDataFileAndMapSelector::mapFileTypeComboBoxSelected(int indx) { this->setMapFileTypeComboBoxCurrentIndex(indx); loadLastSelectionsForFileType(this->getSelectedMapFileType()); enableDisableNewMapAction(); emit selectionChanged(this); } /** * Enable/disable the new map action. */ void CaretMappableDataFileAndMapSelector::enableDisableNewMapAction() { if (m_newMapAction != NULL) { const DataFileTypeEnum::Enum dataFileType = this->getSelectedMapFileType(); if (std::find(m_mapFileTypesThatAllowAddingMaps.begin(), m_mapFileTypesThatAllowAddingMaps.end(), dataFileType) != m_mapFileTypesThatAllowAddingMaps.end()) { m_newMapAction->setEnabled(true); } else { m_newMapAction->setEnabled(false); } } } /** * Called when the structure is changed. * * @param structure * Selected structure. */ void CaretMappableDataFileAndMapSelector::mapFileStructureComboBoxSelected(const StructureEnum::Enum /*structure*/) { loadLastSelectionsForFileType(getSelectedMapFileType()); emit selectionChanged(this); } /** * Set the map file type combo box to the given index. * @param indx * Index for selection. */ void CaretMappableDataFileAndMapSelector::setMapFileTypeComboBoxCurrentIndex(int indx) { this->mapFileTypeComboBox->setCurrentIndex(indx); const int32_t integerCode = this->mapFileTypeComboBox->itemData(indx).toInt(); const DataFileTypeEnum::Enum dataFileType = DataFileTypeEnum::fromIntegerCode(integerCode, NULL); this->updateFileTypeSelections(dataFileType); this->loadMapFileComboBox(0); } /** * Update the selections for specific file types. */ void CaretMappableDataFileAndMapSelector::updateFileTypeSelections(const DataFileTypeEnum::Enum dataFileType) { bool showLabelSelectionWidgets = false; bool showScalarSelectionWidgets = false; switch (dataFileType) { case DataFileTypeEnum::CONNECTIVITY_DENSE_LABEL: showLabelSelectionWidgets = true; break; case DataFileTypeEnum::CONNECTIVITY_DENSE_SCALAR: showScalarSelectionWidgets = true; break; case DataFileTypeEnum::LABEL: showLabelSelectionWidgets = true; break; case DataFileTypeEnum::METRIC: showScalarSelectionWidgets = true; break; default: break; } labelValueControlsGroup->setVisible(showLabelSelectionWidgets); m_floatValueControlsGroup->setVisible(showScalarSelectionWidgets); } /** * Called when the user makes a selection from the map file combo box. * A signal indicating that the selections have changed will be emitted. * * @param indx * Index of item selected. */ void CaretMappableDataFileAndMapSelector::mapFileComboBoxSelected(int indx) { this->setMapFileComboBoxCurrentIndex(indx); emit selectionChanged(this); } /** * Set the map file combo box to the given index. * @param indx * Index for selection. */ void CaretMappableDataFileAndMapSelector::setMapFileComboBoxCurrentIndex(int indx) { this->mapFileComboBox->setCurrentIndex(indx); this->loadMapNameComboBox(0); this->loadLabelNameComboBox(); } /** * Set the map name combo box to the given index. * @param indx * Index for selection. */ void CaretMappableDataFileAndMapSelector::setMapNameComboBoxCurrentIndex(int indx) { this->mapNameComboBox->setCurrentIndex(indx); } /** * Load the contents of the map name combo box. * @param selectedMapIndex * Index of the map that is selected. */ void CaretMappableDataFileAndMapSelector::loadMapNameComboBox(const int32_t selectedMapIndex) { this->mapNameComboBox->clear(); CaretMappableDataFile* cmdf = this->getSelectedMapFile(); if (cmdf != NULL) { const int32_t numMaps = cmdf->getNumberOfMaps(); for (int32_t i = 0; i < numMaps; i++) { this->mapNameComboBox->addItem(cmdf->getMapName(i)); } if ((selectedMapIndex >= 0) && (selectedMapIndex < numMaps)) { this->mapNameComboBox->setCurrentIndex(selectedMapIndex); } } } /** * Called when the user makes a selection from the map name combo box. * * @param indx * Index of item selected. */ void CaretMappableDataFileAndMapSelector::mapNameComboBoxSelected(int /*indx*/) { emit selectionChanged(this); } /** * Called when the new map file tool button is pressed. */ void CaretMappableDataFileAndMapSelector::newMapFileToolButtonSelected() { const DataFileTypeEnum::Enum dataFileType = this->getSelectedMapFileType(); QString fileExtension = DataFileTypeEnum::toFileExtension(dataFileType); QString newFileName = "NewFile"; QString newMapName = "Map Name"; if (m_defaultName.isEmpty() == false) { newFileName = m_defaultName; newMapName = m_defaultName; } newFileName += ("." + fileExtension); WuQDataEntryDialog newFileMapDialog("New Map File and Map", this->widget); QLineEdit* mapFileNameLineEdit = newFileMapDialog.addLineEditWidget("New Map File Name", newFileName); QLineEdit* mapNameLineEdit = newFileMapDialog.addLineEditWidget("New Map Name", newMapName); if (newFileMapDialog.exec() == WuQDataEntryDialog::Accepted) { QString mapFileName = mapFileNameLineEdit->text(); const QString mapName = mapNameLineEdit->text(); try { if (mapFileName.endsWith(fileExtension) == false) { mapFileName += ("." + fileExtension); } const StructureEnum::Enum structure = getSelectedMapFileStructure(); BrainStructure* brainStructure = GuiManager::get()->getBrain()->getBrainStructure(structure, false); if (brainStructure == NULL) { throw DataFileException(newFileName, "Invalid brain structure (not loaded): " + StructureEnum::toGuiName(structure)); } const int32_t numberOfNodes = brainStructure->getNumberOfNodes(); if (numberOfNodes <= 0) { throw DataFileException(newFileName, "Invalid number of nodes (0) in brain structure: " + StructureEnum::toGuiName(structure)); } switch (dataFileType) { case DataFileTypeEnum::CONNECTIVITY_DENSE_LABEL: { AString errorMessage; CiftiMappableDataFile* ciftiMappableDataFile = CiftiMappableDataFile::newInstanceForCiftiFileTypeAndSurface(DataFileTypeEnum::CONNECTIVITY_DENSE_LABEL, structure, numberOfNodes, errorMessage); if (ciftiMappableDataFile == NULL) { throw DataFileException(mapFileName, errorMessage); } CaretAssert(dynamic_cast(ciftiMappableDataFile) != NULL); if (ciftiMappableDataFile != NULL) { ciftiMappableDataFile->setMapName(0, mapName); ciftiMappableDataFile->setFileName(mapFileName); EventManager::get()->sendEvent(EventDataFileAdd(ciftiMappableDataFile).getPointer()); } else { throw DataFileException(mapFileName, errorMessage); } } break; case DataFileTypeEnum::CONNECTIVITY_DENSE_SCALAR: { AString errorMessage; CiftiMappableDataFile* ciftiMappableDataFile = CiftiMappableDataFile::newInstanceForCiftiFileTypeAndSurface(DataFileTypeEnum::CONNECTIVITY_DENSE_SCALAR, structure, numberOfNodes, errorMessage); if (ciftiMappableDataFile == NULL) { throw DataFileException(mapFileName, errorMessage); } CaretAssert(dynamic_cast(ciftiMappableDataFile) != NULL); if (ciftiMappableDataFile != NULL) { ciftiMappableDataFile->setMapName(0, mapName); ciftiMappableDataFile->setFileName(mapFileName); EventManager::get()->sendEvent(EventDataFileAdd(ciftiMappableDataFile).getPointer()); } else { throw DataFileException(mapFileName, errorMessage); } } break; case DataFileTypeEnum::LABEL: { LabelFile* labelFile = new LabelFile(); labelFile->setNumberOfNodesAndColumns(numberOfNodes, 1); labelFile->setMapName(0, mapName); labelFile->setStructure(structure); labelFile->setFileName(mapFileName); EventManager::get()->sendEvent(EventDataFileAdd(labelFile).getPointer()); } break; case DataFileTypeEnum::METRIC: { MetricFile* metricFile = new MetricFile(); metricFile->setNumberOfNodesAndColumns(numberOfNodes, 1); metricFile->setMapName(0, mapName); metricFile->setStructure((structure)); metricFile->setFileName(mapFileName); EventManager::get()->sendEvent(EventDataFileAdd(metricFile).getPointer()); } break; default: CaretAssertMessage(0, ("File Type " + DataFileTypeEnum::toName(dataFileType) + " not allowed.")); break; } this->loadMapFileComboBox(0); const int numMapFiles = this->mapFileComboBox->count(); if (numMapFiles > 0) { this->setMapFileComboBoxCurrentIndex(numMapFiles - 1); } this->setMapNameComboBoxCurrentIndex(0); emit selectionChanged(this); } catch (const DataFileException& dfe) { WuQMessageBox::errorOk(this->widget, dfe.whatString()); } } } /** * @return The widget is for displaying this selector. */ QWidget* CaretMappableDataFileAndMapSelector::getWidget() { return this->widget; } /** * Called when the new map tool button is pressed. */ void CaretMappableDataFileAndMapSelector::newMapToolButtonSelected() { CaretMappableDataFile* mapFile = this->getSelectedMapFile(); if (mapFile != NULL) { const StructureEnum::Enum structure = getSelectedMapFileStructure(); if ( ! mapFile->isMappableToSurfaceStructure(structure)) { WuQMessageBox::errorOk(this->getWidget(), "The selected file cannot be mapped to the given structure, use the New button to create a new file."); return; } QString presetMapName = "Map Name"; if (m_defaultName.isEmpty() == false) { presetMapName = m_defaultName; } bool valid = false; const QString newMapName = QInputDialog::getText(this->mapFileComboBox, "Map Name", "Map Name", QLineEdit::Normal, presetMapName, &valid); if (valid) { try { GiftiTypeFile* gtf = dynamic_cast(mapFile); int32_t mapIndex = 0; if (gtf != NULL ) { gtf->addMaps(gtf->getNumberOfNodes(), 1); mapIndex = gtf->getNumberOfMaps() - 1; gtf->setMapName(mapIndex, newMapName); this->loadMapNameComboBox(mapIndex); } else { CaretAssertMessage(0, "Add support for non-GIFTI files !!!!"); } this->loadLabelNameComboBox(); } catch (const DataFileException& e) { WuQMessageBox::errorOk(this->getWidget(), e.whatString()); } } } else { WuQMessageBox::errorOk(this->getWidget(), "The selected file is invalid, use the New button to create a new file."); } } /** * @return The selected map file. Value will be NULL * if no map file is selected. */ CaretMappableDataFile* CaretMappableDataFileAndMapSelector::getSelectedMapFile() { const int indx = this->mapFileComboBox->currentIndex(); if ((indx >= 0) && (indx < this->mapFileComboBox->count())) { void* pointer = this->mapFileComboBox->itemData(indx).value(); CaretMappableDataFile* cmdf = (CaretMappableDataFile*)pointer; return cmdf; } return NULL; } /** * @return The index of the selected map. Will be negative * value if there is no map file or the map file contains * no maps. */ int32_t CaretMappableDataFileAndMapSelector::getSelectedMapIndex() { const int indx = this->mapNameComboBox->currentIndex(); CaretMappableDataFile* cmdf = this->getSelectedMapFile(); if (cmdf != NULL) { if ((indx >= 0) && (indx < cmdf->getNumberOfMaps())) { return indx; } } return -1; } /** * @return The name of the selected map file type. */ AString CaretMappableDataFileAndMapSelector::getNameOfSelectedMapFileType() { const DataFileTypeEnum::Enum dataFileType = this->getSelectedMapFileType(); const AString name = DataFileTypeEnum::toGuiName(dataFileType); return name; } /** * @return The selected map file type. */ DataFileTypeEnum::Enum CaretMappableDataFileAndMapSelector::getSelectedMapFileType() const { int32_t indx = this->mapFileTypeComboBox->currentIndex(); if (indx < 0) { indx = 0; } if ((indx >= 0) && (indx < this->mapFileTypeComboBox->count())) { const int32_t integerCode = this->mapFileTypeComboBox->itemData(indx).toInt(); DataFileTypeEnum::Enum dataFileType = DataFileTypeEnum::fromIntegerCode(integerCode, NULL); return dataFileType; } CaretAssertMessage(0, "Invalid data file type"); return DataFileTypeEnum::UNKNOWN; } /** * @return The selected structure. */ StructureEnum::Enum CaretMappableDataFileAndMapSelector::getSelectedMapFileStructure() const { return m_mapFileStructureComboBox->getSelectedStructure(); } /** * Are the selections valid? * @param errorMessageOut * Contains error messages on output if selections * are not valid. * @return true if the selections are valid, else false. */ bool CaretMappableDataFileAndMapSelector::isValidSelections(AString& errorMessageOut) { errorMessageOut = ""; if (this->getSelectedMapFileType() == DataFileTypeEnum::UNKNOWN) { errorMessageOut = "Selected file type is invalid."; } else if (this->getSelectedMapFile() == NULL) { errorMessageOut = "No map file is selected, create a file with New button."; } else if (this->getSelectedMapIndex() < 0) { errorMessageOut = "No map is selected, create a map with New button."; } const bool valid = errorMessageOut.isEmpty(); return valid; } /** * Called when a selection is made from the label combo box. * @param indx * Index of selection. */ void CaretMappableDataFileAndMapSelector::labelNameComboBoxSelected(int /*indx*/) { emit selectionChanged(this); } /** * Called when a selection is made from the label combo box. * @param value * New value. */ void CaretMappableDataFileAndMapSelector::floatValueChanged(double /*value*/) { emit selectionChanged(this); } /** * Load the label names into the label selection combo box. */ void CaretMappableDataFileAndMapSelector::loadLabelNameComboBox() { const int32_t selectedKey = this->getSelectedLabelKey(); this->labelSelectionComboBox->clear(); CaretMappableDataFile* cmdf = this->getSelectedMapFile(); if (cmdf != NULL) { if (cmdf->isMappedWithLabelTable()) { const int32_t mapIndex = getSelectedMapIndex(); if (mapIndex >= 0) { GiftiLabelTable* labelTable = cmdf->getMapLabelTable(mapIndex); int32_t selectedIndex = 0; std::vector labelKeys = labelTable->getLabelKeysSortedByName(); const int numKeys = static_cast(labelKeys.size()); for (int32_t i = 0; i < numKeys; i++) { const int32_t key = labelKeys[i]; if (selectedKey == key) { selectedIndex = i; } const AString name = labelTable->getLabelName(key); this->labelSelectionComboBox->addItem(name, (int)key); } if ((selectedIndex >= 0) && (selectedIndex < this->labelSelectionComboBox->count())) { this->labelSelectionComboBox->setCurrentIndex(selectedIndex); } } } } } /** * Display the label editor. */ void CaretMappableDataFileAndMapSelector::showLabelsEditor() { CaretMappableDataFile* cmdf = this->getSelectedMapFile(); if (cmdf != NULL) { if (cmdf->isMappedWithLabelTable()) { const int32_t mapIndex = getSelectedMapIndex(); if (mapIndex >= 0) { GiftiLabelTable* labelTable = cmdf->getMapLabelTable(mapIndex); GiftiLabelTableEditor labelsEditor(cmdf, mapIndex, "Edit Labels", GiftiLabelTableEditor::OPTION_NONE, this->getWidget()); const int dialogResult = labelsEditor.exec(); this->loadLabelNameComboBox(); if (dialogResult == GiftiLabelTableEditor::Accepted) { const AString labelName = labelsEditor.getLastSelectedLabelName(); const int32_t labelKey = labelTable->getLabelKeyFromName(labelName); const int labelIndex = this->labelSelectionComboBox->findData((int)labelKey); if (labelIndex >= 0) { if (labelKey != labelTable->getUnassignedLabelKey()) { this->labelSelectionComboBox->setCurrentIndex(labelIndex); } } } } } } } /** * @return The metric value from the metric value spin box. */ float CaretMappableDataFileAndMapSelector::getSelectedMetricValue() const { const float value = m_floatValueSpinBox->value(); return value; } /** * @return Key of the selected label. */ int32_t CaretMappableDataFileAndMapSelector::getSelectedLabelKey() const { int32_t key = 0; const int indx = this->labelSelectionComboBox->currentIndex(); if ((indx >= 0) && (indx < this->labelSelectionComboBox->count())) { key = this->labelSelectionComboBox->itemData(indx).toInt(); } return key; } /** * @return Name of the selected label, empty string if no selection. */ AString CaretMappableDataFileAndMapSelector::getSelectedLabelName() const { AString name = ""; const int indx = this->labelSelectionComboBox->currentIndex(); if ((indx >= 0) && (indx < this->labelSelectionComboBox->count())) { name = this->labelSelectionComboBox->currentText(); } return name; } /** * Load the last selection that matches the given data file type. If the file type is unknown, * then just find the last selection of any kind. * * @param dataFileTypeIn * Last selected data file type. */ void CaretMappableDataFileAndMapSelector::loadLastSelectionsForFileType(const DataFileTypeEnum::Enum dataFileTypeIn) { DataFileTypeEnum::Enum dataFileType = dataFileTypeIn; StructureEnum::Enum structure = StructureEnum::INVALID; if (dataFileType != DataFileTypeEnum::UNKNOWN) { structure = getSelectedMapFileStructure(); } PreviousSelection* ps = getPreviousSelection(dataFileType, structure); // if (dataFileType == DataFileTypeEnum::UNKNOWN) { // dataFileType = DataFileTypeEnum::LABEL; // //// if (this->brainStructure->getNumberOfLabelFiles() > 0) { //// dataFileType = DataFileTypeEnum::LABEL; //// } //// else if (this->brainStructure->getNumberOfMetricFiles() > 0) { //// dataFileType = DataFileTypeEnum::METRIC; //// } // } // const int mapTypeIndex = this->mapFileTypeComboBox->findData((int)dataFileType); // if (mapTypeIndex >= 0) { // this->setMapFileTypeComboBoxCurrentIndex(mapTypeIndex); // } if (ps != NULL) { const int dataFileTypeInt = DataFileTypeEnum::toIntegerCode(ps->m_dataFileType); const int mapFileTypeIndex = mapFileTypeComboBox->findData(dataFileTypeInt); if (mapFileTypeIndex >= 0) { this->setMapFileTypeComboBoxCurrentIndex(mapFileTypeIndex); } m_mapFileStructureComboBox->setSelectedStructure(ps->m_structure); const int fileIndex = this->mapFileComboBox->findData(qVariantFromValue((void*)ps->m_mapFile)); if (fileIndex >= 0) { this->setMapFileComboBoxCurrentIndex(fileIndex); const int mapIndex = ps->m_mapFile->getMapIndexFromName(ps->m_mapName); if (mapIndex >= 0) { this->setMapNameComboBoxCurrentIndex(mapIndex); } if ( ! ps->m_labelName.isEmpty()) { this->labelSelectionComboBox->clear(); loadLabelNameComboBox(); const int labelIndex = labelSelectionComboBox->findText(ps->m_labelName); if (labelIndex >= 0) { this->labelSelectionComboBox->setCurrentIndex(labelIndex); } } m_floatValueSpinBox->blockSignals(true); m_floatValueSpinBox->setValue(ps->m_scalarValue); m_floatValueSpinBox->blockSignals(false); } } enableDisableNewMapAction(); } /** * Save the current selections. User of this selector * MUST call this to save the selections so that they * can be initialized the next time a selector for the * brain structure is used. */ void CaretMappableDataFileAndMapSelector::saveCurrentSelections() { DataFileTypeEnum::Enum dataFileType = DataFileTypeEnum::UNKNOWN; StructureEnum::Enum structure = StructureEnum::INVALID; CaretMappableDataFile* cmdf = getSelectedMapFile(); if (cmdf != NULL) { dataFileType = cmdf->getDataFileType(); structure = m_mapFileStructureComboBox->getSelectedStructure(); AString mapName = cmdf->getMapName(getSelectedMapIndex()); PreviousSelection* ps = new PreviousSelection(structure, dataFileType, cmdf, mapName, labelSelectionComboBox->currentText(), m_floatValueSpinBox->value()); previousSelections.push_back(ps); } } /** * Get the previous selections for a data file type and structure. * * @param dataFileType * Data file type. If UNKNOWN, match to any file type. * @param structure * Structure for which previous selections are desired. * @return * Pointer to matching previous selection or NULL if no match * with a valid data file. */ CaretMappableDataFileAndMapSelector::PreviousSelection* CaretMappableDataFileAndMapSelector::getPreviousSelection(const DataFileTypeEnum::Enum dataFileType, const StructureEnum::Enum structure) { std::vector allMapFiles; GuiManager::get()->getBrain()->getAllMappableDataFiles(allMapFiles); for (std::vector::reverse_iterator iter = previousSelections.rbegin(); iter != previousSelections.rend(); iter++) { PreviousSelection* ps = *iter; bool matchFlag = false; if (dataFileType == DataFileTypeEnum::UNKNOWN) { matchFlag = true; } else if ((ps->m_dataFileType == dataFileType) && (ps->m_structure == structure)) { matchFlag = true; } if (matchFlag) { if (std::find(allMapFiles.begin(), allMapFiles.end(), ps->m_mapFile) != allMapFiles.end()) { return ps; } } } return NULL; } connectome-workbench-1.2.3+git41-gc4c6c90/src/GuiQt/CaretMappableDataFileAndMapSelector.h000066400000000000000000000155271300200146000306340ustar00rootroot00000000000000#ifndef __CARET_MAPPABLE_DATA_FILE_AND_MAP_SELECTOR__H_ #define __CARET_MAPPABLE_DATA_FILE_AND_MAP_SELECTOR__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" #include "CaretMappableDataFile.h" #include "DataFileTypeEnum.h" #include "StructureEnum.h" #include "WuQWidget.h" class QAction; class QComboBox; class QDoubleSpinBox; class QLineEdit; class QStackedWidget; namespace caret { class Brain; class BrainStructure; class CiftiBrainordinateScalarFile; class GiftiTypeFile; class LabelFile; class MetricFile; class StructureEnumComboBox; class WuQWidgetObjectGroup; class CaretMappableDataFileAndMapSelector : public WuQWidget { Q_OBJECT public: CaretMappableDataFileAndMapSelector(const AString defaultName, Brain* brain, const std::vector& supportedMapFileTypes, const std::vector& supportedStructures, QObject* parent); virtual ~CaretMappableDataFileAndMapSelector(); QWidget* getWidget(); CaretMappableDataFile* getSelectedMapFile(); DataFileTypeEnum::Enum getSelectedMapFileType() const; StructureEnum::Enum getSelectedMapFileStructure() const; int32_t getSelectedMapIndex(); AString getNameOfSelectedMapFileType(); bool isValidSelections(AString& errorMessageOut); float getSelectedMetricValue() const; int32_t getSelectedLabelKey() const; AString getSelectedLabelName() const; void saveCurrentSelections(); signals: void selectionChanged(CaretMappableDataFileAndMapSelector*); private: CaretMappableDataFileAndMapSelector(const CaretMappableDataFileAndMapSelector&); CaretMappableDataFileAndMapSelector& operator=(const CaretMappableDataFileAndMapSelector&); private slots: void mapFileTypeComboBoxSelected(int); void mapFileStructureComboBoxSelected(const StructureEnum::Enum); void mapFileComboBoxSelected(int); void mapNameComboBoxSelected(int); void labelNameComboBoxSelected(int); void floatValueChanged(double); void newMapFileToolButtonSelected(); void newMapToolButtonSelected(); void showLabelsEditor(); private: void setMapFileTypeComboBoxCurrentIndex(int indx); void setMapFileComboBoxCurrentIndex(int indx); void setMapNameComboBoxCurrentIndex(int indx); void loadMapFileComboBox(const int32_t selectedFileIndex); void loadMapNameComboBox(const int32_t selectedMapIndex); void loadLabelNameComboBox(); void updateFileTypeSelections(const DataFileTypeEnum::Enum dataFileType); void enableDisableNewMapAction(); QWidget* widget; QComboBox* mapFileTypeComboBox; StructureEnumComboBox* m_mapFileStructureComboBox; QComboBox* mapFileComboBox; QComboBox* mapNameComboBox; QAction* m_newMapAction; Brain* m_brain; QWidget* m_valueWidgetFloat; WuQWidgetObjectGroup* m_floatValueControlsGroup; QDoubleSpinBox* m_floatValueSpinBox; QWidget* valueWidgetLabel; WuQWidgetObjectGroup* labelValueControlsGroup; QComboBox* labelSelectionComboBox; QStackedWidget* valueEntryStackedWidget; std::vector m_supportedMapFileTypes; std::vector m_supportedStructures; std::vector m_mapFileTypesThatAllowAddingMaps; AString m_defaultName; class PreviousSelection { public: StructureEnum::Enum m_structure; DataFileTypeEnum::Enum m_dataFileType; CaretMappableDataFile* m_mapFile; AString m_mapName; AString m_labelName; float m_scalarValue; PreviousSelection(const StructureEnum::Enum structure, const DataFileTypeEnum::Enum dataFileType, CaretMappableDataFile* mapFile, const AString& mapName, const AString& labelName, const float scalarValue) { m_structure = structure; m_dataFileType = dataFileType; m_mapFile = mapFile; m_mapName = mapName; m_labelName = labelName; m_scalarValue = scalarValue; } bool operator=(const PreviousSelection& ps) const { if ((m_structure == ps.m_structure) && (m_dataFileType == ps.m_dataFileType)) { return true; } return false; } }; PreviousSelection* getPreviousSelection(const DataFileTypeEnum::Enum dataFileType, const StructureEnum::Enum structure); void loadLastSelectionsForFileType(const DataFileTypeEnum::Enum dataFileType); static std::vector previousSelections; }; #ifdef __CARET_MAPPABLE_DATA_FILE_AND_MAP_SELECTOR_DECLARE__ std::vector CaretMappableDataFileAndMapSelector::previousSelections; #endif // __CARET_MAPPABLE_DATA_FILE_AND_MAP_SELECTOR_DECLARE__ } // namespace #endif //__CARET_MAPPABLE_DATA_FILE_AND_MAP_SELECTOR__H_ connectome-workbench-1.2.3+git41-gc4c6c90/src/GuiQt/CaretMappableDataFileAndMapSelectorObject.cxx000066400000000000000000000237031300200146000323310ustar00rootroot00000000000000 /*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 __CARET_MAPPABLE_DATA_FILE_AND_MAP_SELECTOR_OBJECT_DECLARE__ #include "CaretMappableDataFileAndMapSelectorObject.h" #undef __CARET_MAPPABLE_DATA_FILE_AND_MAP_SELECTOR_OBJECT_DECLARE__ #include #include #include "CaretAssert.h" #include "CaretDataFileSelectionComboBox.h" #include "CaretMappableDataFile.h" #include "CaretMappableDataFileAndMapSelectionModel.h" #include "GuiManager.h" #include "WuQEventBlockingFilter.h" using namespace caret; /** * \class caret::CaretMappableDataFileAndMapSelectorObject * \brief Widgets for selecting a map file and map index. * \ingroup GuiQt */ /** * Constructor that creates a selection object. User will need to * insert a model into an instance created with this constructor. * This constructor is used when the selection model is stored * in another object. * * @param options * Options for this instance. * @param parent * Parent of this instance. */ CaretMappableDataFileAndMapSelectorObject::CaretMappableDataFileAndMapSelectorObject(const Options options, QObject* parent) : QObject(parent) { m_model = NULL; initializeConstruction(options); m_needToDestroyModelFlag = false; } /** * Constructor that creates a selection object with a model for the selected * data file type. * * @param dataFileType * File type for selection * @param options * Options for this instance. * @param parent * Parent of this instance. */ CaretMappableDataFileAndMapSelectorObject::CaretMappableDataFileAndMapSelectorObject(const DataFileTypeEnum::Enum dataFileType, const Options options, QObject* parent) : QObject(parent) { m_model = new CaretMappableDataFileAndMapSelectionModel(GuiManager::get()->getBrain(), dataFileType); initializeConstruction(options); m_needToDestroyModelFlag = true; } /** * Constructor that creates a selection object with a model for the selected * data file types. * * @param dataFileTypes * File types for selection * @param options * Options for this instance. * @param parent * Parent of this instance. */ CaretMappableDataFileAndMapSelectorObject::CaretMappableDataFileAndMapSelectorObject(const std::vector& dataFileTypes, const Options options, QObject* parent) : QObject(parent) { m_model = new CaretMappableDataFileAndMapSelectionModel(GuiManager::get()->getBrain(), dataFileTypes); initializeConstruction(options); m_needToDestroyModelFlag = true; } /** * Destructor. */ CaretMappableDataFileAndMapSelectorObject::~CaretMappableDataFileAndMapSelectorObject() { if (m_needToDestroyModelFlag) { delete m_model; m_model = NULL; } } /** * Assist with construction. * * @param options * Options for this instance. */ void CaretMappableDataFileAndMapSelectorObject::initializeConstruction(const Options options) { m_enabled = true; m_mapFileComboBox = new CaretDataFileSelectionComboBox(this); QObject::connect(m_mapFileComboBox, SIGNAL(fileSelected(CaretDataFile*)), this, SLOT(mapFileComboBoxFileSelected(CaretDataFile*))); m_mapIndexSpinBox = NULL; if (options & OPTION_SHOW_MAP_INDEX_SPIN_BOX) { m_mapIndexSpinBox = new QSpinBox(); m_mapIndexSpinBox->setMinimum(1); m_mapIndexSpinBox->setSingleStep(1); QObject::connect(m_mapIndexSpinBox, SIGNAL(valueChanged(int)), this, SLOT(mapIndexSpinBoxValuesChanged(int))); } m_mapNameComboBox = new QComboBox(); QObject::connect(m_mapNameComboBox, SIGNAL(activated(int)), this, SLOT(mapNameComboBoxActivated(int))); WuQEventBlockingFilter::blockMouseWheelEventInMacComboBox(m_mapNameComboBox); } /** * Update the model in this file and map selector. * * @param model * The model. */ void CaretMappableDataFileAndMapSelectorObject::updateFileAndMapSelector(CaretMappableDataFileAndMapSelectionModel* model) { CaretAssert(model); m_model = model; m_mapFileComboBox->updateComboBox(model->getCaretDataFileSelectionModel()); updateContent(); } /** * @return Model in this file and map selector. */ CaretMappableDataFileAndMapSelectionModel* CaretMappableDataFileAndMapSelectorObject::getModel() { return m_model; } /** * Get the widgets for this file and map selector. * * @param mapFileComboBox * Combo box for file selection. * @param mapIndexSpinBox * Spin box for map index selection. * @param mapNameComboBox * Combo box for map name selection. */ void CaretMappableDataFileAndMapSelectorObject::getWidgetsForAddingToLayout(QWidget* &mapFileComboBox, QWidget* &mapIndexSpinBox, QWidget* &mapNameComboBox) { mapFileComboBox = m_mapFileComboBox->getWidget(); mapIndexSpinBox = m_mapIndexSpinBox; mapNameComboBox = m_mapNameComboBox; } /** * Update the content of this object. */ void CaretMappableDataFileAndMapSelectorObject::updateContent() { bool validFlag = false; bool validMapsFlag = false; if (m_model != NULL) { CaretMappableDataFile* mapFile = m_model->getSelectedFile(); if (mapFile != NULL) { validFlag = true; const int32_t numMaps = mapFile->getNumberOfMaps(); const int32_t mapIndex = m_model->getSelectedMapIndex(); if ((mapIndex >= 0) && (mapIndex < numMaps)) { validMapsFlag = true; if (m_mapIndexSpinBox != NULL) { m_mapIndexSpinBox->blockSignals(true); m_mapIndexSpinBox->setMaximum(numMaps); m_mapIndexSpinBox->blockSignals(false); } m_mapNameComboBox->clear(); for (int32_t i = 0; i < numMaps; i++) { m_mapNameComboBox->addItem(mapFile->getMapName(i)); } if (m_mapIndexSpinBox != NULL) { /* * Note: Indices are zero to num-maps minus 1 * but show 1 to num-maps */ m_mapIndexSpinBox->blockSignals(true); m_mapIndexSpinBox->setValue(mapIndex + 1); m_mapIndexSpinBox->blockSignals(false); } m_mapNameComboBox->setCurrentIndex(mapIndex); } /* * Dense connectivity does not allow map selection. */ if (mapFile->getDataFileType() == DataFileTypeEnum::CONNECTIVITY_DENSE) { validMapsFlag = false; } } } const bool fileEnabledFlag = (validFlag & m_enabled); m_mapFileComboBox->getWidget()->setEnabled(fileEnabledFlag); const bool mapEnabledFlag = (validMapsFlag && m_enabled); if (m_mapIndexSpinBox != NULL) { m_mapIndexSpinBox->setEnabled(mapEnabledFlag); } m_mapNameComboBox->setEnabled(mapEnabledFlag); } /** * Gets called when a file is selected by the user. * * @param caretDataFile * File that is selected. */ void CaretMappableDataFileAndMapSelectorObject::mapFileComboBoxFileSelected(CaretDataFile* /*caretDataFile*/) { updateContent(); emit selectionWasPerformed(); } /** * Gets called when a file is selected by the user. * * @param mapIndex * Index of the map. */ void CaretMappableDataFileAndMapSelectorObject::mapIndexSpinBoxValuesChanged(int mapIndex) { if (m_model != NULL) { /* * Note: Indices are zero to num-maps minus 1 * but show 1 to num-maps */ int32_t zeroToOneMapIndex = mapIndex - 1; m_model->setSelectedMapIndex(zeroToOneMapIndex); updateContent(); emit selectionWasPerformed(); } } /** * Gets called when a file is selected by the user. * * @param mapIndex * Index of the map. */ void CaretMappableDataFileAndMapSelectorObject::mapNameComboBoxActivated(int mapIndex) { if (m_model != NULL) { m_model->setSelectedMapIndex(mapIndex); updateContent(); emit selectionWasPerformed(); } } /** * @return Is the selector's widgets enabled. */ bool CaretMappableDataFileAndMapSelectorObject::isEnabled() const { return m_enabled; } /** * Set the selector's widgets enabled. * * @enabled * New enabled status. */ void CaretMappableDataFileAndMapSelectorObject::setEnabled(const bool enabled) { m_enabled = enabled; updateContent(); } connectome-workbench-1.2.3+git41-gc4c6c90/src/GuiQt/CaretMappableDataFileAndMapSelectorObject.h000066400000000000000000000075601300200146000317610ustar00rootroot00000000000000#ifndef __CARET_MAPPABLE_DATA_FILE_AND_MAP_SELECTOR_OBJECT_H__ #define __CARET_MAPPABLE_DATA_FILE_AND_MAP_SELECTOR_OBJECT_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 "DataFileTypeEnum.h" class QComboBox; class QSpinBox; namespace caret { class CaretDataFile; class CaretDataFileSelectionComboBox; class CaretMappableDataFileAndMapSelectionModel; class CaretMappableDataFileAndMapSelectorObject : public QObject { Q_OBJECT public: /** Options for selector */ enum Options { /** Create the map index spin box */ OPTION_SHOW_MAP_INDEX_SPIN_BOX = 1 }; CaretMappableDataFileAndMapSelectorObject(const Options options, QObject* parent); CaretMappableDataFileAndMapSelectorObject(const DataFileTypeEnum::Enum dataFileType, const Options options, QObject* parent); CaretMappableDataFileAndMapSelectorObject(const std::vector& dataFileTypes, const Options options, QObject* parent); void updateFileAndMapSelector(CaretMappableDataFileAndMapSelectionModel* model); CaretMappableDataFileAndMapSelectionModel* getModel(); virtual ~CaretMappableDataFileAndMapSelectorObject(); void getWidgetsForAddingToLayout(QWidget* &mapFileComboBox, QWidget* &mapIndexSpinBox, QWidget* &mapNameComboBox); bool isEnabled() const; void setEnabled(const bool enabled); // ADD_NEW_METHODS_HERE signals: void selectionWasPerformed(); private slots: void mapFileComboBoxFileSelected(CaretDataFile* caretDataFile); void mapIndexSpinBoxValuesChanged(int); void mapNameComboBoxActivated(int); private: CaretMappableDataFileAndMapSelectorObject(const CaretMappableDataFileAndMapSelectorObject&); CaretMappableDataFileAndMapSelectorObject& operator=(const CaretMappableDataFileAndMapSelectorObject&); void initializeConstruction(const Options options); void updateContent(); CaretMappableDataFileAndMapSelectionModel* m_model; bool m_needToDestroyModelFlag; CaretDataFileSelectionComboBox* m_mapFileComboBox; QSpinBox* m_mapIndexSpinBox; QComboBox* m_mapNameComboBox; bool m_enabled; // ADD_NEW_MEMBERS_HERE }; #ifdef __CARET_MAPPABLE_DATA_FILE_AND_MAP_SELECTOR_OBJECT_DECLARE__ // #endif // __CARET_MAPPABLE_DATA_FILE_AND_MAP_SELECTOR_OBJECT_DECLARE__ } // namespace #endif //__CARET_MAPPABLE_DATA_FILE_AND_MAP_SELECTOR_OBJECT_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/GuiQt/ChartHistoryViewController.cxx000066400000000000000000000522541300200146000276530ustar00rootroot00000000000000 /*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 __CHART_HISTORY_VIEW_CONTROLLER_DECLARE__ #include "ChartHistoryViewController.h" #undef __CHART_HISTORY_VIEW_CONTROLLER_DECLARE__ #include "Brain.h" #include "BrowserTabContent.h" #include "CaretAssert.h" #include "CaretColorEnumComboBox.h" #include "ChartDataCartesian.h" #include "ChartDataSource.h" #include "ChartModelDataSeries.h" #include "ChartModelFrequencySeries.h" #include "ChartModelTimeSeries.h" #include "EventManager.h" #include "EventUserInterfaceUpdate.h" #include "EventGraphicsUpdateOneWindow.h" #include "GuiManager.h" #include "ModelChart.h" #include "WuQtUtilities.h" using namespace caret; /** * \class caret::ChartHistoryViewController * \brief Shows history of loaded charts in the selected tab. * \ingroup GuiQt */ /** * Constructor. */ ChartHistoryViewController::ChartHistoryViewController(const Qt::Orientation orientation, const int32_t browserWindowIndex, QWidget* parent) : QWidget(parent), m_orientation(orientation), m_browserWindowIndex(browserWindowIndex) { m_averageCheckBox = new QCheckBox("Show Average"); WuQtUtilities::setWordWrappedToolTip(m_averageCheckBox, "Display an average of the displayed chart data. " "NOTE: If the charts contain a different number of points " "the average will be that of those charts that contain " "the same number of points as the most recently displayed " "chart."); QObject::connect(m_averageCheckBox, SIGNAL(clicked(bool)), this, SLOT(averageCheckBoxClicked(bool))); QPushButton* clearPushButton = new QPushButton("Clear"); clearPushButton->setFixedWidth(clearPushButton->sizeHint().width() + 20); QObject::connect(clearPushButton, SIGNAL(clicked()), this, SLOT(clearPushButtonClicked())); WuQtUtilities::setWordWrappedToolTip(clearPushButton, "Remove all charts of the selected type in this tab"); QLabel* maximumDisplayedLabel = new QLabel("Show last "); m_maximumDisplayedSpinBox = new QSpinBox(); m_maximumDisplayedSpinBox->setMinimum(1); m_maximumDisplayedSpinBox->setMaximum(1000); QObject::connect(m_maximumDisplayedSpinBox, SIGNAL(valueChanged(int)), this, SLOT(maximumDisplayedSpinBoxValueChanged(int))); QHBoxLayout* maxDisplayedLayout = new QHBoxLayout(); maxDisplayedLayout->addWidget(maximumDisplayedLabel); maxDisplayedLayout->addWidget(m_maximumDisplayedSpinBox); maxDisplayedLayout->addStretch(); WuQtUtilities::setWordWrappedToolTip(m_maximumDisplayedSpinBox, "Maximum number of charts of the selected type " "displayed in this tab"); m_chartDataCheckBoxesSignalMapper = new QSignalMapper(this); QObject::connect(m_chartDataCheckBoxesSignalMapper, SIGNAL(mapped(int)), this, SLOT(chartDataCheckBoxSignalMapped(int))); m_chartDataColorComboBoxesSignalMapper = new QSignalMapper(this); QObject::connect(m_chartDataColorComboBoxesSignalMapper, SIGNAL(mapped(int)), this, SLOT(chartDataColorComboBoxSignalMapped(int))); m_chartDataColorConstructionButtonSignalMapper = new QSignalMapper(this); QObject::connect(m_chartDataColorConstructionButtonSignalMapper, SIGNAL(mapped(int)), this, SLOT(chartDataConstructionToolButtonSignalMapped(int))); QWidget* chartDataWidget = new QWidget(); m_chartDataGridLayout = new QGridLayout(chartDataWidget); m_chartDataGridLayout->setHorizontalSpacing(6); m_chartDataGridLayout->setVerticalSpacing(2); m_chartDataGridLayout->setContentsMargins(0, 0, 0, 0); m_chartDataGridLayout->setColumnStretch(COLUMN_CHART_DATA_CHECKBOX, 0); m_chartDataGridLayout->setColumnStretch(COLUMN_CHART_DATA_CONSTRUCTION, 0); m_chartDataGridLayout->setColumnStretch(COLUMN_CHART_DATA_COLOR, 0); m_chartDataGridLayout->setColumnStretch(COLUMN_CHART_DATA_NAME, 100); m_chartDataGridLayout->addWidget(new QLabel("On"), 0, COLUMN_CHART_DATA_CHECKBOX, Qt::AlignHCenter); m_chartDataGridLayout->addWidget(new QLabel("Move"), 0, COLUMN_CHART_DATA_CONSTRUCTION, Qt::AlignHCenter); m_chartDataGridLayout->addWidget(new QLabel("Color"), 0, COLUMN_CHART_DATA_COLOR, Qt::AlignHCenter); QVBoxLayout* leftOrTopLayout = new QVBoxLayout(); leftOrTopLayout->addWidget(m_averageCheckBox); leftOrTopLayout->addLayout(maxDisplayedLayout); leftOrTopLayout->addWidget(clearPushButton); leftOrTopLayout->addStretch(); QVBoxLayout* rightOrBottomLayout = new QVBoxLayout(); rightOrBottomLayout->addWidget(chartDataWidget); rightOrBottomLayout->addStretch(); switch (m_orientation) { case Qt::Horizontal: { m_chartDataGridLayout->addWidget(new QLabel("Name"), 0, COLUMN_CHART_DATA_NAME, Qt::AlignHCenter); QHBoxLayout* layout = new QHBoxLayout(this); layout->addLayout(leftOrTopLayout, 0); layout->addWidget(WuQtUtilities::createVerticalLineWidget()); layout->addLayout(rightOrBottomLayout, 100); layout->addStretch(); } break; case Qt::Vertical: { QVBoxLayout* layout = new QVBoxLayout(this); layout->addLayout(leftOrTopLayout, 0); layout->addLayout(rightOrBottomLayout, 100); layout->addStretch(); } break; } EventManager::get()->addEventListener(this, EventTypeEnum::EVENT_USER_INTERFACE_UPDATE); } /** * Destructor. */ ChartHistoryViewController::~ChartHistoryViewController() { EventManager::get()->removeAllEventsFromListener(this); } /** * Gets called when the average checkbox is clicked. * * @param clicked * New status. */ void ChartHistoryViewController::averageCheckBoxClicked(bool clicked) { ChartModel* chartModel = NULL; int32_t tabIndex = -1; getSelectedChartModelAndTab(chartModel, tabIndex); if (chartModel == NULL) { return; } chartModel->setAverageChartDisplaySelected(clicked); updateAfterSelectionsChanged(); } /** * Called when clear push button is clicked. */ void ChartHistoryViewController::clearPushButtonClicked() { ChartModel* chartModel = NULL; int32_t tabIndex = -1; getSelectedChartModelAndTab(chartModel, tabIndex); if (chartModel == NULL) { return; } chartModel->removeChartData(); updateAfterSelectionsChanged(); } /** * Called when maximum number of displayed charts is changed. * * @param value * New value. */ void ChartHistoryViewController::maximumDisplayedSpinBoxValueChanged(int value) { ChartModel* chartModel = NULL; int32_t tabIndex = -1; getSelectedChartModelAndTab(chartModel, tabIndex); if (chartModel == NULL) { return; } switch (chartModel->getChartSelectionMode()) { case ChartSelectionModeEnum::CHART_SELECTION_MODE_SINGLE: break; case ChartSelectionModeEnum::CHART_SELECTION_MODE_ANY: chartModel->setMaximumNumberOfChartDatasToDisplay(value); break; } updateAfterSelectionsChanged(); } /** * Update after selections in this view controller are changed. */ void ChartHistoryViewController::updateAfterSelectionsChanged() { EventManager::get()->sendEvent(EventGraphicsUpdateOneWindow(m_browserWindowIndex).getPointer()); updateHistoryViewController(); } /** * Update this view controller. */ void ChartHistoryViewController::updateHistoryViewController() { ChartModel* chartModel = NULL; int32_t tabIndex = -1; getSelectedChartModelAndTab(chartModel, tabIndex); if (chartModel == NULL) { return; } switch (chartModel->getChartSelectionMode()) { case ChartSelectionModeEnum::CHART_SELECTION_MODE_ANY: m_maximumDisplayedSpinBox->setEnabled(true); m_maximumDisplayedSpinBox->setValue(chartModel->getMaximumNumberOfChartDatasToDisplay()); break; case ChartSelectionModeEnum::CHART_SELECTION_MODE_SINGLE: m_maximumDisplayedSpinBox->setValue(1); m_maximumDisplayedSpinBox->setEnabled(false); break; } const std::vector chartDataVector = chartModel->getAllChartDatas(); const int32_t numData = static_cast(chartDataVector.size()); const int32_t numWidgetRows = static_cast(m_chartDataCheckBoxes.size()); const int32_t maxItems = std::max(numData, numWidgetRows); for (int32_t i = 0; i < maxItems; i++) { if (i >= static_cast(m_chartDataCheckBoxes.size())) { /* * Checkbox */ QCheckBox* checkBox = new QCheckBox(" "); QObject::connect(checkBox, SIGNAL(clicked(bool)), m_chartDataCheckBoxesSignalMapper, SLOT(map())); m_chartDataCheckBoxesSignalMapper->setMapping(checkBox, i); m_chartDataCheckBoxes.push_back(checkBox); /* * Construction Tool Button */ QIcon constructionIcon; const bool constructionIconValid = WuQtUtilities::loadIcon(":/LayersPanel/construction.png", constructionIcon); QToolButton* constructionToolButton = new QToolButton(); if (constructionIconValid) { constructionToolButton->setIcon(constructionIcon); } else { constructionToolButton->setText("M"); } m_chartDataContructionToolButtons.push_back(constructionToolButton); QObject::connect(constructionToolButton, SIGNAL(clicked()), m_chartDataColorConstructionButtonSignalMapper, SLOT(map())); m_chartDataColorConstructionButtonSignalMapper->setMapping(constructionToolButton, i); /* * Color */ CaretColorEnumComboBox* colorComboBox = new CaretColorEnumComboBox(this); QObject::connect(colorComboBox, SIGNAL(colorSelected(const CaretColorEnum::Enum)), m_chartDataColorComboBoxesSignalMapper, SLOT(map())); m_chartDataColorComboBoxesSignalMapper->setMapping(colorComboBox, i); m_chartDataColorComboBoxes.push_back(colorComboBox); /* * Label */ m_chartDataNameLabels.push_back(new QLabel()); /* * Layout */ const int widgetIndex = static_cast(m_chartDataCheckBoxes.size()) - 1; CaretAssertVectorIndex(m_chartDataCheckBoxes, widgetIndex); const int row = m_chartDataGridLayout->rowCount(); m_chartDataGridLayout->addWidget(m_chartDataCheckBoxes[widgetIndex], row, COLUMN_CHART_DATA_CHECKBOX); m_chartDataGridLayout->addWidget(constructionToolButton, row, COLUMN_CHART_DATA_CONSTRUCTION); m_chartDataGridLayout->addWidget(m_chartDataColorComboBoxes[widgetIndex]->getWidget(), row, COLUMN_CHART_DATA_COLOR); switch (m_orientation) { case Qt::Horizontal: { m_chartDataGridLayout->addWidget(m_chartDataNameLabels[widgetIndex], row, COLUMN_CHART_DATA_NAME); } break; case Qt::Vertical: { const int nextRow = m_chartDataGridLayout->rowCount(); m_chartDataGridLayout->addWidget(m_chartDataNameLabels[widgetIndex], nextRow, COLUMN_CHART_DATA_CHECKBOX, 1, COLUMN_COUNT, Qt::AlignLeft); } break; } } CaretAssertVectorIndex(m_chartDataCheckBoxes, i); CaretAssertVectorIndex(m_chartDataContructionToolButtons, i); CaretAssertVectorIndex(m_chartDataColorComboBoxes, i); CaretAssertVectorIndex(m_chartDataNameLabels, i); QCheckBox* checkBox = m_chartDataCheckBoxes[i]; QToolButton* constructToolButton = m_chartDataContructionToolButtons[i]; CaretColorEnumComboBox* colorComboBox = m_chartDataColorComboBoxes[i]; QLabel* nameLabel = m_chartDataNameLabels[i]; bool showRowFlag = false; if (i < numData) { showRowFlag = true; const ChartData* chartData = chartDataVector[i]; const ChartDataCartesian* chartDataCartesian = dynamic_cast(chartData); const ChartDataSource* dataSource = chartData->getChartDataSource(); checkBox->setChecked(chartData->isSelected(tabIndex)); if (chartDataCartesian != NULL) { colorComboBox->setSelectedColor(chartDataCartesian->getColor()); colorComboBox->getWidget()->setEnabled(true); } else { colorComboBox->getWidget()->setEnabled(false); } nameLabel->setText(dataSource->getDescription()); } checkBox->setVisible(showRowFlag); constructToolButton->setVisible(showRowFlag); colorComboBox->getWidget()->setVisible(showRowFlag); nameLabel->setVisible(showRowFlag); } /* * Update averaging. */ bool enableAvergeWidgetsFlag = false; if (chartModel != NULL) { if (chartModel->isAverageChartDisplaySupported()) { enableAvergeWidgetsFlag = true; m_averageCheckBox->setChecked(chartModel->isAverageChartDisplaySelected()); } } m_averageCheckBox->setEnabled(enableAvergeWidgetsFlag); } /** * Get the chart model and the selected tab. * * @param chartModelOut * Output containing chart model (may be NULL). * @param tabIndexOut * Output containing tab index (negative if invalid). */ void ChartHistoryViewController::getSelectedChartModelAndTab(ChartModel* &chartModelOut, int32_t& tabIndexOut) { chartModelOut = NULL; tabIndexOut = -1; Brain* brain = GuiManager::get()->getBrain(); BrowserTabContent* browserTabContent = GuiManager::get()->getBrowserTabContentForBrowserWindow(m_browserWindowIndex, true); if (browserTabContent == NULL) { return; } tabIndexOut = browserTabContent->getTabNumber(); ModelChart* modelChart = brain->getChartModel(); if (modelChart != NULL) { switch (modelChart->getSelectedChartDataType(tabIndexOut)) { 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: chartModelOut = modelChart->getSelectedDataSeriesChartModel(tabIndexOut); break; case ChartDataTypeEnum::CHART_DATA_TYPE_LINE_FREQUENCY_SERIES: chartModelOut = modelChart->getSelectedFrequencySeriesChartModel(tabIndexOut); break; case ChartDataTypeEnum::CHART_DATA_TYPE_LINE_TIME_SERIES: chartModelOut = modelChart->getSelectedTimeSeriesChartModel(tabIndexOut); break; } } } /** * Called when a check box is changed. * * @param indx * Index of checkbox. */ void ChartHistoryViewController::chartDataCheckBoxSignalMapped(int indx) { CaretAssertVectorIndex(m_chartDataCheckBoxes, indx); ChartModel* chartModel = NULL; int32_t tabIndex = -1; getSelectedChartModelAndTab(chartModel, tabIndex); if (chartModel == NULL) { return; } std::vector chartDataVector = chartModel->getAllChartDatas(); CaretAssertVectorIndex(chartDataVector, indx); chartDataVector[indx]->setSelected(tabIndex, m_chartDataCheckBoxes[indx]->isChecked()); updateAfterSelectionsChanged(); } /** * Called when a check box is changed. * * @param indx * Index of checkbox. */ void ChartHistoryViewController::chartDataColorComboBoxSignalMapped(int indx) { CaretAssertVectorIndex(m_chartDataColorComboBoxes, indx); ChartModel* chartModel = NULL; int32_t tabIndex = -1; getSelectedChartModelAndTab(chartModel, tabIndex); if (chartModel == NULL) { return; } std::vector chartDataVector = chartModel->getAllChartDatas(); CaretAssertVectorIndex(chartDataVector, indx); ChartDataCartesian* chartDataCartesian = dynamic_cast(chartDataVector[indx]); if (chartDataCartesian != NULL) { chartDataCartesian->setColor(m_chartDataColorComboBoxes[indx]->getSelectedColor()); } updateAfterSelectionsChanged(); } /** * Called when construction tool button is clicked * * @param indx * Index of tool button. */ void ChartHistoryViewController::chartDataConstructionToolButtonSignalMapped(int indx) { CaretAssertVectorIndex(m_chartDataContructionToolButtons, indx); ChartModel* chartModel = NULL; int32_t tabIndex = -1; getSelectedChartModelAndTab(chartModel, tabIndex); if (chartModel == NULL) { return; } std::vector chartDataVector = chartModel->getAllChartDatas(); const int32_t numCharts = static_cast(chartDataVector.size()); QMenu menu(m_chartDataContructionToolButtons[indx]); QAction* moveUpAction = menu.addAction("Move Chart Up"); if (indx <= 0) { moveUpAction->setEnabled(false); } QAction* moveDownAction = menu.addAction("Move Chart Down"); if (indx >= (numCharts - 1)) { moveDownAction->setEnabled(false); } QAction* removeAction = menu.addAction("Remove Chart"); QAction* selectedAction = menu.exec(m_chartDataContructionToolButtons[indx]->mapToGlobal(QPoint(0,0))); if (selectedAction != NULL) { if (selectedAction == moveUpAction) { chartModel->moveChartDataAtIndexToOneLowerIndex(indx); } else if (selectedAction == moveDownAction) { chartModel->moveChartDataAtIndexToOneHigherIndex(indx); } else if (selectedAction == removeAction) { chartModel->removeChartAtIndex(indx); } else { CaretAssertMessage(0, "Has a new action been added but not processed?"); } this->updateAfterSelectionsChanged(); } } /** * Receive an event. * * @param event * An event for which this instance is listening. */ void ChartHistoryViewController::receiveEvent(Event* event) { if (event->getEventType() == EventTypeEnum::EVENT_USER_INTERFACE_UPDATE) { EventUserInterfaceUpdate* uiEvent = dynamic_cast(event); CaretAssert(uiEvent); if (uiEvent->isUpdateForWindow(m_browserWindowIndex) || uiEvent->isToolBoxUpdate()) { this->updateHistoryViewController(); uiEvent->setEventProcessed(); } } } connectome-workbench-1.2.3+git41-gc4c6c90/src/GuiQt/ChartHistoryViewController.h000066400000000000000000000100111300200146000272610ustar00rootroot00000000000000#ifndef __CHART_HISTORY_VIEW_CONTROLLER_H__ #define __CHART_HISTORY_VIEW_CONTROLLER_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 "EventListenerInterface.h" class QCheckBox; class QGridLayout; class QLabel; class QMenu; class QSignalMapper; class QSpinBox; class QToolButton; namespace caret { class CaretColorEnumComboBox; class ChartModel; class ChartHistoryViewController : public QWidget, public EventListenerInterface { Q_OBJECT public: ChartHistoryViewController(const Qt::Orientation orientation, const int32_t browserWindowIndex, QWidget* parent); virtual ~ChartHistoryViewController(); private: ChartHistoryViewController(const ChartHistoryViewController&); ChartHistoryViewController& operator=(const ChartHistoryViewController&); public: // ADD_NEW_METHODS_HERE virtual void receiveEvent(Event* event); private slots: void averageCheckBoxClicked(bool); void clearPushButtonClicked(); void maximumDisplayedSpinBoxValueChanged(int); void chartDataCheckBoxSignalMapped(int); void chartDataColorComboBoxSignalMapped(int); void chartDataConstructionToolButtonSignalMapped(int); private: // ADD_NEW_MEMBERS_HERE void updateAfterSelectionsChanged(); void updateHistoryViewController(); void getSelectedChartModelAndTab(ChartModel* &chartModelOut, int32_t& tabIndexOut); const Qt::Orientation m_orientation; const int32_t m_browserWindowIndex; QCheckBox* m_averageCheckBox; QSpinBox* m_maximumDisplayedSpinBox; QGridLayout* m_chartDataGridLayout; std::vector m_chartDataCheckBoxes; std::vector m_chartDataContructionToolButtons; std::vector m_chartDataColorComboBoxes; std::vector m_chartDataNameLabels; QSignalMapper* m_chartDataCheckBoxesSignalMapper; QSignalMapper* m_chartDataColorComboBoxesSignalMapper; QSignalMapper* m_chartDataColorConstructionButtonSignalMapper; static const int32_t COLUMN_CHART_DATA_CHECKBOX; static const int32_t COLUMN_CHART_DATA_CONSTRUCTION; static const int32_t COLUMN_CHART_DATA_COLOR; static const int32_t COLUMN_CHART_DATA_NAME; static const int32_t COLUMN_COUNT; }; #ifdef __CHART_HISTORY_VIEW_CONTROLLER_DECLARE__ const int32_t ChartHistoryViewController::COLUMN_CHART_DATA_CHECKBOX = 0; const int32_t ChartHistoryViewController::COLUMN_CHART_DATA_CONSTRUCTION = 1; const int32_t ChartHistoryViewController::COLUMN_CHART_DATA_COLOR = 2; const int32_t ChartHistoryViewController::COLUMN_CHART_DATA_NAME = 3; const int32_t ChartHistoryViewController::COLUMN_COUNT = 4; #endif // __CHART_HISTORY_VIEW_CONTROLLER_DECLARE__ } // namespace #endif //__CHART_HISTORY_VIEW_CONTROLLER_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/GuiQt/ChartLinesSelectionViewController.cxx000066400000000000000000000341501300200146000311250ustar00rootroot00000000000000 /*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_LINES_SELECTION_VIEW_CONTROLLER_DECLARE__ #include "ChartLinesSelectionViewController.h" #undef __CHART_LINES_SELECTION_VIEW_CONTROLLER_DECLARE__ #include #include #include #include #include #include #include #include #include "Brain.h" #include "BrowserTabContent.h" #include "CaretAssert.h" #include "CaretLogger.h" #include "CaretMappableDataFile.h" #include "ChartModel.h" #include "ChartableLineSeriesBrainordinateInterface.h" #include "ChartableMatrixSeriesInterface.h" #include "CiftiMappableDataFile.h" #include "CiftiParcelLabelFile.h" #include "EventManager.h" #include "EventGraphicsUpdateAllWindows.h" #include "EventGraphicsUpdateOneWindow.h" #include "EventPaletteColorMappingEditorDialogRequest.h" #include "EventSurfaceColoringInvalidate.h" #include "EventUserInterfaceUpdate.h" #include "GuiManager.h" #include "MapYokingGroupComboBox.h" #include "ModelChart.h" #include "WuQFactory.h" #include "WuQMessageBox.h" #include "WuQtUtilities.h" using namespace caret; static const char* BRAINORDINATE_FILE_POINTER_PROPERTY_NAME = "brainordinateFilePointer"; /** * \class caret::ChartLinesSelectionViewController * \brief Handles selection of charts displayed in chart model. * \ingroup GuiQt */ /** * Constructor. */ ChartLinesSelectionViewController::ChartLinesSelectionViewController(const Qt::Orientation /*orientation */, const int32_t browserWindowIndex, QWidget* parent) : QWidget(parent), m_browserWindowIndex(browserWindowIndex) { /* * In the grid layout, there are columns for the checkboxes (used * for brainordinate charts) and radio buttons (used for matrix * charts). Display of checkboxes and radiobuttons is mutually * exclusive. The "Select" column title is over both the checkbox * and radio button columns. */ m_brainordinateGridLayout = new QGridLayout(); WuQtUtilities::setLayoutSpacingAndMargins(m_brainordinateGridLayout, 4, 2); m_brainordinateGridLayout->setColumnStretch(BRAINORDINATE_COLUMN_CHECKBOX, 0); m_brainordinateGridLayout->setColumnStretch(BRAINORDINATE_COLUMN_YOKING_COMBO_BOX, 0); m_brainordinateGridLayout->setColumnStretch(BRAINORDINATE_COLUMN_LINE_EDIT, 100); const int titleRow = m_brainordinateGridLayout->rowCount(); m_brainordinateGridLayout->addWidget(new QLabel("Select"), titleRow, BRAINORDINATE_COLUMN_CHECKBOX, Qt::AlignHCenter); m_brainordinateGridLayout->addWidget(new QLabel("Yoke"), titleRow, BRAINORDINATE_COLUMN_YOKING_COMBO_BOX, Qt::AlignHCenter); m_brainordinateGridLayout->addWidget(new QLabel("Charting File"), titleRow, BRAINORDINATE_COLUMN_LINE_EDIT, Qt::AlignHCenter); m_signalMapperBrainordinateFileEnableCheckBox = new QSignalMapper(this); QObject::connect(m_signalMapperBrainordinateFileEnableCheckBox, SIGNAL(mapped(int)), this, SLOT(brainordinateSelectionCheckBoxClicked(int))); m_signalMapperBrainordinateYokingComboBox = new QSignalMapper(this); QObject::connect(m_signalMapperBrainordinateYokingComboBox, SIGNAL(mapped(int)), this, SLOT(brainordinateYokingComboBoxActivated(int))); EventManager::get()->addEventListener(this, EventTypeEnum::EVENT_USER_INTERFACE_UPDATE); QVBoxLayout* layout = new QVBoxLayout(this); WuQtUtilities::setLayoutSpacingAndMargins(layout, 0, 0); layout->addLayout(m_brainordinateGridLayout); layout->addStretch(); } /** * Destructor. */ ChartLinesSelectionViewController::~ChartLinesSelectionViewController() { EventManager::get()->removeAllEventsFromListener(this); } /** * Update the view controller. */ void ChartLinesSelectionViewController::updateSelectionViewController() { Brain* brain = GuiManager::get()->getBrain(); BrowserTabContent* browserTabContent = GuiManager::get()->getBrowserTabContentForBrowserWindow(m_browserWindowIndex, true); if (browserTabContent == NULL) { return; } const int32_t browserTabIndex = browserTabContent->getTabNumber(); ChartDataTypeEnum::Enum chartDataType = ChartDataTypeEnum::CHART_DATA_TYPE_INVALID; ModelChart* modelChart = brain->getChartModel(); if (modelChart != NULL) { chartDataType = modelChart->getSelectedChartDataType(browserTabIndex); } bool validFlag = false; switch (chartDataType) { 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: validFlag = true; break; case ChartDataTypeEnum::CHART_DATA_TYPE_LINE_FREQUENCY_SERIES: validFlag = true; break; case ChartDataTypeEnum::CHART_DATA_TYPE_LINE_TIME_SERIES: validFlag = true; break; } if (validFlag) { std::vector chartableLineSeriesFilesVector; const ChartDataTypeEnum::Enum chartDataType = modelChart->getSelectedChartDataType(browserTabIndex); brain->getAllChartableLineSeriesDataFilesForChartDataType(chartDataType, chartableLineSeriesFilesVector); const int32_t numChartableFiles = static_cast(chartableLineSeriesFilesVector.size()); for (int32_t i = 0; i < numChartableFiles; i++) { QCheckBox* checkBox = NULL; QLineEdit* lineEdit = NULL; MapYokingGroupComboBox* yokeComboBox = NULL; if (i < static_cast(m_fileInfoRows.size())) { checkBox = m_fileInfoRows[i].m_fileEnableCheckBox;// m_brainordinateFileEnableCheckBoxes[i]; lineEdit = m_fileInfoRows[i].m_fileNameLineEdit; // m_brainordinateFileNameLineEdits[i]; yokeComboBox = m_fileInfoRows[i].m_fileYokingComboBox; // m_brainordinateSeriesYokingComboBoxes[i]; } else { checkBox = new QCheckBox(""); QObject::connect(checkBox, SIGNAL(clicked(bool)), m_signalMapperBrainordinateFileEnableCheckBox, SLOT(map())); m_signalMapperBrainordinateFileEnableCheckBox->setMapping(checkBox, i); yokeComboBox = new MapYokingGroupComboBox(this); yokeComboBox->getWidget()->setStatusTip("Synchronize enabled status and map indices)"); yokeComboBox->getWidget()->setToolTip("Yoke to Overlay Mapped Files"); #ifdef CARET_OS_MACOSX yokeComboBox->getWidget()->setFixedWidth(yokeComboBox->getWidget()->sizeHint().width() - 20); #endif // CARET_OS_MACOSX QObject::connect(yokeComboBox, SIGNAL(itemActivated()), m_signalMapperBrainordinateYokingComboBox, SLOT(map())); m_signalMapperBrainordinateYokingComboBox->setMapping(yokeComboBox, i); lineEdit = new QLineEdit(); lineEdit->setReadOnly(true); FileInfoRow rowInfo; rowInfo.m_fileEnableCheckBox = checkBox; rowInfo.m_fileNameLineEdit = lineEdit; rowInfo.m_fileYokingComboBox = yokeComboBox; rowInfo.m_lineSeriesFile = NULL; m_fileInfoRows.push_back(rowInfo); const int row = m_brainordinateGridLayout->rowCount(); m_brainordinateGridLayout->addWidget(checkBox, row, BRAINORDINATE_COLUMN_CHECKBOX, Qt::AlignHCenter); m_brainordinateGridLayout->addWidget(yokeComboBox->getWidget(), row, BRAINORDINATE_COLUMN_YOKING_COMBO_BOX, Qt::AlignHCenter); m_brainordinateGridLayout->addWidget(lineEdit, row, BRAINORDINATE_COLUMN_LINE_EDIT); } CaretAssertVectorIndex(chartableLineSeriesFilesVector, i); ChartableLineSeriesInterface* chartBrainFile = chartableLineSeriesFilesVector[i]; CaretAssert(chartBrainFile); m_fileInfoRows[i].m_lineSeriesFile = chartBrainFile; const bool checkBoxStatus = chartBrainFile->isLineSeriesChartingEnabled(browserTabIndex); QVariant brainordinateFilePointerVariant = qVariantFromValue((void*)chartBrainFile); CaretMappableDataFile* caretMappableDataFile = chartBrainFile->getLineSeriesChartCaretMappableDataFile(); checkBox->blockSignals(true); checkBox->setChecked(checkBoxStatus); checkBox->blockSignals(false); checkBox->setProperty(BRAINORDINATE_FILE_POINTER_PROPERTY_NAME, brainordinateFilePointerVariant); /* * Could be line chart for matrix series file */ ChartableMatrixSeriesInterface* matrixFile = dynamic_cast(caretMappableDataFile); if (matrixFile != NULL) { yokeComboBox->getWidget()->setEnabled(true); yokeComboBox->setMapYokingGroup(matrixFile->getMatrixRowColumnMapYokingGroup(browserTabIndex)); } else { yokeComboBox->getWidget()->setEnabled(false); } CaretAssert(caretMappableDataFile); lineEdit->setText(caretMappableDataFile->getFileName()); } const int32_t numItems = static_cast(m_fileInfoRows.size()); for (int32_t i = 0; i < numItems; i++) { bool showCheckBox = false; bool showYokeComboBox = false; bool showLineEdit = false; if (i < numChartableFiles) { showLineEdit = true; showYokeComboBox = true; showCheckBox = true; } else { m_fileInfoRows[i].m_lineSeriesFile = NULL; } m_fileInfoRows[i].m_fileEnableCheckBox->setVisible(showCheckBox); m_fileInfoRows[i].m_fileNameLineEdit->setVisible(showLineEdit); m_fileInfoRows[i].m_fileYokingComboBox->getWidget()->setVisible(showYokeComboBox); } } } /** * Called when a brainordinate yoking combo box changes. * * @param indx * Index of yoking combo box that was clicked. */ void ChartLinesSelectionViewController::brainordinateYokingComboBoxActivated(int indx) { CaretAssertVectorIndex(m_fileInfoRows, indx); ChartableMatrixSeriesInterface* matrixFile = dynamic_cast(m_fileInfoRows[indx].m_lineSeriesFile); if (matrixFile != NULL) { BrowserTabContent* browserTabContent = GuiManager::get()->getBrowserTabContentForBrowserWindow(m_browserWindowIndex, true); if (browserTabContent == NULL) { return; } const int32_t browserTabIndex = browserTabContent->getTabNumber(); matrixFile->setMatrixRowColumnMapYokingGroup(browserTabIndex, m_fileInfoRows[indx].m_fileYokingComboBox->getMapYokingGroup()); } } /** * Called when a brainordinate enabled check box changes state. * * @param indx * Index of checkbox that was clicked. */ void ChartLinesSelectionViewController::brainordinateSelectionCheckBoxClicked(int indx) { CaretAssertVectorIndex(m_fileInfoRows, indx); BrowserTabContent* browserTabContent = GuiManager::get()->getBrowserTabContentForBrowserWindow(m_browserWindowIndex, true); if (browserTabContent == NULL) { return; } const int32_t browserTabIndex = browserTabContent->getTabNumber(); const bool newStatus = m_fileInfoRows[indx].m_fileEnableCheckBox->isChecked(); if (m_fileInfoRows[indx].m_lineSeriesFile != NULL) { m_fileInfoRows[indx].m_lineSeriesFile->setLineSeriesChartingEnabled(browserTabIndex, newStatus); } } /** * Receive an event. * * @param event * An event for which this instance is listening. */ void ChartLinesSelectionViewController::receiveEvent(Event* event) { if (event->getEventType() == EventTypeEnum::EVENT_USER_INTERFACE_UPDATE) { EventUserInterfaceUpdate* uiEvent = dynamic_cast(event); CaretAssert(uiEvent); if (uiEvent->isUpdateForWindow(m_browserWindowIndex) || uiEvent->isToolBoxUpdate()) { this->updateSelectionViewController(); uiEvent->setEventProcessed(); } } } connectome-workbench-1.2.3+git41-gc4c6c90/src/GuiQt/ChartLinesSelectionViewController.h000066400000000000000000000066711300200146000305610ustar00rootroot00000000000000#ifndef __CHART_LINES_SELECTION_VIEW_CONTROLLER_H__ #define __CHART_LINES_SELECTION_VIEW_CONTROLLER_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 "EventListenerInterface.h" class QCheckBox; class QGridLayout; class QLineEdit; class QSignalMapper; namespace caret { class Brain; class ChartableLineSeriesInterface; class ChartableLineSeriesBrainordinateInterface; class MapYokingGroupComboBox; class ModelChart; class ChartLinesSelectionViewController : public QWidget, public EventListenerInterface { Q_OBJECT public: ChartLinesSelectionViewController(const Qt::Orientation orientation, const int32_t browserWindowIndex, QWidget* parent); virtual ~ChartLinesSelectionViewController(); private slots: void brainordinateSelectionCheckBoxClicked(int); void brainordinateYokingComboBoxActivated(int); private: ChartLinesSelectionViewController(const ChartLinesSelectionViewController&); ChartLinesSelectionViewController& operator=(const ChartLinesSelectionViewController&); public: // ADD_NEW_METHODS_HERE virtual void receiveEvent(Event* event); private: // ADD_NEW_MEMBERS_HERE void updateSelectionViewController(); const int32_t m_browserWindowIndex; struct FileInfoRow { QLineEdit* m_fileNameLineEdit; QCheckBox* m_fileEnableCheckBox; MapYokingGroupComboBox* m_fileYokingComboBox; ChartableLineSeriesInterface* m_lineSeriesFile; }; std::vector m_fileInfoRows; QGridLayout* m_brainordinateGridLayout; QSignalMapper* m_signalMapperBrainordinateFileEnableCheckBox; QSignalMapper* m_signalMapperBrainordinateYokingComboBox; static const int BRAINORDINATE_COLUMN_CHECKBOX; static const int BRAINORDINATE_COLUMN_YOKING_COMBO_BOX; static const int BRAINORDINATE_COLUMN_LINE_EDIT; }; #ifdef __CHART_LINES_SELECTION_VIEW_CONTROLLER_DECLARE__ const int ChartLinesSelectionViewController::BRAINORDINATE_COLUMN_CHECKBOX = 0; const int ChartLinesSelectionViewController::BRAINORDINATE_COLUMN_YOKING_COMBO_BOX = 1; const int ChartLinesSelectionViewController::BRAINORDINATE_COLUMN_LINE_EDIT = 2; #endif // __CHART_LINES_SELECTION_VIEW_CONTROLLER_DECLARE__ } // namespace #endif //__CHART_LINES_SELECTION_VIEW_CONTROLLER_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/GuiQt/ChartMatrixParcelSelectionViewController.cxx000066400000000000000000001372451300200146000324570ustar00rootroot00000000000000 /*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_MATRIX_PARCEL_SELECTION_VIEW_CONTROLLER_DECLARE__ #include "ChartMatrixParcelSelectionViewController.h" #undef __CHART_MATRIX_PARCEL_SELECTION_VIEW_CONTROLLER_DECLARE__ #include #include #include #include #include #include #include #include "AnnotationColorBar.h" #include "Brain.h" #include "BrowserTabContent.h" #include "CaretAssert.h" #include "CaretDataFileSelectionComboBox.h" #include "CaretDataFileSelectionModel.h" #include "CaretLogger.h" #include "CaretMappableDataFile.h" #include "CaretMappableDataFileAndMapSelectionModel.h" #include "CaretMappableDataFileAndMapSelectorObject.h" #include "ChartableMatrixInterface.h" #include "ChartMatrixDisplayProperties.h" #include "ChartMatrixLoadingDimensionEnum.h" #include "ChartableMatrixSeriesInterface.h" #include "ChartModel.h" #include "CiftiMappableConnectivityMatrixDataFile.h" #include "CiftiMappableDataFile.h" #include "CiftiParcelLabelFile.h" #include "DeveloperFlagsEnum.h" #include "EnumComboBoxTemplate.h" #include "EventChartMatrixParcelYokingValidation.h" #include "EventManager.h" #include "EventGraphicsUpdateAllWindows.h" #include "EventGraphicsUpdateOneWindow.h" #include "EventPaletteColorMappingEditorDialogRequest.h" #include "EventSurfaceColoringInvalidate.h" #include "EventUserInterfaceUpdate.h" #include "GuiManager.h" #include "ModelChart.h" #include "WuQFactory.h" #include "WuQMessageBox.h" #include "WuQtUtilities.h" using namespace caret; /** * \class caret::ChartMatrixParcelSelectionViewController * \brief Handles selection of charts displayed in chart model. * \ingroup GuiQt */ /** * Constructor. */ ChartMatrixParcelSelectionViewController::ChartMatrixParcelSelectionViewController(const Qt::Orientation orientation, const int32_t browserWindowIndex, QWidget* parent) : QWidget(parent), m_browserWindowIndex(browserWindowIndex) { m_matrixParcelChartWidget = createMatrixParcelChartWidget(orientation); m_parcelRemappingGroupBox = createParcelRemappingWidget(orientation); QVBoxLayout* layout = new QVBoxLayout(this); WuQtUtilities::setLayoutSpacingAndMargins(layout, 1, 0); layout->addWidget(m_matrixParcelChartWidget); layout->addWidget(m_parcelRemappingGroupBox); //layout->addStretch(); EventManager::get()->addEventListener(this, EventTypeEnum::EVENT_USER_INTERFACE_UPDATE); // /* // * ColorBar Tool Button // */ // QIcon colorBarIcon; // const bool colorBarIconValid = WuQtUtilities::loadIcon(":/LayersPanel/colorbar.png", // colorBarIcon); // m_matrixParcelColorBarAction = WuQtUtilities::createAction("CB", // "Display color bar for this overlay", // this, // this, // SLOT(matrixParcelColorBarActionTriggered(bool))); // m_matrixParcelColorBarAction->setCheckable(true); // if (colorBarIconValid) { // m_matrixParcelColorBarAction->setIcon(colorBarIcon); // } // QToolButton* colorBarToolButton = new QToolButton(); // colorBarToolButton->setDefaultAction(m_matrixParcelColorBarAction); // // /* // * Settings Tool Button // */ // QLabel* settingsLabel = new QLabel("Settings"); // QIcon settingsIcon; // const bool settingsIconValid = WuQtUtilities::loadIcon(":/LayersPanel/wrench.png", // settingsIcon); // // m_matrixParcelSettingsAction = WuQtUtilities::createAction("S", // "Edit settings for this map and overlay", // this, // this, // SLOT(matrixParcelSettingsActionTriggered())); // if (settingsIconValid) { // m_matrixParcelSettingsAction->setIcon(settingsIcon); // } // QToolButton* settingsToolButton = new QToolButton(); // settingsToolButton->setDefaultAction(m_matrixParcelSettingsAction); // // // QLabel* fileLabel = new QLabel("Matrix File"); // m_matrixParcelFileSelectionComboBox = new CaretDataFileSelectionComboBox(this); // QObject::connect(m_matrixParcelFileSelectionComboBox, SIGNAL(fileSelected(CaretDataFile*)), // this, SLOT(matrixParcelFileSelected(CaretDataFile*))); // // QLabel* loadDimensionLabel = new QLabel("Load By"); // m_matrixParcelLoadByColumnRowComboBox = new EnumComboBoxTemplate(this); // m_matrixParcelLoadByColumnRowComboBox->setup(); // QObject::connect(m_matrixParcelLoadByColumnRowComboBox, SIGNAL(itemActivated()), // this, SLOT(matrixParcelFileLoadingComboBoxActivated())); // // // QLabel* yokeLabel = new QLabel("Yoke "); // m_matrixParcelYokingGroupComboBox = new EnumComboBoxTemplate(this); // m_matrixParcelYokingGroupComboBox->setup(); // QObject::connect(m_matrixParcelYokingGroupComboBox, SIGNAL(itemActivated()), // this, SLOT(matrixParcelYokingGroupEnumComboBoxActivated())); // // m_matrixParcelChartWidget = new QGroupBox("Matrix Loading"); // QGridLayout* matrixLayout = new QGridLayout(m_matrixParcelChartWidget); // // switch (orientation) { // case Qt::Horizontal: // { // WuQtUtilities::setLayoutSpacingAndMargins(matrixLayout, 2, 0); // matrixLayout->setColumnStretch(0, 0); // matrixLayout->setColumnStretch(1, 0); // matrixLayout->setColumnStretch(2, 0); // matrixLayout->setColumnStretch(3, 0); // matrixLayout->setColumnStretch(4, 100); // // matrixLayout->addWidget(loadDimensionLabel, // 0, 0, // Qt::AlignHCenter); // matrixLayout->addWidget(settingsLabel, // 0, 1, // 1, 2, // Qt::AlignHCenter); // matrixLayout->addWidget(yokeLabel, // 0, 3, // Qt::AlignHCenter); // matrixLayout->addWidget(fileLabel, // 0, 4, // Qt::AlignHCenter); // matrixLayout->addWidget(m_matrixParcelLoadByColumnRowComboBox->getWidget(), // 1, 0); // matrixLayout->addWidget(settingsToolButton, // 1, 1); // matrixLayout->addWidget(colorBarToolButton, // 1, 2); // matrixLayout->addWidget(m_matrixParcelYokingGroupComboBox->getWidget(), // 1, 3); // matrixLayout->addWidget(m_matrixParcelFileSelectionComboBox->getWidget(), // 1, 4); // } // break; // case Qt::Vertical: // { // WuQtUtilities::setLayoutSpacingAndMargins(matrixLayout, 2, 0); // matrixLayout->setColumnStretch(0, 0); // matrixLayout->setColumnStretch(1, 0); // matrixLayout->setColumnStretch(2, 0); // matrixLayout->setColumnStretch(3, 0); // matrixLayout->setColumnStretch(4, 100); // // matrixLayout->addWidget(loadDimensionLabel, // 0, 0, // Qt::AlignHCenter); // matrixLayout->addWidget(settingsLabel, // 0, 1, // 1, 2, // Qt::AlignHCenter); // matrixLayout->addWidget(yokeLabel, // 0, 3, // Qt::AlignHCenter); // matrixLayout->addWidget(m_matrixParcelLoadByColumnRowComboBox->getWidget(), // 1, 0); // matrixLayout->addWidget(settingsToolButton, // 1, 1); // matrixLayout->addWidget(colorBarToolButton, // 1, 2); // matrixLayout->addWidget(m_matrixParcelYokingGroupComboBox->getWidget(), // 1, 3); // matrixLayout->addWidget(fileLabel, // 2, 0, 1, 4, // Qt::AlignHCenter); // matrixLayout->addWidget(m_matrixParcelFileSelectionComboBox->getWidget(), // 3, 0, 1, 4); // } // break; // default: // CaretAssert(0); // break; // } // // m_parcelReorderingEnabledCheckBox = new QCheckBox(""); // QObject::connect(m_parcelReorderingEnabledCheckBox, SIGNAL(clicked(bool)), // this, SLOT(parcelLabelFileRemappingFileSelectorChanged())); // // m_parcelLabelFileRemappingFileSelector = new CaretMappableDataFileAndMapSelectorObject(DataFileTypeEnum::CONNECTIVITY_PARCEL_LABEL, // CaretMappableDataFileAndMapSelectorObject::OPTION_SHOW_MAP_INDEX_SPIN_BOX, // this); // QObject::connect(m_parcelLabelFileRemappingFileSelector, SIGNAL(selectionWasPerformed()), // this, SLOT(parcelLabelFileRemappingFileSelectorChanged())); // // QLabel* parcelCheckBoxLabel = new QLabel("On"); // QLabel* parcelFileLabel = new QLabel("Parcel Label File"); // QLabel* parcelFileMapLabel = new QLabel("Map"); // QLabel* parcelFileMapIndexLabel = new QLabel("Index"); // QWidget* mapFileComboBox = NULL; // QWidget* mapIndexSpinBox = NULL; // QWidget* mapNameComboBox = NULL; // m_parcelLabelFileRemappingFileSelector->getWidgetsForAddingToLayout(mapFileComboBox, // mapIndexSpinBox, // mapNameComboBox); // m_parcelRemappingGroupBox = new QGroupBox("Parcel Reordering"); // m_parcelRemappingGroupBox->setFlat(true); // m_parcelRemappingGroupBox->setAlignment(Qt::AlignHCenter); // QGridLayout* parcelMapFileLayout = new QGridLayout(m_parcelRemappingGroupBox); // switch (orientation) { // case Qt::Horizontal: // { // WuQtUtilities::setLayoutSpacingAndMargins(parcelMapFileLayout, 2, 0); // parcelMapFileLayout->setColumnStretch(0, 0); // parcelMapFileLayout->setColumnStretch(1, 100); // parcelMapFileLayout->setColumnStretch(2, 0); // parcelMapFileLayout->setColumnStretch(3, 100); // parcelMapFileLayout->addWidget(parcelCheckBoxLabel, 0, 0, Qt::AlignHCenter); // parcelMapFileLayout->addWidget(parcelFileLabel, 0, 1, Qt::AlignHCenter); // parcelMapFileLayout->addWidget(parcelFileMapLabel, 0, 2, 1, 2, Qt::AlignHCenter); // parcelMapFileLayout->addWidget(m_parcelReorderingEnabledCheckBox, 1,0); // parcelMapFileLayout->addWidget(mapFileComboBox, 1, 1); // parcelMapFileLayout->addWidget(mapIndexSpinBox, 1, 2); // parcelMapFileLayout->addWidget(mapNameComboBox, 1, 3); // } // break; // case Qt::Vertical: // { // WuQtUtilities::setLayoutSpacingAndMargins(parcelMapFileLayout, 2, 0); // parcelMapFileLayout->setColumnStretch(0, 0); // parcelMapFileLayout->setColumnStretch(1, 100); // parcelMapFileLayout->addWidget(parcelCheckBoxLabel, 0, 0, Qt::AlignHCenter); // parcelMapFileLayout->addWidget(parcelFileLabel, 0, 1, Qt::AlignHCenter); // parcelMapFileLayout->addWidget(m_parcelReorderingEnabledCheckBox, 1,0, Qt::AlignHCenter); // parcelMapFileLayout->addWidget(mapFileComboBox, 1, 1); // parcelMapFileLayout->addWidget(parcelFileMapIndexLabel, 2, 0, Qt::AlignHCenter); // parcelMapFileLayout->addWidget(parcelFileMapLabel, 2, 1, Qt::AlignHCenter); // parcelMapFileLayout->addWidget(mapIndexSpinBox, 3, 0); // parcelMapFileLayout->addWidget(mapNameComboBox, 3, 1); // } // break; // default: // CaretAssert(0); // } // // // QWidget* widget = new QWidget(this); // QVBoxLayout* layout = new QVBoxLayout(widget); // WuQtUtilities::setLayoutSpacingAndMargins(layout, 1, 0); // layout->addWidget(m_matrixParcelChartWidget); // layout->addWidget(m_parcelRemappingGroupBox); // //layout->addStretch(); // // /* // * TEMP TODO // * FINISH IMPLEMENTATION OF LOADING AND YOKING // */ // const bool hideLoadControls = false; // const bool hideYokeControls = false; // if (hideLoadControls) { // loadDimensionLabel->hide(); // m_matrixParcelLoadByColumnRowComboBox->getWidget()->hide(); // } // if (hideYokeControls) { // yokeLabel->hide(); // m_matrixParcelYokingGroupComboBox->getWidget()->hide(); // } // // EventManager::get()->addEventListener(this, EventTypeEnum::EVENT_USER_INTERFACE_UPDATE); } /** * Destructor. */ ChartMatrixParcelSelectionViewController::~ChartMatrixParcelSelectionViewController() { EventManager::get()->removeAllEventsFromListener(this); } /** * Update the view controller. */ void ChartMatrixParcelSelectionViewController::updateSelectionViewController() { Brain* brain = GuiManager::get()->getBrain(); BrowserTabContent* browserTabContent = GuiManager::get()->getBrowserTabContentForBrowserWindow(m_browserWindowIndex, true); if (browserTabContent == NULL) { return; } const int32_t browserTabIndex = browserTabContent->getTabNumber(); ChartDataTypeEnum::Enum chartDataType = ChartDataTypeEnum::CHART_DATA_TYPE_INVALID; ModelChart* modelChart = brain->getChartModel(); if (modelChart != NULL) { chartDataType = modelChart->getSelectedChartDataType(browserTabIndex); } if (chartDataType == ChartDataTypeEnum::CHART_DATA_TYPE_MATRIX_LAYER) { updateMatrixParcelChartWidget(brain, modelChart, browserTabIndex); } } /** * Receive an event. * * @param event * An event for which this instance is listening. */ void ChartMatrixParcelSelectionViewController::receiveEvent(Event* event) { if (event->getEventType() == EventTypeEnum::EVENT_USER_INTERFACE_UPDATE) { EventUserInterfaceUpdate* uiEvent = dynamic_cast(event); CaretAssert(uiEvent); if (uiEvent->isUpdateForWindow(m_browserWindowIndex) || uiEvent->isToolBoxUpdate()) { this->updateSelectionViewController(); uiEvent->setEventProcessed(); } } } /** * Called when a matrix file is selected. * * @param caretDataFile * Caret data file that was selected. */ void ChartMatrixParcelSelectionViewController::matrixParcelFileSelected(CaretDataFile* /*caretDataFile*/) { updateSelectionViewController(); EventManager::get()->sendEvent(EventGraphicsUpdateOneWindow(m_browserWindowIndex).getPointer()); } /** * Gets called when matrix loading combo box is changed. */ void ChartMatrixParcelSelectionViewController::matrixParcelFileLoadingComboBoxActivated() { CaretMappableDataFile* caretMappableDataFile = NULL; ChartableMatrixInterface* chartableMatrixInterface = NULL; ChartMatrixDisplayProperties* chartMatrixDisplayProperties = NULL; ChartableMatrixParcelInterface* chartableMatrixParcelInterface = NULL; ChartableMatrixSeriesInterface* chartableMatrixSeriesInterface = NULL; int32_t browserTabIndex = -1; if ( ! getChartMatrixAndProperties(caretMappableDataFile, chartableMatrixInterface, chartableMatrixParcelInterface, chartableMatrixSeriesInterface, chartMatrixDisplayProperties, browserTabIndex)) { return; } CaretAssert(chartableMatrixParcelInterface); chartableMatrixParcelInterface->setMatrixLoadingDimension(m_matrixParcelLoadByColumnRowComboBox->getSelectedItem()); EventManager::get()->sendEvent(EventSurfaceColoringInvalidate().getPointer()); EventManager::get()->sendEvent(EventGraphicsUpdateAllWindows().getPointer()); } /** * Gets called when yoking gruup is changed. */ void ChartMatrixParcelSelectionViewController::matrixParcelYokingGroupEnumComboBoxActivated() { CaretMappableDataFile* caretMappableDataFile = NULL; ChartableMatrixInterface* chartableMatrixInterface = NULL; ChartMatrixDisplayProperties* chartMatrixDisplayProperties = NULL; ChartableMatrixParcelInterface* chartableMatrixParcelInterface = NULL; ChartableMatrixSeriesInterface* chartableMatrixSeriesInterface = NULL; int32_t browserTabIndex = -1; if ( ! getChartMatrixAndProperties(caretMappableDataFile, chartableMatrixInterface, chartableMatrixParcelInterface, chartableMatrixSeriesInterface, chartMatrixDisplayProperties, browserTabIndex)) { return; } CaretAssert(chartableMatrixParcelInterface); YokingGroupEnum::Enum newYokingGroup = m_matrixParcelYokingGroupComboBox->getSelectedItem(); int32_t selectedRowColumnIndex = -1; if (newYokingGroup != YokingGroupEnum::YOKING_GROUP_OFF) { const YokingGroupEnum::Enum previousYokingGroup = chartableMatrixParcelInterface->getYokingGroup(); EventChartMatrixParcelYokingValidation yokeEvent(chartableMatrixParcelInterface, newYokingGroup); EventManager::get()->sendEvent(yokeEvent.getPointer()); AString message; if ( ! yokeEvent.isValidateYokingCompatible(message, selectedRowColumnIndex)) { message = WuQtUtilities::createWordWrappedToolTipText(message); WuQMessageBox::YesNoCancelResult result = WuQMessageBox::warningYesNoCancel(m_matrixParcelYokingGroupComboBox->getWidget(), message, ""); switch (result) { case WuQMessageBox::RESULT_YES: break; case WuQMessageBox::RESULT_NO: newYokingGroup = YokingGroupEnum::YOKING_GROUP_OFF; selectedRowColumnIndex = -1; break; case WuQMessageBox::RESULT_CANCEL: newYokingGroup = previousYokingGroup; selectedRowColumnIndex = -1; break; } } } /* * Need to update combo box since user may have changed mind and * the combo box status needs to change */ m_matrixParcelYokingGroupComboBox->setSelectedItem(newYokingGroup); chartableMatrixParcelInterface->setYokingGroup(newYokingGroup); /* * If yoking changed update the file's selected row or column */ if (newYokingGroup != YokingGroupEnum::YOKING_GROUP_OFF) { if (selectedRowColumnIndex >= 0) { CiftiMappableConnectivityMatrixDataFile* matrixFile = dynamic_cast(chartableMatrixInterface); if (matrixFile != NULL) { switch (chartableMatrixParcelInterface->getMatrixLoadingDimension()) { case ChartMatrixLoadingDimensionEnum::CHART_MATRIX_LOADING_BY_COLUMN: matrixFile->loadDataForColumnIndex(selectedRowColumnIndex); break; case ChartMatrixLoadingDimensionEnum::CHART_MATRIX_LOADING_BY_ROW: matrixFile->loadDataForRowIndex(selectedRowColumnIndex); break; } } } } EventManager::get()->sendEvent(EventSurfaceColoringInvalidate().getPointer()); EventManager::get()->sendEvent(EventGraphicsUpdateAllWindows().getPointer()); } /** * Called when colorbar icon button is clicked. */ void ChartMatrixParcelSelectionViewController::matrixParcelColorBarActionTriggered(bool status) { CaretMappableDataFile* caretMappableDataFile = NULL; ChartableMatrixInterface* chartableMatrixInterface = NULL; ChartMatrixDisplayProperties* chartMatrixDisplayProperties = NULL; ChartableMatrixParcelInterface* chartableMatrixParcelInterface = NULL; ChartableMatrixSeriesInterface* chartableMatrixSeriesInterface = NULL; int32_t browserTabIndex = -1; if ( ! getChartMatrixAndProperties(caretMappableDataFile, chartableMatrixInterface, chartableMatrixParcelInterface, chartableMatrixSeriesInterface, chartMatrixDisplayProperties, browserTabIndex)) { return; } chartMatrixDisplayProperties->getColorBar()->setDisplayed(status); EventManager::get()->sendEvent(EventGraphicsUpdateAllWindows().getPointer()); } /** * Called when settings icon button is clicked to display palette editor. */ void ChartMatrixParcelSelectionViewController::matrixParcelSettingsActionTriggered() { CaretMappableDataFile* caretMappableDataFile = NULL; ChartableMatrixInterface* chartableMatrixInterface = NULL; ChartMatrixDisplayProperties* chartMatrixDisplayProperties = NULL; ChartableMatrixParcelInterface* chartableMatrixParcelInterface = NULL; ChartableMatrixSeriesInterface* chartableMatrixSeriesInterface = NULL; int32_t browserTabIndex = -1; if ( ! getChartMatrixAndProperties(caretMappableDataFile, chartableMatrixInterface, chartableMatrixParcelInterface, chartableMatrixSeriesInterface, chartMatrixDisplayProperties, browserTabIndex)) { return; } const int32_t mapIndex = 0; EventPaletteColorMappingEditorDialogRequest dialogEvent(m_browserWindowIndex, caretMappableDataFile, mapIndex); EventManager::get()->sendEvent(dialogEvent.getPointer()); } /** * @param orientation * Orientation for the widget. * @return * The matrix chart widget. */ QGroupBox* ChartMatrixParcelSelectionViewController::createMatrixParcelChartWidget(const Qt::Orientation orientation) { /* * ColorBar Tool Button */ QIcon colorBarIcon; const bool colorBarIconValid = WuQtUtilities::loadIcon(":/LayersPanel/colorbar.png", colorBarIcon); m_matrixParcelColorBarAction = WuQtUtilities::createAction("CB", "Display color bar for this overlay", this, this, SLOT(matrixParcelColorBarActionTriggered(bool))); m_matrixParcelColorBarAction->setCheckable(true); if (colorBarIconValid) { m_matrixParcelColorBarAction->setIcon(colorBarIcon); } QToolButton* colorBarToolButton = new QToolButton(); colorBarToolButton->setDefaultAction(m_matrixParcelColorBarAction); /* * Settings Tool Button */ QLabel* settingsLabel = new QLabel("Settings"); QIcon settingsIcon; const bool settingsIconValid = WuQtUtilities::loadIcon(":/LayersPanel/wrench.png", settingsIcon); m_matrixParcelSettingsAction = WuQtUtilities::createAction("S", "Edit settings for this map and overlay", this, this, SLOT(matrixParcelSettingsActionTriggered())); if (settingsIconValid) { m_matrixParcelSettingsAction->setIcon(settingsIcon); } QToolButton* settingsToolButton = new QToolButton(); settingsToolButton->setDefaultAction(m_matrixParcelSettingsAction); QLabel* fileLabel = new QLabel("Matrix File"); m_matrixParcelFileSelectionComboBox = new CaretDataFileSelectionComboBox(this); QObject::connect(m_matrixParcelFileSelectionComboBox, SIGNAL(fileSelected(CaretDataFile*)), this, SLOT(matrixParcelFileSelected(CaretDataFile*))); QLabel* loadDimensionLabel = new QLabel("Load By"); m_matrixParcelLoadByColumnRowComboBox = new EnumComboBoxTemplate(this); m_matrixParcelLoadByColumnRowComboBox->setup(); QObject::connect(m_matrixParcelLoadByColumnRowComboBox, SIGNAL(itemActivated()), this, SLOT(matrixParcelFileLoadingComboBoxActivated())); QLabel* yokeLabel = new QLabel("Yoke "); m_matrixParcelYokingGroupComboBox = new EnumComboBoxTemplate(this); m_matrixParcelYokingGroupComboBox->setup(); QObject::connect(m_matrixParcelYokingGroupComboBox, SIGNAL(itemActivated()), this, SLOT(matrixParcelYokingGroupEnumComboBoxActivated())); QGroupBox* fileYokeGroupBox = new QGroupBox("Matrix Loading"); fileYokeGroupBox->setFlat(true); fileYokeGroupBox->setAlignment(Qt::AlignHCenter); QGridLayout* fileYokeLayout = new QGridLayout(fileYokeGroupBox); switch (orientation) { case Qt::Horizontal: { WuQtUtilities::setLayoutSpacingAndMargins(fileYokeLayout, 2, 0); fileYokeLayout->setColumnStretch(0, 0); fileYokeLayout->setColumnStretch(1, 0); fileYokeLayout->setColumnStretch(2, 0); fileYokeLayout->setColumnStretch(3, 0); fileYokeLayout->setColumnStretch(4, 100); fileYokeLayout->addWidget(loadDimensionLabel, 0, 0, Qt::AlignHCenter); fileYokeLayout->addWidget(settingsLabel, 0, 1, 1, 2, Qt::AlignHCenter); fileYokeLayout->addWidget(yokeLabel, 0, 3, Qt::AlignHCenter); fileYokeLayout->addWidget(fileLabel, 0, 4, Qt::AlignHCenter); fileYokeLayout->addWidget(m_matrixParcelLoadByColumnRowComboBox->getWidget(), 1, 0); fileYokeLayout->addWidget(settingsToolButton, 1, 1); fileYokeLayout->addWidget(colorBarToolButton, 1, 2); fileYokeLayout->addWidget(m_matrixParcelYokingGroupComboBox->getWidget(), 1, 3); fileYokeLayout->addWidget(m_matrixParcelFileSelectionComboBox->getWidget(), 1, 4); } break; case Qt::Vertical: { WuQtUtilities::setLayoutSpacingAndMargins(fileYokeLayout, 2, 0); fileYokeLayout->setColumnStretch(0, 0); fileYokeLayout->setColumnStretch(1, 0); fileYokeLayout->setColumnStretch(2, 0); fileYokeLayout->setColumnStretch(3, 0); fileYokeLayout->setColumnStretch(4, 100); fileYokeLayout->addWidget(loadDimensionLabel, 0, 0, Qt::AlignHCenter); fileYokeLayout->addWidget(settingsLabel, 0, 1, 1, 2, Qt::AlignHCenter); fileYokeLayout->addWidget(yokeLabel, 0, 3, Qt::AlignHCenter); fileYokeLayout->addWidget(m_matrixParcelLoadByColumnRowComboBox->getWidget(), 1, 0); fileYokeLayout->addWidget(settingsToolButton, 1, 1); fileYokeLayout->addWidget(colorBarToolButton, 1, 2); fileYokeLayout->addWidget(m_matrixParcelYokingGroupComboBox->getWidget(), 1, 3); fileYokeLayout->addWidget(fileLabel, 2, 0, 1, 4, Qt::AlignHCenter); fileYokeLayout->addWidget(m_matrixParcelFileSelectionComboBox->getWidget(), 3, 0, 1, 4); } break; default: CaretAssert(0); break; } return fileYokeGroupBox; } /** * @param orientation * Orientation for the widget. * @return * The parcel remapping widget. */ QGroupBox* ChartMatrixParcelSelectionViewController::createParcelRemappingWidget(const Qt::Orientation orientation) { m_parcelReorderingEnabledCheckBox = new QCheckBox(""); QObject::connect(m_parcelReorderingEnabledCheckBox, SIGNAL(clicked(bool)), this, SLOT(parcelLabelFileRemappingFileSelectorChanged())); m_parcelLabelFileRemappingFileSelector = new CaretMappableDataFileAndMapSelectorObject(DataFileTypeEnum::CONNECTIVITY_PARCEL_LABEL, CaretMappableDataFileAndMapSelectorObject::OPTION_SHOW_MAP_INDEX_SPIN_BOX, this); QObject::connect(m_parcelLabelFileRemappingFileSelector, SIGNAL(selectionWasPerformed()), this, SLOT(parcelLabelFileRemappingFileSelectorChanged())); QLabel* parcelCheckBoxLabel = new QLabel("On"); QLabel* parcelFileLabel = new QLabel("Parcel Label File"); QLabel* parcelFileMapLabel = new QLabel("Map"); QLabel* parcelFileMapIndexLabel = new QLabel("Index"); QWidget* mapFileComboBox = NULL; QWidget* mapIndexSpinBox = NULL; QWidget* mapNameComboBox = NULL; m_parcelLabelFileRemappingFileSelector->getWidgetsForAddingToLayout(mapFileComboBox, mapIndexSpinBox, mapNameComboBox); QGroupBox* groupBox = new QGroupBox("Parcel Reordering"); groupBox->setFlat(true); groupBox->setAlignment(Qt::AlignHCenter); QGridLayout* parcelMapFileLayout = new QGridLayout(groupBox); switch (orientation) { case Qt::Horizontal: { WuQtUtilities::setLayoutSpacingAndMargins(parcelMapFileLayout, 2, 0); parcelMapFileLayout->setColumnStretch(0, 0); parcelMapFileLayout->setColumnStretch(1, 100); parcelMapFileLayout->setColumnStretch(2, 0); parcelMapFileLayout->setColumnStretch(3, 100); parcelMapFileLayout->addWidget(parcelCheckBoxLabel, 0, 0, Qt::AlignHCenter); parcelMapFileLayout->addWidget(parcelFileLabel, 0, 1, Qt::AlignHCenter); parcelMapFileLayout->addWidget(parcelFileMapLabel, 0, 2, 1, 2, Qt::AlignHCenter); parcelMapFileLayout->addWidget(m_parcelReorderingEnabledCheckBox, 1,0); parcelMapFileLayout->addWidget(mapFileComboBox, 1, 1); parcelMapFileLayout->addWidget(mapIndexSpinBox, 1, 2); parcelMapFileLayout->addWidget(mapNameComboBox, 1, 3); } break; case Qt::Vertical: { WuQtUtilities::setLayoutSpacingAndMargins(parcelMapFileLayout, 2, 0); parcelMapFileLayout->setColumnStretch(0, 0); parcelMapFileLayout->setColumnStretch(1, 100); parcelMapFileLayout->addWidget(parcelCheckBoxLabel, 0, 0, Qt::AlignHCenter); parcelMapFileLayout->addWidget(parcelFileLabel, 0, 1, Qt::AlignHCenter); parcelMapFileLayout->addWidget(m_parcelReorderingEnabledCheckBox, 1,0, Qt::AlignHCenter); parcelMapFileLayout->addWidget(mapFileComboBox, 1, 1); parcelMapFileLayout->addWidget(parcelFileMapIndexLabel, 2, 0, Qt::AlignHCenter); parcelMapFileLayout->addWidget(parcelFileMapLabel, 2, 1, Qt::AlignHCenter); parcelMapFileLayout->addWidget(mapIndexSpinBox, 3, 0); parcelMapFileLayout->addWidget(mapNameComboBox, 3, 1); } break; default: CaretAssert(0); } return groupBox; } /** * Get the matrix related files and properties in this view controller. * * @param caretMappableDataFileOut * Output with selected caret mappable data file. * @param chartableMatrixInterfaceOut * Output with ChartableMatrixInterface implemented by the caret * mappable data file. * @param chartableMatrixParcelInterfaceOut * Output with ChartableMatrixParcelInterfaceOut implemented by the * caret mappable data file (may be NULL). * @param chartableMatrixSeriesInterfaceOut * Output with ChartableMatrixSeriesInterfaceOut implemented by the * caret mappable data file (may be NULL). * @param browserTabIndexOut * Index selected tab. * @param chartMatrixDisplayPropertiesOut * Matrix display properties from the ChartableMatrixInterface. * @return True if all output values are valid, else false. */ bool ChartMatrixParcelSelectionViewController::getChartMatrixAndProperties(CaretMappableDataFile* &caretMappableDataFileOut, ChartableMatrixInterface* & chartableMatrixInterfaceOut, ChartableMatrixParcelInterface* &chartableMatrixParcelInterfaceOut, ChartableMatrixSeriesInterface* &chartableMatrixSeriesInterfaceOut, ChartMatrixDisplayProperties* &chartMatrixDisplayPropertiesOut, int32_t& browserTabIndexOut) { caretMappableDataFileOut = NULL; chartableMatrixInterfaceOut = NULL; chartableMatrixParcelInterfaceOut = NULL; chartableMatrixSeriesInterfaceOut = NULL; chartMatrixDisplayPropertiesOut = NULL; browserTabIndexOut = -1; Brain* brain = GuiManager::get()->getBrain(); BrowserTabContent* browserTabContent = GuiManager::get()->getBrowserTabContentForBrowserWindow(m_browserWindowIndex, true); if (browserTabContent == NULL) { return false; } browserTabIndexOut = browserTabContent->getTabNumber(); if (browserTabIndexOut < 0) { return false; } ModelChart* modelChart = brain->getChartModel(); if (modelChart != NULL) { switch (modelChart->getSelectedChartDataType(browserTabIndexOut)) { case ChartDataTypeEnum::CHART_DATA_TYPE_INVALID: break; case ChartDataTypeEnum::CHART_DATA_TYPE_MATRIX_LAYER: { CaretDataFileSelectionModel* parcelFileSelectionModel = modelChart->getChartableMatrixParcelFileSelectionModel(browserTabIndexOut); //m_matrixParcelFileSelectionComboBox->updateComboBox(parcelFileSelectionModel); CaretDataFile* caretParcelFile = parcelFileSelectionModel->getSelectedFile(); if (caretParcelFile != NULL) { chartableMatrixInterfaceOut = dynamic_cast(caretParcelFile); if (chartableMatrixInterfaceOut != NULL) { chartableMatrixParcelInterfaceOut = dynamic_cast(caretParcelFile); chartMatrixDisplayPropertiesOut = chartableMatrixInterfaceOut->getChartMatrixDisplayProperties(browserTabIndexOut); caretMappableDataFileOut = chartableMatrixInterfaceOut->getMatrixChartCaretMappableDataFile(); return true; } } } break; case ChartDataTypeEnum::CHART_DATA_TYPE_MATRIX_SERIES: { CaretDataFileSelectionModel* seriesFileSelectionModel = modelChart->getChartableMatrixSeriesFileSelectionModel(browserTabIndexOut); CaretDataFile* caretSeriesFile = seriesFileSelectionModel->getSelectedFile(); if (caretSeriesFile != NULL) { chartableMatrixInterfaceOut = dynamic_cast(caretSeriesFile); if (chartableMatrixInterfaceOut != NULL) { chartableMatrixSeriesInterfaceOut = dynamic_cast(caretSeriesFile); chartMatrixDisplayPropertiesOut = chartableMatrixInterfaceOut->getChartMatrixDisplayProperties(browserTabIndexOut); caretMappableDataFileOut = chartableMatrixInterfaceOut->getMatrixChartCaretMappableDataFile(); return true; } } } break; case ChartDataTypeEnum::CHART_DATA_TYPE_LINE_DATA_SERIES: break; case ChartDataTypeEnum::CHART_DATA_TYPE_LINE_FREQUENCY_SERIES: break; case ChartDataTypeEnum::CHART_DATA_TYPE_LINE_TIME_SERIES: break; } // CaretDataFileSelectionModel* parcelFileSelectionModel = modelChart->getChartableMatrixParcelFileSelectionModel(browserTabIndexOut); // //m_matrixParcelFileSelectionComboBox->updateComboBox(parcelFileSelectionModel); // CaretDataFile* caretParcelFile = parcelFileSelectionModel->getSelectedFile(); // // CaretDataFileSelectionModel* seriesFileSelectionModel = modelChart->getChartableMatrixSeriesFileSelectionModel(browserTabIndexOut); // CaretDataFile* caretSeriesFile = seriesFileSelectionModel->getSelectedFile(); // // if (caretParcelFile != NULL) { // chartableMatrixInterfaceOut = dynamic_cast(caretParcelFile); // if (chartableMatrixInterfaceOut != NULL) { // chartableMatrixParcelInterfaceOut = dynamic_cast(caretParcelFile); // chartableMatrixSeriesInterfaceOut = dynamic_cast(caretParcelFile); // chartMatrixDisplayPropertiesOut = chartableMatrixInterfaceOut->getChartMatrixDisplayProperties(browserTabIndexOut); // caretMappableDataFileOut = chartableMatrixInterfaceOut->getMatrixChartCaretMappableDataFile(); // return true; // } // } } return false; } /** * Gets called when a change is made in the parcel label file remapping * selections. */ void ChartMatrixParcelSelectionViewController::parcelLabelFileRemappingFileSelectorChanged() { CaretMappableDataFile* caretMappableDataFile = NULL; ChartableMatrixInterface* chartableMatrixInterface = NULL; ChartMatrixDisplayProperties* chartMatrixDisplayProperties = NULL; ChartableMatrixParcelInterface* chartableMatrixParcelInterface = NULL; ChartableMatrixSeriesInterface* chartableMatrixSeriesInterface = NULL; int32_t browserTabIndex = -1; if ( ! getChartMatrixAndProperties(caretMappableDataFile, chartableMatrixInterface, chartableMatrixParcelInterface, chartableMatrixSeriesInterface, chartMatrixDisplayProperties, browserTabIndex)) { return; } CaretAssert(chartableMatrixParcelInterface); const bool remappingEnabled = m_parcelReorderingEnabledCheckBox->isChecked(); CaretMappableDataFileAndMapSelectionModel* model = m_parcelLabelFileRemappingFileSelector->getModel(); CiftiParcelLabelFile* parcelLabelFile = model->getSelectedFileOfType(); int32_t parcelLabelFileMapIndex = model->getSelectedMapIndex(); chartableMatrixParcelInterface->setSelectedParcelLabelFileAndMapForReordering(parcelLabelFile, parcelLabelFileMapIndex, remappingEnabled); if (remappingEnabled) { AString errorMessage; if ( ! chartableMatrixParcelInterface->createParcelReordering(parcelLabelFile, parcelLabelFileMapIndex, errorMessage)) { WuQMessageBox::errorOk(this, errorMessage); } } } /** * Update the matrix chart widget. * * @param brain * The Brain. * @param modelChart * The Model for charts. * @param browserTabIndex * Index of the browser tab. */ void ChartMatrixParcelSelectionViewController::updateMatrixParcelChartWidget(Brain* /* brain */, ModelChart* modelChart, const int32_t /*browserTabIndex*/) { CaretMappableDataFile* caretMappableDataFile = NULL; ChartableMatrixInterface* chartableMatrixInterface = NULL; ChartMatrixDisplayProperties* chartMatrixDisplayProperties = NULL; ChartableMatrixParcelInterface* chartableMatrixParcelInterface = NULL; ChartableMatrixSeriesInterface* chartableMatrixSeriesInterface = NULL; int32_t browserTabIndex = -1; if ( ! getChartMatrixAndProperties(caretMappableDataFile, chartableMatrixInterface, chartableMatrixParcelInterface, chartableMatrixSeriesInterface, chartMatrixDisplayProperties, browserTabIndex)) { return; } if (chartableMatrixParcelInterface != NULL) { CaretDataFileSelectionModel* fileSelectionModel = modelChart->getChartableMatrixParcelFileSelectionModel(browserTabIndex); m_matrixParcelFileSelectionComboBox->updateComboBox(fileSelectionModel); const ChartMatrixLoadingDimensionEnum::Enum loadType = chartableMatrixParcelInterface->getMatrixLoadingDimension(); m_matrixParcelLoadByColumnRowComboBox->setSelectedItem(loadType); const YokingGroupEnum::Enum yokingGroup = chartableMatrixParcelInterface->getYokingGroup(); m_matrixParcelYokingGroupComboBox->setSelectedItem(yokingGroup); m_matrixParcelColorBarAction->blockSignals(true); m_matrixParcelColorBarAction->setChecked(chartMatrixDisplayProperties->getColorBar()->isDisplayed()); m_matrixParcelColorBarAction->blockSignals(false); m_matrixParcelYokingGroupComboBox->getWidget()->setEnabled(chartableMatrixParcelInterface->isSupportsLoadingAttributes()); m_matrixParcelLoadByColumnRowComboBox->getWidget()->setEnabled(chartableMatrixParcelInterface->isSupportsLoadingAttributes()); /* * Update palette reordering. */ std::vector parcelLabelFiles; CiftiParcelLabelFile* parcelLabelFile = NULL; int32_t parcelLabelFileMapIndex = -1; bool remappingEnabled = false; chartableMatrixParcelInterface->getSelectedParcelLabelFileAndMapForReordering(parcelLabelFiles, parcelLabelFile, parcelLabelFileMapIndex, remappingEnabled); std::vector caretMapDataFiles; if ( ! parcelLabelFiles.empty()) { caretMapDataFiles.insert(caretMapDataFiles.end(), parcelLabelFiles.begin(), parcelLabelFiles.end()); } m_parcelReorderingEnabledCheckBox->setChecked(remappingEnabled); CaretMappableDataFileAndMapSelectionModel* model = m_parcelLabelFileRemappingFileSelector->getModel(); model->overrideAvailableDataFiles(caretMapDataFiles); model->setSelectedFile(parcelLabelFile); model->setSelectedMapIndex(parcelLabelFileMapIndex); m_parcelLabelFileRemappingFileSelector->updateFileAndMapSelector(model); bool reorderCheckBoxEnabledFlag = false; if (model->getSelectedFile() != NULL) { if ((model->getSelectedMapIndex() >= 0) && (model->getSelectedMapIndex() < model->getSelectedFile()->getNumberOfMaps())) { reorderCheckBoxEnabledFlag = true; } } m_parcelReorderingEnabledCheckBox->setEnabled(reorderCheckBoxEnabledFlag); m_matrixParcelColorBarAction->setEnabled(caretMappableDataFile->isMappedWithPalette()); m_matrixParcelSettingsAction->setEnabled(caretMappableDataFile->isMappedWithPalette()); m_parcelRemappingGroupBox->setVisible(true); } m_parcelRemappingGroupBox->setEnabled(chartableMatrixParcelInterface != NULL); } connectome-workbench-1.2.3+git41-gc4c6c90/src/GuiQt/ChartMatrixParcelSelectionViewController.h000066400000000000000000000110361300200146000320710ustar00rootroot00000000000000#ifndef __CHART_MATRIX_PARCEL_SELECTION_VIEW_CONTROLLER_H__ #define __CHART_MATRIX_PARCEL_SELECTION_VIEW_CONTROLLER_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 "EventListenerInterface.h" class QCheckBox; class QGroupBox; namespace caret { class Brain; class CaretDataFile; class CaretDataFileSelectionComboBox; class CaretMappableDataFile; class CaretMappableDataFileAndMapSelectorObject; class ChartMatrixDisplayProperties; class ChartableMatrixInterface; class ChartableMatrixParcelInterface; class ChartableMatrixSeriesInterface; class EnumComboBoxTemplate; class ModelChart; class ChartMatrixParcelSelectionViewController : public QWidget, public EventListenerInterface { Q_OBJECT public: ChartMatrixParcelSelectionViewController(const Qt::Orientation orientation, const int32_t browserWindowIndex, QWidget* parent); virtual ~ChartMatrixParcelSelectionViewController(); private slots: void matrixParcelFileSelected(CaretDataFile* caretDataFile); void matrixParcelFileLoadingComboBoxActivated(); void matrixParcelYokingGroupEnumComboBoxActivated(); void matrixParcelColorBarActionTriggered(bool status); void matrixParcelSettingsActionTriggered(); void parcelLabelFileRemappingFileSelectorChanged(); private: ChartMatrixParcelSelectionViewController(const ChartMatrixParcelSelectionViewController&); ChartMatrixParcelSelectionViewController& operator=(const ChartMatrixParcelSelectionViewController&); public: // ADD_NEW_METHODS_HERE virtual void receiveEvent(Event* event); private: // ADD_NEW_MEMBERS_HERE QGroupBox* createMatrixParcelChartWidget(const Qt::Orientation orientation); QGroupBox* createParcelRemappingWidget(const Qt::Orientation orientation); void updateSelectionViewController(); void updateMatrixParcelChartWidget(Brain* brain, ModelChart* modelChart, const int32_t browserTabIndex); bool getChartMatrixAndProperties(CaretMappableDataFile* &caretMappableDataFileOut, ChartableMatrixInterface* & chartableMatrixInterfaceOut, ChartableMatrixParcelInterface* &chartableMatrixParcelInterfaceOut, ChartableMatrixSeriesInterface* &chartableMatrixSeriesInterfaceOut, ChartMatrixDisplayProperties* &chartMatrixDisplayPropertiesOut, int32_t& browserTabIndexOut); QGroupBox* m_matrixParcelChartWidget; const int32_t m_browserWindowIndex; CaretDataFileSelectionComboBox* m_matrixParcelFileSelectionComboBox; EnumComboBoxTemplate* m_matrixParcelLoadByColumnRowComboBox; EnumComboBoxTemplate* m_matrixParcelYokingGroupComboBox; QAction* m_matrixParcelColorBarAction; QAction* m_matrixParcelSettingsAction; QGroupBox* m_parcelRemappingGroupBox; QCheckBox* m_parcelReorderingEnabledCheckBox; CaretMappableDataFileAndMapSelectorObject* m_parcelLabelFileRemappingFileSelector; }; #ifdef __CHART_MATRIX_PARCEL_SELECTION_VIEW_CONTROLLER_DECLARE__ #endif // __CHART_MATRIX_PARCEL_SELECTION_VIEW_CONTROLLER_DECLARE__ } // namespace #endif //__CHART_MATRIX_PARCEL_SELECTION_VIEW_CONTROLLER_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/GuiQt/ChartMatrixSeriesSelectionViewController.cxx000066400000000000000000000524241300200146000324760ustar00rootroot00000000000000 /*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_MATRIX_SERIES_SELECTION_VIEW_CONTROLLER_DECLARE__ #include "ChartMatrixSeriesSelectionViewController.h" #undef __CHART_MATRIX_SERIES_SELECTION_VIEW_CONTROLLER_DECLARE__ #include #include #include #include #include #include #include #include #include #include #include #include #include "AnnotationColorBar.h" #include "Brain.h" #include "BrowserTabContent.h" #include "CaretAssert.h" #include "CaretDataFileSelectionComboBox.h" #include "CaretDataFileSelectionModel.h" #include "CaretLogger.h" #include "CaretMappableDataFile.h" #include "CaretMappableDataFileAndMapSelectionModel.h" #include "CaretMappableDataFileAndMapSelectorObject.h" #include "ChartableMatrixInterface.h" #include "ChartMatrixDisplayProperties.h" #include "ChartMatrixLoadingDimensionEnum.h" #include "ChartModel.h" #include "CiftiMappableConnectivityMatrixDataFile.h" #include "CiftiMappableDataFile.h" #include "CiftiParcelLabelFile.h" #include "CiftiScalarDataSeriesFile.h" #include "DeveloperFlagsEnum.h" #include "EnumComboBoxTemplate.h" #include "EventChartMatrixParcelYokingValidation.h" #include "EventManager.h" #include "EventGraphicsUpdateAllWindows.h" #include "EventGraphicsUpdateOneWindow.h" #include "EventPaletteColorMappingEditorDialogRequest.h" #include "EventSurfaceColoringInvalidate.h" #include "EventUserInterfaceUpdate.h" #include "GuiManager.h" #include "MapYokingGroupComboBox.h" #include "ModelChart.h" #include "WuQFactory.h" #include "WuQMessageBox.h" #include "WuQtUtilities.h" using namespace caret; /** * \class caret::ChartMatrixSeriesSelectionViewController * \brief Handles selection of charts displayed in chart model. * \ingroup GuiQt */ /** * Constructor. */ ChartMatrixSeriesSelectionViewController::ChartMatrixSeriesSelectionViewController(const Qt::Orientation orientation, const int32_t browserWindowIndex, QWidget* parent) : QWidget(parent), m_browserWindowIndex(browserWindowIndex) { /* * ColorBar Tool Button */ QIcon colorBarIcon; const bool colorBarIconValid = WuQtUtilities::loadIcon(":/LayersPanel/colorbar.png", colorBarIcon); m_matrixSeriesColorBarAction = WuQtUtilities::createAction("CB", "Display color bar for this overlay", this, this, SLOT(matrixSeriesColorBarActionTriggered(bool))); m_matrixSeriesColorBarAction->setCheckable(true); if (colorBarIconValid) { m_matrixSeriesColorBarAction->setIcon(colorBarIcon); } QToolButton* colorBarToolButton = new QToolButton(); colorBarToolButton->setDefaultAction(m_matrixSeriesColorBarAction); /* * Settings Tool Button */ QLabel* settingsLabel = new QLabel("Settings"); QIcon settingsIcon; const bool settingsIconValid = WuQtUtilities::loadIcon(":/LayersPanel/wrench.png", settingsIcon); m_matrixSeriesSettingsAction = WuQtUtilities::createAction("S", "Edit settings for this map and overlay", this, this, SLOT(matrixSeriesSettingsActionTriggered())); if (settingsIconValid) { m_matrixSeriesSettingsAction->setIcon(settingsIcon); } QToolButton* settingsToolButton = new QToolButton(); settingsToolButton->setDefaultAction(m_matrixSeriesSettingsAction); QLabel* fileLabel = new QLabel("Matrix File"); m_matrixSeriesFileSelectionComboBox = new CaretDataFileSelectionComboBox(this); QObject::connect(m_matrixSeriesFileSelectionComboBox, SIGNAL(fileSelected(CaretDataFile*)), this, SLOT(matrixSeriesFileSelected(CaretDataFile*))); /* * Yoking Group */ QLabel* yokeLabel = new QLabel("Yoke "); m_matrixSeriesYokingComboBox = new MapYokingGroupComboBox(this); m_matrixSeriesYokingComboBox->getWidget()->setStatusTip("Synchronize enabled status and map indices)"); m_matrixSeriesYokingComboBox->getWidget()->setToolTip("Yoke to Overlay Mapped Files"); #ifdef CARET_OS_MACOSX m_matrixSeriesYokingComboBox->getWidget()->setFixedWidth(m_matrixSeriesYokingComboBox->getWidget()->sizeHint().width() - 20); #endif // CARET_OS_MACOSX QObject::connect(m_matrixSeriesYokingComboBox, SIGNAL(itemActivated()), this, SLOT(matrixSeriesYokingGroupActivated())); QWidget* gridWidget = new QWidget(); QGridLayout* gridLayout = new QGridLayout(gridWidget); switch (orientation) { case Qt::Horizontal: { WuQtUtilities::setLayoutSpacingAndMargins(gridLayout, 2, 0); gridLayout->setColumnStretch(0, 0); gridLayout->setColumnStretch(1, 0); gridLayout->setColumnStretch(2, 0); gridLayout->setColumnStretch(3, 100); gridLayout->addWidget(settingsLabel, 0, 0, 1, 2, Qt::AlignHCenter); gridLayout->addWidget(yokeLabel, 0, 2, Qt::AlignHCenter); gridLayout->addWidget(fileLabel, 0, 3, Qt::AlignHCenter); gridLayout->addWidget(settingsToolButton, 1, 0); gridLayout->addWidget(colorBarToolButton, 1, 1); gridLayout->addWidget(m_matrixSeriesYokingComboBox->getWidget(), 1, 2); gridLayout->addWidget(m_matrixSeriesFileSelectionComboBox->getWidget(), 1, 3); } break; case Qt::Vertical: { WuQtUtilities::setLayoutSpacingAndMargins(gridLayout, 2, 0); gridLayout->setColumnStretch(0, 0); gridLayout->setColumnStretch(1, 0); gridLayout->setColumnStretch(2, 0); gridLayout->setColumnStretch(3, 100); gridLayout->addWidget(settingsLabel, 0, 0, 1, 2, Qt::AlignHCenter); gridLayout->addWidget(yokeLabel, 0, 2, Qt::AlignHCenter); gridLayout->addWidget(settingsToolButton, 1, 0); gridLayout->addWidget(colorBarToolButton, 1, 1); gridLayout->addWidget(m_matrixSeriesYokingComboBox->getWidget(), 1, 2); gridLayout->addWidget(fileLabel, 2, 0, 1, 4, Qt::AlignHCenter); gridLayout->addWidget(m_matrixSeriesFileSelectionComboBox->getWidget(), 3, 0, 1, 4); } break; default: CaretAssert(0); break; } gridWidget->setSizePolicy(gridWidget->sizePolicy().horizontalPolicy(), QSizePolicy::Fixed); QVBoxLayout* layout = new QVBoxLayout(this); WuQtUtilities::setLayoutSpacingAndMargins(layout, 1, 0); layout->addWidget(gridWidget); layout->addStretch(); EventManager::get()->addEventListener(this, EventTypeEnum::EVENT_USER_INTERFACE_UPDATE); } /** * Destructor. */ ChartMatrixSeriesSelectionViewController::~ChartMatrixSeriesSelectionViewController() { EventManager::get()->removeAllEventsFromListener(this); } /** * Update the view controller. */ void ChartMatrixSeriesSelectionViewController::updateSelectionViewController() { Brain* brain = GuiManager::get()->getBrain(); BrowserTabContent* browserTabContent = GuiManager::get()->getBrowserTabContentForBrowserWindow(m_browserWindowIndex, true); if (browserTabContent == NULL) { return; } const int32_t browserTabIndex = browserTabContent->getTabNumber(); ChartDataTypeEnum::Enum chartDataType = ChartDataTypeEnum::CHART_DATA_TYPE_INVALID; ModelChart* modelChart = brain->getChartModel(); if (modelChart != NULL) { chartDataType = modelChart->getSelectedChartDataType(browserTabIndex); } if (chartDataType == ChartDataTypeEnum::CHART_DATA_TYPE_MATRIX_SERIES) { CaretMappableDataFile* caretMappableDataFile = NULL; ChartableMatrixInterface* chartableMatrixInterface = NULL; ChartMatrixDisplayProperties* chartMatrixDisplayProperties = NULL; ChartableMatrixParcelInterface* chartableMatrixParcelInterface = NULL; ChartableMatrixSeriesInterface* chartableMatrixSeriesInterface = NULL; int32_t browserTabIndex = -1; if ( ! getChartMatrixAndProperties(caretMappableDataFile, chartableMatrixInterface, chartableMatrixParcelInterface, chartableMatrixSeriesInterface, chartMatrixDisplayProperties, browserTabIndex)) { return; } if (chartableMatrixSeriesInterface != NULL) { CaretDataFileSelectionModel* fileSelectionModel = modelChart->getChartableMatrixSeriesFileSelectionModel(browserTabIndex); m_matrixSeriesFileSelectionComboBox->updateComboBox(fileSelectionModel); const MapYokingGroupEnum::Enum yokingGroup = chartableMatrixSeriesInterface->getMatrixRowColumnMapYokingGroup(browserTabIndex); m_matrixSeriesYokingComboBox->setMapYokingGroup(yokingGroup); m_matrixSeriesColorBarAction->blockSignals(true); m_matrixSeriesColorBarAction->setChecked(chartMatrixDisplayProperties->getColorBar()->isDisplayed()); m_matrixSeriesColorBarAction->blockSignals(false); m_matrixSeriesColorBarAction->setEnabled(caretMappableDataFile->isMappedWithPalette()); m_matrixSeriesSettingsAction->setEnabled(caretMappableDataFile->isMappedWithPalette()); } } } /** * Receive an event. * * @param event * An event for which this instance is listening. */ void ChartMatrixSeriesSelectionViewController::receiveEvent(Event* event) { if (event->getEventType() == EventTypeEnum::EVENT_USER_INTERFACE_UPDATE) { EventUserInterfaceUpdate* uiEvent = dynamic_cast(event); CaretAssert(uiEvent); if (uiEvent->isUpdateForWindow(m_browserWindowIndex) || uiEvent->isToolBoxUpdate()) { this->updateSelectionViewController(); uiEvent->setEventProcessed(); } } } /** * Get the matrix related files and properties in this view controller. * * @param caretMappableDataFileOut * Output with selected caret mappable data file. * @param chartableMatrixInterfaceOut * Output with ChartableMatrixInterface implemented by the caret * mappable data file. * @param chartableMatrixParcelInterfaceOut * Output with ChartableMatrixParcelInterfaceOut implemented by the * caret mappable data file (may be NULL). * @param chartableMatrixSeriesInterfaceOut * Output with ChartableMatrixSeriesInterfaceOut implemented by the * caret mappable data file (may be NULL). * @param browserTabIndexOut * Index selected tab. * @param chartMatrixDisplayPropertiesOut * Matrix display properties from the ChartableMatrixInterface. * @return True if all output values are valid, else false. */ bool ChartMatrixSeriesSelectionViewController::getChartMatrixAndProperties(CaretMappableDataFile* &caretMappableDataFileOut, ChartableMatrixInterface* & chartableMatrixInterfaceOut, ChartableMatrixParcelInterface* &chartableMatrixParcelInterfaceOut, ChartableMatrixSeriesInterface* &chartableMatrixSeriesInterfaceOut, ChartMatrixDisplayProperties* &chartMatrixDisplayPropertiesOut, int32_t& browserTabIndexOut) { caretMappableDataFileOut = NULL; chartableMatrixInterfaceOut = NULL; chartableMatrixParcelInterfaceOut = NULL; chartableMatrixSeriesInterfaceOut = NULL; chartMatrixDisplayPropertiesOut = NULL; browserTabIndexOut = -1; Brain* brain = GuiManager::get()->getBrain(); BrowserTabContent* browserTabContent = GuiManager::get()->getBrowserTabContentForBrowserWindow(m_browserWindowIndex, true); if (browserTabContent == NULL) { return false; } browserTabIndexOut = browserTabContent->getTabNumber(); if (browserTabIndexOut < 0) { return false; } ModelChart* modelChart = brain->getChartModel(); if (modelChart != NULL) { switch (modelChart->getSelectedChartDataType(browserTabIndexOut)) { case ChartDataTypeEnum::CHART_DATA_TYPE_INVALID: break; case ChartDataTypeEnum::CHART_DATA_TYPE_MATRIX_LAYER: { CaretDataFileSelectionModel* parcelFileSelectionModel = modelChart->getChartableMatrixParcelFileSelectionModel(browserTabIndexOut); //m_matrixParcelFileSelectionComboBox->updateComboBox(parcelFileSelectionModel); CaretDataFile* caretParcelFile = parcelFileSelectionModel->getSelectedFile(); if (caretParcelFile != NULL) { chartableMatrixInterfaceOut = dynamic_cast(caretParcelFile); if (chartableMatrixInterfaceOut != NULL) { chartableMatrixParcelInterfaceOut = dynamic_cast(caretParcelFile); chartMatrixDisplayPropertiesOut = chartableMatrixInterfaceOut->getChartMatrixDisplayProperties(browserTabIndexOut); caretMappableDataFileOut = chartableMatrixInterfaceOut->getMatrixChartCaretMappableDataFile(); return true; } } } break; case ChartDataTypeEnum::CHART_DATA_TYPE_MATRIX_SERIES: { CaretDataFileSelectionModel* seriesFileSelectionModel = modelChart->getChartableMatrixSeriesFileSelectionModel(browserTabIndexOut); CaretDataFile* caretSeriesFile = seriesFileSelectionModel->getSelectedFile(); if (caretSeriesFile != NULL) { chartableMatrixInterfaceOut = dynamic_cast(caretSeriesFile); if (chartableMatrixInterfaceOut != NULL) { chartableMatrixSeriesInterfaceOut = dynamic_cast(caretSeriesFile); chartMatrixDisplayPropertiesOut = chartableMatrixInterfaceOut->getChartMatrixDisplayProperties(browserTabIndexOut); caretMappableDataFileOut = chartableMatrixInterfaceOut->getMatrixChartCaretMappableDataFile(); return true; } } } break; case ChartDataTypeEnum::CHART_DATA_TYPE_LINE_DATA_SERIES: break; case ChartDataTypeEnum::CHART_DATA_TYPE_LINE_FREQUENCY_SERIES: break; case ChartDataTypeEnum::CHART_DATA_TYPE_LINE_TIME_SERIES: break; } } return false; } /** * Called when a matrix series file is selected. * * @param caretDataFile * Caret data file that was selected. */ void ChartMatrixSeriesSelectionViewController::matrixSeriesFileSelected(CaretDataFile* /*caretDataFile*/) { updateSelectionViewController(); EventManager::get()->sendEvent(EventGraphicsUpdateOneWindow(m_browserWindowIndex).getPointer()); } /** * Called when colorbar icon button is clicked for matrix series file. */ void ChartMatrixSeriesSelectionViewController::matrixSeriesColorBarActionTriggered(bool status) { CaretMappableDataFile* caretMappableDataFile = NULL; ChartableMatrixInterface* chartableMatrixInterface = NULL; ChartMatrixDisplayProperties* chartMatrixDisplayProperties = NULL; ChartableMatrixParcelInterface* chartableMatrixParcelInterface = NULL; ChartableMatrixSeriesInterface* chartableMatrixSeriesInterface = NULL; int32_t browserTabIndex = -1; if ( ! getChartMatrixAndProperties(caretMappableDataFile, chartableMatrixInterface, chartableMatrixParcelInterface, chartableMatrixSeriesInterface, chartMatrixDisplayProperties, browserTabIndex)) { return; } if (chartableMatrixSeriesInterface != NULL) { chartMatrixDisplayProperties->getColorBar()->setDisplayed(status); EventManager::get()->sendEvent(EventGraphicsUpdateAllWindows().getPointer()); } } /** * Called when settings icon button is clicked to display palette editor * for matrix series file. */ void ChartMatrixSeriesSelectionViewController::matrixSeriesSettingsActionTriggered() { CaretMappableDataFile* caretMappableDataFile = NULL; ChartableMatrixInterface* chartableMatrixInterface = NULL; ChartMatrixDisplayProperties* chartMatrixDisplayProperties = NULL; ChartableMatrixParcelInterface* chartableMatrixParcelInterface = NULL; ChartableMatrixSeriesInterface* chartableMatrixSeriesInterface = NULL; int32_t browserTabIndex = -1; if ( ! getChartMatrixAndProperties(caretMappableDataFile, chartableMatrixInterface, chartableMatrixParcelInterface, chartableMatrixSeriesInterface, chartMatrixDisplayProperties, browserTabIndex)) { return; } if (chartableMatrixSeriesInterface != NULL) { const int32_t mapIndex = 0; EventPaletteColorMappingEditorDialogRequest dialogEvent(m_browserWindowIndex, caretMappableDataFile, mapIndex); EventManager::get()->sendEvent(dialogEvent.getPointer()); } } /** * Called when matrix series yoking group is changed. */ void ChartMatrixSeriesSelectionViewController::matrixSeriesYokingGroupActivated() { CaretMappableDataFile* caretMappableDataFile = NULL; ChartableMatrixInterface* chartableMatrixInterface = NULL; ChartMatrixDisplayProperties* chartMatrixDisplayProperties = NULL; ChartableMatrixParcelInterface* chartableMatrixParcelInterface = NULL; ChartableMatrixSeriesInterface* chartableMatrixSeriesInterface = NULL; int32_t browserTabIndex = -1; if ( ! getChartMatrixAndProperties(caretMappableDataFile, chartableMatrixInterface, chartableMatrixParcelInterface, chartableMatrixSeriesInterface, chartMatrixDisplayProperties, browserTabIndex)) { return; } m_matrixSeriesYokingComboBox->validateYokingChange(chartableMatrixSeriesInterface, browserTabIndex); EventManager::get()->sendEvent(EventGraphicsUpdateAllWindows().getPointer()); } connectome-workbench-1.2.3+git41-gc4c6c90/src/GuiQt/ChartMatrixSeriesSelectionViewController.h000066400000000000000000000070551300200146000321230ustar00rootroot00000000000000#ifndef __CHART_MATRIX_SERIES_SELECTION_VIEW_CONTROLLER_H__ #define __CHART_MATRIX_SERIES_SELECTION_VIEW_CONTROLLER_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 "EventListenerInterface.h" namespace caret { class CaretDataFile; class CaretDataFileSelectionComboBox; class CaretMappableDataFile; class ChartMatrixDisplayProperties; class ChartableMatrixInterface; class ChartableMatrixParcelInterface; class ChartableMatrixSeriesInterface; class MapYokingGroupComboBox; class ChartMatrixSeriesSelectionViewController : public QWidget, public EventListenerInterface { Q_OBJECT public: ChartMatrixSeriesSelectionViewController(const Qt::Orientation orientation, const int32_t browserWindowIndex, QWidget* parent); virtual ~ChartMatrixSeriesSelectionViewController(); private slots: void matrixSeriesFileSelected(CaretDataFile* caretDataFile); void matrixSeriesColorBarActionTriggered(bool status); void matrixSeriesSettingsActionTriggered(); void matrixSeriesYokingGroupActivated(); private: ChartMatrixSeriesSelectionViewController(const ChartMatrixSeriesSelectionViewController&); ChartMatrixSeriesSelectionViewController& operator=(const ChartMatrixSeriesSelectionViewController&); public: // ADD_NEW_METHODS_HERE virtual void receiveEvent(Event* event); private: // ADD_NEW_MEMBERS_HERE void updateSelectionViewController(); bool getChartMatrixAndProperties(CaretMappableDataFile* &caretMappableDataFileOut, ChartableMatrixInterface* & chartableMatrixInterfaceOut, ChartableMatrixParcelInterface* &chartableMatrixParcelInterfaceOut, ChartableMatrixSeriesInterface* &chartableMatrixSeriesInterfaceOut, ChartMatrixDisplayProperties* &chartMatrixDisplayPropertiesOut, int32_t& browserTabIndexOut); const int32_t m_browserWindowIndex; QAction* m_matrixSeriesColorBarAction; QAction* m_matrixSeriesSettingsAction; CaretDataFileSelectionComboBox* m_matrixSeriesFileSelectionComboBox; MapYokingGroupComboBox* m_matrixSeriesYokingComboBox; }; #ifdef __CHART_MATRIX_SERIES_SELECTION_VIEW_CONTROLLER_DECLARE__ #endif // __CHART_MATRIX_SERIES_SELECTION_VIEW_CONTROLLER_DECLARE__ } // namespace #endif //__CHART_MATRIX_SERIES_SELECTION_VIEW_CONTROLLER_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/GuiQt/ChartSelectionViewController.cxx000066400000000000000000000140221300200146000301260ustar00rootroot00000000000000 /*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_SELECTION_VIEW_CONTROLLER_DECLARE__ #include "ChartSelectionViewController.h" #undef __CHART_SELECTION_VIEW_CONTROLLER_DECLARE__ #include #include #include #include "Brain.h" #include "BrowserTabContent.h" #include "CaretAssert.h" #include "ChartLinesSelectionViewController.h" #include "ChartMatrixParcelSelectionViewController.h" #include "ChartMatrixSeriesSelectionViewController.h" #include "EventManager.h" #include "EventUserInterfaceUpdate.h" #include "GuiManager.h" #include "ModelChart.h" #include "WuQtUtilities.h" using namespace caret; /** * \class caret::ChartSelectionViewController * \brief Handles selection of charts displayed in chart model. * \ingroup GuiQt */ /** * Constructor. */ ChartSelectionViewController::ChartSelectionViewController(const Qt::Orientation orientation, const int32_t browserWindowIndex, QWidget* parent) : QWidget(parent), m_browserWindowIndex(browserWindowIndex) { m_mode = MODE_INVALID; m_brainordinateChartWidget = new ChartLinesSelectionViewController(orientation, browserWindowIndex, parent); m_matrixParcelChartWidget = new ChartMatrixParcelSelectionViewController(orientation, browserWindowIndex, parent); m_matrixSeriesChartWidget = new ChartMatrixSeriesSelectionViewController(orientation, browserWindowIndex, parent); m_stackedWidget = new QStackedWidget(); m_stackedWidget->addWidget(m_brainordinateChartWidget); m_stackedWidget->addWidget(m_matrixParcelChartWidget); m_stackedWidget->addWidget(m_matrixSeriesChartWidget); QVBoxLayout* layout = new QVBoxLayout(this); WuQtUtilities::setLayoutSpacingAndMargins(layout, 0, 0); layout->addWidget(m_stackedWidget); layout->addStretch(); EventManager::get()->addEventListener(this, EventTypeEnum::EVENT_USER_INTERFACE_UPDATE); } /** * Destructor. */ ChartSelectionViewController::~ChartSelectionViewController() { EventManager::get()->removeAllEventsFromListener(this); } /** * Update the view controller. */ void ChartSelectionViewController::updateSelectionViewController() { m_mode = MODE_INVALID; Brain* brain = GuiManager::get()->getBrain(); BrowserTabContent* browserTabContent = GuiManager::get()->getBrowserTabContentForBrowserWindow(m_browserWindowIndex, true); if (browserTabContent == NULL) { return; } const int32_t browserTabIndex = browserTabContent->getTabNumber(); ChartDataTypeEnum::Enum chartDataType = ChartDataTypeEnum::CHART_DATA_TYPE_INVALID; ModelChart* modelChart = brain->getChartModel(); if (modelChart != NULL) { chartDataType = modelChart->getSelectedChartDataType(browserTabIndex); } switch (chartDataType) { case ChartDataTypeEnum::CHART_DATA_TYPE_INVALID: break; case ChartDataTypeEnum::CHART_DATA_TYPE_MATRIX_LAYER: m_mode = MODE_MATRIX_LAYER; break; case ChartDataTypeEnum::CHART_DATA_TYPE_MATRIX_SERIES: m_mode = MODE_MATRIX_SERIES; break; case ChartDataTypeEnum::CHART_DATA_TYPE_LINE_DATA_SERIES: m_mode = MODE_BRAINORDINATE; break; case ChartDataTypeEnum::CHART_DATA_TYPE_LINE_FREQUENCY_SERIES: m_mode = MODE_BRAINORDINATE; break; case ChartDataTypeEnum::CHART_DATA_TYPE_LINE_TIME_SERIES: m_mode = MODE_BRAINORDINATE; break; } switch (m_mode) { case MODE_INVALID: break; case MODE_BRAINORDINATE: m_stackedWidget->setCurrentWidget(m_brainordinateChartWidget); //m_brainordinateChartWidget->updateSelectionViewController(); break; case MODE_MATRIX_LAYER: m_stackedWidget->setCurrentWidget(m_matrixParcelChartWidget); //m_matrixParcelChartWidget->updateSelectionViewController(); break; case MODE_MATRIX_SERIES: m_stackedWidget->setCurrentWidget(m_matrixSeriesChartWidget); //m_matrixSeriesChartWidget->updateSelectionViewController(); break; } } /** * Receive an event. * * @param event * An event for which this instance is listening. */ void ChartSelectionViewController::receiveEvent(Event* event) { if (event->getEventType() == EventTypeEnum::EVENT_USER_INTERFACE_UPDATE) { EventUserInterfaceUpdate* uiEvent = dynamic_cast(event); CaretAssert(uiEvent); if (uiEvent->isUpdateForWindow(m_browserWindowIndex) || uiEvent->isToolBoxUpdate()) { this->updateSelectionViewController(); uiEvent->setEventProcessed(); } } } connectome-workbench-1.2.3+git41-gc4c6c90/src/GuiQt/ChartSelectionViewController.h000066400000000000000000000052141300200146000275560ustar00rootroot00000000000000#ifndef __CHART_SELECTION_VIEW_CONTROLLER_H__ #define __CHART_SELECTION_VIEW_CONTROLLER_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 "EventListenerInterface.h" class QStackedWidget; namespace caret { class ChartLinesSelectionViewController; class ChartMatrixParcelSelectionViewController; class ChartMatrixSeriesSelectionViewController; class ChartSelectionViewController : public QWidget, public EventListenerInterface { Q_OBJECT public: ChartSelectionViewController(const Qt::Orientation orientation, const int32_t browserWindowIndex, QWidget* parent); virtual ~ChartSelectionViewController(); private: ChartSelectionViewController(const ChartSelectionViewController&); ChartSelectionViewController& operator=(const ChartSelectionViewController&); public: // ADD_NEW_METHODS_HERE virtual void receiveEvent(Event* event); private: enum Mode { MODE_INVALID, MODE_BRAINORDINATE, MODE_MATRIX_LAYER, MODE_MATRIX_SERIES }; // ADD_NEW_MEMBERS_HERE void updateSelectionViewController(); QStackedWidget* m_stackedWidget; ChartLinesSelectionViewController* m_brainordinateChartWidget; ChartMatrixParcelSelectionViewController* m_matrixParcelChartWidget; ChartMatrixSeriesSelectionViewController* m_matrixSeriesChartWidget; Mode m_mode; const int32_t m_browserWindowIndex; }; #ifdef __CHART_SELECTION_VIEW_CONTROLLER_DECLARE__ #endif // __CHART_SELECTION_VIEW_CONTROLLER_DECLARE__ } // namespace #endif //__CHART_SELECTION_VIEW_CONTROLLER_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/GuiQt/ChartToolBoxViewController.cxx000066400000000000000000000221011300200146000275640ustar00rootroot00000000000000 /*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_TOOL_BOX_VIEW_CONTROLLER_DECLARE__ #include "ChartToolBoxViewController.h" #undef __CHART_TOOL_BOX_VIEW_CONTROLLER_DECLARE__ #include #include #include #include "Brain.h" #include "BrowserTabContent.h" #include "CaretAssert.h" #include "ChartHistoryViewController.h" #include "ChartModel.h" #include "ChartModelDataSeries.h" #include "ChartModelFrequencySeries.h" #include "ChartModelTimeSeries.h" #include "ChartSelectionViewController.h" #include "EventManager.h" #include "EventUserInterfaceUpdate.h" #include "GuiManager.h" #include "ModelChart.h" #include "SceneClass.h" #include "SceneClassAssistant.h" using namespace caret; /** * \class caret::ChartToolBoxViewController * \brief View controller for the "Charting" tab of the overlay toolbox * \ingroup GuiQt */ /** * Constructor. */ ChartToolBoxViewController::ChartToolBoxViewController(const Qt::Orientation orientation, const int32_t browserWindowIndex, QWidget* parent) : QWidget(parent), EventListenerInterface(), m_browserWindowIndex(browserWindowIndex) { /* * Data series widgets */ m_chartSelectionViewController = new ChartSelectionViewController(orientation, browserWindowIndex, NULL); m_chartHistoryViewController = new ChartHistoryViewController(orientation, browserWindowIndex, NULL); m_tabWidget = new QTabWidget(); m_tabWidget->addTab(m_chartSelectionViewController, "Loading"); m_tabWidget->addTab(m_chartHistoryViewController, "History"); QVBoxLayout* layout = new QVBoxLayout(this); layout->addWidget(m_tabWidget); layout->addStretch(); m_sceneAssistant = new SceneClassAssistant(); EventManager::get()->addEventListener(this, EventTypeEnum::EVENT_USER_INTERFACE_UPDATE); } /** * Destructor. */ ChartToolBoxViewController::~ChartToolBoxViewController() { EventManager::get()->removeAllEventsFromListener(this); delete m_sceneAssistant; } /** * Receive an event. * * @param event * An event for which this instance is listening. */ void ChartToolBoxViewController::receiveEvent(Event* event) { if (event->getEventType() == EventTypeEnum::EVENT_USER_INTERFACE_UPDATE) { EventUserInterfaceUpdate* uiEvent = dynamic_cast(event); CaretAssert(uiEvent); if (uiEvent->isUpdateForWindow(m_browserWindowIndex) || uiEvent->isToolBoxUpdate()) { uiEvent->setEventProcessed(); ChartModel* chartModel = getSelectedChartModel(); bool historyWidgetValid = false; if (chartModel != NULL) { switch (chartModel->getChartDataType()) { 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: historyWidgetValid = true; break; case ChartDataTypeEnum::CHART_DATA_TYPE_LINE_FREQUENCY_SERIES: historyWidgetValid = true; break; case ChartDataTypeEnum::CHART_DATA_TYPE_LINE_TIME_SERIES: historyWidgetValid = true; break; } } const int32_t historyTabIndex = m_tabWidget->indexOf(m_chartHistoryViewController); if (historyTabIndex >= 0) { m_tabWidget->setTabEnabled(historyTabIndex, historyWidgetValid); } if ( ! historyWidgetValid) { m_tabWidget->setCurrentWidget(m_chartSelectionViewController); } } } } /** * @return Chart model selected in the selected tab (NULL if not valid) */ ChartModel* ChartToolBoxViewController::getSelectedChartModel() { Brain* brain = GuiManager::get()->getBrain(); BrowserTabContent* browserTabContent = GuiManager::get()->getBrowserTabContentForBrowserWindow(m_browserWindowIndex, true); if (browserTabContent == NULL) { return NULL; } ChartModel* chartModel = NULL; ModelChart* modelChart = brain->getChartModel(); if (modelChart != NULL) { const int32_t browserTabIndex = browserTabContent->getTabNumber(); switch (modelChart->getSelectedChartDataType(browserTabIndex)) { 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: chartModel = modelChart->getSelectedDataSeriesChartModel(browserTabIndex); break; case ChartDataTypeEnum::CHART_DATA_TYPE_LINE_FREQUENCY_SERIES: chartModel = modelChart->getSelectedFrequencySeriesChartModel(browserTabIndex); break; case ChartDataTypeEnum::CHART_DATA_TYPE_LINE_TIME_SERIES: chartModel = modelChart->getSelectedTimeSeriesChartModel(browserTabIndex); break; } } return chartModel; } /** * 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* ChartToolBoxViewController::saveToScene(const SceneAttributes* sceneAttributes, const AString& instanceName) { SceneClass* sceneClass = new SceneClass(instanceName, "ChartToolBoxViewController", 1); AString tabName; const int tabIndex = m_tabWidget->currentIndex(); if ((tabIndex >= 0) && tabIndex < m_tabWidget->count()) { tabName = m_tabWidget->tabText(tabIndex); } sceneClass->addString("selectedChartTabName", tabName); 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 ChartToolBoxViewController::restoreFromScene(const SceneAttributes* sceneAttributes, const SceneClass* sceneClass) { if (sceneClass == NULL) { return; } const AString tabName = sceneClass->getStringValue("selectedChartTabName", ""); for (int32_t i = 0; i < m_tabWidget->count(); i++) { if (m_tabWidget->tabText(i) == tabName) { m_tabWidget->setCurrentIndex(i); break; } } m_sceneAssistant->restoreMembers(sceneAttributes, sceneClass); //Uncomment if sub-classes must restore from scene //restoreSubClassDataFromScene(sceneAttributes, // sceneClass); } connectome-workbench-1.2.3+git41-gc4c6c90/src/GuiQt/ChartToolBoxViewController.h000066400000000000000000000065121300200146000272210ustar00rootroot00000000000000#ifndef __CHART_TOOL_BOX_VIEW_CONTROLLER_H__ #define __CHART_TOOL_BOX_VIEW_CONTROLLER_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 "EventListenerInterface.h" #include "SceneableInterface.h" class QStackedWidget; class QTabWidget; namespace caret { class ChartHistoryViewController; class ChartModel; class ChartSelectionViewController; class SceneClassAssistant; class ChartToolBoxViewController : public QWidget, public EventListenerInterface, public SceneableInterface { Q_OBJECT public: ChartToolBoxViewController(const Qt::Orientation orientation, const int32_t browserWindowIndex, QWidget* parent); virtual ~ChartToolBoxViewController(); // ADD_NEW_METHODS_HERE 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 implemetation by sub-classes. // protected: // virtual void saveSubClassDataToScene(const SceneAttributes* sceneAttributes, // SceneClass* sceneClass) = 0; // // virtual void restoreSubClassDataFromScene(const SceneAttributes* sceneAttributes, // const SceneClass* sceneClass) = 0; private: ChartToolBoxViewController(const ChartToolBoxViewController&); ChartToolBoxViewController& operator=(const ChartToolBoxViewController&); ChartModel* getSelectedChartModel(); SceneClassAssistant* m_sceneAssistant; const int32_t m_browserWindowIndex; QTabWidget* m_tabWidget; ChartSelectionViewController* m_chartSelectionViewController; ChartHistoryViewController* m_chartHistoryViewController; // ADD_NEW_MEMBERS_HERE }; #ifdef __CHART_TOOL_BOX_VIEW_CONTROLLER_DECLARE__ // #endif // __CHART_TOOL_BOX_VIEW_CONTROLLER_DECLARE__ } // namespace #endif //__CHART_TOOL_BOX_VIEW_CONTROLLER_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/GuiQt/CiftiConnectivityMatrixViewController.cxx000066400000000000000000000555111300200146000320510ustar00rootroot00000000000000 /*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 __CIFTI_CONNECTIVITY_MATRIX_VIEW_CONTROLLER_DECLARE__ #include "CiftiConnectivityMatrixViewController.h" #undef __CIFTI_CONNECTIVITY_MATRIX_VIEW_CONTROLLER_DECLARE__ #include #include #include #include #include #include #include #include #include "Brain.h" #include "CiftiBrainordinateScalarFile.h" #include "CiftiConnectivityMatrixDenseDynamicFile.h" #include "CiftiFiberOrientationFile.h" #include "CiftiFiberTrajectoryFile.h" #include "CiftiMappableConnectivityMatrixDataFile.h" #include "CursorDisplayScoped.h" #include "EventDataFileAdd.h" #include "EventManager.h" #include "EventGraphicsUpdateAllWindows.h" #include "EventSurfaceColoringInvalidate.h" #include "EventUserInterfaceUpdate.h" #include "FiberTrajectoryMapProperties.h" #include "FilePathNamePrefixCompactor.h" #include "GuiManager.h" #include "WuQMessageBox.h" #include "WuQtUtilities.h" using namespace caret; static const char* FILE_POINTER_PROPERTY_NAME = "filePointer"; /** * \class caret::CiftiConnectivityMatrixViewController * \brief View-Controller connectivity files * \ingroup GuiQt */ /** * Constructor. */ CiftiConnectivityMatrixViewController::CiftiConnectivityMatrixViewController(const Qt::Orientation /*orientation*/, QWidget* parent) : QWidget(parent) { m_gridLayout = new QGridLayout(); WuQtUtilities::setLayoutSpacingAndMargins(m_gridLayout, 2, 2); m_gridLayout->setColumnStretch(COLUMN_ENABLE_CHECKBOX, 0); m_gridLayout->setColumnStretch(COLUMN_LAYER_CHECKBOX, 0); m_gridLayout->setColumnStretch(COLUMN_COPY_BUTTON, 0); m_gridLayout->setColumnStretch(COLUMN_NAME_LINE_EDIT, 100); m_gridLayout->setColumnStretch(COLUMN_ORIENTATION_FILE_COMBO_BOX, 100); const int titleRow = m_gridLayout->rowCount(); m_gridLayout->addWidget(new QLabel("Load"), titleRow, COLUMN_ENABLE_CHECKBOX); m_gridLayout->addWidget(new QLabel("Layer"), titleRow, COLUMN_LAYER_CHECKBOX); m_gridLayout->addWidget(new QLabel("Copy"), titleRow, COLUMN_COPY_BUTTON); m_gridLayout->addWidget(new QLabel("Connectivity File"), titleRow, COLUMN_NAME_LINE_EDIT); m_gridLayout->addWidget(new QLabel("Fiber Orientation File"), titleRow, COLUMN_ORIENTATION_FILE_COMBO_BOX); m_signalMapperFileEnableCheckBox = new QSignalMapper(this); QObject::connect(m_signalMapperFileEnableCheckBox, SIGNAL(mapped(int)), this, SLOT(enabledCheckBoxClicked(int))); m_signalMapperLayerCheckBox = new QSignalMapper(this); QObject::connect(m_signalMapperLayerCheckBox, SIGNAL(mapped(int)), this, SLOT(layerCheckBoxClicked(int))); m_signalMapperFileCopyToolButton = new QSignalMapper(this); QObject::connect(m_signalMapperFileCopyToolButton, SIGNAL(mapped(int)), this, SLOT(copyToolButtonClicked(int))); m_signalMapperFiberOrientationFileComboBox = new QSignalMapper(this); QObject::connect(m_signalMapperFiberOrientationFileComboBox, SIGNAL(mapped(int)), this, SLOT(fiberOrientationFileComboBoxActivated(int))); QVBoxLayout* layout = new QVBoxLayout(this); layout->addLayout(m_gridLayout); layout->addStretch(); s_allCiftiConnectivityMatrixViewControllers.insert(this); EventManager::get()->addEventListener(this, EventTypeEnum::EVENT_USER_INTERFACE_UPDATE); } /** * Destructor. */ CiftiConnectivityMatrixViewController::~CiftiConnectivityMatrixViewController() { s_allCiftiConnectivityMatrixViewControllers.erase(this); EventManager::get()->removeAllEventsFromListener(this); } /** * Update this view controller. */ void CiftiConnectivityMatrixViewController::updateViewController() { Brain* brain = GuiManager::get()->getBrain(); std::vector files; std::vector trajFiles; brain->getConnectivityFiberTrajectoryFiles(trajFiles); for (std::vector::iterator trajIter = trajFiles.begin(); trajIter != trajFiles.end(); trajIter++) { files.push_back(*trajIter); } std::vector matrixFiles; brain->getAllCiftiConnectivityMatrixFiles(matrixFiles); for (std::vector::iterator matrixIter = matrixFiles.begin(); matrixIter != matrixFiles.end(); matrixIter++) { files.push_back(*matrixIter); } const int32_t numFiles = static_cast(files.size()); // std::vector displayNames; // FilePathNamePrefixCompactor::removeMatchingPathPrefixFromCaretDataFiles(files, // displayNames); // // CaretAssert(files.size() == displayNames.size()); for (int32_t i = 0; i < numFiles; i++) { QCheckBox* checkBox = NULL; QCheckBox* layerCheckBox = NULL; QLineEdit* lineEdit = NULL; QToolButton* copyToolButton = NULL; QComboBox* comboBox = NULL; if (i < static_cast(m_fileEnableCheckBoxes.size())) { checkBox = m_fileEnableCheckBoxes[i]; layerCheckBox = m_layerCheckBoxes[i]; lineEdit = m_fileNameLineEdits[i]; copyToolButton = m_fileCopyToolButtons[i]; comboBox = m_fiberOrientationFileComboBoxes[i]; } else { checkBox = new QCheckBox(""); checkBox->setToolTip("When selected, load data during\n" "an identification operation"); m_fileEnableCheckBoxes.push_back(checkBox); layerCheckBox = new QCheckBox(""); layerCheckBox->setToolTip("When selected, allow selection of this\n" "file as an overlay in the Layers tab"); m_layerCheckBoxes.push_back(layerCheckBox); lineEdit = new QLineEdit(); lineEdit->setReadOnly(true); m_fileNameLineEdits.push_back(lineEdit); copyToolButton = new QToolButton(); copyToolButton->setText("Copy"); copyToolButton->setToolTip("Copy loaded row data to a new CIFTI Scalar File"); m_fileCopyToolButtons.push_back(copyToolButton); comboBox = new QComboBox(); m_fiberOrientationFileComboBoxes.push_back(comboBox); QObject::connect(copyToolButton, SIGNAL(clicked()), m_signalMapperFileCopyToolButton, SLOT(map())); m_signalMapperFileCopyToolButton->setMapping(copyToolButton, i); QObject::connect(checkBox, SIGNAL(clicked(bool)), m_signalMapperFileEnableCheckBox, SLOT(map())); m_signalMapperFileEnableCheckBox->setMapping(checkBox, i); QObject::connect(layerCheckBox, SIGNAL(clicked(bool)), m_signalMapperLayerCheckBox, SLOT(map())); m_signalMapperLayerCheckBox->setMapping(layerCheckBox, i); QObject::connect(comboBox, SIGNAL(activated(int)), m_signalMapperFiberOrientationFileComboBox, SLOT(map())); m_signalMapperFiberOrientationFileComboBox->setMapping(comboBox, i); const int row = m_gridLayout->rowCount(); m_gridLayout->addWidget(checkBox, row, COLUMN_ENABLE_CHECKBOX); m_gridLayout->addWidget(layerCheckBox, row, COLUMN_LAYER_CHECKBOX); m_gridLayout->addWidget(copyToolButton, row, COLUMN_COPY_BUTTON); m_gridLayout->addWidget(lineEdit, row, COLUMN_NAME_LINE_EDIT); m_gridLayout->addWidget(comboBox, row, COLUMN_ORIENTATION_FILE_COMBO_BOX); } const CiftiMappableConnectivityMatrixDataFile* matrixFile = dynamic_cast(files[i]); const CiftiFiberTrajectoryFile* trajFile = dynamic_cast(files[i]); bool checkStatus = false; if (matrixFile != NULL) { checkStatus = matrixFile->isMapDataLoadingEnabled(0); } else if (trajFile != NULL) { checkStatus = trajFile->isDataLoadingEnabled(); } else { CaretAssertMessage(0, "Has a new file type been added?"); } checkBox->setChecked(checkStatus); checkBox->setProperty(FILE_POINTER_PROPERTY_NAME, qVariantFromValue((void*)files[i])); const CiftiConnectivityMatrixDenseDynamicFile* dynConnFile = dynamic_cast(files[i]); if (dynConnFile != NULL) { layerCheckBox->setChecked(dynConnFile->isEnabledAsLayer()); } else { layerCheckBox->setChecked(false); } lineEdit->setText(files[i]->getFileName()); // displayNames[i]); } const int32_t numItems = static_cast(m_fileEnableCheckBoxes.size()); for (int32_t i = 0; i < numItems; i++) { bool layerCheckBoxValid = false; bool showRow = false; bool showOrientationComboBox = false; if (i < numFiles) { showRow = true; if (dynamic_cast(files[i]) != NULL) { showOrientationComboBox = true; } if (dynamic_cast(files[i]) != NULL) { layerCheckBoxValid = true; } } m_fileEnableCheckBoxes[i]->setVisible(showRow); m_layerCheckBoxes[i]->setVisible(showRow); m_layerCheckBoxes[i]->setEnabled(layerCheckBoxValid); m_fileCopyToolButtons[i]->setVisible(showRow); m_fileNameLineEdits[i]->setVisible(showRow); m_fiberOrientationFileComboBoxes[i]->setVisible(showOrientationComboBox); m_fiberOrientationFileComboBoxes[i]->setEnabled(showOrientationComboBox); } updateFiberOrientationComboBoxes(); } /** * Update the fiber orientation combo boxes. */ void CiftiConnectivityMatrixViewController::updateFiberOrientationComboBoxes() { std::vector orientationFiles; GuiManager::get()->getBrain()->getConnectivityFiberOrientationFiles(orientationFiles); const int32_t numOrientationFiles = static_cast(orientationFiles.size()); const int32_t numItems = static_cast(m_fiberOrientationFileComboBoxes.size()); for (int32_t i = 0; i < numItems; i++) { QComboBox* comboBox = m_fiberOrientationFileComboBoxes[i]; CiftiMappableConnectivityMatrixDataFile* matrixFile = NULL; CiftiFiberTrajectoryFile* trajFile = NULL; if (comboBox->isEnabled()) { getFileAtIndex(i, matrixFile, trajFile); } if (trajFile != NULL) { int32_t selectedIndex = 0; CiftiFiberOrientationFile* selectedOrientationFile = trajFile->getMatchingFiberOrientationFile(); comboBox->clear(); for (int32_t iOrient = 0; iOrient < numOrientationFiles; iOrient++) { CiftiFiberOrientationFile* orientFile = orientationFiles[iOrient]; if (trajFile->isFiberOrientationFileCombatible(orientFile)) { if (orientFile == selectedOrientationFile) { selectedIndex = iOrient; } comboBox->addItem(orientFile->getFileNameNoPath(), qVariantFromValue((void*)orientFile)); } } if ((selectedIndex >= 0) && (selectedIndex < comboBox->count())) { comboBox->setCurrentIndex(selectedIndex); } } else { comboBox->clear(); } // const bool showComboBox = (trajFile != NULL); // m_fiberOrientationFileComboBoxes[i]->setVisible(showComboBox); // m_fiberOrientationFileComboBoxes[i]->setEnabled(showComboBox); // std::cout << "Show Orientation File Combo Box: " // << i // << ": " // << qPrintable(AString::fromBool(showComboBox)) // << std::endl; } } /** * Called when an enabled check box changes state. * * @param indx * Index of checkbox that was clicked. */ void CiftiConnectivityMatrixViewController::enabledCheckBoxClicked(int indx) { CaretAssertVectorIndex(m_fileEnableCheckBoxes, indx); const bool newStatus = m_fileEnableCheckBoxes[indx]->isChecked(); CiftiMappableConnectivityMatrixDataFile* matrixFile = NULL; CiftiFiberTrajectoryFile* trajFile = NULL; getFileAtIndex(indx, matrixFile, trajFile); if (matrixFile != NULL) { matrixFile->setMapDataLoadingEnabled(0, newStatus); } else if (trajFile != NULL) { trajFile->setDataLoadingEnabled(newStatus); } else { CaretAssertMessage(0, "Has a new file type been added?"); } updateOtherCiftiConnectivityMatrixViewControllers(); } /** * Called when a layer check box changes state. * * @param indx * Index of checkbox that was clicked. */ void CiftiConnectivityMatrixViewController::layerCheckBoxClicked(int indx) { CaretAssertVectorIndex(m_layerCheckBoxes, indx); const bool newStatus = m_layerCheckBoxes[indx]->isChecked(); CiftiMappableConnectivityMatrixDataFile* matrixFile = NULL; CiftiFiberTrajectoryFile* trajFile = NULL; getFileAtIndex(indx, matrixFile, trajFile); if (matrixFile != NULL) { CiftiConnectivityMatrixDenseDynamicFile* dynConnFile = dynamic_cast(matrixFile); if (dynConnFile != NULL) { dynConnFile->setEnabledAsLayer(newStatus); } } else if (trajFile != NULL) { CaretAssertMessage(0, "Should never get caled for fiber trajectory file"); } else { CaretAssertMessage(0, "Has a new file type been added?"); } EventManager::get()->sendEvent(EventUserInterfaceUpdate().getPointer()); EventManager::get()->sendEvent(EventSurfaceColoringInvalidate().getPointer()); EventManager::get()->sendEvent(EventGraphicsUpdateAllWindows().getPointer()); //updateOtherCiftiConnectivityMatrixViewControllers(); } /** * Get the file associated with the given index. One of the output files * will be NULL and the other will be non-NULL. * * @param indx * The index. * @param ciftiMatrixFileOut * If there is a CIFTI matrix file at the given index, this will be non-NULL. * @param ciftiTrajFileOut * If there is a CIFTI trajectory file at the given index, this will be non-NULL. */ void CiftiConnectivityMatrixViewController::getFileAtIndex(const int32_t indx, CiftiMappableConnectivityMatrixDataFile* &ciftiMatrixFileOut, CiftiFiberTrajectoryFile* &ciftiTrajFileOut) { CaretAssertVectorIndex(m_fileEnableCheckBoxes, indx); void* ptr = m_fileEnableCheckBoxes[indx]->property(FILE_POINTER_PROPERTY_NAME).value(); CaretMappableDataFile* mapFilePointer = (CaretMappableDataFile*)ptr; ciftiMatrixFileOut = dynamic_cast(mapFilePointer); ciftiTrajFileOut = dynamic_cast(mapFilePointer); AString name = ""; if (mapFilePointer != NULL) { name = mapFilePointer->getFileNameNoPath(); } // std::cout << "File at index: " // << indx // << " name: " // << qPrintable(name) // << " cifti-matrix-ptr: " // << (long)ciftiMatrixFileOut // << " cifti-traj-ptr: " // << (long)ciftiTrajFileOut // << std::endl; if (ciftiMatrixFileOut != NULL) { /* OK */ } else if (ciftiTrajFileOut != NULL) { /* OK */ } else { CaretAssertMessage(0, "Has a new file type been added?"); } } /** * Called when fiber orientation file combo box changed. * * @param indx * Index of combo box that was changed. */ void CiftiConnectivityMatrixViewController::fiberOrientationFileComboBoxActivated(int indx) { CaretAssertVectorIndex(m_fiberOrientationFileComboBoxes, indx); CiftiMappableConnectivityMatrixDataFile* matrixFile = NULL; CiftiFiberTrajectoryFile* trajFile = NULL; getFileAtIndex(indx, matrixFile, trajFile); CaretAssertMessage(trajFile, "Selected orientation file but trajectory file is invalid."); QComboBox* cb = m_fiberOrientationFileComboBoxes[indx]; void* ptr = cb->itemData(indx, Qt::UserRole).value(); CaretAssert(ptr); CiftiFiberOrientationFile* orientFile = (CiftiFiberOrientationFile*)ptr; std::vector orientationFiles; GuiManager::get()->getBrain()->getConnectivityFiberOrientationFiles(orientationFiles); if (std::find(orientationFiles.begin(), orientationFiles.end(), orientFile) == orientationFiles.end()) { CaretAssertMessage(0, "Selected fiber orientation file is no longer valid."); return; } trajFile->setMatchingFiberOrientationFile(orientFile); updateOtherCiftiConnectivityMatrixViewControllers(); EventManager::get()->sendEvent(EventGraphicsUpdateAllWindows().getPointer()); } /** * Called when copy tool button is clicked. * * @param indx * Index of copy tool button that was clicked. */ void CiftiConnectivityMatrixViewController::copyToolButtonClicked(int indx) { CursorDisplayScoped cursor; cursor.showWaitCursor(); CiftiMappableConnectivityMatrixDataFile* matrixFile = NULL; CiftiFiberTrajectoryFile* trajFile = NULL; getFileAtIndex(indx, matrixFile, trajFile); bool errorFlag = false; AString errorMessage; const AString directoryName = GuiManager::get()->getBrain()->getCurrentDirectory(); if (matrixFile != NULL) { CiftiBrainordinateScalarFile* scalarFile = CiftiBrainordinateScalarFile::newInstanceFromRowInCiftiConnectivityMatrixFile(matrixFile, directoryName, errorMessage); if (scalarFile != NULL) { EventDataFileAdd dataFileAdd(scalarFile); EventManager::get()->sendEvent(dataFileAdd.getPointer()); if (dataFileAdd.isError()) { errorMessage = dataFileAdd.getErrorMessage(); errorFlag = true; } } else { errorFlag = true; } } else if (trajFile != NULL) { CiftiFiberTrajectoryFile* newTrajFile = trajFile->newFiberTrajectoryFileFromLoadedRowData(directoryName, errorMessage); if (newTrajFile != NULL) { EventDataFileAdd dataFileAdd(newTrajFile); EventManager::get()->sendEvent(dataFileAdd.getPointer()); if (dataFileAdd.isError()) { errorMessage = dataFileAdd.getErrorMessage(); errorFlag = true; } } else { errorFlag = true; } } else { CaretAssertMessage(0, "Has a new file type been added?"); } EventManager::get()->sendEvent(EventUserInterfaceUpdate().getPointer()); cursor.restoreCursor(); if (errorFlag) { WuQMessageBox::errorOk(m_fileCopyToolButtons[indx], errorMessage); } } ///** // * Update graphics and GUI after // */ //void //CiftiConnectivityMatrixViewController::updateUserInterfaceAndGraphicsWindow() //{ // updateOtherCiftiConnectivityMatrixViewControllers(); // // EventManager::get()->sendEvent(EventSurfaceColoringInvalidate().getPointer()); // EventManager::get()->sendEvent(EventGraphicsUpdateAllWindows().getPointer()); //} /** * Update other connectivity view controllers other than 'this' instance * that contain the same connectivity file. */ void CiftiConnectivityMatrixViewController::updateOtherCiftiConnectivityMatrixViewControllers() { for (std::set::iterator iter = s_allCiftiConnectivityMatrixViewControllers.begin(); iter != s_allCiftiConnectivityMatrixViewControllers.end(); iter++) { CiftiConnectivityMatrixViewController* clvc = *iter; if (clvc != this) { clvc->updateViewController(); } } } /** * Receive events from the event manager. * * @param event * Event sent by event manager. */ void CiftiConnectivityMatrixViewController::receiveEvent(Event* event) { if (event->getEventType() == EventTypeEnum::EVENT_USER_INTERFACE_UPDATE) { EventUserInterfaceUpdate* uiEvent = dynamic_cast(event); CaretAssert(uiEvent); // if (uiEvent->isUpdateForWindow(this->browserWindowIndex)) { if (uiEvent->isConnectivityUpdate() || uiEvent->isToolBoxUpdate()) { this->updateViewController(); uiEvent->setEventProcessed(); } // } } } connectome-workbench-1.2.3+git41-gc4c6c90/src/GuiQt/CiftiConnectivityMatrixViewController.h000066400000000000000000000103141300200146000314660ustar00rootroot00000000000000#ifndef __CIFTI_CONNECTIVITY_MATRIX_VIEW_CONTROLLER__H_ #define __CIFTI_CONNECTIVITY_MATRIX_VIEW_CONTROLLER__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 "EventListenerInterface.h" class QCheckBox; class QComboBox; class QGridLayout; class QLineEdit; class QSignalMapper; class QToolButton; namespace caret { class CiftiMappableConnectivityMatrixDataFile; class CiftiFiberTrajectoryFile; class CiftiConnectivityMatrixViewController : public QWidget, EventListenerInterface { Q_OBJECT public: CiftiConnectivityMatrixViewController(const Qt::Orientation orientation, QWidget* parent); virtual ~CiftiConnectivityMatrixViewController(); void receiveEvent(Event* event); private slots: void enabledCheckBoxClicked(int); void layerCheckBoxClicked(int); void copyToolButtonClicked(int); void fiberOrientationFileComboBoxActivated(int); private: CiftiConnectivityMatrixViewController(const CiftiConnectivityMatrixViewController&); CiftiConnectivityMatrixViewController& operator=(const CiftiConnectivityMatrixViewController&); // void updateUserInterfaceAndGraphicsWindow(); void updateViewController(); void updateOtherCiftiConnectivityMatrixViewControllers(); void updateFiberOrientationComboBoxes(); void getFileAtIndex(const int32_t indx, CiftiMappableConnectivityMatrixDataFile* &ciftiMatrixFileOut, CiftiFiberTrajectoryFile* &ciftiTrajFileOut); std::vector m_fileEnableCheckBoxes; std::vector m_layerCheckBoxes; std::vector m_fileNameLineEdits; std::vector m_fileCopyToolButtons; std::vector m_fiberOrientationFileComboBoxes; QGridLayout* m_gridLayout; QSignalMapper* m_signalMapperFileEnableCheckBox; QSignalMapper* m_signalMapperLayerCheckBox; QSignalMapper* m_signalMapperFileCopyToolButton; QSignalMapper* m_signalMapperFiberOrientationFileComboBox; static std::set s_allCiftiConnectivityMatrixViewControllers; static int COLUMN_ENABLE_CHECKBOX; static int COLUMN_LAYER_CHECKBOX; static int COLUMN_COPY_BUTTON; static int COLUMN_NAME_LINE_EDIT; static int COLUMN_ORIENTATION_FILE_COMBO_BOX; }; #ifdef __CIFTI_CONNECTIVITY_MATRIX_VIEW_CONTROLLER_DECLARE__ std::set CiftiConnectivityMatrixViewController::s_allCiftiConnectivityMatrixViewControllers; int CiftiConnectivityMatrixViewController::COLUMN_ENABLE_CHECKBOX = 0; int CiftiConnectivityMatrixViewController::COLUMN_LAYER_CHECKBOX = 1; int CiftiConnectivityMatrixViewController::COLUMN_COPY_BUTTON = 2; int CiftiConnectivityMatrixViewController::COLUMN_NAME_LINE_EDIT = 3; int CiftiConnectivityMatrixViewController::COLUMN_ORIENTATION_FILE_COMBO_BOX = 4; #endif // __CIFTI_CONNECTIVITY_MATRIX_VIEW_CONTROLLER_DECLARE__ } // namespace #endif //__CIFTI_CONNECTIVITY_MATRIX_VIEW_CONTROLLER__H_ connectome-workbench-1.2.3+git41-gc4c6c90/src/GuiQt/CiftiParcelSelectionComboBox.cxx000066400000000000000000000100351300200146000300040ustar00rootroot00000000000000 /*LICENSE_START*/ /* * Copyright (C) 2015 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 __CIFTI_PARCEL_SELECTION_COMBO_BOX_DECLARE__ #include "CiftiParcelSelectionComboBox.h" #undef __CIFTI_PARCEL_SELECTION_COMBO_BOX_DECLARE__ #include #include "AStringNaturalComparison.h" #include "CaretAssert.h" #include "CaretLogger.h" #include "CiftiParcelsMap.h" using namespace caret; /** * \class caret::CiftiParcelSelectionComboBox * \brief ComboBox for selection of a Parcel from a CiftiParcelsMap. * \ingroup GuiQt */ /** * Constructor. */ CiftiParcelSelectionComboBox::CiftiParcelSelectionComboBox(QObject* parent) : WuQWidget(parent) { m_comboBox = new QComboBox(); QObject::connect(m_comboBox, SIGNAL(activated(const QString&)), this, SIGNAL(parcelNameSelected(const QString&))); } /** * Destructor. */ CiftiParcelSelectionComboBox::~CiftiParcelSelectionComboBox() { } /** * @return The QComboBox for adding to a layout, enalbling, etc. */ QWidget* CiftiParcelSelectionComboBox::getWidget() { return m_comboBox; } /** * Update the combo box with the given parcels map. If NULL, * combo box will be empty. * * @param parcelsMap * Parcels map inserted into combo box. */ void CiftiParcelSelectionComboBox::updateComboBox(const CiftiParcelsMap* parcelsMap) { QString selectedParcelName = m_comboBox->currentText(); m_comboBox->clear(); if (parcelsMap != NULL) { std::set sortedNames; const std::vector& allParcels = parcelsMap->getParcels(); for (std::vector::const_iterator parcelIter = allParcels.begin(); parcelIter != allParcels.end(); parcelIter++) { sortedNames.insert(parcelIter->m_name); } for (std::set::iterator iter = sortedNames.begin(); iter != sortedNames.end(); iter++) { m_comboBox->addItem(*iter); } // QStringList parcelNamesList = QStringList::fromSet(sortedNames); // parcelNamesList.append(parcelIter->m_name); // // m_comboBox->addItems(parcelNamesList); } if ( ! selectedParcelName.isEmpty()) { if (m_comboBox->findText(selectedParcelName) < 0) { selectedParcelName = ""; } } if (selectedParcelName.isEmpty()) { if (m_comboBox->count() > 0) { selectedParcelName = m_comboBox->itemText(0); } } if ( ! selectedParcelName.isEmpty()) { setSelectedParcelName(selectedParcelName); } } /** * @return Name of selected parcel. */ AString CiftiParcelSelectionComboBox::getSelectedParcelName() { return m_comboBox->currentText(); } /** * Set the selected parcel to the parcel with the given name. */ void CiftiParcelSelectionComboBox::setSelectedParcelName(const QString& parcelName) { const int32_t parcelNameIndex = m_comboBox->findText(parcelName); if (parcelNameIndex >= 0) { m_comboBox->setCurrentIndex(parcelNameIndex); } else { CaretLogWarning("Parcel named \"" + parcelName + "\" is not valid name in CiftiParcelSelectionComboBox"); } } connectome-workbench-1.2.3+git41-gc4c6c90/src/GuiQt/CiftiParcelSelectionComboBox.h000066400000000000000000000041061300200146000274330ustar00rootroot00000000000000#ifndef __CIFTI_PARCEL_SELECTION_COMBO_BOX_H__ #define __CIFTI_PARCEL_SELECTION_COMBO_BOX_H__ /*LICENSE_START*/ /* * Copyright (C) 2015 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 "AString.h" #include "WuQWidget.h" class QComboBox; namespace caret { class CiftiParcelsMap; class CiftiParcelSelectionComboBox : public WuQWidget { Q_OBJECT public: CiftiParcelSelectionComboBox(QObject* parent); virtual ~CiftiParcelSelectionComboBox(); virtual QWidget* getWidget(); void updateComboBox(const CiftiParcelsMap* parcelsMap); AString getSelectedParcelName(); public slots: void setSelectedParcelName(const QString& parcelName); signals: void parcelNameSelected(const QString& parcelName); // ADD_NEW_METHODS_HERE private: CiftiParcelSelectionComboBox(const CiftiParcelSelectionComboBox&); CiftiParcelSelectionComboBox& operator=(const CiftiParcelSelectionComboBox&); QComboBox* m_comboBox; // ADD_NEW_MEMBERS_HERE }; #ifdef __CIFTI_PARCEL_SELECTION_COMBO_BOX_DECLARE__ // #endif // __CIFTI_PARCEL_SELECTION_COMBO_BOX_DECLARE__ } // namespace #endif //__CIFTI_PARCEL_SELECTION_COMBO_BOX_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/GuiQt/ClippingPlanesDialog.cxx000066400000000000000000000420301300200146000263500ustar00rootroot00000000000000 /*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 __CLIPPING_PLANES_DIALOG_DECLARE__ #include "ClippingPlanesDialog.h" #undef __CLIPPING_PLANES_DIALOG_DECLARE__ #include #include #include #include #include #include "BrainBrowserWindow.h" #include "BrainBrowserWindowComboBox.h" #include "BrowserTabContent.h" #include "CaretAssert.h" #include "EventGraphicsUpdateAllWindows.h" #include "EventManager.h" #include "EventUserInterfaceUpdate.h" #include "GuiManager.h" #include "WuQtUtilities.h" #include "WuQWidgetObjectGroup.h" using namespace caret; /** * \class caret::ClippingPlanesDialog * \brief Dialog for adjusting clipping planes transformation. * \ingroup GuiQt */ /** * Constructor. */ ClippingPlanesDialog::ClippingPlanesDialog(QWidget* parent) : WuQDialogNonModal("Clipping Planes", parent) { m_blockDialogUpdate = true; /*------------------------------------------------------------------------*/ /* * Create widgets */ /* * Window number */ QLabel* windowLabel = new QLabel("Workbench Window: "); m_browserWindowComboBox = new BrainBrowserWindowComboBox(BrainBrowserWindowComboBox::STYLE_NUMBER, this); m_browserWindowComboBox->getWidget()->setFixedWidth(50); QObject::connect(m_browserWindowComboBox, SIGNAL(browserWindowSelected(BrainBrowserWindow*)), this, SLOT(browserWindowComboBoxValueChanged(BrainBrowserWindow*))); QHBoxLayout* windowLayout = new QHBoxLayout(); windowLayout->addWidget(windowLabel); windowLayout->addWidget(m_browserWindowComboBox->getWidget()); windowLayout->addStretch(); /* * X, Y, Z column labels */ QLabel* xColumnLabel = new QLabel("X"); QLabel* yColumnLabel = new QLabel("Y"); QLabel* zColumnLabel = new QLabel("Z"); const int spinBoxWidth = 90; /* * Panning */ const double panStep = 1.0; QLabel* panLabel = new QLabel("Pan:"); m_xPanDoubleSpinBox = new QDoubleSpinBox; m_xPanDoubleSpinBox->setMinimum(-100000.0); m_xPanDoubleSpinBox->setMaximum( 100000.0); m_xPanDoubleSpinBox->setSingleStep(panStep); m_xPanDoubleSpinBox->setDecimals(2); m_xPanDoubleSpinBox->setFixedWidth(spinBoxWidth); QObject::connect(m_xPanDoubleSpinBox, SIGNAL(valueChanged(double)), this, SLOT(clippingValueChanged())); m_yPanDoubleSpinBox = new QDoubleSpinBox; m_yPanDoubleSpinBox->setMinimum(-100000.0); m_yPanDoubleSpinBox->setMaximum( 100000.0); m_yPanDoubleSpinBox->setSingleStep(panStep); m_yPanDoubleSpinBox->setDecimals(2); m_yPanDoubleSpinBox->setFixedWidth(spinBoxWidth); QObject::connect(m_yPanDoubleSpinBox, SIGNAL(valueChanged(double)), this, SLOT(clippingValueChanged())); m_zPanDoubleSpinBox = new QDoubleSpinBox; m_zPanDoubleSpinBox->setMinimum(-100000.0); m_zPanDoubleSpinBox->setMaximum( 100000.0); m_zPanDoubleSpinBox->setSingleStep(panStep); m_zPanDoubleSpinBox->setDecimals(2); m_zPanDoubleSpinBox->setFixedWidth(spinBoxWidth); QObject::connect(m_zPanDoubleSpinBox, SIGNAL(valueChanged(double)), this, SLOT(clippingValueChanged())); /* * Rotation */ const double rotationMinimum = -360.0; const double rotationMaximum = 360.0; const double rotateStep = 1.0; QLabel* rotateLabel = new QLabel("Rotate: "); m_xRotateDoubleSpinBox = new QDoubleSpinBox; m_xRotateDoubleSpinBox->setWrapping(true); m_xRotateDoubleSpinBox->setMinimum(rotationMinimum); m_xRotateDoubleSpinBox->setMaximum(rotationMaximum); m_xRotateDoubleSpinBox->setSingleStep(rotateStep); m_xRotateDoubleSpinBox->setDecimals(2); m_xRotateDoubleSpinBox->setFixedWidth(spinBoxWidth); QObject::connect(m_xRotateDoubleSpinBox, SIGNAL(valueChanged(double)), this, SLOT(clippingValueChanged())); m_yRotateDoubleSpinBox = new QDoubleSpinBox; m_yRotateDoubleSpinBox->setWrapping(true); m_yRotateDoubleSpinBox->setMinimum(rotationMinimum); m_yRotateDoubleSpinBox->setMaximum(rotationMaximum); m_yRotateDoubleSpinBox->setSingleStep(rotateStep); m_yRotateDoubleSpinBox->setDecimals(2); m_yRotateDoubleSpinBox->setFixedWidth(spinBoxWidth); QObject::connect(m_yRotateDoubleSpinBox, SIGNAL(valueChanged(double)), this, SLOT(clippingValueChanged())); m_zRotateDoubleSpinBox = new QDoubleSpinBox; m_zRotateDoubleSpinBox->setWrapping(true); m_zRotateDoubleSpinBox->setMinimum(rotationMinimum); m_zRotateDoubleSpinBox->setMaximum(rotationMaximum); m_zRotateDoubleSpinBox->setSingleStep(rotateStep); m_zRotateDoubleSpinBox->setDecimals(2); m_zRotateDoubleSpinBox->setFixedWidth(spinBoxWidth); QObject::connect(m_zRotateDoubleSpinBox, SIGNAL(valueChanged(double)), this, SLOT(clippingValueChanged())); /* * Thickness */ const double thicknessMinimum = 0.0; const double thicknessMaximum = 1000000.0; const double thicknessStep = 1.0; QLabel* thicknessLabel = new QLabel("Thickness (mm)"); m_xThicknessDoubleSpinBox = new QDoubleSpinBox(); m_xThicknessDoubleSpinBox->setMinimum(thicknessMinimum); m_xThicknessDoubleSpinBox->setMaximum(thicknessMaximum); m_xThicknessDoubleSpinBox->setSingleStep(thicknessStep); m_xThicknessDoubleSpinBox->setDecimals(2); m_xThicknessDoubleSpinBox->setFixedWidth(spinBoxWidth); QObject::connect(m_xThicknessDoubleSpinBox, SIGNAL(valueChanged(double)), this, SLOT(clippingValueChanged())); m_yThicknessDoubleSpinBox = new QDoubleSpinBox(); m_yThicknessDoubleSpinBox->setMinimum(thicknessMinimum); m_yThicknessDoubleSpinBox->setMaximum(thicknessMaximum); m_yThicknessDoubleSpinBox->setSingleStep(thicknessStep); m_yThicknessDoubleSpinBox->setDecimals(2); m_yThicknessDoubleSpinBox->setFixedWidth(spinBoxWidth); QObject::connect(m_yThicknessDoubleSpinBox, SIGNAL(valueChanged(double)), this, SLOT(clippingValueChanged())); m_zThicknessDoubleSpinBox = new QDoubleSpinBox(); m_zThicknessDoubleSpinBox->setMinimum(thicknessMinimum); m_zThicknessDoubleSpinBox->setMaximum(thicknessMaximum); m_zThicknessDoubleSpinBox->setSingleStep(thicknessStep); m_zThicknessDoubleSpinBox->setDecimals(2); m_zThicknessDoubleSpinBox->setFixedWidth(spinBoxWidth); QObject::connect(m_zThicknessDoubleSpinBox, SIGNAL(valueChanged(double)), this, SLOT(clippingValueChanged())); m_clippingWidgetGroup = new WuQWidgetObjectGroup(this); m_clippingWidgetGroup->add(m_xPanDoubleSpinBox); m_clippingWidgetGroup->add(m_yPanDoubleSpinBox); m_clippingWidgetGroup->add(m_zPanDoubleSpinBox); m_clippingWidgetGroup->add(m_xRotateDoubleSpinBox); m_clippingWidgetGroup->add(m_yRotateDoubleSpinBox); m_clippingWidgetGroup->add(m_zRotateDoubleSpinBox); m_clippingWidgetGroup->add(m_xThicknessDoubleSpinBox); m_clippingWidgetGroup->add(m_yThicknessDoubleSpinBox); m_clippingWidgetGroup->add(m_zThicknessDoubleSpinBox); /* * Show clipping box checkbox */ m_displayClippingBoxCheckBox = new QCheckBox("Show Clipping Box Outline"); QObject::connect(m_displayClippingBoxCheckBox, SIGNAL(clicked(bool)), this, SLOT(clippingValueChanged())); /*------------------------------------------------------------------------*/ /* * Layout widgets */ /* * Columns for grid layout */ int column = 0; const int COLUMN_LABEL = column++; const int COLUMN_X = column++; const int COLUMN_Y = column++; const int COLUMN_Z = column++; QWidget* gridWidget = new QWidget(); QGridLayout* gridLayout = new QGridLayout(gridWidget); WuQtUtilities::setLayoutSpacingAndMargins(gridLayout, 4, 4); int row = 0; gridLayout->addWidget(xColumnLabel, row, COLUMN_X, Qt::AlignHCenter); gridLayout->addWidget(yColumnLabel, row, COLUMN_Y, Qt::AlignHCenter); gridLayout->addWidget(zColumnLabel, row, COLUMN_Z, Qt::AlignHCenter); row++; gridLayout->addWidget(panLabel, row, COLUMN_LABEL); gridLayout->addWidget(m_xPanDoubleSpinBox, row, COLUMN_X); gridLayout->addWidget(m_yPanDoubleSpinBox, row, COLUMN_Y); gridLayout->addWidget(m_zPanDoubleSpinBox, row, COLUMN_Z); row++; gridLayout->addWidget(rotateLabel, row, COLUMN_LABEL); gridLayout->addWidget(m_xRotateDoubleSpinBox, row, COLUMN_X); gridLayout->addWidget(m_yRotateDoubleSpinBox, row, COLUMN_Y); gridLayout->addWidget(m_zRotateDoubleSpinBox, row, COLUMN_Z); row++; gridLayout->addWidget(thicknessLabel, row, COLUMN_LABEL); gridLayout->addWidget(m_xThicknessDoubleSpinBox, row, COLUMN_X); gridLayout->addWidget(m_yThicknessDoubleSpinBox, row, COLUMN_Y); gridLayout->addWidget(m_zThicknessDoubleSpinBox, row, COLUMN_Z); /*------------------------------------------------------------------------*/ /* * Finish up */ QWidget* widget = new QWidget(); QVBoxLayout* layout = new QVBoxLayout(widget); WuQtUtilities::setLayoutSpacingAndMargins(layout, 4, 4); layout->addLayout(windowLayout); layout->addWidget(m_displayClippingBoxCheckBox); layout->addWidget(WuQtUtilities::createHorizontalLineWidget()); layout->addWidget(gridWidget); widget->setFixedSize(widget->sizeHint()); /* * Remove apply button by using an empty name */ setApplyButtonText(""); m_resetPushButton = addUserPushButton("Reset", QDialogButtonBox::NoRole); setCentralWidget(widget, WuQDialog::SCROLL_AREA_NEVER); /* * No auto default button processing (Qt highlights button) */ disableAutoDefaultForAllPushButtons(); EventManager::get()->addEventListener(this, EventTypeEnum::EVENT_USER_INTERFACE_UPDATE); setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); m_blockDialogUpdate = false; } /** * Destructor. */ ClippingPlanesDialog::~ClippingPlanesDialog() { EventManager::get()->removeAllEventsFromListener(this); } /** * Called when a user (added) push button is clicked. * * @param userPushButton * Button that was clicked. */ WuQDialogNonModal::DialogUserButtonResult ClippingPlanesDialog::userButtonPressed(QPushButton* userPushButton) { if (userPushButton == m_resetPushButton) { BrainBrowserWindow* bbw = m_browserWindowComboBox->getSelectedBrowserWindow(); if (bbw != NULL) { BrowserTabContent* btc = bbw->getBrowserTabContent(); if (btc != NULL) { btc->resetClippingPlaneTransformation(); updateContent(btc->getTabNumber()); updateGraphicsWindow(); } } } else { CaretAssert(0); } return WuQDialogNonModal::RESULT_NONE; } /** * Called when window number combo box value changed. */ void ClippingPlanesDialog::browserWindowComboBoxValueChanged(BrainBrowserWindow* browserWindow) { int32_t windowIndex = -1; if (browserWindow != NULL) { windowIndex = browserWindow->getBrowserWindowIndex(); } updateContent(windowIndex); } /** * Called when a clipping value is changed. */ void ClippingPlanesDialog::clippingValueChanged() { const float panning[3] = { m_xPanDoubleSpinBox->value(), m_yPanDoubleSpinBox->value(), m_zPanDoubleSpinBox->value() }; const float rotation[3] = { m_xRotateDoubleSpinBox->value(), m_yRotateDoubleSpinBox->value(), m_zRotateDoubleSpinBox->value() }; const float thickness[3] ={ m_xThicknessDoubleSpinBox->value(), m_yThicknessDoubleSpinBox->value(), m_zThicknessDoubleSpinBox->value() }; /* * Update, set, and validate selected browser window */ BrainBrowserWindow* bbw = m_browserWindowComboBox->getSelectedBrowserWindow(); if (bbw != NULL) { BrowserTabContent* btc = bbw->getBrowserTabContent(); if (btc != NULL) { btc->setClippingPlaneTransformation(panning, rotation, thickness, m_displayClippingBoxCheckBox->isChecked()); updateGraphicsWindow(); } } } /** * Update the selected graphics window. */ void ClippingPlanesDialog::updateGraphicsWindow() { EventManager::get()->sendEvent(EventGraphicsUpdateAllWindows().getPointer()); EventManager::get()->sendEvent(EventUserInterfaceUpdate().getPointer()); } /** * Gets called when the dialog gains focus. */ void ClippingPlanesDialog::focusGained() { updateDialog(); } /** * Update the dialog. */ void ClippingPlanesDialog::updateDialog() { m_browserWindowComboBox->updateComboBox(); updateContent(m_browserWindowComboBox->getSelectedBrowserWindowIndex()); } /** * Update the content in the dialog * @param browserWindowIndexIn * Index of the browser window. */ void ClippingPlanesDialog::updateContent(const int32_t browserWindowIndexIn) { /* * May get updates when graphics are redrawn by this dialog * and not doing this could result in infinite loop */ if (m_blockDialogUpdate) { return; } /* * Update, set, and validate selected browser window */ m_browserWindowComboBox->updateComboBox(); m_browserWindowComboBox->setBrowserWindowByIndex(browserWindowIndexIn); const int32_t browserWindowIndex = m_browserWindowComboBox->getSelectedBrowserWindowIndex(); BrainBrowserWindow* bbw = GuiManager::get()->getBrowserWindowByWindowIndex(browserWindowIndex); if (bbw != NULL) { BrowserTabContent* btc = bbw->getBrowserTabContent(); if (btc != NULL) { m_clippingWidgetGroup->blockAllSignals(true); float panning[3]; float rotation[3]; float thickness[3]; bool displayClippingBox; btc->getClippingPlaneTransformation(panning, rotation, thickness, displayClippingBox); m_xPanDoubleSpinBox->setValue(panning[0]); m_yPanDoubleSpinBox->setValue(panning[1]); m_zPanDoubleSpinBox->setValue(panning[2]); m_xRotateDoubleSpinBox->setValue(rotation[0]); m_yRotateDoubleSpinBox->setValue(rotation[1]); m_zRotateDoubleSpinBox->setValue(rotation[2]); m_xThicknessDoubleSpinBox->setValue(thickness[0]); m_yThicknessDoubleSpinBox->setValue(thickness[1]); m_zThicknessDoubleSpinBox->setValue(thickness[2]); m_displayClippingBoxCheckBox->setChecked(displayClippingBox); m_clippingWidgetGroup->blockAllSignals(false); } } } /** * Receive events from the event manager. * * @param event * Event sent by event manager. */ void ClippingPlanesDialog::receiveEvent(Event* event) { if (event->getEventType() == EventTypeEnum::EVENT_USER_INTERFACE_UPDATE) { EventUserInterfaceUpdate* updateEvent = dynamic_cast(event); CaretAssert(updateEvent); updateEvent->setEventProcessed(); updateDialog(); } } connectome-workbench-1.2.3+git41-gc4c6c90/src/GuiQt/ClippingPlanesDialog.h000066400000000000000000000060711300200146000260020ustar00rootroot00000000000000#ifndef __CLIPPING_PLANES_DIALOG_H__ #define __CLIPPING_PLANES_DIALOG_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 "WuQDialogNonModal.h" #include "EventListenerInterface.h" class QCheckBox; class QDoubleSpinBox; class QPushButton; namespace caret { class BrainBrowserWindow; class BrainBrowserWindowComboBox; class WuQWidgetObjectGroup; class ClippingPlanesDialog : public WuQDialogNonModal, public EventListenerInterface { Q_OBJECT public: ClippingPlanesDialog(QWidget* parent); virtual ~ClippingPlanesDialog(); void updateDialog(); void updateContent(const int32_t browserWindowIndex); // ADD_NEW_METHODS_HERE virtual void receiveEvent(Event* event); private slots: void browserWindowComboBoxValueChanged(BrainBrowserWindow* browserWindow); void clippingValueChanged(); protected: void focusGained(); virtual DialogUserButtonResult userButtonPressed(QPushButton* userPushButton); private: void updateGraphicsWindow(); ClippingPlanesDialog(const ClippingPlanesDialog&); ClippingPlanesDialog& operator=(const ClippingPlanesDialog&); QPushButton* m_resetPushButton; BrainBrowserWindowComboBox* m_browserWindowComboBox; WuQWidgetObjectGroup* m_clippingWidgetGroup; QDoubleSpinBox* m_xPanDoubleSpinBox; QDoubleSpinBox* m_yPanDoubleSpinBox; QDoubleSpinBox* m_zPanDoubleSpinBox; QDoubleSpinBox* m_xRotateDoubleSpinBox; QDoubleSpinBox* m_yRotateDoubleSpinBox; QDoubleSpinBox* m_zRotateDoubleSpinBox; QDoubleSpinBox* m_xThicknessDoubleSpinBox; QDoubleSpinBox* m_yThicknessDoubleSpinBox; QDoubleSpinBox* m_zThicknessDoubleSpinBox; QCheckBox* m_displayClippingBoxCheckBox; bool m_blockDialogUpdate; // ADD_NEW_MEMBERS_HERE }; #ifdef __CLIPPING_PLANES_DIALOG_DECLARE__ // #endif // __CLIPPING_PLANES_DIALOG_DECLARE__ } // namespace #endif //__CLIPPING_PLANES_DIALOG_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/GuiQt/ColorEditorWidget.cxx000066400000000000000000000233601300200146000257160ustar00rootroot00000000000000 /*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 #define __COLOR_EDITOR_WIDGET_DECLARE__ #include "ColorEditorWidget.h" #undef __COLOR_EDITOR_WIDGET_DECLARE__ #include "WuQFactory.h" #include "WuQWidgetObjectGroup.h" #include "WuQtUtilities.h" using namespace caret; /** * \class caret::ColorEditorWidget * \brief Widget for editing RGBA colors. * \ingroup GuiQt */ /** * Constructor. */ ColorEditorWidget::ColorEditorWidget(const bool alphaControlEnabled, QWidget* parent) : QWidget(parent) { this->colorSwatchWidget = new QWidget(); this->colorSwatchWidget->setFixedHeight(25); QLabel* redLabel = new QLabel("Red"); this->redSpinBox = WuQFactory::newSpinBox(); this->redSpinBox->setRange(0, 255); this->redSpinBox->setSingleStep(1); QObject::connect(this->redSpinBox, SIGNAL(valueChanged(int)), this, SLOT(redValueChanged(int))); this->redSlider = new QSlider(); this->redSlider->setRange(0, 255); this->redSlider->setOrientation(Qt::Horizontal); QObject::connect(this->redSlider, SIGNAL(valueChanged(int)), this, SLOT(redValueChanged(int))); QLabel* greenLabel = new QLabel("Green"); this->greenSpinBox = WuQFactory::newSpinBox(); this->greenSpinBox->setRange(0, 255); this->greenSpinBox->setSingleStep(1); QObject::connect(this->greenSpinBox, SIGNAL(valueChanged(int)), this, SLOT(greenValueChanged(int))); this->greenSlider = new QSlider(); this->greenSlider->setRange(0, 255); this->greenSlider->setOrientation(Qt::Horizontal); QObject::connect(this->greenSlider, SIGNAL(valueChanged(int)), this, SLOT(greenValueChanged(int))); QLabel* blueLabel = new QLabel("Blue"); this->blueSpinBox = WuQFactory::newSpinBox(); this->blueSpinBox->setRange(0, 255); this->blueSpinBox->setSingleStep(1); QObject::connect(this->blueSpinBox, SIGNAL(valueChanged(int)), this, SLOT(blueValueChanged(int))); this->blueSlider = new QSlider(); this->blueSlider->setRange(0, 255); this->blueSlider->setOrientation(Qt::Horizontal); QObject::connect(this->blueSlider, SIGNAL(valueChanged(int)), this, SLOT(blueValueChanged(int))); QLabel* alphaLabel = NULL; this->alphaSpinBox = NULL; if (alphaControlEnabled) { alphaLabel = new QLabel("Alpha"); this->alphaSpinBox = WuQFactory::newSpinBox(); this->alphaSpinBox->setRange(0, 255); this->alphaSpinBox->setSingleStep(1); QObject::connect(this->alphaSpinBox, SIGNAL(valueChanged(int)), this, SLOT(alphaValueChanged(int))); this->alphaSlider = new QSlider(); this->alphaSlider->setRange(0, 255); this->alphaSlider->setOrientation(Qt::Horizontal); QObject::connect(this->alphaSlider, SIGNAL(valueChanged(int)), this, SLOT(alphaValueChanged(int))); } int columnCount = 0; const int COLUMN_NAME = columnCount++; const int COLUMN_SLIDER = columnCount++; const int COLUMN_SPINBOX = columnCount++; const int COLUMN_LAST = columnCount; int row = 0; QGridLayout* gridLayout = new QGridLayout(this); WuQtUtilities::setLayoutSpacingAndMargins(gridLayout, 2, 2); gridLayout->addWidget(this->colorSwatchWidget, row, 0, 1, COLUMN_LAST); row++; gridLayout->addWidget(redLabel, row, COLUMN_NAME); gridLayout->addWidget(this->redSlider, row, COLUMN_SLIDER); gridLayout->addWidget(this->redSpinBox, row, COLUMN_SPINBOX); row++; gridLayout->addWidget(greenLabel, row, COLUMN_NAME); gridLayout->addWidget(this->greenSlider, row, COLUMN_SLIDER); gridLayout->addWidget(this->greenSpinBox, row, COLUMN_SPINBOX); row++; gridLayout->addWidget(blueLabel, row, COLUMN_NAME); gridLayout->addWidget(this->blueSlider, row, COLUMN_SLIDER); gridLayout->addWidget(this->blueSpinBox, row, COLUMN_SPINBOX); row++; if (this->alphaSpinBox != NULL) { gridLayout->addWidget(alphaLabel, row, COLUMN_NAME); gridLayout->addWidget(this->alphaSlider, row, COLUMN_SLIDER); gridLayout->addWidget(this->alphaSpinBox, row, COLUMN_SPINBOX); row++; } this->controlsWidgetGroup = new WuQWidgetObjectGroup(this); this->controlsWidgetGroup->add(this->redSpinBox); this->controlsWidgetGroup->add(this->redSlider); this->controlsWidgetGroup->add(this->greenSpinBox); this->controlsWidgetGroup->add(this->greenSlider); this->controlsWidgetGroup->add(this->blueSpinBox); this->controlsWidgetGroup->add(this->blueSlider); if (this->alphaSpinBox != NULL) { this->controlsWidgetGroup->add(this->alphaSpinBox); this->controlsWidgetGroup->add(this->alphaSlider); } } /** * Destructor. */ ColorEditorWidget::~ColorEditorWidget() { } /** * Set the color in the control with RGBA * values ranging 0.0 to 1.0. * @param rgba * Input RGBA values. */ void ColorEditorWidget::setColor(const float rgba[4]) { const int32_t intRGBA[4] = { std::min(static_cast(rgba[0] * 255.0), 255), std::min(static_cast(rgba[1] * 255.0), 255), std::min(static_cast(rgba[2] * 255.0), 255), std::min(static_cast(rgba[3] * 255.0), 255) }; this->setColor(intRGBA); } /** * Get the color in the control with RGBA * values ranging 0.0 to 1.0. * @param rgba * Output RGBA values. */ void ColorEditorWidget::getColor(float rgba[4]) const { int intRGBA[4]; this->getColor(intRGBA); rgba[0] = intRGBA[0] / 255.0; rgba[1] = intRGBA[1] / 255.0; rgba[2] = intRGBA[2] / 255.0; rgba[3] = intRGBA[3] / 255.0; } /** * Set the color in the control with RGBA * values ranging 0 to 255. * @param rgba * Input RGBA values. */ void ColorEditorWidget::setColor(const int rgba[4]) { this->controlsWidgetGroup->blockAllSignals(true); this->redSpinBox->setValue(rgba[0]); this->redSlider->setValue(rgba[0]); this->greenSpinBox->setValue(rgba[1]); this->greenSlider->setValue(rgba[1]); this->blueSpinBox->setValue(rgba[2]); this->blueSlider->setValue(rgba[2]); if (this->alphaSpinBox != NULL) { this->alphaSpinBox->setValue(rgba[3]); this->alphaSlider->setValue(rgba[3]); } this->updateColorSwatch(); this->controlsWidgetGroup->blockAllSignals(false); } /** * Get the color in the control with RGBA * values ranging 0 to 255 * @param rgba * Output RGBA values. */ void ColorEditorWidget::getColor(int rgba[4]) const { rgba[0] = this->redSpinBox->value(); rgba[1] = this->greenSpinBox->value(); rgba[2] = this->blueSpinBox->value(); if (this->alphaSpinBox != NULL) { rgba[3] = this->alphaSpinBox->value(); } else { rgba[3] = 255; } } /** * Called when a red component value is changed. */ void ColorEditorWidget::redValueChanged(int value) { this->controlsWidgetGroup->blockAllSignals(true); this->redSlider->setValue(value); this->redSpinBox->setValue(value); this->emitColorChangedSignal(); this->controlsWidgetGroup->blockAllSignals(false); } /** * Called when a green component value is changed. */ void ColorEditorWidget::greenValueChanged(int value) { this->controlsWidgetGroup->blockAllSignals(true); this->greenSlider->setValue(value); this->greenSpinBox->setValue(value); this->emitColorChangedSignal(); this->controlsWidgetGroup->blockAllSignals(false); } /** * Called when a blue component value is changed. */ void ColorEditorWidget::blueValueChanged(int value) { this->controlsWidgetGroup->blockAllSignals(true); this->blueSlider->setValue(value); this->blueSpinBox->setValue(value); this->emitColorChangedSignal(); this->controlsWidgetGroup->blockAllSignals(false); } /** * Called when a alpha component value is changed. */ void ColorEditorWidget::alphaValueChanged(int value) { this->controlsWidgetGroup->blockAllSignals(true); this->alphaSlider->setValue(value); this->alphaSpinBox->setValue(value); this->emitColorChangedSignal(); this->controlsWidgetGroup->blockAllSignals(false); } void ColorEditorWidget::updateColorSwatch() { int rgb[4]; this->getColor(rgb); QPalette pal; pal.setColor(QPalette::Window, QColor(rgb[0], rgb[1], rgb[2])); this->colorSwatchWidget->setAutoFillBackground(true); this->colorSwatchWidget->setBackgroundRole(QPalette::Window); this->colorSwatchWidget->setPalette(pal); } /** * Call to emit the color changed signals. */ void ColorEditorWidget::emitColorChangedSignal() { this->updateColorSwatch(); float rgba[4]; this->getColor(rgba); emit colorChanged(rgba); int intRgba[4]; this->getColor(intRgba); emit colorChanged(intRgba); } connectome-workbench-1.2.3+git41-gc4c6c90/src/GuiQt/ColorEditorWidget.h000066400000000000000000000050031300200146000253350ustar00rootroot00000000000000#ifndef __COLOR_EDITOR_WIDGET__H_ #define __COLOR_EDITOR_WIDGET__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 class QSlider; class QSpinBox; namespace caret { class WuQWidgetObjectGroup; class ColorEditorWidget : public QWidget { Q_OBJECT public: ColorEditorWidget(const bool alphaControlEnabled = false, QWidget* parent = 0); virtual ~ColorEditorWidget(); void setColor(const float rgba[4]); void getColor(float rgba[4]) const; void setColor(const int rgba[4]); void getColor(int rgba[4]) const; signals: void colorChanged(const float*); void colorChanged(const int*); public slots: void redValueChanged(int); void blueValueChanged(int); void greenValueChanged(int); void alphaValueChanged(int); void emitColorChangedSignal(); private: ColorEditorWidget(const ColorEditorWidget&); ColorEditorWidget& operator=(const ColorEditorWidget&); private: void updateColorSwatch(); WuQWidgetObjectGroup* controlsWidgetGroup; QWidget* colorSwatchWidget; QSpinBox* redSpinBox; QSpinBox* greenSpinBox; QSpinBox* blueSpinBox; QSpinBox* alphaSpinBox; QSlider* redSlider; QSlider* greenSlider; QSlider* blueSlider; QSlider* alphaSlider; }; #ifdef __COLOR_EDITOR_WIDGET_DECLARE__ // #endif // __COLOR_EDITOR_WIDGET_DECLARE__ } // namespace #endif //__COLOR_EDITOR_WIDGET__H_ connectome-workbench-1.2.3+git41-gc4c6c90/src/GuiQt/CursorDisplayScoped.cxx000066400000000000000000000043411300200146000262640ustar00rootroot00000000000000 /*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 __CURSOR_DISPLAY_SCOPED_DECLARE__ #include "CursorDisplayScoped.h" #undef __CURSOR_DISPLAY_SCOPED_DECLARE__ #include "CursorManager.h" #include "GuiManager.h" using namespace caret; /** * \class caret::CursorDisplayScoped * \brief Displays a override cursor * \ingroup GuiQt * * When one of the 'show' methods is called, * displays a cursor that overrides all other cursors in the * application until an instance of this goes out of scope or the * method restoreCursor() is called. */ /** * Constructor that displays a wait cursor. * */ CursorDisplayScoped::CursorDisplayScoped() : CaretObject() { this->isCursorActive = false; } /** * Destructor. */ CursorDisplayScoped::~CursorDisplayScoped() { this->restoreCursor(); } /** * Display the given cursor. * * @param cursor * Cursor that is displayed. */ void CursorDisplayScoped::showCursor(const QCursor& cursor) { this->isCursorActive = true; QApplication::setOverrideCursor(cursor); QApplication::processEvents(); } /** * Display the wait cursor. */ void CursorDisplayScoped::showWaitCursor() { this->showCursor(Qt::WaitCursor); } /** * Restore the default cursor. */ void CursorDisplayScoped::restoreCursor() { if (this->isCursorActive) { QApplication::restoreOverrideCursor(); QApplication::processEvents(); this->isCursorActive = false; } } connectome-workbench-1.2.3+git41-gc4c6c90/src/GuiQt/CursorDisplayScoped.h000066400000000000000000000032061300200146000257100ustar00rootroot00000000000000#ifndef __CURSOR_DISPLAY_SCOPED_H_ #define __CURSOR_DISPLAY_SCOPED_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" class QCursor; namespace caret { class CursorDisplayScoped : public CaretObject { public: CursorDisplayScoped(); void showWaitCursor(); void showCursor(const QCursor& cursor); void restoreCursor(); virtual ~CursorDisplayScoped(); private: CursorDisplayScoped(const CursorDisplayScoped&); CursorDisplayScoped& operator=(const CursorDisplayScoped&); bool isCursorActive; private: }; #ifdef __CURSOR_DISPLAY_SCOPED_DECLARE__ // #endif // __CURSOR_DISPLAY_SCOPED_DECLARE__ } // namespace #endif //__CURSOR_DISPLAY_SCOPED__H_ connectome-workbench-1.2.3+git41-gc4c6c90/src/GuiQt/CursorEnum.cxx000066400000000000000000000300771300200146000244320ustar00rootroot00000000000000 /*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 __CURSOR_ENUM_DECLARE__ #include "CursorEnum.h" #undef __CURSOR_ENUM_DECLARE__ #include "CaretAssert.h" using namespace caret; /** * \class caret::CursorEnum * \brief Enumerated type for Cursors (both Qt and custom) * \ingroup GuiQt * * An enumerated type for cursors. Some cursors are * from Qt and others are created using bitmaps. */ /** * Constructor. * * @param enumValue * An enumerated value. * @param name * Name of enumerated value. * * @param guiName * User-friendly name for use in user-interface. */ CursorEnum::CursorEnum(const Enum enumValue, const AString& name, const AString& guiName, const Qt::CursorShape qtCursorShape) { this->enumValue = enumValue; this->integerCode = integerCodeCounter++; this->name = name; this->guiName = guiName; this->qtCursorShape = qtCursorShape; } /** * Destructor. */ CursorEnum::~CursorEnum() { } /** * Initialize the enumerated metadata. */ void CursorEnum::initialize() { if (initializedFlag) { return; } initializedFlag = true; enumData.push_back(CursorEnum(CURSOR_DEFAULT, "CURSOR_DEFAULT", "Default Cursor", Qt::ArrowCursor)); enumData.push_back(CursorEnum(CURSOR_ARROW, "CURSOR_ARROW", "Arrow Cursor", Qt::ArrowCursor)); enumData.push_back(CursorEnum(CURSOR_CLOSED_HAND, "CURSOR_CLOSED_HAND", "Closed Hand Cursor", Qt::ClosedHandCursor)); enumData.push_back(CursorEnum(CURSOR_CROSS, "CURSOR_CROSS", "Cross Cursor", Qt::CrossCursor)); enumData.push_back(CursorEnum(CURSOR_DRAWING_PEN, "CURSOR_DRAWING_PEN", "Drawing Pen Cursor", Qt::ArrowCursor)); enumData.push_back(CursorEnum(CURSOR_FOUR_ARROWS, "CURSOR_FOUR_ARROWS", "Four Arrows Cursor", Qt::SizeAllCursor)); enumData.push_back(CursorEnum(CURSOR_POINTING_HAND, "CURSOR_POINTING_HAND", "Pointing Hand Cursor", Qt::PointingHandCursor)); enumData.push_back(CursorEnum(CURSOR_RESIZE_BOTTOM_LEFT_TOP_RIGHT, "CURSOR_RESIZE_BOTTOM_LEFT_TOP_RIGHT", "Resize arrows pointing bottom left and top right Cursor", Qt::SizeBDiagCursor)); enumData.push_back(CursorEnum(CURSOR_RESIZE_BOTTOM_RIGHT_TOP_LEFT, "CURSOR_RESIZE_BOTTOM_RIGHT_TOP_LEFT", "Resize arrows pointing top left and bottom right Cursor", Qt::SizeFDiagCursor)); enumData.push_back(CursorEnum(CURSOR_RESIZE_HORIZONTAL, "CURSOR_RESIZE_HORIZONTAL", "Resize Horizontal Cursor", Qt::SizeHorCursor)); enumData.push_back(CursorEnum(CURSOR_RESIZE_VERTICAL, "CURSOR_RESIZE_VERTICAL", "Resize Vertical Cursor", Qt::SizeVerCursor)); enumData.push_back(CursorEnum(CURSOR_ROTATION, "CURSOR_ROTATION", "Rotation Cursor", Qt::ArrowCursor)); enumData.push_back(CursorEnum(CURSOR_WAIT, "CURSOR_WAIT", "Wait Cursor", Qt::WaitCursor)); enumData.push_back(CursorEnum(CURSOR_WHATS_THIS, "CURSOR_WHATS_THIS", "What's this Cursor", Qt::WhatsThisCursor)); } /** * 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 CursorEnum* CursorEnum::findData(const Enum enumValue) { if (initializedFlag == false) initialize(); size_t num = enumData.size(); for (size_t i = 0; i < num; i++) { const CursorEnum* d = &enumData[i]; if (d->enumValue == enumValue) { return d; } } return NULL; } /** * Get the Qt Cursor corresponding to the enumerated type. * @param enumValue * Enumerated value. * @return * String representing enumerated value. */ Qt::CursorShape CursorEnum::toQtCursorShape(Enum enumValue) { if (initializedFlag == false) initialize(); const CursorEnum* enumInstance = findData(enumValue); return enumInstance->qtCursorShape; } /** * Get a string representation of the enumerated type. * @param enumValue * Enumerated value. * @return * String representing enumerated value. */ AString CursorEnum::toName(Enum enumValue) { if (initializedFlag == false) initialize(); const CursorEnum* 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. */ CursorEnum::Enum CursorEnum::fromName(const AString& name, bool* isValidOut) { if (initializedFlag == false) initialize(); bool validFlag = false; Enum enumValue = CURSOR_DEFAULT; for (std::vector::iterator iter = enumData.begin(); iter != enumData.end(); iter++) { const CursorEnum& 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 CursorEnum")); } return enumValue; } /** * Get a GUI string representation of the enumerated type. * @param enumValue * Enumerated value. * @return * String representing enumerated value. */ AString CursorEnum::toGuiName(Enum enumValue) { if (initializedFlag == false) initialize(); const CursorEnum* 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. */ CursorEnum::Enum CursorEnum::fromGuiName(const AString& guiName, bool* isValidOut) { if (initializedFlag == false) initialize(); bool validFlag = false; Enum enumValue = CURSOR_DEFAULT; for (std::vector::iterator iter = enumData.begin(); iter != enumData.end(); iter++) { const CursorEnum& 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 CursorEnum")); } return enumValue; } /** * Get the integer code for a data type. * * @return * Integer code for data type. */ int32_t CursorEnum::toIntegerCode(Enum enumValue) { if (initializedFlag == false) initialize(); const CursorEnum* 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. */ CursorEnum::Enum CursorEnum::fromIntegerCode(const int32_t integerCode, bool* isValidOut) { if (initializedFlag == false) initialize(); bool validFlag = false; Enum enumValue = CURSOR_DEFAULT; for (std::vector::iterator iter = enumData.begin(); iter != enumData.end(); iter++) { const CursorEnum& 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 CursorEnum")); } 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 CursorEnum::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 CursorEnum::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(CursorEnum::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 CursorEnum::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(CursorEnum::toGuiName(iter->enumValue)); } if (isSorted) { std::sort(allGuiNames.begin(), allGuiNames.end()); } } connectome-workbench-1.2.3+git41-gc4c6c90/src/GuiQt/CursorEnum.h000066400000000000000000000101731300200146000240520ustar00rootroot00000000000000#ifndef __CURSOR_ENUM__H_ #define __CURSOR_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 CursorEnum { public: /** * Enumerated values. */ enum Enum { /** The default cursor which is usually the parent widget's cursor */ CURSOR_DEFAULT, /** Arrow (typically same as CURSOR_DEFAULT but overrides cursor provided by parent widget, Qt::ArrowCursor) */ CURSOR_ARROW, /** Closed Hand */ CURSOR_CLOSED_HAND, /** Cross (plus symbol) */ CURSOR_CROSS, /** Drawing Pen */ CURSOR_DRAWING_PEN, /** Size All "four arrows", Qt::SizeAllCursor */ CURSOR_FOUR_ARROWS, /** Pointing hand, Qt::PointingHandCursor*/ CURSOR_POINTING_HAND, /** Resize arrows pointing bottom left and top right */ CURSOR_RESIZE_BOTTOM_LEFT_TOP_RIGHT, /** Resize arrows pointing top left and bottom right */ CURSOR_RESIZE_BOTTOM_RIGHT_TOP_LEFT, /** Resize Horizontal */ CURSOR_RESIZE_HORIZONTAL, /** Resize Vertical */ CURSOR_RESIZE_VERTICAL, /** Rotation Cursor */ CURSOR_ROTATION, /** Wait, Qt::WaitCursor*/ CURSOR_WAIT, /** What's this? Arrow with question mark */ CURSOR_WHATS_THIS }; ~CursorEnum(); static Qt::CursorShape toQtCursorShape(Enum enumValue); 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: CursorEnum(const Enum enumValue, const AString& name, const AString& guiName, const Qt::CursorShape qtCursorShape); static const CursorEnum* 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; /** The corresponding Qt Cursor Shape */ Qt::CursorShape qtCursorShape; }; #ifdef __CURSOR_ENUM_DECLARE__ std::vector CursorEnum::enumData; bool CursorEnum::initializedFlag = false; int32_t CursorEnum::integerCodeCounter = 0; #endif // __CURSOR_ENUM_DECLARE__ } // namespace #endif //__CURSOR_ENUM__H_ connectome-workbench-1.2.3+git41-gc4c6c90/src/GuiQt/CursorManager.cxx000066400000000000000000000206621300200146000250770ustar00rootroot00000000000000 /*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 #define __CURSOR_MANAGER_DECLARE__ #include "CursorManager.h" #undef __CURSOR_MANAGER_DECLARE__ #include "WuQtUtilities.h" using namespace caret; /** * \class caret::CursorManager * \brief Manages cursors * \ingroup GuiQt * Provides cursors, some predefined by Qt and * others unique to workbench. */ /** * Constructor. */ CursorManager::CursorManager() : CaretObject() { m_fourArrowsCursor = createFourArrowsCursor(); m_penCursor = this->loadCursor(":Cursor/pen_eraser.png", 6, 32 - 7, Qt::UpArrowCursor); m_rotationCursor = createRotationCursor(); } /** * Destructor. */ CursorManager::~CursorManager() { } /** * Set the given cursor for the given widget. * @param widget * Widget that has its cursor set. * @param cursor * Cursor for the widget. */ void CursorManager::setCursorForWidget(QWidget* widget, const CursorEnum::Enum cursor) const { switch (cursor) { case CursorEnum::CURSOR_DEFAULT: widget->unsetCursor(); break; case CursorEnum::CURSOR_DRAWING_PEN: widget->setCursor(m_penCursor); break; case CursorEnum::CURSOR_FOUR_ARROWS: widget->setCursor(m_fourArrowsCursor); break; case CursorEnum::CURSOR_ROTATION: widget->setCursor(m_rotationCursor); break; default: widget->setCursor(CursorEnum::toQtCursorShape(cursor)); break; } } /** * Load an image and create a cursor using the image. * * @param filename * Name of file containing the image. * @param hotSpotX * Hot spot (location in cursor reported to GUI) * @param hotSpotY * Hot spot (location in cursor reported to GUI) * @param cursorShapeIfImageLoadingFails * Cursor shape used if loading the image fails. * @return * Cursor that was created. */ QCursor CursorManager::loadCursor(const QString& filename, const int hotSpotX, const int hotSpotY, const Qt::CursorShape& cursorShapeIfImageLoadingFails) const { QPixmap cursorPixmap; if (WuQtUtilities::loadPixmap(filename, cursorPixmap)) { QCursor cursor(cursorPixmap, hotSpotX, hotSpotY); return cursor; } return QCursor(cursorShapeIfImageLoadingFails); } /** * @return A four-arrows cursor */ QCursor CursorManager::createFourArrowsCursor() { /* * Create image into which to draw */ const int width = 32; const int height = width; QImage image(width, height, QImage::Format_ARGB32); image.fill(Qt::transparent); /* * Create painter with black for filling and white for outline */ QPainter painter(&image); painter.setRenderHint(QPainter::Antialiasing, false); painter.setBackgroundMode(Qt::OpaqueMode); QPen pen = painter.pen(); pen.setColor(Qt::white); pen.setWidth(1); painter.setPen(pen); QBrush brush(QColor(0, 0, 0), Qt::SolidPattern); painter.setBrush(brush); /* * Four arrows symbol is symmetric */ const int middle = width / 2; const int a = 2; const int b = 8; const int c = middle - 6; const int d = middle - 2; const int e = middle; const int f = width - d; const int g = width - c; const int h = width - b; const int i = width - a; /* * Points for polygon filled with black */ QPolygon polygon; polygon.push_back(QPoint(a, e)); polygon.push_back(QPoint(b, g)); polygon.push_back(QPoint(b, f)); polygon.push_back(QPoint(d, f)); polygon.push_back(QPoint(d, h)); polygon.push_back(QPoint(c, h)); polygon.push_back(QPoint(e, i)); polygon.push_back(QPoint(g, h)); polygon.push_back(QPoint(f, h)); polygon.push_back(QPoint(f, f)); polygon.push_back(QPoint(h, f)); polygon.push_back(QPoint(h, g)); polygon.push_back(QPoint(i, e)); polygon.push_back(QPoint(h, c)); polygon.push_back(QPoint(h, d)); polygon.push_back(QPoint(f, d)); polygon.push_back(QPoint(f, b)); polygon.push_back(QPoint(g, b)); polygon.push_back(QPoint(e, a)); polygon.push_back(QPoint(c, b)); polygon.push_back(QPoint(d, b)); polygon.push_back(QPoint(d, d)); polygon.push_back(QPoint(b, d)); polygon.push_back(QPoint(b, c)); /* * Draw filled polygon. * Note that last point is connected to first point by drawPolygon(). */ painter.drawPolygon(polygon); /* * Draw white around arrows. * Note that drawPolyline() DOES NOT connect last point to first point so add first point again at end. * Turn anti-aliasing on for lines */ polygon.push_back(QPoint(a, e)); painter.setRenderHint(QPainter::Antialiasing, false); painter.drawPolyline(polygon); /* * Create the cursor */ QPixmap cursorPixmap = QPixmap::fromImage(image); QCursor cursor(cursorPixmap); return cursor; } /** * @return A rotation cursor */ QCursor CursorManager::createRotationCursor() { /* * Create image into which to draw */ const int width = 32; const int height = width; QImage image(width, height, QImage::Format_ARGB32); image.fill(Qt::transparent); /* * Create painter with black for filling and white for outline */ QPainter painter(&image); painter.setRenderHint(QPainter::Antialiasing, false); painter.setBackgroundMode(Qt::OpaqueMode); QPen pen = painter.pen(); pen.setColor(Qt::white); pen.setWidth(1); painter.setPen(pen); QBrush brush(QColor(0, 0, 0), Qt::SolidPattern); painter.setBrush(brush); /* * points left to right in rotation symbol */ const int a = 2; const int b = 6; const int c = width - 14; const int d = width - 10; const int e = width - 8; const int f = width - 6; const int g = width - 2; /* * Points top to bottom in rotation symbol */ const int h = 2; const int i = 6; const int j = height - 8; const int k = height - 2; /* * Points for polygon filled with black */ QPolygon polygon; polygon.push_back(QPoint(a, k)); polygon.push_back(QPoint(b, k)); polygon.push_back(QPoint(b, i)); polygon.push_back(QPoint(d, i)); polygon.push_back(QPoint(d, j)); polygon.push_back(QPoint(c, j)); polygon.push_back(QPoint(e, k)); polygon.push_back(QPoint(g, j)); polygon.push_back(QPoint(f, j)); polygon.push_back(QPoint(f, h)); polygon.push_back(QPoint(a, h)); /* * Draw filled polygon. * Note that last point is connected to first point by drawPolygon(). */ painter.drawPolygon(polygon); /* * Draw white around arrows. * Note that drawPolyline() DOES NOT connect last point to first point so add first point again at end. * Turn anti-aliasing on for lines */ polygon.push_back(QPoint(a, k)); painter.setRenderHint(QPainter::Antialiasing, false); painter.drawPolyline(polygon); /* * Create the cursor */ QPixmap cursorPixmap = QPixmap::fromImage(image); QCursor cursor(cursorPixmap); return cursor; } connectome-workbench-1.2.3+git41-gc4c6c90/src/GuiQt/CursorManager.h000066400000000000000000000037711300200146000245260ustar00rootroot00000000000000#ifndef __CURSOR_MANAGER__H_ #define __CURSOR_MANAGER__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 "CaretObject.h" #include "CursorEnum.h" class QWidget; namespace caret { class CursorManager : public CaretObject { public: CursorManager(); virtual ~CursorManager(); void setCursorForWidget(QWidget* widget, const CursorEnum::Enum cursor) const; private: CursorManager(const CursorManager&); CursorManager& operator=(const CursorManager&); QCursor loadCursor(const QString& filename, const int hotSpotX, const int hotSpotY, const Qt::CursorShape& cursorShapeIfImageLoadingFails) const; QCursor createFourArrowsCursor(); QCursor createRotationCursor(); QCursor m_penCursor; QCursor m_fourArrowsCursor; QCursor m_rotationCursor; }; #ifdef __CURSOR_MANAGER_DECLARE__ // #endif // __CURSOR_MANAGER_DECLARE__ } // namespace #endif //__CURSOR_MANAGER__H_ connectome-workbench-1.2.3+git41-gc4c6c90/src/GuiQt/CustomViewDialog.cxx000066400000000000000000001145521300200146000255560ustar00rootroot00000000000000 /*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 __CUSTOM_VIEW_DIALOG_DECLARE__ #include "CustomViewDialog.h" #undef __CUSTOM_VIEW_DIALOG_DECLARE__ #include #include #include #include #include #include #include #include #include #include #include "BrainBrowserWindow.h" #include "BrainBrowserWindowComboBox.h" #include "BrowserTabContent.h" #include "CaretAssert.h" #include "CaretPreferences.h" #include "EventBrowserWindowGraphicsRedrawn.h" #include "EventGraphicsUpdateOneWindow.h" #include "EventManager.h" #include "GuiManager.h" #include "Matrix4x4.h" #include "Model.h" #include "ModelTransform.h" #include "SessionManager.h" #include "WuQDataEntryDialog.h" #include "WuQListWidget.h" #include "WuQMessageBox.h" #include "WuQtUtilities.h" #include "WuQWidgetObjectGroup.h" using namespace caret; /** * \class caret::CustomViewDialog * \brief Dialog for creation of custom views (orientations). * \ingroup GuiQt */ /** * Constructor. */ CustomViewDialog::CustomViewDialog(QWidget* parent) : WuQDialogNonModal("Custom Orientation", parent) { m_blockDialogUpdate = true; /* * Remove apply button by using an empty name */ setApplyButtonText(""); /* * Create the controls */ QWidget* customViewWidget = createCustomViewWidget(); QWidget* transformWidget = createTransformsWidget(); m_copyWidget = createCopyWidget(); /* * Layout for dialog */ QWidget* widget = new QWidget(); QHBoxLayout* layout = new QHBoxLayout(widget); layout->setSpacing(layout->spacing() / 2); layout->addWidget(transformWidget, 0, Qt::AlignVCenter); layout->addWidget(m_copyWidget, 0, Qt::AlignVCenter); layout->addWidget(customViewWidget, 0, Qt::AlignVCenter); setCentralWidget(widget, WuQDialog::SCROLL_AREA_NEVER); /* * No auto default button processing (Qt highlights button) */ disableAutoDefaultForAllPushButtons(); EventManager::get()->addEventListener(this, EventTypeEnum::EVENT_BROWSER_WINDOW_GRAPHICS_HAVE_BEEN_REDRAWN); setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); m_blockDialogUpdate = false; } /** * Destructor. */ CustomViewDialog::~CustomViewDialog() { EventManager::get()->removeAllEventsFromListener(this); } /** * @return The Custom View widget. */ QWidget* CustomViewDialog::createCustomViewWidget() { /* * View selection list widget */ m_customViewListWidget = new WuQListWidget(); m_customViewListWidget->setFixedHeight(100); m_customViewListWidget->setFixedWidth(140); m_customViewListWidget->setSelectionMode(QListWidget::SingleSelection); QObject::connect(m_customViewListWidget, SIGNAL(itemClicked(QListWidgetItem*)), this, SLOT(customViewSelected())); QObject::connect(m_customViewListWidget, SIGNAL(itemDoubleClicked(QListWidgetItem*)), this, SLOT(customViewSelectedAndApplied())); m_newCustomViewPushButton = new QPushButton("New..."); WuQtUtilities::setWordWrappedToolTip(m_newCustomViewPushButton, "Create a new Custom View by entering its name. The view will use the current transformation values."); QObject::connect(m_newCustomViewPushButton, SIGNAL(clicked()), this, SLOT(newCustomViewPushButtonClicked())); m_deleteCustomViewPushButton = new QPushButton("Delete..."); WuQtUtilities::setWordWrappedToolTip(m_deleteCustomViewPushButton, "Delete the selected Custom View."); QObject::connect(m_deleteCustomViewPushButton, SIGNAL(clicked()), this, SLOT(deleteCustomViewPushButtonClicked())); WuQtUtilities::matchWidgetWidths(m_newCustomViewPushButton, m_deleteCustomViewPushButton); QGroupBox* groupBox = new QGroupBox("Custom Orientation"); QVBoxLayout* layout = new QVBoxLayout(groupBox); WuQtUtilities::setLayoutSpacingAndMargins(layout, 4, 2); layout->addWidget(m_customViewListWidget, 100, Qt::AlignHCenter); layout->addWidget(m_newCustomViewPushButton, 0, Qt::AlignHCenter); layout->addWidget(m_deleteCustomViewPushButton, 0, Qt::AlignHCenter); layout->addStretch(); groupBox->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); return groupBox; } /** * Called when a custom view is selected. */ void CustomViewDialog::customViewSelected() { } /** * Called when a custom view name is double-clicked. */ void CustomViewDialog::customViewSelectedAndApplied() { copyToTransformPushButtonClicked(); } /** * @return Name of selected custom view.. */ AString CustomViewDialog::getSelectedCustomViewName() { AString viewName; QListWidgetItem* lwi = m_customViewListWidget->currentItem(); if (lwi != NULL) { viewName = lwi->text(); } return viewName; } /** * Load content of custom view list widget. * * @param selectedName * If not empty, this name will be selected. */ void CustomViewDialog::loadCustomViewListWidget(const AString& selectedName) { /* * Cannot use getSelectedUserView() since the item returned may be invalid * if items have been removed. */ QString previousViewName = getSelectedCustomViewName(); if (selectedName.isEmpty() == false) { previousViewName = selectedName; } int defaultSelectedIndex = m_customViewListWidget->currentRow(); m_customViewListWidget->clear(); CaretPreferences* prefs = getCaretPreferences(); prefs->readCustomViews(); const std::vector > customViewNamesAndComments = prefs->getCustomViewNamesAndComments(); const int32_t numViews = static_cast(customViewNamesAndComments.size()); for (int32_t i = 0; i < numViews; i++) { const AString viewName = customViewNamesAndComments[i].first; const AString comment = customViewNamesAndComments[i].second; if (viewName == previousViewName) { defaultSelectedIndex = i; } QListWidgetItem* lwi = new QListWidgetItem(viewName); if (comment.isEmpty() == false) { lwi->setToolTip(WuQtUtilities::createWordWrappedToolTipText(comment)); } m_customViewListWidget->addItem(lwi); } if (defaultSelectedIndex >= numViews) { defaultSelectedIndex = numViews - 1; } if (defaultSelectedIndex < 0) { defaultSelectedIndex = 0; } if ((defaultSelectedIndex >= 0) && (defaultSelectedIndex < m_customViewListWidget->count())) { m_customViewListWidget->setCurrentRow(defaultSelectedIndex); customViewSelected(); m_customViewListWidget->scrollToItem(m_customViewListWidget->currentItem()); } const bool haveViews = (numViews > 0); m_copyWidget->setEnabled(haveViews); const bool haveSelectedView = (getSelectedCustomViewName().isEmpty() == false); m_deleteCustomViewPushButton->setEnabled(haveSelectedView); } /** * @return Create and return the copy buttons widget. */ QWidget* CustomViewDialog::createCopyWidget() { QPushButton* copyToCustomViewPushButton = new QPushButton("Copy -->"); WuQtUtilities::setWordWrappedToolTip(copyToCustomViewPushButton, "Copy the Transform's values into the selected Custom Orientation."); QObject::connect(copyToCustomViewPushButton, SIGNAL(clicked()), this, SLOT(copyToCustomViewPushButtonClicked())); QPushButton* copyToTransformPushButton = new QPushButton("<-- Load"); WuQtUtilities::setWordWrappedToolTip(copyToTransformPushButton, "Load the Custom Orientation's transform values into the Transform."); QObject::connect(copyToTransformPushButton, SIGNAL(clicked()), this, SLOT(copyToTransformPushButtonClicked())); QWidget* widget = new QWidget(); QVBoxLayout* layout = new QVBoxLayout(widget); layout->addWidget(copyToCustomViewPushButton); layout->addWidget(copyToTransformPushButton); widget->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); return widget; } /** * Called when Copy To Custom View push buttton is clicked. */ void CustomViewDialog::copyToCustomViewPushButtonClicked() { CaretPreferences* prefs = getCaretPreferences(); ModelTransform modelTransform; if (prefs->getCustomView(getSelectedCustomViewName(), modelTransform)) { moveTransformToCustomView(modelTransform); prefs->addOrReplaceCustomView(modelTransform); } } /** * Move the transform values to the given user view. * * @param userView * User View into which transform values are moved. */ void CustomViewDialog::moveTransformToCustomView(ModelTransform& modelTransform) { double panX, panY, panZ, rotX, rotY, rotZ, obRotX, obRotY, obRotZ, zoom, rightFlatX, rightFlatY, rightFlatZoom; getTransformationControlValues(panX, panY, panZ, rotX, rotY, rotZ, obRotX, obRotY, obRotZ, zoom, rightFlatX, rightFlatY, rightFlatZoom); Matrix4x4 rotationMatrix; rotationMatrix.setRotation(rotX, rotY, rotZ); float rotationMatrixArray[4][4]; rotationMatrix.getMatrix(rotationMatrixArray); Matrix4x4 obliqueRotationMatrix; obliqueRotationMatrix.setRotation(obRotX, obRotY, obRotZ); float obliqueRotationMatrixArray[4][4]; obliqueRotationMatrix.getMatrix(obliqueRotationMatrixArray); modelTransform.setPanningRotationMatrixAndZoom(panX, panY, panZ, rotationMatrixArray, obliqueRotationMatrixArray, zoom, rightFlatX, rightFlatY, rightFlatZoom); } /** * Called when Move To Transform push buttton is clicked. */ void CustomViewDialog::copyToTransformPushButtonClicked() { CaretPreferences* prefs = getCaretPreferences(); const AString customViewName = getSelectedCustomViewName(); ModelTransform modelTransform; if (prefs->getCustomView(customViewName, modelTransform)) { float panX, panY, panZ, rotationMatrixArray[4][4], obliqueRotationMatrixArray[4][4], zoom, rightFlatX, rightFlatY, rightFlatZoom; modelTransform.getPanningRotationMatrixAndZoom(panX, panY, panZ, rotationMatrixArray, obliqueRotationMatrixArray, zoom, rightFlatX, rightFlatY, rightFlatZoom); Matrix4x4 rotationMatrix; rotationMatrix.setMatrix(rotationMatrixArray); double rotX, rotY, rotZ; rotationMatrix.getRotation(rotX, rotY, rotZ); Matrix4x4 obliqueRotationMatrix; obliqueRotationMatrix.setMatrix(obliqueRotationMatrixArray); double obRotX, obRotY, obRotZ; obliqueRotationMatrix.getRotation(obRotX, obRotY, obRotZ); setTransformationControlValues(panX, panY, panZ, rotX, rotY, rotZ, obRotX, obRotY, obRotZ, zoom, rightFlatX, rightFlatY, rightFlatZoom); transformValueChanged(); } } /** * @return The Transform widget. */ QWidget* CustomViewDialog::createTransformsWidget() { const int spinBoxWidth = 90; /* * Window number */ QLabel* windowLabel = new QLabel("Workbench Window: "); m_browserWindowComboBox = new BrainBrowserWindowComboBox(BrainBrowserWindowComboBox::STYLE_NUMBER, this); m_browserWindowComboBox->getWidget()->setFixedWidth(spinBoxWidth); QObject::connect(m_browserWindowComboBox, SIGNAL(browserWindowSelected(BrainBrowserWindow*)), this, SLOT(browserWindowComboBoxValueChanged(BrainBrowserWindow*))); /* * Panning */ const double panStep = 1.0; QLabel* panLabel = new QLabel("Pan (X,Y):"); m_xPanDoubleSpinBox = new QDoubleSpinBox; m_xPanDoubleSpinBox->setMinimum(-100000.0); m_xPanDoubleSpinBox->setMaximum( 100000.0); m_xPanDoubleSpinBox->setSingleStep(panStep); m_xPanDoubleSpinBox->setDecimals(2); m_xPanDoubleSpinBox->setFixedWidth(spinBoxWidth); QObject::connect(m_xPanDoubleSpinBox, SIGNAL(valueChanged(double)), this, SLOT(transformValueChanged())); m_yPanDoubleSpinBox = new QDoubleSpinBox; m_yPanDoubleSpinBox->setMinimum(-100000.0); m_yPanDoubleSpinBox->setMaximum( 100000.0); m_yPanDoubleSpinBox->setSingleStep(panStep); m_yPanDoubleSpinBox->setDecimals(2); m_yPanDoubleSpinBox->setFixedWidth(spinBoxWidth); QObject::connect(m_yPanDoubleSpinBox, SIGNAL(valueChanged(double)), this, SLOT(transformValueChanged())); m_zPanDoubleSpinBox = new QDoubleSpinBox; m_zPanDoubleSpinBox->setMinimum(-100000.0); m_zPanDoubleSpinBox->setMaximum( 100000.0); m_zPanDoubleSpinBox->setSingleStep(panStep); m_zPanDoubleSpinBox->setDecimals(2); m_zPanDoubleSpinBox->setFixedWidth(spinBoxWidth); QObject::connect(m_zPanDoubleSpinBox, SIGNAL(valueChanged(double)), this, SLOT(transformValueChanged())); /* * Rotation */ const double rotationMinimum = -360.0; const double rotationMaximum = 360.0; const double rotateStep = 1.0; QLabel* rotateLabel = new QLabel("Rotate (X,Y,Z): "); m_xRotateDoubleSpinBox = new QDoubleSpinBox; m_xRotateDoubleSpinBox->setWrapping(true); m_xRotateDoubleSpinBox->setMinimum(rotationMinimum); m_xRotateDoubleSpinBox->setMaximum(rotationMaximum); m_xRotateDoubleSpinBox->setSingleStep(rotateStep); m_xRotateDoubleSpinBox->setDecimals(2); m_xRotateDoubleSpinBox->setFixedWidth(spinBoxWidth); QObject::connect(m_xRotateDoubleSpinBox, SIGNAL(valueChanged(double)), this, SLOT(transformValueChanged())); m_yRotateDoubleSpinBox = new QDoubleSpinBox; m_yRotateDoubleSpinBox->setWrapping(true); m_yRotateDoubleSpinBox->setMinimum(rotationMinimum); m_yRotateDoubleSpinBox->setMaximum(rotationMaximum); m_yRotateDoubleSpinBox->setSingleStep(rotateStep); m_yRotateDoubleSpinBox->setDecimals(2); m_yRotateDoubleSpinBox->setFixedWidth(spinBoxWidth); QObject::connect(m_yRotateDoubleSpinBox, SIGNAL(valueChanged(double)), this, SLOT(transformValueChanged())); m_zRotateDoubleSpinBox = new QDoubleSpinBox; m_zRotateDoubleSpinBox->setWrapping(true); m_zRotateDoubleSpinBox->setMinimum(rotationMinimum); m_zRotateDoubleSpinBox->setMaximum(rotationMaximum); m_zRotateDoubleSpinBox->setSingleStep(rotateStep); m_zRotateDoubleSpinBox->setDecimals(2); m_zRotateDoubleSpinBox->setFixedWidth(spinBoxWidth); QObject::connect(m_zRotateDoubleSpinBox, SIGNAL(valueChanged(double)), this, SLOT(transformValueChanged())); /* * Rotation */ QLabel* obliqueRotateLabel = new QLabel("Oblique Rotate (X,Y,Z): "); m_xObliqueRotateDoubleSpinBox = new QDoubleSpinBox; m_xObliqueRotateDoubleSpinBox->setWrapping(true); m_xObliqueRotateDoubleSpinBox->setMinimum(rotationMinimum); m_xObliqueRotateDoubleSpinBox->setMaximum(rotationMaximum); m_xObliqueRotateDoubleSpinBox->setSingleStep(rotateStep); m_xObliqueRotateDoubleSpinBox->setDecimals(2); m_xObliqueRotateDoubleSpinBox->setFixedWidth(spinBoxWidth); QObject::connect(m_xObliqueRotateDoubleSpinBox, SIGNAL(valueChanged(double)), this, SLOT(transformValueChanged())); m_yObliqueRotateDoubleSpinBox = new QDoubleSpinBox; m_yObliqueRotateDoubleSpinBox->setWrapping(true); m_yObliqueRotateDoubleSpinBox->setMinimum(rotationMinimum); m_yObliqueRotateDoubleSpinBox->setMaximum(rotationMaximum); m_yObliqueRotateDoubleSpinBox->setSingleStep(rotateStep); m_yObliqueRotateDoubleSpinBox->setDecimals(2); m_yObliqueRotateDoubleSpinBox->setFixedWidth(spinBoxWidth); QObject::connect(m_yObliqueRotateDoubleSpinBox, SIGNAL(valueChanged(double)), this, SLOT(transformValueChanged())); m_zObliqueRotateDoubleSpinBox = new QDoubleSpinBox; m_zObliqueRotateDoubleSpinBox->setWrapping(true); m_zObliqueRotateDoubleSpinBox->setMinimum(rotationMinimum); m_zObliqueRotateDoubleSpinBox->setMaximum(rotationMaximum); m_zObliqueRotateDoubleSpinBox->setSingleStep(rotateStep); m_zObliqueRotateDoubleSpinBox->setDecimals(2); m_zObliqueRotateDoubleSpinBox->setFixedWidth(spinBoxWidth); QObject::connect(m_zObliqueRotateDoubleSpinBox, SIGNAL(valueChanged(double)), this, SLOT(transformValueChanged())); /* * Zoom */ const double zoomStep = 0.01; QLabel* zoomLabel = new QLabel("Zoom: "); m_zoomDoubleSpinBox = new QDoubleSpinBox; m_zoomDoubleSpinBox->setMinimum(0.001); m_zoomDoubleSpinBox->setMaximum(10000.0); m_zoomDoubleSpinBox->setSingleStep(zoomStep); m_zoomDoubleSpinBox->setDecimals(3); m_zoomDoubleSpinBox->setFixedWidth(spinBoxWidth); QObject::connect(m_zoomDoubleSpinBox, SIGNAL(valueChanged(double)), this, SLOT(transformValueChanged())); /* * Flat offset */ QLabel* rightFlatOffsetLabel = new QLabel("Right Flat Offset: "); m_xRightFlatMapSpinBox = new QDoubleSpinBox; m_xRightFlatMapSpinBox->setMinimum(-100000.0); m_xRightFlatMapSpinBox->setMaximum( 100000.0); m_xRightFlatMapSpinBox->setSingleStep(panStep); m_xRightFlatMapSpinBox->setDecimals(2); m_xRightFlatMapSpinBox->setFixedWidth(spinBoxWidth); QObject::connect(m_xRightFlatMapSpinBox, SIGNAL(valueChanged(double)), this, SLOT(transformValueChanged())); m_yRightFlatMapSpinBox = new QDoubleSpinBox; m_yRightFlatMapSpinBox->setMinimum(-100000.0); m_yRightFlatMapSpinBox->setMaximum( 100000.0); m_yRightFlatMapSpinBox->setSingleStep(panStep); m_yRightFlatMapSpinBox->setDecimals(2); m_xRightFlatMapSpinBox->setFixedWidth(spinBoxWidth); QObject::connect(m_yRightFlatMapSpinBox, SIGNAL(valueChanged(double)), this, SLOT(transformValueChanged())); QLabel* rightFlatZoomLabel = new QLabel("Right Flat Zoom: "); m_rightFlatMapZoomFactorSpinBox = new QDoubleSpinBox; m_rightFlatMapZoomFactorSpinBox->setMinimum(0.001); m_rightFlatMapZoomFactorSpinBox->setMaximum(10000.0); m_rightFlatMapZoomFactorSpinBox->setSingleStep(zoomStep); m_rightFlatMapZoomFactorSpinBox->setDecimals(3); m_rightFlatMapZoomFactorSpinBox->setFixedWidth(spinBoxWidth); QObject::connect(m_rightFlatMapZoomFactorSpinBox, SIGNAL(valueChanged(double)), this, SLOT(transformValueChanged())); m_transformWidgetGroup = new WuQWidgetObjectGroup(this); m_transformWidgetGroup->add(m_xPanDoubleSpinBox); m_transformWidgetGroup->add(m_yPanDoubleSpinBox); m_transformWidgetGroup->add(m_zPanDoubleSpinBox); m_transformWidgetGroup->add(m_xRotateDoubleSpinBox); m_transformWidgetGroup->add(m_yRotateDoubleSpinBox); m_transformWidgetGroup->add(m_zRotateDoubleSpinBox); m_transformWidgetGroup->add(m_xObliqueRotateDoubleSpinBox); m_transformWidgetGroup->add(m_yObliqueRotateDoubleSpinBox); m_transformWidgetGroup->add(m_zObliqueRotateDoubleSpinBox); m_transformWidgetGroup->add(m_zoomDoubleSpinBox); m_transformWidgetGroup->add(m_xRightFlatMapSpinBox); m_transformWidgetGroup->add(m_yRightFlatMapSpinBox); m_transformWidgetGroup->add(m_rightFlatMapZoomFactorSpinBox); /*------------------------------------------------------------------------*/ /* * Layout widgets */ /* * Columns for grid layout */ int column = 0; const int COLUMN_LABEL = column++; const int COLUMN_X = column++; const int COLUMN_Y = column++; const int COLUMN_Z = column++; const int COLUMN_COUNT = column++; QGroupBox* groupBox = new QGroupBox("Transform"); QGridLayout* gridLayout = new QGridLayout(groupBox); WuQtUtilities::setLayoutSpacingAndMargins(gridLayout, 4, 4); int row = 0; gridLayout->addWidget(windowLabel, row, COLUMN_LABEL); gridLayout->addWidget(m_browserWindowComboBox->getWidget(), row, COLUMN_X); row++; gridLayout->addWidget(WuQtUtilities::createHorizontalLineWidget(), row, COLUMN_LABEL, 1, COLUMN_COUNT); gridLayout->setRowMinimumHeight(row, 10.0); row++; gridLayout->addWidget(panLabel, row, COLUMN_LABEL); gridLayout->addWidget(m_xPanDoubleSpinBox, row, COLUMN_X); gridLayout->addWidget(m_yPanDoubleSpinBox, row, COLUMN_Y); gridLayout->addWidget(m_zPanDoubleSpinBox, row, COLUMN_Z); row++; gridLayout->addWidget(rotateLabel, row, COLUMN_LABEL); gridLayout->addWidget(m_xRotateDoubleSpinBox, row, COLUMN_X); gridLayout->addWidget(m_yRotateDoubleSpinBox, row, COLUMN_Y); gridLayout->addWidget(m_zRotateDoubleSpinBox, row, COLUMN_Z); row++; gridLayout->addWidget(obliqueRotateLabel, row, COLUMN_LABEL); gridLayout->addWidget(m_xObliqueRotateDoubleSpinBox, row, COLUMN_X); gridLayout->addWidget(m_yObliqueRotateDoubleSpinBox, row, COLUMN_Y); gridLayout->addWidget(m_zObliqueRotateDoubleSpinBox, row, COLUMN_Z); row++; gridLayout->addWidget(zoomLabel, row, COLUMN_LABEL); gridLayout->addWidget(m_zoomDoubleSpinBox, row, COLUMN_X); row++; gridLayout->addWidget(rightFlatOffsetLabel, row, COLUMN_LABEL); gridLayout->addWidget(m_xRightFlatMapSpinBox, row, COLUMN_X); gridLayout->addWidget(m_yRightFlatMapSpinBox, row, COLUMN_Y); row++; gridLayout->addWidget(rightFlatZoomLabel, row, COLUMN_LABEL); gridLayout->addWidget(m_rightFlatMapZoomFactorSpinBox, row, COLUMN_X); groupBox->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); return groupBox; } /** * Called when window number combo box value changed. */ void CustomViewDialog::browserWindowComboBoxValueChanged(BrainBrowserWindow* browserWindow) { int32_t windowIndex = -1; if (browserWindow != NULL) { windowIndex = browserWindow->getBrowserWindowIndex(); } updateContent(windowIndex); } /** * Called when a transform value is changed. */ void CustomViewDialog::transformValueChanged() { double panX, panY, panZ, rotX, rotY, rotZ, obRotX, obRotY, obRotZ, zoom, rightFlatX, rightFlatY, rightFlatZoom; getTransformationControlValues(panX, panY, panZ, rotX, rotY, rotZ, obRotX, obRotY, obRotZ, zoom, rightFlatX, rightFlatY, rightFlatZoom); BrainBrowserWindow* bbw = m_browserWindowComboBox->getSelectedBrowserWindow(); if (bbw != NULL) { BrowserTabContent* btc = bbw->getBrowserTabContent(); if (btc != NULL) { Model* model = btc->getModelForDisplay(); if (model != NULL) { Matrix4x4 rotationMatrix; rotationMatrix.setRotation(rotX, rotY, rotZ); float rotationMatrixArray[4][4]; rotationMatrix.getMatrix(rotationMatrixArray); Matrix4x4 obliqueRotationMatrix; obliqueRotationMatrix.setRotation(obRotX, obRotY, obRotZ); float obliqueRotationMatrixArray[4][4]; obliqueRotationMatrix.getMatrix(obliqueRotationMatrixArray); ModelTransform modelTransform; modelTransform.setPanningRotationMatrixAndZoom(panX, panY, panZ, rotationMatrixArray, obliqueRotationMatrixArray, zoom, rightFlatX, rightFlatY, rightFlatZoom); btc->setTransformationsFromModelTransform(modelTransform); updateGraphicsWindow(); } } } } /** * Update the selected graphics window. */ void CustomViewDialog::updateGraphicsWindow() { BrainBrowserWindow* bbw = m_browserWindowComboBox->getSelectedBrowserWindow(); if (bbw != NULL) { const int32_t windowIndex = bbw->getBrowserWindowIndex(); m_blockDialogUpdate = true; EventManager::get()->sendEvent(EventGraphicsUpdateOneWindow(windowIndex).getPointer()); m_blockDialogUpdate = false; } } /** * Gets called when the dialog gains focus. */ void CustomViewDialog::focusGained() { updateDialog(); } /** * Update the dialog. */ void CustomViewDialog::updateDialog() { m_browserWindowComboBox->updateComboBox(); updateContent(m_browserWindowComboBox->getSelectedBrowserWindowIndex()); } /** * Update the dialog. */ void CustomViewDialog::updateContent(const int32_t browserWindowIndexIn) { /* * May get updates when graphics are redrawn by this dialog * and not doing this could result in infinite loop */ if (m_blockDialogUpdate) { return; } /* * Update, set, and validate selected browser window */ m_browserWindowComboBox->updateComboBox(); m_browserWindowComboBox->setBrowserWindowByIndex(browserWindowIndexIn); const int32_t browserWindowIndex = m_browserWindowComboBox->getSelectedBrowserWindowIndex(); BrainBrowserWindow* bbw = GuiManager::get()->getBrowserWindowByWindowIndex(browserWindowIndex); if (bbw != NULL) { BrowserTabContent* btc = bbw->getBrowserTabContent(); if (btc != NULL) { Model* model = btc->getModelForDisplay(); if (model != NULL) { const float* panning = btc->getTranslation(); const Matrix4x4 rotationMatrix = btc->getRotationMatrix(); const float zooming = btc->getScaling(); const Matrix4x4 obliqueRotationMatrix = btc->getObliqueVolumeRotationMatrix(); double rotX, rotY, rotZ; rotationMatrix.getRotation(rotX, rotY, rotZ); double obRotX, obRotY, obRotZ; obliqueRotationMatrix.getRotation(obRotX, obRotY, obRotZ); float rightFlatX, rightFlatY; btc->getRightCortexFlatMapOffset(rightFlatX, rightFlatY); const float rightFlatZoom = btc->getRightCortexFlatMapZoomFactor(); setTransformationControlValues(panning[0], panning[1], panning[2], rotX, rotY, rotZ, obRotX, obRotY, obRotZ, zooming, rightFlatX, rightFlatY, rightFlatZoom); } } m_transformWidgetGroup->setEnabled(true); } else { m_transformWidgetGroup->setEnabled(false); } loadCustomViewListWidget(); } /** * Get the transformation values. * * @param panX * X pannning * @param panX * X pannning * @param rotX * X rotation * @param rotY * Y rotation * @param rotZ * Z rotation * @param zoom * Zooming * @param rightFlatX * Offset for right flat map. * @param rightFlat& * Offset for right flat map. * @param rightFlatZoom * Zoom for right flat map. */ void CustomViewDialog::getTransformationControlValues(double& panX, double& panY, double& panZ, double& rotX, double& rotY, double& rotZ, double& obRotX, double& obRotY, double& obRotZ, double& zoom, double& rightFlatX, double& rightFlatY, double& rightFlatZoom) const { panX = m_xPanDoubleSpinBox->value(); panY = m_yPanDoubleSpinBox->value(); panZ = m_zPanDoubleSpinBox->value(); rotX = m_xRotateDoubleSpinBox->value(); rotY = m_yRotateDoubleSpinBox->value(); rotZ = m_zRotateDoubleSpinBox->value(); obRotX = m_xObliqueRotateDoubleSpinBox->value(); obRotY = m_yObliqueRotateDoubleSpinBox->value(); obRotZ = m_zObliqueRotateDoubleSpinBox->value(); zoom = m_zoomDoubleSpinBox->value(); rightFlatX = m_xRightFlatMapSpinBox->value(); rightFlatY = m_yRightFlatMapSpinBox->value(); rightFlatZoom = m_rightFlatMapZoomFactorSpinBox->value(); } /** * Set the transformation values. * * @param panX * X pannning * @param panX * X pannning * @param rotX * X rotation * @param rotY * Y rotation * @param rotZ * Z rotation * @param zoom * Zooming * @param rightFlatX * Offset for right flat map. * @param rightFlat * Offset for right flat map. * @param rightFlatZoom * Zoom factor for right flat map. */ void CustomViewDialog::setTransformationControlValues(const double panX, const double panY, const double panZ, const double rotX, const double rotY, const double rotZ, const double obRotX, const double obRotY, const double obRotZ, const double zoom, const double rightFlatX, const double rightFlatY, const double rightFlatZoom) const { m_transformWidgetGroup->blockAllSignals(true); m_xPanDoubleSpinBox->setValue(panX); m_yPanDoubleSpinBox->setValue(panY); m_zPanDoubleSpinBox->setValue(panZ); m_xRotateDoubleSpinBox->setValue(rotX); m_yRotateDoubleSpinBox->setValue(rotY); m_zRotateDoubleSpinBox->setValue(rotZ); m_xObliqueRotateDoubleSpinBox->setValue(obRotX); m_yObliqueRotateDoubleSpinBox->setValue(obRotY); m_zObliqueRotateDoubleSpinBox->setValue(obRotZ); m_zoomDoubleSpinBox->setValue(zoom); m_xRightFlatMapSpinBox->setValue(rightFlatX); m_yRightFlatMapSpinBox->setValue(rightFlatY); m_rightFlatMapZoomFactorSpinBox->setValue(rightFlatZoom); m_transformWidgetGroup->blockAllSignals(false); } /** * @return The caret preferences. */ CaretPreferences* CustomViewDialog::getCaretPreferences() { CaretPreferences* prefs = SessionManager::get()->getCaretPreferences(); return prefs; } /** * Called when new custom view push button clicked. */ void CustomViewDialog::newCustomViewPushButtonClicked() { CaretPreferences* prefs = getCaretPreferences(); const std::vector existingCustomViewNames = prefs->getCustomViewNames(); bool createViewFlag = false; AString newViewName; AString newViewComment; bool exitLoop = false; while (exitLoop == false) { WuQDataEntryDialog ded("New Custom View", m_newCustomViewPushButton); QLineEdit* nameLineEdit = ded.addLineEditWidget("View Name"); QTextEdit* commentTextEdit = ded.addTextEdit("Comment", "", false); nameLineEdit->setFocus(); if (ded.exec() == WuQDataEntryDialog::Accepted) { newViewName = nameLineEdit->text().trimmed(); if (newViewName.isEmpty() == false) { newViewComment = commentTextEdit->toPlainText().trimmed(); /* * If custom view exists with name entered by user, * then warn the user. */ if (std::find(existingCustomViewNames.begin(), existingCustomViewNames.end(), newViewName) != existingCustomViewNames.end()) { const QString msg = ("View named \"" + newViewName + "\" already exits. Replace?"); if (WuQMessageBox::warningYesNo(m_newCustomViewPushButton, msg)) { exitLoop = true; createViewFlag = true; } } else { exitLoop = true; createViewFlag = true; } } } else { exitLoop = true; } } if (createViewFlag && (newViewName.isEmpty() == false)) { ModelTransform mt; mt.setName(newViewName); mt.setComment(newViewComment); moveTransformToCustomView(mt); prefs->addOrReplaceCustomView(mt); loadCustomViewListWidget(newViewName); } } /** * Called when save custom view push button clicked. */ void CustomViewDialog::deleteCustomViewPushButtonClicked() { const AString viewName = getSelectedCustomViewName(); if (viewName.isEmpty() == false) { const QString msg = ("Delete view named \"" + viewName + "\" ?"); if (WuQMessageBox::warningYesNo(m_newCustomViewPushButton, msg)) { CaretPreferences* prefs = getCaretPreferences(); prefs->removeCustomView(viewName); loadCustomViewListWidget(); } } } /** * Receive events from the event manager. * * @param event * Event sent by event manager. */ void CustomViewDialog::receiveEvent(Event* event) { if (event->getEventType() == EventTypeEnum::EVENT_BROWSER_WINDOW_GRAPHICS_HAVE_BEEN_REDRAWN) { EventBrowserWindowGraphicsRedrawn* redrawnEvent = dynamic_cast(event); CaretAssert(redrawnEvent); redrawnEvent->setEventProcessed(); const int32_t selectedBrowserWindowIndex = m_browserWindowComboBox->getSelectedBrowserWindowIndex(); if (redrawnEvent->getBrowserWindowIndex() == selectedBrowserWindowIndex) { updateContent(selectedBrowserWindowIndex); } } } connectome-workbench-1.2.3+git41-gc4c6c90/src/GuiQt/CustomViewDialog.h000066400000000000000000000134771300200146000252070ustar00rootroot00000000000000#ifndef __CUSTOM_VIEW_DIALOG_H__ #define __CUSTOM_VIEW_DIALOG_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 "EventListenerInterface.h" #include "WuQDialogNonModal.h" class QDoubleSpinBox; class QPushButton; namespace caret { class BrainBrowserWindow; class BrainBrowserWindowComboBox; class CaretPreferences; class ModelTransform; class WuQListWidget; class WuQWidgetObjectGroup; class CustomViewDialog : public WuQDialogNonModal, public EventListenerInterface { Q_OBJECT public: CustomViewDialog(QWidget* parent); virtual ~CustomViewDialog(); void updateDialog(); void updateContent(const int32_t browserWindowIndex); void receiveEvent(Event* event); private: CustomViewDialog(const CustomViewDialog&); CustomViewDialog& operator=(const CustomViewDialog&); private slots: void transformValueChanged(); void newCustomViewPushButtonClicked(); void deleteCustomViewPushButtonClicked(); void browserWindowComboBoxValueChanged(BrainBrowserWindow* browserWindow); void customViewSelected(); void customViewSelectedAndApplied(); void copyToCustomViewPushButtonClicked(); void copyToTransformPushButtonClicked(); public: // ADD_NEW_METHODS_HERE protected: void focusGained(); private: void loadCustomViewListWidget(const AString& selectedName = ""); void updateGraphicsWindow(); void getTransformationControlValues(double& panX, double& panY, double& panZ, double& rotX, double& rotY, double& rotZ, double& obRotX, double& obRotY, double& obRotZ, double& zoom, double& rightFlatX, double& rightFlatY, double& rightFlatZoom) const; void setTransformationControlValues(const double panX, const double panY, const double panZ, const double rotX, const double rotY, const double rotZ, const double obRotX, const double obRotY, const double obRotZ, const double zoom, const double rightFlatX, const double rightFlatY, const double rightFlatZoom) const; // ADD_NEW_MEMBERS_HERE CaretPreferences* getCaretPreferences(); //std::vector getAllCustomViewNames(); QWidget* createCustomViewWidget(); QWidget* createCopyWidget(); QWidget* createTransformsWidget(); //UserView* getSelectedUserView(); AString getSelectedCustomViewName(); void moveTransformToCustomView(ModelTransform& modelTransform); QWidget* m_copyWidget; QDoubleSpinBox* m_xPanDoubleSpinBox; QDoubleSpinBox* m_yPanDoubleSpinBox; QDoubleSpinBox* m_zPanDoubleSpinBox; QDoubleSpinBox* m_xRotateDoubleSpinBox; QDoubleSpinBox* m_yRotateDoubleSpinBox; QDoubleSpinBox* m_zRotateDoubleSpinBox; QDoubleSpinBox* m_xObliqueRotateDoubleSpinBox; QDoubleSpinBox* m_yObliqueRotateDoubleSpinBox; QDoubleSpinBox* m_zObliqueRotateDoubleSpinBox; QDoubleSpinBox* m_zoomDoubleSpinBox; QDoubleSpinBox* m_xRightFlatMapSpinBox; QDoubleSpinBox* m_yRightFlatMapSpinBox; QDoubleSpinBox* m_rightFlatMapZoomFactorSpinBox; WuQWidgetObjectGroup* m_transformWidgetGroup; BrainBrowserWindowComboBox* m_browserWindowComboBox; QPushButton* m_newCustomViewPushButton; QPushButton* m_deleteCustomViewPushButton; WuQListWidget* m_customViewListWidget; bool m_blockDialogUpdate; }; #ifdef __CUSTOM_VIEW_DIALOG_DECLARE__ // #endif // __CUSTOM_VIEW_DIALOG_DECLARE__ } // namespace #endif //__CUSTOM_VIEW_DIALOG_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/GuiQt/DataFileContentCopyMoveDialog.cxx000066400000000000000000000261401300200146000301320ustar00rootroot00000000000000 /*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 __DATA_FILE_CONTENT_COPY_MOVE_DIALOG_DECLARE__ #include "DataFileContentCopyMoveDialog.h" #undef __DATA_FILE_CONTENT_COPY_MOVE_DIALOG_DECLARE__ #include #include #include #include #include #include #include #include #include #include "CaretAssert.h" #include "CaretDataFile.h" #include "CaretFileDialog.h" #include "DataFile.h" #include "DataFileContentCopyMoveInterface.h" #include "DataFileContentCopyMoveParameters.h" #include "DataFileException.h" #include "EventDataFileAdd.h" #include "EventDataFileDelete.h" #include "EventManager.h" #include "FileInformation.h" #include "WuQMessageBox.h" using namespace caret; /** * \class caret::DataFileContentCopyMoveDialog * \brief Dialog for copying/moving content between data files * \ingroup GuiQt */ /** * Constructor. * * @param windowIndex * Index of window. * @param sourceDataFileInterface * The source data file (copy from file) * @param destinationDataFileInterfaces * Files to which data may be moved. * @param parent * Optional parent for this dialog. */ DataFileContentCopyMoveDialog::DataFileContentCopyMoveDialog(const int32_t windowIndex, DataFileContentCopyMoveInterface* sourceDataFileInterface, std::vector& destinationDataFileInterfaces, QWidget* parent) : WuQDialogModal("Copy/Move Data File Content", parent), m_windowIndex(windowIndex), m_sourceDataFileInterface(sourceDataFileInterface), m_newDestinatonFileButtonGroupIndex(-1) { CaretAssert(m_sourceDataFileInterface); /* * Copy pointers to destination files but ignore the source file */ for (std::vector::iterator fileIter = destinationDataFileInterfaces.begin(); fileIter != destinationDataFileInterfaces.end(); fileIter++) { DataFileContentCopyMoveInterface* df = *fileIter; if (df != sourceDataFileInterface) { m_destinationDataFileInterfaces.push_back(df); } } QWidget* dialogWidget = new QWidget; QVBoxLayout* dialogLayout = new QVBoxLayout(dialogWidget); dialogLayout->addWidget(createSourceWidget()); dialogLayout->addWidget(createDestinationWidget()); dialogLayout->addWidget(createOptionsWidget()); setCentralWidget(dialogWidget, WuQDialog::SCROLL_AREA_AS_NEEDED); } /** * Destructor. */ DataFileContentCopyMoveDialog::~DataFileContentCopyMoveDialog() { } /** * @return The source widget. */ QWidget* DataFileContentCopyMoveDialog::createSourceWidget() { QLabel* sourceFileLabel = new QLabel(m_sourceDataFileInterface->getAsDataFile()->getFileNameNoPath()); QGroupBox* groupBox = new QGroupBox("Source File"); QVBoxLayout* layout = new QVBoxLayout(groupBox); layout->addWidget(sourceFileLabel); return groupBox; } /** * @return The options widget. */ QWidget* DataFileContentCopyMoveDialog::createOptionsWidget() { m_closeSourceFileCheckBox = new QCheckBox("Close Source File After Copying Data"); m_closeSourceFileCheckBox->setChecked(true); QObject::connect(m_closeSourceFileCheckBox, SIGNAL(toggled(bool)), this, SLOT(closeSourceFileCheckBoxToggled(bool))); m_copySelectedAnnotationsOnlyCheckBox = new QCheckBox("Copy Only Selected Annotations"); m_copySelectedAnnotationsOnlyCheckBox->setChecked(false); QGroupBox* groupBox = new QGroupBox("Options"); QVBoxLayout* layout = new QVBoxLayout(groupBox); layout->addWidget(m_closeSourceFileCheckBox); layout->addWidget(m_copySelectedAnnotationsOnlyCheckBox); return groupBox; } /** * Called when the close source file checkbox is toggled. * * @param checked * New checked status. */ void DataFileContentCopyMoveDialog::closeSourceFileCheckBoxToggled(bool checked) { if ( ! checked) { /* * Display warning only once. */ static bool firstTimeFlag = true; if (firstTimeFlag) { const QString msg("If the source file is not closed, identical data items will appear " "in the graphics region."); WuQMessageBox::warningOk(m_closeSourceFileCheckBox, msg); firstTimeFlag = false; } } } /** * @return The destination widget. */ QWidget* DataFileContentCopyMoveDialog::createDestinationWidget() { m_destinationButtonGroup = new QButtonGroup; const int32_t numberOfFiles = static_cast(m_destinationDataFileInterfaces.size()); for (int32_t iFile = 0; iFile < numberOfFiles; iFile++) { DataFile* dataFile = m_destinationDataFileInterfaces[iFile]->getAsDataFile(); QRadioButton* rb = new QRadioButton(dataFile->getFileNameNoPath()); m_destinationButtonGroup->addButton(rb, iFile); } QToolButton* newDestinationFileToolButton = new QToolButton(); QObject::connect(newDestinationFileToolButton, SIGNAL(clicked(bool)), this, SLOT(newDestinationFileToolButtonClicked())); newDestinationFileToolButton->setText("New File Name..."); m_newDestinationFileNameLabel = new QLabel(" "); m_newDestinatonFileButtonGroupIndex = m_destinationButtonGroup->buttons().size(); QRadioButton* newFileRadioButton = new QRadioButton(""); m_destinationButtonGroup->addButton(newFileRadioButton, m_newDestinatonFileButtonGroupIndex); QGroupBox* groupBox = new QGroupBox("Destination File"); QGridLayout* layout = new QGridLayout(groupBox); layout->setColumnStretch(0, 0); layout->setColumnStretch(1, 0); layout->setColumnStretch(2, 0); layout->setColumnStretch(3, 100); QListIterator buttonIter(m_destinationButtonGroup->buttons()); while (buttonIter.hasNext()) { const int row = layout->rowCount(); QAbstractButton* button = buttonIter.next(); if (button == newFileRadioButton) { layout->addWidget(button, row, 0); layout->addWidget(newDestinationFileToolButton, row, 1); layout->addWidget(m_newDestinationFileNameLabel, row, 2, Qt::AlignLeft); } else { layout->addWidget(button, row, 0, 1, 3, Qt::AlignLeft); } } return groupBox; } /** * Gets called when "New File..." button is clicked. */ void DataFileContentCopyMoveDialog::newDestinationFileToolButtonClicked() { const CaretDataFile* caretDataFile = dynamic_cast(m_sourceDataFileInterface); CaretAssert(caretDataFile); const QString fileName = CaretFileDialog::getSaveFileNameDialog(caretDataFile->getDataFileType(), this, "Choose New File"); if ( ! fileName.isEmpty()) { m_newDestinationFileName = fileName; FileInformation fileInfo(fileName); m_newDestinationFileNameLabel->setText(fileInfo.getFileName()); } } /** * Called when OK button clicked. */ void DataFileContentCopyMoveDialog::okButtonClicked() { try { DataFileContentCopyMoveInterface* destinationFile = NULL; bool newFileFlag = false; const int32_t destinationFileIndex = m_destinationButtonGroup->checkedId(); if (destinationFileIndex >= 0) { if (destinationFileIndex == m_newDestinatonFileButtonGroupIndex) { if (m_newDestinationFileName.isEmpty()) { throw DataFileException("New file name is empty."); } const CaretDataFile* caretDataFile = dynamic_cast(m_sourceDataFileInterface); CaretAssert(caretDataFile); destinationFile = m_sourceDataFileInterface->newInstanceOfDataFile(); if (destinationFile == NULL) { throw DataFileException("Failed to created new file."); } destinationFile->getAsDataFile()->setFileName(m_newDestinationFileName); newFileFlag = true; } else { CaretAssertVectorIndex(m_destinationDataFileInterfaces, destinationFileIndex); destinationFile = m_destinationDataFileInterfaces[destinationFileIndex]; } } if (destinationFile == NULL) { throw DataFileException("No destination file is selected"); } CaretAssert(destinationFile); DataFileContentCopyMoveParameters copyMoveParams(m_sourceDataFileInterface, m_windowIndex); copyMoveParams.setOptionSelectedItems(m_copySelectedAnnotationsOnlyCheckBox->isChecked()); destinationFile->appendContentFromDataFile(copyMoveParams); if (newFileFlag) { CaretDataFile* destinationCaretFile = dynamic_cast(destinationFile); CaretAssert(destinationCaretFile); if (destinationCaretFile->isEmpty()) { throw DataFileException("There was no data to copy. New file was not created."); } EventDataFileAdd addFileEvent(destinationCaretFile); EventManager::get()->sendEvent(addFileEvent.getPointer()); if (addFileEvent.isError()) { throw DataFileException(addFileEvent.getErrorMessage()); } } if (m_closeSourceFileCheckBox->isChecked()) { CaretDataFile* sourceCaretFile = dynamic_cast(m_sourceDataFileInterface); CaretAssert(sourceCaretFile); EventManager::get()->sendEvent(EventDataFileDelete(sourceCaretFile).getPointer()); } } catch (const DataFileException& dfe) { WuQMessageBox::errorOk(this, dfe.whatString()); return; } WuQDialogModal::okButtonClicked(); } connectome-workbench-1.2.3+git41-gc4c6c90/src/GuiQt/DataFileContentCopyMoveDialog.h000066400000000000000000000056471300200146000275700ustar00rootroot00000000000000#ifndef __DATA_FILE_CONTENT_COPY_MOVE_DIALOG_H__ #define __DATA_FILE_CONTENT_COPY_MOVE_DIALOG_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 "WuQDialogModal.h" class QButtonGroup; class QCheckBox; class QLabel; namespace caret { class DataFileContentCopyMoveInterface; class DataFileContentCopyMoveDialog : public WuQDialogModal { Q_OBJECT public: DataFileContentCopyMoveDialog(const int32_t windowIndex, DataFileContentCopyMoveInterface* sourceDataFileInterface, std::vector& dataFilesInterface, QWidget* parent); virtual ~DataFileContentCopyMoveDialog(); // ADD_NEW_METHODS_HERE private slots: void newDestinationFileToolButtonClicked(); void closeSourceFileCheckBoxToggled(bool); private: DataFileContentCopyMoveDialog(const DataFileContentCopyMoveDialog&); DataFileContentCopyMoveDialog& operator=(const DataFileContentCopyMoveDialog&); virtual void okButtonClicked(); QWidget* createOptionsWidget(); QWidget* createSourceWidget(); QWidget* createDestinationWidget(); const int32_t m_windowIndex; DataFileContentCopyMoveInterface* m_sourceDataFileInterface; std::vector m_destinationDataFileInterfaces; QButtonGroup* m_destinationButtonGroup; QLabel* m_newDestinationFileNameLabel; int m_newDestinatonFileButtonGroupIndex; QString m_newDestinationFileName; QCheckBox* m_closeSourceFileCheckBox; QCheckBox* m_copySelectedAnnotationsOnlyCheckBox; // ADD_NEW_MEMBERS_HERE }; #ifdef __DATA_FILE_CONTENT_COPY_MOVE_DIALOG_DECLARE__ // #endif // __DATA_FILE_CONTENT_COPY_MOVE_DIALOG_DECLARE__ } // namespace #endif //__DATA_FILE_CONTENT_COPY_MOVE_DIALOG_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/GuiQt/DisplayGroupAndTabItemTreeWidgetItem.cxx000066400000000000000000000257421300200146000314510ustar00rootroot00000000000000 /*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 __DISPLAY_GROUP_AND_TAB_ITEM_TREE_WIDGET_ITEM_DECLARE__ #include "DisplayGroupAndTabItemTreeWidgetItem.h" #undef __DISPLAY_GROUP_AND_TAB_ITEM_TREE_WIDGET_ITEM_DECLARE__ #include #include #include #include "CaretAssert.h" #include "CaretLogger.h" #include "DisplayGroupAndTabItemInterface.h" #include "WuQtUtilities.h" using namespace caret; /** * \class caret::DisplayGroupAndTabItemTreeWidgetItem * \brief Item for display group and tab selection hierarchy * \ingroup GuiQt */ /** * Constructor. */ DisplayGroupAndTabItemTreeWidgetItem::DisplayGroupAndTabItemTreeWidgetItem(const int32_t browserWindowIndex) : QTreeWidgetItem(), m_browserWindowIndex(browserWindowIndex) { m_displayGroup = DisplayGroupEnum::DISPLAY_GROUP_A; m_tabIndex = -1; } /** * Destructor. */ DisplayGroupAndTabItemTreeWidgetItem::~DisplayGroupAndTabItemTreeWidgetItem() { } /** * @return A deep copy of the item. */ QTreeWidgetItem* DisplayGroupAndTabItemTreeWidgetItem::clone() const { const QString msg("Cloning of DisplayGroupAndTabItemTreeWidgetItem not allowed."); CaretAssertMessage(0, msg); CaretLogSevere(msg); return NULL; } /** * Update the content of this widget. * * @param displayGroupAndTabItem * Display group and tab item for this instance. * @param treeWidget * Tree widget that owns this item. * @param displayGroup * The display group. * @param tabIndex * The tab index. */ void DisplayGroupAndTabItemTreeWidgetItem::updateContent(DisplayGroupAndTabItemInterface *displayGroupAndTabItem, QTreeWidget* treeWidget, const DisplayGroupEnum::Enum displayGroup, const int32_t tabIndex) { CaretAssert(displayGroupAndTabItem); m_displayGroup = displayGroup; m_tabIndex = tabIndex; setText(NAME_COLUMN, displayGroupAndTabItem->getItemName()); setDisplayGroupAndTabItem(displayGroupAndTabItem); Qt::CheckState qtCheckState = toQCheckState(displayGroupAndTabItem->getItemDisplaySelected(m_displayGroup, m_tabIndex)); setCheckState(NAME_COLUMN, qtCheckState); setItemIcon(treeWidget, displayGroupAndTabItem); const int32_t numExistingChildren = childCount(); const int32_t numValidChildren = displayGroupAndTabItem->getNumberOfItemChildren(); const int32_t numberOfChildrenToAdd = numValidChildren - numExistingChildren; for (int32_t i = 0; i < numberOfChildrenToAdd; i++) { addChild(new DisplayGroupAndTabItemTreeWidgetItem(m_browserWindowIndex)); } CaretAssert(childCount() >= numValidChildren); for (int32_t i = 0; i < numValidChildren; i++) { QTreeWidgetItem* treeWidgetChild = child(i); if (i < numValidChildren) { treeWidgetChild->setHidden(false); DisplayGroupAndTabItemTreeWidgetItem* dgtChild = dynamic_cast(treeWidgetChild); CaretAssert(dgtChild); dgtChild->updateContent(displayGroupAndTabItem->getItemChild(i), treeWidget, displayGroup, tabIndex); } else { treeWidgetChild->setHidden(true); setDisplayGroupAndTabItem(NULL); } } // const bool expandedFlag = displayGroupAndTabItem->isItemExpanded(m_displayGroup, // m_tabIndex); // setExpanded(expandedFlag); // setSelected(displayGroupAndTabItem->isItemSelectedForEditingInWindow()) for (int32_t i = (numExistingChildren - 1); i >= numValidChildren; i--) { /* * Take removes it from the parent but * does not destruct it. */ QTreeWidgetItem* item = takeChild(i); delete item; } } /** * @return The data item in this tree widget item (may be NULL). */ DisplayGroupAndTabItemInterface* DisplayGroupAndTabItemTreeWidgetItem::getDisplayGroupAndTabItem() const { DisplayGroupAndTabItemInterface* myItem = NULL; void* myDataPtr = data(NAME_COLUMN, Qt::UserRole).value(); if (myDataPtr != NULL) { myItem = (DisplayGroupAndTabItemInterface*)myDataPtr; CaretAssert(myItem); } return myItem; } /** * Set the data item in this tree widget item. * * @param displayGroupAndTabItem * The data item (may be NULL). */ void DisplayGroupAndTabItemTreeWidgetItem::setDisplayGroupAndTabItem(DisplayGroupAndTabItemInterface* displayGroupAndTabItem) { setData(NAME_COLUMN, Qt::UserRole, qVariantFromValue(displayGroupAndTabItem)); } /** * Set the icon for this item. */ void DisplayGroupAndTabItemTreeWidgetItem::setItemIcon(QTreeWidget* treeWidget, DisplayGroupAndTabItemInterface* myDataItem) { CaretAssert(myDataItem); float backgroundRGBA[4]; float outlineRGBA[4]; float textRGBA[4]; myDataItem->getItemIconColorsRGBA(backgroundRGBA, outlineRGBA, textRGBA); if ((backgroundRGBA[3] > 0.0) || (outlineRGBA[3] > 0.0) || (textRGBA[3] > 0.0)) { const int pixmapSize = 24; QPixmap pixmap(pixmapSize, pixmapSize); QSharedPointer painter = WuQtUtilities::createPixmapWidgetPainterOriginBottomLeft(treeWidget, pixmap); if (backgroundRGBA[3] > 0.0) { painter->fillRect(pixmap.rect(), QColor::fromRgbF(backgroundRGBA[0], backgroundRGBA[1], backgroundRGBA[2])); } if (outlineRGBA[3] > 0.0) { QPen pen = painter->pen(); QColor outlineColor = QColor::fromRgbF(outlineRGBA[0], outlineRGBA[1], outlineRGBA[2]); painter->fillRect(0, 0, 3, pixmapSize, outlineColor); painter->fillRect(pixmapSize - 3, 0, 3, pixmapSize, outlineColor); painter->fillRect(0, 0, pixmapSize, 3, outlineColor); painter->fillRect(0, pixmapSize - 4, pixmapSize, 3, outlineColor); } if (textRGBA[3] > 0.0) { QColor textColor = QColor::fromRgbF(textRGBA[0], textRGBA[1], textRGBA[2]); const int rectSize = 8; const int cornerXY = (pixmapSize / 2) - (rectSize / 2); painter->fillRect(cornerXY, cornerXY, rectSize, rectSize, textColor); } setIcon(NAME_COLUMN, QIcon(pixmap)); } else { setIcon(NAME_COLUMN, QIcon()); } } /** * Update the selected and expanded checkboxes. * * @param displayGroup * The display group. * @param tabIndex * Index of the tab. */ void DisplayGroupAndTabItemTreeWidgetItem::updateSelectedAndExpandedCheckboxes(const DisplayGroupEnum::Enum displayGroup, const int32_t tabIndex) { const int32_t numChildren = childCount(); for (int32_t iChild = 0; iChild < numChildren; iChild++) { QTreeWidgetItem* treeChild = child(iChild); CaretAssert(treeChild); DisplayGroupAndTabItemTreeWidgetItem* item = dynamic_cast(treeChild); CaretAssert(item); DisplayGroupAndTabItemInterface* data = item->getDisplayGroupAndTabItem(); if (data != NULL) { item->updateSelectedAndExpandedCheckboxes(displayGroup, tabIndex); } } DisplayGroupAndTabItemInterface* myData = getDisplayGroupAndTabItem(); if (myData != NULL) { Qt::CheckState checkState = toQCheckState(myData->getItemDisplaySelected(displayGroup, tabIndex)); setCheckState(NAME_COLUMN, checkState); setExpanded(myData->isItemExpanded(displayGroup, tabIndex)); setSelected(myData->isItemSelectedForEditingInWindow(m_browserWindowIndex)); } } /** * Convert QCheckState to GroupAndNameCheckStateEnum * @param checkState * The QCheckState * @return GroupAndNameCheckStateEnum converted from QCheckState */ TriStateSelectionStatusEnum::Enum DisplayGroupAndTabItemTreeWidgetItem::fromQCheckState(const Qt::CheckState checkState) { switch (checkState) { case Qt::Unchecked: return TriStateSelectionStatusEnum::UNSELECTED; break; case Qt::PartiallyChecked: return TriStateSelectionStatusEnum::PARTIALLY_SELECTED; break; case Qt::Checked: return TriStateSelectionStatusEnum::SELECTED; break; } return TriStateSelectionStatusEnum::UNSELECTED; } /** * Convert the tri state selection status to Qt::CheckState * * @param triStateStatus * The tri state selection status. * @return * Qt::CheckState status. */ Qt::CheckState DisplayGroupAndTabItemTreeWidgetItem::toQCheckState(const TriStateSelectionStatusEnum::Enum triStateStatus) { switch (triStateStatus) { case TriStateSelectionStatusEnum::PARTIALLY_SELECTED: return Qt::PartiallyChecked; break; case TriStateSelectionStatusEnum::SELECTED: return Qt::Checked; break; case TriStateSelectionStatusEnum::UNSELECTED: return Qt::Unchecked; break; } return Qt::Unchecked; } connectome-workbench-1.2.3+git41-gc4c6c90/src/GuiQt/DisplayGroupAndTabItemTreeWidgetItem.h000066400000000000000000000062671300200146000310770ustar00rootroot00000000000000#ifndef __DISPLAY_GROUP_AND_TAB_ITEM_TREE_WIDGET_ITEM_H__ #define __DISPLAY_GROUP_AND_TAB_ITEM_TREE_WIDGET_ITEM_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 "DisplayGroupEnum.h" #include "TriStateSelectionStatusEnum.h" class QTreeWidget; namespace caret { class DisplayGroupAndTabItemInterface; class DisplayGroupAndTabItemTreeWidgetItem : public QTreeWidgetItem { public: DisplayGroupAndTabItemTreeWidgetItem(const int32_t browserWindowIndex); virtual ~DisplayGroupAndTabItemTreeWidgetItem(); void updateContent(DisplayGroupAndTabItemInterface *displayGroupAndTabItem, QTreeWidget* treeWidget, const DisplayGroupEnum::Enum displayGroup, const int32_t tabIndex); DisplayGroupAndTabItemInterface* getDisplayGroupAndTabItem() const; virtual QTreeWidgetItem* clone() const; // ADD_NEW_METHODS_HERE private: DisplayGroupAndTabItemTreeWidgetItem(const DisplayGroupAndTabItemTreeWidgetItem&); DisplayGroupAndTabItemTreeWidgetItem& operator=(const DisplayGroupAndTabItemTreeWidgetItem&); static TriStateSelectionStatusEnum::Enum fromQCheckState(const Qt::CheckState checkState); static Qt::CheckState toQCheckState(const TriStateSelectionStatusEnum::Enum triStateStatus); void setItemIcon(QTreeWidget* treeWidget, DisplayGroupAndTabItemInterface* myDataItem); void setDisplayGroupAndTabItem(DisplayGroupAndTabItemInterface* displayGroupAndTabItem); void updateSelectedAndExpandedCheckboxes(const DisplayGroupEnum::Enum displayGroup, const int32_t tabIndex); const int32_t m_browserWindowIndex; DisplayGroupEnum::Enum m_displayGroup; int32_t m_tabIndex; // ADD_NEW_MEMBERS_HERE static const int NAME_COLUMN; friend class DisplayGroupAndTabItemViewController; }; #ifdef __DISPLAY_GROUP_AND_TAB_ITEM_TREE_WIDGET_ITEM_DECLARE__ const int DisplayGroupAndTabItemTreeWidgetItem::NAME_COLUMN = 0; #endif // __DISPLAY_GROUP_AND_TAB_ITEM_TREE_WIDGET_ITEM_DECLARE__ } // namespace #endif //__DISPLAY_GROUP_AND_TAB_ITEM_TREE_WIDGET_ITEM_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/GuiQt/DisplayGroupAndTabItemViewController.cxx000066400000000000000000000367651300200146000315540ustar00rootroot00000000000000 /*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 __DISPLAY_GROUP_AND_TAB_ITEM_VIEW_CONTROLLER_DECLARE__ #include "DisplayGroupAndTabItemViewController.h" #undef __DISPLAY_GROUP_AND_TAB_ITEM_VIEW_CONTROLLER_DECLARE__ #include #include #include "Annotation.h" #include "AnnotationGroup.h" #include "AnnotationManager.h" #include "Brain.h" #include "BrowserTabContent.h" #include "CaretAssert.h" #include "DisplayGroupAndTabItemInterface.h" #include "DisplayGroupAndTabItemTreeWidgetItem.h" #include "DisplayPropertiesAnnotation.h" #include "EventGraphicsUpdateAllWindows.h" #include "EventManager.h" #include "GuiManager.h" using namespace caret; /** * \class caret::DisplayGroupAndTabItemViewController * \brief View controller for display group and tab item hierarchy * \ingroup GuiQt */ /** * Constructor. * * @param dataFileType * Type of data file using this view controller. * @param browserWindowIndex * The browser window containing this instance. * @param parent * Parent of this instance. */ DisplayGroupAndTabItemViewController::DisplayGroupAndTabItemViewController(const DataFileTypeEnum::Enum dataFileType, const int32_t browserWindowIndex, QWidget* parent) : QWidget(parent), m_dataFileType(dataFileType), m_browserWindowIndex(browserWindowIndex) { m_treeWidget = new QTreeWidget(); m_treeWidget->setHeaderHidden(true); //m_treeWidget->setSelectionMode(QTreeWidget::ExtendedSelection); m_treeWidget->setSelectionMode(QTreeWidget::NoSelection); QObject::connect(m_treeWidget, SIGNAL(itemCollapsed(QTreeWidgetItem*)), this, SLOT(itemWasCollapsed(QTreeWidgetItem*))); QObject::connect(m_treeWidget, SIGNAL(itemExpanded(QTreeWidgetItem*)), this, SLOT(itemWasExpanded(QTreeWidgetItem*))); QObject::connect(m_treeWidget, SIGNAL(itemChanged(QTreeWidgetItem*, int)), this, SLOT(itemWasChanged(QTreeWidgetItem*, int))); QObject::connect(m_treeWidget, SIGNAL(itemSelectionChanged()), this, SLOT(itemsWereSelected())); QVBoxLayout* layout = new QVBoxLayout(this); layout->addWidget(m_treeWidget, 100); s_allViewControllers.insert(this); } /** * Destructor. */ DisplayGroupAndTabItemViewController::~DisplayGroupAndTabItemViewController() { s_allViewControllers.erase(this); } /** * Gets called when items are selected. */ void DisplayGroupAndTabItemViewController::itemsWereSelected() { QList itemsSelected = m_treeWidget->selectedItems(); if ( ! itemsSelected.empty()) { std::vector itemInterfacesVector; QListIterator itemsIter(itemsSelected); while (itemsIter.hasNext()) { QTreeWidgetItem* item = itemsIter.next(); DisplayGroupAndTabItemTreeWidgetItem* widgetItem = dynamic_cast(item); CaretAssert(widgetItem); DisplayGroupAndTabItemInterface* itemInterface = widgetItem->getDisplayGroupAndTabItem(); CaretAssert(itemInterface); if (itemInterface != NULL) { itemInterfacesVector.push_back(itemInterface); } } if ( ! itemInterfacesVector.empty()) { if (m_dataFileType == DataFileTypeEnum::ANNOTATION) { processAnnotationDataSelection(itemInterfacesVector); } } } DisplayGroupEnum::Enum displayGroup = DisplayGroupEnum::DISPLAY_GROUP_TAB; int32_t tabIndex = -1; getDisplayGroupAndTabIndex(displayGroup, tabIndex); updateSelectedAndExpandedCheckboxes(displayGroup, tabIndex); //updateSelectedAndExpandedCheckboxesInOtherViewControllers(); updateGraphics(); } /** * Process the selection of annotations. * * @param interfaceItems * Items that should be annotations ! */ void DisplayGroupAndTabItemViewController::processAnnotationDataSelection(const std::vector& interfaceItems) { std::set annotationSet; for (std::vector::const_iterator iter = interfaceItems.begin(); iter != interfaceItems.end(); iter++) { Annotation* ann = dynamic_cast(*iter); if (ann != NULL) { annotationSet.insert(ann); } else { AnnotationGroup* annGroup = dynamic_cast(*iter); if (annGroup != NULL) { std::vector groupAnns; annGroup->getAllAnnotations(groupAnns); annotationSet.insert(groupAnns.begin(), groupAnns.end()); } } } if ( ! annotationSet.empty()) { std::vector selectedAnnotations(annotationSet.begin(), annotationSet.end()); AnnotationManager* annMan = GuiManager::get()->getBrain()->getAnnotationManager(); annMan->setAnnotationsForEditing(m_browserWindowIndex, selectedAnnotations); } } /** * Gets called when an item is collapsed so that its children are not visible. * * @param item * The QTreeWidgetItem that was collapsed. */ void DisplayGroupAndTabItemViewController::itemWasCollapsed(QTreeWidgetItem* item) { processItemExpanded(item, false); } /** * Gets called when an item is expaned so that its children are visible. * * @param item * The QTreeWidgetItem that was expanded. */ void DisplayGroupAndTabItemViewController::itemWasExpanded(QTreeWidgetItem* item) { processItemExpanded(item, true); } /** * Called when an item is changed (checkbox selected/deselected). * * @param item * The QTreeWidgetItem that was collapsed. * @param column * Ignored. */ void DisplayGroupAndTabItemViewController::itemWasChanged(QTreeWidgetItem* item, int /*column*/) { DisplayGroupEnum::Enum displayGroup = DisplayGroupEnum::DISPLAY_GROUP_TAB; int32_t tabIndex = -1; getDisplayGroupAndTabIndex(displayGroup, tabIndex); DisplayGroupAndTabItemInterface* dataItem = getDataItem(item); const Qt::CheckState checkState = item->checkState(DisplayGroupAndTabItemTreeWidgetItem::NAME_COLUMN); const TriStateSelectionStatusEnum::Enum itemCheckState = DisplayGroupAndTabItemTreeWidgetItem::fromQCheckState(checkState); dataItem->setItemDisplaySelected(displayGroup, tabIndex, itemCheckState); updateSelectedAndExpandedCheckboxes(displayGroup, tabIndex); updateSelectedAndExpandedCheckboxesInOtherViewControllers(); updateGraphics(); } /** * Process item expanded or collapsed. * * @param item * The QTreeWidgetItem that was expanded or collapsed. * @param expandedStatus * True if expanded, false if collapsed. */ void DisplayGroupAndTabItemViewController::processItemExpanded(QTreeWidgetItem* item, const bool expandedStatus) { DisplayGroupEnum::Enum displayGroup = DisplayGroupEnum::DISPLAY_GROUP_TAB; int32_t tabIndex = -1; getDisplayGroupAndTabIndex(displayGroup, tabIndex); DisplayGroupAndTabItemInterface* dataItem = getDataItem(item); dataItem->setItemExpanded(displayGroup, tabIndex, expandedStatus); updateSelectedAndExpandedCheckboxes(displayGroup, tabIndex); updateSelectedAndExpandedCheckboxesInOtherViewControllers(); } /** * Get the data item in the given tree widget item. * * @param item * The tree widget item. * @return * The data item in the tree widget item. */ DisplayGroupAndTabItemInterface* DisplayGroupAndTabItemViewController::getDataItem(QTreeWidgetItem* item) const { DisplayGroupAndTabItemTreeWidgetItem* treeItem = dynamic_cast(item); CaretAssert(treeItem); DisplayGroupAndTabItemInterface* dataItem = treeItem->getDisplayGroupAndTabItem(); CaretAssert(dataItem); return dataItem; } /** * Get the display group and tab index currently active. */ void DisplayGroupAndTabItemViewController::getDisplayGroupAndTabIndex(DisplayGroupEnum::Enum& displayGroupOut, int32_t& tabIndexOut) const { BrowserTabContent* tabContent = GuiManager::get()->getBrowserTabContentForBrowserWindow(m_browserWindowIndex, false); CaretAssert(tabContent); tabIndexOut= tabContent->getTabNumber(); CaretAssert(tabIndexOut >= 0); DisplayPropertiesAnnotation* dpa = GuiManager::get()->getBrain()->getDisplayPropertiesAnnotation(); displayGroupOut = dpa->getDisplayGroupForTab(tabIndexOut); } /** * Update the content. * * @param contentItemsIn * Items that are displayed. * @param displayGroup * The display group. * @param tabIndex * Index of the tab * @param allowSelectionFlag * Allows selection of items by user clicking items. */ void DisplayGroupAndTabItemViewController::updateContent(std::vector& contentItemsIn, const DisplayGroupEnum::Enum displayGroup, const int32_t tabIndex, const bool allowSelectionFlag) { if (allowSelectionFlag) { m_treeWidget->setSelectionMode(QTreeWidget::ExtendedSelection); } else { m_treeWidget->setSelectionMode(QTreeWidget::NoSelection); } /* * Ignore items without children */ std::vector contentItems; for (std::vector::iterator contIter = contentItemsIn.begin(); contIter != contentItemsIn.end(); contIter++) { DisplayGroupAndTabItemInterface* item = *contIter; if (item->getNumberOfItemChildren() > 0) { contentItems.push_back(item); } } /* * Updating the tree will cause signals so block them until update is done */ m_treeWidget->blockSignals(true); const int32_t numExistingChildren = m_treeWidget->topLevelItemCount(); const int32_t numValidChildren = contentItems.size(); const int32_t numberOfChildrenToAdd = numValidChildren - numExistingChildren; for (int32_t i = 0; i < numberOfChildrenToAdd; i++) { m_treeWidget->addTopLevelItem(new DisplayGroupAndTabItemTreeWidgetItem(m_browserWindowIndex)); } CaretAssert(m_treeWidget->topLevelItemCount() >= numValidChildren); for (int32_t i = 0; i < numValidChildren; i++) { QTreeWidgetItem* treeWidgetChild = m_treeWidget->topLevelItem(i); CaretAssert(treeWidgetChild); DisplayGroupAndTabItemTreeWidgetItem* dgtChild = dynamic_cast(treeWidgetChild); CaretAssert(dgtChild); treeWidgetChild->setHidden(false); CaretAssertVectorIndex(contentItems, i); CaretAssert(contentItems[i]); DisplayGroupAndTabItemInterface* displayGroupAndTabItem = contentItems[i]; dgtChild->updateContent(displayGroupAndTabItem, m_treeWidget, displayGroup, tabIndex); } for (int32_t i = (numExistingChildren - 1); i >= numValidChildren; i--) { /* * Take removes it from the parent but * does not destruct it. */ QTreeWidgetItem* item = m_treeWidget->takeTopLevelItem(i); delete item; } updateSelectedAndExpandedCheckboxes(displayGroup, tabIndex); /* * Allow signals now that updating is done */ m_treeWidget->blockSignals(false); } /** * Update graphics and, in some circumstances, surface node coloring. */ void DisplayGroupAndTabItemViewController::updateGraphics() { EventManager::get()->sendSimpleEvent(EventTypeEnum::EVENT_ANNOTATION_TOOLBAR_UPDATE); EventManager::get()->sendEvent(EventGraphicsUpdateAllWindows().getPointer()); } /** * Update the selected and expanded checkboxes. */ void DisplayGroupAndTabItemViewController::updateSelectedAndExpandedCheckboxes(const DisplayGroupEnum::Enum displayGroup, const int32_t tabIndex) { m_treeWidget->blockSignals(true); const int32_t numChildren = m_treeWidget->topLevelItemCount(); for (int32_t itemIndex = 0; itemIndex < numChildren; itemIndex++) { QTreeWidgetItem* treeChild = m_treeWidget->topLevelItem(itemIndex); CaretAssert(treeChild); DisplayGroupAndTabItemTreeWidgetItem* item = dynamic_cast(treeChild); CaretAssert(item); DisplayGroupAndTabItemInterface* data = item->getDisplayGroupAndTabItem(); if (data != NULL) { item->updateSelectedAndExpandedCheckboxes(displayGroup, tabIndex); } } m_treeWidget->blockSignals(false); } /** * Update the selection and expansion controls in ALL other view controllers. * All of them need to be updated since window annotation selection is not * affected by the display group and tab selection. */ void DisplayGroupAndTabItemViewController::updateSelectedAndExpandedCheckboxesInOtherViewControllers() { for (std::set::iterator iter = s_allViewControllers.begin(); iter != s_allViewControllers.end(); iter++) { DisplayGroupAndTabItemViewController* otherViewController = *iter; if (otherViewController != this) { if (otherViewController->m_dataFileType == m_dataFileType) { DisplayGroupEnum::Enum otherDisplayGroup = DisplayGroupEnum::DISPLAY_GROUP_TAB; int32_t otherTabIndex = -1; otherViewController->getDisplayGroupAndTabIndex(otherDisplayGroup, otherTabIndex); otherViewController->updateSelectedAndExpandedCheckboxes(otherDisplayGroup, otherTabIndex); } } } } connectome-workbench-1.2.3+git41-gc4c6c90/src/GuiQt/DisplayGroupAndTabItemViewController.h000066400000000000000000000075021300200146000311640ustar00rootroot00000000000000#ifndef __DISPLAY_GROUP_AND_TAB_ITEM_VIEW_CONTROLLER_H__ #define __DISPLAY_GROUP_AND_TAB_ITEM_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 "DataFileTypeEnum.h" #include "DisplayGroupEnum.h" class QTreeWidget; class QTreeWidgetItem; namespace caret { class DisplayGroupAndTabItemInterface; class DisplayGroupAndTabItemTreeWidgetItem; class DisplayGroupAndTabItemViewController : public QWidget { Q_OBJECT public: DisplayGroupAndTabItemViewController(const DataFileTypeEnum::Enum dataFileType, const int32_t browserWindowIndex, QWidget* parent = 0); virtual ~DisplayGroupAndTabItemViewController(); void updateContent(std::vector& contentItemsIn, const DisplayGroupEnum::Enum displayGroup, const int32_t tabIndex, const bool allowSelectionFlag); // ADD_NEW_METHODS_HERE private slots: void itemWasCollapsed(QTreeWidgetItem* item); void itemWasExpanded(QTreeWidgetItem* item); void itemWasChanged(QTreeWidgetItem* item, int column); void itemsWereSelected(); private: DisplayGroupAndTabItemViewController(const DisplayGroupAndTabItemViewController&); DisplayGroupAndTabItemViewController& operator=(const DisplayGroupAndTabItemViewController&); DisplayGroupAndTabItemInterface *m_displayGroupAndTabItem; void getDisplayGroupAndTabIndex(DisplayGroupEnum::Enum& displayGroupOut, int32_t& tabIndexOut) const; void processItemExpanded(QTreeWidgetItem* item, const bool expandedStatus); void processAnnotationDataSelection(const std::vector& interfaceItems); DisplayGroupAndTabItemInterface* getDataItem(QTreeWidgetItem* item) const; void updateGraphics(); void updateSelectedAndExpandedCheckboxes(const DisplayGroupEnum::Enum displayGroup, const int32_t tabIndex); void updateSelectedAndExpandedCheckboxesInOtherViewControllers(); const DataFileTypeEnum::Enum m_dataFileType; const int32_t m_browserWindowIndex; QTreeWidget* m_treeWidget; static std::set s_allViewControllers; // ADD_NEW_MEMBERS_HERE }; #ifdef __DISPLAY_GROUP_AND_TAB_ITEM_VIEW_CONTROLLER_DECLARE__ std::set DisplayGroupAndTabItemViewController::s_allViewControllers; #endif // __DISPLAY_GROUP_AND_TAB_ITEM_VIEW_CONTROLLER_DECLARE__ } // namespace #endif //__DISPLAY_GROUP_AND_TAB_ITEM_VIEW_CONTROLLER_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/GuiQt/DisplayGroupEnumComboBox.cxx000066400000000000000000000102131300200146000272160ustar00rootroot00000000000000 /*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 __DISPLAY_GROUP_ENUM_COMBO_BOX_DECLARE__ #include "DisplayGroupEnumComboBox.h" #undef __DISPLAY_GROUP_ENUM_COMBO_BOX_DECLARE__ using namespace caret; /** * \class caret::DisplayGroupEnumComboBox * \brief Combo box for selection of a display group. * \ingroup GuiQt * * Encapsulates a QComboBox for the selection of a * DisplayGroupEnum value. QComboBox is not extended * to prevent access to its methods that could cause * the selection to get messed up. */ /** * Constructor. * @param Parent * Parent object. */ DisplayGroupEnumComboBox::DisplayGroupEnumComboBox(QObject* parent) : WuQWidget(parent) { std::vector allDisplayGroups; DisplayGroupEnum::getAllEnums(allDisplayGroups); const int32_t numStructures = static_cast(allDisplayGroups.size()); this->displayGroupComboBox = new QComboBox(); for (int32_t i = 0; i < numStructures; i++) { this->displayGroupComboBox->addItem(DisplayGroupEnum::toGuiName(allDisplayGroups[i])); this->displayGroupComboBox->setItemData(i, DisplayGroupEnum::toIntegerCode(allDisplayGroups[i])); } QObject::connect(this->displayGroupComboBox, SIGNAL(activated(int)), this, SLOT(displayGroupComboBoxSelection(int))); } /** * Destructor. */ DisplayGroupEnumComboBox::~DisplayGroupEnumComboBox() { } /** * @return The selected display group. */ DisplayGroupEnum::Enum DisplayGroupEnumComboBox::getSelectedDisplayGroup() const { DisplayGroupEnum::Enum displayGroup = DisplayGroupEnum::getDefaultValue(); const int32_t indx = this->displayGroupComboBox->currentIndex(); if (indx >= 0) { const int32_t integerCode = this->displayGroupComboBox->itemData(indx).toInt(); displayGroup = DisplayGroupEnum::fromIntegerCode(integerCode, NULL); } return displayGroup; } /** * @return The widget (combo box) for adding to layout. */ QWidget* DisplayGroupEnumComboBox::getWidget() { return this->displayGroupComboBox; } /** * Set the display group. * @param displayGroup * New value for display group. */ void DisplayGroupEnumComboBox::setSelectedDisplayGroup(const DisplayGroupEnum::Enum displayGroup) { const int32_t displayGroupIntegerCode = DisplayGroupEnum::toIntegerCode(displayGroup); const int numStructures = this->displayGroupComboBox->count(); for (int32_t i = 0; i < numStructures; i++) { if (displayGroupIntegerCode == this->displayGroupComboBox->itemData(i).toInt()) { if (this->signalsBlocked()) { this->displayGroupComboBox->blockSignals(true); } this->displayGroupComboBox->setCurrentIndex(i); if (this->signalsBlocked()) { this->displayGroupComboBox->blockSignals(false); } break; } } } /** * Called when a display group is selected (receives signal) * @param indx * Index of selected item. */ void DisplayGroupEnumComboBox::displayGroupComboBoxSelection(int indx) { if (this->signalsBlocked() == false) { const int32_t integerCode = this->displayGroupComboBox->itemData(indx).toInt(); DisplayGroupEnum::Enum displayGroup = DisplayGroupEnum::fromIntegerCode(integerCode, NULL); emit displayGroupSelected(displayGroup); } } connectome-workbench-1.2.3+git41-gc4c6c90/src/GuiQt/DisplayGroupEnumComboBox.h000066400000000000000000000037601300200146000266540ustar00rootroot00000000000000#ifndef __DISPLAY_GROUP_ENUM_COMBO_BOX__H_ #define __DISPLAY_GROUP_ENUM_COMBO_BOX__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 "DisplayGroupEnum.h" #include "WuQWidget.h" class QComboBox; namespace caret { class DisplayGroupEnumComboBox : public WuQWidget { Q_OBJECT public: DisplayGroupEnumComboBox(QObject* parent); virtual ~DisplayGroupEnumComboBox(); DisplayGroupEnum::Enum getSelectedDisplayGroup() const; QWidget* getWidget(); public slots: void setSelectedDisplayGroup(const DisplayGroupEnum::Enum displayGroup); signals: void displayGroupSelected(const DisplayGroupEnum::Enum); private slots: void displayGroupComboBoxSelection(int); private: DisplayGroupEnumComboBox(const DisplayGroupEnumComboBox&); DisplayGroupEnumComboBox& operator=(const DisplayGroupEnumComboBox&); QComboBox* displayGroupComboBox; }; #ifdef __DISPLAY_GROUP_ENUM_COMBO_BOX_DECLARE__ // #endif // __DISPLAY_GROUP_ENUM_COMBO_BOX_DECLARE__ } // namespace #endif //__DISPLAY_GROUP_ENUM_COMBO_BOX__H_ connectome-workbench-1.2.3+git41-gc4c6c90/src/GuiQt/EnumComboBoxTemplate.h000066400000000000000000000167201300200146000260050ustar00rootroot00000000000000#ifndef __ENUM_COMBOBOX_TEMPLATE__H_ #define __ENUM_COMBOBOX_TEMPLATE__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 "AString.h" #include "WuQFactory.h" #include "WuQWidget.h" /** * \class caret::EnumComboBoxTemplate * \brief Create a combo box for a workbench enumerated type. * \ingroup GuiQt * * Create a combo box for a workbench enumerated type. Typically, * the enumerated types are created with wb_command: * wb_command.app/Contents/MacOS/wb_command -class-create-enum LabelDrawingTypeEnum 2 true * * This class is not a template class because QObject subclasses may * not be templates. See http://doc.trolltech.com/qq/qq15-academic.html * While the class cannot be a template, methods in the class be templates. * *

* How to Use This Class *

* Declare:
* EnumComboBoxTemplate* m_someTypeComboBox; * *

* Construct:
* m_someTypeComboBox = new EnumComboBoxTemplate(this);
* m_someTypeComboBox->setup();
* *

* Read:
* const SomeTypeEnum::Enum enumValue = m_someTypeComboBox->getSelectedItem(); * *

* Set:
* m_someTypeComboBox->setSelectedItem(enumValue); * *

* Get notified when user makes selection:
* See itemActivated() and itemChanged(). * */ namespace caret { class EnumComboBoxTemplate : public WuQWidget { Q_OBJECT public: /** * Constructor. * @param parent * Parent object. */ EnumComboBoxTemplate(QObject* parent) : WuQWidget(parent) { m_itemComboBox = WuQFactory::newComboBox(); QObject::connect(m_itemComboBox, SIGNAL(currentIndexChanged(int)), this, SLOT(itemComboBoxIndexChanged(int))); QObject::connect(m_itemComboBox, SIGNAL(activated(int)), this, SLOT(itemComboBoxActivated(int))); } /** * Destructor. */ virtual ~EnumComboBoxTemplate() { } /** * Setup the combo box. */ template void setup() { std::vector allEnums; CT::getAllEnums(allEnums); m_itemComboBox->blockSignals(true); m_itemComboBox->clear(); const int32_t numColors = static_cast(allEnums.size()); for (int32_t i = 0; i < numColors; i++) { const ET enumValue = allEnums[i]; const int32_t indx = m_itemComboBox->count(); const AString name = CT::toGuiName(enumValue); m_itemComboBox->addItem(name); m_itemComboBox->setItemData(indx, CT::toIntegerCode(enumValue)); } m_itemComboBox->blockSignals(false); } /** * Setup the combo box. */ template void setupWithItems(const std::vector& comboBoxItems) { m_itemComboBox->blockSignals(true); m_itemComboBox->clear(); const int32_t numColors = static_cast(comboBoxItems.size()); for (int32_t i = 0; i < numColors; i++) { const ET enumValue = comboBoxItems[i]; const int32_t indx = m_itemComboBox->count(); const AString name = CT::toGuiName(enumValue); m_itemComboBox->addItem(name); m_itemComboBox->setItemData(indx, CT::toIntegerCode(enumValue)); } m_itemComboBox->blockSignals(false); } /** * @return The selected item. */ template ET getSelectedItem() const { const int32_t indx = m_itemComboBox->currentIndex(); const int32_t integerCode = m_itemComboBox->itemData(indx).toInt(); ET item = CT::fromIntegerCode(integerCode, NULL); return item; } /** * Set the selected item. * @param item * New item for selection. */ template void setSelectedItem(const ET item) { const int32_t numItems = static_cast(m_itemComboBox->count()); for (int32_t i = 0; i < numItems; i++) { const int32_t integerCode = m_itemComboBox->itemData(i).toInt(); ET enumValue = CT::fromIntegerCode(integerCode, NULL); if (enumValue == item) { m_itemComboBox->blockSignals(true); m_itemComboBox->setCurrentIndex(i); m_itemComboBox->blockSignals(false); break; } } } /** * @return The actual widget. */ virtual QWidget* getWidget() { return m_itemComboBox; } /** * @return The actual combo box. */ QComboBox* getComboBox() { return m_itemComboBox; } signals: /** * This signal is sent when the user chooses an item in the combobox. * The item's index is passed. Note that this signal is sent even * when the choice is not changed. If you need to know when the * choice actually changes, use signal itemChanged(). */ void itemActivated(); /** * This signal is sent whenever the currentIndex in the * combobox changes either through user interaction or * programmatically. */ void itemChanged(); private slots: /** * Called when the user selects an item * @param indx * Index of item selected. */ void itemComboBoxIndexChanged(int) { emit itemChanged(); } /** * Called when the user selects an item * @param indx * Index of item selected. */ void itemComboBoxActivated(int) { emit itemActivated(); } private: EnumComboBoxTemplate(const EnumComboBoxTemplate&); EnumComboBoxTemplate& operator=(const EnumComboBoxTemplate&); private: QComboBox* m_itemComboBox; }; #ifdef __ENUM_COMBOBOX_TEMPLATE_DECLARE__ // #endif // __ENUM_COMBOBOX_TEMPLATE_DECLARE__ } // namespace #endif //__ENUM_COMBOBOX_TEMPLATE__H_ connectome-workbench-1.2.3+git41-gc4c6c90/src/GuiQt/EventAnnotationCreateNewType.cxx000066400000000000000000000046301300200146000301000ustar00rootroot00000000000000 /*LICENSE_START*/ /* * Copyright (C) 2015 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_CREATE_NEW_TYPE_DECLARE__ #include "EventAnnotationCreateNewType.h" #undef __EVENT_ANNOTATION_CREATE_NEW_TYPE_DECLARE__ #include "CaretAssert.h" #include "EventTypeEnum.h" using namespace caret; /** * \class caret::EventAnnotationCreateNewType * \brief Event that starts creationg of a new annotation with the annotation type. * \ingroup GuiQt */ /** * Constructor. * * @param annotationSpace * Space for new annotation. * @param annotationType * Type for new annotation. * */ EventAnnotationCreateNewType::EventAnnotationCreateNewType(AnnotationFile* annotationFile, const AnnotationCoordinateSpaceEnum::Enum annotationSpace, const AnnotationTypeEnum::Enum annotationType) : Event(EventTypeEnum::EVENT_ANNOTATION_CREATE_NEW_TYPE), m_annotationFile(annotationFile), m_annotationSpace(annotationSpace), m_annotationType(annotationType) { } /** * Destructor. */ EventAnnotationCreateNewType::~EventAnnotationCreateNewType() { } /** * @return Annotation file for new anotation. */ AnnotationFile* EventAnnotationCreateNewType::getAnnotationFile() const { return m_annotationFile; } /** * @return Space of annotation for creation. */ AnnotationCoordinateSpaceEnum::Enum EventAnnotationCreateNewType::getAnnotationSpace() const { return m_annotationSpace; } /** * @return Type of annotation for creation. */ AnnotationTypeEnum::Enum EventAnnotationCreateNewType::getAnnotationType() const { return m_annotationType; } connectome-workbench-1.2.3+git41-gc4c6c90/src/GuiQt/EventAnnotationCreateNewType.h000066400000000000000000000044631300200146000275310ustar00rootroot00000000000000#ifndef __EVENT_ANNOTATION_CREATE_NEW_TYPE_H__ #define __EVENT_ANNOTATION_CREATE_NEW_TYPE_H__ /*LICENSE_START*/ /* * Copyright (C) 2015 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 "AnnotationCoordinateSpaceEnum.h" #include "AnnotationTypeEnum.h" #include "Event.h" namespace caret { class AnnotationFile; class EventAnnotationCreateNewType : public Event { public: EventAnnotationCreateNewType(AnnotationFile* annotationFile, const AnnotationCoordinateSpaceEnum::Enum annotationSpace, const AnnotationTypeEnum::Enum annotationType); virtual ~EventAnnotationCreateNewType(); AnnotationFile* getAnnotationFile() const; AnnotationCoordinateSpaceEnum::Enum getAnnotationSpace() const; AnnotationTypeEnum::Enum getAnnotationType() const; // ADD_NEW_METHODS_HERE private: EventAnnotationCreateNewType(const EventAnnotationCreateNewType&); EventAnnotationCreateNewType& operator=(const EventAnnotationCreateNewType&); AnnotationFile* m_annotationFile; const AnnotationCoordinateSpaceEnum::Enum m_annotationSpace; const AnnotationTypeEnum::Enum m_annotationType; // ADD_NEW_MEMBERS_HERE }; #ifdef __EVENT_ANNOTATION_CREATE_NEW_TYPE_DECLARE__ // #endif // __EVENT_ANNOTATION_CREATE_NEW_TYPE_DECLARE__ } // namespace #endif //__EVENT_ANNOTATION_CREATE_NEW_TYPE_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/GuiQt/EventAnnotationGetDrawnInWindow.cxx000066400000000000000000000044431300200146000305550ustar00rootroot00000000000000 /*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_ANNOTATION_GET_DRAWN_IN_WINDOW_DECLARE__ #include "EventAnnotationGetDrawnInWindow.h" #undef __EVENT_ANNOTATION_GET_DRAWN_IN_WINDOW_DECLARE__ #include "CaretAssert.h" #include "EventTypeEnum.h" using namespace caret; /** * \class caret::EventAnnotationGetDrawnInWindow * \brief Event to get annotations that are drawn in a window. * \ingroup GuiQt */ /** * Constructor. * * @param windowIndex * Index of window. */ EventAnnotationGetDrawnInWindow::EventAnnotationGetDrawnInWindow(const int32_t windowIndex) : Event(EventTypeEnum::EVENT_ANNOTATION_GET_DRAWN_IN_WINDOW), m_windowIndex(windowIndex) { } /** * Destructor. */ EventAnnotationGetDrawnInWindow::~EventAnnotationGetDrawnInWindow() { } /** * @return Index of the window. */ int32_t EventAnnotationGetDrawnInWindow::getWindowIndex() const { return m_windowIndex; } /** * Add annotations that were drawn in the requested window. * * @param annotations * Annotations added. */ void EventAnnotationGetDrawnInWindow::addAnnotations(const std::vector& annotations) { m_annotations.insert(m_annotations.end(), annotations.begin(), annotations.end()); } /** * Get annotations that were drawn in the requested window. * * @param annotationsOut * Annotations output. */ void EventAnnotationGetDrawnInWindow::getAnnotations(std::vector& annotationsOut) const { annotationsOut = m_annotations; } connectome-workbench-1.2.3+git41-gc4c6c90/src/GuiQt/EventAnnotationGetDrawnInWindow.h000066400000000000000000000037711300200146000302050ustar00rootroot00000000000000#ifndef __EVENT_ANNOTATION_GET_DRAWN_IN_WINDOW_H__ #define __EVENT_ANNOTATION_GET_DRAWN_IN_WINDOW_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 Annotation; class EventAnnotationGetDrawnInWindow : public Event { public: EventAnnotationGetDrawnInWindow(const int32_t windowIndex); virtual ~EventAnnotationGetDrawnInWindow(); int32_t getWindowIndex() const; void addAnnotations(const std::vector& annotations); void getAnnotations(std::vector& annotationsOut) const; // ADD_NEW_METHODS_HERE private: EventAnnotationGetDrawnInWindow(const EventAnnotationGetDrawnInWindow&); EventAnnotationGetDrawnInWindow& operator=(const EventAnnotationGetDrawnInWindow&); const int32_t m_windowIndex; std::vector m_annotations; // ADD_NEW_MEMBERS_HERE }; #ifdef __EVENT_ANNOTATION_GET_DRAWN_IN_WINDOW_DECLARE__ // #endif // __EVENT_ANNOTATION_GET_DRAWN_IN_WINDOW_DECLARE__ } // namespace #endif //__EVENT_ANNOTATION_GET_DRAWN_IN_WINDOW_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/GuiQt/EventBrowserWindowContentGet.cxx000066400000000000000000000075461300200146000301450ustar00rootroot00000000000000/*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 "EventBrowserWindowContentGet.h" using namespace caret; #include "CaretAssert.h" /** * \class caret::DisplayGroupEnumComboBox * \brief Combo box for selection of a display group. * \ingroup GuiQt */ /** * Constructor. * @param browserWindowIndex * Index of browser window. */ EventBrowserWindowContentGet::EventBrowserWindowContentGet(const int32_t browserWindowIndex) : Event(EventTypeEnum::EVENT_BROWSER_WINDOW_CONTENT_GET) { m_browserWindowIndex = browserWindowIndex; m_tabIndexForTileTabsHighlighting = -1; m_tileTabsConfiguration = NULL; m_selectedBrowserTabContent = NULL; } /* * Destructor. */ EventBrowserWindowContentGet::~EventBrowserWindowContentGet() { } /** * @return Get the browser window index. */ int32_t EventBrowserWindowContentGet::getBrowserWindowIndex() const { return m_browserWindowIndex; } /** * @return The number of items to draw. */ int32_t EventBrowserWindowContentGet::getNumberOfItemsToDraw() const { return this->browserTabContents.size(); } /** * Add tab content for drawing in a window. */ void EventBrowserWindowContentGet::addTabContentToDraw(BrowserTabContent* browserTabContent) { this->browserTabContents.push_back(browserTabContent); } /** * Get the tab content for drawing in a window. * * @param itemIndex * Index of the item to draw. * @return * Pointer to tab contents for the item index. */ BrowserTabContent* EventBrowserWindowContentGet::getTabContentToDraw(const int32_t itemIndex) { CaretAssertVectorIndex(this->browserTabContents, itemIndex); return this->browserTabContents[itemIndex]; } /** * Set index of tab for highlighting in tile tabs mode. * * @param tabIndex * Index of tab for highlighting. */ void EventBrowserWindowContentGet::setTabIndexForTileTabsHighlighting(const int32_t tabIndex) { m_tabIndexForTileTabsHighlighting = tabIndex; } /** * @return Index of tab for highlighting in Tile Tabs mode. */ int32_t EventBrowserWindowContentGet::getTabIndexForTileTabsHighlighting() const { return m_tabIndexForTileTabsHighlighting; } /** * @return The tile tabs configuration when more than one tab to draw. * May be NULL. */ TileTabsConfiguration* EventBrowserWindowContentGet::getTileTabsConfiguration() const { return m_tileTabsConfiguration; } /** * Set the tile tabs configuration. * * @param tileTabsConfiguration * New selected tile tabs configuration. */ void EventBrowserWindowContentGet::setTileTabsConfiguration(TileTabsConfiguration* tileTabsConfiguration) { m_tileTabsConfiguration = tileTabsConfiguration; } /** * @return The selected browser tab content. May be NULL. */ BrowserTabContent* EventBrowserWindowContentGet::getSelectedBrowserTabContent() { return m_selectedBrowserTabContent; } /** * Set the selected browser tab content. * * @param browserTabContent * The selected browser tab content. */ void EventBrowserWindowContentGet::setSelectedBrowserTabContent(BrowserTabContent* browserTabContent) { m_selectedBrowserTabContent = browserTabContent; } connectome-workbench-1.2.3+git41-gc4c6c90/src/GuiQt/EventBrowserWindowContentGet.h000066400000000000000000000055001300200146000275560ustar00rootroot00000000000000#ifndef __EVENT_BROWSER_WINDOW_CONTENT_GET_H__ #define __EVENT_BROWSER_WINDOW_CONTENT_GET_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 BrowserTabContent; class Model; class TileTabsConfiguration; /// Get the content of a browser window class EventBrowserWindowContentGet : public Event { public: EventBrowserWindowContentGet(const int32_t browserWindowIndex); virtual ~EventBrowserWindowContentGet(); int32_t getBrowserWindowIndex() const; int32_t getNumberOfItemsToDraw() const; void addTabContentToDraw(BrowserTabContent* browserTabContent); BrowserTabContent* getTabContentToDraw(const int32_t itemIndex); void setTabIndexForTileTabsHighlighting(const int32_t tabIndex); int32_t getTabIndexForTileTabsHighlighting() const; TileTabsConfiguration* getTileTabsConfiguration() const; void setTileTabsConfiguration(TileTabsConfiguration* tileTabsConfiguration); BrowserTabContent* getSelectedBrowserTabContent(); void setSelectedBrowserTabContent(BrowserTabContent* browserTabContent); private: EventBrowserWindowContentGet(const EventBrowserWindowContentGet&); EventBrowserWindowContentGet& operator=(const EventBrowserWindowContentGet&); BrowserTabContent* m_selectedBrowserTabContent; /** index of browswer window */ int32_t m_browserWindowIndex; /** Tab content that are to be drawn in the window */ std::vector browserTabContents; /** Index of tab that is highlighted in Tile Tabs mode */ int32_t m_tabIndexForTileTabsHighlighting; /** Selected tile tabs configuration when more than one item to draw */ TileTabsConfiguration* m_tileTabsConfiguration; }; } // namespace #endif // __EVENT_BROWSER_WINDOW_CONTENT_GET_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/GuiQt/EventBrowserWindowCreateTabs.cxx000066400000000000000000000031321300200146000300730ustar00rootroot00000000000000 /*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_BROWSER_WINDOW_CREATE_TABS_DECLARE__ #include "EventBrowserWindowCreateTabs.h" #undef __EVENT_BROWSER_WINDOW_CREATE_TABS_DECLARE__ using namespace caret; /** * \class caret::EventBrowserWindowCreateTabs * \brief If needed, create browser tabs after loading a spec or data files * \ingroup GuiQt */ /** * Constructor. * @param mode * Mode for tab creation. */ EventBrowserWindowCreateTabs::EventBrowserWindowCreateTabs(const Mode mode) : Event(EventTypeEnum::EVENT_BROWSER_WINDOW_CREATE_TABS), m_mode(mode) { } /** * Destructor. */ EventBrowserWindowCreateTabs::~EventBrowserWindowCreateTabs() { } /** * @return The mode. */ EventBrowserWindowCreateTabs::Mode EventBrowserWindowCreateTabs::getMode() const { return m_mode; } connectome-workbench-1.2.3+git41-gc4c6c90/src/GuiQt/EventBrowserWindowCreateTabs.h000066400000000000000000000035031300200146000275220ustar00rootroot00000000000000#ifndef __EVENT_BROWSER_WINDOW_CREATE_TABS__H_ #define __EVENT_BROWSER_WINDOW_CREATE_TABS__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 EventBrowserWindowCreateTabs : public Event { public: enum Mode { MODE_LOADED_DATA_FILE, MODE_LOADED_SPEC_FILE }; EventBrowserWindowCreateTabs(const Mode mode); virtual ~EventBrowserWindowCreateTabs(); Mode getMode() const; private: EventBrowserWindowCreateTabs(const EventBrowserWindowCreateTabs&); EventBrowserWindowCreateTabs& operator=(const EventBrowserWindowCreateTabs&); public: // ADD_NEW_METHODS_HERE private: // ADD_NEW_MEMBERS_HERE const Mode m_mode; }; #ifdef __EVENT_BROWSER_WINDOW_CREATE_TABS_DECLARE__ // #endif // __EVENT_BROWSER_WINDOW_CREATE_TABS_DECLARE__ } // namespace #endif //__EVENT_BROWSER_WINDOW_CREATE_TABS__H_ connectome-workbench-1.2.3+git41-gc4c6c90/src/GuiQt/EventBrowserWindowGraphicsRedrawn.cxx000066400000000000000000000037531300200146000311520ustar00rootroot00000000000000 /*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_BROWSER_WINDOW_GRAPHICS_REDRAWN_DECLARE__ #include "EventBrowserWindowGraphicsRedrawn.h" #undef __EVENT_BROWSER_WINDOW_GRAPHICS_REDRAWN_DECLARE__ #include "BrainBrowserWindow.h" #include "EventTypeEnum.h" using namespace caret; /** * \class caret::EventBrowserWindowGraphicsRedrawn * \brief Event issued when a browser windows graphics are redrawn. * \ingroup GuiQt */ /** * Constructor. */ EventBrowserWindowGraphicsRedrawn::EventBrowserWindowGraphicsRedrawn(BrainBrowserWindow* brainBrowserWindow) : Event(EventTypeEnum::EVENT_BROWSER_WINDOW_GRAPHICS_HAVE_BEEN_REDRAWN) { m_brainBrowserWindow = brainBrowserWindow; m_brainBrowserWindowIndex = m_brainBrowserWindow->getBrowserWindowIndex(); } /** * Destructor. */ EventBrowserWindowGraphicsRedrawn::~EventBrowserWindowGraphicsRedrawn() { } /** * @return Browser window that was redrawn. */ BrainBrowserWindow* EventBrowserWindowGraphicsRedrawn::getBrowserWindow() const { return m_brainBrowserWindow; } /** * @return Index of browser window that was redrawn. */ int32_t EventBrowserWindowGraphicsRedrawn::getBrowserWindowIndex() const { return m_brainBrowserWindowIndex; } connectome-workbench-1.2.3+git41-gc4c6c90/src/GuiQt/EventBrowserWindowGraphicsRedrawn.h000066400000000000000000000037601300200146000305750ustar00rootroot00000000000000#ifndef __EVENT_BROWSER_WINDOW_GRAPHICS_REDRAWN_H__ #define __EVENT_BROWSER_WINDOW_GRAPHICS_REDRAWN_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 BrainBrowserWindow; class EventBrowserWindowGraphicsRedrawn : public Event { public: EventBrowserWindowGraphicsRedrawn(BrainBrowserWindow* brainBrowserWindow); virtual ~EventBrowserWindowGraphicsRedrawn(); BrainBrowserWindow* getBrowserWindow() const; int32_t getBrowserWindowIndex() const; private: EventBrowserWindowGraphicsRedrawn(const EventBrowserWindowGraphicsRedrawn&); EventBrowserWindowGraphicsRedrawn& operator=(const EventBrowserWindowGraphicsRedrawn&); public: // ADD_NEW_METHODS_HERE private: BrainBrowserWindow* m_brainBrowserWindow; int32_t m_brainBrowserWindowIndex; // ADD_NEW_MEMBERS_HERE }; #ifdef __EVENT_BROWSER_WINDOW_GRAPHICS_REDRAWN_DECLARE__ // #endif // __EVENT_BROWSER_WINDOW_GRAPHICS_REDRAWN_DECLARE__ } // namespace #endif //__EVENT_BROWSER_WINDOW_GRAPHICS_REDRAWN_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/GuiQt/EventBrowserWindowNew.cxx000066400000000000000000000050711300200146000266130ustar00rootroot00000000000000/*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 "EventBrowserWindowNew.h" using namespace caret; /** * \class caret::EventBrowserWindowNew * \brief Event Issued to create a new browser window * \ingroup GuiQt */ /** * Constructor. * @param parent * Widget used for window placement (may be NULL). * @param browserTabContent * Initial content for window (may be NULL). */ EventBrowserWindowNew::EventBrowserWindowNew(QWidget* parent, BrowserTabContent* browserTabContent) : Event(EventTypeEnum::EVENT_BROWSER_WINDOW_NEW) { this->parent = parent; this->browserTabContent = browserTabContent; this->browserWindowCreated = NULL; } /** * Constructor. */ EventBrowserWindowNew::EventBrowserWindowNew() : Event(EventTypeEnum::EVENT_BROWSER_WINDOW_NEW) { this->browserTabContent = NULL; } /* * Destructor. */ EventBrowserWindowNew::~EventBrowserWindowNew() { } /** * @return Parent for placement of new window (my be NULL). */ QWidget* EventBrowserWindowNew::getParent() { return this->parent; } /** * Returns the tab content for the window. If the * returned value is NULL, then a new tab should be * created. * * @return Returns the browser tab content. */ BrowserTabContent* EventBrowserWindowNew::getBrowserTabContent() const { return this->browserTabContent; } /** * @return The browser window that was created. */ BrainBrowserWindow* EventBrowserWindowNew::getBrowserWindowCreated() const { return this->browserWindowCreated; } /** * Set the browser window that was created. * param browserWindowCreated * Browser window that was created. */ void EventBrowserWindowNew::setBrowserWindowCreated(BrainBrowserWindow* browserWindowCreated) { this->browserWindowCreated = browserWindowCreated; } connectome-workbench-1.2.3+git41-gc4c6c90/src/GuiQt/EventBrowserWindowNew.h000066400000000000000000000041271300200146000262410ustar00rootroot00000000000000#ifndef __EVENT_BROWSER_WINDOW_NEW_H__ #define __EVENT_BROWSER_WINDOW_NEW_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" class QWidget; namespace caret { class BrainBrowserWindow; class BrowserTabContent; /// Create a new browser window class EventBrowserWindowNew : public Event { public: EventBrowserWindowNew(); EventBrowserWindowNew(QWidget* parent, BrowserTabContent* browserTabContent); virtual ~EventBrowserWindowNew(); QWidget* getParent(); BrowserTabContent* getBrowserTabContent() const; BrainBrowserWindow* getBrowserWindowCreated() const; void setBrowserWindowCreated(BrainBrowserWindow* browserWindowCreated); private: EventBrowserWindowNew(const EventBrowserWindowNew&); EventBrowserWindowNew& operator=(const EventBrowserWindowNew&); /** Widget used for placement of new window */ QWidget* parent; /** If not NULL, contains tab for the new window */ BrowserTabContent* browserTabContent; /** Window that was created. */ BrainBrowserWindow* browserWindowCreated; }; } // namespace #endif // __EVENT_BROWSER_WINDOW_NEW_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/GuiQt/EventGetOrSetUserInputModeProcessor.cxx000066400000000000000000000067031300200146000314110ustar00rootroot00000000000000/*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 "EventGetOrSetUserInputModeProcessor.h" using namespace caret; /** * \class caret::EventGetOrSetUserInputModeProcessor * \brief Event that acts as a getter/setting for user input mode. * \ingroup GuiQt */ /** * Constructor for SETTING the user input mode. * * @param windowIndex * Index of the window for the user input mode. * @param userInputMode * The requested input mode. */ EventGetOrSetUserInputModeProcessor::EventGetOrSetUserInputModeProcessor(const int32_t windowIndex, const UserInputModeAbstract::UserInputMode userInputMode) : Event(EventTypeEnum::EVENT_GET_OR_SET_USER_INPUT_MODE) { this->userInputProcessor = NULL; this->userInputMode = userInputMode; this->windowIndex = windowIndex; this->modeGetOrSet = SET; } /** * Constructor for GETTING the user input mode. * * @param windowIndex * Index of the window for the user input mode. */ EventGetOrSetUserInputModeProcessor::EventGetOrSetUserInputModeProcessor(const int32_t windowIndex) : Event(EventTypeEnum::EVENT_GET_OR_SET_USER_INPUT_MODE) { this->userInputProcessor = NULL; this->userInputMode = UserInputModeAbstract::INVALID; this->windowIndex = windowIndex; this->modeGetOrSet = GET; } /* * Destructor. */ EventGetOrSetUserInputModeProcessor::~EventGetOrSetUserInputModeProcessor() { } /** * @return The window index. */ int32_t EventGetOrSetUserInputModeProcessor::getWindowIndex() const { return this->windowIndex; } /** * @return The requested input mode. */ UserInputModeAbstract::UserInputMode EventGetOrSetUserInputModeProcessor::getUserInputMode() const { return this->userInputMode; } /** * Set the user input processor which is called when GETTING. * @param userInputProcessor * Value of current input processor. */ void EventGetOrSetUserInputModeProcessor::setUserInputProcessor(UserInputModeAbstract* userInputProcessor) { this->userInputProcessor = userInputProcessor; this->userInputMode = this->userInputProcessor->getUserInputMode(); } /** * @return The user input processor valid only when GETTING. */ UserInputModeAbstract* EventGetOrSetUserInputModeProcessor::getUserInputProcessor() { return this->userInputProcessor; } /** * @return true if this event is GETTING the user input mode. */ bool EventGetOrSetUserInputModeProcessor::isGetUserInputMode() const { return (this->modeGetOrSet == GET); } /** * @return true if this event is SETTING the user input mode. */ bool EventGetOrSetUserInputModeProcessor::isSetUserInputMode() const { return (this->modeGetOrSet == SET); } connectome-workbench-1.2.3+git41-gc4c6c90/src/GuiQt/EventGetOrSetUserInputModeProcessor.h000066400000000000000000000051401300200146000310300ustar00rootroot00000000000000#ifndef __EVENT_GET_OR_SET_USER_INPUT_MODE_PROCESSOR_H__ #define __EVENT_GET_OR_SET_USER_INPUT_MODE_PROCESSOR_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" #include "UserInputModeAbstract.h" namespace caret { /// Event for getting or setting the user input mode processor class EventGetOrSetUserInputModeProcessor : public Event { public: EventGetOrSetUserInputModeProcessor(const int32_t windowIndex, const UserInputModeAbstract::UserInputMode userInputMode); EventGetOrSetUserInputModeProcessor(const int32_t windowIndex); virtual ~EventGetOrSetUserInputModeProcessor(); bool isGetUserInputMode() const; bool isSetUserInputMode() const; int32_t getWindowIndex() const; UserInputModeAbstract::UserInputMode getUserInputMode() const; void setUserInputProcessor(UserInputModeAbstract* userInputProcessor); UserInputModeAbstract* getUserInputProcessor(); private: enum MODE_GET_OR_SET { GET, SET }; EventGetOrSetUserInputModeProcessor(const EventGetOrSetUserInputModeProcessor&); EventGetOrSetUserInputModeProcessor& operator=(const EventGetOrSetUserInputModeProcessor&); /** Is set when GETTING input mode */ UserInputModeAbstract* userInputProcessor; /** Requested input mode for SETTING and set when GETTING*/ UserInputModeAbstract::UserInputMode userInputMode; /** index of window for update */ int32_t windowIndex; /** getting or setting */ MODE_GET_OR_SET modeGetOrSet; }; } // namespace #endif // __EVENT_GET_OR_SET_USER_INPUT_MODE_PROCESSOR_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/GuiQt/EventGraphicsUpdateAllWindows.cxx000066400000000000000000000033341300200146000302350ustar00rootroot00000000000000/*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 "EventGraphicsUpdateAllWindows.h" using namespace caret; /** * \class caret::EventGraphicsUpdateAllWindows * \brief Event for updating all Window gui elements. * \ingroup GuiQt */ /** * Constructor. * @param doRepaint * If true, a repaint is performed and this event does not * return until painting is complete. If false, an update * is performed which schedules a repaint but does not dictate * when the repaint is performed. */ EventGraphicsUpdateAllWindows::EventGraphicsUpdateAllWindows(const bool doRepaint) : Event(EventTypeEnum::EVENT_GRAPHICS_UPDATE_ALL_WINDOWS) { this->doRepaint = doRepaint; } /* * Destructor. */ EventGraphicsUpdateAllWindows::~EventGraphicsUpdateAllWindows() { } /** * @return Indicates a repaint (instead of updates) is * to be performed. */ bool EventGraphicsUpdateAllWindows::isRepaint() const { return this->doRepaint; } connectome-workbench-1.2.3+git41-gc4c6c90/src/GuiQt/EventGraphicsUpdateAllWindows.h000066400000000000000000000030461300200146000276620ustar00rootroot00000000000000#ifndef __EVENT_GRAPHICS_UPDATE_ALL_WINDOWS_H__ #define __EVENT_GRAPHICS_UPDATE_ALL_WINDOWS_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 { /// Event for updating all graphics windows. class EventGraphicsUpdateAllWindows : public Event { public: EventGraphicsUpdateAllWindows(const bool doRepaint = false); virtual ~EventGraphicsUpdateAllWindows(); bool isRepaint() const; private: EventGraphicsUpdateAllWindows(const EventGraphicsUpdateAllWindows&); EventGraphicsUpdateAllWindows& operator=(const EventGraphicsUpdateAllWindows&); bool doRepaint; }; } // namespace #endif // __EVENT_GRAPHICS_UPDATE_ALL_WINDOWS_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/GuiQt/EventGraphicsUpdateOneWindow.cxx000066400000000000000000000024571300200146000300700ustar00rootroot00000000000000/*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 "EventGraphicsUpdateOneWindow.h" using namespace caret; /** * \class caret::EventGraphicsUpdateOneWindow * \brief Event for updating a single window * \ingroup GuiQt */ /** * Constructor. */ EventGraphicsUpdateOneWindow::EventGraphicsUpdateOneWindow(const int32_t windowIndex) : Event(EventTypeEnum::EVENT_GRAPHICS_UPDATE_ONE_WINDOW) { this->windowIndex = windowIndex; } /* * Destructor. */ EventGraphicsUpdateOneWindow::~EventGraphicsUpdateOneWindow() { } connectome-workbench-1.2.3+git41-gc4c6c90/src/GuiQt/EventGraphicsUpdateOneWindow.h000066400000000000000000000032561300200146000275130ustar00rootroot00000000000000#ifndef __EVENT_GRAPHICS_UPDATE_ONE_WINDOW_H__ #define __EVENT_GRAPHICS_UPDATE_ONE_WINDOW_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 { /// Event for updating graphics in one window. class EventGraphicsUpdateOneWindow : public Event { public: EventGraphicsUpdateOneWindow(const int32_t windowIndex); virtual ~EventGraphicsUpdateOneWindow(); /// get the index of the window that is to be updated. int32_t getWindowIndex() const { return this->windowIndex; } private: EventGraphicsUpdateOneWindow(const EventGraphicsUpdateOneWindow&); EventGraphicsUpdateOneWindow& operator=(const EventGraphicsUpdateOneWindow&); /** index of window for update */ int32_t windowIndex; }; } // namespace #endif // __EVENT_GRAPHICS_UPDATE_ONE_WINDOW_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/GuiQt/EventHelpViewerDisplay.cxx000066400000000000000000000036431300200146000267310ustar00rootroot00000000000000 /*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_HELP_VIEWER_DISPLAY_DECLARE__ #include "EventHelpViewerDisplay.h" #undef __EVENT_HELP_VIEWER_DISPLAY_DECLARE__ #include "CaretAssert.h" #include "EventTypeEnum.h" using namespace caret; /** * \class caret::EventHelpViewerDisplay * \brief Display the topic in the help viewer. * \ingroup GuiQt */ /** * Constructor displays help dialog with default topic. */ EventHelpViewerDisplay::EventHelpViewerDisplay(BrainBrowserWindow* brainBrowserWindow) : Event(EventTypeEnum::EVENT_HELP_VIEWER_DISPLAY) { m_brainBrowserWindow = brainBrowserWindow; m_helpPageName = ""; } /** * Destructor. */ 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* EventHelpViewerDisplay::getBrainBrowserWindow() const { return m_brainBrowserWindow; } /** * @return Name of page for display (may be empty string). */ AString EventHelpViewerDisplay::getHelpPageName() const { return m_helpPageName; } connectome-workbench-1.2.3+git41-gc4c6c90/src/GuiQt/EventHelpViewerDisplay.h000066400000000000000000000034721300200146000263560ustar00rootroot00000000000000#ifndef __EVENT_HELP_VIEWER_DISPLAY_H__ #define __EVENT_HELP_VIEWER_DISPLAY_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 BrainBrowserWindow; class EventHelpViewerDisplay : public Event { public: EventHelpViewerDisplay(BrainBrowserWindow* brainBrowserWindow); virtual ~EventHelpViewerDisplay(); BrainBrowserWindow* getBrainBrowserWindow() const; AString getHelpPageName() const; // ADD_NEW_METHODS_HERE private: EventHelpViewerDisplay(const EventHelpViewerDisplay&); EventHelpViewerDisplay& operator=(const EventHelpViewerDisplay&); BrainBrowserWindow* m_brainBrowserWindow; AString m_helpPageName; // ADD_NEW_MEMBERS_HERE }; #ifdef __EVENT_HELP_VIEWER_DISPLAY_DECLARE__ // #endif // __EVENT_HELP_VIEWER_DISPLAY_DECLARE__ } // namespace #endif //__EVENT_HELP_VIEWER_DISPLAY_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/GuiQt/EventIdentificationRequest.cxx000066400000000000000000000047621300200146000276360ustar00rootroot00000000000000 /*LICENSE_START*/ /* * Copyright (C) 2015 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_IDENTIFICATION_REQUEST_DECLARE__ #include "EventIdentificationRequest.h" #undef __EVENT_IDENTIFICATION_REQUEST_DECLARE__ #include "CaretAssert.h" #include "EventTypeEnum.h" using namespace caret; /** * \class caret::EventIdentificationRequest * \brief Perform an identification operation in a window * \ingroup GuiQt */ /** * Constructor. */ EventIdentificationRequest::EventIdentificationRequest(const int32_t windowIndex, const int32_t windowX, const int32_t windowY) : Event(EventTypeEnum::EVENT_IDENTIFICATION_REQUEST), m_windowIndex(windowIndex), m_windowX(windowX), m_windowY(windowY) { m_selectionManager = NULL; } /** * Destructor. */ EventIdentificationRequest::~EventIdentificationRequest() { } /** * @return Selection manager (NULL if event failed). */ SelectionManager* EventIdentificationRequest::getSelectionManager() const { return m_selectionManager; } /** * Set the selection manager. * * @param selectionManager * The selection manager. */ void EventIdentificationRequest::setSelectionManager(SelectionManager* selectionManager) { m_selectionManager = selectionManager; } /** * @return Index of window in which identification is performed. */ int32_t EventIdentificationRequest::getWindowIndex() const { return m_windowIndex; } /** * @return X window coordinate for identification. */ int32_t EventIdentificationRequest::getWindowX() const { return m_windowX; } /** * @return Y window coordinate for identification. */ int32_t EventIdentificationRequest::getWindowY() const { return m_windowY; } connectome-workbench-1.2.3+git41-gc4c6c90/src/GuiQt/EventIdentificationRequest.h000066400000000000000000000043021300200146000272510ustar00rootroot00000000000000#ifndef __EVENT_IDENTIFICATION_REQUEST_H__ #define __EVENT_IDENTIFICATION_REQUEST_H__ /*LICENSE_START*/ /* * Copyright (C) 2015 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 SelectionManager; class EventIdentificationRequest : public Event { public: EventIdentificationRequest(const int32_t windowIndex, const int32_t windowX, const int32_t windowY); virtual ~EventIdentificationRequest(); SelectionManager* getSelectionManager() const; void setSelectionManager(SelectionManager* selectionManager); int32_t getWindowIndex() const; int32_t getWindowX() const; int32_t getWindowY() const; // ADD_NEW_METHODS_HERE private: EventIdentificationRequest(const EventIdentificationRequest&); EventIdentificationRequest& operator=(const EventIdentificationRequest&); const int32_t m_windowIndex; const int32_t m_windowX; const int32_t m_windowY; SelectionManager* m_selectionManager; // ADD_NEW_MEMBERS_HERE }; #ifdef __EVENT_IDENTIFICATION_REQUEST_DECLARE__ // #endif // __EVENT_IDENTIFICATION_REQUEST_DECLARE__ } // namespace #endif //__EVENT_IDENTIFICATION_REQUEST_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/GuiQt/EventImageCapture.cxx000066400000000000000000000113351300200146000256740ustar00rootroot00000000000000 /*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_IMAGE_CAPTURE_DECLARE__ #include "EventImageCapture.h" #undef __EVENT_IMAGE_CAPTURE_DECLARE__ #include "CaretAssert.h" #include "EventTypeEnum.h" using namespace caret; /** * \class caret::EventImageCapture * \brief Event for capturing images. * \ingroup GuiQt */ /** * Constructor for capturing image of window without any resizing. * * @param browserWindowIndex * The browser window index. */ EventImageCapture::EventImageCapture(const int32_t browserWindowIndex) : Event(EventTypeEnum::EVENT_IMAGE_CAPTURE), m_browserWindowIndex(browserWindowIndex), m_captureOffsetX(0), m_captureOffsetY(0), m_captureWidth(0), m_captureHeight(0), m_outputWidth(0), m_outputHeight(0) { } /** * Constructor for capturing image of window with the given sizing. If * the X & Y sizes are both zero, the no image resizing is performed. * * @param browserWindowIndex * The browser window index. * @param captureOffsetX * X-offset for capturing image. * @param captureOffsetY * Y-offset for capturing image. * @param captureWidth * Width for capturing image. * @param captureHeight * Height for capturing image. * @param outputWidth * Width of output image. * @param outputHeight * Height of output image. */ EventImageCapture::EventImageCapture(const int32_t browserWindowIndex, const int32_t captureOffsetX, const int32_t captureOffsetY, const int32_t captureWidth, const int32_t captureHeight, const int32_t outputWidth, const int32_t outputHeight) : Event(EventTypeEnum::EVENT_IMAGE_CAPTURE), m_browserWindowIndex(browserWindowIndex), m_captureOffsetX(captureOffsetX), m_captureOffsetY(captureOffsetY), m_captureWidth(captureWidth), m_captureHeight(captureHeight), m_outputWidth(outputWidth), m_outputHeight(outputHeight) { m_backgroundColor[0] = 0; m_backgroundColor[1] = 0; m_backgroundColor[2] = 0; } /** * Destructor. */ EventImageCapture::~EventImageCapture() { } /** * @return The browser window index. */ int32_t EventImageCapture::getBrowserWindowIndex() const { return m_browserWindowIndex; } /** * @return The capture X offset */ int32_t EventImageCapture::getCaptureOffsetX() const { return m_captureOffsetX; } /** * @return The capture Y offset */ int32_t EventImageCapture::getCaptureOffsetY() const { return m_captureOffsetY; } /** * @return The capture width */ int32_t EventImageCapture::getCaptureWidth() const { return m_captureWidth; } /** * @return The capture height */ int32_t EventImageCapture::getCaptureHeight() const { return m_captureHeight; } /** * @return The output image width. */ int32_t EventImageCapture::getOutputWidth() const { return m_outputWidth; } /** * @return The output image height. */ int32_t EventImageCapture::getOutputHeight() const { return m_outputHeight; } /** * Get the graphics area's background color. * * @param backgroundColor * RGB components of background color [0, 255] */ void EventImageCapture::getBackgroundColor(uint8_t backgroundColor[3]) const { backgroundColor[0] = m_backgroundColor[0]; backgroundColor[1] = m_backgroundColor[1]; backgroundColor[2] = m_backgroundColor[2]; } /** * Set the graphics area's background color. * * @param backgroundColor * RGB components of background color [0, 255] */ void EventImageCapture::setBackgroundColor(const uint8_t backgroundColor[3]) { m_backgroundColor[0] = backgroundColor[0]; m_backgroundColor[1] = backgroundColor[1]; m_backgroundColor[2] = backgroundColor[2]; } /** * @return The captured image. */ QImage EventImageCapture::getImage() const { return m_image; } /** * Set the captured image. */ void EventImageCapture::setImage(const QImage& image) { m_image = image; } connectome-workbench-1.2.3+git41-gc4c6c90/src/GuiQt/EventImageCapture.h000066400000000000000000000056671300200146000253340ustar00rootroot00000000000000#ifndef __EVENT_IMAGE_CAPTURE_H__ #define __EVENT_IMAGE_CAPTURE_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 "Event.h" namespace caret { class EventImageCapture : public Event { public: EventImageCapture(const int32_t browserWindowIndex, const int32_t captureOffsetX, const int32_t captureOffsetY, const int32_t captureWidth, const int32_t captureHeight, const int32_t outputWidth, const int32_t outputHeight); EventImageCapture(const int32_t browserWindowIndex); virtual ~EventImageCapture(); int32_t getBrowserWindowIndex() const; int32_t getOutputWidth() const; int32_t getOutputHeight() const; int32_t getCaptureOffsetX() const; int32_t getCaptureOffsetY() const; int32_t getCaptureWidth() const; int32_t getCaptureHeight() const; QImage getImage() const; void setImage(const QImage& image); void getBackgroundColor(uint8_t backgroundColor[3]) const; void setBackgroundColor(const uint8_t backgroundColor[3]); private: EventImageCapture(const EventImageCapture&); EventImageCapture& operator=(const EventImageCapture&); public: // ADD_NEW_METHODS_HERE private: // ADD_NEW_MEMBERS_HERE const int32_t m_browserWindowIndex; const int32_t m_captureOffsetX; const int32_t m_captureOffsetY; const int32_t m_captureWidth; const int32_t m_captureHeight; const int32_t m_outputWidth; const int32_t m_outputHeight; uint8_t m_backgroundColor[3]; QImage m_image; }; #ifdef __EVENT_IMAGE_CAPTURE_DECLARE__ // #endif // __EVENT_IMAGE_CAPTURE_DECLARE__ } // namespace #endif //__EVENT_IMAGE_CAPTURE_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/GuiQt/EventMacDockMenuUpdate.cxx000066400000000000000000000026131300200146000266160ustar00rootroot00000000000000 /*LICENSE_START*/ /* * Copyright (C) 2015 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_MAC_DOCK_MENU_UPDATE_DECLARE__ #include "EventMacDockMenuUpdate.h" #undef __EVENT_MAC_DOCK_MENU_UPDATE_DECLARE__ #include "CaretAssert.h" #include "EventTypeEnum.h" using namespace caret; /** * \class caret::EventMacDockMenuUpdate * \brief * \ingroup GuiQt * * */ /** * Constructor. */ EventMacDockMenuUpdate::EventMacDockMenuUpdate() : Event(EventTypeEnum::EVENT_MAC_DOCK_MENU_UPDATE) { } /** * Destructor. */ EventMacDockMenuUpdate::~EventMacDockMenuUpdate() { } connectome-workbench-1.2.3+git41-gc4c6c90/src/GuiQt/EventMacDockMenuUpdate.h000066400000000000000000000030441300200146000262420ustar00rootroot00000000000000#ifndef __EVENT_MAC_DOCK_MENU_UPDATE_H__ #define __EVENT_MAC_DOCK_MENU_UPDATE_H__ /*LICENSE_START*/ /* * Copyright (C) 2015 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 EventMacDockMenuUpdate : public Event { public: EventMacDockMenuUpdate(); virtual ~EventMacDockMenuUpdate(); // ADD_NEW_METHODS_HERE private: EventMacDockMenuUpdate(const EventMacDockMenuUpdate&); EventMacDockMenuUpdate& operator=(const EventMacDockMenuUpdate&); // ADD_NEW_MEMBERS_HERE }; #ifdef __EVENT_MAC_DOCK_MENU_UPDATE_DECLARE__ // #endif // __EVENT_MAC_DOCK_MENU_UPDATE_DECLARE__ } // namespace #endif //__EVENT_MAC_DOCK_MENU_UPDATE_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/GuiQt/EventOperatingSystemRequestOpenDataFile.cxx000066400000000000000000000032561300200146000322530ustar00rootroot00000000000000/*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 "EventOperatingSystemRequestOpenDataFile.h" using namespace caret; /** * \class caret::EventOperatingSystemRequestOpenDataFile * \brief Event for responding to an open file request from the operating system * \ingroup GuiQt * * On Macs, the QApplication instance may receive an open data file request * from the operating system. This event is used to by the QApplication * instance (actually MacApplication derived from QApplication) to route * the request for opening the file to the GUI. */ /** * Constructor. */ EventOperatingSystemRequestOpenDataFile::EventOperatingSystemRequestOpenDataFile(const AString& dataFileName) : Event(EventTypeEnum::EVENT_OPERATING_SYSTEM_REQUEST_OPEN_DATA_FILE), m_dataFileName(dataFileName) { } /* * Destructor. */ EventOperatingSystemRequestOpenDataFile::~EventOperatingSystemRequestOpenDataFile() { } connectome-workbench-1.2.3+git41-gc4c6c90/src/GuiQt/EventOperatingSystemRequestOpenDataFile.h000066400000000000000000000032071300200146000316740ustar00rootroot00000000000000#ifndef __EVENT_OPERATING_SYSTEM_REQUEST_OPEN_DATA_FILE_H__ #define __EVENT_OPERATING_SYSTEM_REQUEST_OPEN_DATA_FILE_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 EventOperatingSystemRequestOpenDataFile : public Event { public: EventOperatingSystemRequestOpenDataFile(const AString& dataFileName); virtual ~EventOperatingSystemRequestOpenDataFile(); AString getDataFileName() const { return m_dataFileName; } private: EventOperatingSystemRequestOpenDataFile(const EventOperatingSystemRequestOpenDataFile&); EventOperatingSystemRequestOpenDataFile& operator=(const EventOperatingSystemRequestOpenDataFile&); const AString m_dataFileName; }; } // namespace #endif // __EVENT_OPERATING_SYSTEM_REQUEST_OPEN_DATA_FILE_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/GuiQt/EventOverlaySettingsEditorDialogRequest.cxx000066400000000000000000000041311300200146000323240ustar00rootroot00000000000000/*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 "EventOverlaySettingsEditorDialogRequest.h" using namespace caret; /** * \class caret::EventOverlaySettingsEditorDialogRequest * \brief Event for creating overlay settings editor dialog * \ingroup GuiQt */ /** * Constructor. * @param browserWindowIndex * Index of browser window. * @param overlay * Overlay for map editor. * @param mapFile * Caret Mappable Data File. * @param mapIndex * Map index in mapFile. */ EventOverlaySettingsEditorDialogRequest::EventOverlaySettingsEditorDialogRequest(const Mode mode, const int32_t browserWindowIndex, Overlay* overlay, CaretMappableDataFile* mapFile, const int32_t mapIndex) : Event(EventTypeEnum::EVENT_OVERLAY_SETTINGS_EDITOR_SHOW), m_mode(mode) { m_browserWindowIndex = browserWindowIndex; m_overlay = overlay; m_mapFile = mapFile; m_mapIndex = mapIndex; } /* * Destructor. */ EventOverlaySettingsEditorDialogRequest::~EventOverlaySettingsEditorDialogRequest() { } connectome-workbench-1.2.3+git41-gc4c6c90/src/GuiQt/EventOverlaySettingsEditorDialogRequest.h000066400000000000000000000065601300200146000317610ustar00rootroot00000000000000#ifndef __EVENT_MAP_SETTINGS_EDITOR_DIALOG_REQUEST_H__ #define __EVENT_MAP_SETTINGS_EDITOR_DIALOG_REQUEST_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 CaretMappableDataFile; class Overlay; /// Event for showing edit map scalar color mapping editor class EventOverlaySettingsEditorDialogRequest : public Event { public: enum Mode { MODE_SHOW_EDITOR, MODE_OVERLAY_MAP_CHANGED, MODE_UPDATE_ALL }; EventOverlaySettingsEditorDialogRequest(const Mode mode, const int32_t browserWindowIndex, Overlay* overlay, CaretMappableDataFile* mapFile, const int32_t mapIndex); virtual ~EventOverlaySettingsEditorDialogRequest(); /** * @return The mode (show or update) */ Mode getMode() const { return m_mode; } /** * @return Get the index of the browser window for palette being edited. */ int32_t getBrowserWindowIndex() const { return m_browserWindowIndex; } /** * @return Map file containing map whose color palette is edited */ CaretMappableDataFile* getCaretMappableDataFile() const { return m_mapFile; } /** * @return Index of map in the map file */ int32_t getMapIndex() const { return m_mapIndex; } /** * @return The overlay for the editor. */ Overlay* getOverlay() { return m_overlay; } /** * @return The overlay for the editor. */ const Overlay* getOverlay() const { return m_overlay; } private: EventOverlaySettingsEditorDialogRequest(const EventOverlaySettingsEditorDialogRequest&); EventOverlaySettingsEditorDialogRequest& operator=(const EventOverlaySettingsEditorDialogRequest&); /** The mode show/update */ const Mode m_mode; /** index of browser window for palette editing */ int32_t m_browserWindowIndex; /** Overlay for editor. */ Overlay* m_overlay; /** Map file containing map whose color palette is edited */ CaretMappableDataFile* m_mapFile; /** Index of map in the map file */ int32_t m_mapIndex; }; } // namespace #endif // __EVENT_MAP_SETTINGS_EDITOR_DIALOG_REQUEST_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/GuiQt/EventPaletteColorMappingEditorDialogRequest.cxx000066400000000000000000000037021300200146000330760ustar00rootroot00000000000000 /*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_PALETTE_COLOR_MAPPING_EDITOR_DIALOG_REQUEST_DECLARE__ #include "EventPaletteColorMappingEditorDialogRequest.h" #undef __EVENT_PALETTE_COLOR_MAPPING_EDITOR_DIALOG_REQUEST_DECLARE__ #include "CaretAssert.h" #include "EventTypeEnum.h" using namespace caret; /** * \class caret::EventPaletteColorMappingEditorDialogRequest * \brief Event for editing palette color mapping in a dialog * \ingroup GuiQt */ /** * Constructor. */ EventPaletteColorMappingEditorDialogRequest::EventPaletteColorMappingEditorDialogRequest(const int32_t browserWindowIndex, CaretMappableDataFile* mapFile, const int32_t mapIndex) : Event(EventTypeEnum::EVENT_PALETTE_COLOR_MAPPING_EDITOR_SHOW), m_browserWindowIndex(browserWindowIndex), m_mapFile(mapFile), m_mapIndex(mapIndex) { CaretAssert(mapFile); CaretAssert(mapIndex >= 0); } /** * Destructor. */ EventPaletteColorMappingEditorDialogRequest::~EventPaletteColorMappingEditorDialogRequest() { } connectome-workbench-1.2.3+git41-gc4c6c90/src/GuiQt/EventPaletteColorMappingEditorDialogRequest.h000066400000000000000000000055101300200146000325220ustar00rootroot00000000000000#ifndef __EVENT_PALETTE_COLOR_MAPPING_EDITOR_DIALOG_REQUEST_H__ #define __EVENT_PALETTE_COLOR_MAPPING_EDITOR_DIALOG_REQUEST_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 CaretMappableDataFile; class EventPaletteColorMappingEditorDialogRequest : public Event { public: EventPaletteColorMappingEditorDialogRequest(const int32_t browserWindowIndex, CaretMappableDataFile* mapFile, const int32_t mapIndex); virtual ~EventPaletteColorMappingEditorDialogRequest(); /** * @return Get the index of the browser window for palette being edited. */ int32_t getBrowserWindowIndex() const { return m_browserWindowIndex; } /** * @return Map file containing map whose color palette is edited */ CaretMappableDataFile* getCaretMappableDataFile() const { return m_mapFile; } /** * @return Index of map in the map file */ int32_t getMapIndex() const { return m_mapIndex; } // ADD_NEW_METHODS_HERE private: EventPaletteColorMappingEditorDialogRequest(const EventPaletteColorMappingEditorDialogRequest&); EventPaletteColorMappingEditorDialogRequest& operator=(const EventPaletteColorMappingEditorDialogRequest&); /** index of browser window for palette editing */ int32_t m_browserWindowIndex; /** Map file containing map whose color palette is edited */ CaretMappableDataFile* m_mapFile; /** Index of map in the map file */ int32_t m_mapIndex; // ADD_NEW_MEMBERS_HERE }; #ifdef __EVENT_PALETTE_COLOR_MAPPING_EDITOR_DIALOG_REQUEST_DECLARE__ // #endif // __EVENT_PALETTE_COLOR_MAPPING_EDITOR_DIALOG_REQUEST_DECLARE__ } // namespace #endif //__EVENT_PALETTE_COLOR_MAPPING_EDITOR_DIALOG_REQUEST_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/GuiQt/EventUpdateInformationWindows.cxx000066400000000000000000000051601300200146000303300ustar00rootroot00000000000000/*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 "Brain.h" #include "EventUpdateInformationWindows.h" #include "GuiManager.h" #include "IdentificationManager.h" #include "IdentifiedItem.h" using namespace caret; /** * \class caret::EventUpdateInformationWindows * \brief Event for updating Information Windows * \ingroup GuiQt */ /** * Construct an event for update of information window with * content from the identification manager. The given * text will be added to the information manager prior to * update of information window. * * @param informationText * Next text that will be added to the information manager. */ EventUpdateInformationWindows::EventUpdateInformationWindows(const AString& informationText) : Event(EventTypeEnum::EVENT_UPDATE_INFORMATION_WINDOWS) { if ( ! informationText.isEmpty()) { IdentificationManager* idManager = GuiManager::get()->getBrain()->getIdentificationManager(); CaretAssert(idManager); idManager->addIdentifiedItem(new IdentifiedItem(informationText)); } m_important = true; } /** * Construct an event for update of information window with * content from the identification manager. */ EventUpdateInformationWindows::EventUpdateInformationWindows() : Event(EventTypeEnum::EVENT_UPDATE_INFORMATION_WINDOWS) { m_important = true; } /** * Destructor. */ EventUpdateInformationWindows::~EventUpdateInformationWindows() { } /** * Set the message as not important so that the toolbox * does NOT switch to the information panel. */ EventUpdateInformationWindows* EventUpdateInformationWindows::setNotImportant() { m_important = false; return this; } /** * @return Is this message important. If so, * the Toolbox will switch to the information * display. */ bool EventUpdateInformationWindows::isImportant() const { return m_important; } connectome-workbench-1.2.3+git41-gc4c6c90/src/GuiQt/EventUpdateInformationWindows.h000066400000000000000000000032711300200146000277560ustar00rootroot00000000000000#ifndef __EVENT_UPDATE_INFORMATION_WINDOWS_H__ #define __EVENT_UPDATE_INFORMATION_WINDOWS_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 BrainStructure; /// Request display of text in the information window(s). class EventUpdateInformationWindows : public Event { public: EventUpdateInformationWindows(); EventUpdateInformationWindows(const AString& informationText); virtual ~EventUpdateInformationWindows(); EventUpdateInformationWindows* setNotImportant(); bool isImportant() const; private: EventUpdateInformationWindows(const EventUpdateInformationWindows&); EventUpdateInformationWindows& operator=(const EventUpdateInformationWindows&); bool m_important; }; } // namespace #endif // __EVENT_UPDATE_INFORMATION_WINDOWS_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/GuiQt/EventUpdateVolumeEditingToolBar.cxx000066400000000000000000000027361300200146000305340ustar00rootroot00000000000000 /*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_UPDATE_VOLUME_EDITING_TOOL_BAR_DECLARE__ #include "EventUpdateVolumeEditingToolBar.h" #undef __EVENT_UPDATE_VOLUME_EDITING_TOOL_BAR_DECLARE__ #include "CaretAssert.h" #include "EventTypeEnum.h" using namespace caret; /** * \class caret::EventUpdateVolumeEditingToolBar * \brief * \ingroup GuiQt * * */ /** * Constructor. */ EventUpdateVolumeEditingToolBar::EventUpdateVolumeEditingToolBar() : Event(EventTypeEnum::EVENT_UPDATE_VOLUME_EDITING_TOOLBAR) { } /** * Destructor. */ EventUpdateVolumeEditingToolBar::~EventUpdateVolumeEditingToolBar() { } connectome-workbench-1.2.3+git41-gc4c6c90/src/GuiQt/EventUpdateVolumeEditingToolBar.h000066400000000000000000000032251300200146000301530ustar00rootroot00000000000000#ifndef __EVENT_UPDATE_VOLUME_EDITING_TOOL_BAR_H__ #define __EVENT_UPDATE_VOLUME_EDITING_TOOL_BAR_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 EventUpdateVolumeEditingToolBar : public Event { public: EventUpdateVolumeEditingToolBar(); virtual ~EventUpdateVolumeEditingToolBar(); // ADD_NEW_METHODS_HERE private: EventUpdateVolumeEditingToolBar(const EventUpdateVolumeEditingToolBar&); EventUpdateVolumeEditingToolBar& operator=(const EventUpdateVolumeEditingToolBar&); // ADD_NEW_MEMBERS_HERE }; #ifdef __EVENT_UPDATE_VOLUME_EDITING_TOOL_BAR_DECLARE__ // #endif // __EVENT_UPDATE_VOLUME_EDITING_TOOL_BAR_DECLARE__ } // namespace #endif //__EVENT_UPDATE_VOLUME_EDITING_TOOL_BAR_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/GuiQt/EventUpdateYokedWindows.cxx000066400000000000000000000050241300200146000271150ustar00rootroot00000000000000 /*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_UPDATE_YOKED_WINDOWS_DECLARE__ #include "EventUpdateYokedWindows.h" #undef __EVENT_UPDATE_YOKED_WINDOWS_DECLARE__ #include "EventTypeEnum.h" using namespace caret; /** * \class caret::EventUpdateYokedWindows * \brief Updates windows that are yoked. * \ingroup GuiQt */ /** * Constructor for a window. This given window will NOT be updated. * * @param browserWindowIndexThatIssuedEvent * Index of browser window that issued event. * @param yokingGroup * Yoking group that is selected. */ EventUpdateYokedWindows::EventUpdateYokedWindows(const int32_t browserWindowIndexThatIssuedEvent, const YokingGroupEnum::Enum yokingGroup) : Event(EventTypeEnum::EVENT_UPDATE_YOKED_WINDOWS), m_browserWindowIndexThatIssuedEvent(browserWindowIndexThatIssuedEvent), m_yokingGroup(yokingGroup) { } /** * Constructor for updating all windows. * * @param browserWindowIndexThatIssuedEvent * Index of browser window that issued event. * @param yokingGroup * Yoking group that is selected. */ EventUpdateYokedWindows::EventUpdateYokedWindows(const YokingGroupEnum::Enum yokingGroup) : Event(EventTypeEnum::EVENT_UPDATE_YOKED_WINDOWS), m_browserWindowIndexThatIssuedEvent(-1), m_yokingGroup(yokingGroup) { } /** * Destructor. */ EventUpdateYokedWindows::~EventUpdateYokedWindows() { } /** * @return Index of browser window that issued the event. */ int32_t EventUpdateYokedWindows::getBrowserWindowIndexThatIssuedEvent() const { return m_browserWindowIndexThatIssuedEvent; } /** * @return The yoking group that should be updated. */ YokingGroupEnum::Enum EventUpdateYokedWindows::getYokingGroup() const { return m_yokingGroup; } connectome-workbench-1.2.3+git41-gc4c6c90/src/GuiQt/EventUpdateYokedWindows.h000066400000000000000000000040731300200146000265450ustar00rootroot00000000000000#ifndef __EVENT_UPDATE_YOKED_WINDOWS_H__ #define __EVENT_UPDATE_YOKED_WINDOWS_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" #include "YokingGroupEnum.h" namespace caret { class EventUpdateYokedWindows : public Event { public: EventUpdateYokedWindows(const int32_t browserWindowIndexThatIssuedEvent, const YokingGroupEnum::Enum yokingGroup); EventUpdateYokedWindows(const YokingGroupEnum::Enum yokingGroup); virtual ~EventUpdateYokedWindows(); int32_t getBrowserWindowIndexThatIssuedEvent() const; YokingGroupEnum::Enum getYokingGroup() const; private: EventUpdateYokedWindows(const EventUpdateYokedWindows&); EventUpdateYokedWindows& operator=(const EventUpdateYokedWindows&); public: // ADD_NEW_METHODS_HERE private: const int32_t m_browserWindowIndexThatIssuedEvent; const YokingGroupEnum::Enum m_yokingGroup; // ADD_NEW_MEMBERS_HERE }; #ifdef __EVENT_UPDATE_YOKED_WINDOWS_DECLARE__ // #endif // __EVENT_UPDATE_YOKED_WINDOWS_DECLARE__ } // namespace #endif //__EVENT_UPDATE_YOKED_WINDOWS_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/GuiQt/EventUserInterfaceUpdate.cxx000066400000000000000000000153061300200146000272320ustar00rootroot00000000000000/*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 "EventUserInterfaceUpdate.h" using namespace caret; /** * \class caret::EventUserInterfaceUpdate * \brief Event for updating the user-interface. * \ingroup GuiQt * * After the user executes an action, there is often the * need to update the user-interface. Options are available * to limit the update for specific types of data. However, * some receivers will update regardless of any specific * update type requests. In addition, the update can * be targeted to a specific window. */ /** * Constructor. By default, everything is updated. * The 'add...()' methods can be used to limit the * types of data that get updated. */ EventUserInterfaceUpdate::EventUserInterfaceUpdate() : Event(EventTypeEnum::EVENT_USER_INTERFACE_UPDATE) { m_windowIndex = -1; this->setAll(true); } /* * Destructor. */ EventUserInterfaceUpdate::~EventUserInterfaceUpdate() { } /** * @return Is the update for the given window? * * @param windowIndex * Index of window. */ bool EventUserInterfaceUpdate::isUpdateForWindow(const int32_t windowIndex) const { if (m_windowIndex < 0) { return true; } else if (m_windowIndex == windowIndex) { return true; } return false; } /** * Set the update so that it only updates a specific window. * * @return A reference to the instance so that the * request update calls can be chained. */ EventUserInterfaceUpdate& EventUserInterfaceUpdate::setWindowIndex(const int32_t windowIndex) { m_windowIndex = windowIndex; return *this; } /** * Set the status of all update types. * @param new selection status. */ void EventUserInterfaceUpdate::setAll(bool selected) { m_borderUpdate = selected; m_connectivityUpdate = selected; m_fociUpdate = selected; m_surfaceUpdate = selected; m_tabUpdate = selected; m_toolBarUpdate = selected; m_toolBoxUpdate = selected; } /** * Add borders for update (borders have changed). * * Note the first call to an 'add...()' method will * set all other updates to off. * * @return A reference to the instance so that the * request update calls can be chained. */ EventUserInterfaceUpdate& EventUserInterfaceUpdate::addBorder() { addInitialization(); m_borderUpdate = true; return *this; } /** * Add connectivity for update (connectivity has changed). * * Note the first call to an 'add...()' method will * set all other updates to off. * * @return A reference to the instance so that the * request update calls can be chained. */ EventUserInterfaceUpdate& EventUserInterfaceUpdate::addConnectivity() { addInitialization(); m_connectivityUpdate = true; return *this; } /** * Add foci for update (foci have changed). * * Note the first call to an 'add...()' method will * set all other updates to off. * * @return A reference to the instance so that the * request update calls can be chained. */ EventUserInterfaceUpdate& EventUserInterfaceUpdate::addFoci() { addInitialization(); m_fociUpdate = true; return *this; } /** * Add surface for update (surfaces have changed). * * Note the first call to an 'add...()' method will * set all other updates to off. * * @return A reference to the instance so that the * request update calls can be chained. */ EventUserInterfaceUpdate& EventUserInterfaceUpdate::addSurface() { addInitialization(); m_surfaceUpdate = true; return *this; } /** * Add browser tabs for update (browser tabs have changed). * * Note the first call to an 'add...()' method will * set all other updates to off. * * @return A reference to the instance so that the * request update calls can be chained. */ EventUserInterfaceUpdate& EventUserInterfaceUpdate::addTab() { addInitialization(); m_tabUpdate = true; return *this; } /** * Add toolbar for update (toolbar data has changed). * * Note the first call to an 'add...()' method will * set all other updates to off. * * @return A reference to the instance so that the * request update calls can be chained. */ EventUserInterfaceUpdate& EventUserInterfaceUpdate::addToolBar() { addInitialization(); m_toolBarUpdate = true; return *this; } /** * Add toolbox for update (toolbox data has changed). * * Note the first call to an 'add...()' method will * set all other updates to off. * * @return A reference to the instance so that the * request update calls can be chained. */ EventUserInterfaceUpdate& EventUserInterfaceUpdate::addToolBox() { addInitialization(); m_toolBoxUpdate = true; return *this; } /** * This is called by any of the 'add...() methods * and, if this is the first time called after * creation of an instance, it will turn off all * update types so that the 'add...()' only has * to turn on its update type. */ void EventUserInterfaceUpdate::addInitialization() { if (m_isFirstUpdateType) { setAll(false); m_isFirstUpdateType = false; } } /** * @return Is border update requested. */ bool EventUserInterfaceUpdate::isBorderUpdate() const { return m_borderUpdate; } /** * @return Is connectivity update requested. */ bool EventUserInterfaceUpdate::isConnectivityUpdate() const { return m_connectivityUpdate; } /** * @return Is foci update requested. */ bool EventUserInterfaceUpdate::isFociUpdate() const { return m_fociUpdate; } /** * @return Is surface update requested. */ bool EventUserInterfaceUpdate::isSurfaceUpdate() const { return m_surfaceUpdate; } /** * @return Is tab update requested (browser tabs have changed) */ bool EventUserInterfaceUpdate::isTabUpdate() const { return m_tabUpdate; } /** * @return Is toolbar update requested. */ bool EventUserInterfaceUpdate::isToolBarUpdate() const { return m_toolBarUpdate; } /** * @return Is toolbox update requested. */ bool EventUserInterfaceUpdate::isToolBoxUpdate() const { return m_toolBoxUpdate; } connectome-workbench-1.2.3+git41-gc4c6c90/src/GuiQt/EventUserInterfaceUpdate.h000066400000000000000000000052531300200146000266570ustar00rootroot00000000000000#ifndef __EVENT_USER_INTERFACE_UPDATE_H__ #define __EVENT_USER_INTERFACE_UPDATE_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 { /// Event for updating the user-interface class EventUserInterfaceUpdate : public Event { public: EventUserInterfaceUpdate(); virtual ~EventUserInterfaceUpdate(); EventUserInterfaceUpdate& addBorder(); EventUserInterfaceUpdate& addConnectivity(); EventUserInterfaceUpdate& addFoci(); EventUserInterfaceUpdate& addSurface(); EventUserInterfaceUpdate& addTab(); EventUserInterfaceUpdate& addToolBar(); EventUserInterfaceUpdate& addToolBox(); bool isBorderUpdate() const; bool isConnectivityUpdate() const; bool isFociUpdate() const; bool isSurfaceUpdate() const; bool isTabUpdate() const; bool isToolBarUpdate() const; bool isToolBoxUpdate() const; bool isUpdateForWindow(const int32_t windowIndex) const; EventUserInterfaceUpdate& setWindowIndex(const int32_t windowIndex); private: EventUserInterfaceUpdate(const EventUserInterfaceUpdate&); EventUserInterfaceUpdate& operator=(const EventUserInterfaceUpdate&); void setAll(bool selected); void addInitialization(); bool m_borderUpdate; bool m_connectivityUpdate; bool m_fociUpdate; bool m_surfaceUpdate; bool m_tabUpdate; bool m_toolBarUpdate; bool m_toolBoxUpdate; bool m_isFirstUpdateType; int32_t m_windowIndex; }; } // namespace #endif // __EVENT_USER_INTERFACE_UPDATE_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/GuiQt/FiberOrientationSelectionViewController.cxx000066400000000000000000000603351300200146000323400ustar00rootroot00000000000000 /*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 __FIBER_ORIENTATION_SELECTION_VIEW_CONTROLLER_DECLARE__ #include "FiberOrientationSelectionViewController.h" #undef __FIBER_ORIENTATION_SELECTION_VIEW_CONTROLLER_DECLARE__ #include #include #include #include #include #include #include #include "Brain.h" #include "BrowserTabContent.h" #include "DisplayGroupEnumComboBox.h" #include "DisplayPropertiesFiberOrientation.h" #include "EventManager.h" #include "EventGraphicsUpdateAllWindows.h" #include "EventUserInterfaceUpdate.h" #include "CiftiFiberOrientationFile.h" #include "FiberOrientationColoringTypeEnum.h" #include "FiberOrientationSelectionViewController.h" #include "FiberSamplesOpenGLWidget.h" #include "GuiManager.h" #include "SceneClass.h" #include "WuQFactory.h" #include "WuQTabWidget.h" #include "WuQtUtilities.h" using namespace caret; /** * \class caret::FiberOrientationSelectionViewController * \brief View/Controller for fiber orientations * \ingroup GuiQt * */ /** * Constructor. */ FiberOrientationSelectionViewController::FiberOrientationSelectionViewController(const int32_t browserWindowIndex, QWidget* parent) : QWidget(parent), m_browserWindowIndex(browserWindowIndex) { QLabel* groupLabel = new QLabel("Group"); m_displayGroupComboBox = new DisplayGroupEnumComboBox(this); QObject::connect(m_displayGroupComboBox, SIGNAL(displayGroupSelected(const DisplayGroupEnum::Enum)), this, SLOT(displayGroupSelected(const DisplayGroupEnum::Enum))); QHBoxLayout* groupLayout = new QHBoxLayout(); WuQtUtilities::setLayoutSpacingAndMargins(groupLayout, 2, 2); groupLayout->addWidget(groupLabel); groupLayout->addWidget(m_displayGroupComboBox->getWidget()); groupLayout->addStretch(); QWidget* attributesWidget = this->createAttributesWidget(); QWidget* selectionWidget = this->createSelectionWidget(); QWidget* samplesWidget = this->createSamplesWidget(); m_displayFibersCheckBox = new QCheckBox("Display Fibers"); m_displayFibersCheckBox->setToolTip("Display Fibers"); QObject::connect(m_displayFibersCheckBox, SIGNAL(clicked(bool)), this, SLOT(processSelectionChanges())); m_tabWidget = new WuQTabWidget(WuQTabWidget::TAB_ALIGN_LEFT, this); m_tabWidget->addTab(attributesWidget, "Attributes"); m_tabWidget->addTab(selectionWidget, "Selection"); m_tabWidget->addTab(samplesWidget, "Samples"); m_tabWidget->setCurrentWidget(attributesWidget); QVBoxLayout* layout = new QVBoxLayout(this); WuQtUtilities::setLayoutSpacingAndMargins(layout, 2, 2); layout->addLayout(groupLayout); layout->addSpacing(10); layout->addWidget(m_displayFibersCheckBox); layout->addWidget(m_tabWidget->getWidget(), 0, Qt::AlignLeft); layout->addStretch(); EventManager::get()->addEventListener(this, EventTypeEnum::EVENT_USER_INTERFACE_UPDATE); s_allViewControllers.insert(this); } /** * Destructor. */ FiberOrientationSelectionViewController::~FiberOrientationSelectionViewController() { EventManager::get()->removeAllEventsFromListener(this); s_allViewControllers.erase(this); } /** * @return The selection widget. */ QWidget* FiberOrientationSelectionViewController::createSelectionWidget() { m_selectionWidgetLayout = new QVBoxLayout(); QWidget* widget = new QWidget(); QVBoxLayout* layout = new QVBoxLayout(widget); layout->addLayout(m_selectionWidgetLayout); layout->addStretch(); return widget; } /** * @return The attributes widget. */ QWidget* FiberOrientationSelectionViewController::createAttributesWidget() { m_updateInProgress = true; QLabel* aboveLimitLabel = new QLabel("Slice Above Limit"); m_aboveLimitSpinBox = WuQFactory::newDoubleSpinBox(); m_aboveLimitSpinBox->setRange(0.0, std::numeric_limits::max()); m_aboveLimitSpinBox->setDecimals(3); m_aboveLimitSpinBox->setSingleStep(0.1); m_aboveLimitSpinBox->setToolTip("Fibers within this distance above the volume slice will be displayed"); QObject::connect(m_aboveLimitSpinBox, SIGNAL(valueChanged(double)), this, SLOT(processAttributesChanges())); QLabel* belowLimitLabel = new QLabel("Slice Below Limit"); m_belowLimitSpinBox = WuQFactory::newDoubleSpinBox(); m_belowLimitSpinBox->setRange(-std::numeric_limits::max(), 0.0); m_belowLimitSpinBox->setDecimals(3); m_belowLimitSpinBox->setSingleStep(0.1); m_belowLimitSpinBox->setToolTip("Fibers within this distance below the volume slice will be displayed"); QObject::connect(m_belowLimitSpinBox, SIGNAL(valueChanged(double)), this, SLOT(processAttributesChanges())); QLabel* minimumMagnitudeLabel = new QLabel("Minimum Magnitude"); m_minimumMagnitudeSpinBox = WuQFactory::newDoubleSpinBox(); m_minimumMagnitudeSpinBox->setRange(0.0, std::numeric_limits::max()); m_minimumMagnitudeSpinBox->setDecimals(2); m_minimumMagnitudeSpinBox->setSingleStep(0.05); m_minimumMagnitudeSpinBox->setToolTip("Minimum magnitude for displaying fibers"); QObject::connect(m_minimumMagnitudeSpinBox, SIGNAL(valueChanged(double)), this, SLOT(processAttributesChanges())); QLabel* lengthMultiplierLabel = new QLabel("Length Multiplier"); m_lengthMultiplierSpinBox = WuQFactory::newDoubleSpinBox(); m_lengthMultiplierSpinBox->setRange(0.0, std::numeric_limits::max()); m_lengthMultiplierSpinBox->setDecimals(2); m_lengthMultiplierSpinBox->setSingleStep(1.0); m_lengthMultiplierSpinBox->setToolTip("Fiber lengths are scaled by this value"); QObject::connect(m_lengthMultiplierSpinBox, SIGNAL(valueChanged(double)), this, SLOT(processAttributesChanges())); QLabel* fanMultiplierLabel = new QLabel("Fan Multiplier"); m_fanMultiplierSpinBox = WuQFactory::newDoubleSpinBox(); m_fanMultiplierSpinBox->setRange(0.0, std::numeric_limits::max()); m_fanMultiplierSpinBox->setDecimals(2); m_fanMultiplierSpinBox->setSingleStep(0.05); m_fanMultiplierSpinBox->setToolTip("Fan angles are scaled by this value"); QObject::connect(m_fanMultiplierSpinBox, SIGNAL(valueChanged(double)), this, SLOT(processAttributesChanges())); m_drawWithMagnitudeCheckBox = new QCheckBox("Draw With Magnitude"); m_drawWithMagnitudeCheckBox->setToolTip("When drawing fibers, the magnitude is reflected in the length of the fiber"); QObject::connect(m_drawWithMagnitudeCheckBox, SIGNAL(clicked(bool)), this, SLOT(processAttributesChanges())); QLabel* coloringTypeLabel = new QLabel("Coloring"); m_coloringTypeComboBox = new EnumComboBoxTemplate(this); m_coloringTypeComboBox->getWidget()->setToolTip("Selects method for assigning the red, green, and blue\n" "color components when drawing fibers"); QObject::connect(m_coloringTypeComboBox, SIGNAL(itemActivated()), this, SLOT(processAttributesChanges())); m_coloringTypeComboBox->setup(); QLabel* symbolTypeLabel = new QLabel("Symbol"); m_symbolTypeComboBox = new EnumComboBoxTemplate(this); m_symbolTypeComboBox->getWidget()->setToolTip("Selects type of symbol for drawing fibers"); QObject::connect(m_symbolTypeComboBox, SIGNAL(itemActivated()), this, SLOT(processAttributesChanges())); m_symbolTypeComboBox->setup(); QWidget* gridWidget = new QWidget(); QGridLayout* gridLayout = new QGridLayout(gridWidget); gridLayout->setColumnStretch(0, 0); gridLayout->setColumnStretch(1, 100); WuQtUtilities::setLayoutSpacingAndMargins(gridLayout, 8, 2); int row = gridLayout->rowCount(); gridLayout->addWidget(coloringTypeLabel, row, 0); gridLayout->addWidget(m_coloringTypeComboBox->getWidget() , row, 1); row++; gridLayout->addWidget(symbolTypeLabel, row, 0); gridLayout->addWidget(m_symbolTypeComboBox->getWidget() , row, 1); row++; gridLayout->addWidget(WuQtUtilities::createHorizontalLineWidget(), row, 0, 1, 2); row++; gridLayout->addWidget(m_drawWithMagnitudeCheckBox, row, 0, 1, 2, Qt::AlignLeft); row++; gridLayout->addWidget(minimumMagnitudeLabel, row, 0); gridLayout->addWidget(m_minimumMagnitudeSpinBox , row, 1); row++; gridLayout->addWidget(lengthMultiplierLabel, row, 0); gridLayout->addWidget(m_lengthMultiplierSpinBox , row, 1); row++; gridLayout->addWidget(fanMultiplierLabel, row, 0); gridLayout->addWidget(m_fanMultiplierSpinBox , row, 1); row++; gridLayout->addWidget(WuQtUtilities::createHorizontalLineWidget(), row, 0, 1, 2); row++; gridLayout->addWidget(aboveLimitLabel, row, 0); gridLayout->addWidget(m_aboveLimitSpinBox, row, 1); row++; gridLayout->addWidget(belowLimitLabel, row, 0); gridLayout->addWidget(m_belowLimitSpinBox, row, 1); row++; gridWidget->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); QWidget* widget = new QWidget(); QVBoxLayout* layout = new QVBoxLayout(widget); WuQtUtilities::setLayoutSpacingAndMargins(layout, 2, 2); layout->addWidget(gridWidget); layout->addStretch(); m_updateInProgress = false; return widget; } /** * Called when a widget on the attributes page has * its value changed. */ void FiberOrientationSelectionViewController::processAttributesChanges() { if (m_updateInProgress) { return; } DisplayPropertiesFiberOrientation* dpfo = GuiManager::get()->getBrain()->getDisplayPropertiesFiberOrientation(); BrowserTabContent* browserTabContent = GuiManager::get()->getBrowserTabContentForBrowserWindow(m_browserWindowIndex, true); if (browserTabContent == NULL) { return; } const int32_t browserTabIndex = browserTabContent->getTabNumber(); const DisplayGroupEnum::Enum displayGroup = dpfo->getDisplayGroupForTab(browserTabIndex); dpfo->setAboveLimit(displayGroup, browserTabIndex, m_aboveLimitSpinBox->value()); dpfo->setBelowLimit(displayGroup, browserTabIndex, m_belowLimitSpinBox->value()); dpfo->setMinimumMagnitude(displayGroup, browserTabIndex, m_minimumMagnitudeSpinBox->value()); dpfo->setLengthMultiplier(displayGroup, browserTabIndex, m_lengthMultiplierSpinBox->value()); dpfo->setFanMultiplier(displayGroup, browserTabIndex, m_fanMultiplierSpinBox->value()); dpfo->setDrawWithMagnitude(displayGroup, browserTabIndex, m_drawWithMagnitudeCheckBox->isChecked()); const FiberOrientationColoringTypeEnum::Enum coloringType = m_coloringTypeComboBox->getSelectedItem(); dpfo->setColoringType(displayGroup, browserTabIndex, coloringType); const FiberOrientationSymbolTypeEnum::Enum symbolType = m_symbolTypeComboBox->getSelectedItem(); dpfo->setSymbolType(displayGroup, browserTabIndex, symbolType); dpfo->setSphereOrientationsDisplayed(displayGroup, browserTabIndex, m_displaySphereOrientationsCheckBox->isChecked()); EventManager::get()->sendEvent(EventGraphicsUpdateAllWindows().getPointer()); updateOtherViewControllers(); } /** * Called when the fiber orientation display group combo box is changed. */ void FiberOrientationSelectionViewController::displayGroupSelected(const DisplayGroupEnum::Enum displayGroup) { if (m_updateInProgress) { return; } /* * Update selected display group in model. */ BrowserTabContent* browserTabContent = GuiManager::get()->getBrowserTabContentForBrowserWindow(m_browserWindowIndex, false); if (browserTabContent == NULL) { return; } const int32_t browserTabIndex = browserTabContent->getTabNumber(); Brain* brain = GuiManager::get()->getBrain(); DisplayPropertiesFiberOrientation* dpfo = brain->getDisplayPropertiesFiberOrientation(); dpfo->setDisplayGroupForTab(browserTabIndex, displayGroup); /* * Since display group has changed, need to update controls */ updateViewController(); /* * Apply the changes. */ processSelectionChanges(); } /** * Update the fiber orientation widget. */ void FiberOrientationSelectionViewController::updateViewController() { m_updateInProgress = true; BrowserTabContent* browserTabContent = GuiManager::get()->getBrowserTabContentForBrowserWindow(m_browserWindowIndex, true); if (browserTabContent == NULL) { return; } const int32_t browserTabIndex = browserTabContent->getTabNumber(); Brain* brain = GuiManager::get()->getBrain(); DisplayPropertiesFiberOrientation* dpfo = brain->getDisplayPropertiesFiberOrientation(); const DisplayGroupEnum::Enum displayGroup = dpfo->getDisplayGroupForTab(browserTabIndex); m_displayGroupComboBox->setSelectedDisplayGroup(displayGroup); //dpfo->isDisplayed(displayGroup, browserTabIndex) /* * Update file selection checkboxes */ const int32_t numberOfFileCheckBoxes = static_cast(m_fileSelectionCheckBoxes.size()); const int32_t numberOfFiberOrientFiles = brain->getNumberOfConnectivityFiberOrientationFiles(); for (int32_t iff = 0; iff < numberOfFiberOrientFiles; iff++) { CiftiFiberOrientationFile* cfof = brain->getConnectivityFiberOrientationFile(iff); QCheckBox* cb = NULL; if (iff < numberOfFileCheckBoxes) { cb = m_fileSelectionCheckBoxes[iff]; } else { cb = new QCheckBox(""); QObject::connect(cb, SIGNAL(clicked(bool)), this, SLOT(processSelectionChanges())); m_fileSelectionCheckBoxes.push_back(cb); m_selectionWidgetLayout->addWidget(cb); } cb->setText(cfof->getFileNameNoPath()); cb->setChecked(cfof->isDisplayed(displayGroup, browserTabIndex)); cb->setVisible(true); } /* * Hide unused file selection checkboxes */ for (int32_t iff = numberOfFiberOrientFiles; iff < numberOfFileCheckBoxes; iff++) { m_fileSelectionCheckBoxes[iff]->setVisible(false); } /* * Update the attributes */ m_displayFibersCheckBox->setChecked(dpfo->isDisplayed(displayGroup, browserTabIndex)); m_aboveLimitSpinBox->setValue(dpfo->getAboveLimit(displayGroup, browserTabIndex)); m_belowLimitSpinBox->setValue(dpfo->getBelowLimit(displayGroup, browserTabIndex)); m_lengthMultiplierSpinBox->setValue(dpfo->getLengthMultiplier(displayGroup, browserTabIndex)); m_fanMultiplierSpinBox->setValue(dpfo->getFanMultiplier(displayGroup, browserTabIndex)); m_minimumMagnitudeSpinBox->setValue(dpfo->getMinimumMagnitude(displayGroup, browserTabIndex)); m_drawWithMagnitudeCheckBox->setChecked(dpfo->isDrawWithMagnitude(displayGroup, browserTabIndex)); m_coloringTypeComboBox->setSelectedItem(dpfo->getColoringType(displayGroup, browserTabIndex)); m_symbolTypeComboBox->setSelectedItem(dpfo->getSymbolType(displayGroup, browserTabIndex)); m_displaySphereOrientationsCheckBox->setChecked(dpfo->isSphereOrientationsDisplayed(displayGroup, browserTabIndex)); /* * Update the samples */ m_samplesOpenGLWidget->update(); m_updateInProgress = false; } /** * Issue update events after selections are changed. */ void FiberOrientationSelectionViewController::processSelectionChanges() { if (m_updateInProgress) { return; } BrowserTabContent* browserTabContent = GuiManager::get()->getBrowserTabContentForBrowserWindow(m_browserWindowIndex, true); if (browserTabContent == NULL) { return; } Brain* brain = GuiManager::get()->getBrain(); const int32_t browserTabIndex = browserTabContent->getTabNumber(); DisplayPropertiesFiberOrientation* dpfo = brain->getDisplayPropertiesFiberOrientation(); const DisplayGroupEnum::Enum displayGroup = dpfo->getDisplayGroupForTab(browserTabIndex); dpfo->setDisplayed(displayGroup, browserTabIndex, m_displayFibersCheckBox->isChecked()); const int32_t numberOfFiberOrientFiles = brain->getNumberOfConnectivityFiberOrientationFiles(); CaretAssert(numberOfFiberOrientFiles <= static_cast(m_fileSelectionCheckBoxes.size())); for (int32_t iff = 0; iff < numberOfFiberOrientFiles; iff++) { CiftiFiberOrientationFile* cfof = brain->getConnectivityFiberOrientationFile(iff); cfof->setDisplayed(displayGroup, browserTabIndex, m_fileSelectionCheckBoxes[iff]->isChecked()); } updateOtherViewControllers(); EventManager::get()->sendEvent(EventGraphicsUpdateAllWindows().getPointer()); } /** * @return Create and return the spherical samples widget. */ QWidget* FiberOrientationSelectionViewController::createSamplesWidget() { m_displaySphereOrientationsCheckBox = new QCheckBox("Show Orientations"); QObject::connect(m_displaySphereOrientationsCheckBox, SIGNAL(clicked(bool)), this, SLOT(processAttributesChanges())); QGridLayout* fiberLabelsGridLayout = new QGridLayout(); fiberLabelsGridLayout->setColumnStretch(0, 0); fiberLabelsGridLayout->setColumnStretch(1, 50); fiberLabelsGridLayout->setColumnStretch(2, 50); int row = 0; fiberLabelsGridLayout->addWidget(new QLabel("Fiber "), row, 0); fiberLabelsGridLayout->addWidget(new QLabel("Mean "), row, 1); fiberLabelsGridLayout->addWidget(new QLabel("Variance"), row, 2); row++; QLabel* fiberMeanLabels[3]; QLabel* fiberVarianceLabels[3]; for (int32_t i = 0; i < 3; i++) { fiberMeanLabels[i] = new QLabel(""); fiberVarianceLabels[i] = new QLabel(""); fiberLabelsGridLayout->addWidget(new QLabel(QString::number(i+1)), row, 0); fiberLabelsGridLayout->addWidget(fiberMeanLabels[i], row, 1); fiberLabelsGridLayout->addWidget(fiberVarianceLabels[i], row, 2); row++; } m_samplesOpenGLWidget = new FiberSamplesOpenGLWidget(this->m_browserWindowIndex, m_displaySphereOrientationsCheckBox, fiberMeanLabels, fiberVarianceLabels); m_samplesOpenGLWidget->setMinimumSize(200, 200); QWidget* widget = new QWidget(); QVBoxLayout* layout = new QVBoxLayout(widget); WuQtUtilities::setLayoutSpacingAndMargins(layout, 2, 2); layout->addWidget(m_displaySphereOrientationsCheckBox); layout->addWidget(m_samplesOpenGLWidget, 100); // stretch factor layout->addLayout(fiberLabelsGridLayout); // layout->addStretch(); return widget; } /** * Update other fiber orientation view controllers. */ void FiberOrientationSelectionViewController::updateOtherViewControllers() { for (std::set::iterator iter = s_allViewControllers.begin(); iter != s_allViewControllers.end(); iter++) { FiberOrientationSelectionViewController* fosvc = *iter; if (fosvc != this) { fosvc->updateViewController(); } } } /** * Receive events from the event manager. * * @param event * Event sent by event manager. */ void FiberOrientationSelectionViewController::receiveEvent(Event* event) { bool doUpdate = false; if (event->getEventType() == EventTypeEnum::EVENT_USER_INTERFACE_UPDATE) { EventUserInterfaceUpdate* uiEvent = dynamic_cast(event); CaretAssert(uiEvent); if (uiEvent->isUpdateForWindow(m_browserWindowIndex)) { if (uiEvent->isToolBoxUpdate()) { doUpdate = true; uiEvent->setEventProcessed(); } } } if (doUpdate) { updateViewController(); } } /** * Create a scene for an instance of a class. * * @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. * * @return Pointer to SceneClass object representing the state of * this object. Under some circumstances a NULL pointer may be * returned. Caller will take ownership of returned object. */ SceneClass* FiberOrientationSelectionViewController::saveToScene(const SceneAttributes* sceneAttributes, const AString& instanceName) { SceneClass* sceneClass = new SceneClass(instanceName, "FiberOrientationSelectionViewController", 1); sceneClass->addClass(m_tabWidget->saveToScene(sceneAttributes, "m_tabWidget")); return sceneClass; } /** * Restore the state of an instance of a class. * * @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 containing the state that was previously * saved and should be restored. */ void FiberOrientationSelectionViewController::restoreFromScene(const SceneAttributes* sceneAttributes, const SceneClass* sceneClass) { if (sceneClass == NULL) { return; } m_tabWidget->restoreFromScene(sceneAttributes, sceneClass->getClass("m_tabWidget")); } connectome-workbench-1.2.3+git41-gc4c6c90/src/GuiQt/FiberOrientationSelectionViewController.h000066400000000000000000000102261300200146000317570ustar00rootroot00000000000000#ifndef __FIBER_ORIENTATION_SELECTION_VIEW_CONTROLLER__H_ #define __FIBER_ORIENTATION_SELECTION_VIEW_CONTROLLER__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 "DisplayGroupEnum.h" #include "EnumComboBoxTemplate.h" #include "EventListenerInterface.h" #include "SceneableInterface.h" class QCheckBox; class QDoubleSpinBox; class QVBoxLayout; namespace caret { class DisplayGroupEnumComboBox; class FiberSamplesOpenGLWidget; class WuQTabWidget; class WuQTrueFalseComboBox; class FiberOrientationSelectionViewController : public QWidget, public EventListenerInterface, public SceneableInterface { Q_OBJECT public: FiberOrientationSelectionViewController(const int32_t browserWindowIndex, QWidget* parent = 0); virtual ~FiberOrientationSelectionViewController(); void receiveEvent(Event* event); virtual SceneClass* saveToScene(const SceneAttributes* sceneAttributes, const AString& instanceName); virtual void restoreFromScene(const SceneAttributes* sceneAttributes, const SceneClass* sceneClass); // ADD_NEW_METHODS_HERE private slots: void processSelectionChanges(); void displayGroupSelected(const DisplayGroupEnum::Enum); void processAttributesChanges(); private: FiberOrientationSelectionViewController(const FiberOrientationSelectionViewController&); FiberOrientationSelectionViewController& operator=(const FiberOrientationSelectionViewController&); void updateViewController(); void updateOtherViewControllers(); QWidget* createSelectionWidget(); QWidget* createAttributesWidget(); QWidget* createSamplesWidget(); const int32_t m_browserWindowIndex; DisplayGroupEnumComboBox* m_displayGroupComboBox; QCheckBox* m_displayFibersCheckBox; std::vector m_fileSelectionCheckBoxes; QDoubleSpinBox* m_aboveLimitSpinBox; QDoubleSpinBox* m_belowLimitSpinBox; QDoubleSpinBox* m_minimumMagnitudeSpinBox; QDoubleSpinBox* m_lengthMultiplierSpinBox; QDoubleSpinBox* m_fanMultiplierSpinBox; EnumComboBoxTemplate* m_coloringTypeComboBox; EnumComboBoxTemplate* m_symbolTypeComboBox; QCheckBox* m_drawWithMagnitudeCheckBox; QVBoxLayout* m_selectionWidgetLayout; FiberSamplesOpenGLWidget* m_samplesOpenGLWidget; QCheckBox* m_displaySphereOrientationsCheckBox; WuQTabWidget* m_tabWidget; bool m_updateInProgress; // ADD_NEW_MEMBERS_HERE static std::set s_allViewControllers; }; #ifdef __FIBER_ORIENTATION_SELECTION_VIEW_CONTROLLER_DECLARE__ std::set FiberOrientationSelectionViewController::s_allViewControllers; #endif // __FIBER_ORIENTATION_SELECTION_VIEW_CONTROLLER_DECLARE__ } // namespace #endif //__FIBER_ORIENTATION_SELECTION_VIEW_CONTROLLER__H_ connectome-workbench-1.2.3+git41-gc4c6c90/src/GuiQt/FiberSamplesOpenGLWidget.cxx000066400000000000000000000516111300200146000271120ustar00rootroot00000000000000 /*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 #define __FIBER_SAMPLES_OPEN_G_L_WIDGET_DECLARE__ #include "FiberSamplesOpenGLWidget.h" #undef __FIBER_SAMPLES_OPEN_G_L_WIDGET_DECLARE__ using namespace caret; #include "Brain.h" #include "BrainOpenGL.h" #include "BrainOpenGLShapeRing.h" #include "BrainOpenGLShapeSphere.h" #include "DisplayPropertiesFiberOrientation.h" #include "Fiber.h" #include "FiberOrientation.h" #include "GuiManager.h" #include "WuQMessageBox.h" /** * \class caret::FiberSamplesOpenGLWidget * \brief OpenGL Widget for drawing fiber samples on a sphere * \defgroup GuiQt * \ingroup GuiQt */ /** * Constructor. */ FiberSamplesOpenGLWidget::FiberSamplesOpenGLWidget(const int32_t browserWindowIndex, QCheckBox* enabledCheckBox, QLabel* fiberMeanLabels[3], QLabel* fiberVarianceLabels[3], QWidget* parent) : QGLWidget(parent) { m_browserWindowIndex = browserWindowIndex; m_enabledCheckBox = enabledCheckBox; for (int32_t i = 0; i < 3; i++) { m_fiberMeanLabels[i] = fiberMeanLabels[i]; m_fiberVarianceLabels[i] = fiberVarianceLabels[i]; } m_ring = NULL; m_sphereBig = NULL; m_sphereSmall = NULL; setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); } /** * Destructor. */ FiberSamplesOpenGLWidget::~FiberSamplesOpenGLWidget() { makeCurrent(); if (m_ring != NULL) { delete m_ring; } if (m_sphereBig != NULL) { delete m_sphereBig; } if (m_sphereSmall != NULL) { delete m_sphereSmall; } } /** * Called once prior to resizeGL() and paintGL() to * make any necessary initializations. */ void FiberSamplesOpenGLWidget::initializeGL() { glEnable(GL_DEPTH_TEST); glDepthFunc(GL_LEQUAL); glClearDepth(1.0); glFrontFace(GL_CCW); glEnable(GL_NORMALIZE); // // Avoid drawing backfacing polygons // glCullFace(GL_BACK); glEnable(GL_CULL_FACE); glShadeModel(GL_SMOOTH); createShapes(); } /** * Setup lighting parameters. */ void FiberSamplesOpenGLWidget::setupLighting() { glLightModeli(GL_LIGHT_MODEL_TWO_SIDE, GL_FALSE); glLightModeli(GL_LIGHT_MODEL_LOCAL_VIEWER, GL_FALSE); float lightColor[] = { 0.9f, 0.9f, 0.9f, 1.0f }; glLightfv(GL_LIGHT0, GL_DIFFUSE, lightColor); glLightfv(GL_LIGHT1, GL_DIFFUSE, lightColor); float materialColor[] = { 0.8f, 0.8f, 0.8f, 1.0f }; glMaterialfv(GL_FRONT, GL_DIFFUSE, materialColor); glColorMaterial(GL_FRONT, GL_DIFFUSE); float ambient[] = { 0.8f, 0.8f, 0.8f, 1.0f }; glLightModelfv(GL_LIGHT_MODEL_AMBIENT, ambient); float lightPosition[] = { 0.0f, 0.0f, 100.0f, 0.0f }; glPushMatrix(); glLoadIdentity(); glLightfv(GL_LIGHT0, GL_POSITION, lightPosition); glEnable(GL_LIGHT0); // // Light 1 position is opposite of light 0 // lightPosition[0] = -lightPosition[0]; lightPosition[1] = -lightPosition[1]; lightPosition[2] = -lightPosition[2]; glLightfv(GL_LIGHT1, GL_POSITION, lightPosition); glEnable(GL_LIGHT1); glPopMatrix(); glEnable(GL_LIGHTING); glEnable(GL_COLOR_MATERIAL); glColorMaterial(GL_FRONT, GL_DIFFUSE); } /** * Gets called whenever the widget is resized. * @param width * New width of widget. * @param height * New height of widget. */ void FiberSamplesOpenGLWidget::resizeGL(int width, int height) { setOrthographicProjection(width, height); } /** * Set the orthographic projection. * @param width * New width. * @param height * New height. */ void FiberSamplesOpenGLWidget::setOrthographicProjection(const int width, const int height) { m_widgetWidth = width; m_widgetHeight = height; glViewport(0, 0, width, height); const double orthoSize = 125.0; const double aspectRatio = (static_cast(width) / static_cast(height)); double orthoWidth = orthoSize; double orthoHeight = orthoSize; if (aspectRatio > 1.0) { orthoWidth *= aspectRatio; } else { const float inverseAspectRatio = 1.0 / aspectRatio; orthoHeight *= inverseAspectRatio; } const double orthoNearFar = 5000.0; // allows zooming double orthographicRight = orthoWidth; double orthographicLeft = -orthoWidth; double orthographicTop = orthoHeight; double orthographicBottom = -orthoHeight; double orthographicNear = -orthoNearFar; double orthographicFar = orthoNearFar; glMatrixMode(GL_PROJECTION); glLoadIdentity(); glOrtho(orthographicLeft, orthographicRight, orthographicBottom, orthographicTop, orthographicNear, orthographicFar); glMatrixMode(GL_MODELVIEW); } /** * Called when the widget needs to be repainted. */ void FiberSamplesOpenGLWidget::paintGL() { setOrthographicProjection(width(), height()); glClearColor(0.0, 0.0, 0.0, 1.0); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); glEnable(GL_DEPTH_TEST); glMatrixMode(GL_MODELVIEW); setupLighting(); glPushMatrix(); glLoadIdentity(); double rotationMatrixElements[16]; m_rotationMatrix.getMatrixForOpenGL(rotationMatrixElements); glMultMatrixd(rotationMatrixElements); const GLfloat lineStart = -s_sphereBigRadius * 5.0; const GLfloat lineEnd = s_sphereBigRadius * 5.0; glDisable(GL_LIGHTING); glDisable(GL_COLOR_MATERIAL); /* * Axis lines */ glLineWidth(3.0); glBegin(GL_LINES); glColor3f(0.5, 0.0, 0.0); glVertex3f(lineStart, 0.0, 0.0); glVertex3f(0.0, 0.0, 0.0); glColor3f(1.0,0.0, 0.0); glVertex3f(0.0, 0.0, 0.0); glVertex3f(lineEnd, 0.0, 0.0); glColor3f(0.0, 0.5, 0.0); glVertex3f(0.0, lineStart, 0.0); glVertex3f(0.0, 0.0, 0.0); glColor3f(0.0, 1.0, 0.0); glVertex3f(0.0, 0.0, 0.0); glVertex3f(0.0, lineEnd, 0.0); glColor3f(0.0, 0.0, 0.5); glVertex3f(0.0, 0.0, lineStart); glVertex3f(0.0, 0.0, 0.0); glColor3f(0.0,0.0, 1.0); glVertex3f(0.0, 0.0, 0.0); glVertex3f(0.0, 0.0, lineEnd); glEnd(); glEnable(GL_LIGHTING); glEnable(GL_COLOR_MATERIAL); /* * Sphere */ // glColor3f(0.7, // 0.7, // 0.7); const float rgba[4] = { 0.7, 0.7, 0.7, 1.0 }; m_sphereBig->draw(rgba); glDisable(GL_LIGHTING); glDisable(GL_COLOR_MATERIAL); if (m_enabledCheckBox->isChecked()) { drawOrientations(); } glPopMatrix(); } /** * Draw the fibers on the sphere */ void FiberSamplesOpenGLWidget::drawOrientations() { glDisable(GL_CULL_FACE); DisplayPropertiesFiberOrientation* dpfo = GuiManager::get()->getBrain()->getDisplayPropertiesFiberOrientation(); std::vector xVectors; std::vector yVectors; std::vector zVectors; FiberOrientation* fiberOrientation; AString errorMessage; Brain* brain = GuiManager::get()->getBrain(); if (brain->getFiberOrientationSphericalSamplesVectors(xVectors, yVectors, zVectors, fiberOrientation, errorMessage)) { const DisplayGroupEnum::Enum displayGroup = dpfo->getDisplayGroupForTab(this->m_browserWindowIndex); const FiberOrientationColoringTypeEnum::Enum coloringType = dpfo->getColoringType(displayGroup, m_browserWindowIndex); const float minimumMagnitude = dpfo->getMinimumMagnitude(displayGroup, m_browserWindowIndex); /* * First orientations */ const int32_t numVectorsX = static_cast(xVectors.size()); for (int32_t i = 0; i < numVectorsX; i++) { if (xVectors[i].magnitude < minimumMagnitude) { continue; } float xyz[3] = { xVectors[i].direction[0] * s_sphereBigRadius, xVectors[i].direction[1] * s_sphereBigRadius, xVectors[i].direction[2] * s_sphereBigRadius }; float rgba[4] = { 0.0, 0.0, 0.0, 255.0 }; switch (coloringType) { case FiberOrientationColoringTypeEnum::FIBER_COLORING_FIBER_INDEX_AS_RGB: //glColor3f(1.0, 0.0, 0.0); rgba[0] = 1.0; break; case FiberOrientationColoringTypeEnum::FIBER_COLORING_XYZ_AS_RGB: //glColor3fv(xVectors[i].rgb); rgba[0] = xVectors[i].rgb[0]; rgba[1] = xVectors[i].rgb[1]; rgba[2] = xVectors[i].rgb[2]; break; } glPushMatrix(); glTranslatef(xyz[0], xyz[1], xyz[2]); m_sphereSmall->draw(rgba); glPopMatrix(); glPushMatrix(); glTranslatef(-xyz[0], -xyz[1], -xyz[2]); m_sphereSmall->draw(rgba); glPopMatrix(); } /* * Second orientations */ const int32_t numVectorsY = static_cast(yVectors.size()); for (int32_t i = 0; i < numVectorsY; i++) { if (yVectors[i].magnitude < minimumMagnitude) { continue; } float xyz[3] = { yVectors[i].direction[0] * s_sphereBigRadius, yVectors[i].direction[1] * s_sphereBigRadius, yVectors[i].direction[2] * s_sphereBigRadius }; float rgba[4] = { 0.0, 0.0, 0.0, 255.0 }; switch (coloringType) { case FiberOrientationColoringTypeEnum::FIBER_COLORING_FIBER_INDEX_AS_RGB: //glColor3f(0.0, 0.0, 1.0); // BLUE (RBG!) rgba[2] = 1.0; break; case FiberOrientationColoringTypeEnum::FIBER_COLORING_XYZ_AS_RGB: //glColor3fv(yVectors[i].rgb); rgba[0] = yVectors[i].rgb[0]; rgba[1] = yVectors[i].rgb[1]; rgba[2] = yVectors[i].rgb[2]; break; } glPushMatrix(); glTranslatef(xyz[0], xyz[1], xyz[2]); m_sphereSmall->draw(rgba); glPopMatrix(); glPushMatrix(); glTranslatef(-xyz[0], -xyz[1], -xyz[2]); m_sphereSmall->draw(rgba); glPopMatrix(); } /* * Third orientations */ const int32_t numVectorsZ = static_cast(zVectors.size()); for (int32_t i = 0; i < numVectorsZ; i++) { if (zVectors[i].magnitude < minimumMagnitude) { continue; } float xyz[3] = { zVectors[i].direction[0] * s_sphereBigRadius, zVectors[i].direction[1] * s_sphereBigRadius, zVectors[i].direction[2] * s_sphereBigRadius }; float rgba[4] = { 0.0, 0.0, 0.0, 255.0 }; switch (coloringType) { case FiberOrientationColoringTypeEnum::FIBER_COLORING_FIBER_INDEX_AS_RGB: //glColor3f(0.0, 1.0, 0.0); // GREEN (RBG!) rgba[1] = 1.0; break; case FiberOrientationColoringTypeEnum::FIBER_COLORING_XYZ_AS_RGB: //glColor3fv(zVectors[i].rgb); rgba[0] = zVectors[i].rgb[0]; rgba[1] = zVectors[i].rgb[1]; rgba[2] = zVectors[i].rgb[2]; break; } glPushMatrix(); glTranslatef(xyz[0], xyz[1], xyz[2]); m_sphereSmall->draw(rgba); glPopMatrix(); glPushMatrix(); glTranslatef(-xyz[0], -xyz[1], -xyz[2]); m_sphereSmall->draw(rgba); glPopMatrix(); } double viewingMatrixArray[16]; glGetDoublev(GL_MODELVIEW_MATRIX, viewingMatrixArray); Matrix4x4 viewingMatrix; viewingMatrix.setMatrixFromOpenGL(viewingMatrixArray); Matrix4x4 inverseViewingMatrix(viewingMatrix); inverseViewingMatrix.invert(); inverseViewingMatrix.setTranslation(0.0, 0.0, 0.0); double inverseViewingMatrixArray[16]; inverseViewingMatrix.getMatrixForOpenGL(inverseViewingMatrixArray); /* * Orientation Ellipse */ const float radiansToDegrees = 180.0 / M_PI; if (fiberOrientation != NULL) { const int32_t numFibers = fiberOrientation->m_numberOfFibers; for (int32_t j = 0; j < numFibers; j++) { const Fiber* fiber = fiberOrientation->m_fibers[j]; if (fiber->m_valid) { if (j < 3) { m_fiberMeanLabels[j]->setText(AString::number(fiber->m_meanF)); m_fiberVarianceLabels[j]->setText(AString::number(fiber->m_varF)); } /* * Only draw if magnitude exceeds minimum magnitude */ if (fiber->m_meanF < minimumMagnitude) { continue; } float rgba[4] = { 0.0, 0.0, 0.0, 1.0 }; switch (coloringType) { case FiberOrientationColoringTypeEnum::FIBER_COLORING_FIBER_INDEX_AS_RGB: { const int32_t colorIndex = j % 3; switch (colorIndex) { case 0: rgba[0] = 1.0; //glColor3f(1.0, 0.0, 0.0); break; case 1: rgba[2] = 1.0; //glColor3f(0.0, 0.0, 1.0); break; case 2: rgba[1] = 1.0; //glColor3f(0.0, 1.0, 0.0); break; } } break; case FiberOrientationColoringTypeEnum::FIBER_COLORING_XYZ_AS_RGB: //glColor3fv(fiber->m_directionUnitVectorRGB); rgba[0] = fiber->m_directionUnitVectorRGB[0]; rgba[1] = fiber->m_directionUnitVectorRGB[1]; rgba[2] = fiber->m_directionUnitVectorRGB[2]; break; } const float maxAngle = M_PI_2 * 0.95; float baseMajorAngle = fiber->m_fanningMajorAxisAngle; if (baseMajorAngle > maxAngle) { baseMajorAngle = maxAngle; } float baseMinorAngle = fiber->m_fanningMinorAxisAngle; if (baseMinorAngle > maxAngle) { baseMinorAngle = maxAngle; } const float z = s_sphereBigRadius; const float baseRadiusScaling = 1.0; const float maxWidth = z; const float majorAxis = std::min(z * std::tan(baseMajorAngle) * baseRadiusScaling, maxWidth); const float minorAxis = std::min(z * std::tan(baseMinorAngle) * baseRadiusScaling, maxWidth); glPushMatrix(); glRotatef(-fiber->m_phi * radiansToDegrees, 0.0, 0.0, 1.0); glRotatef(-fiber->m_theta * radiansToDegrees, 0.0, 1.0, 0.0); glRotatef(-fiber->m_psi * radiansToDegrees, 0.0, 0.0, 1.0); glPushMatrix(); glTranslatef(0.0, 0.0, s_sphereBigRadius); glScalef(majorAxis * 4.0, minorAxis * 4.0, 1.0); m_ring->draw(rgba); glPopMatrix(); glPushMatrix(); glTranslatef(0.0, 0.0, -s_sphereBigRadius); glScalef(majorAxis * 4.0, minorAxis * 4.0, 1.0); m_ring->draw(rgba); glPopMatrix(); glPopMatrix(); } } } } else { if (errorMessage.isEmpty() == false) { WuQMessageBox::errorOk(this, errorMessage); } } glEnable(GL_CULL_FACE); } /** * Create the shapes on which fibers are viewed. */ void FiberSamplesOpenGLWidget::createShapes() { if (m_sphereBig == NULL) { m_sphereBig = new BrainOpenGLShapeSphere(25, s_sphereBigRadius); } if (m_sphereSmall == NULL) { m_sphereSmall = new BrainOpenGLShapeSphere(5, s_sphereSmallRadius); } if (m_ring == NULL) { m_ring = new BrainOpenGLShapeRing(10, 0.75, 1.0); } } /** * Receive mouse press events from Qt. * @param me * The mouse event. */ void FiberSamplesOpenGLWidget::mousePressEvent(QMouseEvent* me) { const Qt::MouseButton button = me->button(); // const Qt::KeyboardModifiers keyModifiers = me->modifiers(); if (button == Qt::LeftButton) { const int mouseX = me->x(); const int mouseY = m_widgetHeight - me->y(); m_mousePressX = mouseX; m_mousePressY = mouseY; m_lastMouseX = mouseX; m_lastMouseY = mouseY; } else { m_mousePressX = -10000; m_mousePressY = -10000; } me->accept(); } /** * Receive mouse button release events from Qt. * @param me * The mouse event. */ void FiberSamplesOpenGLWidget::mouseReleaseEvent(QMouseEvent* me) { m_mousePressX = -10000; m_mousePressY = -10000; me->accept(); } /** * Receive mouse move events from Qt. * @param me * The mouse event. */ void FiberSamplesOpenGLWidget::mouseMoveEvent(QMouseEvent* me) { const Qt::MouseButton button = me->button(); // Qt::KeyboardModifiers keyModifiers = me->modifiers(); if (button == Qt::NoButton) { if (me->buttons() == Qt::LeftButton) { const int mouseX = me->x(); const int mouseY = m_widgetHeight - me->y(); const int dx = mouseX - m_lastMouseX; const int dy = mouseY - m_lastMouseY; const int absDX = (dx >= 0) ? dx : -dx; const int absDY = (dy >= 0) ? dy : -dy; if ((absDX > 0) || (absDY > 0)) { m_rotationMatrix.rotateX(-dy); m_rotationMatrix.rotateY(dx); } m_lastMouseX = mouseX; m_lastMouseY = mouseY; } } me->accept(); updateGL(); } connectome-workbench-1.2.3+git41-gc4c6c90/src/GuiQt/FiberSamplesOpenGLWidget.h000066400000000000000000000064611300200146000265420ustar00rootroot00000000000000#ifndef __FIBER_SAMPLES_OPEN_G_L_WIDGET__H_ #define __FIBER_SAMPLES_OPEN_G_L_WIDGET__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 "Matrix4x4.h" class QLabel; class QCheckBox; namespace caret { class BrainOpenGLShapeRing; class BrainOpenGLShapeSphere; class FiberSamplesOpenGLWidget : public QGLWidget { Q_OBJECT public: FiberSamplesOpenGLWidget(const int32_t browserWindowIndex, QCheckBox* enabledCheckBox, QLabel* fiberMeanLabels[3], QLabel* fiberVarianceLabels[3], QWidget* parent = 0); virtual ~FiberSamplesOpenGLWidget(); // ADD_NEW_METHODS_HERE protected: void initializeGL(); void resizeGL(int width, int height); void paintGL(); void mouseMoveEvent(QMouseEvent* e); void mousePressEvent(QMouseEvent* e); void mouseReleaseEvent(QMouseEvent* e); private: FiberSamplesOpenGLWidget(const FiberSamplesOpenGLWidget&); FiberSamplesOpenGLWidget& operator=(const FiberSamplesOpenGLWidget&); void createShapes(); void setupLighting(); void setOrthographicProjection(const int width, const int height); void drawOrientations(); // ADD_NEW_MEMBERS_HERE int32_t m_browserWindowIndex; BrainOpenGLShapeSphere* m_sphereBig; BrainOpenGLShapeSphere* m_sphereSmall; BrainOpenGLShapeRing* m_ring; GLint m_widgetWidth; GLint m_widgetHeight; int m_mousePressX; int m_mousePressY; int m_lastMouseX; int m_lastMouseY; Matrix4x4 m_rotationMatrix; QCheckBox* m_enabledCheckBox; QLabel* m_fiberMeanLabels[3]; QLabel* m_fiberVarianceLabels[3]; static const float s_sphereBigRadius; static const float s_sphereSmallRadius; }; #ifdef __FIBER_SAMPLES_OPEN_G_L_WIDGET_DECLARE__ const float FiberSamplesOpenGLWidget::s_sphereBigRadius = 100.0; const float FiberSamplesOpenGLWidget::s_sphereSmallRadius = 1.0; #endif // __FIBER_SAMPLES_OPEN_G_L_WIDGET_DECLARE__ } // namespace #endif //__FIBER_SAMPLES_OPEN_G_L_WIDGET__H_ connectome-workbench-1.2.3+git41-gc4c6c90/src/GuiQt/FociProjectionDialog.cxx000066400000000000000000000237601300200146000263660ustar00rootroot00000000000000 /*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 #define __FOCI_PROJECTION_DIALOG_DECLARE__ #include "FociProjectionDialog.h" #undef __FOCI_PROJECTION_DIALOG_DECLARE__ #include "Brain.h" #include "CursorDisplayScoped.h" #include "EventGraphicsUpdateAllWindows.h" #include "EventManager.h" #include "FociFile.h" #include "GuiManager.h" #include "Surface.h" #include "SurfaceProjector.h" #include "SurfaceSelectionViewController.h" #include "WuQFactory.h" #include "WuQMessageBox.h" using namespace caret; /** * \class caret::FociProjectionDialog * \brief Dialog for projection of foci. * \ingroup GuiQt */ /** * Constructor. * @param parent * The parent widget. */ FociProjectionDialog::FociProjectionDialog(QWidget* parent) : WuQDialogModal("Project Foci", parent) { QWidget* surfaceWidget = NULL; //createSurfaceSelectionWidget(); QWidget* fociFileWidget = createFociFileSelectionWidget(); QWidget* optionsWidget = createOptionsWidget(); QWidget* widget = new QWidget(); QVBoxLayout* layout = new QVBoxLayout(widget); if (surfaceWidget != NULL) { layout->addWidget(surfaceWidget); } layout->addWidget(fociFileWidget); layout->addWidget(optionsWidget); setCentralWidget(widget, WuQDialog::SCROLL_AREA_NEVER); } /** * Destructor. */ FociProjectionDialog::~FociProjectionDialog() { } /** * Called when the OK button is clicked. */ void FociProjectionDialog::okButtonClicked() { CursorDisplayScoped cursor; cursor.showWaitCursor(); Brain* brain = GuiManager::get()->getBrain(); std::vector surfaceFiles = brain->getPrimaryAnatomicalSurfaceFiles(); SurfaceFile* leftSurfaceFile = NULL; SurfaceFile* rightSurfaceFile = NULL; SurfaceFile* cerebellumSurfaceFile = NULL; for (std::vector::iterator iter = surfaceFiles.begin(); iter != surfaceFiles.end(); iter++) { const SurfaceFile* sf = *iter; switch (sf->getStructure()) { case StructureEnum::CORTEX_LEFT: leftSurfaceFile = const_cast(sf); break; case StructureEnum::CORTEX_RIGHT: rightSurfaceFile = const_cast(sf); break; case StructureEnum::CEREBELLUM: cerebellumSurfaceFile = const_cast(sf); break; default: break; } } // if (m_leftSurfaceCheckBox != NULL) { // if (m_leftSurfaceCheckBox->isChecked()) { // const Surface* sf = m_leftSurfaceViewController->getSurface(); // if (sf != NULL) { // surfaceFiles.push_back(sf); // } // } // } // if (m_rightSurfaceCheckBox != NULL) { // if (m_rightSurfaceCheckBox->isChecked()) { // const Surface* sf = m_rightSurfaceViewController->getSurface(); // if (sf != NULL) { // surfaceFiles.push_back(sf); // } // } // } // if (m_cerebellumSurfaceCheckBox != NULL) { // if (m_cerebellumSurfaceCheckBox->isChecked()) { // const Surface* sf = m_cerebellumSurfaceViewController->getSurface(); // if (sf != NULL) { // surfaceFiles.push_back(sf); // } // } // } AString errorMessages = ""; const int32_t numberOfFociFiles = static_cast(m_fociFiles.size()); for (int32_t i = 0; i < numberOfFociFiles; i++) { if (m_fociFileCheckBoxes[i]->isChecked()) { SurfaceProjector projector(leftSurfaceFile, rightSurfaceFile, cerebellumSurfaceFile); if (m_projectAboveSurfaceCheckBox->isChecked()) { projector.setSurfaceOffset(m_projectAboveSurfaceSpinBox->value()); } FociFile* ff = brain->getFociFile(i); try { projector.projectFociFile(ff); } catch (const SurfaceProjectorException& spe) { errorMessages += (spe.whatString() + "\n"); } } } EventManager::get()->sendEvent(EventGraphicsUpdateAllWindows().getPointer()); cursor.restoreCursor(); if (errorMessages.isEmpty() == false) { WuQMessageBox::errorOk(this, errorMessages); } WuQDialogModal::okButtonClicked(); } /** * @return The controls for surface selection. */ QWidget* FociProjectionDialog::createSurfaceSelectionWidget() { Brain* brain = GuiManager::get()->getBrain(); BrainStructure* leftBrainStructure = brain->getBrainStructure(StructureEnum::CORTEX_LEFT, false); BrainStructure* rightBrainStructure = brain->getBrainStructure(StructureEnum::CORTEX_RIGHT, false); BrainStructure* cerebellumBrainStructure = brain->getBrainStructure(StructureEnum::CEREBELLUM, false); m_leftSurfaceCheckBox = NULL; m_leftSurfaceViewController = NULL; if (leftBrainStructure != NULL) { m_leftSurfaceCheckBox = new QCheckBox("Left: "); m_leftSurfaceCheckBox->setChecked(true); m_leftSurfaceViewController = new SurfaceSelectionViewController(this, leftBrainStructure); m_leftSurfaceViewController->updateControl(); } m_rightSurfaceCheckBox = NULL; m_rightSurfaceViewController = NULL; if (rightBrainStructure != NULL) { m_rightSurfaceCheckBox = new QCheckBox("Right: "); m_rightSurfaceCheckBox->setChecked(true); m_rightSurfaceViewController = new SurfaceSelectionViewController(this, rightBrainStructure); m_rightSurfaceViewController->updateControl(); } m_cerebellumSurfaceCheckBox = NULL; m_cerebellumSurfaceViewController = NULL; if (cerebellumBrainStructure != NULL) { m_cerebellumSurfaceCheckBox = new QCheckBox("Cerebellum: "); m_cerebellumSurfaceCheckBox->setChecked(true); m_cerebellumSurfaceViewController = new SurfaceSelectionViewController(this, cerebellumBrainStructure); m_cerebellumSurfaceViewController->updateControl(); } QGroupBox* groupBox = new QGroupBox("Projection Surfaces"); QGridLayout* layout = new QGridLayout(groupBox); layout->setColumnStretch(0, 0); layout->setColumnStretch(1, 100); int rowIndex = layout->rowCount(); if (m_leftSurfaceCheckBox != NULL) { layout->addWidget(m_leftSurfaceCheckBox, rowIndex, 0); layout->addWidget(m_leftSurfaceViewController->getWidget(), rowIndex, 1); rowIndex++; } if (m_rightSurfaceCheckBox != NULL) { layout->addWidget(m_rightSurfaceCheckBox, rowIndex, 0); layout->addWidget(m_rightSurfaceViewController->getWidget(), rowIndex, 1); rowIndex++; } if (m_cerebellumSurfaceCheckBox != NULL) { layout->addWidget(m_cerebellumSurfaceCheckBox, rowIndex, 0); layout->addWidget(m_cerebellumSurfaceViewController->getWidget(), rowIndex, 1); rowIndex++; } return groupBox; } /** * @return The controls for foci file selection. */ QWidget* FociProjectionDialog::createFociFileSelectionWidget() { QGroupBox* groupBox = new QGroupBox("Foci Files for Projection"); QVBoxLayout* layout = new QVBoxLayout(groupBox); Brain* brain = GuiManager::get()->getBrain(); const int32_t numberOfFociFiles = brain->getNumberOfFociFiles(); for (int32_t i = 0; i < numberOfFociFiles; i++) { FociFile* ff = brain->getFociFile(i); m_fociFiles.push_back(ff); QCheckBox* checkBox = new QCheckBox(ff->getFileNameNoPath()); checkBox->setChecked(true); m_fociFileCheckBoxes.push_back(checkBox); layout->addWidget(checkBox); } return groupBox; } /** * @return The options controls. */ QWidget* FociProjectionDialog::createOptionsWidget() { m_projectAboveSurfaceCheckBox = new QCheckBox("Project fixed distance above surface(s)"); m_projectAboveSurfaceSpinBox = WuQFactory::newDoubleSpinBox(); m_projectAboveSurfaceSpinBox->setRange(-100000.0, 100000.0); m_projectAboveSurfaceSpinBox->setDecimals(2); m_projectAboveSurfaceSpinBox->setSingleStep(1.0); QObject::connect(m_projectAboveSurfaceCheckBox, SIGNAL(toggled(bool)), m_projectAboveSurfaceSpinBox, SLOT(setEnabled(bool))); m_projectAboveSurfaceCheckBox->setChecked(false); QGroupBox* groupBox = new QGroupBox("Projection Options"); QGridLayout* layout = new QGridLayout(groupBox); layout->setColumnStretch(0, 0); layout->setColumnStretch(1, 100); int rowIndex = layout->rowCount(); layout->addWidget(m_projectAboveSurfaceCheckBox, rowIndex, 0); layout->addWidget(m_projectAboveSurfaceSpinBox, rowIndex, 1); rowIndex++; return groupBox; } connectome-workbench-1.2.3+git41-gc4c6c90/src/GuiQt/FociProjectionDialog.h000066400000000000000000000050131300200146000260020ustar00rootroot00000000000000#ifndef __FOCI_PROJECTION_DIALOG__H_ #define __FOCI_PROJECTION_DIALOG__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 "WuQDialogModal.h" class QCheckBox; class QDoubleSpinBox; namespace caret { class FociFile; class SurfaceSelectionViewController; class FociProjectionDialog : public WuQDialogModal { Q_OBJECT public: FociProjectionDialog(QWidget* parent); virtual ~FociProjectionDialog(); // ADD_NEW_METHODS_HERE protected: virtual void okButtonClicked(); private: FociProjectionDialog(const FociProjectionDialog&); FociProjectionDialog& operator=(const FociProjectionDialog&); QWidget* createSurfaceSelectionWidget(); QWidget* createFociFileSelectionWidget(); QWidget* createOptionsWidget(); QCheckBox* m_leftSurfaceCheckBox; SurfaceSelectionViewController* m_leftSurfaceViewController; QCheckBox* m_rightSurfaceCheckBox; SurfaceSelectionViewController* m_rightSurfaceViewController; QCheckBox* m_cerebellumSurfaceCheckBox; SurfaceSelectionViewController* m_cerebellumSurfaceViewController; QCheckBox* m_projectAboveSurfaceCheckBox; QDoubleSpinBox* m_projectAboveSurfaceSpinBox; std::vector m_fociFiles; std::vector m_fociFileCheckBoxes; // ADD_NEW_MEMBERS_HERE }; #ifdef __FOCI_PROJECTION_DIALOG_DECLARE__ // #endif // __FOCI_PROJECTION_DIALOG_DECLARE__ } // namespace #endif //__FOCI_PROJECTION_DIALOG__H_ connectome-workbench-1.2.3+git41-gc4c6c90/src/GuiQt/FociPropertiesEditorDialog.cxx000066400000000000000000000606551300200146000275610ustar00rootroot00000000000000 /*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 #include #define __FOCI_PROPERTIES_EDITOR_DIALOG__DECLARE__ #include "FociPropertiesEditorDialog.h" #undef __FOCI_PROPERTIES_EDITOR_DIALOG__DECLARE__ #include "Brain.h" #include "BrowserTabContent.h" #include "Focus.h" #include "FociFile.h" #include "CaretAssert.h" #include "CaretFileDialog.h" #include "DisplayPropertiesFoci.h" #include "EventGraphicsUpdateAllWindows.h" #include "EventDataFileAdd.h" #include "EventManager.h" #include "EventUserInterfaceUpdate.h" #include "GiftiLabel.h" #include "GiftiLabelTable.h" #include "GiftiLabelTableEditor.h" #include "GiftiLabelTableSelectionComboBox.h" #include "CaretLogger.h" #include "GuiManager.h" #include "SurfaceProjector.h" #include "WuQDataEntryDialog.h" #include "WuQFactory.h" #include "WuQMessageBox.h" #include "WuQtUtilities.h" using namespace caret; /** * \class caret::FociPropertiesEditorDialog * \brief dialog for Foci Properties Editor * \ingroup GuiQt */ /** * Create (edit and add) a new focus. * * @param focus * The focus that will be displayed in the focus editor. * If the user accepts (presses OK) to add the focus * it will be added to the selected foci file. If the * user cancels, the focus will be destroyed. Thus, * the caller MUST NEVER access the focus after calling * this static method. * @param browserTabContent * Tab content on which focis are created. * @param parent * Parent widget on which dialog is displayed. * @return * true if user pressed OK, else false. */ bool FociPropertiesEditorDialog::createFocus(Focus* focus, BrowserTabContent* browserTabContent, QWidget* parent) { CaretAssert(focus); if (s_previousCreateFocus != NULL) { focus->setName(s_previousCreateFocus->getName()); focus->setArea(s_previousCreateFocus->getArea()); focus->setClassName(s_previousCreateFocus->getClassName()); focus->setComment(s_previousCreateFocus->getComment()); focus->setExtent(s_previousCreateFocus->getExtent()); focus->setGeography(s_previousCreateFocus->getGeography()); focus->setRegionOfInterest(s_previousCreateFocus->getRegionOfInterest()); focus->setStatistic(s_previousCreateFocus->getStatistic()); } FociPropertiesEditorDialog focusCreateDialog("Create Focus", s_previousCreateFociFile, focus, true, parent); if (focusCreateDialog.exec() == FociPropertiesEditorDialog::Accepted) { s_previousCreateFociFile = focusCreateDialog.getSelectedFociFile(); if (s_previousCreateFocus == NULL) { s_previousCreateFocus = new Focus(); } focusCreateDialog.loadFromDialogIntoFocusData(s_previousCreateFocus); s_previousCreateFociFile->addFocus(focus); if (browserTabContent != NULL) { const int32_t tabIndex = browserTabContent->getTabNumber(); DisplayPropertiesFoci* dsf = GuiManager::get()->getBrain()->getDisplayPropertiesFoci(); DisplayGroupEnum::Enum displayGroup = dsf->getDisplayGroupForTab(tabIndex); dsf->setDisplayed(displayGroup, tabIndex, true); } focusCreateDialog.updateGraphicsAndUserInterface(); return true; } delete focus; return false; } /** * Edit an existing focus. * * @param focus * The focus that will be displayed in the focus editor. * If the user presses the OK button the focus will be * updated. Otherwise, no further action is taken. * @param parent * Parent widget on which dialog is displayed. * @return * true if user pressed OK, else false. */ bool FociPropertiesEditorDialog::editFocus(FociFile* fociFile, Focus* focus, QWidget* parent) { FociPropertiesEditorDialog fped("Edit Focus", fociFile, focus, false, parent); if (fped.exec() == FociPropertiesEditorDialog::Accepted) { return true; } return false; } /** * Delete all static members to eliminate reported memory leaks. */ void FociPropertiesEditorDialog::deleteStaticMembers() { if (s_previousCreateFocus != NULL) { delete s_previousCreateFocus; s_previousCreateFocus = NULL; } } /** * Constructor. */ FociPropertiesEditorDialog::FociPropertiesEditorDialog(const QString& title, FociFile* fociFile, Focus* focus, const bool allowFociFileSelection, QWidget* parent) : WuQDialogModal(title, parent) { CaretAssert(focus); m_focus = focus; m_classComboBox = NULL; /* * File selection combo box */ QLabel* fociFileLabel = new QLabel("File"); m_fociFileSelectionComboBox = new QComboBox(); WuQtUtilities::setToolTipAndStatusTip(m_fociFileSelectionComboBox, "Selects an existing focus file\n" "to which new focus are added."); QObject::connect(m_fociFileSelectionComboBox, SIGNAL(activated(int)), this, SLOT(fociFileSelected())); QAction* newFileAction = WuQtUtilities::createAction("New", "Create a new focus file", this, this, SLOT(newFociFileButtonClicked())); QToolButton* newFileToolButton = new QToolButton(); newFileToolButton->setDefaultAction(newFileAction); /* * Completer for name */ m_nameCompleterStringListModel = new QStringListModel(this); /* * Name */ QLabel* nameLabel = new QLabel("Name"); m_nameComboBox = new GiftiLabelTableSelectionComboBox(this); m_nameComboBox->setUnassignedLabelTextOverride("Select Name"); QAction* displayNameColorEditorAction = WuQtUtilities::createAction("Add/Edit...", "Add and/or edit name colors", this, this, SLOT(displayNameEditor())); QToolButton* displayNameColorEditorToolButton = new QToolButton(); displayNameColorEditorToolButton->setDefaultAction(displayNameColorEditorAction); /* * Class */ QLabel* classLabel = new QLabel("Class"); m_classComboBox = new GiftiLabelTableSelectionComboBox(this); WuQtUtilities::setToolTipAndStatusTip(m_classComboBox->getWidget(), "The class is used to group focuss with similar\n" "characteristics. Controls are available to\n" "display focuss by their class attributes."); QAction* displayClassEditorAction = WuQtUtilities::createAction("Add/Edit...", "Add and/or edit classes", this, this, SLOT(displayClassEditor())); QToolButton* displayClassEditorToolButton = new QToolButton(); displayClassEditorToolButton->setDefaultAction(displayClassEditorAction); /* * Coordinates */ QLabel* coordLabel = new QLabel("XYZ (mm)"); m_xCoordSpinBox = WuQFactory::newDoubleSpinBox(); m_xCoordSpinBox->setDecimals(2); m_xCoordSpinBox->setSingleStep(1.0); m_xCoordSpinBox->setRange(-10000000.0, 10000000.0); m_yCoordSpinBox = WuQFactory::newDoubleSpinBox(); m_yCoordSpinBox->setDecimals(2); m_yCoordSpinBox->setSingleStep(1.0); m_yCoordSpinBox->setRange(-10000000.0, 10000000.0); m_zCoordSpinBox = WuQFactory::newDoubleSpinBox(); m_zCoordSpinBox->setDecimals(2); m_zCoordSpinBox->setSingleStep(1.0); m_zCoordSpinBox->setRange(-10000000.0, 10000000.0); /* * Comment */ QLabel* commentLabel = new QLabel("Comment"); m_commentTextEdit = new QTextEdit(); m_commentTextEdit->setFixedHeight(60); QLabel* areaLabel = new QLabel("Area"); m_areaLineEdit = new QLineEdit(); QLabel* geographyLabel = new QLabel("Geography"); m_geographyLineEdit = new QLineEdit(); QLabel* extentLabel = new QLabel("Extent"); m_extentSpinBox = WuQFactory::newDoubleSpinBox(); m_extentSpinBox->setDecimals(2); m_extentSpinBox->setSingleStep(1.0); m_extentSpinBox->setRange(-10000000.0, 10000000.0); QLabel* regionOfInterestLabel = new QLabel("ROI"); m_regionOfInterestLineEdit = new QLineEdit(); QLabel* statisticLabel = new QLabel("Statistic"); m_statisticLineEdit = new QLineEdit(); m_projectCheckBox = new QCheckBox("Project to Surface"); m_projectCheckBox->setChecked(s_previousFociProjectSelected); /* * Layout widgets */ QWidget* widget = new QWidget(); QGridLayout* gridLayout = new QGridLayout(widget); WuQtUtilities::setLayoutSpacingAndMargins(gridLayout, 4, 4); gridLayout->setColumnStretch(0, 0); gridLayout->setColumnStretch(1, 100); gridLayout->setColumnStretch(2, 100); gridLayout->setColumnStretch(3, 100); gridLayout->setColumnStretch(4, 0); int row = 0; gridLayout->addWidget(fociFileLabel, row, 0); gridLayout->addWidget(m_fociFileSelectionComboBox, row, 1, 1, 3); gridLayout->addWidget(newFileToolButton, row, 4); row++; gridLayout->addWidget(WuQtUtilities::createHorizontalLineWidget(), row, 0, 1, 5); row++; gridLayout->addWidget(nameLabel, row, 0); gridLayout->addWidget(m_nameComboBox->getWidget(), row, 1, 1, 3); gridLayout->addWidget(displayNameColorEditorToolButton, row, 4); row++; gridLayout->addWidget(classLabel, row, 0); gridLayout->addWidget(m_classComboBox->getWidget(), row, 1, 1, 3); gridLayout->addWidget(displayClassEditorToolButton, row, 4); row++; gridLayout->addWidget(coordLabel, row, 0); gridLayout->addWidget(m_xCoordSpinBox, row, 1); gridLayout->addWidget(m_yCoordSpinBox, row, 2); gridLayout->addWidget(m_zCoordSpinBox, row, 3); row++; gridLayout->addWidget(commentLabel, row, 0); gridLayout->addWidget(m_commentTextEdit, row, 1, 1, 3); row++; gridLayout->addWidget(areaLabel, row, 0); gridLayout->addWidget(m_areaLineEdit, row, 1, 1, 3); row++; gridLayout->addWidget(geographyLabel, row, 0); gridLayout->addWidget(m_geographyLineEdit, row, 1, 1, 3); row++; gridLayout->addWidget(extentLabel, row, 0); gridLayout->addWidget(m_extentSpinBox, row, 1, 1, 1); row++; gridLayout->addWidget(regionOfInterestLabel, row, 0); gridLayout->addWidget(m_regionOfInterestLineEdit, row, 1, 1, 3); row++; gridLayout->addWidget(statisticLabel, row, 0); gridLayout->addWidget(m_statisticLineEdit, row, 1, 1, 3); row++; gridLayout->addWidget(WuQtUtilities::createHorizontalLineWidget(), row, 0, 1, 4); row++; gridLayout->addWidget(m_projectCheckBox, row, 0, 1, 4); row++; /* * Allow/Disallow file selection */ fociFileLabel->setEnabled(allowFociFileSelection); m_fociFileSelectionComboBox->setEnabled(allowFociFileSelection); newFileToolButton->setEnabled(allowFociFileSelection); /* * Set the widget for the dialog. */ setCentralWidget(widget, WuQDialog::SCROLL_AREA_NEVER); loadFociFileComboBox(fociFile); loadFromFocusDataIntoDialog(m_focus); } /** * Destructor. */ FociPropertiesEditorDialog::~FociPropertiesEditorDialog() { } /** * Get the selected focus file. * @return FociFile or NULL if no focus file. */ FociFile* FociPropertiesEditorDialog::getSelectedFociFile() { FociFile* fociFile = NULL; const int fileComboBoxIndex = m_fociFileSelectionComboBox->currentIndex(); if (fileComboBoxIndex >= 0) { void* filePointer = m_fociFileSelectionComboBox->itemData(fileComboBoxIndex).value(); fociFile = (FociFile*)filePointer; } return fociFile; } /** * Load the focus files into the focus file combo box. * @param fociFile * Selected foci file, may be NULL. */ void FociPropertiesEditorDialog::loadFociFileComboBox(const FociFile* selectedFociFile) { Brain* brain = GuiManager::get()->getBrain(); const int32_t numFociFiles = brain->getNumberOfFociFiles(); m_fociFileSelectionComboBox->clear(); int defaultFileComboIndex = 0; for (int32_t i = 0; i < numFociFiles; i++) { FociFile* fociFile = brain->getFociFile(i); const AString name = fociFile->getFileNameNoPath(); m_fociFileSelectionComboBox->addItem(name, qVariantFromValue((void*)fociFile)); if (selectedFociFile == fociFile) { defaultFileComboIndex = m_fociFileSelectionComboBox->count() - 1; } } if (numFociFiles > 0) { m_fociFileSelectionComboBox->setCurrentIndex(defaultFileComboIndex); const FociFile* fociFile = getSelectedFociFile(); if (fociFile != NULL) { m_nameCompleterStringList = fociFile->getAllFociNamesSorted(); m_nameCompleterStringListModel->setStringList(m_nameCompleterStringList); } } } /** * Called to create a new focus file. */ void FociPropertiesEditorDialog::newFociFileButtonClicked() { /* * Let user choose a different path/name */ FociFile* newFociFile = new FociFile(); GuiManager::get()->getBrain()->convertDataFilePathNameToAbsolutePathName(newFociFile); AString newFociFileName = CaretFileDialog::getSaveFileNameDialog(DataFileTypeEnum::FOCI, this, "Choose Foci File Name", newFociFile->getFileName()); /* * If user cancels, delete the new focus file and return */ if (newFociFileName.isEmpty()) { delete newFociFile; return; } /* * Set name of new scene file */ newFociFile->setFileName(newFociFileName); EventManager::get()->sendEvent(EventDataFileAdd(newFociFile).getPointer()); loadFociFileComboBox(newFociFile); fociFileSelected(); } /** * Called when a focus file is selected. */ void FociPropertiesEditorDialog::fociFileSelected() { loadNameComboBox(); if (m_classComboBox != NULL) { loadClassComboBox(); } } /** * Load the class combo box. * * @param name * If not empty, make this name the selected name. */ void FociPropertiesEditorDialog::loadClassComboBox(const QString& name) { FociFile* fociFile = getSelectedFociFile(); if (fociFile != NULL) { m_classComboBox->updateContent(fociFile->getClassColorTable()); if (name.isEmpty() == false) { m_classComboBox->setSelectedLabelName(name); } } else { m_classComboBox->updateContent(NULL); } } /** * Load the name combo box. * * @param name * If not empty, make this name the selected name. */ void FociPropertiesEditorDialog::loadNameComboBox(const QString& name) { FociFile* fociFile = getSelectedFociFile(); if (fociFile != NULL) { m_nameComboBox->updateContent(fociFile->getNameColorTable()); if (name.isEmpty() == false) { m_nameComboBox->setSelectedLabelName(name); } } else { m_nameComboBox->updateContent(NULL); } } /** * Called when the OK button is pressed. */ void FociPropertiesEditorDialog::okButtonClicked() { AString errorMessage; /* * Get focus file. */ FociFile* fociFile = getSelectedFociFile(); if (fociFile == NULL) { WuQMessageBox::errorOk(this, "Foci file is not valid, use the New button to create a foci file."); return; } /* * Get data entered by the user. */ const AString name = m_nameComboBox->getSelectedLabelName(); if (name.isEmpty()) { errorMessage += ("Name is invalid.\n"); } else { const int32_t unassignedNameKey = fociFile->getNameColorTable()->getUnassignedLabelKey(); const int32_t selectedNameKey = m_nameComboBox->getSelectedLabelKey(); if (selectedNameKey == unassignedNameKey) { errorMessage += "Choose or create a name for the border"; } } const QString className = m_classComboBox->getSelectedLabelName(); if ((m_xCoordSpinBox->value() == 0.0) && (m_yCoordSpinBox->value() == 0.0) && (m_zCoordSpinBox->value() == 0.0)) { errorMessage += "Coordinates are invalid (all zeros)\n"; } /* * Error? */ if (errorMessage.isEmpty() == false) { WuQMessageBox::errorOk(this, errorMessage); return; } /* * Copy data to the focus */ loadFromDialogIntoFocusData(m_focus); /* * Project the focus */ if (m_projectCheckBox->isChecked()) { Brain* brain = GuiManager::get()->getBrain(); std::vector surfaceFiles = brain->getPrimaryAnatomicalSurfaceFiles(); try { SurfaceProjector projector(surfaceFiles); projector.projectFocus(0, m_focus); } catch (SurfaceProjectorException& spe) { CaretLogSevere(spe.whatString()); } } /* * Save project status */ s_previousFociProjectSelected = m_projectCheckBox->isChecked(); updateGraphicsAndUserInterface(); if (m_nameCompleterStringList.contains(name) == false) { m_nameCompleterStringList.append(name); m_nameCompleterStringList.sort(); m_nameCompleterStringListModel->setStringList(m_nameCompleterStringList); } /* * continue with OK button processing */ WuQDialogModal::okButtonClicked(); } /** * Update the graphics and user interface. */ void FociPropertiesEditorDialog::updateGraphicsAndUserInterface() { EventManager::get()->sendEvent(EventUserInterfaceUpdate().getPointer()); EventManager::get()->sendEvent(EventGraphicsUpdateAllWindows().getPointer()); } /** * Load data from the given focus into the dialog. * @param focus * Focus from which data is obtained. */ void FociPropertiesEditorDialog::loadFromFocusDataIntoDialog(const Focus* focus) { loadNameComboBox(focus->getName()); loadClassComboBox(focus->getClassName()); float xyz[3] = { 0.0, 0.0, 0.0 }; if (focus->getNumberOfProjections() > 0) { focus->getProjection(0)->getStereotaxicXYZ(xyz); } m_xCoordSpinBox->setValue(xyz[0]); m_yCoordSpinBox->setValue(xyz[1]); m_zCoordSpinBox->setValue(xyz[2]); m_commentTextEdit->setText(focus->getComment()); m_areaLineEdit->setText(focus->getArea()); m_geographyLineEdit->setText(focus->getGeography()); m_extentSpinBox->setValue(focus->getExtent()); m_regionOfInterestLineEdit->setText(focus->getRegionOfInterest()); m_statisticLineEdit->setText(focus->getStatistic()); } /** * Load data from dialog into the focus. * @param focus * Focus into which data is placed. */ void 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() }; CaretAssert(focus->getNumberOfProjections() > 0); focus->getProjection(0)->setStereotaxicXYZ(xyz); focus->setComment(m_commentTextEdit->toPlainText().trimmed()); focus->setArea(m_areaLineEdit->text().trimmed()); focus->setGeography(m_geographyLineEdit->text().trimmed()); focus->setExtent(m_extentSpinBox->value()); focus->setRegionOfInterest(m_regionOfInterestLineEdit->text().trimmed()); focus->setStatistic(m_statisticLineEdit->text().trimmed()); } /** * Display the class editor */ void FociPropertiesEditorDialog::displayClassEditor() { FociFile* fociFile = getSelectedFociFile(); if (fociFile == NULL) { WuQMessageBox::errorOk(this, "Focus file is not valid, use the New button to create a focus file."); return; } GiftiLabelTableEditor editor(fociFile, fociFile->getClassColorTable(), "Edit Class Attributes", GiftiLabelTableEditor::OPTION_NONE, this); const QString className = m_classComboBox->getSelectedLabelName(); if (className.isEmpty() == false) { editor.selectLabelWithName(className); } const int dialogResult = editor.exec(); loadClassComboBox(); if (dialogResult == GiftiLabelTableEditor::Accepted) { const QString selectedClassName = editor.getLastSelectedLabelName(); if (selectedClassName.isEmpty() == false) { m_classComboBox->setSelectedLabelName(selectedClassName); } } } /** * Display the name editor */ void FociPropertiesEditorDialog::displayNameEditor() { FociFile* fociFile = getSelectedFociFile(); if (fociFile == NULL) { WuQMessageBox::errorOk(this, "Focus file is not valid, use the New button to create a focus file."); return; } GiftiLabelTableEditor editor(fociFile, fociFile->getNameColorTable(), "Edit Name Attributes", GiftiLabelTableEditor::OPTION_UNASSIGNED_LABEL_HIDDEN, this); const QString name = this->m_nameComboBox->getSelectedLabelName(); if (name.isEmpty() == false) { editor.selectLabelWithName(name); } const int dialogResult = editor.exec(); this->loadNameComboBox(); if (dialogResult == GiftiLabelTableEditor::Accepted) { const QString selectedName = editor.getLastSelectedLabelName(); if (selectedName.isEmpty() == false) { m_nameComboBox->setSelectedLabelName(selectedName); } } } /** * @return Is the project checkbox selected? */ bool FociPropertiesEditorDialog::isProjectSelected() { return m_projectCheckBox->isChecked(); } /** * set the status of the project checkbox. * @param selected * New status. */ void FociPropertiesEditorDialog::setProjectSelected(const bool selected) { m_projectCheckBox->setChecked(selected); } connectome-workbench-1.2.3+git41-gc4c6c90/src/GuiQt/FociPropertiesEditorDialog.h000066400000000000000000000110711300200146000271720ustar00rootroot00000000000000#ifndef __FOCI_PROPERTIES_EDITOR_DIALOG__H_ #define __FOCI_PROPERTIES_EDITOR_DIALOG__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 "CaretColorEnum.h" #include "WuQDialogModal.h" class QCheckBox; class QComboBox; class QDoubleSpinBox; class QLabel; class QLineEdit; class QStringListModel; class QTextEdit; namespace caret { class BrowserTabContent; class Focus; class FociFile; class GiftiLabelTableSelectionComboBox; class FociPropertiesEditorDialog : public WuQDialogModal { Q_OBJECT public: static bool createFocus(Focus* focus, BrowserTabContent* browserTabContent, QWidget* parent); static bool editFocus(FociFile* fociFile, Focus* focus, QWidget* parent); FociPropertiesEditorDialog(const QString& title, FociFile* fociFile, Focus* focus, const bool allowFociFileSelection, QWidget* parent = 0); virtual ~FociPropertiesEditorDialog(); FociFile* getSelectedFociFile(); void loadFromFocusDataIntoDialog(const Focus* focus); void loadFromDialogIntoFocusData(Focus* focus) const; bool isProjectSelected(); void setProjectSelected(const bool selected); static void deleteStaticMembers(); protected: virtual void okButtonClicked(); private slots: void displayClassEditor(); void displayNameEditor(); void fociFileSelected(); void newFociFileButtonClicked(); private: FociPropertiesEditorDialog(const FociPropertiesEditorDialog&); FociPropertiesEditorDialog& operator=(const FociPropertiesEditorDialog&); void loadFociFileComboBox(const FociFile* fociFile); void loadClassComboBox(const QString& name = ""); void loadNameComboBox(const QString& name = ""); void updateGraphicsAndUserInterface(); Focus* m_focus; QComboBox* m_fociFileSelectionComboBox; GiftiLabelTableSelectionComboBox* m_nameComboBox; GiftiLabelTableSelectionComboBox* m_classComboBox; QDoubleSpinBox* m_xCoordSpinBox; QDoubleSpinBox* m_yCoordSpinBox; QDoubleSpinBox* m_zCoordSpinBox; QTextEdit* m_commentTextEdit; QLineEdit* m_areaLineEdit; QLineEdit* m_geographyLineEdit; QDoubleSpinBox* m_extentSpinBox; QLineEdit* m_regionOfInterestLineEdit; QLineEdit* m_statisticLineEdit; QCheckBox* m_projectCheckBox; QStringList m_nameCompleterStringList; QStringListModel* m_nameCompleterStringListModel; static bool s_previousFociProjectSelected; /** * Previous selected foci file for creation of foci * NOTE: DO NOT DELETE THIS SINCE IT POINTS TO * AN EXISTING FOCI FILE */ static FociFile* s_previousCreateFociFile; /** Copy of previously created focus */ static Focus* s_previousCreateFocus; }; #ifdef __FOCI_PROPERTIES_EDITOR_DIALOG__DECLARE__ bool FociPropertiesEditorDialog::s_previousFociProjectSelected = true; FociFile* FociPropertiesEditorDialog::s_previousCreateFociFile = NULL; Focus* FociPropertiesEditorDialog::s_previousCreateFocus = NULL; #endif // __FOCI_PROPERTIES_EDITOR_DIALOG__DECLARE__ } // namespace #endif //__FOCI_PROPERTIES_EDITOR_DIALOG__H_ connectome-workbench-1.2.3+git41-gc4c6c90/src/GuiQt/FociSelectionViewController.cxx000066400000000000000000000442231300200146000277530ustar00rootroot00000000000000 /*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 __FOCI_SELECTION_VIEW_CONTROLLER_DECLARE__ #include "FociSelectionViewController.h" #undef __FOCI_SELECTION_VIEW_CONTROLLER_DECLARE__ #include "Brain.h" #include "BrainOpenGL.h" #include "BrowserTabContent.h" #include "CaretAssert.h" #include "CaretColorEnumComboBox.h" #include "GroupAndNameHierarchyViewController.h" #include "DisplayGroupEnumComboBox.h" #include "DisplayPropertiesFoci.h" #include "EnumComboBoxTemplate.h" #include "FeatureColoringTypeEnum.h" #include "EventGraphicsUpdateAllWindows.h" #include "EventManager.h" #include "EventUserInterfaceUpdate.h" #include "GuiManager.h" #include "SceneClass.h" #include "WuQDataEntryDialog.h" #include "WuQFactory.h" #include "WuQTabWidget.h" #include "WuQTrueFalseComboBox.h" #include "WuQtUtilities.h" using namespace caret; /** * \class caret::FociSelectionViewController * \brief Widget for controlling display of foci * \ingroup GuiQt * * Widget for controlling the display of foci including * different display groups. */ /** * Constructor. */ FociSelectionViewController::FociSelectionViewController(const int32_t browserWindowIndex, QWidget* parent) : QWidget(parent) { m_browserWindowIndex = browserWindowIndex; QLabel* groupLabel = new QLabel("Group"); m_fociDisplayGroupComboBox = new DisplayGroupEnumComboBox(this); QObject::connect(m_fociDisplayGroupComboBox, SIGNAL(displayGroupSelected(const DisplayGroupEnum::Enum)), this, SLOT(fociDisplayGroupSelected(const DisplayGroupEnum::Enum))); QHBoxLayout* groupLayout = new QHBoxLayout(); groupLayout->addWidget(groupLabel); groupLayout->addWidget(m_fociDisplayGroupComboBox->getWidget()); groupLayout->addStretch(); m_fociDisplayCheckBox = new QCheckBox("Display Foci"); m_fociDisplayCheckBox->setToolTip("Enable the display of foci"); QObject::connect(m_fociDisplayCheckBox, SIGNAL(clicked(bool)), this, SLOT(processAttributesChanges())); QWidget* attributesWidget = this->createAttributesWidget(); QWidget* selectionWidget = this->createSelectionWidget(); m_tabWidget = new WuQTabWidget(WuQTabWidget::TAB_ALIGN_LEFT, this); m_tabWidget->addTab(attributesWidget, "Attributes"); m_tabWidget->addTab(selectionWidget, "Selection"); m_tabWidget->setCurrentWidget(attributesWidget); QVBoxLayout* layout = new QVBoxLayout(this); //WuQtUtilities::setLayoutSpacingAndMargins(layout, 2, 2); layout->addWidget(m_fociDisplayCheckBox); layout->addWidget(WuQtUtilities::createHorizontalLineWidget()); layout->addLayout(groupLayout); layout->addWidget(m_tabWidget->getWidget(), 0, Qt::AlignLeft); layout->addStretch(); EventManager::get()->addEventListener(this, EventTypeEnum::EVENT_USER_INTERFACE_UPDATE); FociSelectionViewController::allFociSelectionViewControllers.insert(this); } /** * Destructor. */ FociSelectionViewController::~FociSelectionViewController() { EventManager::get()->removeAllEventsFromListener(this); FociSelectionViewController::allFociSelectionViewControllers.erase(this); } QWidget* FociSelectionViewController::createSelectionWidget() { m_fociClassNameHierarchyViewController = new GroupAndNameHierarchyViewController(m_browserWindowIndex); return m_fociClassNameHierarchyViewController; } /** * @return The attributes widget. */ QWidget* FociSelectionViewController::createAttributesWidget() { m_fociContralateralCheckBox = new QCheckBox("Contralateral"); m_fociContralateralCheckBox->setToolTip("Enable display of foci from contralateral brain structure"); QObject::connect(m_fociContralateralCheckBox, SIGNAL(clicked(bool)), this, SLOT(processAttributesChanges())); m_pasteOntoSurfaceCheckBox = new QCheckBox("Paste Onto Surface"); m_pasteOntoSurfaceCheckBox->setToolTip("Place the foci onto the surface"); QObject::connect(m_pasteOntoSurfaceCheckBox, SIGNAL(clicked(bool)), this, SLOT(processAttributesChanges())); QLabel* coloringLabel = new QLabel("Coloring"); m_coloringTypeComboBox = new EnumComboBoxTemplate(this); m_coloringTypeComboBox->setup(); m_coloringTypeComboBox->getWidget()->setToolTip("Select the coloring assignment for foci"); QObject::connect(m_coloringTypeComboBox, SIGNAL(itemActivated()), this, SLOT(processAttributesChanges())); QLabel* standardColorLabel = new QLabel("Standard Color"); m_standardColorComboBox = new CaretColorEnumComboBox(this); m_standardColorComboBox->getWidget()->setToolTip("Select the standard color"); QObject::connect(m_standardColorComboBox, SIGNAL(colorSelected(const CaretColorEnum::Enum)), this, SLOT(processAttributesChanges())); std::vector drawingTypeEnums; FociDrawingTypeEnum::getAllEnums(drawingTypeEnums); const int32_t numDrawingTypeEnums = static_cast(drawingTypeEnums.size()); QLabel* drawAsLabel = new QLabel("Draw As"); m_drawTypeComboBox = new QComboBox(); for (int32_t i = 0; i < numDrawingTypeEnums; i++) { FociDrawingTypeEnum::Enum drawType = drawingTypeEnums[i]; m_drawTypeComboBox->addItem(FociDrawingTypeEnum::toGuiName(drawType), (int)drawType); } m_drawTypeComboBox->setToolTip("Select the drawing style of foci"); QObject::connect(m_drawTypeComboBox, SIGNAL(activated(int)), this, SLOT(processAttributesChanges())); float minLineWidth = 0; float maxLineWidth = 1000; //BrainOpenGL::getMinMaxLineWidth(minLineWidth, // maxLineWidth); QLabel* pointSizeLabel = new QLabel("Symbol Diameter"); m_sizeSpinBox = WuQFactory::newDoubleSpinBox(); m_sizeSpinBox->setFixedWidth(80); m_sizeSpinBox->setRange(minLineWidth, maxLineWidth); m_sizeSpinBox->setSingleStep(1.0); m_sizeSpinBox->setDecimals(1); m_sizeSpinBox->setToolTip("Adjust the diameter of foci"); m_sizeSpinBox->setSuffix("mm"); QObject::connect(m_sizeSpinBox, SIGNAL(valueChanged(double)), this, SLOT(processAttributesChanges())); QWidget* gridWidget = new QWidget(); QGridLayout* gridLayout = new QGridLayout(gridWidget); WuQtUtilities::setLayoutSpacingAndMargins(gridLayout, 8, 2); int row = gridLayout->rowCount(); gridLayout->addWidget(m_fociContralateralCheckBox, row, 0, 1, 2); row++; gridLayout->addWidget(m_pasteOntoSurfaceCheckBox, row, 0, 1, 2); row++; gridLayout->addWidget(WuQtUtilities::createHorizontalLineWidget(), row, 0, 1, 2); row++; gridLayout->addWidget(coloringLabel, row, 0); gridLayout->addWidget(m_coloringTypeComboBox->getWidget(), row, 1); row++; gridLayout->addWidget(standardColorLabel, row, 0); gridLayout->addWidget(m_standardColorComboBox->getWidget(), row, 1); row++; gridLayout->addWidget(drawAsLabel, row, 0); gridLayout->addWidget(m_drawTypeComboBox , row, 1); row++; gridLayout->addWidget(pointSizeLabel, row, 0); gridLayout->addWidget(m_sizeSpinBox, row, 1); gridWidget->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); QWidget* widget = new QWidget(); QVBoxLayout* layout = new QVBoxLayout(widget); layout->addWidget(gridWidget); layout->addStretch(); return widget; } /** * Called when a widget on the attributes page has * its value changed. */ void FociSelectionViewController::processAttributesChanges() { DisplayPropertiesFoci* dpf = GuiManager::get()->getBrain()->getDisplayPropertiesFoci(); const FeatureColoringTypeEnum::Enum selectedColoringType = m_coloringTypeComboBox->getSelectedItem(); const int selectedDrawTypeIndex = m_drawTypeComboBox->currentIndex(); const int drawTypeInteger = m_drawTypeComboBox->itemData(selectedDrawTypeIndex).toInt(); const FociDrawingTypeEnum::Enum selectedDrawingType = static_cast(drawTypeInteger); const CaretColorEnum::Enum standardColorType = m_standardColorComboBox->getSelectedColor(); BrowserTabContent* browserTabContent = GuiManager::get()->getBrowserTabContentForBrowserWindow(m_browserWindowIndex, true); if (browserTabContent == NULL) { return; } const int32_t browserTabIndex = browserTabContent->getTabNumber(); const DisplayGroupEnum::Enum displayGroup = dpf->getDisplayGroupForTab(browserTabIndex); dpf->setDisplayed(displayGroup, browserTabIndex, m_fociDisplayCheckBox->isChecked()); dpf->setContralateralDisplayed(displayGroup, browserTabIndex, m_fociContralateralCheckBox->isChecked()); dpf->setPasteOntoSurface(displayGroup, browserTabIndex, m_pasteOntoSurfaceCheckBox->isChecked()); dpf->setColoringType(displayGroup, browserTabIndex, selectedColoringType); dpf->setStandardColorType(displayGroup, browserTabIndex, standardColorType); dpf->setFociSize(displayGroup, browserTabIndex, m_sizeSpinBox->value()); dpf->setDrawingType(displayGroup, browserTabIndex, selectedDrawingType); EventManager::get()->sendEvent(EventGraphicsUpdateAllWindows().getPointer()); updateOtherFociViewControllers(); } /** * Called when the foci display group combo box is changed. */ void FociSelectionViewController::fociDisplayGroupSelected(const DisplayGroupEnum::Enum displayGroup) { /* * Update selected display group in model. */ BrowserTabContent* browserTabContent = GuiManager::get()->getBrowserTabContentForBrowserWindow(m_browserWindowIndex, false); if (browserTabContent == NULL) { return; } const int32_t browserTabIndex = browserTabContent->getTabNumber(); Brain* brain = GuiManager::get()->getBrain(); DisplayPropertiesFoci* dpf = brain->getDisplayPropertiesFoci(); dpf->setDisplayGroupForTab(browserTabIndex, displayGroup); /* * Since display group has changed, need to update controls */ updateFociViewController(); /* * Apply the changes. */ processFociSelectionChanges(); } /** * Update the foci widget. */ void FociSelectionViewController::updateFociViewController() { BrowserTabContent* browserTabContent = GuiManager::get()->getBrowserTabContentForBrowserWindow(m_browserWindowIndex, true); if (browserTabContent == NULL) { return; } const int32_t browserTabIndex = browserTabContent->getTabNumber(); Brain* brain = GuiManager::get()->getBrain(); DisplayPropertiesFoci* dpf = brain->getDisplayPropertiesFoci(); const DisplayGroupEnum::Enum displayGroup = dpf->getDisplayGroupForTab(browserTabIndex); // dpf->setDisplayGroupForTab(browserTabIndex, // m_fociDisplayGroupComboBox->getSelectedDisplayGroup()); setWindowTitle("Foci"); m_fociDisplayGroupComboBox->setSelectedDisplayGroup(dpf->getDisplayGroupForTab(browserTabIndex)); /*; * Get all of foci files. */ std::vector allFociFiles; const int32_t numberOfFociFiles = brain->getNumberOfFociFiles(); for (int32_t iff = 0; iff < numberOfFociFiles; iff++) { allFociFiles.push_back(brain->getFociFile(iff)); } /* * Update the class/name hierarchy */ m_fociClassNameHierarchyViewController->updateContents(allFociFiles, displayGroup); std::vector drawingTypeEnums; FociDrawingTypeEnum::getAllEnums(drawingTypeEnums); const int32_t numDrawingTypeEnums = static_cast(drawingTypeEnums.size()); m_fociDisplayCheckBox->setChecked(dpf->isDisplayed(displayGroup, browserTabIndex)); m_fociContralateralCheckBox->setChecked(dpf->isContralateralDisplayed(displayGroup, browserTabIndex)); m_pasteOntoSurfaceCheckBox->setChecked(dpf->isPasteOntoSurface(displayGroup, browserTabIndex)); m_coloringTypeComboBox->setSelectedItem(dpf->getColoringType(displayGroup, browserTabIndex)); m_standardColorComboBox->setSelectedColor(dpf->getStandardColorType(displayGroup, browserTabIndex)); const FociDrawingTypeEnum::Enum selectedDrawingType = dpf->getDrawingType(displayGroup, browserTabIndex); int32_t selectedDrawingTypeIndex = 0; for (int32_t i = 0; i < numDrawingTypeEnums; i++) { FociDrawingTypeEnum::Enum drawType = drawingTypeEnums[i]; if (drawType == selectedDrawingType) { selectedDrawingTypeIndex = i; } } m_drawTypeComboBox->setCurrentIndex(selectedDrawingTypeIndex); m_sizeSpinBox->blockSignals(true); m_sizeSpinBox->setValue(dpf->getFociSize(displayGroup, browserTabIndex)); m_sizeSpinBox->blockSignals(false); } /** * Update other foci view controllers. */ void FociSelectionViewController::updateOtherFociViewControllers() { for (std::set::iterator iter = FociSelectionViewController::allFociSelectionViewControllers.begin(); iter != FociSelectionViewController::allFociSelectionViewControllers.end(); iter++) { FociSelectionViewController* bsw = *iter; if (bsw != this) { bsw->updateFociViewController(); } } } /** * Gets called when foci selections are changed. */ void FociSelectionViewController::processFociSelectionChanges() { processSelectionChanges(); } /** * Issue update events after selections are changed. */ void FociSelectionViewController::processSelectionChanges() { updateOtherFociViewControllers(); EventManager::get()->sendEvent(EventGraphicsUpdateAllWindows().getPointer()); } /** * Receive events from the event manager. * * @param event * Event sent by event manager. */ void FociSelectionViewController::receiveEvent(Event* event) { bool doUpdate = false; if (event->getEventType() == EventTypeEnum::EVENT_USER_INTERFACE_UPDATE) { EventUserInterfaceUpdate* uiEvent = dynamic_cast(event); CaretAssert(uiEvent); if (uiEvent->isUpdateForWindow(m_browserWindowIndex)) { if (uiEvent->isFociUpdate() || uiEvent->isToolBoxUpdate()) { doUpdate = true; uiEvent->setEventProcessed(); } } } if (doUpdate) { updateFociViewController(); } } /** * Create a scene for an instance of a class. * * @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. * * @return Pointer to SceneClass object representing the state of * this object. Under some circumstances a NULL pointer may be * returned. Caller will take ownership of returned object. */ SceneClass* FociSelectionViewController::saveToScene(const SceneAttributes* sceneAttributes, const AString& instanceName) { SceneClass* sceneClass = new SceneClass(instanceName, "FociSelectionViewController", 1); sceneClass->addClass(m_tabWidget->saveToScene(sceneAttributes, "m_tabWidget")); return sceneClass; } /** * Restore the state of an instance of a class. * * @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 containing the state that was previously * saved and should be restored. */ void FociSelectionViewController::restoreFromScene(const SceneAttributes* sceneAttributes, const SceneClass* sceneClass) { if (sceneClass == NULL) { return; } m_tabWidget->restoreFromScene(sceneAttributes, sceneClass->getClass("m_tabWidget")); } connectome-workbench-1.2.3+git41-gc4c6c90/src/GuiQt/FociSelectionViewController.h000066400000000000000000000072271300200146000274030ustar00rootroot00000000000000#ifndef __FOCI_SELECTION_VIEW_CONTROLLER__H_ #define __FOCI_SELECTION_VIEW_CONTROLLER__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 "DisplayGroupEnum.h" #include "EventListenerInterface.h" #include "SceneableInterface.h" class QCheckBox; class QComboBox; class QDoubleSpinBox; namespace caret { class CaretColorEnumComboBox; class GroupAndNameHierarchyViewController; class DisplayGroupEnumComboBox; class EnumComboBoxTemplate; class WuQTabWidget; class FociSelectionViewController : public QWidget, public EventListenerInterface, public SceneableInterface { Q_OBJECT public: FociSelectionViewController(const int32_t browserWindowIndex, QWidget* parent = 0); virtual ~FociSelectionViewController(); void receiveEvent(Event* event); virtual SceneClass* saveToScene(const SceneAttributes* sceneAttributes, const AString& instanceName); virtual void restoreFromScene(const SceneAttributes* sceneAttributes, const SceneClass* sceneClass); private slots: void processFociSelectionChanges(); void processSelectionChanges(); void fociDisplayGroupSelected(const DisplayGroupEnum::Enum); void processAttributesChanges(); private: FociSelectionViewController(const FociSelectionViewController&); FociSelectionViewController& operator=(const FociSelectionViewController&); void updateFociViewController(); void updateOtherFociViewControllers(); QWidget* createSelectionWidget(); QWidget* createAttributesWidget(); int32_t m_browserWindowIndex; GroupAndNameHierarchyViewController* m_fociClassNameHierarchyViewController; QCheckBox* m_fociDisplayCheckBox; QCheckBox* m_fociContralateralCheckBox; QCheckBox* m_pasteOntoSurfaceCheckBox; DisplayGroupEnumComboBox* m_fociDisplayGroupComboBox; EnumComboBoxTemplate* m_coloringTypeComboBox; CaretColorEnumComboBox* m_standardColorComboBox; QDoubleSpinBox* m_lineWidthSpinBox; QDoubleSpinBox* m_sizeSpinBox; QComboBox* m_drawTypeComboBox; WuQTabWidget* m_tabWidget; static std::set allFociSelectionViewControllers; }; #ifdef __FOCI_SELECTION_VIEW_CONTROLLER_DECLARE__ std::set FociSelectionViewController::allFociSelectionViewControllers; #endif // __FOCI_SELECTION_VIEW_CONTROLLER_DECLARE__ } // namespace #endif //__FOCI_SELECTION_VIEW_CONTROLLER__H_ connectome-workbench-1.2.3+git41-gc4c6c90/src/GuiQt/GapsAndMarginsDialog.cxx000066400000000000000000000732661300200146000263150ustar00rootroot00000000000000 /*LICENSE_START*/ /* * Copyright (C) 2015 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 __GAPS_AND_MARGINS_DIALOG_DECLARE__ #include "GapsAndMarginsDialog.h" #undef __GAPS_AND_MARGINS_DIALOG_DECLARE__ #include #include #include #include #include #include #include #include #include "Brain.h" #include "BrainBrowserWindow.h" #include "BrainBrowserWindowComboBox.h" #include "BrainConstants.h" #include "BrowserTabContent.h" #include "CaretAssert.h" #include "CaretLogger.h" #include "EventBrowserTabGet.h" #include "EventBrowserTabGetAll.h" #include "EventGetViewportSize.h" #include "EventGraphicsUpdateAllWindows.h" #include "EventManager.h" #include "EventUserInterfaceUpdate.h" #include "GapsAndMargins.h" #include "GuiManager.h" #include "WuQGridLayoutGroup.h" #include "WuQtUtilities.h" using namespace caret; /** * \class caret::GapsAndMarginsDialog * \brief Dialog for adjustment of tab margins. * \ingroup GuiQt */ /** * Constructor. */ GapsAndMarginsDialog::GapsAndMarginsDialog(QWidget* parent) : WuQDialogNonModal("Gaps and Margins", parent) { setApplyButtonText(""); m_browserWindowComboBox = new BrainBrowserWindowComboBox(BrainBrowserWindowComboBox::STYLE_NAME_AND_NUMBER, this); QObject::connect(m_browserWindowComboBox, SIGNAL(browserWindowIndexSelected(const int32_t)), this, SLOT(browserWindowIndexChanged(const int32_t))); QWidget* gapsWidget = createGapsWidget(); QWidget* marginsWidget = createMarginsWidget(); QWidget* dialogWidget = new QWidget; QVBoxLayout* dialogLayout = new QVBoxLayout(dialogWidget); dialogLayout->addWidget(m_browserWindowComboBox->getWidget(), 0, Qt::AlignHCenter); //dialogLayout->addWidget(WuQtUtilities::createHorizontalLineWidget()); dialogLayout->addSpacing(8); dialogLayout->addWidget(gapsWidget, 0, Qt::AlignHCenter); dialogLayout->addWidget(marginsWidget); setCentralWidget(dialogWidget, SCROLL_AREA_AS_NEEDED); updateDialog(); EventManager::get()->addEventListener(this, EventTypeEnum::EVENT_USER_INTERFACE_UPDATE); } /** * Destructor. */ GapsAndMarginsDialog::~GapsAndMarginsDialog() { EventManager::get()->removeAllEventsFromListener(this); } /** * @return Create the gaps widget. */ QWidget* GapsAndMarginsDialog::createGapsWidget() { QLabel* surfaceLabel = new QLabel("Surface"); QLabel* volumeLabel = new QLabel("Volume"); QLabel* horizontalLabel = new QLabel("Horizontal"); QLabel* verticalLabel = new QLabel("Vertical"); m_surfaceMontageHorizontalGapSpinBox = createPercentageSpinBox(); QObject::connect(m_surfaceMontageHorizontalGapSpinBox, SIGNAL(valueChanged(double)), this, SLOT(surfaceMontageGapChanged())); m_surfaceMontageVerticalGapSpinBox = createPercentageSpinBox(); QObject::connect(m_surfaceMontageVerticalGapSpinBox, SIGNAL(valueChanged(double)), this, SLOT(surfaceMontageGapChanged())); m_surfaceMontageMatchPixelToolButton = new QToolButton; m_surfaceMontageMatchPixelToolButton->setText("Match Pixel Width\nto Pixel Height"); m_surfaceMontageMatchPixelToolButton->setToolTip("Set the width to same pixel size as height"); QObject::connect(m_surfaceMontageMatchPixelToolButton, SIGNAL(clicked(bool)), this, SLOT(surfaceMontageMatchPixelButtonClicked())); m_volumeMontageHorizontalGapSpinBox = createPercentageSpinBox(); QObject::connect(m_volumeMontageHorizontalGapSpinBox, SIGNAL(valueChanged(double)), this, SLOT(volumeMontageGapChanged())); m_volumeMontageVerticalGapSpinBox = createPercentageSpinBox(); QObject::connect(m_volumeMontageVerticalGapSpinBox, SIGNAL(valueChanged(double)), this, SLOT(volumeMontageGapChanged())); m_volumeMontageMatchPixelToolButton = new QToolButton; m_volumeMontageMatchPixelToolButton->setText("Match Pixel Width\nto Pixel Height"); m_volumeMontageMatchPixelToolButton->setToolTip("Set the width to same pixel size as height"); QObject::connect(m_volumeMontageMatchPixelToolButton, SIGNAL(clicked(bool)), this, SLOT(volumeMontageMatchPixelButtonClicked())); const int COLUMN_LABEL = 0; const int COLUMN_HORIZONTAL = 1; const int COLUMN_VERTICAL = 2; const int COLUMN_SCALE = 3; QWidget* widget = new QGroupBox("Montage Gaps"); QGridLayout* gridLayout = new QGridLayout(widget); int row = gridLayout->rowCount(); gridLayout->addWidget(horizontalLabel, row, COLUMN_HORIZONTAL, Qt::AlignHCenter); gridLayout->addWidget(verticalLabel, row, COLUMN_VERTICAL, Qt::AlignHCenter); row++; gridLayout->addWidget(surfaceLabel, row, COLUMN_LABEL); gridLayout->addWidget(m_surfaceMontageHorizontalGapSpinBox, row, COLUMN_HORIZONTAL); gridLayout->addWidget(m_surfaceMontageVerticalGapSpinBox, row, COLUMN_VERTICAL); gridLayout->addWidget(m_surfaceMontageMatchPixelToolButton, row, COLUMN_SCALE, Qt::AlignHCenter); row++; gridLayout->addWidget(volumeLabel, row, COLUMN_LABEL); gridLayout->addWidget(m_volumeMontageHorizontalGapSpinBox, row, COLUMN_HORIZONTAL); gridLayout->addWidget(m_volumeMontageVerticalGapSpinBox, row, COLUMN_VERTICAL); gridLayout->addWidget(m_volumeMontageMatchPixelToolButton, row, COLUMN_SCALE, Qt::AlignHCenter); row++; widget->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); return widget; } /** * @return Create the margins widget. */ QWidget* GapsAndMarginsDialog::createMarginsWidget() { m_tabIndexSignalMapper = new QSignalMapper(this); QObject::connect(m_tabIndexSignalMapper, SIGNAL(mapped(int)), this, SLOT(tabMarginChanged(int))); m_tabMarginMatchPixelsToolButtonSignalMapper = new QSignalMapper(this); QObject::connect(m_tabMarginMatchPixelsToolButtonSignalMapper, SIGNAL(mapped(int)), this, SLOT(tabMarginMatchPixelButtonClicked(int))); QLabel* tabLabel = new QLabel("Tab"); QLabel* leftLabel = new QLabel("Left"); QLabel* rightLabel = new QLabel("Right"); QLabel* topLabel = new QLabel("Top"); QLabel* bottomLabel = new QLabel("Bottom"); m_applyFirstTabToAllToolButton = new QToolButton(); m_applyFirstTabToAllToolButton->setText("Apply First Row to All"); QObject::connect(m_applyFirstTabToAllToolButton, SIGNAL(clicked(bool)), this, SLOT(applyFirstTabToAllButtonClicked())); QWidget* tabsWidget = new QWidget(); QGridLayout* gridLayout = new QGridLayout(tabsWidget); int32_t columnCounter = 0; const int32_t COLUMN_LABEL = columnCounter++; const int32_t COLUMN_LEFT = columnCounter++; const int32_t COLUMN_RIGHT = columnCounter++; const int32_t COLUMN_BOTTOM = columnCounter++; const int32_t COLUMN_TOP = columnCounter++; const int32_t COLUMN_MATCH = columnCounter++; const int32_t applyToAllRow = gridLayout->rowCount(); gridLayout->addWidget(m_applyFirstTabToAllToolButton, applyToAllRow, COLUMN_LABEL, 1, COLUMN_MATCH, Qt::AlignHCenter); const int32_t titlesRow = gridLayout->rowCount(); gridLayout->addWidget(tabLabel, titlesRow, COLUMN_LABEL, Qt::AlignLeft); gridLayout->addWidget(leftLabel, titlesRow, COLUMN_LEFT, Qt::AlignHCenter); gridLayout->addWidget(rightLabel, titlesRow, COLUMN_RIGHT, Qt::AlignHCenter); gridLayout->addWidget(bottomLabel, titlesRow, COLUMN_BOTTOM, Qt::AlignHCenter); gridLayout->addWidget(topLabel, titlesRow, COLUMN_TOP, Qt::AlignHCenter); for (int32_t iTab = 0; iTab < BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS; iTab++) { QLabel* tabLabel = new QLabel("123456789012345"); m_tabNumberLabels.push_back(tabLabel); QDoubleSpinBox* leftMarginSpinBox = createPercentageSpinBox(); m_leftMarginSpinBoxes.push_back(leftMarginSpinBox); QObject::connect(leftMarginSpinBox, SIGNAL(valueChanged(double)), m_tabIndexSignalMapper, SLOT(map())); m_tabIndexSignalMapper->setMapping(leftMarginSpinBox, iTab); QDoubleSpinBox* rightMarginSpinBox = createPercentageSpinBox(); m_rightMarginSpinBoxes.push_back(rightMarginSpinBox); QObject::connect(rightMarginSpinBox, SIGNAL(valueChanged(double)), m_tabIndexSignalMapper, SLOT(map())); m_tabIndexSignalMapper->setMapping(rightMarginSpinBox, iTab); QDoubleSpinBox* bottomMarginSpinBox = createPercentageSpinBox(); m_bottomMarginSpinBoxes.push_back(bottomMarginSpinBox); QObject::connect(bottomMarginSpinBox, SIGNAL(valueChanged(double)), m_tabIndexSignalMapper, SLOT(map())); m_tabIndexSignalMapper->setMapping(bottomMarginSpinBox, iTab); QDoubleSpinBox* topMarginSpinBox = createPercentageSpinBox(); m_topMarginSpinBoxes.push_back(topMarginSpinBox); QObject::connect(topMarginSpinBox, SIGNAL(valueChanged(double)), m_tabIndexSignalMapper, SLOT(map())); m_tabIndexSignalMapper->setMapping(topMarginSpinBox, iTab); QToolButton* matchPixelsToolButton = new QToolButton; matchPixelsToolButton->setText("Match Pixel\nSize to Top"); matchPixelsToolButton->setToolTip("When clicked, percentages of Left, Right, and\n" "Top are set to match the pixel height of Top"); m_tabMarginMatchPixelToolButtons.push_back(matchPixelsToolButton); QObject::connect(matchPixelsToolButton, SIGNAL(clicked(bool)), m_tabMarginMatchPixelsToolButtonSignalMapper, SLOT(map())); m_tabMarginMatchPixelsToolButtonSignalMapper->setMapping(matchPixelsToolButton, iTab); const int32_t gridRow = gridLayout->rowCount(); gridLayout->addWidget(tabLabel, gridRow, COLUMN_LABEL); //, Qt::AlignRight); gridLayout->addWidget(leftMarginSpinBox, gridRow, COLUMN_LEFT); gridLayout->addWidget(rightMarginSpinBox, gridRow, COLUMN_RIGHT); gridLayout->addWidget(bottomMarginSpinBox, gridRow, COLUMN_BOTTOM); gridLayout->addWidget(topMarginSpinBox, gridRow, COLUMN_TOP); gridLayout->addWidget(matchPixelsToolButton, gridRow, COLUMN_MATCH); } QScrollArea* scrollArea = new QScrollArea(); scrollArea->setWidget(tabsWidget); scrollArea->setWidgetResizable(true); scrollArea->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); scrollArea->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOn); /* * Ensure scroll are width is set so widget is fully visible in the horizontal direction */ const int width = tabsWidget->sizeHint().width() + scrollArea->verticalScrollBar()->sizeHint().width(); scrollArea->setMinimumWidth(width); QWidget* widget = new QGroupBox("Tab Margins"); QVBoxLayout* layout = new QVBoxLayout(widget); layout->addWidget(scrollArea); return widget; } /** * @return Create a double spin box for percentage values. */ QDoubleSpinBox* GapsAndMarginsDialog::createPercentageSpinBox() { const double minValue = 0.0; const double maxValue = 100.0; const int decimals = 1; const double singleStep = 0.1; QDoubleSpinBox* doubleSpinBox = new QDoubleSpinBox(); doubleSpinBox->setMinimum(minValue); doubleSpinBox->setMaximum(maxValue); doubleSpinBox->setSingleStep(singleStep); doubleSpinBox->setDecimals(decimals); doubleSpinBox->setKeyboardTracking(false); doubleSpinBox->setSuffix("%"); return doubleSpinBox; } /** * Update the content of the dialog. */ void GapsAndMarginsDialog::updateDialog() { m_browserWindowComboBox->updateComboBox(); const int32_t windowIndex = m_browserWindowComboBox->getSelectedBrowserWindowIndex(); updateGapsSpinBoxes(windowIndex); updateMarginSpinBoxes(windowIndex); if (windowIndex >= 0) { setEnabled(true); } else { setDisabled(true); } } /** * Update the gaps spin boxes. * * @param windowIndex * Index of window whose gaps are being updated. */ void GapsAndMarginsDialog::updateGapsSpinBoxes(const int32_t windowIndex) { const GapsAndMargins* gapsAndMargins = GuiManager::get()->getBrain()->getGapsAndMargins(); m_surfaceMontageHorizontalGapSpinBox->blockSignals(true); m_surfaceMontageHorizontalGapSpinBox->setValue(gapsAndMargins->getSurfaceMontageHorizontalGapForWindow(windowIndex)); m_surfaceMontageHorizontalGapSpinBox->blockSignals(false); m_surfaceMontageVerticalGapSpinBox->blockSignals(true); m_surfaceMontageVerticalGapSpinBox->setValue(gapsAndMargins->getSurfaceMontageVerticalGapForWindow(windowIndex)); m_surfaceMontageVerticalGapSpinBox->blockSignals(false); m_volumeMontageHorizontalGapSpinBox->blockSignals(true); m_volumeMontageHorizontalGapSpinBox->setValue(gapsAndMargins->getVolumeMontageHorizontalGapForWindow(windowIndex)); m_volumeMontageHorizontalGapSpinBox->blockSignals(false); m_volumeMontageVerticalGapSpinBox->blockSignals(true); m_volumeMontageVerticalGapSpinBox->setValue(gapsAndMargins->getVolumeMontageVerticalGapForWindow(windowIndex)); m_volumeMontageVerticalGapSpinBox->blockSignals(false); } /** * Update the margin spin boxes. * * @param windowIndex * Index of window whose margins are being updated. */ void GapsAndMarginsDialog::updateMarginSpinBoxes(const int32_t windowIndex) { std::vector tabContents; // std::vector tabIndices; const BrainBrowserWindow* browserWindow = GuiManager::get()->getBrowserWindowByWindowIndex(windowIndex); if (browserWindow != NULL) { // browserWindow->getAllTabContentIndices(tabIndices); browserWindow->getAllTabContent(tabContents); } const GapsAndMargins* gapsAndMargins = GuiManager::get()->getBrain()->getGapsAndMargins(); const int32_t numValidTabs = static_cast(tabContents.size()); for (int32_t iTab = 0; iTab < BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS; iTab++) { bool tabValid = false; if (iTab < numValidTabs) { CaretAssertVectorIndex(tabContents, iTab); const BrowserTabContent* browserTab = tabContents[iTab]; CaretAssert(browserTab); const int32_t tabIndex = browserTab->getTabNumber(); CaretAssertArrayIndex(m_tabIndexInTabMarginRow, BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS, iTab); m_tabIndexInTabMarginRow[iTab] = tabIndex; const float leftMargin = gapsAndMargins->getMarginLeftForTab(tabIndex); const float rightMargin = gapsAndMargins->getMarginRightForTab(tabIndex); const float bottomMargin = gapsAndMargins->getMarginBottomForTab(tabIndex); const float topMargin = gapsAndMargins->getMarginTopForTab(tabIndex); CaretAssertVectorIndex(m_tabNumberLabels, iTab); // m_tabNumberLabels[iTab]->setText("Tab " + QString::number(tabIndex + 1)); m_tabNumberLabels[iTab]->setText(browserTab->getName()); CaretAssertVectorIndex(m_leftMarginSpinBoxes, iTab); m_leftMarginSpinBoxes[iTab]->blockSignals(true); m_leftMarginSpinBoxes[iTab]->setValue(leftMargin); m_leftMarginSpinBoxes[iTab]->blockSignals(false); CaretAssertVectorIndex(m_rightMarginSpinBoxes, iTab); m_rightMarginSpinBoxes[iTab]->blockSignals(true); m_rightMarginSpinBoxes[iTab]->setValue(rightMargin); m_rightMarginSpinBoxes[iTab]->blockSignals(false); CaretAssertVectorIndex(m_bottomMarginSpinBoxes, iTab); m_bottomMarginSpinBoxes[iTab]->blockSignals(true); m_bottomMarginSpinBoxes[iTab]->setValue(bottomMargin); m_bottomMarginSpinBoxes[iTab]->blockSignals(false); CaretAssertVectorIndex(m_topMarginSpinBoxes, iTab); m_topMarginSpinBoxes[iTab]->blockSignals(true); m_topMarginSpinBoxes[iTab]->setValue(topMargin); m_topMarginSpinBoxes[iTab]->blockSignals(false); tabValid = true; } else { m_tabIndexInTabMarginRow[iTab] = -1; } CaretAssertVectorIndex(m_tabNumberLabels, iTab); m_tabNumberLabels[iTab]->setVisible(tabValid); CaretAssertVectorIndex(m_leftMarginSpinBoxes, iTab); m_leftMarginSpinBoxes[iTab]->setVisible(tabValid); CaretAssertVectorIndex(m_rightMarginSpinBoxes, iTab); m_rightMarginSpinBoxes[iTab]->setVisible(tabValid); CaretAssertVectorIndex(m_bottomMarginSpinBoxes, iTab); m_bottomMarginSpinBoxes[iTab]->setVisible(tabValid); CaretAssertVectorIndex(m_topMarginSpinBoxes, iTab); m_topMarginSpinBoxes[iTab]->setVisible(tabValid); CaretAssertVectorIndex(m_tabMarginMatchPixelToolButtons, iTab); m_tabMarginMatchPixelToolButtons[iTab]->setVisible(tabValid); } } /** * Find the horizontal percentage that is the equivalent pixel * size as the vertical percentage. * * @param verticalPercentage * Vertical percentage size. * @param viewportWidth * Width of viewport in pixels. * @param viewportHeight * Height of viewport in pixels. * @return * The horizontal percentage. */ float GapsAndMarginsDialog::matchHorizontalPercentageFromVerticalPercentage(const float verticalPercentage, const float viewportWidth, const float viewportHeight) const { float horizontalPercentageOut = 0.0; const float marginPixelSize = verticalPercentage * viewportHeight; if (viewportWidth > 0) { horizontalPercentageOut = marginPixelSize / viewportWidth; } return horizontalPercentageOut; } /** * Called when match pixel button is clicked. * * @param setVisible(tabValid); * Index of row that is clicked */ void GapsAndMarginsDialog::tabMarginMatchPixelButtonClicked(int rowIndex) { CaretAssertArrayIndex(m_tabIndexInTabMarginRow, BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS, rowIndex); const int32_t tabIndex = m_tabIndexInTabMarginRow[rowIndex]; if (tabIndex >= 0) { EventGetViewportSize viewportSizeEvent(EventGetViewportSize::MODE_TAB_BEFORE_MARGINS_INDEX, tabIndex); EventManager::get()->sendEvent(viewportSizeEvent.getPointer()); if (viewportSizeEvent.isViewportSizeValid()) { int32_t viewport[4]; viewportSizeEvent.getViewportSize(viewport); CaretAssertVectorIndex(m_topMarginSpinBoxes, rowIndex); const float bottomTopPercentage = m_topMarginSpinBoxes[rowIndex]->value(); const float leftRightMarginPercentage = matchHorizontalPercentageFromVerticalPercentage(bottomTopPercentage, viewport[2], viewport[3]); CaretAssertVectorIndex(m_leftMarginSpinBoxes, rowIndex); CaretAssertVectorIndex(m_rightMarginSpinBoxes, rowIndex); CaretAssertVectorIndex(m_bottomMarginSpinBoxes, rowIndex); CaretAssertVectorIndex(m_topMarginSpinBoxes, rowIndex); m_leftMarginSpinBoxes[rowIndex]->blockSignals(true); m_leftMarginSpinBoxes[rowIndex]->setValue(leftRightMarginPercentage); m_leftMarginSpinBoxes[rowIndex]->blockSignals(false); m_rightMarginSpinBoxes[rowIndex]->blockSignals(true); m_rightMarginSpinBoxes[rowIndex]->setValue(leftRightMarginPercentage); m_rightMarginSpinBoxes[rowIndex]->blockSignals(false); m_bottomMarginSpinBoxes[rowIndex]->blockSignals(true); m_bottomMarginSpinBoxes[rowIndex]->setValue(bottomTopPercentage); m_bottomMarginSpinBoxes[rowIndex]->blockSignals(false); m_topMarginSpinBoxes[rowIndex]->blockSignals(true); m_topMarginSpinBoxes[rowIndex]->setValue(bottomTopPercentage); m_topMarginSpinBoxes[rowIndex]->blockSignals(false); tabMarginChanged(rowIndex); } else { CaretLogWarning("Unable to get tab " + AString::number(tabIndex + 1) + " viewport size."); } } } /** * Receive an event. * * @param event * An event for which this instance is listening. */ void GapsAndMarginsDialog::receiveEvent(Event* event) { if (event->getEventType() == EventTypeEnum::EVENT_USER_INTERFACE_UPDATE) { EventUserInterfaceUpdate* eventUpdate = dynamic_cast(event); CaretAssert(eventUpdate); updateDialog(); eventUpdate->setEventProcessed(); } } /** * Gets called when the browser window index is changed. * * @param browserWindowIndex * Index of the browser window. */ void GapsAndMarginsDialog::browserWindowIndexChanged(const int32_t /*browserWindowIndex*/) { updateDialog(); } /** * Gets called when a tab's margin is changed. * * @param rowIndex * Index of the tab. */ void GapsAndMarginsDialog::tabMarginChanged(int rowIndex) { GapsAndMargins* gapsAndMargins = GuiManager::get()->getBrain()->getGapsAndMargins(); CaretAssertArrayIndex(m_tabIndexInTabMarginRow, BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS, rowIndex); const int32_t tabIndex = m_tabIndexInTabMarginRow[rowIndex]; CaretAssert(tabIndex >= 0); CaretAssertVectorIndex(m_leftMarginSpinBoxes, rowIndex); CaretAssertVectorIndex(m_rightMarginSpinBoxes, rowIndex); CaretAssertVectorIndex(m_bottomMarginSpinBoxes, rowIndex); CaretAssertVectorIndex(m_topMarginSpinBoxes, rowIndex); gapsAndMargins->setMarginLeftForTab(tabIndex, m_leftMarginSpinBoxes[rowIndex]->value()); gapsAndMargins->setMarginRightForTab(tabIndex, m_rightMarginSpinBoxes[rowIndex]->value()); gapsAndMargins->setMarginBottomForTab(tabIndex, m_bottomMarginSpinBoxes[rowIndex]->value()); gapsAndMargins->setMarginTopForTab(tabIndex, m_topMarginSpinBoxes[rowIndex]->value()); EventManager::get()->sendEvent(EventGraphicsUpdateAllWindows().getPointer()); } /** * Gets called when apply first tab to all button is clicked. */ void GapsAndMarginsDialog::applyFirstTabToAllButtonClicked() { const int32_t rowZero = 0; CaretAssertVectorIndex(m_leftMarginSpinBoxes, rowZero); const float leftMargin = m_leftMarginSpinBoxes[rowZero]->value(); CaretAssertVectorIndex(m_rightMarginSpinBoxes, rowZero); const float rightMargin = m_rightMarginSpinBoxes[rowZero]->value(); CaretAssertVectorIndex(m_bottomMarginSpinBoxes, rowZero); const float bottomMargin = m_bottomMarginSpinBoxes[rowZero]->value(); CaretAssertVectorIndex(m_topMarginSpinBoxes, rowZero); const float topMargin = m_topMarginSpinBoxes[rowZero]->value(); GapsAndMargins* gapsAndMargins = GuiManager::get()->getBrain()->getGapsAndMargins(); for (int32_t iRow = 1; iRow < BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS; iRow++) { CaretAssertArrayIndex(m_tabIndexInTabMarginRow, BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS, iRow); const int32_t tabIndex = m_tabIndexInTabMarginRow[iRow]; if (tabIndex >= 0) { gapsAndMargins->setMarginLeftForTab(tabIndex, leftMargin); gapsAndMargins->setMarginRightForTab(tabIndex, rightMargin); gapsAndMargins->setMarginBottomForTab(tabIndex, bottomMargin); gapsAndMargins->setMarginTopForTab(tabIndex, topMargin); } } /* * Update dialog since "select all" will change all margins to the first margin value */ updateDialog(); EventManager::get()->sendEvent(EventGraphicsUpdateAllWindows().getPointer()); } /** * Gets called when a surface montage gap is changed. */ void GapsAndMarginsDialog::surfaceMontageGapChanged() { const int32_t windowIndex = m_browserWindowComboBox->getSelectedBrowserWindowIndex(); if (windowIndex < 0) { return; } GapsAndMargins* gapsAndMargins = GuiManager::get()->getBrain()->getGapsAndMargins(); gapsAndMargins->setSurfaceMontageHorizontalGapForWindow(windowIndex, m_surfaceMontageHorizontalGapSpinBox->value()); gapsAndMargins->setSurfaceMontageVerticalGapForWindow(windowIndex, m_surfaceMontageVerticalGapSpinBox->value()); EventManager::get()->sendEvent(EventGraphicsUpdateAllWindows().getPointer()); } /** * Gets called when a volume montage gap is changed. */ void GapsAndMarginsDialog::volumeMontageGapChanged() { const int32_t windowIndex = m_browserWindowComboBox->getSelectedBrowserWindowIndex(); if (windowIndex < 0) { return; } GapsAndMargins* gapsAndMargins = GuiManager::get()->getBrain()->getGapsAndMargins(); gapsAndMargins->setVolumeMontageHorizontalGapForWindow(windowIndex, m_volumeMontageHorizontalGapSpinBox->value()); gapsAndMargins->setVolumeMontageVerticalGapForWindow(windowIndex, m_volumeMontageVerticalGapSpinBox->value()); EventManager::get()->sendEvent(EventGraphicsUpdateAllWindows().getPointer()); } /** * Gets called when the surface montage match pixels tool button is clicked. */ void GapsAndMarginsDialog::surfaceMontageMatchPixelButtonClicked() { const int32_t windowIndex = m_browserWindowComboBox->getSelectedBrowserWindowIndex(); if (windowIndex < 0) { return; } EventGetViewportSize viewportSizeEvent(EventGetViewportSize::MODE_SURFACE_MONTAGE, windowIndex); EventManager::get()->sendEvent(viewportSizeEvent.getPointer()); if (viewportSizeEvent.isViewportSizeValid()) { int32_t viewport[4]; viewportSizeEvent.getViewportSize(viewport); const float verticalPercentage = m_surfaceMontageVerticalGapSpinBox->value(); const float leftRightMarginPercentage = matchHorizontalPercentageFromVerticalPercentage(verticalPercentage, viewport[2], viewport[3]); m_surfaceMontageHorizontalGapSpinBox->blockSignals(true); m_surfaceMontageHorizontalGapSpinBox->setValue(leftRightMarginPercentage); m_surfaceMontageHorizontalGapSpinBox->blockSignals(false); surfaceMontageGapChanged(); } EventManager::get()->sendEvent(EventGraphicsUpdateAllWindows().getPointer()); } /** * Gets called when the volume montage match pixels tool button is clicked. */ void GapsAndMarginsDialog::volumeMontageMatchPixelButtonClicked() { const int32_t windowIndex = m_browserWindowComboBox->getSelectedBrowserWindowIndex(); if (windowIndex < 0) { return; } EventGetViewportSize viewportSizeEvent(EventGetViewportSize::MODE_VOLUME_MONTAGE, windowIndex); EventManager::get()->sendEvent(viewportSizeEvent.getPointer()); if (viewportSizeEvent.isViewportSizeValid()) { int32_t viewport[4]; viewportSizeEvent.getViewportSize(viewport); const float verticalPercentage = m_volumeMontageVerticalGapSpinBox->value(); const float horizontalPercentage = matchHorizontalPercentageFromVerticalPercentage(verticalPercentage, viewport[2], viewport[3]); m_volumeMontageHorizontalGapSpinBox->blockSignals(true); m_volumeMontageHorizontalGapSpinBox->setValue(horizontalPercentage); m_volumeMontageHorizontalGapSpinBox->blockSignals(false); volumeMontageGapChanged(); } EventManager::get()->sendEvent(EventGraphicsUpdateAllWindows().getPointer()); } connectome-workbench-1.2.3+git41-gc4c6c90/src/GuiQt/GapsAndMarginsDialog.h000066400000000000000000000102611300200146000257240ustar00rootroot00000000000000#ifndef __GAPS_AND_MARGINS_DIALOG_H__ #define __GAPS_AND_MARGINS_DIALOG_H__ /*LICENSE_START*/ /* * Copyright (C) 2015 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 "EventListenerInterface.h" #include "WuQDialogNonModal.h" class QLabel; class QSignalMapper; class QDoubleSpinBox; class QToolButton; namespace caret { class BrainBrowserWindowComboBox; class WuQGridLayoutGroup; class GapsAndMarginsDialog : public WuQDialogNonModal, public EventListenerInterface { Q_OBJECT public: GapsAndMarginsDialog(QWidget* parent = 0); virtual ~GapsAndMarginsDialog(); virtual void updateDialog(); // ADD_NEW_METHODS_HERE virtual void receiveEvent(Event* event); private slots: void browserWindowIndexChanged(const int32_t browserWindowIndex); void tabMarginChanged(int rowIndex); void surfaceMontageGapChanged(); void volumeMontageGapChanged(); void applyFirstTabToAllButtonClicked(); void surfaceMontageMatchPixelButtonClicked(); void volumeMontageMatchPixelButtonClicked(); void tabMarginMatchPixelButtonClicked(int rowIndex); private: GapsAndMarginsDialog(const GapsAndMarginsDialog&); GapsAndMarginsDialog& operator=(const GapsAndMarginsDialog&); QWidget* createGapsWidget(); QWidget* createMarginsWidget(); QDoubleSpinBox* createPercentageSpinBox(); void updateGapsSpinBoxes(const int32_t windowIndex); void updateMarginSpinBoxes(const int32_t windowIndex); float matchHorizontalPercentageFromVerticalPercentage(const float verticalPercentage, const float viewportWidth, const float viewportHeight) const; BrainBrowserWindowComboBox* m_browserWindowComboBox; WuQGridLayoutGroup* m_gridLayoutGroup; std::vector m_tabNumberLabels; std::vector m_leftMarginSpinBoxes; std::vector m_rightMarginSpinBoxes; std::vector m_bottomMarginSpinBoxes; std::vector m_topMarginSpinBoxes; QToolButton* m_applyFirstTabToAllToolButton; QDoubleSpinBox* m_surfaceMontageHorizontalGapSpinBox; QDoubleSpinBox* m_surfaceMontageVerticalGapSpinBox; QDoubleSpinBox* m_volumeMontageHorizontalGapSpinBox; QDoubleSpinBox* m_volumeMontageVerticalGapSpinBox; QToolButton* m_surfaceMontageMatchPixelToolButton; QToolButton* m_volumeMontageMatchPixelToolButton; std::vector m_tabMarginMatchPixelToolButtons; QSignalMapper* m_tabIndexSignalMapper; QSignalMapper* m_tabMarginMatchPixelsToolButtonSignalMapper; int32_t m_tabIndexInTabMarginRow[BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS]; // ADD_NEW_MEMBERS_HERE }; #ifdef __GAPS_AND_MARGINS_DIALOG_DECLARE__ // #endif // __GAPS_AND_MARGINS_DIALOG_DECLARE__ } // namespace #endif //__GAPS_AND_MARGINS_DIALOG_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/GuiQt/GiftiLabelTableEditor.cxx000066400000000000000000001014661300200146000264520ustar00rootroot00000000000000 /*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 __GIFTI_LABEL_TABLE_EDITOR_DECLARE__ #include "GiftiLabelTableEditor.h" #undef __GIFTI_LABEL_TABLE_EDITOR_DECLARE__ #include #include #include #include #include #include #include #include #include #include #include #include "ApplicationInformation.h" #include "BorderFile.h" #include "Brain.h" #include "CaretAssert.h" #include "CaretMappableDataFile.h" #include "ColorEditorWidget.h" #include "EventGraphicsUpdateAllWindows.h" #include "EventManager.h" #include "EventSurfaceColoringInvalidate.h" #include "EventUserInterfaceUpdate.h" #include "FociFile.h" #include "GiftiLabel.h" #include "GiftiLabelTable.h" #include "GroupAndNameHierarchyItem.h" #include "GuiManager.h" #include "WuQDataEntryDialog.h" #include "WuQMessageBox.h" #include "WuQtUtilities.h" #include "WuQWidgetObjectGroup.h" using namespace caret; /** * \class caret::GiftiLabelTableEditor * \brief Dialog for editing a GIFTI lable table. * \ingroup GuiQt */ /** * Constructor for editing label table in a mappable data file. * * @param fociFile * Foci file whose color table being edited. As colors are edited, * the assigned foci will have their color validity invalidated. * @param giftiLabelTable * Label table being edited. * @param dialogTitle * Title for the dialog. * @param options * Bitwise OR'ed Options values. * @param parent * Parent on which this dialog is displayed. */ GiftiLabelTableEditor::GiftiLabelTableEditor(CaretMappableDataFile* caretMappableDataFile, const int32_t mapIndex, const AString& dialogTitle, const uint32_t options, QWidget* parent) : WuQDialogModal(dialogTitle, parent) { CaretAssert(caretMappableDataFile); CaretAssert(mapIndex >= 0); CaretAssert(mapIndex < caretMappableDataFile->getNumberOfMaps()); initializeDialog(caretMappableDataFile->getMapLabelTable(mapIndex), options); m_caretMappableDataFile = caretMappableDataFile; m_caretMappableDataFileMapIndex = mapIndex; } /** * Constructor for a label table in a foci file. * * @param fociFile * Foci file whose color table being edited. As colors are edited, * the assigned foci will have their color validity invalidated. * @param giftiLabelTable * Label table being edited. * @param dialogTitle * Title for the dialog. * @param options * Bitwise OR'ed Options values. * @param parent * Parent on which this dialog is displayed. */ GiftiLabelTableEditor::GiftiLabelTableEditor(FociFile* fociFile, GiftiLabelTable* giftiLableTable, const AString& dialogTitle, const uint32_t options, QWidget* parent) : WuQDialogModal(dialogTitle, parent) { CaretAssert(fociFile); initializeDialog(giftiLableTable, options); m_fociFile = fociFile; } /** * Constructor for a label table in a border file. * * @param borderFile * Border file whose color table being edited. As colors are edited, * the assigned borders will have their color validity invalidated. * @param giftiLabelTable * Label table being edited. * @param dialogTitle * Title for the dialog. * @param options * Bitwise OR'ed Options values. * @param parent * Parent on which this dialog is displayed. */ GiftiLabelTableEditor::GiftiLabelTableEditor(BorderFile* borderFile, GiftiLabelTable* giftiLableTable, const AString& dialogTitle, const uint32_t options, QWidget* parent) : WuQDialogModal(dialogTitle, parent) { CaretAssert(borderFile); initializeDialog(giftiLableTable, options); m_borderFile = borderFile; } /** * Destructor. */ GiftiLabelTableEditor::~GiftiLabelTableEditor() { if (m_undoGiftiLabel != NULL) { delete m_undoGiftiLabel; m_undoGiftiLabel = NULL; } } /** * Initialize the dialog. * * @param giftiLabelTable * Label table being edited. * @param options * Bitwise OR'ed Options values. */ void GiftiLabelTableEditor::initializeDialog(GiftiLabelTable* giftiLabelTable, const uint32_t options) { m_borderFile = NULL; m_caretMappableDataFile = NULL; m_caretMappableDataFileMapIndex = -1; m_fociFile = NULL; m_showUnassignedLabelInEditor = true; if (options & OPTION_UNASSIGNED_LABEL_HIDDEN) { m_showUnassignedLabelInEditor = false; } CaretAssert(giftiLabelTable); m_giftiLableTable = giftiLabelTable; m_undoGiftiLabel = NULL; /* * Sorting */ QLabel* sortByLabel = new QLabel("Sort by "); m_sortLabelsByComboBox = new QComboBox(); m_sortLabelsByComboBox->addItem(s_SORT_COMBO_BOX_NAME_BY_KEY); m_sortLabelsByComboBox->addItem(s_SORT_COMBO_BOX_NAME_BY_NAME); m_sortLabelsByComboBox->setCurrentIndex(1); QObject::connect(m_sortLabelsByComboBox, SIGNAL(activated(int)), this, SLOT(sortingLabelsActivated())); /* * Sorting layout */ QHBoxLayout* sortingLayout = new QHBoxLayout(); sortingLayout->addWidget(sortByLabel); sortingLayout->addWidget(m_sortLabelsByComboBox); sortingLayout->addStretch(); /* * List widget for editing labels. */ m_labelSelectionListWidget = new QListWidget(); m_labelSelectionListWidget->setSelectionMode(QListWidget::SingleSelection); // QObject::connect(m_labelSelectionListWidget, SIGNAL(currentRowChanged(int)), // this, SLOT(listWidgetLabelSelected())); QObject::connect(m_labelSelectionListWidget, SIGNAL(itemClicked(QListWidgetItem*)), //SIGNAL(currentRowChanged(int)), this, SLOT(listWidgetLabelSelected())); /* * New color button. */ QPushButton* newPushButton = WuQtUtilities::createPushButton("New", "Create a new entry", this, SLOT(newButtonClicked())); /* * Undo Edit button. */ QPushButton* undoPushButton = WuQtUtilities::createPushButton("Undo Edit", "Create a new entry", this, SLOT(undoButtonClicked())); /* * Delete button. */ QPushButton* deletePushButton = WuQtUtilities::createPushButton("Delete", "Delete the selected entry", this, SLOT(deleteButtonClicked())); /* * Color editor widget */ m_colorEditorWidget = new ColorEditorWidget(); QObject::connect(m_colorEditorWidget, SIGNAL(colorChanged(const float*)), this, SLOT(colorEditorColorChanged(const float*))); /* * Label name line edit */ QLabel* nameLabel = new QLabel("Name "); m_labelNameLineEdit = new QLineEdit(); QObject::connect(m_labelNameLineEdit, SIGNAL(textEdited(const QString&)), this, SLOT(labelNameLineEditTextEdited(const QString&))); WuQtUtilities::setToolTipAndStatusTip(m_labelNameLineEdit, "Edit the name"); /* * Key value editing */ QLabel* keyLabel = NULL; m_keyValueLineEdit = NULL; m_changeKeyValueToolButton = NULL; if (options & OPTION_ADD_KEY_EDITING) { keyLabel = new QLabel("Key "); m_keyValueLineEdit = new QLineEdit(); m_keyValueLineEdit->setFixedWidth(100); m_keyValueLineEdit->setAlignment(Qt::AlignRight); m_keyValueLineEdit->setReadOnly(true); m_changeKeyValueToolButton = new QToolButton(); m_changeKeyValueToolButton->setText("Change..."); m_changeKeyValueToolButton->setToolTip("Change a label's key"); QObject::connect(m_changeKeyValueToolButton, SIGNAL(clicked(bool)), this, SLOT(changeLabelKeyLockButtonClicked())); } /* * Layout for name and key */ QGridLayout* nameKeyLayout = new QGridLayout(); nameKeyLayout->setColumnStretch(0, 0); nameKeyLayout->setColumnStretch(1, 0); nameKeyLayout->setColumnStretch(2, 100); nameKeyLayout->addWidget(nameLabel, 0, 0); nameKeyLayout->addWidget(m_labelNameLineEdit, 0, 1, 1, 2); if (options & OPTION_ADD_KEY_EDITING) { CaretAssert(keyLabel); CaretAssert(m_keyValueLineEdit); CaretAssert(m_changeKeyValueToolButton); nameKeyLayout->addWidget(keyLabel, 1, 0); nameKeyLayout->addWidget(m_keyValueLineEdit, 1, 1); nameKeyLayout->addWidget(m_changeKeyValueToolButton, 1, 2, Qt::AlignLeft); } /* * Layout for buttons */ QHBoxLayout* buttonsLayout = new QHBoxLayout(); buttonsLayout->addWidget(newPushButton); buttonsLayout->addStretch(); buttonsLayout->addWidget(undoPushButton); buttonsLayout->addStretch(); buttonsLayout->addWidget(deletePushButton); /* * Layout items in dialog */ QWidget* widget = new QWidget(); QVBoxLayout* layout = new QVBoxLayout(widget); WuQtUtilities::setLayoutSpacingAndMargins(layout, 4, 2); layout->addLayout(sortingLayout); layout->addWidget(m_labelSelectionListWidget); layout->addLayout(buttonsLayout); layout->addWidget(WuQtUtilities::createHorizontalLineWidget()); layout->addLayout(nameKeyLayout); layout->addWidget(m_colorEditorWidget); setCentralWidget(widget, WuQDialog::SCROLL_AREA_NEVER); m_editingGroup = new WuQWidgetObjectGroup(this); m_editingGroup->add(undoPushButton); m_editingGroup->add(deletePushButton); m_editingGroup->add(nameLabel); m_editingGroup->add(m_labelNameLineEdit); if (options & OPTION_ADD_KEY_EDITING) { CaretAssert(keyLabel); CaretAssert(m_keyValueLineEdit); CaretAssert(m_changeKeyValueToolButton); m_editingGroup->add(keyLabel); m_editingGroup->add(m_keyValueLineEdit); m_editingGroup->add(m_changeKeyValueToolButton); } m_editingGroup->add(m_colorEditorWidget); loadLabels("", false); m_applyPushButton = NULL; if (options & OPTION_ADD_APPLY_BUTTON) { m_applyPushButton = addUserPushButton("Apply", QDialogButtonBox::ApplyRole); } //setOkButtonText("Close"); //setCancelButtonText(""); /* * No auto default button processing (Qt highlights button) */ disableAutoDefaultForAllPushButtons(); std::map::iterator lastEditIter = s_previousSelections.find(m_giftiLableTable); if (lastEditIter != s_previousSelections.end()) { const PreviousSelections& ps = lastEditIter->second; const int sortIndex = m_sortLabelsByComboBox->findText(ps.m_sortingName); if (sortIndex >= 0) { m_sortLabelsByComboBox->setCurrentIndex(sortIndex); } sortingLabelsActivated(); selectLabelWithName(ps.m_selectedLabelName); } } /** * Called when name line edit text is changed. * @param text * Text currently in the line edit. */ void GiftiLabelTableEditor::labelNameLineEditTextEdited(const QString& text) { GiftiLabel* gl = getSelectedLabel(); if (gl != NULL) { gl->setName(text); } QListWidgetItem* selectedItem = m_labelSelectionListWidget->currentItem(); if (selectedItem != NULL) { selectedItem->setText(gl->getNameAndKeyForLabelEditor()); } m_lastSelectedLabelName = text; } /** * @return * The last name that was selected. */ AString GiftiLabelTableEditor::getLastSelectedLabelName() const { return m_lastSelectedLabelName; } /** * Select the label with the given name. * @param labelName * Name of label that is to be selected. */ void GiftiLabelTableEditor::selectLabelWithName(const AString& labelName) { const GiftiLabel* gl = m_giftiLableTable->getLabel(labelName); if (gl != NULL) { const AString keyLabelName = gl->getNameAndKeyForLabelEditor(); QList itemsWithLabelName = m_labelSelectionListWidget->findItems(keyLabelName, Qt::MatchExactly); if ( ! itemsWithLabelName.empty()) { QListWidgetItem* item = itemsWithLabelName.at(0); m_labelSelectionListWidget->setCurrentItem(item); listWidgetLabelSelected(); } } } ///** // * Called when a label in the list widget is selected. // * @param row // * Row of label selected. // */ //void //GiftiLabelTableEditor::listWidgetLabelSelected(int /*row*/) //{ // if (m_undoGiftiLabel != NULL) { // delete m_undoGiftiLabel; // m_undoGiftiLabel = NULL; // } // // bool isEditingAllowed = false; // GiftiLabel* gl = getSelectedLabel(); // if (gl != NULL) { // const bool isUnassignedLabel = (gl->getKey() == m_giftiLableTable->getUnassignedLabelKey()); // float rgba[4]; // gl->getColor(rgba); // m_colorEditorWidget->setColor(rgba); // m_labelNameLineEdit->setText(gl->getName()); // if (m_keyValueLabel != NULL) { // m_keyValueLabel->setNum(gl->getKey()); // } // // m_lastSelectedLabelName = gl->getName(); // // if (isUnassignedLabel) { // m_undoGiftiLabel = NULL; // } // else { // m_undoGiftiLabel = new GiftiLabel(*gl); // isEditingAllowed = true; // } // } // else { // m_lastSelectedLabelName = ""; // if (m_keyValueLabel != NULL) { // m_keyValueLabel->setText(""); // } // // } // // m_editingGroup->setEnabled(isEditingAllowed); //} /** * Called when a label in the list widget is selected. */ void GiftiLabelTableEditor::listWidgetLabelSelected() { if (m_undoGiftiLabel != NULL) { delete m_undoGiftiLabel; m_undoGiftiLabel = NULL; } bool isEditingAllowed = false; GiftiLabel* gl = getSelectedLabel(); if (gl != NULL) { const bool isUnassignedLabel = (gl->getKey() == m_giftiLableTable->getUnassignedLabelKey()); float rgba[4]; gl->getColor(rgba); m_colorEditorWidget->setColor(rgba); m_labelNameLineEdit->setText(gl->getName()); if (m_keyValueLineEdit != NULL) { m_keyValueLineEdit->setText(AString::number(gl->getKey())); } m_lastSelectedLabelName = gl->getName(); if (isUnassignedLabel) { m_undoGiftiLabel = NULL; } else { m_undoGiftiLabel = new GiftiLabel(*gl); isEditingAllowed = true; } } else { m_lastSelectedLabelName = ""; if (m_keyValueLineEdit != NULL) { m_keyValueLineEdit->setText(""); } } //m_editingGroup->setEnabled(isEditingAllowed); allowLabelDataEditing(isEditingAllowed); } /** * Called when a change is made in the color editor. * @param rgba * New RGBA values. */ void GiftiLabelTableEditor::colorEditorColorChanged(const float* rgba) { QListWidgetItem* selectedItem = m_labelSelectionListWidget->currentItem(); if (selectedItem != NULL) { setWidgetItemIconColor(selectedItem, rgba); } GiftiLabel* gl = getSelectedLabel(); if (gl != NULL) { gl->setColor(rgba); if (m_fociFile != NULL) { m_fociFile->invalidateAllAssignedColors(); } else if (m_borderFile != NULL) { m_borderFile->invalidateAllAssignedColors(); } else if (m_caretMappableDataFile != NULL) { VolumeFile* volumeFile = dynamic_cast(m_caretMappableDataFile); if (volumeFile != NULL) { volumeFile->clearVoxelColoringForMap(m_caretMappableDataFileMapIndex); } GroupAndNameHierarchyItem* item = gl->getGroupNameSelectionItem(); if (item != NULL) { item->setIconColorRGBA(rgba); } } } } /** * Called when label sorting is changed. */ void GiftiLabelTableEditor::sortingLabelsActivated() { AString selectedLabelName; const GiftiLabel* gl = getSelectedLabel(); if (gl != NULL) { selectedLabelName = gl->getName(); } loadLabels(selectedLabelName, false); } /** * Load labels into the list widget. * * @param selectedName * If not empty, select the label with this name * @param usePreviouslySelectedIndex * If true, use selected index prior to reloading list widget. */ void GiftiLabelTableEditor::loadLabels(const AString& selectedNameIn, const bool usePreviouslySelectedIndex) { m_labelSelectionListWidget->blockSignals(true); int32_t previousSelectedIndex = -1; if (usePreviouslySelectedIndex) { previousSelectedIndex = m_labelSelectionListWidget->currentRow(); } // const int32_t invalidLabelKey = GiftiLabel::getInvalidLabelKey(); // const GiftiLabel* invalidLabel = m_giftiLableTable->getLabel(invalidLabelKey); const GiftiLabel* selectedLabel = getSelectedLabel(); AString selectedName; if (selectedLabel != NULL) { selectedName = selectedLabel->getName(); } if ( ! selectedNameIn.isEmpty()) { const GiftiLabel* selectedNameInLabel = m_giftiLableTable->getLabel(selectedNameIn); if (selectedNameInLabel != NULL) { selectedName = selectedNameInLabel->getName(); } } m_labelSelectionListWidget->clear(); int defaultIndex = -1; const int32_t unassignedLabelKey = m_giftiLableTable->getUnassignedLabelKey(); std::vector keys; const QString sortName = m_sortLabelsByComboBox->currentText(); if (sortName == s_SORT_COMBO_BOX_NAME_BY_KEY) { m_giftiLableTable->getKeys(keys); } else if (sortName == s_SORT_COMBO_BOX_NAME_BY_NAME) { keys = m_giftiLableTable->getLabelKeysSortedByName(); } else { CaretAssertMessage(0, "Invalid sort by name"); } for (std::vector::iterator keyIterator = keys.begin(); keyIterator != keys.end(); keyIterator++) { const int32_t key = *keyIterator; if ( ! m_showUnassignedLabelInEditor) { if (key == unassignedLabelKey) { continue; } } const GiftiLabel* gl = m_giftiLableTable->getLabel(key); float rgba[4]; gl->getColor(rgba); QString keyAndNameText(QString::number(gl->getKey()).rightJustified(4, ' ', false) + ": " + (gl->getName())); QListWidgetItem* colorItem = new QListWidgetItem(keyAndNameText); setWidgetItemIconColor(colorItem, rgba); colorItem->setData(Qt::UserRole, qVariantFromValue((void*)gl)); m_labelSelectionListWidget->addItem(colorItem); if (selectedName == gl->getName()) { defaultIndex = m_labelSelectionListWidget->count() - 1; } } if (usePreviouslySelectedIndex) { defaultIndex = previousSelectedIndex; if (defaultIndex >= m_labelSelectionListWidget->count()) { defaultIndex--; } } if (usePreviouslySelectedIndex) { if (defaultIndex < 0) { if (m_labelSelectionListWidget->count() > 0) { defaultIndex = 0; } } } m_labelSelectionListWidget->blockSignals(false); if (defaultIndex >= 0) { m_labelSelectionListWidget->setCurrentRow(defaultIndex); } else { //m_editingGroup->setEnabled(false); allowLabelDataEditing(false); } } /** * Allow editing of label data. * * @param allowEditingFlag * If true allow editing of label data. */ void GiftiLabelTableEditor::allowLabelDataEditing(const bool allowEditingFlag) { m_editingGroup->setEnabled(allowEditingFlag); } /** * Set the Icon color for the item. * @param item * The list widget item. * @param rgba * RGBA values. */ void GiftiLabelTableEditor::setWidgetItemIconColor(QListWidgetItem* item, const float rgba[4]) { QColor color; color.setRedF(rgba[0]); color.setGreenF(rgba[1]); color.setBlueF(rgba[2]); color.setAlphaF(1.0); QPixmap pixmap(14, 14); pixmap.fill(color); QIcon colorIcon(pixmap); item->setIcon(colorIcon); } /** * @return The selected label or NULL if * no label is selected. */ GiftiLabel* GiftiLabelTableEditor::getSelectedLabel() { GiftiLabel* gl = NULL; QListWidgetItem* selectedItem = m_labelSelectionListWidget->currentItem(); if (selectedItem != NULL) { void* pointer = selectedItem->data(Qt::UserRole).value(); gl = (GiftiLabel*)pointer; } return gl; } /** * Called to create a new label. */ void GiftiLabelTableEditor::newButtonClicked() { /* * Make sure default name does not already exist */ AString name = "NewName_"; for (int i = 1; i < 10000; i++) { const AString testName = name + QString::number(i); if (m_giftiLableTable->getLabel(testName) == NULL) { name = testName; break; } } float rgba[4] = { 0.0, 0.0, 0.0, 1.0 }; m_giftiLableTable->addLabel(name, rgba[0], rgba[1], rgba[2], rgba[3]); loadLabels(name, false); // m_labelNameLineEdit->grabKeyboard(); // m_labelNameLineEdit->grabMouse(); listWidgetLabelSelected(); m_labelNameLineEdit->setFocus(); m_labelNameLineEdit->selectAll(); colorEditorColorChanged(rgba); } /** * Called when change label key lock button is clicked. * * @param checked * Checked status of button. */ void GiftiLabelTableEditor::changeLabelKeyLockButtonClicked() { if (m_changeKeyValueToolButton == NULL) { return; } const GiftiLabel* selectedLabel = getSelectedLabel(); if (selectedLabel == NULL) { return; } if (s_displayKeyEditingWarningFlag) { s_displayKeyEditingWarningFlag = false; const AString text("Are you sure that you want to edit label keys?"); const AString infoText("Brainordinate values are not changed in the label type file\n" "and changing label keys may cause label data to display\n" "incorrectly.\n" "\n" "This warning will not be displayed again until " + ApplicationInformation().getName() + " is restarted."); if ( ! WuQMessageBox::warningOkCancel(m_changeKeyValueToolButton, text, infoText)) { return; } } const AString labelName = selectedLabel->getName(); ChangeLabelKeyDialog changeLabelDialog(m_giftiLableTable, selectedLabel, m_changeKeyValueToolButton); if (changeLabelDialog.exec() == ChangeLabelKeyDialog::Accepted) { loadLabels(labelName, false); processApplyButton(); } } /** * Called to undo changes to selected label. */ void GiftiLabelTableEditor::undoButtonClicked() { if (m_undoGiftiLabel != NULL) { labelNameLineEditTextEdited(m_undoGiftiLabel->getName()); float rgba[4]; m_undoGiftiLabel->getColor(rgba); colorEditorColorChanged(rgba); listWidgetLabelSelected(); } } /** * Called to delete the label. */ void GiftiLabelTableEditor::deleteButtonClicked() { GiftiLabel* gl = getSelectedLabel(); if (gl != NULL) { if (WuQMessageBox::warningOkCancel(this, "Delete " + gl->getName())) { m_giftiLableTable->deleteLabel(gl); loadLabels("", true); listWidgetLabelSelected(); } } } /** * Gets called when a button this dialog added is clicked. * * @param userPushButton * Button that was clicked. */ WuQDialogModal::DialogUserButtonResult GiftiLabelTableEditor::userButtonPressed(QPushButton* userPushButton) { DialogUserButtonResult result = RESULT_NONE; if (userPushButton == m_applyPushButton) { processApplyButton(); result = RESULT_NONE; } else { result = WuQDialogModal::userButtonPressed(userPushButton); } return result; } /** * Process as if the apply button was pressed. * Apply is like OK, except that the dialog remains open. */ void GiftiLabelTableEditor::processApplyButton() { if (m_caretMappableDataFile != NULL) { const PaletteFile* paletteFile = GuiManager::get()->getBrain()->getPaletteFile(); m_caretMappableDataFile->updateScalarColoringForMap(m_caretMappableDataFileMapIndex, paletteFile); } EventManager::get()->sendEvent(EventSurfaceColoringInvalidate().getPointer()); EventManager::get()->sendEvent(EventGraphicsUpdateAllWindows().getPointer()); EventManager::get()->sendEvent(EventUserInterfaceUpdate().getPointer()); } /** * Called when the OK button is clicked. */ void GiftiLabelTableEditor::okButtonClicked() { processApplyButton(); const GiftiLabel* gl = getSelectedLabel(); const AString labelName = (gl != NULL) ? gl->getName() : ""; const AString sortingName = m_sortLabelsByComboBox->currentText(); std::map::iterator lastEditIter = s_previousSelections.find(m_giftiLableTable); if (lastEditIter != s_previousSelections.end()) { PreviousSelections& ps = lastEditIter->second; ps.m_selectedLabelName = labelName; ps.m_sortingName = sortingName; } else { PreviousSelections ps; ps.m_selectedLabelName = labelName; ps.m_sortingName = sortingName; s_previousSelections.insert(std::make_pair(m_giftiLableTable, ps)); } WuQDialogModal::okButtonClicked(); } /* ==================================================================================================================== */ /** * Dialog for changing a label's key. * * @param giftiLabelTable * Label table that is being edited. * @param giftiLabel * Label that may have its key changed. * @param parent * Parent on which this dialog is displayed. */ ChangeLabelKeyDialog::ChangeLabelKeyDialog(GiftiLabelTable* giftiLabelTable, const GiftiLabel* giftiLabel, QWidget* parent) : WuQDialogModal("Change Label Key", parent), m_giftiLabelTable(giftiLabelTable), m_giftiLabel(giftiLabel) { QLabel* nameLabel = new QLabel("Name: "); QLabel* nameTextLabel = new QLabel(giftiLabel->getName()); QLabel* keyLabel = new QLabel("Key: "); QLabel* keyValueLabel = new QLabel(AString::number(giftiLabel->getKey())); QLabel* newKeyLabel = new QLabel("New Key: "); m_labelKeyLineEdit = new QLineEdit(); m_labelKeyLineEdit->setFixedWidth(100); m_labelKeyLineEdit->setValidator(new QIntValidator(0, 999999, m_labelKeyLineEdit)); m_labelKeyLineEdit->setText(keyValueLabel->text()); QWidget* widget = new QWidget(); QGridLayout* gridLayout = new QGridLayout(widget); gridLayout->addWidget(nameLabel, 0, 0); gridLayout->addWidget(nameTextLabel, 0, 1, Qt::AlignLeft); gridLayout->addWidget(keyLabel, 1, 0); gridLayout->addWidget(keyValueLabel, 1, 1, Qt::AlignLeft); gridLayout->addWidget(newKeyLabel, 2, 0); gridLayout->addWidget(m_labelKeyLineEdit, 2, 1, Qt::AlignLeft); setCentralWidget(widget, WuQDialog::SCROLL_AREA_NEVER); } /** * Destructor. */ ChangeLabelKeyDialog::~ChangeLabelKeyDialog() { } /** * Called when ok button is clicked. */ void ChangeLabelKeyDialog::okButtonClicked() { const AString keyText = m_labelKeyLineEdit->text().trimmed(); if ( ! keyText.isEmpty()) { const int32_t newKey = keyText.toInt(); const int32_t oldKey = m_giftiLabel->getKey(); if (newKey != oldKey) { const AString oldKeyMsg("Any brainordinates assigned to \"" + m_giftiLabel->getName() + "\" will no long receive any coloring since there is no longer a label matching the brainordinates' value."); const GiftiLabel* newKeyLabel = m_giftiLabelTable->getLabel(newKey); if (newKeyLabel != NULL) { const AString msg("WARNING: Each label must have a unique key.\n" "\n" "\n" "\n" "In addition:\n" " (1) A label named \"" + newKeyLabel->getName() + "\" is assigned the key " + AString::number(newKey) + " and this label will be removed.\n\n" " (2) Any brainordinates assigned to \"" + newKeyLabel->getName() + "\" will be assigned to \"" + m_giftiLabel->getName() + "\".\n\n" + " (3) " + oldKeyMsg); if (WuQMessageBox::warningOkCancel(this, msg)) { m_giftiLabelTable->changeLabelKey(oldKey, newKey); } else { return; } } else { if (WuQMessageBox::warningOkCancel(this, ("WARNING: " + oldKeyMsg))) { m_giftiLabelTable->changeLabelKey(oldKey, newKey); } else { return; } } } } WuQDialogModal::okButtonClicked(); } ///** // * // */ //int32_t //ChangeLabelKeyDialog::getNewKeyValue() const //{ // //} // connectome-workbench-1.2.3+git41-gc4c6c90/src/GuiQt/GiftiLabelTableEditor.h000066400000000000000000000157561300200146000261050ustar00rootroot00000000000000#ifndef __GIFTI_LABEL_TABLE_EDITOR__H_ #define __GIFTI_LABEL_TABLE_EDITOR__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 "CaretPointer.h" #include "WuQDialogModal.h" class QAction; class QComboBox; class QLabel; class QLineEdit; class QListWidget; class QListWidgetItem; class QPushButton; class QToolButton; namespace caret { class BorderFile; class CaretMappableDataFile; class ColorEditorWidget; class FociFile; class GiftiLabel; class GiftiLabelTable; class WuQWidgetObjectGroup; class GiftiLabelTableEditor : public WuQDialogModal { Q_OBJECT public: enum Options { /** * No options */ OPTION_NONE = 0, /** * Hide the unassigned label so that it is not shown in editor. * May be bitwise OR'ed with other options. */ OPTION_UNASSIGNED_LABEL_HIDDEN = 1, /** * Add an apply button so that the graphics windows can be * updated without having to close the dialog. */ OPTION_ADD_APPLY_BUTTON = 2, /** * Add GUI components that allow the user to edit the * key assigned to the selected label. */ OPTION_ADD_KEY_EDITING = 4 }; // GiftiLabelTableEditor(GiftiLabelTable* giftiLableTable, // const AString& dialogTitle, // const uint32_t options, // QWidget* parent); GiftiLabelTableEditor(CaretMappableDataFile* caretMappableDataFile, const int32_t mapIndex, const AString& dialogTitle, const uint32_t options, QWidget* parent); GiftiLabelTableEditor(FociFile* fociFile, GiftiLabelTable* giftiLableTable, const AString& dialogTitle, const uint32_t options, QWidget* parent); GiftiLabelTableEditor(BorderFile* borderFile, GiftiLabelTable* giftiLableTable, const AString& dialogTitle, const uint32_t options, QWidget* parent); virtual ~GiftiLabelTableEditor(); AString getLastSelectedLabelName() const; void selectLabelWithName(const AString& labelName); private: GiftiLabelTableEditor(const GiftiLabelTableEditor&); GiftiLabelTableEditor& operator=(const GiftiLabelTableEditor&); private slots: void newButtonClicked(); void deleteButtonClicked(); void undoButtonClicked(); void changeLabelKeyLockButtonClicked(); // void listWidgetLabelSelected(int row); void listWidgetLabelSelected(); void colorEditorColorChanged(const float*); void labelNameLineEditTextEdited(const QString&); void sortingLabelsActivated(); protected: virtual void okButtonClicked(); DialogUserButtonResult userButtonPressed(QPushButton* userPushButton); private: void initializeDialog(GiftiLabelTable* giftiLabelTable, const uint32_t options); void loadLabels(const AString& selectedName, const bool usePreviouslySelectedIndex); GiftiLabel* getSelectedLabel(); void setWidgetItemIconColor(QListWidgetItem* item, const float rgba[4]); void processApplyButton(); void allowLabelDataEditing(const bool allowEditingFlag); QListWidget* m_labelSelectionListWidget; BorderFile* m_borderFile; FociFile* m_fociFile; CaretMappableDataFile* m_caretMappableDataFile; int32_t m_caretMappableDataFileMapIndex; GiftiLabelTable* m_giftiLableTable; ColorEditorWidget* m_colorEditorWidget; QLineEdit* m_labelNameLineEdit; QLineEdit* m_keyValueLineEdit; QToolButton* m_changeKeyValueToolButton; AString m_lastSelectedLabelName; GiftiLabel* m_undoGiftiLabel; bool m_showUnassignedLabelInEditor; WuQWidgetObjectGroup* m_editingGroup; QPushButton* m_applyPushButton; QComboBox* m_sortLabelsByComboBox; struct PreviousSelections { AString m_sortingName; AString m_selectedLabelName; }; static std::map s_previousSelections; static const AString s_SORT_COMBO_BOX_NAME_BY_KEY; static const AString s_SORT_COMBO_BOX_NAME_BY_NAME; static bool s_displayKeyEditingWarningFlag; }; class ChangeLabelKeyDialog : public WuQDialogModal { Q_OBJECT public: ChangeLabelKeyDialog(GiftiLabelTable* giftiLabelTable, const GiftiLabel* giftiLabel, QWidget* parent); ~ChangeLabelKeyDialog(); // int32_t getNewKeyValue() const; protected: virtual void okButtonClicked(); private: GiftiLabelTable* m_giftiLabelTable; const GiftiLabel* m_giftiLabel; QLineEdit* m_labelKeyLineEdit; }; #ifdef __GIFTI_LABEL_TABLE_EDITOR_DECLARE__ std::map GiftiLabelTableEditor::s_previousSelections; const AString GiftiLabelTableEditor::s_SORT_COMBO_BOX_NAME_BY_KEY = "Key"; const AString GiftiLabelTableEditor::s_SORT_COMBO_BOX_NAME_BY_NAME = "Name"; bool GiftiLabelTableEditor::s_displayKeyEditingWarningFlag = true; #endif // __GIFTI_LABEL_TABLE_EDITOR_DECLARE__ } // namespace #endif //__GIFTI_LABEL_TABLE_EDITOR__H_ connectome-workbench-1.2.3+git41-gc4c6c90/src/GuiQt/GiftiLabelTableSelectionComboBox.cxx000066400000000000000000000262561300200146000306050ustar00rootroot00000000000000 /*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 __GIFTI_LABEL_TABLE_SELECTION_COMBO_BOX_DECLARE__ #include "GiftiLabelTableSelectionComboBox.h" #undef __GIFTI_LABEL_TABLE_SELECTION_COMBO_BOX_DECLARE__ #include #include #include #include "CaretAssert.h" #include "CaretLogger.h" #include "GiftiLabel.h" #include "GiftiLabelTable.h" using namespace caret; /** * \class caret::GiftiLabelTableSelectionComboBox * \brief Combo box for selection of a gifti label. * \ingroup GuiQt */ /** * Constructor. * * @param parent * Parent of this object. */ GiftiLabelTableSelectionComboBox::GiftiLabelTableSelectionComboBox(QObject* parent) : WuQWidget(parent) { m_ignoreInsertedRowsFlag = true; m_giftiLabelTable = NULL; m_unassignedLabelTextOverride = ""; m_comboBox = new QComboBox(); m_comboBox->setMaxVisibleItems(20); QObject::connect(m_comboBox, SIGNAL(activated(int)), this, SLOT(itemActivated(int))); QObject::connect(m_comboBox, SIGNAL(currentIndexChanged(int)), this, SLOT(currentIndexChanged(int))); #ifdef CARET_OS_MACOSX /* * On Mac, use a windows combo box so that * the pop-up is not a list the covers the * full vertical dimension of the monitor. */ QStyle* style = QStyleFactory::create("Windows"); if (style != NULL) { m_comboBox->setStyle(style); //QPalette macPalette(QPalette(m_comboBox->palette())); //m_comboBox->setPalette(macPalette); } // QStringList styleNames; // //styleNames << "Windows" << "Plastique"; // QStringListIterator styleNameIterator(styleNames); // while (styleNameIterator.hasNext()) { // style = QStyleFactory::create(styleNameIterator.next()); // if (style != NULL) { // break; // } // } #endif // CARET_OS_MACOSX QStyle::SH_ComboBox_Popup const bool allowEditingFlag = false; if (allowEditingFlag) { m_comboBox->setEditable(true); m_comboBox->setDuplicatesEnabled(false); m_comboBox->setInsertPolicy(QComboBox::InsertAlphabetically); QAbstractItemModel* model = m_comboBox->model(); QObject::connect(model, SIGNAL(rowsInserted(const QModelIndex&, int, int)), this, SLOT(rowsWereInserted(const QModelIndex, int, int))); } } /** * Destructor. */ GiftiLabelTableSelectionComboBox::~GiftiLabelTableSelectionComboBox() { } /** * Override the text of the unassigned label. * * @param text * Text that overrides the text of the unassigned label. */ void GiftiLabelTableSelectionComboBox::setUnassignedLabelTextOverride(const AString& text) { m_unassignedLabelTextOverride = text; } /** * If the combo box is editable, this can be used to create the new * label. * * @param parent * The parent model index. * @param start * First new row index. * @param end * Last new row index. */ void GiftiLabelTableSelectionComboBox::rowsWereInserted(const QModelIndex& /*parent*/, int start, int end) { if (m_ignoreInsertedRowsFlag) { return; } for (int i = start; i <= end; i++) { const QString text = m_comboBox->itemText(i); QVariant userData = m_comboBox->itemData(i); void* pointer = userData.value(); if (pointer == NULL) { const int32_t key = m_giftiLabelTable->addLabel(text, 0.0f, 0.0f, 0.0f, 1.0f); GiftiLabel* label = m_giftiLabelTable->getLabel(key); QPixmap pm(10, 10); pm.fill(QColor::fromRgbF(label->getRed(), label->getGreen(), label->getBlue())); QIcon icon(pm); QVariant userData = qVariantFromValue((void*)label); m_comboBox->setItemData(i, userData); m_comboBox->setItemIcon(i, icon); } } } /** * Called when the add/edit button is clicked. */ void GiftiLabelTableSelectionComboBox::addEditButtonClicked() { } /** * Gets called when the index is changed by user or program code. * * @parma index * Index of item. */ void GiftiLabelTableSelectionComboBox::currentIndexChanged(int /*indx*/) { GiftiLabel* gl = getSelectedLabel(); emit labelChanged(gl); if (gl != NULL) { emit labelKeyChanged(gl->getKey()); } else { emit labelKeyChanged(GiftiLabel::getInvalidLabelKey()); } } /** * Gets called when the user selects an item even if the selection * does not change. * * @parma index * Index of item. */ void GiftiLabelTableSelectionComboBox::itemActivated(int /*indx*/) { GiftiLabel* gl = getSelectedLabel(); emit labelActivated(gl); if (gl != NULL) { emit labelKeyActivated(gl->getKey()); } else { emit labelKeyActivated(GiftiLabel::getInvalidLabelKey()); } } /** * Update the content of this control with the given lable table. * * @param giftiLabelTable * The label table. */ void GiftiLabelTableSelectionComboBox::updateContent(GiftiLabelTable* giftiLabelTable) { m_ignoreInsertedRowsFlag = true; m_giftiLabelTable = giftiLabelTable; const AString selectedLabelName = m_comboBox->currentText(); m_comboBox->clear(); int32_t defaultIndex = -1; if (m_giftiLabelTable != NULL) { const int32_t unassignedKey = m_giftiLabelTable->getUnassignedLabelKey(); const std::vector keySet = m_giftiLabelTable->getLabelKeysSortedByName(); for (std::vector::const_iterator iter = keySet.begin(); iter != keySet.end(); iter++) { const int32_t key = *iter; GiftiLabel* label = giftiLabelTable->getLabel(key); AString labelName = label->getName(); if (key == unassignedKey) { if (m_unassignedLabelTextOverride.isEmpty() == false) { labelName = m_unassignedLabelTextOverride; } } if (labelName == selectedLabelName) { defaultIndex = m_comboBox->count(); } QPixmap pm(10, 10); pm.fill(QColor::fromRgbF(label->getRed(), label->getGreen(), label->getBlue())); QIcon icon(pm); QVariant userData = qVariantFromValue((void*)label); m_comboBox->addItem(icon, labelName, userData); } m_comboBox->setEnabled(true); if (defaultIndex >= 0) { m_comboBox->blockSignals(true); m_comboBox->setCurrentIndex(defaultIndex); m_comboBox->blockSignals(false); } } else { m_comboBox->setEnabled(false); } m_ignoreInsertedRowsFlag = false; } /** * Return the widget in this object. */ QWidget* GiftiLabelTableSelectionComboBox::getWidget() { return m_comboBox; } /** * @return The selected label (NULL if no selection). */ const GiftiLabel* GiftiLabelTableSelectionComboBox::getSelectedLabel() const { const int indx = m_comboBox->currentIndex(); if (indx >= 0) { QVariant userData = m_comboBox->itemData(indx); void* pointer = userData.value(); GiftiLabel* giftiLabel = (GiftiLabel*)pointer; return giftiLabel; } return NULL; } /** * @return The selected label (NULL if no selection). */ GiftiLabel* GiftiLabelTableSelectionComboBox::getSelectedLabel() { const int indx = m_comboBox->currentIndex(); if (indx >= 0) { QVariant userData = m_comboBox->itemData(indx); void* pointer = userData.value(); GiftiLabel* giftiLabel = (GiftiLabel*)pointer; return giftiLabel; } return NULL; } /** * Set the selected label to the given label. * * @param label * The label that is to be selected. */ void GiftiLabelTableSelectionComboBox::setSelectedLabel(const GiftiLabel* label) { if (label != NULL) { QVariant userData = qVariantFromValue((void*)label); int indx = m_comboBox->findData(userData); if (indx >= 0) { m_comboBox->setCurrentIndex(indx); } else { CaretLogSevere("Label not found: " + label->getName()); } } } /** * @return Key of the selected label or GiftiLabel::getInvalidLabelKey() * if no label selected. */ int32_t GiftiLabelTableSelectionComboBox::getSelectedLabelKey() const { const GiftiLabel* gl = getSelectedLabel(); if (gl != NULL) { return gl->getKey(); } return GiftiLabel::getInvalidLabelKey(); } /** * Set the selected label to the label with the given key. * * @param key * Key of label that is to be selected. */ void GiftiLabelTableSelectionComboBox::setSelectedLabelKey(const int32_t key) { const GiftiLabel* label = m_giftiLabelTable->getLabel(key); if (label != NULL) { setSelectedLabel(label); } else { CaretLogSevere("No label with key found: " + QString::number(key)); } } /** * @return Name of selected label or empty string if no label selected. */ QString GiftiLabelTableSelectionComboBox::getSelectedLabelName() const { const GiftiLabel* gl = getSelectedLabel(); if (gl != NULL) { return gl->getName(); } return ""; } /** * Set the selected label to the label with the given name. * * @param labelName * Name of label that is to be selected. */ void GiftiLabelTableSelectionComboBox::setSelectedLabelName(const QString& labelName) { const GiftiLabel* label = m_giftiLabelTable->getLabel(labelName); if (label != NULL) { setSelectedLabel(label); } else { // CaretLogSevere("No label with name found: " + // labelName); } } connectome-workbench-1.2.3+git41-gc4c6c90/src/GuiQt/GiftiLabelTableSelectionComboBox.h000066400000000000000000000105571300200146000302270ustar00rootroot00000000000000#ifndef __GIFTI_LABEL_TABLE_SELECTION_COMBO_BOX_H__ #define __GIFTI_LABEL_TABLE_SELECTION_COMBO_BOX_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 "AString.h" #include "WuQWidget.h" class QComboBox; class QModelIndex; namespace caret { class GiftiLabel; class GiftiLabelTable; class GiftiLabelTableSelectionComboBox : public WuQWidget { Q_OBJECT public: GiftiLabelTableSelectionComboBox(QObject* parent); virtual ~GiftiLabelTableSelectionComboBox(); void updateContent(GiftiLabelTable* giftiLabelTable); virtual QWidget* getWidget(); const GiftiLabel* getSelectedLabel() const; GiftiLabel* getSelectedLabel(); void setSelectedLabel(const GiftiLabel* label); int32_t getSelectedLabelKey() const; void setSelectedLabelKey(const int32_t key); QString getSelectedLabelName() const; void setSelectedLabelName(const QString& labelName); void setUnassignedLabelTextOverride(const AString& text); signals: /** * This signal is sent when the user chooses a label in the combobox. * The item's label is passed. Note that this signal is sent even when * the choice is not changed. If you need to know when the choice * actually changes, use signal labelChanged(). If the selected * label is invalid, NULL is passed. */ void labelActivated(GiftiLabel*); /** * This signal is sent when the user chooses a label in the combobox. * The item's key is passed. Note that this signal is sent even when * the choice is not changed. If you need to know when the choice * actually changes, use signal labelKeyChanged(). If the selected * label is invalid, GiftiLabel::getInvalidLabelKey() is passed. */ void labelKeyActivated(const int32_t key); /** * This signal is sent whenever the label in the combobox * changes either through user interaction or programmatically. * If the selected label is invalid, NULL is passed. */ void labelChanged(GiftiLabel*); /** * This signal is sent whenever the label in the combobox * changes either through user interaction or programmatically. * If the selected label is invalid, GiftiLabel::getInvalidLabelKey() is passed. */ void labelKeyChanged(const int32_t key); // ADD_NEW_METHODS_HERE private slots: void currentIndexChanged(int indx); void itemActivated(int indx); void addEditButtonClicked(); void rowsWereInserted(const QModelIndex& parent, int start, int end); private: GiftiLabelTableSelectionComboBox(const GiftiLabelTableSelectionComboBox&); GiftiLabelTableSelectionComboBox& operator=(const GiftiLabelTableSelectionComboBox&); GiftiLabelTable* m_giftiLabelTable; QComboBox* m_comboBox; bool m_ignoreInsertedRowsFlag; AString m_unassignedLabelTextOverride; // ADD_NEW_MEMBERS_HERE }; #ifdef __GIFTI_LABEL_TABLE_SELECTION_COMBO_BOX_DECLARE__ // #endif // __GIFTI_LABEL_TABLE_SELECTION_COMBO_BOX_DECLARE__ } // namespace #endif //__GIFTI_LABEL_TABLE_SELECTION_COMBO_BOX_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/GuiQt/GroupAndNameHierarchyTreeWidgetItem.cxx000066400000000000000000000422261300200146000313110ustar00rootroot00000000000000 /*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 __CLASS_AND_NAME_HIERARCHY_TREE_WIDGET_ITEM_DECLARE__ #include "GroupAndNameHierarchyTreeWidgetItem.h" #undef __CLASS_AND_NAME_HIERARCHY_TREE_WIDGET_ITEM_DECLARE__ #include "CaretAssert.h" #include "GroupAndNameHierarchyGroup.h" #include "GroupAndNameHierarchyModel.h" #include "GroupAndNameHierarchyName.h" using namespace caret; /** * \class caret::ClassAndNameHierarchySelectionInfo * \brief Tree Widget Item for Class and Name Hierarchy * \ingroup GuiQt */ /** * Constructor for ClassAndNameHierarchyModel * @param classAndNameHierarchyModel * The class name hierarchy model. */ GroupAndNameHierarchyTreeWidgetItem::GroupAndNameHierarchyTreeWidgetItem(const DisplayGroupEnum::Enum displayGroup, const int32_t tabIndex, GroupAndNameHierarchyModel* classAndNameHierarchyModel) : QTreeWidgetItem() { CaretAssert(classAndNameHierarchyModel); initialize(classAndNameHierarchyModel, displayGroup, tabIndex, ITEM_TYPE_HIERARCHY_MODEL, classAndNameHierarchyModel->getName(), NULL); m_classAndNameHierarchyModel = classAndNameHierarchyModel; /* * Loop through each class */ std::vector classChildren = m_classAndNameHierarchyModel->getChildren(); for (std::vector::iterator classIter = classChildren.begin(); classIter != classChildren.end(); classIter++) { GroupAndNameHierarchyItem* classItem = *classIter; CaretAssert(classItem); GroupAndNameHierarchyGroup* group = dynamic_cast(classItem); CaretAssert(group); GroupAndNameHierarchyTreeWidgetItem* groupItem = new GroupAndNameHierarchyTreeWidgetItem(displayGroup, tabIndex, group); addChildItem(groupItem); } } /** * Constructor for ClassDisplayGroupSelector * @param classDisplayGroupSelector * The class display group selector. */ GroupAndNameHierarchyTreeWidgetItem::GroupAndNameHierarchyTreeWidgetItem(const DisplayGroupEnum::Enum displayGroup, const int32_t tabIndex, GroupAndNameHierarchyGroup* classDisplayGroupSelector) : QTreeWidgetItem() { CaretAssert(classDisplayGroupSelector); initialize(classDisplayGroupSelector, displayGroup, tabIndex, ITEM_TYPE_CLASS, classDisplayGroupSelector->getName(), classDisplayGroupSelector->getIconColorRGBA()); m_classDisplayGroupSelector = classDisplayGroupSelector; std::vector nameChildren = m_classDisplayGroupSelector->getChildren(); for (std::vector::iterator nameIter = nameChildren.begin(); nameIter != nameChildren.end(); nameIter++) { GroupAndNameHierarchyItem* nameItem = *nameIter; CaretAssert(nameItem); GroupAndNameHierarchyName* name = dynamic_cast(nameItem); CaretAssert(name); GroupAndNameHierarchyTreeWidgetItem* nameTreeItem = new GroupAndNameHierarchyTreeWidgetItem(displayGroup, tabIndex, name); addChildItem(nameTreeItem); } } /** * Constructor for NameDisplayGroupSelector * @param nameDisplayGroupSelector * The name display group selector. */ GroupAndNameHierarchyTreeWidgetItem::GroupAndNameHierarchyTreeWidgetItem(const DisplayGroupEnum::Enum displayGroup, const int32_t tabIndex, GroupAndNameHierarchyName* nameDisplayGroupSelector) : QTreeWidgetItem() { CaretAssert(nameDisplayGroupSelector); initialize(nameDisplayGroupSelector, displayGroup, tabIndex, ITEM_TYPE_NAME, nameDisplayGroupSelector->getName(), nameDisplayGroupSelector->getIconColorRGBA()); m_nameDisplayGroupSelector = nameDisplayGroupSelector; } /** * Destructor. */ GroupAndNameHierarchyTreeWidgetItem::~GroupAndNameHierarchyTreeWidgetItem() { /* * Note: Do not need to delete children since they are added to * Qt layouts which will delete them. */ } /** * Initialize this instance. * @param itemType * Type of item contained in this instance. */ void GroupAndNameHierarchyTreeWidgetItem::initialize(GroupAndNameHierarchyItem* groupAndNameHierarchyItem, const DisplayGroupEnum::Enum displayGroup, const int32_t tabIndex, const ItemType itemType, const QString text, const float* /*iconColorRGBA*/) { m_groupAndNameHierarchyItem = groupAndNameHierarchyItem; m_classAndNameHierarchyModel = dynamic_cast(groupAndNameHierarchyItem); m_classDisplayGroupSelector = dynamic_cast(groupAndNameHierarchyItem); m_nameDisplayGroupSelector = dynamic_cast(groupAndNameHierarchyItem); const int32_t count = (((m_classAndNameHierarchyModel != NULL) ? 1 : 0) + ((m_classDisplayGroupSelector != NULL) ? 1 : 0) + ((m_nameDisplayGroupSelector != NULL) ? 1 : 0)); if (count != 1) { CaretAssertMessage(0, "Invalid item added to group/name hierarchy tree."); } m_iconColorRGBA[0] = -1.0; m_iconColorRGBA[1] = -1.0; m_iconColorRGBA[2] = -1.0; m_iconColorRGBA[3] = -1.0; m_displayGroup = displayGroup; m_tabIndex = tabIndex; m_itemType = itemType; // m_classAndNameHierarchyModel = NULL; // m_classDisplayGroupSelector = NULL; // m_nameDisplayGroupSelector = NULL; m_hasChildren = false; switch (m_itemType) { case ITEM_TYPE_CLASS: m_hasChildren = true; break; case ITEM_TYPE_HIERARCHY_MODEL: m_hasChildren = true; break; case ITEM_TYPE_NAME: m_hasChildren = false; break; } setText(TREE_COLUMN, text); /*Qt::ItemFlags itemFlags = (Qt::ItemIsSelectable | Qt::ItemIsUserCheckable | Qt::ItemIsEnabled);//*/ if (m_hasChildren) { // itemFlags |= Qt::ItemIsTristate; } // setFlags(itemFlags); // NEW 11/14/12 // if (iconColorRGBA != NULL) { // if (iconColorRGBA[3] > 0.0) { // QPixmap pm(10, 10); // pm.fill(QColor::fromRgbF(iconColorRGBA[0], // iconColorRGBA[1], // iconColorRGBA[2])); // QIcon icon(pm); // setIcon(TREE_COLUMN, icon); // } // } updateIconColorIncludingChildren(); } /** * Update the selections in this and its children. * @param displayGroup * Display group that is active. * @param tabIndex * Index of tab that is displayed. */ void GroupAndNameHierarchyTreeWidgetItem::updateSelections(const DisplayGroupEnum::Enum displayGroup, const int32_t tabIndex) { m_displayGroup = displayGroup; m_tabIndex = tabIndex; GroupAndNameCheckStateEnum::Enum checkState = GroupAndNameCheckStateEnum::UNCHECKED; bool expandedStatus = false; switch (m_itemType) { case ITEM_TYPE_CLASS: CaretAssert(m_classDisplayGroupSelector); checkState = m_classDisplayGroupSelector->getCheckState(m_displayGroup, m_tabIndex); expandedStatus = m_classDisplayGroupSelector->isExpandedToDisplayChildren(m_displayGroup, m_tabIndex); break; case ITEM_TYPE_HIERARCHY_MODEL: CaretAssert(m_classAndNameHierarchyModel); checkState = m_classAndNameHierarchyModel->getCheckState(m_displayGroup, m_tabIndex); expandedStatus = m_classAndNameHierarchyModel->isExpandedToDisplayChildren(m_displayGroup, m_tabIndex); break; case ITEM_TYPE_NAME: CaretAssert(m_nameDisplayGroupSelector); checkState = m_nameDisplayGroupSelector->getCheckState(m_displayGroup, m_tabIndex); expandedStatus = false; break; } Qt::CheckState qtCheckState = toQCheckState(checkState); setCheckState(TREE_COLUMN, qtCheckState); if (m_hasChildren) { setExpanded(expandedStatus); for (std::vector::iterator iter = m_children.begin(); iter != m_children.end(); iter++) { GroupAndNameHierarchyTreeWidgetItem* item = *iter; item->updateSelections(m_displayGroup, tabIndex); } } } /** * If this item's color has changed, update its icon. * Process all of its children. */ void GroupAndNameHierarchyTreeWidgetItem::updateIconColorIncludingChildren() { if (m_groupAndNameHierarchyItem != NULL) { const float* rgba = m_groupAndNameHierarchyItem->getIconColorRGBA(); if (rgba != NULL) { if (rgba[3] > 0.0) { bool colorChanged = false; for (int32_t i = 0; i < 4; i++) { if (m_iconColorRGBA[i] != rgba[i]) { colorChanged = true; break; } } if (colorChanged) { m_iconColorRGBA[0] = rgba[0]; m_iconColorRGBA[1] = rgba[1]; m_iconColorRGBA[2] = rgba[2]; m_iconColorRGBA[3] = rgba[3]; QPixmap pm(10, 10); pm.fill(QColor::fromRgbF(m_iconColorRGBA[0], m_iconColorRGBA[1], m_iconColorRGBA[2])); QIcon icon(pm); setIcon(TREE_COLUMN, icon); } } } } for (std::vector::iterator iter = m_children.begin(); iter != m_children.end(); iter++) { GroupAndNameHierarchyTreeWidgetItem* item = *iter; item->updateIconColorIncludingChildren(); } } /** * Add a child. * @param child. * The child. */ void GroupAndNameHierarchyTreeWidgetItem::addChildItem(GroupAndNameHierarchyTreeWidgetItem* child) { CaretAssert(child); m_children.push_back(child); addChild(child); } /** * @return ItemType of the selected item. */ GroupAndNameHierarchyTreeWidgetItem::ItemType GroupAndNameHierarchyTreeWidgetItem::getItemType() const { return m_itemType; } /** * @return The class name and hierarchy model. NULL * if this instance contains another type of data. */ GroupAndNameHierarchyModel* GroupAndNameHierarchyTreeWidgetItem::getClassAndNameHierarchyModel() { CaretAssert(m_itemType == ITEM_TYPE_HIERARCHY_MODEL); return m_classAndNameHierarchyModel; } /** * @return The class name and hierarchy model. NULL * if this instance contains another type of data. */ GroupAndNameHierarchyGroup* GroupAndNameHierarchyTreeWidgetItem::getClassDisplayGroupSelector() { CaretAssert(m_itemType == ITEM_TYPE_CLASS); return m_classDisplayGroupSelector; } /** * @return The class name and hierarchy model. NULL * if this instance contains another type of data. */ GroupAndNameHierarchyName* GroupAndNameHierarchyTreeWidgetItem::getNameDisplayGroupSelector() { CaretAssert(m_itemType == ITEM_TYPE_NAME); return m_nameDisplayGroupSelector; } /** * Convert QCheckState to GroupAndNameCheckStateEnum * @param checkState * The QCheckState * @return GroupAndNameCheckStateEnum converted from QCheckState */ GroupAndNameCheckStateEnum::Enum GroupAndNameHierarchyTreeWidgetItem::fromQCheckState(const Qt::CheckState checkState) { switch (checkState) { case Qt::Unchecked: return GroupAndNameCheckStateEnum::UNCHECKED; break; case Qt::PartiallyChecked: return GroupAndNameCheckStateEnum::PARTIALLY_CHECKED; break; case Qt::Checked: return GroupAndNameCheckStateEnum::CHECKED; break; } return GroupAndNameCheckStateEnum::UNCHECKED; } /** * Convert GroupAndNameCheckStateEnum to QCheckState * @param checkState * The GroupAndNameCheckStateEnum * @return QCheckState converted from GroupAndNameCheckStateEnum converted. */ Qt::CheckState GroupAndNameHierarchyTreeWidgetItem::toQCheckState(const GroupAndNameCheckStateEnum::Enum checkState) { switch (checkState) { case GroupAndNameCheckStateEnum::CHECKED: return Qt::Checked; break; case GroupAndNameCheckStateEnum::PARTIALLY_CHECKED: return Qt::PartiallyChecked; break; case GroupAndNameCheckStateEnum::UNCHECKED: return Qt::Unchecked; break; } return Qt::Unchecked; } /** * Set the expanded status of the data in the model that is presented * by this item. * @param expanded * Status of expansion. */ void GroupAndNameHierarchyTreeWidgetItem::setModelDataExpanded(const bool expanded) { GroupAndNameHierarchyItem* item = NULL; switch (m_itemType) { case ITEM_TYPE_CLASS: item = m_classDisplayGroupSelector; break; case ITEM_TYPE_HIERARCHY_MODEL: item = m_classAndNameHierarchyModel; break; case ITEM_TYPE_NAME: item = m_nameDisplayGroupSelector; break; } if (item != NULL) { item->setExpandedToDisplayChildren(m_displayGroup, m_tabIndex, expanded); } } /** * Set the selected status of the data in the model that is presented * by this item. * @param selected * Status of selection. */ void GroupAndNameHierarchyTreeWidgetItem::setModelDataSelected(const bool selected) { GroupAndNameHierarchyItem* item = NULL; switch (m_itemType) { case ITEM_TYPE_CLASS: item = m_classDisplayGroupSelector; break; case ITEM_TYPE_HIERARCHY_MODEL: item = m_classAndNameHierarchyModel; break; case ITEM_TYPE_NAME: item = m_nameDisplayGroupSelector; break; } if (item != NULL) { const GroupAndNameCheckStateEnum::Enum existingCheckState = item->getCheckState(m_displayGroup, m_tabIndex); switch (existingCheckState) { case GroupAndNameCheckStateEnum::CHECKED: break; case GroupAndNameCheckStateEnum::PARTIALLY_CHECKED: break; case GroupAndNameCheckStateEnum::UNCHECKED: break; } if (selected) { item->setSelected(m_displayGroup, m_tabIndex, true); item->setAncestorsSelected(m_displayGroup, m_tabIndex, true); item->setDescendantsSelected(m_displayGroup, m_tabIndex, true); } else { item->setSelected(m_displayGroup, m_tabIndex, false); item->setDescendantsSelected(m_displayGroup, m_tabIndex, false); } } } connectome-workbench-1.2.3+git41-gc4c6c90/src/GuiQt/GroupAndNameHierarchyTreeWidgetItem.h000066400000000000000000000116541300200146000307370ustar00rootroot00000000000000#ifndef __CLASS_AND_NAME_HIERARCHY_TREE_WIDGET_ITEM__H_ #define __CLASS_AND_NAME_HIERARCHY_TREE_WIDGET_ITEM__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 "DisplayGroupEnum.h" #include "GroupAndNameCheckStateEnum.h" class QAction; class QCheckBox; class QVBoxLayout; namespace caret { class GroupAndNameHierarchyGroup; class GroupAndNameHierarchyItem; class GroupAndNameHierarchyModel; class GroupAndNameHierarchyName; class GroupAndNameHierarchyTreeWidgetItem : public QTreeWidgetItem { public: /** Type of item within the hierarchy */ enum ItemType { /** The class/name hierarchy model */ ITEM_TYPE_HIERARCHY_MODEL, /** Class in the class/name hierarchy */ ITEM_TYPE_CLASS, /** Name in the class/name hieracrchy */ ITEM_TYPE_NAME }; GroupAndNameHierarchyTreeWidgetItem(const DisplayGroupEnum::Enum displayGroup, const int32_t tabIndex, GroupAndNameHierarchyModel* classAndNameHierarchyModel); GroupAndNameHierarchyTreeWidgetItem(const DisplayGroupEnum::Enum displayGroup, const int32_t tabIndex, GroupAndNameHierarchyGroup* classDisplayGroupSelector); GroupAndNameHierarchyTreeWidgetItem(const DisplayGroupEnum::Enum displayGroup, const int32_t tabIndex, GroupAndNameHierarchyName* nameDisplayGroupSelector); ~GroupAndNameHierarchyTreeWidgetItem(); ItemType getItemType() const; GroupAndNameHierarchyModel* getClassAndNameHierarchyModel(); GroupAndNameHierarchyGroup* getClassDisplayGroupSelector(); GroupAndNameHierarchyName* getNameDisplayGroupSelector(); void addChildItem(GroupAndNameHierarchyTreeWidgetItem* child); void updateSelections(const DisplayGroupEnum::Enum displayGroup, const int32_t tabIndex); void setModelDataExpanded(const bool expanded); void setModelDataSelected(const bool selected); void updateIconColorIncludingChildren(); private: GroupAndNameHierarchyTreeWidgetItem(const GroupAndNameHierarchyTreeWidgetItem&); GroupAndNameHierarchyTreeWidgetItem& operator=(const GroupAndNameHierarchyTreeWidgetItem&); void initialize(GroupAndNameHierarchyItem* groupAndNameHierarchyItem, const DisplayGroupEnum::Enum displayGroup, const int32_t tabIndex, const ItemType itemType, const QString text, const float* iconColorRGBA); static GroupAndNameCheckStateEnum::Enum fromQCheckState(const Qt::CheckState checkState); static Qt::CheckState toQCheckState(const GroupAndNameCheckStateEnum::Enum checkState); ItemType m_itemType; DisplayGroupEnum::Enum m_displayGroup; int32_t m_tabIndex; GroupAndNameHierarchyItem* m_groupAndNameHierarchyItem; GroupAndNameHierarchyModel* m_classAndNameHierarchyModel; GroupAndNameHierarchyGroup* m_classDisplayGroupSelector; GroupAndNameHierarchyName* m_nameDisplayGroupSelector; std::vector m_children; bool m_displayNamesWithZeroCount; bool m_hasChildren; float m_iconColorRGBA[4]; static const int TREE_COLUMN; friend class GroupAndNameHierarchyViewController; }; #ifdef __CLASS_AND_NAME_HIERARCHY_TREE_WIDGET_ITEM_DECLARE__ const int GroupAndNameHierarchyTreeWidgetItem::TREE_COLUMN = 0; #endif // __CLASS_AND_NAME_HIERARCHY_TREE_WIDGET_ITEM_DECLARE__ } // namespace #endif //__CLASS_AND_NAME_HIERARCHY_TREE_WIDGET_ITEM__H_ connectome-workbench-1.2.3+git41-gc4c6c90/src/GuiQt/GroupAndNameHierarchyViewController.cxx000066400000000000000000000436441300200146000314120ustar00rootroot00000000000000 /*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 __CLASS_AND_NAME_HIERARCHY_VIEW_CONTROLLER_DECLARE__ #include "GroupAndNameHierarchyViewController.h" #undef __CLASS_AND_NAME_HIERARCHY_VIEW_CONTROLLER_DECLARE__ #include #include #include #include #include #include #include "Brain.h" #include "BorderFile.h" #include "BrowserTabContent.h" #include "CaretAssert.h" #include "CiftiBrainordinateLabelFile.h" #include "EventGraphicsUpdateAllWindows.h" #include "EventManager.h" #include "EventSurfaceColoringInvalidate.h" #include "EventUserInterfaceUpdate.h" #include "GroupAndNameHierarchyGroup.h" #include "GroupAndNameHierarchyModel.h" #include "GroupAndNameHierarchyName.h" #include "GroupAndNameHierarchyTreeWidgetItem.h" #include "FociFile.h" #include "GuiManager.h" #include "GiftiLabel.h" #include "GiftiLabelTable.h" #include "LabelFile.h" #include "VolumeFile.h" #include "WuQTreeWidget.h" #include "WuQtUtilities.h" using namespace caret; /** * \class caret::GroupAndNameHierarchyViewController * \brief View controller for ClassAndNameHierarchyModels * \ingroup GuiQt * * A view controller for one or more ClassAndNameHierarchyModel * instances. */ /** * Constructor. * @param parent * Parent widget. */ GroupAndNameHierarchyViewController::GroupAndNameHierarchyViewController(const int32_t browserWindowIndex, QWidget* parent) : QWidget(parent) { m_dataFileType = DataFileTypeEnum::UNKNOWN; m_displayGroup = DisplayGroupEnum::getDefaultValue(); m_previousDisplayGroup = DisplayGroupEnum::getDefaultValue(); m_previousBrowserTabIndex = -1; m_browserWindowIndex = browserWindowIndex; QWidget* allOnOffWidget = createAllOnOffControls(); m_modelTreeWidgetLayout = new QVBoxLayout(); WuQtUtilities::setLayoutSpacingAndMargins(m_modelTreeWidgetLayout, 0, 0); m_modelTreeWidget = NULL; createTreeWidget(); QVBoxLayout* layout = new QVBoxLayout(this); WuQtUtilities::setLayoutSpacingAndMargins(layout, 0, 0); layout->addWidget(allOnOffWidget); layout->addSpacing(5); layout->addLayout(m_modelTreeWidgetLayout, 100); layout->addStretch(); s_allViewControllers.insert(this); } /** * Destructor. */ GroupAndNameHierarchyViewController::~GroupAndNameHierarchyViewController() { s_allViewControllers.erase(this); } /** * Gets called when an item is collapsed so that its children are not visible. * * @param item * The QTreeWidgetItem that was collapsed. */ void GroupAndNameHierarchyViewController::itemWasCollapsed(QTreeWidgetItem* item) { GroupAndNameHierarchyTreeWidgetItem* treeItem = dynamic_cast(item); CaretAssert(treeItem); treeItem->setModelDataExpanded(false); updateSelectedAndExpandedCheckboxes(); updateSelectedAndExpandedCheckboxesInOtherViewControllers(); } /** * Gets called when an item is expaned so that its children are visible. * * @param item * The QTreeWidgetItem that was expanded. */ void GroupAndNameHierarchyViewController::itemWasExpanded(QTreeWidgetItem* item) { GroupAndNameHierarchyTreeWidgetItem* treeItem = dynamic_cast(item); CaretAssert(treeItem); treeItem->setModelDataExpanded(true); updateSelectedAndExpandedCheckboxes(); updateSelectedAndExpandedCheckboxesInOtherViewControllers(); } /** * Called when an item is changed (checkbox selected/deselected). * * @param item * The QTreeWidgetItem that was collapsed. * @param column * Ignored. */ void GroupAndNameHierarchyViewController::itemWasChanged(QTreeWidgetItem* item, int /*column*/) { GroupAndNameHierarchyTreeWidgetItem* treeItem = dynamic_cast(item); CaretAssert(treeItem); const Qt::CheckState checkState = item->checkState(GroupAndNameHierarchyTreeWidgetItem::TREE_COLUMN); const GroupAndNameCheckStateEnum::Enum itemCheckState = GroupAndNameHierarchyTreeWidgetItem::fromQCheckState(checkState); const bool newStatus = (itemCheckState != GroupAndNameCheckStateEnum::UNCHECKED); treeItem->setModelDataSelected(newStatus); updateSelectedAndExpandedCheckboxes(); updateSelectedAndExpandedCheckboxesInOtherViewControllers(); updateGraphics(); } /** * Update graphics and, in some circumstances, surface node coloring. */ void GroupAndNameHierarchyViewController::updateGraphics() { if (m_selectionInvalidatesSurfaceNodeColoring) { EventManager::get()->sendEvent(EventSurfaceColoringInvalidate().getPointer()); } EventManager::get()->sendEvent(EventGraphicsUpdateAllWindows().getPointer()); } /** * Create buttons for all on and off */ QWidget* GroupAndNameHierarchyViewController::createAllOnOffControls() { QLabel* allLabel = new QLabel("All: "); QPushButton* onPushButton = new QPushButton("On"); QObject::connect(onPushButton, SIGNAL(clicked()), this, SLOT(allOnPushButtonClicked())); QPushButton* offPushButton = new QPushButton("Off"); QObject::connect(offPushButton, SIGNAL(clicked()), this, SLOT(allOffPushButtonClicked())); QWidget* w = new QWidget(); QHBoxLayout* layout = new QHBoxLayout(w); layout->addWidget(allLabel); layout->addWidget(onPushButton); layout->addWidget(offPushButton); layout->addStretch(); return w; } /** * Called when all on push button clicked. */ void GroupAndNameHierarchyViewController::allOnPushButtonClicked() { setAllSelected(true); } /** * Called when all off push button clicked. */ void GroupAndNameHierarchyViewController::allOffPushButtonClicked() { setAllSelected(false); } /** * Set selection status of all items. * @param selected * New selection status for all items. */ void GroupAndNameHierarchyViewController::setAllSelected(bool selected) { BrowserTabContent* browserTabContent = GuiManager::get()->getBrowserTabContentForBrowserWindow(m_browserWindowIndex, false); if (browserTabContent != NULL) { const int32_t browserTabIndex = browserTabContent->getTabNumber(); std::vector allModels = getAllModels(); const int32_t numModels = static_cast(allModels.size()); for (int32_t i = 0; i < numModels; i++) { GroupAndNameHierarchyModel* model = allModels[i]; model->setAllSelected(m_displayGroup, browserTabIndex, selected); } updateSelectedAndExpandedCheckboxesInOtherViewControllers(); updateSelectedAndExpandedCheckboxes(); updateGraphics(); } } /** * @return All models in this view controller. */ std::vector GroupAndNameHierarchyViewController::getAllModels() const { std::vector allModels; const int32_t numItems = static_cast(m_treeWidgetItems.size()); for (int32_t i = 0; i < numItems; i++) { GroupAndNameHierarchyTreeWidgetItem* treeItem = m_treeWidgetItems[i]; GroupAndNameHierarchyModel* model = treeItem->getClassAndNameHierarchyModel(); allModels.push_back(model); } return allModels; } /** * Update with border files. * @param borderFiles * The border files. * @param displayGroup * The selected display group. */ void GroupAndNameHierarchyViewController::updateContents(std::vector& borderFiles, const DisplayGroupEnum::Enum displayGroup) { std::vector models; m_displayGroup = displayGroup; std::vector classAndNameHierarchyModels; for (std::vector::iterator iter = borderFiles.begin(); iter != borderFiles.end(); iter++) { BorderFile* bf = *iter; CaretAssert(bf); models.push_back(bf->getGroupAndNameHierarchyModel()); } updateContents(models, DataFileTypeEnum::BORDER, false); } /** * Update with border files. * @param borderFiles * The border files. * @param displayGroup * The selected display group. */ void GroupAndNameHierarchyViewController::updateContents(std::vector& fociFiles, const DisplayGroupEnum::Enum displayGroup) { std::vector models; m_displayGroup = displayGroup; std::vector classAndNameHierarchyModels; for (std::vector::iterator iter = fociFiles.begin(); iter != fociFiles.end(); iter++) { FociFile* ff = *iter; CaretAssert(ff); models.push_back(ff->getGroupAndNameHierarchyModel()); } updateContents(models, DataFileTypeEnum::FOCI, false); } /** * Update with label files. * @param labelFiles * The label files. * @param ciftiLabelFiles * The CIFTI label files. * @param volumeLabelFiles * The volume label files. * @param displayGroup * The selected display group. */ void GroupAndNameHierarchyViewController::updateContents(std::vector& labelFiles, std::vector& ciftiLabelFiles, std::vector& volumeLabelFiles, const DisplayGroupEnum::Enum displayGroup) { std::vector models; m_displayGroup = displayGroup; std::vector classAndNameHierarchyModels; for (std::vector::iterator iter = labelFiles.begin(); iter != labelFiles.end(); iter++) { LabelFile* lf = *iter; CaretAssert(lf); models.push_back(lf->getGroupAndNameHierarchyModel()); } for (std::vector::iterator iter = ciftiLabelFiles.begin(); iter != ciftiLabelFiles.end(); iter++) { CiftiBrainordinateLabelFile* clf = *iter; CaretAssert(clf); models.push_back(clf->getGroupAndNameHierarchyModel()); } for (std::vector::iterator iter = volumeLabelFiles.begin(); iter != volumeLabelFiles.end(); iter++) { VolumeFile* vf = *iter; CaretAssert(vf); models.push_back(vf->getGroupAndNameHierarchyModel()); } updateContents(models, DataFileTypeEnum::LABEL, true); } /** * Create/recreate the tree widget. */ void GroupAndNameHierarchyViewController::createTreeWidget() { /* * Delete and recreate the tree widget * Seems that adding and removing items from tree widget eventually * causes a crash. */ m_treeWidgetItems.clear(); if (m_modelTreeWidget != NULL) { m_modelTreeWidget->blockSignals(true); m_modelTreeWidget->clear(); m_modelTreeWidgetLayout->removeWidget(m_modelTreeWidget); delete m_modelTreeWidget; } m_modelTreeWidget = new WuQTreeWidget(); QObject::connect(m_modelTreeWidget, SIGNAL(itemCollapsed(QTreeWidgetItem*)), this, SLOT(itemWasCollapsed(QTreeWidgetItem*))); QObject::connect(m_modelTreeWidget, SIGNAL(itemExpanded(QTreeWidgetItem*)), this, SLOT(itemWasExpanded(QTreeWidgetItem*))); QObject::connect(m_modelTreeWidget, SIGNAL(itemChanged(QTreeWidgetItem*, int)), this, SLOT(itemWasChanged(QTreeWidgetItem*, int))); m_modelTreeWidgetLayout->addWidget(m_modelTreeWidget); m_modelTreeWidget->blockSignals(false); } /** * Update the content of the view controller. * @param classAndNameHierarchyModels * ClassAndNameHierarchyModels instances for display. * @param allowNamesWithZeroCounts * If true, display names even if the usage count is zero. */ void GroupAndNameHierarchyViewController::updateContents(std::vector& classAndNameHierarchyModels, const DataFileTypeEnum::Enum dataFileType, const bool selectionInvalidatesSurfaceNodeColoring) { m_dataFileType= dataFileType; m_selectionInvalidatesSurfaceNodeColoring = selectionInvalidatesSurfaceNodeColoring; BrowserTabContent* browserTabContent = GuiManager::get()->getBrowserTabContentForBrowserWindow(m_browserWindowIndex, false); CaretAssert(browserTabContent); const int32_t browserTabIndex = browserTabContent->getTabNumber(); /* * May need an update */ bool needUpdate = false; int32_t numberOfModels = static_cast(classAndNameHierarchyModels.size()); /* * Has the number of models changed? */ if (numberOfModels != static_cast(this->m_treeWidgetItems.size())) { needUpdate = true; } // else if (m_displayGroup != m_previousDisplayGroup) { // needUpdate = true; // } // else if (browserTabIndex != m_previousBrowserTabIndex) { // needUpdate = true; // } else { /* * Have the displayed models changed? */ for (int32_t iModel = 0; iModel < numberOfModels; iModel++) { if (classAndNameHierarchyModels[iModel] != this->m_treeWidgetItems[iModel]->getClassAndNameHierarchyModel()) { needUpdate = true; break; } else if (classAndNameHierarchyModels[iModel]->getChildren().size() != this->m_treeWidgetItems[iModel]->getClassAndNameHierarchyModel()->getChildren().size()) { needUpdate = true; break; } } /* * Has the model's content been altered? */ for (int32_t iModel = 0; iModel < numberOfModels; iModel++) { if (classAndNameHierarchyModels[iModel]->needsUserInterfaceUpdate(m_displayGroup, browserTabIndex)) { needUpdate = true; break; } } } m_modelTreeWidget->blockSignals(true); if (needUpdate) { createTreeWidget(); m_modelTreeWidget->blockSignals(true); // gets reset /* * Copy the models */ for (int32_t iModel = 0; iModel < numberOfModels; iModel++) { GroupAndNameHierarchyTreeWidgetItem* modelItem = new GroupAndNameHierarchyTreeWidgetItem(m_displayGroup, browserTabIndex, classAndNameHierarchyModels[iModel]); this->m_treeWidgetItems.push_back(modelItem); m_modelTreeWidget->addTopLevelItem(modelItem); } } else { for (int32_t iModel = 0; iModel < numberOfModels; iModel++) { this->m_treeWidgetItems[iModel]->updateIconColorIncludingChildren(); } } updateSelectedAndExpandedCheckboxes(); m_previousBrowserTabIndex = browserTabIndex; m_previousDisplayGroup = m_displayGroup; m_modelTreeWidget->blockSignals(false); if (needUpdate) { m_modelTreeWidget->resizeToFitContent(); } } /** * Update the selection and expansion controls. */ void GroupAndNameHierarchyViewController::updateSelectedAndExpandedCheckboxes() { if (m_modelTreeWidget == NULL) { return; } m_modelTreeWidget->blockSignals(true); BrowserTabContent* browserTabContent = GuiManager::get()->getBrowserTabContentForBrowserWindow(m_browserWindowIndex, false); CaretAssert(browserTabContent); const int32_t browserTabIndex = browserTabContent->getTabNumber(); const int32_t numberOfModels = static_cast(this->m_treeWidgetItems.size()); for (int32_t iModel = 0; iModel < numberOfModels; iModel++) { m_treeWidgetItems[iModel]->updateSelections(m_displayGroup, browserTabIndex); } m_modelTreeWidget->blockSignals(false); } /** * Update the selection and expansion controls in other view controllers * that are set to the same display group (not tab) and contain the * same type of data. */ void GroupAndNameHierarchyViewController::updateSelectedAndExpandedCheckboxesInOtherViewControllers() { if (m_displayGroup == DisplayGroupEnum::DISPLAY_GROUP_TAB) { return; } for (std::set::iterator iter = s_allViewControllers.begin(); iter != s_allViewControllers.end(); iter++) { GroupAndNameHierarchyViewController* vc = *iter; if (vc != this) { if (vc->m_displayGroup == m_displayGroup) { if (vc->m_dataFileType == m_dataFileType) { vc->updateSelectedAndExpandedCheckboxes(); } } } } } connectome-workbench-1.2.3+git41-gc4c6c90/src/GuiQt/GroupAndNameHierarchyViewController.h000066400000000000000000000106321300200146000310260ustar00rootroot00000000000000#ifndef __CLASS_AND_NAME_HIERARCHY_VIEW_CONTROLLER__H_ #define __CLASS_AND_NAME_HIERARCHY_VIEW_CONTROLLER__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 "DataFileTypeEnum.h" #include "DisplayGroupEnum.h" class QTreeWidgetItem; class QVBoxLayout; namespace caret { class BorderFile; class CiftiBrainordinateLabelFile; class FociFile; class LabelFile; class GroupAndNameHierarchyModel; class GroupAndNameHierarchyTreeWidgetItem; class VolumeFile; class WuQTreeWidget; class GroupAndNameHierarchyViewController : public QWidget { Q_OBJECT public: GroupAndNameHierarchyViewController(const int32_t browserWindowIndex, QWidget* parent = 0); virtual ~GroupAndNameHierarchyViewController(); void updateContents(std::vector& borderFiles, const DisplayGroupEnum::Enum displayGroup); void updateContents(std::vector& fociFiles, const DisplayGroupEnum::Enum displayGroup); void updateContents(std::vector& labelFiles, std::vector& ciftiLabelFiles, std::vector& volumeLabelFiles, const DisplayGroupEnum::Enum displayGroup); private slots: void allOnPushButtonClicked(); void allOffPushButtonClicked(); void itemWasCollapsed(QTreeWidgetItem* item); void itemWasExpanded(QTreeWidgetItem* item); void itemWasChanged(QTreeWidgetItem* item, int column); private: GroupAndNameHierarchyViewController(const GroupAndNameHierarchyViewController&); GroupAndNameHierarchyViewController& operator=(const GroupAndNameHierarchyViewController&); void updateContents(std::vector& modelItems, const DataFileTypeEnum::Enum dataFileType, const bool selectionInvalidatesSurfaceNodeColoring); std::vector getAllModels() const; void updateGraphics(); void updateSelectedAndExpandedCheckboxes(); void updateSelectedAndExpandedCheckboxesInOtherViewControllers(); void createTreeWidget(); QWidget* createAllOnOffControls(); void setAllSelected(bool selected); DataFileTypeEnum::Enum m_dataFileType; /** Contains pointers to items managed by Qt, so do not delete content */ std::vector m_treeWidgetItems; QVBoxLayout* m_modelTreeWidgetLayout; WuQTreeWidget* m_modelTreeWidget; int32_t m_browserWindowIndex; DisplayGroupEnum::Enum m_displayGroup; DisplayGroupEnum::Enum m_previousDisplayGroup; int32_t m_previousBrowserTabIndex; bool m_selectionInvalidatesSurfaceNodeColoring; static std::set s_allViewControllers; }; #ifdef __CLASS_AND_NAME_HIERARCHY_VIEW_CONTROLLER_DECLARE__ std::set GroupAndNameHierarchyViewController::s_allViewControllers; #endif // __CLASS_AND_NAME_HIERARCHY_VIEW_CONTROLLER_DECLARE__ } // namespace #endif //__CLASS_AND_NAME_HIERARCHY_VIEW_CONTROLLER__H_ connectome-workbench-1.2.3+git41-gc4c6c90/src/GuiQt/GuiManager.cxx000066400000000000000000003204171300200146000243470ustar00rootroot00000000000000 /*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 #define __GUI_MANAGER_DEFINE__ #include "GuiManager.h" #undef __GUI_MANAGER_DEFINE__ #include "AnnotationFile.h" #include "Brain.h" #include "BrainBrowserWindow.h" #include "BrainOpenGL.h" #include "BrainStructure.h" #include "BrowserTabContent.h" #include "BugReportDialog.h" #include "CaretAssert.h" #include "CaretLogger.h" #include "CaretMappableDataFile.h" #include "ChartingDataManager.h" #include "CiftiConnectivityMatrixDataFileManager.h" #include "CiftiFiberTrajectoryManager.h" #include "CiftiConnectivityMatrixParcelFile.h" #include "CiftiScalarDataSeriesFile.h" #include "ClippingPlanesDialog.h" #include "CursorDisplayScoped.h" #include "CursorManager.h" #include "CustomViewDialog.h" #include "DataFileException.h" #include "ElapsedTimer.h" #include "EventAlertUser.h" #include "EventAnnotationGetDrawnInWindow.h" #include "EventBrowserTabGetAll.h" #include "EventBrowserWindowNew.h" #include "EventGraphicsUpdateAllWindows.h" #include "EventGraphicsUpdateOneWindow.h" #include "EventHelpViewerDisplay.h" #include "EventIdentificationHighlightLocation.h" #include "EventMacDockMenuUpdate.h" #include "EventManager.h" #include "EventMapYokingSelectMap.h" #include "EventModelGetAll.h" #include "EventOperatingSystemRequestOpenDataFile.h" #include "EventOverlaySettingsEditorDialogRequest.h" #include "EventPaletteColorMappingEditorDialogRequest.h" #include "EventProgressUpdate.h" #include "EventSurfaceColoringInvalidate.h" #include "EventUpdateInformationWindows.h" #include "EventUserInterfaceUpdate.h" #include "FociPropertiesEditorDialog.h" #include "GapsAndMarginsDialog.h" #include "HelpViewerDialog.h" #include "IdentifiedItemNode.h" #include "IdentifiedItemVoxel.h" #include "IdentificationManager.h" #include "IdentificationStringBuilder.h" #include "IdentifyBrainordinateDialog.h" #include "ImageFile.h" #include "ImageCaptureDialog.h" #include "InformationDisplayDialog.h" #include "OverlaySettingsEditorDialog.h" #include "MacDockMenu.h" #include "MovieDialog.h" #include "PaletteColorMappingEditorDialog.h" #include "PreferencesDialog.h" #include "SceneAttributes.h" #include "SceneClass.h" #include "SceneClassArray.h" #include "SceneDialog.h" #include "SceneWindowGeometry.h" #include "SelectionManager.h" #include "SelectionItemChartMatrix.h" #include "SelectionItemCiftiConnectivityMatrixRowColumn.h" #include "SelectionItemSurfaceNode.h" #include "SelectionItemSurfaceNodeIdentificationSymbol.h" #include "SelectionItemVoxel.h" #include "SelectionItemVoxelIdentificationSymbol.h" #include "SessionManager.h" #include "SpecFile.h" #include "SpecFileManagementDialog.h" #include "SurfacePropertiesEditorDialog.h" #include "Surface.h" #include "TileTabsConfigurationDialog.h" #include "VolumeMappableInterface.h" #include "WuQMessageBox.h" //#include "WuQWebView.h" #include "WuQtUtilities.h" #include "CaretAssert.h" using namespace caret; /** * \defgroup GuiQt */ /** * \class caret::GuiManager * \brief Top level class for passing events to Gui widgets belonging to workbench window * \ingroup GuiQt */ /** * Constructor. * @param parent * Parent of this object. */ GuiManager::GuiManager(QObject* parent) : QObject(parent) { /* * This constructor should be called only once. * When the first instance of GuiManager is created, * singletonGuiManager will be NULL. */ CaretAssertMessage((GuiManager::singletonGuiManager == NULL), "There should never be more than one instance of GuiManager."); } /** * Initialize the GUI manager. * * NOTE: This method is NOT called from the constructor. * If there are problems loading some of the images, there will * be calls to GuiManager::get() which results in recursive calls. */ void GuiManager::initializeGuiManager() { this->nameOfApplication = "Connectome Workbench"; //this->brainOpenGL = NULL; this->allowBrowserWindowsToCloseWithoutConfirmation = false; m_bugReportDialog = NULL; m_clippingPlanesDialog = NULL; m_customViewDialog = NULL; m_gapsAndMarginsDialog = NULL; this->imageCaptureDialog = NULL; this->movieDialog = NULL; m_informationDisplayDialog = NULL; m_identifyBrainordinateDialog = NULL; this->preferencesDialog = NULL; this->connectomeDatabaseWebView = NULL; m_helpViewerDialog = NULL; m_paletteColorMappingEditor = NULL; this->sceneDialog = NULL; m_surfacePropertiesEditorDialog = NULL; m_tileTabsConfigurationDialog = NULL; this->cursorManager = new CursorManager(); /* * Information window. */ QIcon infoDisplayIcon; const bool infoDisplayIconValid = WuQtUtilities::loadIcon(":/ToolBar/info.png", infoDisplayIcon); m_informationDisplayDialogEnabledAction = WuQtUtilities::createAction("Information...", "Enables display of the Information Window\n" "when new information is available", this, this, SLOT(showHideInfoWindowSelected(bool))); if (infoDisplayIconValid) { m_informationDisplayDialogEnabledAction->setIcon(infoDisplayIcon); m_informationDisplayDialogEnabledAction->setIconVisibleInMenu(false); } else { m_informationDisplayDialogEnabledAction->setIconText("Info"); } m_informationDisplayDialogEnabledAction->blockSignals(true); m_informationDisplayDialogEnabledAction->setCheckable(true); m_informationDisplayDialogEnabledAction->setChecked(true); this->showHideInfoWindowSelected(m_informationDisplayDialogEnabledAction->isChecked()); m_informationDisplayDialogEnabledAction->setIconText("Info"); m_informationDisplayDialogEnabledAction->blockSignals(false); /* * Identify brainordinate window */ QIcon identifyDisplayIcon; const bool identifyDisplayIconValid = WuQtUtilities::loadIcon(":/ToolBar/identify.png", identifyDisplayIcon); m_identifyBrainordinateDialogEnabledAction = WuQtUtilities::createAction("Identify...", "Enables display of the Identify Brainordinate Window", this, this, SLOT(showIdentifyBrainordinateDialogActionToggled(bool))); if (identifyDisplayIconValid) { m_identifyBrainordinateDialogEnabledAction->setIcon(identifyDisplayIcon); m_identifyBrainordinateDialogEnabledAction->setIconVisibleInMenu(false); } else { m_identifyBrainordinateDialogEnabledAction->setIconText("ID"); } m_identifyBrainordinateDialogEnabledAction->blockSignals(true); m_identifyBrainordinateDialogEnabledAction->setCheckable(true); m_identifyBrainordinateDialogEnabledAction->setChecked(false); m_identifyBrainordinateDialogEnabledAction->blockSignals(false); /* * Scene dialog action */ m_sceneDialogDisplayAction = WuQtUtilities::createAction("Scenes...", "Show/Hide the Scenes Window", this, this, SLOT(sceneDialogDisplayActionToggled(bool))); QIcon clapBoardIcon; const bool clapBoardIconValid = WuQtUtilities::loadIcon(":/ToolBar/clapboard.png", clapBoardIcon); if (clapBoardIconValid) { m_sceneDialogDisplayAction->setIcon(clapBoardIcon); m_sceneDialogDisplayAction->setIconVisibleInMenu(false); } else { m_sceneDialogDisplayAction->setIconText("Scenes"); } m_sceneDialogDisplayAction->blockSignals(true); m_sceneDialogDisplayAction->setCheckable(true); m_sceneDialogDisplayAction->setChecked(false); m_sceneDialogDisplayAction->blockSignals(false); /* * Help dialog action */ m_helpViewerDialogDisplayAction = WuQtUtilities::createAction("Workbench Help...", "Show/Hide the Help Window", QKeySequence::HelpContents, this, this, SLOT(showHelpDialogActionToggled(bool))); QIcon helpIcon; const bool helpIconValid = WuQtUtilities::loadIcon(":/ToolBar/help.png", helpIcon); if (helpIconValid) { m_helpViewerDialogDisplayAction->setIcon(helpIcon); m_helpViewerDialogDisplayAction->setIconVisibleInMenu(false); } else { m_helpViewerDialogDisplayAction->setIconText("?"); } m_helpViewerDialogDisplayAction->blockSignals(true); m_helpViewerDialogDisplayAction->setCheckable(true); m_helpViewerDialogDisplayAction->setChecked(false); m_helpViewerDialogDisplayAction->blockSignals(false); 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_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_UPDATE_INFORMATION_WINDOWS); EventManager::get()->addEventListener(this, EventTypeEnum::EVENT_USER_INTERFACE_UPDATE); } /** * Destructor. */ GuiManager::~GuiManager() { EventManager::get()->removeAllEventsFromListener(this); delete this->cursorManager; if (this->connectomeDatabaseWebView != NULL) { CaretAssertMessage(0, "Need to uncomment out line below if webkit is renabled"); //delete this->connectomeDatabaseWebView; } FociPropertiesEditorDialog::deleteStaticMembers(); for (std::set::iterator iter = m_parentlessNonModalDialogs.begin(); iter != m_parentlessNonModalDialogs.end(); iter++) { QWidget* w = *iter; delete w; } m_parentlessNonModalDialogs.clear(); } /** * Get the GUI Manager. */ GuiManager* GuiManager::get() { CaretAssertMessage((GuiManager::singletonGuiManager != NULL), "GuiManager::singletonGuiManager has not been created. Must call GuiManager::createGuiManager()"); return GuiManager::singletonGuiManager; } /* * Create the singleton GUI Manager. */ void GuiManager::createGuiManager() { CaretAssertMessage((GuiManager::singletonGuiManager == NULL), "GUI manager has already been created."); GuiManager::singletonGuiManager = new GuiManager(); GuiManager::singletonGuiManager->initializeGuiManager(); WuQtUtilities::sendListOfResourcesToCaretLogger(); } /* * Delete the singleton GUI Manager. */ void GuiManager::deleteGuiManager() { CaretAssertMessage((GuiManager::singletonGuiManager != NULL), "GUI manager does not exist, cannot delete it."); delete GuiManager::singletonGuiManager; GuiManager::singletonGuiManager = NULL; } /** * Beep to alert the user. */ void GuiManager::beep() { QApplication::beep(); } /** * @return The brain. */ Brain* 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 * changes to files will be lost and the application will exit. * If there is more than one brain browser window open and the * window being closed contains more than one tab, the user will * be warned. * * @param brainBrowserWindow * Brain browser window that will be closed. * @param numberOfOpenTabs * Number of tabs in window. * @return * True if window should be closed, else false. */ bool GuiManager::allowBrainBrowserWindowToClose(BrainBrowserWindow* brainBrowserWindow, const int32_t numberOfOpenTabs) { bool isBrowserWindowAllowedToClose = false; if (this->allowBrowserWindowsToCloseWithoutConfirmation) { isBrowserWindowAllowedToClose = true; } else { if (this->getNumberOfOpenBrainBrowserWindows() > 1) { /* * Warn if multiple tabs in window */ if (numberOfOpenTabs > 1) { QString tabMessage = QString::number(numberOfOpenTabs) + " tabs are open."; isBrowserWindowAllowedToClose = WuQMessageBox::warningCloseCancel(brainBrowserWindow, "Are you sure you want to close this window?", tabMessage); } else { isBrowserWindowAllowedToClose = true; } } else { isBrowserWindowAllowedToClose = this->exitProgram(brainBrowserWindow); } } if (isBrowserWindowAllowedToClose) { for (int32_t i = 0; i < static_cast(m_brainBrowserWindows.size()); i++) { if (m_brainBrowserWindows[i] == brainBrowserWindow) { m_brainBrowserWindows[i] = NULL; // must set to NULL BEFORE reparenting non-modal dialogs so they dont' see it this->reparentNonModalDialogs(brainBrowserWindow); } } EventManager::get()->sendEvent(EventMacDockMenuUpdate().getPointer()); } return isBrowserWindowAllowedToClose; } /** * Get the number of brain browser windows. * * @return Number of brain browser windows that are valid. */ int32_t GuiManager::getNumberOfOpenBrainBrowserWindows() const { int32_t numberOfWindows = 0; for (int32_t i = 0; i < static_cast(m_brainBrowserWindows.size()); i++) { if (m_brainBrowserWindows[i] != NULL) { numberOfWindows++; } } return numberOfWindows; } /** * Get all of the brain browser windows. * * @return * Vector containing all open brain browser windows. */ std::vector GuiManager::getAllOpenBrainBrowserWindows() const { std::vector windows; int32_t numWindows = static_cast(m_brainBrowserWindows.size()); for (int32_t i = 0; i < numWindows; i++) { if (m_brainBrowserWindows[i] != NULL) { windows.push_back(m_brainBrowserWindows[i]); } } return windows; } /** * Get all of the brain browser window indices * * @return * Vector containing all open brain browser window indices. */ std::vector GuiManager::getAllOpenBrainBrowserWindowIndices() const { std::vector windowIndices; int32_t numWindows = static_cast(m_brainBrowserWindows.size()); for (int32_t i = 0; i < numWindows; i++) { if (m_brainBrowserWindows[i] != NULL) { windowIndices.push_back(i); } } return windowIndices; } /** * @return Return the active browser window. If no browser window is active, * the browser window with the lowest index is returned. If no browser * window is open (which likely should never occur), NULL is returned. * * To verify that the returned window was the active window, call its * "isActiveWindow()" method. */ BrainBrowserWindow* GuiManager::getActiveBrowserWindow() const { BrainBrowserWindow* firstWindowFound = NULL; int32_t numWindows = static_cast(m_brainBrowserWindows.size()); for (int32_t i = 0; i < numWindows; i++) { BrainBrowserWindow* bbw = m_brainBrowserWindows[i]; if (bbw != NULL) { if (firstWindowFound == NULL) { firstWindowFound = bbw; } if (bbw->isActiveWindow()) { return bbw; } } } return firstWindowFound; } /** * Get the brain browser window with the given window index. * Note that as browser windows are opened or closed, a window's * index NEVER changes. Thus, a NULL value may be returned for * a window index referring to a window that was closed. * * @param browserWindowIndex * Index of the window. * @return * Pointer to window at given index or NULL in cases where * the window was closed. */ BrainBrowserWindow* GuiManager::getBrowserWindowByWindowIndex(const int32_t browserWindowIndex) { if (browserWindowIndex < static_cast(m_brainBrowserWindows.size())) { return m_brainBrowserWindows[browserWindowIndex]; } return NULL; } /** * Create a new BrainBrowser Window. * @param parent * Optional parent that is used only for window placement. * @param browserTabContent * Optional tab for initial windwo tab. * @param createDefaultTabs * If true, create the default tabs in the new window. */ BrainBrowserWindow* GuiManager::newBrainBrowserWindow(QWidget* parent, BrowserTabContent* browserTabContent, const bool createDefaultTabs) { /* * 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) { return NULL; } int32_t windowIndex = -1; int32_t numWindows = static_cast(m_brainBrowserWindows.size()); for (int32_t i = 0; i < numWindows; i++) { if (m_brainBrowserWindows[i] == NULL) { windowIndex = i; break; } } 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); } else { bbw = new BrainBrowserWindow(windowIndex, browserTabContent, tabsMode); m_brainBrowserWindows[windowIndex] = bbw; } if (parent != NULL) { WuQtUtilities::moveWindowToOffset(parent, bbw, 20, 20); } bbw->show(); bbw->resetGraphicsWidgetMinimumSize(); return bbw; } /** * Test for modified data files. * * @param testModifiedMode * Mode of testing for modified files. * @param textMessageOut * Message displayed at top of dialog. * @param modifiedFilesMessageOut * If there are any modified files, this will contain information * about the modified files. * @return * True if there are modified files and the warning message is valid, * else false. */ bool GuiManager::testForModifiedFiles(const TestModifiedMode testModifiedMode, AString& textMessageOut, AString& modifiedFilesMessageOut) const { textMessageOut.clear(); modifiedFilesMessageOut.clear(); /* * Exclude all * Connectivity Files */ std::vector dataFileTypesToExclude; dataFileTypesToExclude.push_back(DataFileTypeEnum::CONNECTIVITY_DENSE); dataFileTypesToExclude.push_back(DataFileTypeEnum::CONNECTIVITY_DENSE_DYNAMIC); dataFileTypesToExclude.push_back(DataFileTypeEnum::CONNECTIVITY_FIBER_ORIENTATIONS_TEMPORARY); dataFileTypesToExclude.push_back(DataFileTypeEnum::CONNECTIVITY_FIBER_TRAJECTORY_TEMPORARY); switch (testModifiedMode) { case TEST_FOR_MODIFIED_FILES_MODE_FOR_EXIT: break; case TEST_FOR_MODIFIED_FILES_MODE_FOR_SCENE_ADD: dataFileTypesToExclude.push_back(DataFileTypeEnum::SCENE); dataFileTypesToExclude.push_back(DataFileTypeEnum::SPECIFICATION); break; case TEST_FOR_MODIFIED_FILES_MODE_FOR_SCENE_SHOW: dataFileTypesToExclude.push_back(DataFileTypeEnum::SCENE); break; } /* * Are files modified? */ std::vector allModifiedDataFiles; getBrain()->getAllModifiedFiles(dataFileTypesToExclude, allModifiedDataFiles); std::vector modifiedDataFiles; std::vector paletteModifiedDataFiles; for (std::vector::iterator allModFilesIter = allModifiedDataFiles.begin(); allModFilesIter != allModifiedDataFiles.end(); allModFilesIter++) { CaretDataFile* file = *allModFilesIter; CaretAssert(file); switch (testModifiedMode) { case TEST_FOR_MODIFIED_FILES_MODE_FOR_EXIT: modifiedDataFiles.push_back(file); break; case TEST_FOR_MODIFIED_FILES_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); } else { modifiedDataFiles.push_back(file); } } break; case TEST_FOR_MODIFIED_FILES_MODE_FOR_SCENE_SHOW: modifiedDataFiles.push_back(file); break; } } const int32_t modFileCount = static_cast(modifiedDataFiles.size()); const int32_t paletteModFileCount = static_cast(paletteModifiedDataFiles.size()); /* * Are there scene annotations ? */ const CaretDataFile* sceneAnnotationFile = getBrain()->getSceneAnnotationFile(); bool sceneAnnotationsModifiedFlag = sceneAnnotationFile->isModified(); switch (testModifiedMode) { case TEST_FOR_MODIFIED_FILES_MODE_FOR_EXIT: break; case TEST_FOR_MODIFIED_FILES_MODE_FOR_SCENE_ADD: /* * Do not need to notify about modified scene annotations * since scene annotations are saved to the scene */ sceneAnnotationsModifiedFlag = false; break; case TEST_FOR_MODIFIED_FILES_MODE_FOR_SCENE_SHOW: break; } if ((modFileCount > 0) || sceneAnnotationsModifiedFlag || paletteModFileCount) { /* * Display dialog allowing user to save files (goes to Save/Manage * Files dialog), exit without saving, or cancel. */ 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: textMessageOut = "Do you want to continue creating the scene?"; 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()); } infoTextMsg.append("\n"); } 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"); for (std::vector::iterator iter = paletteModifiedDataFiles.begin(); iter != paletteModifiedDataFiles.end(); iter++) { const CaretDataFile* cdf = *iter; infoTextMsg.appendWithNewLine(" " + cdf->getFileNameNoPath()); } infoTextMsg.append("\n"); } if (sceneAnnotationsModifiedFlag) { infoTextMsg.appendWithNewLine("Scene annotations are modified."); } modifiedFilesMessageOut = infoTextMsg; return true; } return false; } /** * Exit the program. * @param * Parent over which dialogs are displayed for saving/verifying. * return * true if application should exit, else false. */ bool GuiManager::exitProgram(BrainBrowserWindow* parent) { bool okToExit = false; AString textMessage; AString modifiedFilesMessage; if (testForModifiedFiles(TEST_FOR_MODIFIED_FILES_MODE_FOR_EXIT, textMessage, modifiedFilesMessage)) { modifiedFilesMessage.appendWithNewLine(""); QMessageBox quitDialog(QMessageBox::Warning, "Exit Workbench", textMessage, QMessageBox::NoButton, parent); quitDialog.setInformativeText(modifiedFilesMessage); QPushButton* saveButton = quitDialog.addButton("Save...", QMessageBox::AcceptRole); saveButton->setToolTip("Display manage files window to save files"); QPushButton* dontSaveButton = quitDialog.addButton("Don't Save", QMessageBox::DestructiveRole); dontSaveButton->setToolTip("Do not save changes and exit."); QPushButton* cancelButton = quitDialog.addButton("Cancel", QMessageBox::RejectRole); quitDialog.setDefaultButton(saveButton); quitDialog.setEscapeButton(cancelButton); quitDialog.exec(); const QAbstractButton* clickedButton = quitDialog.clickedButton(); if (clickedButton == saveButton) { if (SpecFileManagementDialog::runSaveFilesDialogWhileQuittingWorkbench(this->getBrain(), parent)) { okToExit = true; } } else if (clickedButton == dontSaveButton) { okToExit = true; } else if (clickedButton == cancelButton) { /* nothing */ } else { CaretAssert(0); } } else { const AString textMsg("Exit Workbench?"); QMessageBox quitDialog(QMessageBox::Warning, "Exit Workbench", textMsg, QMessageBox::NoButton, parent); if (SceneDialog::isInformUserAboutScenesOnExit()) { const AString infoTextMsg("Would you like to save your Workbench windows in scene file " "so you can easily pick up where you left off?" "

" "Click the Show Details button for " "more information."); const AString detailTextMsg("Scenes allow one to regenerate exactly what is displayed in " "Workbench. This can be useful in these and other situations:" "\n\n" " * During manuscript preparation to restore Workbench to match " "a previously generated figure (image capture)." "\n\n" " * When returning to this dataset for further analysis." "\n\n" " * When sharing data sets with others to provide a particular " "view of a surface/volume with desired data (overlay and feature) " "selections."); quitDialog.setInformativeText(infoTextMsg); quitDialog.setDetailedText(detailTextMsg); } QPushButton* exitButton = quitDialog.addButton("Exit", QMessageBox::AcceptRole); QPushButton* cancelButton = quitDialog.addButton("Cancel", QMessageBox::RejectRole); quitDialog.setDefaultButton(exitButton); quitDialog.setEscapeButton(cancelButton); quitDialog.exec(); const QAbstractButton* clickedButton = quitDialog.clickedButton(); if (clickedButton == exitButton) { okToExit = true; } else if (clickedButton == cancelButton) { /* Nothing */ } else { CaretAssert(0); } } if (okToExit) { std::vector bws = this->getAllOpenBrainBrowserWindows(); for (int i = 0; i < static_cast(bws.size()); i++) { bws[i]->deleteLater(); } QCoreApplication::instance()->quit(); } return okToExit; } /** * Show the Open Spec File Dialog with the given spec file. * * @param specFile * SpecFile displayed in the dialog. * @param browserWindow * Window on which dialog is displayed. * @return * True if user opened spec file, else false. */ bool GuiManager::processShowOpenSpecFileDialog(SpecFile* specFile, BrainBrowserWindow* browserWindow) { return SpecFileManagementDialog::runOpenSpecFileDialog(getBrain(), specFile, browserWindow); } /** * Show the Save/Manage Files Dialog. * * @param browserWindow * Window on which dialog is displayed. */ void GuiManager::processShowSaveManageFilesDialog(BrainBrowserWindow* browserWindow) { SpecFileManagementDialog::runManageFilesDialog(getBrain(), browserWindow); } /** * Get the model displayed in the given browser window. * @param browserWindowIndex * Index of browser window. * @return * Model in the browser window. May be NULL if no data loaded. */ Model* GuiManager::getModelInBrowserWindow(const int32_t browserWindowIndex) { BrowserTabContent* browserTabContent = getBrowserTabContentForBrowserWindow(browserWindowIndex, true); Model* model = NULL; if (browserTabContent != NULL) { model = browserTabContent->getModelForDisplay(); } return model; } /** * Get the browser tab content in a browser window. * @param browserWindowIndex * Index of browser window. * @param allowInvalidBrowserWindowIndex * In some instance, such as GUI construction or destruction, the window is not * fully created or deleted, thus "this->brainBrowserWindows" is invalid for * the given index. If this parameter is true, NULL will be * returned in this case. * @return * Browser tab content in the browser window. Value may be NULL * is allowInvalidBrowserWindowIndex is true */ BrowserTabContent* GuiManager::getBrowserTabContentForBrowserWindow(const int32_t browserWindowIndex, const bool allowInvalidBrowserWindowIndex) { if (allowInvalidBrowserWindowIndex) { if (browserWindowIndex >= static_cast(m_brainBrowserWindows.size())) { return NULL; } } CaretAssertVectorIndex(m_brainBrowserWindows, browserWindowIndex); BrainBrowserWindow* browserWindow = m_brainBrowserWindows[browserWindowIndex]; if (allowInvalidBrowserWindowIndex) { if (browserWindow == NULL) { return NULL; } } CaretAssert(browserWindow); BrowserTabContent* tabContent = browserWindow->getBrowserTabContent(); return tabContent; } /** * Called when bring all windows to front is selected. */ void GuiManager::processBringAllWindowsToFront() { for (int32_t i = 0; i < static_cast(m_brainBrowserWindows.size()); i++) { if (m_brainBrowserWindows[i] != NULL) { m_brainBrowserWindows[i]->show(); m_brainBrowserWindows[i]->activateWindow(); } } for (std::set::iterator iter = this->nonModalDialogs.begin(); iter != this->nonModalDialogs.end(); iter++) { QWidget* w = *iter; if (w->isVisible()) { w->raise(); } } for (std::set::iterator iter = m_parentlessNonModalDialogs.begin(); iter != m_parentlessNonModalDialogs.end(); iter++) { QWidget* w = *iter; if (w->isVisible()) { w->raise(); } } } /** * Called to tile the windows (arrange browser windows in a grid). */ void GuiManager::processTileWindows() { std::vector windows = getAllOpenBrainBrowserWindows(); const int32_t numWindows = static_cast(windows.size()); if (numWindows <= 1) { return; } QDesktopWidget* dw = QApplication::desktop(); const int32_t numScreens = dw->screenCount(); const int32_t windowsPerScreen = std::max(numWindows / numScreens, 1); /** * Determine the number of rows and columns for the montage. * Since screen width typically exceeds height, always have * columns greater than or equal to rows. */ int32_t numRows = (int)std::sqrt((double)windowsPerScreen); int32_t numCols = numRows; int32_t row2 = numRows * numRows; if (row2 < numWindows) { numCols++; } if ((numRows * numCols) < numWindows) { numRows++; } AString windowInfo("Tiled Windows"); /* * Arrange models left-to-right and top-to-bottom. * Note that origin is top-left corner. */ int32_t windowIndex = 0; for (int32_t iScreen = 0; iScreen < numScreens; iScreen++) { const QRect rect = dw->availableGeometry(iScreen); const int screenX = rect.x(); const int screenY = rect.y(); const int screenWidth = rect.width(); const int screenHeight = rect.height(); const int32_t windowWidth = screenWidth / numCols; const int32_t windowHeight = screenHeight / numRows; int32_t windowX = 0; int32_t windowY = screenY; for (int32_t iRow = 0; iRow < numRows; iRow++) { windowX = screenX; for (int32_t iCol = 0; iCol < numCols; iCol++) { windows[windowIndex]->setGeometry(windowX, windowY, windowWidth, windowHeight); windowX += windowWidth; QString info = (" Window: " + windows[windowIndex]->windowTitle() + " screen/x/y/w/h (" + QString::number(iScreen) + ", " + QString::number(windowX) + ", " + QString::number(windowY) + ", " + QString::number(windowWidth) + ", " + QString::number(windowHeight) + ")"); windowInfo.appendWithNewLine(info); windowIndex++; if (windowIndex >= numWindows) { /* * No more windows to place. * Containing loops would cause a crash. */ CaretLogFine(windowInfo); return; } } windowY += windowHeight; } } CaretLogFine(windowInfo); } /** * @return Name of the application. */ QString GuiManager::applicationName() const { return this->nameOfApplication; } /** * Receive events from the event manager. * * @param event * Event sent by event manager. */ void GuiManager::receiveEvent(Event* event) { if (event->getEventType() == EventTypeEnum::EVENT_ALERT_USER) { EventAlertUser* alertUserEvent = dynamic_cast(event); const AString message = alertUserEvent->getMessage(); BrainBrowserWindow* bbw = getActiveBrowserWindow(); CaretAssert(bbw); WuQMessageBox::errorOk(bbw, message); } else if (event->getEventType() == EventTypeEnum::EVENT_ANNOTATION_GET_DRAWN_IN_WINDOW) { EventAnnotationGetDrawnInWindow* annGetEvent = dynamic_cast(event); CaretAssert(annGetEvent); const int32_t windowIndex = annGetEvent->getWindowIndex(); Brain* brain = getBrain(); std::vector allAnnotationFiles; brain->getAllAnnotationFilesIncludingSceneAnnotationFile(allAnnotationFiles); /* * Clear "drawn in window status" for all annotations */ for (std::vector::iterator fileIter = allAnnotationFiles.begin(); fileIter != allAnnotationFiles.end(); fileIter++) { (*fileIter)->clearAllAnnotationsDrawnInWindowStatus(); } /* * Draw the given window */ EventManager::get()->sendEvent(EventGraphicsUpdateOneWindow(windowIndex).getPointer()); /* * Find annotations that were drawn in the given window. */ for (std::vector::iterator fileIter = allAnnotationFiles.begin(); fileIter != allAnnotationFiles.end(); fileIter++) { std::vector annotations; (*fileIter)->getAllAnnotationWithDrawnInWindowStatusSet(windowIndex, annotations); annGetEvent->addAnnotations(annotations); } } else if (event->getEventType() == EventTypeEnum::EVENT_BROWSER_WINDOW_NEW) { EventBrowserWindowNew* eventNewBrowser = dynamic_cast(event); CaretAssert(eventNewBrowser); BrainBrowserWindow* bbw = this->newBrainBrowserWindow(eventNewBrowser->getParent(), eventNewBrowser->getBrowserTabContent(), true); if (bbw == NULL) { eventNewBrowser->setErrorMessage("Workench is exhausted. It cannot create any more windows."); eventNewBrowser->setEventProcessed(); return; } eventNewBrowser->setBrowserWindowCreated(bbw); eventNewBrowser->setEventProcessed(); /* * Initialize the size of the window */ const int w = bbw->width(); const int preferredMaxHeight = (WuQtUtilities::isSmallDisplay() ? 550 : 850); const int h = std::min(bbw->height(), preferredMaxHeight); bbw->resize(w, h); EventManager::get()->sendEvent(EventMacDockMenuUpdate().getPointer()); } else if (event->getEventType() == EventTypeEnum::EVENT_MAC_DOCK_MENU_UPDATE) { EventMacDockMenuUpdate* macDockMenuEvent = dynamic_cast(event); CaretAssert(event); MacDockMenu::createUpdateMacDockMenu(); macDockMenuEvent->setEventProcessed(); } else if (event->getEventType() == EventTypeEnum::EVENT_UPDATE_INFORMATION_WINDOWS) { EventUpdateInformationWindows* infoEvent = dynamic_cast(event); CaretAssert(infoEvent); bool showInfoDialog = infoEvent->isImportant(); if (showInfoDialog) { this->processShowInformationDisplayDialog(false); } infoEvent->setEventProcessed(); } else if (event->getEventType() == EventTypeEnum::EVENT_OVERLAY_SETTINGS_EDITOR_SHOW) { EventOverlaySettingsEditorDialogRequest* mapEditEvent = dynamic_cast(event); CaretAssert(mapEditEvent); const EventOverlaySettingsEditorDialogRequest::Mode mode = mapEditEvent->getMode(); const int browserWindowIndex = mapEditEvent->getBrowserWindowIndex(); CaretAssertVectorIndex(m_brainBrowserWindows, browserWindowIndex); BrainBrowserWindow* browserWindow = m_brainBrowserWindows[browserWindowIndex]; CaretAssert(browserWindow); Overlay* overlay = mapEditEvent->getOverlay(); switch (mode) { case EventOverlaySettingsEditorDialogRequest::MODE_OVERLAY_MAP_CHANGED: { for (std::set::iterator overlayEditorIter = m_overlaySettingsEditors.begin(); overlayEditorIter != m_overlaySettingsEditors.end(); overlayEditorIter++) { OverlaySettingsEditorDialog* med = *overlayEditorIter; med->updateIfThisOverlayIsInDialog(overlay); } } break; case EventOverlaySettingsEditorDialogRequest::MODE_SHOW_EDITOR: { OverlaySettingsEditorDialog* overlayEditor = NULL; for (std::set::iterator overlayEditorIter = m_overlaySettingsEditors.begin(); overlayEditorIter != m_overlaySettingsEditors.end(); overlayEditorIter++) { OverlaySettingsEditorDialog* med = *overlayEditorIter; if (med->isDoNotReplaceSelected() == false) { overlayEditor = med; break; } } bool placeInDefaultLocation = false; if (overlayEditor == NULL) { overlayEditor = new OverlaySettingsEditorDialog(browserWindow); m_overlaySettingsEditors.insert(overlayEditor); this->addNonModalDialog(overlayEditor); placeInDefaultLocation = true; } else { if (overlayEditor->isHidden()) { placeInDefaultLocation = true; } /* * Is the overlay editor requested for a window * that is not the parent of this overlay editor? */ if (browserWindow != overlayEditor->parent()) { /* * Switch the parent of the overlay editor. * On Linux, this will cause the overlay editor * to be "brought to the front" only when its * parent is "brought to the front". * * The position must be preserved. */ const QPoint globalPos = overlayEditor->pos(); overlayEditor->setParent(browserWindow, overlayEditor->windowFlags()); overlayEditor->move(globalPos); } } overlayEditor->updateDialogContent(overlay); overlayEditor->show(); overlayEditor->raise(); overlayEditor->activateWindow(); if (placeInDefaultLocation) { WuQtUtilities::moveWindowToSideOfParent(browserWindow, overlayEditor); } } case EventOverlaySettingsEditorDialogRequest::MODE_UPDATE_ALL: for (std::set::iterator overlayEditorIter = m_overlaySettingsEditors.begin(); overlayEditorIter != m_overlaySettingsEditors.end(); overlayEditorIter++) { OverlaySettingsEditorDialog* med = *overlayEditorIter; med->updateDialog(); } break; } mapEditEvent->setEventProcessed(); } else if (event->getEventType() == EventTypeEnum::EVENT_OPERATING_SYSTEM_REQUEST_OPEN_DATA_FILE) { EventOperatingSystemRequestOpenDataFile* openFileEvent = dynamic_cast(event); CaretAssert(openFileEvent); BrainBrowserWindow* bbw = getActiveBrowserWindow(); if (bbw != NULL) { std::vector filenamesVector; std::vector dataFileTypeVectorNotUsed; filenamesVector.push_back(openFileEvent->getDataFileName()); bbw->loadFiles(bbw, filenamesVector, dataFileTypeVectorNotUsed, BrainBrowserWindow::LOAD_SPEC_FILE_WITH_DIALOG, "", ""); } else { /* * Browser window has not yet been created. * After it is created, the file will be opened. */ m_nameOfDataFileToOpenAfterStartup = openFileEvent->getDataFileName(); } } else if (event->getEventType() == EventTypeEnum::EVENT_PALETTE_COLOR_MAPPING_EDITOR_SHOW) { EventPaletteColorMappingEditorDialogRequest* paletteEditEvent = dynamic_cast(event); CaretAssert(paletteEditEvent); BrainBrowserWindow* browserWindow = m_brainBrowserWindows[paletteEditEvent->getBrowserWindowIndex()]; CaretAssert(browserWindow); int32_t browserTabIndex = -1; BrowserTabContent* tabContent = browserWindow->getBrowserTabContent(); if (tabContent != NULL) { browserTabIndex = tabContent->getTabNumber(); } bool placeInDefaultLocation = false; if (m_paletteColorMappingEditor == NULL) { m_paletteColorMappingEditor = new PaletteColorMappingEditorDialog(browserWindow); addNonModalDialog(m_paletteColorMappingEditor); placeInDefaultLocation = true; } else if (m_paletteColorMappingEditor->isHidden()) { placeInDefaultLocation = true; } m_paletteColorMappingEditor->updateDialogContent(paletteEditEvent->getCaretMappableDataFile(), paletteEditEvent->getMapIndex(), browserTabIndex); m_paletteColorMappingEditor->show(); m_paletteColorMappingEditor->raise(); m_paletteColorMappingEditor->activateWindow(); if (placeInDefaultLocation) { WuQtUtilities::moveWindowToSideOfParent(browserWindow, m_paletteColorMappingEditor); } paletteEditEvent->setEventProcessed(); } else if (event->getEventType() == EventTypeEnum::EVENT_HELP_VIEWER_DISPLAY) { EventHelpViewerDisplay* helpEvent = dynamic_cast(event); CaretAssert(helpEvent); showHideHelpDialog(true, helpEvent->getBrainBrowserWindow()); m_helpViewerDialog->showHelpPageWithName(helpEvent->getHelpPageName()); } } /** * Remove the tab content from all browser windows except for the given * browser windows, close the other browser windows, and then return * the tab content. * * @param browserWindow * Browser window that gets tab content from all other windows. * @param tabContents * Tab content from all other windows. */ void GuiManager::closeOtherWindowsAndReturnTheirTabContent(BrainBrowserWindow* browserWindow, std::vector& tabContents) { tabContents.clear(); const int32_t numWindows = m_brainBrowserWindows.size(); for (int32_t i = 0; i < numWindows; i++) { BrainBrowserWindow* bbw = m_brainBrowserWindows[i]; if (bbw != NULL) { if (bbw != browserWindow) { std::vector tabs; bbw->removeAndReturnAllTabs(tabs); tabContents.insert(tabContents.end(), tabs.begin(), tabs.end()); this->allowBrowserWindowsToCloseWithoutConfirmation = true; bbw->close(); /* * Should delete the windows that were closed! * When a window is closed, Qt uses 'deleteLater' * but we need them deleted now so that event listeners * are shut down since the closed windows no longer * have any content. */ QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete); this->allowBrowserWindowsToCloseWithoutConfirmation = false; } } } } /** * Close all but the given window. * @param browserWindow * Window that is NOT closed. */ void GuiManager::closeAllOtherWindows(BrainBrowserWindow* browserWindow) { const int32_t numWindows = m_brainBrowserWindows.size(); for (int32_t i = 0; i < numWindows; i++) { BrainBrowserWindow* bbw = m_brainBrowserWindows[i]; if (bbw != NULL) { if (bbw != browserWindow) { this->allowBrowserWindowsToCloseWithoutConfirmation = true; bbw->close(); /* * Should delete the windows that were closed! * When a window is closed, Qt uses 'deleteLater' * but we need them deleted now so that event listeners * are shut down since the closed windows no longer * have any content. */ QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete); this->allowBrowserWindowsToCloseWithoutConfirmation = false; } } } } /** * Reparent non-modal dialogs that may need to be reparented if the * original parent, a BrainBrowserWindow is closed in which case the * dialog is reparented to a different BrainBrowserWindow. * * @param closingBrainBrowserWindow * Browser window that is closing. */ void GuiManager::reparentNonModalDialogs(BrainBrowserWindow* closingBrainBrowserWindow) { BrainBrowserWindow* firstBrainBrowserWindow = NULL; for (int32_t i = 0; i < static_cast(m_brainBrowserWindows.size()); i++) { if (m_brainBrowserWindows[i] != NULL) { if (m_brainBrowserWindows[i] != closingBrainBrowserWindow) { firstBrainBrowserWindow = m_brainBrowserWindows[i]; break; } } } if (firstBrainBrowserWindow != NULL) { for (std::set::iterator iter = this->nonModalDialogs.begin(); iter != this->nonModalDialogs.end(); iter++) { QWidget* d = *iter; if (d->parent() == closingBrainBrowserWindow) { const bool wasVisible = d->isVisible(); const QPoint globalPos = d->pos(); d->setParent(firstBrainBrowserWindow, d->windowFlags()); d->move(globalPos); if (wasVisible) { d->show(); } else { d->hide(); } } /* * Update any dialogs that are WuQ non modal dialogs. */ WuQDialogNonModal* wuqNonModalDialog = dynamic_cast(d); if (wuqNonModalDialog != NULL) { wuqNonModalDialog->updateDialog(); } } } } /** * Show the surface properties editor dialog. * @param browserWindow * Browser window on which dialog is displayed. */ void GuiManager::processShowSurfacePropertiesEditorDialog(BrainBrowserWindow* browserWindow) { bool wasCreatedFlag = false; if (this->m_surfacePropertiesEditorDialog == NULL) { m_surfacePropertiesEditorDialog = new SurfacePropertiesEditorDialog(browserWindow); this->addNonModalDialog(m_surfacePropertiesEditorDialog); m_surfacePropertiesEditorDialog->setSaveWindowPositionForNextTime(true); wasCreatedFlag = true; } m_surfacePropertiesEditorDialog->setVisible(true); m_surfacePropertiesEditorDialog->show(); m_surfacePropertiesEditorDialog->activateWindow(); if (wasCreatedFlag) { WuQtUtilities::moveWindowToSideOfParent(browserWindow, m_surfacePropertiesEditorDialog); } } /** * @return The action for showing/hiding the scene dialog. */ QAction* GuiManager::getSceneDialogDisplayAction() { return m_sceneDialogDisplayAction; } /** * Gets called when the scene dialog action is toggled. * * @param status * New status (true display dialog, false hide it). */ void GuiManager::sceneDialogDisplayActionToggled(bool status) { showHideSceneDialog(status, NULL); } /** * Gets called by the scene dialog when the scene dialog is closed. * Ensures that the scene dialog display action status remains * synchronized with the displayed status of the scene dialog. */ void GuiManager::sceneDialogWasClosed() { m_sceneDialogDisplayAction->blockSignals(true); m_sceneDialogDisplayAction->setChecked(false); m_sceneDialogDisplayAction->blockSignals(false); } /** * Show or hide the scene dialog. * * @param status * True means show, false means hide. * @param parentBrainBrowserWindow * If this is not NULL, and the scene dialog needs to be created, * use this window as the parent and place the dialog next to this * window. */ void GuiManager::showHideSceneDialog(const bool status, BrainBrowserWindow* parentBrainBrowserWindow) { bool dialogWasCreated = false; QWidget* moveWindowParent = parentBrainBrowserWindow; if (status) { if (this->sceneDialog == NULL) { BrainBrowserWindow* sceneDialogParent = parentBrainBrowserWindow; if (sceneDialogParent == NULL) { sceneDialogParent = getActiveBrowserWindow(); } this->sceneDialog = new SceneDialog(sceneDialogParent); this->addNonModalDialog(this->sceneDialog); QObject::connect(this->sceneDialog, SIGNAL(dialogWasClosed()), this, SLOT(sceneDialogWasClosed())); dialogWasCreated = true; /* * If there was no parent dialog for placement of the scene * dialog and there is only one browser window, use the browser * for placement of the scene dialog. */ if (moveWindowParent == NULL) { if (getAllOpenBrainBrowserWindows().size() == 1) { moveWindowParent = sceneDialogParent; } } } this->sceneDialog->show(); this->sceneDialog->activateWindow(); } else { this->sceneDialog->close(); } if (dialogWasCreated) { if (moveWindowParent != NULL) { WuQtUtilities::moveWindowToSideOfParent(moveWindowParent, this->sceneDialog); } } m_sceneDialogDisplayAction->blockSignals(true); m_sceneDialogDisplayAction->setChecked(status); m_sceneDialogDisplayAction->blockSignals(false); } /** * Show the scene dialog. If dialog needs to be created, use the * given window as the parent. * @param browserWindowIn * Parent of scene dialog if it needs to be created. */ void GuiManager::processShowSceneDialog(BrainBrowserWindow* browserWindowIn) { showHideSceneDialog(true, browserWindowIn); } /** * Show the scene dialog and load the given scene from the given scene file. * If displaying the scene had an error, the scene dialog will remain open. * Otherwise, the scene dialog is closed. * * @param browserWindow * Parent of scene dialog if it needs to be created. * @param sceneFile * Scene File that contains the scene. * @param scene * Scene that is displayed. */ void GuiManager::processShowSceneDialogAndScene(BrainBrowserWindow* browserWindow, SceneFile* sceneFile, Scene* scene) { showHideSceneDialog(true, browserWindow); const bool sceneWasDisplayed = this->sceneDialog->displayScene(sceneFile, scene); if (sceneWasDisplayed) { showHideSceneDialog(false, NULL); } } /** * Show the Workbench Bug Report Dialog. * * @param browserWindow * Parent of dialog if it needs to be created. * @param openGLInformation * Information about OpenGL. */ void GuiManager::processShowBugReportDialog(BrainBrowserWindow* browserWindow, const AString& openGLInformation) { if (m_bugReportDialog == NULL) { m_bugReportDialog = new BugReportDialog(browserWindow, openGLInformation); this->addNonModalDialog(m_bugReportDialog); } m_bugReportDialog->setVisible(true); m_bugReportDialog->show(); m_bugReportDialog->activateWindow(); } /** * @return Action for display of help viewer. */ QAction* GuiManager::getHelpViewerDialogDisplayAction() { return m_helpViewerDialogDisplayAction; } /** * Show or hide the help dialog. * * @param status * True means show, false means hide. * @param parentBrainBrowserWindow * If this is not NULL, and the help dialog needs to be created, * use this window as the parent and place the dialog next to this * window. */ void GuiManager::showHideHelpDialog(const bool status, BrainBrowserWindow* parentBrainBrowserWindow) { bool dialogWasCreated = false; QWidget* moveWindowParent = parentBrainBrowserWindow; if (status) { if (m_helpViewerDialog == NULL) { BrainBrowserWindow* helpDialogParent = parentBrainBrowserWindow; if (helpDialogParent == NULL) { helpDialogParent = getActiveBrowserWindow(); } m_helpViewerDialog = new HelpViewerDialog(helpDialogParent); 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; } } } m_helpViewerDialog->show(); m_helpViewerDialog->activateWindow(); } 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); } /** * Gets called by the help dialog when the help dialog is closed. * Ensures that the help dialog display action status remains * synchronized with the displayed status of the help dialog. */ void GuiManager::helpDialogWasClosed() { m_helpViewerDialogDisplayAction->blockSignals(true); m_helpViewerDialogDisplayAction->setChecked(false); m_helpViewerDialogDisplayAction->blockSignals(false); } /** * Called when show help action is triggered */ void GuiManager::showHelpDialogActionToggled(bool status) { showHideHelpDialog(status, NULL); } /** * @return The action that indicates the enabled status * for display of the information window. */ QAction* GuiManager::getInformationDisplayDialogEnabledAction() { return m_informationDisplayDialogEnabledAction; } /** * @return The action that indicates the enabled status * for display of the identify brainordinate dialog. */ QAction* GuiManager::getIdentifyBrainordinateDialogDisplayAction() { return m_identifyBrainordinateDialogEnabledAction; } /** * Show the information window. */ void GuiManager::processShowInformationWindow() { this->processShowInformationDisplayDialog(true); } void GuiManager::showHideInfoWindowSelected(bool status) { QString text("Show Information Window"); if (status) { text = "Hide Information Window"; if (m_informationDisplayDialogEnabledAction->signalsBlocked() == false) { this->processShowInformationDisplayDialog(true); } } text += ("\n\n" "When this button is 'on', the information window\n" "is automatically displayed when an identification\n" "operation (mouse click over surface or volume slice)\n" "is performed. "); m_informationDisplayDialogEnabledAction->setToolTip(text); } /** * Show the information display window. * @param forceDisplayOfDialog * If true, the window will be displayed even if its display * enabled status is off. */ void GuiManager::processShowInformationDisplayDialog(const bool forceDisplayOfDialog) { if (m_informationDisplayDialog == NULL) { std::vector bbws = this->getAllOpenBrainBrowserWindows(); if (bbws.empty() == false) { BrainBrowserWindow* parentWindow = bbws[0]; #ifdef CARET_OS_MACOSX m_informationDisplayDialog = new InformationDisplayDialog(parentWindow); this->addNonModalDialog(m_informationDisplayDialog); #else // CARET_OS_MACOSX m_informationDisplayDialog = new InformationDisplayDialog(NULL); addParentLessNonModalDialog(m_informationDisplayDialog); #endif // CARET_OS_MACOSX m_informationDisplayDialog->resize(600, 200); m_informationDisplayDialog->setSaveWindowPositionForNextTime(true); WuQtUtilities::moveWindowToSideOfParent(parentWindow, m_informationDisplayDialog); } } if (forceDisplayOfDialog || m_informationDisplayDialogEnabledAction->isChecked()) { if (m_informationDisplayDialog != NULL) { m_informationDisplayDialog->setVisible(true); m_informationDisplayDialog->show(); m_informationDisplayDialog->activateWindow(); } } } /** * Show/hide the identify dialog. * * @param status * Status (true/false) to display dialog. */ void GuiManager::showIdentifyBrainordinateDialogActionToggled(bool status) { showHideIdentfyBrainordinateDialog(status, NULL); } /** * Show or hide the identify brainordinate dialog. * * @param status * True means show, false means hide. * @param parentBrainBrowserWindow * If this is not NULL, and the help dialog needs to be created, * use this window as the parent and place the dialog next to this * window. */ void GuiManager::showHideIdentfyBrainordinateDialog(const bool status, BrainBrowserWindow* parentBrainBrowserWindow) { bool dialogWasCreated = false; QWidget* moveWindowParent = parentBrainBrowserWindow; if (status) { if (m_identifyBrainordinateDialog == NULL) { BrainBrowserWindow* idDialogParent = parentBrainBrowserWindow; if (idDialogParent == NULL) { idDialogParent = getActiveBrowserWindow(); } m_identifyBrainordinateDialog = new IdentifyBrainordinateDialog(idDialogParent); //m_identifyBrainordinateDialog->setSaveWindowPositionForNextTime(true); this->addNonModalDialog(m_identifyBrainordinateDialog); QObject::connect(m_identifyBrainordinateDialog, SIGNAL(dialogWasClosed()), this, SLOT(identifyBrainordinateDialogWasClosed())); 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 = idDialogParent; } } } m_identifyBrainordinateDialog->show(); m_identifyBrainordinateDialog->activateWindow(); } else { m_identifyBrainordinateDialog->close(); } if (dialogWasCreated) { if (moveWindowParent != NULL) { WuQtUtilities::moveWindowToSideOfParent(moveWindowParent, m_identifyBrainordinateDialog); } } m_identifyBrainordinateDialogEnabledAction->blockSignals(true); m_identifyBrainordinateDialogEnabledAction->setChecked(status); m_identifyBrainordinateDialogEnabledAction->blockSignals(false); } /** * Gets called when the identify dialog is closed. */ void GuiManager::identifyBrainordinateDialogWasClosed() { m_identifyBrainordinateDialogEnabledAction->blockSignals(true); m_identifyBrainordinateDialogEnabledAction->setChecked(false); m_identifyBrainordinateDialogEnabledAction->blockSignals(false); } /** * Add a non-modal dialog so that it may be reparented. * * @param dialog * The dialog. */ void GuiManager::addNonModalDialog(QWidget* dialog) { CaretAssert(dialog); this->nonModalDialogs.insert(dialog); } /** * Add a parent-less dialog so that it may be deleted when * the application is exited. * * @param dialog * The dialog. */ void GuiManager::addParentLessNonModalDialog(QWidget* dialog) { CaretAssert(dialog); m_parentlessNonModalDialogs.insert(dialog); } /** * Show the clipping planes dialog. * @param browserWindow * Window on which dialog was requested. */ void GuiManager::processShowClippingPlanesDialog(BrainBrowserWindow* browserWindow) { if (m_clippingPlanesDialog == NULL) { m_clippingPlanesDialog = new ClippingPlanesDialog(browserWindow); this->addNonModalDialog(m_clippingPlanesDialog); } const int32_t browserWindowIndex = browserWindow->getBrowserWindowIndex(); m_clippingPlanesDialog->updateContent(browserWindowIndex); m_clippingPlanesDialog->setVisible(true); m_clippingPlanesDialog->show(); m_clippingPlanesDialog->activateWindow(); } /** * Show the custom view dialog. * @param browserWindow * Window on which dialog was requested. */ void GuiManager::processShowCustomViewDialog(BrainBrowserWindow* browserWindow) { if (m_customViewDialog == NULL) { m_customViewDialog = new CustomViewDialog(browserWindow); this->addNonModalDialog(m_customViewDialog); } const int32_t browserWindowIndex = browserWindow->getBrowserWindowIndex(); m_customViewDialog->updateContent(browserWindowIndex); m_customViewDialog->setVisible(true); m_customViewDialog->show(); m_customViewDialog->activateWindow(); } /** * Show the tile tabs configuration dialog. * @param browserWindow * Window on which dialog was requested. */ void GuiManager::processShowTileTabsConfigurationDialog(caret::BrainBrowserWindow *browserWindow) { if (m_tileTabsConfigurationDialog == NULL) { m_tileTabsConfigurationDialog = new TileTabsConfigurationDialog(browserWindow); this->addNonModalDialog(m_tileTabsConfigurationDialog); } m_tileTabsConfigurationDialog->updateDialogWithSelectedTileTabsFromWindow(browserWindow); m_tileTabsConfigurationDialog->setVisible(true); m_tileTabsConfigurationDialog->show(); m_tileTabsConfigurationDialog->activateWindow(); } /** * Show the image capture window. * @param browserWindow * Window on which dialog was requested. */ void GuiManager::processShowImageCaptureDialog(BrainBrowserWindow* browserWindow) { if (this->imageCaptureDialog == NULL) { this->imageCaptureDialog = new ImageCaptureDialog(browserWindow); this->addNonModalDialog(this->imageCaptureDialog); } this->imageCaptureDialog->updateDialog(); this->imageCaptureDialog->setBrowserWindowIndex(browserWindow->getBrowserWindowIndex()); this->imageCaptureDialog->setVisible(true); this->imageCaptureDialog->show(); this->imageCaptureDialog->activateWindow(); } /** * Show the gaps and margins window. * @param browserWindow * Window on which dialog was requested. */ void GuiManager::processShowGapsAndMarginsDialog(BrainBrowserWindow* browserWindow) { if (m_gapsAndMarginsDialog == NULL) { m_gapsAndMarginsDialog = new GapsAndMarginsDialog(browserWindow); this->addNonModalDialog(m_gapsAndMarginsDialog); } m_gapsAndMarginsDialog->updateDialog(); //m_tabMarginsDialog->setBrowserWindowIndex(browserWindow->getBrowserWindowIndex()); m_gapsAndMarginsDialog->setVisible(true); m_gapsAndMarginsDialog->show(); m_gapsAndMarginsDialog->activateWindow(); } /** * Show the record movie window. * @param browserWindow * Window on which dialog was requested. */ void GuiManager::processShowMovieDialog(BrainBrowserWindow* browserWindow) { if (this->movieDialog == NULL) { 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(); } /** * Show the preferences window. * @param browserWindow * Window on which dialog was requested. */ void GuiManager::processShowPreferencesDialog(BrainBrowserWindow* browserWindow) { if (this->preferencesDialog == NULL) { this->preferencesDialog = new PreferencesDialog(browserWindow); this->addNonModalDialog(this->preferencesDialog); } this->preferencesDialog->updateDialog(); this->preferencesDialog->setVisible(true); this->preferencesDialog->show(); this->preferencesDialog->activateWindow(); } /** * Show the allen database web view. * @param browserWindow * If the web view needs to be created, use this as parent. */ void GuiManager::processShowAllenDataBaseWebView(BrainBrowserWindow* browserWindow) { WuQMessageBox::informationOk(browserWindow, "Allen Database connection not yet implemented"); } /** * Show the connectome database web view. * @param browserWindow * If the web view needs to be created, use this as parent. */ void GuiManager::processShowConnectomeDataBaseWebView(BrainBrowserWindow* /*browserWindow*/) { CaretLogSevere("Webkit is disabled !!!"); // no webkit // if (this->connectomeDatabaseWebView == NULL) { // this->connectomeDatabaseWebView = new WuQWebView(); // this->connectomeDatabaseWebView->load(QUrl("https://db.humanconnectome.org/")); // this->addNonModalDialog(this->connectomeDatabaseWebView); // } // this->connectomeDatabaseWebView->show(); } /** * sets animation start time for Time Course Dialogs */ void GuiManager::updateAnimationStartTime(double /*value*/) { } /** * @return The cursor manager. */ const CursorManager* GuiManager::getCursorManager() const { return this->cursorManager; } /** * Create a scene for an instance of a class. * * @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. * * @return Pointer to SceneClass object representing the state of * this object. Under some circumstances a NULL pointer may be * returned. Caller will take ownership of returned object. */ SceneClass* GuiManager::saveToScene(const SceneAttributes* sceneAttributes, const AString& instanceName) { SceneClass* sceneClass = new SceneClass(instanceName, "GuiManager", 1); /* * Save session manager (brain, etc) */ sceneClass->addClass(SessionManager::get()->saveToScene(sceneAttributes, "m_sessionManager")); /* * Save windows */ std::vector browserWindowClasses; const int32_t numBrowserWindows = static_cast(m_brainBrowserWindows.size()); for (int32_t i = 0; i < numBrowserWindows; i++) { BrainBrowserWindow* bbw = m_brainBrowserWindows[i]; if (bbw != NULL) { browserWindowClasses.push_back(bbw->saveToScene(sceneAttributes, "m_brainBrowserWindows")); } } SceneClassArray* browserWindowArray = new SceneClassArray("m_brainBrowserWindows", browserWindowClasses); sceneClass->addChild(browserWindowArray); /* * Save information window */ if (m_informationDisplayDialog != NULL) { sceneClass->addClass(m_informationDisplayDialog->saveToScene(sceneAttributes, "m_informationDisplayDialog")); } /* * Save surface properties window */ if (m_surfacePropertiesEditorDialog != NULL) { sceneClass->addClass(m_surfacePropertiesEditorDialog->saveToScene(sceneAttributes, "m_surfacePropertiesEditorDialog")); } switch (sceneAttributes->getSceneType()) { case SceneTypeEnum::SCENE_TYPE_FULL: break; case SceneTypeEnum::SCENE_TYPE_GENERIC: break; } return sceneClass; } /** * Restore the state of an instance of a class. * * @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 containing the state that was previously * saved and should be restored. */ void GuiManager::restoreFromScene(const SceneAttributes* sceneAttributes, const SceneClass* sceneClass) { const int64_t eventCountAtStart = EventManager::get()->getEventIssuedCounter(); ElapsedTimer sceneRestoreTimer; sceneRestoreTimer.start(); if (sceneClass == NULL) { return; } switch (sceneAttributes->getSceneType()) { case SceneTypeEnum::SCENE_TYPE_FULL: break; case SceneTypeEnum::SCENE_TYPE_GENERIC: break; } /* * Invalid first window position used when * positioning other windows */ SceneWindowGeometry::setFirstBrowserWindowCoordinatesInvalid(); /* * Close all but one window and remove its tabs */ BrainBrowserWindow* firstBrowserWindow = getActiveBrowserWindow();; if (firstBrowserWindow != NULL) { closeAllOtherWindows(firstBrowserWindow); /* * Remove all tabs from window. * The tab content will be deleted by the session manager. */ std::vector windowTabContent; firstBrowserWindow->removeAndReturnAllTabs(windowTabContent); } /* * Close Overlay and palette editor windows since the files * displayed in them may become invalid */ for (std::set::iterator overlayEditorIter = m_overlaySettingsEditors.begin(); overlayEditorIter != m_overlaySettingsEditors.end(); overlayEditorIter++) { OverlaySettingsEditorDialog* med = *overlayEditorIter; med->close(); } if (m_paletteColorMappingEditor != NULL) { m_paletteColorMappingEditor->close(); } /* * Update the windows */ EventManager::get()->sendEvent(EventUserInterfaceUpdate().getPointer()); EventManager::get()->sendEvent(EventGraphicsUpdateAllWindows().getPointer()); /* * Blocking user-interface and graphics event will speed up * restoration of the user interface. */ bool blockUserInteraceUpdateEvents = true; if (blockUserInteraceUpdateEvents) { EventManager::get()->blockEvent(EventTypeEnum::EVENT_USER_INTERFACE_UPDATE, true); } EventManager::get()->blockEvent(EventTypeEnum::EVENT_GRAPHICS_UPDATE_ALL_WINDOWS, true); EventManager::get()->blockEvent(EventTypeEnum::EVENT_GRAPHICS_UPDATE_ONE_WINDOW, true); /* * Restore session manager */ SessionManager::get()->restoreFromScene(sceneAttributes, sceneClass->getClass("m_sessionManager")); EventProgressUpdate progressEvent(""); /* * See if models available (user may have cancelled scene loading. */ EventModelGetAll getAllModelsEvent; EventManager::get()->sendEvent(getAllModelsEvent.getPointer()); const bool haveModels = (getAllModelsEvent.getModels().empty() == false); ElapsedTimer timer; timer.start(); if (haveModels) { /* * Get open windows */ std::list availableWindows; const int32_t numBrowserWindows = static_cast(m_brainBrowserWindows.size()); for (int32_t i = 0; i < numBrowserWindows; i++) { if (m_brainBrowserWindows[i] != NULL) { availableWindows.push_back(m_brainBrowserWindows[i]); } } /* * Restore windows */ progressEvent.setProgressMessage("Restoring browser windows"); EventManager::get()->sendEvent(progressEvent.getPointer()); const SceneClassArray* browserWindowArray = sceneClass->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); BrainBrowserWindow* bbw = NULL; if (availableWindows.empty() == false) { bbw = availableWindows.front(); availableWindows.pop_front(); } else { bbw = newBrainBrowserWindow(NULL, NULL, false); } if (bbw != NULL) { bbw->restoreFromScene(sceneAttributes, browserClass); } } } CaretLogFine("Time to restore browser windows was " + QString::number(timer.getElapsedTimeSeconds(), 'f', 3) + " seconds"); timer.reset(); /* * Restore information window */ progressEvent.setProgressMessage("Restoring Information Window"); EventManager::get()->sendEvent(progressEvent.getPointer()); const SceneClass* infoWindowClass = sceneClass->getClass("m_informationDisplayDialog"); if (infoWindowClass != NULL) { if (m_informationDisplayDialog == NULL) { processShowInformationWindow(); } else if (m_informationDisplayDialog->isVisible() == false) { processShowInformationWindow(); } m_informationDisplayDialog->restoreFromScene(sceneAttributes, infoWindowClass); } else { if (m_informationDisplayDialog != NULL) { /* * Will clear text */ m_informationDisplayDialog->restoreFromScene(sceneAttributes, NULL); } } /* * Restore surface properties */ progressEvent.setProgressMessage("Restoring Surface Properties Window"); EventManager::get()->sendEvent(progressEvent.getPointer()); const SceneClass* surfPropClass = sceneClass->getClass("m_surfacePropertiesEditorDialog"); if (surfPropClass != NULL) { if (m_surfacePropertiesEditorDialog == NULL) { processShowSurfacePropertiesEditorDialog(firstBrowserWindow); } else if (m_surfacePropertiesEditorDialog->isVisible() == false) { processShowSurfacePropertiesEditorDialog(firstBrowserWindow); } m_surfacePropertiesEditorDialog->restoreFromScene(sceneAttributes, surfPropClass); } CaretLogFine("Time to restore information/property windows was " + QString::number(timer.getElapsedTimeSeconds(), 'f', 3) + " seconds"); timer.reset(); } if (imageCaptureDialog != NULL) { imageCaptureDialog->updateDialog(); } progressEvent.setProgressMessage("Invalidating coloring and updating user interface"); EventManager::get()->sendEvent(progressEvent.getPointer()); EventManager::get()->sendEvent(EventSurfaceColoringInvalidate().getPointer()); if (blockUserInteraceUpdateEvents) { EventManager::get()->blockEvent(EventTypeEnum::EVENT_USER_INTERFACE_UPDATE, false); } EventManager::get()->sendEvent(EventUserInterfaceUpdate().getPointer()); /* * Unblock graphics updates */ EventManager::get()->blockEvent(EventTypeEnum::EVENT_GRAPHICS_UPDATE_ALL_WINDOWS, false); EventManager::get()->blockEvent(EventTypeEnum::EVENT_GRAPHICS_UPDATE_ONE_WINDOW, false); progressEvent.setProgressMessage("Updating graphics in all windows"); EventManager::get()->sendEvent(progressEvent.getPointer()); EventManager::get()->sendEvent(EventGraphicsUpdateAllWindows().getPointer()); CaretLogFine("Time to update graphics in all windows was " + QString::number(timer.getElapsedTimeSeconds(), 'f', 3) + " seconds"); timer.reset(); const int64_t totalNumberOfEvents = (EventManager::get()->getEventIssuedCounter() - eventCountAtStart); CaretLogFine("Time to restore scene was " + QString::number(sceneRestoreTimer.getElapsedTimeSeconds(), 'f', 3) + " seconds with " + AString::number(totalNumberOfEvents) + " events"); } /** * Get the name of the data file that may have been set * if Workbench was started by double-clicking a file * in the Mac OSX finder. */ AString GuiManager::getNameOfDataFileToOpenAfterStartup() const { return m_nameOfDataFileToOpenAfterStartup; } /** * Process identification after item(s) selected using a selection manager. * * @parma tabIndex * Index of tab in which identification took place. This value may * be negative indicating that the identification request is not * from a browser tab. One source for this is the Select Brainordinate * option on the Information Window. * @param selectionManager * The selection manager. * @param parentWidget * Widget on which any error message windows are displayed. */ void GuiManager::processIdentification(const int32_t tabIndex, SelectionManager* selectionManager, QWidget* parentWidget) { CursorDisplayScoped cursor; cursor.showWaitCursor(); Brain* brain = GuiManager::get()->getBrain(); CiftiConnectivityMatrixDataFileManager* ciftiConnectivityManager = SessionManager::get()->getCiftiConnectivityMatrixDataFileManager(); CiftiFiberTrajectoryManager* ciftiFiberTrajectoryManager = SessionManager::get()->getCiftiFiberTrajectoryManager(); ChartingDataManager* chartingDataManager = brain->getChartingDataManager(); IdentificationManager* identificationManager = brain->getIdentificationManager(); bool updateGraphicsFlag = false; bool updateInformationFlag = false; std::vector ciftiLoadingInfo; const QString breakAndIndent("
    "); SelectionItemSurfaceNodeIdentificationSymbol* nodeIdSymbol = selectionManager->getSurfaceNodeIdentificationSymbol(); SelectionItemVoxelIdentificationSymbol* voxelIdSymbol = selectionManager->getVoxelIdentificationSymbol(); if ((nodeIdSymbol->getSurface() != NULL) && (nodeIdSymbol->getNodeNumber() >= 0)) { const Surface* surface = nodeIdSymbol->getSurface(); const int32_t surfaceNumberOfNodes = surface->getNumberOfNodes(); const int32_t nodeIndex = nodeIdSymbol->getNodeNumber(); const StructureEnum::Enum structure = surface->getStructure(); identificationManager->removeIdentifiedNodeItem(structure, surfaceNumberOfNodes, nodeIndex); updateGraphicsFlag = true; updateInformationFlag = true; } else if (voxelIdSymbol->isValid()) { float voxelXYZ[3]; voxelIdSymbol->getVoxelXYZ(voxelXYZ); identificationManager->removeIdentifiedVoxelItem(voxelXYZ); updateGraphicsFlag = true; updateInformationFlag = true; } else { IdentifiedItem* identifiedItem = NULL; SelectionItemSurfaceNode* idNode = selectionManager->getSurfaceNodeIdentification(); SelectionItemVoxel* idVoxel = selectionManager->getVoxelIdentification(); /* * If there is a voxel ID but no node ID, identify a * node near the voxel coordinate, if it is close by. */ bool nodeIdentificationCreatedFromVoxelIdentificationFlag = false; if (idNode->isValid() == false) { if (idVoxel->isValid()) { double doubleXYZ[3]; idVoxel->getModelXYZ(doubleXYZ); const float voxelXYZ[3] = { doubleXYZ[0], doubleXYZ[1], doubleXYZ[2] }; Surface* surface = brain->getPrimaryAnatomicalSurfaceNearestCoordinate(voxelXYZ, 3.0); if (surface != NULL) { const int nodeIndex = surface->closestNode(voxelXYZ); if (nodeIndex >= 0) { idNode->reset(); idNode->setBrain(brain); idNode->setSurface(surface); idNode->setNodeNumber(nodeIndex); nodeIdentificationCreatedFromVoxelIdentificationFlag = true; } } } } /* * Load CIFTI NODE data prior to ID Message so that CIFTI * data shown in identification text is correct for the * node that was loaded. */ bool triedToLoadSurfaceODemandData = false; if (idNode->isValid()) { /* * NOT: node id was NOT created from voxel ID */ if (nodeIdentificationCreatedFromVoxelIdentificationFlag == false) { Surface* surface = idNode->getSurface(); const int32_t nodeIndex = idNode->getNodeNumber(); try { triedToLoadSurfaceODemandData = true; ciftiConnectivityManager->loadDataForSurfaceNode(brain, surface, nodeIndex, ciftiLoadingInfo); ciftiFiberTrajectoryManager->loadDataForSurfaceNode(brain, surface, nodeIndex, ciftiLoadingInfo); chartingDataManager->loadChartForSurfaceNode(surface, nodeIndex); updateGraphicsFlag = true; } catch (const DataFileException& e) { cursor.restoreCursor(); QMessageBox::critical(parentWidget, "", e.whatString()); cursor.showWaitCursor(); } } } /* * Load CIFTI VOXEL data prior to ID Message so that CIFTI * data shown in identification text is correct for the * voxel that was loaded. * * Note: If there was an attempt to load data for surface, * do not try to load voxel data. Otherwise, if the voxel * data loading fails, it will clear the coloring for * the connetivity data and make it appear as if loading data * failed. */ if (idVoxel->isValid() && ( ! triedToLoadSurfaceODemandData)) { const VolumeMappableInterface* volumeFile = idVoxel->getVolumeFile(); int64_t voxelIJK[3]; idVoxel->getVoxelIJK(voxelIJK); if (volumeFile != NULL) { float xyz[3]; volumeFile->indexToSpace(voxelIJK, xyz); updateGraphicsFlag = true; try { ciftiConnectivityManager->loadDataForVoxelAtCoordinate(brain, xyz, ciftiLoadingInfo); ciftiFiberTrajectoryManager->loadDataForVoxelAtCoordinate(brain, xyz, ciftiLoadingInfo); } catch (const DataFileException& e) { cursor.restoreCursor(); QMessageBox::critical(parentWidget, "", e.whatString()); cursor.showWaitCursor(); } try { chartingDataManager->loadChartForVoxelAtCoordinate(xyz); } catch (const DataFileException& e) { cursor.restoreCursor(); QMessageBox::critical(parentWidget, "", e.whatString()); cursor.showWaitCursor(); } } } SelectionItemChartMatrix* idChartMatrix = selectionManager->getChartMatrixIdentification(); if (idChartMatrix->isValid()) { ChartableMatrixInterface* chartMatrixInterface = idChartMatrix->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(); if ((rowIndex >= 0) && (columnIndex >= 0)) { try { ciftiConnectivityManager->loadRowOrColumnFromParcelFile(brain, ciftiParcelFile, rowIndex, columnIndex, ciftiLoadingInfo); } catch (const DataFileException& e) { cursor.restoreCursor(); QMessageBox::critical(parentWidget, "", e.whatString()); cursor.showWaitCursor(); } updateGraphicsFlag = true; } } } CiftiScalarDataSeriesFile* scalarDataSeriesFile = dynamic_cast(chartMatrixInterface); if (scalarDataSeriesFile != NULL) { const int32_t rowIndex = idChartMatrix->getMatrixRowIndex(); 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; } } } } SelectionItemCiftiConnectivityMatrixRowColumn* idCiftiConnMatrix = selectionManager->getCiftiConnectivityMatrixRowColumnIdentification(); if (idCiftiConnMatrix != NULL) { CiftiMappableConnectivityMatrixDataFile* matrixFile = idCiftiConnMatrix->getCiftiConnectivityMatrixFile(); if (matrixFile != NULL) { const int32_t rowIndex = idCiftiConnMatrix->getMatrixRowIndex(); const int32_t columnIndex = idCiftiConnMatrix->getMatrixColumnIndex(); if ((rowIndex >= 0) || (columnIndex >= 0)) { ciftiConnectivityManager->loadRowOrColumnFromConnectivityMatrixFile(brain, matrixFile, rowIndex, columnIndex, ciftiLoadingInfo); updateGraphicsFlag = true; } } } /* * Generate identification manager */ const AString identificationMessage = selectionManager->getIdentificationText(brain); bool issuedIdentificationLocationEvent = false; if (idNode->isValid()) { Surface* surface = idNode->getSurface(); const int32_t nodeIndex = idNode->getNodeNumber(); /* * Save last selected node which may get used for foci creation. */ selectionManager->setLastSelectedItem(idNode); BrainStructure* brainStructure = surface->getBrainStructure(); CaretAssert(brainStructure); float xyz[3]; const Surface* primaryAnatomicalSurface = brainStructure->getPrimaryAnatomicalSurface(); if (primaryAnatomicalSurface != NULL) { primaryAnatomicalSurface->getCoordinate(nodeIndex, xyz); } else { CaretLogWarning("No surface/primary anatomical surface for " + StructureEnum::toGuiName(brainStructure->getStructure())); xyz[0] = -10000000.0; xyz[1] = -10000000.0; xyz[2] = -10000000.0; } identifiedItem = new IdentifiedItemNode(identificationMessage, surface->getStructure(), surface->getNumberOfNodes(), nodeIndex); /* * Only issue identification event for node * if it WAS NOT created from the voxel identification * location. */ if (nodeIdentificationCreatedFromVoxelIdentificationFlag == false) { if (issuedIdentificationLocationEvent == false) { EventIdentificationHighlightLocation idLocation(tabIndex, xyz, EventIdentificationHighlightLocation::LOAD_FIBER_ORIENTATION_SAMPLES_MODE_YES); EventManager::get()->sendEvent(idLocation.getPointer()); issuedIdentificationLocationEvent = true; } } } if (idVoxel->isValid()) { const VolumeMappableInterface* volumeFile = idVoxel->getVolumeFile(); int64_t voxelIJK[3]; idVoxel->getVoxelIJK(voxelIJK); if (volumeFile != NULL) { float xyz[3]; volumeFile->indexToSpace(voxelIJK, xyz); if (identifiedItem == NULL) { identifiedItem = new IdentifiedItemVoxel(identificationMessage, xyz); } if (issuedIdentificationLocationEvent == false) { EventIdentificationHighlightLocation idLocation(tabIndex, xyz, EventIdentificationHighlightLocation::LOAD_FIBER_ORIENTATION_SAMPLES_MODE_YES); EventManager::get()->sendEvent(idLocation.getPointer()); issuedIdentificationLocationEvent = true; } /* * Save last selected node which may get used for foci creation. */ selectionManager->setLastSelectedItem(idVoxel); } } if (identifiedItem == NULL) { if (identificationMessage.isEmpty() == false) { identifiedItem = new IdentifiedItem(identificationMessage); } } AString ciftiInfo; if (ciftiLoadingInfo.empty() == false) { IdentificationStringBuilder ciftiIdStringBuilder; ciftiIdStringBuilder.addLine(false, "CIFTI data loaded", " "); for (std::vector::iterator iter = ciftiLoadingInfo.begin(); iter != ciftiLoadingInfo.end(); iter++) { ciftiIdStringBuilder.addLine(true, *iter); } ciftiInfo = ciftiIdStringBuilder.toString(); } if (ciftiInfo.isEmpty() == false) { if (identifiedItem != NULL) { identifiedItem->appendText(ciftiInfo); } else { identifiedItem = new IdentifiedItem(ciftiInfo); } } if (identifiedItem != NULL) { identificationManager->addIdentifiedItem(identifiedItem); updateInformationFlag = true; } } if (updateInformationFlag) { EventManager::get()->sendEvent(EventUpdateInformationWindows().getPointer()); } if (updateGraphicsFlag) { EventManager::get()->sendEvent(EventSurfaceColoringInvalidate().getPointer()); EventManager::get()->sendEvent(EventGraphicsUpdateAllWindows().getPointer()); EventManager::get()->sendEvent(EventUserInterfaceUpdate().addToolBar().addToolBox().getPointer()); } } // tabIndex connectome-workbench-1.2.3+git41-gc4c6c90/src/GuiQt/GuiManager.h000066400000000000000000000306121300200146000237670ustar00rootroot00000000000000 #ifndef __GUI_MANAGER_H__ #define __GUI_MANAGER_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 "DataFileTypeEnum.h" #include "EventListenerInterface.h" #include "SceneableInterface.h" class QAction; class QDialog; class QMenu; class QWidget; class MovieDialog; class WuQWebView; namespace caret { class Brain; class BrainBrowserWindow; class BrowserTabContent; class BugReportDialog; class ClippingPlanesDialog; class CursorManager; class CustomViewDialog; class GapsAndMarginsDialog; class HelpViewerDialog; class IdentifyBrainordinateDialog; class ImageFile; class ImageCaptureDialog; class InformationDisplayDialog; class OverlaySettingsEditorDialog; class Model; class PaletteColorMappingEditorDialog; class PreferencesDialog; class Scene; class SceneDialog; class SceneFile; class SelectionManager; class SpecFile; class SurfacePropertiesEditorDialog; class TileTabsConfigurationDialog; /** * Manages the graphical user-interface. */ class GuiManager : public QObject, public EventListenerInterface, public SceneableInterface { Q_OBJECT public: static GuiManager* get(); static void createGuiManager(); static void deleteGuiManager(); static void beep(); Brain* getBrain() const; int32_t getNumberOfOpenBrainBrowserWindows() const; BrainBrowserWindow* getActiveBrowserWindow() const; std::vector getAllOpenBrainBrowserWindows() const; std::vector getAllOpenBrainBrowserWindowIndices() const; BrainBrowserWindow* getBrowserWindowByWindowIndex(const int32_t browserWindowIndex); bool allowBrainBrowserWindowToClose(BrainBrowserWindow* bbw, const int32_t numberOfOpenTabs); bool exitProgram(BrainBrowserWindow* parent); bool processShowOpenSpecFileDialog(SpecFile* specFile, BrainBrowserWindow* browserWindow); void processShowSaveManageFilesDialog(BrainBrowserWindow* browserWindow); QString applicationName() const; //BrainOpenGL* getBrainOpenGL(); BrowserTabContent* getBrowserTabContentForBrowserWindow(const int32_t browserWindowIndex, const bool allowInvalidBrowserWindowIndex); Model* getModelInBrowserWindow(const int32_t browserWindowIndex); void receiveEvent(Event* event); const CursorManager* getCursorManager() const; QAction* getInformationDisplayDialogEnabledAction(); QAction* getIdentifyBrainordinateDialogDisplayAction(); QAction* getSceneDialogDisplayAction(); QAction* getHelpViewerDialogDisplayAction(); void closeAllOtherWindows(BrainBrowserWindow* browserWindow); void closeOtherWindowsAndReturnTheirTabContent(BrainBrowserWindow* browserWindow, std::vector& tabContents); void processShowBugReportDialog(BrainBrowserWindow* browserWindow, const AString& openGLInformation); void processShowClippingPlanesDialog(BrainBrowserWindow* browserWindow); void processShowCustomViewDialog(BrainBrowserWindow* browserWindow); void processShowGapsAndMarginsDialog(BrainBrowserWindow* browserWindow); void processShowImageCaptureDialog(BrainBrowserWindow* browserWindow); void processShowMovieDialog(BrainBrowserWindow* browserWindow); void processShowPreferencesDialog(BrainBrowserWindow* browserWindow); void processShowInformationDisplayDialog(const bool forceDisplayOfDialog); void processShowTileTabsConfigurationDialog(BrainBrowserWindow* browserWindow); void showHideIdentfyBrainordinateDialog(const bool status, BrainBrowserWindow* parentBrainBrowserWindow); void processShowSceneDialog(BrainBrowserWindow* browserWindow); void processShowSurfacePropertiesEditorDialog(BrainBrowserWindow* browserWindow); void processShowSceneDialogAndScene(BrainBrowserWindow* browserWindow, SceneFile* sceneFile, Scene* scene); void processShowAllenDataBaseWebView(BrainBrowserWindow* browserWindow); void processShowConnectomeDataBaseWebView(BrainBrowserWindow* browserWindow); void updateAnimationStartTime(double value); virtual SceneClass* saveToScene(const SceneAttributes* sceneAttributes, const AString& instanceName); virtual void restoreFromScene(const SceneAttributes* sceneAttributes, const SceneClass* sceneClass); AString getNameOfDataFileToOpenAfterStartup() const; void processIdentification(const int32_t tabIndex, SelectionManager* selectionManager, QWidget* parentWidget); /* * Mode used when testing for modified files */ 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 shown */ TEST_FOR_MODIFIED_FILES_MODE_FOR_SCENE_SHOW }; bool testForModifiedFiles(const TestModifiedMode testModifiedMode, AString& textMesssageOut, AString& modifiedFilesMessageOut) const; public slots: void processBringAllWindowsToFront(); void processShowInformationWindow(); void processTileWindows(); void showHideInfoWindowSelected(bool); void showIdentifyBrainordinateDialogActionToggled(bool); void sceneDialogDisplayActionToggled(bool); void showHelpDialogActionToggled(bool); private slots: void helpDialogWasClosed(); void sceneDialogWasClosed(); void identifyBrainordinateDialogWasClosed(); private: GuiManager(QObject* parent = 0); virtual ~GuiManager(); GuiManager(const GuiManager&); GuiManager& operator=(const GuiManager&); void initializeGuiManager(); BrainBrowserWindow* newBrainBrowserWindow(QWidget* parent, BrowserTabContent* browserTabContent, const bool createDefaultTabs); void reparentNonModalDialogs(BrainBrowserWindow* closingBrainBrowserWindow); void showHideSceneDialog(const bool status, BrainBrowserWindow* parentBrainBrowserWindow); void showHideHelpDialog(const bool status, BrainBrowserWindow* parentBrainBrowserWindow); // void processShowHelpViewerDialog(BrainBrowserWindow* browserWindow, // const AString& helpPageName); void addNonModalDialog(QWidget* dialog); void addParentLessNonModalDialog(QWidget* dialog); /** 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. */ 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; TileTabsConfigurationDialog* m_tileTabsConfigurationDialog; ClippingPlanesDialog* m_clippingPlanesDialog; CustomViewDialog* m_customViewDialog; ImageCaptureDialog* imageCaptureDialog; GapsAndMarginsDialog* m_gapsAndMarginsDialog; MovieDialog* movieDialog; PreferencesDialog* preferencesDialog; InformationDisplayDialog* m_informationDisplayDialog; IdentifyBrainordinateDialog* m_identifyBrainordinateDialog; SceneDialog* sceneDialog; QAction* m_sceneDialogDisplayAction; SurfacePropertiesEditorDialog* m_surfacePropertiesEditorDialog; WuQWebView* connectomeDatabaseWebView; CursorManager* cursorManager; QAction* m_informationDisplayDialogEnabledAction; QAction* m_identifyBrainordinateDialogEnabledAction; BugReportDialog* m_bugReportDialog; QAction* m_helpViewerDialogDisplayAction; HelpViewerDialog* m_helpViewerDialog; /** * Tracks non-modal dialogs that are created only one time * and may need to be reparented if the original parent, a * BrainBrowserWindow is closed in which case the dialog * is reparented to a different BrainBrowserWindow. * * On Linux and Windows, behavior is that when a dialog becomes active, * its parent window is brought forward to be directly behind the * dialog. When there are multiple browser windows, this may cause * the parent browser window to be brought forward and obscure * a browser window (not a parent of the dialog) that the user * was viewing. In this case, it may be desirable to make * the dialog parent-less. */ std::set nonModalDialogs; /** * Tracks non-modal dialogs WITHOUT parents. Dialogs without parents * are undesirable on Mac because when the parent-less dialog is * the active window, the menu bar will disappear. See the * comment for 'nonModalDialogs' for more information. */ std::set m_parentlessNonModalDialogs; /** * If Workbench is started by double-clicking a data file in * the Mac OSX Finder, this will contain the name of the data * file. When the event is received, Workbench has not yet * created windows. After creating the first Browser Window, * the values of this string is requested, and if valid, * the data file is opened. */ AString m_nameOfDataFileToOpenAfterStartup; }; #ifdef __GUI_MANAGER_DEFINE__ GuiManager* GuiManager::singletonGuiManager = NULL; #endif // __GUI_MANAGER_DEFINE__ } #endif // __GUI_MANAGER_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/GuiQt/HelpViewerDialog.cxx000066400000000000000000001065221300200146000255210ustar00rootroot00000000000000 /*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 __HELP_VIEWER_DIALOG_DECLARE__ #include "HelpViewerDialog.h" #undef __HELP_VIEWER_DIALOG_DECLARE__ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "CaretAssert.h" #include "CaretLogger.h" #include "CommandOperation.h" #include "CommandOperationManager.h" #include "WuQMessageBox.h" #include "WuQtUtilities.h" using namespace caret; /** * \class caret::HelpViewerDialog * \brief Dialog that displays the applications help information. * \ingroup GuiQt */ /** * Constructor. * * @param parent * Parent of this dialog. * @param f * Qt's window flags. */ HelpViewerDialog::HelpViewerDialog(QWidget* parent, Qt::WindowFlags f) : WuQDialogNonModal("Help", parent, f) { m_helpBrowser = NULL; m_topicSearchLineEditFirstMouseClick = true; setApplyButtonText(""); QTabWidget* indexSearchTabWidget = new QTabWidget(); indexSearchTabWidget->addTab(createTableOfContentsWidget(), "Table of Contents"); indexSearchTabWidget->addTab(createIndexSearchWidget(), "Search"); /* * Need some space above the tab widget */ QWidget* indexSearchWidget = new QWidget(); QVBoxLayout* indexSearchLayout = new QVBoxLayout(indexSearchWidget); indexSearchLayout->addWidget(indexSearchTabWidget); /* * Create the splitter and add the widgets to the splitter */ m_splitter = new QSplitter; m_splitter->setOrientation(Qt::Horizontal); m_splitter->addWidget(indexSearchWidget); m_splitter->addWidget(createHelpViewerWidget()); QList sizeList; sizeList << 225 << 375; m_splitter->setSizes(sizeList); setCentralWidget(m_splitter, WuQDialog::SCROLL_AREA_NEVER); loadHelpTopicsIntoIndexTree(); loadSearchListWidget(); resize(800, 600); } /** * Destructor. */ HelpViewerDialog::~HelpViewerDialog() { } /** * Update the content of the dialog. */ void HelpViewerDialog::updateDialog() { } /** * @return Create and return the table of contents widget. */ QWidget* HelpViewerDialog::createTableOfContentsWidget() { /* * Create the tree widget for the help topics */ m_topicIndexTreeWidget = new QTreeWidget; m_topicIndexTreeWidget->setColumnCount(1); m_topicIndexTreeWidget->setColumnHidden(0, false); m_topicIndexTreeWidget->headerItem()->setHidden(true); QObject::connect(m_topicIndexTreeWidget, SIGNAL(currentItemChanged(QTreeWidgetItem*, QTreeWidgetItem*)), this, SLOT(topicIndexTreeItemChanged(QTreeWidgetItem*, QTreeWidgetItem*))); /* * Collapse All button */ QAction* collapseAllAction = WuQtUtilities::createAction("Collapse All", "", this, this, SLOT(topicCollapseAllTriggered())); QToolButton* collapseAllToolButton = new QToolButton; collapseAllToolButton->setDefaultAction(collapseAllAction); /* * Expand All button */ QAction* expandAllAction = WuQtUtilities::createAction("Expand All", "", this, this, SLOT(topicExpandAllTriggered())); QToolButton* expandAllToolButton = new QToolButton; expandAllToolButton->setDefaultAction(expandAllAction); /* * Layout for collapse/expand all buttons */ QHBoxLayout* collapseExpandLayout = new QHBoxLayout; collapseExpandLayout->addStretch(); collapseExpandLayout->addWidget(collapseAllToolButton); collapseExpandLayout->addWidget(expandAllToolButton); collapseExpandLayout->addStretch(); /* * Layout for search line edit and topics */ QWidget* topicWidgets = new QWidget(); QVBoxLayout* topicLayout = new QVBoxLayout(topicWidgets); WuQtUtilities::setLayoutMargins(topicLayout, 0); topicLayout->addLayout(collapseExpandLayout); topicLayout->addWidget(m_topicIndexTreeWidget); return topicWidgets; } /** * @return Create and return the search widget. */ QWidget* HelpViewerDialog::createIndexSearchWidget() { /* * Search line edit and list widget */ const AString searchText = ("All searches are case insensitive.\n" "\n" "You may use wildcard characters:\n" " * - Matches any characters.\n" " ? - Matches a single character.\n"); const AString topicSearchToolTipText = ("Enter text to search content of ALL help pages.\n" + searchText); m_topicSearchLineEdit = new QLineEdit; m_topicSearchLineEdit->setToolTip(topicSearchToolTipText.convertToHtmlPage()); QObject::connect(m_topicSearchLineEdit, SIGNAL(returnPressed()), this, SLOT(topicSearchLineEditStartSearch())); QObject::connect(m_topicSearchLineEdit, SIGNAL(textEdited(const QString&)), this, SLOT(topicSearchLineEditStartSearch())); QObject::connect(m_topicSearchLineEdit, SIGNAL(cursorPositionChanged(int,int)), this, SLOT(topicSearchLineEditCursorPositionChanged(int,int))); /* * List widget containing matched topics */ m_topicSearchListWidget = new QListWidget(); m_topicSearchListWidget->setSortingEnabled(false); QObject::connect(m_topicSearchListWidget, SIGNAL(itemClicked(QListWidgetItem*)), this, SLOT(topicSearchListWidgetItemClicked(QListWidgetItem*))); /* * Layout for search line edit and topics */ QWidget* searchWidgets = new QWidget(); QVBoxLayout* searchLayout = new QVBoxLayout(searchWidgets); WuQtUtilities::setLayoutSpacingAndMargins(searchLayout, 4, 2); searchLayout->addWidget(m_topicSearchLineEdit); searchLayout->addWidget(m_topicSearchListWidget); return searchWidgets; } /** * @return Create and return the help viewer widget. */ QWidget* HelpViewerDialog::createHelpViewerWidget() { /* DISPLAY HELP SECTION */ /* * create the back toolbar button */ QToolButton* backwardButton = new QToolButton; backwardButton->setArrowType(Qt::LeftArrow); backwardButton->setToolTip("Show the previous page"); /* * Create the forward toolbar button */ QToolButton* forwardButton = new QToolButton; forwardButton->setArrowType(Qt::RightArrow); forwardButton->setToolTip("Show the next page"); /* * Create the print toolbar button */ QToolButton* printButton = new QToolButton; connect(printButton, SIGNAL(clicked()), this, SLOT(helpPagePrintButtonClicked())); printButton->setText("Print"); printButton->hide(); /** * Copy button */ QToolButton* copyButton = new QToolButton; copyButton->setText("Copy"); copyButton->setToolTip("Copies selected help text to clipboard."); copyButton->setEnabled(false); /* * create the help browser */ m_helpBrowser = new HelpTextBrowser(this); m_helpBrowser->setMinimumWidth(400); m_helpBrowser->setMinimumHeight(200); m_helpBrowser->setOpenExternalLinks(false); m_helpBrowser->setOpenLinks(true); QObject::connect(forwardButton, SIGNAL(clicked()), m_helpBrowser, SLOT(forward())); QObject::connect(backwardButton, SIGNAL(clicked()), m_helpBrowser, SLOT(backward())); /* * Hook up copy button to help browser */ QObject::connect(m_helpBrowser, SIGNAL(copyAvailable(bool)), copyButton, SLOT(setEnabled(bool))); QObject::connect(copyButton, SIGNAL(clicked()), m_helpBrowser, SLOT(copy())); /* * Layout for toolbuttons */ QHBoxLayout* toolButtonLayout = new QHBoxLayout; toolButtonLayout->addWidget(new QLabel("Navigate:")); toolButtonLayout->addWidget(backwardButton); toolButtonLayout->addWidget(forwardButton); toolButtonLayout->addStretch(); toolButtonLayout->addWidget(copyButton); toolButtonLayout->addWidget(printButton); /* * Layout for help browser and buttons */ QWidget* helpBrowserWidgets = new QWidget; QVBoxLayout* helpBrowserLayout = new QVBoxLayout(helpBrowserWidgets); helpBrowserLayout->addLayout(toolButtonLayout); helpBrowserLayout->addWidget(m_helpBrowser); return helpBrowserWidgets; } /** * Show the help page with the given name. * * @param helpPageName * Name of help page. */ 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."); } /** * Load Workbench help from the given directory and add it to the * the given parent. Also process any subdirectories * * @param parent * Parent tree widget item * @param dirInfo * The directory examined for HTML pages and subdirectories * @return * Tree widget item that was created. */ HelpTreeWidgetItem* HelpViewerDialog::loadWorkbenchHelpInfoFromDirectory(QTreeWidgetItem* parent, const QFileInfo& dirInfo) { QDir directory(dirInfo.absoluteFilePath()); /* * Get all HTML pages in the directory * and file an HTML page that is the same * name as the directory. */ QStringList htmlNameFilter; htmlNameFilter << "*.htm" << "*.html"; QFileInfoList htmlFileList = directory.entryInfoList(htmlNameFilter, QDir::Files, QDir::Name); QString dirHtmlPageName; QFileInfoList otherHtmlPagesList; QListIterator htmlFileIter(htmlFileList); while (htmlFileIter.hasNext()) { const QFileInfo htmlFileInfo = htmlFileIter.next(); if (htmlFileInfo.baseName() == dirInfo.baseName()) { dirHtmlPageName = htmlFileInfo.absoluteFilePath(); } else { otherHtmlPagesList.append(htmlFileInfo.absoluteFilePath()); } } /* * Create a tree widget item for this directory * that may have a help page. */ HelpTreeWidgetItem* treeItem = NULL; if ( ! dirHtmlPageName.isEmpty()) { treeItem = createHelpTreeWidgetItemForHelpPage(parent, dirInfo.baseName(), dirHtmlPageName); } else { QString text = dirInfo.fileName(); text = text.replace('_', ' '); treeItem = HelpTreeWidgetItem::newInstanceEmptyItem(parent, text); } /* * Add items for any other HTML pages found in the directory */ QListIterator otherHtmlPageIter(otherHtmlPagesList); while (otherHtmlPageIter.hasNext()) { const QFileInfo pageInfo = otherHtmlPageIter.next(); createHelpTreeWidgetItemForHelpPage(treeItem, pageInfo.baseName(), pageInfo.absoluteFilePath()); } /* * Add any subdirectories as children */ QFileInfoList subDirList = directory.entryInfoList((QDir::AllDirs | QDir::NoDotAndDotDot), QDir::Name); QListIterator subDirIter(subDirList); while (subDirIter.hasNext()) { const QFileInfo subDirInfo = subDirIter.next(); const QString name = subDirInfo.fileName(); if (name.endsWith(".fld")) { /* * Ignore directories ending in ".fld" which is created when * a Microsoft Word file is saved as HTML. The ".fld" * directory contains image files from the Word file. */ } else { loadWorkbenchHelpInfoFromDirectory(treeItem, subDirInfo); } } return treeItem; } /** * load the index tree with the help topics. */ void HelpViewerDialog::loadHelpTopicsIntoIndexTree() { m_topicIndexTreeWidget->blockSignals(true); QTreeWidgetItem* workbenchItem = new QTreeWidgetItem(m_topicIndexTreeWidget, TREE_ITEM_NONE); workbenchItem->setText(0, "wb_view"); QDir resourceHelpDirectory(":/HelpFiles"); 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); while (subDirIter.hasNext()) { const QFileInfo subDirInfo = subDirIter.next(); HelpTreeWidgetItem* item = loadWorkbenchHelpInfoFromDirectory(workbenchItem, subDirInfo); /* * Is this the GLOSSARY? * If so, move it so that it is a top level item. */ if (subDirInfo.baseName().toLower() == "glossary") { if (glossaryItem != NULL) { CaretAssertMessage(0, "There should be only one glossary subdirectory !!!!"); } glossaryItem = item; workbenchItem->removeChild(glossaryItem); m_topicIndexTreeWidget->addTopLevelItem(glossaryItem); } } /* * Load commands */ CommandOperationManager* commandOperationManager = CommandOperationManager::getCommandOperationManager(); std::vector commandOperations = commandOperationManager->getCommandOperations(); QTreeWidgetItem* wbCommandItem = NULL; if ( ! commandOperations.empty()) { /* * Use map to sort commands by short description */ std::map sortCommandsMap; for (std::vector::iterator vecIter = commandOperations.begin(); vecIter != commandOperations.end(); vecIter++) { CommandOperation* op = *vecIter; sortCommandsMap.insert(std::make_pair(op->getCommandLineSwitch(), op)); } wbCommandItem = new QTreeWidgetItem(m_topicIndexTreeWidget, TREE_ITEM_NONE); wbCommandItem->setText(0, "wb_command"); QFont commandFont = wbCommandItem->font(0); commandFont.setPointSize(10); for (std::map::iterator mapIter = sortCommandsMap.begin(); mapIter != sortCommandsMap.end(); mapIter++) { CommandOperation* op = mapIter->second; HelpTreeWidgetItem* item = HelpTreeWidgetItem::newInstanceForCommandOperation(wbCommandItem, op); item->setFont(0, commandFont); addToAllItems(item); } } /* * Using setExpanded on a QTreeWidgetItem only expands its immediate children. * So, expand everything and then collapse Glossary and wb_command items so * that only wb_view items are expanded. */ m_topicIndexTreeWidget->expandAll(); if (glossaryItem != NULL) { glossaryItem->setExpanded(false); } if (wbCommandItem != NULL) { wbCommandItem->setExpanded(false); } m_topicIndexTreeWidget->sortItems(0, Qt::AscendingOrder); m_topicIndexTreeWidget->blockSignals(false); } /** * Add an item to the menu's item. * If the given item is NULL, it was not found an an error message will * be logged. * * @param parentMenu * The parent menu item. * @param item * The item that is added to the parent menu. * @param itemName * Name for item. */ void HelpViewerDialog::addItemToParentMenu(QTreeWidgetItem* parentMenu, QTreeWidgetItem* item, const AString& itemName) { CaretAssert(parentMenu); if (item != NULL) { if ( ! itemName.isEmpty()) { item->setText(0, itemName); } parentMenu->addChild(item); } else { CaretLogSevere("Did not find help for menu: " + itemName); } } /** * Create a help tree widget item for a help page URL. * * @param parent * Parent for item in index. * @param itemText * Text for the item shown in the topic index. * @param helpPageURL * URL for the help page. */ HelpTreeWidgetItem* HelpViewerDialog::createHelpTreeWidgetItemForHelpPage(QTreeWidgetItem* parent, const AString& itemText, const AString& helpPageURL) { HelpTreeWidgetItem* helpItem = HelpTreeWidgetItem::newInstanceForHtmlHelpPage(parent, itemText, helpPageURL); addToAllItems(helpItem); return helpItem; } /** * Called when selected index tree item changes. * * @param currentItem * The selected item * @param previousItem * The previously selected item */ void HelpViewerDialog::topicIndexTreeItemChanged(QTreeWidgetItem* currentItem, QTreeWidgetItem* /*previousItem*/) { if (currentItem != NULL) { /* * Note not all items are castable to HelpTreeWidgetItem. * Items not castable are category items that have an arrow to * expand/collapse its children. */ HelpTreeWidgetItem* helpItem = dynamic_cast(currentItem); if (helpItem != NULL) { displayHelpTextForHelpTreeWidgetItem(helpItem); m_topicIndexTreeWidget->scrollToItem(helpItem, QTreeWidget::EnsureVisible); } else { const AString html = AString(currentItem->text(0)).convertToHtmlPage(); m_helpBrowser->setHtml(html); } } } /** * Add the help item to all items and to the search list widget. * * @helpItem * Help item to add. */ void HelpViewerDialog::addToAllItems(HelpTreeWidgetItem* helpItem) { m_allHelpTreeWidgetItems.push_back(helpItem); } /** * Load the search list widget. List all Workbench items in alphabetical * order followed by all commands in alphabetical order. */ void HelpViewerDialog::loadSearchListWidget() { std::vector commandItems; std::vector workbenchItems; for (std::vector::iterator allIter = m_allHelpTreeWidgetItems.begin(); allIter != m_allHelpTreeWidgetItems.end(); allIter++) { HelpTreeWidgetItem* treeItem = *allIter; switch (treeItem->m_treeItemType) { case HelpTreeWidgetItem::TREE_ITEM_NONE: /* * Do not allow in search */ continue; break; case HelpTreeWidgetItem::TREE_ITEM_HELP_PAGE_URL: break; case HelpTreeWidgetItem::TREE_ITEM_HELP_TEXT: break; } HelpSearchListItem* searchItem = new HelpSearchListItem(treeItem); m_allHelpSearchListWidgetItems.push_back(searchItem); if (treeItem->text(0).startsWith("-")) { commandItems.push_back(searchItem); } else { workbenchItems.push_back(searchItem); } } std::sort(commandItems.begin(), commandItems.end(), HelpSearchListItem::sortAlphabetically); std::sort(workbenchItems.begin(), workbenchItems.end(), HelpSearchListItem::sortAlphabetically); for (std::vector::iterator wbIter = workbenchItems.begin(); wbIter != workbenchItems.end(); wbIter++) { m_topicSearchListWidget->addItem(*wbIter); } for (std::vector::iterator cmdIter = commandItems.begin(); cmdIter != commandItems.end(); cmdIter++) { m_topicSearchListWidget->addItem(*cmdIter); } } /** * Called when an item in the search list widget is clicked. * * @param item * The selected item */ void HelpViewerDialog::topicSearchListWidgetItemClicked(QListWidgetItem* item) { HelpSearchListItem* searchItem = dynamic_cast(item); if (searchItem != NULL) { HelpTreeWidgetItem* helpItem = searchItem->m_matchingTreeWidgetItem; if (helpItem != NULL) { displayHelpTextForHelpTreeWidgetItem(helpItem); } } } /** * Display the help information for the given help item. * * @param helpItem * Item for which help text is loaded. */ void HelpViewerDialog::displayHelpTextForHelpTreeWidgetItem(HelpTreeWidgetItem* helpItem) { CaretAssert(helpItem); m_helpBrowser->setSource(helpItem->m_helpPageURL); } /** * Called when search text is changed or return pressed to start * searching the help topics */ void HelpViewerDialog::topicSearchLineEditStartSearch() { const QString searchText = m_topicSearchLineEdit->text().trimmed(); const bool haveSearchTextFlag = ( ! searchText.isEmpty()); QRegExp regEx; bool haveWildcardSearchFlag = false; if (haveSearchTextFlag) { if (searchText.contains('*') || searchText.contains('?')) { haveWildcardSearchFlag = true; regEx.setPatternSyntax(QRegExp::Wildcard); regEx.setPattern(searchText); regEx.setCaseSensitivity(Qt::CaseInsensitive); } } for (std::vector::iterator iter = m_allHelpSearchListWidgetItems.begin(); iter != m_allHelpSearchListWidgetItems.end(); iter++) { HelpSearchListItem* helpListItem = *iter; CaretAssert(helpListItem); HelpTreeWidgetItem* helpTreeItem = helpListItem->m_matchingTreeWidgetItem; CaretAssert(helpTreeItem); bool showItemFlag = true; if (haveSearchTextFlag) { showItemFlag = false; if (haveWildcardSearchFlag) { if (regEx.exactMatch(helpTreeItem->m_helpText)) { showItemFlag = true; } } else if (helpTreeItem->m_helpText.contains(searchText, Qt::CaseInsensitive)) { showItemFlag = true; } } helpListItem->setHidden( ! showItemFlag); } } /** * called to print currently displayed page. */ void HelpViewerDialog::helpPagePrintButtonClicked() { QPrinter printer; QPrintDialog* printDialog = new QPrintDialog(&printer, this); if (printDialog->exec() == QPrintDialog::Accepted) { m_helpBrowser->document()->print(&printer); } } /** * Called when the cursor position is changed */ void HelpViewerDialog::topicSearchLineEditCursorPositionChanged(int,int) { if (m_topicSearchLineEditFirstMouseClick) { m_topicSearchLineEditFirstMouseClick = false; m_topicSearchLineEdit->clear(); topicSearchLineEditStartSearch(); } } /** * Expand all help topics */ void HelpViewerDialog::topicExpandAllTriggered() { m_topicIndexTreeWidget->expandAll(); } /** * Collapse all help topics */ void HelpViewerDialog::topicCollapseAllTriggered() { m_topicIndexTreeWidget->collapseAll(); } // ========================================================================= // /** * Create a help viewer widget. * * @param parentHelpViewerDialog * The parent help viewer dialog. */ HelpTextBrowser::HelpTextBrowser(HelpViewerDialog* parentHelpViewerDialog) : QTextBrowser(parentHelpViewerDialog), m_parentHelpViewerDialog(parentHelpViewerDialog) { CaretAssert(parentHelpViewerDialog); } /** * Destructor. */ HelpTextBrowser::~HelpTextBrowser() { } /** * Overrides superclass version so that images get loaded properly. * Setting search paths may eliminate need for this method. * * @param type * Type of resource. * @param url * URL of resource. * @return * QVariant containing content for display in the help viewer. */ QVariant HelpTextBrowser::loadResource(int type, const QUrl& url) { const QString urlText = url.toString(); QVariant result; for (std::vector::iterator iter = m_parentHelpViewerDialog->m_allHelpTreeWidgetItems.begin(); iter != m_parentHelpViewerDialog->m_allHelpTreeWidgetItems.end(); iter++) { HelpTreeWidgetItem* treeItem = *iter; if (treeItem->m_helpPageURL == urlText) { result = treeItem->m_helpText; break; } } if ( ! result.isValid()) { result = QTextBrowser::loadResource(type, url); if (result.isValid()) { /* nothing */ } else { QString typeName("Unknown"); if ( ! result.isValid()) { switch (type) { case QTextDocument::HtmlResource: typeName = "Html Resource"; break; case QTextDocument::ImageResource: typeName = "Image Resource"; break; case QTextDocument::StyleSheetResource: typeName = "Style Sheet Resource"; break; } } CaretLogSevere("Failed to load type: " + typeName + " file: " + url.toString()); } } return result; } /** * Set the source of the help browser. * * @param url * URL directing to content. */ void HelpTextBrowser::setSource(const QUrl& url) { const AString urlText = url.toString(); if (urlText.startsWith("http:")) { if (WuQMessageBox::warningOkCancel(this, "The link clicked will be displayed in your web browser.")) { if ( ! QDesktopServices::openUrl(urlText)) { WuQMessageBox::errorOk(this, ("Failed to load " + urlText)); } } } else if (urlText.startsWith("mailto")) { if (WuQMessageBox::warningOkCancel(this, "This link will open your mail client.")) { if ( ! QDesktopServices::openUrl(urlText)) { WuQMessageBox::errorOk(this, ("Failed to load " + urlText)); } } } else { QTextBrowser::setSource(url); } } // ========================================================================= // /** * Create a new help tree widget item for a wb_command item. * * @param parent * Parent for item in index. * @param commandOperation * The command. */ HelpTreeWidgetItem* HelpTreeWidgetItem::newInstanceForCommandOperation(QTreeWidgetItem* parent, CommandOperation* commandOperation) { const AString itemText = commandOperation->getCommandLineSwitch(); const AString helpInfoCopy = commandOperation->getHelpInformation("wb_command"); const AString helpText = helpInfoCopy.convertToHtmlPageWithFontSize(2); const AString helpPageURL("command:/" + commandOperation->getOperationShortDescription().replace(' ', '_')); HelpTreeWidgetItem* instance = new HelpTreeWidgetItem(parent, TREE_ITEM_HELP_TEXT, itemText, helpPageURL, helpText); return instance; } /** * Create a new help tree widget item for a help page URL. * * @param parent * Parent for item in index. * @param itemText * Text for the item shown in the topic index. * @param helpPageURL * URL for the help page. */ HelpTreeWidgetItem* HelpTreeWidgetItem::newInstanceForHtmlHelpPage(QTreeWidgetItem* parent, const AString& itemText, const AString& helpPageURL) { CaretAssertMessage( ( ! itemText.startsWith(":/")), "All help pages must be resources (page name starts with \":/\")"); QString helpText; QFile file(helpPageURL); if (file.exists()) { if (file.open(QFile::ReadOnly)) { QTextStream stream(&file); helpText = stream.readAll(); file.close(); } else { AString msg = ("Help file exists but unable to open for reading: " + helpPageURL); CaretLogSevere(msg); helpText = msg.convertToHtmlPage(); } } else { AString msg = ("HTML Help file missing: " + helpPageURL); CaretLogSevere(msg); helpText = msg.convertToHtmlPage(); } HelpTreeWidgetItem* instance = NULL; AString text = itemText; text = text.replace('_', ' '); if (parent != NULL) { instance = new HelpTreeWidgetItem(parent, TREE_ITEM_HELP_TEXT, text, "qrc" + helpPageURL, helpText); } else { instance = new HelpTreeWidgetItem(TREE_ITEM_HELP_TEXT, text, "qrc" + helpPageURL, helpText); } return instance; } /** * Constructor for item with parent but only a name. * * @param parent * Parent for item in index. * @param itemText * Text for the item shown in the topic index. */ HelpTreeWidgetItem* HelpTreeWidgetItem::newInstanceEmptyItem(QTreeWidgetItem* parent, const AString& itemText) { HelpTreeWidgetItem* item = new HelpTreeWidgetItem(parent, TREE_ITEM_NONE, itemText, "", ""); return item; } /** * Constructor for item with parent * * @param parent * Parent for item in index. * @param treeItemType * Type of tree item. * @param itemText * Text for the item shown in the topic index. * @param helpPageURL * URL for external help pages * @param helpText * Text displayed in help browser. */ HelpTreeWidgetItem::HelpTreeWidgetItem(QTreeWidgetItem* parent, const TreeItemType treeItemType, const AString& itemText, const AString& helpPageURL, const AString& helpText) : QTreeWidgetItem(parent), m_treeItemType(treeItemType), m_helpPageURL(helpPageURL), m_helpText(helpText) { setText(0, itemText); } /** * Constructor for item WITHOUT parent * * @param treeItemType * Type of tree item. * @param itemText * Text for the item shown in the topic index. * @param helpPageURL * URL for external help pages * @param helpText * Text displayed in help browser. */ HelpTreeWidgetItem::HelpTreeWidgetItem(const TreeItemType treeItemType, const AString& itemText, const AString& helpPageURL, const AString& helpText) : QTreeWidgetItem(), m_treeItemType(treeItemType), m_helpPageURL(helpPageURL), m_helpText(helpText) { setText(0, itemText); } /** * Destructor. */ HelpTreeWidgetItem::~HelpTreeWidgetItem() { } // ========================================================================= // /** * Constructs a list widget item for use during search operation * * @param matchingTreeWidgetItem * The matching tree widget item that contains the help information. */ HelpSearchListItem::HelpSearchListItem(HelpTreeWidgetItem* matchingTreeWidgetItem) { CaretAssert(matchingTreeWidgetItem); m_matchingTreeWidgetItem = matchingTreeWidgetItem; setText(m_matchingTreeWidgetItem->text(0)); } /** * Destructor. */ HelpSearchListItem::~HelpSearchListItem() { } /** * Sort the two "pointed to" items alphabetically. * * @param h1 * First item. * @param h2 * Second item. * @return * True if h1 is alphabetically before h2. */ bool HelpSearchListItem::sortAlphabetically(const HelpSearchListItem* h1, const HelpSearchListItem* h2) { CaretAssert(h1); CaretAssert(h2); return (h1->m_matchingTreeWidgetItem->text(0) < h2->m_matchingTreeWidgetItem->text(0)); } connectome-workbench-1.2.3+git41-gc4c6c90/src/GuiQt/HelpViewerDialog.h000066400000000000000000000156311300200146000251460ustar00rootroot00000000000000#ifndef __HELP_VIEWER_DIALOG_H__ #define __HELP_VIEWER_DIALOG_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 "WuQDialogNonModal.h" class QFileInfo; class QLineEdit; class QListWidget; class QSplitter; class QToolButton; class QTreeWidget; class QUrl; namespace caret { class CommandOperation; class HelpSearchListItem; class HelpTreeWidgetItem; class HelpViewerDialog : public WuQDialogNonModal { Q_OBJECT public: HelpViewerDialog(QWidget* parent, Qt::WindowFlags f = 0); virtual ~HelpViewerDialog(); virtual void updateDialog(); void showHelpPageWithName(const AString& helpPageName); // ADD_NEW_METHODS_HERE private slots: void topicIndexTreeItemChanged(QTreeWidgetItem* currentItem, QTreeWidgetItem* previousItem); void helpPagePrintButtonClicked(); void topicSearchLineEditStartSearch(); void topicSearchLineEditCursorPositionChanged(int,int); void topicExpandAllTriggered(); void topicCollapseAllTriggered(); void topicSearchListWidgetItemClicked(QListWidgetItem*); private: enum TreeItemType { TREE_ITEM_NONE, TREE_ITEM_HELP_PAGE, TREE_ITEM_WB_COMMAND }; HelpViewerDialog(const HelpViewerDialog&); HelpViewerDialog& operator=(const HelpViewerDialog&); QWidget* createTableOfContentsWidget(); QWidget* createIndexSearchWidget(); QWidget* createHelpViewerWidget(); void loadHelpTopicsIntoIndexTree(); HelpTreeWidgetItem* createHelpTreeWidgetItemForHelpPage(QTreeWidgetItem* parent, const AString& itemText, const AString& helpPageURL); void displayHelpTextForHelpTreeWidgetItem(HelpTreeWidgetItem* helpItem); void addItemToParentMenu(QTreeWidgetItem* parentMenu, QTreeWidgetItem* item, const AString& itemName); HelpTreeWidgetItem* loadWorkbenchHelpInfoFromDirectory(QTreeWidgetItem* parent, const QFileInfo& dirInfo); void addToAllItems(HelpTreeWidgetItem* helpItem); void loadSearchListWidget(); /// the help browser QTextBrowser* m_helpBrowser; /// the splitter QSplitter* m_splitter; /// the topic index tree widget QTreeWidget* m_topicIndexTreeWidget; /// line edit for searching topics QLineEdit* m_topicSearchLineEdit; QListWidget* m_topicSearchListWidget; /// tracks first mouse click in search topic line edit bool m_topicSearchLineEditFirstMouseClick; /// All help pages in tree widget std::vector m_allHelpTreeWidgetItems; /// All items in search list widget std::vector m_allHelpSearchListWidgetItems; // ADD_NEW_MEMBERS_HERE friend class HelpTextBrowser; }; /** * The help text browser. */ class HelpTextBrowser : public QTextBrowser { Q_OBJECT public: HelpTextBrowser(HelpViewerDialog* parentHelpViewerDialog); virtual ~HelpTextBrowser(); virtual QVariant loadResource(int type, const QUrl& url); virtual void setSource(const QUrl& url); private: HelpViewerDialog* m_parentHelpViewerDialog; }; /** * Base class for items in the tree widget. */ class HelpTreeWidgetItem : public QTreeWidgetItem { public: enum TreeItemType { TREE_ITEM_NONE, TREE_ITEM_HELP_PAGE_URL, TREE_ITEM_HELP_TEXT }; static HelpTreeWidgetItem* newInstanceForCommandOperation(QTreeWidgetItem* parent, CommandOperation* commandOperation); static HelpTreeWidgetItem* newInstanceForHtmlHelpPage(QTreeWidgetItem* parent, const AString& itemText, const AString& helpPageURL); static HelpTreeWidgetItem* newInstanceEmptyItem(QTreeWidgetItem* parent, const AString& itemText); private: HelpTreeWidgetItem(QTreeWidgetItem* parent, const TreeItemType treeItemType, const AString& itemText, const AString& helpPageURL, const AString& helpText); HelpTreeWidgetItem(const TreeItemType treeItemType, const AString& itemText, const AString& helpPageURL, const AString& helpText); public: virtual ~HelpTreeWidgetItem(); const TreeItemType m_treeItemType; const AString m_helpPageURL; const AString m_helpText; }; class HelpSearchListItem : public QListWidgetItem { public: HelpSearchListItem(HelpTreeWidgetItem* matchingTreeWidgetItem); ~HelpSearchListItem(); static bool sortAlphabetically(const HelpSearchListItem* h1, const HelpSearchListItem* h2); HelpTreeWidgetItem* m_matchingTreeWidgetItem; }; #ifdef __HELP_VIEWER_DIALOG_DECLARE__ // #endif // __HELP_VIEWER_DIALOG_DECLARE__ } // namespace #endif //__HELP_VIEWER_DIALOG_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/GuiQt/HyperLinkTextBrowser.cxx000066400000000000000000000107601300200146000264430ustar00rootroot00000000000000/*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" #include "HyperLinkTextBrowser.h" using namespace caret; /** * \class caret::HyperLinkTextBrowser * \brief Displays HTML text. * \ingroup GuiQt * * Displays HTML text. Input text may contain URLs * which are converted into hyperlinks. */ /** * Constructor */ HyperLinkTextBrowser::HyperLinkTextBrowser(QWidget* parent) : QTextBrowser(parent) { //QT4setTextFormat(QTextBrowser::RichText); } /** * Destructor */ HyperLinkTextBrowser::~HyperLinkTextBrowser() { } /** * Override of QT's QTextBrowser method. * Called when the user clicks a link in this browser. * @param url * User for display. */ void HyperLinkTextBrowser::setSource(const QUrl& url) { const AString s(url.toString()); if (s.startsWith("vocabulary://")) { this->appendHtml("Vocabulary links not supported yet."); // const AString name = s.mid(13); // BrainModelIdentification* bmi = theMainWindow->getBrainSet()->getBrainModelIdentification(); // const AString idString(bmi->getIdentificationTextForVocabulary(true, name)); // if (idString.isEmpty() == false) { // appendHtml(idString); // } } else { this->appendHtml("Displaying of web pages not supported yet."); // theMainWindow->displayWebPage(s); } } /** * Append text (Override of QT's QTextBrowser method). * Any URLs in text are converted to hyperlinks. */ void HyperLinkTextBrowser::append(const AString& textIn) { // // See if string contains a URL // AString text; if (textIn.indexOf("http://") >= 0) { // // Insert the string with hyperlinks into text browser // text = textIn.convertURLsToHyperlinks(); } else { text = textIn; } text.replace("\n", "
"); AString displayText = toHtml(); displayText.append("
"); displayText.append(text); setHtml(displayText); // // Scroll to newest text (at end of scroll bar) // QScrollBar* vsb = verticalScrollBar(); vsb->setValue(vsb->maximum()); } /** * Append html. * @param html * HTML that is appended. */ void HyperLinkTextBrowser::appendHtml(const AString& html) { AString displayText = toHtml(); displayText.append("
"); displayText.append(html); setHtml(displayText); // // Scroll to newest text (at end of scroll bar) // QScrollBar* vsb = verticalScrollBar(); vsb->setValue(vsb->maximum()); } /** * Set the content of the browser to the given html and then * scroll to the bottom. * * @param html * Html for browser. */ void HyperLinkTextBrowser::setContentToHtml(const AString& html) { this->setHtml(html); // // Scroll to newest text (at end of scroll bar) // QScrollBar* vsb = verticalScrollBar(); vsb->setValue(vsb->maximum()); } /** * Set the content of the browser to the given text and then * scroll to the bottom. * * @param textIn * Text for browser. */ void HyperLinkTextBrowser::setContentToText(const AString& textIn) { // // See if string contains a URL // AString displayText; if (textIn.indexOf("http://") != -1) { // // Insert the string with hyperlinks into text browser // displayText = textIn.convertURLsToHyperlinks(); } else { displayText = textIn; } displayText.replace("\n", "
"); setHtml(displayText); // // Scroll to newest text (at end of scroll bar) // QScrollBar* vsb = verticalScrollBar(); vsb->setValue(vsb->maximum()); } /** * called if a key is pressed over the text browser. * @param key * Key that is pressed. */ void HyperLinkTextBrowser::keyPressEvent(QKeyEvent* /*e*/) { emit keyPressed(); } connectome-workbench-1.2.3+git41-gc4c6c90/src/GuiQt/HyperLinkTextBrowser.h000066400000000000000000000032201300200146000260610ustar00rootroot00000000000000/*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*/ #ifndef __HYPER_LINK_TEXT_BROWSER_H__ #define __HYPER_LINK_TEXT_BROWSER_H__ #include class QKeyEvent; namespace caret { class AString; class HyperLinkTextBrowser : public QTextBrowser { Q_OBJECT public: HyperLinkTextBrowser(QWidget* parent = 0); ~HyperLinkTextBrowser(); void setContentToHtml(const AString& html); void setContentToText(const AString& text); signals: void keyPressed(); public slots: void append(const AString& text); void appendHtml(const AString& html); private slots: void setSource(const QUrl& url); private: void keyPressEvent(QKeyEvent* e); }; } #endif // __HYPER_LINK_TEXT_BROWSER_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/GuiQt/IdentifyBrainordinateDialog.cxx000066400000000000000000001073261300200146000277270ustar00rootroot00000000000000 /*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 __IDENTIFY_BRAINORDINATE_DIALOG_DECLARE__ #include "IdentifyBrainordinateDialog.h" #undef __IDENTIFY_BRAINORDINATE_DIALOG_DECLARE__ using namespace caret; #include #include #include #include #include #include #include "Brain.h" #include "BrainordinateRegionOfInterest.h" #include "BrainStructure.h" #include "CaretAssert.h" #include "CaretDataFileSelectionComboBox.h" #include "CaretDataFileSelectionModel.h" #include "CaretLogger.h" #include "CiftiFiberTrajectoryFile.h" #include "CaretMappableDataFile.h" #include "CiftiConnectivityMatrixDataFileManager.h" #include "CiftiFiberTrajectoryManager.h" #include "CiftiMappableConnectivityMatrixDataFile.h" #include "CaretMappableDataFileAndMapSelectionModel.h" #include "CaretMappableDataFileAndMapSelectorObject.h" #include "CiftiParcelSelectionComboBox.h" #include "EventGraphicsUpdateAllWindows.h" #include "EventIdentificationHighlightLocation.h" #include "EventManager.h" #include "EventSurfaceColoringInvalidate.h" #include "EventUpdateInformationWindows.h" #include "EventUserInterfaceUpdate.h" #include "GiftiLabelTableSelectionComboBox.h" #include "GuiManager.h" #include "IdentifiedItemNode.h" #include "IdentificationManager.h" #include "SelectionItemCiftiConnectivityMatrixRowColumn.h" #include "SelectionItemSurfaceNode.h" #include "SelectionItemVoxel.h" #include "SelectionManager.h" #include "SessionManager.h" #include "StructureEnumComboBox.h" #include "Surface.h" #include "SystemUtilities.h" #include "WuQFactory.h" #include "WuQGroupBoxExclusiveWidget.h" #include "WuQMessageBox.h" #include "WuQtUtilities.h" #include static const int INDEX_SPIN_BOX_WIDTH = 100; /** * Constructor. */ IdentifyBrainordinateDialog::IdentifyBrainordinateDialog(QWidget* parent) : WuQDialogNonModal("Identify Brainordinate", parent) { /* * Surface Vertex widgets */ m_surfaceVertexWidget = createSurfaceVertexlWidget(); /* * Filter file types for CIFTI type files */ std::vector allDataFileTypes; DataFileTypeEnum::getAllEnums(allDataFileTypes, DataFileTypeEnum::OPTIONS_INCLUDE_CONNECTIVITY_DENSE_DYNAMIC); std::vector supportedCiftiRowFileTypes; std::vector supportedLabelFileTypes; for (std::vector::iterator dtIter = allDataFileTypes.begin(); dtIter != allDataFileTypes.end(); dtIter++) { const DataFileTypeEnum::Enum dataFileType = *dtIter; bool ciftiRowFlag = false; bool labelFileFlag = false; ParcelSourceDimension parcelSourceDimension = PARCEL_SOURCE_INVALID_DIMENSION; switch (dataFileType) { case DataFileTypeEnum::ANNOTATION: break; case DataFileTypeEnum::BORDER: break; case DataFileTypeEnum::CONNECTIVITY_DENSE: ciftiRowFlag = true; break; case DataFileTypeEnum::CONNECTIVITY_DENSE_DYNAMIC: ciftiRowFlag = true; break; case DataFileTypeEnum::CONNECTIVITY_DENSE_LABEL: labelFileFlag = true; break; case DataFileTypeEnum::CONNECTIVITY_DENSE_PARCEL: ciftiRowFlag = true; break; case DataFileTypeEnum::CONNECTIVITY_PARCEL: parcelSourceDimension = PARCEL_SOURCE_LOADING_DIMENSION; break; case DataFileTypeEnum::CONNECTIVITY_PARCEL_DENSE: parcelSourceDimension = PARCEL_SOURCE_LOADING_DIMENSION; break; case DataFileTypeEnum::CONNECTIVITY_PARCEL_LABEL: parcelSourceDimension = PARCEL_SOURCE_MAPPING_DIMENSION; labelFileFlag = true; break; case DataFileTypeEnum::CONNECTIVITY_PARCEL_SCALAR: parcelSourceDimension = PARCEL_SOURCE_MAPPING_DIMENSION; break; case DataFileTypeEnum::CONNECTIVITY_PARCEL_SERIES: parcelSourceDimension = PARCEL_SOURCE_MAPPING_DIMENSION; break; case DataFileTypeEnum::CONNECTIVITY_DENSE_SCALAR: ciftiRowFlag = true; break; case DataFileTypeEnum::CONNECTIVITY_DENSE_TIME_SERIES: ciftiRowFlag = true; break; case DataFileTypeEnum::CONNECTIVITY_FIBER_ORIENTATIONS_TEMPORARY: break; case DataFileTypeEnum::CONNECTIVITY_FIBER_TRAJECTORY_TEMPORARY: ciftiRowFlag = true; break; case DataFileTypeEnum::CONNECTIVITY_SCALAR_DATA_SERIES: break; case DataFileTypeEnum::FOCI: break; case DataFileTypeEnum::LABEL: labelFileFlag = true; break; case DataFileTypeEnum::IMAGE: 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 (parcelSourceDimension != PARCEL_SOURCE_INVALID_DIMENSION) { CaretAssert(parcelSourceDimension != PARCEL_SOURCE_INVALID_DIMENSION); m_parcelSourceDimensionMap.insert(std::make_pair(dataFileType, parcelSourceDimension)); } if (ciftiRowFlag) { supportedCiftiRowFileTypes.push_back(dataFileType); } if (labelFileFlag) { supportedLabelFileTypes.push_back(dataFileType); } } /* * CIFTI row widgets */ m_ciftiRowWidget = createCiftiRowWidget(supportedCiftiRowFileTypes); /* * Label files widget */ m_labelFileWidgets.m_widget = createLabelFilesWidget(supportedLabelFileTypes); /* * CIFTI Parcel Widgets */ m_ciftiParcelWidget = createCiftiParcelWidget(); m_widgetBox = new WuQGroupBoxExclusiveWidget(); m_widgetBox->addWidget(m_ciftiRowWidget, "Identify Brainordinate from CIFTI File Row"); m_widgetBox->addWidget(m_ciftiParcelWidget, "Identify CIFTI File Parcel"); m_widgetBox->addWidget(m_labelFileWidgets.m_widget, "Identify Label"); m_widgetBox->addWidget(m_surfaceVertexWidget, "Identify Surface Vertex"); QObject::connect(m_widgetBox, SIGNAL(currentChanged(int32_t)), this, SLOT(selectedWidgetChanged())); setCentralWidget(m_widgetBox, WuQDialog::SCROLL_AREA_NEVER); updateDialog(); EventManager::get()->addEventListener(this, EventTypeEnum::EVENT_USER_INTERFACE_UPDATE); } /** * Destructor. */ IdentifyBrainordinateDialog::~IdentifyBrainordinateDialog() { EventManager::get()->removeAllEventsFromListener(this); delete m_ciftiRowFileSelectionModel; m_ciftiRowFileSelectionModel = NULL; } /** * Receive an event. * * @param event * The event that the receive can respond to. */ void IdentifyBrainordinateDialog::receiveEvent(Event* event) { if (event->getEventType() == EventTypeEnum::EVENT_USER_INTERFACE_UPDATE) { EventUserInterfaceUpdate* uiEvent = dynamic_cast(event); CaretAssert(uiEvent); uiEvent->setEventProcessed(); updateDialog(); } } /** * Create and return the CIFTI Parcel Widget * * @param supportedFileTypes * The supported file types for this widget. * @return * The CIFTI Parcel Widget */ QWidget* IdentifyBrainordinateDialog::createCiftiParcelWidget() { int columnCount = 0; const int COLUMN_LABEL = columnCount++; const int COLUMN_MAP_LEFT = columnCount++; const int COLUMN_MAP_RIGHT = columnCount++; m_ciftiParcelFileLabel = new QLabel("File"); m_ciftiParcelFileMapLabel = new QLabel("Map"); std::vector supportedParcelFileTypes; for (std::map::iterator iter = m_parcelSourceDimensionMap.begin(); iter != m_parcelSourceDimensionMap.end(); iter++) { const DataFileTypeEnum::Enum dataFileType = iter->first; CaretAssert(dataFileType != DataFileTypeEnum::UNKNOWN); supportedParcelFileTypes.push_back(dataFileType); } m_ciftiParcelFileSelector = new CaretMappableDataFileAndMapSelectorObject(supportedParcelFileTypes, CaretMappableDataFileAndMapSelectorObject::OPTION_SHOW_MAP_INDEX_SPIN_BOX, this); QObject::connect(m_ciftiParcelFileSelector, SIGNAL(selectionWasPerformed()), this, SLOT(slotParcelFileOrMapSelectionChanged())); m_ciftiParcelFileSelector->getWidgetsForAddingToLayout(m_ciftiParcelFileComboBox, m_ciftiParcelFileMapSpinBox, m_ciftiParcelFileMapComboBox); m_ciftiParcelFileMapSpinBox->setFixedWidth(INDEX_SPIN_BOX_WIDTH); m_ciftiParcelFileMapSpinBox->setToolTip("Map indices start at one."); m_ciftiParcelFileParcelLabel = new QLabel("Parcel"); m_ciftiParcelFileParcelNameComboBox = new CiftiParcelSelectionComboBox(this); /* * Widget and layout */ QWidget* widget = new QWidget(); QGridLayout* ciftiParcelLayout = new QGridLayout(widget); ciftiParcelLayout->addWidget(m_ciftiParcelFileLabel, 0, COLUMN_LABEL); ciftiParcelLayout->addWidget(m_ciftiParcelFileComboBox, 0, COLUMN_MAP_LEFT, 1, 2); ciftiParcelLayout->addWidget(m_ciftiParcelFileMapLabel, 1, COLUMN_LABEL); ciftiParcelLayout->addWidget(m_ciftiParcelFileMapSpinBox, 1, COLUMN_MAP_LEFT); //, Qt::AlignLeft); ciftiParcelLayout->addWidget(m_ciftiParcelFileMapComboBox, 1, COLUMN_MAP_RIGHT); //, Qt::AlignLeft); ciftiParcelLayout->addWidget(m_ciftiParcelFileParcelLabel, 2, COLUMN_LABEL); ciftiParcelLayout->addWidget(m_ciftiParcelFileParcelNameComboBox->getWidget(), 2, COLUMN_MAP_LEFT, 1, 2); return widget; } /** * Create and return the CIFTI Row Widget * * @param supportedFileTypes * The supported file types for this widget. * @return * The CIFTI Row Widget */ QWidget* IdentifyBrainordinateDialog::createCiftiRowWidget(const std::vector& supportedFileTypes) { int columnCount = 0; const int COLUMN_LABEL = columnCount++; const int COLUMN_MAP_LEFT = columnCount++; /* * CIFTI Row selection */ m_ciftiRowFileLabel = new QLabel("File"); m_ciftiRowFileSelectionModel = CaretDataFileSelectionModel::newInstanceForCaretDataFileTypes(GuiManager::get()->getBrain(), supportedFileTypes); m_ciftiRowFileComboBox = new CaretDataFileSelectionComboBox(this); m_ciftiRowFileIndexLabel = new QLabel("Row Index"); m_ciftiRowFileIndexSpinBox = WuQFactory::newSpinBoxWithMinMaxStep(0, std::numeric_limits::max(), 1); m_ciftiRowFileIndexSpinBox->setFixedWidth(INDEX_SPIN_BOX_WIDTH); m_ciftiRowFileIndexSpinBox->setToolTip("Row indices start at zero."); QWidget* widget = new QWidget(); QGridLayout* ciftiRowLayout = new QGridLayout(widget); ciftiRowLayout->addWidget(m_ciftiRowFileLabel, 0, COLUMN_LABEL); ciftiRowLayout->addWidget(m_ciftiRowFileComboBox->getWidget(), 0, COLUMN_MAP_LEFT, 1, 2); ciftiRowLayout->addWidget(m_ciftiRowFileIndexLabel, 1, COLUMN_LABEL); ciftiRowLayout->addWidget(m_ciftiRowFileIndexSpinBox, 1, COLUMN_MAP_LEFT); return widget; } /** * Create and return the Label Files Widget * * @param supportedFileTypes * The supported file types for this widget. * @return * The Label Files Row Widget */ QWidget* IdentifyBrainordinateDialog::createLabelFilesWidget(const std::vector& supportedFileTypes) { int columnCount = 0; const int COLUMN_LABEL = columnCount++; const int COLUMN_MAP_LEFT = columnCount++; const int COLUMN_MAP_RIGHT = columnCount++; m_labelFileWidgets.m_fileLabel = new QLabel("File"); m_labelFileWidgets.m_fileMapLabel = new QLabel("Map"); m_labelFileWidgets.m_fileSelector = new CaretMappableDataFileAndMapSelectorObject(supportedFileTypes, CaretMappableDataFileAndMapSelectorObject::OPTION_SHOW_MAP_INDEX_SPIN_BOX, this); QObject::connect(m_labelFileWidgets.m_fileSelector, SIGNAL(selectionWasPerformed()), this, SLOT(slotLabelFileOrMapSelectionChanged())); m_labelFileWidgets.m_fileSelector->getWidgetsForAddingToLayout(m_labelFileWidgets.m_fileComboBox, m_labelFileWidgets.m_fileMapSpinBox, m_labelFileWidgets.m_fileMapComboBox); m_labelFileWidgets.m_fileMapSpinBox->setFixedWidth(INDEX_SPIN_BOX_WIDTH); m_labelFileWidgets.m_fileMapSpinBox->setToolTip("Map indices start at one."); m_labelFileWidgets.m_fileLabellLabel = new QLabel("Label"); m_labelFileWidgets.m_fileLabelComboBox = new GiftiLabelTableSelectionComboBox(this); /* * Widget and layout */ QWidget* widget = new QWidget(); QGridLayout* labelWidgetLayout = new QGridLayout(widget); labelWidgetLayout->addWidget(m_labelFileWidgets.m_fileLabel, 0, COLUMN_LABEL); labelWidgetLayout->addWidget(m_labelFileWidgets.m_fileComboBox, 0, COLUMN_MAP_LEFT, 1, 2); labelWidgetLayout->addWidget(m_labelFileWidgets.m_fileMapLabel, 1, COLUMN_LABEL); labelWidgetLayout->addWidget(m_labelFileWidgets.m_fileMapSpinBox, 1, COLUMN_MAP_LEFT); //, Qt::AlignLeft); labelWidgetLayout->addWidget(m_labelFileWidgets.m_fileMapComboBox, 1, COLUMN_MAP_RIGHT); //, Qt::AlignLeft); labelWidgetLayout->addWidget(m_labelFileWidgets.m_fileLabellLabel, 2, COLUMN_LABEL); labelWidgetLayout->addWidget(m_labelFileWidgets.m_fileLabelComboBox->getWidget(), 2, COLUMN_MAP_LEFT, 1, 2); return widget; } /** * Called when the user changes the label file or map. */ void IdentifyBrainordinateDialog::slotLabelFileOrMapSelectionChanged() { updateDialog(); } /** * @return Create a Surface Vertex Widget */ QWidget* IdentifyBrainordinateDialog::createSurfaceVertexlWidget() { int columnCount = 0; const int COLUMN_LABEL = columnCount++; const int COLUMN_MAP_LEFT = columnCount++; m_vertexStructureLabel = new QLabel("Structure"); m_vertexStructureComboBox = new StructureEnumComboBox(this); m_vertexStructureComboBox->listOnlyValidStructures(); m_vertexIndexLabel = new QLabel("Vertex Index"); m_vertexIndexSpinBox = WuQFactory::newSpinBoxWithMinMaxStep(0, std::numeric_limits::max(), 1); m_vertexIndexSpinBox->setFixedWidth(INDEX_SPIN_BOX_WIDTH); m_vertexIndexSpinBox->setToolTip("Vertex indices start at zero."); QWidget* widget = new QWidget(); QGridLayout* surfaceVertexLayout = new QGridLayout(widget); surfaceVertexLayout->addWidget(m_vertexStructureLabel, 0, COLUMN_LABEL); surfaceVertexLayout->addWidget(m_vertexStructureComboBox->getWidget(), 0, COLUMN_MAP_LEFT, 1, 2); surfaceVertexLayout->addWidget(m_vertexIndexLabel, 1, COLUMN_LABEL); surfaceVertexLayout->addWidget(m_vertexIndexSpinBox, 1, COLUMN_MAP_LEFT); return widget; } /** * Called when the user changes the parcel file or map. */ void IdentifyBrainordinateDialog::slotParcelFileOrMapSelectionChanged() { updateDialog(); } /** * Called when the selected widget changes. */ void IdentifyBrainordinateDialog::selectedWidgetChanged() { updateDialog(); } /** * Update the dialog. */ void IdentifyBrainordinateDialog::updateDialog() { CaretMappableDataFileAndMapSelectionModel* parcelFileModel = m_ciftiParcelFileSelector->getModel(); m_ciftiParcelFileSelector->updateFileAndMapSelector(parcelFileModel); CaretMappableDataFile* parcelFile = parcelFileModel->getSelectedFile(); bool parcelsMapValidFlag = false; if (parcelFile != NULL) { const int32_t mapIndex = parcelFileModel->getSelectedMapIndex(); if ((mapIndex >= 0) && (mapIndex < parcelFile->getNumberOfMaps())) { CiftiMappableDataFile* ciftiMapFile = dynamic_cast(parcelFile); if (ciftiMapFile != NULL) { const ParcelSourceDimension parcelSourceDimension = getParcelSourceDimensionFromFile(parcelFile); switch (parcelSourceDimension) { case PARCEL_SOURCE_INVALID_DIMENSION: CaretAssertMessage(0, "Should never be invalid."); break; case PARCEL_SOURCE_LOADING_DIMENSION: m_ciftiParcelFileParcelNameComboBox->updateComboBox(ciftiMapFile->getCiftiParcelsMapForLoading()); parcelsMapValidFlag = true; break; case PARCEL_SOURCE_MAPPING_DIMENSION: m_ciftiParcelFileParcelNameComboBox->updateComboBox(ciftiMapFile->getCiftiParcelsMapForBrainordinateMapping()); parcelsMapValidFlag = true; break; } } } } if ( ! parcelsMapValidFlag) { m_ciftiParcelFileParcelNameComboBox->updateComboBox(NULL); } CaretMappableDataFileAndMapSelectionModel* labelFileModel = m_labelFileWidgets.m_fileSelector->getModel(); m_labelFileWidgets.m_fileSelector->updateFileAndMapSelector(labelFileModel); CaretMappableDataFile* labelFile = labelFileModel->getSelectedFile(); bool labelTableComboBoxValid = false; if (labelFile != NULL) { const int32_t mapIndex = labelFileModel->getSelectedMapIndex(); if ((mapIndex >= 0) && (mapIndex < labelFile->getNumberOfMaps())) { GiftiLabelTable* labelTable = labelFile->getMapLabelTable(mapIndex); m_labelFileWidgets.m_fileLabelComboBox->updateContent(labelTable); labelTableComboBoxValid = true; } } if ( ! labelTableComboBoxValid) { m_labelFileWidgets.m_fileLabelComboBox->updateContent(NULL); } m_ciftiRowFileComboBox->updateComboBox(m_ciftiRowFileSelectionModel); m_vertexStructureComboBox->listOnlyValidStructures(); } /** * Get the source of parcels for a caret data file. * * @param mapFile * The caret data file. * @return * The source for parcels (loading or mapping dimension). */ IdentifyBrainordinateDialog::ParcelSourceDimension IdentifyBrainordinateDialog::getParcelSourceDimensionFromFile(const CaretMappableDataFile* mapFile) { ParcelSourceDimension parcelSourceDim = PARCEL_SOURCE_INVALID_DIMENSION; std::map::iterator typeIter = m_parcelSourceDimensionMap.find(mapFile->getDataFileType()); if (typeIter != m_parcelSourceDimensionMap.end()) { parcelSourceDim = typeIter->second; } CaretAssert(parcelSourceDim != PARCEL_SOURCE_INVALID_DIMENSION); return parcelSourceDim; } /** * Gets called when Apply button is clicked. */ void IdentifyBrainordinateDialog::applyButtonClicked() { AString errorMessage; QWidget* selectedWidget = m_widgetBox->currentWidget(); if (m_surfaceVertexWidget == selectedWidget) { processSurfaceVertexWidget(errorMessage); } else if (m_ciftiParcelWidget == selectedWidget) { processCiftiParcelWidget(errorMessage); } else if (m_ciftiRowWidget == selectedWidget) { processCiftiRowWidget(errorMessage); } else if (m_labelFileWidgets.m_widget == selectedWidget) { processLabelFileWidget(errorMessage); } else { const QString msg("Choose one of the methods for identifying a brainordinate."); errorMessage = (WuQtUtilities::createWordWrappedToolTipText(msg)); } if ( ! errorMessage.isEmpty()) { WuQMessageBox::errorOk(this, errorMessage); return; } WuQDialogNonModal::applyButtonClicked(); } /** * Update coloring and redraw all windows. */ void IdentifyBrainordinateDialog::updateColoringAndDrawAllWindows() { EventManager::get()->sendEvent(EventSurfaceColoringInvalidate().getPointer()); EventManager::get()->sendEvent(EventGraphicsUpdateAllWindows().getPointer()); } /** * Flash the brainordinate highlighting region of interest. * * @param brainROI * brainordinate highlighting region of interest that is flashed. */ void IdentifyBrainordinateDialog::flashBrainordinateHighlightingRegionOfInterest(BrainordinateRegionOfInterest* brainROI) { const float flashDelayTime = 0.25; const int32_t flashCount = 4; for (int32_t iFlash = 0; iFlash < flashCount; iFlash++) { brainROI->setBrainordinateHighlightingEnabled(true); updateColoringAndDrawAllWindows(); SystemUtilities::sleepSeconds(flashDelayTime); brainROI->setBrainordinateHighlightingEnabled(false); updateColoringAndDrawAllWindows(); SystemUtilities::sleepSeconds(flashDelayTime); } } /** * Process user's selectons in the Label File Widget * * @param errorMessageOut * Output containing error message. */ void IdentifyBrainordinateDialog::processLabelFileWidget(AString& errorMessageOut) { Brain* brain = GuiManager::get()->getBrain(); CaretMappableDataFile* mapFile = m_labelFileWidgets.m_fileSelector->getModel()->getSelectedFile(); const int32_t mapIndex = m_labelFileWidgets.m_fileSelector->getModel()->getSelectedMapIndex(); if (mapFile != NULL) { const AString labelName = m_labelFileWidgets.m_fileLabelComboBox->getSelectedLabelName(); BrainordinateRegionOfInterest* brainROI = brain->getBrainordinateHighlightRegionOfInterest(); if (brainROI->setWithLabelFileLabel(mapFile, mapIndex, labelName, errorMessageOut)) { flashBrainordinateHighlightingRegionOfInterest(brainROI); } } } /** * Process user's selectons in the CIFTI Parcel Widget * * @param errorMessageOut * Output containing error message. */ void IdentifyBrainordinateDialog::processCiftiParcelWidget(AString& errorMessageOut) { Brain* brain = GuiManager::get()->getBrain(); CaretMappableDataFile* mapFile = m_ciftiParcelFileSelector->getModel()->getSelectedFile(); const int32_t mapIndex = m_ciftiParcelFileSelector->getModel()->getSelectedMapIndex(); if (mapFile != NULL) { CiftiMappableDataFile* ciftiFile = dynamic_cast(mapFile); const QString parcelName = m_ciftiParcelFileParcelNameComboBox->getSelectedParcelName(); const ParcelSourceDimension parcelSourceDimension = getParcelSourceDimensionFromFile(ciftiFile); BrainordinateRegionOfInterest* brainROI = brain->getBrainordinateHighlightRegionOfInterest(); switch (parcelSourceDimension) { case PARCEL_SOURCE_INVALID_DIMENSION: CaretAssertMessage(0, "Should never be invalid."); break; case PARCEL_SOURCE_LOADING_DIMENSION: brainROI->setWithCiftiParcelLoadingBrainordinates(ciftiFile, mapIndex, parcelName, errorMessageOut); break; case PARCEL_SOURCE_MAPPING_DIMENSION: brainROI->setWithCiftiParcelMappingBrainordinates(ciftiFile, mapIndex, parcelName, errorMessageOut); break; } if (brainROI->hasSurfaceNodes() || brainROI->hasVolumeVoxels()) { /* * Need to load data? */ if (parcelSourceDimension == PARCEL_SOURCE_LOADING_DIMENSION) { CiftiMappableConnectivityMatrixDataFile* connMatrixFile = dynamic_cast(ciftiFile); if (connMatrixFile != NULL) { SelectionManager* selectionManager = brain->getSelectionManager(); selectionManager->reset(); selectionManager->setAllSelectionsEnabled(true); SelectionItemCiftiConnectivityMatrixRowColumn* selectionCiftiRowColumn = selectionManager->getCiftiConnectivityMatrixRowColumnIdentification(); const CiftiParcelsMap* ciftiParcelsMap = connMatrixFile->getCiftiParcelsMapForLoading(); const int64_t parcelIndex = ciftiParcelsMap->getIndexFromNumberOrName(parcelName); if (parcelIndex >= 0) { switch (connMatrixFile->getChartMatrixLoadingDimension()) { case ChartMatrixLoadingDimensionEnum::CHART_MATRIX_LOADING_BY_COLUMN: selectionCiftiRowColumn->setFileColumn(connMatrixFile, parcelIndex); break; case ChartMatrixLoadingDimensionEnum::CHART_MATRIX_LOADING_BY_ROW: selectionCiftiRowColumn->setFileRow(connMatrixFile, parcelIndex); break; } GuiManager::get()->processIdentification(-1, // invalid tab index selectionManager, this); } else { errorMessageOut = ("Parcel name=" + parcelName + " not found in parcels map for file " + connMatrixFile->getFileNameNoPath()); } } } /* * Highlight the selected parcel */ flashBrainordinateHighlightingRegionOfInterest(brainROI); } else { brainROI->setBrainordinateHighlightingEnabled(false); updateColoringAndDrawAllWindows(); } } else { errorMessageOut = ("No parcel-type file is selected."); } } /** * Process user's selectons in the * * @param errorMessageOut * Output containing error message. */ void IdentifyBrainordinateDialog::processCiftiRowWidget(AString& errorMessageOut) { Brain* brain = GuiManager::get()->getBrain(); SelectionManager* selectionManager = brain->getSelectionManager(); selectionManager->reset(); selectionManager->setAllSelectionsEnabled(true); CaretDataFile* dataFile = m_ciftiRowFileSelectionModel->getSelectedFile(); if (dataFile != NULL) { CiftiMappableDataFile* ciftiMapFile = dynamic_cast(dataFile); CiftiFiberTrajectoryFile* ciftiTrajFile = dynamic_cast(dataFile); const int32_t selectedCiftiRowIndex = m_ciftiRowFileIndexSpinBox->value(); try { StructureEnum::Enum surfaceStructure; int32_t surfaceNodeIndex; int32_t surfaceNumberOfNodes; bool surfaceNodeValid = false; int64_t voxelIJK[3]; float voxelXYZ[3]; bool voxelValid = false; if (ciftiMapFile != NULL) { ciftiMapFile->getBrainordinateFromRowIndex(selectedCiftiRowIndex, surfaceStructure, surfaceNodeIndex, surfaceNumberOfNodes, surfaceNodeValid, voxelIJK, voxelXYZ, voxelValid); } else if (ciftiTrajFile != NULL) { ciftiTrajFile->getBrainordinateFromRowIndex(selectedCiftiRowIndex, surfaceStructure, surfaceNodeIndex, surfaceNumberOfNodes, surfaceNodeValid, voxelIJK, voxelXYZ, voxelValid); } else { errorMessageOut = "Neither CIFTI Mappable nor CIFTI Trajectory file. Has new file type been added?"; } if (surfaceNodeValid) { SelectionItemSurfaceNode* surfaceID = selectionManager->getSurfaceNodeIdentification(); const Surface* surface = brain->getPrimaryAnatomicalSurfaceForStructure(surfaceStructure); if (surface != NULL) { if ((surfaceNodeIndex >= 0) && (surfaceNodeIndex < surface->getNumberOfNodes())) { surfaceID->setSurface(const_cast(surface)); surfaceID->setBrain(brain); const float* xyz = surface->getCoordinate(surfaceNodeIndex); const double doubleXYZ[3] = { xyz[0], xyz[1], xyz[2] }; surfaceID->setModelXYZ(doubleXYZ); surfaceID->setNodeNumber(surfaceNodeIndex); GuiManager::get()->processIdentification(-1, // invalid tab index selectionManager, this); } else { errorMessageOut = ("Surface vertex index " + AString::number(surfaceNodeIndex) + " is not valid for surface " + surface->getFileNameNoPath()); } } else{ errorMessageOut = ("No surfaces are loaded for structure " + StructureEnum::toGuiName(surfaceStructure)); } } else if (voxelValid) { SelectionItemVoxel* voxelID = selectionManager->getVoxelIdentification(); voxelID->setBrain(brain); voxelID->setEnabledForSelection(true); voxelID->setVoxelIdentification(brain, ciftiMapFile, voxelIJK, 0.0); const double doubleXYZ[3] = { voxelXYZ[0], voxelXYZ[1], voxelXYZ[2] }; voxelID->setModelXYZ(doubleXYZ); GuiManager::get()->processIdentification(-1, // invalid tab index selectionManager, this); } } catch (const DataFileException& dfe) { errorMessageOut = dfe.whatString(); } } } /** * Process user's selectons in the * * @param errorMessageOut * Output containing error message. */ void IdentifyBrainordinateDialog::processSurfaceVertexWidget(AString& errorMessageOut) { Brain* brain = GuiManager::get()->getBrain(); SelectionManager* selectionManager = brain->getSelectionManager(); selectionManager->reset(); const StructureEnum::Enum selectedStructure = m_vertexStructureComboBox->getSelectedStructure(); const int32_t selectedVertexIndex = m_vertexIndexSpinBox->value(); BrainStructure* bs = brain->getBrainStructure(selectedStructure, false); if (bs != NULL) { if (selectedVertexIndex < bs->getNumberOfNodes()) { Surface* surface = bs->getPrimaryAnatomicalSurface(); if (surface != NULL) { SelectionItemSurfaceNode* nodeID = selectionManager->getSurfaceNodeIdentification(); nodeID->setBrain(brain); nodeID->setNodeNumber(selectedVertexIndex); nodeID->setSurface(surface); const float* fxyz = surface->getCoordinate(selectedVertexIndex); const double xyz[3] = { fxyz[0], fxyz[1], fxyz[2] }; nodeID->setModelXYZ(xyz); GuiManager::get()->processIdentification(-1, selectionManager, this); } else { errorMessageOut = ("PROGRAM ERROR: Primary Anatomical Surface not found for structure " + StructureEnum::toName(selectedStructure)); } } else { errorMessageOut = ("Vertex Index " + AString::number(selectedVertexIndex) + " is out of range [0, " + AString::number(bs->getNumberOfNodes() - 1) + "] for " + StructureEnum::toGuiName(selectedStructure)); } } else { errorMessageOut = ("Structure " + StructureEnum::toName(selectedStructure) + " not found."); } } connectome-workbench-1.2.3+git41-gc4c6c90/src/GuiQt/IdentifyBrainordinateDialog.h000066400000000000000000000131021300200146000273400ustar00rootroot00000000000000#ifndef __IDENTIFY_BRAINORDINATE_DIALOG_H__ #define __IDENTIFY_BRAINORDINATE_DIALOG_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 "DataFileTypeEnum.h" #include "EventListenerInterface.h" #include "WuQDialogNonModal.h" class QLabel; class QRadioButton; class QSpinBox; namespace caret { class BrainordinateRegionOfInterest; class CaretDataFileSelectionComboBox; class CaretDataFileSelectionModel; class CaretMappableDataFileAndMapSelectorObject; class CaretMappableDataFile; class CiftiParcelSelectionComboBox; class GiftiLabelTableSelectionComboBox; class StructureEnumComboBox; class WuQGroupBoxExclusiveWidget; class IdentifyBrainordinateDialog : public WuQDialogNonModal, public EventListenerInterface { Q_OBJECT public: IdentifyBrainordinateDialog(QWidget* parent); virtual ~IdentifyBrainordinateDialog(); virtual void updateDialog(); virtual void receiveEvent(Event* event); protected: virtual void applyButtonClicked(); private: IdentifyBrainordinateDialog(const IdentifyBrainordinateDialog&); IdentifyBrainordinateDialog& operator=(const IdentifyBrainordinateDialog&); public: // ADD_NEW_METHODS_HERE private slots: void slotParcelFileOrMapSelectionChanged(); void slotLabelFileOrMapSelectionChanged(); void selectedWidgetChanged(); private: enum Mode { MODE_NONE, MODE_CIFTI_PARCEL, MODE_CIFTI_ROW, MODE_SURFACE_VERTEX }; enum ParcelSourceDimension { PARCEL_SOURCE_INVALID_DIMENSION, PARCEL_SOURCE_LOADING_DIMENSION, PARCEL_SOURCE_MAPPING_DIMENSION }; // ADD_NEW_MEMBERS_HERE QWidget* createCiftiParcelWidget(); QWidget* createCiftiRowWidget(const std::vector& supportedFileTypes); QWidget* createLabelFilesWidget(const std::vector& supportedFileTypes); QWidget* createSurfaceVertexlWidget(); void processCiftiParcelWidget(AString& errorMessageOut); void processCiftiRowWidget(AString& errorMessageOut); void processLabelFileWidget(AString& errorMessageOut); void processSurfaceVertexWidget(AString& errorMessageOut); void flashBrainordinateHighlightingRegionOfInterest(BrainordinateRegionOfInterest* brainROI); void updateColoringAndDrawAllWindows(); ParcelSourceDimension getParcelSourceDimensionFromFile(const CaretMappableDataFile* mapFile); StructureEnumComboBox* m_vertexStructureComboBox; QWidget* m_surfaceVertexWidget; QLabel* m_vertexStructureLabel; QSpinBox* m_vertexIndexSpinBox; QLabel* m_vertexIndexLabel; QWidget* m_ciftiRowWidget; struct LabelFileWidgets { QWidget* m_widget; CaretMappableDataFileAndMapSelectorObject* m_fileSelector; QLabel* m_fileLabel; QLabel* m_fileMapLabel; QWidget* m_fileComboBox; QWidget* m_fileMapSpinBox; QWidget* m_fileMapComboBox; QLabel* m_fileLabellLabel; GiftiLabelTableSelectionComboBox* m_fileLabelComboBox; }; LabelFileWidgets m_labelFileWidgets; QLabel* m_ciftiRowFileLabel; CaretDataFileSelectionComboBox* m_ciftiRowFileComboBox; CaretDataFileSelectionModel* m_ciftiRowFileSelectionModel; QLabel* m_ciftiRowFileIndexLabel; QSpinBox* m_ciftiRowFileIndexSpinBox; QWidget* m_ciftiParcelWidget; QLabel* m_ciftiParcelFileLabel; QLabel* m_ciftiParcelFileMapLabel; QWidget* m_ciftiParcelFileComboBox; QWidget* m_ciftiParcelFileMapSpinBox; QWidget* m_ciftiParcelFileMapComboBox; QLabel* m_ciftiParcelFileParcelLabel; CiftiParcelSelectionComboBox* m_ciftiParcelFileParcelNameComboBox; CaretMappableDataFileAndMapSelectorObject* m_ciftiParcelFileSelector; WuQGroupBoxExclusiveWidget* m_widgetBox; std::map m_parcelSourceDimensionMap; }; #ifdef __IDENTIFY_BRAINORDINATE_DIALOG_DECLARE__ #endif // __IDENTIFY_BRAINORDINATE_DIALOG_DECLARE__ } // namespace #endif //__IDENTIFY_BRAINORDINATE_DIALOG_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/GuiQt/ImageCaptureDialog.cxx000066400000000000000000001271251300200146000260170ustar00rootroot00000000000000 /*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 #include #include #include #include #include #include #define __IMAGE_CAPTURE_DIALOG__H__DECLARE__ #include "ImageCaptureDialog.h" #undef __IMAGE_CAPTURE_DIALOG__H__DECLARE__ #include "Brain.h" #include "BrainBrowserWindow.h" #include "CaretAssert.h" #include "CaretPreferences.h" #include "ChartMatrixDisplayProperties.h" #include "DataFileException.h" #include "EnumComboBoxTemplate.h" #include "EventBrowserWindowGraphicsRedrawn.h" #include "EventGraphicsUpdateOneWindow.h" #include "EventImageCapture.h" #include "EventManager.h" #include "FileInformation.h" #include "GuiManager.h" #include "ImageFile.h" #include "ImageCaptureSettings.h" #include "SessionManager.h" #include "CaretFileDialog.h" #include "WuQFactory.h" #include "WuQMessageBox.h" #include "WuQTimedMessageDisplay.h" #include "WuQtUtilities.h" using namespace caret; /** * \class caret::ImageCaptureDialog * \brief Dialog for capturing images. * \ingroup GuiQt * */ /** * Constructor for editing a palette selection. * * @param parent * Parent widget on which this dialog is displayed. */ ImageCaptureDialog::ImageCaptureDialog(BrainBrowserWindow* parent) : WuQDialogNonModal("Image Capture", parent) { m_imageDimensionsWidget = NULL; setDeleteWhenClosed(false); /* * Use Apply button for image capture */ setApplyButtonText("Capture"); /* * Image Source */ QWidget* imageSourceWidget = createImageSourceSection(); /* * Image Size * Note: Label is updated when window size is updated */ m_imageDimensionsWidget = createImageDimensionsSection(); /* * Image Options */ QWidget* imageOptionsWidget = createImageOptionsSection(); /* * Image Destination */ QWidget* imageDestinationWidget = createImageDestinationSection(); QWidget* w = new QWidget(); QVBoxLayout* layout = new QVBoxLayout(w); layout->addWidget(imageSourceWidget); layout->addWidget(m_imageDimensionsWidget); layout->addWidget(imageOptionsWidget); layout->addWidget(imageDestinationWidget); setCentralWidget(w, WuQDialog::SCROLL_AREA_NEVER); /* * Make apply button the default button. */ QPushButton* applyButton = getDialogButtonBox()->button(QDialogButtonBox::Apply); CaretAssert(applyButton); applyButton->setAutoDefault(true); applyButton->setDefault(true); 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); updateDimensionsSection(); } /** * Destructor. */ ImageCaptureDialog::~ImageCaptureDialog() { EventManager::get()->removeAllEventsFromListener(this); } /** * @return Create and return the image source section. */ QWidget* ImageCaptureDialog::createImageSourceSection() { QLabel* windowLabel = new QLabel("Workbench Window: "); m_windowSelectionSpinBox = new QSpinBox(); m_windowSelectionSpinBox->setRange(1, BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_WINDOWS); m_windowSelectionSpinBox->setSingleStep(1); m_windowSelectionSpinBox->setFixedWidth(60); QObject::connect(m_windowSelectionSpinBox, SIGNAL(valueChanged(int)), this, SLOT(updateBrowserWindowWidthAndHeightLabel())); const QString cbTT = WuQtUtilities::createWordWrappedToolTipText("When Tab/Window Lock Aspect is selected, empty " "regions may appear in the graphics region. Selection " "of this option will exclude these empty regions in " "the captured image."); m_windowCropToLockAspectRegionCheckBox = new QCheckBox("Crop to Tab/Window Lock Aspect Region"); m_windowCropToLockAspectRegionCheckBox->setChecked(true); QObject::connect(m_windowCropToLockAspectRegionCheckBox, SIGNAL(clicked(bool)), this, SLOT(cropToTabWindowLockAspectRegionClicked(bool))); m_windowCropToLockAspectRegionCheckBox->setToolTip(cbTT); 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); return groupBox; } /** * @return Create and return the image options section. */ QWidget* ImageCaptureDialog::createImageOptionsSection() { m_imageAutoCropCheckBox = new QCheckBox("Automatically Crop Image"); QObject::connect(m_imageAutoCropCheckBox, SIGNAL(clicked(bool)), this, SLOT(imageCroppingCheckBoxClicked(bool))); QLabel* imageAutoCropMarginLabel = new QLabel(" Margin"); m_imageAutoCropMarginSpinBox = new QSpinBox(); QObject::connect(m_imageAutoCropMarginSpinBox, SIGNAL(valueChanged(int)), this, SLOT(imageCroppingMarginValueChanged(int))); m_imageAutoCropMarginSpinBox->setMinimum(0); m_imageAutoCropMarginSpinBox->setMaximum(100000); m_imageAutoCropMarginSpinBox->setSingleStep(1); m_imageAutoCropMarginSpinBox->setMaximumWidth(100); QHBoxLayout* cropMarginLayout = new QHBoxLayout(); cropMarginLayout->addWidget(imageAutoCropMarginLabel); cropMarginLayout->addWidget(m_imageAutoCropMarginSpinBox); cropMarginLayout->addStretch(); QGroupBox* groupBox = new QGroupBox("Image Options"); QVBoxLayout* layout = new QVBoxLayout(groupBox); layout->addWidget(m_imageAutoCropCheckBox); layout->addLayout(cropMarginLayout); return groupBox; } /** * @return Create and return the image dimensions section. */ QWidget* ImageCaptureDialog::createImageDimensionsSection() { m_imageSizeWindowRadioButton = new QRadioButton("Size of Window"); m_imageSizeCustomRadioButton = new QRadioButton("Custom"); QButtonGroup* sizeButtonGroup = new QButtonGroup(this); sizeButtonGroup->addButton(m_imageSizeWindowRadioButton); sizeButtonGroup->addButton(m_imageSizeCustomRadioButton); QObject::connect(sizeButtonGroup, SIGNAL(buttonClicked(QAbstractButton*)), this, SLOT(sizeRadioButtonClicked(QAbstractButton*))); QLabel* customPixelsWidthLabel = new QLabel("Width:"); QLabel* customPixelsHeightLabel = new QLabel("Height:"); QLabel* customUnitsWidthLabel = new QLabel("Width:"); QLabel* customUnitsHeightLabel = new QLabel("Height:"); QLabel* customResolutionLabel = new QLabel("Resolution:"); const int pixelSpinBoxWidth = 80; QLabel* pixelDimensionsLabel = new QLabel("Pixel Dimensions"); m_pixelWidthSpinBox = new QSpinBox(); m_pixelWidthSpinBox->setFixedWidth(pixelSpinBoxWidth); m_pixelWidthSpinBox->setRange(100, 10000000); m_pixelWidthSpinBox->setSingleStep(1); QObject::connect(m_pixelWidthSpinBox, SIGNAL(valueChanged(int)), this, SLOT(pixelWidthValueChanged(int))); m_pixelHeightSpinBox = new QSpinBox(); m_pixelHeightSpinBox->setFixedWidth(pixelSpinBoxWidth); m_pixelHeightSpinBox->setRange(100, 10000000); m_pixelHeightSpinBox->setSingleStep(1); QObject::connect(m_pixelHeightSpinBox, SIGNAL(valueChanged(int)), this, SLOT(pixelHeightValueChanged(int))); const int imageSpinBoxWidth = 100; QLabel* imageDimensionsLabel = new QLabel("Image Dimensions"); m_imageWidthSpinBox = new QDoubleSpinBox(); m_imageWidthSpinBox->setFixedWidth(imageSpinBoxWidth); m_imageWidthSpinBox->setRange(0.01, 100000000.0); m_imageWidthSpinBox->setSingleStep(0.1); QObject::connect(m_imageWidthSpinBox, SIGNAL(valueChanged(double)), this, SLOT(imageWidthValueChanged(double))); m_imageHeightSpinBox = new QDoubleSpinBox(); m_imageHeightSpinBox->setFixedWidth(imageSpinBoxWidth); m_imageHeightSpinBox->setRange(0.01, 100000000.0); m_imageHeightSpinBox->setSingleStep(0.1); QObject::connect(m_imageHeightSpinBox, SIGNAL(valueChanged(double)), this, SLOT(imageHeightValueChanged(double))); m_imageResolutionSpinBox = new QDoubleSpinBox(); m_imageResolutionSpinBox->setFixedWidth(imageSpinBoxWidth); m_imageResolutionSpinBox->setRange(0.01, 1000000); m_imageResolutionSpinBox->setSingleStep(1); QObject::connect(m_imageResolutionSpinBox, SIGNAL(valueChanged(double)), this, SLOT(imageResolutionValueChanged(double))); m_imageSpatialUnitsEnumComboBox = new EnumComboBoxTemplate(this); m_imageSpatialUnitsEnumComboBox->setup(); QObject::connect(m_imageSpatialUnitsEnumComboBox, SIGNAL(itemActivated()), this, SLOT(imageSizeUnitsEnumComboBoxItemActivated())); m_imagePixelsPerSpatialUnitsEnumComboBox = new EnumComboBoxTemplate(this); m_imagePixelsPerSpatialUnitsEnumComboBox->setup(); QObject::connect(m_imagePixelsPerSpatialUnitsEnumComboBox, SIGNAL(itemActivated()), this, SLOT(imageResolutionUnitsEnumComboBoxItemActivated())); m_scaleProportionallyCheckBox = new QCheckBox("Scale Proportionally"); WuQtUtilities::setWordWrappedToolTip(m_scaleProportionallyCheckBox, ("If checked, the heights of the pixel and image dimensions " "are automatically adjusted so that their proportions " "match the proportion of the selected window's " "graphics region.")); QObject::connect(m_scaleProportionallyCheckBox, SIGNAL(clicked(bool)), this, SLOT(scaleProportionallyCheckBoxClicked(bool))); QWidget* pixelsSizeWidget = new QWidget(); QGridLayout* pixelsSizeLayout = new QGridLayout(pixelsSizeWidget); WuQtUtilities::setLayoutSpacingAndMargins(pixelsSizeLayout, 4, 0); int pixelsRow = 0; pixelsSizeLayout->addWidget(pixelDimensionsLabel, pixelsRow, 0, 1, 2, Qt::AlignHCenter); pixelsRow++; pixelsSizeLayout->addWidget(customPixelsWidthLabel, pixelsRow, 0); pixelsSizeLayout->addWidget(m_pixelWidthSpinBox, pixelsRow, 1); pixelsRow++; pixelsSizeLayout->addWidget(customPixelsHeightLabel, pixelsRow, 0); pixelsSizeLayout->addWidget(m_pixelHeightSpinBox, pixelsRow, 1); pixelsRow++; pixelsSizeLayout->addWidget(WuQtUtilities::createHorizontalLineWidget(), pixelsRow, 0, 1, 2); pixelsRow++; pixelsSizeLayout->addWidget(m_scaleProportionallyCheckBox, pixelsRow, 0, 1, 2, Qt::AlignLeft); pixelsRow++; pixelsSizeWidget->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); QWidget* imageUnitsWidget = new QWidget(); QGridLayout* imageUnitsLayout = new QGridLayout(imageUnitsWidget); WuQtUtilities::setLayoutSpacingAndMargins(imageUnitsLayout, 4, 0); int unitsRow = 0; imageUnitsLayout->addWidget(imageDimensionsLabel, unitsRow, 0, 1, 3, Qt::AlignHCenter); unitsRow++; imageUnitsLayout->addWidget(customUnitsWidthLabel, unitsRow, 0); imageUnitsLayout->addWidget(m_imageWidthSpinBox, unitsRow, 1); imageUnitsLayout->addWidget(m_imageSpatialUnitsEnumComboBox->getWidget(), unitsRow, 2, 2, 1); unitsRow++; imageUnitsLayout->addWidget(customUnitsHeightLabel, unitsRow, 0); imageUnitsLayout->addWidget(m_imageHeightSpinBox, unitsRow, 1); unitsRow++; imageUnitsLayout->addWidget(customResolutionLabel, unitsRow, 0); imageUnitsLayout->addWidget(m_imageResolutionSpinBox, unitsRow, 1); imageUnitsLayout->addWidget(m_imagePixelsPerSpatialUnitsEnumComboBox->getWidget(), unitsRow, 2); unitsRow++; imageUnitsWidget->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); m_customDimensionsWidget = new QWidget(); QHBoxLayout* customDimensionsLayout = new QHBoxLayout(m_customDimensionsWidget); customDimensionsLayout->addSpacing(20); customDimensionsLayout->addWidget(pixelsSizeWidget, 0, Qt::AlignTop); customDimensionsLayout->addWidget(WuQtUtilities::createVerticalLineWidget()); customDimensionsLayout->addWidget(imageUnitsWidget, 0, Qt::AlignTop); QLabel* imageBytesLabel = new QLabel("Uncompressed Image Memory Size: "); m_imageNumberOfBytesLabel = new QLabel(" "); QWidget* imageBytesWidget = new QWidget(); QHBoxLayout* imageBytesLayout = new QHBoxLayout(imageBytesWidget); WuQtUtilities::setLayoutSpacingAndMargins(imageBytesLayout, 4, 0); imageBytesLayout->addWidget(imageBytesLabel); imageBytesLayout->addWidget(m_imageNumberOfBytesLabel); imageBytesLayout->addStretch(); QGroupBox* groupBox = new QGroupBox("Dimensions"); QVBoxLayout* layout = new QVBoxLayout(groupBox); layout->addWidget(m_imageSizeWindowRadioButton, 0, Qt::AlignLeft); layout->addWidget(m_imageSizeCustomRadioButton, 0, Qt::AlignLeft); layout->addWidget(m_customDimensionsWidget, 0, Qt::AlignLeft); layout->addWidget(WuQtUtilities::createHorizontalLineWidget()); layout->addWidget(imageBytesWidget); // // m_imageSizeWindowRadioButton->setChecked(true); // sizeRadioButtonClicked(sizeButtonGroup->checkedButton()); return groupBox; } /** * Gets called when the Window Size or Custom radio button is clicked. * * @param button * Button that was clicked. */ void ImageCaptureDialog::sizeRadioButtonClicked(QAbstractButton* /*button*/) { ImageCaptureSettings* imageCaptureSettings = SessionManager::get()->getImageCaptureDialogSettings(); if (m_imageSizeCustomRadioButton->isChecked()) { imageCaptureSettings->setImageCaptureDimensionsMode(ImageCaptureDimensionsModeEnum::IMAGE_CAPTURE_DIMENSIONS_MODE_CUSTOM); } else if (m_imageSizeWindowRadioButton->isChecked()) { imageCaptureSettings->setImageCaptureDimensionsMode(ImageCaptureDimensionsModeEnum::IMAGE_CAPTURE_DIMENSIONS_MODE_WINDOW_SIZE); } updateDimensionsSection(); // updateDialogWithImageDimensionsModel(); // updateImageNumberOfBytesLabel(); } /** * Gets called when the image resolution units are changed. */ void ImageCaptureDialog::imageResolutionUnitsEnumComboBoxItemActivated() { ImageCaptureSettings* imageCaptureSettings = SessionManager::get()->getImageCaptureDialogSettings(); imageCaptureSettings->setImageResolutionUnits(m_imagePixelsPerSpatialUnitsEnumComboBox->getSelectedItem()); updateDimensionsSection(); } /** * Gets called when the image size units are changed. */ void ImageCaptureDialog::imageSizeUnitsEnumComboBoxItemActivated() { ImageCaptureSettings* imageCaptureSettings = SessionManager::get()->getImageCaptureDialogSettings(); imageCaptureSettings->setSpatialUnits(m_imageSpatialUnitsEnumComboBox->getSelectedItem()); updateDimensionsSection(); } /** * Gets called when Crop to Tab/Window Lock Aspect Region checkbox clicked * * @param checked * New checked status. */ void ImageCaptureDialog::cropToTabWindowLockAspectRegionClicked(bool checked) { ImageCaptureSettings* imageCaptureSettings = SessionManager::get()->getImageCaptureDialogSettings(); imageCaptureSettings->setCropToTabWindowLockAspectRegionEnabled(checked); updateBrowserWindowWidthAndHeightLabel(); } /** * May be called to update the dialog's content. */ void ImageCaptureDialog::updateDialog() { ImageCaptureSettings* imageCaptureSettings = SessionManager::get()->getImageCaptureDialogSettings(); m_windowCropToLockAspectRegionCheckBox->setChecked(imageCaptureSettings->isCropToTabWindowLockAspectRegionEnabled()); updateBrowserWindowWidthAndHeightLabel(); updateDimensionsSection(); updateImageOptionsSection(); updateDestinationSection(); } /** * Update the section. */ void ImageCaptureDialog::updateDimensionsSection() { ImageCaptureSettings* imageCaptureSettings = SessionManager::get()->getImageCaptureDialogSettings(); CaretAssert(imageCaptureSettings); switch (imageCaptureSettings->getImageCaptureDimensionsMode()) { 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_pixelWidthSpinBox->blockSignals(true); m_pixelWidthSpinBox->setValue(imageCaptureSettings->getPixelWidth()); m_pixelWidthSpinBox->blockSignals(false); m_pixelHeightSpinBox->blockSignals(true); m_pixelHeightSpinBox->setValue(imageCaptureSettings->getPixelHeight()); m_pixelHeightSpinBox->blockSignals(false); m_scaleProportionallyCheckBox->setChecked(imageCaptureSettings->isScaleProportionately()); m_imageWidthSpinBox->blockSignals(true); m_imageWidthSpinBox->setValue(imageCaptureSettings->getSpatialWidth()); m_imageWidthSpinBox->blockSignals(false); m_imageHeightSpinBox->blockSignals(true); m_imageHeightSpinBox->setValue(imageCaptureSettings->getSpatialHeight()); m_imageHeightSpinBox->blockSignals(false); m_imageResolutionSpinBox->blockSignals(true); m_imageResolutionSpinBox->setValue(imageCaptureSettings->getImageResolutionInSelectedUnits()); m_imageResolutionSpinBox->blockSignals(false); m_imageSpatialUnitsEnumComboBox->setSelectedItem(imageCaptureSettings->getSpatialUnits()); m_imagePixelsPerSpatialUnitsEnumComboBox->setSelectedItem(imageCaptureSettings->getImageResolutionUnits()); AString windowSizeText = "Size of Window"; int32_t width; int32_t height; float aspectRatio; if (getSelectedWindowWidthAndHeight(width, height, aspectRatio)) { windowSizeText += (" (" + AString::number(width) + " x " + AString::number(height) + " pixels)"); if (imageCaptureSettings->isScaleProportionately()) { imageCaptureSettings->updateForAspectRatio(width, height); } CaretAssert(m_imageDimensionsWidget); m_imageDimensionsWidget->setEnabled(true); } else { windowSizeText += (" (Invalid Window Number)"); CaretAssert(m_imageDimensionsWidget); m_imageDimensionsWidget->setEnabled(false); } m_imageSizeWindowRadioButton->setText(windowSizeText); updateImageNumberOfBytesLabel(); } /** * Update the section. */ void ImageCaptureDialog::updateImageOptionsSection() { ImageCaptureSettings* imageCaptureSettings = SessionManager::get()->getImageCaptureDialogSettings(); CaretAssert(imageCaptureSettings); m_imageAutoCropCheckBox->setChecked(imageCaptureSettings->isCroppingEnabled()); m_imageAutoCropMarginSpinBox->blockSignals(true); m_imageAutoCropMarginSpinBox->setValue(imageCaptureSettings->getCroppingMargin()); m_imageAutoCropMarginSpinBox->blockSignals(false); } /** * Update the section. */ void ImageCaptureDialog::updateDestinationSection() { ImageCaptureSettings* imageCaptureSettings = SessionManager::get()->getImageCaptureDialogSettings(); CaretAssert(imageCaptureSettings); m_copyImageToClipboardCheckBox->setChecked(imageCaptureSettings->isCopyToClipboardEnabled()); m_saveImageToFileCheckBox->setChecked(imageCaptureSettings->isSaveToFileEnabled()); 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. */ void ImageCaptureDialog::updateImageNumberOfBytesLabel() { int32_t imageWidth = 0; int32_t imageHeight = 0; if (m_imageSizeCustomRadioButton->isChecked()) { imageWidth = m_pixelWidthSpinBox->value(); imageHeight = m_pixelHeightSpinBox->value(); } else if (m_imageSizeWindowRadioButton->isChecked()) { float aspectRatio = 0.0; if ( ! getSelectedWindowWidthAndHeight(imageWidth, imageHeight, aspectRatio)) { imageWidth = 0; imageHeight = 0; } } const int64_t bytesPerPixel = 3; // RGB const int64_t numberOfBytes = (static_cast(imageWidth) * static_cast(imageHeight) * bytesPerPixel); if (numberOfBytes > 0) { const AString sizeString = FileInformation::fileSizeToStandardUnits(numberOfBytes); m_imageNumberOfBytesLabel->setText(sizeString); } else { m_imageNumberOfBytesLabel->setText("Invalid/Unknown"); } } /** * Update the radio containing the size of the window. */ void ImageCaptureDialog::updateBrowserWindowWidthAndHeightLabel() { ImageCaptureSettings* imageCaptureSettings = SessionManager::get()->getImageCaptureDialogSettings(); AString windowSizeText = "Size of Window"; int32_t width; int32_t height; float aspectRatio; if (getSelectedWindowWidthAndHeight(width, height, aspectRatio)) { windowSizeText += (" (" + AString::number(width) + " x " + AString::number(height) + " pixels)"); if (m_scaleProportionallyCheckBox->isChecked()) { imageCaptureSettings->updateForAspectRatio(width, height); } updateDimensionsSection(); CaretAssert(m_imageDimensionsWidget); m_imageDimensionsWidget->setEnabled(true); } else { windowSizeText += (" (Invalid Window Number)"); CaretAssert(m_imageDimensionsWidget); m_imageDimensionsWidget->setEnabled(false); } m_imageSizeWindowRadioButton->setText(windowSizeText); } /** * Called when pixel width value is changed. * * @param value * New value. */ void ImageCaptureDialog::pixelWidthValueChanged(int value) { ImageCaptureSettings* imageCaptureSettings = SessionManager::get()->getImageCaptureDialogSettings(); imageCaptureSettings->setPixelWidth(value); updateDimensionsSection(); } /** * Called when pixel height value is changed. * * @param value * New value. */ void ImageCaptureDialog::pixelHeightValueChanged(int value) { ImageCaptureSettings* imageCaptureSettings = SessionManager::get()->getImageCaptureDialogSettings(); imageCaptureSettings->setPixelHeight(value); updateDimensionsSection(); } /** * Called when image width value is changed. * * @param value * New value. */ void ImageCaptureDialog::imageWidthValueChanged(double value) { ImageCaptureSettings* imageCaptureSettings = SessionManager::get()->getImageCaptureDialogSettings(); imageCaptureSettings->setSpatialWidth(value); updateDimensionsSection(); } /** * Called when image height value is changed. * * @param value * New value. */ void ImageCaptureDialog::imageHeightValueChanged(double value) { ImageCaptureSettings* imageCaptureSettings = SessionManager::get()->getImageCaptureDialogSettings(); imageCaptureSettings->setSpatialHeight(value); updateDimensionsSection(); } /** * Called when image resolution value changed. * * @param value * New value. */ void ImageCaptureDialog::imageResolutionValueChanged(double value) { ImageCaptureSettings* imageCaptureSettings = SessionManager::get()->getImageCaptureDialogSettings(); imageCaptureSettings->setImageResolutionInSelectedUnits(value); updateDimensionsSection(); } /** * Called when scale proportionately check box clicked. * * @parm checked * New checked status. */ void ImageCaptureDialog::scaleProportionallyCheckBoxClicked(bool checked) { ImageCaptureSettings* imageCaptureSettings = SessionManager::get()->getImageCaptureDialogSettings(); imageCaptureSettings->setScaleProportionately(checked); if (checked) { /* * Will cause pixel height to change appropriately */ int32_t windowWidth; int32_t windowHeight; float aspectRatio; if (getSelectedWindowWidthAndHeight(windowWidth, windowHeight, aspectRatio)) { imageCaptureSettings->updateForAspectRatio(windowWidth, windowHeight); updateDimensionsSection(); } } } /** * Called when value for cropping margin is changed. * * @parm value * New value for cropping margin. */ void ImageCaptureDialog::imageCroppingMarginValueChanged(int value) { ImageCaptureSettings* imageCaptureSettings = SessionManager::get()->getImageCaptureDialogSettings(); imageCaptureSettings->setCroppingMargin(value); } /** * Called when scale proportionately check box clicked. * * @parm checked * New checked status. */ void ImageCaptureDialog::imageCroppingCheckBoxClicked(bool checked) { ImageCaptureSettings* imageCaptureSettings = SessionManager::get()->getImageCaptureDialogSettings(); imageCaptureSettings->setCroppingEnabled(checked); } /** * Called when scale proportionately check box clicked. * * @parm checked * New checked status. */ void ImageCaptureDialog::copyImageToClipboardCheckBoxClicked(bool checked) { ImageCaptureSettings* imageCaptureSettings = SessionManager::get()->getImageCaptureDialogSettings(); imageCaptureSettings->setCopyToClipboardEnabled(checked); } /** * Called when scale proportionately check box clicked. * * @parm checked * New checked status. */ void ImageCaptureDialog::saveImageToFileCheckBoxClicked(bool checked) { ImageCaptureSettings* imageCaptureSettings = SessionManager::get()->getImageCaptureDialogSettings(); imageCaptureSettings->setSaveToFileEnabled(checked); } /** * @return Create and return the image destination section. */ QWidget* ImageCaptureDialog::createImageDestinationSection() { m_copyImageToClipboardCheckBox = new QCheckBox("Copy to Clipboard"); QObject::connect(m_copyImageToClipboardCheckBox, SIGNAL(clicked(bool)), this, SLOT(copyImageToClipboardCheckBoxClicked(bool))); m_saveImageToFileCheckBox = new QCheckBox("Save to File: " ); QObject::connect(m_saveImageToFileCheckBox, SIGNAL(clicked(bool)), this, SLOT(saveImageToFileCheckBoxClicked(bool))); m_imageFileNameLineEdit = new QLineEdit(); m_imageFileNameLineEdit->setText("untitled.png"); QObject::connect(m_imageFileNameLineEdit, SIGNAL(editingFinished()), this, SLOT(imageFileNameValueChanged())); QPushButton* fileNameSelectionPushButton = new QPushButton("Choose File..."); QObject::connect(fileNameSelectionPushButton, SIGNAL(clicked()), this, SLOT(selectImagePushButtonPressed())); QGroupBox* groupBox = new QGroupBox("Destination"); QGridLayout* gridLayout = new QGridLayout(groupBox); gridLayout->addWidget(m_copyImageToClipboardCheckBox, 0, 0, 1, 3); gridLayout->addWidget(m_saveImageToFileCheckBox, 1, 0); gridLayout->addWidget(m_imageFileNameLineEdit, 1, 1); gridLayout->addWidget(fileNameSelectionPushButton, 1, 2); return groupBox; } /** * Receive events from the event manager. * * @param event * Event sent by event manager. */ void ImageCaptureDialog::receiveEvent(Event* event) { AString windowSizeText = ""; if (event->getEventType() == EventTypeEnum::EVENT_BROWSER_WINDOW_GRAPHICS_HAVE_BEEN_REDRAWN) { EventBrowserWindowGraphicsRedrawn* graphicsRedrawnEvent = dynamic_cast(event); CaretAssert(graphicsRedrawnEvent); graphicsRedrawnEvent->setEventProcessed(); updateBrowserWindowWidthAndHeightLabel(); } } /** * Get the width and height of the selected window. * * @param xOut * X-Offset of window graphics * @param yOut * Y-Offset of window graphics * @param widthOut * Width of window with aspect applied. * @param heightOut * Height of window with aspect applied * @param int32_t& graphicsWidthOut * Width of graphics without aspect applied. * @param int32_t& graphicsHeightOut * Height of graphics without aspect applied. * @param aspectRatioOut * Aspect ratio of window (height / width) * @return * True if selected window is valid and both height and width * are greater than zero, else false. */ bool ImageCaptureDialog::getSelectedWindowCoordsWidthAndHeight(int32_t& xOut, int32_t& yOut, int32_t& widthOut, int32_t& heightOut, int32_t& graphicsWidthOut, int32_t& graphicsHeightOut, float& aspectRatioOut) const { xOut = 0; yOut = 0; widthOut = 0; heightOut = 0; graphicsWidthOut = 0; graphicsHeightOut = 0; const int selectedBrowserWindowIndex = m_windowSelectionSpinBox->value() - 1; BrainBrowserWindow* browserWindow = GuiManager::get()->getBrowserWindowByWindowIndex(selectedBrowserWindowIndex); if (browserWindow != NULL) { browserWindow->getGraphicsWidgetSize(xOut, yOut, widthOut, heightOut, graphicsWidthOut, graphicsHeightOut, m_windowCropToLockAspectRegionCheckBox->isChecked()); if ((widthOut > 0) && (heightOut > 0)) { aspectRatioOut = (static_cast(heightOut) / static_cast(widthOut)); return true; } } return false; } /** * Get the width and height of the selected window. * * @param widthOut * Width of window. * @param heightOut * Height of window. * @param aspectRatioOut * Aspect ratio of window (height / width) * @return * True if selected window is valid and both height and width * are greater than zero, else false. */ bool ImageCaptureDialog::getSelectedWindowWidthAndHeight(int32_t& widthOut, int32_t& heightOut, float& aspectRatioOut) const { int32_t x = 0; int32_t y = 0; int32_t graphicsWidth = 0; int32_t graphicsHeight = 0; return getSelectedWindowCoordsWidthAndHeight(x, y, widthOut, heightOut, 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; } /** * Set the selected browser window to the browser window with the * given index. * @param browserWindowIndex * Index of browser window. */ void ImageCaptureDialog::setBrowserWindowIndex(const int32_t browserWindowIndex) { m_windowSelectionSpinBox->setValue(browserWindowIndex + 1); } /** * Called when choose file pushbutton is pressed. */ void ImageCaptureDialog::selectImagePushButtonPressed() { QString defaultFileName = m_imageFileNameLineEdit->text().trimmed(); if (defaultFileName.isEmpty()) { defaultFileName = "untitled.png"; } FileInformation fileInfo(m_imageFileNameLineEdit->text().trimmed()); if (fileInfo.isRelative()) { FileInformation absFileInfo(GuiManager::get()->getBrain()->getCurrentDirectory(), m_imageFileNameLineEdit->text().trimmed()); defaultFileName = absFileInfo.getAbsoluteFilePath(); } std::vector imageFileFilters; AString defaultFileFilter; ImageFile::getImageFileFilters(imageFileFilters, defaultFileFilter); QString filters; for (std::vector::iterator filterIterator = imageFileFilters.begin(); filterIterator != imageFileFilters.end(); filterIterator++) { if (filters.isEmpty() == false) { filters += ";;"; } filters += *filterIterator; } AString name = CaretFileDialog::getSaveFileNameDialog(this, "Choose File Name", defaultFileName, //GuiManager::get()->getBrain()->getCurrentDirectory(), filters, &defaultFileFilter); if (name.isEmpty() == false) { ImageCaptureSettings* imageCaptureSettings = SessionManager::get()->getImageCaptureDialogSettings(); imageCaptureSettings->setImageFileName(name); updateDestinationSection(); } } /** * Gets called when user changes name of the image file. */ void ImageCaptureDialog::imageFileNameValueChanged() { ImageCaptureSettings* imageCaptureSettings = SessionManager::get()->getImageCaptureDialogSettings(); imageCaptureSettings->setImageFileName(m_imageFileNameLineEdit->text().trimmed()); } /** * Called when the apply button is pressed. */ void ImageCaptureDialog::applyButtonClicked() { ImageCaptureSettings* imageCaptureSettings = SessionManager::get()->getImageCaptureDialogSettings(); const int browserWindowIndex = m_windowSelectionSpinBox->value() - 1; int32_t windowX; int32_t windowY; int32_t windowWidth; int32_t windowHeight; int32_t widgetWidth; int32_t widgetHeight; float windowAspectRatio; if ( ! getSelectedWindowCoordsWidthAndHeight(windowX, windowY, windowWidth, windowHeight, widgetWidth, widgetHeight, windowAspectRatio)) { WuQMessageBox::errorOk(this, "Failed to get window size"); return; } /* * Default to width of window that may exclude empty regions * caused by locking of aspect ratio. */ int32_t imageWidth = windowWidth; int32_t imageHeight = windowHeight; if (m_imageSizeCustomRadioButton->isChecked()) { imageWidth = m_pixelWidthSpinBox->value(); imageHeight = m_pixelHeightSpinBox->value(); if ((windowWidth > 0) && (windowHeight > 0)) { const float windowWidthScaling = (static_cast(imageWidth) / static_cast(windowWidth)); const float windowHeightScaling = (static_cast(imageHeight) / static_cast(windowHeight)); ChartMatrixDisplayProperties::setManualScaleModeWindowWidthHeightScaling(windowWidthScaling, windowHeightScaling); } } EventImageCapture imageCaptureEvent(browserWindowIndex, windowX, windowY, windowWidth, windowHeight, imageWidth, imageHeight); EventManager::get()->sendEvent(imageCaptureEvent.getPointer()); bool errorFlag = false; if (imageCaptureEvent.getEventProcessCount() <= 0) { WuQMessageBox::errorOk(this, "Invalid window selected"); errorFlag = true; } else if (imageCaptureEvent.isError()) { WuQMessageBox::errorOk(this, imageCaptureEvent.getErrorMessage()); errorFlag = true; } if ( ! errorFlag) { uint8_t backgroundColor[3]; imageCaptureEvent.getBackgroundColor(backgroundColor); ImageFile imageFile; imageFile.setFromQImage(imageCaptureEvent.getImage()); const float pixelsPerCentimeter = imageCaptureSettings->getImageResolutionInCentimeters(); const float pixelsPerMeter = pixelsPerCentimeter * 100; imageFile.setDotsPerMeter(pixelsPerMeter, pixelsPerMeter); if (imageCaptureSettings->isCroppingEnabled()) { const int32_t marginSize = imageCaptureSettings->getCroppingMargin(); imageFile.cropImageRemoveBackground(marginSize, backgroundColor); } if (m_copyImageToClipboardCheckBox->isChecked()) { QApplication::clipboard()->setImage(*imageFile.getAsQImage(), QClipboard::Clipboard); } if (m_saveImageToFileCheckBox->isChecked()) { std::vector imageFileExtensions; AString defaultFileExtension; ImageFile::getImageFileExtensions(imageFileExtensions, defaultFileExtension); AString filename = m_imageFileNameLineEdit->text().trimmed(); bool validExtension = false; for (std::vector::iterator extensionIterator = imageFileExtensions.begin(); extensionIterator != imageFileExtensions.end(); extensionIterator++) { if (filename.endsWith(*extensionIterator)) { validExtension = true; } } if (validExtension == false) { if (defaultFileExtension.isEmpty() == false) { filename += ("." + defaultFileExtension); } } try { imageFile.writeFile(filename); } catch (const DataFileException& /*e*/) { QString msg("Unable to save: " + filename); WuQMessageBox::errorOk(this, msg); errorFlag = true; } } } ChartMatrixDisplayProperties::setManualScaleModeWindowWidthHeightScaling(1.0, 1.0); if (errorFlag == false) { /* * Display over "Capture" (the renamed Apply) button. */ QWidget* parent = getDialogButtonBox()->button(QDialogButtonBox::Apply); CaretAssert(parent); WuQTimedMessageDisplay::show(parent, 2.0, "Image captured"); } EventManager::get()->sendEvent(EventGraphicsUpdateOneWindow(browserWindowIndex).getPointer()); } connectome-workbench-1.2.3+git41-gc4c6c90/src/GuiQt/ImageCaptureDialog.h000066400000000000000000000122331300200146000254350ustar00rootroot00000000000000#ifndef __IMAGE_CAPTURE_DIALOG__H_ #define __IMAGE_CAPTURE_DIALOG__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 "EventListenerInterface.h" #include "WuQDialogNonModal.h" class QCheckBox; class QDoubleSpinBox; class QLabel; class QLineEdit; class QRadioButton; class QSpinBox; namespace caret { class BrainBrowserWindow; class EnumComboBoxTemplate; class ImageCaptureSettings; class WuQWidgetObjectGroup; class ImageCaptureDialog : public WuQDialogNonModal, public EventListenerInterface { Q_OBJECT public: ImageCaptureDialog(BrainBrowserWindow* parent); virtual ~ImageCaptureDialog(); void setBrowserWindowIndex(const int32_t browserWindowIndex); void updateDialog(); void receiveEvent(Event* event); protected: virtual void applyButtonClicked(); private slots: void selectImagePushButtonPressed(); void cropToTabWindowLockAspectRegionClicked(bool clicked); void updateBrowserWindowWidthAndHeightLabel(); void imageResolutionUnitsEnumComboBoxItemActivated(); void imageSizeUnitsEnumComboBoxItemActivated(); void pixelWidthValueChanged(int); void pixelHeightValueChanged(int); void imageFileNameValueChanged(); void imageWidthValueChanged(double); void imageHeightValueChanged(double); void imageResolutionValueChanged(double); void scaleProportionallyCheckBoxClicked(bool); void imageCroppingCheckBoxClicked(bool); void imageCroppingMarginValueChanged(int); void copyImageToClipboardCheckBoxClicked(bool); void saveImageToFileCheckBoxClicked(bool); void sizeRadioButtonClicked(QAbstractButton* button); private: ImageCaptureDialog(const ImageCaptureDialog&); ImageCaptureDialog& operator=(const ImageCaptureDialog&); QWidget* createImageSourceSection(); QWidget* createImageOptionsSection(); QWidget* createImageDimensionsSection(); QWidget* createImageDestinationSection(); void updateDimensionsSection(); void updateImageOptionsSection(); void updateDestinationSection(); bool getSelectedWindowCoordsWidthAndHeight(int32_t& xOut, int32_t& yOut, int32_t& widthOut, int32_t& heightOut, int32_t& graphicsWidthOut, int32_t& graphicsHeightOut, float& aspectRatioOut) const; bool getSelectedWindowWidthAndHeight(int32_t& widthOut, int32_t& heightOut, float& aspectRatioOut) const; //void updateDialogWithImageDimensionsModel(); void updateImageNumberOfBytesLabel(); QCheckBox* m_saveImageToFileCheckBox; QCheckBox* m_copyImageToClipboardCheckBox; QLineEdit* m_imageFileNameLineEdit; QRadioButton* m_imageSizeWindowRadioButton; QRadioButton* m_imageSizeCustomRadioButton; QSpinBox* m_pixelWidthSpinBox; QSpinBox* m_pixelHeightSpinBox; QDoubleSpinBox* m_imageWidthSpinBox; QDoubleSpinBox* m_imageHeightSpinBox; QDoubleSpinBox* m_imageResolutionSpinBox; QCheckBox* m_scaleProportionallyCheckBox; EnumComboBoxTemplate* m_imagePixelsPerSpatialUnitsEnumComboBox; EnumComboBoxTemplate* m_imageSpatialUnitsEnumComboBox; QLabel* m_imageNumberOfBytesLabel; QCheckBox* m_imageAutoCropCheckBox; QSpinBox* m_imageAutoCropMarginSpinBox; QSpinBox* m_windowSelectionSpinBox; QCheckBox* m_windowCropToLockAspectRegionCheckBox; QWidget* m_customDimensionsWidget; QWidget* m_imageDimensionsWidget; }; #ifdef __IMAGE_CAPTURE_DIALOG__H__DECLARE__ #endif // __IMAGE_CAPTURE_DIALOG__H__DECLARE__ } // namespace #endif //__IMAGE_CAPTURE_DIALOG__H_ connectome-workbench-1.2.3+git41-gc4c6c90/src/GuiQt/ImageFileConvertToVolumeFileDialog.cxx000066400000000000000000000337571300200146000311360ustar00rootroot00000000000000 /*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 __IMAGE_FILE_CONVERT_TO_VOLUME_FILE_DIALOG_DECLARE__ #include "ImageFileConvertToVolumeFileDialog.h" #undef __IMAGE_FILE_CONVERT_TO_VOLUME_FILE_DIALOG_DECLARE__ #include #include #include #include #include #include "Brain.h" #include "BrowserTabContent.h" #include "CaretAssert.h" #include "ControlPointFile.h" #include "ControlPoint3D.h" #include "DisplayPropertiesImages.h" #include "EventBrowserTabGet.h" #include "EventDataFileAdd.h" #include "EventGraphicsUpdateAllWindows.h" #include "EventManager.h" #include "EventUserInterfaceUpdate.h" #include "FileInformation.h" #include "GuiManager.h" #include "ImageFile.h" #include "Matrix4x4.h" #include "VolumeFile.h" #include "VolumeSliceViewPlaneEnum.h" #include "WuQMessageBox.h" #include "WuQtUtilities.h" using namespace caret; /** * \class caret::ImageFileConvertToVolumeFileDialog * \brief Dialog for converting an image file to a volume file. * \ingroup GuiQt */ /** * Constructor. */ ImageFileConvertToVolumeFileDialog::ImageFileConvertToVolumeFileDialog(QWidget* parent, const int32_t tabIndex, ImageFile* imageFile) : WuQDialogModal("Convert Image File to Volume File", parent), m_tabIndex(tabIndex), m_imageFile(imageFile) { CaretAssert(m_imageFile); QWidget* volumeWidget = createVolumeSelectionWidget(); QWidget* controlPointWidget = createControlPointWidget(); QWidget* widget = new QWidget(); QVBoxLayout* layout = new QVBoxLayout(widget); layout->addWidget(volumeWidget); layout->addWidget(controlPointWidget); setCentralWidget(widget, WuQDialog::SCROLL_AREA_NEVER); } /** * Destructor. */ ImageFileConvertToVolumeFileDialog::~ImageFileConvertToVolumeFileDialog() { } /** * @return New instance of the volume selection widget. */ QWidget* ImageFileConvertToVolumeFileDialog::createVolumeSelectionWidget() { FileInformation fileInfo(m_imageFile->getFileName()); const AString volumeFileName = FileInformation::assembleFileComponents(fileInfo.getAbsolutePath(), fileInfo.getFileNameNoExtension(), DataFileTypeEnum::toFileExtension(DataFileTypeEnum::VOLUME)); QLabel* volumeFileNameLabel = new QLabel("Volume File Name:"); m_volumeFileNameLineEdit = new QLineEdit; m_volumeFileNameLineEdit->setText(volumeFileName); std::vector slicePlanes; slicePlanes.push_back(VolumeSliceViewPlaneEnum::ALL); slicePlanes.push_back(VolumeSliceViewPlaneEnum::AXIAL); slicePlanes.push_back(VolumeSliceViewPlaneEnum::CORONAL); slicePlanes.push_back(VolumeSliceViewPlaneEnum::PARASAGITTAL); QLabel* colorConversionLabel = new QLabel("Color Conversion:"); m_colorConversionComboBox = new QComboBox(); m_colorConversionComboBox->addItem("Grayscale"); m_colorConversionComboBox->setItemData(0, ImageFile::CONVERT_TO_VOLUME_COLOR_GRAYSCALE); m_colorConversionComboBox->addItem("RGB"); m_colorConversionComboBox->setItemData(1, ImageFile::CONVERT_TO_VOLUME_COLOR_RGB); QGroupBox* groupBox = new QGroupBox("Volume Selection"); QGridLayout* layout = new QGridLayout(groupBox); layout->setColumnStretch(0, 0); layout->setColumnStretch(1, 100); layout->setColumnMinimumWidth(1, 250); int row = layout->rowCount(); layout->addWidget(volumeFileNameLabel, row, 0); layout->addWidget(m_volumeFileNameLineEdit, row, 1); row++; layout->addWidget(colorConversionLabel, row, 0); layout->addWidget(m_colorConversionComboBox, row, 1); row++; return groupBox; } /** * @return New instance of the control point widget. */ QWidget* ImageFileConvertToVolumeFileDialog::createControlPointWidget() { const ControlPointFile* controlPointFile = m_imageFile->getControlPointFile(); const int32_t numberOfControlPoints = controlPointFile->getNumberOfControlPoints(); int32_t columnCounter = 0; const int32_t COLUMN_PIXEL_I = columnCounter++; const int32_t COLUMN_PIXEL_J = columnCounter++; const int32_t COLUMN_SEPARATOR_1 = columnCounter++; const int32_t COLUMN_VOLUME_X = columnCounter++; const int32_t COLUMN_VOLUME_Y = columnCounter++; const int32_t COLUMN_VOLUME_Z = columnCounter++; const int32_t COLUMN_SEPARATOR_2 = columnCounter++; const int32_t COLUMN_TRANSFORMED_X = columnCounter++; const int32_t COLUMN_TRANSFORMED_Y = columnCounter++; const int32_t COLUMN_TRANSFORMED_Z = columnCounter++; const int32_t COLUMN_SEPARATOR_3 = columnCounter++; const int32_t COLUMN_ERROR_X = columnCounter++; const int32_t COLUMN_ERROR_Y = columnCounter++; const int32_t COLUMN_ERROR_Z = columnCounter++; const int32_t COLUMN_ERROR_TOTAL = columnCounter++; const int32_t FLOAT_PRECISION = 1; QGroupBox* widget = new QGroupBox("Control Points"); QGridLayout* gridLayout = new QGridLayout(widget); int32_t row = gridLayout->rowCount(); gridLayout->addWidget(new QLabel("Image"), row, COLUMN_PIXEL_I, 1, 2, Qt::AlignHCenter); gridLayout->addWidget(new QLabel("Volume"), row, COLUMN_VOLUME_X, 1, 3, Qt::AlignHCenter); gridLayout->addWidget(new QLabel("Transformed"), row, COLUMN_TRANSFORMED_X, 1, 3, Qt::AlignHCenter); gridLayout->addWidget(new QLabel("Error"), row, COLUMN_ERROR_X, 1, 4, Qt::AlignHCenter); row++; gridLayout->addWidget(new QLabel("I"), row, COLUMN_PIXEL_I, Qt::AlignHCenter); gridLayout->addWidget(new QLabel("J"), row, COLUMN_PIXEL_J, Qt::AlignHCenter); gridLayout->addWidget(new QLabel("X"), row, COLUMN_VOLUME_X, Qt::AlignHCenter); gridLayout->addWidget(new QLabel("Y"), row, COLUMN_VOLUME_Y, Qt::AlignHCenter); gridLayout->addWidget(new QLabel("Z"), row, COLUMN_VOLUME_Z, Qt::AlignHCenter); gridLayout->addWidget(new QLabel("X"), row, COLUMN_TRANSFORMED_X, Qt::AlignHCenter); gridLayout->addWidget(new QLabel("Y"), row, COLUMN_TRANSFORMED_Y, Qt::AlignHCenter); gridLayout->addWidget(new QLabel("Z"), row, COLUMN_TRANSFORMED_Z, Qt::AlignHCenter); gridLayout->addWidget(new QLabel("X"), row, COLUMN_ERROR_X, Qt::AlignHCenter); gridLayout->addWidget(new QLabel("Y"), row, COLUMN_ERROR_Y, Qt::AlignHCenter); gridLayout->addWidget(new QLabel("Z"), row, COLUMN_ERROR_Z, Qt::AlignHCenter); gridLayout->addWidget(new QLabel("Total"), row, COLUMN_ERROR_TOTAL, Qt::AlignHCenter); row++; for (int32_t icp = 0; icp < numberOfControlPoints; icp++) { const ControlPoint3D* cp = controlPointFile->getControlPointAtIndex(icp); float sourceXYZ[3]; cp->getSourceXYZ(sourceXYZ); gridLayout->addWidget(new QLabel(QString::number(sourceXYZ[0], 'f', 0)), row, COLUMN_PIXEL_I, Qt::AlignRight); gridLayout->addWidget(new QLabel(QString::number(sourceXYZ[1], 'f', 0)), row, COLUMN_PIXEL_J, Qt::AlignRight); float targetXYZ[3]; cp->getTargetXYZ(targetXYZ); gridLayout->addWidget(new QLabel(QString::number(targetXYZ[0], 'f', FLOAT_PRECISION)), row, COLUMN_VOLUME_X, Qt::AlignRight); gridLayout->addWidget(new QLabel(QString::number(targetXYZ[1], 'f', FLOAT_PRECISION)), row, COLUMN_VOLUME_Y, Qt::AlignRight); gridLayout->addWidget(new QLabel(QString::number(targetXYZ[2], 'f', FLOAT_PRECISION)), row, COLUMN_VOLUME_Z, Qt::AlignRight); float transformedXYZ[3]; cp->getTransformedXYZ(transformedXYZ); gridLayout->addWidget(new QLabel(QString::number(transformedXYZ[0], 'f', FLOAT_PRECISION)), row, COLUMN_TRANSFORMED_X, Qt::AlignRight); gridLayout->addWidget(new QLabel(QString::number(transformedXYZ[1], 'f', FLOAT_PRECISION)), row, COLUMN_TRANSFORMED_Y, Qt::AlignRight); gridLayout->addWidget(new QLabel(QString::number(transformedXYZ[2], 'f', FLOAT_PRECISION)), row, COLUMN_TRANSFORMED_Z, Qt::AlignRight); float errorXYZTotal[4]; cp->getErrorMeasurements(errorXYZTotal); gridLayout->addWidget(new QLabel(QString::number(errorXYZTotal[0], 'f', FLOAT_PRECISION)), row, COLUMN_ERROR_X, Qt::AlignRight); gridLayout->addWidget(new QLabel(QString::number(errorXYZTotal[1], 'f', FLOAT_PRECISION)), row, COLUMN_ERROR_Y, Qt::AlignRight); gridLayout->addWidget(new QLabel(QString::number(errorXYZTotal[2], 'f', FLOAT_PRECISION)), row, COLUMN_ERROR_Z, Qt::AlignRight); gridLayout->addWidget(new QLabel(QString::number(errorXYZTotal[3], 'f', FLOAT_PRECISION)), row, COLUMN_ERROR_TOTAL, Qt::AlignRight); row++; } gridLayout->addWidget(WuQtUtilities::createVerticalLineWidget(), 0, COLUMN_SEPARATOR_1, row, 1); gridLayout->addWidget(WuQtUtilities::createVerticalLineWidget(), 0, COLUMN_SEPARATOR_2, row, 1); gridLayout->addWidget(WuQtUtilities::createVerticalLineWidget(), 0, COLUMN_SEPARATOR_3, row, 1); return widget; } /** * Gets called when the OK button is clicked. */ void ImageFileConvertToVolumeFileDialog::okButtonClicked() { Brain* brain = GuiManager::get()->getBrain(); EventBrowserTabGet tabEvent(m_tabIndex); EventManager::get()->sendEvent(tabEvent.getPointer()); BrowserTabContent* tabContent = tabEvent.getBrowserTab(); ModelWholeBrain* wholeBrainModel = NULL; ModelVolume* volumeModel = NULL; if (tabContent != NULL) { volumeModel = tabContent->getDisplayedVolumeModel(); wholeBrainModel = tabContent->getDisplayedWholeBrainModel(); } if ((volumeModel == NULL) && (wholeBrainModel == NULL)) { WuQMessageBox::errorOk(this, "Conversion must be performed in ALL or VOLUME view."); return; } float translation[3]; tabContent->getTranslation(translation); Matrix4x4 rotMatrix = tabContent->getRotationMatrix(); const float scaling = tabContent->getScaling(); Matrix4x4 sformMatrix; sformMatrix.translate(translation[0], translation[1], translation[2]); sformMatrix.postmultiply(rotMatrix); sformMatrix.scale(scaling, scaling, scaling); CaretAssert(m_imageFile); std::vector controlPoints; const ControlPointFile* controlPointFile = m_imageFile->getControlPointFile(); const int32_t numberOfControlPoints = controlPointFile->getNumberOfControlPoints(); for (int32_t icp = 0; icp < numberOfControlPoints; icp++) { controlPoints.push_back(*controlPointFile->getControlPointAtIndex(icp)); } const int colorConversionIndex = m_colorConversionComboBox->itemData(m_colorConversionComboBox->currentIndex()).toInt(); 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) { WuQMessageBox::errorOk(this, errorMessage); return; } volumeFile->setFileName(m_volumeFileNameLineEdit->text().trimmed()); EventManager::get()->sendEvent(EventDataFileAdd(volumeFile).getPointer()); EventManager::get()->sendEvent(EventUserInterfaceUpdate().getPointer()); EventManager::get()->sendEvent(EventGraphicsUpdateAllWindows().getPointer()); WuQDialogModal::okButtonClicked(); } connectome-workbench-1.2.3+git41-gc4c6c90/src/GuiQt/ImageFileConvertToVolumeFileDialog.h000066400000000000000000000045361300200146000305540ustar00rootroot00000000000000#ifndef __IMAGE_FILE_CONVERT_TO_VOLUME_FILE_DIALOG_H__ #define __IMAGE_FILE_CONVERT_TO_VOLUME_FILE_DIALOG_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 "CaretPointer.h" #include "WuQDialogModal.h" class QComboBox; class QLineEdit; class QTableWidget; namespace caret { class ImageFile; class ImageFileConvertToVolumeFileDialog : public WuQDialogModal { Q_OBJECT public: ImageFileConvertToVolumeFileDialog(QWidget* parent, const int32_t tabIndex, ImageFile* imageFile); virtual ~ImageFileConvertToVolumeFileDialog(); // ADD_NEW_METHODS_HERE protected: virtual void okButtonClicked(); private: ImageFileConvertToVolumeFileDialog(const ImageFileConvertToVolumeFileDialog&); ImageFileConvertToVolumeFileDialog& operator=(const ImageFileConvertToVolumeFileDialog&); QWidget* createVolumeSelectionWidget(); QWidget* createControlPointWidget(); // ADD_NEW_MEMBERS_HERE const int32_t m_tabIndex; ImageFile* m_imageFile; QLineEdit* m_volumeFileNameLineEdit; QComboBox* m_colorConversionComboBox; QTableWidget* m_tableWidget; }; #ifdef __IMAGE_FILE_CONVERT_TO_VOLUME_FILE_DIALOG_DECLARE__ // #endif // __IMAGE_FILE_CONVERT_TO_VOLUME_FILE_DIALOG_DECLARE__ } // namespace #endif //__IMAGE_FILE_CONVERT_TO_VOLUME_FILE_DIALOG_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/GuiQt/ImageSelectionViewController.cxx000066400000000000000000000463501300200146000301200ustar00rootroot00000000000000 /*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 __IMAGE_SELECTION_VIEW_CONTROLLER_DECLARE__ #include "ImageSelectionViewController.h" #undef __IMAGE_SELECTION_VIEW_CONTROLLER_DECLARE__ #include "Brain.h" #include "BrowserTabContent.h" #include "CaretAssert.h" #include "DisplayGroupEnumComboBox.h" #include "DisplayPropertiesImages.h" #include "EnumComboBoxTemplate.h" #include "EventGraphicsUpdateAllWindows.h" #include "EventManager.h" #include "EventUserInterfaceUpdate.h" #include "GuiManager.h" #include "ImageFile.h" #include "SceneClass.h" #include "SceneClassAssistant.h" #include "WuQSpinBoxGroup.h" #include "WuQtUtilities.h" #include "WuQTabWidget.h" using namespace caret; static const int COLUMN_RADIO_BUTTON = 0; /** * \class caret::ImageSelectionViewController * \brief View controller for image selection * \ingroup GuiQt */ /** * Constructor. */ ImageSelectionViewController::ImageSelectionViewController(const int32_t browserWindowIndex, QWidget* parent) : QWidget(parent), m_browserWindowIndex(browserWindowIndex) { setWindowTitle("Images"); m_sceneAssistant = new SceneClassAssistant(); QLabel* groupLabel = new QLabel("Group"); m_imagesDisplayGroupComboBox = new DisplayGroupEnumComboBox(this); QObject::connect(m_imagesDisplayGroupComboBox, SIGNAL(displayGroupSelected(const DisplayGroupEnum::Enum)), this, SLOT(imageDisplayGroupSelected(const DisplayGroupEnum::Enum))); QHBoxLayout* groupLayout = new QHBoxLayout(); groupLayout->addWidget(groupLabel); groupLayout->addWidget(m_imagesDisplayGroupComboBox->getWidget()); groupLayout->addStretch(); m_imageDisplayCheckBox = new QCheckBox("Display Image"); QObject::connect(m_imageDisplayCheckBox, SIGNAL(clicked(bool)), this, SLOT(processAttributesChanges())); m_controlPointsDisplayCheckBox = new QCheckBox("Display Control Points"); QObject::connect(m_controlPointsDisplayCheckBox, SIGNAL(clicked(bool)), this, SLOT(processAttributesChanges())); QWidget* attributesWidget = this->createAttributesWidget(); QWidget* selectionWidget = this->createSelectionWidget(); m_tabWidget = new WuQTabWidget(WuQTabWidget::TAB_ALIGN_LEFT, this); m_tabWidget->addTab(attributesWidget, "Attributes"); m_tabWidget->addTab(selectionWidget, "Selection"); m_tabWidget->setCurrentWidget(attributesWidget); QVBoxLayout* layout = new QVBoxLayout(this); // WuQtUtilities::setLayoutSpacingAndMargins(layout, 2, 2); layout->addLayout(groupLayout, 0); layout->addWidget(m_imageDisplayCheckBox, 0); layout->addWidget(m_controlPointsDisplayCheckBox, 0); layout->addSpacing(10); layout->addWidget(m_tabWidget->getWidget(), 100); layout->addStretch(); EventManager::get()->addEventListener(this, EventTypeEnum::EVENT_USER_INTERFACE_UPDATE); s_allImageSelectionViewControllers.insert(this); } /** * Destructor. */ ImageSelectionViewController::~ImageSelectionViewController() { EventManager::get()->removeAllEventsFromListener(this); s_allImageSelectionViewControllers.erase(this); delete m_sceneAssistant; } /** * Receive an event. * * @param event * An event for which this instance is listening. */ void ImageSelectionViewController::receiveEvent(Event* event) { bool doUpdate = false; if (event->getEventType() == EventTypeEnum::EVENT_USER_INTERFACE_UPDATE) { EventUserInterfaceUpdate* uiEvent = dynamic_cast(event); CaretAssert(uiEvent); if (uiEvent->isUpdateForWindow(m_browserWindowIndex)) { if (uiEvent->isToolBoxUpdate()) { doUpdate = true; uiEvent->setEventProcessed(); } } } if (doUpdate) { updateImageViewController(); } } /** * Create the image selection widget. */ QWidget* ImageSelectionViewController::createSelectionWidget() { m_imageRadioButtonGroup = new QButtonGroup(this); QObject::connect(m_imageRadioButtonGroup, SIGNAL(buttonClicked(int)), this, SLOT(imageRadioButtonClicked(int))); QWidget* imageRadioButtonWidget = new QWidget(); imageRadioButtonWidget->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); m_imageRadioButtonLayout = new QGridLayout(imageRadioButtonWidget); m_imageRadioButtonLayout->setColumnStretch(COLUMN_RADIO_BUTTON, 100); const int imageLayoutRow = m_imageRadioButtonLayout->rowCount(); m_imageRadioButtonLayout->addWidget(new QLabel("Image"), imageLayoutRow, COLUMN_RADIO_BUTTON, Qt::AlignHCenter); QScrollArea* imageRadioButtonScrollArea = new QScrollArea(); imageRadioButtonScrollArea->setWidget(imageRadioButtonWidget); imageRadioButtonScrollArea->setHorizontalScrollBarPolicy(Qt::ScrollBarAsNeeded); imageRadioButtonScrollArea->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOn); imageRadioButtonScrollArea->setWidgetResizable(true); return imageRadioButtonScrollArea; } /** * @return The attributes widget. */ QWidget* ImageSelectionViewController::createAttributesWidget() { QLabel* depthLabel = new QLabel("Depth"); m_depthComboBox = new EnumComboBoxTemplate(this); m_depthComboBox->setup(); m_depthComboBox->getWidget()->setToolTip("Set the depth position of the image3"); QObject::connect(m_depthComboBox, SIGNAL(itemActivated()), this, SLOT(processAttributesChanges())); const float threshMin = -1.0; const float threshMax = 100000.0; QLabel* thresholdMinimumLabel = new QLabel("Minimum Threshold"); m_thresholdMinimumSpinBox = WuQFactory::newDoubleSpinBox(); m_thresholdMinimumSpinBox->setFixedWidth(80); m_thresholdMinimumSpinBox->setRange(threshMin, threshMax); m_thresholdMinimumSpinBox->setSingleStep(1.0); m_thresholdMinimumSpinBox->setDecimals(1); m_thresholdMinimumSpinBox->setToolTip("Do not display image pixels containing a color component less than this value"); QObject::connect(m_thresholdMinimumSpinBox, SIGNAL(valueChanged(double)), this, SLOT(processAttributesChanges())); QLabel* thresholdMaximumLabel = new QLabel("Maximum Threshold"); m_thresholdMaximumSpinBox = WuQFactory::newDoubleSpinBox(); m_thresholdMaximumSpinBox->setFixedWidth(80); m_thresholdMaximumSpinBox->setRange(threshMin, threshMax); m_thresholdMaximumSpinBox->setSingleStep(1.0); m_thresholdMaximumSpinBox->setDecimals(1); m_thresholdMaximumSpinBox->setToolTip("Do not display image pixels containing a color component greater than this value"); QObject::connect(m_thresholdMaximumSpinBox, SIGNAL(valueChanged(double)), this, SLOT(processAttributesChanges())); const float minOpacity = 0.0; const float maxOpacity = 1.0; QLabel* opacityLabel = new QLabel("Opacity"); m_opacitySpinBox = WuQFactory::newDoubleSpinBox(); m_opacitySpinBox->setFixedWidth(80); m_opacitySpinBox->setRange(minOpacity, maxOpacity); m_opacitySpinBox->setSingleStep(0.1); m_opacitySpinBox->setDecimals(1); m_opacitySpinBox->setToolTip("Opacity for image"); QObject::connect(m_opacitySpinBox, SIGNAL(valueChanged(double)), this, SLOT(processAttributesChanges())); QWidget* gridWidget = new QWidget(); QGridLayout* gridLayout = new QGridLayout(gridWidget); WuQtUtilities::setLayoutSpacingAndMargins(gridLayout, 8, 2); int row = gridLayout->rowCount(); gridLayout->addWidget(depthLabel, row, 0); gridLayout->addWidget(m_depthComboBox->getWidget(), row, 1); row++; gridLayout->addWidget(thresholdMinimumLabel, row, 0); gridLayout->addWidget(m_thresholdMinimumSpinBox, row, 1); row++; gridLayout->addWidget(thresholdMaximumLabel, row, 0); gridLayout->addWidget(m_thresholdMaximumSpinBox, row, 1); row++; gridLayout->addWidget(opacityLabel, row, 0); gridLayout->addWidget(m_opacitySpinBox, row, 1); row++; // gridLayout->addWidget(pointSizeLabel, row, 0); // gridLayout->addWidget(m_pointSizeSpinBox, row, 1); // row++; // gridLayout->addWidget(m_enableUnstretchedLinesCheckBox, row, 0); // gridLayout->addWidget(m_unstretchedLinesLengthSpinBox, row, 1); // row++; // gridLayout->addWidget(aboveSurfaceLabel, row, 0); // gridLayout->addWidget(m_aboveSurfaceOffsetSpinBox, row, 1); // // gridWidget->setSizePolicy(QSizePolicy::Fixed, // QSizePolicy::Fixed); QWidget* widget = new QWidget(); QVBoxLayout* layout = new QVBoxLayout(widget); layout->addWidget(gridWidget); layout->addStretch(); return widget; } /** * Called when a selection changes. */ void ImageSelectionViewController::processAttributesChanges() { DisplayPropertiesImages* dpi = GuiManager::get()->getBrain()->getDisplayPropertiesImages(); BrowserTabContent* browserTabContent = GuiManager::get()->getBrowserTabContentForBrowserWindow(m_browserWindowIndex, true); if (browserTabContent == NULL) { return; } const int32_t browserTabIndex = browserTabContent->getTabNumber(); const DisplayGroupEnum::Enum displayGroup = dpi->getDisplayGroupForTab(browserTabIndex); dpi->setDisplayed(displayGroup, browserTabIndex, m_imageDisplayCheckBox->isChecked()); dpi->setControlPointsDisplayed(displayGroup, browserTabIndex, m_controlPointsDisplayCheckBox->isChecked()); dpi->setImagePosition(displayGroup, browserTabIndex, m_depthComboBox->getSelectedItem()); dpi->setThresholdMinimum(displayGroup, browserTabIndex, m_thresholdMinimumSpinBox->value()); dpi->setThresholdMaximum(displayGroup, browserTabIndex, m_thresholdMaximumSpinBox->value()); dpi->setOpacity(displayGroup, browserTabIndex, m_opacitySpinBox->value()); EventManager::get()->sendEvent(EventGraphicsUpdateAllWindows().getPointer()); updateOtherImageViewControllers(); } /** * Called when the display group is changed. * * @param displayGroup * Display group selected. */ void ImageSelectionViewController::imageDisplayGroupSelected(const DisplayGroupEnum::Enum displayGroup) { /* * Update selected display group in model. */ BrowserTabContent* browserTabContent = GuiManager::get()->getBrowserTabContentForBrowserWindow(m_browserWindowIndex, false); if (browserTabContent == NULL) { return; } const int32_t browserTabIndex = browserTabContent->getTabNumber(); Brain* brain = GuiManager::get()->getBrain(); DisplayPropertiesImages* dsb = brain->getDisplayPropertiesImages(); dsb->setDisplayGroupForTab(browserTabIndex, displayGroup); /* * Since display group has changed, need to update controls */ updateImageViewController(); EventManager::get()->sendEvent(EventGraphicsUpdateAllWindows().getPointer()); } /** * Called when an image radio button is clicked. * * @param buttonID * ID of button that was clicked. */ void ImageSelectionViewController::imageRadioButtonClicked(int buttonID) { BrowserTabContent* browserTabContent = GuiManager::get()->getBrowserTabContentForBrowserWindow(m_browserWindowIndex, true); if (browserTabContent == NULL) { return; } const int32_t browserTabIndex = browserTabContent->getTabNumber(); Brain* brain = GuiManager::get()->getBrain(); DisplayPropertiesImages* dpi = brain->getDisplayPropertiesImages(); const DisplayGroupEnum::Enum displayGroup = dpi->getDisplayGroupForTab(browserTabIndex); std::vector allImageFiles = brain->getAllImagesFiles(); CaretAssertVectorIndex(allImageFiles, buttonID); dpi->setSelectedImageFile(displayGroup, browserTabIndex, allImageFiles[buttonID]); updateOtherImageViewControllers(); EventManager::get()->sendEvent(EventGraphicsUpdateAllWindows().getPointer()); } /** * Update the image selection widget. */ void ImageSelectionViewController::updateImageViewController() { BrowserTabContent* browserTabContent = GuiManager::get()->getBrowserTabContentForBrowserWindow(m_browserWindowIndex, true); if (browserTabContent == NULL) { return; } const int32_t browserTabIndex = browserTabContent->getTabNumber(); Brain* brain = GuiManager::get()->getBrain(); DisplayPropertiesImages* dpi = brain->getDisplayPropertiesImages(); const DisplayGroupEnum::Enum displayGroup = dpi->getDisplayGroupForTab(browserTabIndex); m_imagesDisplayGroupComboBox->setSelectedDisplayGroup(dpi->getDisplayGroupForTab(browserTabIndex)); std::vector allImageFiles = brain->getAllImagesFiles(); m_imageDisplayCheckBox->setChecked(dpi->isDisplayed(displayGroup, browserTabIndex)); m_controlPointsDisplayCheckBox->setChecked(dpi->isControlPointsDisplayed(displayGroup, browserTabIndex)); const int32_t numImageFiles = static_cast(allImageFiles.size()); int32_t numRadioButtons = static_cast(m_imageRadioButtons.size()); if (numImageFiles > numRadioButtons) { const int32_t numToAdd = numImageFiles - numRadioButtons; for (int32_t i = 0; i < numToAdd; i++) { const int buttonID = static_cast(m_imageRadioButtons.size()); QRadioButton* rb = new QRadioButton(""); m_imageRadioButtons.push_back(rb); const int row = m_imageRadioButtonLayout->rowCount(); m_imageRadioButtonLayout->addWidget(rb, row, COLUMN_RADIO_BUTTON); m_imageRadioButtonGroup->addButton(rb, buttonID); } numRadioButtons = static_cast(m_imageRadioButtons.size()); } const ImageFile* selectedImageFile = dpi->getSelectedImageFile(displayGroup, browserTabIndex); for (int32_t i = 0; i < numRadioButtons; i++) { CaretAssertVectorIndex(m_imageRadioButtons, i); QRadioButton* rb = m_imageRadioButtons[i]; if (i < numImageFiles) { rb->setText(allImageFiles[i]->getFileNameNoPath()); if (allImageFiles[i] == selectedImageFile) { rb->setChecked(true); } rb->setVisible(true); } else { rb->setVisible(false); } } m_depthComboBox->setSelectedItem(dpi->getImagePosition(displayGroup, browserTabIndex)); m_thresholdMinimumSpinBox->blockSignals(true); m_thresholdMinimumSpinBox->setValue(dpi->getThresholdMinimum(displayGroup, browserTabIndex)); m_thresholdMinimumSpinBox->blockSignals(false); m_thresholdMaximumSpinBox->blockSignals(true); m_thresholdMaximumSpinBox->setValue(dpi->getThresholdMaximum(displayGroup, browserTabIndex)); m_thresholdMaximumSpinBox->blockSignals(false); m_opacitySpinBox->blockSignals(true); m_opacitySpinBox->setValue(dpi->getOpacity(displayGroup, browserTabIndex)); m_opacitySpinBox->blockSignals(false); } /** * Update other selection toolbox since they should all be the same. */ void ImageSelectionViewController::updateOtherImageViewControllers() { for (std::set::iterator iter = s_allImageSelectionViewControllers.begin(); iter != s_allImageSelectionViewControllers.end(); iter++) { ImageSelectionViewController* ivc = *iter; if (ivc != this) { ivc->updateImageViewController(); } } } /** * 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* ImageSelectionViewController::saveToScene(const SceneAttributes* sceneAttributes, const AString& instanceName) { SceneClass* sceneClass = new SceneClass(instanceName, "ImageSelectionViewController", 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 ImageSelectionViewController::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); } connectome-workbench-1.2.3+git41-gc4c6c90/src/GuiQt/ImageSelectionViewController.h000066400000000000000000000104401300200146000275340ustar00rootroot00000000000000#ifndef __IMAGE_SELECTION_VIEW_CONTROLLER_H__ #define __IMAGE_SELECTION_VIEW_CONTROLLER_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 "DisplayGroupEnum.h" #include "EventListenerInterface.h" #include "SceneableInterface.h" class QButtonGroup; class QCheckBox; class QDoubleSpinBox; class QGridLayout; class QRadioButton; class QSignalMapper; namespace caret { class DisplayGroupEnumComboBox; class EnumComboBoxTemplate; class SceneClassAssistant; class WuQTabWidget; class ImageSelectionViewController : public QWidget, public EventListenerInterface, public SceneableInterface { Q_OBJECT public: ImageSelectionViewController(const int32_t browserWindowIndex, QWidget* parent = 0); virtual ~ImageSelectionViewController(); // ADD_NEW_METHODS_HERE virtual void receiveEvent(Event* event); virtual SceneClass* saveToScene(const SceneAttributes* sceneAttributes, const AString& instanceName); virtual void restoreFromScene(const SceneAttributes* sceneAttributes, const SceneClass* sceneClass); private slots: void processAttributesChanges(); void imageDisplayGroupSelected(const DisplayGroupEnum::Enum); void imageRadioButtonClicked(int); // 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 implemetation by sub-classes. // protected: // virtual void saveSubClassDataToScene(const SceneAttributes* sceneAttributes, // SceneClass* sceneClass) = 0; // // virtual void restoreSubClassDataFromScene(const SceneAttributes* sceneAttributes, // const SceneClass* sceneClass) = 0; private: ImageSelectionViewController(const ImageSelectionViewController&); ImageSelectionViewController& operator=(const ImageSelectionViewController&); void updateOtherImageViewControllers(); void updateImageViewController(); QWidget* createSelectionWidget(); QWidget* createAttributesWidget(); const int32_t m_browserWindowIndex; WuQTabWidget* m_tabWidget; SceneClassAssistant* m_sceneAssistant; DisplayGroupEnumComboBox* m_imagesDisplayGroupComboBox; QCheckBox* m_imageDisplayCheckBox; QCheckBox* m_controlPointsDisplayCheckBox; std::vector m_imageRadioButtons; QButtonGroup* m_imageRadioButtonGroup; QGridLayout* m_imageRadioButtonLayout; EnumComboBoxTemplate* m_depthComboBox; QDoubleSpinBox* m_thresholdMinimumSpinBox; QDoubleSpinBox* m_thresholdMaximumSpinBox; QDoubleSpinBox* m_opacitySpinBox; static std::set s_allImageSelectionViewControllers; // ADD_NEW_MEMBERS_HERE }; #ifdef __IMAGE_SELECTION_VIEW_CONTROLLER_DECLARE__ std::set ImageSelectionViewController::s_allImageSelectionViewControllers; #endif // __IMAGE_SELECTION_VIEW_CONTROLLER_DECLARE__ } // namespace #endif //__IMAGE_SELECTION_VIEW_CONTROLLER_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/GuiQt/InformationDisplayDialog.cxx000066400000000000000000000254161300200146000272640ustar00rootroot00000000000000 /*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 #define __INFORMATION_DISPLAY_DIALOG_DECLARE__ #include "InformationDisplayDialog.h" #undef __INFORMATION_DISPLAY_DIALOG_DECLARE__ #include "Brain.h" #include "BrainBrowserWindow.h" #include "EventGraphicsUpdateAllWindows.h" #include "EventUserInterfaceUpdate.h" #include "EventUpdateInformationWindows.h" #include "EventManager.h" #include "GuiManager.h" #include "HyperLinkTextBrowser.h" #include "IdentificationManager.h" #include "InformationDisplayPropertiesDialog.h" #include "SceneClass.h" #include "SceneWindowGeometry.h" #include "SelectionItemSurfaceNode.h" #include "SelectionManager.h" #include "StructureEnumComboBox.h" #include "Surface.h" #include "WuQtUtilities.h" using namespace caret; /** * \class caret::InformationDisplayDialog * \brief Dialog for display of information. * \ingroup GuiQt * */ /** * Constructor. */ InformationDisplayDialog::InformationDisplayDialog(BrainBrowserWindow* parent) : WuQDialogNonModal("Information", parent) { this->setDeleteWhenClosed(false); /* * No apply button */ this->setApplyButtonText(""); m_propertiesDialog = NULL; m_informationTextBrowser = new HyperLinkTextBrowser(); m_informationTextBrowser->setLineWrapMode(QTextEdit::NoWrap); m_informationTextBrowser->setSizePolicy(QSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Minimum)); QAction* clearAction = WuQtUtilities::createAction("Clear", "Clear contents of information display", this, this, SLOT(clearInformationText())); m_contralateralIdentificationAction = WuQtUtilities::createAction("Contra ID", "Enable contralateral identification", this, this, SLOT(contralateralIdentificationToggled(bool))); m_contralateralIdentificationAction->setCheckable(true); QAction* copyAction = WuQtUtilities::createAction("Copy", "Copy selection from information display", this, m_informationTextBrowser, SLOT(copy())); QAction* removeIdSymbolAction = WuQtUtilities::createAction("RID", "Remove All ID symbols", this, this, SLOT(removeIdSymbols())); QAction* settingsAction = WuQtUtilities::createAction("Properties", "Displays dialog for changing ID symbol colors and size", this, this, SLOT(showPropertiesDialog())); QObject::connect(m_informationTextBrowser, SIGNAL(copyAvailable(bool)), copyAction, SLOT(setEnabled(bool))); copyAction->setEnabled(false); QToolBar* idToolBarLeft = new QToolBar(); idToolBarLeft->setOrientation(Qt::Vertical); idToolBarLeft->setFloatable(false); idToolBarLeft->setMovable(false); idToolBarLeft->addAction(clearAction); idToolBarLeft->addSeparator(); idToolBarLeft->addAction(copyAction); idToolBarLeft->addSeparator(); QToolBar* idToolBarRight = new QToolBar(); idToolBarRight->setOrientation(Qt::Vertical); idToolBarRight->setFloatable(false); idToolBarRight->setMovable(false); idToolBarRight->addAction(removeIdSymbolAction); idToolBarRight->addSeparator(); idToolBarRight->addAction(m_contralateralIdentificationAction); idToolBarRight->addSeparator(); idToolBarRight->addAction(settingsAction); idToolBarRight->addSeparator(); QWidget* widget = new QWidget(); QHBoxLayout* layout = new QHBoxLayout(widget); WuQtUtilities::setLayoutSpacingAndMargins(layout, 0, 0); layout->addWidget(idToolBarLeft); layout->addWidget(m_informationTextBrowser); layout->addWidget(idToolBarRight); layout->setStretchFactor(idToolBarLeft, 0); layout->setStretchFactor(m_informationTextBrowser, 100); layout->setStretchFactor(idToolBarRight, 0); /* * Use processed event listener since the text event * is first processed by GuiManager which will create * this dialog, if needed, and then display it. */ EventManager::get()->addProcessedEventListener(this, EventTypeEnum::EVENT_UPDATE_INFORMATION_WINDOWS); this->setCentralWidget(widget, WuQDialog::SCROLL_AREA_NEVER); /* * There may already be identification text, so try to display it. */ updateDialog(); } /** * Destructor. */ InformationDisplayDialog::~InformationDisplayDialog() { EventManager::get()->removeAllEventsFromListener(this); } /** * Update the dialog's content. */ void InformationDisplayDialog::updateDialog() { Brain* brain = GuiManager::get()->getBrain(); IdentificationManager* idManager = brain->getIdentificationManager(); const AString text = idManager->getIdentificationText(); m_informationTextBrowser->setContentToHtml(text); m_contralateralIdentificationAction->blockSignals(true); m_contralateralIdentificationAction->setChecked(idManager->isContralateralIdentificationEnabled()); m_contralateralIdentificationAction->blockSignals(false); } /** * Create a scene for an instance of a class. * * @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. * * @return Pointer to SceneClass object representing the state of * this object. Under some circumstances a NULL pointer may be * returned. Caller will take ownership of returned object. */ SceneClass* InformationDisplayDialog::saveToScene(const SceneAttributes* sceneAttributes, const AString& instanceName) { SceneClass* sceneClass = new SceneClass(instanceName, "InformationDisplayDialog", 1); /* * Position and size */ SceneWindowGeometry swg(this); sceneClass->addClass(swg.saveToScene(sceneAttributes, "geometry")); return sceneClass; } /** * Restore the state of an instance of a class. * * @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 containing the state that was previously * saved and should be restored. */ void InformationDisplayDialog::restoreFromScene(const SceneAttributes* sceneAttributes, const SceneClass* sceneClass) { if (sceneClass == NULL) { return; } /* * Position and size */ SceneWindowGeometry swg(this); swg.restoreFromScene(sceneAttributes, sceneClass->getClass("geometry")); } /** * Called when contralateral toolbutton is toggled. */ void InformationDisplayDialog::contralateralIdentificationToggled(bool) { Brain* brain = GuiManager::get()->getBrain(); IdentificationManager* idManager = brain->getIdentificationManager(); idManager->setContralateralIdentificationEnabled(m_contralateralIdentificationAction->isChecked()); } /** * Clear the information text. */ void InformationDisplayDialog::clearInformationText() { Brain* brain = GuiManager::get()->getBrain(); IdentificationManager* idManager = brain->getIdentificationManager(); idManager->removeIdentificationText(); updateDialog(); EventManager::get()->sendEvent(EventGraphicsUpdateAllWindows().getPointer()); } /** * Remove ID symbols from all surfaces. */ void InformationDisplayDialog::removeIdSymbols() { Brain* brain = GuiManager::get()->getBrain(); IdentificationManager* idManager = brain->getIdentificationManager(); idManager->removeAllIdentifiedSymbols(); updateDialog(); EventManager::get()->sendEvent(EventGraphicsUpdateAllWindows().getPointer()); } /** * Receive events from the event manager. * * @param event * Event sent by event manager. */ void InformationDisplayDialog::receiveEvent(Event* event) { bool doUpdate = false; if (event->getEventType() == EventTypeEnum::EVENT_UPDATE_INFORMATION_WINDOWS) { EventUpdateInformationWindows* textEvent = dynamic_cast(event); CaretAssert(textEvent); textEvent->setEventProcessed(); doUpdate = true; } else if (event->getEventType() == EventTypeEnum::EVENT_USER_INTERFACE_UPDATE) { EventUserInterfaceUpdate* uiUpdateEvent = dynamic_cast(event); CaretAssert(uiUpdateEvent); uiUpdateEvent->setEventProcessed(); doUpdate = true; } if (doUpdate) { updateDialog(); } } /** * Show the symbol properties dialog */ void InformationDisplayDialog::showPropertiesDialog() { if (m_propertiesDialog == NULL) { m_propertiesDialog = new InformationDisplayPropertiesDialog(this); } m_propertiesDialog->show(); } connectome-workbench-1.2.3+git41-gc4c6c90/src/GuiQt/InformationDisplayDialog.h000066400000000000000000000052661300200146000267120ustar00rootroot00000000000000#ifndef __INFORMATION_DISPLAY_DIALOG__H_ #define __INFORMATION_DISPLAY_DIALOG__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 "EventListenerInterface.h" #include "SceneableInterface.h" #include "WuQDialogNonModal.h" namespace caret { class BrainBrowserWindow; class HyperLinkTextBrowser; class InformationDisplayPropertiesDialog; class InformationDisplayDialog : public WuQDialogNonModal, public EventListenerInterface, public SceneableInterface { Q_OBJECT public: InformationDisplayDialog(BrainBrowserWindow* parent); virtual ~InformationDisplayDialog(); void receiveEvent(Event* event); virtual void updateDialog(); virtual SceneClass* saveToScene(const SceneAttributes* sceneAttributes, const AString& instanceName); virtual void restoreFromScene(const SceneAttributes* sceneAttributes, const SceneClass* sceneClass); private slots: void clearInformationText(); void removeIdSymbols(); void contralateralIdentificationToggled(bool); //void volumeSliceIdentificationToggled(bool); void showPropertiesDialog(); private: InformationDisplayDialog(const InformationDisplayDialog&); InformationDisplayDialog& operator=(const InformationDisplayDialog&); HyperLinkTextBrowser* m_informationTextBrowser; QAction* m_contralateralIdentificationAction; QString m_informationText; InformationDisplayPropertiesDialog* m_propertiesDialog; }; #ifdef __INFORMATION_DISPLAY_DIALOG_DECLARE__ // #endif // __INFORMATION_DISPLAY_DIALOG_DECLARE__ } // namespace #endif //__INFORMATION_DISPLAY_DIALOG__H_ connectome-workbench-1.2.3+git41-gc4c6c90/src/GuiQt/InformationDisplayPropertiesDialog.cxx000066400000000000000000000160331300200146000313340ustar00rootroot00000000000000 /*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 __INFORMATION_DISPLAY_OPTIONS_DIALOG_DECLARE__ #include "InformationDisplayPropertiesDialog.h" #undef __INFORMATION_DISPLAY_OPTIONS_DIALOG_DECLARE__ #include #include #include #include "Brain.h" #include "CaretAssert.h" #include "CaretColorEnumComboBox.h" #include "EventGraphicsUpdateAllWindows.h" #include "EventManager.h" #include "GuiManager.h" #include "IdentificationManager.h" #include "WuQFactory.h" #include "WuQtUtilities.h" using namespace caret; /** * \class caret::InformationDisplayPropertiesDialog * \brief Options for information dialog display. * \ingroup GuiQt */ /** * Constructor. */ InformationDisplayPropertiesDialog::InformationDisplayPropertiesDialog(QWidget* parent, Qt::WindowFlags f) : WuQDialogNonModal("Information Properties", parent, f) { const int WIDGET_WIDTH = 100; QLabel* idColorLabel = new QLabel("ID Symbol Color: "); m_idColorComboBox = new CaretColorEnumComboBox(this); m_idColorComboBox->getWidget()->setFixedWidth(WIDGET_WIDTH); QObject::connect(m_idColorComboBox, SIGNAL(colorSelected(const CaretColorEnum::Enum)), this, SLOT(informationPropertyChanged())); QLabel* idContralateralLabel = new QLabel("ID Contralateral Symbol Color: "); m_idContralateralColorComboBox = new CaretColorEnumComboBox(this); m_idContralateralColorComboBox->getWidget()->setFixedWidth(WIDGET_WIDTH); QObject::connect(m_idContralateralColorComboBox, SIGNAL(colorSelected(const CaretColorEnum::Enum)), this, SLOT(informationPropertyChanged())); QLabel* symbolSizeLabel = new QLabel("Symbol Diameter: "); m_symbolSizeSpinBox = WuQFactory::newDoubleSpinBoxWithMinMaxStepDecimalsSignalDouble(0.1, 10000.0, 0.1, 1, this, SLOT(informationPropertyChanged())); m_symbolSizeSpinBox->setFixedWidth(WIDGET_WIDTH); m_symbolSizeSpinBox->setSuffix("mm"); QObject::connect(m_symbolSizeSpinBox, SIGNAL(valueChanged(double)), this, SLOT(informationPropertyChanged())); QLabel* mostRecentSymbolSizeLabel = new QLabel("Most Recent ID Symbol Diameter: "); m_mostRecentSymbolSizeSpinBox = WuQFactory::newDoubleSpinBoxWithMinMaxStepDecimalsSignalDouble(0.1, 10000.0, 0.1, 1, this, SLOT(informationPropertyChanged())); m_mostRecentSymbolSizeSpinBox->setFixedWidth(WIDGET_WIDTH); m_mostRecentSymbolSizeSpinBox->setSuffix("mm"); 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(idColorLabel, row, COLUMN_LABEL); gridLayout->addWidget(m_idColorComboBox->getWidget(), row, COLUMN_WIDGET); row++; gridLayout->addWidget(idContralateralLabel, row, COLUMN_LABEL); gridLayout->addWidget(m_idContralateralColorComboBox->getWidget(), row, COLUMN_WIDGET); row++; gridLayout->addWidget(symbolSizeLabel, row, COLUMN_LABEL); gridLayout->addWidget(m_symbolSizeSpinBox, row, COLUMN_WIDGET); row++; gridLayout->addWidget(mostRecentSymbolSizeLabel, row, COLUMN_LABEL); gridLayout->addWidget(m_mostRecentSymbolSizeSpinBox, row, COLUMN_WIDGET); 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); setApplyButtonText(""); setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); updateDialog(); setSaveWindowPositionForNextTime(true); } /** * Destructor. */ InformationDisplayPropertiesDialog::~InformationDisplayPropertiesDialog() { } /** * update its content */ void InformationDisplayPropertiesDialog::updateDialog() { Brain* brain = GuiManager::get()->getBrain(); IdentificationManager* info = brain->getIdentificationManager(); m_idColorComboBox->setSelectedColor(info->getIdentificationSymbolColor()); m_idContralateralColorComboBox->setSelectedColor(info->getIdentificationContralateralSymbolColor()); m_symbolSizeSpinBox->blockSignals(true); m_symbolSizeSpinBox->setValue(info->getIdentificationSymbolSize()); m_symbolSizeSpinBox->blockSignals(false); m_mostRecentSymbolSizeSpinBox->blockSignals(true); m_mostRecentSymbolSizeSpinBox->setValue(info->getMostRecentIdentificationSymbolSize()); m_mostRecentSymbolSizeSpinBox->blockSignals(false); } /** * Gets called when a property changes. */ void InformationDisplayPropertiesDialog::informationPropertyChanged() { Brain* brain = GuiManager::get()->getBrain(); IdentificationManager* info = brain->getIdentificationManager(); info->setIdentificationSymbolColor(m_idColorComboBox->getSelectedColor()); info->setIdentificationContralateralSymbolColor(m_idContralateralColorComboBox->getSelectedColor()); info->setIdentificationSymbolSize(m_symbolSizeSpinBox->value()); info->setMostRecentIdentificationSymbolSize(m_mostRecentSymbolSizeSpinBox->value()); EventManager::get()->sendEvent(EventGraphicsUpdateAllWindows().getPointer()); } connectome-workbench-1.2.3+git41-gc4c6c90/src/GuiQt/InformationDisplayPropertiesDialog.h000066400000000000000000000042731300200146000307640ustar00rootroot00000000000000#ifndef __INFORMATION_DISPLAY_OPTIONS_DIALOG_H__ #define __INFORMATION_DISPLAY_OPTIONS_DIALOG_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 "WuQDialogNonModal.h" #include namespace caret { class CaretColorEnumComboBox; class InformationDisplayPropertiesDialog : public WuQDialogNonModal { Q_OBJECT public: InformationDisplayPropertiesDialog(QWidget* parent, Qt::WindowFlags f = 0); virtual ~InformationDisplayPropertiesDialog(); void updateDialog(); // ADD_NEW_METHODS_HERE private slots: void informationPropertyChanged(); private: InformationDisplayPropertiesDialog(const InformationDisplayPropertiesDialog&); InformationDisplayPropertiesDialog& operator=(const InformationDisplayPropertiesDialog&); CaretColorEnumComboBox* m_idColorComboBox; CaretColorEnumComboBox* m_idContralateralColorComboBox; QDoubleSpinBox* m_symbolSizeSpinBox; QDoubleSpinBox* m_mostRecentSymbolSizeSpinBox; // ADD_NEW_MEMBERS_HERE }; #ifdef __INFORMATION_DISPLAY_OPTIONS_DIALOG_DECLARE__ // #endif // __INFORMATION_DISPLAY_OPTIONS_DIALOG_DECLARE__ } // namespace #endif //__INFORMATION_DISPLAY_OPTIONS_DIALOG_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/GuiQt/KeyEvent.cxx000066400000000000000000000061711300200146000240600ustar00rootroot00000000000000/*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 "KeyEvent.h" using namespace caret; /** * \class caret::KeyEvent * \brief Event issued when key is moved or buttons are pressed. * \ingroup GuiQt */ /** * Constructor. * @param openGLWidget * OpenGL Widget in which key activity took place. * @param browserWindowIndex * Index of the browser winddow in which key activity took place. * @param keyCode * The key that was pressed. See Qt::Key for list of codes. * @param firstKeyPressFlag * True if this is the first key press when key is held down for a period of time * @param shiftKeyDownFlag * True if the shift key is down */ KeyEvent::KeyEvent(BrainOpenGLWidget* openGLWidget, const int32_t browserWindowIndex, const int32_t keyCode, const bool firstKeyPressFlag, const bool shiftKeyDownFlag) : CaretObject(), m_openGLWidget(openGLWidget), m_browserWindowIndex(browserWindowIndex), m_keyCode(keyCode), m_firstKeyPressFlag(firstKeyPressFlag), m_shiftKeyDownFlag(shiftKeyDownFlag) { } /** * Destructor */ KeyEvent::~KeyEvent() { } /** * Initialize all members. */ void KeyEvent::initializeMembersKeyEvent() { } /** * @return The OpenGL Widget in which the key event occurred. */ BrainOpenGLWidget* KeyEvent::getOpenGLWidget() const { return m_openGLWidget; } /** * Get a string showing the contents of this key event. * @return String describing the key status. */ AString KeyEvent::toString() const { const AString msg = ", keyCord=" + AString::number(m_keyCode); return msg; } /** * @return Index of the browser window in which the * event took place. */ int32_t KeyEvent::getBrowserWindowIndex() const { return m_browserWindowIndex; } /** * Get the key code. See Qt::Key for list of codes. * @return The key code. * */ int32_t KeyEvent::getKeyCode() const { return m_keyCode; } /** * @return Is this the first (of possibly many) key press events? * If a key is held down for a period of time, this method * will return true for only the first key press events. * */ bool KeyEvent::isFirstKeyPressFlag() const { return m_firstKeyPressFlag; } /** * @return Is this the SHIFT key down? * */ bool KeyEvent::isShiftKeyDownFlag() const { return m_shiftKeyDownFlag; } connectome-workbench-1.2.3+git41-gc4c6c90/src/GuiQt/KeyEvent.h000066400000000000000000000042511300200146000235020ustar00rootroot00000000000000#ifndef __KEY_EVENT_H__ #define __KEY_EVENT_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 class QKeyEvent; namespace caret { class BrainOpenGLWidget; /** * Contains information about a key event in the OpenGL region. */ class KeyEvent : public CaretObject { public: KeyEvent(BrainOpenGLWidget* openGLWidget, const int32_t browserWindowIndex, const int32_t keyCode, const bool firstKeyPressFlag, const bool shiftKeyDownFlag); virtual ~KeyEvent(); private: void initializeMembersKeyEvent(); KeyEvent(const KeyEvent& o); KeyEvent& operator=(const KeyEvent& o); public: AString toString() const; BrainOpenGLWidget* getOpenGLWidget() const; int32_t getBrowserWindowIndex() const; int32_t getKeyCode() const; bool isFirstKeyPressFlag() const; bool isShiftKeyDownFlag() const; private: BrainOpenGLWidget* m_openGLWidget; const int32_t m_browserWindowIndex; const int32_t m_keyCode; const bool m_firstKeyPressFlag; const bool m_shiftKeyDownFlag; }; } // namespace #endif // __KEY_EVENT_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/GuiQt/LabelDrawingTypeComboBox.cxx000066400000000000000000000025001300200146000271440ustar00rootroot00000000000000 /*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 __LABEL_DRAWING_TYPE_COMBO_BOX_DECLARE__ #include "LabelDrawingTypeComboBox.h" #undef __LABEL_DRAWING_TYPE_COMBO_BOX_DECLARE__ using namespace caret; /** * \class caret::LabelDrawingTypeComboBox * \brief * * */ /** * Constructor. */ LabelDrawingTypeComboBox::LabelDrawingTypeComboBox() : EnumComboBoxTemplate() { } /** * Destructor. */ LabelDrawingTypeComboBox::~LabelDrawingTypeComboBox() { } connectome-workbench-1.2.3+git41-gc4c6c90/src/GuiQt/LabelDrawingTypeComboBox.h000066400000000000000000000031631300200146000265770ustar00rootroot00000000000000#ifndef __LABEL_DRAWING_TYPE_COMBO_BOX__H_ #define __LABEL_DRAWING_TYPE_COMBO_BOX__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 "EnumComboBoxTemplate.h" namespace caret { class LabelDrawingTypeComboBox : public EnumComboBoxTemplate { public: LabelDrawingTypeComboBox(); virtual ~LabelDrawingTypeComboBox(); private: LabelDrawingTypeComboBox(const LabelDrawingTypeComboBox&); LabelDrawingTypeComboBox& operator=(const LabelDrawingTypeComboBox&); public: // ADD_NEW_METHODS_HERE private: // ADD_NEW_MEMBERS_HERE }; #ifdef __LABEL_DRAWING_TYPE_COMBO_BOX_DECLARE__ // #endif // __LABEL_DRAWING_TYPE_COMBO_BOX_DECLARE__ } // namespace #endif //__LABEL_DRAWING_TYPE_COMBO_BOX__H_ connectome-workbench-1.2.3+git41-gc4c6c90/src/GuiQt/LabelSelectionViewController.cxx000066400000000000000000000246301300200146000301120ustar00rootroot00000000000000 /*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 __LABEL_SELECTION_VIEW_CONTROLLER_DECLARE__ #include "LabelSelectionViewController.h" #undef __LABEL_SELECTION_VIEW_CONTROLLER_DECLARE__ #include "Brain.h" #include "BrainOpenGL.h" #include "BrainStructure.h" #include "BrowserTabContent.h" #include "CaretAssert.h" #include "GroupAndNameHierarchyViewController.h" #include "DisplayGroupEnumComboBox.h" #include "DisplayPropertiesLabels.h" #include "EventGraphicsUpdateAllWindows.h" #include "EventManager.h" #include "EventSurfaceColoringInvalidate.h" #include "EventUserInterfaceUpdate.h" #include "GuiManager.h" #include "SceneClass.h" #include "VolumeFile.h" #include "WuQDataEntryDialog.h" #include "WuQTabWidget.h" #include "WuQtUtilities.h" using namespace caret; /** * \class caret::LabelSelectionViewController * \brief Widget for controlling display of labels * \ingroup GuiQt * * Widget for controlling the display of labels including * different display groups. */ /** * Constructor. */ LabelSelectionViewController::LabelSelectionViewController(const int32_t browserWindowIndex, QWidget* parent) : QWidget(parent) { m_browserWindowIndex = browserWindowIndex; QLabel* groupLabel = new QLabel("Group"); m_labelsDisplayGroupComboBox = new DisplayGroupEnumComboBox(this); QObject::connect(m_labelsDisplayGroupComboBox, SIGNAL(displayGroupSelected(const DisplayGroupEnum::Enum)), this, SLOT(labelDisplayGroupSelected(const DisplayGroupEnum::Enum))); QHBoxLayout* groupLayout = new QHBoxLayout(); groupLayout->addWidget(groupLabel); groupLayout->addWidget(m_labelsDisplayGroupComboBox->getWidget()); groupLayout->addStretch(); QWidget* selectionWidget = this->createSelectionWidget(); QVBoxLayout* layout = new QVBoxLayout(this); layout->addLayout(groupLayout); layout->addWidget(selectionWidget, 0, Qt::AlignLeft); layout->addStretch(); EventManager::get()->addEventListener(this, EventTypeEnum::EVENT_USER_INTERFACE_UPDATE); LabelSelectionViewController::allLabelSelectionViewControllers.insert(this); } /** * Destructor. */ LabelSelectionViewController::~LabelSelectionViewController() { EventManager::get()->removeAllEventsFromListener(this); LabelSelectionViewController::allLabelSelectionViewControllers.erase(this); } QWidget* LabelSelectionViewController::createSelectionWidget() { m_labelClassNameHierarchyViewController = new GroupAndNameHierarchyViewController(m_browserWindowIndex); return m_labelClassNameHierarchyViewController; } /** * Called when the label display group combo box is changed. */ void LabelSelectionViewController::labelDisplayGroupSelected(const DisplayGroupEnum::Enum displayGroup) { /* * Update selected display group in model. */ BrowserTabContent* browserTabContent = GuiManager::get()->getBrowserTabContentForBrowserWindow(m_browserWindowIndex, false); if (browserTabContent == NULL) { return; } const int32_t browserTabIndex = browserTabContent->getTabNumber(); Brain* brain = GuiManager::get()->getBrain(); DisplayPropertiesLabels* dsb = brain->getDisplayPropertiesLabels(); dsb->setDisplayGroupForTab(browserTabIndex, displayGroup); /* * Since display group has changed, need to update controls */ updateLabelViewController(); /* * Apply the changes. */ processLabelSelectionChanges(); } /** * Update the label selection widget. */ void LabelSelectionViewController::updateLabelViewController() { setWindowTitle("Labels"); BrowserTabContent* browserTabContent = GuiManager::get()->getBrowserTabContentForBrowserWindow(m_browserWindowIndex, true); if (browserTabContent == NULL) { return; } const int32_t browserTabIndex = browserTabContent->getTabNumber(); Brain* brain = GuiManager::get()->getBrain(); DisplayPropertiesLabels* dpb = brain->getDisplayPropertiesLabels(); const DisplayGroupEnum::Enum displayGroup = dpb->getDisplayGroupForTab(browserTabIndex); m_labelsDisplayGroupComboBox->setSelectedDisplayGroup(dpb->getDisplayGroupForTab(browserTabIndex)); /* * Get all of label files. */ std::vector allLabelFiles; const int numBrainStructures = brain->getNumberOfBrainStructures(); for (int32_t ibs = 0; ibs < numBrainStructures; ibs++) { BrainStructure* brainStructure = brain->getBrainStructure(ibs); const int32_t numLabelFiles = brainStructure->getNumberOfLabelFiles(); for (int32_t ilf = 0; ilf < numLabelFiles; ilf++) { allLabelFiles.push_back(brainStructure->getLabelFile(ilf)); } } /* * Get all CIFTI label files */ std::vector allCiftiLabelFiles; const int32_t numCiftiLabelFiles = brain->getNumberOfConnectivityDenseLabelFiles(); for (int32_t iclf = 0; iclf < numCiftiLabelFiles; iclf++) { allCiftiLabelFiles.push_back(brain->getConnectivityDenseLabelFile(iclf)); } /* * Get all Volume Files that are mapped with label tables */ std::vector allVolumeLabelFiles; const int32_t numVolumeFiles = brain->getNumberOfVolumeFiles(); for (int32_t iVol = 0; iVol < numVolumeFiles; iVol++) { VolumeFile* vf = brain->getVolumeFile(iVol); if (vf->isMappedWithLabelTable()) { allVolumeLabelFiles.push_back(vf); } } /* * Update the class/name hierarchy */ m_labelClassNameHierarchyViewController->updateContents(allLabelFiles, allCiftiLabelFiles, allVolumeLabelFiles, displayGroup); } /** * Update other selection toolbox since they should all be the same. */ void LabelSelectionViewController::updateOtherLabelViewControllers() { for (std::set::iterator iter = LabelSelectionViewController::allLabelSelectionViewControllers.begin(); iter != LabelSelectionViewController::allLabelSelectionViewControllers.end(); iter++) { LabelSelectionViewController* bsw = *iter; if (bsw != this) { bsw->updateLabelViewController(); } } } /** * Gets called when label selections are changed. */ void LabelSelectionViewController::processLabelSelectionChanges() { BrowserTabContent* browserTabContent = GuiManager::get()->getBrowserTabContentForBrowserWindow(m_browserWindowIndex, false); CaretAssert(browserTabContent); const int32_t browserTabIndex = browserTabContent->getTabNumber(); Brain* brain = GuiManager::get()->getBrain(); DisplayPropertiesLabels* dsb = brain->getDisplayPropertiesLabels(); dsb->setDisplayGroupForTab(browserTabIndex, m_labelsDisplayGroupComboBox->getSelectedDisplayGroup()); processSelectionChanges(); } /** * Issue update events after selections are changed. */ void LabelSelectionViewController::processSelectionChanges() { updateOtherLabelViewControllers(); EventManager::get()->sendEvent(EventSurfaceColoringInvalidate().getPointer()); EventManager::get()->sendEvent(EventGraphicsUpdateAllWindows().getPointer()); } /** * Receive events from the event manager. * * @param event * Event sent by event manager. */ void LabelSelectionViewController::receiveEvent(Event* event) { bool doUpdate = false; if (event->getEventType() == EventTypeEnum::EVENT_USER_INTERFACE_UPDATE) { EventUserInterfaceUpdate* uiEvent = dynamic_cast(event); CaretAssert(uiEvent); if (uiEvent->isUpdateForWindow(m_browserWindowIndex)) { if (uiEvent->isToolBoxUpdate()) { doUpdate = true; uiEvent->setEventProcessed(); } } } if (doUpdate) { updateLabelViewController(); } } /** * Create a scene for an instance of a class. * * @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. * * @return Pointer to SceneClass object representing the state of * this object. Under some circumstances a NULL pointer may be * returned. Caller will take ownership of returned object. */ SceneClass* LabelSelectionViewController::saveToScene(const SceneAttributes* /*sceneAttributes*/, const AString& instanceName) { SceneClass* sceneClass = new SceneClass(instanceName, "LabelSelectionViewController", 1); return sceneClass; } /** * Restore the state of an instance of a class. * * @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 containing the state that was previously * saved and should be restored. */ void LabelSelectionViewController::restoreFromScene(const SceneAttributes* /*sceneAttributes*/, const SceneClass* sceneClass) { if (sceneClass == NULL) { return; } } connectome-workbench-1.2.3+git41-gc4c6c90/src/GuiQt/LabelSelectionViewController.h000066400000000000000000000061321300200146000275340ustar00rootroot00000000000000#ifndef __LABEL_SELECTION_VIEW_CONTROLLER__H_ #define __LABEL_SELECTION_VIEW_CONTROLLER__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 "DisplayGroupEnum.h" #include "EventListenerInterface.h" #include "SceneableInterface.h" class QCheckBox; namespace caret { class GroupAndNameHierarchyViewController; class DisplayGroupEnumComboBox; class LabelSelectionViewController : public QWidget, public EventListenerInterface, public SceneableInterface { Q_OBJECT public: LabelSelectionViewController(const int32_t browserWindowIndex, QWidget* parent = 0); virtual ~LabelSelectionViewController(); void receiveEvent(Event* event); virtual SceneClass* saveToScene(const SceneAttributes* sceneAttributes, const AString& instanceName); virtual void restoreFromScene(const SceneAttributes* sceneAttributes, const SceneClass* sceneClass); private slots: void processLabelSelectionChanges(); void processSelectionChanges(); void labelDisplayGroupSelected(const DisplayGroupEnum::Enum); private: LabelSelectionViewController(const LabelSelectionViewController&); LabelSelectionViewController& operator=(const LabelSelectionViewController&); void updateLabelViewController(); void updateOtherLabelViewControllers(); QWidget* createSelectionWidget(); int32_t m_browserWindowIndex; GroupAndNameHierarchyViewController* m_labelClassNameHierarchyViewController; QCheckBox* m_labelsDisplayCheckBox; QCheckBox* m_labelsContralateralCheckBox; DisplayGroupEnumComboBox* m_labelsDisplayGroupComboBox; static std::set allLabelSelectionViewControllers; }; #ifdef __LABEL_SELECTION_VIEW_CONTROLLER_DECLARE__ std::set LabelSelectionViewController::allLabelSelectionViewControllers; #endif // __LABEL_SELECTION_VIEW_CONTROLLER_DECLARE__ } // namespace #endif //__LABEL_SELECTION_VIEW_CONTROLLER__H_ connectome-workbench-1.2.3+git41-gc4c6c90/src/GuiQt/MacApplication.cxx000066400000000000000000000046511300200146000252130ustar00rootroot00000000000000 /*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 __MAC_APPLICATION_DECLARE__ #include "MacApplication.h" #undef __MAC_APPLICATION_DECLARE__ #include #include #include #include "CaretAssert.h" #include "EventManager.h" #include "EventOperatingSystemRequestOpenDataFile.h" using namespace caret; /** * \class caret::MacApplication * \brief Subclass of QApplication so that Macs can receive file open events * \ingroup GuiQt * * Allows user to open a spec file via Mac Finder. * * Based upon: http://www.qtcentre.org/wiki/index.php?title=Opening_documents_in_the_Mac_OS_X_Finder&printable=yes&useskin=vector */ /** * Constructor. */ MacApplication::MacApplication(int& argc, char** argv) : QApplication(argc, argv) { } /** * Destructor. */ MacApplication::~MacApplication() { } /** * Receive events and processes them. * * @param event * The event. * @return true if the event was processed, else false. */ bool MacApplication::event(QEvent *event) { bool eventWasProcessed = false; switch (event->type()) { case QEvent::FileOpen: { QFileOpenEvent* openFileEvent = static_cast(event); CaretAssert(openFileEvent); const QString filename = openFileEvent->file(); EventManager::get()->sendEvent(EventOperatingSystemRequestOpenDataFile(filename).getPointer()); eventWasProcessed = true; } break; default: eventWasProcessed = QApplication::event(event); break; } return eventWasProcessed; } connectome-workbench-1.2.3+git41-gc4c6c90/src/GuiQt/MacApplication.h000066400000000000000000000033161300200146000246350ustar00rootroot00000000000000#ifndef __MAC_APPLICATION_H__ #define __MAC_APPLICATION_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*/ /* * Based upon: http://www.qtcentre.org/wiki/index.php?title=Opening_documents_in_the_Mac_OS_X_Finder&printable=yes&useskin=vector */ #include namespace caret { class MacApplication : public QApplication { Q_OBJECT public: MacApplication(int& argc, char** argv); virtual ~MacApplication(); private: MacApplication(const MacApplication&); MacApplication& operator=(const MacApplication&); public: // ADD_NEW_METHODS_HERE protected: virtual bool event(QEvent *event); private: // ADD_NEW_MEMBERS_HERE }; #ifdef __MAC_APPLICATION_DECLARE__ // #endif // __MAC_APPLICATION_DECLARE__ } // namespace #endif //__MAC_APPLICATION_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/GuiQt/MacDockMenu.cxx000066400000000000000000000173161300200146000244570ustar00rootroot00000000000000 /*LICENSE_START*/ /* * Copyright (C) 2015 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 __MAC_DOCK_MENU_DECLARE__ #include "MacDockMenu.h" #undef __MAC_DOCK_MENU_DECLARE__ #include "Brain.h" #include "BrainBrowserWindow.h" #include "CaretAssert.h" #include "EventBrowserWindowNew.h" #include "EventGraphicsUpdateOneWindow.h" #include "EventManager.h" #include "EventUserInterfaceUpdate.h" #include "GuiManager.h" #include "SpecFile.h" using namespace caret; #ifdef CARET_OS_MACOSX /* * Not in Qt Header Files. * See http://qt-project.org/doc/qt-4.8/exportedfunctions.html */ extern void qt_mac_set_dock_menu(QMenu *menu); #endif // CARET_OS_MACOSX /** * \class caret::MacDockMenu * \brief Menu that is displayed Mac's Dock Menu. * \ingroup GuiQt * * The Mac Dock Menu is displayed when the user either holds the mouse button * down or Control-Clicks the icon for a running wb_view instance in the * Mac's Dock. */ /** * Constructor. */ MacDockMenu::MacDockMenu(QWidget* parent) : QMenu(parent) { /* * Recent Spec Files */ const int32_t firstRecentSpecFileIndex = actions().size(); const int32_t numRecentSpecFiles = BrainBrowserWindow::loadRecentSpecFileMenu(this); QList menuActions = actions(); const int32_t numberOfRecentSpecFileActions = menuActions.size() - firstRecentSpecFileIndex; for (int32_t i = firstRecentSpecFileIndex; i < numberOfRecentSpecFileActions; i++) { m_recentSpecFileActions.push_back(menuActions.at(i)); } if (numRecentSpecFiles > 0) { addSeparator(); } /* * Name of spec file */ const AString specFileName = GuiManager::get()->getBrain()->getSpecFile()->getFileNameNoPath(); /* * Open Windows */ const std::vector browserWindows = GuiManager::get()->getAllOpenBrainBrowserWindows(); if ( ! browserWindows.empty()) { for (std::vector::const_iterator bwIter = browserWindows.begin(); bwIter != browserWindows.end(); bwIter++) { const BrainBrowserWindow* bbw = *bwIter; const AString title = (bbw->windowTitle() + " " + specFileName); QAction* action = addAction(title); action->setData(bbw->getBrowserWindowIndex()); m_browserWindowActions.push_back(action); } addSeparator(); } /* * Create New Window */ m_newBrowserWindowAction = addAction("New Window"); /* * Connect the menu to a slot for processing menu selections */ QObject::connect(this, SIGNAL(triggered(QAction*)), this, SLOT(menuActionTriggered(QAction*))); } /** * Destructor. */ MacDockMenu::~MacDockMenu() { std::cout << "Deleting Mac Dock Menu" << std::endl; } /** * Called when an item in the menu is selected. * * @param action * Menu's action that was triggered. */ void MacDockMenu::menuActionTriggered(QAction* action) { if (action == m_newBrowserWindowAction) { createNewBrowserWindow(); } else if (std::find(m_recentSpecFileActions.begin(), m_recentSpecFileActions.end(), action) != m_recentSpecFileActions.end()) { const AString specFileName = action->data().toString(); std::vector fileNamesToLoad; fileNamesToLoad.push_back(specFileName); BrainBrowserWindow* browserWindow = GuiManager::get()->getActiveBrowserWindow(); browserWindow->loadFilesFromCommandLine(fileNamesToLoad, BrainBrowserWindow::LOAD_SPEC_FILE_WITH_DIALOG); } else if (std::find(m_browserWindowActions.begin(), m_browserWindowActions.end(), action) != m_browserWindowActions.end()) { const int32_t browserWindowIndex = action->data().toInt(); BrainBrowserWindow* bbw = GuiManager::get()->getBrowserWindowByWindowIndex(browserWindowIndex); if (bbw != NULL) { bbw->activateWindow(); bbw->raise(); } } else if (action != NULL) { CaretAssertMessage(0, ("Mac Dock menu action not processed: " + action->text())); } } /** * Factory method to create/update the Mac Dock Menu. * On non-Mac platforms, this method does nothing. * * With Qt Menus, the "aboutToShow()" signal is used to * update the content of the menu so that the menu * can be updated just before it is displayed. However, * when a menu is added to the Dock, it is converted * to a platform native menu so the "aboutToShow()" signal * does not get issued. So, in Workbench, a Workbench event * is issued when something happens that requires an * update to the Dock Menu. This results in a new menu * being created and replacing the current Dock menu. */ void MacDockMenu::createUpdateMacDockMenu() { const bool DISABLE_DOCK_MENU = true; if (DISABLE_DOCK_MENU) { return; } #ifdef CARET_OS_MACOSX BrainBrowserWindow* browserWindow = GuiManager::get()->getActiveBrowserWindow(); if (browserWindow == NULL) { return; } MacDockMenu* menu = new MacDockMenu(); qt_mac_set_dock_menu(menu); /* * Previously created menus probably should be deleted but * it is not clear if QT takes ownership of the menu * which could result in a "double delete". In Qt 4.8.x, * the code does not show the menu being deleted at * any time but if that changes a crash would result. * * Another problem is that selection from the Dock * menu may cause the Dock menu to get recreated. */ // if (s_previousMacDockMenu != NULL) { // delete s_previousMacDockMenu; // } // s_previousMacDockMenu = menu; #endif // CARET_OS_MACOSX } /** * Gets called to create a new browser window. */ void MacDockMenu::createNewBrowserWindow() { EventManager::get()->blockEvent(EventTypeEnum::EVENT_GRAPHICS_UPDATE_ONE_WINDOW, true); BrainBrowserWindow* browserWindow = GuiManager::get()->getActiveBrowserWindow(); EventBrowserWindowNew eventNewBrowser(browserWindow, NULL); EventManager::get()->sendEvent(eventNewBrowser.getPointer()); if (eventNewBrowser.isError()) { QMessageBox::critical(browserWindow, "", eventNewBrowser.getErrorMessage()); return; } const int32_t newWindowIndex = eventNewBrowser.getBrowserWindowCreated()->getBrowserWindowIndex(); EventManager::get()->sendEvent(EventUserInterfaceUpdate().setWindowIndex(newWindowIndex).getPointer()); EventManager::get()->blockEvent(EventTypeEnum::EVENT_GRAPHICS_UPDATE_ONE_WINDOW, false); EventManager::get()->sendEvent(EventGraphicsUpdateOneWindow(newWindowIndex).getPointer()); } connectome-workbench-1.2.3+git41-gc4c6c90/src/GuiQt/MacDockMenu.h000066400000000000000000000035461300200146000241040ustar00rootroot00000000000000#ifndef __MAC_DOCK_MENU_H__ #define __MAC_DOCK_MENU_H__ /*LICENSE_START*/ /* * Copyright (C) 2015 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 namespace caret { class MacDockMenu : public QMenu { Q_OBJECT public: static void createUpdateMacDockMenu(); virtual ~MacDockMenu(); // ADD_NEW_METHODS_HERE private slots: void menuActionTriggered(QAction*); private: MacDockMenu(QWidget* parent = 0); MacDockMenu(const MacDockMenu&); MacDockMenu& operator=(const MacDockMenu&); void createNewBrowserWindow(); QAction* m_newBrowserWindowAction; std::vector m_recentSpecFileActions; std::vector m_browserWindowActions; static MacDockMenu* s_previousMacDockMenu; // ADD_NEW_MEMBERS_HERE }; #ifdef __MAC_DOCK_MENU_DECLARE__ MacDockMenu* MacDockMenu::s_previousMacDockMenu = NULL; #endif // __MAC_DOCK_MENU_DECLARE__ } // namespace #endif //__MAC_DOCK_MENU_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/GuiQt/MapSettingsColorBarWidget.cxx000066400000000000000000000430441300200146000273540ustar00rootroot00000000000000 /*LICENSE_START*/ /* * Copyright (C) 2015 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_WIDGET_DECLARE__ #include "MapSettingsColorBarWidget.h" #undef __MAP_SETTINGS_COLOR_BAR_WIDGET_DECLARE__ #include #include #include #include #include #include #include "AnnotationColorBar.h" #include "CaretAssert.h" #include "CaretMappableDataFile.h" #include "EnumComboBoxTemplate.h" #include "EventGraphicsUpdateAllWindows.h" #include "EventManager.h" #include "NumericFormatModeEnum.h" #include "Overlay.h" #include "PaletteColorBarValuesModeEnum.h" #include "PaletteColorMapping.h" #include "WuQtUtilities.h" using namespace caret; /** * \class caret::MapSettingsColorBarWidget * \brief Contains controls for adjusting a color bar's attributes. * \ingroup GuiQt */ /** * Constructor. */ MapSettingsColorBarWidget::MapSettingsColorBarWidget(QWidget* parent) : QWidget(parent) { m_colorBar = NULL; m_paletteColorMapping = NULL; QWidget* locationPositionWidget = this->createLocationPositionSection(); QWidget* numericsWidget = this->createDataNumericsSection(); 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->setSizeConstraint(QLayout::SetFixedSize); setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); } /** * Destructor. */ MapSettingsColorBarWidget::~MapSettingsColorBarWidget() { } ///** // * Update the content of widget. // * // * @param overlay // * Overlay for display in this widget. // */ //void //MapSettingsColorBarWidget::updateContent(Overlay* overlay) //{ // m_colorBar = NULL; // m_paletteColorMapping = NULL; // // if (overlay != NULL) { // CaretMappableDataFile* mapFile = NULL; // int32_t mapIndex = 0; // overlay->getSelectionData(mapFile, // mapIndex); // if (mapFile != NULL) { // if ((mapIndex >= 0) // && (mapIndex < mapFile->getNumberOfMaps())) { // if (mapFile->isMappedWithPalette()) { // m_paletteColorMapping = mapFile->getMapPaletteColorMapping(mapIndex); // m_colorBar = overlay->getColorBar(); // } // } // } // } //} void MapSettingsColorBarWidget::updateContent(AnnotationColorBar* annotationColorBar, PaletteColorMapping* paletteColorMapping) { m_colorBar = annotationColorBar; m_paletteColorMapping = paletteColorMapping; updateContentPrivate(); } /** * Update the content of the widget. */ void MapSettingsColorBarWidget::updateContentPrivate() { bool enableWidget = false; if ((m_colorBar != NULL) && (m_paletteColorMapping != NULL)) { const AnnotationColorBarPositionModeEnum::Enum positionMode = m_colorBar->getPositionMode(); m_annotationColorBarPositionModeEnumComboBox->setSelectedItem(positionMode); const AnnotationCoordinateSpaceEnum::Enum coordinateSpace = m_colorBar->getCoordinateSpace(); m_annotationCoordinateSpaceEnumComboBox->setSelectedItem(coordinateSpace); std::vector annotationVector; annotationVector.push_back(m_colorBar); std::vector annotationTwoDimVector; annotationTwoDimVector.push_back(m_colorBar); enableWidget = true; } this->updateColorBarAttributes(); setEnabled(enableWidget); } /** * Called when a control is changed. */ void 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()); } EventManager::get()->sendEvent(EventGraphicsUpdateAllWindows().getPointer()); } /** * Gets called when a position mode combo box item is selected. */ void MapSettingsColorBarWidget::annotationColorBarPositionModeEnumComboBoxItemActivated() { const AnnotationColorBarPositionModeEnum::Enum positionMode = m_annotationColorBarPositionModeEnumComboBox->getSelectedItem(); if (m_colorBar != NULL) { m_colorBar->setPositionMode(positionMode); switch (positionMode) { case AnnotationColorBarPositionModeEnum::AUTOMATIC: m_colorBar->resetSizeAttributes(); break; case AnnotationColorBarPositionModeEnum::MANUAL: break; } EventManager::get()->sendEvent(EventGraphicsUpdateAllWindows().getPointer()); } updateContentPrivate(); } /** * Gets called when a coordinate space combo box item is selected. */ void MapSettingsColorBarWidget::annotationCoordinateSpaceEnumComboBoxItemActivated() { const AnnotationCoordinateSpaceEnum::Enum coordinateSpace = m_annotationCoordinateSpaceEnumComboBox->getSelectedItem(); if (m_colorBar != NULL) { m_colorBar->setCoordinateSpace(coordinateSpace); EventManager::get()->sendEvent(EventGraphicsUpdateAllWindows().getPointer()); } } QWidget* MapSettingsColorBarWidget::createLocationPositionSection() { QLabel* positionModeLabel = new QLabel("Positioning"); m_annotationColorBarPositionModeEnumComboBox = new EnumComboBoxTemplate(this); m_annotationColorBarPositionModeEnumComboBox->setup(); QObject::connect(m_annotationColorBarPositionModeEnumComboBox, SIGNAL(itemActivated()), this, SLOT(annotationColorBarPositionModeEnumComboBoxItemActivated())); m_annotationColorBarPositionModeEnumComboBox->getWidget()->setToolTip("AUTOMATIC - color bars are stacked\n" " in lower left corner of Tab/Window\n" "MANUAL - user must set the X and Y\n" " coordinates in the Tab/Window\n"); std::vector supportedSpaces; supportedSpaces.push_back(AnnotationCoordinateSpaceEnum::TAB); supportedSpaces.push_back(AnnotationCoordinateSpaceEnum::WINDOW); QLabel* coordinateSpaceLabel = new QLabel("Show Color Bar in "); m_annotationCoordinateSpaceEnumComboBox = new EnumComboBoxTemplate(this); m_annotationCoordinateSpaceEnumComboBox->setupWithItems(supportedSpaces); QObject::connect(m_annotationCoordinateSpaceEnumComboBox, SIGNAL(itemActivated()), this, SLOT(annotationCoordinateSpaceEnumComboBoxItemActivated())); m_annotationCoordinateSpaceEnumComboBox->getWidget()->setToolTip("TAB - Restricts location of colorbar\n" " to within its respective tab's\n" " region in the graphics area\n" "WINDOW - Allows colorbar in any\n" " location in the graphics area"); QWidget* modeSpaceWidget = new QWidget(); QGridLayout* modeSpaceLayout = new QGridLayout(modeSpaceWidget); modeSpaceLayout->setContentsMargins(0, 0, 0, 0); modeSpaceLayout->addWidget(coordinateSpaceLabel, 0, 0); modeSpaceLayout->addWidget(m_annotationCoordinateSpaceEnumComboBox->getWidget(), 0, 1); modeSpaceLayout->addWidget(positionModeLabel, 1, 0); modeSpaceLayout->addWidget(m_annotationColorBarPositionModeEnumComboBox->getWidget(), 1, 1); QGroupBox* locationAndDimGroupBox = new QGroupBox("Location and Positioning"); QVBoxLayout* locationAndDimLayout = new QVBoxLayout(locationAndDimGroupBox); locationAndDimLayout->setContentsMargins(0, 0, 0, 0); locationAndDimLayout->setSpacing(2); locationAndDimLayout->addWidget(modeSpaceWidget, 0, Qt::AlignLeft); locationAndDimGroupBox->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); return locationAndDimGroupBox; } /** * @return Create the color bar section. */ QWidget* MapSettingsColorBarWidget::createDataNumericsSection() { QLabel* valuesModeLabel = new QLabel("Data Mode"); m_colorBarDataModeComboBox = new EnumComboBoxTemplate(this); QObject::connect(m_colorBarDataModeComboBox, SIGNAL(itemActivated()), this, SLOT(colorBarItemActivated())); m_colorBarDataModeComboBox->setup(); m_colorBarDataModeComboBox->getWidget()->setToolTip("Format of numbers above color bar\n" " DATA: Numbers show mapping of data to the colobar\n" " PERCENTILE: Numbers show data percentiles\n" " SIGN ONLY: Text above colorbar indicates sign of data"); m_colorBarNumericFormatModeLabel = new QLabel("Numeric Format"); m_colorBarNumericFormatModeComboBox = new EnumComboBoxTemplate(this); QObject::connect(m_colorBarNumericFormatModeComboBox, SIGNAL(itemActivated()), this, SLOT(colorBarItemActivated())); m_colorBarNumericFormatModeComboBox->setup(); m_colorBarNumericFormatModeComboBox->getWidget()->setToolTip("Format of numbers above color bar\n" " AUTO: Format (decimal or scientific notation)\n" " and precision digits are automatically\n" " generated by examination of the data\n" " DECIMAL: Format as decimal number with the\n" " selected precision\n" " SCIENTIFIC: Format as scientific notation\n" " with the selected precision in the\n" " significand"); m_colorBarDecimalsLabel = new QLabel("Dec/Sci Decimals"); //Precision Digits"); m_colorBarDecimalsSpinBox = new QSpinBox(); m_colorBarDecimalsSpinBox->setMinimum(0); m_colorBarDecimalsSpinBox->setMaximum(100); m_colorBarDecimalsSpinBox->setSingleStep(1); QObject::connect(m_colorBarDecimalsSpinBox, SIGNAL(valueChanged(int)), this, SLOT(applySelections())); m_colorBarDecimalsSpinBox->setToolTip("Number of digits right of the decimal point\n" "in the numbers above the color bar when format\n" "is " + NumericFormatModeEnum::toGuiName(NumericFormatModeEnum::DECIMAL) + " or " + NumericFormatModeEnum::toGuiName(NumericFormatModeEnum::SCIENTIFIC)); m_colorBarNumericSubdivisionsLabel = new QLabel("Subdivisions"); m_colorBarNumericSubdivisionsSpinBox = new QSpinBox(); m_colorBarNumericSubdivisionsSpinBox->setMinimum(0); m_colorBarNumericSubdivisionsSpinBox->setMaximum(100); m_colorBarNumericSubdivisionsSpinBox->setSingleStep(1); QObject::connect(m_colorBarNumericSubdivisionsSpinBox, SIGNAL(valueChanged(int)), this, SLOT(applySelections())); m_colorBarNumericSubdivisionsSpinBox->setToolTip("Adds additional numbers to the negative\n" "and positive regions in the color bar"); m_showTickMarksCheckBox = new QCheckBox("Show Tick Marks"); QObject::connect(m_showTickMarksCheckBox, SIGNAL(clicked(bool)), this, SLOT(applySelections())); QGroupBox* colorBarGroupBox = new QGroupBox("Data Numerics"); QGridLayout* gridLayout = new QGridLayout(colorBarGroupBox); int row = gridLayout->rowCount(); // this->setLayoutSpacingAndMargins(gridLayout); gridLayout->addWidget(valuesModeLabel, row, 0); gridLayout->addWidget(m_colorBarDataModeComboBox->getWidget(), row, 1); row++; gridLayout->addWidget(m_colorBarNumericFormatModeLabel, row, 0); gridLayout->addWidget(m_colorBarNumericFormatModeComboBox->getWidget(), row, 1); row++; gridLayout->addWidget(m_colorBarDecimalsLabel, row, 0); gridLayout->addWidget(m_colorBarDecimalsSpinBox, row, 1); row++; gridLayout->addWidget(m_colorBarNumericSubdivisionsLabel, row, 0); gridLayout->addWidget(m_colorBarNumericSubdivisionsSpinBox, row, 1); row++; gridLayout->addWidget(m_showTickMarksCheckBox, row, 0, 1, 2, Qt::AlignLeft); colorBarGroupBox->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); return colorBarGroupBox; } /** * Gets called when a color bar item is selected. */ void MapSettingsColorBarWidget::colorBarItemActivated() { applySelections(); updateColorBarAttributes(); } /** * Update the colorbar attributes section. */ void MapSettingsColorBarWidget::updateColorBarAttributes() { if (m_paletteColorMapping != NULL) { bool numericForatComboBoxEnabled = true; bool precisionDigitsSpinBoxEnabled = true; bool subdivisionsSpinBoxEnabled = true; const PaletteColorBarValuesModeEnum::Enum valuesMode = m_paletteColorMapping->getColorBarValuesMode(); switch (valuesMode) { case PaletteColorBarValuesModeEnum::DATA: break; case PaletteColorBarValuesModeEnum::PERCENTILE: break; case PaletteColorBarValuesModeEnum::SIGN_ONLY: numericForatComboBoxEnabled = false; precisionDigitsSpinBoxEnabled = false; subdivisionsSpinBoxEnabled = false; break; } const NumericFormatModeEnum::Enum numericFormat = m_paletteColorMapping->getNumericFormatMode(); switch (numericFormat) { case NumericFormatModeEnum::AUTO: precisionDigitsSpinBoxEnabled = false; break; case NumericFormatModeEnum::DECIMAL: break; case NumericFormatModeEnum::SCIENTIFIC: break; } m_colorBarDataModeComboBox->setSelectedItem(valuesMode); m_colorBarNumericFormatModeLabel->setEnabled(numericForatComboBoxEnabled); m_colorBarNumericFormatModeComboBox->setSelectedItem(numericFormat); m_colorBarNumericFormatModeComboBox->getWidget()->setEnabled(numericForatComboBoxEnabled); m_colorBarDecimalsLabel->setEnabled(precisionDigitsSpinBoxEnabled); m_colorBarDecimalsSpinBox->blockSignals(true); m_colorBarDecimalsSpinBox->setValue(m_paletteColorMapping->getPrecisionDigits()); m_colorBarDecimalsSpinBox->blockSignals(false); m_colorBarDecimalsSpinBox->setEnabled(precisionDigitsSpinBoxEnabled); m_colorBarNumericSubdivisionsLabel->setEnabled(subdivisionsSpinBoxEnabled); m_colorBarNumericSubdivisionsSpinBox->blockSignals(true); m_colorBarNumericSubdivisionsSpinBox->setValue(m_paletteColorMapping->getNumericSubdivisionCount()); m_colorBarNumericSubdivisionsSpinBox->blockSignals(false); m_colorBarNumericSubdivisionsSpinBox->setEnabled(subdivisionsSpinBoxEnabled); m_showTickMarksCheckBox->setChecked(m_paletteColorMapping->isShowTickMarksSelected()); } } connectome-workbench-1.2.3+git41-gc4c6c90/src/GuiQt/MapSettingsColorBarWidget.h000066400000000000000000000062201300200146000267740ustar00rootroot00000000000000#ifndef __MAP_SETTINGS_COLOR_BAR_WIDGET_H__ #define __MAP_SETTINGS_COLOR_BAR_WIDGET_H__ /*LICENSE_START*/ /* * Copyright (C) 2015 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 QCheckBox; class QLabel; class QSpinBox; namespace caret { class AnnotationColorBar; class EnumComboBoxTemplate; class PaletteColorMapping; class Overlay; class MapSettingsColorBarWidget : public QWidget { Q_OBJECT public: MapSettingsColorBarWidget(QWidget* parent = 0); virtual ~MapSettingsColorBarWidget(); // void updateContent(Overlay* overlay); void updateContent(AnnotationColorBar* annotationColorBar, PaletteColorMapping* paletteColorMapping); // ADD_NEW_METHODS_HERE private slots: void applySelections(); void annotationColorBarPositionModeEnumComboBoxItemActivated(); void annotationCoordinateSpaceEnumComboBoxItemActivated(); void colorBarItemActivated(); private: MapSettingsColorBarWidget(const MapSettingsColorBarWidget&); MapSettingsColorBarWidget& operator=(const MapSettingsColorBarWidget&); QWidget* createDataNumericsSection(); QWidget* createLocationPositionSection(); void updateContentPrivate(); void updateColorBarAttributes(); AnnotationColorBar* m_colorBar; PaletteColorMapping* m_paletteColorMapping; EnumComboBoxTemplate* m_annotationColorBarPositionModeEnumComboBox; EnumComboBoxTemplate* m_annotationCoordinateSpaceEnumComboBox; EnumComboBoxTemplate* m_colorBarDataModeComboBox; QLabel* m_colorBarNumericFormatModeLabel; EnumComboBoxTemplate* m_colorBarNumericFormatModeComboBox; QLabel* m_colorBarDecimalsLabel; QSpinBox* m_colorBarDecimalsSpinBox; QLabel* m_colorBarNumericSubdivisionsLabel; QSpinBox* m_colorBarNumericSubdivisionsSpinBox; QCheckBox* m_showTickMarksCheckBox; // ADD_NEW_MEMBERS_HERE }; #ifdef __MAP_SETTINGS_COLOR_BAR_WIDGET_DECLARE__ // #endif // __MAP_SETTINGS_COLOR_BAR_WIDGET_DECLARE__ } // namespace #endif //__MAP_SETTINGS_COLOR_BAR_WIDGET_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/GuiQt/MapSettingsFiberTrajectoryWidget.cxx000066400000000000000000000416071300200146000307520ustar00rootroot00000000000000 /*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 __MAP_SETTINGS_FIBER_TRAJECTORY_WIDGET_DECLARE__ #include "MapSettingsFiberTrajectoryWidget.h" #undef __MAP_SETTINGS_FIBER_TRAJECTORY_WIDGET_DECLARE__ #include #include #include #include #include #include #include #include #include #include "CiftiFiberTrajectoryFile.h" #include "EventManager.h" #include "EventGraphicsUpdateAllWindows.h" #include "EventUserInterfaceUpdate.h" #include "FiberTrajectoryColorModel.h" #include "FiberTrajectoryMapProperties.h" #include "GuiManager.h" #include "WuQFactory.h" #include "WuQtUtilities.h" using namespace caret; /** * \class caret::MapSettingsFiberTrajectoryWidget * \brief View/Controller for fiber trajectories * \ingroup GuiQt */ /** * Constructor. * @param browserWindowIndex * Index of browser window in which this view controller is displayed. * @param parent * Parent of this object. */ MapSettingsFiberTrajectoryWidget::MapSettingsFiberTrajectoryWidget(QWidget* parent) : QWidget(parent) { m_updateInProgress = true; QWidget* attributesWidget = createAttributesWidget(); QWidget* displayModeWidget = createDisplayModeWidget(); QWidget* dataMappingWidget = createDataMappingWidget(); QVBoxLayout* layout = new QVBoxLayout(this); layout->addWidget(attributesWidget, 0, Qt::AlignLeft); layout->addWidget(displayModeWidget, 0, Qt::AlignLeft); layout->addWidget(dataMappingWidget, 0, Qt::AlignLeft); layout->addStretch(); setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); } /** * Destructor. */ MapSettingsFiberTrajectoryWidget::~MapSettingsFiberTrajectoryWidget() { } QWidget* MapSettingsFiberTrajectoryWidget::createAttributesWidget() { QLabel* colorLabel = new QLabel("Coloring: "); m_colorSelectionComboBox = WuQFactory::newComboBoxSignalInt(this, SLOT(processAttributesChanges())); QGroupBox* attributesGroupBox = new QGroupBox("Attribues"); QGridLayout* attributesGridLayout = new QGridLayout(attributesGroupBox); int row = 0; attributesGridLayout->addWidget(colorLabel, row, 0); attributesGridLayout->addWidget(m_colorSelectionComboBox, row, 1); return attributesGroupBox; } /** * @return Create and return the display mode widget. */ QWidget* MapSettingsFiberTrajectoryWidget::createDisplayModeWidget() { m_displayModeButtonGroup = new QButtonGroup(this); QObject::connect(m_displayModeButtonGroup, SIGNAL(buttonClicked(int)), this, SLOT(processAttributesChanges())); std::vector displayModes; FiberTrajectoryDisplayModeEnum::getAllEnums(displayModes); const int32_t numDisplayModes = static_cast(displayModes.size()); for (int32_t i = 0; i < numDisplayModes; i++) { const FiberTrajectoryDisplayModeEnum::Enum mode = displayModes[i]; QRadioButton* radioButton = new QRadioButton(FiberTrajectoryDisplayModeEnum::toGuiName(mode)); m_displayModeButtonGroup->addButton(radioButton, i); m_displayModeRadioButtons.push_back(radioButton); m_displayModeRadioButtonData.push_back(mode); } QGroupBox* modeGroupBox = new QGroupBox("Display Mode"); QVBoxLayout* modeGroupLayout = new QVBoxLayout(modeGroupBox); for (int32_t i = 0; i < numDisplayModes; i++) { modeGroupLayout->addWidget(m_displayModeRadioButtons[i]); } return modeGroupBox; } /** * @return Create and return the data mapping widget. */ QWidget* MapSettingsFiberTrajectoryWidget::createDataMappingWidget() { const int spinBoxWidth = 85; m_proportionStreamlineSpinBox = WuQFactory::newDoubleSpinBox(); m_proportionStreamlineSpinBox->setRange(0, std::numeric_limits::max()); m_proportionStreamlineSpinBox->setSingleStep(5); m_proportionStreamlineSpinBox->setFixedWidth(spinBoxWidth); m_proportionStreamlineSpinBox->setDecimals(3); m_proportionStreamlineSpinBox->setToolTip("A fiber is displayed only if the total number of its\n" "streamlines is greater than or equal to this value"); QObject::connect(m_proportionStreamlineSpinBox, SIGNAL(valueChanged(double)), this, SLOT(processAttributesChanges())); m_proportionMinimumSpinBox = WuQFactory::newDoubleSpinBox(); m_proportionMinimumSpinBox->setRange(0.0, 1.0); m_proportionMinimumSpinBox->setDecimals(3); m_proportionMinimumSpinBox->setSingleStep(0.05); m_proportionMinimumSpinBox->setFixedWidth(spinBoxWidth); m_proportionMinimumSpinBox->setToolTip("If the proportion for an axis is less than or equal\n" "to this value, the opacity will be zero (clear)"); QObject::connect(m_proportionMinimumSpinBox, SIGNAL(valueChanged(double)), this, SLOT(processAttributesChanges())); m_proportionMaximumSpinBox = WuQFactory::newDoubleSpinBox(); m_proportionMaximumSpinBox->setRange(0.0, 1.0); m_proportionMaximumSpinBox->setDecimals(3); m_proportionMaximumSpinBox->setSingleStep(0.05); m_proportionMaximumSpinBox->setFixedWidth(spinBoxWidth); m_proportionMaximumSpinBox->setToolTip("If the proportion for an axis is greater than or equal\n" "to this value, the opacity will be one (opaque)"); QObject::connect(m_proportionMaximumSpinBox, SIGNAL(valueChanged(double)), this, SLOT(processAttributesChanges())); m_countStreamlineSpinBox = WuQFactory::newDoubleSpinBox(); m_countStreamlineSpinBox->setRange(0, std::numeric_limits::max()); m_countStreamlineSpinBox->setDecimals(3); m_countStreamlineSpinBox->setSingleStep(5); m_countStreamlineSpinBox->setFixedWidth(spinBoxWidth); m_countStreamlineSpinBox->setToolTip("A fiber is displayed only if the total number of its\n" "streamlines is greater than or equal to this value"); QObject::connect(m_countStreamlineSpinBox, SIGNAL(valueChanged(double)), this, SLOT(processAttributesChanges())); m_countMinimumSpinBox = WuQFactory::newDoubleSpinBox(); m_countMinimumSpinBox->setRange(0, std::numeric_limits::max()); m_countMinimumSpinBox->setDecimals(3); m_countMinimumSpinBox->setSingleStep(5); m_countMinimumSpinBox->setFixedWidth(spinBoxWidth); m_countMinimumSpinBox->setToolTip("If the number of fibers for an axis is less than or equal\n" "to this value, the opacity will be zero (clear)"); QObject::connect(m_countMinimumSpinBox, SIGNAL(valueChanged(double)), this, SLOT(processAttributesChanges())); m_countMaximumSpinBox = WuQFactory::newDoubleSpinBox(); m_countMaximumSpinBox->setRange(0, std::numeric_limits::max()); m_countMaximumSpinBox->setDecimals(3); m_countMaximumSpinBox->setSingleStep(5); m_countMaximumSpinBox->setFixedWidth(spinBoxWidth); m_countMaximumSpinBox->setToolTip("If the number of fibers for an axis is greater than or equal\n" "to this value, the opacity will be one (opaque)"); QObject::connect(m_countMaximumSpinBox, SIGNAL(valueChanged(double)), this, SLOT(processAttributesChanges())); m_distanceStreamlineSpinBox = WuQFactory::newDoubleSpinBox(); m_distanceStreamlineSpinBox->setRange(0, std::numeric_limits::max()); m_distanceStreamlineSpinBox->setDecimals(3); m_distanceStreamlineSpinBox->setSingleStep(5); m_distanceStreamlineSpinBox->setFixedWidth(spinBoxWidth); m_distanceStreamlineSpinBox->setToolTip("A fiber is displayed only if the total number of its\n" "streamlines is greater than or equal to this value"); QObject::connect(m_distanceStreamlineSpinBox, SIGNAL(valueChanged(double)), this, SLOT(processAttributesChanges())); m_distanceMinimumSpinBox = WuQFactory::newDoubleSpinBox(); m_distanceMinimumSpinBox->setRange(0, std::numeric_limits::max()); m_distanceMinimumSpinBox->setDecimals(3); m_distanceMinimumSpinBox->setSingleStep(5); m_distanceMinimumSpinBox->setFixedWidth(spinBoxWidth); m_distanceMinimumSpinBox->setToolTip("If count times distance for an axis is less than or equal\n" "to this value, the opacity will be zero (clear)"); QObject::connect(m_distanceMinimumSpinBox, SIGNAL(valueChanged(double)), this, SLOT(processAttributesChanges())); m_distanceMaximumSpinBox = WuQFactory::newDoubleSpinBox(); m_distanceMaximumSpinBox->setRange(0, std::numeric_limits::max()); m_distanceMaximumSpinBox->setDecimals(3); m_distanceMaximumSpinBox->setSingleStep(5); m_distanceMaximumSpinBox->setFixedWidth(spinBoxWidth); m_distanceMaximumSpinBox->setToolTip("If the count times distance for an axis is greater than or equal\n" "to this value, the opacity will be one (opaque)"); QObject::connect(m_distanceMaximumSpinBox, SIGNAL(valueChanged(double)), this, SLOT(processAttributesChanges())); QGroupBox* dataMappingGroupBox = new QGroupBox("Data Mapping"); QGridLayout* dataMappingLayout = new QGridLayout(dataMappingGroupBox); WuQtUtilities::setLayoutSpacingAndMargins(dataMappingLayout, 4, 2); int row = dataMappingLayout->rowCount(); int columnCounter = 0; const int COLUMN_LABELS = columnCounter++; const int COLUMN_THRESHOLD = columnCounter++; const int COLUMN_MINIMUM = columnCounter++; const int COLUMN_MAXIMUM = columnCounter++; dataMappingLayout->addWidget(new QLabel("Display
Mode"), row, COLUMN_LABELS, Qt::AlignLeft); dataMappingLayout->addWidget(new QLabel("Streamline
Threshold"), row, COLUMN_THRESHOLD, Qt::AlignLeft); dataMappingLayout->addWidget(new QLabel("Map to
Clear"), row, COLUMN_MINIMUM, Qt::AlignLeft); dataMappingLayout->addWidget(new QLabel("Map to
Opaque"), row, COLUMN_MAXIMUM, Qt::AlignLeft); row++; dataMappingLayout->addWidget(new QLabel("Absolute"), row, COLUMN_LABELS, Qt::AlignHCenter); dataMappingLayout->addWidget(m_countStreamlineSpinBox, row, COLUMN_THRESHOLD, Qt::AlignHCenter); dataMappingLayout->addWidget(m_countMinimumSpinBox, row, COLUMN_MINIMUM, Qt::AlignHCenter); dataMappingLayout->addWidget(m_countMaximumSpinBox, row, COLUMN_MAXIMUM, Qt::AlignHCenter); row++; dataMappingLayout->addWidget(new QLabel("Distance"), row, COLUMN_LABELS, Qt::AlignHCenter); dataMappingLayout->addWidget(m_distanceStreamlineSpinBox, row, COLUMN_THRESHOLD, Qt::AlignHCenter); dataMappingLayout->addWidget(m_distanceMinimumSpinBox, row, COLUMN_MINIMUM, Qt::AlignHCenter); dataMappingLayout->addWidget(m_distanceMaximumSpinBox, row, COLUMN_MAXIMUM, Qt::AlignHCenter); row++; dataMappingLayout->addWidget(new QLabel("Proportion"), row, COLUMN_LABELS, Qt::AlignHCenter); dataMappingLayout->addWidget(m_proportionStreamlineSpinBox, row, COLUMN_THRESHOLD, Qt::AlignHCenter); dataMappingLayout->addWidget(m_proportionMinimumSpinBox, row, COLUMN_MINIMUM, Qt::AlignHCenter); dataMappingLayout->addWidget(m_proportionMaximumSpinBox, row, COLUMN_MAXIMUM, Qt::AlignHCenter); row++; return dataMappingGroupBox; } /** * Called when a widget on the attributes page has * its value changed. */ void MapSettingsFiberTrajectoryWidget::processAttributesChanges() { if (m_updateInProgress) { return; } if (m_fiberTrajectoryFile == NULL) { return; } FiberTrajectoryMapProperties* ftmp = m_fiberTrajectoryFile->getFiberTrajectoryMapProperties(); const int32_t selectedColorIndex = m_colorSelectionComboBox->currentIndex(); if (selectedColorIndex >= 0) { void* ptr = m_colorSelectionComboBox->itemData(selectedColorIndex, Qt::UserRole).value(); const FiberTrajectoryColorModel::Item* item = (FiberTrajectoryColorModel::Item*)ptr; ftmp->getFiberTrajectoryColorModel()->setSelectedItem(item); } const int32_t selectedModeRadioButtonIndex = m_displayModeButtonGroup->checkedId(); const FiberTrajectoryDisplayModeEnum::Enum displayMode = m_displayModeRadioButtonData[selectedModeRadioButtonIndex]; ftmp->setDisplayMode(displayMode); ftmp->setProportionStreamline(m_proportionStreamlineSpinBox->value());; ftmp->setProportionMinimumOpacity(m_proportionMinimumSpinBox->value()); ftmp->setProportionMaximumOpacity(m_proportionMaximumSpinBox->value()); ftmp->setCountStreamline(m_countStreamlineSpinBox->value()); ftmp->setCountMaximumOpacity(m_countMaximumSpinBox->value()); ftmp->setCountMinimumOpacity(m_countMinimumSpinBox->value()); ftmp->setDistanceStreamline(m_distanceStreamlineSpinBox->value()); ftmp->setDistanceMaximumOpacity(m_distanceMaximumSpinBox->value()); ftmp->setDistanceMinimumOpacity(m_distanceMinimumSpinBox->value()); EventManager::get()->sendEvent(EventGraphicsUpdateAllWindows().getPointer()); } /** * Update with the given fiber trajectory file. */ void MapSettingsFiberTrajectoryWidget::updateEditor(CiftiFiberTrajectoryFile* fiberTrajectoryFile) { m_fiberTrajectoryFile = fiberTrajectoryFile; if (m_fiberTrajectoryFile == NULL) { return; } m_updateInProgress = true; FiberTrajectoryMapProperties* ftmp = m_fiberTrajectoryFile->getFiberTrajectoryMapProperties(); FiberTrajectoryColorModel* colorModel = ftmp->getFiberTrajectoryColorModel(); std::vector colorItems = colorModel->getValidItems(); const FiberTrajectoryColorModel::Item* selectedItem = colorModel->getSelectedItem(); const int32_t numColorItems = static_cast(colorItems.size()); m_colorSelectionComboBox->blockSignals(true); m_colorSelectionComboBox->clear(); int32_t defaultIndex = 0; for (int32_t i = 0; i < numColorItems; i++) { FiberTrajectoryColorModel::Item* item = colorItems[i]; if (item == selectedItem) { defaultIndex = i; } m_colorSelectionComboBox->addItem(item->getName(), qVariantFromValue((void*)item)); } if ((defaultIndex >= 0) && (defaultIndex < m_colorSelectionComboBox->count())) { m_colorSelectionComboBox->setCurrentIndex(defaultIndex); } m_colorSelectionComboBox->blockSignals(false); const FiberTrajectoryDisplayModeEnum::Enum selectedDisplayMode = ftmp->getDisplayMode(); const int32_t numDisplayModeRadioButtons = m_displayModeButtonGroup->buttons().size(); for (int32_t i = 0; i < numDisplayModeRadioButtons; i++) { if (m_displayModeRadioButtonData[i] == selectedDisplayMode) { m_displayModeRadioButtons[i]->setChecked(true); break; } } /* * Update the attributes */ m_proportionStreamlineSpinBox->setValue(ftmp->getProportionStreamline()); m_proportionMaximumSpinBox->setValue(ftmp->getProportionMaximumOpacity()); m_proportionMinimumSpinBox->setValue(ftmp->getProportionMinimumOpacity()); m_countStreamlineSpinBox->setValue(ftmp->getCountStreamline()); m_countMaximumSpinBox->setValue(ftmp->getCountMaximumOpacity()); m_countMinimumSpinBox->setValue(ftmp->getCountMinimumOpacity()); m_distanceStreamlineSpinBox->setValue(ftmp->getDistanceStreamline()); m_distanceMaximumSpinBox->setValue(ftmp->getDistanceMaximumOpacity()); m_distanceMinimumSpinBox->setValue(ftmp->getDistanceMinimumOpacity()); m_updateInProgress = false; } /** * Update the fiber trajectory widget. */ void MapSettingsFiberTrajectoryWidget::updateWidget() { updateEditor(m_fiberTrajectoryFile); } connectome-workbench-1.2.3+git41-gc4c6c90/src/GuiQt/MapSettingsFiberTrajectoryWidget.h000066400000000000000000000061141300200146000303710ustar00rootroot00000000000000#ifndef __MAP_SETTINGS_FIBER_TRAJECTORY_WIDGET__H_ #define __MAP_SETTINGS_FIBER_TRAJECTORY_WIDGET__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 "FiberTrajectoryDisplayModeEnum.h" class QButtonGroup; class QComboBox; class QDoubleSpinBox; class QRadioButton; namespace caret { class CaretMappableDataFile; class CiftiFiberTrajectoryFile; class MapSettingsFiberTrajectoryWidget : public QWidget { Q_OBJECT public: MapSettingsFiberTrajectoryWidget(QWidget* parent = 0); virtual ~MapSettingsFiberTrajectoryWidget(); void updateEditor(CiftiFiberTrajectoryFile* fiberTrajectoryFile); void updateWidget(); // ADD_NEW_METHODS_HERE private slots: void processAttributesChanges(); private: MapSettingsFiberTrajectoryWidget(const MapSettingsFiberTrajectoryWidget&); MapSettingsFiberTrajectoryWidget& operator=(const MapSettingsFiberTrajectoryWidget&); QWidget* createAttributesWidget(); QWidget* createDisplayModeWidget(); QWidget* createDataMappingWidget(); CiftiFiberTrajectoryFile* m_fiberTrajectoryFile; QComboBox* m_colorSelectionComboBox; std::vector m_displayModeRadioButtons; std::vector m_displayModeRadioButtonData; QButtonGroup* m_displayModeButtonGroup; QDoubleSpinBox* m_proportionStreamlineSpinBox; QDoubleSpinBox* m_proportionMinimumSpinBox; QDoubleSpinBox* m_proportionMaximumSpinBox; QDoubleSpinBox* m_countStreamlineSpinBox; QDoubleSpinBox* m_countMinimumSpinBox; QDoubleSpinBox* m_countMaximumSpinBox; QDoubleSpinBox* m_distanceStreamlineSpinBox; QDoubleSpinBox* m_distanceMinimumSpinBox; QDoubleSpinBox* m_distanceMaximumSpinBox; bool m_updateInProgress; // ADD_NEW_MEMBERS_HERE }; #ifdef __MAP_SETTINGS_FIBER_TRAJECTORY_WIDGET_DECLARE__ #endif // __MAP_SETTINGS_FIBER_TRAJECTORY_WIDGET_DECLARE__ } // namespace #endif //__MAP_SETTINGS_FIBER_TRAJECTORY_WIDGET__H_ connectome-workbench-1.2.3+git41-gc4c6c90/src/GuiQt/MapSettingsLabelsWidget.cxx000066400000000000000000000167471300200146000270650ustar00rootroot00000000000000 /*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 __MAP_SETTINGS_LABELS_WIDGET_DECLARE__ #include "MapSettingsLabelsWidget.h" #undef __MAP_SETTINGS_LABELS_WIDGET_DECLARE__ #include #include #include #include #include #include "CaretAssert.h" #include "CaretColorEnumComboBox.h" #include "CaretMappableDataFile.h" #include "EnumComboBoxTemplate.h" #include "EventGraphicsUpdateAllWindows.h" #include "EventManager.h" #include "EventSurfaceColoringInvalidate.h" #include "GiftiLabelTableEditor.h" #include "LabelDrawingTypeEnum.h" #include "LabelDrawingProperties.h" #include "Overlay.h" #include "WuQtUtilities.h" using namespace caret; /** * \class caret::MapSettingsLabelsWidget * \brief Labels page for overlay and map settings. * \ingroup GuiQt */ /** * Constructor. */ MapSettingsLabelsWidget::MapSettingsLabelsWidget(QWidget* parent) : QWidget(parent) { QLabel* drawingTypeLabel = new QLabel("Drawing Type"); m_drawingTypeComboBox = new EnumComboBoxTemplate(this); m_drawingTypeComboBox->setup(); QObject::connect(m_drawingTypeComboBox, SIGNAL(itemActivated()), this, SLOT(applySelections())); QLabel* outlineColorLabel = new QLabel("Outline Color"); m_outlineColorComboBox = new CaretColorEnumComboBox(this); QObject::connect(m_outlineColorComboBox, SIGNAL(colorSelected(const CaretColorEnum::Enum)), this, SLOT(applySelections())); m_drawMedialWallFilledCheckBox = new QCheckBox("Draw Surface Medial Wall Filled"); QObject::connect(m_drawMedialWallFilledCheckBox, SIGNAL(clicked(bool)), this, SLOT(applySelections())); m_drawMedialWallFilledCheckBox->setToolTip(WuQtUtilities::createWordWrappedToolTipText("When an outline type drawing is selected and if the " "selected map contains vertices identified as the " "'medial wall', checking this option will fill the medial " "wall vertices.")); QPushButton* editLabelsPushButton = new QPushButton("Edit Map's Labels"); QObject::connect(editLabelsPushButton, SIGNAL(clicked()), this, SLOT(editLabelTablePushButtonClicked())); editLabelsPushButton->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); QWidget* gridWidget = new QWidget(); QGridLayout* gridLayout = new QGridLayout(gridWidget); gridLayout->addWidget(drawingTypeLabel, 0, 0); gridLayout->addWidget(m_drawingTypeComboBox->getWidget(), 0, 1); gridLayout->addWidget(outlineColorLabel, 1, 0); gridLayout->addWidget(m_outlineColorComboBox->getWidget(), 1, 1); gridLayout->addWidget(m_drawMedialWallFilledCheckBox, 2, 0, 1, 2, Qt::AlignLeft); gridLayout->addWidget(editLabelsPushButton, 3, 0, 1, 2, Qt::AlignLeft); gridWidget->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); QVBoxLayout* layout = new QVBoxLayout(this); layout->addWidget(gridWidget); layout->addStretch(); setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); } /** * Destructor. */ MapSettingsLabelsWidget::~MapSettingsLabelsWidget() { } /** * Update the content of widget. * * @param overlay * Overlay for display in this widget. */ void MapSettingsLabelsWidget::updateContent(Overlay* overlay) { m_overlay = overlay; bool enableWidget = false; if (m_overlay != NULL) { CaretMappableDataFile* mapFile = NULL; int32_t mapIndex = -1; m_overlay->getSelectionData(mapFile, mapIndex); if (mapFile != NULL) { if (mapFile->isMappedWithLabelTable()) { const LabelDrawingProperties* labelProps = mapFile->getLabelDrawingProperties(); m_drawingTypeComboBox->setSelectedItem(labelProps->getDrawingType()); m_outlineColorComboBox->setSelectedColor(labelProps->getOutlineColor()); m_drawMedialWallFilledCheckBox->setChecked(labelProps->isDrawMedialWallFilled()); m_drawMedialWallFilledCheckBox->setEnabled(mapFile->isMedialWallLabelInMapLabelTable(mapIndex)); enableWidget = true; } } } setEnabled(enableWidget); } /** * Called when a control is changed. */ void MapSettingsLabelsWidget::applySelections() { if (m_overlay != NULL) { CaretMappableDataFile* mapFile = NULL; int32_t mapIndex = -1; m_overlay->getSelectionData(mapFile, mapIndex); if (mapFile != NULL) { if (mapFile->isMappedWithLabelTable()) { const LabelDrawingTypeEnum::Enum drawType = m_drawingTypeComboBox->getSelectedItem(); const CaretColorEnum::Enum outlineColor = m_outlineColorComboBox->getSelectedColor(); LabelDrawingProperties* labelProps = mapFile->getLabelDrawingProperties(); labelProps->setDrawingType(drawType); labelProps->setOutlineColor(outlineColor); labelProps->setDrawMedialWallFilled(m_drawMedialWallFilledCheckBox->isChecked()); EventManager::get()->sendEvent(EventSurfaceColoringInvalidate().getPointer()); EventManager::get()->sendEvent(EventGraphicsUpdateAllWindows().getPointer()); } } } } /** * Called when the edit label table button is clicked. */ void MapSettingsLabelsWidget::editLabelTablePushButtonClicked() { if (m_overlay != NULL) { CaretMappableDataFile* mapFile = NULL; int32_t mapIndex = -1; m_overlay->getSelectionData(mapFile, mapIndex); if (mapFile != NULL) { if (mapFile->isMappedWithLabelTable()) { if (mapIndex >= 0) { GiftiLabelTableEditor labelTableEditor(mapFile, mapIndex, "Edit Labels", GiftiLabelTableEditor::OPTION_ADD_APPLY_BUTTON, this); labelTableEditor.exec(); } } } } } connectome-workbench-1.2.3+git41-gc4c6c90/src/GuiQt/MapSettingsLabelsWidget.h000066400000000000000000000041061300200146000264740ustar00rootroot00000000000000#ifndef __MAP_SETTINGS_LABELS_WIDGET_H__ #define __MAP_SETTINGS_LABELS_WIDGET_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 class QCheckBox; namespace caret { class CaretColorEnumComboBox; class EnumComboBoxTemplate; class Overlay; class MapSettingsLabelsWidget : public QWidget { Q_OBJECT public: MapSettingsLabelsWidget(QWidget* parent = 0); virtual ~MapSettingsLabelsWidget(); void updateContent(Overlay* overlay); private slots: void applySelections(); void editLabelTablePushButtonClicked(); // ADD_NEW_METHODS_HERE private: MapSettingsLabelsWidget(const MapSettingsLabelsWidget&); MapSettingsLabelsWidget& operator=(const MapSettingsLabelsWidget&); // ADD_NEW_MEMBERS_HERE Overlay* m_overlay; CaretColorEnumComboBox* m_outlineColorComboBox; EnumComboBoxTemplate* m_drawingTypeComboBox; QCheckBox* m_drawMedialWallFilledCheckBox; }; #ifdef __MAP_SETTINGS_LABELS_WIDGET_DECLARE__ // #endif // __MAP_SETTINGS_LABELS_WIDGET_DECLARE__ } // namespace #endif //__MAP_SETTINGS_LABELS_WIDGET_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/GuiQt/MapSettingsLayerWidget.cxx000066400000000000000000000111631300200146000267220ustar00rootroot00000000000000 /*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 __MAP_SETTINGS_LAYER_WIDGET_DECLARE__ #include "MapSettingsLayerWidget.h" #undef __MAP_SETTINGS_LAYER_WIDGET_DECLARE__ #include #include #include #include #include #include "CaretMappableDataFile.h" #include "EnumComboBoxTemplate.h" #include "EventGraphicsUpdateAllWindows.h" #include "EventManager.h" #include "EventSurfaceColoringInvalidate.h" #include "Overlay.h" #include "VolumeMappableInterface.h" #include "WuQtUtilities.h" using namespace caret; /** * \class caret::MapSettingsLayerWidget * \brief Contains user-interface components for overlay parameters. * \ingroup GuiQt */ /** * Constructor. */ MapSettingsLayerWidget::MapSettingsLayerWidget(QWidget* parent) : QWidget(parent) { QLabel* wholeBrainVoxelDrawingModeLabel = new QLabel("Voxel Drawing Mode"); m_wholeBrainVoxelDrawingModeComboBox = new EnumComboBoxTemplate(this); m_wholeBrainVoxelDrawingModeComboBox->setup(); QObject::connect(m_wholeBrainVoxelDrawingModeComboBox, SIGNAL(itemActivated()), this, SLOT(applySelections())); const AString warningText("Drawing voxels in Whole Brain view with 3D cubes can be very slow " "and should only be used with a volume that displays a limited number " "of voxels. One example is a label volume that identifies subcortical " "structures. Another example is a functional volume that is thresholded " "so that a small number of voxels are displayed. 3D cubes should never " "be used with anatomical volumes."); // QLabel* voxelWarningLabel = new QLabel(WuQtUtilities::createWordWrappedToolTipText(warningText)); QLabel* voxelWarningLabel = new QLabel(warningText); voxelWarningLabel->setWordWrap(true); QGroupBox* wholeBraingroupBox = new QGroupBox("Whole Brain"); wholeBraingroupBox->setFlat(true); QGridLayout* wholeBrainGridLayout = new QGridLayout(wholeBraingroupBox); wholeBrainGridLayout->addWidget(wholeBrainVoxelDrawingModeLabel, 0, 0); wholeBrainGridLayout->addWidget(m_wholeBrainVoxelDrawingModeComboBox->getWidget(), 0, 1); wholeBrainGridLayout->addWidget(voxelWarningLabel, 1, 0, 1, 2); wholeBraingroupBox->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); QVBoxLayout* layout = new QVBoxLayout(this); layout->addWidget(wholeBraingroupBox); layout->addStretch(); setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); } /** * Destructor. */ MapSettingsLayerWidget::~MapSettingsLayerWidget() { } /** * Update the content of widget. * * @param overlay * Overlay for display in this widget. */ void MapSettingsLayerWidget::updateContent(Overlay* overlay) { m_overlay = overlay; bool enableWidget = false; if (m_overlay != NULL) { m_wholeBrainVoxelDrawingModeComboBox->setSelectedItem(m_overlay->getWholeBrainVoxelDrawingMode()); enableWidget = true; } setEnabled(enableWidget); } /** * Called when a control is changed. */ void MapSettingsLayerWidget::applySelections() { const WholeBrainVoxelDrawingMode::Enum wholeBrainVoxelDrawingMode = m_wholeBrainVoxelDrawingModeComboBox->getSelectedItem(); if (m_overlay != NULL) { m_overlay->setWholeBrainVoxelDrawingMode(wholeBrainVoxelDrawingMode); } EventManager::get()->sendEvent(EventSurfaceColoringInvalidate().getPointer()); EventManager::get()->sendEvent(EventGraphicsUpdateAllWindows().getPointer()); } connectome-workbench-1.2.3+git41-gc4c6c90/src/GuiQt/MapSettingsLayerWidget.h000066400000000000000000000036071300200146000263530ustar00rootroot00000000000000#ifndef __MAP_SETTINGS_LAYER_WIDGET_H__ #define __MAP_SETTINGS_LAYER_WIDGET_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 class QPushButton; namespace caret { class EnumComboBoxTemplate; class Overlay; class MapSettingsLayerWidget : public QWidget { Q_OBJECT public: MapSettingsLayerWidget(QWidget* parent = 0); virtual ~MapSettingsLayerWidget(); void updateContent(Overlay* overlay); private slots: void applySelections(); private: MapSettingsLayerWidget(const MapSettingsLayerWidget&); MapSettingsLayerWidget& operator=(const MapSettingsLayerWidget&); public: // ADD_NEW_METHODS_HERE private: // ADD_NEW_MEMBERS_HERE Overlay* m_overlay; EnumComboBoxTemplate* m_wholeBrainVoxelDrawingModeComboBox; }; #ifdef __MAP_SETTINGS_LAYER_WIDGET_DECLARE__ // #endif // __MAP_SETTINGS_LAYER_WIDGET_DECLARE__ } // namespace #endif //__MAP_SETTINGS_LAYER_WIDGET_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/GuiQt/MapSettingsPaletteColorMappingWidget.cxx000066400000000000000000003226121300200146000315630ustar00rootroot00000000000000 /*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 #include #include #include #include #include #include #include #include #define __MAP_SETTINGS_PALETTE_COLOR_MAPPING_WIDGET_DECLARE__ #include "MapSettingsPaletteColorMappingWidget.h" #undef __MAP_SETTINGS_PALETTE_COLOR_MAPPING_WIDGET_DECLARE__ #include "Brain.h" #include "CaretMappableDataFile.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 "MathFunctions.h" #include "NodeAndVoxelColoring.h" #include "NumericTextFormatting.h" #include "Palette.h" #include "PaletteColorMapping.h" #include "PaletteFile.h" #include "VolumeFile.h" #include "WuQDataEntryDialog.h" #include "WuQWidgetObjectGroup.h" #include "WuQDoubleSlider.h" #include "WuQFactory.h" #include "WuQMessageBox.h" #include "WuQtUtilities.h" #include "WuQwtPlot.h" #include "qwt_plot_curve.h" #include "qwt_plot_histogram.h" #include "qwt_plot_intervalcurve.h" #include "qwt_plot_layout.h" #include "PlotMagnifier.h" #include "PlotPanner.h" using namespace caret; /* The QSlider uses integer for min/max so use max-int / 4 (approximately) */ static const int32_t BIG_NUMBER = 500000000; /** * \class caret::MapSettingsScalarDataEditorDialog * \brief Dialog for editing scalar data map settings * \ingroup GuiQt * * Presents controls for setting palettes, and thresholding used to color * scalar data. */ /** * Constructor for editing a palette selection. * * @param parent * Parent widget on which this dialog is displayed. */ 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; this->paletteWidgetGroup = new WuQWidgetObjectGroup(this); this->thresholdWidgetGroup = new WuQWidgetObjectGroup(this); this->paletteColorMapping = NULL; QWidget* histogramWidget = this->createHistogramSection(); QWidget* histogramControlWidget = this->createHistogramControlSection(); QWidget* dataOptionsWidget = this->createDataOptionsSection(); QWidget* normalizationWidget = this->createNormalizationControlSection(); QWidget* paletteWidget = this->createPaletteSection(); QWidget* thresholdWidget = this->createThresholdSection(); 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(); 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(); QHBoxLayout* layout = new QHBoxLayout(this); this->setLayoutSpacingAndMargins(layout); layout->addWidget(leftWidget, 0); layout->addWidget(rightWidget, 100); setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); } /** * Destructor. */ MapSettingsPaletteColorMappingWidget::~MapSettingsPaletteColorMappingWidget() { } /** * May be called to update the widget's content. */ void MapSettingsPaletteColorMappingWidget::updateWidget() { this->updateEditorInternal(this->caretMappableDataFile, this->mapFileIndex); } /** * Called when the threshold type is changed. */ void MapSettingsPaletteColorMappingWidget::thresholdTypeChanged(int indx) { PaletteThresholdTypeEnum::Enum paletteThresholdType = static_cast(this->thresholdTypeComboBox->itemData(indx).toInt()); this->paletteColorMapping->setThresholdType(paletteThresholdType); this->updateEditorInternal(this->caretMappableDataFile, this->mapFileIndex); this->applySelections(); } /** * Update the minimum and maximum values for the thresholding controls. */ void MapSettingsPaletteColorMappingWidget::updateThresholdControlsMinimumMaximumRangeValues() { if (paletteColorMapping != NULL) { if (this->caretMappableDataFile != NULL) { if ((this->mapFileIndex >= 0) && (this->mapFileIndex < this->caretMappableDataFile->getNumberOfMaps())) { const PaletteThresholdRangeModeEnum::Enum thresholdRangeMode = paletteColorMapping->getThresholdRangeMode(); this->thresholdRangeModeComboBox->setSelectedItem(thresholdRangeMode); float maxValue = BIG_NUMBER; float minValue = -maxValue; float stepMax = maxValue; float stepMin = minValue; switch (thresholdRangeMode) { case PaletteThresholdRangeModeEnum::PALETTE_THRESHOLD_RANGE_MODE_FILE: this->caretMappableDataFile->getDataRangeFromAllMaps(minValue, maxValue); stepMin = minValue; stepMax = maxValue; break; case PaletteThresholdRangeModeEnum::PALETTE_THRESHOLD_RANGE_MODE_MAP: { 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; } if (statistics != NULL) { minValue = statistics->getMin(); maxValue = statistics->getMax(); stepMin = minValue; stepMax = maxValue; } } break; case PaletteThresholdRangeModeEnum::PALETTE_THRESHOLD_RANGE_MODE_UNLIMITED: { /* * For unlimited range, use twice the maximum value in the file * Using very large values can cause problems with some * Qt widgets. */ float allMapMinValue = 0.0; float allMapMaxValue = 0.0; this->caretMappableDataFile->getDataRangeFromAllMaps(allMapMinValue, allMapMaxValue); if (allMapMaxValue > allMapMinValue) { const float absMax = std::max(std::fabs(allMapMaxValue), std::fabs(allMapMinValue)); minValue = -absMax * 2.0; maxValue = absMax * 2.0; } stepMin = minValue; stepMax = maxValue; } break; } /* * Set the spin box step value to one percent of * the data's range. */ float stepValue = 1.0; const float diff = stepMax - stepMin; if (diff > 0.0) { stepValue = diff / 100.0; } float lowMin = minValue; float lowMax = maxValue; float highMin = minValue; float highMax = maxValue; if (this->paletteColorMapping->isThresholdNegMinPosMaxLinked()) { const float absMax = std::max(std::fabs(minValue), std::fabs(maxValue)); lowMin = -absMax; lowMax = 0.0; highMin = 0.0; highMax = absMax; } this->thresholdLowSlider->setRange(lowMin, lowMax); this->thresholdHighSlider->setRange(highMin, highMax); /* * Since there are multiple ways for the user to adjust * a threshold (slider or spin box) these controls must * dispaly the same values. In addition, linking the * thresholds requires updating the spin boxes and sliders * for both low and high thresholding. * * The spin box allows the user to hold down one of the * arrow keys to continuously update the data. However, * if any of the spin box's "set" methods are called * while the user holds down the arrow, holding down of * the arrow key will not work. So, when the signal is * emitted for the spin box value being changed, we need * to avoid updating that spin box during that time. */ if (allowUpdateOfThresholdLowSpinBox) { this->thresholdLowSpinBox->setRange(lowMin, lowMax); this->thresholdLowSpinBox->setSingleStep(stepValue); } if (allowUpdateOfThresholdHighSpinBox) { this->thresholdHighSpinBox->setRange(highMin, highMax); this->thresholdHighSpinBox->setSingleStep(stepValue); } } } } } /** * Should be called when a control is changed and * the change requires and update to other controls. */ void MapSettingsPaletteColorMappingWidget::applyAndUpdate() { this->applySelections(); this->updateEditorInternal(this->caretMappableDataFile, this->mapFileIndex); } /** * Update after a threshold value is changed. * * @param lowThreshold * New value for low threshold. * @param highThreshold * New value for high threshold. */ void MapSettingsPaletteColorMappingWidget::updateAfterThresholdValuesChanged(const float lowThreshold, const float highThreshold) { const PaletteThresholdTypeEnum::Enum threshType = this->paletteColorMapping->getThresholdType(); this->paletteColorMapping->setThresholdMinimum(threshType, lowThreshold); this->paletteColorMapping->setThresholdMaximum(threshType, highThreshold); updateThresholdSection(); updateHistogramPlot(); updateColoringAndGraphics(); } /** * Called when low value spin box changed. * @param thresholdLow * New value. */ void MapSettingsPaletteColorMappingWidget::thresholdLowSpinBoxValueChanged(double thresholdLow) { const PaletteThresholdTypeEnum::Enum threshType = this->paletteColorMapping->getThresholdType(); float thresholdHigh = this->paletteColorMapping->getThresholdMaximum(threshType); if (this->paletteColorMapping->isThresholdNegMinPosMaxLinked()) { thresholdHigh = -thresholdLow; } else { if (thresholdLow > thresholdHigh) { thresholdHigh = thresholdLow; } } allowUpdateOfThresholdLowSpinBox = false; updateAfterThresholdValuesChanged(thresholdLow, thresholdHigh); allowUpdateOfThresholdLowSpinBox = true; } /** * Called when high value spin box changed. * @param thresholdHigh * New value. */ void MapSettingsPaletteColorMappingWidget::thresholdHighSpinBoxValueChanged(double thresholdHigh) { const PaletteThresholdTypeEnum::Enum threshType = this->paletteColorMapping->getThresholdType(); float thresholdLow = this->paletteColorMapping->getThresholdMinimum(threshType); if (this->paletteColorMapping->isThresholdNegMinPosMaxLinked()) { thresholdLow = -thresholdHigh; } else { if (thresholdHigh < thresholdLow) { thresholdLow = thresholdHigh; } } allowUpdateOfThresholdHighSpinBox = false; updateAfterThresholdValuesChanged(thresholdLow, thresholdHigh); allowUpdateOfThresholdHighSpinBox = true; } /** * Called when low value slider changed. * @param thresholdLow * New value. */ void MapSettingsPaletteColorMappingWidget::thresholdLowSliderValueChanged(double thresholdLow) { const PaletteThresholdTypeEnum::Enum threshType = this->paletteColorMapping->getThresholdType(); float thresholdHigh = this->paletteColorMapping->getThresholdMaximum(threshType); if (this->paletteColorMapping->isThresholdNegMinPosMaxLinked()) { thresholdHigh = -thresholdLow; } else { if (thresholdLow > thresholdHigh) { thresholdHigh = thresholdLow; } } updateAfterThresholdValuesChanged(thresholdLow, thresholdHigh); } /** * Called when high value slider changed. * @param thresholdHigh * New value. */ void MapSettingsPaletteColorMappingWidget::thresholdHighSliderValueChanged(double thresholdHigh) { const PaletteThresholdTypeEnum::Enum threshType = this->paletteColorMapping->getThresholdType(); float thresholdLow = this->paletteColorMapping->getThresholdMinimum(threshType); if (this->paletteColorMapping->isThresholdNegMinPosMaxLinked()) { thresholdLow = -thresholdHigh; } else { if (thresholdHigh < thresholdLow) { thresholdLow = thresholdHigh; } } updateAfterThresholdValuesChanged(thresholdLow, thresholdHigh); } /** * Called when the threshold link check box is toggled. * * @param checked * Checked status of the checkbox. */ void MapSettingsPaletteColorMappingWidget::thresholdLinkCheckBoxToggled(bool checked) { if (this->paletteColorMapping != NULL) { this->paletteColorMapping->setThresholdNegMinPosMaxLinked(checked); const PaletteThresholdTypeEnum::Enum threshType = this->paletteColorMapping->getThresholdType(); float lowValue = this->paletteColorMapping->getThresholdMinimum(threshType); float highValue = this->paletteColorMapping->getThresholdMaximum(threshType); if (checked) { if (highValue > 0.0) { lowValue = -highValue; } else if (lowValue < 0.0) { highValue = -lowValue; } else { highValue = 1.0; lowValue = -1.0; } } updateAfterThresholdValuesChanged(lowValue, highValue); } } /** * 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 * The threshold section. */ QWidget* MapSettingsPaletteColorMappingWidget::createThresholdSection() { allowUpdateOfThresholdLowSpinBox = true; allowUpdateOfThresholdHighSpinBox = true; /* * Threshold types on/off */ QLabel* thresholdTypeLabel = new QLabel("Type"); std::vector thresholdTypes; PaletteThresholdTypeEnum::getAllEnums(thresholdTypes); const int32_t numThresholdTypes = static_cast(thresholdTypes.size()); this->thresholdTypeComboBox = new QComboBox(); WuQtUtilities::setToolTipAndStatusTip(this->thresholdTypeComboBox, "Select thresholding off/on"); for (int32_t i = 0; i < numThresholdTypes; i++) { this->thresholdTypeComboBox->addItem(PaletteThresholdTypeEnum::toGuiName(thresholdTypes[i])); this->thresholdTypeComboBox->setItemData(i, static_cast(thresholdTypes[i])); } QObject::connect(this->thresholdTypeComboBox, SIGNAL(currentIndexChanged(int)), this, SLOT(thresholdTypeChanged(int))); QLabel* thresholdRangeLabel = new QLabel("Range"); this->thresholdRangeModeComboBox = new EnumComboBoxTemplate(this); QObject::connect(this->thresholdRangeModeComboBox, SIGNAL(itemActivated()), this, SLOT(thresholdRangeModeChanged())); this->thresholdRangeModeComboBox->setup(); const AString rangeModeToolTip = ("Controls range of threshold controls\n" " File: Range is from all values in file.\n" " Map: Range is from all values in selected map.\n" " Unlimited: Range is +/- infinity."); this->thresholdRangeModeComboBox->getWidget()->setToolTip(WuQtUtilities::createWordWrappedToolTipText(rangeModeToolTip)); /* * Linking of low/high thresholds */ QPixmap chainLinkPixmap; const bool chainLinkPixmapValid = WuQtUtilities::loadPixmap(":/PaletteSettings/chain_link_icon.png", chainLinkPixmap); const AString linkToolTipText("When linked, both low and high are the same\n" "ABSOLUTE value with low always being negative and\n" "high always being positive.\n" " low range: [- maximum-absolute value, 0]\n" " high range: [0, + maximum-absolute-value]\n" "\n" "When NOT linked, the low and high range controls\n" "operate independently.\n" " low and high range: [minimum-value, maximum-value]\n" "\n" "NOTE: When 'Link' is unchecked, the thresholds may \n" "change due to a difference in the allowable range of \n" "values while linked and unlinked."); this->thresholdLinkCheckBox = new QCheckBox(""); QObject::connect(this->thresholdLinkCheckBox, SIGNAL(toggled(bool)), this, SLOT(thresholdLinkCheckBoxToggled(bool))); this->thresholdLinkCheckBox->setToolTip(linkToolTipText); this->thresholdWidgetGroup->add(this->thresholdLinkCheckBox); QLabel* linkLabel = new QLabel(); if (chainLinkPixmapValid) { linkLabel->setPixmap(chainLinkPixmap); } else { linkLabel->setText("Link"); } linkLabel->setToolTip(linkToolTipText); QVBoxLayout* linkLayout = new QVBoxLayout(); linkLayout->addWidget(this->thresholdLinkCheckBox); linkLayout->addWidget(linkLabel); /* * Sliders and Spin Boxes for adjustment */ QLabel* thresholdLowLabel = new QLabel("Low"); QLabel* thresholdHighLabel = new QLabel("High"); const float thresholdMinimum = -BIG_NUMBER; const float thresholdMaximum = BIG_NUMBER; this->thresholdLowSlider = new WuQDoubleSlider(Qt::Horizontal, this); this->thresholdLowSlider->setRange(thresholdMinimum, thresholdMaximum); WuQtUtilities::setToolTipAndStatusTip(this->thresholdLowSlider->getWidget(), "Adjust the low threshold value"); this->thresholdWidgetGroup->add(this->thresholdLowSlider); QObject::connect(this->thresholdLowSlider, SIGNAL(valueChanged(double)), this, SLOT(thresholdLowSliderValueChanged(double))); this->thresholdHighSlider = new WuQDoubleSlider(Qt::Horizontal, this); this->thresholdHighSlider->setRange(thresholdMinimum, thresholdMaximum); WuQtUtilities::setToolTipAndStatusTip(this->thresholdHighSlider->getWidget(), "Adjust the high threshold value"); this->thresholdWidgetGroup->add(this->thresholdHighSlider); QObject::connect(this->thresholdHighSlider, SIGNAL(valueChanged(double)), this, SLOT(thresholdHighSliderValueChanged(double))); const int spinBoxWidth = 80.0; this->thresholdLowSpinBox = WuQFactory::newDoubleSpinBoxWithMinMaxStepDecimalsSignalDouble(thresholdMinimum, thresholdMaximum, 1.0, 3, this, SLOT(thresholdLowSpinBoxValueChanged(double))); this->thresholdLowSpinBox->setAccelerated(true); WuQtUtilities::setToolTipAndStatusTip(this->thresholdLowSpinBox, "Adjust the low threshold value"); this->thresholdWidgetGroup->add(this->thresholdLowSpinBox); this->thresholdLowSpinBox->setFixedWidth(spinBoxWidth); this->thresholdHighSpinBox = WuQFactory::newDoubleSpinBoxWithMinMaxStepDecimalsSignalDouble(thresholdMinimum, thresholdMaximum, 1.0, 3, this, SLOT(thresholdHighSpinBoxValueChanged(double))); WuQtUtilities::setToolTipAndStatusTip(this->thresholdHighSpinBox, "Adjust the high threshold value"); this->thresholdWidgetGroup->add(this->thresholdHighSpinBox); this->thresholdHighSpinBox->setFixedWidth(spinBoxWidth); this->thresholdShowInsideRadioButton = new QRadioButton("Show Data Inside Thresholds"); WuQtUtilities::setWordWrappedToolTip(this->thresholdShowInsideRadioButton, "Displays data that is greater than or equal to " "the minimum threshold value and less than or " "equal to the maximum threshold value."); this->thresholdShowOutsideRadioButton = new QRadioButton("Show Data Outside Thresholds"); WuQtUtilities::setWordWrappedToolTip(this->thresholdShowOutsideRadioButton, "Displays data that is less than the minimum " "threshold value or greater than the maximum " "threshold value."); QButtonGroup* thresholdShowButtonGroup = new QButtonGroup(this); this->thresholdWidgetGroup->add(thresholdShowButtonGroup); thresholdShowButtonGroup->addButton(this->thresholdShowInsideRadioButton); thresholdShowButtonGroup->addButton(this->thresholdShowOutsideRadioButton); QObject::connect(thresholdShowButtonGroup, SIGNAL(buttonClicked(int)), this, SLOT(applyAndUpdate())); QWidget* 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->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(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); thresholdAdjustmentWidget->setFixedHeight(thresholdAdjustmentWidget->sizeHint().height()); QWidget* topWidget = new QWidget(); QHBoxLayout* topLayout = new QHBoxLayout(topWidget); this->setLayoutSpacingAndMargins(topLayout); topLayout->addWidget(thresholdTypeLabel); topLayout->addWidget(this->thresholdTypeComboBox); topLayout->addWidget(thresholdRangeLabel); topLayout->addWidget(this->thresholdRangeModeComboBox->getWidget()); topLayout->addStretch(); QGroupBox* thresholdGroupBox = new QGroupBox("Threshold"); QVBoxLayout* layout = new QVBoxLayout(thresholdGroupBox); this->setLayoutSpacingAndMargins(layout); layout->addWidget(topWidget, 0, Qt::AlignLeft); layout->addWidget(WuQtUtilities::createHorizontalLineWidget()); layout->addWidget(thresholdAdjustmentWidget); thresholdGroupBox->setFixedHeight(thresholdGroupBox->sizeHint().height()); this->thresholdAdjustmentWidgetGroup = new WuQWidgetObjectGroup(this); 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->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()); return thresholdGroupBox; } /** * Called when threshold range mode is changed. */ void MapSettingsPaletteColorMappingWidget::thresholdRangeModeChanged() { const PaletteThresholdRangeModeEnum::Enum thresholdRange = this->thresholdRangeModeComboBox->getSelectedItem(); this->paletteColorMapping->setThresholdRangeMode(thresholdRange); updateThresholdControlsMinimumMaximumRangeValues(); applySelections(); } /** * Called when a histogram control is changed. */ void MapSettingsPaletteColorMappingWidget::histogramControlChanged() { this->updateWidget(); } /** * Create the statistics section * @return the statistics section widget. */ 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())); 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); controlWidget->setFixedSize(controlWidget->sizeHint()); /* * Statistics */ const AString blankText(""); //" "); this->statisticsMinimumValueLabel = new QLabel(blankText); this->statisticsMinimumValueLabel->setAlignment(Qt::AlignRight); this->statisticsMaximumValueLabel = new QLabel(blankText); this->statisticsMaximumValueLabel->setAlignment(Qt::AlignRight); this->statisticsMeanValueLabel = new QLabel(blankText); this->statisticsMeanValueLabel->setAlignment(Qt::AlignRight); this->statisticsStandardDeviationLabel = new QLabel(blankText); this->statisticsStandardDeviationLabel->setAlignment(Qt::AlignRight); QWidget* statisticsWidget = new QWidget(); QGridLayout* statisticsLayout = new QGridLayout(statisticsWidget); statisticsLayout->setColumnStretch(0, 0); statisticsLayout->setColumnStretch(0, 100); statisticsLayout->setColumnMinimumWidth(1, 10); this->setLayoutSpacingAndMargins(statisticsLayout); statisticsLayout->addWidget(new QLabel("Mean"), 0, 0); statisticsLayout->addWidget(this->statisticsMeanValueLabel, 0, 1); statisticsLayout->addWidget(new QLabel("Std Dev"), 1, 0); statisticsLayout->addWidget(this->statisticsStandardDeviationLabel, 1, 1); statisticsLayout->addWidget(new QLabel("Max"), 2, 0); statisticsLayout->addWidget(this->statisticsMaximumValueLabel, 2, 1); statisticsLayout->addWidget(new QLabel("Min"), 3, 0); statisticsLayout->addWidget(this->statisticsMinimumValueLabel, 3, 1); //statisticsWidget->setFixedHeight(statisticsWidget->sizeHint().height()); QGroupBox* groupBox = new QGroupBox("Histogram"); QHBoxLayout* layout = new QHBoxLayout(groupBox); this->setLayoutSpacingAndMargins(layout); layout->addWidget(controlWidget); layout->addWidget(WuQtUtilities::createVerticalLineWidget()); layout->addWidget(statisticsWidget); //groupBox->setFixedHeight(groupBox->sizeHint().height()); groupBox->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Fixed); return groupBox; } /** * Create the histogram section of the dialog. * @return * The histogram section. */ QWidget* MapSettingsPaletteColorMappingWidget::createHistogramSection() { //this->thresholdPlot = new QwtPlot(); this->thresholdPlot = new WuQwtPlot(); QObject::connect(this->thresholdPlot, SIGNAL(contextMenuDisplay(QContextMenuEvent*, float, float)), this, SLOT(contextMenuDisplayRequested(QContextMenuEvent*, float, float))); this->thresholdPlot->plotLayout()->setAlignCanvasToScales(true); /* * Allow zooming */ PlotMagnifier* magnifier = new PlotMagnifier(qobject_cast(this->thresholdPlot->canvas())); magnifier->setAxisEnabled(QwtPlot::yLeft, true); magnifier->setAxisEnabled(QwtPlot::yRight, true); /* * Allow panning */ (void)new PlotPanner(qobject_cast(this->thresholdPlot->canvas())); /* * Auto scaling */ this->thresholdPlot->setAxisAutoScale(QwtPlot::xBottom); this->thresholdPlot->setAxisAutoScale(QwtPlot::yLeft); /* * Reset View tool button */ QAction* resetViewAction = WuQtUtilities::createAction("Reset View", "Remove any zooming/panning from histogram chart", this, this, SLOT(histogramResetViewButtonClicked())); QToolButton* resetViewToolButton = new QToolButton(); resetViewToolButton->setDefaultAction(resetViewAction); QWidget* widget = new QWidget(); QVBoxLayout* layout = new QVBoxLayout(widget); layout->addWidget(this->thresholdPlot); layout->addWidget(resetViewToolButton, 0, Qt::AlignHCenter); WuQtUtilities::setLayoutSpacingAndMargins(layout, 2, 0); return widget; } /** * Called when the context menu is to be displayed. * * @param event * The context menu event. * @param graphX * X-coordinate on plot. * @param graphY * Y-coordinate on plot. */ void MapSettingsPaletteColorMappingWidget::contextMenuDisplayRequested(QContextMenuEvent* event, float graphX, float /*graphY*/) { if (this->paletteColorMapping != NULL) { const PaletteThresholdTypeEnum::Enum threshType = this->paletteColorMapping->getThresholdType(); if (threshType != PaletteThresholdTypeEnum::THRESHOLD_TYPE_OFF) { const float absValue = std::fabs(graphX); AString textValue = NumericTextFormatting::formatValue(absValue); CursorDisplayScoped cursor; cursor.showCursor(Qt::ArrowCursor); QMenu menu(this); QAction* linkedAction = NULL; QAction* minThreshAction = NULL; QAction* maxThreshAction = NULL; if (this->paletteColorMapping->isThresholdNegMinPosMaxLinked()) { linkedAction = menu.addAction("Set Thresholds to -" + textValue + " and " + textValue); } else { minThreshAction = menu.addAction("Set Minimum Threshold to " + textValue); maxThreshAction = menu.addAction("Set Maximum Threshold to " + textValue); } QAction* selectedAction = menu.exec(event->globalPos()); if (selectedAction != NULL) { float minThresh = this->paletteColorMapping->getThresholdMinimum(threshType); float maxThresh = this->paletteColorMapping->getThresholdMaximum(threshType); if (selectedAction == linkedAction) { minThresh = -absValue; maxThresh = absValue; } else if (selectedAction == minThreshAction) { minThresh = graphX; } else if (selectedAction == maxThreshAction) { maxThresh = graphX; } updateAfterThresholdValuesChanged(minThresh, maxThresh); } } } } /** * Called to reset the view of the histogram chart. */ void MapSettingsPaletteColorMappingWidget::histogramResetViewButtonClicked() { this->thresholdPlot->setAxisAutoScale(QwtPlot::xBottom,true); this->thresholdPlot->setAxisAutoScale(QwtPlot::yLeft,true); this->updateHistogramPlot(); this->thresholdPlot->setAxisAutoScale(QwtPlot::xBottom,false); this->thresholdPlot->setAxisAutoScale(QwtPlot::yLeft,false); } /** * Called when the auto percentage negative maximum value changes. * * @param value * The new value. */ void MapSettingsPaletteColorMappingWidget::scaleAutoPercentageNegativeMaximumValueChanged(double value) { /* * Ensure maximum >= minimum */ if (value < this->scaleAutoPercentageNegativeMinimumSpinBox->value()) { this->scaleAutoPercentageNegativeMinimumSpinBox->blockSignals(true); this->scaleAutoPercentageNegativeMinimumSpinBox->setValue(value); this->scaleAutoPercentageNegativeMinimumSpinBox->blockSignals(false); } applySelections(); } /** * Called when the auto percentage negative minimum value changes. * * @param value * The new value. */ void MapSettingsPaletteColorMappingWidget::scaleAutoPercentageNegativeMinimumValueChanged(double value) { /* * Ensure maximum >= minimum */ if (value > this->scaleAutoPercentageNegativeMaximumSpinBox->value()) { this->scaleAutoPercentageNegativeMaximumSpinBox->blockSignals(true); this->scaleAutoPercentageNegativeMaximumSpinBox->setValue(value); this->scaleAutoPercentageNegativeMaximumSpinBox->blockSignals(false); } applySelections(); } /** * Called when the auto percentage positive minimum value changes. * * @param value * The new value. */ void MapSettingsPaletteColorMappingWidget::scaleAutoPercentagePositiveMinimumValueChanged(double value) { /* * Ensure maximum >= minimum */ if (value > this->scaleAutoPercentagePositiveMaximumSpinBox->value()) { this->scaleAutoPercentagePositiveMaximumSpinBox->blockSignals(true); this->scaleAutoPercentagePositiveMaximumSpinBox->setValue(value); this->scaleAutoPercentagePositiveMaximumSpinBox->blockSignals(false); } applySelections(); } /** * Called when the auto percentage maximum value changes. * * @param value * The new value. */ void MapSettingsPaletteColorMappingWidget::scaleAutoPercentagePositiveMaximumValueChanged(double value) { /* * Ensure maximum >= minimum */ if (value < this->scaleAutoPercentagePositiveMinimumSpinBox->value()) { this->scaleAutoPercentagePositiveMinimumSpinBox->blockSignals(true); this->scaleAutoPercentagePositiveMinimumSpinBox->setValue(value); this->scaleAutoPercentagePositiveMinimumSpinBox->blockSignals(false); } applySelections(); } /** * Called when the auto absolute percentage minimum value changes. * * @param value * The new value. */ void MapSettingsPaletteColorMappingWidget::scaleAutoAbsolutePercentageMinimumValueChanged(double value) { /* * Ensure maximum >= minimum */ if (value > this->scaleAutoAbsolutePercentageMaximumSpinBox->value()) { this->scaleAutoAbsolutePercentageMaximumSpinBox->blockSignals(true); this->scaleAutoAbsolutePercentageMaximumSpinBox->setValue(value); this->scaleAutoAbsolutePercentageMaximumSpinBox->blockSignals(false); } applySelections(); } /** * Called when the auto absolute percentage maximum value changes. * * @param value * The new value. */ void MapSettingsPaletteColorMappingWidget::scaleAutoAbsolutePercentageMaximumValueChanged(double value) { /* * Ensure maximum >= minimum */ if (value < this->scaleAutoAbsolutePercentageMinimumSpinBox->value()) { this->scaleAutoAbsolutePercentageMinimumSpinBox->blockSignals(true); this->scaleAutoAbsolutePercentageMinimumSpinBox->setValue(value); this->scaleAutoAbsolutePercentageMinimumSpinBox->blockSignals(false); } applySelections(); } /** * Called when the fixed negative maximum value changes. * * @param value * The new value. */ void MapSettingsPaletteColorMappingWidget::scaleFixedNegativeMaximumValueChanged(double value) { /* * Ensure maximum >= minimum */ if (value > this->scaleFixedNegativeMinimumSpinBox->value()) { this->scaleFixedNegativeMinimumSpinBox->blockSignals(true); this->scaleFixedNegativeMinimumSpinBox->setValue(value); this->scaleFixedNegativeMinimumSpinBox->blockSignals(false); } applySelections(); } /** * Called when the fixed negative minimum value changes. * * @param value * The new value. */ void MapSettingsPaletteColorMappingWidget::scaleFixedNegativeMinimumValueChanged(double value) { /* * Ensure maximum >= minimum */ if (value < this->scaleFixedNegativeMaximumSpinBox->value()) { this->scaleFixedNegativeMaximumSpinBox->blockSignals(true); this->scaleFixedNegativeMaximumSpinBox->setValue(value); this->scaleFixedNegativeMaximumSpinBox->blockSignals(false); } applySelections(); } /** * Called when the fixed positive minimum value changes. * * @param value * The new value. */ void MapSettingsPaletteColorMappingWidget::scaleFixedPositiveMinimumValueChanged(double value) { /* * Ensure maximum >= minimum */ if (value > this->scaleFixedPositiveMaximumSpinBox->value()) { this->scaleFixedPositiveMaximumSpinBox->blockSignals(true); this->scaleFixedPositiveMaximumSpinBox->setValue(value); this->scaleFixedPositiveMaximumSpinBox->blockSignals(false); } applySelections(); } /** * Called when the fixed positive maximum value changes. * * @param value * The new value. */ void MapSettingsPaletteColorMappingWidget::scaleFixedPositiveMaximumValueChanged(double value) { /* * Ensure maximum >= minimum */ if (value < this->scaleFixedPositiveMinimumSpinBox->value()) { this->scaleFixedPositiveMinimumSpinBox->blockSignals(true); this->scaleFixedPositiveMinimumSpinBox->setValue(value); this->scaleFixedPositiveMinimumSpinBox->blockSignals(false); } applySelections(); } /** * Create the palette section of the dialog. * @return * The palette section. */ QWidget* MapSettingsPaletteColorMappingWidget::createPaletteSection() { /* * Selection */ this->paletteNameComboBox = new QComboBox(); WuQtUtilities::setToolTipAndStatusTip(this->paletteNameComboBox, "Select palette for coloring map data"); this->paletteWidgetGroup->add(this->paletteNameComboBox); QObject::connect(this->paletteNameComboBox, SIGNAL(currentIndexChanged(int)), this, SLOT(applySelections())); /* * Interpolate Colors */ this->interpolateColorsCheckBox = new QCheckBox("Interpolate Colors"); 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())); QWidget* paletteSelectionWidget = new QWidget(); QVBoxLayout* paletteSelectionLayout = new QVBoxLayout(paletteSelectionWidget); this->setLayoutSpacingAndMargins(paletteSelectionLayout); paletteSelectionLayout->addWidget(this->paletteNameComboBox); paletteSelectionLayout->addWidget(this->interpolateColorsCheckBox); paletteSelectionWidget->setFixedHeight(paletteSelectionWidget->sizeHint().height()); /* * Color Mapping */ this->scaleAutoRadioButton = new QRadioButton("Full"); //Auto Scale"); this->scaleAutoAbsolutePercentageRadioButton = new QRadioButton("Abs Pct"); this->scaleAutoPercentageRadioButton = new QRadioButton("Percent"); //"Auto Scale Percentage"); this->scaleFixedRadioButton = new QRadioButton("Fixed"); //"Fixed Scale"); WuQtUtilities::setToolTipAndStatusTip(this->scaleAutoRadioButton, "Map (most negative, zero, most positive) data values to (-1, 0, 0, 1) in palette"); WuQtUtilities::setToolTipAndStatusTip(this->scaleAutoAbsolutePercentageRadioButton, "Map (most absolute percentiles (NOT percentages) data values to (-1, 0, 0, 1) in palette"); WuQtUtilities::setToolTipAndStatusTip(this->scaleAutoPercentageRadioButton, "Map percentiles (NOT percentages) of (most neg, least neg, least pos, most pos) data values to (-1, 0, 0, 1) in palette"); WuQtUtilities::setToolTipAndStatusTip(this->scaleFixedRadioButton, "Map specified values (most neg, least neg, least pos, most pos) to (-1, 0, 0, 1) in palette"); QButtonGroup* scaleButtonGroup = new QButtonGroup(this); this->paletteWidgetGroup->add(scaleButtonGroup); scaleButtonGroup->addButton(this->scaleAutoRadioButton); scaleButtonGroup->addButton(this->scaleAutoAbsolutePercentageRadioButton); scaleButtonGroup->addButton(this->scaleAutoPercentageRadioButton); scaleButtonGroup->addButton(this->scaleFixedRadioButton); QObject::connect(scaleButtonGroup, SIGNAL(buttonClicked(int)), this, SLOT(applyAndUpdate())); /* * Spin box width */ const int percentSpinBoxWidth = 75; const int fixedSpinBoxWidth = 100; // fixed may have much larger data values /* * Percentage mapping */ this->scaleAutoPercentageNegativeMaximumSpinBox = WuQFactory::newDoubleSpinBoxWithMinMaxStepDecimalsSignalDouble(0.0, 100.0, 1.0, 2, this, SLOT(scaleAutoPercentageNegativeMaximumValueChanged(double))); WuQtUtilities::setToolTipAndStatusTip(this->scaleAutoPercentageNegativeMaximumSpinBox, "Map percentile (NOT percentage) most negative value to -1.0 in palette"); this->paletteWidgetGroup->add(this->scaleAutoPercentageNegativeMaximumSpinBox); this->scaleAutoPercentageNegativeMaximumSpinBox->setFixedWidth(percentSpinBoxWidth); this->scaleAutoPercentageNegativeMinimumSpinBox = WuQFactory::newDoubleSpinBoxWithMinMaxStepDecimalsSignalDouble(0.0, 100.0, 1.0, 2, this, SLOT(scaleAutoPercentageNegativeMinimumValueChanged(double))); WuQtUtilities::setToolTipAndStatusTip(this->scaleAutoPercentageNegativeMinimumSpinBox, "Map percentile (NOT percentage) least negative value to 0.0 in palette"); this->paletteWidgetGroup->add(this->scaleAutoPercentageNegativeMinimumSpinBox); this->scaleAutoPercentageNegativeMinimumSpinBox->setFixedWidth(percentSpinBoxWidth); this->scaleAutoPercentagePositiveMinimumSpinBox = WuQFactory::newDoubleSpinBoxWithMinMaxStepDecimalsSignalDouble(0.0, 100.0, 1.0, 2, this, SLOT(scaleAutoPercentagePositiveMinimumValueChanged(double))); WuQtUtilities::setToolTipAndStatusTip(this->scaleAutoPercentagePositiveMinimumSpinBox, "Map percentile (NOT percentage) least positive value to 0.0 in palette"); this->paletteWidgetGroup->add(this->scaleAutoPercentagePositiveMinimumSpinBox); this->scaleAutoPercentagePositiveMinimumSpinBox->setFixedWidth(percentSpinBoxWidth); this->scaleAutoPercentagePositiveMaximumSpinBox = WuQFactory::newDoubleSpinBoxWithMinMaxStepDecimalsSignalDouble(0.0, 100.0, 1.0, 2, this, SLOT(scaleAutoPercentagePositiveMaximumValueChanged(double))); WuQtUtilities::setToolTipAndStatusTip(this->scaleAutoPercentagePositiveMaximumSpinBox, "Map percentile (NOT percentage) most positive value to 1.0 in palette"); this->paletteWidgetGroup->add(this->scaleAutoPercentagePositiveMaximumSpinBox); this->scaleAutoPercentagePositiveMaximumSpinBox->setFixedWidth(percentSpinBoxWidth); /* * Absolute percentage mapping */ this->scaleAutoAbsolutePercentageMinimumSpinBox = WuQFactory::newDoubleSpinBoxWithMinMaxStepDecimalsSignalDouble(0.0, 100.0, 1.0, 2, this, SLOT(scaleAutoAbsolutePercentageMinimumValueChanged(double))); WuQtUtilities::setToolTipAndStatusTip(this->scaleAutoAbsolutePercentageMinimumSpinBox, "Map percentile (NOT percentage) least absolute value to 0.0 in palette"); this->paletteWidgetGroup->add(this->scaleAutoAbsolutePercentageMinimumSpinBox); this->scaleAutoAbsolutePercentageMinimumSpinBox->setFixedWidth(percentSpinBoxWidth); this->scaleAutoAbsolutePercentageMaximumSpinBox = WuQFactory::newDoubleSpinBoxWithMinMaxStepDecimalsSignalDouble(0.0, 100.0, 1.0, 2, this, SLOT(scaleAutoAbsolutePercentageMaximumValueChanged(double))); WuQtUtilities::setToolTipAndStatusTip(this->scaleAutoAbsolutePercentageMaximumSpinBox, "Map percentile (NOT percentage) most absolute value to 1.0 in palette"); this->paletteWidgetGroup->add(this->scaleAutoAbsolutePercentageMaximumSpinBox); this->scaleAutoAbsolutePercentageMaximumSpinBox->setFixedWidth(percentSpinBoxWidth); /* * Fixed mapping */ this->scaleFixedNegativeMaximumSpinBox = WuQFactory::newDoubleSpinBoxWithMinMaxStepDecimalsSignalDouble(-BIG_NUMBER, 0.0, 1.0, 3, this, SLOT(scaleFixedNegativeMaximumValueChanged(double))); WuQtUtilities::setToolTipAndStatusTip(this->scaleFixedNegativeMaximumSpinBox, "Map this value to -1.0 in palette"); this->paletteWidgetGroup->add(this->scaleFixedNegativeMaximumSpinBox); this->scaleFixedNegativeMaximumSpinBox->setFixedWidth(fixedSpinBoxWidth); this->scaleFixedNegativeMinimumSpinBox = WuQFactory::newDoubleSpinBoxWithMinMaxStepDecimalsSignalDouble(-BIG_NUMBER, 0.0, 1.0, 3, this, SLOT(scaleFixedNegativeMinimumValueChanged(double))); WuQtUtilities::setToolTipAndStatusTip(this->scaleFixedNegativeMinimumSpinBox, "Map this value to 0.0 in palette"); this->paletteWidgetGroup->add(this->scaleFixedNegativeMinimumSpinBox); this->scaleFixedNegativeMinimumSpinBox->setFixedWidth(fixedSpinBoxWidth); this->scaleFixedPositiveMinimumSpinBox = WuQFactory::newDoubleSpinBoxWithMinMaxStepDecimalsSignalDouble(0.0, BIG_NUMBER, 1.0, 3, this, SLOT(scaleFixedPositiveMinimumValueChanged(double))); WuQtUtilities::setToolTipAndStatusTip(this->scaleFixedPositiveMinimumSpinBox, "Map this value to 0.0 in palette"); this->paletteWidgetGroup->add(this->scaleFixedPositiveMinimumSpinBox); this->scaleFixedPositiveMinimumSpinBox->setFixedWidth(fixedSpinBoxWidth); this->scaleFixedPositiveMaximumSpinBox = WuQFactory::newDoubleSpinBoxWithMinMaxStepDecimalsSignalDouble(0.0, BIG_NUMBER, 1.0, 3, this, SLOT(scaleFixedPositiveMaximumValueChanged(double))); WuQtUtilities::setToolTipAndStatusTip(this->scaleFixedPositiveMaximumSpinBox, "Map this value to 1.0 in palette"); this->paletteWidgetGroup->add(this->scaleFixedPositiveMaximumSpinBox); this->scaleFixedPositiveMaximumSpinBox->setFixedWidth(fixedSpinBoxWidth); QWidget* colorMappingWidget = new QWidget(); QGridLayout* colorMappingLayout = new QGridLayout(colorMappingWidget); colorMappingLayout->setColumnStretch(0, 0); colorMappingLayout->setColumnStretch(1, 100); colorMappingLayout->setColumnStretch(2, 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("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); colorMappingLayout->addWidget(new QLabel("Neg Max"), 4, 0, Qt::AlignRight); colorMappingLayout->addWidget(this->scaleAutoAbsolutePercentageMaximumSpinBox, 1, 1); colorMappingLayout->addWidget(this->scaleAutoAbsolutePercentageMinimumSpinBox, 2, 1); colorMappingLayout->addWidget(this->scaleAutoPercentagePositiveMaximumSpinBox, 1, 2); colorMappingLayout->addWidget(this->scaleAutoPercentagePositiveMinimumSpinBox, 2, 2); colorMappingLayout->addWidget(this->scaleAutoPercentageNegativeMinimumSpinBox, 3, 2); colorMappingLayout->addWidget(this->scaleAutoPercentageNegativeMaximumSpinBox, 4, 2); colorMappingLayout->addWidget(this->scaleFixedPositiveMaximumSpinBox, 1, 3); colorMappingLayout->addWidget(this->scaleFixedPositiveMinimumSpinBox, 2, 3); colorMappingLayout->addWidget(this->scaleFixedNegativeMinimumSpinBox, 3, 3); colorMappingLayout->addWidget(this->scaleFixedNegativeMaximumSpinBox, 4, 3); /* * Display Mode */ this->displayModePositiveCheckBox = new QCheckBox("Positive"); this->paletteWidgetGroup->add(this->displayModePositiveCheckBox); QObject::connect(this->displayModePositiveCheckBox, SIGNAL(toggled(bool)), this, SLOT(applySelections())); this->displayModeZeroCheckBox = new QCheckBox("Zero"); this->paletteWidgetGroup->add(this->displayModeZeroCheckBox); QObject::connect(this->displayModeZeroCheckBox, SIGNAL(toggled(bool)), this, SLOT(applySelections())); this->displayModeNegativeCheckBox = new QCheckBox("Negative"); this->paletteWidgetGroup->add(this->displayModeNegativeCheckBox); QObject::connect(this->displayModeNegativeCheckBox , SIGNAL(toggled(bool)), this, SLOT(applySelections())); WuQtUtilities::setToolTipAndStatusTip(this->displayModePositiveCheckBox, "Enable/Disable the display of positive data"); 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."); WuQtUtilities::setToolTipAndStatusTip(this->displayModeNegativeCheckBox, "Enable/Disable the display of negative data"); QWidget* displayModeWidget = new QWidget(); QHBoxLayout* displayModeLayout = new QHBoxLayout(displayModeWidget); WuQtUtilities::setLayoutSpacingAndMargins(displayModeLayout, 10, 3); displayModeLayout->addWidget(this->displayModeNegativeCheckBox); displayModeLayout->addStretch(); displayModeLayout->addWidget(this->displayModeZeroCheckBox); displayModeLayout->addStretch(); displayModeLayout->addWidget(this->displayModePositiveCheckBox); /* * Layout widgets */ QGroupBox* paletteGroupBox = new QGroupBox("Palette"); QVBoxLayout* paletteLayout = new QVBoxLayout(paletteGroupBox); this->setLayoutSpacingAndMargins(paletteLayout); paletteLayout->addWidget(paletteSelectionWidget); paletteLayout->addWidget(WuQtUtilities::createHorizontalLineWidget()); paletteLayout->addWidget(colorMappingWidget); paletteLayout->addWidget(WuQtUtilities::createHorizontalLineWidget()); paletteLayout->addWidget(displayModeWidget); paletteGroupBox->setFixedHeight(paletteGroupBox->sizeHint().height()); return paletteGroupBox; } /** * 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 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; } /** * Update the threshold section. */ void MapSettingsPaletteColorMappingWidget::updateThresholdSection() { this->thresholdWidgetGroup->blockAllSignals(true); const int32_t numTypes = this->thresholdTypeComboBox->count(); for (int32_t i = 0; i < numTypes; i++) { const int value = this->thresholdTypeComboBox->itemData(i).toInt(); if (value == static_cast(this->paletteColorMapping->getThresholdType())) { this->thresholdTypeComboBox->setCurrentIndex(i); break; } } const bool enableThresholdControls = (this->paletteColorMapping->getThresholdType() != PaletteThresholdTypeEnum::THRESHOLD_TYPE_OFF); this->thresholdAdjustmentWidgetGroup->setEnabled(enableThresholdControls); const float lowValue = this->paletteColorMapping->getThresholdMinimum(this->paletteColorMapping->getThresholdType()); const float highValue = this->paletteColorMapping->getThresholdMaximum(this->paletteColorMapping->getThresholdType()); switch (this->paletteColorMapping->getThresholdTest()) { case PaletteThresholdTestEnum::THRESHOLD_TEST_SHOW_OUTSIDE: this->thresholdShowOutsideRadioButton->setChecked(true); break; case PaletteThresholdTestEnum::THRESHOLD_TEST_SHOW_INSIDE: this->thresholdShowInsideRadioButton->setChecked(true); break; } const PaletteThresholdRangeModeEnum::Enum thresholdRangeMode = paletteColorMapping->getThresholdRangeMode(); this->thresholdRangeModeComboBox->blockSignals(true); this->thresholdRangeModeComboBox->setSelectedItem(thresholdRangeMode); this->thresholdRangeModeComboBox->blockSignals(false); updateThresholdControlsMinimumMaximumRangeValues(); this->thresholdLowSlider->setValue(lowValue); this->thresholdHighSlider->setValue(highValue); if (allowUpdateOfThresholdLowSpinBox) { this->thresholdLowSpinBox->setValue(lowValue); } if (allowUpdateOfThresholdHighSpinBox) { this->thresholdHighSpinBox->setValue(highValue); } this->thresholdLinkCheckBox->setChecked(this->paletteColorMapping->isThresholdNegMinPosMaxLinked()); this->thresholdWidgetGroup->blockAllSignals(false); } /** * This PRIVATE method updates the editor content and MUST always be used * when something within this class requires updating the displayed data. * * 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 mapIndexIn * Index of map for palette that is edited. */ void MapSettingsPaletteColorMappingWidget::updateEditorInternal(CaretMappableDataFile* caretMappableDataFile, const int32_t mapIndexIn) { this->caretMappableDataFile = caretMappableDataFile; this->mapFileIndex = mapIndexIn; if (this->caretMappableDataFile == NULL) { return; } else if (this->mapFileIndex < 0) { return; } this->paletteWidgetGroup->blockAllSignals(true); const AString title = this->caretMappableDataFile->getFileNameNoPath() + ": " + this->caretMappableDataFile->getMapName(this->mapFileIndex); this->setWindowTitle(title); this->paletteNameComboBox->clear(); this->paletteColorMapping = this->caretMappableDataFile->getMapPaletteColorMapping(this->mapFileIndex); if (this->paletteColorMapping != NULL) { PaletteFile* paletteFile = GuiManager::get()->getBrain()->getPaletteFile(); int defaultIndex = 0; const int32_t numPalettes = paletteFile->getNumberOfPalettes(); for (int32_t i = 0; i < numPalettes; i++) { Palette* palette = paletteFile->getPalette(i); const AString name = palette->getName(); if (name == this->paletteColorMapping->getSelectedPaletteName()) { defaultIndex = i; } this->paletteNameComboBox->addItem(name, name); } if (defaultIndex < this->paletteNameComboBox->count()) { this->paletteNameComboBox->setCurrentIndex(defaultIndex); } bool isPercentageSpinBoxesEnabled = false; bool isAbsolutePercentageSpinBoxesEnabled = false; bool isFixedSpinBoxesEnabled = false; switch (this->paletteColorMapping->getScaleMode()) { case PaletteScaleModeEnum::MODE_AUTO_SCALE: this->scaleAutoRadioButton->setChecked(true); break; case PaletteScaleModeEnum::MODE_AUTO_SCALE_ABSOLUTE_PERCENTAGE: this->scaleAutoAbsolutePercentageRadioButton->setChecked(true); isAbsolutePercentageSpinBoxesEnabled = true; break; case PaletteScaleModeEnum::MODE_AUTO_SCALE_PERCENTAGE: this->scaleAutoPercentageRadioButton->setChecked(true); isPercentageSpinBoxesEnabled = true; break; case PaletteScaleModeEnum::MODE_USER_SCALE: this->scaleFixedRadioButton->setChecked(true); isFixedSpinBoxesEnabled = true; break; } this->scaleAutoPercentageNegativeMaximumSpinBox->setValue(this->paletteColorMapping->getAutoScalePercentageNegativeMaximum()); this->scaleAutoPercentageNegativeMinimumSpinBox->setValue(this->paletteColorMapping->getAutoScalePercentageNegativeMinimum()); this->scaleAutoPercentagePositiveMinimumSpinBox->setValue(this->paletteColorMapping->getAutoScalePercentagePositiveMinimum()); this->scaleAutoPercentagePositiveMaximumSpinBox->setValue(this->paletteColorMapping->getAutoScalePercentagePositiveMaximum()); this->scaleAutoPercentageNegativeMaximumSpinBox->setEnabled(isPercentageSpinBoxesEnabled); this->scaleAutoPercentageNegativeMinimumSpinBox->setEnabled(isPercentageSpinBoxesEnabled); this->scaleAutoPercentagePositiveMinimumSpinBox->setEnabled(isPercentageSpinBoxesEnabled); this->scaleAutoPercentagePositiveMaximumSpinBox->setEnabled(isPercentageSpinBoxesEnabled); this->scaleAutoAbsolutePercentageMaximumSpinBox->setValue(this->paletteColorMapping->getAutoScaleAbsolutePercentageMaximum()); this->scaleAutoAbsolutePercentageMinimumSpinBox->setValue(this->paletteColorMapping->getAutoScaleAbsolutePercentageMinimum()); this->scaleAutoAbsolutePercentageMaximumSpinBox->setEnabled(isAbsolutePercentageSpinBoxesEnabled); this->scaleAutoAbsolutePercentageMinimumSpinBox->setEnabled(isAbsolutePercentageSpinBoxesEnabled); this->scaleFixedNegativeMaximumSpinBox->setValue(this->paletteColorMapping->getUserScaleNegativeMaximum()); this->scaleFixedNegativeMinimumSpinBox->setValue(this->paletteColorMapping->getUserScaleNegativeMinimum()); this->scaleFixedPositiveMinimumSpinBox->setValue(this->paletteColorMapping->getUserScalePositiveMinimum()); this->scaleFixedPositiveMaximumSpinBox->setValue(this->paletteColorMapping->getUserScalePositiveMaximum()); this->scaleFixedNegativeMaximumSpinBox->setEnabled(isFixedSpinBoxesEnabled); this->scaleFixedNegativeMinimumSpinBox->setEnabled(isFixedSpinBoxesEnabled); this->scaleFixedPositiveMinimumSpinBox->setEnabled(isFixedSpinBoxesEnabled); this->scaleFixedPositiveMaximumSpinBox->setEnabled(isFixedSpinBoxesEnabled); this->displayModePositiveCheckBox->setChecked(this->paletteColorMapping->isDisplayPositiveDataFlag()); this->displayModeZeroCheckBox->setChecked(this->paletteColorMapping->isDisplayZeroDataFlag()); this->displayModeNegativeCheckBox->setChecked(this->paletteColorMapping->isDisplayNegativeDataFlag()); this->interpolateColorsCheckBox->setChecked(this->paletteColorMapping->isInterpolatePaletteFlag()); updateThresholdSection(); float minValue = 0.0; float maxValue = 0.0; 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; } if (statistics != NULL) { minValue = statistics->getMin(); maxValue = statistics->getMax(); } /* * Set fixed spin boxes so that they increment by 1% of data. */ float stepValue = 1.0; const float diff = maxValue - minValue; if (diff > 0.0) { stepValue = diff / 100.0; } this->scaleFixedNegativeMaximumSpinBox->setSingleStep(stepValue); this->scaleFixedNegativeMinimumSpinBox->setSingleStep(stepValue); this->scaleFixedPositiveMinimumSpinBox->setSingleStep(stepValue); this->scaleFixedPositiveMaximumSpinBox->setSingleStep(stepValue); } this->updateHistogramPlot(); this->updateNormalizationControlSection(); this->paletteWidgetGroup->blockAllSignals(false); } /** * Get histogram for displaying data * @param statisticsForAll * Statistics for all data. * @param * Histogram. */ const Histogram* MapSettingsPaletteColorMappingWidget::getHistogram(const FastStatistics* statisticsForAll) const { float mostPos = 0.0; float leastPos = 0.0; 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; } } else { CaretAssert(0); } const PaletteNormalizationModeEnum::Enum normMode = this->caretMappableDataFile->getPaletteNormalizationMode(); /* * Remove data that is not displayed */ bool isZeroIncluded = true; const Histogram* ret = NULL; if (matchFlag) { isZeroIncluded = this->displayModeZeroCheckBox->isChecked(); if (this->displayModeNegativeCheckBox->isChecked() == false) { mostNeg = 0.0; leastNeg = 0.0; } if (this->displayModePositiveCheckBox->isChecked() == false) { mostPos = 0.0; leastPos = 0.0; } switch (normMode) { case PaletteNormalizationModeEnum::NORMALIZATION_ALL_MAP_DATA: ret = this->caretMappableDataFile->getFileHistogram(mostPos, leastPos, leastNeg, mostNeg, isZeroIncluded); break; case PaletteNormalizationModeEnum::NORMALIZATION_SELECTED_MAP_DATA: ret = this->caretMappableDataFile->getMapHistogram(this->mapFileIndex, mostPos, leastPos, leastNeg, mostNeg, isZeroIncluded); break; } } else { switch (normMode) { case PaletteNormalizationModeEnum::NORMALIZATION_ALL_MAP_DATA: ret = caretMappableDataFile->getFileHistogram(); break; case PaletteNormalizationModeEnum::NORMALIZATION_SELECTED_MAP_DATA: ret = caretMappableDataFile->getMapHistogram(this->mapFileIndex); break; } } CaretAssert(ret); return ret; } /** * Update the histogram plot. */ void MapSettingsPaletteColorMappingWidget::updateHistogramPlot() { /* * Remove all previously attached items from the histogram plot. * The items are automatically deleted by the plot. */ this->thresholdPlot->detachItems(); 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; } 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); //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()); /* * Width of each 'bar' in the histogram */ 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; } } } 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 (displayZeros == false) { if ((startValue <= 0.0) && (stopValue >= 0.0)) { dataFrequency = 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); minBox->setZ(z); minBox->attach(this->thresholdPlot); /* * Draw shaded region to right of maximum threshold */ 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); 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)); //, Qt::Dense4Pattern)); minBox->setPen(QPen(minColor)); minBox->setSamples(minSamples); minBox->setZ(z); minBox->attach(this->thresholdPlot); } break; } 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); } } if (dataValues != NULL) { delete[] dataValues; } if (dataRGBA != NULL) { delete[] dataRGBA; } /* * Causes updates of plots. */ this->thresholdPlot->replot(); } } /** * Called to apply selections. */ void MapSettingsPaletteColorMappingWidget::applySelections() { if (this->caretMappableDataFile == NULL) { return; } else if (this->mapFileIndex < 0) { return; } const int itemIndex = this->paletteNameComboBox->currentIndex(); if (itemIndex >= 0) { const AString name = this->paletteNameComboBox->itemData(itemIndex).toString(); if (this->paletteColorMapping != NULL) { this->paletteColorMapping->setSelectedPaletteName(name); } } if (this->scaleAutoRadioButton->isChecked()) { this->paletteColorMapping->setScaleMode(PaletteScaleModeEnum::MODE_AUTO_SCALE); } else if (this->scaleAutoAbsolutePercentageRadioButton->isChecked()) { this->paletteColorMapping->setScaleMode(PaletteScaleModeEnum::MODE_AUTO_SCALE_ABSOLUTE_PERCENTAGE); } else if (this->scaleAutoPercentageRadioButton->isChecked()) { this->paletteColorMapping->setScaleMode(PaletteScaleModeEnum::MODE_AUTO_SCALE_PERCENTAGE); } else if (this->scaleFixedRadioButton->isChecked()) { this->paletteColorMapping->setScaleMode(PaletteScaleModeEnum::MODE_USER_SCALE); } this->paletteColorMapping->setUserScaleNegativeMaximum(this->scaleFixedNegativeMaximumSpinBox->value()); this->paletteColorMapping->setUserScaleNegativeMinimum(this->scaleFixedNegativeMinimumSpinBox->value()); this->paletteColorMapping->setUserScalePositiveMinimum(this->scaleFixedPositiveMinimumSpinBox->value()); this->paletteColorMapping->setUserScalePositiveMaximum(this->scaleFixedPositiveMaximumSpinBox->value()); this->paletteColorMapping->setAutoScalePercentageNegativeMaximum(this->scaleAutoPercentageNegativeMaximumSpinBox->value()); this->paletteColorMapping->setAutoScalePercentageNegativeMinimum(this->scaleAutoPercentageNegativeMinimumSpinBox->value()); this->paletteColorMapping->setAutoScalePercentagePositiveMinimum(this->scaleAutoPercentagePositiveMinimumSpinBox->value()); this->paletteColorMapping->setAutoScalePercentagePositiveMaximum(this->scaleAutoPercentagePositiveMaximumSpinBox->value()); this->paletteColorMapping->setAutoScaleAbsolutePercentageMaximum(this->scaleAutoAbsolutePercentageMaximumSpinBox->value()); this->paletteColorMapping->setAutoScaleAbsolutePercentageMinimum(this->scaleAutoAbsolutePercentageMinimumSpinBox->value()); this->paletteColorMapping->setDisplayPositiveDataFlag(this->displayModePositiveCheckBox->isChecked()); this->paletteColorMapping->setDisplayNegativeDataFlag(this->displayModeNegativeCheckBox->isChecked()); this->paletteColorMapping->setDisplayZeroDataFlag(this->displayModeZeroCheckBox->isChecked()); this->paletteColorMapping->setInterpolatePaletteFlag(this->interpolateColorsCheckBox->isChecked()); float lowValue = this->thresholdLowSpinBox->value(); float highValue = this->thresholdHighSpinBox->value(); const int thresholdTypeIndex = this->thresholdTypeComboBox->currentIndex(); PaletteThresholdTypeEnum::Enum paletteThresholdType = static_cast(this->thresholdTypeComboBox->itemData(thresholdTypeIndex).toInt()); this->paletteColorMapping->setThresholdType(paletteThresholdType); this->paletteColorMapping->setThresholdMinimum(paletteThresholdType, lowValue); this->paletteColorMapping->setThresholdMaximum(paletteThresholdType, highValue); if (this->thresholdShowInsideRadioButton->isChecked()) { this->paletteColorMapping->setThresholdTest(PaletteThresholdTestEnum::THRESHOLD_TEST_SHOW_INSIDE); } else if (this->thresholdShowOutsideRadioButton->isChecked()) { 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(); } /** * Set the layout margins. * @param layout * Layout for which margins are set. */ void MapSettingsPaletteColorMappingWidget::setLayoutSpacingAndMargins(QLayout* layout) { WuQtUtilities::setLayoutSpacingAndMargins(layout, 5, 3); } /** * @return The normalization control section. */ QWidget* MapSettingsPaletteColorMappingWidget::createNormalizationControlSection() { m_normalizationModeComboBox = new QComboBox(); QObject::connect(m_normalizationModeComboBox, SIGNAL(activated(int)), this, SLOT(normalizationModeComboBoxActivated(int))); /* * Initially load with all modes so that the combo box can * be set to a fixed size that allows display of all text */ std::vector allModes; PaletteNormalizationModeEnum::getAllEnums(allModes); for (std::vector::iterator allModesIter = allModes.begin(); allModesIter != allModes.end(); allModesIter++) { const PaletteNormalizationModeEnum::Enum normalMode = *allModesIter; m_normalizationModeComboBox->addItem(PaletteNormalizationModeEnum::toGuiName(normalMode), (int)PaletteNormalizationModeEnum::toIntegerCode(normalMode)); } m_normalizationModeComboBox->setToolTip(PaletteNormalizationModeEnum::getEnumToolTopInHTML()); QGroupBox* groupBox = new QGroupBox("Data Normalization"); QVBoxLayout* layout = new QVBoxLayout(groupBox); layout->addWidget(m_normalizationModeComboBox); groupBox->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); return groupBox; } /** * Update the normalization control section. */ void MapSettingsPaletteColorMappingWidget::updateNormalizationControlSection() { m_normalizationModeComboBox->clear(); std::vector validModes; if (this->caretMappableDataFile != NULL) { this->caretMappableDataFile->getPaletteNormalizationModesSupported(validModes); const int32_t numValidModes = static_cast(validModes.size()); if (numValidModes > 0) { const PaletteNormalizationModeEnum::Enum selectedMode = this->caretMappableDataFile->getPaletteNormalizationMode(); int selectedModeIndex = -1; /* * Loop through ALL modes so that the selections are always * in a consistent order. */ std::vector allModes; PaletteNormalizationModeEnum::getAllEnums(allModes); for (std::vector::iterator allModesIter = allModes.begin(); allModesIter != allModes.end(); allModesIter++) { const PaletteNormalizationModeEnum::Enum normalMode = *allModesIter; if (std::find(validModes.begin(), validModes.end(), normalMode) != validModes.end()) { if (selectedMode == normalMode) { selectedModeIndex = m_normalizationModeComboBox->count(); } m_normalizationModeComboBox->addItem(PaletteNormalizationModeEnum::toGuiName(normalMode), (int)PaletteNormalizationModeEnum::toIntegerCode(normalMode)); } } if (selectedModeIndex >= 0) { m_normalizationModeComboBox->setCurrentIndex(selectedModeIndex); } } } } /** * Called when normalization combo box is changed by user. */ void MapSettingsPaletteColorMappingWidget::normalizationModeComboBoxActivated(int) { if (this->caretMappableDataFile != NULL) { const int selectedIndex = m_normalizationModeComboBox->currentIndex(); if ((selectedIndex >= 0) && (selectedIndex < m_normalizationModeComboBox->count())) { const int32_t enumIntegerCode = m_normalizationModeComboBox->itemData(selectedIndex).toInt(); bool validFlag = false; const PaletteNormalizationModeEnum::Enum mode = PaletteNormalizationModeEnum::fromIntegerCode(enumIntegerCode, &validFlag); if (validFlag) { if (mode != this->caretMappableDataFile->getPaletteNormalizationMode()) { bool doItFlag = true; if (mode == PaletteNormalizationModeEnum::NORMALIZATION_ALL_MAP_DATA) { /* * 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 warningDataSize = 100 * megabyte; const int64_t dataSize = this->caretMappableDataFile->getDataSizeUncompressedInBytes(); if (dataSize > warningDataSize) { const int64_t gigabyte = 1000000000; // 10e9 const int64_t numReallys = std::min(dataSize / gigabyte, (int64_t)10); AString veryString; if (numReallys > 0) { veryString = ("very" + QString(", very").repeated(numReallys - 1) + " "); } const AString message("File size is " + veryString + "large (" + FileInformation::fileSizeToStandardUnits(dataSize) + "). This operation may take a " + veryString + "long time."); if (WuQMessageBox::warningOkCancel(m_normalizationModeComboBox, message)) { doItFlag = true; } else { doItFlag = false; } } } if (doItFlag) { CursorDisplayScoped cursor; cursor.showCursor(Qt::WaitCursor); this->caretMappableDataFile->setPaletteNormalizationMode(mode); this->updateEditorInternal(this->caretMappableDataFile, this->mapFileIndex); this->updateColoringAndGraphics(); } else { this->updateNormalizationControlSection(); } } } } } } /** * 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(); } } connectome-workbench-1.2.3+git41-gc4c6c90/src/GuiQt/MapSettingsPaletteColorMappingWidget.h000066400000000000000000000172641300200146000312140ustar00rootroot00000000000000#ifndef __MAP_SETTINGS_PALETTE_COLOR_MAPPING_WIDGET_H_ #define __MAP_SETTINGS_PALETTE_COLOR_MAPPING_WIDGET_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 // needed by windows #include #include class QAbstractButton; class QCheckBox; class QDoubleSpinBox; class QComboBox; class QLabel; class QLayout; class QPushButton; class QRadioButton; class QSpinBox; class QwtPlot; namespace caret { class CaretMappableDataFile; class EnumComboBoxTemplate; class FastStatistics; class Histogram; class PaletteColorMapping; class WuQDoubleSlider; class WuQWidgetObjectGroup; class WuQwtPlot; class MapSettingsPaletteColorMappingWidget : public QWidget { Q_OBJECT public: MapSettingsPaletteColorMappingWidget(QWidget* parent = 0); void updateEditor(CaretMappableDataFile* caretMappableDataFile, const int32_t mapIndex); virtual ~MapSettingsPaletteColorMappingWidget(); void updateWidget(); private: MapSettingsPaletteColorMappingWidget(const MapSettingsPaletteColorMappingWidget&); MapSettingsPaletteColorMappingWidget& operator=(const MapSettingsPaletteColorMappingWidget&); private slots: void thresholdLowSpinBoxValueChanged(double); void thresholdHighSpinBoxValueChanged(double); void thresholdLowSliderValueChanged(double); void thresholdHighSliderValueChanged(double); void thresholdTypeChanged(int); void thresholdRangeModeChanged(); void thresholdLinkCheckBoxToggled(bool); void scaleAutoPercentageNegativeMaximumValueChanged(double value); void scaleAutoPercentageNegativeMinimumValueChanged(double value); void scaleAutoPercentagePositiveMinimumValueChanged(double value); void scaleAutoPercentagePositiveMaximumValueChanged(double value); void scaleAutoAbsolutePercentageMinimumValueChanged(double value); void scaleAutoAbsolutePercentageMaximumValueChanged(double value); void scaleFixedNegativeMaximumValueChanged(double value); void scaleFixedNegativeMinimumValueChanged(double value); 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); void normalizationModeComboBoxActivated(int); private: void updateEditorInternal(CaretMappableDataFile* caretMappableDataFile, const int32_t mapIndex); QWidget* createPaletteSection(); QWidget* createThresholdSection(); void updateThresholdSection(); QWidget* createHistogramSection(); QWidget* createHistogramControlSection(); QWidget* createNormalizationControlSection(); QWidget* createDataOptionsSection(); void updateNormalizationControlSection(); void updateAfterThresholdValuesChanged(const float lowThreshold, const float highThreshold); void updateHistogramPlot(); void updateColoringAndGraphics(); void updateThresholdControlsMinimumMaximumRangeValues(); void setLayoutSpacingAndMargins(QLayout* layout); const Histogram* getHistogram(const FastStatistics* statisticsForAll) const; PaletteColorMapping* paletteColorMapping; QComboBox* paletteNameComboBox; QCheckBox* applyAllMapsCheckBox; QPushButton* applyToMultipleFilesPushButton; QRadioButton* scaleAutoRadioButton; QRadioButton* scaleAutoAbsolutePercentageRadioButton; QRadioButton* scaleAutoPercentageRadioButton; QRadioButton* scaleFixedRadioButton; QDoubleSpinBox* scaleAutoPercentageNegativeMaximumSpinBox; QDoubleSpinBox* scaleAutoPercentageNegativeMinimumSpinBox; QDoubleSpinBox* scaleAutoPercentagePositiveMinimumSpinBox; QDoubleSpinBox* scaleAutoPercentagePositiveMaximumSpinBox; QDoubleSpinBox* scaleAutoAbsolutePercentageMinimumSpinBox; QDoubleSpinBox* scaleAutoAbsolutePercentageMaximumSpinBox; QDoubleSpinBox* scaleFixedNegativeMaximumSpinBox; QDoubleSpinBox* scaleFixedNegativeMinimumSpinBox; QDoubleSpinBox* scaleFixedPositiveMinimumSpinBox; QDoubleSpinBox* scaleFixedPositiveMaximumSpinBox; QCheckBox* displayModePositiveCheckBox; QCheckBox* displayModeZeroCheckBox; QCheckBox* displayModeNegativeCheckBox; QCheckBox* interpolateColorsCheckBox; QComboBox* thresholdTypeComboBox; WuQDoubleSlider* thresholdLowSlider; WuQDoubleSlider* thresholdHighSlider; WuQWidgetObjectGroup* thresholdAdjustmentWidgetGroup; QDoubleSpinBox* thresholdLowSpinBox; QDoubleSpinBox* thresholdHighSpinBox; bool allowUpdateOfThresholdLowSpinBox; bool allowUpdateOfThresholdHighSpinBox; QRadioButton* thresholdShowInsideRadioButton; QRadioButton* thresholdShowOutsideRadioButton; EnumComboBoxTemplate* thresholdRangeModeComboBox; QCheckBox* thresholdLinkCheckBox; WuQwtPlot* thresholdPlot; QLabel* statisticsMinimumValueLabel; QLabel* statisticsMaximumValueLabel; QLabel* statisticsMeanValueLabel; QLabel* statisticsStandardDeviationLabel; QComboBox* m_normalizationModeComboBox; QRadioButton* histogramAllRadioButton; QRadioButton* histogramMatchPaletteRadioButton; QCheckBox* histogramUsePaletteColors; bool isHistogramColored; CaretMappableDataFile* caretMappableDataFile; CaretMappableDataFile* m_previousCaretMappableDataFile; int32_t mapFileIndex; std::set previousApplyPaletteToMapFilesSelected; WuQWidgetObjectGroup* paletteWidgetGroup; WuQWidgetObjectGroup* thresholdWidgetGroup; }; #ifdef __MAP_SETTINGS_PALETTE_COLOR_MAPPING_WIDGET_DECLARE__ // #endif // __MAP_SETTINGS_PALETTE_COLOR_MAPPING_WIDGET_DECLARE__ } // namespace #endif //__MAP_SETTINGS_PALETTE_COLOR_MAPPING_WIDGET_H_ connectome-workbench-1.2.3+git41-gc4c6c90/src/GuiQt/MapSettingsParcelsWidget.cxx000066400000000000000000000110461300200146000272370ustar00rootroot00000000000000 /*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 __MAP_SETTINGS_PARCELS_WIDGET_DECLARE__ #include "MapSettingsParcelsWidget.h" #undef __MAP_SETTINGS_PARCELS_WIDGET_DECLARE__ #include #include #include "CaretAssert.h" #include "CaretColorEnumComboBox.h" #include "CiftiConnectivityMatrixParcelFile.h" #include "CiftiParcelColoringModeEnum.h" #include "EnumComboBoxTemplate.h" #include "EventGraphicsUpdateAllWindows.h" #include "EventManager.h" #include "EventSurfaceColoringInvalidate.h" using namespace caret; /** * \class caret::MapSettingsParcelsWidget * \brief Widget for setting parcel coloring properties. * \ingroup GuiQt */ /** * Constructor. */ MapSettingsParcelsWidget::MapSettingsParcelsWidget(QWidget* parent) : QWidget(parent) { m_ciftiParcelFile = NULL; QLabel* colorModeLabel = new QLabel("Color Mode"); m_parcelColoringModeEnumComboBox = new EnumComboBoxTemplate(this); m_parcelColoringModeEnumComboBox->setup(); QObject::connect(m_parcelColoringModeEnumComboBox, SIGNAL(itemActivated()), this, SLOT(ciftiParcelColoringModeEnumComboBoxItemActivated())); QLabel* colorLabel = new QLabel("Color"); m_parcelColorEnumComboBox = new CaretColorEnumComboBox(this); QObject::connect(m_parcelColorEnumComboBox, SIGNAL(colorSelected(const CaretColorEnum::Enum)), this, SLOT(parcelColorSelected(const CaretColorEnum::Enum))); QWidget* gridWidget = new QWidget(); QGridLayout* gridLayout = new QGridLayout(gridWidget); gridLayout->addWidget(colorModeLabel, 0, 0); gridLayout->addWidget(m_parcelColoringModeEnumComboBox->getWidget(), 0, 1); gridLayout->addWidget(colorLabel, 1, 0); gridLayout->addWidget(m_parcelColorEnumComboBox->getWidget(), 1, 1); gridWidget->setFixedSize(gridWidget->sizeHint()); QVBoxLayout* layout = new QVBoxLayout(this); layout->addWidget(gridWidget, 0, Qt::AlignLeft); layout->addStretch(); setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); } /** * Destructor. */ MapSettingsParcelsWidget::~MapSettingsParcelsWidget() { } /** * Update the editor with the given CIFTI matrix file. */ void MapSettingsParcelsWidget::updateEditor(CiftiConnectivityMatrixParcelFile* ciftiParcelFile) { CaretAssert(ciftiParcelFile); m_ciftiParcelFile = ciftiParcelFile; m_parcelColoringModeEnumComboBox->setSelectedItem(m_ciftiParcelFile->getSelectedParcelColoringMode()); m_parcelColorEnumComboBox->setSelectedColor(m_ciftiParcelFile->getSelectedParcelColor()); } /** * Update the widget. */ void MapSettingsParcelsWidget::updateWidget() { updateEditor(m_ciftiParcelFile); } /** * Called when parcel color combo box is changed. */ void MapSettingsParcelsWidget::parcelColorSelected(const CaretColorEnum::Enum color) { m_ciftiParcelFile->setSelectedParcelColor(color); EventManager::get()->sendEvent(EventSurfaceColoringInvalidate().getPointer()); EventManager::get()->sendEvent(EventGraphicsUpdateAllWindows().getPointer()); } /** * Called when parcel coloring mode combo box is changed. */ void MapSettingsParcelsWidget::ciftiParcelColoringModeEnumComboBoxItemActivated() { const CiftiParcelColoringModeEnum::Enum colorMode = m_parcelColoringModeEnumComboBox->getSelectedItem(); m_ciftiParcelFile->setSelectedParcelColoringMode(colorMode); EventManager::get()->sendEvent(EventSurfaceColoringInvalidate().getPointer()); EventManager::get()->sendEvent(EventGraphicsUpdateAllWindows().getPointer()); } connectome-workbench-1.2.3+git41-gc4c6c90/src/GuiQt/MapSettingsParcelsWidget.h000066400000000000000000000043331300200146000266650ustar00rootroot00000000000000#ifndef __MAP_SETTINGS_PARCELS_WIDGET_H__ #define __MAP_SETTINGS_PARCELS_WIDGET_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 "CaretColorEnum.h" namespace caret { class CaretColorEnumComboBox; class CiftiConnectivityMatrixParcelFile; class EnumComboBoxTemplate; class MapSettingsParcelsWidget : public QWidget { Q_OBJECT public: MapSettingsParcelsWidget(QWidget* parent = 0); virtual ~MapSettingsParcelsWidget(); void updateEditor(CiftiConnectivityMatrixParcelFile* ciftiMatrixFile); void updateWidget(); private slots: void ciftiParcelColoringModeEnumComboBoxItemActivated(); void parcelColorSelected(const CaretColorEnum::Enum); // ADD_NEW_METHODS_HERE private: MapSettingsParcelsWidget(const MapSettingsParcelsWidget&); MapSettingsParcelsWidget& operator=(const MapSettingsParcelsWidget&); EnumComboBoxTemplate* m_parcelColoringModeEnumComboBox; CaretColorEnumComboBox* m_parcelColorEnumComboBox; CiftiConnectivityMatrixParcelFile* m_ciftiParcelFile; // ADD_NEW_MEMBERS_HERE }; #ifdef __MAP_SETTINGS_PARCELS_WIDGET_DECLARE__ // #endif // __MAP_SETTINGS_PARCELS_WIDGET_DECLARE__ } // namespace #endif //__MAP_SETTINGS_PARCELS_WIDGET_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/GuiQt/MapYokingGroupComboBox.cxx000066400000000000000000000335331300200146000266740ustar00rootroot00000000000000 /*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 __MAP_YOKING_GROUP_COMBO_BOX_DECLARE__ #include "MapYokingGroupComboBox.h" #undef __MAP_YOKING_GROUP_COMBO_BOX_DECLARE__ #include "CaretAssert.h" #include "CaretMappableDataFile.h" #include "ChartableMatrixSeriesInterface.h" #include "CiftiScalarDataSeriesFile.h" #include "EnumComboBoxTemplate.h" #include "EventManager.h" #include "EventMapYokingValidation.h" #include "Overlay.h" #include "WuQMessageBox.h" #include "WuQtUtilities.h" using namespace caret; /** * \class caret::MapYokingGroupComboBox * \brief Combo box for selection of a map yoking group. * \ingroup GuiQt */ /** * Constructor. */ MapYokingGroupComboBox::MapYokingGroupComboBox(QObject* parent) : WuQWidget(parent) { m_comboBox = new EnumComboBoxTemplate(this); m_comboBox->setup(); m_comboBox->getWidget()->setStatusTip("Synchronize selected map indices (and selection status for overlays)"); m_comboBox->getWidget()->setToolTip("Synchronize selected map indices (and selection status for overlays)"); #ifdef CARET_OS_MACOSX m_comboBox->getComboBox()->setFixedWidth(m_comboBox->getComboBox()->sizeHint().width() - 20); #endif // CARET_OS_MACOSX QObject::connect(m_comboBox, SIGNAL(itemActivated()), this, SLOT(comboBoxActivated())); } /** * Destructor. */ MapYokingGroupComboBox::~MapYokingGroupComboBox() { } /** * Called when the user selects a yoking group. * Verify compatibility before accepting the selection. */ void MapYokingGroupComboBox::comboBoxActivated() { emit itemActivated(); // MapYokingGroupEnum::Enum mapYokingGroup = getMapYokingGroup(); // EventMapYokingValidation validateEvent(mapYokingGroup); } /** * @return The widget. */ QWidget* MapYokingGroupComboBox::getWidget() { return m_comboBox->getWidget(); } /** * @return The selected map yoking group. */ MapYokingGroupEnum::Enum MapYokingGroupComboBox::getMapYokingGroup() const { return m_comboBox->getSelectedItem(); } /** * Set the map yoking group. * * @param mapYokingGroup * The map yoking group. */ void MapYokingGroupComboBox::setMapYokingGroup(const MapYokingGroupEnum::Enum mapYokingGroup) { m_comboBox->setSelectedItem(mapYokingGroup); } /** * Validate a change in yoking for a matrix series file. * * @param chartableMatrixSeriesInterface * Matrix series file that has yoking changed. * @param tabIndex * Index of tab for the file. */ void MapYokingGroupComboBox::validateYokingChange(ChartableMatrixSeriesInterface* chartableMatrixSeriesInterface, const int32_t tabIndex) { if (chartableMatrixSeriesInterface != NULL) { int32_t mapIndex = chartableMatrixSeriesInterface->getSelectedMapIndex(tabIndex); const MapYokingGroupEnum::Enum previousMapYokingGroup = chartableMatrixSeriesInterface->getMatrixRowColumnMapYokingGroup(tabIndex); const MapYokingGroupEnum::Enum newYokingGroup = getMapYokingGroup(); CaretMappableDataFile* mapFile = dynamic_cast(chartableMatrixSeriesInterface); CaretAssert(mapFile); bool selectionStatus = true; if ((mapFile != NULL) && (mapIndex >= 0)) { const YokeValidationResult result = validateYoking(mapFile, mapIndex, selectionStatus); switch (result) { case YOKE_VALIDATE_RESULT_ACCEPT: chartableMatrixSeriesInterface->setMatrixRowColumnMapYokingGroup(tabIndex, newYokingGroup); chartableMatrixSeriesInterface->setSelectedMapIndex(tabIndex, mapIndex); break; case YOKE_VALIDATE_RESULT_OFF: chartableMatrixSeriesInterface->setMatrixRowColumnMapYokingGroup(tabIndex, MapYokingGroupEnum::MAP_YOKING_GROUP_OFF); break; case YOKE_VALIDATE_RESULT_PREVIOUS: chartableMatrixSeriesInterface->setMatrixRowColumnMapYokingGroup(tabIndex, previousMapYokingGroup); break; } setMapYokingGroup(chartableMatrixSeriesInterface->getMatrixRowColumnMapYokingGroup(tabIndex)); } } } /** * Validate a change in yoking for an overlay. * * @param overlay * Overlay whose yoking changes. */ void MapYokingGroupComboBox::validateYokingChange(Overlay* overlay) { const MapYokingGroupEnum::Enum previousMapYokingGroup = overlay->getMapYokingGroup(); const MapYokingGroupEnum::Enum newYokingGroup = getMapYokingGroup(); CaretMappableDataFile* mapFile = NULL; int32_t mapIndex = -1; overlay->getSelectionData(mapFile, mapIndex); bool selectionStatus = overlay->isEnabled(); if ((mapFile != NULL) && (mapIndex >= 0)) { const YokeValidationResult result = validateYoking(mapFile, mapIndex, selectionStatus); switch (result) { case YOKE_VALIDATE_RESULT_ACCEPT: overlay->setEnabled(selectionStatus); overlay->setSelectionData(mapFile, mapIndex); overlay->setMapYokingGroup(newYokingGroup); break; case YOKE_VALIDATE_RESULT_OFF: overlay->setMapYokingGroup(MapYokingGroupEnum::MAP_YOKING_GROUP_OFF); break; case YOKE_VALIDATE_RESULT_PREVIOUS: overlay->setMapYokingGroup(previousMapYokingGroup); break; } setMapYokingGroup(overlay->getMapYokingGroup()); } } /** * Validate yoking when a new file is added to a yoking group. * * @param previousMapYokingGroup * The previous yoking group. * @param selectedFile * The file that the user would like to yoke. * @param selectedMapIndexInOut * The current map selected for the file. Its value will be updated * if yoking is selected (turned on or changed). * @param selectionStatusInOut * The selection status for an overlay. Its value will be updated * if yoking is selected (turned on or changed). */ MapYokingGroupComboBox::YokeValidationResult MapYokingGroupComboBox::validateYoking(CaretMappableDataFile* selectedFile, int32_t& selectedMapIndexInOut, bool& /* selectionStatusInOut */) { YokeValidationResult yokeResult = YOKE_VALIDATE_RESULT_OFF; //YOKE_VALIDATE_RESULT_PREVIOUS; MapYokingGroupEnum::Enum newYokingGroup = getMapYokingGroup(); if (newYokingGroup != MapYokingGroupEnum::MAP_YOKING_GROUP_OFF) { if ((selectedFile != NULL) && (selectedMapIndexInOut >= 0)) { /* * Get info on yoking selections */ EventMapYokingValidation validateEvent(newYokingGroup); EventManager::get()->sendEvent(validateEvent.getPointer()); /* * Check compatibility based (number of maps in yoked files) * and warn use if there is an incompatibility. */ int32_t numberOfYokedFiles = 0; AString message; if (validateEvent.validateCompatibility(selectedFile, numberOfYokedFiles, message)) { yokeResult = YOKE_VALIDATE_RESULT_ACCEPT; } else { 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: yokeResult = YOKE_VALIDATE_RESULT_ACCEPT; break; case WuQMessageBox::RESULT_NO: yokeResult = YOKE_VALIDATE_RESULT_OFF; break; case WuQMessageBox::RESULT_CANCEL: yokeResult = YOKE_VALIDATE_RESULT_PREVIOUS; break; } } if (yokeResult == YOKE_VALIDATE_RESULT_ACCEPT) { if (numberOfYokedFiles > 0) { /* * Already have files yoked to this group so use * the map index and status from the yoking group. */ selectedMapIndexInOut = MapYokingGroupEnum::getSelectedMapIndex(newYokingGroup); //selectionStatusInOut = MapYokingGroupEnum::isEnabled(newYokingGroup); } else { /* * This is the first file added to the yoking group * so set the map index and status in the yoking group * to the file's selections. */ MapYokingGroupEnum::setSelectedMapIndex(newYokingGroup, selectedMapIndexInOut); //MapYokingGroupEnum::setEnabled(newYokingGroup, // selectionStatusInOut); } } } } 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(); // //} // connectome-workbench-1.2.3+git41-gc4c6c90/src/GuiQt/MapYokingGroupComboBox.h000066400000000000000000000053171300200146000263200ustar00rootroot00000000000000#ifndef __MAP_YOKING_GROUP_COMBO_BOX_H__ #define __MAP_YOKING_GROUP_COMBO_BOX_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 "MapYokingGroupEnum.h" #include "WuQWidget.h" namespace caret { class CaretMappableDataFile; class ChartableMatrixSeriesInterface; class EnumComboBoxTemplate; class Overlay; class MapYokingGroupComboBox : public WuQWidget { Q_OBJECT public: MapYokingGroupComboBox(QObject* parent); virtual ~MapYokingGroupComboBox(); virtual QWidget* getWidget(); MapYokingGroupEnum::Enum getMapYokingGroup() const; void setMapYokingGroup(const MapYokingGroupEnum::Enum mapYokingGroup); void validateYokingChange(ChartableMatrixSeriesInterface* chartableMatrixSeriesInterface, const int32_t tabIndex); void validateYokingChange(Overlay* overlay); // ADD_NEW_METHODS_HERE signals: void itemActivated(); private slots: void comboBoxActivated(); private: enum YokeValidationResult { YOKE_VALIDATE_RESULT_ACCEPT, YOKE_VALIDATE_RESULT_OFF, YOKE_VALIDATE_RESULT_PREVIOUS }; MapYokingGroupComboBox(const MapYokingGroupComboBox&); MapYokingGroupComboBox& operator=(const MapYokingGroupComboBox&); YokeValidationResult validateYoking(CaretMappableDataFile* selectedFile, int32_t& selectedMapIndexInOut, bool& selectionStatusInOut); EnumComboBoxTemplate* m_comboBox; // ADD_NEW_MEMBERS_HERE }; #ifdef __MAP_YOKING_GROUP_COMBO_BOX_DECLARE__ // #endif // __MAP_YOKING_GROUP_COMBO_BOX_DECLARE__ } // namespace #endif //__MAP_YOKING_GROUP_COMBO_BOX_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/GuiQt/MetaDataEditorDialog.cxx000066400000000000000000000077311300200146000263000ustar00rootroot00000000000000 /*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 __META_DATA_EDITOR_DIALOG_DECLARE__ #include "MetaDataEditorDialog.h" #undef __META_DATA_EDITOR_DIALOG_DECLARE__ #include #include #include #include #include "CaretAssert.h" #include "CaretDataFile.h" #include "CaretMappableDataFile.h" #include "GiftiMetaData.h" #include "MetaDataEditorWidget.h" #include "WuQMessageBox.h" using namespace caret; /** * \class caret::MetaDataEditorDialog * \brief Dialog for editing metadata. * \ingroup GuiQt */ /** * Constructor for editing a file's metadata. * * @param caretDataFile * Caret Data File that will have its metadata edited. * @param parent * Widget on which this dialog is displayed. */ MetaDataEditorDialog::MetaDataEditorDialog(CaretDataFile* caretDataFile, QWidget* parent) : WuQDialogModal("", parent) { CaretAssert(caretDataFile); initializeDialog(("Edit File Metadata: " + caretDataFile->getFileNameNoPath()), caretDataFile->getFileMetaData()); } /** * Constructor for editing a map's metadata. * * @param caretMappableDataFile * Caret Data File that will have its map's metadata edited. * @param mapIndex * Index of map that will have its metadata edited. * @param parent * Widget on which this dialog is displayed. */ MetaDataEditorDialog::MetaDataEditorDialog(CaretMappableDataFile* caretMappableDataFile, const int32_t mapIndex, QWidget* parent) : WuQDialogModal("", parent) { CaretAssert(caretMappableDataFile); const AString mapName = caretMappableDataFile->getMapName(mapIndex); initializeDialog(("Edit Map Metadata: " + mapName), caretMappableDataFile->getMapMetaData(mapIndex)); } /** * Destructor. */ MetaDataEditorDialog::~MetaDataEditorDialog() { } void MetaDataEditorDialog::initializeDialog(const AString& dialogTitle, GiftiMetaData* metaData) { CaretAssert(metaData); setWindowTitle(dialogTitle); m_metaDataEditorWidget = new MetaDataEditorWidget(this); setCentralWidget(m_metaDataEditorWidget, WuQDialog::SCROLL_AREA_NEVER); m_metaDataEditorWidget->loadMetaData(metaData); } /** * Called when OK button clicked. */ void MetaDataEditorDialog::okButtonClicked() { const AString errorMessage = m_metaDataEditorWidget->saveMetaData(); if (errorMessage.isEmpty() == false) { WuQMessageBox::errorOk(this, errorMessage); return; } WuQDialogModal::okButtonClicked(); } /** * Called when Cancel button clicked. */ void MetaDataEditorDialog::cancelButtonClicked() { if (m_metaDataEditorWidget->isMetaDataModified()) { const AString errorMessage = ("The metadata has been modified. Discard changes?"); if (WuQMessageBox::warningOkCancel(this, errorMessage)) { WuQDialogModal::cancelButtonClicked(); } } else { WuQDialogModal::cancelButtonClicked(); } } connectome-workbench-1.2.3+git41-gc4c6c90/src/GuiQt/MetaDataEditorDialog.h000066400000000000000000000042421300200146000257170ustar00rootroot00000000000000#ifndef __META_DATA_EDITOR_DIALOG_H__ #define __META_DATA_EDITOR_DIALOG_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 "WuQDialogModal.h" namespace caret { class CaretDataFile; class CaretMappableDataFile; class GiftiMetaData; class MetaDataEditorWidget; class MetaDataEditorDialog : public WuQDialogModal { Q_OBJECT public: MetaDataEditorDialog(CaretDataFile* caretDataFile, QWidget* parent); MetaDataEditorDialog(CaretMappableDataFile* caretMappableDataFile, const int32_t mapIndex, QWidget* parent); virtual ~MetaDataEditorDialog(); virtual void okButtonClicked(); virtual void cancelButtonClicked(); private: MetaDataEditorDialog(const MetaDataEditorDialog&); MetaDataEditorDialog& operator=(const MetaDataEditorDialog&); void initializeDialog(const AString& dialogTitle, GiftiMetaData* metaData); MetaDataEditorWidget* m_metaDataEditorWidget; // ADD_NEW_MEMBERS_HERE }; #ifdef __META_DATA_EDITOR_DIALOG_DECLARE__ // #endif // __META_DATA_EDITOR_DIALOG_DECLARE__ } // namespace #endif //__META_DATA_EDITOR_DIALOG_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/GuiQt/MetaDataEditorWidget.cxx000066400000000000000000000352431300200146000263230ustar00rootroot00000000000000 /*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 __META_DATA_EDITOR_WIDGET_DECLARE__ #include "MetaDataEditorWidget.h" #undef __META_DATA_EDITOR_WIDGET_DECLARE__ #include #include #include #include #include #include #include #include #include #include #include #include #include "CaretAssert.h" #include "GiftiMetaData.h" #include "WuQDataEntryDialog.h" #include "WuQWidgetObjectGroup.h" using namespace caret; /** * \class caret::MetaDataEditorWidget * \brief Widget for editing GIFTI MetaData. * \ingroup GuiQt */ /** * Constructor. * @param parent * Parent widget. */ MetaDataEditorWidget::MetaDataEditorWidget(QWidget* parent) : QWidget(parent) { m_metaDataBeingEdited = NULL; m_deleteActionSignalMapper = new QSignalMapper(); QObject::connect(m_deleteActionSignalMapper, SIGNAL(mapped(int)), this, SLOT(deleteActionTriggered(int))); m_newPushButton = new QPushButton("New..."); QObject::connect(m_newPushButton, SIGNAL(clicked()), this, SLOT(newPushButtonClicked())); QVBoxLayout* buttonsLayout = new QVBoxLayout(); buttonsLayout->addStretch(); buttonsLayout->addWidget(m_newPushButton); buttonsLayout->addSpacing(10); m_metaGridLayout = new QGridLayout(); m_metaGridLayout->addWidget(new QLabel("Delete"), 0, COLUMN_DELETE, Qt::AlignCenter); m_metaGridLayout->addWidget(new QLabel("Name"), 0, COLUMN_NAME, Qt::AlignCenter); m_metaGridLayout->addWidget(new QLabel("Value"), 0, COLUMN_VALUE, Qt::AlignCenter); QWidget* metaDataWidget = new QWidget(); QVBoxLayout* metaDataWidgetLayout = new QVBoxLayout(metaDataWidget); metaDataWidgetLayout->addLayout(m_metaGridLayout); metaDataWidgetLayout->addStretch(); m_metaDataNameValueScrollArea = new QScrollArea(); m_metaDataNameValueScrollArea->setHorizontalScrollBarPolicy(Qt::ScrollBarAsNeeded); m_metaDataNameValueScrollArea->setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded); m_metaDataNameValueScrollArea->setWidget(metaDataWidget); m_metaDataNameValueScrollArea->setWidgetResizable(true); m_metaDataNameValueScrollArea->setFrameShape(QFrame::NoFrame); QHBoxLayout* dialogLayout = new QHBoxLayout(this); dialogLayout->addWidget(m_metaDataNameValueScrollArea, 100); dialogLayout->addLayout(buttonsLayout, 0); setMinimumWidth(600); setMinimumHeight(260); } /** * Destructor. */ MetaDataEditorWidget::~MetaDataEditorWidget() { } /** * Called when Add push button is clicked. */ void MetaDataEditorWidget::newPushButtonClicked() { WuQDataEntryDialog ded("New Metadata Name", m_newPushButton); m_newNameDialogLineEdit = ded.addLineEditWidget("New MetaData Name"); QObject::connect(&ded, SIGNAL(validateData(WuQDataEntryDialog*)), this, SLOT(validateNewName(WuQDataEntryDialog*))); if (ded.exec() == WuQDataEntryDialog::Accepted) { readNamesAndValues(); m_namesAndValues.push_back(std::make_pair(m_newNameDialogLineEdit->text().trimmed(), "")); displayNamesAndValues(); const int32_t numNames = static_cast(m_namesAndValues.size()); m_metaDataWidgetRows[numNames - 1]->m_valueLineEdit->setFocus(); m_metaDataNameValueScrollArea->ensureWidgetVisible(m_metaDataWidgetRows[numNames-1]->m_nameLineEdit); } } /** * Validate the new name from new name dialog. * @param dataEntryDialog * The dialog used for entry of new name. */ void MetaDataEditorWidget::validateNewName(WuQDataEntryDialog* dataEntryDialog) { const AString newName = m_newNameDialogLineEdit->text().trimmed(); if (newName.isEmpty()) { dataEntryDialog->setDataValid(false, "Name may not be empty."); return; } std::set allNames; getNamesInDialog(allNames, NULL, NULL); if (std::find(allNames.begin(), allNames.end(), newName) != allNames.end()) { const AString msg = ("Name \"" + newName + "\" already exists. Duplicate names are not allowed."); dataEntryDialog->setDataValid(false, Qt::convertFromPlainText(msg)); return; } dataEntryDialog->setDataValid(true, ""); } /** * Load the given metadata in this widget. * * @param metaData * Metadata that displayed in widget. */ void MetaDataEditorWidget::loadMetaData(GiftiMetaData* metaData) { CaretAssert(metaData); m_metaDataBeingEdited = metaData; std::vector metaDataNames = m_metaDataBeingEdited->getAllMetaDataNames(); const int32_t numMetaData = static_cast(metaDataNames.size()); /* * Get names and values */ m_unmodifiedNamesAndValues.clear(); m_namesAndValues.clear(); for (int32_t iRow = 0; iRow < numMetaData; iRow++) { const AString name = metaDataNames[iRow].trimmed(); const AString value = m_metaDataBeingEdited->get(name).trimmed(); m_namesAndValues.push_back(std::make_pair(name, value)); m_unmodifiedNamesAndValues.insert(std::make_pair(name, value)); } displayNamesAndValues(); } /** * Display the names and values. */ void MetaDataEditorWidget::displayNamesAndValues() { const int32_t numNamesAndValues = static_cast(m_namesAndValues.size()); int32_t numWidgetRows = static_cast(m_metaDataWidgetRows.size()); /* * Update existing rows and add new rows as needed. */ for (int32_t iRow = 0; iRow < numNamesAndValues; iRow++) { const AString name = m_namesAndValues[iRow].first; const AString value = m_namesAndValues[iRow].second; MetaDataWidgetRow* widgetsRow = NULL; if (iRow < numWidgetRows) { widgetsRow = m_metaDataWidgetRows[iRow]; } else { widgetsRow = new MetaDataWidgetRow(this, m_deleteActionSignalMapper, iRow); const int layoutRow = m_metaGridLayout->rowCount(); m_metaGridLayout->addWidget(widgetsRow->m_deleteToolButton, layoutRow, COLUMN_DELETE, Qt::AlignCenter); m_metaGridLayout->addWidget(widgetsRow->m_nameLineEdit, layoutRow, COLUMN_NAME); m_metaGridLayout->addWidget(widgetsRow->m_valueLineEdit, layoutRow, COLUMN_VALUE); m_metaDataWidgetRows.push_back(widgetsRow); } widgetsRow->m_nameLineEdit->setText(name); widgetsRow->m_valueLineEdit->setText(value); widgetsRow->m_widgetGroup->setVisible(true); } /* * Hide rows that are no longer used. */ numWidgetRows = static_cast(m_metaDataWidgetRows.size()); for (int32_t iRow = numNamesAndValues; iRow < numWidgetRows; iRow++) { m_metaDataWidgetRows[iRow]->m_widgetGroup->setVisible(false); } } /** * Read the names and values from the GUI. */ void MetaDataEditorWidget::readNamesAndValues() { m_namesAndValues.clear(); /* * Read from the rows. */ int32_t numWidgetRows = static_cast(m_metaDataWidgetRows.size()); for (int32_t iRow = 0; iRow < numWidgetRows; iRow++) { MetaDataWidgetRow* widgetRow = m_metaDataWidgetRows[iRow]; if (widgetRow->m_widgetGroup->isVisible()) { m_namesAndValues.push_back(std::make_pair(widgetRow->m_nameLineEdit->text().trimmed(), widgetRow->m_valueLineEdit->text().trimmed())); } } } /** * Get the names that are listed in the editor. * * @param namesOut * Output will contain all unique names. * @param duplicateNamesOut * If not NULL, any duplicate names will be placed here. * @param haveEmptyNamesOut * If not NULL, will be true if any empty names were found. * @return true if all names valid, else false. */ bool MetaDataEditorWidget::getNamesInDialog(std::set& namesOut, std::set* duplicateNamesOut, bool* haveEmptyNamesOut) { readNamesAndValues(); namesOut.clear(); if (duplicateNamesOut != NULL) { duplicateNamesOut->clear(); } if (haveEmptyNamesOut != NULL) { *haveEmptyNamesOut = false; } bool allValidNames = true; const int32_t numItems = static_cast(m_namesAndValues.size()); for (int32_t i = 0; i < numItems; i++) { const AString name = m_namesAndValues[i].first; if (name.isEmpty()) { if (haveEmptyNamesOut != NULL) { *haveEmptyNamesOut = true; } allValidNames = false; } else if (std::find(namesOut.begin(), namesOut.end(), name) != namesOut.end()) { if (duplicateNamesOut != NULL) { duplicateNamesOut->insert(name); } allValidNames = false; } else { namesOut.insert(name); } } return allValidNames; } /** * Transfer values in dialog into metadata. * * @return Empty string if no errors, otherwise error message. */ AString MetaDataEditorWidget::saveMetaData() { if (isMetaDataModified() == false) { return ""; } readNamesAndValues(); std::set allNames; std::set duplicateNames; bool haveEmptyNames; const bool valid = getNamesInDialog(allNames, &duplicateNames, &haveEmptyNames); if (valid) { m_metaDataBeingEdited->clear(); const int32_t numItems = static_cast(m_namesAndValues.size()); for (int32_t i = 0; i < numItems; i++) { const AString name = m_namesAndValues[i].first; const AString value = m_namesAndValues[i].second; m_metaDataBeingEdited->set(name, value); } return ""; } AString errorMessage = ""; if (haveEmptyNames) { errorMessage.appendWithNewLine("Empty names are not allowed."); } if (duplicateNames.empty() == false) { errorMessage.appendWithNewLine("Duplicate names are not allowed:"); for (std::set::iterator iter = duplicateNames.begin(); iter != duplicateNames.end(); iter++) { errorMessage.appendWithNewLine(" " + *iter); } } return errorMessage; } /** * @return true if the names and values been modified, else false. */ bool MetaDataEditorWidget::isMetaDataModified() { readNamesAndValues(); const int32_t numItems = static_cast(m_namesAndValues.size()); if (numItems != static_cast(m_unmodifiedNamesAndValues.size())) { return true; } std::map nameValueMap; for (int32_t i = 0; i < numItems; i++) { nameValueMap.insert(std::make_pair(m_namesAndValues[i].first, m_namesAndValues[i].second)); } const bool theSame = std::equal(m_unmodifiedNamesAndValues.begin(), m_unmodifiedNamesAndValues.end(), nameValueMap.begin()); return (theSame == false); } /** * Called when a delete tool button is clicked. */ void MetaDataEditorWidget::deleteActionTriggered(int indx) { readNamesAndValues(); CaretAssertVectorIndex(m_namesAndValues, indx); m_namesAndValues.erase(m_namesAndValues.begin() + indx); displayNamesAndValues(); } /** * Constructor. * @param parent * The parent widget. * @param deleteActionSignalMapper * The signal mapper for delete action. * @param rowIndex * Row index of widgets and used by signal mapper. */ MetaDataEditorWidget::MetaDataWidgetRow::MetaDataWidgetRow(QWidget* parent, QSignalMapper* deleteActionSignalMapper, const int32_t rowIndex) { QAction* deleteAction = new QAction("X", parent); QObject::connect(deleteAction, SIGNAL(triggered(bool)), deleteActionSignalMapper, SLOT(map())); deleteActionSignalMapper->setMapping(deleteAction, rowIndex); m_deleteToolButton = new QToolButton(); m_deleteToolButton->setDefaultAction(deleteAction); const int minWidth = 150; m_nameLineEdit = new QLineEdit(); m_nameLineEdit->setMinimumWidth(minWidth); m_valueLineEdit = new QLineEdit(); m_valueLineEdit->setMinimumWidth(minWidth); m_widgetGroup = new WuQWidgetObjectGroup(parent); m_widgetGroup->add(deleteAction); m_widgetGroup->add(m_deleteToolButton); m_widgetGroup->add(m_nameLineEdit); m_widgetGroup->add(m_valueLineEdit); } MetaDataEditorWidget::MetaDataWidgetRow::~MetaDataWidgetRow() { } connectome-workbench-1.2.3+git41-gc4c6c90/src/GuiQt/MetaDataEditorWidget.h000066400000000000000000000071111300200146000257410ustar00rootroot00000000000000#ifndef __META_DATA_EDITOR_WIDGET_H__ #define __META_DATA_EDITOR_WIDGET_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 "AString.h" class QAction; class QGridLayout; class QLineEdit; class QPushButton; class QScrollArea; class QSignalMapper; class QToolButton; namespace caret { class GiftiMetaData; class WuQDataEntryDialog; class WuQWidgetObjectGroup; class MetaDataEditorWidget : public QWidget { Q_OBJECT public: MetaDataEditorWidget(QWidget* parent); virtual ~MetaDataEditorWidget(); void loadMetaData(GiftiMetaData* metaData); bool isMetaDataModified(); AString saveMetaData(); private slots: void newPushButtonClicked(); void deleteActionTriggered(int indx); void validateNewName(WuQDataEntryDialog* dataEntryDialog); private: MetaDataEditorWidget(const MetaDataEditorWidget&); MetaDataEditorWidget& operator=(const MetaDataEditorWidget&); enum { COLUMN_DELETE = 0, COLUMN_NAME = 1, COLUMN_VALUE = 2 }; class MetaDataWidgetRow { public: MetaDataWidgetRow(QWidget* parent, QSignalMapper* deleteActionSignalMapper, const int32_t indx); ~MetaDataWidgetRow(); QToolButton* m_deleteToolButton; QLineEdit* m_nameLineEdit; QLineEdit* m_valueLineEdit; WuQWidgetObjectGroup* m_widgetGroup; }; void displayNamesAndValues(); void readNamesAndValues(); bool getNamesInDialog(std::set& namesOut, std::set* duplicateNamesOut, bool* haveEmptyNamesOut); std::vector > m_namesAndValues; std::map m_unmodifiedNamesAndValues; /** Metadata that is being edited */ GiftiMetaData* m_metaDataBeingEdited; QGridLayout* m_metaGridLayout; std::vector m_metaDataWidgetRows; QSignalMapper* m_deleteActionSignalMapper; QPushButton* m_newPushButton; QLineEdit* m_newNameDialogLineEdit; QScrollArea* m_metaDataNameValueScrollArea; // ADD_NEW_MEMBERS_HERE }; #ifdef __META_DATA_EDITOR_WIDGET_DECLARE__ // #endif // __META_DATA_EDITOR_WIDGET_DECLARE__ } // namespace #endif //__META_DATA_EDITOR_WIDGET_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/GuiQt/MouseEvent.cxx000066400000000000000000000202021300200146000244070ustar00rootroot00000000000000/*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 "BrainOpenGLViewportContent.h" #include "BrainOpenGLWidget.h" #include "CaretAssert.h" #include "MouseEvent.h" using namespace caret; /** * \class caret::MouseEvent * \brief Event issued when mouse is moved or buttons are pressed. * \ingroup GuiQt */ /** * Constructor. * @param viewportContent * Content of viewport. * @param openGLWidget * OpenGL Widget in which mouse activity took place. * @param browserWindowIndex * Index of the browser winddow in which mouse activity took place. * @param x * Current mouse X-coordinate (left == 0) * @param y * Current mouse Y-coordinate (bottom == 0) * @param dx * Change in mouse X-coordinate since last mouse event * @param dy * Change in mouse Y-coordinate since last mouse event * @param mousePressX * X-coordinate of mouse when button was pressed * @param mousePressY * Y-coordinate of mouse when button was pressed * @param firstDraggingFlag * Should be true the first time in in a mouse dragging operation. */ MouseEvent::MouseEvent(const BrainOpenGLViewportContent* viewportContent, BrainOpenGLWidget* openGLWidget, const int32_t browserWindowIndex, const int32_t x, const int32_t y, const int32_t dx, const int32_t dy, const int32_t mousePressX, const int32_t mousePressY, const bool firstDraggingFlag) : CaretObject() { initializeMembersMouseEvent(); /* * MUST copy viewport content as it may be deleted by caller * prior to this instance being deleted */ m_viewportContent = NULL; if (viewportContent != NULL) { m_viewportContent = new BrainOpenGLViewportContent(*viewportContent); } m_openGLWidget = openGLWidget; m_browserWindowIndex = browserWindowIndex; m_x = x; m_y = y; m_dx = dx; m_dy = dy; m_pressX = mousePressX; m_pressY = mousePressY; m_firstDraggingFlag = firstDraggingFlag; } /** * Destructor */ MouseEvent::~MouseEvent() { if (m_viewportContent != NULL) { delete m_viewportContent; } } /** * Copy constructor. * @param obj * Object that is copied. */ MouseEvent::MouseEvent(const MouseEvent& obj) : CaretObject(obj) { this->initializeMembersMouseEvent(); this->copyHelperMouseEvent(obj); } /** * Assignment operator. * @param obj * Data copied from obj to this. * @return * Reference to this object. */ MouseEvent& MouseEvent::operator=(const MouseEvent& obj) { if (this != &obj) { CaretObject::operator=(obj); this->copyHelperMouseEvent(obj); } return *this; } /** * Helps with copying an object of this type. * @param obj * Object that is copied. */ void MouseEvent::copyHelperMouseEvent(const MouseEvent& obj) { /* * MUST copy viewport content as it may be deleted */ if (m_viewportContent != NULL) { delete m_viewportContent; m_viewportContent = NULL; } CaretAssert(obj.m_viewportContent); if (obj.m_viewportContent != NULL) { m_viewportContent = new BrainOpenGLViewportContent(*obj.m_viewportContent); } m_openGLWidget = obj.m_openGLWidget; m_browserWindowIndex = obj.m_browserWindowIndex; m_x = obj.m_x; m_y = obj.m_y; m_dx = obj.m_dx; m_dy = obj.m_dy; m_pressX = obj.m_pressX; m_pressY = obj.m_pressY; m_wheelRotation = obj.m_wheelRotation; m_firstDraggingFlag = obj.m_firstDraggingFlag; } /** * Initialize all members. */ void MouseEvent::initializeMembersMouseEvent() { m_viewportContent = NULL; m_openGLWidget = NULL; m_browserWindowIndex = -1; m_x = 0; m_y = 0; m_dx = 0; m_dy = 0; m_pressX = 0; m_pressY = 0; m_wheelRotation = 0; m_firstDraggingFlag = false; } /** * @return The viewport content in which the mouse was pressed. */ BrainOpenGLViewportContent* MouseEvent::getViewportContent() const { return m_viewportContent; } /** * @return The OpenGL Widget in which the mouse event occurred. */ BrainOpenGLWidget* MouseEvent::getOpenGLWidget() const { return m_openGLWidget; } /** * Get a string showing the contents of this mouse event. * @return String describing the mouse status. */ AString MouseEvent::toString() const { const AString msg = ", x=" + AString::number(m_x) + ", y=" + AString::number(m_y) + ", dx=" + AString::number(m_dx) + ", dy=" + AString::number(m_dy); + ", pressX=" + AString::number(m_pressX) + ", pressY=" + AString::number(m_pressY); + ", wheelRotation=" + AString::number(m_wheelRotation); return msg; } /** * @return Index of the browser window in which the * event took place. */ int32_t MouseEvent::getBrowserWindowIndex() const { return m_browserWindowIndex; } /** * Get the change in the X-coordinate. * @return change in the X-coordinate. * */ int32_t MouseEvent::getDx() const { return m_dx; } /** * Get the change in the Y-coordinate. * @return change in the Y-coordinate. * */ int32_t MouseEvent::getDy() const { return m_dy; } /** * Get the X-coordinate of the mouse. * @return The X-coordinate. * */ int32_t MouseEvent::getX() const { return m_x; } /** * Get the Y-coordinate of the mouse. * Origin is at the BOTTOM of the widget !!! * @return The Y-coordinate. * */ int32_t MouseEvent::getY() const { return m_y; } /** * Get the X-coordinate of where the mouse was pressed. * @return The X-coordinate. * */ int32_t MouseEvent::getPressedX() const { return m_pressX; } /** * Get the Y-coordinate of where the mouse was pressed. * Origin is at the BOTTOM of the widget !!! * @return The Y-coordinate. * */ int32_t MouseEvent::getPressedY() const { return m_pressY; } /** * Get the Global X-coordinate of where the mouse was pressed. * @return The Global X-coordinate. * * @param x * X-coordinate in widget (0 is left side of widget) * @param y * Y-coordinate in widget (0 is bottom side of widget) * @param outGlobalX * Output with global X-coordinate * @param outGlobalY * Output with global Y-coordinate */ void MouseEvent::getGlobalXY(const int32_t x, const int32_t y, int32_t& outGlobalX, int32_t& outGlobalY) const { CaretAssert(m_openGLWidget); const int32_t yOriginTop = m_openGLWidget->height() - y; const QPoint globalPoint = m_openGLWidget->mapToGlobal(QPoint(x, yOriginTop)); outGlobalX = globalPoint.x(); outGlobalY = globalPoint.y(); } /** * Get the amount of rotation in the mouse wheel. * @return Amount mouse wheel rotated. * */ int32_t MouseEvent::getWheelRotation() const { return m_wheelRotation; } /** * @return Is this the first in a sequence of mouse dragging? * * A mouse drag is the sequence: * (1) User presses the mouse * (2) One or more calls are made to the input receivers mouseLeftDrag() method * (3) User releases the mouse ending the dragging. * * This method returns true for the first call made to mouseLeftDrag() in * step 2 and false in all other calls to mouseLeftDrag(). */ bool MouseEvent::isFirstDragging() const { return m_firstDraggingFlag; } connectome-workbench-1.2.3+git41-gc4c6c90/src/GuiQt/MouseEvent.h000066400000000000000000000062311300200146000240420ustar00rootroot00000000000000#ifndef __MouseEvent_H__ #define __MouseEvent_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 class QMouseEvent; namespace caret { class BrainOpenGLViewportContent; class BrainOpenGLWidget; /** * Contains information about a mouse event in the OpenGL region. */ class MouseEvent : public CaretObject { public: MouseEvent(const BrainOpenGLViewportContent* viewportContent, BrainOpenGLWidget* openGLWidget, const int32_t browserWindowIndex, const int32_t x, const int32_t y, const int32_t dx, const int32_t dy, const int32_t mousePressX, const int32_t mousePressY, const bool firstDraggingFlag); virtual ~MouseEvent(); MouseEvent(const MouseEvent& o); MouseEvent& operator=(const MouseEvent& o); private: void initializeMembersMouseEvent(); void copyHelperMouseEvent(const MouseEvent& me); public: AString toString() const; BrainOpenGLViewportContent* getViewportContent() const; BrainOpenGLWidget* getOpenGLWidget() const; int32_t getBrowserWindowIndex() const; int32_t getDx() const; int32_t getDy() const; int32_t getX() const; int32_t getY() const; int32_t getPressedX() const; int32_t getPressedY() const; void getGlobalXY(const int32_t x, const int32_t y, int32_t& outGlobalX, int32_t& outGlobalY) const; int32_t getWheelRotation() const; bool isFirstDragging() const; private: BrainOpenGLViewportContent* m_viewportContent; BrainOpenGLWidget* m_openGLWidget; int32_t m_browserWindowIndex; int32_t m_x; int32_t m_y; int32_t m_dx; int32_t m_dy; int32_t m_pressX; int32_t m_pressY; int32_t m_wheelRotation; bool m_firstDraggingFlag; }; } // namespace #endif // __MouseEvent_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/GuiQt/MovieDialog.cxx000066400000000000000000000721461300200146000245320ustar00rootroot00000000000000/*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 "MovieDialog.h" #include "ui_MovieDialog.h" #include "BrowserTabContent.h" #include "BrainBrowserWindow.h" #include "CaretAssert.h" #include "CaretLogger.h" #include "CaretPreferences.h" #include "DataFileException.h" #include "EventManager.h" #include "EventGraphicsUpdateAllWindows.h" #include "EventGraphicsUpdateOneWindow.h" #include "EventImageCapture.h" #include "EventUserInterfaceUpdate.h" #include "GuiManager.h" #include "ImageFile.h" #include #include "Model.h" #include "ModelSurface.h" #include "ModelSurfaceSelector.h" #include "SessionManager.h" #include "Surface.h" #include "WuQMessageBox.h" #include #include #include using namespace caret; /** * \class caret::MovieDialog * \brief Dialog used for setting up and rendering Movies. * \ingroup GuiQt */ MovieDialog::MovieDialog(QWidget *parent) : QDialog(parent), ui(new Ui::MovieDialog) { ui->setupUi(this); EventManager::get()->addEventListener(this, EventTypeEnum::EVENT_GRAPHICS_UPDATE_ONE_WINDOW); EventManager::get()->addEventListener(this, EventTypeEnum::EVENT_GRAPHICS_UPDATE_ALL_WINDOWS); m_browserWindowIndex = 0; frame_number = 0; rotate_frame_number = 0; imageX = 0; imageY = 0; m_animationStarted = false; m_volumeSliceIncrement = 0; m_reverseVolumeSliceDirection = false; m_PStart = 0; m_CStart = 0; m_AStart = 0; m_PEnd = 0; m_CEnd = 0; m_AEnd = 0; m_interpolationIndex = 0; m_surface = NULL; m_surface1 = NULL; m_surface2 = NULL; } MovieDialog::~MovieDialog() { ui->animateButton->setChecked(false); delete ui; EventManager::get()->removeAllEventsFromListener(this); } void MovieDialog::on_closeButton_clicked() { ui->animateButton->setChecked(false); this->close(); } void MovieDialog::on_interpolateSurfaceCheckbox_toggled(bool checked) { if ( ! checked) { return; } BrainBrowserWindow *bw = GuiManager::get()->getBrowserWindowByWindowIndex(m_browserWindowIndex); if(!bw) { WuQMessageBox::errorOk(this, "Invalid browser window index, " + AString::number(m_browserWindowIndex)); return; } BrowserTabContent *btc1 = bw->getBrowserTabContent(0); BrowserTabContent *btc2 = bw->getBrowserTabContent(1); if(!btc1 || !btc2) { this->ui->interpolateSurfaceCheckbox->blockSignals(true); this->ui->interpolateSurfaceCheckbox->setChecked(false); this->ui->interpolateSurfaceCheckbox->blockSignals(false); WuQMessageBox::errorOk(this, "There must be two browser tabs."); return; } // int32_t tabIndex1 = btc1->getTabNumber(); if ((btc1->getSelectedModelType() != ModelTypeEnum::MODEL_TYPE_SURFACE) || (btc2->getSelectedModelType() != ModelTypeEnum::MODEL_TYPE_SURFACE)) { this->ui->interpolateSurfaceCheckbox->blockSignals(true); this->ui->interpolateSurfaceCheckbox->setChecked(false); this->ui->interpolateSurfaceCheckbox->blockSignals(false); WuQMessageBox::errorOk(this, "Both Tab 1 and Tab 2 must contain surface models."); return; } ModelSurface *ms1 = btc1->getDisplayedSurfaceModel(); // int32_t tabIndex2 = btc2->getTabNumber(); ModelSurface *ms2 = btc2->getDisplayedSurfaceModel(); if(!(ms1&&ms2)) return; if (ms1->getSurface()->getStructure() != ms2->getSurface()->getStructure()) { this->ui->interpolateSurfaceCheckbox->blockSignals(true); this->ui->interpolateSurfaceCheckbox->setChecked(false); this->ui->interpolateSurfaceCheckbox->blockSignals(false); WuQMessageBox::errorOk(this, "Surfaces in Tab 1 and Tab 2 must be the same structure."); return; } } void MovieDialog::on_animateButton_toggled(bool checked) { // this->renderMovieButton->setChecked(status); dx = this->ui->rotateXSpinBox->value(); dy = this->ui->rotateYSpinBox->value(); dz = this->ui->rotateZSpinBox->value(); frameCount = this->ui->rotateFrameCountSpinBox->value(); reverseDirection = this->ui->reverseDirectionCheckBox->isChecked(); frameCountEnabled = this->ui->rotateFrameCountSpinBox->value() ? true : false; m_interpolationEnabled = this->ui->interpolateSurfaceCheckbox->isChecked(); m_interpolationSteps = this->ui->interpolationStepsSpinBox->value(); if(checked) { //this->renderMovieButton->setText("Stop"); this->m_animationStarted = true; this->m_isInterpolating = true; while(this->ui->animateButton->isChecked()) { EventManager::get()->sendEvent(EventGraphicsUpdateAllWindows(true).getPointer()); QCoreApplication::instance()->processEvents(); m_animationStarted = false; } this->m_animationStarted = false; } else { //this->renderMovieButton->setText("Play"); this->CleanupInterpolation(); EventManager::get()->sendEvent(EventGraphicsUpdateAllWindows(true).getPointer()); } rotate_frame_number = 0; } void MovieDialog::on_recordButton_toggled(bool checked) { if(checked) { m_useCustomSize = ui->customSizeRadioButton->isChecked(); imageX = 0; imageY = 0; if(m_useCustomSize) { imageX = ui->windowWidthSpinBox->value(); imageY = ui->windowHeightSpinBox->value(); } } if(!checked&&(frame_number > 0)) { //render frames.... QString formatString("Movie Files (*.mpg *.mp4)"); AString fileName = QFileDialog::getSaveFileName( this, tr("Save File"),QString::null, formatString ); AString tempDir = QDir::tempPath(); if ( !fileName.isEmpty() ) { // int crop[4]; // crop[0] = imageX; // crop[1] = imageY; // crop[2] = 0; // crop[3] = 0; // if(!(crop[0]&&crop[1])) GuiManager::get()->getBrowserWindowByWindowIndex(m_browserWindowIndex)->getViewportSize(crop[0],crop[1]); unlink(fileName); CaretLogInfo("Rendering movie to:" + fileName); AString ffmpeg = SystemUtilities::getWorkbenchHome() + AString("/ffmpeg "); // JWH ffmpeg = "/mnt/myelin/distribution/caret7_distribution/workbench//macosx64_apps/ffmpeg "; double frame_rate = 30.0/double(1 + this->ui->repeatFramesSpinBox->value()); // if(ui->cropImageCheckBox->isChecked()) // { // this->getImageCrop(tempDir + "/movie0" + AString(".png"),crop); // } // // crop[0] = (crop[0]/2)*2; // crop[1] = (crop[1]/2)*2; // CaretLogInfo("Resizing image from " + AString::number(imageX) + AString(":") + AString::number(imageY) + AString(" to ") + // AString::number(crop[0]) + AString(":") + AString::number(crop[1])); // // AString command = ffmpeg + AString("-threads 4 -r " + AString::number(frame_rate) + " -i "+ tempDir + "/movie%d.png -r 30 -q:v 1 -vf crop=" + // AString::number(crop[0]) + ":" + AString::number(crop[1]) + ":" + // AString::number(crop[2]) + ":" + AString::number(crop[3]) + " " + fileName); AString command = ffmpeg + AString("-threads 4 -r " + AString::number(frame_rate) + " -i "+ tempDir + "/movie%d.png -r 30 -q:v 1 " + fileName); CaretLogFine("running " + command); system(command.toAscii().data()); CaretLogFine("Finished rendering " + fileName); } for(int i = 0;iui->marginSpinBox->value(); // CaretPreferences* prefs = SessionManager::get()->getCaretPreferences(); // uint8_t backgroundColor[3]; // prefs->getColorBackground(backgroundColor); // // ImageFile file; // file.readFile(fileName); // // int leftTopRightBottom[4]; // file.findImageObject(backgroundColor,leftTopRightBottom); // // const int width = leftTopRightBottom[2] - leftTopRightBottom[0] + 1; // const int height = leftTopRightBottom[3] - leftTopRightBottom[1] + 1; // cropOut[0] = width + 2*marginSize; // cropOut[1] = height + 2*marginSize; // cropOut[2] = leftTopRightBottom[0]+marginSize; // cropOut[3] = leftTopRightBottom[1]+marginSize; //} void MovieDialog::processRotateTransformation(const double dx, const double dy, const double dz) { BrowserTabContent* browserTabContent = GuiManager::get()->getBrowserTabContentForBrowserWindow(m_browserWindowIndex, true); if (browserTabContent == NULL) { return; } Model* modelController = browserTabContent->getModelForDisplay(); if (modelController != NULL) { // const int32_t tabIndex = browserTabContent->getTabNumber(); { Matrix4x4 rotationMatrix = browserTabContent->getRotationMatrix(); rotationMatrix.rotateX(dx); rotationMatrix.rotateY(dy); rotationMatrix.rotateZ(dz); browserTabContent->setRotationMatrix(rotationMatrix); // /* // * There are several rotation matrix. The 'NORMAL' matrix is used // * in most cases and others are used in special viewing modes // * such as surface montage and right/left lateral medial yoking // */ // if (browserTabContent->isDisplayedModelSurfaceRightLateralMedialYoked()) { // Matrix4x4* rotationMatrix = modelController->getViewingRotationMatrix(tabIndex, // Model::VIEWING_TRANSFORM_NORMAL); // rotationMatrix->rotateX(dx); // rotationMatrix->rotateY(dy); // rotationMatrix->rotateZ(dz); // // /* // * Matrix for a right medial/lateral yoked surface // */ // Matrix4x4* rotationMatrixRightLatMedYoked = modelController->getViewingRotationMatrix(tabIndex, // Model::VIEWING_TRANSFORM_RIGHT_LATERAL_MEDIAL_YOKED); // rotationMatrixRightLatMedYoked->rotateX(-dx); // rotationMatrixRightLatMedYoked->rotateY(-dy); // rotationMatrixRightLatMedYoked->rotateZ(-dz); // } // else { // // // Matrix4x4* rotationMatrix = modelController->getViewingRotationMatrix(tabIndex, // Model::VIEWING_TRANSFORM_NORMAL); // rotationMatrix->rotateX(-dx); // rotationMatrix->rotateY(dy); // rotationMatrix->rotateZ(dz); // // /* // * Matrix for a left surface opposite view in surface montage // */ // Matrix4x4* rotationMatrixSurfMontLeftOpp = modelController->getViewingRotationMatrix(tabIndex, // Model::VIEWING_TRANSFORM_SURFACE_MONTAGE_LEFT_OPPOSITE); // rotationMatrixSurfMontLeftOpp->rotateX(-dx); // rotationMatrixSurfMontLeftOpp->rotateY(dy); // rotationMatrixSurfMontLeftOpp->rotateZ(dz); // // /* // * Matrix for a right surface view in surface montage // */ // Matrix4x4* rotationMatrixSurfMontRight = modelController->getViewingRotationMatrix(tabIndex, // Model::VIEWING_TRANSFORM_SURFACE_MONTAGE_RIGHT); // rotationMatrixSurfMontRight->rotateX(dx); // rotationMatrixSurfMontRight->rotateY(-dy); // rotationMatrixSurfMontRight->rotateZ(dz); // // /* // * Matrix for a right surface opposite view in surface montage // */ // Matrix4x4* rotationMatrixSurfMontRightOpp = modelController->getViewingRotationMatrix(tabIndex, // Model::VIEWING_TRANSFORM_SURFACE_MONTAGE_RIGHT_OPPOSITE); // rotationMatrixSurfMontRightOpp->rotateX(dx); // rotationMatrixSurfMontRightOpp->rotateY(-dy); // rotationMatrixSurfMontRightOpp->rotateZ(dz); // // /* // * Matrix for a right medial/lateral yoked surface // */ // Matrix4x4* rotationMatrixRightLatMedYoked = modelController->getViewingRotationMatrix(tabIndex, // Model::VIEWING_TRANSFORM_RIGHT_LATERAL_MEDIAL_YOKED); // rotationMatrixRightLatMedYoked->rotateX(dx); // rotationMatrixRightLatMedYoked->rotateY(-dy); // rotationMatrixRightLatMedYoked->rotateZ(dz); // } } } } /** * Receive events from the event manager. * * @param event * Event sent by event manager. */ void MovieDialog::receiveEvent(Event* event) { if(event->getEventType() == EventTypeEnum::EVENT_GRAPHICS_UPDATE_ALL_WINDOWS || event->getEventType() == EventTypeEnum::EVENT_GRAPHICS_UPDATE_ONE_WINDOW ) { AString tempPath = QDir::tempPath(); if(this->ui->recordButton->isChecked()) { this->captureFrame(tempPath + AString("/movie") + AString::number(frame_number) + AString(".png")); AString temp = tempPath + AString("/movie") + AString::number(frame_number) + AString(".png"); CaretLogFine(temp); CaretLogFine("frame number:" + QString::number(frame_number)); frame_number++; } if(this->ui->animateButton->isChecked()) { this->processUpdateVolumeSlice(); this->processUpdateSurfaceInterpolation(); if(frameCountEnabled && frameCount) { if(!reverseDirection) { if(frameCount <= rotate_frame_number) { //this->ui->animateButton->setChecked(false); dx = dy = dz = 0.0; return; } } else { if(!(rotate_frame_number %frameCount) && (rotate_frame_number > 0)) { dx *= -1.0; dy *= -1.0; dz *= -1.0; } } } if(dx || dy || dz) { this->processRotateTransformation(dx, dy, dz); } rotate_frame_number++; } } } int32_t MovieDialog::getSliceDelta(const std::vector &dim, const caret::VolumeSliceViewPlaneEnum::Enum &vpe, const int32_t &sliceIndex) { if(m_animationStarted) { m_volumeSliceIncrement = ui->sliceIncrementCountSpinBox->value(); m_reverseVolumeSliceDirection = ui->reverseSliceDirectionCheckBox->isChecked(); m_sliceIncrementIsNegative = (m_volumeSliceIncrement != abs(m_volumeSliceIncrement)); switch(vpe) { case VolumeSliceViewPlaneEnum::AXIAL: if(m_sliceIncrementIsNegative) { m_AEnd = sliceIndex; m_AStart = 0>(m_AEnd+m_volumeSliceIncrement)?0:m_AEnd+m_volumeSliceIncrement; dA=-1; } else { m_AStart = sliceIndex; m_AEnd = (dim[2]-1)<(m_AStart+m_volumeSliceIncrement)?(dim[2]-1):m_AStart+m_volumeSliceIncrement; dA=1; } break; case VolumeSliceViewPlaneEnum::CORONAL: if(m_sliceIncrementIsNegative) { m_CEnd = sliceIndex; m_CStart = 0>(m_CEnd+m_volumeSliceIncrement)?0:m_CEnd+m_volumeSliceIncrement; dC=-1; } else { m_CStart = sliceIndex; m_CEnd = (dim[1]-1)<(m_CStart+m_volumeSliceIncrement)?(dim[1]-1):m_CStart+m_volumeSliceIncrement; dC=1; } break; case VolumeSliceViewPlaneEnum::PARASAGITTAL: if(m_sliceIncrementIsNegative) { m_PEnd = sliceIndex; m_PStart = 0>(m_PEnd+m_volumeSliceIncrement)?0:m_PEnd+m_volumeSliceIncrement; dP=-1; } else { m_PStart = sliceIndex; m_PEnd = (dim[0]-1)<(m_PStart+m_volumeSliceIncrement)?(dim[0]-1):m_PStart+m_volumeSliceIncrement; dP=1; } break; default: break; } } switch(vpe) { case VolumeSliceViewPlaneEnum::AXIAL: if(dA > 0 ) //the current increment is positive { if((m_AEnd-sliceIndex) > 0) //there is still room to increment by one { return dA; } else if(m_reverseVolumeSliceDirection) { return dA = -dA; } else { return 0; } } else if(dA < 0) { if((sliceIndex-m_AStart) > 0) { return dA; } else if(m_reverseVolumeSliceDirection) { return dA = -dA; } else { return 0; } } return dA; break; case VolumeSliceViewPlaneEnum::CORONAL: if(dC > 0 ) //the current increment is positive { if((m_CEnd-sliceIndex) > 0) //there is still room to increment by one { return dC; } else if(m_reverseVolumeSliceDirection) { return dC = -dC; } else { return 0; } } else if(dC < 0) { if((sliceIndex-m_CStart) > 0) { return dC; } else if(m_reverseVolumeSliceDirection) { return dC = -dC; } else { return 0; } } return dC; break; case VolumeSliceViewPlaneEnum::PARASAGITTAL: if(dP > 0 ) //the current increment is positive { if((m_PEnd-sliceIndex) > 0) //there is still room to increment by one { return dP; } else if(m_reverseVolumeSliceDirection) { return dP = -dP; } else { return 0; } } else if(dP < 0) { if((sliceIndex-m_PStart) > 0) { return dP; } else if(m_reverseVolumeSliceDirection) { return dP = -dP; } else { return 0; } } return dP; break; default: break; } return 0; } void MovieDialog::processUpdateVolumeSlice() { BrainBrowserWindow *bw = GuiManager::get()->getBrowserWindowByWindowIndex(m_browserWindowIndex); if(!bw) { CaretLogInfo("Invalid browser window index, " + AString::number(m_browserWindowIndex)); ui->animateButton->setChecked(false); return; } BrowserTabContent *btc = bw->getBrowserTabContent(); int32_t tabIndex = btc->getTabNumber(); ModelVolume* mv = btc->getDisplayedVolumeModel(); if(mv == NULL) { return; } switch (btc->getSliceProjectionType()) { case caret::VolumeSliceProjectionTypeEnum::VOLUME_SLICE_PROJECTION_OBLIQUE: return; break; case caret::VolumeSliceProjectionTypeEnum::VOLUME_SLICE_PROJECTION_ORTHOGONAL: break; } // VolumeSliceCoordinateSelection* vscs = btc->getSelectedVolumeSlices(); VolumeMappableInterface* vf = mv->getUnderlayVolumeFile(tabIndex); std::vector dim; vf->getDimensions(dim); VolumeSliceViewPlaneEnum::Enum vpe = btc->getSliceViewPlane(); if(vpe == VolumeSliceViewPlaneEnum::ALL || vpe == VolumeSliceViewPlaneEnum::AXIAL) { int64_t sliceIndex = btc->getSliceIndexAxial(vf); sliceIndex += this->getSliceDelta(dim,VolumeSliceViewPlaneEnum::AXIAL,sliceIndex); btc->setSliceIndexAxial(vf,sliceIndex); } if(vpe == VolumeSliceViewPlaneEnum::ALL || vpe == VolumeSliceViewPlaneEnum::CORONAL) { int64_t sliceIndex = btc->getSliceIndexCoronal(vf); sliceIndex += this->getSliceDelta(dim,VolumeSliceViewPlaneEnum::CORONAL,sliceIndex); btc->setSliceIndexCoronal(vf,sliceIndex); } if(vpe == VolumeSliceViewPlaneEnum::ALL || vpe == VolumeSliceViewPlaneEnum::PARASAGITTAL) { int64_t sliceIndex = btc->getSliceIndexParasagittal(vf); sliceIndex += this->getSliceDelta(dim,VolumeSliceViewPlaneEnum::PARASAGITTAL,sliceIndex); btc->setSliceIndexParasagittal(vf,sliceIndex); } } void MovieDialog::captureFrame(AString filename) { ImageFile imageFile; QApplication::setOverrideCursor(QCursor(Qt::BlankCursor)); // bool valid = GuiManager::get()->captureImageOfBrowserWindowGraphicsArea(m_browserWindowIndex, // imageX, // imageY, // imageFile, // false); EventImageCapture imageCaptureEvent(m_browserWindowIndex, 0, 0, 0, 0, imageX, imageY); EventManager::get()->sendEvent(imageCaptureEvent.getPointer()); bool valid = true; AString errorMessage; if (imageCaptureEvent.getEventProcessCount() <= 0) { errorMessage = "Invalid window selected"; valid = false; } else if (imageCaptureEvent.isError()) { errorMessage = imageCaptureEvent.getErrorMessage(); valid = false; } imageFile.setFromQImage(imageCaptureEvent.getImage()); QApplication::restoreOverrideCursor(); if (valid == false) { WuQMessageBox::errorOk(this, errorMessage); // "Invalid window selected"); ui->recordButton->setChecked(false); return; } /*if (ui->cropImageCheckBox->isChecked()) { const int marginSize = ui->marginSpinBox->value(); CaretPreferences* prefs = SessionManager::get()->getCaretPreferences(); uint8_t backgroundColor[3]; prefs->getColorBackground(backgroundColor); imageFile.cropImageRemoveBackground(marginSize, backgroundColor); croppedImageX = imageFile.getAsQImage()->size().height(); croppedImageY = imageFile.getAsQImage()->size().width(); }*/ std::vector imageFileExtensions; AString defaultFileExtension; ImageFile::getImageFileExtensions(imageFileExtensions, defaultFileExtension); bool validExtension = false; for (std::vector::iterator extensionIterator = imageFileExtensions.begin(); extensionIterator != imageFileExtensions.end(); extensionIterator++) { if (filename.endsWith(*extensionIterator)) { validExtension = true; } } if (validExtension == false) { if (defaultFileExtension.isEmpty() == false) { filename += ("." + defaultFileExtension); } } uint8_t backgroundColor[3]; imageCaptureEvent.getBackgroundColor(backgroundColor); try { const int marginSize = this->ui->marginSpinBox->value(); if (marginSize > 0) { imageFile.addMargin(marginSize, backgroundColor); } imageFile.writeFile(filename); } catch (const DataFileException& /*e*/) { QString msg("Unable to save: " + filename); WuQMessageBox::errorOk(this, msg); } } void MovieDialog::on_workbenchWindowSpinBox_valueChanged(int arg1) { m_browserWindowIndex = arg1-1; } void MovieDialog::processUpdateSurfaceInterpolation() { if(!m_interpolationEnabled||!m_isInterpolating) return; BrainBrowserWindow *bw = GuiManager::get()->getBrowserWindowByWindowIndex(m_browserWindowIndex); if(!bw) { CaretLogInfo("Invalid browser window index, " + AString::number(m_browserWindowIndex)); ui->animateButton->setChecked(false); return; } BrowserTabContent *btc1 = bw->getBrowserTabContent(0); BrowserTabContent *btc2 = bw->getBrowserTabContent(1); if(!btc1) return; if(!btc2) return; // int32_t tabIndex1 = btc1->getTabNumber(); if ((btc1->getSelectedModelType() != ModelTypeEnum::MODEL_TYPE_SURFACE) || (btc2->getSelectedModelType() != ModelTypeEnum::MODEL_TYPE_SURFACE)) { CaretLogInfo("Both Tab 1 and Tab 2 must contain surface models."); return; } ModelSurface *ms1 = btc1->getDisplayedSurfaceModel(); // int32_t tabIndex2 = btc2->getTabNumber(); ModelSurface *ms2 = btc2->getDisplayedSurfaceModel(); if(!(ms1&&ms2)) return; if (ms1->getSurface()->getStructure() != ms2->getSurface()->getStructure()) { CaretLogInfo("Surfaces in Tab 1 and Tab 2 must be the same structure."); return; } if(m_interpolationIndex == 0) { m_surface1 = ms1->getSurface(); m_surface2 = ms2->getSurface(); int32_t coordCount1 = m_surface1->getNumberOfNodes(); int32_t coordCount2 = m_surface2->getNumberOfNodes(); if(coordCount1 != coordCount2) return; const float* coords1 = m_surface1->getCoordinateData(); const float* coords2 = m_surface2->getCoordinateData(); float center1[3]; float center2[3]; float centerdelta[3]; m_surface1->getBoundingBox()->getCenter(center1); m_surface2->getBoundingBox()->getCenter(center2); for(int i = 0;i<3;i++) { centerdelta[i] = center2[i]-center1[i]; } m_surfaceCoords2Back.clear(); m_delta.clear(); for(int32_t i = 0;isetCoordinates(coords); m_surface2->invalidateNormals(); m_surface2->computeNormals(); btc1->getSurfaceModelSelector()->setSelectedSurfaceModel(btc2->getSurfaceModelSelector()->getSelectedSurfaceModel()); btc1->getSurfaceModelSelector()->setSelectedStructure(btc2->getSurfaceModelSelector()->getSelectedStructure()); EventManager::get()->sendEvent(EventUserInterfaceUpdate().getPointer()); } if(m_interpolationIndex setCoordinates(coords); m_surface2->invalidateNormals(); m_surface2->computeNormals(); m_interpolationIndex++; } else { CleanupInterpolation(); } } void MovieDialog::CleanupInterpolation() { if(!m_interpolationEnabled||!m_isInterpolating) return; if (m_surface2 == NULL) { return; } for(int64_t i = 0;isetCoordinates(coords); m_surface2->invalidateNormals(); m_surface2->computeNormals(); m_isInterpolating = false; m_interpolationIndex = 0; if(!coords) { delete coords; coords = NULL; coordsCount = 0; } m_surface1 = NULL; m_surface2 = NULL; } connectome-workbench-1.2.3+git41-gc4c6c90/src/GuiQt/MovieDialog.h000066400000000000000000000061301300200146000241450ustar00rootroot00000000000000#ifndef MOVIE_DIALOG_H #define MOVIE_DIALOG_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 "Event.h" #include "EventListenerInterface.h" #include namespace Ui { class MovieDialog; } using namespace caret; namespace caret { class Surface; } class MovieDialog : public QDialog, public EventListenerInterface { Q_OBJECT public: explicit MovieDialog(QWidget *parent = 0); ~MovieDialog(); void receiveEvent(Event* event); void processUpdateVolumeSlice(); void processUpdateSurfaceInterpolation(); int32_t getSliceDelta(const std::vector &dim, const caret::VolumeSliceViewPlaneEnum::Enum &vpe, const int32_t &sliceIndex); //void getImageCrop(AString fileName, int *cropout); private slots: void on_closeButton_clicked(); void on_animateButton_toggled(bool checked); void on_interpolateSurfaceCheckbox_toggled(bool checked); void on_recordButton_toggled(bool checked); void on_workbenchWindowSpinBox_valueChanged(int arg1); private: Ui::MovieDialog *ui; void captureFrame(AString filename); void processRotateTransformation(const double dx, const double dy, const double dz); void CleanupInterpolation(); int32_t m_browserWindowIndex; int frame_number; int rotate_frame_number; double dx; double dy; double dz; bool frameCountEnabled; int frameCount; bool reverseDirection; int32_t dP;//change in Parasagital slice int32_t dC;//change in Coronal slice int32_t dA;//change in Axial slice bool m_useCustomSize; int32_t imageX; int32_t imageY; int32_t croppedImageX; int32_t croppedImageY; bool m_animationStarted; int32_t m_volumeSliceIncrement; bool m_sliceIncrementIsNegative; bool m_reverseVolumeSliceDirection; int64_t m_AStart; int64_t m_AEnd; int64_t m_CStart; int64_t m_CEnd; int64_t m_PStart; int64_t m_PEnd; bool m_interpolationEnabled; int64_t m_interpolationSteps; int64_t m_interpolationIndex; bool m_isInterpolating; std::vector m_delta; std::vector m_surfaceCoords2Back; float *coords; int64_t coordsCount; Surface *m_surface1; Surface *m_surface2; Surface *m_surface; }; #endif // MOVIE_DIALOG_H connectome-workbench-1.2.3+git41-gc4c6c90/src/GuiQt/MovieDialog.ui000077500000000000000000000355471300200146000243540ustar00rootroot00000000000000 MovieDialog 0 0 459 567 0 0 Animation Control true Image Source Workbench Window: 1 10 Qt::Horizontal 40 20 Recording Control true Repeat Frames: true Qt::Horizontal 40 20 true Record true Rotation Control 0 0 Y: 0 0 X: Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter -180.000000000000000 180.000000000000000 -180.000000000000000 180.000000000000000 999999999 Rotate Frame Count: Reverse Direction -180.000000000000000 180.000000000000000 Z: Qt::Horizontal 40 20 true Slice Control Slice Increment Count -999 999 Reverse Direction Qt::Horizontal 40 20 true Interpolate Surfaces true Interpolate Surface in Tab 1 to Surface in Tab 2 Qt::Horizontal 40 20 Interpolation Steps true 1 99999 20 Qt::Horizontal 40 20 true Image Size Size of Window true Custom 1 10000 2560 1 10000 1600 Qt::Horizontal 40 20 true Image Options true Margin true 1000 10 Qt::Horizontal 40 20 Qt::Horizontal 40 20 Animate true Close true connectome-workbench-1.2.3+git41-gc4c6c90/src/GuiQt/OverlaySetViewController.cxx000066400000000000000000000244011300200146000273160ustar00rootroot00000000000000 /*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 #define __OVERLAY_SET_VIEW_CONTROLLER_DECLARE__ #include "OverlaySetViewController.h" #undef __OVERLAY_SET_VIEW_CONTROLLER_DECLARE__ #include "BrainConstants.h" #include "BrowserTabContent.h" #include "CaretAssert.h" #include "EventGraphicsUpdateOneWindow.h" #include "EventManager.h" #include "EventSurfaceColoringInvalidate.h" #include "EventUserInterfaceUpdate.h" #include "GuiManager.h" #include "OverlaySet.h" #include "OverlayViewController.h" #include "WuQtUtilities.h" using namespace caret; /** * \class caret::OverlaySetViewController * \brief View Controller for an 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. */ OverlaySetViewController::OverlaySetViewController(const Qt::Orientation orientation, const int32_t browserWindowIndex, QWidget* parent) : QWidget(parent) { this->browserWindowIndex = browserWindowIndex; QWidget* gridWidget = new QWidget(); QGridLayout* gridLayout = new QGridLayout(gridWidget); WuQtUtilities::setLayoutSpacingAndMargins(gridLayout, 4, 2); if (orientation == Qt::Horizontal) { gridLayout->setColumnStretch(0, 0); gridLayout->setColumnStretch(1, 0); gridLayout->setColumnStretch(2, 0); gridLayout->setColumnStretch(3, 0); gridLayout->setColumnStretch(4, 0); gridLayout->setColumnStretch(5, 100); gridLayout->setColumnStretch(6, 0); gridLayout->setColumnStretch(8, 100); QLabel* onLabel = new QLabel("On"); QLabel* settingsLabel = new QLabel("Settings"); QLabel* opacityLabel = new QLabel("Opacity"); QLabel* fileLabel = new QLabel("File"); QLabel* yokeLabel = new QLabel("Yoke"); QLabel* mapLabel = new QLabel("Map"); const int row = gridLayout->rowCount(); gridLayout->addWidget(onLabel, row, 0, Qt::AlignHCenter); gridLayout->addWidget(settingsLabel, row, 1, 1, 3, Qt::AlignHCenter); gridLayout->addWidget(opacityLabel, row, 4, Qt::AlignHCenter); gridLayout->addWidget(fileLabel, row, 5, Qt::AlignHCenter); gridLayout->addWidget(yokeLabel, row, 6, Qt::AlignHCenter); gridLayout->addWidget(mapLabel, row, 8, 1, 2, Qt::AlignHCenter); } else { 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, 100); } for (int32_t i = 0; i < BrainConstants::MAXIMUM_NUMBER_OF_OVERLAYS; i++) { OverlayViewController* ovc = new OverlayViewController(orientation, gridLayout, browserWindowIndex, i, this); this->overlayViewControllers.push_back(ovc); 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) { QVBoxLayout* verticalLayout = new QVBoxLayout(this); WuQtUtilities::setLayoutSpacingAndMargins(verticalLayout, 2, 2); verticalLayout->addWidget(gridWidget); verticalLayout->addStretch(); } else { 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); } /** * Destructor. */ OverlaySetViewController::~OverlaySetViewController() { EventManager::get()->removeAllEventsFromListener(this); } /** * @return The overlay set in this view controller. */ OverlaySet* OverlaySetViewController::getOverlaySet() { OverlaySet* overlaySet = NULL; BrowserTabContent* browserTabContent = GuiManager::get()->getBrowserTabContentForBrowserWindow(this->browserWindowIndex, true); if (browserTabContent != NULL) { overlaySet = browserTabContent->getOverlaySet(); } return overlaySet; } /** * Update this overlay set view controller using the given overlay set. */ void OverlaySetViewController::updateViewController() { OverlaySet* overlaySet = this->getOverlaySet(); if (overlaySet == NULL) { return; } const int32_t numberOfOverlays = static_cast(this->overlayViewControllers.size()); const int32_t numberOfDisplayedOverlays = overlaySet->getNumberOfDisplayedOverlays(); for (int32_t i = 0; i < numberOfOverlays; i++) { Overlay* overlay = NULL; if (overlaySet != NULL) { overlay = overlaySet->getOverlay(i); } this->overlayViewControllers[i]->updateViewController(overlay); bool displayOverlay = (overlay != NULL); if (i >= numberOfDisplayedOverlays) { displayOverlay = false; } this->overlayViewControllers[i]->setVisible(displayOverlay); } } /** * Receive events from the event manager. * * @param event * Event sent by event manager. */ void OverlaySetViewController::receiveEvent(Event* event) { if (event->getEventType() == EventTypeEnum::EVENT_USER_INTERFACE_UPDATE) { EventUserInterfaceUpdate* uiEvent = dynamic_cast(event); CaretAssert(uiEvent); if (uiEvent->isUpdateForWindow(this->browserWindowIndex)) { if (uiEvent->isToolBoxUpdate()) { this->updateViewController(); uiEvent->setEventProcessed(); } } } } /** * Add an overlay above the overlay with the given index. * @param overlayIndex * Index of overlay that will have an overlay added above it. */ void OverlaySetViewController::processAddOverlayAbove(const int32_t overlayIndex) { OverlaySet* overlaySet = getOverlaySet(); if (overlaySet != NULL) { overlaySet->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 OverlaySetViewController::processAddOverlayBelow(const int32_t overlayIndex) { OverlaySet* overlaySet = getOverlaySet(); if (overlaySet != NULL) { overlaySet->insertOverlayBelow(overlayIndex); this->updateColoringAndGraphics(); } } /** * Remove an overlay above the overlay with the given index. * @param overlayIndex * Index of overlay that will be removed */ void OverlaySetViewController::processRemoveOverlay(const int32_t overlayIndex) { OverlaySet* overlaySet = getOverlaySet(); if (overlaySet != NULL) { overlaySet->removeDisplayedOverlay(overlayIndex); this->updateColoringAndGraphics(); } } /** * Remove an overlay above the overlay with the given index. * @param overlayIndex * Index of overlay that will be removed */ void OverlaySetViewController::processMoveOverlayDown(const int32_t overlayIndex) { OverlaySet* overlaySet = getOverlaySet(); if (overlaySet != NULL) { overlaySet->moveDisplayedOverlayDown(overlayIndex); this->updateColoringAndGraphics(); } } /** * Remove an overlay above the overlay with the given index. * @param overlayIndex * Index of overlay that will be removed */ void OverlaySetViewController::processMoveOverlayUp(const int32_t overlayIndex) { OverlaySet* overlaySet = getOverlaySet(); if (overlaySet != NULL) { overlaySet->moveDisplayedOverlayUp(overlayIndex); this->updateColoringAndGraphics(); } } /** * Update surface coloring and graphics after overlay changes. */ void OverlaySetViewController::updateColoringAndGraphics() { this->updateViewController(); EventManager::get()->sendEvent(EventSurfaceColoringInvalidate().getPointer()); EventGraphicsUpdateOneWindow graphicsUpdate(this->browserWindowIndex); EventManager::get()->sendEvent(graphicsUpdate.getPointer()); } connectome-workbench-1.2.3+git41-gc4c6c90/src/GuiQt/OverlaySetViewController.h000066400000000000000000000051011300200146000267370ustar00rootroot00000000000000#ifndef __OVERLAY_SET_VIEW_CONTROLLER__H_ #define __OVERLAY_SET_VIEW_CONTROLLER__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 "EventListenerInterface.h" class QScrollArea; class QSpinBox; namespace caret { class OverlaySet; class OverlayViewController; class OverlaySetViewController : public QWidget, public EventListenerInterface { Q_OBJECT public: OverlaySetViewController(const Qt::Orientation orientation, const int32_t browserWindowIndex, QWidget* parent = 0); virtual ~OverlaySetViewController(); 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: OverlaySetViewController(const OverlaySetViewController&); OverlaySetViewController& operator=(const OverlaySetViewController&); OverlaySet* getOverlaySet(); void updateViewController(); void updateColoringAndGraphics(); std::vector overlayViewControllers; int32_t browserWindowIndex; QScrollArea* scrollArea; }; #ifdef __OVERLAY_SET_VIEW_CONTROLLER_DECLARE__ // #endif // __OVERLAY_SET_VIEW_CONTROLLER_DECLARE__ } // namespace #endif //__OVERLAY_SET_VIEW_CONTROLLER__H_ connectome-workbench-1.2.3+git41-gc4c6c90/src/GuiQt/OverlaySettingsEditorDialog.cxx000066400000000000000000000420271300200146000277570ustar00rootroot00000000000000 /*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 __OVERLAY_SETTINGS_EDITOR_DIALOG_DECLARE__ #include "OverlaySettingsEditorDialog.h" #undef __OVERLAY_SETTINGS_EDITOR_DIALOG_DECLARE__ #include "CaretMappableDataFile.h" #include "CiftiFiberTrajectoryFile.h" #include "CiftiConnectivityMatrixParcelFile.h" #include "EventDataFileDelete.h" #include "EventGraphicsUpdateAllWindows.h" #include "EventManager.h" #include "EventOverlayValidate.h" #include "EventSurfaceColoringInvalidate.h" #include "EventUserInterfaceUpdate.h" #include "GiftiLabelTableEditor.h" #include "MapSettingsColorBarWidget.h" #include "MapSettingsFiberTrajectoryWidget.h" #include "MapSettingsLabelsWidget.h" #include "MapSettingsLayerWidget.h" #include "MapSettingsPaletteColorMappingWidget.h" #include "MapSettingsParcelsWidget.h" #include "Overlay.h" #include "WuQtUtilities.h" using namespace caret; /** * \class caret::__OVERLAY_SETTINGS_EDITOR_DIALOG_DECLARE__ * \brief Dialog for editing an overlay's settings * \ingroup GuiQt */ /** * Constructor for editing a palette selection. * * @param parent * Parent widget on which this dialog is displayed. */ OverlaySettingsEditorDialog::OverlaySettingsEditorDialog(QWidget* parent) : WuQDialogNonModal("Overlay and Map Settings", parent), EventListenerInterface() { /* * No context menu, it screws things up */ this->setContextMenuPolicy(Qt::NoContextMenu); this->setDeleteWhenClosed(false); m_caretMappableDataFile = NULL; m_mapIndex = -1; /* * No apply button */ this->setApplyButtonText(""); QWidget* mapNameWidget = createMapFileAndNameSection(); m_colorBarWidget = new MapSettingsColorBarWidget(); m_fiberTrajectoryWidget = new MapSettingsFiberTrajectoryWidget(); m_layerWidget = new MapSettingsLayerWidget(); m_paletteColorMappingWidget = new MapSettingsPaletteColorMappingWidget(); m_parcelsWidget = new MapSettingsParcelsWidget(); QWidget* windowOptionsWidget = this->createWindowOptionsSection(); m_labelsWidget = new MapSettingsLabelsWidget(); m_tabWidget = new QTabWidget(); m_colorBarWidgetTabIndex = m_tabWidget->addTab(m_colorBarWidget, "Color Bar"); m_labelsWidgetTabIndex = m_tabWidget->addTab(m_labelsWidget, "Labels"); m_tabWidget->setTabEnabled(m_tabWidget->count() - 1, false); m_layersWidgetTabIndex = m_tabWidget->addTab(m_layerWidget, "Layer"); m_metadataWidgetTabIndex = m_tabWidget->addTab(new QWidget(), "Metadata"); m_tabWidget->setTabEnabled(m_tabWidget->count() - 1, false); m_paletteWidgetTabIndex = m_tabWidget->addTab(m_paletteColorMappingWidget, "Palette"); m_parcelsWidgetTabIndex = m_tabWidget->addTab(m_parcelsWidget, "Parcels"); m_trajectoryWidgetTabIndex = m_tabWidget->addTab(m_fiberTrajectoryWidget, "Trajectory"); m_tabWidget->setCurrentIndex(m_tabWidget->count() - 1); QWidget* w = new QWidget(); QVBoxLayout* layout = new QVBoxLayout(w); this->setLayoutSpacingAndMargins(layout); layout->addWidget(mapNameWidget); layout->addWidget(m_tabWidget); //layout->addWidget(windowOptionsWidget); this->setCentralWidget(w, WuQDialog::SCROLL_AREA_NEVER); this->addWidgetToLeftOfButtons(windowOptionsWidget); disableAutoDefaultForAllPushButtons(); EventManager::get()->addProcessedEventListener(this, EventTypeEnum::EVENT_DATA_FILE_DELETE); } /** * Destructor. */ OverlaySettingsEditorDialog::~OverlaySettingsEditorDialog() { EventManager::get()->removeAllEventsFromListener(this); } /** * Receive an event. * * @param event * An event for which this instance is listening. */ void OverlaySettingsEditorDialog::receiveEvent(Event* event) { if (event->getEventType() == EventTypeEnum::EVENT_DATA_FILE_DELETE) { updateDialog(); } } /** * @return Create and return map file and name section of the dialog. * */ QWidget* OverlaySettingsEditorDialog::createMapFileAndNameSection() { QLabel* mapFileNameLabel = new QLabel("Map File: "); m_selectedMapFileNameLabel = new QLabel(""); QLabel* mapNameLabel = new QLabel("Map Name: "); m_selectedMapNameLabel = new QLabel(""); QGroupBox* groupBox = new QGroupBox(); QGridLayout* gridLayout = new QGridLayout(groupBox); WuQtUtilities::setLayoutSpacingAndMargins(gridLayout, 2, 2); gridLayout->setColumnStretch(0, 0); gridLayout->setColumnStretch(1, 100); gridLayout->addWidget(mapFileNameLabel, 0, 0); gridLayout->addWidget(m_selectedMapFileNameLabel, 0, 1, Qt::AlignLeft); gridLayout->addWidget(mapNameLabel, 1, 0); gridLayout->addWidget(m_selectedMapNameLabel, 1, 1, Qt::AlignLeft); return groupBox; } /** * Called for focus events. Since this dialog stores a pointer * to the overlay, we need to be aware that the overlay's parameters * may change or the overlay may even be deleted. So, when * this dialog gains focus, validate the overlay and then update * the dialog. * * @param event * The focus event. */ void OverlaySettingsEditorDialog::focusInEvent(QFocusEvent* /*event*/) { updateDialog(); } /** * Update if the given overlay is displayed in the dialog. * * @param overlay * The overlay. */ void OverlaySettingsEditorDialog::updateIfThisOverlayIsInDialog(Overlay* overlay) { if (m_overlay == overlay) { updateDialogContent(m_overlay); } } /** * May be called to update the dialog's content. * * @param overlay * Overlay for the dialog. */ void OverlaySettingsEditorDialog::updateDialogContent(Overlay* overlay) { const int32_t selectedTabIndex = m_tabWidget->currentIndex(); m_overlay = overlay; // m_caretMappableDataFile = caretMappableDataFile; // m_mapFileIndex = mapFileIndex; m_caretMappableDataFile = NULL; m_mapIndex = -1; if (m_overlay != NULL) { overlay->getSelectionData(m_caretMappableDataFile, m_mapIndex); } bool isLabelsValid = false; bool isMetadataValid = false; GiftiMetaData* metadata = NULL; bool isPaletteValid = false; bool isParcelsValid = false; bool isFiberTrajectoryValid = false; bool isVolumeLayer = 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); } } metadata = m_caretMappableDataFile->getMapMetaData(m_mapIndex); if (metadata != NULL) { /* * TODO: Update metadata widget */ //isMetadataValid = true; } if (m_caretMappableDataFile->isMappedWithPalette()) { PaletteColorMapping* paletteColorMapping = m_caretMappableDataFile->getMapPaletteColorMapping(m_mapIndex); if (paletteColorMapping != NULL) { /* * Update palette settings */ isPaletteValid = true; m_paletteColorMappingWidget->updateEditor(m_caretMappableDataFile, m_mapIndex); m_colorBarWidget->updateContent(m_overlay->getColorBar(), 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); } /* * Is volume mappable */ if (m_caretMappableDataFile->isVolumeMappable()) { if (isFiberTrajectoryValid) { /* nothing */ } else { isVolumeLayer = true; } } } } /* * Set enabled status of tabs */ m_tabWidget->setTabEnabled(m_colorBarWidgetTabIndex, isPaletteValid); m_tabWidget->setTabEnabled(m_labelsWidgetTabIndex, isLabelsValid); m_tabWidget->setTabEnabled(m_layersWidgetTabIndex, isVolumeLayer); m_tabWidget->setTabEnabled(m_metadataWidgetTabIndex, isMetadataValid); m_tabWidget->setTabEnabled(m_paletteWidgetTabIndex, isPaletteValid); m_tabWidget->setTabEnabled(m_parcelsWidgetTabIndex, isParcelsValid); m_tabWidget->setTabEnabled(m_trajectoryWidgetTabIndex, isFiberTrajectoryValid); /* * When the selected tab is invalid, we want to select the * "best" tab that is enabled. The order in this vector * is the priority of the tabs. */ std::vector priorityTabIndices; priorityTabIndices.push_back(m_paletteWidgetTabIndex); priorityTabIndices.push_back(m_colorBarWidgetTabIndex); priorityTabIndices.push_back(m_labelsWidgetTabIndex); priorityTabIndices.push_back(m_parcelsWidgetTabIndex); priorityTabIndices.push_back(m_trajectoryWidgetTabIndex); priorityTabIndices.push_back(m_layersWidgetTabIndex); priorityTabIndices.push_back(m_metadataWidgetTabIndex); CaretAssertMessage((static_cast(priorityTabIndices.size()) == m_tabWidget->count()), "Number of elements in priorityTabIndices is different " "than number of tab indices. Was a new tab added?"); /* * Make sure an enabled tab is selected using the tabs * in the priority order */ if (selectedTabIndex >= 0) { if (m_tabWidget->isTabEnabled(selectedTabIndex) == false) { const int32_t numPriorityTabs = static_cast(priorityTabIndices.size()); for (int32_t i = 0; i < numPriorityTabs; i++) { const int32_t tabIndex = priorityTabIndices[i]; if (m_tabWidget->isTabEnabled(tabIndex)) { m_tabWidget->setCurrentIndex(tabIndex); break; } } } } m_selectedMapFileNameLabel->setText(mapFileName); m_selectedMapNameLabel->setText(mapName); } /** * May be called to update the dialog. */ void OverlaySettingsEditorDialog::updateDialog() { /* * Validate overlay to prevent crash */ EventOverlayValidate validateOverlayEvent(m_overlay); EventManager::get()->sendEvent(validateOverlayEvent.getPointer()); if (validateOverlayEvent.isValidOverlay() == false) { m_overlay = NULL; } updateDialogContent(m_overlay); if (m_overlay == NULL) { close(); } } /** * Called when close button pressed. */ void OverlaySettingsEditorDialog::closeButtonPressed() { /* * Clear the content since it could be tied to an overlay * and we don't want the dialog updating if it is not * visible. */ updateDialogContent(NULL); /* * Allow this dialog to be reused (checked means DO NOT reuse) */ m_doNotReplaceCheckBox->setCheckState(Qt::Unchecked); WuQDialogNonModal::closeButtonClicked(); } /** * Set the layout margins. * @param layout * Layout for which margins are set. */ void OverlaySettingsEditorDialog::setLayoutSpacingAndMargins(QLayout* layout) { WuQtUtilities::setLayoutSpacingAndMargins(layout, 5, 3); } /** * @return Is the Do Not Replace selected. If so, this instance of the * dialog should not be replaced. */ bool OverlaySettingsEditorDialog::isDoNotReplaceSelected() const { const bool checked = (m_doNotReplaceCheckBox->checkState() == Qt::Checked); return checked; } /** * Called when the state of the do not reply checkbox is changed. * @param state * New state of checkbox. */ void OverlaySettingsEditorDialog::doNotReplaceCheckBoxStateChanged(int /*state*/) { // const bool checked = (state == Qt::Checked); } /** * @return A widget containing the window options. */ QWidget* OverlaySettingsEditorDialog::createWindowOptionsSection() { m_doNotReplaceCheckBox = new QCheckBox("Do Not Replace"); m_doNotReplaceCheckBox->setToolTip("If checked: \n" " (1) this window remains displayed until it is\n" " closed.\n" " (2) if the user selects editing of another map's\n" " palette, it will not replace the content of\n" " this window.\n" "If NOT checked:\n" " If the user selects editing of another map's \n" " palette, it will replace the content of this\n" " window."); QWidget* optionsWidget = new QWidget(); QVBoxLayout* optionsLayout = new QVBoxLayout(optionsWidget); this->setLayoutSpacingAndMargins(optionsLayout); optionsLayout->addWidget(m_doNotReplaceCheckBox); optionsWidget->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); return optionsWidget; } /** * Called when the edit label table button is clicked. */ void OverlaySettingsEditorDialog::editLabelTablePushButtonClicked() { if (m_caretMappableDataFile != NULL) { if (m_caretMappableDataFile->isMappedWithLabelTable()) { if (m_mapIndex >= 0) { GiftiLabelTableEditor labelTableEditor(m_caretMappableDataFile, m_mapIndex, "Edit Labels", GiftiLabelTableEditor::OPTION_ADD_APPLY_BUTTON, m_editLabelTablePushButton); labelTableEditor.exec(); } } } } /** * Create and return widget for editing label tables. */ QWidget* OverlaySettingsEditorDialog::createLabelsSection() { m_editLabelTablePushButton = new QPushButton("Edit"); QObject::connect(m_editLabelTablePushButton, SIGNAL(clicked()), this, SLOT(editLabelTablePushButtonClicked())); QWidget* widget = new QWidget(); QVBoxLayout* layout = new QVBoxLayout(widget); layout->addWidget(m_editLabelTablePushButton, 0, Qt::AlignLeft); layout->addStretch(); return widget; } connectome-workbench-1.2.3+git41-gc4c6c90/src/GuiQt/OverlaySettingsEditorDialog.h000066400000000000000000000077121300200146000274060ustar00rootroot00000000000000#ifndef __OVERLAY_SETTINGS_EDITOR_DIALOG__H_ #define __OVERLAY_SETTINGS_EDITOR_DIALOG__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 "EventListenerInterface.h" #include "WuQDialogNonModal.h" class QCheckBox; class QLabel; class QLayout; class QPushButton; class QTabWidget; namespace caret { class CaretMappableDataFile; class LabelTableEditorWidget; class MapSettingsColorBarWidget; class MapSettingsFiberTrajectoryWidget; class MapSettingsLabelsWidget; class MapSettingsLayerWidget; class MapSettingsPaletteColorMappingWidget; class MapSettingsParcelsWidget; class Overlay; class OverlaySettingsEditorDialog : public WuQDialogNonModal, public EventListenerInterface { Q_OBJECT public: OverlaySettingsEditorDialog(QWidget* parent); void updateDialogContent(Overlay* overlay); void updateIfThisOverlayIsInDialog(Overlay* overlay); void updateDialog(); virtual ~OverlaySettingsEditorDialog(); bool isDoNotReplaceSelected() const; virtual void receiveEvent(Event* event); protected: virtual void closeButtonPressed(); virtual void focusInEvent(QFocusEvent* event); private: OverlaySettingsEditorDialog(const OverlaySettingsEditorDialog&); OverlaySettingsEditorDialog& operator=(const OverlaySettingsEditorDialog&); private slots: void doNotReplaceCheckBoxStateChanged(int state); void editLabelTablePushButtonClicked(); private: QWidget* createWindowOptionsSection(); QWidget* createMapFileAndNameSection(); QWidget* createLabelsSection(); void setLayoutSpacingAndMargins(QLayout* layout); QTabWidget* m_tabWidget; QCheckBox* m_doNotReplaceCheckBox; CaretMappableDataFile* m_caretMappableDataFile; Overlay* m_overlay; int32_t m_mapIndex; MapSettingsPaletteColorMappingWidget* m_paletteColorMappingWidget; MapSettingsParcelsWidget* m_parcelsWidget; MapSettingsColorBarWidget* m_colorBarWidget; LabelTableEditorWidget* m_labelTableEditorWidget; MapSettingsFiberTrajectoryWidget* m_fiberTrajectoryWidget; MapSettingsLayerWidget* m_layerWidget; MapSettingsLabelsWidget* m_labelsWidget; QPushButton* m_editLabelTablePushButton; QLabel* m_selectedMapFileNameLabel; QLabel* m_selectedMapNameLabel; int32_t m_colorBarWidgetTabIndex; int32_t m_labelsWidgetTabIndex; int32_t m_layersWidgetTabIndex; int32_t m_metadataWidgetTabIndex; int32_t m_paletteWidgetTabIndex; int32_t m_parcelsWidgetTabIndex; int32_t m_trajectoryWidgetTabIndex; }; #ifdef __OVERLAY_SETTINGS_EDITOR_DIALOG_DECLARE__ #endif // __OVERLAY_SETTINGS_EDITOR_DIALOG_DECLARE__ } // namespace #endif //__OVERLAY_SETTINGS_EDITOR_DIALOG__H_ connectome-workbench-1.2.3+git41-gc4c6c90/src/GuiQt/OverlayViewController.cxx000066400000000000000000001031301300200146000266370ustar00rootroot00000000000000 /*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 #include #include #include #include #define __OVERLAY_VIEW_CONTROLLER_DECLARE__ #include "OverlayViewController.h" #undef __OVERLAY_VIEW_CONTROLLER_DECLARE__ #include "AnnotationColorBar.h" #include "CaretMappableDataFile.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 "Overlay.h" #include "UsernamePasswordWidget.h" #include "WuQFactory.h" #include "WuQMessageBox.h" #include "WuQtUtilities.h" #include "WuQGridLayoutGroup.h" #include "WuQWidgetObjectGroup.h" using namespace caret; /** * \class caret::OverlayViewController * \brief View Controller for an overlay. * \ingroup GuiQt */ /** * Constructor. * * @param browserWindowIndex * Index of browser window in which this view controller resides. * @param showTopHorizontalLine * If true, display a horizontal line above the controls. * @param parent * The parent widget. */ OverlayViewController::OverlayViewController(const Qt::Orientation orientation, QGridLayout* gridLayout, const int32_t browserWindowIndex, const int32_t overlayIndex, QObject* parent) : QObject(parent), browserWindowIndex(browserWindowIndex), m_overlayIndex(overlayIndex) { this->overlay = NULL; m_constructionReloadFileAction = NULL; int minComboBoxWidth = 200; int maxComboBoxWidth = 100000; //400; if (orientation == Qt::Horizontal) { minComboBoxWidth = 50; maxComboBoxWidth = 100000; } /* * Enabled Check Box */ const QString checkboxText = ((orientation == Qt::Horizontal) ? " " : " "); this->enabledCheckBox = new QCheckBox(checkboxText); QObject::connect(this->enabledCheckBox, SIGNAL(clicked(bool)), this, SLOT(enabledCheckBoxClicked(bool))); this->enabledCheckBox->setToolTip("Enables display of this overlay"); /* * File Selection Check Box */ this->fileComboBox = WuQFactory::newComboBox(); this->fileComboBox->setMinimumWidth(minComboBoxWidth); this->fileComboBox->setMaximumWidth(maxComboBoxWidth); QObject::connect(this->fileComboBox, SIGNAL(activated(int)), this, SLOT(fileComboBoxSelected(int))); this->fileComboBox->setToolTip("Selects file for this overlay"); /* * Map Index Spin Box */ m_mapIndexSpinBox = WuQFactory::newSpinBox(); QObject::connect(m_mapIndexSpinBox, SIGNAL(valueChanged(int)), this, SLOT(mapIndexSpinBoxValueChanged(int))); m_mapIndexSpinBox->setToolTip("Select map by its index"); /* * Map Name Combo Box */ this->mapNameComboBox = WuQFactory::newComboBox(); this->mapNameComboBox->setMinimumWidth(minComboBoxWidth); this->mapNameComboBox->setMaximumWidth(maxComboBoxWidth); QObject::connect(this->mapNameComboBox, SIGNAL(activated(int)), this, SLOT(mapNameComboBoxSelected(int))); this->mapNameComboBox->setToolTip("Select map by its name"); /* * Opacity double spin box */ this->opacityDoubleSpinBox = WuQFactory::newDoubleSpinBox(); this->opacityDoubleSpinBox->setMinimum(0.0); this->opacityDoubleSpinBox->setMaximum(1.0); this->opacityDoubleSpinBox->setSingleStep(0.10); this->opacityDoubleSpinBox->setDecimals(1); this->opacityDoubleSpinBox->setFixedWidth(50); QObject::connect(this->opacityDoubleSpinBox, SIGNAL(valueChanged(double)), this, SLOT(opacityDoubleSpinBoxValueChanged(double))); this->opacityDoubleSpinBox->setToolTip("Opacity (0.0=transparent, 1.0=opaque)"); /* * ColorBar Tool Button */ QIcon colorBarIcon; const bool colorBarIconValid = WuQtUtilities::loadIcon(":/LayersPanel/colorbar.png", colorBarIcon); this->colorBarAction = WuQtUtilities::createAction("CB", "Display color bar for this overlay", this, this, SLOT(colorBarActionTriggered(bool))); this->colorBarAction->setCheckable(true); if (colorBarIconValid) { this->colorBarAction->setIcon(colorBarIcon); } QToolButton* colorBarToolButton = new QToolButton(); colorBarToolButton->setDefaultAction(this->colorBarAction); /* * Settings Tool Button */ QIcon settingsIcon; const bool settingsIconValid = WuQtUtilities::loadIcon(":/LayersPanel/wrench.png", settingsIcon); this->settingsAction = WuQtUtilities::createAction("S", "Edit settings for this map and overlay", this, this, SLOT(settingsActionTriggered())); if (settingsIconValid) { this->settingsAction->setIcon(settingsIcon); } QToolButton* settingsToolButton = new QToolButton(); settingsToolButton->setDefaultAction(this->settingsAction); /* * Construction Tool Button */ QIcon constructionIcon; const bool constructionIconValid = WuQtUtilities::loadIcon(":/LayersPanel/construction.png", constructionIcon); this->constructionAction = WuQtUtilities::createAction("M", "Add/Move/Remove Overlays", this); if (constructionIconValid) { this->constructionAction->setIcon(constructionIcon); } m_constructionToolButton = new QToolButton(); QMenu* constructionMenu = createConstructionMenu(m_constructionToolButton); this->constructionAction->setMenu(constructionMenu); m_constructionToolButton->setDefaultAction(this->constructionAction); m_constructionToolButton->setPopupMode(QToolButton::InstantPopup); 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"); /* * Yoking Group */ m_mapYokingGroupComboBox = new MapYokingGroupComboBox(this); m_mapYokingGroupComboBox->getWidget()->setStatusTip("Synchronize enabled status and map indices)"); m_mapYokingGroupComboBox->getWidget()->setToolTip("Yoke to Overlay Mapped Files"); #ifdef CARET_OS_MACOSX m_mapYokingGroupComboBox->getWidget()->setFixedWidth(m_mapYokingGroupComboBox->getWidget()->sizeHint().width() - 20); #endif // CARET_OS_MACOSX QObject::connect(m_mapYokingGroupComboBox, SIGNAL(itemActivated()), this, SLOT(yokingGroupActivated())); /* * Use layout group so that items can be shown/hidden */ this->gridLayoutGroup = new WuQGridLayoutGroup(gridLayout, this); if (orientation == Qt::Horizontal) { int row = this->gridLayoutGroup->rowCount(); this->gridLayoutGroup->addWidget(this->enabledCheckBox, row, 0, Qt::AlignHCenter); this->gridLayoutGroup->addWidget(settingsToolButton, row, 1, Qt::AlignHCenter); this->gridLayoutGroup->addWidget(colorBarToolButton, row, 2); this->gridLayoutGroup->addWidget(m_constructionToolButton, row, 3); this->gridLayoutGroup->addWidget(this->opacityDoubleSpinBox, row, 4); this->gridLayoutGroup->addWidget(this->fileComboBox, row, 5); this->gridLayoutGroup->addWidget(this->m_mapYokingGroupComboBox->getWidget(), row, 6, Qt::AlignHCenter); this->gridLayoutGroup->addWidget(m_mapIndexSpinBox, row, 7); this->gridLayoutGroup->addWidget(this->mapNameComboBox, row, 8); } else { QFrame* bottomHorizontalLineWidget = new QFrame(); bottomHorizontalLineWidget->setLineWidth(0); bottomHorizontalLineWidget->setMidLineWidth(1); bottomHorizontalLineWidget->setFrameStyle(QFrame::HLine | QFrame::Raised); QLabel* fileLabel = new QLabel("File"); QLabel* mapLabel = new QLabel("Map"); int row = this->gridLayoutGroup->rowCount(); this->gridLayoutGroup->addWidget(this->enabledCheckBox, row, 0); this->gridLayoutGroup->addWidget(settingsToolButton, row, 1); this->gridLayoutGroup->addWidget(colorBarToolButton, row, 2); this->gridLayoutGroup->addWidget(m_constructionToolButton, row, 3); this->gridLayoutGroup->addWidget(fileLabel, row, 4); this->gridLayoutGroup->addWidget(this->fileComboBox, row, 5, 1, 2); row++; this->gridLayoutGroup->addWidget(this->opacityDoubleSpinBox, row, 0, 1, 2, Qt::AlignCenter); this->gridLayoutGroup->addWidget(this->m_mapYokingGroupComboBox->getWidget(), row, 2, 1, 2); this->gridLayoutGroup->addWidget(mapLabel, row, 4); this->gridLayoutGroup->addWidget(m_mapIndexSpinBox, row, 5); this->gridLayoutGroup->addWidget(this->mapNameComboBox, row, 6); row++; this->gridLayoutGroup->addWidget(bottomHorizontalLineWidget, row, 0, 1, -1); } } /** * Destructor. */ OverlayViewController::~OverlayViewController() { } /** * Set the visiblity of this overlay view controller. */ void OverlayViewController::setVisible(bool visible) { this->gridLayoutGroup->setVisible(visible); } /* * If this overlay ins an overlay settings editor, update its content */ void OverlayViewController::updateOverlaySettingsEditor() { if (overlay == NULL) { return; } CaretMappableDataFile* mapFile = NULL; int32_t mapIndex = -1; overlay->getSelectionData(mapFile, mapIndex); if ((mapFile != NULL) && (mapIndex >= 0)) { EventOverlaySettingsEditorDialogRequest pcme(EventOverlaySettingsEditorDialogRequest::MODE_OVERLAY_MAP_CHANGED, this->browserWindowIndex, this->overlay, mapFile, mapIndex); EventManager::get()->sendEvent(pcme.getPointer()); } } /** * Called when a selection is made from the file combo box. * @parm indx * Index of selection. */ void OverlayViewController::fileComboBoxSelected(int indx) { if (overlay == NULL) { return; } void* pointer = this->fileComboBox->itemData(indx).value(); CaretMappableDataFile* file = (CaretMappableDataFile*)pointer; overlay->setSelectionData(file, 0); validateYokingSelection(); //validateYokingSelection(overlay->getYokingGroup()); // not needed with call to validateYokingSelection: this->updateViewController(this->overlay); // called inside validateYokingSelection(); this->updateUserInterfaceAndGraphicsWindow(); updateOverlaySettingsEditor(); updateViewController(this->overlay); updateGraphicsWindow(); if (file != NULL) { if (file->isVolumeMappable()) { /* * Need to update slice indices/coords in toolbar. */ EventManager::get()->sendEvent(EventUserInterfaceUpdate().setWindowIndex(browserWindowIndex).addToolBar().getPointer()); } } } /** * Called when a selection is made from the map index spin box. * @parm indx * Index of selection. */ void OverlayViewController::mapIndexSpinBoxValueChanged(int indx) { if (overlay == NULL) { //TSC: not sure how to put the displayed integer back to 0 where it starts when opening without data files return; } /* * Get the file that is selected from the file combo box */ const int32_t fileIndex = this->fileComboBox->currentIndex(); void* pointer = this->fileComboBox->itemData(fileIndex).value(); CaretMappableDataFile* file = (CaretMappableDataFile*)pointer; /* * Overlay indices range [0, N-1] but spin box shows [1, N]. */ const int overlayIndex = indx - 1; overlay->setSelectionData(file, overlayIndex); const MapYokingGroupEnum::Enum mapYoking = overlay->getMapYokingGroup(); if (mapYoking != MapYokingGroupEnum::MAP_YOKING_GROUP_OFF) { EventMapYokingSelectMap selectMapEvent(mapYoking, file, overlayIndex, overlay->isEnabled()); EventManager::get()->sendEvent(selectMapEvent.getPointer()); } /* * Need to update map name combo box. */ mapNameComboBox->blockSignals(true); if ((overlayIndex >= 0) && (overlayIndex < mapNameComboBox->count())) { mapNameComboBox->setCurrentIndex(overlayIndex); } mapNameComboBox->blockSignals(false); this->updateUserInterface(); this->updateGraphicsWindow(); updateOverlaySettingsEditor(); } /** * Called when a selection is made from the map name combo box. * @parm indx * Index of selection. */ void OverlayViewController::mapNameComboBoxSelected(int indx) { if (overlay == NULL) { return; } /* * Get the file that is selected from the file combo box */ const int32_t fileIndex = this->fileComboBox->currentIndex(); void* pointer = this->fileComboBox->itemData(fileIndex).value(); CaretMappableDataFile* file = (CaretMappableDataFile*)pointer; overlay->setSelectionData(file, indx); const MapYokingGroupEnum::Enum mapYoking = overlay->getMapYokingGroup(); if (mapYoking != MapYokingGroupEnum::MAP_YOKING_GROUP_OFF) { EventMapYokingSelectMap selectMapEvent(mapYoking, file, indx, overlay->isEnabled()); EventManager::get()->sendEvent(selectMapEvent.getPointer()); } /* * Need to update map index spin box. * Note that the map index spin box ranges [1, N]. */ m_mapIndexSpinBox->blockSignals(true); m_mapIndexSpinBox->setValue(indx + 1); m_mapIndexSpinBox->blockSignals(false); this->updateUserInterface(); this->updateGraphicsWindow(); updateOverlaySettingsEditor(); } /** * Called when enabled checkbox state is changed * @parm checked * Checked status */ void OverlayViewController::enabledCheckBoxClicked(bool checked) { if (overlay == NULL) { return; } overlay->setEnabled(checked); const MapYokingGroupEnum::Enum mapYoking = overlay->getMapYokingGroup(); if (mapYoking != MapYokingGroupEnum::MAP_YOKING_GROUP_OFF) { CaretMappableDataFile* myFile = NULL; int32_t myIndex = -1; this->overlay->getSelectionData(myFile, myIndex); EventMapYokingSelectMap selectMapEvent(mapYoking, myFile, myIndex, overlay->isEnabled()); EventManager::get()->sendEvent(selectMapEvent.getPointer()); } this->updateUserInterface(); this->updateGraphicsWindow(); } /** * Called when colorbar toolbutton is toggled. * @param status * New status. */ void OverlayViewController::colorBarActionTriggered(bool status) { if (overlay == NULL) { return; } this->overlay->getColorBar()->setDisplayed(status); this->updateGraphicsWindow(); } /** * Called when opacity value is changed. * @param value * New value. */ void OverlayViewController::opacityDoubleSpinBoxValueChanged(double value) { if (overlay == NULL) { return; } this->overlay->setOpacity(value); this->updateGraphicsWindow(); } /** * Validate yoking when there are changes made to the overlay. */ void OverlayViewController::validateYokingSelection() { m_mapYokingGroupComboBox->validateYokingChange(this->overlay); updateViewController(this->overlay); updateGraphicsWindow(); //EventManager::get()->sendEvent(EventGraphicsUpdateAllWindows().getPointer()); } /** * Called when the yoking group is changed. */ void OverlayViewController::yokingGroupActivated() { MapYokingGroupEnum::Enum yokingGroup = m_mapYokingGroupComboBox->getMapYokingGroup(); /* * Has yoking group changed? * TSC: overlay can be null when opened without loaded files */ if (overlay != NULL && yokingGroup != overlay->getMapYokingGroup()) { validateYokingSelection(); } } /** * Called when the settings action is selected. */ void OverlayViewController::settingsActionTriggered() { if (overlay == NULL) { return; } CaretMappableDataFile* mapFile; int32_t mapIndex = -1; this->overlay->getSelectionData(mapFile, mapIndex); if (mapFile != NULL) { EventOverlaySettingsEditorDialogRequest pcme(EventOverlaySettingsEditorDialogRequest::MODE_SHOW_EDITOR, this->browserWindowIndex, this->overlay, mapFile, mapIndex); EventManager::get()->sendEvent(pcme.getPointer()); } } /** * Update this view controller using the given overlay. * @param overlay * Overlay that is used in this view controller. */ void OverlayViewController::updateViewController(Overlay* overlay) { this->overlay = overlay; this->fileComboBox->clear(); /* * Get the selection information for the overlay. */ std::vector dataFiles; CaretMappableDataFile* selectedFile = NULL; //AString selectedMapUniqueID = ""; int32_t selectedMapIndex = -1; if (this->overlay != NULL) { this->overlay->getSelectionData(dataFiles, selectedFile, //selectedMapUniqueID, selectedMapIndex); } std::vector displayNames; FilePathNamePrefixCompactor::removeMatchingPathPrefixFromCaretDataFiles(dataFiles, displayNames); CaretAssert(dataFiles.size() == displayNames.size()); /* * Load the file selection combo box. */ 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); this->fileComboBox->addItem(displayNames[i], qVariantFromValue((void*)dataFile)); if (dataFile == selectedFile) { selectedFileIndex = i; } } if (selectedFileIndex >= 0) { this->fileComboBox->setCurrentIndex(selectedFileIndex); } /* * Load the map selection combo box */ int32_t numberOfMaps = 0; this->mapNameComboBox->blockSignals(true); this->mapNameComboBox->clear(); if (selectedFile != NULL) { numberOfMaps = selectedFile->getNumberOfMaps(); for (int32_t i = 0; i < numberOfMaps; i++) { this->mapNameComboBox->addItem(selectedFile->getMapName(i)); } this->mapNameComboBox->setCurrentIndex(selectedMapIndex); } this->mapNameComboBox->blockSignals(false); /* * Load the map index spin box that ranges [1, N]. */ m_mapIndexSpinBox->blockSignals(true); m_mapIndexSpinBox->setRange(1, numberOfMaps); if (selectedFile != NULL) { m_mapIndexSpinBox->setValue(selectedMapIndex + 1); } m_mapIndexSpinBox->blockSignals(false); /* * Update enable check box */ Qt::CheckState checkState = Qt::Unchecked; if (this->overlay != NULL) { if (this->overlay->isEnabled()) { checkState = Qt::Checked; } } this->enabledCheckBox->setCheckState(checkState); m_mapYokingGroupComboBox->setMapYokingGroup(overlay->getMapYokingGroup()); this->colorBarAction->blockSignals(true); this->colorBarAction->setChecked(overlay->getColorBar()->isDisplayed()); this->colorBarAction->blockSignals(false); this->opacityDoubleSpinBox->blockSignals(true); this->opacityDoubleSpinBox->setValue(overlay->getOpacity()); this->opacityDoubleSpinBox->blockSignals(false); const bool haveFile = (selectedFile != NULL); bool haveMultipleMaps = false; bool dataIsMappedWithPalette = false; bool dataIsMappedWithLabelTable = false; bool dataIsMappedWithRGBA = false; bool haveOpacity = false; if (haveFile) { dataIsMappedWithPalette = selectedFile->isMappedWithPalette(); dataIsMappedWithLabelTable = selectedFile->isMappedWithLabelTable(); dataIsMappedWithRGBA = selectedFile->isMappedWithRGBA(); haveMultipleMaps = (selectedFile->getNumberOfMaps() > 1); haveOpacity = (dataIsMappedWithLabelTable || dataIsMappedWithPalette || dataIsMappedWithRGBA); } /** * Yoking is enabled when either: * (1) The file maps to both surface and volumes * (2) The file has multiple maps. */ bool haveYoking = false; if (haveFile) { if (selectedFile->isSurfaceMappable() && selectedFile->isVolumeMappable()) { haveYoking = true; } if (haveMultipleMaps) { haveYoking = true; } } /* * 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" + this->mapNameComboBox->currentText()); } this->fileComboBox->setToolTip(fileComboBoxToolTip); this->mapNameComboBox->setToolTip(nameComboBoxToolTip); /* * Make sure items are enabled at the appropriate time */ this->fileComboBox->setEnabled(haveFile); this->mapNameComboBox->setEnabled(haveFile); this->m_mapIndexSpinBox->setEnabled(haveMultipleMaps); this->enabledCheckBox->setEnabled(haveFile); this->constructionAction->setEnabled(true); this->opacityDoubleSpinBox->setEnabled(haveOpacity); this->m_mapYokingGroupComboBox->getWidget()->setEnabled(haveYoking); this->colorBarAction->setEnabled(dataIsMappedWithPalette); this->settingsAction->setEnabled(true); } /** * Update graphics and GUI after selections made */ void OverlayViewController::updateUserInterfaceAndGraphicsWindow() { updateUserInterface(); updateGraphicsWindow(); } /** * Update graphics and GUI after selections made */ void OverlayViewController::updateUserInterface() { if (this->overlay->getMapYokingGroup() != MapYokingGroupEnum::MAP_YOKING_GROUP_OFF) { EventManager::get()->sendEvent(EventUserInterfaceUpdate().getPointer()); } else { EventManager::get()->sendEvent(EventUserInterfaceUpdate().setWindowIndex(this->browserWindowIndex).getPointer()); } } /** * Update graphics after selections made */ void OverlayViewController::updateGraphicsWindow() { EventManager::get()->sendEvent(EventSurfaceColoringInvalidate().getPointer()); if (this->overlay->getMapYokingGroup() != MapYokingGroupEnum::MAP_YOKING_GROUP_OFF) { EventManager::get()->sendEvent(EventGraphicsUpdateAllWindows().getPointer()); } else { EventManager::get()->sendEvent(EventGraphicsUpdateOneWindow(this->browserWindowIndex).getPointer()); } } /** * Create the construction menu. * @param parent * Parent widget. */ QMenu* OverlayViewController::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 OverlayViewController::menuConstructionAboutToShow() { if (this->overlay != NULL) { CaretMappableDataFile* caretDataFile = NULL; int32_t mapIndex = -1; this->overlay->getSelectionData(caretDataFile, mapIndex); QString menuText = "Reload Selected File"; if (caretDataFile != NULL) { if (caretDataFile->isModified()) { QString suffix = " (MODIFIED)"; if (caretDataFile->isModifiedPaletteColorMapping()) { if ( ! caretDataFile->isModifiedExcludingPaletteColorMapping()) { suffix = " (MODIFIED PALETTE)"; } } menuText += suffix; } } m_constructionReloadFileAction->setText(menuText); } } /** * Add an overlay above this overlay. */ void OverlayViewController::menuAddOverlayAboveTriggered() { emit requestAddOverlayAbove(m_overlayIndex); } /** * Add an overlay below this overlay. */ void OverlayViewController::menuAddOverlayBelowTriggered() { emit requestAddOverlayBelow(m_overlayIndex); } /** * Remove this overlay. */ void OverlayViewController::menuRemoveOverlayTriggered() { emit requestRemoveOverlay(m_overlayIndex); } /** * Move this overlay down. */ void OverlayViewController::menuMoveOverlayDownTriggered() { emit requestMoveOverlayDown(m_overlayIndex); } /** * Move this overlay down. */ void OverlayViewController::menuMoveOverlayUpTriggered() { emit requestMoveOverlayUp(m_overlayIndex); } /** * Copy the file name to the clip board. */ void OverlayViewController::menuCopyFileNameToClipBoard() { if (this->overlay != NULL) { CaretMappableDataFile* caretDataFile = NULL; int32_t mapIndex = -1; this->overlay->getSelectionData(caretDataFile, mapIndex); if (caretDataFile != NULL) { QApplication::clipboard()->setText(caretDataFile->getFileName().trimmed(), QClipboard::Clipboard); } } } /** * Copy the map name to the clip board. */ void OverlayViewController::menuCopyMapNameToClipBoard() { if (this->overlay != NULL) { CaretMappableDataFile* caretDataFile = NULL; int32_t mapIndex = -1; this->overlay->getSelectionData(caretDataFile, mapIndex); if (caretDataFile != NULL) { if ((mapIndex >= 0) && (mapIndex < caretDataFile->getNumberOfMaps())) { QApplication::clipboard()->setText(caretDataFile->getMapName(mapIndex).trimmed(), QClipboard::Clipboard); } } } } /** * Reload the file in the overlay. */ void OverlayViewController::menuReloadFileTriggered() { if (this->overlay != NULL) { CaretMappableDataFile* caretDataFile = NULL; int32_t mapIndex = -1; this->overlay->getSelectionData(caretDataFile, mapIndex); if (caretDataFile != NULL) { AString username; AString password; if (DataFile::isFileOnNetwork(caretDataFile->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(), caretDataFile); reloadEvent.setUsernameAndPassword(username, password); EventManager::get()->sendEvent(reloadEvent.getPointer()); if (reloadEvent.isError()) { WuQMessageBox::errorOk(m_constructionToolButton, reloadEvent.getErrorMessage()); } updateOverlaySettingsEditor(); updateUserInterfaceAndGraphicsWindow(); } } } connectome-workbench-1.2.3+git41-gc4c6c90/src/GuiQt/OverlayViewController.h000066400000000000000000000106451300200146000262740ustar00rootroot00000000000000#ifndef __OVERLAY_VIEW_CONTROLLER__H_ #define __OVERLAY_VIEW_CONTROLLER__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 class QAction; class QCheckBox; class QComboBox; class QDoubleSpinBox; class QGridLayout; class QMenu; class QSpinBox; class QToolButton; namespace caret { class MapYokingGroupComboBox; class Overlay; class WuQGridLayoutGroup; class OverlayViewController : public QObject { Q_OBJECT public: OverlayViewController(const Qt::Orientation orientation, QGridLayout* gridLayout, const int32_t browserWindowIndex, const int32_t overlayIndex, QObject* parent); virtual ~OverlayViewController(); void setVisible(bool visible); void updateViewController(Overlay* overlay); 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 mapNameComboBoxSelected(int); void mapIndexSpinBoxValueChanged(int); void enabledCheckBoxClicked(bool); void colorBarActionTriggered(bool); void settingsActionTriggered(); void opacityDoubleSpinBoxValueChanged(double value); void yokingGroupActivated(); void menuAddOverlayAboveTriggered(); void menuAddOverlayBelowTriggered(); void menuRemoveOverlayTriggered(); void menuMoveOverlayDownTriggered(); void menuMoveOverlayUpTriggered(); void menuReloadFileTriggered(); void menuCopyFileNameToClipBoard(); void menuCopyMapNameToClipBoard(); void menuConstructionAboutToShow(); private: OverlayViewController(const OverlayViewController&); OverlayViewController& operator=(const OverlayViewController&); void updateUserInterfaceAndGraphicsWindow(); void updateUserInterface(); void updateGraphicsWindow(); QMenu* createConstructionMenu(QWidget* parent); void validateYokingSelection(); void updateOverlaySettingsEditor(); const int32_t browserWindowIndex; const int32_t m_overlayIndex; Overlay* overlay; QCheckBox* enabledCheckBox; QComboBox* fileComboBox; QComboBox* mapNameComboBox; QSpinBox* m_mapIndexSpinBox; QDoubleSpinBox* opacityDoubleSpinBox; QToolButton* m_constructionToolButton; QAction* constructionAction; QAction* colorBarAction; QAction* settingsAction; MapYokingGroupComboBox* m_mapYokingGroupComboBox; QAction* m_constructionReloadFileAction; WuQGridLayoutGroup* gridLayoutGroup; friend class OverlaySetViewController; }; #ifdef __OVERLAY_VIEW_CONTROLLER_DECLARE__ // #endif // __OVERLAY_VIEW_CONTROLLER_DECLARE__ } // namespace #endif //__OVERLAY_VIEW_CONTROLLER__H_ connectome-workbench-1.2.3+git41-gc4c6c90/src/GuiQt/PaletteColorMappingEditorDialog.cxx000066400000000000000000000166431300200146000305330ustar00rootroot00000000000000 /*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 #define __PALETTE_COLOR_MAPPING_EDITOR_DIALOG_DECLARE__ #include "PaletteColorMappingEditorDialog.h" #undef __PALETTE_COLOR_MAPPING_EDITOR_DIALOG_DECLARE__ #include "CaretAssert.h" #include "CaretMappableDataFile.h" #include "ChartableMatrixInterface.h" #include "ChartMatrixDisplayProperties.h" #include "EventCaretMappableDataFilesGet.h" #include "EventManager.h" #include "MapSettingsColorBarWidget.h" #include "MapSettingsPaletteColorMappingWidget.h" using namespace caret; /** * \class caret::PaletteColorMappingEditorDialog * \brief Dialog for editing color palettes * \ingroup GuiQt */ /** * Constructor. */ PaletteColorMappingEditorDialog::PaletteColorMappingEditorDialog(QWidget* parent) : WuQDialogNonModal("Palette Color Mapping Editor", parent) { m_mapFile = NULL; m_mapIndex = -1; m_browserTabIndex = -1; QLabel* fileLabel = new QLabel("Map File: "); m_fileNameValueLabel = new QLabel(""); QLabel* mapLabel = new QLabel("Map Name: "); m_mapNameValueLabel = new QLabel(""); m_paletteColorBarWidget = new MapSettingsColorBarWidget(); m_paletteColorMappingEditor = new MapSettingsPaletteColorMappingWidget(this); QGridLayout* namesLayout = new QGridLayout(); namesLayout->setColumnStretch(0, 0); namesLayout->setColumnStretch(1, 100); int namesRow = 0; namesLayout->addWidget(fileLabel, namesRow, 0); namesLayout->addWidget(m_fileNameValueLabel, namesRow, 1, Qt::AlignLeft); namesRow++; namesLayout->addWidget(mapLabel, namesRow, 0); namesLayout->addWidget(m_mapNameValueLabel, namesRow, 1, Qt::AlignLeft); /* * For now, hide map name labels */ mapLabel->hide(); m_mapNameValueLabel->hide(); m_tabWidget = new QTabWidget(); m_paletteColorBarWidgetTabIndex = m_tabWidget->addTab(m_paletteColorBarWidget, "Color Bar"); m_paletteEditorWidgetTabIndex = m_tabWidget->addTab(m_paletteColorMappingEditor, "Palette"); QWidget* w = new QWidget(); QVBoxLayout* layout = new QVBoxLayout(w); layout->addLayout(namesLayout); layout->addWidget(m_tabWidget); setCentralWidget(w, WuQDialog::SCROLL_AREA_NEVER); disableAutoDefaultForAllPushButtons(); EventManager::get()->addProcessedEventListener(this, EventTypeEnum::EVENT_DATA_FILE_DELETE); } /** * Destructor. */ PaletteColorMappingEditorDialog::~PaletteColorMappingEditorDialog() { EventManager::get()->removeAllEventsFromListener(this); } /** * Receive an event. * * @param event * An event for which this instance is listening. */ void PaletteColorMappingEditorDialog::receiveEvent(Event* event) { if (event->getEventType() == EventTypeEnum::EVENT_DATA_FILE_DELETE) { updateDialog(); } } /** * Called for focus events. Since this dialog stores a pointer * to the overlay, we need to be aware that the overlay's parameters * may change or the overlay may even be deleted. So, when * this dialog gains focus, validate the overlay and then update * the dialog. * * @param event * The focus event. */ void PaletteColorMappingEditorDialog::focusInEvent(QFocusEvent* /*event*/) { updateDialog(); } /** * May be called to update the dialog's content. * * @param mapFile * Map file whose map is having its palette color mapping edited * @param mapIndex * Index of the map whose color mapping is being edited. */ void PaletteColorMappingEditorDialog::updateDialogContent(CaretMappableDataFile* mapFile, const int32_t mapIndex, const int32_t browserTabIndex) { m_mapFile = mapFile; m_mapIndex = mapIndex; m_browserTabIndex = browserTabIndex; bool enableEditor = false; if (m_mapFile != NULL) { if ((m_mapIndex >= 0) && (m_mapIndex < m_mapFile->getNumberOfMaps())) { if (browserTabIndex >= 0) { enableEditor = true; } } } if (enableEditor) { m_fileNameValueLabel->setText(m_mapFile->getFileNameNoPath()); m_mapNameValueLabel->setText(m_mapFile->getMapName(m_mapIndex)); m_paletteColorBarWidget->setEnabled(true); AnnotationColorBar* colorBar = NULL; PaletteColorMapping* paletteColorMapping = mapFile->getMapPaletteColorMapping(m_mapIndex); ChartableMatrixInterface* matrixInterface = dynamic_cast(m_mapFile); if (matrixInterface != NULL) { ChartMatrixDisplayProperties* matrixProps = matrixInterface->getChartMatrixDisplayProperties(m_browserTabIndex); if (matrixProps != NULL) { colorBar = matrixProps->getColorBar(); } } m_paletteColorBarWidget->updateContent(colorBar, paletteColorMapping); m_paletteColorMappingEditor->setEnabled(true); m_paletteColorMappingEditor->updateEditor(m_mapFile, m_mapIndex); } else { m_fileNameValueLabel->setText(""); m_mapNameValueLabel->setText(""); m_paletteColorBarWidget->setEnabled(false); m_paletteColorMappingEditor->setEnabled(false); } } /** * May be called to update the dialog. */ void PaletteColorMappingEditorDialog::updateDialog() { if (m_mapFile != NULL) { /* * Validate map file to prevent crash */ EventCaretMappableDataFilesGet getMapFilesEvent; EventManager::get()->sendEvent(getMapFilesEvent.getPointer()); std::vector allMapFiles; getMapFilesEvent.getAllFiles(allMapFiles); if (std::find(allMapFiles.begin(), allMapFiles.end(), m_mapFile) != allMapFiles.end()) { if (m_mapIndex < 0) { m_mapIndex = 0; } if (m_mapFile->getNumberOfMaps() > 0) { if (m_mapIndex >= m_mapFile->getNumberOfMaps()) { m_mapIndex = m_mapFile->getNumberOfMaps() - 1; } } else { m_mapFile = NULL; m_mapIndex = -1; } } else { m_mapFile = NULL; m_mapIndex = -1; } updateDialogContent(m_mapFile, m_mapIndex, m_browserTabIndex); } if (m_mapFile == NULL) { close(); } } connectome-workbench-1.2.3+git41-gc4c6c90/src/GuiQt/PaletteColorMappingEditorDialog.h000066400000000000000000000055751300200146000301620ustar00rootroot00000000000000#ifndef __PALETTE_COLOR_MAPPING_EDITOR_DIALOG_H__ #define __PALETTE_COLOR_MAPPING_EDITOR_DIALOG_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 "EventListenerInterface.h" #include "WuQDialogNonModal.h" class QLabel; class QTabWidget; namespace caret { class CaretMappableDataFile; class MapSettingsColorBarWidget; class MapSettingsPaletteColorMappingWidget; class PaletteColorMappingEditorDialog : public WuQDialogNonModal, public EventListenerInterface { Q_OBJECT public: PaletteColorMappingEditorDialog(QWidget* parent = 0); virtual ~PaletteColorMappingEditorDialog(); virtual void receiveEvent(Event* event); /** May be called requesting the dialog to update its content */ virtual void updateDialog(); void updateDialogContent(CaretMappableDataFile* mapFile, const int32_t mapIndex, const int32_t browserTabIndex); protected: virtual void focusInEvent(QFocusEvent* event); // ADD_NEW_METHODS_HERE private: PaletteColorMappingEditorDialog(const PaletteColorMappingEditorDialog&); PaletteColorMappingEditorDialog& operator=(const PaletteColorMappingEditorDialog&); CaretMappableDataFile* m_mapFile; int32_t m_mapIndex; int32_t m_browserTabIndex; QTabWidget* m_tabWidget; MapSettingsColorBarWidget* m_paletteColorBarWidget; MapSettingsPaletteColorMappingWidget* m_paletteColorMappingEditor; QLabel* m_fileNameValueLabel; QLabel* m_mapNameValueLabel; int32_t m_paletteColorBarWidgetTabIndex; int32_t m_paletteEditorWidgetTabIndex; // ADD_NEW_MEMBERS_HERE }; #ifdef __PALETTE_COLOR_MAPPING_EDITOR_DIALOG_DECLARE__ // #endif // __PALETTE_COLOR_MAPPING_EDITOR_DIALOG_DECLARE__ } // namespace #endif //__PALETTE_COLOR_MAPPING_EDITOR_DIALOG_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/GuiQt/PlotMagnifier.cxx000066400000000000000000000111741300200146000250650ustar00rootroot00000000000000/*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 caret::PlotMagnifier * \brief Helper class for magnifying Qwt plots * \ingroup GuiQt */ #include "PlotMagnifier.h" using namespace caret; PlotMagnifier::PlotMagnifier(QwtPlotCanvas * canvas) : QwtPlotMagnifier(canvas) { this->setEnabled(true); //The three function calls below reverse the default behavior of zooming so that it is consistent with //brainview's zooming this->setMouseFactor(1.05);//inverse of default value, 0.95 this->setWheelFactor(1.11);//inverse of default value, 0.9 this->setMouseButton(Qt::LeftButton); } bool PlotMagnifier::eventFilter(QObject * object,QEvent *event) { if ( object && (object == parent() || object == parent()->parent()) ) { switch ( event->type() ) { case QEvent::MouseButtonPress: { widgetMousePressEvent( ( QMouseEvent * )event ); break; } case QEvent::MouseMove: { widgetMouseMoveEvent( ( QMouseEvent * )event ); break; } case QEvent::MouseButtonRelease: { widgetMouseReleaseEvent( ( QMouseEvent * )event ); break; } case QEvent::Wheel: { widgetWheelEvent( ( QWheelEvent * )event ); break; } case QEvent::KeyPress: { widgetKeyPressEvent( ( QKeyEvent * )event ); break; } case QEvent::KeyRelease: { widgetKeyReleaseEvent( ( QKeyEvent * )event ); break; } default:; } } return QObject::eventFilter( object, event ); } /*! Handle a mouse press event for the observed widget. \param mouseEvent Mouse event \sa eventFilter(), widgetMouseReleaseEvent(), widgetMouseMoveEvent() */ void PlotMagnifier::widgetMousePressEvent( QMouseEvent *mouseEvent ) { if(mouseEvent->modifiers() != Qt::ControlModifier || mouseEvent->modifiers() == Qt::ShiftModifier) { return; } mouseEvent->setModifiers(Qt::NoModifier); // remove modifier so base class accepts event, we ignore events when shift key is pressed so panning works QwtPlotMagnifier::widgetMousePressEvent( mouseEvent); } /*! Handle a mouse release event for the observed widget. \param mouseEvent Mouse event \sa eventFilter(), widgetMousePressEvent(), widgetMouseMoveEvent(), */ void PlotMagnifier::widgetMouseReleaseEvent( QMouseEvent *mouseEvent ) { /*if(mouseEvent->modifiers() != Qt::ControlModifier || mouseEvent->modifiers() == Qt::ShiftModifier) { return; }*/ mouseEvent->setModifiers(Qt::NoModifier); // remove modifier so base class accepts event, we ignore events when shift key is pressed so panning works QwtPlotMagnifier::widgetMouseReleaseEvent( mouseEvent); } /*! Handle a mouse move event for the observed widget. \param mouseEvent Mouse event \sa eventFilter(), widgetMousePressEvent(), widgetMouseReleaseEvent(), */ void PlotMagnifier::widgetMouseMoveEvent( QMouseEvent *mouseEvent ) { if(mouseEvent->modifiers() != Qt::ControlModifier || mouseEvent->modifiers() == Qt::ShiftModifier) { return; } mouseEvent->setModifiers(Qt::NoModifier);// remove modifier so base class accepts event, we ignore events when shift key is pressed so panning works QwtPlotMagnifier::widgetMouseMoveEvent( mouseEvent); } /*! Handle a wheel event for the observed widget. \param wheelEvent Wheel event \sa eventFilter() */ void PlotMagnifier::widgetWheelEvent( QWheelEvent *wheelEvent ) { // remove modifier so base class accepts event wheelEvent->setModifiers(Qt::NoModifier); QwtPlotMagnifier::widgetWheelEvent( wheelEvent); }connectome-workbench-1.2.3+git41-gc4c6c90/src/GuiQt/PlotMagnifier.h000066400000000000000000000034341300200146000245120ustar00rootroot00000000000000#ifndef __PLOT_MAGNIFIER__ #define __PLOT_MAGNIFIER__ /*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" //qwt includes #include #include "qlayout.h" #include "qwt_plot.h" #include "qwt_plot_marker.h" #include "qwt_plot_curve.h" #include "qwt_legend.h" #include "qwt_series_data.h" #include "qwt_plot_canvas.h" #include "qwt_plot_panner.h" #include "qwt_plot_magnifier.h" #include "qwt_text.h" #include "qwt_math.h" #include namespace caret { class PlotMagnifier : public QwtPlotMagnifier { Q_OBJECT public: PlotMagnifier(QwtPlotCanvas * canvas = NULL); protected: virtual bool eventFilter(QObject * object,QEvent *event); virtual void widgetWheelEvent( QWheelEvent *wheelEvent ); virtual void widgetMouseMoveEvent( QMouseEvent *mouseEvent ); virtual void widgetMousePressEvent( QMouseEvent *mouseEvent ); virtual void widgetMouseReleaseEvent( QMouseEvent *mouseEvent ); private: }; } #endif //__PLOT_MAGNIFIER__ connectome-workbench-1.2.3+git41-gc4c6c90/src/GuiQt/PlotPanner.cxx000066400000000000000000000075711300200146000244150ustar00rootroot00000000000000/*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 caret::PlotPanner * \brief Helper class for panning Qwt plots * \ingroup GuiQt */ #include "PlotPanner.h" using namespace caret; PlotPanner::PlotPanner(QwtPlotCanvas * canvas) : QwtPlotPanner(canvas) { this->setEnabled(true); this->setMouseButton(Qt::LeftButton); } bool PlotPanner::eventFilter(QObject * object,QEvent *event) { if ( object && (object == parent() || object == parent()->parent()) ) { switch ( event->type() ) { case QEvent::MouseButtonPress: { widgetMousePressEvent( ( QMouseEvent * )event ); break; } case QEvent::MouseMove: { widgetMouseMoveEvent( ( QMouseEvent * )event ); break; } case QEvent::MouseButtonRelease: { widgetMouseReleaseEvent( ( QMouseEvent * )event ); break; } case QEvent::KeyPress: { widgetKeyPressEvent( ( QKeyEvent * )event ); break; } case QEvent::KeyRelease: { widgetKeyReleaseEvent( ( QKeyEvent * )event ); break; } default:; } } return QObject::eventFilter( object, event ); } /*! Handle a mouse press event for the observed widget. \param mouseEvent Mouse event \sa eventFilter(), widgetMouseReleaseEvent(), widgetMouseMoveEvent() */ void PlotPanner::widgetMousePressEvent( QMouseEvent *mouseEvent ) { if(mouseEvent->modifiers() != Qt::ShiftModifier || mouseEvent->modifiers() == Qt::ControlModifier) { return; } mouseEvent->setModifiers(Qt::NoModifier); // remove modifier so base class accepts event, we ignore events when shift key is pressed so panning works QwtPlotPanner::widgetMousePressEvent( mouseEvent); } /*! Handle a mouse release event for the observed widget. \param mouseEvent Mouse event \sa eventFilter(), widgetMousePressEvent(), widgetMouseMoveEvent(), */ void PlotPanner::widgetMouseReleaseEvent( QMouseEvent *mouseEvent ) { /*if(mouseEvent->modifiers() != Qt::ShiftModifier || mouseEvent->modifiers() == Qt::ControlModifier) { return; }*/ mouseEvent->setModifiers(Qt::NoModifier); // remove modifier so base class accepts event, we ignore events when shift key is pressed so panning works QwtPlotPanner::widgetMouseReleaseEvent( mouseEvent); } /*! Handle a mouse move event for the observed widget. \param mouseEvent Mouse event \sa eventFilter(), widgetMousePressEvent(), widgetMouseReleaseEvent(), */ void PlotPanner::widgetMouseMoveEvent( QMouseEvent *mouseEvent ) { if(mouseEvent->modifiers() != Qt::ShiftModifier || mouseEvent->modifiers() == Qt::ControlModifier) { return; } mouseEvent->setModifiers(Qt::NoModifier);// remove modifier so base class accepts event, we ignore events when shift key is pressed so panning works QwtPlotPanner::widgetMouseMoveEvent( mouseEvent); } connectome-workbench-1.2.3+git41-gc4c6c90/src/GuiQt/PlotPanner.h000066400000000000000000000033141300200146000240310ustar00rootroot00000000000000#ifndef __PLOT_PANNER__ #define __PLOT_PANNER__ /*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" //qwt includes #include #include "qlayout.h" #include "qwt_plot.h" #include "qwt_plot_marker.h" #include "qwt_plot_curve.h" #include "qwt_legend.h" #include "qwt_series_data.h" #include "qwt_plot_canvas.h" #include "qwt_plot_panner.h" #include "qwt_plot_magnifier.h" #include "qwt_text.h" #include "qwt_math.h" #include namespace caret { class PlotPanner : public QwtPlotPanner { Q_OBJECT public: PlotPanner(QwtPlotCanvas * canvas = NULL); protected: virtual bool eventFilter(QObject * object,QEvent *event); virtual void widgetMouseMoveEvent( QMouseEvent *mouseEvent ); virtual void widgetMousePressEvent( QMouseEvent *mouseEvent ); virtual void widgetMouseReleaseEvent( QMouseEvent *mouseEvent ); private: }; } #endif //__PLOT_PANNER__ connectome-workbench-1.2.3+git41-gc4c6c90/src/GuiQt/PreferencesDialog.cxx000066400000000000000000001110601300200146000257010ustar00rootroot00000000000000 /*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 #include #include #define __PREFERENCES_DIALOG__H__DECLARE__ #include "PreferencesDialog.h" #undef __PREFERENCES_DIALOG__H__DECLARE__ #include "Brain.h" #include "CaretAssert.h" #include "CaretLogger.h" #include "CaretPreferences.h" #include "EnumComboBoxTemplate.h" #include "EventGraphicsUpdateAllWindows.h" #include "EventManager.h" #include "GuiManager.h" #include "ImageCaptureMethodEnum.h" #include "OpenGLDrawingMethodEnum.h" #include "SessionManager.h" #include "WuQtUtilities.h" #include "WuQFactory.h" #include "WuQMessageBox.h" #include "WuQTrueFalseComboBox.h" #include "WuQWidgetObjectGroup.h" using namespace caret; /** * \class caret::PreferencesDialog * \brief Dialog for display/editing of prefernces. * \ingroup GuiQt * * Presents controls for editing palettes used to color * scalar data. */ /** * Constructor for editing a palette selection. * * @param parent * Parent widget on which this dialog is displayed. */ PreferencesDialog::PreferencesDialog(QWidget* parent) : WuQDialogNonModal("Preferences", parent) { setDeleteWhenClosed(false); /* * No apply button */ setApplyButtonText(""); /* * Used to block signals in all widgets */ m_allWidgets = new WuQWidgetObjectGroup(this); /* * Create the tab widget and all tab content */ QTabWidget* tabWidget = new QTabWidget(); tabWidget->addTab(createColorsWidget(), "Colors"); tabWidget->addTab(createIdentificationSymbolWidget(), "ID"); tabWidget->addTab(createMiscellaneousWidget(), "Misc"); tabWidget->addTab(createOpenGLWidget(), "OpenGL"); tabWidget->addTab(createVolumeWidget(), "Volume"); setCentralWidget(tabWidget, WuQDialog::SCROLL_AREA_NEVER); disableAutoDefaultForAllPushButtons(); } /** * Destructor. */ PreferencesDialog::~PreferencesDialog() { } /** * Add a color button and swatch. * * @param gridLayout * Grid layout for widgets. * @param prefColor * Enumerated value for color. * @param colorSignalMapper * Signal mapper for buttons. */ void PreferencesDialog::addColorButtonAndSwatch(QGridLayout* gridLayout, const PREF_COLOR prefColor, QSignalMapper* colorSignalMapper) { QString buttonText; QWidget* colorSwatchWidget = new QWidget(); switch (prefColor) { case PREF_COLOR_BACKGROUND_ALL: buttonText = "All Background"; m_backgroundColorAllWidget = colorSwatchWidget; break; case PREF_COLOR_BACKGROUND_CHART: buttonText = "Chart Background"; m_backgroundColorChartWidget = colorSwatchWidget; break; case PREF_COLOR_BACKGROUND_SURFACE: buttonText = "Surface Background"; m_backgroundColorSurfaceWidget = colorSwatchWidget; break; case PREF_COLOR_BACKGROUND_VOLUME: buttonText = "Volume Background"; m_backgroundColorVolumeWidget = colorSwatchWidget; break; case PREF_COLOR_FOREGROUND_ALL: buttonText = "All Foreground"; m_foregroundColorAllWidget = colorSwatchWidget; break; case PREF_COLOR_FOREGROUND_CHART: buttonText = "Chart Foreground"; m_foregroundColorChartWidget = colorSwatchWidget; break; case PREF_COLOR_FOREGROUND_SURFACE: buttonText = "Surface Foreground"; m_foregroundColorSurfaceWidget = colorSwatchWidget; break; case PREF_COLOR_FOREGROUND_VOLUME: buttonText = "Volume Foreground"; m_foregroundColorVolumeWidget = colorSwatchWidget; break; case PREF_COLOR_CHART_MATRIX_GRID_LINES: buttonText = "Chart Grid Lines"; m_chartMatrixGridLinesColorWidget = colorSwatchWidget; break; case NUMBER_OF_PREF_COLORS: CaretAssert(0); break; } buttonText.append("..."); CaretAssert( ! buttonText.isEmpty()); QPushButton* colorPushButton = new QPushButton(buttonText); QObject::connect(colorPushButton, SIGNAL(clicked()), colorSignalMapper, SLOT(map())); colorSignalMapper->setMapping(colorPushButton, (int)prefColor); addWidgetsToLayout(gridLayout, colorPushButton, colorSwatchWidget); } /** * @return The colors widget. */ QWidget* PreferencesDialog::createColorsWidget() { QSignalMapper* colorSignalMapper = new QSignalMapper(this); QGridLayout* gridLayout = new QGridLayout(); addColorButtonAndSwatch(gridLayout, PREF_COLOR_FOREGROUND_ALL, colorSignalMapper); addColorButtonAndSwatch(gridLayout, PREF_COLOR_BACKGROUND_ALL, colorSignalMapper); addColorButtonAndSwatch(gridLayout, PREF_COLOR_FOREGROUND_CHART, colorSignalMapper); addColorButtonAndSwatch(gridLayout, PREF_COLOR_BACKGROUND_CHART, colorSignalMapper); addColorButtonAndSwatch(gridLayout, PREF_COLOR_CHART_MATRIX_GRID_LINES, colorSignalMapper); addColorButtonAndSwatch(gridLayout, PREF_COLOR_FOREGROUND_SURFACE, colorSignalMapper); addColorButtonAndSwatch(gridLayout, PREF_COLOR_BACKGROUND_SURFACE, colorSignalMapper); addColorButtonAndSwatch(gridLayout, PREF_COLOR_FOREGROUND_VOLUME, colorSignalMapper); addColorButtonAndSwatch(gridLayout, PREF_COLOR_BACKGROUND_VOLUME, colorSignalMapper); QObject::connect(colorSignalMapper, SIGNAL(mapped(int)), this, SLOT(colorPushButtonClicked(int))); m_allWidgets->add(colorSignalMapper); QWidget* widget = new QWidget(); QVBoxLayout* layout = new QVBoxLayout(widget); layout->addLayout(gridLayout); layout->addStretch(); return widget; } /** * Update the color widget's items. * * @param prefs * The Caret preferences. */ void PreferencesDialog::updateColorWidget(CaretPreferences* prefs) { const BackgroundAndForegroundColors colors = prefs->getUserBackgroundAndForegroundColors(); for (int32_t i = 0; i < NUMBER_OF_PREF_COLORS; i++) { const PREF_COLOR prefColor = (PREF_COLOR)i; uint8_t rgb[3] = { 0, 0, 0 }; QWidget* colorSwatchWidget = NULL; switch (prefColor) { case PREF_COLOR_BACKGROUND_ALL: colors.getColorBackgroundAllView(rgb); colorSwatchWidget = m_backgroundColorAllWidget; break; case PREF_COLOR_BACKGROUND_CHART: colors.getColorBackgroundChartView(rgb); colorSwatchWidget = m_backgroundColorChartWidget; break; case PREF_COLOR_BACKGROUND_SURFACE: colors.getColorBackgroundSurfaceView(rgb); colorSwatchWidget = m_backgroundColorSurfaceWidget; break; case PREF_COLOR_BACKGROUND_VOLUME: colors.getColorBackgroundVolumeView(rgb); colorSwatchWidget = m_backgroundColorVolumeWidget; break; case PREF_COLOR_FOREGROUND_ALL: colors.getColorForegroundAllView(rgb); colorSwatchWidget = m_foregroundColorAllWidget; break; case PREF_COLOR_FOREGROUND_CHART: colors.getColorForegroundChartView(rgb); colorSwatchWidget = m_foregroundColorChartWidget; break; case PREF_COLOR_FOREGROUND_SURFACE: colors.getColorForegroundSurfaceView(rgb); colorSwatchWidget = m_foregroundColorSurfaceWidget; break; case PREF_COLOR_FOREGROUND_VOLUME: colors.getColorForegroundVolumeView(rgb); colorSwatchWidget = m_foregroundColorVolumeWidget; break; case PREF_COLOR_CHART_MATRIX_GRID_LINES: colors.getColorChartMatrixGridLines(rgb); colorSwatchWidget = m_chartMatrixGridLinesColorWidget; break; case NUMBER_OF_PREF_COLORS: CaretAssert(0); break; } CaretAssert(colorSwatchWidget); colorSwatchWidget->setStyleSheet("background-color: rgb(" + AString::number(rgb[0]) + ", " + AString::number(rgb[1]) + ", " + AString::number(rgb[2]) + ");"); } } /** * @return The miscellaneous widget. */ QWidget* PreferencesDialog::createMiscellaneousWidget() { /* * Dynamic connectivity defautl */ m_dynamicConnectivityComboBox = new WuQTrueFalseComboBox("On", "Off", this); QObject::connect(m_dynamicConnectivityComboBox, SIGNAL(statusChanged(bool)), this, SLOT(miscDynamicConnectivityComboBoxChanged(bool))); m_allWidgets->add(m_dynamicConnectivityComboBox); /* * Logging Level */ m_miscLoggingLevelComboBox = new QComboBox(); std::vector loggingLevels; LogLevelEnum::getAllEnums(loggingLevels); const int32_t numLogLevels = static_cast(loggingLevels.size()); for (int32_t i = 0; i < numLogLevels; i++) { const LogLevelEnum::Enum logLevel = loggingLevels[i]; m_miscLoggingLevelComboBox->addItem(LogLevelEnum::toGuiName(logLevel)); m_miscLoggingLevelComboBox->setItemData(i, LogLevelEnum::toIntegerCode(logLevel)); } QObject::connect(m_miscLoggingLevelComboBox, SIGNAL(currentIndexChanged(int)), this, SLOT(miscLoggingLevelComboBoxChanged(int))); m_allWidgets->add(m_miscLoggingLevelComboBox); /* * Splash Screen */ m_miscSplashScreenShowAtStartupComboBox = new WuQTrueFalseComboBox("On", "Off", this); QObject::connect(m_miscSplashScreenShowAtStartupComboBox, SIGNAL(statusChanged(bool)), 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 */ m_miscDevelopMenuEnabledComboBox = new WuQTrueFalseComboBox("On", "Off", this); QObject::connect(m_miscDevelopMenuEnabledComboBox, SIGNAL(statusChanged(bool)), this, SLOT(miscDevelopMenuEnabledComboBoxChanged(bool))); m_allWidgets->add(m_miscDevelopMenuEnabledComboBox); /* * Manage Files View Files Type */ m_miscSpecFileDialogViewFilesTypeEnumComboBox = new EnumComboBoxTemplate(this); m_miscSpecFileDialogViewFilesTypeEnumComboBox->setup(); QObject::connect(m_miscSpecFileDialogViewFilesTypeEnumComboBox, SIGNAL(itemActivated()), this, SLOT(miscSpecFileDialogViewFilesTypeEnumComboBoxItemActivated())); m_allWidgets->add(m_miscSpecFileDialogViewFilesTypeEnumComboBox->getWidget()); QGridLayout* gridLayout = new QGridLayout(); addWidgetToLayout(gridLayout, "Show Dynconn By Default: ", m_dynamicConnectivityComboBox->getWidget()); addWidgetToLayout(gridLayout, "Logging Level: ", m_miscLoggingLevelComboBox); 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()); addWidgetToLayout(gridLayout, "Show Splash Screen at Startup: ", m_miscSplashScreenShowAtStartupComboBox->getWidget()); QWidget* widget = new QWidget(); QVBoxLayout* layout = new QVBoxLayout(widget); layout->addLayout(gridLayout); layout->addStretch(); return widget; } /** * Update the miscellaneous widget's items. * * @param prefs * The Caret preferences. */ void PreferencesDialog::updateMiscellaneousWidget(CaretPreferences* prefs) { m_dynamicConnectivityComboBox->setStatus(prefs->isDynamicConnectivityDefaultedOn()); const LogLevelEnum::Enum loggingLevel = prefs->getLoggingLevel(); int indx = m_miscLoggingLevelComboBox->findData(LogLevelEnum::toIntegerCode(loggingLevel)); if (indx >= 0) { m_miscLoggingLevelComboBox->setCurrentIndex(indx); } m_miscDevelopMenuEnabledComboBox->setStatus(prefs->isDevelopMenuEnabled()); m_miscSplashScreenShowAtStartupComboBox->setStatus(prefs->isSplashScreenEnabled()); m_yokingDefaultComboBox->setStatus(prefs->isYokingDefaultedOn()); m_miscSpecFileDialogViewFilesTypeEnumComboBox->setSelectedItem(prefs->getManageFilesViewFileType()); } /** * @return The identification symbol widget. */ QWidget* PreferencesDialog::createIdentificationSymbolWidget() { QLabel* surfaceLabel = new QLabel("Show Surface ID Symbols"); 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); QWidget* widget = new QWidget(); QVBoxLayout* layout = new QVBoxLayout(widget); layout->addLayout(gridLayout); layout->addStretch(); return widget; } /** * Update the identification widget. * * @param prefs * The Caret preferences. */ void PreferencesDialog::updateIdentificationWidget(CaretPreferences* prefs) { m_surfaceIdentificationSymbolComboBox->setStatus(prefs->isShowSurfaceIdentificationSymbols()); m_volumeIdentificationSymbolComboBox->setStatus(prefs->isShowVolumeIdentificationSymbols()); } /** * Gets called when an identification symbol check box is toggled. */ void PreferencesDialog::identificationSymbolToggled() { CaretPreferences* prefs = SessionManager::get()->getCaretPreferences(); prefs->setShowSurfaceIdentificationSymbols(m_surfaceIdentificationSymbolComboBox->isTrue()); prefs->setShowVolumeIdentificationSymbols(m_volumeIdentificationSymbolComboBox->isTrue()); } /** * @return The OpenGL widget. */ QWidget* PreferencesDialog::createOpenGLWidget() { /* * Image Capture Method */ m_openGLImageCaptureMethodEnumComboBox = new EnumComboBoxTemplate(this); m_openGLImageCaptureMethodEnumComboBox->setup(); QObject::connect(m_openGLImageCaptureMethodEnumComboBox, SIGNAL(itemActivated()), this, SLOT(openGLImageCaptureMethodEnumComboBoxItemActivated())); const AString captureMethodToolTip = ("Sometimes, the default image capture method fails to " "function correctly and the captured image does not match " "the content of the graphics window. If this occurs, " "try changing the Capture Method to Grab Frame Buffer."); WuQtUtilities::setWordWrappedToolTip(m_openGLImageCaptureMethodEnumComboBox->getComboBox(), captureMethodToolTip); m_allWidgets->add(m_openGLImageCaptureMethodEnumComboBox->getWidget()); /* * OpenGL Drawing Method */ m_openGLDrawingMethodEnumComboBox = new EnumComboBoxTemplate(this); m_openGLDrawingMethodEnumComboBox->setup(); QObject::connect(m_openGLDrawingMethodEnumComboBox, SIGNAL(itemActivated()), this, SLOT(openGLDrawingMethodEnumComboBoxItemActivated())); m_allWidgets->add(m_openGLDrawingMethodEnumComboBox->getWidget()); QGridLayout* gridLayout = new QGridLayout(); addWidgetToLayout(gridLayout, "Image Capture Method: ", m_openGLImageCaptureMethodEnumComboBox->getWidget()); QLabel* vertexBuffersLabel = addWidgetToLayout(gridLayout, "OpenGL Vertex Buffers: ", m_openGLDrawingMethodEnumComboBox->getWidget()); /* * HIDE THE VERTEX BUFFERS OPTION */ vertexBuffersLabel->setHidden(true); m_openGLDrawingMethodEnumComboBox->getWidget()->setHidden(true); QWidget* widget = new QWidget(); QVBoxLayout* layout = new QVBoxLayout(widget); layout->addLayout(gridLayout); layout->addStretch(); return widget; } /** * Update the OpenGL widget's items. * * @param prefs * The Caret preferences. */ void PreferencesDialog::updateOpenGLWidget(CaretPreferences* prefs) { const ImageCaptureMethodEnum::Enum captureMethod = prefs->getImageCaptureMethod(); m_openGLImageCaptureMethodEnumComboBox->setSelectedItem(captureMethod); const OpenGLDrawingMethodEnum::Enum drawingMethod = prefs->getOpenDrawingMethod(); m_openGLDrawingMethodEnumComboBox->setSelectedItem(drawingMethod); } /** * @return The volume widget. */ QWidget* PreferencesDialog::createVolumeWidget() { /* * Crosshairs On/Off */ m_volumeAxesCrosshairsComboBox = new WuQTrueFalseComboBox("On", "Off", this); QObject::connect(m_volumeAxesCrosshairsComboBox, SIGNAL(statusChanged(bool)), this, SLOT(volumeAxesCrosshairsComboBoxToggled(bool))); m_allWidgets->add(m_volumeAxesCrosshairsComboBox); /* * Axes Labels On/Off */ m_volumeAxesLabelsComboBox = new WuQTrueFalseComboBox("On", "Off", this); QObject::connect(m_volumeAxesLabelsComboBox, SIGNAL(statusChanged(bool)), this, SLOT(volumeAxesLabelsComboBoxToggled(bool))); m_allWidgets->add(m_volumeAxesLabelsComboBox); /* * Identification On/Off */ m_volumeIdentificationComboBox = new WuQTrueFalseComboBox("On", "Off", this); QObject::connect(m_volumeIdentificationComboBox, SIGNAL(statusChanged(bool)), this, SLOT(volumeIdentificationComboBoxToggled(bool))); m_allWidgets->add(m_volumeIdentificationComboBox); /* * Montage Coordinates On/Off */ m_volumeAxesMontageCoordinatesComboBox = new WuQTrueFalseComboBox("On", "Off", this); QObject::connect(m_volumeAxesMontageCoordinatesComboBox, SIGNAL(statusChanged(bool)), 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))); 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(); addWidgetToLayout(gridLayout, "Volume Axes Crosshairs: ", m_volumeAxesCrosshairsComboBox->getWidget()); addWidgetToLayout(gridLayout, "Volume Axes Labels: ", m_volumeAxesLabelsComboBox->getWidget()); addWidgetToLayout(gridLayout, "Volume Identification For New Tabs: ", 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); QWidget* widget = new QWidget(); QVBoxLayout* layout = new QVBoxLayout(widget); layout->addLayout(gridLayout); layout->addStretch(); return widget; } /** * Update the Volume widget's items. * * @param prefs * The Caret preferences. */ void PreferencesDialog::updateVolumeWidget(CaretPreferences* prefs) { m_volumeAxesCrosshairsComboBox->setStatus(prefs->isVolumeAxesCrosshairsDisplayed()); 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()); } /** * Add a label in the left column and the widget in the right column. * * @param gridLayout * The grid layout to which the widgets are added. * @param labelText * Text for label. * @param widget * Widget for right column. * @return * The label that corresponds to the widget. */ QLabel* PreferencesDialog::addWidgetToLayout(QGridLayout* gridLayout, const QString& labelText, QWidget* widget) { QLabel* label = new QLabel(labelText); label->setAlignment(Qt::AlignRight); addWidgetsToLayout(gridLayout, label, widget); return label; } /** * Add widgets to the layout. If rightWidget is NULL, * leftItem spans both columns. * * @param leftWidget * Widget for left column. * @param rightWidget * Widget for right column. */ void PreferencesDialog::addWidgetsToLayout(QGridLayout* gridLayout, QWidget* leftWidget, QWidget* rightWidget) { int row = gridLayout->rowCount(); if (rightWidget != NULL) { gridLayout->addWidget(leftWidget, row, 0); gridLayout->addWidget(rightWidget, row, 1); } else { gridLayout->addWidget(leftWidget, row, 0, 1, 2, Qt::AlignLeft); } } /** * May be called to update the dialog's content. */ void PreferencesDialog::updateDialog() { m_allWidgets->blockAllSignals(true); CaretPreferences* prefs = SessionManager::get()->getCaretPreferences(); updateColorWidget(prefs); updateMiscellaneousWidget(prefs); updateIdentificationWidget(prefs); updateOpenGLWidget(prefs); updateVolumeWidget(prefs); m_allWidgets->blockAllSignals(false); } /** * Update the colors in the dialog. * * @param prefColor * Color that will be updated. */ void PreferencesDialog::updateColorWithDialog(const PREF_COLOR prefColor) { CaretPreferences* prefs = SessionManager::get()->getCaretPreferences(); const BackgroundAndForegroundColors colors = prefs->getUserBackgroundAndForegroundColors(); uint8_t rgb[3]; AString prefColorName; switch (prefColor) { case PREF_COLOR_BACKGROUND_ALL: colors.getColorBackgroundAllView(rgb); prefColorName = "Background - All"; break; case PREF_COLOR_BACKGROUND_CHART: colors.getColorBackgroundChartView(rgb); prefColorName = "Background - Chart"; break; case PREF_COLOR_BACKGROUND_SURFACE: colors.getColorBackgroundSurfaceView(rgb); prefColorName = "Background - Surface"; break; case PREF_COLOR_BACKGROUND_VOLUME: colors.getColorBackgroundVolumeView(rgb); prefColorName = "Background - Volume"; break; case PREF_COLOR_FOREGROUND_ALL: colors.getColorForegroundAllView(rgb); prefColorName = "Foreground - All"; break; case PREF_COLOR_FOREGROUND_CHART: colors.getColorForegroundChartView(rgb); prefColorName = "Foreground - Chart"; break; case PREF_COLOR_FOREGROUND_SURFACE: colors.getColorForegroundSurfaceView(rgb); prefColorName = "Foreground - Surface"; break; case PREF_COLOR_FOREGROUND_VOLUME: colors.getColorForegroundVolumeView(rgb); prefColorName = "Foreground - Volume"; break; case PREF_COLOR_CHART_MATRIX_GRID_LINES: colors.getColorChartMatrixGridLines(rgb); prefColorName = "Chart Matrix Grid Lines"; break; case NUMBER_OF_PREF_COLORS: CaretAssert(0); break; } const QColor initialColor(rgb[0], rgb[1], rgb[2]); QColorDialog colorDialog(this); colorDialog.setCurrentColor(initialColor); colorDialog.setOption(QColorDialog::DontUseNativeDialog); colorDialog.setWindowTitle(prefColorName); if (colorDialog.exec() == QColorDialog::Accepted) { const QColor newColor = colorDialog.currentColor(); rgb[0] = newColor.red(); rgb[1] = newColor.green(); rgb[2] = newColor.blue(); BackgroundAndForegroundColors colors = prefs->getUserBackgroundAndForegroundColors(); switch (prefColor) { case PREF_COLOR_BACKGROUND_ALL: colors.setColorBackgroundAllView(rgb); break; case PREF_COLOR_BACKGROUND_CHART: colors.setColorBackgroundChartView(rgb); break; case PREF_COLOR_BACKGROUND_SURFACE: colors.setColorBackgroundSurfaceView(rgb); break; case PREF_COLOR_BACKGROUND_VOLUME: colors.setColorBackgroundVolumeView(rgb); break; case PREF_COLOR_FOREGROUND_ALL: colors.setColorForegroundAllView(rgb); break; case PREF_COLOR_FOREGROUND_CHART: colors.setColorForegroundChartView(rgb); break; case PREF_COLOR_FOREGROUND_SURFACE: colors.setColorForegroundSurfaceView(rgb); break; case PREF_COLOR_FOREGROUND_VOLUME: colors.setColorForegroundVolumeView(rgb); break; case PREF_COLOR_CHART_MATRIX_GRID_LINES: colors.setColorChartMatrixGridLines(rgb); break; case NUMBER_OF_PREF_COLORS: CaretAssert(0); break; } prefs->setUserBackgroundAndForegroundColors(colors); prefs->setBackgroundAndForegroundColorsMode(BackgroundAndForegroundColorsModeEnum::USER_PREFERENCES); updateColorWidget(prefs); EventManager::get()->sendEvent(EventGraphicsUpdateAllWindows().getPointer()); } } /** * Called when a color button is clicked. * * @param enumIndex * color enum integer value indicating button that was clicked. */ void PreferencesDialog::colorPushButtonClicked(int enumIndex) { const PREF_COLOR prefColor = (PREF_COLOR)enumIndex; updateColorWithDialog(prefColor); } /** * Called when the logging level is changed. * @param int indx * New index of logging level combo box. */ void PreferencesDialog::miscLoggingLevelComboBoxChanged(int indx) { CaretPreferences* prefs = SessionManager::get()->getCaretPreferences(); const int32_t logLevelIntegerCode = m_miscLoggingLevelComboBox->itemData(indx).toInt(); prefs->setLoggingLevel(LogLevelEnum::fromIntegerCode(logLevelIntegerCode, NULL)); } /** * Called when the apply button is pressed. */ void PreferencesDialog::applyButtonClicked() { EventManager::get()->sendEvent(EventGraphicsUpdateAllWindows().getPointer()); } /** * Called when the OpenGL drawing method is changed. */ void PreferencesDialog::openGLDrawingMethodEnumComboBoxItemActivated() { const OpenGLDrawingMethodEnum::Enum drawingMethod = m_openGLDrawingMethodEnumComboBox->getSelectedItem(); CaretPreferences* prefs = SessionManager::get()->getCaretPreferences(); prefs->setOpenGLDrawingMethod(drawingMethod); EventManager::get()->sendEvent(EventGraphicsUpdateAllWindows().getPointer()); } /** * Called when the image capture method is changed. */ void PreferencesDialog::openGLImageCaptureMethodEnumComboBoxItemActivated() { const ImageCaptureMethodEnum::Enum imageCaptureMethod = m_openGLImageCaptureMethodEnumComboBox->getSelectedItem(); CaretPreferences* prefs = SessionManager::get()->getCaretPreferences(); prefs->setImageCaptureMethod(imageCaptureMethod); } /** * Called when volume crosshairs is toggled. * @param value * New value. */ void PreferencesDialog::volumeAxesCrosshairsComboBoxToggled(bool value) { CaretPreferences* prefs = SessionManager::get()->getCaretPreferences(); prefs->setVolumeAxesCrosshairsDisplayed(value); EventManager::get()->sendEvent(EventGraphicsUpdateAllWindows().getPointer()); } /** * Called when volume labels is toggled. * @param value * New value. */ void PreferencesDialog::volumeAxesLabelsComboBoxToggled(bool value) { CaretPreferences* prefs = SessionManager::get()->getCaretPreferences(); prefs->setVolumeAxesLabelsDisplayed(value); EventManager::get()->sendEvent(EventGraphicsUpdateAllWindows().getPointer()); } /** * Called when volume labels is toggled. * @param value * New value. */ void PreferencesDialog::volumeAxesMontageCoordinatesComboBoxToggled(bool value) { CaretPreferences* prefs = SessionManager::get()->getCaretPreferences(); prefs->setVolumeMontageAxesCoordinatesDisplayed(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. */ void PreferencesDialog::volumeMontageCoordinatePrecisionChanged(int value) { CaretPreferences* prefs = SessionManager::get()->getCaretPreferences(); prefs->setVolumeMontageCoordinatePrecision(value); EventManager::get()->sendEvent(EventGraphicsUpdateAllWindows().getPointer()); } /** * Called when volume identification value is changed. */ void PreferencesDialog::volumeIdentificationComboBoxToggled(bool value) { CaretPreferences* prefs = SessionManager::get()->getCaretPreferences(); prefs->setVolumeIdentificationDefaultedOn(value); } /** * Called when yoking default value is changed. */ void PreferencesDialog::yokingComboBoxToggled(bool value) { CaretPreferences* prefs = SessionManager::get()->getCaretPreferences(); prefs->setYokingDefaultedOn(value); } /** * Called when show splash screen option changed. * @param value * New value. */ void PreferencesDialog::miscSplashScreenShowAtStartupComboBoxChanged(bool value) { CaretPreferences* prefs = SessionManager::get()->getCaretPreferences(); prefs->setSplashScreenEnabled(value); } /** * Called when dynamic connectivity option changed. * @param value * New value. */ void PreferencesDialog::miscDynamicConnectivityComboBoxChanged(bool value) { CaretPreferences* prefs = SessionManager::get()->getCaretPreferences(); prefs->setDynamicConnectivityDefaultedOn(value); } /** * Called when show develop menu option changed. * @param value * New value. */ void PreferencesDialog::miscDevelopMenuEnabledComboBoxChanged(bool value) { CaretPreferences* prefs = SessionManager::get()->getCaretPreferences(); prefs->setDevelopMenuEnabled(value); EventManager::get()->sendSimpleEvent(EventTypeEnum::EVENT_BROWSER_WINDOW_MENUS_UPDATE); } /** * Gets called when view files type is changed. */ void PreferencesDialog::miscSpecFileDialogViewFilesTypeEnumComboBoxItemActivated() { const SpecFileDialogViewFilesTypeEnum::Enum viewFilesType = m_miscSpecFileDialogViewFilesTypeEnumComboBox->getSelectedItem(); CaretPreferences* prefs = SessionManager::get()->getCaretPreferences(); prefs->setManageFilesViewFileType(viewFilesType); } connectome-workbench-1.2.3+git41-gc4c6c90/src/GuiQt/PreferencesDialog.h000066400000000000000000000137231300200146000253350ustar00rootroot00000000000000#ifndef __PREFERENCES_DIALOG__H_ #define __PREFERENCES_DIALOG__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 "WuQDialogNonModal.h" class QCheckBox; class QComboBox; class QDoubleSpinBox; class QGridLayout; class QLabel; class QSignalMapper; class QSpinBox; namespace caret { class CaretPreferences; class EnumComboBoxTemplate; class WuQTrueFalseComboBox; class WuQWidgetObjectGroup; class PreferencesDialog : public WuQDialogNonModal { Q_OBJECT public: PreferencesDialog(QWidget* parent); virtual ~PreferencesDialog(); void updateDialog(); protected: virtual void applyButtonClicked(); private slots: void colorPushButtonClicked(int); void miscDevelopMenuEnabledComboBoxChanged(bool value); void miscLoggingLevelComboBoxChanged(int); void miscSplashScreenShowAtStartupComboBoxChanged(bool value); void miscSpecFileDialogViewFilesTypeEnumComboBoxItemActivated(); void miscDynamicConnectivityComboBoxChanged(bool value); void openGLDrawingMethodEnumComboBoxItemActivated(); void openGLImageCaptureMethodEnumComboBoxItemActivated(); void volumeAxesCrosshairsComboBoxToggled(bool value); void volumeAxesLabelsComboBoxToggled(bool value); void volumeAxesMontageCoordinatesComboBoxToggled(bool value); // void volumeMontageGapValueChanged(int value); void volumeMontageCoordinatePrecisionChanged(int value); void volumeIdentificationComboBoxToggled(bool value); void yokingComboBoxToggled(bool value); void identificationSymbolToggled(); private: enum PREF_COLOR { PREF_COLOR_BACKGROUND_ALL = 0, PREF_COLOR_BACKGROUND_CHART = 1, PREF_COLOR_BACKGROUND_SURFACE = 2, PREF_COLOR_BACKGROUND_VOLUME = 3, PREF_COLOR_FOREGROUND_ALL = 4, PREF_COLOR_FOREGROUND_CHART = 5, PREF_COLOR_FOREGROUND_SURFACE = 6, PREF_COLOR_FOREGROUND_VOLUME = 7, PREF_COLOR_CHART_MATRIX_GRID_LINES = 8, NUMBER_OF_PREF_COLORS = 9 }; QWidget* createColorsWidget(); QWidget* createIdentificationSymbolWidget(); QWidget* createMiscellaneousWidget(); QWidget* createOpenGLWidget(); QWidget* createVolumeWidget(); void updateColorWidget(CaretPreferences* prefs); void updateIdentificationWidget(CaretPreferences* prefs); void updateMiscellaneousWidget(CaretPreferences* prefs); void updateOpenGLWidget(CaretPreferences* prefs); void updateVolumeWidget(CaretPreferences* prefs); void updateColorWithDialog(const PREF_COLOR prefColor); QLabel* addWidgetToLayout(QGridLayout* gridLayout, const QString& labelText, QWidget* widget); void addWidgetsToLayout(QGridLayout* gridLayout, QWidget* leftWidget, QWidget* rightWidget); void addColorButtonAndSwatch(QGridLayout* gridLayout, const PREF_COLOR prefColor, QSignalMapper* colorSignalMapper); PreferencesDialog(const PreferencesDialog&); PreferencesDialog& operator=(const PreferencesDialog&); QWidget* m_foregroundColorAllWidget; QWidget* m_foregroundColorChartWidget; QWidget* m_foregroundColorSurfaceWidget; QWidget* m_foregroundColorVolumeWidget; QWidget* m_backgroundColorAllWidget; QWidget* m_backgroundColorChartWidget; QWidget* m_backgroundColorSurfaceWidget; QWidget* m_backgroundColorVolumeWidget; QWidget* m_chartMatrixGridLinesColorWidget; WuQTrueFalseComboBox* m_miscDevelopMenuEnabledComboBox; QComboBox* m_miscLoggingLevelComboBox; WuQTrueFalseComboBox* m_miscSplashScreenShowAtStartupComboBox; EnumComboBoxTemplate* m_miscSpecFileDialogViewFilesTypeEnumComboBox; EnumComboBoxTemplate* m_openGLDrawingMethodEnumComboBox; EnumComboBoxTemplate* m_openGLImageCaptureMethodEnumComboBox; WuQTrueFalseComboBox* m_dynamicConnectivityComboBox; WuQTrueFalseComboBox* m_volumeAxesCrosshairsComboBox; WuQTrueFalseComboBox* m_volumeAxesLabelsComboBox; WuQTrueFalseComboBox* m_volumeAxesMontageCoordinatesComboBox; // QSpinBox* m_volumeMontageGapSpinBox; QSpinBox* m_volumeMontageCoordinatePrecisionSpinBox; WuQTrueFalseComboBox* m_volumeIdentificationComboBox; WuQTrueFalseComboBox* m_yokingDefaultComboBox; WuQTrueFalseComboBox* m_surfaceIdentificationSymbolComboBox; WuQTrueFalseComboBox* m_volumeIdentificationSymbolComboBox; WuQWidgetObjectGroup* m_allWidgets; }; #ifdef __PREFERENCES_DIALOG__H__DECLARE__ #endif // __PREFERENCES_DIALOG__H__DECLARE__ } // namespace #endif //__PREFERENCES_DIALOG__H_ connectome-workbench-1.2.3+git41-gc4c6c90/src/GuiQt/ProgressReportingDialog.cxx000066400000000000000000000117321300200146000271430ustar00rootroot00000000000000 /*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 __PROGRESS_REPORTING_DIALOG_DECLARE__ #include "ProgressReportingDialog.h" #undef __PROGRESS_REPORTING_DIALOG_DECLARE__ #include #include #include "CaretAssert.h" #include "EventManager.h" #include "EventProgressUpdate.h" #include "ProgressReportingFromEvent.h" #include "ProgressReportingWithSlots.h" using namespace caret; /** * \class caret::ProgressReportingDialog * \brief Progress Report Dialog * \ingroup GuiQt */ /** * \class caret::ProgressReportingDialog * \brief Dialog that displays "progress" as an event is running. * * This dialog will the display the "progress" on a task as the * task executes. As the task is running, it should send * EventProgressUpdate events indicating the progress of the processing. * If the user chooses to cancel the event, this dialog will indicate so * on the first EventProgressUpdate received after the Cancel button is * clicked. The task should query the ProgressReportingDialog::isCancelled() * after each progress update is sent. * * Tasks may send the EventProgressUpdate event from other threads. * When the event is received signals and slots are used to update * the contents of the dialog instead of directly updating the dialog's * content. Qt's signals and slots mechanism supports signals connecting * to slots that are in a different thread. * * Use the static method "runEvent" to run an event with a progress dialog. * * For other cases, use the public constructor. Create an instance of the * progress dialog and then start the desired task. The progress dialog * will automatically close when the progress value equals the maximum * progress value or when the progress dialog goes out of scope. */ /** * Constructor. * * User will need to send EventProgressUpdate events to update * this progress dialog. * * @param title * Title for dialog. * @param initialMessage * Message that is first displayed. * @param parent * Parent on which this progress dialog is displayed. * @param f * Window flags. */ ProgressReportingDialog::ProgressReportingDialog(const AString& title, const AString& initialMessage, QWidget* parent, Qt::WindowFlags f) : QProgressDialog(initialMessage, "Cancel", 0, 100, parent, f) { ProgressReportingFromEvent* progressFromEvent = new ProgressReportingFromEvent(this); m_progressReporter = progressFromEvent; if (title.isEmpty() == false) { setWindowTitle(title); } const int minimumTimeInMillisecondsBeforeDialogDisplayed = 0; setMinimumDuration(minimumTimeInMillisecondsBeforeDialogDisplayed); QObject::connect(progressFromEvent, SIGNAL(reportProgressRange(const int, const int)), this, SLOT(setRange(int, int))); QObject::connect(progressFromEvent, SIGNAL(reportProgressValue(const int)), this, SLOT(setValue(int))); QObject::connect(progressFromEvent, SIGNAL(reportProgressMessage(const QString&)), this, SLOT(setLabelText(const QString&))); QObject::connect(this, SIGNAL(canceled()), progressFromEvent, SLOT(requestCancel())); } /** * Destructor. */ ProgressReportingDialog::~ProgressReportingDialog() { } /** * Run the event in a progress dialog. Dialog will close after the * event completes. Progress is updated each time a * EventProgressUpdate is received. * * @param event * Event that is executed. * @param parent * Widget on which the dialog is displayed. * @param title * If not empty, title is in window's title bar */ void ProgressReportingDialog::runEvent(Event* event, QWidget* parent, const AString& title) { ProgressReportingDialog prd(title, "", parent); prd.setValue(0); EventManager::get()->sendEvent(event); prd.setValue(prd.maximum()); } connectome-workbench-1.2.3+git41-gc4c6c90/src/GuiQt/ProgressReportingDialog.h000066400000000000000000000041201300200146000265610ustar00rootroot00000000000000#ifndef __PROGRESS_REPORTING_DIALOG_H__ #define __PROGRESS_REPORTING_DIALOG_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 "AString.h" #include "EventListenerInterface.h" namespace caret { class ProgressReportingInterface; class ProgressReportingDialog : public QProgressDialog { Q_OBJECT public: ProgressReportingDialog(const AString& title, const AString& initialMessage, QWidget* parent, Qt::WindowFlags f = 0); static void runEvent(Event* event, QWidget* parent, const AString& title); public: virtual ~ProgressReportingDialog(); private: ProgressReportingDialog(const ProgressReportingDialog&); ProgressReportingDialog& operator=(const ProgressReportingDialog&); public: // ADD_NEW_METHODS_HERE private: // ADD_NEW_MEMBERS_HERE ProgressReportingInterface* m_progressReporter; }; #ifdef __PROGRESS_REPORTING_DIALOG_DECLARE__ // #endif // __PROGRESS_REPORTING_DIALOG_DECLARE__ } // namespace #endif //__PROGRESS_REPORTING_DIALOG_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/GuiQt/ProgressReportingFromEvent.cxx000066400000000000000000000101151300200146000276430ustar00rootroot00000000000000 /*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 __PROGRESS_REPORTING_FROM_EVENT_DECLARE__ #include "ProgressReportingFromEvent.h" #undef __PROGRESS_REPORTING_FROM_EVENT_DECLARE__ #include #include "CaretAssert.h" #include "EventManager.h" #include "EventProgressUpdate.h" using namespace caret; //============================================================================= /** * \class caret::ProgressReportingFromEvent * \brief Interfaces between the ProgressReportingDialog and the Workbench event system * * Listens for EventProgressUpdate events and then updates the progress * dialog using signals and slots. Using signals and slots should allow * the progress events to be sent from a thread that is not the GUI thread. */ /** * Constructor. * * @parent * Parent of this instance. */ ProgressReportingFromEvent::ProgressReportingFromEvent(QObject* parent) : ProgressReportingWithSlots(parent) { EventManager::get()->addEventListener(this, EventTypeEnum::EVENT_PROGRESS_UPDATE); m_eventReceivingEnabled = true; } /** * Destructor. */ ProgressReportingFromEvent::~ProgressReportingFromEvent() { EventManager::get()->removeAllEventsFromListener(this); } /** * 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 ProgressReportingFromEvent::setEventReceivingEnabled(bool enabled) { m_eventReceivingEnabled = enabled; } /** * @return Is event receiving enabled? The default is true. */ bool ProgressReportingFromEvent::isEventReceivingEnabled() const { return m_eventReceivingEnabled; } /** * Receive an event. * * @param event * An event for which this instance is listening. */ void ProgressReportingFromEvent::receiveEvent(Event* event) { if ( ! m_eventReceivingEnabled) { return; } if (event->getEventType() == EventTypeEnum::EVENT_PROGRESS_UPDATE) { EventProgressUpdate* progressEvent = dynamic_cast(event); CaretAssert(progressEvent); const int minProg = progressEvent->getMinimumProgressValue(); const int maxProg = progressEvent->getMaximumProgressValue(); const int progValue = progressEvent->getProgressValue(); const AString progMessage = progressEvent->getProgressMessage(); /* * Note: It appears that the content of the progress dialog * only changes when the progress value is changed. So, set * the message prior to the progress value so that if the message * changes, the message in the progress dialog will be updated * when the progress value changes. */ if (progMessage.isEmpty() == false) { setProgressMessage(progMessage); } if ((minProg >= 0) && (maxProg >= minProg)) { setProgressRange(minProg, maxProg); } if (progValue >= 0) { setProgressValue(progValue); } if (isCancelRequested()) { progressEvent->setCancelled(); } QApplication::processEvents(); progressEvent->setEventProcessed(); } } connectome-workbench-1.2.3+git41-gc4c6c90/src/GuiQt/ProgressReportingFromEvent.h000066400000000000000000000036411300200146000272760ustar00rootroot00000000000000#ifndef __PROGRESS_REPORTING_FROM_EVENT_H__ #define __PROGRESS_REPORTING_FROM_EVENT_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 "ProgressReportingWithSlots.h" #include "EventListenerInterface.h" namespace caret { class ProgressReportingFromEvent : public ProgressReportingWithSlots, public EventListenerInterface { Q_OBJECT public: ProgressReportingFromEvent(QObject* parent); virtual ~ProgressReportingFromEvent(); void setEventReceivingEnabled(bool enabled); bool isEventReceivingEnabled() const; // ADD_NEW_METHODS_HERE virtual void receiveEvent(Event* event); private: ProgressReportingFromEvent(const ProgressReportingFromEvent&); ProgressReportingFromEvent& operator=(const ProgressReportingFromEvent&); bool m_eventReceivingEnabled; // ADD_NEW_MEMBERS_HERE }; #ifdef __PROGRESS_REPORTING_FROM_EVENT_DECLARE__ // #endif // __PROGRESS_REPORTING_FROM_EVENT_DECLARE__ } // namespace #endif //__PROGRESS_REPORTING_FROM_EVENT_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/GuiQt/ProgressReportingWithSlots.cxx000066400000000000000000000104031300200146000276760ustar00rootroot00000000000000 /*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 __PROGRESS_REPORTING_WITH_SLOTS_DECLARE__ #include "ProgressReportingWithSlots.h" #undef __PROGRESS_REPORTING_WITH_SLOTS_DECLARE__ using namespace caret; /** * \class caret::ProgressReportingWithSlots * \brief Interfaces between the ProgressReportingDialog and the Workbench event system * \ingroup GuiQt * * Listens for EventProgressUpdate events and then updates the progress * dialog using signals and slots. Using signals and slots should allow * the progress events to be sent from a thread that is not the GUI thread. */ /** * Constructor. */ ProgressReportingWithSlots::ProgressReportingWithSlots(QObject* parent) : QObject(parent), ProgressReportingInterface() { m_synchronizeMutex.unlock(); m_cancelled = false; } /** * Constructor. */ ProgressReportingWithSlots::~ProgressReportingWithSlots() { } /** * Used by task to set the range of progress reporting. * * @param minimumProgress * The minimum amount reported (typically zero). * @param maximumProgress * The maximum amount of progress. */ void ProgressReportingWithSlots::setProgressRange(const int minimumProgress, const int maximumProgress) { /* * Don't let any other method in an instance of this * class execute until this method completes. */ QMutexLocker locker(&m_synchronizeMutex); if ((minimumProgress >= 0) && (maximumProgress >= minimumProgress)) { emit reportProgressRange(minimumProgress, maximumProgress); } } /** * Used by task to set the current progress. * * @param progressValue * The current progress within range of the minimum and maximum. */ void ProgressReportingWithSlots::setProgressValue(const int progressValue) { /* * Don't let any other method in an instance of this * class execute until this method completes. */ QMutexLocker locker(&m_synchronizeMutex); if (progressValue >= 0) { emit reportProgressValue(progressValue); } } /** * Used by task to set the message describing the task's activity. * * @param message * Message that is displayed. */ void ProgressReportingWithSlots::setProgressMessage(const AString& progressMessage) { /* * Don't let any other method in an instance of this * class execute until this method completes. */ QMutexLocker locker(&m_synchronizeMutex); if (progressMessage.isEmpty() == false) { emit reportProgressMessage(progressMessage); } } /** * (SLOT) Used by the user-interface to request that the task end as * soon as possible. */ void ProgressReportingWithSlots::requestCancel() { setCancelRequested(); } /** * @return "true" if a request been made to cancel the task, else false. * * Used by the task to see if the task should end as soon as * possible. If so, the task should clean up after itself * (release resources); */ bool ProgressReportingWithSlots::isCancelRequested() const { /* * Don't let any other method in an instance of this * class execute until this method completes. */ QMutexLocker locker(&m_synchronizeMutex); return m_cancelled; } /** * Set the cancel request. */ void ProgressReportingWithSlots::setCancelRequested() { /* * Don't let any other method in an instance of this * class execute until this method completes. */ QMutexLocker locker(&m_synchronizeMutex); m_cancelled = true; } connectome-workbench-1.2.3+git41-gc4c6c90/src/GuiQt/ProgressReportingWithSlots.h000066400000000000000000000061431300200146000273310ustar00rootroot00000000000000#ifndef __PROGRESS_REPORTING_WITH_SLOTS_H__ #define __PROGRESS_REPORTING_WITH_SLOTS_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 "ProgressReportingInterface.h" namespace caret { class ProgressReportingWithSlots : public QObject, public ProgressReportingInterface { Q_OBJECT public: ProgressReportingWithSlots(QObject* parent); virtual ~ProgressReportingWithSlots(); virtual void setProgressRange(const int minimumProgress, const int maximumProgress); virtual void setProgressValue(const int progressValue); virtual void setProgressMessage(const AString& message); virtual bool isCancelRequested() const; virtual void setCancelRequested(); public slots: void requestCancel(); signals: /* * Emitted when the range of progress is updated. * @param minimumProgress * New value for minimum. * @param maximumProgress * New value for maximum. */ void reportProgressRange(const int minimumProgress, const int maximumProgress); /** * Emitted when the progress value is updated. * @param progressValue * New value for progress. */ void reportProgressValue(const int progressValue); /** * Emitted when the progress message is updated. * @param progressMessage * New value for progress message. * * NOTE: This must use a QString (not AString) since * it connects to a Qt slot expecting a QString */ void reportProgressMessage(const QString& progressMessage); private: /** * Ensures each method is synchronized meaning that each of * the methods for updating complete blocks if any other * methods are in progress. */ mutable QMutex m_synchronizeMutex; bool m_cancelled; }; #ifdef __PROGRESS_REPORTING_WITH_SLOTS_DECLARE__ // #endif // __PROGRESS_REPORTING_WITH_SLOTS_DECLARE__ } // namespace #endif //__PROGRESS_REPORTING_WITH_SLOTS_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/GuiQt/QGLWidgetTextRenderer.cxx000066400000000000000000001053631300200146000264540ustar00rootroot00000000000000 /*LICENSE_START*/ /* * Copyright (C) 2015 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 __QT_OPEN_G_L_TEXT_RENDERER_DECLARE__ #include "QGLWidgetTextRenderer.h" #undef __QT_OPEN_G_L_TEXT_RENDERER_DECLARE__ #include #include #include #include #include #include #include "AnnotationPointSizeText.h" #include "CaretAssert.h" #include "CaretLogger.h" #include "CaretOpenGLInclude.h" using namespace caret; /** * \class caret::QGLWidgetTextRenderer * \brief Draw text in OpenGL using Qt's GLWidget. * \ingroup GuiQt */ /** * Constructor. * * @param glWidget * Qt's GL Widget used for drawing text. */ QGLWidgetTextRenderer::QGLWidgetTextRenderer(QGLWidget* glWidget) : BrainOpenGLTextRenderInterface(), m_glWidget(glWidget) { m_defaultFont = NULL; AnnotationPointSizeText defaultAnnotationText(AnnotationAttributesDefaultTypeEnum::NORMAL); defaultAnnotationText.setFontPointSize(AnnotationTextFontPointSizeEnum::SIZE14); defaultAnnotationText.setFont(AnnotationTextFontNameEnum::VERA); defaultAnnotationText.setItalicStyleEnabled(false); defaultAnnotationText.setBoldStyleEnabled(false); defaultAnnotationText.setUnderlineStyleEnabled(false); m_defaultFont = findFont(defaultAnnotationText, true); } /** * Destructor. */ QGLWidgetTextRenderer::~QGLWidgetTextRenderer() { for (FONT_MAP_ITERATOR iter = m_fontNameToFontMap.begin(); iter != m_fontNameToFontMap.end(); iter++) { delete iter->second; } m_fontNameToFontMap.clear(); /* * Do not delete "m_defaultFont" since it points to a font * in m_fontNameToFontMap. Doing so would cause * a double delete. */ } /** * Draw annnotation text at the given viewport coordinates using * the the annotations attributes for the style of text. * * Depth testing is DISABLED when drawing text with this method. * * @param viewportX * Viewport X-coordinate. * @param viewportY * Viewport Y-coordinate. * @param viewportZ * Viewport Z-coordinate. * @param annotationText * Annotation text and attributes. */ void QGLWidgetTextRenderer::drawTextAtViewportCoords(const double viewportX, const double viewportY, const AnnotationText& annotationText) { setViewportHeight(); drawTextAtViewportCoords(viewportX, viewportY, 0.0, annotationText); } /** * Draw annnotation text at the given viewport coordinates using * the the annotations attributes for the style of text. * * Depth testing is ENABLED when drawing text with this method. * * @param viewportX * Viewport X-coordinate. * @param viewportY * Viewport Y-coordinate. * @param viewportZ * Viewport Z-coordinate. * @param annotationText * Annotation text and attributes. */ void QGLWidgetTextRenderer::drawTextAtViewportCoords(const double viewportX, const double viewportY, const double /*viewportZ */, const AnnotationText& annotationText) { setViewportHeight(); GLdouble modelMatrix[16]; GLdouble projectionMatrix[16]; GLint viewport[4]; glGetDoublev(GL_MODELVIEW_MATRIX, modelMatrix); glGetDoublev(GL_PROJECTION_MATRIX, projectionMatrix); glGetIntegerv(GL_VIEWPORT, viewport); /* * Set the orthographic projection so that its origin is in the bottom * left corner. It needs to be there since we are drawing in window * coordinates. We do not know the true size of the window but that * is okay since we can set the orthographic view so that the bottom * left corner is the origin and the top right corner is the top * right corner of the user's viewport. */ glMatrixMode(GL_PROJECTION); glPushMatrix(); glLoadIdentity(); glOrtho(0, (viewport[2]), 0, (viewport[3]), -1, 1); /* * Viewing projection is just the identity matrix since * we are drawing in window coordinates. */ glMatrixMode(GL_MODELVIEW); glPushMatrix(); glLoadIdentity(); //std::cout << "Drawing \"" << qPrintable(text) << "\" at " << windowX << ", " << windowY << std::endl; if (annotationText.getText().isEmpty()) { return; } switch (annotationText.getOrientation()) { case AnnotationTextOrientationEnum::HORIZONTAL: drawHorizontalTextAtWindowCoords(viewportX, viewportY, annotationText); break; case AnnotationTextOrientationEnum::STACKED: drawVerticalTextAtWindowCoords(viewportX, viewportY, annotationText); break; } glPopMatrix(); glMatrixMode(GL_PROJECTION); glPopMatrix(); glMatrixMode(GL_MODELVIEW); } /** * Draw annnotation text at the given model coordinates using * the the annotations attributes for the style of text. * * Depth testing is ENABLED when drawing text with this method. * * @param modelX * Model X-coordinate. * @param modelY * Model Y-coordinate. * @param modelZ * Model Z-coordinate. * @param annotationText * Annotation text and attributes. */ void QGLWidgetTextRenderer::drawTextAtModelCoords(const double modelX, const double modelY, const double modelZ, const AnnotationText& annotationText) { setViewportHeight(); GLdouble modelMatrix[16]; GLdouble projectionMatrix[16]; GLint viewport[4]; glGetDoublev(GL_MODELVIEW_MATRIX, modelMatrix); glGetDoublev(GL_PROJECTION_MATRIX, projectionMatrix); glGetIntegerv(GL_VIEWPORT, viewport); /* * Project model coordinate to a window coordinate. */ GLdouble windowX, windowY, windowZ; if (gluProject(modelX, modelY, modelZ, modelMatrix, projectionMatrix, viewport, &windowX, &windowY, &windowZ) == GL_TRUE) { glViewport(viewport[0], viewport[1], viewport[2], viewport[3]); // /* // * Set the orthographic projection so that its origin is in the bottom // * left corner. It needs to be there since we are drawing in window // * coordinates. We do not know the true size of the window but that // * is okay since we can set the orthographic view so that the bottom // * left corner is the origin and the top right corner is the top // * right corner of the user's viewport. // */ // glMatrixMode(GL_PROJECTION); // glPushMatrix(); // glLoadIdentity(); // glOrtho(0, // (viewport[2]), // 0, // (viewport[3]), // -1, // 1); // /* // * Viewing projection is just the identity matrix since // * we are drawing in window coordinates. // */ // glMatrixMode(GL_MODELVIEW); // glPushMatrix(); // glLoadIdentity(); // std::cout << "VP:" << AString::fromNumbers(viewport, 4, ",") // << " Window: " << windowX << ", " << windowY << std::endl; /* * Convert window coordinate to viewport coordinatde */ const double x = windowX - viewport[0]; const double y = windowY - viewport[1]; drawTextAtViewportCoords(x, y, 0.0, annotationText); // glPopMatrix(); // glMatrixMode(GL_PROJECTION); // glPopMatrix(); // glMatrixMode(GL_MODELVIEW); } else { CaretLogSevere("gluProject() failed for drawing text at model coordinates."); } } /** * Draw horizontal annotation text at the given window coordinates. * * @param viewport * The current viewport. * @param windowX * X-coordinate in the window of first text character * using the 'alignment' * @param windowY * Y-coordinate in the window at which bottom of text is placed. * @param annotationText * Annotation Text that is to be drawn. */ void QGLWidgetTextRenderer::drawHorizontalTextAtWindowCoords(const double windowX, const double windowY, const AnnotationText& annotationText) { QFont* font = findFont(annotationText, false); if (! font) { return; } /* * Get the viewport */ GLint viewport[4]; glGetIntegerv(GL_VIEWPORT, viewport); const bool drawCrosshairsAtFontStartingCoordinate = false; if (drawCrosshairsAtFontStartingCoordinate) { GLfloat savedRGBA[4]; glGetFloatv(GL_CURRENT_COLOR, savedRGBA); glColor3f(1.0, 0.0, 0.0); glLineWidth(1.0); glPushMatrix(); glTranslatef(windowX, windowY, 0.0); glBegin(GL_LINES); glVertex2i(-50, 0); glVertex2i( 50, 0); glVertex2i(0, -50); glVertex2i(0, 50); glEnd(); glPopMatrix(); glColor3f(savedRGBA[0], savedRGBA[1], savedRGBA[2]); } 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); double left = bottomLeft[0]; double right = bottomRight[0]; double bottom = bottomLeft[1]; double top = topLeft[1]; // getTextBoundsInPixels(annotationText, // left, // right, // bottom, // top); double textOffsetX = 0; switch (annotationText.getHorizontalAlignment()) { case AnnotationTextAlignHorizontalEnum::CENTER: textOffsetX = -((right - left) / 2.0); break; case AnnotationTextAlignHorizontalEnum::LEFT: textOffsetX = -left; break; case AnnotationTextAlignHorizontalEnum::RIGHT: textOffsetX = -right; break; } double textOffsetY = 0; // switch (annotationText.getVerticalAlignment()) { // case AnnotationTextAlignVerticalEnum::BOTTOM: // textOffsetY = 0.0; //-bottom; // break; // case AnnotationTextAlignVerticalEnum::CENTER: // textOffsetY = ((top - bottom) / 2.0); // break; // case AnnotationTextAlignVerticalEnum::TOP: // textOffsetY = top; // break; // } switch (annotationText.getVerticalAlignment()) { case AnnotationTextAlignVerticalEnum::BOTTOM: textOffsetY = -bottom; break; case AnnotationTextAlignVerticalEnum::MIDDLE: textOffsetY = -((top - bottom) / 2.0); break; case AnnotationTextAlignVerticalEnum::TOP: textOffsetY = -top; break; } // /* // * Qt seems to place coordinate at TOP of text // */ // const double height = top - bottom; // if (height > 0.0) { // const double oneCharHeight = (height / text.length()); // textOffsetY -= oneCharHeight; // } double textX = windowX + textOffsetX; double textY = windowY + textOffsetY; if (drawCrosshairsAtFontStartingCoordinate) { std::cout << "BBox: (" << left << " " << bottom << ") (" << right << ", " << top << ")" << std::endl; // const float width = right - left; // const float height = top - bottom; // // GLfloat savedRGBA[4]; // glGetFloatv(GL_CURRENT_COLOR, savedRGBA); // glColor3f(1.0, 0.0, 1.0); // glLineWidth(1.0); // glPushMatrix(); // glRectf(textX, textY, textX + width, textY + height); // glPopMatrix(); // glColor3f(savedRGBA[0], savedRGBA[1], savedRGBA[2]); } const double backgroundBounds[4] = { textX, textY, textX + (right - left), textY + (top - bottom) }; glPushMatrix(); //glLoadIdentity(); applyBackgroundColoring(annotationText, backgroundBounds); applyForegroundColoring(annotationText); glPopMatrix(); /* * Qt places origin at TOP LEFT */ // const int qtWindowX = textX + viewport[0]; // const int qtWindowY = m_glWidget->height() - textY + viewport[1]; const int qtWindowX = textX + viewport[0]; const int qtWindowY = m_glWidget->height() - textY - viewport[1]; if (drawCrosshairsAtFontStartingCoordinate) { std::cout << "Drawing " << qPrintable(text) << " at XY: " << textX << ", " << textY << " qtXY=" << qtWindowX << ", " << qtWindowY << " windowXY=" << windowX << ", " << windowY << std::endl; GLdouble modelMatrix[16]; GLdouble projectionMatrix[16]; GLint viewport[4]; glGetDoublev(GL_MODELVIEW_MATRIX, modelMatrix); glGetDoublev(GL_PROJECTION_MATRIX, projectionMatrix); glGetIntegerv(GL_VIEWPORT, viewport); std::cout << std::endl; std::cout << "Drawing Text: " << qPrintable(annotationText.getText()) << std::endl; std::cout << " Model Matrix: " << AString::fromNumbers(modelMatrix, 16, ",") << std::endl; std::cout << " Proj Matrix: " << AString::fromNumbers(projectionMatrix, 16, ",") << std::endl; std::cout << " Viewport: " << AString::fromNumbers(viewport, 4, ",") << std::endl; std::cout << std::endl; } m_glWidget->renderText(qtWindowX, qtWindowY, text, *font); } /** * Draw vertical text at the given window coordinates. * * @param windowX * X-coordinate in the window of first text character * using the 'alignment' * @param windowY * Y-coordinate in the window at which bottom of text is placed. * @param annotationText * Text that is to be drawn. */ void QGLWidgetTextRenderer::drawVerticalTextAtWindowCoords(const double windowX, const double windowY, const AnnotationText& annotationText) { QFont* font = findFont(annotationText, false); if ( ! font) { return; } /* * Get the viewport */ GLint viewport[4]; glGetIntegerv(GL_VIEWPORT, viewport); bool drawCrosshairsAtFontStartingCoordinate = false; if (drawCrosshairsAtFontStartingCoordinate) { GLfloat savedRGBA[4]; glGetFloatv(GL_CURRENT_COLOR, savedRGBA); glColor3f(1.0, 0.0, 1.0); glLineWidth(1.0); glPushMatrix(); //glTranslatef(windowX, windowY, 0.0); const double yStart = windowY - 50.0; const double yEnd = windowY + 50.0; glBegin(GL_LINES); glVertex2d(windowX, yStart); glVertex2d(windowX, yEnd); glVertex2d(-50.0, windowY); glVertex2d( 50.0, windowY); glEnd(); glPopMatrix(); glColor3f(savedRGBA[0], savedRGBA[1], savedRGBA[2]); } double textMinX = 0.0; double textMaxX = 0.0; double textHeight = 0.0; std::vector textCharsToDraw; getVerticalTextCharInfo(annotationText, textMinX, textMaxX, textHeight, textCharsToDraw); const double textBoundsWidth = textMaxX - textMinX; const double textBoundsHeight = textHeight; double textOffsetX = 0; switch (annotationText.getHorizontalAlignment()) { case AnnotationTextAlignHorizontalEnum::CENTER: textOffsetX = -((textMaxX - textMinX) / 2.0); textOffsetX = 0.0; break; case AnnotationTextAlignHorizontalEnum::LEFT: textOffsetX = -textMinX; textOffsetX = (textBoundsWidth / 2.0); break; case AnnotationTextAlignHorizontalEnum::RIGHT: textOffsetX = -textMaxX; textOffsetX = -(textBoundsWidth / 2.0); break; } /* * The character coordinates are set so that the top of the first * will be at Y=0. */ double textOffsetY = 0.0; double textBackgroundTopOffsetY = 0.0; switch (annotationText.getVerticalAlignment()) { case AnnotationTextAlignVerticalEnum::BOTTOM: textOffsetY = textHeight; textBackgroundTopOffsetY = textHeight; break; case AnnotationTextAlignVerticalEnum::MIDDLE: textOffsetY = (textHeight / 2.0); textBackgroundTopOffsetY = (textHeight / 2.0); break; case AnnotationTextAlignVerticalEnum::TOP: textOffsetY = 0.0; textBackgroundTopOffsetY = 0.0; break; } const double backMinX = windowX - (textBoundsWidth / 2.0) + textOffsetX; const double backMaxX = backMinX + textBoundsWidth; const double backMaxY = windowY + textBackgroundTopOffsetY; const double backMinY = backMaxY - textBoundsHeight; const double backgroundBounds[4] = { backMinX, backMinY, backMaxX, backMaxY }; applyBackgroundColoring(annotationText, backgroundBounds); applyForegroundColoring(annotationText); for (std::vector::const_iterator textIter = textCharsToDraw.begin(); textIter != textCharsToDraw.end(); textIter++) { const double charX = windowX + textIter->m_x + textOffsetX; const double charY = windowY + textIter->m_y + textOffsetY; const int qtWindowX = charX + viewport[0]; const int qtWindowY = m_glWidget->height() - charY - viewport[1]; m_glWidget->renderText(qtWindowX, qtWindowY, textIter->m_char, *font); } } /** * Apply the background coloring by drawing a rectangle in the background * color that encloses the text. If the background color is * invalid (alpha => 0), no action is taken. * * @param annotationText * Annotation Text that is to be drawn. * @param textBoundsBox * Bounding box for text (min-x, min-y, max-x, max-y) */ void QGLWidgetTextRenderer::applyBackgroundColoring(const AnnotationText& annotationText, const double textBoundsBox[4]) { // GLdouble modelMatrix[16]; // GLdouble projectionMatrix[16]; // GLint viewport[4]; // // glGetDoublev(GL_MODELVIEW_MATRIX, // modelMatrix); // glGetDoublev(GL_PROJECTION_MATRIX, // projectionMatrix); // glGetIntegerv(GL_VIEWPORT, // viewport); // // std::cout << std::endl; // std::cout << "Background for: " << qPrintable(annotationText.getText()) << std::endl; // std::cout << " Rectangle: " << AString::fromNumbers(textBoundsBox, 4, ",") << std::endl; // std::cout << " Model Matrix: " << AString::fromNumbers(modelMatrix, 16, ",") << std::endl; // std::cout << " Proj Matrix: " << AString::fromNumbers(projectionMatrix, 16, ",") << std::endl; // std::cout << " Viewport: " << AString::fromNumbers(viewport, 4, ",") << std::endl; // std::cout << std::endl; float backgroundColor[4]; annotationText.getBackgroundColorRGBA(backgroundColor); if (backgroundColor[3] > 0.0) { glColor4fv(backgroundColor); const double margin = s_textMarginSize * 2; glRectd(textBoundsBox[0] - margin, textBoundsBox[1] - margin, textBoundsBox[2] + margin, textBoundsBox[3] + margin); } } /** * Apply the foreground color. * * @param annotationText * Annotation Text that is to be drawn. * @param textBoundsBox * Bounding box for text (min-x, min-y, max-x, max-y) */ void QGLWidgetTextRenderer::applyForegroundColoring(const AnnotationText& annotationText) { float rgba[4]; annotationText.getLineColorRGBA(rgba); glColor4fv(rgba); } /** * Get the estimated width and height of text (in pixels) using the given text * attributes. * * See http://ftgl.sourceforge.net/docs/html/metrics.png * * @param annotationText * Text for width and height estimation. * @param viewportHeight * Height of the viewport needed for percentage height text. * @param widthOut * Estimated width of text. * @param heightOut * Estimated height of text. */ void QGLWidgetTextRenderer::getTextWidthHeightInPixels(const AnnotationText& annotationText, const double viewportHeight, double& widthOut, double& heightOut) { setViewportHeight(); double xMin = 0.0; double xMax = 0.0; double yMin = 0.0; double yMax = 0.0; double bottomLeft[3], bottomRight[3], topRight[3], topLeft[3]; getBoundsForTextAtViewportCoords(annotationText, 0.0, 0.0, 0.0, viewportHeight, bottomLeft, bottomRight, topRight, topLeft); widthOut = xMax - xMin; heightOut = yMax - yMin; } /** * Get the bounds of text (in pixels) using the given text * attributes. * * See http://ftgl.sourceforge.net/docs/html/metrics.png * * @param annotationText * Text that is to be drawn. * @param viewportX * Viewport X-coordinate. * @param viewportY * Viewport Y-coordinate. * @param viewportZ * Viewport Z-coordinate. * @param viewportHeight * Height of the viewport needed for percentage height text. * @param bottomLeftOut * The bottom left corner of the text bounds. * @param bottomRightOut * The bottom right corner of the text bounds. * @param topRightOut * The top right corner of the text bounds. * @param topLeftOut * The top left corner of the text bounds. */ void QGLWidgetTextRenderer::getBoundsForTextAtViewportCoords(const AnnotationText& annotationText, const double viewportX, const double viewportY, const double viewportZ, const double /*viewportHeight*/, double bottomLeftOut[3], double bottomRightOut[3], double topRightOut[3], double topLeftOut[3]) { setViewportHeight(); QFont* font = findFont(annotationText, false); if (font == NULL) { return; } double xMin = 0.0; double xMax = 0.0; double yMin = 0.0; double yMax = 0.0; switch (annotationText.getOrientation()) { case AnnotationTextOrientationEnum::HORIZONTAL: { QFontMetricsF fontMetrics(*font); QRectF boundsRect = fontMetrics.boundingRect(annotationText.getText()); /* * Note: sometimes boundsRect.top() is negative and * that screws things up. */ xMin = boundsRect.left(); xMax = boundsRect.right(); yMin = boundsRect.bottom(); yMax = boundsRect.bottom() + boundsRect.height(); } break; case AnnotationTextOrientationEnum::STACKED: { double textHeight = 0.0; std::vector charInfo; getVerticalTextCharInfo(annotationText, xMin, xMax, textHeight, charInfo); yMax = textHeight; } break; } const double width = xMax - xMin; double left = 0.0; switch (annotationText.getHorizontalAlignment()) { case AnnotationTextAlignHorizontalEnum::LEFT: left = viewportX; break; case AnnotationTextAlignHorizontalEnum::CENTER: left = viewportX - (width / 2.0); break; case AnnotationTextAlignHorizontalEnum::RIGHT: left = viewportX - width; break; } const double right = left + width; const double height = yMax - yMin; double bottom = 0.0; switch (annotationText.getVerticalAlignment()) { case AnnotationTextAlignVerticalEnum::BOTTOM: bottom = viewportY; break; case AnnotationTextAlignVerticalEnum::MIDDLE: bottom = viewportY - (height / 2.0); break; case AnnotationTextAlignVerticalEnum::TOP: bottom = viewportY - height; break; } const double top = bottom + height; bottomLeftOut[0] = left; bottomLeftOut[1] = bottom; bottomLeftOut[2] = viewportZ; bottomRightOut[0] = right; bottomRightOut[0] = bottom; bottomRightOut[0] = viewportZ; topRightOut[0] = right; topRightOut[0] = top; topRightOut[0] = viewportZ; topLeftOut[0] = left; topLeftOut[0] = top; topLeftOut[0] = viewportZ; } /** * Get the character info for drawing vertical text which includes * position for each of the characters. The TOP of the first * character will be at Y=0. * * @param annotationText * Text that is to be drawn. * @param xMinOut * Minimum X of text. * @param xMaxOut * Maximum X of text. * @param heightOut * Total height of text. * @param charInfoOut * Contains each character and its X, Y position * for rendering vertically. */ void QGLWidgetTextRenderer::getVerticalTextCharInfo(const AnnotationText& annotationText, double& xMinOut, double& xMaxOut, double& heightOut, std::vector& charInfoOut) { charInfoOut.clear(); xMinOut = 0.0; xMaxOut = 0.0; heightOut = 0.0; const AString text = annotationText.getText(); const int32_t numChars = static_cast(text.size()); if (numChars <= 0) { return; } QFont* font = findFont(annotationText, false); if ( ! font) { return; } xMinOut = std::numeric_limits::max(); xMaxOut = -std::numeric_limits::max(); double y = 0.0; const int32_t lastCharIndex = numChars - 1; for (int32_t i = 0; i < numChars; i++) { const QString oneChar = text[i]; QFontMetricsF fontMetrics(*font); QRectF boundsRect = fontMetrics.boundingRect(oneChar); /* * Note: sometimes boundsRect.top() is negative and * that screws things up. */ const double lowerX = boundsRect.left(); const double upperX = boundsRect.right(); const double lowerY = boundsRect.bottom(); const double upperY = boundsRect.bottom() + boundsRect.height(); const double width = upperX - lowerX; xMinOut = std::min(xMinOut, lowerX); xMaxOut = std::max(xMaxOut, upperX); /* * Center the character horizontally. */ const double xChar = -lowerX - (width / 2.0); /* * Want the top of character at the Y-coordinate. */ const double yChar = y - upperY; charInfoOut.push_back(CharInfo(oneChar, xChar, yChar)); const double height = upperY - lowerY; if (i == lastCharIndex) { y -= height; } else { const double heightWithSpacing = height + s_textMarginSize; y -= heightWithSpacing; } } heightOut = std::fabs(y); } /** * Set the height of the viewport. This method must be called * at the beginning of all public methods. */ void QGLWidgetTextRenderer::setViewportHeight() { GLint viewport[4]; glGetIntegerv(GL_VIEWPORT, viewport); m_viewportHeight = viewport[3]; } /** * Find a font with the given name, height, and style. * Once a font is created it is cached so that it can be * used again and avoids font creation which may be * expensive. * * @return a font */ QFont* QGLWidgetTextRenderer::findFont(const AnnotationText& annotationText, const bool creatingDefaultFontFlag) { const AString fontName = annotationText.getFontRenderingEncodedName(m_viewportHeight); /* * Has the font already has been created? */ FONT_MAP_ITERATOR fontIter = m_fontNameToFontMap.find(fontName); if (fontIter != m_fontNameToFontMap.end()) { FontData* fontData = fontIter->second; CaretAssert(fontData); return fontData->m_font; } /* * Create and save the font */ FontData* fontData = new FontData(annotationText, m_viewportHeight); if (fontData->m_valid) { /* * Request font is valid. */ m_fontNameToFontMap.insert(std::make_pair(fontName, fontData)); CaretLogFine("Created font with encoded name " + fontName); return fontData->m_font; } else { /* * Error creating font */ delete fontData; fontData = NULL; /* * Issue a message about failure to create font but * don't print same message more than once. */ if (std::find(m_failedFontNames.begin(), m_failedFontNames.end(), fontName) == m_failedFontNames.end()) { m_failedFontNames.insert(fontName); CaretLogSevere("Failed to create font with encoded name " + fontName); } } /* * Were we trying to create the default font? */ if (creatingDefaultFontFlag) { return NULL; } /* * Failed so use the default font. */ return m_defaultFont; } /** * @return The font system is valid. */ bool QGLWidgetTextRenderer::isValid() const { return true; } /** * @return Name of the text renderer. */ AString QGLWidgetTextRenderer::getName() const { return AString("Qt OpenGL Text Renderer"); } /** * Constructs invalid font data. */ QGLWidgetTextRenderer::FontData::FontData() { m_valid = false; m_font = NULL; } /** * Constructs a font with the given attributes. * Called should verify that this instance is valid (construction was successful). * * @param annotationText * Annotation Text that is to be drawn. * @param viewportHeight * Height of viewport. */ QGLWidgetTextRenderer::FontData::FontData(const AnnotationText& annotationText, const int32_t viewportHeight) { m_valid = false; m_font = NULL; const AnnotationTextFontNameEnum::Enum fontEnumName = annotationText.getFont(); AString fontFileName = AnnotationTextFontNameEnum::getResourceFontFileName(fontEnumName); if (annotationText.isBoldStyleEnabled() && annotationText.isItalicStyleEnabled()) { fontFileName = AnnotationTextFontNameEnum::getResourceBoldItalicFontFileName(fontEnumName); } else if (annotationText.isBoldStyleEnabled()) { fontFileName = AnnotationTextFontNameEnum::getResourceBoldFontFileName(fontEnumName); } else if (annotationText.isItalicStyleEnabled()) { fontFileName = AnnotationTextFontNameEnum::getResourceItalicFontFileName(fontEnumName); } switch (fontEnumName) { case AnnotationTextFontNameEnum::LIBERTINE: case AnnotationTextFontNameEnum::VERA: fontFileName = "Helvetica"; break; case AnnotationTextFontNameEnum::VERA_MONOSPACE: fontFileName = "Monaco"; break; } CaretAssert( ! fontFileName.isEmpty()); const QString fontName = AnnotationTextFontNameEnum::toGuiName(fontEnumName); m_font = new QFont(fontName); CaretAssert(m_font); const int32_t fontSizePoints = annotationText.getFontSizeForDrawing(viewportHeight); m_font->setPointSize(fontSizePoints); m_font->setBold(annotationText.isBoldStyleEnabled()); m_font->setItalic(annotationText.isItalicStyleEnabled()); m_font->setUnderline(annotationText.isUnderlineStyleEnabled()); m_valid = true; QFontInfo fontInfo(*m_font); CaretLogWarning("Requested font \"" + fontName + "\" and actual font is \"" + fontInfo.family() + " exact match=" + AString::fromBool(m_font->exactMatch())); if ( ! m_valid) { if (m_font != NULL) { delete m_font; m_font = NULL; } } } /** * Destructs font data. */ QGLWidgetTextRenderer::FontData::~FontData() { if (m_font != NULL) { delete m_font; m_font = NULL; } } connectome-workbench-1.2.3+git41-gc4c6c90/src/GuiQt/QGLWidgetTextRenderer.h000066400000000000000000000143561300200146000261020ustar00rootroot00000000000000#ifndef __QGLWIDGET_TEXT_RENDERER_H__ #define __QGLWIDGET_TEXT_RENDERER_H__ /*LICENSE_START*/ /* * Copyright (C) 2015 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 "BrainOpenGLTextRenderInterface.h" class QFont; class QGLWidget; namespace caret { class QGLWidgetTextRenderer : public BrainOpenGLTextRenderInterface { public: QGLWidgetTextRenderer(QGLWidget* glWidget); virtual ~QGLWidgetTextRenderer(); virtual void drawTextAtViewportCoords(const double viewportX, const double viewportY, const double viewportZ, const AnnotationText& annotationText); virtual void drawTextAtViewportCoords(const double viewportX, const double viewportY, const AnnotationText& annotationText); virtual void drawTextAtModelCoords(const double modelX, const double modelY, const double modelZ, const AnnotationText& annotationText); virtual void getTextWidthHeightInPixels(const AnnotationText& annotationText, const double viewportHeight, double& widthOut, double& heightOut); virtual void getBoundsForTextAtViewportCoords(const AnnotationText& annotationText, const double viewportX, const double viewportY, const double viewportZ, const double viewportHeight, double bottomLeftOut[3], double bottomRightOut[3], double topRightOut[3], double topLeftOut[3]); virtual bool isValid() const; virtual AString getName() const; // ADD_NEW_METHODS_HERE private: class FontData { public: FontData(); FontData(const AnnotationText& annotationText, const int32_t viewportHeight); ~FontData(); void initialize(const AString& fontFileName); QFont* m_font; bool m_valid; }; struct CharInfo { CharInfo(const QString& theChar, double x, double y) : m_char(theChar), m_x(x), m_y(y) { } QString m_char; double m_x; double m_y; }; QGLWidgetTextRenderer(const QGLWidgetTextRenderer&); QGLWidgetTextRenderer& operator=(const QGLWidgetTextRenderer&); QFont* findFont(const AnnotationText& annotationText, const bool creatingDefaultFontFlag); void getVerticalTextCharInfo(const AnnotationText& annotationText, double& xMinOut, double& xMaxOut, double& heightOut, std::vector& charInfoOut); void drawHorizontalTextAtWindowCoords(const double windowX, const double windowY, const AnnotationText& annotationText); void drawVerticalTextAtWindowCoords(const double windowX, const double windowY, const AnnotationText& annotationText); void applyForegroundColoring(const AnnotationText& annotationText); void applyBackgroundColoring(const AnnotationText& annotationText, const double textBoundsBox[4]); void setViewportHeight(); QGLWidget* m_glWidget; /** * The default font. DO NOT delete it since it points to * a font in "m_fontNameToFontMap". */ QFont* m_defaultFont; /** * Map for caching font */ typedef std::map FONT_MAP; /** * Iterator for cached fonts. */ typedef FONT_MAP::iterator FONT_MAP_ITERATOR; /** * Caches fonts as they are created */ FONT_MAP m_fontNameToFontMap; /** * Tracks fonts that failed creation to avoid * printing an error message more than once. */ std::set m_failedFontNames; /** Height of the viewport */ int32_t m_viewportHeight; // ADD_NEW_MEMBERS_HERE static const double s_textMarginSize; }; #ifdef __QT_OPEN_G_L_TEXT_RENDERER_DECLARE__ const double QGLWidgetTextRenderer::s_textMarginSize = 2.0; #endif // __QT_OPEN_G_L_TEXT_RENDERER_DECLARE__ } // namespace #endif //__QT_OPEN_G_L_TEXT_RENDERER_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/GuiQt/RegionOfInterestCreateFromBorderDialog.cxx000066400000000000000000000534611300200146000320060ustar00rootroot00000000000000 /*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 #define __REGION_OF_INTEREST_CREATE_FROM_BORDER_DIALOG_DECLARE__ #include "RegionOfInterestCreateFromBorderDialog.h" #undef __REGION_OF_INTEREST_CREATE_FROM_BORDER_DIALOG_DECLARE__ #include #include #include #include #include #include #include #include #include #include #include using namespace caret; #include "AlgorithmException.h" #include "AlgorithmNodesInsideBorder.h" #include "Border.h" #include "BorderFile.h" #include "Brain.h" #include "BrainStructure.h" #include "CaretAssert.h" #include "CaretMappableDataFileAndMapSelector.h" #include "CiftiBrainordinateLabelFile.h" #include "CiftiBrainordinateScalarFile.h" #include "CursorDisplayScoped.h" #include "EventDataFileAdd.h" #include "EventManager.h" #include "EventGraphicsUpdateAllWindows.h" #include "EventSurfaceColoringInvalidate.h" #include "EventUserInterfaceUpdate.h" #include "GuiManager.h" #include "LabelFile.h" #include "MetricFile.h" #include "Surface.h" #include "WuQMessageBox.h" #include "WuQtUtilities.h" /** * \class caret::RegionOfInterestCreateFromBorderDialog * \brief Dailog for creating regions of interest from border(s). * \ingroup GuiQt * * Dialog that creates regions of interest from nodes inside * borders. */ /** * Constructor. * * @param border * Border used to create region of interest. * @param surface * Surface on which border is drawn and whose nodes are selected. * @param parent * Parent on which dialog is displayed. * */ RegionOfInterestCreateFromBorderDialog::RegionOfInterestCreateFromBorderDialog(Border* border, Surface* surface, QWidget* parent) : WuQDialogModal("Create Region of Interest", parent) { std::vector borders; borders.push_back(border); std::vector surfaces; surfaces.push_back(surface); this->createDialog(borders, surfaces); } /** * Destructor. */ RegionOfInterestCreateFromBorderDialog::~RegionOfInterestCreateFromBorderDialog() { } /** * Create the dialog. * * @param borders * Borders used to create regions of interest. * @param surfaceFiles * Surface files used for node selections. */ void RegionOfInterestCreateFromBorderDialog::createDialog(const std::vector& borders, std::vector& surfaces) { this->borders = borders; this->surfaces = surfaces; std::set requiredStructures; const int32_t numberOfBorders = static_cast(borders.size()); for (int32_t i = 0; i < numberOfBorders; i++) { const StructureEnum::Enum structure = borders[i]->getStructure(); requiredStructures.insert(structure); } QWidget* selectorWidget = this->createSelectors(requiredStructures, this->surfaces, this->mapFileTypeSelectors); this->inverseCheckBox = new QCheckBox("Invert Selected Vertices"); QWidget* widget = new QWidget(); QVBoxLayout* dialogLayout = new QVBoxLayout(widget); WuQtUtilities::setLayoutSpacingAndMargins(dialogLayout, 2, 2); dialogLayout->addWidget(selectorWidget); dialogLayout->addWidget(this->inverseCheckBox, 0, Qt::AlignLeft); this->setCentralWidget(widget, WuQDialog::SCROLL_AREA_NEVER); } /** * Create map files selectors for a map file type. * * @param mapFileType * Type of map file for selector. * @param requiredStructures * Structures needed. * @param surfaces * Surfaces available. * @param mapFileSelectors * Output containing the selectors. * @return Widget containing the map selector controls. */ QWidget* RegionOfInterestCreateFromBorderDialog::createSelectors(std::set& requiredStructures, std::vector& surfaces, STRUCTURE_MAP_FILE_SELECTOR_MAP& mapFileSelectors) { AString borderName; if (this->borders.empty() == false) { borderName = this->borders[0]->getName(); } QWidget* widget = new QWidget(); QVBoxLayout* mapSelectionLayout = new QVBoxLayout(widget); std::vector allowedMapFileTypes; allowedMapFileTypes.push_back(DataFileTypeEnum::CONNECTIVITY_DENSE_LABEL); allowedMapFileTypes.push_back(DataFileTypeEnum::CONNECTIVITY_DENSE_SCALAR); allowedMapFileTypes.push_back(DataFileTypeEnum::LABEL); allowedMapFileTypes.push_back(DataFileTypeEnum::METRIC); for (std::set::iterator structureIter = requiredStructures.begin(); structureIter != requiredStructures.end(); structureIter++) { const StructureEnum::Enum structure = *structureIter; for (std::vector::iterator surfaceIter = this->surfaces.begin(); surfaceIter != surfaces.end(); surfaceIter++) { Surface* surface = *surfaceIter; if (surface->getStructure() == structure) { std::vector allowedStructures; allowedStructures.push_back(surface->getStructure()); CaretMappableDataFileAndMapSelector* mapSelector = new CaretMappableDataFileAndMapSelector(borderName, GuiManager::get()->getBrain(), allowedMapFileTypes, allowedStructures, this); QObject::connect(mapSelector, SIGNAL(selectionChanged(CaretMappableDataFileAndMapSelector*)), this, SLOT(fileSelectionWasChanged(CaretMappableDataFileAndMapSelector*))); mapSelectionLayout->addWidget(mapSelector->getWidget()); mapFileSelectors.insert(std::make_pair(structure, mapSelector)); } } } return widget; } /** * Called when a map type/file/name is selected. * @param selector * Selector in which selection was made. */ void RegionOfInterestCreateFromBorderDialog::fileSelectionWasChanged(CaretMappableDataFileAndMapSelector* /*selector*/) { // std::cout << "Selection changed. " << std::endl; } /** * Called when the user presses the OK button. */ void RegionOfInterestCreateFromBorderDialog::okButtonClicked() { AString errorMessage; for (STRUCTURE_MAP_FILE_SELECTOR_ITERATOR iter = this->mapFileTypeSelectors.begin(); iter != this->mapFileTypeSelectors.end(); iter++) { //const StructureEnum::Enum structure = iter->first; CaretMappableDataFileAndMapSelector* mapSelector = iter->second; AString msg; if (mapSelector->isValidSelections(msg) == false) { if (errorMessage.isEmpty() == false) { errorMessage += "\n"; } errorMessage += msg; } } BorderFile* debugBorderFile = NULL; const bool isInverseSelection = this->inverseCheckBox->isChecked(); bool allowDialogToClose = true; if (errorMessage.isEmpty() == false) { allowDialogToClose = false; } else { /* * Show the wait cursor. */ CursorDisplayScoped cursor; cursor.showWaitCursor(); for (std::vector::iterator borderIterator = this->borders.begin(); borderIterator != this->borders.end(); borderIterator++) { Border* border = *borderIterator; const StructureEnum::Enum structure = border->getStructure(); STRUCTURE_MAP_FILE_SELECTOR_ITERATOR structureMapIterator = this->mapFileTypeSelectors.find(structure); if (structureMapIterator != this->mapFileTypeSelectors.end()) { const StructureEnum::Enum structure = structureMapIterator->first; CaretMappableDataFileAndMapSelector* mapSelector = structureMapIterator->second; mapSelector->saveCurrentSelections(); // save for next time switch (mapSelector->getSelectedMapFileType()) { case DataFileTypeEnum::CONNECTIVITY_DENSE_LABEL: { AString errorMessage; CiftiBrainordinateLabelFile* ciftiLabelFile = dynamic_cast(mapSelector->getSelectedMapFile()); CaretAssert(ciftiLabelFile); const int32_t mapIndex = mapSelector->getSelectedMapIndex(); const int32_t labelKey = mapSelector->getSelectedLabelKey(); Surface* surface = NULL; for (std::vector::iterator surfaceIterator = this->surfaces.begin(); surfaceIterator != this->surfaces.end(); surfaceIterator++) { Surface* s = *surfaceIterator; if (s->getStructure() == structure) { surface = s; break; } } CaretAssert(surface); if (surface != NULL) { try { AlgorithmNodesInsideBorder algorithmInsideBorder(NULL, surface, border, isInverseSelection, mapIndex, labelKey, ciftiLabelFile); const BorderFile* dbf = algorithmInsideBorder.getDebugBorderFile(); if (dbf != NULL) { debugBorderFile = new BorderFile(*dbf); } } catch (const AlgorithmException& e) { if (errorMessage.isEmpty() == false) { errorMessage += "\n"; } errorMessage += e.whatString(); } } else { if (errorMessage.isEmpty() == false) { errorMessage += "\n"; } errorMessage += ("Surface for structure " + StructureEnum::toGuiName(structure) + " was not found"); } } break; case DataFileTypeEnum::CONNECTIVITY_DENSE_SCALAR: { AString errorMessage; CiftiBrainordinateScalarFile* ciftiScalarFile = dynamic_cast(mapSelector->getSelectedMapFile()); CaretAssert(ciftiScalarFile); const int32_t mapIndex = mapSelector->getSelectedMapIndex(); const float value = mapSelector->getSelectedMetricValue(); Surface* surface = NULL; for (std::vector::iterator surfaceIterator = this->surfaces.begin(); surfaceIterator != this->surfaces.end(); surfaceIterator++) { Surface* s = *surfaceIterator; if (s->getStructure() == structure) { surface = s; break; } } CaretAssert(surface); if (surface != NULL) { try { AlgorithmNodesInsideBorder algorithmInsideBorder(NULL, surface, border, isInverseSelection, mapIndex, value, ciftiScalarFile); const BorderFile* dbf = algorithmInsideBorder.getDebugBorderFile(); if (dbf != NULL) { debugBorderFile = new BorderFile(*dbf); } } catch (const AlgorithmException& e) { if (errorMessage.isEmpty() == false) { errorMessage += "\n"; } errorMessage += e.whatString(); } } else { if (errorMessage.isEmpty() == false) { errorMessage += "\n"; } errorMessage += ("Surface for structure " + StructureEnum::toGuiName(structure) + " was not found"); } } break; case DataFileTypeEnum::LABEL: { LabelFile* labelFile = dynamic_cast(mapSelector->getSelectedMapFile()); const int32_t mapIndex = mapSelector->getSelectedMapIndex(); const int32_t labelKey = mapSelector->getSelectedLabelKey(); Surface* surface = NULL; for (std::vector::iterator surfaceIterator = this->surfaces.begin(); surfaceIterator != this->surfaces.end(); surfaceIterator++) { Surface* s = *surfaceIterator; if (s->getStructure() == structure) { surface = s; break; } } CaretAssert(surface); if (surface != NULL) { try { AlgorithmNodesInsideBorder algorithmInsideBorder(NULL, surface, border, isInverseSelection, mapIndex, labelKey, labelFile); const BorderFile* dbf = algorithmInsideBorder.getDebugBorderFile(); if (dbf != NULL) { debugBorderFile = new BorderFile(*dbf); } } catch (const AlgorithmException& e) { if (errorMessage.isEmpty() == false) { errorMessage += "\n"; } errorMessage += e.whatString(); } } else { if (errorMessage.isEmpty() == false) { errorMessage += "\n"; } errorMessage += ("Surface for structure " + StructureEnum::toGuiName(structure) + " was not found"); } } break; case DataFileTypeEnum::METRIC: { MetricFile* metricFile = dynamic_cast(mapSelector->getSelectedMapFile()); const int32_t mapIndex = mapSelector->getSelectedMapIndex(); const float value = mapSelector->getSelectedMetricValue(); Surface* surface = NULL; for (std::vector::iterator surfaceIterator = this->surfaces.begin(); surfaceIterator != this->surfaces.end(); surfaceIterator++) { Surface* s = *surfaceIterator; if (s->getStructure() == structure) { surface = s; break; } } CaretAssert(surface); if (surface != NULL) { try { AlgorithmNodesInsideBorder algorithmInsideBorder(NULL, surface, border, isInverseSelection, mapIndex, value, metricFile); const BorderFile* dbf = algorithmInsideBorder.getDebugBorderFile(); if (dbf != NULL) { debugBorderFile = new BorderFile(*dbf); } } catch (const AlgorithmException& e) { if (errorMessage.isEmpty() == false) { errorMessage += "\n"; } errorMessage += e.whatString(); } } else { if (errorMessage.isEmpty() == false) { errorMessage += "\n"; } errorMessage += ("Surface for structure " + StructureEnum::toGuiName(structure) + " was not found"); } } break; default: CaretAssertMessage(0, "Unsupported file type."); break; } } else { } } } if (debugBorderFile != NULL) { EventManager::get()->sendEvent(EventDataFileAdd(debugBorderFile).getPointer()); } EventManager::get()->sendEvent(EventSurfaceColoringInvalidate().getPointer()); EventManager::get()->sendEvent(EventUserInterfaceUpdate().getPointer()); EventManager::get()->sendEvent(EventGraphicsUpdateAllWindows().getPointer()); if (errorMessage.isEmpty() == false) { WuQMessageBox::errorOk(this, errorMessage); } if (allowDialogToClose) { WuQDialogModal::okButtonClicked(); } } connectome-workbench-1.2.3+git41-gc4c6c90/src/GuiQt/RegionOfInterestCreateFromBorderDialog.h000066400000000000000000000061101300200146000314200ustar00rootroot00000000000000#ifndef __REGION_OF_INTEREST_CREATE_FROM_BORDER_DIALOG__H_ #define __REGION_OF_INTEREST_CREATE_FROM_BORDER_DIALOG__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 "CaretMappableDataFileAndMapSelector.h" #include "StructureEnum.h" #include "WuQDialogModal.h" class QButtonGroup; class QCheckBox; class QDoubleSpinBox; class QLineEdit; class QStackedWidget; namespace caret { class Border; class Surface; class RegionOfInterestCreateFromBorderDialog : public WuQDialogModal { Q_OBJECT public: RegionOfInterestCreateFromBorderDialog(Border* border, Surface* surface, QWidget* parent); virtual ~RegionOfInterestCreateFromBorderDialog(); private slots: void fileSelectionWasChanged(CaretMappableDataFileAndMapSelector*); protected: virtual void okButtonClicked(); private: RegionOfInterestCreateFromBorderDialog(const RegionOfInterestCreateFromBorderDialog&); RegionOfInterestCreateFromBorderDialog& operator=(const RegionOfInterestCreateFromBorderDialog&); void createDialog(const std::vector& borders, std::vector& surfaces); typedef std::map STRUCTURE_MAP_FILE_SELECTOR_MAP; typedef STRUCTURE_MAP_FILE_SELECTOR_MAP::iterator STRUCTURE_MAP_FILE_SELECTOR_ITERATOR; QWidget* createSelectors(std::set& requiredStructures, std::vector& surfaces, STRUCTURE_MAP_FILE_SELECTOR_MAP& mapFileSelectors); STRUCTURE_MAP_FILE_SELECTOR_MAP mapFileTypeSelectors; std::vector surfaces; std::vector borders; QCheckBox* inverseCheckBox; }; #ifdef __REGION_OF_INTEREST_CREATE_FROM_BORDER_DIALOG_DECLARE__ // #endif // __REGION_OF_INTEREST_CREATE_FROM_BORDER_DIALOG_DECLARE__ } // namespace #endif //__REGION_OF_INTEREST_CREATE_FROM_BORDER_DIALOG__H_ connectome-workbench-1.2.3+git41-gc4c6c90/src/GuiQt/SceneCreateReplaceDialog.cxx000066400000000000000000000521221300200146000271200ustar00rootroot00000000000000 /*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 __SCENE_CREATE_REPLACE_DIALOG_DECLARE__ #include "SceneCreateReplaceDialog.h" #undef __SCENE_CREATE_REPLACE_DIALOG_DECLARE__ #include #include #include #include #include #include #include #include #include "Brain.h" #include "BrainBrowserWindow.h" #include "CaretAssert.h" #include "CaretPreferences.h" #include "DataFileException.h" #include "EventBrowserTabGetAll.h" #include "EventBrowserTabGetAllViewed.h" #include "EventImageCapture.h" #include "EventManager.h" #include "GuiManager.h" #include "ImageFile.h" #include "PlainTextStringBuilder.h" #include "Scene.h" #include "SceneAttributes.h" #include "SceneFile.h" #include "SceneInfo.h" #include "SessionManager.h" #include "WuQMessageBox.h" #include "WuQtUtilities.h" using namespace caret; /** * \class caret::SceneCreateReplaceDialog * \brief Dialog for creating or replacing a scene. * \ingroup GuiQt * */ #include "Scene.h" /** * Constructor. * * @param dialogTitle * Title of the dialog. * @param parent * Parent on which dialog is displayed. * @param sceneFile * Scene file to which scene is added or replaced. * @param mode * Scene add/insert/replace mode * @param sceneToReplace * If non-NULL, this scene will be replaced. */ SceneCreateReplaceDialog::SceneCreateReplaceDialog(const AString& dialogTitle, QWidget* parent, SceneFile* sceneFile, const Mode mode, Scene* sceneToInsertOrReplace) : WuQDialogModal(dialogTitle, parent) { CaretAssert(sceneFile); switch (m_mode) { case MODE_ADD_NEW_SCENE: break; case MODE_INSERT_NEW_SCENE: CaretAssert(sceneToInsertOrReplace); break; case MODE_REPLACE_SCENE: CaretAssert(sceneToInsertOrReplace); break; } if ( ! s_previousSelectionsValid) { s_previousSelectionsValid = true; s_previousSelections.m_addAllLoadedFiles = true; s_previousSelections.m_addAllTabs = true; s_previousSelections.m_addModifiedPaletteSettings = true; s_previousSelections.m_addSpecFileNameToScene = true; } m_sceneFile = sceneFile; m_mode = mode; m_sceneToInsertOrReplace = sceneToInsertOrReplace; m_sceneThatWasCreated = NULL; /* * Options widget */ QLabel* optionsLabel = new QLabel("Options"); QWidget* optionsWidget = createSceneOptionsWidget(); /* * Create scene information widgets */ QLabel* nameLabel = new QLabel("Name"); m_nameLineEdit = new QLineEdit(); QLabel* sceneIDLabel = new QLabel("BALSA Scene ID"); m_balsaSceneIDLineEdit = new QLineEdit(); m_balsaSceneIDLineEdit->setToolTip("Scene ID is for use with BALSA Database"); 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++; QGridLayout* infoGridLayout = new QGridLayout(); infoGridLayout->setColumnStretch(labelColumn, 0); infoGridLayout->setColumnStretch(widgetColumn, 100); int32_t rowCounter = 0; infoGridLayout->setRowStretch(rowCounter, 0); infoGridLayout->addWidget(nameLabel, rowCounter, labelColumn, labelAlignment); infoGridLayout->addWidget(m_nameLineEdit, rowCounter, widgetColumn); rowCounter++; infoGridLayout->addWidget(sceneIDLabel, rowCounter, labelColumn, labelAlignment); infoGridLayout->addWidget(m_balsaSceneIDLineEdit, rowCounter, widgetColumn); rowCounter++; infoGridLayout->setRowStretch(rowCounter, 100); infoGridLayout->addWidget(descriptionLabel, rowCounter, labelColumn, labelAlignment); infoGridLayout->addWidget(m_descriptionTextEdit, rowCounter, widgetColumn); rowCounter++; infoGridLayout->setRowStretch(rowCounter, 0); infoGridLayout->addWidget(optionsLabel, rowCounter, labelColumn, labelAlignment); infoGridLayout->addWidget(optionsWidget, rowCounter, widgetColumn); rowCounter++; /* * Add the layout to a widget and return the widget. */ QWidget* infoWidget = new QWidget(); infoWidget->setLayout(infoGridLayout); QWidget* dialogWidget = new QWidget(); QVBoxLayout* dialogLayout = new QVBoxLayout(dialogWidget); dialogLayout->addWidget(infoWidget, 100); 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); 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(""); } QString descriptionString = description.getText().trimmed(); switch (m_mode) { case MODE_ADD_NEW_SCENE: m_descriptionTextEdit->setPlainText(descriptionString); break; case MODE_INSERT_NEW_SCENE: m_descriptionTextEdit->setPlainText(descriptionString); break; case MODE_REPLACE_SCENE: m_nameLineEdit->setText(sceneToInsertOrReplace->getName()); m_balsaSceneIDLineEdit->setText(sceneToInsertOrReplace->getBalsaSceneID()); m_descriptionTextEdit->setPlainText(sceneToInsertOrReplace->getDescription()); break; } setMinimumWidth(600); setMinimumHeight(300); setSaveWindowPositionForNextTime("SceneCreateDialog"); } /** * Destructor. */ SceneCreateReplaceDialog::~SceneCreateReplaceDialog() { } /** * Static method that creates a dialog for creating a new scene and * returns the scene that was created or NULL if scene was not created. * * @param parent * Parent widget on which dialog is displayed. * @param sceneFile * Scene file to which new scene is added. * @return * Scene that was created or NULL if user cancelled or there was an error. */ Scene* SceneCreateReplaceDialog::createNewScene(QWidget* parent, SceneFile* sceneFile) { SceneCreateReplaceDialog dialog("Create New Scene", parent, sceneFile, MODE_ADD_NEW_SCENE, NULL); dialog.exec(); Scene* scene = dialog.m_sceneThatWasCreated; return scene; } /** * Static method that creates a dialog for creating a new scene and * returns the scene that was created or NULL if scene was not created. * * @param parent * Parent widget on which dialog is displayed. * @param sceneFile * Scene file to which new scene is added. * @param insertBeforeScene * Insert the newly created scene BEFORE this scene. * @return * Scene that was created or NULL if user cancelled or there was an error. */ Scene* SceneCreateReplaceDialog::createNewSceneInsertBeforeScene(QWidget* parent, SceneFile* sceneFile, const Scene* insertBeforeScene) { SceneCreateReplaceDialog dialog("Insert New Scene", parent, sceneFile, MODE_INSERT_NEW_SCENE, const_cast(insertBeforeScene)); dialog.exec(); Scene* scene = dialog.m_sceneThatWasCreated; return scene; } /** * Static method that creates a dialog for replacing and existing scene and * returns the scene that was created or NULL if scene was not created. * * @param parent * Parent widget on which dialog is displayed. * @param sceneFile * File in which the given scene exists and will be replaced. * @param sceneToReplace * Scene that is being replaced. If the user presses the OK button * to replace this scene it will be destroyed so the pointer must not * be deferenced at any time after calling this method. * @return * Scene that was created or NULL if user cancelled or there was an error. */ Scene* SceneCreateReplaceDialog::replaceExistingScene(QWidget* parent, SceneFile* sceneFile, Scene* sceneToReplace) { const AString title = ("Replace Scene: " + sceneToReplace->getName()); SceneCreateReplaceDialog dialog(title, parent, sceneFile, MODE_REPLACE_SCENE, sceneToReplace); /* * Error checking will not allow two scenes with the same name * so temporarily modify name of scene being replaced and restore * it if the user does not hit OK. */ const AString savedSceneName = sceneToReplace->getName(); sceneToReplace->setName("slkkjdlkfjaslfjdljfdkljdfjsdfj"); if (dialog.exec() == SceneCreateReplaceDialog::Rejected) { sceneToReplace->setName(savedSceneName); } Scene* scene = dialog.m_sceneThatWasCreated; return scene; } /** * Add an image to the scene. * * @param scene * Scene to which image is added. */ void SceneCreateReplaceDialog::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); 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); scene->getSceneInfo()->setImageBytes(byteArray, PREFERRED_IMAGE_FORMAT); } catch (const DataFileException& dfe) { WuQMessageBox::errorOk(this, (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; } } /** * @return Widget containing the scene options widgets. */ QWidget* SceneCreateReplaceDialog::createSceneOptionsWidget() { /* * Create scene options widgets */ m_addSpecFileNameToSceneCheckBox = new QCheckBox("Add name of spec file to scene"); m_addSpecFileNameToSceneCheckBox->setChecked(s_previousSelections.m_addSpecFileNameToScene); WuQtUtilities::setWordWrappedToolTip(m_addSpecFileNameToSceneCheckBox, "Include name of spec file in the scene"); m_addAllTabsCheckBox = new QCheckBox("Add all tabs to scene"); m_addAllTabsCheckBox->setChecked(s_previousSelections.m_addAllTabs); WuQtUtilities::setWordWrappedToolTip(m_addAllTabsCheckBox, "Add all tabs to the scene. When this option is selected, " "the scene will be larger and require additional time to " "load. If NOT selected, only the selected tab in each " "window is saved to the scene."); m_addAllLoadedFilesCheckBox = new QCheckBox("Add all loaded files to scene"); m_addAllLoadedFilesCheckBox->setChecked(s_previousSelections.m_addAllLoadedFiles); WuQtUtilities::setWordWrappedToolTip(m_addAllLoadedFilesCheckBox, "Add all loaded files to scene. When this option is selected, " "the scene may require additional time to load as file that " "play no role in reproducing the scene will be loaded. If NOT " "selected, the scene may load more quickly."); m_addModifiedPaletteSettingsCheckBox = new QCheckBox("Add modified palette color mapping to scene"); m_addModifiedPaletteSettingsCheckBox->setChecked(s_previousSelections.m_addModifiedPaletteSettings); WuQtUtilities::setWordWrappedToolTip(m_addModifiedPaletteSettingsCheckBox, "The palette color mapping is saved within each data files that maps " "its data to brainordinates. However, there are instances in which " "the user wants the scene to display the data with palette color mapping " "that is different from that in the file. If this option is " "selected, modified palettes color mapping will be saved to the scene " "and the data files with modified palette color mapping do not need " "to be saved."); /* * Layout for scene options widgets */ QVBoxLayout* optionsLayout = new QVBoxLayout(); optionsLayout->addWidget(m_addSpecFileNameToSceneCheckBox); optionsLayout->addWidget(m_addAllTabsCheckBox); optionsLayout->addWidget(m_addAllLoadedFilesCheckBox); optionsLayout->addWidget(m_addModifiedPaletteSettingsCheckBox); /* * Add the layout to a widget and return the widget. */ QFrame* optionsWidget = new QFrame(); optionsWidget->setFrameStyle(QFrame::Box | QFrame::Plain); optionsWidget->setLineWidth(1); // QWidget* optionsWidget = new QWidget(); optionsWidget->setLayout(optionsLayout); return optionsWidget; } /** * Gets called if the user presses the OK button. */ void SceneCreateReplaceDialog::okButtonClicked() { s_previousSelections.m_addAllLoadedFiles = m_addAllLoadedFilesCheckBox->isChecked(); s_previousSelections.m_addAllTabs = m_addAllTabsCheckBox->isChecked(); s_previousSelections.m_addModifiedPaletteSettings = m_addModifiedPaletteSettingsCheckBox->isChecked(); s_previousSelections.m_addSpecFileNameToScene = m_addSpecFileNameToSceneCheckBox->isChecked(); const AString newSceneName = m_nameLineEdit->text(); AString errorMessage; if (newSceneName.isEmpty()) { errorMessage = "Scene Name is empty."; } else if (m_sceneFile->getSceneWithName(newSceneName) != NULL) { errorMessage = ("An existing scene uses the name \"" + newSceneName + "\". Scene names must be unique."); } if ( ! errorMessage.isEmpty()) { WuQMessageBox::errorOk(this, errorMessage); return; } Scene* newScene = new Scene(SceneTypeEnum::SCENE_TYPE_FULL); Scene::setSceneBeingCreated(newScene); newScene->setName(newSceneName); newScene->setDescription(m_descriptionTextEdit->toPlainText()); newScene->setBalsaSceneID(m_balsaSceneIDLineEdit->text().trimmed()); const std::vector windowIndices = GuiManager::get()->getAllOpenBrainBrowserWindowIndices(); /* * Get all browser tabs and only save transformations for tabs * that are valid. */ std::vector tabIndices; if (m_addAllTabsCheckBox->isChecked()) { EventBrowserTabGetAll getAllTabs; EventManager::get()->sendEvent(getAllTabs.getPointer()); tabIndices = getAllTabs.getBrowserTabIndices(); } else { EventBrowserTabGetAllViewed getViewedTabs; EventManager::get()->sendEvent(getViewedTabs.getPointer()); tabIndices = getViewedTabs.getViewdedBrowserTabIndices(); } std::sort(tabIndices.begin(), tabIndices.end()); SceneAttributes* sceneAttributes = newScene->getAttributes(); sceneAttributes->setSceneFileName(m_sceneFile->getFileName()); sceneAttributes->setSceneName(newSceneName); sceneAttributes->setIndicesOfTabsAndWindowsForSavingToScene(tabIndices, windowIndices); sceneAttributes->setSpecFileNameSavedToScene(m_addSpecFileNameToSceneCheckBox->isChecked()); sceneAttributes->setAllLoadedFilesSavedToScene(m_addAllLoadedFilesCheckBox->isChecked()); sceneAttributes->setModifiedPaletteSettingsSavedToScene(m_addModifiedPaletteSettingsCheckBox->isChecked()); newScene->addClass(GuiManager::get()->saveToScene(sceneAttributes, "guiManager")); addImageToScene(newScene); switch (m_mode) { case MODE_ADD_NEW_SCENE: m_sceneFile->addScene(newScene); break; case MODE_INSERT_NEW_SCENE: m_sceneFile->insertScene(newScene, m_sceneToInsertOrReplace); break; case MODE_REPLACE_SCENE: m_sceneFile->replaceScene(newScene, m_sceneToInsertOrReplace); break; } m_sceneThatWasCreated = newScene; Scene::setSceneBeingCreated(NULL); WuQDialogModal::okButtonClicked(); } connectome-workbench-1.2.3+git41-gc4c6c90/src/GuiQt/SceneCreateReplaceDialog.h000066400000000000000000000075241300200146000265530ustar00rootroot00000000000000#ifndef __SCENE_CREATE_REPLACE_DIALOG_H__ #define __SCENE_CREATE_REPLACE_DIALOG_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 "WuQDialogModal.h" class QCheckBox; class QLineEdit; class QPlainTextEdit; namespace caret { class Scene; class SceneFile; class SceneCreateReplaceDialog : public WuQDialogModal { Q_OBJECT public: static Scene* createNewScene(QWidget* parent, SceneFile* sceneFile); static Scene* createNewSceneInsertBeforeScene(QWidget* parent, SceneFile* sceneFile, const Scene* insertBeforeScene); static Scene* replaceExistingScene(QWidget* parent, SceneFile* sceneFile, Scene* sceneToReplace); virtual ~SceneCreateReplaceDialog(); private: enum Mode { MODE_ADD_NEW_SCENE, MODE_INSERT_NEW_SCENE, MODE_REPLACE_SCENE }; SceneCreateReplaceDialog(const AString& dialogTitle, QWidget* parent, SceneFile* sceneFile, const Mode mode, Scene* sceneToInsertOrReplace); SceneCreateReplaceDialog(const SceneCreateReplaceDialog&); SceneCreateReplaceDialog& operator=(const SceneCreateReplaceDialog&); public: // ADD_NEW_METHODS_HERE protected: virtual void okButtonClicked(); private: // ADD_NEW_MEMBERS_HERE void addImageToScene(Scene* scene); SceneFile* m_sceneFile; Mode m_mode; Scene* m_sceneToInsertOrReplace; /** Scene that was created DO NOT DESTROY SINCE RETURNED TO CALLER */ Scene* m_sceneThatWasCreated; QWidget* createSceneOptionsWidget(); QLineEdit* m_nameLineEdit; QLineEdit* m_balsaSceneIDLineEdit; QPlainTextEdit* m_descriptionTextEdit; QCheckBox* m_addSpecFileNameToSceneCheckBox; QCheckBox* m_addAllTabsCheckBox; QCheckBox* m_addAllLoadedFilesCheckBox; QCheckBox* m_addModifiedPaletteSettingsCheckBox; struct PreviousSelections { bool m_addSpecFileNameToScene; bool m_addAllTabs; bool m_addAllLoadedFiles; bool m_addModifiedPaletteSettings; }; static PreviousSelections s_previousSelections; static bool s_previousSelectionsValid; }; #ifdef __SCENE_CREATE_REPLACE_DIALOG_DECLARE__ SceneCreateReplaceDialog::PreviousSelections SceneCreateReplaceDialog::s_previousSelections; bool SceneCreateReplaceDialog::s_previousSelectionsValid = false; #endif // __SCENE_CREATE_REPLACE_DIALOG_DECLARE__ } // namespace #endif //__SCENE_CREATE_REPLACE_DIALOG_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/GuiQt/SceneDialog.cxx000066400000000000000000001746771300200146000245240ustar00rootroot00000000000000 /*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 #include #include #include #include #include #include #define __SCENE_DIALOG_DECLARE__ #include "SceneDialog.h" #undef __SCENE_DIALOG_DECLARE__ #include "BalsaDatabaseUploadSceneFileDialog.h" #include "Brain.h" #include "BrainBrowserWindow.h" #include "BrainConstants.h" #include "BrowserTabContent.h" #include "CaretAssert.h" #include "CaretFileDialog.h" #include "CaretLogger.h" #include "CaretMappableDataFile.h" #include "CursorDisplayScoped.h" #include "CaretPreferences.h" #include "DataFileException.h" #include "ElapsedTimer.h" #include "EventBrowserTabGetAll.h" #include "EventDataFileAdd.h" #include "EventDataFileRead.h" #include "EventDataFileReload.h" #include "EventGraphicsUpdateAllWindows.h" #include "EventImageCapture.h" #include "EventManager.h" #include "EventModelGetAll.h" #include "EventUserInterfaceUpdate.h" #include "FileInformation.h" #include "GuiManager.h" #include "ImageFile.h" #include "ProgressReportingDialog.h" #include "Scene.h" #include "SceneAttributes.h" #include "SceneClass.h" #include "SceneCreateReplaceDialog.h" #include "SceneFile.h" #include "SceneInfo.h" #include "ScenePreviewDialog.h" #include "SessionManager.h" #include "UsernamePasswordWidget.h" #include "WuQDataEntryDialog.h" #include "WuQDialogNonModal.h" #include "WuQMessageBox.h" #include "WuQtUtilities.h" #include "WuQWidgetObjectGroup.h" #include "ZipSceneFileDialog.h" using namespace caret; /** * \class caret::SceneDialog * \brief Dialog for manipulation of scenes. * \ingroup GuiQt */ /** * Constructor. * * @param parent * The parent widget. */ SceneDialog::SceneDialog(QWidget* parent) : WuQDialogNonModal("Scenes", parent) { m_selectedSceneClassInfoIndex = -1; /* * No apply buton */ setApplyButtonText(""); /* * Set the dialog's widget */ this->setCentralWidget(createMainPage(), WuQDialog::SCROLL_AREA_NEVER); /* * No auto default button processing (Qt highlights button) */ disableAutoDefaultForAllPushButtons(); setSaveWindowPositionForNextTime(true); /* * Update the dialog. */ updateDialog(); EventManager::get()->addEventListener(this, EventTypeEnum::EVENT_USER_INTERFACE_UPDATE); /* * Need to use a "processed" event listener for file read and reload events * so that our receiveEvent() method is called after the files have been * read. */ EventManager::get()->addProcessedEventListener(this, EventTypeEnum::EVENT_DATA_FILE_READ); EventManager::get()->addProcessedEventListener(this, EventTypeEnum::EVENT_DATA_FILE_RELOAD); resize(900, 700); } /** * Destructor. */ SceneDialog::~SceneDialog() { EventManager::get()->removeAllEventsFromListener(this); } /** * Update the scene dialog. */ void SceneDialog::updateDialog() { loadSceneFileComboBox(NULL); loadScenesIntoDialog(NULL); } /** * Get the selected scene file. * @return SceneFile or NULL if no scene file. */ SceneFile* SceneDialog::getSelectedSceneFile() { SceneFile* sceneFile = NULL; const int fileComboBoxIndex = m_sceneFileSelectionComboBox->currentIndex(); if (fileComboBoxIndex >= 0) { void* filePointer = m_sceneFileSelectionComboBox->itemData(fileComboBoxIndex).value(); sceneFile = (SceneFile*)filePointer; } return sceneFile; } /** * @return The selected scene. */ Scene* SceneDialog::getSelectedScene() { Scene* selectedScene = NULL; SceneFile* sceneFile = getSelectedSceneFile(); if (sceneFile != NULL) { int32_t numValidScenes = sceneFile->getNumberOfScenes(); bool selectedSceneChanged = false; if (m_selectedSceneClassInfoIndex >= numValidScenes) { m_selectedSceneClassInfoIndex = numValidScenes - 1; selectedSceneChanged = true; } if (m_selectedSceneClassInfoIndex < 0) { if (numValidScenes > 0) { m_selectedSceneClassInfoIndex = 0; selectedSceneChanged = true; } } if ((m_selectedSceneClassInfoIndex >= 0) && (m_selectedSceneClassInfoIndex < numValidScenes)) { selectedScene = sceneFile->getSceneAtIndex(m_selectedSceneClassInfoIndex); } if (selectedSceneChanged) { highlightSceneAtIndex(m_selectedSceneClassInfoIndex); } } return selectedScene; } /** * Load the scene files into the scene file combo box. * * @param selectedSceneFileIn * Scene file that is added. */ void SceneDialog::loadSceneFileComboBox(SceneFile* selectedSceneFileIn) { SceneFile* selectedSceneFile = selectedSceneFileIn; if (selectedSceneFile == NULL) { selectedSceneFile = getSelectedSceneFile(); } Brain* brain = GuiManager::get()->getBrain(); const int32_t numSceneFiles = brain->getNumberOfSceneFiles(); m_sceneFileSelectionComboBox->clear(); int defaultFileComboIndex = 0; for (int32_t i = 0; i < numSceneFiles; i++) { SceneFile* sceneFile = brain->getSceneFile(i); if (sceneFile == selectedSceneFile) { defaultFileComboIndex = i; } const AString name = sceneFile->getFileNameNoPath(); 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()); } } /** * Load the scene into the dialog. * * @param selectedSceneIn * Scene to add. */ void SceneDialog::loadScenesIntoDialog(Scene* selectedSceneIn) { SceneFile* sceneFile = getSelectedSceneFile(); Scene* selectedScene = selectedSceneIn; if (selectedScene == NULL) { selectedScene = getSelectedScene(); } for (std::vector::iterator iter = m_sceneClassInfoWidgets.begin(); iter != m_sceneClassInfoWidgets.end(); iter++) { SceneClassInfoWidget* sciw = *iter; sciw->blockSignals(true); sciw->updateContent(NULL, -1); } int32_t numberOfValidSceneInfoWidgets = 0; int32_t defaultIndex = -1; if (sceneFile != NULL) { const int32_t numScenes = sceneFile->getNumberOfScenes(); for (int32_t i = 0; i < numScenes; i++) { Scene* scene = sceneFile->getSceneAtIndex(i); QByteArray imageByteArray; AString imageBytesFormat; scene->getSceneInfo()->getImageBytes(imageByteArray, imageBytesFormat); SceneClassInfoWidget* sciw = NULL; if (i >= static_cast(m_sceneClassInfoWidgets.size())) { sciw = new SceneClassInfoWidget(); QObject::connect(sciw, SIGNAL(activated(int32_t)), this, SLOT(sceneActivated(int32_t))); QObject::connect(sciw, SIGNAL(highlighted(int32_t)), this, SLOT(sceneHighlighted(int32_t))); m_sceneClassInfoWidgets.push_back(sciw); m_sceneSelectionLayout->addWidget(sciw); } else { sciw = m_sceneClassInfoWidgets[i]; } sciw->updateContent(scene, i); sciw->setBackgroundForSelected(i == 1); if (scene == selectedScene) { defaultIndex = i; } else if (defaultIndex < 0) { defaultIndex = i; } } numberOfValidSceneInfoWidgets = numScenes; } const int32_t numberOfSceneInfoWidgets = static_cast(m_sceneClassInfoWidgets.size()); for (int32_t i = 0; i < numberOfSceneInfoWidgets; i++) { bool visibilityStatus = false; if (i < numberOfValidSceneInfoWidgets) { visibilityStatus = true; } m_sceneClassInfoWidgets[i]->setVisible(visibilityStatus); m_sceneClassInfoWidgets[i]->blockSignals(false); } if (defaultIndex >= 0) { highlightSceneAtIndex(defaultIndex); } const bool validFile = (getSelectedSceneFile() != NULL); bool validScene = false; if (validFile) { const Scene* scene = getSelectedScene(); if (scene != NULL) { validScene = true; } } enableSceneMoveUpAndDownButtons(); m_addNewScenePushButton->setEnabled(validFile); m_deleteScenePushButton->setEnabled(validScene); m_insertNewScenePushButton->setEnabled(validScene); m_replaceScenePushButton->setEnabled(validScene); m_showScenePushButton->setEnabled(validScene); m_showSceneImagePreviewPushButton->setEnabled(validScene); } /** * Enable the move up/down buttons */ void SceneDialog::enableSceneMoveUpAndDownButtons() { const int32_t numberOfSceneInfoWidgets = static_cast(m_sceneClassInfoWidgets.size()); m_moveSceneUpPushButton->setEnabled(m_selectedSceneClassInfoIndex > 0); m_moveSceneDownPushButton->setEnabled((m_selectedSceneClassInfoIndex >= 0) && (m_selectedSceneClassInfoIndex < (numberOfSceneInfoWidgets - 1))); } /** * Highlight the scene at the given index. * * @param sceneIndex * Index of scene to highlight. */ void SceneDialog::highlightSceneAtIndex(const int32_t sceneIndex) { bool sceneIndexValid = false; const int32_t numberOfSceneInfoWidgets = static_cast(m_sceneClassInfoWidgets.size()); for (int32_t i = 0; i < numberOfSceneInfoWidgets; i++) { SceneClassInfoWidget* sciw = m_sceneClassInfoWidgets[i]; if (sciw->isValid()) { if (sciw->getSceneIndex() == sceneIndex) { sciw->setBackgroundForSelected(true); sceneIndexValid = true; m_selectedSceneClassInfoIndex = i; /* * Ensure that the selected scene remains visible * and do not alter value of horiztonal scroll bar */ const int horizValue = m_sceneSelectionScrollArea->horizontalScrollBar()->value(); const int xMargin = 0; const int yMargin = 50; m_sceneSelectionScrollArea->ensureWidgetVisible(sciw, xMargin, yMargin); m_sceneSelectionScrollArea->horizontalScrollBar()->setSliderPosition(horizValue); } else { sciw->setBackgroundForSelected(false); } } } if ( ! sceneIndexValid) { m_selectedSceneClassInfoIndex = -1; } enableSceneMoveUpAndDownButtons(); } /** * Highlight the given scene. * * @param scene * Scene to highlight. */ void SceneDialog::highlightScene(const Scene* scene) { const int32_t numberOfSceneInfoWidgets = static_cast(m_sceneClassInfoWidgets.size()); for (int32_t i = 0; i < numberOfSceneInfoWidgets; i++) { SceneClassInfoWidget* sciw = m_sceneClassInfoWidgets[i]; if (sciw->isValid()) { if (sciw->getScene() == scene) { highlightSceneAtIndex(sciw->getSceneIndex()); return; } } } } /** * Called to create a new scene file. */ void SceneDialog::newSceneFileButtonClicked() { /* * 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()); /* * If user cancels, delete the new scene file and return */ if (newSceneFileName.isEmpty()) { delete newSceneFile; return; } /* * Set name of new scene file and add to brain */ newSceneFile->setFileName(newSceneFileName); EventManager::get()->sendEvent(EventDataFileAdd(newSceneFile).getPointer()); this->loadSceneFileComboBox(newSceneFile); this->sceneFileSelected(); } /** * Called when upload scene file is selected. */ void SceneDialog::uploadSceneFileButtonClicked() { 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."); 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); return; } BalsaDatabaseUploadSceneFileDialog uploadDialog(sceneFile, this); uploadDialog.exec(); } /** * Called when zip scene file is selected. */ void SceneDialog::zipSceneFileButtonClicked() { SceneFile* sceneFile = getSelectedSceneFile(); if (sceneFile == NULL) { WuQMessageBox::errorOk(m_zipSceneFilePushButton, "No Scene File is selected."); return; } if (sceneFile->isModified()) { WuQMessageBox::errorOk(m_zipSceneFilePushButton, "Selected Scene File is modified and must be saved prior to zipping."); return; } ZipSceneFileDialog zipDialog(sceneFile, this); zipDialog.exec(); } /** * Called when a scene file is selected. */ void SceneDialog::sceneFileSelected() { loadScenesIntoDialog(NULL); loadSceneFileMetaDataWidgets(); } /** * Called when Edit Study ID button is clicked. */ void SceneDialog::editFileBalsaStudyIDButtonClicked() { SceneFile* sceneFile = getSelectedSceneFile(); if (sceneFile == NULL) { return; } WuQDataEntryDialog ded("Edit BALSA Database Info", m_editBalsaStudyIDPushButton, WuQDialog::SCROLL_AREA_AS_NEEDED); 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(); } } /** * Called when upload scene file is selected. */ void SceneDialog::editBaseDirectoryPushButtonClicked() { SceneFile* sceneFile = getSelectedSceneFile(); if (sceneFile == NULL) { return; } WuQDataEntryDialog ded("Edit Base Directory", m_editBaseDirectoryPushButton, WuQDialog::SCROLL_AREA_AS_NEEDED); 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(); } } /** * Called when upload scene file is selected. */ void SceneDialog::browseBaseDirectoryPushButtonClicked() { SceneFile* sceneFile = getSelectedSceneFile(); if (sceneFile == NULL) { return; } /* * Let user choose directory path */ QString directoryName; FileInformation fileInfo(sceneFile->getBaseDirectory()); if (fileInfo.exists()) { if (fileInfo.isDirectory()) { directoryName = fileInfo.getAbsoluteFilePath(); } } AString newDirectoryName = CaretFileDialog::getExistingDirectoryDialog(m_browseBaseDirectoryPushButton, "Choose Base Directory", directoryName); /* * If user cancels, return */ if (newDirectoryName.isEmpty()) { return; } /* * Set name of new scene file and add to brain */ sceneFile->setBaseDirectory(newDirectoryName); loadSceneFileMetaDataWidgets(); } /** * Called when add new scene button clicked. */ void SceneDialog::addNewSceneButtonClicked() { if ( ! checkForModifiedFiles(true)) { return; } SceneFile* sceneFile = getSelectedSceneFile(); if (sceneFile != NULL) { Scene* newScene = SceneCreateReplaceDialog::createNewScene(m_addNewScenePushButton, sceneFile); if (newScene != NULL) { s_informUserAboutScenesOnExitFlag = false; } loadScenesIntoDialog(newScene); } } /** * Called when insert new scene button clicked. */ void SceneDialog::insertSceneButtonClicked() { if ( ! checkForModifiedFiles(true)) { return; } SceneFile* sceneFile = getSelectedSceneFile(); if (sceneFile != NULL) { Scene* scene = getSelectedScene(); if (scene != NULL) { Scene* newScene = SceneCreateReplaceDialog::createNewSceneInsertBeforeScene(m_insertNewScenePushButton, sceneFile, scene); if (newScene != NULL) { s_informUserAboutScenesOnExitFlag = false; } loadScenesIntoDialog(newScene); } } } /** * Move the selected scene up. */ void SceneDialog::moveSceneUpButtonClicked() { SceneFile* sceneFile = getSelectedSceneFile(); if (sceneFile != NULL) { Scene* scene = getSelectedScene(); if (scene != NULL) { sceneFile->moveScene(scene, -1); loadScenesIntoDialog(scene); } } } /** * Move the selected scene down. */ void SceneDialog::moveSceneDownButtonClicked() { SceneFile* sceneFile = getSelectedSceneFile(); if (sceneFile != NULL) { Scene* scene = getSelectedScene(); if (scene != NULL) { sceneFile->moveScene(scene, 1); loadScenesIntoDialog(scene); } } } /** * Called when replace scene button clicked. */ void SceneDialog::replaceSceneButtonClicked() { if ( ! checkForModifiedFiles(true)) { return; } SceneFile* sceneFile = getSelectedSceneFile(); if (sceneFile != NULL) { Scene* scene = getSelectedScene(); if (scene != NULL) { Scene* newScene = SceneCreateReplaceDialog::replaceExistingScene(m_addNewScenePushButton, sceneFile, scene); if (newScene != NULL) { s_informUserAboutScenesOnExitFlag = false; } 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()); } } /* * Free memory from the image files. */ for (std::vector::iterator iter = imageFiles.begin(); iter != imageFiles.end(); iter++) { delete *iter; } } /** * Check to see if there are modified files. If there are * allow the user to continue or cancel creation of the scene. * * param creatingSceneFlag * When true scene is being created, else a scene is being shown * @return * true if the scene should be created, otherwise false. */ bool SceneDialog::checkForModifiedFiles(const bool creatingSceneFlag) { 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, msg)) { return false; } } } 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); bool result = true; if (haveModifiedFilesFlag) { QMessageBox warningDialog(QMessageBox::Warning, "Warning", dialogMessage, QMessageBox::NoButton, this); warningDialog.setInformativeText(modifiedFilesMessage); QPushButton* yesButton = warningDialog.addButton("Yes", QMessageBox::AcceptRole); QPushButton* noButton = warningDialog.addButton("No", QMessageBox::RejectRole); warningDialog.setDefaultButton(noButton); warningDialog.setEscapeButton(noButton); warningDialog.exec(); const QAbstractButton* clickedButton = warningDialog.clickedButton(); if (clickedButton == yesButton) { result = true; } else if (clickedButton == noButton) { result = false; } else { CaretAssert(0); } } return result; } /** * Create the main page. * @return the main page. */ QWidget* SceneDialog::createMainPage() { QWidget* sceneFileWidget = createSceneFileWidget(); /* * Scene controls */ QLabel* sceneLabel = new QLabel("Scenes"); /* * Add new scene button */ m_addNewScenePushButton = new QPushButton("Add..."); m_addNewScenePushButton->setToolTip("Add a new scene to the END of the list"); QObject::connect(m_addNewScenePushButton, SIGNAL(clicked()), this, SLOT(addNewSceneButtonClicked())); /* * Delete new scene button */ m_deleteScenePushButton = new QPushButton("Delete..."); m_deleteScenePushButton->setToolTip("Delete the selected scene"); QObject::connect(m_deleteScenePushButton, SIGNAL(clicked()), this, SLOT(deleteSceneButtonClicked())); /* * Move scene up button */ m_moveSceneUpPushButton = new QPushButton("Move Up"); m_moveSceneUpPushButton->setToolTip("Move the selected scene up one position"); QObject::connect(m_moveSceneUpPushButton, SIGNAL(clicked()), this, SLOT(moveSceneUpButtonClicked())); /* * Move scene down button */ m_moveSceneDownPushButton = new QPushButton("Move Down"); m_moveSceneDownPushButton->setToolTip("Move the selected scene down one position"); QObject::connect(m_moveSceneDownPushButton, SIGNAL(clicked()), this, SLOT(moveSceneDownButtonClicked())); /* * Insert scene button */ m_insertNewScenePushButton = new QPushButton("Insert..."); m_insertNewScenePushButton->setToolTip("Insert a new scene ABOVE the selected scene in the list"); QObject::connect(m_insertNewScenePushButton, SIGNAL(clicked()), this, SLOT(insertSceneButtonClicked())); /* * Replace scene button */ m_replaceScenePushButton = new QPushButton("Replace..."); m_replaceScenePushButton->setToolTip("Replace the selected scene"); QObject::connect(m_replaceScenePushButton, SIGNAL(clicked()), this, SLOT(replaceSceneButtonClicked())); /* * Show new scene button */ m_showScenePushButton = new QPushButton("Show"); m_showScenePushButton->setToolTip("Show the selected scene"); QObject::connect(m_showScenePushButton, SIGNAL(clicked()), this, SLOT(showSceneButtonClicked())); /* * Show scene image button */ m_showSceneImagePreviewPushButton = new QPushButton("Preview..."); m_showSceneImagePreviewPushButton->setToolTip("Show larger image and full description\n" "for the selected scene"); QObject::connect(m_showSceneImagePreviewPushButton, SIGNAL(clicked()), this, SLOT(showImagePreviewButtonClicked())); /* * Layout for scene buttons */ QVBoxLayout* sceneButtonLayout = new QVBoxLayout(); sceneButtonLayout->addWidget(m_showScenePushButton); sceneButtonLayout->addWidget(m_showSceneImagePreviewPushButton); 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->addSpacing(20); sceneButtonLayout->addWidget(m_deleteScenePushButton); /* * Widget and layout containing the scene class info. */ m_sceneSelectionLayout = new QVBoxLayout(); m_sceneSelectionWidget = new QWidget(); m_sceneSelectionWidget->setSizePolicy(m_sceneSelectionWidget->sizePolicy().horizontalPolicy(), QSizePolicy::Fixed); QVBoxLayout* sceneSelectionWidgetLayout = new QVBoxLayout(m_sceneSelectionWidget); sceneSelectionWidgetLayout->addLayout(m_sceneSelectionLayout); sceneSelectionWidgetLayout->addStretch(); m_sceneSelectionScrollArea = new QScrollArea(); m_sceneSelectionScrollArea->setHorizontalScrollBarPolicy(Qt::ScrollBarAsNeeded); m_sceneSelectionScrollArea->setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded); 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 */ QWidget* widget = new QWidget(); QGridLayout* gridLayout = new QGridLayout(widget); gridLayout->setColumnStretch(0, 0); gridLayout->setColumnStretch(1, 100); gridLayout->setColumnStretch(2, 0); gridLayout->setSpacing(6); WuQtUtilities::setLayoutMargins(gridLayout, 0); int row = 0; gridLayout->addWidget(sceneFileWidget, row, 0, 1, 3); row++; gridLayout->addWidget(WuQtUtilities::createHorizontalLineWidget(), row, 0, 1, 3); row++; gridLayout->addWidget(sceneLabel, row, 0, (Qt::AlignTop | Qt::AlignRight)); gridLayout->addWidget(m_sceneSelectionScrollArea, row, 1); gridLayout->addLayout(sceneButtonLayout, row, 2); 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; } /** * @return The Scene File Widget. */ QWidget* SceneDialog::createSceneFileWidget() { /* * Scene File Controls */ QLabel* sceneFileLabel = new QLabel("Scene File"); m_sceneFileSelectionComboBox = new QComboBox(); m_sceneFileSelectionComboBox->setSizeAdjustPolicy(QComboBox::AdjustToMinimumContentsLength); m_sceneFileSelectionComboBox->setSizePolicy(QSizePolicy::MinimumExpanding, m_sceneFileSelectionComboBox->sizePolicy().verticalPolicy()); WuQtUtilities::setToolTipAndStatusTip(m_sceneFileSelectionComboBox, "Selects an existing scene file\n" "to which new scenes are added."); QObject::connect(m_sceneFileSelectionComboBox, SIGNAL(activated(int)), this, SLOT(sceneFileSelected())); /* * New File button */ QPushButton* newSceneFilePushButton = new QPushButton("New..."); newSceneFilePushButton->setToolTip("Create a new scene file"); QObject::connect(newSceneFilePushButton, SIGNAL(clicked()), this, SLOT(newSceneFileButtonClicked())); /* * Upload button */ m_uploadSceneFilePushButton = new QPushButton("Upload..."); m_uploadSceneFilePushButton->setToolTip("Upload the selected scene file to the BALSA Database"); QObject::connect(m_uploadSceneFilePushButton, SIGNAL(clicked()), this, SLOT(uploadSceneFileButtonClicked())); /* * Zip button */ m_zipSceneFilePushButton = new QPushButton("Zip..."); m_zipSceneFilePushButton->setToolTip("Zip the selected scene file"); 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_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 */ QWidget* widget = new QWidget(); QGridLayout* gridLayout = new QGridLayout(widget); QMargins gridMargins = gridLayout->contentsMargins(); gridMargins.setBottom(0); gridMargins.setTop(3); gridLayout->setContentsMargins(gridMargins); gridLayout->setSpacing(6); gridLayout->setColumnStretch(0, 0); gridLayout->setColumnStretch(1, 100); gridLayout->setColumnStretch(2, 0); gridLayout->setColumnStretch(3, 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); 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); 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 * Scene creation dialog. */ void SceneDialog::validateContentOfCreateSceneDialog(WuQDataEntryDialog* sceneCreateDialog) { CaretAssert(sceneCreateDialog); bool dataValid = true; QString errorMessage = ""; QWidget* sceneNameWidget = sceneCreateDialog->getDataWidgetWithName("sceneNameLineEdit"); if (sceneNameWidget != NULL) { QLineEdit* sceneNameLineEdit = qobject_cast(sceneNameWidget); const AString sceneName = sceneNameLineEdit->text().trimmed(); if (sceneName.isEmpty()) { dataValid = false; if (errorMessage.isEmpty() == false) { errorMessage += "\n"; } errorMessage += ("Scene Name is empty."); } else { SceneFile* sceneFile = getSelectedSceneFile(); if (sceneFile != NULL) { const int32_t numScenes = sceneFile->getNumberOfScenes(); for (int32_t i = 0; i < numScenes; i++) { Scene* scene = sceneFile->getSceneAtIndex(i); if (scene->getName() == sceneName) { dataValid = false; if (errorMessage.isEmpty() == false) { errorMessage += "\n"; } errorMessage += ("An existing scene uses the name \"" + sceneName + "\". Scene names must be unique."); break; } } } } } sceneCreateDialog->setDataValid(dataValid, errorMessage); } /** * Slot that get called when a scene is highlighted (clicked). * * @param sceneIndex * Index of the scene. */ void SceneDialog::sceneHighlighted(const int32_t sceneIndex) { highlightSceneAtIndex(sceneIndex); } /** * Slot that get called when a scene is activated (double clicked). * * @param sceneIndex * Index of the scene. */ void SceneDialog::sceneActivated(const int32_t sceneIndex) { highlightSceneAtIndex(sceneIndex); showSceneButtonClicked(); } /** * Called when delete scene button clicked. */ void SceneDialog::deleteSceneButtonClicked() { Scene* scene = getSelectedScene(); if (scene != NULL) { const AString sceneName = scene->getName(); const AString msg = ("Are you sure you want to delete scene named: " + sceneName); if (WuQMessageBox::warningYesNo(m_deleteScenePushButton, msg)) { SceneFile* sceneFile = getSelectedSceneFile(); sceneFile->removeScene(scene); updateDialog(); } } } /** * Called when show scene button clicked. */ 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; // } AString sceneFileName; SceneFile* sceneFile = getSelectedSceneFile(); if (sceneFile != NULL) { sceneFileName = sceneFile->getFileName(); } Scene* scene = getSelectedScene(); 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."); AString username; AString password; if (UsernamePasswordWidget::getUserNameAndPasswordInDialog(this, "Username and Password", msg, username, password)) { CaretDataFile::setFileReadingUsernameAndPassword(username, password); } else { return; } } ProgressReportingDialog progressDialog(("Restoring Scene " + scene->getName()), "", this); progressDialog.setValue(0); displayScenePrivate(sceneFile, scene, false); } } /** * Called when show image preview button is clicked. */ void SceneDialog::showImagePreviewButtonClicked() { const Scene* scene = getSelectedScene(); if (scene != NULL) { QByteArray imageByteArray; AString imageBytesFormat; scene->getSceneInfo()->getImageBytes(imageByteArray, imageBytesFormat); bool useNonModalDialogFlag = true; if (useNonModalDialogFlag) { /* * Scene preview dialog will be deleted when user closes it */ ScenePreviewDialog* spd = new ScenePreviewDialog(scene, m_showSceneImagePreviewPushButton); spd->show(); } else { WuQDataEntryDialog ded(scene->getName(), m_showSceneImagePreviewPushButton, WuQDialog::SCROLL_AREA_AS_NEEDED); ded.setCancelButtonText(""); ded.setOkButtonText("Close"); try { if (imageByteArray.length() > 0) { ImageFile imageFile; imageFile.setImageFromByteArray(imageByteArray, imageBytesFormat); const QImage* image = imageFile.getAsQImage(); if (image != NULL) { if (image->isNull()) { CaretLogSevere("Preview image is invalid (isNull)"); } else { QLabel* imageLabel = new QLabel(); imageLabel->setPixmap(QPixmap::fromImage(*image)); ded.addWidget("", imageLabel); } } } } catch (const DataFileException& dfe) { CaretLogSevere("Converting preview to image: " + dfe.whatString()); } AString nameText; AString sceneIdText; AString descriptionText; const int32_t negativeIsUnlimitedNumberOfLines = -1; SceneClassInfoWidget::getFormattedTextForSceneNameAndDescription(scene->getSceneInfo(), nameText, sceneIdText, descriptionText, negativeIsUnlimitedNumberOfLines); QLabel* nameLabel = new QLabel(nameText); ded.addWidget("", nameLabel); QLabel* sceneIdLabel = new QLabel(sceneIdText); ded.addWidget("", sceneIdLabel); if (! descriptionText.isEmpty()) { QLabel* descriptionLabel = new QLabel(descriptionText); descriptionLabel->setWordWrap(true); ded.addWidget("", descriptionLabel); } ded.exec(); } } } /** * Gets called when the use scene colors checkbox is clicked. * * @param checked * New checked status. */ void SceneDialog::useSceneColorsCheckBoxClicked(bool checked) { if ( ! checked) { CaretPreferences* prefs = SessionManager::get()->getCaretPreferences(); prefs->setBackgroundAndForegroundColorsMode(BackgroundAndForegroundColorsModeEnum::USER_PREFERENCES); EventManager::get()->sendEvent(EventGraphicsUpdateAllWindows().getPointer()); } } /** * Display the given scene from the given scene file. * @param sceneFile * Scene file. * @param scene * Scene that is displayed. * @return * true if scene was displayed without error, else false. */ bool SceneDialog::displayScene(SceneFile* sceneFile, Scene* scene) { const bool isSuccessful = displayScenePrivate(sceneFile, scene, true); loadSceneFileComboBox(sceneFile); loadScenesIntoDialog(scene); return isSuccessful; } /** * Display the given scene from the given scene file. * @param sceneFile * Scene file. * @param scene * Scene that is displayed. * @return * true if scene was displayed without error, else false. */ bool SceneDialog::displayScenePrivate(SceneFile* sceneFile, Scene* scene, const bool showWaitCursor) { CaretAssert(sceneFile); CaretAssert(scene); 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()); return false; } /* * Show the wait cursor */ CursorDisplayScoped cursor; if (showWaitCursor) { cursor.showWaitCursor(); } /* * Setup scene attributes */ SceneAttributes* sceneAttributes = scene->getAttributes(); sceneAttributes->clearErrorMessage(); sceneAttributes->setSceneFileName(sceneFileName); sceneAttributes->setSceneName(scene->getName()); sceneAttributes->setWindowRestoreBehaviorInSceneDisplay(SceneAttributes::RESTORE_WINDOW_POSITION_RELATIVE_TO_FIRST_AND_USE_SIZES); sceneAttributes->setUseSceneForegroundAndBackgroundColors(m_useSceneColorsCheckBox->isChecked()); GuiManager::get()->restoreFromScene(sceneAttributes, guiManagerClass); cursor.restoreCursor(); const AString sceneErrorMessage = sceneAttributes->getErrorMessage(); if (sceneErrorMessage.isEmpty()) { /* * Add to recent scene files but only if the scene * file is NOT modified. * * It is possible for the user to create a new scene in * a new scene file and never write the scene file. * So, we don't want a non-existent scene file in the * recent scene file's menu. */ if ( ! sceneFile->isModified()) { CaretPreferences* prefs = SessionManager::get()->getCaretPreferences(); prefs->addToPreviousSceneFiles(sceneFile->getFileName()); } } else { WuQMessageBox::errorOk(this, sceneErrorMessage); return false; } return true; } /** * Receive events from the event manager. * * @param event * Event sent by event manager. */ void SceneDialog::receiveEvent(Event* event) { SceneFile* lastSceneFileRead = NULL; if (event->getEventType() == EventTypeEnum::EVENT_USER_INTERFACE_UPDATE) { EventUserInterfaceUpdate* uiEvent = dynamic_cast(event); CaretAssert(uiEvent); updateDialog(); uiEvent->setEventProcessed(); } else if (event->getEventType() == EventTypeEnum::EVENT_DATA_FILE_READ) { EventDataFileRead* readEvent = dynamic_cast(event); CaretAssert(readEvent); /* * Determine if a scene file was read */ const int32_t numFilesRead = readEvent->getNumberOfDataFilesToRead(); for (int32_t i = (numFilesRead - 1); i >= 0; i--) { if ( ! readEvent->isFileError(i)) { if (readEvent->getDataFileType(i) == DataFileTypeEnum::SCENE) { CaretDataFile* dataFileRead = readEvent->getDataFileRead(i); lastSceneFileRead = dynamic_cast(dataFileRead); if (lastSceneFileRead != NULL) { break; } } } } } else if (event->getEventType() == EventTypeEnum::EVENT_DATA_FILE_RELOAD) { EventDataFileReload* reloadEvent = dynamic_cast(event); CaretAssert(reloadEvent); if ( ! reloadEvent->isError()) { CaretDataFile* dataFileRead = reloadEvent->getCaretDataFile(); lastSceneFileRead = dynamic_cast(dataFileRead); } } /* * If a scene file was read, make it the selected scene file * in this dialog. */ if (lastSceneFileRead != NULL) { loadSceneFileComboBox(lastSceneFileRead); loadScenesIntoDialog(NULL); highlightSceneAtIndex(0); m_sceneSelectionScrollArea->horizontalScrollBar()->setSliderPosition(0); } } /** * @return True if user should be informed about * creating scenes when exiting wb_view (user * never created a scene). */ bool SceneDialog::isInformUserAboutScenesOnExit() { return s_informUserAboutScenesOnExitFlag; } /* ======================================================================== */ /** * \class caret::SceneClassWidget * \brief Dialog for manipulation of scenes. * \ingroup GuiQt */ /** * Constructor. */ SceneClassInfoWidget::SceneClassInfoWidget() : QGroupBox(0) { m_scene = NULL; m_sceneIndex = -1; m_defaultBackgroundRole = backgroundRole(); m_defaultAutoFillBackgroundStatus = autoFillBackground(); m_nameLabel = new QLabel(); m_descriptionLabel = new QLabel(); m_descriptionLabel->setWordWrap(true); m_sceneIdLabel = new QLabel(); m_previewImageLabel = new QLabel(); m_previewImageLabel->setContentsMargins(0, 0, 0, 0); m_rightSideWidget = new QWidget(); QVBoxLayout* rightLayout = new QVBoxLayout(m_rightSideWidget); rightLayout->setContentsMargins(0, 0, 0, 0); rightLayout->setSpacing(3); rightLayout->addWidget(m_nameLabel); rightLayout->addWidget(m_sceneIdLabel); rightLayout->addWidget(m_descriptionLabel); rightLayout->addStretch(); m_leftSideWidget = new QWidget(); QVBoxLayout* leftLayout = new QVBoxLayout(m_leftSideWidget); leftLayout->setContentsMargins(0, 0, 0, 0); leftLayout->setSpacing(0); leftLayout->addWidget(m_previewImageLabel); leftLayout->addStretch(); QHBoxLayout* layout = new QHBoxLayout(this); layout->setContentsMargins(0, 3, 0, 0); layout->setSpacing(3); layout->addWidget(m_leftSideWidget); layout->addWidget(m_rightSideWidget, 100); setSizePolicy(sizePolicy().horizontalPolicy(), QSizePolicy::Fixed); } /** * Destructor. */ SceneClassInfoWidget::~SceneClassInfoWidget() { } /** * Set/reset the background so that the widget appears to be * selected/unselected. * * @param selected * Selection status. */ void SceneClassInfoWidget::setBackgroundForSelected(const bool selected) { if (selected) { setAutoFillBackground(true); setBackgroundRole(QPalette::Highlight); } else { setAutoFillBackground(m_defaultAutoFillBackgroundStatus); setBackgroundRole(m_defaultBackgroundRole); } } /** * Update the content. * * @param scene * Scene for display. * @param sceneIndex * Index of the scene. */ void SceneClassInfoWidget::updateContent(Scene* scene, const int32_t sceneIndex) { m_scene = scene; m_sceneIndex = sceneIndex; if ((m_scene != NULL) && (m_sceneIndex >= 0)) { AString nameText; AString sceneIdText; AString descriptionText; const int32_t numLinesToDisplay = 9; SceneClassInfoWidget::getFormattedTextForSceneNameAndDescription(scene->getSceneInfo(), nameText, sceneIdText, descriptionText, numLinesToDisplay); m_nameLabel->setText(nameText); m_sceneIdLabel->setText(sceneIdText); m_descriptionLabel->setText(descriptionText); QByteArray imageByteArray; AString imageBytesFormat; scene->getSceneInfo()->getImageBytes(imageByteArray, imageBytesFormat); const int previewImageWidth = 192; //128; QImage previewImage; bool previewImageValid = false; if (imageByteArray.length() > 0) { try { ImageFile imageFile; imageFile.setImageFromByteArray(imageByteArray, imageBytesFormat); imageFile.resizeToWidth(previewImageWidth); previewImage = *imageFile.getAsQImage(); previewImageValid = true; } catch (const DataFileException& dfe) { CaretLogSevere(dfe.whatString()); } } m_previewImageLabel->clear(); m_previewImageLabel->setAlignment(Qt::AlignHCenter | Qt::AlignTop); if (previewImageValid) { m_previewImageLabel->setPixmap(QPixmap::fromImage(previewImage)); } 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); } } /** * Examine the given string for newlines and remove * any lines that exceed the maximum allowed. * * @param textLines * String containing lines of text separated by a newline character. * @param maximumNumberOfLines * Maximum number of lines for the text. */ void SceneClassInfoWidget::limitToNumberOfLines(AString& textLines, const int32_t maximumNumberOfLines) { if (textLines.isEmpty()) { return; } const QString lineSeparator("\n"); QStringList descriptionLines = textLines.split(lineSeparator, QString::KeepEmptyParts); const int32_t numLines = descriptionLines.size(); const int32_t numLinesToRemove = numLines - maximumNumberOfLines; if (numLinesToRemove > 0) { for (int32_t i = 0; i < numLinesToRemove; i++) { descriptionLines.pop_back(); } } textLines = descriptionLines.join(lineSeparator); } /** * Get formatted text for display of scene name and description. * * @param sceneInfo * Info for the scene. * @param nameTextOut * Text for name. * @param sceneIdTextOut * Text for scene ID. * @param desciptionTextOut * Text for description. * @param maximumLinesInDescription * Maximum number of lines allowed in description. * If value is negative, an unlimited number of lines are allowed. */ void SceneClassInfoWidget::getFormattedTextForSceneNameAndDescription(const SceneInfo* sceneInfo, AString& nameTextOut, AString& sceneIdTextOut, AString& descriptionTextOut, const int32_t maximumLinesInDescription) { CaretAssert(sceneInfo); AString name = sceneInfo->getName(); if (name.isEmpty()) { name = "NAME IS MISSING !!!"; } nameTextOut = ("NAME: " + name + ""); sceneIdTextOut = ("BALSA SCENE ID: " + sceneInfo->getBalsaSceneID() + ""); AString description = sceneInfo->getDescription(); if (maximumLinesInDescription > 0) { SceneClassInfoWidget::limitToNumberOfLines(description, maximumLinesInDescription); } if ( ! description.isEmpty()) { /* * HTML formatting is needed so text is properly displayed. * Want to put "Description" at beginning in bold but any * HTML tags are converted to text. So, after conversion to * HTML, perform a replace to insert "Description" in bold. */ const AString replaceWithDescriptionBoldText = "REPLACE_WITH_DESCRIPTION"; description = WuQtUtilities::createWordWrappedToolTipText(replaceWithDescriptionBoldText + description); description.replace(replaceWithDescriptionBoldText, "DESCRIPTION: "); } descriptionTextOut = description; } /** * Called by Qt when the mouse is pressed. * * @param event * The mouse event information. */ void SceneClassInfoWidget::mousePressEvent(QMouseEvent* event) { emit highlighted(m_sceneIndex); event->setAccepted(true); } /** * Called when the mouse is double clicked. * * @param event * The mouse event information. */ void SceneClassInfoWidget::mouseDoubleClickEvent(QMouseEvent* event) { emit activated(m_sceneIndex); event->setAccepted(true); } /** * @return The scene. */ Scene* SceneClassInfoWidget::getScene() { return m_scene; } /** * @return The index of the scene. */ int32_t SceneClassInfoWidget::getSceneIndex() const { return m_sceneIndex; } /** * @return True if this scene class info widget is valid (has * a scene with a valid index). */ bool SceneClassInfoWidget::isValid() const { if ((m_scene != NULL) && (m_sceneIndex >= 0)) { return true; } return false; } connectome-workbench-1.2.3+git41-gc4c6c90/src/GuiQt/SceneDialog.h000066400000000000000000000170601300200146000241270ustar00rootroot00000000000000#ifndef __SCENE_DIALOG__H_ #define __SCENE_DIALOG__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 "EventListenerInterface.h" #include "WuQDialogNonModal.h" class QCheckBox; class QComboBox; class QLabel; class QLineEdit; class QPushButton; class QScrollArea; class QVBoxLayout; namespace caret { class Scene; class SceneClassInfoWidget; class SceneFile; class SceneInfo; class WuQDataEntryDialog; class WuQWidgetObjectGroup; class SceneDialog : public WuQDialogNonModal, public EventListenerInterface { Q_OBJECT public: SceneDialog(QWidget* parent = 0); virtual ~SceneDialog(); void updateDialog(); void receiveEvent(Event* event); bool displayScene(SceneFile* sceneFile, Scene* scene); static bool isInformUserAboutScenesOnExit(); private: SceneDialog(const SceneDialog&); SceneDialog& operator=(const SceneDialog&); private slots: void sceneFileSelected(); void newSceneFileButtonClicked(); void uploadSceneFileButtonClicked(); void zipSceneFileButtonClicked(); void addNewSceneButtonClicked(); void deleteSceneButtonClicked(); void insertSceneButtonClicked(); void moveSceneUpButtonClicked(); void moveSceneDownButtonClicked(); void replaceSceneButtonClicked(); void showSceneButtonClicked(); void showImagePreviewButtonClicked(); void validateContentOfCreateSceneDialog(WuQDataEntryDialog*); void sceneHighlighted(const int32_t sceneIndex); void sceneActivated(const int32_t sceneIndex); void useSceneColorsCheckBoxClicked(bool checked); void editFileBalsaStudyIDButtonClicked(); void editBaseDirectoryPushButtonClicked(); void browseBaseDirectoryPushButtonClicked(); public: // ADD_NEW_METHODS_HERE private: SceneFile* getSelectedSceneFile(); Scene* getSelectedScene(); void loadSceneFileComboBox(SceneFile* selectedSceneFileIn); void loadScenesIntoDialog(Scene* selectedSceneIn); void addImageToScene(Scene* scene); void highlightSceneAtIndex(const int32_t sceneIndex); void highlightScene(const Scene* scene); QWidget* createMainPage(); QWidget* createShowOptionsWidget(); QWidget* createSceneFileWidget(); bool displayScenePrivate(SceneFile* sceneFile, Scene* scene, const bool showWaitCursor); bool checkForModifiedFiles(const bool creatingSceneFlag); void enableSceneMoveUpAndDownButtons(); void loadSceneFileMetaDataWidgets(); // ADD_NEW_MEMBERS_HERE QComboBox* m_sceneFileSelectionComboBox; QPushButton* m_uploadSceneFilePushButton; QPushButton* m_zipSceneFilePushButton; QPushButton* m_addNewScenePushButton; QPushButton* m_insertNewScenePushButton; QPushButton* m_deleteScenePushButton; QPushButton* m_moveSceneUpPushButton; QPushButton* m_moveSceneDownPushButton; QPushButton* m_replaceScenePushButton; QPushButton* m_showScenePushButton; QPushButton* m_showSceneImagePreviewPushButton; QCheckBox* m_useSceneColorsCheckBox; QScrollArea* m_sceneSelectionScrollArea; QWidget* m_sceneSelectionWidget; QVBoxLayout* m_sceneSelectionLayout; std::vector m_sceneClassInfoWidgets; int32_t m_selectedSceneClassInfoIndex; QPushButton* m_editBalsaStudyIDPushButton; QLineEdit* m_fileBalsaStudyIDLineEdit; QPushButton* m_browseBaseDirectoryPushButton; QPushButton* m_editBaseDirectoryPushButton; QLineEdit* m_fileBaseDirectoryLineEdit; WuQWidgetObjectGroup* m_sceneFileButtonsGroup; static const AString PREFERRED_IMAGE_FORMAT; static bool s_informUserAboutScenesOnExitFlag; }; class SceneClassInfoWidget : public QGroupBox { Q_OBJECT public: SceneClassInfoWidget(); ~SceneClassInfoWidget(); void updateContent(Scene* scene, const int32_t sceneIndex); void setBackgroundForSelected(const bool selected); Scene* getScene(); int32_t getSceneIndex() const; bool isValid() const; static void getFormattedTextForSceneNameAndDescription(const SceneInfo* sceneInfo, AString& nameTextOut, AString& sceneIdTextOut, AString& descriptionTextOut, const int32_t maximumLinesInDescription); signals: /** * Emited when user activates (double clicks) this widget. */ void activated(const int32_t sceneIndex); /** * Emited when user highlights (clicks) this widget. */ void highlighted(const int32_t sceneIndex); protected: virtual void mousePressEvent(QMouseEvent* event); virtual void mouseDoubleClickEvent(QMouseEvent* event); private: static void limitToNumberOfLines(AString& textLines, const int32_t maximumNumberOfLines); QWidget* m_leftSideWidget; QWidget* m_rightSideWidget; QLabel* m_previewImageLabel; QLabel* m_nameLabel; QLabel* m_sceneIdLabel; QLabel* m_descriptionLabel; Scene* m_scene; int32_t m_sceneIndex; bool m_previewImageValid; bool m_defaultAutoFillBackgroundStatus; QPalette::ColorRole m_defaultBackgroundRole; }; #ifdef __SCENE_DIALOG_DECLARE__ const AString SceneDialog::PREFERRED_IMAGE_FORMAT = "jpg"; bool SceneDialog::s_informUserAboutScenesOnExitFlag = true; #endif // __SCENE_DIALOG_DECLARE__ } // namespace #endif //__SCENE_DIALOG__H_ connectome-workbench-1.2.3+git41-gc4c6c90/src/GuiQt/ScenePreviewDialog.cxx000066400000000000000000000101721300200146000260410ustar00rootroot00000000000000 /*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 __SCENE_PREVIEW_DIALOG_DECLARE__ #include "ScenePreviewDialog.h" #undef __SCENE_PREVIEW_DIALOG_DECLARE__ #include #include #include "CaretAssert.h" #include "CaretLogger.h" #include "DataFileException.h" #include "ImageFile.h" #include "Scene.h" #include "SceneDialog.h" #include "SceneInfo.h" using namespace caret; /** * \class caret::ScenePreviewDialog * \brief Non-modal dialog that shows preview image of a scene. * \ingroup GuiQt */ /** * Constructor. */ ScenePreviewDialog::ScenePreviewDialog(const Scene* scene, QWidget* parent) : WuQDialogNonModal(scene->getName(), parent) { /* * Dialog will be deleted when closed. */ setDeleteWhenClosed(true); setApplyButtonText(""); setCloseButtonText("Close"); QByteArray imageByteArray; AString imageBytesFormat; scene->getSceneInfo()->getImageBytes(imageByteArray, imageBytesFormat); QLabel* imageLabel = new QLabel(); bool imageValidFlag = false; try { if (imageByteArray.length() > 0) { ImageFile imageFile; imageFile.setImageFromByteArray(imageByteArray, imageBytesFormat); const QImage* image = imageFile.getAsQImage(); if (image != NULL) { if (image->isNull()) { CaretLogSevere("Preview image is invalid (isNull)"); } else { imageLabel->setPixmap(QPixmap::fromImage(*image)); imageValidFlag = true; } } } } catch (const DataFileException& dfe) { CaretLogSevere("Converting preview to image: " + dfe.whatString()); } if ( ! imageValidFlag) { imageLabel->setText("No image available"); } AString nameText; AString sceneIdText; AString descriptionText; const int32_t negativeIsUnlimitedNumberOfLines = -1; SceneClassInfoWidget::getFormattedTextForSceneNameAndDescription(scene->getSceneInfo(), nameText, sceneIdText, descriptionText, negativeIsUnlimitedNumberOfLines); QLabel* nameLabel = new QLabel(nameText); QLabel* sceneIdLabel = new QLabel(sceneIdText); QLabel* descriptionLabel = NULL; if (! descriptionText.isEmpty()) { descriptionLabel = new QLabel(descriptionText); descriptionLabel->setWordWrap(true); } QWidget* widget = new QWidget(); QVBoxLayout* layout = new QVBoxLayout(widget); layout->addWidget(imageLabel); layout->addWidget(nameLabel); layout->addWidget(sceneIdLabel); if (descriptionLabel != NULL) { layout->addWidget(descriptionLabel); } setCentralWidget(widget, SCROLL_AREA_AS_NEEDED); } /** * Destructor. */ ScenePreviewDialog::~ScenePreviewDialog() { } /** May be called requesting the dialog to update its content */ void ScenePreviewDialog::updateDialog() { /* nothing to update */ } connectome-workbench-1.2.3+git41-gc4c6c90/src/GuiQt/ScenePreviewDialog.h000066400000000000000000000033261300200146000254710ustar00rootroot00000000000000#ifndef __SCENE_PREVIEW_DIALOG_H__ #define __SCENE_PREVIEW_DIALOG_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 "WuQDialogNonModal.h" namespace caret { class Scene; class ScenePreviewDialog : public WuQDialogNonModal { Q_OBJECT public: ScenePreviewDialog(const Scene* scene, QWidget* parent); virtual ~ScenePreviewDialog(); /** May be called requesting the dialog to update its content */ virtual void updateDialog(); // ADD_NEW_METHODS_HERE private: ScenePreviewDialog(const ScenePreviewDialog&); ScenePreviewDialog& operator=(const ScenePreviewDialog&); // ADD_NEW_MEMBERS_HERE }; #ifdef __SCENE_PREVIEW_DIALOG_DECLARE__ // #endif // __SCENE_PREVIEW_DIALOG_DECLARE__ } // namespace #endif //__SCENE_PREVIEW_DIALOG_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/GuiQt/SceneWindowGeometry.cxx000066400000000000000000000256241300200146000262730ustar00rootroot00000000000000 /*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 #define __SCENE_WINDOW_GEOMETRY_DECLARE__ #include "SceneWindowGeometry.h" #undef __SCENE_WINDOW_GEOMETRY_DECLARE__ #include "BrainBrowserWindow.h" #include "SceneAttributes.h" #include "SceneClass.h" #include "WuQtUtilities.h" using namespace caret; /** * \class caret::SceneWindowGeometry * \brief Assists with saving and restoring a window's geometry * \ingroup GuiQt */ /** * Constructor for window that is child of the first browser window. * @param window * The window for which geometry is restored/saved */ SceneWindowGeometry::SceneWindowGeometry(QWidget* window) : CaretObject() { m_window = window; m_parentWindow = NULL; } /** * Constructor for window that is child of the given parent window. * The window is ALWAYS positioned relative to the parent. * @param window * The window for which geometry is restored/saved * @param parentWindow * The parent window of 'window'. */ SceneWindowGeometry::SceneWindowGeometry(QWidget* window, QWidget* parentWindow) : CaretObject() { m_window = window; m_parentWindow = parentWindow; } /** * Destructor. */ SceneWindowGeometry::~SceneWindowGeometry() { } /** * Set the coordinates of the first browser window invalid. * Normally called at beginning of scene restoration prior * to restoration of windows. */ void SceneWindowGeometry::setFirstBrowserWindowCoordinatesInvalid() { s_firstBrowserWindowCoordinatesValid = false; s_firstBrowserWindow = NULL; } /** * Create a scene for an instance of a class. * * @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. * * @return Pointer to SceneClass object representing the state of * this object. Under some circumstances a NULL pointer may be * returned. Caller will take ownership of returned object. */ SceneClass* SceneWindowGeometry::saveToScene(const SceneAttributes* /*sceneAttributes*/, const AString& instanceName) { SceneClass* sceneClass = new SceneClass(instanceName, "SceneWindowGeometry", 1); /* * Position and size */ sceneClass->addInteger("geometryX", m_window->x()); sceneClass->addInteger("geometryY", m_window->y()); sceneClass->addInteger("geometryWidth", m_window->width()); sceneClass->addInteger("geometryHeight", m_window->height()); int32_t geometryOffsetX = 0; int32_t geometryOffsetY = 0; if (m_parentWindow != NULL) { geometryOffsetX = m_window->x() - m_parentWindow->x(); geometryOffsetY = m_window->y() - m_parentWindow->y(); } sceneClass->addInteger("geometryOffsetX", geometryOffsetX); sceneClass->addInteger("geometryOffsetY", geometryOffsetY); sceneClass->addBoolean("visibility", m_window->isVisible()); return sceneClass; } /** * Restore the state of an instance of a class. * * @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 containing the state that was previously * saved and should be restored. */ void SceneWindowGeometry::restoreFromScene(const SceneAttributes* sceneAttributes, const SceneClass* sceneClass) { if (sceneClass == NULL) { return; } /* * Get window geometry from the scene */ const int32_t sceneX = sceneClass->getIntegerValue("geometryX", 100); const int32_t sceneY = sceneClass->getIntegerValue("geometryY", 100); const int32_t sceneWidth = sceneClass->getIntegerValue("geometryWidth", -1); const int32_t sceneHeight = sceneClass->getIntegerValue("geometryHeight", -1); const bool isDialog = (qobject_cast(m_window) != NULL); QDockWidget* dockWidget = qobject_cast(m_window); const bool isDockWidget = (dockWidget != NULL); const bool isWindow = (qobject_cast(m_window) != NULL); bool isWidget = true; if (isDialog || isDockWidget || isWindow) { isWidget = false; } /* * If it is a widget */ if (isWidget) { if ((sceneWidth > 0) && (sceneHeight > 0)) { m_window->resize(sceneWidth, sceneHeight); return; } } /* * If dock widget and not floating * just set size and exit */ if (isDockWidget && (dockWidget->isFloating() == false)) { if ((sceneWidth > 0) && (sceneHeight > 0)) { m_window->resize(sceneWidth, sceneHeight); return; } } /* * Is this window always positions relative to parent window? */ if (m_parentWindow != NULL) { /* * 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; /* * Move the window to its desired position and set its width/height * but limit to available screen space */ int32_t xywh[4]; WuQtUtilities::moveAndSizeWindow(m_window, windowX, windowY, sceneWidth, sceneHeight, xywh); return; } /* * Visibility */ const bool windowVisible = sceneClass->getBooleanValue("visibility", true); m_window->setVisible(windowVisible); /* * Is this a browser window? */ bool isFirstBrowserWindow = false; BrainBrowserWindow* browserWindow = dynamic_cast(m_window); if (browserWindow != NULL) { /* * Is this the first browser window being restored? * If so, save its positions from scene file and * its actual position. */ if (s_firstBrowserWindowCoordinatesValid == false) { isFirstBrowserWindow = true; } } /* * Determine how windows are positions and sized */ bool isResizeWindow = false; bool isMoveWindow = false; bool isMoveWindowRelative = false; switch (sceneAttributes->getRestoreWindowBehaviorInSceneDisplay()) { case SceneAttributes::RESTORE_WINDOW_USE_ALL_POSITIONS_AND_SIZES: isResizeWindow = true; isMoveWindow = true; break; case SceneAttributes::RESTORE_WINDOW_IGNORE_ALL_POSITIONS_AND_SIZES: break; case SceneAttributes::RESTORE_WINDOW_POSITION_RELATIVE_TO_FIRST_AND_USE_SIZES: if (isFirstBrowserWindow == false) { isMoveWindow = true; isMoveWindowRelative = true; } isResizeWindow = true; break; } // const QDesktopWidget* desktopWidget = QApplication::desktop(); // const QSize screenSize = WuQtUtilities::getMinimumScreenSize(); // const int maxX = screenSize.width() - 100; // const int maxY = screenSize.height() - 100; /* * Position of window defaults to window's current position */ int32_t windowX = m_window->x(); int32_t windowY = m_window->y(); if (isMoveWindow) { if ((sceneX > 0) & (sceneY > 0)) { /* * Use position in scene */ windowX = sceneX; windowY = sceneY; /* * Move position relative to first window? */ if (isMoveWindowRelative) { if ((s_firstBrowserWindowRestoredX > 0) && (s_firstBrowserWindowRestoredY > 0)) { const int32_t dx = windowX - s_firstBrowserWindowSceneX; const int32_t dy = windowY - s_firstBrowserWindowSceneY; windowX = s_firstBrowserWindowRestoredX + dx; windowY = s_firstBrowserWindowRestoredY + dy; } else { } } } } /* * NOT Resize window? */ int32_t windowWidth = sceneWidth; int32_t windowHeight = sceneHeight; if (isResizeWindow == false) { windowWidth = -1; windowHeight = -1; } /* * Move the window to its desired position and set its width/height * but limit to available screen space */ int32_t xywh[4]; WuQtUtilities::moveAndSizeWindow(m_window, windowX, windowY, windowWidth, windowHeight, xywh); /* * Is this a browser window? */ if (isFirstBrowserWindow) { s_firstBrowserWindowCoordinatesValid = true; s_firstBrowserWindow = browserWindow; s_firstBrowserWindowSceneX = sceneX; s_firstBrowserWindowSceneY = sceneY; s_firstBrowserWindowRestoredX = xywh[0]; s_firstBrowserWindowRestoredY = xywh[1]; } } connectome-workbench-1.2.3+git41-gc4c6c90/src/GuiQt/SceneWindowGeometry.h000066400000000000000000000066371300200146000257230ustar00rootroot00000000000000#ifndef __SCENE_WINDOW_GEOMETRY__H_ #define __SCENE_WINDOW_GEOMETRY__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 "SceneableInterface.h" class QWidget; namespace caret { class BrainBrowserWindow; class SceneAttributes; class SceneClass; class SceneWindowGeometry : public CaretObject, public SceneableInterface { public: SceneWindowGeometry(QWidget* window); SceneWindowGeometry(QWidget* window, QWidget* parentWindow); virtual ~SceneWindowGeometry(); virtual SceneClass* saveToScene(const SceneAttributes* sceneAttributes, const AString& instanceName); virtual void restoreFromScene(const SceneAttributes* sceneAttributes, const SceneClass* sceneClass); static void setFirstBrowserWindowCoordinatesInvalid(); private: SceneWindowGeometry(const SceneWindowGeometry&); SceneWindowGeometry& operator=(const SceneWindowGeometry&); /** The window whose geometry is restored/saved */ QWidget* m_window; /** Optional parent window (may be NULL) */ QWidget* m_parentWindow; /** X-coordinate of first browser window from the scene file */ static int32_t s_firstBrowserWindowSceneX; /** Y-coordinate of first browser window from the scene file */ static int32_t s_firstBrowserWindowSceneY; /** X-coordinate of first browser window after restoration from scene */ static int32_t s_firstBrowserWindowRestoredX; /** Y-coordinate of first browser window after restoration from scene */ static int32_t s_firstBrowserWindowRestoredY; /** First browser window */ static BrainBrowserWindow* s_firstBrowserWindow; /** Are first browser window coordinates valid */ static bool s_firstBrowserWindowCoordinatesValid; // ADD_NEW_MEMBERS_HERE }; #ifdef __SCENE_WINDOW_GEOMETRY_DECLARE__ BrainBrowserWindow* SceneWindowGeometry::s_firstBrowserWindow = NULL; int32_t SceneWindowGeometry::s_firstBrowserWindowSceneX = 0; int32_t SceneWindowGeometry::s_firstBrowserWindowSceneY = 0; int32_t SceneWindowGeometry::s_firstBrowserWindowRestoredX = 0; int32_t SceneWindowGeometry::s_firstBrowserWindowRestoredY = 0; bool SceneWindowGeometry::s_firstBrowserWindowCoordinatesValid = false; #endif // __SCENE_WINDOW_GEOMETRY_DECLARE__ } // namespace #endif //__SCENE_WINDOW_GEOMETRY__H_ connectome-workbench-1.2.3+git41-gc4c6c90/src/GuiQt/SpecFileManagementDialog.cxx000066400000000000000000003313211300200146000271330ustar00rootroot00000000000000 /*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 __SPEC_FILE_MANAGEMENT_DIALOG_DECLARE__ #include "SpecFileManagementDialog.h" #undef __SPEC_FILE_MANAGEMENT_DIALOG_DECLARE__ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "AnnotationFile.h" #include "Brain.h" #include "BrowserTabContent.h" #include "BrainBrowserWindow.h" #include "CaretAssert.h" #include "CaretDataFile.h" #include "CaretFileDialog.h" #include "CaretLogger.h" #include "CaretMappableDataFile.h" #include "CaretPreferences.h" #include "CursorDisplayScoped.h" #include "DataFileContentCopyMoveDialog.h" #include "DataFileContentCopyMoveInterface.h" #include "DataFileException.h" #include "EventBrowserTabGetAllViewed.h" #include "EventDataFileRead.h" #include "EventDataFileReload.h" #include "EventGetDisplayedDataFiles.h" #include "EventGraphicsUpdateAllWindows.h" #include "EventManager.h" #include "EventSpecFileReadDataFiles.h" #include "EventSurfaceColoringInvalidate.h" #include "EventUserInterfaceUpdate.h" #include "FileInformation.h" #include "GuiManager.h" #include "MetaDataEditorDialog.h" #include "ProgressReportingDialog.h" #include "SessionManager.h" #include "SpecFile.h" #include "SpecFileDataFile.h" #include "SpecFileDataFileTypeGroup.h" #include "UsernamePasswordWidget.h" #include "WuQImageLabel.h" #include "WuQMessageBox.h" #include "WuQWidgetObjectGroup.h" #include "WuQtUtilities.h" using namespace caret; /** * \class caret::SpecFileManagementDialog * \brief Dialog for operations with Spec Files. * \ingroup GuiQt */ /** * Run a dialog for opening a spec file. * * @param brain * Brain into which spec file is read. * @param specFile * Spec File. * @param parent * Parent of dialog. * @return * True if user loaded the spec file, else false. */ bool SpecFileManagementDialog::runOpenSpecFileDialog(Brain* brain, SpecFile* specFile, BrainBrowserWindow* parent) { CaretAssert(brain); CaretAssert(specFile); const AString title = ("Open Spec File: " + specFile->getFileNameNoPath()); SpecFileManagementDialog dialog(MODE_OPEN_SPEC_FILE, brain, specFile, title, parent); if (dialog.exec() == SpecFileManagementDialog::Accepted) { return true; } return false; } /** * Run a dialog for managing files in a brain. * * DO NOT delete the returned dialog as it will delete itself when closed. * * @param brain * Brain for which files are managed. * @param parent * Parent of dialog. */ void SpecFileManagementDialog::runManageFilesDialog(Brain* brain, BrainBrowserWindow* parent) { CaretAssert(brain); const AString title = ("Manage Data Files"); SpecFileManagementDialog dialog(MODE_MANAGE_FILES, brain, brain->getSpecFile(), title, parent); /* * Override view files type the first time the dialog is displayed * using the value from the user's preferences */ static bool firstTime = true; if (firstTime) { const CaretPreferences* prefs = SessionManager::get()->getCaretPreferences(); s_manageFilesViewFilesType = prefs->getManageFilesViewFileType(); firstTime = false; } dialog.setFilterSelections(s_manageFilesViewFilesType, s_manageFilesFilteredDataFileType, s_manageFilesFilteredStructureType); if ( ! s_manageFilesGeometry.isEmpty()) { dialog.restoreGeometry(s_manageFilesGeometry); } dialog.loadSpecFileContentIntoDialog(); dialog.exec(); dialog.getFilterSelections(s_manageFilesViewFilesType, s_manageFilesFilteredDataFileType, s_manageFilesFilteredStructureType); s_manageFilesGeometry = dialog.saveGeometry(); } /** * Run a dialog for saving files in a brain while exiting workbench. * * DO NOT delete the returned dialog as it will delete itself when closed. * * @param brain * Brain for which files are managed. * @param parent * Parent of dialog. * @return * true if workbench is allowed to exit, else false. */ bool SpecFileManagementDialog::runSaveFilesDialogWhileQuittingWorkbench(Brain* brain, BrainBrowserWindow* parent) { CaretAssert(brain); const AString title = ("Save Data Files"); SpecFileManagementDialog dialog(MODE_SAVE_FILES_WHILE_QUITTING, brain, brain->getSpecFile(), title, parent); if (dialog.exec()) { return true; } return false; } /** * Constructor. * * @param dialogMode * Mode of dialog. * @param brain * Brain * @param specFile * Spec File. * @param dialogTitle * Title for dialog. * @param parent * Parent of dialog. */ SpecFileManagementDialog::SpecFileManagementDialog(const Mode dialogMode, Brain* brain, SpecFile* specFile, const AString& dialogTitle, BrainBrowserWindow* parent) : WuQDialogModal(dialogTitle, parent), m_parentBrainBrowserWindow(parent), m_dialogMode(dialogMode), m_brain(brain), m_specFile(specFile) { CaretAssert(parent); /* * Initialize members */ m_filesTableWidget = NULL; m_fileSelectionActionGroup = NULL; m_manageFilesLoadedNotLoadedActionGroup = NULL; m_specFileDataFileCounter = 0; m_specFileTableRowIndex = -1; m_sceneAnnotationFileRowIndex = -1; m_fileSorting = SpecFileManagementDialogRowContent::SORTING_TYPE_STRUCTURE_NAME; /* * Load icons. */ m_iconOpenFile = WuQtUtilities::loadIcon(":/SpecFileDialog/load_icon.png"); m_iconOptions = WuQtUtilities::loadIcon(":/SpecFileDialog/options_icon.png"); m_iconReloadFile = WuQtUtilities::loadIcon(":/SpecFileDialog/reload_icon.png"); m_iconCloseFile = WuQtUtilities::loadIcon(":/SpecFileDialog/delete_icon.png"); /* * Open Spec File or Manage Files? */ bool enableManageItems = false; bool enableOpenItems = false; switch (m_dialogMode) { case SpecFileManagementDialog::MODE_MANAGE_FILES: case SpecFileManagementDialog::MODE_SAVE_FILES_WHILE_QUITTING: m_specFile->setModifiedFilesSelectedForSaving(); enableManageItems = true; break; case SpecFileManagementDialog::MODE_OPEN_SPEC_FILE: enableOpenItems = true; break; } /* * Signal mappers for buttons */ m_fileReloadOrOpenFileActionSignalMapper = new QSignalMapper(this); QObject::connect(m_fileReloadOrOpenFileActionSignalMapper, SIGNAL(mapped(int)), this, SLOT(fileReloadOrOpenFileActionSelected(int))); m_fileOptionsActionSignalMapper = new QSignalMapper(this); QObject::connect(m_fileOptionsActionSignalMapper, SIGNAL(mapped(int)), this, SLOT(fileOptionsActionSelected(int))); m_fileCloseFileActionSignalMapper = new QSignalMapper(this); QObject::connect(m_fileCloseFileActionSignalMapper, SIGNAL(mapped(int)), this, SLOT(fileRemoveActionSelected(int))); int tableRowCounter = 0; /* * Spec and scene annotations only when managing files */ if (enableManageItems) { m_specFileTableRowIndex = tableRowCounter++; m_sceneAnnotationFileRowIndex = tableRowCounter++; } bool testForDisplayedDataFiles = false; m_loadScenesPushButton = NULL; switch (m_dialogMode) { case SpecFileManagementDialog::MODE_MANAGE_FILES: setOkButtonText("Save Checked Files"); setCancelButtonText("Close"); testForDisplayedDataFiles = true; break; case SpecFileManagementDialog::MODE_OPEN_SPEC_FILE: setOkButtonText("Load"); setCancelButtonText("Cancel"); m_loadScenesPushButton = addUserPushButton("Load Scenes", QDialogButtonBox::AcceptRole); break; case SpecFileManagementDialog::MODE_SAVE_FILES_WHILE_QUITTING: setOkButtonText("Save Selected Files and Exit"); setCancelButtonText("Cancel"); testForDisplayedDataFiles = true; break; } if (testForDisplayedDataFiles) { const std::vector windowIndices = GuiManager::get()->getAllOpenBrainBrowserWindowIndices(); /* * Get all browser tabs and only save transformations for tabs * that are valid. */ EventBrowserTabGetAllViewed getViewedTabs; EventManager::get()->sendEvent(getViewedTabs.getPointer()); std::vector tabIndices = getViewedTabs.getViewdedBrowserTabIndices(); EventGetDisplayedDataFiles displayedFilesEvent(windowIndices, tabIndices); EventManager::get()->sendEvent(displayedFilesEvent.getPointer()); m_displayedDataFiles = displayedFilesEvent.getDisplayedDataFiles(); } /* * Set column indices for table's members */ int columnCounter = 0; m_COLUMN_LOAD_CHECKBOX = -1; m_COLUMN_SAVE_CHECKBOX = -1; m_COLUMN_STATUS_LABEL = -1; m_COLUMN_DISPLAYED_LABEL = -1; m_COLUMN_IN_SPEC_FILE_CHECKBOX = -1; m_COLUMN_READ_BUTTON = -1; m_COLUMN_CLOSE_BUTTON = -1; m_COLUMN_OPTIONS_TOOLBUTTON = -1; m_COLUMN_DATA_FILE_TYPE_LABEL = -1; m_COLUMN_STRUCTURE = -1; m_COLUMN_FILE_NAME_LABEL = -1; m_COLUMN_COUNT = -1; if (enableOpenItems) { m_COLUMN_LOAD_CHECKBOX = columnCounter++; } if (enableManageItems) { m_COLUMN_SAVE_CHECKBOX = columnCounter++; m_COLUMN_STATUS_LABEL = columnCounter++; m_COLUMN_DISPLAYED_LABEL = columnCounter++; m_COLUMN_IN_SPEC_FILE_CHECKBOX = columnCounter++; m_COLUMN_READ_BUTTON = columnCounter++; m_COLUMN_CLOSE_BUTTON = columnCounter++; } m_COLUMN_OPTIONS_TOOLBUTTON = columnCounter++; m_COLUMN_DATA_FILE_TYPE_LABEL = columnCounter++; m_COLUMN_STRUCTURE = columnCounter++; m_COLUMN_FILE_NAME_LABEL = columnCounter++; m_COLUMN_COUNT = columnCounter++; /* * Get the content from the spec file. * It is examined when creating some of the toolbars. */ getDataFileContentFromSpecFile(); /* * Create the table */ m_filesTableWidget = new QTableWidget(); m_filesTableWidget->setAlternatingRowColors(true); m_filesTableWidget->setSelectionBehavior(QTableWidget::SelectItems); m_filesTableWidget->setSelectionMode(QTableWidget::SingleSelection); QObject::connect(m_filesTableWidget, SIGNAL(cellChanged(int,int)), this, SLOT(filesTableWidgetCellChanged(int,int))); QObject::connect(m_filesTableWidget->horizontalHeader(), SIGNAL(sectionClicked(int)), this, SLOT(horizontalHeaderSelectedForSorting(int))); const QString headerToolTip = ("Click on the column names (of those columns that contain text) to sort."); m_filesTableWidget->horizontalHeader()->setToolTip(WuQtUtilities::createWordWrappedToolTipText(headerToolTip)); /* * Widget and layout for files. * * Two layouts used so widget is pushed to the top left (and not * spread out) when groups of files are hidden. */ QWidget* centralWidget = NULL; centralWidget = m_filesTableWidget; m_filesTableWidget->resizeColumnsToContents(); m_filesTableWidget->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::MinimumExpanding); QWidget* toolbarWidget = new QWidget(); QVBoxLayout* toolbarLayout = new QVBoxLayout(toolbarWidget); WuQtUtilities::setLayoutSpacingAndMargins(toolbarLayout, 0, 0); QLabel* fileTypesToolBarLabel = NULL; toolbarLayout->addWidget(createFilesTypesToolBar(fileTypesToolBarLabel)); QLabel* fileStructureToolBarLabel = NULL; toolbarLayout->addWidget(createStructureToolBar(fileStructureToolBarLabel)); QLabel* fileSelectionToolBarLabel = NULL; QLabel* fileLoadedNotLoadedToolBarLabel = NULL; if (enableOpenItems) { toolbarLayout->addWidget(createFilesSelectionToolBar(fileSelectionToolBarLabel)); } else if (enableManageItems) { toolbarLayout->addWidget(createManageFilesLoadedNotLoadedToolBar(fileLoadedNotLoadedToolBarLabel)); } toolbarWidget->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed); /* * Make all of the toolbar labels have the same width to that * they line up nicely. Note that the match widgets widgets * method will ignore NULL values. */ WuQtUtilities::matchWidgetWidths(fileTypesToolBarLabel, fileStructureToolBarLabel, fileSelectionToolBarLabel, fileLoadedNotLoadedToolBarLabel); /* * No scrollbars in dialog since the table widget will have scroll bars */ // const bool enableScrollBars = false; setTopBottomAndCentralWidgets(toolbarWidget, centralWidget, NULL, WuQDialog::SCROLL_AREA_NEVER); /* * Display the data files. */ updateTableDimensionsToFitFiles(); loadSpecFileContentIntoDialog(); disableAutoDefaultForAllPushButtons(); // /* // * Table widget has a default size of 640 x 480. // * So estimate the size of the dialog with the table fully // * expanded. // */ // QHeaderView* columnHeader = m_filesTableWidget->horizontalHeader(); // const int columnHeaderHeight = columnHeader->sizeHint().height(); // // int dialogWidth = 10; // start out with a little extra space // int dialogHeight = 0; // // const int numRows = m_filesTableWidget->rowCount(); // const int numCols = m_filesTableWidget->columnCount(); // const int cellGap = 1; // if ((numRows > 0) // && (numCols > 0)) { // for (int i = 0; i < numCols; i++) { // dialogWidth += (m_filesTableWidget->columnWidth(i) + cellGap); // std::cout << "column width " << i << ": " << m_filesTableWidget->columnWidth(i) << std::endl; // } // // for (int i = 0; i < numRows; i++) { // dialogHeight += (m_filesTableWidget->rowHeight(i) + cellGap); // } // } const QSize tableSize = WuQtUtilities::estimateTableWidgetSize(m_filesTableWidget); int dialogWidth = std::max(tableSize.width(), toolbarWidget->sizeHint().width()); int dialogHeight = (toolbarWidget->sizeHint().height() + getDialogButtonBox()->sizeHint().height() + tableSize.height()); WuQtUtilities::resizeWindow(this, dialogWidth, dialogHeight); } /** * Destructor. */ SpecFileManagementDialog::~SpecFileManagementDialog() { clearSpecFileManagementDialogRowContent(); if (m_iconOpenFile != NULL) { delete m_iconOpenFile; } if (m_iconOptions != NULL) { delete m_iconOptions; } if (m_iconReloadFile != NULL) { delete m_iconReloadFile; } if (m_iconCloseFile != NULL) { delete m_iconCloseFile; } } /** * Clear content of all of the table row. */ void SpecFileManagementDialog::clearSpecFileManagementDialogRowContent() { const int32_t numRows = static_cast(m_tableRowDataFileContent.size()); for (int32_t i = 0; i < numRows; i++) { delete m_tableRowDataFileContent[i]; } m_tableRowDataFileContent.clear(); } /** * Get the info about the data files from the spec file. */ void SpecFileManagementDialog::getDataFileContentFromSpecFile() { clearSpecFileManagementDialogRowContent(); bool haveSceneFiles = false; /* * Display each type of data file */ const int32_t numGroups = m_specFile->getNumberOfDataFileTypeGroups(); for (int32_t ig = 0 ; ig < numGroups; ig++) { /* * File type of group */ SpecFileDataFileTypeGroup* group = m_specFile->getDataFileTypeGroupByIndex(ig); const DataFileTypeEnum::Enum dataFileType = group->getDataFileType(); const int32_t numFiles = group->getNumberOfFiles(); for (int iFile = 0; iFile < numFiles; iFile++) { SpecFileDataFile* sfdf = group->getFileInformation(iFile); /* * If the spec file entry is not a member of the spec file, * AND if it does not match a loaded file * AND the file does not exist * THEN the file likely was a duplicate (more than one copy * of a specific file was loaded with the same name) and * the user had removed it from Workbench. * * So, hide the file from view. */ if ( ! sfdf->isSpecFileMember()) { if (sfdf->getCaretDataFile() == NULL) { if (! sfdf->exists()) { continue; } } } SpecFileManagementDialogRowContent* rowContent = new SpecFileManagementDialogRowContent(group, sfdf); m_tableRowDataFileContent.push_back(rowContent); m_specFileDataFileCounter++; } if (dataFileType == DataFileTypeEnum::SCENE) { if (numFiles > 0) { haveSceneFiles = true; } } } if (m_loadScenesPushButton != NULL) { m_loadScenesPushButton->setEnabled(haveSceneFiles); } } /** * 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 SpecFileManagementDialog::filesTableWidgetCellChanged(int rowIndex, int columnIndex) { QTableWidgetItem* item = getTableWidgetItem(rowIndex, columnIndex); if (item != NULL) { /* * Is this the row containing the spec file? */ if (rowIndex == m_specFileTableRowIndex) { /* * Nothing to do for spec file */ } else if (rowIndex == m_sceneAnnotationFileRowIndex) { /* Nothing to do for annotation scene file */ } else { SpecFileManagementDialogRowContent* rowContent = getFileContentInRow(rowIndex); CaretAssert(rowContent); SpecFileDataFile* sfdf = rowContent->m_specFileDataFile; const bool isSelected = WuQtUtilities::checkStateToBool(item->checkState()); if (columnIndex == m_COLUMN_SAVE_CHECKBOX) { sfdf->setSavingSelected(isSelected); } else if (columnIndex == m_COLUMN_LOAD_CHECKBOX) { sfdf->setLoadingSelected(isSelected); } else if (columnIndex == m_COLUMN_IN_SPEC_FILE_CHECKBOX) { sfdf->setSpecFileMember(isSelected); updateSpecFileRowInTable(); /* * Check the Spec File SAVE checkbox since the user has changed the * "in spec" status for a data file */ if (m_specFileTableRowIndex >= 0) { QTableWidgetItem* saveItem = getTableWidgetItem(m_specFileTableRowIndex, m_COLUMN_SAVE_CHECKBOX); saveItem->setCheckState(Qt::Checked); } } } } enableLoadOrSaveButton(); } void SpecFileManagementDialog::enableLoadOrSaveButton() { bool isAnyFileSelected = false; if (m_specFile != NULL) { switch (m_dialogMode) { case MODE_MANAGE_FILES: case MODE_SAVE_FILES_WHILE_QUITTING: { if (m_specFile->getNumberOfFilesSelectedForSaving() > 0) { isAnyFileSelected = true; } QTableWidgetItem* specCheckItem = getTableWidgetItem(m_specFileTableRowIndex, m_COLUMN_SAVE_CHECKBOX); if (WuQtUtilities::checkStateToBool(specCheckItem->checkState())) { isAnyFileSelected = true; } } break; case MODE_OPEN_SPEC_FILE: if (m_specFile->getNumberOfFilesSelectedForLoading() > 0) { isAnyFileSelected = true; } break; } } QAbstractButton* okButton = getDialogButtonBox()->button(QDialogButtonBox::Ok); CaretAssert(okButton); okButton->setEnabled(isAnyFileSelected); } /** * Get the table widget item at the given row and column. If compiled * debug the assertions will fail if the row or column is invalid. * * @param rowIndex * The row of the desired cell. * @param columnIndex * The column of the desired cell. * @return * item at row and column. */ QTableWidgetItem* SpecFileManagementDialog::getTableWidgetItem(const int rowIndex, const int columnIndex) { CaretAssert((rowIndex >= 0) && (rowIndex < m_filesTableWidget->rowCount())); CaretAssert((columnIndex >= 0) && (columnIndex < m_filesTableWidget->columnCount())); return m_filesTableWidget->item(rowIndex, columnIndex); } /** * Set the table widget item at the given row and column. If compiled * debug the assertions will fail if the row or column is invalid. * * @param rowIndex * The row of the desired cell. * @param columnIndex * The column of the desired cell. * @param item * The item to add. */ void SpecFileManagementDialog::setTableWidgetItem(const int rowIndex, const int columnIndex, QTableWidgetItem* item) { CaretAssert(item); CaretAssert((rowIndex >= 0) && (rowIndex < m_filesTableWidget->rowCount())); CaretAssert((columnIndex >= 0) && (columnIndex < m_filesTableWidget->columnCount())); m_filesTableWidget->blockSignals(true); m_filesTableWidget->setItem(rowIndex, columnIndex, item); m_filesTableWidget->blockSignals(false); } /** * Set the labels for the column names in the table. */ void SpecFileManagementDialog::setTableColumnLabels() { /* * Set names of table's columns */ if (m_COLUMN_LOAD_CHECKBOX >= 0){ m_filesTableWidget->setHorizontalHeaderItem(m_COLUMN_LOAD_CHECKBOX, createHeaderTextItem("Load")); } if (m_COLUMN_SAVE_CHECKBOX >= 0){ m_filesTableWidget->setHorizontalHeaderItem(m_COLUMN_SAVE_CHECKBOX, createHeaderTextItem("Save")); } if (m_COLUMN_STATUS_LABEL >= 0){ m_filesTableWidget->setHorizontalHeaderItem(m_COLUMN_STATUS_LABEL, createHeaderTextItem("Modified")); } if (m_COLUMN_DISPLAYED_LABEL >= 0) { m_filesTableWidget->setHorizontalHeaderItem(m_COLUMN_DISPLAYED_LABEL, createHeaderTextItem("Displayed")); } if (m_COLUMN_IN_SPEC_FILE_CHECKBOX >= 0){ m_filesTableWidget->setHorizontalHeaderItem(m_COLUMN_IN_SPEC_FILE_CHECKBOX, createHeaderTextItem("In Spec")); } if (m_COLUMN_READ_BUTTON >= 0){ m_filesTableWidget->setHorizontalHeaderItem(m_COLUMN_READ_BUTTON, createHeaderTextItem("Read")); } if (m_COLUMN_CLOSE_BUTTON >= 0) { m_filesTableWidget->setHorizontalHeaderItem(m_COLUMN_CLOSE_BUTTON, createHeaderTextItem("Close")); } if (m_COLUMN_OPTIONS_TOOLBUTTON >= 0){ m_filesTableWidget->setHorizontalHeaderItem(m_COLUMN_OPTIONS_TOOLBUTTON, createHeaderTextItem("More")); } if (m_COLUMN_DATA_FILE_TYPE_LABEL >= 0){ m_filesTableWidget->setHorizontalHeaderItem(m_COLUMN_DATA_FILE_TYPE_LABEL, createHeaderTextItem("Data Type")); } if (m_COLUMN_STRUCTURE >= 0){ m_filesTableWidget->setHorizontalHeaderItem(m_COLUMN_STRUCTURE, createHeaderTextItem("Structure")); } if (m_COLUMN_FILE_NAME_LABEL >= 0){ m_filesTableWidget->setHorizontalHeaderItem(m_COLUMN_FILE_NAME_LABEL, createHeaderTextItem("Data File Name")); } } /** * @return A table widget item used in the column header. * * @param text * Text for the item. * @return Table widget item containing the given text. */ QTableWidgetItem* SpecFileManagementDialog::createHeaderTextItem(const QString& text) { QList sizesList = QFontDatabase::standardSizes(); std::sort(sizesList.begin(), sizesList.end()); QTableWidgetItem* item = createTextItem(); item->setText(text); QFont font = item->font(); QListIterator sizeIter(sizesList); while (sizeIter.hasNext()) { const int nextSize = sizeIter.next(); if (nextSize > font.pointSize()) { font.setPointSize(nextSize); break; } } font.setBold(true); item->setFont(font); return item; } /** * Load items into the table widget adding rows as needed. */ void SpecFileManagementDialog::updateTableDimensionsToFitFiles() { /* * If needed, add a row for the spec file */ m_specFileTableRowIndex = -1; m_sceneAnnotationFileRowIndex = -1; int numberOfRows = 0; switch (m_dialogMode) { case MODE_MANAGE_FILES: case MODE_SAVE_FILES_WHILE_QUITTING: m_specFileTableRowIndex = numberOfRows; numberOfRows++; // if ( ! m_brain->getSceneAnnotationFile()->isEmpty()) { m_sceneAnnotationFileRowIndex = numberOfRows; numberOfRows++; // } break; case MODE_OPEN_SPEC_FILE: break; } /* * Update rows indices for data files */ const int32_t numFiles = static_cast(m_tableRowDataFileContent.size()); for (int32_t i = 0; i < numFiles; i++) { m_tableRowDataFileContent[i]->m_tableRowIndex = numberOfRows; numberOfRows++; } /* * If the number of rows has not changed, no need to update table dimensions */ bool needToCreateColumnHeaders = false; if (numberOfRows == m_filesTableWidget->rowCount()) { return; } else if (m_filesTableWidget->rowCount() == 0) { needToCreateColumnHeaders = true; } /* * Update the dimensions of the table */ const int32_t firstNewRowIndex = m_filesTableWidget->rowCount(); m_filesTableWidget->setRowCount(numberOfRows); m_filesTableWidget->setColumnCount(m_COLUMN_COUNT); m_filesTableWidget->verticalHeader()->hide(); m_filesTableWidget->setGridStyle(Qt::NoPen); m_filesTableWidget->setSortingEnabled(false); if (needToCreateColumnHeaders) { setTableColumnLabels(); } const int32_t lastNewRowIndex = m_filesTableWidget->rowCount(); // value changed by setRowCount() /* * Add new cells to the table widget */ for (int32_t iRow = firstNewRowIndex; iRow < lastNewRowIndex; iRow++) { bool hasCloseCheckBoxFlag = true; bool hasDisplayedLabelFlag = true; bool hasInSpecCheckBoxFlag = true; bool hasLoadCheckBoxFlag = true; bool hasOptionsButtonFlag = true; bool hasReadCheckBoxFlag = true; bool hasSaveCheckBoxFlag = true; bool hasStatusLabelFlag = true; if (iRow == m_specFileTableRowIndex) { hasCloseCheckBoxFlag = false; hasDisplayedLabelFlag = false; hasInSpecCheckBoxFlag = false; hasLoadCheckBoxFlag = false; hasReadCheckBoxFlag = false; } else if (iRow == m_sceneAnnotationFileRowIndex) { hasDisplayedLabelFlag = false; hasInSpecCheckBoxFlag = false; hasLoadCheckBoxFlag = false; hasReadCheckBoxFlag = false; hasSaveCheckBoxFlag = false; } if (hasLoadCheckBoxFlag) { if (m_COLUMN_LOAD_CHECKBOX >= 0) { QTableWidgetItem* item = createCheckableItem(); item->setTextAlignment(Qt::AlignHCenter); setTableWidgetItem(iRow, m_COLUMN_LOAD_CHECKBOX, item); } } if (hasSaveCheckBoxFlag) { if (m_COLUMN_SAVE_CHECKBOX >= 0) { QTableWidgetItem* item = createCheckableItem(); item->setTextAlignment(Qt::AlignHCenter); setTableWidgetItem(iRow, m_COLUMN_SAVE_CHECKBOX, item); } } if (hasStatusLabelFlag) { if (m_COLUMN_STATUS_LABEL >= 0) { QTableWidgetItem* item = createTextItem(); setTableWidgetItem(iRow, m_COLUMN_STATUS_LABEL, item); /* * Text for modified cell is always red and centered */ item->setTextAlignment(Qt::AlignCenter); QBrush modifiedBrush = item->foreground(); modifiedBrush.setColor(Qt::red); item->setForeground(modifiedBrush); } } if (hasDisplayedLabelFlag) { if (m_COLUMN_DISPLAYED_LABEL >= 0) { QTableWidgetItem* item = createTextItem(); setTableWidgetItem(iRow, m_COLUMN_DISPLAYED_LABEL, item); item->setTextAlignment(Qt::AlignCenter); } } if (hasInSpecCheckBoxFlag) { if (m_COLUMN_IN_SPEC_FILE_CHECKBOX >= 0) { QTableWidgetItem* item = createCheckableItem(); item->setTextAlignment(Qt::AlignHCenter); setTableWidgetItem(iRow, m_COLUMN_IN_SPEC_FILE_CHECKBOX, item); } } if (hasReadCheckBoxFlag) { if (m_COLUMN_READ_BUTTON >= 0) { WuQImageLabel* loadImageLabel = new WuQImageLabel(m_iconReloadFile, "Reload"); QObject::connect(loadImageLabel, SIGNAL(clicked()), m_fileReloadOrOpenFileActionSignalMapper, SLOT(map())); m_fileReloadOrOpenFileActionSignalMapper->setMapping(loadImageLabel, iRow); m_filesTableWidget->setCellWidget(iRow, m_COLUMN_READ_BUTTON, loadImageLabel); } } if (hasCloseCheckBoxFlag) { if (m_COLUMN_CLOSE_BUTTON >= 0) { WuQImageLabel* closeImageLabel = new WuQImageLabel(m_iconCloseFile, "Close"); QObject::connect(closeImageLabel, SIGNAL(clicked()), m_fileCloseFileActionSignalMapper, SLOT(map())); m_fileCloseFileActionSignalMapper->setMapping(closeImageLabel, iRow); m_filesTableWidget->setCellWidget(iRow, m_COLUMN_CLOSE_BUTTON, closeImageLabel); } } if (hasOptionsButtonFlag) { if (m_COLUMN_OPTIONS_TOOLBUTTON >= 0) { WuQImageLabel* optionsImageLabel = new WuQImageLabel(m_iconOptions, "Options"); QObject::connect(optionsImageLabel, SIGNAL(clicked()), m_fileOptionsActionSignalMapper, SLOT(map())); m_fileOptionsActionSignalMapper->setMapping(optionsImageLabel, iRow); m_filesTableWidget->setCellWidget(iRow, m_COLUMN_OPTIONS_TOOLBUTTON, optionsImageLabel); } } if (m_COLUMN_DATA_FILE_TYPE_LABEL >= 0) { setTableWidgetItem(iRow, m_COLUMN_DATA_FILE_TYPE_LABEL, createTextItem()); } if (m_COLUMN_STRUCTURE >= 0) { setTableWidgetItem(iRow, m_COLUMN_STRUCTURE, createTextItem()); } if (m_COLUMN_FILE_NAME_LABEL >= 0) { setTableWidgetItem(iRow, m_COLUMN_FILE_NAME_LABEL, createTextItem()); } } } /** * Update the table row containing the spec file. */ void SpecFileManagementDialog::updateSpecFileRowInTable() { bool isUpdateRow = false; switch (m_dialogMode) { case MODE_MANAGE_FILES: case MODE_SAVE_FILES_WHILE_QUITTING: isUpdateRow = true; break; case MODE_OPEN_SPEC_FILE: break; } /* * Update spec file data */ if (isUpdateRow) { if (m_specFileTableRowIndex >= 0) { CaretAssert(m_COLUMN_DATA_FILE_TYPE_LABEL >= 0); QTableWidgetItem* dataTypeItem = getTableWidgetItem(m_specFileTableRowIndex, m_COLUMN_DATA_FILE_TYPE_LABEL); dataTypeItem->setText(getEditedDataFileTypeName(DataFileTypeEnum::SPECIFICATION)); CaretAssert(m_COLUMN_FILE_NAME_LABEL >= 0); QTableWidgetItem* nameItem = getTableWidgetItem(m_specFileTableRowIndex, m_COLUMN_FILE_NAME_LABEL); CaretAssert(nameItem); const AString specFileName = m_specFile->getFileName(); AString path; AString name; if (specFileName.isEmpty() == false) { FileInformation fileInfo(specFileName); path = fileInfo.getAbsolutePath(); name = fileInfo.getFileName(); } nameItem->setText(name); nameItem->setToolTip(path); CaretAssert(m_COLUMN_STATUS_LABEL >= 0); QTableWidgetItem* statusItem = getTableWidgetItem(m_specFileTableRowIndex, m_COLUMN_STATUS_LABEL); CaretAssert(statusItem); if (m_specFile->isModified()) { statusItem->setText("YES"); } else { statusItem->setText(""); } /* * Get filtering selections */ SpecFileDialogViewFilesTypeEnum::Enum viewFilesType; DataFileTypeEnum::Enum filteredDataFileType; StructureEnum::Enum filteredStructureType; getFilterSelections(viewFilesType, filteredDataFileType, filteredStructureType); bool hideSpecFileRow = false; if ((filteredStructureType != StructureEnum::ALL) && (filteredStructureType != StructureEnum::OTHER)) { hideSpecFileRow = true; } switch (viewFilesType) { case SpecFileDialogViewFilesTypeEnum::VIEW_FILES_ALL: break; case SpecFileDialogViewFilesTypeEnum::VIEW_FILES_LOADED: break; case SpecFileDialogViewFilesTypeEnum::VIEW_FILES_LOADED_MODIFIED: if (! m_specFile->isModified()) { hideSpecFileRow = true; } break; case SpecFileDialogViewFilesTypeEnum::VIEW_FILES_LOADED_NOT_MODIFIED: if (m_specFile->isModified()) { hideSpecFileRow = true; } break; case SpecFileDialogViewFilesTypeEnum::VIEW_FILES_NOT_LOADED: hideSpecFileRow = true; break; } if (filteredDataFileType != DataFileTypeEnum::UNKNOWN) { hideSpecFileRow = true; } m_filesTableWidget->setRowHidden(m_specFileTableRowIndex, hideSpecFileRow); } } } /** * Update the table row containing the annotation scene. */ void SpecFileManagementDialog::updateAnnotationSceneFileRowInTable() { bool isUpdateRow = false; switch (m_dialogMode) { case MODE_MANAGE_FILES: case MODE_SAVE_FILES_WHILE_QUITTING: isUpdateRow = true; break; case MODE_OPEN_SPEC_FILE: break; } /* * Update spec file data */ if (isUpdateRow) { if (m_sceneAnnotationFileRowIndex >= 0) { const AnnotationFile* sceneAnnotationFile = m_brain->getSceneAnnotationFile(); CaretAssert(m_COLUMN_DATA_FILE_TYPE_LABEL >= 0); QTableWidgetItem* dataTypeItem = getTableWidgetItem(m_sceneAnnotationFileRowIndex, m_COLUMN_DATA_FILE_TYPE_LABEL); dataTypeItem->setText("Scene Annotations"); CaretAssert(m_COLUMN_FILE_NAME_LABEL >= 0); QTableWidgetItem* nameItem = getTableWidgetItem(m_sceneAnnotationFileRowIndex, m_COLUMN_FILE_NAME_LABEL); CaretAssert(nameItem); nameItem->setText(""); nameItem->setToolTip(""); CaretAssert(m_COLUMN_STATUS_LABEL >= 0); QTableWidgetItem* statusItem = getTableWidgetItem(m_sceneAnnotationFileRowIndex, m_COLUMN_STATUS_LABEL); /* * Scene annotation file is considered modified if * it is NOT empty. */ const bool modifiedStatusFlag = sceneAnnotationFile->isModified(); CaretAssert(statusItem); if (modifiedStatusFlag) { statusItem->setText("YES"); } else { statusItem->setText(""); } const bool haveSceneAnnotationsFlag = ( ! sceneAnnotationFile->isEmpty()); QWidget* closeWidget = m_filesTableWidget->cellWidget(m_sceneAnnotationFileRowIndex, m_COLUMN_CLOSE_BUTTON); CaretAssert(closeWidget); QWidget* optionsWidget = m_filesTableWidget->cellWidget(m_sceneAnnotationFileRowIndex, m_COLUMN_OPTIONS_TOOLBUTTON); CaretAssert(optionsWidget); closeWidget->setEnabled(haveSceneAnnotationsFlag); optionsWidget->setEnabled(haveSceneAnnotationsFlag); /* * Get filtering selections */ SpecFileDialogViewFilesTypeEnum::Enum viewFilesType; DataFileTypeEnum::Enum filteredDataFileType; StructureEnum::Enum filteredStructureType; getFilterSelections(viewFilesType, filteredDataFileType, filteredStructureType); bool hideSceneAnnontationFileRow = false; if ((filteredStructureType != StructureEnum::ALL) && (filteredStructureType != StructureEnum::OTHER)) { hideSceneAnnontationFileRow = true; } switch (viewFilesType) { case SpecFileDialogViewFilesTypeEnum::VIEW_FILES_ALL: break; case SpecFileDialogViewFilesTypeEnum::VIEW_FILES_LOADED: break; case SpecFileDialogViewFilesTypeEnum::VIEW_FILES_LOADED_MODIFIED: if (! modifiedStatusFlag) { hideSceneAnnontationFileRow = true; } break; case SpecFileDialogViewFilesTypeEnum::VIEW_FILES_LOADED_NOT_MODIFIED: if (modifiedStatusFlag) { hideSceneAnnontationFileRow = true; } break; case SpecFileDialogViewFilesTypeEnum::VIEW_FILES_NOT_LOADED: hideSceneAnnontationFileRow = true; break; } if (filteredDataFileType != DataFileTypeEnum::UNKNOWN) { hideSceneAnnontationFileRow = true; } m_filesTableWidget->setRowHidden(m_sceneAnnotationFileRowIndex, hideSceneAnnontationFileRow); } } } /** * Gets called when a column header is selected. * @param logicalIndex * Index of item that was selected. */ void SpecFileManagementDialog::horizontalHeaderSelectedForSorting(int logicalIndex) { if (logicalIndex == m_COLUMN_DATA_FILE_TYPE_LABEL) { m_fileSorting = SpecFileManagementDialogRowContent::SORTING_TYPE_STRUCTURE_NAME; loadSpecFileContentIntoDialog(); } else if (logicalIndex == m_COLUMN_FILE_NAME_LABEL) { m_fileSorting = SpecFileManagementDialogRowContent::SORTING_NAME; loadSpecFileContentIntoDialog(); } else if (logicalIndex == m_COLUMN_STRUCTURE) { m_fileSorting = SpecFileManagementDialogRowContent::SORTING_STRUCTURE_TYPE_NAME; loadSpecFileContentIntoDialog(); } } /** * Get the file filtering selections * * @param viewFilesTypeOut * view files types * @param filteredDataFileTypeOut * Data file type * @param filteredStructureTypeOut * Structure */ void SpecFileManagementDialog::getFilterSelections(SpecFileDialogViewFilesTypeEnum::Enum& viewFilesTypeOut, DataFileTypeEnum::Enum& filteredDataFileTypeOut, StructureEnum::Enum& filteredStructureTypeOut) const { /* * All/Loaded/Not-Loaded */ viewFilesTypeOut = SpecFileDialogViewFilesTypeEnum::VIEW_FILES_ALL; if (m_manageFilesLoadedNotLoadedActionGroup != NULL) { const QAction* manageFileAction = m_manageFilesLoadedNotLoadedActionGroup->checkedAction(); if (manageFileAction != NULL) { viewFilesTypeOut = SpecFileDialogViewFilesTypeEnum::fromIntegerCode(manageFileAction->data().toInt(), NULL); } } /* * Filter for data file type selection */ filteredDataFileTypeOut = DataFileTypeEnum::UNKNOWN; const QAction* dataFileTypeAction = m_fileTypesActionGroup->checkedAction(); if (dataFileTypeAction != NULL) { const int dataFileTypeInt = dataFileTypeAction->data().toInt(); filteredDataFileTypeOut = DataFileTypeEnum::fromIntegerCode(dataFileTypeInt, NULL); } /* * Filter for structure */ filteredStructureTypeOut = StructureEnum::ALL; const QAction* filteredStructureAction = m_structureActionGroup->checkedAction(); if (filteredStructureAction != NULL) { const int structureTypeInt = filteredStructureAction->data().toInt(); filteredStructureTypeOut = StructureEnum::fromIntegerCode(structureTypeInt, NULL); } } /** * Set the file filtering selections * * @param viewFilesType * View files type * @param filteredDataFileType * Data file type * @param filteredStructureType * Structure */ void SpecFileManagementDialog::setFilterSelections(const SpecFileDialogViewFilesTypeEnum::Enum viewFilesType, const DataFileTypeEnum::Enum filteredDataFileType, const StructureEnum::Enum filteredStructureType) { const int32_t viewFilesTypeInt = SpecFileDialogViewFilesTypeEnum::toIntegerCode(viewFilesType); QList manageActions = m_manageFilesLoadedNotLoadedActionGroup->actions(); QListIterator manageIterator(manageActions); while (manageIterator.hasNext()) { QAction* action = manageIterator.next(); if (action->data().toInt() == viewFilesTypeInt) { action->setChecked(true); break; } } const int32_t dataFileTypeInt = DataFileTypeEnum::toIntegerCode(filteredDataFileType); QList fileTypeActions = m_fileTypesActionGroup->actions(); QListIterator fileTypeIterator(fileTypeActions); while (fileTypeIterator.hasNext()) { QAction* action = fileTypeIterator.next(); if (action->data() == dataFileTypeInt) { action->setChecked(true); break; } } const int32_t structureTypeInt = StructureEnum::toIntegerCode(filteredStructureType); QList structureTypeActions = m_structureActionGroup->actions(); QListIterator structureTypeIterator(structureTypeActions); while (structureTypeIterator.hasNext()) { QAction* action = structureTypeIterator.next(); if (action->data() == structureTypeInt) { action->setChecked(true); break; } } } ///** // * Less than method for sorting using the sorting key. // * // * @param item1 // * Tested for less than the item2 // * @param item2 // * Tested for greater than the item1 // * @return // * True if item1 is less than item2, else false. // */ //bool //lessThanForSorting(const SpecFileManagementDialogRowContent *item1, // const SpecFileManagementDialogRowContent* item2) //{ // return (item1->m_sortingKey < item2->m_sortingKey); //} /** * Sort the file content. */ void SpecFileManagementDialog::sortFileContent() { /* * Update key used for sorting */ const int32_t numDataFiles = static_cast(m_tableRowDataFileContent.size()); for (int32_t i = 0; i < numDataFiles; i++) { m_tableRowDataFileContent[i]->setSortingKey(m_fileSorting); } /* * Sort */ std::sort(m_tableRowDataFileContent.begin(), m_tableRowDataFileContent.end(), SpecFileManagementDialogRowContent::lessThanForSorting); /* * Update row indices in table */ int rowCounter = 0; if (m_specFileTableRowIndex >= 0) { rowCounter++; } if (m_sceneAnnotationFileRowIndex >= 0) { rowCounter++; } for (int32_t i = 0; i < numDataFiles; i++) { m_tableRowDataFileContent[i]->m_tableRowIndex= rowCounter; rowCounter++; } } /** * Load the spec file data into the dialog. */ void SpecFileManagementDialog::loadSpecFileContentIntoDialog() { /* * Disable signals so cell changed signal not emitted while * modifying table content. */ m_filesTableWidget->blockSignals(true); /* * Sort the rows */ sortFileContent(); SpecFileDialogViewFilesTypeEnum::Enum filteredViewFilesType; DataFileTypeEnum::Enum filteredDataFileType; StructureEnum::Enum filteredStructureType; getFilterSelections(filteredViewFilesType, filteredDataFileType, filteredStructureType); updateSpecFileRowInTable(); updateAnnotationSceneFileRowInTable(); /* * Load all of the data file content. */ const int32_t numDataFiles = static_cast(m_tableRowDataFileContent.size()); for (int32_t i = 0; i < numDataFiles; i++) { const int rowIndex = m_tableRowDataFileContent[i]->m_tableRowIndex; CaretAssert((rowIndex >= 0) && (rowIndex < m_filesTableWidget->rowCount())); SpecFileDataFile* specFileDataFile = m_tableRowDataFileContent[i]->m_specFileDataFile; CaretDataFile* caretDataFile = specFileDataFile->getCaretDataFile(); const StructureEnum::Enum structure = specFileDataFile->getStructure(); bool isFileSavable = false; if (caretDataFile != NULL) { if (caretDataFile->supportsWriting()) { isFileSavable = true; } } const DataFileTypeEnum::Enum dataFileType = specFileDataFile->getDataFileType(); switch (m_dialogMode) { case MODE_MANAGE_FILES: case MODE_SAVE_FILES_WHILE_QUITTING: { /* * Save checkbox */ CaretAssert(m_COLUMN_SAVE_CHECKBOX >= 0); QTableWidgetItem* saveItem = getTableWidgetItem(rowIndex, m_COLUMN_SAVE_CHECKBOX); CaretAssert(saveItem); if (isFileSavable) { saveItem->setFlags(saveItem->flags() | Qt::ItemIsEnabled); saveItem->setCheckState(WuQtUtilities::boolToCheckState(specFileDataFile->isSavingSelected())); } else { saveItem->setFlags(saveItem->flags() & (~ Qt::ItemIsEnabled)); saveItem->setCheckState(Qt::Unchecked); } /* * Status label. */ CaretAssert(m_COLUMN_STATUS_LABEL >= 0); QTableWidgetItem* statusItem = getTableWidgetItem(rowIndex, m_COLUMN_STATUS_LABEL); CaretAssert(statusItem); statusItem->setText(""); if (caretDataFile != NULL) { if (caretDataFile->isModified()) { if (isFileSavable) { statusItem->setText("YES"); /* * 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 modification just the palette color mapping? */ if (mapFile->isModifiedPaletteColorMapping()) { if ( ! mapFile->isModifiedExcludingPaletteColorMapping()) { statusItem->setText("PALETTE"); } } } } } } /* * Displayed label. */ CaretAssert(m_COLUMN_DISPLAYED_LABEL >= 0); QTableWidgetItem* displayedItem = getTableWidgetItem(rowIndex, m_COLUMN_DISPLAYED_LABEL); CaretAssert(displayedItem); displayedItem->setText(""); if (caretDataFile != NULL) { if (m_displayedDataFiles.find(caretDataFile) != m_displayedDataFiles.end()) { displayedItem->setText("YES"); } } /* * In-spec checkbox */ CaretAssert(m_COLUMN_IN_SPEC_FILE_CHECKBOX >= 0); QTableWidgetItem* inSpecItem = getTableWidgetItem(rowIndex, m_COLUMN_IN_SPEC_FILE_CHECKBOX); CaretAssert(inSpecItem); inSpecItem->setCheckState(WuQtUtilities::boolToCheckState(specFileDataFile->isSpecFileMember())); /* * Read button */ CaretAssert(m_COLUMN_READ_BUTTON >= 0); QWidget* readCellWidget = m_filesTableWidget->cellWidget(rowIndex, m_COLUMN_READ_BUTTON); CaretAssert(readCellWidget); WuQImageLabel* readImageLabel = dynamic_cast(readCellWidget); CaretAssert(readImageLabel); QWidget* closeWidget = m_filesTableWidget->cellWidget(rowIndex, m_COLUMN_CLOSE_BUTTON); CaretAssert(closeWidget); closeWidget->setEnabled(caretDataFile != NULL); if (caretDataFile != NULL) { readImageLabel->updateIconText(m_iconReloadFile, "Reload"); readImageLabel->setToolTip("Reload this file"); } else { readImageLabel->updateIconText(m_iconOpenFile, "Load"); readImageLabel->setToolTip("Load this file"); } } break; case MODE_OPEN_SPEC_FILE: { /* * Load checkbox */ CaretAssert(m_COLUMN_LOAD_CHECKBOX >= 0); QTableWidgetItem* loadItem = getTableWidgetItem(rowIndex, m_COLUMN_LOAD_CHECKBOX); CaretAssert(loadItem); loadItem->setCheckState(WuQtUtilities::boolToCheckState(specFileDataFile->isLoadingSelected())); } break; } /* * Data file type label */ CaretAssert(m_COLUMN_DATA_FILE_TYPE_LABEL >= 0); QTableWidgetItem* dataTypeItem = getTableWidgetItem(rowIndex, m_COLUMN_DATA_FILE_TYPE_LABEL); CaretAssert(dataTypeItem); dataTypeItem->setText(SpecFileManagementDialog::getEditedDataFileTypeName(dataFileType)); /* * Structure label */ CaretAssert(m_COLUMN_STRUCTURE >= 0); QTableWidgetItem* structureItem = getTableWidgetItem(rowIndex, m_COLUMN_STRUCTURE); CaretAssert(structureItem); structureItem->setText(""); if (DataFileTypeEnum::isFileUsedWithOneStructure(dataFileType)) { structureItem->setText(StructureEnum::toGuiName(specFileDataFile->getStructure())); } /* * File name and path */ CaretAssert(m_COLUMN_FILE_NAME_LABEL >= 0); QTableWidgetItem* nameItem = getTableWidgetItem(rowIndex, m_COLUMN_FILE_NAME_LABEL); CaretAssert(nameItem); AString path; AString name; const QString fileName(specFileDataFile->getFileName()); if (fileName.isEmpty()) { CaretLogSevere("While loading spec file dialog, file of type " + DataFileTypeEnum::toGuiName(dataFileType) + " has empty file name."); } else { FileInformation fileInfo(specFileDataFile->getFileName()); path = fileInfo.getAbsolutePath(); name = fileInfo.getFileName(); } nameItem->setText(name); nameItem->setToolTip(path); /* * Should file (row) be hidden? */ bool isFileHidden = false; if (filteredDataFileType != DataFileTypeEnum::UNKNOWN) { if (dataFileType != filteredDataFileType) { isFileHidden = true; } } if (filteredStructureType != StructureEnum::ALL) { switch (filteredStructureType) { case StructureEnum::CORTEX_LEFT: if (structure != StructureEnum::CORTEX_LEFT) { isFileHidden = true; } break; case StructureEnum::CORTEX_RIGHT: if (structure != StructureEnum::CORTEX_RIGHT) { isFileHidden = true; } break; case StructureEnum::CEREBELLUM: case StructureEnum::CEREBELLUM_LEFT: case StructureEnum::CEREBELLUM_RIGHT: if (structure != StructureEnum::CEREBELLUM) { isFileHidden = true; } break; case StructureEnum::OTHER: { switch (structure) { case StructureEnum::CORTEX_LEFT: case StructureEnum::CORTEX_RIGHT: case StructureEnum::CEREBELLUM: case StructureEnum::CEREBELLUM_LEFT: case StructureEnum::CEREBELLUM_RIGHT: isFileHidden = true; break; default: break; } } break; default: break; } } switch (filteredViewFilesType) { case SpecFileDialogViewFilesTypeEnum::VIEW_FILES_ALL: break; case SpecFileDialogViewFilesTypeEnum::VIEW_FILES_LOADED: if (caretDataFile == NULL) { isFileHidden = true; } break; case SpecFileDialogViewFilesTypeEnum::VIEW_FILES_LOADED_MODIFIED: if (caretDataFile != NULL) { if ( ! caretDataFile->isModified()) { isFileHidden = true; } } else { isFileHidden = true; } break; case SpecFileDialogViewFilesTypeEnum::VIEW_FILES_LOADED_NOT_MODIFIED: if (caretDataFile != NULL) { if (caretDataFile->isModified()) { isFileHidden = true; } } else { isFileHidden = true; } break; case SpecFileDialogViewFilesTypeEnum::VIEW_FILES_NOT_LOADED: if (caretDataFile != NULL) { isFileHidden = true; } break; } m_filesTableWidget->setRowHidden(rowIndex, isFileHidden); } /* * Enable cell changed signal now that table has been updated. */ m_filesTableWidget->blockSignals(false); /* * Fix table geometry. */ m_filesTableWidget->horizontalHeader()->setStretchLastSection(true); m_filesTableWidget->resizeColumnsToContents(); m_filesTableWidget->resizeRowsToContents(); enableLoadOrSaveButton(); } /** * @return Create and return a text item for the table. */ QTableWidgetItem* SpecFileManagementDialog::createTextItem() { QTableWidgetItem* item = new QTableWidgetItem(); Qt::ItemFlags flags(Qt::ItemIsEnabled); item->setFlags(flags); return item; } /** * @return Create and return a checkable item for the table. */ QTableWidgetItem* SpecFileManagementDialog::createCheckableItem() { QTableWidgetItem* item = new QTableWidgetItem(); Qt::ItemFlags flags(Qt::ItemIsEnabled | Qt::ItemIsUserCheckable); item->setFlags(flags); item->setCheckState(Qt::Unchecked); return item; } #include "EventDataFileDelete.h" /** * Called when a push button was added using addUserPushButton(). * Subclasses MUST override this if user push buttons were * added using addUserPushButton(). * * @param userPushButton * User push button that was pressed. * @return * The result that indicates action that should be taken * as a result of the button being pressed. */ WuQDialogModal::DialogUserButtonResult SpecFileManagementDialog::userButtonPressed(QPushButton* userPushButton) { if (userPushButton == m_loadScenesPushButton) { /* * Load all of the scene files but nothing else */ m_specFile->setAllSceneFilesSelectedForLoadingAndAllOtherFilesNotSelected(); okButtonClickedOpenSpecFile(); GuiManager::get()->processShowSceneDialog(GuiManager::get()->getActiveBrowserWindow()); return RESULT_MODAL_ACCEPT; } else { CaretAssert(0); } return RESULT_NONE; } /** * Gets called when the OK button is pressed. */ void SpecFileManagementDialog::okButtonClicked () { bool allowDialogToClose = false; switch (m_dialogMode) { case MODE_MANAGE_FILES: allowDialogToClose = okButtonClickedManageAndSaveFiles(); break; case MODE_OPEN_SPEC_FILE: okButtonClickedOpenSpecFile(); allowDialogToClose = true; break; case MODE_SAVE_FILES_WHILE_QUITTING: allowDialogToClose = okButtonClickedManageAndSaveFiles(); break; } if (allowDialogToClose) { WuQDialogModal::okButtonClicked(); } else { getDataFileContentFromSpecFile(); loadSpecFileContentIntoDialog(); updateGraphicWindowsAndUserInterface(); } } /** * Perform processing when the Open button is pressed for Open Spec File mode. */ void SpecFileManagementDialog::okButtonClickedOpenSpecFile() { AString username; AString password; if (m_specFile->hasFilesWithRemotePathSelectedForLoading()) { const QString msg("This spec file 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)) { /* nothing */ } else { return; } } AString specFileErrorMessage = writeSpecFile(true); AString errorMessages; errorMessages.appendWithNewLine(specFileErrorMessage); EventSpecFileReadDataFiles readSpecFileEvent(m_brain, m_specFile); readSpecFileEvent.setUsernameAndPassword(username, password); ProgressReportingDialog::runEvent(&readSpecFileEvent, this, m_specFile->getFileNameNoPath()); errorMessages.appendWithNewLine(readSpecFileEvent.getErrorMessage()); updateGraphicWindowsAndUserInterface(); if (errorMessages.isEmpty() == false) { WuQMessageBox::errorOk(this, errorMessages); } } /** * Perform processing when the Open button is pressed for Manage Files * or Save Files mode. */ bool SpecFileManagementDialog::okButtonClickedManageAndSaveFiles() { /* * Wait cursor */ CursorDisplayScoped cursor; cursor.showWaitCursor(); AString errorMessages; const int32_t numDataFiles = static_cast(m_tableRowDataFileContent.size()); for (int32_t i = 0; i < numDataFiles; i++) { SpecFileDataFile* specFileDataFile = m_tableRowDataFileContent[i]->m_specFileDataFile; CaretAssert(specFileDataFile); if (specFileDataFile->isSavingSelected()) { CaretDataFile* caretDataFile = specFileDataFile->getCaretDataFile(); if (caretDataFile != NULL) { if (caretDataFile->supportsWriting()) { try { m_brain->writeDataFile(caretDataFile); specFileDataFile->setSavingSelected(false); } catch (const DataFileException& e) { errorMessages.appendWithNewLine(e.whatString()); } } } } } CaretAssert(m_COLUMN_SAVE_CHECKBOX >= 0); QTableWidgetItem* saveItem = getTableWidgetItem(m_specFileTableRowIndex, m_COLUMN_SAVE_CHECKBOX); if (saveItem->checkState() == Qt::Checked) { AString specFileName = m_specFile->getFileName(); if (m_specFile->getFileName().isEmpty()) { errorMessages.appendWithNewLine("Spec File name is empty."); } else { m_specFile->removeAnyFileInformationIfNotInSpecAndNoCaretDataFile(); AString specFileErrorMessage = writeSpecFile(false); if (specFileErrorMessage.isEmpty() == false) { errorMessages.appendWithNewLine(specFileErrorMessage); } else { } saveItem->setCheckState(Qt::Unchecked); } } /* * Spec file may have changed by SpecFile::removeAnyFileInformationIfNotInSpecAndNoCaretDataFile(). */ getDataFileContentFromSpecFile(); updateTableDimensionsToFitFiles(); loadSpecFileContentIntoDialog(); cursor.restoreCursor(); if (errorMessages.isEmpty() == false) { WuQMessageBox::errorOk(this, errorMessages); return false; } bool allowDialogToClose = false; switch (m_dialogMode) { case MODE_MANAGE_FILES: break; case MODE_OPEN_SPEC_FILE: break; case MODE_SAVE_FILES_WHILE_QUITTING: allowDialogToClose = true; break; } EventManager::get()->sendEvent(EventUserInterfaceUpdate().getPointer()); return allowDialogToClose; } /** * Write the spec file if it is modified. * * @param writeOnlyIfModified * Write only if the spec file is modified. * @return Non-empty string if there was an error writing the spec file. */ AString SpecFileManagementDialog::writeSpecFile(const bool writeOnlyIfModified) { if (writeOnlyIfModified) { if (m_specFile->isModified() == false) { return ""; } } AString errorMessage; try { m_specFile->writeFile(m_specFile->getFileName()); CaretPreferences* prefs = SessionManager::get()->getCaretPreferences(); prefs->addToPreviousSpecFiles(m_specFile->getFileName()); } catch (const DataFileException& e) { errorMessage = e.whatString(); } return errorMessage; } /** * Called when a file remove button is clicked. * * @param rowIndex * Row index of the item selected. */ void SpecFileManagementDialog::fileRemoveActionSelected(int rowIndex) { QWidget* removeButtonWidget = m_filesTableWidget->cellWidget(rowIndex, m_COLUMN_CLOSE_BUTTON); CaretAssert(removeButtonWidget); bool updateFlag = false; if (rowIndex == m_sceneAnnotationFileRowIndex) { AnnotationFile* sceneAnnotationFile = m_brain->getSceneAnnotationFile(); CaretAssert(sceneAnnotationFile); if ( ! sceneAnnotationFile->isEmpty()) { const QString msg = ("Remove all scene annotations?"); if ( ! WuQMessageBox::warningOkCancel(removeButtonWidget, msg)) { return; } sceneAnnotationFile->clear(); updateFlag = true; } } else { SpecFileManagementDialogRowContent* rowContent = getFileContentInRow(rowIndex); SpecFileDataFile* specFileDataFile = rowContent->m_specFileDataFile; CaretDataFile* caretDataFile = specFileDataFile->getCaretDataFile(); if (caretDataFile != NULL) { if (caretDataFile->isModified()) { const QString msg = (caretDataFile->getFileNameNoPath() + " is modified. Close without saving changes?"); if ( ! WuQMessageBox::warningOkCancel(removeButtonWidget, msg)) { return; } } if (m_COLUMN_SAVE_CHECKBOX >= 0) { /* * Turn of save check box in case file was modified to prevent * crash if user hits save file button */ QTableWidgetItem* saveItem = getTableWidgetItem(rowIndex, m_COLUMN_SAVE_CHECKBOX); CaretAssert(saveItem); saveItem->setCheckState(WuQtUtilities::boolToCheckState(false)); } EventManager::get()->sendEvent(EventDataFileDelete(caretDataFile).getPointer()); updateFlag = true; } } if (updateFlag) { getDataFileContentFromSpecFile(); loadSpecFileContentIntoDialog(); updateGraphicWindowsAndUserInterface(); } } /** * Get the content for the given row. * * @param rowIndex * Index of the row. * @return * Content for the given row. */ SpecFileManagementDialogRowContent* SpecFileManagementDialog::getFileContentInRow(const int rowIndex) { const int numDataFiles = static_cast(m_tableRowDataFileContent.size()); for (int32_t i = 0; i < numDataFiles; i++) { if (m_tableRowDataFileContent[i]->m_tableRowIndex == rowIndex) { return m_tableRowDataFileContent[i]; } } CaretAssertMessage(0, ("Invalid data file rowIndex (0 is spec file!!!): " + AString::number(rowIndex))); return NULL; } /** * Called when a file reload or open button is clicked. * * @param rowIndex * Index of the SpecFileDataFile item. */ void SpecFileManagementDialog::fileReloadOrOpenFileActionSelected(int rowIndex) { SpecFileManagementDialogRowContent* rowContent = getFileContentInRow(rowIndex); SpecFileDataFile* specFileDataFile = rowContent->m_specFileDataFile; QWidget* toolButtonWidget = m_filesTableWidget->cellWidget(rowIndex, m_COLUMN_READ_BUTTON); CaretAssert(toolButtonWidget); AString errorMessage; CaretDataFile* caretDataFile = specFileDataFile->getCaretDataFile(); if (caretDataFile != NULL) { if (caretDataFile->isModified()) { const QString msg = (caretDataFile->getFileNameNoPath() + " is modified. Reopen without saving changes?"); if (WuQMessageBox::warningOkCancel(toolButtonWidget, msg) == false) { return; } } AString username; AString password; if (DataFile::isFileOnNetwork(caretDataFile->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(this, "Username and Password", msg, username, password)) { /* nothing */ } else { return; } } specFileDataFile->setSavingSelected(false); EventDataFileReload reloadEvent(m_brain, caretDataFile); reloadEvent.setUsernameAndPassword(username, password); EventManager::get()->sendEvent(reloadEvent.getPointer()); if (reloadEvent.isError()) { errorMessage.appendWithNewLine(reloadEvent.getErrorMessage()); } } else { AString username; AString password; if (DataFile::isFileOnNetwork(specFileDataFile->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(this, "Username and Password", msg, username, password)) { /* nothing */ } else { return; } } EventDataFileRead readEvent(m_brain); readEvent.setUsernameAndPassword(username, password); readEvent.addDataFile(specFileDataFile->getStructure(), specFileDataFile->getDataFileType(), specFileDataFile->getFileName()); EventManager::get()->sendEvent(readEvent.getPointer()); if (readEvent.isError()) { errorMessage.appendWithNewLine(readEvent.getErrorMessage()); } } updateGraphicWindowsAndUserInterface(); loadSpecFileContentIntoDialog(); if (errorMessage.isEmpty() == false) { WuQMessageBox::errorOk(toolButtonWidget, errorMessage); } } /** * Updates graphics windows and user interface */ void SpecFileManagementDialog::updateGraphicWindowsAndUserInterface() { EventManager::get()->sendEvent(EventSurfaceColoringInvalidate().getPointer()); EventManager::get()->sendEvent(EventUserInterfaceUpdate().getPointer()); EventManager::get()->sendEvent(EventGraphicsUpdateAllWindows().getPointer()); } /** * Called when a file options button is clicked. * * @param rowIndex * Index of the SpecFileDataFile item. */ void SpecFileManagementDialog::fileOptionsActionSelected(int rowIndex) { m_filesTableWidget->setRangeSelected(QTableWidgetSelectionRange(rowIndex, m_COLUMN_OPTIONS_TOOLBUTTON, rowIndex, m_COLUMN_OPTIONS_TOOLBUTTON), false); const AString copyPathText("Copy Path and File Name to Clipboard"); if (rowIndex == m_specFileTableRowIndex) { QMenu menu; QAction* copyFilePathToClipboardAction = NULL; if (m_specFile != NULL) { if ( ! m_specFile->getFileName().trimmed().isEmpty()) { copyFilePathToClipboardAction = menu.addAction(copyPathText); } } QAction* editMetaDataAction = menu.addAction("Edit Metadata...");; QAction* setFileNameAction = menu.addAction("Set File Name..."); QAction* selectedAction = menu.exec(QCursor::pos()); if (selectedAction == NULL) { /* * If the selected action is NULL, it indicates that the user did * not make a selection. This test is needed as some of the actions * (such as setFileNameAction) may be NULL and with out this test, * those NULL actions would match. */ } else if (selectedAction == setFileNameAction) { changeFileName(&menu, NULL, m_specFile); } else if (selectedAction == editMetaDataAction) { MetaDataEditorDialog mded(m_specFile, &menu); mded.exec(); loadSpecFileContentIntoDialog(); } else if (selectedAction == copyFilePathToClipboardAction) { QApplication::clipboard()->setText(m_specFile->getFileName().trimmed(), QClipboard::Clipboard); } else if (selectedAction != NULL) { CaretAssertMessage(0, ("Unhandled Menu Action: " + selectedAction->text())); } } else { const bool sceneAnnotationFileFlag = (rowIndex == m_sceneAnnotationFileRowIndex); SpecFileDataFile* specFileDataFile = NULL; CaretDataFile* caretDataFile = NULL; if (sceneAnnotationFileFlag) { caretDataFile = m_brain->getSceneAnnotationFile(); if (caretDataFile->isEmpty()) { WuQMessageBox::informationOk(this, "There are no scene annotations"); return; } } else { SpecFileManagementDialogRowContent* rowContent = getFileContentInRow(rowIndex); specFileDataFile = rowContent->m_specFileDataFile; caretDataFile = specFileDataFile->getCaretDataFile(); } QAction* copyFilePathToClipboardAction = NULL; QAction* copyMoveFileContentAction = NULL; QAction* editMetaDataAction = NULL; QAction* setFileNameAction = NULL; QAction* setStructureAction = NULL; QAction* unloadFileMapsAction = NULL; QAction* viewMetaDataAction = NULL; QMenu menu; switch (m_dialogMode) { case MODE_MANAGE_FILES: case MODE_SAVE_FILES_WHILE_QUITTING: if (caretDataFile != NULL) { DataFileContentCopyMoveInterface* copyMoveInterface = dynamic_cast(caretDataFile); if (copyMoveInterface != NULL) { copyMoveFileContentAction = menu.addAction("Copy/Move Content..."); } if ( ! sceneAnnotationFileFlag) { copyFilePathToClipboardAction = menu.addAction(copyPathText); editMetaDataAction = menu.addAction("Edit Metadata..."); setFileNameAction = menu.addAction("Set File Name..."); } } else if ( ! sceneAnnotationFileFlag) { copyFilePathToClipboardAction = menu.addAction(copyPathText); } break; case MODE_OPEN_SPEC_FILE: if ( ! sceneAnnotationFileFlag) { copyFilePathToClipboardAction = menu.addAction(copyPathText); } break; } if ( ! menu.actions().isEmpty()) { QAction* selectedAction = menu.exec(QCursor::pos()); if (selectedAction == NULL) { /* * If the selected action is NULL, it indicates that the user did * not make a selection. This test is needed as some of the actions * (such as setFileNameAction) may be NULL and with out this test, * those NULL actions would match. */ } else if (selectedAction == copyMoveFileContentAction) { copyMoveFileContent(&menu, caretDataFile); } else if (selectedAction == copyFilePathToClipboardAction) { copyFilePathToClipboard(specFileDataFile, caretDataFile); } else if (selectedAction == setFileNameAction) { changeFileName(&menu, specFileDataFile, caretDataFile); } else if (selectedAction == setStructureAction) { CaretAssert(0); } else if (selectedAction == unloadFileMapsAction) { } else if (selectedAction == editMetaDataAction) { if (caretDataFile != NULL) { MetaDataEditorDialog mded(caretDataFile, &menu); mded.exec(); if (caretDataFile->isModified()) { specFileDataFile->setSavingSelected(true); } loadSpecFileContentIntoDialog(); } } else if (selectedAction == viewMetaDataAction) { } else if (selectedAction != NULL) { CaretAssertMessage(0, ("Unhandled Menu Action: " + selectedAction->text())); } } } } /** * Copy the loaded file's path to the clipboard. * * @param caretDataFile * The data file. */ void SpecFileManagementDialog::copyFilePathToClipboard(const SpecFileDataFile* specFileDataFile, const CaretDataFile* caretDataFile) { if (caretDataFile != NULL) { QApplication::clipboard()->setText(caretDataFile->getFileName().trimmed(), QClipboard::Clipboard); } else if (specFileDataFile != NULL) { QApplication::clipboard()->setText(specFileDataFile->getFileName().trimmed(), QClipboard::Clipboard); } else { CaretAssert(0); } } /** * Copy or move content of the data file to another data file of same type. * * @param parent * Widget on which copy/move dialog is displayed. * @param caretDataFile * Caret Data File that is having is content copied or moved */ void SpecFileManagementDialog::copyMoveFileContent(QWidget* parent, CaretDataFile* caretDataFile) { CaretAssert(caretDataFile); DataFileContentCopyMoveInterface* copyMoveInterfaceFile = dynamic_cast(caretDataFile); CaretAssert(copyMoveInterfaceFile); Brain* brain = GuiManager::get()->getBrain(); std::vector copyMoveDataFiles; if (caretDataFile->getDataFileType() == DataFileTypeEnum::ANNOTATION) { std::vector annotationFiles; brain->getAllAnnotationFilesIncludingSceneAnnotationFile(annotationFiles); copyMoveDataFiles.insert(copyMoveDataFiles.end(), annotationFiles.begin(), annotationFiles.end()); } else { brain->getAllDataFilesWithDataFileType(caretDataFile->getDataFileType(), copyMoveDataFiles); } std::vector copyMoveDestinationFiles; for (std::vector::iterator fileIter = copyMoveDataFiles.begin(); fileIter != copyMoveDataFiles.end(); fileIter++) { DataFileContentCopyMoveInterface* cmFile = dynamic_cast(*fileIter); if (cmFile != NULL) { copyMoveDestinationFiles.push_back(cmFile); } } DataFileContentCopyMoveDialog copyMoveDialog(m_parentBrainBrowserWindow->getBrowserWindowIndex(), copyMoveInterfaceFile, copyMoveDestinationFiles, parent); copyMoveDialog.exec(); /* * Spec file has changed */ getDataFileContentFromSpecFile(); /* * Table may need to add/remove rows */ updateTableDimensionsToFitFiles(); /* * Update the table rows with data */ loadSpecFileContentIntoDialog(); /* * Files have changed */ updateGraphicWindowsAndUserInterface(); } /** * Change the name of a file. * * @param parent * Widget on which file dialog is displayed. * @param specFileDataFile * Entry in spec file (NULL if caretDataFile is spec file). * @param caretDataFileIn * Caret Data File that is having name changed. */ void SpecFileManagementDialog::changeFileName(QWidget* parent, SpecFileDataFile* specFileDataFile, CaretDataFile* caretDataFileIn) { CaretDataFile* caretDataFile = caretDataFileIn; CaretAssert(caretDataFile); if (caretDataFile != m_specFile) { CaretAssert(m_specFile); } const AString newFileName = CaretFileDialog::getChooseFileNameDialog(caretDataFile->getDataFileType(), caretDataFile->getFileName(), parent); if (newFileName.isEmpty()) { return; } // QStringList filenameFilterList; // filenameFilterList.append(DataFileTypeEnum::toQFileDialogFilter(caretDataFile->getDataFileType())); // CaretFileDialog fd(parent); // fd.setAcceptMode(CaretFileDialog::AcceptSave); // fd.setNameFilters(filenameFilterList); // fd.setFileMode(CaretFileDialog::AnyFile); // fd.setViewMode(CaretFileDialog::List); // fd.selectFile(caretDataFile->getFileName()); // fd.setLabelText(CaretFileDialog::Accept, "Choose"); // fd.setWindowTitle("Choose File Name"); // if (fd.exec() == CaretFileDialog::Accepted) { // QStringList files = fd.selectedFiles(); // if (files.isEmpty() == false) { // AString newFileName = files.at(0); if (newFileName != caretDataFile->getFileName()) { /* * Clone current item, remove file from it, * and create new item. * * Note: specFileDataFile is NULL when changing the name * of the spec file. */ if (specFileDataFile != NULL) { SpecFileDataFile* sfdf = m_specFile->changeFileName(specFileDataFile, newFileName); caretDataFile = sfdf->getCaretDataFile(); CaretAssert(caretDataFile); if (caretDataFile->isModified()) { sfdf->setSavingSelected(true); } } caretDataFile->setFileName(newFileName); /* * Spec file has changed */ getDataFileContentFromSpecFile(); /* * Table may need to add/remove rows */ updateTableDimensionsToFitFiles(); /* * Update the table rows with data */ loadSpecFileContentIntoDialog(); /* * Files have changed */ updateGraphicWindowsAndUserInterface(); } // } // } } ///** // * Called when spec file options tool button is triggered. // */ //void //SpecFileManagementDialog::specFileOptionsActionTriggered() //{ // QAction* setFileNameAction = NULL; // // QMenu menu; // QAction* metadataAction = menu.addAction("Edit Metadata..."); // metadataAction->setEnabled(false); // switch (m_dialogMode) { // case MODE_MANAGE_FILES: // case MODE_SAVE_FILES_WHILE_QUITTING: // setFileNameAction = menu.addAction("Set File Name..."); // break; // case MODE_OPEN_SPEC_FILE: // break; // } // // QAction* selectedAction = menu.exec(QCursor::pos()); // // if (selectedAction == setFileNameAction) { // QStringList filenameFilterList; // filenameFilterList.append(DataFileTypeEnum::toQFileDialogFilter(DataFileTypeEnum::SPECIFICATION)); // CaretFileDialog fd(&menu); // fd.setAcceptMode(CaretFileDialog::AcceptSave); // fd.setNameFilters(filenameFilterList); // fd.setFileMode(CaretFileDialog::AnyFile); // fd.setViewMode(CaretFileDialog::List); // fd.selectFile(m_specFile->getFileName()); // fd.setLabelText(CaretFileDialog::Accept, "Choose"); // fd.setWindowTitle("Choose Spec File Name"); // if (fd.exec() == CaretFileDialog::Accepted) { // QStringList files = fd.selectedFiles(); // if (files.isEmpty() == false) { // AString newFileName = files.at(0); // m_specFile->setFileName(newFileName); // loadSpecFileContentIntoDialog(); // } // } // } // else if (selectedAction == metadataAction) { // // } // else if (selectedAction != NULL) { // CaretAssertMessage(0, // ("Unhandled Menu Action: " + selectedAction->text())); // } //} ///** // * Called to choose the name of the spec file. // */ //void //SpecFileManagementDialog::chooseSpecFileNameActionTriggered() //{ // QWidget* toolButtonWidget = m_filesTableWidget->cellWidget(m_specFileTableRowIndex, // m_COLUMN_OPTIONS_TOOLBUTTON); // CaretAssert(toolButtonWidget); // // QStringList filenameFilterList; // filenameFilterList.append(DataFileTypeEnum::toQFileDialogFilter(DataFileTypeEnum::SPECIFICATION)); // CaretFileDialog fd(toolButtonWidget); // fd.setAcceptMode(CaretFileDialog::AcceptSave); // fd.setNameFilters(filenameFilterList); // fd.setFileMode(CaretFileDialog::AnyFile); // fd.setViewMode(CaretFileDialog::List); // fd.selectFile(m_specFile->getFileName()); // fd.setLabelText(CaretFileDialog::Accept, "Choose"); // fd.setWindowTitle("Choose Spec File Name"); // if (fd.exec() == CaretFileDialog::Accepted) { // QStringList files = fd.selectedFiles(); // if (files.isEmpty() == false) { // AString newFileName = files.at(0); // m_specFile->setFileName(newFileName); // loadSpecFileContentIntoDialog(); // } // } //} /** * @return Create and return a toolbar for viewing files by type of file. * @param labelOut * If text was not empty, this parameter will be set to the QLabel that * contains the text. Otherwise, it will be NULL upon exit. */ QToolBar* SpecFileManagementDialog::createFilesTypesToolBar(QLabel* &labelOut) { m_fileTypesActionGroup = new QActionGroup(this); m_fileTypesActionGroup->setExclusive(true); QObject::connect(m_fileTypesActionGroup, SIGNAL(triggered(QAction*)), this, SLOT(toolBarFileTypeActionTriggered(QAction*))); QAction* fileTypeAllAction = m_fileTypesActionGroup->addAction("All"); fileTypeAllAction->setCheckable(true); fileTypeAllAction->setData(qVariantFromValue(DataFileTypeEnum::toIntegerCode(DataFileTypeEnum::UNKNOWN))); /* * All types of files */ std::vector allDataFileTypes; DataFileTypeEnum::getAllEnums(allDataFileTypes, DataFileTypeEnum::OPTIONS_INCLUDE_UNKNOWN); /* * Get data types of files that are listed in the dialog */ std::set loadedDataFileTypes; const int32_t numFiles = static_cast(m_tableRowDataFileContent.size()); for (int32_t i = 0; i < numFiles; i++) { SpecFileDataFile* sfdf = m_tableRowDataFileContent[i]->m_specFileDataFile; loadedDataFileTypes.insert(sfdf->getDataFileType()); } for (std::vector::iterator iter = allDataFileTypes.begin(); iter != allDataFileTypes.end(); iter++) { DataFileTypeEnum::Enum dataFileType = *iter; /* * Only list file types if listed in dialog */ if (dataFileType == DataFileTypeEnum::SPECIFICATION) { continue; } if (std::find(loadedDataFileTypes.begin(), loadedDataFileTypes.end(), dataFileType) == loadedDataFileTypes.end()) { continue; } const AString text = getEditedDataFileTypeName(dataFileType); QAction* action = m_fileTypesActionGroup->addAction(text); action->setCheckable(true); action->setData(qVariantFromValue(DataFileTypeEnum::toIntegerCode(dataFileType))); } if (m_fileTypesActionGroup->actions().isEmpty() == false) { m_fileTypesActionGroup->blockSignals(true); m_fileTypesActionGroup->actions().at(0)->setChecked(true); m_fileTypesActionGroup->blockSignals(false); } QToolBar* toolbar = createToolBarWithActionGroup("View File Types: ", labelOut, m_fileTypesActionGroup); return toolbar; } /** * @return Create and return a toolbar for selecting all or no files. * @param labelOut * If text was not empty, this parameter will be set to the QLabel that * contains the text. Otherwise, it will be NULL upon exit. */ QToolBar* SpecFileManagementDialog::createFilesSelectionToolBar(QLabel* &labelOut) { // * When loading, ALL or NONE but only ones that are visibleRegion() m_fileSelectionActionGroup = new QActionGroup(this); QObject::connect(m_fileSelectionActionGroup, SIGNAL(triggered(QAction*)), this, SLOT(toolBarSelectFilesActionTriggered(QAction*))); QAction* allFilesAction = m_fileSelectionActionGroup->addAction("All"); allFilesAction->setData(qVariantFromValue(SHOW_FILES_ALL)); QAction* noneFilesAction = m_fileSelectionActionGroup->addAction("None"); noneFilesAction->setData(qVariantFromValue(SHOW_FILES_NONE)); QToolBar* toolbar = createToolBarWithActionGroup("Select Files: ", labelOut, m_fileSelectionActionGroup); return toolbar; } /** * @return Create and return a toolbar for selecting loaded or not loaded files. * @param labelOut * If text was not empty, this parameter will be set to the QLabel that * contains the text. Otherwise, it will be NULL upon exit. */ QToolBar* SpecFileManagementDialog::createManageFilesLoadedNotLoadedToolBar(QLabel* &labelOut) { m_manageFilesLoadedNotLoadedActionGroup = new QActionGroup(this); QObject::connect(m_manageFilesLoadedNotLoadedActionGroup, SIGNAL(triggered(QAction*)), this, SLOT(toolBarManageFilesLoadedNotLoadedActionTriggered(QAction*))); QAction* allFilesAction = m_manageFilesLoadedNotLoadedActionGroup->addAction("All"); allFilesAction->setData(qVariantFromValue((int)MANAGE_FILES_ALL)); allFilesAction->setCheckable(true); QAction* loadedFilesAction = m_manageFilesLoadedNotLoadedActionGroup->addAction("Loaded"); loadedFilesAction->setData(qVariantFromValue((int)MANAGE_FILES_LOADED)); loadedFilesAction->setCheckable(true); QAction* loadedFilesModifiedAction = m_manageFilesLoadedNotLoadedActionGroup->addAction("Loaded:Modified"); loadedFilesModifiedAction->setData(qVariantFromValue((int)MANAGE_FILES_LOADED_MODIFIED)); loadedFilesModifiedAction->setCheckable(true); QAction* loadedFilesNotModifiedAction = m_manageFilesLoadedNotLoadedActionGroup->addAction("Loaded:Not Modified"); loadedFilesNotModifiedAction->setData(qVariantFromValue((int)MANAGE_FILES_LOADED_NOT_MODIFIED)); loadedFilesNotModifiedAction->setCheckable(true); QAction* notLoadedFilesAction = m_manageFilesLoadedNotLoadedActionGroup->addAction("Not Loaded"); notLoadedFilesAction->setData(qVariantFromValue((int)MANAGE_FILES_NOT_LOADED)); notLoadedFilesAction->setCheckable(true); m_manageFilesLoadedNotLoadedActionGroup->blockSignals(true); switch (m_dialogMode) { case MODE_MANAGE_FILES: allFilesAction->setChecked(true); break; case MODE_OPEN_SPEC_FILE: allFilesAction->setChecked(true); break; case MODE_SAVE_FILES_WHILE_QUITTING: loadedFilesModifiedAction->setChecked(true); break; } m_manageFilesLoadedNotLoadedActionGroup->blockSignals(false); QToolBar* toolbar = createToolBarWithActionGroup("View Files: ", labelOut, m_manageFilesLoadedNotLoadedActionGroup); return toolbar; } /** * @return Edit and return the text for a data file type. */ 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 Create and return a toolbar for viewing files by structure. */ QToolBar* SpecFileManagementDialog::createStructureToolBar(QLabel* &labelOut) { std::vector structureTypes; structureTypes.push_back(StructureEnum::ALL); structureTypes.push_back(StructureEnum::CORTEX_LEFT); structureTypes.push_back(StructureEnum::CORTEX_RIGHT); structureTypes.push_back(StructureEnum::CEREBELLUM); structureTypes.push_back(StructureEnum::OTHER); m_structureActionGroup = new QActionGroup(this); m_structureActionGroup->setExclusive(true); QObject::connect(m_structureActionGroup, SIGNAL(triggered(QAction*)), this, SLOT(toolBarStructuresActionTriggered(QAction*))); for (std::vector::iterator iter = structureTypes.begin(); iter != structureTypes.end(); iter++) { StructureEnum::Enum structure = *iter; QAction* action = m_structureActionGroup->addAction(StructureEnum::toGuiName(structure)); action->setCheckable(true); action->setData(qVariantFromValue(StructureEnum::toIntegerCode(structure))); } if (m_structureActionGroup->actions().isEmpty() == false) { m_structureActionGroup->blockSignals(true); m_structureActionGroup->actions().at(0)->setChecked(true); m_structureActionGroup->blockSignals(false); } QToolBar* toolbar = createToolBarWithActionGroup("View Structures: ", labelOut, m_structureActionGroup); return toolbar; } /** * Create a toolbar with the given label containing all items * in the given action group. * * @param text * If not empty, this text is inserted into the left side of the toolbar. * @param labelOut * If text was not empty, this parameter will be set to the QLabel that * contains the text. Otherwise, it will be NULL upon exit. * @param actionGroup * All actions from this action group are added to the toolbar. * @return * The toolbar. */ QToolBar* SpecFileManagementDialog::createToolBarWithActionGroup(const QString& text, QLabel* &labelOut, QActionGroup* actionGroup) { QToolBar* toolbar = new QToolBar(); if (text.isEmpty() == false) { labelOut = new QLabel(text); labelOut->setAlignment(Qt::AlignLeft); toolbar->addWidget(labelOut); } else { labelOut = NULL; } QList actions = actionGroup->actions(); QListIterator iterator(actions); while (iterator.hasNext()) { toolbar->addAction(iterator.next()); } return toolbar; } /** * Called when a tool bar's file type button is selected. * * @param action * QAction of item selected. */ void SpecFileManagementDialog::toolBarFileTypeActionTriggered(QAction* /*action*/) { // if (action != NULL) { // const int dataValue = action->data().toInt(); // bool isValid = false; // const DataFileTypeEnum::Enum dataFileType = DataFileTypeEnum::fromIntegerCode(dataValue, // &isValid); // } loadSpecFileContentIntoDialog(); } /** * Called when tool bar's structure button is selected. * * @param action * QAction of item selected. */ void SpecFileManagementDialog::toolBarStructuresActionTriggered(QAction* /*action*/) { // if (action != NULL) { // const int dataValue = action->data().toInt(); // bool isValid = false; // const StructureEnum::Enum structure = StructureEnum::fromIntegerCode(dataValue, // &isValid); // } loadSpecFileContentIntoDialog(); } /** * Show loaded/not loaded files when manage files mode. * * @param action * QAction of item selected. */ void SpecFileManagementDialog::toolBarManageFilesLoadedNotLoadedActionTriggered(QAction* /*action*/) { loadSpecFileContentIntoDialog(); } /** * Set all files as selected. * * @param action * QAction of item selected. */ void SpecFileManagementDialog::toolBarSelectFilesActionTriggered(QAction* action) { // m_filesTableWidget->blockSignals(true); if (action != NULL) { const int dataValue = action->data().toInt(); bool newStatus = false; if (dataValue == SHOW_FILES_ALL) { newStatus = true; } else if (dataValue == SHOW_FILES_NONE) { newStatus = false; } const int32_t numFiles = static_cast(m_tableRowDataFileContent.size()); for (int32_t i = 0; i < numFiles; i++) { const int32_t rowIndex = m_tableRowDataFileContent[i]->m_tableRowIndex; if (m_filesTableWidget->isRowHidden(rowIndex) == false) { if (m_COLUMN_LOAD_CHECKBOX >= 0) { QTableWidgetItem* loadItem = getTableWidgetItem(rowIndex, m_COLUMN_LOAD_CHECKBOX); CaretAssert(loadItem); loadItem->setCheckState(WuQtUtilities::boolToCheckState(newStatus)); } if (m_COLUMN_SAVE_CHECKBOX >= 0) { QTableWidgetItem* saveItem = getTableWidgetItem(rowIndex, m_COLUMN_SAVE_CHECKBOX); CaretAssert(saveItem); saveItem->setCheckState(WuQtUtilities::boolToCheckState(newStatus)); } } } } // m_filesTableWidget->blockSignals(false); } /* ======================================================================= */ /** * \class caret::SpecFileManagementDialogRowContent * \brief Content of a row in the SpecFileManagementDialog. * \ingroup GuiQt */ SpecFileManagementDialogRowContent::SpecFileManagementDialogRowContent(SpecFileDataFileTypeGroup* specFileDataFileTypeGroup, SpecFileDataFile* specFileDataFile) { m_tableRowIndex = -1; m_specFileDataFileTypeGroup = specFileDataFileTypeGroup; m_specFileDataFile = specFileDataFile; } SpecFileManagementDialogRowContent::~SpecFileManagementDialogRowContent() { } /** * Less than method for sorting using the sorting key. * * @param item1 * Tested for less than the item2 * @param item2 * Tested for greater than the item1 * @return * True if item1 is less than item2, else false. */ bool SpecFileManagementDialogRowContent::lessThanForSorting(const SpecFileManagementDialogRowContent* item1, const SpecFileManagementDialogRowContent* item2) { return (item1->m_sortingKey < item2->m_sortingKey); } /** * Set the sorting key for the given sorting type prior to sorting. * Creates a text string that is used for sorting that is used by * the static sorting method. * * @param sorting * Type of sorting. */ void SpecFileManagementDialogRowContent::setSortingKey(const Sorting sorting) { FileInformation fileInfo(m_specFileDataFile->getFileName()); const QString filename = fileInfo.getFileName().toUpper(); const DataFileTypeEnum::Enum dataFileType = m_specFileDataFile->getDataFileType(); QString typeName = SpecFileManagementDialog::getEditedDataFileTypeName(dataFileType); /* * Push surface files to the top??? */ // if (dataFileType == DataFileTypeEnum::SURFACE) { // typeName = "AAAAAA"; // } /* * Push non-specific structures to the bottom */ const StructureEnum::Enum structure = m_specFileDataFile->getStructure(); QString structureName = StructureEnum::toGuiName(structure); switch (structure) { case StructureEnum::ALL: case StructureEnum::ALL_GREY_MATTER: case StructureEnum::ALL_WHITE_MATTER: case StructureEnum::INVALID: case StructureEnum::OTHER: structureName = "zzzzzz"; break; default: break; } m_sortingKey = ""; switch (sorting) { case SORTING_TYPE_STRUCTURE_NAME: m_sortingKey = (typeName + structureName + filename); break; case SORTING_NAME: m_sortingKey = filename; break; case SORTING_STRUCTURE_TYPE_NAME: m_sortingKey = (structureName + typeName + filename); break; } } connectome-workbench-1.2.3+git41-gc4c6c90/src/GuiQt/SpecFileManagementDialog.h000066400000000000000000000251011300200146000265540ustar00rootroot00000000000000#ifndef __SPEC_FILE_MANAGEMENT_DIALOG_H__ #define __SPEC_FILE_MANAGEMENT_DIALOG_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 "CaretObject.h" #include "DataFileTypeEnum.h" #include "SpecFileDialogViewFilesTypeEnum.h" #include "StructureEnum.h" #include "WuQDialogModal.h" class QActionGroup; class QIcon; class QLabel; class QSignalMapper; class QTableWidget; class QTableWidgetItem; class QToolBar; namespace caret { class Brain; class BrainBrowserWindow; class CaretDataFile; class SpecFile; class SpecFileDataFile; class SpecFileDataFileTypeGroup; class SpecFileManagementDialogRowContent; /** * Data in a row of the table widget */ class SpecFileManagementDialogRowContent : public CaretObject { public: /** * Type of sorting */ enum Sorting { SORTING_TYPE_STRUCTURE_NAME, SORTING_STRUCTURE_TYPE_NAME, SORTING_NAME }; SpecFileManagementDialogRowContent(SpecFileDataFileTypeGroup* specFileDataFileTypeGroup, SpecFileDataFile* specFileDataFile); ~SpecFileManagementDialogRowContent(); void setSortingKey(const Sorting sorting); int m_tableRowIndex; SpecFileDataFileTypeGroup* m_specFileDataFileTypeGroup; SpecFileDataFile* m_specFileDataFile; QString m_sortingKey; static bool lessThanForSorting(const SpecFileManagementDialogRowContent* item1, const SpecFileManagementDialogRowContent* item2); }; class SpecFileManagementDialog : public WuQDialogModal { Q_OBJECT public: static bool runOpenSpecFileDialog(Brain* brain, SpecFile* specFile, BrainBrowserWindow* parent); static void runManageFilesDialog(Brain* brain, BrainBrowserWindow* parent); static bool runSaveFilesDialogWhileQuittingWorkbench(Brain* brain, BrainBrowserWindow* parent); virtual ~SpecFileManagementDialog(); protected: WuQDialogModal::DialogUserButtonResult userButtonPressed(QPushButton* userPushButton); private slots: void toolBarFileTypeActionTriggered(QAction* action); void toolBarStructuresActionTriggered(QAction* action); void toolBarSelectFilesActionTriggered(QAction* action); void toolBarManageFilesLoadedNotLoadedActionTriggered(QAction* action); // void chooseSpecFileNameActionTriggered(); // void specFileOptionsActionTriggered(); void fileReloadOrOpenFileActionSelected(int indx); void fileRemoveActionSelected(int indx); void fileOptionsActionSelected(int indx); void filesTableWidgetCellChanged(int row, int column); void horizontalHeaderSelectedForSorting(int logicalIndex); private: enum Mode { MODE_MANAGE_FILES, MODE_OPEN_SPEC_FILE, MODE_SAVE_FILES_WHILE_QUITTING }; enum ManageFilesDisplay { MANAGE_FILES_ALL, MANAGE_FILES_LOADED, MANAGE_FILES_LOADED_MODIFIED, MANAGE_FILES_LOADED_NOT_MODIFIED, MANAGE_FILES_NOT_LOADED }; SpecFileManagementDialog(const Mode dialogMode, Brain* brain, SpecFile* specFile, const AString& dialogTitle, BrainBrowserWindow* parent); SpecFileManagementDialog(const SpecFileManagementDialog&); SpecFileManagementDialog& operator=(const SpecFileManagementDialog&); BrainBrowserWindow* m_parentBrainBrowserWindow; QToolBar* createFilesTypesToolBar(QLabel* &labelOut); QToolBar* createFilesSelectionToolBar(QLabel* &labelOut); QToolBar* createManageFilesLoadedNotLoadedToolBar(QLabel* &labelOut); QToolBar* createStructureToolBar(QLabel* &labelOut); QToolBar* createToolBarWithActionGroup(const QString& text, QLabel* &labelOut, QActionGroup* actionGroup); static AString getEditedDataFileTypeName(const DataFileTypeEnum::Enum dataFileType); SpecFileManagementDialogRowContent* getFileContentInRow(const int rowIndex); void updateGraphicWindowsAndUserInterface(); void getDataFileContentFromSpecFile(); void loadSpecFileContentIntoDialog(); void loadDialogContentIntoSpecFile(); void updateSpecFileRowInTable(); void updateAnnotationSceneFileRowInTable(); void sortFileContent(); void setTableColumnLabels(); virtual void okButtonClicked(); void okButtonClickedOpenSpecFile(); bool okButtonClickedManageAndSaveFiles(); void changeFileName(QWidget* parent, SpecFileDataFile* specFileDataFile, CaretDataFile* caretDataFile); void copyMoveFileContent(QWidget* parent, CaretDataFile* caretDataFile); void updateTableDimensionsToFitFiles(); QTableWidgetItem* createHeaderTextItem(const QString& text); QTableWidgetItem* createTextItem(); QTableWidgetItem* createCheckableItem(); AString writeSpecFile(const bool writeOnlyIfModified); QTableWidgetItem* getTableWidgetItem(const int rowIndex, const int columnIndex); void setTableWidgetItem(const int rowIndex, const int columnIndex, QTableWidgetItem* item); void getFilterSelections(SpecFileDialogViewFilesTypeEnum::Enum& viewFilesTypeOut, DataFileTypeEnum::Enum& filteredDataFileTypeOut, StructureEnum::Enum& filteredStructureTypeOut) const; void setFilterSelections(const SpecFileDialogViewFilesTypeEnum::Enum viewFilesType, const DataFileTypeEnum::Enum filteredDataFileType, const StructureEnum::Enum filteredStructureType); void clearSpecFileManagementDialogRowContent(); void enableLoadOrSaveButton(); void copyFilePathToClipboard(const SpecFileDataFile* specFileDataFile, const CaretDataFile* caretDataFile); // ADD_NEW_MEMBERS_HERE const Mode m_dialogMode; Brain* m_brain; SpecFile* m_specFile; QPushButton* m_loadScenesPushButton; QActionGroup* m_fileTypesActionGroup; QActionGroup* m_fileSelectionActionGroup; QActionGroup* m_manageFilesLoadedNotLoadedActionGroup; QActionGroup* m_structureActionGroup; std::vector m_tableRowDataFileContent; int m_specFileTableRowIndex; int m_sceneAnnotationFileRowIndex; QSignalMapper* m_fileReloadOrOpenFileActionSignalMapper; QSignalMapper* m_fileCloseFileActionSignalMapper; QSignalMapper* m_fileOptionsActionSignalMapper; int m_specFileDataFileCounter; QTableWidget* m_filesTableWidget; SpecFileManagementDialogRowContent::Sorting m_fileSorting; QIcon* m_iconOptions; QIcon* m_iconOpenFile; QIcon* m_iconReloadFile; QIcon* m_iconCloseFile; std::set m_displayedDataFiles; static QByteArray s_manageFilesGeometry; static SpecFileDialogViewFilesTypeEnum::Enum s_manageFilesViewFilesType; static DataFileTypeEnum::Enum s_manageFilesFilteredDataFileType; static StructureEnum::Enum s_manageFilesFilteredStructureType; static const int SHOW_FILES_ALL; static const int SHOW_FILES_NONE; int m_COLUMN_LOAD_CHECKBOX; int m_COLUMN_SAVE_CHECKBOX; int m_COLUMN_STATUS_LABEL; int m_COLUMN_DISPLAYED_LABEL; int m_COLUMN_IN_SPEC_FILE_CHECKBOX; int m_COLUMN_READ_BUTTON; int m_COLUMN_CLOSE_BUTTON; int m_COLUMN_OPTIONS_TOOLBUTTON; int m_COLUMN_DATA_FILE_TYPE_LABEL; int m_COLUMN_STRUCTURE; int m_COLUMN_FILE_NAME_LABEL; int m_COLUMN_COUNT; friend class SpecFileManagementDialogRowContent; }; #ifdef __SPEC_FILE_MANAGEMENT_DIALOG_DECLARE__ QByteArray SpecFileManagementDialog::s_manageFilesGeometry; SpecFileDialogViewFilesTypeEnum::Enum SpecFileManagementDialog::s_manageFilesViewFilesType = SpecFileDialogViewFilesTypeEnum::VIEW_FILES_ALL; DataFileTypeEnum::Enum SpecFileManagementDialog::s_manageFilesFilteredDataFileType = DataFileTypeEnum::UNKNOWN; StructureEnum::Enum SpecFileManagementDialog::s_manageFilesFilteredStructureType = StructureEnum::ALL; const int SpecFileManagementDialog::SHOW_FILES_ALL = -1; const int SpecFileManagementDialog::SHOW_FILES_NONE = -2; #endif // __SPEC_FILE_MANAGEMENT_DIALOG_DECLARE__ } // namespace #endif //__SPEC_FILE_MANAGEMENT_DIALOG_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/GuiQt/SplashScreen.cxx000066400000000000000000000444131300200146000247210ustar00rootroot00000000000000 /*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 #include #include #include #define __SPLASH_SCREEN_DECLARE__ #include "SplashScreen.h" #undef __SPLASH_SCREEN_DECLARE__ #include "ApplicationInformation.h" #include "Brain.h" #include "CaretAssert.h" #include "CaretFileDialog.h" #include "CaretPreferences.h" #include "DataFile.h" #include "DataFileTypeEnum.h" #include "FileInformation.h" #include "GuiManager.h" #include "SessionManager.h" #include "WuQtUtilities.h" using namespace caret; /** * \class caret::SplashScreen * \brief Splash Screen display when Workbench is started. * \ingroup GuiQt */ /** * Constructor. */ SplashScreen::SplashScreen(QWidget* parent) : WuQDialogModal("", parent) { /* * Removes title bar */ // this->setWindowFlags(Qt::SplashScreen); QLabel* imageLabel = NULL; QPixmap pixmap; if (WuQtUtilities::loadPixmap(":/Splash/startup_image.png", pixmap)) { imageLabel = new QLabel(); imageLabel->setPixmap(pixmap); imageLabel->setAlignment(Qt::AlignCenter); } const QString labelStyle = ("QLabel { " " font: 20px bold " "}"); 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); QLabel* hcpWebsiteLabel = new QLabel("" "Visit
" "Human Connectome Project
" "Website" ""); hcpWebsiteLabel->setStyleSheet(labelStyle); hcpWebsiteLabel->setAlignment(Qt::AlignCenter); QObject::connect(hcpWebsiteLabel, SIGNAL(linkActivated(const QString&)), this, SLOT(websiteLinkActivated(const QString&))); QToolButton* twitterToolButton = NULL; QIcon twitterIcon; if (WuQtUtilities::loadIcon(":/Splash/twitter.png", twitterIcon)) { QAction* twitterAction = WuQtUtilities::createAction("Twitter", "Follow HCP on Twitter", this, this, SLOT(twitterActionTriggered())); twitterAction->setIcon(twitterIcon); twitterToolButton = new QToolButton(); twitterToolButton->setDefaultAction(twitterAction); } QStringList headerText; headerText.append("Spec File"); headerText.append("Path"); m_dataFileTreeWidget = new QTreeWidget(); m_dataFileTreeWidget->setHeaderLabels(headerText); QObject::connect(m_dataFileTreeWidget, SIGNAL(itemClicked(QTreeWidgetItem*,int)), this, SLOT(dataFileTreeWidgetItemClicked(QTreeWidgetItem*))); QObject::connect(m_dataFileTreeWidget, SIGNAL(itemDoubleClicked(QTreeWidgetItem*,int)), this, SLOT(dataFileTreeWidgetItemDoubleClicked(QTreeWidgetItem*))); QScrollArea* treeScrollArea = new QScrollArea(); treeScrollArea->setWidget(m_dataFileTreeWidget); treeScrollArea->setWidgetResizable(true); QWidget* leftWidget = new QWidget(); QVBoxLayout* leftColumnLayout = new QVBoxLayout(leftWidget); if (imageLabel != NULL) { leftColumnLayout->addWidget(imageLabel); leftColumnLayout->addSpacing(15); } leftColumnLayout->addWidget(workbenchLabel); leftColumnLayout->addWidget(versionLabel); leftColumnLayout->addSpacing(10); leftColumnLayout->addWidget(hcpWebsiteLabel); if (twitterToolButton != NULL) { leftColumnLayout->addSpacing(3); leftColumnLayout->addWidget(twitterToolButton, 0, Qt::AlignHCenter); } leftColumnLayout->addStretch(); QWidget* widget = new QWidget(); QHBoxLayout* horizLayout = new QHBoxLayout(widget); horizLayout->addWidget(leftWidget); horizLayout->addWidget(treeScrollArea); const int32_t treeDesiredWidth = loadDataFileTreeWidget(); m_openOtherSpecFilePushButton = addUserPushButton("Open Other...", QDialogButtonBox::AcceptRole); setCancelButtonText("Skip"); setOkButtonText("Open"); setCentralWidget(widget, WuQDialog::SCROLL_AREA_NEVER); /* * Set a minimum width so spec files are visible */ int totalWidth = (leftWidget->sizeHint().width() + treeDesiredWidth); const int maxWidth = std::max(WuQtUtilities::getMinimumScreenSize().width() - 300, 600); if (totalWidth > maxWidth) { totalWidth = maxWidth; } widget->setMinimumWidth(totalWidth); } /** * Destructor. */ SplashScreen::~SplashScreen() { } /** * @return The selected data file name. */ AString SplashScreen::getSelectedDataFileName() const { return m_selectedDataFileName; } /** * Called when a label's hyperlink is selected. * @param link * The URL. */ void SplashScreen::websiteLinkActivated(const QString& link) { if (link.isEmpty() == false) { QDesktopServices::openUrl(QUrl(link)); } } /** * Display twitter page in web browser. */ void SplashScreen::twitterActionTriggered() { websiteLinkActivated("http://twitter.com/#!/HumanConnectome"); } /** * Called when spec file tree widget item is clicked. * @param item * Item clicked. */ void SplashScreen::dataFileTreeWidgetItemClicked(QTreeWidgetItem* item) { m_selectedDataFileName = ""; if (item != NULL) { if ( ! item->isDisabled()) { m_selectedDataFileName = item->data(0, Qt::UserRole).toString(); } } } /** * Called when spec file tree widget item is double-clicked. * @param item * Item clicked. */ void SplashScreen::dataFileTreeWidgetItemDoubleClicked(QTreeWidgetItem* item) { m_selectedDataFileName = ""; if (item != NULL) { if ( ! item->isDisabled()) { m_selectedDataFileName = item->data(0, Qt::UserRole).toString(); /* * Accept is like hitting OK button */ this->accept(); } } } /** * Called when a push button was added using addUserPushButton(). * * @param userPushButton * User push button that was pressed. */ WuQDialogModal::DialogUserButtonResult SplashScreen::userButtonPressed(QPushButton* userPushButton) { if (userPushButton == m_openOtherSpecFilePushButton) { chooseDataFileViaOpenFileDialog(); } else { CaretAssertMessage(0, "Unrecognized user pushbutton clicked \"" + userPushButton->text() + "\""); } return WuQDialogModal::RESULT_NONE; } /** * Choose a data file with the Open File Dialog. */ void SplashScreen::chooseDataFileViaOpenFileDialog() { QStringList filenameFilterList; filenameFilterList.append(DataFileTypeEnum::toQFileDialogFilter(DataFileTypeEnum::SCENE)); filenameFilterList.append(DataFileTypeEnum::toQFileDialogFilter(DataFileTypeEnum::SPECIFICATION)); CaretFileDialog fd(this); fd.setAcceptMode(CaretFileDialog::AcceptOpen); fd.setNameFilters(filenameFilterList); fd.setFileMode(CaretFileDialog::ExistingFile); fd.setViewMode(CaretFileDialog::List); fd.selectNameFilter(DataFileTypeEnum::toQFileDialogFilter(DataFileTypeEnum::SPECIFICATION)); AString errorMessages; if (fd.exec()) { QStringList selectedFiles = fd.selectedFiles(); if (selectedFiles.empty() == false) { m_selectedDataFileName = selectedFiles.at(0); /* * Accept indicates user has 'accepted' the * dialog so dialog is closed (like OK clicked) */ accept(); } } } /** * Load Spec Files into the tree widget. */ int32_t SplashScreen::loadDataFileTreeWidget() { m_dataFileTreeWidget->clear(); QTreeWidgetItem* selectedItem = addDirectorySpecFiles(); if (selectedItem != NULL) { addDirectorySceneFiles(); } else { selectedItem = addDirectorySceneFiles(); } QTreeWidgetItem* specItem = addRecentSpecFiles(); if (specItem != NULL) { selectedItem = specItem; } QTreeWidgetItem* sceneItem = addRecentSceneFiles(); if (selectedItem == NULL) { selectedItem = sceneItem; } int nameColWidth = 0; int pathColWidth = 0; if (selectedItem != NULL) { m_dataFileTreeWidget->setCurrentItem(selectedItem); nameColWidth = m_dataFileTreeWidget->QAbstractItemView::sizeHintForColumn(0) + 25; pathColWidth = m_dataFileTreeWidget->QAbstractItemView::sizeHintForColumn(1); m_dataFileTreeWidget->setColumnWidth(0, nameColWidth); this->dataFileTreeWidgetItemClicked(selectedItem); } int treeWidgetWidth = (nameColWidth + pathColWidth); if (treeWidgetWidth < 250) { treeWidgetWidth = 250; } m_dataFileTreeWidget->setMinimumWidth(treeWidgetWidth); return treeWidgetWidth; } /** * Add recent spec files. */ QTreeWidgetItem* SplashScreen::addRecentSpecFiles() { CaretPreferences* prefs = SessionManager::get()->getCaretPreferences(); std::vector recentSpecFiles; prefs->getPreviousSpecFiles(recentSpecFiles); QTreeWidgetItem* firstItem = NULL; const int32_t numRecentSpecFiles = static_cast(recentSpecFiles.size()); for (int32_t i = 0; i < numRecentSpecFiles; i++) { QString path; QString name; QString fullPath; if (firstItem == NULL) { QStringList itemText; itemText.append("Recent Spec Files"); itemText.append("-------------------------------"); QTreeWidgetItem* titleItem = new QTreeWidgetItem(itemText); titleItem->setDisabled(true); m_dataFileTreeWidget->addTopLevelItem(titleItem); } const QString specFileName = recentSpecFiles[i]; if (DataFile::isFileOnNetwork(specFileName)) { const int lastSlash = specFileName.lastIndexOf('/'); name = specFileName.mid(lastSlash + 1); path = specFileName.left(lastSlash); fullPath = specFileName; } else { FileInformation fileInfo(specFileName); path = fileInfo.getPathName().trimmed(); name = fileInfo.getFileName().trimmed(); fullPath = fileInfo.getAbsoluteFilePath(); } if (name.isEmpty() == false) { QStringList treeText; treeText.append(" " + name); treeText.append(path); QTreeWidgetItem* lwi = new QTreeWidgetItem(treeText); lwi->setData(0, Qt::UserRole, fullPath); lwi->setData(1, Qt::UserRole, fullPath); m_dataFileTreeWidget->addTopLevelItem(lwi); if (firstItem == NULL) { firstItem = lwi; } } } return firstItem; } /** * Add recent scene files. */ QTreeWidgetItem* SplashScreen::addRecentSceneFiles() { CaretPreferences* prefs = SessionManager::get()->getCaretPreferences(); std::vector recentSceneFiles; prefs->getPreviousSceneFiles(recentSceneFiles); QTreeWidgetItem* firstItem = NULL; const int32_t numRecentSceneFiles = static_cast(recentSceneFiles.size()); for (int32_t i = 0; i < numRecentSceneFiles; i++) { QString path; QString name; QString fullPath; if (firstItem == NULL) { QStringList itemText; itemText.append("Recent Scene Files"); itemText.append("-------------------------------"); QTreeWidgetItem* titleItem = new QTreeWidgetItem(itemText); titleItem->setDisabled(true); m_dataFileTreeWidget->addTopLevelItem(titleItem); } const QString sceneFileName = recentSceneFiles[i]; if (DataFile::isFileOnNetwork(sceneFileName)) { const int lastSlash = sceneFileName.lastIndexOf('/'); name = sceneFileName.mid(lastSlash + 1); path = sceneFileName.left(lastSlash); fullPath = sceneFileName; } else { FileInformation fileInfo(sceneFileName); path = fileInfo.getPathName().trimmed(); name = fileInfo.getFileName().trimmed(); fullPath = fileInfo.getAbsoluteFilePath(); } if (name.isEmpty() == false) { QStringList treeText; treeText.append(" " + name); treeText.append(path); QTreeWidgetItem* lwi = new QTreeWidgetItem(treeText); lwi->setData(0, Qt::UserRole, fullPath); lwi->setData(1, Qt::UserRole, fullPath); m_dataFileTreeWidget->addTopLevelItem(lwi); if (firstItem == NULL) { firstItem = lwi; } } } return firstItem; } /** * Add spec files in current directory */ QTreeWidgetItem* SplashScreen::addDirectorySpecFiles() { Brain* brain = GuiManager::get()->getBrain(); const QString dirName = brain->getCurrentDirectory(); QTreeWidgetItem* firstItem = NULL; QStringList nameFilters; nameFilters.append("*." + DataFileTypeEnum::toFileExtension(DataFileTypeEnum::SPECIFICATION)); QDir dir(dirName); QStringList specFileList = dir.entryList(nameFilters, QDir::Files, QDir::Name); const int32_t numFiles = specFileList.count(); for (int32_t i = 0; i < numFiles; i++) { if (firstItem == NULL) { QStringList itemText; itemText.append("Current Directory Spec Files"); itemText.append(dirName); QTreeWidgetItem* titleItem = new QTreeWidgetItem(itemText); titleItem->setDisabled(true); m_dataFileTreeWidget->addTopLevelItem(titleItem); } FileInformation fileInfo(specFileList.at(i)); const QString name = fileInfo.getFileName().trimmed(); const QString fullPath = fileInfo.getAbsoluteFilePath(); QStringList treeText; treeText.append(" " + name); treeText.append(" . "); // Use . for current directory QTreeWidgetItem* lwi = new QTreeWidgetItem(treeText); lwi->setData(0, Qt::UserRole, fullPath); lwi->setData(1, Qt::UserRole, fullPath); m_dataFileTreeWidget->addTopLevelItem(lwi); if (firstItem == NULL) { firstItem = lwi; } } return firstItem; } /** * Add scene files in current directory */ QTreeWidgetItem* SplashScreen::addDirectorySceneFiles() { Brain* brain = GuiManager::get()->getBrain(); const QString dirName = brain->getCurrentDirectory(); QTreeWidgetItem* firstItem = NULL; QStringList nameFilters; nameFilters.append("*." + DataFileTypeEnum::toFileExtension(DataFileTypeEnum::SCENE)); QDir dir(dirName); QStringList sceneFileList = dir.entryList(nameFilters, QDir::Files, QDir::Name); const int32_t numFiles = sceneFileList.count(); for (int32_t i = 0; i < numFiles; i++) { if (firstItem == NULL) { QStringList itemText; itemText.append("Current Directory Scene"); itemText.append(dirName); QTreeWidgetItem* titleItem = new QTreeWidgetItem(itemText); titleItem->setDisabled(true); m_dataFileTreeWidget->addTopLevelItem(titleItem); } FileInformation fileInfo(sceneFileList.at(i)); const QString name = fileInfo.getFileName().trimmed(); const QString fullPath = fileInfo.getAbsoluteFilePath(); QStringList treeText; treeText.append(" " + name); treeText.append(" . "); // Use . for current directory QTreeWidgetItem* lwi = new QTreeWidgetItem(treeText); lwi->setData(0, Qt::UserRole, fullPath); lwi->setData(1, Qt::UserRole, fullPath); m_dataFileTreeWidget->addTopLevelItem(lwi); if (firstItem == NULL) { firstItem = lwi; } } return firstItem; } connectome-workbench-1.2.3+git41-gc4c6c90/src/GuiQt/SplashScreen.h000066400000000000000000000046151300200146000243460ustar00rootroot00000000000000#ifndef __SPLASH_SCREEN__H_ #define __SPLASH_SCREEN__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 "WuQDialogModal.h" class QPushButton; class QTreeWidget; class QTreeWidgetItem; namespace caret { class SplashScreen : public WuQDialogModal { Q_OBJECT public: SplashScreen(QWidget* parent = 0); virtual ~SplashScreen(); AString getSelectedDataFileName() const; private slots: void websiteLinkActivated(const QString& link); void dataFileTreeWidgetItemClicked(QTreeWidgetItem* item); void dataFileTreeWidgetItemDoubleClicked(QTreeWidgetItem* item); void chooseDataFileViaOpenFileDialog(); void twitterActionTriggered(); protected: WuQDialogModal::DialogUserButtonResult userButtonPressed(QPushButton* userPushButton); private: SplashScreen(const SplashScreen&); SplashScreen& operator=(const SplashScreen&); QTreeWidgetItem* addRecentSceneFiles(); QTreeWidgetItem* addRecentSpecFiles(); QTreeWidgetItem* addDirectorySceneFiles(); QTreeWidgetItem* addDirectorySpecFiles(); int32_t loadDataFileTreeWidget(); QTreeWidget* m_dataFileTreeWidget; AString m_selectedDataFileName; QPushButton* m_openOtherSpecFilePushButton; }; #ifdef __SPLASH_SCREEN_DECLARE__ // #endif // __SPLASH_SCREEN_DECLARE__ } // namespace #endif //__SPLASH_SCREEN__H_ connectome-workbench-1.2.3+git41-gc4c6c90/src/GuiQt/StructureEnumComboBox.cxx000066400000000000000000000136731300200146000266110ustar00rootroot00000000000000 /*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 __STRUCTURE_ENUM_COMBOBOX_DECLARE__ #include "StructureEnumComboBox.h" #undef __STRUCTURE_ENUM_COMBOBOX_DECLARE__ #include "Brain.h" #include "BrainStructure.h" #include "GuiManager.h" using namespace caret; /** * \class caret::StructureEnumComboBox * \brief Control for selection of a structure. * \ingroup GuiQt */ /** * Constructor. * @param parent * The parent. */ StructureEnumComboBox::StructureEnumComboBox(QObject* parent) : WuQWidget(parent) { std::vector allStructures; StructureEnum::getAllEnums(allStructures); const int32_t numStructures = static_cast(allStructures.size()); this->structureComboBox = new QComboBox(); for (int32_t i = 0; i < numStructures; i++) { this->structureComboBox->addItem(StructureEnum::toGuiName(allStructures[i])); this->structureComboBox->setItemData(i, StructureEnum::toIntegerCode(allStructures[i])); } QObject::connect(this->structureComboBox, SIGNAL(activated(int)), this, SLOT(structureComboBoxSelection(int))); } /** * Destructor. */ StructureEnumComboBox::~StructureEnumComboBox() { } /** * @return Number of items in combo box. */ int StructureEnumComboBox::count() const { return structureComboBox->count(); } /** * Limit the available structure to the given structures * * @param structures * Structures for display in combo box. */ void StructureEnumComboBox::listOnlyTheseStructures(const std::vector& structures) { this->structureComboBox->clear(); this->structureComboBox->blockSignals(true); const int32_t numStructures = static_cast(structures.size()); for (int32_t i = 0; i < numStructures; i++) { const StructureEnum::Enum structure = structures[i]; this->structureComboBox->addItem(StructureEnum::toGuiName(structure)); this->structureComboBox->setItemData(i, StructureEnum::toIntegerCode(structure)); } this->structureComboBox->blockSignals(false); } /** * Limit selections to those structures that are loaded. */ void StructureEnumComboBox::listOnlyValidStructures() { const StructureEnum::Enum selectedStructure = getSelectedStructure(); this->structureComboBox->clear(); this->structureComboBox->blockSignals(true); int32_t selectedStructureIndex = -1; const Brain* brain = GuiManager::get()->getBrain(); const int32_t numStructures = brain->getNumberOfBrainStructures(); for (int32_t i = 0; i < numStructures; i++) { const BrainStructure* bs = brain->getBrainStructure(i); const StructureEnum::Enum structure = bs->getStructure(); this->structureComboBox->addItem(StructureEnum::toGuiName(structure)); this->structureComboBox->setItemData(i, StructureEnum::toIntegerCode(structure)); if (selectedStructure != StructureEnum::INVALID) { if (structure == selectedStructure) { selectedStructureIndex = this->structureComboBox->count() - 1; } } } if (selectedStructureIndex >= 0) { this->structureComboBox->setCurrentIndex(selectedStructureIndex); } this->structureComboBox->blockSignals(false); } /** * Called to set the selected structure. * @param structure * New value for structure. */ void StructureEnumComboBox::setSelectedStructure(const StructureEnum::Enum structure) { const int32_t structureIntegerCode = StructureEnum::toIntegerCode(structure); const int numStructures = this->structureComboBox->count(); for (int32_t i = 0; i < numStructures; i++) { if (structureIntegerCode == this->structureComboBox->itemData(i).toInt()) { if (this->signalsBlocked()) { this->structureComboBox->blockSignals(true); } this->structureComboBox->setCurrentIndex(i); if (this->signalsBlocked()) { this->structureComboBox->blockSignals(false); } break; } } } /** * @return The selected structure. */ StructureEnum::Enum StructureEnumComboBox::getSelectedStructure() const { StructureEnum::Enum structure = StructureEnum::INVALID; const int32_t indx = this->structureComboBox->currentIndex(); if (indx >= 0) { const int32_t integerCode = this->structureComboBox->itemData(indx).toInt(); structure = StructureEnum::fromIntegerCode(integerCode, NULL); } return structure; } /** * @return The widget for this control. */ QWidget* StructureEnumComboBox::getWidget() { return this->structureComboBox; } /** * Called when a structure is selected * @param indx * Index of selection. */ void StructureEnumComboBox::structureComboBoxSelection(int indx) { if (this->signalsBlocked() == false) { if ((indx >= 0) && (indx < this->structureComboBox->count())) { const int32_t integerCode = this->structureComboBox->itemData(indx).toInt(); StructureEnum::Enum structure = StructureEnum::fromIntegerCode(integerCode, NULL); emit structureSelected(structure); } } } connectome-workbench-1.2.3+git41-gc4c6c90/src/GuiQt/StructureEnumComboBox.h000066400000000000000000000042231300200146000262250ustar00rootroot00000000000000#ifndef __STRUCTURE_ENUM_COMBOBOX__H_ #define __STRUCTURE_ENUM_COMBOBOX__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 "StructureEnum.h" #include "WuQWidget.h" namespace caret { class Surface; class StructureEnumComboBox : public WuQWidget { Q_OBJECT public: StructureEnumComboBox(QObject* parent); virtual ~StructureEnumComboBox(); StructureEnum::Enum getSelectedStructure() const; int count() const; void listOnlyTheseStructures(const std::vector& structures); void listOnlyValidStructures(); QWidget* getWidget(); public slots: void setSelectedStructure(const StructureEnum::Enum structure); signals: void structureSelected(const StructureEnum::Enum); private slots: void structureComboBoxSelection(int); private: StructureEnumComboBox(const StructureEnumComboBox&); StructureEnumComboBox& operator=(const StructureEnumComboBox&); QComboBox* structureComboBox; public: private: }; #ifdef __STRUCTURE_ENUM_COMBOBOX_DECLARE__ // #endif // __STRUCTURE_ENUM_COMBOBOX_DECLARE__ } // namespace #endif //__STRUCTURE_ENUM_COMBOBOX__H_ connectome-workbench-1.2.3+git41-gc4c6c90/src/GuiQt/StructureSurfaceSelectionControl.cxx000066400000000000000000000230541300200146000310450ustar00rootroot00000000000000 /*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 "Brain.h" #include "BrainStructure.h" #include "CaretAssert.h" #include "EventManager.h" #include "EventModelGetAll.h" #include "WuQFactory.h" #include "GuiManager.h" #include "WuQtUtilities.h" #define __STRUCTURE_SURFACE_SELECTION_CONTROL_DECLARE__ #include "StructureSurfaceSelectionControl.h" #undef __STRUCTURE_SURFACE_SELECTION_CONTROL_DECLARE__ #include "ModelSurface.h" #include "ModelSurfaceSelector.h" #include "Surface.h" using namespace caret; /** * \class caret::StructureSufaceSelectionControl * \brief * \ingroup GuiQt */ /** * Constructor. */ StructureSurfaceSelectionControl::StructureSurfaceSelectionControl(const bool showLabels) : QWidget() { this->surfaceControllerSelector = NULL; this->structureSelectionComboBox = WuQFactory::newComboBox(); this->structureSelectionComboBox->setSizeAdjustPolicy(QComboBox::AdjustToContents); QObject::connect(this->structureSelectionComboBox, SIGNAL(currentIndexChanged(int)), this, SLOT(structureSelected(int))); this->surfaceControllerSelectionComboBox = WuQFactory::newComboBox(); this->surfaceControllerSelectionComboBox->setSizeAdjustPolicy(QComboBox::AdjustToContents); QObject::connect(this->surfaceControllerSelectionComboBox, SIGNAL(currentIndexChanged(int)), this, SLOT(surfaceControllerSelected(int))); QGridLayout* layout = new QGridLayout(this); layout->setColumnStretch(0, 0); layout->setColumnStretch(1, 100); WuQtUtilities::setLayoutSpacingAndMargins(layout, 3, 2); if (showLabels) { QLabel* structureLabel = new QLabel("Structure"); QLabel* surfaceLabel = new QLabel("Surface"); layout->addWidget(structureLabel, 0, 0); layout->addWidget(surfaceLabel, 1, 0); } layout->addWidget(this->structureSelectionComboBox, 0, 1); layout->addWidget(this->surfaceControllerSelectionComboBox, 1, 1); } /** * Destructor. */ StructureSurfaceSelectionControl::~StructureSurfaceSelectionControl() { } void StructureSurfaceSelectionControl::structureSelected(int currentIndex) { CaretAssert((currentIndex >= 0) && (currentIndex < this->structureSelectionComboBox->count())); int32_t structID = this->structureSelectionComboBox->itemData(currentIndex).toInt(); StructureEnum::Enum selectedStructure = StructureEnum::fromIntegerCode(structID, NULL); this->surfaceControllerSelector->setSelectedStructure(selectedStructure); emitSelectionChangedSignal(); // emit selectionChanged(this->surfaceControllerSelector->getSelectedStructure(), // this->surfaceControllerSelector->getSelectedSurfaceModel()); this->updateControlAfterSelection(); } void StructureSurfaceSelectionControl::surfaceControllerSelected(int currentIndex) { CaretAssert((currentIndex >= 0) && (currentIndex < this->surfaceControllerSelectionComboBox->count())); void* pointer = this->surfaceControllerSelectionComboBox->itemData(currentIndex).value(); ModelSurface* surfaceController = (ModelSurface*)pointer; this->surfaceControllerSelector->setSelectedSurfaceModel(surfaceController); emitSelectionChangedSignal(); // emit selectionChanged(this->surfaceControllerSelector->getSelectedStructure(), // this->surfaceControllerSelector->getSelectedSurfaceModel()); } /** * Emit the selection changed signal. * Also preserves mouse focus that can be disrupted by user-interface updates. */ void StructureSurfaceSelectionControl::emitSelectionChangedSignal() { const bool structureHasFocus = this->structureSelectionComboBox->hasFocus(); const bool surfaceHasFocus = this->surfaceControllerSelectionComboBox->hasFocus(); emit selectionChanged(this->surfaceControllerSelector->getSelectedStructure(), this->surfaceControllerSelector->getSelectedSurfaceModel()); if (structureHasFocus) { this->structureSelectionComboBox->setFocus(); } else if (surfaceHasFocus) { this->surfaceControllerSelectionComboBox->setFocus(); } } /** * @return The selected mode surface. */ ModelSurface* StructureSurfaceSelectionControl::getSelectedSurfaceModel() { return this->surfaceControllerSelector->getSelectedSurfaceModel(); } /** * @return The selected structure. */ StructureEnum::Enum StructureSurfaceSelectionControl::getSelectedStructure() { return this->surfaceControllerSelector->getSelectedStructure(); } /* void StructureSurfaceSelectionControl::setSelectedSurfaceModel( ModelSurface* surfaceController) { this->surfaceControllerSelector->setSelectedSurfaceModel(surfaceController); this->updateControl(); } void StructureSurfaceSelectionControl::setSelectedStructure(const StructureEnum::Enum selectedStructure) { this->surfaceControllerSelector->setSelectedStructure(selectedStructure); this->updateControl(); } */ void StructureSurfaceSelectionControl::updateControl(ModelSurfaceSelector* surfaceControllerSelector) { this->surfaceControllerSelector = surfaceControllerSelector; this->updateControlAfterSelection(); } void StructureSurfaceSelectionControl::updateControlAfterSelection() { /* * Don't let any signals get sent by updating. */ this->structureSelectionComboBox->blockSignals(true); this->surfaceControllerSelectionComboBox->blockSignals(true); this->structureSelectionComboBox->clear(); this->surfaceControllerSelectionComboBox->clear(); if (this->surfaceControllerSelector == NULL) { return; } std::vector availableSurfaceModels; std::vector availableStructures; this->surfaceControllerSelector->getSelectableStructures(availableStructures); this->surfaceControllerSelector->getSelectableSurfaceModels(availableSurfaceModels); Surface* primaryAnatomicalSurface = NULL; /* * Update the structure selection. */ StructureEnum::Enum selectedStructure = this->surfaceControllerSelector->getSelectedStructure(); int32_t defaultStructureIndex = 0; for (int32_t i = 0; i < static_cast(availableStructures.size()); i++) { StructureEnum::Enum structType = availableStructures[i]; if (structType == selectedStructure) { defaultStructureIndex = i; } this->structureSelectionComboBox->addItem(StructureEnum::toGuiName(structType), StructureEnum::toIntegerCode(structType)); } if (defaultStructureIndex < this->structureSelectionComboBox->count()) { this->structureSelectionComboBox->setCurrentIndex(defaultStructureIndex); BrainStructure* brainStructure = GuiManager::get()->getBrain()->getBrainStructure(availableStructures[defaultStructureIndex], false); if (brainStructure != NULL) { primaryAnatomicalSurface = brainStructure->getPrimaryAnatomicalSurface(); } } const bool allSelected (selectedStructure == StructureEnum::ALL); /* * Update the surface selection. */ ModelSurface* selectedSurfaceController = this->surfaceControllerSelector->getSelectedSurfaceModel(); int32_t defaultSurfaceIndex = -1; int32_t primaryAnatomicalSurfaceIndex = -1; for (std::vector::const_iterator iter = availableSurfaceModels.begin(); iter != availableSurfaceModels.end(); iter++) { ModelSurface* surfaceController = *iter; this->surfaceControllerSelectionComboBox->addItem(surfaceController->getNameForGUI(allSelected), qVariantFromValue((void*)surfaceController)); if (selectedSurfaceController == surfaceController) { defaultSurfaceIndex = this->surfaceControllerSelectionComboBox->count() - 1; } if (surfaceController->getSurface() == primaryAnatomicalSurface) { primaryAnatomicalSurfaceIndex = this->surfaceControllerSelectionComboBox->count() - 1; } } if (defaultSurfaceIndex < 0) { if (primaryAnatomicalSurfaceIndex >= 0) { defaultSurfaceIndex = primaryAnatomicalSurfaceIndex; } else if (this->surfaceControllerSelectionComboBox->count() > 0) { defaultSurfaceIndex = 0; } } if (defaultSurfaceIndex < this->surfaceControllerSelectionComboBox->count()) { this->surfaceControllerSelectionComboBox->setCurrentIndex(defaultSurfaceIndex); } this->structureSelectionComboBox->blockSignals(false); this->surfaceControllerSelectionComboBox->blockSignals(false); } connectome-workbench-1.2.3+git41-gc4c6c90/src/GuiQt/StructureSurfaceSelectionControl.h000066400000000000000000000053171300200146000304740ustar00rootroot00000000000000#ifndef __STRUCTURE_SURFACE_SELECTION_CONTROL__H_ #define __STRUCTURE_SURFACE_SELECTION_CONTROL__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 "StructureEnum.h" class QComboBox; namespace caret { class ModelSurfaceSelector; class ModelSurface; class Structure; class StructureSurfaceSelectionControl : public QWidget { Q_OBJECT public: StructureSurfaceSelectionControl(const bool showLabels); virtual ~StructureSurfaceSelectionControl(); ModelSurface* getSelectedSurfaceModel(); StructureEnum::Enum getSelectedStructure(); //void setSelectedSurfaceModel(ModelSurface* surfaceController); //void setSelectedStructure(const StructureEnum::Enum selectedStructure); void updateControl(ModelSurfaceSelector* surfaceControllerSelector); signals: void selectionChanged(const StructureEnum::Enum selectedStructure, ModelSurface* surfaceController); private slots: void structureSelected(int currentIndex); void surfaceControllerSelected(int currentIndex); private: void updateControlAfterSelection(); void emitSelectionChangedSignal(); StructureSurfaceSelectionControl(const StructureSurfaceSelectionControl&); StructureSurfaceSelectionControl& operator=(const StructureSurfaceSelectionControl&); QComboBox* structureSelectionComboBox; QComboBox* surfaceControllerSelectionComboBox; ModelSurfaceSelector* surfaceControllerSelector; public: private: }; #ifdef __STRUCTURE_SURFACE_SELECTION_CONTROL_DECLARE__ // #endif // __STRUCTURE_SURFACE_SELECTION_CONTROL_DECLARE__ } // namespace #endif //__STRUCTURE_SURFACE_SELECTION_CONTROL__H_ connectome-workbench-1.2.3+git41-gc4c6c90/src/GuiQt/SurfacePropertiesEditorDialog.cxx000066400000000000000000000212711300200146000302600ustar00rootroot00000000000000 /*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 __SURFACE_PROPERTIES_EDITOR_DIALOG_DECLARE__ #include "SurfacePropertiesEditorDialog.h" #undef __SURFACE_PROPERTIES_EDITOR_DIALOG_DECLARE__ #include #include #include #include using namespace caret; #include "Brain.h" #include "CaretAssert.h" #include "DisplayPropertiesSurface.h" #include "GuiManager.h" #include "EnumComboBoxTemplate.h" #include "EventGraphicsUpdateAllWindows.h" #include "EventSurfaceColoringInvalidate.h" #include "EventManager.h" #include "EventUserInterfaceUpdate.h" #include "SceneClass.h" #include "SceneWindowGeometry.h" #include "WuQFactory.h" #include "WuQTrueFalseComboBox.h" #include "WuQtUtilities.h" /** * \class caret::SurfacePropertiesEditorDialog * \brief Dialog for adjusting surface display properties. * \ingroup GuitQt */ /** * Constructor. */ SurfacePropertiesEditorDialog::SurfacePropertiesEditorDialog(QWidget* parent) : WuQDialogNonModal("Surface Properties", parent) { m_updateInProgress = true; QLabel* surfaceDrawingTypeLabel = new QLabel("Drawing Type: "); m_surfaceDrawingTypeComboBox = new EnumComboBoxTemplate(this); QObject::connect(m_surfaceDrawingTypeComboBox, SIGNAL(itemActivated()), this, SLOT(surfaceDisplayPropertyChanged())); m_surfaceDrawingTypeComboBox->setup(); QLabel* linkSizeLabel = new QLabel("Link Diameter: "); m_linkSizeSpinBox = WuQFactory::newDoubleSpinBox(); m_linkSizeSpinBox->setRange(0.0, std::numeric_limits::max()); m_linkSizeSpinBox->setSingleStep(1.0); m_linkSizeSpinBox->setDecimals(1); m_linkSizeSpinBox->setSuffix("mm"); QObject::connect(m_linkSizeSpinBox, SIGNAL(valueChanged(double)), this, SLOT(surfaceDisplayPropertyChanged())); QLabel* nodeSizeLabel = new QLabel("Vertex Diameter: "); m_nodeSizeSpinBox = WuQFactory::newDoubleSpinBox(); m_nodeSizeSpinBox->setRange(0.0, std::numeric_limits::max()); m_nodeSizeSpinBox->setSingleStep(1.0); m_nodeSizeSpinBox->setDecimals(1); m_nodeSizeSpinBox->setSuffix("mm"); QObject::connect(m_nodeSizeSpinBox, SIGNAL(valueChanged(double)), this, SLOT(surfaceDisplayPropertyChanged())); QLabel* displayNormalVectorsLabel = new QLabel("Display Normal Vectors: "); m_displayNormalVectorsComboBox = new WuQTrueFalseComboBox(this); QObject::connect(m_displayNormalVectorsComboBox, SIGNAL(statusChanged(bool)), this, SLOT(surfaceDisplayPropertyChanged())); QLabel* opacityLabel = new QLabel("Opacity: "); m_opacitySpinBox = WuQFactory::newDoubleSpinBox(); m_opacitySpinBox->setRange(0.0, 1.0); m_opacitySpinBox->setSingleStep(0.1); m_opacitySpinBox->setDecimals(2); QObject::connect(m_opacitySpinBox, SIGNAL(valueChanged(double)), this, SLOT(surfaceDisplayPropertyChanged())); QWidget* w = new QWidget(); QGridLayout* gridLayout = new QGridLayout(w); WuQtUtilities::setLayoutSpacingAndMargins(gridLayout, 2, 2); int row = gridLayout->rowCount(); gridLayout->addWidget(displayNormalVectorsLabel, row, 0); gridLayout->addWidget(m_displayNormalVectorsComboBox->getWidget(), row, 1); row++; gridLayout->addWidget(surfaceDrawingTypeLabel, row, 0); gridLayout->addWidget(m_surfaceDrawingTypeComboBox->getWidget(), row, 1); row++; gridLayout->addWidget(linkSizeLabel, row, 0); gridLayout->addWidget(m_linkSizeSpinBox, row, 1); row++; gridLayout->addWidget(nodeSizeLabel, row, 0); gridLayout->addWidget(m_nodeSizeSpinBox, row, 1); row++; gridLayout->addWidget(opacityLabel, row, 0); gridLayout->addWidget(m_opacitySpinBox, row, 1); row++; setCentralWidget(w, WuQDialog::SCROLL_AREA_NEVER); updateDialog(); EventManager::get()->addEventListener(this, EventTypeEnum::EVENT_USER_INTERFACE_UPDATE); /* * No apply button */ setApplyButtonText(""); } /** * Destructor. */ SurfacePropertiesEditorDialog::~SurfacePropertiesEditorDialog() { EventManager::get()->removeAllEventsFromListener(this); } /** * Called when a surface display property is changed. */ void SurfacePropertiesEditorDialog::surfaceDisplayPropertyChanged() { /* * Updating some widgets causes signals to be emitted */ if (m_updateInProgress) { return; } const SurfaceDrawingTypeEnum::Enum surfaceDrawingType = m_surfaceDrawingTypeComboBox->getSelectedItem(); DisplayPropertiesSurface* dps = GuiManager::get()->getBrain()->getDisplayPropertiesSurface(); dps->setSurfaceDrawingType(surfaceDrawingType); dps->setDisplayNormalVectors(m_displayNormalVectorsComboBox->isTrue()); dps->setLinkSize(m_linkSizeSpinBox->value()); dps->setNodeSize(m_nodeSizeSpinBox->value()); dps->setOpacity(m_opacitySpinBox->value()); EventManager::get()->sendEvent(EventSurfaceColoringInvalidate().getPointer()); EventManager::get()->sendEvent(EventGraphicsUpdateAllWindows().getPointer()); } /** * Update the properties editor. */ void SurfacePropertiesEditorDialog::updateDialog() { m_updateInProgress = true; const DisplayPropertiesSurface* dps = GuiManager::get()->getBrain()->getDisplayPropertiesSurface(); m_surfaceDrawingTypeComboBox->setSelectedItem(dps->getSurfaceDrawingType()); m_displayNormalVectorsComboBox->setStatus(dps->isDisplayNormalVectors()); m_linkSizeSpinBox->setValue(dps->getLinkSize()); m_nodeSizeSpinBox->setValue(dps->getNodeSize()); m_opacitySpinBox->setValue(dps->getOpacity()); m_updateInProgress = false; } /** * Receive events from the event manager. * * @param event * Event sent by event manager. */ void SurfacePropertiesEditorDialog::receiveEvent(Event* event) { if (event->getEventType() == EventTypeEnum::EVENT_USER_INTERFACE_UPDATE) { CaretAssert(dynamic_cast(event) != NULL); updateDialog(); } } /** * Create a scene for an instance of a class. * * @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. * * @return Pointer to SceneClass object representing the state of * this object. Under some circumstances a NULL pointer may be * returned. Caller will take ownership of returned object. */ SceneClass* SurfacePropertiesEditorDialog::saveToScene(const SceneAttributes* sceneAttributes, const AString& instanceName) { SceneClass* sceneClass = new SceneClass(instanceName, "SurfacePropertiesEditorDialog", 1); /* * Position and size */ SceneWindowGeometry swg(this); sceneClass->addClass(swg.saveToScene(sceneAttributes, "geometry")); return sceneClass; } /** * Restore the state of an instance of a class. * * @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 containing the state that was previously * saved and should be restored. */ void SurfacePropertiesEditorDialog::restoreFromScene(const SceneAttributes* sceneAttributes, const SceneClass* sceneClass) { if (sceneClass == NULL) { return; } /* * Position and size */ SceneWindowGeometry swg(this); swg.restoreFromScene(sceneAttributes, sceneClass->getClass("geometry")); } connectome-workbench-1.2.3+git41-gc4c6c90/src/GuiQt/SurfacePropertiesEditorDialog.h000066400000000000000000000052161300200146000277060ustar00rootroot00000000000000#ifndef __SURFACE_PROPERTIES_EDITOR_DIALOG__H_ #define __SURFACE_PROPERTIES_EDITOR_DIALOG__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 "EventListenerInterface.h" #include "SceneableInterface.h" #include "WuQDialogNonModal.h" class QDoubleSpinBox; namespace caret { class EnumComboBoxTemplate; class WuQTrueFalseComboBox; class SurfacePropertiesEditorDialog : public WuQDialogNonModal, public EventListenerInterface, public SceneableInterface { Q_OBJECT public: SurfacePropertiesEditorDialog(QWidget* parent = 0); virtual ~SurfacePropertiesEditorDialog(); void receiveEvent(Event* event); void updateDialog(); virtual SceneClass* saveToScene(const SceneAttributes* sceneAttributes, const AString& instanceName); virtual void restoreFromScene(const SceneAttributes* sceneAttributes, const SceneClass* sceneClass); private slots: void surfaceDisplayPropertyChanged(); private: SurfacePropertiesEditorDialog(const SurfacePropertiesEditorDialog&); SurfacePropertiesEditorDialog& operator=(const SurfacePropertiesEditorDialog&); WuQTrueFalseComboBox* m_displayNormalVectorsComboBox; QDoubleSpinBox* m_linkSizeSpinBox; QDoubleSpinBox* m_nodeSizeSpinBox; EnumComboBoxTemplate* m_surfaceDrawingTypeComboBox; QDoubleSpinBox* m_opacitySpinBox; bool m_updateInProgress; // ADD_NEW_MEMBERS_HERE }; #ifdef __SURFACE_PROPERTIES_EDITOR_DIALOG_DECLARE__ // #endif // __SURFACE_PROPERTIES_EDITOR_DIALOG_DECLARE__ } // namespace #endif //__SURFACE_PROPERTIES_EDITOR_DIALOG__H_ connectome-workbench-1.2.3+git41-gc4c6c90/src/GuiQt/SurfaceSelectionViewController.cxx000066400000000000000000000200061300200146000304540ustar00rootroot00000000000000 /*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 __SURFACE_SELECTION_CONTROL_DECLARE__ #include "SurfaceSelectionViewController.h" #undef __SURFACE_SELECTION_CONTROL_DECLARE__ #include #include "BrainStructure.h" #include "Surface.h" #include "SurfaceSelectionModel.h" #include "WuQEventBlockingFilter.h" #include "WuQFactory.h" using namespace caret; /** * \class caret::SurfaceSelectionViewController * \brief Control for selecting surfaces. * \ingroup GuiQt * * Control for selecting surfaces. */ /** * Constructor. * @param parent * The parent. * @param surfaceSelection * Surface selection that is controlled through this control. */ SurfaceSelectionViewController::SurfaceSelectionViewController(QObject* parent, SurfaceSelectionModel* surfaceSelectionModel) : WuQWidget(parent) { this->initializeControl(MODE_SELECTION_MODEL_STATIC, surfaceSelectionModel); } /** * Constructor. * @param parent * The parent. * @param brainStructure * Allows selection of any surface with the specified brain structure. */ SurfaceSelectionViewController::SurfaceSelectionViewController(QObject* parent, BrainStructure* brainStructure) : WuQWidget(parent) { std::vector allSurfaceTypes; SurfaceTypeEnum::getAllEnums(allSurfaceTypes); SurfaceSelectionModel* ss = new SurfaceSelectionModel(brainStructure->getStructure(), allSurfaceTypes); this->initializeControl(MODE_BRAIN_STRUCTURE, ss); } /** * Destructor. */ SurfaceSelectionViewController::~SurfaceSelectionViewController() { bool isDeleteSelectionModel = false; switch (this->mode) { case MODE_BRAIN_STRUCTURE: isDeleteSelectionModel = true; break; case MODE_SELECTION_MODEL_DYNAMIC: break; case MODE_SELECTION_MODEL_STATIC: break; } if (isDeleteSelectionModel) { delete this->surfaceSelectionModel; } } /** * Constructor. Creates a selection control. User MUST call updateControl(SurfaceSelectionModel*) * so that surfaces get loaded. A instance created this way will NEVER use the selection * model this is passed to updateControl() anywhere outside of updateControl(). Thus, * slots in the model are NEVER called. * * @param parent * The parent. */ SurfaceSelectionViewController::SurfaceSelectionViewController(QObject* parent) : WuQWidget(parent) { this->initializeControl(MODE_SELECTION_MODEL_DYNAMIC, NULL); } /** * Help initialize an instance. */ void SurfaceSelectionViewController::initializeControl(const Mode mode, SurfaceSelectionModel* surfaceSelectionModel) { this->mode = mode; this->surfaceComboBox = WuQFactory::newComboBox(); this->surfaceComboBox->setSizeAdjustPolicy(QComboBox::AdjustToContents); QObject::connect(this->surfaceComboBox, SIGNAL(currentIndexChanged(int)), this, SLOT(comboBoxCurrentIndexChanged(int))); //#ifdef CARET_OS_MACOSX // /* // * Block the wheel event on Mac since it get issued when a mouse // * is moved over the combo box. // */ // WuQEventBlockingFilter* comboBoxWheelEventBlockingFilter = new WuQEventBlockingFilter(this); // comboBoxWheelEventBlockingFilter->setEventBlocked(QEvent::Wheel, // true); // surfaceComboBox->installEventFilter(comboBoxWheelEventBlockingFilter); //#endif // CARET_OS_MACOSX this->surfaceSelectionModel = surfaceSelectionModel; } /** * Update the control using the given selection model. The model is NEVER * used outside this method so slots in the model are NEVER called. * * @param selectionModel * Selection model used to update this control. If this parameter is NULL * all selections are removed from the control. */ void SurfaceSelectionViewController::updateControl(SurfaceSelectionModel* selectionModel) { std::vector surfaces; Surface* selectedSurface = NULL; if (selectionModel != NULL) { surfaces = selectionModel->getAvailableSurfaces(); selectedSurface = selectionModel->getSurface(); } this->surfaceComboBox->blockSignals(true); 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->setItemData(indx, qVariantFromValue((void*)surfaces[i])); if (surfaces[i] == selectedSurface) { selectedIndex = indx; } } if (numSurfaces > 0) { this->surfaceComboBox->setCurrentIndex(selectedIndex); } this->surfaceComboBox->blockSignals(false); } /** * Update the control. */ void SurfaceSelectionViewController::updateControl() { CaretAssertMessage(this->surfaceSelectionModel, "The surface selection model is NULL, you should have called " "updateControl(SurfaceSelectionModel*)"); this->updateControl(this->surfaceSelectionModel); } /** * @return the actual widget. */ QWidget* SurfaceSelectionViewController::getWidget() { return this->surfaceComboBox; } /** * @return the selected surface. NULL if no surface selected. */ Surface* SurfaceSelectionViewController::getSurface() { //return this->surfaceSelectionModel->getSurface(); Surface* s = NULL; const int indx = this->surfaceComboBox->currentIndex(); if ((indx >= 0) && (indx < this->surfaceComboBox->count())) { void* pointer = this->surfaceComboBox->itemData(indx).value(); s = (Surface*)pointer; } return s; } /** * Set the selected surface. * @param surface * The selected surface. */ void SurfaceSelectionViewController::setSurface(Surface* surface) { if (this->surfaceSelectionModel != NULL) { this->surfaceSelectionModel->setSurface(surface); this->updateControl(); } else { const int32_t numItems = this->surfaceComboBox->count(); for (int32_t i = 0; i < numItems; i++) { void* pointer = this->surfaceComboBox->itemData(i).value(); Surface* s = (Surface*)pointer; if (surface == s) { this->surfaceComboBox->blockSignals(true); this->surfaceComboBox->setCurrentIndex(i); this->surfaceComboBox->blockSignals(false); break; } } } } /** * Called when the item in the combo box is changed. * @param indx * Index of item that was selected. */ void SurfaceSelectionViewController::comboBoxCurrentIndexChanged(int indx) { void* pointer = this->surfaceComboBox->itemData(indx).value(); Surface* s = (Surface*)pointer; if (this->surfaceSelectionModel != NULL) { this->surfaceSelectionModel->setSurface(s); } emit surfaceSelected(s); } connectome-workbench-1.2.3+git41-gc4c6c90/src/GuiQt/SurfaceSelectionViewController.h000066400000000000000000000057351300200146000301150ustar00rootroot00000000000000#ifndef __SURFACE_SELECTION_CONTROL__H_ #define __SURFACE_SELECTION_CONTROL__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 "WuQWidget.h" class QComboBox; namespace caret { class BrainStructure; class Surface; class SurfaceSelectionModel; class SurfaceSelectionViewController : public WuQWidget { Q_OBJECT public: SurfaceSelectionViewController(QObject* parent, SurfaceSelectionModel* surfaceSelectionModel); SurfaceSelectionViewController(QObject* parent, BrainStructure* brainStructure); SurfaceSelectionViewController(QObject* parent); virtual ~SurfaceSelectionViewController(); QWidget* getWidget(); Surface* getSurface(); void updateControl(); void updateControl(SurfaceSelectionModel* surfaceSelectionModel); signals: void surfaceSelected(Surface*); public slots: void setSurface(Surface*); private slots: void comboBoxCurrentIndexChanged(int); private: SurfaceSelectionViewController(const SurfaceSelectionViewController&); SurfaceSelectionViewController& operator=(const SurfaceSelectionViewController&); private: enum Mode { /** Use all surfaces from a brain structure */ MODE_BRAIN_STRUCTURE, /** Selection mode NOT passed to constructor, user must use updateControl(SurfaceSelectionModel*) */ MODE_SELECTION_MODEL_DYNAMIC, /** Selection model passed to constructor, and use it */ MODE_SELECTION_MODEL_STATIC }; void initializeControl(const Mode mode, SurfaceSelectionModel* surfaceSelectionModel); Mode mode; SurfaceSelectionModel* surfaceSelectionModel; QComboBox* surfaceComboBox; }; #ifdef __SURFACE_SELECTION_CONTROL_DECLARE__ // #endif // __SURFACE_SELECTION_CONTROL_DECLARE__ } // namespace #endif //__SURFACE_SELECTION_CONTROL__H_ connectome-workbench-1.2.3+git41-gc4c6c90/src/GuiQt/TileTabsConfigurationDialog.cxx000066400000000000000000000661641300200146000277150ustar00rootroot00000000000000 /*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 #include #include #include #define __TILE_TABS_CONFIGURATION_DIALOG_DECLARE__ #include "TileTabsConfigurationDialog.h" #undef __TILE_TABS_CONFIGURATION_DIALOG_DECLARE__ #include "BrainBrowserWindow.h" #include "CaretAssert.h" #include "CaretPreferences.h" #include "EventGraphicsUpdateOneWindow.h" #include "EventManager.h" #include "GuiManager.h" #include "SessionManager.h" #include "TileTabsConfiguration.h" #include "WuQDataEntryDialog.h" #include "WuQFactory.h" #include "WuQMessageBox.h" #include "WuQtUtilities.h" using namespace caret; /** * \class caret::TileTabsConfigurationDialog * \brief Edit and create configurations for tile tabs viewing. * \ingroup GuiQt */ /** * Constructor. */ TileTabsConfigurationDialog::TileTabsConfigurationDialog(QWidget* parent) : WuQDialogNonModal("Tile Tabs Configuration", parent) { m_blockReadConfigurationsFromPreferences = false; m_caretPreferences = SessionManager::get()->getCaretPreferences(); QWidget* dialogWidget = new QWidget(); QVBoxLayout* dialogLayout = new QVBoxLayout(dialogWidget); dialogLayout->setSpacing(0); dialogLayout->addWidget(createConfigurationSelectionWidget(), 0, Qt::AlignHCenter); dialogLayout->addWidget(createEditConfigurationWidget(), 100, Qt::AlignHCenter); setCentralWidget(dialogWidget, WuQDialog::SCROLL_AREA_NEVER); disableAutoDefaultForAllPushButtons(); setApplyButtonText(""); // EventManager::get()->addEventListener(this, EventTypeEnum::); updateDialog(); /* * The content region of a scroll area is often too large vertically * so adjust the size of the dialog which will cause the scroll area * to approximately fit its content. */ WuQDialog::adjustSizeOfDialogWithScrollArea(this, m_stretchFactorScrollArea); } /** * Destructor. */ TileTabsConfigurationDialog::~TileTabsConfigurationDialog() { } /** * Gets called when the dialog gains focus. */ void TileTabsConfigurationDialog::focusGained() { updateDialog(); } /** * @return The configuration selection widget. */ QWidget* TileTabsConfigurationDialog::createConfigurationSelectionWidget() { QLabel* configurationLabel = new QLabel("Configuration"); m_configurationSelectionComboBox = WuQFactory::newComboBoxSignalInt(this, SLOT(configurationComboBoxItemSelected(int))); QHBoxLayout* selectionLayout = new QHBoxLayout(); WuQtUtilities::setLayoutMargins(selectionLayout, 0); selectionLayout->addWidget(configurationLabel, 0); selectionLayout->addWidget(m_configurationSelectionComboBox, 100); m_newConfigurationPushButton = new QPushButton("New..."); QObject::connect(m_newConfigurationPushButton, SIGNAL(clicked()), this, SLOT(newConfigurationButtonClicked())); m_renameConfigurationPushButton = new QPushButton("Rename..."); QObject::connect(m_renameConfigurationPushButton, SIGNAL(clicked()), this, SLOT(renameConfigurationButtonClicked())); m_deleteConfigurationPushButton = new QPushButton("Delete..."); QObject::connect(m_deleteConfigurationPushButton, SIGNAL(clicked()), this, SLOT(deleteConfigurationButtonClicked())); QHBoxLayout* buttonsLayout = new QHBoxLayout(); buttonsLayout->addWidget(m_newConfigurationPushButton); buttonsLayout->addStretch(); buttonsLayout->addWidget(m_renameConfigurationPushButton); buttonsLayout->addStretch(); buttonsLayout->addWidget(m_deleteConfigurationPushButton); QGroupBox* configurationWidget = new QGroupBox("Configuration Selection"); QVBoxLayout* configurationLayout = new QVBoxLayout(configurationWidget); configurationLayout->addLayout(selectionLayout); configurationLayout->addLayout(buttonsLayout); return configurationWidget; } /** * @return The edit configuration widget. */ QWidget* TileTabsConfigurationDialog::createEditConfigurationWidget() { const int32_t maximuNumberOfRows = TileTabsConfiguration::getMaximumNumberOfRows(); const int32_t maximumNumberOfColumns = TileTabsConfiguration::getMaximumNumberOfColumns(); QLabel* rowsLabel = new QLabel("Number of Rows"); QLabel* columnsLabel = new QLabel("Number of Columns"); m_numberOfRowsSpinBox = WuQFactory::newSpinBoxWithMinMaxStepSignalInt(1, maximuNumberOfRows, 1, this, SLOT(numberOfRowsOrColumnsChanged())); m_numberOfColumnsSpinBox = WuQFactory::newSpinBoxWithMinMaxStepSignalInt(1, maximumNumberOfColumns, 1, this, SLOT(numberOfRowsOrColumnsChanged())); QWidget* numberOfWidget = new QWidget(); QGridLayout* numberOfGridLayout = new QGridLayout(numberOfWidget); WuQtUtilities::setLayoutMargins(numberOfGridLayout, 0); numberOfGridLayout->addWidget(rowsLabel, 0, 0); numberOfGridLayout->addWidget(m_numberOfRowsSpinBox, 0, 1); numberOfGridLayout->addWidget(columnsLabel, 1, 0); numberOfGridLayout->addWidget(m_numberOfColumnsSpinBox, 1, 1); numberOfWidget->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); QLabel* stretchFactorLabel = new QLabel("Stretch Factors"); QLabel* indexLabel = new QLabel("Index"); QLabel* rowLabel = new QLabel("Row"); QLabel* columnLabel = new QLabel("Column"); m_stretchFactorWidget = new QWidget(); QGridLayout* stretchFactorGridLayout = new QGridLayout(m_stretchFactorWidget); WuQtUtilities::setLayoutMargins(stretchFactorGridLayout, 2); stretchFactorGridLayout->setSizeConstraint(QLayout::SetFixedSize); int row = 0; stretchFactorGridLayout->addWidget(indexLabel, row, GRID_LAYOUT_COLUMN_INDEX_FOR_LABELS, Qt::AlignHCenter); stretchFactorGridLayout->addWidget(rowLabel, row, GRID_LAYOUT_COLUMN_INDEX_FOR_ROW_CONTROLS, Qt::AlignHCenter); stretchFactorGridLayout->addWidget(columnLabel, row, GRID_LAYOUT_COLUMN_INDEX_FOR_COLUMN_CONTROLS, Qt::AlignHCenter); row++; const float stretchMinimumValue = 1.0; const float stretchMaximumValue = 10000000.0; const float stretchStep = 0.1; const float stretchDigitsRightOfDecimal = 2; const int32_t spinBoxWidth = 80; const int32_t maxItems = std::max(maximuNumberOfRows, maximumNumberOfColumns); for (int i = 0; i < maxItems; i++) { QLabel* indexLabel = new QLabel(AString::number(i + 1)); m_stretchFactorIndexLabels.push_back(indexLabel); stretchFactorGridLayout->addWidget(indexLabel, row, GRID_LAYOUT_COLUMN_INDEX_FOR_LABELS, Qt::AlignHCenter); if (i < maximuNumberOfRows) { QDoubleSpinBox* rowSpinBox = WuQFactory::newDoubleSpinBoxWithMinMaxStepDecimalsSignalDouble(stretchMinimumValue, stretchMaximumValue, stretchStep, stretchDigitsRightOfDecimal, this, SLOT(configurationStretchFactorWasChanged())); rowSpinBox->setFixedWidth(spinBoxWidth); m_rowStretchFactorSpinBoxes.push_back(rowSpinBox); stretchFactorGridLayout->addWidget(rowSpinBox, row, GRID_LAYOUT_COLUMN_INDEX_FOR_ROW_CONTROLS); } if (i < maximumNumberOfColumns) { QDoubleSpinBox* colSpinBox = WuQFactory::newDoubleSpinBoxWithMinMaxStepDecimalsSignalDouble(stretchMinimumValue, stretchMaximumValue, stretchStep, stretchDigitsRightOfDecimal, this, SLOT(configurationStretchFactorWasChanged())); colSpinBox->setFixedWidth(spinBoxWidth); m_columnStretchFactorSpinBoxes.push_back(colSpinBox); stretchFactorGridLayout->addWidget(colSpinBox, row, GRID_LAYOUT_COLUMN_INDEX_FOR_COLUMN_CONTROLS); } row++; } m_stretchFactorScrollArea = new QScrollArea(); m_stretchFactorScrollArea->setWidget(m_stretchFactorWidget); m_stretchFactorScrollArea->setWidgetResizable(true); QGroupBox* widget = new QGroupBox("Edit Configuration"); QVBoxLayout* widgetLayout = new QVBoxLayout(widget); widgetLayout->addWidget(numberOfWidget, 0); widgetLayout->addWidget(WuQtUtilities::createHorizontalLineWidget(), 0); widgetLayout->addWidget(stretchFactorLabel, 0, Qt::AlignHCenter); widgetLayout->addWidget(m_stretchFactorScrollArea, 100); return widget; } /** * Update the content of the dialog. If tile tabs is selected in the given * window, the dialog will be initialized with the tile tabs configuration * selected in the window. * * @param brainBrowserWindow * Browser window from which dialog was selected. */ void TileTabsConfigurationDialog::updateDialogWithSelectedTileTabsFromWindow(BrainBrowserWindow* brainBrowserWindow) { CaretAssert(brainBrowserWindow); m_brainBrowserWindow = brainBrowserWindow; readConfigurationsFromPreferences(); if (m_brainBrowserWindow->isTileTabsSelected()) { TileTabsConfiguration* configuration = m_brainBrowserWindow->getSelectedTileTabsConfiguration(); if (configuration != NULL) { selectTileTabConfigurationByUniqueID(configuration->getUniqueIdentifier()); } } updateDialog(); } /** * Update the tile tabs configuration in the brain browser window if * the browser window has tile tabs enabled. */ void TileTabsConfigurationDialog::updateBrowserWindowsTileTabsConfigurationSelection() { if (m_brainBrowserWindow != NULL) { if (m_brainBrowserWindow->isTileTabsSelected()) { m_brainBrowserWindow->setSelectedTileTabsConfiguration(getSelectedTileTabsConfiguration()); updateGraphicsWindows(); } } } /** * Read the configurations from the preferences. */ void TileTabsConfigurationDialog::readConfigurationsFromPreferences() { if (m_blockReadConfigurationsFromPreferences) { return; } m_caretPreferences->readTileTabsConfigurations(); } /** * Update the content of the dialog. */ void TileTabsConfigurationDialog::updateDialog() { readConfigurationsFromPreferences(); const AString selectedUniqueID = getSelectedTileTabsConfigurationUniqueID(); int defaultIndex = m_configurationSelectionComboBox->currentIndex(); m_configurationSelectionComboBox->blockSignals(true); m_configurationSelectionComboBox->clear(); std::vector configurations = m_caretPreferences->getTileTabsConfigurationsSortedByName(); const int32_t numConfig = static_cast(configurations.size()); for (int32_t i = 0; i < numConfig; i++) { const TileTabsConfiguration* configuration = configurations[i]; /* * Second element is user data which contains the Unique ID */ m_configurationSelectionComboBox->addItem(configuration->getName(), QVariant(configuration->getUniqueIdentifier())); if (configuration->getUniqueIdentifier() == selectedUniqueID) { defaultIndex = i; } } const int32_t numItemsInComboBox = m_configurationSelectionComboBox->count(); if (defaultIndex >= numItemsInComboBox) { defaultIndex = numItemsInComboBox - 1; } if (defaultIndex < 0) { defaultIndex = 0; } if (defaultIndex < m_configurationSelectionComboBox->count()) { m_configurationSelectionComboBox->setCurrentIndex(defaultIndex); selectConfigurationFromComboBoxIndex(defaultIndex); } m_configurationSelectionComboBox->blockSignals(false); updateStretchFactors(); } /** * Update the stretch factors. */ void TileTabsConfigurationDialog::updateStretchFactors() { int32_t numValidRows = 0; int32_t numValidColumns = 0; const TileTabsConfiguration* configuration = getSelectedTileTabsConfiguration(); if (configuration != NULL) { numValidRows = configuration->getNumberOfRows(); numValidColumns = configuration->getNumberOfColumns(); } const int32_t numColSpinBoxes = static_cast(m_columnStretchFactorSpinBoxes.size()); for (int32_t i = 0; i < numColSpinBoxes; i++) { QDoubleSpinBox* sb = m_columnStretchFactorSpinBoxes[i]; if (i < numValidColumns) { sb->setVisible(true); sb->blockSignals(true); sb->setValue(configuration->getColumnStretchFactor(i)); sb->blockSignals(false); } else { sb->setVisible(false); } } const int32_t numRowSpinBoxes = static_cast(m_rowStretchFactorSpinBoxes.size()); for (int32_t i = 0; i < numRowSpinBoxes; i++) { QDoubleSpinBox* sb = m_rowStretchFactorSpinBoxes[i]; if (i < numValidRows) { sb->setVisible(true); sb->blockSignals(true); sb->setValue(configuration->getRowStretchFactor(i)); sb->blockSignals(false); } else { sb->setVisible(false); } } const int32_t numIndexLabels = static_cast(m_stretchFactorIndexLabels.size()); const int32_t numValidLabels = std::max(numValidRows, numValidColumns); for (int32_t i = 0; i < numIndexLabels; i++) { if (i < numValidLabels) { m_stretchFactorIndexLabels[i]->setVisible(true); } else { m_stretchFactorIndexLabels[i]->setVisible(false); } } m_stretchFactorWidget->setFixedSize(m_stretchFactorWidget->sizeHint()); } /** * Select the tile tabs configuration with the given name. */ void TileTabsConfigurationDialog::selectTileTabConfigurationByUniqueID(const AString& uniqueID) { const int32_t numItems = m_configurationSelectionComboBox->count(); for (int32_t i = 0; i < numItems; i++) { const AString itemID = m_configurationSelectionComboBox->itemData(i, Qt::UserRole).toString(); if (itemID == uniqueID) { m_configurationSelectionComboBox->setCurrentIndex(i); selectConfigurationFromComboBoxIndex(i); break; } } } /** * Called when a configuration is selected from the combo box. * * @param indx * Index of item selected. */ void TileTabsConfigurationDialog::configurationComboBoxItemSelected(int indx) { selectConfigurationFromComboBoxIndex(indx); updateBrowserWindowsTileTabsConfigurationSelection(); } /** * Select the configuration at the given index from the configuration combo box. * * @param indx * Index of item for selection. */ void TileTabsConfigurationDialog::selectConfigurationFromComboBoxIndex(int indx) { if ((indx >= 0) && (indx < m_configurationSelectionComboBox->count())) { const AString itemID = m_configurationSelectionComboBox->itemData(indx, Qt::UserRole).toString(); TileTabsConfiguration* configuration = m_caretPreferences->getTileTabsConfigurationByUniqueIdentifier(itemID); if (configuration != NULL) { m_numberOfRowsSpinBox->blockSignals(true); m_numberOfRowsSpinBox->setValue(configuration->getNumberOfRows()); m_numberOfRowsSpinBox->blockSignals(false); m_numberOfColumnsSpinBox->blockSignals(true); m_numberOfColumnsSpinBox->setValue(configuration->getNumberOfColumns()); m_numberOfColumnsSpinBox->blockSignals(false); } } updateStretchFactors(); } /** * Called when new configuration button is clicked. */ void TileTabsConfigurationDialog::newConfigurationButtonClicked() { AString newTileTabsName; AString configurationUniqueID; bool exitLoop = false; while (exitLoop == false) { /* * Popup dialog to get name for new configuration */ WuQDataEntryDialog ded("New Tile Tabs Configuration", m_newConfigurationPushButton); QLineEdit* nameLineEdit = ded.addLineEditWidget("View Name"); nameLineEdit->setText(newTileTabsName); if (ded.exec() == WuQDataEntryDialog::Accepted) { /* * Make sure name is not empty */ newTileTabsName = nameLineEdit->text().trimmed(); if (newTileTabsName.isEmpty()) { WuQMessageBox::errorOk(m_newConfigurationPushButton, "Enter a name"); } else { /* * See if a configuration with the user entered name already exists */ TileTabsConfiguration* configuration = m_caretPreferences->getTileTabsConfigurationByName(newTileTabsName); if (configuration != NULL) { const QString msg = ("Configuration named \"" + newTileTabsName + "\" already exits. Rename it?"); if (WuQMessageBox::warningYesNo(m_newConfigurationPushButton, msg)) { configuration->setName(newTileTabsName); configurationUniqueID = configuration->getUniqueIdentifier(); exitLoop = true; } } else { /* * New configuration is copy of selected configuration (if available) */ const TileTabsConfiguration* selectedConfiguration = getSelectedTileTabsConfiguration(); TileTabsConfiguration* configuration = ((selectedConfiguration != NULL) ? selectedConfiguration->newCopyWithNewUniqueIdentifier() : new TileTabsConfiguration()); configuration->setName(newTileTabsName); configurationUniqueID = configuration->getUniqueIdentifier(); m_caretPreferences->addTileTabsConfiguration(configuration); exitLoop = true; } } } else { /* * User pressed cancel button. */ exitLoop = true; } } if (configurationUniqueID.isEmpty() == false) { updateDialog(); selectTileTabConfigurationByUniqueID(configurationUniqueID); updateBrowserWindowsTileTabsConfigurationSelection(); } } /** * Called when delete configuration button is clicked. */ void TileTabsConfigurationDialog::deleteConfigurationButtonClicked() { TileTabsConfiguration* configuration = getSelectedTileTabsConfiguration(); if (configuration != NULL) { const AString uniqueID = configuration->getUniqueIdentifier(); const QString msg = ("Delete configuration named \"" + configuration->getName() + "\" ?"); if (WuQMessageBox::warningYesNo(m_newConfigurationPushButton, msg)) { m_caretPreferences->removeTileTabsConfigurationByUniqueIdentifier(uniqueID); updateDialog(); updateBrowserWindowsTileTabsConfigurationSelection(); } } } /** * Called when rename configuration button is clicked. */ void TileTabsConfigurationDialog::renameConfigurationButtonClicked() { TileTabsConfiguration* configuration = getSelectedTileTabsConfiguration(); if (configuration != NULL) { m_blockReadConfigurationsFromPreferences = true; bool ok = false; const AString oldName = configuration->getName(); const AString newName = QInputDialog::getText(m_deleteConfigurationPushButton, "Rename Configuration", "Name", QLineEdit::Normal, oldName, &ok); if (ok && (newName.isEmpty() == false)) { configuration->setName(newName); m_caretPreferences->writeTileTabsConfigurations(); m_blockReadConfigurationsFromPreferences = false; updateDialog(); } else { m_blockReadConfigurationsFromPreferences = false; } } } /** * @return A pointer to the selected tile tabs configuration of NULL if * no configuration is available. */ TileTabsConfiguration* TileTabsConfigurationDialog::getSelectedTileTabsConfiguration() { const AString uniqueID = getSelectedTileTabsConfigurationUniqueID(); TileTabsConfiguration* configuration = m_caretPreferences->getTileTabsConfigurationByUniqueIdentifier(uniqueID); return configuration; } /** * @return The unique identifier of the selected tile tabs configuration an * empty string if no configuration is available. */ AString TileTabsConfigurationDialog::getSelectedTileTabsConfigurationUniqueID() { AString uniqueID; const int32_t indx = m_configurationSelectionComboBox->currentIndex(); if ((indx >= 0) && (indx < m_configurationSelectionComboBox->count())) { uniqueID = m_configurationSelectionComboBox->itemData(indx, Qt::UserRole).toString(); } return uniqueID; } /** * Called when the number of rows or columns changes. */ void TileTabsConfigurationDialog::numberOfRowsOrColumnsChanged() { TileTabsConfiguration* configuration = getSelectedTileTabsConfiguration(); if (configuration != NULL) { configuration->setNumberOfRows(m_numberOfRowsSpinBox->value()); configuration->setNumberOfColumns(m_numberOfColumnsSpinBox->value()); m_caretPreferences->writeTileTabsConfigurations(); updateStretchFactors(); updateGraphicsWindows(); } } /** * Called when a configuration stretch factor value is changed. */ void TileTabsConfigurationDialog::configurationStretchFactorWasChanged() { TileTabsConfiguration* configuration = getSelectedTileTabsConfiguration(); if (configuration == NULL) { return; } const int32_t numColSpinBoxes = static_cast(m_columnStretchFactorSpinBoxes.size()); for (int32_t i = 0; i < numColSpinBoxes; i++) { if (m_columnStretchFactorSpinBoxes[i]->isEnabled()) { configuration->setColumnStretchFactor(i, m_columnStretchFactorSpinBoxes[i]->value()); } } const int32_t numRowSpinBoxes = static_cast(m_rowStretchFactorSpinBoxes.size()); for (int32_t i = 0; i < numRowSpinBoxes; i++) { if (m_rowStretchFactorSpinBoxes[i]->isEnabled()) { configuration->setRowStretchFactor(i, m_rowStretchFactorSpinBoxes[i]->value()); } } m_caretPreferences->writeTileTabsConfigurations(); updateGraphicsWindows(); } /** * Update the graphics in any windows that have tile tabs enabled to the * selected tile tabs configuration in this dialog. */ void TileTabsConfigurationDialog::updateGraphicsWindows() { std::vector allBrowserWindows = GuiManager::get()->getAllOpenBrainBrowserWindows(); for (std::vector::iterator iter = allBrowserWindows.begin(); iter != allBrowserWindows.end(); iter++) { BrainBrowserWindow* bbw = *iter; if (bbw->isTileTabsSelected()) { // if (bbw->getSelectedTileTabsConfiguration() == getSelectedTileTabsConfiguration()) { EventManager::get()->sendEvent(EventGraphicsUpdateOneWindow(bbw->getBrowserWindowIndex()).getPointer()); // } } } } connectome-workbench-1.2.3+git41-gc4c6c90/src/GuiQt/TileTabsConfigurationDialog.h000066400000000000000000000107231300200146000273300ustar00rootroot00000000000000#ifndef __TILE_TABS_CONFIGURATION_DIALOG_H__ #define __TILE_TABS_CONFIGURATION_DIALOG_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 "WuQDialogNonModal.h" class QComboBox; class QDoubleSpinBox; class QLabel; class QLineEdit; class QPushButton; class QScrollArea; class QSpinBox; namespace caret { class BrainBrowserWindow; class CaretPreferences; class TileTabsConfiguration; class TileTabsConfigurationDialog : public WuQDialogNonModal { Q_OBJECT public: TileTabsConfigurationDialog(QWidget* parent); virtual ~TileTabsConfigurationDialog(); void updateDialogWithSelectedTileTabsFromWindow(BrainBrowserWindow* brainBrowserWindow); void updateDialog(); private: TileTabsConfigurationDialog(const TileTabsConfigurationDialog&); TileTabsConfigurationDialog& operator=(const TileTabsConfigurationDialog&); public: // ADD_NEW_METHODS_HERE private slots: void newConfigurationButtonClicked(); void deleteConfigurationButtonClicked(); void renameConfigurationButtonClicked(); void configurationComboBoxItemSelected(int); void numberOfRowsOrColumnsChanged(); void configurationStretchFactorWasChanged(); protected: void focusGained(); private: // ADD_NEW_MEMBERS_HERE enum { GRID_LAYOUT_COLUMN_INDEX_FOR_LABELS = 0, GRID_LAYOUT_COLUMN_INDEX_FOR_ROW_CONTROLS = 1, GRID_LAYOUT_COLUMN_INDEX_FOR_COLUMN_CONTROLS = 2 }; void selectTileTabConfigurationByUniqueID(const AString& uniqueID); AString getSelectedTileTabsConfigurationUniqueID(); TileTabsConfiguration* getSelectedTileTabsConfiguration(); QWidget* createConfigurationSelectionWidget(); QWidget* createEditConfigurationWidget(); void updateBrowserWindowsTileTabsConfigurationSelection(); void updateStretchFactors(); void updateGraphicsWindows(); void selectConfigurationFromComboBoxIndex(int indx); void readConfigurationsFromPreferences(); QPushButton* m_newConfigurationPushButton; QPushButton* m_deleteConfigurationPushButton; QPushButton* m_renameConfigurationPushButton; QComboBox* m_configurationSelectionComboBox; QLineEdit* m_nameLineEdit; QSpinBox* m_numberOfRowsSpinBox; QSpinBox* m_numberOfColumnsSpinBox; QScrollArea* m_stretchFactorScrollArea; QWidget* m_stretchFactorWidget; std::vector m_stretchFactorIndexLabels; std::vector m_rowStretchFactorSpinBoxes; std::vector m_columnStretchFactorSpinBoxes; /** Blocks reading of preferences since that may invalidate data pointers */ bool m_blockReadConfigurationsFromPreferences; /** browser window from which this dialog was last displayed */ BrainBrowserWindow* m_brainBrowserWindow; /** * Keep a pointer to preferences but DO NOT delete it * since the preferences are managed by the session * manager. */ CaretPreferences* m_caretPreferences; }; #ifdef __TILE_TABS_CONFIGURATION_DIALOG_DECLARE__ // #endif // __TILE_TABS_CONFIGURATION_DIALOG_DECLARE__ } // namespace #endif //__TILE_TABS_CONFIGURATION_DIALOG_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/GuiQt/UserInputModeAbstract.cxx000066400000000000000000000133371300200146000265570ustar00rootroot00000000000000 /*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 __USER_INPUT_MODE_ABSTRACT_DECLARE__ #include "UserInputModeAbstract.h" #undef __USER_INPUT_MODE_ABSTRACT_DECLARE__ #include #include #include "CaretAssert.h" #include "MouseEvent.h" using namespace caret; /** * \class caret::UserInputModeAbstract * \brief Abstract class for processing user input events * \ingroup GuiQt * * Classes implementing this interface receive * user input events from the OpenGL graphics * region of a BrowserWindow containing brain * models. */ /** * Constructor. */ UserInputModeAbstract::UserInputModeAbstract(const UserInputMode inputMode) : CaretObject(), m_userInputMode(inputMode), m_widgetForToolBar(NULL), m_mousePositionValid(false) { m_mousePositionEvent.grabNew(new MouseEvent(NULL, NULL, -1, 0, 0, 0, 0, 0, 0, false)); } /** * Destructor. */ UserInputModeAbstract::~UserInputModeAbstract() { /* * If the widget does not have a parent, then it is not * displayed (owned by another QWidget class) and must * be destroyed to avoid a memory leak. */ if (m_widgetForToolBar != NULL) { if (m_widgetForToolBar->parent() == 0) { delete m_widgetForToolBar; } m_widgetForToolBar = NULL; } } /** * @return The input mode enumerated type. */ UserInputModeAbstract::UserInputMode UserInputModeAbstract::getUserInputMode() const { return m_userInputMode; } /** * @return A widget for display at the bottom of the * Browser Window Toolbar when this mode is active. * If no user-interface controls are needed, this * method will return NULL. */ QWidget* UserInputModeAbstract::getWidgetForToolBar() { return m_widgetForToolBar; } /** * Set the widget that is displayed in the toolbar when * the user input mode is active. * * @param widgetForToolBar * Widget that is displayed in toolbar, may be NULL indicating * no widget. */ void UserInputModeAbstract::setWidgetForToolBar(QWidget* widgetForToolBar) { m_widgetForToolBar = widgetForToolBar; } /** * Process a selection that was made from the browser window's edit menu. * Intended for override by sub-classes. * * @param editMenuItem * Item that was selected from the edit menu. */ void UserInputModeAbstract::processEditMenuItemSelection(const BrainBrowserWindowEditMenuItemEnum::Enum /*editMenuItem*/) { } /** * Get the menu items that should be enabled for the current user input processor. * Intended for override by sub-classes. * Unless this method is overridden, all items on Edit menu are disabled. * * @param enabledEditMenuItemsOut * Upon exit contains edit menu items that should be enabled. * @param redoMenuItemSuffixTextOut * If the redo menu is enabled, the contents of string becomes * the suffix for the 'Redo' menu item. * @param undoMenuItemSuffixTextOut * If the undo menu is enabled, the contents of string becomes * the suffix for the 'Undo' menu item. * @param pasteTextOut * If not empty, this text is shown for the PASTE menu item * @param pasteSpecialTextOut * If not empty, this text is shown for the PASTE_SPECIAL menu item */ void UserInputModeAbstract::getEnabledEditMenuItems(std::vector& enabledEditMenuItemsOut, AString& redoMenuItemSuffixTextOut, AString& undoMenuItemSuffixTextOut, AString& pasteTextOut, AString& pasteSpecialTextOut) { enabledEditMenuItemsOut.clear(); redoMenuItemSuffixTextOut = ""; undoMenuItemSuffixTextOut = ""; pasteTextOut = ""; pasteSpecialTextOut = ""; } /** * Get information about the current mouse location. * * @return Pointer to a MouseEvent or NULL if the * mouse location is invalid. */ const MouseEvent* UserInputModeAbstract::getMousePosition() const { if (m_mousePositionValid) { return m_mousePositionEvent; } return NULL; } /** * Set the position of the mouse. * * @param mouseEvent * Information about the current mouse location. * @param valid * True if the mouse position is valid, else false. */ void UserInputModeAbstract::setMousePosition(const MouseEvent* mouseEvent, const bool valid) { m_mousePositionValid = valid; if (m_mousePositionValid) { CaretAssert(mouseEvent); *m_mousePositionEvent = *mouseEvent; } } connectome-workbench-1.2.3+git41-gc4c6c90/src/GuiQt/UserInputModeAbstract.h000066400000000000000000000200301300200146000261700ustar00rootroot00000000000000#ifndef __USER_INPUT_MODE_ABSTRACT_H__ #define __USER_INPUT_MODE_ABSTRACT_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 "BrainBrowserWindowEditMenuItemEnum.h" #include "CaretObject.h" #include "CaretPointer.h" #include "CursorEnum.h" class QPoint; class QWidget; namespace caret { class BrainOpenGLViewportContent; class BrainOpenGLWidget; class KeyEvent; class MouseEvent; class UserInputModeAbstract : public CaretObject { public: /** Enumerated type for input modes */ enum UserInputMode { /** Invalid */ INVALID, /** Annotation Operations */ ANNOTATIONS, /** Border Operations */ BORDERS, /** Foci Operations */ FOCI, /** Image Operations */ IMAGE, /** Viewing Operations */ VIEW, /** Volume Edit Operations */ VOLUME_EDIT }; UserInputModeAbstract(const UserInputMode inputMode); virtual ~UserInputModeAbstract(); /** * @return The input mode enumerated type. */ UserInputMode getUserInputMode() const; /** * Called when 'this' user input receiver is set * to receive events. */ virtual void initialize() = 0; /** * Called when 'this' user input receiver is no * longer set to receive events. */ virtual void finish() = 0; /** * Called to update the input receiver for various events. */ virtual void update() = 0; QWidget* getWidgetForToolBar(); /** * @return The cursor for display in the OpenGL widget. */ virtual CursorEnum::Enum getCursor() const = 0; /** * Process a key press event * * @param keyEvent * Key event information. */ virtual void keyPressEvent(const KeyEvent& /*keyEvent*/) { } /** * Process a mouse left double-click event. * * @param mouseEvent * Mouse event information. */ virtual void mouseLeftDoubleClick(const MouseEvent& /*mouseEvent*/) { } /** * Process a mouse left press event. * * @param mouseEvent * Mouse event information. */ virtual void mouseLeftPress(const MouseEvent& /*mouseEvent*/) { } /** * Process a mouse left release event. * * @param mouseEvent * Mouse event information. */ virtual void mouseLeftRelease(const MouseEvent& /*mouseEvent*/) { } /** * Process a mouse left click event. * * @param mouseEvent * Mouse event information. */ virtual void mouseLeftClick(const MouseEvent& /*mouseEvent*/) { } /** * Process a mouse left click with shift key down event. * * @param mouseEvent * Mouse event information. */ virtual void mouseLeftClickWithShift(const MouseEvent& /*mouseEvent*/) { } /** * Process a mouse left click with ctrl and shift keys down event. * * @param mouseEvent * Mouse event information. */ virtual void mouseLeftClickWithCtrlShift(const MouseEvent& /*mouseEvent*/) { } /** * Process a mouse left drag with no keys down event. * * @param mouseEvent * Mouse event information. */ virtual void mouseLeftDrag(const MouseEvent& /*mouseEvent*/) { } /** * Process a mouse left drag with only the alt key down event. * * @param mouseEvent * Mouse event information. */ virtual void mouseLeftDragWithAlt(const MouseEvent& /*mouseEvent*/) { } /** * Process a mouse left drag with ctrl key down event. * * @param mouseEvent * Mouse event information. */ virtual void mouseLeftDragWithCtrl(const MouseEvent& /*mouseEvent*/) { } /** * Process a mouse left drag with ctrl and shift keys down event. * * @param mouseEvent * Mouse event information. */ virtual void mouseLeftDragWithCtrlShift(const MouseEvent& /*mouseEvent*/) { } /** * Process a mouse left drag with shift key down event. * * @param mouseEvent * Mouse event information. */ virtual void mouseLeftDragWithShift(const MouseEvent& /*mouseEvent*/) { } /** * Process a mouse move with no buttons or keys down * * @param mouseEvent * Mouse event information. */ virtual void mouseMove(const MouseEvent& /*mouseEvent*/) { } /** * Process a mouse move with no buttons and shift key down * * @param mouseEvent * Mouse event information. */ virtual void mouseMoveWithShift(const MouseEvent& /*mouseEvent*/) { } /** * Show a context menu (pop-up menu at mouse location) * * @param mouseEvent * Mouse event information. * @param menuPosition * Point at which menu is displayed (passed to QMenu::exec()) * @param openGLWidget * OpenGL widget in which context menu is requested */ virtual void showContextMenu(const MouseEvent& /*mouseEvent*/, const QPoint& /* menuPosition */, BrainOpenGLWidget* /* openGLWidget */) { } virtual void processEditMenuItemSelection(const BrainBrowserWindowEditMenuItemEnum::Enum editMenuItem); virtual void getEnabledEditMenuItems(std::vector& enabledEditMenuItemsOut, AString& redoMenuItemSuffixTextOut, AString& undoMenuItemSuffixTextOut, AString& pasteTextOut, AString& pasteSpecialTextOut); const MouseEvent* getMousePosition() const; void setMousePosition(const MouseEvent* mouseEvent, const bool valid); protected: void setWidgetForToolBar(QWidget* widgetForToolBar); private: UserInputModeAbstract(const UserInputModeAbstract&); UserInputModeAbstract& operator=(const UserInputModeAbstract&); const UserInputMode m_userInputMode; QWidget* m_widgetForToolBar; bool m_mousePositionValid; CaretPointer m_mousePositionEvent; // ADD_NEW_MEMBERS_HERE }; #ifdef __USER_INPUT_MODE_ABSTRACT_DECLARE__ // #endif // __USER_INPUT_MODE_ABSTRACT_DECLARE__ } // namespace #endif //__USER_INPUT_MODE_ABSTRACT_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/GuiQt/UserInputModeAnnotations.cxx000066400000000000000000002106301300200146000273040ustar00rootroot00000000000000 /*LICENSE_START*/ /* * Copyright (C) 2015 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_MODE_ANNOTATIONS_DECLARE__ #include "UserInputModeAnnotations.h" #undef __USER_INPUT_MODE_ANNOTATIONS_DECLARE__ #include #include "AnnotationChangeCoordinateDialog.h" #include "AnnotationCreateDialog.h" #include "AnnotationColorBar.h" #include "AnnotationCoordinate.h" #include "AnnotationCoordinateInformation.h" #include "AnnotationFile.h" #include "AnnotationManager.h" #include "AnnotationOneDimensionalShape.h" #include "AnnotationPasteDialog.h" #include "AnnotationRedoUndoCommand.h" #include "AnnotationSpatialModification.h" #include "AnnotationText.h" #include "AnnotationTextEditorDialog.h" #include "AnnotationTwoDimensionalShape.h" #include "Brain.h" #include "BrainOpenGLViewportContent.h" #include "BrainOpenGLWidget.h" #include "BrowserTabContent.h" #include "CaretAssert.h" #include "CaretLogger.h" #include "CaretUndoStack.h" #include "CursorEnum.h" #include "CaretPreferences.h" #include "DisplayPropertiesAnnotation.h" #include "EventAnnotationCreateNewType.h" #include "EventAnnotationGetDrawnInWindow.h" #include "EventGraphicsUpdateAllWindows.h" #include "EventIdentificationRequest.h" #include "EventUserInterfaceUpdate.h" #include "EventManager.h" #include "GuiManager.h" #include "IdentificationManager.h" #include "KeyEvent.h" #include "ModelSurfaceMontage.h" #include "MouseEvent.h" #include "SelectionItemAnnotation.h" #include "SelectionManager.h" #include "SelectionItemSurfaceNode.h" #include "SelectionItemVoxel.h" #include "SessionManager.h" #include "Surface.h" #include "SurfaceMontageViewport.h" #include "UserInputModeAnnotationsContextMenu.h" #include "UserInputModeAnnotationsWidget.h" #include "WuQMessageBox.h" using namespace caret; /** * \class caret::UserInputModeAnnotations * \brief Input mode processor for Annotations. * \ingroup GuiQt */ /** * Constructor. */ UserInputModeAnnotations::UserInputModeAnnotations(const int32_t windowIndex) : UserInputModeView(UserInputModeAbstract::ANNOTATIONS), m_browserWindowIndex(windowIndex), m_annotationUnderMouse(NULL), m_annotationBeingDragged(NULL) { m_allowMultipleSelectionModeFlag = true; m_mode = MODE_SELECT; m_annotationUnderMouseSizeHandleType = AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_NONE; m_modeNewAnnotationFileSpaceAndType.grabNew(new NewAnnotationFileSpaceAndType(NULL, AnnotationCoordinateSpaceEnum::PIXELS, AnnotationTypeEnum::LINE)); m_newAnnotationCreatingWithMouseDrag.grabNew(NULL); m_annotationToolsWidget = new UserInputModeAnnotationsWidget(this, m_browserWindowIndex); setWidgetForToolBar(m_annotationToolsWidget); EventManager::get()->addEventListener(this, EventTypeEnum::EVENT_ANNOTATION_CREATE_NEW_TYPE); } /** * Destructor. */ UserInputModeAnnotations::~UserInputModeAnnotations() { EventManager::get()->removeAllEventsFromListener(this); } /** * Receive an event. * * @param event * An event for which this instance is listening. */ void UserInputModeAnnotations::receiveEvent(Event* event) { if (event->getEventType() == EventTypeEnum::EVENT_ANNOTATION_CREATE_NEW_TYPE) { EventAnnotationCreateNewType* annotationEvent = dynamic_cast(event); CaretAssert(annotationEvent); AnnotationManager* annotationManager = GuiManager::get()->getBrain()->getAnnotationManager(); annotationManager->deselectAllAnnotationsForEditing(m_browserWindowIndex); resetAnnotationUnderMouse(); m_modeNewAnnotationFileSpaceAndType.grabNew(new NewAnnotationFileSpaceAndType(annotationEvent->getAnnotationFile(), annotationEvent->getAnnotationSpace(), annotationEvent->getAnnotationType())); setMode(MODE_NEW_WITH_CLICK); EventManager::get()->sendEvent(EventGraphicsUpdateAllWindows().getPointer()); } } /** * Called when 'this' user input receiver is set * to receive events. */ void UserInputModeAnnotations::initialize() { m_mode = MODE_SELECT; DisplayPropertiesAnnotation* dpa = GuiManager::get()->getBrain()->getDisplayPropertiesAnnotation(); dpa->setDisplayAnnotations(true); resetAnnotationUnderMouse(); } /** * Called when 'this' user input receiver is no * longer set to receive events. */ void UserInputModeAnnotations::finish() { m_mode = MODE_SELECT; resetAnnotationUnderMouse(); } void UserInputModeAnnotations::resetAnnotationUnderMouse() { m_annotationUnderMouse = NULL; m_annotationUnderMouseSizeHandleType = AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_NONE; m_annotationBeingDragged = NULL; m_annotationBeingDraggedHandleType = AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_NONE; } /** * Called to update the input receiver for various events. */ void UserInputModeAnnotations::update() { } /** * Get a description of this object's content. * @return String describing this object's content. */ AString UserInputModeAnnotations::toString() const { return "UserInputModeBorders"; } /** * @return the mode. */ UserInputModeAnnotations::Mode UserInputModeAnnotations::getMode() const { return m_mode; } /** * Set the mode. * @param mode * New value for mode. */ void UserInputModeAnnotations::setMode(const Mode mode) { if (m_mode != mode) { m_mode = mode; } EventManager::get()->sendEvent(EventUserInterfaceUpdate().getPointer()); } /** * @return The cursor for display in the OpenGL widget. */ CursorEnum::Enum UserInputModeAnnotations::getCursor() const { CursorEnum::Enum cursor = CursorEnum::CURSOR_DEFAULT; switch (m_mode) { case MODE_NEW_WITH_CLICK: cursor = CursorEnum::CURSOR_CROSS; break; case MODE_NEW_WITH_DRAG: cursor = CursorEnum::CURSOR_CROSS; break; case MODE_PASTE: cursor = CursorEnum::CURSOR_CROSS; break; case MODE_PASTE_SPECIAL: cursor = CursorEnum::CURSOR_CROSS; break; case MODE_SELECT: if (m_annotationUnderMouse != NULL) { cursor = CursorEnum::CURSOR_FOUR_ARROWS; switch (m_annotationUnderMouseSizeHandleType) { case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_BOX_BOTTOM: cursor = CursorEnum::CURSOR_RESIZE_VERTICAL; break; case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_BOX_BOTTOM_LEFT: cursor = CursorEnum::CURSOR_RESIZE_BOTTOM_LEFT_TOP_RIGHT; break; case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_BOX_BOTTOM_RIGHT: cursor = CursorEnum::CURSOR_RESIZE_BOTTOM_RIGHT_TOP_LEFT; break; case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_BOX_LEFT: cursor = CursorEnum::CURSOR_RESIZE_HORIZONTAL; break; case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_BOX_RIGHT: cursor = CursorEnum::CURSOR_RESIZE_HORIZONTAL; break; case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_BOX_TOP: cursor = CursorEnum::CURSOR_RESIZE_VERTICAL; break; case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_BOX_TOP_LEFT: cursor = CursorEnum::CURSOR_RESIZE_BOTTOM_RIGHT_TOP_LEFT; break; case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_BOX_TOP_RIGHT: cursor = CursorEnum::CURSOR_RESIZE_BOTTOM_LEFT_TOP_RIGHT; break; case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_LINE_END: cursor = CursorEnum::CURSOR_RESIZE_BOTTOM_LEFT_TOP_RIGHT; break; case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_LINE_START: cursor = CursorEnum::CURSOR_RESIZE_BOTTOM_LEFT_TOP_RIGHT; break; case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_NONE: cursor = CursorEnum::CURSOR_FOUR_ARROWS; break; case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_ROTATION: cursor = CursorEnum::CURSOR_ROTATION; break; } } break; case MODE_SET_COORDINATE_ONE: cursor = CursorEnum::CURSOR_CROSS; break; case MODE_SET_COORDINATE_TWO: cursor = CursorEnum::CURSOR_CROSS; break; } return cursor; } /** * Delete all selected annotations. */ void UserInputModeAnnotations::deleteSelectedAnnotations() { AnnotationManager* annotationManager = GuiManager::get()->getBrain()->getAnnotationManager(); if (annotationManager->isAnnotationSelectedForEditingDeletable(m_browserWindowIndex)) { std::vector selectedAnnotations = annotationManager->getAnnotationsSelectedForEditing(m_browserWindowIndex); if ( ! selectedAnnotations.empty()) { AnnotationRedoUndoCommand* undoCommand = new AnnotationRedoUndoCommand(); undoCommand->setModeDeleteAnnotations(selectedAnnotations); AString errorMessage; if ( ! annotationManager->applyCommand(undoCommand, errorMessage)) { WuQMessageBox::errorOk(m_annotationToolsWidget, errorMessage); } } } else { std::vector selectedAnnotations = annotationManager->getAnnotationsSelectedForEditing(m_browserWindowIndex); for (std::vector::iterator iter = selectedAnnotations.begin(); iter != selectedAnnotations.end(); iter++) { Annotation* ann = *iter; CaretAssert(ann); if (ann->getType() == AnnotationTypeEnum::COLOR_BAR) { AnnotationColorBar* colorBar = dynamic_cast(ann); CaretAssert(colorBar); colorBar->setDisplayed(false); } } } EventManager::get()->sendEvent(EventUserInterfaceUpdate().getPointer()); EventManager::get()->sendEvent(EventGraphicsUpdateAllWindows().getPointer()); } /** * Process a key press event * * @param keyEvent * Key event information. */ void UserInputModeAnnotations::keyPressEvent(const KeyEvent& keyEvent) { const int32_t keyCode = keyEvent.getKeyCode(); switch (keyCode) { case Qt::Key_Backspace: case Qt::Key_Delete: { switch (m_mode) { case MODE_NEW_WITH_CLICK: break; case MODE_NEW_WITH_DRAG: break; case MODE_PASTE: break; case MODE_PASTE_SPECIAL: break; case MODE_SELECT: deleteSelectedAnnotations(); break; case MODE_SET_COORDINATE_ONE: break; case MODE_SET_COORDINATE_TWO: break; } } break; case Qt::Key_Escape: { bool selectModeFlag = false; switch (m_mode) { case MODE_NEW_WITH_CLICK: break; case MODE_NEW_WITH_DRAG: break; case MODE_PASTE: selectModeFlag = true; break; case MODE_PASTE_SPECIAL: selectModeFlag = true; break; case MODE_SELECT: break; case MODE_SET_COORDINATE_ONE: selectModeFlag = true; break; case MODE_SET_COORDINATE_TWO: selectModeFlag = true; break; } if (selectModeFlag) { setMode(MODE_SELECT); EventManager::get()->sendEvent(EventGraphicsUpdateAllWindows().getPointer()); } } break; case Qt::Key_Down: case Qt::Key_Left: case Qt::Key_Right: case Qt::Key_Up: { Annotation* selectedAnnotation = getSingleSelectedAnnotation(); if (selectedAnnotation != NULL) { bool changeCoordFlag = false; bool moveOnePixelFlag = false; switch (selectedAnnotation->getCoordinateSpace()) { case AnnotationCoordinateSpaceEnum::STEREOTAXIC: changeCoordFlag = true; break; case AnnotationCoordinateSpaceEnum::PIXELS: break; case AnnotationCoordinateSpaceEnum::SURFACE: break; case AnnotationCoordinateSpaceEnum::TAB: changeCoordFlag = true; moveOnePixelFlag = true; break; case AnnotationCoordinateSpaceEnum::WINDOW: changeCoordFlag = true; moveOnePixelFlag = true; break; } if (changeCoordFlag) { float distanceX = 1.0; float distanceY = 1.0; if (moveOnePixelFlag) { const float pixelHeight = keyEvent.getOpenGLWidget()->height(); const float pixelWidth = keyEvent.getOpenGLWidget()->width(); /* * 100 is "full width/height" in relative coordinates. */ distanceX = 100.0 / pixelWidth; distanceY = 100.0 / pixelHeight; } if (keyEvent.isShiftKeyDownFlag()) { const float multiplier = 10; distanceX *= multiplier; distanceY *= multiplier; } float dx = 0.0; float dy = 0.0; switch (keyCode) { case Qt::Key_Down: dy = -distanceY; break; case Qt::Key_Left: dx = -distanceX; break; case Qt::Key_Right: dx = distanceX; break; case Qt::Key_Up: dy = distanceY; break; default: CaretAssert(0); break; } AnnotationOneDimensionalShape* oneDim = dynamic_cast(selectedAnnotation); AnnotationTwoDimensionalShape* twoDim = dynamic_cast(selectedAnnotation); { bool surfaceFlag = false; switch (selectedAnnotation->getCoordinateSpace()) { case AnnotationCoordinateSpaceEnum::STEREOTAXIC: break; case AnnotationCoordinateSpaceEnum::PIXELS: break; case AnnotationCoordinateSpaceEnum::SURFACE: surfaceFlag = true; break; case AnnotationCoordinateSpaceEnum::TAB: break; case AnnotationCoordinateSpaceEnum::WINDOW: break; } if ( ! surfaceFlag) { AnnotationRedoUndoCommand* undoCommand = new AnnotationRedoUndoCommand(); std::vector annotations; annotations.push_back(selectedAnnotation); if (oneDim != NULL) { AnnotationCoordinate startCoord = *oneDim->getStartCoordinate(); float xyzStart[3]; startCoord.getXYZ(xyzStart); xyzStart[0] += dx; xyzStart[1] += dy; startCoord.setXYZ(xyzStart); AnnotationCoordinate endCoord = *oneDim->getEndCoordinate(); float xyzEnd[3]; endCoord.getXYZ(xyzEnd); xyzEnd[0] += dx; xyzEnd[1] += dy; endCoord.setXYZ(xyzEnd); undoCommand->setModeCoordinateOneAndTwo(startCoord, endCoord, annotations); } else if (twoDim != NULL) { AnnotationCoordinate coord = *twoDim->getCoordinate(); float xyz[3]; coord.getXYZ(xyz); xyz[0] += dx; xyz[1] += dy; coord.setXYZ(xyz); undoCommand->setModeCoordinateOne(coord, annotations); } else { CaretAssert(0); } if ( ! keyEvent.isFirstKeyPressFlag()) { undoCommand->setMergeEnabled(true); } AnnotationManager* annMan = GuiManager::get()->getBrain()->getAnnotationManager(); AString errorMessage; if ( ! annMan->applyCommand(undoCommand, errorMessage)) { WuQMessageBox::errorOk(m_annotationToolsWidget, errorMessage); } EventManager::get()->sendSimpleEvent(EventTypeEnum::EVENT_ANNOTATION_TOOLBAR_UPDATE); EventManager::get()->sendEvent(EventGraphicsUpdateAllWindows().getPointer()); } } } } } break; } } /** * Process a mouse left drag with no keys down event. * * @param mouseEvent * Mouse event information. */ void UserInputModeAnnotations::mouseLeftDrag(const MouseEvent& mouseEvent) { AnnotationManager* annotationManager = GuiManager::get()->getBrain()->getAnnotationManager(); switch (m_mode) { case MODE_NEW_WITH_CLICK: { if (m_newAnnotationCreatingWithMouseDrag != NULL) { m_newAnnotationCreatingWithMouseDrag.grabNew(NULL); } /* * Note ALWAYS use WINDOW space for the drag anntotion. * Otherwise it will not get displayed if surface/stereotaxic */ m_newAnnotationCreatingWithMouseDrag.grabNew(new NewMouseDragCreateAnnotation(m_modeNewAnnotationFileSpaceAndType->m_annotationFile, AnnotationCoordinateSpaceEnum::WINDOW, m_modeNewAnnotationFileSpaceAndType->m_annotationType, mouseEvent)); m_mode = MODE_NEW_WITH_DRAG; return; } break; case MODE_NEW_WITH_DRAG: userDrawingAnnotationFromMouseDrag(mouseEvent); return; break; case MODE_PASTE: break; case MODE_PASTE_SPECIAL: break; case MODE_SELECT: break; case MODE_SET_COORDINATE_ONE: break; case MODE_SET_COORDINATE_TWO: break; } AnnotationCoordinateSpaceEnum::Enum draggingCoordinateSpace = AnnotationCoordinateSpaceEnum::PIXELS; std::vector selectedAnnotations = annotationManager->getAnnotationsSelectedForEditing(m_browserWindowIndex); const int32_t numSelectedAnnotations = static_cast(selectedAnnotations.size()); bool draggingValid = false; if (numSelectedAnnotations == 1) { draggingCoordinateSpace = selectedAnnotations[0]->getCoordinateSpace(); draggingValid = true; } else if (numSelectedAnnotations > 1) { if (m_annotationBeingDraggedHandleType == AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_NONE) { bool allSameSpaceFlag = true; draggingCoordinateSpace = selectedAnnotations[0]->getCoordinateSpace(); for (int32_t i = 1; i < numSelectedAnnotations; i++) { if (selectedAnnotations[i]->getCoordinateSpace() != draggingCoordinateSpace) { allSameSpaceFlag = false; break; } } if (allSameSpaceFlag) { draggingValid = true; } } } if (draggingValid) { BrainOpenGLViewportContent* vpContent = mouseEvent.getViewportContent(); if (vpContent == NULL) { return; } AnnotationCoordinateInformation coordInfo; AnnotationCoordinateInformation::createCoordinateInformationFromXY(mouseEvent.getOpenGLWidget(), mouseEvent.getViewportContent(), mouseEvent.getX(), mouseEvent.getY(), coordInfo); float spaceOriginX = 0.0; float spaceOriginY = 0.0; float spaceWidth = 0.0; float spaceHeight = 0.0; switch (draggingCoordinateSpace) { case AnnotationCoordinateSpaceEnum::STEREOTAXIC: { /* * Want viewport within montage that contains the surface */ int montVP[4]; vpContent->getSurfaceMontageModelViewport(mouseEvent.getX(), mouseEvent.getY(), montVP); spaceOriginX = montVP[0]; spaceOriginY = montVP[1]; spaceWidth = montVP[2]; 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: { /* * Want viewport within montage that contains the surface */ int montVP[4]; vpContent->getSurfaceMontageModelViewport(mouseEvent.getX(), mouseEvent.getY(), montVP); spaceOriginX = montVP[0]; spaceOriginY = montVP[1]; spaceWidth = montVP[2]; spaceHeight = montVP[3]; /* * Also need to find viewport where mouse was originally * pressed. If the viewports are different, then * mouse has left the viewport in which it was pressed * and the mouse position is no longer valid. */ int pressVP[4]; vpContent->getSurfaceMontageModelViewport(mouseEvent.getPressedX(), mouseEvent.getPressedY(), pressVP); if ((montVP[0] != pressVP[0]) || (montVP[1] != pressVP[1]) || (montVP[2] != pressVP[2]) || (montVP[3] != pressVP[3])) { draggingValid = false; } } break; case AnnotationCoordinateSpaceEnum::TAB: { int viewport[4]; vpContent->getModelViewport(viewport); spaceOriginX = viewport[0]; spaceOriginY = viewport[1]; spaceWidth = viewport[2]; spaceHeight = viewport[3]; } break; case AnnotationCoordinateSpaceEnum::WINDOW: { int viewport[4]; vpContent->getWindowViewport(viewport); spaceOriginX = viewport[0]; spaceOriginY = viewport[1]; spaceWidth = viewport[2]; spaceHeight = viewport[3]; } break; } if (draggingValid) { const float dx = mouseEvent.getDx(); const float dy = mouseEvent.getDy(); const float mouseViewportX = mouseEvent.getX() - spaceOriginX; const float mouseViewportY = mouseEvent.getY() - spaceOriginY; const float mousePressViewportX = mouseEvent.getPressedX() - spaceOriginX; const float mousePressViewportY = mouseEvent.getPressedY() - spaceOriginY; AnnotationSpatialModification annSpatialMod(m_annotationBeingDraggedHandleType, spaceWidth, spaceHeight, mousePressViewportX, mousePressViewportY, mouseViewportX, mouseViewportY, dx, dy, mouseEvent.isFirstDragging()); if (coordInfo.m_surfaceNodeValid) { annSpatialMod.setSurfaceCoordinateAtMouseXY(coordInfo.m_surfaceStructure, coordInfo.m_surfaceNumberOfNodes, coordInfo.m_surfaceNodeIndex); } if (coordInfo.m_modelXYZValid) { annSpatialMod.setStereotaxicCoordinateAtMouseXY(coordInfo.m_modelXYZ[0], coordInfo.m_modelXYZ[1], coordInfo.m_modelXYZ[2]); } std::vector annotationsBeforeMoveAndResize; std::vector annotationsAfterMoveAndResize; for (int32_t i = 0; i < numSelectedAnnotations; i++) { Annotation* annotationModified(selectedAnnotations[i]->clone()); if (annotationModified->applySpatialModification(annSpatialMod)) { annotationsBeforeMoveAndResize.push_back(selectedAnnotations[i]); annotationsAfterMoveAndResize.push_back(annotationModified); } else { delete annotationModified; annotationModified = NULL; } } CaretAssert(annotationsAfterMoveAndResize.size() == annotationsBeforeMoveAndResize.size()); if ( ! annotationsAfterMoveAndResize.empty()) { AnnotationRedoUndoCommand* command = new AnnotationRedoUndoCommand(); command->setModeLocationAndSize(annotationsBeforeMoveAndResize, annotationsAfterMoveAndResize); if ( ! mouseEvent.isFirstDragging()) { command->setMergeEnabled(true); } AString errorMessage; if ( ! annotationManager->applyCommand(command, errorMessage)) { WuQMessageBox::errorOk(m_annotationToolsWidget, errorMessage); } } for (std::vector::iterator iter = annotationsAfterMoveAndResize.begin(); iter != annotationsAfterMoveAndResize.end(); iter++) { delete *iter; } annotationsAfterMoveAndResize.clear(); EventManager::get()->sendEvent(EventGraphicsUpdateAllWindows().getPointer()); EventManager::get()->sendSimpleEvent(EventTypeEnum::EVENT_ANNOTATION_TOOLBAR_UPDATE); } } } /** * Process a mouse left drag with only the alt key down event. * * @param mouseEvent * Mouse event information. */ void UserInputModeAnnotations::mouseLeftDragWithAlt(const MouseEvent& /*mouseEvent*/) { } /** * Process a mouse left drag with ctrl key down event. * * @param mouseEvent * Mouse event information. */ void UserInputModeAnnotations::mouseLeftDragWithCtrl(const MouseEvent& /*mouseEvent*/) { } /** * Process a mouse left drag with ctrl and shift keys down event. * * @param mouseEvent * Mouse event information. */ void UserInputModeAnnotations::mouseLeftDragWithCtrlShift(const MouseEvent& /*mouseEvent*/) { } /** * Process a mouse left drag with shift key down event. * * @param mouseEvent * Mouse event information. */ void UserInputModeAnnotations::mouseLeftDragWithShift(const MouseEvent& mouseEvent) { mouseLeftDrag(mouseEvent); } /** * Process a mouse left click event. * * @param mouseEvent * Mouse event information. */ void UserInputModeAnnotations::mouseLeftClick(const MouseEvent& mouseEvent) { switch (m_mode) { case MODE_NEW_WITH_CLICK: processModeNewMouseLeftClick(mouseEvent); break; case MODE_NEW_WITH_DRAG: break; case MODE_PASTE: pasteAnnotationFromAnnotationClipboard(mouseEvent); break; case MODE_PASTE_SPECIAL: pasteAnnotationFromAnnotationClipboardAndChangeSpace(mouseEvent); break; case MODE_SELECT: break; case MODE_SET_COORDINATE_ONE: processModeSetCoordinate(mouseEvent); break; case MODE_SET_COORDINATE_TWO: processModeSetCoordinate(mouseEvent); break; } } /** * Process a mouse left click with Shift key event. * * @param mouseEvent * Mouse event information. */ void UserInputModeAnnotations::mouseLeftClickWithShift(const MouseEvent& mouseEvent) { switch (m_mode) { case MODE_NEW_WITH_CLICK: break; case MODE_NEW_WITH_DRAG: break; case MODE_PASTE: break; case MODE_PASTE_SPECIAL: break; case MODE_SELECT: if (m_allowMultipleSelectionModeFlag) { processMouseSelectAnnotation(mouseEvent, true); } break; case MODE_SET_COORDINATE_ONE: break; case MODE_SET_COORDINATE_TWO: break; } } /** * Process a mouse left press event. * * @param mouseEvent * Mouse event information. */ void UserInputModeAnnotations::mouseLeftPress(const MouseEvent& mouseEvent) { switch (m_mode) { case MODE_NEW_WITH_CLICK: break; case MODE_NEW_WITH_DRAG: break; case MODE_PASTE: break; case MODE_PASTE_SPECIAL: break; case MODE_SELECT: processMouseSelectAnnotation(mouseEvent, false); break; case MODE_SET_COORDINATE_ONE: break; case MODE_SET_COORDINATE_TWO: break; } } /** * Set the annotation under the mouse that results in update of the cursor. * * @param mouseEvent * Mouse event information. * @param annotationIDIn * Optional (if not NULL) annotation ID that may be provided by caller * and can be used to avoid an identification operation. */ void UserInputModeAnnotations::setAnnotationUnderMouse(const MouseEvent& mouseEvent, SelectionItemAnnotation* annotationIDIn) { m_annotationUnderMouse = NULL; m_annotationUnderMouseSizeHandleType = AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_NONE; BrainOpenGLWidget* openGLWidget = mouseEvent.getOpenGLWidget(); SelectionItemAnnotation* annotationID = annotationIDIn; if (annotationID == NULL) { annotationID = openGLWidget->performIdentificationAnnotations(mouseEvent.getX(), mouseEvent.getY()); } if (annotationID->isValid()) { m_annotationUnderMouse = annotationID->getAnnotation(); m_annotationUnderMouseSizeHandleType = annotationID->getSizingHandle(); } openGLWidget->updateCursor(); } /** * User drawing a new annotation from dragging the mouse from corner/end to another * corner/end. * * @param mouseEvent * Mouse event issued when mouse button was released. */ void UserInputModeAnnotations::userDrawingAnnotationFromMouseDrag(const MouseEvent& mouseEvent) { if (m_newAnnotationCreatingWithMouseDrag != NULL) { m_newAnnotationCreatingWithMouseDrag->update(mouseEvent.getX(), mouseEvent.getY()); AnnotationManager* annotationManager = GuiManager::get()->getBrain()->getAnnotationManager(); annotationManager->setAnnotationBeingDrawnInWindow(m_browserWindowIndex, m_newAnnotationCreatingWithMouseDrag->getAnnotation()); EventManager::get()->sendEvent(EventGraphicsUpdateAllWindows().getPointer()); } } /** * Create a new annotation from dragging the mouse from corner/end to another * corner/end. * * @param mouseEvent * Mouse event issued when mouse button was released. */ void UserInputModeAnnotations::createNewAnnotationFromMouseDrag(const MouseEvent& mouseEvent) { if (m_newAnnotationCreatingWithMouseDrag != NULL) { Annotation* ann = AnnotationCreateDialog::newAnnotationFromSpaceTypeAndBounds(mouseEvent, m_modeNewAnnotationFileSpaceAndType->m_annotationSpace, m_modeNewAnnotationFileSpaceAndType->m_annotationType, m_modeNewAnnotationFileSpaceAndType->m_annotationFile); if (ann != NULL) { selectAnnotation(ann); } setMode(MODE_SELECT); m_newAnnotationCreatingWithMouseDrag.grabNew(NULL); AnnotationManager* annotationManager = GuiManager::get()->getBrain()->getAnnotationManager(); annotationManager->setAnnotationBeingDrawnInWindow(m_browserWindowIndex, NULL); EventManager::get()->sendEvent(EventGraphicsUpdateAllWindows().getPointer()); } } /** * Process a mouse left release event. * * @param mouseEvent * Mouse event information. */ void UserInputModeAnnotations::mouseLeftRelease(const MouseEvent& mouseEvent) { switch (m_mode) { case MODE_NEW_WITH_CLICK: break; case MODE_NEW_WITH_DRAG: createNewAnnotationFromMouseDrag(mouseEvent); m_mode = MODE_SELECT; break; case MODE_PASTE: break; case MODE_PASTE_SPECIAL: break; case MODE_SELECT: break; case MODE_SET_COORDINATE_ONE: break; case MODE_SET_COORDINATE_TWO: break; } m_annotationBeingDragged = NULL; setAnnotationUnderMouse(mouseEvent, NULL); } /** * Process a mouse left double-click event. * * @param mouseEvent * Mouse event information. */ void UserInputModeAnnotations::mouseLeftDoubleClick(const MouseEvent& mouseEvent) { const int32_t mouseX = mouseEvent.getX(); const int32_t mouseY = mouseEvent.getY(); BrainOpenGLWidget* openGLWidget = mouseEvent.getOpenGLWidget(); SelectionItemAnnotation* annotationID = openGLWidget->performIdentificationAnnotations(mouseX, mouseY); if (annotationID->isValid()) { Annotation* annotation = annotationID->getAnnotation(); if (annotation != NULL) { AnnotationText* textAnnotation = dynamic_cast(annotation); if (textAnnotation != NULL) { AnnotationTextEditorDialog ted(textAnnotation, openGLWidget); /* * Note: Y==0 is at top for widget. * Y==0 is at bottom for OpenGL mouse x,y */ ted.move(openGLWidget->mapToGlobal(QPoint(mouseX, (openGLWidget->height() - mouseY + 20)))); ted.exec(); EventManager::get()->sendSimpleEvent(EventTypeEnum::EVENT_ANNOTATION_TOOLBAR_UPDATE); } } } } /** * Process a mouse move with no buttons or keys down * * @param mouseEvent * Mouse event information. */ void UserInputModeAnnotations::mouseMove(const MouseEvent& mouseEvent) { setAnnotationUnderMouse(mouseEvent, NULL); } /** * Process a mouse move with no buttons and shift key down * * @param mouseEvent * Mouse event information. */ void UserInputModeAnnotations::mouseMoveWithShift(const MouseEvent& mouseEvent) { setAnnotationUnderMouse(mouseEvent, NULL); } /** * Process a mouse left click to set a coordinate. * * @param mouseEvent * Mouse event information. */ void UserInputModeAnnotations::processModeSetCoordinate(const MouseEvent& mouseEvent) { Annotation* selectedAnnotation = getSingleSelectedAnnotation(); if (selectedAnnotation == NULL) { return; } AnnotationCoordinateInformation coordInfo; AnnotationCoordinateInformation::createCoordinateInformationFromXY(mouseEvent.getOpenGLWidget(), mouseEvent.getViewportContent(), mouseEvent.getX(), mouseEvent.getY(), coordInfo); AnnotationOneDimensionalShape* oneDimAnn = dynamic_cast(selectedAnnotation); AnnotationTwoDimensionalShape* twoDimAnn = dynamic_cast(selectedAnnotation); AnnotationCoordinate* coordinate = NULL; AnnotationCoordinate* otherCoordinate = NULL; switch (m_mode) { case MODE_NEW_WITH_CLICK: break; case MODE_NEW_WITH_DRAG: break; case MODE_PASTE: break; case MODE_PASTE_SPECIAL: break; case MODE_SELECT: break; case MODE_SET_COORDINATE_ONE: if (oneDimAnn != NULL) { coordinate = oneDimAnn->getStartCoordinate(); otherCoordinate = oneDimAnn->getEndCoordinate(); } else if (twoDimAnn != NULL) { coordinate = twoDimAnn->getCoordinate(); } break; case MODE_SET_COORDINATE_TWO: if (oneDimAnn != NULL) { coordinate = oneDimAnn->getEndCoordinate(); otherCoordinate = oneDimAnn->getStartCoordinate(); } break; } if (coordinate != NULL) { StructureEnum::Enum structure = StructureEnum::INVALID; int32_t numNodes = -1; int32_t nodeIndex = -1; float surfaceOffset = 0.0; AnnotationSurfaceOffsetVectorTypeEnum::Enum surfaceVector = AnnotationSurfaceOffsetVectorTypeEnum::CENTROID_THRU_VERTEX; coordinate->getSurfaceSpace(structure, numNodes, nodeIndex, surfaceOffset, surfaceVector); coordInfo.m_surfaceNodeOffset = surfaceOffset; coordInfo.m_surfaceNodeVector = surfaceVector; } AnnotationChangeCoordinateDialog changeCoordDialog(coordInfo, selectedAnnotation, coordinate, otherCoordinate, m_annotationToolsWidget); if (changeCoordDialog.exec() == AnnotationChangeCoordinateDialog::Accepted) { } setMode(MODE_SELECT); EventManager::get()->sendEvent(EventGraphicsUpdateAllWindows().getPointer()); EventManager::get()->sendSimpleEvent(EventTypeEnum::EVENT_ANNOTATION_TOOLBAR_UPDATE); } /** * Process a mouse left click for new mode. * * @param mouseEvent * Mouse event information. */ void UserInputModeAnnotations::processModeNewMouseLeftClick(const MouseEvent& mouseEvent) { resetAnnotationUnderMouse(); Annotation* ann = AnnotationCreateDialog::newAnnotationFromSpaceAndType(mouseEvent, m_modeNewAnnotationFileSpaceAndType->m_annotationSpace, m_modeNewAnnotationFileSpaceAndType->m_annotationType, m_modeNewAnnotationFileSpaceAndType->m_annotationFile); if (ann != NULL) { selectAnnotation(ann); } setMode(MODE_SELECT); EventManager::get()->sendEvent(EventGraphicsUpdateAllWindows().getPointer()); EventManager::get()->sendSimpleEvent(EventTypeEnum::EVENT_ANNOTATION_TOOLBAR_UPDATE); } /** * @return The single selected annotation. If there is ONE and ONLY one annotation * selected, it is returned. Otherwise, NULL is returned. */ Annotation* UserInputModeAnnotations::getSingleSelectedAnnotation() { AnnotationManager* annotationManager = GuiManager::get()->getBrain()->getAnnotationManager(); std::vector allSelectedAnnotations = annotationManager->getAnnotationsSelectedForEditing(m_browserWindowIndex); Annotation* selectedAnnotation = NULL; if (allSelectedAnnotations.size() == 1) { CaretAssertVectorIndex(allSelectedAnnotations, 0); selectedAnnotation = allSelectedAnnotations[0]; } return selectedAnnotation; } /** * Select the given annotation (typically when a new annontation is created). * * @param annotation * Annotation that is selected. */ void UserInputModeAnnotations::selectAnnotation(Annotation* annotation) { resetAnnotationUnderMouse(); m_annotationBeingDragged = annotation; m_annotationUnderMouse = annotation; } /** * Process a mouse left click for selection mode. * * @param mouseEvent * Mouse event information. * @param shiftKeyDownFlag * True if shift key is down. */ void UserInputModeAnnotations::processMouseSelectAnnotation(const MouseEvent& mouseEvent, const bool shiftKeyDownFlag) { BrainOpenGLWidget* openGLWidget = mouseEvent.getOpenGLWidget(); const int mouseX = mouseEvent.getX(); const int mouseY = mouseEvent.getY(); m_annotationBeingDragged = NULL; /* * NOTE: When selecting annotations: * (A) When the mouse is clicked WITHOUT the SHIFT key down, the user is in * 'single-annotation-selection-mode' and at most one annotation will * be selected when this method completes. * (B) If the mouse is clicked with the SHIFT key down, the user is in * 'multi-annotation-selection-mode' and any number of annotation will * be selected when this method completes. */ SelectionItemAnnotation* annotationID = openGLWidget->performIdentificationAnnotations(mouseX, mouseY); Annotation* selectedAnnotation = annotationID->getAnnotation(); AnnotationManager* annotationManager = GuiManager::get()->getBrain()->getAnnotationManager(); AnnotationManager::SelectionMode selectionMode = AnnotationManager::SELECTION_MODE_SINGLE; if (m_allowMultipleSelectionModeFlag) { selectionMode = AnnotationManager::SELECTION_MODE_EXTENDED; } annotationManager->selectAnnotationForEditing(m_browserWindowIndex, selectionMode, shiftKeyDownFlag, selectedAnnotation); setAnnotationUnderMouse(mouseEvent, annotationID); if (selectedAnnotation != NULL) { m_annotationBeingDragged = selectedAnnotation; m_annotationBeingDraggedHandleType = annotationID->getSizingHandle(); } EventManager::get()->sendEvent(EventGraphicsUpdateAllWindows().getPointer()); EventManager::get()->sendSimpleEvent(EventTypeEnum::EVENT_ANNOTATION_TOOLBAR_UPDATE); } /** * Show a context menu (pop-up menu at mouse location) * * @param mouseEvent * Mouse event information. * @param menuPosition * Point at which menu is displayed (passed to QMenu::exec()) * @param openGLWidget * OpenGL widget in which context menu is requested */ void UserInputModeAnnotations::showContextMenu(const MouseEvent& mouseEvent, const QPoint& menuPosition, BrainOpenGLWidget* openGLWidget) { BrainOpenGLViewportContent* viewportContent = mouseEvent.getViewportContent(); BrowserTabContent* tabContent = viewportContent->getBrowserTabContent(); if (tabContent == NULL) { return; } /* * Select any annotation that is under the mouse. * There might not be an annotation under the * mouse and that is okay. */ processMouseSelectAnnotation(mouseEvent, false); UserInputModeAnnotationsContextMenu contextMenu(this, mouseEvent, NULL, //idManager, tabContent, openGLWidget); if (contextMenu.actions().size() > 0) { contextMenu.exec(menuPosition); Annotation* newAnnotation = contextMenu.getNewAnnotationCreatedByContextMenu(); if (newAnnotation != NULL) { selectAnnotation(newAnnotation); EventManager::get()->sendEvent(EventGraphicsUpdateAllWindows().getPointer()); EventManager::get()->sendSimpleEvent(EventTypeEnum::EVENT_ANNOTATION_TOOLBAR_UPDATE); } } } /** * @return True if the edit menu items should be enabled, else false. */ bool UserInputModeAnnotations::isEditMenuValid() const { bool editMenuValid = false; switch (m_mode) { case MODE_NEW_WITH_CLICK: break; case MODE_NEW_WITH_DRAG: break; case MODE_PASTE: editMenuValid = true; break; case MODE_PASTE_SPECIAL: editMenuValid = true; break; case MODE_SELECT: editMenuValid = true; break; case MODE_SET_COORDINATE_ONE: break; case MODE_SET_COORDINATE_TWO: break; } return editMenuValid; } /** * Cut the selected annotation. Only functions if * one and only one annotation is selected. */ void UserInputModeAnnotations::cutAnnotation() { std::vector > selectedAnnotations; AnnotationManager* annotationManager = GuiManager::get()->getBrain()->getAnnotationManager(); annotationManager->getAnnotationsSelectedForEditing(m_browserWindowIndex, selectedAnnotations); if (selectedAnnotations.size() == 1) { CaretAssertVectorIndex(selectedAnnotations, 0); AnnotationFile* annotationFile = selectedAnnotations[0].second; Annotation* annotation = selectedAnnotations[0].first; annotationManager->copyAnnotationToClipboard(annotationFile, annotation); std::vector annotationVector; annotationVector.push_back(annotation); AnnotationRedoUndoCommand* undoCommand = new AnnotationRedoUndoCommand(); undoCommand->setModeCutAnnotations(annotationVector); AString errorMessage; if ( ! annotationManager->applyCommand(undoCommand, errorMessage)) { WuQMessageBox::errorOk(m_annotationToolsWidget, errorMessage); } EventManager::get()->sendSimpleEvent(EventTypeEnum::EVENT_ANNOTATION_TOOLBAR_UPDATE); EventManager::get()->sendEvent(EventGraphicsUpdateAllWindows().getPointer()); } } /** * Process a selection that was made from the browser window's edit menu. * Intended for override by sub-classes. * * @param editMenuItem * Item that was selected from the edit menu. */ void UserInputModeAnnotations::processEditMenuItemSelection(const BrainBrowserWindowEditMenuItemEnum::Enum editMenuItem) { if ( ! isEditMenuValid()) { return; } switch (editMenuItem) { case BrainBrowserWindowEditMenuItemEnum::COPY: { AnnotationManager* annotationManager = GuiManager::get()->getBrain()->getAnnotationManager(); std::vector > selectedAnnotations; annotationManager->getAnnotationsSelectedForEditing(m_browserWindowIndex, selectedAnnotations); if (selectedAnnotations.size() == 1) { CaretAssertVectorIndex(selectedAnnotations, 0); annotationManager->copyAnnotationToClipboard(selectedAnnotations[0].second, selectedAnnotations[0].first); } } break; case BrainBrowserWindowEditMenuItemEnum::CUT: cutAnnotation(); break; case BrainBrowserWindowEditMenuItemEnum::DELETER: deleteSelectedAnnotations(); break; case BrainBrowserWindowEditMenuItemEnum::PASTE: { const MouseEvent* mouseEvent = getMousePosition(); if (mouseEvent != NULL) { pasteAnnotationFromAnnotationClipboard(*mouseEvent); } else { setMode(MODE_PASTE); } } break; case BrainBrowserWindowEditMenuItemEnum::PASTE_SPECIAL: { const MouseEvent* mouseEvent = getMousePosition(); if (mouseEvent != NULL) { pasteAnnotationFromAnnotationClipboardAndChangeSpace(*mouseEvent); } else { setMode(MODE_PASTE_SPECIAL); } } break; case BrainBrowserWindowEditMenuItemEnum::REDO: { AnnotationManager* annMan = GuiManager::get()->getBrain()->getAnnotationManager(); CaretUndoStack* undoStack = annMan->getCommandRedoUndoStack(); AString errorMessage; if ( ! undoStack->redo(errorMessage)) { WuQMessageBox::errorOk(m_annotationToolsWidget, errorMessage); } EventManager::get()->sendSimpleEvent(EventTypeEnum::EVENT_ANNOTATION_TOOLBAR_UPDATE); EventManager::get()->sendEvent(EventGraphicsUpdateAllWindows().getPointer()); } break; case BrainBrowserWindowEditMenuItemEnum::SELECT_ALL: processSelectAllAnnotations(); break; case BrainBrowserWindowEditMenuItemEnum::UNDO: { AnnotationManager* annMan = GuiManager::get()->getBrain()->getAnnotationManager(); CaretUndoStack* undoStack = annMan->getCommandRedoUndoStack(); AString errorMessage; if ( ! undoStack->undo(errorMessage)) { WuQMessageBox::errorOk(m_annotationToolsWidget, errorMessage); } EventManager::get()->sendSimpleEvent(EventTypeEnum::EVENT_ANNOTATION_TOOLBAR_UPDATE); EventManager::get()->sendEvent(EventGraphicsUpdateAllWindows().getPointer()); } break; } } /** * Process the selection of all annotations. */ void UserInputModeAnnotations::processSelectAllAnnotations() { AnnotationManager* annMan = GuiManager::get()->getBrain()->getAnnotationManager(); annMan->deselectAllAnnotationsForEditing(m_browserWindowIndex); EventAnnotationGetDrawnInWindow getDrawnEvent(m_browserWindowIndex); EventManager::get()->sendEvent(getDrawnEvent.getPointer()); std::vector annotationsSelected; getDrawnEvent.getAnnotations(annotationsSelected); annMan->setAnnotationsForEditing(m_browserWindowIndex, annotationsSelected); EventManager::get()->sendSimpleEvent(EventTypeEnum::EVENT_ANNOTATION_TOOLBAR_UPDATE); EventManager::get()->sendEvent(EventGraphicsUpdateAllWindows().getPointer()); } /** * Get the menu items that should be enabled for the current user input processor. * Intended for override by sub-classes. * Unless this method is overridden, all items on Edit menu are disabled. * * @param enabledEditMenuItemsOut * Upon exit contains edit menu items that should be enabled. * @param redoMenuItemSuffixTextOut * If the redo menu is enabled, the contents of string becomes * the suffix for the 'Redo' menu item. * @param undoMenuItemSuffixTextOut * If the undo menu is enabled, the contents of string becomes * the suffix for the 'Undo' menu item. * @param pasteTextOut * If not empty, this text is shown for the PASTE menu item * @param pasteSpecialTextOut * If not empty, this text is shown for the PASTE_SPECIAL menu item */ void UserInputModeAnnotations::getEnabledEditMenuItems(std::vector& enabledEditMenuItemsOut, AString& redoMenuItemSuffixTextOut, AString& undoMenuItemSuffixTextOut, AString& pasteTextOut, AString& pasteSpecialTextOut) { enabledEditMenuItemsOut.clear(); redoMenuItemSuffixTextOut = ""; undoMenuItemSuffixTextOut = ""; pasteTextOut = BrainBrowserWindowEditMenuItemEnum::toGuiName(BrainBrowserWindowEditMenuItemEnum::PASTE); pasteSpecialTextOut = BrainBrowserWindowEditMenuItemEnum::toGuiName(BrainBrowserWindowEditMenuItemEnum::PASTE_SPECIAL); if (isEditMenuValid()) { AnnotationManager* annotationManager = GuiManager::get()->getBrain()->getAnnotationManager(); const std::vector allSelectedAnnotations = annotationManager->getAnnotationsSelectedForEditing(m_browserWindowIndex); /* * Copy, Cut, and Delete disabled if ANY colorbar is selected. */ bool noColorBarsSelectedFlag = true; for (std::vector::const_iterator iter = allSelectedAnnotations.begin(); iter != allSelectedAnnotations.end(); iter++) { const Annotation* ann = *iter; if (ann->getType() == AnnotationTypeEnum::COLOR_BAR) { noColorBarsSelectedFlag = false; } } if (noColorBarsSelectedFlag) { const bool anySelectedFlag = ( ! allSelectedAnnotations.empty()); const bool oneSelectedFlag = (allSelectedAnnotations.size() == 1); if (oneSelectedFlag) { enabledEditMenuItemsOut.push_back(BrainBrowserWindowEditMenuItemEnum::COPY); enabledEditMenuItemsOut.push_back(BrainBrowserWindowEditMenuItemEnum::CUT); } if (anySelectedFlag) { enabledEditMenuItemsOut.push_back(BrainBrowserWindowEditMenuItemEnum::DELETER); } enabledEditMenuItemsOut.push_back(BrainBrowserWindowEditMenuItemEnum::SELECT_ALL); } if (annotationManager->isAnnotationOnClipboardValid()) { const Annotation* clipBoardAnn = annotationManager->getAnnotationOnClipboard(); CaretAssert(clipBoardAnn); enabledEditMenuItemsOut.push_back(BrainBrowserWindowEditMenuItemEnum::PASTE); enabledEditMenuItemsOut.push_back(BrainBrowserWindowEditMenuItemEnum::PASTE_SPECIAL); clipBoardAnn->getTextForPasteMenuItems(pasteTextOut, pasteSpecialTextOut); } CaretUndoStack* undoStack = annotationManager->getCommandRedoUndoStack(); if (undoStack->canRedo()) { enabledEditMenuItemsOut.push_back(BrainBrowserWindowEditMenuItemEnum::REDO); redoMenuItemSuffixTextOut = undoStack->redoText(); } if (undoStack->canUndo()) { enabledEditMenuItemsOut.push_back(BrainBrowserWindowEditMenuItemEnum::UNDO); undoMenuItemSuffixTextOut = undoStack->undoText(); } } } /** * Paste the annotation from the annotation clipboard. * * @param mouseEvent * Mouse event containing XY location for placement of * pasted annotation. */ void UserInputModeAnnotations::pasteAnnotationFromAnnotationClipboard(const MouseEvent& mouseEvent) { Annotation* newPastedAnnotation = AnnotationPasteDialog::pasteAnnotationOnClipboard(mouseEvent, m_browserWindowIndex); if (newPastedAnnotation != NULL) { selectAnnotation(newPastedAnnotation); DisplayPropertiesAnnotation* dpa = GuiManager::get()->getBrain()->getDisplayPropertiesAnnotation(); dpa->updateForNewAnnotation(newPastedAnnotation); } setMode(MODE_SELECT); EventManager::get()->sendEvent(EventGraphicsUpdateAllWindows().getPointer()); EventManager::get()->sendEvent(EventUserInterfaceUpdate().getPointer()); } /** * Paste the annotation from the annotation clipboard and allow user to change its space. * * @param mouseEvent * Mouse event containing XY location for placement of * pasted annotation. */ void UserInputModeAnnotations::pasteAnnotationFromAnnotationClipboardAndChangeSpace(const MouseEvent& mouseEvent) { Annotation* newPastedAnnotation = AnnotationPasteDialog::pasteAnnotationOnClipboardChangeSpace(mouseEvent); if (newPastedAnnotation != NULL) { selectAnnotation(newPastedAnnotation); DisplayPropertiesAnnotation* dpa = GuiManager::get()->getBrain()->getDisplayPropertiesAnnotation(); dpa->updateForNewAnnotation(newPastedAnnotation); } setMode(MODE_SELECT); EventManager::get()->sendEvent(EventGraphicsUpdateAllWindows().getPointer()); EventManager::get()->sendEvent(EventUserInterfaceUpdate().getPointer()); } /* =============================================================================== */ /** * Constructor. * * @param annotationFile * File for annotation. * @param annotationSpace * Space for annotation being created. * @param annotationType * Type of annotation being created. * @param mouseEvent * Mouse event. */ UserInputModeAnnotations::NewMouseDragCreateAnnotation::NewMouseDragCreateAnnotation(AnnotationFile* annotationFile, const AnnotationCoordinateSpaceEnum::Enum annotationSpace, const AnnotationTypeEnum::Enum annotationType, const MouseEvent& mouseEvent) { BrainOpenGLViewportContent* vpContent = mouseEvent.getViewportContent(); CaretAssert(vpContent); if (vpContent == NULL) { CaretLogSevere("Viewport content is invalid."); return; } int32_t windowViewport[4]; vpContent->getWindowViewport(windowViewport); m_windowOriginX = windowViewport[0]; m_windowOriginY = windowViewport[1]; m_mousePressWindowX = mouseEvent.getX() - m_windowOriginX; m_mousePressWindowY = mouseEvent.getY() - m_windowOriginY; int viewport[4]; vpContent->getWindowViewport(viewport); m_windowWidth = viewport[2]; m_windowHeight = viewport[3]; m_annotationFile = annotationFile; m_annotation = Annotation::newAnnotationOfType(annotationType, AnnotationAttributesDefaultTypeEnum::USER); m_annotation->setCoordinateSpace(annotationSpace); CaretAssert(m_annotation); AnnotationOneDimensionalShape* oneDimShape = dynamic_cast(m_annotation); AnnotationTwoDimensionalShape* twoDimShape = dynamic_cast(m_annotation); if (oneDimShape != NULL) { setCoordinate(oneDimShape->getStartCoordinate(), m_mousePressWindowX, m_mousePressWindowY); setCoordinate(oneDimShape->getEndCoordinate(), m_mousePressWindowX, m_mousePressWindowY); } else if (twoDimShape != NULL) { setCoordinate(twoDimShape->getCoordinate(), m_mousePressWindowX, m_mousePressWindowY); twoDimShape->setWidth(1.0); twoDimShape->setHeight(1.0); } else { CaretAssert(0); } if ((m_annotation->getLineColor() == CaretColorEnum::NONE) && (m_annotation->getBackgroundColor() == CaretColorEnum::NONE)) { m_annotation->setLineColor(CaretColorEnum::RED); } } /** * Destructor. */ UserInputModeAnnotations::NewMouseDragCreateAnnotation::~NewMouseDragCreateAnnotation() { delete m_annotation; } /** * Update with current mouse location. * * @param mouseWindowX * Mouse window X-coordinate * @param mouseWindowY * Mouse window Y-coordinate */ void UserInputModeAnnotations::NewMouseDragCreateAnnotation::update(const int32_t mouseWindowXIn, const int32_t mouseWindowYIn) { int32_t mouseWindowX = mouseWindowXIn - m_windowOriginX; int32_t mouseWindowY = mouseWindowYIn - m_windowOriginY; AnnotationOneDimensionalShape* oneDimShape = dynamic_cast(m_annotation); AnnotationTwoDimensionalShape* twoDimShape = dynamic_cast(m_annotation); if (oneDimShape != NULL) { setCoordinate(oneDimShape->getEndCoordinate(), mouseWindowX, mouseWindowY); } else if (twoDimShape != NULL) { const float minX = std::min(m_mousePressWindowX, mouseWindowX); const float maxX = std::max(m_mousePressWindowX, mouseWindowX); const float minY = std::min(m_mousePressWindowY, mouseWindowY); const float maxY = std::max(m_mousePressWindowY, mouseWindowY); const float x = (minX + maxX) / 2.0; const float y = (minY + maxY) / 2.0; const float width = maxX - minX; const float height = maxY - minY; const float relativeWidth = 100.0 * ((m_windowWidth > 0.0) ? (width / static_cast(m_windowWidth)) : 0.01); const float relativeHeight = 100.0 * ((m_windowHeight > 0.0) ? (height / static_cast(m_windowHeight)) : 0.01); AnnotationCoordinate* coord = twoDimShape->getCoordinate(); setCoordinate(coord, x, y); twoDimShape->setWidth(relativeWidth); twoDimShape->setHeight(relativeHeight); } else { CaretAssert(0); } } /** * Set the given coordinate to the XY-location. * * @param coordinate * Coordinate that is set. * @param x * Mouse window X-coordinate * @param y * Mouse window Y-coordinate */ void UserInputModeAnnotations::NewMouseDragCreateAnnotation::setCoordinate(AnnotationCoordinate* coordinate, const int32_t x, const int32_t y) { const float relativeX = 100.0 * ((m_windowWidth > 0.0) ? (x / m_windowWidth) : 0.01); const float relativeY = 100.0 * ((m_windowHeight > 0.0) ? (y / m_windowHeight) : 0.01); coordinate->setXYZ(relativeX, relativeY, 0.0); } /** * @return New annotation being drawn by the user. */ const Annotation* UserInputModeAnnotations::NewMouseDragCreateAnnotation::getAnnotation() const { return m_annotation; } connectome-workbench-1.2.3+git41-gc4c6c90/src/GuiQt/UserInputModeAnnotations.h000066400000000000000000000225421300200146000267340ustar00rootroot00000000000000#ifndef __USER_INPUT_MODE_ANNOTATIONS_H__ #define __USER_INPUT_MODE_ANNOTATIONS_H__ /*LICENSE_START*/ /* * Copyright (C) 2015 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 "AnnotationCoordinateSpaceEnum.h" #include "AnnotationSizingHandleTypeEnum.h" #include "AnnotationSurfaceOffsetVectorTypeEnum.h" #include "AnnotationTypeEnum.h" #include "CaretPointer.h" #include "EventListenerInterface.h" #include "StructureEnum.h" #include "UserInputModeView.h" namespace caret { class Annotation; class AnnotationCoordinate; class AnnotationCoordinateInformation; class AnnotationFile; class AnnotationOneDimensionalShape; class AnnotationTwoDimensionalShape; class KeyEvent; class MouseEvent; class SelectionItemAnnotation; class UserInputModeAnnotationsWidget; class UserInputModeAnnotations : public UserInputModeView, EventListenerInterface { public: /** * Annotation mode */ enum Mode { /** Mouse creates an annotation by clicking */ MODE_NEW_WITH_CLICK, /** Mouse creates an annotation by draggin from one point to another */ MODE_NEW_WITH_DRAG, /** User selected Paste from Edit Menu, user may need to click mouse to paste the annotation */ MODE_PASTE, /** User selected Paste Special from Edit Menu, user may need to click mouse to paste the annotation */ MODE_PASTE_SPECIAL, /** Mouse selects annotation */ MODE_SELECT, /** Set coordinate one in annotation*/ MODE_SET_COORDINATE_ONE, /** Set coordinate two in annotation*/ MODE_SET_COORDINATE_TWO }; UserInputModeAnnotations(const int32_t windowIndex); virtual ~UserInputModeAnnotations(); virtual void receiveEvent(Event* event); virtual void initialize(); virtual void finish(); virtual void update(); Mode getMode() const; virtual void keyPressEvent(const KeyEvent& /*keyEvent*/); virtual void mouseLeftDoubleClick(const MouseEvent& mouseEvent); virtual void mouseLeftClick(const MouseEvent& mouseEvent); virtual void mouseLeftClickWithShift(const MouseEvent& mouseEvent); virtual void mouseLeftDrag(const MouseEvent& mouseEvent); virtual void mouseLeftDragWithAlt(const MouseEvent& mouseEvent); virtual void mouseLeftDragWithCtrl(const MouseEvent& mouseEvent); virtual void mouseLeftDragWithCtrlShift(const MouseEvent& mouseEvent); virtual void mouseLeftDragWithShift(const MouseEvent& mouseEvent); virtual void mouseLeftPress(const MouseEvent& mouseEvent); virtual void mouseLeftRelease(const MouseEvent& mouseEvent); virtual void mouseMove(const MouseEvent& mouseEvent); virtual void mouseMoveWithShift(const MouseEvent& mouseEvent); virtual void showContextMenu(const MouseEvent& mouseEvent, const QPoint& menuPosition, BrainOpenGLWidget* openGLWidget); virtual CursorEnum::Enum getCursor() const; virtual AString toString() const; virtual void processEditMenuItemSelection(const BrainBrowserWindowEditMenuItemEnum::Enum editMenuItem); virtual void getEnabledEditMenuItems(std::vector& enabledEditMenuItemsOut, AString& redoMenuItemSuffixTextOut, AString& undoMenuItemSuffixTextOut, AString& pasteTextOut, AString& pasteSpecialTextOut); // ADD_NEW_METHODS_HERE private: class NewMouseDragCreateAnnotation { public: NewMouseDragCreateAnnotation(AnnotationFile* annotationFile, const AnnotationCoordinateSpaceEnum::Enum annotationSpace, const AnnotationTypeEnum::Enum annotationType, const MouseEvent& mousePressEvent); ~NewMouseDragCreateAnnotation(); void update(const int32_t mouseWindowX, const int32_t mouseWindowY); void setCoordinate(AnnotationCoordinate* coordinate, const int32_t x, const int32_t y); const Annotation* getAnnotation() const; private: AnnotationFile* m_annotationFile; Annotation* m_annotation; int32_t m_mousePressWindowX; int32_t m_mousePressWindowY; int32_t m_windowOriginX; int32_t m_windowOriginY; float m_windowWidth; float m_windowHeight; }; class NewAnnotationFileSpaceAndType { public: NewAnnotationFileSpaceAndType(AnnotationFile* annotationFile, AnnotationCoordinateSpaceEnum::Enum annotationSpace, AnnotationTypeEnum::Enum annotationType) : m_annotationFile(annotationFile), m_annotationSpace(annotationSpace), m_annotationType(annotationType) { } AnnotationFile* m_annotationFile; AnnotationCoordinateSpaceEnum::Enum m_annotationSpace; AnnotationTypeEnum::Enum m_annotationType; }; UserInputModeAnnotations(const UserInputModeAnnotations&); UserInputModeAnnotations& operator=(const UserInputModeAnnotations&); void setMode(const Mode mode); void processModeNewMouseLeftClick(const MouseEvent& mouseEvent); void processMouseSelectAnnotation(const MouseEvent& mouseEvent, const bool shiftKeyDownFlag); void processModeSetCoordinate(const MouseEvent& mouseEvent); void setAnnotationUnderMouse(const MouseEvent& mouseEvent, SelectionItemAnnotation* annotationIDIn); void createNewAnnotationFromMouseDrag(const MouseEvent& mouseEvent); void userDrawingAnnotationFromMouseDrag(const MouseEvent& mouseEvent); void selectAnnotation(Annotation* annotation); Annotation* getSingleSelectedAnnotation(); void cutAnnotation(); void deleteSelectedAnnotations(); void processSelectAllAnnotations(); void resetAnnotationUnderMouse(); bool isEditMenuValid() const; void pasteAnnotationFromAnnotationClipboard(const MouseEvent& mouseEvent); void pasteAnnotationFromAnnotationClipboardAndChangeSpace(const MouseEvent& mouseEvent); // bool pasteOneDimensionalShape(AnnotationOneDimensionalShape* oneDimShape, // AnnotationCoordinateInformation& coordInfo); UserInputModeAnnotationsWidget* m_annotationToolsWidget; Mode m_mode; const int32_t m_browserWindowIndex; Annotation* m_annotationUnderMouse; AnnotationSizingHandleTypeEnum::Enum m_annotationUnderMouseSizeHandleType; Annotation* m_annotationBeingDragged; AnnotationSizingHandleTypeEnum::Enum m_annotationBeingDraggedHandleType; CaretPointer m_modeNewAnnotationFileSpaceAndType; CaretPointer m_newAnnotationCreatingWithMouseDrag; bool m_allowMultipleSelectionModeFlag; //static const AString s_pasteSpecialMenuItemText; // ADD_NEW_MEMBERS_HERE /* * Some private methods are accessed by this friend class */ friend class UserInputModeAnnotationsContextMenu; friend class UserInputModeAnnotationsWidget; }; #ifdef __USER_INPUT_MODE_ANNOTATIONS_DECLARE__ //const AString UserInputModeAnnotations::s_pasteSpecialMenuItemText = "Paste and Change Space"; #endif // __USER_INPUT_MODE_ANNOTATIONS_DECLARE__ } // namespace #endif //__USER_INPUT_MODE_ANNOTATIONS_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/GuiQt/UserInputModeAnnotationsContextMenu.cxx000066400000000000000000000431441300200146000315020ustar00rootroot00000000000000 /*LICENSE_START*/ /* * Copyright (C) 2015 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_MODE_ANNOTATIONS_CONTEXT_MENU_DECLARE__ #include "UserInputModeAnnotationsContextMenu.h" #undef __USER_INPUT_MODE_ANNOTATIONS_CONTEXT_MENU_DECLARE__ #include #include #include "AnnotationCoordinate.h" #include "AnnotationCreateDialog.h" #include "AnnotationFile.h" #include "AnnotationManager.h" #include "AnnotationOneDimensionalShape.h" #include "AnnotationRedoUndoCommand.h" #include "AnnotationText.h" #include "AnnotationTextEditorDialog.h" #include "Brain.h" #include "BrainOpenGLWidget.h" #include "BrowserTabContent.h" #include "CaretAssert.h" #include "EventGraphicsUpdateAllWindows.h" #include "EventManager.h" #include "EventUserInterfaceUpdate.h" #include "GuiManager.h" #include "MathFunctions.h" #include "SelectionItemAnnotation.h" #include "SelectionManager.h" #include "UserInputModeAnnotations.h" #include "WuQDataEntryDialog.h" #include "WuQMessageBox.h" using namespace caret; /** * \class caret::UserInputModeAnnotationsContextMenu * \brief Context (pop-up) menu for User Input Annotations Mode. * \ingroup GuiQt */ /** * Constructor. * * @param mouseEvent * The mouse event that caused display of this menu. * @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. */ UserInputModeAnnotationsContextMenu::UserInputModeAnnotationsContextMenu(UserInputModeAnnotations* userInputModeAnnotations, const MouseEvent& mouseEvent, SelectionManager* selectionManager, BrowserTabContent* browserTabContent, BrainOpenGLWidget* parentOpenGLWidget) : QMenu(parentOpenGLWidget), m_userInputModeAnnotations(userInputModeAnnotations), m_mouseEvent(mouseEvent), m_selectionManager(selectionManager), m_browserTabContent(browserTabContent), m_parentOpenGLWidget(parentOpenGLWidget), m_newAnnotationCreatedByContextMenu(NULL) { CaretAssert(m_userInputModeAnnotations); const int32_t browserWindexIndex = m_mouseEvent.getBrowserWindowIndex(); std::vector > selectedAnnotations; AnnotationManager* annotationManager = GuiManager::get()->getBrain()->getAnnotationManager(); annotationManager->getAnnotationsSelectedForEditing(browserWindexIndex, selectedAnnotations); m_annotationFile = NULL; m_annotation = NULL; m_stereotaxicAndSurfaceAnnotations.clear(); for (std::vector >::iterator iter = selectedAnnotations.begin(); iter != selectedAnnotations.end(); iter++) { Annotation* ann = iter->first; CaretAssert(ann); bool stereoOrSurfaceSpaceFlag = false; switch (ann->getCoordinateSpace()) { case AnnotationCoordinateSpaceEnum::PIXELS: break; case AnnotationCoordinateSpaceEnum::STEREOTAXIC: stereoOrSurfaceSpaceFlag = true; break; case AnnotationCoordinateSpaceEnum::SURFACE: stereoOrSurfaceSpaceFlag = true; break; case AnnotationCoordinateSpaceEnum::TAB: break; case AnnotationCoordinateSpaceEnum::WINDOW: break; } if (stereoOrSurfaceSpaceFlag) { m_stereotaxicAndSurfaceAnnotations.push_back(ann); } } const bool haveStereotaxicAnnotationsFlag = ( ! m_stereotaxicAndSurfaceAnnotations.empty()); bool oneAnnotationSelectedFlag = false; if (selectedAnnotations.size() == 1) { CaretAssertVectorIndex(selectedAnnotations, 0); m_annotationFile = selectedAnnotations[0].second; m_annotation = selectedAnnotations[0].first; oneAnnotationSelectedFlag = true; } m_textAnnotation = NULL; if (m_annotation != NULL) { m_textAnnotation = dynamic_cast(m_annotation); } std::vector editMenuItemsEnabled; AString redoMenuItemSuffix; AString undoMenuItemSuffix; AString pasteText; AString pasteSpecialText; userInputModeAnnotations->getEnabledEditMenuItems(editMenuItemsEnabled, redoMenuItemSuffix, undoMenuItemSuffix, pasteText, pasteSpecialText); /* * Cut */ QAction* cutAction = addAction(BrainBrowserWindowEditMenuItemEnum::toGuiName(BrainBrowserWindowEditMenuItemEnum::CUT), this, SLOT(cutAnnnotation())); cutAction->setEnabled(oneAnnotationSelectedFlag); /* * Copy */ QAction* copyAction = addAction(BrainBrowserWindowEditMenuItemEnum::toGuiName(BrainBrowserWindowEditMenuItemEnum::COPY), this, SLOT(copyAnnotationToAnnotationClipboard())); copyAction->setEnabled(oneAnnotationSelectedFlag); /* * Delete */ QAction* deleteAction = addAction(BrainBrowserWindowEditMenuItemEnum::toGuiName(BrainBrowserWindowEditMenuItemEnum::DELETER), this, SLOT(deleteAnnotations())); deleteAction->setEnabled( ! selectedAnnotations.empty()); /* * Paste */ QAction* pasteAction = addAction(pasteText, this, SLOT(pasteAnnotationFromAnnotationClipboard())); pasteAction->setEnabled(annotationManager->isAnnotationOnClipboardValid()); /* * Paste Special */ QAction* pasteSpecialAction = addAction(pasteSpecialText, this, SLOT(pasteSpecialAnnotationFromAnnotationClipboard())); pasteSpecialAction->setEnabled(annotationManager->isAnnotationOnClipboardValid()); /* * Separator */ addSeparator(); /* * Select All annotations */ addAction(BrainBrowserWindowEditMenuItemEnum::toGuiName(BrainBrowserWindowEditMenuItemEnum::SELECT_ALL), this, SLOT(selectAllAnnotations())); /* * Separator */ addSeparator(); /* * Edit Text */ QAction* editTextAction = addAction("Edit Text...", this, SLOT(setAnnotationText())); editTextAction->setEnabled(m_textAnnotation != NULL); /* * Separator */ addSeparator(); /* * Turn on/off display in tabs */ QAction* turnOffTabDisplayAction = addAction("Turn Off Stereotaxic/Surface Annotation Display in Other Tabs", this, SLOT(turnOffDisplayInOtherTabs())); QAction* turnOnTabDisplayAction = addAction("Turn On Stereotaxic/Surface Annotation Display in All Tabs", this, SLOT(turnOnDisplayInAllTabs())); turnOffTabDisplayAction->setEnabled(haveStereotaxicAnnotationsFlag); turnOnTabDisplayAction->setEnabled(haveStereotaxicAnnotationsFlag); /* * Turn on/off display in groups */ QAction* turnOnGroupDisplayAction = addAction("Turn On Stereotaxic/Surface Annotation Display in All Groups", this, SLOT(turnOnDisplayInAllGroups())); turnOnGroupDisplayAction->setEnabled(haveStereotaxicAnnotationsFlag); QMenu* turnOnInDisplayGroupMenu = createTurnOnInDisplayGroupMenu(); turnOnInDisplayGroupMenu->setEnabled(haveStereotaxicAnnotationsFlag); addMenu(turnOnInDisplayGroupMenu); /* * Separator */ addSeparator(); /* * Group annotations */ QAction* groupAction = addAction(AnnotationGroupingModeEnum::toGuiName(AnnotationGroupingModeEnum::GROUP), this, SLOT(applyGroupingGroup())); groupAction->setEnabled(annotationManager->isGroupingModeValid(browserWindexIndex, AnnotationGroupingModeEnum::GROUP)); /* * Ungroup annotations */ QAction* ungroupAction = addAction(AnnotationGroupingModeEnum::toGuiName(AnnotationGroupingModeEnum::UNGROUP), this, SLOT(applyGroupingUngroup())); ungroupAction->setEnabled(annotationManager->isGroupingModeValid(browserWindexIndex, AnnotationGroupingModeEnum::UNGROUP)); /* * Regroup annotations */ QAction* regroupAction = addAction(AnnotationGroupingModeEnum::toGuiName(AnnotationGroupingModeEnum::REGROUP), this, SLOT(applyGroupingRegroup())); regroupAction->setEnabled(annotationManager->isGroupingModeValid(browserWindexIndex, AnnotationGroupingModeEnum::REGROUP)); } /** * Destructor. */ UserInputModeAnnotationsContextMenu::~UserInputModeAnnotationsContextMenu() { } Annotation* UserInputModeAnnotationsContextMenu::getNewAnnotationCreatedByContextMenu() { return m_newAnnotationCreatedByContextMenu; } /** * Copy the annotation to the annotation clipboard. */ void UserInputModeAnnotationsContextMenu::copyAnnotationToAnnotationClipboard() { CaretAssert(m_annotationFile); CaretAssert(m_annotation); AnnotationManager* annotationManager = GuiManager::get()->getBrain()->getAnnotationManager(); annotationManager->copyAnnotationToClipboard(m_annotationFile, m_annotation); } /** * Cut the selected annotation. */ void UserInputModeAnnotationsContextMenu::cutAnnnotation() { m_userInputModeAnnotations->cutAnnotation(); } /** * Delete the annotation. */ void UserInputModeAnnotationsContextMenu::deleteAnnotations() { /* * Delete the annotation that is under the mouse */ AnnotationManager* annotationManager = GuiManager::get()->getBrain()->getAnnotationManager(); std::vector selectedAnnotations = annotationManager->getAnnotationsSelectedForEditing(m_mouseEvent.getBrowserWindowIndex()); if ( ! selectedAnnotations.empty()) { AnnotationRedoUndoCommand* undoCommand = new AnnotationRedoUndoCommand(); undoCommand->setModeDeleteAnnotations(selectedAnnotations); AString errorMessage; if ( ! annotationManager->applyCommand(undoCommand, errorMessage)) { WuQMessageBox::errorOk(this, errorMessage); } EventManager::get()->sendSimpleEvent(EventTypeEnum::EVENT_ANNOTATION_TOOLBAR_UPDATE); EventManager::get()->sendEvent(EventGraphicsUpdateAllWindows().getPointer()); } } /** * Paste the annotation from the annotation clipboard. */ void UserInputModeAnnotationsContextMenu::pasteAnnotationFromAnnotationClipboard() { m_userInputModeAnnotations->pasteAnnotationFromAnnotationClipboard(m_mouseEvent); } /** * Paste special the annotation from the annotation clipboard. */ void UserInputModeAnnotationsContextMenu::pasteSpecialAnnotationFromAnnotationClipboard() { m_userInputModeAnnotations->pasteAnnotationFromAnnotationClipboardAndChangeSpace(m_mouseEvent); } /** * Select all annotations in the window. */ void UserInputModeAnnotationsContextMenu::selectAllAnnotations() { m_userInputModeAnnotations->processSelectAllAnnotations(); } /** * Set the text for an annotation. */ void UserInputModeAnnotationsContextMenu::setAnnotationText() { CaretAssert(m_textAnnotation); AnnotationTextEditorDialog ted(m_textAnnotation, this); /* * Note: Y==0 is at top for widget. * Y==0 is at bottom for OpenGL mouse x,y */ QPoint diaglogPos(this->pos().x(), this->pos().y() + 20); ted.move(diaglogPos); ted.exec(); EventManager::get()->sendEvent(EventUserInterfaceUpdate().getPointer()); } /** * Turn off display of annotation in other tabs. */ void UserInputModeAnnotationsContextMenu::turnOffDisplayInOtherTabs() { for (std::vector::iterator iter = m_stereotaxicAndSurfaceAnnotations.begin(); iter != m_stereotaxicAndSurfaceAnnotations.end(); iter++) { (*iter)->setItemDisplaySelectedInOneTab(m_browserTabContent->getTabNumber()); } EventManager::get()->sendEvent(EventUserInterfaceUpdate().getPointer()); EventManager::get()->sendEvent(EventGraphicsUpdateAllWindows().getPointer()); } /** * Turn on display of annotation in all tabs. */ void UserInputModeAnnotationsContextMenu::turnOnDisplayInAllTabs() { for (std::vector::iterator iter = m_stereotaxicAndSurfaceAnnotations.begin(); iter != m_stereotaxicAndSurfaceAnnotations.end(); iter++) { (*iter)->setItemDisplaySelectedInAllTabs(); } EventManager::get()->sendEvent(EventUserInterfaceUpdate().getPointer()); EventManager::get()->sendEvent(EventGraphicsUpdateAllWindows().getPointer()); } /** * Turn on display of annotation in all groups. */ void UserInputModeAnnotationsContextMenu::turnOnDisplayInAllGroups() { for (std::vector::iterator iter = m_stereotaxicAndSurfaceAnnotations.begin(); iter != m_stereotaxicAndSurfaceAnnotations.end(); iter++) { (*iter)->setItemDisplaySelectedInAllGroups(); } EventManager::get()->sendEvent(EventUserInterfaceUpdate().getPointer()); EventManager::get()->sendEvent(EventGraphicsUpdateAllWindows().getPointer()); } /** * Turn on display of annotation in a group and off in other groups * * @param action * Menu action that was selected. */ void UserInputModeAnnotationsContextMenu::turnOnDisplayInGroup(QAction* action) { const int intValue = action->data().toInt(); bool validFlag = false; DisplayGroupEnum::Enum displayGroup = DisplayGroupEnum::fromIntegerCode(intValue, &validFlag); if ( ! validFlag) { CaretAssert(0); return; } if (displayGroup == DisplayGroupEnum::DISPLAY_GROUP_TAB) { CaretAssert(0); // TAB NOT ALLOWED return; } for (std::vector::iterator iter = m_stereotaxicAndSurfaceAnnotations.begin(); iter != m_stereotaxicAndSurfaceAnnotations.end(); iter++) { (*iter)->setItemDisplaySelectedInOneGroup(displayGroup); } EventManager::get()->sendEvent(EventUserInterfaceUpdate().getPointer()); EventManager::get()->sendEvent(EventGraphicsUpdateAllWindows().getPointer()); } QMenu* UserInputModeAnnotationsContextMenu::createTurnOnInDisplayGroupMenu() { QMenu* menu = new QMenu("Turn On Stereotaxic/Surface Annotation Only in Group"); QObject::connect(menu, SIGNAL(triggered(QAction*)), this, SLOT(turnOnDisplayInGroup(QAction*))); std::vector groupEnums; DisplayGroupEnum::getAllEnumsExceptTab(groupEnums); for (std::vector::iterator iter = groupEnums.begin(); iter != groupEnums.end(); iter++) { const DisplayGroupEnum::Enum dg = *iter; QAction* action = menu->addAction(DisplayGroupEnum::toGuiName(dg)); action->setData((int)DisplayGroupEnum::toIntegerCode(dg)); } return menu; } /** * Group annotations. */ void UserInputModeAnnotationsContextMenu::applyGroupingGroup() { applyGrouping(AnnotationGroupingModeEnum::GROUP); } /** * Ungroup annotations. */ void UserInputModeAnnotationsContextMenu::applyGroupingRegroup() { applyGrouping(AnnotationGroupingModeEnum::REGROUP); } /** * Regroup annotations. */ void UserInputModeAnnotationsContextMenu::applyGroupingUngroup() { applyGrouping(AnnotationGroupingModeEnum::UNGROUP); } /** * Apply grouping selection. * * @param grouping * Selected grouping. */ void UserInputModeAnnotationsContextMenu::applyGrouping(const AnnotationGroupingModeEnum::Enum grouping) { AnnotationManager* annMan = GuiManager::get()->getBrain()->getAnnotationManager(); AString errorMessage; if ( ! annMan->applyGroupingMode(m_mouseEvent.getBrowserWindowIndex(), grouping, errorMessage)) { WuQMessageBox::errorOk(this, errorMessage); } EventManager::get()->sendSimpleEvent(EventTypeEnum::EVENT_ANNOTATION_TOOLBAR_UPDATE); EventManager::get()->sendEvent(EventGraphicsUpdateAllWindows().getPointer()); } connectome-workbench-1.2.3+git41-gc4c6c90/src/GuiQt/UserInputModeAnnotationsContextMenu.h000066400000000000000000000076151300200146000311320ustar00rootroot00000000000000#ifndef __USER_INPUT_MODE_ANNOTATIONS_CONTEXT_MENU_H__ #define __USER_INPUT_MODE_ANNOTATIONS_CONTEXT_MENU_H__ /*LICENSE_START*/ /* * Copyright (C) 2015 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 "AnnotationGroupingModeEnum.h" #include "MouseEvent.h" namespace caret { class Annotation; class AnnotationFile; class AnnotationText; class BrainOpenGLWidget; class BrowserTabContent; class SelectionManager; class UserInputModeAnnotations; class UserInputModeAnnotationsContextMenu : public QMenu { Q_OBJECT public: UserInputModeAnnotationsContextMenu(UserInputModeAnnotations* userInputModeAnnotations, const MouseEvent& mouseEvent, SelectionManager* selectionManager, BrowserTabContent* browserTabContent, BrainOpenGLWidget* parentOpenGLWidget); virtual ~UserInputModeAnnotationsContextMenu(); Annotation* getNewAnnotationCreatedByContextMenu(); // ADD_NEW_METHODS_HERE private slots: void copyAnnotationToAnnotationClipboard(); void cutAnnnotation(); void deleteAnnotations(); void pasteAnnotationFromAnnotationClipboard(); void pasteSpecialAnnotationFromAnnotationClipboard(); void selectAllAnnotations(); void setAnnotationText(); void turnOffDisplayInOtherTabs(); void turnOnDisplayInAllTabs(); void turnOnDisplayInAllGroups(); void turnOnDisplayInGroup(QAction*); void applyGroupingGroup(); void applyGroupingRegroup(); void applyGroupingUngroup(); private: UserInputModeAnnotationsContextMenu(const UserInputModeAnnotationsContextMenu&); UserInputModeAnnotationsContextMenu& operator=(const UserInputModeAnnotationsContextMenu&); void applyGrouping(const AnnotationGroupingModeEnum::Enum grouping); QMenu* createTurnOnInDisplayGroupMenu(); UserInputModeAnnotations* m_userInputModeAnnotations; /* * NOT a reference. Need to COPY as its source may be deleted. */ const MouseEvent m_mouseEvent; SelectionManager* m_selectionManager; BrowserTabContent* m_browserTabContent; BrainOpenGLWidget* m_parentOpenGLWidget; AnnotationFile* m_annotationFile; std::vector m_stereotaxicAndSurfaceAnnotations; Annotation* m_annotation; AnnotationText* m_textAnnotation; Annotation* m_newAnnotationCreatedByContextMenu; // ADD_NEW_MEMBERS_HERE }; #ifdef __USER_INPUT_MODE_ANNOTATIONS_CONTEXT_MENU_DECLARE__ // #endif // __USER_INPUT_MODE_ANNOTATIONS_CONTEXT_MENU_DECLARE__ } // namespace #endif //__USER_INPUT_MODE_ANNOTATIONS_CONTEXT_MENU_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/GuiQt/UserInputModeAnnotationsWidget.cxx000066400000000000000000000335611300200146000304560ustar00rootroot00000000000000 /*LICENSE_START*/ /* * Copyright (C) 2015 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_MODE_ANNOTATIONS_WIDGET_DECLARE__ #include "UserInputModeAnnotationsWidget.h" #undef __USER_INPUT_MODE_ANNOTATIONS_WIDGET_DECLARE__ #include #include #include #include #include #include #include "AnnotationCoordinateSpaceWidget.h" #include "AnnotationCoordinateWidget.h" #include "AnnotationColorWidget.h" #include "AnnotationDeleteWidget.h" #include "AnnotationFontWidget.h" #include "AnnotationFormatWidget.h" #include "AnnotationInsertNewWidget.h" #include "AnnotationLine.h" #include "AnnotationLineArrowTipsWidget.h" #include "AnnotationManager.h" #include "AnnotationOneDimensionalShape.h" #include "AnnotationRedoUndoWidget.h" #include "AnnotationRotationWidget.h" #include "AnnotationText.h" #include "AnnotationTextAlignmentWidget.h" #include "AnnotationTextEditorWidget.h" #include "AnnotationTextOrientationWidget.h" #include "AnnotationWidthHeightWidget.h" #include "Brain.h" #include "CaretAssert.h" #include "EventAnnotationCreateNewType.h" #include "EventBrainReset.h" #include "EventManager.h" #include "EventUserInterfaceUpdate.h" #include "GuiManager.h" #include "UserInputModeAnnotations.h" #include "WuQtUtilities.h" using namespace caret; /** * \class caret::UserInputModeAnnotationsWidget * \brief Toolbar widget for annotations. * \ingroup GuiQt */ /** * Constructor. * * @param inputModeAnnotations * Annotation mode input processor. * @param browserWindowIndex * Index of browser window using this widget. */ UserInputModeAnnotationsWidget::UserInputModeAnnotationsWidget(UserInputModeAnnotations* inputModeAnnotations, const int32_t browserWindowIndex) : QWidget(), m_browserWindowIndex(browserWindowIndex), m_inputModeAnnotations(inputModeAnnotations) { CaretAssert(inputModeAnnotations); m_textEditorWidget = new AnnotationTextEditorWidget(m_browserWindowIndex); m_lineArrowTipsWidget = new AnnotationLineArrowTipsWidget(m_browserWindowIndex); m_fontWidget = new AnnotationFontWidget(AnnotationWidgetParentEnum::ANNOTATION_TOOL_BAR_WIDGET, m_browserWindowIndex); m_colorWidget = new AnnotationColorWidget(AnnotationWidgetParentEnum::ANNOTATION_TOOL_BAR_WIDGET, m_browserWindowIndex); m_textAlignmentWidget = new AnnotationTextAlignmentWidget(m_browserWindowIndex); m_textOrientationWidget = new AnnotationTextOrientationWidget(m_browserWindowIndex); m_coordinateSpaceWidget = new AnnotationCoordinateSpaceWidget(m_browserWindowIndex); m_coordinateOneWidget = new AnnotationCoordinateWidget(AnnotationWidgetParentEnum::ANNOTATION_TOOL_BAR_WIDGET, AnnotationCoordinateWidget::COORDINATE_ONE, m_browserWindowIndex); m_coordinateTwoWidget = new AnnotationCoordinateWidget(AnnotationWidgetParentEnum::ANNOTATION_TOOL_BAR_WIDGET, AnnotationCoordinateWidget::COORDINATE_TWO, m_browserWindowIndex); m_widthHeightWidget = new AnnotationWidthHeightWidget(AnnotationWidgetParentEnum::ANNOTATION_TOOL_BAR_WIDGET, m_browserWindowIndex); m_rotationWidget = new AnnotationRotationWidget(m_browserWindowIndex); m_formatWidget = new AnnotationFormatWidget(m_browserWindowIndex); m_insertDeleteWidget = new AnnotationInsertNewWidget(m_browserWindowIndex); m_deleteWidget = new AnnotationDeleteWidget(m_browserWindowIndex); m_redoUndoWidget = new AnnotationRedoUndoWidget(m_browserWindowIndex); /* * Connect signals for setting a coordinate with the mouse. */ QObject::connect(m_coordinateOneWidget, SIGNAL(signalSelectCoordinateWithMouse()), this, SLOT(selectCoordinateOneWithMouse())); QObject::connect(m_coordinateTwoWidget, SIGNAL(signalSelectCoordinateWithMouse()), this, SLOT(selectCoordinateTwoWithMouse())); /* * Layout top row of widgets */ QWidget* topRowWidget = new QWidget(); QHBoxLayout* topRowLayout = new QHBoxLayout(topRowWidget); WuQtUtilities::setLayoutSpacingAndMargins(topRowLayout, 2, 2); topRowLayout->addWidget(m_colorWidget, 0, Qt::AlignTop); topRowLayout->addWidget(WuQtUtilities::createVerticalLineWidget()); topRowLayout->addWidget(m_lineArrowTipsWidget, 0, Qt::AlignTop); topRowLayout->addWidget(WuQtUtilities::createVerticalLineWidget()); topRowLayout->addWidget(m_textEditorWidget, 100, Qt::AlignTop); topRowLayout->addWidget(WuQtUtilities::createVerticalLineWidget()); topRowLayout->addWidget(m_fontWidget, 0, Qt::AlignTop); topRowLayout->addWidget(WuQtUtilities::createVerticalLineWidget()); topRowLayout->addWidget(m_textAlignmentWidget, 0, Qt::AlignTop); topRowLayout->addWidget(WuQtUtilities::createVerticalLineWidget()); topRowLayout->addWidget(m_textOrientationWidget, 0, Qt::AlignTop); topRowLayout->addWidget(WuQtUtilities::createVerticalLineWidget()); topRowLayout->addWidget(m_insertDeleteWidget, 0, Qt::AlignTop); topRowLayout->addWidget(WuQtUtilities::createVerticalLineWidget()); topRowLayout->addWidget(m_deleteWidget, 0, Qt::AlignTop); topRowLayout->addWidget(WuQtUtilities::createVerticalLineWidget()); topRowLayout->addWidget(m_formatWidget, 0, Qt::AlignTop); topRowLayout->addWidget(WuQtUtilities::createVerticalLineWidget()); topRowLayout->addWidget(m_redoUndoWidget, 0, Qt::AlignTop); topRowLayout->addStretch(); topRowWidget->setFixedHeight(topRowWidget->sizeHint().height()); /* * Layout bottom row of widgets */ QHBoxLayout* bottomRowLayout = new QHBoxLayout(); WuQtUtilities::setLayoutSpacingAndMargins(bottomRowLayout, 2, 2); bottomRowLayout->addWidget(m_coordinateSpaceWidget); bottomRowLayout->addWidget(WuQtUtilities::createVerticalLineWidget()); bottomRowLayout->addWidget(m_coordinateOneWidget); bottomRowLayout->addWidget(WuQtUtilities::createVerticalLineWidget()); bottomRowLayout->addWidget(m_coordinateTwoWidget); bottomRowLayout->addWidget(WuQtUtilities::createVerticalLineWidget()); bottomRowLayout->addWidget(m_widthHeightWidget); bottomRowLayout->addWidget(WuQtUtilities::createVerticalLineWidget()); bottomRowLayout->addWidget(m_rotationWidget); bottomRowLayout->addStretch(); QVBoxLayout* layout = new QVBoxLayout(this); WuQtUtilities::setLayoutSpacingAndMargins(layout, 0, 2); layout->addWidget(topRowWidget); layout->addWidget(WuQtUtilities::createHorizontalLineWidget()); layout->addLayout(bottomRowLayout); EventManager::get()->addEventListener(this, EventTypeEnum::EVENT_BRAIN_RESET); EventManager::get()->addEventListener(this, EventTypeEnum::EVENT_ANNOTATION_CREATE_NEW_TYPE); EventManager::get()->addEventListener(this, EventTypeEnum::EVENT_ANNOTATION_TOOLBAR_UPDATE); EventManager::get()->addEventListener(this, EventTypeEnum::EVENT_USER_INTERFACE_UPDATE); setFixedHeight(sizeHint().height()); } /** * Destructor. */ UserInputModeAnnotationsWidget::~UserInputModeAnnotationsWidget() { EventManager::get()->removeAllEventsFromListener(this); } /** * Receive an event. * * @param event * The event that the receive can respond to. */ void UserInputModeAnnotationsWidget::receiveEvent(Event* event) { bool updateAnnotationWidgetsFlag = false; if (event->getEventType() == EventTypeEnum::EVENT_BRAIN_RESET) { EventBrainReset* brainEvent = dynamic_cast(event); CaretAssert(brainEvent); updateAnnotationWidgetsFlag = true; brainEvent->setEventProcessed(); } else if (event->getEventType() == EventTypeEnum::EVENT_ANNOTATION_CREATE_NEW_TYPE) { EventAnnotationCreateNewType* annotationEvent = dynamic_cast(event); CaretAssert(annotationEvent); updateAnnotationWidgetsFlag = true; annotationEvent->setEventProcessed(); } else if (event->getEventType() == EventTypeEnum::EVENT_USER_INTERFACE_UPDATE) { EventUserInterfaceUpdate* updateEvent = dynamic_cast(event); CaretAssert(updateEvent); updateAnnotationWidgetsFlag = true; updateEvent->setEventProcessed(); } else if (event->getEventType() == EventTypeEnum::EVENT_ANNOTATION_TOOLBAR_UPDATE) { updateAnnotationWidgetsFlag = true; } else { return; } if (! updateAnnotationWidgetsFlag) { return; } updateWidget(); } /** * Select coordinate one with the mouse. */ void UserInputModeAnnotationsWidget::selectCoordinateOneWithMouse() { m_inputModeAnnotations->setMode(UserInputModeAnnotations::MODE_SET_COORDINATE_ONE); } /** * Select coordinate two with the mouse. */ void UserInputModeAnnotationsWidget::selectCoordinateTwoWithMouse() { m_inputModeAnnotations->setMode(UserInputModeAnnotations::MODE_SET_COORDINATE_TWO); } /** * Update the widget. */ void UserInputModeAnnotationsWidget::updateWidget() { /* * Show the proper widget */ switch (m_inputModeAnnotations->getMode()) { case UserInputModeAnnotations::MODE_NEW_WITH_CLICK: break; case UserInputModeAnnotations::MODE_NEW_WITH_DRAG: break; case UserInputModeAnnotations::MODE_PASTE: break; case UserInputModeAnnotations::MODE_PASTE_SPECIAL: break; case UserInputModeAnnotations::MODE_SELECT: break; case UserInputModeAnnotations::MODE_SET_COORDINATE_ONE: break; case UserInputModeAnnotations::MODE_SET_COORDINATE_TWO: break; } AnnotationManager* annotationManager = GuiManager::get()->getBrain()->getAnnotationManager(); std::vector selectedAnnotations = annotationManager->getAnnotationsSelectedForEditing(m_browserWindowIndex); std::vector lineAnnotations; std::vector textAnnotations; std::vector fontStyleAnnotations; std::vector twoDimAnnotations; std::vector oneDimAnnotations; for (std::vector::iterator iter = selectedAnnotations.begin(); iter != selectedAnnotations.end(); iter++) { Annotation* ann = *iter; CaretAssert(ann); AnnotationText* annText = dynamic_cast(ann); if (annText != NULL) { textAnnotations.push_back(annText); } AnnotationFontAttributesInterface* annFontStyle = dynamic_cast(ann); if (annFontStyle != NULL) { fontStyleAnnotations.push_back(annFontStyle); } AnnotationOneDimensionalShape* annOne = dynamic_cast(ann); if (annOne != NULL) { oneDimAnnotations.push_back(annOne); } AnnotationTwoDimensionalShape* annTwo= dynamic_cast(ann); if (annTwo != NULL) { twoDimAnnotations.push_back(annTwo); } AnnotationLine* annLine = dynamic_cast(ann); if (annLine != NULL) { lineAnnotations.push_back(annLine); } } m_coordinateSpaceWidget->updateContent(selectedAnnotations); m_fontWidget->updateContent(fontStyleAnnotations); m_textEditorWidget->updateContent(textAnnotations); m_colorWidget->updateContent(selectedAnnotations); m_lineArrowTipsWidget->updateContent(lineAnnotations); m_textAlignmentWidget->updateContent(textAnnotations); m_textOrientationWidget->updateContent(textAnnotations); m_widthHeightWidget->updateContent(twoDimAnnotations); m_rotationWidget->updateContent(selectedAnnotations); //twoDimAnnotations); m_insertDeleteWidget->updateContent(); m_deleteWidget->updateContent(); Annotation* coordEditAnnotation = NULL; AnnotationOneDimensionalShape* coordEditOneDimAnnotation = NULL; if (selectedAnnotations.size() == 1) { coordEditAnnotation = selectedAnnotations[0]; coordEditOneDimAnnotation = dynamic_cast(coordEditAnnotation); } m_coordinateOneWidget->updateContent(coordEditAnnotation); if (coordEditOneDimAnnotation != NULL) { m_coordinateTwoWidget->updateContent(coordEditOneDimAnnotation); m_coordinateTwoWidget->setVisible(true); } else { m_coordinateTwoWidget->setVisible(false); } m_redoUndoWidget->updateContent(); } connectome-workbench-1.2.3+git41-gc4c6c90/src/GuiQt/UserInputModeAnnotationsWidget.h000066400000000000000000000075201300200146000300770ustar00rootroot00000000000000#ifndef __USER_INPUT_MODE_ANNOTATIONS_WIDGET_H__ #define __USER_INPUT_MODE_ANNOTATIONS_WIDGET_H__ /*LICENSE_START*/ /* * Copyright (C) 2015 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 "EventListenerInterface.h" class QComboBox; namespace caret { class Annotation; class AnnotationColorWidget; class AnnotationCoordinateSpaceWidget; class AnnotationCoordinateWidget; class AnnotationDeleteWidget; class AnnotationFontWidget; class AnnotationFormatWidget; class AnnotationInsertNewWidget; class AnnotationLineArrowTipsWidget; class AnnotationRedoUndoWidget; class AnnotationRotationWidget; class AnnotationTextAlignmentWidget; class AnnotationTextEditorWidget; class AnnotationTextOrientationWidget; class AnnotationWidthHeightWidget; class UserInputModeAnnotations; class UserInputModeAnnotationsWidget : public QWidget, public EventListenerInterface { Q_OBJECT public: UserInputModeAnnotationsWidget(UserInputModeAnnotations* inputModeAnnotations, const int32_t browserWindowIndex); virtual ~UserInputModeAnnotationsWidget(); virtual void receiveEvent(Event* event); void updateWidget(); // ADD_NEW_METHODS_HERE private slots: void selectCoordinateOneWithMouse(); void selectCoordinateTwoWithMouse(); private: UserInputModeAnnotationsWidget(const UserInputModeAnnotationsWidget&); UserInputModeAnnotationsWidget& operator=(const UserInputModeAnnotationsWidget&); QWidget* createInsertMenuToolButton(); QWidget* createTextEditorWidget(); const int32_t m_browserWindowIndex; UserInputModeAnnotations* m_inputModeAnnotations; AnnotationCoordinateSpaceWidget* m_coordinateSpaceWidget; AnnotationCoordinateWidget* m_coordinateOneWidget; AnnotationCoordinateWidget* m_coordinateTwoWidget; AnnotationWidthHeightWidget* m_widthHeightWidget; AnnotationRotationWidget* m_rotationWidget; AnnotationLineArrowTipsWidget* m_lineArrowTipsWidget; AnnotationFontWidget* m_fontWidget; AnnotationColorWidget* m_colorWidget; AnnotationTextAlignmentWidget* m_textAlignmentWidget; AnnotationFormatWidget* m_formatWidget; AnnotationTextEditorWidget* m_textEditorWidget; AnnotationTextOrientationWidget* m_textOrientationWidget; AnnotationInsertNewWidget* m_insertDeleteWidget; AnnotationDeleteWidget* m_deleteWidget; AnnotationRedoUndoWidget* m_redoUndoWidget; // ADD_NEW_MEMBERS_HERE }; #ifdef __USER_INPUT_MODE_ANNOTATIONS_WIDGET_DECLARE__ // #endif // __USER_INPUT_MODE_ANNOTATIONS_WIDGET_DECLARE__ } // namespace #endif //__USER_INPUT_MODE_ANNOTATIONS_WIDGET_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/GuiQt/UserInputModeBorders.cxx000066400000000000000000000374731300200146000264230ustar00rootroot00000000000000 /*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 __USER_INPUT_MODE_BORDERS_DECLARE__ #include "UserInputModeBorders.h" #undef __USER_INPUT_MODE_BORDERS_DECLARE__ #include "Border.h" #include "BorderFile.h" #include "BorderPropertiesEditorDialog.h" #include "Brain.h" #include "BrainBrowserWindow.h" #include "BrainOpenGLWidget.h" #include "BrainOpenGLViewportContent.h" #include "BrowserTabContent.h" #include "CaretLogger.h" #include "CursorManager.h" #include "EventGraphicsUpdateAllWindows.h" #include "EventGraphicsUpdateOneWindow.h" #include "EventUserInterfaceUpdate.h" #include "EventManager.h" #include "GuiManager.h" #include "SelectionItemBorderSurface.h" #include "SelectionManager.h" #include "Model.h" #include "ModelSurface.h" #include "MouseEvent.h" #include "Surface.h" #include "SurfaceProjectedItem.h" #include "SurfaceProjectionBarycentric.h" #include "UserInputModeBordersWidget.h" #include "UserInputModeView.h" #include "WuQMessageBox.h" using namespace caret; /** * \class caret::UserInputModeBorders * \brief Processing user input for Borders mode. * * Processes user input in Border mode which includes * drawing and editing borders. */ /** * Constructor. * * @param borderBeingDrawnByOpenGL * Border that is displayed in OpenGL area when a border is being drawn * @param windowIndex * Index of the browser window using this border processing. */ UserInputModeBorders::UserInputModeBorders(Border* borderBeingDrawnByOpenGL, const int32_t windowIndex) : UserInputModeView(UserInputModeAbstract::BORDERS) { this->borderBeingDrawnByOpenGL = borderBeingDrawnByOpenGL; this->windowIndex = windowIndex; this->mode = MODE_DRAW; this->drawOperation = DRAW_OPERATION_CREATE; this->editOperation = EDIT_OPERATION_PROPERTIES; this->borderToolsWidget = new UserInputModeBordersWidget(this); setWidgetForToolBar(this->borderToolsWidget); } /** * Destructor. */ UserInputModeBorders::~UserInputModeBorders() { } /** * Draw a border point at the mouse coordinates. */ void UserInputModeBorders::drawPointAtMouseXY(BrainOpenGLWidget* openGLWidget, const int32_t mouseX, const int32_t mouseY) { SurfaceProjectedItem projectedItem; openGLWidget->performProjection(mouseX, mouseY, projectedItem); SurfaceProjectedItem* spi = new SurfaceProjectedItem(); AString txt; // if (projectedItem.isStereotaxicXYZValid()) { // spi->setStereotaxicXYZ(projectedItem.getStereotaxicXYZ()); // // txt += ("Projected Position: " // + AString::fromNumbers(projectedItem.getStereotaxicXYZ(), 3, ",")); // } if (projectedItem.getBarycentricProjection()->isValid()) { SurfaceProjectionBarycentric* bp = projectedItem.getBarycentricProjection(); txt += ("\nBarycentric Position: " + AString::fromNumbers(bp->getTriangleAreas(), 3, ",") + " " + AString::fromNumbers(bp->getTriangleNodes(), 3, ",")); SurfaceProjectionBarycentric* spb = spi->getBarycentricProjection(); spb->setProjectionSurfaceNumberOfNodes(bp->getProjectionSurfaceNumberOfNodes()); spb->setTriangleAreas(bp->getTriangleAreas()); spb->setTriangleNodes(bp->getTriangleNodes()); spb->setSignedDistanceAboveSurface(0.0); spb->setValid(true); } if (spi->isStereotaxicXYZValid() || spi->getBarycentricProjection()->isValid()) { if (borderBeingDrawnByOpenGL->getNumberOfPoints() == 0 || borderBeingDrawnByOpenGL->getStructure() == projectedItem.getStructure()) { spi->setStructure(projectedItem.getStructure()); this->borderBeingDrawnByOpenGL->addPoint(spi); } else { const AString prevName(StructureEnum::toGuiName(borderBeingDrawnByOpenGL->getStructure())); const AString newName(StructureEnum::toGuiName(projectedItem.getStructure())); WuQMessageBox::errorOk(borderToolsWidget, ("The last point added is on " + newName + " but all previous point(s) are on " + prevName + ". Either resume drawing on " + prevName + " or press the Reset button to remove all previous point(s) " "from " + prevName + " and draw on " + newName)); delete spi; } } else { delete spi; } CaretLogFiner(txt); } /** * Called when 'this' user input receiver is set * to receive events. */ void UserInputModeBorders::initialize() { this->borderToolsWidget->updateWidget(); } /** * Called when 'this' user input receiver is no * longer set to receive events. */ void UserInputModeBorders::finish() { } /** * Called to update the input receiver for various events. */ void UserInputModeBorders::update() { } /** * Update after borders changed. */ void UserInputModeBorders::updateAfterBordersChanged() { /* * Need to update all graphics windows and all border controllers. */ EventManager::get()->sendEvent(EventGraphicsUpdateAllWindows().getPointer()); EventManager::get()->sendEvent(EventUserInterfaceUpdate().addBorder().getPointer()); } /** * Get a description of this object's content. * @return String describing this object's content. */ AString UserInputModeBorders::toString() const { return "UserInputModeBorders"; } /** * @return the mode. */ UserInputModeBorders::Mode UserInputModeBorders::getMode() const { return this->mode; } /** * Set the mode. * @param mode * New value for mode. */ void UserInputModeBorders::setMode(const Mode mode) { if (this->mode != mode) { this->mode = mode; this->borderBeingDrawnByOpenGL->clear(); EventManager::get()->sendEvent(EventGraphicsUpdateOneWindow(this->windowIndex).getPointer()); } this->borderToolsWidget->updateWidget(); } /** * @return the create operation. */ UserInputModeBorders::DrawOperation UserInputModeBorders::getDrawOperation() const { return this->drawOperation; } /** * Set the create operation. * @param createOperation * New value for create operation. */ void UserInputModeBorders::setDrawOperation(const DrawOperation drawOperation) { this->drawOperation = drawOperation; this->borderToolsWidget->updateWidget(); EventManager::get()->sendEvent(EventGraphicsUpdateAllWindows().getPointer()); } /** * @return True if the border endpoints should be highlighted, else false. */ bool UserInputModeBorders::isHighlightBorderEndPoints() const { if (getMode() == MODE_DRAW) { if (getDrawOperation() != DRAW_OPERATION_CREATE) { return true; } } return false; } /** * Finish the border that the user was drawing. */ void UserInputModeBorders::drawOperationFinish() { this->borderBeingDrawnByOpenGL->clear(); EventManager::get()->sendEvent(EventGraphicsUpdateAllWindows().getPointer()); EventManager::get()->sendEvent(EventUserInterfaceUpdate().addBorder().getPointer()); } /** * Undo (remove last point) from border being drawn. */ void UserInputModeBorders::drawOperationUndo() { this->borderBeingDrawnByOpenGL->removeLastPoint(); EventManager::get()->sendEvent(EventGraphicsUpdateOneWindow(this->windowIndex).getPointer()); } /** * Reset the border being drawn. */ void UserInputModeBorders::drawOperationReset() { this->borderBeingDrawnByOpenGL->clear(); EventManager::get()->sendEvent(EventGraphicsUpdateOneWindow(this->windowIndex).getPointer()); } /** * @return The edit operation. */ UserInputModeBorders::EditOperation UserInputModeBorders::getEditOperation() const { return this->editOperation; } /** * Set the edit operation. * @param editOperation * New edit operation. */ void UserInputModeBorders::setEditOperation(const EditOperation editOperation) { this->editOperation = editOperation; } /** * @return The cursor for display in the OpenGL widget. */ CursorEnum::Enum UserInputModeBorders::getCursor() const { CursorEnum::Enum cursor = CursorEnum::CURSOR_DEFAULT; switch (this->mode) { case MODE_DRAW: cursor = CursorEnum::CURSOR_DRAWING_PEN; break; case MODE_EDIT: cursor = CursorEnum::CURSOR_POINTING_HAND; break; case MODE_ROI: cursor = CursorEnum::CURSOR_POINTING_HAND; break; } return cursor; } /** * Process a mouse left click event. * * @param mouseEvent * Mouse event information. */ void UserInputModeBorders::mouseLeftClick(const MouseEvent& mouseEvent) { BrainOpenGLWidget* openGLWidget = mouseEvent.getOpenGLWidget(); const int mouseX = mouseEvent.getX(); const int mouseY = mouseEvent.getY(); switch (this->mode) { case MODE_DRAW: this->drawPointAtMouseXY(openGLWidget, mouseX, mouseY); EventManager::get()->sendEvent(EventGraphicsUpdateOneWindow(this->windowIndex).getPointer()); break; case MODE_EDIT: { SelectionManager* idManager = openGLWidget->performIdentification(mouseX, mouseY, true); SelectionItemBorderSurface* idBorder = idManager->getSurfaceBorderIdentification(); if (idBorder->isValid()) { BorderFile* borderFile = idBorder->getBorderFile(); if (borderFile->isSingleStructure()) { switch (this->editOperation) { case EDIT_OPERATION_DELETE: { Border* border = idBorder->getBorder(); borderFile->removeBorder(border); this->updateAfterBordersChanged(); } break; case EDIT_OPERATION_PROPERTIES: { Border* border = idBorder->getBorder(); std::auto_ptr editBorderDialog( BorderPropertiesEditorDialog::newInstanceEditBorder(borderFile, border, openGLWidget)); if (editBorderDialog->exec() == BorderPropertiesEditorDialog::Accepted) { this->updateAfterBordersChanged(); } } break; } } else { WuQMessageBox::errorOk(this->borderToolsWidget, borderFile->getObsoleteMultiStructureFormatMessage()); } } } break; case MODE_ROI: { SelectionManager* idManager = openGLWidget->performIdentification(mouseX, mouseY, true); SelectionItemBorderSurface* idBorder = idManager->getSurfaceBorderIdentification(); if (idBorder->isValid()) { Brain* brain = idBorder->getBrain(); Surface* surface = idBorder->getSurface(); //BorderFile* borderFile = idBorder->getBorderFile(); Border* border = idBorder->getBorder(); this->borderToolsWidget->executeRoiInsideSelectedBorderOperation(brain, surface, border); } } break; } } /** * Process a mouse left click with Shift key event. * * @param mouseEvent * Mouse event information. */ void UserInputModeBorders::mouseLeftClickWithShift(const MouseEvent& /*mouseEvent*/) { switch (this->mode) { case MODE_DRAW: this->borderToolsWidget->executeFinishOperation(); break; case MODE_EDIT: break; case MODE_ROI: break; } } /** * Process a mouse left click with ctrl and shift keys down event. * * @param mouseEvent * Mouse event information. */ void UserInputModeBorders::mouseLeftClickWithCtrlShift(const MouseEvent& mouseEvent) { BrainOpenGLWidget* openGLWidget = mouseEvent.getOpenGLWidget(); const int mouseX = mouseEvent.getX(); const int mouseY = mouseEvent.getY(); switch (this->mode) { case MODE_DRAW: this->drawPointAtMouseXY(openGLWidget, mouseX, mouseY); EventManager::get()->sendEvent(EventGraphicsUpdateOneWindow(this->windowIndex).getPointer()); break; case MODE_EDIT: break; case MODE_ROI: break; } } /** * Process a mouse left drag with ctrl and shift keys down event. * * @param mouseEvent * Mouse event information. */ void UserInputModeBorders::mouseLeftDragWithCtrlShift(const MouseEvent& mouseEvent) { BrainOpenGLWidget* openGLWidget = mouseEvent.getOpenGLWidget(); const int mouseX = mouseEvent.getX(); const int mouseY = mouseEvent.getY(); switch (this->mode) { case MODE_DRAW: this->drawPointAtMouseXY(openGLWidget, mouseX, mouseY); EventManager::get()->sendEvent(EventGraphicsUpdateOneWindow(this->windowIndex).getPointer()); break; case MODE_EDIT: break; case MODE_ROI: break; } } /** * Show a context menu (pop-up menu at mouse location) * * @param mouseEvent * Mouse event information. * @param menuPosition * Point at which menu is displayed (passed to QMenu::exec()) * @param openGLWidget * OpenGL widget in which context menu is requested */ void UserInputModeBorders::showContextMenu(const MouseEvent& mouseEvent, const QPoint& menuPosition, BrainOpenGLWidget* openGLWidget) { UserInputModeView::showContextMenu(mouseEvent, menuPosition, openGLWidget); } connectome-workbench-1.2.3+git41-gc4c6c90/src/GuiQt/UserInputModeBorders.h000066400000000000000000000101241300200146000260300ustar00rootroot00000000000000#ifndef __USER_INPUT_MODE_BORDERS__H_ #define __USER_INPUT_MODE_BORDERS__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 "UserInputModeView.h" namespace caret { class Border; class UserInputModeBordersWidget; class UserInputModeBorders : public UserInputModeView { public: enum Mode { MODE_DRAW, MODE_EDIT, MODE_ROI }; enum DrawOperation { DRAW_OPERATION_CREATE, DRAW_OPERATION_ERASE, DRAW_OPERATION_EXTEND, DRAW_OPERATION_OPTIMIZE, DRAW_OPERATION_REPLACE }; enum EditOperation { EDIT_OPERATION_DELETE, EDIT_OPERATION_PROPERTIES }; UserInputModeBorders(Border* borderBeingDrawnByOpenGL, const int32_t windowIndex); virtual ~UserInputModeBorders(); virtual void initialize(); virtual void finish(); virtual void update(); Mode getMode() const; virtual void mouseLeftClick(const MouseEvent& mouseEvent); virtual void mouseLeftClickWithShift(const MouseEvent& mouseEvent); virtual void mouseLeftClickWithCtrlShift(const MouseEvent& mouseEvent); virtual void mouseLeftDragWithCtrlShift(const MouseEvent& mouseEvent); virtual void showContextMenu(const MouseEvent& mouseEvent, const QPoint& menuPosition, BrainOpenGLWidget* openGLWidget); virtual CursorEnum::Enum getCursor() const; virtual AString toString() const; bool isHighlightBorderEndPoints() const; private: /* * Some private methods are accessed by this friend class */ friend class UserInputModeBordersWidget; UserInputModeBorders(const UserInputModeBorders&); UserInputModeBorders& operator=(const UserInputModeBorders&); void setMode(const Mode mode); DrawOperation getDrawOperation() const; void setDrawOperation(const DrawOperation drawOperation); EditOperation getEditOperation() const; void setEditOperation(const EditOperation editOperation); void drawPointAtMouseXY(BrainOpenGLWidget* openGLWidget, const int32_t mouseX, const int32_t mouseY); void drawOperationFinish(); void drawOperationUndo(); void drawOperationReset(); void updateAfterBordersChanged(); UserInputModeBordersWidget* borderToolsWidget; Mode mode; DrawOperation drawOperation; EditOperation editOperation; /** * Pointer to border drawn by OpenGL. Since owned * by OpenGL, DO NOT delete this!!! */ Border* borderBeingDrawnByOpenGL; int32_t windowIndex; }; #ifdef __USER_INPUT_MODE_BORDERS_DECLARE__ // #endif // __USER_INPUT_MODE_BORDERS_DECLARE__ } // namespace #endif //__USER_INPUT_MODE_BORDERS__H_ connectome-workbench-1.2.3+git41-gc4c6c90/src/GuiQt/UserInputModeBordersWidget.cxx000066400000000000000000001346541300200146000275660ustar00rootroot00000000000000 /*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 #include #include #define __USER_INPUT_MODE_BORDERS_WIDGET_DECLARE__ #include "UserInputModeBordersWidget.h" #undef __USER_INPUT_MODE_BORDERS_WIDGET_DECLARE__ #include "AlgorithmException.h" #include "AlgorithmNodesInsideBorder.h" #include "Border.h" #include "BorderEditingSelectionDialog.h" #include "BorderFile.h" #include "BorderOptimizeDialog.h" #include "BorderPointFromSearch.h" #include "BorderPropertiesEditorDialog.h" #include "Brain.h" #include "BrainBrowserWindow.h" #include "BrainStructure.h" #include "BrowserTabContent.h" #include "CaretAssert.h" #include "CaretLogger.h" #include "DisplayPropertiesBorders.h" #include "EventBrainReset.h" #include "EventGraphicsUpdateAllWindows.h" #include "EventManager.h" #include "EventUserInterfaceUpdate.h" #include "GuiManager.h" #include "LabelFile.h" #include "MetricFile.h" #include "ModelSurface.h" #include "ModelSurfaceMontage.h" #include "ModelWholeBrain.h" #include "RegionOfInterestCreateFromBorderDialog.h" #include "Surface.h" #include "UserInputModeBorders.h" #include "WuQDataEntryDialog.h" #include "WuQMessageBox.h" #include "WuQtUtilities.h" using namespace caret; /** * \class caret::UserInputModeBordersWidget * \brief Controls for when the user input mode is border operations */ /** * Constructor. * * @param inputModeBorders * Mouse processing for border operations. * @param parent * Optional parent widget. */ UserInputModeBordersWidget::UserInputModeBordersWidget(UserInputModeBorders* inputModeBorders, QWidget* parent) : QWidget(parent) { m_transformToolTipText = ("\n\n" "At any time, the view of the surface may be changed by\n" " PAN: Move the mouse with the left mouse button down while " "holding down the Shift key.\n" " ROTATE: Move the mouse with the left mouse button down.\n" " ZOOM: Move the mouse with the left mouse button down while " "holding down the Ctrl key (Apple key on Macs)." ); m_borderOptimizeDialog = NULL; this->inputModeBorders = inputModeBorders; QLabel* nameLabel = new QLabel("Borders "); this->widgetMode = this->createModeWidget(); resetLastEditedBorder(); this->widgetDrawOperation = this->createDrawOperationWidget(); this->widgetEditOperation = this->createEditOperationWidget(); this->widgetRoiOperation = this->createRoiOperationWidget(); this->operationStackedWidget = new QStackedWidget(); this->operationStackedWidget->addWidget(this->widgetDrawOperation); this->operationStackedWidget->addWidget(this->widgetEditOperation); this->operationStackedWidget->addWidget(this->widgetRoiOperation); QHBoxLayout* layout = new QHBoxLayout(this); WuQtUtilities::setLayoutSpacingAndMargins(layout, 0, 0); layout->addWidget(nameLabel); layout->addWidget(this->widgetMode); layout->addSpacing(10); layout->addWidget(this->operationStackedWidget); layout->addStretch(); EventManager::get()->addEventListener(this, EventTypeEnum::EVENT_BRAIN_RESET); } /** * Destructor. */ UserInputModeBordersWidget::~UserInputModeBordersWidget() { EventManager::get()->removeAllEventsFromListener(this); } /** * Receive an event. * * @param event * The event that the receive can respond to. */ void UserInputModeBordersWidget::receiveEvent(Event* event) { if (event->getEventType() == EventTypeEnum::EVENT_BRAIN_RESET) { EventBrainReset* brainEvent = dynamic_cast(event); CaretAssert(brainEvent); if (m_borderOptimizeDialog != NULL) { delete m_borderOptimizeDialog; m_borderOptimizeDialog = NULL; } brainEvent->setEventProcessed(); } } /** * Update the contents of the widget. */ void UserInputModeBordersWidget::updateWidget() { /* * Show the proper widget */ switch (this->inputModeBorders->getMode()) { case UserInputModeBorders::MODE_DRAW: this->operationStackedWidget->setCurrentWidget(this->widgetDrawOperation); this->setActionGroupByActionData(this->drawOperationActionGroup, inputModeBorders->getDrawOperation()); resetLastEditedBorder(); break; case UserInputModeBorders::MODE_EDIT: this->operationStackedWidget->setCurrentWidget(this->widgetEditOperation); this->setActionGroupByActionData(this->editOperationActionGroup, inputModeBorders->getEditOperation()); break; case UserInputModeBorders::MODE_ROI: this->operationStackedWidget->setCurrentWidget(this->widgetRoiOperation); resetLastEditedBorder(); break; } const int selectedModeInteger = (int)this->inputModeBorders->getMode(); const int modeComboBoxIndex = this->modeComboBox->findData(selectedModeInteger); CaretAssert(modeComboBoxIndex >= 0); this->modeComboBox->blockSignals(true); this->modeComboBox->setCurrentIndex(modeComboBoxIndex); this->modeComboBox->blockSignals(false); } /** * Set the action with its data value of the given integer * as the active action. * @param actionGroup * Action group for which action is selected. * @param dataInteger * Integer value for data attribute. */ void UserInputModeBordersWidget::setActionGroupByActionData(QActionGroup* actionGroup, const int dataInteger) { actionGroup->blockSignals(true); const QList actionList = actionGroup->actions(); QListIterator iter(actionList); while (iter.hasNext()) { QAction* action = iter.next(); const int actionDataInteger = action->data().toInt(); if (dataInteger == actionDataInteger) { action->setChecked(true); break; } } actionGroup->blockSignals(false); } /** * @return The mode widget. */ QWidget* UserInputModeBordersWidget::createModeWidget() { this->modeComboBox = new QComboBox(); this->modeComboBox->addItem("Draw", (int)UserInputModeBorders::MODE_DRAW); this->modeComboBox->addItem("Edit", (int)UserInputModeBorders::MODE_EDIT); this->modeComboBox->addItem("ROI", (int)UserInputModeBorders::MODE_ROI); QObject::connect(this->modeComboBox, SIGNAL(currentIndexChanged(int)), this, SLOT(modeComboBoxSelection(int))); QWidget* widget = new QWidget(); QHBoxLayout* layout = new QHBoxLayout(widget); WuQtUtilities::setLayoutSpacingAndMargins(layout, 2, 0); layout->addWidget(this->modeComboBox); widget->setFixedWidth(widget->sizeHint().width()); return widget; } /** * Called when a mode is selected from the mode combo box. * @param indx * Index of item selected. */ void UserInputModeBordersWidget::modeComboBoxSelection(int indx) { const int modeInteger = this->modeComboBox->itemData(indx).toInt(); const UserInputModeBorders::Mode mode = (UserInputModeBorders::Mode)modeInteger; this->inputModeBorders->setMode(mode); resetLastEditedBorder(); } /** * @return The draw operation widget. */ QWidget* UserInputModeBordersWidget::createDrawOperationWidget() { /* * Draw */ const AString drawToolTipText = ("To draw a new border segment either click the mouse " "to discretely add border points or hold down the CTRL (Apple key on Mac) and SHIFT keys " "and move the mouse with the left mouse button down to continuously " "add points. " "When the border is complete, either press the Finish button " "or hold down the Shift key and click the mouse to display a " "dialog for assigning the borders attributes (name and color). " + m_transformToolTipText); QAction* drawAction = WuQtUtilities::createAction("New", WuQtUtilities::createWordWrappedToolTipText(drawToolTipText), this); drawAction->setCheckable(true); drawAction->setData(static_cast(UserInputModeBorders::DRAW_OPERATION_CREATE)); QToolButton* drawToolButton = new QToolButton(); drawToolButton->setDefaultAction(drawAction); /* * Erase */ const AString eraseToolTipText = ("To erase a section of a border, click the mouse twice, once " "at the beginning of the section that is to be removed and a " "second time at the end of the section. " "Press the Finish button or hold down the Shift key and click the " "mouse remove the border section." + m_transformToolTipText); QAction* eraseAction = WuQtUtilities::createAction("Erase", WuQtUtilities::createWordWrappedToolTipText(eraseToolTipText), this); eraseAction->setCheckable(true); eraseAction->setData(static_cast(UserInputModeBorders::DRAW_OPERATION_ERASE)); QToolButton* eraseToolButton = new QToolButton(); eraseToolButton->setDefaultAction(eraseAction); /* * Extend */ const AString extendToolTipText = ("To extend a border, move the mouse ANY point in the border and " "either click the mouse to discretely add points or hold down the CTRL (Apple key on Mac) and SHIFT keys " "and move the mouse with the left mouse button down to continuously add points. " "Press the Finish button or hold down the Shift key and click the " "mouse add the extension to the border." "\n\n" "If the segment starts at a point within the border (not an end point), points will be removed " "from that point to the nearest end point in the border and then the extension " "will be added." + m_transformToolTipText); QAction* extendAction = WuQtUtilities::createAction("Extend", WuQtUtilities::createWordWrappedToolTipText(extendToolTipText), this); extendAction->setCheckable(true); extendAction->setData(static_cast(UserInputModeBorders::DRAW_OPERATION_EXTEND)); QToolButton* extendToolButton = new QToolButton(); extendToolButton->setDefaultAction(extendAction); /* * Replace */ const AString replaceToolTipText = ("To replace a section of a border, move the mouse to the " "start of the section that is being replaced. Either click " "the mouse to discretely add points in the new section or hold down the CTRL (Apple key on Mac) and SHIFT keys " "and move the mouse with the left mouse button down to continuously add points. " "Press the Finish button or hold down the Shift key and click the " "mouse to conclude replacing the section in the border." "\n\n" "Both the first point and the last point in the segment must " "overlap points in the border." + m_transformToolTipText); QAction* replaceAction = WuQtUtilities::createAction("Replace", WuQtUtilities::createWordWrappedToolTipText(replaceToolTipText), this); replaceAction->setCheckable(true); replaceAction->setData(static_cast(UserInputModeBorders::DRAW_OPERATION_REPLACE)); QToolButton* replaceToolButton = new QToolButton(); replaceToolButton->setDefaultAction(replaceAction); /* * Optimize */ const AString optimizeToolTipText("A new border optimization process automatically repositions a manually drawn " "border segment to follow the most probable path based on spatial gradients of " "a set of user-selected feature maps (useful for cortical parcellation)."); QAction* optimizeAction = WuQtUtilities::createAction("Optimize", WuQtUtilities::createWordWrappedToolTipText(optimizeToolTipText), this); optimizeAction->setCheckable(true); optimizeAction->setData(static_cast(UserInputModeBorders::DRAW_OPERATION_OPTIMIZE)); QToolButton* optimizeToolButton = new QToolButton(); optimizeToolButton->setDefaultAction(optimizeAction); /* * Finish */ const AString finishToolTipText = ("The finish button must be pressed (holding down the Shift key " "and clicking the mouse is a shortcut to clicking the Finish " "button) to complete any of the border drawing operations."); QAction* finishAction = WuQtUtilities::createAction("Finish", WuQtUtilities::createWordWrappedToolTipText(finishToolTipText), this, this, SLOT(drawFinishButtonClicked())); QToolButton* finishToolButton = new QToolButton(); finishToolButton->setDefaultAction(finishAction); /* * Undo */ QAction* undoAction = WuQtUtilities::createAction("Undo", "Remove (undo) the last point in the\n" "drawn border segment. If the button\n" "is held down, it will repeat removal\n" "of points until the button is released.", this, this, SLOT(drawUndoButtonClicked())); QToolButton* undoToolButton = new QToolButton(); undoToolButton->setDefaultAction(undoAction); undoToolButton->setAutoRepeat(true); undoToolButton->setAutoRepeatDelay(500); // 500ms = 1/2 second undoToolButton->setAutoRepeatInterval(100); // 100ms = 1/10 second QAction* undoFinishAction = WuQtUtilities::createAction("Undo Finish", "Undo the last Erase/Extend/Replace\n" "performed on a border.", this, this, SLOT(drawUndoLastEditButtonClicked())); m_undoFinishToolButton = new QToolButton(); m_undoFinishToolButton->setDefaultAction(undoFinishAction); /* * Reset */ QAction* resetAction = WuQtUtilities::createAction("Reset", "Remove all points in the unfinished border", this, this, SLOT(drawResetButtonClicked())); QToolButton* resetToolButton = new QToolButton(); resetToolButton->setDefaultAction(resetAction); this->drawOperationActionGroup = new QActionGroup(this); this->drawOperationActionGroup->addAction(drawAction); this->drawOperationActionGroup->addAction(eraseAction); this->drawOperationActionGroup->addAction(extendAction); this->drawOperationActionGroup->addAction(optimizeAction); this->drawOperationActionGroup->addAction(replaceAction); this->drawOperationActionGroup->setExclusive(true); QObject::connect(this->drawOperationActionGroup, SIGNAL(triggered(QAction*)), this, SLOT(drawOperationActionTriggered(QAction*))); QWidget* widget = new QWidget(); QHBoxLayout* layout = new QHBoxLayout(widget); WuQtUtilities::setLayoutSpacingAndMargins(layout, 2, 0); layout->addWidget(drawToolButton); layout->addWidget(eraseToolButton); layout->addWidget(extendToolButton); layout->addWidget(optimizeToolButton); layout->addWidget(replaceToolButton); layout->addSpacing(10); layout->addWidget(finishToolButton); layout->addWidget(m_undoFinishToolButton); layout->addSpacing(10); layout->addWidget(undoToolButton); layout->addWidget(resetToolButton); widget->setFixedWidth(widget->sizeHint().width()); return widget; } /** * Called when draw border reset button clicked. */ void UserInputModeBordersWidget::drawResetButtonClicked() { this->inputModeBorders->drawOperationReset(); } /** * Called when draw border undo button clicked. */ void UserInputModeBordersWidget::drawUndoButtonClicked() { this->inputModeBorders->drawOperationUndo(); } /** * Undo editing (erase/extend/replace) of last border. */ void UserInputModeBordersWidget::drawUndoLastEditButtonClicked() { for (std::vector::iterator iter = m_undoFinishBorders.begin(); iter != m_undoFinishBorders.end(); iter++) { BorderFile* undoBorderFile = iter->m_borderFile; Border* undoBorder = iter->m_border; bool foundBorderFlag = false; Brain* brain = GuiManager::get()->getBrain(); const int32_t numBorderFiles = brain->getNumberOfBorderFiles(); for (int32_t i = 0; i < numBorderFiles; i++) { BorderFile* bf = brain->getBorderFile(i); if (bf == undoBorderFile) { foundBorderFlag = true; break; } } if (foundBorderFlag) { foundBorderFlag = false; const int32_t numBorders = undoBorderFile->getNumberOfBorders(); for (int32_t i = 0; i < numBorders; i++) { if (undoBorderFile->getBorder(i) == undoBorder) { foundBorderFlag = true; break; } } } if (foundBorderFlag) { if (undoBorder->isUndoBorderValid()) { if (WuQMessageBox::warningOkCancel(m_undoFinishToolButton, ("Undo changes to " + undoBorder->getName()))) { undoBorder->undoLastBorderEditing(); } } else { WuQMessageBox::errorOk(m_undoFinishToolButton, ("Cannot undo border " + undoBorder->getName())); } } else { WuQMessageBox::errorOk(m_undoFinishToolButton, "Cannot undo last edited border. " "Did not find border for undoing."); } } resetLastEditedBorder(); } /** * Publicly accessible method for initiating * an operation as if the Finish button was * pressed. */ void UserInputModeBordersWidget::executeFinishOperation() { this->drawFinishButtonClicked(); } /** * Called when draw border finish button clicked. */ void UserInputModeBordersWidget::drawFinishButtonClicked() { BrainBrowserWindow* browserWindow = GuiManager::get()->getBrowserWindowByWindowIndex(this->inputModeBorders->windowIndex); if (browserWindow == NULL) { return; } BrowserTabContent* btc = browserWindow->getBrowserTabContent(); if (btc == NULL) { return; } const int32_t browserTabIndex = btc->getTabNumber(); if (this->inputModeBorders->borderBeingDrawnByOpenGL->verifyAllPointsOnSameStructure() == false) { WuQMessageBox::errorOk(this, "Error: Border points are on more than one structure."); return; } AString modeText; int32_t minimumNumberOfBorderPoints = 2; switch (this->inputModeBorders->getDrawOperation()) { case UserInputModeBorders::DRAW_OPERATION_CREATE: modeText = "creating a border."; minimumNumberOfBorderPoints = 2; break; case UserInputModeBorders::DRAW_OPERATION_OPTIMIZE: modeText = "optimizing borders."; minimumNumberOfBorderPoints = 3; break; case UserInputModeBorders::DRAW_OPERATION_ERASE: modeText = "erasing a segment in a border."; minimumNumberOfBorderPoints = 2; break; case UserInputModeBorders::DRAW_OPERATION_EXTEND: modeText = "extending a border."; minimumNumberOfBorderPoints = 2; break; case UserInputModeBorders::DRAW_OPERATION_REPLACE: modeText = "replacing points in a border."; minimumNumberOfBorderPoints = 2; break; } if (this->inputModeBorders->borderBeingDrawnByOpenGL->getNumberOfPoints() < minimumNumberOfBorderPoints) { WuQMessageBox::errorOk(this, ("There must be at least " + AString::number(minimumNumberOfBorderPoints) + " points when " + modeText)); return; } ModelSurface* surfaceController = btc->getDisplayedSurfaceModel(); ModelWholeBrain* wholeBrainController = btc->getDisplayedWholeBrainModel(); ModelSurfaceMontage* surfaceMontageController = btc->getDisplayedSurfaceMontageModel(); Brain* brain = NULL; Surface* surface = NULL; if (surfaceController != NULL) { brain = surfaceController->getBrain(); surface = surfaceController->getSurface(); } else if (wholeBrainController != NULL) { brain = wholeBrainController->getBrain(); const StructureEnum::Enum structure = this->inputModeBorders->borderBeingDrawnByOpenGL->getStructure(); surface = wholeBrainController->getSelectedSurface(structure, btc->getTabNumber()); } else if (surfaceMontageController != NULL) { brain = surfaceMontageController->getBrain(); const StructureEnum::Enum structure = this->inputModeBorders->borderBeingDrawnByOpenGL->getStructure(); surface = surfaceMontageController->getSelectedSurface(structure, btc->getTabNumber()); } if (surface == NULL) { CaretLogSevere("PROGRAM ERROR: Cannot find surface for border drawing."); return; } if (brain == NULL) { CaretLogSevere("PROGRAM ERROR: Cannot find surface for border drawing."); return; } DisplayPropertiesBorders* dpb = GuiManager::get()->getBrain()->getDisplayPropertiesBorders(); const DisplayGroupEnum::Enum displayGroup = dpb->getDisplayGroupForTab(btc->getTabNumber()); dpb->setDisplayed(displayGroup, browserTabIndex, true); switch (this->inputModeBorders->getDrawOperation()) { case UserInputModeBorders::DRAW_OPERATION_CREATE: { std::auto_ptr finishBorderDialog( BorderPropertiesEditorDialog::newInstanceFinishBorder(this->inputModeBorders->borderBeingDrawnByOpenGL, surface, this)); if (finishBorderDialog->exec() == BorderPropertiesEditorDialog::Accepted) { this->inputModeBorders->drawOperationFinish(); } } break; case UserInputModeBorders::DRAW_OPERATION_OPTIMIZE: processBorderOptimization(displayGroup, browserTabIndex, surface, this->inputModeBorders->borderBeingDrawnByOpenGL); break; case UserInputModeBorders::DRAW_OPERATION_ERASE: case UserInputModeBorders::DRAW_OPERATION_EXTEND: case UserInputModeBorders::DRAW_OPERATION_REPLACE: { const float nearestTolerance = 10; std::vector allNearbyBorders; const int32_t numBorderFiles = brain->getNumberOfBorderFiles(); for (int32_t ibf = 0; ibf < numBorderFiles; ibf++) { const BorderFile* borderFile = brain->getBorderFile(ibf); std::vector bordersFoundFromFile; switch (this->inputModeBorders->getDrawOperation()) { case UserInputModeBorders::DRAW_OPERATION_CREATE: CaretAssert(0); break; case UserInputModeBorders::DRAW_OPERATION_ERASE: borderFile->findAllBordersWithPointsNearBothSegmentEndPoints(displayGroup, browserTabIndex, surface, this->inputModeBorders->borderBeingDrawnByOpenGL, nearestTolerance, bordersFoundFromFile); break; case UserInputModeBorders::DRAW_OPERATION_EXTEND: borderFile->findAllBordersWithAnyPointNearSegmentFirstPoint(displayGroup, browserTabIndex, surface, this->inputModeBorders->borderBeingDrawnByOpenGL, nearestTolerance, bordersFoundFromFile); // borderFile->findAllBordersWithEndPointNearSegmentFirstPoint(displayGroup, // browserTabIndex, // surface, // this->inputModeBorders->borderBeingDrawnByOpenGL, // nearestTolerance, // bordersFoundFromFile); break; case UserInputModeBorders::DRAW_OPERATION_OPTIMIZE: CaretAssert(0); break; case UserInputModeBorders::DRAW_OPERATION_REPLACE: borderFile->findAllBordersWithPointsNearBothSegmentEndPoints(displayGroup, browserTabIndex, surface, this->inputModeBorders->borderBeingDrawnByOpenGL, nearestTolerance, bordersFoundFromFile); break; } allNearbyBorders.insert(allNearbyBorders.end(), bordersFoundFromFile.begin(), bordersFoundFromFile.end()); } if (allNearbyBorders.empty()) { WuQMessageBox::errorOk(this, "No borders were found near the border editing points"); return; } else { std::sort(allNearbyBorders.begin(), allNearbyBorders.end()); const int32_t numBorders = static_cast(allNearbyBorders.size()); AString modeMessage; switch (this->inputModeBorders->getDrawOperation()) { case UserInputModeBorders::DRAW_OPERATION_CREATE: CaretAssert(0); break; case UserInputModeBorders::DRAW_OPERATION_ERASE: modeMessage = "Erase segement in"; break; case UserInputModeBorders::DRAW_OPERATION_EXTEND: modeMessage = "Extend"; break; case UserInputModeBorders::DRAW_OPERATION_OPTIMIZE: CaretAssert(0); break; case UserInputModeBorders::DRAW_OPERATION_REPLACE: modeMessage = "Replace segment in"; break; } std::vector borderNames; for (int32_t i = 0; i < numBorders; i++) { BorderPointFromSearch& bpfs = allNearbyBorders[i]; borderNames.push_back(bpfs.border()->getName() + " (" + AString::number(bpfs.distance(), 'f', 6) + " mm)"); } BorderEditingSelectionDialog selDialog(modeMessage, borderNames, this); if (selDialog.exec() == BorderEditingSelectionDialog::Accepted) { AString errorMessage; std::vector undoBorders; for (int32_t i = 0; i < numBorders; i++) { if (selDialog.isBorderNameSelected(i)) { BorderPointFromSearch& bpfs = allNearbyBorders[i]; BorderFile* borderFile = bpfs.borderFile(); Border* border = bpfs.border(); int32_t borderPointIndex = bpfs.borderPointIndex(); CaretAssert(borderFile); CaretAssert(border); try { switch (this->inputModeBorders->getDrawOperation()) { case UserInputModeBorders::DRAW_OPERATION_CREATE: CaretAssert(0); break; case UserInputModeBorders::DRAW_OPERATION_ERASE: border->reviseEraseFromEnd(surface, this->inputModeBorders->borderBeingDrawnByOpenGL); break; case UserInputModeBorders::DRAW_OPERATION_EXTEND: border->reviseExtendFromPointIndex(surface, borderPointIndex, this->inputModeBorders->borderBeingDrawnByOpenGL); //border->reviseExtendFromEnd(surface, // this->inputModeBorders->borderBeingDrawnByOpenGL); break; case UserInputModeBorders::DRAW_OPERATION_OPTIMIZE: CaretAssert(0); break; case UserInputModeBorders::DRAW_OPERATION_REPLACE: border->reviseReplaceSegment(surface, this->inputModeBorders->borderBeingDrawnByOpenGL); break; } undoBorders.push_back(BorderFileAndBorderMemento(borderFile, border)); } catch (BorderException& e) { errorMessage.appendWithNewLine(e.whatString()); } } } if ( ! errorMessage.isEmpty()) { WuQMessageBox::errorOk(this, errorMessage); } else { setLastEditedBorder(undoBorders); this->inputModeBorders->borderBeingDrawnByOpenGL->clear(); } EventManager::get()->sendEvent(EventUserInterfaceUpdate().getPointer()); EventManager::get()->sendEvent(EventGraphicsUpdateAllWindows().getPointer()); } } } break; } } /** * Process the border optimization operation. * * @param surface * Surface on which border is drawn. * @param borderDrawnByUser * Border drawn by the user. */ void UserInputModeBordersWidget::processBorderOptimization(const DisplayGroupEnum::Enum displayGroup, const int32_t browserTabIndex, Surface* surface, Border* borderDrawnByUser) { std::vector nodesInsideBorder; try { ProgressObject* myProgObj = NULL; const bool inverseSelectionFlag = false; /* * Find nodes inside border */ AlgorithmNodesInsideBorder nib(myProgObj, surface, borderDrawnByUser, inverseSelectionFlag, nodesInsideBorder); } catch (const AlgorithmException& e) { WuQMessageBox::errorOk(this, e.whatString()); return; } if (nodesInsideBorder.empty()) { WuQMessageBox::errorOk(this, ("No nodes were found inside the drawn border " + borderDrawnByUser->getName())); return; } Brain* brain = GuiManager::get()->getBrain(); const StructureEnum::Enum surfaceStructure = surface->getStructure(); const BrainStructure* brainStructure = brain->getBrainStructure(surfaceStructure, false); if (brainStructure == NULL) { WuQMessageBox::errorOk(this, "No files for surface structure " + StructureEnum::toGuiName(surfaceStructure)); return; } std::vector optimizeDataFileTypes; optimizeDataFileTypes.push_back(DataFileTypeEnum::CONNECTIVITY_DENSE); optimizeDataFileTypes.push_back(DataFileTypeEnum::CONNECTIVITY_DENSE_SCALAR); optimizeDataFileTypes.push_back(DataFileTypeEnum::CONNECTIVITY_DENSE_TIME_SERIES); optimizeDataFileTypes.push_back(DataFileTypeEnum::METRIC); std::vector optimizeDataFiles; brain->getAllMappableDataFileWithDataFileTypes(optimizeDataFileTypes, optimizeDataFiles); /* * Create a bool vector that indicates nodes inside border * from the node index. */ const int32_t numberOfSurfaceNodes = surface->getNumberOfNodes(); std::vector nodeInROI(numberOfSurfaceNodes, false); for (std::vector::const_iterator iter = nodesInsideBorder.begin(); iter != nodesInsideBorder.end(); iter++) { const int32_t nodeIndex = *iter; CaretAssertVectorIndex(nodeInROI, nodeIndex); nodeInROI[nodeIndex] = true; } /* * Find borders inside region of interest */ std::map borderToBorderFileMap; std::vector > bordersInsideRegionOfInterest; const int32_t numberOfBorderFiles = brain->getNumberOfBorderFiles(); for (int32_t iFile = 0; iFile < numberOfBorderFiles; iFile++) { BorderFile* borderFile = brain->getBorderFile(iFile); std::vector > nodeCountAndBordersFromFileInROI; borderFile->findBordersInsideRegionOfInterest(displayGroup, browserTabIndex, surface, nodeInROI, nodeCountAndBordersFromFileInROI); for (std::vector > ::iterator bi = nodeCountAndBordersFromFileInROI.begin(); bi != nodeCountAndBordersFromFileInROI.end(); bi++) { Border* border = bi->second; borderToBorderFileMap.insert(std::make_pair(border, borderFile)); bordersInsideRegionOfInterest.push_back(*bi); } } if (bordersInsideRegionOfInterest.empty()) { WuQMessageBox::errorOk(this, "No borders were found inside the drawn border."); return; } std::sort(bordersInsideRegionOfInterest.begin(), bordersInsideRegionOfInterest.end()); if (m_borderOptimizeDialog == NULL) { m_borderOptimizeDialog = new BorderOptimizeDialog(this); } m_borderOptimizeDialog->updateDialog(browserTabIndex, surface, bordersInsideRegionOfInterest, const_cast(borderDrawnByUser), nodesInsideBorder); if (m_borderOptimizeDialog->exec() == BorderOptimizeDialog::Accepted) { std::vector modifiedBorders; m_borderOptimizeDialog->getModifiedBorders(modifiedBorders); /* * Track modified borders so that changes can be 'undone' by * the user. */ std::vector undoBorders; for (std::vector::iterator mbi = modifiedBorders.begin(); mbi != modifiedBorders.end(); mbi++) { Border* border = *mbi; std::map::iterator mapIter = borderToBorderFileMap.find(border); if (mapIter != borderToBorderFileMap.end()) { BorderFile* borderFile = mapIter->second; undoBorders.push_back(BorderFileAndBorderMemento(borderFile, border)); } else { CaretAssertMessage(0, "PROGRAM ERROR: border file not found for border."); } } setLastEditedBorder(undoBorders); if ( ! m_borderOptimizeDialog->isKeepBoundaryBorderSelected()) { this->inputModeBorders->borderBeingDrawnByOpenGL->clear(); } } /* * Update all graphics windows to displayed changed borders */ EventManager::get()->sendEvent(EventUserInterfaceUpdate().getPointer()); adjustViewActionTriggered(); } /** * Called when Adjust View button is pressed. */ void UserInputModeBordersWidget::adjustViewActionTriggered() { EventManager::get()->sendEvent(EventGraphicsUpdateAllWindows().getPointer()); } /** * Called when a draw mode button is clicked. * @param action * Action that was triggered. */ void UserInputModeBordersWidget::drawOperationActionTriggered(QAction* action) { const int drawModeInteger = action->data().toInt(); const UserInputModeBorders::DrawOperation drawOperation = static_cast(drawModeInteger); this->inputModeBorders->setDrawOperation(drawOperation); } /** * @return The edit widget. */ QWidget* UserInputModeBordersWidget::createEditOperationWidget() { const AString deleteToolTipText = ("Delete a border by clicking the mouse over " "any point in the border." + m_transformToolTipText); QAction* deleteAction = WuQtUtilities::createAction("Delete", WuQtUtilities::createWordWrappedToolTipText(deleteToolTipText), this); deleteAction->setCheckable(true); deleteAction->setData(static_cast(UserInputModeBorders::EDIT_OPERATION_DELETE)); QToolButton* deleteToolButton = new QToolButton(); deleteToolButton->setDefaultAction(deleteAction); const AString propertiesToolTipText = ("A dialog for editing a border's properties is displayed by " "clicking any point in a border." + m_transformToolTipText); QAction* propertiesAction = WuQtUtilities::createAction("Properties", WuQtUtilities::createWordWrappedToolTipText(propertiesToolTipText), this); propertiesAction->setCheckable(true); propertiesAction->setData(static_cast(UserInputModeBorders::EDIT_OPERATION_PROPERTIES)); QToolButton* propertiesToolButton = new QToolButton(); propertiesToolButton->setDefaultAction(propertiesAction); this->editOperationActionGroup = new QActionGroup(this); this->editOperationActionGroup->addAction(deleteAction); this->editOperationActionGroup->addAction(propertiesAction); this->editOperationActionGroup->setExclusive(true); QObject::connect(this->editOperationActionGroup, SIGNAL(triggered(QAction*)), this, SLOT(editOperationActionTriggered(QAction*))); QWidget* widget = new QWidget(); QHBoxLayout* layout = new QHBoxLayout(widget); WuQtUtilities::setLayoutSpacingAndMargins(layout, 2, 0); layout->addWidget(deleteToolButton); layout->addWidget(propertiesToolButton); widget->setFixedWidth(widget->sizeHint().width()); return widget; } /** * Called when a edit button is clicked. * @param action * Action that was triggered. */ void UserInputModeBordersWidget::editOperationActionTriggered(QAction* action) { const int editModeInteger = action->data().toInt(); const UserInputModeBorders::EditOperation editOperation = static_cast(editModeInteger); this->inputModeBorders->setEditOperation(editOperation); } /** * @return The ROI widget. */ QWidget* UserInputModeBordersWidget::createRoiOperationWidget() { QWidget* widget = new QWidget(); // QHBoxLayout* layout = new QHBoxLayout(widget); // WuQtUtilities::setLayoutSpacingAndMargins(layout, 2, 0); // // widget->setFixedWidth(widget->sizeHint().width()); return widget; } /** * Called when the user selects a border in ROI opeation. * * @param brain * Brain on which identification occurred. * @param surfaceFile * Surface on which border is located. * @param border * Border for which nodes are found inside. */ void UserInputModeBordersWidget::executeRoiInsideSelectedBorderOperation(Brain* /*brain*/, Surface* surface, Border* border) { if (border->verifyAllPointsOnSameStructure() == false) { WuQMessageBox::errorOk(this, "Error: Border points are on more than one structure."); return; } RegionOfInterestCreateFromBorderDialog createRoiDialog(border, surface, this); createRoiDialog.exec(); } /** * Reset the last edited border. */ void UserInputModeBordersWidget::resetLastEditedBorder() { m_undoFinishBorders.clear(); } /** * Set the last edited border. * * @param undoFinishBorders * Borders that were changed by the last border edit operation. */ void UserInputModeBordersWidget::setLastEditedBorder(std::vector& undoFinishBorders) { m_undoFinishBorders = undoFinishBorders; } connectome-workbench-1.2.3+git41-gc4c6c90/src/GuiQt/UserInputModeBordersWidget.h000066400000000000000000000107731300200146000272060ustar00rootroot00000000000000#ifndef __USER_INPUT_MODE_BORDERS_WIDGET__H_ #define __USER_INPUT_MODE_BORDERS_WIDGET__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 "DisplayGroupEnum.h" #include "EventListenerInterface.h" class QAction; class QActionGroup; class QComboBox; class QStackedWidget; class QToolButton; namespace caret { class BorderFile; class BorderOptimizeDialog; class Border; class Brain; class Surface; class UserInputModeBorders; class UserInputModeBordersWidget : public QWidget, public EventListenerInterface { Q_OBJECT public: UserInputModeBordersWidget(UserInputModeBorders* inputModeBorders, QWidget* parent = 0); virtual ~UserInputModeBordersWidget(); virtual void receiveEvent(Event* event); void updateWidget(); void executeFinishOperation(); void executeRoiInsideSelectedBorderOperation(Brain* brain, Surface* surface, Border* border); private slots: void adjustViewActionTriggered(); void drawOperationActionTriggered(QAction*); void editOperationActionTriggered(QAction*); void modeComboBoxSelection(int); void drawResetButtonClicked(); void drawUndoButtonClicked(); void drawUndoLastEditButtonClicked(); void drawFinishButtonClicked(); private: class BorderFileAndBorderMemento { public: BorderFileAndBorderMemento(BorderFile* borderFile, Border* border) { m_borderFile = borderFile; m_border = border; } BorderFile* m_borderFile; Border* m_border; }; UserInputModeBordersWidget(const UserInputModeBordersWidget&); UserInputModeBordersWidget& operator=(const UserInputModeBordersWidget&); void setActionGroupByActionData(QActionGroup* actionGroup, const int dataInteger); QActionGroup* drawOperationActionGroup; QActionGroup* editOperationActionGroup; QWidget* createModeWidget(); QWidget* createDrawOperationWidget(); QWidget* createEditOperationWidget(); QWidget* createRoiOperationWidget(); void setLastEditedBorder(std::vector& undoFinishBorders); void resetLastEditedBorder(); void processBorderOptimization(const DisplayGroupEnum::Enum displayGroup, const int32_t browserTabIndex, Surface* surface, Border* borderDrawnByUser); QComboBox* modeComboBox; QWidget* widgetMode; QWidget* widgetDrawOperation; QWidget* widgetEditOperation; QWidget* widgetRoiOperation; QStackedWidget* operationStackedWidget; UserInputModeBorders* inputModeBorders; QString m_transformToolTipText; QToolButton* m_undoFinishToolButton; BorderOptimizeDialog* m_borderOptimizeDialog; std::vector m_undoFinishBorders; }; #ifdef __USER_INPUT_MODE_BORDERS_WIDGET_DECLARE__ // #endif // __USER_INPUT_MODE_BORDERS_WIDGET_DECLARE__ } // namespace #endif //__USER_INPUT_MODE_BORDERS_WIDGET__H_ connectome-workbench-1.2.3+git41-gc4c6c90/src/GuiQt/UserInputModeFoci.cxx000066400000000000000000000250271300200146000256730ustar00rootroot00000000000000 /*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 __USER_INPUT_MODE_FOCI_DECLARE__ #include "UserInputModeFoci.h" #undef __USER_INPUT_MODE_FOCI_DECLARE__ #include "Brain.h" #include "BrainOpenGLViewportContent.h" #include "BrainOpenGLWidget.h" #include "BrainStructure.h" #include "BrowserTabContent.h" #include "CaretAssert.h" #include "EventGraphicsUpdateAllWindows.h" #include "EventGraphicsUpdateOneWindow.h" #include "EventManager.h" #include "EventUserInterfaceUpdate.h" #include "FociFile.h" #include "FociPropertiesEditorDialog.h" #include "Focus.h" #include "GuiManager.h" #include "SelectionItemFocusSurface.h" #include "SelectionItemFocusVolume.h" #include "SelectionItemSurfaceNode.h" #include "SelectionItemVoxel.h" #include "SelectionManager.h" #include "MouseEvent.h" #include "Surface.h" #include "UserInputModeFociWidget.h" #include "UserInputModeView.h" #include "VolumeFile.h" using namespace caret; /** * \class caret::UserInputModeFoci * \brief Processes user input for foci. */ /** * Constructor. */ UserInputModeFoci::UserInputModeFoci(const int32_t windowIndex) : UserInputModeView(UserInputModeAbstract::FOCI), m_windowIndex(windowIndex) { m_inputModeFociWidget = new UserInputModeFociWidget(this, windowIndex); m_mode = MODE_CREATE; m_editOperation = EDIT_OPERATION_PROPERTIES; setWidgetForToolBar(m_inputModeFociWidget); } /** * Destructor. */ UserInputModeFoci::~UserInputModeFoci() { } /** * @return the mode. */ UserInputModeFoci::Mode UserInputModeFoci::getMode() const { return m_mode; } /** * Set the mode. * @param mode * New value for mode. */ void UserInputModeFoci::setMode(const Mode mode) { if (m_mode != mode) { m_mode = mode; EventManager::get()->sendEvent(EventGraphicsUpdateOneWindow(m_windowIndex).getPointer()); } this->m_inputModeFociWidget->updateWidget(); } /** * @return The edit operation. */ UserInputModeFoci::EditOperation UserInputModeFoci::getEditOperation() const { return m_editOperation; } /** * Set the edit operation. * @param editOperation * New edit operation. */ void UserInputModeFoci::setEditOperation(const EditOperation editOperation) { m_editOperation = editOperation; } /** * Called when 'this' user input receiver is set * to receive events. */ void UserInputModeFoci::initialize() { m_inputModeFociWidget->updateWidget(); } /** * Called when 'this' user input receiver is no * longer set to receive events. */ void UserInputModeFoci::finish() { } /** * Called to update the input receiver for various events. */ void UserInputModeFoci::update() { } /** * @return The cursor for display in the OpenGL widget. */ CursorEnum::Enum UserInputModeFoci::getCursor() const { CursorEnum::Enum cursor = CursorEnum::CURSOR_DEFAULT; switch (m_mode) { case MODE_CREATE: break; case MODE_EDIT: cursor = CursorEnum::CURSOR_POINTING_HAND; switch (m_editOperation) { case EDIT_OPERATION_DELETE: cursor = CursorEnum::CURSOR_CROSS; break; case EDIT_OPERATION_PROPERTIES: cursor = CursorEnum::CURSOR_WHATS_THIS; break; } break; case MODE_OPERATIONS: cursor = CursorEnum::CURSOR_POINTING_HAND; break; } return cursor; } void UserInputModeFoci::updateAfterFociChanged() { /* * Need to update all graphics windows and all border controllers. */ EventManager::get()->sendEvent(EventGraphicsUpdateAllWindows().getPointer()); EventManager::get()->sendEvent(EventUserInterfaceUpdate().addFoci().getPointer()); } /** * Determine the structure for the given surface. Find the primary anatomical * surface for the structure and return it. If no primary anatomical surface * is found, the return the surface that was passed in. */ Surface* UserInputModeFoci::getAnatomicalSurfaceForSurface(Surface* surface) { Brain* brain = GuiManager::get()->getBrain(); const StructureEnum::Enum structure = surface->getStructure(); BrainStructure* bs = brain->getBrainStructure(structure, false); Surface* anatSurf = bs->getPrimaryAnatomicalSurface(); if (anatSurf != NULL) { return anatSurf; } return surface; } /** * Process a mouse left click event. * * @param mouseEvent * Mouse event information. */ void UserInputModeFoci::mouseLeftClick(const MouseEvent& mouseEvent) { BrainOpenGLViewportContent* viewportContent = mouseEvent.getViewportContent(); if (viewportContent == NULL) { return; } BrainOpenGLWidget* openGLWidget = mouseEvent.getOpenGLWidget(); BrowserTabContent* browserTabContent = viewportContent->getBrowserTabContent(); SelectionManager* idManager = openGLWidget->performIdentification(mouseEvent.getX(), mouseEvent.getY(), true); switch (m_mode) { case MODE_CREATE: { SelectionItemSurfaceNode* idNode = idManager->getSurfaceNodeIdentification(); SelectionItemVoxel* idVoxel = idManager->getVoxelIdentification(); if (idNode->isValid()) { Surface* surfaceViewed = idNode->getSurface(); CaretAssert(surfaceViewed); const Surface* anatSurface = getAnatomicalSurfaceForSurface(surfaceViewed); const StructureEnum::Enum anatStructure = anatSurface->getStructure(); const int32_t nodeIndex = idNode->getNodeNumber(); const AString focusName = (StructureEnum::toGuiName(anatStructure) + " Vertex " + AString::number(nodeIndex)); const float* xyz = anatSurface->getCoordinate(nodeIndex); const AString comment = ("Created from " + focusName); Focus* focus = new Focus(); focus->setName(focusName); focus->getProjection(0)->setStereotaxicXYZ(xyz); focus->setComment(comment); FociPropertiesEditorDialog::createFocus(focus, browserTabContent, m_inputModeFociWidget); } else if (idVoxel->isValid()) { const VolumeMappableInterface* vf = idVoxel->getVolumeFile(); const CaretMappableDataFile* cmdf = dynamic_cast(vf); int64_t ijk[3]; idVoxel->getVoxelIJK(ijk); float xyz[3]; vf->indexToSpace(ijk, xyz); const AString focusName = (cmdf->getFileNameNoPath() + " IJK (" + AString::fromNumbers(ijk, 3, ",") + ")"); const AString comment = ("Created from " + focusName); Focus* focus = new Focus(); focus->setName(focusName); focus->getProjection(0)->setStereotaxicXYZ(xyz); focus->setComment(comment); FociPropertiesEditorDialog::createFocus(focus, browserTabContent, m_inputModeFociWidget); } } break; case MODE_EDIT: { FociFile* fociFile = NULL; Focus* focus = NULL; SelectionItemFocusVolume* idVolFocus = idManager->getVolumeFocusIdentification(); if (idVolFocus->isValid()) { fociFile = idVolFocus->getFociFile(); CaretAssert(fociFile); focus = idVolFocus->getFocus(); CaretAssert(focus); } SelectionItemFocusSurface* idFocus = idManager->getSurfaceFocusIdentification(); if (idFocus->isValid()) { fociFile = idFocus->getFociFile(); CaretAssert(fociFile); focus = idFocus->getFocus(); CaretAssert(focus); } if ((fociFile != NULL) && (focus != NULL)) { switch (m_editOperation) { case EDIT_OPERATION_DELETE: fociFile->removeFocus(focus); updateAfterFociChanged(); break; case EDIT_OPERATION_PROPERTIES: { FociPropertiesEditorDialog::editFocus(fociFile, focus, openGLWidget); } } } } break; case MODE_OPERATIONS: break; } } /** * Show a context menu (pop-up menu at mouse location) * * @param mouseEvent * Mouse event information. * @param menuPosition * Point at which menu is displayed (passed to QMenu::exec()) * @param openGLWidget * OpenGL widget in which context menu is requested */ void UserInputModeFoci::showContextMenu(const MouseEvent& /*mouseEvent*/, const QPoint& /*menuPosition*/, BrainOpenGLWidget* /*openGLWidget*/) { /* no context menu */ } connectome-workbench-1.2.3+git41-gc4c6c90/src/GuiQt/UserInputModeFoci.h000066400000000000000000000060541300200146000253170ustar00rootroot00000000000000#ifndef __USER_INPUT_MODE_FOCI__H_ #define __USER_INPUT_MODE_FOCI__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 "UserInputModeView.h" namespace caret { class BrainOpenGLViewportContent; class BrainOpenGLWidget; class MouseEvent; class Surface; class UserInputModeFociWidget; class UserInputModeFoci : public UserInputModeView { public: enum Mode { MODE_CREATE, MODE_EDIT, MODE_OPERATIONS }; enum EditOperation { EDIT_OPERATION_DELETE, EDIT_OPERATION_PROPERTIES }; UserInputModeFoci(const int32_t windowIndex); virtual ~UserInputModeFoci(); virtual void initialize(); virtual void finish(); virtual void update(); virtual CursorEnum::Enum getCursor() const; virtual void mouseLeftClick(const MouseEvent& mouseEvent); virtual void showContextMenu(const MouseEvent& mouseEvent, const QPoint& menuPosition, BrainOpenGLWidget* openGLWidget); private: /* * Note some private methods are accessed by the * friend UserInputModeFociWidget. */ friend class UserInputModeFociWidget; UserInputModeFoci(const UserInputModeFoci&); UserInputModeFoci& operator=(const UserInputModeFoci&); Mode getMode() const; void setMode(const Mode mode); EditOperation getEditOperation() const; void setEditOperation(const EditOperation editOperation); void updateAfterFociChanged(); Surface* getAnatomicalSurfaceForSurface(Surface* surface); // ADD_NEW_MEMBERS_HERE const int32_t m_windowIndex; UserInputModeFociWidget* m_inputModeFociWidget; Mode m_mode; EditOperation m_editOperation; }; #ifdef __USER_INPUT_MODE_FOCI_DECLARE__ // #endif // __USER_INPUT_MODE_FOCI_DECLARE__ } // namespace #endif //__USER_INPUT_MODE_FOCI__H_ connectome-workbench-1.2.3+git41-gc4c6c90/src/GuiQt/UserInputModeFociWidget.cxx000066400000000000000000000427131300200146000270400ustar00rootroot00000000000000 /*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 __USER_INPUT_MODE_FOCI_WIDGET_DECLARE__ #include "UserInputModeFociWidget.h" #undef __USER_INPUT_MODE_FOCI_WIDGET_DECLARE__ #include "Brain.h" #include "BrainBrowserWindow.h" #include "BrowserTabContent.h" #include "CaretAssert.h" #include "DisplayPropertiesFoci.h" #include "EventGraphicsUpdateAllWindows.h" #include "EventManager.h" #include "EventUserInterfaceUpdate.h" #include "FociFile.h" #include "FociPropertiesEditorDialog.h" #include "Focus.h" #include "GuiManager.h" #include "SelectionManager.h" #include "SelectionItemSurfaceNode.h" #include "SelectionItemVoxel.h" #include "ModelSurface.h" #include "ModelWholeBrain.h" #include "Surface.h" #include "UserInputModeFoci.h" #include "VolumeFile.h" #include "WuQMessageBox.h" #include "WuQtUtilities.h" using namespace caret; /** * \class caret::UserInputModeFociWidget * \brief Foci controls shown at bottom of toolbar */ /** * Constructor. * @param inputModeFoci * Process of mouse input for foci * @param windowIndex * Index of browser window * @param parent * Parent widget */ UserInputModeFociWidget::UserInputModeFociWidget(UserInputModeFoci* inputModeFoci, const int32_t windowIndex, QWidget* parent) : QWidget(parent), m_windowIndex(windowIndex) { m_transformToolTipText = ("\n\n" "At any time, the view of the surface may be changed by\n" " PAN: Move the mouse with the left mouse button down while " "holding down the Shift key.\n" " ROTATE: Move the mouse with the left mouse button down.\n" " ZOOM: Move the mouse with the left mouse button down while " "holding down the Ctrl key (Apple key on Macs)." ); m_inputModeFoci = inputModeFoci; QLabel* nameLabel = new QLabel("Foci "); QWidget* modeWidget = createModeWidget(); m_createOperationWidget = createCreateOperationWidget(); m_editOperationWidget = createEditOperationWidget(); m_taskOperationWidget = createTaskOperationWidget(); m_operationStackedWidget = new QStackedWidget(); m_operationStackedWidget->addWidget(m_createOperationWidget); m_operationStackedWidget->addWidget(m_editOperationWidget); //m_operationStackedWidget->addWidget(m_taskOperationWidget); QHBoxLayout* layout = new QHBoxLayout(this); WuQtUtilities::setLayoutSpacingAndMargins(layout, 0, 0); layout->addWidget(nameLabel); layout->addWidget(modeWidget); layout->addSpacing(10); layout->addWidget(m_operationStackedWidget); layout->addStretch(); } /** * Destructor. */ UserInputModeFociWidget::~UserInputModeFociWidget() { } /** * Update the contents of the widget. */ void UserInputModeFociWidget::updateWidget() { /* * Show the proper widget */ switch (m_inputModeFoci->getMode()) { case UserInputModeFoci::MODE_CREATE: m_operationStackedWidget->setCurrentWidget(m_createOperationWidget); // setActionGroupByActionData(m_createOperationActionGroup, // m_inputModeFoci->getCreateOperation()); break; case UserInputModeFoci::MODE_EDIT: m_operationStackedWidget->setCurrentWidget(m_editOperationWidget); setActionGroupByActionData(m_editOperationActionGroup, m_inputModeFoci->getEditOperation()); break; case UserInputModeFoci::MODE_OPERATIONS: m_operationStackedWidget->setCurrentWidget(m_taskOperationWidget); break; } const int selectedModeInteger = (int)m_inputModeFoci->getMode(); const int modeComboBoxIndex = m_modeComboBox->findData(selectedModeInteger); CaretAssert(modeComboBoxIndex >= 0); m_modeComboBox->blockSignals(true); m_modeComboBox->setCurrentIndex(modeComboBoxIndex); m_modeComboBox->blockSignals(false); } /** * Set the action with its data value of the given integer * as the active action. * @param actionGroup * Action group for which action is selected. * @param dataInteger * Integer value for data attribute. */ void UserInputModeFociWidget::setActionGroupByActionData(QActionGroup* actionGroup, const int dataInteger) { actionGroup->blockSignals(true); const QList actionList = actionGroup->actions(); QListIterator iter(actionList); while (iter.hasNext()) { QAction* action = iter.next(); const int actionDataInteger = action->data().toInt(); if (dataInteger == actionDataInteger) { action->setChecked(true); break; } } actionGroup->blockSignals(false); } /** * @return The mode widget. */ QWidget* UserInputModeFociWidget::createModeWidget() { m_modeComboBox = new QComboBox(); m_modeComboBox->addItem("Create", (int)UserInputModeFoci::MODE_CREATE); m_modeComboBox->addItem("Edit", (int)UserInputModeFoci::MODE_EDIT); // m_modeComboBox->addItem("Tasks", (int)UserInputModeFoci::MODE_OPERATIONS); QObject::connect(m_modeComboBox, SIGNAL(currentIndexChanged(int)), this, SLOT(modeComboBoxSelection(int))); QWidget* widget = new QWidget(); QHBoxLayout* layout = new QHBoxLayout(widget); WuQtUtilities::setLayoutSpacingAndMargins(layout, 2, 0); layout->addWidget(m_modeComboBox); widget->setFixedWidth(widget->sizeHint().width()); return widget; } /** * Called when a mode is selected from the mode combo box. * @param indx * Index of item selected. */ void UserInputModeFociWidget::modeComboBoxSelection(int indx) { const int modeInteger = m_modeComboBox->itemData(indx).toInt(); const UserInputModeFoci::Mode mode = (UserInputModeFoci::Mode)modeInteger; m_inputModeFoci->setMode(mode); } /** * @return The draw operation widget. */ QWidget* UserInputModeFociWidget::createCreateOperationWidget() { const AString newToolTipText = ("Press this button to display a dialog for creating a new focus. " "If the mouse is clicked over a model, the dialog for creating a focus is " "displayed with the focus' coordinates set to the stereotaxic coordinates at " "the location of the mouse click." + m_transformToolTipText); QAction* newFocusAction = WuQtUtilities::createAction("New...", WuQtUtilities::createWordWrappedToolTipText(newToolTipText), this, this, SLOT(createNewFocusActionTriggered())); QToolButton* newFocusToolButton = new QToolButton(); newFocusToolButton->setDefaultAction(newFocusAction); 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 " "last identification operation. While in focus mode, an identification " "is performed by holding down the Shift key and clicking the mouse." + m_transformToolTipText); QAction* lastIdFocusAction = WuQtUtilities::createAction("Last ID", WuQtUtilities::createWordWrappedToolTipText(lastIDToolTipText), this, this, SLOT(createLastIdentificationFocusActionTriggered())); QToolButton* lastIdFocusToolButton = new QToolButton(); lastIdFocusToolButton->setDefaultAction(lastIdFocusAction); QWidget* widget = new QWidget(); QHBoxLayout* layout = new QHBoxLayout(widget); WuQtUtilities::setLayoutSpacingAndMargins(layout, 2, 0); layout->addWidget(newFocusToolButton); layout->addSpacing(5); layout->addWidget(lastIdFocusToolButton); widget->setFixedWidth(widget->sizeHint().width()); return widget; } /** * Called when new focus button is triggered * @param action * Action that was selected. */ void UserInputModeFociWidget::createNewFocusActionTriggered() { BrainBrowserWindow* browserWindow = GuiManager::get()->getBrowserWindowByWindowIndex(m_windowIndex); if (browserWindow == NULL) { return; } BrowserTabContent* btc = browserWindow->getBrowserTabContent(); if (btc == NULL) { return; } const int32_t browserTabIndex = btc->getTabNumber(); DisplayPropertiesFoci* dpf = GuiManager::get()->getBrain()->getDisplayPropertiesFoci(); const DisplayGroupEnum::Enum displayGroup = dpf->getDisplayGroupForTab(btc->getTabNumber()); dpf->setDisplayed(displayGroup, browserTabIndex, true); FociPropertiesEditorDialog::createFocus(new Focus(), btc, this); } /** * Called when last ID focus button is triggered * @param action * Action that was selected. */ void UserInputModeFociWidget::createLastIdentificationFocusActionTriggered() { Brain* brain = GuiManager::get()->getBrain(); const SelectionManager* idManager = brain->getSelectionManager(); const SelectionItem* idItem = idManager->getLastSelectedItem(); if (idItem != NULL) { const SelectionItemSurfaceNode* nodeID = dynamic_cast(idItem); const SelectionItemVoxel* voxelID = dynamic_cast(idItem); BrainBrowserWindow* browserWindow = GuiManager::get()->getBrowserWindowByWindowIndex(m_windowIndex); BrowserTabContent* browserTabContent = NULL; if (browserWindow != NULL) { browserTabContent = browserWindow->getBrowserTabContent(); } if (nodeID != NULL) { if (nodeID->isValid()) { const Surface* idSurface = nodeID->getSurface(); if (brain->isFileValid(idSurface)) { CaretAssert(idSurface); const StructureEnum::Enum structure = idSurface->getStructure(); const Surface* surface = brain->getPrimaryAnatomicalSurfaceForStructure(structure); if (surface != NULL) { const int32_t nodeIndex = nodeID->getNodeNumber(); const AString focusName = ("Last ID " + StructureEnum::toGuiName(structure) + " Node " + AString::number(nodeIndex)); const float* xyz = surface->getCoordinate(nodeIndex); const AString comment = ("Created from " + focusName); Focus* focus = new Focus(); focus->setName(focusName); focus->getProjection(0)->setStereotaxicXYZ(xyz); focus->setComment(comment); FociPropertiesEditorDialog::createFocus(focus, browserTabContent, this); } else { WuQMessageBox::errorOk(this, ("No anatomical surface found for " + StructureEnum::toGuiName(structure))); } } } } else if (voxelID != NULL) { if (voxelID->isValid()) { const VolumeMappableInterface* volumeFile = voxelID->getVolumeFile(); const CaretMappableDataFile* cmdf = dynamic_cast(volumeFile); if (brain->isFileValid(cmdf)) { CaretAssert(volumeFile); int64_t ijk[3]; voxelID->getVoxelIJK(ijk); float xyz[3]; volumeFile->indexToSpace(ijk, xyz); const AString focusName = ("Last ID " + cmdf->getFileNameNoPath() + " IJK (" + AString::fromNumbers(ijk, 3, ",") + ")"); const AString comment = ("Created from " + focusName); Focus* focus = new Focus(); focus->setName(focusName); focus->getProjection(0)->setStereotaxicXYZ(xyz); focus->setComment(comment); FociPropertiesEditorDialog::createFocus(focus, browserTabContent, this); } } } } } /** * @return The edit widget. */ QWidget* UserInputModeFociWidget::createEditOperationWidget() { const AString deleteToolTipText = ("Delete a focus by clicking the mouse over the focus." + m_transformToolTipText); QAction* deleteAction = WuQtUtilities::createAction("Delete", WuQtUtilities::createWordWrappedToolTipText(deleteToolTipText), this); deleteAction->setCheckable(true); deleteAction->setData(static_cast(UserInputModeFoci::EDIT_OPERATION_DELETE)); QToolButton* deleteToolButton = new QToolButton(); deleteToolButton->setDefaultAction(deleteAction); const AString propertiesToolTipText = ("Click the mouse over a focus to display a dialog " "for editing the focus' properties." + m_transformToolTipText); QAction* propertiesAction = WuQtUtilities::createAction("Properties", WuQtUtilities::createWordWrappedToolTipText(propertiesToolTipText), this); propertiesAction->setCheckable(true); propertiesAction->setData(static_cast(UserInputModeFoci::EDIT_OPERATION_PROPERTIES)); QToolButton* propertiesToolButton = new QToolButton(); propertiesToolButton->setDefaultAction(propertiesAction); m_editOperationActionGroup = new QActionGroup(this); m_editOperationActionGroup->addAction(deleteAction); m_editOperationActionGroup->addAction(propertiesAction); m_editOperationActionGroup->setExclusive(true); QObject::connect(m_editOperationActionGroup, SIGNAL(triggered(QAction*)), this, SLOT(editOperationActionTriggered(QAction*))); QWidget* widget = new QWidget(); QHBoxLayout* layout = new QHBoxLayout(widget); WuQtUtilities::setLayoutSpacingAndMargins(layout, 2, 0); layout->addWidget(deleteToolButton); layout->addWidget(propertiesToolButton); widget->setFixedWidth(widget->sizeHint().width()); return widget; } /** * @return The task operation widget. */ QWidget* UserInputModeFociWidget::createTaskOperationWidget() { QWidget* widget = new QWidget(); QHBoxLayout* layout = new QHBoxLayout(widget); WuQtUtilities::setLayoutSpacingAndMargins(layout, 2, 0); widget->setFixedWidth(widget->sizeHint().width()); return widget; } /** * Called when an edit operation button is selected. * @param action * Action that was selected. */ void UserInputModeFociWidget::editOperationActionTriggered(QAction* action) { const int editModeInteger = action->data().toInt(); const UserInputModeFoci::EditOperation editOperation = static_cast(editModeInteger); m_inputModeFoci->setEditOperation(editOperation); } connectome-workbench-1.2.3+git41-gc4c6c90/src/GuiQt/UserInputModeFociWidget.h000066400000000000000000000057341300200146000264670ustar00rootroot00000000000000#ifndef __USER_INPUT_MODE_FOCI_WIDGET__H_ #define __USER_INPUT_MODE_FOCI_WIDGET__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" class QAction; class QActionGroup; class QComboBox; class QStackedWidget; namespace caret { class Focus; class FociFile; class UserInputModeFoci; class UserInputModeFociWidget : public QWidget { Q_OBJECT public: UserInputModeFociWidget(UserInputModeFoci* inputModeFoci, const int32_t windowIndex, QWidget* parent = 0); virtual ~UserInputModeFociWidget(); void updateWidget(); // ADD_NEW_METHODS_HERE private slots: void createNewFocusActionTriggered(); void createLastIdentificationFocusActionTriggered(); void editOperationActionTriggered(QAction*); void modeComboBoxSelection(int); private: UserInputModeFociWidget(const UserInputModeFociWidget&); UserInputModeFociWidget& operator=(const UserInputModeFociWidget&); QWidget* createModeWidget(); QWidget* createCreateOperationWidget(); QWidget* createEditOperationWidget(); QWidget* createTaskOperationWidget(); void setActionGroupByActionData(QActionGroup* actionGroup, const int dataInteger); // ADD_NEW_MEMBERS_HERE UserInputModeFoci* m_inputModeFoci; const int32_t m_windowIndex; QComboBox* m_modeComboBox; QActionGroup* m_editOperationActionGroup; QWidget* m_createOperationWidget; QWidget* m_editOperationWidget; QWidget* m_taskOperationWidget; QStackedWidget* m_operationStackedWidget; QString m_transformToolTipText; friend class UserInputModeFoci; }; #ifdef __USER_INPUT_MODE_FOCI_WIDGET_DECLARE__ #endif // __USER_INPUT_MODE_FOCI_WIDGET_DECLARE__ } // namespace #endif //__USER_INPUT_MODE_FOCI_WIDGET__H_ connectome-workbench-1.2.3+git41-gc4c6c90/src/GuiQt/UserInputModeImage.cxx000066400000000000000000000234541300200146000260370ustar00rootroot00000000000000 /*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 __USER_INPUT_MODE_IMAGE_DECLARE__ #include "UserInputModeImage.h" #undef __USER_INPUT_MODE_IMAGE_DECLARE__ #include #include "Brain.h" #include "BrainOpenGLViewportContent.h" #include "BrainOpenGLWidget.h" #include "BrowserTabContent.h" #include "CaretAssert.h" #include "ControlPoint3D.h" #include "ControlPointFile.h" #include "DisplayPropertiesImages.h" #include "EventBrowserWindowContentGet.h" #include "EventGraphicsUpdateAllWindows.h" #include "EventGraphicsUpdateOneWindow.h" #include "EventManager.h" #include "EventUserInterfaceUpdate.h" #include "GuiManager.h" #include "ImageFile.h" #include "SelectionItemImage.h" #include "SelectionItemImageControlPoint.h" #include "SelectionItemVoxel.h" #include "SelectionManager.h" #include "MouseEvent.h" #include "UserInputModeImageWidget.h" #include "UserInputModeView.h" #include "VolumeFile.h" #include "WuQTimedMessageDisplay.h" using namespace caret; /** * \class caret::UserInputModeImage * \brief Processes user input for images. */ /** * Constructor. */ UserInputModeImage::UserInputModeImage(const int32_t windowIndex) : UserInputModeView(UserInputModeAbstract::IMAGE), m_windowIndex(windowIndex) { m_inputModeImageWidget = new UserInputModeImageWidget(this, windowIndex); m_editOperation = EDIT_OPERATION_ADD; setWidgetForToolBar(m_inputModeImageWidget); } /** * Destructor. */ UserInputModeImage::~UserInputModeImage() { } /** * @return The edit operation. */ UserInputModeImage::EditOperation UserInputModeImage::getEditOperation() const { return m_editOperation; } /** * Set the edit operation. * @param editOperation * New edit operation. */ void UserInputModeImage::setEditOperation(const EditOperation editOperation) { m_editOperation = editOperation; } /** * Called when 'this' user input receiver is set * to receive events. */ void UserInputModeImage::initialize() { m_inputModeImageWidget->updateWidget(); } /** * Called when 'this' user input receiver is no * longer set to receive events. */ void UserInputModeImage::finish() { } /** * Called to update the input receiver for various events. */ void UserInputModeImage::update() { } /** * @return The cursor for display in the OpenGL widget. */ CursorEnum::Enum UserInputModeImage::getCursor() const { CursorEnum::Enum cursor = CursorEnum::CURSOR_DEFAULT; cursor = CursorEnum::CURSOR_POINTING_HAND; switch (m_editOperation) { case EDIT_OPERATION_ADD: cursor = CursorEnum::CURSOR_DEFAULT; break; case EDIT_OPERATION_DELETE: cursor = CursorEnum::CURSOR_CROSS; break; } return cursor; } /** * Updates after any changes to control points */ void UserInputModeImage::updateAfterControlPointsChanged() { /* * Need to update all graphics windows and all border controllers. */ EventManager::get()->sendEvent(EventGraphicsUpdateAllWindows().getPointer()); EventManager::get()->sendEvent(EventUserInterfaceUpdate().getPointer()); } /** * Process a mouse left click event. * * @param mouseEvent * Mouse event information. */ void UserInputModeImage::mouseLeftClick(const MouseEvent& mouseEvent) { BrainOpenGLViewportContent* viewportContent = mouseEvent.getViewportContent(); if (viewportContent == NULL) { return; } BrainOpenGLWidget* openGLWidget = mouseEvent.getOpenGLWidget(); //BrowserTabContent* browserTabContent = viewportContent->getBrowserTabContent(); SelectionManager* idManager = openGLWidget->performIdentification(mouseEvent.getX(), mouseEvent.getY(), true); SelectionItemImage* idImage = idManager->getImageIdentification(); CaretAssert(idImage); SelectionItemVoxel* idVoxel = idManager->getVoxelIdentification(); CaretAssert(idVoxel); SelectionItemImageControlPoint* idImageControlPoint = idManager->getImageControlPointIdentification(); CaretAssert(idImageControlPoint); AString toolTipMessage; switch (m_editOperation) { case EDIT_OPERATION_ADD: if (idImage->isValid() && idVoxel->isValid()) { addControlPoint(idImage, idVoxel); } else if (idImage->isValid()) { toolTipMessage = "Mouse click is over image but must also be over volume slice"; } else if (idVoxel->isValid()) { toolTipMessage = "Mouse click is over volume slice but must also be over an image"; } else { toolTipMessage = "Mouse click must be over both an image and a volume slice"; } break; case EDIT_OPERATION_DELETE: if (idImageControlPoint->isValid()) { deleteControlPoint(idImageControlPoint); } else { toolTipMessage = "Mouse click must be over an image control point"; } break; } updateAfterControlPointsChanged(); if ( ! toolTipMessage.isEmpty()) { WuQTimedMessageDisplay::show(openGLWidget, mouseEvent.getX(), mouseEvent.getY(), 2, toolTipMessage); } } /** * Create a control point for the given image and voxel coordinates. * * @param imageSelection * The image selection. * @param voxelSelection * The voxel selection. */ void UserInputModeImage::addControlPoint(SelectionItemImage* imageSelection, const SelectionItemVoxel* voxelSelection) { ImageFile* imageFile = imageSelection->getImageFile(); CaretAssert(imageFile); ControlPointFile* controlPointFile = imageFile->getControlPointFile(); CaretAssert(controlPointFile); const float pixelX = imageSelection->getPixelI(); const float pixelY = imageSelection->getPixelJ(); const float pixelZ = 0.0; double voxelXYZ[3] = { 0.0, 0.0, 0.0 }; voxelSelection->getModelXYZ(voxelXYZ); controlPointFile->addControlPoint(ControlPoint3D(pixelX, pixelY, pixelZ, voxelXYZ[0], voxelXYZ[1], voxelXYZ[2])); } /** * Delete the selection control point. * * @param idImageControlPoint * Control point identification. */ void UserInputModeImage::deleteControlPoint(SelectionItemImageControlPoint* idImageControlPoint) { ControlPointFile* controlPointFile = idImageControlPoint->getControlPointFile(); CaretAssert(controlPointFile); const int32_t controlPointIndex = idImageControlPoint->getControlPointIndexInFile(); CaretAssert(controlPointIndex >= 0); controlPointFile->removeControlPointAtIndex(controlPointIndex); } /** * Delete all control points */ void UserInputModeImage::deleteAllControlPoints() { ImageFile* imageFile = getImageFile(); if (imageFile != NULL) { imageFile->getControlPointFile()->removeAllControlPoints(); } updateAfterControlPointsChanged(); } /** * @return The selected image file in the window (NULL if not valid) */ ImageFile* UserInputModeImage::getImageFile() const { EventBrowserWindowContentGet windowGet(m_windowIndex); EventManager::get()->sendEvent(windowGet.getPointer()); DisplayPropertiesImages* dpi = GuiManager::get()->getBrain()->getDisplayPropertiesImages(); BrowserTabContent* tabContent = windowGet.getSelectedBrowserTabContent(); if (tabContent == NULL) { return NULL; } const DisplayGroupEnum::Enum displayGroup = dpi->getDisplayGroupForTab(tabContent->getTabNumber()); ImageFile* imageFile = dpi->getSelectedImageFile(displayGroup, tabContent->getTabNumber()); return imageFile; } /** * @return The tab index (negative if invalid). */ int32_t UserInputModeImage::getTabIndex() const { int32_t tabIndex = -1; EventBrowserWindowContentGet windowGet(m_windowIndex); EventManager::get()->sendEvent(windowGet.getPointer()); //DisplayPropertiesImages* dpi = GuiManager::get()->getBrain()->getDisplayPropertiesImages(); BrowserTabContent* tabContent = windowGet.getSelectedBrowserTabContent(); if (tabContent != NULL) { tabIndex = tabContent->getTabNumber(); } return tabIndex; } /** * Show a context menu (pop-up menu at mouse location) * * @param mouseEvent * Mouse event information. * @param menuPosition * Point at which menu is displayed (passed to QMenu::exec()) * @param openGLWidget * OpenGL widget in which context menu is requested */ void UserInputModeImage::showContextMenu(const MouseEvent& /*mouseEvent*/, const QPoint& /*menuPosition*/, BrainOpenGLWidget* /*openGLWidget*/) { /* no context menu */ } connectome-workbench-1.2.3+git41-gc4c6c90/src/GuiQt/UserInputModeImage.h000066400000000000000000000062321300200146000254570ustar00rootroot00000000000000#ifndef __USER_INPUT_MODE_IMAGE__H_ #define __USER_INPUT_MODE_IMAGE__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 "UserInputModeView.h" namespace caret { class ImageFile; class SelectionItemImage; class SelectionItemImageControlPoint; class SelectionItemVoxel; class UserInputModeImageWidget; class UserInputModeImage : public UserInputModeView { public: enum EditOperation { EDIT_OPERATION_ADD, EDIT_OPERATION_DELETE }; UserInputModeImage(const int32_t windowIndex); virtual ~UserInputModeImage(); virtual void initialize(); virtual void finish(); virtual void update(); virtual CursorEnum::Enum getCursor() const; virtual void mouseLeftClick(const MouseEvent& mouseEvent); virtual void showContextMenu(const MouseEvent& mouseEvent, const QPoint& menuPosition, BrainOpenGLWidget* openGLWidget); private: /* * Note some private methods are accessed by the * friend UserInputModeImageWidget. */ friend class UserInputModeImageWidget; UserInputModeImage(const UserInputModeImage&); UserInputModeImage& operator=(const UserInputModeImage&); EditOperation getEditOperation() const; void setEditOperation(const EditOperation editOperation); void updateAfterControlPointsChanged(); void addControlPoint(SelectionItemImage* imageSelection, const SelectionItemVoxel* voxelSelection); void deleteControlPoint(SelectionItemImageControlPoint* idImageControlPoint); void deleteAllControlPoints(); ImageFile* getImageFile() const; int32_t getTabIndex() const; // ADD_NEW_MEMBERS_HERE const int32_t m_windowIndex; UserInputModeImageWidget* m_inputModeImageWidget; EditOperation m_editOperation; }; #ifdef __USER_INPUT_MODE_IMAGE_DECLARE__ // #endif // __USER_INPUT_MODE_IMAGE_DECLARE__ } // namespace #endif //__USER_INPUT_MODE_IMAGE__H_ connectome-workbench-1.2.3+git41-gc4c6c90/src/GuiQt/UserInputModeImageWidget.cxx000066400000000000000000000234751300200146000272060ustar00rootroot00000000000000 /*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 __USER_INPUT_MODE_IMAGE_WIDGET_DECLARE__ #include "UserInputModeImageWidget.h" #undef __USER_INPUT_MODE_IMAGE_WIDGET_DECLARE__ #include "Brain.h" #include "BrainBrowserWindow.h" #include "BrowserTabContent.h" #include "CaretAssert.h" #include "ControlPointFile.h" #include "DisplayPropertiesFoci.h" #include "EventGraphicsUpdateAllWindows.h" #include "EventManager.h" #include "EventUserInterfaceUpdate.h" #include "FociFile.h" #include "FociPropertiesEditorDialog.h" #include "Focus.h" #include "GuiManager.h" #include "ImageFile.h" #include "ImageFileConvertToVolumeFileDialog.h" #include "Matrix4x4.h" #include "SelectionManager.h" #include "SelectionItemSurfaceNode.h" #include "SelectionItemVoxel.h" #include "ModelSurface.h" #include "ModelWholeBrain.h" #include "Surface.h" #include "UserInputModeImage.h" #include "VolumeFile.h" #include "WuQMessageBox.h" #include "WuQtUtilities.h" using namespace caret; /** * \class caret::UserInputModeImageWidget * \brief Foci controls shown at bottom of toolbar */ /** * Constructor. * @param inputModeImage * Process of mouse input for image * @param windowIndex * Index of browser window * @param parent * Parent widget */ UserInputModeImageWidget::UserInputModeImageWidget(UserInputModeImage* inputModeImage, const int32_t windowIndex, QWidget* parent) : QWidget(parent), m_windowIndex(windowIndex) { m_transformToolTipText = ("\n\n" "At any time, the view of the surface may be changed by\n" " PAN: Move the mouse with the left mouse button down while " "holding down the Shift key.\n" " ROTATE: Move the mouse with the left mouse button down.\n" " ZOOM: Move the mouse with the left mouse button down while " "holding down the Ctrl key (Apple key on Macs)." ); m_inputModeImage = inputModeImage; QLabel* nameLabel = new QLabel("Image Control Points "); m_editOperationWidget = createEditOperationWidget(); QHBoxLayout* layout = new QHBoxLayout(this); WuQtUtilities::setLayoutSpacingAndMargins(layout, 0, 0); layout->addWidget(nameLabel); layout->addSpacing(10); layout->addWidget(m_editOperationWidget); layout->addStretch(); } /** * Destructor. */ UserInputModeImageWidget::~UserInputModeImageWidget() { } /** * Update the contents of the widget. */ void UserInputModeImageWidget::updateWidget() { setActionGroupByActionData(m_editOperationActionGroup, (int)m_inputModeImage->getEditOperation()); } /** * Set the action with its data value of the given integer * as the active action. * @param actionGroup * Action group for which action is selected. * @param dataInteger * Integer value for data attribute. */ void UserInputModeImageWidget::setActionGroupByActionData(QActionGroup* actionGroup, const int dataInteger) { actionGroup->blockSignals(true); const QList actionList = actionGroup->actions(); QListIterator iter(actionList); while (iter.hasNext()) { QAction* action = iter.next(); const int actionDataInteger = action->data().toInt(); if (dataInteger == actionDataInteger) { action->setChecked(true); break; } } actionGroup->blockSignals(false); } /** * @return The edit widget. */ QWidget* UserInputModeImageWidget::createEditOperationWidget() { /* * Add button */ const AString addToolTipText = ("Click the mouse over an image and volume slice " "to add a control point." + m_transformToolTipText); QAction* addAction = WuQtUtilities::createAction("Add", WuQtUtilities::createWordWrappedToolTipText(addToolTipText), this); addAction->setCheckable(true); addAction->setData(static_cast(UserInputModeImage::EDIT_OPERATION_ADD)); QToolButton* addToolButton = new QToolButton(); addToolButton->setDefaultAction(addAction); /* * Delete button */ const AString deleteToolTipText = ("Delete a control point by clicking the mouse over the control point." + m_transformToolTipText); QAction* deleteAction = WuQtUtilities::createAction("Delete", WuQtUtilities::createWordWrappedToolTipText(deleteToolTipText), this); deleteAction->setCheckable(true); deleteAction->setData(static_cast(UserInputModeImage::EDIT_OPERATION_DELETE)); QToolButton* deleteToolButton = new QToolButton(); deleteToolButton->setDefaultAction(deleteAction); /* * Action group to make add/delete actions mutually exclusive */ m_editOperationActionGroup = new QActionGroup(this); m_editOperationActionGroup->addAction(deleteAction); m_editOperationActionGroup->addAction(addAction); m_editOperationActionGroup->setExclusive(true); QObject::connect(m_editOperationActionGroup, SIGNAL(triggered(QAction*)), this, SLOT(editOperationActionTriggered(QAction*))); /* * Convert button */ const AString convertToolTipText = ("Convert image to volume"); QAction* convertAction = WuQtUtilities::createAction("Convert...", WuQtUtilities::createWordWrappedToolTipText(convertToolTipText), this, this, SLOT(convertActionTriggered())); convertAction->setCheckable(false); m_convertToolButton = new QToolButton(); m_convertToolButton->setDefaultAction(convertAction); /* * Delete all button */ const AString deleteAllToolTipText = ("Delete all control points"); QAction* deleteAllAction = WuQtUtilities::createAction("Delete All", WuQtUtilities::createWordWrappedToolTipText(deleteAllToolTipText), this, this, SLOT(deleteAllActionTriggered())); deleteAllAction->setCheckable(false); m_deleteAllToolButton = new QToolButton(); m_deleteAllToolButton->setDefaultAction(deleteAllAction); QWidget* widget = new QWidget(); QHBoxLayout* layout = new QHBoxLayout(widget); WuQtUtilities::setLayoutSpacingAndMargins(layout, 2, 0); layout->addWidget(addToolButton); layout->addWidget(deleteToolButton); layout->addSpacing(15); layout->addWidget(m_deleteAllToolButton); layout->addSpacing(35); layout->addWidget(m_convertToolButton); layout->addStretch(); // widget->setFixedWidth(widget->sizeHint().width()); return widget; } /** * Called when convert action is triggered. */ void UserInputModeImageWidget::convertActionTriggered() { ImageFile* imageFile = m_inputModeImage->getImageFile(); const int32_t tabIndex = m_inputModeImage->getTabIndex(); if ((imageFile != NULL) && (tabIndex >= 0)) { ControlPointFile* controlPointFile = imageFile->getControlPointFile(); AString errorMessage; if ( ! controlPointFile->updateLandmarkTransformationMatrix(errorMessage)) { WuQMessageBox::errorOk(m_convertToolButton, errorMessage); return; } ImageFileConvertToVolumeFileDialog convertDialog(this, tabIndex, imageFile); convertDialog.exec(); } } /** * Called when convert action is triggered. */ void UserInputModeImageWidget::deleteAllActionTriggered() { if (WuQMessageBox::warningOkCancel(m_deleteAllToolButton, "Delete all control points?")) { m_inputModeImage->deleteAllControlPoints(); } } /** * Called when an edit operation button is selected. * @param action * Action that was selected. */ void UserInputModeImageWidget::editOperationActionTriggered(QAction* action) { const int editModeInteger = action->data().toInt(); const UserInputModeImage::EditOperation editOperation = static_cast(editModeInteger); m_inputModeImage->setEditOperation(editOperation); } connectome-workbench-1.2.3+git41-gc4c6c90/src/GuiQt/UserInputModeImageWidget.h000066400000000000000000000051301300200146000266170ustar00rootroot00000000000000#ifndef __USER_INPUT_MODE_IMAGE_WIDGET__H_ #define __USER_INPUT_MODE_IMAGE_WIDGET__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" class QActionGroup; class QToolButton; namespace caret { class UserInputModeImage; class UserInputModeImageWidget : public QWidget { Q_OBJECT public: UserInputModeImageWidget(UserInputModeImage* inputModeImage, const int32_t windowIndex, QWidget* parent = 0); virtual ~UserInputModeImageWidget(); void updateWidget(); // ADD_NEW_METHODS_HERE private slots: void convertActionTriggered(); void deleteAllActionTriggered(); void editOperationActionTriggered(QAction*); private: UserInputModeImageWidget(const UserInputModeImageWidget&); UserInputModeImageWidget& operator=(const UserInputModeImageWidget&); QWidget* createEditOperationWidget(); void setActionGroupByActionData(QActionGroup* actionGroup, const int dataInteger); // ADD_NEW_MEMBERS_HERE UserInputModeImage* m_inputModeImage; const int32_t m_windowIndex; QActionGroup* m_editOperationActionGroup; QToolButton* m_deleteAllToolButton; QToolButton* m_convertToolButton; QWidget* m_editOperationWidget; QString m_transformToolTipText; friend class UserInputModeImage; }; #ifdef __USER_INPUT_MODE_IMAGE_WIDGET_DECLARE__ #endif // __USER_INPUT_MODE_IMAGE_WIDGET_DECLARE__ } // namespace #endif //__USER_INPUT_MODE_IMAGE_WIDGET__H_ connectome-workbench-1.2.3+git41-gc4c6c90/src/GuiQt/UserInputModeView.cxx000066400000000000000000000235161300200146000257260ustar00rootroot00000000000000 /*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 __USER_INPUT_MODE_VIEW_DECLARE__ #include "UserInputModeView.h" #undef __USER_INPUT_MODE_VIEW_DECLARE__ #include "Brain.h" #include "BrainOpenGLViewportContent.h" #include "BrainOpenGLWidget.h" #include "BrowserTabContent.h" #include "EventGraphicsUpdateOneWindow.h" #include "EventUpdateYokedWindows.h" #include "EventManager.h" #include "GuiManager.h" #include "MouseEvent.h" #include "UserInputModeViewContextMenu.h" using namespace caret; /** * \class caret::UserInputModeView * \brief Processing user input for VIEW mode. * * Processes user input in VIEW mode which includes * viewing transformation of brain models and * identification operations. */ /** * Constructor. */ UserInputModeView::UserInputModeView() : UserInputModeAbstract(UserInputModeAbstract::VIEW) { } /** * Constructor for subclasses. * * @param inputMode * Subclass' input mode. */ UserInputModeView::UserInputModeView(const UserInputMode inputMode) : UserInputModeAbstract(inputMode) { } /** * Destructor. */ UserInputModeView::~UserInputModeView() { } /** * Process identification.. * * @param mouseEvent * The mouse event. * @param browserTabContent * Content of the browser window's tab. * @param openGLWidget * OpenGL Widget in which mouse event occurred. * @param mouseClickX * Location of where mouse was clicked. * @param mouseClickY * Location of where mouse was clicked. */ void UserInputModeView::processModelViewIdentification(BrainOpenGLViewportContent* viewportContent, BrainOpenGLWidget* openGLWidget, const int32_t mouseClickX, const int32_t mouseClickY) { SelectionManager* selectionManager = openGLWidget->performIdentification(mouseClickX, mouseClickY, false); BrowserTabContent* btc = viewportContent->getBrowserTabContent(); if (btc != NULL) { const int32_t tabIndex = btc->getTabNumber(); GuiManager::get()->processIdentification(tabIndex, selectionManager, openGLWidget); } } /** * Called when 'this' user input receiver is set * to receive events. */ void UserInputModeView::initialize() { } /** * Called when 'this' user input receiver is no * longer set to receive events. */ void UserInputModeView::finish() { } /** * Called to update the input receiver for various events. */ void UserInputModeView::update() { } /** * @return The cursor for display in the OpenGL widget. */ CursorEnum::Enum UserInputModeView::getCursor() const { return CursorEnum::CURSOR_DEFAULT; } /** * Get a description of this object's content. * @return String describing this object's content. */ AString UserInputModeView::toString() const { return "UserInputModeView"; } /** * Process a mouse left click event. * * @param mouseEvent * Mouse event information. */ void UserInputModeView::mouseLeftClick(const MouseEvent& mouseEvent) { if (mouseEvent.getViewportContent() == NULL) { return; } processModelViewIdentification(mouseEvent.getViewportContent(), mouseEvent.getOpenGLWidget(), mouseEvent.getX(), mouseEvent.getY()); } /** * Process a mouse left click with shift key down event. * * @param mouseEvent * Mouse event information. */ void UserInputModeView::mouseLeftClickWithShift(const MouseEvent& mouseEvent) { if (mouseEvent.getViewportContent() == NULL) { return; } } /** * Process a mouse left click with ctrl and shift keys down event. * * @param mouseEvent * Mouse event information. */ void UserInputModeView::mouseLeftClickWithCtrlShift(const MouseEvent& mouseEvent) { /* * Perform identification same as a left click */ mouseLeftClick(mouseEvent); } /** * Process a mouse left drag with no keys down event. * * @param mouseEvent * Mouse event information. */ void UserInputModeView::mouseLeftDrag(const MouseEvent& mouseEvent) { BrainOpenGLViewportContent* viewportContent = mouseEvent.getViewportContent(); if (viewportContent == NULL) { return; } BrowserTabContent* browserTabContent = viewportContent->getBrowserTabContent(); if (browserTabContent == NULL) { return; } browserTabContent->applyMouseRotation(viewportContent, mouseEvent.getPressedX(), mouseEvent.getPressedY(), mouseEvent.getX(), mouseEvent.getY(), mouseEvent.getDx(), mouseEvent.getDy()); /* * Update graphics. */ updateGraphics(mouseEvent); } /** * Process a mouse left drag with only the alt key down event. * * @param mouseEvent * Mouse event information. */ void UserInputModeView::mouseLeftDragWithAlt(const MouseEvent& mouseEvent) { if (mouseEvent.getViewportContent() == NULL) { return; } } /** * Process a mouse left drag with ctrl key down event. * * @param mouseEvent * Mouse event information. */ void UserInputModeView::mouseLeftDragWithCtrl(const MouseEvent& mouseEvent) { BrainOpenGLViewportContent* viewportContent = mouseEvent.getViewportContent(); if (viewportContent == NULL) { return; } BrowserTabContent* browserTabContent = viewportContent->getBrowserTabContent(); if (browserTabContent == NULL) { return; } browserTabContent->applyMouseScaling(mouseEvent.getDx(), mouseEvent.getDy()); updateGraphics(mouseEvent); } /** * Process a mouse left drag with shift key down event. * * @param mouseEvent * Mouse event information. */ void UserInputModeView::mouseLeftDragWithShift(const MouseEvent& mouseEvent) { BrainOpenGLViewportContent* viewportContent = mouseEvent.getViewportContent(); if (viewportContent == NULL) { return; } BrowserTabContent* browserTabContent = viewportContent->getBrowserTabContent(); if (browserTabContent == NULL) { return; } browserTabContent->applyMouseTranslation(viewportContent, mouseEvent.getPressedX(), mouseEvent.getPressedY(), mouseEvent.getDx(), mouseEvent.getDy()); updateGraphics(mouseEvent); } /** * Show a context menu (pop-up menu at mouse location) * * @param mouseEvent * Mouse event information. * @param menuPosition * Point at which menu is displayed (passed to QMenu::exec()) * @param openGLWidget * OpenGL widget in which context menu is requested */ void UserInputModeView::showContextMenu(const MouseEvent& mouseEvent, const QPoint& menuPosition, BrainOpenGLWidget* openGLWidget) { BrainOpenGLViewportContent* viewportContent = mouseEvent.getViewportContent(); BrowserTabContent* tabContent = viewportContent->getBrowserTabContent(); if (tabContent == NULL) { return; } const int32_t mouseX = mouseEvent.getX(); const int32_t mouseY = mouseEvent.getY(); SelectionManager* idManager = openGLWidget->performIdentification(mouseX, mouseY, false); UserInputModeViewContextMenu contextMenu(idManager, tabContent, openGLWidget); contextMenu.exec(menuPosition); } /** * If this windows is yoked, issue an event to update other * windows that are using the same yoking. */ void UserInputModeView::updateGraphics(const MouseEvent& mouseEvent) { bool issuedYokeEvent = false; if (mouseEvent.getViewportContent() != NULL) { BrowserTabContent* browserTabContent = mouseEvent.getViewportContent()->getBrowserTabContent(); const int32_t browserWindowIndex = mouseEvent.getBrowserWindowIndex(); EventManager::get()->sendEvent(EventGraphicsUpdateOneWindow(browserWindowIndex).getPointer()); if (browserTabContent != NULL) { if (browserTabContent->isYoked()) { issuedYokeEvent = true; EventManager::get()->sendEvent(EventUpdateYokedWindows(browserTabContent->getYokingGroup()).getPointer()); } } } /* * If not yoked, just need to update graphics. */ if (issuedYokeEvent == false) { EventManager::get()->sendEvent(EventGraphicsUpdateOneWindow(mouseEvent.getBrowserWindowIndex()).getPointer()); } } connectome-workbench-1.2.3+git41-gc4c6c90/src/GuiQt/UserInputModeView.h000066400000000000000000000056361300200146000253560ustar00rootroot00000000000000#ifndef __USER_INPUT_MODE_VIEW__H_ #define __USER_INPUT_MODE_VIEW__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 "UserInputModeAbstract.h" namespace caret { class UserInputModeView : public UserInputModeAbstract { public: UserInputModeView(); virtual ~UserInputModeView(); virtual void initialize(); virtual void finish(); virtual void update(); virtual CursorEnum::Enum getCursor() const; virtual void mouseLeftClick(const MouseEvent& mouseEvent); virtual void mouseLeftClickWithShift(const MouseEvent& mouseEvent); virtual void mouseLeftClickWithCtrlShift(const MouseEvent& mouseEvent); virtual void mouseLeftDrag(const MouseEvent& mouseEvent); virtual void mouseLeftDragWithAlt(const MouseEvent& mouseEvent); virtual void mouseLeftDragWithCtrl(const MouseEvent& mouseEvent); virtual void mouseLeftDragWithShift(const MouseEvent& mouseEvent); virtual void showContextMenu(const MouseEvent& mouseEvent, const QPoint& menuPosition, BrainOpenGLWidget* openGLWidget); protected: UserInputModeView(const UserInputMode inputMode); private: UserInputModeView(const UserInputModeView&); UserInputModeView& operator=(const UserInputModeView&); void updateGraphics(const MouseEvent& mouseEvent); void processModelViewIdentification(BrainOpenGLViewportContent* viewportContent, BrainOpenGLWidget* openGLWidget, const int32_t mouseClickX, const int32_t mouseClickY); public: virtual AString toString() const; }; #ifdef __USER_INPUT_MODE_VIEW_DECLARE__ // #endif // __USER_INPUT_MODE_VIEW_DECLARE__ } // namespace #endif //__USER_INPUT_MODE_VIEW__H_ connectome-workbench-1.2.3+git41-gc4c6c90/src/GuiQt/UserInputModeViewContextMenu.cxx000066400000000000000000001752431300200146000301250ustar00rootroot00000000000000 /*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 __USER_INPUT_MODE_VIEW_CONTEXT_MENU_DECLARE__ #include "UserInputModeViewContextMenu.h" #undef __USER_INPUT_MODE_VIEW_CONTEXT_MENU_DECLARE__ #include "AlgorithmException.h" #include "AlgorithmNodesInsideBorder.h" #include "Border.h" #include "Brain.h" #include "BrainOpenGLWidget.h" #include "BrainStructure.h" #include "BrowserTabContent.h" #include "ChartableLineSeriesBrainordinateInterface.h" #include "ChartingDataManager.h" #include "CiftiBrainordinateLabelFile.h" #include "CiftiConnectivityMatrixDataFileManager.h" #include "CiftiFiberTrajectoryFile.h" #include "CiftiFiberTrajectoryManager.h" #include "CiftiMappableConnectivityMatrixDataFile.h" #include "CursorDisplayScoped.h" #include "EventManager.h" #include "EventGraphicsUpdateAllWindows.h" #include "EventUpdateInformationWindows.h" #include "EventUserInterfaceUpdate.h" #include "FociPropertiesEditorDialog.h" #include "Focus.h" #include "GiftiLabel.h" #include "GiftiLabelTable.h" #include "GuiManager.h" #include "IdentifiedItemNode.h" #include "IdentificationManager.h" #include "LabelFile.h" #include "Overlay.h" #include "OverlaySet.h" #include "Model.h" #include "ProgressReportingDialog.h" #include "SelectionItemBorderSurface.h" #include "SelectionItemFocusSurface.h" #include "SelectionItemFocusVolume.h" #include "SelectionItemSurfaceNode.h" #include "SelectionItemSurfaceNodeIdentificationSymbol.h" #include "SelectionItemVoxel.h" #include "SelectionManager.h" #include "SessionManager.h" #include "Surface.h" #include "UserInputModeFociWidget.h" #include "VolumeFile.h" #include "WuQMessageBox.h" #include "WuQtUtilities.h" using namespace caret; /** * \class caret::UserInputModeViewContextMenu * \brief Context (pop-up) menu for User Input View Mode * * Displays a menu in the BrainOpenGLWidget. Content of menu * is dependent upon data under the cursor. */ /** * Constructor. * @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, BrainOpenGLWidget* parentOpenGLWidget) : QMenu(parentOpenGLWidget) { this->parentOpenGLWidget = parentOpenGLWidget; this->selectionManager = selectionManager; this->browserTabContent = browserTabContent; /* * Add the identification actions. */ addIdentificationActions(); /* * Add the border options. */ addBorderRegionOfInterestActions(); /* * Add the foci actions. */ addFociActions(); /* * Show Label ROI operations only for surfaces */ addLabelRegionOfInterestActions(); const SelectionItemSurfaceNodeIdentificationSymbol* idSymbol = selectionManager->getSurfaceNodeIdentificationSymbol(); if (this->actions().count() > 0) { this->addSeparator(); } this->addAction("Remove All Vertex Identification Symbols", this, SLOT(removeAllNodeIdentificationSymbolsSelected())); if (idSymbol->isValid()) { const AString text = ("Remove Identification of Vertices " + AString::number(idSymbol->getNodeNumber())); this->addAction(WuQtUtilities::createAction(text, "", this, this, SLOT(removeNodeIdentificationSymbolSelected()))); } } /** * Destructor. */ UserInputModeViewContextMenu::~UserInputModeViewContextMenu() { for (std::vector::iterator parcelIter = this->parcelConnectivities.begin(); parcelIter != this->parcelConnectivities.end(); parcelIter++) { delete *parcelIter; } } /** * Add the actions to this context menu. * * @param actionsToAdd * Actions to add to the menum. * @param addSeparatorBeforeActions * If true and there are actions presently in the menu, a separator * (horizontal bar) is added prior to adding the given actions. */ void UserInputModeViewContextMenu::addActionsToMenu(QList& actionsToAdd, const bool addSeparatorBeforeActions) { if (actionsToAdd.empty() == false) { if (addSeparatorBeforeActions) { if (actions().isEmpty() == false) { addSeparator(); } } addActions(actionsToAdd); } } /** * Add the identification actions to the menu. */ void UserInputModeViewContextMenu::addIdentificationActions() { /* * Accumlate identification actions */ QList identificationActions; /* * Identify Border */ SelectionItemBorderSurface* borderID = this->selectionManager->getSurfaceBorderIdentification(); if (borderID->isValid()) { const QString text = ("Identify Border (" + borderID->getBorder()->getName() + ") Under Mouse"); identificationActions.push_back(WuQtUtilities::createAction(text, "", this, this, SLOT(identifySurfaceBorderSelected()))); } /* * Identify Surface Focus */ SelectionItemFocusSurface* focusID = this->selectionManager->getSurfaceFocusIdentification(); if (focusID->isValid()) { const QString text = ("Identify Surface Focus (" + focusID->getFocus()->getName() + ") Under Mouse"); identificationActions.push_back(WuQtUtilities::createAction(text, "", this, this, SLOT(identifySurfaceFocusSelected()))); } /* * Identify Node */ SelectionItemSurfaceNode* surfaceID = this->selectionManager->getSurfaceNodeIdentification(); if (surfaceID->isValid()) { const int32_t nodeIndex = surfaceID->getNodeNumber(); const Surface* surface = surfaceID->getSurface(); const QString text = ("Identify Vertex " + QString::number(nodeIndex) + " (" + AString::fromNumbers(surface->getCoordinate(nodeIndex), 3, ",") + ") Under Mouse"); identificationActions.push_back(WuQtUtilities::createAction(text, "", this, this, SLOT(identifySurfaceNodeSelected()))); } /* * Identify Voxel */ SelectionItemVoxel* idVoxel = this->selectionManager->getVoxelIdentification(); if (idVoxel->isValid()) { int64_t ijk[3]; idVoxel->getVoxelIJK(ijk); const AString text = ("Identify Voxel (" + AString::fromNumbers(ijk, 3, ",") + ")"); identificationActions.push_back(WuQtUtilities::createAction(text, "", this, this, SLOT(identifyVoxelSelected()))); } /* * Identify Volume Focus */ SelectionItemFocusVolume* focusVolID = this->selectionManager->getVolumeFocusIdentification(); if (focusVolID->isValid()) { const QString text = ("Identify Volume Focus (" + focusVolID->getFocus()->getName() + ") Under Mouse"); identificationActions.push_back(WuQtUtilities::createAction(text, "", this, this, SLOT(identifyVolumeFocusSelected()))); } addActionsToMenu(identificationActions, true); } /** * Add the border region of interest actions to the menu. */ void UserInputModeViewContextMenu::addBorderRegionOfInterestActions() { SelectionItemBorderSurface* borderID = this->selectionManager->getSurfaceBorderIdentification(); QList borderActions; if (borderID->isValid()) { Brain* brain = borderID->getBrain(); std::vector ciftiMatrixFiles; brain->getAllCiftiConnectivityMatrixFiles(ciftiMatrixFiles); bool hasCiftiConnectivity = (ciftiMatrixFiles.empty() == false); /* * Connectivity actions for borders */ if (hasCiftiConnectivity) { const QString text = ("Show CIFTI Connectivity for Nodes Inside Border " + borderID->getBorder()->getName()); QAction* action = WuQtUtilities::createAction(text, "", this, this, SLOT(borderCiftiConnectivitySelected())); borderActions.push_back(action); } std::vector chartableFiles; brain->getAllChartableBrainordinateDataFiles(chartableFiles); if (chartableFiles.empty() == false) { const QString text = ("Show Charts for Nodes Inside Border " + borderID->getBorder()->getName()); QAction* action = WuQtUtilities::createAction(text, "", this, this, SLOT(borderDataSeriesSelected())); borderActions.push_back(action); } } addActionsToMenu(borderActions, true); } /** * Add all label region of interest options to the menu */ void UserInputModeViewContextMenu::addLabelRegionOfInterestActions() { Brain* brain = NULL; float voxelXYZ[3] = { 0.0, 0.0, 0.0 }; SelectionItemVoxel* idVoxel = this->selectionManager->getVoxelIdentification(); if (idVoxel->isValid()) { double voxelXYZDouble[3]; idVoxel->getModelXYZ(voxelXYZDouble); voxelXYZ[0] = voxelXYZDouble[0]; voxelXYZ[1] = voxelXYZDouble[1]; voxelXYZ[2] = voxelXYZDouble[2]; brain = idVoxel->getBrain(); } Surface* surface = NULL; int32_t surfaceNodeIndex = -1; int32_t surfaceNumberOfNodes = -1; StructureEnum::Enum surfaceStructure = StructureEnum::INVALID; SelectionItemSurfaceNode* idNode = this->selectionManager->getSurfaceNodeIdentification(); if (idNode->isValid()) { surface = idNode->getSurface(); surfaceNodeIndex = idNode->getNodeNumber(); surfaceNumberOfNodes = surface->getNumberOfNodes(); surfaceStructure = surface->getStructure(); brain = idNode->getBrain(); } /* * If Brain is invalid, then there is no identified node or voxel */ if (brain == NULL) { return; } /* * Manager for connectivity matrix files */ CiftiConnectivityMatrixDataFileManager* ciftiConnectivityMatrixManager = SessionManager::get()->getCiftiConnectivityMatrixDataFileManager(); std::vector ciftiMatrixFiles; brain->getAllCiftiConnectivityMatrixFiles(ciftiMatrixFiles); bool hasCiftiConnectivity = (ciftiMatrixFiles.empty() == false); /* * Manager for fiber trajectory */ CiftiFiberTrajectoryManager* ciftiFiberTrajectoryManager = SessionManager::get()->getCiftiFiberTrajectoryManager(); std::vector ciftiFiberTrajectoryFiles; const int32_t numFiberFiles = brain->getNumberOfConnectivityFiberTrajectoryFiles(); for (int32_t i = 0; i < numFiberFiles; i++) { ciftiFiberTrajectoryFiles.push_back(brain->getConnectivityFiberTrajectoryFile(i)); } const bool haveCiftiFiberTrajectoryFiles = (ciftiFiberTrajectoryFiles.empty() == false); /* * Manager for Chartable files */ std::vector chartableFiles; brain->getAllChartableBrainordinateDataFiles(chartableFiles); const bool haveChartableFiles = (chartableFiles.empty() == false); ChartingDataManager* chartingDataManager = brain->getChartingDataManager(); /* * Actions for each file type */ QList ciftiConnectivityActions; QActionGroup* ciftiConnectivityActionGroup = new QActionGroup(this); QObject::connect(ciftiConnectivityActionGroup, SIGNAL(triggered(QAction*)), this, SLOT(parcelCiftiConnectivityActionSelected(QAction*))); QList ciftiFiberTrajectoryActions; QActionGroup* ciftiFiberTrajectoryActionGroup = new QActionGroup(this); QObject::connect(ciftiFiberTrajectoryActionGroup, SIGNAL(triggered(QAction*)), this, SLOT(parcelCiftiFiberTrajectoryActionSelected(QAction*))); QList chartableDataActions; QActionGroup* chartableDataActionGroup = new QActionGroup(this); QObject::connect(chartableDataActionGroup, SIGNAL(triggered(QAction*)), this, SLOT(parcelChartableDataActionSelected(QAction*))); /* * Get all mappable files and find files mapped with using labels */ std::vector mappableFiles; brain->getAllMappableDataFiles(mappableFiles); /* * Process each map file */ for (std::vector::iterator mapFileIterator = mappableFiles.begin(); mapFileIterator != mappableFiles.end(); mapFileIterator++) { CaretMappableDataFile* mappableLabelFile = *mapFileIterator; if (mappableLabelFile->isMappedWithLabelTable()) { const int32_t numMaps = mappableLabelFile->getNumberOfMaps(); for (int32_t mapIndex = 0; mapIndex < numMaps; mapIndex++) { Surface* labelSurface = NULL; int32_t labelNodeNumber = -1; int32_t labelKey = -1; AString labelName; int64_t volumeDimensions[3] = { -1, -1, -1 }; ParcelConnectivity::ParcelType parcelType = ParcelConnectivity::PARCEL_TYPE_INVALID; if (mappableLabelFile->isVolumeMappable()) { CiftiBrainordinateLabelFile* ciftiLabelFile = dynamic_cast(mappableLabelFile); VolumeFile* volumeLabelFile = dynamic_cast(mappableLabelFile); VolumeMappableInterface* volumeInterface = dynamic_cast(mappableLabelFile); if (volumeInterface != NULL) { int64_t voxelIJK[3]; float voxelValue; bool voxelValueValid; AString textValue; if (ciftiLabelFile != NULL) { if (ciftiLabelFile->getMapVolumeVoxelValue(mapIndex, voxelXYZ, voxelIJK, voxelValue, voxelValueValid, textValue)) { if (voxelValueValid) { labelKey = static_cast(voxelValue); const GiftiLabelTable* labelTable = ciftiLabelFile->getMapLabelTable(mapIndex); labelName = labelTable->getLabelName(labelKey); if (labelName.isEmpty() == false) { parcelType = ParcelConnectivity::PARCEL_TYPE_VOLUME_VOXELS; } } } } else if (volumeLabelFile != NULL) { int64_t voxelIJK[3]; volumeLabelFile->enclosingVoxel(voxelXYZ, voxelIJK); if (volumeLabelFile->indexValid(voxelIJK)) { const float voxelValue = volumeLabelFile->getValue(voxelIJK[0], voxelIJK[1], voxelIJK[2], mapIndex); const GiftiLabelTable* labelTable = volumeLabelFile->getMapLabelTable(mapIndex); labelKey = static_cast(voxelValue); labelName = labelTable->getLabelName(voxelValue); if (labelName.isEmpty() == false) { parcelType = ParcelConnectivity::PARCEL_TYPE_VOLUME_VOXELS; } } } else { CaretAssertMessage(0, "Should never get here, new or invalid label file type"); } std::vector dims; volumeInterface->getDimensions(dims); if (dims.size() >= 3) { volumeDimensions[0] = dims[0]; volumeDimensions[1] = dims[1]; volumeDimensions[2] = dims[2]; } } } if (mappableLabelFile->isSurfaceMappable()) { if (labelName.isEmpty()) { if (idNode->isValid()) { labelSurface = idNode->getSurface(); labelNodeNumber = idNode->getNodeNumber(); LabelFile* labelFile = dynamic_cast(mappableLabelFile); CiftiBrainordinateLabelFile* ciftiLabelFile = dynamic_cast(mappableLabelFile); if (labelFile != NULL) { labelKey = labelFile->getLabelKey(labelNodeNumber, mapIndex); const GiftiLabelTable* labelTable = labelFile->getMapLabelTable(mapIndex); labelName = labelTable->getLabelName(labelKey); if (labelName.isEmpty() == false) { parcelType = ParcelConnectivity::PARCEL_TYPE_SURFACE_NODES; } } else if (ciftiLabelFile != NULL) { float nodeValue = 0.0; bool nodeValueValid = false; AString stringValue; if (ciftiLabelFile->getMapSurfaceNodeValue(mapIndex, surfaceStructure, surfaceNodeIndex, surfaceNumberOfNodes, nodeValue, nodeValueValid, stringValue)) { if (nodeValueValid) { labelKey = nodeValue; const GiftiLabelTable* labelTable = ciftiLabelFile->getMapLabelTable(mapIndex); labelName = labelTable->getLabelName(labelKey); if (labelName.isEmpty() == false) { parcelType = ParcelConnectivity::PARCEL_TYPE_SURFACE_NODES; } } } } } } } if (parcelType != ParcelConnectivity::PARCEL_TYPE_INVALID) { const AString mapName = (AString::number(mapIndex + 1) + ": " + mappableLabelFile->getMapName(mapIndex)); ParcelConnectivity* parcelConnectivity = new ParcelConnectivity(brain, parcelType, mappableLabelFile, mapIndex, labelKey, labelName, labelSurface, labelNodeNumber, volumeDimensions, chartingDataManager, ciftiConnectivityMatrixManager, ciftiFiberTrajectoryManager); this->parcelConnectivities.push_back(parcelConnectivity); if (hasCiftiConnectivity) { bool matchFlag = false; if (parcelType == ParcelConnectivity::PARCEL_TYPE_SURFACE_NODES) { matchFlag = true; } else if (parcelType == ParcelConnectivity::PARCEL_TYPE_VOLUME_VOXELS) { for (std::vector::iterator iter = ciftiMatrixFiles.begin(); iter != ciftiMatrixFiles.end(); iter++) { const CiftiMappableConnectivityMatrixDataFile* ciftiFile = *iter; if (ciftiFile->matchesDimensions(volumeDimensions[0], volumeDimensions[1], volumeDimensions[2])) { matchFlag = true; break; } } } if (matchFlag) { const AString actionName("Show Cifti Connectivity For Parcel " + labelName + " in map " + mapName); QAction* action = ciftiConnectivityActionGroup->addAction(actionName); action->setData(qVariantFromValue((void*)parcelConnectivity)); ciftiConnectivityActions.push_back(action); } } if (haveCiftiFiberTrajectoryFiles) { const AString fiberTrajActionName("Show Average Fiber Trajectory for Parcel " + labelName + " in map " + mapName); QAction* fiberTrajAction = ciftiFiberTrajectoryActionGroup->addAction(fiberTrajActionName); fiberTrajAction->setData(qVariantFromValue((void*)parcelConnectivity)); ciftiFiberTrajectoryActions.push_back(fiberTrajAction); } if (haveChartableFiles) { bool matchFlag = false; if (parcelType == ParcelConnectivity::PARCEL_TYPE_SURFACE_NODES) { matchFlag = true; } else if (parcelType == ParcelConnectivity::PARCEL_TYPE_VOLUME_VOXELS) { for (std::vector::iterator iter = chartableFiles.begin(); iter != chartableFiles.end(); iter++) { const ChartableLineSeriesBrainordinateInterface* chartFile = *iter; const CaretMappableDataFile* mapDataFile = chartFile->getLineSeriesChartCaretMappableDataFile(); if (mapDataFile != NULL){ if (mapDataFile->isVolumeMappable()) { const VolumeMappableInterface* volMap = dynamic_cast(mapDataFile); if (volMap->matchesDimensions(volumeDimensions[0], volumeDimensions[1], volumeDimensions[2])) { matchFlag = true; break; } } } } } if (matchFlag) { const AString tsActionName("Show Data/Time Series Graph For Parcel " + labelName + " in map " + mapName); QAction* tsAction = chartableDataActionGroup->addAction(tsActionName); tsAction->setData(qVariantFromValue((void*)parcelConnectivity)); chartableDataActions.push_back(tsAction); } } } } } } addActionsToMenu(ciftiConnectivityActions, true); addActionsToMenu(ciftiFiberTrajectoryActions, true); addActionsToMenu(chartableDataActions, true); } /** * Add the foci options to the menu. */ void UserInputModeViewContextMenu::addFociActions() { QList fociCreateActions; const SelectionItemSurfaceNodeIdentificationSymbol* idSymbol = selectionManager->getSurfaceNodeIdentificationSymbol(); SelectionItemFocusSurface* focusID = this->selectionManager->getSurfaceFocusIdentification(); SelectionItemSurfaceNode* surfaceID = this->selectionManager->getSurfaceNodeIdentification(); SelectionItemVoxel* idVoxel = this->selectionManager->getVoxelIdentification(); SelectionItemFocusVolume* focusVolID = this->selectionManager->getVolumeFocusIdentification(); /* * Create focus at surface node or at ID symbol */ if (surfaceID->isValid() && (focusID->isValid() == false)) { const int32_t nodeIndex = surfaceID->getNodeNumber(); const Surface* surface = surfaceID->getSurface(); const QString text = ("Create Focus at Vertex " + QString::number(nodeIndex) + " (" + AString::fromNumbers(surface->getCoordinate(nodeIndex), 3, ",") + ")..."); fociCreateActions.push_back(WuQtUtilities::createAction(text, "", this, this, SLOT(createSurfaceFocusSelected()))); } else if (idSymbol->isValid() && (focusID->isValid() == false)) { const int32_t nodeIndex = idSymbol->getNodeNumber(); const Surface* surface = idSymbol->getSurface(); const QString text = ("Create Focus at Selected Vertex " + QString::number(nodeIndex) + " (" + AString::fromNumbers(surface->getCoordinate(nodeIndex), 3, ",") + ")..."); fociCreateActions.push_back(WuQtUtilities::createAction(text, "", this, this, SLOT(createSurfaceIDSymbolFocusSelected()))); } /* * Create focus at voxel as long as there is no volume focus ID */ if (idVoxel->isValid() && (focusVolID->isValid() == false)) { int64_t ijk[3]; idVoxel->getVoxelIJK(ijk); float xyz[3]; const VolumeMappableInterface* vf = idVoxel->getVolumeFile(); vf->indexToSpace(ijk, xyz); const AString text = ("Create Focus at Voxel IJK (" + AString::fromNumbers(ijk, 3, ",") + ") XYZ (" + AString::fromNumbers(xyz, 3, ",") + ")..."); fociCreateActions.push_back(WuQtUtilities::createAction(text, "", this, this, SLOT(createVolumeFocusSelected()))); } addActionsToMenu(fociCreateActions, true); /* * Actions for editing */ QList fociEditActions; /* * Edit Surface Focus */ if (focusID->isValid()) { const QString text = ("Edit Surface Focus (" + focusID->getFocus()->getName() + ")"); fociEditActions.push_back(WuQtUtilities::createAction(text, "", this, this, SLOT(editSurfaceFocusSelected()))); } /* * Edit volume focus */ if (focusVolID->isValid()) { const QString text = ("Edit Volume Focus (" + focusVolID->getFocus()->getName() + ")"); fociEditActions.push_back(WuQtUtilities::createAction(text, "", this, this, SLOT(editVolumeFocusSelected()))); } addActionsToMenu(fociEditActions, true); } /** * Called when a cifti connectivity action is selected. * @param action * Action that was selected. */ void UserInputModeViewContextMenu::parcelCiftiConnectivityActionSelected(QAction* action) { void* pointer = action->data().value(); ParcelConnectivity* pc = (ParcelConnectivity*)pointer; std::vector nodeIndices; std::vector voxelIndices; switch (pc->parcelType) { case ParcelConnectivity::PARCEL_TYPE_INVALID: break; case ParcelConnectivity::PARCEL_TYPE_SURFACE_NODES: pc->getNodeIndices(nodeIndices); if (nodeIndices.empty()) { WuQMessageBox::errorOk(this, "No vertices match label " + pc->labelName); return; } if (pc->ciftiConnectivityManager->hasNetworkFiles(pc->brain)) { if (warnIfNetworkBrainordinateCountIsLarge(nodeIndices.size()) == false) { return; } } break; case ParcelConnectivity::PARCEL_TYPE_VOLUME_VOXELS: pc->getVoxelIndices(voxelIndices); if (voxelIndices.empty()) { WuQMessageBox::errorOk(this, "No voxels match label " + pc->labelName); return; } if (pc->ciftiConnectivityManager->hasNetworkFiles(pc->brain)) { if (warnIfNetworkBrainordinateCountIsLarge(voxelIndices.size()) == false) { return; } } break; } CursorDisplayScoped cursor; cursor.showWaitCursor(); try { ProgressReportingDialog progressDialog("Connectivity Within Parcel", "", this); progressDialog.setValue(0); switch (pc->parcelType) { case ParcelConnectivity::PARCEL_TYPE_INVALID: break; case ParcelConnectivity::PARCEL_TYPE_SURFACE_NODES: pc->ciftiConnectivityManager->loadAverageDataForSurfaceNodes(pc->brain, pc->surface, nodeIndices); break; case ParcelConnectivity::PARCEL_TYPE_VOLUME_VOXELS: pc->ciftiConnectivityManager->loadAverageDataForVoxelIndices(pc->brain, pc->volumeDimensions, voxelIndices); break; } } catch (const DataFileException& e) { cursor.restoreCursor(); WuQMessageBox::errorOk(this, e.whatString()); } EventManager::get()->sendEvent(EventUserInterfaceUpdate().getPointer()); EventManager::get()->sendEvent(EventGraphicsUpdateAllWindows().getPointer()); } /** * Called when a cifti connectivity action is selected. * @param action * Action that was selected. */ void UserInputModeViewContextMenu::parcelCiftiFiberTrajectoryActionSelected(QAction* action) { void* pointer = action->data().value(); ParcelConnectivity* pc = (ParcelConnectivity*)pointer; std::vector nodeIndices; switch (pc->parcelType) { case ParcelConnectivity::PARCEL_TYPE_INVALID: break; case ParcelConnectivity::PARCEL_TYPE_SURFACE_NODES: pc->getNodeIndices(nodeIndices); if (nodeIndices.empty()) { WuQMessageBox::errorOk(this, "No vertices match label " + pc->labelName); return; } break; case ParcelConnectivity::PARCEL_TYPE_VOLUME_VOXELS: break; } CursorDisplayScoped cursor; cursor.showWaitCursor(); try { ProgressReportingDialog progressDialog("Trajectory Within Parcel", "", this); progressDialog.setValue(0); switch (pc->parcelType) { case ParcelConnectivity::PARCEL_TYPE_INVALID: break; case ParcelConnectivity::PARCEL_TYPE_SURFACE_NODES: pc->ciftiFiberTrajectoryManager->loadDataAverageForSurfaceNodes(pc->brain, pc->surface, nodeIndices); break; case ParcelConnectivity::PARCEL_TYPE_VOLUME_VOXELS: std::vector voxelIndices; pc->getVoxelIndices(voxelIndices); pc->ciftiFiberTrajectoryManager->loadAverageDataForVoxelIndices(pc->brain, pc->volumeDimensions, voxelIndices); break; } } catch (const DataFileException& e) { cursor.restoreCursor(); WuQMessageBox::errorOk(this, e.whatString()); } EventManager::get()->sendEvent(EventUserInterfaceUpdate().getPointer()); EventManager::get()->sendEvent(EventGraphicsUpdateAllWindows().getPointer()); } /** * Called when border cifti connectivity is selected. */ void UserInputModeViewContextMenu::borderCiftiConnectivitySelected() { SelectionItemBorderSurface* borderID = this->selectionManager->getSurfaceBorderIdentification(); Border* border = borderID->getBorder(); Surface* surface = borderID->getSurface(); const int32_t numberOfNodes = surface->getNumberOfNodes(); LabelFile labelFile; labelFile.setNumberOfNodesAndColumns(numberOfNodes, 1); const int32_t labelKey = labelFile.getLabelTable()->addLabel("TempLabel", 1.0f, 1.0f, 1.0f, 1.0f); const int32_t mapIndex = 0; try { AlgorithmNodesInsideBorder algorithmInsideBorder(NULL, surface, border, false, mapIndex, labelKey, &labelFile); std::vector nodeIndices; nodeIndices.reserve(numberOfNodes); for (int32_t i = 0; i < numberOfNodes; i++) { if (labelFile.getLabelKey(i, mapIndex) == labelKey) { nodeIndices.push_back(i); } } if (nodeIndices.empty()) { WuQMessageBox::errorOk(this, "No vertices found inside border " + border->getName()); return; } if (borderID->getBrain()->getChartingDataManager()->hasNetworkFiles()) { if (warnIfNetworkBrainordinateCountIsLarge(nodeIndices.size()) == false) { return; } } CursorDisplayScoped cursor; cursor.showWaitCursor(); try { ProgressReportingDialog progressDialog("Connectivity Within Border", "", this); progressDialog.setValue(0); CiftiConnectivityMatrixDataFileManager* ciftiConnMann = SessionManager::get()->getCiftiConnectivityMatrixDataFileManager(); ciftiConnMann->loadAverageDataForSurfaceNodes(borderID->getBrain(), surface, nodeIndices); } catch (const DataFileException& e) { cursor.restoreCursor(); WuQMessageBox::errorOk(this, e.whatString()); } EventManager::get()->sendEvent(EventUserInterfaceUpdate().getPointer()); EventManager::get()->sendEvent(EventGraphicsUpdateAllWindows().getPointer()); } catch (const AlgorithmException& e) { WuQMessageBox::errorOk(this, e.whatString()); } } /** * Called when a connectivity action is selected. * @param action * Action that was selected. */ void UserInputModeViewContextMenu::parcelChartableDataActionSelected(QAction* action) { void* pointer = action->data().value(); ParcelConnectivity* pc = (ParcelConnectivity*)pointer; std::vector nodeIndices; std::vector voxelIndices; switch (pc->parcelType) { case ParcelConnectivity::PARCEL_TYPE_INVALID: break; case ParcelConnectivity::PARCEL_TYPE_SURFACE_NODES: pc->getNodeIndices(nodeIndices); if (nodeIndices.empty()) { WuQMessageBox::errorOk(this, "No vertices match label " + pc->labelName); return; } if (pc->chartingDataManager->hasNetworkFiles()) { if (warnIfNetworkBrainordinateCountIsLarge(nodeIndices.size()) == false) { return; } } break; case ParcelConnectivity::PARCEL_TYPE_VOLUME_VOXELS: pc->getVoxelIndices(voxelIndices); if (voxelIndices.empty()) { WuQMessageBox::errorOk(this, "No voxels match label " + pc->labelName); return; } if (pc->chartingDataManager->hasNetworkFiles()) { if (warnIfNetworkBrainordinateCountIsLarge(voxelIndices.size()) == false) { return; } } break; } CursorDisplayScoped cursor; cursor.showWaitCursor(); try { ProgressReportingDialog progressDialog("Data Series Within Parcel", "", this); progressDialog.setValue(0); switch (pc->parcelType) { case ParcelConnectivity::PARCEL_TYPE_INVALID: break; case ParcelConnectivity::PARCEL_TYPE_SURFACE_NODES: pc->chartingDataManager->loadAverageChartForSurfaceNodes(pc->surface, nodeIndices); break; case ParcelConnectivity::PARCEL_TYPE_VOLUME_VOXELS: cursor.restoreCursor(); WuQMessageBox::errorOk(this, "Charting of voxel average has not been implemented."); break; } } catch (const DataFileException& e) { cursor.restoreCursor(); WuQMessageBox::errorOk(this, e.whatString()); } EventManager::get()->sendEvent(EventUserInterfaceUpdate().getPointer()); EventManager::get()->sendEvent(EventGraphicsUpdateAllWindows().getPointer()); } /** * Called when border timeseries is selected. */ void UserInputModeViewContextMenu::borderDataSeriesSelected() { SelectionItemBorderSurface* borderID = this->selectionManager->getSurfaceBorderIdentification(); Border* border = borderID->getBorder(); Surface* surface = borderID->getSurface(); const int32_t numberOfNodes = surface->getNumberOfNodes(); LabelFile labelFile; labelFile.setNumberOfNodesAndColumns(numberOfNodes, 1); const int32_t labelKey = labelFile.getLabelTable()->addLabel("TempLabel", 1.0f, 1.0f, 1.0f, 1.0f); const int32_t mapIndex = 0; try { AlgorithmNodesInsideBorder algorithmInsideBorder(NULL, surface, border, false, mapIndex, labelKey, &labelFile); std::vector nodeIndices; nodeIndices.reserve(numberOfNodes); for (int32_t i = 0; i < numberOfNodes; i++) { if (labelFile.getLabelKey(i, mapIndex) == labelKey) { nodeIndices.push_back(i); } } if (nodeIndices.empty()) { WuQMessageBox::errorOk(this, "No vertices found inside border " + border->getName()); return; } if (borderID->getBrain()->getChartingDataManager()->hasNetworkFiles()) { if (warnIfNetworkBrainordinateCountIsLarge(nodeIndices.size()) == false) { return; } } CursorDisplayScoped cursor; cursor.showWaitCursor(); try { ProgressReportingDialog progressDialog("Data Series Within Border", "", this); progressDialog.setValue(0); ChartingDataManager* chartingDataManager = borderID->getBrain()->getChartingDataManager(); chartingDataManager->loadAverageChartForSurfaceNodes(surface, nodeIndices); } catch (const DataFileException& e) { cursor.restoreCursor(); WuQMessageBox::errorOk(this, e.whatString()); } } catch (const AlgorithmException& e) { WuQMessageBox::errorOk(this, e.whatString()); } EventManager::get()->sendEvent(EventUserInterfaceUpdate().getPointer()); EventManager::get()->sendEvent(EventGraphicsUpdateAllWindows().getPointer()); } /** * @return If no data series graphs are enabled, enable all of them and return * true. Else, return false. */ bool UserInputModeViewContextMenu::enableDataSeriesGraphsIfNoneEnabled() { Brain* brain = GuiManager::get()->getBrain(); std::vector chartFiles; brain->getAllChartableBrainordinateDataFiles(chartFiles); if (chartFiles.empty()) { return false; } const int32_t tabIndex = this->browserTabContent->getTabNumber(); /* * Exit if any data series graph is enabled. */ for (std::vector::iterator iter = chartFiles.begin(); iter != chartFiles.end(); iter++) { ChartableLineSeriesBrainordinateInterface* chartFile = *iter; if (chartFile->isLineSeriesChartingEnabled(tabIndex)) { return false; } } /* * Enable and display all data series graphs. */ for (std::vector::iterator iter = chartFiles.begin(); iter != chartFiles.end(); iter++) { ChartableLineSeriesBrainordinateInterface* chartFile = *iter; chartFile->setLineSeriesChartingEnabled(tabIndex, true); } return true; } /** * Called to display identication information on the surface border. */ void UserInputModeViewContextMenu::identifySurfaceBorderSelected() { SelectionItemBorderSurface* borderID = this->selectionManager->getSurfaceBorderIdentification(); Brain* brain = borderID->getBrain(); this->selectionManager->clearOtherSelectedItems(borderID); const AString idMessage = this->selectionManager->getIdentificationText(brain); IdentificationManager* idManager = brain->getIdentificationManager(); idManager->addIdentifiedItem(new IdentifiedItem(idMessage)); EventManager::get()->sendEvent(EventUpdateInformationWindows().getPointer()); } /** * Called to create a focus at a node location */ void UserInputModeViewContextMenu::createSurfaceFocusSelected() { SelectionItemSurfaceNode* surfaceID = this->selectionManager->getSurfaceNodeIdentification(); const Surface* surface = surfaceID->getSurface(); const int32_t nodeIndex = surfaceID->getNodeNumber(); const float* xyz = surface->getCoordinate(nodeIndex); const AString focusName = (StructureEnum::toGuiName(surface->getStructure()) + " Vertex " + AString::number(nodeIndex)); const AString comment = ("Created from " + focusName); Focus* focus = new Focus(); focus->setName(focusName); focus->getProjection(0)->setStereotaxicXYZ(xyz); focus->setComment(comment); FociPropertiesEditorDialog::createFocus(focus, this->browserTabContent, this->parentOpenGLWidget); } /** * Called to create a focus at a node location */ void UserInputModeViewContextMenu::createSurfaceIDSymbolFocusSelected() { SelectionItemSurfaceNodeIdentificationSymbol* nodeSymbolID = this->selectionManager->getSurfaceNodeIdentificationSymbol(); const Surface* surface = nodeSymbolID->getSurface(); const int32_t nodeIndex = nodeSymbolID->getNodeNumber(); const float* xyz = surface->getCoordinate(nodeIndex); const AString focusName = (StructureEnum::toGuiName(surface->getStructure()) + " Vertex " + AString::number(nodeIndex)); const AString comment = ("Created from " + focusName); Focus* focus = new Focus(); focus->setName(focusName); focus->getProjection(0)->setStereotaxicXYZ(xyz); focus->setComment(comment); FociPropertiesEditorDialog::createFocus(focus, this->browserTabContent, this->parentOpenGLWidget); } /** * Called to create a focus at a voxel location */ void UserInputModeViewContextMenu::createVolumeFocusSelected() { SelectionItemVoxel* voxelID = this->selectionManager->getVoxelIdentification(); const VolumeMappableInterface* vf = voxelID->getVolumeFile(); int64_t ijk[3]; voxelID->getVoxelIJK(ijk); float xyz[3]; vf->indexToSpace(ijk, xyz); const CaretMappableDataFile* cmdf = dynamic_cast(vf); const AString focusName = (cmdf->getFileNameNoPath() + " IJK (" + AString::fromNumbers(ijk, 3, ",") + ")"); const AString comment = ("Created from " + focusName); Focus* focus = new Focus(); focus->setName(focusName); focus->getProjection(0)->setStereotaxicXYZ(xyz); focus->setComment(comment); FociPropertiesEditorDialog::createFocus(focus, this->browserTabContent, this->parentOpenGLWidget); } /** * Called to display identication information on the surface focus. */ void UserInputModeViewContextMenu::identifySurfaceFocusSelected() { SelectionItemFocusSurface* focusID = this->selectionManager->getSurfaceFocusIdentification(); Brain* brain = focusID->getBrain(); this->selectionManager->clearOtherSelectedItems(focusID); const AString idMessage = this->selectionManager->getIdentificationText(brain); IdentificationManager* idManager = brain->getIdentificationManager(); idManager->addIdentifiedItem(new IdentifiedItem(idMessage)); EventManager::get()->sendEvent(EventUpdateInformationWindows().getPointer()); } /** * Called to display identication information on the volume focus. */ void UserInputModeViewContextMenu::identifyVolumeFocusSelected() { SelectionItemFocusVolume* focusID = this->selectionManager->getVolumeFocusIdentification(); Brain* brain = focusID->getBrain(); this->selectionManager->clearOtherSelectedItems(focusID); const AString idMessage = this->selectionManager->getIdentificationText(brain); IdentificationManager* idManager = brain->getIdentificationManager(); idManager->addIdentifiedItem(new IdentifiedItem(idMessage)); EventManager::get()->sendEvent(EventUpdateInformationWindows().getPointer()); } /** * Called to edit the surface focus. */ void UserInputModeViewContextMenu::editSurfaceFocusSelected() { SelectionItemFocusSurface* focusID = this->selectionManager->getSurfaceFocusIdentification(); Focus* focus = focusID->getFocus(); FociFile* fociFile = focusID->getFociFile(); FociPropertiesEditorDialog::editFocus(fociFile, focus, this->parentOpenGLWidget); } /** * Called to edit the volume focus. */ void UserInputModeViewContextMenu::editVolumeFocusSelected() { SelectionItemFocusVolume* focusID = this->selectionManager->getVolumeFocusIdentification(); Focus* focus = focusID->getFocus(); FociFile* fociFile = focusID->getFociFile(); FociPropertiesEditorDialog::editFocus(fociFile, focus, this->parentOpenGLWidget); } /** * Called to display identication information on the surface border. */ void UserInputModeViewContextMenu::identifySurfaceNodeSelected() { GuiManager::get()->processIdentification(this->browserTabContent->getTabNumber(), this->selectionManager, this->parentOpenGLWidget); } /** * Called to display identication information on the surface border. */ void UserInputModeViewContextMenu::identifyVoxelSelected() { SelectionItemVoxel* voxelID = this->selectionManager->getVoxelIdentification(); Brain* brain = voxelID->getBrain(); this->selectionManager->clearOtherSelectedItems(voxelID); const AString idMessage = this->selectionManager->getIdentificationText(brain); IdentificationManager* idManager = brain->getIdentificationManager(); idManager->addIdentifiedItem(new IdentifiedItem(idMessage)); EventManager::get()->sendEvent(EventUpdateInformationWindows().getPointer()); } /** * Called to remove all node identification symbols. */ void UserInputModeViewContextMenu::removeAllNodeIdentificationSymbolsSelected() { IdentificationManager* idManager = GuiManager::get()->getBrain()->getIdentificationManager(); idManager->removeAllIdentifiedItems(); EventManager::get()->sendEvent(EventGraphicsUpdateAllWindows().getPointer()); } /** * Called to remove node identification symbol from node. */ void UserInputModeViewContextMenu::removeNodeIdentificationSymbolSelected() { SelectionItemSurfaceNodeIdentificationSymbol* idSymbol = selectionManager->getSurfaceNodeIdentificationSymbol(); if (idSymbol->isValid()) { Surface* surface = idSymbol->getSurface(); IdentificationManager* idManager = GuiManager::get()->getBrain()->getIdentificationManager(); idManager->removeIdentifiedNodeItem(surface->getStructure(), surface->getNumberOfNodes(), idSymbol->getNodeNumber()); EventManager::get()->sendEvent(EventGraphicsUpdateAllWindows().getPointer()); } } /** * This is called when the data file is on the network to warn users if the * query is very large and may take a long time. * * @param numberOfBrainordinatesInROI * Number of brainordinates in the ROI. */ bool UserInputModeViewContextMenu::warnIfNetworkBrainordinateCountIsLarge(const int64_t numberOfBrainordinatesInROI) { if (numberOfBrainordinatesInROI < 200) { return true; } const QString msg = ("There are " + QString::number(numberOfBrainordinatesInROI) + " brainordinates in the selected region. Loading data from the network for " + "this quantity of brainordinates may take a very long time."); const bool result = WuQMessageBox::warningYesNo(this, "Do you want to continue?", msg); return result; } /* ------------------------------------------------------------------------- */ /** * Constructor. */ UserInputModeViewContextMenu::ParcelConnectivity::ParcelConnectivity(Brain* brain, const ParcelType parcelType, CaretMappableDataFile* mappableLabelFile, const int32_t labelFileMapIndex, const int32_t labelKey, const QString& labelName, Surface* surface, const int32_t nodeNumber, const int64_t volumeDimensions[3], ChartingDataManager* chartingDataManager, CiftiConnectivityMatrixDataFileManager* ciftiConnectivityManager, CiftiFiberTrajectoryManager* ciftiFiberTrajectoryManager) { this->brain = brain; this->parcelType = parcelType; this->mappableLabelFile = mappableLabelFile; this->labelFileMapIndex = labelFileMapIndex; this->labelKey = labelKey; this->labelName = labelName; this->surface = surface; this->nodeNumber = nodeNumber; this->volumeDimensions[0] = volumeDimensions[0]; this->volumeDimensions[1] = volumeDimensions[1]; this->volumeDimensions[2] = volumeDimensions[2]; this->chartingDataManager = chartingDataManager; this->ciftiConnectivityManager = ciftiConnectivityManager; this->ciftiFiberTrajectoryManager = ciftiFiberTrajectoryManager; } /** * Destructor. */ UserInputModeViewContextMenu::ParcelConnectivity::~ParcelConnectivity() { } /** * Get the node indices comprising the parcel. * * @param nodeIndicesOut * Contains node indices upon exit. */ void UserInputModeViewContextMenu::ParcelConnectivity::getNodeIndices(std::vector& nodeIndicesOut) const { nodeIndicesOut.clear(); if (this->parcelType != PARCEL_TYPE_SURFACE_NODES) { return; } CiftiBrainordinateLabelFile* ciftiLabelFile = dynamic_cast(mappableLabelFile); LabelFile* labelFile = dynamic_cast(mappableLabelFile); if (ciftiLabelFile != NULL) { ciftiLabelFile->getNodeIndicesWithLabelKey(surface->getStructure(), surface->getNumberOfNodes(), labelFileMapIndex, labelKey, nodeIndicesOut); } else if (labelFile != NULL) { labelFile->getNodeIndicesWithLabelKey(labelFileMapIndex, labelKey, nodeIndicesOut); } else { CaretAssertMessage(0, "Should never get here, new or invalid label file type for surface nodes"); } } /** * Get the voxel indices comprising the parcel * * @param voxelIndicesOut * Contains voxel indices upon exit. */ void UserInputModeViewContextMenu::ParcelConnectivity::getVoxelIndices(std::vector& voxelIndicesOut) const { voxelIndicesOut.clear(); if (this->parcelType != PARCEL_TYPE_VOLUME_VOXELS) { return; } CiftiBrainordinateLabelFile* ciftiLabelFile = dynamic_cast(mappableLabelFile); VolumeFile* volumeLabelFile = dynamic_cast(mappableLabelFile); if (ciftiLabelFile != NULL) { ciftiLabelFile->getVoxelIndicesWithLabelKey(labelFileMapIndex, labelKey, voxelIndicesOut); } else if (volumeLabelFile != NULL) { volumeLabelFile->getVoxelIndicesWithLabelKey(labelFileMapIndex, labelKey, voxelIndicesOut); } else { CaretAssertMessage(0, "Should never get here, new or invalid label file type for volume voxels"); } } connectome-workbench-1.2.3+git41-gc4c6c90/src/GuiQt/UserInputModeViewContextMenu.h000066400000000000000000000131271300200146000275420ustar00rootroot00000000000000#ifndef __USER_INPUT_MODE_VIEW_CONTEXT_MENU_H__ #define __USER_INPUT_MODE_VIEW_CONTEXT_MENU_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 "VolumeSliceViewPlaneEnum.h" #include "VoxelIJK.h" class QAction; namespace caret { class Brain; class BrainOpenGLWidget; class BrowserTabContent; class CaretMappableDataFile; class CiftiConnectivityMatrixDataFileManager; class CiftiFiberTrajectoryManager; class ChartingDataManager; class SelectionManager; class LabelFile; class Surface; class UserInputModeViewContextMenu : public QMenu { Q_OBJECT public: UserInputModeViewContextMenu(SelectionManager* selectionManager, BrowserTabContent* browserTabContent, BrainOpenGLWidget* parentOpenGLWidget); virtual ~UserInputModeViewContextMenu(); private slots: void createSurfaceFocusSelected(); void createSurfaceIDSymbolFocusSelected(); void createVolumeFocusSelected(); void editSurfaceFocusSelected(); void editVolumeFocusSelected(); void removeAllNodeIdentificationSymbolsSelected(); void removeNodeIdentificationSymbolSelected(); void identifySurfaceBorderSelected(); void identifySurfaceFocusSelected(); void identifyVolumeFocusSelected(); void identifySurfaceNodeSelected(); void identifyVoxelSelected(); void parcelCiftiFiberTrajectoryActionSelected(QAction* action); void parcelCiftiConnectivityActionSelected(QAction* action); void parcelChartableDataActionSelected(QAction* action); void borderCiftiConnectivitySelected(); void borderDataSeriesSelected(); private: class ParcelConnectivity { public: enum ParcelType { PARCEL_TYPE_INVALID, PARCEL_TYPE_SURFACE_NODES, PARCEL_TYPE_VOLUME_VOXELS }; ParcelConnectivity(Brain* brain, const ParcelType parcelType, CaretMappableDataFile* mappableLabelFile, const int32_t labelFileMapIndex, const int32_t labelKey, const QString& labelName, Surface* surface, const int32_t nodeNumber, const int64_t volumeDimensions[3], ChartingDataManager* chartingDataManager, CiftiConnectivityMatrixDataFileManager* ciftiConnectivityManager, CiftiFiberTrajectoryManager* ciftiFiberTrajectoryManager); virtual ~ParcelConnectivity(); void getNodeIndices(std::vector& nodeIndicesOut) const; void getVoxelIndices(std::vector& voxelIndicesOut) const; Brain* brain; ParcelType parcelType; CaretMappableDataFile* mappableLabelFile; int32_t labelFileMapIndex; int32_t labelKey; QString labelName; Surface* surface; int32_t nodeNumber; int64_t volumeDimensions[3]; CiftiConnectivityMatrixDataFileManager* ciftiConnectivityManager; ChartingDataManager* chartingDataManager; CiftiFiberTrajectoryManager* ciftiFiberTrajectoryManager; }; UserInputModeViewContextMenu(const UserInputModeViewContextMenu&); UserInputModeViewContextMenu& operator=(const UserInputModeViewContextMenu&); bool warnIfNetworkBrainordinateCountIsLarge(const int64_t numberOfBrainordinatesInROI); bool enableDataSeriesGraphsIfNoneEnabled(); void addIdentificationActions(); void addBorderRegionOfInterestActions(); void addFociActions(); void addLabelRegionOfInterestActions(); void addActionsToMenu(QList& actionsToAdd, const bool addSeparatorBeforeActions); BrainOpenGLWidget* parentOpenGLWidget; std::vector parcelConnectivities; SelectionManager* selectionManager; BrowserTabContent* browserTabContent; }; #ifdef __USER_INPUT_MODE_VIEW_CONTEXT_MENU_DECLARE__ // #endif // __USER_INPUT_MODE_VIEW_CONTEXT_MENU_DECLARE__ } // namespace #endif //__USER_INPUT_MODE_VIEW_CONTEXT_MENU_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/GuiQt/UserInputModeVolumeEdit.cxx000066400000000000000000000311461300200146000270670ustar00rootroot00000000000000 /*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 __USER_INPUT_MODE_VOLUME_EDIT_DECLARE__ #include "UserInputModeVolumeEdit.h" #undef __USER_INPUT_MODE_VOLUME_EDIT_DECLARE__ #include "Brain.h" #include "BrainOpenGLWidget.h" #include "BrainOpenGLViewportContent.h" #include "BrowserTabContent.h" #include "CaretAssert.h" #include "EventBrowserWindowContentGet.h" #include "EventGraphicsUpdateAllWindows.h" #include "EventManager.h" #include "EventUserInterfaceUpdate.h" #include "GiftiLabel.h" #include "GiftiLabelTable.h" #include "GuiManager.h" #include "MouseEvent.h" #include "Overlay.h" #include "OverlaySet.h" #include "SelectionItemVoxelEditing.h" #include "SelectionManager.h" #include "UserInputModeVolumeEditWidget.h" #include "VolumeFile.h" #include "VolumeFileEditorDelegate.h" #include "VolumeSliceViewPlaneEnum.h" #include "WuQMessageBox.h" using namespace caret; /** * \class caret::UserInputModeVolumeEdit * \brief User input processor for editing volume voxels * \ingroup GuiQt */ /** * Constructor. * * Index of window using this volume editor input handler. */ UserInputModeVolumeEdit::UserInputModeVolumeEdit(const int32_t windowIndex) : UserInputModeView(UserInputModeAbstract::VOLUME_EDIT), m_windowIndex(windowIndex) { m_inputModeVolumeEditWidget = new UserInputModeVolumeEditWidget(this, windowIndex); setWidgetForToolBar(m_inputModeVolumeEditWidget); } /** * Destructor. */ UserInputModeVolumeEdit::~UserInputModeVolumeEdit() { } /** * Called when 'this' user input receiver is set * to receive events. */ void UserInputModeVolumeEdit::initialize() { m_inputModeVolumeEditWidget->updateWidget(); } /** * Called when 'this' user input receiver is no * longer set to receive events. */ void UserInputModeVolumeEdit::finish() { } /** * Called to update the input receiver for various events. */ void UserInputModeVolumeEdit::update() { m_inputModeVolumeEditWidget->updateWidget(); } /** * Process a mouse event for editing the voxels. * * @param mouseEvent * Mouse event information. */ void UserInputModeVolumeEdit::processEditCommandFromMouse(const MouseEvent& mouseEvent) { if (mouseEvent.getViewportContent() == NULL) { return; } VolumeEditInfo volumeEditInfo; if ( ! getVolumeEditInfo(volumeEditInfo)) { return; } BrainOpenGLWidget* openGLWidget = mouseEvent.getOpenGLWidget(); const int mouseX = mouseEvent.getX(); const int mouseY = mouseEvent.getY(); SelectionManager* idManager = GuiManager::get()->getBrain()->getSelectionManager(); SelectionItemVoxelEditing* idEditVoxel = idManager->getVoxelEditingIdentification(); idEditVoxel->setVolumeFileForEditing(volumeEditInfo.m_volumeFile); idManager = openGLWidget->performIdentificationVoxelEditing(volumeEditInfo.m_volumeFile, mouseX, mouseY); if ((volumeEditInfo.m_volumeFile == idEditVoxel->getVolumeFile()) && idEditVoxel->isValid()) { int64_t ijk[3]; idEditVoxel->getVoxelIJK(ijk); VolumeEditingModeEnum::Enum editMode = VolumeEditingModeEnum::VOLUME_EDITING_MODE_ON; int32_t brushSizes[3] = { 0, 0, 0 }; float paletteMappedValue = 0; AString labelMappedName; m_inputModeVolumeEditWidget->getEditingParameters(editMode, brushSizes, paletteMappedValue, labelMappedName); const int64_t brushSizesInt64[3] = { brushSizes[0], brushSizes[1], brushSizes[2] }; VolumeFileEditorDelegate* editor = volumeEditInfo.m_volumeFileEditorDelegate; CaretAssert(editor); const VolumeSliceViewPlaneEnum::Enum slicePlane = volumeEditInfo.m_sliceViewPlane; const VolumeSliceProjectionTypeEnum::Enum sliceProjectionType = volumeEditInfo.m_sliceProjectionType; const Matrix4x4 obliqueRotationMatrix = volumeEditInfo.m_obliqueRotationMatrix; bool successFlag = true; AString errorMessage; float voxelValueOn = paletteMappedValue; float voxelValueOff = 0.0; float voxelDiffXYZ[3]; idEditVoxel->getVoxelDiffXYZ(voxelDiffXYZ); if (volumeEditInfo.m_volumeFile->isMappedWithLabelTable()) { const GiftiLabelTable* labelTable = volumeEditInfo.m_volumeFile->getMapLabelTable(volumeEditInfo.m_mapIndex); const GiftiLabel* label = labelTable->getLabel(labelMappedName); if (label != NULL) { voxelValueOn = label->getKey(); } else { errorMessage = ("Label name " + labelMappedName + " is not in label table."); successFlag = false; } const GiftiLabel* unassignedLabel = labelTable->getLabel(labelTable->getUnassignedLabelKey()); if (unassignedLabel != NULL) { voxelValueOff = unassignedLabel->getKey(); } } if (successFlag) { successFlag = editor->performEditingOperation(volumeEditInfo.m_mapIndex, editMode, slicePlane, sliceProjectionType, obliqueRotationMatrix, voxelDiffXYZ, ijk, brushSizesInt64, voxelValueOn, voxelValueOff, errorMessage); } if ( ! successFlag) { WuQMessageBox::errorOk(m_inputModeVolumeEditWidget, errorMessage); } updateGraphicsAfterEditing(volumeEditInfo.m_volumeFile, volumeEditInfo.m_mapIndex); } } /** * Process a mouse left click event. * * @param mouseEvent * Mouse event information. */ void UserInputModeVolumeEdit::mouseLeftClick(const MouseEvent& mouseEvent) { processEditCommandFromMouse(mouseEvent); } /** * Process a mouse left drag with ctrl and shift keys down event. * * @param mouseEvent * Mouse event information. */ void UserInputModeVolumeEdit::mouseLeftDragWithCtrlShift(const MouseEvent& mouseEvent) { processEditCommandFromMouse(mouseEvent); } /** * Show a context menu (pop-up menu at mouse location) * * @param mouseEvent * Mouse event information. * @param menuPosition * Point at which menu is displayed (passed to QMenu::exec()) * @param openGLWidget * OpenGL widget in which context menu is requested */ void UserInputModeVolumeEdit::showContextMenu(const MouseEvent& /*mouseEvent*/, const QPoint& /*menuPosition*/, BrainOpenGLWidget* /*openGLWidget*/) { /* no context menu */ } /** * Update the graphics after editing. * * @param volumeFile * Volume file that needs coloring update. * @param mapIndex * Index of the map. */ void UserInputModeVolumeEdit::updateGraphicsAfterEditing(VolumeFile* volumeFile, const int32_t mapIndex) { CaretAssert(volumeFile); CaretAssert((mapIndex >= 0) && (mapIndex < volumeFile->getNumberOfMaps())); volumeFile->clearVoxelColoringForMap(mapIndex); PaletteFile* paletteFile = GuiManager::get()->getBrain()->getPaletteFile(); volumeFile->updateScalarColoringForMap(mapIndex, paletteFile); EventManager::get()->sendEvent(EventGraphicsUpdateAllWindows().getPointer()); } /** * @return The cursor for display in the OpenGL widget. */ CursorEnum::Enum UserInputModeVolumeEdit::getCursor() const { CursorEnum::Enum cursor = CursorEnum::CURSOR_DEFAULT; // switch (m_mode) { // case MODE_CREATE: // break; // case MODE_EDIT: // cursor = CursorEnum::CURSOR_POINTING_HAND; // switch (m_editOperation) { // case EDIT_OPERATION_DELETE: // cursor = CursorEnum::CURSOR_CROSS; // break; // case EDIT_OPERATION_PROPERTIES: // cursor = CursorEnum::CURSOR_WHATS_THIS; // break; // } // break; // case MODE_OPERATIONS: // cursor = CursorEnum::CURSOR_POINTING_HAND; // break; // } return cursor; } /** * Get information about volume data being edited. * * @param volumeEditInfo * Loaded with editing information upon sucess. * @return * True if all ofvolumeEditInfo is valid, else false. Even * if false the overlay and model volume MAY be valid (non-NULL). */ bool UserInputModeVolumeEdit::getVolumeEditInfo(VolumeEditInfo& volumeEditInfo) { volumeEditInfo.m_topOverlay = NULL; volumeEditInfo.m_volumeOverlay = NULL; volumeEditInfo.m_volumeFile = NULL; volumeEditInfo.m_mapIndex = -1; volumeEditInfo.m_sliceViewPlane = VolumeSliceViewPlaneEnum::ALL; volumeEditInfo.m_sliceProjectionType = VolumeSliceProjectionTypeEnum::VOLUME_SLICE_PROJECTION_ORTHOGONAL; volumeEditInfo.m_obliqueRotationMatrix.identity(); // volumeEditInfo.m_modelVolume = NULL; EventBrowserWindowContentGet windowEvent(m_windowIndex); EventManager::get()->sendEvent(windowEvent.getPointer()); BrowserTabContent* tabContent = windowEvent.getSelectedBrowserTabContent(); if (tabContent != NULL) { ModelVolume* modelVolume = tabContent->getDisplayedVolumeModel(); ModelWholeBrain* modelWholeBrain = tabContent->getDisplayedWholeBrainModel(); if ((modelVolume != NULL) || (modelWholeBrain != NULL)) { OverlaySet* overlaySet = tabContent->getOverlaySet(); const int32_t numOverlays = overlaySet->getNumberOfDisplayedOverlays(); for (int32_t i = 0; i < numOverlays; i++) { Overlay* overlay = overlaySet->getOverlay(i); if (i == 0) { volumeEditInfo.m_topOverlay = overlay; } if (overlay->isEnabled()) { CaretMappableDataFile* mapFile = NULL; int32_t mapIndex; overlay->getSelectionData(mapFile, mapIndex); if (mapFile != NULL) { VolumeFile* vf = dynamic_cast(mapFile); if (vf != NULL) { volumeEditInfo.m_volumeOverlay = overlay; volumeEditInfo.m_volumeFile = vf; volumeEditInfo.m_mapIndex = mapIndex; volumeEditInfo.m_sliceViewPlane = tabContent->getSliceViewPlane(); volumeEditInfo.m_sliceProjectionType = tabContent->getSliceProjectionType(); volumeEditInfo.m_volumeFileEditorDelegate = vf->getVolumeFileEditorDelegate(); volumeEditInfo.m_obliqueRotationMatrix = tabContent->getObliqueVolumeRotationMatrix(); return true; } } } } } } return false; } connectome-workbench-1.2.3+git41-gc4c6c90/src/GuiQt/UserInputModeVolumeEdit.h000066400000000000000000000100221300200146000265020ustar00rootroot00000000000000#ifndef __USER_INPUT_MODE_VOLUME_EDIT_H__ #define __USER_INPUT_MODE_VOLUME_EDIT_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 "Matrix4x4.h" #include "UserInputModeView.h" #include "VolumeSliceViewPlaneEnum.h" #include "VolumeSliceProjectionTypeEnum.h" namespace caret { class ModelVolume; class Overlay; class VolumeFile; class VolumeFileEditorDelegate; class UserInputModeVolumeEditWidget; class UserInputModeVolumeEdit : public UserInputModeView { public: /** * Contains information regarding the volume file that is * being edited. */ struct VolumeEditInfo { /** The top-most overlay in the tab */ Overlay* m_topOverlay; /** The overlay containing the volume file */ Overlay* m_volumeOverlay; /** Model volume containing the volume file */ //ModelVolume* m_modelVolume; /** The volume file being edited */ VolumeFile* m_volumeFile; /** Index of the map in the volume file being edited */ int32_t m_mapIndex; /** The current slice view plane */ VolumeSliceViewPlaneEnum::Enum m_sliceViewPlane; /** Slice projection type (orthogonal/oblique) */ VolumeSliceProjectionTypeEnum::Enum m_sliceProjectionType; /** The volume's editor delegate */ VolumeFileEditorDelegate* m_volumeFileEditorDelegate; /** The oblique projection rotation matrix */ Matrix4x4 m_obliqueRotationMatrix; }; UserInputModeVolumeEdit(const int32_t windowIndex); virtual ~UserInputModeVolumeEdit(); virtual void initialize(); virtual void finish(); virtual void update(); virtual CursorEnum::Enum getCursor() const; virtual void mouseLeftClick(const MouseEvent& mouseEvent); virtual void mouseLeftDragWithCtrlShift(const MouseEvent& mouseEvent); virtual void showContextMenu(const MouseEvent& mouseEvent, const QPoint& menuPosition, BrainOpenGLWidget* openGLWidget); bool getVolumeEditInfo(VolumeEditInfo& volumeEditInfo); void updateGraphicsAfterEditing(VolumeFile* volumeFile, const int32_t mapIndex); // ADD_NEW_METHODS_HERE private: UserInputModeVolumeEdit(const UserInputModeVolumeEdit&); UserInputModeVolumeEdit& operator=(const UserInputModeVolumeEdit&); void processEditCommandFromMouse(const MouseEvent& mouseEvent); const int32_t m_windowIndex; UserInputModeVolumeEditWidget* m_inputModeVolumeEditWidget; friend class UserInputModeVolumeEditWidget; // ADD_NEW_MEMBERS_HERE }; #ifdef __USER_INPUT_MODE_VOLUME_EDIT_DECLARE__ // #endif // __USER_INPUT_MODE_VOLUME_EDIT_DECLARE__ } // namespace #endif //__USER_INPUT_MODE_VOLUME_EDIT_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/GuiQt/UserInputModeVolumeEditWidget.cxx000066400000000000000000000615171300200146000302400ustar00rootroot00000000000000 /*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 __USER_INPUT_MODE_VOLUME_EDIT_WIDGET_DECLARE__ #include "UserInputModeVolumeEditWidget.h" #undef __USER_INPUT_MODE_VOLUME_EDIT_WIDGET_DECLARE__ #include #include #include #include #include #include #include #include #include "Brain.h" #include "CaretAssert.h" #include "CaretLogger.h" #include "EventDataFileAdd.h" #include "EventManager.h" #include "EventUpdateVolumeEditingToolBar.h" #include "EventUserInterfaceUpdate.h" #include "GiftiLabel.h" #include "GiftiLabelTableEditor.h" #include "GuiManager.h" #include "Overlay.h" #include "VolumeFile.h" #include "VolumeFileEditorDelegate.h" #include "VolumeFileCreateDialog.h" #include "WuQDataEntryDialog.h" #include "WuQFactory.h" #include "WuQMessageBox.h" #include "WuQSpinBoxOddValue.h" #include "WuQtUtilities.h" using namespace caret; /** * \class caret::UserInputModeVolumeEditWidget * \brief User input widget for volume editing. * \ingroup GuiQt */ /** * Constructor. * @param inputModeVolumeEdit * Process of mouse input for volume editing * @param windowIndex * Index of browser window * @param parent * Parent widget */ UserInputModeVolumeEditWidget::UserInputModeVolumeEditWidget(UserInputModeVolumeEdit* inputModeVolumeEdit, const int32_t windowIndex, QWidget* parent) : QWidget(parent), EventListenerInterface(), m_inputModeVolumeEdit(inputModeVolumeEdit), m_windowIndex(windowIndex) { CaretAssert(inputModeVolumeEdit); QVBoxLayout* layout = new QVBoxLayout(this); WuQtUtilities::setLayoutSpacingAndMargins(layout, 2, 0); layout->addWidget(createSelectionToolBar()); layout->addWidget(createModeToolBar()); setSizePolicy(sizePolicy().horizontalPolicy(), QSizePolicy::Fixed); EventManager::get()->addEventListener(this, EventTypeEnum::EVENT_UPDATE_VOLUME_EDITING_TOOLBAR); } /** * Destructor. */ UserInputModeVolumeEditWidget::~UserInputModeVolumeEditWidget() { EventManager::get()->removeAllEventsFromListener(this); } /** * Receive an event * * @param event * The event. */ void UserInputModeVolumeEditWidget::receiveEvent(Event* event) { if (event->getEventType() == EventTypeEnum::EVENT_UPDATE_VOLUME_EDITING_TOOLBAR) { EventUpdateVolumeEditingToolBar* editVolEvent = dynamic_cast(event); CaretAssert(editVolEvent); editVolEvent->setEventProcessed(); updateWidget(); } } /** * Update the widget. */ void UserInputModeVolumeEditWidget::updateWidget() { bool isValid = false; UserInputModeVolumeEdit::VolumeEditInfo volumeEditInfo; if (m_inputModeVolumeEdit->getVolumeEditInfo(volumeEditInfo)) { m_lockAction->setChecked(volumeEditInfo.m_volumeFileEditorDelegate->isLocked(volumeEditInfo.m_mapIndex)); if (volumeEditInfo.m_volumeFile != NULL) { if (volumeEditInfo.m_volumeFile->isMappedWithLabelTable()) { m_voxelLabelValueToolButton->setVisible(true); m_voxelFloatValueSpinBox->setVisible(false); AString buttonText = m_voxelLabelValueAction->text(); GiftiLabelTable* labelTable = volumeEditInfo.m_volumeFile->getMapLabelTable(volumeEditInfo.m_mapIndex); if (labelTable != NULL) { GiftiLabel* label = labelTable->getLabel(buttonText); /* * Is there a label in the label table whose name * matches the name in the button's actoin? */ if (label == NULL) { /* * Default to the unassigned label */ const int32_t unassignedKey = labelTable->getUnassignedLabelKey(); GiftiLabel* unassignedLabel = labelTable->getLabel(unassignedKey); if (unassignedLabel != NULL) { buttonText = unassignedLabel->getName(); } /* * Find the first label that is not the unassigned * label */ const std::set keys = labelTable->getKeys(); for (std::set::iterator iter = keys.begin(); iter != keys.end(); iter++) { const int32_t key = *iter; if (key != unassignedKey) { GiftiLabel* keyLabel = labelTable->getLabel(key); if (keyLabel != NULL) { buttonText = keyLabel->getName(); break; } } } } } m_voxelLabelValueAction->setText(buttonText); isValid = true; } else if (volumeEditInfo.m_volumeFile->isMappedWithPalette()) { m_voxelLabelValueToolButton->setVisible(false); m_voxelFloatValueSpinBox->setVisible(true); isValid = true; } else if (volumeEditInfo.m_volumeFile->isMappedWithRGBA()) { /* nothing */ } else { CaretAssert(0); } m_addMapsToolButton->defaultAction()->setEnabled(isValid); const bool orthogonalFlag = (volumeEditInfo.m_sliceProjectionType == VolumeSliceProjectionTypeEnum::VOLUME_SLICE_PROJECTION_ORTHOGONAL); QList modeActions = m_volumeEditModeActionGroup->actions(); const int32_t numModeActions = modeActions.size(); for (int32_t i = 0; i < numModeActions; i++) { QAction* action = modeActions.at(i); const int modeInt = action->data().toInt(); bool validFlag = false; VolumeEditingModeEnum::Enum mode = VolumeEditingModeEnum::fromIntegerCode(modeInt, &validFlag); CaretAssert(validFlag); bool modeEnabledFlag = false; if (orthogonalFlag) { modeEnabledFlag = true; } else { modeEnabledFlag = VolumeEditingModeEnum::isObliqueEditingAllowed(mode); } action->setEnabled(modeEnabledFlag); } } } this->setEnabled(isValid); } /** * @return Create and return the selection toolbar. */ QWidget* UserInputModeVolumeEditWidget::createSelectionToolBar() { QLabel* volumeLabel = new QLabel("Volume:"); m_newFileToolButton = new QToolButton(); m_newFileToolButton->setDefaultAction(WuQtUtilities::createAction("New", "Create a new volume file that will become the top-most overlay", this, this, SLOT(newFileActionTriggered()))); m_addMapsToolButton = new QToolButton(); m_addMapsToolButton->setDefaultAction(WuQtUtilities::createAction("Add", ("Add maps to the selected volume file.\n" "First new map will become the top-most overlay."), this, this, SLOT(addMapsActionTriggered()))); m_lockAction = WuQtUtilities::createAction("Lock", "Lock/unlock volume file to disallow/allow editing", this, this, SLOT(lockFileActionTriggered())); m_lockAction->setCheckable(true); QToolButton* lockFileToolButton = new QToolButton(); lockFileToolButton->setDefaultAction(m_lockAction); QLabel* editLabel = new QLabel("Edit:"); QToolButton* undoToolButton = new QToolButton(); undoToolButton->setDefaultAction(WuQtUtilities::createAction("Undo", "Undo the last volume edit", this, this, SLOT(undoActionTriggered()))); QToolButton* redoToolButton = new QToolButton(); redoToolButton->setDefaultAction(WuQtUtilities::createAction("Redo", "Redo (or is it undo) the last undo", this, this, SLOT(redoActionTriggered()))); QToolButton* resetToolButton = new QToolButton(); resetToolButton->setDefaultAction(WuQtUtilities::createAction("Reset", "Reset all edting of the volume", this, this, SLOT(resetActionTriggered()))); QLabel* brushSizeLabel = new QLabel("Brush Size:"); const int MIN_BRUSH_SIZE = 1; const int MAX_BRUSH_SIZE = 999; m_xBrushSizeSpinBox = new WuQSpinBoxOddValue(this); m_xBrushSizeSpinBox->setRange(MIN_BRUSH_SIZE, MAX_BRUSH_SIZE); QObject::connect(m_xBrushSizeSpinBox, SIGNAL(valueChanged(int)), this, SLOT(xBrushSizeValueChanged(int))); m_xBrushSizeSpinBox->getWidget()->setToolTip("Parasagittal brush size (voxels).\n" "Must be an odd value."); m_yBrushSizeSpinBox = new WuQSpinBoxOddValue(this); m_yBrushSizeSpinBox->setRange(MIN_BRUSH_SIZE, MAX_BRUSH_SIZE); QObject::connect(m_yBrushSizeSpinBox, SIGNAL(valueChanged(int)), this, SLOT(yBrushSizeValueChanged(int))); m_yBrushSizeSpinBox->getWidget()->setToolTip("Coronal brush size (voxels).\n" "Must be an odd value."); m_zBrushSizeSpinBox = new WuQSpinBoxOddValue(this); m_zBrushSizeSpinBox->setRange(MIN_BRUSH_SIZE, MAX_BRUSH_SIZE); QObject::connect(m_zBrushSizeSpinBox, SIGNAL(valueChanged(int)), this, SLOT(zBrushSizeValueChanged(int))); m_zBrushSizeSpinBox->getWidget()->setToolTip("Axial brush size (voxels).\n" "Must be an odd value."); m_voxelValueLabel = new QLabel("Value:"); m_voxelFloatValueSpinBox = WuQFactory::newDoubleSpinBoxWithMinMaxStepDecimalsSignalDouble(-1000.0, 1000.0, 1.0, 1, this, SLOT(voxelValueChanged(double))); m_voxelFloatValueSpinBox->setValue(1.0); m_voxelLabelValueAction = WuQtUtilities::createAction("XXX", "Choose Label for Voxels", this, this, SLOT(labelValueActionTriggered())); m_voxelLabelValueToolButton = new QToolButton(); m_voxelLabelValueToolButton->setDefaultAction(m_voxelLabelValueAction); const int SPACE = 10; QWidget* widget = new QWidget(); QHBoxLayout* layout = new QHBoxLayout(widget); WuQtUtilities::setLayoutSpacingAndMargins(layout, 2, 0); layout->addWidget(volumeLabel); layout->addWidget(m_newFileToolButton); layout->addWidget(m_addMapsToolButton); layout->addWidget(lockFileToolButton); layout->addSpacing(SPACE); layout->addWidget(editLabel); layout->addWidget(undoToolButton); layout->addWidget(redoToolButton); layout->addWidget(resetToolButton); layout->addSpacing(SPACE); layout->addWidget(brushSizeLabel); layout->addWidget(m_xBrushSizeSpinBox->getWidget()); layout->addWidget(m_yBrushSizeSpinBox->getWidget()); layout->addWidget(m_zBrushSizeSpinBox->getWidget()); layout->addSpacing(SPACE); layout->addWidget(m_voxelValueLabel); layout->addWidget(m_voxelFloatValueSpinBox); layout->addWidget(m_voxelLabelValueToolButton); layout->addStretch(); return widget; } /** * @return Create and return the mode toolbar. */ QWidget* UserInputModeVolumeEditWidget::createModeToolBar() { QWidget* widget = new QWidget(); QHBoxLayout* layout = new QHBoxLayout(widget); WuQtUtilities::setLayoutSpacingAndMargins(layout, 2, 0); QLabel* modeLabel = new QLabel("Mode:"); layout->addWidget(modeLabel); m_volumeEditModeActionGroup = new QActionGroup(this); m_volumeEditModeActionGroup->setExclusive(true); QObject::connect(m_volumeEditModeActionGroup, SIGNAL(triggered(QAction*)), this, SLOT(editingModeActionTriggered(QAction*))); std::vector editModes; VolumeEditingModeEnum::getAllEnums(editModes); bool firstActionFlag = true; for (std::vector::iterator iter = editModes.begin(); iter != editModes.end(); iter++) { const VolumeEditingModeEnum::Enum mode = *iter; const int modeInt = VolumeEditingModeEnum::toIntegerCode(mode); const AString modeName = VolumeEditingModeEnum::toGuiName(mode); const AString toolTip = WuQtUtilities::createWordWrappedToolTipText(VolumeEditingModeEnum::toToolTip(mode)); QAction* action = new QAction(modeName, this); action->setData(modeInt); action->setToolTip(toolTip); action->setCheckable(true); if (firstActionFlag) { firstActionFlag = false; action->setChecked(true); } m_volumeEditModeActionGroup->addAction(action); QToolButton* toolButton = new QToolButton(); toolButton->setDefaultAction(action); layout->addWidget(toolButton); } layout->addStretch(); return widget; } /** * Get the editing parameters from the toolbar. * * @param editingModeOut * Output containing the selected editing mode. * @param brushSizeOut * Brush IJK/XYZ sizes. * @param floatValueOut * Float value for palette mapped volume files. * @param labelNameOut * Label name for label mapped volume files. */ void UserInputModeVolumeEditWidget::getEditingParameters(VolumeEditingModeEnum::Enum& editingModeOut, int32_t brushSizesOut[3], float& floatValueOut, AString& labelNameOut) const { editingModeOut = getEditingMode(); brushSizesOut[0] = m_xBrushSizeSpinBox->value(); brushSizesOut[1] = m_yBrushSizeSpinBox->value(); brushSizesOut[2] = m_zBrushSizeSpinBox->value(); floatValueOut = m_voxelFloatValueSpinBox->value(); labelNameOut = m_voxelLabelValueAction->text(); } /** * @return The volume editing mode. */ VolumeEditingModeEnum::Enum UserInputModeVolumeEditWidget::getEditingMode() const { QAction* action = m_volumeEditModeActionGroup->checkedAction(); CaretAssert(action); const int modeInt = action->data().toInt(); bool validFlag = false; const VolumeEditingModeEnum::Enum editMode = VolumeEditingModeEnum::fromIntegerCode(modeInt, &validFlag); CaretAssert(validFlag); return editMode; } /** * Called when new volume file button is clicked. */ void UserInputModeVolumeEditWidget::newFileActionTriggered() { VolumeFileCreateDialog newVolumeDialog(m_newFileToolButton); if (newVolumeDialog.exec() == VolumeFileCreateDialog::Accepted) { VolumeFile* vf = newVolumeDialog.getVolumeFile(); if (vf != NULL) { for (int32_t i = 0; i < vf->getNumberOfMaps(); i++) { vf->getVolumeFileEditorDelegate()->setLocked(i, false); } EventDataFileAdd addFileEvent(vf); EventManager::get()->sendEvent(addFileEvent.getPointer()); const int32_t mapIndex = 0; UserInputModeVolumeEdit::VolumeEditInfo volumeEditInfo; m_inputModeVolumeEdit->getVolumeEditInfo(volumeEditInfo); if (volumeEditInfo.m_topOverlay != NULL) { volumeEditInfo.m_topOverlay->setSelectionData(vf, mapIndex); volumeEditInfo.m_topOverlay->setEnabled(true); volumeEditInfo.m_topOverlay->setMapYokingGroup(MapYokingGroupEnum::MAP_YOKING_GROUP_OFF); m_inputModeVolumeEdit->updateGraphicsAfterEditing(vf, mapIndex); } EventManager::get()->sendEvent(EventUserInterfaceUpdate().getPointer()); } } } /** * Called when the add maps action is triggered. */ void UserInputModeVolumeEditWidget::addMapsActionTriggered() { UserInputModeVolumeEdit::VolumeEditInfo volumeEditInfo; if (m_inputModeVolumeEdit->getVolumeEditInfo(volumeEditInfo)) { VolumeFile* vf = volumeEditInfo.m_volumeFile; WuQDataEntryDialog ded("Add Map to Volume File", m_addMapsToolButton); const int32_t newMapIndex = vf->getNumberOfMaps(); QLineEdit* nameLineEdit = ded.addLineEditWidget("Map Name"); nameLineEdit->setText("Editing (" + AString::number(newMapIndex + 1) + ")"); if (ded.exec() == WuQDataEntryDialog::Accepted) { /* * Add map, set the map name, * set the selected map to the new map, * update graphics, update user interface. */ vf->addSubvolumes(1); vf->setMapName(newMapIndex, nameLineEdit->text().trimmed()); vf->getVolumeFileEditorDelegate()->setLocked(newMapIndex, false); volumeEditInfo.m_volumeOverlay->setSelectionData(vf, newMapIndex); m_inputModeVolumeEdit->updateGraphicsAfterEditing(vf, newMapIndex); EventManager::get()->sendEvent(EventUserInterfaceUpdate().getPointer()); updateWidget(); } } } /** * Called when lock button is clicked. */ void UserInputModeVolumeEditWidget::lockFileActionTriggered() { UserInputModeVolumeEdit::VolumeEditInfo volumeEditInfo; if (m_inputModeVolumeEdit->getVolumeEditInfo(volumeEditInfo)) { volumeEditInfo.m_volumeFileEditorDelegate->setLocked(volumeEditInfo.m_mapIndex, m_lockAction->isChecked()); } } /** * Called when undo button is clicked. */ void UserInputModeVolumeEditWidget::undoActionTriggered() { UserInputModeVolumeEdit::VolumeEditInfo volumeEditInfo; if (m_inputModeVolumeEdit->getVolumeEditInfo(volumeEditInfo)) { AString errorMessage; if ( ! volumeEditInfo.m_volumeFileEditorDelegate->undo(volumeEditInfo.m_mapIndex, errorMessage)) { WuQMessageBox::errorOk(this, errorMessage); } m_inputModeVolumeEdit->updateGraphicsAfterEditing(volumeEditInfo.m_volumeFile, volumeEditInfo.m_mapIndex); } } /** * Called when redo button is clicked. */ void UserInputModeVolumeEditWidget::redoActionTriggered() { UserInputModeVolumeEdit::VolumeEditInfo volumeEditInfo; if (m_inputModeVolumeEdit->getVolumeEditInfo(volumeEditInfo)) { AString errorMessage; if ( ! volumeEditInfo.m_volumeFileEditorDelegate->redo(volumeEditInfo.m_mapIndex, errorMessage)) { WuQMessageBox::errorOk(this, errorMessage); } m_inputModeVolumeEdit->updateGraphicsAfterEditing(volumeEditInfo.m_volumeFile, volumeEditInfo.m_mapIndex); } } /** * Called when reset button is clicked. */ void UserInputModeVolumeEditWidget::resetActionTriggered() { UserInputModeVolumeEdit::VolumeEditInfo volumeEditInfo; if (m_inputModeVolumeEdit->getVolumeEditInfo(volumeEditInfo)) { AString errorMessage; if ( ! volumeEditInfo.m_volumeFileEditorDelegate->reset(volumeEditInfo.m_mapIndex, errorMessage)) { WuQMessageBox::errorOk(this, errorMessage); } m_inputModeVolumeEdit->updateGraphicsAfterEditing(volumeEditInfo.m_volumeFile, volumeEditInfo.m_mapIndex); } } /** * Called when X-size brush value is changed. */ void UserInputModeVolumeEditWidget::xBrushSizeValueChanged(int) { } /** * Called when X-size brush value is changed. */ void UserInputModeVolumeEditWidget::yBrushSizeValueChanged(int) { } /** * Called when X-size brush value is changed. */ void UserInputModeVolumeEditWidget::zBrushSizeValueChanged(int) { } /** * Called when voxel value is changed. */ void UserInputModeVolumeEditWidget::voxelValueChanged(double) { } /** * Called when voxel label value action is triggered. */ void UserInputModeVolumeEditWidget::labelValueActionTriggered() { UserInputModeVolumeEdit::VolumeEditInfo volumeEditInfo; if (m_inputModeVolumeEdit->getVolumeEditInfo(volumeEditInfo)) { if (volumeEditInfo.m_volumeFile != NULL) { GiftiLabelTableEditor lte(volumeEditInfo.m_volumeFile, volumeEditInfo.m_mapIndex, "Select Label", GiftiLabelTableEditor::OPTION_NONE, m_voxelLabelValueToolButton); const AString defaultLabelName = m_voxelLabelValueAction->text(); if ( ! defaultLabelName.isEmpty()) { lte.selectLabelWithName(defaultLabelName); } if (lte.exec() == GiftiLabelTableEditor::Accepted) { const AString selectedName = lte.getLastSelectedLabelName(); m_voxelLabelValueAction->setText(selectedName); } } } } /** * Called when an editing mode is selected. * * @param action * Editing action that was selected. */ void UserInputModeVolumeEditWidget::editingModeActionTriggered(QAction* action) { CaretAssert(action); const int modeInt = action->data().toInt(); bool validFlag = false; (void)VolumeEditingModeEnum::fromIntegerCode(modeInt, &validFlag); CaretAssert(validFlag); } connectome-workbench-1.2.3+git41-gc4c6c90/src/GuiQt/UserInputModeVolumeEditWidget.h000066400000000000000000000100741300200146000276550ustar00rootroot00000000000000#ifndef __USER_INPUT_MODE_VOLUME_EDIT_WIDGET_H__ #define __USER_INPUT_MODE_VOLUME_EDIT_WIDGET_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 "EventListenerInterface.h" #include "UserInputModeVolumeEdit.h" #include "VolumeEditingModeEnum.h" class QAction; class QActionGroup; class QDoubleSpinBox; class QLabel; class QSpinBox; class QToolButton; namespace caret { class UserInputModeVolumeEdit; class WuQSpinBoxOddValue; class UserInputModeVolumeEditWidget : public QWidget, public EventListenerInterface { Q_OBJECT public: UserInputModeVolumeEditWidget(UserInputModeVolumeEdit* inputModeVolumeEdit, const int32_t windowIndex, QWidget* parent = 0); virtual ~UserInputModeVolumeEditWidget(); void receiveEvent(Event* event); void updateWidget(); void getEditingParameters(VolumeEditingModeEnum::Enum& editingModeOut, int32_t brushSizesOut[3], float& floatValueOut, AString& labelNameOut) const; VolumeEditingModeEnum::Enum getEditingMode() const; // ADD_NEW_METHODS_HERE private slots: void newFileActionTriggered(); void lockFileActionTriggered(); void undoActionTriggered(); void redoActionTriggered(); void resetActionTriggered(); void xBrushSizeValueChanged(int); void yBrushSizeValueChanged(int); void zBrushSizeValueChanged(int); void voxelValueChanged(double); void labelValueActionTriggered(); void editingModeActionTriggered(QAction*); void addMapsActionTriggered(); private: UserInputModeVolumeEditWidget(const UserInputModeVolumeEditWidget&); UserInputModeVolumeEditWidget& operator=(const UserInputModeVolumeEditWidget&); QWidget* createSelectionToolBar(); QWidget* createModeToolBar(); QAction* m_lockAction; UserInputModeVolumeEdit* m_inputModeVolumeEdit; const int32_t m_windowIndex; QActionGroup* m_volumeEditModeActionGroup; QToolButton* m_newFileToolButton; WuQSpinBoxOddValue* m_xBrushSizeSpinBox; WuQSpinBoxOddValue* m_yBrushSizeSpinBox; WuQSpinBoxOddValue* m_zBrushSizeSpinBox; QLabel* m_voxelValueLabel; QAction* m_voxelLabelValueAction; QToolButton* m_voxelLabelValueToolButton; QDoubleSpinBox* m_voxelFloatValueSpinBox; QToolButton* m_addMapsToolButton; // ADD_NEW_MEMBERS_HERE }; #ifdef __USER_INPUT_MODE_VOLUME_EDIT_WIDGET_DECLARE__ // #endif // __USER_INPUT_MODE_VOLUME_EDIT_WIDGET_DECLARE__ } // namespace #endif //__USER_INPUT_MODE_VOLUME_EDIT_WIDGET_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/GuiQt/UsernamePasswordWidget.cxx000066400000000000000000000173321300200146000267750ustar00rootroot00000000000000 /*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 __USERNAME_PASSWORD_WIDGET_DECLARE__ #include "UsernamePasswordWidget.h" #undef __USERNAME_PASSWORD_WIDGET_DECLARE__ #include #include #include #include #include "CaretAssert.h" #include "CaretPreferences.h" #include "SessionManager.h" #include "WuQDialogModal.h" #include "WuQMessageBox.h" #include "WuQtUtilities.h" using namespace caret; /** * \class caret::UsernamePasswordWidget * \brief Widget for username and password with saving to preferences. * \ingroup GuiQt */ /** * Constructor. * * @param parent * Parent widget. */ UsernamePasswordWidget::UsernamePasswordWidget(QWidget* parent) : QWidget(parent) { QLabel* usernameLabel = new QLabel("User Name: "); m_usernameLineEdit = new QLineEdit(); m_usernameLineEdit->setFixedWidth(200); QLabel* passwordLabel = new QLabel("Password: "); m_passwordLineEdit = new QLineEdit(); m_passwordLineEdit->setFixedWidth(200); m_passwordLineEdit->setEchoMode(QLineEdit::Password); m_savePasswordToPreferencesCheckBox = new QCheckBox("Save Password to Preferences"); QObject::connect(m_savePasswordToPreferencesCheckBox, SIGNAL(clicked(bool)), this, SLOT(savePasswordToPreferencesClicked(bool))); int row = 0; QGridLayout* loginGridLayout = new QGridLayout(this); loginGridLayout->setColumnStretch(0, 0); loginGridLayout->setColumnStretch(1, 100); loginGridLayout->addWidget(usernameLabel, row, 0); loginGridLayout->addWidget(m_usernameLineEdit, row, 1); row++; loginGridLayout->addWidget(passwordLabel, row, 0); loginGridLayout->addWidget(m_passwordLineEdit, row, 1); row++; loginGridLayout->addWidget(WuQtUtilities::createHorizontalLineWidget(), row, 0, 1, 2); row++; loginGridLayout->addWidget(m_savePasswordToPreferencesCheckBox, row, 0, 1, 2, Qt::AlignLeft); row++; CaretPreferences* prefs = SessionManager::get()->getCaretPreferences(); if (s_previousUsernamePassword.m_firstTime) { s_previousUsernamePassword.m_firstTime = false; AString userName; AString password; prefs->getRemoteFileUserNameAndPassword(userName, password); s_previousUsernamePassword.m_username = userName; if (prefs->isRemoteFilePasswordSaved()) { s_previousUsernamePassword.m_password = password; } else { s_previousUsernamePassword.m_password = ""; } } m_usernameLineEdit->setText(s_previousUsernamePassword.m_username); m_passwordLineEdit->setText(s_previousUsernamePassword.m_password); m_savePasswordToPreferencesCheckBox->setChecked(prefs->isRemoteFilePasswordSaved()); } /** * Destructor. */ UsernamePasswordWidget::~UsernamePasswordWidget() { } /** * Popup a modal dialog for the username and password. * * @param parent * Parent on which dialog is displayed. * @param title * Title of dialog. * @param message * Optional message shown in dialog. * @param usernameOut * The username. * @param passwordOut * The password. * @return * True if the user pressed the OK button, else false. */ bool UsernamePasswordWidget::getUserNameAndPasswordInDialog(QWidget* parent, const AString& title, const AString& message, AString& usernameOut, AString& passwordOut) { WuQDialogModal dialog(title, parent); dialog.setWindowTitle(title); QWidget* dialogWidget = new QWidget(); QVBoxLayout* dialogLayout = new QVBoxLayout(dialogWidget); if (message.isEmpty() == false) { QLabel* messageLabel = new QLabel(message); messageLabel->setWordWrap(true); dialogLayout->addWidget(messageLabel); } UsernamePasswordWidget* userNameAndPasswordWidget = new UsernamePasswordWidget(); dialogLayout->addWidget(userNameAndPasswordWidget); dialog.setCentralWidget(dialogWidget, WuQDialog::SCROLL_AREA_NEVER); if (dialog.exec() == WuQDialogModal::Accepted) { userNameAndPasswordWidget->getUsernameAndPassword(usernameOut, passwordOut); return true; } return false; } /** * Get the username and password. If the both the username and password * are valid (returns true) and "Save Password to Preferences" is checked, * the username and password are written to the user's preferences. If it is * not checked, only user username is written to preferences. * * @param usernameOut * The username. * @param passwordOut * The password. * @return * True if both the username and password are non-empty, else false. */ bool UsernamePasswordWidget::getUsernameAndPassword(AString& usernameOut, AString& passwordOut) { usernameOut = m_usernameLineEdit->text().trimmed(); passwordOut = m_passwordLineEdit->text().trimmed(); if (usernameOut.isEmpty()) { return false; } else if (passwordOut.isEmpty()) { return false; } const bool savePassword = m_savePasswordToPreferencesCheckBox->isChecked(); CaretPreferences* prefs = SessionManager::get()->getCaretPreferences(); prefs->setRemoteFilePasswordSaved(savePassword); if (savePassword) { prefs->setRemoteFileUserNameAndPassword(usernameOut, passwordOut); } else { prefs->setRemoteFileUserNameAndPassword(usernameOut, ""); } s_previousUsernamePassword.m_username = usernameOut; s_previousUsernamePassword.m_password = passwordOut; return true; } /** * Called when save password to preferences checkbox value is changed * by the user. * * @param status * New status of save password to preferences checkbox. */ void UsernamePasswordWidget::savePasswordToPreferencesClicked(bool status) { if (status) { const QString msg = ("The Workbench preferences are stored in a file somewhere in your " "home directory and the location depends upon your operating " "system. This is not a secure file and it may be possible " "other users to access this file and find your " "open location password. Unchceck the box if you do not want " "your password saved within your preferences."); WuQMessageBox::informationOk(m_savePasswordToPreferencesCheckBox, WuQtUtilities::createWordWrappedToolTipText(msg)); } } connectome-workbench-1.2.3+git41-gc4c6c90/src/GuiQt/UsernamePasswordWidget.h000066400000000000000000000055001300200146000264140ustar00rootroot00000000000000#ifndef __USERNAME_PASSWORD_WIDGET_H__ #define __USERNAME_PASSWORD_WIDGET_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 "AString.h" class QLineEdit; class QCheckBox; namespace caret { class UsernamePasswordWidget : public QWidget { Q_OBJECT public: UsernamePasswordWidget(QWidget* parent = 0); virtual ~UsernamePasswordWidget(); bool getUsernameAndPassword(AString& usernameOut, AString& passwordOut); static bool getUserNameAndPasswordInDialog(QWidget* parent, const AString& title, const AString& message, AString& usernameOut, AString& passwordOut); private: UsernamePasswordWidget(const UsernamePasswordWidget&); UsernamePasswordWidget& operator=(const UsernamePasswordWidget&); private slots: void savePasswordToPreferencesClicked(bool); public: // ADD_NEW_METHODS_HERE private: class PreviousUsernamePassword { public: PreviousUsernamePassword() { m_username = ""; m_password = ""; m_firstTime = true; } AString m_username; AString m_password; bool m_firstTime; }; // ADD_NEW_MEMBERS_HERE QLineEdit* m_usernameLineEdit; QLineEdit* m_passwordLineEdit; QCheckBox* m_savePasswordToPreferencesCheckBox; static PreviousUsernamePassword s_previousUsernamePassword; }; #ifdef __USERNAME_PASSWORD_WIDGET_DECLARE__ UsernamePasswordWidget::PreviousUsernamePassword UsernamePasswordWidget::s_previousUsernamePassword; #endif // __USERNAME_PASSWORD_WIDGET_DECLARE__ } // namespace #endif //__USERNAME_PASSWORD_WIDGET_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/GuiQt/ViewModeEnum.cxx000066400000000000000000000140461300200146000246720ustar00rootroot00000000000000/*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 __VIEW_MODE_DECLARE__ #include "ViewModeEnum.h" #undef __VIEW_MODE_DECLARE__ using namespace caret; /** * Constructor. * * @param e * An enumerated value. * @param name * Name of enumberated value. */ ViewModeEnum::ViewModeEnum(const Enum e, const int32_t integerCode, const QString& name, const QString& guiName) { this->e = e; this->integerCode = integerCode; this->name = name; this->guiName = guiName; } /** * Destructor. */ ViewModeEnum::~ViewModeEnum() { } /** * Initialize the enumerated metadata. */ void ViewModeEnum::initialize() { if (initializedFlag) { return; } initializedFlag = true; enumData.push_back(ViewModeEnum(VIEW_MODE_INVALID, 0, "VIEW_MODE_INVALID", "Invalid")); enumData.push_back(ViewModeEnum(VIEW_MODE_SURFACE, 1, "VIEW_MODE_SURFACE", "Surface")); enumData.push_back(ViewModeEnum(VIEW_MODE_VOLUME_SLICES, 2, "VIEW_MODE_VOLUME_SLICES", "Volume")); enumData.push_back(ViewModeEnum(VIEW_MODE_WHOLE_BRAIN, 3, "VIEW_MODE_WHOLE_BRAIN", "Whole Brain")); } /** * Find the data for and enumerated value. * @param e * The enumerated value. * @return Pointer to data for this enumerated type * or NULL if no data for type or if type is invalid. */ const ViewModeEnum* ViewModeEnum::findData(const Enum e) { initialize(); size_t num = enumData.size(); for (size_t i = 0; i < num; i++) { const ViewModeEnum* d = &enumData[i]; if (d->e == e) { return d; } } return &enumData[0]; } /** * Get a string representation of the enumerated type. * @param e * Enumerated value. * @return * String representing enumerated value. */ QString ViewModeEnum::toName(Enum e) { initialize(); const ViewModeEnum* gaio = findData(e); return gaio->name; } /** * Get an enumerated value corresponding to its 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. */ ViewModeEnum::Enum ViewModeEnum::fromName(const QString& s, bool* isValidOut) { initialize(); bool validFlag = false; Enum e = VIEW_MODE_INVALID; for (std::vector::iterator iter = enumData.begin(); iter != enumData.end(); iter++) { const ViewModeEnum& d = *iter; if (d.name == s) { e = d.e; validFlag = true; break; } } if (isValidOut != 0) { *isValidOut = validFlag; } return e; } /** * Get a GUI string representation of the enumerated type. * @param e * Enumerated value. * @return * String representing enumerated value. */ QString ViewModeEnum::toGuiName(Enum e) { initialize(); const ViewModeEnum* gaio = findData(e); return gaio->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. */ ViewModeEnum::Enum ViewModeEnum::fromGuiName(const QString& s, bool* isValidOut) { initialize(); bool validFlag = false; Enum e = VIEW_MODE_INVALID; for (std::vector::iterator iter = enumData.begin(); iter != enumData.end(); iter++) { const ViewModeEnum& d = *iter; if (d.guiName == s) { e = d.e; validFlag = true; break; } } if (isValidOut != 0) { *isValidOut = validFlag; } return e; } /** * Get the integer code for a data type. * * @return * Integer code for data type. */ int32_t ViewModeEnum::toIntegerCode(Enum e) { initialize(); const ViewModeEnum* ndt = findData(e); return ndt->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. */ ViewModeEnum::Enum ViewModeEnum::fromIntegerCode(const int32_t integerCode, bool* isValidOut) { initialize(); bool validFlag = false; Enum e = VIEW_MODE_INVALID; for (std::vector::iterator iter = enumData.begin(); iter != enumData.end(); iter++) { const ViewModeEnum& ndt = *iter; if (ndt.integerCode == integerCode) { e = ndt.e; validFlag = true; break; } } if (isValidOut != 0) { *isValidOut = validFlag; } return e; } connectome-workbench-1.2.3+git41-gc4c6c90/src/GuiQt/ViewModeEnum.h000066400000000000000000000044151300200146000243160ustar00rootroot00000000000000#ifndef __VIEW_MODE_ENUM_H__ #define __VIEW_MODE_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 namespace caret { /** * View Mode Types. */ class ViewModeEnum { public: /** View Mode Types. */ enum Enum { /** Invalid */ VIEW_MODE_INVALID, /** View Surface */ VIEW_MODE_SURFACE, /** View Volume Slices */ VIEW_MODE_VOLUME_SLICES, /** View Whole Brain */ VIEW_MODE_WHOLE_BRAIN }; ~ViewModeEnum(); static QString toName(Enum e); static Enum fromName(const QString& s, bool* isValidOut); static QString toGuiName(Enum e); static Enum fromGuiName(const QString& s, bool* isValidOut); static int32_t toIntegerCode(Enum e); static Enum fromIntegerCode(const int32_t integerCode, bool* isValidOut); private: ViewModeEnum(const Enum e, const int32_t integerCode, const QString& name, const QString& guiName); static const ViewModeEnum* findData(const Enum e); static std::vector enumData; static void initialize(); static bool initializedFlag; Enum e; int32_t integerCode; QString name; QString guiName; }; #ifdef __VIEW_MODE_DECLARE__ std::vector ViewModeEnum::enumData; bool ViewModeEnum::initializedFlag = false; #endif // __VIEW_MODE_DECLARE__ } // namespace #endif // __VIEW_MODE_ENUM_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/GuiQt/VolumeFileCreateDialog.cxx000066400000000000000000000572031300200146000266430ustar00rootroot00000000000000 /*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 __VOLUME_FILE_CREATE_DIALOG_DECLARE__ #include "VolumeFileCreateDialog.h" #undef __VOLUME_FILE_CREATE_DIALOG_DECLARE__ #include #include #include #include #include #include #include #include #include #include #include #include "Brain.h" #include "CaretAssert.h" #include "CaretFileDialog.h" #include "CiftiMappableDataFile.h" #include "CaretVolumeExtension.h" #include "GuiManager.h" #include "VolumeFile.h" #include "WuQDataEntryDialog.h" #include "WuQFactory.h" #include "WuQMessageBox.h" #include "WuQtUtilities.h" using namespace caret; /** * \class caret::VolumeFileCreateDialog * \brief Dialog for creating volume file or adding map to volume file * \ingroup GuiQt */ /** * Constructor. */ VolumeFileCreateDialog::VolumeFileCreateDialog(QWidget* parent) : WuQDialogModal("Create Volume", parent) { if ( ! s_previousVolumeSettingsValid) { s_previousVolumeSettings.m_dimensions.clear(); s_previousVolumeSettings.m_dimensions.push_back(182); s_previousVolumeSettings.m_dimensions.push_back(218); s_previousVolumeSettings.m_dimensions.push_back(182); s_previousVolumeSettings.m_dimensions.push_back(1); std::vector row1; row1.push_back(-1); row1.push_back(0); row1.push_back(0); row1.push_back(90); std::vector row2; row2.push_back(0); row2.push_back(1); row2.push_back(0); row2.push_back(-126); std::vector row3; row3.push_back(0); row3.push_back(0); row3.push_back(1); row3.push_back(-72); std::vector row4; row4.push_back(0); row4.push_back(0); row4.push_back(0); row4.push_back(1); s_previousVolumeSettings.m_indexToSpace.clear(); s_previousVolumeSettings.m_indexToSpace.push_back(row1); s_previousVolumeSettings.m_indexToSpace.push_back(row2); s_previousVolumeSettings.m_indexToSpace.push_back(row3); s_previousVolumeSettings.m_indexToSpace.push_back(row4); s_previousVolumeSettings.m_volumeType = SubvolumeAttributes::FUNCTIONAL; s_previousVolumeSettingsValid = true; } m_volumeFile = NULL; QWidget* widget = new QWidget(); QVBoxLayout* layout = new QVBoxLayout(widget); layout->addWidget(createNewVolumeFileWidget()); setCentralWidget(widget, WuQDialog::SCROLL_AREA_NEVER); } /** * Destructor. */ VolumeFileCreateDialog::~VolumeFileCreateDialog() { } /** * @return Create and return the add map to volume file widget */ QWidget* VolumeFileCreateDialog::addMapToVolumeFileWidget() { QWidget* widget = new QWidget(); return widget; } /** * @return Create and return the create new volume file widget */ QWidget* VolumeFileCreateDialog::createNewVolumeFileWidget() { const AString defaultFileName = ("File_" + AString::number(s_fileNameCounter) + ".nii.gz"); s_fileNameCounter++; QLabel* newFileNameLabel = new QLabel("Name:"); m_newFileNameLineEdit = new QLineEdit(); m_newFileNameLineEdit->setText(defaultFileName); QPushButton* newFileNamePushButton = new QPushButton("Select..."); QObject::connect(newFileNamePushButton, SIGNAL(clicked()), this, SLOT(newFileNamePushButtonClicked())); QLabel* newFileTypeLabel = new QLabel("Type:"); m_newFileTypeComboBox = new QComboBox(); m_newFileTypeComboBox->addItem("Functional (Scalar)", SubvolumeAttributes::FUNCTIONAL); m_newFileTypeComboBox->addItem("Label", SubvolumeAttributes::LABEL); QLabel* newFileNumberOfMapsLabel = new QLabel("Maps:"); m_newFileNumberOfMapsSpinBox = WuQFactory::newSpinBoxWithMinMaxStep(1, s_maximumNumberOfMaps, 1); m_newFileNumberOfMapsSpinBox->setFixedWidth(50); QObject::connect(m_newFileNumberOfMapsSpinBox, SIGNAL(valueChanged(int)), this, SLOT(numberOfMapsSpinBoxValueChanged(int))); for (int32_t iMap = 0; iMap < s_maximumNumberOfMaps; iMap++) { const AString mapNumText("Map " + AString::number(iMap + 1) + " "); QLabel* label = new QLabel(mapNumText + "Name"); m_mapNameLabels.push_back(label); QLineEdit* lineEdit = new QLineEdit(); lineEdit->setText(mapNumText); m_mapNameLineEdits.push_back(lineEdit); } QGroupBox* fileGroupBox = new QGroupBox("File"); QGridLayout* nameTypeLayout = new QGridLayout(fileGroupBox); int nameTypeRow = 0; nameTypeLayout->setColumnStretch(0, 0); nameTypeLayout->setColumnStretch(1, 0); nameTypeLayout->setColumnStretch(2, 100); nameTypeLayout->setColumnStretch(3, 0); nameTypeLayout->addWidget(newFileNameLabel, nameTypeRow, 0); nameTypeLayout->addWidget(m_newFileNameLineEdit, nameTypeRow, 1, 1, 2); nameTypeLayout->addWidget(newFileNamePushButton, nameTypeRow, 4); nameTypeRow++; nameTypeLayout->addWidget(newFileTypeLabel, nameTypeRow, 0); nameTypeLayout->addWidget(m_newFileTypeComboBox, nameTypeRow, 1); nameTypeRow++; nameTypeLayout->addWidget(newFileNumberOfMapsLabel, nameTypeRow, 0); nameTypeLayout->addWidget(m_newFileNumberOfMapsSpinBox, nameTypeRow, 1, Qt::AlignLeft); nameTypeRow++; for (int32_t iMap = 0; iMap < s_maximumNumberOfMaps; iMap++) { CaretAssertVectorIndex(m_mapNameLabels, iMap); CaretAssertVectorIndex(m_mapNameLineEdits, iMap); nameTypeLayout->addWidget(m_mapNameLabels[iMap], nameTypeRow, 0); nameTypeLayout->addWidget(m_mapNameLineEdits[iMap], nameTypeRow, 1, 1, 2); nameTypeRow++; } const int SPIN_BOX_WIDTH = 80; QLabel* dimensionsLabel = new QLabel("Dimensions:"); m_newDimXSpinBox = WuQFactory::newSpinBoxWithMinMaxStep(1, 100000, 1); m_newDimYSpinBox = WuQFactory::newSpinBoxWithMinMaxStep(1, 100000, 1); m_newDimZSpinBox = WuQFactory::newSpinBoxWithMinMaxStep(1, 100000, 1); m_newDimXSpinBox->setFixedWidth(SPIN_BOX_WIDTH); m_newDimYSpinBox->setFixedWidth(SPIN_BOX_WIDTH); m_newDimZSpinBox->setFixedWidth(SPIN_BOX_WIDTH); const double bigDouble = 100000000.0; QLabel* originLabel = new QLabel("Origin:"); m_newOriginXSpinBox = WuQFactory::newDoubleSpinBoxWithMinMaxStepDecimals(-bigDouble, bigDouble, 1.0, 1); m_newOriginYSpinBox = WuQFactory::newDoubleSpinBoxWithMinMaxStepDecimals(-bigDouble, bigDouble, 1.0, 1); m_newOriginZSpinBox = WuQFactory::newDoubleSpinBoxWithMinMaxStepDecimals(-bigDouble, bigDouble, 1.0, 1); m_newOriginXSpinBox->setFixedWidth(SPIN_BOX_WIDTH); m_newOriginYSpinBox->setFixedWidth(SPIN_BOX_WIDTH); m_newOriginZSpinBox->setFixedWidth(SPIN_BOX_WIDTH); QLabel* spacingLabel = new QLabel("Spacing:"); m_newSpacingXSpinBox = WuQFactory::newDoubleSpinBoxWithMinMaxStepDecimals(-bigDouble, bigDouble, 1.0, 1); m_newSpacingYSpinBox = WuQFactory::newDoubleSpinBoxWithMinMaxStepDecimals(-bigDouble, bigDouble, 1.0, 1); m_newSpacingZSpinBox = WuQFactory::newDoubleSpinBoxWithMinMaxStepDecimals(-bigDouble, bigDouble, 1.0, 1); m_newSpacingXSpinBox->setValue(1.0); m_newSpacingYSpinBox->setValue(1.0); m_newSpacingZSpinBox->setValue(1.0); m_newSpacingXSpinBox->setFixedWidth(SPIN_BOX_WIDTH); m_newSpacingYSpinBox->setFixedWidth(SPIN_BOX_WIDTH); m_newSpacingZSpinBox->setFixedWidth(SPIN_BOX_WIDTH); QLabel* xLabel = new QLabel("X"); QLabel* yLabel = new QLabel("Y"); QLabel* zLabel = new QLabel("Z"); QLabel* loadFromLabel = new QLabel("Load From"); m_paramFromFilePushButton = new QPushButton("File..."); QObject::connect(m_paramFromFilePushButton, SIGNAL(clicked()), this, SLOT(loadVolumeParametersFromFilePushButtonClicked())); const int COL_LABEL = 0; const int COL_X = COL_LABEL + 1; const int COL_Y = COL_X + 1; const int COL_Z = COL_Y + 1; const int COL_LOAD = COL_Z + 1; QGroupBox* paramGroupBox = new QGroupBox("Voxels"); QGridLayout* paramsLayout = new QGridLayout(paramGroupBox); int paramsRow = 0; paramsLayout->addWidget(xLabel, paramsRow, COL_X, Qt::AlignHCenter); paramsLayout->addWidget(yLabel, paramsRow, COL_Y, Qt::AlignHCenter); paramsLayout->addWidget(zLabel, paramsRow, COL_Z, Qt::AlignHCenter); paramsLayout->addWidget(loadFromLabel, paramsRow, COL_LOAD, Qt::AlignHCenter); paramsRow++; paramsLayout->addWidget(dimensionsLabel, paramsRow, COL_LABEL); paramsLayout->addWidget(m_newDimXSpinBox, paramsRow, COL_X); paramsLayout->addWidget(m_newDimYSpinBox, paramsRow, COL_Y); paramsLayout->addWidget(m_newDimZSpinBox, paramsRow, COL_Z); paramsLayout->addWidget(m_paramFromFilePushButton, paramsRow, COL_LOAD); paramsRow++; paramsLayout->addWidget(originLabel, paramsRow, COL_LABEL); paramsLayout->addWidget(m_newOriginXSpinBox, paramsRow, COL_X); paramsLayout->addWidget(m_newOriginYSpinBox, paramsRow, COL_Y); paramsLayout->addWidget(m_newOriginZSpinBox, paramsRow, COL_Z); paramsRow++; paramsLayout->addWidget(spacingLabel, paramsRow, COL_LABEL); paramsLayout->addWidget(m_newSpacingXSpinBox, paramsRow, COL_X); paramsLayout->addWidget(m_newSpacingYSpinBox, paramsRow, COL_Y); paramsLayout->addWidget(m_newSpacingZSpinBox, paramsRow, COL_Z); paramsRow++; QWidget* widget = new QWidget(); QVBoxLayout* layout = new QVBoxLayout(widget); layout->addWidget(fileGroupBox); layout->addWidget(paramGroupBox); if (s_previousVolumeSettingsValid) { m_newDimXSpinBox->setValue(s_previousVolumeSettings.m_dimensions[0]); m_newDimYSpinBox->setValue(s_previousVolumeSettings.m_dimensions[1]); m_newDimZSpinBox->setValue(s_previousVolumeSettings.m_dimensions[2]); m_newFileNumberOfMapsSpinBox->setValue(s_previousVolumeSettings.m_dimensions[3]); const int typeIndex = m_newFileTypeComboBox->findData(static_cast(s_previousVolumeSettings.m_volumeType)); if (typeIndex >= 0) { m_newFileTypeComboBox->setCurrentIndex(typeIndex); } std::vector > m = s_previousVolumeSettings.m_indexToSpace; m_newSpacingXSpinBox->setValue(m[0][0]); m_newSpacingYSpinBox->setValue(m[1][1]); m_newSpacingZSpinBox->setValue(m[2][2]); m_newOriginXSpinBox->setValue(m[0][3]); m_newOriginYSpinBox->setValue(m[1][3]); m_newOriginZSpinBox->setValue(m[2][3]); } m_newFileNameLineEdit->selectAll(); numberOfMapsSpinBoxValueChanged(m_newFileNumberOfMapsSpinBox->value()); return widget; } /** * @return Volume file that was created (NULL if error). */ VolumeFile* VolumeFileCreateDialog::getVolumeFile() { return m_volumeFile; } /** * Called to create a new file name via the file selection dialog. */ void VolumeFileCreateDialog::newFileNamePushButtonClicked() { const AString filename = CaretFileDialog::getChooseFileNameDialog(DataFileTypeEnum::VOLUME, ""); if ( ! filename.isEmpty()) { m_newFileNameLineEdit->setText(filename); } } /** * Called when the number of maps is changed. * * @param value * New value for number of maps. */ void VolumeFileCreateDialog::numberOfMapsSpinBoxValueChanged(int value) { for (int32_t iMap = 0; iMap < s_maximumNumberOfMaps; iMap++) { CaretAssertVectorIndex(m_mapNameLabels, iMap); CaretAssertVectorIndex(m_mapNameLineEdits, iMap); const bool enabledFlag = (iMap < value); m_mapNameLabels[iMap]->setEnabled(enabledFlag); m_mapNameLineEdits[iMap]->setEnabled(enabledFlag); } } /** * Gets called when the ok button is clicked. */ void VolumeFileCreateDialog::okButtonClicked() { AString filename = m_newFileNameLineEdit->text().trimmed(); if (filename.isEmpty()) { WuQMessageBox::errorOk(this, "Filename is empty."); return; } if (! DataFileTypeEnum::isValidFileExtension(filename, DataFileTypeEnum::VOLUME)) { AString validExtensions; const std::vector allExts = DataFileTypeEnum::getAllFileExtensions(DataFileTypeEnum::VOLUME); const int32_t numExts = static_cast(allExts.size()); WuQDataEntryDialog ded("Invalid Volume File Extension", m_newFileNameLineEdit); ded.setTextAtTop("Filename extension is invalid. Choose one " "of the extensions below and press OK. Otherwise, " "press Cancel to change the name of the file.", true); std::vector extButtons; for (int32_t i = 0; i < numExts; i++) { QRadioButton* rb = ded.addRadioButton("." + allExts[i]); if (allExts[i] == "nii.gz") { rb->setChecked(true); } extButtons.push_back(rb); } bool extValid = false; if (ded.exec() == WuQDataEntryDialog::Accepted) { for (int32_t i = 0; i < numExts; i++) { QRadioButton* rb = extButtons[i]; if (rb->isChecked()) { filename += rb->text(); extValid = true; break; } } } if ( ! extValid) { return; } } const int typeIndex = m_newFileTypeComboBox->currentIndex(); const SubvolumeAttributes::VolumeType volumeType = static_cast(m_newFileTypeComboBox->itemData(typeIndex).toInt()); const int32_t numMaps = m_newFileNumberOfMapsSpinBox->value(); std::vector dimensions; dimensions.push_back(m_newDimXSpinBox->value()); dimensions.push_back(m_newDimYSpinBox->value()); dimensions.push_back(m_newDimZSpinBox->value()); dimensions.push_back(numMaps); const float xOrigin = m_newOriginXSpinBox->value(); const float yOrigin = m_newOriginYSpinBox->value(); const float zOrigin = m_newOriginZSpinBox->value(); const float xSpacing = m_newSpacingXSpinBox->value(); const float ySpacing = m_newSpacingYSpinBox->value(); const float zSpacing = m_newSpacingZSpinBox->value(); if ((xSpacing == 0.0) || (ySpacing == 0.0) || (zSpacing == 0.0)) { WuQMessageBox::errorOk(this, "Spacing values must be non-zero."); return; } std::vector rowOne(4, 0.0); rowOne[0] = xSpacing; rowOne[3] = xOrigin; std::vector rowTwo(4, 0.0); rowTwo[1] = ySpacing; rowTwo[3] = yOrigin; std::vector rowThree(4, 0.0); rowThree[2] = zSpacing; rowThree[3] = zOrigin; std::vector rowFour(4, 0.0); rowFour[3] = 1.0; std::vector > indexToSpace; indexToSpace.push_back(rowOne); indexToSpace.push_back(rowTwo); indexToSpace.push_back(rowThree); indexToSpace.push_back(rowFour); m_volumeFile = new VolumeFile(dimensions, indexToSpace, 1, volumeType); m_volumeFile->setFileName(filename); m_volumeFile->setType(volumeType); s_previousVolumeSettings.m_dimensions = dimensions; s_previousVolumeSettings.m_indexToSpace = indexToSpace; s_previousVolumeSettings.m_volumeType = volumeType; s_previousVolumeSettingsValid = true; float defaultValue = 0.0; if (numMaps > 0) { switch (volumeType) { case SubvolumeAttributes::ANATOMY: break; case SubvolumeAttributes::FUNCTIONAL: break; case SubvolumeAttributes::LABEL: defaultValue = m_volumeFile->getMapLabelTable(0)->getUnassignedLabelKey(); break; case SubvolumeAttributes::RGB: CaretAssert(0); break; case SubvolumeAttributes::SEGMENTATION: break; case SubvolumeAttributes::UNKNOWN: CaretAssert(0); break; case SubvolumeAttributes::VECTOR: CaretAssert(0); break; } } m_volumeFile->setValueAllVoxels(defaultValue); m_volumeFile->updateScalarColoringForAllMaps(GuiManager::get()->getBrain()->getPaletteFile()); for (int32_t iMap = 0; iMap < numMaps; iMap++) { CaretAssertVectorIndex(m_mapNameLineEdits, iMap); m_volumeFile->setMapName(iMap, m_mapNameLineEdits[iMap]->text().trimmed()); } WuQDialog::okButtonClicked(); } /** * Set the volume parameters from a loaded file. */ void VolumeFileCreateDialog::loadVolumeParametersFromFilePushButtonClicked() { Brain* brain = GuiManager::get()->getBrain(); std::vector allMappableFiles; brain->getAllMappableDataFiles(allMappableFiles); std::vector volumeMappableFiles; for (std::vector::iterator allIter = allMappableFiles.begin(); allIter != allMappableFiles.end(); allIter++) { CaretMappableDataFile* mapFile = *allIter; if (mapFile->isVolumeMappable()) { volumeMappableFiles.push_back(mapFile); } } const int32_t numberOfVolumeMappableFiles = static_cast(volumeMappableFiles.size()); if (numberOfVolumeMappableFiles <= 0) { WuQMessageBox::errorOk(m_paramFromFilePushButton, "No volume mappable files are loaded."); } std::vector volumeRadioButtons; WuQDataEntryDialog dialog("Select File For Volume Parameters", m_paramFromFilePushButton); for (int32_t i = 0; i < numberOfVolumeMappableFiles; i++) { CaretAssertVectorIndex(volumeMappableFiles, i); QRadioButton* rb = dialog.addRadioButton(volumeMappableFiles[i]->getFileNameNoPath()); volumeRadioButtons.push_back(rb); if (i == 0) { rb->setChecked(true); } } if (dialog.exec() == WuQDataEntryDialog::Accepted) { for (int32_t i = 0; i < numberOfVolumeMappableFiles; i++) { CaretAssertVectorIndex(volumeRadioButtons, i); if (volumeRadioButtons[i]->isChecked()) { CaretAssertVectorIndex(volumeMappableFiles, i); CaretMappableDataFile* volMapFile = volumeMappableFiles[i]; CiftiMappableDataFile* ciftiFile = dynamic_cast(volMapFile); VolumeFile* volumeFile = dynamic_cast(volMapFile); int64_t dimensions[5] = { 0, 0, 0, 0, 0 }; float origin[3] = { 0.0, 0.0, 0.0 }; float spacing[3] = { 0.0, 0.0, 0.0 }; int32_t numberOfMaps = 1; SubvolumeAttributes::VolumeType volumeType = SubvolumeAttributes::FUNCTIONAL; if (ciftiFile != NULL) { int64_t dimI, dimJ, dimK, dimTime, dimComponents; ciftiFile->getDimensions(dimI, dimJ, dimK, dimTime, dimComponents); dimensions[0] = dimI; dimensions[1] = dimJ; dimensions[2] = dimK; ciftiFile->indexToSpace(0, 0, 0, origin); float oneOneOneVoxelXYZ[3]; ciftiFile->indexToSpace(1, 1, 1, oneOneOneVoxelXYZ); spacing[0] = oneOneOneVoxelXYZ[0] - origin[0]; spacing[1] = oneOneOneVoxelXYZ[1] - origin[1]; spacing[2] = oneOneOneVoxelXYZ[2] - origin[2]; numberOfMaps = volMapFile->getNumberOfMaps(); if (volMapFile->isMappedWithLabelTable()) { volumeType = SubvolumeAttributes::LABEL; } else if (volMapFile->isMappedWithPalette()) { volumeType = SubvolumeAttributes::FUNCTIONAL; } } else if (volumeFile != NULL) { const VolumeSpace volumeSpace = volumeFile->getVolumeSpace(); const int64_t* dimArray = volumeSpace.getDims(); dimensions[0] = dimArray[0]; dimensions[1] = dimArray[1]; dimensions[2] = dimArray[2]; VolumeSpace::OrientTypes orientation[3]; volumeSpace.getOrientAndSpacingForPlumb(orientation, spacing, origin); numberOfMaps = volumeFile->getNumberOfMaps(); volumeType = volumeFile->getType(); } else { AString msg = ("Program Error: " + volMapFile->getFileNameNoPath() + " is volume mappable but neither a volume nor a CIFTI file."); WuQMessageBox::errorOk(m_paramFromFilePushButton, msg); return; } const bool includeTypeAndNumberOfMapsFlag = false; if (includeTypeAndNumberOfMapsFlag) { const int typeIndex = m_newFileTypeComboBox->findData(static_cast(volumeType)); if (typeIndex >= 0) { m_newFileTypeComboBox->setCurrentIndex(typeIndex); } m_newFileNumberOfMapsSpinBox->setValue(numberOfMaps); } m_newDimXSpinBox->setValue(dimensions[0]); m_newDimYSpinBox->setValue(dimensions[1]); m_newDimZSpinBox->setValue(dimensions[2]); m_newOriginXSpinBox->setValue(origin[0]); m_newOriginYSpinBox->setValue(origin[1]); m_newOriginZSpinBox->setValue(origin[2]); m_newSpacingXSpinBox->setValue(spacing[0]); m_newSpacingYSpinBox->setValue(spacing[1]); m_newSpacingZSpinBox->setValue(spacing[2]); break; } } } } connectome-workbench-1.2.3+git41-gc4c6c90/src/GuiQt/VolumeFileCreateDialog.h000066400000000000000000000071521300200146000262660ustar00rootroot00000000000000#ifndef __VOLUME_FILE_CREATE_DIALOG_H__ #define __VOLUME_FILE_CREATE_DIALOG_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 "VolumeFile.h" #include "WuQDialogModal.h" class QComboBox; class QDoubleSpinBox; class QLabel; class QLineEdit; class QPushButton; class QSpinBox; namespace caret { class VolumeFileCreateDialog : public WuQDialogModal { Q_OBJECT public: VolumeFileCreateDialog(QWidget* parent); virtual ~VolumeFileCreateDialog(); VolumeFile* getVolumeFile(); // ADD_NEW_METHODS_HERE private slots: void newFileNamePushButtonClicked(); void loadVolumeParametersFromFilePushButtonClicked(); void numberOfMapsSpinBoxValueChanged(int); protected: virtual void okButtonClicked(); private: VolumeFileCreateDialog(const VolumeFileCreateDialog&); VolumeFileCreateDialog& operator=(const VolumeFileCreateDialog&); struct PreviousVolumeSettings { std::vector m_dimensions; std::vector > m_indexToSpace; SubvolumeAttributes::VolumeType m_volumeType; }; QWidget* createNewVolumeFileWidget(); QWidget* addMapToVolumeFileWidget(); QLineEdit* m_newFileNameLineEdit; QComboBox* m_newFileTypeComboBox; QSpinBox* m_newFileNumberOfMapsSpinBox; std::vector m_mapNameLabels; std::vector m_mapNameLineEdits; QSpinBox* m_newDimXSpinBox; QSpinBox* m_newDimYSpinBox; QSpinBox* m_newDimZSpinBox; QDoubleSpinBox* m_newSpacingXSpinBox; QDoubleSpinBox* m_newSpacingYSpinBox; QDoubleSpinBox* m_newSpacingZSpinBox; QDoubleSpinBox* m_newOriginXSpinBox; QDoubleSpinBox* m_newOriginYSpinBox; QDoubleSpinBox* m_newOriginZSpinBox; QPushButton* m_paramFromFilePushButton; VolumeFile* m_volumeFile; static int32_t s_maximumNumberOfMaps; static int32_t s_fileNameCounter; static PreviousVolumeSettings s_previousVolumeSettings; static bool s_previousVolumeSettingsValid; // ADD_NEW_MEMBERS_HERE }; #ifdef __VOLUME_FILE_CREATE_DIALOG_DECLARE__ int32_t VolumeFileCreateDialog::s_maximumNumberOfMaps = 5; int32_t VolumeFileCreateDialog::s_fileNameCounter = 1; VolumeFileCreateDialog::PreviousVolumeSettings VolumeFileCreateDialog::s_previousVolumeSettings; bool VolumeFileCreateDialog::s_previousVolumeSettingsValid = false; #endif // __VOLUME_FILE_CREATE_DIALOG_DECLARE__ } // namespace #endif //__VOLUME_FILE_CREATE_DIALOG_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/GuiQt/VolumeSurfaceOutlineColorOrTabViewController.cxx000066400000000000000000000070711300200146000332740ustar00rootroot00000000000000 /*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 __VOLUME_SURFACE_OUTLINE_COLOR_OR_TAB_VIEW_CONTROLLER_DECLARE__ #include "VolumeSurfaceOutlineColorOrTabViewController.h" #undef __VOLUME_SURFACE_OUTLINE_COLOR_OR_TAB_VIEW_CONTROLLER_DECLARE__ #include #include "WuQFactory.h" using namespace caret; /** * \class caret::VolumeSurfaceOutlineColorOrTabViewController * \brief View Controller for VolumeSurfaceOutlineColorOrTabModel * */ /** * Constructor. */ VolumeSurfaceOutlineColorOrTabViewController::VolumeSurfaceOutlineColorOrTabViewController(QObject* parent) : WuQWidget(parent) { this->colorOrTabModel = NULL; this->modelComboBox = WuQFactory::newComboBox(); QObject::connect(this->modelComboBox, SIGNAL(activated(int)), this, SLOT(itemActivated(int))); } /** * Destructor. */ VolumeSurfaceOutlineColorOrTabViewController::~VolumeSurfaceOutlineColorOrTabViewController() { } /** * Update this view controller. */ void VolumeSurfaceOutlineColorOrTabViewController::updateViewController(VolumeSurfaceOutlineColorOrTabModel* model) { this->colorOrTabModel = model; this->modelComboBox->blockSignals(true); this->modelComboBox->clear(); VolumeSurfaceOutlineColorOrTabModel::Item* selectedItem = this->colorOrTabModel->getSelectedItem(); int32_t selectedItemIndex = -1; std::vector validItems = this->colorOrTabModel->getValidItems(); const int32_t numItems = static_cast(validItems.size()); for (int32_t i = 0; i < numItems; i++) { VolumeSurfaceOutlineColorOrTabModel::Item* item = validItems[i]; if (selectedItem == item) { selectedItemIndex = i; } this->modelComboBox->addItem(item->getName(), qVariantFromValue((void*)item)); } if (selectedItemIndex >= 0) { this->modelComboBox->setCurrentIndex(selectedItemIndex); } this->modelComboBox->blockSignals(false); } /** * @return The widget for this view controller. */ QWidget* VolumeSurfaceOutlineColorOrTabViewController::getWidget() { return this->modelComboBox; } /** * Called when the user selects an item. * @param indx * Index of item selected by the user. */ void VolumeSurfaceOutlineColorOrTabViewController::itemActivated(int indx) { if (this->colorOrTabModel == NULL) { return; } if (indx >= 0) { void* pointer = this->modelComboBox->itemData(indx).value(); VolumeSurfaceOutlineColorOrTabModel::Item* item = (VolumeSurfaceOutlineColorOrTabModel::Item*)pointer; this->colorOrTabModel->setSelectedItem(item); emit modelSelected(item); } } connectome-workbench-1.2.3+git41-gc4c6c90/src/GuiQt/VolumeSurfaceOutlineColorOrTabViewController.h000066400000000000000000000046261300200146000327240ustar00rootroot00000000000000#ifndef __VOLUME_SURFACE_OUTLINE_COLOR_OR_TAB_VIEW_CONTROLLER__H_ #define __VOLUME_SURFACE_OUTLINE_COLOR_OR_TAB_VIEW_CONTROLLER__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 "VolumeSurfaceOutlineColorOrTabModel.h" #include "WuQWidget.h" class QComboBox; namespace caret { class VolumeSurfaceOutlineColorOrTabModel; class VolumeSurfaceOutlineColorOrTabViewController : public WuQWidget { Q_OBJECT public: VolumeSurfaceOutlineColorOrTabViewController(QObject* parent); virtual ~VolumeSurfaceOutlineColorOrTabViewController(); QWidget* getWidget(); void updateViewController(VolumeSurfaceOutlineColorOrTabModel* model); signals: /** * Emitted when the user selects a model. * @param pointer to model selected. */ void modelSelected(VolumeSurfaceOutlineColorOrTabModel::Item*); private slots: void itemActivated(int indx); private: VolumeSurfaceOutlineColorOrTabViewController(const VolumeSurfaceOutlineColorOrTabViewController&); VolumeSurfaceOutlineColorOrTabViewController& operator=(const VolumeSurfaceOutlineColorOrTabViewController&); QComboBox* modelComboBox; VolumeSurfaceOutlineColorOrTabModel* colorOrTabModel; }; #ifdef __VOLUME_SURFACE_OUTLINE_COLOR_OR_TAB_VIEW_CONTROLLER_DECLARE__ // #endif // __VOLUME_SURFACE_OUTLINE_COLOR_OR_TAB_VIEW_CONTROLLER_DECLARE__ } // namespace #endif //__VOLUME_SURFACE_OUTLINE_COLOR_OR_TAB_VIEW_CONTROLLER__H_ connectome-workbench-1.2.3+git41-gc4c6c90/src/GuiQt/VolumeSurfaceOutlineSetViewController.cxx000066400000000000000000000170121300200146000320150ustar00rootroot00000000000000 /*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 #define __VOLUME_SURFACE_OUTLINE_SET_VIEW_CONTROLLER_DECLARE__ #include "VolumeSurfaceOutlineSetViewController.h" #undef __VOLUME_SURFACE_OUTLINE_SET_VIEW_CONTROLLER_DECLARE__ #include "BrowserTabContent.h" #include "CaretAssert.h" #include "EventManager.h" #include "EventUserInterfaceUpdate.h" #include "GuiManager.h" #include "VolumeSurfaceOutlineSetModel.h" #include "VolumeSurfaceOutlineViewController.h" #include "WuQFactory.h" #include "WuQtUtilities.h" using namespace caret; /** * \class caret::VolumeSurfaceOutlineSetViewController * \brief View Controller for VolumeSurfaceOutlineSetModel */ /** * Constructor. * * @param orientation * Orientation for layout * @param browserWindowIndex * Index of browser window that contains this view controller. * @param parent * Parent widget. */ VolumeSurfaceOutlineSetViewController::VolumeSurfaceOutlineSetViewController(const Qt::Orientation orientation, const int32_t browserWindowIndex, QWidget* parent) : QWidget(parent) { this->browserWindowIndex = browserWindowIndex; QWidget* gridWidget = new QWidget(); QGridLayout* gridLayout = new QGridLayout(gridWidget); WuQtUtilities::setLayoutSpacingAndMargins(gridLayout, 4, 2); if (orientation == Qt::Horizontal) { gridLayout->setColumnStretch(0, 0); gridLayout->setColumnStretch(1, 0); gridLayout->setColumnStretch(2, 0); gridLayout->setColumnStretch(3, 100); QLabel* onLabel = new QLabel("On"); QLabel* colorLabel = new QLabel("Color Source"); QLabel* thicknessLabel = new QLabel("Thickness"); QLabel* fileLabel = new QLabel("File"); const int row = gridLayout->rowCount(); gridLayout->addWidget(onLabel, row, 0, Qt::AlignHCenter); gridLayout->addWidget(colorLabel, row, 1, Qt::AlignHCenter); gridLayout->addWidget(thicknessLabel, row, 2, Qt::AlignHCenter); gridLayout->addWidget(fileLabel, row, 3, Qt::AlignHCenter); } else { gridLayout->setColumnStretch(0, 0); gridLayout->setColumnStretch(1, 0); gridLayout->setColumnStretch(2, 100); } for (int32_t i = 0; i < BrainConstants::MAXIMUM_NUMBER_OF_VOLUME_SURFACE_OUTLINES; i++) { VolumeSurfaceOutlineViewController* ovc = new VolumeSurfaceOutlineViewController(orientation, gridLayout); this->outlineViewControllers.push_back(ovc); } QLabel* outlineCountLabel = new QLabel("Number of Outlines: "); this->outlineCountSpinBox = WuQFactory::newSpinBox(); this->outlineCountSpinBox->setRange(BrainConstants::MINIMUM_NUMBER_OF_VOLUME_SURFACE_OUTLINES, BrainConstants::MAXIMUM_NUMBER_OF_VOLUME_SURFACE_OUTLINES); this->outlineCountSpinBox->setSingleStep(1); QObject::connect(this->outlineCountSpinBox, SIGNAL(valueChanged(int)), this, SLOT(outlineCountSpinBoxValueChanged(int))); QHBoxLayout* overlayCountLayout = new QHBoxLayout(); overlayCountLayout->addWidget(outlineCountLabel); overlayCountLayout->addWidget(this->outlineCountSpinBox); overlayCountLayout->addStretch(); QVBoxLayout* layout = new QVBoxLayout(this); WuQtUtilities::setLayoutSpacingAndMargins(layout, 2, 2); layout->addWidget(gridWidget); layout->addLayout(overlayCountLayout); layout->addStretch(); EventManager::get()->addEventListener(this, EventTypeEnum::EVENT_USER_INTERFACE_UPDATE); } /** * Destructor. */ VolumeSurfaceOutlineSetViewController::~VolumeSurfaceOutlineSetViewController() { EventManager::get()->removeAllEventsFromListener(this); for (size_t i = 0; i < outlineViewControllers.size(); ++i) { delete outlineViewControllers[i]; } } /** * Called when outline count spin box value changed. * @param value * New value. */ void VolumeSurfaceOutlineSetViewController::outlineCountSpinBoxValueChanged(int value) { VolumeSurfaceOutlineSetModel* outlineSet = this->getOutlineSet(); if (outlineSet != NULL) { outlineSet->setNumberOfDisplayedVolumeSurfaceOutlines(value); this->updateViewController(); } } /** * @return The outline set in this view controller. */ VolumeSurfaceOutlineSetModel* VolumeSurfaceOutlineSetViewController::getOutlineSet() { VolumeSurfaceOutlineSetModel* outlineSet = NULL; BrowserTabContent* browserTabContent = GuiManager::get()->getBrowserTabContentForBrowserWindow(this->browserWindowIndex, true); if (browserTabContent != NULL) { outlineSet = browserTabContent->getVolumeSurfaceOutlineSet(); } return outlineSet; } /** * Update this overlay set view controller using the given overlay set. */ void VolumeSurfaceOutlineSetViewController::updateViewController() { VolumeSurfaceOutlineSetModel* outlineSet = this->getOutlineSet(); if (outlineSet == NULL) { return; } const int32_t numberOfOutlines = static_cast(this->outlineViewControllers.size()); const int32_t numberOfDisplayedOutline = outlineSet->getNumberOfDislayedVolumeSurfaceOutlines(); this->outlineCountSpinBox->blockSignals(true); this->outlineCountSpinBox->setValue(numberOfDisplayedOutline); this->outlineCountSpinBox->blockSignals(false); for (int32_t i = 0; i < numberOfOutlines; i++) { VolumeSurfaceOutlineModel* outlineModel = NULL; if (outlineSet != NULL) { outlineModel = outlineSet->getVolumeSurfaceOutlineModel(i); } this->outlineViewControllers[i]->updateViewController(outlineModel); bool displayOutline = (outlineModel != NULL); if (i >= numberOfDisplayedOutline) { displayOutline = false; } this->outlineViewControllers[i]->setVisible(displayOutline); } } /** * Receive events from the event manager. * * @param event * Event sent by event manager. */ void VolumeSurfaceOutlineSetViewController::receiveEvent(Event* event) { if (event->getEventType() == EventTypeEnum::EVENT_USER_INTERFACE_UPDATE) { EventUserInterfaceUpdate* uiEvent = dynamic_cast(event); CaretAssert(uiEvent); if (uiEvent->isUpdateForWindow(this->browserWindowIndex)) { if (uiEvent->isSurfaceUpdate() || uiEvent->isToolBoxUpdate()) { this->updateViewController(); uiEvent->setEventProcessed(); } } } } connectome-workbench-1.2.3+git41-gc4c6c90/src/GuiQt/VolumeSurfaceOutlineSetViewController.h000066400000000000000000000047051300200146000314470ustar00rootroot00000000000000#ifndef __VOLUME_SURFACE_OUTLINE_SET_VIEW_CONTROLLER__H_ #define __VOLUME_SURFACE_OUTLINE_SET_VIEW_CONTROLLER__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 "EventListenerInterface.h" class QSpinBox; namespace caret { class VolumeSurfaceOutlineSetModel; class VolumeSurfaceOutlineViewController; class VolumeSurfaceOutlineSetViewController : public QWidget, public EventListenerInterface { Q_OBJECT public: VolumeSurfaceOutlineSetViewController(const Qt::Orientation orientation, const int32_t browserWindowIndex, QWidget* parent = 0); virtual ~VolumeSurfaceOutlineSetViewController(); void receiveEvent(Event* event); private slots: void outlineCountSpinBoxValueChanged(int); private: VolumeSurfaceOutlineSetViewController(const VolumeSurfaceOutlineSetViewController&); VolumeSurfaceOutlineSetViewController& operator=(const VolumeSurfaceOutlineSetViewController&); VolumeSurfaceOutlineSetModel* getOutlineSet(); void updateViewController(); QSpinBox* outlineCountSpinBox; int32_t browserWindowIndex; std::vector outlineViewControllers; }; #ifdef __VOLUME_SURFACE_OUTLINE_SET_VIEW_CONTROLLER_DECLARE__ // #endif // __VOLUME_SURFACE_OUTLINE_SET_VIEW_CONTROLLER_DECLARE__ } // namespace #endif //__VOLUME_SURFACE_OUTLINE_SET_VIEW_CONTROLLER__H_ connectome-workbench-1.2.3+git41-gc4c6c90/src/GuiQt/VolumeSurfaceOutlineViewController.cxx000066400000000000000000000177101300200146000313460ustar00rootroot00000000000000 /*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 __VOLUME_SURFACE_OUTLINE_VIEW_CONTROLLER_DECLARE__ #include "VolumeSurfaceOutlineViewController.h" #undef __VOLUME_SURFACE_OUTLINE_VIEW_CONTROLLER_DECLARE__ #include "EventGraphicsUpdateAllWindows.h" #include "EventManager.h" #include "SurfaceSelectionModel.h" #include "SurfaceSelectionViewController.h" #include "VolumeSurfaceOutlineColorOrTabViewController.h" #include "VolumeSurfaceOutlineModel.h" #include "WuQFactory.h" #include "WuQGridLayoutGroup.h" #include "WuQtUtilities.h" using namespace caret; /** * \class caret::VolumeSurfaceOutlineViewController * \brief View controller for volume surface outline * */ /** * Constructor. */ VolumeSurfaceOutlineViewController::VolumeSurfaceOutlineViewController(const Qt::Orientation orientation, QGridLayout* gridLayout, QObject* parent) : QObject(parent) { this->outlineModel = NULL; this->enabledCheckBox = new QCheckBox(" "); QObject::connect(this->enabledCheckBox, SIGNAL(stateChanged(int)), this, SLOT(enabledCheckBoxStateChanged(int))); this->enabledCheckBox->setToolTip("Enables display of this volume surface outline"); this->surfaceSelectionViewController = new SurfaceSelectionViewController(this); QObject::connect(this->surfaceSelectionViewController, SIGNAL(surfaceSelected(Surface*)), this, SLOT(surfaceSelected(Surface*))); this->surfaceSelectionViewController->getWidget()->setToolTip("Select surface drawn as outline over volume slices"); this->colorOrTabSelectionControl = new VolumeSurfaceOutlineColorOrTabViewController(this); QObject::connect(this->colorOrTabSelectionControl, SIGNAL(modelSelected(VolumeSurfaceOutlineColorOrTabModel::Item*)), this, SLOT(colorTabSelected(VolumeSurfaceOutlineColorOrTabModel::Item*))); 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"); if (orientation == Qt::Horizontal) { this->gridLayoutGroup = new WuQGridLayoutGroup(gridLayout, this); 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->surfaceSelectionViewController->getWidget(), row, 3); } else { QFrame* bottomHorizontalLineWidget = new QFrame(); bottomHorizontalLineWidget->setLineWidth(0); bottomHorizontalLineWidget->setMidLineWidth(1); bottomHorizontalLineWidget->setFrameStyle(QFrame::HLine | QFrame::Raised); this->gridLayoutGroup = new WuQGridLayoutGroup(gridLayout, this); int row = this->gridLayoutGroup->rowCount(); this->gridLayoutGroup->addWidget(this->enabledCheckBox, row, 0, 2, 1, Qt::AlignCenter); 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); row++; this->gridLayoutGroup->addWidget(bottomHorizontalLineWidget, row, 0, 1, -1); } } /** * Destructor. */ VolumeSurfaceOutlineViewController::~VolumeSurfaceOutlineViewController() { } /** * Set the visibility of widgets in this view controller. */ void VolumeSurfaceOutlineViewController::setVisible(bool visible) { this->gridLayoutGroup->setVisible(visible); } /** * Called when a surface is selected. * @param surface * Surface that was selected. */ void VolumeSurfaceOutlineViewController::surfaceSelected(Surface* surface) { if (this->outlineModel != NULL) { this->outlineModel->getSurfaceSelectionModel()->setSurface(surface); } this->updateGraphics(); } /** * Called when a color/tab is selected. * @param colorTab * Value that was selected. */ void VolumeSurfaceOutlineViewController::colorTabSelected(VolumeSurfaceOutlineColorOrTabModel::Item* /*colorTab*/) { this->updateGraphics(); } /** * Called when enabled checkbox is selected. * @param state * New state of checkbox. */ void VolumeSurfaceOutlineViewController::enabledCheckBoxStateChanged(int state) { if (this->outlineModel != NULL) { const bool selected = (state == Qt::Checked); this->outlineModel->setDisplayed(selected); } this->updateGraphics(); } /** * Called when thickness value is changed. * @param value * Value that was selected. */ void VolumeSurfaceOutlineViewController::thicknessSpinBoxValueChanged(double value) { if (this->outlineModel != NULL) { this->outlineModel->setThickness(value); } this->updateGraphics(); } /** * Update this view controller. * @param outlineModel * Outline model for use in this view controller. */ void VolumeSurfaceOutlineViewController::updateViewController(VolumeSurfaceOutlineModel* outlineModel) { this->outlineModel = outlineModel; if (this->outlineModel != NULL) { Qt::CheckState state = Qt::Unchecked; if (this->outlineModel->isDisplayed()) { state = Qt::Checked; } this->enabledCheckBox->setCheckState(state); this->thicknessSpinBox->blockSignals(true); this->thicknessSpinBox->setValue(outlineModel->getThickness()); this->thicknessSpinBox->blockSignals(false); this->surfaceSelectionViewController->updateControl(outlineModel->getSurfaceSelectionModel()); //this->surfaceSelectionViewController->setSurface(outlineModel->getSurface()); this->colorOrTabSelectionControl->updateViewController(outlineModel->getColorOrTabModel()); } } /** * Update the graphics. */ void VolumeSurfaceOutlineViewController::updateGraphics() { EventManager::get()->sendEvent(EventGraphicsUpdateAllWindows().getPointer()); } connectome-workbench-1.2.3+git41-gc4c6c90/src/GuiQt/VolumeSurfaceOutlineViewController.h000066400000000000000000000055651300200146000310000ustar00rootroot00000000000000#ifndef __VOLUME_SURFACE_OUTLINE_VIEW_CONTROLLER__H_ #define __VOLUME_SURFACE_OUTLINE_VIEW_CONTROLLER__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 "VolumeSurfaceOutlineColorOrTabModel.h" class QCheckBox; class QDoubleSpinBox; class QGridLayout; namespace caret { class Surface; class SurfaceSelectionViewController; class VolumeSurfaceOutlineModel; class VolumeSurfaceOutlineColorOrTabViewController; class WuQGridLayoutGroup; class VolumeSurfaceOutlineViewController : public QObject { Q_OBJECT public: VolumeSurfaceOutlineViewController(const Qt::Orientation orientation, QGridLayout* gridLayout, QObject* parent = 0); virtual ~VolumeSurfaceOutlineViewController(); void setVisible(bool visible); void updateViewController(VolumeSurfaceOutlineModel* outlineModel); private slots: void enabledCheckBoxStateChanged(int); void thicknessSpinBoxValueChanged(double); void surfaceSelected(Surface*); void colorTabSelected(VolumeSurfaceOutlineColorOrTabModel::Item*); private: VolumeSurfaceOutlineViewController(const VolumeSurfaceOutlineViewController&); VolumeSurfaceOutlineViewController& operator=(const VolumeSurfaceOutlineViewController&); void updateGraphics(); VolumeSurfaceOutlineModel* outlineModel; WuQGridLayoutGroup* gridLayoutGroup; QCheckBox* enabledCheckBox; VolumeSurfaceOutlineColorOrTabViewController* colorOrTabSelectionControl; QDoubleSpinBox* thicknessSpinBox; SurfaceSelectionViewController* surfaceSelectionViewController; }; #ifdef __VOLUME_SURFACE_OUTLINE_VIEW_CONTROLLER_DECLARE__ // #endif // __VOLUME_SURFACE_OUTLINE_VIEW_CONTROLLER_DECLARE__ } // namespace #endif //__VOLUME_SURFACE_OUTLINE_VIEW_CONTROLLER__H_ connectome-workbench-1.2.3+git41-gc4c6c90/src/GuiQt/WuQCollapsibleWidget.cxx000066400000000000000000000116031300200146000263540ustar00rootroot00000000000000 /*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 #define __WU_Q_COLLAPSIBLE_WIDGET_DECLARE__ #include "WuQCollapsibleWidget.h" #undef __WU_Q_COLLAPSIBLE_WIDGET_DECLARE__ #include "CaretAssert.h" #include "WuQtUtilities.h" using namespace caret; /** * \class caret::WuQCollapsibleWidget * \brief A container widget with multiple collapsible sections * * A container widget with multiple collapsible sections in * a vertical orientation. Each section is independent of * the other sections so that multiple sections may be open * at one time. */ /** * Constructor. */ WuQCollapsibleWidget::WuQCollapsibleWidget(QWidget* parent) : QWidget(parent) { this->showHideActionGroup = new QActionGroup(this); QObject::connect(this->showHideActionGroup, SIGNAL(triggered(QAction*)), this, SLOT(showHideActionGroupTriggered(QAction*))); this->collapsibleLayout = new QVBoxLayout(); WuQtUtilities::setLayoutSpacingAndMargins(this->collapsibleLayout, 2, 2); QWidget* widget = new QWidget(); QVBoxLayout* widgetLayout = new QVBoxLayout(widget); widgetLayout->addLayout(this->collapsibleLayout); widgetLayout->addStretch(); QScrollArea* scrollArea = new QScrollArea(); scrollArea->setWidgetResizable(true); scrollArea->setHorizontalScrollBarPolicy(Qt::ScrollBarAsNeeded); scrollArea->setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded); scrollArea->setWidget(widget); QVBoxLayout* layout = new QVBoxLayout(this); layout->addWidget(scrollArea); //layout->addStretch(); Using this will result in the widget too small vertically } /** * Destructor. */ WuQCollapsibleWidget::~WuQCollapsibleWidget() { } /** * @return The recommended size for this widget. */ QSize WuQCollapsibleWidget::sizeHint() const { //QSize minSize = this->minimumSize(); //std::cout << "Collapse Min Size: " //<< minSize.width() << ", " << minSize.width() << std::endl; QSize sz = QWidget::sizeHint(); sz.setHeight(500); return sz; } /** * Add a widget to this collapsible widget with the * given label in the titlebar. * @param widget * Widget that is added. * @param text * Text displayed for collapsing/expanding view * of widget. */ void WuQCollapsibleWidget::addItem(QWidget* widget, const QString& text) { CaretAssert(widget); QLabel* label = new QLabel(text); QAction* showHideAction = this->showHideActionGroup->addAction("-"); showHideAction->setData(qVariantFromValue((void*)widget)); QToolButton* showHideToolButton = new QToolButton(); showHideToolButton->setDefaultAction(showHideAction); QHBoxLayout* controlLayout = new QHBoxLayout(); WuQtUtilities::setLayoutSpacingAndMargins(controlLayout, 2, 2); controlLayout->addWidget(showHideToolButton); controlLayout->addSpacing(15); controlLayout->addWidget(label); controlLayout->addStretch(); QFrame* controlFrame = new QFrame(); controlFrame->setLineWidth(1); controlFrame->setMidLineWidth(2); controlFrame->setFrameStyle(QFrame::Box | QFrame::Plain); controlFrame->setLayout(controlLayout); controlFrame->setFixedHeight(controlFrame->sizeHint().height()); this->collapsibleLayout->addWidget(controlFrame); this->collapsibleLayout->addWidget(widget); this->widgets.push_back(widget); } /** * Called when a show/hide tool button is triggered */ void WuQCollapsibleWidget::showHideActionGroupTriggered(QAction* action) { if (action != NULL) { void* pointer = action->data().value(); CaretAssert(pointer); QWidget* widget = (QWidget*)pointer; QString collapseExpandText = ""; if (widget->isVisible()) { widget->setVisible(false); collapseExpandText = "+"; } else { widget->setVisible(true); collapseExpandText = "-"; } action->setText(collapseExpandText); } } connectome-workbench-1.2.3+git41-gc4c6c90/src/GuiQt/WuQCollapsibleWidget.h000066400000000000000000000036301300200146000260020ustar00rootroot00000000000000#ifndef __WU_Q_COLLAPSIBLE_WIDGET__H_ #define __WU_Q_COLLAPSIBLE_WIDGET__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 class QAction; class QActionGroup; class QVBoxLayout; namespace caret { class WuQCollapsibleWidget : public QWidget { Q_OBJECT public: WuQCollapsibleWidget(QWidget* parent = 0); virtual ~WuQCollapsibleWidget(); void addItem(QWidget* widget, const QString& text); virtual QSize sizeHint() const; private slots: void showHideActionGroupTriggered(QAction*); private: WuQCollapsibleWidget(const WuQCollapsibleWidget&); WuQCollapsibleWidget& operator=(const WuQCollapsibleWidget&); QVBoxLayout* collapsibleLayout; QActionGroup* showHideActionGroup; QVector widgets; }; #ifdef __WU_Q_COLLAPSIBLE_WIDGET_DECLARE__ // #endif // __WU_Q_COLLAPSIBLE_WIDGET_DECLARE__ } // namespace #endif //__WU_Q_COLLAPSIBLE_WIDGET__H_ connectome-workbench-1.2.3+git41-gc4c6c90/src/GuiQt/WuQDataEntryDialog.cxx000066400000000000000000000333331300200146000257760ustar00rootroot00000000000000/*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 #include #include #include #include #include #include #include "CaretColorEnumComboBox.h" #include "StructureEnumComboBox.h" #include "SurfaceSelectionViewController.h" #include "WuQDataEntryDialog.h" #include "WuQFactory.h" #include "WuQMessageBox.h" #include "WuQtUtilities.h" using namespace caret; /** * constructor. * * @param title * Title for dialog. * @param parent * Parent of the dialog. * @param addScrollBarsFlag * If true, add scrollbars to the dialog (typically true when the dialog * will contain many items. * @param f * Qt's Window flags. * */ WuQDataEntryDialog::WuQDataEntryDialog(const QString& title, QWidget* parent, const bool addScrollBarsFlag, Qt::WindowFlags f) : WuQDialogModal(title, parent, f) { WuQDialog::ScrollAreaStatus scrollBarStatus = WuQDialog::SCROLL_AREA_NEVER; if (addScrollBarsFlag) { scrollBarStatus = WuQDialog::SCROLL_AREA_ALWAYS; } constructDialog(scrollBarStatus); } /** * constructor. * * @param title * Title for dialog. * @param parent * Parent of the dialog. * @param scrollBarStatus * Add scrollbars (never, as needed, always). * @param f * Qt's Window flags. * */ WuQDataEntryDialog::WuQDataEntryDialog(const QString& title, QWidget* parent, const WuQDialog::ScrollAreaStatus scrollBarStatus, Qt::WindowFlags f) : WuQDialogModal(title, parent, f) { constructDialog(scrollBarStatus); } /** * destructor. */ WuQDataEntryDialog::~WuQDataEntryDialog() { } /** * Finish construction of the dialog. * * @param scrollBarStatus * Status for scroll bars. */ void WuQDataEntryDialog::constructDialog(const WuQDialog::ScrollAreaStatus scrollBarStatus) { // // Widget and Layout for user's widgets // QWidget* widgetForGridLayout = new QWidget; widgetGridLayout = new QGridLayout(widgetForGridLayout); // // Labels for text at top, hidden until set by user // textAtTopLabel = new QLabel; textAtTopLabel->setHidden(true); // // ButtonGroup for radio buttons // radioButtonGroup = new QButtonGroup(this); // // Layout for dialog // const int NO_STRETCH = 0; const int BIG_STRETCH = 100; QWidget* widget = new QWidget(); QVBoxLayout* dialogLayout = new QVBoxLayout(widget); dialogLayout->addWidget(textAtTopLabel, NO_STRETCH); dialogLayout->addWidget(widgetForGridLayout, BIG_STRETCH); this->setCentralWidget(widget, scrollBarStatus); } /** * Called when the OK button is pressed. * If needed should override this to process * data when the OK button is pressed and then * call this to issue the accept signal. */ void WuQDataEntryDialog::okButtonClicked() { m_isDataValid = true; m_dataInvalidErrorMessage = ""; emit validateData(this); if (m_isDataValid) { // if (dataEnteredIsValid()) { // accept(); // } WuQDialogModal::okButtonClicked(); } else { if (m_dataInvalidErrorMessage.isEmpty()) { m_dataInvalidErrorMessage = "Data is not valid."; } WuQMessageBox::errorOk(this, m_dataInvalidErrorMessage); } } /** * This method is used to set the validity of the data * widgets that were added by the user. To use this, * connect to the validateData() signal and then call * this to indicate the validity of the data widgets. * getDataWidgetWithName() can be used to get widgets * but only will work if the user sets the name of * the widgets. * @param isValid * True if the data is valid, else false. * @param dataInvalidErrorMessage * If data is invalid, message describing why data is invalid. */ void WuQDataEntryDialog::setDataValid(const bool isValid, const QString& dataInvalidErrorMessage) { m_isDataValid = isValid; m_dataInvalidErrorMessage = dataInvalidErrorMessage; } /** * Get the data widget that was added by user with the * given name. For this method to return the correct * widget, the user must call setObjectName() on a * widget after it is added to the dialog. * * @param name * Name of the widget. * @return * Pointer to widget with the given name or NULL * if the widget is not found. */ QWidget* WuQDataEntryDialog::getDataWidgetWithName(const QString& name) { const int numWidgets = widgets.size(); for (int i = 0; i < numWidgets; i++) { if (widgets[i]->objectName() == name) { return widgets[i]; } } return NULL; } ///** // * override to verify data after OK button pressed. // */ //bool //WuQDataEntryDialog::dataEnteredIsValid() //{ // return true; //} /** * set text at top of dialog (text is automatically wrapped). */ void WuQDataEntryDialog::setTextAtTop(const QString& s, const bool wrapTheText) { textAtTopLabel->setText(s); textAtTopLabel->setWordWrap(wrapTheText); if (s.isEmpty()) { textAtTopLabel->setHidden(true); } else { textAtTopLabel->setHidden(false); } } /** * add widgets to the next available row in the dialog. */ void WuQDataEntryDialog::addWidgetsToNextRow(QWidget* leftColumnWidget, QWidget* rightColumnWidget) { // // add widgets to the next row // const int rowNumber = widgetGridLayout->rowCount(); if (leftColumnWidget != NULL) { widgetGridLayout->addWidget(leftColumnWidget, rowNumber, 0); } if (rightColumnWidget != NULL) { widgetGridLayout->addWidget(rightColumnWidget, rowNumber, 1); } } /** * add widget to next available row in the dialog. */ QWidget* WuQDataEntryDialog::addWidget(const QString& labelText, QWidget* widget) { // // Create the label // QLabel* label = new QLabel(labelText); // // Keep pointer to widget // widgets.push_back(widget); // // add widget to layout // addWidgetsToNextRow(label, widget); return widget; } /** * add a check box. */ QCheckBox* WuQDataEntryDialog::addCheckBox(const QString& text, const bool defaultValue) { // // Create check box // QCheckBox* cb = new QCheckBox(text); cb->setChecked(defaultValue); // // Keep pointer to widget // widgets.push_back(cb); // // add widget to both columns of layout // const int rowNumber = widgetGridLayout->rowCount(); widgetGridLayout->addWidget(cb, rowNumber, 0, 1, 2, Qt::AlignLeft); return cb; } /** * get radio button selected (-1 if none, value is sequence added). */ int WuQDataEntryDialog::getRadioButtonSelected() const { return radioButtonGroup->checkedId(); } /** * add a radio button (all radio buttons will be mutually exclusive). */ QRadioButton* WuQDataEntryDialog::addRadioButton(const QString& text, const bool defaultValue) { // // Create radio button // QRadioButton* rb = new QRadioButton(text); rb->setChecked(defaultValue); // // Add to radio button group // const int buttNum = radioButtonGroup->buttons().count(); radioButtonGroup->addButton(rb, buttNum); // // Keep pointer to widget // widgets.push_back(rb); // // add widget to both columns of layout // const int rowNumber = widgetGridLayout->rowCount(); widgetGridLayout->addWidget(rb, rowNumber, 0, 1, 2, Qt::AlignLeft); return rb; } /** * add a combo box. */ QComboBox* WuQDataEntryDialog::addComboBox(const QString& labelText, const QStringList& comboBoxItems, const QList* comboBoxItemsUserData) { // // Create combo box // QComboBox* comboBox = WuQFactory::newComboBox(); for (int i = 0; i < comboBoxItems.size(); i++) { QVariant userData; if (comboBoxItemsUserData != NULL) { if (i < comboBoxItemsUserData->size()) { userData = (*comboBoxItemsUserData).at(i); } } comboBox->addItem(comboBoxItems.at(i), userData); } // // Add to dialog // addWidget(labelText, comboBox); return comboBox; } /** * add line edit. */ QLineEdit* WuQDataEntryDialog::addLineEditWidget(const QString& labelText, const QString& defaultText) { // // Create line edit // QLineEdit* le = new QLineEdit; le->setText(defaultText); // // Add to dialog // addWidget(labelText, le); return le; } /* * add list box. */ QListWidget* WuQDataEntryDialog::addListWidget(const QString& labelText, const QStringList& listBoxItems) { // // Create and populate list widget // QListWidget* lw = new QListWidget; lw->addItems(listBoxItems); if (listBoxItems.count() > 0) { lw->setCurrentRow(0); } // // Add to dialog // addWidget(labelText, lw); return lw; } /** * add spin box. */ QSpinBox* WuQDataEntryDialog::addSpinBox(const QString& labelText, const int defaultValue, const int minimumValue, const int maximumValue, const int singleStep) { // // Create spin box // QSpinBox* sb = WuQFactory::newSpinBox(); sb->setMinimum(minimumValue); sb->setMaximum(maximumValue); sb->setSingleStep(singleStep); sb->setValue(defaultValue); // // Add to dialog // addWidget(labelText, sb); return sb; } /** * add double spin box. */ QDoubleSpinBox* WuQDataEntryDialog::addDoubleSpinBox(const QString& labelText, const float defaultValue, const float minimumValue, const float maximumValue, const float singleStep, const int numberOfDecimals) { // // Create spin box // QDoubleSpinBox* sb = WuQFactory::newDoubleSpinBox(); sb->setMinimum(minimumValue); sb->setMaximum(maximumValue); sb->setSingleStep(singleStep); sb->setValue(defaultValue); sb->setDecimals(numberOfDecimals); // // Add to dialog // addWidget(labelText, sb); return sb; } /** * add a text edit. */ QTextEdit* WuQDataEntryDialog::addTextEdit(const QString& labelText, const QString& defaultText, const bool readOnlyFlag) { // // Create text edit // QTextEdit* te = new QTextEdit; te->setReadOnly(readOnlyFlag); te->setPlainText(defaultText); // // add to dialog // addWidget(labelText, te); return te; } /** * Add a caret color selection control. */ CaretColorEnumComboBox* WuQDataEntryDialog::addCaretColorEnumComboBox(const QString& labelText, const CaretColorEnum::Enum defaultColor) { CaretColorEnumComboBox* caretColorComboBox = new CaretColorEnumComboBox(this); caretColorComboBox->setSelectedColor(defaultColor); this->addWidget(labelText, caretColorComboBox->getWidget()); return caretColorComboBox; } /** * Add a structure selection control. */ StructureEnumComboBox* WuQDataEntryDialog::addStructureEnumComboBox(const QString& labelText, const StructureEnum::Enum defaultStructure) { StructureEnumComboBox* structureEnumComboBox = new StructureEnumComboBox(this); structureEnumComboBox->setSelectedStructure(defaultStructure); this->addWidget(labelText, structureEnumComboBox->getWidget()); return structureEnumComboBox; } /** * Add a surface selection control */ SurfaceSelectionViewController* WuQDataEntryDialog::addSurfaceSelectionViewController(const QString& labelText, BrainStructure* brainStructure) { SurfaceSelectionViewController* surfaceSelectionViewController = new SurfaceSelectionViewController(this, brainStructure); this->addWidget(labelText, surfaceSelectionViewController->getWidget()); return surfaceSelectionViewController; } connectome-workbench-1.2.3+git41-gc4c6c90/src/GuiQt/WuQDataEntryDialog.h000066400000000000000000000153471300200146000254300ustar00rootroot00000000000000 #ifndef __WU_Q_DATA_ENTRY_DIALOG_H__ #define __WU_Q_DATA_ENTRY_DIALOG_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 "CaretColorEnum.h" #include "StructureEnum.h" #include "WuQDialogModal.h" class QButtonGroup; class QCheckBox; class QComboBox; class QDialogButtonBox; class QDoubleSpinBox; class QGridLayout; class QLabel; class QLineEdit; class QListWidget; class QRadioButton; class QSpinBox; class QTextEdit; namespace caret { class BrainStructure; class CaretColorEnumComboBox; class StructureEnumComboBox; class SurfaceSelectionViewController; /// class for a modal data entry dialog class WuQDataEntryDialog : public WuQDialogModal { Q_OBJECT public: // constructor WuQDataEntryDialog(const QString& title, QWidget* parent, const bool addScrollBarsFlag = false, Qt::WindowFlags f = 0); WuQDataEntryDialog(const QString& title, QWidget* parent, const WuQDialog::ScrollAreaStatus scrollBarStatus, Qt::WindowFlags f = 0); // destructor ~WuQDataEntryDialog(); // add widget to next available row in the dialog QWidget* addWidget(const QString& labelText, QWidget* widget); // add widgets to the next available row in the dialog void addWidgetsToNextRow(QWidget* leftColumnWidget, QWidget* rightColumnWidget); // add a check box QCheckBox* addCheckBox(const QString& text, const bool defaultValue = false); // add a combo box QComboBox* addComboBox(const QString& labelText, const QStringList& comboBoxItems, const QList* comboBoxItemsUserData = NULL); // add line edit QLineEdit* addLineEditWidget(const QString& labelText, const QString& defaultText = ""); // add list box QListWidget* addListWidget(const QString& labelText, const QStringList& listBoxItems); // add a radio button (all radio buttons will be mutually exclusive) QRadioButton* addRadioButton(const QString& text, const bool defaultValue = false); // get radio button selected (-1 if none, value is sequence added) int getRadioButtonSelected() const; // add spin box QSpinBox* addSpinBox(const QString& labelText, const int defaultValue, const int minimumValue = -10000, const int maximumValue = 10000, const int singleStep = 1); // add double spin box QDoubleSpinBox* addDoubleSpinBox(const QString& labelText, const float defaultValue, const float minimumValue = -10000000.0, const float maximumValue = 10000000.0, const float singleStep = 1.0, const int numberOfDecimals = 3); // add a text edit QTextEdit* addTextEdit(const QString& labelText, const QString& defaultText, const bool readOnlyFlag); // add a structure selection control StructureEnumComboBox* addStructureEnumComboBox(const QString& labelText, const StructureEnum::Enum defaultStructure = StructureEnum::INVALID); // add a caret color selection control CaretColorEnumComboBox* addCaretColorEnumComboBox(const QString& labelText, const CaretColorEnum::Enum defaultColor = CaretColorEnum::BLACK); SurfaceSelectionViewController* addSurfaceSelectionViewController(const QString& labelText, BrainStructure* brainStructure); // set text at top of dialog void setTextAtTop(const QString& s, const bool wrapTheText); void setDataValid(const bool isValid, const QString& dataInvalidErrorMessage); QWidget* getDataWidgetWithName(const QString& name); signals: /** * This signal is emitted when the user presses the OK button. * The user of the dialog can then examine the widgets that were * added to the dialog for validity and then pass the validity * status back to this dialog via setDataValid(). */ void validateData(WuQDataEntryDialog* dataEntryDialog); protected: virtual void okButtonClicked(); // // override to verify data after OK button pressed if subclassing this dialog // virtual bool dataEnteredIsValid(); private: void constructDialog(const WuQDialog::ScrollAreaStatus scrollBarStatus); /// widgets in dialog QVector widgets; /// layout for widgets in dialog QGridLayout* widgetGridLayout; /// label for text at dialog top QLabel* textAtTopLabel; /// button group for radio buttons QButtonGroup* radioButtonGroup; /** Indicates validity of data */ bool m_isDataValid; /** Error message displayed when data is invalid */ QString m_dataInvalidErrorMessage; }; } // namespace #endif // __WU_Q_DATA_ENTRY_DIALOG_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/GuiQt/WuQDialog.cxx000066400000000000000000000640431300200146000241640ustar00rootroot00000000000000/*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*/ /* * Some code in keyPressEvent copied from QT4, original license follows */ /**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtGui 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 Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/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 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #include #include #include #include #include #include #include #include #include #include #include #include #include #include "CaretAssert.h" #include "CaretLogger.h" #define __WU_QDIALOG_DECLARE__ #include "WuQDialog.h" #undef __WU_QDIALOG_DECLARE__ #include "WuQtUtilities.h" using namespace caret; /** * \class caret::WuQDialog * \brief Base class for dialogs. * * A base class for dialogs. */ /** * constructor. * * @param dialogTitle * Title for dialog. * @param parent * Parent widget on which this dialog is displayed. * @param f * optional Qt::WindowFlags */ WuQDialog::WuQDialog(const AString& dialogTitle, QWidget* parent, Qt::WindowFlags f) : QDialog(parent, f) { m_placeCentralWidgetInScrollAreaStatus = SCROLL_AREA_NEVER; m_topWidget = NULL; m_centralWidget = NULL; m_bottomWidget = NULL; m_firstTimeInShowMethodFlag = true; m_centralWidgetLayoutIndex = -1; m_sizeHintWidth = -1; m_sizeHintHeight = -1; this->autoDefaultProcessingEnabledFlag = true; this->setAttribute(Qt::WA_DeleteOnClose, false); this->setWindowTitle(dialogTitle); this->setFocusPolicy(Qt::ClickFocus); this->userWidgetLayout = new QVBoxLayout(); WuQtUtilities::setLayoutSpacingAndMargins(this->userWidgetLayout, 0, 0); m_layoutLeftOfButtonBox = new QHBoxLayout(); m_layoutLeftOfButtonBox->setContentsMargins(0, 0, 0, 0); this->buttonBox = new QDialogButtonBox(Qt::Horizontal, this); QObject::connect(this->buttonBox, SIGNAL(clicked(QAbstractButton*)), this, SLOT(clicked(QAbstractButton*))); QHBoxLayout* bottomLayout = new QHBoxLayout(); bottomLayout->addLayout(m_layoutLeftOfButtonBox, 0); bottomLayout->addWidget(this->buttonBox, 1000); QVBoxLayout* dialogLayout = new QVBoxLayout(this); WuQtUtilities::setLayoutSpacingAndMargins(dialogLayout, 0, 0); dialogLayout->addLayout(this->userWidgetLayout); dialogLayout->addLayout(bottomLayout); } /** * destructor. */ WuQDialog::~WuQDialog() { } ///** // * Set the size hint for the dialog to the given width and height. // * // * NOTE: If there are scrollbars added to the dialog, this size hint // * may be ignored. // */ //void //WuQDialog::setDialogSizeHint(const int32_t width, // const int32_t height) //{ // m_sizeHintWidth = width; // m_sizeHintHeight = height; //} /** * Add a widget to the left of the buttons at the bottom of the dialog. * More than one widget can be added and the first widget will be on * the left-most side. * * @param widget * Widget to add. */ void WuQDialog::addWidgetToLeftOfButtons(QWidget* widget) { m_layoutLeftOfButtonBox->addWidget(widget); } /** * Allows deletion of the dialog by the windowing * system when the dialog is closed. This should only * be true if the dialog is dynamically allocated (with * new) and not explicitly delete'ed by the user's code. * * @param deleteFlag * If true, dialog will be deleted by the windowing system. */ void WuQDialog::setDeleteWhenClosed(bool deleteFlag) { this->setAttribute(Qt::WA_DeleteOnClose, deleteFlag); } /** * Set the text of a standard button. If the * text is an empty string, the button is removed. * * @param button * Standard button enum identifying button. * @param text * Text for the button. */ void WuQDialog::setStandardButtonText(QDialogButtonBox::StandardButton button, const AString& text) { QPushButton* pushButton = this->buttonBox->button(button); if (text.isEmpty()) { if (pushButton != NULL) { this->buttonBox->removeButton(pushButton); } } else { if (pushButton == NULL) { pushButton = this->buttonBox->addButton(button); } pushButton->setText(text); } } /** * called to capture image after timeout so nothing obscures window. */ void WuQDialog::slotCaptureImageAfterTimeOut() { QImage image = QPixmap::grabWindow(this->winId()).toImage(); if (image.isNull() == false) { QClipboard* clipboard = QApplication::clipboard(); clipboard->setImage(image); QMessageBox::information(this, "Information", "An image of this dialog has been placed onto the computer's clipboard."); } } /** * called to capture image of window and place it on the clipboard */ void WuQDialog::slotMenuCaptureImageOfWindowToClipboard() { // // Need to delay capture so that the context sensistive // menu closes or else the menu will be in the captured image. // QApplication::processEvents(); QTimer::singleShot(1000, this, SLOT(slotCaptureImageAfterTimeOut())); } /** * add a capture image of window menu item to the menu. */ void WuQDialog::addImageCaptureToMenu(QMenu* menu) { menu->addAction("Capture Image to Clipboard", this, SLOT(slotMenuCaptureImageOfWindowToClipboard())); } /** * called by parent when context menu event occurs. */ void WuQDialog::contextMenuEvent(QContextMenuEvent* cme) { const bool enableCaptureMenu = false; if (enableCaptureMenu) { // // Popup menu for selection of pages // QMenu menu(this); // // Add menu item for image capture // addImageCaptureToMenu(&menu); // // Popup the menu // menu.exec(cme->globalPos()); } } /** * Called by parent when a key press event occurs * * This code is taken from QDialog::keyPressEvent. * so it may need to be updated with new version of * Qt. * * It is used to disable the AutoDault button * property which triggers a button click when * the return key is pressed on a dialog. */ void WuQDialog::keyPressEvent(QKeyEvent* e) { if (autoDefaultProcessingEnabledFlag) { QDialog::keyPressEvent(e); return; } bool acceptedEvent = false; // 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 if(e->modifiers() == Qt::ControlModifier && e->key() == Qt::Key_Period) { } else #endif if (!e->modifiers() || (e->modifiers() & Qt::KeypadModifier && e->key() == Qt::Key_Enter)) { switch (e->key()) { case Qt::Key_Enter: case Qt::Key_Return: e->accept(); acceptedEvent = true; break; case Qt::Key_Escape: break; default: return; } } if (acceptedEvent == false) { QDialog::keyPressEvent(e); } } /** * ring the bell. */ void WuQDialog::beep() { QApplication::beep(); } /** * show the watch cursor. */ void WuQDialog::showWaitCursor() { QApplication::setOverrideCursor(QCursor(Qt::WaitCursor)); } /** * normal cursor. */ void WuQDialog::showNormalCursor() { QApplication::restoreOverrideCursor(); } /** * called to close. */ bool WuQDialog::close() { return QDialog::close(); } /** * @return The dialog button box for adding buttons. */ QDialogButtonBox* WuQDialog::getDialogButtonBox() { return this->buttonBox; } /** * Sets the given widgets to be the window's top and central widget. * Note: WuQDialog takes ownership of the widget pointer * and deletes it at the appropriate time. * * This should be called ONE and ONLY one time. * * Unless prohibited by the second parameter, the central widget * will be automatically placed into a scroll area if the * widget makes the dialog too big for the screen. * * @param topWidget * The optional widget displayed at top (may be NULL). * @param centralWidget * The central widget. * @param bottomWidget * The optional widget displayed at bottom (may be NULL). * @param placeCentralWidgetInScrollAreaStatus * Status of using a scroll area for the central widget */ void WuQDialog::setTopBottomAndCentralWidgets(QWidget* topWidget, QWidget* centralWidget, QWidget* bottomWidget, const ScrollAreaStatus placeCentralWidgetInScrollAreaStatus) { CaretAssert(centralWidget); this->setTopBottomAndCentralWidgetsInternal(topWidget, centralWidget, bottomWidget, placeCentralWidgetInScrollAreaStatus); } /** * Sets the give widget to be the window's central widget. * Note: WuQDialog takes ownership of the widget pointer * and deletes it at the appropriate time. * * This should be called ONE and ONLY one time. * * Unless prohibited by the second parameter, the central widget * will be automatically placed into a scroll area if the * widget makes the dialog too big for the screen. * * @param centralWidget * The central widget. * @param placeCentralWidgetInScrollAreaStatus * Status of using a scroll area for the central widget */ void WuQDialog::setCentralWidget(QWidget* centralWidget, const ScrollAreaStatus placeCentralWidgetInScrollAreaStatus) { CaretAssert(centralWidget); this->setTopBottomAndCentralWidgetsInternal(NULL, centralWidget, NULL, placeCentralWidgetInScrollAreaStatus); } /** * Sets the given widgets to be the window's top and central widget. * Note: WuQDialog takes ownership of the widget pointer * and deletes it at the appropriate time. * * This should be called ONE and ONLY one time. * * Unless prohibited by the second parameter, the central widget * will be automatically placed into a scroll area if the * widget makes the dialog too big for the screen. * * @param topWidget * The widget display at top. * @param centralWidget * The central widget. * @param topWidget * The widget display at top. * @param placeCentralWidgetInScrollAreaStatus * Status of using a scroll area for the central widget */ void WuQDialog::setTopBottomAndCentralWidgetsInternal(QWidget* topWidget, QWidget* centralWidget, QWidget* bottomWidget, const ScrollAreaStatus placeCentralWidgetInScrollAreaStatus) { m_topWidget = topWidget; m_centralWidget = centralWidget; m_bottomWidget = bottomWidget; m_placeCentralWidgetInScrollAreaStatus = placeCentralWidgetInScrollAreaStatus; if (topWidget != NULL) { this->userWidgetLayout->addWidget(topWidget); } m_centralWidgetLayoutIndex = userWidgetLayout->count(); this->userWidgetLayout->addWidget(centralWidget); if (bottomWidget != NULL) { this->userWidgetLayout->addWidget(bottomWidget); } } /** * Enable auto default button processing. * Often it is very annoying so use this method * withe enable == false, to disable it. * * @param enabled * New status for auto default processing. */ void WuQDialog::setAutoDefaultButtonProcessing(bool enabled) { this->autoDefaultProcessingEnabledFlag = enabled; } /** * Disable the auto default property for ANY buttons * in this dialog. This method must be called after * the subclass has added its widget(s) to the dialog. */ void WuQDialog::disableAutoDefaultForAllPushButtons() { /* * Disable auto default for all push buttons */ QList allChildPushButtons = findChildren(QRegExp(".*")); QListIterator allChildPushButtonsIterator(allChildPushButtons); while (allChildPushButtonsIterator.hasNext()) { QPushButton* pushButton = allChildPushButtonsIterator.next(); pushButton->setAutoDefault(false); pushButton->setDefault(false); } } /** * Called when a help button has been added to the dialog. * User must override this method when adding a help button. */ void WuQDialog::helpButtonClicked() { CaretAssertMessage(0, "Help button was added to dialog but WuQDialog::helpButtonClicked() was not overriden"); } /** * Called for focus events. Since this dialog stores a pointer * to the overlay, we need to be aware that the overlay's parameters * may change or the overlay may even be deleted. So, when * this dialog gains focus, validate the overlay and then update * the dialog. * * @param event * The focus event. */ void WuQDialog::focusInEvent(QFocusEvent* /*event*/) { focusGained(); } /** * Will be called when dialog gains focus. User may override this * method to receive focus in events. */ void WuQDialog::focusGained() { /* nothing - intended to be overridden by users */ } /** * Gets called when the dialog is to be displayed. */ void WuQDialog::showEvent(QShowEvent* event) { int32_t resizedDialogWidth = -1; int32_t resizedDialogHeight = -1; if (m_firstTimeInShowMethodFlag) { const int32_t dialogWidth = sizeHint().width(); const int32_t dialogHeight = sizeHint().height(); /* * Maximum size is the size of the smallest screen. */ const QSize maxSize = WuQtUtilities::getMinimumScreenSize(); const int32_t margin = 100; const int32_t maximumDialogWidth = maxSize.width() - margin; const int32_t maximumDialogHeight = maxSize.height() - (margin * 2); // allow space top/bottom //const int32_t maximumDialogHeight = (maxSize.height() / 2) - margin; bool putCentralWidgetIntoScrollAreaFlag = false; bool testCentralWidgetForTooBig = false; switch (m_placeCentralWidgetInScrollAreaStatus) { case SCROLL_AREA_ALWAYS: putCentralWidgetIntoScrollAreaFlag = true; break; case SCROLL_AREA_AS_NEEDED_VERT_NO_HORIZ: putCentralWidgetIntoScrollAreaFlag = true; break; case SCROLL_AREA_AS_NEEDED: testCentralWidgetForTooBig = true; break; case SCROLL_AREA_NEVER: break; } if (testCentralWidgetForTooBig) { /* * Are contents too big for window? */ if ((dialogWidth > maximumDialogWidth) || (dialogHeight > maximumDialogHeight)) { putCentralWidgetIntoScrollAreaFlag = true; resizedDialogWidth = std::min(dialogWidth, maximumDialogWidth); resizedDialogHeight = std::min(dialogHeight, maximumDialogHeight); } } if (putCentralWidgetIntoScrollAreaFlag) { /* * Remove the central widget, * place it into a scroll area, * and insert the scroll area into the layout */ userWidgetLayout->removeWidget(m_centralWidget); QScrollArea* scrollArea = new QScrollArea(); scrollArea->setWidgetResizable(true); scrollArea->setWidget(m_centralWidget); if (m_placeCentralWidgetInScrollAreaStatus == SCROLL_AREA_AS_NEEDED_VERT_NO_HORIZ) { scrollArea->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); scrollArea->setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded); } userWidgetLayout->insertWidget(m_centralWidgetLayoutIndex, scrollArea); /* * Resize the dialog. */ if ((resizedDialogWidth > 0) && (resizedDialogHeight > 0)) { m_sizeHintWidth = resizedDialogWidth; m_sizeHintHeight = resizedDialogHeight; adjustSize(); } } } QDialog::showEvent(event); m_firstTimeInShowMethodFlag = false; } /** * @return The size hint. * * Overriding the size hint is the best way to set the size of a dialog. */ QSize WuQDialog::sizeHint () const { QSize sh = QDialog::sizeHint(); if ((m_sizeHintWidth > 0) && (m_sizeHintHeight > 0)) { sh.setWidth(m_sizeHintWidth); sh.setHeight(m_sizeHintHeight); /* * Reset the size hint so that the user can resize the dialog * without it reverting to this size hint. */ m_sizeHintWidth = -1; m_sizeHintHeight = -1; } return sh; } /** * Set the size of the dialog for next time it is displayed. * This must be called BEFORE displayng the dialog. */ void WuQDialog::setSizeOfDialogWhenDisplayed(const QSize& size) { const int32_t w = size.width(); const int32_t h = size.height(); if ((w > 0) && (h > 0)) { m_sizeHintWidth = w; m_sizeHintHeight = h; } } /** * A scroll area's size hint is often larger than its content widget and in * this case the dialog will be too large with blank space in the scroll area. * This method will adjust the size of the dialog which in turn will shrink * the size of the scroll area to better fit its content. This should only * be called once for a dialog so that the user can adjust the size of the * dialog, if desired. * * @param dialog * Dialog whose size is adjusted. * @param scrollArea * The scroll area in the dialog. */ void WuQDialog::adjustSizeOfDialogWithScrollArea(QDialog* dialog, QScrollArea* scrollArea) { /* * The content region of a scroll area is often too large vertically * so adjust the size of the dialog which will cause the scroll area * to approximately fit its content. */ QWidget* scrollAreaContentWidget = scrollArea->widget(); if (scrollAreaContentWidget != NULL) { int32_t contentHeight = scrollAreaContentWidget->sizeHint().height(); int32_t scrollHeight = scrollArea->sizeHint().height(); int32_t scrollBarHeight = scrollArea->horizontalScrollBar()->sizeHint().height(); int32_t diff = (scrollHeight - (contentHeight + scrollBarHeight + 10)); if (diff > 0) { QSize dialogSizeHint = dialog->sizeHint(); dialogSizeHint.setHeight(dialogSizeHint.height() - diff); dialog->resize(dialogSizeHint); } } } /** * Adds a button to the dialog. When the button is * pressed, userButtonPressed(QPushButton*) will be * called with the button that was created and returned * by this method. The subclass of the dialog MUST * override userButtonPressed(QPushButton*). * * @param text * Text for the pushbutton. * @param buttonRole * Role of button. NOTE: This is used for placement of buttons in * the appropriate location for the operating system. Any action, * such as closing the dialog will not occur because of this button * push. * @return * QPushButton that was created. */ QPushButton* WuQDialog::addUserPushButton(const AString& text, const QDialogButtonBox::ButtonRole buttonRole) { QPushButton* pushButton = getDialogButtonBox()->addButton(text, buttonRole); return pushButton; } /** * Called when a push button was added using addUserPushButton(). * Subclasses MUST override this if user push buttons were * added using addUserPushButton(). * * @param userPushButton * User push button that was pressed. * @return * The result that indicates action that should be taken * as a result of the button being pressed. */ WuQDialog::DialogUserButtonResult WuQDialog::userButtonPressed(QPushButton* userPushButton) { const AString msg = ("Subclass of WuQDialog added a user pushbutton but failed to override userButtonPressed for button labeled \"" + userPushButton->text() + "\""); CaretAssertMessage(0, msg); CaretLogSevere(msg); return RESULT_NONE; } /** * Called when the OK button is clicked. * If needed should override this to process * data when the OK button is clicked and then * call this to issue the accept signal. */ void WuQDialog::okButtonClicked() { if (! isModal()) { CaretAssertMessage(0, "WuQDialog::okButtonClicked() should never be called for a NON-modal dialog."); } accept(); } /** * Called when the Cancel button is clicked. * If needed should override this to process * data when the Cancel button is clicked. * Call this to issue the reject signal. */ void WuQDialog::cancelButtonClicked() { if (! isModal()) { CaretAssertMessage(0, "WuQDialog::cancelButtonClicked() should never be called for a NON-modal dialog."); } reject(); } /** * Called when the Apply button is pressed. * If needed should override this to process * data when the Apply button is pressed. */ void WuQDialog::applyButtonClicked() { if (isModal()) { CaretAssertMessage(0, "WuQDialog::applyButtonClicked() should never be called for a MODAL dialog."); } } /** * Called when the Close button is pressed. * If needed should override this to process * data when the Close button is pressed. */ void WuQDialog::closeButtonClicked() { if (isModal()) { CaretAssertMessage(0, "WuQDialog::closeButtonClicked() should never be called for a MODAL dialog."); } close(); } /** * Called when a button is pressed. */ void WuQDialog::clicked(QAbstractButton* button) { QDialogButtonBox::StandardButton standardButton = this->getDialogButtonBox()->standardButton(button); if (standardButton == QDialogButtonBox::Ok) { button->setEnabled(false); okButtonClicked(); button->setEnabled(true); } else if (standardButton == QDialogButtonBox::Cancel) { cancelButtonClicked(); } else if (standardButton == QDialogButtonBox::Apply) { applyButtonClicked(); } else if (standardButton == QDialogButtonBox::Close) { closeButtonClicked(); } else if (standardButton == QDialogButtonBox::Help) { this->helpButtonClicked(); } else { QPushButton* pushButton = qobject_cast(button); CaretAssert(pushButton); const DialogUserButtonResult result = this->userButtonPressed(pushButton); switch (result) { case RESULT_MODAL_ACCEPT: accept(); break; case RESULT_MODAL_REJECT: reject(); break; case RESULT_NON_MODAL_CLOSE: close(); break; case RESULT_NONE: break; }; } } connectome-workbench-1.2.3+git41-gc4c6c90/src/GuiQt/WuQDialog.h000066400000000000000000000131741300200146000236100ustar00rootroot00000000000000#ifndef __WU_QDIALOG_H__ #define __WU_QDIALOG_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 "AString.h" class QFocusEvent; class QHBoxLayout; class QKeyEvent; class QMenu; class QScrollArea; class QVBoxLayout; namespace caret { class WuQDialog : public QDialog { Q_OBJECT protected: WuQDialog(const AString& dialogTitle, QWidget* parent, Qt::WindowFlags f = 0); public: /** * Status of scroll area displayed */ enum ScrollAreaStatus { SCROLL_AREA_ALWAYS, SCROLL_AREA_AS_NEEDED, SCROLL_AREA_AS_NEEDED_VERT_NO_HORIZ, SCROLL_AREA_NEVER }; /** * Result of user button pressed. */ enum DialogUserButtonResult { /** MODAL accept which means OK pressed and dialog closes */ RESULT_MODAL_ACCEPT, /** MODAL reject which means Cancel pressed and dialog closes */ RESULT_MODAL_REJECT, /** NON-MODAL close dialog. */ RESULT_NON_MODAL_CLOSE, /** none which means no action is taken and dialog remains open */ RESULT_NONE }; virtual ~WuQDialog(); void setCentralWidget(QWidget* centralWidget, const ScrollAreaStatus placeCentralWidgetInScrollAreaStatus); void setTopBottomAndCentralWidgets(QWidget* topWidget, QWidget* centralWidget, QWidget* bottomWidget, const ScrollAreaStatus placeCentralWidgetInScrollAreaStatus); void setStandardButtonText(QDialogButtonBox::StandardButton button, const AString& text); QPushButton* addUserPushButton(const AString& text, const QDialogButtonBox::ButtonRole buttonRole); void setDeleteWhenClosed(bool deleteFlag); void setAutoDefaultButtonProcessing(bool enabled); void disableAutoDefaultForAllPushButtons(); void addWidgetToLeftOfButtons(QWidget* widget); static void beep(); static void showWaitCursor(); static void showNormalCursor(); static void adjustSizeOfDialogWithScrollArea(QDialog* dialog, QScrollArea* scrollArea); virtual QSize sizeHint() const; void setSizeOfDialogWhenDisplayed(const QSize& size); public slots: virtual bool close(); protected slots: void slotMenuCaptureImageOfWindowToClipboard(); void slotCaptureImageAfterTimeOut(); protected: QDialogButtonBox* getDialogButtonBox(); virtual DialogUserButtonResult userButtonPressed(QPushButton* userPushButton); void addImageCaptureToMenu(QMenu* menu); virtual void okButtonClicked(); virtual void cancelButtonClicked(); virtual void applyButtonClicked(); virtual void closeButtonClicked(); virtual void keyPressEvent(QKeyEvent* e); virtual void contextMenuEvent(QContextMenuEvent*); virtual void focusInEvent(QFocusEvent* event); virtual void helpButtonClicked(); virtual void focusGained(); virtual void showEvent(QShowEvent* event); // void setDialogSizeHint(const int32_t width, // const int32_t height); private slots: void clicked(QAbstractButton* button); private: void setTopBottomAndCentralWidgetsInternal(QWidget* topWidget, QWidget* centralWidget, QWidget* bottomWidget, const ScrollAreaStatus placeCentralWidgetInScrollAreaStatus); QVBoxLayout* userWidgetLayout; QDialogButtonBox* buttonBox; QHBoxLayout* m_layoutLeftOfButtonBox; bool autoDefaultProcessingEnabledFlag; bool m_firstTimeInShowMethodFlag; ScrollAreaStatus m_placeCentralWidgetInScrollAreaStatus; int32_t m_centralWidgetLayoutIndex; QWidget* m_topWidget; QWidget* m_centralWidget; QWidget* m_bottomWidget; mutable int32_t m_sizeHintWidth; mutable int32_t m_sizeHintHeight; }; #ifdef __WU_QDIALOG_DECLARE__ #endif // __WU_QDIALOG_DECLARE__ } #endif // __WU_QDIALOG_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/GuiQt/WuQDialogModal.cxx000066400000000000000000000153641300200146000251430ustar00rootroot00000000000000/*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 __WU_Q_DIALOG_MODAL_DECLARE__ #include "WuQDialogModal.h" #undef __WU_Q_DIALOG_MODAL_DECLARE__ #include "CaretAssert.h" #include "CaretLogger.h" using namespace caret; /** * \class caret::WuQDialogModal * \brief Base class for modal dialogs. * * A base class for modal dialogs. */ /** * Constructs a modal dialog. After construction, * use exec() to display the dialog. * * @param dialogTitle * Title for dialog. * @param parent * Parent widget on which this dialog is displayed. * @param f * optional Qt::WindowFlags */ WuQDialogModal::WuQDialogModal(const AString& dialogTitle, QWidget* parent, Qt::WindowFlags f) : WuQDialog(dialogTitle, parent, f) { m_isSaveDialogPosition = false; this->getDialogButtonBox()->addButton(QDialogButtonBox::Ok); this->getDialogButtonBox()->addButton(QDialogButtonBox::Cancel); // QObject::connect(this->getDialogButtonBox(), SIGNAL(clicked(QAbstractButton*)), // this, SLOT(clicked(QAbstractButton*))); this->getDialogButtonBox()->button(QDialogButtonBox::Ok)->setDefault(true); //this->getDialogButtonBox()->button(QDialogButtonBox::Ok)->setAutoDefault(true); } /** * Constructs a modal dialog. After construction, * use exec() to display the dialog. * * @param dialogTitle * Title for dialog. * @param centralWidget, * Central widget that is displayed in the dialog. * @param parent * Parent widget on which this dialog is displayed. * @param f * optional Qt::WindowFlags */ WuQDialogModal::WuQDialogModal(const AString& dialogTitle, QWidget* centralWidget, QWidget* parent, Qt::WindowFlags f) : WuQDialog(dialogTitle, parent, f) { this->getDialogButtonBox()->addButton(QDialogButtonBox::Ok); this->getDialogButtonBox()->addButton(QDialogButtonBox::Cancel); QObject::connect(this->getDialogButtonBox(), SIGNAL(clicked(QAbstractButton*)), this, SLOT(clicked(QAbstractButton*))); this->setCentralWidget(centralWidget, WuQDialog::SCROLL_AREA_NEVER); } /** * Destructor. */ WuQDialogModal::~WuQDialogModal() { } /** * Set the OK button to the given text. If the text * is zero length, the OK button is removed. * * @text * Text for OK button. */ void WuQDialogModal::setOkButtonText(const AString& text) { this->setStandardButtonText(QDialogButtonBox::Ok, text); /* if (text.isEmpty()) { this->getDialogButtonBox()->button(QDialogButtonBox::Cancel)->setDefault(true); this->getDialogButtonBox()->button(QDialogButtonBox::Cancel)->setAutoDefault(true); } else { this->getDialogButtonBox()->button(QDialogButtonBox::Ok)->setDefault(true); this->getDialogButtonBox()->button(QDialogButtonBox::Ok)->setAutoDefault(true); } */ } /** * Set the Cancel button to the given text. If the text * is zero length, the Cancel button is removed. * * @text * Text for OK button. */ void WuQDialogModal::setCancelButtonText(const AString& text) { this->setStandardButtonText(QDialogButtonBox::Cancel, text); } /** * If this method is called, each time the dialog is closed, it will * save the position of the dialog and restore the dialog to that * position when the dialog is reopened. * * @param savePositionName * Name used for saving the dialog's position. If the default value * (an empty string), the dialog's title is used for saving the * dialog's position. Note that is there is more than one dialog * with the same name positions may not be correctly restored. */ void WuQDialogModal::setSaveWindowPositionForNextTime(const AString& savePositionName) { m_isSaveDialogPosition = true; m_saveDialogPositionName = savePositionName; if (m_saveDialogPositionName.isEmpty()) { m_saveDialogPositionName = windowTitle(); if (m_saveDialogPositionName.isEmpty()) { m_isSaveDialogPosition = false; } } } /** * Shows/hides a widget. * Override to optionally place dialog via values passed to setDisplayedXY. */ void WuQDialogModal::setVisible(bool visible) { WuQDialog::setVisible(visible); if (m_isSaveDialogPosition) { /* * Find previous position of dialog. */ std::map::iterator iter = s_savedDialogPositions.find(m_saveDialogPositionName); if (visible) { if (iter != s_savedDialogPositions.end()) { /* * Restore dialog position */ SavedPosition savedPosition = iter->second; if ((savedPosition.x > 0) && (savedPosition.y > 0)) { move(savedPosition.x, savedPosition.y); } if ((savedPosition.w > 0) && (savedPosition.h > 0)) { resize(savedPosition.w, savedPosition.h); } } } else { /* * Save position of dialog. */ SavedPosition savedPosition; savedPosition.x = x(); savedPosition.y = y(); savedPosition.w = width(); savedPosition.h = height(); if (iter != s_savedDialogPositions.end()) { /* * Replace dialog position */ iter->second = savedPosition; } else { /* * Insert dialog position */ s_savedDialogPositions.insert(std::make_pair(m_saveDialogPositionName, savedPosition)); } } } } connectome-workbench-1.2.3+git41-gc4c6c90/src/GuiQt/WuQDialogModal.h000066400000000000000000000046641300200146000245710ustar00rootroot00000000000000#ifndef __WU_Q_DIALOG_MODAL__H_ #define __WU_Q_DIALOG_MODAL__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 "WuQDialog.h" namespace caret { class WuQDialogModal : public WuQDialog { Q_OBJECT public: WuQDialogModal(const AString& dialogTitle, QWidget* parent, Qt::WindowFlags f = 0); WuQDialogModal(const AString& dialogTitle, QWidget* centralWidget, QWidget* parent, Qt::WindowFlags f = 0); virtual ~WuQDialogModal(); void setOkButtonText(const AString& text); void setCancelButtonText(const AString& text); void setSaveWindowPositionForNextTime(const AString& savePositionName = ""); public slots: virtual void setVisible(bool); private: WuQDialogModal(const WuQDialogModal&); WuQDialogModal& operator=(const WuQDialogModal&); private: bool m_isSaveDialogPosition; AString m_saveDialogPositionName; struct SavedPosition { SavedPosition() { x = -1; y = -1; w = -1; h = -1; } int x; int y; int w; int h; }; static std::map s_savedDialogPositions; }; #ifdef __WU_Q_DIALOG_MODAL_DECLARE__ std::map WuQDialogModal::s_savedDialogPositions; #endif // __WU_Q_DIALOG_MODAL_DECLARE__ } // namespace #endif //__WU_Q_DIALOG_MODAL__H_ connectome-workbench-1.2.3+git41-gc4c6c90/src/GuiQt/WuQDialogNonModal.cxx000066400000000000000000000121341300200146000256060ustar00rootroot00000000000000 /*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 __WU_Q_DIALOG_NON_MODAL_DECLARE__ #include "WuQDialogNonModal.h" #undef __WU_Q_DIALOG_NON_MODAL_DECLARE__ #include "CaretAssert.h" #include "CaretLogger.h" using namespace caret; /** * \class caret::WuQDialogNonModal * \brief Base class for non-modal dialogs. * * A base class for non-modal dialogs. */ /** * Constructs a modal dialog. After construction, * use exec() to display the dialog. * * @param dialogTitle * Title for dialog. * @param parent * Parent widget on which this dialog is displayed. * @param f * optional Qt::WindowFlags */ WuQDialogNonModal::WuQDialogNonModal(const AString& dialogTitle, QWidget* parent, Qt::WindowFlags f) : WuQDialog(dialogTitle, parent, f) { this->getDialogButtonBox()->addButton(QDialogButtonBox::Apply); this->getDialogButtonBox()->addButton(QDialogButtonBox::Close); // QObject::connect(this->getDialogButtonBox(), SIGNAL(clicked(QAbstractButton*)), // this, SLOT(clicked(QAbstractButton*))); this->getDialogButtonBox()->button(QDialogButtonBox::Apply)->setDefault(false); this->getDialogButtonBox()->button(QDialogButtonBox::Apply)->setAutoDefault(false); this->getDialogButtonBox()->button(QDialogButtonBox::Close)->setDefault(false); this->getDialogButtonBox()->button(QDialogButtonBox::Close)->setAutoDefault(false); m_isPositionRestoredWhenReopened = false; m_positionWhenClosedValid = false; } /** * Destructor. */ WuQDialogNonModal::~WuQDialogNonModal() { } /** * Gets called when the dialog is closing. * Overriden so that position of dialog * can be saved. */ void WuQDialogNonModal::closeEvent(QCloseEvent* /*event*/) { // if (m_isPositionRestoredWhenReopened) { // /* // * Save position and size of dialog so that // * when it is shown next time, it will be // * in the position and size as when it was // * closed. // */ // m_positionWhenClosedValid = true; // m_positionWhenClosed = this->pos(); // m_sizeWhenClosed = this->size(); // } // // WuQDialog::closeEvent(event); emit dialogWasClosed(); } /** * Gets called when the dialog is to be displayed. */ void WuQDialogNonModal::showEvent(QShowEvent* event) { // if (m_isPositionRestoredWhenReopened) { // if (m_positionWhenClosedValid) { // /* // * Restore the dialog in the position and size that it // * was in when closed. Use move() for position and // * the size hint for the size. // */ // this->move(m_positionWhenClosed); // this->resize(m_sizeWhenClosed); //// const int32_t w = m_sizeWhenClosed.width(); //// const int32_t h = m_sizeWhenClosed.height(); //// if ((w > 0) //// && (h > 0)) { //// this->setDialogSizeHint(w, //// h); //// adjustSize(); //// } // } // } WuQDialog::showEvent(event); } /** * This slot can be called and it simply calls * applyButtonClicked. This slot can be connected * to GUI components. */ void WuQDialogNonModal::apply() { this->applyButtonClicked(); } /** * Set the Apply button to the given text. If the text * is zero length, the Apply button is removed. * * @text * Text for OK button. */ void WuQDialogNonModal::setApplyButtonText(const AString& text) { this->setStandardButtonText(QDialogButtonBox::Apply, text); } /** * Set the Close button to the given text. If the text * is zero length, the Close button is removed. * * @text * Text for OK button. */ void WuQDialogNonModal::setCloseButtonText(const AString& text) { this->setStandardButtonText(QDialogButtonBox::Close, text); } /** * If the given parameter is true, save the position of this * dialog when it is closed. Next time window is displayed * in the current session, use the position at the time the * dialog was closed. * @param saveIt * If true save the position for next time. */ void WuQDialogNonModal::setSaveWindowPositionForNextTime(const bool saveIt) { m_isPositionRestoredWhenReopened = saveIt; } connectome-workbench-1.2.3+git41-gc4c6c90/src/GuiQt/WuQDialogNonModal.h000066400000000000000000000045511300200146000252370ustar00rootroot00000000000000#ifndef __WU_Q_DIALOG_NON_MODAL__H_ #define __WU_Q_DIALOG_NON_MODAL__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 "WuQDialog.h" namespace caret { class WuQDialogNonModal : public WuQDialog { Q_OBJECT public: WuQDialogNonModal(const AString& dialogTitle, QWidget* parent = 0, Qt::WindowFlags f = 0); virtual ~WuQDialogNonModal(); void setApplyButtonText(const AString& text); void setCloseButtonText(const AString& text); /** May be called requesting the dialog to update its content */ virtual void updateDialog() = 0; void setSaveWindowPositionForNextTime(const bool saveIt); signals: /** * This signal is emitted when the dialog is closed (hidden). */ void dialogWasClosed(); protected slots: void apply(); protected: virtual void closeEvent(QCloseEvent* event); virtual void showEvent(QShowEvent* event); private: WuQDialogNonModal(const WuQDialogNonModal&); WuQDialogNonModal& operator=(const WuQDialogNonModal&); QPoint m_positionWhenClosed; QSize m_sizeWhenClosed; bool m_positionWhenClosedValid; bool m_isPositionRestoredWhenReopened; }; #ifdef __WU_Q_DIALOG_NON_MODAL_DECLARE__ // #endif // __WU_Q_DIALOG_NON_MODAL_DECLARE__ } // namespace #endif //__WU_Q_DIALOG_NON_MODAL__H_ connectome-workbench-1.2.3+git41-gc4c6c90/src/GuiQt/WuQDoubleSlider.cxx000066400000000000000000000075371300200146000253470ustar00rootroot00000000000000 /*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 __WU_Q_DOUBLE_SLIDER_DECLARE__ #include "WuQDoubleSlider.h" #undef __WU_Q_DOUBLE_SLIDER_DECLARE__ #include using namespace caret; /** * \class caret::WuQDoubleSlider * \brief A slider for real values. * * Creates a slider for real values by encapsulating * a QSlider that operates only on integer values. */ /** * Constructor. * * @param orientation * horizontal or vertical * @param parent * optional parent */ WuQDoubleSlider::WuQDoubleSlider(Qt::Orientation orientation, QObject* parent) : WuQWidget(parent) { this->slider = new QSlider(orientation); this->slider->setRange(0, 1000); QObject::connect(this->slider, SIGNAL(valueChanged(int)), this, SLOT(qSliderValueChanged(int))); this->setRange(-100.0, 100.0); this->setValue(0); } /** * Destructor. */ WuQDoubleSlider::~WuQDoubleSlider() { } /** * Called when the encapsulated QSlider * value is changed. * @param value * New value. */ void WuQDoubleSlider::qSliderValueChanged(int value) { const double dSlider = this->slider->maximum() - this->slider->minimum(); const double parametricValue = (static_cast(value) - this->slider->minimum()) / dSlider; double dRange = this->maximum - this->minimum; if (dRange == 0.0) { dRange = 1.0; } this->sliderValue = dRange * parametricValue + this->minimum; emit valueChanged(this->sliderValue); } /** * @return The widget that is enapsulated. */ QWidget* WuQDoubleSlider::getWidget() { return this->slider; } /** * Set range of values. * @param minValue * New value for minimum. * @param maxValue * New value for maximum. */ void WuQDoubleSlider::setRange(double minValue, double maxValue) { this->minimum = minValue; this->maximum = maxValue; if (this->minimum > this->maximum) { this->maximum = this->minimum; } this->updateSlider(); } /** * @return The value of the slider. */ double WuQDoubleSlider::value() const { return this->sliderValue; } /** * Set the value for the slider. * @param d * New value. */ void WuQDoubleSlider::setValue(double valueIn) { this->sliderValue = valueIn; this->updateSlider(); emit valueChanged(this->sliderValue); } /** * Update the encapsulated slider. */ void WuQDoubleSlider::updateSlider() { double dRange = this->maximum - this->minimum; if (dRange == 0.0) { dRange = 1.0; } if (this->sliderValue > this->maximum) { this->sliderValue = this->maximum; } if (this->sliderValue < this->minimum) { this->sliderValue = this->minimum; } 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; this->slider->blockSignals(true); this->slider->setValue(qSliderValue); this->slider->blockSignals(false); } connectome-workbench-1.2.3+git41-gc4c6c90/src/GuiQt/WuQDoubleSlider.h000066400000000000000000000037301300200146000247630ustar00rootroot00000000000000#ifndef __WU_Q_DOUBLE_SLIDER__H_ #define __WU_Q_DOUBLE_SLIDER__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 "WuQWidget.h" class QSlider; namespace caret { class WuQDoubleSlider : public WuQWidget { Q_OBJECT public: WuQDoubleSlider(Qt::Orientation orientation, QObject* parent); virtual ~WuQDoubleSlider(); QWidget* getWidget(); void setRange(double minValue, double maxValue); double value() const; signals: void valueChanged(double); public slots: void setValue(double); private slots: void qSliderValueChanged(int); private: WuQDoubleSlider(const WuQDoubleSlider&); WuQDoubleSlider& operator=(const WuQDoubleSlider&); void updateSlider(); QSlider* slider; double minimum; double maximum; double sliderValue; }; #ifdef __WU_Q_DOUBLE_SLIDER_DECLARE__ // #endif // __WU_Q_DOUBLE_SLIDER_DECLARE__ } // namespace #endif //__WU_Q_DOUBLE_SLIDER__H_ connectome-workbench-1.2.3+git41-gc4c6c90/src/GuiQt/WuQEventBlockingFilter.cxx000066400000000000000000000107351300200146000266640ustar00rootroot00000000000000 /*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 __WU_Q_EVENT_BLOCKING_FILTER_DECLARE__ #include "WuQEventBlockingFilter.h" #undef __WU_Q_EVENT_BLOCKING_FILTER_DECLARE__ #include #include using namespace caret; /** * \class caret::WuQEventBlockingFilter * \brief A QEvent Filter * * A QEvent Filter. There are cases in which one wants to * block a specific event from being processed by widgets. * For example, on Mac, dragging the mouse over a combo box * causes a wheel event that unintentionally changes the * value of the combo box. * * To use an instance of this blocking filter, (1) Create * an instance of this class, (2) call setEventBlocked() which * the event enumerated type that is to be blocked, (3) pass * the instance of this class to the widget's * installEventFilter() method. */ /** * Constructor. * @param object * Parent that will own this event filter. The parent * will handle destruction of this event filter. */ WuQEventBlockingFilter::WuQEventBlockingFilter(QObject* parent) : QObject(parent) { } /** * Add a wheel blocking filter to a combo box (on mac only). On a Mac, * if the mouse pointer is moved across a combo box (particularly when * the user is using a Track Pad), Qt will get this event and apply it * as a wheel event to the combo box changing the selection in the * combo box. * * @param comboBox * Combo box that has its wheel event blocked. */ void WuQEventBlockingFilter::blockMouseWheelEventInMacComboBox(QComboBox* comboBox) { #ifdef CARET_OS_MACOSX /* * Attach an event filter that blocks wheel events in the combo box if Mac */ WuQEventBlockingFilter* comboBoxWheelEventBlockingFilter = new WuQEventBlockingFilter(comboBox); comboBoxWheelEventBlockingFilter->setEventBlocked(QEvent::Wheel, true); comboBox->installEventFilter(comboBoxWheelEventBlockingFilter); #endif // CARET_OS_MACOSX } /** * Destructor. */ WuQEventBlockingFilter::~WuQEventBlockingFilter() { } /** * Set the blocking status for the given event type. * @param eventType * Type of event to block. * @param blockedStatus * New status for blocking. */ void WuQEventBlockingFilter::setEventBlocked(const QEvent::Type eventType, const bool blockedStatus) { const int eventAsInt = static_cast(eventType); this->blockedEventsHashTable.insert(eventAsInt, blockedStatus); } /** * Is the given event blocked? * @param eventType * Type of event. * @return * True if event is being blocked, else false. */ bool WuQEventBlockingFilter::isEventBlocked(const QEvent::Type eventType) const { const int eventAsInt = static_cast(eventType); return this->blockedEventsHashTable.value(eventAsInt, false); } /** * Filters events for the given object. * @param object * Object for which events are being filtered. * @param event * Event that is examined for blocking. * @ @return * True, meaning that this event will not receive * any furthere processing. Or false, meaning * that the processing has been passed to the * parent class of the object. */ bool WuQEventBlockingFilter::eventFilter(QObject* object, QEvent* event) { const int eventAsInt = static_cast(event->type()); if (this->blockedEventsHashTable.value(eventAsInt, false)) { return true; } /* * Let parent do the filtering */ return QObject::eventFilter(object, event); } connectome-workbench-1.2.3+git41-gc4c6c90/src/GuiQt/WuQEventBlockingFilter.h000066400000000000000000000042511300200146000263050ustar00rootroot00000000000000#ifndef __WU_Q_EVENT_BLOCKING_FILTER__H_ #define __WU_Q_EVENT_BLOCKING_FILTER__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 class QComboBox; namespace caret { class WuQEventBlockingFilter : public QObject { Q_OBJECT public: static void blockMouseWheelEventInMacComboBox(QComboBox* comboBox); WuQEventBlockingFilter(QObject* parent); virtual ~WuQEventBlockingFilter(); void setEventBlocked(const QEvent::Type eventType, const bool blockedStatus); bool isEventBlocked(const QEvent::Type eventType) const; protected: bool eventFilter(QObject* object, QEvent* event); private: WuQEventBlockingFilter(const WuQEventBlockingFilter&); WuQEventBlockingFilter& operator=(const WuQEventBlockingFilter&); /** * Hash Table is probably fastest way to track multiple events * that can be blocked. Key is int (QEvent::Type) and value * is blocked status. */ QHash blockedEventsHashTable; }; #ifdef __WU_Q_EVENT_BLOCKING_FILTER_DECLARE__ // #endif // __WU_Q_EVENT_BLOCKING_FILTER_DECLARE__ } // namespace #endif //__WU_Q_EVENT_BLOCKING_FILTER__H_ connectome-workbench-1.2.3+git41-gc4c6c90/src/GuiQt/WuQFactory.cxx000066400000000000000000000424701300200146000243740ustar00rootroot00000000000000 /*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 __WU_Q_FACTORY_DECLARE__ #include "WuQFactory.h" #undef __WU_Q_FACTORY_DECLARE__ #include "CaretLogger.h" using namespace caret; /** * \class caret::WuQFactory * \brief A factory for creating Qt Widgets with desired attributes. * \ingroup GuiQt * * This factory creates Qt Widgets with some attributes set to desired * values. For QSpinBox, by default, issues value changed events as * each character is entered into the spin box. So, if the user enters * five numbers, five events are issued, which at times, can cause * undesired updates to the user-interface. In addition, the methods in * this factory can simplify the creation of new widgets. *

* Qt3 contained overloaded constructors that contained parameters for * widget attributes but these were removed in Qt4 most likely because * there was no way to clearly indicate the purpose of the parameters other * than the documentation. Thus, in Qt4 a widget must be created and then * methods called to set the widget's attributes. Using the a naming * convention in these factory methods removes the ambiguity about any * parameters. *

* Rules for the method naming:
*

  1. use the pattern "new[OptionalParameterNames][WithSignal[Type]]
    *
      *
    • A method name always begins with new *
    • WidgetType is the name of the Qt widget without the Q *
    • OptionalParameterNames describe any additional parameters *
    • WithSignal is at the end of the method name for those methods * that connect a value changed signal to a receiving class and slot. If a parameter * is passed, the type should also be indicated (WithSignalInt, WithSignalString, etc.). * Keep in mind that the receiving slot does not need to contain any parameters even * if the signal does contain parameters. In most cases, the signal is only issued * if the user changes the value. However, some widgets issue a signal when either the * user changes the value or the value is changed programmatically and this should be * noted in the documentation. *
    *
  2. Do not use default parameters. *
  3. Do not overload method names (identically named methods containing different parameters). *
  4. This naming convention may result in long names. However, the names will make the usage fairly obvious. *
  5. These naming patterns is based off that for Objective-C. *
*/ #include #include #include #include #include #include #include "WuQEventBlockingFilter.h" /** * Constructor. */ WuQFactory::WuQFactory() { } /** * Destructor. */ WuQFactory::~WuQFactory() { } /** * Create a combo box. * * On Apple computers, the mouse wheel can cause unintended changes * to a combo box even if the combo box does not have focus. So, * block the wheel event on Apple computers. In addition, on Apple * computers, every item that is in the combo box is displayed in * the pop-up menu (as large as the vertical size of the screen) * so use a Windows Style combo box to improve the usability. * * @return * A combo box. */ QComboBox* WuQFactory::newComboBox() { QComboBox* cb = new QComboBox(); #ifdef CARET_OS_MACOSX /* * Attach an event filter that blocks wheel events in the combo box if Mac */ WuQEventBlockingFilter* comboBoxWheelEventBlockingFilter = new WuQEventBlockingFilter(cb); comboBoxWheelEventBlockingFilter->setEventBlocked(QEvent::Wheel, true); cb->installEventFilter(comboBoxWheelEventBlockingFilter); //setWindowsStyleForApple(cb); #endif // CARET_OS_MACOSX return cb; } /** * Create a combo box. * * On Apple computers, the mouse wheel can cause unintended changes * to a combo box even if the combo box does not have focus. So, * block the wheel event on Apple computers. In addition, on Apple * computers, every item that is in the combo box is displayed in * the pop-up menu (as large as the vertical size of the screen) * so use a Windows Style combo box to improve the usability. * * @param receiver * Object that received the signal when the value is changed by the user. * @param method * Method that is connected to the spin box's valueChanged(int) * signal. * @return * A combo box. */ QComboBox* WuQFactory::newComboBoxSignalInt(QObject* receiver, const char* method) { QComboBox* cb = newComboBox(); QObject::connect(cb, SIGNAL(activated(int)), receiver, method); return cb; } /** * Create a spin box. * * The minimum value is the most negative 32-bit integer, the maximum * values is the most positive 32-bit integer, step size is one, and * the default value is zero. * Keyboard tracking is disabled so that signal are NOT issued when * the user changes the text contained in the spin box. * * @return * A QSpinBox initialized with the default parameters. */ QSpinBox* WuQFactory::newSpinBox() { QSpinBox* sb = newSpinBoxWithMinMaxStep(std::numeric_limits::min(), std::numeric_limits::max(), 1); return sb; } /** * Create a spin box. * * The minimum value is the most negative 32-bit integer, the maximum * values is the most positive 32-bit integer, step size is one, and * the default value is zero. * Keyboard tracking is disabled so that signal are NOT issued when * the user changes the text contained in the spin box. *

* NOTE: The signal contains an integer parameters that is the new value * contained in the spin box. The signal is emitted when the user * changes the value AND when the value is changed programatically. * * @param receiver * Object that received the signal when the value is changed. * @param method * Method that is connected to the spin box's valueChanged(int) * signal. * @return * A QSpinBox initialized with the default parameters. */ QSpinBox* WuQFactory::newSpinBoxWithSignalInt(QObject* receiver, const char* method) { QSpinBox* sb = newSpinBoxWithMinMaxStepSignalInt(std::numeric_limits::min(), std::numeric_limits::max(), 1, receiver, method); return sb; } /** * Create a spin box with the given minimum, maximum, and step values. * Keyboard tracking is disabled so that signal are NOT issued when * the user changes the text contained in the spin box. *

* The default value is zero. If zero is not within the given * minimum and maximum values, the default value is the minimum value. * * @param minimumValue * Minimum value for spin box. * @param maximumValue * Maximum value for spin box. * @param stepSize * Step (change in value) when the user increments the spin box. * @return * A QSpinBox initialized with the given parameters. */ QSpinBox* WuQFactory::newSpinBoxWithMinMaxStep(const int minimumValue, const int maximumValue, const int stepSize) { QSpinBox* sb = new QSpinBox(); sb->setMinimum(minimumValue); sb->setMaximum(maximumValue); sb->setSingleStep(stepSize); sb->setKeyboardTracking(false); if ((0 >= minimumValue) && (0 <= maximumValue)) { sb->setValue(0); } else { sb->setValue(minimumValue); } return sb; } /** * Create a spin box with the given minimum, maximum, and step values. * Keyboard tracking is disabled so that signal are NOT issued when * the user changes the text contained in the spin box. *

* The default value is zero. If zero is not within the given * minimum and maximum values, the default value is the minimum value. *

* NOTE: The signal contains an integer parameters that is the new value * contained in the spin box. The signal is emitted when the user * changes the value AND when the value is changed programatically. * * @param minimumValue * Minimum value for spin box. * @param maximumValue * Maximum value for spin box. * @param stepSize * Step (change in value) when the user increments the spin box. * @param receiver * Object that received the signal when the value is changed. * @param method * Method that is connected to the spin box's valueChanged(int) * signal. * @return * A QSpinBox initialized with the given parameters. */ QSpinBox* WuQFactory::newSpinBoxWithMinMaxStepSignalInt(const int minimumValue, const int maximumValue, const int stepSize, QObject* receiver, const char* method) { QSpinBox* sb = newSpinBoxWithMinMaxStep(minimumValue, maximumValue, stepSize); QObject::connect(sb, SIGNAL(valueChanged(int)), receiver, method); return sb; } /** * Create a double spin box with the minimum value set to the most negative * float (not double) value, the maximum value set to the most positive * float (not double) value, the step size set to one and the number of * digits right of the decimal set to two. The default value is zero. * Keyboard tracking is disabled so that signal are NOT issued when * the user changes the text contained in the spin box. *

* Since workbench stores most data as 32-bit floats, the default minimum and * maximum values are those for float, not double. * * @return * A QDoubleSpinBox initialized with the default parameters. */ QDoubleSpinBox* WuQFactory::newDoubleSpinBox() { QDoubleSpinBox* sb = newDoubleSpinBoxWithMinMaxStepDecimals(-std::numeric_limits::max(), std::numeric_limits::max(), 1.0, 2); return sb; } /** * Create a double spin box with the minimum value set to the most negative * float (not double) value, the maximum value set to the most positive * float (not double) value, the step size set to one and the number of * digits right of the decimal set to two. The default value is zero. * Keyboard tracking is disabled so that signal are NOT issued when * the user changes the text contained in the spin box. *

* Since workbench stores most data as 32-bit floats, the default minimum and * maximum values are those for float, not double. * *

* NOTE: The signal contains an double parameter that is the new value * contained in the spin box. The signal is emitted when the user * changes the value AND when the value is changed programatically. * * @param receiver * Object that received the signal when the value is changed. * @param method * Method that is connected to the spin box's valueChanged(double) * signal. * @return * A QDoubleSpinBox initialized with the default parameters. */ QDoubleSpinBox* WuQFactory::newDoubleSpinBoxWithSignalDouble(QObject* receiver, const char* method) { QDoubleSpinBox* sb = newDoubleSpinBoxWithMinMaxStepDecimalsSignalDouble(-std::numeric_limits::max(), std::numeric_limits::max(), 1.0, 2, receiver, method); return sb; } /** * Create a double spin box with the given minimum, maximum, step, and * digits right of decimal values. * Keyboard tracking is disabled so that signal are NOT issued when * the user changes the text contained in the spin box. *

* The default value is zero. If zero is not within the given * minimum and maximum values, the default value is the minimum value. * * @param minimumValue * Minimum value for spin box. * @param maximumValue * Maximum value for spin box. * @param stepSize * Step (change in value) when the user increments the spin box. * @param digitsRightOfDecimal * Number of digits to right of decimal shown in the spin box. * @return * A QDoubleSpinBox initialized with the given parameters. */ QDoubleSpinBox* WuQFactory::newDoubleSpinBoxWithMinMaxStepDecimals(const double minimumValue, const double maximumValue, const double stepSize, const int digitsRightOfDecimal) { QDoubleSpinBox* sb = new QDoubleSpinBox(); sb->setMinimum(minimumValue); sb->setMaximum(maximumValue); sb->setSingleStep(stepSize); sb->setDecimals(digitsRightOfDecimal); sb->setKeyboardTracking(false); if ((0 >= minimumValue) && (0 <= maximumValue)) { sb->setValue(0); } else { sb->setValue(minimumValue); } return sb; } /** * Create a double spin box with the given minimum, maximum, step, and * digits right of decimal values. * Keyboard tracking is disabled so that signal are NOT issued when * the user changes the text contained in the spin box. *

* The default value is zero. If zero is not within the given * minimum and maximum values, the default value is the minimum value. *

* NOTE: The signal contains an double parameter that is the new value * contained in the spin box. The signal is emitted when the user * changes the value AND when the value is changed programatically. * * @param minimumValue * Minimum value for spin box. * @param maximumValue * Maximum value for spin box. * @param stepSize * Step (change in value) when the user increments the spin box. * @param digitsRightOfDecimal * Number of digits to right of decimal shown in the spin box. * @param receiver * Object that received the signal when the value is changed. * @param method * Method that is connected to the spin box's valueChanged(double) * signal. * @return * A QDoubleSpinBox initialized with the given parameters. */ QDoubleSpinBox* WuQFactory::newDoubleSpinBoxWithMinMaxStepDecimalsSignalDouble(const double minimumValue, const double maximumValue, const double stepSize, const int digitsRightOfDecimal, QObject* receiver, const char* method) { QDoubleSpinBox* sb = newDoubleSpinBoxWithMinMaxStepDecimals(minimumValue, maximumValue, stepSize, digitsRightOfDecimal); QObject::connect(sb, SIGNAL(valueChanged(double)), receiver, method); return sb; } /** * Sets the style of the given windows to windows. */ void WuQFactory::setWindowsStyleForApple(QWidget* w) { // w->setStyle(new WorkbenchMacStyle()); // return; /* * Only try creating once */ if (s_windowsStyleForAppleWasCreated == false) { s_windowsStyleForAppleWasCreated = true; s_windowsStyleForApple = QStyleFactory::create("Windows"); if (s_windowsStyleForApple == NULL) { CaretLogSevere("Failed to create Windows Style"); } } if (s_windowsStyleForApple != NULL) { w->setStyle(s_windowsStyleForApple); } } //int //WorkbenchMacStyle::styleHint ( StyleHint sh, const QStyleOption * opt, const QWidget * w, QStyleHintReturn * hret) const //{ // int value = QMacStyle::styleHint(sh, opt, w, hret); // // if (sh == QStyle::SH_ComboBox_Popup) { // value = 0; // } // // return value; //} connectome-workbench-1.2.3+git41-gc4c6c90/src/GuiQt/WuQFactory.h000066400000000000000000000104011300200146000240060ustar00rootroot00000000000000#ifndef __WU_Q_FACTORY_H__ #define __WU_Q_FACTORY_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 QComboBox; class QDoubleSpinBox; class QObject; class QSpinBox; class QStyle; class QWidget; namespace caret { class WuQFactory { public: static QComboBox* newComboBox(); static QComboBox* newComboBoxSignalInt(QObject* receiver, const char* method); static QSpinBox* newSpinBox(); static QSpinBox* newSpinBoxWithSignalInt(QObject* receiver, const char* method); static QSpinBox* newSpinBoxWithMinMaxStep(const int minimumValue, const int maximumValue, const int stepSize); static QSpinBox* newSpinBoxWithMinMaxStepSignalInt(const int minimumValue, const int maximumValue, const int stepSize, QObject* receiver, const char* method); static QDoubleSpinBox* newDoubleSpinBox(); static QDoubleSpinBox* newDoubleSpinBoxWithSignalDouble(QObject* receiver, const char* method); static QDoubleSpinBox* newDoubleSpinBoxWithMinMaxStepDecimals(const double minimumValue, const double maximumValue, const double stepSize, const int digitsRightOfDecimal); static QDoubleSpinBox* newDoubleSpinBoxWithMinMaxStepDecimalsSignalDouble(const double minimumValue, const double maximumValue, const double stepSize, const int digitsRightOfDecimal, QObject* receiver, const char* method); private: WuQFactory(); virtual ~WuQFactory(); WuQFactory(const WuQFactory&); WuQFactory& operator=(const WuQFactory&); static void setWindowsStyleForApple(QWidget* w); static QStyle* s_windowsStyleForApple; static bool s_windowsStyleForAppleWasCreated; public: // ADD_NEW_METHODS_HERE private: // ADD_NEW_MEMBERS_HERE }; // class WorkbenchMacStyle : public QMacStyle { // public: // WorkbenchMacStyle() {} // // virtual ~WorkbenchMacStyle() {} // virtual int styleHint ( StyleHint sh, const QStyleOption * opt = 0, const QWidget * w = 0, QStyleHintReturn * hret = 0 ) const; // }; #ifdef __WU_Q_FACTORY_DECLARE__ QStyle* WuQFactory::s_windowsStyleForApple = NULL; bool WuQFactory::s_windowsStyleForAppleWasCreated; #endif // __WU_Q_FACTORY_DECLARE__ } // namespace #endif //__WU_Q_FACTORY_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/GuiQt/WuQGridLayoutGroup.cxx000066400000000000000000000141341300200146000260610ustar00rootroot00000000000000 /*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 #define __WU_Q_GRID_LAYOUT_GROUP_DECLARE__ #include "WuQGridLayoutGroup.h" #undef __WU_Q_GRID_LAYOUT_GROUP_DECLARE__ using namespace caret; /** * \class caret::WuQGridLayoutGroup * \brief Controls display of a group of widgets in a QGridLayout * * When displaying QWidgets in a QGridLayout one may want to * hide the widgets in a row. However, even though the widgets * are hidden and QGridLayout removes the space occupied by * the widgets, it seems to keep the spacing around the widgets * and the layout does not full shrink. * * This group will remove and add the widgets as the group * is set visible or hidden. This seems to remove the extra * spacing and allows the QGridLayout to fully shrink. */ /** * Constructor. * @param gridLayout * Grid layout of the widgets. * @param parent * Parent object. */ WuQGridLayoutGroup::WuQGridLayoutGroup(QGridLayout* gridLayout, QObject* parent) : QObject(parent) { this->gridLayout = gridLayout; this->areWidgetsInLayout = true; } /** * Destructor. */ WuQGridLayoutGroup::~WuQGridLayoutGroup() { const int numItems = this->layoutItems.size(); for (int i = 0; i < numItems; i++) { delete this->layoutItems[i]; } this->layoutItems.clear(); } /** * Add a widget to the group. * @param widget * Widget added to the layout * @param row * Row for widget. * @param column * Column for widget. * @param alignment * Alignment of widget. */ void WuQGridLayoutGroup::addWidget(QWidget* widget, int row, int column, Qt::Alignment alignment) { this->addWidget(widget, row, column, 1, 1, alignment); } /** * Add a widget to the group. * @param widget * Widget added to the layout * @param fromRow * Row for widget. * @param fromColumn * Column for widget. * @param rowSpan * Rows for widget. * @param columnSpan * Columns for widget. * @param alignment * Alignment of widget. */ void WuQGridLayoutGroup::addWidget(QWidget* widget, int fromRow, int fromColumn, int rowSpan, int columnSpan, Qt::Alignment alignment) { ItemRowCol* irc = new ItemRowCol(widget, fromRow, fromColumn, rowSpan, columnSpan, alignment); this->layoutItems.push_back(irc); this->gridLayout->addWidget(irc->widget, irc->fromRow, irc->fromColumn, irc->rowSpan, irc->columnSpan, irc->alignment); } /** * Set the visibility of the widgets in the group. */ void WuQGridLayoutGroup::setVisible(bool visible) { if (visible) { if (this->areWidgetsInLayout) { return; } const int numItems = this->layoutItems.size(); for (int i = 0; i < numItems; i++) { ItemRowCol* irc = this->layoutItems[i]; this->gridLayout->addWidget(irc->widget, irc->fromRow, irc->fromColumn, irc->rowSpan, irc->columnSpan, irc->alignment); irc->widget->setVisible(true); } this->areWidgetsInLayout = true; } else { if (this->areWidgetsInLayout) { const int numItems = this->layoutItems.size(); for (int i = 0; i < numItems; i++) { ItemRowCol* irc = this->layoutItems[i]; irc->widget->setVisible(false); this->gridLayout->removeWidget(irc->widget); } this->areWidgetsInLayout = false; } } } /** * Constructor for widget. * * @param widget * Widget added to the layout * @param fromRow * Row for widget. * @param fromColumn * Column for widget. * @param rowSpan * Rows for widget. * @param columnSpan * Columns for widget. * @param alignment * Alignment of widget. */ WuQGridLayoutGroup::ItemRowCol::ItemRowCol(QWidget *widget, int fromRow, int fromColumn, int rowSpan, int columnSpan, Qt::Alignment alignment) { this->widget = widget; this->fromRow = fromRow; this->fromColumn = fromColumn; this->rowSpan = rowSpan; this->columnSpan = columnSpan; this->alignment = alignment; } /** * @return Number of rows in grid layout. */ int WuQGridLayoutGroup::rowCount() const { return this->gridLayout->rowCount(); } /** * @return Number of columns in grid layout. */ int WuQGridLayoutGroup::columnCount() const { return this->gridLayout->columnCount(); } connectome-workbench-1.2.3+git41-gc4c6c90/src/GuiQt/WuQGridLayoutGroup.h000066400000000000000000000052741300200146000255130ustar00rootroot00000000000000#ifndef __WU_Q_GRID_LAYOUT_GROUP__H_ #define __WU_Q_GRID_LAYOUT_GROUP__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 class QGridLayout; namespace caret { class WuQGridLayoutGroup : public QObject { Q_OBJECT public: WuQGridLayoutGroup(QGridLayout* gridLayout, QObject* parent = 0); virtual ~WuQGridLayoutGroup(); void addWidget(QWidget* widget, int row, int column, Qt::Alignment alignment = 0); void addWidget(QWidget* widget, int fromRow, int fromColumn, int rowSpan, int columnSpan, Qt::Alignment alignment = 0); int rowCount() const; int columnCount() const; void setVisible(bool visible); private: WuQGridLayoutGroup(const WuQGridLayoutGroup&); WuQGridLayoutGroup& operator=(const WuQGridLayoutGroup&); class ItemRowCol { public: ItemRowCol(QWidget *widget, int fromRow, int fromColumn, int rowSpan, int columnSpan, Qt::Alignment alignment); QWidget* widget; int fromRow; int fromColumn; int rowSpan; int columnSpan; Qt::Alignment alignment; }; QGridLayout* gridLayout; bool areWidgetsInLayout; QVector layoutItems; }; #ifdef __WU_Q_GRID_LAYOUT_GROUP_DECLARE__ // #endif // __WU_Q_GRID_LAYOUT_GROUP_DECLARE__ } // namespace #endif //__WU_Q_GRID_LAYOUT_GROUP__H_ connectome-workbench-1.2.3+git41-gc4c6c90/src/GuiQt/WuQGroupBoxExclusiveWidget.cxx000066400000000000000000000234551300200146000275700ustar00rootroot00000000000000 /*LICENSE_START*/ /* * Copyright (C) 2015 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_GROUP_BOX_EXCLUSIVE_WIDGET_DECLARE__ #include "WuQGroupBoxExclusiveWidget.h" #undef __WU_Q_GROUP_BOX_EXCLUSIVE_WIDGET_DECLARE__ #include #include #include #include #include #include #include "CaretAssert.h" #include "CaretLogger.h" #include "WuQtUtilities.h" using namespace caret; /** * \class caret::WuQGroupBoxExclusiveWidget * \brief A vertical layout with only one active item. * \ingroup GuiQt * * The items are layout out vertically inside an outline box * with each item containing a name an radio button for * mutual selection. Sort of a list of group boxes where * only one item is enabled at a time. */ /** * Constructor. * * @param parent * The parent widget. */ WuQGroupBoxExclusiveWidget::WuQGroupBoxExclusiveWidget(QWidget* parent) : QWidget(parent) { m_currentWidgetIndex = -1; m_widgetsGridLayout = new QGridLayout(this); m_widgetsGridLayout->setVerticalSpacing(10); m_widgetsGridLayout->setColumnMinimumWidth(0, 10); m_widgetsGridLayout->setColumnStretch(0, 0); m_widgetsGridLayout->setColumnStretch(1, 100); m_radioButtonGroup = new QButtonGroup(this); m_radioButtonGroup->setExclusive(true); QObject::connect(m_radioButtonGroup, SIGNAL(buttonClicked(QAbstractButton*)), this, SLOT(radioButtonClicked(QAbstractButton*))); } /** * Destructor. */ WuQGroupBoxExclusiveWidget::~WuQGroupBoxExclusiveWidget() { } /** * Called when a group's radio button is clicked. Will emit the * currentChanged() signal. * * @param button * Button that was clicked. */ void WuQGroupBoxExclusiveWidget::radioButtonClicked(QAbstractButton* button) { int32_t itemIndex = -1; QRadioButton* selectedRadioButton = qobject_cast(button); const int32_t num = count(); for (int32_t i = 0; i < num; i++) { CaretAssertVectorIndex(m_widgetDatas, i); if (m_widgetDatas[i].m_radioButton == selectedRadioButton) { itemIndex = i; m_widgetDatas[i].m_widget->setEnabled(true); } else { m_widgetDatas[i].m_widget->setEnabled(false); } } if (itemIndex >= 0) { m_currentWidgetIndex = itemIndex; updateSelectionWithValidWidget(); emit currentChanged(itemIndex); } else { m_currentWidgetIndex = -1; } } /** * Appends the given widget and returns its index. * * @param widget * Widget that is added. * @param textLabel * Text label displayed next to radio button. * @return * Index of the widget. */ int32_t WuQGroupBoxExclusiveWidget::addWidget(QWidget* widget, const QString& textLabel) { QRadioButton* radioButton = new QRadioButton(textLabel); m_radioButtonGroup->addButton(radioButton); QFrame* frameBox = new QFrame(); frameBox->setFrameStyle(QFrame::StyledPanel | QFrame::Plain); frameBox->setLineWidth(1); frameBox->setMidLineWidth(1); QHBoxLayout* frameBoxLayout = new QHBoxLayout(frameBox); frameBoxLayout->setContentsMargins(2, 2, 2, 2); frameBoxLayout->addWidget(widget, 100, Qt::AlignLeft); int row = m_widgetsGridLayout->rowCount(); m_widgetsGridLayout->addWidget(radioButton, row, 0, 1, 2, Qt::AlignLeft); row++; m_widgetsGridLayout->addWidget(frameBox, row, 1); WidgetData widgetData; widgetData.m_frameBox = frameBox; widgetData.m_radioButton = radioButton; widgetData.m_widget = widget; m_widgetDatas.push_back(widgetData); updateSelectionWithValidWidget(); return (m_widgetDatas.size() - 1); } /** * @return Number of widgets in container. */ int32_t WuQGroupBoxExclusiveWidget::count() const { return m_widgetDatas.size(); } /** * @return Index of widget that is selected. -1 if no widgets have been added. */ int32_t WuQGroupBoxExclusiveWidget::currentIndex() const { if (m_widgetDatas.empty()) { return -1; } CaretAssertVectorIndex(m_widgetDatas, m_currentWidgetIndex); return m_currentWidgetIndex; } void WuQGroupBoxExclusiveWidget::updateSelectionWithValidWidget() { if (m_widgetDatas.empty()) { m_currentWidgetIndex = -1; return; } if (m_currentWidgetIndex < 0) { m_currentWidgetIndex = 0; } else if (m_currentWidgetIndex >= count()) { m_currentWidgetIndex = count() - 1; } CaretAssertVectorIndex(m_widgetDatas, m_currentWidgetIndex); if ( ! isWidgetEnabled(m_currentWidgetIndex)) { m_currentWidgetIndex = -1; const int32_t num = count(); for (int32_t i = 0; i < num; i++) { if (isWidgetEnabled(i)) { m_currentWidgetIndex = i; break; } } } if (m_currentWidgetIndex >= 0) { CaretAssertVectorIndex(m_widgetDatas, m_currentWidgetIndex); m_radioButtonGroup->blockSignals(true); m_widgetDatas[m_currentWidgetIndex].m_radioButton->setChecked(true); m_radioButtonGroup->blockSignals(false); } const int32_t num = count(); for (int32_t i = 0; i < num; i++) { m_widgetDatas[i].m_widget->setEnabled(m_widgetDatas[i].m_radioButton->isChecked()); } } /** * @return The current widget or NULL is no widgets have been added. */ QWidget* WuQGroupBoxExclusiveWidget::currentWidget() const { const int32_t index = currentIndex(); if (index >= 0) { CaretAssertVectorIndex(m_widgetDatas, index); return m_widgetDatas[index].m_widget; } return NULL; } /** * @param widget * Widget for its index. * @return * Index of the given widget or -1 if the widget was never added. */ int32_t WuQGroupBoxExclusiveWidget::indexOf(QWidget* widget) const { const int32_t numWidgets = count(); for (int32_t i = 0; i < numWidgets; i++) { CaretAssertVectorIndex(m_widgetDatas, i); if (m_widgetDatas[i].m_widget == widget) { return i; } } return -1; } /** * @param index * Index of widget that is returned. * * @return * The widget at the given index or NULL if index is invalid. */ QWidget* WuQGroupBoxExclusiveWidget::widget(int32_t index) const { if ((index < 0) || (index >= count())) { const QString msg("Request with invalid widget index=" + QString::number(index)); CaretLogWarning(msg); CaretAssertMessage(0, msg); return NULL; } CaretAssertVectorIndex(m_widgetDatas, index); return m_widgetDatas[index].m_widget; } /** * Sets the current widget to the widget at the given index. * * @param index * Index of the widget. */ void WuQGroupBoxExclusiveWidget::setCurrentIndex(int32_t index) { if ((index < 0) || (index >= count())) { const QString msg("Attemp to set invalid current widget index=" + QString::number(index)); CaretLogWarning(msg); CaretAssertMessage(0, msg); return; } m_currentWidgetIndex = index; updateSelectionWithValidWidget(); } /** * Sets the current widget to the widget at the given widget. * * @param widget * Widget that is selected. */ void WuQGroupBoxExclusiveWidget::setCurrentWidget(QWidget* widget) { if (m_widgetDatas.empty()) { return; } const int32_t index = indexOf(widget); if (index >= 0) { m_currentWidgetIndex = index; } else { const QString msg("Widget was not found"); CaretLogWarning(msg); CaretAssertMessage(0, msg); } updateSelectionWithValidWidget(); } /** * Set the widget at given index enabled. * * @param index * Index of the widget. * @param enabled * New enabled status. */ void WuQGroupBoxExclusiveWidget::setWidgetEnabled(const int32_t index, const bool enabled) { if ((index < 0) || (index >= count())) { const QString msg("Attemp to set invalid widget enabled index=" + QString::number(index)); CaretLogWarning(msg); CaretAssertMessage(0, msg); return; } CaretAssertVectorIndex(m_widgetDatas, index); m_widgetDatas[index].m_radioButton->setEnabled(enabled); updateSelectionWithValidWidget(); } /** * @return Is the widget at the given index enabled? * * @param index * Index of the widget. */ bool WuQGroupBoxExclusiveWidget::isWidgetEnabled(const int32_t index) const { if ((index < 0) || (index >= count())) { const QString msg("Attemp to query invalid widget enabled index=" + QString::number(index)); CaretLogWarning(msg); CaretAssertMessage(0, msg); return false; } CaretAssertVectorIndex(m_widgetDatas, index); return m_widgetDatas[index].m_radioButton->isEnabled(); } connectome-workbench-1.2.3+git41-gc4c6c90/src/GuiQt/WuQGroupBoxExclusiveWidget.h000066400000000000000000000061711300200146000272110ustar00rootroot00000000000000#ifndef __WU_Q_GROUP_BOX_EXCLUSIVE_WIDGET_H__ #define __WU_Q_GROUP_BOX_EXCLUSIVE_WIDGET_H__ /*LICENSE_START*/ /* * Copyright (C) 2015 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 class QAbstractButton; class QButtonGroup; class QFrame; class QGridLayout; class QRadioButton; namespace caret { class WuQGroupBoxExclusiveWidget : public QWidget { Q_OBJECT public: WuQGroupBoxExclusiveWidget(QWidget* parent = 0); virtual ~WuQGroupBoxExclusiveWidget(); int32_t addWidget(QWidget* widget, const QString& textLabel); int32_t count() const; int32_t currentIndex() const; QWidget* currentWidget() const; int32_t indexOf(QWidget* widget) const; QWidget* widget(int32_t index) const; void setWidgetEnabled(int32_t index, const bool enabled); bool isWidgetEnabled(const int32_t index) const; // ADD_NEW_METHODS_HERE signals: /** * Emitted when the user changes the selected widget. * * @param * Index of the selected widget. */ void currentChanged(int32_t index); public slots: void setCurrentIndex(int32_t index); void setCurrentWidget(QWidget* widget); private slots: void radioButtonClicked(QAbstractButton* button); private: WuQGroupBoxExclusiveWidget(const WuQGroupBoxExclusiveWidget&); WuQGroupBoxExclusiveWidget& operator=(const WuQGroupBoxExclusiveWidget&); void updateSelectionWithValidWidget(); // ADD_NEW_MEMBERS_HERE struct WidgetData { QWidget* m_widget; QFrame* m_frameBox; QRadioButton* m_radioButton; }; std::vector m_widgetDatas; QButtonGroup* m_radioButtonGroup; QGridLayout* m_widgetsGridLayout; mutable int32_t m_currentWidgetIndex; }; #ifdef __WU_Q_GROUP_BOX_EXCLUSIVE_WIDGET_DECLARE__ // #endif // __WU_Q_GROUP_BOX_EXCLUSIVE_WIDGET_DECLARE__ } // namespace #endif //__WU_Q_GROUP_BOX_EXCLUSIVE_WIDGET_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/GuiQt/WuQImageLabel.cxx000066400000000000000000000125771300200146000247540ustar00rootroot00000000000000 /*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 __WU_Q_IMAGE_LABEL_DECLARE__ #include "WuQImageLabel.h" #undef __WU_Q_IMAGE_LABEL_DECLARE__ using namespace caret; /** * \class caret::WuQImageLabel * \brief Displays an icon in a pixmap and issues clicked signal. * \ingroup GuiQt * * Displays an icon in a QLabel. If the icon is clicked by the user * the clicked signal is emitted. This ImageLabel is intended use is * within a QTableWidget cell as a QButton'ish class would have a * background. * */ #include #include #include #include /** * Default constructor creates a label with no image nor text. */ WuQImageLabel::WuQImageLabel() : QLabel() { setAlignment(Qt::AlignCenter); } /** * Constructor constructs an image label with either the given image if the * image is valid (not NULL). If the image is invalid (NULL), the text * will be displayed. * * @param image * Image that is displayed. * @param text * Text that is displayed if image is not valid (NULL). */ WuQImageLabel::WuQImageLabel(const QImage* image, const QString& text) : QLabel() { updateImageText(image, text); setAlignment(Qt::AlignCenter); } /** * Constructor constructs an image label with either the given icon if the * icon is valid (not NULL). If the icon is invalid (NULL), the text * will be displayed. * * @param icon * Icon that is displayed. * @param text * Text that is displayed if icon is not valid (NULL). */ WuQImageLabel::WuQImageLabel(const QIcon* icon, const QString& text) : QLabel() { updateIconText(icon, text); setAlignment(Qt::AlignCenter); } /** * Constructor constructs an image label with either the given icon if the * icon is valid (not NULL). If the icon is invalid (NULL), the text * will be displayed. * * @param icon * Icon that is displayed. * @param text * Text that is displayed if icon is not valid (NULL). */ WuQImageLabel::WuQImageLabel(const QIcon& icon, const QString& text) : QLabel() { updateIconText(&icon, text); setAlignment(Qt::AlignCenter); } /** * Destructor. */ WuQImageLabel::~WuQImageLabel() { } /* * Update image label with either the given icon if the * icon is valid (not NULL). If the icon is invalid (NULL), the text * will be displayed. * * @param icon * Icon that is displayed. * @param text * Text that is displayed if icon is not valid (NULL). */ void WuQImageLabel::updateIconText(const QIcon* icon, const QString& text) { if (icon != NULL) { setPixmap(icon->pixmap(16)); } else { setText(text); } } /* * Update image label with either the given image if the * image is valid (not NULL). If the image is invalid (NULL), the text * will be displayed. * * @param image * Image that is displayed. * @param text * Text that is displayed if icon is not valid (NULL). */ void WuQImageLabel::updateImageText(const QImage* image, const QString& text) { if (image != NULL) { setPixmap(QPixmap::fromImage(*image)); } else { setPixmap(QPixmap()); } setText(text); } /** * Called when the mouse is moved. * * @param ev * The mouse event. */ void WuQImageLabel::mouseMoveEvent(QMouseEvent* ev) { if (ev->button() == Qt::NoButton) { if (ev->buttons() == Qt::LeftButton) { const int x = ev->x(); const int y = ev->y(); if (x < m_mouseMinX) m_mouseMinX = x; if (x > m_mouseMaxX) m_mouseMaxX = x; if (y < m_mouseMinY) m_mouseMinY = y; if (y > m_mouseMaxY) m_mouseMaxY = y; } } } /** * Called when the mouse button is pressed. * * @param ev * The mouse event. */ void WuQImageLabel::mousePressEvent(QMouseEvent* ev) { if (ev->button() == Qt::LeftButton) { m_mouseMinX = ev->x(); m_mouseMaxX = ev->x(); m_mouseMinY = ev->y(); m_mouseMaxY = ev->y(); } } /** * Called when the mouse button is released. * * @param ev * The mouse event. */ void WuQImageLabel::mouseReleaseEvent(QMouseEvent* ev) { if (ev->button() == Qt::LeftButton) { const int dx = std::abs(m_mouseMaxX - m_mouseMinX); const int dy = std::abs(m_mouseMaxY - m_mouseMinY); const int tolerance = 5; if ((dx < tolerance) && (dy < tolerance)) { emit clicked(); } } } connectome-workbench-1.2.3+git41-gc4c6c90/src/GuiQt/WuQImageLabel.h000066400000000000000000000046601300200146000243730ustar00rootroot00000000000000#ifndef __WU_Q_IMAGE_LABEL_H__ #define __WU_Q_IMAGE_LABEL_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 class QIcon; class QImage; namespace caret { class WuQImageLabel : public QLabel { Q_OBJECT public: WuQImageLabel(); WuQImageLabel(const QImage* image, const QString& text); WuQImageLabel(const QIcon* icon, const QString& text); WuQImageLabel(const QIcon& icon, const QString& text); virtual ~WuQImageLabel(); void updateImageText(const QImage* icon, const QString& text); void updateIconText(const QIcon* icon, const QString& text); virtual void mouseMoveEvent(QMouseEvent* ev); virtual void mousePressEvent(QMouseEvent* ev); virtual void mouseReleaseEvent(QMouseEvent* ev); // ADD_NEW_METHODS_HERE signals: /** * Emitted if the mouse button is clicked over * this widget. */ void clicked(); private: WuQImageLabel(const WuQImageLabel&); WuQImageLabel& operator=(const WuQImageLabel&); int m_mouseMinX; int m_mouseMaxX; int m_mouseMinY; int m_mouseMaxY; // ADD_NEW_MEMBERS_HERE }; #ifdef __WU_Q_IMAGE_LABEL_DECLARE__ // #endif // __WU_Q_IMAGE_LABEL_DECLARE__ } // namespace #endif //__WU_Q_IMAGE_LABEL_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/GuiQt/WuQListWidget.cxx000066400000000000000000000040771300200146000250450ustar00rootroot00000000000000 /*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 __WU_Q_LIST_WIDGET_DECLARE__ #include "WuQListWidget.h" #undef __WU_Q_LIST_WIDGET_DECLARE__ using namespace caret; /** * \class caret::WuQListWidget * \brief List widget that allows drag and drop to itself. * * QListWidget supports drag and drop but also requires * that one subclass QListWidget to receive the drop event. * This derivation of QListWidget receives the drop event * and emits a signal to indicate an item has been dropped. * * This class can be used instead of QListWidget so that * one can be notified, via a signal, that an item has been * dropped without having to subclass QListWidget. */ /** * Constructor. * @param parent * The optional parent widget. */ WuQListWidget::WuQListWidget(QWidget* parent) : QListWidget(parent) { /* * Enable dragging and dropping within this list widget. */ setSelectionMode(QListWidget::SingleSelection); setDragEnabled(true); setDragDropMode(QListWidget::InternalMove); } /** * Destructor. */ WuQListWidget::~WuQListWidget() { } /** * Receives drop events then emits the itemWasDropped signal. * @param e * The drop event. */ void WuQListWidget::dropEvent(QDropEvent* e) { QListWidget::dropEvent(e); emit itemWasDropped(); } connectome-workbench-1.2.3+git41-gc4c6c90/src/GuiQt/WuQListWidget.h000066400000000000000000000032561300200146000244700ustar00rootroot00000000000000#ifndef __WU_Q_LIST_WIDGET__H_ #define __WU_Q_LIST_WIDGET__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 namespace caret { class WuQListWidget : public QListWidget { Q_OBJECT public: WuQListWidget(QWidget* parent = 0); virtual ~WuQListWidget(); signals: /** Is emitted when an item has been dropped. */ void itemWasDropped(); protected: virtual void dropEvent(QDropEvent*); private: WuQListWidget(const WuQListWidget&); WuQListWidget& operator=(const WuQListWidget&); public: // ADD_NEW_METHODS_HERE private: // ADD_NEW_MEMBERS_HERE }; #ifdef __WU_Q_LIST_WIDGET_DECLARE__ // #endif // __WU_Q_LIST_WIDGET_DECLARE__ } // namespace #endif //__WU_Q_LIST_WIDGET__H_ connectome-workbench-1.2.3+git41-gc4c6c90/src/GuiQt/WuQMessageBox.cxx000066400000000000000000000260571300200146000250250ustar00rootroot00000000000000 /*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 "CaretAssert.h" #include "WuQMessageBox.h" using namespace caret; /** * Constructor. * * @param parent * Parent on which message box is displayed. */ WuQMessageBox::WuQMessageBox(QWidget* parent) : QMessageBox(parent) { } /** * Destructor. */ WuQMessageBox::~WuQMessageBox() { } /** * Display a message box with the buttons Save, Discard, * and Cancel. Pressing the enter key is the equivalent of * pressing the Save button. * * @param parent * Parent on which message box is displayed. * @param text * Message that is displayed. * @param informativeText * Displayed below 'text' if this is not empty. * @return * QMessageBox::Save, QMessageBox::Discard, or QMessageBox::Cancel. */ QMessageBox::StandardButton WuQMessageBox::saveDiscardCancel(QWidget* parent, 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::Save); msgBox.addButton(QMessageBox::Discard); msgBox.addButton(QMessageBox::Cancel); msgBox.setDefaultButton(QMessageBox::Save); msgBox.setEscapeButton(QMessageBox::Cancel); QMessageBox::StandardButton buttonPressed = static_cast(msgBox.exec()); switch (buttonPressed) { case QMessageBox::Save: case QMessageBox::Discard: case QMessageBox::Cancel: break; default: CaretAssert(0); } return buttonPressed; } /** * Display a warning message box with Close and Cancel * buttons. Pressing the enter key is the equivalent * of pressing the Close button. * * @param parent * Parent on which message box is displayed. * @param text * Message that is displayed. * @param informativeText * Displayed below 'text' if this is not empty. * @return * true if the Close button was pressed else false * if the cancel button was pressed. */ bool WuQMessageBox::warningCloseCancel(QWidget* parent, 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::Close); msgBox.addButton(QMessageBox::Cancel); msgBox.setDefaultButton(QMessageBox::Close); msgBox.setEscapeButton(QMessageBox::Cancel); QMessageBox::StandardButton buttonPressed = static_cast(msgBox.exec()); bool closePressed = false; switch (buttonPressed) { case QMessageBox::Close: closePressed = true; break; case QMessageBox::Cancel: break; default: CaretAssert(0); break; } return closePressed; } /** * Display a warning message box with Ok and Cancel * 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 informativeText * Displayed below 'text' if this is not empty. * @return * true if the Ok button was pressed else false * if the cancel button was pressed. */ bool WuQMessageBox::warningOkCancel(QWidget* parent, 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::Ok); msgBox.addButton(QMessageBox::Cancel); msgBox.setDefaultButton(QMessageBox::Ok); msgBox.setEscapeButton(QMessageBox::Cancel); QMessageBox::StandardButton buttonPressed = static_cast(msgBox.exec()); bool okPressed = false; switch (buttonPressed) { case QMessageBox::Ok: okPressed = true; break; case QMessageBox::Cancel: break; default: CaretAssert(0); break; } return okPressed; } /** * Display a warning message box with Yes and No * buttons. Pressing the enter key is the equivalent * of pressing the Yes button. * * @param parent * Parent on which message box is displayed. * @param text * Message that is displayed. * @param informativeText * Displayed below 'text' if this is not empty. * @return * true if the Yes button was pressed else false * if the No button was pressed. */ bool WuQMessageBox::warningYesNo(QWidget* parent, 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::Yes); msgBox.addButton(QMessageBox::No); msgBox.setDefaultButton(QMessageBox::Yes); msgBox.setEscapeButton(QMessageBox::No); QMessageBox::StandardButton buttonPressed = static_cast(msgBox.exec()); bool yesPressed = false; switch (buttonPressed) { case QMessageBox::Yes: yesPressed = true; break; case QMessageBox::No: break; default: CaretAssert(0); break; } return yesPressed; } /** * Display an error message box with the * given text and an OK button. * * @param parent * Parent on which message box is displayed. * @param text * Message that is displayed. */ void WuQMessageBox::warningOk(QWidget* parent, const QString& text) { QMessageBox msgBox(parent); msgBox.setIcon(QMessageBox::Warning); msgBox.setWindowTitle(""); msgBox.setText(text); msgBox.addButton(QMessageBox::Ok); msgBox.setDefaultButton(QMessageBox::Ok); msgBox.setEscapeButton(QMessageBox::Ok); msgBox.exec(); } /** * Display a warning message box with Ok and Cancel * 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. * @return * true if the Ok button was pressed else false * if the cancel button was pressed. */ bool WuQMessageBox::warningOkCancel(QWidget* parent, const QString& text) { return WuQMessageBox::warningOkCancel(parent, text, ""); } /** * Display a warning message box with Yes and No * buttons. Pressing the enter key is the equivalent * of pressing the Yes button. * * @param parent * Parent on which message box is displayed. * @param text * Message that is displayed. * @return * true if the Yes button was pressed else false * if the No button was pressed. */ bool WuQMessageBox::warningYesNo(QWidget* parent, const QString& text) { return WuQMessageBox::warningYesNo(parent, text, ""); } /** * Display a warning message box with Yes, No, and Cancel * buttons. Pressing the enter key is the equivalent * of pressing the Yes button. * * @param parent * Parent on which message box is displayed. * @param text * Message that is displayed. * @return * true if the Yes button was pressed else false * if the No button was pressed. */ WuQMessageBox::YesNoCancelResult WuQMessageBox::warningYesNoCancel(QWidget* parent, 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::Yes); msgBox.addButton(QMessageBox::No); msgBox.addButton(QMessageBox::Cancel); msgBox.setDefaultButton(QMessageBox::Yes); msgBox.setEscapeButton(QMessageBox::No); QMessageBox::StandardButton buttonPressed = static_cast(msgBox.exec()); YesNoCancelResult result = RESULT_CANCEL; switch (buttonPressed) { case QMessageBox::Yes: result = RESULT_YES; break; case QMessageBox::No: result = RESULT_NO; break; case QMessageBox::Cancel: result = RESULT_CANCEL; break; default: CaretAssert(0); break; } return result; } /** * Display an information message box with the * given text and an OK button. * * @param parent * Parent on which message box is displayed. * @param text * Message that is displayed. */ void WuQMessageBox::informationOk(QWidget* parent, const QString& text) { QMessageBox msgBox(parent); msgBox.setIcon(QMessageBox::Information); msgBox.setWindowTitle(""); msgBox.setText(text); msgBox.addButton(QMessageBox::Ok); msgBox.setDefaultButton(QMessageBox::Ok); msgBox.setEscapeButton(QMessageBox::Ok); msgBox.exec(); } /** * Display an error message box with the * given text and an OK button. * * @param parent * Parent on which message box is displayed. * @param text * Message that is displayed. */ void WuQMessageBox::errorOk(QWidget* parent, const QString& text) { QMessageBox msgBox(parent); msgBox.setIcon(QMessageBox::Critical); msgBox.setWindowTitle(""); msgBox.setText(text); msgBox.addButton(QMessageBox::Ok); msgBox.setDefaultButton(QMessageBox::Ok); msgBox.setEscapeButton(QMessageBox::Ok); msgBox.exec(); } connectome-workbench-1.2.3+git41-gc4c6c90/src/GuiQt/WuQMessageBox.h000066400000000000000000000057511300200146000244500ustar00rootroot00000000000000 #ifndef __WU_QMESSAGE_BOX_H__ #define __WU_QMESSAGE_BOX_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 namespace caret { class WuQMessageBox : public QMessageBox { Q_OBJECT public: enum YesNoCancelResult { RESULT_YES, RESULT_NO, RESULT_CANCEL }; static void errorOk(QWidget* parent, const QString& text); static void informationOk(QWidget* parent, const QString& text); static void warningOk(QWidget* parent, const QString& text); static bool warningOkCancel(QWidget* parent, const QString& text); static bool warningYesNo(QWidget* parent, const QString& text); static bool warningOkCancel(QWidget* parent, const QString& text, const QString& informativeText); static bool warningYesNo(QWidget* parent, const QString& text, const QString& informativeText); static YesNoCancelResult warningYesNoCancel(QWidget* parent, const QString& text, const QString& informativeText); static bool warningCloseCancel(QWidget* parent, const QString& text, const QString& informativeText); static QMessageBox::StandardButton saveDiscardCancel(QWidget* parent, const QString& text, const QString& informativeText); private: WuQMessageBox(QWidget* parent = 0); ~WuQMessageBox(); WuQMessageBox(const WuQMessageBox&); WuQMessageBox& operator=(const WuQMessageBox&); }; } #endif // __WU_QMESSAGE_DIALOG_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/GuiQt/WuQSpecialIncrementDoubleSpinBox.cxx000066400000000000000000000061701300200146000306450ustar00rootroot00000000000000 /*LICENSE_START*/ /* * Copyright (C) 2015 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 #define __WU_Q_SPECIAL_INCREMENT_DOUBLE_SPIN_BOX_DECLARE__ #include "WuQSpecialIncrementDoubleSpinBox.h" #undef __WU_Q_SPECIAL_INCREMENT_DOUBLE_SPIN_BOX_DECLARE__ #include "CaretAssert.h" using namespace caret; /** * \class caret::WuQSpecialIncrementDoubleSpinBox * \brief A double spin box that allows customized processing of increment/decrement using a function object. * \ingroup GuiQt */ /** * Constructor. * * @param stepFunctionObject * Function object that processes increment and decrement requests. */ WuQSpecialIncrementDoubleSpinBox::WuQSpecialIncrementDoubleSpinBox(StepFunctionObject* stepFunctionObject) : QDoubleSpinBox(), m_stepFunctionObject(stepFunctionObject) { CaretAssert(stepFunctionObject); } /** * Destructor. */ WuQSpecialIncrementDoubleSpinBox::~WuQSpecialIncrementDoubleSpinBox() { } /** * Virtual function that is called whenever the user triggers a step. * The steps parameter indicates how many steps were taken, e.g. * Pressing Qt::Key_Down will trigger a call to stepBy(-1), whereas * pressing Qt::Key_Prior will trigger a call to stepBy(10). * * If you subclass QAbstractSpinBox you must reimplement this function. * Note that this function is called even if the resulting value will * be outside the bounds of minimum and maximum. It's this function's * job to handle these situations. * * @param steps * Number of steps */ void WuQSpecialIncrementDoubleSpinBox::stepBy(int steps) { const double previousValue = value(); // QString msg("Step " // + QString::number(steps) // + " current value " // + QString::number(previousValue)); double newValue = m_stepFunctionObject->getNewValue(previousValue, steps); newValue = std::min(newValue, maximum()); newValue = std::max(newValue, minimum()); if (newValue != previousValue) { blockSignals(true); setValue(newValue); blockSignals(false); // msg += (" new value " // + QString::number(value())); // std::cout << qPrintable(msg) << std::endl; emit valueChanged(newValue); emit valueChanged(text()); } } connectome-workbench-1.2.3+git41-gc4c6c90/src/GuiQt/WuQSpecialIncrementDoubleSpinBox.h000066400000000000000000000040451300200146000302710ustar00rootroot00000000000000#ifndef __WU_Q_SPECIAL_INCREMENT_DOUBLE_SPIN_BOX_H__ #define __WU_Q_SPECIAL_INCREMENT_DOUBLE_SPIN_BOX_H__ /*LICENSE_START*/ /* * Copyright (C) 2015 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 namespace caret { class WuQSpecialIncrementDoubleSpinBox : public QDoubleSpinBox { Q_OBJECT public: class StepFunctionObject { public: virtual double getNewValue(const double currentValue, const int steps) const = 0; }; WuQSpecialIncrementDoubleSpinBox(StepFunctionObject* stepFunctionObject); virtual ~WuQSpecialIncrementDoubleSpinBox(); virtual void stepBy(int steps); // ADD_NEW_METHODS_HERE private: WuQSpecialIncrementDoubleSpinBox(const WuQSpecialIncrementDoubleSpinBox&); WuQSpecialIncrementDoubleSpinBox& operator=(const WuQSpecialIncrementDoubleSpinBox&); StepFunctionObject* m_stepFunctionObject; // ADD_NEW_MEMBERS_HERE }; #ifdef __WU_Q_SPECIAL_INCREMENT_DOUBLE_SPIN_BOX_DECLARE__ // #endif // __WU_Q_SPECIAL_INCREMENT_DOUBLE_SPIN_BOX_DECLARE__ } // namespace #endif //__WU_Q_SPECIAL_INCREMENT_DOUBLE_SPIN_BOX_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/GuiQt/WuQSpinBoxGroup.cxx000066400000000000000000000075021300200146000253610ustar00rootroot00000000000000 /*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 __WU_Q_SPIN_BOX_GROUP_DECLARE__ #include "WuQSpinBoxGroup.h" #undef __WU_Q_SPIN_BOX_GROUP_DECLARE__ #include #include using namespace caret; /** * \class caret::WuQSpinBoxGroup * \brief Container that organizes a group of spin boxes. * * Container that organizes a group of spin boxes. */ /** * Constructor. * @param parent * Parent of this container. */ WuQSpinBoxGroup::WuQSpinBoxGroup(QObject* parent) : QObject(parent) { } /** * Destructor. */ WuQSpinBoxGroup::~WuQSpinBoxGroup() { for (std::vector::iterator iter = this->spinBoxReceivers.begin(); iter != this->spinBoxReceivers.end(); iter++) { delete *iter; } this->spinBoxReceivers.clear(); } /** * Add a spin box to this spin box group. * @param abstractSpinBox * Spin box that is added. */ void WuQSpinBoxGroup::addSpinBox(QAbstractSpinBox* abstractSpinBox) { SpinBoxReceiver* sbr = new SpinBoxReceiver(this, abstractSpinBox, this->spinBoxReceivers.size()); this->spinBoxReceivers.push_back(sbr); } /** * Called when double spin box value is changed. * @param doubleSpinBox * Double spin box that had its value changed. * @param d * New value. */ void WuQSpinBoxGroup::doubleSpinBoxChangeReceiver(QDoubleSpinBox* doubleSpinBox, const double d) { emit doubleSpinBoxValueChanged(doubleSpinBox, d); } /** * Called when spin box value is changed. * @param spinBox * Spin box that had its value changed. * @param i * New value. */ void WuQSpinBoxGroup::spinBoxChangeReceiver(QSpinBox* spinBox, const int i) { emit spinBoxValueChanged(spinBox, i); } //------------------------------------------------------------ SpinBoxReceiver::SpinBoxReceiver(WuQSpinBoxGroup* spinBoxGroup, QAbstractSpinBox* abstractSpinBox, const int spinBoxIndex) { this->spinBoxGroup = spinBoxGroup; this->spinBoxIndex = spinBoxIndex; this->spinBox = qobject_cast(abstractSpinBox); if (this->spinBox != NULL) { QObject::connect(this->spinBox, SIGNAL(valueChanged(int)), this, SLOT(valueChangedSlot(int))); } this->doubleSpinBox = qobject_cast(abstractSpinBox); if (this->doubleSpinBox != NULL) { QObject::connect(this->doubleSpinBox, SIGNAL(valueChanged(double)), this, SLOT(valueChangedSlot(double))); } } SpinBoxReceiver::~SpinBoxReceiver() { } void SpinBoxReceiver::valueChangedSlot(int i) { this->spinBoxGroup->spinBoxChangeReceiver(this->spinBox, i); } void SpinBoxReceiver::valueChangedSlot(double d) { this->spinBoxGroup->doubleSpinBoxChangeReceiver(this->doubleSpinBox, d); } connectome-workbench-1.2.3+git41-gc4c6c90/src/GuiQt/WuQSpinBoxGroup.h000066400000000000000000000067031300200146000250100ustar00rootroot00000000000000#ifndef __WU_Q_SPIN_BOX_GROUP__H_ #define __WU_Q_SPIN_BOX_GROUP__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 class QAbstractSpinBox; class QDoubleSpinBox; class QSpinBox; namespace caret { class AString; class SpinBoxReceiver; class WuQSpinBoxGroup : public QObject { Q_OBJECT public: WuQSpinBoxGroup(QObject* parent); virtual ~WuQSpinBoxGroup(); void addSpinBox(QAbstractSpinBox* abstractSpinBox); signals: /** * Emitted when a QSpinBox has its value changed. * @param spinBox * Spin box that had its value changed. * @param i * New value from spin box. */ void spinBoxValueChanged(QSpinBox* spinBox, const int i); /** * Emitted when a QDoubleSpinBox has its value changed. * @param doubleSpinBox * Double spin box that had its value changed. * @param d * New value from spin box. */ void doubleSpinBoxValueChanged(QDoubleSpinBox* doubleSpinBox, const double d); //void valueChanged(const AString& text); private: WuQSpinBoxGroup(const WuQSpinBoxGroup&); WuQSpinBoxGroup& operator=(const WuQSpinBoxGroup&); std::vector spinBoxReceivers; void doubleSpinBoxChangeReceiver(QDoubleSpinBox* doubleSpinBox, const double d); void spinBoxChangeReceiver(QSpinBox* spinBox, const int i); friend class SpinBoxReceiver; }; class SpinBoxReceiver : public QObject { Q_OBJECT public: SpinBoxReceiver(WuQSpinBoxGroup* spinBoxGroup, QAbstractSpinBox* abstractSpinBox, const int indx); ~SpinBoxReceiver(); private slots: void valueChangedSlot(int i); void valueChangedSlot(double d); private: SpinBoxReceiver(const SpinBoxReceiver&); SpinBoxReceiver& operator=(const SpinBoxReceiver&); WuQSpinBoxGroup* spinBoxGroup; QSpinBox* spinBox; QDoubleSpinBox* doubleSpinBox; int spinBoxIndex; }; #ifdef __WU_Q_SPIN_BOX_GROUP_DECLARE__ // #endif // __WU_Q_SPIN_BOX_GROUP_DECLARE__ } // namespace #endif //__WU_Q_SPIN_BOX_GROUP__H_ connectome-workbench-1.2.3+git41-gc4c6c90/src/GuiQt/WuQSpinBoxOddValue.cxx000066400000000000000000000175351300200146000257770ustar00rootroot00000000000000 /*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*/ /* Qt License is included since the API is copied from QSpinBox */ /**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtGui 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 Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/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 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #define __WU_Q_SPIN_BOX_ODD_VALUE_DECLARE__ #include "WuQSpinBoxOddValue.h" #undef __WU_Q_SPIN_BOX_ODD_VALUE_DECLARE__ #include #include #include "CaretAssert.h" #include "CaretLogger.h" #include "MathFunctions.h" using namespace caret; /** * \class caret::WuQSpinBoxOddValue * \brief Spin box that allows only odd values * \ingroup GuiQt * * Creates a spin box that allows only odd values. * Any attempts to set the value to an event value * or any attempts to set other parameters that * would cause an even value are ignored. To * ensure only odd values, enter of text is disabled. */ /** * Constructor. */ WuQSpinBoxOddValue::WuQSpinBoxOddValue(QObject* parent) : WuQWidget(parent) { m_spinBox = new WuQSpinBoxOddValueSpinBox(); m_spinBox->setMinimum(-99999999); m_spinBox->setMaximum( 99999999); m_spinBox->setSingleStep(2); } /** * Destructor. */ WuQSpinBoxOddValue::~WuQSpinBoxOddValue() { } /** * Return the enapsulated widget so that it can be added * to a layout */ QWidget* WuQSpinBoxOddValue::getWidget() { return m_spinBox; } /** * @return The minimum value allowed in the spin box. */ int WuQSpinBoxOddValue::minimum() const { return m_spinBox->minimum(); } /** * @return The maximum value allowed in the spin box. */ int WuQSpinBoxOddValue::maximum() const { return m_spinBox->maximum(); } /** * Set the minimum value. If the value is even, no action is taken. * * @param min * New minimum value. */ void WuQSpinBoxOddValue::setMinimum(int min) { if (MathFunctions::isEvenNumber(min)) { const AString msg = "Value passed to WuQSpinBoxOddValue::setMinimum MUST BE AN ODD NUMBER"; CaretAssertMessage(0, msg); CaretLogSevere(msg); return; } m_spinBox->setMinimum(min); } /** * Set the maximum value. If the value is even, no action is taken. * * @param min * New maximum value. */ void WuQSpinBoxOddValue::setMaximum(int max) { if (MathFunctions::isEvenNumber(max)) { const AString msg = "Value passed to WuQSpinBoxOddValue::setMaximum MUST BE AN ODD NUMBER"; CaretAssertMessage(0, msg); CaretLogSevere(msg); return; } m_spinBox->setMaximum(max); } /** * Set the range of values. * * @param minimum * New minimum value. If the value is even, no action is taken. * @param maximum * New maximum value. If the value is even, no action is taken. */ void WuQSpinBoxOddValue::setRange(int minimum, int maximum) { setMinimum(minimum); setMaximum(maximum); } /** * @return The step value when the user presses the up or down arrow. */ int WuQSpinBoxOddValue::singleStep() const { return m_spinBox->singleStep(); } /** * Set the step value for when the up or down arrow is pressed. * If the value is odd, no action is taken since the spin box * must change value by 2 so that the value remains odd. * * @param val * New step value. */ void WuQSpinBoxOddValue::setSingleStep(int val) { if (MathFunctions::isOddNumber(val)) { const AString msg = ("Value passed to WuQSpinBoxOddValue::setMaximum MUST BE AN EVEN NUMBER " "so that incrementing and decrementing preserves an odd value in the " "spin box."); CaretAssertMessage(0, msg); CaretLogSevere(msg); return; } m_spinBox->setSingleStep(val); } /** * @return The value in the spin box. */ int WuQSpinBoxOddValue::value() const { return m_spinBox->value(); } /** * Set the value. If the value is even, no action is taken. * * @param val * New value. */ void WuQSpinBoxOddValue::setValue(int val) { if (MathFunctions::isEvenNumber(val)) { const AString msg = "Value passed to WuQSpinBoxOddValue::setValue MUST BE AN ODD NUMBER"; CaretAssertMessage(0, msg); CaretLogSevere(msg); return; } m_spinBox->setValue(val); } /* -----------------------------------------------------------------------*/ /** * \class caret::WuQSpinBoxOddValueSpinBox * \brief Spin Box used by WuQSpinBoxOddValue * \ingroup GuiQt * * Spin Box used by WuQSpinBoxOddValue. * This is intended only for use by WuQSpinBoxOddValue * and it is simply a QSpinBox with its line edit * disabled. Disabling the line edit is a protected * functions in QSpinBox and that is the only reason * this class exists. */ /** * Constructor. * * @param parent * Optional parent widget. */ WuQSpinBoxOddValueSpinBox::WuQSpinBoxOddValueSpinBox(QWidget* parent) : QSpinBox(parent) { lineEdit()->setEnabled(false); } /** * Destructor. */ WuQSpinBoxOddValueSpinBox::~WuQSpinBoxOddValueSpinBox() { } int WuQSpinBoxOddValueSpinBox::valueFromText(const QString& text) const { bool validFlag = false; int value = text.toInt(&validFlag); if (MathFunctions::isEvenNumber(value)) { if (value > 0) { value = value - 1; } else if (value < 0) { value = value + 1; } } return value; } connectome-workbench-1.2.3+git41-gc4c6c90/src/GuiQt/WuQSpinBoxOddValue.h000066400000000000000000000106031300200146000254110ustar00rootroot00000000000000#ifndef __WU_Q_SPIN_BOX_ODD_VALUE_H__ #define __WU_Q_SPIN_BOX_ODD_VALUE_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*/ /* Qt License is included since the API is copied from QSpinBox */ /**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtGui 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 Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/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 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #include "WuQWidget.h" #include namespace caret { class WuQSpinBoxOddValueSpinBox : public QSpinBox { public: WuQSpinBoxOddValueSpinBox(QWidget* parent = 0); virtual ~WuQSpinBoxOddValueSpinBox(); protected: virtual int valueFromText(const QString& text) const; }; class WuQSpinBoxOddValue : public WuQWidget { Q_OBJECT public: WuQSpinBoxOddValue(QObject* parent); virtual ~WuQSpinBoxOddValue(); virtual QWidget* getWidget(); int minimum() const; int maximum() const; void setMinimum(int min); void setMaximum(int max); void setRange(int minimum, int maximum); int singleStep() const; void setSingleStep(int val); int value() const; // ADD_NEW_METHODS_HERE signals: void valueChanged(int i); public slots: void setValue(int val); private: WuQSpinBoxOddValue(const WuQSpinBoxOddValue&); WuQSpinBoxOddValue& operator=(const WuQSpinBoxOddValue&); QSpinBox* m_spinBox; // ADD_NEW_MEMBERS_HERE }; #ifdef __WU_Q_SPIN_BOX_ODD_VALUE_DECLARE__ // #endif // __WU_Q_SPIN_BOX_ODD_VALUE_DECLARE__ } // namespace #endif //__WU_Q_SPIN_BOX_ODD_VALUE_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/GuiQt/WuQTabWidget.cxx000066400000000000000000000162631300200146000246400ustar00rootroot00000000000000 /*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 #define __WU_Q_TAB_WIDGET_DECLARE__ #include "WuQTabWidget.h" #undef __WU_Q_TAB_WIDGET_DECLARE__ #include "SceneClass.h" using namespace caret; /** * \class caret::WuQTabWidget * \brief Replaces QTabWidget and allows the tab to be aligned. * * On some platforms, the tabs are centered, and when the tab widget * is wide an in a scrollable area it is sometimes difficult to find the * tabs. So, this tab widget allows the tab bar placed at a defined * alignment. */ /** * Constructor. * @param alignment * Aligment of the tab. * @param parent * Parent of this instance. */ WuQTabWidget::WuQTabWidget(const TabAlignment alignment, QObject* parent) : WuQWidget(parent) { m_tabBar = new QTabBar(); QObject::connect(m_tabBar, SIGNAL(currentChanged(int)), this, SLOT(tabBarCurrentIndexChanged(int))); m_stackedWidget = new QStackedWidget(); QHBoxLayout* tabBarLayout = new QHBoxLayout(); Qt::Alignment tabBarAlignment = Qt::AlignLeft; switch (alignment) { case TAB_ALIGN_CENTER: tabBarAlignment = Qt::AlignHCenter; break; case TAB_ALIGN_LEFT: tabBarAlignment = Qt::AlignLeft; break; case TAB_ALIGN_RIGHT: tabBarAlignment = Qt::AlignRight; break; } tabBarLayout->addWidget(m_tabBar, 100, tabBarAlignment); tabBarLayout->setMargin(0); QGroupBox* stackedWidgetGroupBox = new QGroupBox(); QVBoxLayout* groupBoxLayout = new QVBoxLayout(stackedWidgetGroupBox); groupBoxLayout->addWidget(m_stackedWidget); m_widget = new QWidget(); QVBoxLayout* verticalLayout = new QVBoxLayout(m_widget); QMargins margins = verticalLayout->contentsMargins(); margins.setLeft(0); margins.setRight(0); verticalLayout->setContentsMargins(margins); verticalLayout->setSpacing(3); verticalLayout->addLayout(tabBarLayout, 0); verticalLayout->addWidget(stackedWidgetGroupBox, 0, tabBarAlignment); verticalLayout->addStretch(); } /** * Destructor. */ WuQTabWidget::~WuQTabWidget() { } /** * @return The embedded widget. */ QWidget* WuQTabWidget::getWidget() { return m_widget; } /** * Adds a tab with the given page and label to the tab widget, and returns * the index of the tab in the tab bar. If the tab's label contains an * ampersand, the letter following the ampersand is used as a shortcut * for the tab, e.g. if the label is "Bro&wse" then Alt+W becomes a * shortcut which will move the focus to this tab. * * @param page * New page that is added (must not be NULL). * @param label * Label displayed in the page's tab. */ void WuQTabWidget::addTab(QWidget* page, const QString& label) { m_tabBar->addTab(label); m_stackedWidget->addWidget(page); } /** * Called when the tab bar changes the current widget. * @param index * Index of selected widget. */ void WuQTabWidget::tabBarCurrentIndexChanged(int index) { setCurrentIndex(index); emit currentChanged(index); } /** * @return Returns the index position of the current tab page. * The current index is -1 if there is no current widget. */ int WuQTabWidget::currentIndex() const { return m_tabBar->currentIndex(); } /** * @return Returns a pointer to the page currently being displayed by the * tab dialog. The tab dialog does its best to make sure that this value * is never 0 (but if you try hard enough, it can be). */ QWidget* WuQTabWidget::currentWidget() const { return m_stackedWidget->currentWidget(); } //signals: //void currentChanged(int index); /** * Makes widget at the given index the current widget. The widget used must * be a page in this tab widget. */ void WuQTabWidget::setCurrentIndex(int index) { m_tabBar->blockSignals(true); m_tabBar->setCurrentIndex(index); m_tabBar->blockSignals(false); m_stackedWidget->setCurrentIndex(index); } /** * Makes widget the current widget. The widget used must be a page in * this tab widget. */ void WuQTabWidget::setCurrentWidget(QWidget* widget) { const int indx = m_stackedWidget->indexOf(widget); if (indx > 0) { m_tabBar->blockSignals(true); m_tabBar->setCurrentIndex(indx); m_tabBar->blockSignals(false); } } /** * Create a scene for an instance of a class. * * @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. * * @return Pointer to SceneClass object representing the state of * this object. Under some circumstances a NULL pointer may be * returned. Caller will take ownership of returned object. */ SceneClass* WuQTabWidget::saveToScene(const SceneAttributes* /*sceneAttributes*/, const AString& instanceName) { SceneClass* sceneClass = new SceneClass(instanceName, "WuQTabWidget", 1); AString tabName; const int32_t selectedTabIndex = currentIndex(); if (selectedTabIndex >= 0) { tabName = m_tabBar->tabText(selectedTabIndex); } sceneClass->addString("selectedTabName", tabName); return sceneClass; } /** * Restore the state of an instance of a class. * * @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 containing the state that was previously * saved and should be restored. */ void WuQTabWidget::restoreFromScene(const SceneAttributes* /*sceneAttributes*/, const SceneClass* sceneClass) { if (sceneClass == NULL) { return; } const AString tabName = sceneClass->getStringValue("selectedTabName"); const int32_t numTabs = m_tabBar->count(); for (int32_t i = 0; i < numTabs; i++) { if (m_tabBar->tabText(i) == tabName) { setCurrentIndex(i); break; } } } connectome-workbench-1.2.3+git41-gc4c6c90/src/GuiQt/WuQTabWidget.h000066400000000000000000000051401300200146000242550ustar00rootroot00000000000000#ifndef __WU_Q_TAB_WIDGET__H_ #define __WU_Q_TAB_WIDGET__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 "WuQWidget.h" #include "SceneableInterface.h" class QTabBar; class QStackedWidget; namespace caret { class WuQTabWidget : public WuQWidget, public SceneableInterface { Q_OBJECT public: enum TabAlignment { TAB_ALIGN_LEFT, TAB_ALIGN_CENTER, TAB_ALIGN_RIGHT }; WuQTabWidget(const TabAlignment alignment, QObject* parent); virtual ~WuQTabWidget(); QWidget* getWidget(); void addTab(QWidget* page, const QString& label); int currentIndex() const; QWidget* currentWidget() const; virtual SceneClass* saveToScene(const SceneAttributes* sceneAttributes, const AString& instanceName); virtual void restoreFromScene(const SceneAttributes* sceneAttributes, const SceneClass* sceneClass); signals: void currentChanged(int index); public slots: void setCurrentIndex(int index); void setCurrentWidget(QWidget* widget); private slots: void tabBarCurrentIndexChanged(int index); private: WuQTabWidget(const WuQTabWidget&); WuQTabWidget& operator=(const WuQTabWidget&); QTabBar* m_tabBar; QStackedWidget* m_stackedWidget; QWidget* m_widget; // ADD_NEW_MEMBERS_HERE }; #ifdef __WU_Q_TAB_WIDGET_DECLARE__ // #endif // __WU_Q_TAB_WIDGET_DECLARE__ } // namespace #endif //__WU_Q_TAB_WIDGET__H_ connectome-workbench-1.2.3+git41-gc4c6c90/src/GuiQt/WuQTimedMessageDisplay.cxx000066400000000000000000000117451300200146000266630ustar00rootroot00000000000000 /*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 #define __WU_Q_TIMED_MESSAGE_DISPLAY_DECLARE__ #include "WuQTimedMessageDisplay.h" #undef __WU_Q_TIMED_MESSAGE_DISPLAY_DECLARE__ #include "AString.h" #include "CaretAssert.h" using namespace caret; /** * \class caret::WuQTimedMessageDisplay * \brief Timed message display. * * Displays a buttonless dialog for a period of time. * * Use the static show() method to display a timed message display. */ /** * Constructor. * * Display a message containing the given message for the given amount * of time. * * @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. */ WuQTimedMessageDisplay::WuQTimedMessageDisplay(QWidget* parent, const float displayForSeconds, const QString& message) : QDialog(parent, Qt::FramelessWindowHint) { CaretAssertMessage(displayForSeconds > 0.0, "Display time must be greater than zero."); /* * Modal so it blocks until done. */ setModal(true); /* * Delete self when done. */ this->setAttribute(Qt::WA_DeleteOnClose, true); /* * Put message in window. */ QLabel* label = new QLabel(message); label->setFrameStyle(QFrame::Panel | QFrame::Plain); label->setLineWidth(2); label->setWordWrap(true); QVBoxLayout* layout = new QVBoxLayout(this); layout->setContentsMargins(3, 3, 3, 3); layout->addWidget(label); /* * Setup a timer to call the accept(() slot when done. * accept() will close the dialog. */ QTimer* timer = new QTimer(this); timer->setSingleShot(true); QObject::connect(timer, SIGNAL(timeout()), this, SLOT(accept())); timer->start(displayForSeconds * 1000.0); /* * Display directly over the parent */ QPoint pos = parent->mapToGlobal(parent->pos()); move(pos); } /** * Display a message containing the given message for the given amount * of time. This method will not return until the message window closes. * * @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::show(QWidget* parent, const float displayForSeconds, const QString& message) { WuQTimedMessageDisplay* md = new WuQTimedMessageDisplay(parent, displayForSeconds, message); md->exec(); } /** * Display a message containing the given message for the given amount * of time. This method will not return until the message window closes. * * @param parent * Parent on which message is displayed. * @param x * X-coordinate of parent for display of message. * @param y * Y-coordinate of parent for display of message (origin at bottom). * @param displayForSeconds * Message is displayed for this amount of time, in milliseconds. * @param message * Message that is displayed. */ void WuQTimedMessageDisplay::show(QWidget* parent, const int32_t x, const int32_t y, const float displayForSeconds, const QString& message) { WuQTimedMessageDisplay* md = new WuQTimedMessageDisplay(parent, displayForSeconds, message); const int32_t originAtTopWindowY = parent->height() - y; QPoint globalXY = parent->mapToGlobal(QPoint(x, originAtTopWindowY)); md->move(globalXY); md->exec(); } /** * Destructor. */ WuQTimedMessageDisplay::~WuQTimedMessageDisplay() { } connectome-workbench-1.2.3+git41-gc4c6c90/src/GuiQt/WuQTimedMessageDisplay.h000066400000000000000000000042041300200146000263000ustar00rootroot00000000000000#ifndef __WU_Q_TIMED_MESSAGE_DISPLAY_H__ #define __WU_Q_TIMED_MESSAGE_DISPLAY_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 namespace caret { class WuQTimedMessageDisplay : public QDialog { Q_OBJECT private: WuQTimedMessageDisplay(QWidget* parent, const float displayForSeconds, const QString& message); public: static void show(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); virtual ~WuQTimedMessageDisplay(); private: WuQTimedMessageDisplay(const WuQTimedMessageDisplay&); WuQTimedMessageDisplay& operator=(const WuQTimedMessageDisplay&); public: // ADD_NEW_METHODS_HERE private: // ADD_NEW_MEMBERS_HERE }; #ifdef __WU_Q_TIMED_MESSAGE_DISPLAY_DECLARE__ // #endif // __WU_Q_TIMED_MESSAGE_DISPLAY_DECLARE__ } // namespace #endif //__WU_Q_TIMED_MESSAGE_DISPLAY_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/GuiQt/WuQTreeWidget.cxx000066400000000000000000000076631300200146000250350ustar00rootroot00000000000000 /*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 #define __WU_Q_TREE_WIDGET_DECLARE__ #include "WuQTreeWidget.h" #undef __WU_Q_TREE_WIDGET_DECLARE__ using namespace caret; /** * \class caret::WuQTreeWidget * \brief Tree Widget that can size to its content's size. * * A QTreeWidget normally gets a size hint of (256, 256) and * does not increase in size when the scroll bars are turned * off. If fitToContentSizeWithoutScrollBars() is called * this tree widget will resize to the size of its content. */ /** * Constructor. */ WuQTreeWidget::WuQTreeWidget(QWidget* parent) : QTreeWidget(parent) { this->setHeaderHidden(true); this->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); this->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff); this->setVerticalScrollMode(QAbstractItemView::ScrollPerPixel); QObject::connect(this, SIGNAL(itemCollapsed(QTreeWidgetItem*)), this, SLOT(itemExpandedOrCollapsed(QTreeWidgetItem*))); QObject::connect(this, SIGNAL(itemExpanded(QTreeWidgetItem*)), this, SLOT(itemExpandedOrCollapsed(QTreeWidgetItem*))); } /** * Destructor. */ WuQTreeWidget::~WuQTreeWidget() { } /** * Called when an item is expanded or collapsed to * update the fixed size. */ void WuQTreeWidget::itemExpandedOrCollapsed(QTreeWidgetItem*) { this->resizeToFitContent(); } /** * Size the widget so that it is the size of * its content's without any scroll bars. */ void WuQTreeWidget::resizeToFitContent() { const int height = this->calculateHeight() + 6; this->setFixedHeight(height); int totalColumnWidths = 20; // space for arrows const int numCols = this->columnCount(); for (int i = 0; i < numCols; i++) { totalColumnWidths += this->sizeHintForColumn(i); } if (totalColumnWidths < 256) { totalColumnWidths = 256; } this->setFixedWidth(totalColumnWidths); } /* * From http://qt-project.org/forums/viewthread/2533 */ int WuQTreeWidget::calculateHeight() const { int h = 0; int topLevelCount = this->topLevelItemCount(); for (int i = 0;i < topLevelCount;i++) { QTreeWidgetItem * item = topLevelItem(i); h += this->calculateHeightRec(item); } if (h != 0) { h += header()->sizeHint().height(); } return h; } /* * */ int WuQTreeWidget::calculateHeightRec(QTreeWidgetItem * item) const { if (item == NULL) return 0; QModelIndex index = indexFromItem(item); if (item->isExpanded() == false) { int h = rowHeight(index); return h; } /* int h = 0; for (int j = 0; j < item->columnCount(); j++) { const int itemHeight = item->sizeHint(j).height() + 2; if (itemHeight > h) { h = itemHeight; } } */ //int h = item->sizeHint(0).height() + 2; //std::cout << "EXPANDED h=" << h << " rowSizeHint=" << indexRowSizeHint(index); int h = indexRowSizeHint(index); int childCount = item->childCount(); for (int i = 0; i < childCount;i++) { h += this->calculateHeightRec(item->child(i)); } return h; } connectome-workbench-1.2.3+git41-gc4c6c90/src/GuiQt/WuQTreeWidget.h000066400000000000000000000032051300200146000244460ustar00rootroot00000000000000#ifndef __WU_Q_TREE_WIDGET__H_ #define __WU_Q_TREE_WIDGET__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 namespace caret { class WuQTreeWidget : public QTreeWidget { Q_OBJECT public: WuQTreeWidget(QWidget* parent = 0); virtual ~WuQTreeWidget(); void resizeToFitContent(); private slots: void itemExpandedOrCollapsed(QTreeWidgetItem*); private: WuQTreeWidget(const WuQTreeWidget&); WuQTreeWidget& operator=(const WuQTreeWidget&); int calculateHeight() const; int calculateHeightRec(QTreeWidgetItem* treeItem) const; }; #ifdef __WU_Q_TREE_WIDGET_DECLARE__ // #endif // __WU_Q_TREE_WIDGET_DECLARE__ } // namespace #endif //__WU_Q_TREE_WIDGET__H_ connectome-workbench-1.2.3+git41-gc4c6c90/src/GuiQt/WuQTrueFalseComboBox.cxx000066400000000000000000000060661300200146000263110ustar00rootroot00000000000000 /*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 __WU_Q_TRUE_FALSE_COMBO_BOX_DECLARE__ #include "WuQTrueFalseComboBox.h" #undef __WU_Q_TRUE_FALSE_COMBO_BOX_DECLARE__ using namespace caret; /** * \class caret::WuQTrueFalseComboBox * \brief Combo box for true/false type values. */ /** * Constructor. */ WuQTrueFalseComboBox::WuQTrueFalseComboBox(const QString& trueText, const QString& falseText, QObject* parent) : WuQWidget(parent) { this->createComboBox(trueText, falseText); } /** * Constructor. */ WuQTrueFalseComboBox::WuQTrueFalseComboBox(QObject* parent) : WuQWidget(parent) { this->createComboBox("true", "false"); } /** * Destructor. */ WuQTrueFalseComboBox::~WuQTrueFalseComboBox() { } /** * Create the combo box. * @param trueText * Text for 'true' value. * @param falseText * Text for 'false' value. */ void WuQTrueFalseComboBox::createComboBox(const QString& trueText, const QString& falseText) { this->comboBox = new QComboBox(); this->comboBox->addItem(trueText); this->comboBox->addItem(falseText); QObject::connect(this->comboBox, SIGNAL(activated(int)), this, SLOT(comboBoxValueChanged(int))); } /** * Called when value is changed. */ void WuQTrueFalseComboBox::comboBoxValueChanged(int indx) { bool boolValue = (indx == 0); emit statusChanged(boolValue); } /** * @return The embedded widget. */ QWidget* WuQTrueFalseComboBox::getWidget() { return this->comboBox; } /** * @return If true is selected. */ bool WuQTrueFalseComboBox::isTrue() { const int indx = this->comboBox->currentIndex(); const bool boolValue = (indx == 0); return boolValue; } /** * @return If false is selected. */ bool WuQTrueFalseComboBox::isFalse() { const int indx = this->comboBox->currentIndex(); const bool boolValue = (indx == 1); return boolValue; } /** * Set the new true/false status. * @parma status * New status. */ void WuQTrueFalseComboBox::setStatus(const bool status) { if (status) { this->comboBox->setCurrentIndex(0); } else { this->comboBox->setCurrentIndex(1); } } connectome-workbench-1.2.3+git41-gc4c6c90/src/GuiQt/WuQTrueFalseComboBox.h000066400000000000000000000041761300200146000257360ustar00rootroot00000000000000#ifndef __WU_Q_TRUE_FALSE_COMBO_BOX__H_ #define __WU_Q_TRUE_FALSE_COMBO_BOX__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 "WuQWidget.h" namespace caret { class WuQTrueFalseComboBox : public WuQWidget { Q_OBJECT public: WuQTrueFalseComboBox(const QString& trueText, const QString& falseText, QObject* parent); WuQTrueFalseComboBox(QObject* parent); virtual ~WuQTrueFalseComboBox(); QWidget* getWidget(); bool isTrue(); bool isFalse(); void setStatus(const bool status); signals: /** Emitted when user makes a selection */ void statusChanged(bool); private slots: void comboBoxValueChanged(int indx); private: WuQTrueFalseComboBox(const WuQTrueFalseComboBox&); WuQTrueFalseComboBox& operator=(const WuQTrueFalseComboBox&); void createComboBox(const QString& trueText, const QString& falseText); QComboBox* comboBox; }; #ifdef __WU_Q_TRUE_FALSE_COMBO_BOX_DECLARE__ // #endif // __WU_Q_TRUE_FALSE_COMBO_BOX_DECLARE__ } // namespace #endif //__WU_Q_TRUE_FALSE_COMBO_BOX__H_ connectome-workbench-1.2.3+git41-gc4c6c90/src/GuiQt/WuQWebView.cxx000066400000000000000000000026721300200146000243350ustar00rootroot00000000000000/*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 "WuQWebView.h" #include #include #include WuQWebView::WuQWebView(QWidget *parent) : QWebView(parent) { connect(page()->networkAccessManager(), SIGNAL(sslErrors(QNetworkReply*, const QList & )), this, SLOT(handleSslErrors(QNetworkReply*, const QList & ))); } void WuQWebView::handleSslErrors(QNetworkReply* reply, const QList &/*errors*/) { /*qDebug() << "handleSslErrors: "; foreach (QSslError e, errors) { qDebug() << "ssl error: " << e; }*/ reply->ignoreSslErrors(); } connectome-workbench-1.2.3+git41-gc4c6c90/src/GuiQt/WuQWebView.h000066400000000000000000000021641300200146000237560ustar00rootroot00000000000000#ifndef WUQWEBVIEW_H #define WUQWEBVIEW_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 class WuQWebView : public QWebView { Q_OBJECT public: WuQWebView(QWidget *parent = 0); private slots: void handleSslErrors(QNetworkReply* reply, const QList &/*errors*/); }; #endif // WUQWEBVIEW_H connectome-workbench-1.2.3+git41-gc4c6c90/src/GuiQt/WuQWidget.cxx000066400000000000000000000056441300200146000242120ustar00rootroot00000000000000 /*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 __WU_Q_WIDGET_DECLARE__ #include "WuQWidget.h" #undef __WU_Q_WIDGET_DECLARE__ #include "CaretAssert.h" using namespace caret; /** * \class caret::WuQWidget * \brief Class for extending Qt GUI Widgets through encapsulation * * It is often desirable to extend a Qt GUI widget but one does * not want to directly subclass a QWidget. For example, a * QComboBox is a useful control for selection of an enumerated * type. It is desirable to allow the user of the class to only * access the combo box by setting and getting the enumerated type. * However, if one directly subclasses QComboBox, it allows access * to many methods including those that get and set via an index * or name which could allow the insertion of invalid values. So, * by encapsualting, one can provide accessor methods using the * enumerated types. Protected inheritance is not a viable solution * because it prevents the connection of signals and slots. * * This class is not derived from QWidget since that would require. * the additional of a layout to hold the actual widget. Instead, * this class is derived from QObject so that there are no 'widget' * methods available to the user and so that the signal and slot * mechanism is available. Subclasses can define, signals and slots * that are appropriate, such as those that use an enumerated type * as a parameter. * * The parent of derived classes must be passed to the contructor. * The parent is typically some deriviative of QWidget such as * QDialog. By using a parent, Qt will destroy an instance of * this class when the parent is destroyed. * * An instance of this class is never added to a layout. Instead, * deriving classes implement the getWidget() method to provide * the enapsulated widget for insertion into a layout. * * Since the encapsulated QWidget is added to a layout, never * delete the encapsulated widget since it will have a Qt parent * which will destroy it. */ /** * Constructor. */ WuQWidget::WuQWidget(QObject* parent) : QObject(parent) { CaretAssert(parent); } /** * Destructor. */ WuQWidget::~WuQWidget() { } connectome-workbench-1.2.3+git41-gc4c6c90/src/GuiQt/WuQWidget.h000066400000000000000000000026711300200146000236340ustar00rootroot00000000000000#ifndef __WU_Q_WIDGET__H_ #define __WU_Q_WIDGET__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 class QWidget; namespace caret { class WuQWidget : public QObject { Q_OBJECT public: WuQWidget(QObject* parent); virtual ~WuQWidget(); virtual QWidget* getWidget() = 0; private: WuQWidget(const WuQWidget&); WuQWidget& operator=(const WuQWidget&); public: private: }; #ifdef __WU_Q_WIDGET_DECLARE__ // #endif // __WU_Q_WIDGET_DECLARE__ } // namespace #endif //__WU_Q_WIDGET__H_ connectome-workbench-1.2.3+git41-gc4c6c90/src/GuiQt/WuQWidgetObjectGroup.cxx000066400000000000000000000107441300200146000263530ustar00rootroot00000000000000/*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 "WuQWidgetObjectGroup.h" using namespace caret; /** * constructor. * WuQWidgetObjectGroup::WuQWidgetObjectGroup(QWidget* parent) : QObject(parent) { } */ /** * constructor. */ WuQWidgetObjectGroup::WuQWidgetObjectGroup(QObject* parent) : QObject(parent) { } /** * destructor. */ WuQWidgetObjectGroup::~WuQWidgetObjectGroup() { this->clear(); } /** * Remove all objects from this group. The objects are NOT deleted * since widgets are typically 'owned' by their parents. */ void WuQWidgetObjectGroup::clear() { this->objects.clear(); } /** * add a QObject (QWidget is descendent of QObject) to the group. */ void WuQWidgetObjectGroup::add(QObject* o) { this->objects.push_back(o); } /** * enable the group's widgets. */ void WuQWidgetObjectGroup::setEnabled(bool enable) { for (int i = 0; i < this->objects.size(); i++) { QWidget* widget = qobject_cast(this->objects[i]); if (widget != NULL) { widget->setEnabled(enable); } } } /** * disable the group's widgets. */ void WuQWidgetObjectGroup::setDisabled(bool disable) { for (int i = 0; i < this->objects.size(); i++) { QWidget* widget = qobject_cast(this->objects[i]); if (widget != NULL) { widget->setDisabled(disable); } } } /** * @return true if any of the widgets are visible. */ bool WuQWidgetObjectGroup::isVisible() const { for (int i = 0; i < this->objects.size(); i++) { QWidget* widget = qobject_cast(this->objects[i]); if (widget != NULL) { if (widget->isVisible()) { return true; } } } return false; } /** * make the group's widgets visible. */ void WuQWidgetObjectGroup::setVisible(bool makeVisible) { for (int i = 0; i < this->objects.size(); i++) { QWidget* widget = qobject_cast(this->objects[i]); if (widget != NULL) { widget->setVisible(makeVisible); } } } /** * make the group's widgets hidden. */ void WuQWidgetObjectGroup::setHidden(bool hidden) { setVisible(! hidden); } /** * block signals. */ void WuQWidgetObjectGroup::blockAllSignals(bool blockTheSignals) { for (int i = 0; i < this->objects.size(); i++) { this->objects.at(i)->blockSignals(blockTheSignals); } } /** * set status of all checkboxes. */ void WuQWidgetObjectGroup::setAllCheckBoxesChecked(const bool b) { for (int i = 0; i < this->objects.size(); i++) { QCheckBox* cb = qobject_cast(this->objects.at(i)); if (cb != NULL) { cb->setChecked(b); } } } /** * make all of the widgets in the group the same size as size hint * of largest widget. */ void WuQWidgetObjectGroup::resizeAllToLargestSizeHint() { int largestWidth = -1; int largestHeight = -1; for (int i = 0; i < this->objects.size(); i++) { QWidget* widget = qobject_cast(this->objects[i]); if (widget == NULL) { continue; } const QSize size = widget->sizeHint(); if (size.width() > largestWidth) { largestWidth = size.width(); } if (size.height() > largestHeight) { largestHeight = size.height(); } } if ((largestWidth > 0) && (largestHeight > 0)) { QSize newSize(largestWidth, largestHeight); for (int i = 0; i < this->objects.size(); i++) { QWidget* widget = qobject_cast(this->objects[i]); if (widget != NULL) { widget->setFixedSize(newSize); } } } } connectome-workbench-1.2.3+git41-gc4c6c90/src/GuiQt/WuQWidgetObjectGroup.h000066400000000000000000000042171300200146000257760ustar00rootroot00000000000000 #ifndef __QT_WIDGET_OBJECT_GROUP_H__ #define __QT_WIDGET_OBJECT_GROUP_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 class QLayout; class QObject; namespace caret { /** * Groups QWidget and/or QObjects for applying operations to * all such as blocking signals and setting visibility. */ class WuQWidgetObjectGroup : public QObject { Q_OBJECT public: //WuQWidgetObjectGroup(QWidget* parent); WuQWidgetObjectGroup(QObject* parent); ~WuQWidgetObjectGroup(); void add(QObject* w); void clear(); QObject* getObject() { return qobject_cast(this); } bool isVisible() const; public slots: void blockAllSignals(bool blockTheSignals); void setEnabled(bool enable); void setDisabled(bool disable); void setVisible(bool makeVisible); void setHidden(bool hidden); void resizeAllToLargestSizeHint(); void setAllCheckBoxesChecked(const bool b); protected: QVector objects; private: // prevent access to QObject's blockSignals() method bool blockSignals(bool); }; } // namespace #endif // __QT_WIDGET_OBJECT_GROUP_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/GuiQt/WuQtUtilities.cxx000066400000000000000000001207621300200146000251250ustar00rootroot00000000000000/*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 #include #include #include #include #include #include #include #include #include #include #include #include "CaretAssert.h" #include "CaretLogger.h" #include "WuQtUtilities.h" using namespace caret; /** * Create an action with the specified text. * * @param text * Text for the action. * @param toolAndStatusTipText * Text for both tool and status tips. * @param parent * Owner of the created action. * @return * Action that was created. */ QAction* WuQtUtilities::createAction(const QString& text, const QString& toolAndStatusTipText, QObject* parent) { QAction* action = new QAction(parent); action->setText(text); if (toolAndStatusTipText.isEmpty() == false) { action->setStatusTip(toolAndStatusTipText); action->setToolTip(toolAndStatusTipText); } return action; } /** * Create an action with the specified text. * * @param text * Text for the action. * @param toolAndStatusTipText * Text for both tool and status tips. * @param shortcut * Keyboard shortcut. * @param parent * Owner of the created action. * @return * Action that was created. */ QAction* WuQtUtilities::createAction(const QString& text, const QString& toolAndStatusTipText, const QKeySequence& shortcut, QObject* parent) { QAction* action = new QAction(parent); action->setText(text); if (toolAndStatusTipText.isEmpty() == false) { action->setStatusTip(toolAndStatusTipText); action->setToolTip(toolAndStatusTipText); } action->setShortcut(shortcut); return action; } /** * Create an action with the specified text, shortcut, * and calls the specified slot. * * @param text * Text for the action. * @param toolAndStatusTipText * Text for both tool and status tips. * @param shortcut * Keyboard shortcut. * @param parent * Owner of the created action. * @param receiver * Owner of method that is called when action is triggered. * @param method * method in receiver that is called when action is triggered. * @return * Action that was created. */ QAction* WuQtUtilities::createAction(const QString& text, const QString& toolAndStatusTipText, const QKeySequence& shortcut, QObject* parent, QObject* receiver, const char* method) { QAction* action = WuQtUtilities::createAction(text, toolAndStatusTipText, parent, receiver, method); action->setShortcut(shortcut); return action; } /** * Create an action with the specified text and calls * the specified slot. * * @param text * Text for the action. * @param toolAndStatusTipText * Text for both tool and status tips. * @param parent * Owner of the created action. * @param receiver * Owner of method that is called when action is triggered. * @param method * method in receiver that is called when action is triggered. * @return * Action that was created. */ QAction* WuQtUtilities::createAction(const QString& text, const QString& toolAndStatusTipText, QObject* parent, QObject* receiver, const char* method) { QAction* action = WuQtUtilities::createAction(text, toolAndStatusTipText, parent); // QAction* action = new QAction(parent); // action->setText(text); // if (toolAndStatusTipText.isEmpty() == false) { // action->setStatusTip(toolAndStatusTipText); // action->setToolTip(toolAndStatusTipText); // } QObject::connect(action, SIGNAL(triggered(bool)), receiver, method); return action; } /** * Create a pushbutton. * * @param text * Text for the pushbutton. * @param toolAndStatusTipText * Text for both tool and status tips. * @param receiver * Owner of method that is called when button is clicked. * @param method * method in receiver that is called when button is clicked. * @return * Pushbutton that was created. */ QPushButton* WuQtUtilities::createPushButton(const QString& text, const QString& toolAndStatusTipText, QObject* receiver, const char* method) { QPushButton* pb = new QPushButton(text); if (toolAndStatusTipText.isEmpty() == false) { pb->setStatusTip(toolAndStatusTipText); pb->setToolTip(toolAndStatusTipText); } QObject::connect(pb, SIGNAL(clicked()), receiver, method); return pb; } /** * Create a horizontal line widget used as a separator. * * @return A horizontal line widget used as a separator. */ QWidget* WuQtUtilities::createHorizontalLineWidget() { QFrame* frame = new QFrame(); frame->setMidLineWidth(1); frame->setLineWidth(1); frame->setFrameStyle(QFrame::HLine | QFrame::Sunken); return frame; } /** * Create a vertical line widget used as a separator. * * @return A vertical line widget used as a separator. */ QWidget* WuQtUtilities::createVerticalLineWidget() { QFrame* frame = new QFrame(); frame->setMidLineWidth(0); frame->setLineWidth(2); frame->setFrameStyle(QFrame::VLine | QFrame::Sunken); return frame; } /** * Move a window relative to its parent window * but do not let the window move off the screen. * X is left to right, Y is top to bottom. * * @param parentWindow * The parent window of the window being moved. * @param window * The window. * @param xOffset * Offset widget from parent by this X amount. * @param yOffset * Offset widget from parent by this Y amount. */ void WuQtUtilities::moveWindowToOffset(QWidget* parentWindow, QWidget* window, const int xOffset, const int yOffset) { int x = parentWindow->x() + xOffset; int y = parentWindow->y() + yOffset; QDesktopWidget* dw = QApplication::desktop(); const QRect geometry = dw->availableGeometry(parentWindow); const int margin = 20; const int maxX = geometry.width() - margin; const int maxY = geometry.height() - margin; if (x > maxX) x = maxX; if (x < margin) x = margin; if (y > maxY) y = maxY; if (y < margin) y = margin; window->move(x, y); } /** * Place a dialog next to its parent. May not work correctly with * multi-screen systems. * * MUST BE CALLED after a window is displayed since the given window * may not have its geometry (size) set until AFTER it is displayed. * * It will stop after the first one of these actions that is successful: * 1) Put window on right of parent if all of window will be visible. * 2) Put window on left of parent if all of window will be visible. * 3) Put window on right of parent if more space to right of window. * 4) Put window on left of parent. * @param parent * The parent. * @param window * The window. */ void WuQtUtilities::moveWindowToSideOfParent(QWidget* parent, QWidget* window) { const QRect parentGeometry = parent->geometry(); const int px = parentGeometry.x(); const int py = parentGeometry.y(); const int pw = parentGeometry.width(); const int ph = parentGeometry.height(); const int parentMaxX = px + pw; //int x = px + pw + 1; int y = py + ph - window->height() - 20; // int y = py; const int windowWidth = window->width(); QDesktopWidget* dw = QApplication::desktop(); const QRect geometry = dw->availableGeometry(parent); const int screenMinX = geometry.x(); const int screenWidth = geometry.width(); const int screenMaxX = screenMinX + screenWidth; const int screenMaxY = geometry.x() + geometry.height(); const int spaceOnLeft = px -screenMinX; const int spaceOnRight = screenMaxX - parentMaxX; int x = screenMinX; if (spaceOnRight > windowWidth) { x = parentMaxX; } else if (spaceOnLeft > windowWidth) { x = px - windowWidth; } else if (spaceOnRight > spaceOnLeft) { x = screenMaxX - windowWidth; } // else { // x = screenMinX; // } if ((x + windowWidth) > screenMaxX) { x = screenMaxX - windowWidth; } if (x < screenMinX) { x = screenMinX; } const int maxY = screenMaxY - window->height() - 50; if (y > maxY) { y = maxY; } if (y < 50) { y = 50; } window->move(x, y); } /** * Move and size a window limiting window so that * it fits within the screen. * @param x * X-coordinate of window. * @param y * Y-coordinate of window. * @param w * Width of window. * @param h * Height of window. * @param xywhOut * On exit contains 4 values that are the actual * x, y, width, and height of the window after * any needed adjustments for screen sizes. */ void WuQtUtilities::moveAndSizeWindow(QWidget* window, const int32_t x, const int32_t y, const int32_t w, const int32_t h, int32_t* xywhOut) { QDesktopWidget* dw = QApplication::desktop(); /* * Get available geometry where window is to be placed * This geometry is all screens together as one large screen */ QPoint pXY(x, y); const QRect availableRect = dw->screen()->geometry(); const int32_t screenSizeX = availableRect.width(); const int32_t screenSizeY = availableRect.height(); /* * Limit width/height in desktop */ int32_t width = std::min(w, screenSizeX); int32_t height = std::min(h, screenSizeY); /* * Limit window position in desktop */ int32_t xPos = x; if (xPos < availableRect.x()) { xPos = availableRect.x(); } const int32_t maxX = screenSizeX - 200; if (xPos >= maxX) { xPos = maxX; } int32_t yPos = y; if (yPos < availableRect.y()) { yPos = availableRect.y(); } const int32_t maxY = screenSizeY - 200; if (yPos >= maxY) { yPos = maxY; } /* * Make sure visible in closest screen */ pXY.setX(xPos); pXY.setY(yPos); const int32_t nearestScreen = dw->screenNumber(pXY); if (nearestScreen >= 0) { const QRect screenRect = dw->availableGeometry(nearestScreen); if (xPos < screenRect.x()) { xPos = screenRect.x(); } const int32_t maxX = screenRect.right() - 200; if (xPos > maxX) { xPos = maxX; } if (yPos < screenRect.y()) { yPos = screenRect.y(); } const int32_t maxY = screenRect.bottom() - 200; if (yPos > maxY) { yPos = maxY; } /* * ScreenRect width/height is size of screen * reduced by menu bars, docks and other items * that reduce available screen space */ const int32_t maxWidth = screenRect.width(); if (width > maxWidth) { width = maxWidth; } const int32_t maxHeight = screenRect.height(); if (height > maxHeight) { height = maxHeight; } const QRect geom = dw->screenGeometry(nearestScreen); CaretLogInfo(QString("Window Available width/height: %1, %2 \n Screen width/height: %3, %4" ).arg(maxWidth).arg(maxHeight).arg(geom.width()).arg(geom.height())); } /* * Move and size window */ window->move(xPos, yPos); window->resize(width, height); if (xywhOut != NULL) { xywhOut[0] = window->x(); xywhOut[1] = window->y(); xywhOut[2] = window->width(); xywhOut[3] = window->height(); } } /** * Resize a window but limit maximum size of window * to an area less than the full size of the display. * Has no effect on position of the window so part * of window may end up being outside the display. * * @param window * Window that is resized. * @param width * Desired width of the window. * @param height * Desired width of the window. */ void WuQtUtilities::resizeWindow(QWidget* window, const int32_t width, const int32_t height) { 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() - 100; const int32_t screenHeight = screenRect.height() - 100; const int windowWidth = std::min(width, screenWidth); const int windowHeight = std::min(height, screenHeight); window->resize(windowWidth, windowHeight); } } /** * Table widget has a default size of 640 x 480. * Estimate the size of the dialog with the table fully expanded. * * @param tableWidget * Table widget whose size is estimated. */ QSize WuQtUtilities::estimateTableWidgetSize(QTableWidget* tableWidget) { QSize tableSize(0, 0); /* * Table widget has a default size of 640 x 480. * So estimate the size of the dialog with the table fully * expanded. */ const int numRows = tableWidget->rowCount(); const int numCols = tableWidget->columnCount(); const int cellGap = (tableWidget->showGrid() ? 3 : 0); if ((numRows > 0) && (numCols > 0)) { int tableWidth = 10; // start out with a little extra space int tableHeight = 0; if (tableWidget->horizontalHeader()->isHidden() == false) { QHeaderView* columnHeader = tableWidget->horizontalHeader(); const int columnHeaderHeight = columnHeader->sizeHint().height(); tableHeight += columnHeaderHeight; } if (tableWidget->verticalHeader()->isHidden() == false) { QHeaderView* rowHeader = tableWidget->verticalHeader(); const int rowHeaderHeight = rowHeader->sizeHint().width(); tableHeight += rowHeaderHeight; } std::vector columnWidths(numCols, 0); std::vector rowHeights(numRows, 0); for (int iCol = 0; iCol < numCols; iCol++) { columnWidths[iCol] = tableWidget->columnWidth(iCol) + cellGap; } for (int jRow = 0; jRow < numRows; jRow++) { rowHeights[jRow]= (tableWidget->rowHeight(jRow) + cellGap); } for (int iCol = 0; iCol < numCols; iCol++) { for (int jRow = 0; jRow < numRows; jRow++) { QWidget* widget = tableWidget->cellWidget(jRow, iCol); if (widget != NULL) { const QSize widgetSizeHint = widget->sizeHint(); columnWidths[iCol] = std::max(columnWidths[iCol], widgetSizeHint.width()); rowHeights[jRow] = std::max(rowHeights[jRow], widgetSizeHint.height()); } QTableWidgetItem* item = tableWidget->item(jRow, iCol); if (item != NULL) { int itemWidth = 0; int itemHeight = 0; if (item->flags() && Qt::ItemIsUserCheckable) { itemWidth += 12; } QFont font = item->font(); const QString text = item->text(); if (text.isEmpty() == false) { QFont font = item->font(); QFontMetrics fontMetrics(font); const int textWidth = fontMetrics.width(text); const int textHeight = fontMetrics.height(); itemWidth += textWidth; itemHeight = std::max(itemHeight, textHeight); } columnWidths[iCol] = std::max(columnWidths[iCol], itemWidth); rowHeights[jRow] = std::max(rowHeights[jRow], itemHeight); } } } for (int iCol = 0; iCol < numCols; iCol++) { tableWidth += columnWidths[iCol]; } for (int jRow = 0; jRow < numRows; jRow++) { tableHeight += (rowHeights[jRow] - 2); } tableSize.setWidth(tableWidth); tableSize.setHeight(tableHeight); } return tableSize; } /** * Set the tool tip and status tip for a widget. * * @param widget * Widget that has its tool and status tip set. * @param text * Text for the tool and status tip. */ void WuQtUtilities::setToolTipAndStatusTip(QWidget* widget, const QString& text) { widget->setToolTip(text); widget->setStatusTip(text); } /** * Set the tool tip and status tip for an action. * * @param action * Action that has its tool and status tip set. * @param text * Text for the tool and status tip. */ void WuQtUtilities::setToolTipAndStatusTip(QAction* action, const QString& text) { action->setToolTip(text); action->setStatusTip(text); } /** * Print a list of resources to the Caret Logger. */ void WuQtUtilities::sendListOfResourcesToCaretLogger() { QString msg = "Resources loaded:\n"; QDir dir(":/"); QFileInfoList infoList = dir.entryInfoList(); for (int i = 0; i < infoList.count(); i++) { msg += " "; msg += infoList.at(i).filePath(); } CaretLogInfo(msg); } /** * Load an icon. * * @param filename * Name of file (or resource) containing the icon. * @param iconOut * Output that will contain the desired icon. * @return * True if the icon is valid, else false. */ bool WuQtUtilities::loadIcon(const QString& filename, QIcon& iconOut) { QPixmap pixmap; const bool valid = WuQtUtilities::loadPixmap(filename, pixmap); if (valid) { iconOut.addPixmap(pixmap); } return valid; } /** * Load an icon. * @param filename * Name of file containing the icon. * @return Pointer to icon (call must delete it) or NULL * if there was a failure to load the icon. */ QIcon* WuQtUtilities::loadIcon(const QString& filename) { QPixmap pixmap; const bool valid = WuQtUtilities::loadPixmap(filename, pixmap); QIcon* icon = NULL; if (valid) { icon = new QIcon(pixmap); } return icon; } /** * Load an pixmap. * * @param filename * Name of file (or resource) containing the pixmap. * @param pixmapOut * Output that will contain the desired pixmap. * @return * True if the pixmap is valid, else false. */ bool WuQtUtilities::loadPixmap(const QString& filename, QPixmap& pixmapOut) { bool valid = pixmapOut.load(filename); if (valid == false) { QString msg = "Failed to load Pixmap \"" + filename + "\"."; CaretLogSevere(msg); } else if ((pixmapOut.width() <= 0) || (pixmapOut.height() <= 0)) { QString msg = "Pixmap \"" + filename + "\" has invalid size."; CaretLogSevere(msg); valid = false; } return valid; } /** * Get the maximum height from the given widgets. * * @param w1 Required widget. * @param w2 Required widget. * @param w3 Optional widget. * @param w4 Optional widget. * @param w5 Optional widget. * @param w6 Optional widget. * @param w7 Optional widget. * @param w8 Optional widget. * @param w9 Optional widget. * @param w10 Optional widget. * @return * Maximum height of the widgets. */ int WuQtUtilities::getMaximumWidgetHeight(QWidget* w1, QWidget* w2, QWidget* w3, QWidget* w4, QWidget* w5, QWidget* w6, QWidget* w7, QWidget* w8, QWidget* w9, QWidget* w10) { QVector widgets; if (w1 != NULL) widgets.push_back(w1); if (w2 != NULL) widgets.push_back(w2); if (w3 != NULL) widgets.push_back(w3); if (w4 != NULL) widgets.push_back(w4); if (w5 != NULL) widgets.push_back(w5); if (w6 != NULL) widgets.push_back(w6); if (w7 != NULL) widgets.push_back(w7); if (w8 != NULL) widgets.push_back(w8); if (w9 != NULL) widgets.push_back(w9); if (w10 != NULL) widgets.push_back(w10); int maxHeight = 0; const int num = widgets.size(); for (int i = 0; i < num; i++) { const int h = widgets[i]->sizeHint().height(); if (h > maxHeight) { maxHeight = h; } } return maxHeight; } /** * Find the widget with the maximum height in its * size hint. Apply this height to all of the widgets. * * @param w1 Required widget. * @param w2 Required widget. * @param w3 Optional widget. * @param w4 Optional widget. * @param w5 Optional widget. * @param w6 Optional widget. * @param w7 Optional widget. * @param w8 Optional widget. * @param w9 Optional widget. * @param w10 Optional widget. */ void WuQtUtilities::matchWidgetHeights(QWidget* w1, QWidget* w2, QWidget* w3, QWidget* w4, QWidget* w5, QWidget* w6, QWidget* w7, QWidget* w8, QWidget* w9, QWidget* w10) { const int maxHeight = getMaximumWidgetHeight(w1, w2, w3, w4, w5, w6, w7, w8, w9, w10); if (maxHeight > 0) { w1->setFixedHeight(maxHeight); w2->setFixedHeight(maxHeight); if (w3 != NULL) w3->setFixedHeight(maxHeight); if (w4 != NULL) w4->setFixedHeight(maxHeight); if (w5 != NULL) w5->setFixedHeight(maxHeight); if (w6 != NULL) w6->setFixedHeight(maxHeight); if (w7 != NULL) w7->setFixedHeight(maxHeight); if (w8 != NULL) w8->setFixedHeight(maxHeight); if (w9 != NULL) w9->setFixedHeight(maxHeight); if (w10 != NULL) w10->setFixedHeight(maxHeight); } } /** * Find the widget with the maximum width in its * size hint. Apply this width to all of the widgets. * * @param w1 Required widget. * @param w2 Required widget. * @param w3 Optional widget. * @param w4 Optional widget. * @param w5 Optional widget. * @param w6 Optional widget. * @param w7 Optional widget. * @param w8 Optional widget. * @param w9 Optional widget. * @param w10 Optional widget. */ void WuQtUtilities::matchWidgetWidths(QWidget* w1, QWidget* w2, QWidget* w3, QWidget* w4, QWidget* w5, QWidget* w6, QWidget* w7, QWidget* w8, QWidget* w9, QWidget* w10) { QVector widgets; if (w1 != NULL) widgets.push_back(w1); if (w2 != NULL) widgets.push_back(w2); if (w3 != NULL) widgets.push_back(w3); if (w4 != NULL) widgets.push_back(w4); if (w5 != NULL) widgets.push_back(w5); if (w6 != NULL) widgets.push_back(w6); if (w7 != NULL) widgets.push_back(w7); if (w8 != NULL) widgets.push_back(w8); if (w9 != NULL) widgets.push_back(w9); if (w10 != NULL) widgets.push_back(w10); int maxWidth = 0; const int num = widgets.size(); for (int i = 0; i < num; i++) { const int w = widgets[i]->sizeHint().width(); if (w > maxWidth) { maxWidth = w; } } if (maxWidth > 0) { for (int i = 0; i < num; i++) { widgets[i]->setFixedWidth(maxWidth); } } } /** * Set the margins and spacing for a layout. * @param layout * Layout that has margins and spacings set. * @param spacing * Spacing between widgets within layout. * @param contentsMargin * Margin around the layout. */ void WuQtUtilities::setLayoutSpacingAndMargins(QLayout* layout, const int spacing, const int contentsMargin) { layout->setSpacing(spacing); layout->setContentsMargins(contentsMargin, contentsMargin, contentsMargin, contentsMargin); } /** * Set the content margins around a layout. * * @param contentsMargin * Margin around the layout. */ void WuQtUtilities::setLayoutMargins(QLayout* layout, const int contentsMargin) { layout->setContentsMargins(contentsMargin, contentsMargin, contentsMargin, contentsMargin); } /** * @return The minimum size (width/height) of all screens. */ QSize WuQtUtilities::getMinimumScreenSize() { int minWidth = std::numeric_limits::max(); int minHeight = std::numeric_limits::max(); QDesktopWidget* dw = QApplication::desktop(); const int numScreens = dw->screenCount(); for (int i = 0; i < numScreens; i++) { const QRect rect = dw->availableGeometry(i); const int w = rect.width(); const int h = rect.height(); minWidth = std::min(minWidth, w); minHeight = std::min(minHeight, h); } const QSize size(minWidth, minHeight); return size; } /** * Is the user's display small? This is loosely * defined as a vertical resolution of 800 or less. * @return true if resolution is 800 or less, * else false. */ bool WuQtUtilities::isSmallDisplay() { QDesktopWidget* dw = QApplication::desktop(); QRect screenRect = dw->screenGeometry(); const int verticalSize = screenRect.height(); if (verticalSize <= 800) { return true; } return false; } /** * Get a String containing information about a layout' content. * @param layout * The layout * @return * String with info. */ QString WuQtUtilities::getLayoutContentDescription(QLayout* layout) { QString s; s.reserve(25000); s += ("Layout type : " + QString(typeid(*layout).name()) + "\n"); const int itemCount = layout->count(); for (int32_t i = 0; i < itemCount; i++) { s += " "; QLayoutItem* layoutItem = layout->itemAt(i); QLayout* layout = layoutItem->layout(); if (layout != NULL) { s += QString(typeid(*layout).name()); } QWidget* widget = layoutItem->widget(); if (widget != NULL) { s += QString(typeid(*widget).name()); } QSpacerItem* spacerItem = layoutItem->spacerItem(); if (spacerItem != NULL) { s += "QSpacerItem"; } } 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."); } } /** * Create 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 tooltipText * Text for the tooltip. * @return * Text reformatted for display in a tool tip. */ QString WuQtUtilities::createWordWrappedToolTipText(const QString& tooltipText) { if (tooltipText.isEmpty()) { return ""; } QTextDocument textDocument(tooltipText); QString html = textDocument.toHtml(); return html; } /** * 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 widget * Widget on which tooltip is set. * @param tooltipText * Text for the widget's tooltip. */ void WuQtUtilities::setWordWrappedToolTip(QWidget* widget, const QString& tooltipText) { widget->setToolTip(createWordWrappedToolTipText(tooltipText)); } /** * Convert a Qt::CheckState to a boolean value. * * @param checkState * The check state value. * @return * true if checked or partially checked, else false. */ bool WuQtUtilities::checkStateToBool(const Qt::CheckState checkState) { if (checkState == Qt::Unchecked) { return false; } return true; } /** * Convert a boolean value to a Qt::CheckState * * @param value * The boolean value. * @return * Check state indicating checked or not checked. */ Qt::CheckState WuQtUtilities::boolToCheckState(const bool value) { if (value) { return Qt::Checked; } return Qt::Unchecked; } /** * Create a pixmap with the given color. * * @param widget * Widget that will contain pixmap. It used for getting the widget's * foreground and background colors. * @param pixmapWidth * Width for the pixmap. * @param pixmapHeight * Height for the pixmap. * @param caretColor * The Caret Color Enum value. * @param rgba * RGBA color for the pixmap. If the alpha component is zero, a * outline box with an 'X' symbol is drawn using the widget's * foreground color. * @param outlineFlag * If true, drawn an outline with the given color and the background * using the widget's background color. * @return * The pixmap. */ QPixmap WuQtUtilities::createCaretColorEnumPixmap(const QWidget* widget, const int32_t pixmapWidth, const int32_t pixmapHeight, const CaretColorEnum::Enum caretColor, const float customColorRGBA[4], const bool outlineFlag) { bool noneColorFlag = false; bool validColorFlag = false; float colorRGBA[4]; switch (caretColor) { case CaretColorEnum::NONE: noneColorFlag = true; break; case CaretColorEnum::CUSTOM: if (customColorRGBA[3] > 0.0) { colorRGBA[0] = customColorRGBA[0]; colorRGBA[1] = customColorRGBA[1]; colorRGBA[2] = customColorRGBA[2]; colorRGBA[3] = customColorRGBA[3]; validColorFlag = true; } break; case CaretColorEnum::AQUA: case CaretColorEnum::BLACK: case CaretColorEnum::BLUE: case CaretColorEnum::FUCHSIA: case CaretColorEnum::GRAY: case CaretColorEnum::GREEN: case CaretColorEnum::LIME: case CaretColorEnum::MAROON: case CaretColorEnum::NAVY: case CaretColorEnum::OLIVE: case CaretColorEnum::PURPLE: case CaretColorEnum::RED: case CaretColorEnum::SILVER: case CaretColorEnum::TEAL: case CaretColorEnum::WHITE: case CaretColorEnum::YELLOW: CaretColorEnum::toRGBFloat(caretColor, colorRGBA); colorRGBA[3] = 1.0; validColorFlag = true; break; } /* * Create a small pixmap that will contain * the foreground color around the pixmap's perimeter. */ QPixmap pixmap(pixmapWidth, pixmapHeight); QSharedPointer painter = WuQtUtilities::createPixmapWidgetPainter(widget, pixmap); if (noneColorFlag) { /* * Draw lines (rectangle) around the perimeter of the pixmap * and an 'X' in the widget's foreground color. */ for (int32_t i = 0; i < 1; i++) { painter->drawRect(i, i, pixmapWidth - 1 - i * 2, pixmapHeight - 1 - i * 2); } painter->drawLine(0, 0, pixmapWidth - 1, pixmapHeight - 1); painter->drawLine(0, pixmapHeight - 1, pixmapWidth - 1, 0); } else if (validColorFlag) { if (outlineFlag) { /* * Draw lines (rectangle) around the perimeter of the pixmap */ painter->setPen(QColor::fromRgbF(colorRGBA[0], colorRGBA[1], colorRGBA[2])); for (int32_t i = 0; i < 3; i++) { painter->drawRect(i, i, pixmapWidth - 1 - i * 2, pixmapHeight - 1 - i * 2); } } else { /* * Fill the pixmap with the RGBA color. */ pixmap.fill(QColor::fromRgbF(colorRGBA[0], colorRGBA[1], colorRGBA[2])); } } return pixmap; } /** * 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::createPixmapWidgetPainterOriginCenter100x100(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(pixmap.width() / 200.0, -(pixmap.height() / 200.0)); 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 painter will be in the BOTTOM LEFT corner. * * @param widget * Widget used for coloring. * @param pixmap * The Pixmap. * @return * Shared pointer containing QPainter for drawing to the pixmap. */ QSharedPointer WuQtUtilities::createPixmapWidgetPainterOriginBottomLeft(const QWidget* widget, QPixmap& pixmap) { 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(0.0, pixmap.height() - 1); 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 * 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 painter will be in the TOP LEFT corner. * * @param widget * Widget used for coloring. * @param pixmap * The Pixmap. * @return * Shared pointer containing QPainter for drawing to the pixmap. */ QSharedPointer WuQtUtilities::createPixmapWidgetPainter(const QWidget* widget, QPixmap& pixmap) { CaretAssert(widget); CaretAssert(pixmap.width() > 0); CaretAssert(pixmap.height() > 0); /* * Get the widget's background and foreground color */ const QPalette palette = widget->palette(); const QPalette::ColorRole backgroundRole = widget->backgroundRole(); const QBrush backgroundBrush = palette.brush(backgroundRole); const QColor backgroundColor = backgroundBrush.color(); const QPalette::ColorRole foregroundRole = widget->foregroundRole(); const QBrush foregroundBrush = palette.brush(foregroundRole); const QColor foregroundColor = foregroundBrush.color(); /* * Create a painter and fill the pixmap with * the background color */ QSharedPointer painter(new QPainter(&pixmap)); painter->setRenderHint(QPainter::Antialiasing, true); painter->setBackgroundMode(Qt::OpaqueMode); painter->fillRect(pixmap.rect(), backgroundColor); painter->setPen(foregroundColor); return painter; } connectome-workbench-1.2.3+git41-gc4c6c90/src/GuiQt/WuQtUtilities.h000066400000000000000000000204631300200146000245470ustar00rootroot00000000000000 #ifndef __WU_QT_UTILITIES_H__ #define __WU_QT_UTILITIES_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 "CaretColorEnum.h" class QAction; class QBoxLayout; class QDialog; class QIcon; class QKeySequence; class QLayout; class QObject; class QPixmap; class QPushButton; class QString; class QTableWidget; class QWidget; namespace caret { /** * Utilities for use with Qt. */ class WuQtUtilities { public: static QAction* createAction(const QString& text, const QString& toolAndStatusTipText, const QKeySequence& shortcut, QObject* parent, QObject* receiver, const char* method); static QAction* createAction(const QString& text, const QString& toolAndStatusTipText, QObject* parent, QObject* receiver, const char* method); static QAction* createAction(const QString& text, const QString& toolAndStatusTipText, QObject* parent); static QAction* createAction(const QString& text, const QString& toolAndStatusTipText, const QKeySequence& shortcut, QObject* parent); static QPushButton* createPushButton(const QString& text, const QString& toolAndStatusTipText, QObject* receiver, const char* method); static QWidget* createVerticalLineWidget(); static QWidget* createHorizontalLineWidget(); static QPixmap createCaretColorEnumPixmap(const QWidget* widgetForPixmap, const int32_t pixmapWidth, const int32_t pixmapHeight, const CaretColorEnum::Enum caretColor, const float rgba[4], const bool outlineFlag); static QSharedPointer createPixmapWidgetPainter(const QWidget* widget, QPixmap& pixmap); static QSharedPointer createPixmapWidgetPainterOriginBottomLeft(const QWidget* widget, QPixmap& pixmap); static QSharedPointer createPixmapWidgetPainterOriginCenter100x100(const QWidget* widget, QPixmap& pixmap); static void moveWindowToOffset(QWidget* parentWidget, QWidget* window, const int xOffset, const int yOffset); static void setToolTipAndStatusTip(QWidget* widget, const QString& text); static void setToolTipAndStatusTip(QAction* action, const QString& text); static void sendListOfResourcesToCaretLogger(); static bool loadIcon(const QString& filename, QIcon& iconOut); static QIcon* loadIcon(const QString& filename); static bool loadPixmap(const QString& filename, QPixmap& pixmapOut); static void moveWindowToSideOfParent(QWidget* parent, QWidget* window); static void moveAndSizeWindow(QWidget* window, const int32_t x, const int32_t y, const int32_t w, const int32_t h, int32_t* xywhOut); static void resizeWindow(QWidget* window, const int32_t width, const int32_t height); static int getMaximumWidgetHeight(QWidget* w1, QWidget* w2, QWidget* w3 = 0, QWidget* w4 = 0, QWidget* w5 = 0, QWidget* w6 = 0, QWidget* w7 = 0, QWidget* w8 = 0, QWidget* w9 = 0, QWidget* w10 = 0); static void matchWidgetHeights(QWidget* w1, QWidget* w2, QWidget* w3 = 0, QWidget* w4 = 0, QWidget* w5 = 0, QWidget* w6 = 0, QWidget* w7 = 0, QWidget* w8 = 0, QWidget* w9 = 0, QWidget* w10 = 0); static void matchWidgetWidths(QWidget* w1, QWidget* w2, QWidget* w3 = 0, QWidget* w4 = 0, QWidget* w5 = 0, QWidget* w6 = 0, QWidget* w7 = 0, QWidget* w8 = 0, QWidget* w9 = 0, QWidget* w10 = 0); static void setLayoutSpacingAndMargins(QLayout* layout, const int spacing, const int contentsMargin); static void setLayoutMargins(QLayout* layout, const int contentsMargin); static QSize estimateTableWidgetSize(QTableWidget* tableWidget); static QSize getMinimumScreenSize(); static bool isSmallDisplay(); static QString getLayoutContentDescription(QLayout* layout); static void playSound(const QString& soundFileName); static QString createWordWrappedToolTipText(const QString& tooltipText); static void setWordWrappedToolTip(QWidget* widget, const QString& tooltipText); static bool checkStateToBool(const Qt::CheckState checkState); static Qt::CheckState boolToCheckState(const bool value); private: WuQtUtilities(); ~WuQtUtilities(); WuQtUtilities(const WuQtUtilities&); WuQtUtilities& operator=(const WuQtUtilities&); }; } #endif // __WU_QT_UTILITIES_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/GuiQt/WuQwtPlot.cxx000066400000000000000000000042041300200146000242470ustar00rootroot00000000000000 /*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 __WU_QWT_PLOT_DECLARE__ #include "WuQwtPlot.h" #undef __WU_QWT_PLOT_DECLARE__ #include #include #include "qwt_plot_canvas.h" #include "CaretAssert.h" using namespace caret; /** * \class caret::WuQwtPlot * \brief Extends QwtPlot * \ingroup GuiQt * * Extends QwtPlot by adding a context menu with graph coordinates */ /** * Constructor. */ WuQwtPlot::WuQwtPlot(QWidget* w) : QwtPlot(w) { } /** * Destructor. */ WuQwtPlot::~WuQwtPlot() { } /** * Receives the context menu event. * * @param event * The context menu event. */ void WuQwtPlot::contextMenuEvent(QContextMenuEvent* event) { QPoint canvasPos = canvas()->mapFromGlobal(event->globalPos()); const QPointF plotPos = inverseTransform(canvasPos); emit contextMenuDisplay(event, plotPos.x(), plotPos.y()); } /*! Translate a point from pixel into plot coordinates \return Point in plot coordinates \sa transform() */ QPointF WuQwtPlot::inverseTransform( const QPoint &pos ) const { QwtScaleMap xMap = canvasMap( QwtPlot::xBottom ); QwtScaleMap yMap = canvasMap( QwtPlot::yLeft ); return QPointF( xMap.invTransform( pos.x() ), yMap.invTransform( pos.y() ) ); } connectome-workbench-1.2.3+git41-gc4c6c90/src/GuiQt/WuQwtPlot.h000066400000000000000000000033251300200146000236770ustar00rootroot00000000000000#ifndef __WU_QWT_PLOT_H__ #define __WU_QWT_PLOT_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 "qwt_plot.h" namespace caret { class WuQwtPlot : public QwtPlot { Q_OBJECT public: WuQwtPlot(QWidget* w = 0); virtual ~WuQwtPlot(); virtual void contextMenuEvent(QContextMenuEvent* event); // ADD_NEW_METHODS_HERE QPointF inverseTransform( const QPoint &pos ) const; signals: void contextMenuDisplay(QContextMenuEvent* event, float graphX, float graphY); private: WuQwtPlot(const WuQwtPlot&); WuQwtPlot& operator=(const WuQwtPlot&); // ADD_NEW_MEMBERS_HERE }; #ifdef __WU_QWT_PLOT_DECLARE__ // #endif // __WU_QWT_PLOT_DECLARE__ } // namespace #endif //__WU_QWT_PLOT_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/GuiQt/ZipSceneFileDialog.cxx000066400000000000000000000145771300200146000257770ustar00rootroot00000000000000 /*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 __ZIP_SCENE_FILE_DIALOG_DECLARE__ #include "ZipSceneFileDialog.h" #undef __ZIP_SCENE_FILE_DIALOG_DECLARE__ #include #include #include #include #include "BalsaDatabaseManager.h" #include "Brain.h" #include "CaretAssert.h" #include "CaretFileDialog.h" #include "CaretLogger.h" #include "CursorDisplayScoped.h" #include "FileInformation.h" #include "GuiManager.h" #include "SceneFile.h" #include "SystemUtilities.h" #include "WuQMessageBox.h" using namespace caret; /** * \class caret::ZipSceneFileDialog * \brief Dialog for zipping a scene file. * \ingroup GuiQt */ /** * Constructor. * * @param sceneFile * Scene file that will be uploaded. * @param parent * Parent of this dialog. */ ZipSceneFileDialog::ZipSceneFileDialog(const SceneFile* sceneFile, QWidget* parent) : WuQDialogModal("Zip Scene File", parent), m_sceneFile(sceneFile) { const int minimumLineEditWidth = 400; AString extractToDirectoryName("ext_dir"); AString zipFileName("file.zip"); FileInformation fileInfo(sceneFile->getFileName()); AString sceneFileDirectory = fileInfo.getAbsolutePath(); if ( ! sceneFileDirectory.isEmpty()) { QDir dir(sceneFileDirectory); if (dir.exists()) { const AString dirName = dir.dirName(); if ( ! dirName.isEmpty()) { if (dirName != ".") { extractToDirectoryName = dirName; } } } } else { sceneFileDirectory = GuiManager::get()->getBrain()->getCurrentDirectory(); } if ( ! sceneFileDirectory.isEmpty()) { zipFileName = FileInformation::assembleFileComponents(sceneFileDirectory, sceneFile->getFileNameNoPathNoExtension(), "zip"); } QLabel* zipFileNameLabel = new QLabel("Zip File Name"); m_zipFileNameLineEdit = new QLineEdit; m_zipFileNameLineEdit->setMinimumWidth(minimumLineEditWidth); m_zipFileNameLineEdit->setText(zipFileName); QPushButton* chooseZipFileButton = new QPushButton("Choose..."); QObject::connect(chooseZipFileButton, SIGNAL(clicked()), this, SLOT(chooseZipFileButtonClicked())); QLabel* extractDirectoryLabel = new QLabel("Extract to Directory"); m_extractDirectoryNameLineEdit = new QLineEdit(); m_extractDirectoryNameLineEdit->setMinimumWidth(minimumLineEditWidth); m_extractDirectoryNameLineEdit->setText(extractToDirectoryName); QWidget* dialogWidget = new QWidget(); QGridLayout* gridLayout = new QGridLayout(dialogWidget); gridLayout->setColumnStretch(0, 0); gridLayout->setColumnStretch(1, 100); gridLayout->setColumnStretch(2, 0); int row = 0; gridLayout->addWidget(zipFileNameLabel, row, 0); gridLayout->addWidget(m_zipFileNameLineEdit, row, 1); gridLayout->addWidget(chooseZipFileButton, row, 2); row++; gridLayout->addWidget(extractDirectoryLabel, row, 0); gridLayout->addWidget(m_extractDirectoryNameLineEdit, row, 1); row++; setCentralWidget(dialogWidget, WuQDialogModal::SCROLL_AREA_NEVER); } /** * Destructor. */ ZipSceneFileDialog::~ZipSceneFileDialog() { } /** * Choose the zip file name with a file browser dialog. */ void ZipSceneFileDialog::chooseZipFileButtonClicked() { /* * Let user choose a different path/name */ AString newZipFileName = CaretFileDialog::getSaveFileNameDialog(this, "Choose Zip File Name", m_zipFileNameLineEdit->text().trimmed(), "Zip File (*.zip)"); /* * If user cancels, delete the new scene file and return */ if (newZipFileName.isEmpty()) { return; } m_zipFileNameLineEdit->setText(newZipFileName); } /** * Gets called when the OK button is clicked. */ void ZipSceneFileDialog::okButtonClicked() { const AString zipFileName = m_zipFileNameLineEdit->text().trimmed(); const AString extractToDirectoryName = m_extractDirectoryNameLineEdit->text().trimmed(); AString errorMessage; if (zipFileName.isEmpty()) { errorMessage.appendWithNewLine("Zip file name is missing"); } if (extractToDirectoryName.isEmpty()) { errorMessage.appendWithNewLine("Extract to directory is missing."); } if (errorMessage.isEmpty()) { CursorDisplayScoped cursor; cursor.showWaitCursor(); const bool successFlag = BalsaDatabaseManager::zipSceneAndDataFiles(m_sceneFile, extractToDirectoryName, zipFileName, errorMessage); if ( ! successFlag) { if (errorMessage.isEmpty()) { errorMessage = "Zipping scene file failed with unknown error."; } } cursor.restoreCursor(); } if (errorMessage.isEmpty()) { WuQMessageBox::informationOk(this, "Zip file was successfully created"); } else { WuQMessageBox::errorOk(this, errorMessage); return; } WuQDialogModal::okButtonClicked(); } connectome-workbench-1.2.3+git41-gc4c6c90/src/GuiQt/ZipSceneFileDialog.h000066400000000000000000000036471300200146000254200ustar00rootroot00000000000000#ifndef __ZIP_SCENE_FILE_DIALOG_H__ #define __ZIP_SCENE_FILE_DIALOG_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 "WuQDialogModal.h" class QLineEdit; namespace caret { class SceneFile; class ZipSceneFileDialog : public WuQDialogModal { Q_OBJECT public: ZipSceneFileDialog(const SceneFile* sceneFile, QWidget* parent); virtual ~ZipSceneFileDialog(); // ADD_NEW_METHODS_HERE protected: virtual void okButtonClicked(); private slots: void chooseZipFileButtonClicked(); private: ZipSceneFileDialog(const ZipSceneFileDialog&); ZipSceneFileDialog& operator=(const ZipSceneFileDialog&); const SceneFile* m_sceneFile; QLineEdit* m_zipFileNameLineEdit; QLineEdit* m_extractDirectoryNameLineEdit; // ADD_NEW_MEMBERS_HERE }; #ifdef __ZIP_SCENE_FILE_DIALOG_DECLARE__ // #endif // __ZIP_SCENE_FILE_DIALOG_DECLARE__ } // namespace #endif //__ZIP_SCENE_FILE_DIALOG_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Nifti/000077500000000000000000000000001300200146000216155ustar00rootroot00000000000000connectome-workbench-1.2.3+git41-gc4c6c90/src/Nifti/CMakeLists.txt000066400000000000000000000010321300200146000243510ustar00rootroot00000000000000# # The NIFTI Project # project (Nifti) # # Need XML from Qt # SET(QT_DONT_USE_QTGUI) # # Add QT for includes # INCLUDE (${QT_USE_FILE}) # # Create the NIFTI library # ADD_LIBRARY(Nifti ControlPoint3D.h Matrix4x4.h NiftiHeader.h NiftiIO.h ControlPoint3D.cxx Matrix4x4.cxx NiftiHeader.cxx NiftiIO.cxx ) # # Find Headers # INCLUDE_DIRECTORIES( ${CMAKE_SOURCE_DIR}/Nifti ${CMAKE_SOURCE_DIR}/FilesBase ${CMAKE_SOURCE_DIR}/Common ${CMAKE_SOURCE_DIR}/Palette ${CMAKE_SOURCE_DIR}/Cifti ${CMAKE_SOURCE_DIR}/Scenes ${CMAKE_SOURCE_DIR}/Xml ) connectome-workbench-1.2.3+git41-gc4c6c90/src/Nifti/ControlPoint3D.cxx000066400000000000000000000266331300200146000251740ustar00rootroot00000000000000 /*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 __CONTROL_POINT3_D_DECLARE__ #include "ControlPoint3D.h" #undef __CONTROL_POINT3_D_DECLARE__ #include "CaretAssert.h" #include "CaretLogger.h" #include "MathFunctions.h" #include "SceneClass.h" #include "SceneClassAssistant.h" using namespace caret; /** * \class caret::ControlPoint3D * \brief 3D control point with source and target coordinates. * \ingroup Common */ /** * Constructor. * * @param sourceXYZ * The source coordinate. * @param targetXYZ * The target coordinate. */ ControlPoint3D::ControlPoint3D(const float sourceXYZ[3], const float targetXYZ[3]) : CaretObjectTracksModification(), SceneableInterface(), m_sourceX(sourceXYZ[0]), m_sourceY(sourceXYZ[1]), m_sourceZ(sourceXYZ[2]), m_targetX(targetXYZ[0]), m_targetY(targetXYZ[1]), m_targetZ(targetXYZ[2]), m_transformedX(0.0), m_transformedY(0.0), m_transformedZ(0.0) { initializeInstance(); setModified(); } /** * Constructor. * * @param sourceX * The source X-coordinate. * @param sourceY * The source Y-coordinate. * @param sourceZ * The source Z-coordinate. * @param targetX * The target X-coordinate. * @param targetY * The target Y-coordinate. * @param targetZ * The target Z-coordinate. */ ControlPoint3D::ControlPoint3D(const float sourceX, const float sourceY, const float sourceZ, const float targetX, const float targetY, const float targetZ) : CaretObjectTracksModification(), SceneableInterface(), m_sourceX(sourceX), m_sourceY(sourceY), m_sourceZ(sourceZ), m_targetX(targetX), m_targetY(targetY), m_targetZ(targetZ), m_transformedX(0.0), m_transformedY(0.0), m_transformedZ(0.0) { initializeInstance(); setModified(); } /** * Destructor. */ ControlPoint3D::~ControlPoint3D() { delete m_sceneAssistant; } /** * Copy constructor. * @param obj * Object that is copied. */ ControlPoint3D::ControlPoint3D(const ControlPoint3D& obj) : CaretObjectTracksModification(obj), SceneableInterface(obj) { initializeInstance(); this->copyHelperControlPoint3D(obj); } /** * Assignment operator. * @param obj * Data copied from obj to this. * @return * Reference to this object. */ ControlPoint3D& ControlPoint3D::operator=(const ControlPoint3D& obj) { if (this != &obj) { CaretObject::operator=(obj); this->copyHelperControlPoint3D(obj); } return *this; } /** * Helps with copying an object of this type. * @param obj * Object that is copied. */ void ControlPoint3D::copyHelperControlPoint3D(const ControlPoint3D& obj) { m_sourceX = obj.m_sourceX; m_sourceY = obj.m_sourceY; m_sourceZ = obj.m_sourceZ; m_targetX = obj.m_targetX; m_targetY = obj.m_targetY; m_targetZ = obj.m_targetZ; m_transformedX = obj.m_transformedX; m_transformedY = obj.m_transformedY; m_transformedZ = obj.m_transformedZ; setModified(); } /** * Initialize an instance of a control point */ void ControlPoint3D::initializeInstance() { m_sceneAssistant = new SceneClassAssistant(); m_sceneAssistant->add("m_sourceX", &m_sourceX); m_sceneAssistant->add("m_sourceY", &m_sourceY); m_sceneAssistant->add("m_sourceZ", &m_sourceZ); m_sceneAssistant->add("m_targetX", &m_targetX); m_sceneAssistant->add("m_targetY", &m_targetY); m_sceneAssistant->add("m_targetZ", &m_targetZ); m_sceneAssistant->add("m_transformedX", &m_transformedX); m_sceneAssistant->add("m_transformedY", &m_transformedY); m_sceneAssistant->add("m_transformedZ", &m_transformedZ); } /** * Get the source coordinate. * * @param pt * Output with source coordinate. */ void ControlPoint3D::getSourceXYZ(double pt[3]) const { pt[0] = m_sourceX; pt[1] = m_sourceY; pt[2] = m_sourceZ; } /** * Get the target coordinate. * * @param pt * Output with target coordinate. */ void ControlPoint3D::getTargetXYZ(double pt[3]) const { pt[0] = m_targetX; pt[1] = m_targetY; pt[2] = m_targetZ; } /** * Get the transformed coordinate that is * source coordinate multiplied by * the landmard transformation matrix. * Can be compared with target coordinate * for error measurement. * * @param pt * Output with transformed coordinate. */ void ControlPoint3D::getTransformedXYZ(double pt[3]) const { pt[0] = m_transformedX; pt[1] = m_transformedY; pt[2] = m_transformedZ; } /** * Set the transformed coordinate that is * source coordinate multiplied by * the landmard transformation matrix. * Can be compared with target coordinate * for error measurement. * * @param pt * Output with transformed coordinate. */ void ControlPoint3D::setTransformedXYZ(const double pt[3]) { m_transformedX = pt[0]; m_transformedY = pt[1]; m_transformedZ = pt[2]; } /** * Get the transformed coordinate that is * source coordinate multiplied by * the landmard transformation matrix. * Can be compared with target coordinate * for error measurement. * * @param pt * Output with transformed coordinate. */ void ControlPoint3D::getTransformedXYZ(float pt[3]) const { pt[0] = m_transformedX; pt[1] = m_transformedY; pt[2] = m_transformedZ; } /** * Set the transformed coordinate that is * source coordinate multiplied by * the landmard transformation matrix. * Can be compared with target coordinate * for error measurement. * * @param pt * Output with transformed coordinate. */ void ControlPoint3D::setTransformedXYZ(const float pt[3]) { m_transformedX = pt[0]; m_transformedY = pt[1]; m_transformedZ = pt[2]; } /** * Get the source coordinate. * * @param pt * Output with source coordinate. */ void ControlPoint3D::getSourceXYZ(float pt[3]) const { pt[0] = m_sourceX; pt[1] = m_sourceY; pt[2] = m_sourceZ; } /** * Get the target coordinate. * * @param pt * Output with target coordinate. */ void ControlPoint3D::getTargetXYZ(float pt[3]) const { pt[0] = m_targetX; pt[1] = m_targetY; pt[2] = m_targetZ; } /** * @return The source X-coordinate */ float ControlPoint3D::getSourceX() const { return m_sourceX; } /** * @return The source Y-coordinate */ float ControlPoint3D::getSourceY() const { return m_sourceY; } /** * @return The source Z-coordinate */ float ControlPoint3D::getSourceZ() const { return m_sourceZ; } /** * @return The target X-coordinate */ float ControlPoint3D::getTargetX() const { return m_targetX; } /** * @return The target Y-coordinate */ float ControlPoint3D::getTargetY() const { return m_targetY; } /** @return * The target Z-coordinate */ float ControlPoint3D::getTargetZ() const { return m_targetZ; } /** * Get the error measurements. Error is difference * between target and transformed coordinates. * * @param xyzTotalErrorOut * 4 elements error in x, y, z, and total error. */ void ControlPoint3D::getErrorMeasurements(float xyzTotalErrorOut[4]) const { xyzTotalErrorOut[0] = std::fabs(m_targetX - m_transformedX); xyzTotalErrorOut[1] = std::fabs(m_targetY - m_transformedY); xyzTotalErrorOut[2] = std::fabs(m_targetZ - m_transformedZ); xyzTotalErrorOut[3] = std::sqrt((xyzTotalErrorOut[0] * xyzTotalErrorOut[0]) + (xyzTotalErrorOut[1] * xyzTotalErrorOut[1]) + (xyzTotalErrorOut[2] * xyzTotalErrorOut[2])); } /** * @return String containing control point coordinates. */ AString ControlPoint3D::toString() const { const AString s("Source: (" + AString::number(m_sourceX) + ", " + AString::number(m_sourceY) + ", " + AString::number(m_sourceZ) + ") Target: (" + AString::number(m_targetX) + ", " + AString::number(m_targetY) + ", " + AString::number(m_targetZ) + ") Transformed: (" + AString::number(m_transformedX) + ", " + AString::number(m_transformedY) + ", " + AString::number(m_transformedZ) + ")"); return s; } /** * Get the normal vector for the source coordinates of the first three * control points. If there are less than three control points, * a unit vector is returned. * * @param controlPoints * The control points. * @param sourceNormalVectorOut * Output unit normal vector for source points. */ void ControlPoint3D::getSourceNormalVector(const std::vector& controlPoints, float sourceNormalVectorOut[3]) { if (controlPoints.size() < 3) { sourceNormalVectorOut[0] = 0.0; sourceNormalVectorOut[1] = 0.0; sourceNormalVectorOut[2] = 1.0; CaretLogSevere("Cannot compute normal vector for fewer than three control points."); } CaretAssertVectorIndex(controlPoints, 2); float s1[3]; controlPoints[0].getSourceXYZ(s1); float s2[3]; controlPoints[1].getSourceXYZ(s2); float s3[3]; controlPoints[2].getSourceXYZ(s3); MathFunctions::normalVector(s1, s2, s3, sourceNormalVectorOut); } /** * 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* ControlPoint3D::saveToScene(const SceneAttributes* sceneAttributes, const AString& instanceName) { SceneClass* sceneClass = new SceneClass(instanceName, "ControlPoint", 1); m_sceneAssistant->saveMembers(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 ControlPoint3D::restoreFromScene(const SceneAttributes* sceneAttributes, const SceneClass* sceneClass) { if (sceneClass == NULL) { return; } m_sceneAssistant->restoreMembers(sceneAttributes, sceneClass); } connectome-workbench-1.2.3+git41-gc4c6c90/src/Nifti/ControlPoint3D.h000066400000000000000000000073041300200146000246130ustar00rootroot00000000000000#ifndef __CONTROL_POINT3_D_H__ #define __CONTROL_POINT3_D_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 "CaretObjectTracksModification.h" #include "SceneableInterface.h" namespace caret { class SceneClassAssistant; class ControlPoint3D : public CaretObjectTracksModification, public SceneableInterface { public: ControlPoint3D(const float sourceXYZ[3], const float targetXYZ[3]); ControlPoint3D(const float sourceX, const float sourceY, const float sourceZ, const float targetX, const float targetY, const float targetZ); virtual ~ControlPoint3D(); ControlPoint3D(const ControlPoint3D& obj); ControlPoint3D& operator=(const ControlPoint3D& obj); void getSourceXYZ(double pt[3]) const; void getTargetXYZ(double pt[3]) const; void getTransformedXYZ(double pt[3]) const; void setTransformedXYZ(const double pt[3]); void getSourceXYZ(float pt[3]) const; void getTargetXYZ(float pt[3]) const; void getTransformedXYZ(float pt[3]) const; void setTransformedXYZ(const float pt[3]); float getSourceX() const; float getSourceY() const; float getSourceZ() const; float getTargetX() const; float getTargetY() const; float getTargetZ() const; void getErrorMeasurements(float xyzTotalErrorOut[4]) const; virtual AString toString() const; static void getSourceNormalVector(const std::vector& controlPoints, float sourceNormalVectorOut[3]); virtual SceneClass* saveToScene(const SceneAttributes* sceneAttributes, const AString& instanceName); virtual void restoreFromScene(const SceneAttributes* sceneAttributes, const SceneClass* sceneClass); // ADD_NEW_METHODS_HERE private: void copyHelperControlPoint3D(const ControlPoint3D& obj); void initializeInstance(); // ADD_NEW_MEMBERS_HERE SceneClassAssistant* m_sceneAssistant; float m_sourceX; float m_sourceY; float m_sourceZ; float m_targetX; float m_targetY; float m_targetZ; float m_transformedX; float m_transformedY; float m_transformedZ; }; #ifdef __CONTROL_POINT3_D_DECLARE__ // #endif // __CONTROL_POINT3_D_DECLARE__ } // namespace #endif //__CONTROL_POINT3_D_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Nifti/Matrix4x4.cxx000066400000000000000000002104021300200146000241440ustar00rootroot00000000000000/*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*/ /*========================================================================= Program: Visualization Toolkit Module: $RCSfile: vtkBase64Utilities.h,v $ Copyright (c) Ken Martin, Will Schroeder, Bill Lorensen All rights reserved. See Copyright.txt or http://www.kitware.com/Copyright.htm for details. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the above copyright notice for more information. Program: Visualization Toolkit Module: $RCSfile: vtkMatrix4x4.cxx,v $ Copyright (c) Ken Martin, Will Schroeder, Bill Lorensen All rights reserved. See Copyright.txt or http://www.kitware.com/Copyright.htm for details. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the above copyright notice for more information. =========================================================================*/ #include #include #include #include "CaretAssert.h" #include "CaretLogger.h" #include "ControlPoint3D.h" #include "MathFunctions.h" #include "Matrix4x4.h" #include "NiftiEnums.h" #include "XmlWriter.h" using namespace caret; //static const bool INFO = false; //static const bool DEBUG = false; //static const float iDF = 1.0f; static const double SMALL_POSITIVE_NUMBER = 0.000001; static const double SMALL_NEGATIVE_NUMBER = -0.000001; /** * * constructor that creates an identity matrix. * */ Matrix4x4::Matrix4x4() : CaretObject() { this->initializeMembersMatrix4x4(); } /** * Destructor */ Matrix4x4::~Matrix4x4() { } /** * Copy Constructor * @param Object that is copied. */ Matrix4x4::Matrix4x4(const Matrix4x4& o) : CaretObject(o) { this->initializeMembersMatrix4x4(); this->copyHelper(o); } /** * Assignment operator. */ Matrix4x4& Matrix4x4::operator=(const Matrix4x4& o) { if (this != &o) { CaretObject::operator=(o); this->copyHelper(o); }; return *this; } /** * Helps with copy constructor and assignment operator. */ void Matrix4x4::copyHelper(const Matrix4x4& o) { this->setMatrix(o); this->dataSpaceName = o.dataSpaceName; this->transformedSpaceName = o.transformedSpaceName; } void Matrix4x4::initializeMembersMatrix4x4() { this->identity(); this->dataSpaceName = NiftiTransformEnum::toName(NiftiTransformEnum::NIFTI_XFORM_TALAIRACH); this->transformedSpaceName = NiftiTransformEnum::toName(NiftiTransformEnum::NIFTI_XFORM_TALAIRACH); this->clearModified(); } /** * Set the matrix to the identity matrix. * */ void Matrix4x4::identity() { matrix[0][0] = 1.0; matrix[0][1] = 0.0; matrix[0][2] = 0.0; matrix[0][3] = 0.0; matrix[1][0] = 0.0; matrix[1][1] = 1.0; matrix[1][2] = 0.0; matrix[1][3] = 0.0; matrix[2][0] = 0.0; matrix[2][1] = 0.0; matrix[2][2] = 1.0; matrix[2][3] = 0.0; matrix[3][0] = 0.0; matrix[3][1] = 0.0; matrix[3][2] = 0.0; matrix[3][3] = 1.0; this->setModified(); } /** * Get the translation from the matrix. * * param The translation as an array of three floats. * */ void Matrix4x4::getTranslation(float translatationOut[3]) const { translatationOut[0] = matrix[0][3]; translatationOut[1] = matrix[1][3]; translatationOut[2] = matrix[2][3]; } /** * Set (replace) the matrix's translation. * * @param t An array of three float containing the translation. * */ void Matrix4x4::setTranslation(const float t[3]) { matrix[0][3] = t[0]; matrix[1][3] = t[1]; matrix[2][3] = t[2]; this->setModified(); } /** * Set (replace) the matrix's translation. * * @param tx The translation along the X-Axis. * @param ty The translation along the Y-Axis. * @param tz The translation along the Z-Axis. * */ void Matrix4x4::setTranslation( const double tx, const double ty, const double tz) { matrix[0][3] = tx; matrix[1][3] = ty; matrix[2][3] = tz; this->setModified(); } /** * * Apply a translation by multiplying the matrix by a matrix * containing the specified translation. Translates in the * screen' coordinate system. * * @param tx The translation along the X-Axis. * @param ty The translation along the Y-Axis. * @param tz The translation along the Z-Axis. * */ void Matrix4x4::translate( const double tx, const double ty, const double tz) { Matrix4x4 cm; cm.setTranslation(tx, ty, tz); postmultiply(cm); this->setModified(); } /** * * Apply a translation by multiplying the matrix by a matrix * containing the specified translation. Translates in the * screen' coordinate system. * * @param txyz The translation along the XYZ-Axis. */ void Matrix4x4::translate(const double txyz[3]) { translate(txyz[0], txyz[1], txyz[2]); } /** * Apply scaling by multiplying the matrix by a matrix * containing the specified scaling. Translates in the * screen' coordinate system. * * @param sx The scaling along the X-Axis. * @param sy The scaling along the Y-Axis. * @param sz The scaling along the Z-Axis. * */ void Matrix4x4::scale( const double sx, const double sy, const double sz) { Matrix4x4 cm; cm.matrix[0][0] = sx; cm.matrix[1][1] = sy; cm.matrix[2][2] = sz; postmultiply(cm); this->setModified(); } /** * Get the scaling from the matrix. * @param scaleOutX * X scaling output. * @param scaleOutY * Y scaling output. * @param scaleOutZ * Z scaling output. */ void Matrix4x4::getScale(double& scaleOutX, double& scaleOutY, double& scaleOutZ) const { double U[3][3], VT[3][3]; for (int i = 0; i < 3; i++) { U[0][i] = matrix[0][i]; U[1][i] = matrix[1][i]; U[2][i] = matrix[2][i]; } double scale[3]; Matrix4x4::SingularValueDecomposition3x3(U, U, scale, VT); scaleOutX = scale[0]; scaleOutY = scale[1]; scaleOutZ = scale[2]; } //---------------------------------------------------------------------------- // Perform singular value decomposition on the matrix A: // A = U * W * VT // where U and VT are orthogonal W is diagonal (the diagonal elements // are returned in vector w). // The matrices U and VT will both have positive determinants. // The scale factors w are ordered according to how well the // corresponding eigenvectors (in VT) match the x, y and z axes // respectively. // // The singular value decomposition is used to decompose a linear // transformation into a rotation, followed by a scale, followed // by a second rotation. The scale factors w will be negative if // the determinant of matrix A is negative. // // Contributed by David Gobbi (dgobbi@irus.rri.on.ca) void Matrix4x4::SingularValueDecomposition3x3(const double A[3][3], double U[3][3], double w[3], double VT[3][3]) { int i; double B[3][3]; // copy so that A can be used for U or VT without risk for (i = 0; i < 3; i++) { B[0][i] = A[0][i]; B[1][i] = A[1][i]; B[2][i] = A[2][i]; } // temporarily flip if determinant is negative double d = Determinant3x3(B); if (d < 0) { for (i = 0; i < 3; i++) { B[0][i] = -B[0][i]; B[1][i] = -B[1][i]; B[2][i] = -B[2][i]; } } // orthogonalize, diagonalize, etc. Orthogonalize3x3(B, U); Transpose3x3(B, B); Multiply3x3(B, U, VT); Diagonalize3x3(VT, w, VT); Multiply3x3(U, VT, U); Transpose3x3(VT, VT); // re-create the flip if (d < 0) { w[0] = -w[0]; w[1] = -w[1]; w[2] = -w[2]; } /* paranoia check: recombine to ensure that the SVD is correct vtkMath::Transpose3x3(B, B); if (d < 0) { for (i = 0; i < 3; i++) { B[0][i] = -B[0][i]; B[1][i] = -B[1][i]; B[2][i] = -B[2][i]; } } int j; T2 maxerr = 0; T2 tmp; T2 M[3][3]; T2 W[3][3]; vtkMath::Identity3x3(W); W[0][0] = w[0]; W[1][1] = w[1]; W[2][2] = w[2]; vtkMath::Identity3x3(M); vtkMath::Multiply3x3(M, U, M); vtkMath::Multiply3x3(M, W, M); vtkMath::Multiply3x3(M, VT, M); for (i = 0; i < 3; i++) { for (j = 0; j < 3; j++) { if ((tmp = fabs(B[i][j] - M[i][j])) > maxerr) { maxerr = tmp; } } } vtkGenericWarningMacro("SingularValueDecomposition max error = " << maxerr); */ } void Matrix4x4::Diagonalize3x3(const double A[3][3], double w[3], double V[3][3]) { int i,j,k,maxI; double tmp, maxVal; // do the matrix[3][3] to **matrix conversion for Jacobi double C[3][3]; double *ATemp[3],*VTemp[3]; for (i = 0; i < 3; i++) { C[i][0] = A[i][0]; C[i][1] = A[i][1]; C[i][2] = A[i][2]; ATemp[i] = C[i]; VTemp[i] = V[i]; } // diagonalize using Jacobi Matrix4x4::JacobiN(ATemp,3,w,VTemp); // if all the eigenvalues are the same, return identity matrix if (w[0] == w[1] && w[0] == w[2]) { for (int i = 0; i < 3; i++) { for (int j = 0; j < 3; j++) { V[i][j] = 0.0; } } return; } // transpose temporarily, it makes it easier to sort the eigenvectors Transpose3x3(V,V); // if two eigenvalues are the same, re-orthogonalize to optimally line // up the eigenvectors with the x, y, and z axes for (i = 0; i < 3; i++) { if (w[(i+1)%3] == w[(i+2)%3]) // two eigenvalues are the same { // find maximum element of the independant eigenvector maxVal = fabs(V[i][0]); maxI = 0; for (j = 1; j < 3; j++) { if (maxVal < (tmp = fabs(V[i][j]))) { maxVal = tmp; maxI = j; } } // swap the eigenvector into its proper position if (maxI != i) { tmp = w[maxI]; w[maxI] = w[i]; w[i] = tmp; SwapVectors3(V[i],V[maxI]); } // maximum element of eigenvector should be positive if (V[maxI][maxI] < 0) { V[maxI][0] = -V[maxI][0]; V[maxI][1] = -V[maxI][1]; V[maxI][2] = -V[maxI][2]; } // re-orthogonalize the other two eigenvectors j = (maxI+1)%3; k = (maxI+2)%3; V[j][0] = 0.0; V[j][1] = 0.0; V[j][2] = 0.0; V[j][j] = 1.0; MathFunctions::crossProduct(V[maxI],V[j],V[k]); MathFunctions::normalizeVector(V[k]); MathFunctions::crossProduct(V[k],V[maxI],V[j]); // transpose vectors back to columns Transpose3x3(V,V); return; } } // the three eigenvalues are different, just sort the eigenvectors // to align them with the x, y, and z axes // find the vector with the largest x element, make that vector // the first vector maxVal = fabs(V[0][0]); maxI = 0; for (i = 1; i < 3; i++) { if (maxVal < (tmp = fabs(V[i][0]))) { maxVal = tmp; maxI = i; } } // swap eigenvalue and eigenvector if (maxI != 0) { tmp = w[maxI]; w[maxI] = w[0]; w[0] = tmp; SwapVectors3(V[maxI],V[0]); } // do the same for the y element if (fabs(V[1][1]) < fabs(V[2][1])) { tmp = w[2]; w[2] = w[1]; w[1] = tmp; SwapVectors3(V[2],V[1]); } // ensure that the sign of the eigenvectors is correct for (i = 0; i < 2; i++) { if (V[i][i] < 0) { V[i][0] = -V[i][0]; V[i][1] = -V[i][1]; V[i][2] = -V[i][2]; } } // set sign of final eigenvector to ensure that determinant is positive if (Determinant3x3(V) < 0) { V[2][0] = -V[2][0]; V[2][1] = -V[2][1]; V[2][2] = -V[2][2]; } // transpose the eigenvectors back again Transpose3x3(V,V); } #define VTK_ROTATE(a,i,j,k,l) g=a[i][j];h=a[k][l];a[i][j]=g-s*(h+g*tau);\ a[k][l]=h+s*(g-h*tau) // Jacobi iteration for the solution of eigenvectors/eigenvalues of a nxn // real symmetric matrix. Square nxn matrix a; size of matrix in n; // output eigenvalues in w; and output eigenvectors in v. Resulting // eigenvalues/vectors are sorted in decreasing order; eigenvectors are // normalized. int Matrix4x4::JacobiN(double **a, int n, double *w, double **v) { const int VTK_MAX_ROTATIONS = 20; int i, j, k, iq, ip, numPos; double tresh, theta, tau, t, sm, s, h, g, c, tmp; double bspace[4], zspace[4]; double *b = bspace; double *z = zspace; // only allocate memory if the matrix is large if (n > 4) { b = new double[n]; z = new double[n]; } // initialize for (ip=0; ip 3 && (fabs(w[ip])+g) == fabs(w[ip]) && (fabs(w[iq])+g) == fabs(w[iq])) { a[ip][iq] = 0.0; } else if (fabs(a[ip][iq]) > tresh) { h = w[iq] - w[ip]; if ( (fabs(h)+g) == fabs(h)) { t = (a[ip][iq]) / h; } else { theta = 0.5*h / (a[ip][iq]); t = 1.0 / (fabs(theta)+sqrt(1.0+theta*theta)); if (theta < 0.0) { t = -t; } } c = 1.0 / sqrt(1+t*t); s = t*c; tau = s/(1.0+c); h = t*a[ip][iq]; z[ip] -= h; z[iq] += h; w[ip] -= h; w[iq] += h; a[ip][iq]=0.0; // ip already shifted left by 1 unit for (j = 0;j <= ip-1;j++) { VTK_ROTATE(a,j,ip,j,iq); } // ip and iq already shifted left by 1 unit for (j = ip+1;j <= iq-1;j++) { VTK_ROTATE(a,ip,j,j,iq); } // iq already shifted left by 1 unit for (j=iq+1; j= VTK_MAX_ROTATIONS ) { CaretLogWarning("Matrix4x4::Jacobi: Error extracting eigenfunctions"); return 0; } // sort eigenfunctions these changes do not affect accuracy for (j=0; j= tmp) // why exchage if same? { k = i; tmp = w[k]; } } if (k != j) { w[k] = w[j]; w[j] = tmp; for (i=0; i> 1) + (n & 1); for (j=0; j= 0.0 ) { numPos++; } } // if ( numPos < ceil(double(n)/double(2.0)) ) if ( numPos < ceil_half_n) { for(i=0; i 4) { delete [] b; delete [] z; } return 1; } #undef VTK_ROTATE void Matrix4x4::Multiply3x3(const double A[3][3], const double B[3][3], double C[3][3]) { double D[3][3]; for (int i = 0; i < 3; i++) { D[0][i] = A[0][0]*B[0][i] + A[0][1]*B[1][i] + A[0][2]*B[2][i]; D[1][i] = A[1][0]*B[0][i] + A[1][1]*B[1][i] + A[1][2]*B[2][i]; D[2][i] = A[2][0]*B[0][i] + A[2][1]*B[1][i] + A[2][2]*B[2][i]; } for (int j = 0; j < 3; j++) { C[j][0] = D[j][0]; C[j][1] = D[j][1]; C[j][2] = D[j][2]; } } //---------------------------------------------------------------------------- void Matrix4x4::Transpose3x3(const double A[3][3], double AT[3][3]) { double tmp; tmp = A[1][0]; AT[1][0] = A[0][1]; AT[0][1] = tmp; tmp = A[2][0]; AT[2][0] = A[0][2]; AT[0][2] = tmp; tmp = A[2][1]; AT[2][1] = A[1][2]; AT[1][2] = tmp; AT[0][0] = A[0][0]; AT[1][1] = A[1][1]; AT[2][2] = A[2][2]; } /** * Apply a rotation about the X-axis. Rotates in the * screen's coordinate system. * * @param degrees Amount to rotate in degrees. * */ void Matrix4x4::rotateX(const double degrees) { rotate(degrees, 1.0, 0.0, 0.0); } /** * Apply a rotation about the Y-axis. Rotates in the * screen's coordinate system. * * @param degrees Amount to rotate in degrees. * */ void Matrix4x4::rotateY(const double degrees) { rotate(degrees, 0.0, 1.0, 0.0); } /** * Apply a rotation about the Z-axis. Rotates in the * screen's coordinate system. * * @param degrees Amount to rotate in degrees. * */ void Matrix4x4::rotateZ(const double degrees) { rotate(degrees, 0.0, 0.0, 1.0); } /** * Rotate angle degrees about the vector. * @param angle - Angle of rotation. * @param vector - Vector about which rotation occurs. * */ void Matrix4x4::rotate( const double angle, const double vector[4]) { this->rotate(angle, vector[0], vector[1], vector[2]); this->setModified(); } /** * Rotate angle degrees about the vector (x,y,z). * * @param angle Amount to rotate in degrees. * @param xin X-component of the vector. * @param yin Y-component of the vector. * @param zin Z-component of the vector. * */ void Matrix4x4::rotate( const double angleIn, const double xin, const double yin, const double zin) { /// from vtkTransformConcatenation::Rotate() float angle = angleIn; if (angle == 0.0 || (xin == 0.0 && yin == 0.0 && zin == 0.0)) { return; } // convert to radians angle = MathFunctions::toRadians(angle); // make a normalized quaternion double w = std::cos(0.5*angle); double f = std::sin(0.5*angle) / std::sqrt(xin*xin+yin*yin+zin*zin); double x = xin * f; double y = yin * f; double z = zin * f; double ww = w*w; double wx = w*x; double wy = w*y; double wz = w*z; double xx = x*x; double yy = y*y; double zz = z*z; double xy = x*y; double xz = x*z; double yz = y*z; double s = ww - xx - yy - zz; // convert the quaternion to a matrix Matrix4x4 m; m.matrix[0][0] = xx*2.0 + s; m.matrix[1][0] = (xy + wz)*2; m.matrix[2][0] = (xz - wy)*2; m.matrix[0][1] = (xy - wz)*2; m.matrix[1][1] = yy*2.0 + s; m.matrix[2][1] = (yz + wx)*2; 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(); } /* * Set the rotation matrix using the given angles. * WARNING: Any scaling or translation is will be removed!!! * * @rotationX * The X-rotation angle. * @rotationY * The Y-rotation angle. * @rotationZ * The Z-rotation angle. */ void Matrix4x4::setRotation(const double rotationX, const double rotationY, const double rotationZ) { identity(); rotateY(rotationY); rotateX(rotationX); rotateZ(rotationZ); } /* * Get the rotation angles from the matrix. * * From vktTransform::GetOrientation() * * @rotationOutX * Output containing X-rotation from matrix. * @rotationOutY * Output containing X-rotation from matrix. * @rotationOutZ * Output containing X-rotation from matrix. */ void Matrix4x4::getRotation(double& rotationOutX, double& rotationOutY, double& rotationOutZ) const { #define VTK_AXIS_EPSILON 0.001 int i; // convenient access to matrix // double (*matrix)[4] = amatrix->Element; double ortho[3][3]; for (i = 0; i < 3; i++) { ortho[0][i] = matrix[0][i]; ortho[1][i] = matrix[1][i]; ortho[2][i] = matrix[2][i]; } if (Determinant3x3(ortho) < 0) { ortho[0][2] = -ortho[0][2]; ortho[1][2] = -ortho[1][2]; ortho[2][2] = -ortho[2][2]; } Matrix4x4::Orthogonalize3x3(ortho, ortho); // first rotate about y axis double x2 = ortho[2][0]; double y2 = ortho[2][1]; double z2 = ortho[2][2]; double x3 = ortho[1][0]; double y3 = ortho[1][1]; double z3 = ortho[1][2]; double d1 = sqrt(x2*x2 + z2*z2); double cosTheta, sinTheta; if (d1 < VTK_AXIS_EPSILON) { cosTheta = 1.0; sinTheta = 0.0; } else { cosTheta = z2/d1; sinTheta = x2/d1; } double theta = std::atan2(sinTheta, cosTheta); rotationOutY = - MathFunctions::toDegrees(theta ); // now rotate about x axis double d = std::sqrt(x2*x2 + y2*y2 + z2*z2); double sinPhi, cosPhi; if (d < VTK_AXIS_EPSILON) { sinPhi = 0.0; cosPhi = 1.0; } else if (d1 < VTK_AXIS_EPSILON) { sinPhi = y2/d; cosPhi = z2/d; } else { sinPhi = y2/d; cosPhi = (x2*x2 + z2*z2)/(d1*d); } double phi = std::atan2(sinPhi, cosPhi); rotationOutX = MathFunctions::toDegrees(phi); // finally, rotate about z double x3p = x3*cosTheta - z3*sinTheta; double y3p = - sinPhi*sinTheta*x3 + cosPhi*y3 - sinPhi*cosTheta*z3; double d2 = std::sqrt(x3p*x3p + y3p*y3p); double cosAlpha, sinAlpha; if (d2 < VTK_AXIS_EPSILON) { cosAlpha = 1.0; sinAlpha = 0.0; } else { cosAlpha = y3p/d2; sinAlpha = x3p/d2; } double alpha = std::atan2(sinAlpha, cosAlpha); rotationOutZ = MathFunctions::toDegrees(alpha); if (MathFunctions::isNaN(rotationOutX)) { rotationOutX = 0.0; } if (MathFunctions::isNaN(rotationOutY)) { rotationOutY = 0.0; } if (MathFunctions::isNaN(rotationOutZ)) { rotationOutZ = 0.0; } } void Matrix4x4::Orthogonalize3x3(const double A[3][3], double B[3][3]) { int i; // copy the matrix for (i = 0; i < 3; i++) { B[0][i] = A[0][i]; B[1][i] = A[1][i]; B[2][i] = A[2][i]; } // Pivot the matrix to improve accuracy double scale[3]; int index[3]; double tmp, largest; // Loop over rows to get implicit scaling information for (i = 0; i < 3; i++) { largest = fabs(B[i][0]); if ((tmp = fabs(B[i][1])) > largest) { largest = tmp; } if ((tmp = fabs(B[i][2])) > largest) { largest = tmp; } scale[i] = 1.0; if (largest != 0) { scale[i] = double(1.0)/largest; } } // first column index[0] = 0; largest = scale[0]*fabs(B[0][0]); if ((tmp = scale[1]*fabs(B[1][0])) >= largest) { largest = tmp; index[0] = 1; } if ((tmp = scale[2]*fabs(B[2][0])) >= largest) { index[0] = 2; } if (index[0] != 0) { SwapVectors3(B[index[0]],B[0]); scale[index[0]] = scale[0]; } // second column index[1] = 1; largest = scale[1]*fabs(B[1][1]); if ((tmp = scale[2]*fabs(B[2][1])) >= largest) { index[1] = 2; SwapVectors3(B[2],B[1]); } // third column index[2] = 2; // A quaternian can only describe a pure rotation, not // a rotation with a flip, therefore the flip must be // removed before the matrix is converted to a quaternion. double d = Matrix4x4::Determinant3x3(B); if (d < 0) { for (i = 0; i < 3; i++) { B[0][i] = -B[0][i]; B[1][i] = -B[1][i]; B[2][i] = -B[2][i]; } } // Do orthogonalization using a quaternion intermediate // (this, essentially, does the orthogonalization via // diagonalization of an appropriately constructed symmetric // 4x4 matrix rather than by doing SVD of the 3x3 matrix) double quat[4]; MathFunctions::matrixToQuatern(B,quat); MathFunctions::quaternToMatrix(quat,B); // Put the flip back into the orthogonalized matrix. if (d < 0) { for (i = 0; i < 3; i++) { B[0][i] = -B[0][i]; B[1][i] = -B[1][i]; B[2][i] = -B[2][i]; } } // Undo the pivoting if (index[1] != 1) { SwapVectors3(B[index[1]],B[1]); } if (index[0] != 0) { SwapVectors3(B[index[0]],B[0]); } } /** * Swap elements in vectors. * @param v1 * First vector. * @param v2 * Second vector. */ void Matrix4x4::SwapVectors3(double v1[3], double v2[3]) { double x[3] = { v1[0], v1[1], v1[2] }; v1[0] = v2[0]; v1[1] = v2[1]; v1[2] = v2[2]; v2[0] = x[0]; v2[1] = x[1]; v2[2] = x[2]; } /** * Premultiply by a matrix. * * @param tm Matrix that is used for pre-multiplication. * */ void Matrix4x4::premultiply(const Matrix4x4& tm) { double matrixOut[4][4]; for (int row = 0; row < 4; row++) { matrixOut[row][0] = matrix[row][0] * tm.matrix[0][0] + matrix[row][1] * tm.matrix[1][0] + matrix[row][2] * tm.matrix[2][0] + matrix[row][3] * tm.matrix[3][0]; matrixOut[row][1] = matrix[row][0] * tm.matrix[0][1] + matrix[row][1] * tm.matrix[1][1] + matrix[row][2] * tm.matrix[2][1] + matrix[row][3] * tm.matrix[3][1]; matrixOut[row][2] = matrix[row][0] * tm.matrix[0][2] + matrix[row][1] * tm.matrix[1][2] + matrix[row][2] * tm.matrix[2][2] + matrix[row][3] * tm.matrix[3][2]; matrixOut[row][3] = matrix[row][0] * tm.matrix[0][3] + matrix[row][1] * tm.matrix[1][3] + matrix[row][2] * tm.matrix[2][3] + matrix[row][3] * tm.matrix[3][3]; } setMatrix(matrixOut); this->fixNumericalError(); this->setModified(); } /** * Postmultiply by a matrix. * * @param tm Matrix that is used for post-multiplication. * */ void Matrix4x4::postmultiply(const Matrix4x4& tm) { double matrixOut[4][4]; for (int row = 0; row < 4; row++) { matrixOut[row][0] = tm.matrix[row][0] * matrix[0][0] + tm.matrix[row][1] * matrix[1][0] + tm.matrix[row][2] * matrix[2][0] + tm.matrix[row][3] * matrix[3][0]; matrixOut[row][1] = tm.matrix[row][0] * matrix[0][1] + tm.matrix[row][1] * matrix[1][1] + tm.matrix[row][2] * matrix[2][1] + tm.matrix[row][3] * matrix[3][1]; matrixOut[row][2] = tm.matrix[row][0] * matrix[0][2] + tm.matrix[row][1] * matrix[1][2] + tm.matrix[row][2] * matrix[2][2] + tm.matrix[row][3] * matrix[3][2]; matrixOut[row][3] = tm.matrix[row][0] * matrix[0][3] + tm.matrix[row][1] * matrix[1][3] + tm.matrix[row][2] * matrix[2][3] + tm.matrix[row][3] * matrix[3][3]; } setMatrix(matrixOut); this->fixNumericalError(); this->setModified(); } /** * Get the matrix as 16 element one-dimensional array for use by OpenGL. * * @param m A 16-element array of double. * */ void Matrix4x4::getMatrixForOpenGL(double m[16]) const { m[0] = this->matrix[0][0]; m[1] = this->matrix[1][0]; m[2] = this->matrix[2][0]; m[3] = this->matrix[3][0]; m[4] = this->matrix[0][1]; m[5] = this->matrix[1][1]; m[6] = this->matrix[2][1]; m[7] = this->matrix[3][1]; m[8] = this->matrix[0][2]; m[9] = this->matrix[1][2]; m[10] = this->matrix[2][2]; m[11] = this->matrix[3][2]; m[12] = this->matrix[0][3]; m[13] = this->matrix[1][3]; m[14] = this->matrix[2][3]; m[15] = this->matrix[3][3]; } /** * Set the matrix from a one-dimensional OpenGL Matrix as 16 elements. * * @param m A 16-element array of double containing OpenGL Matrix. * */ void Matrix4x4::setMatrixFromOpenGL(const double m[16]) { this->matrix[0][0] = m[0]; this->matrix[1][0] = m[1]; this->matrix[2][0] = m[2]; this->matrix[3][0] = m[3]; this->matrix[0][1] = m[4]; this->matrix[1][1] = m[5]; this->matrix[2][1] = m[6]; this->matrix[3][1] = m[7]; this->matrix[0][2] = m[8]; this->matrix[1][2] = m[9]; this->matrix[2][2] = m[10]; this->matrix[3][2] = m[11]; this->matrix[0][3] = m[12]; this->matrix[1][3] = m[13]; this->matrix[2][3] = m[14]; this->matrix[3][3] = m[15]; this->setModified(); } /** * Get the matrix as 16 element one-dimensional array for use by OpenGL. * * @param m A 16-element array of double. * */ void Matrix4x4::getMatrixForOpenGL(float m[16]) const { m[0] = this->matrix[0][0]; m[1] = this->matrix[1][0]; m[2] = this->matrix[2][0]; m[3] = this->matrix[3][0]; m[4] = this->matrix[0][1]; m[5] = this->matrix[1][1]; m[6] = this->matrix[2][1]; m[7] = this->matrix[3][1]; m[8] = this->matrix[0][2]; m[9] = this->matrix[1][2]; m[10] = this->matrix[2][2]; m[11] = this->matrix[3][2]; m[12] = this->matrix[0][3]; m[13] = this->matrix[1][3]; m[14] = this->matrix[2][3]; m[15] = this->matrix[3][3]; } /** * Set the matrix from a one-dimensional OpenGL Matrix as 16 elements. * * @param m A 16-element array of double containing OpenGL Matrix. * */ void Matrix4x4::setMatrixFromOpenGL(const float m[16]) { this->matrix[0][0] = m[0]; this->matrix[1][0] = m[1]; this->matrix[2][0] = m[2]; this->matrix[3][0] = m[3]; this->matrix[0][1] = m[4]; this->matrix[1][1] = m[5]; this->matrix[2][1] = m[6]; this->matrix[3][1] = m[7]; this->matrix[0][2] = m[8]; this->matrix[1][2] = m[9]; this->matrix[2][2] = m[10]; this->matrix[3][2] = m[11]; this->matrix[0][3] = m[12]; this->matrix[1][3] = m[13]; this->matrix[2][3] = m[14]; this->matrix[3][3] = m[15]; this->setModified(); } /** * Convert the given vector to an OpenGL rotation matrix. * "This" matrix is set to the identity matrix before createing * the rotation matrix. Use Matrix4x4::getMatrixForOpenGL() * to get the matrix after calling this method and then pass * array to glMultMatrixd(). * * http://lifeofaprogrammergeek.blogspot.com/2008/07/rendering-cylinder-between-two-points.html * * @param vector * The vector. MUST be a unit vector. */ void Matrix4x4::setMatrixToOpenGLRotationFromVector(const float vector[3]) { float vx = vector[0]; float vy = vector[1]; float vz = vector[2]; float z = (float)std::sqrt( vx*vx + vy*vy + vz*vz ); double ax = 0.0f; double zero = 1.0e-3; if (std::abs(vz) < zero) { 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 if ( vz <= 0.0f ) ax = -ax; } float rx = -vy*vz; float ry = vx*vz; if ((std::abs(vx) < zero) && (std::fabs(vz) < zero)) { if (vy > 0) { ax = 90; } } identity(); if (std::abs(vz) < zero) { rotateY(90.0); // Rotate & align with x axis rotateX(-ax); // Rotate to point 2 in x-y plane } else { rotate(ax, rx, ry, 0.0); // Rotate about rotation vector } } /** * Transpose the matrix. * */ void Matrix4x4::transpose() { double m[4][4]; m[0][0] = matrix[0][0]; m[0][1] = matrix[1][0]; m[0][2] = matrix[2][0]; m[0][3] = matrix[3][0]; m[1][0] = matrix[0][1]; m[1][1] = matrix[1][1]; m[1][2] = matrix[2][1]; m[1][3] = matrix[3][1]; m[2][0] = matrix[0][2]; m[2][1] = matrix[1][2]; m[2][2] = matrix[2][2]; m[2][3] = matrix[3][2]; m[3][0] = matrix[0][3]; m[3][1] = matrix[1][3]; m[3][2] = matrix[2][3]; m[3][3] = matrix[3][3]; setMatrix(m); this->setModified(); } /** * Set the matrix. * * @param m A 4x4 array of doubles. * */ void Matrix4x4::setMatrix(const double m[4][4]) { for (int i = 0; i < 4; i++) { for (int j = 0; j < 4; j++) { matrix[i][j] = m[i][j]; } } this->setModified(); } /** * Get the matrix. * * @param m A 4x4 array of doubles. * */ void Matrix4x4::getMatrix(double m[4][4]) const { for (int i = 0; i < 4; i++) { for (int j = 0; j < 4; j++) { m[i][j] = matrix[i][j]; } } } /** * Set the matrix. * * @param m A 4x4 array of float. * */ void Matrix4x4::setMatrix(const float m[4][4]) { for (int i = 0; i < 4; i++) { for (int j = 0; j < 4; j++) { matrix[i][j] = m[i][j]; } } this->setModified(); } /** * Get the matrix. * * @param m A 4x4 array of floats. * */ void Matrix4x4::getMatrix(float m[4][4]) const { for (int i = 0; i < 4; i++) { for (int j = 0; j < 4; j++) { m[i][j] = matrix[i][j]; } } } /** * Set the matrix. * * @param cm A Matrix. * */ void Matrix4x4::setMatrix(const Matrix4x4& cm) { for (int i = 0; i < 4; i++) { for (int j = 0; j < 4; j++) { matrix[i][j] = cm.matrix[i][j]; } } this->setModified(); } void Matrix4x4::multiplyPoint3(float p[3]) const { float pout[3] = { 0.0f, 0.0f, 0.0f }; for (int row = 0; row < 3; row++) { pout[row] = (float)(this->matrix[row][0] * p[0] + this->matrix[row][1] * p[1] + this->matrix[row][2] * p[2] + this->matrix[row][3]); } p[0] = pout[0]; p[1] = pout[1]; p[2] = pout[2]; } void Matrix4x4::multiplyPoint3(double p[3]) const { double pout[3] = { 0.0f, 0.0f, 0.0f }; for (int row = 0; row < 3; row++) { pout[row] = (this->matrix[row][0] * p[0] + this->matrix[row][1] * p[1] + this->matrix[row][2] * p[2] + this->matrix[row][3]); } p[0] = pout[0]; p[1] = pout[1]; p[2] = pout[2]; } void Matrix4x4::multiplyPoint4(float p[4]) const { float pout[4] = { 0.0f, 0.0f, 0.0f, 0.0f }; for (int row = 0; row < 4; row++) { pout[row] = (float)(this->matrix[row][0] * p[0] + this->matrix[row][1] * p[1] + this->matrix[row][2] * p[2] + this->matrix[row][3] * p[3]); } p[0] = pout[0]; p[1] = pout[1]; p[2] = pout[2]; p[3] = pout[3]; } void Matrix4x4::multiplyPoint3X3(float p[3]) const { float pout[3] = { 0.0f, 0.0f, 0.0f }; for (int row = 0; row < 3; row++) { pout[row] = (float)(this->matrix[row][0] * p[0] + this->matrix[row][1] * p[1] + this->matrix[row][2] * p[2]); } p[0] = pout[0]; p[1] = pout[1]; p[2] = pout[2]; } /** * Get the data space name (used by GIFTI). * * @return Name of data space. * */ AString Matrix4x4::getDataSpaceName() const { return dataSpaceName; } /** * Set the data space name (used by GIFTI). * * @param name Name of data space. * */ void Matrix4x4::setDataSpaceName(const AString& name) { dataSpaceName = name; this->setModified(); } /** * Get the transformed space name (used by GIFTI). * * @return Name of transformed space. * */ AString Matrix4x4::getTransformedSpaceName() const { return transformedSpaceName; } /** * Set the transformed space name (used by GIFTI). * * @param name Name of transformed space. * */ void Matrix4x4::setTransformedSpaceName(const AString& name) { transformedSpaceName = name; this->setModified(); } /** * Set a matrix element. * @param i Row * @param j Column * @return value at [i][j] * */ double Matrix4x4::getMatrixElement( const int32_t i, const int32_t j) const { return this->matrix[i][j]; } /** * Set a matrix element. * @param i Row * @param j Column * @param e New Value * */ void Matrix4x4::setMatrixElement( const int32_t i, const int32_t j, const double e) { this->matrix[i][j] = e; this->setModified(); } /** * Determine if two matrices are approximately equal. * @param m - matrix to compare. * @param error - Maximum difference between a matrix element. * @return true if all corresponding elements differ by less than "error", * else false. * */ bool Matrix4x4::compare( const Matrix4x4& m, const float error) { for (int i = 0; i < 4; i++) { for (int j = 0; j < 4; j++) { float diff = (float)std::abs(this->matrix[i][j] - m.matrix[i][j]); if (diff > error) { return false; } } } return true; } double Matrix4x4::fixZero(const double f) { if (f > SMALL_POSITIVE_NUMBER) { return f; } else if (f < SMALL_NEGATIVE_NUMBER) { return f; } return 0.0; } /** * Fix numerical error such as very tiny numbers and "negative zeros". * */ void Matrix4x4::fixNumericalError() { for (int i = 0; i < 4; i++) { for (int j = 0; j < 4; j++) { double f = fixZero(this->matrix[i][j]); this->matrix[i][j] = f; } } this->setModified(); } /** * Invert a matrix. * @return true if inversion of matrix is successful, * else false. * */ bool Matrix4x4::invert() { double m[4][4]; const bool valid = Matrix4x4::Inverse(this->matrix, m); if (valid) { for (int i = 0; i < 4; i++) { for (int j = 0; j < 4; j++) { this->matrix[i][j] = m[i][j]; } } this->setModified(); return true; } else { CaretLogWarning("Matrix inversion failed for " + this->toString()); return false; } } /** * Inverse from VTK */ bool Matrix4x4::Inverse(const double a[4][4], double matrixOut[4][4]) const { /////SqMatPtr outElem = (SqMatPtr)outElements; // inverse( original_matrix, inverse_matrix ) // calculate the inverse of a 4x4 matrix // // -1 // A = ___1__ adjoint A // det A // // calculate the 4x4 determinent // if the determinent is zero, // then the inverse matrix is not unique. const double det = Determinant4x4(a); if ( det == 0.0 ) { return false; } // calculate the adjoint matrix Adjoint(a, matrixOut); //vtkMatrix4x4::Adjoint(inElements, outElements ); // scale the adjoint matrix to get the inverse for (int i=0; i<4; i++) { for(int j=0; j<4; j++) { matrixOut[i][j] = matrixOut[i][j] / det; } } return true; } /** * Adjoint from vtkMatrix4x4::Adjoint */ void Matrix4x4::Adjoint(const double inputMatrix[4][4], double outputMatrix[4][4]) const { // // adjoint( original_matrix, inverse_matrix ) // // calculate the adjoint of a 4x4 matrix // // Let a denote the minor determinant of matrix A obtained by // ij // // deleting the ith row and jth column from A. // // i+j // Let b = (-1) a // ij ji // // The matrix B = (b ) is the adjoint of A // ij // double a1, a2, a3, a4, b1, b2, b3, b4; double c1, c2, c3, c4, d1, d2, d3, d4; // assign to individual variable names to aid // selecting correct values a1 = inputMatrix[0][0]; b1 = inputMatrix[0][1]; c1 = inputMatrix[0][2]; d1 = inputMatrix[0][3]; a2 = inputMatrix[1][0]; b2 = inputMatrix[1][1]; c2 = inputMatrix[1][2]; d2 = inputMatrix[1][3]; a3 = inputMatrix[2][0]; b3 = inputMatrix[2][1]; c3 = inputMatrix[2][2]; d3 = inputMatrix[2][3]; a4 = inputMatrix[3][0]; b4 = inputMatrix[3][1]; c4 = inputMatrix[3][2]; d4 = inputMatrix[3][3]; // row column labeling reversed since we transpose rows & columns outputMatrix[0][0] = Matrix4x4::Determinant3x3( b2, b3, b4, c2, c3, c4, d2, d3, d4); outputMatrix[1][0] = - Matrix4x4::Determinant3x3( a2, a3, a4, c2, c3, c4, d2, d3, d4); outputMatrix[2][0] = Matrix4x4::Determinant3x3( a2, a3, a4, b2, b3, b4, d2, d3, d4); outputMatrix[3][0] = - Matrix4x4::Determinant3x3( a2, a3, a4, b2, b3, b4, c2, c3, c4); outputMatrix[0][1] = - Matrix4x4::Determinant3x3( b1, b3, b4, c1, c3, c4, d1, d3, d4); outputMatrix[1][1] = Matrix4x4::Determinant3x3( a1, a3, a4, c1, c3, c4, d1, d3, d4); outputMatrix[2][1] = - Matrix4x4::Determinant3x3( a1, a3, a4, b1, b3, b4, d1, d3, d4); outputMatrix[3][1] = Matrix4x4::Determinant3x3( a1, a3, a4, b1, b3, b4, c1, c3, c4); outputMatrix[0][2] = Matrix4x4::Determinant3x3( b1, b2, b4, c1, c2, c4, d1, d2, d4); outputMatrix[1][2] = - Matrix4x4::Determinant3x3( a1, a2, a4, c1, c2, c4, d1, d2, d4); outputMatrix[2][2] = Matrix4x4::Determinant3x3( a1, a2, a4, b1, b2, b4, d1, d2, d4); outputMatrix[3][2] = - Matrix4x4::Determinant3x3( a1, a2, a4, b1, b2, b4, c1, c2, c4); outputMatrix[0][3] = - Matrix4x4::Determinant3x3( b1, b2, b3, c1, c2, c3, d1, d2, d3); outputMatrix[1][3] = Matrix4x4::Determinant3x3( a1, a2, a3, c1, c2, c3, d1, d2, d3); outputMatrix[2][3] = - Matrix4x4::Determinant3x3( a1, a2, a3, b1, b2, b3, d1, d2, d3); outputMatrix[3][3] = Matrix4x4::Determinant3x3( a1, a2, a3, b1, b2, b3, c1, c2, c3); } /** * From vtkMatrix4x4::Determinant */ double Matrix4x4::Determinant4x4(const double matrixIn[4][4]) const { double a1, a2, a3, a4, b1, b2, b3, b4, c1, c2, c3, c4, d1, d2, d3, d4; // assign to individual variable names to aid selecting // correct elements a1 = matrixIn[0][0]; b1 = matrixIn[0][1]; c1 = matrixIn[0][2]; d1 = matrixIn[0][3]; a2 = matrixIn[1][0]; b2 = matrixIn[1][1]; c2 = matrixIn[1][2]; d2 = matrixIn[1][3]; a3 = matrixIn[2][0]; b3 = matrixIn[2][1]; c3 = matrixIn[2][2]; d3 = matrixIn[2][3]; a4 = matrixIn[3][0]; b4 = matrixIn[3][1]; c4 = matrixIn[3][2]; d4 = matrixIn[3][3]; const double result = a1 * Matrix4x4::Determinant3x3( b2, b3, b4, c2, c3, c4, d2, d3, d4) - b1 * Matrix4x4::Determinant3x3( a2, a3, a4, c2, c3, c4, d2, d3, d4) + c1 * Matrix4x4::Determinant3x3( a2, a3, a4, b2, b3, b4, d2, d3, d4) - d1 * Matrix4x4::Determinant3x3( a2, a3, a4, b2, b3, b4, c2, c3, c4); return result; } double Matrix4x4::Determinant3x3(double a1, double a2, double a3, double b1, double b2, double b3, double c1, double c2, double c3) { return ( a1 * Matrix4x4::Determinant2x2( b2, b3, c2, c3 ) - b1 * Matrix4x4::Determinant2x2( a2, a3, c2, c3 ) + c1 * Matrix4x4::Determinant2x2( a2, a3, b2, b3 ) ); } double Matrix4x4::Determinant2x2(double a, double b, double c, double d) { return (a * d - b * c); }; double Matrix4x4::Determinant3x3(double A[3][3]) { return A[0][0] * A[1][1] * A[2][2] + A[1][0] * A[2][1] * A[0][2] + A[2][0] * A[0][1] * A[1][2] - A[0][0] * A[2][1] * A[1][2] - A[1][0] * A[0][1] * A[2][2] - A[2][0] * A[1][1] * A[0][2]; } /** * Set this matrix to a landmark transform that maps from the source * to the target space as defined by the control points. * Replaces the current matrix. * * @param controlPoints * The control points (pair of source and target coordinates). * @param errorMessageOut * Contains error message. * @return * True if output matrix is valid, else false. If false, this * matrix will be the identity matrix. */ bool Matrix4x4::createLandmarkTransformMatrix(const std::vector& controlPoints, AString& errorMessageOut) { const bool debugFlag = false; identity(); errorMessageOut.clear(); if (controlPoints.size() < 3) { errorMessageOut = "There must be at least three control points."; return false; } float s1[3]; controlPoints[0]->getSourceXYZ(s1); float s2[3]; controlPoints[1]->getSourceXYZ(s2); float s3[3]; controlPoints[2]->getSourceXYZ(s3); float sourceNormalVector[3] = { 0.0, 0.0, 0.0 }; MathFunctions::normalVector(s1, s2, s3, sourceNormalVector); const float tinyValue = 0.00001; if ((sourceNormalVector[2] < tinyValue) && (sourceNormalVector[2] > -tinyValue)) { errorMessageOut = ("First three control points are along a line. Edit control points to that " "the first three control points form a triangular shape."); return false; } // if (sourceNormalVector[2] < 0.0) { // CaretLogWarning("Control points are orientated clockwise; unknown if this causes a problem."); // } double leastError = std::numeric_limits::max(); Matrix4x4 leastErrorMatrix; bool leastErrorMatrixValid = false; const bool testAllTransformTypesFlag = true; if (testAllTransformTypesFlag) { for (int32_t j = 0; j < 3; j++) { LANDMARK_TRANSFORM_MODE mode = LANDMARK_TRANSFORM_AFFINE; AString modeName; switch (j) { case 0: mode = LANDMARK_TRANSFORM_AFFINE; modeName = "LANDMARK_TRANSFORM_AFFINE"; break; case 1: mode = LANDMARK_TRANSFORM_RIGIDBODY; modeName = "LANDMARK_TRANSFORM_RIGIDBODY"; break; case 2: mode = LANDMARK_TRANSFORM_SIMILARITY; modeName = "LANDMARK_TRANSFORM_SIMILARITY"; break; } Matrix4x4 matrix; if (matrix.createLandmarkTransformMatrixPrivate(controlPoints, mode, errorMessageOut)) { if (debugFlag) { std::cout << std::endl; std::cout << "Mode: " << qPrintable(modeName) << std::endl; std::cout << "Matrix: " << qPrintable(matrix.toFormattedString(" ")) << std::endl; } const int32_t numcp = static_cast(controlPoints.size()); double sum = 0.0; for (int32_t i = 0; i < numcp; i++) { double source[3]; controlPoints[i]->getSourceXYZ(source); double predicted[3] = { source[0], source[1], source[2] }; matrix.multiplyPoint3(predicted); double target[3]; controlPoints[i]->getTargetXYZ(target); const double error = MathFunctions::distance3D(predicted, target); sum += error; if (debugFlag) { std::cout << "CP("<< i << ") Source: (" << qPrintable(AString::fromNumbers(source, 3, ",")) << ") Target: (" << qPrintable(AString::fromNumbers(target, 3, ",")) << ") Predicted: (" << qPrintable(AString::fromNumbers(predicted, 3, ",")) << ") Error: " << error << std::endl; } } const double error = (sum /= static_cast(numcp)); if (debugFlag) { std::cout << " Average error per control point: " << error << std::endl; } if (error < leastError) { leastError = error; leastErrorMatrix = matrix; leastErrorMatrixValid = true; } } } } *this = leastErrorMatrix; const float averageError = measureTransformError(controlPoints, *this); // const bool result = createLandmarkTransformMatrixPrivate(controlPoints, // LANDMARK_TRANSFORM_AFFINE, // errorMessageOut); if (debugFlag) { std::cout << std::endl; std::cout << "Transform Error: " << averageError << std::endl; } return leastErrorMatrixValid; } /** * Set this matrix to a landmark transform that maps from the source * to the target space as defined by the control points. * Replaces the current matrix. * * Code Copied From vtkLandmarkTransform * Program: Visualization Toolkit * Module: vtkLandmarkTransform.cxx * * Copyright (c) Ken Martin, Will Schroeder, Bill Lorensen * All rights reserved. * See Copyright.txt or http://www.kitware.com/Copyright.htm for details. * * This software is distributed WITHOUT ANY WARRANTY; without even * the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR * PURPOSE. See the above copyright notice for more information. * * @param controlPoints * The control points (pair of source and target coordinates). * @param mode * Mode for transformation. * @param errorMessageOut * Contains error message. * @return * True if output matrix is valid, else false. If false, this * matrix will be the identity matrix. */ bool Matrix4x4::createLandmarkTransformMatrixPrivate(const std::vector& controlPoints, const LANDMARK_TRANSFORM_MODE mode, AString& errorMessageOut) { identity(); errorMessageOut.clear(); //vtkIdType i; int32_t i; int32_t j; //if (this->SourceLandmarks == NULL || this->TargetLandmarks == NULL) if (controlPoints.empty()) { identity(); errorMessageOut = "Control points are empty."; return false; } // --- compute the necessary transform to match the two sets of landmarks --- /* The solution is based on Berthold K. P. Horn (1987), "Closed-form solution of absolute orientation using unit quaternions," Journal of the Optical Society of America A, 4:629-642 */ // Original python implementation by David G. Gobbi //const vtkIdType N_PTS = this->SourceLandmarks->GetNumberOfPoints(); int32_t N_PTS = static_cast(controlPoints.size()); if (mode == LANDMARK_TRANSFORM_AFFINE) { if (N_PTS > 3) { N_PTS = 3; } } // if(N_PTS != this->TargetLandmarks->GetNumberOfPoints()) // { // vtkErrorMacro("Update: Source and Target Landmarks contain a different number of points"); // return; // } // -- if no points, stop here if (N_PTS == 0) { identity(); return false; } // -- find the centroid of each set -- double source_centroid[3]={0,0,0}; double target_centroid[3]={0,0,0}; double p[3]; for(i=0;iSourceLandmarks->GetPoint(i, p); controlPoints[i]->getSourceXYZ(p); source_centroid[0] += p[0]; source_centroid[1] += p[1]; source_centroid[2] += p[2]; //this->TargetLandmarks->GetPoint(i, p); controlPoints[i]->getTargetXYZ(p); target_centroid[0] += p[0]; target_centroid[1] += p[1]; target_centroid[2] += p[2]; } source_centroid[0] /= N_PTS; source_centroid[1] /= N_PTS; source_centroid[2] /= N_PTS; target_centroid[0] /= N_PTS; target_centroid[1] /= N_PTS; target_centroid[2] /= N_PTS; double matrixArray[4][4]; getMatrix(matrixArray); // -- if only one point, stop right here if (N_PTS == 1) { //this->Matrix->Identity(); matrixArray[0][3] = target_centroid[0] - source_centroid[0]; matrixArray[1][3] = target_centroid[1] - source_centroid[1]; matrixArray[2][3] = target_centroid[2] - source_centroid[2]; return true; } // -- build the 3x3 matrix M -- double M[3][3]; double AAT[3][3]; for(i=0;i<3;i++) { AAT[i][0] = M[i][0]=0.0F; // fill M with zeros AAT[i][1] = M[i][1]=0.0F; AAT[i][2] = M[i][2]=0.0F; } //vtkIdType pt; int32_t pt; double a[3],b[3]; double sa=0.0F,sb=0.0F; for(pt=0;ptSourceLandmarks->GetPoint(pt,a); CaretAssertVectorIndex(controlPoints, pt); controlPoints[pt]->getSourceXYZ(a); a[0] -= source_centroid[0]; a[1] -= source_centroid[1]; a[2] -= source_centroid[2]; // get the origin-centred point (b) in the target set //this->TargetLandmarks->GetPoint(pt,b); controlPoints[pt]->getTargetXYZ(b); b[0] -= target_centroid[0]; b[1] -= target_centroid[1]; b[2] -= target_centroid[2]; // accumulate the products a*T(b) into the matrix M for(i=0;i<3;i++) { M[i][0] += a[i]*b[0]; M[i][1] += a[i]*b[1]; M[i][2] += a[i]*b[2]; // for the affine transform, compute ((a.a^t)^-1 . a.b^t)^t. // a.b^t is already in M. here we put a.a^t in AAT. if (mode == LANDMARK_TRANSFORM_AFFINE) { AAT[i][0] += a[i]*a[0]; AAT[i][1] += a[i]*a[1]; AAT[i][2] += a[i]*a[2]; } } // accumulate scale factors (if desired) sa += a[0]*a[0]+a[1]*a[1]+a[2]*a[2]; sb += b[0]*b[0]+b[1]*b[1]+b[2]*b[2]; /* * If source points all have same value for 'k', * it will cause element [2][2] to be zero * and the matrix cannot be inverted. */ if (AAT[2][2] == 0.0) { AAT[2][2] = 1.0; } } if(mode == LANDMARK_TRANSFORM_AFFINE) { // AAT = (a.a^t)^-1 MathFunctions::vtkInvert3x3(AAT,AAT); // M = (a.a^t)^-1 . a.b^t MathFunctions::vtkMultiply3x3(AAT,M,M); // this->Matrix = M^t for(i=0;i<3;++i) { for(j=0;j<3;++j) { matrixArray[i][j] = M[j][i]; } } } else { // compute required scaling factor (if desired) double scale = (double)std::sqrt(sb/sa); // -- build the 4x4 matrix N -- double Ndata[4][4]; double *N[4]; for(i=0;i<4;i++) { N[i] = Ndata[i]; N[i][0]=0.0F; // fill N with zeros N[i][1]=0.0F; N[i][2]=0.0F; N[i][3]=0.0F; } // on-diagonal elements N[0][0] = M[0][0]+M[1][1]+M[2][2]; N[1][1] = M[0][0]-M[1][1]-M[2][2]; N[2][2] = -M[0][0]+M[1][1]-M[2][2]; N[3][3] = -M[0][0]-M[1][1]+M[2][2]; // off-diagonal elements N[0][1] = N[1][0] = M[1][2]-M[2][1]; N[0][2] = N[2][0] = M[2][0]-M[0][2]; N[0][3] = N[3][0] = M[0][1]-M[1][0]; N[1][2] = N[2][1] = M[0][1]+M[1][0]; N[1][3] = N[3][1] = M[2][0]+M[0][2]; N[2][3] = N[3][2] = M[1][2]+M[2][1]; // -- eigen-decompose N (is symmetric) -- double eigenvectorData[4][4]; double *eigenvectors[4],eigenvalues[4]; eigenvectors[0] = eigenvectorData[0]; eigenvectors[1] = eigenvectorData[1]; eigenvectors[2] = eigenvectorData[2]; eigenvectors[3] = eigenvectorData[3]; MathFunctions::vtkJacobiN(N,4,eigenvalues,eigenvectors); // the eigenvector with the largest eigenvalue is the quaternion we want // (they are sorted in decreasing order for us by JacobiN) double w,x,y,z; // first: if points are collinear, choose the quaternion that // results in the smallest rotation. if (eigenvalues[0] == eigenvalues[1] || N_PTS == 2) { double s0[3],t0[3],s1[3],t1[3]; // this->SourceLandmarks->GetPoint(0,s0); // this->TargetLandmarks->GetPoint(0,t0); // this->SourceLandmarks->GetPoint(1,s1); // this->TargetLandmarks->GetPoint(1,t1); CaretAssertVectorIndex(controlPoints, 1); controlPoints[0]->getSourceXYZ(s0); controlPoints[0]->getTargetXYZ(t0); controlPoints[1]->getSourceXYZ(s1); controlPoints[1]->getTargetXYZ(t1); double ds[3],dt[3]; double rs = 0, rt = 0; for (i = 0; i < 3; i++) { ds[i] = s1[i] - s0[i]; // vector between points rs += ds[i]*ds[i]; dt[i] = t1[i] - t0[i]; rt += dt[i]*dt[i]; } // normalize the two vectors rs = sqrt(rs); ds[0] /= rs; ds[1] /= rs; ds[2] /= rs; rt = sqrt(rt); dt[0] /= rt; dt[1] /= rt; dt[2] /= rt; // take dot & cross product w = ds[0]*dt[0] + ds[1]*dt[1] + ds[2]*dt[2]; x = ds[1]*dt[2] - ds[2]*dt[1]; y = ds[2]*dt[0] - ds[0]*dt[2]; z = ds[0]*dt[1] - ds[1]*dt[0]; double r = sqrt(x*x + y*y + z*z); double theta = atan2(r,w); // construct quaternion w = cos(theta/2); if (r != 0) { r = sin(theta/2)/r; x = x*r; y = y*r; z = z*r; } else // rotation by 180 degrees: special case { // rotate around a vector perpendicular to ds MathFunctions::vtkPerpendiculars(ds,dt,0,0); r = sin(theta/2); x = dt[0]*r; y = dt[1]*r; z = dt[2]*r; } } else // points are not collinear { w = eigenvectors[0][0]; x = eigenvectors[1][0]; y = eigenvectors[2][0]; z = eigenvectors[3][0]; } // convert quaternion to a rotation matrix double ww = w*w; double wx = w*x; double wy = w*y; double wz = w*z; double xx = x*x; double yy = y*y; double zz = z*z; double xy = x*y; double xz = x*z; double yz = y*z; matrixArray[0][0] = ww + xx - yy - zz; matrixArray[1][0] = 2.0*(wz + xy); matrixArray[2][0] = 2.0*(-wy + xz); matrixArray[0][1] = 2.0*(-wz + xy); matrixArray[1][1] = ww - xx + yy - zz; matrixArray[2][1] = 2.0*(wx + yz); matrixArray[0][2] = 2.0*(wy + xz); matrixArray[1][2] = 2.0*(-wx + yz); matrixArray[2][2] = ww - xx - yy + zz; if (mode != LANDMARK_TRANSFORM_RIGIDBODY) { // add in the scale factor (if desired) for(i=0;i<3;i++) { matrixArray[i][0] *= scale; matrixArray[i][1] *= scale; matrixArray[i][2] *= scale; } } } // the translation is given by the difference in the transformed source // centroid and the target centroid double sx, sy, sz; sx = matrixArray[0][0] * source_centroid[0] + matrixArray[0][1] * source_centroid[1] + matrixArray[0][2] * source_centroid[2]; sy = matrixArray[1][0] * source_centroid[0] + matrixArray[1][1] * source_centroid[1] + matrixArray[1][2] * source_centroid[2]; sz = matrixArray[2][0] * source_centroid[0] + matrixArray[2][1] * source_centroid[1] + matrixArray[2][2] * source_centroid[2]; matrixArray[0][3] = target_centroid[0] - sx; matrixArray[1][3] = target_centroid[1] - sy; matrixArray[2][3] = target_centroid[2] - sz; // fill the bottom row of the 4x4 matrix matrixArray[3][0] = 0.0; matrixArray[3][1] = 0.0; matrixArray[3][2] = 0.0; matrixArray[3][3] = 1.0; /* * Can get zero for a scale factor if planes of the * source and target are parallel */ for (int32_t i = 0; i < 3; i++) { if (matrixArray[i][i] == 0.0) { matrixArray[i][i] = 1.0; } } setMatrix(matrixArray); //this->Matrix->Modified(); return true; } /** * Measure the average error for control points using transform matrix. * Error is straight line distance between ([source] * [matrix]) and * (target). * * @param controlProints * The control points * @param matrix * The transform matrix. */ float Matrix4x4::measureTransformError(const std::vector& controlPoints, const Matrix4x4& matrix) const { const int32_t numcp = static_cast(controlPoints.size()); if (numcp <= 0) { return 0.0; } double sum = 0.0; for (int32_t i = 0; i < numcp; i++) { ControlPoint3D* cp = controlPoints[i]; double pt[3]; cp->getSourceXYZ(pt); matrix.multiplyPoint3(pt); cp->setTransformedXYZ(pt); double target[3]; controlPoints[i]->getTargetXYZ(target); const double totalError = MathFunctions::distance3D(pt, target); sum += totalError; } const double error = (sum /= static_cast(numcp)); return error; } /** * Write the matrix as a GIFTI matrix using the given XML tags. * * @param xmlWriter * The XML writer * @param xmlMatrixTag * XML tag for for the matrix and its components. * @param xmlDataSpaceTag * XML tag for for the data space name. * @param xmlTransformedSpaceTag * XML tag for for the transformed space name. * @param xmlMatrixDataTag * XML tag for the matrix data. * * @throws XmlException * If an error occurs while writing. */ void Matrix4x4::writeAsGiftiXML(XmlWriter& xmlWriter, const AString& xmlMatrixTag, const AString& xmlDataSpaceTag, const AString& xmlTransformedSpaceTag, const AString& xmlMatrixDataTag) { xmlWriter.writeStartElement(xmlMatrixTag); xmlWriter.writeElementCData(xmlDataSpaceTag, this->dataSpaceName); xmlWriter.writeElementCData(xmlTransformedSpaceTag, this->transformedSpaceName); /* * Note: Matrix4x4 is column major order but GIFTI uses * row major order so transpose matrix as values are * written. */ xmlWriter.writeStartElement(xmlMatrixDataTag); for (int32_t iRow = 0; iRow < 4; iRow++) { for (int32_t jCol = 0; jCol < 4; jCol++) { /* * Transpose indices */ const int32_t i = jCol; const int32_t j = iRow; const AString txt = (AString::number(this->matrix[i][j]) + " "); if (jCol == 0) { xmlWriter.writeCharactersWithIndent(txt); } else { xmlWriter.writeCharacters(txt); } if (jCol == 3) { xmlWriter.writeCharacters("\n"); } } } xmlWriter.writeEndElement(); xmlWriter.writeEndElement(); } /** * Convert the matrix into a string representation. * * @return String representation of the matrix. * */ AString Matrix4x4::toString() const { return toFormattedString(" "); } /** * Get a nicely formatted string for printing. * * @param indentation - use as indentation. * @return String containing label information. * */ AString Matrix4x4::toFormattedString(const AString& indentation) const { float translation[3]; getTranslation(translation); double rotation[3]; getRotation(rotation[0], rotation[1], rotation[2]); double scale[3]; getScale(scale[0], scale[1], scale[2]); AString elements; for (int32_t iRow = 0; iRow < 4; iRow++) { elements.append(indentation); for (int32_t iCol = 0; iCol < 4; iCol++) { elements.append(AString::number(matrix[iRow][iCol]) + " "); } elements.append("\n"); } const AString s("Matrix4x4: \n" + elements + indentation + "Translation: " + AString::fromNumbers(translation, 3, ", ") + "\n" + indentation + "Rotation: " + AString::fromNumbers(rotation, 3, ", ") + "\n" + indentation + "Scale: " + AString::fromNumbers(scale, 3, ", ")); return s; } /** * Set this object has been modified. * */ void Matrix4x4::setModified() { this->modifiedFlag = true; } /** * Set this object as not modified. Object should also * clear the modification status of its children. * */ void Matrix4x4::clearModified() { this->modifiedFlag = false; } /** * Get the modification status. Returns true if this object or * any of its children have been modified. * @return - The modification status. * */ bool Matrix4x4::isModified() const { return this->modifiedFlag; } connectome-workbench-1.2.3+git41-gc4c6c90/src/Nifti/Matrix4x4.h000066400000000000000000000157761300200146000236120ustar00rootroot00000000000000#ifndef __MATRIX4X4_H__ #define __MATRIX4X4_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 #include namespace caret { class ControlPoint3D; class XmlWriter; /** * A 4x4 homogeneous transformation matrix. */ class Matrix4x4 : public CaretObject { public: Matrix4x4(); public: Matrix4x4(const Matrix4x4& o); Matrix4x4& operator=(const Matrix4x4& o); virtual ~Matrix4x4(); private: void copyHelper(const Matrix4x4& o); void initializeMembersMatrix4x4(); public: void identity(); void getTranslation(float translatationOut[3]) const; void setTranslation(const float t[]); void setTranslation( const double tx, const double ty, const double tz); void translate( const double tx, const double ty, const double tz); void translate(const double txyz[3]); void scale( const double sx, const double sy, const double sz); void getScale(double& scaleOutX, double& scaleOutY, double& scaleOutZ) const; void rotateX(const double degrees); void rotateY(const double degrees); void rotateZ(const double degrees); void rotate( const double angle, const double vector[]); void rotate( const double angle, const double x, const double y, const double z); void getRotation(double& rotationOutX, double& rotationOutY, double& rotationOutZ) const; void setRotation(const double rotationX, const double rotationY, const double rotationZ); void premultiply(const Matrix4x4& tm); void postmultiply(const Matrix4x4& tm); void getMatrixForOpenGL(double m[16]) const; void setMatrixFromOpenGL(const double m[16]); void getMatrixForOpenGL(float m[16]) const; void setMatrixFromOpenGL(const float m[16]); void setMatrixToOpenGLRotationFromVector(const float vector[3]); void transpose(); void setMatrix(const double m[4][4]); void getMatrix(double m[4][4]) const; void setMatrix(const float m[4][4]); void getMatrix(float m[4][4]) const; void setMatrix(const Matrix4x4& cm); void multiplyPoint4(float p[4]) const; void multiplyPoint3(float p[3]) const; void multiplyPoint3(double p[3]) const; void multiplyPoint3X3(float p[3]) const; AString getDataSpaceName() const; void setDataSpaceName(const AString& name); AString getTransformedSpaceName() const; void setTransformedSpaceName(const AString& name); double getMatrixElement( const int32_t i, const int32_t j) const; void setMatrixElement( const int32_t i, const int32_t j, const double e); bool compare( const Matrix4x4& m, const float error); bool invert(); bool createLandmarkTransformMatrix(const std::vector& controlPoints, AString& errorMessageOut); AString toString() const; AString toFormattedString(const AString& indentation) const; void setModified(); void clearModified(); bool isModified() const; void writeAsGiftiXML(XmlWriter& xmlWriter, const AString& xmlMatrixTag, const AString& xmlDataSpaceTag, const AString& xmlTransformedSpaceTag, const AString& xmlMatrixDataTag); private: enum LANDMARK_TRANSFORM_MODE { LANDMARK_TRANSFORM_AFFINE, LANDMARK_TRANSFORM_RIGIDBODY, LANDMARK_TRANSFORM_SIMILARITY }; double fixZero(const double f); void fixNumericalError(); void Transpose(const double input[4][4], double output[4][4]) const; bool Inverse(const double inputMatrix[4][4], double outputMatrix[4][4]) const; void Adjoint(const double inputMatrix[4][4], double outputMatrix[4][4]) const; void UpperTriangle4(const double inputMatrix[4][4], double outputMatrix[4][4]) const; void UpperTriangle3(const double inputMatrix[3][3], double outputMatrix[3][3]) const; double Determinant4x4(const double matrix[4][4]) const; static double Determinant3x3(double A[3][3]); double Determinant3(const double matrix[3][3]) const; static double Determinant3x3(double a1, double a2, double a3, double b1, double b2, double b3, double c1, double c2, double c3); static double Determinant2x2(double a, double b, double c, double d); static void Orthogonalize3x3(const double A[3][3], double B[3][3]); static void SwapVectors3(double v1[3], double v2[3]); static void SingularValueDecomposition3x3(const double A[3][3], double U[3][3], double w[3], double VT[3][3]); static void Transpose3x3(const double A[3][3], double AT[3][3]); static void Multiply3x3(const double A[3][3], const double B[3][3], double C[3][3]); static void Diagonalize3x3(const double A[3][3], double w[3], double V[3][3]); static int JacobiN(double **a, int n, double *w, double **v); bool createLandmarkTransformMatrixPrivate(const std::vector& controlPoints, const LANDMARK_TRANSFORM_MODE mode, AString& errorMessageOut); float measureTransformError(const std::vector& controlPoints, const Matrix4x4& matrix) const; protected: /**the 4x4 matrix */ double matrix[4][4]; /**data space name (used by GIFTI) */ AString dataSpaceName; /**transformed space name (used by GIFTI) */ AString transformedSpaceName; private: /**data modification status (DO NOT CLONE) */ bool modifiedFlag; }; } // namespace #endif // __MATRIX4X4_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Nifti/NiftiHeader.cxx000066400000000000000000001046231300200146000245310ustar00rootroot00000000000000/*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 "NiftiHeader.h" #include "ByteSwapping.h" #include "CaretAssert.h" #include "CaretLogger.h" #include "DataFileException.h" #include "FloatMatrix.h" #include "MathFunctions.h" #include #include #include using namespace std; using namespace caret; NiftiHeader::NiftiHeader() { if (sizeof(nifti_1_header) != 348 || sizeof(nifti_2_header) != 540)//these should be made into static asserts when we move to c++11 or decide to use boost { throw DataFileException("internal error: nifti header structs are the wrong size");//this is not a runtime assert, because we want this checked in release builds too } memset(&m_header, 0, sizeof(m_header)); for (int i = 0; i < 8; ++i) { m_header.dim[i] = 1;//we maintain 1s on unused dimensions to make some stupid nifti readers happy m_header.pixdim[i] = 1.0f;//also set pixdims to 1 by default, to make some other nifti readers happy when reading cifti headers }//note that we still warn when asking for spacing info and qform and sform codes are both 0, so this doesn't prevent us from catching non-volume files loaded as volumes m_header.xyzt_units = SPACE_TIME_TO_XYZT(NIFTI_UNITS_MM, NIFTI_UNITS_SEC); m_header.scl_slope = 1.0;//default to identity scaling m_header.scl_inter = 0.0; m_header.datatype = NIFTI_TYPE_FLOAT32; m_header.bitpix = typeToNumBits(m_header.datatype); m_version = 0; m_isSwapped = false; } AbstractHeader* NiftiHeader::clone() const { return new NiftiHeader(*this); } NiftiHeader::NiftiHeader(const NiftiHeader& rhs) { m_header = rhs.m_header; m_isSwapped = rhs.m_isSwapped; m_version = rhs.m_version; m_extensions.reserve(rhs.m_extensions.size()); for (size_t i = 0; i < rhs.m_extensions.size(); ++i) { m_extensions.push_back(CaretPointer(new NiftiExtension(*(rhs.m_extensions[i])))); } } NiftiHeader& NiftiHeader::operator=(const NiftiHeader& rhs) { if (this == &rhs) return *this; m_header = rhs.m_header; m_isSwapped = rhs.m_isSwapped; m_version = rhs.m_version; m_extensions.clear(); m_extensions.reserve(rhs.m_extensions.size()); for (size_t i = 0; i < rhs.m_extensions.size(); ++i) { m_extensions.push_back(CaretPointer(new NiftiExtension(*(rhs.m_extensions[i])))); } return *this; } bool NiftiHeader::canWriteVersion(const int& version) const { if (computeVoxOffset(version) < 0) return false;//error condition, can happen if an extension is longer than 2^31 if (version == 2) return true;//our internal state is nifti-2, return early if (version != 1) return false;//we can only write 1 and 2 vector dims = getDimensions(); for (int i = 0; i < (int)dims.size(); ++i) { if (dims[i] > numeric_limits::max()) return false; } if (m_header.intent_code > numeric_limits::max() || m_header.intent_code < numeric_limits::min()) return false; if (m_header.slice_code > numeric_limits::max() || m_header.slice_code < numeric_limits::min()) return false; if (m_header.xyzt_units > numeric_limits::max() || m_header.xyzt_units < numeric_limits::min()) return false; if (m_header.qform_code > numeric_limits::max() || m_header.qform_code < numeric_limits::min()) return false; if (m_header.sform_code > numeric_limits::max() || m_header.sform_code < numeric_limits::min()) return false; return true; } int64_t NiftiHeader::computeVoxOffset(const int& version) const { int64_t ret; switch (version) { case 1: ret = 4 + sizeof(nifti_1_header);//the 4 is the extender bytes break; case 2: ret = 4 + sizeof(nifti_2_header); break; default: return -1; } int numExtensions = (int)m_extensions.size(); for (int i = 0; i < numExtensions; ++i) { CaretAssert(m_extensions[i] != NULL); int64_t thisSize = 8 + m_extensions[i]->m_bytes.size();//8 is for the int32_t size and ecode for nifti-1 style extensions if (thisSize % 16 != 0)//round up to nearest multiple of 16 { int paddingBytes = 16 - (thisSize % 16); thisSize += paddingBytes; } if (thisSize > numeric_limits::max()) return -1;//since we don't have nifti-2 style extensions yet, always fail ret += thisSize; } if (version == 1)//need to put it into a float exactly (yes, really) { float temp = ret; if (ret != (int64_t)temp) return -1;//for now, just fail, until it actually becomes a problem } return ret; } bool NiftiHeader::getDataScaling(double& mult, double& offset) const { if (m_header.datatype == NIFTI_TYPE_RGB24 || m_header.scl_slope == 0.0 || (m_header.scl_slope == 1.0 && m_header.scl_inter == 0.0))//the "if slope is zero" case is in the nifti spec { mult = 1.0;//in case someone ignores the boolean offset = 0.0; return false; } mult = m_header.scl_slope; offset = m_header.scl_inter; return true; } vector NiftiHeader::getDimensions() const { CaretAssert(m_header.dim[0] >= 0 && m_header.dim[0] <= 7);//because storage is private and initialized to zero, so it should never be invalid vector ret(m_header.dim[0]); for (int i = 0; i < m_header.dim[0]; ++i) { ret[i] = m_header.dim[i + 1]; } return ret; } vector > NiftiHeader::getFSLSpace() const {//don't look at me, blame analyze and flirt vector dimensions = getDimensions(); if (dimensions.size() < 3) throw DataFileException("NiftiHeaderIO has less than 3 dimensions, can't generate the FSL space for it"); FloatMatrix ret; vector > sform = getSForm(); float determinant = sform[0][0] * sform[1][1] * sform[2][2] + sform[0][1] * sform[1][2] * sform[2][0] + sform[0][2] * sform[1][0] * sform[2][1] - sform[0][2] * sform[1][1] * sform[2][0] - sform[0][0] * sform[1][2] * sform[2][1] - sform[0][1] * sform[1][0] * sform[2][2];//just write out the 3x3 determinant rather than packing it into a FloatMatrix first - and I haven't put a determinant function in yet ret = FloatMatrix::identity(4);//generate a 4x4 with 0 0 0 1 last row via FloatMatrix for convenience if (determinant > 0.0f) { ret[0][0] = -m_header.pixdim[1];//yes, they really use pixdim, despite checking the SForm/QForm for flipping - ask them, not me ret[0][3] = (dimensions[0] - 1) * m_header.pixdim[1];//note - pixdim[1] is for i, pixdim[0] is qfac } else { ret[0][0] = m_header.pixdim[1]; } ret[1][1] = m_header.pixdim[2]; ret[2][2] = m_header.pixdim[3]; int32_t spaceUnit = XYZT_TO_SPACE(m_header.xyzt_units); switch (spaceUnit) { case NIFTI_UNITS_METER: ret *= 1000.0f; ret[3][3] = 1.0f; break; case NIFTI_UNITS_MICRON: ret *= 0.001f; ret[3][3] = 1.0f; break; case 0://will already have warned in getSForm() case NIFTI_UNITS_MM: break; default: break;//will already have warned in getSForm() } return ret.getMatrix(); } bool NiftiHeader::operator==(const NiftiHeader& rhs) const { if (m_version != rhs.m_version) return false;//this is to test for consistency, not to test if two headers mean the same thing if (m_isSwapped != rhs.m_isSwapped) return false; return memcmp(&m_header, &(rhs.m_header), sizeof(m_header)) == 0; } vector > NiftiHeader::getSForm() const { FloatMatrix ret = FloatMatrix::zeros(4, 4); ret[3][3] = 1.0f;//force 0 0 0 1 last row if (m_header.sform_code != 0)//prefer sform { for(int i = 0; i < 4; i++) { ret[0][i] = m_header.srow_x[i]; ret[1][i] = m_header.srow_y[i]; ret[2][i] = m_header.srow_z[i]; } } else if (m_header.qform_code != 0) {//fall back to qform float rotmat[3][3], quat[4]; quat[1] = m_header.quatern_b; quat[2] = m_header.quatern_c; quat[3] = m_header.quatern_d; float checkquat = quat[1] * quat[1] + quat[2] * quat[2] + quat[3] * quat[3]; if (checkquat <= 1.01f)//make sure qform is sane { if (checkquat > 1.0f) { quat[0] = 0.0f; } else { quat[0] = sqrt(1.0f - checkquat); } MathFunctions::quaternToMatrix(quat, rotmat); for (int i = 0; i < 3; ++i) { for (int j = 0; j < 3; ++j) { rotmat[i][j] *= m_header.pixdim[i + 1]; } } if (m_header.pixdim[0] < 0.0f)//left handed coordinate system, flip the kvec { rotmat[0][2] = -rotmat[0][2]; rotmat[1][2] = -rotmat[1][2]; rotmat[2][2] = -rotmat[2][2]; } for (int i = 0; i < 3; ++i) { for (int j = 0; j < 3; ++j) { ret[i][j] = rotmat[i][j]; } } ret[0][3] = m_header.qoffset_x; 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"); ret[0][0] = m_header.pixdim[1]; ret[1][1] = m_header.pixdim[2]; ret[2][2] = m_header.pixdim[3]; } } else {//fall back to analyze and complain CaretLogWarning("no sform or qform code found, using ANALYZE coordinates!"); ret[0][0] = m_header.pixdim[1]; ret[1][1] = m_header.pixdim[2]; ret[2][2] = m_header.pixdim[3]; } int32_t spaceUnit = XYZT_TO_SPACE(m_header.xyzt_units); switch (spaceUnit) { case NIFTI_UNITS_METER: ret *= 1000.0f; ret[3][3] = 1.0f; break; case NIFTI_UNITS_MICRON: ret *= 0.001f; ret[3][3] = 1.0f; break; case 0: CaretLogFine("found spatial unit of '0' in nifti header, assuming millimeters"); case NIFTI_UNITS_MM: break; default: CaretLogWarning("unrecognized spatial unit in nifti header"); } return ret.getMatrix(); } QString NiftiHeader::toString() const { QString ret; if (isSwapped()) { ret += "native endian: false\n"; } else { ret += "native endian: true\n"; } ret += "sizeof_hdr: " + QString::number(m_header.sizeof_hdr) + "\n";//skip the fields that aren't important, like intent_p1, cal_max, etc ret += "magic: " + QByteArray(m_header.magic, 8);//quirk: QByteArray supports embedded nulls, so adding "\n" here doesn't result in a newline in the string ret += "\ndatatype: " + QString::number(m_header.datatype) + "\n"; ret += "bitpix: " + QString::number(m_header.bitpix) + "\n"; CaretAssert(m_header.dim[0] < 8); for (int i = 0; i <= m_header.dim[0]; ++i) { ret += "dim[" + QString::number(i) + "]: " + QString::number(m_header.dim[i]) + "\n"; } for (int i = 0; i <= m_header.dim[0]; ++i) { ret += "pixdim[" + QString::number(i) + "]: " + QString::number(m_header.pixdim[i]) + "\n"; } ret += "vox_offset: " + QString::number(m_header.vox_offset) + "\n"; ret += "scl_slope: " + QString::number(m_header.scl_slope) + "\n"; ret += "scl_inter: " + QString::number(m_header.scl_inter) + "\n"; ret += "sform_code: " + QString::number(m_header.sform_code) + "\n"; if (m_header.sform_code != NIFTI_XFORM_UNKNOWN) { ret += "srow_x:"; for (int i = 0; i < 4; ++i) { ret += " " + QString::number(m_header.srow_x[i]); } ret += "\nsrow_y:"; for (int i = 0; i < 4; ++i) { ret += " " + QString::number(m_header.srow_y[i]); } ret += "\nsrow_z:"; for (int i = 0; i < 4; ++i) { ret += " " + QString::number(m_header.srow_z[i]); } ret += "\n"; } ret += "qform_code: " + QString::number(m_header.qform_code) + "\n"; if (m_header.qform_code != NIFTI_XFORM_UNKNOWN) { ret += "quatern_b: " + QString::number(m_header.quatern_b) + "\n"; ret += "quatern_c: " + QString::number(m_header.quatern_c) + "\n"; ret += "quatern_d: " + QString::number(m_header.quatern_d) + "\n"; ret += "qoffset_x: " + QString::number(m_header.qoffset_x) + "\n"; ret += "qoffset_y: " + QString::number(m_header.qoffset_y) + "\n"; ret += "qoffset_z: " + QString::number(m_header.qoffset_z) + "\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 ret += "\n"; int numExts = (int)m_extensions.size(); ret += QString::number(numExts) + " extension"; if (numExts != 1) ret += "s"; if (numExts == 0) { ret += "\n"; } else { ret += ":\n"; for (int i = 0; i < numExts; ++i) { CaretAssert(m_extensions[i] != NULL); ret += "\n"; ret += "code: " + QString::number(m_extensions[i]->m_ecode) + "\n"; ret += "length: " + QString::number(m_extensions[i]->m_bytes.size()) + "\n"; } } return ret; } void NiftiHeader::setDataType(const int16_t& type) { m_header.bitpix = typeToNumBits(m_header.datatype);//to check for errors m_header.datatype = type; } void NiftiHeader::setDimensions(const vector& dimsIn) { if (dimsIn.size() > 7 || dimsIn.empty()) throw DataFileException("Number of dimensions must be between 1 and 7, inclusive."); m_header.dim[0] = dimsIn.size(); int i = 0; for(; i < (int)dimsIn.size(); i++) { if (dimsIn[i] < 1) throw DataFileException("all dimension lengths must be positive");//maybe these should be asserts? m_header.dim[i + 1] = dimsIn[i]; } for (; i < 7; ++i) { m_header.dim[i + 1] = 1;//we maintain 1s on unused dimensions to make some stupid nifti readers happy } } void NiftiHeader::setIntent(const int32_t& code, const char name[16]) { m_header.intent_code = code; int i;//custom strncpy-like code to fill nulls to the end for (i = 0; i < 16 && name[i] != '\0'; ++i) m_header.intent_name[i] = name[i]; for (; i < 16; ++i) m_header.intent_name[i] = '\0'; } void NiftiHeader::setSForm(const vector >& sForm) { CaretAssert(sForm.size() >= 3);//programmer error to pass badly sized matrix if (sForm.size() < 3) throw DataFileException("internal error: setSForm matrix badly sized");//but make release also throw for (int i = 0; i < (int)sForm.size(); i++) { CaretAssert(sForm[i].size() >= 4);//ditto if (sForm[i].size() < 4) throw DataFileException("internal error: setSForm matrix badly sized"); } m_header.xyzt_units = SPACE_TIME_TO_XYZT(NIFTI_UNITS_MM, NIFTI_UNITS_SEC);//overwrite whatever units we read in for (int i = 0; i < 4; i++) { m_header.srow_x[i] = sForm[0][i]; m_header.srow_y[i] = sForm[1][i]; m_header.srow_z[i] = sForm[2][i]; } m_header.sform_code = NIFTI_XFORM_MNI_152; Vector3D ivec, jvec, kvec; ivec[0] = sForm[0][0]; ivec[1] = sForm[1][0]; ivec[2] = sForm[2][0]; jvec[0] = sForm[0][1]; jvec[1] = sForm[1][1]; jvec[2] = sForm[2][1]; kvec[0] = sForm[0][2]; kvec[1] = sForm[1][2]; kvec[2] = sForm[2][2]; m_header.pixdim[0] = 1.0f; m_header.pixdim[1] = ivec.length(); m_header.pixdim[2] = jvec.length(); m_header.pixdim[3] = kvec.length(); ivec = ivec.normal(); jvec = jvec.normal(); kvec = kvec.normal(); if (kvec.dot(ivec.cross(jvec)) < 0.0f)//left handed sform! { m_header.pixdim[0] = -1.0f; 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]; float quat[4]; if (!MathFunctions::matrixToQuatern(rotmat, quat)) { m_header.qform_code = NIFTI_XFORM_UNKNOWN;//0, implies that there is no qform m_header.quatern_b = 0.0;//set dummy values anyway m_header.quatern_c = 0.0; m_header.quatern_d = 0.0; m_header.qoffset_x = sForm[0][3]; m_header.qoffset_y = sForm[1][3]; m_header.qoffset_z = sForm[2][3]; } else { m_header.qform_code = NIFTI_XFORM_MNI_152; m_header.quatern_b = quat[1]; m_header.quatern_c = quat[2]; m_header.quatern_d = quat[3]; m_header.qoffset_x = sForm[0][3]; m_header.qoffset_y = sForm[1][3]; m_header.qoffset_z = sForm[2][3]; } } void NiftiHeader::clearDataScaling() { m_header.scl_slope = 1.0; m_header.scl_inter = 0.0; } void NiftiHeader::setDataScaling(const double& mult, const double& offset) { m_header.scl_slope = mult; m_header.scl_inter = offset; } void NiftiHeader::read(CaretBinaryFile& inFile) { nifti_1_header buffer1; nifti_2_header buffer2; inFile.read(&buffer1, sizeof(nifti_1_header)); int version = NIFTI2_VERSION(buffer1); bool swapped = false; try { if (version == 2) { memcpy(&buffer2, &buffer1, sizeof(nifti_1_header)); inFile.read(((char*)&buffer2) + sizeof(nifti_1_header), sizeof(nifti_2_header) - sizeof(nifti_1_header)); if (NIFTI2_NEEDS_SWAP(buffer2)) { swapped = true; swapHeaderBytes(buffer2); } setupFrom(buffer2); } else if (version == 1) { if (NIFTI2_NEEDS_SWAP(buffer1))//yes, this works on nifti-1 also { swapped = true; swapHeaderBytes(buffer1); } setupFrom(buffer1); } else { throw DataFileException(inFile.getFilename() + " is not a valid NIfTI file"); } } catch (DataFileException& e) {//catch and throw in order to add filename info 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) { 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 } } 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) { if (header.sizeof_hdr != sizeof(nifti_1_header)) throw DataFileException("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]"); 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.vox_offset < 352) throw DataFileException("incorrect vox_offset"); int numBits = typeToNumBits(header.datatype); if (header.bitpix != numBits) CaretLogWarning("datatype disagrees with bitpix"); 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 { m_header.magic[i] = header.magic[i]; m_header.srow_x[i] = header.srow_x[i];//slight hack - nifti-1 magic and srows both happen to be 4 long m_header.srow_y[i] = header.srow_y[i]; m_header.srow_z[i] = header.srow_z[i]; } m_header.datatype = header.datatype; m_header.bitpix = header.bitpix; for (int i = 0; i < 8; ++i) { m_header.dim[i] = header.dim[i]; m_header.pixdim[i] = header.pixdim[i]; } m_header.intent_p1 = header.intent_p1; m_header.intent_p2 = header.intent_p2; m_header.intent_p3 = header.intent_p3; m_header.vox_offset = header.vox_offset;//technically, this could result in integer overflow, if the header extensions total exabytes in size m_header.scl_slope = header.scl_slope; m_header.scl_inter = header.scl_inter; m_header.cal_max = header.cal_max; m_header.cal_min = header.cal_min; m_header.slice_duration = header.slice_duration; m_header.toffset = header.toffset; m_header.slice_start = header.slice_start; m_header.slice_end = header.slice_end; for (int i = 0; i < 80; ++i) m_header.descrip[i] = header.descrip[i]; for (int i = 0; i < 24; ++i) m_header.aux_file[i] = header.aux_file[i]; m_header.qform_code = header.qform_code; m_header.sform_code = header.sform_code; m_header.quatern_b = header.quatern_b; m_header.quatern_c = header.quatern_c; m_header.quatern_d = header.quatern_d; m_header.qoffset_x = header.qoffset_x; m_header.qoffset_y = header.qoffset_y; m_header.qoffset_z = header.qoffset_z; m_header.slice_code = header.slice_code; m_header.xyzt_units = header.xyzt_units; 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; } void NiftiHeader::setupFrom(const nifti_2_header& header) { if (header.sizeof_hdr != sizeof(nifti_2_header)) throw DataFileException("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.dim[0] < 1 || header.dim[0] > 7) throw DataFileException("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.vox_offset < 352) throw DataFileException("incorrect vox_offset"); if (header.bitpix != typeToNumBits(header.datatype)) CaretLogWarning("datatype disagrees with bitpix"); memcpy(&m_header, &header, sizeof(nifti_2_header)); } int NiftiHeader::typeToNumBits(const int64_t& type) { switch (type) { case DT_BINARY: return 1; break; case NIFTI_TYPE_INT8: case NIFTI_TYPE_UINT8: return 8; break; case NIFTI_TYPE_INT16: case NIFTI_TYPE_UINT16: return 16; break; case NIFTI_TYPE_RGB24: return 24; break; case NIFTI_TYPE_INT32: case NIFTI_TYPE_UINT32: case NIFTI_TYPE_FLOAT32: return 32; break; case NIFTI_TYPE_INT64: case NIFTI_TYPE_UINT64: case NIFTI_TYPE_FLOAT64: case NIFTI_TYPE_COMPLEX64: return 64; break; case NIFTI_TYPE_FLOAT128: case NIFTI_TYPE_COMPLEX128: return 128; break; case NIFTI_TYPE_COMPLEX256: return 256; break; default: throw DataFileException("incorrect datatype code"); } } void NiftiHeader::swapHeaderBytes(nifti_1_header& header) { ByteSwapping::swap(header.sizeof_hdr);//by order of fields in nifti-1 header, skip unused because we don't store their data ByteSwapping::swapArray(header.dim, 8); ByteSwapping::swap(header.intent_p1); ByteSwapping::swap(header.intent_p2); ByteSwapping::swap(header.intent_p3); ByteSwapping::swap(header.intent_code); ByteSwapping::swap(header.datatype); ByteSwapping::swap(header.bitpix); ByteSwapping::swap(header.slice_start); ByteSwapping::swapArray(header.pixdim, 8); ByteSwapping::swap(header.vox_offset); ByteSwapping::swap(header.scl_slope); ByteSwapping::swap(header.scl_inter); ByteSwapping::swap(header.slice_end); ByteSwapping::swap(header.cal_max); ByteSwapping::swap(header.cal_min); ByteSwapping::swap(header.slice_duration); ByteSwapping::swap(header.toffset); ByteSwapping::swap(header.qform_code); ByteSwapping::swap(header.sform_code); ByteSwapping::swap(header.quatern_b); ByteSwapping::swap(header.quatern_c); ByteSwapping::swap(header.quatern_d); ByteSwapping::swap(header.qoffset_x); ByteSwapping::swap(header.qoffset_y); ByteSwapping::swap(header.qoffset_z); ByteSwapping::swapArray(header.srow_x, 4); ByteSwapping::swapArray(header.srow_y, 4); ByteSwapping::swapArray(header.srow_z, 4); } void NiftiHeader::swapHeaderBytes(nifti_2_header& header) { ByteSwapping::swap(header.sizeof_hdr);//by order of fields in nifti-2 header ByteSwapping::swap(header.datatype); ByteSwapping::swap(header.bitpix); ByteSwapping::swapArray(header.dim, 8); ByteSwapping::swap(header.intent_p1); ByteSwapping::swap(header.intent_p2); ByteSwapping::swap(header.intent_p3); ByteSwapping::swapArray(header.pixdim, 8); ByteSwapping::swap(header.vox_offset); ByteSwapping::swap(header.scl_slope); ByteSwapping::swap(header.scl_inter); ByteSwapping::swap(header.cal_max); ByteSwapping::swap(header.cal_min); ByteSwapping::swap(header.slice_duration); ByteSwapping::swap(header.toffset); ByteSwapping::swap(header.slice_start); ByteSwapping::swap(header.slice_end); ByteSwapping::swap(header.qform_code); ByteSwapping::swap(header.sform_code); ByteSwapping::swap(header.quatern_b); ByteSwapping::swap(header.quatern_c); ByteSwapping::swap(header.quatern_d); ByteSwapping::swap(header.qoffset_x); ByteSwapping::swap(header.qoffset_y); ByteSwapping::swap(header.qoffset_z); ByteSwapping::swapArray(header.srow_x, 4); ByteSwapping::swapArray(header.srow_y, 4); ByteSwapping::swapArray(header.srow_z, 4); ByteSwapping::swap(header.slice_code); ByteSwapping::swap(header.xyzt_units); ByteSwapping::swap(header.intent_code); } 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()); 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) { nifti_2_header outHeader; prepareHeader(outHeader); voxOffset = outHeader.vox_offset; if (swapEndian) swapHeaderBytes(outHeader); outFile.write(&outHeader, sizeof(nifti_2_header)); } else if (version == 1) { nifti_1_header outHeader; prepareHeader(outHeader); voxOffset = outHeader.vox_offset; if (swapEndian) swapHeaderBytes(outHeader); outFile.write(&outHeader, sizeof(nifti_1_header)); } else { CaretAssert(0);//canWriteVersion should have said no throw DataFileException("internal error: NiftiHeader::canWriteVersion() returned true for unimplemented writing version"); } char extender[] = { 0, 0, 0, 0 };//at least until nifti-2 gets a new extension format, use the same code for both int numExtensions = (int)m_extensions.size(); if (numExtensions != 0) extender[0] = 1; outFile.write(extender, 4); for (int i = 0; i < numExtensions; ++i) { CaretAssert(m_extensions[i] != NULL); int64_t thisSize = 8 + m_extensions[i]->m_bytes.size();//8 is for the int32_t size and ecode for nifti-1 style extensions int paddingBytes = 0; if (thisSize % 16 != 0)//round up to nearest multiple of 16 { paddingBytes = 16 - (thisSize % 16); thisSize += paddingBytes; } CaretAssert(thisSize <= numeric_limits::max()); CaretAssert(thisSize + outFile.pos() <= voxOffset); int32_t outSize = thisSize; int32_t outEcode = m_extensions[i]->m_ecode; if (swapEndian) { ByteSwapping::swap(outSize); ByteSwapping::swap(outEcode); } outFile.write(&outSize, sizeof(int32_t)); outFile.write(&outEcode, sizeof(int32_t)); outFile.write(m_extensions[i]->m_bytes.data(), m_extensions[i]->m_bytes.size()); if (paddingBytes != 0) outFile.write(padding, paddingBytes); } CaretAssert(outFile.pos() == voxOffset); m_header.vox_offset = voxOffset;//update internal state to reflect the state that was written to the file m_version = version; m_isSwapped = swapEndian; } void NiftiHeader::prepareHeader(nifti_1_header& header) const { CaretAssert(canWriteVersion(1));//programmer error to call this if it isn't possible header.sizeof_hdr = sizeof(nifti_1_header);//do static things first const char magic[] = "n+1\0";//only support single-file nifti for (int i = 0; i < 4; ++i) header.magic[i] = magic[i]; for (int i = 0; i < 10; ++i) header.data_type[i] = 0;//then zero unused things for (int i = 0; i < 18; ++i) header.db_name[i] = 0; header.extents = 0; header.session_error = 0; header.regular = 0; header.glmax = 0; header.glmin = 0; header.dim_info = m_header.dim_info;//by order of fields in nifti-1 header, skipping unused and static for (int i = 0; i < 8; ++i) header.dim[i] = m_header.dim[i];//canWriteVersion should have already checked that this is okay, first in write(), then asserted above header.intent_p1 = m_header.intent_p1;//theoretically, this could be a problem wih large exponents, or if extremely high precision is required header.intent_p2 = m_header.intent_p2;//but we don't use them at all currently, so we don't care header.intent_p3 = m_header.intent_p3; header.intent_code = m_header.intent_code; header.datatype = m_header.datatype; header.bitpix = typeToNumBits(m_header.datatype);//in case we ever accept wrong bitpix with a warning, NEVER write wrong bitpix header.slice_start = m_header.slice_start; for (int i = 0; i < 8; ++i) header.pixdim[i] = m_header.pixdim[i];//more double to float conversion header.vox_offset = computeVoxOffset(1);//again, canWriteVersion should have checked that this, and later conversions, are okay CaretAssert(header.vox_offset >= 352); header.scl_slope = m_header.scl_slope; header.scl_inter = m_header.scl_inter; header.slice_end = m_header.slice_end; header.slice_code = m_header.slice_code; header.xyzt_units = m_header.xyzt_units; header.cal_min = m_header.cal_min; header.cal_max = m_header.cal_max; header.slice_duration = m_header.slice_duration; header.toffset = m_header.toffset; for (int i = 0; i < 80; ++i) header.descrip[i] = m_header.descrip[i]; for (int i = 0; i < 24; ++i) header.aux_file[i] = m_header.aux_file[i]; header.qform_code = m_header.qform_code; header.sform_code = m_header.sform_code; header.quatern_b = m_header.quatern_b; header.quatern_c = m_header.quatern_c; header.quatern_d = m_header.quatern_d; header.qoffset_x = m_header.qoffset_x; header.qoffset_y = m_header.qoffset_y; header.qoffset_z = m_header.qoffset_z; for (int i = 0; i < 4; ++i) { header.srow_x[i] = m_header.srow_x[i]; header.srow_y[i] = m_header.srow_y[i]; header.srow_z[i] = m_header.srow_z[i]; } for (int i = 0; i < 16; ++i) header.intent_name[i] = m_header.intent_name[i]; } void NiftiHeader::prepareHeader(nifti_2_header& header) const { CaretAssert(canWriteVersion(2)); memcpy(&header, &m_header, sizeof(nifti_2_header));//first copy everything, then fix static and computed fields header.sizeof_hdr = sizeof(nifti_2_header); const char magic[] = "n+2\0\r\n\032\n"; for (int i = 0; i < 8; ++i) header.magic[i] = magic[i]; header.bitpix = typeToNumBits(header.datatype); header.vox_offset = computeVoxOffset(2); for (int i = 0; i < 15; ++i) header.unused_str[i] = 0;//in case we read in a header where these bytes weren't zero } connectome-workbench-1.2.3+git41-gc4c6c90/src/Nifti/NiftiHeader.h000066400000000000000000000074131300200146000241550ustar00rootroot00000000000000#ifndef __NIFTI_HEADER_H__ #define __NIFTI_HEADER_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 "CaretBinaryFile.h" #include "VolumeBase.h" //for AbstractHeader, AbstravtVolumeExtension #include "nifti1.h" #include "nifti2.h" #include namespace caret { struct NiftiExtension { int32_t m_ecode; std::vector m_bytes; }; struct NiftiHeader : public AbstractHeader { std::vector > m_extensions;//allow direct access to the extensions NiftiHeader(); NiftiHeader(const NiftiHeader& rhs); NiftiHeader& operator=(const NiftiHeader& rhs); void read(CaretBinaryFile& inFile); void write(CaretBinaryFile& outFile, const int& version = 1, const bool& swapEndian = false); bool canWriteVersion(const int& version) const; bool isSwapped() const { return m_isSwapped; } int version() const { return m_version; } HeaderType getType() const { return NIFTI; } AbstractHeader* clone() const; std::vector getDimensions() const; std::vector > getSForm() const; int64_t getDataOffset() const { return m_header.vox_offset; } int16_t getDataType() const { return m_header.datatype; } 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 QString toString() const; void setDimensions(const std::vector& dimsIn); void setSForm(const std::vector > &sForm); void setIntent(const int32_t& code, const char name[16]); void setDataType(const int16_t& type); void clearDataScaling(); void setDataScaling(const double& mult, const double& offset); ///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: nifti_2_header m_header;//storage for header values regardless of version int m_version; bool m_isSwapped; static void swapHeaderBytes(nifti_1_header &header); 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); static int typeToNumBits(const int64_t& type); int64_t computeVoxOffset(const int& version) const; }; } #endif //__NIFTI_HEADER_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Nifti/NiftiIO.cxx000066400000000000000000000072011300200146000236420ustar00rootroot00000000000000/*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 "NiftiIO.h" #include "DataFileException.h" using namespace std; using namespace caret; void NiftiIO::openRead(const QString& filename) { m_file.open(filename); m_header.read(m_file); if (m_header.getDataType() == DT_BINARY) { throw DataFileException("file uses the binary datatype, which is unsupported: " + filename); } m_dims = m_header.getDimensions(); } void NiftiIO::writeNew(const QString& filename, const NiftiHeader& header, const int& version, const bool& withRead, const bool& swapEndian) { if (header.getDataType() == DT_BINARY) { throw DataFileException("writing NIFTI with binary datatype is unsupported"); } if (withRead) { m_file.open(filename, CaretBinaryFile::READ_WRITE_TRUNCATE);//for cifti on-disk writing, replace structure with along row needs to RMW } else { m_file.open(filename, CaretBinaryFile::WRITE_TRUNCATE); } m_header = header; m_header.write(m_file, version, swapEndian); m_dims = m_header.getDimensions(); } void NiftiIO::close() { m_file.close(); m_dims.clear(); } 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"); } } int NiftiIO::numBytesPerElem() { switch (m_header.getDataType()) { case NIFTI_TYPE_INT8: case NIFTI_TYPE_UINT8: case NIFTI_TYPE_RGB24: return 1; break; case NIFTI_TYPE_INT16: case NIFTI_TYPE_UINT16: return 2; break; case NIFTI_TYPE_INT32: case NIFTI_TYPE_UINT32: case NIFTI_TYPE_FLOAT32: case NIFTI_TYPE_COMPLEX64: return 4; break; case NIFTI_TYPE_INT64: case NIFTI_TYPE_UINT64: case NIFTI_TYPE_FLOAT64: case NIFTI_TYPE_COMPLEX128: return 8; break; case NIFTI_TYPE_FLOAT128: case NIFTI_TYPE_COMPLEX256: return 16; break; default: CaretAssert(0); throw DataFileException("internal error, report what you did to the developers"); } } connectome-workbench-1.2.3+git41-gc4c6c90/src/Nifti/NiftiIO.h000066400000000000000000000312301300200146000232660ustar00rootroot00000000000000#ifndef __NIFTI_IO_H__ #define __NIFTI_IO_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 "ByteSwapping.h" #include "CaretAssert.h" #include "CaretBinaryFile.h" #include "CaretMutex.h" #include "DataFileException.h" #include "NiftiHeader.h" #include #include #include #include namespace caret { class NiftiIO { CaretBinaryFile m_file; NiftiHeader m_header; std::vector m_dims; std::vector m_scratch;//scratch memory for byteswapping, type conversion, etc CaretMutex m_mutex;//protect multithreaded calls from each other int numBytesPerElem();//for resizing scratch template 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 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); QString getFilename() const { return m_file.getFilename(); } void overrideDimensions(const std::vector& newDims) { m_dims = newDims; }//HACK: deal with reading/writing CIFTI-1's broken headers void close(); const NiftiHeader& getHeader() const { return m_header; } const std::vector& getDimensions() const { return m_dims; } int getNumComponents() const; //to read/write 1 frame of a standard volume file, call with fullDims = 3, indexSelect containing indexes for any of dims 4-7 that exist //NOTE: you need to provide storage for all components within the range, if getNumComponents() == 3 and fullDims == 0, you need 3 elements allocated template void readData(T* dataOut, const int& fullDims, const std::vector& indexSelect, const bool& tolerateShortRead = false); template void writeData(const T* dataIn, const int& fullDims, const std::vector& indexSelect); }; template void NiftiIO::readData(T* dataOut, const int& fullDims, const std::vector& indexSelect, const bool& tolerateShortRead) { CaretAssert(fullDims >= 0 && fullDims <= (int)m_dims.size()); CaretAssert((size_t)fullDims + indexSelect.size() == m_dims.size());//could be >=, but should catch more stupid mistakes as == int64_t numElems = getNumComponents();//for now, calculate read size on the fly, as the read call will be the slowest part int curDim; for (curDim = 0; curDim < fullDims; ++curDim) { numElems *= m_dims[curDim]; } int64_t numDimSkip = numElems, numSkip = 0; for (; curDim < (int)m_dims.size(); ++curDim) { CaretAssert(indexSelect[curDim - fullDims] >= 0 && indexSelect[curDim - fullDims] < m_dims[curDim]); numSkip += indexSelect[curDim - fullDims] * numDimSkip; numDimSkip *= m_dims[curDim]; } CaretMutexLocker locked(&m_mutex);//protect starting with resizing until we are done converting, because we use an internal variable for scratch space //we can't guarantee that the output memory is enough to use as scratch space, as we might be doing a narrowing conversion //we are doing FILE ACCESS, so cpu performance isn't really something to worry about m_scratch.resize(numElems * numBytesPerElem()); m_file.seek(numSkip * numBytesPerElem() + m_header.getDataOffset()); int64_t numRead = 0; m_file.read(m_scratch.data(), m_scratch.size(), &numRead); if ((numRead != (int64_t)m_scratch.size() && !tolerateShortRead) || numRead < 0)//for now, assume read giving -1 is always a problem { throw DataFileException("error while reading from nifti file '" + m_file.getFilename() + "'"); } switch (m_header.getDataType()) { case NIFTI_TYPE_UINT8: case NIFTI_TYPE_RGB24://handled by components convertRead(dataOut, (uint8_t*)m_scratch.data(), numElems); break; case NIFTI_TYPE_INT8: convertRead(dataOut, (int8_t*)m_scratch.data(), numElems); break; case NIFTI_TYPE_UINT16: convertRead(dataOut, (uint16_t*)m_scratch.data(), numElems); break; case NIFTI_TYPE_INT16: convertRead(dataOut, (int16_t*)m_scratch.data(), numElems); break; case NIFTI_TYPE_UINT32: convertRead(dataOut, (uint32_t*)m_scratch.data(), numElems); break; case NIFTI_TYPE_INT32: convertRead(dataOut, (int32_t*)m_scratch.data(), numElems); break; case NIFTI_TYPE_UINT64: convertRead(dataOut, (uint64_t*)m_scratch.data(), numElems); break; case NIFTI_TYPE_INT64: convertRead(dataOut, (int64_t*)m_scratch.data(), numElems); break; case NIFTI_TYPE_FLOAT32: case NIFTI_TYPE_COMPLEX64://components convertRead(dataOut, (float*)m_scratch.data(), numElems); break; case NIFTI_TYPE_FLOAT64: case NIFTI_TYPE_COMPLEX128: convertRead(dataOut, (double*)m_scratch.data(), numElems); break; case NIFTI_TYPE_FLOAT128: case NIFTI_TYPE_COMPLEX256: convertRead(dataOut, (long double*)m_scratch.data(), numElems); break; default: CaretAssert(0); throw DataFileException("internal error, tell the developers what you just tried to do"); } } template void NiftiIO::writeData(const T* dataIn, const int& fullDims, const std::vector& indexSelect) { CaretAssert(fullDims >= 0 && fullDims <= (int)m_dims.size()); CaretAssert((size_t)fullDims + indexSelect.size() == m_dims.size());//could be >=, but should catch more stupid mistakes as == int64_t numElems = getNumComponents();//for now, calculate read size on the fly, as the read call will be the slowest part int curDim; for (curDim = 0; curDim < fullDims; ++curDim) { numElems *= m_dims[curDim]; } int64_t numDimSkip = numElems, numSkip = 0; for (; curDim < (int)m_dims.size(); ++curDim) { CaretAssert(indexSelect[curDim - fullDims] >= 0 && indexSelect[curDim - fullDims] < m_dims[curDim]); numSkip += indexSelect[curDim - fullDims] * numDimSkip; numDimSkip *= m_dims[curDim]; } CaretMutexLocker locked(&m_mutex);//protect starting with resizing until we are done writing, because we use an internal variable for scratch space //we are doing FILE ACCESS, so cpu performance isn't really something to worry about m_scratch.resize(numElems * numBytesPerElem()); m_file.seek(numSkip * numBytesPerElem() + m_header.getDataOffset()); switch (m_header.getDataType()) { case NIFTI_TYPE_UINT8: case NIFTI_TYPE_RGB24://handled by components convertWrite((uint8_t*)m_scratch.data(), dataIn, numElems); break; case NIFTI_TYPE_INT8: convertWrite((int8_t*)m_scratch.data(), dataIn, numElems); break; case NIFTI_TYPE_UINT16: convertWrite((uint16_t*)m_scratch.data(), dataIn, numElems); break; case NIFTI_TYPE_INT16: convertWrite((int16_t*)m_scratch.data(), dataIn, numElems); break; case NIFTI_TYPE_UINT32: convertWrite((uint32_t*)m_scratch.data(), dataIn, numElems); break; case NIFTI_TYPE_INT32: convertWrite((int32_t*)m_scratch.data(), dataIn, numElems); break; case NIFTI_TYPE_UINT64: convertWrite((uint64_t*)m_scratch.data(), dataIn, numElems); break; case NIFTI_TYPE_INT64: convertWrite((int64_t*)m_scratch.data(), dataIn, numElems); break; case NIFTI_TYPE_FLOAT32: case NIFTI_TYPE_COMPLEX64://components convertWrite((float*)m_scratch.data(), dataIn, numElems); break; case NIFTI_TYPE_FLOAT64: case NIFTI_TYPE_COMPLEX128: convertWrite((double*)m_scratch.data(), dataIn, numElems); break; case NIFTI_TYPE_FLOAT128: case NIFTI_TYPE_COMPLEX256: convertWrite((long double*)m_scratch.data(), dataIn, numElems); break; default: CaretAssert(0); throw DataFileException("internal error, tell the developers what you just tried to do"); } m_file.write(m_scratch.data(), m_scratch.size()); } template void NiftiIO::convertRead(TO* out, FROM* in, const int64_t& count) { if (m_header.isSwapped()) { ByteSwapping::swapArray(in, count); } double mult, offset; bool doScale = m_header.getDataScaling(mult, offset); if (std::numeric_limits::is_integer)//do round to nearest when integer output type { if (doScale) { 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 } } else { for (int64_t i = 0; i < count; ++i) { out[i] = (TO)floor(0.5 + in[i]); } } } else { if (doScale) { for (int64_t i = 0; i < count; ++i) { out[i] = (TO)(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)in[i];//explicit cast to make sure the compiler doesn't squawk } } } } template void NiftiIO::convertWrite(TO* out, const FROM* in, const int64_t& count) { double mult, offset; bool doScale = m_header.getDataScaling(mult, offset); if (std::numeric_limits::is_integer)//do round to nearest when integer output type { 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 } } else { for (int64_t i = 0; i < count; ++i) { out[i] = (TO)floor(0.5 + in[i]); } } } else { if (doScale) { for (int64_t i = 0; i < count; ++i) { out[i] = (TO)(((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)in[i];//explicit cast to make sure the compiler doesn't squawk } } } if (m_header.isSwapped()) ByteSwapping::swapArray(out, count); } } #endif //__NIFTI_IO_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/OSMesaDummy/000077500000000000000000000000001300200146000227075ustar00rootroot00000000000000connectome-workbench-1.2.3+git41-gc4c6c90/src/OSMesaDummy/CMakeLists.txt000066400000000000000000000004551300200146000254530ustar00rootroot00000000000000 # # Name of Project # PROJECT(OSMesaDummy) # # Need Qt for reading from resource file. # INCLUDE(${QT_USE_FILE}) # # Create a library # ADD_LIBRARY(OSMesaDummy OSMesaDummy.h OSMesaDummy.c ) # # Include directories # INCLUDE_DIRECTORIES( ${CMAKE_SOURCE_DIR}/Brain ${CMAKE_SOURCE_DIR}/OSMesaDummy ) connectome-workbench-1.2.3+git41-gc4c6c90/src/OSMesaDummy/OSMesaDummy.c000066400000000000000000000045121300200146000252200ustar00rootroot00000000000000 /*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 "assert.h" #define __O_S_MESA_DUMMY_DECLARE__ #include "OSMesaDummy.h" #undef __O_S_MESA_DUMMY_DECLARE__ /* * THESE ARE DUMMY METHODS. * * THE COMMAND LINE USES OSMESA FOR RENDERING IMAGES USING OPENG * WITHOUT A DISPLAY. HOWEVER, WITH THE GUI, IF THE OSMESA * LIBRARY IS LINKED, IT SOMEHOW RESULTS IN THE MESA GL (OPENGL) * LIBRARY BEING LINKED INSTEAD OF THE SYSTEM'S HARDWARE OPENGL * LIBRARY AND EVERYTHING IS SCREWED UP. * * IT IS THE DISPLAY OF HELP INFORMATION FOR COMMANDS THAT INCLUDES * THE SHOW SCENE COMMAND THAT REQUIRES OSMESA. */ /*GLAPI*/ OSMesaContext /*GLAPIENTRY*/ OSMesaCreateContextExt(GLenum format, GLint depthBits, GLint stencilBits, GLint accumBits, OSMesaContext sharelist) { /* * NOTE: These are "C" functions (NOT "C++") and so the names * of the parameters cannot be removed to avoid a compilation * warning as missing parameter names is a compiler error in "C". */ if (format || depthBits || stencilBits || accumBits || sharelist) { } assert(0); return 0; } /*GLAPI*/ void /*GLAPIENTRY*/ OSMesaDestroyContext( OSMesaContext ctx ) { if (ctx) { } assert(0); } /*GLAPI*/ GLboolean /*GLAPIENTRY*/ OSMesaMakeCurrent( OSMesaContext ctx, void * buffer, GLenum type, GLsizei width, GLsizei height ) { if (ctx || buffer || type || width || height) { } assert(0); return GL_FALSE; } connectome-workbench-1.2.3+git41-gc4c6c90/src/OSMesaDummy/OSMesaDummy.h000066400000000000000000000027231300200146000252270ustar00rootroot00000000000000#ifndef __O_S_MESA_DUMMY_H__ #define __O_S_MESA_DUMMY_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 "CaretOpenGLInclude.h" typedef struct osmesa_context *OSMesaContext; /* * THESE ARE DUMMY METHODS. * * See the ".c" file for more info. */ /*GLAPI*/ OSMesaContext /*GLAPIENTRY*/ OSMesaCreateContextExt(GLenum format, GLint depthBits, GLint stencilBits, GLint accumBits, OSMesaContext sharelist); /*GLAPI*/ void /*GLAPIENTRY*/ OSMesaDestroyContext( OSMesaContext ctx ); /*GLAPI*/ GLboolean /*GLAPIENTRY*/ OSMesaMakeCurrent( OSMesaContext ctx, void *buffer, GLenum type, GLsizei width, GLsizei height ); #endif //__O_S_MESA_DUMMY_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Operations/000077500000000000000000000000001300200146000226675ustar00rootroot00000000000000connectome-workbench-1.2.3+git41-gc4c6c90/src/Operations/CMakeLists.txt000066400000000000000000000135731300200146000254400ustar00rootroot00000000000000# # Name of project # PROJECT (Operations) # # Need XML from Qt # SET(QT_DONT_USE_QTGUI) # # Add QT for includes # INCLUDE (${QT_USE_FILE}) # # Create the helper library # ADD_LIBRARY(Operations OperationAddToSpecFile.h OperationBackendAverageDenseROI.h OperationBackendAverageROICorrelation.h OperationBorderExportColorTable.h OperationBorderFileExportToCaret5.h OperationBorderLength.h OperationBorderMerge.h OperationCiftiChangeMapping.h OperationCiftiChangeTimestep.h OperationCiftiConvert.h OperationCiftiConvertToScalar.h OperationCiftiCopyMapping.h OperationCiftiCreateDenseFromTemplate.h OperationCiftiCreateParcellatedFromTemplate.h OperationCiftiCreateScalarSeries.h OperationCiftiEstimateFWHM.h OperationCiftiExportDenseMapping.h OperationCiftiLabelExportTable.h OperationCiftiLabelImport.h OperationCiftiMath.h OperationCiftiMerge.h OperationCiftiPalette.h OperationCiftiResampleDconnMemory.h OperationCiftiROIAverage.h OperationCiftiSeparateAll.h OperationCiftiStats.h OperationCiftiWeightedStats.h OperationConvertAffine.h OperationConvertFiberOrientations.h OperationConvertMatrix4ToMatrix2.h OperationConvertMatrix4ToWorkbenchSparse.h OperationConvertWarpfield.h OperationEstimateFiberBinghams.h OperationException.h OperationFileConvert.h OperationFileInformation.h OperationFociGetProjectionVertex.h OperationFociListCoords.h OperationGiftiConvert.h OperationLabelExportTable.h OperationLabelMask.h OperationLabelMerge.h OperationMetadataRemoveProvenance.h OperationMetadataStringReplace.h OperationMetricConvert.h OperationMetricLabelImport.h OperationMetricMask.h OperationMetricMath.h OperationMetricMerge.h OperationMetricPalette.h OperationMetricStats.h OperationMetricVertexSum.h OperationMetricWeightedStats.h OperationNiftiInformation.h OperationProbtrackXDotConvert.h OperationSceneFileMerge.h OperationSceneFileRelocate.h OperationSetMapName.h OperationSetMapNames.h OperationSetStructure.h OperationShowScene.h OperationSpecFileMerge.h OperationSpecFileRelocate.h OperationSurfaceClosestVertex.h OperationSurfaceCoordinatesToMetric.h OperationSurfaceCutResample.h OperationSurfaceFlipNormals.h OperationSurfaceGeodesicDistance.h OperationSurfaceGeodesicROIs.h OperationSurfaceInformation.h OperationSurfaceNormals.h OperationSurfaceVertexAreas.h OperationVolumeCapturePlane.h OperationVolumeCopyExtensions.h OperationVolumeCreate.h OperationVolumeLabelExportTable.h OperationVolumeLabelImport.h OperationVolumeMath.h OperationVolumeMerge.h OperationVolumePalette.h OperationVolumeReorient.h OperationVolumeSetSpace.h OperationVolumeStats.h OperationVolumeWeightedStats.h OperationWbsparseMergeDense.h OperationZipSceneFile.h OperationZipSpecFile.h OperationAddToSpecFile.cxx OperationBackendAverageDenseROI.cxx OperationBackendAverageROICorrelation.cxx OperationBorderExportColorTable.cxx OperationBorderFileExportToCaret5.cxx OperationBorderLength.cxx OperationBorderMerge.cxx OperationCiftiChangeMapping.cxx OperationCiftiChangeTimestep.cxx OperationCiftiConvert.cxx OperationCiftiConvertToScalar.cxx OperationCiftiCopyMapping.cxx OperationCiftiCreateDenseFromTemplate.cxx OperationCiftiCreateParcellatedFromTemplate.cxx OperationCiftiCreateScalarSeries.cxx OperationCiftiEstimateFWHM.cxx OperationCiftiExportDenseMapping.cxx OperationCiftiLabelExportTable.cxx OperationCiftiLabelImport.cxx OperationCiftiMath.cxx OperationCiftiMerge.cxx OperationCiftiPalette.cxx OperationCiftiResampleDconnMemory.cxx OperationCiftiROIAverage.cxx OperationCiftiSeparateAll.cxx OperationCiftiStats.cxx OperationCiftiWeightedStats.cxx OperationConvertAffine.cxx OperationConvertFiberOrientations.cxx OperationConvertMatrix4ToMatrix2.cxx OperationConvertMatrix4ToWorkbenchSparse.cxx OperationConvertWarpfield.cxx OperationException.cxx OperationEstimateFiberBinghams.cxx OperationFileConvert.cxx OperationFileInformation.cxx OperationFociGetProjectionVertex.cxx OperationFociListCoords.cxx OperationGiftiConvert.cxx OperationLabelExportTable.cxx OperationLabelMask.cxx OperationLabelMerge.cxx OperationMetadataRemoveProvenance.cxx OperationMetadataStringReplace.cxx OperationMetricConvert.cxx OperationMetricLabelImport.cxx OperationMetricMask.cxx OperationMetricMath.cxx OperationMetricMerge.cxx OperationMetricPalette.cxx OperationMetricStats.cxx OperationMetricVertexSum.cxx OperationMetricWeightedStats.cxx OperationNiftiInformation.cxx OperationProbtrackXDotConvert.cxx OperationSceneFileMerge.cxx OperationSceneFileRelocate.cxx OperationSetMapName.cxx OperationSetMapNames.cxx OperationSetStructure.cxx OperationShowScene.cxx OperationSpecFileMerge.cxx OperationSpecFileRelocate.cxx OperationSurfaceClosestVertex.cxx OperationSurfaceCoordinatesToMetric.cxx OperationSurfaceCutResample.cxx OperationSurfaceFlipNormals.cxx OperationSurfaceGeodesicDistance.cxx OperationSurfaceGeodesicROIs.cxx OperationSurfaceInformation.cxx OperationSurfaceNormals.cxx OperationSurfaceVertexAreas.cxx OperationVolumeCapturePlane.cxx OperationVolumeCopyExtensions.cxx OperationVolumeCreate.cxx OperationVolumeLabelExportTable.cxx OperationVolumeLabelImport.cxx OperationVolumeMath.cxx OperationVolumeMerge.cxx OperationVolumePalette.cxx OperationVolumeReorient.cxx OperationVolumeSetSpace.cxx OperationVolumeStats.cxx OperationVolumeWeightedStats.cxx OperationWbsparseMergeDense.cxx OperationZipSceneFile.cxx OperationZipSpecFile.cxx ) # # Find Headers # INCLUDE_DIRECTORIES( ${CMAKE_SOURCE_DIR}/Algorithms ${CMAKE_SOURCE_DIR}/Annotations ${CMAKE_SOURCE_DIR}/Operations ${CMAKE_SOURCE_DIR}/OperationsBase ${CMAKE_SOURCE_DIR}/Brain ${CMAKE_SOURCE_DIR}/Charting ${CMAKE_SOURCE_DIR}/Palette ${CMAKE_SOURCE_DIR}/FilesBase ${CMAKE_SOURCE_DIR}/Files ${CMAKE_SOURCE_DIR}/Gifti ${CMAKE_SOURCE_DIR}/Cifti ${CMAKE_SOURCE_DIR}/Nifti ${QUAZIP_INCLUDE_DIRS} ${CMAKE_SOURCE_DIR}/Scenes ${CMAKE_SOURCE_DIR}/Xml ${CMAKE_SOURCE_DIR}/Common ) # # Mesa Library used by show scene command # IF (OSMESA_FOUND) ADD_DEFINITIONS(${OSMESA_DEFINITION}) INCLUDE_DIRECTORIES(${OSMESA_INCLUDE_DIRECTORY}) ENDIF (OSMESA_FOUND) connectome-workbench-1.2.3+git41-gc4c6c90/src/Operations/OperationAddToSpecFile.cxx000066400000000000000000000063061300200146000277070ustar00rootroot00000000000000/*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 "OperationAddToSpecFile.h" #include "OperationException.h" #include "SpecFile.h" #include "FileInformation.h" #include using namespace caret; using namespace std; AString OperationAddToSpecFile::getCommandSwitch() { return "-add-to-spec-file"; } AString OperationAddToSpecFile::getShortDescription() { return "ADD A FILE TO A SPECIFICATION FILE"; } OperationParameters* OperationAddToSpecFile::getParameters() { OperationParameters* ret = new OperationParameters(); ret->addStringParameter(1, "specfile", "the specification file to add to"); ret->addStringParameter(2, "structure", "the structure of the data file"); ret->addStringParameter(3, "filename", "the path to the file"); AString myText = AString("The resulting spec file overwrites the existing spec file. If the spec file doesn't exist, ") + "it is created with default metadata. The structure argument must be one of the following:\n\n"; vector myStructureEnums; StructureEnum::getAllEnums(myStructureEnums); for (int i = 0; i < (int)myStructureEnums.size(); ++i) { myText += StructureEnum::toName(myStructureEnums[i]) + "\n"; } ret->setHelpText(myText); return ret; } void OperationAddToSpecFile::useParameters(OperationParameters* myParams, ProgressObject* myProgObj) { LevelProgress myProgress(myProgObj); bool ok = false; AString mySpecName = myParams->getString(1);//spec file AString myStructureName = myParams->getString(2);//file structure StructureEnum::Enum myStrucure = StructureEnum::fromName(myStructureName, &ok); if (!ok) { throw OperationException("unrecognized structure type"); } AString myDataFileName = myParams->getString(3);//file path FileInformation myDataFileInfo(myDataFileName); if (!myDataFileInfo.exists()) { throw OperationException("data file not found"); } DataFileTypeEnum::Enum myType = DataFileTypeEnum::fromFileExtension(myDataFileName, &ok); if (!ok) { throw OperationException("unrecognized data file type"); } SpecFile mySpec; FileInformation mySpecInfo(mySpecName); if (mySpecInfo.exists()) { mySpec.readFile(mySpecName); } else { mySpec.setFileName(mySpecName); } mySpec.addDataFile(myType, myStrucure, myDataFileName, true, false, true); mySpec.writeFile(mySpecName); } connectome-workbench-1.2.3+git41-gc4c6c90/src/Operations/OperationAddToSpecFile.h000066400000000000000000000026251300200146000273340ustar00rootroot00000000000000#ifndef __OPERATION_ADD_TO_SPEC_FILE_H__ #define __OPERATION_ADD_TO_SPEC_FILE_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 "AbstractOperation.h" namespace caret { class OperationAddToSpecFile : public AbstractOperation { public: static OperationParameters* getParameters(); static void useParameters(OperationParameters* myParams, ProgressObject* myProgObj); static AString getCommandSwitch(); static AString getShortDescription(); }; typedef TemplateAutoOperation AutoOperationAddToSpecFile; } #endif //__OPERATION_ADD_TO_SPEC_FILE_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Operations/OperationBackendAverageDenseROI.cxx000066400000000000000000000132241300200146000314510ustar00rootroot00000000000000/*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 "AString.h" #include "ByteOrderEnum.h" #include "ByteSwapping.h" #include "CaretPointer.h" #include "CiftiFile.h" #include "FileInformation.h" #include "OperationBackendAverageDenseROI.h" #include "OperationException.h" #include #include #include using namespace caret; using namespace std; AString OperationBackendAverageDenseROI::getCommandSwitch() { return "-backend-average-dense-roi"; } AString OperationBackendAverageDenseROI::getShortDescription() { return "CONNECTOME DB BACKEND COMMAND FOR CIFTI AVERAGE DENSE ROI"; } OperationParameters* OperationBackendAverageDenseROI::getParameters() { OperationParameters* ret = new OperationParameters(); ret->addStringParameter(1, "index-list", "comma separated list of cifti indexes to average"); ret->addStringParameter(2, "out-file", "file to write the average row to"); ret->setHelpText( AString("This command is probably not the one you are looking for, try -cifti-average-dense-roi. ") + "It takes the list of cifti files to average from standard input, and writes its output as little endian, " + "32-bit integer of row size followed by the row as 32-bit floats." ); return ret; } void OperationBackendAverageDenseROI::useParameters(OperationParameters* myParams, ProgressObject* myProgObj) { LevelProgress myProgress(myProgObj); AString indexListString, outfileName; indexListString = myParams->getString(1); outfileName = myParams->getString(2); CiftiXML baseXML; bool ok = false; vector indexList; QStringList indexStrings = indexListString.split(","); int numStrings = (int)indexStrings.size(); indexList.resize(numStrings); for (int i = 0; i < numStrings; ++i) { indexList[i] = indexStrings[i].toInt(&ok); if (!ok) { throw OperationException("failed to parse '" + indexStrings[i] + "' as integer"); } if (indexList[i] < 0) { throw OperationException("negative integers are not valid cifti indexes"); } } vector > ciftiList; string myLine; while (cin.good()) { if (!getline(cin, myLine)) { break; } if (myLine == "") { continue;//skip blank lines } FileInformation ciftiFileInfo(myLine.c_str()); if (!ciftiFileInfo.exists()) { throw OperationException(AString("file does not exist: ") + myLine.c_str()); } CaretPointer tempCifti(new CiftiFile(myLine.c_str()));//can't skip parsing XML, as different arguments could be different cifti versions, which results in different dimension order ciftiList.push_back(tempCifti); } int numCifti = (int)ciftiList.size(); if (numCifti > 0) { baseXML = ciftiList[0]->getCiftiXML(); if (baseXML.getNumberOfDimensions() != 2) throw OperationException("this command currently only supports 2D cifti"); int numRows = baseXML.getDimensionLength(CiftiXML::ALONG_COLUMN); int rowSize = baseXML.getDimensionLength(CiftiXML::ALONG_ROW); vector accum(rowSize, 0.0); vector rowScratch(rowSize); for (int i = 0; i < numCifti; ++i) { if (baseXML != ciftiList[i]->getCiftiXML())//equality testing is smart, compares mapping equivalence, despite multiple ways to specify some mappings { throw OperationException("error, cifti header of file #" + AString::number(i + 1) + " doesn't match"); } for (int j = 0; j < numStrings; ++j) { if (indexList[j] >= numRows) { throw OperationException("error, cifti index outside number of rows"); } ciftiList[i]->getRow(rowScratch.data(), indexList[j]); for (int k = 0; k < rowSize; ++k) { accum[k] += rowScratch[k]; } } } for (int k = 0; k < rowSize; ++k) { rowScratch[k] = accum[k] / numCifti / numStrings; } int32_t outSize = rowSize; if (ByteOrderEnum::isSystemBigEndian()) { ByteSwapping::swapBytes(rowScratch.data(), rowSize);//beware, we are doing the byteswapping in place ByteSwapping::swapBytes(&outSize, 1); } ofstream outfile(outfileName.toLocal8Bit().constData(), ios_base::out | ios_base::binary | ios_base::trunc); if (!outfile.write((char*)&outSize, 4)) { throw OperationException("error writing output"); } if (!outfile.write((char*)rowScratch.data(), rowSize * sizeof(float))) { throw OperationException("error writing output"); } outfile.close(); } } connectome-workbench-1.2.3+git41-gc4c6c90/src/Operations/OperationBackendAverageDenseROI.h000066400000000000000000000027131300200146000310770ustar00rootroot00000000000000#ifndef __OPERATION_BACKEND_AVERAGE_DENSE_ROI_H__ #define __OPERATION_BACKEND_AVERAGE_DENSE_ROI_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 "AbstractOperation.h" namespace caret { class OperationBackendAverageDenseROI : public AbstractOperation { public: static OperationParameters* getParameters(); static void useParameters(OperationParameters* myParams, ProgressObject* myProgObj); static AString getCommandSwitch(); static AString getShortDescription(); }; typedef TemplateAutoOperation AutoOperationBackendAverageDenseROI; } #endif //__OPERATION_BACKEND_AVERAGE_DENSE_ROI_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Operations/OperationBackendAverageROICorrelation.cxx000066400000000000000000000173761300200146000327100ustar00rootroot00000000000000/*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 "AString.h" #include "ByteOrderEnum.h" #include "ByteSwapping.h" #include "CaretOMP.h" #include "CaretPointer.h" #include "CiftiFile.h" #include "FileInformation.h" #include "OperationBackendAverageROICorrelation.h" #include "OperationException.h" #include #include #include using namespace caret; using namespace std; AString OperationBackendAverageROICorrelation::getCommandSwitch() { return "-backend-average-roi-correlation"; } AString OperationBackendAverageROICorrelation::getShortDescription() { return "CONNECTOME DB BACKEND COMMAND FOR CIFTI AVERAGE ROI CORRELATION"; } OperationParameters* OperationBackendAverageROICorrelation::getParameters() { OperationParameters* ret = new OperationParameters(); ret->addStringParameter(1, "index-list", "comma separated list of cifti indexes to average and then correlate"); ret->addStringParameter(2, "out-file", "file to write the average row to"); ret->setHelpText( AString("This command is probably not the one you are looking for, try -cifti-average-roi-correlation. ") + "It takes the list of cifti files to average from standard input, and writes its output as little endian, " + "32-bit integer of row size followed by the row as 32-bit floats." ); return ret; } void OperationBackendAverageROICorrelation::useParameters(OperationParameters* myParams, ProgressObject* myProgObj) { LevelProgress myProgress(myProgObj); AString indexListString, outfileName; indexListString = myParams->getString(1); outfileName = myParams->getString(2); CiftiXML baseXML;//TODO: remove when switching to raw reading bool ok = false; vector indexList; QStringList indexStrings = indexListString.split(","); int numStrings = (int)indexStrings.size(); indexList.resize(numStrings); for (int i = 0; i < numStrings; ++i) { indexList[i] = indexStrings[i].toInt(&ok); if (!ok) { throw OperationException("failed to parse '" + indexStrings[i] + "' as integer"); } if (indexList[i] < 0) { throw OperationException("negative integers are not valid cifti indexes"); } } vector > ciftiList; string myLine; while (cin.good()) { if (!getline(cin, myLine)) { break; } if (myLine == "") { continue;//skip blank lines } FileInformation ciftiFileInfo(myLine.c_str()); if (!ciftiFileInfo.exists()) { throw OperationException(AString("file does not exist: ") + myLine.c_str()); } CaretPointer tempCifti(new CiftiFile(myLine.c_str()));//can't skip parsing XML, as different arguments could be different cifti versions, which results in different dimension order ciftiList.push_back(tempCifti); } int numCifti = (int)ciftiList.size(); if (numCifti > 0) { baseXML = ciftiList[0]->getCiftiXML(); if (baseXML.getNumberOfDimensions() != 2) throw OperationException("operation only supports 2D cifti files"); int rowSize = baseXML.getDimensionLength(CiftiXML::ALONG_ROW); vector accum(rowSize, 0.0); vector rowScratch(rowSize); for (int i = 0; i < numCifti; ++i) { if (!baseXML.approximateMatch(ciftiList[i]->getCiftiXML()))//equality testing is smart, compares mapping equivalence, despite multiple ways to specify some mappings { throw OperationException("error, cifti header of file #" + AString::number(i + 1) + " doesn't match"); } processCifti(ciftiList[i], indexList, rowScratch); for (int k = 0; k < rowSize; ++k) { accum[k] += rowScratch[k]; } } for (int k = 0; k < rowSize; ++k) { rowScratch[k] = accum[k] / numCifti / numStrings; } int32_t outSize = rowSize; if (ByteOrderEnum::isSystemBigEndian()) { ByteSwapping::swapBytes(rowScratch.data(), rowSize);//beware, we are doing the byteswapping in place ByteSwapping::swapBytes(&outSize, 1); } ofstream outfile(outfileName.toLocal8Bit().constData(), ios_base::out | ios_base::binary | ios_base::trunc); if (!outfile.write((char*)&outSize, 4)) { throw OperationException("error writing output"); } if (!outfile.write((char*)rowScratch.data(), rowSize * sizeof(float))) { throw OperationException("error writing output"); } outfile.close(); } } void OperationBackendAverageROICorrelation::processCifti(const CiftiFile* myCifti, const vector& indexList, vector& output) { int rowSize = myCifti->getNumberOfColumns(); int colSize = myCifti->getNumberOfRows(); int listSize = (int)indexList.size(); vector accumarray(rowSize, 0.0); vector average(rowSize); for (int i = 0; i < listSize; ++i) { if (indexList[i] >= colSize)//we already checked for negatives { throw OperationException("cifti index too large"); } myCifti->getRow(average.data(), indexList[i]); for (int j = 0; j < rowSize; ++j) { accumarray[j] += average[j]; } } double accum = 0.0; for (int i = 0; i < rowSize; ++i) { average[i] = accumarray[i] / listSize; accum += average[i]; } float mean = accum / rowSize; accum = 0.0; for (int i = 0; i < rowSize; ++i) { average[i] -= mean; accum += average[i] * average[i]; } float rrs = sqrt(accum); int curRow = 0; #pragma omp CARET_PAR { vector rowscratch(rowSize); #pragma omp CARET_FOR schedule(dynamic) for (int i = 0; i < colSize; ++i) { int myRow; #pragma omp critical { myRow = curRow;//force sequential reading ++curRow; myCifti->getRow(rowscratch.data(), myRow);//but never read multiple rows at once from the same file } double tempaccum = 0.0;//compute mean of new row for (int j = 0; j < rowSize; ++j) { tempaccum += rowscratch[j]; } float thismean = tempaccum / rowSize; tempaccum = 0.0; double corraccum = 0.0;//correlate for (int j = 0; j < rowSize; ++j) { float tempf = rowscratch[j] - thismean; tempaccum += tempf * tempf;//compute rrs on the fly corraccum += tempf * average[j];//gather the correlation } corraccum /= rrs * sqrt(tempaccum); if (corraccum > 0.999999) corraccum = 0.999999; if (corraccum < -0.999999) corraccum = -0.999999; output[i] = 0.5 * log((1 + corraccum) / (1 - corraccum)); } } } connectome-workbench-1.2.3+git41-gc4c6c90/src/Operations/OperationBackendAverageROICorrelation.h000066400000000000000000000031751300200146000323250ustar00rootroot00000000000000#ifndef __OPERATION_BACKEND_AVERAGE_ROI_CORRELATION_H__ #define __OPERATION_BACKEND_AVERAGE_ROI_CORRELATION_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 "AbstractOperation.h" #include namespace caret { class OperationBackendAverageROICorrelation : public AbstractOperation { static void processCifti(const CiftiFile* myCifti, const std::vector& indexList, std::vector& output); public: static OperationParameters* getParameters(); static void useParameters(OperationParameters* myParams, ProgressObject* myProgObj); static AString getCommandSwitch(); static AString getShortDescription(); }; typedef TemplateAutoOperation AutoOperationBackendAverageROICorrelation; } #endif //__OPERATION_BACKEND_AVERAGE_ROI_CORRELATION_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Operations/OperationBorderExportColorTable.cxx000066400000000000000000000076001300200146000316650ustar00rootroot00000000000000/*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 "OperationBorderExportColorTable.h" #include "OperationException.h" #include "Border.h" #include "BorderFile.h" #include "GiftiLabel.h" #include "GiftiLabelTable.h" #include #include using namespace caret; using namespace std; AString OperationBorderExportColorTable::getCommandSwitch() { return "-border-export-color-table"; } AString OperationBorderExportColorTable::getShortDescription() { return "WRITE BORDER NAMES AND COLORS AS TEXT"; } OperationParameters* OperationBorderExportColorTable::getParameters() { OperationParameters* ret = new OperationParameters(); ret->addBorderParameter(1, "border-file", "the input border file"); ret->addStringParameter(2, "table-out", "output - the output text file");//fake output formatting ret->createOptionalParameter(3, "-class-colors", "use class colors instead of the name colors"); ret->setHelpText( AString("Takes the names and colors of each border, and writes it to the same format as -metric-label-import expects. ") + "By default, the borders are colored by border name, specify -class-colors to color them by class instead. " + "The key values start at 1 and follow the order of the borders in the file." ); return ret; } int OperationBorderExportColorTable::floatTo255(const float& in) { return (int)floor(in * 255.0f + 0.5f); } void OperationBorderExportColorTable::useParameters(OperationParameters* myParams, ProgressObject* myProgObj) { LevelProgress myProgress(myProgObj); BorderFile* myFile = myParams->getBorder(1); AString outfileName = myParams->getString(2); bool useClassColors = myParams->getOptionalParameter(3)->m_present; ofstream outFile(outfileName.toLocal8Bit().constData()); if (!outFile) throw OperationException("failed to open output text file"); const GiftiLabelTable* tempTable = NULL; if (useClassColors) { tempTable = myFile->getClassColorTable(); } else { tempTable = myFile->getNameColorTable(); } int numBorders = myFile->getNumberOfBorders(); for (int i = 0; i < numBorders; ++i) { const Border* thisBorder = myFile->getBorder(i); outFile << thisBorder->getName() << endl; outFile << i + 1 << " ";//NOTE: NEVER use the label key const GiftiLabel* tempLabel = NULL; if (useClassColors) { tempLabel = tempTable->getLabelBestMatching(thisBorder->getClassName());//see BrainOpenGLFixedPipeline::drawSurfaceBorders(Surface* surface) } else { tempLabel = tempTable->getLabelBestMatching(thisBorder->getName()); } if (tempLabel == NULL) { outFile << "0 0 0 255" << endl; } else { outFile << floatTo255(tempLabel->getRed()) << " " << floatTo255(tempLabel->getGreen()) << " " << floatTo255(tempLabel->getBlue()) << " " << floatTo255(tempLabel->getAlpha()) << endl; } if (!outFile) throw OperationException("error writing to output text file"); } } connectome-workbench-1.2.3+git41-gc4c6c90/src/Operations/OperationBorderExportColorTable.h000066400000000000000000000027731300200146000313200ustar00rootroot00000000000000#ifndef __OPERATION_BORDER_EXPORT_COLOR_TABLE_H__ #define __OPERATION_BORDER_EXPORT_COLOR_TABLE_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 "AbstractOperation.h" namespace caret { class OperationBorderExportColorTable : public AbstractOperation { static int floatTo255(const float& in); public: static OperationParameters* getParameters(); static void useParameters(OperationParameters* myParams, ProgressObject* myProgObj); static AString getCommandSwitch(); static AString getShortDescription(); }; typedef TemplateAutoOperation AutoOperationBorderExportColorTable; } #endif //__OPERATION_BORDER_EXPORT_COLOR_TABLE_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Operations/OperationBorderFileExportToCaret5.cxx000066400000000000000000000125131300200146000320640ustar00rootroot00000000000000 /*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 "CaretAssert.h" #include "CaretLogger.h" #include "BorderFile.h" #include "DataFileException.h" #include "FileInformation.h" #include "OperationBorderFileExportToCaret5.h" #include "OperationException.h" #include "SurfaceFile.h" using namespace caret; /** * \class caret::OperationBorderFileExportToCaret5 * \brief Export border file to Caret5 file format */ /** * @return Command line switch */ AString OperationBorderFileExportToCaret5::getCommandSwitch() { return "-border-file-export-to-caret5"; } /** * @return Short description of operation */ AString OperationBorderFileExportToCaret5::getShortDescription() { return "EXPORT BORDER FILE TO CARET5 FILE FORMAT"; } /** * @return Parameters for operation */ OperationParameters* OperationBorderFileExportToCaret5::getParameters() { OperationParameters* ret = new OperationParameters(); ret->addStringParameter(1, "border-file", "workbench border file"); ret->addStringParameter(2, "output-file-prefix", "prefix for name of output caret5 border/borderproj/bordercolor files"); ParameterComponent* surfaceOpt = ret->createRepeatableParameter(3, "-surface", "specify an input surface"); surfaceOpt->addSurfaceParameter(1, "surface-in", "a surface file for unprojection of borders"); AString helpText("A Workbench border file may contain borders for multiple " "structures and borders that are both projected and " "unprojected. It also contains a color table for the borders. " "\n" "\n" "Caret5 has both border (unprojected) and border projection " "(projected) files. In addition, each Caret5 border or border " "projection file typically contains data for a single structure. " "Caret5 also uses a border color file that associates colors with " "the names of the borders. " "\n" "\n" "This command will try to output both Caret5 border and " "border projection files. Each output border/border projection " "file will contains data for one structure so there may be " "many files created. The structure name is included in the name of " "each border or border projection file that is created. " "\n" "\n" "One Caret5 border color file will also be produced by this " "command. " "\n" "\n" "Providing surface(s) as input parameters is optional, but recommended. " "Surfaces may be needed to create both projected and/or unprojected coordinates " "of borders. If there is a failure to produce an output border or " "border projection due to a missing surface with the matching structure, " "an error message will be displayed and some " "output files will not be created. " "\n" "\n" "When writing new files, this command will overwrite a file " "with the same name. " ""); ret->setHelpText(helpText); return ret; } /** * Use Parameters and perform operation */ void OperationBorderFileExportToCaret5::useParameters(OperationParameters* myParams, ProgressObject* myProgObj) { LevelProgress myProgress(myProgObj); AString borderFileName = FileInformation(myParams->getString(1)).getAbsoluteFilePath(); AString outputCaret5FilePrefix = FileInformation(myParams->getString(2)).getAbsoluteFilePath(); std::vector allSurfaces; const std::vector& surfaceInputs = *(myParams->getRepeatableParameterInstances(3)); const int32_t numSurfaceInputs = static_cast(surfaceInputs.size()); for (int32_t iSurf = 0; iSurf < numSurfaceInputs; iSurf++) { SurfaceFile* sf = surfaceInputs[iSurf]->getSurface(1); allSurfaces.push_back(sf); } try { BorderFile borderFile; borderFile.readFile(borderFileName); borderFile.exportToCaret5Format(allSurfaces, outputCaret5FilePrefix); } catch (const DataFileException& dfe) { throw OperationException(dfe); } } connectome-workbench-1.2.3+git41-gc4c6c90/src/Operations/OperationBorderFileExportToCaret5.h000066400000000000000000000030111300200146000315020ustar00rootroot00000000000000#ifndef __OPERATION_BORDER_FILE_EXPORT_TO_CARET5_H__ #define __OPERATION_BORDER_FILE_EXPORT_TO_CARET5_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 "AbstractOperation.h" namespace caret { class OperationBorderFileExportToCaret5 : public AbstractOperation { public: static OperationParameters* getParameters(); static void useParameters(OperationParameters* myParams, ProgressObject* myProgObj); static AString getCommandSwitch(); static AString getShortDescription(); }; typedef TemplateAutoOperation AutoOperationBorderFileExportToCaret5; } // namespace #endif //__OPERATION_BORDER_FILE_EXPORT_TO_CARET5_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Operations/OperationBorderLength.cxx000066400000000000000000000106251300200146000276570ustar00rootroot00000000000000/*LICENSE_START*/ /* * Copyright (C) 2015 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 "OperationBorderLength.h" #include "OperationException.h" #include "BorderFile.h" #include "BorderLengthHelper.h" #include "MetricFile.h" #include "SurfaceFile.h" #include "Border.h" #include using namespace caret; using namespace std; AString OperationBorderLength::getCommandSwitch() { return "-border-length"; } AString OperationBorderLength::getShortDescription() { return "REPORT LENGTH OF BORDERS"; } OperationParameters* OperationBorderLength::getParameters() { OperationParameters* ret = new OperationParameters(); ret->addBorderParameter(1, "border", "the input border file"); ret->addSurfaceParameter(2, "surface", "the surface to measure the borders on"); OptionalParameter* corrAreasOpt = ret->createOptionalParameter(3, "-corrected-areas", "vertex areas to use instead of computing them from the surface"); corrAreasOpt->addMetricParameter(1, "area-metric", "the corrected vertex areas, as a metric"); ret->createOptionalParameter(4, "-separate-pieces", "report lengths for multi-part borders as separate numbers"); ret->createOptionalParameter(5, "-hide-border-name", "don't print border name before each output"); ret->setHelpText( AString("For each border, print its length along the surface, in mm. ") + "If a border has multiple parts, their lengths are summed before printing, unless -separate-pieces is specified.\n\n" + "The -corrected-areas option is intended for when the length is not meaningfully measurable on individual surfaces, " + "it is only an approximate correction for the reduction in structure of a group average surface." ); return ret; } void OperationBorderLength::useParameters(OperationParameters* myParams, ProgressObject* myProgObj) { LevelProgress myProgress(myProgObj); BorderFile* myBorderFile = myParams->getBorder(1); SurfaceFile* mySurface = myParams->getSurface(2); MetricFile* corrAreas = NULL; const float* corrAreaData = NULL; OptionalParameter* corrAreasOpt = myParams->getOptionalParameter(3); if (corrAreasOpt->m_present) { corrAreas = corrAreasOpt->getMetric(1); corrAreaData = corrAreas->getValuePointerForColumn(0); } bool separate = myParams->getOptionalParameter(4)->m_present; bool hideNames = myParams->getOptionalParameter(5)->m_present; checkStructureMatch(myBorderFile, mySurface->getStructure(), "border file", "surface file has"); checkStructureMatch(corrAreas, mySurface->getStructure(), "corrected areas metric", "surface file has"); BorderMultiPartHelper myMultiHelper(myBorderFile); BorderLengthHelper myLengthHelper(mySurface, corrAreaData); int numBorders = (int)myMultiHelper.borderPieceList.size(); for (int i = 0; i < numBorders; ++i) { if (!hideNames) { cout << myBorderFile->getBorder(myMultiHelper.borderPieceList[i][0])->getName() << ": "; } int numParts = (int)myMultiHelper.borderPieceList[i].size(); if (separate) { for (int j = 0; j < numParts; ++j) { if (j > 0) { cout << " "; } cout << myLengthHelper.length(myBorderFile->getBorder(myMultiHelper.borderPieceList[i][j])); } } else { float total = 0.0f; for (int j = 0; j < numParts; ++j) { total += myLengthHelper.length(myBorderFile->getBorder(myMultiHelper.borderPieceList[i][j])); } cout << total; } cout << endl; } } connectome-workbench-1.2.3+git41-gc4c6c90/src/Operations/OperationBorderLength.h000066400000000000000000000026111300200146000273000ustar00rootroot00000000000000#ifndef __OPERATION_BORDER_LENGTH_H__ #define __OPERATION_BORDER_LENGTH_H__ /*LICENSE_START*/ /* * Copyright (C) 2015 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 OperationBorderLength : public AbstractOperation { public: static OperationParameters* getParameters(); static void useParameters(OperationParameters* myParams, ProgressObject* myProgObj); static AString getCommandSwitch(); static AString getShortDescription(); }; typedef TemplateAutoOperation AutoOperationBorderLength; } #endif //__OPERATION_BORDER_LENGTH_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Operations/OperationBorderMerge.cxx000066400000000000000000000146641300200146000275040ustar00rootroot00000000000000/*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 "OperationBorderMerge.h" #include "OperationException.h" #include "Border.h" #include "BorderFile.h" #include "GiftiLabelTable.h" #include #include #include using namespace caret; using namespace std; AString OperationBorderMerge::getCommandSwitch() { return "-border-merge"; } AString OperationBorderMerge::getShortDescription() { return "MERGE BORDER FILES INTO A NEW FILE"; } OperationParameters* OperationBorderMerge::getParameters() { OperationParameters* ret = new OperationParameters(); ret->addBorderOutputParameter(1, "border-file-out", "the output border file"); ParameterComponent* borderOpt = ret->createRepeatableParameter(2, "-border", "specify an input border file"); borderOpt->addBorderParameter(1, "border-file-in", "a border file to use borders from"); ParameterComponent* selectOpt = borderOpt->createRepeatableParameter(2, "-select", "select a single border to use"); selectOpt->addStringParameter(1, "border", "the border number or name"); OptionalParameter* upToOpt = selectOpt->createOptionalParameter(2, "-up-to", "use an inclusive range of borders"); upToOpt->addStringParameter(1, "last-border", "the number or name of the last column to include"); upToOpt->createOptionalParameter(2, "-reverse", "use the range in reverse order"); ret->setHelpText( AString("Takes one or more border files and makes a new border file from the borders in them.\n\n") + "Example: wb_command -border-merge out.border -border first.border -select 1 -border second.border\n\n" + "This example would take the first border from first.border, followed by all borders from second.border, " + "and write these to out.border." ); return ret; } void OperationBorderMerge::useParameters(OperationParameters* myParams, ProgressObject* myProgObj) { LevelProgress myProgress(myProgObj); BorderFile* outFile = myParams->getOutputBorder(1); const vector& borderInst = *(myParams->getRepeatableParameterInstances(2)); int numInputs = (int)borderInst.size(); if (numInputs < 1) throw OperationException("no inputs specified"); for (int i = 0; i < numInputs; ++i) { BorderFile* input = borderInst[i]->getBorder(1); if (i != 0)//structure automatically gets set on empty file by added borders { if (outFile->getStructure() != input->getStructure()) throw OperationException("input files have different structures"); } if (input->getNumberOfNodes() != -1) { int outNodes = outFile->getNumberOfNodes(); if (outNodes != -1) { if (outNodes != input->getNumberOfNodes()) throw OperationException("input files have different number of surface vertices"); } else { outFile->setNumberOfNodes(input->getNumberOfNodes()); } } outFile->getClassColorTable()->append(*(input->getClassColorTable()));//let the append logic deal with conflicts outFile->getNameColorTable()->append(*(input->getNameColorTable()));//we don't need the return values, as the numbers in the label tables are meaningless int numBorderParts = input->getNumberOfBorders(); const vector& selectOpts = *(borderInst[i]->getRepeatableParameterInstances(2)); int numSelectOpts = (int)selectOpts.size(); if (numSelectOpts > 0) { BorderMultiPartHelper myHelp(input); for (int j = 0; j < numSelectOpts; ++j) { int initialBorder = myHelp.fromNumberOrName(selectOpts[j]->getString(1)); if (initialBorder < 0) throw OperationException("border '" + selectOpts[j]->getString(1) + "' not found in file '" + input->getFileName() + "'"); OptionalParameter* upToOpt = selectOpts[j]->getOptionalParameter(2); if (upToOpt->m_present) { int finalBorder = myHelp.fromNumberOrName(upToOpt->getString(1)); if (finalBorder < 0) throw OperationException("ending border '" + selectOpts[j]->getString(1) + "' not found in file '" + input->getFileName() + "'"); bool reverse = upToOpt->getOptionalParameter(2)->m_present; if (reverse) { for (int b = finalBorder; b >= initialBorder; --b) { for (int k = 0; k < (int)myHelp.borderPieceList[b].size(); ++k) { outFile->addBorder(new Border(*(input->getBorder(myHelp.borderPieceList[b][k])))); } } } else { for (int b = initialBorder; b <= finalBorder; ++b) { for (int k = 0; k < (int)myHelp.borderPieceList[b].size(); ++k) { outFile->addBorder(new Border(*(input->getBorder(myHelp.borderPieceList[b][k])))); } } } } else { for (int k = 0; k < (int)myHelp.borderPieceList[initialBorder].size(); ++k) { outFile->addBorder(new Border(*(input->getBorder(myHelp.borderPieceList[initialBorder][k])))); } } } } else { for (int j = 0; j < numBorderParts; ++j) { outFile->addBorder(new Border(*(input->getBorder(j)))); } } } } connectome-workbench-1.2.3+git41-gc4c6c90/src/Operations/OperationBorderMerge.h000066400000000000000000000026031300200146000271170ustar00rootroot00000000000000#ifndef __OPERATION_BORDER_MERGE_H__ #define __OPERATION_BORDER_MERGE_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 "AbstractOperation.h" namespace caret { class OperationBorderMerge : public AbstractOperation { public: static OperationParameters* getParameters(); static void useParameters(OperationParameters* myParams, ProgressObject* myProgObj); static AString getCommandSwitch(); static AString getShortDescription(); }; typedef TemplateAutoOperation AutoOperationBorderMerge; } #endif //__OPERATION_BORDER_MERGE_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Operations/OperationCiftiChangeMapping.cxx000066400000000000000000000175401300200146000307630ustar00rootroot00000000000000/*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 "OperationCiftiChangeMapping.h" #include "OperationException.h" #include "CaretLogger.h" #include "CiftiFile.h" #include "FileInformation.h" #include "MultiDimIterator.h" #include using namespace caret; using namespace std; AString OperationCiftiChangeMapping::getCommandSwitch() { return "-cifti-change-mapping"; } AString OperationCiftiChangeMapping::getShortDescription() { return "CONVERT TO SCALAR, COPY MAPPING, ETC"; } OperationParameters* OperationCiftiChangeMapping::getParameters() { OperationParameters* ret = new OperationParameters(); ret->addCiftiParameter(1, "data-cifti", "the cifti file to use the data from"); ret->addStringParameter(2, "direction", "which direction on to replace the mapping"); ret->addCiftiOutputParameter(3, "cifti-out", "the output cifti file"); OptionalParameter* seriesOpt = ret->createOptionalParameter(4, "-series", "set the mapping to series"); seriesOpt->addDoubleParameter(1, "step", "increment between series points");//this is the order in -cifti-create-from-template, roll with it seriesOpt->addDoubleParameter(2, "start", "start value of the series"); OptionalParameter* seriesUnitOpt = seriesOpt->createOptionalParameter(3, "-unit", "select unit for series (default SECOND)"); seriesUnitOpt->addStringParameter(1, "unit", "unit identifier"); OptionalParameter* scalarOpt = ret->createOptionalParameter(5, "-scalar", "set the mapping to scalar"); OptionalParameter* scalarNameFileOpt = scalarOpt->createOptionalParameter(1, "-name-file", "specify names for the maps"); scalarNameFileOpt->addStringParameter(1, "file", "text file containing map names, one per line"); OptionalParameter* fromCiftiOpt = ret->createOptionalParameter(6, "-from-cifti", "copy mapping from another cifti file"); fromCiftiOpt->addCiftiParameter(1, "template-cifti", "a cifti file containing the desired mapping"); fromCiftiOpt->addStringParameter(2, "direction", "which direction to copy the mapping from"); AString helpText = AString("Take an existing cifti file and change one of the mappings. ") + "Exactly one of -series, -scalar, or -from-cifti must be specified. " + CiftiXML::directionFromStringExplanation(); helpText += "\n\nThe argument to -unit must be one of the following:\n"; vector unitList = CiftiSeriesMap::getAllUnits(); for (int i = 0; i < (int)unitList.size(); ++i) { helpText += "\n" + CiftiSeriesMap::unitToString(unitList[i]); } ret->setHelpText(helpText); return ret; } void OperationCiftiChangeMapping::useParameters(OperationParameters* myParams, ProgressObject* myProgObj) { LevelProgress myProgress(myProgObj); const CiftiFile* inputCifti = myParams->getCifti(1); AString dirString = myParams->getString(2); CiftiFile* outputCifti = myParams->getOutputCifti(3); int direction = CiftiXML::directionFromString(dirString); CiftiXML outXML = inputCifti->getCiftiXML(); if (direction >= outXML.getNumberOfDimensions()) { throw OperationException("specified direction does not exist in file"); } bool haveOption = false; OptionalParameter* seriesOpt = myParams->getOptionalParameter(4); if (seriesOpt->m_present) { haveOption = true; CiftiSeriesMap newMap(outXML.getDimensionLength(direction), seriesOpt->getDouble(2), seriesOpt->getDouble(1)); OptionalParameter* seriesUnitOpt = seriesOpt->getOptionalParameter(3); if (seriesUnitOpt->m_present) { bool ok = false; CiftiSeriesMap::Unit myUnit = CiftiSeriesMap::stringToUnit(seriesUnitOpt->getString(1), ok); if (!ok) { throw OperationException("unrecognized string for unit: '" + seriesUnitOpt->getString(1) + "'"); } newMap.setUnit(myUnit); } outXML.setMap(direction, newMap); } OptionalParameter* scalarOpt = myParams->getOptionalParameter(5); if (scalarOpt->m_present) { if (haveOption) { throw OperationException("only one of -series, -scalar, or -from-cifti may be specified"); } haveOption = true; CiftiScalarsMap newMap(outXML.getDimensionLength(direction)); OptionalParameter* scalarNameFileOpt = scalarOpt->getOptionalParameter(1); if (scalarNameFileOpt->m_present) { AString listfileName = scalarNameFileOpt->getString(1); FileInformation textFileInfo(listfileName); if (!textFileInfo.exists()) { throw OperationException("name list file doesn't exist"); } fstream nameListFile(listfileName.toLocal8Bit().constData(), fstream::in); if (!nameListFile.good()) { throw OperationException("error reading name list file"); } string mapName; for (int i = 0; i < newMap.getLength(); ++i) { getline(nameListFile, mapName); if (!nameListFile)//no, seriously, that is how you check if your input was good { CaretLogWarning("name file contained " + AString::number(i) + " names, expected " + AString::number(newMap.getLength())); break; } newMap.setMapName(i, mapName.c_str()); } } outXML.setMap(direction, newMap); } OptionalParameter* fromCiftiOpt = myParams->getOptionalParameter(6); if (fromCiftiOpt->m_present) { if (haveOption) { throw OperationException("only one of -series, -scalar, or -from-cifti may be specified"); } haveOption = true; const CiftiFile* templateCifti = fromCiftiOpt->getCifti(1); const CiftiXML& templateXML = templateCifti->getCiftiXML(); AString tempDirString = fromCiftiOpt->getString(2); int templateDir = CiftiXML::directionFromString(tempDirString); if (templateDir >= templateXML.getNumberOfDimensions()) { throw OperationException("specified direction does not exist in file"); } if (templateXML.getDimensionLength(templateDir) != outXML.getDimensionLength(direction)) { throw OperationException("selected direction on has different length than selected direction on "); } outXML.setMap(direction, *(templateXML.getMap(templateDir))); } if (!haveOption) { throw OperationException("you must specify one of -series, -scalar, or -from-cifti"); } outputCifti->setCiftiXML(outXML); vector outDims = outXML.getDimensions(); vector scratchrow(outDims[0]); for (MultiDimIterator iter(vector(outDims.begin() + 1, outDims.end())); !iter.atEnd(); ++iter) {//drop the first dimension, row length inputCifti->getRow(scratchrow.data(), *iter); outputCifti->setRow(scratchrow.data(), *iter); } } connectome-workbench-1.2.3+git41-gc4c6c90/src/Operations/OperationCiftiChangeMapping.h000066400000000000000000000026601300200146000304050ustar00rootroot00000000000000#ifndef __OPERATION_CIFTI_CHANGE_MAPPING_H__ #define __OPERATION_CIFTI_CHANGE_MAPPING_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 OperationCiftiChangeMapping : public AbstractOperation { public: static OperationParameters* getParameters(); static void useParameters(OperationParameters* myParams, ProgressObject* myProgObj); static AString getCommandSwitch(); static AString getShortDescription(); }; typedef TemplateAutoOperation AutoOperationCiftiChangeMapping; } #endif //__OPERATION_CIFTI_CHANGE_MAPPING_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Operations/OperationCiftiChangeTimestep.cxx000066400000000000000000000107061300200146000311570ustar00rootroot00000000000000/*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 "OperationCiftiChangeTimestep.h" #include "OperationException.h" #include "CiftiFile.h" #include "CaretLogger.h" #include "MultiDimIterator.h" using namespace caret; using namespace std; AString OperationCiftiChangeTimestep::getCommandSwitch() { return "-cifti-change-timestep"; } AString OperationCiftiChangeTimestep::getShortDescription() { return "DEPRECATED: use -cifti-change-mapping"; } OperationParameters* OperationCiftiChangeTimestep::getParameters() { OperationParameters* ret = new OperationParameters(); ret->addStringParameter(1, "cifti", "the cifti file to modify"); OptionalParameter* rowTimestep = ret->createOptionalParameter(2, "-row-timestep", "set the timestep along rows"); rowTimestep->addDoubleParameter(1, "seconds", "seconds per timestep"); OptionalParameter* columnTimestep = ret->createOptionalParameter(3, "-column-timestep", "set the timestep along columns"); columnTimestep->addDoubleParameter(1, "seconds", "seconds per timestep"); ret->setHelpText( AString("DEPRECATED: this command may be removed in a future release, use -cifti-change-mapping.\n\n") + "Warns if a dimension specified is not timepoints, otherwise modifies the timestep, and finally writes the result to " + "the same filename if any dimensions were modified.\nNOTE: you probably want -row-timestep, as that matches the .dtseries.nii specification. " + "The other option is available just for completeness." ); return ret; } void OperationCiftiChangeTimestep::useParameters(OperationParameters* myParams, ProgressObject* myProgObj) { LevelProgress myProgress(myProgObj); AString ciftiName = myParams->getString(1); OptionalParameter* rowTimestep = myParams->getOptionalParameter(2); OptionalParameter* columnTimestep = myParams->getOptionalParameter(3); if (!columnTimestep->m_present && !rowTimestep->m_present) { return; } CiftiFile myCifti; myCifti.openFile(ciftiName); CiftiXML tempXML = myCifti.getCiftiXML(); bool modified = false; if (rowTimestep->m_present) { float step = (float)rowTimestep->getDouble(1); if (tempXML.getMappingType(CiftiXML::ALONG_ROW) == CiftiMappingType::SERIES) { CiftiSeriesMap& myMap = tempXML.getSeriesMap(CiftiXML::ALONG_ROW); myMap.setStep(step);//TSC: leave units as-is, I guess modified = true; } else { CaretLogWarning("could not set row timestep"); } } if (columnTimestep->m_present) { float step = (float)columnTimestep->getDouble(1); if (tempXML.getMappingType(CiftiXML::ALONG_COLUMN) == CiftiMappingType::SERIES) { CiftiSeriesMap& myMap = tempXML.getSeriesMap(CiftiXML::ALONG_COLUMN); myMap.setStep(step);//TSC: leave units as-is, I guess modified = true; } else { CaretLogWarning("could not set row timestep"); } } if (modified) { myCifti.convertToInMemory();//because we are overwriting the input file CiftiFile outCifti; outCifti.setWritingFile(ciftiName);//starts on-disk writing outCifti.setCiftiXML(tempXML); const vector& dims = myCifti.getDimensions(); vector extraDims(dims.begin() + 1, dims.end()); vector scratchrow(dims[0]); for (MultiDimIterator iter(extraDims); !iter.atEnd(); ++iter) { myCifti.getRow(scratchrow.data(), *iter); outCifti.setRow(scratchrow.data(), *iter); } outCifti.writeFile(ciftiName);//superfluous, unless we aren't writing on-disk } } connectome-workbench-1.2.3+git41-gc4c6c90/src/Operations/OperationCiftiChangeTimestep.h000066400000000000000000000026661300200146000306120ustar00rootroot00000000000000#ifndef __OPERATION_CIFTI_CHANGE_TIMESTEP_H__ #define __OPERATION_CIFTI_CHANGE_TIMESTEP_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 "AbstractOperation.h" namespace caret { class OperationCiftiChangeTimestep : public AbstractOperation { public: static OperationParameters* getParameters(); static void useParameters(OperationParameters* myParams, ProgressObject* myProgObj); static AString getCommandSwitch(); static AString getShortDescription(); }; typedef TemplateAutoOperation AutoOperationCiftiChangeTimestep; } #endif //__OPERATION_CIFTI_CHANGE_TIMESTEP_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Operations/OperationCiftiConvert.cxx000066400000000000000000000646501300200146000277060ustar00rootroot00000000000000/*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 "OperationCiftiConvert.h" #include "OperationException.h" #include "CaretAssert.h" #include "CaretPointer.h" #include "CiftiFile.h" #include "CiftiXML.h" #include "FloatMatrix.h" #include "GiftiFile.h" #include "VolumeFile.h" #include #include #include #include #include #include using namespace caret; using namespace std; AString OperationCiftiConvert::getCommandSwitch() { return "-cifti-convert"; } AString OperationCiftiConvert::getShortDescription() { return "DUMP CIFTI MATRIX INTO OTHER FORMATS"; } OperationParameters* OperationCiftiConvert::getParameters() { OperationParameters* ret = new OperationParameters(); OptionalParameter* toGiftiExt = ret->createOptionalParameter(1, "-to-gifti-ext", "convert to GIFTI external binary"); toGiftiExt->addCiftiParameter(1, "cifti-in", "the input cifti file"); toGiftiExt->addStringParameter(2, "gifti-out", "output - the output gifti file"); OptionalParameter* fromGiftiExt = ret->createOptionalParameter(2, "-from-gifti-ext", "convert a GIFTI made with this command back into a CIFTI"); fromGiftiExt->addStringParameter(1, "gifti-in", "the input gifti file"); fromGiftiExt->addCiftiOutputParameter(2, "cifti-out", "the output cifti file"); OptionalParameter* fgresetTimeOpt = fromGiftiExt->createOptionalParameter(3, "-reset-timepoints", "reset the mapping along rows to timepoints, taking length from the gifti file"); fgresetTimeOpt->addDoubleParameter(1, "timestep", "the desired time between frames"); fgresetTimeOpt->addDoubleParameter(2, "timestart", "the desired time offset of the initial frame"); OptionalParameter* fgresetTimeunitsOpt = fgresetTimeOpt->createOptionalParameter(3, "-unit", "use a unit other than time"); fgresetTimeunitsOpt->addStringParameter(1, "unit", "unit identifier (default SECOND)"); fromGiftiExt->createOptionalParameter(4, "-reset-scalars", "reset mapping along rows to scalars, taking length from the gifti file"); OptionalParameter* fromGiftiReplace = fromGiftiExt->createOptionalParameter(5, "-replace-binary", "replace data with a binary file"); fromGiftiReplace->addStringParameter(1, "binary-in", "the binary file that contains replacement data"); fromGiftiReplace->createOptionalParameter(2, "-flip-endian", "byteswap the binary file"); fromGiftiReplace->createOptionalParameter(3, "-transpose", "transpose the binary file"); 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"); 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"); fromNifti->addCiftiParameter(2, "cifti-template", "a cifti file with the dimension(s) and mapping(s) that should be used"); fromNifti->addCiftiOutputParameter(3, "cifti-out", "the output cifti file"); OptionalParameter* fnresetTimeOpt = fromNifti->createOptionalParameter(4, "-reset-timepoints", "reset the mapping along rows to timepoints, taking length from the nifti file"); fnresetTimeOpt->addDoubleParameter(1, "timestep", "the desired time between frames"); fnresetTimeOpt->addDoubleParameter(2, "timestart", "the desired time offset of the initial frame"); OptionalParameter* fnresetTimeunitsOpt = fnresetTimeOpt->createOptionalParameter(3, "-unit", "use a unit other than time"); fnresetTimeunitsOpt->addStringParameter(1, "unit", "unit identifier (default SECOND)"); fromNifti->createOptionalParameter(5, "-reset-scalars", "reset mapping along rows to scalars, taking length from the nifti file"); OptionalParameter* toText = ret->createOptionalParameter(5, "-to-text", "convert to a plain text file"); toText->addCiftiParameter(1, "cifti-in", "the input cifti file"); toText->addStringParameter(2, "text-out", "output - the output text file"); OptionalParameter* toTextColDelimOpt = toText->createOptionalParameter(3, "-col-delim", "choose string to put between elements in a row"); toTextColDelimOpt->addStringParameter(1, "delim-string", "the string to use (default is a tab character)"); OptionalParameter* fromText = ret->createOptionalParameter(6, "-from-text", "convert from plain text to cifti"); fromText->addStringParameter(1, "text-in", "the input text file"); fromText->addCiftiParameter(2, "cifti-template", "a cifti file with the dimension(s) and mapping(s) that should be used"); fromText->addCiftiOutputParameter(3, "cifti-out", "the output cifti file"); OptionalParameter* fromTextColDelimOpt = fromText->createOptionalParameter(4, "-col-delim", "specify string that is between elements in a row"); fromTextColDelimOpt->addStringParameter(1, "delim-string", "the string to use (default is any whitespace)"); OptionalParameter* ftresetTimeOpt = fromText->createOptionalParameter(5, "-reset-timepoints", "reset the mapping along rows to timepoints, taking length from the text file"); ftresetTimeOpt->addDoubleParameter(1, "timestep", "the desired time between frames"); ftresetTimeOpt->addDoubleParameter(2, "timestart", "the desired time offset of the initial frame"); OptionalParameter* ftresetTimeunitsOpt = ftresetTimeOpt->createOptionalParameter(3, "-unit", "use a unit other than time"); ftresetTimeunitsOpt->addStringParameter(1, "unit", "unit identifier (default SECOND)"); fromText->createOptionalParameter(6, "-reset-scalars", "reset mapping along rows to scalars, taking length from the text file"); AString myText = AString("This command is used to convert a full CIFTI matrix to/from formats that can be used by programs that don't understand CIFTI. ") + "You must specify exactly one of -to-gifti-ext, -from-gifti-ext, -to-nifti, -from-nifti, -to-text, or -from-text.\n\n" + "If you want to write an existing CIFTI file with a different CIFTI version, see -file-convert, and its -cifti-version-convert option.\n\n" + "If you want part of the CIFTI file as a metric, label, or volume file, see -cifti-separate. " + "If you want to create a CIFTI file from metric and/or volume files, see the -cifti-create-* commands.\n\n" + "If you want to import a matrix that is restricted to an ROI, first create a template CIFTI file matching that ROI using a -cifti-create-* command. " + "After importing to CIFTI, you can then expand the file into a standard brainordinates space with -cifti-create-dense-from-template. " + "If you want to export only part of a CIFTI file, first create an roi-restricted CIFTI file with -cifti-restrict-dense-mapping.\n\n" + "The -transpose option to -from-gifti-ext is needed if the replacement binary file is in column-major order.\n\n" + "The -unit options accept these values:\n"; vector units = CiftiSeriesMap::getAllUnits(); for (int i = 0; i < (int)units.size(); ++i) { myText += "\n" + CiftiSeriesMap::unitToString(units[i]); } ret->setHelpText(myText); return ret; } void OperationCiftiConvert::useParameters(OperationParameters* myParams, ProgressObject* myProgObj) { LevelProgress myProgress(myProgObj); int modes = 0; OptionalParameter* toGiftiExt = myParams->getOptionalParameter(1); OptionalParameter* fromGiftiExt = myParams->getOptionalParameter(2); OptionalParameter* toNifti = myParams->getOptionalParameter(3); OptionalParameter* fromNifti = myParams->getOptionalParameter(4); OptionalParameter* toText = myParams->getOptionalParameter(5); OptionalParameter* fromText = myParams->getOptionalParameter(6); if (toGiftiExt->m_present) ++modes; if (fromGiftiExt->m_present) ++modes; if (toNifti->m_present) ++modes; if (fromNifti->m_present) ++modes; if (toText->m_present) ++modes; if (fromText->m_present) ++modes; if (modes != 1) { throw OperationException("you must specify exactly one conversion mode"); } if (toGiftiExt->m_present) { CiftiFile* myInFile = toGiftiExt->getCifti(1); AString myGiftiName = toGiftiExt->getString(2); vector myDims; myDims.push_back(myInFile->getNumberOfRows()); myDims.push_back(myInFile->getNumberOfColumns()); const CiftiXML& myXML = myInFile->getCiftiXML();//soft of hack - metric files use "normal" when they really mean none, using the same thing as metric files means it should just work if (myXML.getNumberOfDimensions() != 2) throw OperationException("conversion only supported for 2D cifti"); GiftiDataArray* myArray = new GiftiDataArray(NiftiIntentEnum::NIFTI_INTENT_NORMAL, NiftiDataTypeEnum::NIFTI_TYPE_FLOAT32, myDims, GiftiEncodingEnum::EXTERNAL_FILE_BINARY); float* myOutData = myArray->getDataPointerFloat(); for (int i = 0; i < myInFile->getNumberOfRows(); ++i) { myInFile->getRow(myOutData + i * myInFile->getNumberOfColumns(), i); } AString myCiftiXML = myXML.writeXMLToString(); myArray->getMetaData()->set("CiftiXML", myCiftiXML); GiftiFile myOutFile; myOutFile.setEncodingForWriting(GiftiEncodingEnum::EXTERNAL_FILE_BINARY); myOutFile.addDataArray(myArray); myOutFile.writeFile(myGiftiName); } if (fromGiftiExt->m_present) { AString myGiftiName = fromGiftiExt->getString(1); GiftiFile myInFile; myInFile.readFile(myGiftiName); if (myInFile.getNumberOfDataArrays() != 1) { throw OperationException("gifti file was not created by -cifti-convert, please use a -cifti-create-* command"); } GiftiDataArray* dataArrayRef = myInFile.getDataArray(0); if (!dataArrayRef->getMetaData()->exists("CiftiXML")) throw OperationException("gifti file was not created by -cifti-convert, please use a -cifti-create-* command"); if (dataArrayRef->getDataType() != NiftiDataTypeEnum::NIFTI_TYPE_FLOAT32)//this may not be needed { throw OperationException("input gifti has the wrong data type"); } CiftiFile* myOutFile = fromGiftiExt->getOutputCifti(2); CiftiXML myXML; myXML.readXML(dataArrayRef->getMetaData()->get("CiftiXML")); if (myXML.getNumberOfDimensions() != 2) throw OperationException("conversion only supported for 2D cifti"); int64_t numCols = dataArrayRef->getNumberOfComponents(); int64_t numRows = dataArrayRef->getNumberOfRows(); OptionalParameter* fgresetTimeOpt = fromGiftiExt->getOptionalParameter(3); if (fgresetTimeOpt->m_present) { CiftiSeriesMap::Unit myUnit = CiftiSeriesMap::SECOND; OptionalParameter* fgresetTimeunitsOpt = fgresetTimeOpt->getOptionalParameter(3); if (fgresetTimeunitsOpt->m_present) { bool ok = false; myUnit = CiftiSeriesMap::stringToUnit(fgresetTimeunitsOpt->getString(1), ok); if (!ok) throw OperationException("unrecognized unit name: '" + fgresetTimeunitsOpt->getString(1) + "'"); } myXML.setMap(CiftiXML::ALONG_ROW, CiftiSeriesMap(numCols, fgresetTimeOpt->getDouble(2), fgresetTimeOpt->getDouble(1), myUnit)); } else { if (myXML.getMappingType(CiftiXML::ALONG_ROW) == CiftiMappingType::SERIES) { myXML.getSeriesMap(CiftiXML::ALONG_ROW).setLength(numCols); } } if (myXML.getMappingType(CiftiXML::ALONG_COLUMN) == CiftiMappingType::SERIES) { myXML.getSeriesMap(CiftiXML::ALONG_COLUMN).setLength(numRows); } if (fromGiftiExt->getOptionalParameter(4)->m_present) { if (fgresetTimeOpt->m_present) throw OperationException("only one of -reset-timepoints and -reset-scalars may be specified"); CiftiScalarsMap newMap; newMap.setLength(numCols); myXML.setMap(CiftiXML::ALONG_ROW, newMap); } if (myXML.getDimensionLength(CiftiXML::ALONG_ROW) != numCols || myXML.getDimensionLength(CiftiXML::ALONG_COLUMN) != numRows) { throw OperationException("dimensions of input gifti array (" + AString::number(numRows) + " rows, " + AString::number(numCols) + " columns)" + " do not match dimensions of the Cifti XML (" + AString::number(myXML.getDimensionLength(CiftiXML::ALONG_COLUMN)) + " rows, " + AString::number(myXML.getDimensionLength(CiftiXML::ALONG_ROW)) + " columns)"); } myOutFile->setCiftiXML(myXML); OptionalParameter* fromGiftiReplace = fromGiftiExt->getOptionalParameter(5); if (fromGiftiReplace->m_present) { AString replaceFileName = fromGiftiReplace->getString(1); QFile replaceFile(replaceFileName); if (replaceFile.size() != (int64_t)(sizeof(float) * numCols * numRows)) { throw OperationException("replacement file is the wrong size, size is " + AString::number(replaceFile.size()) + ", needed " + AString::number(sizeof(float) * numCols * numRows)); } if (!replaceFile.open(QIODevice::ReadOnly)) { throw OperationException("unable to open replacement file for reading"); } OptionalParameter* swapBytes = fromGiftiReplace->getOptionalParameter(2); OptionalParameter* transpose = fromGiftiReplace->getOptionalParameter(3); int64_t readSize = numCols, numReads = numRows; if (transpose->m_present) { readSize = numRows; numReads = numCols; } CaretArray myScratch(readSize); for (int i = 0; i < numReads; ++i) { if (replaceFile.read((char*)(myScratch.getArray()), sizeof(float) * readSize) != (int64_t)(sizeof(float) * readSize)) { throw OperationException("short read from replacement file, aborting"); } float tempVal; char* tempValPointer = (char*)&tempVal;//copy method isn't as fast, but it is clean for (int j = 0; j < readSize; ++j) { if (swapBytes->m_present) { char* elemPointer = (char*)(myScratch.getArray() + j); for (int k = 0; k < (int)sizeof(float); ++k) { tempValPointer[k] = elemPointer[sizeof(float) - 1 - k]; } } else { tempVal = myScratch[j]; } if (transpose->m_present) { int32_t indices[] = {j, i}; dataArrayRef->setDataFloat32(indices, tempVal); } else { int32_t indices[] = {i, j}; dataArrayRef->setDataFloat32(indices, tempVal); } } } } vector scratchRow(numCols); for (int i = 0; i < numRows; ++i) { for (int j = 0; j < numCols; ++j) { int32_t indices[] = {i, j}; scratchRow[j] = dataArrayRef->getDataFloat32(indices); } myOutFile->setRow(scratchRow.data(), i); } } if (toNifti->m_present) { CiftiFile* myCiftiIn = toNifti->getCifti(1); if (myCiftiIn->getCiftiXML().getNumberOfDimensions() != 2) throw OperationException("conversion only supported for 2D cifti"); VolumeFile* myNiftiOut = toNifti->getOutputVolume(2); 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; } outDims[index] = temp; myNiftiOut->reinitialize(outDims, FloatMatrix::identity(4).getMatrix()); myNiftiOut->setValueAllVoxels(0.0f); int64_t ijk[3] = { 0, 0, 0 }; vector rowscratch(outDims[3]); for (int64_t i = 0; i < numRows; ++i) { myCiftiIn->getRow(rowscratch.data(), i); for (int64_t j = 0; j < outDims[3]; ++j) { myNiftiOut->setValue(rowscratch[j], ijk, j); } ++ijk[0]; if (ijk[0] >= outDims[0]) { ijk[0] = 0; ++ijk[1]; if (ijk[1] >= outDims[1]) { ijk[1] = 0; ++ijk[2]; CaretAssert(i == numRows - 1 || ijk[2] < outDims[2]);//in case it divided exactly } } } } if (fromNifti->m_present) { VolumeFile* myNiftiIn = fromNifti->getVolume(1); CiftiFile* myTemplate = fromNifti->getCifti(2); CiftiFile* myCiftiOut = fromNifti->getOutputCifti(3); vector myDims; myNiftiIn->getDimensions(myDims); if (myDims[4] != 1) throw OperationException("input nifti has multiple components, aborting"); CiftiXML outXML = myTemplate->getCiftiXML(); if (outXML.getNumberOfDimensions() != 2) throw OperationException("conversion only supported for 2D cifti"); OptionalParameter* fnresetTimeOpt = fromNifti->getOptionalParameter(4); if (fnresetTimeOpt->m_present) { CiftiSeriesMap::Unit myUnit = CiftiSeriesMap::SECOND; OptionalParameter* fnresetTimeunitsOpt = fnresetTimeOpt->getOptionalParameter(3); if (fnresetTimeunitsOpt->m_present) { bool ok = false; myUnit = CiftiSeriesMap::stringToUnit(fnresetTimeunitsOpt->getString(1), ok); if (!ok) throw OperationException("unrecognized unit name: '" + fnresetTimeunitsOpt->getString(1) + "'"); } outXML.setMap(CiftiXML::ALONG_ROW, CiftiSeriesMap(myDims[3], fnresetTimeOpt->getDouble(2), fnresetTimeOpt->getDouble(1), myUnit)); } if (fromNifti->getOptionalParameter(5)->m_present) { if (fnresetTimeOpt->m_present) throw OperationException("only one of -reset-timepoints and -reset-scalars may be specified"); CiftiScalarsMap newMap; newMap.setLength(myDims[3]); outXML.setMap(CiftiXML::ALONG_ROW, newMap); } int64_t numRows = outXML.getDimensionLength(CiftiXML::ALONG_COLUMN), numCols = outXML.getDimensionLength(CiftiXML::ALONG_ROW); if (myDims[3] != numCols) { throw OperationException("input nifti has the different size than cifti template for row length (nifti says " + AString::number(myDims[3]) + ", cifti XML says " + AString::number(numCols) + ")"); } if (numRows > myDims[0] * myDims[1] * myDims[2]) { throw OperationException("input nifti is too small for column length (need at least " + AString::number(numRows) + ", product of first three nifti dimensions is " + AString::number(myDims[0] * myDims[1] * myDims[2]) + ")"); } myCiftiOut->setCiftiXML(outXML); vector rowscratch(numCols); for (int64_t i = 0; i < numRows; ++i) { for (int64_t j = 0; j < numCols; ++j) { rowscratch[j] = myNiftiIn->getFrame(j)[i]; } myCiftiOut->setRow(rowscratch.data(), i); } } if (toText->m_present) { CiftiFile* ciftiIn = toText->getCifti(1); AString textOutName = toText->getString(2); AString delim = "\t"; OptionalParameter* colDelimOpt = toText->getOptionalParameter(3); if (colDelimOpt->m_present) { delim = colDelimOpt->getString(1); if (delim.isEmpty()) throw OperationException("delimiter string must not be empty");//catch some possible stupid mistakes } const CiftiXML& myXML = ciftiIn->getCiftiXML(); if (myXML.getNumberOfDimensions() != 2) throw OperationException("conversion only supported for 2D cifti"); if (myXML.getDimensionLength(0) < 1) throw OperationException("input cifti has zero-length rows"); vector dims = myXML.getDimensions(); vector scratchRow(dims[0]); fstream textOut(textOutName.toLocal8Bit().constData(), fstream::out | fstream::trunc | fstream::binary);//write the same file, no newline translation, regardless of OS for (int64_t i = 0; i < dims[1]; ++i) { ciftiIn->getRow(scratchRow.data(), i); textOut << AString::number(scratchRow[0]); for (int64_t j = 1; j < dims[0]; ++j) { textOut << delim << AString::number(scratchRow[j]); } textOut << "\n"; } } if (fromText->m_present) { AString textInName = fromText->getString(1); CiftiFile* ciftiTemplate = fromText->getCifti(2); CiftiFile* ciftiOut = fromText->getOutputCifti(3); AString delim = "";//empty is special value for "any whitespace" OptionalParameter* colDelimOpt = fromText->getOptionalParameter(4); if (colDelimOpt->m_present) { delim = colDelimOpt->getString(1); if (delim.isEmpty()) throw OperationException("delimiter string must not be empty"); } CiftiXML outXML = ciftiTemplate->getCiftiXML(); if (outXML.getNumberOfDimensions() != 2) throw OperationException("conversion only supported for 2D cifti"); int64_t numRows = outXML.getDimensionLength(CiftiXML::ALONG_COLUMN); fstream textIn(textInName.toLocal8Bit().constData(), fstream::in);//do newline translation on input string templine;//need to extract a row from the file first to set the row length if (!getline(textIn, templine)) throw OperationException("failed to read from input text file"); QStringList entries; if (delim.isEmpty()) { entries = QString(templine.c_str()).split(QRegExp("\\s+"), QString::SkipEmptyParts); } else { entries = QString(templine.c_str()).split(delim, QString::SkipEmptyParts); } if (numRows < 1) throw OperationException("template cifti file has no data");//this probably throws an exception in CiftiFile, but double check int textRowLength = entries.size(); OptionalParameter* ftresetTimeOpt = fromText->getOptionalParameter(5); if (ftresetTimeOpt->m_present) { CiftiSeriesMap::Unit myUnit = CiftiSeriesMap::SECOND; OptionalParameter* ftresetTimeunitsOpt = ftresetTimeOpt->getOptionalParameter(3); if (ftresetTimeunitsOpt->m_present) { bool ok = false; myUnit = CiftiSeriesMap::stringToUnit(ftresetTimeunitsOpt->getString(1), ok); if (!ok) throw OperationException("unrecognized unit name: '" + ftresetTimeunitsOpt->getString(1) + "'"); } outXML.setMap(CiftiXML::ALONG_ROW, CiftiSeriesMap(textRowLength, ftresetTimeOpt->getDouble(2), ftresetTimeOpt->getDouble(1), myUnit)); } if (fromText->getOptionalParameter(6)->m_present) { if (ftresetTimeOpt->m_present) throw OperationException("only one of -reset-timepoints and -reset-scalars may be specified"); CiftiScalarsMap newMap; newMap.setLength(textRowLength); outXML.setMap(CiftiXML::ALONG_ROW, newMap); } if (textRowLength != outXML.getDimensionLength(CiftiXML::ALONG_ROW)) { throw OperationException("text file has different row length than cifti template (text file says " + AString::number(textRowLength) + ", cifti XML says " + AString::number(outXML.getDimensionLength(CiftiXML::ALONG_ROW)) + ")"); } ciftiOut->setCiftiXML(outXML); vector temprow(textRowLength); bool ok = false; for (int i = 0; i < textRowLength; ++i) { temprow[i] = entries[i].toFloat(&ok); if (!ok) throw OperationException("failed to convert text to number: '" + entries[i] + "'"); } ciftiOut->setRow(temprow.data(), 0); for (int64_t j = 1; j < numRows; ++j) { if (!getline(textIn, templine)) throw OperationException("failed to read from input text file (not enough rows)"); if (delim.isEmpty()) { entries = QString(templine.c_str()).split(QRegExp("\\s+"), QString::SkipEmptyParts); } else { entries = QString(templine.c_str()).split(delim, QString::SkipEmptyParts); } if (entries.size() != textRowLength) throw OperationException("text file has inconsistent line length"); for (int i = 0; i < textRowLength; ++i) { temprow[i] = entries[i].toFloat(&ok); if (!ok) throw OperationException("failed to convert text to number: '" + entries[i] + "'"); } ciftiOut->setRow(temprow.data(), j); } } } connectome-workbench-1.2.3+git41-gc4c6c90/src/Operations/OperationCiftiConvert.h000066400000000000000000000026111300200146000273200ustar00rootroot00000000000000#ifndef __OPERATION_CIFTI_CONVERT_H__ #define __OPERATION_CIFTI_CONVERT_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 "AbstractOperation.h" namespace caret { class OperationCiftiConvert : public AbstractOperation { public: static OperationParameters* getParameters(); static void useParameters(OperationParameters* myParams, ProgressObject* myProgObj); static AString getCommandSwitch(); static AString getShortDescription(); }; typedef TemplateAutoOperation AutoOperationCiftiConvert; } #endif //__OPERATION_CIFTI_CONVERT_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Operations/OperationCiftiConvertToScalar.cxx000066400000000000000000000124701300200146000313300ustar00rootroot00000000000000/*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 "CaretLogger.h" #include "CiftiFile.h" #include "FileInformation.h" #include "OperationCiftiConvertToScalar.h" #include "OperationException.h" #include #include using namespace caret; using namespace std; AString OperationCiftiConvertToScalar::getCommandSwitch() { return "-cifti-convert-to-scalar"; } AString OperationCiftiConvertToScalar::getShortDescription() { return "DEPRECATED: use -cifti-change-mapping"; } OperationParameters* OperationCiftiConvertToScalar::getParameters() { OperationParameters* ret = new OperationParameters(); ret->addCiftiParameter(1, "cifti-in", "input cifti file"); ret->addStringParameter(2, "direction", "which mapping to change to scalar maps, ROW or COLUMN"); ret->addCiftiOutputParameter(3, "cifti-out", "output cifti file"); OptionalParameter* nameFileOpt = ret->createOptionalParameter(4, "-name-file", "specify names for the maps"); nameFileOpt->addStringParameter(1, "file", "text file containing map names, one per line"); ret->setHelpText( AString("DEPRECATED: this command may be removed in a future release, use -cifti-change-mapping.\n\n") + "Creates a new cifti file with the same data as the input, but with one of the dimensions set to contain strings identifying each map. " + "Specifying ROW will convert a dtseries file to a dscalar file." ); return ret; } void OperationCiftiConvertToScalar::useParameters(OperationParameters* myParams, ProgressObject* myProgObj) { LevelProgress myProgress(myProgObj); CiftiFile* ciftiIn = myParams->getCifti(1); AString direction = myParams->getString(2); int mydir = -1; if (direction == "ROW") mydir = 0; if (direction == "COLUMN") mydir = 1; if (mydir == -1) { throw OperationException(" must be 'ROW' or 'COLUMN'"); } CiftiFile* ciftiOut = myParams->getOutputCifti(3); OptionalParameter* nameFileOpt = myParams->getOptionalParameter(4); CiftiXMLOld myXML = ciftiIn->getCiftiXMLOld(); int rowSize = myXML.getNumberOfColumns(), colSize = myXML.getNumberOfRows(); if (mydir == 0) { myXML.resetRowsToScalars(rowSize); if (nameFileOpt->m_present) { AString listfileName = nameFileOpt->getString(1); FileInformation textFileInfo(listfileName); if (!textFileInfo.exists()) { throw OperationException("name list file doesn't exist"); } fstream nameListFile(listfileName.toLocal8Bit().constData(), fstream::in); if (!nameListFile.good()) { throw OperationException("error reading name list file"); } string mapName; for (int i = 0; i < rowSize; ++i) { getline(nameListFile, mapName); if (!nameListFile)//no, seriously, that is how you check if your input was good { CaretLogWarning("name file contained " + AString::number(i) + " names, expected " + AString::number(rowSize)); break; } myXML.setMapNameForRowIndex(i, mapName.c_str()); } } } else { myXML.resetColumnsToScalars(colSize); if (nameFileOpt->m_present) { AString listfileName = nameFileOpt->getString(1); FileInformation textFileInfo(listfileName); if (!textFileInfo.exists()) { throw OperationException("name list file doesn't exist"); } fstream nameListFile(listfileName.toLocal8Bit().constData(), fstream::in); if (!nameListFile.good()) { throw OperationException("error reading name list file"); } string mapName; for (int i = 0; i < colSize; ++i) { getline(nameListFile, mapName); if (!nameListFile)//no, seriously, that is how you check if your input was good { CaretLogWarning("warning, name file contained " + AString::number(i) + " names, expected " + AString::number(colSize)); break; } myXML.setMapNameForColumnIndex(i, mapName.c_str()); } } } ciftiOut->setCiftiXML(myXML); vector myrow(rowSize); for (int i = 0; i < colSize; ++i) { ciftiIn->getRow(myrow.data(), i); ciftiOut->setRow(myrow.data(), i); } } connectome-workbench-1.2.3+git41-gc4c6c90/src/Operations/OperationCiftiConvertToScalar.h000066400000000000000000000026771300200146000307650ustar00rootroot00000000000000#ifndef __OPERATION_CIFTI_CONVERT_TO_SCALAR_H__ #define __OPERATION_CIFTI_CONVERT_TO_SCALAR_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 "AbstractOperation.h" namespace caret { class OperationCiftiConvertToScalar : public AbstractOperation { public: static OperationParameters* getParameters(); static void useParameters(OperationParameters* myParams, ProgressObject* myProgObj); static AString getCommandSwitch(); static AString getShortDescription(); }; typedef TemplateAutoOperation AutoOperationCiftiConvertToScalar; } #endif //__OPERATION_CIFTI_CONVERT_TO_SCALAR_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Operations/OperationCiftiCopyMapping.cxx000066400000000000000000000073031300200146000305040ustar00rootroot00000000000000/*LICENSE_START*/ /* * Copyright (C) 2015 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 "OperationCiftiCopyMapping.h" #include "OperationException.h" #include "CiftiFile.h" #include "MultiDimIterator.h" using namespace caret; using namespace std; AString OperationCiftiCopyMapping::getCommandSwitch() { return "-cifti-copy-mapping"; } AString OperationCiftiCopyMapping::getShortDescription() { return "DEPRECATED: use -cifti-change-mapping"; } OperationParameters* OperationCiftiCopyMapping::getParameters() { OperationParameters* ret = new OperationParameters(); ret->addCiftiParameter(1, "data-cifti", "the cifti file to use the data from"); ret->addStringParameter(2, "replace-dir", "which direction on to replace the mapping"); ret->addCiftiParameter(3, "template-cifti", "a cifti file containing the desired mapping"); ret->addStringParameter(4, "template-dir", "which direction on to use the mapping from"); ret->addCiftiOutputParameter(5, "cifti-out", "the output cifti file"); ret->setHelpText( AString("DEPRECATED: this command may be removed in a future release, use -cifti-change-mapping.\n\n") + " must have the same length along the replace direction as has along the template direction. " + CiftiXML::directionFromStringExplanation() ); return ret; } void OperationCiftiCopyMapping::useParameters(OperationParameters* myParams, ProgressObject* myProgObj) { LevelProgress myProgress(myProgObj); const CiftiFile* dataCifti = myParams->getCifti(1); int dataDir = CiftiXML::directionFromString(myParams->getString(2)); const CiftiFile* templateCifti = myParams->getCifti(3); int templateDir = CiftiXML::directionFromString(myParams->getString(4)); CiftiFile* ciftiOut = myParams->getOutputCifti(5); CiftiXML outXML = dataCifti->getCiftiXML();//we need a copy of it so we can modify if (dataDir >= dataCifti->getCiftiXML().getNumberOfDimensions()) { throw OperationException("specified direction doesn't exist in data file"); } if (templateDir >= templateCifti->getCiftiXML().getNumberOfDimensions()) { throw OperationException("specified direction doesn't exist in template file"); } if (outXML.getDimensionLength(dataDir) != templateCifti->getCiftiXML().getDimensionLength(templateDir)) { throw OperationException("selected directions on files have different length"); } outXML.setMap(dataDir, *(templateCifti->getCiftiXML().getMap(templateDir))); ciftiOut->setCiftiXML(outXML); vector outDims = outXML.getDimensions(); vector scratchrow(outDims[0]); for (MultiDimIterator iter(vector(outDims.begin() + 1, outDims.end())); !iter.atEnd(); ++iter) {//drop the first dimension, row length dataCifti->getRow(scratchrow.data(), *iter); ciftiOut->setRow(scratchrow.data(), *iter); } } connectome-workbench-1.2.3+git41-gc4c6c90/src/Operations/OperationCiftiCopyMapping.h000066400000000000000000000026441300200146000301340ustar00rootroot00000000000000#ifndef __OPERATION_CIFTI_COPY_MAPPING_H__ #define __OPERATION_CIFTI_COPY_MAPPING_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 "AbstractOperation.h" namespace caret { class OperationCiftiCopyMapping : public AbstractOperation { public: static OperationParameters* getParameters(); static void useParameters(OperationParameters* myParams, ProgressObject* myProgObj); static AString getCommandSwitch(); static AString getShortDescription(); }; typedef TemplateAutoOperation AutoOperationCiftiCopyMapping; } #endif //__OPERATION_CIFTI_COPY_MAPPING_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Operations/OperationCiftiCreateDenseFromTemplate.cxx000066400000000000000000001002411300200146000327530ustar00rootroot00000000000000/*LICENSE_START*/ /* * Copyright (C) 2015 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 "OperationCiftiCreateDenseFromTemplate.h" #include "OperationException.h" #include "AlgorithmCiftiSeparate.h" #include "AlgorithmCiftiReplaceStructure.h" #include "CaretAssert.h" #include "CiftiFile.h" #include "GiftiLabelTable.h" #include "LabelFile.h" #include "MetricFile.h" #include "VolumeFile.h" #include using namespace caret; using namespace std; AString OperationCiftiCreateDenseFromTemplate::getCommandSwitch() { return "-cifti-create-dense-from-template"; } AString OperationCiftiCreateDenseFromTemplate::getShortDescription() { return "CREATE CIFTI WITH MATCHING DENSE MAP"; } OperationParameters* OperationCiftiCreateDenseFromTemplate::getParameters() { OperationParameters* ret = new OperationParameters(); ret->addCiftiParameter(1, "template-cifti", "file to match brainordinates of"); ret->addCiftiOutputParameter(2, "cifti-out", "the output cifti file"); OptionalParameter* seriesOpt = ret->createOptionalParameter(3, "-series", "make a dtseries file instead of a dscalar"); seriesOpt->addDoubleParameter(1, "step", "increment between series points"); seriesOpt->addDoubleParameter(2, "start", "start value of the series"); OptionalParameter* sUnitOpt = seriesOpt->createOptionalParameter(3, "-unit", "select unit for series (default SECOND)"); sUnitOpt->addStringParameter(1, "unit", "unit identifier"); OptionalParameter* volAllOpt = ret->createOptionalParameter(4, "-volume-all", "specify an input volume file for all voxel data"); volAllOpt->addVolumeParameter(1, "volume-in", "the input volume file"); volAllOpt->createOptionalParameter(2, "-from-cropped", "the input is cropped to the size of the voxel data in the template file"); ParameterComponent* ciftiOpt = ret->createRepeatableParameter(5, "-cifti", "use input data from a cifti file"); ciftiOpt->addCiftiParameter(1, "cifti-in", "cifti file containing input data"); ParameterComponent* metricOpt = ret->createRepeatableParameter(6, "-metric", "use input data from a metric file"); metricOpt->addStringParameter(1, "structure", "which structure to put the metric file into"); metricOpt->addMetricParameter(2, "metric-in", "input metric file"); ParameterComponent* labelOpt = ret->createRepeatableParameter(7, "-label", "use input data from surface label files"); labelOpt->addStringParameter(1, "structure", "which structure to put the label file into"); labelOpt->addLabelParameter(2, "label-in", "input label file"); ParameterComponent* volOpt = ret->createRepeatableParameter(8, "-volume", "use a volume file for a single volume structure's data"); volOpt->addStringParameter(1, "structure", "which structure to put the volume file into"); volOpt->addVolumeParameter(2, "volume-in", "the input volume file"); volOpt->createOptionalParameter(3, "-from-cropped", "the input is cropped to the size of the volume structure"); AString helpText = AString("This command helps you make a new dscalar, dtseries, or dlabel cifti file that matches the brainordinate space used in another cifti file. ") + "The template file must have the desired brainordinate space in the mapping along the column direction (for dtseries, dscalar, dlabel, and symmetric dconn this is always the case). " + "All input cifti files must have a brain models mapping along column and use the same volume space and/or surface vertex count as the template for structures that they contain. " + "If any input files contain label data, then input files with non-label data are not allowed, and the -series option may not be used.\n\n" + "Any structure that isn't covered by an input is filled with zeros or the unlabeled key.\n\n" + "The argument of -metric, -label or -volume must be one of the following:\n"; vector myStructureEnums; StructureEnum::getAllEnums(myStructureEnums); for (int i = 0; i < (int)myStructureEnums.size(); ++i) { helpText += "\n" + StructureEnum::toName(myStructureEnums[i]); } helpText += "\n\nThe argument to -unit must be one of the following:\n"; vector unitList = CiftiSeriesMap::getAllUnits(); for (int i = 0; i < (int)unitList.size(); ++i) { helpText += "\n" + CiftiSeriesMap::unitToString(unitList[i]); } ret->setHelpText(helpText); return ret; } namespace { enum DataSourceType { NONE,//fill with zeros CIFTI, LABEL, METRIC, VOLUME, VOLUME_ALL }; struct DataSourceInfo { DataSourceType type; int64_t index;//for repeatable options DataSourceInfo() { type = NONE; index = -1; }; }; } void OperationCiftiCreateDenseFromTemplate::useParameters(OperationParameters* myParams, ProgressObject* myProgObj) { LevelProgress myProgress(myProgObj); const CiftiFile* templateCifti = myParams->getCifti(1); CiftiFile* ciftiOut = myParams->getOutputCifti(2); const CiftiXML& templateXML = templateCifti->getCiftiXML(); if (templateXML.getNumberOfDimensions() != 2) { throw OperationException("template cifti file must have exactly 2 dimensions"); } if (templateXML.getMappingType(CiftiXML::ALONG_COLUMN) != CiftiMappingType::BRAIN_MODELS) { throw OperationException("template cifti file does not have brain models along column"); } CiftiXML outXML = templateXML; int labelMode = -1;//-1 not set, 0 set to false, 1 set to true int64_t numMaps = -1; const CaretMappableDataFile* nameFile = NULL; const CiftiFile* ciftiNameFile = NULL;//cifti doesn't inherit from CaretMappableDataFile, it is too different const CiftiBrainModelsMap& templateMap = templateXML.getBrainModelsMap(CiftiXML::ALONG_COLUMN); vector surfStructures = templateMap.getSurfaceStructureList(), volStructures = templateMap.getVolumeStructureList(); vector surfInfo(surfStructures.size()), volInfo(volStructures.size()); OptionalParameter* volAllOpt = myParams->getOptionalParameter(4); if (volAllOpt->m_present) { if (!templateMap.hasVolumeData()) { throw OperationException("-volume-all specified, but template cifti file does use any voxels"); } VolumeFile* allVolume = volAllOpt->getVolume(1); bool fromCropped = volAllOpt->getOptionalParameter(2)->m_present; const VolumeSpace& volAllSpace = allVolume->getVolumeSpace(); VolumeSpace templateSpace; if (fromCropped) { int64_t tempDims[3], tempOffset[3]; vector > tempSform; AlgorithmCiftiSeparate::getCroppedVolSpaceAll(templateCifti, CiftiXML::ALONG_COLUMN, tempDims, tempSform, tempOffset); templateSpace.setSpace(tempDims, tempSform); } else { templateSpace = templateMap.getVolumeSpace(); } if (!templateSpace.matches(volAllSpace)) { throw OperationException("-volume-all specifies a volume file that doesn't match the volume space of the template cifti file"); } numMaps = allVolume->getNumberOfMaps(); nameFile = allVolume; if (allVolume->getType() == SubvolumeAttributes::LABEL) { labelMode = 1; } else { labelMode = 0; } for (int i = 0; i < (int)volInfo.size(); ++i) { volInfo[i].type = VOLUME_ALL; } } const vector& ciftiInstances = *(myParams->getRepeatableParameterInstances(5)); for (int instance = 0; instance < (int)ciftiInstances.size(); ++instance) { const CiftiFile* thisCifti = ciftiInstances[instance]->getCifti(1); const CiftiXML& thisXML = thisCifti->getCiftiXML(); if (thisXML.getNumberOfDimensions() != 2) { throw OperationException("input cifti files must have exactly 2 dimensions"); } if (thisXML.getMappingType(CiftiXML::ALONG_COLUMN) != CiftiMappingType::BRAIN_MODELS) { throw OperationException("input cifti files must have a brain models mapping along column"); } const CiftiBrainModelsMap& thisDenseMap = thisXML.getBrainModelsMap(CiftiXML::ALONG_COLUMN); bool isLabel = (thisXML.getMappingType(CiftiXML::ALONG_ROW) == CiftiMappingType::LABELS); if (labelMode == -1) { labelMode = isLabel ? 1 : 0; } else { if (isLabel) { if (labelMode == 0) { throw OperationException("cifti file '" + thisCifti->getFileName() + "' contains label data, but -volume-all contains non-label data"); } } else { if (labelMode == 1) { throw OperationException("cifti file '" + thisCifti->getFileName() + "' contains non-label data, but -volume-all contains label data"); } } } int64_t thisNumMaps = thisXML.getDimensionLength(CiftiXML::ALONG_ROW); if (numMaps == -1) { numMaps = thisNumMaps; } else { if (numMaps != thisNumMaps) { throw OperationException("cifti file '" + thisCifti->getFileName() + "' contains " + AString::number(thisNumMaps) + " maps, but -volume-all contains " + AString::number(numMaps) + " maps"); } } if (thisDenseMap.hasVolumeData() && templateMap.hasVolumeData() && !templateMap.getVolumeSpace().matches(thisDenseMap.getVolumeSpace())) { throw OperationException("cifti file '" + thisCifti->getFileName() + "' uses a different volume space than the template file"); } vector thisSurfStructs = thisDenseMap.getSurfaceStructureList(), thisVolStructs = thisDenseMap.getVolumeStructureList(); for (int whichStruct = 0; whichStruct < (int)thisSurfStructs.size(); ++whichStruct) { vector::const_iterator iter = std::find(surfStructures.begin(), surfStructures.end(), thisSurfStructs[whichStruct]); if (iter == surfStructures.end()) { cout << ("discarding surface structure " + StructureEnum::toName(thisSurfStructs[whichStruct]) + " from file '" + thisCifti->getFileName() + "'") << endl; } else { int outIndex = (int)(iter - surfStructures.begin()); int64_t templateNumNodes = templateMap.getSurfaceNumberOfNodes(thisSurfStructs[whichStruct]); int64_t thisNumNodes = thisDenseMap.getSurfaceNumberOfNodes(thisSurfStructs[whichStruct]); if (thisNumNodes != templateNumNodes) { throw OperationException("cifti file '" + thisCifti->getFileName() + "' contains surface data for " + StructureEnum::toName(thisSurfStructs[whichStruct]) + ", but uses a surface with " + AString::number(thisNumNodes) + " vertices, while template needs " + AString::number(templateNumNodes)); } if (surfInfo[outIndex].type != NONE) { throw OperationException("cifti file '" + thisCifti->getFileName() + "' contains data for structure " + StructureEnum::toName(thisSurfStructs[whichStruct]) + ", but data for this structure also exists in an another -cifti option's file"); } surfInfo[outIndex].type = CIFTI; surfInfo[outIndex].index = instance; } } if (templateMap.hasVolumeData()) { for (int whichStruct = 0; whichStruct < (int)thisVolStructs.size(); ++whichStruct) { vector::const_iterator iter = std::find(volStructures.begin(), volStructures.end(), thisSurfStructs[whichStruct]); if (iter == volStructures.end()) { cout << ("discarding volume structure " + StructureEnum::toName(thisSurfStructs[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]) + ", but data for this structure also exists in an another option's file"); } volInfo[outIndex].type = CIFTI; volInfo[outIndex].index = instance; } } } else { if (thisDenseMap.hasVolumeData()) { cout << ("discarding volume structures from file '" + thisCifti->getFileName() + "'") << endl; } } if (ciftiNameFile == NULL && (isLabel || thisXML.getMappingType(CiftiXML::ALONG_ROW) == CiftiMappingType::SCALARS)) { ciftiNameFile = thisCifti;//cifti trumps everything, if it has scalar or label } } const vector& metricInstances = *(myParams->getRepeatableParameterInstances(6)); for (int instance = 0; instance < (int)metricInstances.size(); ++instance) { if (labelMode == 1) throw OperationException("-metric option specified when other inputs are label-type files"); labelMode = 0; AString structString = metricInstances[instance]->getString(1); const MetricFile* thisMetric = metricInstances[instance]->getMetric(2); bool ok = false; StructureEnum::Enum thisStruct = StructureEnum::fromName(structString, &ok); if (!ok) throw OperationException("unrecognized structure string in -metric option: " + structString); vector::const_iterator iter = std::find(surfStructures.begin(), surfStructures.end(), thisStruct); if (iter == surfStructures.end()) { throw OperationException("-metric option specified for structure " + structString + ", but template cifti file does not contain this surface structure"); } int outIndex = (int)(iter - surfStructures.begin()); if (thisMetric->getNumberOfNodes() != templateMap.getSurfaceNumberOfNodes(thisStruct)) { throw OperationException("metric file '" + thisMetric->getFileName() + "' has " + AString::number(thisMetric->getNumberOfNodes()) + " vertices, but template cifti requires " + AString::number(templateMap.getSurfaceNumberOfNodes(thisStruct)) + " for structure " + structString); } if (surfInfo[outIndex].type != NONE) { throw OperationException("-metric specified with structure " + structString + ", but data for this structure also exists in an another option"); } int64_t thisNumMaps = thisMetric->getNumberOfMaps(); if (numMaps == -1) { numMaps = thisNumMaps; } else { if (numMaps != thisNumMaps) { throw OperationException("metric file '" + thisMetric->getFileName() + "' contains " + AString::number(thisNumMaps) + " maps, but other file(s) contain " + AString::number(numMaps) + " maps"); } } checkStructureMatch(thisMetric, thisStruct, "metric file '" + thisMetric->getFileName() + "'", "the -metric option specified"); surfInfo[outIndex].type = METRIC; surfInfo[outIndex].index = instance; if (instance == 0) nameFile = thisMetric;//-metric trumps -volume-all for names, but use the first -metric } const vector& labelInstances = *(myParams->getRepeatableParameterInstances(7)); for (int instance = 0; instance < (int)labelInstances.size(); ++instance) { if (labelMode == 0) throw OperationException("-label option specified when other inputs are real-valued files"); labelMode = 1; AString structString = labelInstances[instance]->getString(1); const LabelFile* thisLabel = labelInstances[instance]->getLabel(2); bool ok = false; StructureEnum::Enum thisStruct = StructureEnum::fromName(structString, &ok); if (!ok) throw OperationException("unrecognized structure string in -label option: " + structString); vector::const_iterator iter = std::find(surfStructures.begin(), surfStructures.end(), thisStruct); if (iter == surfStructures.end()) { throw OperationException("-label option specified for structure " + structString + ", but template cifti file does not contain this surface structure"); } int outIndex = (int)(iter - surfStructures.begin()); if (thisLabel->getNumberOfNodes() != templateMap.getSurfaceNumberOfNodes(thisStruct)) { throw OperationException("label file '" + thisLabel->getFileName() + "' has " + AString::number(thisLabel->getNumberOfNodes()) + " vertices, but template cifti requires " + AString::number(templateMap.getSurfaceNumberOfNodes(thisStruct)) + " for structure " + structString); } if (surfInfo[outIndex].type != NONE) { throw OperationException("-label specified with structure " + structString + ", but data for this structure also exists in an another option"); } int64_t thisNumMaps = thisLabel->getNumberOfMaps(); if (numMaps == -1) { numMaps = thisNumMaps; } else { if (numMaps != thisNumMaps) { throw OperationException("label file '" + thisLabel->getFileName() + "' contains " + AString::number(thisNumMaps) + " maps, but other file(s) contain " + AString::number(numMaps) + " maps"); } } checkStructureMatch(thisLabel, thisStruct, "label file '" + thisLabel->getFileName() + "'", "the -label option specified"); surfInfo[outIndex].type = LABEL; surfInfo[outIndex].index = instance; if (instance == 0) nameFile = thisLabel;//-label trumps -volume-all for names, but use the first -label } const vector& volumeInstances = *(myParams->getRepeatableParameterInstances(8)); for (int instance = 0; instance < (int)volumeInstances.size(); ++instance) { AString structString = volumeInstances[instance]->getString(1); const VolumeFile* thisVol = volumeInstances[instance]->getVolume(2); bool fromCropped = volumeInstances[instance]->getOptionalParameter(3)->m_present; bool ok = false; StructureEnum::Enum thisStruct = StructureEnum::fromName(structString, &ok); if (!ok) throw OperationException("unrecognized structure string in -volume option: " + structString); vector::const_iterator iter = std::find(volStructures.begin(), volStructures.end(), thisStruct); if (iter == volStructures.end()) { throw OperationException("-volume option specified for structure " + structString + ", but template cifti file does not contain this volume structure"); } int outIndex = (int)(iter - volStructures.begin()); bool isLabel = (thisVol->getType() == SubvolumeAttributes::LABEL); if (labelMode == -1) { labelMode = isLabel ? 1 : 0; } else { if (isLabel) { if (labelMode == 0) { throw OperationException("volume file '" + thisVol->getFileName() + "' contains label data, but other file(s) contain non-label data"); } } else { if (labelMode == 1) { throw OperationException("volume file '" + thisVol->getFileName() + "' contains non-label data, but other file(s) contain label data"); } } } VolumeSpace templateSpace; if (fromCropped) { int64_t tempDims[3], tempOffset[3]; vector > tempSform; AlgorithmCiftiSeparate::getCroppedVolSpace(templateCifti, CiftiXML::ALONG_COLUMN, thisStruct, tempDims, tempSform, tempOffset); templateSpace.setSpace(tempDims, tempSform); } else { templateSpace = templateMap.getVolumeSpace(); } if (!thisVol->matchesVolumeSpace(templateSpace)) { throw OperationException("volume file '" + thisVol->getFileName() + "' does not match volume space of template cifti file"); } if (volInfo[outIndex].type != NONE) { throw OperationException("-volume specified with structure " + structString + ", but data for this structure also exists in an another option"); } int64_t thisNumMaps = thisVol->getNumberOfMaps(); if (numMaps == -1) { numMaps = thisNumMaps; } else { if (numMaps != thisNumMaps) { throw OperationException("volume file '" + thisVol->getFileName() + "' contains " + AString::number(thisNumMaps) + " maps, but other file(s) contain " + AString::number(numMaps) + " maps"); } } volInfo[outIndex].type = VOLUME; volInfo[outIndex].index = instance; if (nameFile == NULL) nameFile = thisVol;//-volume is the lowest priority for names } if (numMaps == -1) { throw OperationException("you must specify at least one input option"); } OptionalParameter* seriesOpt = myParams->getOptionalParameter(3); if (seriesOpt->m_present) { if (labelMode == 1) { throw OperationException("-series option cannot be used when input is label data"); } CiftiSeriesMap outMap; outMap.setLength(numMaps); outMap.setStep(seriesOpt->getDouble(1)); outMap.setStart(seriesOpt->getDouble(2)); OptionalParameter* unitOpt = seriesOpt->getOptionalParameter(3); if (unitOpt->m_present) { AString unitString = unitOpt->getString(1); bool ok = false; CiftiSeriesMap::Unit myUnit = CiftiSeriesMap::stringToUnit(unitString, ok); if (!ok) throw OperationException("unrecognized unit string '" + unitString + "'"); outMap.setUnit(myUnit); } outXML.setMap(CiftiXML::ALONG_ROW, outMap); } else { if (labelMode == 1) { CiftiLabelsMap outMap; outMap.setLength(numMaps); if (ciftiNameFile != NULL) { const CiftiMappingType& nameMap = *(ciftiNameFile->getCiftiXML().getMap(CiftiXML::ALONG_ROW)); for (int64_t i = 0; i < numMaps; ++i) { outMap.setMapName(i, nameMap.getIndexName(i)); } } else { CaretAssert(nameFile != NULL); for (int64_t i = 0; i < numMaps; ++i) { outMap.setMapName(i, nameFile->getMapName(i)); } } outXML.setMap(CiftiXML::ALONG_ROW, outMap); } else { CiftiScalarsMap outMap; outMap.setLength(numMaps); if (ciftiNameFile != NULL) { const CiftiMappingType& nameMap = *(ciftiNameFile->getCiftiXML().getMap(CiftiXML::ALONG_ROW)); for (int64_t i = 0; i < numMaps; ++i) { outMap.setMapName(i, nameMap.getIndexName(i)); } } else { CaretAssert(nameFile != NULL); for (int64_t i = 0; i < numMaps; ++i) { outMap.setMapName(i, nameFile->getMapName(i)); } } outXML.setMap(CiftiXML::ALONG_ROW, outMap); } } ciftiOut->setCiftiXML(outXML); for (int whichStruct = 0; whichStruct < (int)surfStructures.size(); ++whichStruct) { switch (surfInfo[whichStruct].type) { case CIFTI: { const CiftiFile* toUse = ciftiInstances[surfInfo[whichStruct].index]->getCifti(1); if (labelMode == 1) { LabelFile tempLabel; AlgorithmCiftiSeparate(NULL, toUse, CiftiXML::ALONG_COLUMN, surfStructures[whichStruct], &tempLabel); AlgorithmCiftiReplaceStructure(NULL, ciftiOut, CiftiXML::ALONG_COLUMN, surfStructures[whichStruct], &tempLabel); } else { MetricFile tempMetric; AlgorithmCiftiSeparate(NULL, toUse, CiftiXML::ALONG_COLUMN, surfStructures[whichStruct], &tempMetric); AlgorithmCiftiReplaceStructure(NULL, ciftiOut, CiftiXML::ALONG_COLUMN, surfStructures[whichStruct], &tempMetric); } break; } case METRIC: { const MetricFile* toUse = metricInstances[surfInfo[whichStruct].index]->getMetric(2); AlgorithmCiftiReplaceStructure(NULL, ciftiOut, CiftiXML::ALONG_COLUMN, surfStructures[whichStruct], toUse); break; } case LABEL: { const LabelFile* toUse = labelInstances[surfInfo[whichStruct].index]->getLabel(2); AlgorithmCiftiReplaceStructure(NULL, ciftiOut, CiftiXML::ALONG_COLUMN, surfStructures[whichStruct], toUse); break; } case NONE: { int numNodes = templateMap.getSurfaceNumberOfNodes(surfStructures[whichStruct]); if (labelMode == 1) { LabelFile tempLabel; tempLabel.setNumberOfNodesAndColumns(numNodes, numMaps); int32_t unlabeledKey = tempLabel.getLabelTable()->getUnassignedLabelKey(); vector scratchCol(numNodes, unlabeledKey); for (int64_t i = 0; i < numMaps; ++i) { tempLabel.setLabelKeysForColumn(i, scratchCol.data()); } AlgorithmCiftiReplaceStructure(NULL, ciftiOut, CiftiXML::ALONG_COLUMN, surfStructures[whichStruct], &tempLabel); } else { MetricFile tempMetric; tempMetric.setNumberOfNodesAndColumns(numNodes, numMaps); vector scratchCol(numNodes, 0.0f); for (int64_t i = 0; i < numMaps; ++i) { tempMetric.setValuesForColumn(i, scratchCol.data()); } AlgorithmCiftiReplaceStructure(NULL, ciftiOut, CiftiXML::ALONG_COLUMN, surfStructures[whichStruct], &tempMetric); } break; } default: CaretAssert(false); throw OperationException("internal error, invalid source type for surface data, tell the developers what you did"); } } if (volStructures.size() > 0 && volInfo[0].type == VOLUME_ALL)//NOTE: if one structure is VOLUME_ALL, all are, and the cropped space is different than per-structure, so DO NOT enter the structure loop { const VolumeFile* toUse = volAllOpt->getVolume(1); bool fromCropped = volAllOpt->getOptionalParameter(2)->m_present; AlgorithmCiftiReplaceStructure(NULL, ciftiOut, CiftiXML::ALONG_COLUMN, toUse, fromCropped); } else { for (int whichStruct = 0; whichStruct < (int)volStructures.size(); ++whichStruct) { switch (volInfo[whichStruct].type) { case CIFTI: { const CiftiFile* toUse = ciftiInstances[volInfo[whichStruct].index]->getCifti(1); VolumeFile tempVol; int64_t dims1[3], dims2[3], off1[3], off2[3];//check if the cropped space matches, if so we can save memory easily by using the crop argument (and this is also the common case) vector > sform1, sform2; AlgorithmCiftiSeparate::getCroppedVolSpace(toUse, CiftiXML::ALONG_COLUMN, volStructures[whichStruct], dims1, sform1, off1); AlgorithmCiftiSeparate::getCroppedVolSpace(templateCifti, CiftiXML::ALONG_COLUMN, volStructures[whichStruct], dims2, sform2, off2); VolumeSpace space1(dims1, sform1), space2(dims2, sform2); if (space1.matches(space2)) { AlgorithmCiftiSeparate(NULL, toUse, CiftiXML::ALONG_COLUMN, volStructures[whichStruct], &tempVol, off1, NULL, true); AlgorithmCiftiReplaceStructure(NULL, ciftiOut, CiftiXML::ALONG_COLUMN, volStructures[whichStruct], &tempVol, true); } else { AlgorithmCiftiSeparate(NULL, toUse, CiftiXML::ALONG_COLUMN, volStructures[whichStruct], &tempVol, off1, NULL, false); AlgorithmCiftiReplaceStructure(NULL, ciftiOut, CiftiXML::ALONG_COLUMN, volStructures[whichStruct], &tempVol, false); } break; } case VOLUME: { const VolumeFile* toUse = volumeInstances[volInfo[whichStruct].index]->getVolume(2); bool fromCropped = volumeInstances[volInfo[whichStruct].index]->getOptionalParameter(3)->m_present; AlgorithmCiftiReplaceStructure(NULL, ciftiOut, CiftiXML::ALONG_COLUMN, volStructures[whichStruct], toUse, fromCropped); break; } case NONE: { VolumeFile tempVol; int64_t offset[3]; vector dims(3); vector > sform; AlgorithmCiftiSeparate::getCroppedVolSpace(templateCifti, CiftiXML::ALONG_COLUMN, volStructures[whichStruct], dims.data(), sform, offset); dims.push_back(numMaps); if (labelMode == 1) { int64_t frameSize = dims[0] * dims[1] * dims[2]; tempVol.reinitialize(dims, sform, 1, SubvolumeAttributes::LABEL); for (int64_t i = 0; i < numMaps; ++i) { int32_t unlabeledKey = tempVol.getMapLabelTable(i)->getUnassignedLabelKey(); vector scratchFrame(frameSize, unlabeledKey); tempVol.setFrame(scratchFrame.data(), i); } } else { tempVol.reinitialize(dims, sform); tempVol.setValueAllVoxels(0.0f); } AlgorithmCiftiReplaceStructure(NULL, ciftiOut, CiftiXML::ALONG_COLUMN, volStructures[whichStruct], &tempVol, true); break; } default: CaretAssert(false); throw OperationException("internal error, invalid source type for volume structure data, tell the developers what you did"); } } } } connectome-workbench-1.2.3+git41-gc4c6c90/src/Operations/OperationCiftiCreateDenseFromTemplate.h000066400000000000000000000027621300200146000324110ustar00rootroot00000000000000#ifndef __OPERATION_CIFTI_CREATE_DENSE_FROM_TEMPLATE_H__ #define __OPERATION_CIFTI_CREATE_DENSE_FROM_TEMPLATE_H__ /*LICENSE_START*/ /* * Copyright (C) 2015 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 OperationCiftiCreateDenseFromTemplate : public AbstractOperation { public: static OperationParameters* getParameters(); static void useParameters(OperationParameters* myParams, ProgressObject* myProgObj); static AString getCommandSwitch(); static AString getShortDescription(); }; typedef TemplateAutoOperation AutoOperationCiftiCreateDenseFromTemplate; } #endif //__OPERATION_CIFTI_CREATE_DENSE_FROM_TEMPLATE_H__ OperationCiftiCreateParcellatedFromTemplate.cxx000066400000000000000000000220301300200146000340550ustar00rootroot00000000000000connectome-workbench-1.2.3+git41-gc4c6c90/src/Operations/*LICENSE_START*/ /* * Copyright (C) 2015 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 "OperationCiftiCreateParcellatedFromTemplate.h" #include "OperationException.h" #include "CaretLogger.h" #include "CiftiFile.h" #include "MultiDimIterator.h" #include using namespace caret; using namespace std; AString OperationCiftiCreateParcellatedFromTemplate::getCommandSwitch() { return "-cifti-create-parcellated-from-template"; } AString OperationCiftiCreateParcellatedFromTemplate::getShortDescription() { return "MATCH PARCELS TO TEMPLATE BY NAME"; } OperationParameters* OperationCiftiCreateParcellatedFromTemplate::getParameters() { OperationParameters* ret = new OperationParameters(); ret->addCiftiParameter(1, "cifti-template", "a cifti file with the template parcel mapping along column"); ret->addStringParameter(2, "modify-direction", "which dimension of the output file should match the template (integer, 'ROW', or 'COLUMN')"); ret->addCiftiOutputParameter(3, "cifti-out", "the output cifti file"); OptionalParameter* fillOpt = ret->createOptionalParameter(4, "-fill-value", "specify value to be used in parcels that don't match"); fillOpt->addDoubleParameter(1, "value", "value to use (default 0)"); ParameterComponent* ciftiOpt = ret->createRepeatableParameter(5, "-cifti", "specify an input cifti file"); ciftiOpt->addCiftiParameter(1, "cifti-in", "the input parcellated cifti file"); ret->setHelpText( AString("For each parcel name in the template mapping, find that name in an input cifti file and use its data in the output file. ") + "All input cifti files must have a parcels mapping along and matching mappings along other dimensions. " + CiftiXML::directionFromStringExplanation() ); return ret; } void OperationCiftiCreateParcellatedFromTemplate::useParameters(OperationParameters* myParams, ProgressObject* myProgObj) { LevelProgress myProgress(myProgObj); CiftiFile* templateCifti = myParams->getCifti(1); int direction = CiftiXML::directionFromString(myParams->getString(2)); CiftiFile* ciftiOut = myParams->getOutputCifti(3); OptionalParameter* fillOpt = myParams->getOptionalParameter(4); float fillValue = 0.0f; if (fillOpt->m_present) { fillValue = (float)fillOpt->getDouble(1); } const vector& inputInstances = *(myParams->getRepeatableParameterInstances(5)); const CiftiXML& templateXML = templateCifti->getCiftiXML(); if (templateXML.getMappingType(CiftiXML::ALONG_COLUMN) != CiftiMappingType::PARCELS) { throw OperationException("template file does not have a parcel mapping along column"); } int numInstances = (int)inputInstances.size(); if (numInstances < 1) { throw OperationException("at least one input cifti file must be specified"); } const CiftiXML& firstXML = inputInstances[0]->getCifti(1)->getCiftiXML(); int numDims = firstXML.getNumberOfDimensions(); if (direction >= numDims) { throw OperationException("input file '" + inputInstances[0]->getCifti(1)->getFileName() + "' does not contain the specified dimension"); } for (int i = 0; i < numInstances; ++i) { const CiftiFile* thisInput = inputInstances[i]->getCifti(1); const CiftiXML& thisXML = thisInput->getCiftiXML(); if (numDims != thisXML.getNumberOfDimensions()) { throw OperationException("input '" + thisInput->getFileName() + "' has different number of dimensions"); } for (int j = 0; j < numDims; ++j) { if (j == direction) { if (thisXML.getMappingType(j) != CiftiMappingType::PARCELS) { throw OperationException("input '" + thisInput->getFileName() + "' does not contain parcels mapping along specified direction"); } } else { if (!thisXML.getMap(j)->approximateMatch(*(firstXML.getMap(j)))) { throw OperationException("input '" + thisInput->getFileName() + "' has non-matching mapping along other direction(s)"); } } } } const CiftiParcelsMap& myTemplateMap = templateXML.getParcelsMap(CiftiXML::ALONG_COLUMN); int numOutParcels = myTemplateMap.getLength(); map nameMatcher; for (int i = 0; i < numOutParcels; ++i) { if (nameMatcher.find(myTemplateMap.getIndexName(i)) != nameMatcher.end()) { throw OperationException("template parcel mapping reuses a parcel name"); } nameMatcher[myTemplateMap.getIndexName(i)] = i; } vector sourceInput(numOutParcels, -1), sourceParcel(numOutParcels); for (int i = 0; i < numInstances; ++i) { const CiftiFile* thisInput = inputInstances[i]->getCifti(1); const CiftiXML& thisXML = thisInput->getCiftiXML(); const CiftiParcelsMap& thisParcelMap = thisXML.getParcelsMap(direction); int thisNumParcels = thisParcelMap.getLength(); bool match = false; for (int j = 0; j < thisNumParcels; ++j) { map::const_iterator result = nameMatcher.find(thisParcelMap.getIndexName(j)); if (result != nameMatcher.end()) { match = true; if (sourceInput[result->second] != -1) { throw OperationException("multiple input parcels match output parcel '" + result->first + "'"); } sourceInput[result->second] = i; sourceParcel[result->second] = j; } } if (!match) { CaretLogWarning("input '" + thisInput->getFileName() + "' does not match any template parcels"); } } CiftiXML outXML = firstXML; outXML.setMap(direction, myTemplateMap); ciftiOut->setCiftiXML(outXML); if (direction == CiftiXML::ALONG_ROW) { vector outRow(numOutParcels, fillValue); vector > inputRows(numInstances); for (int i = 0; i < numInstances; ++i) { const CiftiFile* thisInput = inputInstances[i]->getCifti(1); const CiftiXML& thisXML = thisInput->getCiftiXML(); inputRows[i].resize(thisXML.getDimensionLength(CiftiXML::ALONG_ROW)); } vector iterDims = firstXML.getDimensions(); iterDims.erase(iterDims.begin());//remove row dimension for (MultiDimIterator iter(iterDims); !iter.atEnd(); ++iter) { for (int i = 0; i < numInstances; ++i) { const CiftiFile* thisInput = inputInstances[i]->getCifti(1); thisInput->getRow(inputRows[i].data(), *iter); } for (int i = 0; i < numOutParcels; ++i) { if (sourceInput[i] != -1) { outRow[i] = inputRows[sourceInput[i]][sourceParcel[i]]; } } ciftiOut->setRow(outRow.data(), *iter); } } else { vector fillRow(firstXML.getDimensionLength(CiftiXML::ALONG_ROW), fillValue); vector scratchRow = fillRow; vector iterDims = firstXML.getDimensions(); iterDims.erase(iterDims.begin() + direction);//remove remapped dimension iterDims.erase(iterDims.begin());//remove row dimension for (MultiDimIterator iter(iterDims); !iter.atEnd(); ++iter) { vector restoreDims = *iter; restoreDims.insert(restoreDims.begin() + (direction - 1), -1);//deliberately invalid placeholder for (int i = 0; i < numOutParcels; ++i) { if (sourceInput[i] == -1) { restoreDims[direction - 1] = i; ciftiOut->setRow(fillRow.data(), restoreDims); } else { restoreDims[direction - 1] = sourceParcel[i]; const CiftiFile* thisInput = inputInstances[sourceInput[i]]->getCifti(1); thisInput->getRow(scratchRow.data(), restoreDims); restoreDims[direction - 1] = i; ciftiOut->setRow(scratchRow.data(), restoreDims); } } } } } OperationCiftiCreateParcellatedFromTemplate.h000066400000000000000000000030261300200146000335060ustar00rootroot00000000000000connectome-workbench-1.2.3+git41-gc4c6c90/src/Operations#ifndef __OPERATION_CIFTI_CREATE_PARCELLATED_FROM_TEMPLATE_H__ #define __OPERATION_CIFTI_CREATE_PARCELLATED_FROM_TEMPLATE_H__ /*LICENSE_START*/ /* * Copyright (C) 2015 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 OperationCiftiCreateParcellatedFromTemplate : public AbstractOperation { public: static OperationParameters* getParameters(); static void useParameters(OperationParameters* myParams, ProgressObject* myProgObj); static AString getCommandSwitch(); static AString getShortDescription(); }; typedef TemplateAutoOperation AutoOperationCiftiCreateParcellatedFromTemplate; } #endif //__OPERATION_CIFTI_CREATE_PARCELLATED_FROM_TEMPLATE_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Operations/OperationCiftiCreateScalarSeries.cxx000066400000000000000000000140311300200146000317560ustar00rootroot00000000000000/*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 "OperationCiftiCreateScalarSeries.h" #include "OperationException.h" #include "CaretLogger.h" #include "CiftiFile.h" #include "FloatMatrix.h" #include #include #include #include using namespace caret; using namespace std; AString OperationCiftiCreateScalarSeries::getCommandSwitch() { return "-cifti-create-scalar-series"; } AString OperationCiftiCreateScalarSeries::getShortDescription() { return "IMPORT SERIES DATA INTO CIFTI"; } OperationParameters* OperationCiftiCreateScalarSeries::getParameters() { OperationParameters* ret = new OperationParameters(); ret->addStringParameter(1, "input", "input file"); ret->addCiftiOutputParameter(2, "cifti-out", "output cifti file"); ret->createOptionalParameter(3, "-transpose", "use if the rows of the text file are along the scalar dimension"); OptionalParameter* nameFileOpt = ret->createOptionalParameter(4, "-name-file", "use a text file to set names on scalar dimension"); nameFileOpt->addStringParameter(1, "file", "text file containing names, one per line"); OptionalParameter* seriesOpt = ret->createOptionalParameter(5, "-series", "set the units and values of the series"); seriesOpt->addStringParameter(1, "unit", "the unit to use"); seriesOpt->addDoubleParameter(2, "start", "the value at the first series point"); seriesOpt->addDoubleParameter(3, "step", "the interval between series points"); AString myHelp = AString("Convert a text file containing series of equal length into a cifti file. ") + "The text file should have lines made up of numbers separated by whitespace, with no extra newlines between lines.\n\n" + "The argument must be one of the following:\n"; vector units = CiftiSeriesMap::getAllUnits(); for (int i = 0; i < (int)units.size(); ++i) { myHelp += "\n" + CiftiSeriesMap::unitToString(units[i]); } ret->setHelpText(myHelp); return ret; } void OperationCiftiCreateScalarSeries::useParameters(OperationParameters* myParams, ProgressObject* myProgObj) { LevelProgress myProgress(myProgObj); AString inFileName = myParams->getString(1); CiftiFile* outFile = myParams->getOutputCifti(2); bool transpose = myParams->getOptionalParameter(3)->m_present; ifstream nameFile; OptionalParameter* nameFileOpt = myParams->getOptionalParameter(4); if (nameFileOpt->m_present) { nameFile.open(nameFileOpt->getString(1).toLocal8Bit().constData()); if (!nameFile) throw OperationException("failed to open name file"); } ifstream inputFile(inFileName.toLocal8Bit().constData()); vector > inFileData; if (!inputFile.good()) throw OperationException("failed to open input file '" + inFileName + "'"); string inputLine; while (inputFile) { getline(inputFile, inputLine); QStringList tokens = QString(inputLine.c_str()).split(QRegExp("\\s+"), QString::SkipEmptyParts); if (tokens.empty()) break;//in case there are extra newlines on the end if (!inFileData.empty() && (int)inFileData.back().size() != tokens.size()) throw OperationException("input file is not a rectangular matrix, starting at line " + AString::number(inFileData.size() + 2));//1 for 0-indexing, 1 for line not added to matrix yet inFileData.push_back(vector()); for (int i = 0; i < tokens.size(); ++i) { bool ok = false; inFileData.back().push_back(tokens[i].toFloat(&ok)); if (!ok) throw OperationException("input file contains non-number '" + tokens[i] + "'"); } } if (inFileData.empty() || inFileData[0].empty()) throw OperationException("input file contains no data"); if (transpose) { inFileData = FloatMatrix(inFileData).transpose().getMatrix(); } CiftiScalarsMap colMap; colMap.setLength(inFileData.size()); if (nameFileOpt->m_present) { for (int i = 0; i < (int)inFileData.size(); ++i) { getline(nameFile, inputLine); if (!nameFile) { CaretLogWarning("name file contained " + AString::number(i) + " names, expected " + AString::number(inFileData.size())); break; } colMap.setMapName(i, inputLine.c_str()); } } CiftiSeriesMap rowMap; rowMap.setLength(inFileData[0].size()); OptionalParameter* seriesOpt = myParams->getOptionalParameter(5); if (seriesOpt->m_present) { AString unitName = seriesOpt->getString(1); bool ok = false; CiftiSeriesMap::Unit myUnit = CiftiSeriesMap::stringToUnit(unitName, ok); if (!ok) { throw OperationException("unrecognized unit name: '" + unitName + "'"); } rowMap.setUnit(myUnit); rowMap.setStart(seriesOpt->getDouble(2)); rowMap.setStep(seriesOpt->getDouble(3)); } CiftiXML outXML; outXML.setNumberOfDimensions(2); outXML.setMap(CiftiXML::ALONG_ROW, rowMap); outXML.setMap(CiftiXML::ALONG_COLUMN, colMap); outFile->setCiftiXML(outXML); for (int i = 0; i < (int)inFileData.size(); ++i) { outFile->setRow(inFileData[i].data(), i); } } connectome-workbench-1.2.3+git41-gc4c6c90/src/Operations/OperationCiftiCreateScalarSeries.h000066400000000000000000000027211300200146000314060ustar00rootroot00000000000000#ifndef __OPERATION_CIFTI_CREATE_SCALAR_SERIES_H__ #define __OPERATION_CIFTI_CREATE_SCALAR_SERIES_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 "AbstractOperation.h" namespace caret { class OperationCiftiCreateScalarSeries : public AbstractOperation { public: static OperationParameters* getParameters(); static void useParameters(OperationParameters* myParams, ProgressObject* myProgObj); static AString getCommandSwitch(); static AString getShortDescription(); }; typedef TemplateAutoOperation AutoOperationCiftiCreateScalarSeries; } #endif //__OPERATION_CIFTI_CREATE_SCALAR_SERIES_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Operations/OperationCiftiEstimateFWHM.cxx000066400000000000000000000200431300200146000305070ustar00rootroot00000000000000/*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 "OperationCiftiEstimateFWHM.h" #include "OperationException.h" #include "AlgorithmCiftiSeparate.h" #include "AlgorithmMetricEstimateFWHM.h" #include "AlgorithmVolumeEstimateFWHM.h" #include "CaretPointer.h" #include "CiftiFile.h" #include "MetricFile.h" #include "SurfaceFile.h" #include "VolumeFile.h" using namespace caret; using namespace std; AString OperationCiftiEstimateFWHM::getCommandSwitch() { return "-cifti-estimate-fwhm"; } AString OperationCiftiEstimateFWHM::getShortDescription() { return "ESTIMATE FWHM SMOOTHNESS OF A CIFTI FILE"; } OperationParameters* OperationCiftiEstimateFWHM::getParameters() { OperationParameters* ret = new OperationParameters(); ret->addCiftiParameter(1, "cifti", "the input cifti file"); ret->createOptionalParameter(2, "-merged-volume", "treat volume components as if they were a single component"); OptionalParameter* columnOpt = ret->createOptionalParameter(3, "-column", "only output estimates for one column"); columnOpt->addIntegerParameter(1, "column", "the column number"); ParameterComponent* surfOpt = ret->createRepeatableParameter(4, "-surface", "specify an input surface"); surfOpt->addStringParameter(1, "structure", "what structure to use this surface for"); surfOpt->addSurfaceParameter(2, "surface", "the surface file"); AString myText = AString("Estimate the smoothness of the components of the cifti file, printing the estimates to standard output. ") + "If -merged-volume is used, all voxels are used as a single component, rather than separated by structure.\n\n" + " must be one of the following:\n"; vector myStructureEnums; StructureEnum::getAllEnums(myStructureEnums); for (int i = 0; i < (int)myStructureEnums.size(); ++i) { myText += "\n" + StructureEnum::toName(myStructureEnums[i]); } ret->setHelpText(myText); return ret; } struct VolParams { AString name; CaretPointer data, roi;//prevent copy in vector expansion }; struct SurfParams { AString name; SurfaceFile* surf; CaretPointer data, roi; }; void OperationCiftiEstimateFWHM::useParameters(OperationParameters* myParams, ProgressObject* myProgObj) { LevelProgress myProgress(myProgObj); CiftiFile* myCifti = myParams->getCifti(1); bool mergedVol = myParams->getOptionalParameter(2)->m_present; int column = -1; OptionalParameter* columnOpt = myParams->getOptionalParameter(3); if (columnOpt->m_present) { column = columnOpt->getInteger(1) - 1;//compensate for 1-based UI indices if (column < 0 || column >= myCifti->getNumberOfColumns()) throw OperationException("invalid column index"); } const vector& surfInstances = *(myParams->getRepeatableParameterInstances(4)); int numInstances = (int)surfInstances.size(); for (int i = 0; i < numInstances; ++i) { bool ok = false; StructureEnum::fromName(surfInstances[i]->getString(1), &ok); if (!ok) throw OperationException("unrecognized structure name: " + surfInstances[i]->getString(1)); } vector surfUsed(surfInstances.size(), false); vector volProcess; vector surfProcess; const CiftiXMLOld& myXML = myCifti->getCiftiXMLOld(); if (myXML.getMappingType(CiftiXMLOld::ALONG_COLUMN) != CIFTI_INDEX_TYPE_BRAIN_MODELS) { throw OperationException("mapping type along column must be brain models"); } vector surfStructs, volStructs; myXML.getStructureLists(CiftiXMLOld::ALONG_COLUMN, surfStructs, volStructs); int numSurf = (int)surfStructs.size(); surfProcess.resize(numSurf); for (int i = 0; i < numSurf; ++i) { surfProcess[i].surf = NULL; for (int j = 0; j < numInstances; ++j) { StructureEnum::Enum myStruct = StructureEnum::fromName(surfInstances[j]->getString(1), NULL);//we already checked that this is a structure name if (myStruct == surfStructs[i]) { surfProcess[i].surf = surfInstances[j]->getSurface(2); break; } } if (surfProcess[i].surf == NULL) throw OperationException("missing surface for structure '" + StructureEnum::toName(surfStructs[i]) + "'"); surfProcess[i].name = StructureEnum::toName(surfStructs[i]); surfProcess[i].data.grabNew(new MetricFile()); surfProcess[i].roi.grabNew(new MetricFile()); AlgorithmCiftiSeparate(NULL, myCifti, CiftiXMLOld::ALONG_COLUMN, surfStructs[i], surfProcess[i].data, surfProcess[i].roi); if (surfProcess[i].surf->getNumberOfNodes() != surfProcess[i].data->getNumberOfNodes()) { throw OperationException("input surface for structure '" + StructureEnum::toName(surfStructs[i]) + "' has different number of nodes than the cifti file"); } } if (mergedVol) { volProcess.resize(1); volProcess[0].name = "Voxels"; volProcess[0].data.grabNew(new VolumeFile()); volProcess[0].roi.grabNew(new VolumeFile()); int64_t offset[3]; AlgorithmCiftiSeparate(NULL, myCifti, CiftiXMLOld::ALONG_COLUMN, volProcess[0].data, offset, volProcess[0].roi, true); } else { int numVol = (int)volStructs.size(); volProcess.resize(numVol); for (int i = 0; i < numVol; ++i) { volProcess[i].name = StructureEnum::toName(volStructs[i]); volProcess[i].data.grabNew(new VolumeFile()); volProcess[i].roi.grabNew(new VolumeFile()); int64_t offset[3]; AlgorithmCiftiSeparate(NULL, myCifti, CiftiXMLOld::ALONG_COLUMN, volStructs[i], volProcess[i].data, offset, volProcess[i].roi, true); } } if (column == -1) { int rowLength = (int)myCifti->getNumberOfColumns(); for (int i = 0; i < rowLength; ++i) { if (rowLength > 1) cout << "Column " << i + 1 << ":" << endl; for (int j = 0; j < (int)surfProcess.size(); ++j) { float fwhm = AlgorithmMetricEstimateFWHM::estimateFWHM(surfProcess[j].surf, surfProcess[j].data, surfProcess[j].roi, i); cout << surfProcess[j].name << " FWHM: " << fwhm << endl; } for (int j = 0; j < (int)volProcess.size(); ++j) { Vector3D fwhm = AlgorithmVolumeEstimateFWHM::estimateFWHM(volProcess[j].data, volProcess[j].roi, i); cout << volProcess[j].name << " FWHM: " << fwhm[0] << ", " << fwhm[1] << ", " << fwhm[2] << endl; } } } else { cout << "Column " << column + 1 << ":" << endl; for (int j = 0; j < (int)surfProcess.size(); ++j) { float fwhm = AlgorithmMetricEstimateFWHM::estimateFWHM(surfProcess[j].surf, surfProcess[j].data, surfProcess[j].roi, column); cout << surfProcess[j].name << " FWHM: " << fwhm << endl; } for (int j = 0; j < (int)volProcess.size(); ++j) { Vector3D fwhm = AlgorithmVolumeEstimateFWHM::estimateFWHM(volProcess[j].data, volProcess[j].roi, column); cout << volProcess[j].name << " FWHM: " << fwhm[0] << ", " << fwhm[1] << ", " << fwhm[2] << endl; } } } connectome-workbench-1.2.3+git41-gc4c6c90/src/Operations/OperationCiftiEstimateFWHM.h000066400000000000000000000026521300200146000301420ustar00rootroot00000000000000#ifndef __OPERATION_CIFTI_ESTIMATE_FWHM_H__ #define __OPERATION_CIFTI_ESTIMATE_FWHM_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 "AbstractOperation.h" namespace caret { class OperationCiftiEstimateFWHM : public AbstractOperation { public: static OperationParameters* getParameters(); static void useParameters(OperationParameters* myParams, ProgressObject* myProgObj); static AString getCommandSwitch(); static AString getShortDescription(); }; typedef TemplateAutoOperation AutoOperationCiftiEstimateFWHM; } #endif //__OPERATION_CIFTI_ESTIMATE_FWHM_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Operations/OperationCiftiExportDenseMapping.cxx000066400000000000000000000210561300200146000320330ustar00rootroot00000000000000/*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 "OperationCiftiExportDenseMapping.h" #include "OperationException.h" #include "CiftiFile.h" #include "CiftiXML.h" #include using namespace caret; using namespace std; AString OperationCiftiExportDenseMapping::getCommandSwitch() { return "-cifti-export-dense-mapping"; } AString OperationCiftiExportDenseMapping::getShortDescription() { return "WRITE INDEX TO ELEMENT MAPPING AS TEXT"; } OperationParameters* OperationCiftiExportDenseMapping::getParameters() { OperationParameters* ret = new OperationParameters(); ret->addCiftiParameter(1, "cifti", "the cifti file"); ret->addStringParameter(2, "direction", "which direction to export the mapping from, ROW or COLUMN");//TODO: 3rd+ dimension ParameterComponent* surfOpt = ret->createRepeatableParameter(3, "-surface", "export the the mapping of one surface structure"); surfOpt->addStringParameter(1, "structure", "the structure to output"); surfOpt->addStringParameter(2, "text-out", "output - the output text file");//fake the output formatting surfOpt->createOptionalParameter(3, "-no-cifti-index", "don't write the cifti index in the output file"); ParameterComponent* volOpt = ret->createRepeatableParameter(4, "-volume", "export the the mapping of one volume structure"); volOpt->addStringParameter(1, "structure", "the structure to output"); volOpt->addStringParameter(2, "text-out", "output - the output text file");//fake the output formatting volOpt->createOptionalParameter(3, "-no-cifti-index", "don't write the cifti index in the output file"); OptionalParameter* volAllOpt = ret->createOptionalParameter(5, "-volume-all", "export the the mapping of all voxels");//repeatable, for different options? volAllOpt->addStringParameter(1, "text-out", "output - the output text file");//fake the output formatting volAllOpt->createOptionalParameter(2, "-no-cifti-index", "don't write the cifti index in the output file"); volAllOpt->createOptionalParameter(3, "-structure", "write the structure each voxel belongs to in the output file"); //-surface-all? -all? AString helpText = AString("This command produces text files that describe the mapping from cifti indices to surface vertices or voxels. ") + "All indices are zero-based. " + "The default format for -surface is lines of the form:\n\n" + " \n\n" + "The default format for -volume and -volume-all is lines of the form:\n\n" + " \n\n" + "For each argument, use one of the following strings:\n"; vector myStructureEnums; StructureEnum::getAllEnums(myStructureEnums); for (int i = 0; i < (int)myStructureEnums.size(); ++i) { helpText += "\n" + StructureEnum::toName(myStructureEnums[i]); } ret->setHelpText(helpText); return ret; } void OperationCiftiExportDenseMapping::useParameters(OperationParameters* myParams, ProgressObject* myProgObj) { LevelProgress myProgress(myProgObj); CiftiFile* myCifti = myParams->getCifti(1); AString dirString = myParams->getString(2); int myDir; if (dirString == "ROW") { myDir = CiftiXML::ALONG_ROW; } else if (dirString == "COLUMN") { myDir = CiftiXML::ALONG_COLUMN; } else { throw OperationException("incorrect string for direction, use ROW or COLUMN"); } const vector& surfOpts = *(myParams->getRepeatableParameterInstances(3)); const vector& volOpts = *(myParams->getRepeatableParameterInstances(4)); OptionalParameter* volAllOpt = myParams->getOptionalParameter(5); const CiftiXML& myXML = myCifti->getCiftiXML(); if (myXML.getMappingType(myDir) != CiftiMappingType::BRAIN_MODELS) throw OperationException("specified direction in cifti file does not have BRAIN_MODELS mapping"); const CiftiBrainModelsMap& myDenseMap = myXML.getBrainModelsMap(myDir); for (int i = 0; i < (int)surfOpts.size(); ++i) { AString structName = surfOpts[i]->getString(1); bool ok = false; StructureEnum::Enum myStruct = StructureEnum::fromName(structName, &ok); if (!ok) throw OperationException("invalid structure name: '" + structName + "'"); if (!myDenseMap.hasSurfaceData(myStruct)) throw OperationException("no surface mapping found in specified direction for structure '" + structName + "'"); AString outName = surfOpts[i]->getString(2); bool writeCiftiIndex = !(surfOpts[i]->getOptionalParameter(3)->m_present); ofstream outFile(outName.toLocal8Bit().constData()); if (!outFile) throw OperationException("failed to open output text file"); vector myMap = myDenseMap.getSurfaceMap(myStruct); for (int j = 0; j < (int)myMap.size(); ++j) { if (writeCiftiIndex) outFile << myMap[j].m_ciftiIndex << " "; outFile << myMap[j].m_surfaceNode << "\n";//avoid endl so it doesn't constantly flush } outFile.flush(); if (!outFile) throw OperationException("failed to write to output text file"); } for (int i = 0; i < (int)volOpts.size(); ++i) { AString structName = volOpts[i]->getString(1); bool ok = false; StructureEnum::Enum myStruct = StructureEnum::fromName(structName, &ok); if (!ok) throw OperationException("invalid structure name: '" + structName + "'"); if (!myDenseMap.hasVolumeData(myStruct)) throw OperationException("no volume mapping found in specified direction for structure '" + structName + "'"); AString outName = volOpts[i]->getString(2); bool writeCiftiIndex = !(volOpts[i]->getOptionalParameter(3)->m_present); ofstream outFile(outName.toLocal8Bit().constData()); if (!outFile) throw OperationException("failed to open output text file"); vector myMap = myDenseMap.getVolumeStructureMap(myStruct); for (int j = 0; j < (int)myMap.size(); ++j) { if (writeCiftiIndex) outFile << myMap[j].m_ciftiIndex << " "; outFile << myMap[j].m_ijk[0] << " " << myMap[j].m_ijk[1] << " " << myMap[j].m_ijk[2] << "\n";//avoid endl so it doesn't constantly flush } outFile.flush(); if (!outFile) throw OperationException("failed to write to output text file"); } if (volAllOpt->m_present) { if (!myDenseMap.hasVolumeData()) throw OperationException("no volume data found in specified direction"); AString outName = volAllOpt->getString(1); bool writeCiftiIndex = !(volAllOpt->getOptionalParameter(2)->m_present); bool writeStructure = volAllOpt->getOptionalParameter(3)->m_present; ofstream outFile(outName.toLocal8Bit().constData()); if (!outFile) throw OperationException("failed to open output text file"); vector volStructs = myDenseMap.getVolumeStructureList();//NOTE: CiftiBrainModelsMap guarantees this is in cifti index order for (int j = 0; j < (int)volStructs.size(); ++j) { vector myMap = myDenseMap.getVolumeStructureMap(volStructs[j]); AString structName = StructureEnum::toName(volStructs[j]); for (int k = 0; k < (int)myMap.size(); ++k) { if (writeCiftiIndex) outFile << myMap[k].m_ciftiIndex << " "; if (writeStructure) outFile << structName << " "; outFile << myMap[k].m_ijk[0] << " " << myMap[k].m_ijk[1] << " " << myMap[k].m_ijk[2] << "\n";//avoid endl so it doesn't constantly flush } } outFile.flush(); if (!outFile) throw OperationException("failed to write to output text file"); } } connectome-workbench-1.2.3+git41-gc4c6c90/src/Operations/OperationCiftiExportDenseMapping.h000066400000000000000000000027211300200146000314560ustar00rootroot00000000000000#ifndef __OPERATION_CIFTI_EXPORT_DENSE_MAPPING_H__ #define __OPERATION_CIFTI_EXPORT_DENSE_MAPPING_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 "AbstractOperation.h" namespace caret { class OperationCiftiExportDenseMapping : public AbstractOperation { public: static OperationParameters* getParameters(); static void useParameters(OperationParameters* myParams, ProgressObject* myProgObj); static AString getCommandSwitch(); static AString getShortDescription(); }; typedef TemplateAutoOperation AutoOperationCiftiExportDenseMapping; } #endif //__OPERATION_CIFTI_EXPORT_DENSE_MAPPING_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Operations/OperationCiftiLabelExportTable.cxx000066400000000000000000000074131300200146000314510ustar00rootroot00000000000000/*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 "OperationCiftiLabelExportTable.h" #include "OperationException.h" #include "CiftiFile.h" #include "GiftiLabel.h" //we should rename these to not imply that they are gifti-specific #include "GiftiLabelTable.h" #include #include using namespace caret; using namespace std; AString OperationCiftiLabelExportTable::getCommandSwitch() { return "-cifti-label-export-table"; } AString OperationCiftiLabelExportTable::getShortDescription() { return "EXPORT LABEL TABLE FROM CIFTI AS TEXT"; } OperationParameters* OperationCiftiLabelExportTable::getParameters() { OperationParameters* ret = new OperationParameters(); ret->addCiftiParameter(1, "label-in", "the input cifti label file"); ret->addStringParameter(2, "map", "the number or name of the label map to use"); ret->addStringParameter(3, "table-out", "output - the output text file");//fake output formatting ret->setHelpText( AString("Takes the label table from the cifti label map, and writes it to a text format matching what is expected by -cifti-label-import.") ); return ret; } int OperationCiftiLabelExportTable::floatTo255(const float& in) { return (int)floor(in * 255.0f + 0.5f); } void OperationCiftiLabelExportTable::useParameters(OperationParameters* myParams, ProgressObject* myProgObj) { LevelProgress myProgress(myProgObj); CiftiFile* myCifti = myParams->getCifti(1); AString mapString = myParams->getString(2); AString outfileName = myParams->getString(3); const CiftiXML& myXML = myCifti->getCiftiXML(); if (myXML.getMappingType(CiftiXML::ALONG_ROW) != CiftiMappingType::LABELS) throw OperationException("cifti file must have LABELS mapping along row"); const CiftiLabelsMap& myLabelsMap = myXML.getLabelsMap(CiftiXML::ALONG_ROW); int64_t mapIndex = myLabelsMap.getIndexFromNumberOrName(mapString); if (mapIndex == -1) throw OperationException("map '" + mapString + "' not found in label map"); ofstream outFile(outfileName.toLocal8Bit().constData()); if (!outFile) throw OperationException("failed to open output text file"); const GiftiLabelTable* myTable = myLabelsMap.getMapLabelTable(mapIndex); set allKeys = myTable->getKeys(); int32_t unassignedKey = myTable->getUnassignedLabelKey(); for (set::iterator iter = allKeys.begin(); iter != allKeys.end(); ++iter) { if (*iter == unassignedKey) continue;//don't output the unused key, because import doesn't want it in the text file const GiftiLabel* thisLabel = myTable->getLabel(*iter); outFile << thisLabel->getName() << endl; outFile << thisLabel->getKey() << " " << floatTo255(thisLabel->getRed()) << " " << floatTo255(thisLabel->getGreen()) << " " << floatTo255(thisLabel->getBlue()) << " " << floatTo255(thisLabel->getAlpha()) << endl; if (!outFile) throw OperationException("error writing to output text file"); } } connectome-workbench-1.2.3+git41-gc4c6c90/src/Operations/OperationCiftiLabelExportTable.h000066400000000000000000000027651300200146000311030ustar00rootroot00000000000000#ifndef __OPERATION_CIFTI_LABEL_EXPORT_TABLE_H__ #define __OPERATION_CIFTI_LABEL_EXPORT_TABLE_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 "AbstractOperation.h" namespace caret { class OperationCiftiLabelExportTable : public AbstractOperation { static int floatTo255(const float& in); public: static OperationParameters* getParameters(); static void useParameters(OperationParameters* myParams, ProgressObject* myProgObj); static AString getCommandSwitch(); static AString getShortDescription(); }; typedef TemplateAutoOperation AutoOperationCiftiLabelExportTable; } #endif //__OPERATION_CIFTI_LABEL_EXPORT_TABLE_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Operations/OperationCiftiLabelImport.cxx000066400000000000000000000320771300200146000304760ustar00rootroot00000000000000/*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 "OperationCiftiLabelImport.h" #include "OperationException.h" #include "CaretLogger.h" #include "CiftiFile.h" #include "FileInformation.h" #include "GiftiLabel.h" #include "GiftiLabelTable.h" #include #include #include #include #include #include #include #include using namespace caret; using namespace std; AString OperationCiftiLabelImport::getCommandSwitch() { return "-cifti-label-import"; } AString OperationCiftiLabelImport::getShortDescription() { return "MAKE A CIFTI LABEL FILE FROM A CIFTI FILE"; } OperationParameters* OperationCiftiLabelImport::getParameters() { OperationParameters* ret = new OperationParameters(); ret->addCiftiParameter(1, "input", "the input cifti file"); ret->addStringParameter(2, "label-list-file", "text file containing the values and names for labels"); ret->addCiftiOutputParameter(3, "output", "the output cifti label file"); ret->createOptionalParameter(4, "-discard-others", "set any values not mentioned in the label list to the ??? label"); OptionalParameter* unlabeledOption = ret->createOptionalParameter(5, "-unlabeled-value", "set the value that will be interpreted as unlabeled"); unlabeledOption->addIntegerParameter(1, "value", "the numeric value for unlabeled (default 0)"); ret->createOptionalParameter(6, "-drop-unused-labels", "remove any unused label values from the label table"); ret->setHelpText( AString("Creates a cifti label file from a cifti file with label-like values. ") + "You may specify the empty string ('' will work on linux/mac) for , which will be treated as if it is an empty file. " + "It is assumed that a value of 0 in the input file means \"unlabeled\", unless -unlabeled-value is specified. " + "Do not specify the \"unlabeled\" label in the text file.\n\n" + "The label list file must have the following format (2 lines per label):\n\n" + "\n \n...\n\n" + "Label names are specified on a separate line from their value and color, in order to let label names contain spaces. " + "Whitespace is trimmed from both ends of the label name, but is kept if it is in the middle of a label. " + "The value of specifies what value in the imported file should be used as this label. " + "The values of , , and must be integers from 0 to 255, and will specify the color the label is drawn as " + "(alpha of 255 means fully opaque, which is probably what you want).\n\n" + "By default, it will create new label names with names like LABEL_5 for any values encountered that are not mentioned in the " + "list file, specify -discard-others to instead set these values to the \"unlabeled\" key." ); return ret; } void OperationCiftiLabelImport::useParameters(OperationParameters* myParams, ProgressObject* myProgObj) { LevelProgress myProgress(myProgObj); CiftiFile* ciftiIn = myParams->getCifti(1); AString listfileName = myParams->getString(2); CiftiFile* ciftiOut = myParams->getOutputCifti(3); bool discardOthers = myParams->getOptionalParameter(4)->m_present; int32_t unlabeledValue = 0; OptionalParameter* unlabeledOption = myParams->getOptionalParameter(5); if (unlabeledOption->m_present) { unlabeledValue = (int32_t)unlabeledOption->getInteger(1); } bool dropUnused = myParams->getOptionalParameter(6)->m_present; map translate; GiftiLabelTable myTable; if (listfileName != "") { AString temp; FileInformation textFileInfo(listfileName); if (!textFileInfo.exists()) { throw OperationException("label list file doesn't exist"); } fstream labelListFile(listfileName.toLocal8Bit().constData(), fstream::in); if (!labelListFile.good()) { throw OperationException("error reading label list file"); } string labelName; int32_t value, red, green, blue, alpha; int labelCount = 0; translate[unlabeledValue] = 0;//placeholder, we don't know the correct translated value yet while (labelListFile.good()) { ++labelCount;//just for error messages, so start at 1 getline(labelListFile, labelName); labelListFile >> value; if (labelListFile.eof() && labelName == "") break;//if end of file trying to read an int, and label name is empty, its really just end of file labelListFile >> red; labelListFile >> green; labelListFile >> blue; if (!(labelListFile >> alpha))//yes, that is seriously the correct way to check if input was successfully extracted...so much fail { throw OperationException("label list file is malformed for entry #" + AString::number(labelCount) + ": " + AString(labelName.c_str())); } if (red < 0 || red > 255) { throw OperationException("bad value for red for entry #" + AString::number(labelCount) + ", " + AString(labelName.c_str()) + ": " + AString::number(red)); } if (green < 0 || green > 255) { throw OperationException("bad value for green for entry #" + AString::number(labelCount) + ", " + AString(labelName.c_str()) + ": " + AString::number(green)); } if (blue < 0 || blue > 255) { throw OperationException("bad value for blue for entry #" + AString::number(labelCount) + ", " + AString(labelName.c_str()) + ": " + AString::number(blue)); } if (alpha < 0 || alpha > 255) { throw OperationException("bad value for alpha for entry #" + AString::number(labelCount) + ", " + AString(labelName.c_str()) + ": " + AString::number(alpha)); } if (value == GiftiLabel::getInvalidLabelKey()) { throw OperationException("entry #" + AString::number(labelCount) + ", " + AString(labelName.c_str()) + " specifies unusable key value: " + value); } while (isspace(labelListFile.peek())) { labelListFile.ignore();//drop the newline, possible carriage return or other whitespace so that getline doesn't get nothing, and cause int extraction to fail } temp = AString(labelName.c_str()).trimmed();//drop errant CR or other whitespace from beginning and end of lines if (translate.find(value) != translate.end()) { if (value == unlabeledValue) { throw OperationException("the unlabeled value must not be specified in label list file"); } else { throw OperationException(AString("label key ") + AString::number(value) + " specified more than once"); } } GiftiLabel myLabel(value, temp, red, green, blue, alpha); if (myTable.getLabelKeyFromName(temp) != GiftiLabel::getInvalidLabelKey()) { AString nameBase = temp, newName;//resolve collision by generating a name with an additional number on it bool success = false; for (int extra = 1; extra < 100; ++extra)//but stop at 100, because really... { newName = nameBase + "_" + AString::number(extra); if (myTable.getLabelKeyFromName(newName) == GiftiLabel::getInvalidLabelKey()) { success = true; break; } } if (success) { CaretLogWarning("name collision in input name '" + nameBase + "', changing one to '" + newName + "'"); } else { throw OperationException("giving up on resolving name collision for input name '" + nameBase + "'"); } myLabel.setName(newName); } int32_t newValue; if (value == 0)//because label 0 exists in the default constructed table { myTable.insertLabel(&myLabel);//but we do want to be able to overwrite the default 0 label newValue = 0;//if value 0 is specified twice, or once without specifying a different unlabeled value, the check versus the translate map will catch it } else { newValue = myTable.addLabel(&myLabel);//we don't want to overwrite relocated labels } translate[value] = newValue; } } int32_t unusedLabel = myTable.getUnassignedLabelKey(); translate[unlabeledValue] = unusedLabel; const CiftiXMLOld& xmlIn = ciftiIn->getCiftiXMLOld(); int rowSize = xmlIn.getNumberOfColumns(), colSize = xmlIn.getNumberOfRows(); CiftiXMLOld xmlOut = xmlIn; xmlOut.resetRowsToLabels(rowSize); vector > usedArray(rowSize); vector rowScratch(rowSize); vector > matrixOut(colSize, vector(rowSize));//because the label table gets modified as we scan values, store the translated values instead of going back for a second pass for (int row = 0; row < colSize; ++row) { ciftiIn->getRow(rowScratch.data(), row); for (int col = 0; col < rowSize; ++col) { int32_t labelval = (int32_t)floor(rowScratch[col] + 0.5f);//just in case it somehow got poorly encoded, round to nearest if (dropUnused) { usedArray[col].insert(labelval); } map::iterator myiter = translate.find(labelval); if (myiter == translate.end()) { if (discardOthers) { matrixOut[row][col] = unusedLabel; } else {//use a random color, but fully opaque for the label GiftiLabel myLabel(labelval, AString("LABEL_") + AString::number(labelval), rand() & 255, rand() & 255, rand() & 255, 255); if (myTable.getLabelKeyFromName(myLabel.getName()) != GiftiLabel::getInvalidLabelKey()) { AString nameBase = myLabel.getName(), newName;//resolve collision by generating a name with an additional number on it bool success = false; for (int extra = 1; extra < 100; ++extra)//but stop at 100, because really... { newName = nameBase + "_" + AString::number(extra); if (myTable.getLabelKeyFromName(newName) == GiftiLabel::getInvalidLabelKey()) { success = true; break; } } if (success) { CaretLogWarning("name collision in auto-generated name '" + nameBase + "', changed to '" + newName + "'"); } else { throw OperationException("giving up on resolving name collision for auto-generated name '" + nameBase + "'"); } myLabel.setName(newName); } int32_t newValue = myTable.addLabel(&myLabel);//don't overwrite any values in the table translate[labelval] = newValue; matrixOut[row][col] = newValue; } } else { matrixOut[row][col] = myiter->second; } } } for (int i = 0; i < rowSize; ++i) { xmlOut.setMapNameForIndex(CiftiXMLOld::ALONG_ROW, i, xmlIn.getMapName(CiftiXMLOld::ALONG_ROW, i)); if (dropUnused) { GiftiLabelTable colTable = myTable; colTable.deleteUnusedLabels(usedArray[i]); *(xmlOut.getMapLabelTable(CiftiXMLOld::ALONG_ROW, i)) = colTable; } else { *(xmlOut.getMapLabelTable(CiftiXMLOld::ALONG_ROW, i)) = myTable; } } ciftiOut->setCiftiXML(xmlOut); for (int row = 0; row < colSize; ++row) { ciftiOut->setRow(matrixOut[row].data(), row); } } connectome-workbench-1.2.3+git41-gc4c6c90/src/Operations/OperationCiftiLabelImport.h000066400000000000000000000026441300200146000301200ustar00rootroot00000000000000#ifndef __OPERATION_CIFTI_LABEL_IMPORT_H__ #define __OPERATION_CIFTI_LABEL_IMPORT_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 "AbstractOperation.h" namespace caret { class OperationCiftiLabelImport : public AbstractOperation { public: static OperationParameters* getParameters(); static void useParameters(OperationParameters* myParams, ProgressObject* myProgObj); static AString getCommandSwitch(); static AString getShortDescription(); }; typedef TemplateAutoOperation AutoOperationCiftiLabelImport; } #endif //__OPERATION_CIFTI_LABEL_IMPORT_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Operations/OperationCiftiMath.cxx000066400000000000000000000352461300200146000271560ustar00rootroot00000000000000/*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 "OperationCiftiMath.h" #include "OperationException.h" #include "CaretAssert.h" #include "CaretLogger.h" #include "CaretMathExpression.h" #include "CiftiFile.h" #include "CiftiXML.h" #include "MultiDimIterator.h" #include using namespace caret; using namespace std; AString OperationCiftiMath::getCommandSwitch() { return "-cifti-math"; } AString OperationCiftiMath::getShortDescription() { return "EVALUATE EXPRESSION ON CIFTI FILES"; } OperationParameters* OperationCiftiMath::getParameters() { OperationParameters* ret = new OperationParameters(); ret->addStringParameter(1, "expression", "the expression to evaluate, in quotes"); ret->addCiftiOutputParameter(2, "cifti-out", "the output cifti file"); ParameterComponent* varOpt = ret->createRepeatableParameter(3, "-var", "a cifti file to use as a variable"); varOpt->addStringParameter(1, "name", "the name of the variable, as used in the expression"); varOpt->addCiftiParameter(2, "cifti", "the cifti file to use as this variable"); ParameterComponent* selectOpt = varOpt->createRepeatableParameter(3, "-select", "select a single index from a dimension");//repeatable option to repeatable option selectOpt->addIntegerParameter(1, "dim", "the dimension to select from (1-based)"); selectOpt->addIntegerParameter(2, "index", "the index to use (1-based)"); selectOpt->createOptionalParameter(3, "-repeat", "repeat the selected values for each index of output in this dimension");//with a repeat option OptionalParameter* fixNanOpt = ret->createOptionalParameter(4, "-fixnan", "replace NaN results with a value"); fixNanOpt->addDoubleParameter(1, "replace", "value to replace NaN with"); ret->createOptionalParameter(5, "-override-mapping-check", "don't check the mappings for compatibility, only check length"); AString myText = AString("This command evaluates at each matrix element independently. ") + "There must be at least one -var option (to get the output layout from), even if the specified in it isn't used in .\n\n" + "To select a single column from a 2D file (most cifti files are 2D), use -select 1 , where is 1-based. " + "To select a single row from a 2D file, use -select 2 . " + "Where -select is not used, the cifti files must have compatible mappings (e.g., brain models and parcels mappings must match exactly except for parcel names). " + "Use -override-mapping-check to skip this checking.\n\n" + "Filenames are not valid in , use a variable name and a -var option with matching to specify an input file. " + "The format of is as follows:\n\n"; myText += CaretMathExpression::getExpressionHelpInfo(); ret->setHelpText(myText); return ret; } void OperationCiftiMath::useParameters(OperationParameters* myParams, ProgressObject* myProgObj) { LevelProgress myProgress(myProgObj); AString expression = myParams->getString(1); CaretMathExpression myExpr(expression); cout << "parsed '" + expression + "' as '" + myExpr.toString() + "'" << endl; vector myVarNames = myExpr.getVarNames(); CiftiFile* myCiftiOut = myParams->getOutputCifti(2); const vector& myVarOpts = *(myParams->getRepeatableParameterInstances(3)); OptionalParameter* fixNanOpt = myParams->getOptionalParameter(4); bool nanfix = false; float nanfixval = 0; if (fixNanOpt->m_present) { nanfix = true; nanfixval = (float)fixNanOpt->getDouble(1); } bool overrideMapCheck = myParams->getOptionalParameter(5)->m_present; int numInputs = myVarOpts.size(); int numVars = myVarNames.size(); vector varCiftiFiles(numVars, (CiftiFile*)NULL); if (numInputs == 0 && numVars == 0) throw OperationException("you must specify at least one input file (-var), even if the expression doesn't use a variable"); CiftiXML outXML; QString xmlText; vector outDims;//don't even assume 2 dimensions, in case someone makes a 1-d cifti vector > selectInfo(numVars); for (int i = 0; i < numInputs; ++i) { AString varName = myVarOpts[i]->getString(1); double constVal; if (CaretMathExpression::getNamedConstant(varName, constVal)) { throw OperationException("'" + varName + "' is a named constant equal to " + AString::number(constVal, 'g', 15) + ", please use a different variable name"); } const CiftiXML& tempXML = myVarOpts[i]->getCifti(2)->getCiftiXML(); int thisNumDims = tempXML.getNumberOfDimensions(); vector thisSelectInfo(thisNumDims, -1); vector thisRepeat(thisNumDims, false); const vector& thisSelectOpts = *(myVarOpts[i]->getRepeatableParameterInstances(3)); for (int j = 0; j < (int)thisSelectOpts.size(); ++j) { int dim = (int)thisSelectOpts[j]->getInteger(1) - 1; int64_t thisIndex = thisSelectOpts[j]->getInteger(2) - 1; if (dim >= (int)thisSelectInfo.size()) { if (thisIndex != 0) throw OperationException("-select used for variable '" + varName + "' with index other than 1 on nonexistent dimension"); thisSelectInfo.resize(dim + 1, -1); thisRepeat.resize(dim + 1, false); } thisSelectInfo[dim] = thisIndex; thisRepeat[dim] = thisSelectOpts[j]->getOptionalParameter(3)->m_present; } bool found = false; for (int j = 0; j < numVars; ++j) { if (varName == myVarNames[j]) { if (varCiftiFiles[j] != NULL) throw OperationException("variable '" + varName + "' specified more than once"); found = true; varCiftiFiles[j] = myVarOpts[i]->getCifti(2); selectInfo[j] = thisSelectInfo;//copy selection info break; } } if (!found && (numVars != 0 || numInputs != 1))//supress warning when a single -var is used with a constant expression, as required per help { CaretLogWarning("variable '" + varName + "' not used in expression"); } int newNumDims = (int)max(thisSelectInfo.size(), outDims.size());//now, to figure out the output dimensions with -select and -repeat for (int j = 0; j < newNumDims; ++j) { if (j >= (int)outDims.size())//need to expand output dimensions { outXML.setNumberOfDimensions(j + 1);//does not clear existing mappings outDims.push_back(-1);//unknown length } if (j >= (int)thisSelectInfo.size())//need to expand input info { thisSelectInfo.push_back(-1);//use "all" indices, but there is only 1 (virtual) index, pushing 0 should have same effect thisRepeat.push_back(false);//repeat not specified } if (outDims[j] == -1)//if we don't know the output length yet, put it in if we have it (-repeat not specified) { if (thisSelectInfo[j] == -1)//no -select for this dimension, use all maps { if (j < thisNumDims) { outDims[j] = tempXML.getDimensionLength(j); outXML.setMap(j, *(tempXML.getMap(j)));//copy the mapping type, since this input defines this dimension } else {//if higher dimension than the file has, transparently say it is of length 1, and don't use the mapping outDims[j] = 1; } } else {//-select was used if (!thisRepeat[j])//if -repeat wasn't used, output length is 1 { outDims[j] = 1; } } } else { if (thisSelectInfo[j] == -1)//-select was not used { if (j < thisNumDims) { if (outDims[j] != tempXML.getDimensionLength(j)) { throw OperationException("variable '" + varName + "' has length " + AString::number(tempXML.getDimensionLength(j)) + " for dimension " + AString::number(j + 1) + " while previous -var options require a length of " + AString::number(outDims[j])); } if (outXML.getMap(j) == NULL)//dimension was set to 1 by -select, but didn't set a mapping, so borrow from here { outXML.setMap(j, *(tempXML.getMap(j))); } else {//test mapping types for compatibility since -select wasn't used AString explanation; if (!overrideMapCheck && !outXML.getMap(j)->approximateMatch(*(tempXML.getMap(j)), &explanation)) { throw OperationException("variable " + varName + "'s " + CiftiMappingType::mappingTypeToName(tempXML.getMap(j)->getType()) + " mapping on dimension " + AString::number(j + 1) + " doesn't match mappings from earlier -var options: '" + explanation + "'"); } } } else { if (outDims[j] != 1) { throw OperationException(AString("variable '" + varName + "' is of lower dimensionality than output, ") + "and the length of output dimension " + AString::number(j + 1) + " is " + AString::number(outDims[j]) + ", you might want to use -select with -repeat"); } } } else { if (!thisRepeat[j]) { if (outDims[j] != 1) { throw OperationException("variable '" + varName + "' uses -select for dimension " + AString::number(j + 1) + ", but previous -var options require a length of " + AString::number(outDims[j])); } } } } } } for (int i = 0; i < numVars; ++i) { if (varCiftiFiles[i] == NULL) throw OperationException("no -var option specified for variable '" + myVarNames[i] + "'"); } CiftiScalarsMap dummyMap;//make an empty length-1 scalar map for dimensions we don't have a mapping for dummyMap.setLength(1); for (int i = 0; i < outXML.getNumberOfDimensions(); ++i) { if (outDims[i] == -1) throw OperationException("all -var options used -select and -repeat for dimension " + AString::number(i + 1) + ", there is no file to get the dimension length from"); if (outXML.getMap(i) == NULL)//-select was used in all variables for this dimension, so we don't have a mapping { outXML.setMap(i, dummyMap);//so, make it a length-1 scalar with no name and empty metadata } CaretAssert(outDims[i] == outXML.getDimensionLength(i)); } if (outXML.getNumberOfDimensions() < 1) throw OperationException("output must have at least 1 dimension"); myCiftiOut->setCiftiXML(outXML); vector values(numVars), scratchRow(outDims[0]); vector > inputRows(numVars); vector > loadedRow(numVars);//to detect and prevent rereading the same row for (int v = 0; v < numVars; ++v) { inputRows[v].resize(varCiftiFiles[v]->getCiftiXML().getDimensionLength(CiftiXML::ALONG_ROW)); loadedRow[v].resize(varCiftiFiles[v]->getCiftiXML().getNumberOfDimensions() - 1, -1);//we always load a full row, so ignore first dim } for (MultiDimIterator iter(vector(outDims.begin() + 1, outDims.end())); !iter.atEnd(); ++iter) { for (int v = 0; v < numVars; ++v)//first, retrieve whichever rows are needed { bool needToLoad = false; for (int dim = 0; dim < (int)loadedRow[v].size(); ++dim) { int64_t indexNeeded = -1; if (selectInfo[v][dim + 1] == -1) { CaretAssert(dim + 1 < (int)outDims.size());//"match to output index" can't work past output dimensionality indexNeeded = (*iter)[dim];//NOTE: iter also doesn't include the first dim } else { indexNeeded = selectInfo[v][dim + 1]; } if (indexNeeded != loadedRow[v][dim]) { needToLoad = true; loadedRow[v][dim] = indexNeeded; } } if (needToLoad) { varCiftiFiles[v]->getRow(inputRows[v].data(), loadedRow[v]); } } for (int j = 0; j < outDims[0]; ++j) { for (int v = 0; v < numVars; ++v)//now we check for select along row { if (selectInfo[v][0] == -1) { values[v] = inputRows[v][j]; } else { values[v] = inputRows[v][selectInfo[v][0]]; } } scratchRow[j] = (float)myExpr.evaluate(values); if (nanfix && scratchRow[j] != scratchRow[j]) { scratchRow[j] = nanfixval; } } myCiftiOut->setRow(scratchRow.data(), *iter); } } connectome-workbench-1.2.3+git41-gc4c6c90/src/Operations/OperationCiftiMath.h000066400000000000000000000025671300200146000266030ustar00rootroot00000000000000#ifndef __OPERATION_CIFTI_MATH_H__ #define __OPERATION_CIFTI_MATH_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 "AbstractOperation.h" namespace caret { class OperationCiftiMath : public AbstractOperation { public: static OperationParameters* getParameters(); static void useParameters(OperationParameters* myParams, ProgressObject* myProgObj); static AString getCommandSwitch(); static AString getShortDescription(); }; typedef TemplateAutoOperation AutoOperationCiftiMath; } #endif //__OPERATION_CIFTI_MATH_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Operations/OperationCiftiMerge.cxx000066400000000000000000000401771300200146000273230ustar00rootroot00000000000000/*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 "OperationCiftiMerge.h" #include "OperationException.h" #include "CaretAssert.h" #include "CaretPointer.h" #include "CiftiFile.h" #include using namespace caret; using namespace std; AString OperationCiftiMerge::getCommandSwitch() { return "-cifti-merge"; } AString OperationCiftiMerge::getShortDescription() { return "MERGE CIFTI TIMESERIES, SCALAR, OR LABEL FILES"; } OperationParameters* OperationCiftiMerge::getParameters() { OperationParameters* ret = new OperationParameters(); ret->addCiftiOutputParameter(1, "cifti-out", "output cifti file"); ParameterComponent* ciftiOpt = ret->createRepeatableParameter(2, "-cifti", "specify an input cifti file"); ciftiOpt->addCiftiParameter(1, "cifti-in", "a cifti file to use columns from"); ParameterComponent* columnOpt = ciftiOpt->createRepeatableParameter(2, "-column", "select a single column to use"); columnOpt->addStringParameter(1, "column", "the column number (starting from 1) or name"); OptionalParameter* upToOpt = columnOpt->createOptionalParameter(2, "-up-to", "use an inclusive range of columns"); upToOpt->addStringParameter(1, "last-column", "the number or name of the last column to include"); upToOpt->createOptionalParameter(2, "-reverse", "use the range in reverse order"); ret->setHelpText( AString("Given input CIFTI files which have matching mappings along columns, and for which mappings along rows ") + "are the same type, all either series, scalars, or labels, this command concatenates the specified columns horizontally (rows become longer).\n\n" + "Example: wb_command -cifti-merge out.dtseries.nii -cifti first.dtseries.nii -column 1 -cifti second.dtseries.nii\n\n" + "This example would take the first column from first.dtseries.nii, followed by all columns from second.dtseries.nii, " + "and write these columns to out.dtseries.nii." ); return ret; } void OperationCiftiMerge::useParameters(OperationParameters* myParams, ProgressObject* myProgObj) { LevelProgress myProgress(myProgObj); CiftiFile* ciftiOut = myParams->getOutputCifti(1); const vector& myInputs = *(myParams->getRepeatableParameterInstances(2)); vector ciftiList; int numInputs = (int)myInputs.size(); if (numInputs == 0) throw OperationException("no inputs specified"); const CiftiFile* firstCifti = myInputs[0]->getCifti(1); const CiftiXML& baseXML = firstCifti->getCiftiXML(); if (baseXML.getNumberOfDimensions() != 2) throw OperationException("only 2D cifti are supported"); const CiftiMappingType& baseColMapping = *(baseXML.getMap(CiftiXML::ALONG_COLUMN)), &baseRowMapping = *(baseXML.getMap(CiftiXML::ALONG_ROW)); switch (baseRowMapping.getType()) { case CiftiMappingType::SCALARS: case CiftiMappingType::LABELS: case CiftiMappingType::SERIES: break; default: throw OperationException("row mapping type must be series, scalars, or labels"); } int64_t numOutColumns = 0;//output row length for (int i = 0; i < numInputs; ++i) { const CiftiFile* ciftiIn = myInputs[i]->getCifti(1); vector thisDims = ciftiIn->getDimensions(); const CiftiXML& thisXML = ciftiIn->getCiftiXML(); if (thisXML.getNumberOfDimensions() != 2) throw OperationException("only 2D cifti are supported"); if (!thisXML.getMap(CiftiXML::ALONG_COLUMN)->approximateMatch(baseColMapping)) throw OperationException("file '" + ciftiIn->getFileName() + "' has non-matching mapping along columns"); if (thisXML.getMappingType(CiftiXML::ALONG_ROW) != baseRowMapping.getType()) throw OperationException("file '" + ciftiIn->getFileName() + "' has different mapping type along rows"); const vector& columnOpts = *(myInputs[i]->getRepeatableParameterInstances(2)); int numColumnOpts = (int)columnOpts.size(); if (numColumnOpts > 0) { for (int j = 0; j < numColumnOpts; ++j) { int64_t initialColumn = thisXML.getMap(CiftiXML::ALONG_ROW)->getIndexFromNumberOrName(columnOpts[j]->getString(1));//this function has the 1-indexing convention built in if (initialColumn < 0 || initialColumn >= thisDims[0]) throw OperationException("column '" + columnOpts[j]->getString(1) + "' not valid in file '" + ciftiIn->getFileName() + "'"); OptionalParameter* upToOpt = columnOpts[j]->getOptionalParameter(2); if (upToOpt->m_present) { int finalColumn = thisXML.getMap(CiftiXML::ALONG_ROW)->getIndexFromNumberOrName(upToOpt->getString(1));//ditto if (finalColumn < 0 || finalColumn >= thisDims[0]) throw OperationException("ending column '" + columnOpts[j]->getString(1) + "' not valid in file '" + ciftiIn->getFileName() + "'"); if (finalColumn < initialColumn) throw OperationException("ending column occurs before starting column in file '" + ciftiIn->getFileName() + "'"); numOutColumns += finalColumn - initialColumn + 1;//inclusive - we don't need to worry about reversing for counting, though } else { numOutColumns += 1; } } } else { numOutColumns += thisDims[0]; } } CiftiScalarsMap outScalarMap;//we only use one of these CiftiLabelsMap outLabelMap; CiftiSeriesMap outSeriesMap; bool isLabel = false, doLoop = true;//whether we need to set attributes on each column switch (baseRowMapping.getType()) { case CiftiMappingType::SCALARS: outScalarMap.setLength(numOutColumns); break; case CiftiMappingType::LABELS: outLabelMap.setLength(numOutColumns); isLabel = true; break; case CiftiMappingType::SERIES: outSeriesMap.setLength(numOutColumns); outSeriesMap.setUnit(((const CiftiSeriesMap&)baseRowMapping).getUnit()); outSeriesMap.setStart(((const CiftiSeriesMap&)baseRowMapping).getStart()); outSeriesMap.setStep(((const CiftiSeriesMap&)baseRowMapping).getStep()); doLoop = false; break; default: CaretAssert(false); } int64_t curCol = 0, scratchRowLength = 0; for (int i = 0; i < numInputs; ++i) { const CiftiFile* ciftiIn = myInputs[i]->getCifti(1); vector thisDims = ciftiIn->getDimensions(); const CiftiXML& thisXML = ciftiIn->getCiftiXML(); const vector& columnOpts = *(myInputs[i]->getRepeatableParameterInstances(2)); int numColumnOpts = (int)columnOpts.size(); if (numColumnOpts > 0) { scratchRowLength = max(scratchRowLength, thisXML.getDimensionLength(CiftiXML::ALONG_ROW));//if we use the entire row, we don't need a separate scratch row for it if (doLoop) { for (int j = 0; j < numColumnOpts; ++j) { int64_t initialColumn = thisXML.getMap(CiftiXML::ALONG_ROW)->getIndexFromNumberOrName(columnOpts[j]->getString(1));//this function has the 1-indexing convention built in OptionalParameter* upToOpt = columnOpts[j]->getOptionalParameter(2);//we already checked that these strings give a valid column if (upToOpt->m_present) { int finalColumn = thisXML.getMap(CiftiXML::ALONG_ROW)->getIndexFromNumberOrName(upToOpt->getString(1));//ditto bool reverse = upToOpt->getOptionalParameter(2)->m_present; if (reverse) { for (int c = finalColumn; c >= initialColumn; --c) { if (isLabel) { const CiftiLabelsMap& thisLabelMap = thisXML.getLabelsMap(CiftiXML::ALONG_ROW); outLabelMap.setMapName(curCol, thisLabelMap.getMapName(c)); *(outLabelMap.getMapLabelTable(curCol)) = *(thisLabelMap.getMapLabelTable(c)); *(outLabelMap.getMapMetadata(curCol)) = *(thisLabelMap.getMapMetadata(c)); } else { const CiftiScalarsMap& thisScalarMap = thisXML.getScalarsMap(CiftiXML::ALONG_ROW); outScalarMap.setMapName(curCol, thisScalarMap.getMapName(c)); *(outScalarMap.getMapPalette(curCol)) = *(thisScalarMap.getMapPalette(c)); *(outScalarMap.getMapMetadata(curCol)) = *(thisScalarMap.getMapMetadata(c)); } ++curCol; } } else { for (int c = initialColumn; c <= finalColumn; ++c) { if (isLabel) { const CiftiLabelsMap& thisLabelMap = thisXML.getLabelsMap(CiftiXML::ALONG_ROW); outLabelMap.setMapName(curCol, thisLabelMap.getMapName(c)); *(outLabelMap.getMapLabelTable(curCol)) = *(thisLabelMap.getMapLabelTable(c)); *(outLabelMap.getMapMetadata(curCol)) = *(thisLabelMap.getMapMetadata(c)); } else { const CiftiScalarsMap& thisScalarMap = thisXML.getScalarsMap(CiftiXML::ALONG_ROW); outScalarMap.setMapName(curCol, thisScalarMap.getMapName(c)); *(outScalarMap.getMapPalette(curCol)) = *(thisScalarMap.getMapPalette(c)); *(outScalarMap.getMapMetadata(curCol)) = *(thisScalarMap.getMapMetadata(c)); } ++curCol; } } } else { if (isLabel) { const CiftiLabelsMap& thisLabelMap = thisXML.getLabelsMap(CiftiXML::ALONG_ROW); outLabelMap.setMapName(curCol, thisLabelMap.getMapName(initialColumn)); *(outLabelMap.getMapLabelTable(curCol)) = *(thisLabelMap.getMapLabelTable(initialColumn)); *(outLabelMap.getMapMetadata(curCol)) = *(thisLabelMap.getMapMetadata(initialColumn)); } else { const CiftiScalarsMap& thisScalarMap = thisXML.getScalarsMap(CiftiXML::ALONG_ROW); outScalarMap.setMapName(curCol, thisScalarMap.getMapName(initialColumn)); *(outScalarMap.getMapPalette(curCol)) = *(thisScalarMap.getMapPalette(initialColumn)); *(outScalarMap.getMapMetadata(curCol)) = *(thisScalarMap.getMapMetadata(initialColumn)); } ++curCol; } } } } else { if (doLoop) { for (int64_t j = 0; j < thisDims[0]; ++j) { if (isLabel) { const CiftiLabelsMap& thisLabelMap = thisXML.getLabelsMap(CiftiXML::ALONG_ROW); outLabelMap.setMapName(curCol, thisLabelMap.getMapName(j)); *(outLabelMap.getMapLabelTable(curCol)) = *(thisLabelMap.getMapLabelTable(j)); *(outLabelMap.getMapMetadata(curCol)) = *(thisLabelMap.getMapMetadata(j)); } else { const CiftiScalarsMap& thisScalarMap = thisXML.getScalarsMap(CiftiXML::ALONG_ROW); outScalarMap.setMapName(curCol, thisScalarMap.getMapName(j)); *(outScalarMap.getMapPalette(curCol)) = *(thisScalarMap.getMapPalette(j)); *(outScalarMap.getMapMetadata(curCol)) = *(thisScalarMap.getMapMetadata(j)); } ++curCol; } } } } CaretAssert(!doLoop || curCol == numOutColumns); CiftiXML outXML; outXML.setNumberOfDimensions(2); outXML.setMap(CiftiXML::ALONG_COLUMN, baseColMapping); switch (baseRowMapping.getType()) { case CiftiMappingType::LABELS: outXML.setMap(CiftiXML::ALONG_ROW, outLabelMap); break; case CiftiMappingType::SCALARS: outXML.setMap(CiftiXML::ALONG_ROW, outScalarMap); break; case CiftiMappingType::SERIES: outXML.setMap(CiftiXML::ALONG_ROW, outSeriesMap); break; default: CaretAssert(false); } ciftiOut->setCiftiXML(outXML); int64_t numRows = baseColMapping.getLength(); vector outRow(numOutColumns), scratchRow(scratchRowLength); for (int64_t row = 0; row < numRows; ++row) { curCol = 0; for (int i = 0; i < numInputs; ++i) { const CiftiFile* ciftiIn = myInputs[i]->getCifti(1); vector thisDims = ciftiIn->getDimensions(); const CiftiXML& thisXML = ciftiIn->getCiftiXML(); const vector& columnOpts = *(myInputs[i]->getRepeatableParameterInstances(2)); int numColumnOpts = (int)columnOpts.size(); if (numColumnOpts > 0) { ciftiIn->getRow(scratchRow.data(), row); for (int j = 0; j < numColumnOpts; ++j) { int64_t initialColumn = thisXML.getMap(CiftiXML::ALONG_ROW)->getIndexFromNumberOrName(columnOpts[j]->getString(1));//this function has the 1-indexing convention built in OptionalParameter* upToOpt = columnOpts[j]->getOptionalParameter(2);//we already checked that these strings give a valid column if (upToOpt->m_present) { int finalColumn = thisXML.getMap(CiftiXML::ALONG_ROW)->getIndexFromNumberOrName(upToOpt->getString(1));//ditto bool reverse = upToOpt->getOptionalParameter(2)->m_present; if (reverse) { for (int c = finalColumn; c >= initialColumn; --c) { outRow[curCol] = scratchRow[c]; ++curCol; } } else { for (int c = initialColumn; c <= finalColumn; ++c) { outRow[curCol] = scratchRow[c]; ++curCol; } } } else { outRow[curCol] = scratchRow[initialColumn]; ++curCol; } } } else { ciftiIn->getRow(outRow.data() + curCol, row); curCol += thisDims[0]; } } CaretAssert(curCol == numOutColumns); ciftiOut->setRow(outRow.data(), row); } } connectome-workbench-1.2.3+git41-gc4c6c90/src/Operations/OperationCiftiMerge.h000066400000000000000000000025751300200146000267500ustar00rootroot00000000000000#ifndef __OPERATION_CIFTI_MERGE_H__ #define __OPERATION_CIFTI_MERGE_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 "AbstractOperation.h" namespace caret { class OperationCiftiMerge : public AbstractOperation { public: static OperationParameters* getParameters(); static void useParameters(OperationParameters* myParams, ProgressObject* myProgObj); static AString getCommandSwitch(); static AString getShortDescription(); }; typedef TemplateAutoOperation AutoOperationCiftiMerge; } #endif //__OPERATION_CIFTI_MERGE_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Operations/OperationCiftiPalette.cxx000066400000000000000000000274311300200146000276600ustar00rootroot00000000000000/*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 "OperationCiftiPalette.h" #include "CiftiFile.h" #include "OperationException.h" #include "Palette.h" #include "PaletteColorMapping.h" #include "PaletteFile.h" #include using namespace caret; using namespace std; AString OperationCiftiPalette::getCommandSwitch() { return "-cifti-palette"; } AString OperationCiftiPalette::getShortDescription() { return "SET PALETTE ON A CIFTI FILE"; } OperationParameters* OperationCiftiPalette::getParameters() { OperationParameters* ret = new OperationParameters(); ret->addCiftiParameter(1, "cifti-in", "the cifti input"); ret->addStringParameter(2, "mode", "the mapping mode"); ret->addCiftiOutputParameter(3, "cifti-out", "the output cifti file"); OptionalParameter* columnSelect = ret->createOptionalParameter(4, "-column", "select a single column for scalar maps"); columnSelect->addStringParameter(1, "column", "the column number or name"); OptionalParameter* posMinMaxPercent = ret->createOptionalParameter(5, "-pos-percent", "percentage min/max for positive data coloring"); posMinMaxPercent->addDoubleParameter(1, "pos-min-%", "the percentile for the least positive data"); posMinMaxPercent->addDoubleParameter(2, "pos-max-%", "the percentile for the most positive data"); OptionalParameter* negMinMaxPercent = ret->createOptionalParameter(6, "-neg-percent", "percentage min/max for negative data coloring"); negMinMaxPercent->addDoubleParameter(1, "neg-min-%", "the percentile for the least negative data"); negMinMaxPercent->addDoubleParameter(2, "neg-max-%", "the percentile for the most negative data"); OptionalParameter* posMinMaxValue = ret->createOptionalParameter(7, "-pos-user", "user min/max values for positive data coloring"); posMinMaxValue->addDoubleParameter(1, "pos-min-user", "the value for the least positive data"); posMinMaxValue->addDoubleParameter(2, "pos-max-user", "the value for the most positive data"); OptionalParameter* negMinMaxValue = ret->createOptionalParameter(8, "-neg-user", "user min/max values for negative data coloring"); negMinMaxValue->addDoubleParameter(1, "neg-min-user", "the value for the least negative data"); negMinMaxValue->addDoubleParameter(2, "neg-max-user", "the value for the most negative data"); OptionalParameter* interpolate = ret->createOptionalParameter(9, "-interpolate", "interpolate colors"); interpolate->addBooleanParameter(1, "interpolate", "boolean, whether to interpolate"); OptionalParameter* displayPositive = ret->createOptionalParameter(10, "-disp-pos", "display positive data"); displayPositive->addBooleanParameter(1, "display", "boolean, whether to display"); OptionalParameter* displayNegative = ret->createOptionalParameter(11, "-disp-neg", "display positive data"); displayNegative->addBooleanParameter(1, "display", "boolean, whether to display"); OptionalParameter* displayZero = ret->createOptionalParameter(12, "-disp-zero", "display data closer to zero than the min cutoff"); displayZero->addBooleanParameter(1, "display", "boolean, whether to display"); OptionalParameter* paletteName = ret->createOptionalParameter(13, "-palette-name", "set the palette used"); paletteName->addStringParameter(1, "name", "the name of the palette"); OptionalParameter* thresholdOpt = ret->createOptionalParameter(14, "-thresholding", "set the thresholding"); thresholdOpt->addStringParameter(1, "type", "thresholding setting"); thresholdOpt->addStringParameter(2, "test", "show values inside or outside thresholds"); thresholdOpt->addDoubleParameter(3, "min", "lower threshold"); thresholdOpt->addDoubleParameter(4, "max", "upper threshold"); 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. " + "The argument must be one of the following:\n\n"; vector myEnums; PaletteScaleModeEnum::getAllEnums(myEnums); for (int i = 0; i < (int)myEnums.size(); ++i) { myText += PaletteScaleModeEnum::toName(myEnums[i]) + "\n"; } myText += "\nThe argument to -palette-name must be one of the following:\n\n"; PaletteFile myPF; int32_t numPalettes = myPF.getNumberOfPalettes(); for (int i = 0; i < numPalettes; ++i) { myText += myPF.getPalette(i)->getName() + "\n"; } myText += "\nThe argument to -thresholding must be one of the following:\n\n"; vector myEnums2; PaletteThresholdTypeEnum::getAllEnums(myEnums2); for (int i = 0; i < (int)myEnums2.size(); ++i) { myText += PaletteThresholdTypeEnum::toName(myEnums2[i]) + "\n"; } myText += "\nThe argument to -thresholding must be one of the following:\n\n"; vector myEnums3; PaletteThresholdTestEnum::getAllEnums(myEnums3); for (int i = 0; i < (int)myEnums3.size(); ++i) { myText += PaletteThresholdTestEnum::toName(myEnums3[i]) + "\n"; } ret->setHelpText(myText); return ret; } void OperationCiftiPalette::useParameters(OperationParameters* myParams, ProgressObject* myProgObj) { LevelProgress myProgress(myProgObj); CiftiFile* ciftiIn = myParams->getCifti(1); CiftiXMLOld myOutXML = ciftiIn->getCiftiXMLOld(); int64_t numRows = myOutXML.getNumberOfRows(), numCols = myOutXML.getNumberOfColumns(); if (numRows < 1 || numCols < 1) { throw OperationException("cifti file has invalid dimensions"); } PaletteColorMapping myMapping; if (myOutXML.getRowMappingType() == CIFTI_INDEX_TYPE_SCALARS) { myMapping = *(myOutXML.getMapPalette(CiftiXMLOld::ALONG_ROW, 0)); } else { myMapping = *(myOutXML.getFilePalette()); } AString myModeName = myParams->getString(2); bool ok = false; PaletteScaleModeEnum::Enum myMode = PaletteScaleModeEnum::fromName(myModeName, &ok); if (!ok) { throw OperationException("unknown mapping mode"); } CiftiFile* ciftiOut = myParams->getOutputCifti(3); int myColumn = -1; OptionalParameter* columnOpt = myParams->getOptionalParameter(4); if (columnOpt->m_present) { if (myOutXML.getRowMappingType() != CIFTI_INDEX_TYPE_SCALARS) { throw OperationException("-column option can only be used on scalar maps"); } AString columnIdentifier = columnOpt->getString(1); bool ok = false; myColumn = columnIdentifier.toInt(&ok) - 1; if (ok) { if (myColumn < 0 || myColumn >= numCols) { throw OperationException("invalid column specified"); } } else { int i = 0; for (; i < numCols; ++i) { if (myOutXML.getMapNameForRowIndex(i) == columnIdentifier)//index along a row - we need to fix these function names { myColumn = i; break; } } if (i >= numCols) { throw OperationException("invalid column specified"); } } } myMapping.setScaleMode(myMode); OptionalParameter* posMinMaxPercent = myParams->getOptionalParameter(5); if (posMinMaxPercent->m_present) { myMapping.setAutoScalePercentagePositiveMinimum(posMinMaxPercent->getDouble(1)); myMapping.setAutoScalePercentagePositiveMaximum(posMinMaxPercent->getDouble(2)); } OptionalParameter* negMinMaxPercent = myParams->getOptionalParameter(6); if (negMinMaxPercent->m_present) { myMapping.setAutoScalePercentageNegativeMinimum(negMinMaxPercent->getDouble(1)); myMapping.setAutoScalePercentageNegativeMaximum(negMinMaxPercent->getDouble(2)); } OptionalParameter* posMinMaxValue = myParams->getOptionalParameter(7); if (posMinMaxValue->m_present) { myMapping.setUserScalePositiveMinimum(posMinMaxValue->getDouble(1)); myMapping.setUserScalePositiveMaximum(posMinMaxValue->getDouble(2)); } OptionalParameter* negMinMaxValue = myParams->getOptionalParameter(8); if (negMinMaxValue->m_present) { myMapping.setUserScaleNegativeMinimum(negMinMaxValue->getDouble(1)); myMapping.setUserScaleNegativeMaximum(negMinMaxValue->getDouble(2)); } OptionalParameter* interpolate = myParams->getOptionalParameter(9); if (interpolate->m_present) { myMapping.setInterpolatePaletteFlag(interpolate->getBoolean(1)); } OptionalParameter* displayPositive = myParams->getOptionalParameter(10); if (displayPositive->m_present) { myMapping.setDisplayPositiveDataFlag(displayPositive->getBoolean(1)); } OptionalParameter* displayNegative = myParams->getOptionalParameter(11); if (displayNegative->m_present) { myMapping.setDisplayNegativeDataFlag(displayNegative->getBoolean(1)); } OptionalParameter* displayZero = myParams->getOptionalParameter(12); if (displayZero->m_present) { myMapping.setDisplayZeroDataFlag(displayZero->getBoolean(1)); } OptionalParameter* paletteName = myParams->getOptionalParameter(13); if (paletteName->m_present) { myMapping.setSelectedPaletteName(paletteName->getString(1)); } OptionalParameter* thresholdOpt = myParams->getOptionalParameter(14); if (thresholdOpt->m_present) { bool ok = false; PaletteThresholdTypeEnum::Enum mytype = PaletteThresholdTypeEnum::fromName(thresholdOpt->getString(1), &ok); if (!ok) throw OperationException("unrecognized threshold type string: " + thresholdOpt->getString(1)); PaletteThresholdTestEnum::Enum mytest = PaletteThresholdTestEnum::fromName(thresholdOpt->getString(2), &ok); if (!ok) throw OperationException("unrecognized threshold test string: " + thresholdOpt->getString(2)); myMapping.setThresholdType(mytype); myMapping.setThresholdTest(mytest); myMapping.setThresholdMinimum(mytype, thresholdOpt->getDouble(3)); myMapping.setThresholdMaximum(mytype, thresholdOpt->getDouble(4)); } if (myOutXML.getRowMappingType() == CIFTI_INDEX_TYPE_SCALARS) { if (myColumn == -1) { for (int i = 0; i < numCols; ++i) { *(myOutXML.getMapPalette(CiftiXMLOld::ALONG_ROW, i)) = myMapping; } } else { *(myOutXML.getMapPalette(CiftiXMLOld::ALONG_ROW, myColumn)) = myMapping; } } else { *(myOutXML.getFilePalette()) = myMapping; } ciftiOut->setCiftiXML(myOutXML); vector scratchRow(numCols); for (int64_t i = 0; i < numRows; ++i) { ciftiIn->getRow(scratchRow.data(), i); ciftiOut->setRow(scratchRow.data(), i); } } connectome-workbench-1.2.3+git41-gc4c6c90/src/Operations/OperationCiftiPalette.h000066400000000000000000000026111300200146000272760ustar00rootroot00000000000000#ifndef __OPERATION_CIFTI_PALETTE_H__ #define __OPERATION_CIFTI_PALETTE_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 "AbstractOperation.h" namespace caret { class OperationCiftiPalette : public AbstractOperation { public: static OperationParameters* getParameters(); static void useParameters(OperationParameters* myParams, ProgressObject* myProgObj); static AString getCommandSwitch(); static AString getShortDescription(); }; typedef TemplateAutoOperation AutoOperationCiftiPalette; } #endif //__OPERATION_CIFTI_PALETTE_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Operations/OperationCiftiROIAverage.cxx000066400000000000000000000214701300200146000302030ustar00rootroot00000000000000/*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 "OperationCiftiROIAverage.h" #include "AlgorithmCiftiSeparate.h" #include "CiftiFile.h" #include "MetricFile.h" #include "OperationException.h" #include "VolumeFile.h" #include using namespace caret; using namespace std; AString OperationCiftiROIAverage::getCommandSwitch() { return "-cifti-roi-average"; } AString OperationCiftiROIAverage::getShortDescription() { return "AVERAGE ROWS IN A SINGLE CIFTI FILE"; } OperationParameters* OperationCiftiROIAverage::getParameters() { OperationParameters* ret = new OperationParameters(); ret->addCiftiParameter(1, "cifti-in", "the cifti file to average rows from"); ret->addStringParameter(2, "text-out", "output text file of the average values"); OptionalParameter* ciftiRoiOpt = ret->createOptionalParameter(3, "-cifti-roi", "cifti file containing combined rois"); ciftiRoiOpt->addCiftiParameter(1, "roi-cifti", "the rois as a cifti file"); OptionalParameter* leftRoiOpt = ret->createOptionalParameter(4, "-left-roi", "vertices to use from left hemisphere"); leftRoiOpt->addMetricParameter(1, "roi-metric", "the left roi as a metric file"); OptionalParameter* rightRoiOpt = ret->createOptionalParameter(5, "-right-roi", "vertices to use from right hemisphere"); rightRoiOpt->addMetricParameter(1, "roi-metric", "the right roi as a metric file"); OptionalParameter* cerebRoiOpt = ret->createOptionalParameter(6, "-cerebellum-roi", "vertices to use from cerebellum"); cerebRoiOpt->addMetricParameter(1, "roi-metric", "the cerebellum roi as a metric file"); OptionalParameter* volRoiOpt = ret->createOptionalParameter(7, "-vol-roi", "voxels to use"); volRoiOpt->addVolumeParameter(1, "roi-vol", "the roi volume file"); ret->setHelpText( AString("Average the rows that are within the specified ROIs, and write the resulting average row to a text file, separated by newlines. ") + "If -cifti-roi is specified, -left-roi, -right-roi, -cerebellum-roi, and -vol-roi must not be specified." ); return ret; } void OperationCiftiROIAverage::useParameters(OperationParameters* myParams, ProgressObject* myProgObj) { LevelProgress myProgress(myProgObj); CiftiFile* myCifti = myParams->getCifti(1); if (myCifti->getCiftiXML().getMappingType(CiftiXML::ALONG_COLUMN) != CiftiMappingType::BRAIN_MODELS) { throw OperationException("input cifti file does not have a brain models mapping along column"); } AString textFileName = myParams->getString(2); fstream textFile(textFileName.toLocal8Bit().constData(), fstream::out | fstream::trunc); if (!textFile.good()) { throw OperationException("error opening output file for writing"); } int numCols = myCifti->getNumberOfColumns(); vector accum(numCols, 0.0); int accumCount = 0; CiftiFile* ciftiROI = NULL; OptionalParameter* ciftiRoiOpt = myParams->getOptionalParameter(3); if (ciftiRoiOpt->m_present) { ciftiROI = ciftiRoiOpt->getCifti(1); } OptionalParameter* leftRoiOpt = myParams->getOptionalParameter(4); if (leftRoiOpt->m_present) { if (ciftiROI != NULL) throw OperationException("-cifti-roi cannot be used with any other ROI option"); MetricFile* tempMetric = leftRoiOpt->getMetric(1); processSurfaceComponent(myCifti, StructureEnum::CORTEX_LEFT, tempMetric, accum, accumCount); } OptionalParameter* rightRoiOpt = myParams->getOptionalParameter(5); if (rightRoiOpt->m_present) { if (ciftiROI != NULL) throw OperationException("-cifti-roi cannot be used with any other ROI option"); MetricFile* tempMetric = rightRoiOpt->getMetric(1); processSurfaceComponent(myCifti, StructureEnum::CORTEX_RIGHT, tempMetric, accum, accumCount); } OptionalParameter* cerebRoiOpt = myParams->getOptionalParameter(6); if (cerebRoiOpt->m_present) { if (ciftiROI != NULL) throw OperationException("-cifti-roi cannot be used with any other ROI option"); MetricFile* tempMetric = cerebRoiOpt->getMetric(1); processSurfaceComponent(myCifti, StructureEnum::CEREBELLUM, tempMetric, accum, accumCount); } OptionalParameter* volRoiOpt = myParams->getOptionalParameter(7); if (volRoiOpt->m_present) { if (ciftiROI != NULL) throw OperationException("-cifti-roi cannot be used with any other ROI option"); VolumeFile* tempVol = volRoiOpt->getVolume(1); processVolume(myCifti, tempVol, accum, accumCount); } if (ciftiROI != NULL) { const CiftiXML& roiXML = ciftiROI->getCiftiXML(); const CiftiXML& inputXML = myCifti->getCiftiXML(); if (!(roiXML.getMap(CiftiXML::ALONG_COLUMN)->approximateMatch(*inputXML.getMap(CiftiXML::ALONG_COLUMN)))) { throw OperationException("dense mappings of input and roi cifti files don't match"); } const CiftiBrainModelsMap& roiDense = roiXML.getBrainModelsMap(CiftiXML::ALONG_COLUMN); if (roiDense.hasVolumeData()) { VolumeFile roiVol; int64_t offset[3]; AlgorithmCiftiSeparate(NULL, ciftiROI, CiftiXML::ALONG_COLUMN, &roiVol, offset, NULL, false);//don't crop because there should be only one map, and processVolume doesn't currently accept cropped processVolume(myCifti, &roiVol, accum, accumCount); } vector surfStructs = roiDense.getSurfaceStructureList(); for (int i = 0; i < (int)surfStructs.size(); ++i) { MetricFile roiMetric; AlgorithmCiftiSeparate(NULL, ciftiROI, CiftiXML::ALONG_COLUMN, surfStructs[i], &roiMetric); processSurfaceComponent(myCifti, surfStructs[i], &roiMetric, accum, accumCount); } } if (accumCount == 0) { throw OperationException("ROI(s) don't match any data"); } for (int i = 0; i < numCols; ++i) { textFile << accum[i] / accumCount << endl; } } void OperationCiftiROIAverage::processSurfaceComponent(const CiftiFile* myCifti, const StructureEnum::Enum& myStruct, const MetricFile* myRoi, vector& accum, int& accumCount) { int numCols = myCifti->getNumberOfColumns(); int numNodes = myRoi->getNumberOfNodes(); const CiftiBrainModelsMap& myDenseMap = myCifti->getCiftiXML().getBrainModelsMap(CiftiXML::ALONG_COLUMN); if (myDenseMap.getSurfaceNumberOfNodes(myStruct) != numNodes) { throw OperationException("roi number of vertices doesn't match for structure " + StructureEnum::toName(myStruct)); } vector scratch(numCols); vector myMap = myDenseMap.getSurfaceMap(myStruct); int mapSize = myMap.size(); for (int i = 0; i < mapSize; ++i) { if (myRoi->getValue(myMap[i].m_surfaceNode, 0) > 0.0f) { ++accumCount; myCifti->getRow(scratch.data(), myMap[i].m_ciftiIndex); for (int j = 0; j < numCols; ++j) { accum[j] += scratch[j]; } } } } void OperationCiftiROIAverage::processVolume(const CiftiFile* myCifti, const VolumeFile* myRoi, vector& accum, int& accumCount) { int numCols = myCifti->getNumberOfColumns(); const CiftiXMLOld& myXml = myCifti->getCiftiXMLOld(); int64_t dims[3]; vector > sform; if (!myXml.getVolumeDimsAndSForm(dims, sform)) { throw OperationException("no volume data in cifti file"); } if (!myRoi->matchesVolumeSpace(dims, sform)) { throw OperationException("volume roi doesn't match cifti volume space"); } vector scratch(numCols); vector myMap; myXml.getVolumeMapForColumns(myMap); int mapSize = myMap.size(); for (int i = 0; i < mapSize; ++i) { if (myRoi->getValue(myMap[i].m_ijk) > 0.0f) { ++accumCount; myCifti->getRow(scratch.data(), myMap[i].m_ciftiIndex); for (int j = 0; j < numCols; ++j) { accum[j] += scratch[j]; } } } } connectome-workbench-1.2.3+git41-gc4c6c90/src/Operations/OperationCiftiROIAverage.h000066400000000000000000000035061300200146000276300ustar00rootroot00000000000000#ifndef __OPERATION_CIFTI_ROI_AVERAGE_H__ #define __OPERATION_CIFTI_ROI_AVERAGE_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 "AbstractOperation.h" #include "StructureEnum.h" #include namespace caret { class CiftiFile; class MetricFile; class VolumeFile; class OperationCiftiROIAverage : public AbstractOperation { static void processSurfaceComponent(const CiftiFile* myCifti, const StructureEnum::Enum& myStruct, const MetricFile* myRoi, std::vector& accum, int& accumCount); static void processVolume(const CiftiFile* myCifti, const VolumeFile* myRoi, std::vector& accum, int& accumCount); public: static OperationParameters* getParameters(); static void useParameters(OperationParameters* myParams, ProgressObject* myProgObj); static AString getCommandSwitch(); static AString getShortDescription(); }; typedef TemplateAutoOperation AutoOperationCiftiROIAverage; } #endif //__OPERATION_CIFTI_ROI_AVERAGE_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Operations/OperationCiftiResampleDconnMemory.cxx000066400000000000000000000613361300200146000322070ustar00rootroot00000000000000/*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 "OperationCiftiResampleDconnMemory.h" #include "OperationException.h" #include "AffineFile.h" #include "AlgorithmCiftiResample.h" #include "CiftiFile.h" #include "MetricFile.h" #include "SurfaceFile.h" #include "WarpfieldFile.h" using namespace caret; using namespace std; AString OperationCiftiResampleDconnMemory::getCommandSwitch() { return "-cifti-resample-dconn-memory"; } AString OperationCiftiResampleDconnMemory::getShortDescription() { return "USE LOTS OF MEMORY TO RESAMPLE DCONN"; } OperationParameters* OperationCiftiResampleDconnMemory::getParameters() { OperationParameters* ret = new OperationParameters(); ret->addCiftiParameter(1, "cifti-in", "the cifti file to resample"); ret->addCiftiParameter(3, "cifti-template", "a cifti file containing the cifti space to resample to"); ret->addStringParameter(4, "template-direction", "the direction of the template to use as the resampling space, ROW or COLUMN"); ret->addStringParameter(5, "surface-method", "specify a surface resampling method"); ret->addStringParameter(6, "volume-method", "specify a volume interpolation method"); ret->addCiftiOutputParameter(7, "cifti-out", "the output cifti file"); ret->createOptionalParameter(8, "-surface-largest", "use largest weight instead of weighted average when doing surface resampling"); OptionalParameter* volDilateOpt = ret->createOptionalParameter(9, "-volume-predilate", "dilate the volume components before resampling"); volDilateOpt->addDoubleParameter(1, "dilate-mm", "distance, in mm, to dilate"); volDilateOpt->createOptionalParameter(2, "-nearest", "use nearest value dilation"); OptionalParameter* volDilateWeightedOpt = volDilateOpt->createOptionalParameter(3, "-weighted", "use weighted dilation"); OptionalParameter* volDilateExpOpt = volDilateWeightedOpt->createOptionalParameter(1, "-exponent", "specify exponent in weighting function"); volDilateExpOpt->addDoubleParameter(1, "exponent", "exponent 'n' to use in (1 / (distance ^ n)) as the weighting function (default 2)"); OptionalParameter* surfDilateOpt = ret->createOptionalParameter(10, "-surface-postdilate", "dilate the surface components after resampling"); surfDilateOpt->addDoubleParameter(1, "dilate-mm", "distance, in mm, to dilate"); surfDilateOpt->createOptionalParameter(2, "-nearest", "use nearest value dilation"); surfDilateOpt->createOptionalParameter(3, "-linear", "use linear dilation"); OptionalParameter* surfDilateWeightedOpt = surfDilateOpt->createOptionalParameter(4, "-weighted", "use weighted dilation"); OptionalParameter* surfDilateExpOpt = surfDilateWeightedOpt->createOptionalParameter(1, "-exponent", "specify exponent in weighting function"); surfDilateExpOpt->addDoubleParameter(1, "exponent", "exponent 'n' to use in (area / (distance ^ n)) as the weighting function (default 2)"); OptionalParameter* affineOpt = ret->createOptionalParameter(11, "-affine", "use an affine transformation on the volume components"); affineOpt->addStringParameter(1, "affine-file", "the affine file to use"); OptionalParameter* flirtOpt = affineOpt->createOptionalParameter(2, "-flirt", "MUST be used if affine is a flirt affine"); flirtOpt->addStringParameter(1, "source-volume", "the source volume used when generating the affine"); flirtOpt->addStringParameter(2, "target-volume", "the target volume used when generating the affine"); OptionalParameter* warpfieldOpt = ret->createOptionalParameter(12, "-warpfield", "use a warpfield on the volume components"); warpfieldOpt->addStringParameter(1, "warpfield", "the warpfield to use"); OptionalParameter* fnirtOpt = warpfieldOpt->createOptionalParameter(2, "-fnirt", "MUST be used if using a fnirt warpfield"); fnirtOpt->addStringParameter(1, "source-volume", "the source volume used when generating the warpfield"); OptionalParameter* leftSpheresOpt = ret->createOptionalParameter(13, "-left-spheres", "specify spheres for left surface resampling"); leftSpheresOpt->addSurfaceParameter(1, "current-sphere", "a sphere with the same mesh as the current left surface"); leftSpheresOpt->addSurfaceParameter(2, "new-sphere", "a sphere with the new left mesh that is in register with the current sphere"); OptionalParameter* leftAreaSurfsOpt = leftSpheresOpt->createOptionalParameter(3, "-left-area-surfs", "specify left surfaces to do vertex area correction based on"); leftAreaSurfsOpt->addSurfaceParameter(1, "current-area", "a relevant left anatomical surface with current mesh"); leftAreaSurfsOpt->addSurfaceParameter(2, "new-area", "a relevant left anatomical surface with new mesh"); OptionalParameter* leftAreaMetricsOpt = leftSpheresOpt->createOptionalParameter(4, "-left-area-metrics", "specify left vertex area metrics to do area correction based on"); leftAreaMetricsOpt->addMetricParameter(1, "current-area", "a metric file with vertex areas for the current mesh"); leftAreaMetricsOpt->addMetricParameter(2, "new-area", "a metric file with vertex areas for the new mesh"); OptionalParameter* rightSpheresOpt = ret->createOptionalParameter(14, "-right-spheres", "specify spheres for right surface resampling"); rightSpheresOpt->addSurfaceParameter(1, "current-sphere", "a sphere with the same mesh as the current right surface"); rightSpheresOpt->addSurfaceParameter(2, "new-sphere", "a sphere with the new right mesh that is in register with the current sphere"); OptionalParameter* rightAreaSurfsOpt = rightSpheresOpt->createOptionalParameter(3, "-right-area-surfs", "specify right surfaces to do vertex area correction based on"); rightAreaSurfsOpt->addSurfaceParameter(1, "current-area", "a relevant right anatomical surface with current mesh"); rightAreaSurfsOpt->addSurfaceParameter(2, "new-area", "a relevant right anatomical surface with new mesh"); OptionalParameter* rightAreaMetricsOpt = rightSpheresOpt->createOptionalParameter(4, "-right-area-metrics", "specify right vertex area metrics to do area correction based on"); rightAreaMetricsOpt->addMetricParameter(1, "current-area", "a metric file with vertex areas for the current mesh"); rightAreaMetricsOpt->addMetricParameter(2, "new-area", "a metric file with vertex areas for the new mesh"); OptionalParameter* cerebSpheresOpt = ret->createOptionalParameter(15, "-cerebellum-spheres", "specify spheres for cerebellum surface resampling"); cerebSpheresOpt->addSurfaceParameter(1, "current-sphere", "a sphere with the same mesh as the current cerebellum surface"); cerebSpheresOpt->addSurfaceParameter(2, "new-sphere", "a sphere with the new cerebellum mesh that is in register with the current sphere"); OptionalParameter* cerebAreaSurfsOpt = cerebSpheresOpt->createOptionalParameter(3, "-cerebellum-area-surfs", "specify cerebellum surfaces to do vertex area correction based on"); cerebAreaSurfsOpt->addSurfaceParameter(1, "current-area", "a relevant cerebellum anatomical surface with current mesh"); cerebAreaSurfsOpt->addSurfaceParameter(2, "new-area", "a relevant cerebellum anatomical surface with new mesh"); OptionalParameter* cerebAreaMetricsOpt = cerebSpheresOpt->createOptionalParameter(4, "-cerebellum-area-metrics", "specify cerebellum vertex area metrics to do area correction based on"); cerebAreaMetricsOpt->addMetricParameter(1, "current-area", "a metric file with vertex areas for the current mesh"); cerebAreaMetricsOpt->addMetricParameter(2, "new-area", "a metric file with vertex areas for the new mesh"); AString myHelpText = AString("This command does the same thing as running -cifti-resample twice, but uses memory up to approximately 2x the size that the intermediate file would be. ") + "This is because the intermediate dconn is kept in memory, rather than written to disk, " + "and the components before and after resampling/dilation have to be in memory at the same time during the relevant computation. " + "The argument should usually be COLUMN, as dtseries, dscalar, and dlabel all have brainordinates on that direction. " + "If spheres are not specified for a surface structure which exists in the cifti files, its data is copied without resampling or dilation. " + "Dilation is done with the 'nearest' method, and is done on for surface data. " + "Volume components are padded before dilation so that dilation doesn't run into the edge of the component bounding box.\n\n" + "The argument must be one of the following:\n\n" + "CUBIC\nENCLOSING_VOXEL\nTRILINEAR\n\n" + "The argument must be one of the following:\n\n"; vector allEnums; SurfaceResamplingMethodEnum::getAllEnums(allEnums); for (int i = 0; i < (int)allEnums.size(); ++i) { myHelpText += SurfaceResamplingMethodEnum::toName(allEnums[i]) + "\n"; } ret->setHelpText(myHelpText); return ret; } void OperationCiftiResampleDconnMemory::useParameters(OperationParameters* myParams, ProgressObject* myProgObj) { LevelProgress myProgress(myProgObj); CiftiFile* myCiftiIn = myParams->getCifti(1); CiftiFile* myTemplate = myParams->getCifti(3); AString myTemplDirString = myParams->getString(4); int templateDir = -1; if (myTemplDirString == "ROW") { templateDir = CiftiXML::ALONG_ROW; } else { if (myTemplDirString == "COLUMN") { templateDir = CiftiXML::ALONG_COLUMN; } else { throw OperationException("unrecognized template direction string, use ROW or COLUMN"); } } bool ok = false; SurfaceResamplingMethodEnum::Enum mySurfMethod = SurfaceResamplingMethodEnum::fromName(myParams->getString(5), &ok); if (!ok) { throw OperationException("invalid surface resampling method name"); } AString myVolMethodString = myParams->getString(6); VolumeFile::InterpType myVolMethod = VolumeFile::CUBIC; if (myVolMethodString == "CUBIC") { myVolMethod = VolumeFile::CUBIC; } else if (myVolMethodString == "TRILINEAR") { myVolMethod = VolumeFile::TRILINEAR; } else if (myVolMethodString == "ENCLOSING_VOXEL") { myVolMethod = VolumeFile::ENCLOSING_VOXEL; } else { throw OperationException("unrecognized volume interpolation method"); } CiftiFile* myCiftiOut = myParams->getOutputCifti(7); bool surfLargest = myParams->getOptionalParameter(8)->m_present; float voldilatemm = -1.0f, surfdilatemm = -1.0f; bool isLabelData = false;//dilation method arguments checking needs to know if it is label data const CiftiXML& inputXML = myCiftiIn->getCiftiXML(); for (int i = 0; i < inputXML.getNumberOfDimensions(); ++i) { if (inputXML.getMappingType(i) == CiftiMappingType::LABELS) { isLabelData = true; break; } } AlgorithmVolumeDilate::Method volDilateMethod = AlgorithmVolumeDilate::WEIGHTED; float volDilateExponent = 2.0f; AlgorithmMetricDilate::Method surfDilateMethod = AlgorithmMetricDilate::WEIGHTED;//label dilate doesn't support multiple methods - what to do there, share the enum between them? float surfDilateExponent = 2.0f;//label dilate currently only supports nearest, so in order to accept a default in the algorithm, it currently ignores this on label data, no warning OptionalParameter* volDilateOpt = myParams->getOptionalParameter(9); if (volDilateOpt->m_present) { bool methodSpecified = false; voldilatemm = (float)volDilateOpt->getDouble(1); if (voldilatemm <= 0.0f) throw OperationException("dilation amount must be positive"); if (volDilateOpt->getOptionalParameter(2)->m_present) { methodSpecified = true; volDilateMethod = AlgorithmVolumeDilate::NEAREST; } OptionalParameter* volDilateWeightedOpt = volDilateOpt->getOptionalParameter(3); if (volDilateWeightedOpt->m_present) { if (methodSpecified) throw OperationException("cannot specify multiple volume dilation methods"); methodSpecified = true; volDilateMethod = AlgorithmVolumeDilate::WEIGHTED; OptionalParameter* volDilateExpOpt = volDilateWeightedOpt->getOptionalParameter(1); if (volDilateExpOpt->m_present) { volDilateExponent = (float)volDilateExpOpt->getDouble(1); } } } OptionalParameter* surfDilateOpt = myParams->getOptionalParameter(10); if (surfDilateOpt->m_present) { bool methodSpecified = false; surfdilatemm = (float)surfDilateOpt->getDouble(1); if (surfdilatemm <= 0.0f) throw OperationException("dilation amount must be positive"); if (surfDilateOpt->getOptionalParameter(2)->m_present) { methodSpecified = true; surfDilateMethod = AlgorithmMetricDilate::NEAREST; } if (surfDilateOpt->getOptionalParameter(3)->m_present) { if (methodSpecified) throw OperationException("cannot specify multiple surface dilation methods"); methodSpecified = true; if (isLabelData) throw OperationException("cannot do linear surface dilation on label data"); surfDilateMethod = AlgorithmMetricDilate::LINEAR; } OptionalParameter* surfDilateWeightedOpt = surfDilateOpt->getOptionalParameter(4); if (surfDilateWeightedOpt->m_present) { if (methodSpecified) throw OperationException("cannot specify multiple surface dilation methods"); methodSpecified = true; if (isLabelData) throw OperationException("cannot do weighted surface dilation on label data"); surfDilateMethod = AlgorithmMetricDilate::WEIGHTED; OptionalParameter* surfDilateExpOpt = surfDilateWeightedOpt->getOptionalParameter(1); if (surfDilateExpOpt->m_present) { surfDilateExponent = (float)surfDilateExpOpt->getDouble(1); } } } OptionalParameter* affineOpt = myParams->getOptionalParameter(11); OptionalParameter* warpfieldOpt = myParams->getOptionalParameter(12); if (affineOpt->m_present && warpfieldOpt->m_present) throw OperationException("you cannot specify both -affine and -warpfield"); AffineFile myAffine; WarpfieldFile myWarpfield; if (affineOpt->m_present) { OptionalParameter* flirtOpt = affineOpt->getOptionalParameter(2); if (flirtOpt->m_present) { myAffine.readFlirt(affineOpt->getString(1), flirtOpt->getString(1), flirtOpt->getString(2)); } else { myAffine.readWorld(affineOpt->getString(1)); } } if (warpfieldOpt->m_present) { OptionalParameter* fnirtOpt = warpfieldOpt->getOptionalParameter(2); if (fnirtOpt->m_present) { myWarpfield.readFnirt(warpfieldOpt->getString(1), fnirtOpt->getString(1)); } else { myWarpfield.readWorld(warpfieldOpt->getString(1)); } } SurfaceFile* curLeftSphere = NULL, *newLeftSphere = NULL; MetricFile* curLeftAreas = NULL, *newLeftAreas = NULL; MetricFile curLeftAreasTemp, newLeftAreasTemp; OptionalParameter* leftSpheresOpt = myParams->getOptionalParameter(13); if (leftSpheresOpt->m_present) { curLeftSphere = leftSpheresOpt->getSurface(1); newLeftSphere = leftSpheresOpt->getSurface(2); OptionalParameter* leftAreaSurfsOpt = leftSpheresOpt->getOptionalParameter(3); if (leftAreaSurfsOpt->m_present) { SurfaceFile* curAreaSurf = leftAreaSurfsOpt->getSurface(1); SurfaceFile* newAreaSurf = leftAreaSurfsOpt->getSurface(2); vector nodeAreasTemp; curAreaSurf->computeNodeAreas(nodeAreasTemp); curLeftAreasTemp.setNumberOfNodesAndColumns(curAreaSurf->getNumberOfNodes(), 1); curLeftAreasTemp.setValuesForColumn(0, nodeAreasTemp.data()); curLeftAreas = &curLeftAreasTemp; newAreaSurf->computeNodeAreas(nodeAreasTemp); newLeftAreasTemp.setNumberOfNodesAndColumns(newAreaSurf->getNumberOfNodes(), 1); newLeftAreasTemp.setValuesForColumn(0, nodeAreasTemp.data()); newLeftAreas = &newLeftAreasTemp; } OptionalParameter* leftAreaMetricsOpt = leftSpheresOpt->getOptionalParameter(4); if (leftAreaMetricsOpt->m_present) { if (leftAreaSurfsOpt->m_present) { throw OperationException("only one of -left-area-surfs and -left-area-metrics can be specified"); } curLeftAreas = leftAreaMetricsOpt->getMetric(1); newLeftAreas = leftAreaMetricsOpt->getMetric(2); } } SurfaceFile* curRightSphere = NULL, *newRightSphere = NULL; MetricFile* curRightAreas = NULL, *newRightAreas = NULL; MetricFile curRightAreasTemp, newRightAreasTemp; OptionalParameter* rightSpheresOpt = myParams->getOptionalParameter(14); if (rightSpheresOpt->m_present) { curRightSphere = rightSpheresOpt->getSurface(1); newRightSphere = rightSpheresOpt->getSurface(2); OptionalParameter* rightAreaSurfsOpt = rightSpheresOpt->getOptionalParameter(3); if (rightAreaSurfsOpt->m_present) { SurfaceFile* curAreaSurf = rightAreaSurfsOpt->getSurface(1); SurfaceFile* newAreaSurf = rightAreaSurfsOpt->getSurface(2); vector nodeAreasTemp; curAreaSurf->computeNodeAreas(nodeAreasTemp); curRightAreasTemp.setNumberOfNodesAndColumns(curAreaSurf->getNumberOfNodes(), 1); curRightAreasTemp.setValuesForColumn(0, nodeAreasTemp.data()); curRightAreas = &curRightAreasTemp; newAreaSurf->computeNodeAreas(nodeAreasTemp); newRightAreasTemp.setNumberOfNodesAndColumns(newAreaSurf->getNumberOfNodes(), 1); newRightAreasTemp.setValuesForColumn(0, nodeAreasTemp.data()); newRightAreas = &newRightAreasTemp; } OptionalParameter* rightAreaMetricsOpt = rightSpheresOpt->getOptionalParameter(4); if (rightAreaMetricsOpt->m_present) { if (rightAreaSurfsOpt->m_present) { throw OperationException("only one of -right-area-surfs and -right-area-metrics can be specified"); } curRightAreas = rightAreaMetricsOpt->getMetric(1); newRightAreas = rightAreaMetricsOpt->getMetric(2); } } SurfaceFile* curCerebSphere = NULL, *newCerebSphere = NULL; MetricFile* curCerebAreas = NULL, *newCerebAreas = NULL; MetricFile curCerebAreasTemp, newCerebAreasTemp; OptionalParameter* cerebSpheresOpt = myParams->getOptionalParameter(15); if (cerebSpheresOpt->m_present) { curCerebSphere = cerebSpheresOpt->getSurface(1); newCerebSphere = cerebSpheresOpt->getSurface(2); OptionalParameter* cerebAreaSurfsOpt = cerebSpheresOpt->getOptionalParameter(3); if (cerebAreaSurfsOpt->m_present) { SurfaceFile* curAreaSurf = cerebAreaSurfsOpt->getSurface(1); SurfaceFile* newAreaSurf = cerebAreaSurfsOpt->getSurface(2); vector nodeAreasTemp; curAreaSurf->computeNodeAreas(nodeAreasTemp); curCerebAreasTemp.setNumberOfNodesAndColumns(curAreaSurf->getNumberOfNodes(), 1); curCerebAreasTemp.setValuesForColumn(0, nodeAreasTemp.data()); curCerebAreas = &curCerebAreasTemp; newAreaSurf->computeNodeAreas(nodeAreasTemp); newCerebAreasTemp.setNumberOfNodesAndColumns(newAreaSurf->getNumberOfNodes(), 1); newCerebAreasTemp.setValuesForColumn(0, nodeAreasTemp.data()); newCerebAreas = &newCerebAreasTemp; } OptionalParameter* cerebAreaMetricsOpt = cerebSpheresOpt->getOptionalParameter(4); if (cerebAreaMetricsOpt->m_present) { if (cerebAreaSurfsOpt->m_present) { throw OperationException("only one of -cerebellum-area-surfs and -cerebellum-area-metrics can be specified"); } curCerebAreas = cerebAreaMetricsOpt->getMetric(1); newCerebAreas = cerebAreaMetricsOpt->getMetric(2); } } pair colErrors = AlgorithmCiftiResample::checkForErrors(myCiftiIn, CiftiXML::ALONG_COLUMN, myTemplate, templateDir, mySurfMethod, curLeftSphere, newLeftSphere, curLeftAreas, newLeftAreas, curRightSphere, newRightSphere, curRightAreas, newRightAreas, curCerebSphere, newCerebSphere, curCerebAreas, newCerebAreas); pair rowErrors = AlgorithmCiftiResample::checkForErrors(myCiftiIn, CiftiXML::ALONG_ROW, myTemplate, templateDir, mySurfMethod, curLeftSphere, newLeftSphere, curLeftAreas, newLeftAreas, curRightSphere, newRightSphere, curRightAreas, newRightAreas, curCerebSphere, newCerebSphere, curCerebAreas, newCerebAreas); ok = true; AString message; if (rowErrors.first) { message += "Error in resampling along row: " + rowErrors.second; ok = false; } if (colErrors.first) { if (!ok) message += "\n"; message = "Error in resampling along column: " + colErrors.second; ok = false; } if (!ok) { throw OperationException(message); } CiftiFile tempCifti; //TSC: resampling along column first causes it to hit peak memory usage earlier if (warpfieldOpt->m_present) { AlgorithmCiftiResample(myProgObj, myCiftiIn, CiftiXML::ALONG_COLUMN, myTemplate, templateDir, mySurfMethod, myVolMethod, &tempCifti, surfLargest, voldilatemm, surfdilatemm, myWarpfield.getWarpfield(), curLeftSphere, newLeftSphere, curLeftAreas, newLeftAreas, curRightSphere, newRightSphere, curRightAreas, newRightAreas, curCerebSphere, newCerebSphere, curCerebAreas, newCerebAreas, volDilateMethod, volDilateExponent, surfDilateMethod, surfDilateExponent); AlgorithmCiftiResample(myProgObj, &tempCifti, CiftiXML::ALONG_ROW, myTemplate, templateDir, mySurfMethod, myVolMethod, myCiftiOut, surfLargest, voldilatemm, surfdilatemm, myWarpfield.getWarpfield(), curLeftSphere, newLeftSphere, curLeftAreas, newLeftAreas, curRightSphere, newRightSphere, curRightAreas, newRightAreas, curCerebSphere, newCerebSphere, curCerebAreas, newCerebAreas, volDilateMethod, volDilateExponent, surfDilateMethod, surfDilateExponent); } else {//rely on AffineFile() being the identity transform for if neither option is specified AlgorithmCiftiResample(myProgObj, myCiftiIn, CiftiXML::ALONG_COLUMN, myTemplate, templateDir, mySurfMethod, myVolMethod, &tempCifti, surfLargest, voldilatemm, surfdilatemm, myAffine.getMatrix(), curLeftSphere, newLeftSphere, curLeftAreas, newLeftAreas, curRightSphere, newRightSphere, curRightAreas, newRightAreas, curCerebSphere, newCerebSphere, curCerebAreas, newCerebAreas, volDilateMethod, volDilateExponent, surfDilateMethod, surfDilateExponent); AlgorithmCiftiResample(myProgObj, &tempCifti, CiftiXML::ALONG_ROW, myTemplate, templateDir, mySurfMethod, myVolMethod, myCiftiOut, surfLargest, voldilatemm, surfdilatemm, myAffine.getMatrix(), curLeftSphere, newLeftSphere, curLeftAreas, newLeftAreas, curRightSphere, newRightSphere, curRightAreas, newRightAreas, curCerebSphere, newCerebSphere, curCerebAreas, newCerebAreas, volDilateMethod, volDilateExponent, surfDilateMethod, surfDilateExponent); } } connectome-workbench-1.2.3+git41-gc4c6c90/src/Operations/OperationCiftiResampleDconnMemory.h000066400000000000000000000027271300200146000316330ustar00rootroot00000000000000#ifndef __OPERATION_CIFTI_RESAMPLE_DCONN_MEMORY_H__ #define __OPERATION_CIFTI_RESAMPLE_DCONN_MEMORY_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 "AbstractOperation.h" namespace caret { class OperationCiftiResampleDconnMemory : public AbstractOperation { public: static OperationParameters* getParameters(); static void useParameters(OperationParameters* myParams, ProgressObject* myProgObj); static AString getCommandSwitch(); static AString getShortDescription(); }; typedef TemplateAutoOperation AutoOperationCiftiResampleDconnMemory; } #endif //__OPERATION_CIFTI_RESAMPLE_DCONN_MEMORY_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Operations/OperationCiftiSeparateAll.cxx000066400000000000000000000156741300200146000304650ustar00rootroot00000000000000/*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 "AlgorithmCiftiSeparate.h" #include "CiftiFile.h" #include "MetricFile.h" #include "OperationCiftiSeparateAll.h" #include "OperationException.h" #include "VolumeFile.h" using namespace caret; using namespace std; AString OperationCiftiSeparateAll::getCommandSwitch() { return "-cifti-separate-all"; } AString OperationCiftiSeparateAll::getShortDescription() { return "DEPRECATED: use -cifti-separate"; } OperationParameters* OperationCiftiSeparateAll::getParameters() { OperationParameters* ret = new OperationParameters(); ret->addCiftiParameter(1, "cifti-in", "the cifti to split"); OptionalParameter* leftOpt = ret->createOptionalParameter(2, "-left", "output the left surface data"); leftOpt->addMetricOutputParameter(1, "left-metric", "the output metric for the left surface"); OptionalParameter* leftRoiOpt = leftOpt->createOptionalParameter(2, "-left-roi", "output the ROI for the left surface data"); leftRoiOpt->addMetricOutputParameter(1, "left-roi-metric", "output metric for the left ROI"); OptionalParameter* rightOpt = ret->createOptionalParameter(3, "-right", "output the right surface data"); rightOpt->addMetricOutputParameter(1, "right-metric", "the output metric for the right surface"); OptionalParameter* rightRoiOpt = rightOpt->createOptionalParameter(2, "-right-roi", "output the ROI for the right surface data"); rightRoiOpt->addMetricOutputParameter(1, "right-roi-metric", "output metric for the right ROI"); OptionalParameter* cerebOpt = ret->createOptionalParameter(4, "-cerebellum", "output the cerebellum surface data"); cerebOpt->addMetricOutputParameter(1, "cerebellum-metric", "the output metric for the cerebellum surface"); OptionalParameter* cerebRoiOpt = cerebOpt->createOptionalParameter(2, "-cerebellum-roi", "output the ROI for the cerebellum surface data"); cerebRoiOpt->addMetricOutputParameter(1, "cerebellum-roi-metric", "output metric for the cerebellum ROI"); OptionalParameter* volOpt = ret->createOptionalParameter(5, "-volume", "output the voxel data"); volOpt->addVolumeOutputParameter(1, "volume-out", "output volume file"); OptionalParameter* volRoiOpt = volOpt->createOptionalParameter(2, "-volume-roi", "output the combined ROI for the volume data"); volRoiOpt->addVolumeOutputParameter(1, "volume-roi-out", "output volume for ROI"); OptionalParameter* dirOpt = ret->createOptionalParameter(6, "-direction", "choose the direction to separate (default COLUMN)"); dirOpt->addStringParameter(1, "direction", "which direction to separate into components, ROW or COLUMN"); ret->setHelpText( AString("DEPRECATED: this command may be removed in a future release, use -cifti-separate.\n\n") + "All volume components are put together into one volume, the boundaries between volume components are not output by this command. " + "The COLUMN direction (default) is usually what you want, ROW will only work for dconn. " + "Using this command with -volume will usually take (much) more memory than the cifti file, since it must create the whole volume, rather than just the included voxels." ); return ret; } void OperationCiftiSeparateAll::useParameters(OperationParameters* myParams, ProgressObject* myProgObj) { LevelProgress myProgress(myProgObj); CiftiFile* myCifti = myParams->getCifti(1); int myDir = CiftiXMLOld::ALONG_COLUMN; OptionalParameter* dirOpt = myParams->getOptionalParameter(6); if (dirOpt->m_present) { AString dirName = dirOpt->getString(1); if (dirName == "ROW") { myDir = CiftiXMLOld::ALONG_ROW; } else { if (dirName != "COLUMN") { throw OperationException("direction must be 'ROW' or 'COLUMN'"); } } } OptionalParameter* leftOpt = myParams->getOptionalParameter(2); if (leftOpt->m_present) { MetricFile* outData = leftOpt->getOutputMetric(1); MetricFile* outRoi = NULL; OptionalParameter* roiOpt = leftOpt->getOptionalParameter(2); if (roiOpt->m_present) { outRoi = roiOpt->getOutputMetric(1); } processSurfaceComponent(myCifti, StructureEnum::CORTEX_LEFT, myDir, outData, outRoi); } OptionalParameter* rightOpt = myParams->getOptionalParameter(3); if (rightOpt->m_present) { MetricFile* outData = rightOpt->getOutputMetric(1); MetricFile* outRoi = NULL; OptionalParameter* roiOpt = rightOpt->getOptionalParameter(2); if (roiOpt->m_present) { outRoi = roiOpt->getOutputMetric(1); } processSurfaceComponent(myCifti, StructureEnum::CORTEX_RIGHT, myDir, outData, outRoi); } OptionalParameter* cerebOpt = myParams->getOptionalParameter(4); if (cerebOpt->m_present) { MetricFile* outData = cerebOpt->getOutputMetric(1); MetricFile* outRoi = NULL; OptionalParameter* roiOpt = cerebOpt->getOptionalParameter(2); if (roiOpt->m_present) { outRoi = roiOpt->getOutputMetric(1); } processSurfaceComponent(myCifti, StructureEnum::CEREBELLUM, myDir, outData, outRoi); } OptionalParameter* volOpt = myParams->getOptionalParameter(5); if (volOpt->m_present) { VolumeFile* outData = volOpt->getOutputVolume(1); VolumeFile* outRoi = NULL; OptionalParameter* roiOpt = volOpt->getOptionalParameter(2); if (roiOpt->m_present) { outRoi = roiOpt->getOutputVolume(1); } processVolume(myCifti, myDir, outData, outRoi); } } void OperationCiftiSeparateAll::processSurfaceComponent(const CiftiFile* myCifti, const StructureEnum::Enum& myStruct, const int& myDir, MetricFile* outData, MetricFile* outROI) { AlgorithmCiftiSeparate(NULL, myCifti, myDir, myStruct, outData, outROI); } void OperationCiftiSeparateAll::processVolume(const CiftiFile* myCifti, const int& myDir, VolumeFile* outData, VolumeFile* outROI) { int64_t offset[3]; AlgorithmCiftiSeparate(NULL, myCifti, myDir, outData, offset, outROI, false); } connectome-workbench-1.2.3+git41-gc4c6c90/src/Operations/OperationCiftiSeparateAll.h000066400000000000000000000033541300200146000301020ustar00rootroot00000000000000#ifndef __OPERATION_CIFTI_SEPARATE_ALL_H__ #define __OPERATION_CIFTI_SEPARATE_ALL_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 "AbstractOperation.h" #include "StructureEnum.h" namespace caret { class OperationCiftiSeparateAll : public AbstractOperation { static void processSurfaceComponent(const CiftiFile* myCifti, const StructureEnum::Enum& myStruct, const int& myDir, MetricFile* outData, MetricFile* outROI = NULL); static void processVolume(const CiftiFile* myCifti, const int& myDir, VolumeFile* outData, VolumeFile* outROI = NULL); public: static OperationParameters* getParameters(); static void useParameters(OperationParameters* myParams, ProgressObject* myProgObj); static AString getCommandSwitch(); static AString getShortDescription(); }; typedef TemplateAutoOperation AutoOperationCiftiSeparateAll; } #endif //__OPERATION_CIFTI_SEPARATE_ALL_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Operations/OperationCiftiStats.cxx000066400000000000000000000226431300200146000273600ustar00rootroot00000000000000/*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 "OperationCiftiStats.h" #include "OperationException.h" #include "CiftiFile.h" #include "ReductionOperation.h" #include #include #include #include #include #include using namespace caret; using namespace std; AString OperationCiftiStats::getCommandSwitch() { return "-cifti-stats"; } AString OperationCiftiStats::getShortDescription() { return "STATISTICS ALONG CIFTI COLUMNS"; } OperationParameters* OperationCiftiStats::getParameters() { OperationParameters* ret = new OperationParameters(); ret->addCiftiParameter(1, "cifti-in", "the input cifti"); OptionalParameter* reduceOpt = ret->createOptionalParameter(2, "-reduce", "use a reduction operation"); reduceOpt->addStringParameter(1, "operation", "the reduction operation"); OptionalParameter* percentileOpt = ret->createOptionalParameter(3, "-percentile", "give the value at a percentile"); percentileOpt->addDoubleParameter(1, "percent", "the percentile to find"); OptionalParameter* columnOpt = ret->createOptionalParameter(4, "-column", "only display output for one column"); columnOpt->addIntegerParameter(1, "column", "the column index (starting from 1)"); OptionalParameter* roiOpt = ret->createOptionalParameter(5, "-roi", "only consider data inside an roi"); roiOpt->addCiftiParameter(1, "roi-cifti", "the roi, as a cifti file"); roiOpt->createOptionalParameter(2, "-match-maps", "each column of input uses the corresponding column from the roi file"); 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. " + "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()); return ret; } namespace { float reduce(const vector& data, const ReductionEnum::Enum& myop, const vector& roiData) { if (roiData.empty()) { return ReductionOperation::reduce(data.data(), data.size(), myop); } else { int64_t numElems = (int64_t)data.size(); CaretAssert(numElems == (int64_t)roiData.size()); vector toUse; toUse.reserve(numElems); for (int64_t i = 0; i < numElems; ++i) { if (roiData[i] > 0.0f) { toUse.push_back(data[i]); } } if (toUse.empty()) throw OperationException("roi column is empty"); return ReductionOperation::reduce(toUse.data(), toUse.size(), myop); } } float percentile(const vector& data, const float& percent, const vector& roiData) { CaretAssert(percent >= 0.0f && percent <= 100.0f); vector toUse; if (roiData.empty()) { toUse = data; } else { int64_t numElems = (int64_t)data.size(); CaretAssert(numElems == (int64_t)roiData.size()); toUse.reserve(numElems); for (int i = 0; i < numElems; ++i) { if (roiData[i] > 0.0f) { toUse.push_back(data[i]); } } } if (toUse.empty()) throw OperationException("roi is empty"); sort(toUse.begin(), toUse.end()); const float index = percent / 100.0f * (toUse.size() - 1); if (index <= 0) return toUse[0]; if (index >= toUse.size() - 1) return toUse.back(); float ipart, fpart; fpart = modf(index, &ipart); return (1.0f - fpart) * toUse[(int)ipart] + fpart * toUse[((int)ipart) + 1]; } } void OperationCiftiStats::useParameters(OperationParameters* myParams, ProgressObject* myProgObj) { LevelProgress myProgress(myProgObj); CiftiFile* myInput = myParams->getCifti(1); const CiftiXML& myXML = myInput->getCiftiXML(); if (myXML.getNumberOfDimensions() != 2) throw OperationException("only 2D cifti are supported in this command"); int64_t numCols = myXML.getDimensionLength(CiftiXML::ALONG_ROW); int64_t colLength = myXML.getDimensionLength(CiftiXML::ALONG_COLUMN); OptionalParameter* reduceOpt = myParams->getOptionalParameter(2); OptionalParameter* percentileOpt = myParams->getOptionalParameter(3); if (reduceOpt->m_present == percentileOpt->m_present)//use == as logical xor { throw OperationException("you must use exactly one of -reduce or -percentile"); } ReductionEnum::Enum myop = ReductionEnum::INVALID; if (reduceOpt->m_present) { bool ok = false; myop = ReductionEnum::fromName(reduceOpt->getString(1), &ok); if (!ok) throw OperationException("unrecognized reduction operation: " + reduceOpt->getString(1)); } float percent = 0.0f; if (percentileOpt->m_present) { percent = (float)percentileOpt->getDouble(1);//use not within range to trap NaNs, just in case if (!(percent >= 0.0f && percent <= 100.0f)) throw OperationException("percentile must be between 0 and 100"); } int useColumn = -1; OptionalParameter* columnOpt = myParams->getOptionalParameter(4); if (columnOpt->m_present) { useColumn = columnOpt->getInteger(1) - 1; if (useColumn < 0 || useColumn >= numCols) throw OperationException("invalid column specified"); } vector roiData; bool matchColumnMode = false; CiftiFile* roiCifti = NULL; OptionalParameter* roiOpt = myParams->getOptionalParameter(5); if (roiOpt->m_present) { roiCifti = roiOpt->getCifti(1); if (!roiCifti->getCiftiXML().getMap(CiftiXML::ALONG_COLUMN)->approximateMatch(*(myXML.getMap(CiftiXML::ALONG_COLUMN)))) { throw OperationException("roi cifti does not match input cifti along columns"); } roiData.resize(colLength); if (roiOpt->getOptionalParameter(2)->m_present) { if (myXML.getMap(CiftiXML::ALONG_ROW)->getLength() != roiCifti->getCiftiXML().getMap(CiftiXML::ALONG_ROW)->getLength()) { 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 } } bool showMapName = myParams->getOptionalParameter(6)->m_present; const CiftiMappingType* rowMap = myXML.getMap(CiftiXML::ALONG_ROW); vector colScratch(colLength); if (useColumn == -1) { myInput->convertToInMemory();//we will be getting all columns, so read it all in first if (matchColumnMode) { roiCifti->convertToInMemory();//ditto } for (int i = 0; i < numCols; ++i) { myInput->getColumn(colScratch.data(), i); if (matchColumnMode) { roiCifti->getColumn(roiData.data(), i); } 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(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) << ": "; } stringstream resultsstr; resultsstr << setprecision(7) << result; cout << resultsstr.str() << endl; } } connectome-workbench-1.2.3+git41-gc4c6c90/src/Operations/OperationCiftiStats.h000066400000000000000000000025751300200146000270070ustar00rootroot00000000000000#ifndef __OPERATION_CIFTI_STATS_H__ #define __OPERATION_CIFTI_STATS_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 "AbstractOperation.h" namespace caret { class OperationCiftiStats : public AbstractOperation { public: static OperationParameters* getParameters(); static void useParameters(OperationParameters* myParams, ProgressObject* myProgObj); static AString getCommandSwitch(); static AString getShortDescription(); }; typedef TemplateAutoOperation AutoOperationCiftiStats; } #endif //__OPERATION_CIFTI_STATS_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Operations/OperationCiftiWeightedStats.cxx000066400000000000000000000666661300200146000310560ustar00rootroot00000000000000/*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 "OperationCiftiWeightedStats.h" #include "OperationException.h" #include "CaretHeap.h" #include "CiftiFile.h" #include "MetricFile.h" #include "SurfaceFile.h" #include #include #include #include #include using namespace caret; using namespace std; AString OperationCiftiWeightedStats::getCommandSwitch() { return "-cifti-weighted-stats"; } AString OperationCiftiWeightedStats::getShortDescription() { return "WEIGHTED STATISTICS ALONG CIFTI COLUMNS"; } OperationParameters* OperationCiftiWeightedStats::getParameters() { OperationParameters* ret = new OperationParameters(); ret->addCiftiParameter(1, "cifti-in", "the input cifti"); OptionalParameter* spatialWeightOpt = ret->createOptionalParameter(2, "-spatial-weights", "use vertex area and voxel volume as weights"); OptionalParameter* leftAreaSurfOpt = spatialWeightOpt->createOptionalParameter(1, "-left-area-surf", "use a surface for left vertex areas"); leftAreaSurfOpt->addSurfaceParameter(1, "left-surf", "the left surface to use, areas are in mm^2"); OptionalParameter* rightAreaSurfOpt = spatialWeightOpt->createOptionalParameter(2, "-right-area-surf", "use a surface for right vertex areas"); rightAreaSurfOpt->addSurfaceParameter(1, "right-surf", "the right surface to use, areas are in mm^2"); OptionalParameter* cerebAreaSurfOpt = spatialWeightOpt->createOptionalParameter(3, "-cerebellum-area-surf", "use a surface for cerebellum vertex areas"); cerebAreaSurfOpt->addSurfaceParameter(1, "cerebellum-surf", "the cerebellum surface to use, areas are in mm^2"); OptionalParameter* leftAreaMetricOpt = spatialWeightOpt->createOptionalParameter(4, "-left-area-metric", "use a metric file for left vertex areas"); leftAreaMetricOpt->addMetricParameter(1, "left-metric", "metric file containing left vertex areas"); OptionalParameter* rightAreaMetricOpt = spatialWeightOpt->createOptionalParameter(5, "-right-area-metric", "use a metric file for right vertex areas"); rightAreaMetricOpt->addMetricParameter(1, "right-metric", "metric file containing right vertex areas"); OptionalParameter* cerebAreaMetricOpt = spatialWeightOpt->createOptionalParameter(6, "-cerebellum-area-metric", "use a metric file for cerebellum vertex areas"); cerebAreaMetricOpt->addMetricParameter(1, "cerebellum-metric", "metric file containing cerebellum vertex areas"); OptionalParameter* ciftiWeightOpt = ret->createOptionalParameter(3, "-cifti-weights", "use a cifti file containing weights"); ciftiWeightOpt->addCiftiParameter(1, "weight-cifti", "the weights to use, as a cifti file"); OptionalParameter* columnOpt = ret->createOptionalParameter(4, "-column", "only display output for one column"); columnOpt->addIntegerParameter(1, "column", "the column to use (1-based)"); OptionalParameter* roiOpt = ret->createOptionalParameter(5, "-roi", "only consider data inside an roi"); roiOpt->addCiftiParameter(1, "roi-cifti", "the roi, as a cifti file"); roiOpt->createOptionalParameter(2, "-match-maps", "each column of input uses the corresponding column from the roi file"); ret->createOptionalParameter(6, "-mean", "compute weighted mean"); OptionalParameter* stdevOpt = ret->createOptionalParameter(7, "-stdev", "compute weighted standard deviation"); stdevOpt->createOptionalParameter(1, "-sample", "estimate population stdev from the sample"); OptionalParameter* percentileOpt = ret->createOptionalParameter(8, "-percentile", "compute weighted percentile"); percentileOpt->addDoubleParameter(1, "percent", "the percentile to find"); ret->createOptionalParameter(9, "-sum", "compute weighted sum"); ret->createOptionalParameter(10, "-show-map-name", "print map index and name before each output"); ret->setHelpText( AString("If the mapping along column is brain models, for each column of the input, the specified operation is done on each surface and across all voxels, and the results are printed separately. ") + "For other mapping types, the operation is done on each column, and one number per map is printed. " + "Exactly one of -spatial-weights or -cifti-weights must be specified. " + "Use -column to only give output for a single column. " + "Use -roi to consider only the data within a region. " + "Exactly one of -mean, -stdev, -percentile or -sum must be specified.\n\n" + "Using -sum with -spatial-weights (or with -cifti-weights and a cifti containing weights of similar meaning) is equivalent to integrating with respect to area and volume. " + "When the input is binary ROIs, this will therefore output the area or volume of each ROI." ); return ret; } namespace { enum OperationType { MEAN, STDEV, SAMPSTDEV, PERCENTILE, SUM }; float doOperation(const float* data, const float* weights, const int64_t& numElements, const OperationType& myop, const float* roiData, const float& argument) {//argument is only used for percentile currently if (roiData != NULL) { bool haveData = false; for (int64_t i = 0; i < numElements; ++i) { if (weights[i] > 0.0f) { haveData = true; break; } } if (!haveData) throw OperationException("roi column is empty"); } switch(myop) { case SUM: case MEAN: case STDEV: case SAMPSTDEV://these all start the same way { double accum = 0.0, weightsum = 0.0; for (int64_t i = 0; i < numElements; ++i) { if (roiData == NULL || roiData[i] > 0.0f) { accum += data[i] * weights[i]; weightsum += weights[i]; } } if (myop == SUM) return accum; const float mean = accum / weightsum; if (myop == MEAN) return mean; accum = 0.0; double weightsum2 = 0.0;//for weighted sample stdev for (int64_t i = 0; i < numElements; ++i) { if (roiData == NULL || roiData[i] > 0.0f) { float tempf = data[i] - mean; accum += weights[i] * tempf * tempf; weightsum2 += weights[i] * weights[i]; } } if (myop == STDEV) return sqrt(accum / weightsum); CaretAssert(myop == SAMPSTDEV); return sqrt(accum / (weightsum - weightsum2 / weightsum));//http://en.wikipedia.org/wiki/Weighted_arithmetic_mean#Weighted_sample_variance } case PERCENTILE: { CaretAssert(argument >= 0.0f && argument <= 100.0f); CaretSimpleMinHeap sorter; double weightaccum = 0.0;//double will usually prevent adding weights in a different order from getting a different answer for (int64_t i = 0; i < numElements; ++i) { if (roiData == NULL || roiData[i] > 0.0f) { if (weights[i] < 0.0f) throw OperationException("negative weights not allowed in weighted percentile"); weightaccum += weights[i]; sorter.push(weights[i], data[i]);//sort by value, so the key is the data } } int64_t numUse = sorter.size(); if (numUse == 1)//would need special handling anyway, so get it early { float ret; sorter.top(&ret); return ret; } float targetWeight = argument / 100.0f * weightaccum; float lastData, nextData; float lastWeight = sorter.pop(&lastData); weightaccum = lastWeight; float nextWeight = sorter.top(&nextData); int64_t position = 1;//because the first and last sections get special treatment to not have flat ends on the function while (weightaccum + nextWeight * 0.5f < targetWeight && sorter.size() > 1) { ++position; sorter.pop(); weightaccum += nextWeight; lastWeight = nextWeight; lastData = nextData; nextWeight = sorter.top(&nextData); } if (targetWeight < weightaccum) { if (position == 1) {//stretch interpolation at first position to the edge return lastData + (nextData - lastData) * 0.5f * ((targetWeight - weightaccum) / lastWeight + 1.0f); } else { return lastData + (nextData - lastData) * 0.5f * ((targetWeight - weightaccum) / (lastWeight * 0.5f) + 1.0f); } } else { if (position == numUse - 1) {//ditto return (lastData + nextData) * 0.5f + (nextData - lastData) * 0.5f * (targetWeight - weightaccum) / nextWeight; } else { return (lastData + nextData) * 0.5f + (nextData - lastData) * 0.5f * (targetWeight - weightaccum) / (nextWeight * 0.5f); } } } } CaretAssert(false);//make sure execution never actually reaches end of function throw OperationException("internal error in weighted stats"); } } void OperationCiftiWeightedStats::useParameters(OperationParameters* myParams, ProgressObject* myProgObj) { LevelProgress myProgress(myProgObj); CiftiFile* myInput = myParams->getCifti(1); const CiftiXML& myXML = myInput->getCiftiXML(); if (myXML.getNumberOfDimensions() != 2) throw OperationException("only 2D cifti are supported in this command"); int64_t numCols = myXML.getDimensionLength(CiftiXML::ALONG_ROW); int64_t colLength = myXML.getDimensionLength(CiftiXML::ALONG_COLUMN); OptionalParameter* spatialWeightOpt = myParams->getOptionalParameter(2); OptionalParameter* ciftiWeightOpt = myParams->getOptionalParameter(3); if (spatialWeightOpt->m_present == ciftiWeightOpt->m_present)//use == as logical xnor { throw OperationException("you must use exactly one of -spatial-weights or -cifti-weights"); } vector combinedWeights(colLength); if (spatialWeightOpt->m_present) { if (myXML.getMappingType(CiftiXML::ALONG_COLUMN) != CiftiMappingType::BRAIN_MODELS) throw OperationException("spatial weights require a brain models mapping"); CiftiBrainModelsMap myDenseMap = myXML.getBrainModelsMap(CiftiXML::ALONG_COLUMN); OptionalParameter* leftSurfOpt = spatialWeightOpt->getOptionalParameter(1); OptionalParameter* rightSurfOpt = spatialWeightOpt->getOptionalParameter(2); OptionalParameter* cerebSurfOpt = spatialWeightOpt->getOptionalParameter(3); OptionalParameter* leftMetricOpt = spatialWeightOpt->getOptionalParameter(4); OptionalParameter* rightMetricOpt = spatialWeightOpt->getOptionalParameter(5); OptionalParameter* cerebMetricOpt = spatialWeightOpt->getOptionalParameter(6); if (leftSurfOpt->m_present && leftMetricOpt->m_present) throw OperationException("only one of -left-area-surf and -left-area-metric may be specified"); if (rightSurfOpt->m_present && rightMetricOpt->m_present) throw OperationException("only one of -right-area-surf and -right-area-metric may be specified"); if (cerebSurfOpt->m_present && cerebMetricOpt->m_present) throw OperationException("only one of -cerebellum-area-surf and -cerebellum-area-metric may be specified"); vector surfStructs = myDenseMap.getSurfaceStructureList(); int numSurf = (int)surfStructs.size(); for (int i = 0; i < numSurf; ++i) { OptionalParameter* surfOpt = NULL, *metricOpt = NULL; AString structName; switch (surfStructs[i]) { case StructureEnum::CORTEX_LEFT: surfOpt = leftSurfOpt; metricOpt = leftMetricOpt; structName = "left"; break; case StructureEnum::CORTEX_RIGHT: surfOpt = rightSurfOpt; metricOpt = rightMetricOpt; structName = "right"; break; case StructureEnum::CEREBELLUM: surfOpt = cerebSurfOpt; metricOpt = cerebMetricOpt; structName = "cerebellum"; break; default: throw OperationException("unsupported surface structure: " + StructureEnum::toName(surfStructs[i])); } vector myMap = myDenseMap.getSurfaceMap(surfStructs[i]); int mapSize = (int)myMap.size(); if (surfOpt->m_present) { SurfaceFile* mySurf = surfOpt->getSurface(1); if (mySurf->getNumberOfNodes() != myDenseMap.getSurfaceNumberOfNodes(surfStructs[i])) { throw OperationException(structName + " area surface has different number of vertices than cifti file"); } vector surfAreas; mySurf->computeNodeAreas(surfAreas); for (int i = 0; i < mapSize; ++i) { combinedWeights[myMap[i].m_ciftiIndex] = surfAreas[myMap[i].m_surfaceNode]; } } else if (metricOpt->m_present) { MetricFile* myMetric = metricOpt->getMetric(1); if (myMetric->getNumberOfNodes() != myDenseMap.getSurfaceNumberOfNodes(surfStructs[i])) { throw OperationException(structName + " area metric has different number of vertices than cifti file"); } for (int i = 0; i < mapSize; ++i) { combinedWeights[myMap[i].m_ciftiIndex] = myMetric->getValue(myMap[i].m_surfaceNode, 0); } } else { throw OperationException("no area data specified for " + structName + " surface"); } } if (myDenseMap.hasVolumeData()) { float voxelVolume = myDenseMap.getVolumeSpace().getVoxelVolume(); vector volMap = myDenseMap.getFullVolumeMap(); int64_t volMapSize = (int64_t)volMap.size(); for (int64_t i = 0; i < volMapSize; ++i) { combinedWeights[volMap[i].m_ciftiIndex] = voxelVolume; } } } if (ciftiWeightOpt->m_present) { CiftiFile* weightCifti = ciftiWeightOpt->getCifti(1); if (!myXML.getMap(CiftiXML::ALONG_COLUMN)->approximateMatch(*(weightCifti->getCiftiXML().getMap(CiftiXML::ALONG_COLUMN)))) { throw OperationException("weighting cifti has incompatible mapping along column"); } weightCifti->getColumn(combinedWeights.data(), 0);//since we are only using one column, go ahead and call getColumn while it is on disk } int64_t useColumn = -1; OptionalParameter* columnOpt = myParams->getOptionalParameter(4); if (columnOpt->m_present) { useColumn = columnOpt->getInteger(1) - 1;//1-based indexing convention if (useColumn < 0 || useColumn >= numCols) throw OperationException("invalid column specified"); } vector roiData; bool matchColumnMode = false; CiftiFile* myRoi = NULL; OptionalParameter* roiOpt = myParams->getOptionalParameter(5); if (roiOpt->m_present) { myRoi = roiOpt->getCifti(1); if (!myXML.getMap(CiftiXML::ALONG_COLUMN)->approximateMatch(*(myRoi->getCiftiXML().getMap(CiftiXML::ALONG_COLUMN)))) { throw OperationException("roi cifti has incompatible mapping along column"); } roiData.resize(colLength); if (roiOpt->getOptionalParameter(2)->m_present) { if (myXML.getMap(CiftiXML::ALONG_ROW)->getLength() != myRoi->getCiftiXML().getMap(CiftiXML::ALONG_ROW)->getLength()) { 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 } } bool haveOp = false; OperationType myop; if (myParams->getOptionalParameter(6)->m_present) { haveOp = true; myop = MEAN; } OptionalParameter* stdevOpt = myParams->getOptionalParameter(7); if (stdevOpt->m_present) { if (haveOp) throw OperationException("you may only specify one operation"); haveOp = true; if (stdevOpt->getOptionalParameter(1)->m_present) { myop = SAMPSTDEV; } else { myop = STDEV; } } float argument = -1.0f; OptionalParameter* percentileOpt = myParams->getOptionalParameter(8); if (percentileOpt->m_present) { if (haveOp) throw OperationException("you may only specify one operation"); haveOp = true; myop = PERCENTILE; argument = percentileOpt->getDouble(1); if (!(argument >= 0.0f && argument <= 100.0f)) throw OperationException("percentile must be between 0 and 100"); } if (myParams->getOptionalParameter(9)->m_present) { if (haveOp) throw OperationException("you may only specify one operation"); haveOp = true; myop = SUM; } if (!haveOp) throw OperationException("you must specify an operation"); bool showMapName = myParams->getOptionalParameter(10)->m_present; const CiftiMappingType* rowMap = myXML.getMap(CiftiXML::ALONG_ROW); vector inColumn(colLength); 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) { 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 (matchColumnMode) { myRoi->getColumn(roiData.data(), i); } if (showMapName) { cout << AString::number(i + 1) << ": " << rowMap->getIndexName(i) << ":" << endl; } for (int j = 0; j < numModels; ++j) { if (myModels[j].m_type == CiftiBrainModelsMap::SURFACE) { 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); } 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()) { roiVolData[j] = roiData[volMap[j].m_ciftiIndex]; } } 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); } 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); } if (showMapName) { cout << AString::number(i + 1) << ": " << rowMap->getIndexName(i) << ": "; } stringstream resultsstr; resultsstr << setprecision(7) << result; cout << resultsstr.str() << endl; } } } else { myInput->getColumn(inColumn.data(), useColumn); if (matchColumnMode) { myRoi->getColumn(roiData.data(), useColumn); } if (myXML.getMappingType(CiftiXML::ALONG_COLUMN) == CiftiMappingType::BRAIN_MODELS) { const CiftiBrainModelsMap& myDenseMap = myXML.getBrainModelsMap(CiftiXML::ALONG_COLUMN); vector myModels = myDenseMap.getModelInfo(); int numModels = (int)myModels.size(); if (showMapName) { cout << AString::number(useColumn + 1) << ": " << rowMap->getIndexName(useColumn) << ":" << endl; } for (int j = 0; j < numModels; ++j) { if (myModels[j].m_type == CiftiBrainModelsMap::SURFACE) { 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); } 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()) { roiVolData[j] = roiData[volMap[j].m_ciftiIndex]; } } 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); } 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) << ": "; } stringstream resultsstr; resultsstr << setprecision(7) << result; cout << resultsstr.str() << endl; } } } connectome-workbench-1.2.3+git41-gc4c6c90/src/Operations/OperationCiftiWeightedStats.h000066400000000000000000000026601300200146000304630ustar00rootroot00000000000000#ifndef __OPERATION_CIFTI_WEIGHTED_STATS_H__ #define __OPERATION_CIFTI_WEIGHTED_STATS_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 "AbstractOperation.h" namespace caret { class OperationCiftiWeightedStats : public AbstractOperation { public: static OperationParameters* getParameters(); static void useParameters(OperationParameters* myParams, ProgressObject* myProgObj); static AString getCommandSwitch(); static AString getShortDescription(); }; typedef TemplateAutoOperation AutoOperationCiftiWeightedStats; } #endif //__OPERATION_CIFTI_WEIGHTED_STATS_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Operations/OperationConvertAffine.cxx000066400000000000000000000106711300200146000300320ustar00rootroot00000000000000/*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 "AffineFile.h" #include "OperationConvertAffine.h" #include "OperationException.h" using namespace caret; using namespace std; AString OperationConvertAffine::getCommandSwitch() { return "-convert-affine"; } AString OperationConvertAffine::getShortDescription() { return "CONVERT AN AFFINE FILE BETWEEN CONVENTIONS"; } OperationParameters* OperationConvertAffine::getParameters() { OperationParameters* ret = new OperationParameters(); OptionalParameter* fromWorld = ret->createOptionalParameter(1, "-from-world", "input is a NIFTI 'world' affine"); fromWorld->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"); fromFlirt->addStringParameter(2, "source-volume", "the source volume used when generating the input affine"); fromFlirt->addStringParameter(3, "target-volume", "the target volume used when generating the input affine"); 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) ParameterComponent* toFlirt = ret->createRepeatableParameter(4, "-to-flirt", "write output as a flirt matrix"); toFlirt->addStringParameter(1, "output", "output - the output affine"); toFlirt->addStringParameter(2, "source-volume", "the volume you want to apply the transform to"); toFlirt->addStringParameter(3, "target-volume", "the target space you want the transformed volume to match"); 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." ); return ret; } void OperationConvertAffine::useParameters(OperationParameters* myParams, ProgressObject* myProgObj) { LevelProgress myProgress(myProgObj); AffineFile myAffine; bool haveInput = false; OptionalParameter* fromWorld = myParams->getOptionalParameter(1); if (fromWorld->m_present) { haveInput = true; myAffine.readWorld(fromWorld->getString(1)); } OptionalParameter* fromFlirt = myParams->getOptionalParameter(2); if (fromFlirt->m_present) { if (haveInput) throw OperationException("only one -from option may be specified"); haveInput = true; myAffine.readFlirt(fromFlirt->getString(1), fromFlirt->getString(2), fromFlirt->getString(3)); } if (!haveInput) throw OperationException("you must specify a -from option"); OptionalParameter* toWorld = myParams->getOptionalParameter(3); if (toWorld->m_present) { myAffine.writeWorld(toWorld->getString(1)); } 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 for (int i = 0; i < numFlirt; ++i) { myAffine.writeFlirt(toFlirt[i]->getString(1), toFlirt[i]->getString(2), toFlirt[i]->getString(3)); } } connectome-workbench-1.2.3+git41-gc4c6c90/src/Operations/OperationConvertAffine.h000066400000000000000000000026171300200146000274600ustar00rootroot00000000000000#ifndef __OPERATION_CONVERT_AFFINE_H__ #define __OPERATION_CONVERT_AFFINE_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 "AbstractOperation.h" namespace caret { class OperationConvertAffine : public AbstractOperation { public: static OperationParameters* getParameters(); static void useParameters(OperationParameters* myParams, ProgressObject* myProgObj); static AString getCommandSwitch(); static AString getShortDescription(); }; typedef TemplateAutoOperation AutoOperationConvertAffine; } #endif //__OPERATION_CONVERT_AFFINE_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Operations/OperationConvertFiberOrientations.cxx000066400000000000000000000223621300200146000322700ustar00rootroot00000000000000/*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 "OperationConvertFiberOrientations.h" #include "OperationException.h" #include "CiftiFile.h" #include "VolumeFile.h" using namespace caret; using namespace std; AString OperationConvertFiberOrientations::getCommandSwitch() { return "-convert-fiber-orientations"; } AString OperationConvertFiberOrientations::getShortDescription() { return "CONVERT BINGHAM PARAMETER VOLUMES TO FIBER ORIENTATION FILE"; } OperationParameters* OperationConvertFiberOrientations::getParameters() { OperationParameters* ret = new OperationParameters(); ret->addVolumeParameter(1, "label-volume", "volume of cifti structure labels"); ret->addCiftiOutputParameter(2, "fiber-out", "the output fiber orientation file"); ParameterComponent* fiberOpt = ret->createRepeatableParameter(3, "-fiber", "specify the parameter volumes for a fiber"); fiberOpt->addVolumeParameter(1, "mean-f", "mean fiber strength"); fiberOpt->addVolumeParameter(2, "stdev-f", "standard deviation of fiber strength"); fiberOpt->addVolumeParameter(3, "theta", "theta angle"); fiberOpt->addVolumeParameter(4, "phi", "phi angle"); fiberOpt->addVolumeParameter(5, "psi", "psi angle"); fiberOpt->addVolumeParameter(6, "ka", "ka bingham parameter"); fiberOpt->addVolumeParameter(7, "kb", "kb bingham parameter"); AString myText = AString("Takes precomputed bingham parameters from volume files and converts them to the format workbench uses for display. ") + "The argument must be a label volume, where the labels use these strings:\n\n"; vector myStructureEnums; StructureEnum::getAllEnums(myStructureEnums); for (int i = 0; i < (int)myStructureEnums.size(); ++i) { myText += "\n" + StructureEnum::toName(myStructureEnums[i]); } ret->setHelpText(myText); return ret; } void OperationConvertFiberOrientations::useParameters(OperationParameters* myParams, ProgressObject* myProgObj) { LevelProgress myProgress(myProgObj); VolumeFile* labelVol = myParams->getVolume(1); if (labelVol->getType() != SubvolumeAttributes::LABEL) { throw OperationException(" must have a label table, see -volume-label-import"); } CiftiFile* ciftiOut = myParams->getOutputCifti(2); const vector& myInstances = *(myParams->getRepeatableParameterInstances(3)); int numFibers = (int)myInstances.size(); if (numFibers < 1) throw OperationException("must specify -fiber at least once"); if (numFibers > 3) throw OperationException("only three fibers are supported at this time"); for (int i = 0; i < numFibers; ++i) { VolumeFile* meanfvol = myInstances[i]->getVolume(1); VolumeFile* stdevfvol = myInstances[i]->getVolume(2); VolumeFile* thetavol = myInstances[i]->getVolume(3); VolumeFile* phivol = myInstances[i]->getVolume(4); VolumeFile* psivol = myInstances[i]->getVolume(5); VolumeFile* kavol = myInstances[i]->getVolume(6); VolumeFile* kbvol = myInstances[i]->getVolume(7); if (!labelVol->matchesVolumeSpace(meanfvol) || !labelVol->matchesVolumeSpace(stdevfvol) || !labelVol->matchesVolumeSpace(thetavol) || !labelVol->matchesVolumeSpace(phivol) || !labelVol->matchesVolumeSpace(psivol) || !labelVol->matchesVolumeSpace(kavol) || !labelVol->matchesVolumeSpace(kbvol)) { throw OperationException("all inputs must be in the same volume space"); } } map labelMap;//maps label values to structures vector > voxelLists;//voxel lists for each volume component map componentMap;//maps structures to indexes in voxelLists const GiftiLabelTable* myLabelTable = labelVol->getMapLabelTable(0); vector labelKeys; myLabelTable->getKeys(labelKeys); int count = 0; for (int i = 0; i < (int)labelKeys.size(); ++i) { bool ok = false; StructureEnum::Enum thisStructure = StructureEnum::fromName(myLabelTable->getLabelName(labelKeys[i]), &ok); if (ok) { if (componentMap.find(thisStructure) == componentMap.end())//make sure we don't already have this structure from another label { labelMap[labelKeys[i]] = thisStructure; componentMap[thisStructure] = count; ++count; } } } voxelLists.resize(count); vector mydims; labelVol->getDimensions(mydims); for (int64_t k = 0; k < mydims[2]; ++k) { for (int64_t j = 0; j < mydims[1]; ++j) { for (int64_t i = 0; i < mydims[0]; ++i) { int myval = (int)floor(labelVol->getValue(i, j, k) + 0.5f); map::iterator myiter = labelMap.find(myval); if (myiter != labelMap.end()) { int whichList = componentMap.find(myiter->second)->second;//this should always find one, so we don't need to check for being end voxelLists[whichList].push_back(i); voxelLists[whichList].push_back(j); voxelLists[whichList].push_back(k); } } } } int64_t ciftiVolDims[3]; ciftiVolDims[0] = mydims[0]; ciftiVolDims[1] = mydims[1]; ciftiVolDims[2] = mydims[2]; CiftiXMLOld myXML; myXML.resetColumnsToBrainModels(); myXML.setVolumeDimsAndSForm(ciftiVolDims, labelVol->getSform()); for (map::iterator myiter = componentMap.begin(); myiter != componentMap.end(); ++myiter) { myXML.addVolumeModelToColumns(voxelLists[myiter->second], myiter->first); } myXML.resetRowsToScalars(24); myXML.setMapNameForRowIndex(0, "x coord"); myXML.setMapNameForRowIndex(1, "y coord"); myXML.setMapNameForRowIndex(2, "z coord"); myXML.setMapNameForRowIndex(3, "mean f1"); myXML.setMapNameForRowIndex(4, "stdev f1"); myXML.setMapNameForRowIndex(5, "theta1"); myXML.setMapNameForRowIndex(6, "phi1"); myXML.setMapNameForRowIndex(7, "ka1"); myXML.setMapNameForRowIndex(8, "kb1"); myXML.setMapNameForRowIndex(9, "psi1"); myXML.setMapNameForRowIndex(10, "mean f2"); myXML.setMapNameForRowIndex(11, "stdev f2"); myXML.setMapNameForRowIndex(12, "theta2"); myXML.setMapNameForRowIndex(13, "phi2"); myXML.setMapNameForRowIndex(14, "ka2"); myXML.setMapNameForRowIndex(15, "kb2"); myXML.setMapNameForRowIndex(16, "psi2"); myXML.setMapNameForRowIndex(17, "mean f3"); myXML.setMapNameForRowIndex(18, "stdev f3"); myXML.setMapNameForRowIndex(19, "theta3"); myXML.setMapNameForRowIndex(20, "phi3"); myXML.setMapNameForRowIndex(21, "ka3"); myXML.setMapNameForRowIndex(22, "kb3"); myXML.setMapNameForRowIndex(23, "psi3"); ciftiOut->setCiftiXML(myXML); vector volMap; CaretArray temprow(24, 0.0f); temprow[14] = 1.0f;//do not put zeros in ka and kb, ever temprow[15] = 1.0f; temprow[21] = 1.0f; temprow[22] = 1.0f; myXML.getVolumeMapForColumns(volMap);//we don't need to know which voxel is from which parcel int64_t end = (int64_t)volMap.size(); for (int64_t i = 0; i < end; ++i) { labelVol->indexToSpace(volMap[i].m_ijk, temprow);//first three elements are the coordinates for (int j = 0; j < numFibers; ++j) { int base = 7 * j + 3; VolumeFile* meanfvol = myInstances[j]->getVolume(1); VolumeFile* stdevfvol = myInstances[j]->getVolume(2); VolumeFile* thetavol = myInstances[j]->getVolume(3); VolumeFile* phivol = myInstances[j]->getVolume(4); VolumeFile* psivol = myInstances[j]->getVolume(5); VolumeFile* kavol = myInstances[j]->getVolume(6); VolumeFile* kbvol = myInstances[j]->getVolume(7); temprow[base] = meanfvol->getValue(volMap[i].m_ijk); temprow[base + 1] = stdevfvol->getValue(volMap[i].m_ijk); temprow[base + 2] = thetavol->getValue(volMap[i].m_ijk); temprow[base + 3] = phivol->getValue(volMap[i].m_ijk); temprow[base + 4] = kavol->getValue(volMap[i].m_ijk); temprow[base + 5] = kbvol->getValue(volMap[i].m_ijk); temprow[base + 6] = psivol->getValue(volMap[i].m_ijk); } ciftiOut->setRow(temprow, volMap[i].m_ciftiIndex); } } connectome-workbench-1.2.3+git41-gc4c6c90/src/Operations/OperationConvertFiberOrientations.h000066400000000000000000000027241300200146000317150ustar00rootroot00000000000000#ifndef __OPERATION_CONVERT_FIBER_ORIENTATIONS_H__ #define __OPERATION_CONVERT_FIBER_ORIENTATIONS_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 "AbstractOperation.h" namespace caret { class OperationConvertFiberOrientations : public AbstractOperation { public: static OperationParameters* getParameters(); static void useParameters(OperationParameters* myParams, ProgressObject* myProgObj); static AString getCommandSwitch(); static AString getShortDescription(); }; typedef TemplateAutoOperation AutoOperationConvertFiberOrientations; } #endif //__OPERATION_CONVERT_FIBER_ORIENTATIONS_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Operations/OperationConvertMatrix4ToMatrix2.cxx000066400000000000000000000067461300200146000317540ustar00rootroot00000000000000/*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 "OperationConvertMatrix4ToMatrix2.h" #include "OperationException.h" #include "CiftiFile.h" #include "CaretSparseFile.h" using namespace caret; using namespace std; AString OperationConvertMatrix4ToMatrix2::getCommandSwitch() { return "-convert-matrix4-to-matrix2"; } AString OperationConvertMatrix4ToMatrix2::getShortDescription() { return "GENERATES A MATRIX2 CIFTI FROM MATRIX4 WBSPARSE"; } OperationParameters* OperationConvertMatrix4ToMatrix2::getParameters() { OperationParameters* ret = new OperationParameters(); ret->addStringParameter(1, "matrix4-wbsparse", "a wbsparse matrix4 file"); ret->addCiftiOutputParameter(2, "counts-out", "the total fiber counts, as a cifti file"); OptionalParameter* distanceOpt = ret->createOptionalParameter(3, "-distances", "output average trajectory distance"); distanceOpt->addCiftiOutputParameter(1, "distance-out", "the distances, as a cifti file"); 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.") ); return ret; } void OperationConvertMatrix4ToMatrix2::useParameters(OperationParameters* myParams, ProgressObject* myProgObj) { LevelProgress myProgress(myProgObj); AString matrix4Name = myParams->getString(1); CiftiFile* myCountsOut = myParams->getOutputCifti(2); CiftiFile* myDistOut = NULL; OptionalParameter* distanceOpt = myParams->getOptionalParameter(3); if (distanceOpt->m_present) { myDistOut = distanceOpt->getOutputCifti(1); } CaretSparseFile matrix4(matrix4Name); const CiftiXML& myXML = matrix4.getCiftiXML(); myCountsOut->setCiftiXML(myXML); if (myDistOut != NULL) myDistOut->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) { matrix4.getFibersRowSparse(i, indices, fibers); int numIndices = (int)indices.size(); for (int j = 0; j < numIndices; ++j) { scratchRow[indices[j]] = fibers[j].totalCount; } myCountsOut->setRow(scratchRow.data(), i); if (myDistOut != NULL) { for (int j = 0; j < numIndices; ++j) { scratchRow[indices[j]] = fibers[j].distance; } myDistOut->setRow(scratchRow.data(), i); } for (int j = 0; j < numIndices; ++j) { scratchRow[indices[j]] = 0.0f; } } } connectome-workbench-1.2.3+git41-gc4c6c90/src/Operations/OperationConvertMatrix4ToMatrix2.h000066400000000000000000000027211300200146000313660ustar00rootroot00000000000000#ifndef __OPERATION_CONVERT_MATRIX4_TO_MATRIX2_H__ #define __OPERATION_CONVERT_MATRIX4_TO_MATRIX2_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 "AbstractOperation.h" namespace caret { class OperationConvertMatrix4ToMatrix2 : public AbstractOperation { public: static OperationParameters* getParameters(); static void useParameters(OperationParameters* myParams, ProgressObject* myProgObj); static AString getCommandSwitch(); static AString getShortDescription(); }; typedef TemplateAutoOperation AutoOperationConvertMatrix4ToMatrix2; } #endif //__OPERATION_CONVERT_MATRIX4_TO_MATRIX2_H__ OperationConvertMatrix4ToWorkbenchSparse.cxx000066400000000000000000000236621300200146000334430ustar00rootroot00000000000000connectome-workbench-1.2.3+git41-gc4c6c90/src/Operations/*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 "OperationConvertMatrix4ToWorkbenchSparse.h" #include "OperationException.h" #include "CaretHeap.h" #include "CaretSparseFile.h" #include "CiftiFile.h" #include "OxfordSparseThreeFile.h" #include "MetricFile.h" #include "VolumeFile.h" #include #include #include #include #include using namespace caret; using namespace std; AString OperationConvertMatrix4ToWorkbenchSparse::getCommandSwitch() { return "-convert-matrix4-to-workbench-sparse"; } AString OperationConvertMatrix4ToWorkbenchSparse::getShortDescription() { return "CONVERT A 3-FILE MATRIX4 TO A WORKBENCH SPARSE FILE"; } OperationParameters* OperationConvertMatrix4ToWorkbenchSparse::getParameters() { OperationParameters* ret = new OperationParameters(); ret->addStringParameter(1, "matrix4_1", "the first matrix4 file"); ret->addStringParameter(2, "matrix4_2", "the second matrix4 file"); ret->addStringParameter(3, "matrix4_3", "the third matrix4 file"); ret->addCiftiParameter(4, "orientation-file", "the .fiberTEMP.nii file this trajectory file applies to"); ret->addStringParameter(5, "voxel-list", "list of white matter voxel index triplets as used in the trajectory matrix"); ret->addStringParameter(6, "wb-sparse-out", "output - the output workbench sparse file"); OptionalParameter* surfaceOpt = ret->createOptionalParameter(7, "-surface-seeds", "specify the surface seed space"); surfaceOpt->addMetricParameter(1, "seed-roi", "metric roi file of all vertices used in the seed space"); OptionalParameter* volumeOpt = ret->createOptionalParameter(8, "-volume-seeds", "specify the volume seed space"); volumeOpt->addCiftiParameter(1, "cifti-template", "cifti file to use the volume mappings from"); volumeOpt->addStringParameter(2, "direction", "dimension along the cifti file to take the mapping from, ROW or COLUMN"); ret->setHelpText( AString("Converts the matrix 4 output of probtrackx to workbench sparse file format. ") + "Exactly one of -surface-seeds and -volume-seeds must be specified." ); return ret; } void OperationConvertMatrix4ToWorkbenchSparse::useParameters(OperationParameters* myParams, ProgressObject* myProgObj) { LevelProgress myProgress(myProgObj); AString inFile1 = myParams->getString(1); AString inFile2 = myParams->getString(2); AString inFile3 = myParams->getString(3); AString voxelFileName = myParams->getString(5); CiftiFile* orientationFile = myParams->getCifti(4); AString outFileName = myParams->getString(6); OxfordSparseThreeFile inFile(inFile1, inFile2, inFile3); const int64_t* sparseDims = inFile.getDimensions(); OptionalParameter* surfaceOpt = myParams->getOptionalParameter(7); OptionalParameter* volumeOpt = myParams->getOptionalParameter(8); if (surfaceOpt->m_present == volumeOpt->m_present) throw OperationException("you must specify exactly one of -surface-seeds and -volume-seeds");//use == on booleans as xnor const CiftiXML& orientXML = orientationFile->getCiftiXML(); if (orientXML.getMappingType(CiftiXML::ALONG_COLUMN) != CiftiMappingType::BRAIN_MODELS) throw OperationException("orientation file must have brain models mapping along column"); CiftiXML myXML; myXML.setNumberOfDimensions(2); myXML.setMap(CiftiXML::ALONG_ROW, *(orientXML.getMap(CiftiXML::ALONG_COLUMN))); if (surfaceOpt->m_present) { MetricFile* myROI = surfaceOpt->getMetric(1); const float* roiData = myROI->getValuePointerForColumn(0); CiftiBrainModelsMap tempMap; tempMap.addSurfaceModel(myROI->getNumberOfNodes(), myROI->getStructure(), roiData); if (tempMap.getLength() != sparseDims[1]) { throw OperationException("roi has a different number of selected vertices than the input matrix"); } myXML.setMap(CiftiXML::ALONG_COLUMN, tempMap); } if (volumeOpt->m_present) { CiftiFile* myTemplate = volumeOpt->getCifti(1); AString directionName = volumeOpt->getString(2); int myDir = -1; if (directionName == "ROW") { myDir = CiftiXML::ALONG_ROW; } else if (directionName == "COLUMN") { myDir = CiftiXML::ALONG_COLUMN; } else { throw OperationException("direction must be ROW or COLUMN"); } const CiftiXML& templateXML = myTemplate->getCiftiXML(); if (templateXML.getMappingType(myDir) != CiftiMappingType::BRAIN_MODELS) throw OperationException("template cifti file must have brain models along specified direction"); const CiftiBrainModelsMap& templateMap = templateXML.getBrainModelsMap(myDir); if (!templateMap.hasVolumeData()) throw OperationException("template cifti file has no volume data"); vector surfList = templateMap.getSurfaceStructureList(), volList = templateMap.getVolumeStructureList();//since we only need the volume models, we don't need to loop through all models CiftiBrainModelsMap tempMap; tempMap.setVolumeSpace(templateMap.getVolumeSpace()); for (int i = 0; i < (int)volList.size(); ++i) { vector myMap = templateMap.getVolumeStructureMap(volList[i]); int64_t mapSize = (int64_t)myMap.size(); vector ijkList; for (int64_t j = 0; j < mapSize; ++j) { ijkList.push_back(myMap[j].m_ijk[0]); ijkList.push_back(myMap[j].m_ijk[1]); ijkList.push_back(myMap[j].m_ijk[2]); } tempMap.addVolumeModel(volList[i], ijkList); } if (tempMap.getLength() != sparseDims[1]) throw OperationException("volume models in template cifti file do not match the dimension of the input file"); myXML.setMap(CiftiXML::ALONG_COLUMN, tempMap); } CaretAssert(myXML.getDimensionLength(CiftiXML::ALONG_COLUMN) == sparseDims[1]); fstream voxelFile(voxelFileName.toLocal8Bit().constData(), fstream::in); if (!voxelFile.good()) { throw OperationException("failed to open voxel list file for reading"); } const CiftiBrainModelsMap& rowMap = myXML.getBrainModelsMap(CiftiXML::ALONG_ROW);//tested above, as orientationXML const int64_t* volDims = rowMap.getVolumeSpace().getDims(); vector voxelIndices; int64_t ind1, ind2, ind3; while (voxelFile >> ind1 >> ind2 >> ind3) { if (min(min(ind1, ind2), ind3) < 0) throw OperationException("negative voxel index found in voxel list"); if (ind1 >= volDims[0] || ind2 >= volDims[1] || ind3 >= volDims[2]) throw OperationException("found voxel index that exceeds dimension in voxel list"); voxelIndices.push_back(ind1); voxelIndices.push_back(ind2); voxelIndices.push_back(ind3); } if (!voxelFile.eof()) { voxelFile.clear(); string mystring; while (getline(voxelFile, mystring)) { if (AString(mystring.c_str()).trimmed() != "") { throw OperationException("found non-digit, non-whitespace characters: " + AString(mystring.c_str())); } } } if ((int64_t)voxelIndices.size() != sparseDims[0] * 3) throw OperationException("voxel list file contains the wrong number of voxels, expected " + AString::number(sparseDims[0] * 3) + " integers, read " + AString::number(voxelIndices.size())); vector rowReorder(sparseDims[0], -1); for (int64_t i = 0; i < (int64_t)voxelIndices.size(); i += 3) { int64_t tempInd = rowMap.getIndexForVoxel(voxelIndices.data() + i); if (tempInd != -1) { rowReorder[i / 3] = tempInd; } } CaretSparseFileWriter mywriter(outFileName, myXML);//NOTE: CaretSparseFile has a different encoding of fibers, ALWAYS use getFibersRow, etc vector indicesIn, indicesOut;//this method knows about sparseness, does sorting of indexes in order to avoid scanning full rows vector fibersIn, fibersOut;//can be slower if matrix isn't very sparse, but that is a problem for other reasons anyway CaretMinHeap myHeap;//use our heap to do heapsort, rather than coding a struct for stl sort for (int64_t i = 0; i < sparseDims[1]; ++i) { inFile.getFibersRowSparse(i, indicesIn, fibersIn); size_t numNonzero = indicesIn.size(); myHeap.reserve(numNonzero); for (size_t j = 0; j < numNonzero; ++j) { int64_t newIndex = rowReorder[indicesIn[j]];//reorder if (newIndex != -1) { myHeap.push(fibersIn[j], newIndex);//heapify } } indicesOut.resize(myHeap.size()); fibersOut.resize(myHeap.size()); int64_t curIndex = 0; while (!myHeap.isEmpty()) { int64_t newIndex; fibersOut[curIndex] = myHeap.pop(&newIndex); indicesOut[curIndex] = newIndex; ++curIndex; } mywriter.writeFibersRowSparse(i, indicesOut, fibersOut); } mywriter.finish(); } connectome-workbench-1.2.3+git41-gc4c6c90/src/Operations/OperationConvertMatrix4ToWorkbenchSparse.h000066400000000000000000000030041300200146000331330ustar00rootroot00000000000000#ifndef __OPERATION_CONVERT_MATRIX4_TO_WORKBENCH_SPARSE_H__ #define __OPERATION_CONVERT_MATRIX4_TO_WORKBENCH_SPARSE_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 "AbstractOperation.h" namespace caret { class OperationConvertMatrix4ToWorkbenchSparse : public AbstractOperation { public: static OperationParameters* getParameters(); static void useParameters(OperationParameters* myParams, ProgressObject* myProgObj); static AString getCommandSwitch(); static AString getShortDescription(); }; typedef TemplateAutoOperation AutoOperationConvertMatrix4ToWorkbenchSparse; } #endif //__OPERATION_CONVERT_MATRIX4_TO_WORKBENCH_SPARSE_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Operations/OperationConvertWarpfield.cxx000066400000000000000000000112511300200146000305520ustar00rootroot00000000000000/*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 "OperationConvertWarpfield.h" #include "OperationException.h" #include "WarpfieldFile.h" using namespace caret; using namespace std; AString OperationConvertWarpfield::getCommandSwitch() { return "-convert-warpfield"; } AString OperationConvertWarpfield::getShortDescription() { return "CONVERT A WARPFIELD BETWEEN CONVENTIONS"; } OperationParameters* OperationConvertWarpfield::getParameters() { OperationParameters* ret = new OperationParameters(); OptionalParameter* fromWorld = ret->createOptionalParameter(1, "-from-world", "input is a NIFTI 'world' warpfield"); fromWorld->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"); 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 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" + "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" + "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; } void OperationConvertWarpfield::useParameters(OperationParameters* myParams, ProgressObject* myProgObj) { LevelProgress myProgress(myProgObj); WarpfieldFile myWarp; bool haveInput = false; OptionalParameter* fromWorld = myParams->getOptionalParameter(1); OptionalParameter* fromFnirt = myParams->getOptionalParameter(2); if (fromWorld->m_present) { haveInput = true; } if (fromFnirt->m_present) { if (haveInput) throw OperationException("only one -from option may be specified"); haveInput = true; } if (!haveInput) throw OperationException("you must specify a -from option"); if (fromWorld->m_present) { myWarp.readWorld(fromWorld->getString(1)); } if (fromFnirt->m_present) { myWarp.readFnirt(fromFnirt->getString(1), fromFnirt->getString(2)); } OptionalParameter* toWorld = myParams->getOptionalParameter(3); if (toWorld->m_present) { myWarp.writeWorld(toWorld->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) { myWarp.writeFnirt(toFnirt[i]->getString(1), toFnirt[i]->getString(2)); } } connectome-workbench-1.2.3+git41-gc4c6c90/src/Operations/OperationConvertWarpfield.h000066400000000000000000000026411300200146000302020ustar00rootroot00000000000000#ifndef __OPERATION_CONVERT_WARPFIELD_H__ #define __OPERATION_CONVERT_WARPFIELD_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 "AbstractOperation.h" namespace caret { class OperationConvertWarpfield : public AbstractOperation { public: static OperationParameters* getParameters(); static void useParameters(OperationParameters* myParams, ProgressObject* myProgObj); static AString getCommandSwitch(); static AString getShortDescription(); }; typedef TemplateAutoOperation AutoOperationConvertWarpfield; } #endif //__OPERATION_CONVERT_WARPFIELD_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Operations/OperationEstimateFiberBinghams.cxx000066400000000000000000000343211300200146000314730ustar00rootroot00000000000000/*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 "OperationEstimateFiberBinghams.h" #include "OperationException.h" #include "CiftiFile.h" #include "MathFunctions.h" #include "StructureEnum.h" #include "Vector3D.h" #include "VolumeFile.h" #include #include using namespace caret; using namespace std; AString OperationEstimateFiberBinghams::getCommandSwitch() { return "-estimate-fiber-binghams"; } AString OperationEstimateFiberBinghams::getShortDescription() { return "ESTIMATE FIBER ORIENTATION DISTRIBUTIONS FROM BEDPOSTX SAMPLES"; } OperationParameters* OperationEstimateFiberBinghams::getParameters() { OperationParameters* ret = new OperationParameters(); ret->addVolumeParameter(1, "merged_f1samples", "fiber 1 strength samples"); ret->addVolumeParameter(2, "merged_th1samples", "fiber 1 theta samples"); ret->addVolumeParameter(3, "merged_ph1samples", "fiber 1 phi samples"); ret->addVolumeParameter(4, "merged_f2samples", "fiber 2 strength samples"); ret->addVolumeParameter(5, "merged_th2samples", "fiber 2 theta samples"); ret->addVolumeParameter(6, "merged_ph2samples", "fiber 2 phi samples"); ret->addVolumeParameter(7, "merged_f3samples", "fiber 3 strength samples"); ret->addVolumeParameter(8, "merged_th3samples", "fiber 3 theta samples"); ret->addVolumeParameter(9, "merged_ph3samples", "fiber 3 phi samples"); ret->addVolumeParameter(10, "label-volume", "volume of cifti structure labels"); ret->addCiftiOutputParameter(11, "cifti-out", "output cifti fiber distributons file"); AString myText = AString("This command does an estimation of a bingham distribution for each fiber orientation in each voxel which is ") + "labeled a structure identifier. These labelings come from the argument, which must have labels that match the following strings:\n"; vector myStructureEnums; StructureEnum::getAllEnums(myStructureEnums); for (int i = 0; i < (int)myStructureEnums.size(); ++i) { myText += "\n" + StructureEnum::toName(myStructureEnums[i]); } ret->setHelpText(myText); return ret; } void OperationEstimateFiberBinghams::useParameters(OperationParameters* myParams, ProgressObject* myProgObj) { LevelProgress myProgress(myProgObj); const VolumeFile* f1_samples = myParams->getVolume(1); const VolumeFile* th1_samples = myParams->getVolume(2); const VolumeFile* ph1_samples = myParams->getVolume(3); const VolumeFile* f2_samples = myParams->getVolume(4); const VolumeFile* th2_samples = myParams->getVolume(5); const VolumeFile* ph2_samples = myParams->getVolume(6); const VolumeFile* f3_samples = myParams->getVolume(7); const VolumeFile* th3_samples = myParams->getVolume(8); const VolumeFile* ph3_samples = myParams->getVolume(9); const VolumeFile* myVolLabel = myParams->getVolume(10); if (myVolLabel->getType() != SubvolumeAttributes::LABEL) { throw OperationException(" must have a label table, see -volume-label-import"); } if (!(myVolLabel->matchesVolumeSpace(f1_samples) && myVolLabel->matchesVolumeSpace(th1_samples) && myVolLabel->matchesVolumeSpace(ph1_samples) && myVolLabel->matchesVolumeSpace(f2_samples) && myVolLabel->matchesVolumeSpace(th2_samples) && myVolLabel->matchesVolumeSpace(ph2_samples) && myVolLabel->matchesVolumeSpace(f3_samples) && myVolLabel->matchesVolumeSpace(th3_samples) && myVolLabel->matchesVolumeSpace(ph3_samples))) { throw OperationException("all inputs must be in the same volume space"); } int64_t junk, numfsamp, numthsamp, numphsamp; f1_samples->getDimensions(junk, junk, junk, numfsamp, junk); th1_samples->getDimensions(junk, junk, junk, numthsamp, junk); ph1_samples->getDimensions(junk, junk, junk, numphsamp, junk); if (numfsamp != numthsamp || numfsamp != numphsamp) { throw OperationException("fiber 1 volumes have different numbers of samples"); } f2_samples->getDimensions(junk, junk, junk, numfsamp, junk); th2_samples->getDimensions(junk, junk, junk, numthsamp, junk); ph2_samples->getDimensions(junk, junk, junk, numphsamp, junk); if (numfsamp != numthsamp || numfsamp != numphsamp) { throw OperationException("fiber 2 volumes have different numbers of samples"); } f3_samples->getDimensions(junk, junk, junk, numfsamp, junk); th3_samples->getDimensions(junk, junk, junk, numthsamp, junk); ph3_samples->getDimensions(junk, junk, junk, numphsamp, junk); if (numfsamp != numthsamp || numfsamp != numphsamp) { throw OperationException("fiber 3 volumes have different numbers of samples"); } map labelMap;//maps label values to structures vector > voxelLists;//voxel lists for each volume component map componentMap;//maps structures to indexes in voxelLists const GiftiLabelTable* myLabelTable = myVolLabel->getMapLabelTable(0); vector labelKeys; myLabelTable->getKeys(labelKeys); int count = 0; for (int i = 0; i < (int)labelKeys.size(); ++i) { bool ok = false; StructureEnum::Enum thisStructure = StructureEnum::fromName(myLabelTable->getLabelName(labelKeys[i]), &ok); if (ok) { if (componentMap.find(thisStructure) == componentMap.end())//make sure we don't already have this structure from another label { labelMap[labelKeys[i]] = thisStructure; componentMap[thisStructure] = count; ++count; } } } voxelLists.resize(count); vector mydims; myVolLabel->getDimensions(mydims); for (int64_t k = 0; k < mydims[2]; ++k) { for (int64_t j = 0; j < mydims[1]; ++j) { for (int64_t i = 0; i < mydims[0]; ++i) { int myval = (int)floor(myVolLabel->getValue(i, j, k) + 0.5f); map::iterator myiter = labelMap.find(myval); if (myiter != labelMap.end()) { int whichList = componentMap.find(myiter->second)->second;//this should always find one, so we don't need to check for being end voxelLists[whichList].push_back(i); voxelLists[whichList].push_back(j); voxelLists[whichList].push_back(k); } } } } int64_t ciftiVolDims[3]; ciftiVolDims[0] = mydims[0]; ciftiVolDims[1] = mydims[1]; ciftiVolDims[2] = mydims[2]; CiftiXMLOld myXML; myXML.resetColumnsToBrainModels(); myXML.setVolumeDimsAndSForm(ciftiVolDims, myVolLabel->getSform()); for (map::iterator myiter = componentMap.begin(); myiter != componentMap.end(); ++myiter) { myXML.addVolumeModelToColumns(voxelLists[myiter->second], myiter->first); } myXML.resetRowsToScalars(24); myXML.setMapNameForRowIndex(0, "x coord"); myXML.setMapNameForRowIndex(1, "y coord"); myXML.setMapNameForRowIndex(2, "z coord"); myXML.setMapNameForRowIndex(3, "mean f1"); myXML.setMapNameForRowIndex(4, "stdev f1"); myXML.setMapNameForRowIndex(5, "theta1"); myXML.setMapNameForRowIndex(6, "phi1"); myXML.setMapNameForRowIndex(7, "ka1"); myXML.setMapNameForRowIndex(8, "kb1"); myXML.setMapNameForRowIndex(9, "psi1"); myXML.setMapNameForRowIndex(10, "mean f2"); myXML.setMapNameForRowIndex(11, "stdev f2"); myXML.setMapNameForRowIndex(12, "theta2"); myXML.setMapNameForRowIndex(13, "phi2"); myXML.setMapNameForRowIndex(14, "ka2"); myXML.setMapNameForRowIndex(15, "kb2"); myXML.setMapNameForRowIndex(16, "psi2"); myXML.setMapNameForRowIndex(17, "mean f3"); myXML.setMapNameForRowIndex(18, "stdev f3"); myXML.setMapNameForRowIndex(19, "theta3"); myXML.setMapNameForRowIndex(20, "phi3"); myXML.setMapNameForRowIndex(21, "ka3"); myXML.setMapNameForRowIndex(22, "kb3"); myXML.setMapNameForRowIndex(23, "psi3"); CiftiFile* myCifti = myParams->getOutputCifti(11); myCifti->setCiftiXML(myXML); vector volMap; CaretArray temprow(24); myXML.getVolumeMapForColumns(volMap);//we don't need to know which voxel is from which parcel int64_t end = (int64_t)volMap.size(); for (int64_t i = 0; i < end; ++i) { myVolLabel->indexToSpace(volMap[i].m_ijk, temprow);//first three elements are the coordinates estimateBingham(temprow.getArray() + 3, volMap[i].m_ijk, f1_samples, th1_samples, ph1_samples); estimateBingham(temprow.getArray() + 10, volMap[i].m_ijk, f2_samples, th2_samples, ph2_samples); estimateBingham(temprow.getArray() + 17, volMap[i].m_ijk, f3_samples, th3_samples, ph3_samples); myCifti->setRow(temprow, volMap[i].m_ciftiIndex); } } void OperationEstimateFiberBinghams::estimateBingham(float* binghamOut, const int64_t ijk[3], const VolumeFile* f_samples, const VolumeFile* theta_samples, const VolumeFile* phi_samples) { vector fdims; f_samples->getDimensions(fdims); vector samples(fdims[3]); vector fsamplearray(fdims[3]);//to do two-pass mean/stdev without using the VolumeFile function twice per sample double accum = 0.0; Vector3D accumvec;//initializes to the zero vector for (int i = 0; i < fdims[3]; ++i) { fsamplearray[i] = f_samples->getValue(ijk, i); accum += fsamplearray[i]; float theta = theta_samples->getValue(ijk, i);//these have already been checked to have the same number of bricks float phi = phi_samples->getValue(ijk, i); samples[i][0] = -sin(theta) * cos(phi);//NOTE: theta, phi are polar coordinates for a RADIOLOGICAL volume, so flip x so that +x = right samples[i][1] = sin(theta) * sin(phi);//ignore the f values for directionality testing samples[i][2] = cos(theta);//beware, samples may be the negative of other samples, BAS model doesn't care, and they may have restricted the samples to +z or something if (i != 0) { if (accumvec.dot(samples[i]) < 0.0f)//invert the ones that have a negative dot product with the current accumulated vector, and hope the first samples don't have a spread of more than 90 degrees { samples[i] = -samples[i]; } } accumvec += samples[i]; } accumvec = accumvec.normal();//normalize the average direction, trust this to make all components in [-1, 1] binghamOut[0] = accum / fdims[3];//the mean value of f accum = 0.0; for (int i = 0; i < fdims[3]; ++i) { float temp = fsamplearray[i] - binghamOut[0]; accum += temp * temp; } binghamOut[1] = sqrt(accum / (fdims[3] - 1));//sample standard deviation float theta = acos(accumvec[2]);//[0, pi] float phi = atan2(accumvec[1], -accumvec[0]);//[-pi, pi] - NOTE: radiological polar strikes again if (phi < 0.0f) phi += 2 * 3.14159265358979;//[0, 2pi] binghamOut[2] = theta; binghamOut[3] = phi; Vector3D xhat, yhat;//vectors to project to the normal plane of the average direction, assuming psi=0 xhat[0] = cos(theta) * cos(phi);//more radiological polar to neurological euclidean stuff xhat[1] = -cos(theta) * sin(phi); xhat[2] = sin(theta); yhat[0] = sin(phi); yhat[1] = cos(phi); yhat[2] = 0.0f; vector x_samples(fdims[3]), y_samples(fdims[3]); float dist = -1.0f; int end1 = -1, end2 = -1; for (int i = 0; i < fdims[3]; ++i)//first endpoint is farthest from mean direction { x_samples[i] = samples[i].dot(xhat); y_samples[i] = samples[i].dot(yhat); float tempf = x_samples[i] * x_samples[i] + y_samples[i] * y_samples[i]; if (tempf > dist) { end1 = i; dist = tempf; } } dist = -1.0f; for (int i = 0; i < fdims[3]; ++i)//second endpoint is farthest from first endpoint { float xdiff = x_samples[i] - x_samples[end1]; float ydiff = y_samples[i] - y_samples[end1]; float tempf = xdiff * xdiff + ydiff * ydiff; if (tempf > dist) { end2 = i; dist = tempf; } }//NOTE: the MAJOR axis of fanning is along y when psi = 0! ka > kb means kb is in the direction of more fanning float psi = atan((x_samples[end2] - x_samples[end1]) / (y_samples[end2] - y_samples[end1]));//[-pi/2, pi/2] - TODO: check radiological psi orientation if (!MathFunctions::isNumeric(psi)) { const float LARGE_K = 1800.0f;//stdev of 1/60, typical maximum ka in a voxel is around 200 binghamOut[4] = LARGE_K; binghamOut[5] = LARGE_K; binghamOut[6] = 0; return; } if (psi < 0) psi += 3.14159265358979;//[0, pi] binghamOut[6] = psi; double accumx = 0.0, accumy = 0.0; for (int i = 0; i < fdims[3]; ++i) {//rotate the samples through -psi on the plane to orient them to the axes float newx = x_samples[i] * cos(psi) + y_samples[i] * -sin(psi);//NOTE: again, radiological psi? float newy = x_samples[i] * sin(psi) + y_samples[i] * cos(psi); accumx += newx * newx;//assume mean of zero, because we already chose the center of the distribution accumy += newy * newy; } float ka = (fdims[3] - 1) / (2 * accumx);//convert variance to concentrations float kb = (fdims[3] - 1) / (2 * accumy);//but they aren't negative binghamOut[4] = ka; binghamOut[5] = kb; } connectome-workbench-1.2.3+git41-gc4c6c90/src/Operations/OperationEstimateFiberBinghams.h000066400000000000000000000032021300200146000311120ustar00rootroot00000000000000#ifndef __OPERATION_ESTIMATE_FIBER_BINGHAMS_H__ #define __OPERATION_ESTIMATE_FIBER_BINGHAMS_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 "AbstractOperation.h" namespace caret { class OperationEstimateFiberBinghams : public AbstractOperation { static void estimateBingham(float* binghamOut, const int64_t ijk[3], const caret::VolumeFile* f_samples, const caret::VolumeFile* theta_samples, const caret::VolumeFile* phi_samples); public: static OperationParameters* getParameters(); static void useParameters(OperationParameters* myParams, ProgressObject* myProgObj); static AString getCommandSwitch(); static AString getShortDescription(); }; typedef TemplateAutoOperation AutoOperationEstimateFiberBinghams; } #endif //__OPERATION_ESTIMATE_FIBER_BINGHAMS_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Operations/OperationException.cxx000066400000000000000000000042431300200146000272350ustar00rootroot00000000000000/*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 "OperationException.h" #include using namespace caret; /** * Constructor. * */ OperationException::OperationException() : CaretException() { this->initializeMembersOperationException(); } /** * Constructor that uses stack trace from the exception * passed in as a parameter. * * @param e Any exception whose stack trace becomes * this exception's stack trace. * */ OperationException::OperationException( const CaretException& e) : CaretException(e) { this->initializeMembersOperationException(); } /** * Constructor. * * @param s Description of the exception. * */ OperationException::OperationException(const AString& s) : CaretException(s) { this->initializeMembersOperationException(); } /** * Copy Constructor. * @param e * Exception that is copied. */ OperationException::OperationException(const OperationException& e) : CaretException(e) { } /** * Assignment operator. * @param e * Exception that is copied. * @return * Copy of the exception. */ OperationException& OperationException::operator=(const OperationException& e) { if (this != &e) { CaretException::operator=(e); } return *this; } /** * Destructor */ OperationException::~OperationException() throw() { } void OperationException::initializeMembersOperationException() { } connectome-workbench-1.2.3+git41-gc4c6c90/src/Operations/OperationException.h000066400000000000000000000027421300200146000266640ustar00rootroot00000000000000#ifndef __OPERATION_EXCEPTION_H__ #define __OPERATION_EXCEPTION_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 "CaretException.h" namespace caret { /// An exception thrown during command processing. class OperationException : public CaretException { public: OperationException(); OperationException(const CaretException& e); OperationException(const AString& s); OperationException(const OperationException& e); OperationException& operator=(const OperationException& e); virtual ~OperationException() throw(); private: void initializeMembersOperationException(); }; } // namespace #endif // __OPERATION_EXCEPTION_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Operations/OperationFileConvert.cxx000066400000000000000000000276231300200146000275260ustar00rootroot00000000000000/*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 "OperationFileConvert.h" #include "OperationException.h" #include "BorderFile.h" #include "Border.h" #include "CaretLogger.h" #include "CiftiFile.h" #include "FileInformation.h" #include "MultiDimIterator.h" #include "NiftiIO.h" #include "SurfaceFile.h" using namespace caret; using namespace std; AString OperationFileConvert::getCommandSwitch() { return "-file-convert"; } AString OperationFileConvert::getShortDescription() { return "CHANGE VERSION OF FILE FORMAT"; } OperationParameters* OperationFileConvert::getParameters() { OperationParameters* ret = new OperationParameters(); OptionalParameter* borderConv = ret->createOptionalParameter(1, "-border-version-convert", "write a border file with a different version"); borderConv->addBorderParameter(1, "border-in", "the input border file"); borderConv->addIntegerParameter(2, "out-version", "the format version to write as, 1 or 3 (2 doesn't exist)"); borderConv->addStringParameter(3, "border-out", "output - the output border file");//fake the output formatting, the auto-output code will use whatever version it wants OptionalParameter* borderSurfOpt = borderConv->createOptionalParameter(4, "-surface", "must be specified if the input is version 1"); borderSurfOpt->addSurfaceParameter(1, "surface", "use this surface file for structure and number of vertices, ignore borders on other structures"); OptionalParameter* niftiConv = ret->createOptionalParameter(2, "-nifti-version-convert", "write a nifti file with a different version"); niftiConv->addStringParameter(1, "input", "the input nifti file");//VolumeFile isn't "generic nifti", so we use NiftiIO directly niftiConv->addIntegerParameter(2, "version", "the nifti version to write as"); niftiConv->addStringParameter(3, "output", "output - the output nifti file");//fake the output formatting, we don't have a "generic nifti" type OptionalParameter* ciftiConv = ret->createOptionalParameter(3, "-cifti-version-convert", "write a cifti file with a different version"); ciftiConv->addCiftiParameter(1, "cifti-in", "the input cifti file"); ciftiConv->addStringParameter(2, "version", "the cifti version to write as"); ciftiConv->addStringParameter(3, "cifti-out", "output - the output cifti file");//fake the output formatting so we can just call writeFile and be done with it (and also not add a layer of provenance) ret->setHelpText( AString("You may only specify one top-level option.") ); return ret; } namespace //hide helpers in a file-specific namespace { //hidden templated function to do generic nifti reading and writing template void niftiConvertHelper(NiftiIO& inputIO, const AString& outFileName, const int64_t& maxMem, const int& outVer, const bool& collision) { const vector dims = inputIO.getDimensions();//DO NOT make this a reference if (collision) { int64_t totalBytes = (int)sizeof(T) * inputIO.getNumComponents(), totalElems = inputIO.getNumComponents();//compute memory usage to check whether to warn for (int fullDims = 0; fullDims < (int)dims.size(); ++fullDims) { totalBytes *= dims[fullDims]; totalElems *= dims[fullDims]; } if (totalBytes > maxMem) { CaretLogInfo("collision between input and output filenames, reading input file into memory"); } else { CaretLogFine("collision between input and output filenames, reading input file into memory"); } vector scratchmem(totalElems); inputIO.readData(scratchmem.data(), dims.size(), vector()); inputIO.close();//don't have it try to close after being overwritten NiftiIO outputIO;//now we can open the output file outputIO.writeNew(outFileName, inputIO.getHeader(), outVer);//NOTE: this keeps data scaling fields, data type, extensions, header fields we ignore, etc outputIO.writeData(scratchmem.data(), dims.size(), vector()); } else { NiftiIO outputIO; outputIO.writeNew(outFileName, inputIO.getHeader(), outVer);//NOTE: this keeps data scaling fields, data type, extensions, header fields we ignore, etc int64_t totalBytes = (int)sizeof(T) * inputIO.getNumComponents(), totalElems = inputIO.getNumComponents(); int fullDims = 0; for (; fullDims < (int)dims.size() && totalBytes * dims[fullDims] < maxMem; ++fullDims) { totalBytes *= dims[fullDims]; totalElems *= dims[fullDims]; } vector remainDims(dims.begin() + fullDims, dims.end()); vector scratchmem(totalElems);//this is the main purpose of the template... for (MultiDimIterator myiter(remainDims); !myiter.atEnd(); ++myiter) { inputIO.readData(scratchmem.data(), fullDims, *myiter);//...which results in these templating over the desired type outputIO.writeData(scratchmem.data(), fullDims, *myiter); } } } } void OperationFileConvert::useParameters(OperationParameters* myParams, ProgressObject* myProgObj) { LevelProgress myProgress(myProgObj); OptionalParameter* borderConv = myParams->getOptionalParameter(1); OptionalParameter* niftiConv = myParams->getOptionalParameter(2); OptionalParameter* ciftiConv = myParams->getOptionalParameter(3); int numChosen = 0; if (borderConv->m_present) ++numChosen; if (niftiConv->m_present) ++numChosen; if (ciftiConv->m_present) ++numChosen; if (numChosen != 1) throw OperationException("you must choose exactly one top level option"); if (borderConv->m_present) { const BorderFile* borderIn = borderConv->getBorder(1); int outVersion = (int)borderConv->getInteger(2); AString outFilename = borderConv->getString(3); BorderFile borderOut; int numBorders = borderIn->getNumberOfBorders(); if (borderIn->getNumberOfNodes() < 1)//version 1 border file { OptionalParameter* surfOpt = borderConv->getOptionalParameter(4); if (!surfOpt->m_present) throw OperationException("version 1 border files require the -surface suboption for conversion"); SurfaceFile* convSurf = surfOpt->getSurface(1); StructureEnum::Enum myStructure = convSurf->getStructure(); borderOut.setStructure(myStructure); borderOut.setNumberOfNodes(convSurf->getNumberOfNodes()); for (int i = 0; i < numBorders; ++i) { const Border* thisBorder = borderIn->getBorder(i); if (thisBorder->getStructure() == myStructure) { borderOut.addBorder(new Border(*thisBorder));//takes ownership of RAW POINTER } } } else { borderOut.setStructure(borderIn->getStructure()); borderOut.setNumberOfNodes(borderIn->getNumberOfNodes()); for (int i = 0; i < numBorders; ++i) { borderOut.addBorder(new Border(*borderIn->getBorder(i)));//ditto } } borderOut.writeFile(outFilename, outVersion); } if (niftiConv->m_present) { AString inFileName = niftiConv->getString(1); int outVer = (int)niftiConv->getInteger(2); AString outFileName = niftiConv->getString(3); const int64_t maxMem = 1<<29;//half gigabyte - will usually be much less, could be an option NiftiIO inputIO; inputIO.openRead(inFileName); const NiftiHeader& inHeader = inputIO.getHeader(); FileInformation outInfo(outFileName), inInfo(inFileName); bool collision = false; if (outInfo.getCanonicalFilePath() != "" && outInfo.getCanonicalFilePath() == inInfo.getCanonicalFilePath()) {//collision! can't do on-disk reading during writing collision = true; } double mult, offset;//if the offset is large compared to the scale, it is nontrivial to deduce a suitable type to preserve sufficient precision to round back to the unscaled input values if (inHeader.getDataScaling(mult, offset))//we can't use the on-disk type because we don't provide access to the unscaled (wrong) values {//alternatively, we could give read/write access to the unscaled values, but that is distasteful niftiConvertHelper(inputIO, outFileName, maxMem, outVer, collision);//so, always use long double to reduce rounding problems, even if it is often massive overkill } else { switch (inHeader.getDataType()) { case NIFTI_TYPE_UINT8: case NIFTI_TYPE_RGB24: niftiConvertHelper(inputIO, outFileName, maxMem, outVer, collision); break; case NIFTI_TYPE_INT8: niftiConvertHelper(inputIO, outFileName, maxMem, outVer, collision); break; case NIFTI_TYPE_UINT16: niftiConvertHelper(inputIO, outFileName, maxMem, outVer, collision); break; case NIFTI_TYPE_INT16: niftiConvertHelper(inputIO, outFileName, maxMem, outVer, collision); break; case NIFTI_TYPE_UINT32: niftiConvertHelper(inputIO, outFileName, maxMem, outVer, collision); break; case NIFTI_TYPE_INT32: niftiConvertHelper(inputIO, outFileName, maxMem, outVer, collision); break; case NIFTI_TYPE_UINT64: niftiConvertHelper(inputIO, outFileName, maxMem, outVer, collision); break; case NIFTI_TYPE_INT64: niftiConvertHelper(inputIO, outFileName, maxMem, outVer, collision); break; case NIFTI_TYPE_FLOAT32: case NIFTI_TYPE_COMPLEX64: niftiConvertHelper(inputIO, outFileName, maxMem, outVer, collision); break; case NIFTI_TYPE_FLOAT64: case NIFTI_TYPE_COMPLEX128: niftiConvertHelper(inputIO, outFileName, maxMem, outVer, collision); break; case NIFTI_TYPE_FLOAT128: case NIFTI_TYPE_COMPLEX256: niftiConvertHelper(inputIO, outFileName, maxMem, outVer, collision); break; default: throw OperationException("unsupported nifti datatype"); } } } if (ciftiConv->m_present) { CiftiFile* ciftiIn = ciftiConv->getCifti(1); AString versionString = ciftiConv->getString(2); AString outFileName = ciftiConv->getString(3); ciftiIn->writeFile(outFileName, CiftiVersion(versionString));//also handles complications like writing to the same file as it is set to read on-disk from } } connectome-workbench-1.2.3+git41-gc4c6c90/src/Operations/OperationFileConvert.h000066400000000000000000000026031300200146000271420ustar00rootroot00000000000000#ifndef __OPERATION_FILE_CONVERT_H__ #define __OPERATION_FILE_CONVERT_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 "AbstractOperation.h" namespace caret { class OperationFileConvert : public AbstractOperation { public: static OperationParameters* getParameters(); static void useParameters(OperationParameters* myParams, ProgressObject* myProgObj); static AString getCommandSwitch(); static AString getShortDescription(); }; typedef TemplateAutoOperation AutoOperationFileConvert; } #endif //__OPERATION_FILE_CONVERT_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Operations/OperationFileInformation.cxx000066400000000000000000000164101300200146000303630ustar00rootroot00000000000000 /*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 "CaretAssert.h" #include "CaretLogger.h" #include "CaretDataFile.h" #include "CaretDataFileHelper.h" #include "CaretMappableDataFile.h" #include "CaretPointer.h" #include "CiftiMappableDataFile.h" #include "DataFileContentInformation.h" #include "DataFileException.h" #include "GiftiMetaData.h" #include "OperationFileInformation.h" #include "OperationException.h" using namespace caret; using namespace std; /** * \class caret::OperationFileInformation * \brief List information about a file's content */ /** * @return Command line switch */ AString OperationFileInformation::getCommandSwitch() { return "-file-information"; } /** * @return Short description of operation */ AString OperationFileInformation::getShortDescription() { return "LIST INFORMATION ABOUT A FILE'S CONTENT"; } /** * @return Parameters for operation */ OperationParameters* OperationFileInformation::getParameters() { OperationParameters* ret = new OperationParameters(); ret->addStringParameter(1, "data-file", "data file"); ret->createOptionalParameter(2, "-no-map-info", "do not show map information for files that support maps"); ret->createOptionalParameter(3, "-only-step-interval", "suppress normal output, print the interval between maps"); ret->createOptionalParameter(4, "-only-number-of-maps", "suppress normal output, print the number of maps"); ret->createOptionalParameter(5, "-only-map-names", "suppress normal output, print the names of all maps"); OptionalParameter* metadataOpt = ret->createOptionalParameter(6, "-only-metadata", "suppress normal output, print file metadata"); OptionalParameter* mdKeyOpt = metadataOpt->createOptionalParameter(1, "-key", "only print the metadata for one key, with no formatting"); mdKeyOpt->addStringParameter(1, "key", "the metadata key"); 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."); ret->setHelpText(helpText); return ret; } /** * Use Parameters and perform operation */ void OperationFileInformation::useParameters(OperationParameters* myParams, ProgressObject* myProgObj) { LevelProgress myProgress(myProgObj); const AString dataFileName = myParams->getString(1); const bool showMapInformationFlag = ! (myParams->getOptionalParameter(2)->m_present); int countOnlys = 0; bool onlyTimestep = myParams->getOptionalParameter(3)->m_present; if (onlyTimestep) ++countOnlys; bool onlyNumMaps = myParams->getOptionalParameter(4)->m_present; if (onlyNumMaps) ++countOnlys; bool onlyMapNames = myParams->getOptionalParameter(5)->m_present; if (onlyMapNames) ++countOnlys; OptionalParameter* metadataOpt = myParams->getOptionalParameter(6); bool onlyMetadata = metadataOpt->m_present; AString mdKey = ""; if (onlyMetadata) { ++countOnlys; OptionalParameter* mdKeyOpt = metadataOpt->getOptionalParameter(1); if (mdKeyOpt->m_present) { mdKey = mdKeyOpt->getString(1); if (mdKey == "") { throw OperationException(" must not be empty"); } } } if (countOnlys > 1) throw OperationException("only one -only-* option may be specified"); bool preferOnDisk = (!showMapInformationFlag || countOnlys != 0); CaretPointer caretDataFile; caretDataFile.grabNew(CaretDataFileHelper::readAnyCaretDataFile(dataFileName, preferOnDisk)); //readAnyCaretDataFile now handles cifti files with the wrong extension if (onlyTimestep) { CaretMappableDataFile* mappableFile = dynamic_cast(caretDataFile.getPointer()); if (mappableFile == NULL) throw OperationException("file does not support maps");//TODO: also give error on things that it doesn't make sense on if (mappableFile->getMapIntervalUnits() == NiftiTimeUnitsEnum::NIFTI_UNITS_UNKNOWN) throw OperationException("file does not support series data"); float start, step; mappableFile->getMapIntervalStartAndStep(start, step); cout << step << endl; } if (onlyNumMaps) { CaretMappableDataFile* mappableFile = dynamic_cast(caretDataFile.getPointer()); if (mappableFile == NULL) throw OperationException("file does not support maps");//TODO: also give error on things that it doesn't make sense on int numMaps = mappableFile->getNumberOfMaps(); if (numMaps < 1) throw OperationException("file does not support maps"); cout << numMaps << endl; } if (onlyMapNames) { CaretMappableDataFile* mappableFile = dynamic_cast(caretDataFile.getPointer()); if (mappableFile == NULL) throw OperationException("file does not support maps");//TODO: also give error on things that it doesn't make sense on int numMaps = mappableFile->getNumberOfMaps(); if (numMaps < 1) throw OperationException("file does not support maps"); for (int i = 0; i < numMaps; ++i) { cout << mappableFile->getMapName(i) << endl; } } if (onlyMetadata) { const GiftiMetaData* myMD = caretDataFile->getFileMetaData(); if (mdKey == "") { const map mdMap = myMD->getAsMap(); for (map::const_iterator iter = mdMap.begin(); iter != mdMap.end(); ++iter) { cout << " " << iter->first << ":" << endl; cout << iter->second << endl << endl; } } else { if (!myMD->exists(mdKey)) { throw OperationException("specified metadata key is not present in the file"); } cout << myMD->get(mdKey) << endl; } } if (countOnlys == 0) { DataFileContentInformation dataFileContentInformation; dataFileContentInformation.setOptionFlag(DataFileContentInformation::OPTION_SHOW_MAP_INFORMATION, showMapInformationFlag); caretDataFile->addToDataFileContentInformation(dataFileContentInformation); cout << qPrintable(dataFileContentInformation.getInformationInString()) << endl; } } connectome-workbench-1.2.3+git41-gc4c6c90/src/Operations/OperationFileInformation.h000066400000000000000000000027131300200146000300110ustar00rootroot00000000000000#ifndef __OPERATION_FILE_INFORMATION_H__ #define __OPERATION_FILE_INFORMATION_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 "AbstractOperation.h" namespace caret { class OperationFileInformation : public AbstractOperation { public: static OperationParameters* getParameters(); static void useParameters(OperationParameters* myParams, ProgressObject* myProgObj); static AString getCommandSwitch(); static AString getShortDescription(); }; typedef TemplateAutoOperation AutoOperationFileInformation; } // namespace #endif //__OPERATION_FILE_INFORMATION_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Operations/OperationFociGetProjectionVertex.cxx000066400000000000000000000136321300200146000320540ustar00rootroot00000000000000/*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 "OperationFociGetProjectionVertex.h" #include "OperationException.h" #include "FociFile.h" #include "Focus.h" #include "MetricFile.h" #include "SurfaceFile.h" #include "SurfaceProjectedItem.h" #include "SurfaceProjectionBarycentric.h" #include "SurfaceProjectionVanEssen.h" using namespace caret; using namespace std; AString OperationFociGetProjectionVertex::getCommandSwitch() { return "-foci-get-projection-vertex"; } AString OperationFociGetProjectionVertex::getShortDescription() { return "GET PROJECTION VERTEX FOR FOCI"; } OperationParameters* OperationFociGetProjectionVertex::getParameters() { OperationParameters* ret = new OperationParameters(); ret->addFociParameter(1, "foci", "the foci file"); ret->addSurfaceParameter(2, "surface", "the surface related to the foci file"); ret->addMetricOutputParameter(3, "metric-out", "the output metric file"); OptionalParameter* nameOpt = ret->createOptionalParameter(4, "-name", "select a focus by name"); nameOpt->addStringParameter(1, "name", "the name of the focus"); ret->setHelpText( AString("For each focus, a column is created in , and the vertex with the most influence on its projection ") + "is assigned a value of 1 in that column, with all other vertices 0. " + "If -name is used, only one focus will be used." ); return ret; } void OperationFociGetProjectionVertex::useParameters(OperationParameters* myParams, ProgressObject* myProgObj) { LevelProgress myProgress(myProgObj); const FociFile* myFoci = myParams->getFoci(1); const SurfaceFile* mySurf = myParams->getSurface(2); MetricFile* myMetricOut = myParams->getOutputMetric(3); OptionalParameter* nameOpt = myParams->getOptionalParameter(4); int numFoci = myFoci->getNumberOfFoci(); if (numFoci < 1) throw OperationException("Foci file has no foci"); int numNodes = mySurf->getNumberOfNodes(); if (nameOpt->m_present) { AString name = nameOpt->getString(1); myMetricOut->setNumberOfNodesAndColumns(numNodes, 1); myMetricOut->setStructure(mySurf->getStructure()); bool found = false; for (int i = 0; i < numFoci; ++i) { const Focus* myFocus = myFoci->getFocus(i); if (name == myFocus->getName()) { found = true; if (myFocus->getNumberOfProjections() < 1) throw OperationException("matching focus has no projections"); const SurfaceProjectedItem* firstItem = myFocus->getProjection(0); int whichNode = -1; if (firstItem->getBarycentricProjection()->isValid()) { const float* areas = firstItem->getBarycentricProjection()->getTriangleAreas(); const int32_t* nodes = firstItem->getBarycentricProjection()->getTriangleNodes(); int pos = 0; for (int i = 1; i < 3; ++i) { if (areas[i] > areas[pos]) pos = i; } whichNode = nodes[pos]; } else { throw OperationException("barycentric projection not valid");//someone else can worry about van essen projection, if it is needed } if (whichNode >= numNodes) throw OperationException("foci file has larger node numbers than surface"); myMetricOut->initializeColumn(0, 0.0f); myMetricOut->setColumnName(0, myFocus->getName()); myMetricOut->setValue(whichNode, 0, 1.0f); break; } } if (!found) throw OperationException("specified name did not match any foci"); } else { myMetricOut->setNumberOfNodesAndColumns(numNodes, numFoci); myMetricOut->setStructure(mySurf->getStructure()); for (int i = 0; i < numFoci; ++i) { const Focus* myFocus = myFoci->getFocus(i); if (myFocus->getNumberOfProjections() < 1) throw OperationException("matching focus has no projections"); const SurfaceProjectedItem* firstItem = myFocus->getProjection(0); int whichNode = -1; if (firstItem->getBarycentricProjection()->isValid()) { const float* areas = firstItem->getBarycentricProjection()->getTriangleAreas(); const int32_t* nodes = firstItem->getBarycentricProjection()->getTriangleNodes(); int pos = 0; for (int i = 1; i < 3; ++i) { if (areas[i] > areas[pos]) pos = i; } whichNode = nodes[pos]; } else { throw OperationException("barycentric projection not valid");//someone else can worry about van essen projection, if it is needed } if (whichNode >= numNodes) throw OperationException("foci file has larger node numbers than surface"); myMetricOut->initializeColumn(i, 0.0f); myMetricOut->setColumnName(i, myFocus->getName()); myMetricOut->setValue(whichNode, i, 1.0f); } } } connectome-workbench-1.2.3+git41-gc4c6c90/src/Operations/OperationFociGetProjectionVertex.h000066400000000000000000000027211300200146000314760ustar00rootroot00000000000000#ifndef __OPERATION_FOCI_GET_PROJECTION_VERTEX_H__ #define __OPERATION_FOCI_GET_PROJECTION_VERTEX_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 "AbstractOperation.h" namespace caret { class OperationFociGetProjectionVertex : public AbstractOperation { public: static OperationParameters* getParameters(); static void useParameters(OperationParameters* myParams, ProgressObject* myProgObj); static AString getCommandSwitch(); static AString getShortDescription(); }; typedef TemplateAutoOperation AutoOperationFociGetProjectionVertex; } #endif //__OPERATION_FOCI_GET_PROJECTION_VERTEX_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Operations/OperationFociListCoords.cxx000066400000000000000000000062341300200146000301670ustar00rootroot00000000000000/*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 "OperationFociListCoords.h" #include "OperationException.h" #include "FociFile.h" #include "Focus.h" #include using namespace caret; using namespace std; AString OperationFociListCoords::getCommandSwitch() { return "-foci-list-coords"; } AString OperationFociListCoords::getShortDescription() { return "OUTPUT FOCI COORDINATES IN A TEXT FILE"; } OperationParameters* OperationFociListCoords::getParameters() { OperationParameters* ret = new OperationParameters(); ret->addFociParameter(1, "foci-file", "input foci file"); ret->addStringParameter(2, "coord-file-out", "output - the output coordinate text file"); OptionalParameter* namesOpt = ret->createOptionalParameter(3, "-names-out", "output the foci names"); namesOpt->addStringParameter(1, "names-file-out", "output - text file to put foci names in"); ret->setHelpText( AString("Output the coordinates for every focus in the foci file, and optionally the focus names in a second text file.") ); return ret; } void OperationFociListCoords::useParameters(OperationParameters* myParams, ProgressObject* myProgObj) { LevelProgress myProgress(myProgObj); FociFile* myFoci = myParams->getFoci(1);//gets the surface with key 1 AString coordFileName = myParams->getString(2); OptionalParameter* namesOpt = myParams->getOptionalParameter(3); bool outputNames = false; AString nameFileName; if (namesOpt->m_present) { outputNames = true; nameFileName = namesOpt->getString(1); } ofstream coordFile(coordFileName.toLocal8Bit().constData()); if (!coordFile.good()) { throw OperationException("failed to open coordinate output file for writing"); } fstream nameFile; if (outputNames) { nameFile.open(nameFileName.toLocal8Bit().constData(), fstream::out); if (!nameFile.good()) { throw OperationException("failed to open name output file for writing"); } } int numFoci = myFoci->getNumberOfFoci(); for (int i = 0; i < numFoci; ++i) { const Focus* myFocus = myFoci->getFocus(i); const float* coords = myFocus->getSearchXYZ(); coordFile << coords[0] << " " << coords[1] << " " << coords[2] << endl; if (outputNames) { nameFile << myFocus->getName() << endl; } } } connectome-workbench-1.2.3+git41-gc4c6c90/src/Operations/OperationFociListCoords.h000066400000000000000000000026301300200146000276100ustar00rootroot00000000000000#ifndef __OPERATION_FOCI_LIST_COORDS_H__ #define __OPERATION_FOCI_LIST_COORDS_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 "AbstractOperation.h" namespace caret { class OperationFociListCoords : public AbstractOperation { public: static OperationParameters* getParameters(); static void useParameters(OperationParameters* myParams, ProgressObject* myProgObj); static AString getCommandSwitch(); static AString getShortDescription(); }; typedef TemplateAutoOperation AutoOperationFociListCoords; } #endif //__OPERATION_FOCI_LIST_COORDS_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Operations/OperationGiftiConvert.cxx000066400000000000000000000051661300200146000277070ustar00rootroot00000000000000/*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 "OperationGiftiConvert.h" #include "OperationException.h" #include "GiftiFile.h" using namespace caret; using namespace std; AString OperationGiftiConvert::getCommandSwitch() { return "-gifti-convert"; } AString OperationGiftiConvert::getShortDescription() { return "CONVERT A GIFTI FILE TO A DIFFERENT ENCODING"; } OperationParameters* OperationGiftiConvert::getParameters() { OperationParameters* ret = new OperationParameters(); ret->addStringParameter(1, "gifti-encoding", "what the output encoding should be"); ret->addStringParameter(2, "input-gifti-file", "the input gifti file"); ret->addStringParameter(3, "output-gifti-file", "output - the output gifti file");//HACK: fake the output formatting ret->setHelpText( AString("The value of must be one of the following:\n\n") + "ASCII\nBASE64_BINARY\nGZIP_BASE64_BINARY\nEXTERNAL_FILE_BINARY" ); return ret; } void OperationGiftiConvert::useParameters(OperationParameters* myParams, ProgressObject* myProgObj) { LevelProgress myProgress(myProgObj); AString encodingStr = myParams->getString(1); AString inputName = myParams->getString(2); AString outputName = myParams->getString(3); bool isValidEncoding = false; GiftiEncodingEnum::Enum encoding = GiftiEncodingEnum::fromName(encodingStr, &isValidEncoding); if (isValidEncoding == false) { throw OperationException("Specified GIFTI Encoding is invalid."); } if (inputName.isEmpty()) { throw OperationException("Input GIFTI file name is empty."); } if (outputName.isEmpty()) { throw OperationException("Output GIFTI file name is empty."); } GiftiFile gf; gf.readFile(inputName); gf.setEncodingForWriting(encoding); gf.writeFile(outputName); } connectome-workbench-1.2.3+git41-gc4c6c90/src/Operations/OperationGiftiConvert.h000066400000000000000000000026111300200146000273240ustar00rootroot00000000000000#ifndef __OPERATION_GIFTI_CONVERT_H__ #define __OPERATION_GIFTI_CONVERT_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 OperationGiftiConvert : public AbstractOperation { public: static OperationParameters* getParameters(); static void useParameters(OperationParameters* myParams, ProgressObject* myProgObj); static AString getCommandSwitch(); static AString getShortDescription(); }; typedef TemplateAutoOperation AutoOperationGiftiConvert; } #endif //__OPERATION_GIFTI_CONVERT_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Operations/OperationLabelExportTable.cxx000066400000000000000000000060721300200146000304720ustar00rootroot00000000000000/*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 "OperationLabelExportTable.h" #include "OperationException.h" #include "GiftiLabel.h" #include "GiftiLabelTable.h" #include "LabelFile.h" #include #include using namespace caret; using namespace std; AString OperationLabelExportTable::getCommandSwitch() { return "-label-export-table"; } AString OperationLabelExportTable::getShortDescription() { return "EXPORT LABEL TABLE FROM GIFTI AS TEXT"; } OperationParameters* OperationLabelExportTable::getParameters() { OperationParameters* ret = new OperationParameters(); ret->addLabelParameter(1, "label-in", "the input label file"); ret->addStringParameter(2, "table-out", "output - the output text file");//fake output formatting ret->setHelpText( AString("Takes the label table from the gifti label file, and writes it to a text format matching what is expected by -metric-label-import.") ); return ret; } int OperationLabelExportTable::floatTo255(const float& in) { return (int)floor(in * 255.0f + 0.5f); } void OperationLabelExportTable::useParameters(OperationParameters* myParams, ProgressObject* myProgObj) { LevelProgress myProgress(myProgObj); LabelFile* myLabel = myParams->getLabel(1); AString outName = myParams->getString(2); ofstream outFile(outName.toLocal8Bit().constData()); if (!outFile) throw OperationException("failed to open output text file"); const GiftiLabelTable* myTable = myLabel->getLabelTable(); set allKeys = myTable->getKeys(); int32_t unassignedKey = myTable->getUnassignedLabelKey(); for (set::iterator iter = allKeys.begin(); iter != allKeys.end(); ++iter) { if (*iter == unassignedKey) continue;//don't output the unused key, because import doesn't want it in the text file const GiftiLabel* thisLabel = myTable->getLabel(*iter); outFile << thisLabel->getName() << endl; outFile << thisLabel->getKey() << " " << floatTo255(thisLabel->getRed()) << " " << floatTo255(thisLabel->getGreen()) << " " << floatTo255(thisLabel->getBlue()) << " " << floatTo255(thisLabel->getAlpha()) << endl; if (!outFile) throw OperationException("error writing to output text file"); } } connectome-workbench-1.2.3+git41-gc4c6c90/src/Operations/OperationLabelExportTable.h000066400000000000000000000027241300200146000301170ustar00rootroot00000000000000#ifndef __OPERATION_LABEL_EXPORT_TABLE_H__ #define __OPERATION_LABEL_EXPORT_TABLE_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 "AbstractOperation.h" namespace caret { class OperationLabelExportTable : public AbstractOperation { static int floatTo255(const float& in); public: static OperationParameters* getParameters(); static void useParameters(OperationParameters* myParams, ProgressObject* myProgObj); static AString getCommandSwitch(); static AString getShortDescription(); }; typedef TemplateAutoOperation AutoOperationLabelExportTable; } #endif //__OPERATION_LABEL_EXPORT_TABLE_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Operations/OperationLabelMask.cxx000066400000000000000000000112221300200146000271250ustar00rootroot00000000000000/*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 "OperationLabelMask.h" #include "OperationException.h" #include "GiftiLabelTable.h" #include "LabelFile.h" #include "MetricFile.h" using namespace caret; using namespace std; AString OperationLabelMask::getCommandSwitch() { return "-label-mask"; } AString OperationLabelMask::getShortDescription() { return "MASK A LABEL FILE"; } OperationParameters* OperationLabelMask::getParameters() { OperationParameters* ret = new OperationParameters(); ret->addLabelParameter(1, "label", "the label file to mask"); ret->addMetricParameter(2, "mask", "the mask metric"); ret->addLabelOutputParameter(3, "label-out", "the output label file"); 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. ") + "if -column is specified, the output contains only one column, the masked version of the specified input column." ); return ret; } void OperationLabelMask::useParameters(OperationParameters* myParams, ProgressObject* myProgObj) { LevelProgress myProgress(myProgObj); LabelFile* myLabel = myParams->getLabel(1); MetricFile* myMask = myParams->getMetric(2); int32_t numNodes = myLabel->getNumberOfNodes(), numCols = myLabel->getNumberOfColumns(); if (myMask->getNumberOfNodes() != numNodes) { throw OperationException("mask metric must have the same number of vertices"); } LabelFile* myLabelOut = myParams->getOutputLabel(3);//gets the output metric with key 2 OptionalParameter* columnSelect = myParams->getOptionalParameter(4);//gets optional parameter with key 3 int columnNum = -1; if (columnSelect->m_present) {//set up to use the single column columnNum = (int)myLabel->getMapIndexFromNameOrNumber(columnSelect->getString(1)); if (columnNum < 0 || columnNum >= numCols) { throw OperationException("invalid column specified"); } } vector columnScratch(numNodes); int32_t unusedKey = myLabel->getLabelTable()->getUnassignedLabelKey(); if (columnNum == -1) { myLabelOut->setNumberOfNodesAndColumns(numNodes, numCols); myLabelOut->setStructure(myLabel->getStructure()); *(myLabelOut->getLabelTable()) = *(myLabel->getLabelTable()); const float* roiColumn = myMask->getValuePointerForColumn(0); for (int i = 0; i < numCols; ++i) { myLabelOut->setColumnName(i, myLabel->getColumnName(i)); const int32_t* keyColumn = myLabel->getLabelKeyPointerForColumn(i); for (int j = 0; j < numNodes; ++j) { if (roiColumn[j] > 0.0f) { columnScratch[j] = keyColumn[j]; } else { columnScratch[j] = unusedKey; } } myLabelOut->setLabelKeysForColumn(i, columnScratch.data()); } } else { myLabelOut->setNumberOfNodesAndColumns(numNodes, 1); myLabelOut->setStructure(myLabel->getStructure()); *(myLabelOut->getLabelTable()) = *(myLabel->getLabelTable()); myLabelOut->setColumnName(0, myLabel->getColumnName(columnNum)); const float* roiColumn = myMask->getValuePointerForColumn(0); const int32_t* keyColumn = myLabel->getLabelKeyPointerForColumn(columnNum); for (int j = 0; j < numNodes; ++j) { if (roiColumn[j] > 0.0f) { columnScratch[j] = keyColumn[j]; } else { columnScratch[j] = unusedKey; } } myLabelOut->setLabelKeysForColumn(0, columnScratch.data()); } } connectome-workbench-1.2.3+git41-gc4c6c90/src/Operations/OperationLabelMask.h000066400000000000000000000025671300200146000265660ustar00rootroot00000000000000#ifndef __OPERATION_LABEL_MASK_H__ #define __OPERATION_LABEL_MASK_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 "AbstractOperation.h" namespace caret { class OperationLabelMask : public AbstractOperation { public: static OperationParameters* getParameters(); static void useParameters(OperationParameters* myParams, ProgressObject* myProgObj); static AString getCommandSwitch(); static AString getShortDescription(); }; typedef TemplateAutoOperation AutoOperationLabelMask; } #endif //__OPERATION_LABEL_MASK_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Operations/OperationLabelMerge.cxx000066400000000000000000000221301300200146000272710ustar00rootroot00000000000000/*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 "OperationLabelMerge.h" #include "OperationException.h" #include "GiftiLabelTable.h" #include "LabelFile.h" #include #include using namespace caret; using namespace std; AString OperationLabelMerge::getCommandSwitch() { return "-label-merge"; } AString OperationLabelMerge::getShortDescription() { return "MERGE LABEL FILES INTO A NEW FILE"; } OperationParameters* OperationLabelMerge::getParameters() { OperationParameters* ret = new OperationParameters(); ret->addLabelOutputParameter(1, "label-out", "the output label"); ParameterComponent* labelOpt = ret->createRepeatableParameter(2, "-label", "specify an input label"); labelOpt->addLabelParameter(1, "label-in", "a label file to use columns from"); ParameterComponent* columnOpt = labelOpt->createRepeatableParameter(2, "-column", "select a single column to use"); columnOpt->addStringParameter(1, "column", "the column number or name"); OptionalParameter* upToOpt = columnOpt->createOptionalParameter(2, "-up-to", "use an inclusive range of columns"); upToOpt->addStringParameter(1, "last-column", "the number or name of the last column to include"); upToOpt->createOptionalParameter(2, "-reverse", "use the range in reverse order"); ret->setHelpText( AString("Takes one or more label files and constructs a new label file by concatenating columns from them. ") + "The input files must have the same number of vertices and the same structure.\n\n" + "Example: wb_command -label-merge out.label.gii -label first.label.gii -column 1 -label second.label.gii\n\n" + "This example would take the first column from first.label.gii and all subvolumes from second.label.gii, " + "and write these to out.label.gii." ); return ret; } namespace {//private namespace for helper functions void doRemap(const int32_t* input, const map& remap, const int numElems, const int32_t& unlabeledValue, int32_t* output) { for (int i = 0; i < numElems; ++i) { map::const_iterator iter = remap.find(input[i]); if (iter != remap.end()) { output[i] = iter->second; } else {//values that have no key output[i] = unlabeledValue; } } } } void OperationLabelMerge::useParameters(OperationParameters* myParams, ProgressObject* myProgObj) { LevelProgress myProgress(myProgObj); LabelFile* myLabelOut = myParams->getOutputLabel(1); const vector& myInputs = *(myParams->getRepeatableParameterInstances(2)); int numInputs = (int)myInputs.size(); if (numInputs == 0) throw OperationException("no inputs specified"); const LabelFile* firstLabel = myInputs[0]->getLabel(1); int numOutColumns = 0; int numNodes = firstLabel->getNumberOfNodes(); StructureEnum::Enum myStruct = firstLabel->getStructure(); GiftiLabelTable outTable; vector > fileRemap(numInputs); for (int i = 0; i < numInputs; ++i) { LabelFile* inputLabel = myInputs[i]->getLabel(1); fileRemap[i] = outTable.append(*(inputLabel->getLabelTable()));//NOTE: does (and must) include identity mappings - anything that doesn't match was invalid in the original file if (numNodes != inputLabel->getNumberOfNodes()) throw OperationException("file '" + inputLabel->getFileName() + "' has a different number of nodes than the first"); if (myStruct != inputLabel->getStructure()) throw OperationException("file '" + inputLabel->getFileName() + "' has a different structure than the first"); const vector& columnOpts = *(myInputs[i]->getRepeatableParameterInstances(2)); int numColumnOpts = (int)columnOpts.size(); if (numColumnOpts > 0) { for (int j = 0; j < numColumnOpts; ++j) { int initialColumn = inputLabel->getMapIndexFromNameOrNumber(columnOpts[j]->getString(1)); if (initialColumn < 0) throw OperationException("column '" + columnOpts[j]->getString(1) + "' not found in file '" + inputLabel->getFileName() + "'"); OptionalParameter* upToOpt = columnOpts[j]->getOptionalParameter(2); if (upToOpt->m_present) { int finalColumn = inputLabel->getMapIndexFromNameOrNumber(upToOpt->getString(1)); if (finalColumn < 0) throw OperationException("ending column '" + upToOpt->getString(1) + "' not found in file '" + inputLabel->getFileName() + "'"); if (finalColumn < initialColumn) throw OperationException("ending column '" + upToOpt->getString(1) + "' occurs before starting column '" + columnOpts[j]->getString(1) + "' in file '" + inputLabel->getFileName() + "'"); numOutColumns += finalColumn - initialColumn + 1;//inclusive - we don't need to worry about reversing for counting, though } else { numOutColumns += 1; } } } else { numOutColumns += inputLabel->getNumberOfColumns(); } } myLabelOut->setNumberOfNodesAndColumns(numNodes, numOutColumns); myLabelOut->setStructure(myStruct); *(myLabelOut->getLabelTable()) = outTable; vector scratchCol(numNodes); int curColumn = 0; for (int i = 0; i < numInputs; ++i) { const LabelFile* inputLabel = myInputs[i]->getLabel(1); const vector& columnOpts = *(myInputs[i]->getRepeatableParameterInstances(2)); int numColumnOpts = (int)columnOpts.size(); if (numColumnOpts > 0) { for (int j = 0; j < numColumnOpts; ++j) { int initialColumn = inputLabel->getMapIndexFromNameOrNumber(columnOpts[j]->getString(1)); OptionalParameter* upToOpt = columnOpts[j]->getOptionalParameter(2); if (upToOpt->m_present) { int finalColumn = inputLabel->getMapIndexFromNameOrNumber(upToOpt->getString(1)); bool reverse = upToOpt->getOptionalParameter(2)->m_present; if (reverse) { for (int c = finalColumn; c >= initialColumn; --c) { doRemap(inputLabel->getLabelKeyPointerForColumn(c), fileRemap[i], numNodes, outTable.getUnassignedLabelKey(), scratchCol.data()); myLabelOut->setLabelKeysForColumn(curColumn, scratchCol.data()); myLabelOut->setColumnName(curColumn, inputLabel->getColumnName(c)); ++curColumn; } } else { for (int c = initialColumn; c <= finalColumn; ++c) { doRemap(inputLabel->getLabelKeyPointerForColumn(c), fileRemap[i], numNodes, outTable.getUnassignedLabelKey(), scratchCol.data()); myLabelOut->setLabelKeysForColumn(curColumn, scratchCol.data()); myLabelOut->setColumnName(curColumn, inputLabel->getColumnName(c)); ++curColumn; } } } else { doRemap(inputLabel->getLabelKeyPointerForColumn(initialColumn), fileRemap[i], numNodes, outTable.getUnassignedLabelKey(), scratchCol.data()); myLabelOut->setLabelKeysForColumn(curColumn, scratchCol.data()); myLabelOut->setColumnName(curColumn, inputLabel->getColumnName(initialColumn)); ++curColumn; } } } else { int numColumns = inputLabel->getNumberOfColumns(); for (int j = 0; j < numColumns; ++j) { doRemap(inputLabel->getLabelKeyPointerForColumn(j), fileRemap[i], numNodes, outTable.getUnassignedLabelKey(), scratchCol.data()); myLabelOut->setLabelKeysForColumn(curColumn, scratchCol.data()); myLabelOut->setColumnName(curColumn, inputLabel->getColumnName(j)); ++curColumn; } } } CaretAssert(curColumn == numOutColumns); } connectome-workbench-1.2.3+git41-gc4c6c90/src/Operations/OperationLabelMerge.h000066400000000000000000000025751300200146000267310ustar00rootroot00000000000000#ifndef __OPERATION_LABEL_MERGE_H__ #define __OPERATION_LABEL_MERGE_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 "AbstractOperation.h" namespace caret { class OperationLabelMerge : public AbstractOperation { public: static OperationParameters* getParameters(); static void useParameters(OperationParameters* myParams, ProgressObject* myProgObj); static AString getCommandSwitch(); static AString getShortDescription(); }; typedef TemplateAutoOperation AutoOperationLabelMerge; } #endif //__OPERATION_LABEL_MERGE_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Operations/OperationMetadataRemoveProvenance.cxx000066400000000000000000000127171300200146000322230ustar00rootroot00000000000000/*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 "OperationMetadataRemoveProvenance.h" #include "OperationException.h" #include "CiftiFile.h" #include "DataFileTypeEnum.h" #include "LabelFile.h" #include "MetricFile.h" #include "VolumeFile.h" #include "FileInformation.h" using namespace caret; using namespace std; AString OperationMetadataRemoveProvenance::getCommandSwitch() { return "-metadata-remove-provenance"; } AString OperationMetadataRemoveProvenance::getShortDescription() { return "REMOVE PROVENANCE INFORMATION FROM FILE METADATA"; } OperationParameters* OperationMetadataRemoveProvenance::getParameters() { OperationParameters* ret = new OperationParameters(); ret->addStringParameter(1, "input-file", "the file to remove provenance information from"); ret->addStringParameter(2, "output-file", "output - the name to save the modified file as");//HACK: fake the output format, since it doesn't know what kind of file it is ret->setHelpText( AString("Removes the provenance metadata fields added by workbench during processing.") ); return ret; } void OperationMetadataRemoveProvenance::useParameters(OperationParameters* myParams, ProgressObject* myProgObj) { LevelProgress myProgress(myProgObj); AString inFileName = myParams->getString(1); AString outFileName = myParams->getString(2); bool ok = false; DataFileTypeEnum::Enum myType = DataFileTypeEnum::fromFileExtension(inFileName, &ok); if (!ok) { throw OperationException("unrecognized data file type"); } switch (myType) { case DataFileTypeEnum::METRIC: { MetricFile myMetric; myMetric.readFile(inFileName); removeProvenance(myMetric.getFileMetaData()); myMetric.writeFile(outFileName); break; } case DataFileTypeEnum::LABEL: { LabelFile myLabel; myLabel.readFile(inFileName); removeProvenance(myLabel.getFileMetaData()); myLabel.writeFile(outFileName); break; } case DataFileTypeEnum::VOLUME: { VolumeFile myVol; myVol.readFile(inFileName); removeProvenance(myVol.getFileMetaData()); myVol.writeFile(outFileName); break; } case DataFileTypeEnum::CONNECTIVITY_DENSE: case DataFileTypeEnum::CONNECTIVITY_DENSE_LABEL: case DataFileTypeEnum::CONNECTIVITY_DENSE_PARCEL: case DataFileTypeEnum::CONNECTIVITY_DENSE_SCALAR: case DataFileTypeEnum::CONNECTIVITY_DENSE_TIME_SERIES: case DataFileTypeEnum::CONNECTIVITY_PARCEL: case DataFileTypeEnum::CONNECTIVITY_PARCEL_DENSE: { CiftiFile myCifti; FileInformation myInfo1(inFileName), myInfo2(outFileName); myCifti.openFile(inFileName); if (myInfo1.getCanonicalFilePath() == myInfo2.getCanonicalFilePath()) { myCifti.convertToInMemory(); } CiftiXMLOld myXML = myCifti.getCiftiXMLOld(); CiftiFile myOutCifti; myOutCifti.setWritingFile(outFileName); int numRows = myXML.getNumberOfRows(), rowSize = myXML.getNumberOfColumns(); removeProvenance(myXML.getFileMetaData()); myOutCifti.setCiftiXML(myXML, false); vector scratchRow(rowSize); for (int i = 0; i < numRows; ++i) { myCifti.getRow(scratchRow.data(), i); myOutCifti.setRow(scratchRow.data(), i); } myOutCifti.writeFile(outFileName); break; } default: throw OperationException("file type not supported in metadata remove provenance"); } } void OperationMetadataRemoveProvenance::removeProvenance(GiftiMetaData* toModify) { if (toModify == NULL) return; set provenanceNames = getProvenanceKeys(); for (set::iterator iter = provenanceNames.begin(); iter != provenanceNames.end(); ++iter) { toModify->remove(*iter); } } void OperationMetadataRemoveProvenance::removeProvenance(map* toModify) { if (toModify == NULL) return; set provenanceNames = getProvenanceKeys(); for (set::iterator iter = provenanceNames.begin(); iter != provenanceNames.end(); ++iter) { toModify->erase(*iter); } } set OperationMetadataRemoveProvenance::getProvenanceKeys() { set ret; ret.insert("Provenance");//these should really be in a common location somewhere ret.insert("ParentProvenance"); ret.insert("ProgramProvenance"); ret.insert("WorkingDirectory"); return ret; } connectome-workbench-1.2.3+git41-gc4c6c90/src/Operations/OperationMetadataRemoveProvenance.h000066400000000000000000000033221300200146000316400ustar00rootroot00000000000000#ifndef __OPERATION_METADATA_REMOVE_PROVENANCE_H__ #define __OPERATION_METADATA_REMOVE_PROVENANCE_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 "AbstractOperation.h" #include #include namespace caret { class GiftiMetaData; class OperationMetadataRemoveProvenance : public AbstractOperation { static void removeProvenance(GiftiMetaData* toModify); static void removeProvenance(std::map* toModify); static std::set getProvenanceKeys(); public: static OperationParameters* getParameters(); static void useParameters(OperationParameters* myParams, ProgressObject* myProgObj); static AString getCommandSwitch(); static AString getShortDescription(); }; typedef TemplateAutoOperation AutoOperationMetadataRemoveProvenance; } #endif //__OPERATION_METADATA_REMOVE_PROVENANCE_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Operations/OperationMetadataStringReplace.cxx000066400000000000000000000205541300200146000315050ustar00rootroot00000000000000/*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 "OperationMetadataStringReplace.h" #include "OperationException.h" #include "CiftiFile.h" #include "DataFileTypeEnum.h" #include "LabelFile.h" #include "MetricFile.h" #include "VolumeFile.h" #include "FileInformation.h" using namespace caret; using namespace std; AString OperationMetadataStringReplace::getCommandSwitch() { return "-metadata-string-replace"; } AString OperationMetadataStringReplace::getShortDescription() { return "REPLACE A STRING IN ALL METADATA OF A FILE"; } OperationParameters* OperationMetadataStringReplace::getParameters() { OperationParameters* ret = new OperationParameters(); ret->addStringParameter(1, "input-file", "the file to replace metadata in"); ret->addStringParameter(2, "find-string", "the string to find"); ret->addStringParameter(3, "replace-string", "the string to replace with"); ret->addStringParameter(4, "output-file", "output - the name to save the modified file as");//HACK: fake the output format, since it doesn't know what kind of file it is ret->createOptionalParameter(5, "-case-insensitive", "match with case variation also"); ret->setHelpText( AString("Replaces all occurrences of in the metadata and map names of with .") ); return ret; } void OperationMetadataStringReplace::useParameters(OperationParameters* myParams, ProgressObject* myProgObj) { LevelProgress myProgress(myProgObj); AString inFileName = myParams->getString(1); AString findString = myParams->getString(2); AString replString = myParams->getString(3); AString outFileName = myParams->getString(4); Qt::CaseSensitivity myCS = Qt::CaseSensitive; if (myParams->getOptionalParameter(5)->m_present) { myCS = Qt::CaseInsensitive; } bool ok = false; DataFileTypeEnum::Enum myType = DataFileTypeEnum::fromFileExtension(inFileName, &ok); if (!ok) { throw OperationException("unrecognized data file type"); } switch (myType) { case DataFileTypeEnum::METRIC: { MetricFile myMetric; myMetric.readFile(inFileName); replaceInMetaData(myMetric.getFileMetaData(), findString, replString, myCS); int32_t numMaps = myMetric.getNumberOfMaps(); for (int32_t map = 0; map < numMaps; ++map) { replaceInMetaData(myMetric.getMapMetaData(map), findString, replString, myCS); AString mapName = myMetric.getMapName(map); myMetric.setMapName(map, mapName.replace(findString, replString, myCS)); } myMetric.writeFile(outFileName); break; } case DataFileTypeEnum::LABEL: { LabelFile myLabel; myLabel.readFile(inFileName); replaceInMetaData(myLabel.getFileMetaData(), findString, replString, myCS); int32_t numMaps = myLabel.getNumberOfMaps(); for (int32_t map = 0; map < numMaps; ++map) { replaceInMetaData(myLabel.getMapMetaData(map), findString, replString, myCS); AString mapName = myLabel.getMapName(map); myLabel.setMapName(map, mapName.replace(findString, replString, myCS)); } myLabel.writeFile(outFileName); break; } case DataFileTypeEnum::VOLUME: { VolumeFile myVol; myVol.readFile(inFileName); replaceInMetaData(myVol.getFileMetaData(), findString, replString, myCS); int32_t numMaps = myVol.getNumberOfMaps(); for (int32_t map = 0; map < numMaps; ++map) { replaceInMetaData(myVol.getMapMetaData(map), findString, replString, myCS); AString mapName = myVol.getMapName(map); myVol.setMapName(map, mapName.replace(findString, replString, myCS)); } myVol.writeFile(outFileName); break; } case DataFileTypeEnum::CONNECTIVITY_DENSE: case DataFileTypeEnum::CONNECTIVITY_DENSE_LABEL: case DataFileTypeEnum::CONNECTIVITY_DENSE_PARCEL: case DataFileTypeEnum::CONNECTIVITY_DENSE_SCALAR: case DataFileTypeEnum::CONNECTIVITY_DENSE_TIME_SERIES: case DataFileTypeEnum::CONNECTIVITY_PARCEL: case DataFileTypeEnum::CONNECTIVITY_PARCEL_DENSE: { CiftiFile myCifti; FileInformation myInfo1(inFileName), myInfo2(outFileName); myCifti.openFile(inFileName); if (myInfo1.getCanonicalFilePath() == myInfo2.getCanonicalFilePath()) { myCifti.convertToInMemory(); } CiftiXMLOld myXML = myCifti.getCiftiXMLOld(); CiftiFile myOutCifti; myOutCifti.setWritingFile(outFileName); int numRows = myXML.getNumberOfRows(), rowSize = myXML.getNumberOfColumns(); replaceInMetaData(myXML.getFileMetaData(), findString, replString, myCS); if (myXML.getMappingType(CiftiXMLOld::ALONG_ROW) == CIFTI_INDEX_TYPE_SCALARS || myXML.getMappingType(CiftiXMLOld::ALONG_ROW) == CIFTI_INDEX_TYPE_LABELS) { for (int row = 0; row < numRows; ++row) { AString mapName = myXML.getMapName(CiftiXMLOld::ALONG_ROW, row); myXML.setMapNameForIndex(CiftiXMLOld::ALONG_ROW, row, mapName.replace(findString, replString, myCS)); replaceInMetaData(myXML.getMapMetadata(CiftiXMLOld::ALONG_ROW, row), findString, replString, myCS); } } if (myXML.getMappingType(CiftiXMLOld::ALONG_COLUMN) == CIFTI_INDEX_TYPE_SCALARS || myXML.getMappingType(CiftiXMLOld::ALONG_COLUMN) == CIFTI_INDEX_TYPE_LABELS) { for (int col = 0; col < rowSize; ++col) { AString mapName = myXML.getMapName(CiftiXMLOld::ALONG_COLUMN, col); myXML.setMapNameForIndex(CiftiXMLOld::ALONG_COLUMN, col, mapName.replace(findString, replString, myCS)); replaceInMetaData(myXML.getMapMetadata(CiftiXMLOld::ALONG_COLUMN, col), findString, replString, myCS); } } myOutCifti.setCiftiXML(myXML, false); vector scratchRow(rowSize); for (int i = 0; i < numRows; ++i) { myCifti.getRow(scratchRow.data(), i); myOutCifti.setRow(scratchRow.data(), i); } myOutCifti.writeFile(outFileName); break; } default: throw OperationException("file type not supported in metadata string replace"); } } void OperationMetadataStringReplace::replaceInMetaData(GiftiMetaData* toModify, const AString& findStr, const AString& replStr, const Qt::CaseSensitivity& myCS) { if (toModify == NULL) return; vector metaNames = toModify->getAllMetaDataNames(); int64_t numNames = (int64_t)metaNames.size(); for (int64_t i = 0; i < numNames; ++i) { AString value = toModify->get(metaNames[i]); toModify->set(metaNames[i], value.replace(findStr, replStr, myCS)); } } void OperationMetadataStringReplace::replaceInMetaData(map* toModify, const AString& findStr, const AString& replStr, const Qt::CaseSensitivity& myCS) { if (toModify == NULL) return; for (map::iterator iter = toModify->begin(); iter != toModify->end(); ++iter) { iter->second.replace(findStr, replStr, myCS);//replace actually modifies the string itself } } connectome-workbench-1.2.3+git41-gc4c6c90/src/Operations/OperationMetadataStringReplace.h000066400000000000000000000034371300200146000311330ustar00rootroot00000000000000#ifndef __OPERATION_METADATA_STRING_REPLACE_H__ #define __OPERATION_METADATA_STRING_REPLACE_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 "AbstractOperation.h" #include namespace caret { class GiftiMetaData; class OperationMetadataStringReplace : public AbstractOperation { static void replaceInMetaData(GiftiMetaData* toModify, const AString& findStr, const AString& replStr, const Qt::CaseSensitivity& myCS); static void replaceInMetaData(std::map* toModify, const AString& findStr, const AString& replStr, const Qt::CaseSensitivity& myCS); public: static OperationParameters* getParameters(); static void useParameters(OperationParameters* myParams, ProgressObject* myProgObj); static AString getCommandSwitch(); static AString getShortDescription(); }; typedef TemplateAutoOperation AutoOperationMetadataStringReplace; } #endif //__OPERATION_METADATA_STRING_REPLACE_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Operations/OperationMetricConvert.cxx000066400000000000000000000126611300200146000300660ustar00rootroot00000000000000/*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 "OperationMetricConvert.h" #include "OperationException.h" #include "CaretLogger.h" #include "FloatMatrix.h" #include "MetricFile.h" #include "SurfaceFile.h" #include "VolumeFile.h" #include using namespace caret; using namespace std; AString OperationMetricConvert::getCommandSwitch() { return "-metric-convert"; } AString OperationMetricConvert::getShortDescription() { return "CONVERT METRIC FILE TO FAKE NIFTI"; } OperationParameters* OperationMetricConvert::getParameters() { OperationParameters* ret = new OperationParameters(); OptionalParameter* toNifti = ret->createOptionalParameter(1, "-to-nifti", "convert metric to nifti"); toNifti->addMetricParameter(1, "metric-in", "the metric to convert"); toNifti->addVolumeOutputParameter(2, "nifti-out", "the output nifti file");//we can use VolumeFile because it is 4D, though some spatial dimensions may be singular OptionalParameter* fromNifti = ret->createOptionalParameter(2, "-from-nifti", "convert nifti to metric"); fromNifti->addVolumeParameter(1, "nifti-in", "the nifti file to convert"); fromNifti->addSurfaceParameter(2, "surface-in", "surface file to use number of vertices and structure from"); fromNifti->addMetricOutputParameter(3, "metric-out", "the output metric file"); ret->setHelpText( AString("The purpose of this command is to convert between metric files and nifti1 so that gifti-unaware programs can operate on the data. ") + "You must specify exactly one of the options." ); return ret; } void OperationMetricConvert::useParameters(OperationParameters* myParams, ProgressObject* myProgObj) { LevelProgress myProgress(myProgObj); int modes = 0; OptionalParameter* toNifti = myParams->getOptionalParameter(1); if (toNifti->m_present) ++modes; OptionalParameter* fromNifti = myParams->getOptionalParameter(2); if (fromNifti->m_present) ++modes; if (modes != 1) { throw OperationException("you must specify exactly one conversion mode"); } if (toNifti->m_present) { MetricFile* myMetric = toNifti->getMetric(1); VolumeFile* outVolume = toNifti->getOutputVolume(2); int numNodes = myMetric->getNumberOfNodes(), numCols = myMetric->getNumberOfColumns(); if (numCols > 32767)//max of short { throw OperationException("number of metric columns exceeds nifti1 limit, failing"); } vector myDims(4, 1); myDims[3] = numCols; if (numNodes > 32767) { myDims[0] = 32767; int temp = (numNodes - 1) / 32767 + 1;//round up instead of down if (temp > 32767)//should be rare to the point of nonexistence, but technically possible { myDims[1] = 32767; myDims[2] = (temp - 1) / 32767 + 1; if (myDims[2] > 32767) throw OperationException("number of vertices too large for nifti1 spatial dimensions, failing"); } else { myDims[1] = temp; } } else { myDims[0] = numNodes; } int frameSize = myDims[0] * myDims[1] * myDims[2]; vector scratchFrame(frameSize, 0.0f); FloatMatrix mySpace = FloatMatrix::identity(4); mySpace[0][0] = -1;//use radiological sform so FSL doesn't do stupid stuff outVolume->reinitialize(myDims, mySpace.getMatrix()); for (int i = 0; i < numCols; ++i) { const float* myCol = myMetric->getValuePointerForColumn(i); for (int j = 0; j < numNodes; ++j) { scratchFrame[j] = myCol[j]; } outVolume->setFrame(scratchFrame.data(), i); } } if (fromNifti->m_present) { VolumeFile* myNifti = fromNifti->getVolume(1); SurfaceFile* mySurf = fromNifti->getSurface(2); MetricFile* outMetric = fromNifti->getOutputMetric(3); vector myDims = myNifti->getDimensions(); int numNodes = mySurf->getNumberOfNodes(); int64_t frameSize = myDims[0] * myDims[1] * myDims[2]; if (frameSize < numNodes) { throw OperationException("nifti file does not have dimensions large enough to satisfy specified number of nodes"); } int numCols = (int)myDims[3];//if someone manages over 2 billion timepoints, reward them with truncation outMetric->setNumberOfNodesAndColumns(numNodes, numCols); outMetric->setStructure(mySurf->getStructure()); for (int i = 0; i < numCols; ++i) { outMetric->setValuesForColumn(i, myNifti->getFrame(i)); } } } connectome-workbench-1.2.3+git41-gc4c6c90/src/Operations/OperationMetricConvert.h000066400000000000000000000026171300200146000275130ustar00rootroot00000000000000#ifndef __OPERATION_METRIC_CONVERT_H__ #define __OPERATION_METRIC_CONVERT_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 "AbstractOperation.h" namespace caret { class OperationMetricConvert : public AbstractOperation { public: static OperationParameters* getParameters(); static void useParameters(OperationParameters* myParams, ProgressObject* myProgObj); static AString getCommandSwitch(); static AString getShortDescription(); }; typedef TemplateAutoOperation AutoOperationMetricConvert; } #endif //__OPERATION_METRIC_CONVERT_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Operations/OperationMetricLabelImport.cxx000066400000000000000000000334521300200146000306610ustar00rootroot00000000000000/*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 "OperationMetricLabelImport.h" #include "OperationException.h" #include "CaretLogger.h" #include "FileInformation.h" #include "GiftiLabel.h" #include "GiftiLabelTable.h" #include "LabelFile.h" #include "MetricFile.h" #include #include #include #include #include #include using namespace caret; using namespace std; AString OperationMetricLabelImport::getCommandSwitch() { return "-metric-label-import"; } AString OperationMetricLabelImport::getShortDescription() { return "IMPORT A GIFTI LABEL FILE FROM A METRIC FILE"; } OperationParameters* OperationMetricLabelImport::getParameters() { OperationParameters* ret = new OperationParameters(); ret->addMetricParameter(1, "input", "the input metric file"); ret->addStringParameter(2, "label-list-file", "text file containing the values and names for labels"); ret->addLabelOutputParameter(3, "output", "the output gifti label file"); ret->createOptionalParameter(4, "-discard-others", "set any values not mentioned in the label list to the ??? label"); OptionalParameter* unlabeledOption = ret->createOptionalParameter(5, "-unlabeled-value", "set the value that will be interpreted as unlabeled"); unlabeledOption->addIntegerParameter(1, "value", "the numeric value for unlabeled (default 0)"); OptionalParameter* columnSelect = ret->createOptionalParameter(6, "-column", "select a single column to import"); columnSelect->addStringParameter(1, "column", "the column number or name"); ret->createOptionalParameter(7, "-drop-unused-labels", "remove any unused label values from the label table"); ret->setHelpText( AString("Creates a new gifti label file from a metric file with label-like values. ") + "You may specify the empty string ('' will work on linux/mac) for , which will be treated as if it is an empty file. " + "The label list file must have lines of the following format:\n\n" + "\n \n\n" + "Do not specify the \"unlabeled\" key in the file, it is assumed that 0 means not labeled unless -unlabeled-value is specified. " + "Label names must be on a separate line, but may contain spaces or other unusual characters (but not newline). " + "Whitespace is trimmed from both ends of the label name, but is kept if it is in the middle of a label. " + "The values of red, green, blue and alpha must be integers from 0 to 255, and will specify the color the label is drawn as " + "(alpha of 255 means opaque, which is probably what you want). " + "By default, it will set new label names with names of LABEL_# for any values encountered that are not mentioned in the " + "list file, specify -discard-others to instead set these voxels to the \"unlabeled\" key." ); return ret; } void OperationMetricLabelImport::useParameters(OperationParameters* myParams, ProgressObject* myProgObj) { LevelProgress myProgress(myProgObj); MetricFile* myMetric = myParams->getMetric(1); AString listfileName = myParams->getString(2); LabelFile* myLabelOut = myParams->getOutputLabel(3); bool discardOthers = myParams->getOptionalParameter(4)->m_present; int32_t unlabeledValue = 0; OptionalParameter* unlabeledOption = myParams->getOptionalParameter(5); if (unlabeledOption->m_present) { unlabeledValue = (int32_t)unlabeledOption->getInteger(1); } OptionalParameter* columnSelect = myParams->getOptionalParameter(6); int columnNum = -1; if (columnSelect->m_present) { columnNum = (int)myMetric->getMapIndexFromNameOrNumber(columnSelect->getString(1)); if (columnNum < 0 || columnNum >= myMetric->getNumberOfMaps()) { throw OperationException("invalid column specified"); } } bool dropUnused = myParams->getOptionalParameter(7)->m_present; map translate; GiftiLabelTable myTable; if (listfileName != "")//maybe this should be a function of GiftiLabelTable { AString temp; FileInformation textFileInfo(listfileName); if (!textFileInfo.exists()) { throw OperationException("label list file doesn't exist"); } fstream labelListFile(listfileName.toLocal8Bit().constData(), fstream::in); if (!labelListFile.good()) { throw OperationException("error reading label list file"); } string labelName; int32_t value, red, green, blue, alpha; int labelCount = 0; translate[unlabeledValue] = 0;//placeholder, we don't know the correct translated value yet while (labelListFile.good()) { ++labelCount;//just for error messages, so start at 1 getline(labelListFile, labelName); labelListFile >> value; if (labelListFile.eof() && labelName == "") break;//if end of file trying to read an int, and label name is empty, its really just end of file labelListFile >> red; labelListFile >> green; labelListFile >> blue; if (!(labelListFile >> alpha))//yes, that is seriously the correct way to check if input was successfully extracted...so much fail { throw OperationException("label list file is malformed for entry #" + AString::number(labelCount) + ": " + AString(labelName.c_str())); } if (red < 0 || red > 255) { throw OperationException("bad value for red for entry #" + AString::number(labelCount) + ", " + AString(labelName.c_str()) + ": " + AString::number(red)); } if (green < 0 || green > 255) { throw OperationException("bad value for green for entry #" + AString::number(labelCount) + ", " + AString(labelName.c_str()) + ": " + AString::number(green)); } if (blue < 0 || blue > 255) { throw OperationException("bad value for blue for entry #" + AString::number(labelCount) + ", " + AString(labelName.c_str()) + ": " + AString::number(blue)); } if (alpha < 0 || alpha > 255) { throw OperationException("bad value for alpha for entry #" + AString::number(labelCount) + ", " + AString(labelName.c_str()) + ": " + AString::number(alpha)); } if (value == GiftiLabel::getInvalidLabelKey()) { throw OperationException("entry #" + AString::number(labelCount) + ", " + AString(labelName.c_str()) + " specifies unusable key value: " + value); } while (isspace(labelListFile.peek())) { labelListFile.ignore();//drop the newline, possible carriage return or other whitespace so that getline doesn't get nothing, and cause int extraction to fail } temp = AString(labelName.c_str()).trimmed();//drop errant CR or other whitespace from beginning and end of lines if (translate.find(value) != translate.end()) { if (value == unlabeledValue) { throw OperationException("the unlabeled value must not be specified in label list file"); } else { throw OperationException(AString("label key ") + AString::number(value) + " specified more than once"); } } GiftiLabel myLabel(value, temp, red, green, blue, alpha); if (myTable.getLabelKeyFromName(temp) != GiftiLabel::getInvalidLabelKey()) { AString nameBase = temp, newName;//resolve collision by generating a name with an additional number on it bool success = false; for (int extra = 1; extra < 100; ++extra)//but stop at 100, because really... { newName = nameBase + "_" + AString::number(extra); if (myTable.getLabelKeyFromName(newName) == GiftiLabel::getInvalidLabelKey()) { success = true; break; } } if (success) { CaretLogWarning("name collision in input name '" + nameBase + "', changing one to '" + newName + "'"); } else { throw OperationException("giving up on resolving name collision for input name '" + nameBase + "'"); } myLabel.setName(newName); } int32_t newValue; if (value == 0)//because label 0 exists in the default constructed table { myTable.insertLabel(&myLabel);//but we do want to be able to overwrite the default 0 label newValue = 0;//if value 0 is specified twice, or once without specifying a different unlabeled value, the check versus the translate map will catch it } else { newValue = myTable.addLabel(&myLabel);//we don't want to overwrite relocated labels } translate[value] = newValue; } } int32_t unusedLabel = myTable.getUnassignedLabelKey(); translate[unlabeledValue] = unusedLabel; const int numNodes = myMetric->getNumberOfNodes(); vector colScratch(numNodes); if (columnNum == -1) { const int numCols = myMetric->getNumberOfColumns(); myLabelOut->setNumberOfNodesAndColumns(numNodes, numCols); myLabelOut->setStructure(myMetric->getStructure()); set usedValues; for (int col = 0; col < numCols; ++col) { translateLabels(myMetric->getValuePointerForColumn(col), colScratch.data(), numNodes, myTable, translate, usedValues, dropUnused, discardOthers, unusedLabel); myLabelOut->setLabelKeysForColumn(col, colScratch.data()); } if (dropUnused) { myTable.deleteUnusedLabels(usedValues); } *(myLabelOut->getLabelTable()) = myTable; } else { myLabelOut->setNumberOfNodesAndColumns(numNodes, 1); myLabelOut->setStructure(myMetric->getStructure()); set usedValues; translateLabels(myMetric->getValuePointerForColumn(columnNum), colScratch.data(), numNodes, myTable, translate, usedValues, dropUnused, discardOthers, unusedLabel); myLabelOut->setLabelKeysForColumn(0, colScratch.data()); if (dropUnused) { myTable.deleteUnusedLabels(usedValues); } *(myLabelOut->getLabelTable()) = myTable; } } void OperationMetricLabelImport::translateLabels(const float* valuesIn, int32_t* labelsOut, const int& numNodes, GiftiLabelTable& myTable, map& translate, set& usedValues, const bool& dropUnused, const bool& discardOthers, const int32_t& unusedLabel) { for (int node = 0; node < numNodes; ++node) { int32_t labelval = (int32_t)floor(valuesIn[node] + 0.5f);//just in case it somehow got poorly encoded, round to nearest if (dropUnused) { usedValues.insert(labelval); } map::iterator myiter = translate.find(labelval); if (myiter == translate.end()) { if (discardOthers) { labelsOut[node] = unusedLabel; } else {//use a random color, but fully opaque for the label GiftiLabel myLabel(labelval, AString("LABEL_") + AString::number(labelval), rand() & 255, rand() & 255, rand() & 255, 255); if (myTable.getLabelKeyFromName(myLabel.getName()) != GiftiLabel::getInvalidLabelKey()) { AString nameBase = myLabel.getName(), newName;//resolve collision by generating a name with an additional number on it bool success = false; for (int extra = 1; extra < 100; ++extra)//but stop at 100, because really... { newName = nameBase + "_" + AString::number(extra); if (myTable.getLabelKeyFromName(newName) == GiftiLabel::getInvalidLabelKey()) { success = true; break; } } if (success) { CaretLogWarning("name collision in auto-generated name '" + nameBase + "', changed to '" + newName + "'"); } else { throw OperationException("giving up on resolving name collision for auto-generated name '" + nameBase + "'"); } myLabel.setName(newName); } int32_t newValue = myTable.addLabel(&myLabel);//don't overwrite any values in the table translate[labelval] = newValue; labelsOut[node] = newValue; } } else { labelsOut[node] = myiter->second; } } } connectome-workbench-1.2.3+git41-gc4c6c90/src/Operations/OperationMetricLabelImport.h000066400000000000000000000034321300200146000303010ustar00rootroot00000000000000#ifndef __OPERATION_METRIC_LABEL_IMPORT_H__ #define __OPERATION_METRIC_LABEL_IMPORT_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 "AbstractOperation.h" #include #include namespace caret { class GiftiLabelTable; class OperationMetricLabelImport : public AbstractOperation { static void translateLabels(const float* valuesIn, int32_t* labelsOut, const int& numNodes, GiftiLabelTable& myTable, std::map& translate, std::set& usedValues, const bool& dropUnused, const bool& discardOthers, const int32_t& unusedLabel); public: static OperationParameters* getParameters(); static void useParameters(OperationParameters* myParams, ProgressObject* myProgObj); static AString getCommandSwitch(); static AString getShortDescription(); }; typedef TemplateAutoOperation AutoOperationMetricLabelImport; } #endif //__OPERATION_METRIC_LABEL_IMPORT_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Operations/OperationMetricMask.cxx000066400000000000000000000114001300200146000273270ustar00rootroot00000000000000/*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 "OperationMetricMask.h" #include "OperationException.h" #include "MetricFile.h" #include "PaletteColorMapping.h" using namespace caret; using namespace std; AString OperationMetricMask::getCommandSwitch() { return "-metric-mask"; } AString OperationMetricMask::getShortDescription() { return "MASK A METRIC FILE"; } OperationParameters* OperationMetricMask::getParameters() { OperationParameters* ret = new OperationParameters(); ret->addMetricParameter(1, "metric", "the input metric"); ret->addMetricParameter(2, "mask", "the mask metric"); ret->addMetricOutputParameter(3, "metric-out", "the output metric"); 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. ") + "if -column is specified, the output contains only one column, the masked version of the specified input column." ); return ret; } void OperationMetricMask::useParameters(OperationParameters* myParams, ProgressObject* myProgObj) { LevelProgress myProgress(myProgObj); MetricFile* myMetric = myParams->getMetric(1); MetricFile* myMask = myParams->getMetric(2); int32_t numNodes = myMetric->getNumberOfNodes(); if (myMask->getNumberOfNodes() != numNodes) { throw OperationException("mask metric must have the same number of vertices"); } MetricFile* myMetricOut = myParams->getOutputMetric(3);//gets the output metric with key 2 OptionalParameter* columnSelect = myParams->getOptionalParameter(4);//gets optional parameter with key 3 int columnNum = -1; if (columnSelect->m_present) {//set up to use the single column columnNum = (int)myMetric->getMapIndexFromNameOrNumber(columnSelect->getString(1)); if (columnNum < 0 || columnNum >= myMetric->getNumberOfColumns()) { throw OperationException("invalid column specified"); } } CaretArray myColumnOut(numNodes); if (columnNum == -1) { int32_t numCols = myMetric->getNumberOfColumns(); myMetricOut->setNumberOfNodesAndColumns(numNodes, numCols); myMetricOut->setStructure(myMetric->getStructure()); const float* maskVals = myMask->getValuePointerForColumn(0); for (int32_t col = 0; col < numCols; ++col) { const float* metricVals = myMetric->getValuePointerForColumn(col); *(myMetricOut->getPaletteColorMapping(col)) = *(myMetric->getPaletteColorMapping(col));//copy the palette settings for (int32_t node = 0; node < numNodes; ++node) { if (maskVals[node] > 0.0f) { myColumnOut[node] = metricVals[node]; } else { myColumnOut[node] = 0.0f; } } myMetricOut->setValuesForColumn(col, myColumnOut.getArray()); myMetricOut->setColumnName(col, myMetric->getColumnName(col)); } } else { myMetricOut->setNumberOfNodesAndColumns(numNodes, 1); myMetricOut->setStructure(myMetric->getStructure()); const float* maskVals = myMask->getValuePointerForColumn(0); const float* metricVals = myMetric->getValuePointerForColumn(columnNum); *(myMetricOut->getPaletteColorMapping(0)) = *(myMetric->getPaletteColorMapping(columnNum));//copy the palette settings for (int32_t node = 0; node < numNodes; ++node) { if (maskVals[node] > 0.0f) { myColumnOut[node] = metricVals[node]; } else { myColumnOut[node] = 0.0f; } } myMetricOut->setValuesForColumn(0, myColumnOut.getArray()); myMetricOut->setColumnName(0, myMetric->getColumnName(columnNum)); } } connectome-workbench-1.2.3+git41-gc4c6c90/src/Operations/OperationMetricMask.h000066400000000000000000000025371300200146000267670ustar00rootroot00000000000000#ifndef __METRIC_MASK_H__ #define __METRIC_MASK_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 "AbstractOperation.h" namespace caret { class OperationMetricMask : public AbstractOperation { public: static OperationParameters* getParameters(); static void useParameters(OperationParameters* myParams, ProgressObject* myProgObj); static AString getCommandSwitch(); static AString getShortDescription(); }; typedef TemplateAutoOperation AutoOperationMetricMask; } #endif //__METRIC_MASK_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Operations/OperationMetricMath.cxx000066400000000000000000000226451300200146000273420ustar00rootroot00000000000000/*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 "OperationMetricMath.h" #include "OperationException.h" #include "CaretAssert.h" #include "CaretLogger.h" #include "CaretMathExpression.h" #include "MetricFile.h" #include using namespace caret; using namespace std; AString OperationMetricMath::getCommandSwitch() { return "-metric-math"; } AString OperationMetricMath::getShortDescription() { return "EVALUATE EXPRESSION ON METRIC FILES"; } OperationParameters* OperationMetricMath::getParameters() { OperationParameters* ret = new OperationParameters(); ret->addStringParameter(1, "expression", "the expression to evaluate, in quotes"); ret->addMetricOutputParameter(2, "metric-out", "the output metric"); ParameterComponent* varOpt = ret->createRepeatableParameter(3, "-var", "a metric to use as a variable"); varOpt->addStringParameter(1, "name", "the name of the variable, as used in the expression"); varOpt->addMetricParameter(2, "metric", "the metric file to use as this variable"); OptionalParameter* columnSelect = varOpt->createOptionalParameter(3, "-column", "select a single column"); columnSelect->addStringParameter(1, "column", "the column number or name"); varOpt->createOptionalParameter(4, "-repeat", "reuse a single column for each column of calculation"); OptionalParameter* fixNanOpt = ret->createOptionalParameter(4, "-fixnan", "replace NaN results with a value"); fixNanOpt->addDoubleParameter(1, "replace", "value to replace NaN with"); AString myText = AString("This command evaluates at each surface vertex independently. ") + "There must be at least one -var option (to get the structure, number of vertices, and number of columns from), even if the specified in it isn't used in . " + "All metrics must have the same number of vertices. " + "Filenames are not valid in , use a variable name and a -var option with matching to specify an input file. " + "If the -column option is given to any -var option, only one column is used from that file. " + "If -repeat is specified, the file must either have only one column, or have the -column option specified. " + "All files that don't use -repeat must have the same number of columns requested to be used. " + "The format of is as follows:\n\n"; myText += CaretMathExpression::getExpressionHelpInfo(); ret->setHelpText(myText); return ret; } void OperationMetricMath::useParameters(OperationParameters* myParams, ProgressObject* myProgObj) { LevelProgress myProgress(myProgObj); AString expression = myParams->getString(1); CaretMathExpression myExpr(expression); cout << "parsed '" + expression + "' as '" + myExpr.toString() + "'" << endl; vector myVarNames = myExpr.getVarNames(); MetricFile* myMetricOut = myParams->getOutputMetric(2); const vector& myVarOpts = *(myParams->getRepeatableParameterInstances(3)); OptionalParameter* fixNanOpt = myParams->getOptionalParameter(4); bool nanfix = false; float nanfixval = 0; if (fixNanOpt->m_present) { nanfix = true; nanfixval = (float)fixNanOpt->getDouble(1); } int numInputs = myVarOpts.size(); int numVars = myVarNames.size(); vector varMetrics(numVars, (MetricFile*)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; StructureEnum::Enum myStructure; int numColumns = -1; for (int i = 0; i < numInputs; ++i) { if (i == 0) { numNodes = myVarOpts[0]->getMetric(2)->getNumberOfNodes(); myStructure = myVarOpts[0]->getMetric(2)->getStructure(); } AString varName = myVarOpts[i]->getString(1); double constVal; if (CaretMathExpression::getNamedConstant(varName, constVal)) { throw OperationException("'" + varName + "' is a named constant equal to " + AString::number(constVal, 'g', 15) + ", please use a different variable name"); } MetricFile* thisMetric = myVarOpts[i]->getMetric(2); int thisColumns = thisMetric->getNumberOfColumns(); OptionalParameter* columnSelect = myVarOpts[i]->getOptionalParameter(3); int useColumn = -1; if (columnSelect->m_present) { thisColumns = 1; useColumn = thisMetric->getMapIndexFromNameOrNumber(columnSelect->getString(1)); if (useColumn == -1) throw OperationException("could not find map '" + columnSelect->getString(1) + "' in metric file for '" + varName + "'"); } bool repeat = myVarOpts[i]->getOptionalParameter(4)->m_present; if (thisMetric->getNumberOfNodes() != numNodes) { throw OperationException("metric file for variable '" + varName + "' has different number of vertices than the first metric file"); } if (repeat) { if (thisColumns != 1) { throw OperationException("-repeat specified without -column for variable '" + varName + "', but metric file has " + AString::number(thisColumns) + " columns"); } if (useColumn == -1) useColumn = 0;//-1 means use same input column as current output column, so we need to fix the special case of -repeat on single column file without -column } else { if (numColumns == -1)//then this is the first one that doesn't use -repeat { numColumns = thisColumns; } else { if (numColumns != thisColumns) { if (useColumn == -1) { throw OperationException("metric file for variable '" + varName + "' has " + AString::number(thisColumns) + " column(s), but previous metric files have " + AString::number(numColumns) + " column(s) requested to be used"); } else { throw OperationException("-column specified without -repeat for variable '" + varName + "', but previous metric files have have " + AString::number(numColumns) + " columns requested to be used"); } } } } bool found = false; for (int j = 0; j < numVars; ++j) { if (varName == myVarNames[j]) { if (varMetrics[j] != NULL) throw OperationException("variable '" + varName + "' specified more than once"); varMetrics[j] = thisMetric; metricColumns[j] = useColumn; found = true; break; } } if (!found && (numVars != 0 || numInputs != 1))//supress warning when a single -var is used with a constant expression, as required per help { CaretLogWarning("variable '" + varName + "' not used in expression"); } } for (int i = 0; i < numVars; ++i) { if (varMetrics[i] == NULL) throw OperationException("no -var option specified for variable '" + myVarNames[i] + "'"); } if (numColumns == -1) { throw OperationException("all -var options used -repeat, there is no file to get number of desired output columns from"); } vector values(numVars), colScratch(numNodes); vector columnPointers(numVars); myMetricOut->setNumberOfNodesAndColumns(numNodes, numColumns); myMetricOut->setStructure(myStructure); for (int j = 0; j < numColumns; ++j) { for (int v = 0; v < numVars; ++v) { if (metricColumns[v] == -1) { columnPointers[v] = varMetrics[v]->getValuePointerForColumn(j); } else { columnPointers[v] = varMetrics[v]->getValuePointerForColumn(metricColumns[v]); } } for (int i = 0; i < numNodes; ++i) { for (int v = 0; v < numVars; ++v) { values[v] = columnPointers[v][i]; } colScratch[i] = (float)myExpr.evaluate(values); if (nanfix && colScratch[i] != colScratch[i]) { colScratch[i] = nanfixval; } } myMetricOut->setValuesForColumn(j, colScratch.data()); } } connectome-workbench-1.2.3+git41-gc4c6c90/src/Operations/OperationMetricMath.h000066400000000000000000000025751300200146000267670ustar00rootroot00000000000000#ifndef __OPERATION_METRIC_MATH_H__ #define __OPERATION_METRIC_MATH_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 "AbstractOperation.h" namespace caret { class OperationMetricMath : public AbstractOperation { public: static OperationParameters* getParameters(); static void useParameters(OperationParameters* myParams, ProgressObject* myProgObj); static AString getCommandSwitch(); static AString getShortDescription(); }; typedef TemplateAutoOperation AutoOperationMetricMath; } #endif //__OPERATION_METRIC_MATH_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Operations/OperationMetricMerge.cxx000066400000000000000000000204231300200146000275000ustar00rootroot00000000000000/*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 "OperationMetricMerge.h" #include "OperationException.h" #include "CaretLogger.h" #include "MetricFile.h" #include "PaletteColorMapping.h" #include using namespace caret; using namespace std; AString OperationMetricMerge::getCommandSwitch() { return "-metric-merge"; } AString OperationMetricMerge::getShortDescription() { return "MERGE METRIC FILES INTO A NEW FILE"; } OperationParameters* OperationMetricMerge::getParameters() { OperationParameters* ret = new OperationParameters(); ret->addMetricOutputParameter(1, "metric-out", "the output metric"); ParameterComponent* metricOpt = ret->createRepeatableParameter(2, "-metric", "specify an input metric"); metricOpt->addMetricParameter(1, "metric-in", "a metric file to use columns from"); ParameterComponent* columnOpt = metricOpt->createRepeatableParameter(2, "-column", "select a single column to use"); columnOpt->addStringParameter(1, "column", "the column number or name"); OptionalParameter* upToOpt = columnOpt->createOptionalParameter(2, "-up-to", "use an inclusive range of columns"); upToOpt->addStringParameter(1, "last-column", "the number or name of the last column to include"); upToOpt->createOptionalParameter(2, "-reverse", "use the range in reverse order"); ret->setHelpText( AString("Takes one or more metric files and constructs a new metric file by concatenating columns from them. ") + "The input metric files must have the same number of vertices and same structure.\n\n" + "Example: wb_command -metric-merge out.func.gii -metric first.func.gii -column 1 -metric second.func.gii\n\n" + "This example would take the first column from first.func.gii, followed by all columns from second.func.gii, " + "and write these columns to out.func.gii." ); return ret; } void OperationMetricMerge::useParameters(OperationParameters* myParams, ProgressObject* myProgObj) { LevelProgress myProgress(myProgObj); MetricFile* myMetricOut = myParams->getOutputMetric(1); const vector& myInputs = *(myParams->getRepeatableParameterInstances(2)); int numInputs = (int)myInputs.size(); if (numInputs == 0) throw OperationException("no inputs specified"); const MetricFile* firstMetric = myInputs[0]->getMetric(1); int numOutColumns = 0; int numNodes = firstMetric->getNumberOfNodes(); StructureEnum::Enum myStruct = firstMetric->getStructure(); for (int i = 0; i < numInputs; ++i) { const MetricFile* inputMetric = myInputs[i]->getMetric(1); if (numNodes != inputMetric->getNumberOfNodes()) throw OperationException("file '" + inputMetric->getFileName() + "' has a different number of nodes than the first"); if (myStruct != inputMetric->getStructure()) throw OperationException("file '" + inputMetric->getFileName() + "' has a different structure than the first"); const vector& columnOpts = *(myInputs[i]->getRepeatableParameterInstances(2)); int numColumnOpts = (int)columnOpts.size(); if (numColumnOpts > 0) { for (int j = 0; j < numColumnOpts; ++j) { int initialColumn = inputMetric->getMapIndexFromNameOrNumber(columnOpts[j]->getString(1)); if (initialColumn < 0) throw OperationException("column '" + columnOpts[j]->getString(1) + "' not found in file '" + inputMetric->getFileName() + "'"); OptionalParameter* upToOpt = columnOpts[j]->getOptionalParameter(2); if (upToOpt->m_present) { int finalColumn = inputMetric->getMapIndexFromNameOrNumber(upToOpt->getString(1)); if (finalColumn < 0) throw OperationException("ending column '" + upToOpt->getString(1) + "' not found in file '" + inputMetric->getFileName() + "'"); if (finalColumn < initialColumn) throw OperationException("ending column '" + upToOpt->getString(1) + "' occurs before starting column '" + columnOpts[j]->getString(1) + "' in file '" + inputMetric->getFileName() + "'"); numOutColumns += finalColumn - initialColumn + 1;//inclusive - we don't need to worry about reversing for counting, though } else { numOutColumns += 1; } } } else { numOutColumns += inputMetric->getNumberOfColumns(); } } myMetricOut->setNumberOfNodesAndColumns(numNodes, numOutColumns); myMetricOut->setStructure(myStruct); int curColumn = 0; for (int i = 0; i < numInputs; ++i) { const MetricFile* inputMetric = myInputs[i]->getMetric(1); const vector& columnOpts = *(myInputs[i]->getRepeatableParameterInstances(2)); int numColumnOpts = (int)columnOpts.size(); if (numColumnOpts > 0) { for (int j = 0; j < numColumnOpts; ++j) { int initialColumn = inputMetric->getMapIndexFromNameOrNumber(columnOpts[j]->getString(1)); OptionalParameter* upToOpt = columnOpts[j]->getOptionalParameter(2); if (upToOpt->m_present) { int finalColumn = inputMetric->getMapIndexFromNameOrNumber(upToOpt->getString(1)); bool reverse = upToOpt->getOptionalParameter(2)->m_present; if (reverse) { for (int c = finalColumn; c >= initialColumn; --c) { myMetricOut->setValuesForColumn(curColumn, inputMetric->getValuePointerForColumn(c)); myMetricOut->setColumnName(curColumn, inputMetric->getColumnName(c)); *(myMetricOut->getMapPaletteColorMapping(curColumn)) = *(inputMetric->getMapPaletteColorMapping(c)); ++curColumn; } } else { for (int c = initialColumn; c <= finalColumn; ++c) { myMetricOut->setValuesForColumn(curColumn, inputMetric->getValuePointerForColumn(c)); myMetricOut->setColumnName(curColumn, inputMetric->getColumnName(c)); *(myMetricOut->getMapPaletteColorMapping(curColumn)) = *(inputMetric->getMapPaletteColorMapping(c)); ++curColumn; } } } else { myMetricOut->setValuesForColumn(curColumn, inputMetric->getValuePointerForColumn(initialColumn)); myMetricOut->setColumnName(curColumn, inputMetric->getColumnName(initialColumn)); *(myMetricOut->getMapPaletteColorMapping(curColumn)) = *(inputMetric->getMapPaletteColorMapping(initialColumn)); ++curColumn; } } } else { int numColumns = inputMetric->getNumberOfColumns(); for (int j = 0; j < numColumns; ++j) { myMetricOut->setValuesForColumn(curColumn, inputMetric->getValuePointerForColumn(j)); myMetricOut->setColumnName(curColumn, inputMetric->getColumnName(j)); *(myMetricOut->getMapPaletteColorMapping(curColumn)) = *(inputMetric->getMapPaletteColorMapping(j)); ++curColumn; } } } CaretAssert(curColumn == numOutColumns); } connectome-workbench-1.2.3+git41-gc4c6c90/src/Operations/OperationMetricMerge.h000066400000000000000000000026031300200146000271250ustar00rootroot00000000000000#ifndef __OPERATION_METRIC_MERGE_H__ #define __OPERATION_METRIC_MERGE_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 "AbstractOperation.h" namespace caret { class OperationMetricMerge : public AbstractOperation { public: static OperationParameters* getParameters(); static void useParameters(OperationParameters* myParams, ProgressObject* myProgObj); static AString getCommandSwitch(); static AString getShortDescription(); }; typedef TemplateAutoOperation AutoOperationMetricMerge; } #endif //__OPERATION_METRIC_MERGE_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Operations/OperationMetricPalette.cxx000066400000000000000000000244311300200146000300420ustar00rootroot00000000000000/*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 "OperationMetricPalette.h" #include "OperationException.h" #include "MetricFile.h" #include "Palette.h" #include "PaletteColorMapping.h" #include "PaletteFile.h" #include using namespace caret; using namespace std; AString OperationMetricPalette::getCommandSwitch() { return "-metric-palette"; } AString OperationMetricPalette::getShortDescription() { return "SET THE PALETTE OF A METRIC FILE"; } OperationParameters* OperationMetricPalette::getParameters() { OperationParameters* ret = new OperationParameters(); ret->addStringParameter(1, "metric", "the metric to modify"); ret->addStringParameter(2, "mode", "the mapping mode"); OptionalParameter* columnSelect = ret->createOptionalParameter(3, "-column", "select a single column"); columnSelect->addStringParameter(1, "column", "the column number or name"); OptionalParameter* posMinMaxPercent = ret->createOptionalParameter(4, "-pos-percent", "percentage min/max for positive data coloring"); posMinMaxPercent->addDoubleParameter(1, "pos-min-%", "the percentile for the least positive data"); posMinMaxPercent->addDoubleParameter(2, "pos-max-%", "the percentile for the most positive data"); OptionalParameter* negMinMaxPercent = ret->createOptionalParameter(5, "-neg-percent", "percentage min/max for negative data coloring"); negMinMaxPercent->addDoubleParameter(1, "neg-min-%", "the percentile for the least negative data"); negMinMaxPercent->addDoubleParameter(2, "neg-max-%", "the percentile for the most negative data"); OptionalParameter* posMinMaxValue = ret->createOptionalParameter(11, "-pos-user", "user min/max values for positive data coloring"); posMinMaxValue->addDoubleParameter(1, "pos-min-user", "the value for the least positive data"); posMinMaxValue->addDoubleParameter(2, "pos-max-user", "the value for the most positive data"); OptionalParameter* negMinMaxValue = ret->createOptionalParameter(12, "-neg-user", "user min/max values for negative data coloring"); negMinMaxValue->addDoubleParameter(1, "neg-min-user", "the value for the least negative data"); negMinMaxValue->addDoubleParameter(2, "neg-max-user", "the value for the most negative data"); OptionalParameter* interpolate = ret->createOptionalParameter(9, "-interpolate", "interpolate colors"); interpolate->addBooleanParameter(1, "interpolate", "boolean, whether to interpolate"); OptionalParameter* displayPositive = ret->createOptionalParameter(6, "-disp-pos", "display positive data"); displayPositive->addBooleanParameter(1, "display", "boolean, whether to display"); OptionalParameter* displayNegative = ret->createOptionalParameter(7, "-disp-neg", "display positive data"); displayNegative->addBooleanParameter(1, "display", "boolean, whether to display"); OptionalParameter* displayZero = ret->createOptionalParameter(8, "-disp-zero", "display data closer to zero than the min cutoff"); displayZero->addBooleanParameter(1, "display", "boolean, whether to display"); OptionalParameter* paletteName = ret->createOptionalParameter(10, "-palette-name", "set the palette used"); paletteName->addStringParameter(1, "name", "the name of the palette"); OptionalParameter* thresholdOpt = ret->createOptionalParameter(13, "-thresholding", "set the thresholding"); thresholdOpt->addStringParameter(1, "type", "thresholding setting"); thresholdOpt->addStringParameter(2, "test", "show values inside or outside thresholds"); thresholdOpt->addDoubleParameter(3, "min", "lower threshold"); thresholdOpt->addDoubleParameter(4, "max", "upper threshold"); 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"; vector myEnums; PaletteScaleModeEnum::getAllEnums(myEnums); for (int i = 0; i < (int)myEnums.size(); ++i) { myText += PaletteScaleModeEnum::toName(myEnums[i]) + "\n"; } myText += "\nThe argument to -palette-name must be one of the following:\n\n"; PaletteFile myPF; int32_t numPalettes = myPF.getNumberOfPalettes(); for (int i = 0; i < numPalettes; ++i) { myText += myPF.getPalette(i)->getName() + "\n"; } myText += "\nThe argument to -thresholding must be one of the following:\n\n"; vector myEnums2; PaletteThresholdTypeEnum::getAllEnums(myEnums2); for (int i = 0; i < (int)myEnums2.size(); ++i) { myText += PaletteThresholdTypeEnum::toName(myEnums2[i]) + "\n"; } myText += "\nThe argument to -thresholding must be one of the following:\n\n"; vector myEnums3; PaletteThresholdTestEnum::getAllEnums(myEnums3); for (int i = 0; i < (int)myEnums3.size(); ++i) { myText += PaletteThresholdTestEnum::toName(myEnums3[i]) + "\n"; } ret->setHelpText(myText); return ret; } void OperationMetricPalette::useParameters(OperationParameters* myParams, ProgressObject* myProgObj) { LevelProgress myProgress(myProgObj); AString myMetricName = myParams->getString(1); AString myModeName = myParams->getString(2); bool ok = false; PaletteScaleModeEnum::Enum myMode = PaletteScaleModeEnum::fromName(myModeName, &ok); if (!ok) { throw OperationException("unknown mapping mode"); } MetricFile myMetric; myMetric.readFile(myMetricName); int myColumn = -1; OptionalParameter* columnSelect = myParams->getOptionalParameter(3); if (columnSelect->m_present) { myColumn = (int)myMetric.getMapIndexFromNameOrNumber(columnSelect->getString(1)); if (myColumn < 0 || myColumn >= myMetric.getNumberOfColumns()) { throw OperationException("invalid column specified"); } } PaletteColorMapping myMapping = *(myMetric.getMapPaletteColorMapping(0));//create the mapping, then use operator= to set for all requested columns, take defaults from first map myMapping.setScaleMode(myMode); OptionalParameter* posMinMaxPercent = myParams->getOptionalParameter(4); if (posMinMaxPercent->m_present) { myMapping.setAutoScalePercentagePositiveMinimum(posMinMaxPercent->getDouble(1)); myMapping.setAutoScalePercentagePositiveMaximum(posMinMaxPercent->getDouble(2)); } OptionalParameter* negMinMaxPercent = myParams->getOptionalParameter(5); if (negMinMaxPercent->m_present) { myMapping.setAutoScalePercentageNegativeMinimum(negMinMaxPercent->getDouble(1)); myMapping.setAutoScalePercentageNegativeMaximum(negMinMaxPercent->getDouble(2)); } OptionalParameter* posMinMaxValue = myParams->getOptionalParameter(11); if (posMinMaxValue->m_present) { myMapping.setUserScalePositiveMinimum(posMinMaxValue->getDouble(1)); myMapping.setUserScalePositiveMaximum(posMinMaxValue->getDouble(2)); } OptionalParameter* negMinMaxValue = myParams->getOptionalParameter(12); if (negMinMaxValue->m_present) { myMapping.setUserScaleNegativeMinimum(negMinMaxValue->getDouble(1)); myMapping.setUserScaleNegativeMaximum(negMinMaxValue->getDouble(2)); } OptionalParameter* displayPositive = myParams->getOptionalParameter(6); if (displayPositive->m_present) { myMapping.setDisplayPositiveDataFlag(displayPositive->getBoolean(1)); } OptionalParameter* displayNegative = myParams->getOptionalParameter(7); if (displayNegative->m_present) { myMapping.setDisplayNegativeDataFlag(displayNegative->getBoolean(1)); } OptionalParameter* displayZero = myParams->getOptionalParameter(8); if (displayZero->m_present) { myMapping.setDisplayZeroDataFlag(displayZero->getBoolean(1)); } OptionalParameter* interpolate = myParams->getOptionalParameter(9); if (interpolate->m_present) { myMapping.setInterpolatePaletteFlag(interpolate->getBoolean(1)); } OptionalParameter* paletteName = myParams->getOptionalParameter(10); if (paletteName->m_present) { myMapping.setSelectedPaletteName(paletteName->getString(1)); } OptionalParameter* thresholdOpt = myParams->getOptionalParameter(13); if (thresholdOpt->m_present) { bool ok = false; PaletteThresholdTypeEnum::Enum mytype = PaletteThresholdTypeEnum::fromName(thresholdOpt->getString(1), &ok); if (!ok) throw OperationException("unrecognized threshold type string: " + thresholdOpt->getString(1)); PaletteThresholdTestEnum::Enum mytest = PaletteThresholdTestEnum::fromName(thresholdOpt->getString(2), &ok); if (!ok) throw OperationException("unrecognized threshold test string: " + thresholdOpt->getString(2)); myMapping.setThresholdType(mytype); myMapping.setThresholdTest(mytest); myMapping.setThresholdMinimum(mytype, thresholdOpt->getDouble(3)); myMapping.setThresholdMaximum(mytype, thresholdOpt->getDouble(4)); } if (myColumn == -1) { for (int i = 0; i < myMetric.getNumberOfMaps(); ++i) { *(myMetric.getMapPaletteColorMapping(i)) = myMapping; } } else { *(myMetric.getMapPaletteColorMapping(myColumn)) = myMapping; } myMetric.writeFile(myMetricName); } connectome-workbench-1.2.3+git41-gc4c6c90/src/Operations/OperationMetricPalette.h000066400000000000000000000026171300200146000274710ustar00rootroot00000000000000#ifndef __OPERATION_METRIC_PALETTE_H__ #define __OPERATION_METRIC_PALETTE_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 "AbstractOperation.h" namespace caret { class OperationMetricPalette : public AbstractOperation { public: static OperationParameters* getParameters(); static void useParameters(OperationParameters* myParams, ProgressObject* myProgObj); static AString getCommandSwitch(); static AString getShortDescription(); }; typedef TemplateAutoOperation AutoOperationMetricPalette; } #endif //__OPERATION_METRIC_PALETTE_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Operations/OperationMetricStats.cxx000066400000000000000000000225121300200146000275400ustar00rootroot00000000000000/*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 "OperationMetricStats.h" #include "OperationException.h" #include "MetricFile.h" #include "ReductionOperation.h" #include #include #include #include #include #include using namespace caret; using namespace std; AString OperationMetricStats::getCommandSwitch() { return "-metric-stats"; } AString OperationMetricStats::getShortDescription() { return "SPATIAL STATISTICS ON A METRIC FILE"; } OperationParameters* OperationMetricStats::getParameters() { OperationParameters* ret = new OperationParameters(); ret->addMetricParameter(1, "metric-in", "the input metric"); OptionalParameter* reduceOpt = ret->createOptionalParameter(2, "-reduce", "use a reduction operation"); reduceOpt->addStringParameter(1, "operation", "the reduction operation"); OptionalParameter* percentileOpt = ret->createOptionalParameter(3, "-percentile", "give the value at a percentile"); percentileOpt->addDoubleParameter(1, "percent", "the percentile to find"); OptionalParameter* columnOpt = ret->createOptionalParameter(4, "-column", "only display output for one column"); columnOpt->addStringParameter(1, "column", "the column number or name"); OptionalParameter* roiOpt = ret->createOptionalParameter(5, "-roi", "only consider data inside an roi"); roiOpt->addMetricParameter(1, "roi-metric", "the roi, as a metric file"); roiOpt->createOptionalParameter(2, "-match-maps", "each column of input uses the corresponding column from the roi file"); ret->createOptionalParameter(6, "-show-map-name", "print map 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. " + "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()); return ret; } namespace { float reduce(const float* data, const int& numNodes, const ReductionEnum::Enum& myop, const float* roiData) { if (roiData == NULL) { return ReductionOperation::reduce(data, numNodes, myop); } else { vector toUse; toUse.reserve(numNodes); for (int i = 0; i < numNodes; ++i) { if (roiData[i] > 0.0f) { toUse.push_back(data[i]); } } if (toUse.empty()) throw OperationException("roi contains no vertices"); return ReductionOperation::reduce(toUse.data(), toUse.size(), myop); } } float percentile(const float* data, const int& numNodes, const float& percent, const float* roiData) { CaretAssert(percent >= 0.0f && percent <= 100.0f); vector toUse; if (roiData == NULL) { toUse = vector(data, data + numNodes); } else { toUse.reserve(numNodes); for (int i = 0; i < numNodes; ++i) { if (roiData[i] > 0.0f) { toUse.push_back(data[i]); } } } if (toUse.empty()) throw OperationException("roi contains no vertices"); sort(toUse.begin(), toUse.end()); const float index = percent / 100.0f * (toUse.size() - 1); if (index <= 0) return toUse[0]; if (index >= toUse.size() - 1) return toUse.back(); float ipart, fpart; fpart = modf(index, &ipart); return (1.0f - fpart) * toUse[(int)ipart] + fpart * toUse[((int)ipart) + 1]; } } void OperationMetricStats::useParameters(OperationParameters* myParams, ProgressObject* myProgObj) { LevelProgress myProgress(myProgObj); MetricFile* input = myParams->getMetric(1); int numNodes = input->getNumberOfNodes(); int numCols = input->getNumberOfColumns(); OptionalParameter* reduceOpt = myParams->getOptionalParameter(2); OptionalParameter* percentileOpt = myParams->getOptionalParameter(3); if (reduceOpt->m_present == percentileOpt->m_present)//use == as logical xor { throw OperationException("you must use exactly one of -reduce or -percentile"); } ReductionEnum::Enum myop = ReductionEnum::INVALID; if (reduceOpt->m_present) { bool ok = false; myop = ReductionEnum::fromName(reduceOpt->getString(1), &ok); if (!ok) throw OperationException("unrecognized reduction operation: " + reduceOpt->getString(1)); } float percent = 0.0f; if (percentileOpt->m_present) { percent = (float)percentileOpt->getDouble(1);//use not within range to trap NaNs, just in case if (!(percent >= 0.0f && percent <= 100.0f)) throw OperationException("percentile must be between 0 and 100"); } int column = -1; OptionalParameter* columnOpt = myParams->getOptionalParameter(4); if (columnOpt->m_present) { column = input->getMapIndexFromNameOrNumber(columnOpt->getString(1)); if (column < 0) throw OperationException("invalid column specified"); } bool matchColumnMode = false; MetricFile* myRoi = NULL; const float* roiData = NULL; OptionalParameter* roiOpt = myParams->getOptionalParameter(5); if (roiOpt->m_present) { myRoi = roiOpt->getMetric(1); 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) { 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; 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; } } 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) { roiData = myRoi->getValuePointerForColumn(i); } 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; } } } 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; } } } connectome-workbench-1.2.3+git41-gc4c6c90/src/Operations/OperationMetricStats.h000066400000000000000000000026031300200146000271640ustar00rootroot00000000000000#ifndef __OPERATION_METRIC_STATS_H__ #define __OPERATION_METRIC_STATS_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 "AbstractOperation.h" namespace caret { class OperationMetricStats : public AbstractOperation { public: static OperationParameters* getParameters(); static void useParameters(OperationParameters* myParams, ProgressObject* myProgObj); static AString getCommandSwitch(); static AString getShortDescription(); }; typedef TemplateAutoOperation AutoOperationMetricStats; } #endif //__OPERATION_METRIC_STATS_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Operations/OperationMetricVertexSum.cxx000066400000000000000000000162611300200146000304100ustar00rootroot00000000000000/*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 "OperationMetricVertexSum.h" #include "OperationException.h" #include "MetricFile.h" #include "SurfaceFile.h" #include #include #include using namespace caret; using namespace std; AString OperationMetricVertexSum::getCommandSwitch() { return "-metric-vertex-sum"; } AString OperationMetricVertexSum::getShortDescription() { return "DEPRECATED: use -metric-stats or -metric-weighted-stats"; } OperationParameters* OperationMetricVertexSum::getParameters() { OperationParameters* ret = new OperationParameters(); ret->addMetricParameter(1, "metric-in", "the metric to sum"); OptionalParameter* integrateOpt = ret->createOptionalParameter(2, "-integrate", "integrate across a surface, rather than summing"); integrateOpt->addSurfaceParameter(1, "surface", "the surface to integrate on"); OptionalParameter* integrateMetricOpt = ret->createOptionalParameter(3, "-integrate-metric", "integrate using vertex areas from a metric file"); integrateMetricOpt->addMetricParameter(1, "area-metric", "metric file containing vertex areas"); OptionalParameter* roiOpt = ret->createOptionalParameter(4, "-roi", "only use data inside an roi"); roiOpt->addMetricParameter(1, "roi-metric", "the roi, as a metric file"); OptionalParameter* columnOpt = ret->createOptionalParameter(5, "-column", "select a single column"); columnOpt->addStringParameter(1, "column", "the column number or name"); ret->setHelpText( AString("DEPRECATED: this command may be removed in a future release, use -metric-stats or -metric-weighted-stats.\n\n") + "For each column in , sum the values across all vertices, then print the sum on standard output. " + "-integrate and -integrate-metric multiply each vertex value by the vertex area before doing the sum. " + "Only one of -integrate and -integrate-metric may be specified. " + "Use -roi to only sum within a specific area. " + "Use -column to only report for one column. " ); return ret; } void OperationMetricVertexSum::useParameters(OperationParameters* myParams, ProgressObject* myProgObj) { LevelProgress myProgress(myProgObj); MetricFile* myMetric = myParams->getMetric(1); int numNodes = myMetric->getNumberOfNodes(); const float* integrateData = NULL; vector surfNodeAreas; OptionalParameter* integrateOpt = myParams->getOptionalParameter(2); if (integrateOpt->m_present) { SurfaceFile* integrateSurf = integrateOpt->getSurface(1); if (integrateSurf->getNumberOfNodes() != numNodes) throw OperationException("integration surface has the wrong number of vertices"); integrateSurf->computeNodeAreas(surfNodeAreas); integrateData = surfNodeAreas.data(); } OptionalParameter* integrateMetricOpt = myParams->getOptionalParameter(3); if (integrateMetricOpt->m_present) { if (integrateOpt->m_present) throw OperationException("only one of -integrate and -integrate-metric can be specified"); MetricFile* integrateMetric = integrateMetricOpt->getMetric(1);//we don't need to keep a pointer to it if (integrateMetric->getNumberOfNodes() != numNodes) throw OperationException("integration metric has the wrong number of vertices"); integrateData = integrateMetric->getValuePointerForColumn(0);//just its data } MetricFile* myRoi = NULL; OptionalParameter* roiOpt = myParams->getOptionalParameter(4); if (roiOpt->m_present) { myRoi = roiOpt->getMetric(1); if (myRoi->getNumberOfNodes() != numNodes) throw OperationException("roi-metric has the wrong number of vertices"); } int myColumn = -1; OptionalParameter* columnOpt = myParams->getOptionalParameter(5); if (columnOpt->m_present) { myColumn = myMetric->getMapIndexFromNameOrNumber(columnOpt->getString(1)); if (myColumn < 0 || myColumn >= myMetric->getNumberOfMaps()) { throw OperationException("invalid column specified"); } } const float* roiData = NULL; if (myRoi != NULL) roiData = myRoi->getValuePointerForColumn(0); if (integrateData != NULL) { if (myColumn == -1) { int numCols = myMetric->getNumberOfColumns(); for (int j = 0; j < numCols; ++j) { double accum = 0.0; const float* data = myMetric->getValuePointerForColumn(j); for (int i = 0; i < numNodes; ++i) { if (roiData == NULL || roiData[i] > 0.0f) { accum += data[i] * integrateData[i]; } } stringstream s; s << std::setprecision(7) << accum; cout << s.str() << endl; } } else { double accum = 0.0; const float* data = myMetric->getValuePointerForColumn(myColumn); for (int i = 0; i < numNodes; ++i) { if (roiData == NULL || roiData[i] > 0.0f) { accum += data[i] * integrateData[i]; } } stringstream s; s << std::setprecision(7) << accum; cout << s.str() << endl; } } else { if (myColumn == -1) { int numCols = myMetric->getNumberOfColumns(); for (int j = 0; j < numCols; ++j) { double accum = 0.0; const float* data = myMetric->getValuePointerForColumn(j); for (int i = 0; i < numNodes; ++i) { if (roiData == NULL || roiData[i] > 0.0f) { accum += data[i]; } } stringstream s; s << std::setprecision(7) << accum; cout << s.str() << endl; } } else { double accum = 0.0; const float* data = myMetric->getValuePointerForColumn(myColumn); for (int i = 0; i < numNodes; ++i) { if (roiData == NULL || roiData[i] > 0.0f) { accum += data[i]; } } stringstream s; s << std::setprecision(7) << accum; cout << s.str() << endl; } } } connectome-workbench-1.2.3+git41-gc4c6c90/src/Operations/OperationMetricVertexSum.h000066400000000000000000000026361300200146000300360ustar00rootroot00000000000000#ifndef __OPERATION_METRIC_VERTEX_SUM_H__ #define __OPERATION_METRIC_VERTEX_SUM_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 "AbstractOperation.h" namespace caret { class OperationMetricVertexSum : public AbstractOperation { public: static OperationParameters* getParameters(); static void useParameters(OperationParameters* myParams, ProgressObject* myProgObj); static AString getCommandSwitch(); static AString getShortDescription(); }; typedef TemplateAutoOperation AutoOperationMetricVertexSum; } #endif //__OPERATION_METRIC_VERTEX_SUM_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Operations/OperationMetricWeightedStats.cxx000066400000000000000000000333331300200146000312240ustar00rootroot00000000000000/*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 "OperationMetricWeightedStats.h" #include "OperationException.h" #include "CaretHeap.h" #include "MetricFile.h" #include "SurfaceFile.h" #include #include #include #include #include using namespace caret; using namespace std; AString OperationMetricWeightedStats::getCommandSwitch() { return "-metric-weighted-stats"; } AString OperationMetricWeightedStats::getShortDescription() { return "WEIGHTED SPATIAL STATISTICS ON A METRIC FILE"; } OperationParameters* OperationMetricWeightedStats::getParameters() { OperationParameters* ret = new OperationParameters(); ret->addMetricParameter(1, "metric-in", "the input metric"); OptionalParameter* areaSurfOpt = ret->createOptionalParameter(2, "-area-surface", "use vertex areas as weights"); areaSurfOpt->addSurfaceParameter(1, "area-surface", "the surface to use for vertex areas"); OptionalParameter* weightMetricOpt = ret->createOptionalParameter(3, "-weight-metric", "use weights from a metric file"); weightMetricOpt->addMetricParameter(1, "weight-metric", "metric file containing the weights"); OptionalParameter* columnOpt = ret->createOptionalParameter(4, "-column", "only display output for one column"); columnOpt->addStringParameter(1, "column", "the column number or name"); OptionalParameter* roiOpt = ret->createOptionalParameter(5, "-roi", "only consider data inside an roi"); roiOpt->addMetricParameter(1, "roi-metric", "the roi, as a metric file"); roiOpt->createOptionalParameter(2, "-match-maps", "each column of input uses the corresponding column from the roi file"); ret->createOptionalParameter(6, "-mean", "compute weighted mean"); OptionalParameter* stdevOpt = ret->createOptionalParameter(7, "-stdev", "compute weighted standard deviation"); stdevOpt->createOptionalParameter(1, "-sample", "estimate population stdev from the sample"); OptionalParameter* percentileOpt = ret->createOptionalParameter(8, "-percentile", "compute weighted percentile"); percentileOpt->addDoubleParameter(1, "percent", "the percentile to find"); ret->createOptionalParameter(9, "-sum", "compute weighted sum"); ret->createOptionalParameter(10, "-show-map-name", "print map index and name before each output"); ret->setHelpText( AString("For each column of the input, a single number is printed, resulting from the specified operation. ") + "You must specify exactly one of -area-surface or -weight-metric. " + "Use -column to only give output for a single column. " + "Use -roi to consider only the data within a region. " + "Exactly one of -mean, -stdev, -percentile or -sum must be specified.\n\n" + "Using -sum with -area-surface (or -weight-metric with a metric containing similar data) is equivalent to integrating with respect to surface area. " + "For example, if you want to find the surface area within an roi, do this:\n\n" + "$ wb_command -metric-weighted-stats roi.func.gii -sum -area-surface midthickness.surf.gii" ); return ret; } namespace { enum OperationType { MEAN, STDEV, SAMPSTDEV, PERCENTILE, SUM }; float doOperation(const float* data, const float* weights, const int& numNodes, const OperationType& myop, const float* roiData, const float& argument) {//argument is only used for percentile currently const float* useData = data, *useWeights = weights; int numUse = numNodes; vector dataScratch, weightScratch;//for when we have an ROI if (roiData != NULL) { dataScratch.reserve(numNodes); weightScratch.reserve(numNodes); for (int i = 0; i < numNodes; ++i) { if (roiData[i] > 0.0f) { dataScratch.push_back(data[i]); weightScratch.push_back(weights[i]); } } if (dataScratch.size() < 1) throw OperationException("roi contains no vertices"); useData = dataScratch.data(); useWeights = weightScratch.data(); numUse = (int)dataScratch.size(); } switch(myop) { case SUM: case MEAN: case STDEV: case SAMPSTDEV://these all start the same way { double accum = 0.0, weightsum = 0.0; for (int i = 0; i < numUse; ++i) { accum += useData[i] * useWeights[i]; weightsum += useWeights[i]; } if (myop == SUM) return accum; const float mean = accum / weightsum; if (myop == MEAN) return mean; accum = 0.0; double weightsum2 = 0.0;//for weighted sample stdev for (int i = 0; i < numUse; ++i) { float tempf = useData[i] - mean; accum += useWeights[i] * tempf * tempf; weightsum2 += useWeights[i] * useWeights[i]; } if (myop == STDEV) return sqrt(accum / weightsum); CaretAssert(myop == SAMPSTDEV); if (numUse < 2) throw OperationException("sample standard deviation requires at least 2 elements in the roi"); return sqrt(accum / (weightsum - weightsum2 / weightsum));//http://en.wikipedia.org/wiki/Weighted_arithmetic_mean#Weighted_sample_variance } case PERCENTILE: { CaretAssert(argument >= 0.0f && argument <= 100.0f); if (numUse == 1) return useData[0];//would need special handling anyway, so get it early CaretSimpleMinHeap sorter; double weightaccum = 0.0;//double will usually prevent adding weights in a different order from getting a different answer for (int i = 0; i < numUse; ++i) { if (useWeights[i] < 0.0f) throw OperationException("negative weights not allowed in weighted percentile"); weightaccum += useWeights[i]; sorter.push(useWeights[i], useData[i]);//sort by value } float targetWeight = argument / 100.0f * weightaccum; float lastData, nextData; float lastWeight = sorter.pop(&lastData); weightaccum = lastWeight; float nextWeight = sorter.top(&nextData); int position = 1;//because the first and last sections get special treatment to not have flat ends on the function while (weightaccum + nextWeight * 0.5f < targetWeight && sorter.size() > 1) { ++position; sorter.pop(); weightaccum += nextWeight; lastWeight = nextWeight; lastData = nextData; nextWeight = sorter.top(&nextData); } if (targetWeight < weightaccum) { if (position == 1) {//stretch interpolation at first position to the edge return lastData + (nextData - lastData) * 0.5f * ((targetWeight - weightaccum) / lastWeight + 1.0f); } else { return lastData + (nextData - lastData) * 0.5f * ((targetWeight - weightaccum) / (lastWeight * 0.5f) + 1.0f); } } else { if (position == numUse - 1) {//ditto return (lastData + nextData) * 0.5f + (nextData - lastData) * 0.5f * (targetWeight - weightaccum) / nextWeight; } else { return (lastData + nextData) * 0.5f + (nextData - lastData) * 0.5f * (targetWeight - weightaccum) / (nextWeight * 0.5f); } } } } CaretAssert(false);//make sure execution never actually reaches end of function throw OperationException("internal error in weighted stats"); } } void OperationMetricWeightedStats::useParameters(OperationParameters* myParams, ProgressObject* myProgObj) { LevelProgress myProgress(myProgObj); MetricFile* input = myParams->getMetric(1); int numNodes = input->getNumberOfNodes(); int numCols = input->getNumberOfColumns(); vector areaData; const float* useWeights = NULL; OptionalParameter* areaSurfOpt = myParams->getOptionalParameter(2); if (areaSurfOpt->m_present) { SurfaceFile* mySurf = areaSurfOpt->getSurface(1); if (mySurf->getNumberOfNodes() != numNodes) throw OperationException("area surface has different number of vertices than input metric"); mySurf->computeNodeAreas(areaData); useWeights = areaData.data(); } OptionalParameter* weightMetricOpt = myParams->getOptionalParameter(3); if (weightMetricOpt->m_present) { if (useWeights != NULL) throw OperationException("you may not specify both -area-surface and -weight-metric"); MetricFile* myWeights = weightMetricOpt->getMetric(1); if (myWeights->getNumberOfNodes() != numNodes) throw OperationException("weight metric has different number of vertices than input metric"); useWeights = myWeights->getValuePointerForColumn(0); } if (useWeights == NULL) throw OperationException("you must specify either -area-surface or -weight-metric"); int column = -1; OptionalParameter* columnOpt = myParams->getOptionalParameter(4); if (columnOpt->m_present) { column = input->getMapIndexFromNameOrNumber(columnOpt->getString(1)); if (column < 0) throw OperationException("invalid column specified"); } bool matchColumnMode = false; MetricFile* myRoi = NULL; const float* roiData = NULL; OptionalParameter* roiOpt = myParams->getOptionalParameter(5); if (roiOpt->m_present) { myRoi = roiOpt->getMetric(1); 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) { throw OperationException("-match-maps specified, but roi has different number of columns than input"); } matchColumnMode = true; } else { roiData = myRoi->getValuePointerForColumn(0); } } bool haveOp = false; OperationType myop; if (myParams->getOptionalParameter(6)->m_present) { haveOp = true; myop = MEAN; } OptionalParameter* stdevOpt = myParams->getOptionalParameter(7); if (stdevOpt->m_present) { if (haveOp) throw OperationException("you may only specify one operation"); haveOp = true; if (stdevOpt->getOptionalParameter(1)->m_present) { myop = SAMPSTDEV; } else { myop = STDEV; } } float argument = -1.0f; OptionalParameter* percentileOpt = myParams->getOptionalParameter(8); if (percentileOpt->m_present) { if (haveOp) throw OperationException("you may only specify one operation"); haveOp = true; myop = PERCENTILE; argument = percentileOpt->getDouble(1); if (!(argument >= 0.0f && argument <= 100.0f)) throw OperationException("percentile must be between 0 and 100"); } if (myParams->getOptionalParameter(9)->m_present) { if (haveOp) throw OperationException("you may only specify one operation"); haveOp = true; myop = SUM; } if (!haveOp) throw OperationException("you must specify an operation"); bool showMapName = myParams->getOptionalParameter(10)->m_present; 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); } 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); } 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; } } connectome-workbench-1.2.3+git41-gc4c6c90/src/Operations/OperationMetricWeightedStats.h000066400000000000000000000026661300200146000306560ustar00rootroot00000000000000#ifndef __OPERATION_METRIC_WEIGHTED_STATS_H__ #define __OPERATION_METRIC_WEIGHTED_STATS_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 "AbstractOperation.h" namespace caret { class OperationMetricWeightedStats : public AbstractOperation { public: static OperationParameters* getParameters(); static void useParameters(OperationParameters* myParams, ProgressObject* myProgObj); static AString getCommandSwitch(); static AString getShortDescription(); }; typedef TemplateAutoOperation AutoOperationMetricWeightedStats; } #endif //__OPERATION_METRIC_WEIGHTED_STATS_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Operations/OperationNiftiInformation.cxx000066400000000000000000000133321300200146000305550ustar00rootroot00000000000000/*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 "OperationNiftiInformation.h" #include "OperationException.h" #include "CiftiFile.h" #include "NiftiIO.h" #include "CiftiXML.h" #include "DataFileTypeEnum.h" #include using namespace caret; using namespace std; AString OperationNiftiInformation::getCommandSwitch() { return "-nifti-information"; } AString OperationNiftiInformation::getShortDescription() { return "DISPLAY INFORMATION ABOUT A NIFTI/CIFTI FILE"; } 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"); ret->createOptionalParameter(3, "-print-matrix", "output the values in the matrix (cifti only)"); OptionalParameter* printXmlOpt = ret->createOptionalParameter(4, "-print-xml", "print the cifti XML (cifti only)"); OptionalParameter* pxVersionOpt = printXmlOpt->createOptionalParameter(1, "-version", "convert the XML to a specific CIFTI version (default is the file's cifti version)"); pxVersionOpt->addStringParameter(1, "version", "the CIFTI version to use"); ret->setHelpText( AString("You must specify at least one -print-* option.") ); return ret; } void OperationNiftiInformation::useParameters(OperationParameters* myParams, ProgressObject* myProgObj) { LevelProgress myProgress(myProgObj); const AString fileName = myParams->getString(1); bool printHeader = myParams->getOptionalParameter(2)->m_present; bool printMatrix = myParams->getOptionalParameter(3)->m_present; OptionalParameter* printXmlOpt = myParams->getOptionalParameter(4); bool printXml = printXmlOpt->m_present; if (!printHeader && !printMatrix && !printXml) throw OperationException("you must specify a -print-* option"); if(!QFile::exists(fileName)) throw OperationException("File '" + fileName + "' does not exist."); if(printHeader) { NiftiIO myIO; myIO.openRead(fileName); cout << myIO.getHeader().toString() << endl; cout << getMemorySizeAsString(myIO.getDimensions()) << endl; } if(printXml) { CiftiFile cf(fileName); const CiftiXML& xml = cf.getCiftiXML(); CiftiVersion printVersion = xml.getParsedVersion();//by default, rewrite with the same version that it was read with OptionalParameter* pxVersionOpt = printXmlOpt->getOptionalParameter(1); if (pxVersionOpt->m_present) { printVersion = CiftiVersion(pxVersionOpt->getString(1)); } AString xmlString = xml.writeXMLToString(printVersion); cout << xmlString << endl; } if(printMatrix) { CiftiFile cf(fileName); if (cf.getCiftiXML().getNumberOfDimensions() != 2) throw OperationException("-print-matrix only supports 2D cifti"); int64_t dim0 = cf.getNumberOfRows(); int64_t dim1 = cf.getNumberOfColumns(); vector row(dim1); AString rowString; for(int64_t i = 0;i& dimensions) { AString str; int64_t numVoxelComponents = 0; const int64_t numDims = static_cast(dimensions.size()); if (numDims > 0) { numVoxelComponents = 1; for (int64_t i = 0; i < numDims; i++) { CaretAssertVectorIndex(dimensions, i); numVoxelComponents *= dimensions[i]; } } const int64_t numberOfBytes = sizeof(float) * numVoxelComponents; str += ("Data Size in (float) memory, Bytes: " + AString::number(numberOfBytes)); const double oneKilobyte = 1024.0; const double kilobytes = numberOfBytes / oneKilobyte; const double oneMegabyte = 1048576.0; const double megabytes = numberOfBytes / oneMegabyte; const double oneGigabyte = 1073741824.0; const double gigabytes = numberOfBytes / oneGigabyte; const double oneTerabyte = 1099511627776.0; const double terabytes = numberOfBytes / oneTerabyte; if (terabytes >= 1.0) { str += (" Terabytes: " + AString::number(terabytes)); } else if (gigabytes >= 1.0) { str += (" Gigabytes: " + AString::number(gigabytes)); } else if (megabytes >= 1.0) { str += (" Megabytes: " + AString::number(megabytes)); } else if (kilobytes >= 1.0) { str += (" Kilobytes: " + AString::number(kilobytes)); } return str; } connectome-workbench-1.2.3+git41-gc4c6c90/src/Operations/OperationNiftiInformation.h000066400000000000000000000030041300200146000301750ustar00rootroot00000000000000#ifndef __OPERATION_NIFTI_INFORMATION_H__ #define __OPERATION_NIFTI_INFORMATION_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 "AbstractOperation.h" namespace caret { class OperationNiftiInformation : public AbstractOperation { public: static OperationParameters* getParameters(); static void useParameters(OperationParameters* myParams, ProgressObject* myProgObj); static AString getCommandSwitch(); static AString getShortDescription(); private: static AString getMemorySizeAsString(const std::vector& dimensions); }; typedef TemplateAutoOperation AutoOperationNiftiInformation; } #endif //__OPERATION_NIFTI_INFORMATION_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Operations/OperationProbtrackXDotConvert.cxx000066400000000000000000000516221300200146000313710ustar00rootroot00000000000000/*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 "OperationProbtrackXDotConvert.h" #include "OperationException.h" #include "CaretLogger.h" #include "CiftiFile.h" #include "MetricFile.h" #include "StructureEnum.h" #include "VolumeFile.h" #include #include #include #include using namespace caret; using namespace std; //specifically for the purpose of sorting the input .dot file to the order required, in case it isn't initially ordered correctly struct SparseValue { int32_t index[2];//save some memory float value; bool operator<(const SparseValue& rhs) const { return (index[1] < rhs.index[1]);//NOTE: this specifically avoids minor ordering by the other index to make the sort faster (less swapping because more "equals" cases) } }; AString OperationProbtrackXDotConvert::getCommandSwitch() { return "-probtrackx-dot-convert"; } AString OperationProbtrackXDotConvert::getShortDescription() { return "CONVERT A .DOT FILE FROM PROBTRACKX TO CIFTI"; } OperationParameters* OperationProbtrackXDotConvert::getParameters() { OperationParameters* ret = new OperationParameters(); ret->addStringParameter(1, "dot-file", "input .dot file"); ret->addCiftiOutputParameter(2, "cifti-out", "output cifti file"); OptionalParameter* rowVoxelOpt = ret->createOptionalParameter(3, "-row-voxels", "the output mapping along a row will be voxels"); rowVoxelOpt->addStringParameter(1, "voxel-list-file", "a text file containing IJK indices for the voxels used"); rowVoxelOpt->addVolumeParameter(2, "label-vol", "a label volume with the dimensions and sform used, with structure labels"); OptionalParameter* rowSurfaceOpt = ret->createOptionalParameter(4, "-row-surface", "the output mapping along a row will be surface vertices"); rowSurfaceOpt->addMetricParameter(1, "roi-metric", "a metric file with positive values on all vertices used"); OptionalParameter* rowCiftiOpt = ret->createOptionalParameter(9, "-row-cifti", "take the mapping along a row from a cifti file"); rowCiftiOpt->addCiftiParameter(1, "cifti", "the cifti file to take the mapping from"); rowCiftiOpt->addStringParameter(2, "direction", "which dimension to take the mapping along, ROW or COLUMN"); OptionalParameter* colVoxelOpt = ret->createOptionalParameter(5, "-col-voxels", "the output mapping along a column will be voxels"); colVoxelOpt->addStringParameter(1, "voxel-list-file", "a text file containing IJK indices for the voxels used"); colVoxelOpt->addVolumeParameter(2, "label-vol", "a label volume with the dimensions and sform used, with structure labels"); OptionalParameter* colSurfaceOpt = ret->createOptionalParameter(6, "-col-surface", "the output mapping along a column will be surface vertices"); colSurfaceOpt->addMetricParameter(1, "roi-metric", "a metric file with positive values on all vertices used"); OptionalParameter* colCiftiOpt = ret->createOptionalParameter(10, "-col-cifti", "take the mapping along a column from a cifti file"); colCiftiOpt->addCiftiParameter(1, "cifti", "the cifti file to take the mapping from"); colCiftiOpt->addStringParameter(2, "direction", "which dimension to take the mapping along, ROW or COLUMN"); ret->createOptionalParameter(7, "-transpose", "transpose the input matrix"); ret->createOptionalParameter(8, "-make-symmetric", "transform half-square input into full matrix output"); AString myText = AString("NOTE: exactly one -row option and one -col option must be used.\n\n") + "If the input file does not have its indexes sorted in the correct ordering, this command may take longer than expected. " + "Specifying -transpose will transpose the input matrix before trying to put its values into the cifti file, which is currently needed for at least matrix2 " + "in order to display it as intended. " + "How the cifti file is displayed is based on which -row option is specified: if -row-voxels is specified, then it will display data on volume slices. " + "The label names in the label volume(s) must have the following names, other names are ignored:\n\n"; vector myStructureEnums; StructureEnum::getAllEnums(myStructureEnums); for (int i = 0; i < (int)myStructureEnums.size(); ++i) { myText += "\n" + StructureEnum::toName(myStructureEnums[i]); } ret->setHelpText(myText); return ret; } void OperationProbtrackXDotConvert::useParameters(OperationParameters* myParams, ProgressObject* myProgObj) { LevelProgress myProgress(myProgObj); AString dotFileName = myParams->getString(1); CiftiFile* myCiftiOut = myParams->getOutputCifti(2); OptionalParameter* rowVoxelOpt = myParams->getOptionalParameter(3); OptionalParameter* rowSurfaceOpt = myParams->getOptionalParameter(4); OptionalParameter* rowCiftiOpt = myParams->getOptionalParameter(9); OptionalParameter* colVoxelOpt = myParams->getOptionalParameter(5); OptionalParameter* colSurfaceOpt = myParams->getOptionalParameter(6); OptionalParameter* colCiftiOpt = myParams->getOptionalParameter(10); bool transpose = myParams->getOptionalParameter(7)->m_present; bool halfMatrix = myParams->getOptionalParameter(8)->m_present; int numRowOpts = 0, numColOpts = 0; if (rowVoxelOpt->m_present) ++numRowOpts; if (rowSurfaceOpt->m_present) ++numRowOpts; if (rowCiftiOpt->m_present) ++numRowOpts; if (colVoxelOpt->m_present) ++numColOpts; if (colSurfaceOpt->m_present) ++numColOpts; if (colCiftiOpt->m_present) ++numColOpts; if (numRowOpts != 1)//if both false or both true, basically using equals as a quick hack for xnor { throw OperationException("you must specify exactly one of -row-voxels, -row-surface, and -row-cifti"); } if (numColOpts != 1) { throw OperationException("you must specify exactly one of -col-voxels, -col-surface, and -col-cifti"); } CiftiXMLOld myXML; myXML.resetRowsToBrainModels(); myXML.resetColumnsToBrainModels(); vector rowReorderMap, colReorderMap; if (rowVoxelOpt->m_present) { addVoxelMapping(rowVoxelOpt->getVolume(2), rowVoxelOpt->getString(1), myXML, rowReorderMap, CiftiXMLOld::ALONG_ROW); } if (rowSurfaceOpt->m_present) { MetricFile* myMetric = rowSurfaceOpt->getMetric(1); myXML.addSurfaceModelToRows(myMetric->getNumberOfNodes(), myMetric->getStructure(), myMetric->getValuePointerForColumn(0)); } if (rowCiftiOpt->m_present) { AString directionName = rowCiftiOpt->getString(2); int myDir; if (directionName == "ROW") { myDir = CiftiXMLOld::ALONG_ROW; } else if (directionName == "COLUMN") { myDir = CiftiXMLOld::ALONG_COLUMN; } else { throw OperationException("incorrect string for direction, use ROW or COLUMN"); } myXML.copyMapping(CiftiXMLOld::ALONG_ROW, rowCiftiOpt->getCifti(1)->getCiftiXMLOld(), myDir); } if (colVoxelOpt->m_present) { addVoxelMapping(colVoxelOpt->getVolume(2), colVoxelOpt->getString(1), myXML, colReorderMap, CiftiXMLOld::ALONG_COLUMN); } if (colSurfaceOpt->m_present) { MetricFile* myMetric = colSurfaceOpt->getMetric(1); myXML.addSurfaceModelToColumns(myMetric->getNumberOfNodes(), myMetric->getStructure(), myMetric->getValuePointerForColumn(0)); } if (colCiftiOpt->m_present) { AString directionName = colCiftiOpt->getString(2); int myDir; if (directionName == "ROW") { myDir = CiftiXMLOld::ALONG_ROW; } else if (directionName == "COLUMN") { myDir = CiftiXMLOld::ALONG_COLUMN; } else { throw OperationException("incorrect string for direction, use ROW or COLUMN"); } myXML.copyMapping(CiftiXMLOld::ALONG_COLUMN, colCiftiOpt->getCifti(1)->getCiftiXMLOld(), myDir); } fstream dotFile(dotFileName.toAscii().constData(), fstream::in); if (!dotFile.good()) { throw OperationException("error opening text file '" + dotFileName + "'"); } SparseValue tempValue; vector dotFileContents; int32_t rowSize = myXML.getNumberOfColumns(), colSize = myXML.getNumberOfRows(); if (halfMatrix && rowSize != colSize) { throw OperationException("-make-symmetric was specified, but the matrix is not square"); } if (halfMatrix && transpose) { CaretLogInfo("-transpose is not needed with -make-symmetric"); } int64_t numZeros = 0; bool afterZero = false; if (transpose) { while (dotFile >> tempValue.index[1] >> tempValue.index[0] >> tempValue.value)//this is the only line that is different for transpose { if (tempValue.value == 0.0f) { if (tempValue.index[0] != rowSize || tempValue.index[1] != colSize) { throw OperationException("dimensions line in .dot file doesn't agree with provided row/column spaces"); } ++numZeros;//ignore, we expect one line (last in file) to have this } else { if (tempValue.index[0] < 1 || tempValue.index[0] > rowSize || tempValue.index[1] < 1 || tempValue.index[1] > colSize) { throw OperationException("found invalid index pair in dot file: " + AString::number(tempValue.index[0]) + ", " + AString::number(tempValue.index[1]) + ", perhaps you need to remove -transpose"); } if (numZeros != 0) afterZero = true; tempValue.index[0] -= 1;//fix for 1-indexing tempValue.index[1] -= 1; dotFileContents.push_back(tempValue); if (halfMatrix && tempValue.index[0] != tempValue.index[1]) { int32_t tempIndex = tempValue.index[0]; tempValue.index[0] = tempValue.index[1]; tempValue.index[1] = tempIndex; dotFileContents.push_back(tempValue); } } } } else { while (dotFile >> tempValue.index[0] >> tempValue.index[1] >> tempValue.value) { if (tempValue.value == 0.0f) { if (tempValue.index[0] != rowSize || tempValue.index[1] != colSize) { throw OperationException("dimensions line in .dot file doesn't agree with provided row/column spaces"); } ++numZeros; } else { if (tempValue.index[0] < 1 || tempValue.index[0] > rowSize || tempValue.index[1] < 1 || tempValue.index[1] > colSize) { throw OperationException("found invalid index pair in dot file: " + AString::number(tempValue.index[0]) + ", " + AString::number(tempValue.index[1]) + ", perhaps you need to use -transpose"); } if (numZeros != 0) afterZero = true; tempValue.index[0] -= 1;//fix for 1-indexing tempValue.index[1] -= 1; dotFileContents.push_back(tempValue); if (halfMatrix && tempValue.index[0] != tempValue.index[1]) { int32_t tempIndex = tempValue.index[0]; tempValue.index[0] = tempValue.index[1]; tempValue.index[1] = tempIndex; dotFileContents.push_back(tempValue); } } } } if (numZeros != 1) { CaretLogWarning("found (and ignored) " + AString::number(numZeros) + " lines with zero for value, expected 1"); } if (afterZero) { CaretLogWarning("found data lines after dimensionality line (which should be the last line of the file)"); } bool sorted = true; int64_t numValues = (int64_t)dotFileContents.size(); for (int64_t i = 1; i < numValues; ++i) { if (dotFileContents[i - 1].index[1] > dotFileContents[i].index[1]) { sorted = false; if (!halfMatrix) { CaretLogInfo("dot file indexes are not correctly sorted, sorting them may take a minute or so..."); } break; } } if (!sorted) sort(dotFileContents.begin(), dotFileContents.end()); if (!sorted && !halfMatrix) { CaretLogInfo("sorting finished"); } myCiftiOut->setCiftiXML(myXML); int64_t cur = 0, end = (int64_t)dotFileContents.size(); vector scratchRow(myXML.getNumberOfColumns(), 0.0f); vector checkDuplicate(myXML.getNumberOfColumns(), false); int64_t whichRow = 0;//set all rows, in case initial allocation doesn't give a zeroed matrix while (whichRow < myXML.getNumberOfRows()) { int64_t next = cur; while (next < end && dotFileContents[next].index[1] == whichRow) ++next; if (rowVoxelOpt->m_present) { for (int64_t i = cur; i < next; ++i) { int64_t outIndex = rowReorderMap[dotFileContents[i].index[0]]; if (checkDuplicate[outIndex]) { AString elemString; if (transpose) { elemString = AString::number(dotFileContents[i].index[1] + 1) + ", " + AString::number(dotFileContents[i].index[0] + 1); } else { elemString = AString::number(dotFileContents[i].index[0] + 1) + ", " + AString::number(dotFileContents[i].index[1] + 1); } if (halfMatrix) { throw OperationException("element specified more than once: " + elemString + ", perhaps you should not use -make-symmetric"); } else { throw OperationException("duplicate element found: " + elemString); } } scratchRow[outIndex] = dotFileContents[i].value; checkDuplicate[outIndex] = true; } } else { for (int64_t i = cur; i < next; ++i) { int64_t outIndex = dotFileContents[i].index[0]; if (checkDuplicate[outIndex]) { AString elemString; if (transpose) { elemString = AString::number(dotFileContents[i].index[1] + 1) + ", " + AString::number(dotFileContents[i].index[0] + 1); } else { elemString = AString::number(dotFileContents[i].index[0] + 1) + ", " + AString::number(dotFileContents[i].index[1] + 1); } if (halfMatrix) { throw OperationException("element specified more than once: " + elemString + ", perhaps you should not use -make-symmetric"); } else { throw OperationException("duplicate element found: " + elemString); } } scratchRow[outIndex] = dotFileContents[i].value; checkDuplicate[outIndex] = true; } } if (colVoxelOpt->m_present) { myCiftiOut->setRow(scratchRow.data(), colReorderMap[whichRow]); } else { myCiftiOut->setRow(scratchRow.data(), whichRow); } if (rowVoxelOpt->m_present) { for (int64_t i = cur; i < next; ++i) { int64_t outIndex = rowReorderMap[dotFileContents[i].index[0]]; scratchRow[outIndex] = 0.0f; checkDuplicate[outIndex] = false; } } else { for (int64_t i = cur; i < next; ++i) { int64_t outIndex = dotFileContents[i].index[0]; scratchRow[outIndex] = 0.0f; checkDuplicate[outIndex] = false; } } cur = next; ++whichRow; } } void OperationProbtrackXDotConvert::addVoxelMapping(const VolumeFile* myLabelVol, const AString& textFileName, CiftiXMLOld& myXML, vector& reorderMapping, const int& direction) { if (myLabelVol->getType() != SubvolumeAttributes::LABEL) { throw OperationException("specified volume for row voxels is not a label volume"); } vector myDims; myLabelVol->getDimensions(myDims); myXML.setVolumeDimsAndSForm(myDims.data(), myLabelVol->getSform()); const GiftiLabelTable* myLabelTable = myLabelVol->getMapLabelTable(0); map labelMap;//maps label values to structures vector > voxelLists;//voxel lists for each volume component vector > inputIndices;//index from the input space, matched to voxelLists map componentMap;//maps structures to indexes in voxelLists vector labelKeys; myLabelTable->getKeys(labelKeys); int64_t count = 0; for (int i = 0; i < (int)labelKeys.size(); ++i) { bool ok = false; StructureEnum::Enum thisStructure = StructureEnum::fromName(myLabelTable->getLabelName(labelKeys[i]), &ok); if (ok) { labelMap[labelKeys[i]] = thisStructure; if (componentMap.find(thisStructure) == componentMap.end())//make sure we don't already have this structure from another label { componentMap[thisStructure] = (int)count; ++count; } } } if (labelMap.empty()) { throw OperationException("label volume doesn't contain any structure labels"); } voxelLists.resize(count); inputIndices.resize(count); voxelIndexType vi, vj, vk; fstream myTextFile(textFileName.toAscii().constData(), fstream::in); if (!myTextFile.good()) { throw OperationException("error opening text file '" + textFileName + "'"); } count = 0; while (myTextFile >> vi >> vj >> vk) { if (!myLabelVol->indexValid(vi, vj, vk)) { throw OperationException("invalid voxel index found in text file: " + AString::number(vi) + ", " + AString::number(vj) + ", " + AString::number(vk)); } int myKey = (int)floor(myLabelVol->getValue(vi, vj, vk) + 0.5f); map::const_iterator myIter = labelMap.find(myKey); if (myIter == labelMap.end()) { throw OperationException("voxel index in list file did not match a structure: " + AString::number(vi) + ", " + AString::number(vj) + ", " + AString::number(vk)); } int myListIndex = componentMap[myIter->second];//will always exist voxelLists[myListIndex].push_back(vi); voxelLists[myListIndex].push_back(vj); voxelLists[myListIndex].push_back(vk); inputIndices[myListIndex].push_back(count); ++count; } vector forwardMap; for (map::const_iterator iter = componentMap.begin(); iter != componentMap.end(); ++iter) { int i = iter->second; int64_t listSize = voxelLists[i].size(); if (listSize != 0) { forwardMap.insert(forwardMap.end(), inputIndices[i].begin(), inputIndices[i].end());//append the structure's input index list, building lookup of new index->input index if (direction == CiftiXMLOld::ALONG_ROW) { myXML.addVolumeModelToRows(voxelLists[i], iter->first); } else { myXML.addVolumeModelToColumns(voxelLists[i], iter->first); } } } int64_t reorderSize = (int64_t)forwardMap.size(); reorderMapping.resize(reorderSize); for (int i = 0; i < reorderSize; ++i) { reorderMapping[forwardMap[i]] = i;//reverse it, building lookup from input index->new index } } connectome-workbench-1.2.3+git41-gc4c6c90/src/Operations/OperationProbtrackXDotConvert.h000066400000000000000000000032351300200146000310130ustar00rootroot00000000000000#ifndef __OPERATION_PROBTRACK_X_DOT_CONVERT_H__ #define __OPERATION_PROBTRACK_X_DOT_CONVERT_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 "AbstractOperation.h" #include namespace caret { class CiftiXMLOld; class OperationProbtrackXDotConvert : public AbstractOperation { static void addVoxelMapping(const VolumeFile* myLabelVol, const AString& textFileName, CiftiXMLOld& myXML, std::vector& reorderMapping, const int& direction); public: static OperationParameters* getParameters(); static void useParameters(OperationParameters* myParams, ProgressObject* myProgObj); static AString getCommandSwitch(); static AString getShortDescription(); }; typedef TemplateAutoOperation AutoOperationProbtrackXDotConvert; } #endif //__OPERATION_PROBTRACK_X_DOT_CONVERT_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Operations/OperationSceneFileMerge.cxx000066400000000000000000000127241300200146000301170ustar00rootroot00000000000000/*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 "OperationSceneFileMerge.h" #include "OperationException.h" #include "Scene.h" #include "SceneFile.h" using namespace caret; using namespace std; AString OperationSceneFileMerge::getCommandSwitch() { return "-scene-file-merge"; } AString OperationSceneFileMerge::getShortDescription() { return "REARRANGE SCENES INTO A NEW FILE"; } OperationParameters* OperationSceneFileMerge::getParameters() { OperationParameters* ret = new OperationParameters(); ret->addStringParameter(1, "scene-file-out", "output - the output scene file");//HACK: fake the output formatting ParameterComponent* sceneFileOpt = ret->createRepeatableParameter(2, "-scene-file", "specify a scene file to use scenes from"); sceneFileOpt->addStringParameter(1, "scene-file", "the input scene file"); ParameterComponent* sceneOpt = sceneFileOpt->createRepeatableParameter(2, "-scene", "specify a scene to use"); sceneOpt->addStringParameter(1, "scene", "the scene number or name"); OptionalParameter* upToOpt = sceneOpt->createOptionalParameter(2, "-up-to", "use an inclusive range of scenes"); upToOpt->addStringParameter(1, "last-column", "the number or name of the last scene to include"); upToOpt->createOptionalParameter(2, "-reverse", "use the range in reverse order"); ret->setHelpText( AString("Takes one or more scene files and constructs a new scene file by concatenating specified scenes from them.\n\n") + "Example: wb_command -scene-file-merge out.scene -scene-file first.scene -scene 1 -scene-file second.scene\n\n" + "This example would take the first scene from first.scene, followed by all scenes from second.scene, and write these scenes to out.scene." ); return ret; } void OperationSceneFileMerge::useParameters(OperationParameters* myParams, ProgressObject* myProgObj) { LevelProgress myProgress(myProgObj); SceneFile outSceneFile; const vector& myInputs = *(myParams->getRepeatableParameterInstances(2)); int numInputs = (int)myInputs.size(); if (numInputs == 0) throw OperationException("no inputs specified"); for (int i = 0; i < numInputs; ++i) { ParameterComponent* thisInput = myInputs[i]; SceneFile inputScene; inputScene.readFile(thisInput->getString(1)); const vector& selectOpts = *(thisInput->getRepeatableParameterInstances(2)); int numSelect = (int)selectOpts.size(); if (numSelect > 0) { for (int j = 0; j < numSelect; ++j) { ParameterComponent* thisSelect = selectOpts[j]; int firstInd = inputScene.getSceneIndexFromNumberOrName(thisSelect->getString(1)); if (firstInd < 0) { throw OperationException("scene '" + thisSelect->getString(1) + "' not found in file '" + thisInput->getString(1) + "'"); } OptionalParameter* upToOpt = thisSelect->getOptionalParameter(2); if (upToOpt->m_present) { int lastInd = inputScene.getSceneIndexFromNumberOrName(upToOpt->getString(1)); if (lastInd < 0) { throw OperationException("scene '" + upToOpt->getString(1) + "' not found in file '" + thisInput->getString(1) + "'"); } if (lastInd < firstInd) { throw OperationException("ending scene '" + upToOpt->getString(1) + "' occurs before starting scene '" + thisSelect->getString(1) + "'"); } if (upToOpt->getOptionalParameter(2)->m_present)//reverse { for (int s = lastInd; s >= firstInd; --s) { outSceneFile.addScene(new Scene(*inputScene.getSceneAtIndex(s))); } } else { for (int s = firstInd; s <= lastInd; ++s) { outSceneFile.addScene(new Scene(*inputScene.getSceneAtIndex(s))); } } } else { outSceneFile.addScene(new Scene(*inputScene.getSceneAtIndex(firstInd))); } } } else {//no select options means select all in order int numScenes = inputScene.getNumberOfScenes(); for (int s = 0; s < numScenes; ++s) { outSceneFile.addScene(new Scene(*inputScene.getSceneAtIndex(s))); } } } outSceneFile.writeFile(myParams->getString(1)); } connectome-workbench-1.2.3+git41-gc4c6c90/src/Operations/OperationSceneFileMerge.h000066400000000000000000000026301300200146000275370ustar00rootroot00000000000000#ifndef __OPERATION_SCENE_FILE_MERGE_H__ #define __OPERATION_SCENE_FILE_MERGE_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 OperationSceneFileMerge : public AbstractOperation { public: static OperationParameters* getParameters(); static void useParameters(OperationParameters* myParams, ProgressObject* myProgObj); static AString getCommandSwitch(); static AString getShortDescription(); }; typedef TemplateAutoOperation AutoOperationSceneFileMerge; } #endif //__OPERATION_SCENE_FILE_MERGE_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Operations/OperationSceneFileRelocate.cxx000066400000000000000000000041341300200146000306120ustar00rootroot00000000000000/*LICENSE_START*/ /* * Copyright (C) 2015 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 "OperationSceneFileRelocate.h" #include "OperationException.h" #include "SceneFile.h" using namespace caret; using namespace std; AString OperationSceneFileRelocate::getCommandSwitch() { return "-scene-file-relocate"; } AString OperationSceneFileRelocate::getShortDescription() { return "RECREATE SCENE FILE IN NEW LOCATION"; } OperationParameters* OperationSceneFileRelocate::getParameters() { OperationParameters* ret = new OperationParameters(); ret->addStringParameter(1, "input-scene", "the scene file to use"); ret->addStringParameter(2, "output-scene", "output - the new scene file to create");//fake the output formatting ret->setHelpText( AString("Scene files contain internal relative paths, such that moving or copying a scene file will cause it to lose track of the files it refers to. ") + "This command makes a modified copy of the scene file, changing the relative paths to refer to the new relative locations of the files." ); return ret; } void OperationSceneFileRelocate::useParameters(OperationParameters* myParams, ProgressObject* myProgObj) { LevelProgress myProgress(myProgObj); SceneFile myScene; myScene.readFile(myParams->getString(1)); myScene.writeFile(myParams->getString(2)); } connectome-workbench-1.2.3+git41-gc4c6c90/src/Operations/OperationSceneFileRelocate.h000066400000000000000000000026521300200146000302420ustar00rootroot00000000000000#ifndef __OPERATION_SCENE_FILE_RELOCATE_H__ #define __OPERATION_SCENE_FILE_RELOCATE_H__ /*LICENSE_START*/ /* * Copyright (C) 2015 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 OperationSceneFileRelocate : public AbstractOperation { public: static OperationParameters* getParameters(); static void useParameters(OperationParameters* myParams, ProgressObject* myProgObj); static AString getCommandSwitch(); static AString getShortDescription(); }; typedef TemplateAutoOperation AutoOperationSceneFileRelocate; } #endif //__OPERATION_SCENE_FILE_RELOCATE_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Operations/OperationSetMapName.cxx000066400000000000000000000115511300200146000272710ustar00rootroot00000000000000/*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 "OperationSetMapName.h" #include "OperationException.h" #include "DataFileTypeEnum.h" #include "CiftiFile.h" #include "LabelFile.h" #include "MetricFile.h" #include "VolumeFile.h" #include using namespace caret; using namespace std; AString OperationSetMapName::getCommandSwitch() { return "-set-map-name"; } AString OperationSetMapName::getShortDescription() { return "DEPRECATED: use -set-map-names"; } OperationParameters* OperationSetMapName::getParameters() { OperationParameters* ret = new OperationParameters(); ret->addStringParameter(1, "data-file", "the file to set a map name of"); ret->addIntegerParameter(2, "index", "the map index to set the name of"); ret->addStringParameter(3, "name", "the name to set for the map"); ret->setHelpText( AString("DEPRECATED: this command may be removed in a future release, use -set-map-names.\n\n") + "Sets the name of a map for metric, shape, label, volume, cifti scalar or cifti label files." ); return ret; } void OperationSetMapName::useParameters(OperationParameters* myParams, ProgressObject* myProgObj) { LevelProgress myProgress(myProgObj); AString fileName = myParams->getString(1); int mapIndex = (int)myParams->getInteger(2) - 1; if (mapIndex < 0) throw OperationException("invalid map index specified, indexes are 1-based"); AString mapName = myParams->getString(3); bool ok = false; DataFileTypeEnum::Enum myType = DataFileTypeEnum::fromFileExtension(fileName, &ok); if (!ok) { throw OperationException("unrecognized data file type"); } switch (myType) { case DataFileTypeEnum::METRIC: { MetricFile myMetric; myMetric.readFile(fileName); if (mapIndex >= myMetric.getNumberOfMaps()) throw OperationException("metric file doesn't have enough columns for specified map index"); myMetric.setMapName(mapIndex, mapName); myMetric.writeFile(fileName); break; } case DataFileTypeEnum::LABEL: { LabelFile myLabel; myLabel.readFile(fileName); if (mapIndex >= myLabel.getNumberOfMaps()) throw OperationException("label file doesn't have enough columns for specified map index"); myLabel.setMapName(mapIndex, mapName); myLabel.writeFile(fileName); break; } case DataFileTypeEnum::VOLUME: { VolumeFile myVol; myVol.readFile(fileName); if (mapIndex >= myVol.getNumberOfMaps()) throw OperationException("volume file doesn't have enough subvolumes for specified map index"); myVol.setMapName(mapIndex, mapName); myVol.writeFile(fileName); break; } case DataFileTypeEnum::CONNECTIVITY_DENSE_SCALAR: case DataFileTypeEnum::CONNECTIVITY_DENSE_TIME_SERIES: case DataFileTypeEnum::CONNECTIVITY_DENSE_LABEL: { CiftiFile myCifti; myCifti.openFile(fileName); myCifti.convertToInMemory(); CiftiXMLOld myXML = myCifti.getCiftiXMLOld(); if (mapIndex >= myXML.getNumberOfColumns()) throw OperationException("cifti file doesn't have enough columns for specified map index"); if (!myXML.setMapNameForIndex(CiftiXMLOld::ALONG_ROW, mapIndex, mapName)) throw OperationException("failed to set map name, check the type of the cifti file"); CiftiFile myOutCifti; myOutCifti.setWritingFile(fileName); myOutCifti.setCiftiXML(myXML); int numRows = myXML.getNumberOfRows(), rowSize = myXML.getNumberOfColumns(); vector scratchRow(rowSize); for (int i = 0; i < numRows; ++i) { myCifti.getRow(scratchRow.data(), i); myOutCifti.setRow(scratchRow.data(), i); } myOutCifti.writeFile(fileName); break; } default: throw OperationException("cannot set map name on this file type"); } } connectome-workbench-1.2.3+git41-gc4c6c90/src/Operations/OperationSetMapName.h000066400000000000000000000026001300200146000267110ustar00rootroot00000000000000#ifndef __OPERATION_SET_MAP_NAME_H__ #define __OPERATION_SET_MAP_NAME_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 "AbstractOperation.h" namespace caret { class OperationSetMapName : public AbstractOperation { public: static OperationParameters* getParameters(); static void useParameters(OperationParameters* myParams, ProgressObject* myProgObj); static AString getCommandSwitch(); static AString getShortDescription(); }; typedef TemplateAutoOperation AutoOperationSetMapName; } #endif //__OPERATION_SET_MAP_NAME_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Operations/OperationSetMapNames.cxx000066400000000000000000000116151300200146000274550ustar00rootroot00000000000000/*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 "OperationSetMapNames.h" #include "OperationException.h" #include "CaretDataFile.h" #include "CaretDataFileHelper.h" #include "CaretMappableDataFile.h" #include "CaretPointer.h" #include "CaretLogger.h" #include #include using namespace caret; using namespace std; AString OperationSetMapNames::getCommandSwitch() { return "-set-map-names"; } AString OperationSetMapNames::getShortDescription() { return "SET THE NAME OF ONE OR MORE MAPS IN A FILE"; } OperationParameters* OperationSetMapNames::getParameters() { OperationParameters* ret = new OperationParameters(); ret->addStringParameter(1, "data-file", "the file to set the map names of"); OptionalParameter* fileOpt = ret->createOptionalParameter(2, "-name-file", "use a text file to replace all map names"); fileOpt->addStringParameter(1, "file", "text file containing map names, one per line"); ParameterComponent* mapOpt = ret->createRepeatableParameter(3, "-map", "specify a map to set the name of"); mapOpt->addIntegerParameter(1, "index", "the map index to change the name of"); mapOpt->addStringParameter(2, "new-name", "the name to set for the map"); ret->setHelpText( AString("Sets the name of one or more maps for metric, shape, label, volume, cifti scalar or cifti label files. ") + "If the -name-file option is not specified, the -map option must be specified at least once. " + "The -map option cannot be used when -name-file is specified." ); return ret; } void OperationSetMapNames::useParameters(OperationParameters* myParams, ProgressObject* myProgObj) { LevelProgress myProgress(myProgObj); AString fileName = myParams->getString(1); OptionalParameter* fileOpt = myParams->getOptionalParameter(2); const vector& mapOpts = *(myParams->getRepeatableParameterInstances(3)); CaretPointer caretDataFile(CaretDataFileHelper::readAnyCaretDataFile(fileName)); CaretMappableDataFile* mappableFile = dynamic_cast(caretDataFile.getPointer()); if (mappableFile == NULL) throw OperationException("cannot set map names on this file type"); if (fileOpt->m_present) { if (!mapOpts.empty()) throw OperationException("-map may not be specified when using -name-file"); AString listfileName = fileOpt->getString(1); ifstream nameListFile(listfileName.toLocal8Bit().constData()); if (!nameListFile.good()) { throw OperationException("error reading name list file"); } string mapName; int numMaps = mappableFile->getNumberOfMaps(); for (int i = 0; i < numMaps; ++i) { getline(nameListFile, mapName); if (!nameListFile) { throw OperationException("name file contained " + AString::number(i) + " names, expected " + AString::number(numMaps)); break; } mappableFile->setMapName(i, mapName.c_str()); } if (getline(nameListFile, mapName) && mapName != "") {//accept a blank line as not being another name throw OperationException("name file contains more names than can be used on the file"); } if (getline(nameListFile, mapName)) {//don't accept two or more additional lines, period throw OperationException("name file contains more names than can be used on the file"); } } else { if (mapOpts.empty()) throw OperationException("you must specify at least one option that sets a map name"); for (int i = 0; i < (int)mapOpts.size(); ++i) { int mapIndex = (int)mapOpts[i]->getInteger(1) - 1; if (mapIndex < 0) throw OperationException("invalid map index, indices are 1-based"); if (mapIndex >= mappableFile->getNumberOfMaps()) throw OperationException("invalid map index, file doesn't have enough maps"); AString newName = mapOpts[i]->getString(2); mappableFile->setMapName(mapIndex, newName); } } mappableFile->writeFile(fileName); } connectome-workbench-1.2.3+git41-gc4c6c90/src/Operations/OperationSetMapNames.h000066400000000000000000000026061300200146000271020ustar00rootroot00000000000000#ifndef __OPERATION_SET_MAP_NAMES_H__ #define __OPERATION_SET_MAP_NAMES_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 "AbstractOperation.h" namespace caret { class OperationSetMapNames : public AbstractOperation { public: static OperationParameters* getParameters(); static void useParameters(OperationParameters* myParams, ProgressObject* myProgObj); static AString getCommandSwitch(); static AString getShortDescription(); }; typedef TemplateAutoOperation AutoOperationSetMapNames; } #endif //__OPERATION_SET_MAP_NAMES_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Operations/OperationSetStructure.cxx000066400000000000000000000154341300200146000277570ustar00rootroot00000000000000/*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 "OperationSetStructure.h" #include "OperationException.h" #include "BorderFile.h" #include "CaretLogger.h" #include "DataFileTypeEnum.h" #include "LabelFile.h" #include "MetricFile.h" #include "StructureEnum.h" #include "SurfaceFile.h" using namespace caret; using namespace std; AString OperationSetStructure::getCommandSwitch() { return "-set-structure"; } AString OperationSetStructure::getShortDescription() { return "SET STRUCTURE OF A DATA FILE"; } OperationParameters* OperationSetStructure::getParameters() { OperationParameters* ret = new OperationParameters(); ret->addStringParameter(1, "data-file", "the file to set the structure of"); ret->addStringParameter(2, "structure", "the structure to set the file to"); OptionalParameter* surfType = ret->createOptionalParameter(3, "-surface-type", "set the type of a surface (only used if file is a surface file)"); surfType->addStringParameter(1, "type", "name of surface type"); OptionalParameter* secondaryType = ret->createOptionalParameter(4, "-surface-secondary-type", "set the secondary type of a surface (only used if file is a surface file)"); secondaryType->addStringParameter(1, "secondary type", "name of surface secondary type"); AString myText = AString("The existing file is modified and rewritten to the same filename. Valid values for the structure name are:\n\n"); vector myStructureEnums; StructureEnum::getAllEnums(myStructureEnums); for (int i = 0; i < (int)myStructureEnums.size(); ++i) { myText += StructureEnum::toName(myStructureEnums[i]) + "\n"; } myText += "\nValid names for the surface type are:\n\n"; vector mySurfTypeEnums; SurfaceTypeEnum::getAllEnums(mySurfTypeEnums); for (int i = 0; i < (int)mySurfTypeEnums.size(); ++i) { myText += SurfaceTypeEnum::toName(mySurfTypeEnums[i]) + "\n"; } myText += "\nValid names for the surface secondary type are:\n\n"; vector mySecondaryTypeEnums; SecondarySurfaceTypeEnum::getAllEnums(mySecondaryTypeEnums); for (int i = 0; i < (int)mySecondaryTypeEnums.size(); ++i) { myText += SecondarySurfaceTypeEnum::toName(mySecondaryTypeEnums[i]) + "\n"; } ret->setHelpText(myText); return ret; } void OperationSetStructure::useParameters(OperationParameters* myParams, ProgressObject* myProgObj) { LevelProgress myProgress(myProgObj); AString fileName = myParams->getString(1); AString structureName = myParams->getString(2); OptionalParameter* surfType = myParams->getOptionalParameter(3); OptionalParameter* secondaryType = myParams->getOptionalParameter(4); bool ok = false; SurfaceTypeEnum::Enum mySurfType = SurfaceTypeEnum::UNKNOWN;//so compilers won't complain about uninitialized if (surfType->m_present) { AString mySurfTypeName = surfType->getString(1); mySurfType = SurfaceTypeEnum::fromName(mySurfTypeName, &ok); if (!ok) { throw OperationException("unrecognized surface type"); } } SecondarySurfaceTypeEnum::Enum mySecondType = SecondarySurfaceTypeEnum::INVALID; if (secondaryType->m_present) { AString mySecondaryName = secondaryType->getString(1); mySecondType = SecondarySurfaceTypeEnum::fromName(mySecondaryName, &ok); if (!ok) { throw OperationException("unrecognized secondary surface type"); } } StructureEnum::Enum myStructure = StructureEnum::fromName(structureName, &ok); if (!ok) { throw OperationException("unrecognized structure type"); } DataFileTypeEnum::Enum myType = DataFileTypeEnum::fromFileExtension(fileName, &ok); if (!ok) { throw OperationException("unrecognized data file type"); } switch (myType) { case DataFileTypeEnum::SURFACE: { SurfaceFile mySurf; mySurf.readFile(fileName); mySurf.setStructure(myStructure); if (surfType->m_present) { mySurf.setSurfaceType(mySurfType); } if (secondaryType->m_present) { mySurf.setSecondaryType(mySecondType); } mySurf.writeFile(fileName); } break; case DataFileTypeEnum::LABEL: { if (surfType->m_present) CaretLogInfo("the -surface-type option is ignored with this file type"); if (secondaryType->m_present) CaretLogInfo("the -surface-secondary-type option is ignored with this file type"); LabelFile myLabel; myLabel.readFile(fileName); myLabel.setStructure(myStructure); myLabel.writeFile(fileName); } break; case DataFileTypeEnum::METRIC: { if (surfType->m_present) CaretLogInfo("the -surface-type option is ignored with this file type"); if (secondaryType->m_present) CaretLogInfo("the -surface-secondary-type option is ignored with this file type"); MetricFile myMetric; myMetric.readFile(fileName); myMetric.setStructure(myStructure); myMetric.writeFile(fileName); } break; case DataFileTypeEnum::BORDER: { if (surfType->m_present) CaretLogInfo("the -surface-type option is ignored with this file type"); if (secondaryType->m_present) CaretLogInfo("the -surface-secondary-type option is ignored with this file type"); BorderFile myBorder; myBorder.readFile(fileName); myBorder.setStructure(myStructure); myBorder.writeFile(fileName); } break; default: throw OperationException("cannot set the structure of this file type"); }; } connectome-workbench-1.2.3+git41-gc4c6c90/src/Operations/OperationSetStructure.h000066400000000000000000000026111300200146000273750ustar00rootroot00000000000000#ifndef __OPERATION_SET_STRUCTURE_H__ #define __OPERATION_SET_STRUCTURE_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 "AbstractOperation.h" namespace caret { class OperationSetStructure : public AbstractOperation { public: static OperationParameters* getParameters(); static void useParameters(OperationParameters* myParams, ProgressObject* myProgObj); static AString getCommandSwitch(); static AString getShortDescription(); }; typedef TemplateAutoOperation AutoOperationSetStructure; } #endif //__OPERATION_SET_STRUCTURE_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Operations/OperationShowScene.cxx000066400000000000000000000753341300200146000272060ustar00rootroot00000000000000 /*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 #ifdef HAVE_OSMESA #include #endif // HAVE_OSMESA #include #include #include "Brain.h" #include "BrainOpenGLFixedPipeline.h" #include "BrainOpenGLViewportContent.h" #include "CaretAssert.h" #include "CaretLogger.h" #include "DataFileException.h" #include "EventBrowserTabGet.h" #include "EventManager.h" #include "FileInformation.h" #include "DummyFontTextRenderer.h" #include "FtglFontTextRenderer.h" #include "ImageFile.h" #include "OperationShowScene.h" #include "OperationException.h" #include "Scene.h" #include "SceneAttributes.h" #include "SceneClass.h" #include "SceneClassArray.h" #include "SceneFile.h" #include "ScenePrimitiveArray.h" #include "SessionManager.h" #include "TileTabsConfiguration.h" #include "VolumeFile.h" //#include "workbench_png.h" using namespace caret; /** * \class caret::OperationShowScene * \brief Offscreen rendering of scene to an image file * * Render a scene into an image file using the Offscreen Mesa Library */ /** * @return Command line switch */ AString OperationShowScene::getCommandSwitch() { return "-show-scene"; } /** * @return Short description of operation */ AString OperationShowScene::getShortDescription() { return ("OFFSCREEN RENDERING OF SCENE TO AN IMAGE FILE"); } /** * @return Parameters for operation */ OperationParameters* OperationShowScene::getParameters() { OperationParameters* ret = new OperationParameters(); ret->addStringParameter(1, "scene-file", "scene file"); ret->addStringParameter(2, "scene-name-or-number", "name or number (starting at one) of the scene in the scene file"); 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"); ret->createOptionalParameter(6, windowSizeSwitch, "Override image size with window size"); ret->createOptionalParameter(7, "-no-scene-colors", "Do not use background and foreground colors in scene"); 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 " "to render, the image name will not change. If there is " "more than one image to render, an index will be inserted " "into the image name: \"capture_01.png\", \"capture_02.png\" " "etc.\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"); std::vector imageFileExtensions; AString defaultExtension; ImageFile::getImageFileExtensions(imageFileExtensions, defaultExtension); for (std::vector::iterator iter = imageFileExtensions.begin(); iter != imageFileExtensions.end(); iter++) { const AString ext = *iter; helpText += (" " + ext + "\n"); } helpText += ("\n" "The result of using the \"" + windowSizeSwitch + "\" option\n" "is dependent upon the version used to create the scene.\n" " * Versions 1.2 and newer contain the width and \n" " height of the graphics region. The output image \n" " will be the width and height from the scene and\n" " the image width and height specified on the command\n" " line is ignored.\n" " * If the scene does not contain the width and height\n" " of the graphics region, the width and height specified\n" " on the command line is used for the size of the \n" " output image.\n" ); ret->setHelpText(helpText); return ret; } /** * Use Parameters and perform operation */ #ifndef HAVE_OSMESA void OperationShowScene::useParameters(OperationParameters* /*myParams*/, ProgressObject* /*myProgObj*/) { throw OperationException("Show scene command not available due to this software version " "not being built with the Mesa OffScreen Library"); } #else // HAVE_OSMESA void OperationShowScene::useParameters(OperationParameters* myParams, ProgressObject* myProgObj) { LevelProgress myProgress(myProgObj); AString sceneFileName = FileInformation(myParams->getString(1)).getAbsoluteFilePath(); AString sceneNameOrNumber = myParams->getString(2); AString imageFileName = FileInformation(myParams->getString(3)).getAbsoluteFilePath(); const int32_t userImageWidth = myParams->getInteger(4); const int32_t userImageHeight = myParams->getInteger(5); OptionalParameter* useWindowSizeParam = myParams->getOptionalParameter(6); const bool useWindowSizeForImageSizeFlag = useWindowSizeParam->m_present; const bool doNotUseSceneColorsFlag = myParams->getOptionalParameter(7)->m_present; if ( ! useWindowSizeForImageSizeFlag) { if ((userImageWidth <= 0) || (userImageHeight <= 0)) { throw OperationException("Invalid image size width=" + QString::number(userImageWidth) + " height=" + QString::number(userImageHeight)); } } /* * Read the scene file and load the scene */ SceneFile sceneFile; sceneFile.readFile(sceneFileName); Scene* scene = sceneFile.getSceneWithName(sceneNameOrNumber); if (scene == NULL) { bool valid = false; const int32_t sceneIndexStartAtOne = sceneNameOrNumber.toInt(&valid); if (valid) { const int32_t sceneIndex = sceneIndexStartAtOne - 1; if ((sceneIndex >= 0) && (sceneIndex < sceneFile.getNumberOfScenes())) { scene = sceneFile.getSceneAtIndex(sceneIndex); } else { throw OperationException("Scene index is invalid"); } } else { 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); if (doNotUseSceneColorsFlag) { sceneAttributes.setUseSceneForegroundAndBackgroundColors(false); } /* * Restore the scene */ const SceneClass* guiManagerClass = scene->getClassWithName("guiManager"); if (guiManagerClass->getName() != "guiManager") { throw OperationException("Top level scene class should be guiManager but it is: " + guiManagerClass->getName()); } SessionManager* sessionManager = SessionManager::get(); sessionManager->restoreFromScene(&sceneAttributes, guiManagerClass->getClass("m_sessionManager")); if (sessionManager->getNumberOfBrains() <= 0) { throw OperationException("Scene loading failure, SessionManager contains no Brains"); } Brain* brain = SessionManager::get()->getBrain(0); const GapsAndMargins* gapsAndMargins = brain->getGapsAndMargins(); bool missingWindowMessageHasBeenDisplayed = false; /* * 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."); } /* * If tile tabs was saved to the scene, restore it as the scenes tile tabs configuration */ if (restoreToTabTiles) { CaretPointer brainOpenGL(createBrainOpenGL(windowIndex)); const AString tileTabsConfigString = browserClass->getStringValue("m_sceneTileTabsConfiguration"); if ( ! tileTabsConfigString.isEmpty()) { TileTabsConfiguration tileTabsConfiguration; tileTabsConfiguration.decodeFromXML(tileTabsConfigString); /* * Restore toolbar */ 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."); } } else { CaretPointer brainOpenGL(createBrainOpenGL(windowIndex)); /* * 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); 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)); } CaretPointer content(NULL); content.grabNew(BrainOpenGLViewportContent::createViewportForSingleTab(tabContent, gapsAndMargins, windowIndex, windowViewport)); std::vector viewportContents; viewportContents.push_back(content); brainOpenGL->drawModels(brain, viewportContents); const int32_t outputImageIndex = ((numBrowserClasses > 1) ? i : -1); writeImage(imageFileName, outputImageIndex, imageBuffer, imageWidth, imageHeight); } } /* * Free image memory and Mesa context */ delete[] imageBuffer; OSMesaDestroyContext(mesaContext); } } } /** * Estimate the size of the graphics region from scenes that lack * an explicit entry for the graphics region size. Scenes in version * 1.2.0-pre1 did not contain graphics window size. * * @param windowSceneClass * Scene class for the window. * @param estimatedWidthOut * Estimated width of graphics region (greater than zero if valid). * @param estimatedHeightOut * Estimated height of graphics region (greater than zero if valid). */ void OperationShowScene::estimateGraphicsSize(const SceneClass* windowSceneClass, float& estimatedWidthOut, float& estimatedHeightOut) { estimatedWidthOut = 0; estimatedHeightOut = 0; float winWidth = -1.0; float winHeight = -1.0; const SceneClass* winGeometry = windowSceneClass->getClass("geometry"); if (winGeometry != NULL) { winWidth = winGeometry->getIntegerValue("geometryWidth", -1); winHeight = winGeometry->getIntegerValue("geometryHeight", -1); } else { return; } float tbHeight = -1.0; bool tbHeightValid = false; const SceneClass* tb = windowSceneClass->getClass("m_toolbar"); if (tb != NULL) { if (tb->getBooleanValue("toolBarVisible")) { tbHeight = 165.0; tbHeightValid = true; } } if ( ! tbHeightValid) { return; } float overlayToolBoxWidth = 0; float overlayToolBoxHeight = 0; QString overlayToolBoxOrientation; bool overlayToolBoxValid = getToolBoxSize(windowSceneClass->getClass("overlayToolBox"), windowSceneClass->getClass("m_overlayActiveToolBox"), overlayToolBoxWidth, overlayToolBoxHeight, overlayToolBoxOrientation); float featureToolBoxWidth = 0; float featureToolBoxHeight = 0; QString featureToolBoxOrientation; bool featureToolBoxValid = getToolBoxSize(windowSceneClass->getClass("featureToolBox"), windowSceneClass->getClass("m_featuresToolBox"), featureToolBoxWidth, featureToolBoxHeight, featureToolBoxOrientation); if (overlayToolBoxValid && featureToolBoxValid) { estimatedWidthOut = winWidth - overlayToolBoxWidth - featureToolBoxWidth; estimatedHeightOut = winHeight - tbHeight - overlayToolBoxHeight - featureToolBoxHeight; } } /** * Get the size of a toolbox. * * @param toolBoxClass * The toolbox scene class. * @param overlayToolBoxWidthOut * Output with width of toolbox. * @param overlayToolBoxHeightOut * Output with height of toolbox. * @param overlayToolBoxOrientationOut * Output with orientation of toolbox. * @return * True if the toolbox outputs are valid, else false. */ bool OperationShowScene::getToolBoxSize(const SceneClass* toolBoxClass, const SceneClass* activeToolBoxClass, float& overlayToolBoxWidthOut, float& overlayToolBoxHeightOut, QString& overlayToolBoxOrientationOut) { if (toolBoxClass == NULL) { return false; } if (activeToolBoxClass == NULL) { return false; } overlayToolBoxWidthOut = 0; overlayToolBoxHeightOut = 0; bool overlayToolBoxValid = false; if (toolBoxClass != NULL) { overlayToolBoxValid = true; if (toolBoxClass->getBooleanValue("visible")) { overlayToolBoxValid = false; if ( ! toolBoxClass->getBooleanValue("floating")) { overlayToolBoxOrientationOut = toolBoxClass->getStringValue("orientation"); overlayToolBoxWidthOut = activeToolBoxClass->getIntegerValue("toolboxWidth"); overlayToolBoxHeightOut = activeToolBoxClass->getIntegerValue("toolboxHeight"); if ((overlayToolBoxWidthOut > 0) && (overlayToolBoxHeightOut > 0)) { if (overlayToolBoxOrientationOut == "horizontal") { /* * Toolbar is on bottom so only need height */ overlayToolBoxWidthOut = 0; overlayToolBoxValid = true; } else if (overlayToolBoxOrientationOut == "vertical") { /* * Toolbar is on left side so only need width */ overlayToolBoxHeightOut = 0; overlayToolBoxValid = true; } } } } } return overlayToolBoxValid; } /** * Create OpenGL Rendering. * * @param windowIndex * Index of window. * @return * BrainOpenGL. */ BrainOpenGLFixedPipeline* OperationShowScene::createBrainOpenGL(const int32_t windowIndex) { /* * The OpenGL rendering takes ownership of the text renderer * and will delete the text renderer when OpenGL itself * is deleted. */ BrainOpenGLTextRenderInterface* textRenderer = NULL; if (textRenderer == NULL) { 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(); } /* * Performs OpenGL Rendering * Allocated dynamically so that it can be destroyed prior to OSMesa being * destroyed. Otherwise, if OpenGL is destroyed after OSMesa, errors * will occur as the OpenGL context is invalid when things such as * display lists or buffers are deleted. */ BrainOpenGLFixedPipeline* brainOpenGL = new BrainOpenGLFixedPipeline(windowIndex, textRenderer); brainOpenGL->initializeOpenGL(); return brainOpenGL; } #endif // HAVE_OSMESA /** * Write the image data to a Image File. * * @param imageFileName * Name of image file. * @param imageIndex * Index of image. * @param imageContent * content of image. * @param imageWidth * width of image. * @param imageHeight * height of image. */ void OperationShowScene::writeImage(const AString& imageFileName, const int32_t imageIndex, const unsigned char* imageContent, const int32_t imageWidth, const int32_t imageHeight) { /* * Create name of image */ QString outputName(imageFileName); if (imageIndex >= 0) { const AString imageNumber = QString("_%1").arg((int)(imageIndex + 1), 2, // width 10, // base QChar('0')); // fill character const int dotOffset = outputName.lastIndexOf("."); if (dotOffset >= 0) { outputName.insert(dotOffset, imageNumber); } else { outputName += (imageNumber + ".png"); } } try { //ImageFile imageFile(image); ImageFile imageFile(imageContent, imageWidth, imageHeight, ImageFile::IMAGE_DATA_ORIGIN_AT_BOTTOM); imageFile.writeFile(outputName); } catch (const DataFileException& dfe) { throw OperationException(dfe); } } /** * Is the show scene command available? */ bool OperationShowScene::isShowSceneCommandAvailable() { #ifdef HAVE_OSMESA return true; #else // HAVE_OSMESA return false; #endif // HAVE_OSMESA } connectome-workbench-1.2.3+git41-gc4c6c90/src/Operations/OperationShowScene.h000066400000000000000000000047651300200146000266330ustar00rootroot00000000000000#ifndef __OPERATION_SHOW_SCENE_H__ #define __OPERATION_SHOW_SCENE_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 "AbstractOperation.h" namespace caret { class BrainOpenGLFixedPipeline; class OperationShowScene : public AbstractOperation { public: static OperationParameters* getParameters(); static void useParameters(OperationParameters* myParams, ProgressObject* myProgObj); static AString getCommandSwitch(); static AString getShortDescription(); static bool isShowSceneCommandAvailable(); private: static BrainOpenGLFixedPipeline* createBrainOpenGL(const int32_t windowIndex); static void writeImage(const AString& imageFileName, const int32_t imageIndex, const unsigned char* imageContent, const int32_t imageWidth, const int32_t imageHeight); static void estimateGraphicsSize(const SceneClass* windowSceneClass, float& estimatedWidthOut, float& estimatedHeightOut); static bool getToolBoxSize(const SceneClass* toolBoxClass, const SceneClass* activeToolBoxClass, float& overlayToolBoxWidthOut, float& overlayToolBoxHeightOut, QString& overlayToolBoxOrientationOut); }; typedef TemplateAutoOperation AutoOperationShowScene; } // namespace #endif //__OPERATION_SHOW_SCENE_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Operations/OperationSpecFileMerge.cxx000066400000000000000000000045441300200146000277550ustar00rootroot00000000000000/*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 "FileInformation.h" #include "OperationSpecFileMerge.h" #include "OperationException.h" #include "SpecFile.h" using namespace caret; using namespace std; AString OperationSpecFileMerge::getCommandSwitch() { return "-spec-file-merge"; } AString OperationSpecFileMerge::getShortDescription() { return "MERGE TWO SPEC FILES INTO ONE"; } OperationParameters* OperationSpecFileMerge::getParameters() { OperationParameters* ret = new OperationParameters(); ret->addStringParameter(1, "spec-1", "first spec file to merge"); ret->addStringParameter(2, "spec-2", "second spec file to merge"); ret->addStringParameter(3, "out-spec", "output - output spec file");//fake the "output" formatting, could make a spec file parameter type ret->setHelpText(AString("The output spec file contains every file that is in either of the input spec files.")); return ret; } void OperationSpecFileMerge::useParameters(OperationParameters* myParams, ProgressObject* myProgObj) { LevelProgress myProgress(myProgObj); AString spec1Name = FileInformation(myParams->getString(1)).getAbsoluteFilePath();//since opening a spec file may change the current directory, we must convert all paths to absolute first AString spec2Name = FileInformation(myParams->getString(2)).getAbsoluteFilePath(); AString outSpecName = FileInformation(myParams->getString(3)).getAbsoluteFilePath(); SpecFile spec1, spec2; spec1.readFile(spec1Name); spec2.readFile(spec2Name); spec1.appendSpecFile(spec2); spec1.writeFile(outSpecName); } connectome-workbench-1.2.3+git41-gc4c6c90/src/Operations/OperationSpecFileMerge.h000066400000000000000000000026221300200146000273750ustar00rootroot00000000000000#ifndef __OPERATION_SPEC_FILE_MERGE_H__ #define __OPERATION_SPEC_FILE_MERGE_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 "AbstractOperation.h" namespace caret { class OperationSpecFileMerge : public AbstractOperation { public: static OperationParameters* getParameters(); static void useParameters(OperationParameters* myParams, ProgressObject* myProgObj); static AString getCommandSwitch(); static AString getShortDescription(); }; typedef TemplateAutoOperation AutoOperationSpecFileMerge; } #endif //__OPERATION_SPEC_FILE_MERGE_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Operations/OperationSpecFileRelocate.cxx000066400000000000000000000041111300200146000304420ustar00rootroot00000000000000/*LICENSE_START*/ /* * Copyright (C) 2015 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 "OperationSpecFileRelocate.h" #include "OperationException.h" #include "SpecFile.h" using namespace caret; using namespace std; AString OperationSpecFileRelocate::getCommandSwitch() { return "-spec-file-relocate"; } AString OperationSpecFileRelocate::getShortDescription() { return "RECREATE SPEC FILE IN NEW LOCATION"; } OperationParameters* OperationSpecFileRelocate::getParameters() { OperationParameters* ret = new OperationParameters(); ret->addStringParameter(1, "input-spec", "the spec file to use"); ret->addStringParameter(2, "output-spec", "output - the new spec file to create");//fake the output formatting ret->setHelpText( AString("Spec files contain internal relative paths, such that moving or copying a spec file will cause it to lose track of the files it refers to. ") + "This command makes a modified copy of the spec file, changing the relative paths to refer to the new relative locations of the files." ); return ret; } void OperationSpecFileRelocate::useParameters(OperationParameters* myParams, ProgressObject* myProgObj) { LevelProgress myProgress(myProgObj); SpecFile mySpec; mySpec.readFile(myParams->getString(1)); mySpec.writeFile(myParams->getString(2)); } connectome-workbench-1.2.3+git41-gc4c6c90/src/Operations/OperationSpecFileRelocate.h000066400000000000000000000026441300200146000301000ustar00rootroot00000000000000#ifndef __OPERATION_SPEC_FILE_RELOCATE_H__ #define __OPERATION_SPEC_FILE_RELOCATE_H__ /*LICENSE_START*/ /* * Copyright (C) 2015 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 OperationSpecFileRelocate : public AbstractOperation { public: static OperationParameters* getParameters(); static void useParameters(OperationParameters* myParams, ProgressObject* myProgObj); static AString getCommandSwitch(); static AString getShortDescription(); }; typedef TemplateAutoOperation AutoOperationSpecFileRelocate; } #endif //__OPERATION_SPEC_FILE_RELOCATE_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Operations/OperationSurfaceClosestVertex.cxx000066400000000000000000000070601300200146000314220ustar00rootroot00000000000000/*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 "OperationSurfaceClosestVertex.h" #include "OperationException.h" #include "SurfaceFile.h" #include using namespace caret; using namespace std; AString OperationSurfaceClosestVertex::getCommandSwitch() { return "-surface-closest-vertex"; } AString OperationSurfaceClosestVertex::getShortDescription() { return "FIND CLOSEST SURFACE VERTEX TO COORDINATES"; } OperationParameters* OperationSurfaceClosestVertex::getParameters() { OperationParameters* ret = new OperationParameters(); ret->addSurfaceParameter(1, "surface", "the surface to use"); ret->addStringParameter(2, "coord-list-file", "text file with coordinates"); ret->addStringParameter(3, "vertex-list-out", "output - the output text file with vertex numbers");//HACK: we don't currently have an "output text file" parameter type, fake the formatting ret->setHelpText( AString("For each coordinate XYZ triple, find the closest vertex in the surface, and output its vertex number into a text file. ") + "The input file should only use whitespace to separate coordinates (spaces, newlines, tabs), for instance:\n\n" + "20 30 25\n30 -20 10" ); return ret; } void OperationSurfaceClosestVertex::useParameters(OperationParameters* myParams, ProgressObject* myProgObj) { LevelProgress myProgress(myProgObj); SurfaceFile* mySurf = myParams->getSurface(1); AString coordFileName = myParams->getString(2); fstream coordFile(coordFileName.toLocal8Bit().constData(), fstream::in); if (!coordFile.good()) { throw OperationException("error opening coordinate list file for reading"); } AString nodeFileName = myParams->getString(3); fstream nodeFile(nodeFileName.toLocal8Bit().constData(), fstream::out); if (!nodeFile.good()) { throw OperationException("error opening output file for writing"); } vector coords; float x, y, z; while (coordFile >> x)//yes, really, thats how they intended it to be used, more or less { if (!(coordFile >> y >> z))//if we fail to read the rest of the triple, error { throw OperationException("read incomplete coordinate triple, would have been coordinate number " + AString::number(coords.size() / 3 + 1)); } coords.push_back(x); coords.push_back(y); coords.push_back(z); }//because of how we parse the file, we know that coords contains a multiple of 3 if (coords.empty()) { throw OperationException("did not find any coordinates in file, make sure you use only whitespace to separate numbers"); } for (int i = 0; i < (int)coords.size(); i += 3) { int node = mySurf->closestNode(coords.data() + i); nodeFile << node << endl; } } connectome-workbench-1.2.3+git41-gc4c6c90/src/Operations/OperationSurfaceClosestVertex.h000066400000000000000000000026741300200146000310550ustar00rootroot00000000000000#ifndef __OPERATION_SURFACE_CLOSEST_VERTEX_H__ #define __OPERATION_SURFACE_CLOSEST_VERTEX_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 "AbstractOperation.h" namespace caret { class OperationSurfaceClosestVertex : public AbstractOperation { public: static OperationParameters* getParameters(); static void useParameters(OperationParameters* myParams, ProgressObject* myProgObj); static AString getCommandSwitch(); static AString getShortDescription(); }; typedef TemplateAutoOperation AutoOperationSurfaceClosestVertex; } #endif //__OPERATION_SURFACE_CLOSEST_VERTEX_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Operations/OperationSurfaceCoordinatesToMetric.cxx000066400000000000000000000047271300200146000325400ustar00rootroot00000000000000/*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 "OperationSurfaceCoordinatesToMetric.h" #include "OperationException.h" #include "MetricFile.h" #include "SurfaceFile.h" using namespace caret; using namespace std; AString OperationSurfaceCoordinatesToMetric::getCommandSwitch() { return "-surface-coordinates-to-metric"; } AString OperationSurfaceCoordinatesToMetric::getShortDescription() { return "MAKE METRIC FILE OF SURFACE COORDINATES"; } OperationParameters* OperationSurfaceCoordinatesToMetric::getParameters() { OperationParameters* ret = new OperationParameters(); ret->addSurfaceParameter(1, "surface", "the surface to use the coordinates of"); ret->addMetricOutputParameter(2, "metric-out", "the output metric"); ret->setHelpText( AString("Puts the coordinates of the surface into a 3-map metric file, as x, y, z.") ); return ret; } void OperationSurfaceCoordinatesToMetric::useParameters(OperationParameters* myParams, ProgressObject* myProgObj) { LevelProgress myProgress(myProgObj); SurfaceFile* mySurf = myParams->getSurface(1); MetricFile* myMetricOut = myParams->getOutputMetric(2); int numNodes = mySurf->getNumberOfNodes(); myMetricOut->setNumberOfNodesAndColumns(numNodes, 3); myMetricOut->setStructure(mySurf->getStructure()); myMetricOut->setMapName(0, "x coordinate"); myMetricOut->setMapName(1, "y coordinate"); myMetricOut->setMapName(2, "z coordinate"); for (int i = 0; i < numNodes; ++i) { const float* coord = mySurf->getCoordinate(i); myMetricOut->setValue(i, 0, coord[0]); myMetricOut->setValue(i, 1, coord[1]); myMetricOut->setValue(i, 2, coord[2]); } } connectome-workbench-1.2.3+git41-gc4c6c90/src/Operations/OperationSurfaceCoordinatesToMetric.h000066400000000000000000000027431300200146000321610ustar00rootroot00000000000000#ifndef __OPERATION_SURFACE_COORDINATES_TO_METRIC_H__ #define __OPERATION_SURFACE_COORDINATES_TO_METRIC_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 "AbstractOperation.h" namespace caret { class OperationSurfaceCoordinatesToMetric : public AbstractOperation { public: static OperationParameters* getParameters(); static void useParameters(OperationParameters* myParams, ProgressObject* myProgObj); static AString getCommandSwitch(); static AString getShortDescription(); }; typedef TemplateAutoOperation AutoOperationSurfaceCoordinatesToMetric; } #endif //__OPERATION_SURFACE_COORDINATES_TO_METRIC_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Operations/OperationSurfaceCutResample.cxx000066400000000000000000000047541300200146000310430ustar00rootroot00000000000000/*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 "OperationSurfaceCutResample.h" #include "OperationException.h" #include "SurfaceResamplingHelper.h" using namespace caret; using namespace std; AString OperationSurfaceCutResample::getCommandSwitch() { return "-surface-cut-resample"; } AString OperationSurfaceCutResample::getShortDescription() { return "RESAMPLE A CUT SURFACE"; } OperationParameters* OperationSurfaceCutResample::getParameters() { OperationParameters* ret = new OperationParameters(); ret->addSurfaceParameter(1, "surface-in", "the surface file to resample"); ret->addSurfaceParameter(2, "current-sphere", "a sphere surface with the mesh that the input surface is currently on"); ret->addSurfaceParameter(3, "new-sphere", "a sphere surface that is in register with and has the desired output mesh"); ret->addSurfaceOutputParameter(4, "surface-out", "the output surface file"); ret->setHelpText( AString("Resamples a surface file, given two spherical surfaces that are in register. ") + "Barycentric resampling is used, because it is usually better for resampling surfaces, and because it is needed to figure out the new topology anyway." ); return ret; } void OperationSurfaceCutResample::useParameters(OperationParameters* myParams, ProgressObject* myProgObj) { LevelProgress myProgress(myProgObj); SurfaceFile* surfaceIn = myParams->getSurface(1); SurfaceFile* curSphere = myParams->getSurface(2); SurfaceFile* newSphere = myParams->getSurface(3); SurfaceFile* surfaceOut = myParams->getOutputSurface(4); SurfaceResamplingHelper::resampleCutSurface(surfaceIn, curSphere, newSphere, surfaceOut); } connectome-workbench-1.2.3+git41-gc4c6c90/src/Operations/OperationSurfaceCutResample.h000066400000000000000000000026601300200146000304620ustar00rootroot00000000000000#ifndef __OPERATION_SURFACE_CUT_RESAMPLE_H__ #define __OPERATION_SURFACE_CUT_RESAMPLE_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 "AbstractOperation.h" namespace caret { class OperationSurfaceCutResample : public AbstractOperation { public: static OperationParameters* getParameters(); static void useParameters(OperationParameters* myParams, ProgressObject* myProgObj); static AString getCommandSwitch(); static AString getShortDescription(); }; typedef TemplateAutoOperation AutoOperationSurfaceCutResample; } #endif //__OPERATION_SURFACE_CUT_RESAMPLE_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Operations/OperationSurfaceFlipNormals.cxx000066400000000000000000000043601300200146000310360ustar00rootroot00000000000000/*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 "OperationSurfaceFlipNormals.h" #include "OperationException.h" #include "SurfaceFile.h" using namespace caret; using namespace std; AString OperationSurfaceFlipNormals::getCommandSwitch() { return "-surface-flip-normals"; } AString OperationSurfaceFlipNormals::getShortDescription() { return "FLIP ALL TILES ON A SURFACE"; } OperationParameters* OperationSurfaceFlipNormals::getParameters() { OperationParameters* ret = new OperationParameters(); ret->addSurfaceParameter(1, "surface", "the surface to flip the normals of"); ret->addSurfaceOutputParameter(2, "surface-out", "the output surface"); ret->setHelpText( AString("Flips all triangles on a surface, resulting in surface normals being flipped the other direction (inward vs outward). ") + "If you transform a surface with an affine that has negative determinant, or a warpfield that similarly flips the surface, you may end up " + "with a surface that has normals pointing inwards, which may have display problems. " + "Using this command will solve that problem." ); return ret; } void OperationSurfaceFlipNormals::useParameters(OperationParameters* myParams, ProgressObject* myProgObj) { LevelProgress myProgress(myProgObj); SurfaceFile* mySurf = myParams->getSurface(1); SurfaceFile* mySurfOut = myParams->getOutputSurface(2); *mySurfOut = *mySurf; mySurfOut->flipNormals(); } connectome-workbench-1.2.3+git41-gc4c6c90/src/Operations/OperationSurfaceFlipNormals.h000066400000000000000000000026601300200146000304640ustar00rootroot00000000000000#ifndef __OPERATION_SURFACE_FLIP_NORMALS_H__ #define __OPERATION_SURFACE_FLIP_NORMALS_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 "AbstractOperation.h" namespace caret { class OperationSurfaceFlipNormals : public AbstractOperation { public: static OperationParameters* getParameters(); static void useParameters(OperationParameters* myParams, ProgressObject* myProgObj); static AString getCommandSwitch(); static AString getShortDescription(); }; typedef TemplateAutoOperation AutoOperationSurfaceFlipNormals; } #endif //__OPERATION_SURFACE_FLIP_NORMALS_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Operations/OperationSurfaceGeodesicDistance.cxx000066400000000000000000000074461300200146000320150ustar00rootroot00000000000000/*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 "OperationSurfaceGeodesicDistance.h" #include "OperationException.h" #include "CaretAssert.h" #include "GeodesicHelper.h" #include "MetricFile.h" #include "SurfaceFile.h" using namespace caret; using namespace std; AString OperationSurfaceGeodesicDistance::getCommandSwitch() { return "-surface-geodesic-distance"; } AString OperationSurfaceGeodesicDistance::getShortDescription() { return "COMPUTE GEODESIC DISTANCE FROM ONE VERTEX TO THE ENTIRE SURFACE"; } OperationParameters* OperationSurfaceGeodesicDistance::getParameters() { OperationParameters* ret = new OperationParameters(); ret->addSurfaceParameter(1, "surface", "the surface to compute on"); ret->addIntegerParameter(2, "vertex", "the vertex to compute geodesic distance from"); ret->addMetricOutputParameter(3, "metric-out", "the output metric"); ret->createOptionalParameter(4, "-naive", "use only neighbors, don't crawl triangles (not recommended)"); OptionalParameter* limitOpt = ret->createOptionalParameter(5, "-limit", "stop at a certain distance"); limitOpt->addDoubleParameter(1, "limit-mm", "distance in mm to stop at"); 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. " + "If -naive is not specified, it uses not just immediate neighbors, but also neighbors derived from crawling across pairs of triangles that share an edge." ); return ret; } void OperationSurfaceGeodesicDistance::useParameters(OperationParameters* myParams, ProgressObject* myProgObj) { LevelProgress myProgress(myProgObj); SurfaceFile* mySurf = myParams->getSurface(1); 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); if (limitOpt->m_present) { vector nodes; vector dists; myHelp->getNodesToGeoDist(myVertex, limitOpt->getDouble(1), nodes, dists, smooth); for (int i = 0; i < (int)nodes.size(); ++i) { CaretAssertVectorIndex(dists, i); scratch[nodes[i]] = dists[i]; } } else { myHelp->getGeoFromNode(myVertex, scratch, smooth); if (scratch.size() == 0) throw OperationException("invalid vertex specified"); } myMetricOut->setNumberOfNodesAndColumns(mySurf->getNumberOfNodes(), 1); myMetricOut->setStructure(mySurf->getStructure()); myMetricOut->setColumnName(0, "vertex " + AString::number(myVertex) + " distance"); myMetricOut->setValuesForColumn(0, scratch.data()); } connectome-workbench-1.2.3+git41-gc4c6c90/src/Operations/OperationSurfaceGeodesicDistance.h000066400000000000000000000027161300200146000314350ustar00rootroot00000000000000#ifndef __OPERATION_SURFACE_GEODESIC_DISTANCE_H__ #define __OPERATION_SURFACE_GEODESIC_DISTANCE_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 "AbstractOperation.h" namespace caret { class OperationSurfaceGeodesicDistance : public AbstractOperation { public: static OperationParameters* getParameters(); static void useParameters(OperationParameters* myParams, ProgressObject* myProgObj); static AString getCommandSwitch(); static AString getShortDescription(); }; typedef TemplateAutoOperation AutoOperationSurfaceGeodesicDistance; } #endif //__OPERATION_SURFACE_GEODESIC_DISTANCE_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Operations/OperationSurfaceGeodesicROIs.cxx000066400000000000000000000244301300200146000310670ustar00rootroot00000000000000/*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 "OperationSurfaceGeodesicROIs.h" #include "OperationException.h" #include "GeodesicHelper.h" #include "MetricFile.h" #include "SurfaceFile.h" #include #include #include using namespace caret; using namespace std; AString OperationSurfaceGeodesicROIs::getCommandSwitch() { return "-surface-geodesic-rois"; } AString OperationSurfaceGeodesicROIs::getShortDescription() { return "DRAW GEODESIC LIMITED ROIS AT VERTICES"; } OperationParameters* OperationSurfaceGeodesicROIs::getParameters() { OperationParameters* ret = new OperationParameters(); ret->addSurfaceParameter(1, "surface", "the surface to draw on"); ret->addDoubleParameter(2, "limit", "geodesic distance limit from vertex, in mm"); ret->addStringParameter(3, "vertex-list-file", "a text file containing the vertices to draw ROIs around"); ret->addMetricOutputParameter(4, "metric-out", "the output metric"); OptionalParameter* gaussOpt = ret->createOptionalParameter(5, "-gaussian", "generate a gaussian kernel instead of a flat ROI"); gaussOpt->addDoubleParameter(1, "sigma", "the sigma for the gaussian kernel, in mm"); OptionalParameter* overlapOpt = ret->createOptionalParameter(6, "-overlap-logic", "how to handle overlapping ROIs, default ALLOW"); overlapOpt->addStringParameter(1, "method", "the method of resolving overlaps"); 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"); 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. " + "If the -gaussian option is specified, the values inside the ROI will instead form a gaussian with the specified value of sigma, normalized " + "so that the sum of the nonzero values in the metric column is 1.0. The argument to -overlap-logic must be one of ALLOW, CLOSEST, or EXCLUDE. " + "ALLOW is the default, and means that ROIs are treated independently and may overlap. " + "CLOSEST means that ROIs may not overlap, and that no ROI contains vertices that are closer to a different seed vertex. " + "EXCLUDE means that ROIs may not overlap, and that any vertex within range of more than one ROI does not belong to any ROI." ); return ret; } void OperationSurfaceGeodesicROIs::useParameters(OperationParameters* myParams, ProgressObject* myProgObj) { LevelProgress myProgress(myProgObj); SurfaceFile* mySurf = myParams->getSurface(1); float limit = (float)myParams->getDouble(2); AString nodeFileName = myParams->getString(3); MetricFile* myMetricOut = myParams->getOutputMetric(4); OptionalParameter* gaussOpt = myParams->getOptionalParameter(5); float sigma = -1.0f; if (gaussOpt->m_present) {//set up to use a gaussian function sigma = (float)gaussOpt->getDouble(1); if (sigma <= 0.0f) { throw OperationException("invalid sigma specified"); } } fstream textFile(nodeFileName.toLocal8Bit().constData(), fstream::in); if (!textFile.good()) { throw OperationException("error opening list file for reading"); } int nodenum, numNodes = mySurf->getNumberOfNodes(); vector nodelist; textFile >> nodenum; while (textFile) { if (nodenum < 0 || nodenum >= numNodes) { throw OperationException("invalid vertex number: " + AString::number(nodenum)); } nodelist.push_back(nodenum); textFile >> nodenum; } int overlapType = 1;//ALLOW OptionalParameter* overlapOpt = myParams->getOptionalParameter(6); if (overlapOpt->m_present) { AString overlapString = overlapOpt->getString(1); if (overlapString == "ALLOW") { overlapType = 1; } else if (overlapString == "CLOSEST") { overlapType = 2; } else if (overlapString == "EXCLUDE") { overlapType = 3; } else { throw OperationException("unrecognized overlap method: " + overlapString); } } vector namesList; OptionalParameter* namesOpt = myParams->getOptionalParameter(7); if (namesOpt->m_present) { AString namesFileName = namesOpt->getString(1); fstream namesfile(namesFileName.toLocal8Bit().constData(), fstream::in); if (!namesfile.good()) { throw OperationException("error opening names file for reading"); } int i = 0; string inputline; getline(namesfile, inputline); while (namesfile && i < (int)nodelist.size()) { namesList.push_back(inputline.c_str()); getline(namesfile, inputline); ++i; } } myMetricOut->setNumberOfNodesAndColumns(numNodes, (int)nodelist.size()); myMetricOut->setStructure(mySurf->getStructure()); float invneg2sigmasqr = -0.5f / (sigma * sigma); for (int i = 0; i < (int)nodelist.size(); ++i) { myMetricOut->initializeColumn(i); if (i < (int)namesList.size()) { myMetricOut->setColumnName(i, namesList[i]); } else { const float* myCoord = mySurf->getCoordinate(i); myMetricOut->setColumnName(i, "Vertex " + AString::number(nodelist[i]) + " (" + AString::number(myCoord[0], 'f', 1) + ", " + AString::number(myCoord[1], 'f', 1) + ", " + AString::number(myCoord[2], 'f', 1) + ")"); } } 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); if (sigma > 0.0f) { double accum = 0.0; for (int j = 0; j < (int)dists.size(); ++j) { dists[j] = exp(dists[j] * dists[j] * invneg2sigmasqr);//reuse the vector for weights accum += dists[j]; } for (int j = 0; j < (int)dists.size(); ++j) { dists[j] /= accum; myMetricOut->setValue(roinodes[j], i, dists[j]); } } else { for (int j = 0; j < (int)roinodes.size(); ++j) { myMetricOut->setValue(roinodes[j], i, 1.0f); } } } break; case 2: case 3: { vector useCounts(numNodes, 0); vector closestSeed(numNodes, -1); 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); for (int j = 0; j < (int)roinodes.size(); ++j) { ++useCounts[roinodes[j]]; if (bestDists[roinodes[j]] < 0.0f || dists[j] < bestDists[roinodes[j]]) { bestDists[roinodes[j]] = dists[j]; closestSeed[roinodes[j]] = i;//nodelist array index, not node number } } } if (sigma > 0.0f) { vector accums(nodelist.size(), 0.0); vector > roinodelists(nodelist.size()); vector > weightlists(nodelist.size()); for (int i = 0; i < numNodes; ++i) { if (closestSeed[i] != -1 && (overlapType == 2 || useCounts[i] == 1)) { roinodelists[closestSeed[i]].push_back(i); float weight = exp(bestDists[i] * bestDists[i] * invneg2sigmasqr); weightlists[closestSeed[i]].push_back(weight); accums[closestSeed[i]] += weight; } } for (int i = 0; i < (int)nodelist.size(); ++i) { for (int j = 0; j < (int)roinodelists[i].size(); ++j) { myMetricOut->setValue(roinodelists[i][j], i, weightlists[i][j] / accums[i]); } } } else { for (int i = 0; i < numNodes; ++i) { if (closestSeed[i] != -1 && (overlapType == 2 || useCounts[i] == 1)) { myMetricOut->setValue(i, closestSeed[i], 1.0f); } } } break; } default: throw OperationException("something very bad happened, notify the developers"); } } connectome-workbench-1.2.3+git41-gc4c6c90/src/Operations/OperationSurfaceGeodesicROIs.h000066400000000000000000000026661300200146000305230ustar00rootroot00000000000000#ifndef __OPERATION_SURFACE_GEODESIC_ROIS_H__ #define __OPERATION_SURFACE_GEODESIC_ROIS_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 "AbstractOperation.h" namespace caret { class OperationSurfaceGeodesicROIs : public AbstractOperation { public: static OperationParameters* getParameters(); static void useParameters(OperationParameters* myParams, ProgressObject* myProgObj); static AString getCommandSwitch(); static AString getShortDescription(); }; typedef TemplateAutoOperation AutoOperationSurfaceGeodesicROIs; } #endif //__OPERATION_SURFACE_GEODESIC_ROIS_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Operations/OperationSurfaceInformation.cxx000066400000000000000000000046071300200146000311010ustar00rootroot00000000000000 /*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 "CaretAssert.h" #include "CaretLogger.h" #include "OperationSurfaceInformation.h" #include "OperationException.h" #include "DataFileException.h" #include "SurfaceFile.h" using namespace caret; /** * \class caret::OperationSurfaceInformation * \brief Display information about a surface */ /** * @return Command line switch */ AString OperationSurfaceInformation::getCommandSwitch() { return "-surface-information"; } /** * @return Short description of operation */ AString OperationSurfaceInformation::getShortDescription() { return "DISPLAY INFORMATION ABOUT A SURFACE"; } /** * @return Parameters for operation */ OperationParameters* OperationSurfaceInformation::getParameters() { OperationParameters* ret = new OperationParameters(); ret->addSurfaceParameter(1, "Surface File", "Surface for which information is displayed"); AString helpText = ("Information about surface is displayed including vertices, \n" "triangles, bounding box, and spacing."); ret->setHelpText(helpText); return ret; } /** * Use Parameters and perform operation */ void OperationSurfaceInformation::useParameters(OperationParameters* myParams, ProgressObject* myProgObj) { LevelProgress myProgress(myProgObj); try { SurfaceFile* surfaceFile = myParams->getSurface(1); std::cout << qPrintable(surfaceFile->getInformation()) << std::endl; } catch (const DataFileException& dfe) { throw OperationException(dfe); } } connectome-workbench-1.2.3+git41-gc4c6c90/src/Operations/OperationSurfaceInformation.h000066400000000000000000000027351300200146000305260ustar00rootroot00000000000000#ifndef __OPERATION_SURFACE_INFORMATION_H__ #define __OPERATION_SURFACE_INFORMATION_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 "AbstractOperation.h" namespace caret { class OperationSurfaceInformation : public AbstractOperation { public: static OperationParameters* getParameters(); static void useParameters(OperationParameters* myParams, ProgressObject* myProgObj); static AString getCommandSwitch(); static AString getShortDescription(); }; typedef TemplateAutoOperation AutoOperationSurfaceInformation; } // namespace #endif //__OPERATION_SURFACE_INFORMATION_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Operations/OperationSurfaceNormals.cxx000066400000000000000000000050571300200146000302270ustar00rootroot00000000000000/*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 "OperationSurfaceNormals.h" #include "OperationException.h" #include "MetricFile.h" #include "SurfaceFile.h" using namespace caret; using namespace std; AString OperationSurfaceNormals::getCommandSwitch() { return "-surface-normals"; } AString OperationSurfaceNormals::getShortDescription() { return "OUTPUT VERTEX NORMALS AS METRIC FILE"; } OperationParameters* OperationSurfaceNormals::getParameters() { OperationParameters* ret = new OperationParameters(); ret->addSurfaceParameter(1, "surface", "the surface to output the normals of"); ret->addMetricOutputParameter(2, "metric-out", "the normal vectors"); ret->setHelpText( AString("Computes the normal vectors of the surface file, and outputs them as a 3 column metric file.") ); return ret; } void OperationSurfaceNormals::useParameters(OperationParameters* myParams, ProgressObject* myProgObj) { LevelProgress myProgress(myProgObj); SurfaceFile* mySurf = myParams->getSurface(1); MetricFile* myMetricOut = myParams->getOutputMetric(2); int numNodes = mySurf->getNumberOfNodes(); myMetricOut->setNumberOfNodesAndColumns(numNodes, 3); myMetricOut->setStructure(mySurf->getStructure()); myMetricOut->setColumnName(0, "normal x-component"); myMetricOut->setColumnName(1, "normal y-component"); myMetricOut->setColumnName(2, "normal z-component"); mySurf->computeNormals(); const float* normalData = mySurf->getNormalData(); vector colScratch(numNodes); for (int axis = 0; axis < 3; ++axis) { for (int i = 0; i < numNodes; ++i) { colScratch[i] = normalData[i * 3 + axis]; } myMetricOut->setValuesForColumn(axis, colScratch.data()); } } connectome-workbench-1.2.3+git41-gc4c6c90/src/Operations/OperationSurfaceNormals.h000066400000000000000000000026251300200146000276520ustar00rootroot00000000000000#ifndef __OPERATION_SURFACE_NORMALS_H__ #define __OPERATION_SURFACE_NORMALS_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 "AbstractOperation.h" namespace caret { class OperationSurfaceNormals : public AbstractOperation { public: static OperationParameters* getParameters(); static void useParameters(OperationParameters* myParams, ProgressObject* myProgObj); static AString getCommandSwitch(); static AString getShortDescription(); }; typedef TemplateAutoOperation AutoOperationSurfaceNormals; } #endif //__OPERATION_SURFACE_NORMALS_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Operations/OperationSurfaceVertexAreas.cxx000066400000000000000000000043441300200146000310430ustar00rootroot00000000000000/*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 "OperationSurfaceVertexAreas.h" #include "OperationException.h" #include "MetricFile.h" #include "SurfaceFile.h" #include using namespace caret; using namespace std; AString OperationSurfaceVertexAreas::getCommandSwitch() { return "-surface-vertex-areas"; } AString OperationSurfaceVertexAreas::getShortDescription() { return "MEASURE SURFACE AREA EACH VERTEX IS RESPONSIBLE FOR"; } OperationParameters* OperationSurfaceVertexAreas::getParameters() { OperationParameters* ret = new OperationParameters(); ret->addSurfaceParameter(1, "surface", "the surface to measure"); ret->addMetricOutputParameter(2, "metric", "the output metric"); ret->setHelpText( AString("Each vertex gets one third of the area of each triangle it is a part of. ") + "Units are mm^2." ); return ret; } void OperationSurfaceVertexAreas::useParameters(OperationParameters* myParams, ProgressObject* myProgObj) { LevelProgress myProgress(myProgObj); SurfaceFile* mySurf = myParams->getSurface(1); MetricFile* myMetricOut = myParams->getOutputMetric(2); int numNodes = mySurf->getNumberOfNodes(); vector areas; mySurf->computeNodeAreas(areas); myMetricOut->setNumberOfNodesAndColumns(numNodes, 1); myMetricOut->setStructure(mySurf->getStructure()); myMetricOut->setColumnName(0, "vertex areas"); myMetricOut->setValuesForColumn(0, areas.data()); } connectome-workbench-1.2.3+git41-gc4c6c90/src/Operations/OperationSurfaceVertexAreas.h000066400000000000000000000026601300200146000304670ustar00rootroot00000000000000#ifndef __OPERATION_SURFACE_VERTEX_AREAS_H__ #define __OPERATION_SURFACE_VERTEX_AREAS_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 "AbstractOperation.h" namespace caret { class OperationSurfaceVertexAreas : public AbstractOperation { public: static OperationParameters* getParameters(); static void useParameters(OperationParameters* myParams, ProgressObject* myProgObj); static AString getCommandSwitch(); static AString getShortDescription(); }; typedef TemplateAutoOperation AutoOperationSurfaceVertexAreas; } #endif //__OPERATION_SURFACE_VERTEX_AREAS_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Operations/OperationTemplate.cxx.txt000066400000000000000000000056611300200146000276750ustar00rootroot00000000000000/*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 "OperationName.h" #include "OperationException.h" using namespace caret; using namespace std; AString OperationName::getCommandSwitch() { return "-command-switch"; } AString OperationName::getShortDescription() { return "SHORT DESCRIPTION"; } OperationParameters* OperationName::getParameters() { OperationParameters* ret = new OperationParameters(); //ret->addSurfaceParameter(1, "surface", "the surface to compute on"); //ret->addMetricOutputParameter(2, "metric-out", "the output metric"); //OptionalParameter* columnSelect = ret->createOptionalParameter(3, "-column", "select a single column"); //columnSelect->addStringParameter(1, "column", "the column number or name"); ret->setHelpText( AString("This is where you set the help text. ") + "DO NOT add the info about what the command line format is, and do not give the command switch, " + "short description, or the short descriptions of parameters. " + "Do not indent, manually break long lines, or format the text in any way " + "other than to separate paragraphs within the help text prose, usually with two newlines." ); return ret; } void OperationName::useParameters(OperationParameters* myParams, ProgressObject* myProgObj) { LevelProgress myProgress(myProgObj); //SurfaceFile* mySurf = myParams->getSurface(1);//gets the surface with key 1 //MetricFile* myMetricOut = myParams->getOutputMetric(2);//gets the output metric with key 2 //OptionalParameter* columnSelect = myParams->getOptionalParameter(3);//gets optional parameter with key 3 /*int columnNum = -1; if (columnSelect->m_present) {//set up to use the single column columnNum = (int)myMetric->getMapIndexFromNameOrNumber(columnSelect->getString(1)); if (columnNum < 0 || columnNum >= myMetric->getNumberOfMaps()) { throw OperationException("invalid column specified"); } }//*/ //do the work here //myProgress.reportProgress(0.5f);//this is how you would report being half finished, if the operation takes a while (probably not) } connectome-workbench-1.2.3+git41-gc4c6c90/src/Operations/OperationTemplate.h.txt000066400000000000000000000050411300200146000273120ustar00rootroot00000000000000#ifndef __OPERATION_NAME_H__ #define __OPERATION_NAME_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*/ /* file->save as... and enter what you will name the class, plus .h find and replace these strings in plain text mode (not "whole word only"): OperationName : operation name, in CamelCase, with initial capital, same as what you saved the header file to OPERATION_NAME : uppercase of operation name, with underscore between words, used in #ifdef guards next, make OperationName.cxx from OperationTemplate.cxx.txt via one of the following (depending on working directory): cat OperationTemplate.cxx.txt | sed 's/[O]perationName/OperationName/g' > OperationName.cxx cat Operations/OperationTemplate.cxx.txt | sed 's/[O]perationName/OperationName/g' > Operations/OperationName.cxx cat src/Operations/OperationTemplate.cxx.txt | sed 's/[O]perationName/OperationName/g' > src/Operations/OperationName.cxx or manually copy and replace next, implement its functions add these to Operations/CMakeLists.txt: OperationName.h OperationName.cxx place the following lines into Commands/CommandOperationManager.cxx: #include "OperationName.h" //near the top this->commandOperations.push_back(new CommandParser(new AutoOperationName())); //in CommandOperationManager() finally, remove this block comment */ #include "AbstractOperation.h" namespace caret { class OperationName : public AbstractOperation { public: static OperationParameters* getParameters(); static void useParameters(OperationParameters* myParams, ProgressObject* myProgObj); static AString getCommandSwitch(); static AString getShortDescription(); }; typedef TemplateAutoOperation AutoOperationName; } #endif //__OPERATION_NAME_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Operations/OperationVolumeCapturePlane.cxx000066400000000000000000000144101300200146000310470ustar00rootroot00000000000000/*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 "OperationVolumeCapturePlane.h" #include "OperationException.h" #include "CaretLogger.h" #include "ImageFile.h" #include "Vector3D.h" #include "VolumeFile.h" #include using namespace caret; using namespace std; AString OperationVolumeCapturePlane::getCommandSwitch() { return "-volume-capture-plane"; } AString OperationVolumeCapturePlane::getShortDescription() { return "INTERPOLATE IMAGE FROM PLANE THROUGH VOLUME"; } OperationParameters* OperationVolumeCapturePlane::getParameters() { OperationParameters* ret = new OperationParameters(); ret->addVolumeParameter(1, "volume", "the volume file to interpolate from"); ret->addStringParameter(2, "subvolume", "the name or number of the subvolume to use"); ret->addStringParameter(3, "interp", "interpolation type"); ret->addIntegerParameter(4, "h-dim", "width of output image, in pixels"); ret->addIntegerParameter(5, "v-dim", "height of output image, in pixels"); ret->addDoubleParameter(6, "scale-min", "value to render as black"); ret->addDoubleParameter(7, "scale-max", "value to render as white"); int key = 8; char xyz[] = "xyz"; for (int i = 0; i < 3; ++i) { ret->addDoubleParameter(key, AString("bottom-left-") + xyz[i], xyz[i] + AString("-coordinate of the bottom left of the output image")); ++key; }//8 9 10 for (int i = 0; i < 3; ++i) { ret->addDoubleParameter(key, AString("bottom-right-") + xyz[i], xyz[i] + AString("-coordinate of the bottom right of the output image")); ++key; }//11 12 13 for (int i = 0; i < 3; ++i) { ret->addDoubleParameter(key, AString("top-left-") + xyz[i], xyz[i] + AString("-coordinate of the top left of the output image")); ++key; }//14 15 16 ret->addStringParameter(17, "image", "output - the output image");//fake the output formatting ret->setHelpText( AString("NOTE: If you want to generate an image with all of the capabilities of the GUI rendering, see -show-scene.\n\n") + "Renders an image of an arbitrary plane through the volume file, with a simple linear grayscale palette. " + "The parameter must be one of:\n\n" + "CUBIC\nENCLOSING_VOXEL\nTRILINEAR" ); return ret; } void OperationVolumeCapturePlane::useParameters(OperationParameters* myParams, ProgressObject* myProgObj) { LevelProgress myProgress(myProgObj); VolumeFile* myVol = myParams->getVolume(1); AString subvolName = myParams->getString(2); int subvol = myVol->getMapIndexFromNameOrNumber(subvolName); if (subvol < 0 || subvol >= myVol->getNumberOfMaps()) { throw OperationException("invalid subvolume"); } AString interp = myParams->getString(3); VolumeFile::InterpType myMethod = VolumeFile::CUBIC; if (interp == "CUBIC") { myMethod = VolumeFile::CUBIC; } else if (interp == "TRILINEAR") { myMethod = VolumeFile::TRILINEAR; } else if (interp == "ENCLOSING_VOXEL") { myMethod = VolumeFile::ENCLOSING_VOXEL; } else { throw OperationException("unrecognized interpolation method"); } int width = (int)myParams->getInteger(4); int height = (int)myParams->getInteger(5); if (width < 2 || height < 2) { throw OperationException("output image dimensions must be 2 or greater");//to avoid divide by zero } float scalemin = (float)myParams->getDouble(6); float scalemax = (float)myParams->getDouble(7); Vector3D blvec, brvec, tlvec; int key = 8; for (int i = 0; i < 3; ++i) { blvec[i] = (float)myParams->getDouble(key); ++key; } for (int i = 0; i < 3; ++i) { brvec[i] = (float)myParams->getDouble(key); ++key; } for (int i = 0; i < 3; ++i) { tlvec[i] = (float)myParams->getDouble(key); ++key; } AString outName = myParams->getString(17); Vector3D rightTraverse = brvec - blvec, upTraverse = tlvec - blvec; if (abs(rightTraverse.normal().dot(upTraverse.normal())) > 0.001f) { CaretLogWarning("corner points describe non-orthogonal directions, image will be skewed"); } vector imageData(width * height * 4); for (int h = 0; h < height; ++h) { Vector3D rowStart = blvec + ((float)h) / (height - 1) * upTraverse; for (int w = 0; w < width; ++w) { Vector3D sample = rowStart + ((float)w) / (width - 1) * rightTraverse; bool valid = false; float value = myVol->interpolateValue(sample, myMethod, &valid, subvol); float normalized; if (valid) { if (value <= scalemin) { normalized = 0.0f; } else { if (value >= scalemax) { normalized = 1.0f; } else { normalized = (value - scalemin) / (scalemax - scalemin); } } } else { normalized = 0.0f; } uint8_t intensity = (uint8_t)(normalized * 255 + 0.5f); uint8_t* pixel = imageData.data() + (w + h * width) * 4; for (int i = 0; i < 4; ++i) { pixel[i] = intensity; } } } ImageFile outFile(imageData.data(), width, height, ImageFile::IMAGE_DATA_ORIGIN_AT_BOTTOM); outFile.writeFile(outName); } connectome-workbench-1.2.3+git41-gc4c6c90/src/Operations/OperationVolumeCapturePlane.h000066400000000000000000000026601300200146000305000ustar00rootroot00000000000000#ifndef __OPERATION_VOLUME_CAPTURE_PLANE_H__ #define __OPERATION_VOLUME_CAPTURE_PLANE_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 "AbstractOperation.h" namespace caret { class OperationVolumeCapturePlane : public AbstractOperation { public: static OperationParameters* getParameters(); static void useParameters(OperationParameters* myParams, ProgressObject* myProgObj); static AString getCommandSwitch(); static AString getShortDescription(); }; typedef TemplateAutoOperation AutoOperationVolumeCapturePlane; } #endif //__OPERATION_VOLUME_CAPTURE_PLANE_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Operations/OperationVolumeCopyExtensions.cxx000066400000000000000000000105061300200146000314600ustar00rootroot00000000000000/*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 "OperationVolumeCopyExtensions.h" #include "OperationException.h" #include "GiftiLabelTable.h" #include "GiftiMetaData.h" #include "NiftiHeader.h" //for NiftiExtension #include "PaletteColorMapping.h" #include "VolumeFile.h" using namespace caret; using namespace std; AString OperationVolumeCopyExtensions::getCommandSwitch() { return "-volume-copy-extensions"; } AString OperationVolumeCopyExtensions::getShortDescription() { return "COPY EXTENDED DATA TO ANOTHER VOLUME FILE"; } OperationParameters* OperationVolumeCopyExtensions::getParameters() { OperationParameters* ret = new OperationParameters(); ret->addVolumeParameter(1, "data-volume", "the volume file containing the voxel data to use"); ret->addVolumeParameter(2, "extension-volume", "the volume file containing the extensions to use"); ret->addVolumeOutputParameter(3, "volume-out", "the output volume"); ret->createOptionalParameter(4, "-drop-unknown", "don't copy extensions that workbench doesn't understand"); ret->setHelpText( AString("This command copies the information in a volume file that isn't a critical part of the standard header or data matrix, ") + "e.g. map names, palette settings, label tables. " + "If -drop-unknown is not specified, it also copies similar kinds of information set by other software." ); return ret; } void OperationVolumeCopyExtensions::useParameters(OperationParameters* myParams, ProgressObject* myProgObj) { LevelProgress myProgress(myProgObj); VolumeFile* dataVol = myParams->getVolume(1); VolumeFile* extVol = myParams->getVolume(2); VolumeFile* outVol = myParams->getOutputVolume(3); bool dropUnknown = myParams->getOptionalParameter(4)->m_present; if (!dataVol->getVolumeSpace().matches(extVol->getVolumeSpace())) throw OperationException("volume spaces do not match"); vector dataDims = dataVol->getDimensions(); vector extDims = extVol->getDimensions(); if (dataDims[4] != extDims[4]) throw OperationException("number of components (rgb or complex datatypes) does not match"); if (dataVol->getOriginalDimensions() != extVol->getOriginalDimensions()) throw OperationException("non-spatial dimensions do not match"); outVol->reinitialize(dataVol->getOriginalDimensions(), dataVol->getSform(), dataDims[4], extVol->getType()); outVol->m_header.grabNew(extVol->m_header->clone());//copy all standard header fields, too if (dropUnknown) { switch (outVol->m_header->getType()) { case AbstractHeader::NIFTI: ((NiftiHeader*)outVol->m_header.getPointer())->m_extensions.clear(); break; } } if (outVol->getFileMetaData() != NULL) { *(outVol->getFileMetaData()) = *(extVol->getFileMetaData()); } for (int64_t c = 0; c < dataDims[4]; ++c) { for (int64_t b = 0; b < dataDims[3]; ++b) { if (c == 0)//map names, etc { outVol->setMapName(b, extVol->getMapName(b)); *(outVol->getMapMetaData(b)) = *(extVol->getMapMetaData(b)); if (extVol->getType() == SubvolumeAttributes::LABEL) { *(outVol->getMapLabelTable(b)) = *(extVol->getMapLabelTable(b)); } else { *(outVol->getMapPaletteColorMapping(b)) = *(extVol->getMapPaletteColorMapping(b)); } } outVol->setFrame(dataVol->getFrame(b, c), b, c); } } } connectome-workbench-1.2.3+git41-gc4c6c90/src/Operations/OperationVolumeCopyExtensions.h000066400000000000000000000026741300200146000311140ustar00rootroot00000000000000#ifndef __OPERATION_VOLUME_COPY_EXTENSIONS_H__ #define __OPERATION_VOLUME_COPY_EXTENSIONS_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 "AbstractOperation.h" namespace caret { class OperationVolumeCopyExtensions : public AbstractOperation { public: static OperationParameters* getParameters(); static void useParameters(OperationParameters* myParams, ProgressObject* myProgObj); static AString getCommandSwitch(); static AString getShortDescription(); }; typedef TemplateAutoOperation AutoOperationVolumeCopyExtensions; } #endif //__OPERATION_VOLUME_COPY_EXTENSIONS_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Operations/OperationVolumeCreate.cxx000066400000000000000000000137031300200146000276730ustar00rootroot00000000000000/*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 "OperationVolumeCreate.h" #include "OperationException.h" #include "FloatMatrix.h" #include "VolumeFile.h" using namespace caret; using namespace std; AString OperationVolumeCreate::getCommandSwitch() { return "-volume-create"; } AString OperationVolumeCreate::getShortDescription() { return "CREATE A BLANK VOLUME FILE"; } OperationParameters* OperationVolumeCreate::getParameters() { OperationParameters* ret = new OperationParameters(); ret->addIntegerParameter(1, "i-dim", "length of first dimension"); ret->addIntegerParameter(2, "j-dim", "length of second dimension"); ret->addIntegerParameter(3, "k-dim", "length of third dimension"); ret->addVolumeOutputParameter(4, "volume-out", "the output volume"); OptionalParameter* plumbOpt = ret->createOptionalParameter(5, "-plumb", "set via axis order and spacing/offset"); plumbOpt->addStringParameter(1, "axis-order", "a string like 'XYZ' that specifies which index is along which spatial dimension"); plumbOpt->addDoubleParameter(2, "x-spacing", "change in x-coordinate from incrementing the relevant index"); plumbOpt->addDoubleParameter(3, "y-spacing", "change in y-coordinate from incrementing the relevant index"); plumbOpt->addDoubleParameter(4, "z-spacing", "change in z-coordinate from incrementing the relevant index"); plumbOpt->addDoubleParameter(5, "x-offset", "the x-coordinate of the first voxel"); plumbOpt->addDoubleParameter(6, "y-offset", "the y-coordinate of the first voxel"); plumbOpt->addDoubleParameter(7, "z-offset", "the z-coordinate of the first voxel"); OptionalParameter* sformOpt = ret->createOptionalParameter(6, "-sform", "set via a nifti sform"); char axisNames[] = "xyz", indexNames[] = "ijk"; for (int axis = 0; axis < 3; ++axis) { for (int index = 0; index < 3; ++index) { sformOpt->addDoubleParameter(axis * 4 + index, AString(axisNames[axis]) + indexNames[index] + "-spacing", "increase in " + AString(axisNames[axis]) + " coordinate from incrementing the " + indexNames[index] + " index"); } sformOpt->addDoubleParameter(axis * 4 + 3, AString(axisNames[axis]) + "-offset", AString(axisNames[axis]) + " coordinate of first voxel"); } ret->setHelpText( AString("Creates a volume file full of zeros. ") + "Exactly one of -plumb or -sform must be specified." ); return ret; } void OperationVolumeCreate::useParameters(OperationParameters* myParams, ProgressObject* myProgObj) { LevelProgress myProgress(myProgObj); vector dims(3); dims[0] = myParams->getInteger(1); dims[1] = myParams->getInteger(2); dims[2] = myParams->getInteger(3); VolumeFile* output = myParams->getOutputVolume(4); FloatMatrix newSform = FloatMatrix::zeros(4, 4); newSform[3][3] = 1.0f;//not needed, but just for sanity bool haveSpace = false; OptionalParameter* plumbOpt = myParams->getOptionalParameter(5); if (plumbOpt->m_present) { haveSpace = true; bool used[3] = {false, false, false}; int revorder[3] = {-1, -1, -1}; AString orient = plumbOpt->getString(1); if (orient.size() < 3) throw OperationException(" must have 3 characters"); for (int i = 0; i < 3; ++i) { int dir = -1; switch (orient[i].toAscii()) { case 'X': case 'x': dir = 0; break; case 'Y': case 'y': dir = 1; break; case 'Z': case 'z': dir = 2; break; default: throw OperationException(" must use the characters X, Y, and Z"); } if (used[dir]) throw OperationException(" may not repeat an axis"); used[dir] = true; revorder[dir] = i;//construct the reversed order, because thats what we need } newSform[0][revorder[0]] = (float)plumbOpt->getDouble(2); newSform[0][3] = (float)plumbOpt->getDouble(5); newSform[1][revorder[1]] = (float)plumbOpt->getDouble(3); newSform[1][3] = (float)plumbOpt->getDouble(6); newSform[2][revorder[2]] = (float)plumbOpt->getDouble(4); newSform[2][3] = (float)plumbOpt->getDouble(7); } OptionalParameter* sformOpt = myParams->getOptionalParameter(6); if (sformOpt->m_present) { if (haveSpace) throw OperationException("only one of -plumb and -sform may be specified"); haveSpace = true; for (int axis = 0; axis < 3; ++axis) { for (int index = 0; index < 3; ++index) { newSform[axis][index] = (float)sformOpt->getDouble(axis * 4 + index); } newSform[axis][3] = (float)sformOpt->getDouble(axis * 4 + 3); } } if (!haveSpace) throw OperationException("you must specify -plumb or -sform"); output->reinitialize(dims, newSform.getMatrix()); output->setValueAllVoxels(0.0f); } connectome-workbench-1.2.3+git41-gc4c6c90/src/Operations/OperationVolumeCreate.h000066400000000000000000000026111300200146000273140ustar00rootroot00000000000000#ifndef __OPERATION_VOLUME_CREATE_H__ #define __OPERATION_VOLUME_CREATE_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 "AbstractOperation.h" namespace caret { class OperationVolumeCreate : public AbstractOperation { public: static OperationParameters* getParameters(); static void useParameters(OperationParameters* myParams, ProgressObject* myProgObj); static AString getCommandSwitch(); static AString getShortDescription(); }; typedef TemplateAutoOperation AutoOperationVolumeCreate; } #endif //__OPERATION_VOLUME_CREATE_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Operations/OperationVolumeLabelExportTable.cxx000066400000000000000000000071511300200146000316610ustar00rootroot00000000000000/*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 "OperationVolumeLabelExportTable.h" #include "OperationException.h" #include "GiftiLabel.h" //we should rename these to not imply that they are gifti-specific #include "GiftiLabelTable.h" #include "VolumeFile.h" #include #include using namespace caret; using namespace std; AString OperationVolumeLabelExportTable::getCommandSwitch() { return "-volume-label-export-table"; } AString OperationVolumeLabelExportTable::getShortDescription() { return "EXPORT LABEL TABLE FROM VOLUME AS TEXT"; } OperationParameters* OperationVolumeLabelExportTable::getParameters() { OperationParameters* ret = new OperationParameters(); ret->addVolumeParameter(1, "label-in", "the input volume label file"); ret->addStringParameter(2, "map", "the number or name of the label map to use"); ret->addStringParameter(3, "table-out", "output - the output text file");//fake output formatting ret->setHelpText( AString("Takes the label table from the volume label map, and writes it to a text format matching what is expected by -volume-label-import.") ); return ret; } int OperationVolumeLabelExportTable::floatTo255(const float& in) { return (int)floor(in * 255.0f + 0.5f); } void OperationVolumeLabelExportTable::useParameters(OperationParameters* myParams, ProgressObject* myProgObj) { LevelProgress myProgress(myProgObj); VolumeFile* myVolume = myParams->getVolume(1); AString mapString = myParams->getString(2); AString outfileName = myParams->getString(3); if (myVolume->getType() != SubvolumeAttributes::LABEL) throw OperationException("volume file must be a label volume"); int64_t mapIndex = myVolume->getMapIndexFromNameOrNumber(mapString); if (mapIndex == -1) throw OperationException("map '" + mapString + "' not found"); ofstream outFile(outfileName.toLocal8Bit().constData()); if (!outFile) throw OperationException("failed to open output text file"); const GiftiLabelTable* myTable = myVolume->getMapLabelTable(mapIndex); set allKeys = myTable->getKeys(); int32_t unassignedKey = myTable->getUnassignedLabelKey(); for (set::iterator iter = allKeys.begin(); iter != allKeys.end(); ++iter) { if (*iter == unassignedKey) continue;//don't output the unused key, because import doesn't want it in the text file const GiftiLabel* thisLabel = myTable->getLabel(*iter); outFile << thisLabel->getName() << endl; outFile << thisLabel->getKey() << " " << floatTo255(thisLabel->getRed()) << " " << floatTo255(thisLabel->getGreen()) << " " << floatTo255(thisLabel->getBlue()) << " " << floatTo255(thisLabel->getAlpha()) << endl; if (!outFile) throw OperationException("error writing to output text file"); } } connectome-workbench-1.2.3+git41-gc4c6c90/src/Operations/OperationVolumeLabelExportTable.h000066400000000000000000000027731300200146000313130ustar00rootroot00000000000000#ifndef __OPERATION_VOLUME_LABEL_EXPORT_TABLE_H__ #define __OPERATION_VOLUME_LABEL_EXPORT_TABLE_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 "AbstractOperation.h" namespace caret { class OperationVolumeLabelExportTable : public AbstractOperation { static int floatTo255(const float& in); public: static OperationParameters* getParameters(); static void useParameters(OperationParameters* myParams, ProgressObject* myProgObj); static AString getCommandSwitch(); static AString getShortDescription(); }; typedef TemplateAutoOperation AutoOperationVolumeLabelExportTable; } #endif //__OPERATION_VOLUME_LABEL_EXPORT_TABLE_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Operations/OperationVolumeLabelImport.cxx000066400000000000000000000425011300200146000307000ustar00rootroot00000000000000/*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 "OperationVolumeLabelImport.h" #include "OperationException.h" #include "CaretLogger.h" #include "FileInformation.h" #include "GiftiLabel.h" #include "VolumeFile.h" #include #include #include #include #include #include #include using namespace caret; using namespace std; AString OperationVolumeLabelImport::getCommandSwitch() { return "-volume-label-import"; } AString OperationVolumeLabelImport::getShortDescription() { return "IMPORT A LABEL VOLUME TO CARET FORMAT"; } OperationParameters* OperationVolumeLabelImport::getParameters() { OperationParameters* ret = new OperationParameters(); ret->addVolumeParameter(1, "input", "the label volume to import"); ret->addStringParameter(2, "label-list-file", "text file containing the values and names for labels"); ret->addVolumeOutputParameter(3, "output", "the output workbench label volume"); ret->createOptionalParameter(4, "-discard-others", "set any voxels with values not mentioned in the label list to the ??? label"); OptionalParameter* unlabeledOption = ret->createOptionalParameter(5, "-unlabeled-value", "set the value that will be interpreted as unlabeled"); unlabeledOption->addIntegerParameter(1, "value", "the numeric value for unlabeled (default 0)"); OptionalParameter* subvolumeSelect = ret->createOptionalParameter(6, "-subvolume", "select a single subvolume to import"); subvolumeSelect->addStringParameter(1, "subvol", "the subvolume number or name"); ret->createOptionalParameter(7, "-drop-unused-labels", "remove any unused label values from the label table"); ret->setHelpText( AString("Creates a new volume with label information in the header in the caret nifti extension format. ") + "You may specify the empty string ('' will work on linux/mac) for , which will be treated as if it is an empty file. " + "The label list file must have lines of the following format:\n\n" + "\n \n\n" + "Do not specify the \"unlabeled\" key in the file, it is assumed that 0 means not labeled unless -unlabeled-value is specified. " + "Label names must be on a separate line, but may contain spaces or other unusual characters (but not newline). " + "Whitespace is trimmed from both ends of the label name, but is kept if it is in the middle of a label. " + "The values of red, green, blue and alpha must be integers from 0 to 255, and will specify the color the label is drawn as " + "(alpha of 255 means opaque, which is probably what you want). " + "By default, it will set new label names with names of LABEL_# for any values encountered that are not mentioned in the " + "list file, specify -discard-others to instead set these voxels to the \"unlabeled\" key." ); return ret; } void OperationVolumeLabelImport::useParameters(OperationParameters* myParams, ProgressObject* myProgObj) { AString temp; LevelProgress myProgress(myProgObj); VolumeFile* myVol = myParams->getVolume(1); AString listfileName = myParams->getString(2); VolumeFile* outVol = myParams->getOutputVolume(3); bool discardOthers = false; OptionalParameter* discardOption = myParams->getOptionalParameter(4); if (discardOption->m_present) { discardOthers = true; } int32_t unlabeledValue = 0; OptionalParameter* unlabeledOption = myParams->getOptionalParameter(5); if (unlabeledOption->m_present) { unlabeledValue = (int32_t)unlabeledOption->getInteger(1); } OptionalParameter* subvolumeSelect = myParams->getOptionalParameter(6); int subvol = -1; if (subvolumeSelect->m_present) {//set up to use the single column subvol = (int)myVol->getMapIndexFromNameOrNumber(subvolumeSelect->getString(1)); if (subvol < 0 || subvol >= myVol->getNumberOfMaps()) { throw OperationException("invalid column specified"); } } bool dropUnused = myParams->getOptionalParameter(7)->m_present; GiftiLabelTable myTable; map translate; if (listfileName != "") { FileInformation textFileInfo(listfileName); if (!textFileInfo.exists()) { throw OperationException("label list file doesn't exist"); } fstream labelListFile(listfileName.toLocal8Bit().constData(), fstream::in); if (!labelListFile.good()) { throw OperationException("error reading label list file"); } string labelName; int32_t value, red, green, blue, alpha; int labelCount = 0; translate[unlabeledValue] = 0;//placeholder, we don't know the correct translated value yet while (labelListFile.good()) { ++labelCount;//just for error messages, so start at 1 getline(labelListFile, labelName); labelListFile >> value; if (labelListFile.eof() && labelName == "") break;//if end of file trying to read an int, and label name is empty, its really just end of file labelListFile >> red; labelListFile >> green; labelListFile >> blue; if (!(labelListFile >> alpha))//yes, that is seriously the correct way to check if input was successfully extracted...so much fail { throw OperationException("label list file is malformed for entry #" + AString::number(labelCount) + ": " + AString(labelName.c_str())); } if (red < 0 || red > 255) { throw OperationException("bad value for red for entry #" + AString::number(labelCount) + ", " + AString(labelName.c_str()) + ": " + AString::number(red)); } if (green < 0 || green > 255) { throw OperationException("bad value for green for entry #" + AString::number(labelCount) + ", " + AString(labelName.c_str()) + ": " + AString::number(green)); } if (blue < 0 || blue > 255) { throw OperationException("bad value for blue for entry #" + AString::number(labelCount) + ", " + AString(labelName.c_str()) + ": " + AString::number(blue)); } if (alpha < 0 || alpha > 255) { throw OperationException("bad value for alpha for entry #" + AString::number(labelCount) + ", " + AString(labelName.c_str()) + ": " + AString::number(alpha)); } if (value == GiftiLabel::getInvalidLabelKey()) { throw OperationException("entry #" + AString::number(labelCount) + ", " + AString(labelName.c_str()) + " specifies unusable key value: " + value); } while (isspace(labelListFile.peek())) { labelListFile.ignore();//drop the newline, possible carriage return or other whitespace so that getline doesn't get nothing, and cause int extraction to fail } temp = AString(labelName.c_str()).trimmed();//drop errant CR or other whitespace from beginning and end of lines if (translate.find(value) != translate.end()) { if (value == unlabeledValue) { throw OperationException("the unlabeled value must not be specified in label list file"); } else { throw OperationException(AString("label key ") + AString::number(value) + " specified more than once"); } } GiftiLabel myLabel(value, temp, red, green, blue, alpha); if (myTable.getLabelKeyFromName(temp) != GiftiLabel::getInvalidLabelKey()) { AString nameBase = temp, newName;//resolve collision by generating a name with an additional number on it bool success = false; for (int extra = 1; extra < 100; ++extra)//but stop at 100, because really... { newName = nameBase + "_" + AString::number(extra); if (myTable.getLabelKeyFromName(newName) == GiftiLabel::getInvalidLabelKey()) { success = true; break; } } if (success) { CaretLogWarning("name collision in input name '" + nameBase + "', changing one to '" + newName + "'"); } else { throw OperationException("giving up on resolving name collision for input name '" + nameBase + "'"); } myLabel.setName(newName); } int32_t newValue; if (value == 0)//because label 0 exists in the default constructed table { myTable.insertLabel(&myLabel);//but we do want to be able to overwrite the default 0 label newValue = 0;//if value 0 is specified twice, or once without specifying a different unlabeled value, the check versus the translate map will catch it } else { newValue = myTable.addLabel(&myLabel);//we don't want to overwrite relocated labels } translate[value] = newValue; } } vector myDims; myVol->getDimensions(myDims); const int64_t FRAMESIZE = myDims[0] * myDims[1] * myDims[2]; CaretArray frameOut(FRAMESIZE); int32_t unusedLabel = myTable.getUnassignedLabelKey(); translate[unlabeledValue] = unusedLabel; if (subvol == -1) { outVol->reinitialize(myVol->getOriginalDimensions(), myVol->getSform(), myDims[4], SubvolumeAttributes::LABEL); for (int s = 0; s < myDims[3]; ++s) { set usedValues;//track used values if we have dropUnused for (int c = 0; c < myDims[4]; ++c)//hopefully noone wants a multi-component label volume, that would be silly, but do it anyway { const float* frameIn = myVol->getFrame(s, c);//TODO: rework this when support is added for VolumeFile to handle non-float data for (int i = 0; i < FRAMESIZE; ++i) { int32_t labelval = (int32_t)floor(frameIn[i] + 0.5f);//just in case it somehow got poorly encoded, round to nearest if (dropUnused) { usedValues.insert(labelval); } map::iterator myiter = translate.find(labelval); if (myiter == translate.end()) { if (discardOthers) { frameOut[i] = unusedLabel; } else {//use a random color, but fully opaque for the label GiftiLabel myLabel(labelval, AString("LABEL_") + AString::number(labelval), rand() & 255, rand() & 255, rand() & 255, 255); if (myTable.getLabelKeyFromName(myLabel.getName()) != GiftiLabel::getInvalidLabelKey()) { AString nameBase = myLabel.getName(), newName;//resolve collision by generating a name with an additional number on it bool success = false; for (int extra = 1; extra < 100; ++extra)//but stop at 100, because really... { newName = nameBase + "_" + AString::number(extra); if (myTable.getLabelKeyFromName(newName) == GiftiLabel::getInvalidLabelKey()) { success = true; break; } } if (success) { CaretLogWarning("name collision in auto-generated name '" + nameBase + "', changed to '" + newName + "'"); } else { throw OperationException("giving up on resolving name collision for auto-generated name '" + nameBase + "'"); } myLabel.setName(newName); } int32_t newValue = myTable.addLabel(&myLabel);//don't overwrite any values in the table translate[labelval] = newValue; frameOut[i] = newValue; } } else { frameOut[i] = myiter->second; } } outVol->setFrame(frameOut, s, c); } if (dropUnused) { GiftiLabelTable frameTable = myTable; frameTable.deleteUnusedLabels(usedValues); *(outVol->getMapLabelTable(s)) = frameTable; } else { *(outVol->getMapLabelTable(s)) = myTable;//set the label table AFTER doing the frame, because we may make new labels while scanning } } } else { vector newDims = myDims; newDims.resize(3);//spatial only outVol->reinitialize(newDims, myVol->getSform(), myDims[4], SubvolumeAttributes::LABEL); set usedValues;//track used values if we have dropUnused for (int c = 0; c < myDims[4]; ++c)//hopefully noone wants a multi-component label volume, that would be silly, but do it anyway { const float* frameIn = myVol->getFrame(subvol, c);//TODO: rework this when support is added for VolumeFile to handle non-float data for (int i = 0; i < FRAMESIZE; ++i) { int32_t labelval = (int32_t)floor(frameIn[i] + 0.5f);//just in case it somehow got poorly encoded, round to nearest if (dropUnused) { usedValues.insert(labelval); } map::iterator myiter = translate.find(labelval); if (myiter == translate.end()) { if (discardOthers) { frameOut[i] = unusedLabel; } else {//use a random color, but fully opaque for the label GiftiLabel myLabel(labelval, AString("LABEL_") + AString::number(labelval), rand() & 255, rand() & 255, rand() & 255, 255); if (myTable.getLabelKeyFromName(myLabel.getName()) != GiftiLabel::getInvalidLabelKey()) { AString nameBase = myLabel.getName(), newName;//resolve collision by generating a name with an additional number on it bool success = false; for (int extra = 1; extra < 100; ++extra)//but stop at 100, because really... { newName = nameBase + "_" + AString::number(extra); if (myTable.getLabelKeyFromName(newName) == GiftiLabel::getInvalidLabelKey()) { success = true; break; } } if (success) { CaretLogWarning("name collision in auto-generated name '" + nameBase + "', changed to '" + newName + "'"); } else { throw OperationException("giving up on resolving name collision for auto-generated name '" + nameBase + "'"); } myLabel.setName(newName); } int32_t newValue = myTable.addLabel(&myLabel);//don't overwrite any values in the table translate[labelval] = newValue; frameOut[i] = newValue; } } else { frameOut[i] = myiter->second; } } outVol->setFrame(frameOut, 0, c); } if (dropUnused) { GiftiLabelTable frameTable = myTable; frameTable.deleteUnusedLabels(usedValues); *(outVol->getMapLabelTable(0)) = frameTable; } else { *(outVol->getMapLabelTable(0)) = myTable;//set the label table AFTER doing the frame, because we may make new labels while scanning } } } connectome-workbench-1.2.3+git41-gc4c6c90/src/Operations/OperationVolumeLabelImport.h000066400000000000000000000026521300200146000303300ustar00rootroot00000000000000#ifndef __OPERATION_VOLUME_LABEL_IMPORT_H__ #define __OPERATION_VOLUME_LABEL_IMPORT_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 "AbstractOperation.h" namespace caret { class OperationVolumeLabelImport : public AbstractOperation { public: static OperationParameters* getParameters(); static void useParameters(OperationParameters* myParams, ProgressObject* myProgObj); static AString getCommandSwitch(); static AString getShortDescription(); }; typedef TemplateAutoOperation AutoOperationVolumeLabelImport; } #endif //__OPERATION_VOLUME_LABEL_IMPORT_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Operations/OperationVolumeMath.cxx000066400000000000000000000236711300200146000273660ustar00rootroot00000000000000/*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 "OperationVolumeMath.h" #include "OperationException.h" #include "CaretAssert.h" #include "CaretLogger.h" #include "CaretMathExpression.h" #include "VolumeFile.h" #include using namespace caret; using namespace std; AString OperationVolumeMath::getCommandSwitch() { return "-volume-math"; } AString OperationVolumeMath::getShortDescription() { return "EVALUATE EXPRESSION ON VOLUME FILES"; } OperationParameters* OperationVolumeMath::getParameters() { OperationParameters* ret = new OperationParameters(); ret->addStringParameter(1, "expression", "the expression to evaluate, in quotes"); ret->addVolumeOutputParameter(2, "volume-out", "the output volume"); ParameterComponent* varOpt = ret->createRepeatableParameter(3, "-var", "a volume file to use as a variable"); varOpt->addStringParameter(1, "name", "the name of the variable, as used in the expression"); varOpt->addVolumeParameter(2, "volume", "the volume file to use as this variable"); OptionalParameter* subvolSelect = varOpt->createOptionalParameter(3, "-subvolume", "select a single subvolume"); subvolSelect->addStringParameter(1, "subvol", "the subvolume number or name"); varOpt->createOptionalParameter(4, "-repeat", "reuse a single subvolume for each subvolume of calculation"); OptionalParameter* fixNanOpt = ret->createOptionalParameter(4, "-fixnan", "replace NaN results with a value"); fixNanOpt->addDoubleParameter(1, "replace", "value to replace NaN with"); AString myText = AString("This command evaluates at each voxel independently. ") + "There must be at least one -var option (to get the volume space from), even if the specified in it isn't used in . " + "All volumes must have the same volume space. " + "Filenames are not valid in , use a variable name and a -var option with matching to specify an input file. " + "If the -subvolume option is given to any -var option, only one subvolume is used from that file. " + "If -repeat is specified, the file must either have only one subvolume, or have the -subvolume option specified. " + "All files that don't use -repeat must have the same number of subvolumes requested to be used. " + "The format of is as follows:\n\n"; myText += CaretMathExpression::getExpressionHelpInfo(); ret->setHelpText(myText); return ret; } void OperationVolumeMath::useParameters(OperationParameters* myParams, ProgressObject* myProgObj) { LevelProgress myProgress(myProgObj); AString expression = myParams->getString(1); CaretMathExpression myExpr(expression); cout << "parsed '" + expression + "' as '" + myExpr.toString() + "'" << endl; vector myVarNames = myExpr.getVarNames(); VolumeFile* myVolOut = myParams->getOutputVolume(2); const vector& myVarOpts = *(myParams->getRepeatableParameterInstances(3)); OptionalParameter* fixNanOpt = myParams->getOptionalParameter(4); bool nanfix = false; float nanfixval = 0; if (fixNanOpt->m_present) { nanfix = true; nanfixval = (float)fixNanOpt->getDouble(1); } int numInputs = myVarOpts.size(); int numVars = myVarNames.size(); vector varVolumes(numVars, (VolumeFile*)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; VolumeSpace mySpace; vector outDims; int numSubvols = -1; for (int i = 0; i < numInputs; ++i) { if (i == 0) { first = myVarOpts[0]->getVolume(2); mySpace = first->getVolumeSpace(); } AString varName = myVarOpts[i]->getString(1); double constVal; if (CaretMathExpression::getNamedConstant(varName, constVal)) { throw OperationException("'" + varName + "' is a named constant equal to " + AString::number(constVal, 'g', 15) + ", please use a different variable name"); } VolumeFile* thisVolume = myVarOpts[i]->getVolume(2); if (thisVolume->getNumberOfComponents() != 1) { throw OperationException("volume file for variable '" + varName + "' has multiple components, this is not currently supported in -volume-math"); } int thisSubvols = thisVolume->getNumberOfMaps(); OptionalParameter* subvolSelect = myVarOpts[i]->getOptionalParameter(3); int useSubvolume = -1; if (subvolSelect->m_present) { thisSubvols = 1; useSubvolume = thisVolume->getMapIndexFromNameOrNumber(subvolSelect->getString(1)); if (useSubvolume == -1) throw OperationException("could not find map '" + subvolSelect->getString(1) + "' in volume file for '" + varName + "'"); } bool repeat = myVarOpts[i]->getOptionalParameter(4)->m_present; if (!thisVolume->matchesVolumeSpace(mySpace)) { throw OperationException("volume file for variable '" + varName + "' has different volume space than the first volume file"); } if (repeat) { if (thisSubvols != 1) { throw OperationException("-repeat specified without -subvolume for variable '" + varName + "', but volume file has " + AString::number(thisSubvols) + " subvolumes"); } if (useSubvolume == -1) useSubvolume = 0;//-1 means use same input subvolume as current output subvolume, so we need to fix the special case of -repeat on single subvolume file without -subvolume } else { if (numSubvols == -1)//then this is the first one that doesn't use -repeat { numSubvols = thisSubvols; outDims = thisVolume->getOriginalDimensions(); if (useSubvolume != -1) { outDims.resize(3);//change to output only one subvolume in the simplest way } } else { if (numSubvols != thisSubvols) { if (useSubvolume == -1) { throw OperationException("volume file for variable '" + varName + "' has " + AString::number(thisSubvols) + " subvolume(s), but previous volume files have " + AString::number(numSubvols) + " subvolume(s) requested to be used"); } else { throw OperationException("-subvolume specified without -repeat for variable '" + varName + "', but previous volume files have have " + AString::number(numSubvols) + " subvolumes requested to be used"); } } } } bool found = false; for (int j = 0; j < numVars; ++j) { if (varName == myVarNames[j]) { if (varVolumes[j] != NULL) throw OperationException("variable '" + varName + "' specified more than once"); varVolumes[j] = thisVolume; varSubvolumes[j] = useSubvolume; found = true; break; } } if (!found && (numVars != 0 || numInputs != 1))//supress warning when a single -var is used with a constant expression, as required per help { CaretLogWarning("variable '" + varName + "' not used in expression"); } } for (int i = 0; i < numVars; ++i) { if (varVolumes[i] == NULL) throw OperationException("no -var option specified for variable '" + myVarNames[i] + "'"); } if (numSubvols == -1) { throw OperationException("all -var options used -repeat, there is no file to get number of desired output subvolumes from"); } int64_t frameSize = outDims[0] * outDims[1] * outDims[2]; vector values(numVars), outFrame(frameSize); vector inputFrames(numVars); 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) { for (int v = 0; v < numVars; ++v) { if (varSubvolumes[v] == -1) { inputFrames[v] = varVolumes[v]->getFrame(s); } else { inputFrames[v] = varVolumes[v]->getFrame(varSubvolumes[v]); } } for (int64_t i = 0; i < frameSize; ++i) { for (int v = 0; v < numVars; ++v) { values[v] = inputFrames[v][i]; } float tempf = (float)myExpr.evaluate(values); if (nanfix && tempf != tempf) { tempf = nanfixval; } outFrame[i] = tempf; } myVolOut->setFrame(outFrame.data(), s); } } connectome-workbench-1.2.3+git41-gc4c6c90/src/Operations/OperationVolumeMath.h000066400000000000000000000025751300200146000270130ustar00rootroot00000000000000#ifndef __OPERATION_VOLUME_MATH_H__ #define __OPERATION_VOLUME_MATH_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 "AbstractOperation.h" namespace caret { class OperationVolumeMath : public AbstractOperation { public: static OperationParameters* getParameters(); static void useParameters(OperationParameters* myParams, ProgressObject* myProgObj); static AString getCommandSwitch(); static AString getShortDescription(); }; typedef TemplateAutoOperation AutoOperationVolumeMath; } #endif //__OPERATION_VOLUME_MATH_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Operations/OperationVolumeMerge.cxx000066400000000000000000000233511300200146000275270ustar00rootroot00000000000000/*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 "OperationVolumeMerge.h" #include "OperationException.h" #include "CaretAssert.h" #include "GiftiLabelTable.h" #include "PaletteColorMapping.h" #include "VolumeFile.h" using namespace caret; using namespace std; AString OperationVolumeMerge::getCommandSwitch() { return "-volume-merge"; } AString OperationVolumeMerge::getShortDescription() { return "MERGE VOLUME FILES INTO A NEW FILE"; } OperationParameters* OperationVolumeMerge::getParameters() { OperationParameters* ret = new OperationParameters(); ret->addVolumeOutputParameter(1, "volume-out", "the output volume file"); ParameterComponent* volumeOpt = ret->createRepeatableParameter(2, "-volume", "specify an input volume file"); volumeOpt->addVolumeParameter(1, "volume-in", "a volume file to use subvolumes from"); ParameterComponent* subvolOpt = volumeOpt->createRepeatableParameter(2, "-subvolume", "select a single subvolume to use"); subvolOpt->addStringParameter(1, "subvol", "the subvolume number or name"); OptionalParameter* upToOpt = subvolOpt->createOptionalParameter(2, "-up-to", "use an inclusive range of subvolumes"); upToOpt->addStringParameter(1, "last-subvol", "the number or name of the last subvolume to include"); upToOpt->createOptionalParameter(2, "-reverse", "use the range in reverse order"); ret->setHelpText( AString("Takes one or more volume files and constructs a new volume file by concatenating subvolumes from them. ") + "The input volume files must have the same volume space.\n\n" + "Example: wb_command -volume-merge out.nii -volume first.nii -subvolume 1 -volume second.nii\n\n" + "This example would take the first subvolume from first.nii, followed by all subvolumes from second.nii, " + "and write these to out.nii." ); return ret; } void OperationVolumeMerge::useParameters(OperationParameters* myParams, ProgressObject* myProgObj) { LevelProgress myProgress(myProgObj); VolumeFile* volumeOut = myParams->getOutputVolume(1); const vector& myInputs = *(myParams->getRepeatableParameterInstances(2)); int numInputs = (int)myInputs.size(); if (numInputs < 1) throw OperationException("no inputs specified"); int64_t subvolCount = 0; const VolumeFile* firstVol = myInputs[0]->getVolume(1); vector firstDims = firstVol->getDimensions(); bool isLabel = (firstVol->getType() == SubvolumeAttributes::LABEL); for (int i = 0; i < numInputs; ++i) { const VolumeFile* myVol = myInputs[i]->getVolume(1); if (!myVol->matchesVolumeSpace(firstVol)) { throw OperationException("volume file '" + myVol->getFileName() + "' has a different volume space"); } if (isLabel != (myVol->getType() == SubvolumeAttributes::LABEL)) { throw OperationException("only some volumes are label volumes, first mismatch is '" + myVol->getFileName() + "'"); } vector thisDims = myVol->getDimensions(); if (thisDims[4] != firstDims[4]) throw ("volume file '" + myVol->getFileName() + "' has a different number of components"); const vector& subvolOpts = *(myInputs[i]->getRepeatableParameterInstances(2)); int numSubvolOpts = (int)subvolOpts.size(); if (numSubvolOpts > 0) { for (int j = 0; j < numSubvolOpts; ++j) { int64_t initialFrame = myVol->getMapIndexFromNameOrNumber(subvolOpts[j]->getString(1)); if (initialFrame < 0) throw OperationException("subvolume '" + subvolOpts[j]->getString(1) + "' not found in file '" + myVol->getFileName() + "'"); OptionalParameter* upToOpt = subvolOpts[j]->getOptionalParameter(2); if (upToOpt->m_present) { int64_t finalFrame = myVol->getMapIndexFromNameOrNumber(upToOpt->getString(1)); if (finalFrame < 0) throw OperationException("ending subvolume '" + upToOpt->getString(1) + "' not found in file '" + myVol->getFileName() + "'"); if (finalFrame < initialFrame) throw OperationException("ending subvolume '" + upToOpt->getString(1) + "' occurs before starting subvolume '" + subvolOpts[j]->getString(1) + "' in file '" + myVol->getFileName() + "'"); subvolCount += finalFrame - initialFrame + 1;//inclusive - we don't need to worry about reversing for counting, though } else { subvolCount += 1; } } } else { subvolCount += myVol->getNumberOfMaps(); } } vector outDims = firstVol->getOriginalDimensions(); outDims.resize(4); outDims[3] = subvolCount; volumeOut->reinitialize(outDims, firstVol->getSform(), firstDims[4], firstVol->getType()); int64_t curOutVol = 0; for (int i = 0; i < numInputs; ++i) { const VolumeFile* myVol = myInputs[i]->getVolume(1); const vector& subvolOpts = *(myInputs[i]->getRepeatableParameterInstances(2)); int numSubvolOpts = (int)subvolOpts.size(); if (numSubvolOpts > 0) { for (int j = 0; j < numSubvolOpts; ++j) { int64_t initialFrame = myVol->getMapIndexFromNameOrNumber(subvolOpts[j]->getString(1)); OptionalParameter* upToOpt = subvolOpts[j]->getOptionalParameter(2); if (upToOpt->m_present) { int64_t finalFrame = myVol->getMapIndexFromNameOrNumber(upToOpt->getString(1)); bool reverse = upToOpt->getOptionalParameter(2)->m_present; if (reverse) { for (int64_t b = finalFrame; b >= initialFrame; --b) { for (int64_t c = 0; c < firstDims[4]; ++c) { volumeOut->setFrame(myVol->getFrame(b, c), curOutVol, c); } volumeOut->setMapName(curOutVol, myVol->getMapName(b)); if (isLabel) { *(volumeOut->getMapLabelTable(curOutVol)) = *(myVol->getMapLabelTable(b)); } else { *(volumeOut->getMapPaletteColorMapping(curOutVol)) = *(myVol->getMapPaletteColorMapping(b)); } ++curOutVol; } } else { for (int64_t b = initialFrame; b <= finalFrame; ++b) { for (int64_t c = 0; c < firstDims[4]; ++c) { volumeOut->setFrame(myVol->getFrame(b, c), curOutVol, c); } volumeOut->setMapName(curOutVol, myVol->getMapName(b)); if (isLabel) { *(volumeOut->getMapLabelTable(curOutVol)) = *(myVol->getMapLabelTable(b)); } else { *(volumeOut->getMapPaletteColorMapping(curOutVol)) = *(myVol->getMapPaletteColorMapping(b)); } ++curOutVol; } } } else { for (int64_t c = 0; c < firstDims[4]; ++c) { volumeOut->setFrame(myVol->getFrame(initialFrame, c), curOutVol, c); } volumeOut->setMapName(curOutVol, myVol->getMapName(initialFrame)); if (isLabel) { *(volumeOut->getMapLabelTable(curOutVol)) = *(myVol->getMapLabelTable(initialFrame)); } else { *(volumeOut->getMapPaletteColorMapping(curOutVol)) = *(myVol->getMapPaletteColorMapping(initialFrame)); } ++curOutVol; } } } else { vector myDims = myVol->getDimensions(); for (int64_t b = 0; b < myDims[3]; ++b) { for (int64_t c = 0; c < firstDims[4]; ++c) { volumeOut->setFrame(myVol->getFrame(b, c), curOutVol, c); } volumeOut->setMapName(curOutVol, myVol->getMapName(b)); if (isLabel) { *(volumeOut->getMapLabelTable(curOutVol)) = *(myVol->getMapLabelTable(b)); } else { *(volumeOut->getMapPaletteColorMapping(curOutVol)) = *(myVol->getMapPaletteColorMapping(b)); } ++curOutVol; } } } CaretAssert(curOutVol == subvolCount); } connectome-workbench-1.2.3+git41-gc4c6c90/src/Operations/OperationVolumeMerge.h000066400000000000000000000026031300200146000271510ustar00rootroot00000000000000#ifndef __OPERATION_VOLUME_MERGE_H__ #define __OPERATION_VOLUME_MERGE_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 "AbstractOperation.h" namespace caret { class OperationVolumeMerge : public AbstractOperation { public: static OperationParameters* getParameters(); static void useParameters(OperationParameters* myParams, ProgressObject* myProgObj); static AString getCommandSwitch(); static AString getShortDescription(); }; typedef TemplateAutoOperation AutoOperationVolumeMerge; } #endif //__OPERATION_VOLUME_MERGE_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Operations/OperationVolumePalette.cxx000066400000000000000000000244771300200146000301000ustar00rootroot00000000000000/*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 "OperationVolumePalette.h" #include "OperationException.h" #include "Palette.h" #include "PaletteColorMapping.h" #include "PaletteFile.h" #include "VolumeFile.h" using namespace caret; using namespace std; AString OperationVolumePalette::getCommandSwitch() { return "-volume-palette"; } AString OperationVolumePalette::getShortDescription() { return "SET THE PALETTE OF A VOLUME FILE"; } OperationParameters* OperationVolumePalette::getParameters() { OperationParameters* ret = new OperationParameters(); ret->addStringParameter(1, "volume", "the volume file to modify"); ret->addStringParameter(2, "mode", "the mapping mode"); OptionalParameter* subvolumeSelect = ret->createOptionalParameter(3, "-subvolume", "select a single subvolume"); subvolumeSelect->addStringParameter(1, "subvolume", "the subvolume number or name"); OptionalParameter* posMinMaxPercent = ret->createOptionalParameter(4, "-pos-percent", "percentage min/max for positive data coloring"); posMinMaxPercent->addDoubleParameter(1, "pos-min-%", "the percentile for the least positive data"); posMinMaxPercent->addDoubleParameter(2, "pos-max-%", "the percentile for the most positive data"); OptionalParameter* negMinMaxPercent = ret->createOptionalParameter(5, "-neg-percent", "percentage min/max for negative data coloring"); negMinMaxPercent->addDoubleParameter(1, "neg-min-%", "the percentile for the least negative data"); negMinMaxPercent->addDoubleParameter(2, "neg-max-%", "the percentile for the most negative data"); OptionalParameter* posMinMaxValue = ret->createOptionalParameter(11, "-pos-user", "user min/max values for positive data coloring"); posMinMaxValue->addDoubleParameter(1, "pos-min-user", "the value for the least positive data"); posMinMaxValue->addDoubleParameter(2, "pos-max-user", "the value for the most positive data"); OptionalParameter* negMinMaxValue = ret->createOptionalParameter(12, "-neg-user", "user min/max values for negative data coloring"); negMinMaxValue->addDoubleParameter(1, "neg-min-user", "the value for the least negative data"); negMinMaxValue->addDoubleParameter(2, "neg-max-user", "the value for the most negative data"); OptionalParameter* interpolate = ret->createOptionalParameter(9, "-interpolate", "interpolate colors"); interpolate->addBooleanParameter(1, "interpolate", "boolean, whether to interpolate"); OptionalParameter* displayPositive = ret->createOptionalParameter(6, "-disp-pos", "display positive data"); displayPositive->addBooleanParameter(1, "display", "boolean, whether to display"); OptionalParameter* displayNegative = ret->createOptionalParameter(7, "-disp-neg", "display positive data"); displayNegative->addBooleanParameter(1, "display", "boolean, whether to display"); OptionalParameter* displayZero = ret->createOptionalParameter(8, "-disp-zero", "display data closer to zero than the min cutoff"); displayZero->addBooleanParameter(1, "display", "boolean, whether to display"); OptionalParameter* paletteName = ret->createOptionalParameter(10, "-palette-name", "set the palette used"); paletteName->addStringParameter(1, "name", "the name of the palette"); OptionalParameter* thresholdOpt = ret->createOptionalParameter(13, "-thresholding", "set the thresholding"); thresholdOpt->addStringParameter(1, "type", "thresholding setting"); thresholdOpt->addStringParameter(2, "test", "show values inside or outside thresholds"); thresholdOpt->addDoubleParameter(3, "min", "lower threshold"); thresholdOpt->addDoubleParameter(4, "max", "upper threshold"); 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"; vector myEnums; PaletteScaleModeEnum::getAllEnums(myEnums); for (int i = 0; i < (int)myEnums.size(); ++i) { myText += PaletteScaleModeEnum::toName(myEnums[i]) + "\n"; } myText += "\nThe argument to -palette-name must be one of the following:\n\n"; PaletteFile myPF; int32_t numPalettes = myPF.getNumberOfPalettes(); for (int i = 0; i < numPalettes; ++i) { myText += myPF.getPalette(i)->getName() + "\n"; } myText += "\nThe argument to -thresholding must be one of the following:\n\n"; vector myEnums2; PaletteThresholdTypeEnum::getAllEnums(myEnums2); for (int i = 0; i < (int)myEnums2.size(); ++i) { myText += PaletteThresholdTypeEnum::toName(myEnums2[i]) + "\n"; } myText += "\nThe argument to -thresholding must be one of the following:\n\n"; vector myEnums3; PaletteThresholdTestEnum::getAllEnums(myEnums3); for (int i = 0; i < (int)myEnums3.size(); ++i) { myText += PaletteThresholdTestEnum::toName(myEnums3[i]) + "\n"; } ret->setHelpText(myText); return ret; } void OperationVolumePalette::useParameters(OperationParameters* myParams, ProgressObject* myProgObj) { LevelProgress myProgress(myProgObj); AString myVolumeName = myParams->getString(1); AString myModeName = myParams->getString(2); bool ok = false; PaletteScaleModeEnum::Enum myMode = PaletteScaleModeEnum::fromName(myModeName, &ok); if (!ok) { throw OperationException("unknown mapping mode"); } VolumeFile myVolume; myVolume.readFile(myVolumeName); int mySubvolume = -1; OptionalParameter* subvolumeSelect = myParams->getOptionalParameter(3); if (subvolumeSelect->m_present) { mySubvolume = (int)myVolume.getMapIndexFromNameOrNumber(subvolumeSelect->getString(1)); if (mySubvolume < 0 || mySubvolume >= myVolume.getNumberOfMaps()) { throw OperationException("invalid column specified"); } } PaletteColorMapping myMapping = *(myVolume.getMapPaletteColorMapping(0));//create the mapping, then use operator= to set for all requested columns, take defaults from first map myMapping.setScaleMode(myMode); OptionalParameter* posMinMaxPercent = myParams->getOptionalParameter(4); if (posMinMaxPercent->m_present) { myMapping.setAutoScalePercentagePositiveMinimum(posMinMaxPercent->getDouble(1)); myMapping.setAutoScalePercentagePositiveMaximum(posMinMaxPercent->getDouble(2)); } OptionalParameter* negMinMaxPercent = myParams->getOptionalParameter(5); if (negMinMaxPercent->m_present) { myMapping.setAutoScalePercentageNegativeMinimum(negMinMaxPercent->getDouble(1)); myMapping.setAutoScalePercentageNegativeMaximum(negMinMaxPercent->getDouble(2)); } OptionalParameter* posMinMaxValue = myParams->getOptionalParameter(11); if (posMinMaxValue->m_present) { myMapping.setUserScalePositiveMinimum(posMinMaxValue->getDouble(1)); myMapping.setUserScalePositiveMaximum(posMinMaxValue->getDouble(2)); } OptionalParameter* negMinMaxValue = myParams->getOptionalParameter(12); if (negMinMaxValue->m_present) { myMapping.setUserScaleNegativeMinimum(negMinMaxValue->getDouble(1)); myMapping.setUserScaleNegativeMaximum(negMinMaxValue->getDouble(2)); } OptionalParameter* displayPositive = myParams->getOptionalParameter(6); if (displayPositive->m_present) { myMapping.setDisplayPositiveDataFlag(displayPositive->getBoolean(1)); } OptionalParameter* displayNegative = myParams->getOptionalParameter(7); if (displayNegative->m_present) { myMapping.setDisplayNegativeDataFlag(displayNegative->getBoolean(1)); } OptionalParameter* displayZero = myParams->getOptionalParameter(8); if (displayZero->m_present) { myMapping.setDisplayZeroDataFlag(displayZero->getBoolean(1)); } OptionalParameter* interpolate = myParams->getOptionalParameter(9); if (interpolate->m_present) { myMapping.setInterpolatePaletteFlag(interpolate->getBoolean(1)); } OptionalParameter* paletteName = myParams->getOptionalParameter(10); if (paletteName->m_present) { myMapping.setSelectedPaletteName(paletteName->getString(1)); } OptionalParameter* thresholdOpt = myParams->getOptionalParameter(13); if (thresholdOpt->m_present) { bool ok = false; PaletteThresholdTypeEnum::Enum mytype = PaletteThresholdTypeEnum::fromName(thresholdOpt->getString(1), &ok); if (!ok) throw OperationException("unrecognized threshold type string: " + thresholdOpt->getString(1)); PaletteThresholdTestEnum::Enum mytest = PaletteThresholdTestEnum::fromName(thresholdOpt->getString(2), &ok); if (!ok) throw OperationException("unrecognized threshold test string: " + thresholdOpt->getString(2)); myMapping.setThresholdType(mytype); myMapping.setThresholdTest(mytest); myMapping.setThresholdMinimum(mytype, thresholdOpt->getDouble(3)); myMapping.setThresholdMaximum(mytype, thresholdOpt->getDouble(4)); } if (mySubvolume == -1) { for (int i = 0; i < myVolume.getNumberOfMaps(); ++i) { *(myVolume.getMapPaletteColorMapping(i)) = myMapping; } } else { *(myVolume.getMapPaletteColorMapping(mySubvolume)) = myMapping; } myVolume.writeFile(myVolumeName); } connectome-workbench-1.2.3+git41-gc4c6c90/src/Operations/OperationVolumePalette.h000066400000000000000000000026171300200146000275150ustar00rootroot00000000000000#ifndef __OPERATION_VOLUME_PALETTE_H__ #define __OPERATION_VOLUME_PALETTE_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 "AbstractOperation.h" namespace caret { class OperationVolumePalette : public AbstractOperation { public: static OperationParameters* getParameters(); static void useParameters(OperationParameters* myParams, ProgressObject* myProgObj); static AString getCommandSwitch(); static AString getShortDescription(); }; typedef TemplateAutoOperation AutoOperationVolumePalette; } #endif //__OPERATION_VOLUME_PALETTE_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Operations/OperationVolumeReorient.cxx000066400000000000000000000106351300200146000302600ustar00rootroot00000000000000/*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 "OperationVolumeReorient.h" #include "OperationException.h" #include "VolumeFile.h" using namespace caret; using namespace std; AString OperationVolumeReorient::getCommandSwitch() { return "-volume-reorient"; } AString OperationVolumeReorient::getShortDescription() { return "CHANGE VOXEL ORDER OF A VOLUME FILE"; } OperationParameters* OperationVolumeReorient::getParameters() { OperationParameters* ret = new OperationParameters(); ret->addVolumeParameter(1, "volume", "the volume to reorient"); ret->addStringParameter(2, "orient-string", "the desired orientation"); ret->addStringParameter(3, "volume-out", "out - the reoriented volume");//fake the "out" parameter formatting, because copying a volume file in memory is currently a problem ret->setHelpText( AString("Changes the voxel order and the header spacing/origin information such that the value of any spatial point is unchanged. ") + "Orientation strings look like 'LPI', which means first index is left to right, second is posterior to anterior, and third is inferior to superior. " + "The valid characters are:\n\nL left to right\nR right to left\nP posterior to anterior\nA anterior to posterior\nI inferior to superior\nS superior to inferior" ); return ret; } void OperationVolumeReorient::useParameters(OperationParameters* myParams, ProgressObject* myProgObj) { LevelProgress myProgress(myProgObj); VolumeFile* myVol = myParams->getVolume(1); AString orientString = myParams->getString(2); AString outName = myParams->getString(3); if (orientString.length() < 3) { throw OperationException("orient-string must have 3 characters"); } bool used[3] = {false, false, false}; VolumeSpace::OrientTypes orient[3]; for (int i = 0; i < 3; ++i) { char id = orientString[i].toAscii(); switch (id) { case 'L': case 'l': if (used[0]) throw OperationException("X axis (L, R) specified more than once"); used[0] = true; orient[i] = VolumeSpace::LEFT_TO_RIGHT; break; case 'R': case 'r': if (used[0]) throw OperationException("X axis (L, R) specified more than once"); used[0] = true; orient[i] = VolumeSpace::RIGHT_TO_LEFT; break; case 'P': case 'p': if (used[1]) throw OperationException("Y axis (P, A) specified more than once"); used[1] = true; orient[i] = VolumeSpace::POSTERIOR_TO_ANTERIOR; break; case 'A': case 'a': if (used[1]) throw OperationException("Y axis (P, A) specified more than once"); used[1] = true; orient[i] = VolumeSpace::ANTERIOR_TO_POSTERIOR; break; case 'I': case 'i': if (used[2]) throw OperationException("Z axis (I, S) specified more than once"); used[2] = true; orient[i] = VolumeSpace::INFERIOR_TO_SUPERIOR; break; case 'S': case 's': if (used[2]) throw OperationException("Z axis (I, S) specified more than once"); used[2] = true; orient[i] = VolumeSpace::SUPERIOR_TO_INFERIOR; break; default: throw OperationException(AString("unrecognized character '") + id + "'"); } } myVol->reorient(orient); myVol->writeFile(outName); } connectome-workbench-1.2.3+git41-gc4c6c90/src/Operations/OperationVolumeReorient.h000066400000000000000000000026251300200146000277050ustar00rootroot00000000000000#ifndef __OPERATION_VOLUME_REORIENT_H__ #define __OPERATION_VOLUME_REORIENT_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 "AbstractOperation.h" namespace caret { class OperationVolumeReorient : public AbstractOperation { public: static OperationParameters* getParameters(); static void useParameters(OperationParameters* myParams, ProgressObject* myProgObj); static AString getCommandSwitch(); static AString getShortDescription(); }; typedef TemplateAutoOperation AutoOperationVolumeReorient; } #endif //__OPERATION_VOLUME_REORIENT_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Operations/OperationVolumeSetSpace.cxx000066400000000000000000000135541300200146000302030ustar00rootroot00000000000000/*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 "OperationVolumeSetSpace.h" #include "OperationException.h" #include "FloatMatrix.h" #include "VolumeFile.h" using namespace caret; using namespace std; AString OperationVolumeSetSpace::getCommandSwitch() { return "-volume-set-space"; } AString OperationVolumeSetSpace::getShortDescription() { return "CHANGE VOLUME SPACE INFORMATION"; } OperationParameters* OperationVolumeSetSpace::getParameters() { OperationParameters* ret = new OperationParameters(); ret->addVolumeParameter(1, "volume-in", "the input volume"); ret->addStringParameter(2, "volume-out", "output - the output volume");//fake the "out" parameter formatting, because copying a volume file in memory is currently a problem OptionalParameter* plumbOpt = ret->createOptionalParameter(3, "-plumb", "set via axis order and spacing/offset"); plumbOpt->addStringParameter(1, "axis-order", "a string like 'XYZ' that specifies which index is along which spatial dimension"); plumbOpt->addDoubleParameter(2, "x-spacing", "change in x-coordinate from incrementing the relevant index"); plumbOpt->addDoubleParameter(3, "y-spacing", "change in y-coordinate from incrementing the relevant index"); plumbOpt->addDoubleParameter(4, "z-spacing", "change in z-coordinate from incrementing the relevant index"); plumbOpt->addDoubleParameter(5, "x-offset", "the x-coordinate of the first voxel"); plumbOpt->addDoubleParameter(6, "y-offset", "the y-coordinate of the first voxel"); plumbOpt->addDoubleParameter(7, "z-offset", "the z-coordinate of the first voxel"); OptionalParameter* sformOpt = ret->createOptionalParameter(4, "-sform", "set via a nifti sform"); char axisNames[] = "xyz", indexNames[] = "ijk"; for (int axis = 0; axis < 3; ++axis) { for (int index = 0; index < 3; ++index) { sformOpt->addDoubleParameter(axis * 4 + index, AString(axisNames[axis]) + indexNames[index] + "-spacing", "increase in " + AString(axisNames[axis]) + " coordinate from incrementing the " + indexNames[index] + " index"); } sformOpt->addDoubleParameter(axis * 4 + 3, AString(axisNames[axis]) + "-offset", AString(axisNames[axis]) + " coordinate of first voxel"); } ret->setHelpText( AString("Writes a copy of the volume file, with the spacing information changed as specified. ") + "No reordering of the voxel data occurs. " + "Exactly one of -plumb or -sform must be specified." ); return ret; } void OperationVolumeSetSpace::useParameters(OperationParameters* myParams, ProgressObject* myProgObj) { LevelProgress myProgress(myProgObj); VolumeFile* orig = myParams->getVolume(1); FloatMatrix newSform = FloatMatrix::zeros(4, 4); newSform[3][3] = 1.0f;//not needed, but just for sanity bool haveSpace = false; OptionalParameter* plumbOpt = myParams->getOptionalParameter(3); if (plumbOpt->m_present) { haveSpace = true; bool used[3] = {false, false, false}; int revorder[3] = {-1, -1, -1}; AString orient = plumbOpt->getString(1); if (orient.size() < 3) throw OperationException(" must have 3 characters"); for (int i = 0; i < 3; ++i) { int dir = -1; switch (orient[i].toAscii()) { case 'X': case 'x': dir = 0; break; case 'Y': case 'y': dir = 1; break; case 'Z': case 'z': dir = 2; break; default: throw OperationException(" must use the characters X, Y, and Z"); } if (used[dir]) throw OperationException(" may not repeat an axis"); used[dir] = true; revorder[dir] = i;//construct the reversed order, because thats what we need } newSform[0][revorder[0]] = (float)plumbOpt->getDouble(2); newSform[0][3] = (float)plumbOpt->getDouble(5); newSform[1][revorder[1]] = (float)plumbOpt->getDouble(3); newSform[1][3] = (float)plumbOpt->getDouble(6); newSform[2][revorder[2]] = (float)plumbOpt->getDouble(4); newSform[2][3] = (float)plumbOpt->getDouble(7); } OptionalParameter* sformOpt = myParams->getOptionalParameter(4); if (sformOpt->m_present) { if (haveSpace) throw OperationException("only one of -plumb and -sform may be specified"); haveSpace = true; for (int axis = 0; axis < 3; ++axis) { for (int index = 0; index < 3; ++index) { newSform[axis][index] = (float)sformOpt->getDouble(axis * 4 + index); } newSform[axis][3] = (float)sformOpt->getDouble(axis * 4 + 3); } } if (!haveSpace) throw OperationException("you must specify -plumb or -sform"); orig->setVolumeSpace(newSform.getMatrix()); orig->writeFile(myParams->getString(2)); } connectome-workbench-1.2.3+git41-gc4c6c90/src/Operations/OperationVolumeSetSpace.h000066400000000000000000000026301300200146000276210ustar00rootroot00000000000000#ifndef __OPERATION_VOLUME_SET_SPACE_H__ #define __OPERATION_VOLUME_SET_SPACE_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 "AbstractOperation.h" namespace caret { class OperationVolumeSetSpace : public AbstractOperation { public: static OperationParameters* getParameters(); static void useParameters(OperationParameters* myParams, ProgressObject* myProgObj); static AString getCommandSwitch(); static AString getShortDescription(); }; typedef TemplateAutoOperation AutoOperationVolumeSetSpace; } #endif //__OPERATION_VOLUME_SET_SPACE_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Operations/OperationVolumeStats.cxx000066400000000000000000000227201300200146000275650ustar00rootroot00000000000000/*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 "OperationVolumeStats.h" #include "OperationException.h" #include "ReductionOperation.h" #include "VolumeFile.h" #include #include #include #include #include #include using namespace caret; using namespace std; AString OperationVolumeStats::getCommandSwitch() { return "-volume-stats"; } AString OperationVolumeStats::getShortDescription() { return "SPATIAL STATISTICS ON A VOLUME FILE"; } OperationParameters* OperationVolumeStats::getParameters() { OperationParameters* ret = new OperationParameters(); ret->addVolumeParameter(1, "volume-in", "the input volume"); OptionalParameter* reduceOpt = ret->createOptionalParameter(2, "-reduce", "use a reduction operation"); reduceOpt->addStringParameter(1, "operation", "the reduction operation"); OptionalParameter* percentileOpt = ret->createOptionalParameter(3, "-percentile", "give the value at a percentile"); percentileOpt->addDoubleParameter(1, "percent", "the percentile to find"); OptionalParameter* subvolOpt = ret->createOptionalParameter(4, "-subvolume", "only display output for one subvolume"); subvolOpt->addStringParameter(1, "subvolume", "the subvolume number or name"); OptionalParameter* roiOpt = ret->createOptionalParameter(5, "-roi", "only consider data inside an roi"); roiOpt->addVolumeParameter(1, "roi-volume", "the roi, as a volume file"); roiOpt->createOptionalParameter(2, "-match-maps", "each subvolume of input uses the corresponding subvolume from the roi file"); ret->createOptionalParameter(6, "-show-map-name", "print map index and name before each output"); ret->setHelpText( AString("For each subvolume of the input, a single number is printed, resulting from the specified reduction or percentile operation. ") + "Use -subvolume to only give output for a single subvolume. " + "Use -roi to consider only the data within a region. " + "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()); return ret; } namespace { float reduce(const float* data, const int64_t& numElements, const ReductionEnum::Enum& myop, const float* roiData) { if (roiData == NULL) { return ReductionOperation::reduce(data, numElements, myop); } else { vector toUse; toUse.reserve(numElements); for (int64_t i = 0; i < numElements; ++i) { if (roiData[i] > 0.0f) { toUse.push_back(data[i]); } } if (toUse.empty()) throw OperationException("roi contains no voxels"); return ReductionOperation::reduce(toUse.data(), toUse.size(), myop); } } float percentile(const float* data, const int64_t& numElements, const float& percent, const float* roiData) { CaretAssert(percent >= 0.0f && percent <= 100.0f); vector toUse; if (roiData == NULL) { toUse = vector(data, data + numElements); } else { toUse.reserve(numElements); for (int64_t i = 0; i < numElements; ++i) { if (roiData[i] > 0.0f) { toUse.push_back(data[i]); } } } if (toUse.empty()) throw OperationException("roi contains no voxels"); sort(toUse.begin(), toUse.end()); const double index = percent / 100.0f * (toUse.size() - 1); if (index <= 0) return toUse[0]; if (index >= toUse.size() - 1) return toUse.back(); double ipart, fpart; fpart = modf(index, &ipart); return (1.0f - fpart) * toUse[(int64_t)ipart] + fpart * toUse[((int64_t)ipart) + 1]; } } void OperationVolumeStats::useParameters(OperationParameters* myParams, ProgressObject* myProgObj) { LevelProgress myProgress(myProgObj); VolumeFile* input = myParams->getVolume(1); vector dims = input->getDimensions(); const int64_t frameSize = dims[0] * dims[1] * dims[2]; if (input->getNumberOfComponents() != 1) throw OperationException("multi-component volumes are not supported in -volume-stats"); OptionalParameter* reduceOpt = myParams->getOptionalParameter(2); OptionalParameter* percentileOpt = myParams->getOptionalParameter(3); if (reduceOpt->m_present == percentileOpt->m_present)//use == as logical xnor { throw OperationException("you must use exactly one of -reduce or -percentile"); } ReductionEnum::Enum myop = ReductionEnum::INVALID; if (reduceOpt->m_present) { bool ok = false; myop = ReductionEnum::fromName(reduceOpt->getString(1), &ok); if (!ok) throw OperationException("unrecognized reduction operation: " + reduceOpt->getString(1)); } float percent = 0.0f; if (percentileOpt->m_present) { percent = (float)percentileOpt->getDouble(1);//use not within range to trap NaNs, just in case if (!(percent >= 0.0f && percent <= 100.0f)) throw OperationException("percentile must be between 0 and 100"); } int subvol = -1; OptionalParameter* subvolOpt = myParams->getOptionalParameter(4); if (subvolOpt->m_present) { subvol = input->getMapIndexFromNameOrNumber(subvolOpt->getString(1)); if (subvol < 0) throw OperationException("invalid column specified"); } bool matchSubvolMode = false; VolumeFile* myRoi = NULL; const float* roiData = NULL; OptionalParameter* roiOpt = myParams->getOptionalParameter(5); if (roiOpt->m_present) { myRoi = roiOpt->getVolume(1); 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"); } matchSubvolMode = true; } else { roiData = myRoi->getFrame(); } } bool showMapName = myParams->getOptionalParameter(6)->m_present; int numMaps = input->getNumberOfMaps(); 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; } } 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) { roiData = myRoi->getFrame(i); } 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; } } } 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; } } } connectome-workbench-1.2.3+git41-gc4c6c90/src/Operations/OperationVolumeStats.h000066400000000000000000000026031300200146000272100ustar00rootroot00000000000000#ifndef __OPERATION_VOLUME_STATS_H__ #define __OPERATION_VOLUME_STATS_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 "AbstractOperation.h" namespace caret { class OperationVolumeStats : public AbstractOperation { public: static OperationParameters* getParameters(); static void useParameters(OperationParameters* myParams, ProgressObject* myProgObj); static AString getCommandSwitch(); static AString getShortDescription(); }; typedef TemplateAutoOperation AutoOperationVolumeStats; } #endif //__OPERATION_VOLUME_STATS_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Operations/OperationVolumeWeightedStats.cxx000066400000000000000000000377161300200146000312610ustar00rootroot00000000000000/*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 "OperationVolumeWeightedStats.h" #include "OperationException.h" #include "CaretHeap.h" #include "VolumeFile.h" #include #include #include #include #include using namespace caret; using namespace std; AString OperationVolumeWeightedStats::getCommandSwitch() { return "-volume-weighted-stats"; } AString OperationVolumeWeightedStats::getShortDescription() { return "WEIGHTED SPATIAL STATISTICS ON A VOLUME FILE"; } OperationParameters* OperationVolumeWeightedStats::getParameters() { OperationParameters* ret = new OperationParameters(); ret->addVolumeParameter(1, "volume-in", "the input volume"); OptionalParameter* weightVolumeOpt = ret->createOptionalParameter(2, "-weight-volume", "use weights from a volume file"); weightVolumeOpt->addVolumeParameter(1, "weight-volume", "volume file containing the weights"); OptionalParameter* subvolOpt = ret->createOptionalParameter(3, "-subvolume", "only display output for one subvolume"); subvolOpt->addStringParameter(1, "subvolume", "the subvolume number or name"); OptionalParameter* roiOpt = ret->createOptionalParameter(4, "-roi", "only consider data inside an roi"); roiOpt->addVolumeParameter(1, "roi-volume", "the roi, as a volume file"); roiOpt->createOptionalParameter(2, "-match-maps", "each subvolume of input uses the corresponding subvolume from the roi file"); ret->createOptionalParameter(5, "-mean", "compute weighted mean"); OptionalParameter* stdevOpt = ret->createOptionalParameter(6, "-stdev", "compute weighted standard deviation"); stdevOpt->createOptionalParameter(1, "-sample", "estimate population stdev from the sample"); OptionalParameter* percentileOpt = ret->createOptionalParameter(7, "-percentile", "compute weighted percentile"); percentileOpt->addDoubleParameter(1, "percent", "the percentile to find"); ret->createOptionalParameter(8, "-sum", "compute weighted sum"); ret->createOptionalParameter(9, "-show-map-name", "print map index and name before each output"); ret->setHelpText( AString("For each subvolume of the input, a single number is printed, resulting from the specified operation. ") + "If -weight-volume is not specified, each voxel's volume is used. " + "Use -subvolume to only give output for a single subvolume. " + "Use -roi to consider only the data within a region. " + "Exactly one of -mean, -stdev, -percentile or -sum must be specified.\n\n" + "Using -sum without -weight-volume is equivalent to integrating with respect to volume." ); return ret; } namespace { enum OperationType { MEAN, STDEV, SAMPSTDEV, PERCENTILE, SUM }; float doOperation(const float* data, const float* weights, const int64_t& numElements, const OperationType& myop, const float* roiData, const float& argument) {//argument is only used for percentile currently if (roiData != NULL) { bool haveData = false; for (int64_t i = 0; i < numElements; ++i) { if (weights[i] > 0.0f) { haveData = true; break; } } if (!haveData) throw OperationException("roi contains no voxels"); } switch(myop) { case SUM: case MEAN: case STDEV: case SAMPSTDEV://these all start the same way { double accum = 0.0, weightsum = 0.0; for (int64_t i = 0; i < numElements; ++i) { if (roiData == NULL || roiData[i] > 0.0f) { accum += data[i] * weights[i]; weightsum += weights[i]; } } if (myop == SUM) return accum; const float mean = accum / weightsum; if (myop == MEAN) return mean; accum = 0.0; double weightsum2 = 0.0;//for weighted sample stdev for (int64_t i = 0; i < numElements; ++i) { if (roiData == NULL || roiData[i] > 0.0f) { float tempf = data[i] - mean; accum += weights[i] * tempf * tempf; weightsum2 += weights[i] * weights[i]; } } if (myop == STDEV) return sqrt(accum / weightsum); CaretAssert(myop == SAMPSTDEV); return sqrt(accum / (weightsum - weightsum2 / weightsum));//http://en.wikipedia.org/wiki/Weighted_arithmetic_mean#Weighted_sample_variance } case PERCENTILE: { CaretAssert(argument >= 0.0f && argument <= 100.0f); CaretSimpleMinHeap sorter; double weightaccum = 0.0;//double will usually prevent adding weights in a different order from getting a different answer for (int64_t i = 0; i < numElements; ++i) { if (roiData == NULL || roiData[i] > 0.0f) { if (weights[i] < 0.0f) throw OperationException("negative weights not allowed in weighted percentile"); weightaccum += weights[i]; sorter.push(weights[i], data[i]);//sort by value, so the key is the data } } int64_t numUse = sorter.size(); if (numUse == 1)//would need special handling anyway, so get it early { float ret; sorter.top(&ret); return ret; } float targetWeight = argument / 100.0f * weightaccum; float lastData, nextData; float lastWeight = sorter.pop(&lastData); weightaccum = lastWeight; float nextWeight = sorter.top(&nextData); int64_t position = 1;//because the first and last sections get special treatment to not have flat ends on the function while (weightaccum + nextWeight * 0.5f < targetWeight && sorter.size() > 1) { ++position; sorter.pop(); weightaccum += nextWeight; lastWeight = nextWeight; lastData = nextData; nextWeight = sorter.top(&nextData); } if (targetWeight < weightaccum) { if (position == 1) {//stretch interpolation at first position to the edge return lastData + (nextData - lastData) * 0.5f * ((targetWeight - weightaccum) / lastWeight + 1.0f); } else { return lastData + (nextData - lastData) * 0.5f * ((targetWeight - weightaccum) / (lastWeight * 0.5f) + 1.0f); } } else { if (position == numUse - 1) {//ditto return (lastData + nextData) * 0.5f + (nextData - lastData) * 0.5f * (targetWeight - weightaccum) / nextWeight; } else { return (lastData + nextData) * 0.5f + (nextData - lastData) * 0.5f * (targetWeight - weightaccum) / (nextWeight * 0.5f); } } } } CaretAssert(false);//make sure execution never actually reaches end of function throw OperationException("internal error in weighted stats"); } float doOperationSingleWeight(const float* data, const float& weight, const int64_t& numElements, const OperationType& myop, const float* roiData, const float& argument) {//argument is only used for percentile currently const float* useData = data; int64_t numUse = numElements; vector dataScratch;//for when we have an ROI if (roiData != NULL) { dataScratch.reserve(numElements); for (int64_t i = 0; i < numElements; ++i) { if (roiData[i] > 0.0f) { dataScratch.push_back(data[i]); } } if (dataScratch.size() < 1) throw OperationException("roi contains no voxels"); useData = dataScratch.data(); numUse = (int64_t)dataScratch.size(); } switch(myop) { case SUM: case MEAN: case STDEV: case SAMPSTDEV://these all start the same way { double accum = 0.0; for (int64_t i = 0; i < numUse; ++i) { accum += useData[i]; } if (myop == SUM) return accum * weight;//this is the only operation that needs the weight when it is the same at every location const float mean = accum / numUse; if (myop == MEAN) return mean; accum = 0.0; for (int64_t i = 0; i < numUse; ++i) { float tempf = useData[i] - mean; accum += tempf * tempf; } if (myop == STDEV) return sqrt(accum / numUse); CaretAssert(myop == SAMPSTDEV); if (numUse < 2) throw OperationException("sample standard deviation requires at least 2 elements in the roi"); return sqrt(accum / (numUse - 1)); } case PERCENTILE: { CaretAssert(argument >= 0.0f && argument <= 100.0f);//same as unweighted vector sortCopy(useData, useData + numUse); sort(sortCopy.begin(), sortCopy.end()); const double index = argument / 100.0f * (sortCopy.size() - 1); if (index <= 0) return sortCopy[0]; if (index >= sortCopy.size() - 1) return sortCopy.back(); double ipart, fpart; fpart = modf(index, &ipart); return (1.0f - fpart) * sortCopy[(int64_t)ipart] + fpart * sortCopy[((int64_t)ipart) + 1]; } } CaretAssert(false);//make sure execution never actually reaches end of function throw OperationException("internal error in weighted stats"); } } void OperationVolumeWeightedStats::useParameters(OperationParameters* myParams, ProgressObject* myProgObj) { LevelProgress myProgress(myProgObj); VolumeFile* input = myParams->getVolume(1); const float constWeight = input->getVolumeSpace().getVoxelVolume();//even if we don't need it vector dims = input->getDimensions(); const int64_t frameSize = dims[0] * dims[1] * dims[2]; if (input->getNumberOfComponents() != 1) throw OperationException("multi-component volumes are not supported in -volume-weighted-stats"); OptionalParameter* weightVolumeOpt = myParams->getOptionalParameter(2); VolumeFile* myWeights = NULL; const float* weightData = NULL; 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(); } int subvol = -1; OptionalParameter* subvolOpt = myParams->getOptionalParameter(3); if (subvolOpt->m_present) { subvol = input->getMapIndexFromNameOrNumber(subvolOpt->getString(1)); if (subvol < 0) throw OperationException("invalid column specified"); } bool matchSubvolMode = false; VolumeFile* myRoi = NULL; const float* roiData = NULL; OptionalParameter* roiOpt = myParams->getOptionalParameter(4); if (roiOpt->m_present) { myRoi = roiOpt->getVolume(1); 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"); } matchSubvolMode = true; } else { roiData = myRoi->getFrame(); } } bool haveOp = false; OperationType myop; if (myParams->getOptionalParameter(5)->m_present) { haveOp = true; myop = MEAN; } OptionalParameter* stdevOpt = myParams->getOptionalParameter(6); if (stdevOpt->m_present) { if (haveOp) throw OperationException("you may only specify one operation"); haveOp = true; if (stdevOpt->getOptionalParameter(1)->m_present) { myop = SAMPSTDEV; } else { myop = STDEV; } } float argument = -1.0f; OptionalParameter* percentileOpt = myParams->getOptionalParameter(7); if (percentileOpt->m_present) { if (haveOp) throw OperationException("you may only specify one operation"); haveOp = true; myop = PERCENTILE; argument = percentileOpt->getDouble(1); if (!(argument >= 0.0f && argument <= 100.0f)) throw OperationException("percentile must be between 0 and 100"); } if (myParams->getOptionalParameter(8)->m_present) { if (haveOp) throw OperationException("you may only specify one operation"); haveOp = true; myop = SUM; } if (!haveOp) throw OperationException("you must specify an operation"); bool showMapName = myParams->getOptionalParameter(9)->m_present; int numMaps = input->getNumberOfMaps(); if (subvol == -1) { for (int i = 0; i < numMaps; ++i) {//store result before printing anything, in case it throws while computing if (matchSubvolMode) { roiData = myRoi->getFrame(i); } 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); } 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); } else { result = doOperationSingleWeight(input->getFrame(subvol), constWeight, frameSize, myop, roiData, argument); } if (showMapName) cout << AString::number(subvol + 1) << ": " << input->getMapName(subvol) << ": "; stringstream resultsstr; resultsstr << setprecision(7) << result; cout << resultsstr.str() << endl; } } connectome-workbench-1.2.3+git41-gc4c6c90/src/Operations/OperationVolumeWeightedStats.h000066400000000000000000000026661300200146000307020ustar00rootroot00000000000000#ifndef __OPERATION_VOLUME_WEIGHTED_STATS_H__ #define __OPERATION_VOLUME_WEIGHTED_STATS_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 "AbstractOperation.h" namespace caret { class OperationVolumeWeightedStats : public AbstractOperation { public: static OperationParameters* getParameters(); static void useParameters(OperationParameters* myParams, ProgressObject* myProgObj); static AString getCommandSwitch(); static AString getShortDescription(); }; typedef TemplateAutoOperation AutoOperationVolumeWeightedStats; } #endif //__OPERATION_VOLUME_WEIGHTED_STATS_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Operations/OperationWbsparseMergeDense.cxx000066400000000000000000000324321300200146000310250ustar00rootroot00000000000000/*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 "OperationWbsparseMergeDense.h" #include "OperationException.h" #include "CaretSparseFile.h" using namespace caret; using namespace std; AString OperationWbsparseMergeDense::getCommandSwitch() { return "-wbsparse-merge-dense"; } AString OperationWbsparseMergeDense::getShortDescription() { return "MERGE WBSPARSE FILES ALONG DENSE DIMENSION"; } OperationParameters* OperationWbsparseMergeDense::getParameters() { OperationParameters* ret = new OperationParameters(); ret->addStringParameter(1, "direction", "which dimension to merge along, ROW or COLUMN"); ret->addStringParameter(2, "wbsparse-out", "output - the output wbsparse file");//HACK: fake the output format since we don't have a wbsparse parameter type (or file type, really) ParameterComponent* wbsparseOpt = ret->createRepeatableParameter(3, "-wbsparse", "specify an input wbsparse file"); wbsparseOpt->addStringParameter(1, "wbsparse-in", "a wbsparse file to merge"); ret->setHelpText( AString("The input wbsparse files must have matching mappings along the direction not specified, and the mapping along the specified direction must be brain models.") ); return ret; } void OperationWbsparseMergeDense::useParameters(OperationParameters* myParams, ProgressObject* myProgObj) { LevelProgress myProgress(myProgObj); AString directionName = myParams->getString(1); int myDir; if (directionName == "ROW") { myDir = CiftiXML::ALONG_ROW; } else if (directionName == "COLUMN") { myDir = CiftiXML::ALONG_COLUMN; } else { throw OperationException("incorrect string for direction, use ROW or COLUMN"); } AString outputName = myParams->getString(2); const vector& myInstances = *(myParams->getRepeatableParameterInstances(3)); vector > wbsparseList; int numCifti = (int)myInstances.size(); for (int i = 0; i < numCifti; ++i) { wbsparseList.push_back(CaretPointer(new CaretSparseFile(myInstances[i]->getString(1)))); } if (wbsparseList.size() == 0) throw OperationException("no files specified"); if (myDir != CiftiXML::ALONG_ROW && myDir != CiftiXML::ALONG_COLUMN) throw OperationException("direction not supported by wbsparse merge dense"); int otherDir = 1 - myDir;//find the other direction const CiftiXML& baseXML = wbsparseList[0]->getCiftiXML(); if (baseXML.getMappingType(myDir) != CiftiMappingType::BRAIN_MODELS) throw OperationException("mapping type along specified dimension is not brain models"); if (baseXML.getMappingType(otherDir) == CiftiMappingType::LABELS)throw OperationException("labels not supported in wbsparse merge dense"); CiftiXML outXML = baseXML; VolumeSpace baseSpace; const CiftiBrainModelsMap& baseDenseMap = baseXML.getBrainModelsMap(myDir); CiftiBrainModelsMap newDenseMap = baseDenseMap; bool haveVolSpace = false; if (baseDenseMap.hasVolumeData()) { haveVolSpace = true; baseSpace = baseDenseMap.getVolumeSpace(); } vector sourceWbsparse(baseDenseMap.getModelInfo().size(), 0); for (int i = 1; i < (int)wbsparseList.size(); ++i) { const CiftiXML& otherXML = wbsparseList[i]->getCiftiXML(); if (*(baseXML.getMap(otherDir)) != *(otherXML.getMap(otherDir))) { throw OperationException("mappings along other dimension do not match"); } if (otherXML.getMappingType(myDir) != CiftiMappingType::BRAIN_MODELS) throw OperationException("input files do not have brain models mapping on merge dimension"); const CiftiBrainModelsMap& otherDenseMap = otherXML.getBrainModelsMap(myDir); if (otherDenseMap.hasVolumeData()) { if (haveVolSpace) { if (!baseSpace.matches(otherDenseMap.getVolumeSpace())) throw OperationException("input files have non-matching volume spaces"); } else { haveVolSpace = true; baseSpace = otherDenseMap.getVolumeSpace(); newDenseMap.setVolumeSpace(baseSpace);//need to set the output vol space if the first input didn't have volume data } } vector otherModels = otherDenseMap.getModelInfo(); int numModels = (int)otherModels.size(); for (int j = 0; j < numModels; ++j) { sourceWbsparse.push_back(i); const CiftiBrainModelsMap::ModelInfo& myInfo = otherModels[j]; switch (myInfo.m_type) { case CiftiBrainModelsMap::SURFACE: { vector myMap = otherDenseMap.getSurfaceMap(myInfo.m_structure); vector nodeList(myMap.size()); for (int64_t k = 0; k < (int64_t)myMap.size(); ++k) { nodeList[k] = myMap[k].m_surfaceNode; } newDenseMap.addSurfaceModel(otherDenseMap.getSurfaceNumberOfNodes(myInfo.m_structure), myInfo.m_structure, nodeList); break; } case CiftiBrainModelsMap::VOXELS: { vector myMap = otherDenseMap.getVolumeStructureMap(myInfo.m_structure); vector voxelList(myMap.size() * 3); for (int64_t k = 0; k < (int64_t)myMap.size(); ++k) { int64_t k3 = k * 3; voxelList[k3] = myMap[k].m_ijk[0]; voxelList[k3 + 1] = myMap[k].m_ijk[1]; voxelList[k3 + 2] = myMap[k].m_ijk[2]; } newDenseMap.addVolumeModel(myInfo.m_structure, voxelList); break; } default: throw OperationException("encountered unknown model type in cifti merge dense"); } } } outXML.setMap(myDir, newDenseMap); int numOutModels = (int)sourceWbsparse.size(); CaretAssert(numOutModels == (int)newDenseMap.getModelInfo().size()); int64_t outColSize = outXML.getDimensionLength(CiftiXML::ALONG_COLUMN); CaretSparseFileWriter myWriter(outputName, outXML); vector outModelInfo = newDenseMap.getModelInfo(); switch (myDir) { case CiftiXML::ALONG_ROW: { for (int64_t i = 0; i < outColSize; ++i) { int64_t curOffset = 0; vector outIndices, outValues, inIndices, inValues; int loaded = -1; for (int j = 0; j < numOutModels; ++j)//we could just do the entire row for each file, but doing it by structure could allow structure selection in the future { const CiftiBrainModelsMap::ModelInfo& myInfo = outModelInfo[j]; const CiftiXML& thisXML = wbsparseList[sourceWbsparse[j]]->getCiftiXML(); const CiftiBrainModelsMap& thisDenseMap = thisXML.getBrainModelsMap(myDir); int64_t startIndex = -1, endIndex = -1; switch (myInfo.m_type) { case CiftiBrainModelsMap::SURFACE: { vector tempMap = thisDenseMap.getSurfaceMap(myInfo.m_structure); if (tempMap.size() > 0) { startIndex = tempMap[0].m_ciftiIndex;//NOTE: CiftiXML guarantees these are ordered by cifti index and contiguous endIndex = startIndex + tempMap.size(); } else { startIndex = 0; endIndex = 0; } break; } case CiftiBrainModelsMap::VOXELS: { vector tempMap = thisDenseMap.getVolumeStructureMap(myInfo.m_structure); if (tempMap.size() > 0) { startIndex = tempMap[0].m_ciftiIndex;//NOTE: CiftiXML guarantees these are ordered by cifti index and contiguous endIndex = startIndex + tempMap.size(); } else { startIndex = 0; endIndex = 0; } break; } default: CaretAssert(false); break; } if (endIndex > startIndex) { if (loaded != sourceWbsparse[j]) { wbsparseList[sourceWbsparse[j]]->getRowSparse(i, inIndices, inValues); loaded = sourceWbsparse[j]; } int64_t numSparse = (int64_t)inIndices.size(); for (int64_t k = 0; k < numSparse; ++k) { if (inIndices[k] >= startIndex && inIndices[k] < endIndex) { outIndices.push_back(inIndices[k] + curOffset); outValues.push_back(inValues[k]); } } curOffset += endIndex - startIndex; } } myWriter.writeRowSparse(i, outIndices, outValues); outIndices.clear();//reset for next row outValues.clear(); } break; } case CiftiXML::ALONG_COLUMN: { vector inIndices, inValues; for (int j = 0; j < numOutModels; ++j) { const CiftiBrainModelsMap::ModelInfo& myInfo = outModelInfo[j]; const CiftiXML& thisXML = wbsparseList[sourceWbsparse[j]]->getCiftiXML(); const CiftiBrainModelsMap& thisDenseMap = thisXML.getBrainModelsMap(myDir); switch (myInfo.m_type) { case CiftiBrainModelsMap::SURFACE: { vector tempMap = thisDenseMap.getSurfaceMap(myInfo.m_structure), outMap = newDenseMap.getSurfaceMap(myInfo.m_structure); int64_t mapSize = (int64_t)tempMap.size(); CaretAssert(mapSize == (int64_t)outMap.size()); for (int64_t k = 0; k < mapSize; ++k) { CaretAssert(tempMap[k].m_surfaceNode == outMap[k].m_surfaceNode); wbsparseList[sourceWbsparse[j]]->getRowSparse(tempMap[k].m_ciftiIndex, inIndices, inValues); myWriter.writeRowSparse(outMap[k].m_ciftiIndex, inIndices, inValues); } break; } case CiftiBrainModelsMap::VOXELS: { vector tempMap = thisDenseMap.getVolumeStructureMap(myInfo.m_structure), outMap = newDenseMap.getVolumeStructureMap(myInfo.m_structure); int64_t mapSize = (int64_t)tempMap.size(); CaretAssert(mapSize == (int64_t)outMap.size()); for (int64_t k = 0; k < mapSize; ++k) { CaretAssert(tempMap[k].m_ijk[0] == outMap[k].m_ijk[0]); CaretAssert(tempMap[k].m_ijk[1] == outMap[k].m_ijk[1]); CaretAssert(tempMap[k].m_ijk[2] == outMap[k].m_ijk[2]); wbsparseList[sourceWbsparse[j]]->getRowSparse(tempMap[k].m_ciftiIndex, inIndices, inValues); myWriter.writeRowSparse(outMap[k].m_ciftiIndex, inIndices, inValues); } break; } default: CaretAssert(false); break; } } break; } default: CaretAssert(false); break; } myWriter.finish(); } connectome-workbench-1.2.3+git41-gc4c6c90/src/Operations/OperationWbsparseMergeDense.h000066400000000000000000000026601300200146000304520ustar00rootroot00000000000000#ifndef __OPERATION_WBSPARSE_MERGE_DENSE_H__ #define __OPERATION_WBSPARSE_MERGE_DENSE_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 "AbstractOperation.h" namespace caret { class OperationWbsparseMergeDense : public AbstractOperation { public: static OperationParameters* getParameters(); static void useParameters(OperationParameters* myParams, ProgressObject* myProgObj); static AString getCommandSwitch(); static AString getShortDescription(); }; typedef TemplateAutoOperation AutoOperationWbsparseMergeDense; } #endif //__OPERATION_WBSPARSE_MERGE_DENSE_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Operations/OperationZipSceneFile.cxx000066400000000000000000000506631300200146000276260ustar00rootroot00000000000000/*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 "CaretLogger.h" #include "DataFile.h" #include "EventManager.h" #include "EventProgressUpdate.h" #include "FileInformation.h" #include "OperationZipSceneFile.h" #include "OperationException.h" #include "Scene.h" #include "SceneAttributes.h" #include "SceneClass.h" #include "SceneClassArray.h" #include "SceneFile.h" #include "SpecFile.h" #include "quazip.h" #include "quazipfile.h" #include #include #include using namespace caret; using namespace std; AString OperationZipSceneFile::getCommandSwitch() { return "-zip-scene-file"; } AString OperationZipSceneFile::getShortDescription() { return "ZIP A SCENE FILE AND ITS DATA FILES"; } OperationParameters* OperationZipSceneFile::getParameters() { OperationParameters* ret = new OperationParameters(); ret->addStringParameter(1, "scene-file", "the scene file to make the zip file from"); ret->addStringParameter(2, "extract-folder", "the name of the folder created when the zip file is unzipped"); ret->addStringParameter(3, "zip-file", "out - the zip file that will be created"); OptionalParameter* baseOpt = ret->createOptionalParameter(4, "-base-dir", "specify a directory that all data files are somewhere within, this will become the root of the zipfile's directory structure"); 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. " "The scene file must contain only relative paths, and no data files may be outside the base directory."); return ret; } void OperationZipSceneFile::useParameters(OperationParameters* myParams, ProgressObject* myProgObj) { AString sceneFileName = myParams->getString(1); AString outputSubDirectory = myParams->getString(2); AString zipFileName = myParams->getString(3); OptionalParameter* baseOpt = myParams->getOptionalParameter(4); AString myBaseDir; if (baseOpt->m_present) { myBaseDir = QDir::cleanPath(QDir(baseOpt->getString(1)).absolutePath()); } OperationZipSceneFile::createZipFile(sceneFileName, outputSubDirectory, zipFileName, myBaseDir, PROGRESS_COMMAND_LINE, myProgObj); } void OperationZipSceneFile::createZipFile(const AString& sceneFileName, const AString& outputSubDirectory, const AString& zipFileName, const AString& baseDirectory, const ProgressMode progressMode, ProgressObject* myProgObj) { LevelProgress myProgress(myProgObj); FileInformation sceneFileInfo(sceneFileName); if (outputSubDirectory.isEmpty()) { throw OperationException("extract-dir must contain characters"); } AString myBaseDir; if ( ! baseDirectory.isEmpty()) { myBaseDir = QDir::cleanPath(QDir(baseDirectory).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])) { switch (progressMode) { case PROGRESS_COMMAND_LINE: cout << "skipping network file '" << tempNames[k] << "'" << endl; break; case PROGRESS_GUI_EVENT: break; } 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"); } } } } } EventProgressUpdate progressEvent(0, allFiles.size(), 0, "Creating ZIP File"); EventManager::get()->sendEvent(progressEvent.getPointer()); QFile zipFileObject(zipFileName); QuaZip zipFile(&zipFileObject); if (!zipFile.open(QuaZip::mdCreate)) { throw OperationException("Unable to open ZIP File \"" + zipFileName + "\" for writing."); } int32_t fileIndex = 1; static const char *myUnits[9] = {" B ", " KB", " MB", " GB", " TB", " PB", " EB", " ZB", " YB"}; for (set::iterator iter = allFiles.begin(); iter != allFiles.end(); ++iter, ++fileIndex) { 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 } switch (progressMode) { case PROGRESS_COMMAND_LINE: 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 break; case PROGRESS_GUI_EVENT: progressEvent.setProgress(fileIndex, ("Adding " + (QString::number(fileIndex) + " of " + QString::number(allFiles.size()) + " (") + ((unit > 0) ? AString::number(fileSize, 'f', 2) : AString::number(fileSize)) + myUnits[unit] + ") " + FileInformation(unzippedDataFileName).getFileName())); EventManager::get()->sendEvent(progressEvent.getPointer()); break; } 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(); switch (progressMode) { case PROGRESS_COMMAND_LINE: cout << endl; break; case PROGRESS_GUI_EVENT: break; } } zipFile.close(); switch (progressMode) { case PROGRESS_COMMAND_LINE: break; case PROGRESS_GUI_EVENT: progressEvent.setProgress(allFiles.size(), "Zip created successfully"); EventManager::get()->sendEvent(progressEvent.getPointer()); 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(); //} connectome-workbench-1.2.3+git41-gc4c6c90/src/Operations/OperationZipSceneFile.h000066400000000000000000000036021300200146000272420ustar00rootroot00000000000000#ifndef __OPERATION_ZIP_SCENE_FILE_H__ #define __OPERATION_ZIP_SCENE_FILE_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 "AbstractOperation.h" namespace caret { class OperationZipSceneFile : public AbstractOperation { public: enum ProgressMode { PROGRESS_COMMAND_LINE, PROGRESS_GUI_EVENT }; static OperationParameters* getParameters(); static void useParameters(OperationParameters* myParams, ProgressObject* myProgObj); static AString getCommandSwitch(); static AString getShortDescription(); static void createZipFile(const AString& sceneFileName, const AString& outputSubDirectory, const AString& zipFileName, const AString& baseDirectory, const ProgressMode progressMode, ProgressObject* myProgObj); }; typedef TemplateAutoOperation AutoOperationZipSceneFile; } #endif //__OPERATION_ZIP_SCENE_FILE_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Operations/OperationZipSpecFile.cxx000066400000000000000000000236731300200146000274640ustar00rootroot00000000000000/*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 "CaretLogger.h" #include "DataFile.h" #include "FileInformation.h" #include "OperationZipSpecFile.h" #include "OperationException.h" #include "SpecFile.h" #include "quazip.h" #include "quazipfile.h" //for cleanPath #include //to print file sizes as it makes the zip #include #include using namespace caret; using namespace std; AString OperationZipSpecFile::getCommandSwitch() { return "-zip-spec-file"; } AString OperationZipSpecFile::getShortDescription() { return "ZIP A SPEC FILE AND ITS DATA FILES"; } OperationParameters* OperationZipSpecFile::getParameters() { OperationParameters* ret = new OperationParameters(); ret->addStringParameter(1, "spec-file", "the specification file to add to zip file"); ret->addStringParameter(2, "extract-folder", "the name of the folder created when the zip file is unzipped"); ret->addStringParameter(3, "zip-file", "out - the zip file that will be created"); OptionalParameter* baseOpt = ret->createOptionalParameter(4, "-base-dir", "specify a directory that all data files are somewhere within, this will become the root of the zipfile's directory structure"); baseOpt->addStringParameter(1, "directory", "the directory"); ret->setHelpText(AString("If zip-file already exists, it will be overwritten. ") + "If -base-dir is not specified, the directory containing the spec file is used for the base directory. " + "The spec file must contain only relative paths, and no data files may be outside the base directory. " + "Scene files inside spec files are not checked for what files they reference, ensure that all data files referenced by the scene files are also referenced by the spec file."); return ret; } void OperationZipSpecFile::useParameters(OperationParameters* myParams, ProgressObject* myProgObj) { LevelProgress myProgress(myProgObj); AString specFileName = FileInformation(myParams->getString(1)).getAbsoluteFilePath(); AString outputSubDirectory = myParams->getString(2); AString zipFileName = FileInformation(myParams->getString(3)).getAbsoluteFilePath(); OptionalParameter* baseOpt = myParams->getOptionalParameter(4); AString myBaseDir; if (baseOpt->m_present) { myBaseDir = QDir::cleanPath(QDir(baseOpt->getString(1)).absolutePath()); } else { FileInformation specFileInfo(specFileName); myBaseDir = QDir::cleanPath(specFileInfo.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 } 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."); } } /* * Read the spec file and get the names of its data files. * Look for any files that are missing (name in spec file * but file not found). */ FileInformation specFileInfo(specFileName); AString specPath = QDir::cleanPath(specFileInfo.getAbsolutePath()); if (!specPath.endsWith('/')) { specPath += "/"; } SpecFile specFile; specFile.readFile(specFileName); std::vector allDataFileNames = specFile.getAllDataFileNames(); allDataFileNames.push_back(specFileName); /* * Verify that all data files exist */ AString missingDataFileNames; AString outsideBaseDirFiles; const int32_t numberOfDataFiles = static_cast(allDataFileNames.size()); for (int32_t i = 0; i < numberOfDataFiles; i++) { AString dataFileName = allDataFileNames[i]; if (DataFile::isFileOnNetwork(dataFileName)) { cout << "skipping network file '" << dataFileName << "'" << endl; allDataFileNames.erase(allDataFileNames.begin() + i);//remove it from the list --i;//decrement i in order not to skip anything continue; } FileInformation tempInfo(dataFileName); if (tempInfo.isRelative()) { dataFileName = specPath + dataFileName; } FileInformation dataFileInfo(dataFileName); AString absName = QDir::cleanPath(dataFileInfo.getAbsoluteFilePath()); if (!absName.startsWith(myBaseDir)) { outsideBaseDirFiles += absName + "\n"; } if (dataFileInfo.exists() == false) { missingDataFileNames += absName + "\n"; } allDataFileNames[i] = absName;//so we don't have to do this again } if (!missingDataFileNames.isEmpty()) { throw OperationException("These data files do not exist:\n" + missingDataFileNames); } if (!outsideBaseDirFiles.isEmpty()) { throw OperationException("These data files lie outside the base directiory:\n" + outsideBaseDirFiles + "Try using -base-dir."); } /* * Create the ZIP file */ QFile zipFileObject(zipFileName); QuaZip zipFile(&zipFileObject); if (zipFile.open(QuaZip::mdCreate) == false) { throw OperationException("Unable to open ZIP File \"" + zipFileName + "\" for writing."); } /* * Compress each of the files and add them to the zip file */ AString errorMessage; static const char *myUnits[9] = {" B ", " KB", " MB", " GB", " TB", " PB", " EB", " ZB", " YB"}; for (int32_t i = 0; i < numberOfDataFiles; i++) { AString dataFileName = allDataFileNames[i]; 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) == false) { errorMessage = "Unable to open \"" + dataFileName + "\" for reading: " + dataFileIn.errorString(); break; } 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) == false) { errorMessage = "Unable to open zip output for \"" + dataFileName + "\""; break; } 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) { errorMessage = "Error reading from data file"; break; } if (numRead > 0) { qint64 result = dataFileOut.write(buffer.data(), numRead); if (result != numRead) { errorMessage = "Error writing to zip file"; break; } } } if (!errorMessage.isEmpty()) break; dataFileIn.close(); dataFileOut.close(); cout << endl; } /* * Close the zip file */ zipFile.close(); /* * If there are errors, remove the ZIP file and * indicate an error has occurred. */ if (errorMessage.isEmpty() == false) { QFile::remove(zipFileName); throw OperationException(errorMessage); } } connectome-workbench-1.2.3+git41-gc4c6c90/src/Operations/OperationZipSpecFile.h000066400000000000000000000026061300200146000271020ustar00rootroot00000000000000#ifndef __OPERATION_ZIP_SPEC_FILE_H__ #define __OPERATION_ZIP_SPEC_FILE_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 "AbstractOperation.h" namespace caret { class OperationZipSpecFile : public AbstractOperation { public: static OperationParameters* getParameters(); static void useParameters(OperationParameters* myParams, ProgressObject* myProgObj); static AString getCommandSwitch(); static AString getShortDescription(); }; typedef TemplateAutoOperation AutoOperationZipSpecFile; } #endif //__OPERATION_ZIP_SPEC_FILE_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/OperationsBase/000077500000000000000000000000001300200146000234625ustar00rootroot00000000000000connectome-workbench-1.2.3+git41-gc4c6c90/src/OperationsBase/AbstractOperation.cxx000066400000000000000000000032131300200146000276310ustar00rootroot00000000000000/*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 "AbstractOperation.h" #include "CaretDataFile.h" #include "CaretLogger.h" using namespace std; using namespace caret; void AbstractOperation::checkStructureMatch(const CaretDataFile* toCheck, const StructureEnum::Enum& correctStruct, const AString& fileDescrip, const AString& basisDescrip) { if (toCheck != NULL && toCheck->getStructure() != correctStruct) { CaretLogWarning(fileDescrip + " has structure '" + StructureEnum::toName(toCheck->getStructure()) + "', while " + basisDescrip + " structure '" + StructureEnum::toName(correctStruct) + "'"); } } AbstractOperation::AbstractOperation() { } AbstractOperation::~AbstractOperation() { } OperationParserInterface::~OperationParserInterface() { delete m_autoOper; } AutoOperationInterface::~AutoOperationInterface() { } connectome-workbench-1.2.3+git41-gc4c6c90/src/OperationsBase/AbstractOperation.h000066400000000000000000000101701300200146000272560ustar00rootroot00000000000000#ifndef __ABSTRACT_OPERATION_H__ #define __ABSTRACT_OPERATION_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*/ //make it easy to use these in an algorithm class, don't just forward declare them #include "ProgressObject.h" #include "CaretAssert.h" #include "OperationParameters.h" #include "StructureEnum.h" namespace caret { class CaretDataFile; class AbstractOperation { protected: AbstractOperation(); virtual ~AbstractOperation(); public: ///override these to allow operation parsers to use your operation without writing an explicit command class static OperationParameters* getParameters() { CaretAssert(false); return NULL; } ///override these to allow operation parsers to use your operation without writing an explicit command class static void useParameters(OperationParameters*, ProgressObject*) { CaretAssert(false); } ///override this to set the command switch static AString getCommandSwitch() { CaretAssert(false); return ""; } ///override this to set the short description static AString getShortDescription() { CaretAssert(false); return ""; } ///override this if the operation doesn't take parameters static bool takesParameters() { return true; } ///convenience method for checking structures of input files static void checkStructureMatch(const CaretDataFile* toCheck, const StructureEnum::Enum& correctStruct, const AString& fileDescrip, const AString& basisDescrip); }; ///interface class for use by operation parsers - used because the above interface has only static methods, so to avoid neededing to instantiate the operation or template the parser code struct AutoOperationInterface { virtual OperationParameters* getParameters() = 0; virtual void useParameters(OperationParameters* a, ProgressObject* b) = 0; virtual AString getCommandSwitch() = 0; virtual AString getShortDescription() = 0; virtual bool takesParameters() = 0; virtual ~AutoOperationInterface(); }; ///templated interface class to pass through to something that inherits from AbstractOperation (or implements equivalent functions) ///this makes it easier to create a bridge between the static methods of the operation and an interface pointer that a parser can store template struct TemplateAutoOperation : public AutoOperationInterface { TemplateAutoOperation() { } OperationParameters* getParameters() { return T::getParameters(); } void useParameters(OperationParameters* a, ProgressObject* b) { T::useParameters(a, b); } AString getCommandSwitch() { return T::getCommandSwitch(); } AString getShortDescription() { return T::getShortDescription(); } bool takesParameters() { return T::takesParameters(); } }; ///interface class for parsers to inherit from class OperationParserInterface { OperationParserInterface();//must take an interface object, for its vtable to the real operation, so deny default construction protected: AutoOperationInterface* m_autoOper; public: OperationParserInterface(AutoOperationInterface* myAutoOper) : m_autoOper(myAutoOper) { } virtual ~OperationParserInterface(); }; } #endif //__ABSTRACT_OPERATION_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/OperationsBase/CMakeLists.txt000066400000000000000000000013321300200146000262210ustar00rootroot00000000000000# # Name of project # PROJECT (OperationsBase) # # Need XML from Qt # SET(QT_DONT_USE_QTGUI) # # Add QT for includes # INCLUDE (${QT_USE_FILE}) # # Create the helper library # ADD_LIBRARY(OperationsBase AbstractOperation.h OperationParameters.h OperationParametersEnum.h AbstractOperation.cxx OperationParameters.cxx OperationParametersEnum.cxx ) # # Find Headers # INCLUDE_DIRECTORIES( ${CMAKE_SOURCE_DIR}/Annotations ${CMAKE_SOURCE_DIR}/OperationsBase ${CMAKE_SOURCE_DIR}/Charting ${CMAKE_SOURCE_DIR}/FilesBase ${CMAKE_SOURCE_DIR}/Files ${CMAKE_SOURCE_DIR}/Gifti ${CMAKE_SOURCE_DIR}/Cifti ${CMAKE_SOURCE_DIR}/Palette ${CMAKE_SOURCE_DIR}/Nifti ${CMAKE_SOURCE_DIR}/Scenes ${CMAKE_SOURCE_DIR}/Xml ${CMAKE_SOURCE_DIR}/Common ) connectome-workbench-1.2.3+git41-gc4c6c90/src/OperationsBase/OperationParameters.cxx000066400000000000000000000431171300200146000302000ustar00rootroot00000000000000/*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 "OperationParameters.h" #include "CaretAssert.h" #include "CaretLogger.h" #include "BorderFile.h" #include "CiftiFile.h" #include "FociFile.h" #include "LabelFile.h" #include "MetricFile.h" #include "SurfaceFile.h" #include "VolumeFile.h" using namespace std; using namespace caret; ParameterComponent::ParameterComponent() { } ParameterComponent::~ParameterComponent() { for (size_t i = 0; i < m_paramList.size(); ++i) { delete m_paramList[i]; } for (size_t i = 0; i < m_outputList.size(); ++i) { delete m_outputList[i]; } for (size_t i = 0; i < m_optionList.size(); ++i) { delete m_optionList[i]; } for (size_t i = 0; i < m_repeatableOptions.size(); ++i) { delete m_repeatableOptions[i]; } } RepeatableOption::~RepeatableOption() { for (size_t i = 0; i < m_instances.size(); ++i) { delete m_instances[i]; } } OperationParameters::OperationParameters() { } ParameterComponent::ParameterComponent(const ParameterComponent& rhs) { m_paramList.resize(rhs.m_paramList.size()); for (size_t i = 0; i < m_paramList.size(); ++i) { m_paramList[i] = rhs.m_paramList[i]->cloneAbstractParameter(); } m_outputList.resize(rhs.m_outputList.size()); for (size_t i = 0; i < m_outputList.size(); ++i) { m_outputList[i] = rhs.m_outputList[i]->cloneAbstractParameter(); } m_optionList.resize(rhs.m_optionList.size()); for (size_t i = 0; i < m_optionList.size(); ++i) { m_optionList[i] = new OptionalParameter(*(rhs.m_optionList[i])); } m_repeatableOptions.resize(rhs.m_repeatableOptions.size()); for (size_t i = 0; i < m_repeatableOptions.size(); ++i) { m_repeatableOptions[i] = new RepeatableOption(*(rhs.m_repeatableOptions[i])); } } OptionalParameter* ParameterComponent::createOptionalParameter(const int32_t key, const AString& optionSwitch, const AString& description) { CaretAssertMessage(checkUniqueOption(key), "optional parameter created with previously used key"); if (optionSwitch.isEmpty() || optionSwitch[0] != '-') CaretLogWarning("developer warning: option '" + optionSwitch + "' created, but does not start with dash"); OptionalParameter* ret = new OptionalParameter(key, optionSwitch, description); m_optionList.push_back(ret); return ret; } ParameterComponent* ParameterComponent::createRepeatableParameter(const int32_t key, const AString& optionSwitch, const AString& description) { CaretAssertMessage(checkUniqueRepeatable(key), "repeatable parameter created with previously used key"); if (optionSwitch.isEmpty() || optionSwitch[0] != '-') CaretLogWarning("developer warning: repeatable option '" + optionSwitch + "' created, but does not start with dash"); RepeatableOption* newOpt = new RepeatableOption(key, optionSwitch, description); m_repeatableOptions.push_back(newOpt); return &(newOpt->m_template); } bool ParameterComponent::checkUniqueInput(const int32_t& key, const OperationParametersEnum::Enum& type) { for (size_t i = 0; i < m_paramList.size(); ++i) { if (m_paramList[i]->m_key == key && type == m_paramList[i]->getType()) { return false; } } return true; } bool ParameterComponent::checkUniqueOption(const int32_t& key) { for (size_t i = 0; i < m_optionList.size(); ++i) { if (m_optionList[i]->m_key == key) { return false; } } return true; } bool ParameterComponent::checkUniqueRepeatable(const int32_t& key) { for (size_t i = 0; i < m_repeatableOptions.size(); ++i) { if (m_repeatableOptions[i]->m_key == key) { return false; } } return true; } bool ParameterComponent::checkUniqueOutput(const int32_t& key, const OperationParametersEnum::Enum& type) { for (size_t i = 0; i < m_outputList.size(); ++i) { if (m_outputList[i]->m_key == key && type == m_outputList[i]->getType()) { return false; } } return true; } vector ParameterComponent::findUncheckedParams(const AString& contextString) const { vector ret; for (size_t i = 0; i < m_paramList.size(); ++i) { if (!m_paramList[i]->m_operationUsed) { ret.push_back("parameter '" + m_paramList[i]->m_shortName + "' of " + contextString + " was not checked by the operation"); } } for (size_t i = 0; i < m_outputList.size(); ++i) { if (!m_outputList[i]->m_operationUsed) { ret.push_back("parameter '" + m_outputList[i]->m_shortName + "' of " + contextString + " was not checked by the operation"); } } for (size_t i = 0; i < m_optionList.size(); ++i) { if (!m_optionList[i]->m_operationUsed) { ret.push_back("option '" + m_optionList[i]->m_optionSwitch + "' of " + contextString + " was not checked by the operation"); } if (m_optionList[i]->m_present) { vector temp = m_optionList[i]->findUncheckedParams("option '" + m_optionList[i]->m_optionSwitch + "'"); ret.insert(ret.end(), temp.begin(), temp.end()); } } for (size_t i = 0; i < m_repeatableOptions.size(); ++i) { if (!m_repeatableOptions[i]->m_operationUsed) { ret.push_back("option '" + m_repeatableOptions[i]->m_optionSwitch + "' of " + contextString + " was not checked by the operation"); } for (size_t j = 0; j < m_repeatableOptions[i]->m_instances.size(); ++j) { vector temp = m_repeatableOptions[i]->m_instances[j]->findUncheckedParams("option '" + m_repeatableOptions[i]->m_optionSwitch + "'"); ret.insert(ret.end(), temp.begin(), temp.end()); } } return ret; } AbstractParameter* ParameterComponent::getInputParameter(const int32_t key, const OperationParametersEnum::Enum type) { for (size_t i = 0; i < m_paramList.size(); ++i) { if (m_paramList[i]->m_key == key && type == m_paramList[i]->getType()) { m_paramList[i]->m_operationUsed = true; return m_paramList[i]; } } CaretAssertMessage(false, "Algorithm asked for parameter it didn't specify, or of wrong type"); return NULL; } OptionalParameter* ParameterComponent::getOptionalParameter(const int32_t key) { for (size_t i = 0; i < m_optionList.size(); ++i) { if (m_optionList[i]->m_key == key) { m_optionList[i]->m_operationUsed = true; return m_optionList[i]; } } CaretAssertMessage(false, "Algorithm asked for option it didn't specify"); return NULL; } const vector* ParameterComponent::getRepeatableParameterInstances(const int32_t key) { for (size_t i = 0; i < m_repeatableOptions.size(); ++i) { if (m_repeatableOptions[i]->m_key == key) { m_repeatableOptions[i]->m_operationUsed = true; return &(m_repeatableOptions[i]->m_instances); } } CaretAssertMessage(false, "Algorithm asked for option it didn't specify"); return NULL; } AbstractParameter* ParameterComponent::getOutputParameter(const int32_t key, const OperationParametersEnum::Enum type) { for (size_t i = 0; i < m_outputList.size(); ++i) { if (m_outputList[i]->m_key == key && type == m_outputList[i]->getType()) { m_outputList[i]->m_operationUsed = true; return m_outputList[i]; } } CaretAssertMessage(false, "Algorithm asked for output it didn't specify, or of wrong type"); return NULL; } //sadly, lots of boilerplate for convenience functions void ParameterComponent::addBooleanParameter(const int32_t key, const caret::AString& name, const caret::AString& description) { CaretAssertMessage(checkUniqueInput(key, OperationParametersEnum::BOOL), "input boolean parameter created with previously used key"); m_paramList.push_back(new BooleanParameter(key, name, description)); } void ParameterComponent::addCiftiParameter(const int32_t key, const AString& name, const AString& description) { CaretAssertMessage(checkUniqueInput(key, OperationParametersEnum::CIFTI), "input cifti parameter created with previously used key"); m_paramList.push_back(new CiftiParameter(key, name, description)); } void ParameterComponent::addFociParameter(const int32_t key, const AString& name, const AString& description) { CaretAssertMessage(checkUniqueInput(key, OperationParametersEnum::FOCI), "input foci parameter created with previously used key"); m_paramList.push_back(new FociParameter(key, name, description)); } void ParameterComponent::addBorderParameter(const int32_t key, const AString& name, const AString& description) { CaretAssertMessage(checkUniqueInput(key, OperationParametersEnum::BORDER), "input border parameter created with previously used key"); m_paramList.push_back(new BorderParameter(key, name, description)); } void ParameterComponent::addDoubleParameter(const int32_t key, const AString& name, const AString& description) { CaretAssertMessage(checkUniqueInput(key, OperationParametersEnum::DOUBLE), "input double parameter created with previously used key"); m_paramList.push_back(new DoubleParameter(key, name, description)); } void ParameterComponent::addMetricParameter(const int32_t key, const AString& name, const AString& description) { CaretAssertMessage(checkUniqueInput(key, OperationParametersEnum::METRIC), "input metric parameter created with previously used key"); m_paramList.push_back(new MetricParameter(key, name, description)); } void ParameterComponent::addIntegerParameter(const int32_t key, const AString& name, const AString& description) { CaretAssertMessage(checkUniqueInput(key, OperationParametersEnum::INT), "input integer parameter created with previously used key"); m_paramList.push_back(new IntegerParameter(key, name, description)); } void ParameterComponent::addLabelParameter(const int32_t key, const AString& name, const AString& description) { CaretAssertMessage(checkUniqueInput(key, OperationParametersEnum::LABEL), "input label parameter created with previously used key"); m_paramList.push_back(new LabelParameter(key, name, description)); } void ParameterComponent::addStringParameter(const int32_t key, const AString& name, const AString& description) { CaretAssertMessage(checkUniqueInput(key, OperationParametersEnum::STRING), "input string parameter created with previously used key"); m_paramList.push_back(new StringParameter(key, name, description)); } void ParameterComponent::addSurfaceParameter(const int32_t key, const AString& name, const AString& description) { CaretAssertMessage(checkUniqueInput(key, OperationParametersEnum::SURFACE), "input surface parameter created with previously used key"); m_paramList.push_back(new SurfaceParameter(key, name, description)); } void ParameterComponent::addVolumeParameter(const int32_t key, const AString& name, const AString& description) { CaretAssertMessage(checkUniqueInput(key, OperationParametersEnum::VOLUME), "input volume parameter created with previously used key"); m_paramList.push_back(new VolumeParameter(key, name, description)); } void OperationParameters::setHelpText(const AString& textIn) { m_helpText = textIn; } void ParameterComponent::addCiftiOutputParameter(const int32_t key, const AString& name, const AString& description) { CaretAssertMessage(checkUniqueOutput(key, OperationParametersEnum::CIFTI), "output cifti parameter created with previously used key"); m_outputList.push_back(new CiftiParameter(key, name, description)); } void ParameterComponent::addFociOutputParameter(const int32_t key, const AString& name, const AString& description) { CaretAssertMessage(checkUniqueOutput(key, OperationParametersEnum::FOCI), "output foci parameter created with previously used key"); m_outputList.push_back(new FociParameter(key, name, description)); } void ParameterComponent::addBorderOutputParameter(const int32_t key, const AString& name, const AString& description) { CaretAssertMessage(checkUniqueOutput(key, OperationParametersEnum::BORDER), "output foci parameter created with previously used key"); m_outputList.push_back(new BorderParameter(key, name, description)); } void ParameterComponent::addMetricOutputParameter(const int32_t key, const AString& name, const AString& description) { CaretAssertMessage(checkUniqueOutput(key, OperationParametersEnum::METRIC), "output metric parameter created with previously used key"); m_outputList.push_back(new MetricParameter(key, name, description)); } void ParameterComponent::addLabelOutputParameter(const int32_t key, const AString& name, const AString& description) { CaretAssertMessage(checkUniqueOutput(key, OperationParametersEnum::LABEL), "output label parameter created with previously used key"); m_outputList.push_back(new LabelParameter(key, name, description)); } void ParameterComponent::addSurfaceOutputParameter(const int32_t key, const AString& name, const AString& description) { CaretAssertMessage(checkUniqueOutput(key, OperationParametersEnum::SURFACE), "output surface parameter created with previously used key"); m_outputList.push_back(new SurfaceParameter(key, name, description)); } void ParameterComponent::addVolumeOutputParameter(const int32_t key, const AString& name, const AString& description) { CaretAssertMessage(checkUniqueOutput(key, OperationParametersEnum::VOLUME), "output volume parameter created with previously used key"); m_outputList.push_back(new VolumeParameter(key, name, description)); } AString& OperationParameters::getHelpText() { return m_helpText; } AbstractParameter::~AbstractParameter() { } bool ParameterComponent::getBoolean(const int32_t key) { return ((BooleanParameter*)getInputParameter(key, OperationParametersEnum::BOOL))->m_parameter; } CiftiFile* ParameterComponent::getCifti(const int32_t key) { return ((CiftiParameter*)getInputParameter(key, OperationParametersEnum::CIFTI))->m_parameter.getPointer(); } FociFile* ParameterComponent::getFoci(const int32_t key) { return ((FociParameter*)getInputParameter(key, OperationParametersEnum::FOCI))->m_parameter.getPointer(); } BorderFile* ParameterComponent::getBorder(const int32_t key) { return ((BorderParameter*)getInputParameter(key, OperationParametersEnum::BORDER))->m_parameter.getPointer(); } double ParameterComponent::getDouble(const int32_t key) { return ((DoubleParameter*)getInputParameter(key, OperationParametersEnum::DOUBLE))->m_parameter; } int64_t ParameterComponent::getInteger(const int32_t key) { return ((IntegerParameter*)getInputParameter(key, OperationParametersEnum::INT))->m_parameter; } LabelFile* ParameterComponent::getLabel(const int32_t key) { return ((LabelParameter*)getInputParameter(key, OperationParametersEnum::LABEL))->m_parameter.getPointer(); } MetricFile* ParameterComponent::getMetric(const int32_t key) { return ((MetricParameter*)getInputParameter(key, OperationParametersEnum::METRIC))->m_parameter.getPointer(); } const AString& ParameterComponent::getString(const int32_t key) { return ((StringParameter*)getInputParameter(key, OperationParametersEnum::STRING))->m_parameter; } SurfaceFile* ParameterComponent::getSurface(const int32_t key) { return ((SurfaceParameter*)getInputParameter(key, OperationParametersEnum::SURFACE))->m_parameter.getPointer(); } VolumeFile* ParameterComponent::getVolume(const int32_t key) { return ((VolumeParameter*)getInputParameter(key, OperationParametersEnum::VOLUME))->m_parameter.getPointer(); } CiftiFile* ParameterComponent::getOutputCifti(const int32_t key) { return ((CiftiParameter*)getOutputParameter(key, OperationParametersEnum::CIFTI))->m_parameter.getPointer(); } FociFile* ParameterComponent::getOutputFoci(const int32_t key) { return ((FociParameter*)getOutputParameter(key, OperationParametersEnum::FOCI))->m_parameter.getPointer(); } BorderFile* ParameterComponent::getOutputBorder(const int32_t key) { return ((BorderParameter*)getOutputParameter(key, OperationParametersEnum::BORDER))->m_parameter.getPointer(); } LabelFile* ParameterComponent::getOutputLabel(const int32_t key) { return ((LabelParameter*)getOutputParameter(key, OperationParametersEnum::LABEL))->m_parameter.getPointer(); } SurfaceFile* ParameterComponent::getOutputSurface(const int32_t key) { return ((SurfaceParameter*)getOutputParameter(key, OperationParametersEnum::SURFACE))->m_parameter.getPointer(); } VolumeFile* ParameterComponent::getOutputVolume(const int32_t key) { return ((VolumeParameter*)getOutputParameter(key, OperationParametersEnum::VOLUME))->m_parameter.getPointer(); } MetricFile* ParameterComponent::getOutputMetric(const int32_t key) { return ((MetricParameter*)getOutputParameter(key, OperationParametersEnum::METRIC))->m_parameter.getPointer(); } connectome-workbench-1.2.3+git41-gc4c6c90/src/OperationsBase/OperationParameters.h000066400000000000000000000355211300200146000276250ustar00rootroot00000000000000#ifndef __OPERATION_PARAMETERS_H__ #define __OPERATION_PARAMETERS_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 "AString.h" #include #include "stdint.h" #include "CaretPointer.h" #include "OperationParametersEnum.h" namespace caret { class BorderFile; class CiftiFile; class FociFile; class LabelFile; class MetricFile; class SurfaceFile; class VolumeFile; struct OptionalParameter; struct RepeatableOption; struct AbstractParameter { int32_t m_key;//identifies this parameter uniquely for this algorithm AString m_shortName, m_description; bool m_operationUsed;//check if the operation called get...() for this parameter virtual OperationParametersEnum::Enum getType() = 0; virtual AbstractParameter* cloneAbstractParameter() = 0; AbstractParameter(int32_t key, const AString& shortName, const AString& description) : m_key(key), m_shortName(shortName), m_description(description), m_operationUsed(false) { }; virtual ~AbstractParameter(); }; struct ParameterComponent {//sadly, inheriting from a friend class doesn't give you access to private members, so these are entirely public so parsers can use them std::vector m_paramList;//mandatory arguments std::vector m_outputList;//should this be a different type? input and output parameters are very similar, just pointers to files std::vector m_optionList;//optional arguments std::vector m_repeatableOptions;//repeatable options ///constructor ParameterComponent(); ///destructor virtual ~ParameterComponent(); ///copy constructor so RepeatableOption can copy its template to a new instance ParameterComponent(const ParameterComponent& rhs); //convenience methods for algorithms to use to easily specify parameters ///add a parameter to get next item as a string void addStringParameter(const int32_t key, const AString& name, const AString& description); ///get a string with a key const AString& getString(const int32_t key); ///add a parameter to get next item as a string void addBooleanParameter(const int32_t key, const AString& name, const AString& description); ///get a string with a key bool getBoolean(const int32_t key); ///add a parameter to get next item as an int32 void addIntegerParameter(const int32_t key, const AString& name, const AString& description); ///get an integer with a key int64_t getInteger(const int32_t key); ///add a parameter to get next item as a double void addDoubleParameter(const int32_t key, const AString& name, const AString& description); ///get a double with a key double getDouble(const int32_t key); ///add a parameter to get next item as a surface void addSurfaceParameter(const int32_t key, const AString& name, const AString& description); ///get a surface with a key SurfaceFile* getSurface(const int32_t key); ///add a parameter to get next item as a volume void addVolumeParameter(const int32_t key, const AString& name, const AString& description); ///get a volume with a key VolumeFile* getVolume(const int32_t key); ///add a parameter to get next item as a functional file (metric) void addMetricParameter(const int32_t key, const AString& name, const AString& description); ///get a metric with a key MetricFile* getMetric(const int32_t key); ///add a parameter to get next item as a label file void addLabelParameter(const int32_t key, const AString& name, const AString& description); ///get a label with a key LabelFile* getLabel(const int32_t key); ///add a parameter to get next item as a cifti file - TODO: make methods for different cifti types? void addCiftiParameter(const int32_t key, const AString& name, const AString& description); ///get a cifti with a key CiftiFile* getCifti(const int32_t key); ///add a parameter to get next item as a foci file void addFociParameter(const int32_t key, const AString& name, const AString& description); ///get a foci file with a key FociFile* getFoci(const int32_t key); ///add a parameter to get next item as a border file void addBorderParameter(const int32_t key, const AString& name, const AString& description); ///get a border file with a key BorderFile* getBorder(const int32_t key); ///add a parameter to get next item as a surface void addSurfaceOutputParameter(const int32_t key, const AString& name, const AString& description); ///get a surface with a key SurfaceFile* getOutputSurface(const int32_t key); ///add a parameter to get next item as a volume void addVolumeOutputParameter(const int32_t key, const AString& name, const AString& description); ///get a volume with a key VolumeFile* getOutputVolume(const int32_t key); ///add a parameter to get next item as a functional file (metric) void addMetricOutputParameter(const int32_t key, const AString& name, const AString& description); ///get a metric with a key MetricFile* getOutputMetric(const int32_t key); ///add a parameter to get next item as a label file void addLabelOutputParameter(const int32_t key, const AString& name, const AString& description); ///get a label with a key LabelFile* getOutputLabel(const int32_t key); ///add a parameter to get next item as a cifti file - TODO: make methods for different cifti types? void addCiftiOutputParameter(const int32_t key, const AString& name, const AString& description); ///get a cifti with a key CiftiFile* getOutputCifti(const int32_t key); ///add a parameter to get next item as a foci file void addFociOutputParameter(const int32_t key, const AString& name, const AString& description); ///get a foci file with a key FociFile* getOutputFoci(const int32_t key); ///add a parameter to get next item as a border file void addBorderOutputParameter(const int32_t key, const AString& name, const AString& description); ///get a border file with a key BorderFile* getOutputBorder(const int32_t key); ///convenience method to create, add, and return an optional parameter OptionalParameter* createOptionalParameter(const int32_t key, const AString& optionSwitch, const AString& description); ///convenience method to create, add, and return an optional parameter ParameterComponent* createRepeatableParameter(const int32_t key, const AString& optionSwitch, const AString& description); ///return pointer to an input parameter AbstractParameter* getInputParameter(const int32_t key, const OperationParametersEnum::Enum type); ///return pointer to an output AbstractParameter* getOutputParameter(const int32_t key, const OperationParametersEnum::Enum type); ///return pointer to an option OptionalParameter* getOptionalParameter(const int32_t key); ///return instances of a repeatable option const std::vector* getRepeatableParameterInstances(const int32_t key); ///functions to check for key/type uniqueness - used only in asserts bool checkUniqueInput(const int32_t& key, const OperationParametersEnum::Enum& type); bool checkUniqueOutput(const int32_t& key, const OperationParametersEnum::Enum& type); bool checkUniqueOption(const int32_t& key); bool checkUniqueRepeatable(const int32_t& key); ///helper for checking that all parameters have been checked by the operation, returns warning strings std::vector findUncheckedParams(const AString& contextString) const; }; struct OptionalParameter : public ParameterComponent { int32_t m_key;//uniquely identifies this option AString m_optionSwitch, m_description; bool m_present;//to be filled by parser bool m_operationUsed;//check if the operation called get...() for this parameter OptionalParameter(const OptionalParameter& rhs) ://copy constructor is used by cloning in RepeatableParameter ParameterComponent(rhs), m_key(rhs.m_key), m_optionSwitch(rhs.m_optionSwitch), m_description(rhs.m_description), m_present(false), m_operationUsed(false) { } OptionalParameter(int32_t key, const AString& optionSwitch, const AString& description) : m_key(key), m_optionSwitch(optionSwitch), m_description(description), m_present(false), m_operationUsed(false) { } private: OptionalParameter();//no default construction }; struct RepeatableOption { int32_t m_key;//uniquely identifies this option AString m_optionSwitch, m_description; ParameterComponent m_template; bool m_operationUsed;//check if the operation called get...() for this parameter std::vector m_instances;//to be filled by parser RepeatableOption(const RepeatableOption& rhs) : m_key(rhs.m_key), m_optionSwitch(rhs.m_optionSwitch), m_description(rhs.m_description), m_template(rhs.m_template), m_operationUsed(false) { } RepeatableOption(int32_t key, const AString& optionSwitch, const AString& description) : m_key(key), m_optionSwitch(optionSwitch), m_description(description), m_operationUsed(false) { } ~RepeatableOption(); }; struct OperationParameters : public ParameterComponent { AString m_helpText;//to be formatted by the parser object for display in terminal or modal window ///constructor OperationParameters(); ///set the help text of the algorithm - you DO NOT need to add newlines within paragraphs or list the parameters, or give a description of each parameter! describe ONLY what it does, plus any quirks void setHelpText(const AString& textIn); ///get the unformatted help text, without command or arguments descriptions, to be formatted by the argument parser AString& getHelpText(); }; //templates for the common cases template struct PointerTemplateParameter : public AbstractParameter { virtual OperationParametersEnum::Enum getType() { return TYPE; } virtual AbstractParameter* cloneAbstractParameter() { AbstractParameter* ret = new PointerTemplateParameter(m_key, m_shortName, m_description); return ret; } CaretPointer m_parameter;//so the GUI parser and the commandline parser don't need to do different things to delete the parameter info PointerTemplateParameter(const int32_t key, const AString& shortName, const AString& description) : AbstractParameter(key, shortName, description) {//CaretPointer self-initializes to NULL, so don't need to do anything } }; template struct PrimitiveTemplateParameter : public AbstractParameter { virtual OperationParametersEnum::Enum getType() { return TYPE; } T m_parameter; virtual AbstractParameter* cloneAbstractParameter() { PrimitiveTemplateParameter* ret = new PrimitiveTemplateParameter(m_key, m_shortName, m_description); ret->m_parameter = 0; return ret; } PrimitiveTemplateParameter(const int32_t key, const AString& shortName, const AString& description) : AbstractParameter(key, shortName, description) { m_parameter = 0; } }; struct StringParameter : public AbstractParameter { virtual OperationParametersEnum::Enum getType() { return OperationParametersEnum::STRING; } virtual AbstractParameter* cloneAbstractParameter() { AbstractParameter* ret = new StringParameter(m_key, m_shortName, m_description); return ret; } AString m_parameter; StringParameter(int32_t key, const AString& shortName, const AString& description) : AbstractParameter(key, shortName, description) {//AString self-initializes to "", so don't need to do anything } }; //some friendlier names typedef PointerTemplateParameter SurfaceParameter; typedef PointerTemplateParameter VolumeParameter; typedef PointerTemplateParameter MetricParameter; typedef PointerTemplateParameter LabelParameter; typedef PointerTemplateParameter CiftiParameter; typedef PointerTemplateParameter FociParameter; typedef PointerTemplateParameter BorderParameter; typedef PrimitiveTemplateParameter DoubleParameter; typedef PrimitiveTemplateParameter IntegerParameter; typedef PrimitiveTemplateParameter BooleanParameter; } #endif //__OPERATION_PARAMETERS_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/OperationsBase/OperationParametersEnum.cxx000066400000000000000000000257671300200146000310400ustar00rootroot00000000000000 /*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 "OperationParametersEnum.h" #include "CaretAssert.h" using namespace caret; std::vector OperationParametersEnum::enumData; bool OperationParametersEnum::initializedFlag = false; /** * \class AlgorithmParametersEnum * \brief enum for parameter types * * enum for parameter types */ /** * Constructor. * * @param enumValue * An enumerated value. * @param integerCode * Integer code for this enumerated value. * * @param name * Name of enumerated value. * * @param guiName * User-friendly name for use in user-interface. */ OperationParametersEnum::OperationParametersEnum(const Enum enumValue, const int32_t integerCode, const AString& name, const AString& guiName) { this->enumValue = enumValue; this->integerCode = integerCode; this->name = name; this->guiName = guiName; } /** * Destructor. */ OperationParametersEnum::~OperationParametersEnum() { } /** * Initialize the enumerated metadata. */ void OperationParametersEnum::initialize() { if (initializedFlag) { return; } initializedFlag = true; enumData.push_back(OperationParametersEnum(SURFACE, 0, "Surface File", "Surface")); enumData.push_back(OperationParametersEnum(VOLUME, 1, "Volume File", "Volume")); enumData.push_back(OperationParametersEnum(METRIC, 2, "Metric File", "Metric")); enumData.push_back(OperationParametersEnum(LABEL, 3, "Label File", "Label")); enumData.push_back(OperationParametersEnum(CIFTI, 4, "Cifti File", "Cifti")); enumData.push_back(OperationParametersEnum(FOCI, 5, "Foci File", "Foci File")); enumData.push_back(OperationParametersEnum(BORDER, 6, "Border File", "Border File")); enumData.push_back(OperationParametersEnum(DOUBLE, 7, "Floating Point", "Floating Point")); enumData.push_back(OperationParametersEnum(INT, 8, "Integer", "Integer")); enumData.push_back(OperationParametersEnum(STRING, 9, "String", "String")); enumData.push_back(OperationParametersEnum(BOOL, 10, "Boolean", "Boolean")); } /** * 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 OperationParametersEnum* OperationParametersEnum::findData(const Enum enumValue) { if (initializedFlag == false) initialize(); size_t num = enumData.size(); for (size_t i = 0; i < num; i++) { const OperationParametersEnum* 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 OperationParametersEnum::toName(Enum enumValue) { if (initializedFlag == false) initialize(); const OperationParametersEnum* 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. */ OperationParametersEnum::Enum OperationParametersEnum::fromName(const AString& name, bool* isValidOut) { if (initializedFlag == false) initialize(); bool validFlag = false; Enum enumValue = SURFACE; for (std::vector::iterator iter = enumData.begin(); iter != enumData.end(); iter++) { const OperationParametersEnum& d = *iter; if (d.name == name) { enumValue = d.enumValue; validFlag = true; break; } } if (isValidOut != 0) { *isValidOut = validFlag; } else { CaretAssertMessage(0, AString("Name " + name + "failed to match enumerated value for type AlgorithmParametersEnum")); } return enumValue; } /** * Get a GUI string representation of the enumerated type. * @param enumValue * Enumerated value. * @return * String representing enumerated value. */ AString OperationParametersEnum::toGuiName(Enum enumValue) { if (initializedFlag == false) initialize(); const OperationParametersEnum* 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. */ OperationParametersEnum::Enum OperationParametersEnum::fromGuiName(const AString& guiName, bool* isValidOut) { if (initializedFlag == false) initialize(); bool validFlag = false; Enum enumValue = SURFACE; for (std::vector::iterator iter = enumData.begin(); iter != enumData.end(); iter++) { const OperationParametersEnum& d = *iter; if (d.guiName == guiName) { enumValue = d.enumValue; validFlag = true; break; } } if (isValidOut != 0) { *isValidOut = validFlag; } else { CaretAssertMessage(0, AString("guiName " + guiName + "failed to match enumerated value for type AlgorithmParametersEnum")); } return enumValue; } /** * Get the integer code for a data type. * * @return * Integer code for data type. */ int32_t OperationParametersEnum::toIntegerCode(Enum enumValue) { if (initializedFlag == false) initialize(); const OperationParametersEnum* 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. */ OperationParametersEnum::Enum OperationParametersEnum::fromIntegerCode(const int32_t integerCode, bool* isValidOut) { if (initializedFlag == false) initialize(); bool validFlag = false; Enum enumValue = SURFACE; for (std::vector::iterator iter = enumData.begin(); iter != enumData.end(); iter++) { const OperationParametersEnum& enumInstance = *iter; if (enumInstance.integerCode == integerCode) { enumValue = enumInstance.enumValue; validFlag = true; break; } } if (isValidOut != 0) { *isValidOut = validFlag; } else { CaretAssertMessage(0, AString("Integer code " + AString::number(integerCode) + "failed to match enumerated value for type AlgorithmParametersEnum")); } 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 OperationParametersEnum::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 OperationParametersEnum::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(OperationParametersEnum::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 OperationParametersEnum::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(OperationParametersEnum::toGuiName(iter->enumValue)); } if (isSorted) { std::sort(allGuiNames.begin(), allGuiNames.end()); } } connectome-workbench-1.2.3+git41-gc4c6c90/src/OperationsBase/OperationParametersEnum.h000066400000000000000000000055701300200146000304530ustar00rootroot00000000000000#ifndef __OPERATION_PARAMETERS_ENUM__H_ #define __OPERATION_PARAMETERS_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 OperationParametersEnum { public: /** * Enumerated values. */ enum Enum { SURFACE, VOLUME, METRIC, LABEL, CIFTI, FOCI, BORDER, DOUBLE, INT, STRING, BOOL }; ~OperationParametersEnum(); 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: OperationParametersEnum(const Enum enumValue, const int32_t integerCode, const AString& name, const AString& guiName); static const OperationParametersEnum* 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; /** 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; }; } // namespace #endif //__OPERATION_PARAMETERS_ENUM__H_ connectome-workbench-1.2.3+git41-gc4c6c90/src/Palette/000077500000000000000000000000001300200146000221425ustar00rootroot00000000000000connectome-workbench-1.2.3+git41-gc4c6c90/src/Palette/CMakeLists.txt000066400000000000000000000014651300200146000247100ustar00rootroot00000000000000# # The NIFTI Project # project (Palette) # # Need XML from Qt # SET(QT_DONT_USE_QTGUI) # # Add QT for includes # INCLUDE (${QT_USE_FILE}) # # Create the NIFTI library # ADD_LIBRARY(Palette Palette.h PaletteColorBarValuesModeEnum.h PaletteColorMapping.h PaletteColorMappingSaxReader.h PaletteColorMappingXmlElements.h PaletteEnums.h PaletteNormalizationModeEnum.h PaletteScalarAndColor.h PaletteThresholdRangeModeEnum.h Palette.cxx PaletteColorBarValuesModeEnum.cxx PaletteColorMapping.cxx PaletteColorMappingSaxReader.cxx PaletteEnums.cxx PaletteNormalizationModeEnum.cxx PaletteScalarAndColor.cxx PaletteThresholdRangeModeEnum.cxx ) # # Find Headers # INCLUDE_DIRECTORIES( ${CMAKE_SOURCE_DIR}/Annotations ${CMAKE_SOURCE_DIR}/Common ${CMAKE_SOURCE_DIR}/Palette ${CMAKE_SOURCE_DIR}/Scenes ${CMAKE_SOURCE_DIR}/Xml ) connectome-workbench-1.2.3+git41-gc4c6c90/src/Palette/Palette.cxx000066400000000000000000000403201300200146000242630ustar00rootroot00000000000000/*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 "CaretAssert.h" #define __PALETTE_DEFINE__ #include "Palette.h" #undef __PALETTE_DEFINE__ #include "PaletteScalarAndColor.h" using namespace caret; /** * Constructor. * */ Palette::Palette() : CaretObject() { this->initializeMembersPalette(); } /** * Destructor */ Palette::~Palette() { uint64_t num = this->paletteScalars.size(); for (uint64_t i = 0; i < num; i++) { delete this->paletteScalars[i]; this->paletteScalars[i] = NULL; } this->paletteScalars.clear(); } /** * Copy Constructor * @param Object that is copied. */ Palette::Palette(const Palette& o) : CaretObject(o), TracksModificationInterface() { this->initializeMembersPalette(); this->copyHelper(o); } /** * Assignment operator. */ Palette& Palette::operator=(const Palette& o) { if (this != &o) { CaretObject::operator=(o); this->copyHelper(o); }; return *this; } /** * Helps with copy constructor and assignment operator. */ void Palette::copyHelper(const Palette& o) { this->name = o.name; this->paletteScalars.clear(); uint64_t num = o.paletteScalars.size(); for (uint64_t i = 0; i < num; i++) { this->paletteScalars.push_back(new PaletteScalarAndColor(*o.paletteScalars[i])); } } void Palette::initializeMembersPalette() { this->modifiedFlag = false; this->name = ""; } /** * Get string representation for debugging. * * @return String containing info. * */ AString Palette::toString() const { AString s; s += "[name=" + this->name + ", "; uint64_t num = this->paletteScalars.size(); for (uint64_t i = 0; i < num; i++) { if (i > 0) s += ","; s += this->paletteScalars[i]->toString(); } s += "]"; return s; } /** * Get the name of the palette. * * @return - name of palette. * */ AString Palette::getName() const { return this->name; } /** * Set the name of this palette. * * @param name - new value for name. * */ void Palette::setName(const AString& name) { if (this->name != name) { this->name = name; this->setModified(); } } /** * Add a scalar and color to the palette. * * @param scalar - scalar value. * @param colorName - color name. * */ void Palette::addScalarAndColor(const float scalar, const AString& colorName) { CaretAssert(paletteScalars.size() == 0 || scalar <= paletteScalars.back()->getScalar());//die in debug if a palette is constructed incorrectly this->paletteScalars.push_back(new PaletteScalarAndColor(scalar, colorName)); this->setModified(); } /** * Insert a scalar/color pair. * * @param psac - item to add. * @param insertAfterIndex - Insert after this index. * */ void Palette::insertScalarAndColor( const PaletteScalarAndColor& psac, const int32_t insertAfterIndex) { CaretAssertVectorIndex(this->paletteScalars, insertAfterIndex); this->paletteScalars.insert(this->paletteScalars.begin() + insertAfterIndex, new PaletteScalarAndColor(psac)); this->setModified(); } /** * Remove the scalar and color at index. * * @param index - index of scalar and color to remove. * */ void Palette::removeScalarAndColor(const int32_t indx) { CaretAssertVectorIndex(this->paletteScalars, indx); this->paletteScalars.erase(this->paletteScalars.begin() + indx); this->setModified(); } /** * * Get the minimum and maximum scalar values in this palette. * * @return two-dimensional float array with min and max values. * */ void Palette::getMinMax(float& minOut, float& maxOut) const { minOut = std::numeric_limits::max(); maxOut = -std::numeric_limits::max(); uint64_t num = this->paletteScalars.size(); for (uint64_t i = 0; i < num; i++) { const float f = this->paletteScalars[i]->getScalar(); if (f < minOut) minOut = f; if (f > maxOut) maxOut = f; } } ///** // * Get the RGBA (4) colors in the range of zero to one. // * // * @param scalar - scalar for which color is sought. // * @param interpolateColorFlag - interpolate the color between scalars. // * @return Array of 4 containing color components ranging zero to one. // * // */ //void //Palette::getPaletteColor( // const float scalarIn, // const bool interpolateColorFlagIn, // float rgbaOut[4]) const //{ // rgbaOut[0] = 0.0f; // rgbaOut[1] = 0.0f; // rgbaOut[2] = 0.0f; // rgbaOut[3] = 1.0f; // // bool interpolateColorFlag = interpolateColorFlagIn; // // float scalar = scalarIn; // if (scalar < -1.0) scalar = -1.0; // if (scalar > 1.0) scalar = 1.0; // // int numScalarColors = this->getNumberOfScalarsAndColors(); // if (numScalarColors > 0) { // // int paletteIndex = -1; // if (numScalarColors == 1) { // paletteIndex = 0; // interpolateColorFlag = false; // } // else { // if (scalar >= this->getScalarAndColor(0)->getScalar()) { // paletteIndex = 0; // interpolateColorFlag = false; // } // else if (scalar <= // this->getScalarAndColor(numScalarColors - 1)->getScalar()) { // paletteIndex = numScalarColors - 1; // interpolateColorFlag = false; // } // else { // for (int i = 1; i < numScalarColors; i++) { // const PaletteScalarAndColor* psac = this->getScalarAndColor(i); // if (scalar > psac->getScalar()) { // paletteIndex = i - 1; // break; // } // } // // /* // * 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]); // } // } // } // else if (psac->isNoneColor()) { // rgbaOut[3] = 0.0f; // } // } // } //} /** * Get the RGBA (4) colors in the range of zero to one. * * @param scalar - scalar for which color is sought. * @param interpolateColorFlag - interpolate the color between scalars. * @return Array of 4 containing color components ranging zero to one. * */ void Palette::getPaletteColor( const float scalarIn, const bool interpolateColorFlagIn, float rgbaOut[4]) const { /* * When the number of colors in a palette is small, the * binary search algorithm may be slower than the linear * algorithm. It is moderately faster for large palettes * such as those from FSL with 256 colors. * * TSC: The FSL palettes now just interpolate between two colors, * and don't have 256 entries. Could reorganize the palette * storage to be a vector of structs, rather than of pointers, * to remove cost of indirection. * Also notable is that typical volume files color faster with * methods that color near-zero faster than other values. * I compared performance on the simplified palettes with a simplified * binary search (that doesn't test against the next value after * each guess) and with an interpolation search, with no benefit. * Another possibility would be to prebuild a lookup from rounded * normalized value to min and max possible reference color to * search between (so, values between 0 and 0.01 are always between * the middle and previous point, same for 0.01 to 0.02, etc). This * could take some substantial reorganization of palette code. * For now, only activate binary search when number of points is large. * * The linear search could be improved by starting at the bottom * when the data value is negative. * * N Log2(N) * 1 0 * 2 1 * 3 1.6 * 4 2 * 5 2.3 * 6 2.6 * 7 2.8 * 8 3 * 9 3.2 */ int numScalarColors = this->getNumberOfScalarsAndColors(); const bool doBinarySearchFlag = numScalarColors > 50; rgbaOut[0] = 0.0f; rgbaOut[1] = 0.0f; rgbaOut[2] = 0.0f; rgbaOut[3] = 1.0f; bool interpolateColorFlag = interpolateColorFlagIn; float scalar = scalarIn; if (scalar < -1.0) scalar = -1.0; if (scalar > 1.0) scalar = 1.0; if (numScalarColors > 0) { int32_t highDataIndex = 0; int32_t lowDataIndex = numScalarColors - 1; int32_t paletteIndex = -1; if (numScalarColors == 1) { paletteIndex = 0; interpolateColorFlag = false; } else { if (scalar >= this->getScalarAndColor(highDataIndex)->getScalar()) { paletteIndex = 0; interpolateColorFlag = false; } else if (scalar <= this->getScalarAndColor(lowDataIndex)->getScalar()) { paletteIndex = numScalarColors - 1; interpolateColorFlag = false; } else if (numScalarColors == 2) { paletteIndex = 0; interpolateColorFlag = true; } else { if (doBinarySearchFlag) { /* * Binary Search * NOTE: The palette orders the scalars in DESCENDING ORDER */ int32_t binaryPaletteIndex = -1; const int32_t maximumIndex = numScalarColors - 1; bool loopFlag = true; while (loopFlag) { int32_t midIndex = (lowDataIndex + highDataIndex) / 2; if (midIndex <= 0) { binaryPaletteIndex = 0; loopFlag = false; } else if (midIndex >= maximumIndex) { binaryPaletteIndex = maximumIndex; loopFlag = false; } else { const float midScalar = this->getScalarAndColor(midIndex)->getScalar(); if (scalar <= midScalar) { const float nextScalar = this->getScalarAndColor(midIndex + 1)->getScalar(); if (scalar > nextScalar) { binaryPaletteIndex = midIndex; loopFlag = false; } else { highDataIndex = midIndex; } } else { lowDataIndex = midIndex; } } } paletteIndex = binaryPaletteIndex; } else { /* * Linear Search */ for (int32_t i = 1; i < numScalarColors; i++) { const PaletteScalarAndColor* psac = this->getScalarAndColor(i); if (scalar > psac->getScalar()) { paletteIndex = i - 1; break; } } } // 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]); } } } else if (psac->isNoneColor()) { rgbaOut[3] = 0.0f; } } } } /** * Set this object has been modified. * */ void Palette::setModified() { this->modifiedFlag = true; } /** * Set this object as not modified. Object should also * clear the modification status of its children. * */ void Palette::clearModified() { this->modifiedFlag = false; } /** * Get the modification status. Returns true if this object or * any of its children have been modified. * @return - The modification status. * */ bool Palette::isModified() const { return this->modifiedFlag; } connectome-workbench-1.2.3+git41-gc4c6c90/src/Palette/Palette.h000066400000000000000000000101411300200146000237060ustar00rootroot00000000000000#ifndef __PALETTE_H__ #define __PALETTE_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 "CaretAssert.h" #include "CaretObject.h" #include "TracksModificationInterface.h" namespace caret { class PaletteScalarAndColor; /** * A color palette. */ class Palette : public CaretObject, TracksModificationInterface { public: Palette(); Palette(const Palette& p); Palette& operator=(const Palette& o); virtual ~Palette(); private: void copyHelper(const Palette& o); void initializeMembersPalette(); public: AString toString() const; AString getName() const; void setName(const AString& name); /** * Get the number of scalars and colors. * * @return - number of scalars and colors. * */ inline int32_t getNumberOfScalarsAndColors() const { return this->paletteScalars.size(); } /** * Get a scalar and color for the specified index. * * @param index - index of scalar and color. * @return Reference to item at index or null if invalid index. * */ inline PaletteScalarAndColor* getScalarAndColor(const int32_t indx) const { CaretAssertVectorIndex(this->paletteScalars, indx); return this->paletteScalars[indx]; } void addScalarAndColor(const float scalar, const AString& colorName); void insertScalarAndColor( const PaletteScalarAndColor& psac, const int32_t insertAfterIndex); void removeScalarAndColor(const int32_t index); void getMinMax(float& minOut, float& maxOut) const; void getPaletteColor(const float scalar, const bool interpolateColorFlag, float rgbaOut[4]) const; void setModified(); void clearModified(); bool isModified() const; public: /**Name of gray interpolate palette */ static const AString GRAY_INTERP_PALETTE_NAME; /**Name of gray interpolate palette for positive data */ static const AString GRAY_INTERP_POSITIVE_PALETTE_NAME; /**"none" color name. */ static const AString NONE_COLOR_NAME; /** "ROY-BIG-BL" palette */ static const AString ROY_BIG_BL_PALETTE_NAME; private: /**has this object been modified. (DO NOT CLONE) */ bool modifiedFlag; /**Name of the palette. */ AString name; /**The scalars in the palette. */ std::vector paletteScalars; }; #ifdef __PALETTE_DEFINE__ const AString Palette::GRAY_INTERP_PALETTE_NAME = "Gray_Interp"; const AString Palette::GRAY_INTERP_POSITIVE_PALETTE_NAME = "Gray_Interp_Positive"; //const AString Palette::NONE_COLOR_NAME = "none"; const AString Palette::ROY_BIG_BL_PALETTE_NAME = "ROY-BIG-BL"; #endif // __PALETTE_DEFINE__ } // namespace #endif // __PALETTE_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Palette/PaletteColorBarValuesModeEnum.cxx000066400000000000000000000261441300200146000305310ustar00rootroot00000000000000 /*LICENSE_START*/ /* * Copyright (C) 2015 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_COLOR_BAR_VALUES_MODE_ENUM_DECLARE__ #include "PaletteColorBarValuesModeEnum.h" #undef __PALETTE_COLOR_BAR_VALUES_MODE_ENUM_DECLARE__ #include "CaretAssert.h" using namespace caret; /** * \class caret::PaletteColorBarValuesModeEnum * \brief Enumerated type for palette color values 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_paletteColorBarValuesModeEnumComboBox; * * Declare a slot that is called when user changes selection * private slots: * void paletteColorBarValuesModeEnumComboBoxItemActivated(); * * Implementation File (.cxx) * Include the header files * #include "EnumComboBoxTemplate.h" * #include "PaletteColorBarValuesModeEnum.h" * * Instatiate: * m_paletteColorBarValuesModeEnumComboBox = new EnumComboBoxTemplate(this); * m_paletteColorBarValuesModeEnumComboBox->setup(); * * Get notified when the user changes the selection: * QObject::connect(m_paletteColorBarValuesModeEnumComboBox, SIGNAL(itemActivated()), * this, SLOT(paletteColorBarValuesModeEnumComboBoxItemActivated())); * * Update the selection: * m_paletteColorBarValuesModeEnumComboBox->setSelectedItem(NEW_VALUE); * * Read the selection: * const PaletteColorBarValuesModeEnum::Enum VARIABLE = m_paletteColorBarValuesModeEnumComboBox->getSelectedItem(); * */ /** * Constructor. * * @param enumValue * An enumerated value. * @param name * Name of enumerated value. * * @param guiName * User-friendly name for use in user-interface. */ PaletteColorBarValuesModeEnum::PaletteColorBarValuesModeEnum(const Enum enumValue, const AString& name, const AString& guiName) { this->enumValue = enumValue; this->integerCode = integerCodeCounter++; this->name = name; this->guiName = guiName; } /** * Destructor. */ PaletteColorBarValuesModeEnum::~PaletteColorBarValuesModeEnum() { } /** * Initialize the enumerated metadata. */ void PaletteColorBarValuesModeEnum::initialize() { if (initializedFlag) { return; } initializedFlag = true; enumData.push_back(PaletteColorBarValuesModeEnum(DATA, "DATA", "Data")); enumData.push_back(PaletteColorBarValuesModeEnum(PERCENTILE, "PERCENTILE", "Percentile")); enumData.push_back(PaletteColorBarValuesModeEnum(SIGN_ONLY, "SIGN_ONLY", "Sign Only")); } /** * 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 PaletteColorBarValuesModeEnum* PaletteColorBarValuesModeEnum::findData(const Enum enumValue) { if (initializedFlag == false) initialize(); size_t num = enumData.size(); for (size_t i = 0; i < num; i++) { const PaletteColorBarValuesModeEnum* 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 PaletteColorBarValuesModeEnum::toName(Enum enumValue) { if (initializedFlag == false) initialize(); const PaletteColorBarValuesModeEnum* 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. */ PaletteColorBarValuesModeEnum::Enum PaletteColorBarValuesModeEnum::fromName(const AString& name, bool* isValidOut) { if (initializedFlag == false) initialize(); bool validFlag = false; Enum enumValue = PaletteColorBarValuesModeEnum::enumData[0].enumValue; for (std::vector::iterator iter = enumData.begin(); iter != enumData.end(); iter++) { const PaletteColorBarValuesModeEnum& 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 PaletteColorBarValuesModeEnum")); } return enumValue; } /** * Get a GUI string representation of the enumerated type. * @param enumValue * Enumerated value. * @return * String representing enumerated value. */ AString PaletteColorBarValuesModeEnum::toGuiName(Enum enumValue) { if (initializedFlag == false) initialize(); const PaletteColorBarValuesModeEnum* 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. */ PaletteColorBarValuesModeEnum::Enum PaletteColorBarValuesModeEnum::fromGuiName(const AString& guiName, bool* isValidOut) { if (initializedFlag == false) initialize(); bool validFlag = false; Enum enumValue = PaletteColorBarValuesModeEnum::enumData[0].enumValue; for (std::vector::iterator iter = enumData.begin(); iter != enumData.end(); iter++) { const PaletteColorBarValuesModeEnum& 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 PaletteColorBarValuesModeEnum")); } return enumValue; } /** * Get the integer code for a data type. * * @return * Integer code for data type. */ int32_t PaletteColorBarValuesModeEnum::toIntegerCode(Enum enumValue) { if (initializedFlag == false) initialize(); const PaletteColorBarValuesModeEnum* 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. */ PaletteColorBarValuesModeEnum::Enum PaletteColorBarValuesModeEnum::fromIntegerCode(const int32_t integerCode, bool* isValidOut) { if (initializedFlag == false) initialize(); bool validFlag = false; Enum enumValue = PaletteColorBarValuesModeEnum::enumData[0].enumValue; for (std::vector::iterator iter = enumData.begin(); iter != enumData.end(); iter++) { const PaletteColorBarValuesModeEnum& 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 PaletteColorBarValuesModeEnum")); } 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 PaletteColorBarValuesModeEnum::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 PaletteColorBarValuesModeEnum::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(PaletteColorBarValuesModeEnum::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 PaletteColorBarValuesModeEnum::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(PaletteColorBarValuesModeEnum::toGuiName(iter->enumValue)); } if (isSorted) { std::sort(allGuiNames.begin(), allGuiNames.end()); } } connectome-workbench-1.2.3+git41-gc4c6c90/src/Palette/PaletteColorBarValuesModeEnum.h000066400000000000000000000063221300200146000301520ustar00rootroot00000000000000#ifndef __PALETTE_COLOR_BAR_VALUES_MODE_ENUM_H__ #define __PALETTE_COLOR_BAR_VALUES_MODE_ENUM_H__ /*LICENSE_START*/ /* * Copyright (C) 2015 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 PaletteColorBarValuesModeEnum { public: /** * Enumerated values. */ enum Enum { /** */ DATA, /** */ PERCENTILE, /** */ SIGN_ONLY }; ~PaletteColorBarValuesModeEnum(); 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: PaletteColorBarValuesModeEnum(const Enum enumValue, const AString& name, const AString& guiName); static const PaletteColorBarValuesModeEnum* 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_COLOR_BAR_VALUES_MODE_ENUM_DECLARE__ std::vector PaletteColorBarValuesModeEnum::enumData; bool PaletteColorBarValuesModeEnum::initializedFlag = false; int32_t PaletteColorBarValuesModeEnum::integerCodeCounter = 0; #endif // __PALETTE_COLOR_BAR_VALUES_MODE_ENUM_DECLARE__ } // namespace #endif //__PALETTE_COLOR_BAR_VALUES_MODE_ENUM_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Palette/PaletteColorMapping.cxx000066400000000000000000002570401300200146000266070ustar00rootroot00000000000000/*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 "AnnotationColorBar.h" #include "AnnotationColorBarNumericText.h" #include "CaretLogger.h" #include "CaretOMP.h" #include "EventManager.h" #include "EventPaletteGetByName.h" #include "FastStatistics.h" #include "MathFunctions.h" #include "NumericTextFormatting.h" //because the ROY_BIG_BL palette name is a constant defined in Palette.h #include "Palette.h" #define __PALETTE_COLOR_MAPPING_DECLARE__ #include "PaletteColorMapping.h" #undef __PALETTE_COLOR_MAPPING_DECLARE__ #include "PaletteColorMappingSaxReader.h" #include "PaletteColorMappingXmlElements.h" #include "PaletteScalarAndColor.h" #include "XmlSaxParser.h" #include "XmlUtilities.h" #include "XmlWriter.h" #include using namespace caret; /** * Constructor. * */ PaletteColorMapping::PaletteColorMapping() : CaretObject() { this->initializeMembersPaletteColorMapping(); } /** * Destructor */ PaletteColorMapping::~PaletteColorMapping() { } /** * Copy Constructor * @param Object that is copied. */ PaletteColorMapping::PaletteColorMapping(const PaletteColorMapping& o) : CaretObject(o) { this->initializeMembersPaletteColorMapping(); this->copyHelper(o); } /** * Assignment operator. */ PaletteColorMapping& PaletteColorMapping::operator=(const PaletteColorMapping& o) { if (this != &o) { CaretObject::operator=(o); this->copyHelper(o); }; return *this; } /** * Copy the palette color mapping from the given palette * color mapping. * @param pcm * Color mapping that is copied to this. */ void PaletteColorMapping::copy(const PaletteColorMapping& pcm) { this->copyHelper(pcm); setModified(); } /** * Helps with copy constructor and assignment operator. */ void PaletteColorMapping::copyHelper(const PaletteColorMapping& pcm) { this->autoScalePercentageNegativeMaximum = pcm.autoScalePercentageNegativeMaximum; this->autoScalePercentageNegativeMinimum = pcm.autoScalePercentageNegativeMinimum; this->autoScalePercentagePositiveMaximum = pcm.autoScalePercentagePositiveMaximum; this->autoScalePercentagePositiveMinimum = pcm.autoScalePercentagePositiveMinimum; this->autoScaleAbsolutePercentageMaximum = pcm.autoScaleAbsolutePercentageMaximum; this->autoScaleAbsolutePercentageMinimum = pcm.autoScaleAbsolutePercentageMinimum; this->displayNegativeDataFlag = pcm.displayNegativeDataFlag; this->displayPositiveDataFlag = pcm.displayPositiveDataFlag; this->displayZeroDataFlag = pcm.displayZeroDataFlag; this->interpolatePaletteFlag = pcm.interpolatePaletteFlag; this->scaleMode = pcm.scaleMode; this->selectedPaletteName = pcm.selectedPaletteName; this->userScaleNegativeMaximum = pcm.userScaleNegativeMaximum; this->userScaleNegativeMinimum = pcm.userScaleNegativeMinimum; this->userScalePositiveMaximum = pcm.userScalePositiveMaximum; this->userScalePositiveMinimum = pcm.userScalePositiveMinimum; this->thresholdType = pcm.thresholdType; this->thresholdTest = pcm.thresholdTest; this->thresholdNormalMinimum = pcm.thresholdNormalMinimum; this->thresholdNormalMaximum = pcm.thresholdNormalMaximum; this->thresholdMappedMinimum= pcm.thresholdMappedMinimum; this->thresholdMappedMaximum = pcm.thresholdMappedMaximum; this->thresholdMappedAverageAreaMinimum = pcm.thresholdMappedAverageAreaMinimum; this->thresholdMappedAverageAreaMaximum = pcm.thresholdMappedAverageAreaMaximum; this->thresholdDataName = pcm.thresholdDataName; 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->colorBarValuesMode = pcm.colorBarValuesMode; this->showTickMarksSelected = pcm.showTickMarksSelected; this->clearModified(); } /** * Equality operator. * @param pcm * Palette color mapping compared to 'this' palette color mapping. * @return * True if their members are the same. */ bool PaletteColorMapping::operator==(const PaletteColorMapping& pcm) const { if ((this->autoScalePercentageNegativeMaximum == pcm.autoScalePercentageNegativeMaximum) && (this->autoScalePercentageNegativeMinimum == pcm.autoScalePercentageNegativeMinimum) && (this->autoScalePercentagePositiveMaximum == pcm.autoScalePercentagePositiveMaximum) && (this->autoScalePercentagePositiveMinimum == pcm.autoScalePercentagePositiveMinimum) && (this->autoScaleAbsolutePercentageMaximum == pcm.autoScaleAbsolutePercentageMaximum) && (this->autoScaleAbsolutePercentageMinimum == pcm.autoScaleAbsolutePercentageMinimum) && (this->displayNegativeDataFlag == pcm.displayNegativeDataFlag) && (this->displayPositiveDataFlag == pcm.displayPositiveDataFlag) && (this->displayZeroDataFlag == pcm.displayZeroDataFlag) && (this->interpolatePaletteFlag == pcm.interpolatePaletteFlag) && (this->scaleMode == pcm.scaleMode) && (this->selectedPaletteName == pcm.selectedPaletteName) && (this->userScaleNegativeMaximum == pcm.userScaleNegativeMaximum) && (this->userScaleNegativeMinimum == pcm.userScaleNegativeMinimum) && (this->userScalePositiveMaximum == pcm.userScalePositiveMaximum) && (this->userScalePositiveMinimum == pcm.userScalePositiveMinimum) && (this->thresholdType == pcm.thresholdType) && (this->thresholdTest == pcm.thresholdTest) && (this->thresholdNormalMinimum == pcm.thresholdNormalMinimum) && (this->thresholdNormalMaximum == pcm.thresholdNormalMaximum) && (this->thresholdMappedMinimum== pcm.thresholdMappedMinimum) && (this->thresholdMappedMaximum == pcm.thresholdMappedMaximum) && (this->thresholdMappedAverageAreaMinimum == pcm.thresholdMappedAverageAreaMinimum) && (this->thresholdMappedAverageAreaMaximum == pcm.thresholdMappedAverageAreaMaximum) && (this->thresholdDataName == pcm.thresholdDataName) && (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->colorBarValuesMode == pcm.colorBarValuesMode) && (this->showTickMarksSelected == pcm.showTickMarksSelected)) { return true; } return false; } void PaletteColorMapping::initializeMembersPaletteColorMapping() { this->scaleMode = PaletteScaleModeEnum::MODE_AUTO_SCALE_PERCENTAGE; this->autoScalePercentageNegativeMaximum = 98.0f; this->autoScalePercentageNegativeMinimum = 2.0f; this->autoScalePercentagePositiveMinimum = 2.0f; this->autoScalePercentagePositiveMaximum = 98.0f; this->autoScaleAbsolutePercentageMaximum = 98.0f; this->autoScaleAbsolutePercentageMinimum = 2.0f; this->userScaleNegativeMaximum = -100.0f; this->userScaleNegativeMinimum = 0.0f; this->userScalePositiveMinimum = 0.0f; this->userScalePositiveMaximum = 100.0f; this->selectedPaletteName = Palette::ROY_BIG_BL_PALETTE_NAME; this->interpolatePaletteFlag = true; this->displayPositiveDataFlag = true; this->displayZeroDataFlag = false; this->displayNegativeDataFlag = true; this->thresholdType = PaletteThresholdTypeEnum::THRESHOLD_TYPE_OFF; this->thresholdTest = PaletteThresholdTestEnum::THRESHOLD_TEST_SHOW_OUTSIDE; this->thresholdNormalMinimum = -1.0f; this->thresholdNormalMaximum = 1.0f; this->thresholdMappedMinimum = -1.0f; this->thresholdMappedMaximum = 1.0f; this->thresholdMappedAverageAreaMinimum = -1.0f; this->thresholdMappedAverageAreaMaximum = 1.0f; this->thresholdDataName = ""; this->thresholdShowFailureInGreen = false; this->thresholdRangeMode = PaletteThresholdRangeModeEnum::PALETTE_THRESHOLD_RANGE_MODE_MAP; this->thresholdNegMinPosMaxLinked = false; this->numericFormatMode = NumericFormatModeEnum::AUTO; this->precisionDigits = 2; this->numericSubdivisionCount = 0; this->modifiedFlag = false; this->colorBarValuesMode = PaletteColorBarValuesModeEnum::DATA; this->showTickMarksSelected = false; } /** * Write the object as XML. * @param xmlWriter - write to this-> * @throws XmlException - If an error occurs. * */ void PaletteColorMapping::writeAsXML(XmlWriter& xmlWriter) { XmlAttributes attributes; attributes.addAttribute( PaletteColorMappingXmlElements::XML_ATTRIBUTE_VERSION_NUMBER, PaletteColorMappingXmlElements::XML_VERSION_NUMBER); xmlWriter.writeStartElement( PaletteColorMappingXmlElements::XML_TAG_PALETTE_COLOR_MAPPING, attributes); xmlWriter.writeElementCharacters(PaletteColorMappingXmlElements::XML_TAG_SCALE_MODE, PaletteScaleModeEnum::toName(this->scaleMode)); float autoScaleValues[4] = { this->autoScalePercentageNegativeMaximum, this->autoScalePercentageNegativeMinimum, this->autoScalePercentagePositiveMinimum, this->autoScalePercentagePositiveMaximum }; xmlWriter.writeElementCharacters( PaletteColorMappingXmlElements::XML_TAG_AUTO_SCALE_PERCENTAGE_VALUES, autoScaleValues, 4); float autoScaleAbsolutePercentageValues[2] = { this->autoScaleAbsolutePercentageMinimum, this->autoScaleAbsolutePercentageMaximum }; xmlWriter.writeElementCharacters(PaletteColorMappingXmlElements::XML_TAG_AUTO_SCALE_ABSOLUTE_PERCENTAGE_VALUES, autoScaleAbsolutePercentageValues, 2); float userScaleValues[4] = { this->userScaleNegativeMaximum, this->userScaleNegativeMinimum, this->userScalePositiveMinimum, this->userScalePositiveMaximum }; xmlWriter.writeElementCharacters( PaletteColorMappingXmlElements::XML_TAG_USER_SCALE_VALUES, userScaleValues, 4); xmlWriter.writeElementCharacters(PaletteColorMappingXmlElements::XML_TAG_PALETTE_NAME, XmlUtilities::encodeXmlSpecialCharacters(this->selectedPaletteName)); xmlWriter.writeElementCharacters( PaletteColorMappingXmlElements::XML_TAG_INTERPOLATE, this->interpolatePaletteFlag); xmlWriter.writeElementCharacters( PaletteColorMappingXmlElements::XML_TAG_DISPLAY_POSITIVE, this->displayPositiveDataFlag); xmlWriter.writeElementCharacters( PaletteColorMappingXmlElements::XML_TAG_DISPLAY_ZERO, this->displayZeroDataFlag); xmlWriter.writeElementCharacters( PaletteColorMappingXmlElements::XML_TAG_DISPLAY_NEGATIVE, this->displayNegativeDataFlag); xmlWriter.writeElementCharacters( PaletteColorMappingXmlElements::XML_TAG_THRESHOLD_TEST, PaletteThresholdTestEnum::toName(this->thresholdTest)); xmlWriter.writeElementCharacters( PaletteColorMappingXmlElements::XML_TAG_THRESHOLD_TYPE, PaletteThresholdTypeEnum::toName(this->thresholdType)); xmlWriter.writeElementCharacters( PaletteColorMappingXmlElements::XML_TAG_THRESHOLD_FAILURE_IN_GREEN, this->thresholdShowFailureInGreen); float normalValues[2] = { this->thresholdNormalMinimum, this->thresholdNormalMaximum }; xmlWriter.writeElementCharacters( PaletteColorMappingXmlElements::XML_TAG_THRESHOLD_NORMAL_VALUES, normalValues, 2); float mappedValues[2] = { this->thresholdMappedMinimum, this->thresholdMappedMaximum }; xmlWriter.writeElementCharacters( PaletteColorMappingXmlElements::XML_TAG_THRESHOLD_MAPPED_VALUES, mappedValues, 2); float mappedAvgAreaValues[2] = { this->thresholdMappedAverageAreaMinimum, this->thresholdMappedAverageAreaMaximum }; xmlWriter.writeElementCharacters( PaletteColorMappingXmlElements::XML_TAG_THRESHOLD_MAPPED_AVG_AREA_VALUES, mappedAvgAreaValues, 2); xmlWriter.writeElementCharacters( PaletteColorMappingXmlElements::XML_TAG_THRESHOLD_DATA_NAME, XmlUtilities::encodeXmlSpecialCharacters(this->thresholdDataName)); xmlWriter.writeElementCharacters(PaletteColorMappingXmlElements::XML_TAG_THRESHOLD_RANGE_MODE, PaletteThresholdRangeModeEnum::toName(this->thresholdRangeMode)); xmlWriter.writeElementCharacters(PaletteColorMappingXmlElements::XML_TAG_THRESHOLD_NEG_MIN_POS_MAX_LINKED, this->thresholdNegMinPosMaxLinked); xmlWriter.writeElementCharacters(PaletteColorMappingXmlElements::XML_TAG_NUMERIC_FORMAT_MODE, NumericFormatModeEnum::toName(this->numericFormatMode)); xmlWriter.writeElementCharacters(PaletteColorMappingXmlElements::XML_TAG_PRECISION_DIGITS, this->precisionDigits); xmlWriter.writeElementCharacters(PaletteColorMappingXmlElements::XML_TAG_NUMERIC_SUBDIVISIONS, this->numericSubdivisionCount); xmlWriter.writeElementCharacters(PaletteColorMappingXmlElements::XML_TAG_COLOR_BAR_VALUES_MODE, PaletteColorBarValuesModeEnum::toName(this->colorBarValuesMode)); xmlWriter.writeElementCharacters(PaletteColorMappingXmlElements::XML_TAG_SHOW_TICK_MARKS, this->showTickMarksSelected); xmlWriter.writeEndElement(); } /** * Returns the XML representation of this object in a String. * * @return String containing XML. * @throws XmlException if an error occurs. * */ AString PaletteColorMapping::encodeInXML() { std::ostringstream str; XmlWriter xmlWriter(str); this->writeAsXML(xmlWriter); AString s = AString::fromStdString(str.str()); return s; } /** * Decode this object from a String containing XML. * @param xml - String containing XML. * @throws XmlException If an error occurs. * */ void PaletteColorMapping::decodeFromStringXML(const AString& xml) { PaletteColorMappingSaxReader saxReader(this); XmlSaxParser* parser = XmlSaxParser::createXmlParser(); try { parser->parseString(xml, &saxReader); } catch (XmlSaxParserException& e) { int lineNum = e.getLineNumber(); int colNum = e.getColumnNumber(); std::ostringstream str; str << "Parse Error while reading PaletteColorMapping XML"; if ((lineNum >= 0) && (colNum >= 0)) { str << " line/col (" << e.getLineNumber() << "/" << e.getColumnNumber() << ")"; } str << ": " << e.whatString().toStdString(); throw XmlException(AString::fromStdString(str.str())); } delete parser; } /** * Setup an annotation color bar with its color sections. * * @param statistics * Statistics of data for colorbar. * @param colorBar * The annotation colorbar that has its color sections set. */ void PaletteColorMapping::setupAnnotationColorBar(const FastStatistics* statistics, AnnotationColorBar* colorBar) { CaretAssert(statistics); CaretAssert(colorBar); 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; } /* * 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; } else if (isNegativeOnly) { xMaximum = 0.0; } /* * Always interpolate if the palette has only two colors */ bool interpolateColor = this->interpolatePaletteFlag; 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 ( ! this->displayNegativeDataFlag) { if (nextScalar < 0.0) { continue; } else if (scalar < 0.0) { scalar = 0.0; } } /* * Exclude positive regions if not displayed. */ if ( ! this->displayPositiveDataFlag) { 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()) { if (scalar > -1.0) { const float xStart = -1.0; const float xEnd = scalar; colorBar->addSection(xStart, xEnd, rgba, rgba); } } } /* * If the 'next' color is none, drawing * is skipped to let the background show * throw the 'none' region of the palette. */ if ( ! isNoneColorFlag) { /* * 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. */ colorBar->addSection(xStart, xEnd, startRGBA, endRGBA); /* * 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); } } } } float backgroundRGBA[4]; colorBar->getBackgroundColorRGBA(backgroundRGBA); /* * Draw over thresholded regions with background color */ if (this->thresholdType != PaletteThresholdTypeEnum::THRESHOLD_TYPE_OFF) { const float minMaxThresholds[2] = { getThresholdMinimum(thresholdType), getThresholdMaximum(thresholdType) }; float normalizedThresholds[2]; mapDataToPaletteNormalizedValues(statistics, minMaxThresholds, normalizedThresholds, 2); switch (this->thresholdTest) { case PaletteThresholdTestEnum::THRESHOLD_TEST_SHOW_INSIDE: if (normalizedThresholds[0] >= xMinimum) { colorBar->addSection(xMinimum, normalizedThresholds[0], backgroundRGBA, backgroundRGBA); } if (normalizedThresholds[1] < xMaximum) { colorBar->addSection(normalizedThresholds[1], xMaximum, backgroundRGBA, backgroundRGBA); } break; case PaletteThresholdTestEnum::THRESHOLD_TEST_SHOW_OUTSIDE: { const float xMin = MathFunctions::limitRange(normalizedThresholds[0], xMinimum, xMaximum); const float xMax = MathFunctions::limitRange(normalizedThresholds[1], xMinimum, xMaximum); if (xMin < xMax) { colorBar->addSection(normalizedThresholds[0], normalizedThresholds[1], backgroundRGBA, backgroundRGBA); } } break; } } /* * If zeros are not displayed, draw a line in the * background color at zero in the palette. */ if ( ! this->displayZeroDataFlag) { colorBar->addSection(0.0, 0.0, backgroundRGBA, backgroundRGBA); } /** * Add numeric text to the color bar. */ setupAnnotationColorBarNumericText(statistics, colorBar); } /** * Get auto scale percentage negative maximum. * @return Its value. * */ float PaletteColorMapping::getAutoScalePercentageNegativeMaximum() const { return autoScalePercentageNegativeMaximum; } /** * Set auto scale percentage negative maximum. * @param autoScalePercentageNegativeMaximum - new value. * */ void PaletteColorMapping::setAutoScalePercentageNegativeMaximum(const float autoScalePercentageNegativeMaximum) { if (this->autoScalePercentageNegativeMaximum != autoScalePercentageNegativeMaximum) { this->autoScalePercentageNegativeMaximum = autoScalePercentageNegativeMaximum; this->setModified(); } } /** * Get auto scale percentage negative minimum. * @return Its value. * */ float PaletteColorMapping::getAutoScalePercentageNegativeMinimum() const { return autoScalePercentageNegativeMinimum; } /** * Set auto scale percentage negative minimum. * @param autoScalePercentageNegativeMinimum - new value. * */ void PaletteColorMapping::setAutoScalePercentageNegativeMinimum(const float autoScalePercentageNegativeMinimum) { if (this->autoScalePercentageNegativeMinimum != autoScalePercentageNegativeMinimum) { this->autoScalePercentageNegativeMinimum = autoScalePercentageNegativeMinimum; this->setModified(); } } /** * Get auto scale percentage positive maximum. * @return Its value. * */ float PaletteColorMapping::getAutoScalePercentagePositiveMaximum() const { return autoScalePercentagePositiveMaximum; } /** * Set auto scale percentage positive maximum. * @param autoScalePercentagePositiveMaximum - new value. * */ void PaletteColorMapping::setAutoScalePercentagePositiveMaximum(const float autoScalePercentagePositiveMaximum) { if (this->autoScalePercentagePositiveMaximum != autoScalePercentagePositiveMaximum) { this->autoScalePercentagePositiveMaximum = autoScalePercentagePositiveMaximum; this->setModified(); } } /** * Get auto scale percentage positive minimum. * @return Its value. * */ float PaletteColorMapping::getAutoScalePercentagePositiveMinimum() const { return autoScalePercentagePositiveMinimum; } /** * Set auto scale percentage positive minimum. * @param autoScalePercentagePositiveMinimum - new value. * */ void PaletteColorMapping::setAutoScalePercentagePositiveMinimum(const float autoScalePercentagePositiveMinimum) { if (this->autoScalePercentagePositiveMinimum != autoScalePercentagePositiveMinimum) { this->autoScalePercentagePositiveMinimum = autoScalePercentagePositiveMinimum; this->setModified(); } } /** * @return The auto scale absolute percentage minimum. */ float PaletteColorMapping::getAutoScaleAbsolutePercentageMinimum() const { return this->autoScaleAbsolutePercentageMinimum; } /** * Set the auto scale absolute percentage minimum. * * @param autoScaleAbsolutePercentageMinimum * New value for auto scale absolute percentage minimum. */ void PaletteColorMapping::setAutoScaleAbsolutePercentageMinimum(const float autoScaleAbsolutePercentageMinimum) { if (this->autoScaleAbsolutePercentageMinimum != autoScaleAbsolutePercentageMinimum) { this->autoScaleAbsolutePercentageMinimum = autoScaleAbsolutePercentageMinimum; this->setModified(); } } /** * @return The auto scale absolute percentage maximum. */ float PaletteColorMapping::getAutoScaleAbsolutePercentageMaximum() const { return this->autoScaleAbsolutePercentageMaximum; } /** * Set the auto scale absolute percentage maximum. * * @param autoScaleAbsolutePercentageMaximum * New value for auto scale absolute percentage maximum. */ void PaletteColorMapping::setAutoScaleAbsolutePercentageMaximum(const float autoScaleAbsolutePercentageMaximum) { if (this->autoScaleAbsolutePercentageMaximum != autoScaleAbsolutePercentageMaximum) { this->autoScaleAbsolutePercentageMaximum = autoScaleAbsolutePercentageMaximum; this->setModified(); } } /** * See if negative data should be displayed. * @return true if negative data displayed, else false. * */ bool PaletteColorMapping::isDisplayNegativeDataFlag() const { return displayNegativeDataFlag; } /** * Set negative data should be displayed. * @param displayNegativeDataFlag - true to display negative data, else false * */ void PaletteColorMapping::setDisplayNegativeDataFlag(const bool displayNegativeDataFlag) { if (this->displayNegativeDataFlag != displayNegativeDataFlag) { this->displayNegativeDataFlag = displayNegativeDataFlag; this->setModified(); } } /** * See if positive data should be displayed. * @return true if positive data displayed, else false. * */ bool PaletteColorMapping::isDisplayPositiveDataFlag() const { return displayPositiveDataFlag; } /** * Set positive data should be displayed. * @param displayPositiveDataFlag - true to display positive data, else false * */ void PaletteColorMapping::setDisplayPositiveDataFlag(const bool displayPositiveDataFlag) { if (this->displayPositiveDataFlag != displayPositiveDataFlag) { this->displayPositiveDataFlag = displayPositiveDataFlag; this->setModified(); } } /** * See if zero data should be displayed. * @return true if zero data displayed, else false. * */ bool PaletteColorMapping::isDisplayZeroDataFlag() const { return displayZeroDataFlag; } /** * Set zero data should be displayed. * @param displayZeroDataFlag - true to display zero data, else false * */ void PaletteColorMapping::setDisplayZeroDataFlag(const bool displayZeroDataFlag) { if (this->displayZeroDataFlag != displayZeroDataFlag) { this->displayZeroDataFlag = displayZeroDataFlag; this->setModified(); } } /** * Interpolate palette colors when displaying data? * @return true to interpolate data, else false. * */ bool PaletteColorMapping::isInterpolatePaletteFlag() const { return interpolatePaletteFlag; } /** * Set palette colors should be interpolated when displaying data. * @param interpolatePaletteFlag - true to interpolate, else false. * */ void PaletteColorMapping::setInterpolatePaletteFlag(const bool interpolatePaletteFlag) { if (this->interpolatePaletteFlag != interpolatePaletteFlag) { this->interpolatePaletteFlag = interpolatePaletteFlag; this->setModified(); } } /** * Get how the data is scaled to the palette. * @return Enumerated type indicating how data is scaled to the palette. * */ PaletteScaleModeEnum::Enum PaletteColorMapping::getScaleMode() const { return scaleMode; } /** * Set how the data is scaled to the palette. * @param scaleMode - Enumerated type indicating how data is scaled * to the palette. * */ void PaletteColorMapping::setScaleMode(const PaletteScaleModeEnum::Enum scaleMode) { if (this->scaleMode != scaleMode) { this->scaleMode = scaleMode; this->setModified(); } } /** * Get the name of the selected palette. * @return Name of the selected palette. * */ AString PaletteColorMapping::getSelectedPaletteName() const { return selectedPaletteName; } /** * Set the name of the selected palette. * @param selectedPaletteName - Name of selected palette. * */ void PaletteColorMapping::setSelectedPaletteName(const AString& selectedPaletteName) { if (this->selectedPaletteName != selectedPaletteName) { this->selectedPaletteName = selectedPaletteName; this->setModified(); } } /** * Set the selected palette to PSYCH. * */ void PaletteColorMapping::setSelectedPaletteToPsych() { this->setSelectedPaletteName("PSYCH"); } /** * Set the selected palette to PSYCH-NO-NONE * */ void PaletteColorMapping::setSelectedPaletteToPsychNoNone() { this->setSelectedPaletteName("PSYCH-NO-NONE"); } /** * Set the selected palette to Orange-Yellow. * */ void PaletteColorMapping::setSelectedPaletteToOrangeYellow() { this->setSelectedPaletteName("Orange-Yellow"); } /** * Set the selected palette to Gray Interpolated. * */ void PaletteColorMapping::setSelectedPaletteToGrayInterpolated() { this->setSelectedPaletteName("Gray_Interp"); } /** * Get auto user scale negative maximum. * @return Its value. * */ float PaletteColorMapping::getUserScaleNegativeMaximum() const { return userScaleNegativeMaximum; } /** * Set user scale negative maximum. * @param userScaleNegativeMaximum - new value. * */ void PaletteColorMapping::setUserScaleNegativeMaximum(const float userScaleNegativeMaximum) { if (this->userScaleNegativeMaximum != userScaleNegativeMaximum) { this->userScaleNegativeMaximum = userScaleNegativeMaximum; this->setModified(); } } /** * Get auto user scale negative minimum. * @return Its value. * */ float PaletteColorMapping::getUserScaleNegativeMinimum() const { return userScaleNegativeMinimum; } /** * Set user scale negative minimum. * @param userScaleNegativeMinimum - new value. * */ void PaletteColorMapping::setUserScaleNegativeMinimum(const float userScaleNegativeMinimum) { if (this->userScaleNegativeMinimum != userScaleNegativeMinimum) { this->userScaleNegativeMinimum = userScaleNegativeMinimum; this->setModified(); } } /** * Get auto user scale positive maximum. * @return Its value. * */ float PaletteColorMapping::getUserScalePositiveMaximum() const { return userScalePositiveMaximum; } /** * Set user scale positive maximum. * @param userScalePositiveMaximum - new value. * */ void PaletteColorMapping::setUserScalePositiveMaximum(const float userScalePositiveMaximum) { if (this->userScalePositiveMaximum != userScalePositiveMaximum) { this->userScalePositiveMaximum = userScalePositiveMaximum; this->setModified(); } } /** * Get auto user scale positive maximum. * @return Its value. * */ float PaletteColorMapping::getUserScalePositiveMinimum() const { return userScalePositiveMinimum; } /** * Set user scale positive minimum. * @param userScalePositiveMinimum - new value. * */ void PaletteColorMapping::setUserScalePositiveMinimum(const float userScalePositiveMinimum) { if (this->userScalePositiveMinimum != userScalePositiveMinimum) { this->userScalePositiveMinimum = userScalePositiveMinimum; this->setModified(); } } /** * Get the minimum threshold for the given threshold type. * * @param thresholdType * The threshold type. * @return the threshold's value. * */ float PaletteColorMapping::getThresholdMinimum(const PaletteThresholdTypeEnum::Enum thresholdType) const { float value = 0.0; switch (thresholdType) { case PaletteThresholdTypeEnum::THRESHOLD_TYPE_OFF: break; case PaletteThresholdTypeEnum::THRESHOLD_TYPE_NORMAL: value = this->thresholdNormalMinimum; break; case PaletteThresholdTypeEnum::THRESHOLD_TYPE_MAPPED: value = this->thresholdMappedMinimum; break; case PaletteThresholdTypeEnum::THRESHOLD_TYPE_MAPPED_AVERAGE_AREA: value = this->thresholdMappedAverageAreaMinimum; break; } return value; } /** * Get the minimum threshold for the given threshold type. * * @param thresholdType * The threshold type. * @return the threshold's value. * */ float PaletteColorMapping::getThresholdMaximum(const PaletteThresholdTypeEnum::Enum thresholdType) const { float value = 0.0; switch (thresholdType) { case PaletteThresholdTypeEnum::THRESHOLD_TYPE_OFF: break; case PaletteThresholdTypeEnum::THRESHOLD_TYPE_NORMAL: value = this->thresholdNormalMaximum; break; case PaletteThresholdTypeEnum::THRESHOLD_TYPE_MAPPED: value = this->thresholdMappedMaximum; break; case PaletteThresholdTypeEnum::THRESHOLD_TYPE_MAPPED_AVERAGE_AREA: value = this->thresholdMappedAverageAreaMaximum; break; } return value; } /** * Set the minimum threshold for the given threshold type. * * @param thresholdType * The threshold type. * param thresholdMinimum the threshold's new value. * */ void PaletteColorMapping::setThresholdMinimum(const PaletteThresholdTypeEnum::Enum thresholdType, const float thresholdMinimum) { switch (thresholdType) { case PaletteThresholdTypeEnum::THRESHOLD_TYPE_OFF: break; case PaletteThresholdTypeEnum::THRESHOLD_TYPE_NORMAL: setThresholdNormalMinimum(thresholdMinimum); break; case PaletteThresholdTypeEnum::THRESHOLD_TYPE_MAPPED: setThresholdMappedMinimum(thresholdMinimum); break; case PaletteThresholdTypeEnum::THRESHOLD_TYPE_MAPPED_AVERAGE_AREA: setThresholdMappedAverageAreaMinimum(thresholdMinimum); break; } } /** * Set the maximum threshold for the given threshold type. * * @param thresholdType * The threshold type. * param thresholdMaximum the threshold's new value. * */ void PaletteColorMapping::setThresholdMaximum(const PaletteThresholdTypeEnum::Enum thresholdType, const float thresholdMaximum) { switch (thresholdType) { case PaletteThresholdTypeEnum::THRESHOLD_TYPE_OFF: break; case PaletteThresholdTypeEnum::THRESHOLD_TYPE_NORMAL: setThresholdNormalMaximum(thresholdMaximum); break; case PaletteThresholdTypeEnum::THRESHOLD_TYPE_MAPPED: setThresholdMappedMaximum(thresholdMaximum); break; case PaletteThresholdTypeEnum::THRESHOLD_TYPE_MAPPED_AVERAGE_AREA: setThresholdMappedAverageAreaMaximum(thresholdMaximum); break; } } /** * Get mapped average area minimum threshold * * @return the threshold's value. * */ float PaletteColorMapping::getThresholdMappedAverageAreaMinimum() const { return thresholdMappedAverageAreaMinimum; } /** * Set the mapped average area minimum threshold. * * @param thresholdMappedAverageAreaNegative - new value. * */ void PaletteColorMapping::setThresholdMappedAverageAreaMinimum(const float thresholdMappedAverageAreaMinimum) { if (this->thresholdMappedAverageAreaMinimum != thresholdMappedAverageAreaMinimum) { this->thresholdMappedAverageAreaMinimum = thresholdMappedAverageAreaMinimum; this->setModified(); } } /** * Get mapped average area maximum threshold * * @return the threshold's value. * */ float PaletteColorMapping::getThresholdMappedAverageAreaMaximum() const { return thresholdMappedAverageAreaMaximum; } /** * Set the mapped average area maximum threshold. * * @param thresholdMappedAverageAreaPositive - new value. * */ void PaletteColorMapping::setThresholdMappedAverageAreaMaximum(const float thresholdMappedAverageAreaMaximum) { if (this->thresholdMappedAverageAreaMaximum != thresholdMappedAverageAreaMaximum) { this->thresholdMappedAverageAreaMaximum = thresholdMappedAverageAreaMaximum; this->setModified(); } } /** * Get mapped minimum threshold * * @return the threshold's value. * */ float PaletteColorMapping::getThresholdMappedMinimum() const { return thresholdMappedMinimum; } /** * Set the mapped minimum threshold. * * @param thresholdMappedNegative - new value. * */ void PaletteColorMapping::setThresholdMappedMinimum(const float thresholdMappedMinimum) { if (this->thresholdMappedMinimum != thresholdMappedMinimum) { this->thresholdMappedMinimum = thresholdMappedMinimum; this->setModified(); } } /** * Get mapped maximum threshold * * @return the threshold's value. * */ float PaletteColorMapping::getThresholdMappedMaximum() const { return thresholdMappedMaximum; } /** * Set the mapped maximum threshold. * * @param thresholdMappedPositive - new value. * */ void PaletteColorMapping::setThresholdMappedMaximum(const float thresholdMappedMaximum) { if (this->thresholdMappedMaximum != thresholdMappedMaximum) { this->thresholdMappedMaximum = thresholdMappedMaximum; this->setModified(); } } /** * Get normal minimum threshold * * @return the threshold's value. * */ float PaletteColorMapping::getThresholdNormalMinimum() const { return thresholdNormalMinimum; } /** * Set the normal minimum threshold. * * @param thresholdNormalNegative - new value. * */ void PaletteColorMapping::setThresholdNormalMinimum(const float thresholdNormalMinimum) { if (this->thresholdNormalMinimum != thresholdNormalMinimum) { this->thresholdNormalMinimum = thresholdNormalMinimum; this->setModified(); } } /** * Get normal maximum threshold * * @return the threshold's value. * */ float PaletteColorMapping::getThresholdNormalMaximum() const { return thresholdNormalMaximum; } /** * Set the normal maximum threshold. * * @param thresholdNormalPositive - new value. * */ void PaletteColorMapping::setThresholdNormalMaximum(const float thresholdNormalMaximum) { if (this->thresholdNormalMaximum != thresholdNormalMaximum) { this->thresholdNormalMaximum = thresholdNormalMaximum; this->setModified(); } } /** * Get the threshold test. * @return Threshold test. * */ PaletteThresholdTestEnum::Enum PaletteColorMapping::getThresholdTest() const { return thresholdTest; } /** * Set the threshold test. * @param thresholdTest - The threshold test. * */ void PaletteColorMapping::setThresholdTest(const PaletteThresholdTestEnum::Enum thresholdTest) { if (this->thresholdTest != thresholdTest) { this->thresholdTest = thresholdTest; this->setModified(); } } /** * Get the threshold type. * @return Threshold type. * */ PaletteThresholdTypeEnum::Enum PaletteColorMapping::getThresholdType() const { return thresholdType; } /** * Set the threshold type. * * @param thresholdType - The threshold type. */ void PaletteColorMapping::setThresholdType(const PaletteThresholdTypeEnum::Enum thresholdType) { if (this->thresholdType != thresholdType) { this->thresholdType = thresholdType; this->setModified(); } } /** * @return The threshold range mode. */ PaletteThresholdRangeModeEnum::Enum PaletteColorMapping::getThresholdRangeMode() const { return this->thresholdRangeMode; } /** * Set the threshold range mode. * * @param rangeMode * New value for range mode. */ void PaletteColorMapping::setThresholdRangeMode(const PaletteThresholdRangeModeEnum::Enum rangeMode) { if (this->thresholdRangeMode != rangeMode) { this->thresholdRangeMode = rangeMode; setModified(); } } /** * Get the name of the threshold data which may be the name of a * functional volume or a metric column. * @return Name of data used as a threshold. * */ AString PaletteColorMapping::getThresholdDataName() const { return thresholdDataName; } /** * Set the name of the threshold data which may be the name of a * functional volume or a metric column. * @param thresholdDataName - name of data used as a threshold. * */ void PaletteColorMapping::setThresholdDataName(const AString& thresholdDataName) { if (this->thresholdDataName != thresholdDataName) { this->thresholdDataName = thresholdDataName; this->setModified(); } } /** * Display non-zero data that fails the threshold test in green? * @return true if displayed, else false. * */ bool PaletteColorMapping::isShowThresholdFailureInGreen() const { return this->thresholdShowFailureInGreen; } /** * Set display non-zero data that fails the threshold test in green? * @param showInGreenFlag - true if displayed, else false. * */ void PaletteColorMapping::setShowThresholdFailureInGreen(const bool showInGreenFlag) { if (this->thresholdShowFailureInGreen != showInGreenFlag) { this->thresholdShowFailureInGreen = showInGreenFlag; setModified(); } } /** * Set this object has been modified. * */ void PaletteColorMapping::setModified() { this->modifiedFlag = true; } /** * Set this object as not modified. Object should also * clear the modification status of its children. * */ void PaletteColorMapping::clearModified() { this->modifiedFlag = false; } /** * Get the modification status. Returns true if this object * or any of its children have been modified. * @return - The modification status. * */ bool PaletteColorMapping::isModified() const { return this->modifiedFlag; } /** * Map data values to palette normalized values using the * settings in this palette color mapping. * * @param statistics * Statistics containing min.max values. * @param data * The data values. * @param normalizedValuesOut * Result of mapping data values to palette normalized * values which range [-1.0, 1.0]. This array MUST contain * the same number of values as 'data'. * @param numberOfData * Number of values in both data and normalizedValuesOut. */ void PaletteColorMapping::mapDataToPaletteNormalizedValues(const FastStatistics* statistics, const float* dataValues, float* normalizedValuesOut, const int64_t numberOfData) const { if (numberOfData <= 0) { return; } /* * Minimum and maximum values used when mapping scalar into color palette. */ float mappingMostNegative = 0.0; float mappingLeastNegative = 0.0; float mappingLeastPositive = 0.0; float mappingMostPositive = 0.0; switch (this->getScaleMode()) { case PaletteScaleModeEnum::MODE_AUTO_SCALE: statistics->getNonzeroRanges(mappingMostNegative, mappingLeastNegative, mappingLeastPositive, mappingMostPositive); break; case PaletteScaleModeEnum::MODE_AUTO_SCALE_ABSOLUTE_PERCENTAGE: { const float mostPercentage = this->getAutoScaleAbsolutePercentageMaximum(); const float leastPercentage = this->getAutoScaleAbsolutePercentageMinimum(); mappingMostNegative = -statistics->getApproxAbsolutePercentile(mostPercentage); mappingLeastNegative = -statistics->getApproxAbsolutePercentile(leastPercentage); mappingLeastPositive = statistics->getApproxAbsolutePercentile(leastPercentage); mappingMostPositive = statistics->getApproxAbsolutePercentile(mostPercentage); } break; case PaletteScaleModeEnum::MODE_AUTO_SCALE_PERCENTAGE: { const float mostNegativePercentage = this->getAutoScalePercentageNegativeMaximum(); const float leastNegativePercentage = this->getAutoScalePercentageNegativeMinimum(); const float leastPositivePercentage = this->getAutoScalePercentagePositiveMinimum(); const float mostPositivePercentage = this->getAutoScalePercentagePositiveMaximum(); mappingMostNegative = statistics->getApproxNegativePercentile(mostNegativePercentage); mappingLeastNegative = statistics->getApproxNegativePercentile(leastNegativePercentage); mappingLeastPositive = statistics->getApproxPositivePercentile(leastPositivePercentage); mappingMostPositive = statistics->getApproxPositivePercentile(mostPositivePercentage); } break; case PaletteScaleModeEnum::MODE_USER_SCALE: mappingMostNegative = this->getUserScaleNegativeMaximum(); mappingLeastNegative = this->getUserScaleNegativeMinimum(); mappingLeastPositive = this->getUserScalePositiveMinimum(); 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; 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 settingsValidPos = false; } float mappingNegativeDenominator = (mappingMostNegative - mappingLeastNegative) / (-1.0f + PALETTE_ZERO_COLOR_ZONE);//ditto, but most negative maps to -1 if (mappingNegativeDenominator == 0.0) { settingsValidNeg = false; } for (int64_t i = 0; i < numberOfData; i++) { float scalar = dataValues[i]; /* * Color scalar using palette */ float normalized = 0.0f; if (scalar > 0.0) { if (settingsValidPos) { normalized = (scalar - mappingLeastPositive) / mappingPositiveDenominator + PALETTE_ZERO_COLOR_ZONE; if (normalized > 1.0f) { normalized = 1.0f; } else if (normalized < PALETTE_ZERO_COLOR_ZONE) { normalized = PALETTE_ZERO_COLOR_ZONE; } } else { normalized = 1.0f; } } else if (scalar < 0.0) { if (settingsValidNeg) { normalized = (scalar - mappingLeastNegative) / mappingNegativeDenominator - PALETTE_ZERO_COLOR_ZONE; if (normalized < -1.0f) { normalized = -1.0f; } else if (normalized > -PALETTE_ZERO_COLOR_ZONE) { normalized = -PALETTE_ZERO_COLOR_ZONE; } } else { normalized = -1.0f; } } normalizedValuesOut[i] = normalized; } } /** * Setup the numeric text for the annotation colorbar. * * @param statistics * Statistics for the data. * @param colorBar * Colorbar that receives the numeric text. */ void PaletteColorMapping::setupAnnotationColorBarNumericText(const FastStatistics* statistics, AnnotationColorBar* colorBar) { colorBar->clearNumericText(); std::vector colorBarNumericText; getPaletteColorBarScaleText(statistics, colorBarNumericText); // std::vector > normalizedPositionAndText; // getPaletteColorBarScaleText(statistics, // normalizedPositionAndText); for (std::vector::iterator iter = colorBarNumericText.begin(); iter != colorBarNumericText.end(); iter++) { const AnnotationColorBarNumericText* nt = *iter; colorBar->addNumericText(nt->getScalar(), nt->getNumericText(), nt->getHorizontalAlignment(), nt->isDrawTickMarkAtScalar()); delete nt; } colorBar->setShowTickMarksSelected(this->showTickMarksSelected); } /** * Get the text characters for drawing the scale above the palette * color bar. * * @param statistics * Statistics for the data. * @param colorBarNumericTextOut, * Contains attributes for numeric text displayed above color bar. */ void PaletteColorMapping::getPaletteColorBarScaleText(const FastStatistics* statistics, std::vector& colorBarNumericTextOut) const { colorBarNumericTextOut.clear(); /* * Processing for Sign Only Mode */ 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()) { colorBarNumericTextOut.push_back(new AnnotationColorBarNumericText(0.0, negative, AnnotationTextAlignHorizontalEnum::LEFT, true)); colorBarNumericTextOut.push_back(new AnnotationColorBarNumericText(0.5, zero, AnnotationTextAlignHorizontalEnum::CENTER, true)); colorBarNumericTextOut.push_back(new AnnotationColorBarNumericText(1.0, 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, zero, AnnotationTextAlignHorizontalEnum::LEFT, true)); colorBarNumericTextOut.push_back(new AnnotationColorBarNumericText(1.0, 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, negative, AnnotationTextAlignHorizontalEnum::LEFT, true)); colorBarNumericTextOut.push_back(new AnnotationColorBarNumericText(1.0, 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; } 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); /* * Will 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)); AnnotationTextAlignHorizontalEnum::Enum alignment = AnnotationTextAlignHorizontalEnum::CENTER; if (i == 0) { alignment = AnnotationTextAlignHorizontalEnum::LEFT; } else if (i == (numValues - 1)) { if ( ! positiveDataDisplayedFlag) { alignment = AnnotationTextAlignHorizontalEnum::RIGHT; } } colorBarNumericTextOut.push_back(new AnnotationColorBarNumericText(value, negativeValuesText[i], alignment, true)); // normalizedPositionAndTextOut.push_back(std::make_pair(value, // negativeValuesText[i])); } } /* * Add the zero value text */ if (negativeDataDisplayedFlag && positiveDataDisplayedFlag) { colorBarNumericTextOut.push_back(new AnnotationColorBarNumericText(0.5, zeroValueText, AnnotationTextAlignHorizontalEnum::CENTER, true)); // 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)); AnnotationTextAlignHorizontalEnum::Enum alignment = AnnotationTextAlignHorizontalEnum::CENTER; if (i == (numValues - 1)) { alignment = AnnotationTextAlignHorizontalEnum::RIGHT; } else if (i == 0) { if ( ! negativeDataDisplayedFlag) { alignment = AnnotationTextAlignHorizontalEnum::LEFT; } } colorBarNumericTextOut.push_back(new AnnotationColorBarNumericText(value, positiveValuesText[i], alignment, true)); // 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 = colorBarNumericTextOut.begin(); iter != colorBarNumericTextOut.end(); iter++) { 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; } const bool debugFlag = false; if (debugFlag) { const int numItems = static_cast(colorBarNumericTextOut.size()); std::cout << "Colorbar: " << std::endl; for (int32_t i = 0; i < numItems; i++) { const AnnotationColorBarNumericText* nt = colorBarNumericTextOut[i]; std::cout << " " << qPrintable(QString::number(nt->getScalar()) + ": " + nt->getNumericText() + " " + AnnotationTextAlignHorizontalEnum::toGuiName(nt->getHorizontalAlignment()) + " show-tick=" + AString::fromBool(nt->isDrawTickMarkAtScalar())) << std::endl; // std::cout << " " << qPrintable(QString::number(normalizedPositionAndTextOut[i].first) // + ": " // + normalizedPositionAndTextOut[i].second) << std::endl; } std::cout << std::endl; } } /** * Get the text characters for drawing the scale above the palette * color bar. * * @param statistics * Statistics for the data. * @param normalizedPositionAndTextOut, * Horizontal position ranging 0.0 to 1.0 * and text values. */ void PaletteColorMapping::getPaletteColorBarScaleText(const FastStatistics* statistics, std::vector >& normalizedPositionAndTextOut) const { normalizedPositionAndTextOut.clear(); /* * Processing for Sign Only Mode */ 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; } 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 if thresholding is linked meaning * that the high threshold is restricted to a positive value * the low threshold = -high. * * This is just a status and it is up to the user of this class * to properly set the threshold values. */ bool PaletteColorMapping::isThresholdNegMinPosMaxLinked() const { return this->thresholdNegMinPosMaxLinked; } /** * Set thresholding is linked meaning * that the high threshold is restricted to a positive value * the low threshold = -high. * * This is just a status and it is up to the user of this class * to properly set the threshold values. * * @param linked * New status of low/high linked thresholding. */ void PaletteColorMapping::setThresholdNegMinPosMaxLinked(const bool linked) { if (this->thresholdNegMinPosMaxLinked != linked) { this->thresholdNegMinPosMaxLinked = linked; setModified(); } } /** * @return The numeric format mode. */ NumericFormatModeEnum::Enum PaletteColorMapping::getNumericFormatMode() const { return this->numericFormatMode; } /** * Set the numeric format mode. * * @param numericFormatMode * New value for precision mode. */ void PaletteColorMapping::setNumericFormatMode(const NumericFormatModeEnum::Enum numericFormatMode) { if (numericFormatMode != this->numericFormatMode) { this->numericFormatMode = numericFormatMode; setModified(); } } /** * @return The precision digits (right of decimal). */ int32_t PaletteColorMapping::getPrecisionDigits() const { return this->precisionDigits; } /** * Set the precision digits (right of decimal) * * @param precisionDigits * New value for number of digits right of decimal. */ void PaletteColorMapping::setPrecisionDigits(const int32_t precisionDigits) { if (precisionDigits != this->precisionDigits) { this->precisionDigits = precisionDigits; setModified(); } } /** * @return The numeric subdivision count which is the number of * numeric values uniformly spaced between zero and the maximum * value. */ int32_t PaletteColorMapping::getNumericSubdivisionCount() const { return this->numericSubdivisionCount; } /** * Set the numeric subdivision count which is the number of * numeric values uniformly spaced between zero and the maximum * value. * * @param numericSubvisionCount * New value for subdivision count. */ void PaletteColorMapping::setNumericSubdivisionCount(const int32_t numericSubdivisionCount) { if (numericSubdivisionCount != this->numericSubdivisionCount) { this->numericSubdivisionCount = numericSubdivisionCount; setModified(); } } /** * @return The color bar values mode. */ PaletteColorBarValuesModeEnum::Enum PaletteColorMapping::getColorBarValuesMode() const { return this->colorBarValuesMode; } /** * Set the color bar values mode. * * @param colorBarValuesMode * New value for color bar values mode. */ void PaletteColorMapping::setColorBarValuesMode(const PaletteColorBarValuesModeEnum::Enum colorBarValuesMode) { if (colorBarValuesMode != this->colorBarValuesMode) { this->colorBarValuesMode = colorBarValuesMode; setModified(); } } /** * @param Is show tick marks selected? */ bool PaletteColorMapping::isShowTickMarksSelected() const { return this->showTickMarksSelected; } /** * Set show tick marks selected. * * @param selected * New selection status. */ void PaletteColorMapping::setShowTickMarksSelected(const bool selected) { if (selected != this->showTickMarksSelected) { this->showTickMarksSelected = selected; setModified(); } } connectome-workbench-1.2.3+git41-gc4c6c90/src/Palette/PaletteColorMapping.h000066400000000000000000000265311300200146000262330ustar00rootroot00000000000000#ifndef __PALETTECOLORMAPPING_H__ #define __PALETTECOLORMAPPING_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 "CaretObject.h" #include "AnnotationColorBarNumericText.h" #include "NumericFormatModeEnum.h" #include "PaletteColorBarValuesModeEnum.h" #include "PaletteEnums.h" #include "PaletteThresholdRangeModeEnum.h" #include "XmlException.h" namespace caret { class AnnotationColorBar; class FastStatistics; class XmlWriter; /** * Controls color mapping using a palette. */ class PaletteColorMapping : public CaretObject { public: PaletteColorMapping(); PaletteColorMapping(const PaletteColorMapping& o); PaletteColorMapping& operator=(const PaletteColorMapping& o); bool operator==(const PaletteColorMapping& pcm) const; bool operator!=(const PaletteColorMapping& pcm) const { return !((*this) == pcm); } virtual ~PaletteColorMapping(); void copy(const PaletteColorMapping& pcm); private: void copyHelper(const PaletteColorMapping& o); void initializeMembersPaletteColorMapping(); public: void setupAnnotationColorBar(const FastStatistics* statistics, AnnotationColorBar* colorBar); void setupAnnotationColorBarNumericText(const FastStatistics* statistics, AnnotationColorBar* colorBar); void writeAsXML(XmlWriter& xmlWriter); AString encodeInXML(); void decodeFromStringXML(const AString& xml); float getAutoScalePercentageNegativeMaximum() const; void setAutoScalePercentageNegativeMaximum(const float autoScalePercentageNegativeMaximum); float getAutoScalePercentageNegativeMinimum() const; void setAutoScalePercentageNegativeMinimum(const float autoScalePercentageNegativeMinimum); float getAutoScalePercentagePositiveMaximum() const; void setAutoScalePercentagePositiveMaximum(const float autoScalePercentagePositiveMaximum); float getAutoScalePercentagePositiveMinimum() const; void setAutoScalePercentagePositiveMinimum(const float autoScalePercentagePositiveMinimum); float getAutoScaleAbsolutePercentageMinimum() const; void setAutoScaleAbsolutePercentageMinimum(const float autoScaleAbsolutePercentageMinimum); float getAutoScaleAbsolutePercentageMaximum() const; void setAutoScaleAbsolutePercentageMaximum(const float autoScaleAbsolutePercentageMaximum); bool isDisplayNegativeDataFlag() const; void setDisplayNegativeDataFlag(const bool displayNegativeDataFlag); bool isDisplayPositiveDataFlag() const; void setDisplayPositiveDataFlag(const bool displayPositiveDataFlag); bool isDisplayZeroDataFlag() const; void setDisplayZeroDataFlag(const bool displayZeroDataFlag); bool isInterpolatePaletteFlag() const; void setInterpolatePaletteFlag(const bool interpolatePaletteFlag); PaletteScaleModeEnum::Enum getScaleMode() const; void setScaleMode(const PaletteScaleModeEnum::Enum scaleMode); AString getSelectedPaletteName() const; void setSelectedPaletteName(const AString& selectedPaletteName); void setSelectedPaletteToPsych(); void setSelectedPaletteToPsychNoNone(); void setSelectedPaletteToOrangeYellow(); void setSelectedPaletteToGrayInterpolated(); float getUserScaleNegativeMaximum() const; void setUserScaleNegativeMaximum(const float userScaleNegativeMaximum); float getUserScaleNegativeMinimum() const; void setUserScaleNegativeMinimum(const float userScaleNegativeMinimum); float getUserScalePositiveMaximum() const; void setUserScalePositiveMaximum(const float userScalePositiveMaximum); float getUserScalePositiveMinimum() const; void setUserScalePositiveMinimum(const float userScalePositiveMinimum); float getThresholdMappedAverageAreaMinimum() const; void setThresholdMappedAverageAreaMinimum(const float thresholdMappedAverageAreaMinimum); float getThresholdMappedAverageAreaMaximum() const; void setThresholdMappedAverageAreaMaximum(const float thresholdMappedAverageAreaPositive); float getThresholdMappedMinimum() const; void setThresholdMappedMinimum(const float thresholdMappedMinimum); float getThresholdMappedMaximum() const; void setThresholdMappedMaximum(const float thresholdMappedPositive); float getThresholdNormalMinimum() const; void setThresholdNormalMinimum(const float thresholdNormalMinimum); float getThresholdNormalMaximum() const; void setThresholdNormalMaximum(const float thresholdNormalPositive); float getThresholdMinimum(const PaletteThresholdTypeEnum::Enum thresholdType) const; float getThresholdMaximum(const PaletteThresholdTypeEnum::Enum thresholdType) const; void setThresholdMinimum(const PaletteThresholdTypeEnum::Enum thresholdType, const float thresholdMinimum); void setThresholdMaximum(const PaletteThresholdTypeEnum::Enum thresholdType, const float thresholdMaximum); PaletteThresholdTestEnum::Enum getThresholdTest() const; void setThresholdTest(const PaletteThresholdTestEnum::Enum thresholdTest); PaletteThresholdTypeEnum::Enum getThresholdType() const; void setThresholdType(const PaletteThresholdTypeEnum::Enum thresholdType); PaletteThresholdRangeModeEnum::Enum getThresholdRangeMode() const; void setThresholdRangeMode(const PaletteThresholdRangeModeEnum::Enum rangeMode); AString getThresholdDataName() const; void setThresholdDataName(const AString& thresholdDataName); bool isShowThresholdFailureInGreen() const; void setShowThresholdFailureInGreen(const bool showInGreenFlag); bool isThresholdNegMinPosMaxLinked() const; void setThresholdNegMinPosMaxLinked(const bool linked); NumericFormatModeEnum::Enum getNumericFormatMode() const; int32_t getPrecisionDigits() const; int32_t getNumericSubdivisionCount() const; void setNumericFormatMode(const NumericFormatModeEnum::Enum numericFormatMode); void setPrecisionDigits(const int32_t precisionDigits); void setNumericSubdivisionCount(const int32_t numericSubdivisionCount); PaletteColorBarValuesModeEnum::Enum getColorBarValuesMode() const; void setColorBarValuesMode(const PaletteColorBarValuesModeEnum::Enum colorBarValuesMode); bool isShowTickMarksSelected() const; void setShowTickMarksSelected(const bool selected); void setModified(); void clearModified(); bool isModified() 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; /** A positive value near zero - may be zero! */ static const float SMALL_POSITIVE; /** A negative value near zero - may be zero! */ static const float SMALL_NEGATIVE; private: PaletteScaleModeEnum::Enum scaleMode; float autoScalePercentageNegativeMaximum; float autoScalePercentageNegativeMinimum; float autoScalePercentagePositiveMinimum; float autoScalePercentagePositiveMaximum; float autoScaleAbsolutePercentageMinimum; float autoScaleAbsolutePercentageMaximum; float userScaleNegativeMaximum; float userScaleNegativeMinimum; float userScalePositiveMinimum; float userScalePositiveMaximum; AString selectedPaletteName; bool interpolatePaletteFlag; bool displayPositiveDataFlag; bool displayZeroDataFlag; bool displayNegativeDataFlag; PaletteThresholdTypeEnum::Enum thresholdType; PaletteThresholdTestEnum::Enum thresholdTest; PaletteThresholdRangeModeEnum::Enum thresholdRangeMode; float thresholdNormalMinimum; float thresholdNormalMaximum; float thresholdMappedMinimum; float thresholdMappedMaximum; float thresholdMappedAverageAreaMinimum; float thresholdMappedAverageAreaMaximum; AString thresholdDataName; bool thresholdShowFailureInGreen; bool thresholdNegMinPosMaxLinked; NumericFormatModeEnum::Enum numericFormatMode; int32_t precisionDigits; int32_t numericSubdivisionCount; PaletteColorBarValuesModeEnum::Enum colorBarValuesMode; bool showTickMarksSelected; /**Tracks modification, DO NOT copy */ bool modifiedFlag; }; #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; #endif // __PALETTE_COLOR_MAPPING_DECLARE__ } // namespace #endif // __PALETTECOLORMAPPING_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Palette/PaletteColorMappingSaxReader.cxx000066400000000000000000000370301300200146000304010ustar00rootroot00000000000000/*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 "CaretAssert.h" #include "CaretLogger.h" #include "PaletteColorMapping.h" #include "PaletteColorMappingSaxReader.h" #include "PaletteColorMappingXmlElements.h" #include "XmlAttributes.h" #include "XmlException.h" using namespace caret; /** * constructor. */ PaletteColorMappingSaxReader::PaletteColorMappingSaxReader(PaletteColorMapping* paletteColorMapping) { CaretAssert(paletteColorMapping); this->state = STATE_NONE; this->stateStack.push(state); this->elementText = ""; this->paletteColorMapping = paletteColorMapping; } /** * destructor. */ PaletteColorMappingSaxReader::~PaletteColorMappingSaxReader() { } /** * start an element. */ void PaletteColorMappingSaxReader::startElement(const AString& /* namespaceURI */, const AString& /* localName */, const AString& qName, const XmlAttributes& attributes) { const STATE previousState = this->state; switch (this->state) { case STATE_NONE: if (qName == PaletteColorMappingXmlElements::XML_TAG_PALETTE_COLOR_MAPPING) { this->state = STATE_READING_ELEMENTS; int32_t version = attributes.getValueAsInt(PaletteColorMappingXmlElements::XML_ATTRIBUTE_VERSION_NUMBER); if (version > PaletteColorMappingXmlElements::XML_VERSION_NUMBER) { std::ostringstream str; str << "Version of PaletteColorMapping (" << version << ") is greater than version(s) supported (" << PaletteColorMappingXmlElements::XML_VERSION_NUMBER << ")."; throw XmlSaxParserException(AString::fromStdString(str.str())); } } else { std::ostringstream str; str << "Root element is \"" << qName.toStdString() << "\" but should be " << PaletteColorMappingXmlElements::XML_TAG_PALETTE_COLOR_MAPPING.toStdString(); throw XmlSaxParserException(AString::fromStdString(str.str())); } break; case STATE_READING_ELEMENTS: break; } // // Save previous state // this->stateStack.push(previousState); this->elementText = ""; } /** * Convert the string representation of a bool to a bool. * @param s * String containing boolean value. * @return * The bool value. */ bool toBool(const AString& s) { if ((s == "true") || (s == "TRUE") || (s == "True") || (s == "T") || (s == "t") || (s == "1")) { return true; } return false; } /** * Split up a string containing float values. * * @param s * String containing float values. * @return * float vector containing values extracted from string. */ std::vector toFloatVector(const AString& s) { std::vector fv; std::istringstream str(s.toStdString()); while ((str.eof() == false) && (str.fail() == false)) { float value; str >> value; fv.push_back(value); } return fv; } /** * end an element. */ void PaletteColorMappingSaxReader::endElement(const AString& /* namspaceURI */, const AString& /* localName */, const AString& qName) { switch (state) { case STATE_NONE: break; case STATE_READING_ELEMENTS: if (qName == PaletteColorMappingXmlElements::XML_TAG_AUTO_SCALE_PERCENTAGE_VALUES) { std::vector values = toFloatVector(this->elementText); if (values.size() >= 4) { this->paletteColorMapping->setAutoScalePercentageNegativeMaximum(values[0]); this->paletteColorMapping->setAutoScalePercentageNegativeMinimum(values[1]); this->paletteColorMapping->setAutoScalePercentagePositiveMinimum(values[2]); this->paletteColorMapping->setAutoScalePercentagePositiveMaximum(values[3]); } else { throw XmlSaxParserException("PaletteColorMappingXmlElements::auto scale percenter does not contain four values."); } } else if (qName == PaletteColorMappingXmlElements::XML_TAG_AUTO_SCALE_ABSOLUTE_PERCENTAGE_VALUES) { std::vector values = toFloatVector(this->elementText); if (values.size() >= 2) { this->paletteColorMapping->setAutoScaleAbsolutePercentageMinimum(values[0]); this->paletteColorMapping->setAutoScaleAbsolutePercentageMaximum(values[1]); } } else if (qName == PaletteColorMappingXmlElements::XML_TAG_DISPLAY_NEGATIVE) { this->paletteColorMapping->setDisplayNegativeDataFlag(toBool(this->elementText)); } else if (qName == PaletteColorMappingXmlElements::XML_TAG_DISPLAY_POSITIVE) { this->paletteColorMapping->setDisplayPositiveDataFlag(toBool(this->elementText)); } else if (qName == PaletteColorMappingXmlElements::XML_TAG_DISPLAY_ZERO) { this->paletteColorMapping->setDisplayZeroDataFlag(toBool(this->elementText)); } else if (qName == PaletteColorMappingXmlElements::XML_TAG_INTERPOLATE) { this->paletteColorMapping->setInterpolatePaletteFlag(toBool(this->elementText)); } else if (qName == PaletteColorMappingXmlElements::XML_TAG_PALETTE_NAME) { this->paletteColorMapping->setSelectedPaletteName(this->elementText); } else if (qName == PaletteColorMappingXmlElements::XML_TAG_THRESHOLD_DATA_NAME) { this->paletteColorMapping->setThresholdDataName(this->elementText); } else if (qName == PaletteColorMappingXmlElements::XML_TAG_THRESHOLD_FAILURE_IN_GREEN) { /* ??? */ } else if (qName == PaletteColorMappingXmlElements::XML_TAG_THRESHOLD_MAPPED_AVG_AREA_VALUES) { std::vector values = toFloatVector(this->elementText); if (values.size() >= 2) { this->paletteColorMapping->setThresholdMappedAverageAreaMinimum(values[0]); this->paletteColorMapping->setThresholdMappedAverageAreaMaximum(values[1]); } else { throw XmlSaxParserException("PaletteColorMappingXmlElements::threshild mapped average area does not contain two values."); } } else if (qName == PaletteColorMappingXmlElements::XML_TAG_THRESHOLD_MAPPED_VALUES) { std::vector values = toFloatVector(this->elementText); if (values.size() >= 2) { this->paletteColorMapping->setThresholdMappedMinimum(values[0]); this->paletteColorMapping->setThresholdMappedMaximum(values[1]); } else { throw XmlSaxParserException("PaletteColorMappingXmlElements::threshild mapped does not contain two values."); } } else if (qName == PaletteColorMappingXmlElements::XML_TAG_THRESHOLD_NORMAL_VALUES) { std::vector values = toFloatVector(this->elementText); if (values.size() >= 2) { this->paletteColorMapping->setThresholdNormalMinimum(values[0]); this->paletteColorMapping->setThresholdNormalMaximum(values[1]); } else { throw XmlSaxParserException("PaletteColorMappingXmlElements::threshild mapped normal does not contain two values."); } } else if (qName == PaletteColorMappingXmlElements::XML_TAG_THRESHOLD_TEST) { bool isValid = false; PaletteThresholdTestEnum::Enum thresoldTest = PaletteThresholdTestEnum::fromName(this->elementText, &isValid); if (isValid) { this->paletteColorMapping->setThresholdTest(thresoldTest); } else { throw XmlSaxParserException("Invalid PaletteColorMapping::thresoldTest " + this->elementText); } } else if (qName == PaletteColorMappingXmlElements::XML_TAG_THRESHOLD_TYPE) { bool isValid = false; PaletteThresholdTypeEnum::Enum thresholdType = PaletteThresholdTypeEnum::fromName(this->elementText, &isValid); if (isValid) { this->paletteColorMapping->setThresholdType(thresholdType); } else { throw XmlSaxParserException("Invalid PaletteColorMapping::thresholdType: " + this->elementText); } } else if (qName == PaletteColorMappingXmlElements::XML_TAG_SCALE_MODE) { bool isValid = false; PaletteScaleModeEnum::Enum scaleMode = PaletteScaleModeEnum::fromName(this->elementText, &isValid); if (isValid) { this->paletteColorMapping->setScaleMode(scaleMode); } else { throw XmlSaxParserException("Invalid PaletteColorMapping::scaleMode: " + this->elementText); } } else if (qName == PaletteColorMappingXmlElements::XML_TAG_USER_SCALE_VALUES) { std::vector values = toFloatVector(this->elementText); if (values.size() >= 4) { this->paletteColorMapping->setUserScaleNegativeMaximum(values[0]); this->paletteColorMapping->setUserScaleNegativeMinimum(values[1]); this->paletteColorMapping->setUserScalePositiveMinimum(values[2]); this->paletteColorMapping->setUserScalePositiveMaximum(values[3]); } else { throw XmlSaxParserException("PaletteColorMappingXmlElements::auto scale percenter does not contain four values."); } } else if (qName == PaletteColorMappingXmlElements::XML_TAG_THRESHOLD_RANGE_MODE) { bool isValid = false; PaletteThresholdRangeModeEnum::Enum rangeMode = PaletteThresholdRangeModeEnum::fromName(this->elementText, &isValid); if (isValid) { this->paletteColorMapping->setThresholdRangeMode(rangeMode); } else { throw XmlSaxParserException("Invalid PaletteThresholdRangeModeEnum::Enum: " + this->elementText); } } else if (qName == PaletteColorMappingXmlElements::XML_TAG_THRESHOLD_NEG_MIN_POS_MAX_LINKED) { this->paletteColorMapping->setThresholdNegMinPosMaxLinked(toBool(this->elementText)); } else if (qName == PaletteColorMappingXmlElements::XML_TAG_PALETTE_COLOR_MAPPING) { /* Top level tag, nothing to do */ } 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); } else { throw XmlSaxParserException("Invalid PalettePrecisionModeEnum::Enum: " + this->elementText); } } else if (qName == PaletteColorMappingXmlElements::XML_TAG_PRECISION_DIGITS) { this->paletteColorMapping->setPrecisionDigits(this->elementText.toInt()); } else if (qName == PaletteColorMappingXmlElements::XML_TAG_NUMERIC_SUBDIVISIONS) { this->paletteColorMapping->setNumericSubdivisionCount(this->elementText.toInt()); } else if (qName == PaletteColorMappingXmlElements::XML_TAG_COLOR_BAR_VALUES_MODE) { bool isValid = false; PaletteColorBarValuesModeEnum::Enum colorBarMode = PaletteColorBarValuesModeEnum::fromName(this->elementText, &isValid); if (isValid) { this->paletteColorMapping->setColorBarValuesMode(colorBarMode); } else { throw XmlSaxParserException("Invalid PaletteColorBarValuesModeEnum::Enum: " + this->elementText); } } else if (qName == PaletteColorMappingXmlElements::XML_TAG_SHOW_TICK_MARKS) { this->paletteColorMapping->setShowTickMarksSelected(toBool(this->elementText)); } else { std::ostringstream str; str << "Unrecognized palette color mapping element \"" << qName.toStdString() << "\"."; throw XmlSaxParserException(AString::fromStdString(str.str())); } break; } // // Clear out for new elements // this->elementText = ""; // // Go to previous state // if (this->stateStack.empty()) { throw XmlSaxParserException("State stack is empty while reading PaletteColorMapping."); } this->state = stateStack.top(); this->stateStack.pop(); } /** * get characters in an element. */ void PaletteColorMappingSaxReader::characters(const char* ch) { this->elementText += ch; } /** * a fatal error occurs. */ void PaletteColorMappingSaxReader::fatalError(const XmlSaxParserException& e) { throw e; } /** * A warning occurs */ void PaletteColorMappingSaxReader::warning(const XmlSaxParserException& e) { CaretLogWarning("XML Parser Warning: " + e.whatString()); } // an error occurs void PaletteColorMappingSaxReader::error(const XmlSaxParserException& e) { CaretLogSevere("XML Parser Error: " + e.whatString()); throw e; } void PaletteColorMappingSaxReader::startDocument() { } void PaletteColorMappingSaxReader::endDocument() { } connectome-workbench-1.2.3+git41-gc4c6c90/src/Palette/PaletteColorMappingSaxReader.h000066400000000000000000000067701300200146000300350ustar00rootroot00000000000000 #ifndef __PALETTE_COLOR_MAPPING_SAX_READER_H__ #define __PALETTE_COLOR_MAPPING_SAX_READER_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 "XmlSaxParserException.h" #include "XmlSaxParserHandlerInterface.h" namespace caret { class PaletteColorMapping; class XmlAttributes; /** * Class for reading a PaletteColorMapping object with a SAX Parser */ class PaletteColorMappingSaxReader : public CaretObject, public XmlSaxParserHandlerInterface { public: PaletteColorMappingSaxReader(PaletteColorMapping* paletteColorMapping); virtual ~PaletteColorMappingSaxReader(); private: PaletteColorMappingSaxReader(const PaletteColorMappingSaxReader&); PaletteColorMappingSaxReader& operator=(const PaletteColorMappingSaxReader&); public: void startElement(const AString& namespaceURI, const AString& localName, const AString& qName, const XmlAttributes& attributes); void endElement(const AString& namspaceURI, const AString& localName, const AString& qName); void characters(const char* ch); void fatalError(const XmlSaxParserException& e); void warning(const XmlSaxParserException& e); void error(const XmlSaxParserException& e); void startDocument(); void endDocument(); protected: /// file reading states enum STATE { /// no state STATE_NONE, /// reading elements STATE_READING_ELEMENTS }; /// file reading state STATE state; /// the state stack used when reading a file std::stack stateStack; /// the error message AString errorMessage; /// element text AString elementText; /// GIFTI label table being read PaletteColorMapping* paletteColorMapping; /// label index int labelIndex; /// label color component float labelRed; /// label color component float labelGreen; /// label color component float labelBlue; /// label color component float labelAlpha; /// label's X-coordinate float labelX; /// label's Y-coordinate float labelY; /// label's Z-coordinate float labelZ; }; } // namespace #endif // __PALETTE_COLOR_MAPPING_SAX_READER_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Palette/PaletteColorMappingXmlElements.h000066400000000000000000000060201300200146000304000ustar00rootroot00000000000000#ifndef __PALETTE_COLOR_MAPPING_XML_ELEMENTS_H__ #define __PALETTE_COLOR_MAPPING_XML_ELEMENTS_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 using namespace caret; namespace PaletteColorMappingXmlElements { static const AString XML_TAG_PALETTE_COLOR_MAPPING = "PaletteColorMapping"; static const AString XML_TAG_SCALE_MODE = "ScaleMode"; static const AString XML_TAG_AUTO_SCALE_PERCENTAGE_VALUES = "AutoScalePercentageValues"; static const AString XML_TAG_AUTO_SCALE_ABSOLUTE_PERCENTAGE_VALUES = "AutoScaleAbsolutePercentageValues"; 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_DISPLAY_POSITIVE = "DisplayPositiveData"; static const AString XML_TAG_DISPLAY_NEGATIVE = "DisplayNegativeData"; static const AString XML_TAG_DISPLAY_ZERO = "DisplayZeroData"; static const AString XML_TAG_THRESHOLD_TEST = "ThresholdTest"; static const AString XML_TAG_THRESHOLD_TYPE = "ThresholdType"; static const AString XML_TAG_THRESHOLD_NORMAL_VALUES = "ThresholdNormalValues"; static const AString XML_TAG_THRESHOLD_MAPPED_VALUES = "ThresholdMappedValues"; static const AString XML_TAG_THRESHOLD_MAPPED_AVG_AREA_VALUES = "ThresholdMappedAvgAreaValues"; static const AString XML_TAG_THRESHOLD_DATA_NAME = "ThresholdDataName"; 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_NUMERIC_FORMAT_MODE = "NumericFormatMode"; static const AString XML_TAG_PRECISION_DIGITS = "PrecisionDigits"; static const AString XML_TAG_NUMERIC_SUBDIVISIONS = "NumericSubivisions"; static const AString XML_TAG_COLOR_BAR_VALUES_MODE = "ColorBarValuesMode"; static const AString XML_TAG_SHOW_TICK_MARKS = "ShowTickMarksSelected"; static const AString XML_ATTRIBUTE_VERSION_NUMBER = "Version"; static const int XML_VERSION_NUMBER = 1; } // namespace #endif // __PALETTE_COLOR_MAPPING_XML_ELEMENTS_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Palette/PaletteEnums.cxx000066400000000000000000000470011300200146000252760ustar00rootroot00000000000000/*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 __PALETTE_ENUMS_DECLARE__ #include "PaletteEnums.h" #undef __PALETTE_ENUMS_DECLARE__ #include "CaretAssert.h" using namespace caret; /** * Constructor. * * @param e * An enumerated value. * @param name * Name of enumberated value. */ PaletteScaleModeEnum::PaletteScaleModeEnum( const Enum e, const int32_t integerCode, const AString& name, const AString& guiName) { this->e = e; this->integerCode = integerCode; this->name = name; this->guiName = guiName; } /** * Destructor. */ PaletteScaleModeEnum::~PaletteScaleModeEnum() { } void PaletteScaleModeEnum::initialize() { if (initializedFlag) { return; } initializedFlag = true; enumData.push_back(PaletteScaleModeEnum(MODE_AUTO_SCALE, 0, "MODE_AUTO_SCALE", "Auto Scale")); enumData.push_back(PaletteScaleModeEnum(MODE_AUTO_SCALE_ABSOLUTE_PERCENTAGE, 1, "MODE_AUTO_SCALE_ABSOLUTE_PERCENTAGE", "Auto Scale - Absolute Percentage")); enumData.push_back(PaletteScaleModeEnum(MODE_AUTO_SCALE_PERCENTAGE, 2, "MODE_AUTO_SCALE_PERCENTAGE", "Auto Scale - Percentage")); enumData.push_back(PaletteScaleModeEnum(MODE_USER_SCALE, 3, "MODE_USER_SCALE", "User Scale")); } /** * Find the data for and enumerated value. * @param e * The enumerated value. * @return Pointer to data for this enumerated type * or NULL if no data for type or if type is invalid. */ const PaletteScaleModeEnum* PaletteScaleModeEnum::findData(const Enum e) { initialize(); int64_t num = enumData.size(); for (int64_t i = 0; i < num; i++) { const PaletteScaleModeEnum* d = &enumData[i]; if (d->e == e) { return d; } } return NULL; } /** * Get a string representation of the enumerated type. * @param e * Enumerated value. * @return * String representing enumerated value. */ AString PaletteScaleModeEnum::toName(Enum e) { initialize(); const PaletteScaleModeEnum* psm = findData(e); return psm->name; } /** * Get an enumerated value corresponding to its 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. */ PaletteScaleModeEnum::Enum PaletteScaleModeEnum::fromName(const AString& s, bool* isValidOut) { initialize(); bool validFlag = false; Enum e = MODE_AUTO_SCALE; for (std::vector::iterator iter = enumData.begin(); iter != enumData.end(); iter++) { const PaletteScaleModeEnum& d = *iter; if (d.name == s) { e = d.e; validFlag = true; break; } } if (isValidOut != 0) { *isValidOut = validFlag; } else if (validFlag == false) { CaretAssertMessage(0, AString("name \"" + s + " \"failed to match enumerated value for type PaletteScaleModeEnum")); } return e; } /** * Get a gui name representation of the enumerated type. * @param e * Enumerated value. * @return * String representing enumerated value. */ AString PaletteScaleModeEnum::toGuiName(Enum e) { initialize(); const PaletteScaleModeEnum* psm = findData(e); return psm->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. */ PaletteScaleModeEnum::Enum PaletteScaleModeEnum::fromGuiName(const AString& s, bool* isValidOut) { initialize(); bool validFlag = false; Enum e = MODE_AUTO_SCALE; for (std::vector::iterator iter = enumData.begin(); iter != enumData.end(); iter++) { const PaletteScaleModeEnum& d = *iter; if (d.guiName == s) { e = d.e; validFlag = true; break; } } if (isValidOut != 0) { *isValidOut = validFlag; } else if (validFlag == false) { CaretAssertMessage(0, AString("guiName \"" + s + " \"failed to match enumerated value for type PaletteScaleModeEnum")); } return e; } /** * Get the integer code associated with a scale mode. * @param e * The enum. * @return * Integer code associated with a scale mode. */ int32_t PaletteScaleModeEnum::toIntegerCode(Enum e) { initialize(); const PaletteScaleModeEnum* nsu = findData(e); return nsu->integerCode; } /** * Find enum corresponding to integer code. * @param integerCode * The integer code. * @param isValidOut * If not NULL, on exit it indicates valid integer code. * @return * Enum corresponding to integer code. */ PaletteScaleModeEnum::Enum PaletteScaleModeEnum::fromIntegerCode(const int32_t integerCode, bool* isValidOut) { initialize(); bool validFlag = false; Enum e = MODE_AUTO_SCALE; for (std::vector::const_iterator iter = enumData.begin(); iter != enumData.end(); iter++) { const PaletteScaleModeEnum& nsu = *iter; if (nsu.integerCode == integerCode) { e = nsu.e; validFlag = true; break; } } if (isValidOut != 0) { *isValidOut = validFlag; } else if (validFlag == false) { CaretAssertMessage(0, AString("integerCode \"" + AString::number(integerCode) + " \"failed to match enumerated value for type PaletteScaleModeEnum")); } return e; } /** * Get all enums for the data type. * @param enumsOut * Loaded with all enums for this data type. */ void PaletteScaleModeEnum::getAllEnums(std::vector< PaletteScaleModeEnum::Enum >& enumsOut) { initialize(); enumsOut.resize(enumData.size()); for (int i = 0; i < (int)enumData.size(); ++i) { enumsOut[i] = enumData[i].e; } } /** * Constructor. * * @param e * An enumerated value. * @param name * Name of enumberated value. */ PaletteThresholdTestEnum::PaletteThresholdTestEnum( const Enum e, const int32_t integerCode, const AString& name, const AString& guiName) { this->e = e; this->integerCode = integerCode; this->name = name; this->guiName = guiName; } /** * Destructor. */ PaletteThresholdTestEnum::~PaletteThresholdTestEnum() { } void PaletteThresholdTestEnum::initialize() { if (initializedFlag) { return; } initializedFlag = true; enumData.push_back(PaletteThresholdTestEnum(THRESHOLD_TEST_SHOW_OUTSIDE, 0, "THRESHOLD_TEST_SHOW_OUTSIDE", "Show Data Outside Thresholds")); enumData.push_back(PaletteThresholdTestEnum(THRESHOLD_TEST_SHOW_INSIDE, 1, "THRESHOLD_TEST_SHOW_INSIDE", "Show Data Below Threshold")); } /** * Find the data for and enumerated value. * @param e * The enumerated value. * @return Pointer to data for this enumerated type * or NULL if no data for type or if type is invalid. */ const PaletteThresholdTestEnum* PaletteThresholdTestEnum::findData(const Enum e) { initialize(); int64_t num = enumData.size(); for (int64_t i = 0; i < num; i++) { const PaletteThresholdTestEnum* d = &enumData[i]; if (d->e == e) { return d; } } CaretAssertMessage(0, "Threshold Test enum failed to match."); return NULL; } /** * Get a string representation of the enumerated type. * @param e * Enumerated value. * @return * String representing enumerated value. */ AString PaletteThresholdTestEnum::toName(Enum e) { initialize(); const PaletteThresholdTestEnum* ptt = findData(e); return ptt->name; } /** * Get an enumerated value corresponding to its name. * @param sin * 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. */ PaletteThresholdTestEnum::Enum PaletteThresholdTestEnum::fromName(const AString& sin, bool* isValidOut) { initialize(); bool validFlag = false; Enum e = THRESHOLD_TEST_SHOW_OUTSIDE; AString s = sin; if (s == "THRESHOLD_TEST_SHOW_ABOVE") { s = "THRESHOLD_TEST_SHOW_OUTSIDE"; } else if (s == "THRESHOLD_TEST_SHOW_BELOW") { s = "THRESHOLD_TEST_SHOW_INSIDE"; } for (std::vector::iterator iter = enumData.begin(); iter != enumData.end(); iter++) { const PaletteThresholdTestEnum& d = *iter; if (d.name == s) { e = d.e; validFlag = true; break; } } if (isValidOut != 0) { *isValidOut = validFlag; } else if (validFlag == false) { CaretAssertMessage(0, AString("name \"" + s + " \"failed to match enumerated value for type PaletteThresholdTestEnum")); } return e; } /** * Get a gui name representation of the enumerated type. * @param e * Enumerated value. * @return * String representing enumerated value. */ AString PaletteThresholdTestEnum::toGuiName(Enum e) { initialize(); const PaletteThresholdTestEnum* psm = findData(e); return psm->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. */ PaletteThresholdTestEnum::Enum PaletteThresholdTestEnum::fromGuiName(const AString& s, bool* isValidOut) { initialize(); bool validFlag = false; Enum e = THRESHOLD_TEST_SHOW_OUTSIDE; for (std::vector::iterator iter = enumData.begin(); iter != enumData.end(); iter++) { const PaletteThresholdTestEnum& d = *iter; if (d.guiName == s) { e = d.e; validFlag = true; break; } } if (isValidOut != 0) { *isValidOut = validFlag; } else if (validFlag == false) { CaretAssertMessage(0, AString("guiName \"" + s + " \"failed to match enumerated value for type PaletteThresholdTestEnum")); } return e; } /** * Get the integer code associated with a scale mode. * @param e * The enum. * @return * Integer code associated with a scale mode. */ int32_t PaletteThresholdTestEnum::toIntegerCode(Enum e) { initialize(); const PaletteThresholdTestEnum* nsu = findData(e); return nsu->integerCode; } /** * Find enum corresponding to integer code. * @param integerCode * The integer code. * @param isValidOut * If not NULL, on exit it indicates valid integer code. * @return * Enum corresponding to integer code. */ PaletteThresholdTestEnum::Enum PaletteThresholdTestEnum::fromIntegerCode(const int32_t integerCode, bool* isValidOut) { initialize(); bool validFlag = false; Enum e = THRESHOLD_TEST_SHOW_OUTSIDE; for (std::vector::const_iterator iter = enumData.begin(); iter != enumData.end(); iter++) { const PaletteThresholdTestEnum& nsu = *iter; if (nsu.integerCode == integerCode) { e = nsu.e; validFlag = true; break; } } if (isValidOut != 0) { *isValidOut = validFlag; } else if (validFlag == false) { CaretAssertMessage(0, AString("integerCode \"" + AString::number(integerCode) + " \"failed to match enumerated value for type PaletteThresholdTestEnum")); } return e; } /** * Constructor. * * @param e * An enumerated value. * @param name * Name of enumberated value. */ PaletteThresholdTypeEnum::PaletteThresholdTypeEnum( const Enum e, const int32_t integerCode, const AString& name, const AString& guiName) { this->e = e; this->integerCode = integerCode; this->name = name; this->guiName = guiName; } /** * Destructor. */ PaletteThresholdTypeEnum::~PaletteThresholdTypeEnum() { } void PaletteThresholdTypeEnum::initialize() { if (initializedFlag) { return; } initializedFlag = true; enumData.push_back(PaletteThresholdTypeEnum(THRESHOLD_TYPE_OFF, 0, "THRESHOLD_TYPE_OFF", "Off")); if (PaletteThresholdTypeEnum::mappedThresholdsEnabled) { enumData.push_back(PaletteThresholdTypeEnum(THRESHOLD_TYPE_NORMAL, 1, "THRESHOLD_TYPE_NORMAL", "Normal")); enumData.push_back(PaletteThresholdTypeEnum(THRESHOLD_TYPE_MAPPED, 2, "THRESHOLD_TYPE_MAPPED", "Mapped")); 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")); } } /** * Find the data for and enumerated value. * @param e * The enumerated value. * @return Pointer to data for this enumerated type * or NULL if no data for type or if type is invalid. */ const PaletteThresholdTypeEnum* PaletteThresholdTypeEnum::findData(const Enum e) { initialize(); int64_t num = enumData.size(); for (int64_t i = 0; i < num; i++) { const PaletteThresholdTypeEnum* d = &enumData[i]; if (d->e == e) { return d; } } return NULL; } /** * Get a string representation of the enumerated type. * @param e * Enumerated value. * @return * String representing enumerated value. */ AString PaletteThresholdTypeEnum::toName(Enum e) { initialize(); const PaletteThresholdTypeEnum* ptt = findData(e); return ptt->name; } /** * Get an enumerated value corresponding to its 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. */ PaletteThresholdTypeEnum::Enum PaletteThresholdTypeEnum::fromName(const AString& s, bool* isValidOut) { initialize(); bool validFlag = false; Enum e = THRESHOLD_TYPE_OFF; for (std::vector::iterator iter = enumData.begin(); iter != enumData.end(); iter++) { const PaletteThresholdTypeEnum& d = *iter; if (d.name == s) { e = d.e; validFlag = true; break; } } PaletteThresholdTypeEnum::handleDisabledThresholdTypes(e); if (isValidOut != 0) { *isValidOut = validFlag; } else if (validFlag == false) { CaretAssertMessage(0, AString("name \"" + s + " \"failed to match enumerated value for type PaletteThresholdTypeEnum")); } return e; } /** * If the mapped threshold types are disabled, convert * them to normal thresholding. * @param e * Thresholding type that may be changed. */ void PaletteThresholdTypeEnum::handleDisabledThresholdTypes(Enum& e) { if (PaletteThresholdTypeEnum::mappedThresholdsEnabled == false) { if ((e == THRESHOLD_TYPE_MAPPED) || (e == THRESHOLD_TYPE_MAPPED_AVERAGE_AREA)) { e = THRESHOLD_TYPE_NORMAL; } } } /** * Get a gui name representation of the enumerated type. * @param e * Enumerated value. * @return * String representing enumerated value. */ AString PaletteThresholdTypeEnum::toGuiName(Enum e) { initialize(); const PaletteThresholdTypeEnum* psm = findData(e); return psm->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. */ PaletteThresholdTypeEnum::Enum PaletteThresholdTypeEnum::fromGuiName(const AString& s, bool* isValidOut) { initialize(); bool validFlag = false; Enum e = THRESHOLD_TYPE_OFF; for (std::vector::iterator iter = enumData.begin(); iter != enumData.end(); iter++) { const PaletteThresholdTypeEnum& d = *iter; if (d.guiName == s) { e = d.e; validFlag = true; break; } } PaletteThresholdTypeEnum::handleDisabledThresholdTypes(e); if (isValidOut != 0) { *isValidOut = validFlag; } else if (validFlag == false) { CaretAssertMessage(0, AString("guiName \"" + s + " \"failed to match enumerated value for type PaletteThresholdTypeEnum")); } return e; } /** * Get the integer code associated with a threshold type. * @param e * The enum. * @return * Integer code associated with a threshold type. */ int32_t PaletteThresholdTypeEnum::toIntegerCode(Enum e) { initialize(); const PaletteThresholdTypeEnum* nsu = findData(e); return nsu->integerCode; } /** * Find enum corresponding to integer code. * @param integerCode * The integer code. * @param isValidOut * If not NULL, on exit it indicates valid integer code. * @return * Enum corresponding to integer code. */ PaletteThresholdTypeEnum::Enum PaletteThresholdTypeEnum::fromIntegerCode(const int32_t integerCode, bool* isValidOut) { initialize(); bool validFlag = false; Enum e = THRESHOLD_TYPE_OFF; for (std::vector::const_iterator iter = enumData.begin(); iter != enumData.end(); iter++) { const PaletteThresholdTypeEnum& nsu = *iter; if (nsu.integerCode == integerCode) { e = nsu.e; validFlag = true; break; } } PaletteThresholdTypeEnum::handleDisabledThresholdTypes(e); if (isValidOut != 0) { *isValidOut = validFlag; } else if (validFlag == false) { CaretAssertMessage(0, AString("integerCode \"" + AString::number(integerCode) + " \"failed to match enumerated value for type PaletteThresholdTypeEnum")); } return e; } /** * Get all enums for the data type. * @param enumsOut * Loaded with all enums for this data type. */ void PaletteThresholdTestEnum::getAllEnums(std::vector& enumsOut) { initialize(); enumsOut.resize(enumData.size()); for (int i = 0; i < (int)enumData.size(); ++i) { enumsOut[i] = enumData[i].e; } } /** * Get all enums for the data type. * @param enumsOut * Loaded with all enums for this data type. */ void PaletteThresholdTypeEnum::getAllEnums(std::vector& enumsOut) { initialize(); enumsOut.resize(enumData.size()); for (int i = 0; i < (int)enumData.size(); ++i) { enumsOut[i] = enumData[i].e; } } connectome-workbench-1.2.3+git41-gc4c6c90/src/Palette/PaletteEnums.h000066400000000000000000000124701300200146000247250ustar00rootroot00000000000000#ifndef __PALETTE_ENUMS_H #define __PALETTE_ENUMS_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 "CaretAssert.h" namespace caret { /** * Palette Scale Mode. */ class PaletteScaleModeEnum { public: /** Palette Scale Mode. */ enum Enum { /** Auto Scale */ MODE_AUTO_SCALE, /** Auto Scale Absolute Percentage */ MODE_AUTO_SCALE_ABSOLUTE_PERCENTAGE, /** Auto Scale Percentage */ MODE_AUTO_SCALE_PERCENTAGE, /** User Scale */ MODE_USER_SCALE }; ~PaletteScaleModeEnum(); static AString toName(Enum e); static Enum fromName(const AString& s, bool* isValidOut); static AString toGuiName(Enum e); static Enum fromGuiName(const AString& s, bool* isValidOut); static int32_t toIntegerCode(Enum e); static Enum fromIntegerCode(const int32_t integerCode, bool* isValidOut); static void getAllEnums(std::vector& enumsOut); private: PaletteScaleModeEnum(const Enum e, const int32_t integerCode, const AString& name, const AString& guiName); static const PaletteScaleModeEnum* findData(const Enum e); static std::vector enumData; static void initialize(); static bool initializedFlag; Enum e; int32_t integerCode; AString name; AString guiName; }; /** * Palette Threshold Test. */ class PaletteThresholdTestEnum { public: /** Palette Threshold Test. */ enum Enum { /** show data when value is outside the threshold values */ THRESHOLD_TEST_SHOW_OUTSIDE, /** show data when value is inside the threshold values */ THRESHOLD_TEST_SHOW_INSIDE }; ~PaletteThresholdTestEnum(); static AString toName(Enum e); static Enum fromName(const AString& s, bool* isValidOut); static AString toGuiName(Enum e); static Enum fromGuiName(const AString& s, bool* isValidOut); static int32_t toIntegerCode(Enum e); static Enum fromIntegerCode(const int32_t integerCode, bool* isValidOut); static void getAllEnums(std::vector& enumsOut); private: PaletteThresholdTestEnum(const Enum e, const int32_t integerCode, const AString& name, const AString& guiName); static const PaletteThresholdTestEnum* findData(const Enum e); static std::vector enumData; static void initialize(); static bool initializedFlag; Enum e; int32_t integerCode; AString name; AString guiName; }; /** * Palette Threshold Type. */ class PaletteThresholdTypeEnum { public: /** Palette Threshold Type. */ enum Enum { /** thresholding is off */ THRESHOLD_TYPE_OFF, /** normal thresholding */ THRESHOLD_TYPE_NORMAL, /** threshold from mapping of volume */ THRESHOLD_TYPE_MAPPED, /** threshold from mapping to PALS average area */ THRESHOLD_TYPE_MAPPED_AVERAGE_AREA }; ~PaletteThresholdTypeEnum(); static AString toName(Enum e); static Enum fromName(const AString& s, bool* isValidOut); static AString toGuiName(Enum e); static Enum fromGuiName(const AString& s, bool* isValidOut); static int32_t toIntegerCode(Enum e); static Enum fromIntegerCode(const int32_t integerCode, bool* isValidOut); static void getAllEnums(std::vector& enumsOut); private: PaletteThresholdTypeEnum(const Enum e, const int32_t integerCode, const AString& name, const AString& guiName); static const PaletteThresholdTypeEnum* findData(const Enum e); static std::vector enumData; static void initialize(); static bool initializedFlag; static void handleDisabledThresholdTypes(Enum& e); Enum e; int32_t integerCode; AString name; AString guiName; static const bool mappedThresholdsEnabled; }; #ifdef __PALETTE_ENUMS_DECLARE__ std::vector PaletteScaleModeEnum::enumData; bool PaletteScaleModeEnum::initializedFlag = false; std::vector PaletteThresholdTestEnum::enumData; bool PaletteThresholdTestEnum::initializedFlag = false; std::vector PaletteThresholdTypeEnum::enumData; bool PaletteThresholdTypeEnum::initializedFlag = false; const bool PaletteThresholdTypeEnum::mappedThresholdsEnabled = false; #endif // __PALETTE_ENUMS_DECLARE__ } // namespace #endif // __PALETTE_ENUMS_H connectome-workbench-1.2.3+git41-gc4c6c90/src/Palette/PaletteNormalizationModeEnum.cxx000066400000000000000000000277501300200146000305000ustar00rootroot00000000000000 /*LICENSE_START*/ /* * Copyright (C) 2015 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_NORMALIZATION_MODE_ENUM_DECLARE__ #include "PaletteNormalizationModeEnum.h" #undef __PALETTE_NORMALIZATION_MODE_ENUM_DECLARE__ #include "CaretAssert.h" using namespace caret; /** * \class caret::PaletteNormalizationModeEnum * \brief Enumerated type for normalization of file/map data. * * 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_paletteNormalizationModeEnumComboBox; * * Declare a slot that is called when user changes selection * private slots: * void paletteNormalizationModeEnumComboBoxItemActivated(); * * Implementation File (.cxx) * Include the header files * #include "EnumComboBoxTemplate.h" * #include "PaletteNormalizationModeEnum.h" * * Instatiate: * m_paletteNormalizationModeEnumComboBox = new EnumComboBoxTemplate(this); * m_paletteNormalizationModeEnumComboBox->setup(); * * Get notified when the user changes the selection: * QObject::connect(m_paletteNormalizationModeEnumComboBox, SIGNAL(itemActivated()), * this, SLOT(paletteNormalizationModeEnumComboBoxItemActivated())); * * Update the selection: * m_paletteNormalizationModeEnumComboBox->setSelectedItem(NEW_VALUE); * * Read the selection: * const PaletteNormalizationModeEnum::Enum VARIABLE = m_paletteNormalizationModeEnumComboBox->getSelectedItem(); * */ /** * Constructor. * * @param enumValue * An enumerated value. * @param name * Name of enumerated value. * * @param guiName * User-friendly name for use in user-interface. */ PaletteNormalizationModeEnum::PaletteNormalizationModeEnum(const Enum enumValue, const AString& name, const AString& guiName) { this->enumValue = enumValue; this->integerCode = integerCodeCounter++; this->name = name; this->guiName = guiName; } /** * Destructor. */ PaletteNormalizationModeEnum::~PaletteNormalizationModeEnum() { } /** * Initialize the enumerated metadata. */ void PaletteNormalizationModeEnum::initialize() { if (initializedFlag) { return; } initializedFlag = true; enumData.push_back(PaletteNormalizationModeEnum(NORMALIZATION_ALL_MAP_DATA, "NORMALIZATION_ALL_MAP_DATA", "All Maps In File")); enumData.push_back(PaletteNormalizationModeEnum(NORMALIZATION_SELECTED_MAP_DATA, "NORMALIZATION_SELECTED_MAP_DATA", "Selected Map In File")); } /** * 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 PaletteNormalizationModeEnum* PaletteNormalizationModeEnum::findData(const Enum enumValue) { if (initializedFlag == false) initialize(); size_t num = enumData.size(); for (size_t i = 0; i < num; i++) { const PaletteNormalizationModeEnum* 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 PaletteNormalizationModeEnum::toName(Enum enumValue) { if (initializedFlag == false) initialize(); const PaletteNormalizationModeEnum* 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. */ PaletteNormalizationModeEnum::Enum PaletteNormalizationModeEnum::fromName(const AString& name, bool* isValidOut) { if (initializedFlag == false) initialize(); bool validFlag = false; Enum enumValue = PaletteNormalizationModeEnum::enumData[0].enumValue; for (std::vector::iterator iter = enumData.begin(); iter != enumData.end(); iter++) { const PaletteNormalizationModeEnum& 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 PaletteNormalizationModeEnum")); } return enumValue; } /** * Get a GUI string representation of the enumerated type. * @param enumValue * Enumerated value. * @return * String representing enumerated value. */ AString PaletteNormalizationModeEnum::toGuiName(Enum enumValue) { if (initializedFlag == false) initialize(); const PaletteNormalizationModeEnum* 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. */ PaletteNormalizationModeEnum::Enum PaletteNormalizationModeEnum::fromGuiName(const AString& guiName, bool* isValidOut) { if (initializedFlag == false) initialize(); bool validFlag = false; Enum enumValue = PaletteNormalizationModeEnum::enumData[0].enumValue; for (std::vector::iterator iter = enumData.begin(); iter != enumData.end(); iter++) { const PaletteNormalizationModeEnum& 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 PaletteNormalizationModeEnum")); } return enumValue; } /** * Get the integer code for a data type. * * @return * Integer code for data type. */ int32_t PaletteNormalizationModeEnum::toIntegerCode(Enum enumValue) { if (initializedFlag == false) initialize(); const PaletteNormalizationModeEnum* 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. */ PaletteNormalizationModeEnum::Enum PaletteNormalizationModeEnum::fromIntegerCode(const int32_t integerCode, bool* isValidOut) { if (initializedFlag == false) initialize(); bool validFlag = false; Enum enumValue = PaletteNormalizationModeEnum::enumData[0].enumValue; for (std::vector::iterator iter = enumData.begin(); iter != enumData.end(); iter++) { const PaletteNormalizationModeEnum& 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 PaletteNormalizationModeEnum")); } 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 PaletteNormalizationModeEnum::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 PaletteNormalizationModeEnum::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(PaletteNormalizationModeEnum::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 PaletteNormalizationModeEnum::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(PaletteNormalizationModeEnum::toGuiName(iter->enumValue)); } if (isSorted) { std::sort(allGuiNames.begin(), allGuiNames.end()); } } AString PaletteNormalizationModeEnum::getEnumToolTopInHTML() { const AString msg("" "Normalization controls how data values are mapped to the color palette. " "The data's most negative, zero, and most positive values are mapped to " "the palette's -1.0, 0.0, and 1.0 colors.

" "" + toGuiName(PaletteNormalizationModeEnum::NORMALIZATION_ALL_MAP_DATA) + "
" + " Uses data from all maps within the file and results in identical data " "values from any map in the file receiving identical coloring.

" "" + toGuiName(PaletteNormalizationModeEnum::NORMALIZATION_SELECTED_MAP_DATA) + "
" + " Uses data from the selected map and so identical data values " " in different maps may receive different coloring.
" ""); return msg; } connectome-workbench-1.2.3+git41-gc4c6c90/src/Palette/PaletteNormalizationModeEnum.h000066400000000000000000000065211300200146000301160ustar00rootroot00000000000000#ifndef __PALETTE_NORMALIZATION_MODE_ENUM_H__ #define __PALETTE_NORMALIZATION_MODE_ENUM_H__ /*LICENSE_START*/ /* * Copyright (C) 2015 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 PaletteNormalizationModeEnum { public: /** * Enumerated values. */ enum Enum { /** Use data from all maps in file for normalization */ NORMALIZATION_ALL_MAP_DATA, /** Use data from selected map for normalization */ NORMALIZATION_SELECTED_MAP_DATA }; ~PaletteNormalizationModeEnum(); 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 getEnumToolTopInHTML(); private: PaletteNormalizationModeEnum(const Enum enumValue, const AString& name, const AString& guiName); static const PaletteNormalizationModeEnum* 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_NORMALIZATION_MODE_ENUM_DECLARE__ std::vector PaletteNormalizationModeEnum::enumData; bool PaletteNormalizationModeEnum::initializedFlag = false; int32_t PaletteNormalizationModeEnum::integerCodeCounter = 0; #endif // __PALETTE_NORMALIZATION_MODE_ENUM_DECLARE__ } // namespace #endif //__PALETTE_NORMALIZATION_MODE_ENUM_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Palette/PaletteScalarAndColor.cxx000066400000000000000000000113711300200146000270370ustar00rootroot00000000000000/*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 "PaletteScalarAndColor.h" using namespace caret; /** * Constructor. * * @param scalar - the scalar value * @param colorIndex - the scalar's color index * */ PaletteScalarAndColor::PaletteScalarAndColor( const float scalar, const AString& colorName) : CaretObject() { this->initializeMembersPaletteScalarAndColor(); this->scalar = scalar; setColorName(colorName); } /** * Destructor */ PaletteScalarAndColor::~PaletteScalarAndColor() { } /** * Copy Constructor * @param Object that is copied. */ PaletteScalarAndColor::PaletteScalarAndColor(const PaletteScalarAndColor& o) : CaretObject(o), TracksModificationInterface() { this->initializeMembersPaletteScalarAndColor(); this->copyHelper(o); } /** * Assignment operator. */ PaletteScalarAndColor& PaletteScalarAndColor::operator=(const PaletteScalarAndColor& o) { if (this != &o) { CaretObject::operator=(o); this->copyHelper(o); }; return *this; } /** * Helps with copy constructor and assignment operator. */ void PaletteScalarAndColor::copyHelper(const PaletteScalarAndColor& o) { this->scalar = o.scalar; this->colorName = o.colorName; this->noneColorFlag = o.noneColorFlag; this->rgba[0] = o.rgba[0]; this->rgba[1] = o.rgba[1]; this->rgba[2] = o.rgba[2]; this->rgba[3] = o.rgba[3]; this->clearModified(); } void PaletteScalarAndColor::initializeMembersPaletteScalarAndColor() { this->modifiedFlag = false; this->scalar = 0.0f; this->colorName = ""; this->noneColorFlag = false; this->rgba[0] = 1.0f; this->rgba[1] = 1.0f; this->rgba[2] = 1.0f; this->rgba[3] = 1.0f; } /** * Set the scalar. * * @param scalar - new value for scalar. * */ void PaletteScalarAndColor::setScalar(const float scalar) { if (this->scalar != scalar) { this->scalar = scalar; this->setModified(); } } /** * Set the name of the color for this scalar. * @param colorName * New name for color. */ void PaletteScalarAndColor::setColorName(const AString& colorName) { if (this->colorName != colorName) { this->colorName = colorName; this->noneColorFlag = (colorName == "none"); this->setModified(); } } /** * Get the RGBA components of the color. * @param rgbaOut * float array with red, green, blue, alpha * components ranging 0 to 1 are loaded into. */ void PaletteScalarAndColor::getColor(float rgbaOut[4]) const { rgbaOut[0] = this->rgba[0]; rgbaOut[1] = this->rgba[1]; rgbaOut[2] = this->rgba[2]; rgbaOut[3] = this->rgba[3]; } /** * Set the color's RGBA components. * @param rgba * New components for RGBA color. */ void PaletteScalarAndColor::setColor(const float rgba[4]) { if ((this->rgba[0] != rgba[0]) || (this->rgba[1] != rgba[1]) || (this->rgba[2] != rgba[2]) || (this->rgba[3] != rgba[3])) { this->rgba[0] = rgba[0]; this->rgba[1] = rgba[1]; this->rgba[2] = rgba[2]; this->rgba[3] = rgba[3]; this->setModified(); } } /** * Get string representation for debugging. * * @return A string. * */ AString PaletteScalarAndColor::toString() const { AString s = "[colorName=" + this->colorName + ", scale=" + AString::number(this->scalar) + ", rgba=" + AString::fromNumbers(rgba, 4, ",") + "]"; return s; } /** * Set this object has been modified. * */ void PaletteScalarAndColor::setModified() { this->modifiedFlag = true; } /** * Set this object as not modified. Object should also * clear the modification status of its children. * */ void PaletteScalarAndColor::clearModified() { this->modifiedFlag = false; } /** * Get the modification status. Returns true if this object or * any of its children have been modified. * @return - The modification status. * */ bool PaletteScalarAndColor::isModified() const { return this->modifiedFlag; } connectome-workbench-1.2.3+git41-gc4c6c90/src/Palette/PaletteScalarAndColor.h000066400000000000000000000057121300200146000264660ustar00rootroot00000000000000#ifndef __PALETTESCALARANDCOLOR_H__ #define __PALETTESCALARANDCOLOR_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 "TracksModificationInterface.h" #include #include namespace caret { /** * Contains information about the color assigned to the scalar * value. A palette contains a set of these objects. */ class PaletteScalarAndColor : public CaretObject, TracksModificationInterface { public: PaletteScalarAndColor(const float scalar, const AString& colorName); PaletteScalarAndColor(const PaletteScalarAndColor& o); PaletteScalarAndColor& operator=(const PaletteScalarAndColor& o); virtual ~PaletteScalarAndColor(); private: void copyHelper(const PaletteScalarAndColor& o); void initializeMembersPaletteScalarAndColor(); public: /** * @return The scalar */ inline float getScalar() const { return this->scalar; } void setScalar(const float scalar); /** * Get the name of the color. * @return * Name of the color assigned to the scalar. */ inline const AString& getColorName() const { return this->colorName; } void setColorName(const AString& colorName); /** * @return float array with red, green, blue, alpha color components ranging 0 to 1. */ inline const float* getColor() const { return rgba; } void getColor(float rgbaOut[4]) const; void setColor(const float rgba[4]); AString toString() const; void setModified(); void clearModified(); bool isModified() const; /** * @return Is the color the 'none' color meaning * that no coloring is applied? */ inline bool isNoneColor() const { return this->noneColorFlag; } private: /** has this object been modified. (DO NOT CLONE) */ bool modifiedFlag; /** the scalar value */ float scalar; /** the color's name, use the setName() method so that none color flag is updated */ AString colorName; /** the color's rgba components */ float rgba[4]; bool noneColorFlag; }; } // namespace #endif // __PALETTESCALARANDCOLOR_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Palette/PaletteThresholdRangeModeEnum.cxx000066400000000000000000000233751300200146000305620ustar00rootroot00000000000000 /*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 __PALETTE_THRESHOLD_RANGE_MODE_ENUM_DECLARE__ #include "PaletteThresholdRangeModeEnum.h" #undef __PALETTE_THRESHOLD_RANGE_MODE_ENUM_DECLARE__ #include "CaretAssert.h" using namespace caret; /** * \class caret::PaletteThresholdRangeModeEnum * \brief * * */ /** * Constructor. * * @param enumValue * An enumerated value. * @param name * Name of enumerated value. * * @param guiName * User-friendly name for use in user-interface. */ PaletteThresholdRangeModeEnum::PaletteThresholdRangeModeEnum(const Enum enumValue, const AString& name, const AString& guiName) { this->enumValue = enumValue; this->integerCode = integerCodeCounter++; this->name = name; this->guiName = guiName; } /** * Destructor. */ PaletteThresholdRangeModeEnum::~PaletteThresholdRangeModeEnum() { } /** * Initialize the enumerated metadata. */ void PaletteThresholdRangeModeEnum::initialize() { if (initializedFlag) { return; } initializedFlag = true; enumData.push_back(PaletteThresholdRangeModeEnum(PALETTE_THRESHOLD_RANGE_MODE_FILE, "PALETTE_THRESHOLD_RANGE_MODE_FILE", "File")); enumData.push_back(PaletteThresholdRangeModeEnum(PALETTE_THRESHOLD_RANGE_MODE_MAP, "PALETTE_THRESHOLD_RANGE_MODE_MAP", "Map")); enumData.push_back(PaletteThresholdRangeModeEnum(PALETTE_THRESHOLD_RANGE_MODE_UNLIMITED, "PALETTE_THRESHOLD_RANGE_MODE_UNLIMITED", "Unlimited")); } /** * 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 PaletteThresholdRangeModeEnum* PaletteThresholdRangeModeEnum::findData(const Enum enumValue) { if (initializedFlag == false) initialize(); size_t num = enumData.size(); for (size_t i = 0; i < num; i++) { const PaletteThresholdRangeModeEnum* 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 PaletteThresholdRangeModeEnum::toName(Enum enumValue) { if (initializedFlag == false) initialize(); const PaletteThresholdRangeModeEnum* 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. */ PaletteThresholdRangeModeEnum::Enum PaletteThresholdRangeModeEnum::fromName(const AString& name, bool* isValidOut) { if (initializedFlag == false) initialize(); bool validFlag = false; Enum enumValue = PALETTE_THRESHOLD_RANGE_MODE_MAP; for (std::vector::iterator iter = enumData.begin(); iter != enumData.end(); iter++) { const PaletteThresholdRangeModeEnum& 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 PaletteThresholdRangeModeEnum")); } return enumValue; } /** * Get a GUI string representation of the enumerated type. * @param enumValue * Enumerated value. * @return * String representing enumerated value. */ AString PaletteThresholdRangeModeEnum::toGuiName(Enum enumValue) { if (initializedFlag == false) initialize(); const PaletteThresholdRangeModeEnum* 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. */ PaletteThresholdRangeModeEnum::Enum PaletteThresholdRangeModeEnum::fromGuiName(const AString& guiName, bool* isValidOut) { if (initializedFlag == false) initialize(); bool validFlag = false; Enum enumValue = PALETTE_THRESHOLD_RANGE_MODE_MAP; for (std::vector::iterator iter = enumData.begin(); iter != enumData.end(); iter++) { const PaletteThresholdRangeModeEnum& 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 PaletteThresholdRangeModeEnum")); } return enumValue; } /** * Get the integer code for a data type. * * @return * Integer code for data type. */ int32_t PaletteThresholdRangeModeEnum::toIntegerCode(Enum enumValue) { if (initializedFlag == false) initialize(); const PaletteThresholdRangeModeEnum* 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. */ PaletteThresholdRangeModeEnum::Enum PaletteThresholdRangeModeEnum::fromIntegerCode(const int32_t integerCode, bool* isValidOut) { if (initializedFlag == false) initialize(); bool validFlag = false; Enum enumValue = PALETTE_THRESHOLD_RANGE_MODE_MAP; for (std::vector::iterator iter = enumData.begin(); iter != enumData.end(); iter++) { const PaletteThresholdRangeModeEnum& 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 PaletteThresholdRangeModeEnum")); } 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 PaletteThresholdRangeModeEnum::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 PaletteThresholdRangeModeEnum::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(PaletteThresholdRangeModeEnum::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 PaletteThresholdRangeModeEnum::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(PaletteThresholdRangeModeEnum::toGuiName(iter->enumValue)); } if (isSorted) { std::sort(allGuiNames.begin(), allGuiNames.end()); } } connectome-workbench-1.2.3+git41-gc4c6c90/src/Palette/PaletteThresholdRangeModeEnum.h000066400000000000000000000067161300200146000302070ustar00rootroot00000000000000#ifndef __PALETTE_THRESHOLD_RANGE_MODE_ENUM_H__ #define __PALETTE_THRESHOLD_RANGE_MODE_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 PaletteThresholdRangeModeEnum { public: /** * Enumerated values. */ enum Enum { /** Threshold range is minimum and maximum values from file */ PALETTE_THRESHOLD_RANGE_MODE_FILE, /** Threshold range is minimum and maximum values from map */ PALETTE_THRESHOLD_RANGE_MODE_MAP, /** Threshold range is unlimited (minimum and maximum float values) */ PALETTE_THRESHOLD_RANGE_MODE_UNLIMITED, }; ~PaletteThresholdRangeModeEnum(); 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: PaletteThresholdRangeModeEnum(const Enum enumValue, const AString& name, const AString& guiName); static const PaletteThresholdRangeModeEnum* 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_THRESHOLD_RANGE_MODE_ENUM_DECLARE__ std::vector PaletteThresholdRangeModeEnum::enumData; bool PaletteThresholdRangeModeEnum::initializedFlag = false; int32_t PaletteThresholdRangeModeEnum::integerCodeCounter = 0; #endif // __PALETTE_THRESHOLD_RANGE_MODE_ENUM_DECLARE__ } // namespace #endif //__PALETTE_THRESHOLD_RANGE_MODE_ENUM_H__ connectome-workbench-1.2.3+git41-gc4c6c90/src/Quazip/000077500000000000000000000000001300200146000220155ustar00rootroot00000000000000connectome-workbench-1.2.3+git41-gc4c6c90/src/Quazip/CMakeLists.txt000066400000000000000000000021201300200146000245500ustar00rootroot00000000000000# # Name of project # PROJECT(Quazip) # # QT include files # SET(QT_DONT_USE_QTGUI TRUE) INCLUDE(${QT_USE_FILE}) SET(MOC_INPUT_HEADER_FILES quazipfile.h ) QT4_WRAP_CPP(MOC_SOURCE_FILES ${MOC_INPUT_HEADER_FILES}) # # Prevents dll linkage errors on windows # #IF(WIN32) # ADD_DEFINITIONS(-DQUAZIP_STATIC) # IF(MSVC) # ADD_DEFINITIONS(-DQUAZIP_STATIC) # #ADD_DEFINITIONS(-DQUAZIP_BUILD) # ENDIF(MSVC) #ELSE(WIN32) # ADD_DEFINITIONS(-DQUAZIP_STATIC) #ENDIF(WIN32) # # Create a library # ADD_LIBRARY(Quazip JlCompress.h crypt.h ioapi.h quaadler32.h quachecksum32.h quacrc32.h quagzipfile.h quaziodevice.h quazip.h quazip_global.h quazipdir.h quazipfile.h quazipfileinfo.h quazipnewinfo.h unzip.h zip.h ${MOC_SOURCE_FILES} JlCompress.cpp qioapi.cpp quaadler32.cpp quacrc32.cpp quagzipfile.cpp quaziodevice.cpp quazip.cpp quazipdir.cpp quazipfile.cpp quazipfileinfo.cpp quazipnewinfo.cpp unzip.c zip.c ) SET(QUAZIP_LIBRARIES Quazip PARENT_SCOPE) SET(QUAZIP_INCLUDE_DIRS ${CMAKE_CURRENT_SOURCE_DIR} PARENT_SCOPE) connectome-workbench-1.2.3+git41-gc4c6c90/src/Quazip/COPYING000066400000000000000000000604201300200146000230520ustar00rootroot00000000000000 GNU LESSER GENERAL PUBLIC LICENSE Version 2.1, February 1999 Copyright (C) 1991, 1999 Free Software Foundation, Inc. 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. [This is the first released version of the Lesser GPL. It also counts as the successor of the GNU Library Public License, version 2, hence the version number 2.1.] Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public Licenses are intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This license, the Lesser General Public License, applies to some specially designated software packages--typically libraries--of the Free Software Foundation and other authors who decide to use it. You can use it too, but we suggest you first think carefully about whether this license or the ordinary General Public License is the better strategy to use in any particular case, based on the explanations below. When we speak of free software, we are referring to freedom of use, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish); that you receive source code or can get it if you want it; that you can change the software and use pieces of it in new free programs; and that you are informed that you can do these things. To protect your rights, we need to make restrictions that forbid distributors to deny you these rights or to ask you to surrender these rights. These restrictions translate to certain responsibilities for you if you distribute copies of the library or if you modify it. For example, if you distribute copies of the library, whether gratis or for a fee, you must give the recipients all the rights that we gave you. You must make sure that they, too, receive or can get the source code. If you link other code with the library, you must provide complete object files to the recipients, so that they can relink them with the library after making changes to the library and recompiling it. And you must show them these terms so they know their rights. We protect your rights with a two-step method: (1) we copyright the library, and (2) we offer you this license, which gives you legal permission to copy, distribute and/or modify the library. To protect each distributor, we want to make it very clear that there is no warranty for the free library. Also, if the library is modified by someone else and passed on, the recipients should know that what they have is not the original version, so that the original author's reputation will not be affected by problems that might be introduced by others. Finally, software patents pose a constant threat to the existence of any free program. We wish to make sure that a company cannot effectively restrict the users of a free program by obtaining a restrictive license from a patent holder. Therefore, we insist that any patent license obtained for a version of the library must be consistent with the full freedom of use specified in this license. Most GNU software, including some libraries, is covered by the ordinary GNU General Public License. This license, the GNU Lesser General Public License, applies to certain designated libraries, and is quite different from the ordinary General Public License. We use this license for certain libraries in order to permit linking those libraries into non-free programs. When a program is linked with a library, whether statically or using a shared library, the combination of the two is legally speaking a combined work, a derivative of the original library. The ordinary General Public License therefore permits such linking only if the entire combination fits its criteria of freedom. The Lesser General Public License permits more lax criteria for linking other code with the library. We call this license the "Lesser" General Public License because it does Less to protect the user's freedom than the ordinary General Public License. It also provides other free software developers Less of an advantage over competing non-free programs. These disadvantages are the reason we use the ordinary General Public License for many libraries. However, the Lesser license provides advantages in certain special circumstances. For example, on rare occasions, there may be a special need to encourage the widest possible use of a certain library, so that it becomes a de-facto standard. To achieve this, non-free programs must be allowed to use the library. A more frequent case is that a free library does the same job as widely used non-free libraries. In this case, there is little to gain by limiting the free library to free software only, so we use the Lesser General Public License. In other cases, permission to use a particular library in non-free programs enables a greater number of people to use a large body of free software. For example, permission to use the GNU C Library in non-free programs enables many more people to use the whole GNU operating system, as well as its variant, the GNU/Linux operating system. Although the Lesser General Public License is Less protective of the users' freedom, it does ensure that the user of a program that is linked with the Library has the freedom and the wherewithal to run that program using a modified version of the Library. The precise terms and conditions for copying, distribution and modification follow. Pay close attention to the difference between a "work based on the library" and a "work that uses the library". The former contains code derived from the library, whereas the latter must be combined with the library in order to run. GNU LESSER GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License Agreement applies to any software library or other program which contains a notice placed by the copyright holder or other authorized party saying it may be distributed under the terms of this Lesser General Public License (also called "this License"). Each licensee is addressed as "you". A "library" means a collection of software functions and/or data prepared so as to be conveniently linked with application programs (which use some of those functions and data) to form executables. The "Library", below, refers to any such software library or work which has been distributed under these terms. A "work based on the Library" means either the Library or any derivative work under copyright law: that is to say, a work containing the Library or a portion of it, either verbatim or with modifications and/or translated straightforwardly into another language. (Hereinafter, translation is included without limitation in the term "modification".) "Source code" for a work means the preferred form of the work for making modifications to it. For a library, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the library. Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running a program using the Library is not restricted, and output from such a program is covered only if its contents constitute a work based on the Library (independent of the use of the Library in a tool for writing it). Whether that is true depends on what the Library does and what the program that uses the Library does. 1. You may copy and distribute verbatim copies of the Library's complete source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and distribute a copy of this License along with the Library. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Library or any portion of it, thus forming a work based on the Library, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) The modified work must itself be a software library. b) You must cause the files modified to carry prominent notices stating that you changed the files and the date of any change. c) You must cause the whole of the work to be licensed at no charge to all third parties under the terms of this License. d) If a facility in the modified Library refers to a function or a table of data to be supplied by an application program that uses the facility, other than as an argument passed when the facility is invoked, then you must make a good faith effort to ensure that, in the event an application does not supply such function or table, the facility still operates, and performs whatever part of its purpose remains meaningful. (For example, a function in a library to compute square roots has a purpose that is entirely well-defined independent of the application. Therefore, Subsection 2d requires that any application-supplied function or table used by this function must be optional: if the application does not supply it, the square root function must still compute square roots.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Library, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Library, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Library. In addition, mere aggregation of another work not based on the Library with the Library (or with a work based on the Library) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may opt to apply the terms of the ordinary GNU General Public License instead of this License to a given copy of the Library. To do this, you must alter all the notices that refer to this License, so that they refer to the ordinary GNU General Public License, version 2, instead of to this License. (If a newer version than version 2 of the ordinary GNU General Public License has appeared, then you can specify that version instead if you wish.) Do not make any other change in these notices. Once this change is made in a given copy, it is irreversible for that copy, so the ordinary GNU General Public License applies to all subsequent copies and derivative works made from that copy. This option is useful when you wish to copy part of the code of the Library into a program that is not a library. 4. You may copy and distribute the Library (or a portion or derivative of it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange. If distribution of object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place satisfies the requirement to distribute the source code, even though third parties are not compelled to copy the source along with the object code. 5. A program that contains no derivative of any portion of the Library, but is designed to work with the Library by being compiled or linked with it, is called a "work that uses the Library". Such a work, in isolation, is not a derivative work of the Library, and therefore falls outside the scope of this License. However, linking a "work that uses the Library" with the Library creates an executable that is a derivative of the Library (because it contains portions of the Library), rather than a "work that uses the library". The executable is therefore covered by this License. Section 6 states terms for distribution of such executables. When a "work that uses the Library" uses material from a header file that is part of the Library, the object code for the work may be a derivative work of the Library even though the source code is not. Whether this is true is especially significant if the work can be linked without the Library, or if the work is itself a library. The threshold for this to be true is not precisely defined by law. If such an object file uses only numerical parameters, data structure layouts and accessors, and small macros and small inline functions (ten lines or less in length), then the use of the object file is unrestricted, regardless of whether it is legally a derivative work. (Executables containing this object code plus portions of the Library will still fall under Section 6.) Otherwise, if the work is a derivative of the Library, you may distribute the object code for the work under the terms of Section 6. Any executables containing that work also fall under Section 6, whether or not they are linked directly with the Library itself. 6. As an exception to the Sections above, you may also combine or link a "work that uses the Library" with the Library to produce a work containing portions of the Library, and distribute that work under terms of your choice, provided that the terms permit modification of the work for the customer's own use and reverse engineering for debugging such modifications. You must give prominent notice with each copy of the work that the Library is used in it and that the Library and its use are covered by this License. You must supply a copy of this License. If the work during execution displays copyright notices, you must include the copyright notice for the Library among them, as well as a reference directing the user to the copy of this License. Also, you must do one of these things: a) Accompany the work with the complete corresponding machine-readable source code for the Library including whatever changes were used in the work (which must be distributed under Sections 1 and 2 above); and, if the work is an executable linked with the Library, with the complete machine-readable "work that uses the Library", as object code and/or source code, so that the user can modify the Library and then relink to produce a modified executable containing the modified Library. (It is understood that the user who changes the contents of definitions files in the Library will not necessarily be able to recompile the application to use the modified definitions.) b) Use a suitable shared library mechanism for linking with the Library. A suitable mechanism is one that (1) uses at run time a copy of the library already present on the user's computer system, rather than copying library functions into the executable, and (2) will operate properly with a modified version of the library, if the user installs one, as long as the modified version is interface-compatible with the version that the work was made with. c) Accompany the work with a written offer, valid for at least three years, to give the same user the materials specified in Subsection 6a, above, for a charge no more than the cost of performing this distribution. d) If distribution of the work is made by offering access to copy from a designated place, offer equivalent access to copy the above specified materials from the same place. e) Verify that the user has already received a copy of these materials or that you have already sent this user a copy. For an executable, the required form of the "work that uses the Library" must include any data and utility programs needed for reproducing the executable from it. However, as a special exception, the materials to be distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. It may happen that this requirement contradicts the license restrictions of other proprietary libraries that do not normally accompany the operating system. Such a contradiction means you cannot use both them and the Library together in an executable that you distribute. 7. You may place library facilities that are a work based on the Library side-by-side in a single library together with other library facilities not covered by this License, and distribute such a combined library, provided that the separate distribution of the work based on the Library and of the other library facilities is otherwise permitted, and provided that you do these two things: a) Accompany the combined library with a copy of the same work based on the Library, uncombined with any other library facilities. This must be distributed under the terms of the Sections above. b) Give prominent notice with the combined library of the fact that part of it is a work based on the Library, and explaining where to find the accompanying uncombined form of the same work. 8. You may not copy, modify, sublicense, link with, or distribute the Library except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense, link with, or distribute the Library is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 9. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Library or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Library (or any work based on the Library), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Library or works based on it. 10. Each time you redistribute the Library (or any work based on the Library), the recipient automatically receives a license from the original licensor to copy, distribute, link with or modify the Library subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties with this License. 11. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Library at all. For example, if a patent license would not permit royalty-free redistribution of the Library by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Library. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply, and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 12. If the distribution and/or use of the Library is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Library under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 13. The Free Software Foundation may publish revised and/or new versions of the Lesser General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Library specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Library does not specify a license version number, you may choose any version ever published by the Free Software Foundation. 14. If you wish to incorporate parts of the Library into other free programs whose distribution conditions are incompatible with these, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS connectome-workbench-1.2.3+git41-gc4c6c90/src/Quazip/JlCompress.cpp000066400000000000000000000352311300200146000246060ustar00rootroot00000000000000#include "JlCompress.h" #include static bool copyData(QIODevice &inFile, QIODevice &outFile) { while (!inFile.atEnd()) { char buf[4096]; qint64 readLen = inFile.read(buf, 4096); if (readLen <= 0) return false; if (outFile.write(buf, readLen) != readLen) return false; } return true; } /**OK * Comprime il file fileName, nell'oggetto zip, con il nome fileDest. * * La funzione fallisce se: * * zip==NULL; * * l'oggetto zip e stato aperto in una modalita non compatibile con l'aggiunta di file; * * non e possibile aprire il file d'origine; * * non e possibile creare il file all'interno dell'oggetto zip; * * si e rilevato un errore nella copia dei dati; * * non e stato possibile chiudere il file all'interno dell'oggetto zip; */ bool JlCompress::compressFile(QuaZip* zip, QString fileName, QString fileDest) { // zip: oggetto dove aggiungere il file // fileName: nome del file reale // fileDest: nome del file all'interno del file compresso // Controllo l'apertura dello zip if (!zip) return false; if (zip->getMode()!=QuaZip::mdCreate && zip->getMode()!=QuaZip::mdAppend && zip->getMode()!=QuaZip::mdAdd) return false; // Apro il file originale QFile inFile; inFile.setFileName(fileName); if(!inFile.open(QIODevice::ReadOnly)) return false; // Apro il file risulato QuaZipFile outFile(zip); if(!outFile.open(QIODevice::WriteOnly, QuaZipNewInfo(fileDest, inFile.fileName()))) return false; // Copio i dati if (!copyData(inFile, outFile) || outFile.getZipError()!=UNZ_OK) { return false; } // Chiudo i file outFile.close(); if (outFile.getZipError()!=UNZ_OK) return false; inFile.close(); return true; } /**OK * Comprime la cartella dir nel file fileCompressed, se recursive e true allora * comprime anche le sotto cartelle. I nomi dei file preceduti dal path creato * togliendo il pat della cartella origDir al path della cartella dir. * Se la funzione fallisce restituisce false e cancella il file che si e tentato * di creare. * * La funzione fallisce se: * * zip==NULL; * * l'oggetto zip e stato aperto in una modalita non compatibile con l'aggiunta di file; * * la cartella dir non esiste; * * la compressione di una sotto cartella fallisce (1); * * la compressione di un file fallisce; * (1) La funzione si richiama in maniera ricorsiva per comprimere le sotto cartelle * dunque gli errori di compressione di una sotto cartella sono gli stessi di questa * funzione. */ bool JlCompress::compressSubDir(QuaZip* zip, QString dir, QString origDir, bool recursive) { // zip: oggetto dove aggiungere il file // dir: cartella reale corrente // origDir: cartella reale originale // (path(dir)-path(origDir)) = path interno all'oggetto zip // Controllo l'apertura dello zip if (!zip) return false; if (zip->getMode()!=QuaZip::mdCreate && zip->getMode()!=QuaZip::mdAppend && zip->getMode()!=QuaZip::mdAdd) return false; // Controllo la cartella QDir directory(dir); if (!directory.exists()) return false; QDir origDirectory(origDir); if (dir != origDir) { QuaZipFile dirZipFile(zip); if (!dirZipFile.open(QIODevice::WriteOnly, QuaZipNewInfo(origDirectory.relativeFilePath(dir) + "/", dir), 0, 0, 0)) { return false; } dirZipFile.close(); } // Se comprimo anche le sotto cartelle if (recursive) { // Per ogni sotto cartella QFileInfoList files = directory.entryInfoList(QDir::AllDirs|QDir::NoDotAndDotDot); Q_FOREACH (QFileInfo file, files) { // Comprimo la sotto cartella if(!compressSubDir(zip,file.absoluteFilePath(),origDir,recursive)) return false; } } // Per ogni file nella cartella QFileInfoList files = directory.entryInfoList(QDir::Files); Q_FOREACH (QFileInfo file, files) { // Se non e un file o e il file compresso che sto creando if(!file.isFile()||file.absoluteFilePath()==zip->getZipName()) continue; // Creo il nome relativo da usare all'interno del file compresso QString filename = origDirectory.relativeFilePath(file.absoluteFilePath()); // Comprimo il file if (!compressFile(zip,file.absoluteFilePath(),filename)) return false; } return true; } /**OK * Estrae il file fileName, contenuto nell'oggetto zip, con il nome fileDest. * Se la funzione fallisce restituisce false e cancella il file che si e tentato di estrarre. * * La funzione fallisce se: * * zip==NULL; * * l'oggetto zip e stato aperto in una modalita non compatibile con l'estrazione di file; * * non e possibile aprire il file all'interno dell'oggetto zip; * * non e possibile creare il file estratto; * * si e rilevato un errore nella copia dei dati (1); * * non e stato possibile chiudere il file all'interno dell'oggetto zip (1); * * (1): prima di uscire dalla funzione cancella il file estratto. */ bool JlCompress::extractFile(QuaZip* zip, QString fileName, QString fileDest) { // zip: oggetto dove aggiungere il file // filename: nome del file reale // fileincompress: nome del file all'interno del file compresso // Controllo l'apertura dello zip if (!zip) return false; if (zip->getMode()!=QuaZip::mdUnzip) return false; // Apro il file compresso if (!fileName.isEmpty()) zip->setCurrentFile(fileName); QuaZipFile inFile(zip); if(!inFile.open(QIODevice::ReadOnly) || inFile.getZipError()!=UNZ_OK) return false; // Controllo esistenza cartella file risultato QDir curDir; if (!curDir.mkpath(QFileInfo(fileDest).absolutePath())) { return false; } QuaZipFileInfo info; if (!zip->getCurrentFileInfo(&info)) return false; if (fileDest.endsWith('/') && QFileInfo(fileDest).isDir()) { QFile(fileDest).setPermissions(info.getPermissions()); return true; } // Apro il file risultato QFile outFile; outFile.setFileName(fileDest); if(!outFile.open(QIODevice::WriteOnly)) return false; // Copio i dati if (!copyData(inFile, outFile) || inFile.getZipError()!=UNZ_OK) { outFile.close(); removeFile(QStringList(fileDest)); return false; } outFile.close(); // Chiudo i file inFile.close(); if (inFile.getZipError()!=UNZ_OK) { removeFile(QStringList(fileDest)); return false; } outFile.setPermissions(info.getPermissions()); return true; } /** * Rimuove i file il cui nome e specificato all'interno di listFile. * Restituisce true se tutti i file sono stati cancellati correttamente, attenzione * perche puo restituire false anche se alcuni file non esistevano e si e tentato * di cancellarli. */ bool JlCompress::removeFile(QStringList listFile) { bool ret = true; // Per ogni file for (int i=0; iopen(QuaZip::mdUnzip)) { delete zip; return QStringList(); } // Estraggo i nomi dei file QStringList lst; QuaZipFileInfo info; for(bool more=zip->goToFirstFile(); more; more=zip->goToNextFile()) { if(!zip->getCurrentFileInfo(&info)) { delete zip; return QStringList(); } lst << info.name; //info.name.toLocal8Bit().constData() } // Chiudo il file zip zip->close(); if(zip->getZipError()!=0) { delete zip; return QStringList(); } delete zip; return lst; } connectome-workbench-1.2.3+git41-gc4c6c90/src/Quazip/JlCompress.h000066400000000000000000000103351300200146000242510ustar00rootroot00000000000000#ifndef JLCOMPRESSFOLDER_H_ #define JLCOMPRESSFOLDER_H_ #include "quazip.h" #include "quazipfile.h" #include "quazipfileinfo.h" #include #include #include #include /// Utility class for typical operations. /** This class contains a number of useful static functions to perform simple operations, such as mass ZIP packing or extraction. */ class QUAZIP_EXPORT JlCompress { private: /// Compress a single file. /** \param zip Opened zip to compress the file to. \param fileName The full path to the source file. \param fileDest The full name of the file inside the archive. \return true if success, false otherwise. */ static bool compressFile(QuaZip* zip, QString fileName, QString fileDest); /// Compress a subdirectory. /** \param parentZip Opened zip containing the parent directory. \param dir The full path to the directory to pack. \param parentDir The full path to the directory corresponding to the root of the ZIP. \param recursive Whether to pack sub-directories as well or only files. \return true if success, false otherwise. */ static bool compressSubDir(QuaZip* parentZip, QString dir, QString parentDir, bool recursive = true); /// Extract a single file. /** \param zip The opened zip archive to extract from. \param fileName The full name of the file to extract. \param fileDest The full path to the destination file. \return true if success, false otherwise. */ static bool extractFile(QuaZip* zip, QString fileName, QString fileDest); /// Remove some files. /** \param listFile The list of files to remove. \return true if success, false otherwise. */ static bool removeFile(QStringList listFile); public: /// Compress a single file. /** \param fileCompressed The name of the archive. \param file The file to compress. \return true if success, false otherwise. */ static bool compressFile(QString fileCompressed, QString file); /// Compress a list of files. /** \param fileCompressed The name of the archive. \param files The file list to compress. \return true if success, false otherwise. */ static bool compressFiles(QString fileCompressed, QStringList files); /// Compress a whole directory. /** \param fileCompressed The name of the archive. \param dir The directory to compress. \param recursive Whether to pack the subdirectories as well, or just regular files. \return true if success, false otherwise. */ static bool compressDir(QString fileCompressed, QString dir = QString(), bool recursive = true); public: /// Extract a single file. /** \param fileCompressed The name of the archive. \param fileName The file to extract. \param fileDest The destination file, assumed to be identical to \a file if left empty. \return The list of the full paths of the files extracted, empty on failure. */ static QString extractFile(QString fileCompressed, QString fileName, QString fileDest = QString()); /// Extract a list of files. /** \param fileCompressed The name of the archive. \param files The file list to extract. \param dir The directory to put the files to, the current directory if left empty. \return The list of the full paths of the files extracted, empty on failure. */ static QStringList extractFiles(QString fileCompressed, QStringList files, QString dir = QString()); /// Extract a whole archive. /** \param fileCompressed The name of the archive. \param dir The directory to extract to, the current directory if left empty. \return The list of the full paths of the files extracted, empty on failure. */ static QStringList extractDir(QString fileCompressed, QString dir = QString()); /// Get the file list. /** \return The list of the files in the archive, or, more precisely, the list of the entries, including both files and directories if they are present separately. */ static QStringList getFileList(QString fileCompressed); }; #endif /* JLCOMPRESSFOLDER_H_ */ connectome-workbench-1.2.3+git41-gc4c6c90/src/Quazip/NEWS.txt000066400000000000000000000125551300200146000233420ustar00rootroot00000000000000QuaZIP changes * 2014-01-22 0.6 * Minizip updated to 1.1 (with all the necessary modifications re-done), and that means that... * the long-awaited zip64 support is now available! * A few rather minor fixes. * 2014-01-19 0.5.2 * Some minor bug fixes. * API to access file permissions subfield of the external attributes. * MS VS 2012 Express support. * API to set the default codec used to encode/decode file names (mainly for use by various wrappers such as JlCompress, when you don't have direct access to the underlying QuaZip instance). * 2013-03-02 0.5.1 * Lots of QuaZipDir fixes, thanks to all bug reporters. * Full Qt Creator support. * MS VS 2010 Express support. * Qt5 support (didn't need any source code changes anyway). * Lots of minor bug fixes. * 2012-09-07 0.5 * Added run_moc.bat files for building under Windows in case Qt integration is not available (e. g. VS 2008 Express). * Added the QuaZipDir class to simplify ZIP navigation in terms of directories. * Added the QuaGzipFile class for working with GZIP archives. It was added as a bonus since it has nothing to do with the main purpose of the library. It probably won't get any major improvements, although minor bug fixes are possible. * Added the QuaZIODevice class for working with zlib compression. It has nothing to do with the ZIP format, and therefore the same notice as for the QuaGzipFile applies. * The global comment is no longer erased when adding files to an archive. * Many bug fixes. * 2012-01-14 0.4.4 * Fixed isSequential() test that was causing open() failures on Unix. * Fixed sub-directory compressing in JlCompress. * Added MS VS 2008 solution, compatible with the binary Qt distribution (tested on MS VS 2008 Express, had to run MOC manually due to the lack of plugin in Express). * Fixed extracting directories in JlCompress. * Fixed JlCompress.h includes in the test suite, which used lowercase names thus breaking on case-sensitive systems. * Implemented missing QuaZipFile::getZip() that was only declared. * Fixed reopening closed files. * Fixed possible memory leak in case of open error. * 2011-09-09 0.4.3 * New test suite using QTestLib. * Fixed bytesAvailable(), pos() and atEnd(). * Added ZIP v1.0 support and disabling data descriptor for compatibility with some older software. * Fixed DLL export/import issues for some symbols. * Added QUAZIP_STATIC macro for compiling as a static library or directly including the source. * Added getFileNameList() and getFileInfoList() convenience functions. * Added some buffering to JlCompress to improve performance. * 2011-08-10 0.4.2 * Cmake patch (thanks to Bernhard Rosenkraenzer). * Symbian patch (thanks to Hamish Willee). * Documented the multiple files limitation of QuaZipFile. * Fixed relative paths handling in JlCompress. * Fixed linking to MinGW zlib. * 2011-05-26 0.4.1 * License statement updated to avoid confusion. GPL license removed for the very same reason. * Parts of original package are now clearly marked as modified, just as their license requires. * 2011-05-23 0.4 * QuaZip and QuaZipFile classes now use the Pimpl idiom. This means that future releases will probably be binary compatible with this one, but it also means that this one is binary incompatible with the old ones. * IO API has been rewritten using QIODevice instead of standard C library. Among other things it means that QuaZip now supports files up to 4 GB in size instead of 2 GB. * Added QuaZip methods allowing access to ZIP files represented by any seekable QIODevice implementation (QBuffer is a good example). * 2010-07-23 0.3 * Fixed getComment() for global comments. * Added some useful classes for calculating checksums (thanks to Adam Walczak). * Added some utility classes for working with whole directories (thanks to Roberto Pompermaier). It would be nice if someone documents these in English, though. * Probably fixed some problems with passwords (thanks to Vasiliy Sorokin). I didn't test it, though. * 2008-09-17 0.2.3 * Fixed license notices in sources. * SVN * Fixed a small bug in QuaZipFile::atEnd(). * 2007-01-16 0.2.2 * Added LGPL as alternative license. * Added FAQ documentation page. * 2006-03-21 0.2.1 * Fixed setCommentCodec() bug. * Fixed bug that set month 1-12 instead of 0-11, as specified in zip.h. * Added workaround for Qt's bug that caused wrong timestamps. * Few documentation fixes and cosmetic changes. * 2005-07-08 0.2 * Write support. * Extended QuaZipFile API, including size(), *pos() functions. * Support for comments encoding/decoding. * 2005-07-01 0.1 * Initial version. connectome-workbench-1.2.3+git41-gc4c6c90/src/Quazip/README.txt000066400000000000000000000045021300200146000235140ustar00rootroot00000000000000QuaZIP is the C++ wrapper for Gilles Vollant's ZIP/UNZIP package (AKA minizip) using Trolltech's Qt library. It uses existing ZIP/UNZIP package C code and therefore depends on the zlib library. Also, it depends on Qt 4. To compile it on UNIX dialect: $ cd quazip $ qmake $ make You must make sure that: * You have Qt 4 properly and fully installed (including tools and headers, not just library) * "qmake" command runs Qt 4's qmake, not some other version (you'll have to type full path to qmake otherwise). To install compiled shared library, just type: $ make install By default, it installs in /usr/local, but you may change it using $ qmake PREFIX=/wherever/you/want/to/install You do not have to compile and install QuaZIP to use it. You can just (and sometimes it may be the best way) add QuaZIP's source files to your project and use them. See doc/html or, if you do not have a browser, quazip/*.h and quazip/doc/* files for the more detailed documentation. For Windows, it's essentially the same, but you may have to adjust settings for different environments. If you want to include QuaZIP sources directly in your project or if you want to use QuaZIP compiled as a static library using "qmake CONFIG+=statliclib", you have to define the QUAZIP_STATIC macro, otherwise you're likely to run into problems as QuaZIP symbols will be marked as dllimported. Copyright notice: Copyright (C) 2005-2012 Sergey A. Tachenov This program is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser 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 Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA See COPYING file for the full LGPL text. Original ZIP package is copyrighted by Gilles Vollant, see quazip/(un)zip.h files for details, basically it's zlib license. connectome-workbench-1.2.3+git41-gc4c6c90/src/Quazip/crypt.h000066400000000000000000000115171300200146000233340ustar00rootroot00000000000000/* crypt.h -- base code for crypt/uncrypt ZIPfile Version 1.01e, February 12th, 2005 Copyright (C) 1998-2005 Gilles Vollant This code is a modified version of crypting code in Infozip distribution The encryption/decryption parts of this source code (as opposed to the non-echoing password parts) were originally written in Europe. The whole source package can be freely distributed, including from the USA. (Prior to January 2000, re-export from the US was a violation of US law.) This encryption code is a direct transcription of the algorithm from Roger Schlafly, described by Phil Katz in the file appnote.txt. This file (appnote.txt) is distributed with the PKZIP program (even in the version without encryption capabilities). If you don't need crypting in your application, just define symbols NOCRYPT and NOUNCRYPT. This code support the "Traditional PKWARE Encryption". The new AES encryption added on Zip format by Winzip (see the page http://www.winzip.com/aes_info.htm ) and PKWare PKZip 5.x Strong Encryption is not supported. */ #include "quazip_global.h" #define CRC32(c, b) ((*(pcrc_32_tab+(((int)(c) ^ (b)) & 0xff))) ^ ((c) >> 8)) /*********************************************************************** * Return the next byte in the pseudo-random sequence */ static int decrypt_byte(unsigned long* pkeys, const z_crc_t FAR * pcrc_32_tab UNUSED) { //(void) pcrc_32_tab; /* avoid "unused parameter" warning */ unsigned temp; /* POTENTIAL BUG: temp*(temp^1) may overflow in an * unpredictable manner on 16-bit systems; not a problem * with any known compiler so far, though */ temp = ((unsigned)(*(pkeys+2)) & 0xffff) | 2; return (int)(((temp * (temp ^ 1)) >> 8) & 0xff); } /*********************************************************************** * Update the encryption keys with the next byte of plain text */ static int update_keys(unsigned long* pkeys,const z_crc_t FAR * pcrc_32_tab,int c) { (*(pkeys+0)) = CRC32((*(pkeys+0)), c); (*(pkeys+1)) += (*(pkeys+0)) & 0xff; (*(pkeys+1)) = (*(pkeys+1)) * 134775813L + 1; { register int keyshift = (int)((*(pkeys+1)) >> 24); (*(pkeys+2)) = CRC32((*(pkeys+2)), keyshift); } return c; } /*********************************************************************** * Initialize the encryption keys and the random header according to * the given password. */ static void init_keys(const char* passwd,unsigned long* pkeys,const z_crc_t FAR * pcrc_32_tab) { *(pkeys+0) = 305419896L; *(pkeys+1) = 591751049L; *(pkeys+2) = 878082192L; while (*passwd != '\0') { update_keys(pkeys,pcrc_32_tab,(int)*passwd); passwd++; } } #define zdecode(pkeys,pcrc_32_tab,c) \ (update_keys(pkeys,pcrc_32_tab,c ^= decrypt_byte(pkeys,pcrc_32_tab))) #define zencode(pkeys,pcrc_32_tab,c,t) \ (t=decrypt_byte(pkeys,pcrc_32_tab), update_keys(pkeys,pcrc_32_tab,c), t^(c)) #ifdef INCLUDECRYPTINGCODE_IFCRYPTALLOWED #define RAND_HEAD_LEN 12 /* "last resort" source for second part of crypt seed pattern */ # ifndef ZCR_SEED2 # define ZCR_SEED2 3141592654UL /* use PI as default pattern */ # endif static int crypthead(passwd, buf, bufSize, pkeys, pcrc_32_tab, crcForCrypting) const char *passwd; /* password string */ unsigned char *buf; /* where to write header */ int bufSize; unsigned long* pkeys; const z_crc_t FAR * pcrc_32_tab; unsigned long crcForCrypting; { int n; /* index in random header */ int t; /* temporary */ int c; /* random byte */ unsigned char header[RAND_HEAD_LEN-2]; /* random header */ static unsigned calls = 0; /* ensure different random header each time */ if (bufSize> 7) & 0xff; header[n] = (unsigned char)zencode(pkeys, pcrc_32_tab, c, t); } /* Encrypt random header (last two bytes is high word of crc) */ init_keys(passwd, pkeys, pcrc_32_tab); for (n = 0; n < RAND_HEAD_LEN-2; n++) { buf[n] = (unsigned char)zencode(pkeys, pcrc_32_tab, header[n], t); } buf[n++] = zencode(pkeys, pcrc_32_tab, (int)(crcForCrypting >> 16) & 0xff, t); buf[n++] = zencode(pkeys, pcrc_32_tab, (int)(crcForCrypting >> 24) & 0xff, t); return n; } #endif connectome-workbench-1.2.3+git41-gc4c6c90/src/Quazip/ioapi.h000066400000000000000000000157301300200146000232750ustar00rootroot00000000000000/* ioapi.h -- IO base function header for compress/uncompress .zip part of the MiniZip project - ( http://www.winimage.com/zLibDll/minizip.html ) Copyright (C) 1998-2010 Gilles Vollant (minizip) ( http://www.winimage.com/zLibDll/minizip.html ) Modifications for Zip64 support Copyright (C) 2009-2010 Mathias Svensson ( http://result42.com ) Modified by Sergey A. Tachenov to allow QIODevice API usage. For more info read MiniZip_info.txt Changes Oct-2009 - Defined ZPOS64_T to fpos_t on windows and u_int64_t on linux. (might need to find a better why for this) Oct-2009 - Change to fseeko64, ftello64 and fopen64 so large files would work on linux. More if/def section may be needed to support other platforms Oct-2009 - Defined fxxxx64 calls to normal fopen/ftell/fseek so they would compile on windows. (but you should use iowin32.c for windows instead) */ #ifndef _ZLIBIOAPI64_H #define _ZLIBIOAPI64_H #if (!defined(_WIN32)) && (!defined(WIN32)) // Linux needs this to support file operation on files larger then 4+GB // But might need better if/def to select just the platforms that needs them. #ifndef __USE_FILE_OFFSET64 #define __USE_FILE_OFFSET64 #endif #ifndef __USE_LARGEFILE64 #define __USE_LARGEFILE64 #endif #ifndef _LARGEFILE64_SOURCE #define _LARGEFILE64_SOURCE #endif #ifndef _FILE_OFFSET_BIT #define _FILE_OFFSET_BIT 64 #endif #endif #include #include #include "zlib.h" #if defined(USE_FILE32API) #define fopen64 fopen #define ftello64 ftell #define fseeko64 fseek #else #ifdef _MSC_VER #define fopen64 fopen #if (_MSC_VER >= 1400) && (!(defined(NO_MSCVER_FILE64_FUNC))) #define ftello64 _ftelli64 #define fseeko64 _fseeki64 #else // old MSC #define ftello64 ftell #define fseeko64 fseek #endif #endif #endif /* #ifndef ZPOS64_T #ifdef _WIN32 #define ZPOS64_T fpos_t #else #include #define ZPOS64_T uint64_t #endif #endif */ #ifdef HAVE_MINIZIP64_CONF_H #include "mz64conf.h" #endif /* a type choosen by DEFINE */ #ifdef HAVE_64BIT_INT_CUSTOM typedef 64BIT_INT_CUSTOM_TYPE ZPOS64_T; #else #ifdef HAS_STDINT_H #include "stdint.h" typedef uint64_t ZPOS64_T; #else #if defined(_MSC_VER) || defined(__BORLANDC__) typedef unsigned __int64 ZPOS64_T; #else typedef unsigned long long int ZPOS64_T; #endif #endif #endif #ifdef __cplusplus extern "C" { #endif #ifndef OF #define OF _Z_OF #endif #define ZLIB_FILEFUNC_SEEK_CUR (1) #define ZLIB_FILEFUNC_SEEK_END (2) #define ZLIB_FILEFUNC_SEEK_SET (0) #define ZLIB_FILEFUNC_MODE_READ (1) #define ZLIB_FILEFUNC_MODE_WRITE (2) #define ZLIB_FILEFUNC_MODE_READWRITEFILTER (3) #define ZLIB_FILEFUNC_MODE_EXISTING (4) #define ZLIB_FILEFUNC_MODE_CREATE (8) #ifndef ZCALLBACK #if (defined(WIN32) || defined(_WIN32) || defined (WINDOWS) || defined (_WINDOWS)) && defined(CALLBACK) && defined (USEWINDOWS_CALLBACK) #define ZCALLBACK CALLBACK #else #define ZCALLBACK #endif #endif typedef voidpf (ZCALLBACK *open_file_func) OF((voidpf opaque, voidpf file, int mode)); typedef uLong (ZCALLBACK *read_file_func) OF((voidpf opaque, voidpf stream, void* buf, uLong size)); typedef uLong (ZCALLBACK *write_file_func) OF((voidpf opaque, voidpf stream, const void* buf, uLong size)); typedef int (ZCALLBACK *close_file_func) OF((voidpf opaque, voidpf stream)); typedef int (ZCALLBACK *testerror_file_func) OF((voidpf opaque, voidpf stream)); typedef uLong (ZCALLBACK *tell_file_func) OF((voidpf opaque, voidpf stream)); typedef int (ZCALLBACK *seek_file_func) OF((voidpf opaque, voidpf stream, uLong offset, int origin)); /* here is the "old" 32 bits structure structure */ typedef struct zlib_filefunc_def_s { open_file_func zopen_file; read_file_func zread_file; write_file_func zwrite_file; tell_file_func ztell_file; seek_file_func zseek_file; close_file_func zclose_file; testerror_file_func zerror_file; voidpf opaque; } zlib_filefunc_def; typedef ZPOS64_T (ZCALLBACK *tell64_file_func) OF((voidpf opaque, voidpf stream)); typedef int (ZCALLBACK *seek64_file_func) OF((voidpf opaque, voidpf stream, ZPOS64_T offset, int origin)); typedef voidpf (ZCALLBACK *open64_file_func) OF((voidpf opaque, voidpf file, int mode)); typedef struct zlib_filefunc64_def_s { open64_file_func zopen64_file; read_file_func zread_file; write_file_func zwrite_file; tell64_file_func ztell64_file; seek64_file_func zseek64_file; close_file_func zclose_file; testerror_file_func zerror_file; voidpf opaque; } zlib_filefunc64_def; void fill_qiodevice64_filefunc OF((zlib_filefunc64_def* pzlib_filefunc_def)); void fill_qiodevice_filefunc OF((zlib_filefunc_def* pzlib_filefunc_def)); /* now internal definition, only for zip.c and unzip.h */ typedef struct zlib_filefunc64_32_def_s { zlib_filefunc64_def zfile_func64; open_file_func zopen32_file; tell_file_func ztell32_file; seek_file_func zseek32_file; } zlib_filefunc64_32_def; #define ZREAD64(filefunc,filestream,buf,size) ((*((filefunc).zfile_func64.zread_file)) ((filefunc).zfile_func64.opaque,filestream,buf,size)) #define ZWRITE64(filefunc,filestream,buf,size) ((*((filefunc).zfile_func64.zwrite_file)) ((filefunc).zfile_func64.opaque,filestream,buf,size)) //#define ZTELL64(filefunc,filestream) ((*((filefunc).ztell64_file)) ((filefunc).opaque,filestream)) //#define ZSEEK64(filefunc,filestream,pos,mode) ((*((filefunc).zseek64_file)) ((filefunc).opaque,filestream,pos,mode)) #define ZCLOSE64(filefunc,filestream) ((*((filefunc).zfile_func64.zclose_file)) ((filefunc).zfile_func64.opaque,filestream)) #define ZERROR64(filefunc,filestream) ((*((filefunc).zfile_func64.zerror_file)) ((filefunc).zfile_func64.opaque,filestream)) voidpf call_zopen64 OF((const zlib_filefunc64_32_def* pfilefunc,voidpf file,int mode)); int call_zseek64 OF((const zlib_filefunc64_32_def* pfilefunc,voidpf filestream, ZPOS64_T offset, int origin)); ZPOS64_T call_ztell64 OF((const zlib_filefunc64_32_def* pfilefunc,voidpf filestream)); void fill_zlib_filefunc64_32_def_from_filefunc32(zlib_filefunc64_32_def* p_filefunc64_32,const zlib_filefunc_def* p_filefunc32); #define ZOPEN64(filefunc,filename,mode) (call_zopen64((&(filefunc)),(filename),(mode))) #define ZTELL64(filefunc,filestream) (call_ztell64((&(filefunc)),(filestream))) #define ZSEEK64(filefunc,filestream,pos,mode) (call_zseek64((&(filefunc)),(filestream),(pos),(mode))) #ifdef __cplusplus } #endif #endif connectome-workbench-1.2.3+git41-gc4c6c90/src/Quazip/qioapi.cpp000066400000000000000000000164541300200146000240150ustar00rootroot00000000000000/* ioapi.c -- IO base function header for compress/uncompress .zip files using zlib + zip or unzip API Version 1.01e, February 12th, 2005 Copyright (C) 1998-2005 Gilles Vollant Modified by Sergey A. Tachenov to integrate with Qt. */ #include #include #include #include "zlib.h" #include "ioapi.h" #include "quazip_global.h" #include /* I've found an old Unix (a SunOS 4.1.3_U1) without all SEEK_* defined.... */ #ifndef SEEK_CUR #define SEEK_CUR 1 #endif #ifndef SEEK_END #define SEEK_END 2 #endif #ifndef SEEK_SET #define SEEK_SET 0 #endif voidpf call_zopen64 (const zlib_filefunc64_32_def* pfilefunc,voidpf file,int mode) { if (pfilefunc->zfile_func64.zopen64_file != NULL) return (*(pfilefunc->zfile_func64.zopen64_file)) (pfilefunc->zfile_func64.opaque,file,mode); else { return (*(pfilefunc->zopen32_file))(pfilefunc->zfile_func64.opaque,file,mode); } } int call_zseek64 (const zlib_filefunc64_32_def* pfilefunc,voidpf filestream, ZPOS64_T offset, int origin) { if (pfilefunc->zfile_func64.zseek64_file != NULL) return (*(pfilefunc->zfile_func64.zseek64_file)) (pfilefunc->zfile_func64.opaque,filestream,offset,origin); else { uLong offsetTruncated = (uLong)offset; if (offsetTruncated != offset) return -1; else return (*(pfilefunc->zseek32_file))(pfilefunc->zfile_func64.opaque,filestream,offsetTruncated,origin); } } ZPOS64_T call_ztell64 (const zlib_filefunc64_32_def* pfilefunc,voidpf filestream) { if (pfilefunc->zfile_func64.zseek64_file != NULL) return (*(pfilefunc->zfile_func64.ztell64_file)) (pfilefunc->zfile_func64.opaque,filestream); else { uLong tell_uLong = (*(pfilefunc->ztell32_file))(pfilefunc->zfile_func64.opaque,filestream); if ((tell_uLong) == ((uLong)-1)) return (ZPOS64_T)-1; else return tell_uLong; } } voidpf ZCALLBACK qiodevice_open_file_func ( voidpf /*opaque UNUSED*/, voidpf file, int mode) { QIODevice *iodevice = reinterpret_cast(file); if ((mode & ZLIB_FILEFUNC_MODE_READWRITEFILTER)==ZLIB_FILEFUNC_MODE_READ) iodevice->open(QIODevice::ReadOnly); else if (mode & ZLIB_FILEFUNC_MODE_EXISTING) iodevice->open(QIODevice::ReadWrite); else if (mode & ZLIB_FILEFUNC_MODE_CREATE) iodevice->open(QIODevice::WriteOnly); if (iodevice->isOpen()) { if (iodevice->isSequential()) { iodevice->close(); return NULL; } else { return iodevice; } } else return NULL; } uLong ZCALLBACK qiodevice_read_file_func ( voidpf /*opaque UNUSED*/, voidpf stream, void* buf, uLong size) { uLong ret; ret = (uLong)((QIODevice*)stream)->read((char*)buf,size); return ret; } uLong ZCALLBACK qiodevice_write_file_func ( voidpf /*opaque UNUSED*/, voidpf stream, const void* buf, uLong size) { uLong ret; ret = (uLong)((QIODevice*)stream)->write((char*)buf,size); return ret; } uLong ZCALLBACK qiodevice_tell_file_func ( voidpf /*opaque UNUSED*/, voidpf stream) { uLong ret; ret = ((QIODevice*)stream)->pos(); return ret; } ZPOS64_T ZCALLBACK qiodevice64_tell_file_func ( voidpf /*opaque UNUSED*/, voidpf stream) { qint64 ret; ret = ((QIODevice*)stream)->pos(); return static_cast(ret); } int ZCALLBACK qiodevice_seek_file_func ( voidpf /*opaque UNUSED*/, voidpf stream, uLong offset, int origin) { uLong qiodevice_seek_result=0; int ret; switch (origin) { case ZLIB_FILEFUNC_SEEK_CUR : qiodevice_seek_result = ((QIODevice*)stream)->pos() + offset; break; case ZLIB_FILEFUNC_SEEK_END : qiodevice_seek_result = ((QIODevice*)stream)->size() - offset; break; case ZLIB_FILEFUNC_SEEK_SET : qiodevice_seek_result = offset; break; default: return -1; } ret = !((QIODevice*)stream)->seek(qiodevice_seek_result); return ret; } int ZCALLBACK qiodevice64_seek_file_func ( voidpf /*opaque UNUSED*/, voidpf stream, ZPOS64_T offset, int origin) { qint64 qiodevice_seek_result=0; int ret; switch (origin) { case ZLIB_FILEFUNC_SEEK_CUR : qiodevice_seek_result = ((QIODevice*)stream)->pos() + offset; break; case ZLIB_FILEFUNC_SEEK_END : qiodevice_seek_result = ((QIODevice*)stream)->size() - offset; break; case ZLIB_FILEFUNC_SEEK_SET : qiodevice_seek_result = offset; break; default: return -1; } ret = !((QIODevice*)stream)->seek(qiodevice_seek_result); return ret; } int ZCALLBACK qiodevice_close_file_func ( voidpf /*opaque UNUSED*/, voidpf stream) { ((QIODevice*)stream)->close(); return 0; } int ZCALLBACK qiodevice_error_file_func ( voidpf /*opaque UNUSED*/, voidpf /*stream UNUSED*/) { // can't check for error due to the QIODevice API limitation return 0; } void fill_qiodevice_filefunc ( zlib_filefunc_def* pzlib_filefunc_def) { pzlib_filefunc_def->zopen_file = qiodevice_open_file_func; pzlib_filefunc_def->zread_file = qiodevice_read_file_func; pzlib_filefunc_def->zwrite_file = qiodevice_write_file_func; pzlib_filefunc_def->ztell_file = qiodevice_tell_file_func; pzlib_filefunc_def->zseek_file = qiodevice_seek_file_func; pzlib_filefunc_def->zclose_file = qiodevice_close_file_func; pzlib_filefunc_def->zerror_file = qiodevice_error_file_func; pzlib_filefunc_def->opaque = NULL; } void fill_qiodevice64_filefunc ( zlib_filefunc64_def* pzlib_filefunc_def) { // Open functions are the same for Qt. pzlib_filefunc_def->zopen64_file = qiodevice_open_file_func; pzlib_filefunc_def->zread_file = qiodevice_read_file_func; pzlib_filefunc_def->zwrite_file = qiodevice_write_file_func; pzlib_filefunc_def->ztell64_file = qiodevice64_tell_file_func; pzlib_filefunc_def->zseek64_file = qiodevice64_seek_file_func; pzlib_filefunc_def->zclose_file = qiodevice_close_file_func; pzlib_filefunc_def->zerror_file = qiodevice_error_file_func; pzlib_filefunc_def->opaque = NULL; } void fill_zlib_filefunc64_32_def_from_filefunc32(zlib_filefunc64_32_def* p_filefunc64_32,const zlib_filefunc_def* p_filefunc32) { p_filefunc64_32->zfile_func64.zopen64_file = NULL; p_filefunc64_32->zopen32_file = p_filefunc32->zopen_file; p_filefunc64_32->zfile_func64.zerror_file = p_filefunc32->zerror_file; p_filefunc64_32->zfile_func64.zread_file = p_filefunc32->zread_file; p_filefunc64_32->zfile_func64.zwrite_file = p_filefunc32->zwrite_file; p_filefunc64_32->zfile_func64.ztell64_file = NULL; p_filefunc64_32->zfile_func64.zseek64_file = NULL; p_filefunc64_32->zfile_func64.zclose_file = p_filefunc32->zclose_file; p_filefunc64_32->zfile_func64.zerror_file = p_filefunc32->zerror_file; p_filefunc64_32->zfile_func64.opaque = p_filefunc32->opaque; p_filefunc64_32->zseek32_file = p_filefunc32->zseek_file; p_filefunc64_32->ztell32_file = p_filefunc32->ztell_file; } connectome-workbench-1.2.3+git41-gc4c6c90/src/Quazip/quaadler32.cpp000066400000000000000000000007211300200146000244640ustar00rootroot00000000000000#include "quaadler32.h" #include "zlib.h" QuaAdler32::QuaAdler32() { reset(); } quint32 QuaAdler32::calculate(const QByteArray &data) { return adler32( adler32(0L, Z_NULL, 0), (const Bytef*)data.data(), data.size() ); } void QuaAdler32::reset() { checksum = adler32(0L, Z_NULL, 0); } void QuaAdler32::update(const QByteArray &buf) { checksum = adler32( checksum, (const Bytef*)buf.data(), buf.size() ); } quint32 QuaAdler32::value() { return checksum; } connectome-workbench-1.2.3+git41-gc4c6c90/src/Quazip/quaadler32.h000066400000000000000000000010351300200146000241300ustar00rootroot00000000000000#ifndef QUAADLER32_H #define QUAADLER32_H #include #include "quachecksum32.h" /// Adler32 checksum /** \class QuaAdler32 quaadler32.h * This class wrappers the adler32 function with the QuaChecksum32 interface. * See QuaChecksum32 for more info. */ class QUAZIP_EXPORT QuaAdler32 : public QuaChecksum32 { public: QuaAdler32(); quint32 calculate(const QByteArray &data); void reset(); void update(const QByteArray &buf); quint32 value(); private: quint32 checksum; }; #endif //QUAADLER32_H connectome-workbench-1.2.3+git41-gc4c6c90/src/Quazip/quachecksum32.h000066400000000000000000000025701300200146000246500ustar00rootroot00000000000000#ifndef QUACHECKSUM32_H #define QUACHECKSUM32_H #include #include "quazip_global.h" /// Checksum interface. /** \class QuaChecksum32 quachecksum32.h * This is an interface for 32 bit checksums. * Classes implementing this interface can calcunate a certin * checksum in a single step: * \code * QChecksum32 *crc32 = new QuaCrc32(); * rasoult = crc32->calculate(data); * \endcode * or by streaming the data: * \code * QChecksum32 *crc32 = new QuaCrc32(); * while(!fileA.atEnd()) * crc32->update(fileA.read(bufSize)); * resoultA = crc32->value(); * crc32->reset(); * while(!fileB.atEnd()) * crc32->update(fileB.read(bufSize)); * resoultB = crc32->value(); * \endcode */ class QUAZIP_EXPORT QuaChecksum32 { public: ///Calculates the checksum for data. /** \a data source data * \return data checksum * * This function has no efect on the value returned by value(). */ virtual quint32 calculate(const QByteArray &data) = 0; ///Resets the calculation on a checksun for a stream. virtual void reset() = 0; ///Updates the calculated checksum for the stream /** \a buf next portion of data from the stream */ virtual void update(const QByteArray &buf) = 0; ///Value of the checksum calculated for the stream passed throw update(). /** \return checksum */ virtual quint32 value() = 0; }; #endif //QUACHECKSUM32_H connectome-workbench-1.2.3+git41-gc4c6c90/src/Quazip/quacrc32.cpp000066400000000000000000000006731300200146000241520ustar00rootroot00000000000000#include "quacrc32.h" #include "zlib.h" QuaCrc32::QuaCrc32() { reset(); } quint32 QuaCrc32::calculate(const QByteArray &data) { return crc32( crc32(0L, Z_NULL, 0), (const Bytef*)data.data(), data.size() ); } void QuaCrc32::reset() { checksum = crc32(0L, Z_NULL, 0); } void QuaCrc32::update(const QByteArray &buf) { checksum = crc32( checksum, (const Bytef*)buf.data(), buf.size() ); } quint32 QuaCrc32::value() { return checksum; } connectome-workbench-1.2.3+git41-gc4c6c90/src/Quazip/quacrc32.h000066400000000000000000000007561300200146000236210ustar00rootroot00000000000000#ifndef QUACRC32_H #define QUACRC32_H #include "quachecksum32.h" ///CRC32 checksum /** \class QuaCrc32 quacrc32.h * This class wrappers the crc32 function with the QuaChecksum32 interface. * See QuaChecksum32 for more info. */ class QUAZIP_EXPORT QuaCrc32 : public QuaChecksum32 { public: QuaCrc32(); quint32 calculate(const QByteArray &data); void reset(); void update(const QByteArray &buf); quint32 value(); private: quint32 checksum; }; #endif //QUACRC32_H connectome-workbench-1.2.3+git41-gc4c6c90/src/Quazip/quagzipfile.cpp000066400000000000000000000067641300200146000250560ustar00rootroot00000000000000#include #include "quagzipfile.h" /// \cond internal class QuaGzipFilePrivate { friend class QuaGzipFile; QString fileName; gzFile gzd; inline QuaGzipFilePrivate(): gzd(NULL) {} inline QuaGzipFilePrivate(const QString &fileName): fileName(fileName), gzd(NULL) {} template bool open(FileId id, QIODevice::OpenMode mode, QString &error); gzFile open(int fd, const char *modeString); gzFile open(const QString &name, const char *modeString); }; gzFile QuaGzipFilePrivate::open(const QString &name, const char *modeString) { return gzopen(QFile::encodeName(name).constData(), modeString); } gzFile QuaGzipFilePrivate::open(int fd, const char *modeString) { return gzdopen(fd, modeString); } template bool QuaGzipFilePrivate::open(FileId id, QIODevice::OpenMode mode, QString &error) { char modeString[2]; modeString[0] = modeString[1] = '\0'; if ((mode & QIODevice::Append) != 0) { error = QuaGzipFile::trUtf8("QIODevice::Append is not " "supported for GZIP"); return false; } if ((mode & QIODevice::ReadOnly) != 0 && (mode & QIODevice::WriteOnly) != 0) { error = QuaGzipFile::trUtf8("Opening gzip for both reading" " and writing is not supported"); return false; } else if ((mode & QIODevice::ReadOnly) != 0) { modeString[0] = 'r'; } else if ((mode & QIODevice::WriteOnly) != 0) { modeString[0] = 'w'; } else { error = QuaGzipFile::trUtf8("You can open a gzip either for reading" " or for writing. Which is it?"); return false; } gzd = open(id, modeString); if (gzd == NULL) { error = QuaGzipFile::trUtf8("Could not gzopen() file"); return false; } return true; } /// \endcond QuaGzipFile::QuaGzipFile(): d(new QuaGzipFilePrivate()) { } QuaGzipFile::QuaGzipFile(QObject *parent): QIODevice(parent), d(new QuaGzipFilePrivate()) { } QuaGzipFile::QuaGzipFile(const QString &fileName, QObject *parent): QIODevice(parent), d(new QuaGzipFilePrivate(fileName)) { } QuaGzipFile::~QuaGzipFile() { if (isOpen()) { close(); } delete d; } void QuaGzipFile::setFileName(const QString& fileName) { d->fileName = fileName; } QString QuaGzipFile::getFileName() const { return d->fileName; } bool QuaGzipFile::isSequential() const { return true; } bool QuaGzipFile::open(QIODevice::OpenMode mode) { QString error; if (!d->open(d->fileName, mode, error)) { setErrorString(error); return false; } return QIODevice::open(mode); } bool QuaGzipFile::open(int fd, QIODevice::OpenMode mode) { QString error; if (!d->open(fd, mode, error)) { setErrorString(error); return false; } return QIODevice::open(mode); } bool QuaGzipFile::flush() { return gzflush(d->gzd, Z_SYNC_FLUSH) == Z_OK; } void QuaGzipFile::close() { QIODevice::close(); gzclose(d->gzd); } qint64 QuaGzipFile::readData(char *data, qint64 maxSize) { return gzread(d->gzd, (voidp)data, (unsigned)maxSize); } qint64 QuaGzipFile::writeData(const char *data, qint64 maxSize) { if (maxSize == 0) return 0; int written = gzwrite(d->gzd, (voidp)data, (unsigned)maxSize); if (written == 0) return -1; else return written; } connectome-workbench-1.2.3+git41-gc4c6c90/src/Quazip/quagzipfile.h000066400000000000000000000055011300200146000245070ustar00rootroot00000000000000#ifndef QUAZIP_QUAGZIPFILE_H #define QUAZIP_QUAGZIPFILE_H #include #include "quazip_global.h" #include class QuaGzipFilePrivate; /// GZIP file /** This class is a wrapper around GZIP file access functions in zlib. Unlike QuaZip classes, it doesn't allow reading from a GZIP file opened as QIODevice, for example, if your GZIP file is in QBuffer. It only provides QIODevice access to a GZIP file contents, but the GZIP file itself must be identified by its name on disk or by descriptor id. */ class QUAZIP_EXPORT QuaGzipFile: public QIODevice { Q_OBJECT public: /// Empty constructor. /** Must call setFileName() before trying to open. */ QuaGzipFile(); /// Empty constructor with a parent. /** Must call setFileName() before trying to open. \param parent The parent object, as per QObject logic. */ QuaGzipFile(QObject *parent); /// Constructor. /** \param fileName The name of the GZIP file. \param parent The parent object, as per QObject logic. */ QuaGzipFile(const QString &fileName, QObject *parent = NULL); /// Destructor. virtual ~QuaGzipFile(); /// Sets the name of the GZIP file to be opened. void setFileName(const QString& fileName); /// Returns the name of the GZIP file. QString getFileName() const; /// Returns true. /** Strictly speaking, zlib supports seeking for GZIP files, but it is poorly implemented, because there is no way to implement it properly. For reading, seeking backwards is very slow, and for writing, it is downright impossible. Therefore, QuaGzipFile does not support seeking at all. */ virtual bool isSequential() const; /// Opens the file. /** \param mode Can be either QIODevice::Write or QIODevice::Read. ReadWrite and Append aren't supported. */ virtual bool open(QIODevice::OpenMode mode); /// Opens the file. /** \overload \param fd The file descriptor to read/write the GZIP file from/to. \param mode Can be either QIODevice::Write or QIODevice::Read. ReadWrite and Append aren't supported. */ virtual bool open(int fd, QIODevice::OpenMode mode); /// Flushes data to file. /** The data is written using Z_SYNC_FLUSH mode. Doesn't make any sense when reading. */ virtual bool flush(); /// Closes the file. virtual void close(); protected: /// Implementation of QIODevice::readData(). virtual qint64 readData(char *data, qint64 maxSize); /// Implementation of QIODevice::writeData(). virtual qint64 writeData(const char *data, qint64 maxSize); private: // not implemented by design to disable copy QuaGzipFile(const QuaGzipFile &that); QuaGzipFile& operator=(const QuaGzipFile &that); QuaGzipFilePrivate *d; }; #endif // QUAZIP_QUAGZIPFILE_H connectome-workbench-1.2.3+git41-gc4c6c90/src/Quazip/quaziodevice.cpp000066400000000000000000000175201300200146000252160ustar00rootroot00000000000000#include "quaziodevice.h" #define QUAZIO_INBUFSIZE 4096 #define QUAZIO_OUTBUFSIZE 4096 /// \cond internal class QuaZIODevicePrivate { friend class QuaZIODevice; QuaZIODevicePrivate(QIODevice *io); ~QuaZIODevicePrivate(); QIODevice *io; z_stream zins; z_stream zouts; char *inBuf; int inBufPos; int inBufSize; char *outBuf; int outBufPos; int outBufSize; bool zBufError; int doFlush(QString &error); }; QuaZIODevicePrivate::QuaZIODevicePrivate(QIODevice *io): io(io), inBuf(NULL), inBufPos(0), inBufSize(0), outBuf(NULL), outBufPos(0), outBufSize(0), zBufError(false) { zins.zalloc = (alloc_func) NULL; zins.zfree = (free_func) NULL; zins.opaque = NULL; zouts.zalloc = (alloc_func) NULL; zouts.zfree = (free_func) NULL; zouts.opaque = NULL; inBuf = new char[QUAZIO_INBUFSIZE]; outBuf = new char[QUAZIO_OUTBUFSIZE]; #ifdef QUAZIP_ZIODEVICE_DEBUG_OUTPUT debug.setFileName("debug.out"); debug.open(QIODevice::WriteOnly); #endif #ifdef QUAZIP_ZIODEVICE_DEBUG_INPUT indebug.setFileName("debug.in"); indebug.open(QIODevice::WriteOnly); #endif } QuaZIODevicePrivate::~QuaZIODevicePrivate() { #ifdef QUAZIP_ZIODEVICE_DEBUG_OUTPUT debug.close(); #endif #ifdef QUAZIP_ZIODEVICE_DEBUG_INPUT indebug.close(); #endif if (inBuf != NULL) delete[] inBuf; if (outBuf != NULL) delete[] outBuf; } int QuaZIODevicePrivate::doFlush(QString &error) { int flushed = 0; while (outBufPos < outBufSize) { int more = io->write(outBuf + outBufPos, outBufSize - outBufPos); if (more == -1) { error = io->errorString(); return -1; } if (more == 0) break; outBufPos += more; flushed += more; } if (outBufPos == outBufSize) { outBufPos = outBufSize = 0; } return flushed; } /// \endcond // #define QUAZIP_ZIODEVICE_DEBUG_OUTPUT // #define QUAZIP_ZIODEVICE_DEBUG_INPUT #ifdef QUAZIP_ZIODEVICE_DEBUG_OUTPUT #include static QFile debug; #endif #ifdef QUAZIP_ZIODEVICE_DEBUG_INPUT #include static QFile indebug; #endif QuaZIODevice::QuaZIODevice(QIODevice *io, QObject *parent): QIODevice(parent), d(new QuaZIODevicePrivate(io)) { connect(io, SIGNAL(readyRead()), SIGNAL(readyRead())); } QuaZIODevice::~QuaZIODevice() { if (isOpen()) close(); delete d; } QIODevice *QuaZIODevice::getIoDevice() const { return d->io; } bool QuaZIODevice::open(QIODevice::OpenMode mode) { if ((mode & QIODevice::Append) != 0) { setErrorString(trUtf8("QIODevice::Append is not supported for" " QuaZIODevice")); return false; } if ((mode & QIODevice::ReadWrite) == QIODevice::ReadWrite) { setErrorString(trUtf8("QIODevice::ReadWrite is not supported for" " QuaZIODevice")); return false; } if ((mode & QIODevice::ReadOnly) != 0) { if (inflateInit(&d->zins) != Z_OK) { setErrorString(d->zins.msg); return false; } } if ((mode & QIODevice::WriteOnly) != 0) { if (deflateInit(&d->zouts, Z_DEFAULT_COMPRESSION) != Z_OK) { setErrorString(d->zouts.msg); return false; } } return QIODevice::open(mode); } void QuaZIODevice::close() { if ((openMode() & QIODevice::ReadOnly) != 0) { if (inflateEnd(&d->zins) != Z_OK) { setErrorString(d->zins.msg); } } if ((openMode() & QIODevice::WriteOnly) != 0) { flush(); if (deflateEnd(&d->zouts) != Z_OK) { setErrorString(d->zouts.msg); } } QIODevice::close(); } qint64 QuaZIODevice::readData(char *data, qint64 maxSize) { int read = 0; while (read < maxSize) { if (d->inBufPos == d->inBufSize) { d->inBufPos = 0; d->inBufSize = d->io->read(d->inBuf, QUAZIO_INBUFSIZE); if (d->inBufSize == -1) { d->inBufSize = 0; setErrorString(d->io->errorString()); return -1; } if (d->inBufSize == 0) break; } while (read < maxSize && d->inBufPos < d->inBufSize) { d->zins.next_in = (Bytef *) (d->inBuf + d->inBufPos); d->zins.avail_in = d->inBufSize - d->inBufPos; d->zins.next_out = (Bytef *) (data + read); d->zins.avail_out = (uInt) (maxSize - read); // hope it's less than 2GB int more = 0; switch (inflate(&d->zins, Z_SYNC_FLUSH)) { case Z_OK: read = (char *) d->zins.next_out - data; d->inBufPos = (char *) d->zins.next_in - d->inBuf; break; case Z_STREAM_END: read = (char *) d->zins.next_out - data; d->inBufPos = (char *) d->zins.next_in - d->inBuf; return read; case Z_BUF_ERROR: // this should never happen, but just in case if (!d->zBufError) { qWarning("Z_BUF_ERROR detected with %d/%d in/out, weird", d->zins.avail_in, d->zins.avail_out); d->zBufError = true; } memmove(d->inBuf, d->inBuf + d->inBufPos, d->inBufSize - d->inBufPos); d->inBufSize -= d->inBufPos; d->inBufPos = 0; more = d->io->read(d->inBuf + d->inBufSize, QUAZIO_INBUFSIZE - d->inBufSize); if (more == -1) { setErrorString(d->io->errorString()); return -1; } if (more == 0) return read; d->inBufSize += more; break; default: setErrorString(QString::fromLocal8Bit(d->zins.msg)); return -1; } } } #ifdef QUAZIP_ZIODEVICE_DEBUG_INPUT indebug.write(data, read); #endif return read; } qint64 QuaZIODevice::writeData(const char *data, qint64 maxSize) { int written = 0; QString error; if (d->doFlush(error) == -1) { setErrorString(error); return -1; } while (written < maxSize) { // there is some data waiting in the output buffer if (d->outBufPos < d->outBufSize) return written; d->zouts.next_in = (Bytef *) (data + written); d->zouts.avail_in = (uInt) (maxSize - written); // hope it's less than 2GB d->zouts.next_out = (Bytef *) d->outBuf; d->zouts.avail_out = QUAZIO_OUTBUFSIZE; switch (deflate(&d->zouts, Z_NO_FLUSH)) { case Z_OK: written = (char *) d->zouts.next_in - data; d->outBufSize = (char *) d->zouts.next_out - d->outBuf; break; default: setErrorString(QString::fromLocal8Bit(d->zouts.msg)); return -1; } if (d->doFlush(error) == -1) { setErrorString(error); return -1; } } #ifdef QUAZIP_ZIODEVICE_DEBUG_OUTPUT debug.write(data, written); #endif return written; } bool QuaZIODevice::flush() { QString error; if (d->doFlush(error) < 0) { setErrorString(error); return false; } // can't flush buffer, some data is still waiting if (d->outBufPos < d->outBufSize) return true; Bytef c = 0; d->zouts.next_in = &c; // fake input buffer d->zouts.avail_in = 0; // of zero size do { d->zouts.next_out = (Bytef *) d->outBuf; d->zouts.avail_out = QUAZIO_OUTBUFSIZE; switch (deflate(&d->zouts, Z_SYNC_FLUSH)) { case Z_OK: d->outBufSize = (char *) d->zouts.next_out - d->outBuf; if (d->doFlush(error) < 0) { setErrorString(error); return false; } if (d->outBufPos < d->outBufSize) return true; break; case Z_BUF_ERROR: // nothing to write? return true; default: setErrorString(QString::fromLocal8Bit(d->zouts.msg)); return false; } } while (d->zouts.avail_out == 0); return true; } bool QuaZIODevice::isSequential() const { return true; } connectome-workbench-1.2.3+git41-gc4c6c90/src/Quazip/quaziodevice.h000066400000000000000000000045651300200146000246700ustar00rootroot00000000000000#ifndef QUAZIP_QUAZIODEVICE_H #define QUAZIP_QUAZIODEVICE_H #include #include "quazip_global.h" #include class QuaZIODevicePrivate; /// A class to compress/decompress QIODevice. /** This class can be used to compress any data written to QIODevice or decompress it back. Compressing data sent over a QTcpSocket is a good example. */ class QUAZIP_EXPORT QuaZIODevice: public QIODevice { Q_OBJECT public: /// Constructor. /** \param io The QIODevice to read/write. \param parent The parent object, as per QObject logic. */ QuaZIODevice(QIODevice *io, QObject *parent = NULL); /// Destructor. ~QuaZIODevice(); /// Flushes data waiting to be written. /** Unfortunately, as QIODevice doesn't support flush() by itself, the only thing this method does is write the compressed data into the device using Z_SYNC_FLUSH mode. If you need the compressed data to actually be flushed from the buffer of the underlying QIODevice, you need to call its flush() method as well, providing it supports it (like QTcpSocket does). Example: \code QuaZIODevice dev(&sock); dev.open(QIODevice::Write); dev.write(yourDataGoesHere); dev.flush(); sock->flush(); // this actually sends data to network \endcode This may change in the future versions of QuaZIP by implementing an ugly hack: trying to cast the QIODevice using qobject_cast to known flush()-supporting subclasses, and calling flush if the resulting pointer is not zero. */ virtual bool flush(); /// Opens the device. /** \param mode Neither QIODevice::ReadWrite nor QIODevice::Append are not supported. */ virtual bool open(QIODevice::OpenMode mode); /// Closes this device, but not the underlying one. /** The underlying QIODevice is not closed in case you want to write something else to it. */ virtual void close(); /// Returns the underlying device. QIODevice *getIoDevice() const; /// Returns true. virtual bool isSequential() const; protected: /// Implementation of QIODevice::readData(). virtual qint64 readData(char *data, qint64 maxSize); /// Implementation of QIODevice::writeData(). virtual qint64 writeData(const char *data, qint64 maxSize); private: QuaZIODevicePrivate *d; }; #endif // QUAZIP_QUAZIODEVICE_H connectome-workbench-1.2.3+git41-gc4c6c90/src/Quazip/quazip.cpp000066400000000000000000000507201300200146000240360ustar00rootroot00000000000000/* Copyright (C) 2005-2011 Sergey A. Tachenov This program is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser 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 Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA See COPYING file for the full LGPL text. Original ZIP package is copyrighted by Gilles Vollant, see quazip/(un)zip.h files for details, basically it's zlib license. **/ #include #include #include #include "quazip.h" /// All the internal stuff for the QuaZip class. /** \internal This class keeps all the private stuff for the QuaZip class so it can be changed without breaking binary compatibility, according to the Pimpl idiom. */ class QuaZipPrivate { friend class QuaZip; private: /// The pointer to the corresponding QuaZip instance. QuaZip *q; /// The codec for file names. QTextCodec *fileNameCodec; /// The codec for comments. QTextCodec *commentCodec; /// The archive file name. QString zipName; /// The device to access the archive. QIODevice *ioDevice; /// The global comment. QString comment; /// The open mode. QuaZip::Mode mode; union { /// The internal handle for UNZIP modes. unzFile unzFile_f; /// The internal handle for ZIP modes. zipFile zipFile_f; }; /// Whether a current file is set. bool hasCurrentFile_f; /// The last error. int zipError; /// Whether \ref QuaZip::setDataDescriptorWritingEnabled() "the data descriptor writing mode" is enabled. bool dataDescriptorWritingEnabled; /// The zip64 mode. bool zip64; inline QTextCodec *getDefaultFileNameCodec() { if (defaultFileNameCodec == NULL) { return QTextCodec::codecForLocale(); } else { return defaultFileNameCodec; } } /// The constructor for the corresponding QuaZip constructor. inline QuaZipPrivate(QuaZip *q): q(q), fileNameCodec(getDefaultFileNameCodec()), commentCodec(QTextCodec::codecForLocale()), ioDevice(NULL), mode(QuaZip::mdNotOpen), hasCurrentFile_f(false), zipError(UNZ_OK), dataDescriptorWritingEnabled(true), zip64(false) { lastMappedDirectoryEntry.num_of_file = 0; lastMappedDirectoryEntry.pos_in_zip_directory = 0; } /// The constructor for the corresponding QuaZip constructor. inline QuaZipPrivate(QuaZip *q, const QString &zipName): q(q), fileNameCodec(getDefaultFileNameCodec()), commentCodec(QTextCodec::codecForLocale()), zipName(zipName), ioDevice(NULL), mode(QuaZip::mdNotOpen), hasCurrentFile_f(false), zipError(UNZ_OK), dataDescriptorWritingEnabled(true), zip64(false) { lastMappedDirectoryEntry.num_of_file = 0; lastMappedDirectoryEntry.pos_in_zip_directory = 0; } /// The constructor for the corresponding QuaZip constructor. inline QuaZipPrivate(QuaZip *q, QIODevice *ioDevice): q(q), fileNameCodec(getDefaultFileNameCodec()), commentCodec(QTextCodec::codecForLocale()), ioDevice(ioDevice), mode(QuaZip::mdNotOpen), hasCurrentFile_f(false), zipError(UNZ_OK), dataDescriptorWritingEnabled(true), zip64(false) { lastMappedDirectoryEntry.num_of_file = 0; lastMappedDirectoryEntry.pos_in_zip_directory = 0; } /// Returns either a list of file names or a list of QuaZipFileInfo. template bool getFileInfoList(QList *result) const; /// Stores map of filenames and file locations for unzipping inline void clearDirectoryMap(); inline void addCurrentFileToDirectoryMap(const QString &fileName); bool goToFirstUnmappedFile(); QHash directoryCaseSensitive; QHash directoryCaseInsensitive; unz64_file_pos lastMappedDirectoryEntry; static QTextCodec *defaultFileNameCodec; }; QTextCodec *QuaZipPrivate::defaultFileNameCodec = NULL; void QuaZipPrivate::clearDirectoryMap() { directoryCaseInsensitive.clear(); directoryCaseSensitive.clear(); lastMappedDirectoryEntry.num_of_file = 0; lastMappedDirectoryEntry.pos_in_zip_directory = 0; } void QuaZipPrivate::addCurrentFileToDirectoryMap(const QString &fileName) { if (!hasCurrentFile_f || fileName.isEmpty()) { return; } // Adds current file to filename map as fileName unz64_file_pos fileDirectoryPos; unzGetFilePos64(unzFile_f, &fileDirectoryPos); directoryCaseSensitive.insert(fileName, fileDirectoryPos); // Only add lowercase to directory map if not already there // ensures only map the first one seen QString lower = fileName.toLower(); if (!directoryCaseInsensitive.contains(lower)) directoryCaseInsensitive.insert(lower, fileDirectoryPos); // Mark last one if (fileDirectoryPos.pos_in_zip_directory > lastMappedDirectoryEntry.pos_in_zip_directory) lastMappedDirectoryEntry = fileDirectoryPos; } bool QuaZipPrivate::goToFirstUnmappedFile() { zipError = UNZ_OK; if (mode != QuaZip::mdUnzip) { qWarning("QuaZipPrivate::goToNextUnmappedFile(): ZIP is not open in mdUnzip mode"); return false; } // If not mapped anything, go to beginning if (lastMappedDirectoryEntry.pos_in_zip_directory == 0) { unzGoToFirstFile(unzFile_f); } else { // Goto the last one mapped, plus one unzGoToFilePos64(unzFile_f, &lastMappedDirectoryEntry); unzGoToNextFile(unzFile_f); } hasCurrentFile_f=zipError==UNZ_OK; if(zipError==UNZ_END_OF_LIST_OF_FILE) zipError=UNZ_OK; return hasCurrentFile_f; } QuaZip::QuaZip(): p(new QuaZipPrivate(this)) { } QuaZip::QuaZip(const QString& zipName): p(new QuaZipPrivate(this, zipName)) { } QuaZip::QuaZip(QIODevice *ioDevice): p(new QuaZipPrivate(this, ioDevice)) { } QuaZip::~QuaZip() { if(isOpen()) close(); delete p; } bool QuaZip::open(Mode mode, zlib_filefunc_def* ioApi) { p->zipError=UNZ_OK; if(isOpen()) { qWarning("QuaZip::open(): ZIP already opened"); return false; } QIODevice *ioDevice = p->ioDevice; if (ioDevice == NULL) { if (p->zipName.isEmpty()) { qWarning("QuaZip::open(): set either ZIP file name or IO device first"); return false; } else { ioDevice = new QFile(p->zipName); } } switch(mode) { case mdUnzip: if (ioApi == NULL) { p->unzFile_f=unzOpen2_64(ioDevice, NULL); } else { // QuaZIP pre-zip64 compatibility mode p->unzFile_f=unzOpen2(ioDevice, ioApi); } if(p->unzFile_f!=NULL) { p->mode=mode; p->ioDevice = ioDevice; return true; } else { p->zipError=UNZ_OPENERROR; if (!p->zipName.isEmpty()) delete ioDevice; return false; } case mdCreate: case mdAppend: case mdAdd: if (ioApi == NULL) { p->zipFile_f=zipOpen2_64(ioDevice, mode==mdCreate?APPEND_STATUS_CREATE: mode==mdAppend?APPEND_STATUS_CREATEAFTER: APPEND_STATUS_ADDINZIP, NULL, NULL); } else { // QuaZIP pre-zip64 compatibility mode p->zipFile_f=zipOpen2(ioDevice, mode==mdCreate?APPEND_STATUS_CREATE: mode==mdAppend?APPEND_STATUS_CREATEAFTER: APPEND_STATUS_ADDINZIP, NULL, ioApi); } if(p->zipFile_f!=NULL) { p->mode=mode; p->ioDevice = ioDevice; return true; } else { p->zipError=UNZ_OPENERROR; if (!p->zipName.isEmpty()) delete ioDevice; return false; } default: qWarning("QuaZip::open(): unknown mode: %d", (int)mode); if (!p->zipName.isEmpty()) delete ioDevice; return false; break; } } void QuaZip::close() { p->zipError=UNZ_OK; switch(p->mode) { case mdNotOpen: qWarning("QuaZip::close(): ZIP is not open"); return; case mdUnzip: p->zipError=unzClose(p->unzFile_f); break; case mdCreate: case mdAppend: case mdAdd: p->zipError=zipClose(p->zipFile_f, p->comment.isNull() ? NULL : p->commentCodec->fromUnicode(p->comment).constData()); break; default: qWarning("QuaZip::close(): unknown mode: %d", (int)p->mode); return; } // opened by name, need to delete the internal IO device if (!p->zipName.isEmpty()) { delete p->ioDevice; p->ioDevice = NULL; } p->clearDirectoryMap(); if(p->zipError==UNZ_OK) p->mode=mdNotOpen; } void QuaZip::setZipName(const QString& zipName) { if(isOpen()) { qWarning("QuaZip::setZipName(): ZIP is already open!"); return; } p->zipName=zipName; p->ioDevice = NULL; } void QuaZip::setIoDevice(QIODevice *ioDevice) { if(isOpen()) { qWarning("QuaZip::setIoDevice(): ZIP is already open!"); return; } p->ioDevice = ioDevice; p->zipName = QString(); } int QuaZip::getEntriesCount()const { QuaZip *fakeThis=(QuaZip*)this; // non-const fakeThis->p->zipError=UNZ_OK; if(p->mode!=mdUnzip) { qWarning("QuaZip::getEntriesCount(): ZIP is not open in mdUnzip mode"); return -1; } unz_global_info64 globalInfo; if((fakeThis->p->zipError=unzGetGlobalInfo64(p->unzFile_f, &globalInfo))!=UNZ_OK) return p->zipError; return (int)globalInfo.number_entry; } QString QuaZip::getComment()const { QuaZip *fakeThis=(QuaZip*)this; // non-const fakeThis->p->zipError=UNZ_OK; if(p->mode!=mdUnzip) { qWarning("QuaZip::getComment(): ZIP is not open in mdUnzip mode"); return QString(); } unz_global_info64 globalInfo; QByteArray comment; if((fakeThis->p->zipError=unzGetGlobalInfo64(p->unzFile_f, &globalInfo))!=UNZ_OK) return QString(); comment.resize(globalInfo.size_comment); if((fakeThis->p->zipError=unzGetGlobalComment(p->unzFile_f, comment.data(), comment.size())) < 0) return QString(); fakeThis->p->zipError = UNZ_OK; return p->commentCodec->toUnicode(comment); } bool QuaZip::setCurrentFile(const QString& fileName, CaseSensitivity cs) { p->zipError=UNZ_OK; if(p->mode!=mdUnzip) { qWarning("QuaZip::setCurrentFile(): ZIP is not open in mdUnzip mode"); return false; } if(fileName.isEmpty()) { p->hasCurrentFile_f=false; return true; } // Unicode-aware reimplementation of the unzLocateFile function if(p->unzFile_f==NULL) { p->zipError=UNZ_PARAMERROR; return false; } if(fileName.length()>MAX_FILE_NAME_LENGTH) { p->zipError=UNZ_PARAMERROR; return false; } // Find the file by name bool sens = convertCaseSensitivity(cs) == Qt::CaseSensitive; QString lower, current; if(!sens) lower=fileName.toLower(); p->hasCurrentFile_f=false; // Check the appropriate Map unz64_file_pos fileDirPos; fileDirPos.pos_in_zip_directory = 0; if (sens) { if (p->directoryCaseSensitive.contains(fileName)) fileDirPos = p->directoryCaseSensitive.value(fileName); } else { if (p->directoryCaseInsensitive.contains(lower)) fileDirPos = p->directoryCaseInsensitive.value(lower); } if (fileDirPos.pos_in_zip_directory != 0) { p->zipError = unzGoToFilePos64(p->unzFile_f, &fileDirPos); p->hasCurrentFile_f = p->zipError == UNZ_OK; } if (p->hasCurrentFile_f) return p->hasCurrentFile_f; // Not mapped yet, start from where we have got to so far for(bool more=p->goToFirstUnmappedFile(); more; more=goToNextFile()) { current=getCurrentFileName(); if(current.isEmpty()) return false; if(sens) { if(current==fileName) break; } else { if(current.toLower()==lower) break; } } return p->hasCurrentFile_f; } bool QuaZip::goToFirstFile() { p->zipError=UNZ_OK; if(p->mode!=mdUnzip) { qWarning("QuaZip::goToFirstFile(): ZIP is not open in mdUnzip mode"); return false; } p->zipError=unzGoToFirstFile(p->unzFile_f); p->hasCurrentFile_f=p->zipError==UNZ_OK; return p->hasCurrentFile_f; } bool QuaZip::goToNextFile() { p->zipError=UNZ_OK; if(p->mode!=mdUnzip) { qWarning("QuaZip::goToFirstFile(): ZIP is not open in mdUnzip mode"); return false; } p->zipError=unzGoToNextFile(p->unzFile_f); p->hasCurrentFile_f=p->zipError==UNZ_OK; if(p->zipError==UNZ_END_OF_LIST_OF_FILE) p->zipError=UNZ_OK; return p->hasCurrentFile_f; } bool QuaZip::getCurrentFileInfo(QuaZipFileInfo *info)const { QuaZipFileInfo64 info64; if (info == NULL) { // Very unlikely because of the overloads return false; } if (getCurrentFileInfo(&info64)) { info->versionCreated=info64.versionCreated; info->versionNeeded=info64.versionNeeded; info->flags=info64.flags; info->method=info64.method; info->crc=info64.crc; info->compressedSize=static_cast(info64.compressedSize); info->uncompressedSize=static_cast(info64.uncompressedSize); info->diskNumberStart=info64.diskNumberStart; info->internalAttr=info64.internalAttr; info->externalAttr=info64.externalAttr; info->name=info64.name; info->comment=info64.comment; info->extra=info64.extra; info->dateTime=info64.dateTime; return true; } else { return false; } } bool QuaZip::getCurrentFileInfo(QuaZipFileInfo64 *info)const { QuaZip *fakeThis=(QuaZip*)this; // non-const fakeThis->p->zipError=UNZ_OK; if(p->mode!=mdUnzip) { qWarning("QuaZip::getCurrentFileInfo(): ZIP is not open in mdUnzip mode"); return false; } unz_file_info64 info_z; QByteArray fileName; QByteArray extra; QByteArray comment; if(info==NULL) return false; if(!isOpen()||!hasCurrentFile()) return false; if((fakeThis->p->zipError=unzGetCurrentFileInfo64(p->unzFile_f, &info_z, NULL, 0, NULL, 0, NULL, 0))!=UNZ_OK) return false; fileName.resize(info_z.size_filename); extra.resize(info_z.size_file_extra); comment.resize(info_z.size_file_comment); if((fakeThis->p->zipError=unzGetCurrentFileInfo64(p->unzFile_f, NULL, fileName.data(), fileName.size(), extra.data(), extra.size(), comment.data(), comment.size()))!=UNZ_OK) return false; info->versionCreated=info_z.version; info->versionNeeded=info_z.version_needed; info->flags=info_z.flag; info->method=info_z.compression_method; info->crc=info_z.crc; info->compressedSize=info_z.compressed_size; info->uncompressedSize=info_z.uncompressed_size; info->diskNumberStart=info_z.disk_num_start; info->internalAttr=info_z.internal_fa; info->externalAttr=info_z.external_fa; info->name=p->fileNameCodec->toUnicode(fileName); info->comment=p->commentCodec->toUnicode(comment); info->extra=extra; info->dateTime=QDateTime( QDate(info_z.tmu_date.tm_year, info_z.tmu_date.tm_mon+1, info_z.tmu_date.tm_mday), QTime(info_z.tmu_date.tm_hour, info_z.tmu_date.tm_min, info_z.tmu_date.tm_sec)); // Add to directory map p->addCurrentFileToDirectoryMap(info->name); return true; } QString QuaZip::getCurrentFileName()const { QuaZip *fakeThis=(QuaZip*)this; // non-const fakeThis->p->zipError=UNZ_OK; if(p->mode!=mdUnzip) { qWarning("QuaZip::getCurrentFileName(): ZIP is not open in mdUnzip mode"); return QString(); } if(!isOpen()||!hasCurrentFile()) return QString(); QByteArray fileName(MAX_FILE_NAME_LENGTH, 0); if((fakeThis->p->zipError=unzGetCurrentFileInfo64(p->unzFile_f, NULL, fileName.data(), fileName.size(), NULL, 0, NULL, 0))!=UNZ_OK) return QString(); QString result = p->fileNameCodec->toUnicode(fileName.constData()); if (result.isEmpty()) return result; // Add to directory map p->addCurrentFileToDirectoryMap(result); return result; } void QuaZip::setFileNameCodec(QTextCodec *fileNameCodec) { p->fileNameCodec=fileNameCodec; } void QuaZip::setFileNameCodec(const char *fileNameCodecName) { p->fileNameCodec=QTextCodec::codecForName(fileNameCodecName); } QTextCodec *QuaZip::getFileNameCodec()const { return p->fileNameCodec; } void QuaZip::setCommentCodec(QTextCodec *commentCodec) { p->commentCodec=commentCodec; } void QuaZip::setCommentCodec(const char *commentCodecName) { p->commentCodec=QTextCodec::codecForName(commentCodecName); } QTextCodec *QuaZip::getCommentCodec()const { return p->commentCodec; } QString QuaZip::getZipName() const { return p->zipName; } QIODevice *QuaZip::getIoDevice() const { if (!p->zipName.isEmpty()) // opened by name, using an internal QIODevice return NULL; return p->ioDevice; } QuaZip::Mode QuaZip::getMode()const { return p->mode; } bool QuaZip::isOpen()const { return p->mode!=mdNotOpen; } int QuaZip::getZipError() const { return p->zipError; } void QuaZip::setComment(const QString& comment) { p->comment=comment; } bool QuaZip::hasCurrentFile()const { return p->hasCurrentFile_f; } unzFile QuaZip::getUnzFile() { return p->unzFile_f; } zipFile QuaZip::getZipFile() { return p->zipFile_f; } void QuaZip::setDataDescriptorWritingEnabled(bool enabled) { p->dataDescriptorWritingEnabled = enabled; } bool QuaZip::isDataDescriptorWritingEnabled() const { return p->dataDescriptorWritingEnabled; } template TFileInfo QuaZip_getFileInfo(QuaZip *zip, bool *ok); template<> QuaZipFileInfo QuaZip_getFileInfo(QuaZip *zip, bool *ok) { QuaZipFileInfo info; *ok = zip->getCurrentFileInfo(&info); return info; } template<> QString QuaZip_getFileInfo(QuaZip *zip, bool *ok) { QString name = zip->getCurrentFileName(); *ok = !name.isEmpty(); return name; } template bool QuaZipPrivate::getFileInfoList(QList *result) const { QuaZipPrivate *fakeThis=const_cast(this); fakeThis->zipError=UNZ_OK; if (mode!=QuaZip::mdUnzip) { qWarning("QuaZip::getFileNameList/getFileInfoList(): " "ZIP is not open in mdUnzip mode"); return false; } QString currentFile; if (q->hasCurrentFile()) { currentFile = q->getCurrentFileName(); } if (q->goToFirstFile()) { do { bool ok; result->append(QuaZip_getFileInfo(q, &ok)); if (!ok) return false; } while (q->goToNextFile()); } if (zipError != UNZ_OK) return false; if (currentFile.isEmpty()) { if (!q->goToFirstFile()) return false; } else { if (!q->setCurrentFile(currentFile)) return false; } return true; } QStringList QuaZip::getFileNameList() const { QStringList list; if (p->getFileInfoList(&list)) return list; else return QStringList(); } QList QuaZip::getFileInfoList() const { QList list; if (p->getFileInfoList(&list)) return list; else return QList(); } Qt::CaseSensitivity QuaZip::convertCaseSensitivity(QuaZip::CaseSensitivity cs) { if (cs == csDefault) { #ifdef Q_WS_WIN return Qt::CaseInsensitive; #else return Qt::CaseSensitive; #endif } else { return cs == csSensitive ? Qt::CaseSensitive : Qt::CaseInsensitive; } } void QuaZip::setDefaultFileNameCodec(QTextCodec *codec) { QuaZipPrivate::defaultFileNameCodec = codec; } void QuaZip::setDefaultFileNameCodec(const char *codecName) { setDefaultFileNameCodec(QTextCodec::codecForName(codecName)); } void QuaZip::setZip64Enabled(bool zip64) { p->zip64 = zip64; } bool QuaZip::isZip64Enabled() const { return p->zip64; } connectome-workbench-1.2.3+git41-gc4c6c90/src/Quazip/quazip.h000066400000000000000000000532231300200146000235040ustar00rootroot00000000000000#ifndef QUA_ZIP_H #define QUA_ZIP_H /* Copyright (C) 2005-2011 Sergey A. Tachenov This program is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser 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 Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA See COPYING file for the full LGPL text. Original ZIP package is copyrighted by Gilles Vollant, see quazip/(un)zip.h files for details, basically it's zlib license. **/ #include #include #include #include "zip.h" #include "unzip.h" #include "quazip_global.h" #include "quazipfileinfo.h" // just in case it will be defined in the later versions of the ZIP/UNZIP #ifndef UNZ_OPENERROR // define additional error code #define UNZ_OPENERROR -1000 #endif class QuaZipPrivate; /// ZIP archive. /** \class QuaZip quazip.h * This class implements basic interface to the ZIP archive. It can be * used to read table contents of the ZIP archive and retreiving * information about the files inside it. * * You can also use this class to open files inside archive by passing * pointer to the instance of this class to the constructor of the * QuaZipFile class. But see QuaZipFile::QuaZipFile(QuaZip*, QObject*) * for the possible pitfalls. * * This class is indended to provide interface to the ZIP subpackage of * the ZIP/UNZIP package as well as to the UNZIP subpackage. But * currently it supports only UNZIP. * * The use of this class is simple - just create instance using * constructor, then set ZIP archive file name using setFile() function * (if you did not passed the name to the constructor), then open() and * then use different functions to work with it! Well, if you are * paranoid, you may also wish to call close before destructing the * instance, to check for errors on close. * * You may also use getUnzFile() and getZipFile() functions to get the * ZIP archive handle and use it with ZIP/UNZIP package API directly. * * This class supports localized file names inside ZIP archive, but you * have to set up proper codec with setCodec() function. By default, * locale codec will be used, which is probably ok for UNIX systems, but * will almost certainly fail with ZIP archives created in Windows. This * is because Windows ZIP programs have strange habit of using DOS * encoding for file names in ZIP archives. For example, ZIP archive * with cyrillic names created in Windows will have file names in \c * IBM866 encoding instead of \c WINDOWS-1251. I think that calling one * function is not much trouble, but for true platform independency it * would be nice to have some mechanism for file name encoding auto * detection using locale information. Does anyone know a good way to do * it? **/ class QUAZIP_EXPORT QuaZip { friend class QuaZipPrivate; public: /// Useful constants. enum Constants { MAX_FILE_NAME_LENGTH=256 /**< Maximum file name length. Taken from \c UNZ_MAXFILENAMEINZIP constant in unzip.c. */ }; /// Open mode of the ZIP file. enum Mode { mdNotOpen, ///< ZIP file is not open. This is the initial mode. mdUnzip, ///< ZIP file is open for reading files inside it. mdCreate, ///< ZIP file was created with open() call. mdAppend, /**< ZIP file was opened in append mode. This refers to * \c APPEND_STATUS_CREATEAFTER mode in ZIP/UNZIP package * and means that zip is appended to some existing file * what is useful when that file contains * self-extractor code. This is obviously \em not what * you whant to use to add files to the existing ZIP * archive. **/ mdAdd ///< ZIP file was opened for adding files in the archive. }; /// Case sensitivity for the file names. /** This is what you specify when accessing files in the archive. * Works perfectly fine with any characters thanks to Qt's great * unicode support. This is different from ZIP/UNZIP API, where * only US-ASCII characters was supported. **/ enum CaseSensitivity { csDefault=0, ///< Default for platform. Case sensitive for UNIX, not for Windows. csSensitive=1, ///< Case sensitive. csInsensitive=2 ///< Case insensitive. }; /// Returns the actual case sensitivity for the specified QuaZIP one. /** \param cs The value to convert. \returns If CaseSensitivity::csDefault, then returns the default file name case sensitivity for the platform. Otherwise, just returns the appropriate value from the Qt::CaseSensitivity enum. */ static Qt::CaseSensitivity convertCaseSensitivity( CaseSensitivity cs); private: QuaZipPrivate *p; // not (and will not be) implemented QuaZip(const QuaZip& that); // not (and will not be) implemented QuaZip& operator=(const QuaZip& that); public: /// Constructs QuaZip object. /** Call setName() before opening constructed object. */ QuaZip(); /// Constructs QuaZip object associated with ZIP file \a zipName. QuaZip(const QString& zipName); /// Constructs QuaZip object associated with ZIP file represented by \a ioDevice. /** The IO device must be seekable, otherwise an error will occur when opening. */ QuaZip(QIODevice *ioDevice); /// Destroys QuaZip object. /** Calls close() if necessary. */ ~QuaZip(); /// Opens ZIP file. /** * Argument \a mode specifies open mode of the ZIP archive. See Mode * for details. Note that there is zipOpen2() function in the * ZIP/UNZIP API which accepts \a globalcomment argument, but it * does not use it anywhere, so this open() function does not have this * argument. See setComment() if you need to set global comment. * * If the ZIP file is accessed via explicitly set QIODevice, then * this device is opened in the necessary mode. If the device was * already opened by some other means, then the behaviour is defined by * the device implementation, but generally it is not a very good * idea. For example, QFile will at least issue a warning. * * \return \c true if successful, \c false otherwise. * * \note ZIP/UNZIP API open calls do not return error code - they * just return \c NULL indicating an error. But to make things * easier, quazip.h header defines additional error code \c * UNZ_ERROROPEN and getZipError() will return it if the open call * of the ZIP/UNZIP API returns \c NULL. * * Argument \a ioApi specifies IO function set for ZIP/UNZIP * package to use. See unzip.h, zip.h and ioapi.h for details. Note * that IO API for QuaZip is different from the original package. * The file path argument was changed to be of type \c voidpf, and * QuaZip passes a QIODevice pointer there. This QIODevice is either * set explicitly via setIoDevice() or the QuaZip(QIODevice*) * constructor, or it is created internally when opening the archive * by its file name. The default API (qioapi.cpp) just delegates * everything to the QIODevice API. Not only this allows to use a * QIODevice instead of file name, but also has a nice side effect * of raising the file size limit from 2G to 4G (in non-zip64 archives). * * \note If the zip64 support is needed, the ioApi argument \em must be NULL * because due to the backwards compatibility issues it can be used to * provide a 32-bit API only. * * In short: just forget about the \a ioApi argument and you'll be * fine. **/ bool open(Mode mode, zlib_filefunc_def *ioApi =NULL); /// Closes ZIP file. /** Call getZipError() to determine if the close was successful. The * underlying QIODevice is also closed, regardless of whether it was * set explicitly or not. */ void close(); /// Sets the codec used to encode/decode file names inside archive. /** This is necessary to access files in the ZIP archive created * under Windows with non-latin characters in file names. For * example, file names with cyrillic letters will be in \c IBM866 * encoding. **/ void setFileNameCodec(QTextCodec *fileNameCodec); /// Sets the codec used to encode/decode file names inside archive. /** \overload * Equivalent to calling setFileNameCodec(QTextCodec::codecForName(codecName)); **/ void setFileNameCodec(const char *fileNameCodecName); /// Returns the codec used to encode/decode comments inside archive. QTextCodec* getFileNameCodec() const; /// Sets the codec used to encode/decode comments inside archive. /** This codec defaults to locale codec, which is probably ok. **/ void setCommentCodec(QTextCodec *commentCodec); /// Sets the codec used to encode/decode comments inside archive. /** \overload * Equivalent to calling setCommentCodec(QTextCodec::codecForName(codecName)); **/ void setCommentCodec(const char *commentCodecName); /// Returns the codec used to encode/decode comments inside archive. QTextCodec* getCommentCodec() const; /// Returns the name of the ZIP file. /** Returns null string if no ZIP file name has been set, for * example when the QuaZip instance is set up to use a QIODevice * instead. * \sa setZipName(), setIoDevice(), getIoDevice() **/ QString getZipName() const; /// Sets the name of the ZIP file. /** Does nothing if the ZIP file is open. * * Does not reset error code returned by getZipError(). * \sa setIoDevice(), getIoDevice(), getZipName() **/ void setZipName(const QString& zipName); /// Returns the device representing this ZIP file. /** Returns null string if no device has been set explicitly, for * example when opening a ZIP file by name. * \sa setIoDevice(), getZipName(), setZipName() **/ QIODevice *getIoDevice() const; /// Sets the device representing the ZIP file. /** Does nothing if the ZIP file is open. * * Does not reset error code returned by getZipError(). * \sa getIoDevice(), getZipName(), setZipName() **/ void setIoDevice(QIODevice *ioDevice); /// Returns the mode in which ZIP file was opened. Mode getMode() const; /// Returns \c true if ZIP file is open, \c false otherwise. bool isOpen() const; /// Returns the error code of the last operation. /** Returns \c UNZ_OK if the last operation was successful. * * Error code resets to \c UNZ_OK every time you call any function * that accesses something inside ZIP archive, even if it is \c * const (like getEntriesCount()). open() and close() calls reset * error code too. See documentation for the specific functions for * details on error detection. **/ int getZipError() const; /// Returns number of the entries in the ZIP central directory. /** Returns negative error code in the case of error. The same error * code will be returned by subsequent getZipError() call. **/ int getEntriesCount() const; /// Returns global comment in the ZIP file. QString getComment() const; /// Sets the global comment in the ZIP file. /** The comment will be written to the archive on close operation. * QuaZip makes a distinction between a null QByteArray() comment * and an empty "" comment in the QuaZip::mdAdd mode. * A null comment is the default and it means "don't change * the comment". An empty comment removes the original comment. * * \sa open() **/ void setComment(const QString& comment); /// Sets the current file to the first file in the archive. /** Returns \c true on success, \c false otherwise. Call * getZipError() to get the error code. **/ bool goToFirstFile(); /// Sets the current file to the next file in the archive. /** Returns \c true on success, \c false otherwise. Call * getZipError() to determine if there was an error. * * Should be used only in QuaZip::mdUnzip mode. * * \note If the end of file was reached, getZipError() will return * \c UNZ_OK instead of \c UNZ_END_OF_LIST_OF_FILE. This is to make * things like this easier: * \code * for(bool more=zip.goToFirstFile(); more; more=zip.goToNextFile()) { * // do something * } * if(zip.getZipError()==UNZ_OK) { * // ok, there was no error * } * \endcode **/ bool goToNextFile(); /// Sets current file by its name. /** Returns \c true if successful, \c false otherwise. Argument \a * cs specifies case sensitivity of the file name. Call * getZipError() in the case of a failure to get error code. * * This is not a wrapper to unzLocateFile() function. That is * because I had to implement locale-specific case-insensitive * comparison. * * Here are the differences from the original implementation: * * - If the file was not found, error code is \c UNZ_OK, not \c * UNZ_END_OF_LIST_OF_FILE (see also goToNextFile()). * - If this function fails, it unsets the current file rather than * resetting it back to what it was before the call. * * If \a fileName is null string then this function unsets the * current file and return \c true. Note that you should close the * file first if it is open! See * QuaZipFile::QuaZipFile(QuaZip*,QObject*) for the details. * * Should be used only in QuaZip::mdUnzip mode. * * \sa setFileNameCodec(), CaseSensitivity **/ bool setCurrentFile(const QString& fileName, CaseSensitivity cs =csDefault); /// Returns \c true if the current file has been set. bool hasCurrentFile() const; /// Retrieves information about the current file. /** Fills the structure pointed by \a info. Returns \c true on * success, \c false otherwise. In the latter case structure pointed * by \a info remains untouched. If there was an error, * getZipError() returns error code. * * Should be used only in QuaZip::mdUnzip mode. * * Does nothing and returns \c false in any of the following cases. * - ZIP is not open; * - ZIP does not have current file. * * In both cases getZipError() returns \c UNZ_OK since there * is no ZIP/UNZIP API call. * * This overload doesn't support zip64. * * \sa getCurrentFileInfo(QuaZipFileInfo64* info)const **/ bool getCurrentFileInfo(QuaZipFileInfo* info)const; /// Retrieves information about the current file. /** \overload * * This function supports zip64. If the archive doesn't use zip64, it is * completely equivalent to getCurrentFileInfo(QuaZipFileInfo* info) * except for the argument type. * * \sa **/ bool getCurrentFileInfo(QuaZipFileInfo64* info)const; /// Returns the current file name. /** Equivalent to calling getCurrentFileInfo() and then getting \c * name field of the QuaZipFileInfo structure, but faster and more * convenient. * * Should be used only in QuaZip::mdUnzip mode. **/ QString getCurrentFileName()const; /// Returns \c unzFile handle. /** You can use this handle to directly call UNZIP part of the * ZIP/UNZIP package functions (see unzip.h). * * \warning When using the handle returned by this function, please * keep in mind that QuaZip class is unable to detect any changes * you make in the ZIP file state (e. g. changing current file, or * closing the handle). So please do not do anything with this * handle that is possible to do with the functions of this class. * Or at least return the handle in the original state before * calling some another function of this class (including implicit * destructor calls and calls from the QuaZipFile objects that refer * to this QuaZip instance!). So if you have changed the current * file in the ZIP archive - then change it back or you may * experience some strange behavior or even crashes. **/ unzFile getUnzFile(); /// Returns \c zipFile handle. /** You can use this handle to directly call ZIP part of the * ZIP/UNZIP package functions (see zip.h). Warnings about the * getUnzFile() function also apply to this function. **/ zipFile getZipFile(); /// Changes the data descriptor writing mode. /** According to the ZIP format specification, a file inside archive may have a data descriptor immediately following the file data. This is reflected by a special flag in the local file header and in the central directory. By default, QuaZIP sets this flag and writes the data descriptor unless both method and level were set to 0, in which case it operates in 1.0-compatible mode and never writes data descriptors. By setting this flag to false, it is possible to disable data descriptor writing, thus increasing compatibility with archive readers that don't understand this feature of the ZIP file format. Setting this flag affects all the QuaZipFile instances that are opened after this flag is set. The data descriptor writing mode is enabled by default. \param enabled If \c true, enable local descriptor writing, disable it otherwise. \sa QuaZipFile::setDataDescriptorWritingEnabled() */ void setDataDescriptorWritingEnabled(bool enabled); /// Returns the data descriptor default writing mode. /** \sa setDataDescriptorWritingEnabled() */ bool isDataDescriptorWritingEnabled() const; /// Returns a list of files inside the archive. /** \return A list of file names or an empty list if there was an error or if the archive is empty (call getZipError() to figure out which). \sa getFileInfoList() */ QStringList getFileNameList() const; /// Returns information list about all files inside the archive. /** \return A list of QuaZipFileInfo objects or an empty list if there was an error or if the archive is empty (call getZipError() to figure out which). \sa getFileNameList() */ QList getFileInfoList() const; /// Enables the zip64 mode. /** * @param zip64 If \c true, the zip64 mode is enabled, disabled otherwise. * * Once this is enabled, all new files (until the mode is disabled again) * will be created in the zip64 mode, thus enabling the ability to write * files larger than 4 GB. By default, the zip64 mode is off due to * compatibility reasons. * * \sa isZip64Enabled() */ void setZip64Enabled(bool zip64); /// Returns whether the zip64 mode is enabled. /** * @return \c true if and only if the zip64 mode is enabled. * * \sa setZip64Enabled() */ bool isZip64Enabled() const; /// Sets the default file name codec to use. /** * The default codec is used by the constructors, so calling this function * won't affect the QuaZip instances already created at that moment. * * The codec specified here can be overriden by calling setFileNameCodec(). * If neither function is called, QTextCodec::codecForLocale() will be used * to decode or encode file names. Use this function with caution if * the application uses other libraries that depend on QuaZIP. Those * libraries can either call this function by themselves, thus overriding * your setting or can rely on the default encoding, thus failing * mysteriously if you change it. For these reasons, it isn't recommended * to use this function if you are developing a library, not an application. * Instead, ask your library users to call it in case they need specific * encoding. * * In most cases, using setFileNameCodec() instead is the right choice. * However, if you depend on third-party code that uses QuaZIP, then the * reasons stated above can actually become a reason to use this function * in case the third-party code in question fails because it doesn't * understand the encoding you need and doesn't provide a way to specify it. * This applies to the JlCompress class as well, as it was contributed and * doesn't support explicit encoding parameters. * * In short: use setFileNameCodec() when you can, resort to * setDefaultFileNameCodec() when you don't have access to the QuaZip * instance. * * @param codec The codec to use by default. If NULL, resets to default. */ static void setDefaultFileNameCodec(QTextCodec *codec); /** * @overload * Equivalent to calling * setDefltFileNameCodec(QTextCodec::codecForName(codecName)). */ static void setDefaultFileNameCodec(const char *codecName); }; #endif connectome-workbench-1.2.3+git41-gc4c6c90/src/Quazip/quazip_global.h000066400000000000000000000032201300200146000250140ustar00rootroot00000000000000/** Copyright (C) 2005-2011 Sergey A. Tachenov This program is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser 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 Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA See COPYING file for the full LGPL text. Original ZIP package is copyrighted by Gilles Vollant, see quazip/(un)zip.h files for details, basically it's zlib license. */ #ifndef QUAZIP_GLOBAL_H #define QUAZIP_GLOBAL_H #include /** This is automatically defined when building a static library, but when including QuaZip sources directly into a project, QUAZIP_STATIC should be defined explicitly to avoid possible troubles with unnecessary importing/exporting. */ #ifdef QUAZIP_STATIC #define QUAZIP_EXPORT #else /** * When building a DLL with MSVC, QUAZIP_BUILD must be defined. * qglobal.h takes care of defining Q_DECL_* correctly for msvc/gcc. */ #if defined(QUAZIP_BUILD) #define QUAZIP_EXPORT Q_DECL_EXPORT #else #define QUAZIP_EXPORT Q_DECL_IMPORT #endif #endif // QUAZIP_STATIC #ifdef __GNUC__ #define UNUSED __attribute__((__unused__)) #else #define UNUSED #endif #endif // QUAZIP_GLOBAL_H connectome-workbench-1.2.3+git41-gc4c6c90/src/Quazip/quazipdir.cpp000066400000000000000000000354371300200146000245450ustar00rootroot00000000000000#include "quazipdir.h" #include #include /// \cond internal class QuaZipDirPrivate: public QSharedData { friend class QuaZipDir; private: QuaZipDirPrivate(QuaZip *zip, const QString &dir = QString()): zip(zip), dir(dir), caseSensitivity(QuaZip::csDefault), filter(QDir::NoFilter), sorting(QDir::NoSort) {} QuaZip *zip; QString dir; QuaZip::CaseSensitivity caseSensitivity; QDir::Filters filter; QStringList nameFilters; QDir::SortFlags sorting; template bool entryInfoList(QStringList nameFilters, QDir::Filters filter, QDir::SortFlags sort, TFileInfoList &result) const; inline QString simplePath() const {return QDir::cleanPath(dir);} }; /// \endcond QuaZipDir::QuaZipDir(const QuaZipDir &that): d(that.d) { } QuaZipDir::QuaZipDir(QuaZip *zip, const QString &dir): d(new QuaZipDirPrivate(zip, dir)) { if (d->dir.startsWith('/')) d->dir = d->dir.mid(1); } QuaZipDir::~QuaZipDir() { } bool QuaZipDir::operator==(const QuaZipDir &that) { return d->zip == that.d->zip && d->dir == that.d->dir; } QuaZipDir& QuaZipDir::operator=(const QuaZipDir &that) { this->d = that.d; return *this; } QString QuaZipDir::operator[](int pos) const { return entryList().at(pos); } QuaZip::CaseSensitivity QuaZipDir::caseSensitivity() const { return d->caseSensitivity; } bool QuaZipDir::cd(const QString &directoryName) { if (directoryName == "/") { d->dir = ""; return true; } QString dirName = directoryName; if (dirName.endsWith('/')) dirName.chop(1); if (dirName.contains('/')) { QuaZipDir dir(*this); if (dirName.startsWith('/')) { #ifdef QUAZIP_QUAZIPDIR_DEBUG qDebug("QuaZipDir::cd(%s): going to /", dirName.toUtf8().constData()); #endif if (!dir.cd("/")) return false; } QStringList path = dirName.split('/', QString::SkipEmptyParts); for (QStringList::const_iterator i = path.constBegin(); i != path.end(); ++i) { const QString &step = *i; #ifdef QUAZIP_QUAZIPDIR_DEBUG qDebug("QuaZipDir::cd(%s): going to %s", dirName.toUtf8().constData(), step.toUtf8().constData()); #endif if (!dir.cd(step)) return false; } d->dir = dir.path(); return true; } else { // no '/' if (dirName == ".") { return true; } else if (dirName == "..") { if (isRoot()) { return false; } else { int slashPos = d->dir.lastIndexOf('/'); if (slashPos == -1) { d->dir = ""; } else { d->dir = d->dir.left(slashPos); } return true; } } else { // a simple subdirectory if (exists(dirName)) { if (isRoot()) d->dir = dirName; else d->dir += "/" + dirName; return true; } else { return false; } } } } bool QuaZipDir::cdUp() { return cd(".."); } uint QuaZipDir::count() const { return entryList().count(); } QString QuaZipDir::dirName() const { return QDir(d->dir).dirName(); } QuaZipFileInfo QuaZipDir_getFileInfo(QuaZip *zip, bool *ok, const QString &relativeName, bool isReal) { QuaZipFileInfo info; if (isReal) { *ok = zip->getCurrentFileInfo(&info); } else { *ok = true; info.compressedSize = 0; info.crc = 0; info.diskNumberStart = 0; info.externalAttr = 0; info.flags = 0; info.internalAttr = 0; info.method = 0; info.uncompressedSize = 0; info.versionCreated = info.versionNeeded = 0; } info.name = relativeName; return info; } template void QuaZipDir_convertInfoList(const QList &from, TFileInfoList &to); template<> void QuaZipDir_convertInfoList(const QList &from, QList &to) { to = from; } template<> void QuaZipDir_convertInfoList(const QList &from, QStringList &to) { to.clear(); for (QList::const_iterator i = from.constBegin(); i != from.constEnd(); ++i) { to.append(i->name); } } /// \cond internal /** An utility class to restore the current file. */ class QuaZipDirRestoreCurrent { public: inline QuaZipDirRestoreCurrent(QuaZip *zip): zip(zip), currentFile(zip->getCurrentFileName()) {} inline ~QuaZipDirRestoreCurrent() { zip->setCurrentFile(currentFile); } private: QuaZip *zip; QString currentFile; }; /// \endcond /// \cond internal class QuaZipDirComparator { private: QDir::SortFlags sort; static QString getExtension(const QString &name); int compareStrings(const QString &string1, const QString &string2); public: inline QuaZipDirComparator(QDir::SortFlags sort): sort(sort) {} bool operator()(const QuaZipFileInfo &info1, const QuaZipFileInfo &info2); }; QString QuaZipDirComparator::getExtension(const QString &name) { if (name.endsWith('.') || name.indexOf('.', 1) == -1) { return ""; } else { return name.mid(name.lastIndexOf('.') + 1); } } int QuaZipDirComparator::compareStrings(const QString &string1, const QString &string2) { if (sort & QDir::LocaleAware) { if (sort & QDir::IgnoreCase) { return string1.toLower().localeAwareCompare(string2.toLower()); } else { return string1.localeAwareCompare(string2); } } else { return string1.compare(string2, (sort & QDir::IgnoreCase) ? Qt::CaseInsensitive : Qt::CaseSensitive); } } bool QuaZipDirComparator::operator()(const QuaZipFileInfo &info1, const QuaZipFileInfo &info2) { QDir::SortFlags order = sort & (QDir::Name | QDir::Time | QDir::Size | QDir::Type); if ((sort & QDir::DirsFirst) == QDir::DirsFirst || (sort & QDir::DirsLast) == QDir::DirsLast) { if (info1.name.endsWith('/') && !info2.name.endsWith('/')) return (sort & QDir::DirsFirst) == QDir::DirsFirst; else if (!info1.name.endsWith('/') && info2.name.endsWith('/')) return (sort & QDir::DirsLast) == QDir::DirsLast; } bool result; int extDiff; switch (order) { case QDir::Name: result = compareStrings(info1.name, info2.name) < 0; break; case QDir::Type: extDiff = compareStrings(getExtension(info1.name), getExtension(info2.name)); if (extDiff == 0) { result = compareStrings(info1.name, info2.name) < 0; } else { result = extDiff < 0; } break; case QDir::Size: if (info1.uncompressedSize == info2.uncompressedSize) { result = compareStrings(info1.name, info2.name) < 0; } else { result = info1.uncompressedSize < info2.uncompressedSize; } break; case QDir::Time: if (info1.dateTime == info2.dateTime) { result = compareStrings(info1.name, info2.name) < 0; } else { result = info1.dateTime < info2.dateTime; } break; default: qWarning("QuaZipDirComparator(): Invalid sort mode 0x%2X", static_cast(sort)); return false; } return (sort & QDir::Reversed) ? !result : result; } template bool QuaZipDirPrivate::entryInfoList(QStringList nameFilters, QDir::Filters filter, QDir::SortFlags sort, TFileInfoList &result) const { QString basePath = simplePath(); if (!basePath.isEmpty()) basePath += "/"; int baseLength = basePath.length(); result.clear(); QuaZipDirRestoreCurrent saveCurrent(zip); if (!zip->goToFirstFile()) { return zip->getZipError() == UNZ_OK; } QDir::Filters fltr = filter; if (fltr == QDir::NoFilter) fltr = this->filter; if (fltr == QDir::NoFilter) fltr = QDir::AllEntries; QStringList nmfltr = nameFilters; if (nmfltr.isEmpty()) nmfltr = this->nameFilters; QSet dirsFound; QList list; do { QString name = zip->getCurrentFileName(); if (!name.startsWith(basePath)) continue; QString relativeName = name.mid(baseLength); if (relativeName.isEmpty()) continue; bool isDir = false; bool isReal = true; if (relativeName.contains('/')) { int indexOfSlash = relativeName.indexOf('/'); // something like "subdir/" isReal = indexOfSlash == relativeName.length() - 1; relativeName = relativeName.left(indexOfSlash + 1); if (dirsFound.contains(relativeName)) continue; isDir = true; } dirsFound.insert(relativeName); if ((fltr & QDir::Dirs) == 0 && isDir) continue; if ((fltr & QDir::Files) == 0 && !isDir) continue; if (!nmfltr.isEmpty() && !QDir::match(nmfltr, relativeName)) continue; bool ok; QuaZipFileInfo info = QuaZipDir_getFileInfo(zip, &ok, relativeName, isReal); if (!ok) { return false; } list.append(info); } while (zip->goToNextFile()); QDir::SortFlags srt = sort; if (srt == QDir::NoSort) srt = sorting; #ifdef QUAZIP_QUAZIPDIR_DEBUG qDebug("QuaZipDirPrivate::entryInfoList(): before sort:"); foreach (QuaZipFileInfo info, list) { qDebug("%s\t%s", info.name.toUtf8().constData(), info.dateTime.toString(Qt::ISODate).toUtf8().constData()); } #endif if (srt != QDir::NoSort && (srt & QDir::Unsorted) != QDir::Unsorted) { if (QuaZip::convertCaseSensitivity(caseSensitivity) == Qt::CaseInsensitive) srt |= QDir::IgnoreCase; QuaZipDirComparator lessThan(srt); qSort(list.begin(), list.end(), lessThan); } QuaZipDir_convertInfoList(list, result); return true; } /// \endcond QList QuaZipDir::entryInfoList(const QStringList &nameFilters, QDir::Filters filters, QDir::SortFlags sort) const { QList result; if (d->entryInfoList(nameFilters, filters, sort, result)) return result; else return QList(); } QList QuaZipDir::entryInfoList(QDir::Filters filters, QDir::SortFlags sort) const { return entryInfoList(QStringList(), filters, sort); } QStringList QuaZipDir::entryList(const QStringList &nameFilters, QDir::Filters filters, QDir::SortFlags sort) const { QStringList result; if (d->entryInfoList(nameFilters, filters, sort, result)) return result; else return QStringList(); } QStringList QuaZipDir::entryList(QDir::Filters filters, QDir::SortFlags sort) const { return entryList(QStringList(), filters, sort); } bool QuaZipDir::exists(const QString &filePath) const { if (filePath == "/") return true; QString fileName = filePath; if (fileName.endsWith('/')) fileName.chop(1); if (fileName.contains('/')) { QFileInfo fileInfo(fileName); #ifdef QUAZIP_QUAZIPDIR_DEBUG qDebug("QuaZipDir::exists(): fileName=%s, fileInfo.fileName()=%s, " "fileInfo.path()=%s", fileName.toUtf8().constData(), fileInfo.fileName().toUtf8().constData(), fileInfo.path().toUtf8().constData()); #endif QuaZipDir dir(*this); return dir.cd(fileInfo.path()) && dir.exists(fileInfo.fileName()); } else { if (fileName == "..") { return !isRoot(); } else if (fileName == ".") { return true; } else { QStringList entries = entryList(QDir::AllEntries, QDir::NoSort); #ifdef QUAZIP_QUAZIPDIR_DEBUG qDebug("QuaZipDir::exists(): looking for %s", fileName.toUtf8().constData()); for (QStringList::const_iterator i = entries.constBegin(); i != entries.constEnd(); ++i) { qDebug("QuaZipDir::exists(): entry: %s", i->toUtf8().constData()); } #endif Qt::CaseSensitivity cs = QuaZip::convertCaseSensitivity( d->caseSensitivity); if (filePath.endsWith('/')) { return entries.contains(filePath, cs); } else { return entries.contains(fileName, cs) || entries.contains(fileName + "/", cs); } } } } bool QuaZipDir::exists() const { QDir thisDir(d->dir); return QuaZipDir(d->zip, thisDir.filePath("..")).exists(thisDir.dirName()); } QString QuaZipDir::filePath(const QString &fileName) const { return QDir(d->dir).filePath(fileName); } QDir::Filters QuaZipDir::filter() { return d->filter; } bool QuaZipDir::isRoot() const { return d->simplePath().isEmpty(); } QStringList QuaZipDir::nameFilters() const { return d->nameFilters; } QString QuaZipDir::path() const { return d->dir; } QString QuaZipDir::relativeFilePath(const QString &fileName) const { return QDir(d->dir).relativeFilePath(fileName); } void QuaZipDir::setCaseSensitivity(QuaZip::CaseSensitivity caseSensitivity) { d->caseSensitivity = caseSensitivity; } void QuaZipDir::setFilter(QDir::Filters filters) { d->filter = filters; } void QuaZipDir::setNameFilters(const QStringList &nameFilters) { d->nameFilters = nameFilters; } void QuaZipDir::setPath(const QString &path) { QString newDir = path; if (newDir == "/") { d->dir = ""; } else { if (newDir.endsWith('/')) newDir.chop(1); if (newDir.startsWith('/')) newDir = newDir.mid(1); d->dir = newDir; } } void QuaZipDir::setSorting(QDir::SortFlags sort) { d->sorting = sort; } QDir::SortFlags QuaZipDir::sorting() const { return d->sorting; } connectome-workbench-1.2.3+git41-gc4c6c90/src/Quazip/quazipdir.h000066400000000000000000000145341300200146000242050ustar00rootroot00000000000000#ifndef QUAZIP_QUAZIPDIR_H #define QUAZIP_QUAZIPDIR_H class QuaZipDirPrivate; #include "quazip.h" #include "quazipfileinfo.h" #include #include #include /// Provides ZIP archive navigation. /** * This class is modelled after QDir, and is designed to provide similar * features for ZIP archives. * * The only significant difference from QDir is that the root path is not * '/', but an empty string since that's how the file paths are stored in * the archive. However, QuaZipDir understands the paths starting with * '/'. It is important in a few places: * * - In the cd() function. * - In the constructor. * - In the exists() function. * * Note that since ZIP uses '/' on all platforms, the '\' separator is * not supported. */ class QUAZIP_EXPORT QuaZipDir { private: QSharedDataPointer d; public: /// The copy constructor. QuaZipDir(const QuaZipDir &that); /// Constructs a QuaZipDir instance pointing to the specified directory. /** If \a dir is not specified, points to the root of the archive. The same happens if the \a dir is "/". */ QuaZipDir(QuaZip *zip, const QString &dir = QString()); /// Destructor. ~QuaZipDir(); /// The assignment operator. bool operator==(const QuaZipDir &that); /// operator!= /** \return \c true if either this and \a that use different QuaZip instances or if they point to different directories. */ inline bool operator!=(const QuaZipDir &that) {return !operator==(that);} /// operator== /** \return \c true if both this and \a that use the same QuaZip instance and point to the same directory. */ QuaZipDir& operator=(const QuaZipDir &that); /// Returns the name of the entry at the specified position. QString operator[](int pos) const; /// Returns the current case sensitivity mode. QuaZip::CaseSensitivity caseSensitivity() const; /// Changes the 'current' directory. /** * If the path starts with '/', it is interpreted as an absolute * path from the root of the archive. Otherwise, it is interpreted * as a path relative to the current directory as was set by the * previous cd() or the constructor. * * Note that the subsequent path() call will not return a path * starting with '/' in all cases. */ bool cd(const QString &dirName); /// Goes up. bool cdUp(); /// Returns the number of entries in the directory. uint count() const; /// Returns the current directory name. /** The name doesn't include the path. */ QString dirName() const; /// Returns the list of the entries in the directory. /** \param nameFilters The list of file patterns to list, uses the same syntax as QDir. \param filters The entry type filters, only Files and Dirs are accepted. \param sort Sorting mode (not supported yet). */ QList entryInfoList(const QStringList &nameFilters, QDir::Filters filters = QDir::NoFilter, QDir::SortFlags sort = QDir::NoSort) const; /// Returns the list of the entries in the directory. /** \overload The same as entryInfoList(QStringList(), filters, sort). */ QList entryInfoList(QDir::Filters filters = QDir::NoFilter, QDir::SortFlags sort = QDir::NoSort) const; /// Returns the list of the entry names in the directory. /** The same as entryInfoList(nameFilters, filters, sort), but only returns entry names. */ QStringList entryList(const QStringList &nameFilters, QDir::Filters filters = QDir::NoFilter, QDir::SortFlags sort = QDir::NoSort) const; /// Returns the list of the entry names in the directory. /** \overload The same as entryList(QStringList(), filters, sort). */ QStringList entryList(QDir::Filters filters = QDir::NoFilter, QDir::SortFlags sort = QDir::NoSort) const; /// Returns \c true if the entry with the specified name exists. /** The ".." is considered to exist if the current directory is not root. The "." and "/" are considered to always exist. Paths starting with "/" are relative to the archive root, other paths are relative to the current dir. */ bool exists(const QString &fileName) const; /// Return \c true if the directory pointed by this QuaZipDir exists. bool exists() const; /// Returns the full path to the specified file. /** Doesn't check if the file actually exists. */ QString filePath(const QString &fileName) const; /// Returns the default filter. QDir::Filters filter(); /// Returns if the QuaZipDir points to the root of the archive. /** Not that the root path is the empty string, not '/'. */ bool isRoot() const; /// Return the default name filter. QStringList nameFilters() const; /// Returns the path to the current dir. /** The path never starts with '/', and the root path is an empty string. */ QString path() const; /// Returns the path to the specified file relative to the current dir. QString relativeFilePath(const QString &fileName) const; /// Sets the default case sensitivity mode. void setCaseSensitivity(QuaZip::CaseSensitivity caseSensitivity); /// Sets the default filter. void setFilter(QDir::Filters filters); /// Sets the default name filter. void setNameFilters(const QStringList &nameFilters); /// Goes to the specified path. /** The difference from cd() is that this function never checks if the path actually exists and doesn't use relative paths, so it's possible to go to the root directory with setPath(""). Note that this function still chops the trailing and/or leading '/' and treats a single '/' as the root path (path() will still return an empty string). */ void setPath(const QString &path); /// Sets the default sorting mode. void setSorting(QDir::SortFlags sort); /// Returns the default sorting mode. QDir::SortFlags sorting() const; }; #endif // QUAZIP_QUAZIPDIR_H connectome-workbench-1.2.3+git41-gc4c6c90/src/Quazip/quazipfile.cpp000066400000000000000000000341321300200146000246750ustar00rootroot00000000000000/* Copyright (C) 2005-2011 Sergey A. Tachenov This program is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser 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 Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA See COPYING file for the full LGPL text. Original ZIP package is copyrighted by Gilles Vollant, see quazip/(un)zip.h files for details, basically it's zlib license. **/ #include "quazipfile.h" using namespace std; /// The implementation class for QuaZip. /** \internal This class contains all the private stuff for the QuaZipFile class, thus allowing to preserve binary compatibility between releases, the technique known as the Pimpl (private implementation) idiom. */ class QuaZipFilePrivate { friend class QuaZipFile; private: /// The pointer to the associated QuaZipFile instance. QuaZipFile *q; /// The QuaZip object to work with. QuaZip *zip; /// The file name. QString fileName; /// Case sensitivity mode. QuaZip::CaseSensitivity caseSensitivity; /// Whether this file is opened in the raw mode. bool raw; /// Write position to keep track of. /** QIODevice::pos() is broken for non-seekable devices, so we need our own position. */ qint64 writePos; /// Uncompressed size to write along with a raw file. quint64 uncompressedSize; /// CRC to write along with a raw file. quint32 crc; /// Whether \ref zip points to an internal QuaZip instance. /** This is true if the archive was opened by name, rather than by supplying an existing QuaZip instance. */ bool internal; /// The last error. int zipError; /// Resets \ref zipError. inline void resetZipError() const {setZipError(UNZ_OK);} /// Sets the zip error. /** This function is marked as const although it changes one field. This allows to call it from const functions that don't change anything by themselves. */ void setZipError(int zipError) const; /// The constructor for the corresponding QuaZipFile constructor. inline QuaZipFilePrivate(QuaZipFile *q): q(q), zip(NULL), internal(true), zipError(UNZ_OK) {} /// The constructor for the corresponding QuaZipFile constructor. inline QuaZipFilePrivate(QuaZipFile *q, const QString &zipName): q(q), internal(true), zipError(UNZ_OK) { zip=new QuaZip(zipName); } /// The constructor for the corresponding QuaZipFile constructor. inline QuaZipFilePrivate(QuaZipFile *q, const QString &zipName, const QString &fileName, QuaZip::CaseSensitivity cs): q(q), internal(true), zipError(UNZ_OK) { zip=new QuaZip(zipName); this->fileName=fileName; if (this->fileName.startsWith('/')) this->fileName = this->fileName.mid(1); this->caseSensitivity=cs; } /// The constructor for the QuaZipFile constructor accepting a file name. inline QuaZipFilePrivate(QuaZipFile *q, QuaZip *zip): q(q), zip(zip), internal(false), zipError(UNZ_OK) {} /// The destructor. inline ~QuaZipFilePrivate() { if (internal) delete zip; } }; QuaZipFile::QuaZipFile(): p(new QuaZipFilePrivate(this)) { } QuaZipFile::QuaZipFile(QObject *parent): QIODevice(parent), p(new QuaZipFilePrivate(this)) { } QuaZipFile::QuaZipFile(const QString& zipName, QObject *parent): QIODevice(parent), p(new QuaZipFilePrivate(this, zipName)) { } QuaZipFile::QuaZipFile(const QString& zipName, const QString& fileName, QuaZip::CaseSensitivity cs, QObject *parent): QIODevice(parent), p(new QuaZipFilePrivate(this, zipName, fileName, cs)) { } QuaZipFile::QuaZipFile(QuaZip *zip, QObject *parent): QIODevice(parent), p(new QuaZipFilePrivate(this, zip)) { } QuaZipFile::~QuaZipFile() { if (isOpen()) close(); delete p; } QString QuaZipFile::getZipName() const { return p->zip==NULL ? QString() : p->zip->getZipName(); } QuaZip *QuaZipFile::getZip() const { return p->internal ? NULL : p->zip; } QString QuaZipFile::getActualFileName()const { p->setZipError(UNZ_OK); if (p->zip == NULL || (openMode() & WriteOnly)) return QString(); QString name=p->zip->getCurrentFileName(); if(name.isNull()) p->setZipError(p->zip->getZipError()); return name; } void QuaZipFile::setZipName(const QString& zipName) { if(isOpen()) { qWarning("QuaZipFile::setZipName(): file is already open - can not set ZIP name"); return; } if(p->zip!=NULL && p->internal) delete p->zip; p->zip=new QuaZip(zipName); p->internal=true; } void QuaZipFile::setZip(QuaZip *zip) { if(isOpen()) { qWarning("QuaZipFile::setZip(): file is already open - can not set ZIP"); return; } if(p->zip!=NULL && p->internal) delete p->zip; p->zip=zip; p->fileName=QString(); p->internal=false; } void QuaZipFile::setFileName(const QString& fileName, QuaZip::CaseSensitivity cs) { if(p->zip==NULL) { qWarning("QuaZipFile::setFileName(): call setZipName() first"); return; } if(!p->internal) { qWarning("QuaZipFile::setFileName(): should not be used when not using internal QuaZip"); return; } if(isOpen()) { qWarning("QuaZipFile::setFileName(): can not set file name for already opened file"); return; } p->fileName=fileName; if (p->fileName.startsWith('/')) p->fileName = p->fileName.mid(1); p->caseSensitivity=cs; } void QuaZipFilePrivate::setZipError(int zipError) const { QuaZipFilePrivate *fakeThis = const_cast(this); // non-const fakeThis->zipError=zipError; if(zipError==UNZ_OK) q->setErrorString(QString()); else q->setErrorString(QuaZipFile::tr("ZIP/UNZIP API error %1").arg(zipError)); } bool QuaZipFile::open(OpenMode mode) { return open(mode, NULL); } bool QuaZipFile::open(OpenMode mode, int *method, int *level, bool raw, const char *password) { p->resetZipError(); if(isOpen()) { qWarning("QuaZipFile::open(): already opened"); return false; } if(mode&Unbuffered) { qWarning("QuaZipFile::open(): Unbuffered mode is not supported"); return false; } if((mode&ReadOnly)&&!(mode&WriteOnly)) { if(p->internal) { if(!p->zip->open(QuaZip::mdUnzip)) { p->setZipError(p->zip->getZipError()); return false; } if(!p->zip->setCurrentFile(p->fileName, p->caseSensitivity)) { p->setZipError(p->zip->getZipError()); p->zip->close(); return false; } } else { if(p->zip==NULL) { qWarning("QuaZipFile::open(): zip is NULL"); return false; } if(p->zip->getMode()!=QuaZip::mdUnzip) { qWarning("QuaZipFile::open(): file open mode %d incompatible with ZIP open mode %d", (int)mode, (int)p->zip->getMode()); return false; } if(!p->zip->hasCurrentFile()) { qWarning("QuaZipFile::open(): zip does not have current file"); return false; } } p->setZipError(unzOpenCurrentFile3(p->zip->getUnzFile(), method, level, (int)raw, password)); if(p->zipError==UNZ_OK) { setOpenMode(mode); p->raw=raw; return true; } else return false; } qWarning("QuaZipFile::open(): open mode %d not supported by this function", (int)mode); return false; } bool QuaZipFile::open(OpenMode mode, const QuaZipNewInfo& info, const char *password, quint32 crc, int method, int level, bool raw, int windowBits, int memLevel, int strategy) { zip_fileinfo info_z; p->resetZipError(); if(isOpen()) { qWarning("QuaZipFile::open(): already opened"); return false; } if((mode&WriteOnly)&&!(mode&ReadOnly)) { if(p->internal) { qWarning("QuaZipFile::open(): write mode is incompatible with internal QuaZip approach"); return false; } if(p->zip==NULL) { qWarning("QuaZipFile::open(): zip is NULL"); return false; } if(p->zip->getMode()!=QuaZip::mdCreate&&p->zip->getMode()!=QuaZip::mdAppend&&p->zip->getMode()!=QuaZip::mdAdd) { qWarning("QuaZipFile::open(): file open mode %d incompatible with ZIP open mode %d", (int)mode, (int)p->zip->getMode()); return false; } info_z.tmz_date.tm_year=info.dateTime.date().year(); info_z.tmz_date.tm_mon=info.dateTime.date().month() - 1; info_z.tmz_date.tm_mday=info.dateTime.date().day(); info_z.tmz_date.tm_hour=info.dateTime.time().hour(); info_z.tmz_date.tm_min=info.dateTime.time().minute(); info_z.tmz_date.tm_sec=info.dateTime.time().second(); info_z.dosDate = 0; info_z.internal_fa=(uLong)info.internalAttr; info_z.external_fa=(uLong)info.externalAttr; if (!p->zip->isDataDescriptorWritingEnabled()) zipClearFlags(p->zip->getZipFile(), ZIP_WRITE_DATA_DESCRIPTOR); p->setZipError(zipOpenNewFileInZip3_64(p->zip->getZipFile(), p->zip->getFileNameCodec()->fromUnicode(info.name).constData(), &info_z, info.extraLocal.constData(), info.extraLocal.length(), info.extraGlobal.constData(), info.extraGlobal.length(), p->zip->getCommentCodec()->fromUnicode(info.comment).constData(), method, level, (int)raw, windowBits, memLevel, strategy, password, (uLong)crc, p->zip->isZip64Enabled())); if(p->zipError==UNZ_OK) { p->writePos=0; setOpenMode(mode); p->raw=raw; if(raw) { p->crc=crc; p->uncompressedSize=info.uncompressedSize; } return true; } else return false; } qWarning("QuaZipFile::open(): open mode %d not supported by this function", (int)mode); return false; } bool QuaZipFile::isSequential()const { return true; } qint64 QuaZipFile::pos()const { if(p->zip==NULL) { qWarning("QuaZipFile::pos(): call setZipName() or setZip() first"); return -1; } if(!isOpen()) { qWarning("QuaZipFile::pos(): file is not open"); return -1; } if(openMode()&ReadOnly) // QIODevice::pos() is broken for sequential devices, // but thankfully bytesAvailable() returns the number of // bytes buffered, so we know how far ahead we are. return unztell(p->zip->getUnzFile()) - QIODevice::bytesAvailable(); else return p->writePos; } bool QuaZipFile::atEnd()const { if(p->zip==NULL) { qWarning("QuaZipFile::atEnd(): call setZipName() or setZip() first"); return false; } if(!isOpen()) { qWarning("QuaZipFile::atEnd(): file is not open"); return false; } if(openMode()&ReadOnly) // the same problem as with pos() return QIODevice::bytesAvailable() == 0 && unzeof(p->zip->getUnzFile())==1; else return true; } qint64 QuaZipFile::size()const { if(!isOpen()) { qWarning("QuaZipFile::atEnd(): file is not open"); return -1; } if(openMode()&ReadOnly) return p->raw?csize():usize(); else return p->writePos; } qint64 QuaZipFile::csize()const { unz_file_info64 info_z; p->setZipError(UNZ_OK); if(p->zip==NULL||p->zip->getMode()!=QuaZip::mdUnzip) return -1; p->setZipError(unzGetCurrentFileInfo64(p->zip->getUnzFile(), &info_z, NULL, 0, NULL, 0, NULL, 0)); if(p->zipError!=UNZ_OK) return -1; return info_z.compressed_size; } qint64 QuaZipFile::usize()const { unz_file_info64 info_z; p->setZipError(UNZ_OK); if(p->zip==NULL||p->zip->getMode()!=QuaZip::mdUnzip) return -1; p->setZipError(unzGetCurrentFileInfo64(p->zip->getUnzFile(), &info_z, NULL, 0, NULL, 0, NULL, 0)); if(p->zipError!=UNZ_OK) return -1; return info_z.uncompressed_size; } bool QuaZipFile::getFileInfo(QuaZipFileInfo *info) { if(p->zip==NULL||p->zip->getMode()!=QuaZip::mdUnzip) return false; p->zip->getCurrentFileInfo(info); p->setZipError(p->zip->getZipError()); return p->zipError==UNZ_OK; } void QuaZipFile::close() { p->resetZipError(); if(p->zip==NULL||!p->zip->isOpen()) return; if(!isOpen()) { qWarning("QuaZipFile::close(): file isn't open"); return; } if(openMode()&ReadOnly) p->setZipError(unzCloseCurrentFile(p->zip->getUnzFile())); else if(openMode()&WriteOnly) if(isRaw()) p->setZipError(zipCloseFileInZipRaw64(p->zip->getZipFile(), p->uncompressedSize, p->crc)); else p->setZipError(zipCloseFileInZip(p->zip->getZipFile())); else { qWarning("Wrong open mode: %d", (int)openMode()); return; } if(p->zipError==UNZ_OK) setOpenMode(QIODevice::NotOpen); else return; if(p->internal) { p->zip->close(); p->setZipError(p->zip->getZipError()); } } qint64 QuaZipFile::readData(char *data, qint64 maxSize) { p->setZipError(UNZ_OK); qint64 bytesRead=unzReadCurrentFile(p->zip->getUnzFile(), data, (unsigned)maxSize); if (bytesRead < 0) { p->setZipError((int) bytesRead); return -1; } return bytesRead; } qint64 QuaZipFile::writeData(const char* data, qint64 maxSize) { p->setZipError(ZIP_OK); p->setZipError(zipWriteInFileInZip(p->zip->getZipFile(), data, (uint)maxSize)); if(p->zipError!=ZIP_OK) return -1; else { p->writePos+=maxSize; return maxSize; } } QString QuaZipFile::getFileName() const { return p->fileName; } QuaZip::CaseSensitivity QuaZipFile::getCaseSensitivity() const { return p->caseSensitivity; } bool QuaZipFile::isRaw() const { return p->raw; } int QuaZipFile::getZipError() const { return p->zipError; } qint64 QuaZipFile::bytesAvailable() const { return size() - pos(); } connectome-workbench-1.2.3+git41-gc4c6c90/src/Quazip/quazipfile.h000066400000000000000000000471061300200146000243470ustar00rootroot00000000000000#ifndef QUA_ZIPFILE_H #define QUA_ZIPFILE_H /* Copyright (C) 2005-2011 Sergey A. Tachenov This program is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser 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 Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA See COPYING file for the full LGPL text. Original ZIP package is copyrighted by Gilles Vollant, see quazip/(un)zip.h files for details, basically it's zlib license. **/ #include #include "quazip_global.h" #include "quazip.h" #include "quazipnewinfo.h" class QuaZipFilePrivate; /// A file inside ZIP archive. /** \class QuaZipFile quazipfile.h * This is the most interesting class. Not only it provides C++ * interface to the ZIP/UNZIP package, but also integrates it with Qt by * subclassing QIODevice. This makes possible to access files inside ZIP * archive using QTextStream or QDataStream, for example. Actually, this * is the main purpose of the whole QuaZIP library. * * You can either use existing QuaZip instance to create instance of * this class or pass ZIP archive file name to this class, in which case * it will create internal QuaZip object. See constructors' descriptions * for details. Writing is only possible with the existing instance. * * Note that due to the underlying library's limitation it is not * possible to use multiple QuaZipFile instances to open several files * in the same archive at the same time. If you need to write to * multiple files in parallel, then you should write to temporary files * first, then pack them all at once when you have finished writing. If * you need to read multiple files inside the same archive in parallel, * you should extract them all into a temporary directory first. * * \section quazipfile-sequential Sequential or random-access? * * At the first thought, QuaZipFile has fixed size, the start and the * end and should be therefore considered random-access device. But * there is one major obstacle to making it random-access: ZIP/UNZIP API * does not support seek() operation and the only way to implement it is * through reopening the file and re-reading to the required position, * but this is prohibitively slow. * * Therefore, QuaZipFile is considered to be a sequential device. This * has advantage of availability of the ungetChar() operation (QIODevice * does not implement it properly for non-sequential devices unless they * support seek()). Disadvantage is a somewhat strange behaviour of the * size() and pos() functions. This should be kept in mind while using * this class. * **/ class QUAZIP_EXPORT QuaZipFile: public QIODevice { friend class QuaZipFilePrivate; Q_OBJECT private: QuaZipFilePrivate *p; // these are not supported nor implemented QuaZipFile(const QuaZipFile& that); QuaZipFile& operator=(const QuaZipFile& that); protected: /// Implementation of the QIODevice::readData(). qint64 readData(char *data, qint64 maxSize); /// Implementation of the QIODevice::writeData(). qint64 writeData(const char *data, qint64 maxSize); public: /// Constructs a QuaZipFile instance. /** You should use setZipName() and setFileName() or setZip() before * trying to call open() on the constructed object. **/ QuaZipFile(); /// Constructs a QuaZipFile instance. /** \a parent argument specifies this object's parent object. * * You should use setZipName() and setFileName() or setZip() before * trying to call open() on the constructed object. **/ QuaZipFile(QObject *parent); /// Constructs a QuaZipFile instance. /** \a parent argument specifies this object's parent object and \a * zipName specifies ZIP archive file name. * * You should use setFileName() before trying to call open() on the * constructed object. * * QuaZipFile constructed by this constructor can be used for read * only access. Use QuaZipFile(QuaZip*,QObject*) for writing. **/ QuaZipFile(const QString& zipName, QObject *parent =NULL); /// Constructs a QuaZipFile instance. /** \a parent argument specifies this object's parent object, \a * zipName specifies ZIP archive file name and \a fileName and \a cs * specify a name of the file to open inside archive. * * QuaZipFile constructed by this constructor can be used for read * only access. Use QuaZipFile(QuaZip*,QObject*) for writing. * * \sa QuaZip::setCurrentFile() **/ QuaZipFile(const QString& zipName, const QString& fileName, QuaZip::CaseSensitivity cs =QuaZip::csDefault, QObject *parent =NULL); /// Constructs a QuaZipFile instance. /** \a parent argument specifies this object's parent object. * * \a zip is the pointer to the existing QuaZip object. This * QuaZipFile object then can be used to read current file in the * \a zip or to write to the file inside it. * * \warning Using this constructor for reading current file can be * tricky. Let's take the following example: * \code * QuaZip zip("archive.zip"); * zip.open(QuaZip::mdUnzip); * zip.setCurrentFile("file-in-archive"); * QuaZipFile file(&zip); * file.open(QIODevice::ReadOnly); * // ok, now we can read from the file * file.read(somewhere, some); * zip.setCurrentFile("another-file-in-archive"); // oops... * QuaZipFile anotherFile(&zip); * anotherFile.open(QIODevice::ReadOnly); * anotherFile.read(somewhere, some); // this is still ok... * file.read(somewhere, some); // and this is NOT * \endcode * So, what exactly happens here? When we change current file in the * \c zip archive, \c file that references it becomes invalid * (actually, as far as I understand ZIP/UNZIP sources, it becomes * closed, but QuaZipFile has no means to detect it). * * Summary: do not close \c zip object or change its current file as * long as QuaZipFile is open. Even better - use another constructors * which create internal QuaZip instances, one per object, and * therefore do not cause unnecessary trouble. This constructor may * be useful, though, if you already have a QuaZip instance and do * not want to access several files at once. Good example: * \code * QuaZip zip("archive.zip"); * zip.open(QuaZip::mdUnzip); * // first, we need some information about archive itself * QByteArray comment=zip.getComment(); * // and now we are going to access files inside it * QuaZipFile file(&zip); * for(bool more=zip.goToFirstFile(); more; more=zip.goToNextFile()) { * file.open(QIODevice::ReadOnly); * // do something cool with file here * file.close(); // do not forget to close! * } * zip.close(); * \endcode **/ QuaZipFile(QuaZip *zip, QObject *parent =NULL); /// Destroys a QuaZipFile instance. /** Closes file if open, destructs internal QuaZip object (if it * exists and \em is internal, of course). **/ virtual ~QuaZipFile(); /// Returns the ZIP archive file name. /** If this object was created by passing QuaZip pointer to the * constructor, this function will return that QuaZip's file name * (or null string if that object does not have file name yet). * * Otherwise, returns associated ZIP archive file name or null * string if there are no name set yet. * * \sa setZipName() getFileName() **/ QString getZipName()const; /// Returns a pointer to the associated QuaZip object. /** Returns \c NULL if there is no associated QuaZip or it is * internal (so you will not mess with it). **/ QuaZip* getZip()const; /// Returns file name. /** This function returns file name you passed to this object either * by using * QuaZipFile(const QString&,const QString&,QuaZip::CaseSensitivity,QObject*) * or by calling setFileName(). Real name of the file may differ in * case if you used case-insensitivity. * * Returns null string if there is no file name set yet. This is the * case when this QuaZipFile operates on the existing QuaZip object * (constructor QuaZipFile(QuaZip*,QObject*) or setZip() was used). * * \sa getActualFileName **/ QString getFileName() const; /// Returns case sensitivity of the file name. /** This function returns case sensitivity argument you passed to * this object either by using * QuaZipFile(const QString&,const QString&,QuaZip::CaseSensitivity,QObject*) * or by calling setFileName(). * * Returns unpredictable value if getFileName() returns null string * (this is the case when you did not used setFileName() or * constructor above). * * \sa getFileName **/ QuaZip::CaseSensitivity getCaseSensitivity() const; /// Returns the actual file name in the archive. /** This is \em not a ZIP archive file name, but a name of file inside * archive. It is not necessary the same name that you have passed * to the * QuaZipFile(const QString&,const QString&,QuaZip::CaseSensitivity,QObject*), * setFileName() or QuaZip::setCurrentFile() - this is the real file * name inside archive, so it may differ in case if the file name * search was case-insensitive. * * Equivalent to calling getCurrentFileName() on the associated * QuaZip object. Returns null string if there is no associated * QuaZip object or if it does not have a current file yet. And this * is the case if you called setFileName() but did not open the * file yet. So this is perfectly fine: * \code * QuaZipFile file("somezip.zip"); * file.setFileName("somefile"); * QString name=file.getName(); // name=="somefile" * QString actual=file.getActualFileName(); // actual is null string * file.open(QIODevice::ReadOnly); * QString actual=file.getActualFileName(); // actual can be "SoMeFiLe" on Windows * \endcode * * \sa getZipName(), getFileName(), QuaZip::CaseSensitivity **/ QString getActualFileName()const; /// Sets the ZIP archive file name. /** Automatically creates internal QuaZip object and destroys * previously created internal QuaZip object, if any. * * Will do nothing if this file is already open. You must close() it * first. **/ void setZipName(const QString& zipName); /// Returns \c true if the file was opened in raw mode. /** If the file is not open, the returned value is undefined. * * \sa open(OpenMode,int*,int*,bool,const char*) **/ bool isRaw() const; /// Binds to the existing QuaZip instance. /** This function destroys internal QuaZip object, if any, and makes * this QuaZipFile to use current file in the \a zip object for any * further operations. See QuaZipFile(QuaZip*,QObject*) for the * possible pitfalls. * * Will do nothing if the file is currently open. You must close() * it first. **/ void setZip(QuaZip *zip); /// Sets the file name. /** Will do nothing if at least one of the following conditions is * met: * - ZIP name has not been set yet (getZipName() returns null * string). * - This QuaZipFile is associated with external QuaZip. In this * case you should call that QuaZip's setCurrentFile() function * instead! * - File is already open so setting the name is meaningless. * * \sa QuaZip::setCurrentFile **/ void setFileName(const QString& fileName, QuaZip::CaseSensitivity cs =QuaZip::csDefault); /// Opens a file for reading. /** Returns \c true on success, \c false otherwise. * Call getZipError() to get error code. * * \note Since ZIP/UNZIP API provides buffered reading only, * QuaZipFile does not support unbuffered reading. So do not pass * QIODevice::Unbuffered flag in \a mode, or open will fail. **/ virtual bool open(OpenMode mode); /// Opens a file for reading. /** \overload * Argument \a password specifies a password to decrypt the file. If * it is NULL then this function behaves just like open(OpenMode). **/ inline bool open(OpenMode mode, const char *password) {return open(mode, NULL, NULL, false, password);} /// Opens a file for reading. /** \overload * Argument \a password specifies a password to decrypt the file. * * An integers pointed by \a method and \a level will receive codes * of the compression method and level used. See unzip.h. * * If raw is \c true then no decompression is performed. * * \a method should not be \c NULL. \a level can be \c NULL if you * don't want to know the compression level. **/ bool open(OpenMode mode, int *method, int *level, bool raw, const char *password =NULL); /// Opens a file for writing. /** \a info argument specifies information about file. It should at * least specify a correct file name. Also, it is a good idea to * specify correct timestamp (by default, current time will be * used). See QuaZipNewInfo. * * The \a password argument specifies the password for crypting. Pass NULL * if you don't need any crypting. The \a crc argument was supposed * to be used for crypting too, but then it turned out that it's * false information, so you need to set it to 0 unless you want to * use the raw mode (see below). * * Arguments \a method and \a level specify compression method and * level. The only method supported is Z_DEFLATED, but you may also * specify 0 for no compression. If all of the files in the archive * use both method 0 and either level 0 is explicitly specified or * data descriptor writing is disabled with * QuaZip::setDataDescriptorWritingEnabled(), then the * resulting archive is supposed to be compatible with the 1.0 ZIP * format version, should you need that. Except for this, \a level * has no other effects with method 0. * * If \a raw is \c true, no compression is performed. In this case, * \a crc and uncompressedSize field of the \a info are required. * * Arguments \a windowBits, \a memLevel, \a strategy provide zlib * algorithms tuning. See deflateInit2() in zlib. **/ bool open(OpenMode mode, const QuaZipNewInfo& info, const char *password =NULL, quint32 crc =0, int method =Z_DEFLATED, int level =Z_DEFAULT_COMPRESSION, bool raw =false, int windowBits =-MAX_WBITS, int memLevel =DEF_MEM_LEVEL, int strategy =Z_DEFAULT_STRATEGY); /// Returns \c true, but \ref quazipfile-sequential "beware"! virtual bool isSequential()const; /// Returns current position in the file. /** Implementation of the QIODevice::pos(). When reading, this * function is a wrapper to the ZIP/UNZIP unztell(), therefore it is * unable to keep track of the ungetChar() calls (which is * non-virtual and therefore is dangerous to reimplement). So if you * are using ungetChar() feature of the QIODevice, this function * reports incorrect value until you get back characters which you * ungot. * * When writing, pos() returns number of bytes already written * (uncompressed unless you use raw mode). * * \note Although * \ref quazipfile-sequential "QuaZipFile is a sequential device" * and therefore pos() should always return zero, it does not, * because it would be misguiding. Keep this in mind. * * This function returns -1 if the file or archive is not open. * * Error code returned by getZipError() is not affected by this * function call. **/ virtual qint64 pos()const; /// Returns \c true if the end of file was reached. /** This function returns \c false in the case of error. This means * that you called this function on either not open file, or a file * in the not open archive or even on a QuaZipFile instance that * does not even have QuaZip instance associated. Do not do that * because there is no means to determine whether \c false is * returned because of error or because end of file was reached. * Well, on the other side you may interpret \c false return value * as "there is no file open to check for end of file and there is * no end of file therefore". * * When writing, this function always returns \c true (because you * are always writing to the end of file). * * Error code returned by getZipError() is not affected by this * function call. **/ virtual bool atEnd()const; /// Returns file size. /** This function returns csize() if the file is open for reading in * raw mode, usize() if it is open for reading in normal mode and * pos() if it is open for writing. * * Returns -1 on error, call getZipError() to get error code. * * \note This function returns file size despite that * \ref quazipfile-sequential "QuaZipFile is considered to be sequential device", * for which size() should return bytesAvailable() instead. But its * name would be very misguiding otherwise, so just keep in mind * this inconsistence. **/ virtual qint64 size()const; /// Returns compressed file size. /** Equivalent to calling getFileInfo() and then getting * compressedSize field, but more convenient and faster. * * File must be open for reading before calling this function. * * Returns -1 on error, call getZipError() to get error code. **/ qint64 csize()const; /// Returns uncompressed file size. /** Equivalent to calling getFileInfo() and then getting * uncompressedSize field, but more convenient and faster. See * getFileInfo() for a warning. * * File must be open for reading before calling this function. * * Returns -1 on error, call getZipError() to get error code. **/ qint64 usize()const; /// Gets information about current file. /** This function does the same thing as calling * QuaZip::getCurrentFileInfo() on the associated QuaZip object, * but you can not call getCurrentFileInfo() if the associated * QuaZip is internal (because you do not have access to it), while * you still can call this function in that case. * * File must be open for reading before calling this function. * * Returns \c false in the case of an error. **/ bool getFileInfo(QuaZipFileInfo *info); /// Closes the file. /** Call getZipError() to determine if the close was successful. **/ virtual void close(); /// Returns the error code returned by the last ZIP/UNZIP API call. int getZipError() const; /// Returns the number of bytes available for reading. virtual qint64 bytesAvailable() const; }; #endif connectome-workbench-1.2.3+git41-gc4c6c90/src/Quazip/quazipfileinfo.cpp000066400000000000000000000017731300200146000255560ustar00rootroot00000000000000#include "quazipfileinfo.h" static QFile::Permissions permissionsFromExternalAttr(quint32 externalAttr) { quint32 uPerm = (externalAttr & 0xFFFF0000u) >> 16; QFile::Permissions perm = 0; if ((uPerm & 0400) != 0) perm |= QFile::ReadOwner; if ((uPerm & 0200) != 0) perm |= QFile::WriteOwner; if ((uPerm & 0100) != 0) perm |= QFile::ExeOwner; if ((uPerm & 0040) != 0) perm |= QFile::ReadGroup; if ((uPerm & 0020) != 0) perm |= QFile::WriteGroup; if ((uPerm & 0010) != 0) perm |= QFile::ExeGroup; if ((uPerm & 0004) != 0) perm |= QFile::ReadOther; if ((uPerm & 0002) != 0) perm |= QFile::WriteOther; if ((uPerm & 0001) != 0) perm |= QFile::ExeOther; return perm; } QFile::Permissions QuaZipFileInfo::getPermissions() const { return permissionsFromExternalAttr(externalAttr); } QFile::Permissions QuaZipFileInfo64::getPermissions() const { return permissionsFromExternalAttr(externalAttr); } connectome-workbench-1.2.3+git41-gc4c6c90/src/Quazip/quazipfileinfo.h000066400000000000000000000062771300200146000252270ustar00rootroot00000000000000#ifndef QUA_ZIPFILEINFO_H #define QUA_ZIPFILEINFO_H /* Copyright (C) 2005-2011 Sergey A. Tachenov This program is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser 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 Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA See COPYING file for the full LGPL text. Original ZIP package is copyrighted by Gilles Vollant, see quazip/(un)zip.h files for details, basically it's zlib license. **/ #include #include #include #include "quazip_global.h" /// Information about a file inside archive. /** Call QuaZip::getCurrentFileInfo() or QuaZipFile::getFileInfo() to * fill this structure. */ struct QUAZIP_EXPORT QuaZipFileInfo { /// File name. QString name; /// Version created by. quint16 versionCreated; /// Version needed to extract. quint16 versionNeeded; /// General purpose flags. quint16 flags; /// Compression method. quint16 method; /// Last modification date and time. QDateTime dateTime; /// CRC. quint32 crc; /// Compressed file size. quint32 compressedSize; /// Uncompressed file size. quint32 uncompressedSize; /// Disk number start. quint16 diskNumberStart; /// Internal file attributes. quint16 internalAttr; /// External file attributes. quint32 externalAttr; /// Comment. QString comment; /// Extra field. QByteArray extra; /// Get the file permissions. /** Returns the high 16 bits of external attributes converted to QFile::Permissions. */ QFile::Permissions getPermissions() const; }; /// Information about a file inside archive (with zip64 support). /** Call QuaZip::getCurrentFileInfo() or QuaZipFile::getFileInfo() to * fill this structure. */ struct QUAZIP_EXPORT QuaZipFileInfo64 { /// File name. QString name; /// Version created by. quint16 versionCreated; /// Version needed to extract. quint16 versionNeeded; /// General purpose flags. quint16 flags; /// Compression method. quint16 method; /// Last modification date and time. QDateTime dateTime; /// CRC. quint32 crc; /// Compressed file size. quint64 compressedSize; /// Uncompressed file size. quint64 uncompressedSize; /// Disk number start. quint16 diskNumberStart; /// Internal file attributes. quint16 internalAttr; /// External file attributes. quint32 externalAttr; /// Comment. QString comment; /// Extra field. QByteArray extra; /// Get the file permissions. /** Returns the high 16 bits of external attributes converted to QFile::Permissions. */ QFile::Permissions getPermissions() const; }; #endif connectome-workbench-1.2.3+git41-gc4c6c90/src/Quazip/quazipnewinfo.cpp000066400000000000000000000055021300200146000254220ustar00rootroot00000000000000/* Copyright (C) 2005-2011 Sergey A. Tachenov This program is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser 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 Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA See COPYING file for the full LGPL text. Original ZIP package is copyrighted by Gilles Vollant, see quazip/(un)zip.h files for details, basically it's zlib license. */ #include #include "quazipnewinfo.h" static void QuaZipNewInfo_setPermissions(QuaZipNewInfo *info, QFile::Permissions perm, bool isDir) { quint32 uPerm = isDir ? 0040000 : 0100000; if ((perm & QFile::ReadOwner) != 0) uPerm |= 0400; if ((perm & QFile::WriteOwner) != 0) uPerm |= 0200; if ((perm & QFile::ExeOwner) != 0) uPerm |= 0100; if ((perm & QFile::ReadGroup) != 0) uPerm |= 0040; if ((perm & QFile::WriteGroup) != 0) uPerm |= 0020; if ((perm & QFile::ExeGroup) != 0) uPerm |= 0010; if ((perm & QFile::ReadOther) != 0) uPerm |= 0004; if ((perm & QFile::WriteOther) != 0) uPerm |= 0002; if ((perm & QFile::ExeOther) != 0) uPerm |= 0001; info->externalAttr = (info->externalAttr & ~0xFFFF0000u) | (uPerm << 16); } QuaZipNewInfo::QuaZipNewInfo(const QString& name): name(name), dateTime(QDateTime::currentDateTime()), internalAttr(0), externalAttr(0) { } QuaZipNewInfo::QuaZipNewInfo(const QString& name, const QString& file): name(name), internalAttr(0), externalAttr(0) { QFileInfo info(file); QDateTime lm = info.lastModified(); if (!info.exists()) { dateTime = QDateTime::currentDateTime(); } else { dateTime = lm; QuaZipNewInfo_setPermissions(this, info.permissions(), info.isDir()); } } void QuaZipNewInfo::setFileDateTime(const QString& file) { QFileInfo info(file); QDateTime lm = info.lastModified(); if (info.exists()) dateTime = lm; } void QuaZipNewInfo::setFilePermissions(const QString &file) { QFileInfo info = QFileInfo(file); QFile::Permissions perm = info.permissions(); QuaZipNewInfo_setPermissions(this, perm, info.isDir()); } void QuaZipNewInfo::setPermissions(QFile::Permissions permissions) { QuaZipNewInfo_setPermissions(this, permissions, name.endsWith('/')); } connectome-workbench-1.2.3+git41-gc4c6c90/src/Quazip/quazipnewinfo.h000066400000000000000000000117151300200146000250720ustar00rootroot00000000000000#ifndef QUA_ZIPNEWINFO_H #define QUA_ZIPNEWINFO_H /* Copyright (C) 2005-2011 Sergey A. Tachenov This program is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser 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 Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA See COPYING file for the full LGPL text. Original ZIP package is copyrighted by Gilles Vollant, see quazip/(un)zip.h files for details, basically it's zlib license. **/ #include #include #include #include "quazip_global.h" /// Information about a file to be created. /** This structure holds information about a file to be created inside * ZIP archive. At least name should be set to something correct before * passing this structure to * QuaZipFile::open(OpenMode,const QuaZipNewInfo&,int,int,bool). * * Zip64 support of this structure is slightly limited: in the raw mode (when * a pre-compressed file is written into a ZIP file as-is), it is necessary * to specify the uncompressed file size and the appropriate field is 32 bit. * Since the raw mode is used extremely rare, there is no real need to have * a separate QuaZipNewInfo64 structure like QuaZipFileInfo64. It may be added * in the future though, if there is a demand for the raw mode with zip64 * archives. **/ struct QUAZIP_EXPORT QuaZipNewInfo { /// File name. /** This field holds file name inside archive, including path relative * to archive root. **/ QString name; /// File timestamp. /** This is the last file modification date and time. Will be stored * in the archive central directory. It is a good practice to set it * to the source file timestamp instead of archive creating time. Use * setFileDateTime() or QuaZipNewInfo(const QString&, const QString&). **/ QDateTime dateTime; /// File internal attributes. quint16 internalAttr; /// File external attributes. /** The highest 16 bits contain Unix file permissions and type (dir or file). The constructor QuaZipNewInfo(const QString&, const QString&) takes permissions from the provided file. */ quint32 externalAttr; /// File comment. /** Will be encoded using QuaZip::getCommentCodec(). **/ QString comment; /// File local extra field. QByteArray extraLocal; /// File global extra field. QByteArray extraGlobal; /// Uncompressed file size. /** This is only needed if you are using raw file zipping mode, i. e. * adding precompressed file in the zip archive. **/ ulong uncompressedSize; /// Constructs QuaZipNewInfo instance. /** Initializes name with \a name, dateTime with current date and * time. Attributes are initialized with zeros, comment and extra * field with null values. **/ QuaZipNewInfo(const QString& name); /// Constructs QuaZipNewInfo instance. /** Initializes name with \a name. Timestamp and permissions are taken * from the specified file. If the \a file does not exists or its timestamp * is inaccessible (e. g. you do not have read permission for the * directory file in), uses current time and zero permissions. Other attributes are * initialized with zeros, comment and extra field with null values. * * \sa setFileDateTime() **/ QuaZipNewInfo(const QString& name, const QString& file); /// Sets the file timestamp from the existing file. /** Use this function to set the file timestamp from the existing * file. Use it like this: * \code * QuaZipFile zipFile(&zip); * QFile file("file-to-add"); * file.open(QIODevice::ReadOnly); * QuaZipNewInfo info("file-name-in-archive"); * info.setFileDateTime("file-to-add"); // take the timestamp from file * zipFile.open(QIODevice::WriteOnly, info); * \endcode * * This function does not change dateTime if some error occured (e. g. * file is inaccessible). **/ void setFileDateTime(const QString& file); /// Sets the file permissions from the existing file. /** Takes permissions from the file and sets the high 16 bits of external attributes. Uses QFileInfo to get permissions on all platforms. */ void setFilePermissions(const QString &file); /// Sets the file permissions. /** Modifies the highest 16 bits of external attributes. The type part is set to dir if the name ends with a slash, and to regular file otherwise. */ void setPermissions(QFile::Permissions permissions); }; #endif connectome-workbench-1.2.3+git41-gc4c6c90/src/Quazip/unzip.c000066400000000000000000002171151300200146000233350ustar00rootroot00000000000000/* unzip.c -- IO for uncompress .zip files using zlib Version 1.1, February 14h, 2010 part of the MiniZip project - ( http://www.winimage.com/zLibDll/minizip.html ) Copyright (C) 1998-2010 Gilles Vollant (minizip) ( http://www.winimage.com/zLibDll/minizip.html ) Modifications of Unzip for Zip64 Copyright (C) 2007-2008 Even Rouault Modifications for Zip64 support on both zip and unzip Copyright (C) 2009-2010 Mathias Svensson ( http://result42.com ) For more info read MiniZip_info.txt ------------------------------------------------------------------------------------ Decryption code comes from crypt.c by Info-ZIP but has been greatly reduced in terms of compatibility with older software. The following is from the original crypt.c. Code woven in by Terry Thorsen 1/2003. Copyright (c) 1990-2000 Info-ZIP. All rights reserved. See the accompanying file LICENSE, version 2000-Apr-09 or later (the contents of which are also included in zip.h) for terms of use. If, for some reason, all these files are missing, the Info-ZIP license also may be found at: ftp://ftp.info-zip.org/pub/infozip/license.html crypt.c (full version) by Info-ZIP. Last revised: [see crypt.h] The encryption/decryption parts of this source code (as opposed to the non-echoing password parts) were originally written in Europe. The whole source package can be freely distributed, including from the USA. (Prior to January 2000, re-export from the US was a violation of US law.) This encryption code is a direct transcription of the algorithm from Roger Schlafly, described by Phil Katz in the file appnote.txt. This file (appnote.txt) is distributed with the PKZIP program (even in the version without encryption capabilities). ------------------------------------------------------------------------------------ Changes in unzip.c 2007-2008 - Even Rouault - Addition of cpl_unzGetCurrentFileZStreamPos 2007-2008 - Even Rouault - Decoration of symbol names unz* -> cpl_unz* 2007-2008 - Even Rouault - Remove old C style function prototypes 2007-2008 - Even Rouault - Add unzip support for ZIP64 Copyright (C) 2007-2008 Even Rouault Oct-2009 - Mathias Svensson - Removed cpl_* from symbol names (Even Rouault added them but since this is now moved to a new project (minizip64) I renamed them again). Oct-2009 - Mathias Svensson - Fixed problem if uncompressed size was > 4G and compressed size was <4G should only read the compressed/uncompressed size from the Zip64 format if the size from normal header was 0xFFFFFFFF Oct-2009 - Mathias Svensson - Applied some bug fixes from paches recived from Gilles Vollant Oct-2009 - Mathias Svensson - Applied support to unzip files with compression mathod BZIP2 (bzip2 lib is required) Patch created by Daniel Borca Jan-2010 - back to unzip and minizip 1.0 name scheme, with compatibility layer Copyright (C) 1998 - 2010 Gilles Vollant, Even Rouault, Mathias Svensson */ #include #include #include #include "zlib.h" #if (ZLIB_VERNUM < 0x1270) typedef uLongf z_crc_t; #endif #include "unzip.h" #ifdef STDC # include # include # include #endif #ifdef NO_ERRNO_H extern int errno; #else # include #endif #ifndef local # define local static #endif /* compile with -Dlocal if your debugger can't find static symbols */ #ifndef CASESENSITIVITYDEFAULT_NO # if !defined(unix) && !defined(CASESENSITIVITYDEFAULT_YES) # define CASESENSITIVITYDEFAULT_NO # endif #endif #ifndef UNZ_BUFSIZE #define UNZ_BUFSIZE (16384) #endif #ifndef UNZ_MAXFILENAMEINZIP #define UNZ_MAXFILENAMEINZIP (256) #endif #ifndef ALLOC # define ALLOC(size) (malloc(size)) #endif #ifndef TRYFREE # define TRYFREE(p) {if (p) free(p);} #endif #define SIZECENTRALDIRITEM (0x2e) #define SIZEZIPLOCALHEADER (0x1e) const char unz_copyright[] = " unzip 1.01 Copyright 1998-2004 Gilles Vollant - http://www.winimage.com/zLibDll"; /* unz_file_info_interntal contain internal info about a file in zipfile*/ typedef struct unz_file_info64_internal_s { ZPOS64_T offset_curfile;/* relative offset of local header 8 bytes */ } unz_file_info64_internal; /* file_in_zip_read_info_s contain internal information about a file in zipfile, when reading and decompress it */ typedef struct { char *read_buffer; /* internal buffer for compressed data */ z_stream stream; /* zLib stream structure for inflate */ #ifdef HAVE_BZIP2 bz_stream bstream; /* bzLib stream structure for bziped */ #endif ZPOS64_T pos_in_zipfile; /* position in byte on the zipfile, for fseek*/ uLong stream_initialised; /* flag set if stream structure is initialised*/ ZPOS64_T offset_local_extrafield;/* offset of the local extra field */ uInt size_local_extrafield;/* size of the local extra field */ ZPOS64_T pos_local_extrafield; /* position in the local extra field in read*/ ZPOS64_T total_out_64; uLong crc32; /* crc32 of all data uncompressed */ uLong crc32_wait; /* crc32 we must obtain after decompress all */ ZPOS64_T rest_read_compressed; /* number of byte to be decompressed */ ZPOS64_T rest_read_uncompressed;/*number of byte to be obtained after decomp*/ zlib_filefunc64_32_def z_filefunc; voidpf filestream; /* io structore of the zipfile */ uLong compression_method; /* compression method (0==store) */ ZPOS64_T byte_before_the_zipfile;/* byte before the zipfile, (>0 for sfx)*/ int raw; } file_in_zip64_read_info_s; /* unz64_s contain internal information about the zipfile */ typedef struct { zlib_filefunc64_32_def z_filefunc; int is64bitOpenFunction; voidpf filestream; /* io structore of the zipfile */ unz_global_info64 gi; /* public global information */ ZPOS64_T byte_before_the_zipfile;/* byte before the zipfile, (>0 for sfx)*/ ZPOS64_T num_file; /* number of the current file in the zipfile*/ ZPOS64_T pos_in_central_dir; /* pos of the current file in the central dir*/ ZPOS64_T current_file_ok; /* flag about the usability of the current file*/ ZPOS64_T central_pos; /* position of the beginning of the central dir*/ ZPOS64_T size_central_dir; /* size of the central directory */ ZPOS64_T offset_central_dir; /* offset of start of central directory with respect to the starting disk number */ unz_file_info64 cur_file_info; /* public info about the current file in zip*/ unz_file_info64_internal cur_file_info_internal; /* private info about it*/ file_in_zip64_read_info_s* pfile_in_zip_read; /* structure about the current file if we are decompressing it */ int encrypted; int isZip64; # ifndef NOUNCRYPT unsigned long keys[3]; /* keys defining the pseudo-random sequence */ const z_crc_t FAR * pcrc_32_tab; # endif } unz64_s; #ifndef NOUNCRYPT #include "crypt.h" #endif /* =========================================================================== Read a byte from a gz_stream; update next_in and avail_in. Return EOF for end of file. IN assertion: the stream s has been sucessfully opened for reading. */ local int unz64local_getByte OF(( const zlib_filefunc64_32_def* pzlib_filefunc_def, voidpf filestream, int *pi)); local int unz64local_getByte(const zlib_filefunc64_32_def* pzlib_filefunc_def, voidpf filestream, int *pi) { unsigned char c; int err = (int)ZREAD64(*pzlib_filefunc_def,filestream,&c,1); if (err==1) { *pi = (int)c; return UNZ_OK; } else { if (ZERROR64(*pzlib_filefunc_def,filestream)) return UNZ_ERRNO; else return UNZ_EOF; } } /* =========================================================================== Reads a long in LSB order from the given gz_stream. Sets */ local int unz64local_getShort OF(( const zlib_filefunc64_32_def* pzlib_filefunc_def, voidpf filestream, uLong *pX)); local int unz64local_getShort (const zlib_filefunc64_32_def* pzlib_filefunc_def, voidpf filestream, uLong *pX) { uLong x ; int i = 0; int err; err = unz64local_getByte(pzlib_filefunc_def,filestream,&i); x = (uLong)i; if (err==UNZ_OK) err = unz64local_getByte(pzlib_filefunc_def,filestream,&i); x |= ((uLong)i)<<8; if (err==UNZ_OK) *pX = x; else *pX = 0; return err; } local int unz64local_getLong OF(( const zlib_filefunc64_32_def* pzlib_filefunc_def, voidpf filestream, uLong *pX)); local int unz64local_getLong (const zlib_filefunc64_32_def* pzlib_filefunc_def, voidpf filestream, uLong *pX) { uLong x ; int i = 0; int err; err = unz64local_getByte(pzlib_filefunc_def,filestream,&i); x = (uLong)i; if (err==UNZ_OK) err = unz64local_getByte(pzlib_filefunc_def,filestream,&i); x |= ((uLong)i)<<8; if (err==UNZ_OK) err = unz64local_getByte(pzlib_filefunc_def,filestream,&i); x |= ((uLong)i)<<16; if (err==UNZ_OK) err = unz64local_getByte(pzlib_filefunc_def,filestream,&i); x += ((uLong)i)<<24; if (err==UNZ_OK) *pX = x; else *pX = 0; return err; } local int unz64local_getLong64 OF(( const zlib_filefunc64_32_def* pzlib_filefunc_def, voidpf filestream, ZPOS64_T *pX)); local int unz64local_getLong64 (const zlib_filefunc64_32_def* pzlib_filefunc_def, voidpf filestream, ZPOS64_T *pX) { ZPOS64_T x ; int i = 0; int err; err = unz64local_getByte(pzlib_filefunc_def,filestream,&i); x = (ZPOS64_T)i; if (err==UNZ_OK) err = unz64local_getByte(pzlib_filefunc_def,filestream,&i); x |= ((ZPOS64_T)i)<<8; if (err==UNZ_OK) err = unz64local_getByte(pzlib_filefunc_def,filestream,&i); x |= ((ZPOS64_T)i)<<16; if (err==UNZ_OK) err = unz64local_getByte(pzlib_filefunc_def,filestream,&i); x |= ((ZPOS64_T)i)<<24; if (err==UNZ_OK) err = unz64local_getByte(pzlib_filefunc_def,filestream,&i); x |= ((ZPOS64_T)i)<<32; if (err==UNZ_OK) err = unz64local_getByte(pzlib_filefunc_def,filestream,&i); x |= ((ZPOS64_T)i)<<40; if (err==UNZ_OK) err = unz64local_getByte(pzlib_filefunc_def,filestream,&i); x |= ((ZPOS64_T)i)<<48; if (err==UNZ_OK) err = unz64local_getByte(pzlib_filefunc_def,filestream,&i); x |= ((ZPOS64_T)i)<<56; if (err==UNZ_OK) *pX = x; else *pX = 0; return err; } /* My own strcmpi / strcasecmp */ local int strcmpcasenosensitive_internal (const char* fileName1, const char* fileName2) { for (;;) { char c1=*(fileName1++); char c2=*(fileName2++); if ((c1>='a') && (c1<='z')) c1 -= 0x20; if ((c2>='a') && (c2<='z')) c2 -= 0x20; if (c1=='\0') return ((c2=='\0') ? 0 : -1); if (c2=='\0') return 1; if (c1c2) return 1; } } #ifdef CASESENSITIVITYDEFAULT_NO #define CASESENSITIVITYDEFAULTVALUE 2 #else #define CASESENSITIVITYDEFAULTVALUE 1 #endif #ifndef STRCMPCASENOSENTIVEFUNCTION #define STRCMPCASENOSENTIVEFUNCTION strcmpcasenosensitive_internal #endif /* Compare two filename (fileName1,fileName2). If iCaseSenisivity = 1, comparision is case sensitivity (like strcmp) If iCaseSenisivity = 2, comparision is not case sensitivity (like strcmpi or strcasecmp) If iCaseSenisivity = 0, case sensitivity is defaut of your operating system (like 1 on Unix, 2 on Windows) */ extern int ZEXPORT unzStringFileNameCompare (const char* fileName1, const char* fileName2, int iCaseSensitivity) { if (iCaseSensitivity==0) iCaseSensitivity=CASESENSITIVITYDEFAULTVALUE; if (iCaseSensitivity==1) return strcmp(fileName1,fileName2); return STRCMPCASENOSENTIVEFUNCTION(fileName1,fileName2); } #ifndef BUFREADCOMMENT #define BUFREADCOMMENT (0x400) #endif /* Locate the Central directory of a zipfile (at the end, just before the global comment) */ local ZPOS64_T unz64local_SearchCentralDir OF((const zlib_filefunc64_32_def* pzlib_filefunc_def, voidpf filestream)); local ZPOS64_T unz64local_SearchCentralDir(const zlib_filefunc64_32_def* pzlib_filefunc_def, voidpf filestream) { unsigned char* buf; ZPOS64_T uSizeFile; ZPOS64_T uBackRead; ZPOS64_T uMaxBack=0xffff; /* maximum size of global comment */ ZPOS64_T uPosFound=0; if (ZSEEK64(*pzlib_filefunc_def,filestream,0,ZLIB_FILEFUNC_SEEK_END) != 0) return 0; uSizeFile = ZTELL64(*pzlib_filefunc_def,filestream); if (uMaxBack>uSizeFile) uMaxBack = uSizeFile; buf = (unsigned char*)ALLOC(BUFREADCOMMENT+4); if (buf==NULL) return 0; uBackRead = 4; while (uBackReaduMaxBack) uBackRead = uMaxBack; else uBackRead+=BUFREADCOMMENT; uReadPos = uSizeFile-uBackRead ; uReadSize = ((BUFREADCOMMENT+4) < (uSizeFile-uReadPos)) ? (BUFREADCOMMENT+4) : (uLong)(uSizeFile-uReadPos); if (ZSEEK64(*pzlib_filefunc_def,filestream,uReadPos,ZLIB_FILEFUNC_SEEK_SET)!=0) break; if (ZREAD64(*pzlib_filefunc_def,filestream,buf,uReadSize)!=uReadSize) break; for (i=(int)uReadSize-3; (i--)>0;) if (((*(buf+i))==0x50) && ((*(buf+i+1))==0x4b) && ((*(buf+i+2))==0x05) && ((*(buf+i+3))==0x06)) { uPosFound = uReadPos+i; break; } if (uPosFound!=0) break; } TRYFREE(buf); return uPosFound; } /* Locate the Central directory 64 of a zipfile (at the end, just before the global comment) */ local ZPOS64_T unz64local_SearchCentralDir64 OF(( const zlib_filefunc64_32_def* pzlib_filefunc_def, voidpf filestream)); local ZPOS64_T unz64local_SearchCentralDir64(const zlib_filefunc64_32_def* pzlib_filefunc_def, voidpf filestream) { unsigned char* buf; ZPOS64_T uSizeFile; ZPOS64_T uBackRead; ZPOS64_T uMaxBack=0xffff; /* maximum size of global comment */ ZPOS64_T uPosFound=0; uLong uL; ZPOS64_T relativeOffset; if (ZSEEK64(*pzlib_filefunc_def,filestream,0,ZLIB_FILEFUNC_SEEK_END) != 0) return 0; uSizeFile = ZTELL64(*pzlib_filefunc_def,filestream); if (uMaxBack>uSizeFile) uMaxBack = uSizeFile; buf = (unsigned char*)ALLOC(BUFREADCOMMENT+4); if (buf==NULL) return 0; uBackRead = 4; while (uBackReaduMaxBack) uBackRead = uMaxBack; else uBackRead+=BUFREADCOMMENT; uReadPos = uSizeFile-uBackRead ; uReadSize = ((BUFREADCOMMENT+4) < (uSizeFile-uReadPos)) ? (BUFREADCOMMENT+4) : (uLong)(uSizeFile-uReadPos); if (ZSEEK64(*pzlib_filefunc_def,filestream,uReadPos,ZLIB_FILEFUNC_SEEK_SET)!=0) break; if (ZREAD64(*pzlib_filefunc_def,filestream,buf,uReadSize)!=uReadSize) break; for (i=(int)uReadSize-3; (i--)>0;) if (((*(buf+i))==0x50) && ((*(buf+i+1))==0x4b) && ((*(buf+i+2))==0x06) && ((*(buf+i+3))==0x07)) { uPosFound = uReadPos+i; break; } if (uPosFound!=0) break; } TRYFREE(buf); if (uPosFound == 0) return 0; /* Zip64 end of central directory locator */ if (ZSEEK64(*pzlib_filefunc_def,filestream, uPosFound,ZLIB_FILEFUNC_SEEK_SET)!=0) return 0; /* the signature, already checked */ if (unz64local_getLong(pzlib_filefunc_def,filestream,&uL)!=UNZ_OK) return 0; /* number of the disk with the start of the zip64 end of central directory */ if (unz64local_getLong(pzlib_filefunc_def,filestream,&uL)!=UNZ_OK) return 0; if (uL != 0) return 0; /* relative offset of the zip64 end of central directory record */ if (unz64local_getLong64(pzlib_filefunc_def,filestream,&relativeOffset)!=UNZ_OK) return 0; /* total number of disks */ if (unz64local_getLong(pzlib_filefunc_def,filestream,&uL)!=UNZ_OK) return 0; if (uL != 1) return 0; /* Goto end of central directory record */ if (ZSEEK64(*pzlib_filefunc_def,filestream, relativeOffset,ZLIB_FILEFUNC_SEEK_SET)!=0) return 0; /* the signature */ if (unz64local_getLong(pzlib_filefunc_def,filestream,&uL)!=UNZ_OK) return 0; if (uL != 0x06064b50) return 0; return relativeOffset; } /* Open a Zip file. path contain the full pathname (by example, on a Windows NT computer "c:\\test\\zlib114.zip" or on an Unix computer "zlib/zlib114.zip". If the zipfile cannot be opened (file doesn't exist or in not valid), the return value is NULL. Else, the return value is a unzFile Handle, usable with other function of this unzip package. */ local unzFile unzOpenInternal (voidpf file, zlib_filefunc64_32_def* pzlib_filefunc64_32_def, int is64bitOpenFunction) { unz64_s us; unz64_s *s; ZPOS64_T central_pos; uLong uL; uLong number_disk; /* number of the current dist, used for spaning ZIP, unsupported, always 0*/ uLong number_disk_with_CD; /* number the the disk with central dir, used for spaning ZIP, unsupported, always 0*/ ZPOS64_T number_entry_CD; /* total number of entries in the central dir (same than number_entry on nospan) */ int err=UNZ_OK; if (unz_copyright[0]!=' ') return NULL; us.z_filefunc.zseek32_file = NULL; us.z_filefunc.ztell32_file = NULL; if (pzlib_filefunc64_32_def==NULL) fill_qiodevice64_filefunc(&us.z_filefunc.zfile_func64); else us.z_filefunc = *pzlib_filefunc64_32_def; us.is64bitOpenFunction = is64bitOpenFunction; us.filestream = ZOPEN64(us.z_filefunc, file, ZLIB_FILEFUNC_MODE_READ | ZLIB_FILEFUNC_MODE_EXISTING); if (us.filestream==NULL) return NULL; central_pos = unz64local_SearchCentralDir64(&us.z_filefunc,us.filestream); if (central_pos) { uLong uS; ZPOS64_T uL64; us.isZip64 = 1; if (ZSEEK64(us.z_filefunc, us.filestream, central_pos,ZLIB_FILEFUNC_SEEK_SET)!=0) err=UNZ_ERRNO; /* the signature, already checked */ if (unz64local_getLong(&us.z_filefunc, us.filestream,&uL)!=UNZ_OK) err=UNZ_ERRNO; /* size of zip64 end of central directory record */ if (unz64local_getLong64(&us.z_filefunc, us.filestream,&uL64)!=UNZ_OK) err=UNZ_ERRNO; /* version made by */ if (unz64local_getShort(&us.z_filefunc, us.filestream,&uS)!=UNZ_OK) err=UNZ_ERRNO; /* version needed to extract */ if (unz64local_getShort(&us.z_filefunc, us.filestream,&uS)!=UNZ_OK) err=UNZ_ERRNO; /* number of this disk */ if (unz64local_getLong(&us.z_filefunc, us.filestream,&number_disk)!=UNZ_OK) err=UNZ_ERRNO; /* number of the disk with the start of the central directory */ if (unz64local_getLong(&us.z_filefunc, us.filestream,&number_disk_with_CD)!=UNZ_OK) err=UNZ_ERRNO; /* total number of entries in the central directory on this disk */ if (unz64local_getLong64(&us.z_filefunc, us.filestream,&us.gi.number_entry)!=UNZ_OK) err=UNZ_ERRNO; /* total number of entries in the central directory */ if (unz64local_getLong64(&us.z_filefunc, us.filestream,&number_entry_CD)!=UNZ_OK) err=UNZ_ERRNO; if ((number_entry_CD!=us.gi.number_entry) || (number_disk_with_CD!=0) || (number_disk!=0)) err=UNZ_BADZIPFILE; /* size of the central directory */ if (unz64local_getLong64(&us.z_filefunc, us.filestream,&us.size_central_dir)!=UNZ_OK) err=UNZ_ERRNO; /* offset of start of central directory with respect to the starting disk number */ if (unz64local_getLong64(&us.z_filefunc, us.filestream,&us.offset_central_dir)!=UNZ_OK) err=UNZ_ERRNO; us.gi.size_comment = 0; } else { central_pos = unz64local_SearchCentralDir(&us.z_filefunc,us.filestream); if (central_pos==0) err=UNZ_ERRNO; us.isZip64 = 0; if (ZSEEK64(us.z_filefunc, us.filestream, central_pos,ZLIB_FILEFUNC_SEEK_SET)!=0) err=UNZ_ERRNO; /* the signature, already checked */ if (unz64local_getLong(&us.z_filefunc, us.filestream,&uL)!=UNZ_OK) err=UNZ_ERRNO; /* number of this disk */ if (unz64local_getShort(&us.z_filefunc, us.filestream,&number_disk)!=UNZ_OK) err=UNZ_ERRNO; /* number of the disk with the start of the central directory */ if (unz64local_getShort(&us.z_filefunc, us.filestream,&number_disk_with_CD)!=UNZ_OK) err=UNZ_ERRNO; /* total number of entries in the central dir on this disk */ if (unz64local_getShort(&us.z_filefunc, us.filestream,&uL)!=UNZ_OK) err=UNZ_ERRNO; us.gi.number_entry = uL; /* total number of entries in the central dir */ if (unz64local_getShort(&us.z_filefunc, us.filestream,&uL)!=UNZ_OK) err=UNZ_ERRNO; number_entry_CD = uL; if ((number_entry_CD!=us.gi.number_entry) || (number_disk_with_CD!=0) || (number_disk!=0)) err=UNZ_BADZIPFILE; /* size of the central directory */ if (unz64local_getLong(&us.z_filefunc, us.filestream,&uL)!=UNZ_OK) err=UNZ_ERRNO; us.size_central_dir = uL; /* offset of start of central directory with respect to the starting disk number */ if (unz64local_getLong(&us.z_filefunc, us.filestream,&uL)!=UNZ_OK) err=UNZ_ERRNO; us.offset_central_dir = uL; /* zipfile comment length */ if (unz64local_getShort(&us.z_filefunc, us.filestream,&us.gi.size_comment)!=UNZ_OK) err=UNZ_ERRNO; } if ((central_pospfile_in_zip_read!=NULL) unzCloseCurrentFile(file); ZCLOSE64(s->z_filefunc, s->filestream); TRYFREE(s); return UNZ_OK; } /* Write info about the ZipFile in the *pglobal_info structure. No preparation of the structure is needed return UNZ_OK if there is no problem. */ extern int ZEXPORT unzGetGlobalInfo64 (unzFile file, unz_global_info64* pglobal_info) { unz64_s* s; if (file==NULL) return UNZ_PARAMERROR; s=(unz64_s*)file; *pglobal_info=s->gi; return UNZ_OK; } extern int ZEXPORT unzGetGlobalInfo (unzFile file, unz_global_info* pglobal_info32) { unz64_s* s; if (file==NULL) return UNZ_PARAMERROR; s=(unz64_s*)file; /* to do : check if number_entry is not truncated */ pglobal_info32->number_entry = (uLong)s->gi.number_entry; pglobal_info32->size_comment = s->gi.size_comment; return UNZ_OK; } /* Translate date/time from Dos format to tm_unz (readable more easilty) */ local void unz64local_DosDateToTmuDate (ZPOS64_T ulDosDate, tm_unz* ptm) { ZPOS64_T uDate; uDate = (ZPOS64_T)(ulDosDate>>16); ptm->tm_mday = (uInt)(uDate&0x1f) ; ptm->tm_mon = (uInt)((((uDate)&0x1E0)/0x20)-1) ; ptm->tm_year = (uInt)(((uDate&0x0FE00)/0x0200)+1980) ; ptm->tm_hour = (uInt) ((ulDosDate &0xF800)/0x800); ptm->tm_min = (uInt) ((ulDosDate&0x7E0)/0x20) ; ptm->tm_sec = (uInt) (2*(ulDosDate&0x1f)) ; } /* Get Info about the current file in the zipfile, with internal only info */ local int unz64local_GetCurrentFileInfoInternal OF((unzFile file, unz_file_info64 *pfile_info, unz_file_info64_internal *pfile_info_internal, char *szFileName, uLong fileNameBufferSize, void *extraField, uLong extraFieldBufferSize, char *szComment, uLong commentBufferSize)); local int unz64local_GetCurrentFileInfoInternal (unzFile file, unz_file_info64 *pfile_info, unz_file_info64_internal *pfile_info_internal, char *szFileName, uLong fileNameBufferSize, void *extraField, uLong extraFieldBufferSize, char *szComment, uLong commentBufferSize) { unz64_s* s; unz_file_info64 file_info; unz_file_info64_internal file_info_internal; int err=UNZ_OK; uLong uMagic; ZPOS64_T llSeek=0; uLong uL; if (file==NULL) return UNZ_PARAMERROR; s=(unz64_s*)file; if (ZSEEK64(s->z_filefunc, s->filestream, s->pos_in_central_dir+s->byte_before_the_zipfile, ZLIB_FILEFUNC_SEEK_SET)!=0) err=UNZ_ERRNO; /* we check the magic */ if (err==UNZ_OK) { if (unz64local_getLong(&s->z_filefunc, s->filestream,&uMagic) != UNZ_OK) err=UNZ_ERRNO; else if (uMagic!=0x02014b50) err=UNZ_BADZIPFILE; } if (unz64local_getShort(&s->z_filefunc, s->filestream,&file_info.version) != UNZ_OK) err=UNZ_ERRNO; if (unz64local_getShort(&s->z_filefunc, s->filestream,&file_info.version_needed) != UNZ_OK) err=UNZ_ERRNO; if (unz64local_getShort(&s->z_filefunc, s->filestream,&file_info.flag) != UNZ_OK) err=UNZ_ERRNO; if (unz64local_getShort(&s->z_filefunc, s->filestream,&file_info.compression_method) != UNZ_OK) err=UNZ_ERRNO; if (unz64local_getLong(&s->z_filefunc, s->filestream,&file_info.dosDate) != UNZ_OK) err=UNZ_ERRNO; unz64local_DosDateToTmuDate(file_info.dosDate,&file_info.tmu_date); if (unz64local_getLong(&s->z_filefunc, s->filestream,&file_info.crc) != UNZ_OK) err=UNZ_ERRNO; if (unz64local_getLong(&s->z_filefunc, s->filestream,&uL) != UNZ_OK) err=UNZ_ERRNO; file_info.compressed_size = uL; if (unz64local_getLong(&s->z_filefunc, s->filestream,&uL) != UNZ_OK) err=UNZ_ERRNO; file_info.uncompressed_size = uL; if (unz64local_getShort(&s->z_filefunc, s->filestream,&file_info.size_filename) != UNZ_OK) err=UNZ_ERRNO; if (unz64local_getShort(&s->z_filefunc, s->filestream,&file_info.size_file_extra) != UNZ_OK) err=UNZ_ERRNO; if (unz64local_getShort(&s->z_filefunc, s->filestream,&file_info.size_file_comment) != UNZ_OK) err=UNZ_ERRNO; if (unz64local_getShort(&s->z_filefunc, s->filestream,&file_info.disk_num_start) != UNZ_OK) err=UNZ_ERRNO; if (unz64local_getShort(&s->z_filefunc, s->filestream,&file_info.internal_fa) != UNZ_OK) err=UNZ_ERRNO; if (unz64local_getLong(&s->z_filefunc, s->filestream,&file_info.external_fa) != UNZ_OK) err=UNZ_ERRNO; /* relative offset of local header */ if (unz64local_getLong(&s->z_filefunc, s->filestream,&uL) != UNZ_OK) err=UNZ_ERRNO; file_info_internal.offset_curfile = uL; llSeek+=file_info.size_filename; if ((err==UNZ_OK) && (szFileName!=NULL)) { uLong uSizeRead ; if (file_info.size_filename0) && (fileNameBufferSize>0)) if (ZREAD64(s->z_filefunc, s->filestream,szFileName,uSizeRead)!=uSizeRead) err=UNZ_ERRNO; llSeek -= uSizeRead; } /* Read extrafield */ if ((err==UNZ_OK) && (extraField!=NULL)) { ZPOS64_T uSizeRead ; if (file_info.size_file_extraz_filefunc, s->filestream,llSeek,ZLIB_FILEFUNC_SEEK_CUR)==0) llSeek=0; else err=UNZ_ERRNO; } if ((file_info.size_file_extra>0) && (extraFieldBufferSize>0)) if (ZREAD64(s->z_filefunc, s->filestream,extraField,(uLong)uSizeRead)!=uSizeRead) err=UNZ_ERRNO; llSeek += file_info.size_file_extra - (uLong)uSizeRead; } else llSeek += file_info.size_file_extra; if ((err==UNZ_OK) && (file_info.size_file_extra != 0)) { uLong acc = 0; /* since lSeek now points to after the extra field we need to move back */ llSeek -= file_info.size_file_extra; if (llSeek!=0) { if (ZSEEK64(s->z_filefunc, s->filestream,llSeek,ZLIB_FILEFUNC_SEEK_CUR)==0) llSeek=0; else err=UNZ_ERRNO; } while(acc < file_info.size_file_extra) { uLong headerId; uLong dataSize; if (unz64local_getShort(&s->z_filefunc, s->filestream,&headerId) != UNZ_OK) err=UNZ_ERRNO; if (unz64local_getShort(&s->z_filefunc, s->filestream,&dataSize) != UNZ_OK) err=UNZ_ERRNO; /* ZIP64 extra fields */ if (headerId == 0x0001) { uLong uL; if(file_info.uncompressed_size == (ZPOS64_T)(unsigned long)-1) { if (unz64local_getLong64(&s->z_filefunc, s->filestream,&file_info.uncompressed_size) != UNZ_OK) err=UNZ_ERRNO; } if(file_info.compressed_size == (ZPOS64_T)(unsigned long)-1) { if (unz64local_getLong64(&s->z_filefunc, s->filestream,&file_info.compressed_size) != UNZ_OK) err=UNZ_ERRNO; } if(file_info_internal.offset_curfile == (ZPOS64_T)(unsigned long)-1) { /* Relative Header offset */ if (unz64local_getLong64(&s->z_filefunc, s->filestream,&file_info_internal.offset_curfile) != UNZ_OK) err=UNZ_ERRNO; } if(file_info.disk_num_start == (unsigned long)-1) { /* Disk Start Number */ if (unz64local_getLong(&s->z_filefunc, s->filestream,&uL) != UNZ_OK) err=UNZ_ERRNO; } } else { if (ZSEEK64(s->z_filefunc, s->filestream,dataSize,ZLIB_FILEFUNC_SEEK_CUR)!=0) err=UNZ_ERRNO; } acc += 2 + 2 + dataSize; } } if ((err==UNZ_OK) && (szComment!=NULL)) { uLong uSizeRead ; if (file_info.size_file_commentz_filefunc, s->filestream,llSeek,ZLIB_FILEFUNC_SEEK_CUR)==0) llSeek=0; else err=UNZ_ERRNO; } if ((file_info.size_file_comment>0) && (commentBufferSize>0)) if (ZREAD64(s->z_filefunc, s->filestream,szComment,uSizeRead)!=uSizeRead) err=UNZ_ERRNO; llSeek+=file_info.size_file_comment - uSizeRead; } else llSeek+=file_info.size_file_comment; if ((err==UNZ_OK) && (pfile_info!=NULL)) *pfile_info=file_info; if ((err==UNZ_OK) && (pfile_info_internal!=NULL)) *pfile_info_internal=file_info_internal; return err; } /* Write info about the ZipFile in the *pglobal_info structure. No preparation of the structure is needed return UNZ_OK if there is no problem. */ extern int ZEXPORT unzGetCurrentFileInfo64 (unzFile file, unz_file_info64 * pfile_info, char * szFileName, uLong fileNameBufferSize, void *extraField, uLong extraFieldBufferSize, char* szComment, uLong commentBufferSize) { return unz64local_GetCurrentFileInfoInternal(file,pfile_info,NULL, szFileName,fileNameBufferSize, extraField,extraFieldBufferSize, szComment,commentBufferSize); } extern int ZEXPORT unzGetCurrentFileInfo (unzFile file, unz_file_info * pfile_info, char * szFileName, uLong fileNameBufferSize, void *extraField, uLong extraFieldBufferSize, char* szComment, uLong commentBufferSize) { int err; unz_file_info64 file_info64; err = unz64local_GetCurrentFileInfoInternal(file,&file_info64,NULL, szFileName,fileNameBufferSize, extraField,extraFieldBufferSize, szComment,commentBufferSize); if (err==UNZ_OK && pfile_info != NULL) { pfile_info->version = file_info64.version; pfile_info->version_needed = file_info64.version_needed; pfile_info->flag = file_info64.flag; pfile_info->compression_method = file_info64.compression_method; pfile_info->dosDate = file_info64.dosDate; pfile_info->crc = file_info64.crc; pfile_info->size_filename = file_info64.size_filename; pfile_info->size_file_extra = file_info64.size_file_extra; pfile_info->size_file_comment = file_info64.size_file_comment; pfile_info->disk_num_start = file_info64.disk_num_start; pfile_info->internal_fa = file_info64.internal_fa; pfile_info->external_fa = file_info64.external_fa; pfile_info->tmu_date = file_info64.tmu_date, pfile_info->compressed_size = (uLong)file_info64.compressed_size; pfile_info->uncompressed_size = (uLong)file_info64.uncompressed_size; } return err; } /* Set the current file of the zipfile to the first file. return UNZ_OK if there is no problem */ extern int ZEXPORT unzGoToFirstFile (unzFile file) { int err=UNZ_OK; unz64_s* s; if (file==NULL) return UNZ_PARAMERROR; s=(unz64_s*)file; s->pos_in_central_dir=s->offset_central_dir; s->num_file=0; err=unz64local_GetCurrentFileInfoInternal(file,&s->cur_file_info, &s->cur_file_info_internal, NULL,0,NULL,0,NULL,0); s->current_file_ok = (err == UNZ_OK); return err; } /* Set the current file of the zipfile to the next file. return UNZ_OK if there is no problem return UNZ_END_OF_LIST_OF_FILE if the actual file was the latest. */ extern int ZEXPORT unzGoToNextFile (unzFile file) { unz64_s* s; int err; if (file==NULL) return UNZ_PARAMERROR; s=(unz64_s*)file; if (!s->current_file_ok) return UNZ_END_OF_LIST_OF_FILE; if (s->gi.number_entry != 0xffff) /* 2^16 files overflow hack */ if (s->num_file+1==s->gi.number_entry) return UNZ_END_OF_LIST_OF_FILE; s->pos_in_central_dir += SIZECENTRALDIRITEM + s->cur_file_info.size_filename + s->cur_file_info.size_file_extra + s->cur_file_info.size_file_comment ; s->num_file++; err = unz64local_GetCurrentFileInfoInternal(file,&s->cur_file_info, &s->cur_file_info_internal, NULL,0,NULL,0,NULL,0); s->current_file_ok = (err == UNZ_OK); return err; } /* Try locate the file szFileName in the zipfile. For the iCaseSensitivity signification, see unzipStringFileNameCompare return value : UNZ_OK if the file is found. It becomes the current file. UNZ_END_OF_LIST_OF_FILE if the file is not found */ extern int ZEXPORT unzLocateFile (unzFile file, const char *szFileName, int iCaseSensitivity) { unz64_s* s; int err; /* We remember the 'current' position in the file so that we can jump * back there if we fail. */ unz_file_info64 cur_file_infoSaved; unz_file_info64_internal cur_file_info_internalSaved; ZPOS64_T num_fileSaved; ZPOS64_T pos_in_central_dirSaved; if (file==NULL) return UNZ_PARAMERROR; if (strlen(szFileName)>=UNZ_MAXFILENAMEINZIP) return UNZ_PARAMERROR; s=(unz64_s*)file; if (!s->current_file_ok) return UNZ_END_OF_LIST_OF_FILE; /* Save the current state */ num_fileSaved = s->num_file; pos_in_central_dirSaved = s->pos_in_central_dir; cur_file_infoSaved = s->cur_file_info; cur_file_info_internalSaved = s->cur_file_info_internal; err = unzGoToFirstFile(file); while (err == UNZ_OK) { char szCurrentFileName[UNZ_MAXFILENAMEINZIP+1]; err = unzGetCurrentFileInfo64(file,NULL, szCurrentFileName,sizeof(szCurrentFileName)-1, NULL,0,NULL,0); if (err == UNZ_OK) { if (unzStringFileNameCompare(szCurrentFileName, szFileName,iCaseSensitivity)==0) return UNZ_OK; err = unzGoToNextFile(file); } } /* We failed, so restore the state of the 'current file' to where we * were. */ s->num_file = num_fileSaved ; s->pos_in_central_dir = pos_in_central_dirSaved ; s->cur_file_info = cur_file_infoSaved; s->cur_file_info_internal = cur_file_info_internalSaved; return err; } /* /////////////////////////////////////////// // Contributed by Ryan Haksi (mailto://cryogen@infoserve.net) // I need random access // // Further optimization could be realized by adding an ability // to cache the directory in memory. The goal being a single // comprehensive file read to put the file I need in a memory. */ /* typedef struct unz_file_pos_s { ZPOS64_T pos_in_zip_directory; // offset in file ZPOS64_T num_of_file; // # of file } unz_file_pos; */ extern int ZEXPORT unzGetFilePos64(unzFile file, unz64_file_pos* file_pos) { unz64_s* s; if (file==NULL || file_pos==NULL) return UNZ_PARAMERROR; s=(unz64_s*)file; if (!s->current_file_ok) return UNZ_END_OF_LIST_OF_FILE; file_pos->pos_in_zip_directory = s->pos_in_central_dir; file_pos->num_of_file = s->num_file; return UNZ_OK; } extern int ZEXPORT unzGetFilePos( unzFile file, unz_file_pos* file_pos) { unz64_file_pos file_pos64; int err = unzGetFilePos64(file,&file_pos64); if (err==UNZ_OK) { file_pos->pos_in_zip_directory = (uLong)file_pos64.pos_in_zip_directory; file_pos->num_of_file = (uLong)file_pos64.num_of_file; } return err; } extern int ZEXPORT unzGoToFilePos64(unzFile file, const unz64_file_pos* file_pos) { unz64_s* s; int err; if (file==NULL || file_pos==NULL) return UNZ_PARAMERROR; s=(unz64_s*)file; /* jump to the right spot */ s->pos_in_central_dir = file_pos->pos_in_zip_directory; s->num_file = file_pos->num_of_file; /* set the current file */ err = unz64local_GetCurrentFileInfoInternal(file,&s->cur_file_info, &s->cur_file_info_internal, NULL,0,NULL,0,NULL,0); /* return results */ s->current_file_ok = (err == UNZ_OK); return err; } extern int ZEXPORT unzGoToFilePos( unzFile file, unz_file_pos* file_pos) { unz64_file_pos file_pos64; if (file_pos == NULL) return UNZ_PARAMERROR; file_pos64.pos_in_zip_directory = file_pos->pos_in_zip_directory; file_pos64.num_of_file = file_pos->num_of_file; return unzGoToFilePos64(file,&file_pos64); } /* Unzip Helper Functions - should be here? */ /*///////////////////////////////////////// */ /* Read the local header of the current zipfile Check the coherency of the local header and info in the end of central directory about this file store in *piSizeVar the size of extra info in local header (filename and size of extra field data) */ local int unz64local_CheckCurrentFileCoherencyHeader (unz64_s* s, uInt* piSizeVar, ZPOS64_T * poffset_local_extrafield, uInt * psize_local_extrafield) { uLong uMagic,uData,uFlags; uLong size_filename; uLong size_extra_field; int err=UNZ_OK; *piSizeVar = 0; *poffset_local_extrafield = 0; *psize_local_extrafield = 0; if (ZSEEK64(s->z_filefunc, s->filestream,s->cur_file_info_internal.offset_curfile + s->byte_before_the_zipfile,ZLIB_FILEFUNC_SEEK_SET)!=0) return UNZ_ERRNO; if (err==UNZ_OK) { if (unz64local_getLong(&s->z_filefunc, s->filestream,&uMagic) != UNZ_OK) err=UNZ_ERRNO; else if (uMagic!=0x04034b50) err=UNZ_BADZIPFILE; } if (unz64local_getShort(&s->z_filefunc, s->filestream,&uData) != UNZ_OK) err=UNZ_ERRNO; /* else if ((err==UNZ_OK) && (uData!=s->cur_file_info.wVersion)) err=UNZ_BADZIPFILE; */ if (unz64local_getShort(&s->z_filefunc, s->filestream,&uFlags) != UNZ_OK) err=UNZ_ERRNO; if (unz64local_getShort(&s->z_filefunc, s->filestream,&uData) != UNZ_OK) err=UNZ_ERRNO; else if ((err==UNZ_OK) && (uData!=s->cur_file_info.compression_method)) err=UNZ_BADZIPFILE; if ((err==UNZ_OK) && (s->cur_file_info.compression_method!=0) && /* #ifdef HAVE_BZIP2 */ (s->cur_file_info.compression_method!=Z_BZIP2ED) && /* #endif */ (s->cur_file_info.compression_method!=Z_DEFLATED)) err=UNZ_BADZIPFILE; if (unz64local_getLong(&s->z_filefunc, s->filestream,&uData) != UNZ_OK) /* date/time */ err=UNZ_ERRNO; if (unz64local_getLong(&s->z_filefunc, s->filestream,&uData) != UNZ_OK) /* crc */ err=UNZ_ERRNO; else if ((err==UNZ_OK) && (uData!=s->cur_file_info.crc) && ((uFlags & 8)==0)) err=UNZ_BADZIPFILE; if (unz64local_getLong(&s->z_filefunc, s->filestream,&uData) != UNZ_OK) /* size compr */ err=UNZ_ERRNO; else if (uData != 0xFFFFFFFF && (err==UNZ_OK) && (uData!=s->cur_file_info.compressed_size) && ((uFlags & 8)==0)) err=UNZ_BADZIPFILE; if (unz64local_getLong(&s->z_filefunc, s->filestream,&uData) != UNZ_OK) /* size uncompr */ err=UNZ_ERRNO; else if (uData != 0xFFFFFFFF && (err==UNZ_OK) && (uData!=s->cur_file_info.uncompressed_size) && ((uFlags & 8)==0)) err=UNZ_BADZIPFILE; if (unz64local_getShort(&s->z_filefunc, s->filestream,&size_filename) != UNZ_OK) err=UNZ_ERRNO; else if ((err==UNZ_OK) && (size_filename!=s->cur_file_info.size_filename)) err=UNZ_BADZIPFILE; *piSizeVar += (uInt)size_filename; if (unz64local_getShort(&s->z_filefunc, s->filestream,&size_extra_field) != UNZ_OK) err=UNZ_ERRNO; *poffset_local_extrafield= s->cur_file_info_internal.offset_curfile + SIZEZIPLOCALHEADER + size_filename; *psize_local_extrafield = (uInt)size_extra_field; *piSizeVar += (uInt)size_extra_field; return err; } /* Open for reading data the current file in the zipfile. If there is no error and the file is opened, the return value is UNZ_OK. */ extern int ZEXPORT unzOpenCurrentFile3 (unzFile file, int* method, int* level, int raw, const char* password) { int err=UNZ_OK; uInt iSizeVar; unz64_s* s; file_in_zip64_read_info_s* pfile_in_zip_read_info; ZPOS64_T offset_local_extrafield; /* offset of the local extra field */ uInt size_local_extrafield; /* size of the local extra field */ # ifndef NOUNCRYPT char source[12]; # else if (password != NULL) return UNZ_PARAMERROR; # endif if (file==NULL) return UNZ_PARAMERROR; s=(unz64_s*)file; if (!s->current_file_ok) return UNZ_PARAMERROR; if (s->pfile_in_zip_read != NULL) unzCloseCurrentFile(file); if (unz64local_CheckCurrentFileCoherencyHeader(s,&iSizeVar, &offset_local_extrafield,&size_local_extrafield)!=UNZ_OK) return UNZ_BADZIPFILE; pfile_in_zip_read_info = (file_in_zip64_read_info_s*)ALLOC(sizeof(file_in_zip64_read_info_s)); if (pfile_in_zip_read_info==NULL) return UNZ_INTERNALERROR; pfile_in_zip_read_info->read_buffer=(char*)ALLOC(UNZ_BUFSIZE); pfile_in_zip_read_info->offset_local_extrafield = offset_local_extrafield; pfile_in_zip_read_info->size_local_extrafield = size_local_extrafield; pfile_in_zip_read_info->pos_local_extrafield=0; pfile_in_zip_read_info->raw=raw; if (pfile_in_zip_read_info->read_buffer==NULL) { TRYFREE(pfile_in_zip_read_info); return UNZ_INTERNALERROR; } pfile_in_zip_read_info->stream_initialised=0; if (method!=NULL) *method = (int)s->cur_file_info.compression_method; if (level!=NULL) { *level = 6; switch (s->cur_file_info.flag & 0x06) { case 6 : *level = 1; break; case 4 : *level = 2; break; case 2 : *level = 9; break; } } if ((s->cur_file_info.compression_method!=0) && /* #ifdef HAVE_BZIP2 */ (s->cur_file_info.compression_method!=Z_BZIP2ED) && /* #endif */ (s->cur_file_info.compression_method!=Z_DEFLATED)) err=UNZ_BADZIPFILE; pfile_in_zip_read_info->crc32_wait=s->cur_file_info.crc; pfile_in_zip_read_info->crc32=0; pfile_in_zip_read_info->total_out_64=0; pfile_in_zip_read_info->compression_method = s->cur_file_info.compression_method; pfile_in_zip_read_info->filestream=s->filestream; pfile_in_zip_read_info->z_filefunc=s->z_filefunc; pfile_in_zip_read_info->byte_before_the_zipfile=s->byte_before_the_zipfile; pfile_in_zip_read_info->stream.total_out = 0; if ((s->cur_file_info.compression_method==Z_BZIP2ED) && (!raw)) { #ifdef HAVE_BZIP2 pfile_in_zip_read_info->bstream.bzalloc = (void *(*) (void *, int, int))0; pfile_in_zip_read_info->bstream.bzfree = (free_func)0; pfile_in_zip_read_info->bstream.opaque = (voidpf)0; pfile_in_zip_read_info->bstream.state = (voidpf)0; pfile_in_zip_read_info->stream.zalloc = (alloc_func)0; pfile_in_zip_read_info->stream.zfree = (free_func)0; pfile_in_zip_read_info->stream.opaque = (voidpf)0; pfile_in_zip_read_info->stream.next_in = (voidpf)0; pfile_in_zip_read_info->stream.avail_in = 0; err=BZ2_bzDecompressInit(&pfile_in_zip_read_info->bstream, 0, 0); if (err == Z_OK) pfile_in_zip_read_info->stream_initialised=Z_BZIP2ED; else { TRYFREE(pfile_in_zip_read_info); return err; } #else pfile_in_zip_read_info->raw=1; #endif } else if ((s->cur_file_info.compression_method==Z_DEFLATED) && (!raw)) { pfile_in_zip_read_info->stream.zalloc = (alloc_func)0; pfile_in_zip_read_info->stream.zfree = (free_func)0; pfile_in_zip_read_info->stream.opaque = (voidpf)0; pfile_in_zip_read_info->stream.next_in = 0; pfile_in_zip_read_info->stream.avail_in = 0; err=inflateInit2(&pfile_in_zip_read_info->stream, -MAX_WBITS); if (err == Z_OK) pfile_in_zip_read_info->stream_initialised=Z_DEFLATED; else { TRYFREE(pfile_in_zip_read_info); return err; } /* windowBits is passed < 0 to tell that there is no zlib header. * Note that in this case inflate *requires* an extra "dummy" byte * after the compressed stream in order to complete decompression and * return Z_STREAM_END. * In unzip, i don't wait absolutely Z_STREAM_END because I known the * size of both compressed and uncompressed data */ } pfile_in_zip_read_info->rest_read_compressed = s->cur_file_info.compressed_size ; pfile_in_zip_read_info->rest_read_uncompressed = s->cur_file_info.uncompressed_size ; pfile_in_zip_read_info->pos_in_zipfile = s->cur_file_info_internal.offset_curfile + SIZEZIPLOCALHEADER + iSizeVar; pfile_in_zip_read_info->stream.avail_in = (uInt)0; s->pfile_in_zip_read = pfile_in_zip_read_info; s->encrypted = 0; # ifndef NOUNCRYPT if (password != NULL) { int i; s->pcrc_32_tab = get_crc_table(); init_keys(password,s->keys,s->pcrc_32_tab); if (ZSEEK64(s->z_filefunc, s->filestream, s->pfile_in_zip_read->pos_in_zipfile + s->pfile_in_zip_read->byte_before_the_zipfile, SEEK_SET)!=0) return UNZ_INTERNALERROR; if(ZREAD64(s->z_filefunc, s->filestream,source, 12)<12) return UNZ_INTERNALERROR; for (i = 0; i<12; i++) zdecode(s->keys,s->pcrc_32_tab,source[i]); s->pfile_in_zip_read->pos_in_zipfile+=12; s->encrypted=1; } # endif return UNZ_OK; } extern int ZEXPORT unzOpenCurrentFile (unzFile file) { return unzOpenCurrentFile3(file, NULL, NULL, 0, NULL); } extern int ZEXPORT unzOpenCurrentFilePassword (unzFile file, const char* password) { return unzOpenCurrentFile3(file, NULL, NULL, 0, password); } extern int ZEXPORT unzOpenCurrentFile2 (unzFile file, int* method, int* level, int raw) { return unzOpenCurrentFile3(file, method, level, raw, NULL); } /** Addition for GDAL : START */ extern ZPOS64_T ZEXPORT unzGetCurrentFileZStreamPos64( unzFile file) { unz64_s* s; file_in_zip64_read_info_s* pfile_in_zip_read_info; s=(unz64_s*)file; if (file==NULL) return 0; /*UNZ_PARAMERROR; */ pfile_in_zip_read_info=s->pfile_in_zip_read; if (pfile_in_zip_read_info==NULL) return 0; /*UNZ_PARAMERROR; */ return pfile_in_zip_read_info->pos_in_zipfile + pfile_in_zip_read_info->byte_before_the_zipfile; } /** Addition for GDAL : END */ /* Read bytes from the current file. buf contain buffer where data must be copied len the size of buf. return the number of byte copied if somes bytes are copied return 0 if the end of file was reached return <0 with error code if there is an error (UNZ_ERRNO for IO error, or zLib error for uncompress error) */ extern int ZEXPORT unzReadCurrentFile (unzFile file, voidp buf, unsigned len) { int err=UNZ_OK; uInt iRead = 0; unz64_s* s; file_in_zip64_read_info_s* pfile_in_zip_read_info; if (file==NULL) return UNZ_PARAMERROR; s=(unz64_s*)file; pfile_in_zip_read_info=s->pfile_in_zip_read; if (pfile_in_zip_read_info==NULL) return UNZ_PARAMERROR; if (pfile_in_zip_read_info->read_buffer == NULL) return UNZ_END_OF_LIST_OF_FILE; if (len==0) return 0; pfile_in_zip_read_info->stream.next_out = (Bytef*)buf; pfile_in_zip_read_info->stream.avail_out = (uInt)len; if ((len>pfile_in_zip_read_info->rest_read_uncompressed) && (!(pfile_in_zip_read_info->raw))) pfile_in_zip_read_info->stream.avail_out = (uInt)pfile_in_zip_read_info->rest_read_uncompressed; if ((len>pfile_in_zip_read_info->rest_read_compressed+ pfile_in_zip_read_info->stream.avail_in) && (pfile_in_zip_read_info->raw)) pfile_in_zip_read_info->stream.avail_out = (uInt)pfile_in_zip_read_info->rest_read_compressed+ pfile_in_zip_read_info->stream.avail_in; while (pfile_in_zip_read_info->stream.avail_out>0) { if ((pfile_in_zip_read_info->stream.avail_in==0) && (pfile_in_zip_read_info->rest_read_compressed>0)) { uInt uReadThis = UNZ_BUFSIZE; if (pfile_in_zip_read_info->rest_read_compressedrest_read_compressed; if (uReadThis == 0) return UNZ_EOF; if (ZSEEK64(pfile_in_zip_read_info->z_filefunc, pfile_in_zip_read_info->filestream, pfile_in_zip_read_info->pos_in_zipfile + pfile_in_zip_read_info->byte_before_the_zipfile, ZLIB_FILEFUNC_SEEK_SET)!=0) return UNZ_ERRNO; if (ZREAD64(pfile_in_zip_read_info->z_filefunc, pfile_in_zip_read_info->filestream, pfile_in_zip_read_info->read_buffer, uReadThis)!=uReadThis) return UNZ_ERRNO; # ifndef NOUNCRYPT if(s->encrypted) { uInt i; for(i=0;iread_buffer[i] = zdecode(s->keys,s->pcrc_32_tab, pfile_in_zip_read_info->read_buffer[i]); } # endif pfile_in_zip_read_info->pos_in_zipfile += uReadThis; pfile_in_zip_read_info->rest_read_compressed-=uReadThis; pfile_in_zip_read_info->stream.next_in = (Bytef*)pfile_in_zip_read_info->read_buffer; pfile_in_zip_read_info->stream.avail_in = (uInt)uReadThis; } if ((pfile_in_zip_read_info->compression_method==0) || (pfile_in_zip_read_info->raw)) { uInt uDoCopy,i ; if ((pfile_in_zip_read_info->stream.avail_in == 0) && (pfile_in_zip_read_info->rest_read_compressed == 0)) return (iRead==0) ? UNZ_EOF : iRead; if (pfile_in_zip_read_info->stream.avail_out < pfile_in_zip_read_info->stream.avail_in) uDoCopy = pfile_in_zip_read_info->stream.avail_out ; else uDoCopy = pfile_in_zip_read_info->stream.avail_in ; for (i=0;istream.next_out+i) = *(pfile_in_zip_read_info->stream.next_in+i); pfile_in_zip_read_info->total_out_64 = pfile_in_zip_read_info->total_out_64 + uDoCopy; pfile_in_zip_read_info->crc32 = crc32(pfile_in_zip_read_info->crc32, pfile_in_zip_read_info->stream.next_out, uDoCopy); pfile_in_zip_read_info->rest_read_uncompressed-=uDoCopy; pfile_in_zip_read_info->stream.avail_in -= uDoCopy; pfile_in_zip_read_info->stream.avail_out -= uDoCopy; pfile_in_zip_read_info->stream.next_out += uDoCopy; pfile_in_zip_read_info->stream.next_in += uDoCopy; pfile_in_zip_read_info->stream.total_out += uDoCopy; iRead += uDoCopy; } else if (pfile_in_zip_read_info->compression_method==Z_BZIP2ED) { #ifdef HAVE_BZIP2 uLong uTotalOutBefore,uTotalOutAfter; const Bytef *bufBefore; uLong uOutThis; pfile_in_zip_read_info->bstream.next_in = (char*)pfile_in_zip_read_info->stream.next_in; pfile_in_zip_read_info->bstream.avail_in = pfile_in_zip_read_info->stream.avail_in; pfile_in_zip_read_info->bstream.total_in_lo32 = pfile_in_zip_read_info->stream.total_in; pfile_in_zip_read_info->bstream.total_in_hi32 = 0; pfile_in_zip_read_info->bstream.next_out = (char*)pfile_in_zip_read_info->stream.next_out; pfile_in_zip_read_info->bstream.avail_out = pfile_in_zip_read_info->stream.avail_out; pfile_in_zip_read_info->bstream.total_out_lo32 = pfile_in_zip_read_info->stream.total_out; pfile_in_zip_read_info->bstream.total_out_hi32 = 0; uTotalOutBefore = pfile_in_zip_read_info->bstream.total_out_lo32; bufBefore = (const Bytef *)pfile_in_zip_read_info->bstream.next_out; err=BZ2_bzDecompress(&pfile_in_zip_read_info->bstream); uTotalOutAfter = pfile_in_zip_read_info->bstream.total_out_lo32; uOutThis = uTotalOutAfter-uTotalOutBefore; pfile_in_zip_read_info->total_out_64 = pfile_in_zip_read_info->total_out_64 + uOutThis; pfile_in_zip_read_info->crc32 = crc32(pfile_in_zip_read_info->crc32,bufBefore, (uInt)(uOutThis)); pfile_in_zip_read_info->rest_read_uncompressed -= uOutThis; iRead += (uInt)(uTotalOutAfter - uTotalOutBefore); pfile_in_zip_read_info->stream.next_in = (Bytef*)pfile_in_zip_read_info->bstream.next_in; pfile_in_zip_read_info->stream.avail_in = pfile_in_zip_read_info->bstream.avail_in; pfile_in_zip_read_info->stream.total_in = pfile_in_zip_read_info->bstream.total_in_lo32; pfile_in_zip_read_info->stream.next_out = (Bytef*)pfile_in_zip_read_info->bstream.next_out; pfile_in_zip_read_info->stream.avail_out = pfile_in_zip_read_info->bstream.avail_out; pfile_in_zip_read_info->stream.total_out = pfile_in_zip_read_info->bstream.total_out_lo32; if (err==BZ_STREAM_END) return (iRead==0) ? UNZ_EOF : iRead; if (err!=BZ_OK) break; #endif } /* end Z_BZIP2ED */ else { ZPOS64_T uTotalOutBefore,uTotalOutAfter; const Bytef *bufBefore; ZPOS64_T uOutThis; int flush=Z_SYNC_FLUSH; uTotalOutBefore = pfile_in_zip_read_info->stream.total_out; bufBefore = pfile_in_zip_read_info->stream.next_out; /* if ((pfile_in_zip_read_info->rest_read_uncompressed == pfile_in_zip_read_info->stream.avail_out) && (pfile_in_zip_read_info->rest_read_compressed == 0)) flush = Z_FINISH; */ err=inflate(&pfile_in_zip_read_info->stream,flush); if ((err>=0) && (pfile_in_zip_read_info->stream.msg!=NULL)) err = Z_DATA_ERROR; uTotalOutAfter = pfile_in_zip_read_info->stream.total_out; uOutThis = uTotalOutAfter-uTotalOutBefore; pfile_in_zip_read_info->total_out_64 = pfile_in_zip_read_info->total_out_64 + uOutThis; pfile_in_zip_read_info->crc32 = crc32(pfile_in_zip_read_info->crc32,bufBefore, (uInt)(uOutThis)); pfile_in_zip_read_info->rest_read_uncompressed -= uOutThis; iRead += (uInt)(uTotalOutAfter - uTotalOutBefore); if (err==Z_STREAM_END) return (iRead==0) ? UNZ_EOF : iRead; if (err!=Z_OK) break; } } if (err==Z_OK) return iRead; return err; } /* Give the current position in uncompressed data */ extern z_off_t ZEXPORT unztell (unzFile file) { unz64_s* s; file_in_zip64_read_info_s* pfile_in_zip_read_info; if (file==NULL) return UNZ_PARAMERROR; s=(unz64_s*)file; pfile_in_zip_read_info=s->pfile_in_zip_read; if (pfile_in_zip_read_info==NULL) return UNZ_PARAMERROR; return (z_off_t)pfile_in_zip_read_info->stream.total_out; } extern ZPOS64_T ZEXPORT unztell64 (unzFile file) { unz64_s* s; file_in_zip64_read_info_s* pfile_in_zip_read_info; if (file==NULL) return (ZPOS64_T)-1; s=(unz64_s*)file; pfile_in_zip_read_info=s->pfile_in_zip_read; if (pfile_in_zip_read_info==NULL) return (ZPOS64_T)-1; return pfile_in_zip_read_info->total_out_64; } /* return 1 if the end of file was reached, 0 elsewhere */ extern int ZEXPORT unzeof (unzFile file) { unz64_s* s; file_in_zip64_read_info_s* pfile_in_zip_read_info; if (file==NULL) return UNZ_PARAMERROR; s=(unz64_s*)file; pfile_in_zip_read_info=s->pfile_in_zip_read; if (pfile_in_zip_read_info==NULL) return UNZ_PARAMERROR; if (pfile_in_zip_read_info->rest_read_uncompressed == 0) return 1; else return 0; } /* Read extra field from the current file (opened by unzOpenCurrentFile) This is the local-header version of the extra field (sometimes, there is more info in the local-header version than in the central-header) if buf==NULL, it return the size of the local extra field that can be read if buf!=NULL, len is the size of the buffer, the extra header is copied in buf. the return value is the number of bytes copied in buf, or (if <0) the error code */ extern int ZEXPORT unzGetLocalExtrafield (unzFile file, voidp buf, unsigned len) { unz64_s* s; file_in_zip64_read_info_s* pfile_in_zip_read_info; uInt read_now; ZPOS64_T size_to_read; if (file==NULL) return UNZ_PARAMERROR; s=(unz64_s*)file; pfile_in_zip_read_info=s->pfile_in_zip_read; if (pfile_in_zip_read_info==NULL) return UNZ_PARAMERROR; size_to_read = (pfile_in_zip_read_info->size_local_extrafield - pfile_in_zip_read_info->pos_local_extrafield); if (buf==NULL) return (int)size_to_read; if (len>size_to_read) read_now = (uInt)size_to_read; else read_now = (uInt)len ; if (read_now==0) return 0; if (ZSEEK64(pfile_in_zip_read_info->z_filefunc, pfile_in_zip_read_info->filestream, pfile_in_zip_read_info->offset_local_extrafield + pfile_in_zip_read_info->pos_local_extrafield, ZLIB_FILEFUNC_SEEK_SET)!=0) return UNZ_ERRNO; if (ZREAD64(pfile_in_zip_read_info->z_filefunc, pfile_in_zip_read_info->filestream, buf,read_now)!=read_now) return UNZ_ERRNO; return (int)read_now; } /* Close the file in zip opened with unzipOpenCurrentFile Return UNZ_CRCERROR if all the file was read but the CRC is not good */ extern int ZEXPORT unzCloseCurrentFile (unzFile file) { int err=UNZ_OK; unz64_s* s; file_in_zip64_read_info_s* pfile_in_zip_read_info; if (file==NULL) return UNZ_PARAMERROR; s=(unz64_s*)file; pfile_in_zip_read_info=s->pfile_in_zip_read; if (pfile_in_zip_read_info==NULL) return UNZ_PARAMERROR; if ((pfile_in_zip_read_info->rest_read_uncompressed == 0) && (!pfile_in_zip_read_info->raw)) { if (pfile_in_zip_read_info->crc32 != pfile_in_zip_read_info->crc32_wait) err=UNZ_CRCERROR; } TRYFREE(pfile_in_zip_read_info->read_buffer); pfile_in_zip_read_info->read_buffer = NULL; if (pfile_in_zip_read_info->stream_initialised == Z_DEFLATED) inflateEnd(&pfile_in_zip_read_info->stream); #ifdef HAVE_BZIP2 else if (pfile_in_zip_read_info->stream_initialised == Z_BZIP2ED) BZ2_bzDecompressEnd(&pfile_in_zip_read_info->bstream); #endif pfile_in_zip_read_info->stream_initialised = 0; TRYFREE(pfile_in_zip_read_info); s->pfile_in_zip_read=NULL; return err; } /* Get the global comment string of the ZipFile, in the szComment buffer. uSizeBuf is the size of the szComment buffer. return the number of byte copied or an error code <0 */ extern int ZEXPORT unzGetGlobalComment (unzFile file, char * szComment, uLong uSizeBuf) { unz64_s* s; uLong uReadThis ; if (file==NULL) return (int)UNZ_PARAMERROR; s=(unz64_s*)file; uReadThis = uSizeBuf; if (uReadThis>s->gi.size_comment) uReadThis = s->gi.size_comment; if (ZSEEK64(s->z_filefunc,s->filestream,s->central_pos+22,ZLIB_FILEFUNC_SEEK_SET)!=0) return UNZ_ERRNO; if (uReadThis>0) { *szComment='\0'; if (ZREAD64(s->z_filefunc,s->filestream,szComment,uReadThis)!=uReadThis) return UNZ_ERRNO; } if ((szComment != NULL) && (uSizeBuf > s->gi.size_comment)) *(szComment+s->gi.size_comment)='\0'; return (int)uReadThis; } /* Additions by RX '2004 */ extern ZPOS64_T ZEXPORT unzGetOffset64(unzFile file) { unz64_s* s; if (file==NULL) return 0; /*UNZ_PARAMERROR; */ s=(unz64_s*)file; if (!s->current_file_ok) return 0; if (s->gi.number_entry != 0 && s->gi.number_entry != 0xffff) if (s->num_file==s->gi.number_entry) return 0; return s->pos_in_central_dir; } extern uLong ZEXPORT unzGetOffset (unzFile file) { ZPOS64_T offset64; if (file==NULL) return 0; /*UNZ_PARAMERROR; */ offset64 = unzGetOffset64(file); return (uLong)offset64; } extern int ZEXPORT unzSetOffset64(unzFile file, ZPOS64_T pos) { unz64_s* s; int err; if (file==NULL) return UNZ_PARAMERROR; s=(unz64_s*)file; s->pos_in_central_dir = pos; s->num_file = s->gi.number_entry; /* hack */ err = unz64local_GetCurrentFileInfoInternal(file,&s->cur_file_info, &s->cur_file_info_internal, NULL,0,NULL,0,NULL,0); s->current_file_ok = (err == UNZ_OK); return err; } extern int ZEXPORT unzSetOffset (unzFile file, uLong pos) { return unzSetOffset64(file,pos); } connectome-workbench-1.2.3+git41-gc4c6c90/src/Quazip/unzip.h000066400000000000000000000406071300200146000233420ustar00rootroot00000000000000/* unzip.h -- IO for uncompress .zip files using zlib Version 1.1, February 14h, 2010 part of the MiniZip project - ( http://www.winimage.com/zLibDll/minizip.html ) Copyright (C) 1998-2010 Gilles Vollant (minizip) ( http://www.winimage.com/zLibDll/minizip.html ) Modifications of Unzip for Zip64 Copyright (C) 2007-2008 Even Rouault Modifications for Zip64 support on both zip and unzip Copyright (C) 2009-2010 Mathias Svensson ( http://result42.com ) For more info read MiniZip_info.txt --------------------------------------------------------------------------------- Condition of use and distribution are the same than zlib : This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this software. Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. 3. This notice may not be removed or altered from any source distribution. --------------------------------------------------------------------------------- Changes See header of unzip64.c */ #ifndef _unz64_H #define _unz64_H #ifdef __cplusplus extern "C" { #endif #ifndef _ZLIB_H #include "zlib.h" #endif #ifndef _ZLIBIOAPI_H #include "ioapi.h" #endif #ifdef HAVE_BZIP2 #include "bzlib.h" #endif #define Z_BZIP2ED 12 #if defined(STRICTUNZIP) || defined(STRICTZIPUNZIP) /* like the STRICT of WIN32, we define a pointer that cannot be converted from (void*) without cast */ typedef struct TagunzFile__ { int unused; } unzFile__; typedef unzFile__ *unzFile; #else typedef voidp unzFile; #endif #define UNZ_OK (0) #define UNZ_END_OF_LIST_OF_FILE (-100) #define UNZ_ERRNO (Z_ERRNO) #define UNZ_EOF (0) #define UNZ_PARAMERROR (-102) #define UNZ_BADZIPFILE (-103) #define UNZ_INTERNALERROR (-104) #define UNZ_CRCERROR (-105) /* tm_unz contain date/time info */ typedef struct tm_unz_s { uInt tm_sec; /* seconds after the minute - [0,59] */ uInt tm_min; /* minutes after the hour - [0,59] */ uInt tm_hour; /* hours since midnight - [0,23] */ uInt tm_mday; /* day of the month - [1,31] */ uInt tm_mon; /* months since January - [0,11] */ uInt tm_year; /* years - [1980..2044] */ } tm_unz; /* unz_global_info structure contain global data about the ZIPfile These data comes from the end of central dir */ typedef struct unz_global_info64_s { ZPOS64_T number_entry; /* total number of entries in the central dir on this disk */ uLong size_comment; /* size of the global comment of the zipfile */ } unz_global_info64; typedef struct unz_global_info_s { uLong number_entry; /* total number of entries in the central dir on this disk */ uLong size_comment; /* size of the global comment of the zipfile */ } unz_global_info; /* unz_file_info contain information about a file in the zipfile */ typedef struct unz_file_info64_s { uLong version; /* version made by 2 bytes */ uLong version_needed; /* version needed to extract 2 bytes */ uLong flag; /* general purpose bit flag 2 bytes */ uLong compression_method; /* compression method 2 bytes */ uLong dosDate; /* last mod file date in Dos fmt 4 bytes */ uLong crc; /* crc-32 4 bytes */ ZPOS64_T compressed_size; /* compressed size 8 bytes */ ZPOS64_T uncompressed_size; /* uncompressed size 8 bytes */ uLong size_filename; /* filename length 2 bytes */ uLong size_file_extra; /* extra field length 2 bytes */ uLong size_file_comment; /* file comment length 2 bytes */ uLong disk_num_start; /* disk number start 2 bytes */ uLong internal_fa; /* internal file attributes 2 bytes */ uLong external_fa; /* external file attributes 4 bytes */ tm_unz tmu_date; } unz_file_info64; typedef struct unz_file_info_s { uLong version; /* version made by 2 bytes */ uLong version_needed; /* version needed to extract 2 bytes */ uLong flag; /* general purpose bit flag 2 bytes */ uLong compression_method; /* compression method 2 bytes */ uLong dosDate; /* last mod file date in Dos fmt 4 bytes */ uLong crc; /* crc-32 4 bytes */ uLong compressed_size; /* compressed size 4 bytes */ uLong uncompressed_size; /* uncompressed size 4 bytes */ uLong size_filename; /* filename length 2 bytes */ uLong size_file_extra; /* extra field length 2 bytes */ uLong size_file_comment; /* file comment length 2 bytes */ uLong disk_num_start; /* disk number start 2 bytes */ uLong internal_fa; /* internal file attributes 2 bytes */ uLong external_fa; /* external file attributes 4 bytes */ tm_unz tmu_date; } unz_file_info; extern int ZEXPORT unzStringFileNameCompare OF ((const char* fileName1, const char* fileName2, int iCaseSensitivity)); /* Compare two filename (fileName1,fileName2). If iCaseSenisivity = 1, comparision is case sensitivity (like strcmp) If iCaseSenisivity = 2, comparision is not case sensitivity (like strcmpi or strcasecmp) If iCaseSenisivity = 0, case sensitivity is defaut of your operating system (like 1 on Unix, 2 on Windows) */ extern unzFile ZEXPORT unzOpen OF((voidpf file)); extern unzFile ZEXPORT unzOpen64 OF((voidpf file)); /* Open a Zip file. path contain the full pathname (by example, on a Windows XP computer "c:\\zlib\\zlib113.zip" or on an Unix computer "zlib/zlib113.zip". If the zipfile cannot be opened (file don't exist or in not valid), the return value is NULL. Else, the return value is a unzFile Handle, usable with other function of this unzip package. the "64" function take a const void* pointer, because the path is just the value passed to the open64_file_func callback. Under Windows, if UNICODE is defined, using fill_fopen64_filefunc, the path is a pointer to a wide unicode string (LPCTSTR is LPCWSTR), so const char* does not describe the reality */ extern unzFile ZEXPORT unzOpen2 OF((voidpf file, zlib_filefunc_def* pzlib_filefunc_def)); /* Open a Zip file, like unzOpen, but provide a set of file low level API for read/write the zip file (see ioapi.h) */ extern unzFile ZEXPORT unzOpen2_64 OF((voidpf file, zlib_filefunc64_def* pzlib_filefunc_def)); /* Open a Zip file, like unz64Open, but provide a set of file low level API for read/write the zip file (see ioapi.h) */ extern int ZEXPORT unzClose OF((unzFile file)); /* Close a ZipFile opened with unzipOpen. If there is files inside the .Zip opened with unzOpenCurrentFile (see later), these files MUST be closed with unzipCloseCurrentFile before call unzipClose. return UNZ_OK if there is no problem. */ extern int ZEXPORT unzGetGlobalInfo OF((unzFile file, unz_global_info *pglobal_info)); extern int ZEXPORT unzGetGlobalInfo64 OF((unzFile file, unz_global_info64 *pglobal_info)); /* Write info about the ZipFile in the *pglobal_info structure. No preparation of the structure is needed return UNZ_OK if there is no problem. */ extern int ZEXPORT unzGetGlobalComment OF((unzFile file, char *szComment, uLong uSizeBuf)); /* Get the global comment string of the ZipFile, in the szComment buffer. uSizeBuf is the size of the szComment buffer. return the number of byte copied or an error code <0 */ /***************************************************************************/ /* Unzip package allow you browse the directory of the zipfile */ extern int ZEXPORT unzGoToFirstFile OF((unzFile file)); /* Set the current file of the zipfile to the first file. return UNZ_OK if there is no problem */ extern int ZEXPORT unzGoToNextFile OF((unzFile file)); /* Set the current file of the zipfile to the next file. return UNZ_OK if there is no problem return UNZ_END_OF_LIST_OF_FILE if the actual file was the latest. */ extern int ZEXPORT unzLocateFile OF((unzFile file, const char *szFileName, int iCaseSensitivity)); /* Try locate the file szFileName in the zipfile. For the iCaseSensitivity signification, see unzStringFileNameCompare return value : UNZ_OK if the file is found. It becomes the current file. UNZ_END_OF_LIST_OF_FILE if the file is not found */ /* ****************************************** */ /* Ryan supplied functions */ /* unz_file_info contain information about a file in the zipfile */ typedef struct unz_file_pos_s { uLong pos_in_zip_directory; /* offset in zip file directory */ uLong num_of_file; /* # of file */ } unz_file_pos; extern int ZEXPORT unzGetFilePos( unzFile file, unz_file_pos* file_pos); extern int ZEXPORT unzGoToFilePos( unzFile file, unz_file_pos* file_pos); typedef struct unz64_file_pos_s { ZPOS64_T pos_in_zip_directory; /* offset in zip file directory */ ZPOS64_T num_of_file; /* # of file */ } unz64_file_pos; extern int ZEXPORT unzGetFilePos64( unzFile file, unz64_file_pos* file_pos); extern int ZEXPORT unzGoToFilePos64( unzFile file, const unz64_file_pos* file_pos); /* ****************************************** */ extern int ZEXPORT unzGetCurrentFileInfo64 OF((unzFile file, unz_file_info64 *pfile_info, char *szFileName, uLong fileNameBufferSize, void *extraField, uLong extraFieldBufferSize, char *szComment, uLong commentBufferSize)); extern int ZEXPORT unzGetCurrentFileInfo OF((unzFile file, unz_file_info *pfile_info, char *szFileName, uLong fileNameBufferSize, void *extraField, uLong extraFieldBufferSize, char *szComment, uLong commentBufferSize)); /* Get Info about the current file if pfile_info!=NULL, the *pfile_info structure will contain somes info about the current file if szFileName!=NULL, the filemane string will be copied in szFileName (fileNameBufferSize is the size of the buffer) if extraField!=NULL, the extra field information will be copied in extraField (extraFieldBufferSize is the size of the buffer). This is the Central-header version of the extra field if szComment!=NULL, the comment string of the file will be copied in szComment (commentBufferSize is the size of the buffer) */ /** Addition for GDAL : START */ extern ZPOS64_T ZEXPORT unzGetCurrentFileZStreamPos64 OF((unzFile file)); /** Addition for GDAL : END */ /***************************************************************************/ /* for reading the content of the current zipfile, you can open it, read data from it, and close it (you can close it before reading all the file) */ extern int ZEXPORT unzOpenCurrentFile OF((unzFile file)); /* Open for reading data the current file in the zipfile. If there is no error, the return value is UNZ_OK. */ extern int ZEXPORT unzOpenCurrentFilePassword OF((unzFile file, const char* password)); /* Open for reading data the current file in the zipfile. password is a crypting password If there is no error, the return value is UNZ_OK. */ extern int ZEXPORT unzOpenCurrentFile2 OF((unzFile file, int* method, int* level, int raw)); /* Same than unzOpenCurrentFile, but open for read raw the file (not uncompress) if raw==1 *method will receive method of compression, *level will receive level of compression note : you can set level parameter as NULL (if you did not want known level, but you CANNOT set method parameter as NULL */ extern int ZEXPORT unzOpenCurrentFile3 OF((unzFile file, int* method, int* level, int raw, const char* password)); /* Same than unzOpenCurrentFile, but open for read raw the file (not uncompress) if raw==1 *method will receive method of compression, *level will receive level of compression note : you can set level parameter as NULL (if you did not want known level, but you CANNOT set method parameter as NULL */ extern int ZEXPORT unzCloseCurrentFile OF((unzFile file)); /* Close the file in zip opened with unzOpenCurrentFile Return UNZ_CRCERROR if all the file was read but the CRC is not good */ extern int ZEXPORT unzReadCurrentFile OF((unzFile file, voidp buf, unsigned len)); /* Read bytes from the current file (opened by unzOpenCurrentFile) buf contain buffer where data must be copied len the size of buf. return the number of byte copied if somes bytes are copied return 0 if the end of file was reached return <0 with error code if there is an error (UNZ_ERRNO for IO error, or zLib error for uncompress error) */ extern z_off_t ZEXPORT unztell OF((unzFile file)); extern ZPOS64_T ZEXPORT unztell64 OF((unzFile file)); /* Give the current position in uncompressed data */ extern int ZEXPORT unzeof OF((unzFile file)); /* return 1 if the end of file was reached, 0 elsewhere */ extern int ZEXPORT unzGetLocalExtrafield OF((unzFile file, voidp buf, unsigned len)); /* Read extra field from the current file (opened by unzOpenCurrentFile) This is the local-header version of the extra field (sometimes, there is more info in the local-header version than in the central-header) if buf==NULL, it return the size of the local extra field if buf!=NULL, len is the size of the buffer, the extra header is copied in buf. the return value is the number of bytes copied in buf, or (if <0) the error code */ /***************************************************************************/ /* Get the current file offset */ extern ZPOS64_T ZEXPORT unzGetOffset64 (unzFile file); extern uLong ZEXPORT unzGetOffset (unzFile file); /* Set the current file offset */ extern int ZEXPORT unzSetOffset64 (unzFile file, ZPOS64_T pos); extern int ZEXPORT unzSetOffset (unzFile file, uLong pos); #ifdef __cplusplus } #endif #endif /* _unz64_H */ connectome-workbench-1.2.3+git41-gc4c6c90/src/Quazip/zip.c000066400000000000000000002111131300200146000227620ustar00rootroot00000000000000/* zip.c -- IO on .zip files using zlib Version 1.1, February 14h, 2010 part of the MiniZip project - ( http://www.winimage.com/zLibDll/minizip.html ) Copyright (C) 1998-2010 Gilles Vollant (minizip) ( http://www.winimage.com/zLibDll/minizip.html ) Modifications for Zip64 support Copyright (C) 2009-2010 Mathias Svensson ( http://result42.com ) For more info read MiniZip_info.txt Changes Oct-2009 - Mathias Svensson - Remove old C style function prototypes Oct-2009 - Mathias Svensson - Added Zip64 Support when creating new file archives Oct-2009 - Mathias Svensson - Did some code cleanup and refactoring to get better overview of some functions. Oct-2009 - Mathias Svensson - Added zipRemoveExtraInfoBlock to strip extra field data from its ZIP64 data It is used when recreting zip archive with RAW when deleting items from a zip. ZIP64 data is automaticly added to items that needs it, and existing ZIP64 data need to be removed. Oct-2009 - Mathias Svensson - Added support for BZIP2 as compression mode (bzip2 lib is required) Jan-2010 - back to unzip and minizip 1.0 name scheme, with compatibility layer */ #include #include #include #include #include "zlib.h" #if (ZLIB_VERNUM < 0x1270) typedef uLongf z_crc_t; #endif #include "zip.h" #ifdef STDC # include # include # include #endif #ifdef NO_ERRNO_H extern int errno; #else # include #endif #ifndef local # define local static #endif /* compile with -Dlocal if your debugger can't find static symbols */ #ifndef VERSIONMADEBY # define VERSIONMADEBY (0x031e) /* best for standard pkware crypt */ #endif #ifndef Z_BUFSIZE #define Z_BUFSIZE (64*1024) /* (16384) */ #endif #ifndef Z_MAXFILENAMEINZIP #define Z_MAXFILENAMEINZIP (256) #endif #ifndef ALLOC # define ALLOC(size) (malloc(size)) #endif #ifndef TRYFREE # define TRYFREE(p) {if (p) free(p);} #endif /* #define SIZECENTRALDIRITEM (0x2e) #define SIZEZIPLOCALHEADER (0x1e) */ /* I've found an old Unix (a SunOS 4.1.3_U1) without all SEEK_* defined.... */ /* NOT sure that this work on ALL platform */ #define MAKEULONG64(a, b) ((ZPOS64_T)(((unsigned long)(a)) | ((ZPOS64_T)((unsigned long)(b))) << 32)) #ifndef SEEK_CUR #define SEEK_CUR 1 #endif #ifndef SEEK_END #define SEEK_END 2 #endif #ifndef SEEK_SET #define SEEK_SET 0 #endif #ifndef DEF_MEM_LEVEL #if MAX_MEM_LEVEL >= 8 # define DEF_MEM_LEVEL 8 #else # define DEF_MEM_LEVEL MAX_MEM_LEVEL #endif #endif const char zip_copyright[] =" zip 1.01 Copyright 1998-2004 Gilles Vollant - http://www.winimage.com/zLibDll"; #define SIZEDATA_INDATABLOCK (4096-(4*4)) #define LOCALHEADERMAGIC (0x04034b50) #define DESCRIPTORHEADERMAGIC (0x08074b50) #define CENTRALHEADERMAGIC (0x02014b50) #define ENDHEADERMAGIC (0x06054b50) #define ZIP64ENDHEADERMAGIC (0x6064b50) #define ZIP64ENDLOCHEADERMAGIC (0x7064b50) #define FLAG_LOCALHEADER_OFFSET (0x06) #define CRC_LOCALHEADER_OFFSET (0x0e) #define SIZECENTRALHEADER (0x2e) /* 46 */ typedef struct linkedlist_datablock_internal_s { struct linkedlist_datablock_internal_s* next_datablock; uLong avail_in_this_block; uLong filled_in_this_block; uLong unused; /* for future use and alignement */ unsigned char data[SIZEDATA_INDATABLOCK]; } linkedlist_datablock_internal; typedef struct linkedlist_data_s { linkedlist_datablock_internal* first_block; linkedlist_datablock_internal* last_block; } linkedlist_data; typedef struct { z_stream stream; /* zLib stream structure for inflate */ #ifdef HAVE_BZIP2 bz_stream bstream; /* bzLib stream structure for bziped */ #endif int stream_initialised; /* 1 is stream is initialised */ uInt pos_in_buffered_data; /* last written byte in buffered_data */ ZPOS64_T pos_local_header; /* offset of the local header of the file currenty writing */ char* central_header; /* central header data for the current file */ uLong size_centralExtra; uLong size_centralheader; /* size of the central header for cur file */ uLong size_centralExtraFree; /* Extra bytes allocated to the centralheader but that are not used */ uLong flag; /* flag of the file currently writing */ int method; /* compression method of file currenty wr.*/ int raw; /* 1 for directly writing raw data */ Byte buffered_data[Z_BUFSIZE];/* buffer contain compressed data to be writ*/ uLong dosDate; uLong crc32; int encrypt; int zip64; /* Add ZIP64 extened information in the extra field */ ZPOS64_T pos_zip64extrainfo; ZPOS64_T totalCompressedData; ZPOS64_T totalUncompressedData; #ifndef NOCRYPT unsigned long keys[3]; /* keys defining the pseudo-random sequence */ const z_crc_t FAR * pcrc_32_tab; int crypt_header_size; #endif } curfile64_info; typedef struct { zlib_filefunc64_32_def z_filefunc; voidpf filestream; /* io structore of the zipfile */ linkedlist_data central_dir;/* datablock with central dir in construction*/ int in_opened_file_inzip; /* 1 if a file in the zip is currently writ.*/ curfile64_info ci; /* info on the file curretly writing */ ZPOS64_T begin_pos; /* position of the beginning of the zipfile */ ZPOS64_T add_position_when_writting_offset; ZPOS64_T number_entry; #ifndef NO_ADDFILEINEXISTINGZIP char *globalcomment; #endif unsigned flags; } zip64_internal; #ifndef NOCRYPT #define INCLUDECRYPTINGCODE_IFCRYPTALLOWED #include "crypt.h" #endif local linkedlist_datablock_internal* allocate_new_datablock() { linkedlist_datablock_internal* ldi; ldi = (linkedlist_datablock_internal*) ALLOC(sizeof(linkedlist_datablock_internal)); if (ldi!=NULL) { ldi->next_datablock = NULL ; ldi->filled_in_this_block = 0 ; ldi->avail_in_this_block = SIZEDATA_INDATABLOCK ; } return ldi; } local void free_datablock(linkedlist_datablock_internal* ldi) { while (ldi!=NULL) { linkedlist_datablock_internal* ldinext = ldi->next_datablock; TRYFREE(ldi); ldi = ldinext; } } local void init_linkedlist(linkedlist_data* ll) { ll->first_block = ll->last_block = NULL; } local void free_linkedlist(linkedlist_data* ll) { free_datablock(ll->first_block); ll->first_block = ll->last_block = NULL; } local int add_data_in_datablock(linkedlist_data* ll, const void* buf, uLong len) { linkedlist_datablock_internal* ldi; const unsigned char* from_copy; if (ll==NULL) return ZIP_INTERNALERROR; if (ll->last_block == NULL) { ll->first_block = ll->last_block = allocate_new_datablock(); if (ll->first_block == NULL) return ZIP_INTERNALERROR; } ldi = ll->last_block; from_copy = (unsigned char*)buf; while (len>0) { uInt copy_this; uInt i; unsigned char* to_copy; if (ldi->avail_in_this_block==0) { ldi->next_datablock = allocate_new_datablock(); if (ldi->next_datablock == NULL) return ZIP_INTERNALERROR; ldi = ldi->next_datablock ; ll->last_block = ldi; } if (ldi->avail_in_this_block < len) copy_this = (uInt)ldi->avail_in_this_block; else copy_this = (uInt)len; to_copy = &(ldi->data[ldi->filled_in_this_block]); for (i=0;ifilled_in_this_block += copy_this; ldi->avail_in_this_block -= copy_this; from_copy += copy_this ; len -= copy_this; } return ZIP_OK; } /****************************************************************************/ #ifndef NO_ADDFILEINEXISTINGZIP /* =========================================================================== Inputs a long in LSB order to the given file nbByte == 1, 2 ,4 or 8 (byte, short or long, ZPOS64_T) */ local int zip64local_putValue OF((const zlib_filefunc64_32_def* pzlib_filefunc_def, voidpf filestream, ZPOS64_T x, int nbByte)); local int zip64local_putValue (const zlib_filefunc64_32_def* pzlib_filefunc_def, voidpf filestream, ZPOS64_T x, int nbByte) { unsigned char buf[8]; int n; for (n = 0; n < nbByte; n++) { buf[n] = (unsigned char)(x & 0xff); x >>= 8; } if (x != 0) { /* data overflow - hack for ZIP64 (X Roche) */ for (n = 0; n < nbByte; n++) { buf[n] = 0xff; } } if (ZWRITE64(*pzlib_filefunc_def,filestream,buf,nbByte)!=(uLong)nbByte) return ZIP_ERRNO; else return ZIP_OK; } local void zip64local_putValue_inmemory OF((void* dest, ZPOS64_T x, int nbByte)); local void zip64local_putValue_inmemory (void* dest, ZPOS64_T x, int nbByte) { unsigned char* buf=(unsigned char*)dest; int n; for (n = 0; n < nbByte; n++) { buf[n] = (unsigned char)(x & 0xff); x >>= 8; } if (x != 0) { /* data overflow - hack for ZIP64 */ for (n = 0; n < nbByte; n++) { buf[n] = 0xff; } } } /****************************************************************************/ local uLong zip64local_TmzDateToDosDate(const tm_zip* ptm) { uLong year = (uLong)ptm->tm_year; if (year>=1980) year-=1980; else if (year>=80) year-=80; return (uLong) (((ptm->tm_mday) + (32 * (ptm->tm_mon+1)) + (512 * year)) << 16) | ((ptm->tm_sec/2) + (32* ptm->tm_min) + (2048 * (uLong)ptm->tm_hour)); } /****************************************************************************/ local int zip64local_getByte OF((const zlib_filefunc64_32_def* pzlib_filefunc_def, voidpf filestream, int *pi)); local int zip64local_getByte(const zlib_filefunc64_32_def* pzlib_filefunc_def,voidpf filestream,int* pi) { unsigned char c; int err = (int)ZREAD64(*pzlib_filefunc_def,filestream,&c,1); if (err==1) { *pi = (int)c; return ZIP_OK; } else { if (ZERROR64(*pzlib_filefunc_def,filestream)) return ZIP_ERRNO; else return ZIP_EOF; } } /* =========================================================================== Reads a long in LSB order from the given gz_stream. Sets */ local int zip64local_getShort OF((const zlib_filefunc64_32_def* pzlib_filefunc_def, voidpf filestream, uLong *pX)); local int zip64local_getShort (const zlib_filefunc64_32_def* pzlib_filefunc_def, voidpf filestream, uLong* pX) { uLong x ; int i = 0; int err; err = zip64local_getByte(pzlib_filefunc_def,filestream,&i); x = (uLong)i; if (err==ZIP_OK) err = zip64local_getByte(pzlib_filefunc_def,filestream,&i); x += ((uLong)i)<<8; if (err==ZIP_OK) *pX = x; else *pX = 0; return err; } local int zip64local_getLong OF((const zlib_filefunc64_32_def* pzlib_filefunc_def, voidpf filestream, uLong *pX)); local int zip64local_getLong (const zlib_filefunc64_32_def* pzlib_filefunc_def, voidpf filestream, uLong* pX) { uLong x ; int i = 0; int err; err = zip64local_getByte(pzlib_filefunc_def,filestream,&i); x = (uLong)i; if (err==ZIP_OK) err = zip64local_getByte(pzlib_filefunc_def,filestream,&i); x += ((uLong)i)<<8; if (err==ZIP_OK) err = zip64local_getByte(pzlib_filefunc_def,filestream,&i); x += ((uLong)i)<<16; if (err==ZIP_OK) err = zip64local_getByte(pzlib_filefunc_def,filestream,&i); x += ((uLong)i)<<24; if (err==ZIP_OK) *pX = x; else *pX = 0; return err; } local int zip64local_getLong64 OF((const zlib_filefunc64_32_def* pzlib_filefunc_def, voidpf filestream, ZPOS64_T *pX)); local int zip64local_getLong64 (const zlib_filefunc64_32_def* pzlib_filefunc_def, voidpf filestream, ZPOS64_T *pX) { ZPOS64_T x; int i = 0; int err; err = zip64local_getByte(pzlib_filefunc_def,filestream,&i); x = (ZPOS64_T)i; if (err==ZIP_OK) err = zip64local_getByte(pzlib_filefunc_def,filestream,&i); x += ((ZPOS64_T)i)<<8; if (err==ZIP_OK) err = zip64local_getByte(pzlib_filefunc_def,filestream,&i); x += ((ZPOS64_T)i)<<16; if (err==ZIP_OK) err = zip64local_getByte(pzlib_filefunc_def,filestream,&i); x += ((ZPOS64_T)i)<<24; if (err==ZIP_OK) err = zip64local_getByte(pzlib_filefunc_def,filestream,&i); x += ((ZPOS64_T)i)<<32; if (err==ZIP_OK) err = zip64local_getByte(pzlib_filefunc_def,filestream,&i); x += ((ZPOS64_T)i)<<40; if (err==ZIP_OK) err = zip64local_getByte(pzlib_filefunc_def,filestream,&i); x += ((ZPOS64_T)i)<<48; if (err==ZIP_OK) err = zip64local_getByte(pzlib_filefunc_def,filestream,&i); x += ((ZPOS64_T)i)<<56; if (err==ZIP_OK) *pX = x; else *pX = 0; return err; } #ifndef BUFREADCOMMENT #define BUFREADCOMMENT (0x400) #endif /* Locate the Central directory of a zipfile (at the end, just before the global comment) */ local ZPOS64_T zip64local_SearchCentralDir OF((const zlib_filefunc64_32_def* pzlib_filefunc_def, voidpf filestream)); local ZPOS64_T zip64local_SearchCentralDir(const zlib_filefunc64_32_def* pzlib_filefunc_def, voidpf filestream) { unsigned char* buf; ZPOS64_T uSizeFile; ZPOS64_T uBackRead; ZPOS64_T uMaxBack=0xffff; /* maximum size of global comment */ ZPOS64_T uPosFound=0; if (ZSEEK64(*pzlib_filefunc_def,filestream,0,ZLIB_FILEFUNC_SEEK_END) != 0) return 0; uSizeFile = ZTELL64(*pzlib_filefunc_def,filestream); if (uMaxBack>uSizeFile) uMaxBack = uSizeFile; buf = (unsigned char*)ALLOC(BUFREADCOMMENT+4); if (buf==NULL) return 0; uBackRead = 4; while (uBackReaduMaxBack) uBackRead = uMaxBack; else uBackRead+=BUFREADCOMMENT; uReadPos = uSizeFile-uBackRead ; uReadSize = ((BUFREADCOMMENT+4) < (uSizeFile-uReadPos)) ? (BUFREADCOMMENT+4) : (uLong)(uSizeFile-uReadPos); if (ZSEEK64(*pzlib_filefunc_def,filestream,uReadPos,ZLIB_FILEFUNC_SEEK_SET)!=0) break; if (ZREAD64(*pzlib_filefunc_def,filestream,buf,uReadSize)!=uReadSize) break; for (i=(int)uReadSize-3; (i--)>0;) if (((*(buf+i))==0x50) && ((*(buf+i+1))==0x4b) && ((*(buf+i+2))==0x05) && ((*(buf+i+3))==0x06)) { uPosFound = uReadPos+i; break; } if (uPosFound!=0) break; } TRYFREE(buf); return uPosFound; } /* Locate the End of Zip64 Central directory locator and from there find the CD of a zipfile (at the end, just before the global comment) */ local ZPOS64_T zip64local_SearchCentralDir64 OF((const zlib_filefunc64_32_def* pzlib_filefunc_def, voidpf filestream)); local ZPOS64_T zip64local_SearchCentralDir64(const zlib_filefunc64_32_def* pzlib_filefunc_def, voidpf filestream) { unsigned char* buf; ZPOS64_T uSizeFile; ZPOS64_T uBackRead; ZPOS64_T uMaxBack=0xffff; /* maximum size of global comment */ ZPOS64_T uPosFound=0; uLong uL; ZPOS64_T relativeOffset; if (ZSEEK64(*pzlib_filefunc_def,filestream,0,ZLIB_FILEFUNC_SEEK_END) != 0) return 0; uSizeFile = ZTELL64(*pzlib_filefunc_def,filestream); if (uMaxBack>uSizeFile) uMaxBack = uSizeFile; buf = (unsigned char*)ALLOC(BUFREADCOMMENT+4); if (buf==NULL) return 0; uBackRead = 4; while (uBackReaduMaxBack) uBackRead = uMaxBack; else uBackRead+=BUFREADCOMMENT; uReadPos = uSizeFile-uBackRead ; uReadSize = ((BUFREADCOMMENT+4) < (uSizeFile-uReadPos)) ? (BUFREADCOMMENT+4) : (uLong)(uSizeFile-uReadPos); if (ZSEEK64(*pzlib_filefunc_def,filestream,uReadPos,ZLIB_FILEFUNC_SEEK_SET)!=0) break; if (ZREAD64(*pzlib_filefunc_def,filestream,buf,uReadSize)!=uReadSize) break; for (i=(int)uReadSize-3; (i--)>0;) { /* Signature "0x07064b50" Zip64 end of central directory locater */ if (((*(buf+i))==0x50) && ((*(buf+i+1))==0x4b) && ((*(buf+i+2))==0x06) && ((*(buf+i+3))==0x07)) { uPosFound = uReadPos+i; break; } } if (uPosFound!=0) break; } TRYFREE(buf); if (uPosFound == 0) return 0; /* Zip64 end of central directory locator */ if (ZSEEK64(*pzlib_filefunc_def,filestream, uPosFound,ZLIB_FILEFUNC_SEEK_SET)!=0) return 0; /* the signature, already checked */ if (zip64local_getLong(pzlib_filefunc_def,filestream,&uL)!=ZIP_OK) return 0; /* number of the disk with the start of the zip64 end of central directory */ if (zip64local_getLong(pzlib_filefunc_def,filestream,&uL)!=ZIP_OK) return 0; if (uL != 0) return 0; /* relative offset of the zip64 end of central directory record */ if (zip64local_getLong64(pzlib_filefunc_def,filestream,&relativeOffset)!=ZIP_OK) return 0; /* total number of disks */ if (zip64local_getLong(pzlib_filefunc_def,filestream,&uL)!=ZIP_OK) return 0; if (uL != 1) return 0; /* Goto Zip64 end of central directory record */ if (ZSEEK64(*pzlib_filefunc_def,filestream, relativeOffset,ZLIB_FILEFUNC_SEEK_SET)!=0) return 0; /* the signature */ if (zip64local_getLong(pzlib_filefunc_def,filestream,&uL)!=ZIP_OK) return 0; if (uL != 0x06064b50) /* signature of 'Zip64 end of central directory' */ return 0; return relativeOffset; } int LoadCentralDirectoryRecord(zip64_internal* pziinit) { int err=ZIP_OK; ZPOS64_T byte_before_the_zipfile;/* byte before the zipfile, (>0 for sfx)*/ ZPOS64_T size_central_dir; /* size of the central directory */ ZPOS64_T offset_central_dir; /* offset of start of central directory */ ZPOS64_T central_pos; uLong uL; uLong number_disk; /* number of the current dist, used for spaning ZIP, unsupported, always 0*/ uLong number_disk_with_CD; /* number the the disk with central dir, used for spaning ZIP, unsupported, always 0*/ ZPOS64_T number_entry; ZPOS64_T number_entry_CD; /* total number of entries in the central dir (same than number_entry on nospan) */ uLong VersionMadeBy; uLong VersionNeeded; uLong size_comment; int hasZIP64Record = 0; /* check first if we find a ZIP64 record */ central_pos = zip64local_SearchCentralDir64(&pziinit->z_filefunc,pziinit->filestream); if(central_pos > 0) { hasZIP64Record = 1; } else if(central_pos == 0) { central_pos = zip64local_SearchCentralDir(&pziinit->z_filefunc,pziinit->filestream); } /* disable to allow appending to empty ZIP archive if (central_pos==0) err=ZIP_ERRNO; */ if(hasZIP64Record) { ZPOS64_T sizeEndOfCentralDirectory; if (ZSEEK64(pziinit->z_filefunc, pziinit->filestream, central_pos, ZLIB_FILEFUNC_SEEK_SET) != 0) err=ZIP_ERRNO; /* the signature, already checked */ if (zip64local_getLong(&pziinit->z_filefunc, pziinit->filestream,&uL)!=ZIP_OK) err=ZIP_ERRNO; /* size of zip64 end of central directory record */ if (zip64local_getLong64(&pziinit->z_filefunc, pziinit->filestream, &sizeEndOfCentralDirectory)!=ZIP_OK) err=ZIP_ERRNO; /* version made by */ if (zip64local_getShort(&pziinit->z_filefunc, pziinit->filestream, &VersionMadeBy)!=ZIP_OK) err=ZIP_ERRNO; /* version needed to extract */ if (zip64local_getShort(&pziinit->z_filefunc, pziinit->filestream, &VersionNeeded)!=ZIP_OK) err=ZIP_ERRNO; /* number of this disk */ if (zip64local_getLong(&pziinit->z_filefunc, pziinit->filestream,&number_disk)!=ZIP_OK) err=ZIP_ERRNO; /* number of the disk with the start of the central directory */ if (zip64local_getLong(&pziinit->z_filefunc, pziinit->filestream,&number_disk_with_CD)!=ZIP_OK) err=ZIP_ERRNO; /* total number of entries in the central directory on this disk */ if (zip64local_getLong64(&pziinit->z_filefunc, pziinit->filestream, &number_entry)!=ZIP_OK) err=ZIP_ERRNO; /* total number of entries in the central directory */ if (zip64local_getLong64(&pziinit->z_filefunc, pziinit->filestream,&number_entry_CD)!=ZIP_OK) err=ZIP_ERRNO; if ((number_entry_CD!=number_entry) || (number_disk_with_CD!=0) || (number_disk!=0)) err=ZIP_BADZIPFILE; /* size of the central directory */ if (zip64local_getLong64(&pziinit->z_filefunc, pziinit->filestream,&size_central_dir)!=ZIP_OK) err=ZIP_ERRNO; /* offset of start of central directory with respect to the starting disk number */ if (zip64local_getLong64(&pziinit->z_filefunc, pziinit->filestream,&offset_central_dir)!=ZIP_OK) err=ZIP_ERRNO; /* TODO.. */ /* read the comment from the standard central header. */ size_comment = 0; } else { /* Read End of central Directory info */ if (ZSEEK64(pziinit->z_filefunc, pziinit->filestream, central_pos,ZLIB_FILEFUNC_SEEK_SET)!=0) err=ZIP_ERRNO; /* the signature, already checked */ if (zip64local_getLong(&pziinit->z_filefunc, pziinit->filestream,&uL)!=ZIP_OK) err=ZIP_ERRNO; /* number of this disk */ if (zip64local_getShort(&pziinit->z_filefunc, pziinit->filestream,&number_disk)!=ZIP_OK) err=ZIP_ERRNO; /* number of the disk with the start of the central directory */ if (zip64local_getShort(&pziinit->z_filefunc, pziinit->filestream,&number_disk_with_CD)!=ZIP_OK) err=ZIP_ERRNO; /* total number of entries in the central dir on this disk */ number_entry = 0; if (zip64local_getShort(&pziinit->z_filefunc, pziinit->filestream, &uL)!=ZIP_OK) err=ZIP_ERRNO; else number_entry = uL; /* total number of entries in the central dir */ number_entry_CD = 0; if (zip64local_getShort(&pziinit->z_filefunc, pziinit->filestream, &uL)!=ZIP_OK) err=ZIP_ERRNO; else number_entry_CD = uL; if ((number_entry_CD!=number_entry) || (number_disk_with_CD!=0) || (number_disk!=0)) err=ZIP_BADZIPFILE; /* size of the central directory */ size_central_dir = 0; if (zip64local_getLong(&pziinit->z_filefunc, pziinit->filestream, &uL)!=ZIP_OK) err=ZIP_ERRNO; else size_central_dir = uL; /* offset of start of central directory with respect to the starting disk number */ offset_central_dir = 0; if (zip64local_getLong(&pziinit->z_filefunc, pziinit->filestream, &uL)!=ZIP_OK) err=ZIP_ERRNO; else offset_central_dir = uL; /* zipfile global comment length */ if (zip64local_getShort(&pziinit->z_filefunc, pziinit->filestream, &size_comment)!=ZIP_OK) err=ZIP_ERRNO; } if ((central_posz_filefunc, pziinit->filestream); return ZIP_ERRNO; } if (size_comment>0) { pziinit->globalcomment = (char*)ALLOC(size_comment+1); if (pziinit->globalcomment) { size_comment = ZREAD64(pziinit->z_filefunc, pziinit->filestream, pziinit->globalcomment,size_comment); pziinit->globalcomment[size_comment]=0; } } byte_before_the_zipfile = central_pos - (offset_central_dir+size_central_dir); pziinit->add_position_when_writting_offset = byte_before_the_zipfile; { ZPOS64_T size_central_dir_to_read = size_central_dir; size_t buf_size = SIZEDATA_INDATABLOCK; void* buf_read = (void*)ALLOC(buf_size); if (ZSEEK64(pziinit->z_filefunc, pziinit->filestream, offset_central_dir + byte_before_the_zipfile, ZLIB_FILEFUNC_SEEK_SET) != 0) err=ZIP_ERRNO; while ((size_central_dir_to_read>0) && (err==ZIP_OK)) { ZPOS64_T read_this = SIZEDATA_INDATABLOCK; if (read_this > size_central_dir_to_read) read_this = size_central_dir_to_read; if (ZREAD64(pziinit->z_filefunc, pziinit->filestream,buf_read,(uLong)read_this) != read_this) err=ZIP_ERRNO; if (err==ZIP_OK) err = add_data_in_datablock(&pziinit->central_dir,buf_read, (uLong)read_this); size_central_dir_to_read-=read_this; } TRYFREE(buf_read); } pziinit->begin_pos = byte_before_the_zipfile; pziinit->number_entry = number_entry_CD; if (ZSEEK64(pziinit->z_filefunc, pziinit->filestream, offset_central_dir+byte_before_the_zipfile,ZLIB_FILEFUNC_SEEK_SET) != 0) err=ZIP_ERRNO; return err; } #endif /* !NO_ADDFILEINEXISTINGZIP*/ /************************************************************/ extern zipFile ZEXPORT zipOpen3 (voidpf file, int append, zipcharpc* globalcomment, zlib_filefunc64_32_def* pzlib_filefunc64_32_def) { zip64_internal ziinit; zip64_internal* zi; int err=ZIP_OK; ziinit.z_filefunc.zseek32_file = NULL; ziinit.z_filefunc.ztell32_file = NULL; if (pzlib_filefunc64_32_def==NULL) fill_qiodevice64_filefunc(&ziinit.z_filefunc.zfile_func64); else ziinit.z_filefunc = *pzlib_filefunc64_32_def; ziinit.filestream = ZOPEN64(ziinit.z_filefunc, file, (append == APPEND_STATUS_CREATE) ? (ZLIB_FILEFUNC_MODE_READ | ZLIB_FILEFUNC_MODE_WRITE | ZLIB_FILEFUNC_MODE_CREATE) : (ZLIB_FILEFUNC_MODE_READ | ZLIB_FILEFUNC_MODE_WRITE | ZLIB_FILEFUNC_MODE_EXISTING)); if (ziinit.filestream == NULL) return NULL; if (append == APPEND_STATUS_CREATEAFTER) ZSEEK64(ziinit.z_filefunc,ziinit.filestream,0,SEEK_END); ziinit.begin_pos = ZTELL64(ziinit.z_filefunc,ziinit.filestream); ziinit.in_opened_file_inzip = 0; ziinit.ci.stream_initialised = 0; ziinit.number_entry = 0; ziinit.add_position_when_writting_offset = 0; ziinit.flags = ZIP_WRITE_DATA_DESCRIPTOR; init_linkedlist(&(ziinit.central_dir)); zi = (zip64_internal*)ALLOC(sizeof(zip64_internal)); if (zi==NULL) { ZCLOSE64(ziinit.z_filefunc,ziinit.filestream); return NULL; } /* now we add file in a zipfile */ # ifndef NO_ADDFILEINEXISTINGZIP ziinit.globalcomment = NULL; if (append == APPEND_STATUS_ADDINZIP) { /* Read and Cache Central Directory Records */ err = LoadCentralDirectoryRecord(&ziinit); } if (globalcomment) { *globalcomment = ziinit.globalcomment; } # endif /* !NO_ADDFILEINEXISTINGZIP*/ if (err != ZIP_OK) { # ifndef NO_ADDFILEINEXISTINGZIP TRYFREE(ziinit.globalcomment); # endif /* !NO_ADDFILEINEXISTINGZIP*/ TRYFREE(zi); return NULL; } else { *zi = ziinit; return (zipFile)zi; } } extern zipFile ZEXPORT zipOpen2 (voidpf file, int append, zipcharpc* globalcomment, zlib_filefunc_def* pzlib_filefunc32_def) { if (pzlib_filefunc32_def != NULL) { zlib_filefunc64_32_def zlib_filefunc64_32_def_fill; fill_zlib_filefunc64_32_def_from_filefunc32(&zlib_filefunc64_32_def_fill,pzlib_filefunc32_def); return zipOpen3(file, append, globalcomment, &zlib_filefunc64_32_def_fill); } else return zipOpen3(file, append, globalcomment, NULL); } extern zipFile ZEXPORT zipOpen2_64 (voidpf file, int append, zipcharpc* globalcomment, zlib_filefunc64_def* pzlib_filefunc_def) { if (pzlib_filefunc_def != NULL) { zlib_filefunc64_32_def zlib_filefunc64_32_def_fill; zlib_filefunc64_32_def_fill.zfile_func64 = *pzlib_filefunc_def; zlib_filefunc64_32_def_fill.ztell32_file = NULL; zlib_filefunc64_32_def_fill.zseek32_file = NULL; return zipOpen3(file, append, globalcomment, &zlib_filefunc64_32_def_fill); } else return zipOpen3(file, append, globalcomment, NULL); } extern zipFile ZEXPORT zipOpen (voidpf file, int append) { return zipOpen3(file,append,NULL,NULL); } extern zipFile ZEXPORT zipOpen64 (voidpf file, int append) { return zipOpen3(file,append,NULL,NULL); } int Write_LocalFileHeader(zip64_internal* zi, const char* filename, uInt size_extrafield_local, const void* extrafield_local, uLong version_to_extract) { /* write the local header */ int err; uInt size_filename = (uInt)strlen(filename); uInt size_extrafield = size_extrafield_local; err = zip64local_putValue(&zi->z_filefunc,zi->filestream,(uLong)LOCALHEADERMAGIC, 4); if (err==ZIP_OK) { if(zi->ci.zip64) err = zip64local_putValue(&zi->z_filefunc,zi->filestream,(uLong)45,2);/* version needed to extract */ else err = zip64local_putValue(&zi->z_filefunc,zi->filestream,(uLong)version_to_extract,2); } if (err==ZIP_OK) err = zip64local_putValue(&zi->z_filefunc,zi->filestream,(uLong)zi->ci.flag,2); if (err==ZIP_OK) err = zip64local_putValue(&zi->z_filefunc,zi->filestream,(uLong)zi->ci.method,2); if (err==ZIP_OK) err = zip64local_putValue(&zi->z_filefunc,zi->filestream,(uLong)zi->ci.dosDate,4); /* CRC / Compressed size / Uncompressed size will be filled in later and rewritten later */ if (err==ZIP_OK) err = zip64local_putValue(&zi->z_filefunc,zi->filestream,(uLong)0,4); /* crc 32, unknown */ if (err==ZIP_OK) { if(zi->ci.zip64) err = zip64local_putValue(&zi->z_filefunc,zi->filestream,(uLong)0xFFFFFFFF,4); /* compressed size, unknown */ else err = zip64local_putValue(&zi->z_filefunc,zi->filestream,(uLong)0,4); /* compressed size, unknown */ } if (err==ZIP_OK) { if(zi->ci.zip64) err = zip64local_putValue(&zi->z_filefunc,zi->filestream,(uLong)0xFFFFFFFF,4); /* uncompressed size, unknown */ else err = zip64local_putValue(&zi->z_filefunc,zi->filestream,(uLong)0,4); /* uncompressed size, unknown */ } if (err==ZIP_OK) err = zip64local_putValue(&zi->z_filefunc,zi->filestream,(uLong)size_filename,2); if(zi->ci.zip64) { size_extrafield += 20; } if (err==ZIP_OK) err = zip64local_putValue(&zi->z_filefunc,zi->filestream,(uLong)size_extrafield,2); if ((err==ZIP_OK) && (size_filename > 0)) { if (ZWRITE64(zi->z_filefunc,zi->filestream,filename,size_filename)!=size_filename) err = ZIP_ERRNO; } if ((err==ZIP_OK) && (size_extrafield_local > 0)) { if (ZWRITE64(zi->z_filefunc, zi->filestream, extrafield_local, size_extrafield_local) != size_extrafield_local) err = ZIP_ERRNO; } if ((err==ZIP_OK) && (zi->ci.zip64)) { /* write the Zip64 extended info */ short HeaderID = 1; short DataSize = 16; ZPOS64_T CompressedSize = 0; ZPOS64_T UncompressedSize = 0; /* Remember position of Zip64 extended info for the local file header. (needed when we update size after done with file) */ zi->ci.pos_zip64extrainfo = ZTELL64(zi->z_filefunc,zi->filestream); err = zip64local_putValue(&zi->z_filefunc, zi->filestream, (short)HeaderID,2); err = zip64local_putValue(&zi->z_filefunc, zi->filestream, (short)DataSize,2); err = zip64local_putValue(&zi->z_filefunc, zi->filestream, (ZPOS64_T)UncompressedSize,8); err = zip64local_putValue(&zi->z_filefunc, zi->filestream, (ZPOS64_T)CompressedSize,8); } return err; } /* NOTE. When writing RAW the ZIP64 extended information in extrafield_local and extrafield_global needs to be stripped before calling this function it can be done with zipRemoveExtraInfoBlock It is not done here because then we need to realloc a new buffer since parameters are 'const' and I want to minimize unnecessary allocations. */ extern int ZEXPORT zipOpenNewFileInZip4_64 (zipFile file, const char* filename, const zip_fileinfo* zipfi, const void* extrafield_local, uInt size_extrafield_local, const void* extrafield_global, uInt size_extrafield_global, const char* comment, int method, int level, int raw, int windowBits,int memLevel, int strategy, const char* password, uLong crcForCrypting, uLong versionMadeBy, uLong flagBase, int zip64) { zip64_internal* zi; uInt size_filename; uInt size_comment; uInt i; int err = ZIP_OK; uLong version_to_extract; # ifdef NOCRYPT if (password != NULL) return ZIP_PARAMERROR; # endif if (file == NULL) return ZIP_PARAMERROR; #ifdef HAVE_BZIP2 if ((method!=0) && (method!=Z_DEFLATED) && (method!=Z_BZIP2ED)) return ZIP_PARAMERROR; #else if ((method!=0) && (method!=Z_DEFLATED)) return ZIP_PARAMERROR; #endif zi = (zip64_internal*)file; if (zi->in_opened_file_inzip == 1) { err = zipCloseFileInZip (file); if (err != ZIP_OK) return err; } if (method == 0 && (level == 0 || (zi->flags & ZIP_WRITE_DATA_DESCRIPTOR) == 0)) { version_to_extract = 10; } else { version_to_extract = 20; } if (filename==NULL) filename="-"; if (comment==NULL) size_comment = 0; else size_comment = (uInt)strlen(comment); size_filename = (uInt)strlen(filename); if (zipfi == NULL) zi->ci.dosDate = 0; else { if (zipfi->dosDate != 0) zi->ci.dosDate = zipfi->dosDate; else zi->ci.dosDate = zip64local_TmzDateToDosDate(&zipfi->tmz_date); } zi->ci.flag = flagBase; if ((level==8) || (level==9)) zi->ci.flag |= 2; if (level==2) zi->ci.flag |= 4; if (level==1) zi->ci.flag |= 6; if (password != NULL) zi->ci.flag |= 1; if (version_to_extract >= 20 && (zi->flags & ZIP_WRITE_DATA_DESCRIPTOR) != 0) zi->ci.flag |= 8; zi->ci.crc32 = 0; zi->ci.method = method; zi->ci.encrypt = 0; zi->ci.stream_initialised = 0; zi->ci.pos_in_buffered_data = 0; zi->ci.raw = raw; zi->ci.pos_local_header = ZTELL64(zi->z_filefunc,zi->filestream); zi->ci.size_centralheader = SIZECENTRALHEADER + size_filename + size_extrafield_global + size_comment; zi->ci.size_centralExtraFree = 32; /* Extra space we have reserved in case we need to add ZIP64 extra info data */ zi->ci.central_header = (char*)ALLOC((uInt)zi->ci.size_centralheader + zi->ci.size_centralExtraFree); zi->ci.size_centralExtra = size_extrafield_global; zip64local_putValue_inmemory(zi->ci.central_header,(uLong)CENTRALHEADERMAGIC,4); /* version info */ zip64local_putValue_inmemory(zi->ci.central_header+4,(uLong)versionMadeBy,2); zip64local_putValue_inmemory(zi->ci.central_header+6,(uLong)version_to_extract,2); zip64local_putValue_inmemory(zi->ci.central_header+8,(uLong)zi->ci.flag,2); zip64local_putValue_inmemory(zi->ci.central_header+10,(uLong)zi->ci.method,2); zip64local_putValue_inmemory(zi->ci.central_header+12,(uLong)zi->ci.dosDate,4); zip64local_putValue_inmemory(zi->ci.central_header+16,(uLong)0,4); /*crc*/ zip64local_putValue_inmemory(zi->ci.central_header+20,(uLong)0,4); /*compr size*/ zip64local_putValue_inmemory(zi->ci.central_header+24,(uLong)0,4); /*uncompr size*/ zip64local_putValue_inmemory(zi->ci.central_header+28,(uLong)size_filename,2); zip64local_putValue_inmemory(zi->ci.central_header+30,(uLong)size_extrafield_global,2); zip64local_putValue_inmemory(zi->ci.central_header+32,(uLong)size_comment,2); zip64local_putValue_inmemory(zi->ci.central_header+34,(uLong)0,2); /*disk nm start*/ if (zipfi==NULL) zip64local_putValue_inmemory(zi->ci.central_header+36,(uLong)0,2); else zip64local_putValue_inmemory(zi->ci.central_header+36,(uLong)zipfi->internal_fa,2); if (zipfi==NULL) zip64local_putValue_inmemory(zi->ci.central_header+38,(uLong)0,4); else zip64local_putValue_inmemory(zi->ci.central_header+38,(uLong)zipfi->external_fa,4); if(zi->ci.pos_local_header >= 0xffffffff) zip64local_putValue_inmemory(zi->ci.central_header+42,(uLong)0xffffffff,4); else zip64local_putValue_inmemory(zi->ci.central_header+42,(uLong)zi->ci.pos_local_header - zi->add_position_when_writting_offset,4); for (i=0;ici.central_header+SIZECENTRALHEADER+i) = *(filename+i); for (i=0;ici.central_header+SIZECENTRALHEADER+size_filename+i) = *(((const char*)extrafield_global)+i); for (i=0;ici.central_header+SIZECENTRALHEADER+size_filename+ size_extrafield_global+i) = *(comment+i); if (zi->ci.central_header == NULL) return ZIP_INTERNALERROR; zi->ci.zip64 = zip64; zi->ci.totalCompressedData = 0; zi->ci.totalUncompressedData = 0; zi->ci.pos_zip64extrainfo = 0; err = Write_LocalFileHeader(zi, filename, size_extrafield_local, extrafield_local, version_to_extract); #ifdef HAVE_BZIP2 zi->ci.bstream.avail_in = (uInt)0; zi->ci.bstream.avail_out = (uInt)Z_BUFSIZE; zi->ci.bstream.next_out = (char*)zi->ci.buffered_data; zi->ci.bstream.total_in_hi32 = 0; zi->ci.bstream.total_in_lo32 = 0; zi->ci.bstream.total_out_hi32 = 0; zi->ci.bstream.total_out_lo32 = 0; #endif zi->ci.stream.avail_in = (uInt)0; zi->ci.stream.avail_out = (uInt)Z_BUFSIZE; zi->ci.stream.next_out = zi->ci.buffered_data; zi->ci.stream.total_in = 0; zi->ci.stream.total_out = 0; zi->ci.stream.data_type = Z_BINARY; #ifdef HAVE_BZIP2 if ((err==ZIP_OK) && (zi->ci.method == Z_DEFLATED || zi->ci.method == Z_BZIP2ED) && (!zi->ci.raw)) #else if ((err==ZIP_OK) && (zi->ci.method == Z_DEFLATED) && (!zi->ci.raw)) #endif { if(zi->ci.method == Z_DEFLATED) { zi->ci.stream.zalloc = (alloc_func)0; zi->ci.stream.zfree = (free_func)0; zi->ci.stream.opaque = (voidpf)0; if (windowBits>0) windowBits = -windowBits; err = deflateInit2(&zi->ci.stream, level, Z_DEFLATED, windowBits, memLevel, strategy); if (err==Z_OK) zi->ci.stream_initialised = Z_DEFLATED; } else if(zi->ci.method == Z_BZIP2ED) { #ifdef HAVE_BZIP2 /* Init BZip stuff here */ zi->ci.bstream.bzalloc = 0; zi->ci.bstream.bzfree = 0; zi->ci.bstream.opaque = (voidpf)0; err = BZ2_bzCompressInit(&zi->ci.bstream, level, 0,35); if(err == BZ_OK) zi->ci.stream_initialised = Z_BZIP2ED; #endif } } # ifndef NOCRYPT zi->ci.crypt_header_size = 0; if ((err==Z_OK) && (password != NULL)) { unsigned char bufHead[RAND_HEAD_LEN]; unsigned int sizeHead; zi->ci.encrypt = 1; zi->ci.pcrc_32_tab = get_crc_table(); /*init_keys(password,zi->ci.keys,zi->ci.pcrc_32_tab);*/ if (crcForCrypting == 0) { crcForCrypting = (uLong)zi->ci.dosDate << 16; /* ATTANTION! Without this row, you don't unpack your password protected archive in other app. */ } sizeHead=crypthead(password,bufHead,RAND_HEAD_LEN,zi->ci.keys,zi->ci.pcrc_32_tab,crcForCrypting); zi->ci.crypt_header_size = sizeHead; if (ZWRITE64(zi->z_filefunc,zi->filestream,bufHead,sizeHead) != sizeHead) err = ZIP_ERRNO; } # endif if (err==Z_OK) zi->in_opened_file_inzip = 1; return err; } extern int ZEXPORT zipOpenNewFileInZip4 (zipFile file, const char* filename, const zip_fileinfo* zipfi, const void* extrafield_local, uInt size_extrafield_local, const void* extrafield_global, uInt size_extrafield_global, const char* comment, int method, int level, int raw, int windowBits,int memLevel, int strategy, const char* password, uLong crcForCrypting, uLong versionMadeBy, uLong flagBase) { return zipOpenNewFileInZip4_64 (file, filename, zipfi, extrafield_local, size_extrafield_local, extrafield_global, size_extrafield_global, comment, method, level, raw, windowBits, memLevel, strategy, password, crcForCrypting, versionMadeBy, flagBase, 0); } extern int ZEXPORT zipOpenNewFileInZip3 (zipFile file, const char* filename, const zip_fileinfo* zipfi, const void* extrafield_local, uInt size_extrafield_local, const void* extrafield_global, uInt size_extrafield_global, const char* comment, int method, int level, int raw, int windowBits,int memLevel, int strategy, const char* password, uLong crcForCrypting) { return zipOpenNewFileInZip4_64 (file, filename, zipfi, extrafield_local, size_extrafield_local, extrafield_global, size_extrafield_global, comment, method, level, raw, windowBits, memLevel, strategy, password, crcForCrypting, VERSIONMADEBY, 0, 0); } extern int ZEXPORT zipOpenNewFileInZip3_64(zipFile file, const char* filename, const zip_fileinfo* zipfi, const void* extrafield_local, uInt size_extrafield_local, const void* extrafield_global, uInt size_extrafield_global, const char* comment, int method, int level, int raw, int windowBits,int memLevel, int strategy, const char* password, uLong crcForCrypting, int zip64) { return zipOpenNewFileInZip4_64 (file, filename, zipfi, extrafield_local, size_extrafield_local, extrafield_global, size_extrafield_global, comment, method, level, raw, windowBits, memLevel, strategy, password, crcForCrypting, VERSIONMADEBY, 0, zip64); } extern int ZEXPORT zipOpenNewFileInZip2(zipFile file, const char* filename, const zip_fileinfo* zipfi, const void* extrafield_local, uInt size_extrafield_local, const void* extrafield_global, uInt size_extrafield_global, const char* comment, int method, int level, int raw) { return zipOpenNewFileInZip4_64 (file, filename, zipfi, extrafield_local, size_extrafield_local, extrafield_global, size_extrafield_global, comment, method, level, raw, -MAX_WBITS, DEF_MEM_LEVEL, Z_DEFAULT_STRATEGY, NULL, 0, VERSIONMADEBY, 0, 0); } extern int ZEXPORT zipOpenNewFileInZip2_64(zipFile file, const char* filename, const zip_fileinfo* zipfi, const void* extrafield_local, uInt size_extrafield_local, const void* extrafield_global, uInt size_extrafield_global, const char* comment, int method, int level, int raw, int zip64) { return zipOpenNewFileInZip4_64 (file, filename, zipfi, extrafield_local, size_extrafield_local, extrafield_global, size_extrafield_global, comment, method, level, raw, -MAX_WBITS, DEF_MEM_LEVEL, Z_DEFAULT_STRATEGY, NULL, 0, VERSIONMADEBY, 0, zip64); } extern int ZEXPORT zipOpenNewFileInZip64 (zipFile file, const char* filename, const zip_fileinfo* zipfi, const void* extrafield_local, uInt size_extrafield_local, const void*extrafield_global, uInt size_extrafield_global, const char* comment, int method, int level, int zip64) { return zipOpenNewFileInZip4_64 (file, filename, zipfi, extrafield_local, size_extrafield_local, extrafield_global, size_extrafield_global, comment, method, level, 0, -MAX_WBITS, DEF_MEM_LEVEL, Z_DEFAULT_STRATEGY, NULL, 0, VERSIONMADEBY, 0, zip64); } extern int ZEXPORT zipOpenNewFileInZip (zipFile file, const char* filename, const zip_fileinfo* zipfi, const void* extrafield_local, uInt size_extrafield_local, const void*extrafield_global, uInt size_extrafield_global, const char* comment, int method, int level) { return zipOpenNewFileInZip4_64 (file, filename, zipfi, extrafield_local, size_extrafield_local, extrafield_global, size_extrafield_global, comment, method, level, 0, -MAX_WBITS, DEF_MEM_LEVEL, Z_DEFAULT_STRATEGY, NULL, 0, VERSIONMADEBY, 0, 0); } local int zip64FlushWriteBuffer(zip64_internal* zi) { int err=ZIP_OK; if (zi->ci.encrypt != 0) { #ifndef NOCRYPT uInt i; int t; for (i=0;ici.pos_in_buffered_data;i++) zi->ci.buffered_data[i] = zencode(zi->ci.keys, zi->ci.pcrc_32_tab, zi->ci.buffered_data[i],t); #endif } if (ZWRITE64(zi->z_filefunc,zi->filestream,zi->ci.buffered_data,zi->ci.pos_in_buffered_data) != zi->ci.pos_in_buffered_data) err = ZIP_ERRNO; zi->ci.totalCompressedData += zi->ci.pos_in_buffered_data; #ifdef HAVE_BZIP2 if(zi->ci.method == Z_BZIP2ED) { zi->ci.totalUncompressedData += zi->ci.bstream.total_in_lo32; zi->ci.bstream.total_in_lo32 = 0; zi->ci.bstream.total_in_hi32 = 0; } else #endif { zi->ci.totalUncompressedData += zi->ci.stream.total_in; zi->ci.stream.total_in = 0; } zi->ci.pos_in_buffered_data = 0; return err; } extern int ZEXPORT zipWriteInFileInZip (zipFile file,const void* buf,unsigned int len) { zip64_internal* zi; int err=ZIP_OK; if (file == NULL) return ZIP_PARAMERROR; zi = (zip64_internal*)file; if (zi->in_opened_file_inzip == 0) return ZIP_PARAMERROR; zi->ci.crc32 = crc32(zi->ci.crc32,buf,(uInt)len); #ifdef HAVE_BZIP2 if(zi->ci.method == Z_BZIP2ED && (!zi->ci.raw)) { zi->ci.bstream.next_in = (void*)buf; zi->ci.bstream.avail_in = len; err = BZ_RUN_OK; while ((err==BZ_RUN_OK) && (zi->ci.bstream.avail_in>0)) { if (zi->ci.bstream.avail_out == 0) { if (zip64FlushWriteBuffer(zi) == ZIP_ERRNO) err = ZIP_ERRNO; zi->ci.bstream.avail_out = (uInt)Z_BUFSIZE; zi->ci.bstream.next_out = (char*)zi->ci.buffered_data; } if(err != BZ_RUN_OK) break; if ((zi->ci.method == Z_BZIP2ED) && (!zi->ci.raw)) { uLong uTotalOutBefore_lo = zi->ci.bstream.total_out_lo32; /* uLong uTotalOutBefore_hi = zi->ci.bstream.total_out_hi32; */ err=BZ2_bzCompress(&zi->ci.bstream, BZ_RUN); zi->ci.pos_in_buffered_data += (uInt)(zi->ci.bstream.total_out_lo32 - uTotalOutBefore_lo) ; } } if(err == BZ_RUN_OK) err = ZIP_OK; } else #endif { zi->ci.stream.next_in = (Bytef*)buf; zi->ci.stream.avail_in = len; while ((err==ZIP_OK) && (zi->ci.stream.avail_in>0)) { if (zi->ci.stream.avail_out == 0) { if (zip64FlushWriteBuffer(zi) == ZIP_ERRNO) err = ZIP_ERRNO; zi->ci.stream.avail_out = (uInt)Z_BUFSIZE; zi->ci.stream.next_out = zi->ci.buffered_data; } if(err != ZIP_OK) break; if ((zi->ci.method == Z_DEFLATED) && (!zi->ci.raw)) { uLong uTotalOutBefore = zi->ci.stream.total_out; err=deflate(&zi->ci.stream, Z_NO_FLUSH); if(uTotalOutBefore > zi->ci.stream.total_out) { int bBreak = 0; bBreak++; } zi->ci.pos_in_buffered_data += (uInt)(zi->ci.stream.total_out - uTotalOutBefore) ; } else { uInt copy_this,i; if (zi->ci.stream.avail_in < zi->ci.stream.avail_out) copy_this = zi->ci.stream.avail_in; else copy_this = zi->ci.stream.avail_out; for (i = 0; i < copy_this; i++) *(((char*)zi->ci.stream.next_out)+i) = *(((const char*)zi->ci.stream.next_in)+i); { zi->ci.stream.avail_in -= copy_this; zi->ci.stream.avail_out-= copy_this; zi->ci.stream.next_in+= copy_this; zi->ci.stream.next_out+= copy_this; zi->ci.stream.total_in+= copy_this; zi->ci.stream.total_out+= copy_this; zi->ci.pos_in_buffered_data += copy_this; } } }/* while(...) */ } return err; } extern int ZEXPORT zipCloseFileInZipRaw (zipFile file, uLong uncompressed_size, uLong crc32) { return zipCloseFileInZipRaw64 (file, uncompressed_size, crc32); } extern int ZEXPORT zipCloseFileInZipRaw64 (zipFile file, ZPOS64_T uncompressed_size, uLong crc32) { zip64_internal* zi; ZPOS64_T compressed_size; uLong invalidValue = 0xffffffff; short datasize = 0; int err=ZIP_OK; if (file == NULL) return ZIP_PARAMERROR; zi = (zip64_internal*)file; if (zi->in_opened_file_inzip == 0) return ZIP_PARAMERROR; zi->ci.stream.avail_in = 0; if ((zi->ci.method == Z_DEFLATED) && (!zi->ci.raw)) { while (err==ZIP_OK) { uLong uTotalOutBefore; if (zi->ci.stream.avail_out == 0) { if (zip64FlushWriteBuffer(zi) == ZIP_ERRNO) err = ZIP_ERRNO; zi->ci.stream.avail_out = (uInt)Z_BUFSIZE; zi->ci.stream.next_out = zi->ci.buffered_data; } uTotalOutBefore = zi->ci.stream.total_out; err=deflate(&zi->ci.stream, Z_FINISH); zi->ci.pos_in_buffered_data += (uInt)(zi->ci.stream.total_out - uTotalOutBefore) ; } } else if ((zi->ci.method == Z_BZIP2ED) && (!zi->ci.raw)) { #ifdef HAVE_BZIP2 err = BZ_FINISH_OK; while (err==BZ_FINISH_OK) { uLong uTotalOutBefore; if (zi->ci.bstream.avail_out == 0) { if (zip64FlushWriteBuffer(zi) == ZIP_ERRNO) err = ZIP_ERRNO; zi->ci.bstream.avail_out = (uInt)Z_BUFSIZE; zi->ci.bstream.next_out = (char*)zi->ci.buffered_data; } uTotalOutBefore = zi->ci.bstream.total_out_lo32; err=BZ2_bzCompress(&zi->ci.bstream, BZ_FINISH); if(err == BZ_STREAM_END) err = Z_STREAM_END; zi->ci.pos_in_buffered_data += (uInt)(zi->ci.bstream.total_out_lo32 - uTotalOutBefore); } if(err == BZ_FINISH_OK) err = ZIP_OK; #endif } if (err==Z_STREAM_END) err=ZIP_OK; /* this is normal */ if ((zi->ci.pos_in_buffered_data>0) && (err==ZIP_OK)) { if (zip64FlushWriteBuffer(zi)==ZIP_ERRNO) err = ZIP_ERRNO; } if ((zi->ci.method == Z_DEFLATED) && (!zi->ci.raw)) { int tmp_err = deflateEnd(&zi->ci.stream); if (err == ZIP_OK) err = tmp_err; zi->ci.stream_initialised = 0; } #ifdef HAVE_BZIP2 else if((zi->ci.method == Z_BZIP2ED) && (!zi->ci.raw)) { int tmperr = BZ2_bzCompressEnd(&zi->ci.bstream); if (err==ZIP_OK) err = tmperr; zi->ci.stream_initialised = 0; } #endif if (!zi->ci.raw) { crc32 = (uLong)zi->ci.crc32; uncompressed_size = zi->ci.totalUncompressedData; } compressed_size = zi->ci.totalCompressedData; # ifndef NOCRYPT compressed_size += zi->ci.crypt_header_size; # endif /* update Current Item crc and sizes, */ if(compressed_size >= 0xffffffff || uncompressed_size >= 0xffffffff || zi->ci.pos_local_header >= 0xffffffff) { /*version Made by*/ zip64local_putValue_inmemory(zi->ci.central_header+4,(uLong)45,2); /*version needed*/ zip64local_putValue_inmemory(zi->ci.central_header+6,(uLong)45,2); } zip64local_putValue_inmemory(zi->ci.central_header+16,crc32,4); /*crc*/ if(compressed_size >= 0xffffffff) zip64local_putValue_inmemory(zi->ci.central_header+20, invalidValue,4); /*compr size*/ else zip64local_putValue_inmemory(zi->ci.central_header+20, compressed_size,4); /*compr size*/ /* set internal file attributes field */ if (zi->ci.stream.data_type == Z_ASCII) zip64local_putValue_inmemory(zi->ci.central_header+36,(uLong)Z_ASCII,2); if(uncompressed_size >= 0xffffffff) zip64local_putValue_inmemory(zi->ci.central_header+24, invalidValue,4); /*uncompr size*/ else zip64local_putValue_inmemory(zi->ci.central_header+24, uncompressed_size,4); /*uncompr size*/ /* Add ZIP64 extra info field for uncompressed size */ if(uncompressed_size >= 0xffffffff) datasize += 8; /* Add ZIP64 extra info field for compressed size */ if(compressed_size >= 0xffffffff) datasize += 8; /* Add ZIP64 extra info field for relative offset to local file header of current file */ if(zi->ci.pos_local_header >= 0xffffffff) datasize += 8; if(datasize > 0) { char* p = NULL; if((uLong)(datasize + 4) > zi->ci.size_centralExtraFree) { /* we can not write more data to the buffer that we have room for. */ return ZIP_BADZIPFILE; } p = zi->ci.central_header + zi->ci.size_centralheader; /* Add Extra Information Header for 'ZIP64 information' */ zip64local_putValue_inmemory(p, 0x0001, 2); /* HeaderID */ p += 2; zip64local_putValue_inmemory(p, datasize, 2); /* DataSize */ p += 2; if(uncompressed_size >= 0xffffffff) { zip64local_putValue_inmemory(p, uncompressed_size, 8); p += 8; } if(compressed_size >= 0xffffffff) { zip64local_putValue_inmemory(p, compressed_size, 8); p += 8; } if(zi->ci.pos_local_header >= 0xffffffff) { zip64local_putValue_inmemory(p, zi->ci.pos_local_header, 8); p += 8; } /* Update how much extra free space we got in the memory buffer */ /* and increase the centralheader size so the new ZIP64 fields are included */ /* ( 4 below is the size of HeaderID and DataSize field ) */ zi->ci.size_centralExtraFree -= datasize + 4; zi->ci.size_centralheader += datasize + 4; /* Update the extra info size field */ zi->ci.size_centralExtra += datasize + 4; zip64local_putValue_inmemory(zi->ci.central_header+30,(uLong)zi->ci.size_centralExtra,2); } if (err==ZIP_OK) err = add_data_in_datablock(&zi->central_dir, zi->ci.central_header, (uLong)zi->ci.size_centralheader); free(zi->ci.central_header); if (err==ZIP_OK) { /* Update the LocalFileHeader with the new values. */ ZPOS64_T cur_pos_inzip = ZTELL64(zi->z_filefunc,zi->filestream); if (ZSEEK64(zi->z_filefunc,zi->filestream, zi->ci.pos_local_header + 14,ZLIB_FILEFUNC_SEEK_SET)!=0) err = ZIP_ERRNO; if (err==ZIP_OK) err = zip64local_putValue(&zi->z_filefunc,zi->filestream,crc32,4); /* crc 32, unknown */ if(uncompressed_size >= 0xffffffff || compressed_size >= 0xffffffff) { if(zi->ci.pos_zip64extrainfo > 0) { /* Update the size in the ZIP64 extended field. */ if (ZSEEK64(zi->z_filefunc,zi->filestream, zi->ci.pos_zip64extrainfo + 4,ZLIB_FILEFUNC_SEEK_SET)!=0) err = ZIP_ERRNO; if (err==ZIP_OK) /* compressed size, unknown */ err = zip64local_putValue(&zi->z_filefunc, zi->filestream, uncompressed_size, 8); if (err==ZIP_OK) /* uncompressed size, unknown */ err = zip64local_putValue(&zi->z_filefunc, zi->filestream, compressed_size, 8); } } else { if (err==ZIP_OK) /* compressed size, unknown */ err = zip64local_putValue(&zi->z_filefunc,zi->filestream,compressed_size,4); if (err==ZIP_OK) /* uncompressed size, unknown */ err = zip64local_putValue(&zi->z_filefunc,zi->filestream,uncompressed_size,4); } if (ZSEEK64(zi->z_filefunc,zi->filestream, cur_pos_inzip,ZLIB_FILEFUNC_SEEK_SET)!=0) err = ZIP_ERRNO; if ((zi->ci.flag & 8) != 0) { /* Write local Descriptor after file data */ if (err==ZIP_OK) err = zip64local_putValue(&zi->z_filefunc,zi->filestream,(uLong)DESCRIPTORHEADERMAGIC,4); if (err==ZIP_OK) err = zip64local_putValue(&zi->z_filefunc,zi->filestream,crc32,4); /* crc 32, unknown */ if (zi->ci.zip64) { if (err==ZIP_OK) /* compressed size, unknown */ err = zip64local_putValue(&zi->z_filefunc,zi->filestream,compressed_size,8); if (err==ZIP_OK) /* uncompressed size, unknown */ err = zip64local_putValue(&zi->z_filefunc,zi->filestream,uncompressed_size,8); } else { if (err==ZIP_OK) /* compressed size, unknown */ err = zip64local_putValue(&zi->z_filefunc,zi->filestream,compressed_size,4); if (err==ZIP_OK) /* uncompressed size, unknown */ err = zip64local_putValue(&zi->z_filefunc,zi->filestream,uncompressed_size,4); } } } zi->number_entry ++; zi->in_opened_file_inzip = 0; return err; } extern int ZEXPORT zipCloseFileInZip (zipFile file) { return zipCloseFileInZipRaw (file,0,0); } int Write_Zip64EndOfCentralDirectoryLocator(zip64_internal* zi, ZPOS64_T zip64eocd_pos_inzip) { int err = ZIP_OK; ZPOS64_T pos = zip64eocd_pos_inzip - zi->add_position_when_writting_offset; err = zip64local_putValue(&zi->z_filefunc,zi->filestream,(uLong)ZIP64ENDLOCHEADERMAGIC,4); /*num disks*/ if (err==ZIP_OK) /* number of the disk with the start of the central directory */ err = zip64local_putValue(&zi->z_filefunc,zi->filestream,(uLong)0,4); /*relative offset*/ if (err==ZIP_OK) /* Relative offset to the Zip64EndOfCentralDirectory */ err = zip64local_putValue(&zi->z_filefunc,zi->filestream, pos,8); /*total disks*/ /* Do not support spawning of disk so always say 1 here*/ if (err==ZIP_OK) /* number of the disk with the start of the central directory */ err = zip64local_putValue(&zi->z_filefunc,zi->filestream,(uLong)1,4); return err; } int Write_Zip64EndOfCentralDirectoryRecord(zip64_internal* zi, uLong size_centraldir, ZPOS64_T centraldir_pos_inzip) { int err = ZIP_OK; uLong Zip64DataSize = 44; err = zip64local_putValue(&zi->z_filefunc,zi->filestream,(uLong)ZIP64ENDHEADERMAGIC,4); if (err==ZIP_OK) /* size of this 'zip64 end of central directory' */ err = zip64local_putValue(&zi->z_filefunc,zi->filestream,(ZPOS64_T)Zip64DataSize,8); /* why ZPOS64_T of this ? */ if (err==ZIP_OK) /* version made by */ err = zip64local_putValue(&zi->z_filefunc,zi->filestream,(uLong)45,2); if (err==ZIP_OK) /* version needed */ err = zip64local_putValue(&zi->z_filefunc,zi->filestream,(uLong)45,2); if (err==ZIP_OK) /* number of this disk */ err = zip64local_putValue(&zi->z_filefunc,zi->filestream,(uLong)0,4); if (err==ZIP_OK) /* number of the disk with the start of the central directory */ err = zip64local_putValue(&zi->z_filefunc,zi->filestream,(uLong)0,4); if (err==ZIP_OK) /* total number of entries in the central dir on this disk */ err = zip64local_putValue(&zi->z_filefunc, zi->filestream, zi->number_entry, 8); if (err==ZIP_OK) /* total number of entries in the central dir */ err = zip64local_putValue(&zi->z_filefunc, zi->filestream, zi->number_entry, 8); if (err==ZIP_OK) /* size of the central directory */ err = zip64local_putValue(&zi->z_filefunc,zi->filestream,(ZPOS64_T)size_centraldir,8); if (err==ZIP_OK) /* offset of start of central directory with respect to the starting disk number */ { ZPOS64_T pos = centraldir_pos_inzip - zi->add_position_when_writting_offset; err = zip64local_putValue(&zi->z_filefunc,zi->filestream, (ZPOS64_T)pos,8); } return err; } int Write_EndOfCentralDirectoryRecord(zip64_internal* zi, uLong size_centraldir, ZPOS64_T centraldir_pos_inzip) { int err = ZIP_OK; /*signature*/ err = zip64local_putValue(&zi->z_filefunc,zi->filestream,(uLong)ENDHEADERMAGIC,4); if (err==ZIP_OK) /* number of this disk */ err = zip64local_putValue(&zi->z_filefunc,zi->filestream,(uLong)0,2); if (err==ZIP_OK) /* number of the disk with the start of the central directory */ err = zip64local_putValue(&zi->z_filefunc,zi->filestream,(uLong)0,2); if (err==ZIP_OK) /* total number of entries in the central dir on this disk */ { { if(zi->number_entry >= 0xFFFF) err = zip64local_putValue(&zi->z_filefunc,zi->filestream,(uLong)0xffff,2); /* use value in ZIP64 record */ else err = zip64local_putValue(&zi->z_filefunc,zi->filestream,(uLong)zi->number_entry,2); } } if (err==ZIP_OK) /* total number of entries in the central dir */ { if(zi->number_entry >= 0xFFFF) err = zip64local_putValue(&zi->z_filefunc,zi->filestream,(uLong)0xffff,2); /* use value in ZIP64 record */ else err = zip64local_putValue(&zi->z_filefunc,zi->filestream,(uLong)zi->number_entry,2); } if (err==ZIP_OK) /* size of the central directory */ err = zip64local_putValue(&zi->z_filefunc,zi->filestream,(uLong)size_centraldir,4); if (err==ZIP_OK) /* offset of start of central directory with respect to the starting disk number */ { ZPOS64_T pos = centraldir_pos_inzip - zi->add_position_when_writting_offset; if(pos >= 0xffffffff) { err = zip64local_putValue(&zi->z_filefunc,zi->filestream, (uLong)0xffffffff,4); } else err = zip64local_putValue(&zi->z_filefunc,zi->filestream, (uLong)(centraldir_pos_inzip - zi->add_position_when_writting_offset),4); } return err; } int Write_GlobalComment(zip64_internal* zi, const char* global_comment) { int err = ZIP_OK; uInt size_global_comment = 0; if(global_comment != NULL) size_global_comment = (uInt)strlen(global_comment); err = zip64local_putValue(&zi->z_filefunc,zi->filestream,(uLong)size_global_comment,2); if (err == ZIP_OK && size_global_comment > 0) { if (ZWRITE64(zi->z_filefunc,zi->filestream, global_comment, size_global_comment) != size_global_comment) err = ZIP_ERRNO; } return err; } extern int ZEXPORT zipClose (zipFile file, const char* global_comment) { zip64_internal* zi; int err = 0; uLong size_centraldir = 0; ZPOS64_T centraldir_pos_inzip; ZPOS64_T pos; if (file == NULL) return ZIP_PARAMERROR; zi = (zip64_internal*)file; if (zi->in_opened_file_inzip == 1) { err = zipCloseFileInZip (file); } #ifndef NO_ADDFILEINEXISTINGZIP if (global_comment==NULL) global_comment = zi->globalcomment; #endif centraldir_pos_inzip = ZTELL64(zi->z_filefunc,zi->filestream); if (err==ZIP_OK) { linkedlist_datablock_internal* ldi = zi->central_dir.first_block; while (ldi!=NULL) { if ((err==ZIP_OK) && (ldi->filled_in_this_block>0)) { if (ZWRITE64(zi->z_filefunc,zi->filestream, ldi->data, ldi->filled_in_this_block) != ldi->filled_in_this_block) err = ZIP_ERRNO; } size_centraldir += ldi->filled_in_this_block; ldi = ldi->next_datablock; } } free_linkedlist(&(zi->central_dir)); pos = centraldir_pos_inzip - zi->add_position_when_writting_offset; if(pos >= 0xffffffff) { ZPOS64_T Zip64EOCDpos = ZTELL64(zi->z_filefunc,zi->filestream); Write_Zip64EndOfCentralDirectoryRecord(zi, size_centraldir, centraldir_pos_inzip); Write_Zip64EndOfCentralDirectoryLocator(zi, Zip64EOCDpos); } if (err==ZIP_OK) err = Write_EndOfCentralDirectoryRecord(zi, size_centraldir, centraldir_pos_inzip); if(err == ZIP_OK) err = Write_GlobalComment(zi, global_comment); if (ZCLOSE64(zi->z_filefunc,zi->filestream) != 0) if (err == ZIP_OK) err = ZIP_ERRNO; #ifndef NO_ADDFILEINEXISTINGZIP TRYFREE(zi->globalcomment); #endif TRYFREE(zi); return err; } extern int ZEXPORT zipRemoveExtraInfoBlock (char* pData, int* dataLen, short sHeader) { char* p = pData; int size = 0; char* pNewHeader; char* pTmp; short header; short dataSize; int retVal = ZIP_OK; if(pData == NULL || *dataLen < 4) return ZIP_PARAMERROR; pNewHeader = (char*)ALLOC(*dataLen); pTmp = pNewHeader; while(p < (pData + *dataLen)) { header = *(short*)p; dataSize = *(((short*)p)+1); if( header == sHeader ) /* Header found. */ { p += dataSize + 4; /* skip it. do not copy to temp buffer */ } else { /* Extra Info block should not be removed, So copy it to the temp buffer. */ memcpy(pTmp, p, dataSize + 4); p += dataSize + 4; size += dataSize + 4; } } if(size < *dataLen) { /* clean old extra info block. */ memset(pData,0, *dataLen); /* copy the new extra info block over the old */ if(size > 0) memcpy(pData, pNewHeader, size); /* set the new extra info size */ *dataLen = size; retVal = ZIP_OK; } else retVal = ZIP_ERRNO; TRYFREE(pNewHeader); return retVal; } int ZEXPORT zipSetFlags(zipFile file, unsigned flags) { zip64_internal* zi; if (file == NULL) return ZIP_PARAMERROR; zi = (zip64_internal*)file; zi->flags |= flags; return ZIP_OK; } int ZEXPORT zipClearFlags(zipFile file, unsigned flags) { zip64_internal* zi; if (file == NULL) return ZIP_PARAMERROR; zi = (zip64_internal*)file; zi->flags &= ~flags; return ZIP_OK; } connectome-workbench-1.2.3+git41-gc4c6c90/src/Quazip/zip.h000066400000000000000000000370321300200146000227750ustar00rootroot00000000000000/* zip.h -- IO on .zip files using zlib Version 1.1, February 14h, 2010 part of the MiniZip project - ( http://www.winimage.com/zLibDll/minizip.html ) Copyright (C) 1998-2010 Gilles Vollant (minizip) ( http://www.winimage.com/zLibDll/minizip.html ) Modifications for Zip64 support Copyright (C) 2009-2010 Mathias Svensson ( http://result42.com ) For more info read MiniZip_info.txt --------------------------------------------------------------------------- Condition of use and distribution are the same than zlib : This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this software. Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. 3. This notice may not be removed or altered from any source distribution. --------------------------------------------------------------------------- Changes See header of zip.h */ #ifndef _zip12_H #define _zip12_H #ifdef __cplusplus extern "C" { #endif //#define HAVE_BZIP2 #ifndef _ZLIB_H #include "zlib.h" #endif #ifndef _ZLIBIOAPI_H #include "ioapi.h" #endif #ifdef HAVE_BZIP2 #include "bzlib.h" #endif #define Z_BZIP2ED 12 #if defined(STRICTZIP) || defined(STRICTZIPUNZIP) /* like the STRICT of WIN32, we define a pointer that cannot be converted from (void*) without cast */ typedef struct TagzipFile__ { int unused; } zipFile__; typedef zipFile__ *zipFile; #else typedef voidp zipFile; #endif #define ZIP_OK (0) #define ZIP_EOF (0) #define ZIP_ERRNO (Z_ERRNO) #define ZIP_PARAMERROR (-102) #define ZIP_BADZIPFILE (-103) #define ZIP_INTERNALERROR (-104) #define ZIP_WRITE_DATA_DESCRIPTOR 0x8u #ifndef DEF_MEM_LEVEL # if MAX_MEM_LEVEL >= 8 # define DEF_MEM_LEVEL 8 # else # define DEF_MEM_LEVEL MAX_MEM_LEVEL # endif #endif /* default memLevel */ /* tm_zip contain date/time info */ typedef struct tm_zip_s { uInt tm_sec; /* seconds after the minute - [0,59] */ uInt tm_min; /* minutes after the hour - [0,59] */ uInt tm_hour; /* hours since midnight - [0,23] */ uInt tm_mday; /* day of the month - [1,31] */ uInt tm_mon; /* months since January - [0,11] */ uInt tm_year; /* years - [1980..2044] */ } tm_zip; typedef struct { tm_zip tmz_date; /* date in understandable format */ uLong dosDate; /* if dos_date == 0, tmu_date is used */ /* uLong flag; */ /* general purpose bit flag 2 bytes */ uLong internal_fa; /* internal file attributes 2 bytes */ uLong external_fa; /* external file attributes 4 bytes */ } zip_fileinfo; typedef const char* zipcharpc; #define APPEND_STATUS_CREATE (0) #define APPEND_STATUS_CREATEAFTER (1) #define APPEND_STATUS_ADDINZIP (2) extern zipFile ZEXPORT zipOpen OF((voidpf file, int append)); extern zipFile ZEXPORT zipOpen64 OF((voidpf file, int append)); /* Create a zipfile. the file argument depends on the API used, for QuaZIP it's a QIODevice pointer. if the file pathname exist and append==APPEND_STATUS_CREATEAFTER, the zip will be created at the end of the file. (useful if the file contain a self extractor code) if the file pathname exist and append==APPEND_STATUS_ADDINZIP, we will add files in existing zip (be sure you don't add file that doesn't exist) If the zipfile cannot be opened, the return value is NULL. Else, the return value is a zipFile Handle, usable with other function of this zip package. */ /* Note : there is no delete function into a zipfile. If you want delete file into a zipfile, you must open a zipfile, and create another Of couse, you can use RAW reading and writing to copy the file you did not want delte */ extern zipFile ZEXPORT zipOpen2 OF((voidpf file, int append, zipcharpc* globalcomment, zlib_filefunc_def* pzlib_filefunc_def)); extern zipFile ZEXPORT zipOpen2_64 OF((voidpf file, int append, zipcharpc* globalcomment, zlib_filefunc64_def* pzlib_filefunc_def)); extern int ZEXPORT zipOpenNewFileInZip OF((zipFile file, const char* filename, const zip_fileinfo* zipfi, const void* extrafield_local, uInt size_extrafield_local, const void* extrafield_global, uInt size_extrafield_global, const char* comment, int method, int level)); extern int ZEXPORT zipOpenNewFileInZip64 OF((zipFile file, const char* filename, const zip_fileinfo* zipfi, const void* extrafield_local, uInt size_extrafield_local, const void* extrafield_global, uInt size_extrafield_global, const char* comment, int method, int level, int zip64)); /* Open a file in the ZIP for writing. filename : the filename in zip (if NULL, '-' without quote will be used *zipfi contain supplemental information if extrafield_local!=NULL and size_extrafield_local>0, extrafield_local contains the extrafield data the the local header if extrafield_global!=NULL and size_extrafield_global>0, extrafield_global contains the extrafield data the the local header if comment != NULL, comment contain the comment string method contain the compression method (0 for store, Z_DEFLATED for deflate) level contain the level of compression (can be Z_DEFAULT_COMPRESSION) zip64 is set to 1 if a zip64 extended information block should be added to the local file header. this MUST be '1' if the uncompressed size is >= 0xffffffff. */ extern int ZEXPORT zipOpenNewFileInZip2 OF((zipFile file, const char* filename, const zip_fileinfo* zipfi, const void* extrafield_local, uInt size_extrafield_local, const void* extrafield_global, uInt size_extrafield_global, const char* comment, int method, int level, int raw)); extern int ZEXPORT zipOpenNewFileInZip2_64 OF((zipFile file, const char* filename, const zip_fileinfo* zipfi, const void* extrafield_local, uInt size_extrafield_local, const void* extrafield_global, uInt size_extrafield_global, const char* comment, int method, int level, int raw, int zip64)); /* Same than zipOpenNewFileInZip, except if raw=1, we write raw file */ extern int ZEXPORT zipOpenNewFileInZip3 OF((zipFile file, const char* filename, const zip_fileinfo* zipfi, const void* extrafield_local, uInt size_extrafield_local, const void* extrafield_global, uInt size_extrafield_global, const char* comment, int method, int level, int raw, int windowBits, int memLevel, int strategy, const char* password, uLong crcForCrypting)); extern int ZEXPORT zipOpenNewFileInZip3_64 OF((zipFile file, const char* filename, const zip_fileinfo* zipfi, const void* extrafield_local, uInt size_extrafield_local, const void* extrafield_global, uInt size_extrafield_global, const char* comment, int method, int level, int raw, int windowBits, int memLevel, int strategy, const char* password, uLong crcForCrypting, int zip64 )); /* Same than zipOpenNewFileInZip2, except windowBits,memLevel,,strategy : see parameter strategy in deflateInit2 password : crypting password (NULL for no crypting) crcForCrypting : crc of file to compress (needed for crypting) */ extern int ZEXPORT zipOpenNewFileInZip4 OF((zipFile file, const char* filename, const zip_fileinfo* zipfi, const void* extrafield_local, uInt size_extrafield_local, const void* extrafield_global, uInt size_extrafield_global, const char* comment, int method, int level, int raw, int windowBits, int memLevel, int strategy, const char* password, uLong crcForCrypting, uLong versionMadeBy, uLong flagBase )); extern int ZEXPORT zipOpenNewFileInZip4_64 OF((zipFile file, const char* filename, const zip_fileinfo* zipfi, const void* extrafield_local, uInt size_extrafield_local, const void* extrafield_global, uInt size_extrafield_global, const char* comment, int method, int level, int raw, int windowBits, int memLevel, int strategy, const char* password, uLong crcForCrypting, uLong versionMadeBy, uLong flagBase, int zip64 )); /* Same than zipOpenNewFileInZip4, except versionMadeBy : value for Version made by field flag : value for flag field (compression level info will be added) */ extern int ZEXPORT zipWriteInFileInZip OF((zipFile file, const void* buf, unsigned len)); /* Write data in the zipfile */ extern int ZEXPORT zipCloseFileInZip OF((zipFile file)); /* Close the current file in the zipfile */ extern int ZEXPORT zipCloseFileInZipRaw OF((zipFile file, uLong uncompressed_size, uLong crc32)); extern int ZEXPORT zipCloseFileInZipRaw64 OF((zipFile file, ZPOS64_T uncompressed_size, uLong crc32)); /* Close the current file in the zipfile, for file opened with parameter raw=1 in zipOpenNewFileInZip2 uncompressed_size and crc32 are value for the uncompressed size */ extern int ZEXPORT zipClose OF((zipFile file, const char* global_comment)); /* Close the zipfile */ extern int ZEXPORT zipRemoveExtraInfoBlock OF((char* pData, int* dataLen, short sHeader)); /* zipRemoveExtraInfoBlock - Added by Mathias Svensson Remove extra information block from a extra information data for the local file header or central directory header It is needed to remove ZIP64 extra information blocks when before data is written if using RAW mode. 0x0001 is the signature header for the ZIP64 extra information blocks usage. Remove ZIP64 Extra information from a central director extra field data zipRemoveExtraInfoBlock(pCenDirExtraFieldData, &nCenDirExtraFieldDataLen, 0x0001); Remove ZIP64 Extra information from a Local File Header extra field data zipRemoveExtraInfoBlock(pLocalHeaderExtraFieldData, &nLocalHeaderExtraFieldDataLen, 0x0001); */ /* Added by Sergey A. Tachenov to tweak zipping behaviour. */ extern int ZEXPORT zipSetFlags(zipFile file, unsigned flags); extern int ZEXPORT zipClearFlags(zipFile file, unsigned flags); #ifdef __cplusplus } #endif #endif /* _zip64_H */ connectome-workbench-1.2.3+git41-gc4c6c90/src/Qwt/000077500000000000000000000000001300200146000213175ustar00rootroot00000000000000connectome-workbench-1.2.3+git41-gc4c6c90/src/Qwt/CHANGES-6.1000066400000000000000000000063431300200146000226220ustar00rootroot00000000000000Qwt 6.1.2 ========= 1) Qt 5.4 compatibility - QT_STATIC_CONST "QT_STATIC_CONST" replaced by "static const" 2) build environment - QMAKELIBDIRFLAGS using QMAKELIBDIRFLAGS to avoid conflicts with already installed linux distro packages 3) color maps - QwtLinearColorMap handling of alpha values added - QwtAlphaColorMap basic functinality fixed - QwtPainter QwtPainter::drawColorBar() fixed for semi transparent colors. 4) QwtPlot and friends - QwtLegend layout issue fixed - QwtLogScaleEngine tick duplicates fixed - QwtPainter internal chunk size for drawing polylines with the raster paint engine reduced from 20 to 6 ( faster ). Qwt 6.1.1 ========= 1) build environment - Shadow builds qmake project files to allow shadow builds - pkg-config QwtPkgConfig config option added to qwtconfig.pri to enable generation of pkg-config files ( default setting is: disabled ) 2) scales - QwtDateScaleEngine Aligning/Autoscaling improved. - QwtLinearScaleEngines Trying to avoid overruns for huge intervals ( > max double ) in divideScale(). - QwtAbstractScaleDraw/QwtScaleDraw Layout issues fixed. - QwtRoundScaleDraw Performance for specific use cases improved. 3) controls - QwtAbstractSlider/QwtCounter Fixes for tiny step sizes. - QwtDial/QwtKnob Rounding instead of flooring, when translating values to angles. - QwtKnob/QwtThermo Missing updates, when changing the scale draw. - QwtWheel Aligning of values improved. 4) QwtPlot and friends - QwtPlot Disabling auto-replot in the destructor. QwtPlot::setPlotLayout() fixed. Calculation in QwtPlot::canvasMap fixed for disabled axes. QwtPlot::autoRefresh() when attaching/detaching a plot item. - QwtLegend Clipping code fixed in QwtLegend::renderLegend(). - QwtPlotRenderer Order of setting printer properties changed to work around a Qt 4.8 bug. - QwtGraphic Handling of RenderPensUnscaled flag fixed to respect initial painter transformations ( non cosmetic pens on legend icons will be scaled according to paint device resolution - f.e high dpi printouts - now ). - QwtPainter Painter transformation for simple rich texts fixed. Applying alpha values, when drawing color bars. - QwtPicker Work around a Qt bug when creating an event filter inside of an event filter ( calling the event filter twice ). - QwtPickerMachine Ignoring autorepeated key events. - QwtPlotCurve Paint order of curve pen and brush inverted ( pen on top of brush ). - QwtPlotAbstractBarChart Using layoutHint() as minimum sample width in AutoAdjustSamples mode. - QwtPlotBarChart/QwtPlotMultiBarChart Caluclation of boundingRect() fixed. - QwtPlotScaleItem Internal cache removed to avoid out of sync situations - QwtPlotSpectroCurve Losing alpha values from the color map fixed. - QwtPlotTextLabel No internal caches for record/replay paint devices ( QPicture/QwtGraphic ). - QwtRasterData Handling of NaN values added, when calculating contour lines. - QwtSymbol Pin point translations fixed. connectome-workbench-1.2.3+git41-gc4c6c90/src/Qwt/CMakeLists.txt000066400000000000000000000116351300200146000240650ustar00rootroot00000000000000################################################################ # Qwt Widget Library # Copyright (C) 1997 Josef Wilgen # Copyright (C) 2002 Uwe Rathmann # # This library is free software; you can redistribute it and/or # modify it under the terms of the Qwt License, Version 1.0 ################################################################ # # Name of project # PROJECT (Qwt) # # QT include files # INCLUDE(${QT_USE_FILE}) 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. # # 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 ) # 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 qwt_abstract_legend.h qwt_abstract_scale_draw.h qwt_clipper.h qwt_color_map.h qwt_column_symbol.h qwt_compat.h qwt_curve_fitter.h qwt_date.h qwt_date_scale_draw.h qwt_date_scale_engine.h qwt_dyngrid_layout.h qwt_event_pattern.h qwt_global.h qwt_graphic.h qwt.h qwt_interval.h qwt_interval_symbol.h qwt_legend_data.h qwt_legend.h qwt_legend_label.h qwt_magnifier.h qwt_math.h qwt_matrix_raster_data.h qwt_null_paintdevice.h qwt_painter_command.h qwt_painter.h qwt_panner.h qwt_picker.h qwt_picker_machine.h qwt_pixel_matrix.h qwt_plot_abstract_barchart.h qwt_plot_barchart.h qwt_plot_canvas.h qwt_plot_curve.h qwt_plot_dict.h qwt_plot_directpainter.h qwt_plot_grid.h qwt_plot.h qwt_plot_histogram.h qwt_plot_intervalcurve.h qwt_plot_item.h qwt_plot_layout.h qwt_plot_legenditem.h qwt_plot_magnifier.h qwt_plot_marker.h qwt_plot_multi_barchart.h qwt_plot_panner.h qwt_plot_picker.h qwt_plot_rasteritem.h qwt_plot_renderer.h qwt_plot_rescaler.h qwt_plot_scaleitem.h qwt_plot_seriesitem.h qwt_plot_shapeitem.h qwt_plot_spectrocurve.h qwt_plot_spectrogram.h qwt_plot_textlabel.h qwt_plot_tradingcurve.h qwt_plot_zoneitem.h qwt_plot_zoomer.h qwt_point_3d.h qwt_point_data.h qwt_point_mapper.h qwt_point_polar.h qwt_raster_data.h qwt_round_scale_draw.h qwt_samples.h qwt_sampling_thread.h qwt_scale_div.h qwt_scale_draw.h qwt_scale_engine.h qwt_scale_map.h qwt_scale_widget.h qwt_series_data.h qwt_series_store.h qwt_spline.h qwt_symbol.h qwt_system_clock.h qwt_text_engine.h qwt_text.h qwt_text_label.h qwt_transform.h qwt_widget_overlay.h qwt_abstract_legend.cpp qwt_abstract_scale_draw.cpp qwt_clipper.cpp qwt_color_map.cpp qwt_column_symbol.cpp qwt_curve_fitter.cpp qwt_date.cpp qwt_date_scale_draw.cpp qwt_date_scale_engine.cpp qwt_dyngrid_layout.cpp qwt_event_pattern.cpp qwt_graphic.cpp qwt_interval.cpp qwt_interval_symbol.cpp qwt_legend.cpp qwt_legend_data.cpp qwt_legend_label.cpp qwt_magnifier.cpp qwt_math.cpp qwt_matrix_raster_data.cpp qwt_null_paintdevice.cpp qwt_painter_command.cpp qwt_painter.cpp qwt_panner.cpp qwt_picker.cpp qwt_picker_machine.cpp qwt_pixel_matrix.cpp qwt_plot_abstract_barchart.cpp qwt_plot_axis.cpp qwt_plot_barchart.cpp qwt_plot_canvas.cpp qwt_plot.cpp qwt_plot_curve.cpp qwt_plot_dict.cpp qwt_plot_directpainter.cpp qwt_plot_grid.cpp qwt_plot_histogram.cpp qwt_plot_intervalcurve.cpp qwt_plot_item.cpp qwt_plot_layout.cpp qwt_plot_legenditem.cpp qwt_plot_magnifier.cpp qwt_plot_marker.cpp qwt_plot_multi_barchart.cpp qwt_plot_panner.cpp qwt_plot_picker.cpp qwt_plot_rasteritem.cpp qwt_plot_renderer.cpp qwt_plot_rescaler.cpp qwt_plot_scaleitem.cpp qwt_plot_seriesitem.cpp qwt_plot_shapeitem.cpp qwt_plot_spectrocurve.cpp qwt_plot_spectrogram.cpp qwt_plot_textlabel.cpp qwt_plot_tradingcurve.cpp qwt_plot_xml.cpp qwt_plot_zoneitem.cpp qwt_plot_zoomer.cpp qwt_point_3d.cpp qwt_point_data.cpp qwt_point_mapper.cpp qwt_point_polar.cpp qwt_raster_data.cpp qwt_round_scale_draw.cpp qwt_sampling_thread.cpp qwt_scale_div.cpp qwt_scale_draw.cpp qwt_scale_engine.cpp qwt_scale_map.cpp qwt_scale_widget.cpp qwt_series_data.cpp qwt_spline.cpp qwt_symbol.cpp qwt_system_clock.cpp qwt_text.cpp qwt_text_engine.cpp qwt_text_label.cpp qwt_transform.cpp 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} ) SET(Qwt_LIBRARIES Qwt PARENT_SCOPE) SET(Qwt_INCLUDE_DIRS ${CMAKE_CURRENT_SOURCE_DIR} PARENT_SCOPE) connectome-workbench-1.2.3+git41-gc4c6c90/src/Qwt/COPYING000066400000000000000000000664521300200146000223670ustar00rootroot00000000000000 Qwt License Version 1.0, January 1, 2003 The Qwt library and included programs are provided under the terms of the GNU LESSER GENERAL PUBLIC LICENSE (LGPL) with the following exceptions: 1. Widgets that are subclassed from Qwt widgets do not constitute a derivative work. 2. Static linking of applications and widgets to the Qwt library does not constitute a derivative work and does not require the author to provide source code for the application or widget, use the shared Qwt libraries, or link their applications or widgets against a user-supplied version of Qwt. If you link the application or widget to a modified version of Qwt, then the changes to Qwt must be provided under the terms of the LGPL in sections 1, 2, and 4. 3. You do not have to provide a copy of the Qwt license with programs that are linked to the Qwt library, nor do you have to identify the Qwt license in your program or documentation as required by section 6 of the LGPL. However, programs must still identify their use of Qwt. The following example statement can be included in user documentation to satisfy this requirement: [program/widget] is based in part on the work of the Qwt project (http://qwt.sf.net). ---------------------------------------------------------------------- GNU LESSER GENERAL PUBLIC LICENSE Version 2.1, February 1999 Copyright (C) 1991, 1999 Free Software Foundation, Inc. 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. [This is the first released version of the Lesser GPL. It also counts as the successor of the GNU Library Public License, version 2, hence the version number 2.1.] Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public Licenses are intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This license, the Lesser General Public License, applies to some specially designated software packages--typically libraries--of the Free Software Foundation and other authors who decide to use it. You can use it too, but we suggest you first think carefully about whether this license or the ordinary General Public License is the better strategy to use in any particular case, based on the explanations below. When we speak of free software, we are referring to freedom of use, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish); that you receive source code or can get it if you want it; that you can change the software and use pieces of it in new free programs; and that you are informed that you can do these things. To protect your rights, we need to make restrictions that forbid distributors to deny you these rights or to ask you to surrender these rights. These restrictions translate to certain responsibilities for you if you distribute copies of the library or if you modify it. For example, if you distribute copies of the library, whether gratis or for a fee, you must give the recipients all the rights that we gave you. You must make sure that they, too, receive or can get the source code. If you link other code with the library, you must provide complete object files to the recipients, so that they can relink them with the library after making changes to the library and recompiling it. And you must show them these terms so they know their rights. We protect your rights with a two-step method: (1) we copyright the library, and (2) we offer you this license, which gives you legal permission to copy, distribute and/or modify the library. To protect each distributor, we want to make it very clear that there is no warranty for the free library. Also, if the library is modified by someone else and passed on, the recipients should know that what they have is not the original version, so that the original author's reputation will not be affected by problems that might be introduced by others. Finally, software patents pose a constant threat to the existence of any free program. We wish to make sure that a company cannot effectively restrict the users of a free program by obtaining a restrictive license from a patent holder. Therefore, we insist that any patent license obtained for a version of the library must be consistent with the full freedom of use specified in this license. Most GNU software, including some libraries, is covered by the ordinary GNU General Public License. This license, the GNU Lesser General Public License, applies to certain designated libraries, and is quite different from the ordinary General Public License. We use this license for certain libraries in order to permit linking those libraries into non-free programs. When a program is linked with a library, whether statically or using a shared library, the combination of the two is legally speaking a combined work, a derivative of the original library. The ordinary General Public License therefore permits such linking only if the entire combination fits its criteria of freedom. The Lesser General Public License permits more lax criteria for linking other code with the library. We call this license the "Lesser" General Public License because it does Less to protect the user's freedom than the ordinary General Public License. It also provides other free software developers Less of an advantage over competing non-free programs. These disadvantages are the reason we use the ordinary General Public License for many libraries. However, the Lesser license provides advantages in certain special circumstances. For example, on rare occasions, there may be a special need to encourage the widest possible use of a certain library, so that it becomes a de-facto standard. To achieve this, non-free programs must be allowed to use the library. A more frequent case is that a free library does the same job as widely used non-free libraries. In this case, there is little to gain by limiting the free library to free software only, so we use the Lesser General Public License. In other cases, permission to use a particular library in non-free programs enables a greater number of people to use a large body of free software. For example, permission to use the GNU C Library in non-free programs enables many more people to use the whole GNU operating system, as well as its variant, the GNU/Linux operating system. Although the Lesser General Public License is Less protective of the users' freedom, it does ensure that the user of a program that is linked with the Library has the freedom and the wherewithal to run that program using a modified version of the Library. The precise terms and conditions for copying, distribution and modification follow. Pay close attention to the difference between a "work based on the library" and a "work that uses the library". The former contains code derived from the library, whereas the latter must be combined with the library in order to run. GNU LESSER GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License Agreement applies to any software library or other program which contains a notice placed by the copyright holder or other authorized party saying it may be distributed under the terms of this Lesser General Public License (also called "this License"). Each licensee is addressed as "you". A "library" means a collection of software functions and/or data prepared so as to be conveniently linked with application programs (which use some of those functions and data) to form executables. The "Library", below, refers to any such software library or work which has been distributed under these terms. A "work based on the Library" means either the Library or any derivative work under copyright law: that is to say, a work containing the Library or a portion of it, either verbatim or with modifications and/or translated straightforwardly into another language. (Hereinafter, translation is included without limitation in the term "modification".) "Source code" for a work means the preferred form of the work for making modifications to it. For a library, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the library. Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running a program using the Library is not restricted, and output from such a program is covered only if its contents constitute a work based on the Library (independent of the use of the Library in a tool for writing it). Whether that is true depends on what the Library does and what the program that uses the Library does. 1. You may copy and distribute verbatim copies of the Library's complete source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and distribute a copy of this License along with the Library. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Library or any portion of it, thus forming a work based on the Library, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) The modified work must itself be a software library. b) You must cause the files modified to carry prominent notices stating that you changed the files and the date of any change. c) You must cause the whole of the work to be licensed at no charge to all third parties under the terms of this License. d) If a facility in the modified Library refers to a function or a table of data to be supplied by an application program that uses the facility, other than as an argument passed when the facility is invoked, then you must make a good faith effort to ensure that, in the event an application does not supply such function or table, the facility still operates, and performs whatever part of its purpose remains meaningful. (For example, a function in a library to compute square roots has a purpose that is entirely well-defined independent of the application. Therefore, Subsection 2d requires that any application-supplied function or table used by this function must be optional: if the application does not supply it, the square root function must still compute square roots.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Library, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Library, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Library. In addition, mere aggregation of another work not based on the Library with the Library (or with a work based on the Library) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may opt to apply the terms of the ordinary GNU General Public License instead of this License to a given copy of the Library. To do this, you must alter all the notices that refer to this License, so that they refer to the ordinary GNU General Public License, version 2, instead of to this License. (If a newer version than version 2 of the ordinary GNU General Public License has appeared, then you can specify that version instead if you wish.) Do not make any other change in these notices. Once this change is made in a given copy, it is irreversible for that copy, so the ordinary GNU General Public License applies to all subsequent copies and derivative works made from that copy. This option is useful when you wish to copy part of the code of the Library into a program that is not a library. 4. You may copy and distribute the Library (or a portion or derivative of it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange. If distribution of object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place satisfies the requirement to distribute the source code, even though third parties are not compelled to copy the source along with the object code. 5. A program that contains no derivative of any portion of the Library, but is designed to work with the Library by being compiled or linked with it, is called a "work that uses the Library". Such a work, in isolation, is not a derivative work of the Library, and therefore falls outside the scope of this License. However, linking a "work that uses the Library" with the Library creates an executable that is a derivative of the Library (because it contains portions of the Library), rather than a "work that uses the library". The executable is therefore covered by this License. Section 6 states terms for distribution of such executables. When a "work that uses the Library" uses material from a header file that is part of the Library, the object code for the work may be a derivative work of the Library even though the source code is not. Whether this is true is especially significant if the work can be linked without the Library, or if the work is itself a library. The threshold for this to be true is not precisely defined by law. If such an object file uses only numerical parameters, data structure layouts and accessors, and small macros and small inline functions (ten lines or less in length), then the use of the object file is unrestricted, regardless of whether it is legally a derivative work. (Executables containing this object code plus portions of the Library will still fall under Section 6.) Otherwise, if the work is a derivative of the Library, you may distribute the object code for the work under the terms of Section 6. Any executables containing that work also fall under Section 6, whether or not they are linked directly with the Library itself. 6. As an exception to the Sections above, you may also combine or link a "work that uses the Library" with the Library to produce a work containing portions of the Library, and distribute that work under terms of your choice, provided that the terms permit modification of the work for the customer's own use and reverse engineering for debugging such modifications. You must give prominent notice with each copy of the work that the Library is used in it and that the Library and its use are covered by this License. You must supply a copy of this License. If the work during execution displays copyright notices, you must include the copyright notice for the Library among them, as well as a reference directing the user to the copy of this License. Also, you must do one of these things: a) Accompany the work with the complete corresponding machine-readable source code for the Library including whatever changes were used in the work (which must be distributed under Sections 1 and 2 above); and, if the work is an executable linked with the Library, with the complete machine-readable "work that uses the Library", as object code and/or source code, so that the user can modify the Library and then relink to produce a modified executable containing the modified Library. (It is understood that the user who changes the contents of definitions files in the Library will not necessarily be able to recompile the application to use the modified definitions.) b) Use a suitable shared library mechanism for linking with the Library. A suitable mechanism is one that (1) uses at run time a copy of the library already present on the user's computer system, rather than copying library functions into the executable, and (2) will operate properly with a modified version of the library, if the user installs one, as long as the modified version is interface-compatible with the version that the work was made with. c) Accompany the work with a written offer, valid for at least three years, to give the same user the materials specified in Subsection 6a, above, for a charge no more than the cost of performing this distribution. d) If distribution of the work is made by offering access to copy from a designated place, offer equivalent access to copy the above specified materials from the same place. e) Verify that the user has already received a copy of these materials or that you have already sent this user a copy. For an executable, the required form of the "work that uses the Library" must include any data and utility programs needed for reproducing the executable from it. However, as a special exception, the materials to be distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. It may happen that this requirement contradicts the license restrictions of other proprietary libraries that do not normally accompany the operating system. Such a contradiction means you cannot use both them and the Library together in an executable that you distribute. 7. You may place library facilities that are a work based on the Library side-by-side in a single library together with other library facilities not covered by this License, and distribute such a combined library, provided that the separate distribution of the work based on the Library and of the other library facilities is otherwise permitted, and provided that you do these two things: a) Accompany the combined library with a copy of the same work based on the Library, uncombined with any other library facilities. This must be distributed under the terms of the Sections above. b) Give prominent notice with the combined library of the fact that part of it is a work based on the Library, and explaining where to find the accompanying uncombined form of the same work. 8. You may not copy, modify, sublicense, link with, or distribute the Library except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense, link with, or distribute the Library is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 9. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Library or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Library (or any work based on the Library), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Library or works based on it. 10. Each time you redistribute the Library (or any work based on the Library), the recipient automatically receives a license from the original licensor to copy, distribute, link with or modify the Library subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties with this License. 11. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Library at all. For example, if a patent license would not permit royalty-free redistribution of the Library by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Library. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply, and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 12. If the distribution and/or use of the Library is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Library under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 13. The Free Software Foundation may publish revised and/or new versions of the Lesser General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Library specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Library does not specify a license version number, you may choose any version ever published by the Free Software Foundation. 14. If you wish to incorporate parts of the Library into other free programs whose distribution conditions are incompatible with these, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Libraries If you develop a new library, and you want it to be of the greatest possible use to the public, we recommend making it free software that everyone can redistribute and change. You can do so by permitting redistribution under these terms (or, alternatively, under the terms of the ordinary General Public License). To apply these terms, attach the following notices to the library. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA Also add information on how to contact you by electronic and paper mail. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the library, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the library `Frob' (a library for tweaking knobs) written by James Random Hacker. , 1 April 1990 Ty Coon, President of Vice That's all there is to it! connectome-workbench-1.2.3+git41-gc4c6c90/src/Qwt/README000066400000000000000000000016221300200146000222000ustar00rootroot00000000000000 The Qwt Widget Library ---------------------- Qwt is an extension to the libraries of the Qt Project. The Qwt library contains widgets and components which are primarily useful for technical and scientifical purposes. It includes a 2-D plotting widget, different kinds of sliders, and much more. Qwt is hosted at http://qwt.sf.net Installation ------------ Read INSTALL how to build and install Qwt. Copyright --------- Qwt Widget Library Copyright (C) 1997 Josef Wilgen Copyright (C) 2002 Uwe Rathmann Qwt is published under the Qwt License, Version 1.0. You should have received a copy of this licence in the file COPYING. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. connectome-workbench-1.2.3+git41-gc4c6c90/src/Qwt/README_MODIFY_QWT_FOR_WORKBENCH000066400000000000000000000027651300200146000261230ustar00rootroot00000000000000Setting up QWT for use with workbench In QWT's 'src' directory: Examine src.pro A configuration item (QWT_CONFIG) allow selective building of items. There are some HEADERS and SOURCES that we need. These are the HEADERS and SOURCES not controlled with QWT_CONFIG, and QwtPlot. QwtSvg and QwtWidgets are NOT needed to copy part of the src.pro file to create a command for deleting these unneeded files. Something like this: #!/bin/sh rm \ qwt_plot_svgitem.h \ qwt_plot_svgitem.cpp rm \ qwt_abstract_slider.h \ qwt_abstract_scale.h \ qwt_arrow_button.h \ qwt_analog_clock.h \ qwt_compass.h \ qwt_compass_rose.h \ qwt_counter.h \ qwt_dial.h \ qwt_dial_needle.h \ qwt_double_range.h \ qwt_knob.h \ qwt_slider.h \ qwt_thermo.h \ qwt_wheel.h rm \ qwt_abstract_slider.cpp \ qwt_abstract_scale.cpp \ qwt_arrow_button.cpp \ qwt_analog_clock.cpp \ qwt_compass.cpp \ qwt_compass_rose.cpp \ qwt_counter.cpp \ qwt_dial.cpp \ qwt_dial_needle.cpp \ qwt_double_range.cpp \ qwt_knob.cpp \ qwt_slider.cpp \ qwt_thermo.cpp \ qwt_wheel.cpp Next, copy the src.pro to CMakeLists.txt. Run "grep --files-with-matches Q_OBJECT *.h" to find the files that need to go in the MOC_INPUT_HEADER_FILES section of the CMakeLists.txt file. Next place all headers and CPP files into the SOURCE_FILES section. connectome-workbench-1.2.3+git41-gc4c6c90/src/Qwt/qwt.h000066400000000000000000000007761300200146000223150ustar00rootroot00000000000000/* -*- mode: C++ ; c-file-style: "stroustrup" -*- ***************************** * Qwt Widget Library * Copyright (C) 1997 Josef Wilgen * Copyright (C) 2002 Uwe Rathmann * * This library is free software; you can redistribute it and/or * modify it under the terms of the Qwt License, Version 1.0 *****************************************************************************/ #ifndef QWT_H #define QWT_H #include "qwt_global.h" /*! Some constants for use within Qwt. */ namespace Qwt { } #endif connectome-workbench-1.2.3+git41-gc4c6c90/src/Qwt/qwt_abstract_legend.cpp000066400000000000000000000016411300200146000260410ustar00rootroot00000000000000/* -*- mode: C++ ; c-file-style: "stroustrup" -*- ***************************** * Qwt Widget Library * Copyright (C) 1997 Josef Wilgen * Copyright (C) 2002 Uwe Rathmann * * This library is free software; you can redistribute it and/or * modify it under the terms of the Qwt License, Version 1.0 *****************************************************************************/ #include "qwt_abstract_legend.h" /*! Constructor \param parent Parent widget */ QwtAbstractLegend::QwtAbstractLegend( QWidget *parent ): QFrame( parent ) { } //! Destructor QwtAbstractLegend::~QwtAbstractLegend() { } /*! Return the extent, that is needed for elements to scroll the legend ( usually scrollbars ), \param orientation Orientation \return Extent of the corresponding scroll element */ int QwtAbstractLegend::scrollExtent( Qt::Orientation orientation ) const { Q_UNUSED( orientation ); return 0; } connectome-workbench-1.2.3+git41-gc4c6c90/src/Qwt/qwt_abstract_legend.h000066400000000000000000000037631300200146000255150ustar00rootroot00000000000000/* -*- mode: C++ ; c-file-style: "stroustrup" -*- ***************************** * Qwt Widget Library * Copyright (C) 1997 Josef Wilgen * Copyright (C) 2002 Uwe Rathmann * * This library is free software; you can redistribute it and/or * modify it under the terms of the Qwt License, Version 1.0 *****************************************************************************/ #ifndef QWT_ABSTRACT_LEGEND_H #define QWT_ABSTRACT_LEGEND_H #include "qwt_global.h" #include "qwt_legend_data.h" #include #include class QVariant; /*! \brief Abstract base class for legend widgets Legends, that need to be under control of the QwtPlot layout system need to be derived from QwtAbstractLegend. \note Other type of legends can be implemented by connecting to the QwtPlot::legendDataChanged() signal. But as these legends are unknown to the plot layout system the layout code ( on screen and for QwtPlotRenderer ) need to be organized in application code. \sa QwtLegend */ class QWT_EXPORT QwtAbstractLegend : public QFrame { Q_OBJECT public: explicit QwtAbstractLegend( QWidget *parent = NULL ); virtual ~QwtAbstractLegend(); /*! Render the legend into a given rectangle. \param painter Painter \param rect Bounding rectangle \param fillBackground When true, fill rect with the widget background \sa renderLegend() is used by QwtPlotRenderer */ virtual void renderLegend( QPainter *painter, const QRectF &rect, bool fillBackground ) const = 0; //! \return True, when no plot item is inserted virtual bool isEmpty() const = 0; virtual int scrollExtent( Qt::Orientation ) const; public Q_SLOTS: /*! \brief Update the entries for a plot item \param itemInfo Info about an item \param data List of legend entry attributes for the item */ virtual void updateLegend( const QVariant &itemInfo, const QList &data ) = 0; }; #endif connectome-workbench-1.2.3+git41-gc4c6c90/src/Qwt/qwt_abstract_scale_draw.cpp000066400000000000000000000231241300200146000267070ustar00rootroot00000000000000/* -*- mode: C++ ; c-file-style: "stroustrup" -*- ***************************** * Qwt Widget Library * Copyright (C) 1997 Josef Wilgen * Copyright (C) 2002 Uwe Rathmann * * This library is free software; you can redistribute it and/or * modify it under the terms of the Qwt License, Version 1.0 *****************************************************************************/ #include "qwt_abstract_scale_draw.h" #include "qwt_math.h" #include "qwt_text.h" #include "qwt_painter.h" #include "qwt_scale_map.h" #include #include #include #include class QwtAbstractScaleDraw::PrivateData { public: PrivateData(): spacing( 4.0 ), penWidth( 0 ), minExtent( 0.0 ) { components = QwtAbstractScaleDraw::Backbone | QwtAbstractScaleDraw::Ticks | QwtAbstractScaleDraw::Labels; tickLength[QwtScaleDiv::MinorTick] = 4.0; tickLength[QwtScaleDiv::MediumTick] = 6.0; tickLength[QwtScaleDiv::MajorTick] = 8.0; } ScaleComponents components; QwtScaleMap map; QwtScaleDiv scaleDiv; double spacing; double tickLength[QwtScaleDiv::NTickTypes]; int penWidth; double minExtent; QMap labelCache; }; /*! \brief Constructor The range of the scale is initialized to [0, 100], The spacing (distance between ticks and labels) is set to 4, the tick lengths are set to 4,6 and 8 pixels */ QwtAbstractScaleDraw::QwtAbstractScaleDraw() { d_data = new QwtAbstractScaleDraw::PrivateData; } //! Destructor QwtAbstractScaleDraw::~QwtAbstractScaleDraw() { delete d_data; } /*! En/Disable a component of the scale \param component Scale component \param enable On/Off \sa hasComponent() */ void QwtAbstractScaleDraw::enableComponent( ScaleComponent component, bool enable ) { if ( enable ) d_data->components |= component; else d_data->components &= ~component; } /*! Check if a component is enabled \param component Component type \return true, when component is enabled \sa enableComponent() */ bool QwtAbstractScaleDraw::hasComponent( ScaleComponent component ) const { return ( d_data->components & component ); } /*! Change the scale division \param scaleDiv New scale division */ void QwtAbstractScaleDraw::setScaleDiv( const QwtScaleDiv &scaleDiv ) { d_data->scaleDiv = scaleDiv; d_data->map.setScaleInterval( scaleDiv.lowerBound(), scaleDiv.upperBound() ); d_data->labelCache.clear(); } /*! Change the transformation of the scale \param transformation New scale transformation */ void QwtAbstractScaleDraw::setTransformation( QwtTransform *transformation ) { d_data->map.setTransformation( transformation ); } //! \return Map how to translate between scale and pixel values const QwtScaleMap &QwtAbstractScaleDraw::scaleMap() const { return d_data->map; } //! \return Map how to translate between scale and pixel values QwtScaleMap &QwtAbstractScaleDraw::scaleMap() { return d_data->map; } //! \return scale division const QwtScaleDiv& QwtAbstractScaleDraw::scaleDiv() const { return d_data->scaleDiv; } /*! \brief Specify the width of the scale pen \param width Pen width \sa penWidth() */ void QwtAbstractScaleDraw::setPenWidth( int width ) { if ( width < 0 ) width = 0; if ( width != d_data->penWidth ) d_data->penWidth = width; } /*! \return Scale pen width \sa setPenWidth() */ int QwtAbstractScaleDraw::penWidth() const { return d_data->penWidth; } /*! \brief Draw the scale \param painter The painter \param palette Palette, text color is used for the labels, foreground color for ticks and backbone */ void QwtAbstractScaleDraw::draw( QPainter *painter, const QPalette& palette ) const { painter->save(); QPen pen = painter->pen(); pen.setWidth( d_data->penWidth ); pen.setCosmetic( false ); painter->setPen( pen ); if ( hasComponent( QwtAbstractScaleDraw::Labels ) ) { painter->save(); painter->setPen( palette.color( QPalette::Text ) ); // ignore pen style const QList &majorTicks = d_data->scaleDiv.ticks( QwtScaleDiv::MajorTick ); for ( int i = 0; i < majorTicks.count(); i++ ) { const double v = majorTicks[i]; if ( d_data->scaleDiv.contains( v ) ) drawLabel( painter, v ); } painter->restore(); } if ( hasComponent( QwtAbstractScaleDraw::Ticks ) ) { painter->save(); QPen pen = painter->pen(); pen.setColor( palette.color( QPalette::WindowText ) ); pen.setCapStyle( Qt::FlatCap ); painter->setPen( pen ); for ( int tickType = QwtScaleDiv::MinorTick; tickType < QwtScaleDiv::NTickTypes; tickType++ ) { const double tickLen = d_data->tickLength[tickType]; if ( tickLen <= 0.0 ) continue; const QList &ticks = d_data->scaleDiv.ticks( tickType ); for ( int i = 0; i < ticks.count(); i++ ) { const double v = ticks[i]; if ( d_data->scaleDiv.contains( v ) ) drawTick( painter, v, tickLen ); } } painter->restore(); } if ( hasComponent( QwtAbstractScaleDraw::Backbone ) ) { painter->save(); QPen pen = painter->pen(); pen.setColor( palette.color( QPalette::WindowText ) ); pen.setCapStyle( Qt::FlatCap ); painter->setPen( pen ); drawBackbone( painter ); painter->restore(); } painter->restore(); } /*! \brief Set the spacing between tick and labels The spacing is the distance between ticks and labels. The default spacing is 4 pixels. \param spacing Spacing \sa spacing() */ void QwtAbstractScaleDraw::setSpacing( double spacing ) { if ( spacing < 0 ) spacing = 0; d_data->spacing = spacing; } /*! \brief Get the spacing The spacing is the distance between ticks and labels. The default spacing is 4 pixels. \return Spacing \sa setSpacing() */ double QwtAbstractScaleDraw::spacing() const { return d_data->spacing; } /*! \brief Set a minimum for the extent The extent is calculated from the components of the scale draw. In situations, where the labels are changing and the layout depends on the extent (f.e scrolling a scale), setting an upper limit as minimum extent will avoid jumps of the layout. \param minExtent Minimum extent \sa extent(), minimumExtent() */ void QwtAbstractScaleDraw::setMinimumExtent( double minExtent ) { if ( minExtent < 0.0 ) minExtent = 0.0; d_data->minExtent = minExtent; } /*! Get the minimum extent \return Minimum extent \sa extent(), setMinimumExtent() */ double QwtAbstractScaleDraw::minimumExtent() const { return d_data->minExtent; } /*! Set the length of the ticks \param tickType Tick type \param length New length \warning the length is limited to [0..1000] */ void QwtAbstractScaleDraw::setTickLength( QwtScaleDiv::TickType tickType, double length ) { if ( tickType < QwtScaleDiv::MinorTick || tickType > QwtScaleDiv::MajorTick ) { return; } if ( length < 0.0 ) length = 0.0; const double maxTickLen = 1000.0; if ( length > maxTickLen ) length = maxTickLen; d_data->tickLength[tickType] = length; } /*! \return Length of the ticks \sa setTickLength(), maxTickLength() */ double QwtAbstractScaleDraw::tickLength( QwtScaleDiv::TickType tickType ) const { if ( tickType < QwtScaleDiv::MinorTick || tickType > QwtScaleDiv::MajorTick ) { return 0; } return d_data->tickLength[tickType]; } /*! \return Length of the longest tick Useful for layout calculations \sa tickLength(), setTickLength() */ double QwtAbstractScaleDraw::maxTickLength() const { double length = 0.0; for ( int i = 0; i < QwtScaleDiv::NTickTypes; i++ ) length = qMax( length, d_data->tickLength[i] ); return length; } /*! \brief Convert a value into its representing label The value is converted to a plain text using QLocale().toString(value). This method is often overloaded by applications to have individual labels. \param value Value \return Label string. */ QwtText QwtAbstractScaleDraw::label( double value ) const { return QLocale().toString( value ); } /*! \brief Convert a value into its representing label and cache it. The conversion between value and label is called very often in the layout and painting code. Unfortunately the calculation of the label sizes might be slow (really slow for rich text in Qt4), so it's necessary to cache the labels. \param font Font \param value Value \return Tick label */ const QwtText &QwtAbstractScaleDraw::tickLabel( const QFont &font, double value ) const { QMap::const_iterator it = d_data->labelCache.find( value ); if ( it == d_data->labelCache.end() ) { QwtText lbl = label( value ); lbl.setRenderFlags( 0 ); lbl.setLayoutAttribute( QwtText::MinimumLayout ); ( void )lbl.textSize( font ); // initialize the internal cache it = d_data->labelCache.insert( value, lbl ); } return ( *it ); } /*! Invalidate the cache used by tickLabel() The cache is invalidated, when a new QwtScaleDiv is set. If the labels need to be changed. while the same QwtScaleDiv is set, invalidateCache() needs to be called manually. */ void QwtAbstractScaleDraw::invalidateCache() { d_data->labelCache.clear(); } connectome-workbench-1.2.3+git41-gc4c6c90/src/Qwt/qwt_abstract_scale_draw.h000066400000000000000000000070361300200146000263600ustar00rootroot00000000000000/* -*- mode: C++ ; c-file-style: "stroustrup" -*- ***************************** * Qwt Widget Library * Copyright (C) 1997 Josef Wilgen * Copyright (C) 2002 Uwe Rathmann * * This library is free software; you can redistribute it and/or * modify it under the terms of the Qwt License, Version 1.0 *****************************************************************************/ #ifndef QWT_ABSTRACT_SCALE_DRAW_H #define QWT_ABSTRACT_SCALE_DRAW_H #include "qwt_global.h" #include "qwt_scale_div.h" #include "qwt_text.h" class QPalette; class QPainter; class QFont; class QwtTransform; class QwtScaleMap; /*! \brief A abstract base class for drawing scales QwtAbstractScaleDraw can be used to draw linear or logarithmic scales. After a scale division has been specified as a QwtScaleDiv object using setScaleDiv(), the scale can be drawn with the draw() member. */ class QWT_EXPORT QwtAbstractScaleDraw { public: /*! Components of a scale \sa enableComponent(), hasComponent */ enum ScaleComponent { //! Backbone = the line where the ticks are located Backbone = 0x01, //! Ticks Ticks = 0x02, //! Labels Labels = 0x04 }; //! Scale components typedef QFlags ScaleComponents; QwtAbstractScaleDraw(); virtual ~QwtAbstractScaleDraw(); void setScaleDiv( const QwtScaleDiv &s ); const QwtScaleDiv& scaleDiv() const; void setTransformation( QwtTransform * ); const QwtScaleMap &scaleMap() const; QwtScaleMap &scaleMap(); void enableComponent( ScaleComponent, bool enable = true ); bool hasComponent( ScaleComponent ) const; void setTickLength( QwtScaleDiv::TickType, double length ); double tickLength( QwtScaleDiv::TickType ) const; double maxTickLength() const; void setSpacing( double margin ); double spacing() const; void setPenWidth( int width ); int penWidth() const; virtual void draw( QPainter *, const QPalette & ) const; virtual QwtText label( double ) const; /*! Calculate the extent The extent is the distance from the baseline to the outermost pixel of the scale draw in opposite to its orientation. It is at least minimumExtent() pixels. \param font Font used for drawing the tick labels \return Number of pixels \sa setMinimumExtent(), minimumExtent() */ virtual double extent( const QFont &font ) const = 0; void setMinimumExtent( double ); double minimumExtent() const; protected: /*! Draw a tick \param painter Painter \param value Value of the tick \param len Length of the tick \sa drawBackbone(), drawLabel() */ virtual void drawTick( QPainter *painter, double value, double len ) const = 0; /*! Draws the baseline of the scale \param painter Painter \sa drawTick(), drawLabel() */ virtual void drawBackbone( QPainter *painter ) const = 0; /*! Draws the label for a major scale tick \param painter Painter \param value Value \sa drawTick(), drawBackbone() */ virtual void drawLabel( QPainter *painter, double value ) const = 0; void invalidateCache(); const QwtText &tickLabel( const QFont &, double value ) const; private: QwtAbstractScaleDraw( const QwtAbstractScaleDraw & ); QwtAbstractScaleDraw &operator=( const QwtAbstractScaleDraw & ); class PrivateData; PrivateData *d_data; }; Q_DECLARE_OPERATORS_FOR_FLAGS( QwtAbstractScaleDraw::ScaleComponents ) #endif connectome-workbench-1.2.3+git41-gc4c6c90/src/Qwt/qwt_clipper.cpp000066400000000000000000000310101300200146000243470ustar00rootroot00000000000000/* -*- mode: C++ ; c-file-style: "stroustrup" -*- ***************************** * Qwt Widget Library * Copyright (C) 1997 Josef Wilgen * Copyright (C) 2002 Uwe Rathmann * * This library is free software; you can redistribute it and/or * modify it under the terms of the Qwt License, Version 1.0 *****************************************************************************/ #include "qwt_clipper.h" #include "qwt_point_polar.h" #include #include #include #if QT_VERSION < 0x040601 #define qAtan(x) ::atan(x) #endif namespace QwtClip { // some templates used for inlining template class LeftEdge; template class RightEdge; template class TopEdge; template class BottomEdge; template class PointBuffer; } template class QwtClip::LeftEdge { public: inline LeftEdge( Value x1, Value, Value, Value ): d_x1( x1 ) { } inline bool isInside( const Point &p ) const { return p.x() >= d_x1; } inline Point intersection( const Point &p1, const Point &p2 ) const { double dy = ( p1.y() - p2.y() ) / double( p1.x() - p2.x() ); return Point( d_x1, static_cast< Value >( p2.y() + ( d_x1 - p2.x() ) * dy ) ); } private: const Value d_x1; }; template class QwtClip::RightEdge { public: inline RightEdge( Value, Value x2, Value, Value ): d_x2( x2 ) { } inline bool isInside( const Point &p ) const { return p.x() <= d_x2; } inline Point intersection( const Point &p1, const Point &p2 ) const { double dy = ( p1.y() - p2.y() ) / double( p1.x() - p2.x() ); return Point( d_x2, static_cast( p2.y() + ( d_x2 - p2.x() ) * dy ) ); } private: const Value d_x2; }; template class QwtClip::TopEdge { public: inline TopEdge( Value, Value, Value y1, Value ): d_y1( y1 ) { } inline bool isInside( const Point &p ) const { return p.y() >= d_y1; } inline Point intersection( const Point &p1, const Point &p2 ) const { double dx = ( p1.x() - p2.x() ) / double( p1.y() - p2.y() ); return Point( static_cast( p2.x() + ( d_y1 - p2.y() ) * dx ), d_y1 ); } private: const Value d_y1; }; template class QwtClip::BottomEdge { public: inline BottomEdge( Value, Value, Value, Value y2 ): d_y2( y2 ) { } inline bool isInside( const Point &p ) const { return p.y() <= d_y2; } inline Point intersection( const Point &p1, const Point &p2 ) const { double dx = ( p1.x() - p2.x() ) / double( p1.y() - p2.y() ); return Point( static_cast( p2.x() + ( d_y2 - p2.y() ) * dx ), d_y2 ); } private: const Value d_y2; }; template class QwtClip::PointBuffer { public: PointBuffer( int capacity = 0 ): m_capacity( 0 ), m_size( 0 ), m_buffer( NULL ) { if ( capacity > 0 ) reserve( capacity ); } ~PointBuffer() { if ( m_buffer ) ::free( m_buffer ); } inline void setPoints( int numPoints, const Point *points ) { reserve( numPoints ); m_size = numPoints; ::memcpy( m_buffer, points, m_size * sizeof( Point ) ); } inline void reset() { m_size = 0; } inline int size() const { return m_size; } inline Point *data() const { return m_buffer; } inline Point &operator[]( int i ) { return m_buffer[i]; } inline const Point &operator[]( int i ) const { return m_buffer[i]; } inline void add( const Point &point ) { if ( m_capacity <= m_size ) reserve( m_size + 1 ); m_buffer[m_size++] = point; } private: inline void reserve( int size ) { if ( m_capacity == 0 ) m_capacity = 1; while ( m_capacity < size ) m_capacity *= 2; m_buffer = static_cast( ::realloc( m_buffer, m_capacity * sizeof( Point ) ) ); } int m_capacity; int m_size; Point *m_buffer; }; using namespace QwtClip; template class QwtPolygonClipper { public: QwtPolygonClipper( const Rect &clipRect ): d_clipRect( clipRect ) { } Polygon clipPolygon( const Polygon &polygon, bool closePolygon ) const { #if 0 if ( d_clipRect.contains( polygon.boundingRect() ) ) return polygon; #endif PointBuffer points1; PointBuffer points2( qMin( 256, polygon.size() ) ); points1.setPoints( polygon.size(), polygon.data() ); clipEdge< LeftEdge >( closePolygon, points1, points2 ); clipEdge< RightEdge >( closePolygon, points2, points1 ); clipEdge< TopEdge >( closePolygon, points1, points2 ); clipEdge< BottomEdge >( closePolygon, points2, points1 ); Polygon p; p.resize( points1.size() ); ::memcpy( p.data(), points1.data(), points1.size() * sizeof( Point ) ); return p; } private: template inline void clipEdge( bool closePolygon, PointBuffer &points, PointBuffer &clippedPoints ) const { clippedPoints.reset(); if ( points.size() < 2 ) { if ( points.size() == 1 ) clippedPoints.add( points[0] ); return; } const Edge edge( d_clipRect.x(), d_clipRect.x() + d_clipRect.width(), d_clipRect.y(), d_clipRect.y() + d_clipRect.height() ); int lastPos, start; if ( closePolygon ) { start = 0; lastPos = points.size() - 1; } else { start = 1; lastPos = 0; if ( edge.isInside( points[0] ) ) clippedPoints.add( points[0] ); } const uint nPoints = points.size(); for ( uint i = start; i < nPoints; i++ ) { const Point &p1 = points[i]; const Point &p2 = points[lastPos]; if ( edge.isInside( p1 ) ) { if ( edge.isInside( p2 ) ) { clippedPoints.add( p1 ); } else { clippedPoints.add( edge.intersection( p1, p2 ) ); clippedPoints.add( p1 ); } } else { if ( edge.isInside( p2 ) ) { clippedPoints.add( edge.intersection( p1, p2 ) ); } } lastPos = i; } } const Rect d_clipRect; }; class QwtCircleClipper { public: QwtCircleClipper( const QRectF &r ); QVector clipCircle( const QPointF &, double radius ) const; private: enum Edge { Left, Top, Right, Bottom, NEdges }; QList cuttingPoints( Edge, const QPointF &pos, double radius ) const; double toAngle( const QPointF &, const QPointF & ) const; const QRectF d_rect; }; QwtCircleClipper::QwtCircleClipper( const QRectF &r ): d_rect( r ) { } QVector QwtCircleClipper::clipCircle( const QPointF &pos, double radius ) const { QList points; for ( int edge = 0; edge < NEdges; edge++ ) points += cuttingPoints( static_cast(edge), pos, radius ); QVector intv; if ( points.size() <= 0 ) { QRectF cRect( 0, 0, 2 * radius, 2 * radius ); cRect.moveCenter( pos ); if ( d_rect.contains( cRect ) ) intv += QwtInterval( 0.0, 2 * M_PI ); } else { QList angles; for ( int i = 0; i < points.size(); i++ ) angles += toAngle( pos, points[i] ); qSort( angles ); const int in = d_rect.contains( qwtPolar2Pos( pos, radius, angles[0] + ( angles[1] - angles[0] ) / 2 ) ); if ( in ) { for ( int i = 0; i < angles.size() - 1; i += 2 ) intv += QwtInterval( angles[i], angles[i+1] ); } else { for ( int i = 1; i < angles.size() - 1; i += 2 ) intv += QwtInterval( angles[i], angles[i+1] ); intv += QwtInterval( angles.last(), angles.first() ); } } return intv; } double QwtCircleClipper::toAngle( const QPointF &from, const QPointF &to ) const { if ( from.x() == to.x() ) return from.y() <= to.y() ? M_PI / 2.0 : 3 * M_PI / 2.0; const double m = qAbs( ( to.y() - from.y() ) / ( to.x() - from.x() ) ); double angle = qAtan( m ); if ( to.x() > from.x() ) { if ( to.y() > from.y() ) angle = 2 * M_PI - angle; } else { if ( to.y() > from.y() ) angle = M_PI + angle; else angle = M_PI - angle; } return angle; } QList QwtCircleClipper::cuttingPoints( Edge edge, const QPointF &pos, double radius ) const { QList points; if ( edge == Left || edge == Right ) { const double x = ( edge == Left ) ? d_rect.left() : d_rect.right(); if ( qAbs( pos.x() - x ) < radius ) { const double off = qSqrt( qwtSqr( radius ) - qwtSqr( pos.x() - x ) ); const double m_y1 = pos.y() + off; if ( m_y1 >= d_rect.top() && m_y1 <= d_rect.bottom() ) points += QPointF( x, m_y1 ); const double m_y2 = pos.y() - off; if ( m_y2 >= d_rect.top() && m_y2 <= d_rect.bottom() ) points += QPointF( x, m_y2 ); } } else { const double y = ( edge == Top ) ? d_rect.top() : d_rect.bottom(); if ( qAbs( pos.y() - y ) < radius ) { const double off = qSqrt( qwtSqr( radius ) - qwtSqr( pos.y() - y ) ); const double x1 = pos.x() + off; if ( x1 >= d_rect.left() && x1 <= d_rect.right() ) points += QPointF( x1, y ); const double m_x2 = pos.x() - off; if ( m_x2 >= d_rect.left() && m_x2 <= d_rect.right() ) points += QPointF( m_x2, y ); } } return points; } /*! Sutherland-Hodgman polygon clipping \param clipRect Clip rectangle \param polygon Polygon \param closePolygon True, when the polygon is closed \return Clipped polygon */ QPolygon QwtClipper::clipPolygon( const QRectF &clipRect, const QPolygon &polygon, bool closePolygon ) { const int minX = qCeil( clipRect.left() ); const int maxX = qFloor( clipRect.right() ); const int minY = qCeil( clipRect.top() ); const int maxY = qFloor( clipRect.bottom() ); const QRect r( minX, minY, maxX - minX, maxY - minY ); QwtPolygonClipper clipper( r ); return clipper.clipPolygon( polygon, closePolygon ); } /*! Sutherland-Hodgman polygon clipping \param clipRect Clip rectangle \param polygon Polygon \param closePolygon True, when the polygon is closed \return Clipped polygon */ QPolygon QwtClipper::clipPolygon( const QRect &clipRect, const QPolygon &polygon, bool closePolygon ) { QwtPolygonClipper clipper( clipRect ); return clipper.clipPolygon( polygon, closePolygon ); } /*! Sutherland-Hodgman polygon clipping \param clipRect Clip rectangle \param polygon Polygon \param closePolygon True, when the polygon is closed \return Clipped polygon */ QPolygonF QwtClipper::clipPolygonF( const QRectF &clipRect, const QPolygonF &polygon, bool closePolygon ) { QwtPolygonClipper clipper( clipRect ); return clipper.clipPolygon( polygon, closePolygon ); } /*! Circle clipping clipCircle() divides a circle into intervals of angles representing arcs of the circle. When the circle is completely inside the clip rectangle an interval [0.0, 2 * M_PI] is returned. \param clipRect Clip rectangle \param center Center of the circle \param radius Radius of the circle \return Arcs of the circle */ QVector QwtClipper::clipCircle( const QRectF &clipRect, const QPointF ¢er, double radius ) { QwtCircleClipper clipper( clipRect ); return clipper.clipCircle( center, radius ); } connectome-workbench-1.2.3+git41-gc4c6c90/src/Qwt/qwt_clipper.h000066400000000000000000000020511300200146000240170ustar00rootroot00000000000000/* -*- mode: C++ ; c-file-style: "stroustrup" -*- ***************************** * Qwt Widget Library * Copyright (C) 1997 Josef Wilgen * Copyright (C) 2002 Uwe Rathmann * * This library is free software; you can redistribute it and/or * modify it under the terms of the Qwt License, Version 1.0 *****************************************************************************/ #ifndef QWT_CLIPPER_H #define QWT_CLIPPER_H #include "qwt_global.h" #include "qwt_interval.h" #include #include class QRect; class QRectF; /*! \brief Some clipping algorithms */ class QWT_EXPORT QwtClipper { public: static QPolygon clipPolygon( const QRect &, const QPolygon &, bool closePolygon = false ); static QPolygon clipPolygon( const QRectF &, const QPolygon &, bool closePolygon = false ); static QPolygonF clipPolygonF( const QRectF &, const QPolygonF &, bool closePolygon = false ); static QVector clipCircle( const QRectF &, const QPointF &, double radius ); }; #endif connectome-workbench-1.2.3+git41-gc4c6c90/src/Qwt/qwt_color_map.cpp000066400000000000000000000265451300200146000247050ustar00rootroot00000000000000/* -*- mode: C++ ; c-file-style: "stroustrup" -*- ***************************** * Qwt Widget Library * Copyright (C) 1997 Josef Wilgen * Copyright (C) 2002 Uwe Rathmann * * This library is free software; you can redistribute it and/or * modify it under the terms of the Qwt License, Version 1.0 *****************************************************************************/ #include "qwt_color_map.h" #include "qwt_math.h" #include "qwt_interval.h" #include class QwtLinearColorMap::ColorStops { public: ColorStops(): d_doAlpha( false ) { d_stops.reserve( 256 ); } void insert( double pos, const QColor &color ); QRgb rgb( QwtLinearColorMap::Mode, double pos ) const; QVector stops() const; private: class ColorStop { public: ColorStop(): pos( 0.0 ), rgb( 0 ) { }; ColorStop( double p, const QColor &c ): pos( p ), rgb( c.rgba() ) { r = qRed( rgb ); g = qGreen( rgb ); b = qBlue( rgb ); a = qAlpha( rgb ); /* when mapping a value to rgb we will have to calcualate: - const int v = int( ( s1.v0 + ratio * s1.vStep ) + 0.5 ); Thus adding 0.5 ( for rounding ) can be done in advance */ r0 = r + 0.5; g0 = g + 0.5; b0 = b + 0.5; a0 = a + 0.5; rStep = gStep = bStep = aStep = 0.0; posStep = 0.0; } void updateSteps( const ColorStop &nextStop ) { rStep = nextStop.r - r; gStep = nextStop.g - g; bStep = nextStop.b - b; aStep = nextStop.a - a; posStep = nextStop.pos - pos; } double pos; QRgb rgb; int r, g, b, a; // precalculated values double rStep, gStep, bStep, aStep; double r0, g0, b0, a0; double posStep; }; inline int findUpper( double pos ) const; QVector d_stops; bool d_doAlpha; }; void QwtLinearColorMap::ColorStops::insert( double pos, const QColor &color ) { // Lookups need to be very fast, insertions are not so important. // Anyway, a balanced tree is what we need here. TODO ... if ( pos < 0.0 || pos > 1.0 ) return; int index; if ( d_stops.size() == 0 ) { index = 0; d_stops.resize( 1 ); } else { index = findUpper( pos ); if ( index == d_stops.size() || qAbs( d_stops[index].pos - pos ) >= 0.001 ) { d_stops.resize( d_stops.size() + 1 ); for ( int i = d_stops.size() - 1; i > index; i-- ) d_stops[i] = d_stops[i-1]; } } d_stops[index] = ColorStop( pos, color ); if ( color.alpha() != 255 ) d_doAlpha = true; if ( index > 0 ) d_stops[index-1].updateSteps( d_stops[index] ); if ( index < d_stops.size() - 1 ) d_stops[index].updateSteps( d_stops[index+1] ); } inline QVector QwtLinearColorMap::ColorStops::stops() const { QVector positions( d_stops.size() ); for ( int i = 0; i < d_stops.size(); i++ ) positions[i] = d_stops[i].pos; return positions; } inline int QwtLinearColorMap::ColorStops::findUpper( double pos ) const { int index = 0; int n = d_stops.size(); const ColorStop *stops = d_stops.data(); while ( n > 0 ) { const int half = n >> 1; const int middle = index + half; if ( stops[middle].pos <= pos ) { index = middle + 1; n -= half + 1; } else n = half; } return index; } inline QRgb QwtLinearColorMap::ColorStops::rgb( QwtLinearColorMap::Mode mode, double pos ) const { if ( pos <= 0.0 ) return d_stops[0].rgb; if ( pos >= 1.0 ) return d_stops[ d_stops.size() - 1 ].rgb; const int index = findUpper( pos ); if ( mode == FixedColors ) { return d_stops[index-1].rgb; } else { const ColorStop &s1 = d_stops[index-1]; const double ratio = ( pos - s1.pos ) / ( s1.posStep ); const int r = int( s1.r0 + ratio * s1.rStep ); const int g = int( s1.g0 + ratio * s1.gStep ); const int b = int( s1.b0 + ratio * s1.bStep ); if ( d_doAlpha ) { if ( s1.aStep ) { const int a = int( s1.a0 + ratio * s1.aStep ); return qRgba( r, g, b, a ); } else { return qRgba( r, g, b, s1.a ); } } else { return qRgb( r, g, b ); } } } //! Constructor QwtColorMap::QwtColorMap( Format format ): d_format( format ) { } //! Destructor QwtColorMap::~QwtColorMap() { } /*! Build and return a color map of 256 colors The color table is needed for rendering indexed images in combination with using colorIndex(). \param interval Range for the values \return A color table, that can be used for a QImage */ QVector QwtColorMap::colorTable( const QwtInterval &interval ) const { QVector table( 256 ); if ( interval.isValid() ) { const double step = interval.width() / ( table.size() - 1 ); for ( int i = 0; i < table.size(); i++ ) table[i] = rgb( interval, interval.minValue() + step * i ); } return table; } class QwtLinearColorMap::PrivateData { public: ColorStops colorStops; QwtLinearColorMap::Mode mode; }; /*! Build a color map with two stops at 0.0 and 1.0. The color at 0.0 is Qt::blue, at 1.0 it is Qt::yellow. \param format Preferred format of the color map */ QwtLinearColorMap::QwtLinearColorMap( QwtColorMap::Format format ): QwtColorMap( format ) { d_data = new PrivateData; d_data->mode = ScaledColors; setColorInterval( Qt::blue, Qt::yellow ); } /*! Build a color map with two stops at 0.0 and 1.0. \param color1 Color used for the minimum value of the value interval \param color2 Color used for the maximum value of the value interval \param format Preferred format for the color map */ QwtLinearColorMap::QwtLinearColorMap( const QColor &color1, const QColor &color2, QwtColorMap::Format format ): QwtColorMap( format ) { d_data = new PrivateData; d_data->mode = ScaledColors; setColorInterval( color1, color2 ); } //! Destructor QwtLinearColorMap::~QwtLinearColorMap() { delete d_data; } /*! \brief Set the mode of the color map FixedColors means the color is calculated from the next lower color stop. ScaledColors means the color is calculated by interpolating the colors of the adjacent stops. \sa mode() */ void QwtLinearColorMap::setMode( Mode mode ) { d_data->mode = mode; } /*! \return Mode of the color map \sa setMode() */ QwtLinearColorMap::Mode QwtLinearColorMap::mode() const { return d_data->mode; } /*! Set the color range Add stops at 0.0 and 1.0. \param color1 Color used for the minimum value of the value interval \param color2 Color used for the maximum value of the value interval \sa color1(), color2() */ void QwtLinearColorMap::setColorInterval( const QColor &color1, const QColor &color2 ) { d_data->colorStops = ColorStops(); d_data->colorStops.insert( 0.0, color1 ); d_data->colorStops.insert( 1.0, color2 ); } /*! Add a color stop The value has to be in the range [0.0, 1.0]. F.e. a stop at position 17.0 for a range [10.0,20.0] must be passed as: (17.0 - 10.0) / (20.0 - 10.0) \param value Value between [0.0, 1.0] \param color Color stop */ void QwtLinearColorMap::addColorStop( double value, const QColor& color ) { if ( value >= 0.0 && value <= 1.0 ) d_data->colorStops.insert( value, color ); } /*! \return Positions of color stops in increasing order */ QVector QwtLinearColorMap::colorStops() const { return d_data->colorStops.stops(); } /*! \return the first color of the color range \sa setColorInterval() */ QColor QwtLinearColorMap::color1() const { return QColor( d_data->colorStops.rgb( d_data->mode, 0.0 ) ); } /*! \return the second color of the color range \sa setColorInterval() */ QColor QwtLinearColorMap::color2() const { return QColor( d_data->colorStops.rgb( d_data->mode, 1.0 ) ); } /*! Map a value of a given interval into a RGB value \param interval Range for all values \param value Value to map into a RGB value \return RGB value for value */ QRgb QwtLinearColorMap::rgb( const QwtInterval &interval, double value ) const { if ( qIsNaN(value) ) return 0u; const double width = interval.width(); if ( width <= 0.0 ) return 0u; const double ratio = ( value - interval.minValue() ) / width; return d_data->colorStops.rgb( d_data->mode, ratio ); } /*! \brief Map a value of a given interval into a color index \param interval Range for all values \param value Value to map into a color index \return Index, between 0 and 255 */ unsigned char QwtLinearColorMap::colorIndex( const QwtInterval &interval, double value ) const { const double width = interval.width(); if ( qIsNaN(value) || width <= 0.0 || value <= interval.minValue() ) return 0; if ( value >= interval.maxValue() ) return 255; const double ratio = ( value - interval.minValue() ) / width; unsigned char index; if ( d_data->mode == FixedColors ) index = static_cast( ratio * 255 ); // always floor else index = static_cast( ratio * 255 + 0.5 ); return index; } class QwtAlphaColorMap::PrivateData { public: QColor color; QRgb rgb; QRgb rgbMax; }; /*! Constructor \param color Color of the map */ QwtAlphaColorMap::QwtAlphaColorMap( const QColor &color ): QwtColorMap( QwtColorMap::RGB ) { d_data = new PrivateData; setColor( color ); } //! Destructor QwtAlphaColorMap::~QwtAlphaColorMap() { delete d_data; } /*! Set the color \param color Color \sa color() */ void QwtAlphaColorMap::setColor( const QColor &color ) { d_data->color = color; d_data->rgb = color.rgb() & qRgba( 255, 255, 255, 0 ); d_data->rgbMax = d_data->rgb | ( 255 << 24 ); } /*! \return the color \sa setColor() */ QColor QwtAlphaColorMap::color() const { return d_data->color; } /*! \brief Map a value of a given interval into a alpha value alpha := (value - interval.minValue()) / interval.width(); \param interval Range for all values \param value Value to map into a RGB value \return RGB value, with an alpha value */ QRgb QwtAlphaColorMap::rgb( const QwtInterval &interval, double value ) const { if ( qIsNaN(value) ) return 0u; const double width = interval.width(); if ( width <= 0.0 ) return 0u; if ( value <= interval.minValue() ) return d_data->rgb; if ( value >= interval.maxValue() ) return d_data->rgbMax; const double ratio = ( value - interval.minValue() ) / width; return d_data->rgb | ( qRound( 255 * ratio ) << 24 ); } /*! Dummy function, needed to be implemented as it is pure virtual in QwtColorMap. Color indices make no sense in combination with an alpha channel. \return Always 0 */ unsigned char QwtAlphaColorMap::colorIndex( const QwtInterval &, double ) const { return 0; } connectome-workbench-1.2.3+git41-gc4c6c90/src/Qwt/qwt_color_map.h000066400000000000000000000116751300200146000243500ustar00rootroot00000000000000/* -*- mode: C++ ; c-file-style: "stroustrup" -*- ***************************** * Qwt Widget Library * Copyright (C) 1997 Josef Wilgen * Copyright (C) 2002 Uwe Rathmann * * This library is free software; you can redistribute it and/or * modify it under the terms of the Qwt License, Version 1.0 *****************************************************************************/ #ifndef QWT_COLOR_MAP_H #define QWT_COLOR_MAP_H #include "qwt_global.h" #include "qwt_interval.h" #include #include /*! \brief QwtColorMap is used to map values into colors. For displaying 3D data on a 2D plane the 3rd dimension is often displayed using colors, like f.e in a spectrogram. Each color map is optimized to return colors for only one of the following image formats: - QImage::Format_Indexed8\n - QImage::Format_ARGB32\n \sa QwtPlotSpectrogram, QwtScaleWidget */ class QWT_EXPORT QwtColorMap { public: /*! Format for color mapping \sa rgb(), colorIndex(), colorTable() */ enum Format { //! The map is intended to map into RGB values. RGB, /*! The map is intended to map into 8 bit values, that are indices into the color table. */ Indexed }; QwtColorMap( Format = QwtColorMap::RGB ); virtual ~QwtColorMap(); Format format() const; /*! Map a value of a given interval into a RGB value. \param interval Range for the values \param value Value \return RGB value, corresponding to value */ virtual QRgb rgb( const QwtInterval &interval, double value ) const = 0; /*! Map a value of a given interval into a color index \param interval Range for the values \param value Value \return color index, corresponding to value */ virtual unsigned char colorIndex( const QwtInterval &interval, double value ) const = 0; QColor color( const QwtInterval &, double value ) const; virtual QVector colorTable( const QwtInterval & ) const; private: Format d_format; }; /*! \brief QwtLinearColorMap builds a color map from color stops. A color stop is a color at a specific position. The valid range for the positions is [0.0, 1.0]. When mapping a value into a color it is translated into this interval according to mode(). */ class QWT_EXPORT QwtLinearColorMap: public QwtColorMap { public: /*! Mode of color map \sa setMode(), mode() */ enum Mode { //! Return the color from the next lower color stop FixedColors, //! Interpolating the colors of the adjacent stops. ScaledColors }; QwtLinearColorMap( QwtColorMap::Format = QwtColorMap::RGB ); QwtLinearColorMap( const QColor &from, const QColor &to, QwtColorMap::Format = QwtColorMap::RGB ); virtual ~QwtLinearColorMap(); void setMode( Mode ); Mode mode() const; void setColorInterval( const QColor &color1, const QColor &color2 ); void addColorStop( double value, const QColor& ); QVector colorStops() const; QColor color1() const; QColor color2() const; virtual QRgb rgb( const QwtInterval &, double value ) const; virtual unsigned char colorIndex( const QwtInterval &, double value ) const; class ColorStops; private: // Disabled copy constructor and operator= QwtLinearColorMap( const QwtLinearColorMap & ); QwtLinearColorMap &operator=( const QwtLinearColorMap & ); class PrivateData; PrivateData *d_data; }; /*! \brief QwtAlphaColorMap varies the alpha value of a color */ class QWT_EXPORT QwtAlphaColorMap: public QwtColorMap { public: QwtAlphaColorMap( const QColor & = QColor( Qt::gray ) ); virtual ~QwtAlphaColorMap(); void setColor( const QColor & ); QColor color() const; virtual QRgb rgb( const QwtInterval &, double value ) const; private: QwtAlphaColorMap( const QwtAlphaColorMap & ); QwtAlphaColorMap &operator=( const QwtAlphaColorMap & ); virtual unsigned char colorIndex( const QwtInterval &, double value ) const; class PrivateData; PrivateData *d_data; }; /*! Map a value into a color \param interval Valid interval for values \param value Value \return Color corresponding to value \warning This method is slow for Indexed color maps. If it is necessary to map many values, its better to get the color table once and find the color using colorIndex(). */ inline QColor QwtColorMap::color( const QwtInterval &interval, double value ) const { if ( d_format == RGB ) { return QColor::fromRgba( rgb( interval, value ) ); } else { const unsigned int index = colorIndex( interval, value ); return colorTable( interval )[index]; // slow } } /*! \return Intended format of the color map \sa Format */ inline QwtColorMap::Format QwtColorMap::format() const { return d_format; } #endif connectome-workbench-1.2.3+git41-gc4c6c90/src/Qwt/qwt_column_symbol.cpp000066400000000000000000000152761300200146000256130ustar00rootroot00000000000000/* -*- mode: C++ ; c-file-style: "stroustrup" -*- ***************************** * Qwt Widget Library * Copyright (C) 1997 Josef Wilgen * Copyright (C) 2002 Uwe Rathmann * * This library is free software; you can redistribute it and/or * modify it under the terms of the Qwt License, Version 1.0 *****************************************************************************/ #include "qwt_column_symbol.h" #include "qwt_math.h" #include "qwt_painter.h" #include #include static void qwtDrawBox( QPainter *p, const QRectF &rect, const QPalette &pal, double lw ) { if ( lw > 0.0 ) { if ( rect.width() == 0.0 ) { p->setPen( pal.dark().color() ); p->drawLine( rect.topLeft(), rect.bottomLeft() ); return; } if ( rect.height() == 0.0 ) { p->setPen( pal.dark().color() ); p->drawLine( rect.topLeft(), rect.topRight() ); return; } lw = qMin( lw, rect.height() / 2.0 - 1.0 ); lw = qMin( lw, rect.width() / 2.0 - 1.0 ); const QRectF outerRect = rect.adjusted( 0, 0, 1, 1 ); QPolygonF polygon( outerRect ); if ( outerRect.width() > 2 * lw && outerRect.height() > 2 * lw ) { const QRectF innerRect = outerRect.adjusted( lw, lw, -lw, -lw ); polygon = polygon.subtracted( innerRect ); } p->setPen( Qt::NoPen ); p->setBrush( pal.dark() ); p->drawPolygon( polygon ); } const QRectF windowRect = rect.adjusted( lw, lw, -lw + 1, -lw + 1 ); if ( windowRect.isValid() ) p->fillRect( windowRect, pal.window() ); } static void qwtDrawPanel( QPainter *painter, const QRectF &rect, const QPalette &pal, double lw ) { if ( lw > 0.0 ) { if ( rect.width() == 0.0 ) { painter->setPen( pal.window().color() ); painter->drawLine( rect.topLeft(), rect.bottomLeft() ); return; } if ( rect.height() == 0.0 ) { painter->setPen( pal.window().color() ); painter->drawLine( rect.topLeft(), rect.topRight() ); return; } lw = qMin( lw, rect.height() / 2.0 - 1.0 ); lw = qMin( lw, rect.width() / 2.0 - 1.0 ); const QRectF outerRect = rect.adjusted( 0, 0, 1, 1 ); const QRectF innerRect = outerRect.adjusted( lw, lw, -lw, -lw ); QPolygonF lines[2]; lines[0] += outerRect.bottomLeft(); lines[0] += outerRect.topLeft(); lines[0] += outerRect.topRight(); lines[0] += innerRect.topRight(); lines[0] += innerRect.topLeft(); lines[0] += innerRect.bottomLeft(); lines[1] += outerRect.topRight(); lines[1] += outerRect.bottomRight(); lines[1] += outerRect.bottomLeft(); lines[1] += innerRect.bottomLeft(); lines[1] += innerRect.bottomRight(); lines[1] += innerRect.topRight(); painter->setPen( Qt::NoPen ); painter->setBrush( pal.light() ); painter->drawPolygon( lines[0] ); painter->setBrush( pal.dark() ); painter->drawPolygon( lines[1] ); } painter->fillRect( rect.adjusted( lw, lw, -lw + 1, -lw + 1 ), pal.window() ); } class QwtColumnSymbol::PrivateData { public: PrivateData(): style( QwtColumnSymbol::Box ), frameStyle( QwtColumnSymbol::Raised ), lineWidth( 2 ) { palette = QPalette( Qt::gray ); } QwtColumnSymbol::Style style; QwtColumnSymbol::FrameStyle frameStyle; QPalette palette; int lineWidth; }; /*! Constructor \param style Style of the symbol \sa setStyle(), style(), Style */ QwtColumnSymbol::QwtColumnSymbol( Style style ) { d_data = new PrivateData(); d_data->style = style; } //! Destructor QwtColumnSymbol::~QwtColumnSymbol() { delete d_data; } /*! Specify the symbol style \param style Style \sa style(), setPalette() */ void QwtColumnSymbol::setStyle( Style style ) { d_data->style = style; } /*! \return Current symbol style \sa setStyle() */ QwtColumnSymbol::Style QwtColumnSymbol::style() const { return d_data->style; } /*! Assign a palette for the symbol \param palette Palette \sa palette(), setStyle() */ void QwtColumnSymbol::setPalette( const QPalette &palette ) { d_data->palette = palette; } /*! \return Current palette \sa setPalette() */ const QPalette& QwtColumnSymbol::palette() const { return d_data->palette; } /*! Set the frame, that is used for the Box style. \param frameStyle Frame style \sa frameStyle(), setLineWidth(), setStyle() */ void QwtColumnSymbol::setFrameStyle( FrameStyle frameStyle ) { d_data->frameStyle = frameStyle; } /*! \return Current frame style, that is used for the Box style. \sa setFrameStyle(), lineWidth(), setStyle() */ QwtColumnSymbol::FrameStyle QwtColumnSymbol::frameStyle() const { return d_data->frameStyle; } /*! Set the line width of the frame, that is used for the Box style. \param width Width \sa lineWidth(), setFrameStyle() */ void QwtColumnSymbol::setLineWidth( int width ) { if ( width < 0 ) width = 0; d_data->lineWidth = width; } /*! \return Line width of the frame, that is used for the Box style. \sa setLineWidth(), frameStyle(), setStyle() */ int QwtColumnSymbol::lineWidth() const { return d_data->lineWidth; } /*! Draw the symbol depending on its style. \param painter Painter \param rect Directed rectangle \sa drawBox() */ void QwtColumnSymbol::draw( QPainter *painter, const QwtColumnRect &rect ) const { painter->save(); switch ( d_data->style ) { case QwtColumnSymbol::Box: { drawBox( painter, rect ); break; } default:; } painter->restore(); } /*! Draw the symbol when it is in Box style. \param painter Painter \param rect Directed rectangle \sa draw() */ void QwtColumnSymbol::drawBox( QPainter *painter, const QwtColumnRect &rect ) const { QRectF r = rect.toRect(); if ( QwtPainter::roundingAlignment( painter ) ) { r.setLeft( qRound( r.left() ) ); r.setRight( qRound( r.right() ) ); r.setTop( qRound( r.top() ) ); r.setBottom( qRound( r.bottom() ) ); } switch ( d_data->frameStyle ) { case QwtColumnSymbol::Raised: { qwtDrawPanel( painter, r, d_data->palette, d_data->lineWidth ); break; } case QwtColumnSymbol::Plain: { qwtDrawBox( painter, r, d_data->palette, d_data->lineWidth ); break; } default: { painter->fillRect( r, d_data->palette.window() ); } } } connectome-workbench-1.2.3+git41-gc4c6c90/src/Qwt/qwt_column_symbol.h000066400000000000000000000073601300200146000252530ustar00rootroot00000000000000/* -*- mode: C++ ; c-file-style: "stroustrup" -*- ***************************** * Qwt Widget Library * Copyright (C) 1997 Josef Wilgen * Copyright (C) 2002 Uwe Rathmann * * This library is free software; you can redistribute it and/or * modify it under the terms of the Qwt License, Version 1.0 *****************************************************************************/ #ifndef QWT_COLUMN_SYMBOL_H #define QWT_COLUMN_SYMBOL_H #include "qwt_global.h" #include "qwt_interval.h" #include #include #include class QPainter; class QPalette; class QRect; class QwtText; /*! \brief Directed rectangle representing bounding rectangle and orientation of a column. */ class QWT_EXPORT QwtColumnRect { public: //! Direction of the column enum Direction { //! From left to right LeftToRight, //! From right to left RightToLeft, //! From bottom to top BottomToTop, //! From top to bottom TopToBottom }; //! Build an rectangle with invalid intervals directed BottomToTop. QwtColumnRect(): direction( BottomToTop ) { } //! \return A normalized QRect built from the intervals QRectF toRect() const { QRectF r( hInterval.minValue(), vInterval.minValue(), hInterval.maxValue() - hInterval.minValue(), vInterval.maxValue() - vInterval.minValue() ); r = r.normalized(); if ( hInterval.borderFlags() & QwtInterval::ExcludeMinimum ) r.adjust( 1, 0, 0, 0 ); if ( hInterval.borderFlags() & QwtInterval::ExcludeMaximum ) r.adjust( 0, 0, -1, 0 ); if ( vInterval.borderFlags() & QwtInterval::ExcludeMinimum ) r.adjust( 0, 1, 0, 0 ); if ( vInterval.borderFlags() & QwtInterval::ExcludeMaximum ) r.adjust( 0, 0, 0, -1 ); return r; } //! \return Orientation Qt::Orientation orientation() const { if ( direction == LeftToRight || direction == RightToLeft ) return Qt::Horizontal; return Qt::Vertical; } //! Interval for the horizontal coordinates QwtInterval hInterval; //! Interval for the vertical coordinates QwtInterval vInterval; //! Direction Direction direction; }; //! A drawing primitive for columns class QWT_EXPORT QwtColumnSymbol { public: /*! Style \sa setStyle(), style() */ enum Style { //! No Style, the symbol draws nothing NoStyle = -1, /*! The column is painted with a frame depending on the frameStyle() and lineWidth() using the palette(). */ Box, /*! Styles >= QwtColumnSymbol::UserStyle are reserved for derived classes of QwtColumnSymbol that overload draw() with additional application specific symbol types. */ UserStyle = 1000 }; /*! Frame Style used in Box style(). \sa Style, setFrameStyle(), frameStyle(), setStyle(), setPalette() */ enum FrameStyle { //! No frame NoFrame, //! A plain frame style Plain, //! A raised frame style Raised }; public: QwtColumnSymbol( Style = NoStyle ); virtual ~QwtColumnSymbol(); void setFrameStyle( FrameStyle style ); FrameStyle frameStyle() const; void setLineWidth( int width ); int lineWidth() const; void setPalette( const QPalette & ); const QPalette &palette() const; void setStyle( Style ); Style style() const; virtual void draw( QPainter *, const QwtColumnRect & ) const; protected: void drawBox( QPainter *, const QwtColumnRect & ) const; private: class PrivateData; PrivateData* d_data; }; #endif connectome-workbench-1.2.3+git41-gc4c6c90/src/Qwt/qwt_compat.h000066400000000000000000000020501300200146000236430ustar00rootroot00000000000000/* -*- mode: C++ ; c-file-style: "stroustrup" -*- ***************************** * Qwt Widget Library * Copyright (C) 1997 Josef Wilgen * Copyright (C) 2002 Uwe Rathmann * * This library is free software; you can redistribute it and/or * modify it under the terms of the Qwt License, Version 1.0 *****************************************************************************/ #ifndef _QWT_COMPAT_H_ #define _QWT_COMPAT_H_ #include "qwt_global.h" #include "qwt_interval.h" #include "qwt_point_3d.h" #include #include #include #include #include #include // A couple of definition for Qwt5 compatibility #define qwtMax qMax #define qwtMin qMin #define qwtAbs qAbs #define qwtRound qRound #define QwtArray QVector typedef QList QwtValueList; typedef QPointF QwtDoublePoint; typedef QSizeF QwtDoubleSize; typedef QRectF QwtDoubleRect; typedef QPolygon QwtPolygon; typedef QPolygonF QwtPolygonF; typedef QwtInterval QwtDoubleInterval; typedef QwtPoint3D QwtDoublePoint3D; #endif connectome-workbench-1.2.3+git41-gc4c6c90/src/Qwt/qwt_curve_fitter.cpp000066400000000000000000000240711300200146000254230ustar00rootroot00000000000000/* -*- mode: C++ ; c-file-style: "stroustrup" -*- ***************************** * Qwt Widget Library * Copyright (C) 1997 Josef Wilgen * Copyright (C) 2002 Uwe Rathmann * * This library is free software; you can redistribute it and/or * modify it under the terms of the Qwt License, Version 1.0 *****************************************************************************/ #include "qwt_curve_fitter.h" #include "qwt_math.h" #include "qwt_spline.h" #include #include #if QT_VERSION < 0x040601 #define qFabs(x) ::fabs(x) #endif //! Constructor QwtCurveFitter::QwtCurveFitter() { } //! Destructor QwtCurveFitter::~QwtCurveFitter() { } class QwtSplineCurveFitter::PrivateData { public: PrivateData(): fitMode( QwtSplineCurveFitter::Auto ), splineSize( 250 ) { } QwtSpline spline; QwtSplineCurveFitter::FitMode fitMode; int splineSize; }; //! Constructor QwtSplineCurveFitter::QwtSplineCurveFitter() { d_data = new PrivateData; } //! Destructor QwtSplineCurveFitter::~QwtSplineCurveFitter() { delete d_data; } /*! Select the algorithm used for building the spline \param mode Mode representing a spline algorithm \sa fitMode() */ void QwtSplineCurveFitter::setFitMode( FitMode mode ) { d_data->fitMode = mode; } /*! \return Mode representing a spline algorithm \sa setFitMode() */ QwtSplineCurveFitter::FitMode QwtSplineCurveFitter::fitMode() const { return d_data->fitMode; } /*! Assign a spline \param spline Spline \sa spline() */ void QwtSplineCurveFitter::setSpline( const QwtSpline &spline ) { d_data->spline = spline; d_data->spline.reset(); } /*! \return Spline \sa setSpline() */ const QwtSpline &QwtSplineCurveFitter::spline() const { return d_data->spline; } /*! \return Spline \sa setSpline() */ QwtSpline &QwtSplineCurveFitter::spline() { return d_data->spline; } /*! Assign a spline size ( has to be at least 10 points ) \param splineSize Spline size \sa splineSize() */ void QwtSplineCurveFitter::setSplineSize( int splineSize ) { d_data->splineSize = qMax( splineSize, 10 ); } /*! \return Spline size \sa setSplineSize() */ int QwtSplineCurveFitter::splineSize() const { return d_data->splineSize; } /*! Find a curve which has the best fit to a series of data points \param points Series of data points \return Curve points */ QPolygonF QwtSplineCurveFitter::fitCurve( const QPolygonF &points ) const { const int size = points.size(); if ( size <= 2 ) return points; FitMode fitMode = d_data->fitMode; if ( fitMode == Auto ) { fitMode = Spline; const QPointF *p = points.data(); for ( int i = 1; i < size; i++ ) { if ( p[i].x() <= p[i-1].x() ) { fitMode = ParametricSpline; break; } }; } if ( fitMode == ParametricSpline ) return fitParametric( points ); else return fitSpline( points ); } QPolygonF QwtSplineCurveFitter::fitSpline( const QPolygonF &points ) const { d_data->spline.setPoints( points ); if ( !d_data->spline.isValid() ) return points; QPolygonF fittedPoints( d_data->splineSize ); const double x1 = points[0].x(); const double x2 = points[int( points.size() - 1 )].x(); const double dx = x2 - x1; const double delta = dx / ( d_data->splineSize - 1 ); for ( int i = 0; i < d_data->splineSize; i++ ) { QPointF &p = fittedPoints[i]; const double v = x1 + i * delta; const double sv = d_data->spline.value( v ); p.setX( v ); p.setY( sv ); } d_data->spline.reset(); return fittedPoints; } QPolygonF QwtSplineCurveFitter::fitParametric( const QPolygonF &points ) const { int i; const int size = points.size(); QPolygonF fittedPoints( d_data->splineSize ); QPolygonF splinePointsX( size ); QPolygonF splinePointsY( size ); const QPointF *p = points.data(); QPointF *spX = splinePointsX.data(); QPointF *spY = splinePointsY.data(); double param = 0.0; for ( i = 0; i < size; i++ ) { const double x = p[i].x(); const double y = p[i].y(); if ( i > 0 ) { const double delta = qSqrt( qwtSqr( x - spX[i-1].y() ) + qwtSqr( y - spY[i-1].y() ) ); param += qMax( delta, 1.0 ); } spX[i].setX( param ); spX[i].setY( x ); spY[i].setX( param ); spY[i].setY( y ); } d_data->spline.setPoints( splinePointsX ); if ( !d_data->spline.isValid() ) return points; const double deltaX = splinePointsX[size - 1].x() / ( d_data->splineSize - 1 ); for ( i = 0; i < d_data->splineSize; i++ ) { const double dtmp = i * deltaX; fittedPoints[i].setX( d_data->spline.value( dtmp ) ); } d_data->spline.setPoints( splinePointsY ); if ( !d_data->spline.isValid() ) return points; const double deltaY = splinePointsY[size - 1].x() / ( d_data->splineSize - 1 ); for ( i = 0; i < d_data->splineSize; i++ ) { const double dtmp = i * deltaY; fittedPoints[i].setY( d_data->spline.value( dtmp ) ); } return fittedPoints; } class QwtWeedingCurveFitter::PrivateData { public: PrivateData(): tolerance( 1.0 ), chunkSize( 0 ) { } double tolerance; uint chunkSize; }; class QwtWeedingCurveFitter::Line { public: Line( int i1 = 0, int i2 = 0 ): from( i1 ), to( i2 ) { } int from; int to; }; /*! Constructor \param tolerance Tolerance \sa setTolerance(), tolerance() */ QwtWeedingCurveFitter::QwtWeedingCurveFitter( double tolerance ) { d_data = new PrivateData; setTolerance( tolerance ); } //! Destructor QwtWeedingCurveFitter::~QwtWeedingCurveFitter() { delete d_data; } /*! Assign the tolerance The tolerance is the maximum distance, that is acceptable between the original curve and the smoothed curve. Increasing the tolerance will reduce the number of the resulting points. \param tolerance Tolerance \sa tolerance() */ void QwtWeedingCurveFitter::setTolerance( double tolerance ) { d_data->tolerance = qMax( tolerance, 0.0 ); } /*! \return Tolerance \sa setTolerance() */ double QwtWeedingCurveFitter::tolerance() const { return d_data->tolerance; } /*! Limit the number of points passed to a run of the algorithm The runtime of the Douglas Peucker algorithm increases non linear with the number of points. For a chunk size > 0 the polygon is split into pieces passed to the algorithm one by one. \param numPoints Maximum for the number of points passed to the algorithm \sa chunkSize() */ void QwtWeedingCurveFitter::setChunkSize( uint numPoints ) { if ( numPoints > 0 ) numPoints = qMax( numPoints, 3U ); d_data->chunkSize = numPoints; } /*! \return Maximum for the number of points passed to a run of the algorithm - or 0, when unlimited \sa setChunkSize() */ uint QwtWeedingCurveFitter::chunkSize() const { return d_data->chunkSize; } /*! \param points Series of data points \return Curve points */ QPolygonF QwtWeedingCurveFitter::fitCurve( const QPolygonF &points ) const { QPolygonF fittedPoints; if ( d_data->chunkSize == 0 ) { fittedPoints = simplify( points ); } else { for ( int i = 0; i < points.size(); i += d_data->chunkSize ) { const QPolygonF p = points.mid( i, d_data->chunkSize ); fittedPoints += simplify( p ); } } return fittedPoints; } QPolygonF QwtWeedingCurveFitter::simplify( const QPolygonF &points ) const { const double toleranceSqr = d_data->tolerance * d_data->tolerance; QStack stack; stack.reserve( 500 ); const QPointF *p = points.data(); const int nPoints = points.size(); QVector usePoint( nPoints, false ); stack.push( Line( 0, nPoints - 1 ) ); while ( !stack.isEmpty() ) { const Line r = stack.pop(); // initialize line segment const double vecX = p[r.to].x() - p[r.from].x(); const double vecY = p[r.to].y() - p[r.from].y(); const double vecLength = qSqrt( vecX * vecX + vecY * vecY ); const double unitVecX = ( vecLength != 0.0 ) ? vecX / vecLength : 0.0; const double unitVecY = ( vecLength != 0.0 ) ? vecY / vecLength : 0.0; double maxDistSqr = 0.0; int nVertexIndexMaxDistance = r.from + 1; for ( int i = r.from + 1; i < r.to; i++ ) { //compare to anchor const double fromVecX = p[i].x() - p[r.from].x(); const double fromVecY = p[i].y() - p[r.from].y(); double distToSegmentSqr; if ( fromVecX * unitVecX + fromVecY * unitVecY < 0.0 ) { distToSegmentSqr = fromVecX * fromVecX + fromVecY * fromVecY; } else { const double toVecX = p[i].x() - p[r.to].x(); const double toVecY = p[i].y() - p[r.to].y(); const double toVecLength = toVecX * toVecX + toVecY * toVecY; const double s = toVecX * ( -unitVecX ) + toVecY * ( -unitVecY ); if ( s < 0.0 ) { distToSegmentSqr = toVecLength; } else { distToSegmentSqr = qFabs( toVecLength - s * s ); } } if ( maxDistSqr < distToSegmentSqr ) { maxDistSqr = distToSegmentSqr; nVertexIndexMaxDistance = i; } } if ( maxDistSqr <= toleranceSqr ) { usePoint[r.from] = true; usePoint[r.to] = true; } else { stack.push( Line( r.from, nVertexIndexMaxDistance ) ); stack.push( Line( nVertexIndexMaxDistance, r.to ) ); } } QPolygonF stripped; for ( int i = 0; i < nPoints; i++ ) { if ( usePoint[i] ) stripped += p[i]; } return stripped; } connectome-workbench-1.2.3+git41-gc4c6c90/src/Qwt/qwt_curve_fitter.h000066400000000000000000000072701300200146000250720ustar00rootroot00000000000000/* -*- mode: C++ ; c-file-style: "stroustrup" -*- ***************************** * Qwt Widget Library * Copyright (C) 1997 Josef Wilgen * Copyright (C) 2002 Uwe Rathmann * * This library is free software; you can redistribute it and/or * modify it under the terms of the Qwt License, Version 1.0 *****************************************************************************/ #ifndef QWT_CURVE_FITTER_H #define QWT_CURVE_FITTER_H #include "qwt_global.h" #include #include class QwtSpline; /*! \brief Abstract base class for a curve fitter */ class QWT_EXPORT QwtCurveFitter { public: virtual ~QwtCurveFitter(); /*! Find a curve which has the best fit to a series of data points \param polygon Series of data points \return Curve points */ virtual QPolygonF fitCurve( const QPolygonF &polygon ) const = 0; protected: QwtCurveFitter(); private: QwtCurveFitter( const QwtCurveFitter & ); QwtCurveFitter &operator=( const QwtCurveFitter & ); }; /*! \brief A curve fitter using cubic splines */ class QWT_EXPORT QwtSplineCurveFitter: public QwtCurveFitter { public: /*! Spline type The default setting is Auto \sa setFitMode(), FitMode() */ enum FitMode { /*! Use the default spline algorithm for polygons with increasing x values ( p[i-1] < p[i] ), otherwise use a parametric spline algorithm. */ Auto, //! Use a default spline algorithm Spline, //! Use a parametric spline algorithm ParametricSpline }; QwtSplineCurveFitter(); virtual ~QwtSplineCurveFitter(); void setFitMode( FitMode ); FitMode fitMode() const; void setSpline( const QwtSpline& ); const QwtSpline &spline() const; QwtSpline &spline(); void setSplineSize( int size ); int splineSize() const; virtual QPolygonF fitCurve( const QPolygonF & ) const; private: QPolygonF fitSpline( const QPolygonF & ) const; QPolygonF fitParametric( const QPolygonF & ) const; class PrivateData; PrivateData *d_data; }; /*! \brief A curve fitter implementing Douglas and Peucker algorithm The purpose of the Douglas and Peucker algorithm is that given a 'curve' composed of line segments to find a curve not too dissimilar but that has fewer points. The algorithm defines 'too dissimilar' based on the maximum distance (tolerance) between the original curve and the smoothed curve. The runtime of the algorithm increases non linear ( worst case O( n*n ) ) and might be very slow for huge polygons. To avoid performance issues it might be useful to split the polygon ( setChunkSize() ) and to run the algorithm for these smaller parts. The disadvantage of having no interpolation at the borders is for most use cases irrelevant. The smoothed curve consists of a subset of the points that defined the original curve. In opposite to QwtSplineCurveFitter the Douglas and Peucker algorithm reduces the number of points. By adjusting the tolerance parameter according to the axis scales QwtSplineCurveFitter can be used to implement different level of details to speed up painting of curves of many points. */ class QWT_EXPORT QwtWeedingCurveFitter: public QwtCurveFitter { public: QwtWeedingCurveFitter( double tolerance = 1.0 ); virtual ~QwtWeedingCurveFitter(); void setTolerance( double ); double tolerance() const; void setChunkSize( uint ); uint chunkSize() const; virtual QPolygonF fitCurve( const QPolygonF & ) const; private: virtual QPolygonF simplify( const QPolygonF & ) const; class Line; class PrivateData; PrivateData *d_data; }; #endif connectome-workbench-1.2.3+git41-gc4c6c90/src/Qwt/qwt_date.cpp000066400000000000000000000402701300200146000236360ustar00rootroot00000000000000/* -*- mode: C++ ; c-file-style: "stroustrup" -*- ***************************** * Qwt Widget Library * Copyright (C) 1997 Josef Wilgen * Copyright (C) 2002 Uwe Rathmann * * This library is free software; you can redistribute it and/or * modify it under the terms of the Qwt License, Version 1.0 *****************************************************************************/ #include "qwt_date.h" #include #include #include #include #include #if QT_VERSION >= 0x050000 typedef qint64 QwtJulianDay; static const QwtJulianDay minJulianDayD = Q_INT64_C( -784350574879 ); static const QwtJulianDay maxJulianDayD = Q_INT64_C( 784354017364 ); #else // QDate stores the Julian day as unsigned int, but // but it is QDate::fromJulianDay( int ). That's why // we have the range [ 1, INT_MAX ] typedef int QwtJulianDay; static const QwtJulianDay minJulianDayD = 1; static const QwtJulianDay maxJulianDayD = std::numeric_limits::max(); #endif static inline Qt::DayOfWeek qwtFirstDayOfWeek() { #if QT_VERSION >= 0x040800 return QLocale().firstDayOfWeek(); #else switch( QLocale().country() ) { case QLocale::Maldives: return Qt::Friday; case QLocale::Afghanistan: case QLocale::Algeria: case QLocale::Bahrain: case QLocale::Djibouti: case QLocale::Egypt: case QLocale::Eritrea: case QLocale::Ethiopia: case QLocale::Iran: case QLocale::Iraq: case QLocale::Jordan: case QLocale::Kenya: case QLocale::Kuwait: case QLocale::LibyanArabJamahiriya: case QLocale::Morocco: case QLocale::Oman: case QLocale::Qatar: case QLocale::SaudiArabia: case QLocale::Somalia: case QLocale::Sudan: case QLocale::Tunisia: case QLocale::Yemen: return Qt::Saturday; case QLocale::AmericanSamoa: case QLocale::Argentina: case QLocale::Azerbaijan: case QLocale::Botswana: case QLocale::Canada: case QLocale::China: case QLocale::FaroeIslands: case QLocale::Georgia: case QLocale::Greenland: case QLocale::Guam: case QLocale::HongKong: case QLocale::Iceland: case QLocale::India: case QLocale::Ireland: case QLocale::Israel: case QLocale::Jamaica: case QLocale::Japan: case QLocale::Kyrgyzstan: case QLocale::Lao: case QLocale::Malta: case QLocale::MarshallIslands: case QLocale::Macau: case QLocale::Mongolia: case QLocale::NewZealand: case QLocale::NorthernMarianaIslands: case QLocale::Pakistan: case QLocale::Philippines: case QLocale::RepublicOfKorea: case QLocale::Singapore: case QLocale::SyrianArabRepublic: case QLocale::Taiwan: case QLocale::Thailand: case QLocale::TrinidadAndTobago: case QLocale::UnitedStates: case QLocale::UnitedStatesMinorOutlyingIslands: case QLocale::USVirginIslands: case QLocale::Uzbekistan: case QLocale::Zimbabwe: return Qt::Sunday; default: return Qt::Monday; } #endif } static inline void qwtFloorTime( QwtDate::IntervalType intervalType, QDateTime &dt ) { // when dt is inside the special hour where DST is ending // an hour is no unique. Therefore we have to // use UTC time. const Qt::TimeSpec timeSpec = dt.timeSpec(); if ( timeSpec == Qt::LocalTime ) dt = dt.toTimeSpec( Qt::UTC ); const QTime t = dt.time(); switch( intervalType ) { case QwtDate::Second: { dt.setTime( QTime( t.hour(), t.minute(), t.second() ) ); break; } case QwtDate::Minute: { dt.setTime( QTime( t.hour(), t.minute(), 0 ) ); break; } case QwtDate::Hour: { dt.setTime( QTime( t.hour(), 0, 0 ) ); break; } default: break; } if ( timeSpec == Qt::LocalTime ) dt = dt.toTimeSpec( Qt::LocalTime ); } static inline QDateTime qwtToTimeSpec( const QDateTime &dt, Qt::TimeSpec spec ) { if ( dt.timeSpec() == spec ) return dt; const qint64 jd = dt.date().toJulianDay(); if ( jd < 0 || jd >= INT_MAX ) { // the conversion between local time and UTC // is internally limited. To avoid // overflows we simply ignore the difference // for those dates QDateTime dt2 = dt; dt2.setTimeSpec( spec ); return dt2; } return dt.toTimeSpec( spec ); } //static inline double qwtToJulianDay( int year, int month, int day ) //{ // // code from QDate but using doubles to avoid overflows // // for large values // // const int m1 = ( month - 14 ) / 12; // const int m2 = ( 367 * ( month - 2 - 12 * m1 ) ) / 12; // const double y1 = ::floor( ( 4900.0 + year + m1 ) / 100 ); // // return ::floor( ( 1461.0 * ( year + 4800 + m1 ) ) / 4 ) + m2 // - ::floor( ( 3 * y1 ) / 4 ) + day - 32075; //} // //static inline qint64 qwtFloorDiv64( qint64 a, int b ) //{ // if ( a < 0 ) // a -= b - 1; // // return a / b; //} // //static inline qint64 qwtFloorDiv( int a, int b ) //{ // if ( a < 0 ) // a -= b - 1; // // return a / b; //} static inline QDate qwtToDate( int year, int month = 1, int day = 1 ) { #if QT_VERSION >= 0x050000 return QDate( year, month, day ); #else if ( year > 100000 ) { // code from QDate but using doubles to avoid overflows // for large values const int m1 = ( month - 14 ) / 12; const int m2 = ( 367 * ( month - 2 - 12 * m1 ) ) / 12; const double y1 = ::floor( ( 4900.0 + year + m1 ) / 100 ); const double jd = ::floor( ( 1461.0 * ( year + 4800 + m1 ) ) / 4 ) + m2 - ::floor( ( 3 * y1 ) / 4 ) + day - 32075; if ( jd > maxJulianDayD ) { qWarning() << "qwtToDate: overflow"; return QDate(); } return QDate::fromJulianDay( static_cast( jd ) ); } else { return QDate( year, month, day ); } #endif } /*! Translate from double to QDateTime \param value Number of milliseconds since the epoch, 1970-01-01T00:00:00 UTC \param timeSpec Time specification \return Datetime value \sa toDouble(), QDateTime::setMSecsSinceEpoch() \note The return datetime for Qt::OffsetFromUTC will be Qt::UTC */ QDateTime QwtDate::toDateTime( double value, Qt::TimeSpec timeSpec ) { const int msecsPerDay = 86400000; const double days = static_cast( ::floor( value / msecsPerDay ) ); const double jd = QwtDate::JulianDayForEpoch + days; if ( ( jd > maxJulianDayD ) || ( jd < minJulianDayD ) ) { qWarning() << "QwtDate::toDateTime: overflow"; return QDateTime(); } const QDate d = QDate::fromJulianDay( static_cast( jd ) ); const int msecs = static_cast( value - days * msecsPerDay ); static const QTime timeNull( 0, 0, 0, 0 ); QDateTime dt( d, timeNull.addMSecs( msecs ), Qt::UTC ); if ( timeSpec == Qt::LocalTime ) dt = qwtToTimeSpec( dt, timeSpec ); return dt; } /*! Translate from QDateTime to double \param dateTime Datetime value \return Number of milliseconds since 1970-01-01T00:00:00 UTC has passed. \sa toDateTime(), QDateTime::toMSecsSinceEpoch() \warning For values very far below or above 1970-01-01 UTC rounding errors will happen due to the limited significance of a double. */ double QwtDate::toDouble( const QDateTime &dateTime ) { const int msecsPerDay = 86400000; const QDateTime dt = qwtToTimeSpec( dateTime, Qt::UTC ); const double days = dt.date().toJulianDay() - QwtDate::JulianDayForEpoch; const QTime time = dt.time(); const double secs = 3600.0 * time.hour() + 60.0 * time.minute() + time.second(); return days * msecsPerDay + time.msec() + 1000.0 * secs; } /*! Ceil a datetime according the interval type \param dateTime Datetime value \param intervalType Interval type, how to ceil. F.e. when intervalType = QwtDate::Months, the result will be ceiled to the next beginning of a month \return Ceiled datetime \sa floor() */ QDateTime QwtDate::ceil( const QDateTime &dateTime, IntervalType intervalType ) { if ( dateTime.date() >= QwtDate::maxDate() ) return dateTime; QDateTime dt = dateTime; switch ( intervalType ) { case QwtDate::Millisecond: { break; } case QwtDate::Second: { qwtFloorTime( QwtDate::Second, dt ); if ( dt < dateTime ) dt.addSecs( 1 ); break; } case QwtDate::Minute: { qwtFloorTime( QwtDate::Minute, dt ); if ( dt < dateTime ) dt.addSecs( 60 ); break; } case QwtDate::Hour: { qwtFloorTime( QwtDate::Hour, dt ); if ( dt < dateTime ) dt.addSecs( 3600 ); break; } case QwtDate::Day: { dt.setTime( QTime( 0, 0 ) ); if ( dt < dateTime ) dt = dt.addDays( 1 ); break; } case QwtDate::Week: { dt.setTime( QTime( 0, 0 ) ); if ( dt < dateTime ) dt = dt.addDays( 1 ); int days = qwtFirstDayOfWeek() - dt.date().dayOfWeek(); if ( days < 0 ) days += 7; dt = dt.addDays( days ); break; } case QwtDate::Month: { dt.setTime( QTime( 0, 0 ) ); dt.setDate( qwtToDate( dateTime.date().year(), dateTime.date().month() ) ); if ( dt < dateTime ) dt.addMonths( 1 ); break; } case QwtDate::Year: { dt.setTime( QTime( 0, 0 ) ); const QDate d = dateTime.date(); int year = d.year(); if ( d.month() > 1 || d.day() > 1 || !dateTime.time().isNull() ) year++; if ( year == 0 ) year++; // there is no year 0 dt.setDate( qwtToDate( year ) ); break; } } return dt; } /*! Floor a datetime according the interval type \param dateTime Datetime value \param intervalType Interval type, how to ceil. F.e. when intervalType = QwtDate::Months, the result will be ceiled to the next beginning of a month \return Floored datetime \sa floor() */ QDateTime QwtDate::floor( const QDateTime &dateTime, IntervalType intervalType ) { if ( dateTime.date() <= QwtDate::minDate() ) return dateTime; QDateTime dt = dateTime; switch ( intervalType ) { case QwtDate::Millisecond: { break; } case QwtDate::Second: case QwtDate::Minute: case QwtDate::Hour: { qwtFloorTime( intervalType, dt ); break; } case QwtDate::Day: { dt.setTime( QTime( 0, 0 ) ); break; } case QwtDate::Week: { dt.setTime( QTime( 0, 0 ) ); int days = dt.date().dayOfWeek() - qwtFirstDayOfWeek(); if ( days < 0 ) days += 7; dt = dt.addDays( -days ); break; } case QwtDate::Month: { dt.setTime( QTime( 0, 0 ) ); const QDate date = qwtToDate( dt.date().year(), dt.date().month() ); dt.setDate( date ); break; } case QwtDate::Year: { dt.setTime( QTime( 0, 0 ) ); const QDate date = qwtToDate( dt.date().year() ); dt.setDate( date ); break; } } return dt; } /*! Minimum for the supported date range The range of valid dates depends on how QDate stores the Julian day internally. - For Qt4 it is "Tue Jan 2 -4713" - For Qt5 it is "Thu Jan 1 -2147483648" \return minimum of the date range \sa maxDate() */ QDate QwtDate::minDate() { static QDate date; if ( !date.isValid() ) date = QDate::fromJulianDay( minJulianDayD ); return date; } /*! Maximum for the supported date range The range of valid dates depends on how QDate stores the Julian day internally. - For Qt4 it is "Tue Jun 3 5874898" - For Qt5 it is "Tue Dec 31 2147483647" \return maximum of the date range \sa minDate() \note The maximum differs between Qt4 and Qt5 */ QDate QwtDate::maxDate() { static QDate date; if ( !date.isValid() ) date = QDate::fromJulianDay( maxJulianDayD ); return date; } /*! \brief Date of the first day of the first week for a year The first day of a week depends on the current locale ( QLocale::firstDayOfWeek() ). \param year Year \param type Option how to identify the first week \return First day of week 0 \sa QLocale::firstDayOfWeek(), weekNumber() */ QDate QwtDate::dateOfWeek0( int year, Week0Type type ) { const Qt::DayOfWeek firstDayOfWeek = qwtFirstDayOfWeek(); QDate dt0( year, 1, 1 ); // floor to the first day of the week int days = dt0.dayOfWeek() - firstDayOfWeek; if ( days < 0 ) days += 7; dt0 = dt0.addDays( -days ); if ( type == QwtDate::FirstThursday ) { // according to ISO 8601 the first week is defined // by the first thursday. int d = Qt::Thursday - firstDayOfWeek; if ( d < 0 ) d += 7; if ( dt0.addDays( d ).year() < year ) dt0 = dt0.addDays( 7 ); } return dt0; } /*! Find the week number of a date - QwtDate::FirstThursday\n Corresponding to ISO 8601 ( see QDate::weekNumber() ). - QwtDate::FirstDay\n Number of weeks that have begun since dateOfWeek0(). \param date Date \param type Option how to identify the first week \return Week number, starting with 1 */ int QwtDate::weekNumber( const QDate &date, Week0Type type ) { int weekNo; if ( type == QwtDate::FirstDay ) { const QDate day0 = dateOfWeek0( date.year(), type ); weekNo = day0.daysTo( date ) / 7 + 1; } else { weekNo = date.weekNumber(); } return weekNo; } /*! Offset in seconds from Coordinated Universal Time The offset depends on the time specification of dateTime: - Qt::UTC 0, dateTime has no offset - Qt::OffsetFromUTC returns dateTime.utcOffset() - Qt::LocalTime: number of seconds from the UTC For Qt::LocalTime the offset depends on the timezone and daylight savings. \param dateTime Datetime value \return Offset in seconds */ int QwtDate::utcOffset( const QDateTime &dateTime ) { int seconds = 0; switch( dateTime.timeSpec() ) { case Qt::UTC: { break; } case Qt::OffsetFromUTC: { seconds = dateTime.utcOffset(); } default: { const QDateTime dt1( dateTime.date(), dateTime.time(), Qt::UTC ); seconds = dateTime.secsTo( dt1 ); } } return seconds; } /*! Translate a datetime into a string Beside the format expressions documented in QDateTime::toString() the following expressions are supported: - w\n week number: ( 1 - 53 ) - ww\n week number with a leading zero ( 01 - 53 ) \param dateTime Datetime value \param format Format string \param week0Type Specification of week 0 \return Datetime string \sa QDateTime::toString(), weekNumber(), QwtDateScaleDraw */ QString QwtDate::toString( const QDateTime &dateTime, const QString & format, Week0Type week0Type ) { QString weekNo; weekNo.setNum( QwtDate::weekNumber( dateTime.date(), week0Type ) ); QString weekNoWW; if ( weekNo.length() == 1 ) weekNoWW += "0"; weekNoWW += weekNo; QString fmt = format; fmt.replace( "ww", weekNoWW ); fmt.replace( "w", weekNo ); return dateTime.toString( fmt ); } connectome-workbench-1.2.3+git41-gc4c6c90/src/Qwt/qwt_date.h000066400000000000000000000067321300200146000233100ustar00rootroot00000000000000/* -*- mode: C++ ; c-file-style: "stroustrup" -*- ***************************** * Qwt Widget Library * Copyright (C) 1997 Josef Wilgen * Copyright (C) 2002 Uwe Rathmann * * This library is free software; you can redistribute it and/or * modify it under the terms of the Qwt License, Version 1.0 *****************************************************************************/ #ifndef _QWT_DATE_H_ #define _QWT_DATE_H_ #include "qwt_global.h" #include /*! \brief A collection of methods around date/time values Qt offers convenient classes for dealing with date/time values, but Qwt uses coordinate systems that are based on doubles. QwtDate offers methods to translate from QDateTime to double and v.v. A double is interpreted as the number of milliseconds since 1970-01-01T00:00:00 Universal Coordinated Time - also known as "The Epoch". While the range of the Julian day in Qt4 is limited to [0, MAX_INT], Qt5 stores it as qint64 offering a huge range of valid dates. As the significance of a double is below this ( assuming a fraction of 52 bits ) the translation is not bijective with rounding errors for dates very far from Epoch. For a resolution of 1 ms those start to happen for dates above the year 144683. An axis for a date/time interval is expected to be aligned and divided in time/date units like seconds, minutes, ... QwtDate offers several algorithms that are needed to calculate these axes. \sa QwtDateScaleEngine, QwtDateScaleDraw, QDate, QTime */ class QWT_EXPORT QwtDate { public: /*! How to identify the first week of year differs between countries. */ enum Week0Type { /*! According to ISO 8601 the first week of a year is defined as "the week with the year's first Thursday in it". FirstThursday corresponds to the numbering that is implemented in QDate::weekNumber(). */ FirstThursday, /*! "The week with January 1.1 in it." In the U.S. this definition is more common than FirstThursday. */ FirstDay }; /*! Classification of an time interval Time intervals needs to be classified to decide how to align and divide it. */ enum IntervalType { //! The interval is related to milliseconds Millisecond, //! The interval is related to seconds Second, //! The interval is related to minutes Minute, //! The interval is related to hours Hour, //! The interval is related to days Day, //! The interval is related to weeks Week, //! The interval is related to months Month, //! The interval is related to years Year }; enum { //! The Julian day of "The Epoch" JulianDayForEpoch = 2440588 }; static QDate minDate(); static QDate maxDate(); static QDateTime toDateTime( double value, Qt::TimeSpec = Qt::UTC ); static double toDouble( const QDateTime & ); static QDateTime ceil( const QDateTime &, IntervalType ); static QDateTime floor( const QDateTime &, IntervalType ); static QDate dateOfWeek0( int year, Week0Type ); static int weekNumber( const QDate &, Week0Type ); static int utcOffset( const QDateTime & ); static QString toString( const QDateTime &, const QString & format, Week0Type ); }; #endif connectome-workbench-1.2.3+git41-gc4c6c90/src/Qwt/qwt_date_scale_draw.cpp000066400000000000000000000157761300200146000260370ustar00rootroot00000000000000/* -*- mode: C++ ; c-file-style: "stroustrup" -*- ***************************** * Qwt Widget Library * Copyright (C) 1997 Josef Wilgen * Copyright (C) 2002 Uwe Rathmann * * This library is free software; you can redistribute it and/or * modify it under the terms of the Qwt License, Version 1.0 *****************************************************************************/ #include "qwt_date_scale_draw.h" class QwtDateScaleDraw::PrivateData { public: PrivateData( Qt::TimeSpec spec ): timeSpec( spec ), utcOffset( 0 ), week0Type( QwtDate::FirstThursday ) { dateFormats[ QwtDate::Millisecond ] = "hh:mm:ss:zzz\nddd dd MMM yyyy"; dateFormats[ QwtDate::Second ] = "hh:mm:ss\nddd dd MMM yyyy"; dateFormats[ QwtDate::Minute ] = "hh:mm\nddd dd MMM yyyy"; dateFormats[ QwtDate::Hour ] = "hh:mm\nddd dd MMM yyyy"; dateFormats[ QwtDate::Day ] = "ddd dd MMM yyyy"; dateFormats[ QwtDate::Week ] = "Www yyyy"; dateFormats[ QwtDate::Month ] = "MMM yyyy"; dateFormats[ QwtDate::Year ] = "yyyy"; } Qt::TimeSpec timeSpec; int utcOffset; QwtDate::Week0Type week0Type; QString dateFormats[ QwtDate::Year + 1 ]; }; /*! \brief Constructor The default setting is to display tick labels for the given time specification. The first week of a year is defined like for QwtDate::FirstThursday. \param timeSpec Time specification \sa setTimeSpec(), setWeek0Type() */ QwtDateScaleDraw::QwtDateScaleDraw( Qt::TimeSpec timeSpec ) { d_data = new PrivateData( timeSpec ); } //! Destructor QwtDateScaleDraw::~QwtDateScaleDraw() { delete d_data; } /*! Set the time specification used for the tick labels \param timeSpec Time specification \sa timeSpec(), setUtcOffset(), toDateTime() */ void QwtDateScaleDraw::setTimeSpec( Qt::TimeSpec timeSpec ) { d_data->timeSpec = timeSpec; } /*! \return Time specification used for the tick labels \sa setTimeSpec(), utcOffset(), toDateTime() */ Qt::TimeSpec QwtDateScaleDraw::timeSpec() const { return d_data->timeSpec; } /*! Set the offset in seconds from Coordinated Universal Time \param seconds Offset in seconds \note The offset has no effect beside for the time specification Qt::OffsetFromUTC. \sa QDate::utcOffset(), setTimeSpec(), toDateTime() */ void QwtDateScaleDraw::setUtcOffset( int seconds ) { d_data->utcOffset = seconds; } /*! \return Offset in seconds from Coordinated Universal Time \note The offset has no effect beside for the time specification Qt::OffsetFromUTC. \sa QDate::setUtcOffset(), setTimeSpec(), toDateTime() */ int QwtDateScaleDraw::utcOffset() const { return d_data->utcOffset; } /*! Sets how to identify the first week of a year. \param week0Type Mode how to identify the first week of a year \sa week0Type(). \note week0Type has no effect beside for intervals classified as QwtDate::Week. */ void QwtDateScaleDraw::setWeek0Type( QwtDate::Week0Type week0Type ) { d_data->week0Type = week0Type; } /*! \return Setting how to identify the first week of a year. \sa setWeek0Type() */ QwtDate::Week0Type QwtDateScaleDraw::week0Type() const { return d_data->week0Type; } /*! Set the default format string for an datetime interval type \param intervalType Interval type \param format Default format string \sa dateFormat(), dateFormatOfDate(), QwtDate::toString() */ void QwtDateScaleDraw::setDateFormat( QwtDate::IntervalType intervalType, const QString &format ) { if ( intervalType >= QwtDate::Millisecond && intervalType <= QwtDate::Year ) { d_data->dateFormats[ intervalType ] = format; } } /*! \param intervalType Interval type \return Default format string for an datetime interval type \sa setDateFormat(), dateFormatOfDate() */ QString QwtDateScaleDraw::dateFormat( QwtDate::IntervalType intervalType ) const { if ( intervalType >= QwtDate::Millisecond && intervalType <= QwtDate::Year ) { return d_data->dateFormats[ intervalType ]; } return QString::null; } /*! Format string for the representation of a datetime dateFormatOfDate() is intended to be overloaded for situations, where formats are individual for specific datetime values. The default setting ignores dateTime and return the default format for the interval type. \param dateTime Datetime value \param intervalType Interval type \return Format string \sa setDateFormat(), QwtDate::toString() */ QString QwtDateScaleDraw::dateFormatOfDate( const QDateTime &dateTime, QwtDate::IntervalType intervalType ) const { Q_UNUSED( dateTime ) if ( intervalType >= QwtDate::Millisecond && intervalType <= QwtDate::Year ) { return d_data->dateFormats[ intervalType ]; } return d_data->dateFormats[ QwtDate::Second ]; } /*! \brief Convert a value into its representing label The value is converted to a datetime value using toDateTime() and converted to a plain text using QwtDate::toString(). \param value Value \return Label string. \sa dateFormatOfDate() */ QwtText QwtDateScaleDraw::label( double value ) const { const QDateTime dt = toDateTime( value ); const QString fmt = dateFormatOfDate( dt, intervalType( scaleDiv() ) ); return QwtDate::toString( dt, fmt, d_data->week0Type ); } /*! Find the less detailed datetime unit, where no rounding errors happen. \param scaleDiv Scale division \return Interval type \sa dateFormatOfDate() */ QwtDate::IntervalType QwtDateScaleDraw::intervalType( const QwtScaleDiv &scaleDiv ) const { int intvType = QwtDate::Year; bool alignedToWeeks = true; const QList ticks = scaleDiv.ticks( QwtScaleDiv::MajorTick ); for ( int i = 0; i < ticks.size(); i++ ) { const QDateTime dt = toDateTime( ticks[i] ); for ( int j = QwtDate::Second; j <= intvType; j++ ) { const QDateTime dt0 = QwtDate::floor( dt, static_cast( j ) ); if ( dt0 != dt ) { if ( j == QwtDate::Week ) { alignedToWeeks = false; } else { intvType = j - 1; break; } } } if ( intvType == QwtDate::Millisecond ) break; } if ( intvType == QwtDate::Week && !alignedToWeeks ) intvType = QwtDate::Day; return static_cast( intvType ); } /*! Translate a double value into a QDateTime object. \return QDateTime object initialized with timeSpec() and utcOffset(). \sa timeSpec(), utcOffset(), QwtDate::toDateTime() */ QDateTime QwtDateScaleDraw::toDateTime( double value ) const { QDateTime dt = QwtDate::toDateTime( value, d_data->timeSpec ); if ( d_data->timeSpec == Qt::OffsetFromUTC ) { dt = dt.addSecs( d_data->utcOffset ); dt.setUtcOffset( d_data->utcOffset ); } return dt; } connectome-workbench-1.2.3+git41-gc4c6c90/src/Qwt/qwt_date_scale_draw.h000066400000000000000000000042401300200146000254640ustar00rootroot00000000000000/* -*- mode: C++ ; c-file-style: "stroustrup" -*- ***************************** * Qwt Widget Library * Copyright (C) 1997 Josef Wilgen * Copyright (C) 2002 Uwe Rathmann * * This library is free software; you can redistribute it and/or * modify it under the terms of the Qwt License, Version 1.0 *****************************************************************************/ #ifndef _QWT_DATE_SCALE_DRAW_H_ #define _QWT_DATE_SCALE_DRAW_H_ 1 #include "qwt_global.h" #include "qwt_scale_draw.h" #include "qwt_date.h" /*! \brief A class for drawing datetime scales QwtDateScaleDraw displays values as datetime labels. The format of the labels depends on the alignment of the major tick labels. The default format strings are: - Millisecond\n "hh:mm:ss:zzz\nddd dd MMM yyyy" - Second\n "hh:mm:ss\nddd dd MMM yyyy" - Minute\n "hh:mm\nddd dd MMM yyyy" - Hour\n "hh:mm\nddd dd MMM yyyy" - Day\n "ddd dd MMM yyyy" - Week\n "Www yyyy" - Month\n "MMM yyyy" - Year\n "yyyy" The format strings can be modified using setDateFormat() or individually for each tick label by overloading dateFormatOfDate(), Usually QwtDateScaleDraw is used in combination with QwtDateScaleEngine, that calculates scales for datetime intervals. \sa QwtDateScaleEngine, QwtPlot::setAxisScaleDraw() */ class QWT_EXPORT QwtDateScaleDraw: public QwtScaleDraw { public: QwtDateScaleDraw( Qt::TimeSpec = Qt::LocalTime ); virtual ~QwtDateScaleDraw(); void setDateFormat( QwtDate::IntervalType, const QString & ); QString dateFormat( QwtDate::IntervalType ) const; void setTimeSpec( Qt::TimeSpec ); Qt::TimeSpec timeSpec() const; void setUtcOffset( int seconds ); int utcOffset() const; void setWeek0Type( QwtDate::Week0Type ); QwtDate::Week0Type week0Type() const; virtual QwtText label( double ) const; QDateTime toDateTime( double ) const; protected: virtual QwtDate::IntervalType intervalType( const QwtScaleDiv & ) const; virtual QString dateFormatOfDate( const QDateTime &, QwtDate::IntervalType ) const; private: class PrivateData; PrivateData *d_data; }; #endif connectome-workbench-1.2.3+git41-gc4c6c90/src/Qwt/qwt_date_scale_engine.cpp000066400000000000000000001063671300200146000263440ustar00rootroot00000000000000/* -*- mode: C++ ; c-file-style: "stroustrup" -*- ***************************** * Qwt Widget Library * Copyright (C) 1997 Josef Wilgen * Copyright (C) 2002 Uwe Rathmann * * This library is free software; you can redistribute it and/or * modify it under the terms of the Qwt License, Version 1.0 *****************************************************************************/ #include "qwt_date_scale_engine.h" #include "qwt_math.h" #include "qwt_transform.h" #include #include static inline double qwtMsecsForType( QwtDate::IntervalType type ) { static const double msecs[] = { 1.0, 1000.0, 60.0 * 1000.0, 3600.0 * 1000.0, 24.0 * 3600.0 * 1000.0, 7.0 * 24.0 * 3600.0 * 1000.0, 30.0 * 24.0 * 3600.0 * 1000.0, 365.0 * 24.0 * 3600.0 * 1000.0, }; // eliminate compile time warning - JWH 9/16/2016 // if ( type < 0 || type >= static_cast( sizeof( msecs ) / sizeof( msecs[0] ) ) ) const int typeAsInt = static_cast(type); if ( typeAsInt < 0 || typeAsInt >= static_cast( sizeof( msecs ) / sizeof( msecs[0] ) ) ) return 1.0; return msecs[ type ]; } static inline int qwtAlignValue( double value, double stepSize, bool up ) { double d = value / stepSize; d = up ? ::ceil( d ) : ::floor( d ); return static_cast( d * stepSize ); } static double qwtIntervalWidth( const QDateTime &minDate, const QDateTime &maxDate, QwtDate::IntervalType intervalType ) { switch( intervalType ) { case QwtDate::Millisecond: { const double secsTo = minDate.secsTo( maxDate ); const double msecs = maxDate.time().msec() - minDate.time().msec(); return secsTo * 1000 + msecs; } case QwtDate::Second: { return minDate.secsTo( maxDate ); } case QwtDate::Minute: { const double secsTo = minDate.secsTo( maxDate ); return ::floor( secsTo / 60 ); } case QwtDate::Hour: { const double secsTo = minDate.secsTo( maxDate ); return ::floor( secsTo / 3600 ); } case QwtDate::Day: { return minDate.daysTo( maxDate ); } case QwtDate::Week: { return ::floor( minDate.daysTo( maxDate ) / 7.0 ); } case QwtDate::Month: { const double years = double( maxDate.date().year() ) - minDate.date().year(); int months = maxDate.date().month() - minDate.date().month(); if ( maxDate.date().day() < minDate.date().day() ) months--; return years * 12 + months; } case QwtDate::Year: { double years = double( maxDate.date().year() ) - minDate.date().year(); if ( maxDate.date().month() < minDate.date().month() ) years -= 1.0; return years; } } return 0.0; } static double qwtRoundedIntervalWidth( const QDateTime &minDate, const QDateTime &maxDate, QwtDate::IntervalType intervalType ) { const QDateTime minD = QwtDate::floor( minDate, intervalType ); const QDateTime maxD = QwtDate::ceil( maxDate, intervalType ); return qwtIntervalWidth( minD, maxD, intervalType ); } static inline int qwtStepCount( int intervalSize, int maxSteps, const int limits[], size_t numLimits ) { for ( uint i = 0; i < numLimits; i++ ) { const int numSteps = intervalSize / limits[ i ]; if ( numSteps > 1 && numSteps <= maxSteps && numSteps * limits[ i ] == intervalSize ) { return numSteps; } } return 0; } static int qwtStepSize( int intervalSize, int maxSteps, uint base ) { if ( maxSteps <= 0 ) return 0; if ( maxSteps > 2 ) { for ( int numSteps = maxSteps; numSteps > 1; numSteps-- ) { const double stepSize = double( intervalSize ) / numSteps; const double p = ::floor( ::log( stepSize ) / ::log( double( base ) ) ); const double fraction = qPow( base, p ); for ( uint n = base; n >= 1; n /= 2 ) { if ( qFuzzyCompare( stepSize, n * fraction ) ) return qRound( stepSize ); if ( n == 3 && ( base % 2 ) == 0 ) { if ( qFuzzyCompare( stepSize, 2 * fraction ) ) return qRound( stepSize ); } } } } return 0; } static int qwtDivideInterval( double intervalSize, int numSteps, const int limits[], size_t numLimits ) { const int v = qCeil( intervalSize / double( numSteps ) ); for ( uint i = 0; i < numLimits - 1; i++ ) { if ( v <= limits[i] ) return limits[i]; } return limits[ numLimits - 1 ]; } static double qwtDivideScale( double intervalSize, int numSteps, QwtDate::IntervalType intervalType ) { if ( intervalType != QwtDate::Day ) { if ( ( intervalSize > numSteps ) && ( intervalSize <= 2 * numSteps ) ) { return 2.0; } } double stepSize; switch( intervalType ) { case QwtDate::Second: case QwtDate::Minute: { static int limits[] = { 1, 2, 5, 10, 15, 20, 30, 60 }; stepSize = qwtDivideInterval( intervalSize, numSteps, limits, sizeof( limits ) / sizeof( int ) ); break; } case QwtDate::Hour: { static int limits[] = { 1, 2, 3, 4, 6, 12, 24 }; stepSize = qwtDivideInterval( intervalSize, numSteps, limits, sizeof( limits ) / sizeof( int ) ); break; } case QwtDate::Day: { const double v = intervalSize / double( numSteps ); if ( v <= 5.0 ) stepSize = qCeil( v ); else stepSize = qCeil( v / 7 ) * 7; break; } case QwtDate::Week: { static int limits[] = { 1, 2, 4, 8, 12, 26, 52 }; stepSize = qwtDivideInterval( intervalSize, numSteps, limits, sizeof( limits ) / sizeof( int ) ); break; } case QwtDate::Month: { static int limits[] = { 1, 2, 3, 4, 6, 12 }; stepSize = qwtDivideInterval( intervalSize, numSteps, limits, sizeof( limits ) / sizeof( int ) ); break; } case QwtDate::Year: case QwtDate::Millisecond: default: { stepSize = QwtScaleArithmetic::divideInterval( intervalSize, numSteps, 10 ); } } return stepSize; } static double qwtDivideMajorStep( double stepSize, int maxMinSteps, QwtDate::IntervalType intervalType ) { double minStepSize = 0.0; switch( intervalType ) { case QwtDate::Second: { minStepSize = qwtStepSize( stepSize, maxMinSteps, 10 ); if ( minStepSize == 0.0 ) minStepSize = 0.5 * stepSize; break; } case QwtDate::Minute: { static int limits[] = { 1, 2, 5, 10, 15, 20, 30, 60 }; int numSteps; if ( stepSize > maxMinSteps ) { numSteps = qwtStepCount( stepSize, maxMinSteps, limits, sizeof( limits ) / sizeof( int ) ); } else { numSteps = qwtStepCount( stepSize * 60, maxMinSteps, limits, sizeof( limits ) / sizeof( int ) ); } if ( numSteps > 0 ) minStepSize = double( stepSize ) / numSteps; break; } case QwtDate::Hour: { int numSteps = 0; if ( stepSize > maxMinSteps ) { static int limits[] = { 1, 2, 3, 4, 6, 12, 24, 48, 72 }; numSteps = qwtStepCount( stepSize, maxMinSteps, limits, sizeof( limits ) / sizeof( int ) ); } else { static int limits[] = { 1, 2, 5, 10, 15, 20, 30, 60 }; numSteps = qwtStepCount( stepSize * 60, maxMinSteps, limits, sizeof( limits ) / sizeof( int ) ); } if ( numSteps > 0 ) minStepSize = double( stepSize ) / numSteps; break; } case QwtDate::Day: { int numSteps = 0; if ( stepSize > maxMinSteps ) { static int limits[] = { 1, 2, 3, 7, 14, 28 }; numSteps = qwtStepCount( stepSize, maxMinSteps, limits, sizeof( limits ) / sizeof( int ) ); } else { static int limits[] = { 1, 2, 3, 4, 6, 12, 24, 48, 72 }; numSteps = qwtStepCount( stepSize * 24, maxMinSteps, limits, sizeof( limits ) / sizeof( int ) ); } if ( numSteps > 0 ) minStepSize = double( stepSize ) / numSteps; break; } case QwtDate::Week: { const int daysInStep = stepSize * 7; if ( maxMinSteps >= daysInStep ) { // we want to have one tick per day minStepSize = 1.0 / 7.0; } else { // when the stepSize is more than a week we want to // have a tick for each week const int stepSizeInWeeks = stepSize; if ( stepSizeInWeeks <= maxMinSteps ) { minStepSize = 1; } else { minStepSize = QwtScaleArithmetic::divideInterval( stepSizeInWeeks, maxMinSteps, 10 ); } } break; } case QwtDate::Month: { // fractions of months doesn't make any sense if ( stepSize < maxMinSteps ) maxMinSteps = static_cast( stepSize ); static int limits[] = { 1, 2, 3, 4, 6, 12 }; int numSteps = qwtStepCount( stepSize, maxMinSteps, limits, sizeof( limits ) / sizeof( int ) ); if ( numSteps > 0 ) minStepSize = double( stepSize ) / numSteps; break; } case QwtDate::Year: { if ( stepSize >= maxMinSteps ) { minStepSize = QwtScaleArithmetic::divideInterval( stepSize, maxMinSteps, 10 ); } else { // something in months static int limits[] = { 1, 2, 3, 4, 6, 12 }; int numSteps = qwtStepCount( 12 * stepSize, maxMinSteps, limits, sizeof( limits ) / sizeof( int ) ); if ( numSteps > 0 ) minStepSize = double( stepSize ) / numSteps; } break; } default: break; } if ( intervalType != QwtDate::Month && minStepSize == 0.0 ) { minStepSize = 0.5 * stepSize; } return minStepSize; } static QList qwtDstTicks( const QDateTime &dateTime, int secondsMajor, int secondsMinor ) { if ( secondsMinor <= 0 ) QList(); QDateTime minDate = dateTime.addSecs( -secondsMajor ); minDate = QwtDate::floor( minDate, QwtDate::Hour ); const double utcOffset = QwtDate::utcOffset( dateTime ); // find the hours where daylight saving time happens double dstMin = QwtDate::toDouble( minDate ); while ( minDate < dateTime && QwtDate::utcOffset( minDate ) != utcOffset ) { minDate = minDate.addSecs( 3600 ); dstMin += 3600 * 1000.0; } QList ticks; for ( int i = 0; i < 3600; i += secondsMinor ) ticks += dstMin + i * 1000.0; return ticks; } static QwtScaleDiv qwtDivideToSeconds( const QDateTime &minDate, const QDateTime &maxDate, double stepSize, int maxMinSteps, QwtDate::IntervalType intervalType ) { // calculate the min step size double minStepSize = 0; if ( maxMinSteps > 1 ) { minStepSize = qwtDivideMajorStep( stepSize, maxMinSteps, intervalType ); } bool daylightSaving = false; if ( minDate.timeSpec() == Qt::LocalTime ) { daylightSaving = intervalType > QwtDate::Hour; if ( intervalType == QwtDate::Hour ) { daylightSaving = stepSize > 1; } } const double s = qwtMsecsForType( intervalType ) / 1000; const int secondsMajor = static_cast( stepSize * s ); const double secondsMinor = minStepSize * s; // UTC excludes daylight savings. So from the difference // of a date and its UTC counterpart we can find out // the daylight saving hours const double utcOffset = QwtDate::utcOffset( minDate ); double dstOff = 0; QList majorTicks; QList mediumTicks; QList minorTicks; for ( QDateTime dt = minDate; dt <= maxDate; dt = dt.addSecs( secondsMajor ) ) { if ( !dt.isValid() ) break; double majorValue = QwtDate::toDouble( dt ); if ( daylightSaving ) { const double offset = utcOffset - QwtDate::utcOffset( dt ); majorValue += offset * 1000.0; if ( offset > dstOff ) { // we add some minor ticks for the DST hour, // otherwise the ticks will be unaligned: 0, 2, 3, 5 ... minorTicks += qwtDstTicks( dt, secondsMajor, qRound( secondsMinor ) ); } dstOff = offset; } if ( majorTicks.isEmpty() || majorTicks.last() != majorValue ) majorTicks += majorValue; if ( secondsMinor > 0.0 ) { const int numMinorSteps = qFloor( secondsMajor / secondsMinor ); for ( int i = 1; i < numMinorSteps; i++ ) { const QDateTime mt = dt.addMSecs( qRound64( i * secondsMinor * 1000 ) ); double minorValue = QwtDate::toDouble( mt ); if ( daylightSaving ) { const double offset = utcOffset - QwtDate::utcOffset( mt ); minorValue += offset * 1000.0; } if ( minorTicks.isEmpty() || minorTicks.last() != minorValue ) { const bool isMedium = ( numMinorSteps % 2 == 0 ) && ( i != 1 ) && ( i == numMinorSteps / 2 ); if ( isMedium ) mediumTicks += minorValue; else minorTicks += minorValue; } } } } QwtScaleDiv scaleDiv; scaleDiv.setInterval( QwtDate::toDouble( minDate ), QwtDate::toDouble( maxDate ) ); scaleDiv.setTicks( QwtScaleDiv::MajorTick, majorTicks ); scaleDiv.setTicks( QwtScaleDiv::MediumTick, mediumTicks ); scaleDiv.setTicks( QwtScaleDiv::MinorTick, minorTicks ); return scaleDiv; } static QwtScaleDiv qwtDivideToMonths( QDateTime &minDate, const QDateTime &maxDate, double stepSize, int maxMinSteps ) { // months are intervals with non // equidistant ( in ms ) steps: we have to build the // scale division manually int minStepDays = 0; int minStepSize = 0.0; if ( maxMinSteps > 1 ) { if ( stepSize == 1 ) { if ( maxMinSteps >= 30 ) minStepDays = 1; else if ( maxMinSteps >= 6 ) minStepDays = 5; else if ( maxMinSteps >= 3 ) minStepDays = 10; minStepDays = 15; } else { minStepSize = qwtDivideMajorStep( stepSize, maxMinSteps, QwtDate::Month ); } } QList majorTicks; QList mediumTicks; QList minorTicks; for ( QDateTime dt = minDate; dt <= maxDate; dt = dt.addMonths( stepSize ) ) { if ( !dt.isValid() ) break; majorTicks += QwtDate::toDouble( dt ); if ( minStepDays > 0 ) { for ( int days = minStepDays; days < 30; days += minStepDays ) { const double tick = QwtDate::toDouble( dt.addDays( days ) ); if ( days == 15 && minStepDays != 15 ) mediumTicks += tick; else minorTicks += tick; } } else if ( minStepSize > 0.0 ) { const int numMinorSteps = qRound( stepSize / (double) minStepSize ); for ( int i = 1; i < numMinorSteps; i++ ) { const double minorValue = QwtDate::toDouble( dt.addMonths( i * minStepSize ) ); if ( ( numMinorSteps % 2 == 0 ) && ( i == numMinorSteps / 2 ) ) mediumTicks += minorValue; else minorTicks += minorValue; } } } QwtScaleDiv scaleDiv; scaleDiv.setInterval( QwtDate::toDouble( minDate ), QwtDate::toDouble( maxDate ) ); scaleDiv.setTicks( QwtScaleDiv::MajorTick, majorTicks ); scaleDiv.setTicks( QwtScaleDiv::MediumTick, mediumTicks ); scaleDiv.setTicks( QwtScaleDiv::MinorTick, minorTicks ); return scaleDiv; } static QwtScaleDiv qwtDivideToYears( const QDateTime &minDate, const QDateTime &maxDate, double stepSize, int maxMinSteps ) { QList majorTicks; QList mediumTicks; QList minorTicks; double minStepSize = 0.0; if ( maxMinSteps > 1 ) { minStepSize = qwtDivideMajorStep( stepSize, maxMinSteps, QwtDate::Year ); } int numMinorSteps = 0; if ( minStepSize > 0.0 ) numMinorSteps = qFloor( stepSize / minStepSize ); bool dateBC = minDate.date().year() < -1; for ( QDateTime dt = minDate; dt <= maxDate; dt = dt.addYears( stepSize ) ) { if ( dateBC && dt.date().year() > 1 ) { // there is no year 0 in the Julian calendar dt = dt.addYears( -1 ); dateBC = false; } if ( !dt.isValid() ) break; majorTicks += QwtDate::toDouble( dt ); for ( int i = 1; i < numMinorSteps; i++ ) { QDateTime tickDate; const double years = qRound( i * minStepSize ); if ( years >= INT_MAX / 12 ) { tickDate = dt.addYears( years ); } else { tickDate = dt.addMonths( qRound( years * 12 ) ); } const bool isMedium = ( numMinorSteps > 2 ) && ( numMinorSteps % 2 == 0 ) && ( i == numMinorSteps / 2 ); const double minorValue = QwtDate::toDouble( tickDate ); if ( isMedium ) mediumTicks += minorValue; else minorTicks += minorValue; } if ( QwtDate::maxDate().addYears( -stepSize ) < dt.date() ) { break; } } QwtScaleDiv scaleDiv; scaleDiv.setInterval( QwtDate::toDouble( minDate ), QwtDate::toDouble( maxDate ) ); scaleDiv.setTicks( QwtScaleDiv::MajorTick, majorTicks ); scaleDiv.setTicks( QwtScaleDiv::MediumTick, mediumTicks ); scaleDiv.setTicks( QwtScaleDiv::MinorTick, minorTicks ); return scaleDiv; } class QwtDateScaleEngine::PrivateData { public: PrivateData( Qt::TimeSpec spec ): timeSpec( spec ), utcOffset( 0 ), week0Type( QwtDate::FirstThursday ), maxWeeks( 4 ) { } Qt::TimeSpec timeSpec; int utcOffset; QwtDate::Week0Type week0Type; int maxWeeks; }; /*! \brief Constructor The engine is initialized to build scales for the given time specification. It classifies intervals > 4 weeks as >= Qt::Month. The first week of a year is defined like for QwtDate::FirstThursday. \param timeSpec Time specification \sa setTimeSpec(), setMaxWeeks(), setWeek0Type() */ QwtDateScaleEngine::QwtDateScaleEngine( Qt::TimeSpec timeSpec ): QwtLinearScaleEngine( 10 ) { d_data = new PrivateData( timeSpec ); } //! Destructor QwtDateScaleEngine::~QwtDateScaleEngine() { delete d_data; } /*! Set the time specification used by the engine \param timeSpec Time specification \sa timeSpec(), setUtcOffset(), toDateTime() */ void QwtDateScaleEngine::setTimeSpec( Qt::TimeSpec timeSpec ) { d_data->timeSpec = timeSpec; } /*! \return Time specification used by the engine \sa setTimeSpec(), utcOffset(), toDateTime() */ Qt::TimeSpec QwtDateScaleEngine::timeSpec() const { return d_data->timeSpec; } /*! Set the offset in seconds from Coordinated Universal Time \param seconds Offset in seconds \note The offset has no effect beside for the time specification Qt::OffsetFromUTC. \sa QDate::utcOffset(), setTimeSpec(), toDateTime() */ void QwtDateScaleEngine::setUtcOffset( int seconds ) { d_data->utcOffset = seconds; } /*! \return Offset in seconds from Coordinated Universal Time \note The offset has no effect beside for the time specification Qt::OffsetFromUTC. \sa QDate::setUtcOffset(), setTimeSpec(), toDateTime() */ int QwtDateScaleEngine::utcOffset() const { return d_data->utcOffset; } /*! Sets how to identify the first week of a year. \param week0Type Mode how to identify the first week of a year \sa week0Type(), setMaxWeeks() \note week0Type has no effect beside for intervals classified as QwtDate::Week. */ void QwtDateScaleEngine::setWeek0Type( QwtDate::Week0Type week0Type ) { d_data->week0Type = week0Type; } /*! \return Setting how to identify the first week of a year. \sa setWeek0Type(), maxWeeks() */ QwtDate::Week0Type QwtDateScaleEngine::week0Type() const { return d_data->week0Type; } /*! Set a upper limit for the number of weeks, when an interval can be classified as Qt::Week. The default setting is 4 weeks. \param weeks Upper limit for the number of weeks \note In business charts a year is often devided into weeks [1-52] \sa maxWeeks(), setWeek0Type() */ void QwtDateScaleEngine::setMaxWeeks( int weeks ) { d_data->maxWeeks = qMax( weeks, 0 ); } /*! \return Upper limit for the number of weeks, when an interval can be classified as Qt::Week. \sa setMaxWeeks(), week0Type() */ int QwtDateScaleEngine::maxWeeks() const { return d_data->maxWeeks; } /*! Classification of a date/time interval division \param minDate Minimum ( = earlier ) of the interval \param maxDate Maximum ( = later ) of the interval \param maxSteps Maximum for the number of steps \return Interval classification */ QwtDate::IntervalType QwtDateScaleEngine::intervalType( const QDateTime &minDate, const QDateTime &maxDate, int maxSteps ) const { const double jdMin = minDate.date().toJulianDay(); const double jdMax = maxDate.date().toJulianDay(); if ( ( jdMax - jdMin ) / 365 > maxSteps ) return QwtDate::Year; const int months = qwtRoundedIntervalWidth( minDate, maxDate, QwtDate::Month ); if ( months > maxSteps * 6 ) return QwtDate::Year; const int days = qwtRoundedIntervalWidth( minDate, maxDate, QwtDate::Day ); const int weeks = qwtRoundedIntervalWidth( minDate, maxDate, QwtDate::Week ); if ( weeks > d_data->maxWeeks ) { if ( days > 4 * maxSteps * 7 ) return QwtDate::Month; } if ( days > maxSteps * 7 ) return QwtDate::Week; const int hours = qwtRoundedIntervalWidth( minDate, maxDate, QwtDate::Hour ); if ( hours > maxSteps * 24 ) return QwtDate::Day; const int seconds = qwtRoundedIntervalWidth( minDate, maxDate, QwtDate::Second ); if ( seconds >= maxSteps * 3600 ) return QwtDate::Hour; if ( seconds >= maxSteps * 60 ) return QwtDate::Minute; if ( seconds >= maxSteps ) return QwtDate::Second; return QwtDate::Millisecond; } /*! Align and divide an interval The algorithm aligns and divides the interval into steps. Datetime interval divisions are usually not equidistant and the calculated stepSize can only be used as an approximation for the steps calculated by divideScale(). \param maxNumSteps Max. number of steps \param x1 First limit of the interval (In/Out) \param x2 Second limit of the interval (In/Out) \param stepSize Step size (Out) \sa QwtScaleEngine::setAttribute() */ void QwtDateScaleEngine::autoScale( int maxNumSteps, double &x1, double &x2, double &stepSize ) const { stepSize = 0.0; QwtInterval interval( x1, x2 ); interval = interval.normalized(); interval.setMinValue( interval.minValue() - lowerMargin() ); interval.setMaxValue( interval.maxValue() + upperMargin() ); if ( testAttribute( QwtScaleEngine::Symmetric ) ) interval = interval.symmetrize( reference() ); if ( testAttribute( QwtScaleEngine::IncludeReference ) ) interval = interval.extend( reference() ); if ( interval.width() == 0.0 ) interval = buildInterval( interval.minValue() ); const QDateTime from = toDateTime( interval.minValue() ); const QDateTime to = toDateTime( interval.maxValue() ); if ( from.isValid() && to.isValid() ) { if ( maxNumSteps < 1 ) maxNumSteps = 1; const QwtDate::IntervalType intvType = intervalType( from, to, maxNumSteps ); const double width = qwtIntervalWidth( from, to, intvType ); const double stepWidth = qwtDivideScale( width, maxNumSteps, intvType ); if ( stepWidth != 0.0 && !testAttribute( QwtScaleEngine::Floating ) ) { const QDateTime d1 = alignDate( from, stepWidth, intvType, false ); const QDateTime d2 = alignDate( to, stepWidth, intvType, true ); interval.setMinValue( QwtDate::toDouble( d1 ) ); interval.setMaxValue( QwtDate::toDouble( d2 ) ); } stepSize = stepWidth * qwtMsecsForType( intvType ); } x1 = interval.minValue(); x2 = interval.maxValue(); if ( testAttribute( QwtScaleEngine::Inverted ) ) { qSwap( x1, x2 ); stepSize = -stepSize; } } /*! \brief Calculate a scale division for a date/time interval \param x1 First interval limit \param x2 Second interval limit \param maxMajorSteps Maximum for the number of major steps \param maxMinorSteps Maximum number of minor steps \param stepSize Step size. If stepSize == 0, the scaleEngine calculates one. \return Calculated scale division */ QwtScaleDiv QwtDateScaleEngine::divideScale( double x1, double x2, int maxMajorSteps, int maxMinorSteps, double stepSize ) const { if ( maxMajorSteps < 1 ) maxMajorSteps = 1; const double min = qMin( x1, x2 ); const double max = qMax( x1, x2 ); const QDateTime from = toDateTime( min ); const QDateTime to = toDateTime( max ); if ( from == to ) return QwtScaleDiv(); stepSize = qAbs( stepSize ); if ( stepSize > 0.0 ) { // as interval types above hours are not equidistant // ( even days might have 23/25 hours because of daylight saving ) // the stepSize is used as a hint only maxMajorSteps = qCeil( ( max - min ) / stepSize ); } const QwtDate::IntervalType intvType = intervalType( from, to, maxMajorSteps ); QwtScaleDiv scaleDiv; if ( intvType == QwtDate::Millisecond ) { // for milliseconds and below we can use the decimal system scaleDiv = QwtLinearScaleEngine::divideScale( min, max, maxMajorSteps, maxMinorSteps, stepSize ); } else { const QDateTime minDate = QwtDate::floor( from, intvType ); const QDateTime maxDate = QwtDate::ceil( to, intvType ); scaleDiv = buildScaleDiv( minDate, maxDate, maxMajorSteps, maxMinorSteps, intvType ); // scaleDiv has been calculated from an extended interval // adjusted to the step size. We have to shrink it again. scaleDiv = scaleDiv.bounded( min, max ); } if ( x1 > x2 ) scaleDiv.invert(); return scaleDiv; } QwtScaleDiv QwtDateScaleEngine::buildScaleDiv( const QDateTime &minDate, const QDateTime &maxDate, int maxMajorSteps, int maxMinorSteps, QwtDate::IntervalType intervalType ) const { // calculate the step size const double stepSize = qwtDivideScale( qwtIntervalWidth( minDate, maxDate, intervalType ), maxMajorSteps, intervalType ); // align minDate to the step size QDateTime dt0 = alignDate( minDate, stepSize, intervalType, false ); if ( !dt0.isValid() ) { // the floored date is out of the range of a // QDateTime - we ceil instead. dt0 = alignDate( minDate, stepSize, intervalType, true ); } QwtScaleDiv scaleDiv; if ( intervalType <= QwtDate::Week ) { scaleDiv = qwtDivideToSeconds( dt0, maxDate, stepSize, maxMinorSteps, intervalType ); } else { if( intervalType == QwtDate::Month ) { scaleDiv = qwtDivideToMonths( dt0, maxDate, stepSize, maxMinorSteps ); } else if ( intervalType == QwtDate::Year ) { scaleDiv = qwtDivideToYears( dt0, maxDate, stepSize, maxMinorSteps ); } } return scaleDiv; } /*! Align a date/time value for a step size For Qt::Day alignments there is no "natural day 0" - instead the first day of the year is used to avoid jumping major ticks positions when panning a scale. For other alignments ( f.e according to the first day of the month ) alignDate() has to be overloaded. \param dateTime Date/time value \param stepSize Step size \param intervalType Interval type \param up When true dateTime is ceiled - otherwise it is floored \return Aligned date/time value */ QDateTime QwtDateScaleEngine::alignDate( const QDateTime &dateTime, double stepSize, QwtDate::IntervalType intervalType, bool up ) const { // what about: (year == 1582 && month == 10 && day > 4 && day < 15) ?? QDateTime dt = dateTime; if ( dateTime.timeSpec() == Qt::OffsetFromUTC ) { dt.setUtcOffset( 0 ); } switch( intervalType ) { case QwtDate::Millisecond: { const int ms = qwtAlignValue( dt.time().msec(), stepSize, up ) ; dt = QwtDate::floor( dateTime, QwtDate::Second ); dt = dt.addMSecs( ms ); break; } case QwtDate::Second: { int second = dt.time().second(); if ( up ) { if ( dt.time().msec() > 0 ) second++; } const int s = qwtAlignValue( second, stepSize, up ); dt = QwtDate::floor( dt, QwtDate::Minute ); dt = dt.addSecs( s ); break; } case QwtDate::Minute: { int minute = dt.time().minute(); if ( up ) { if ( dt.time().msec() > 0 || dt.time().second() > 0 ) minute++; } const int m = qwtAlignValue( minute, stepSize, up ); dt = QwtDate::floor( dt, QwtDate::Hour ); dt = dt.addSecs( m * 60 ); break; } case QwtDate::Hour: { int hour = dt.time().hour(); if ( up ) { if ( dt.time().msec() > 0 || dt.time().second() > 0 || dt.time().minute() > 0 ) { hour++; } } const int h = qwtAlignValue( hour, stepSize, up ); dt = QwtDate::floor( dt, QwtDate::Day ); dt = dt.addSecs( h * 3600 ); break; } case QwtDate::Day: { // What date do we expect f.e. from an alignment of 5 days ?? // Aligning them to the beginning of the year avoids at least // jumping major ticks when panning int day = dt.date().dayOfYear(); if ( up ) { if ( dt.time() > QTime( 0, 0 ) ) day++; } const int d = qwtAlignValue( day, stepSize, up ); dt = QwtDate::floor( dt, QwtDate::Year ); dt = dt.addDays( d - 1 ); break; } case QwtDate::Week: { const QDate date = QwtDate::dateOfWeek0( dt.date().year(), d_data->week0Type ); int numWeeks = date.daysTo( dt.date() ) / 7; if ( up ) { if ( dt.time() > QTime( 0, 0 ) || date.daysTo( dt.date() ) % 7 ) { numWeeks++; } } const int d = qwtAlignValue( numWeeks, stepSize, up ) * 7; dt = QwtDate::floor( dt, QwtDate::Day ); dt.setDate( date ); dt = dt.addDays( d ); break; } case QwtDate::Month: { int month = dt.date().month(); if ( up ) { if ( dt.date().day() > 1 || dt.time() > QTime( 0, 0 ) ) { month++; } } const int m = qwtAlignValue( month - 1, stepSize, up ); dt = QwtDate::floor( dt, QwtDate::Year ); dt = dt.addMonths( m ); break; } case QwtDate::Year: { int year = dateTime.date().year(); if ( up ) { if ( dateTime.date().dayOfYear() > 1 || dt.time() > QTime( 0, 0 ) ) { year++; } } const int y = qwtAlignValue( year, stepSize, up ); dt = QwtDate::floor( dt, QwtDate::Day ); if ( y == 0 ) { // there is no year 0 in the Julian calendar dt.setDate( QDate( stepSize, 1, 1 ).addYears( -stepSize ) ); } else { dt.setDate( QDate( y, 1, 1 ) ); } break; } } if ( dateTime.timeSpec() == Qt::OffsetFromUTC ) { dt.setUtcOffset( dateTime.utcOffset() ); } return dt; } /*! Translate a double value into a QDateTime object. For QDateTime result is bounded by QwtDate::minDate() and QwtDate::maxDate() \return QDateTime object initialized with timeSpec() and utcOffset(). \sa timeSpec(), utcOffset(), QwtDate::toDateTime() */ QDateTime QwtDateScaleEngine::toDateTime( double value ) const { QDateTime dt = QwtDate::toDateTime( value, d_data->timeSpec ); if ( !dt.isValid() ) { const QDate date = ( value <= 0.0 ) ? QwtDate::minDate() : QwtDate::maxDate(); dt = QDateTime( date, QTime( 0, 0 ), d_data->timeSpec ); } if ( d_data->timeSpec == Qt::OffsetFromUTC ) { dt = dt.addSecs( d_data->utcOffset ); dt.setUtcOffset( d_data->utcOffset ); } return dt; } connectome-workbench-1.2.3+git41-gc4c6c90/src/Qwt/qwt_date_scale_engine.h000066400000000000000000000051611300200146000257770ustar00rootroot00000000000000/* -*- mode: C++ ; c-file-style: "stroustrup" -*- ***************************** * Qwt Widget Library * Copyright (C) 1997 Josef Wilgen * Copyright (C) 2002 Uwe Rathmann * * This library is free software; you can redistribute it and/or * modify it under the terms of the Qwt License, Version 1.0 *****************************************************************************/ #ifndef _QWT_DATE_SCALE_ENGINE_H_ #define _QWT_DATE_SCALE_ENGINE_H_ 1 #include "qwt_date.h" #include "qwt_scale_engine.h" /*! \brief A scale engine for date/time values QwtDateScaleEngine builds scales from a time intervals. Together with QwtDateScaleDraw it can be used for axes according to date/time values. Years, months, weeks, days, hours and minutes are organized in steps with non constant intervals. QwtDateScaleEngine classifies intervals and aligns the boundaries and tick positions according to this classification. QwtDateScaleEngine supports representations depending on Qt::TimeSpec specifications. The valid range for scales is limited by the range of QDateTime, that differs between Qt4 and Qt5. Datetime values are expected as the number of milliseconds since 1970-01-01T00:00:00 Universal Coordinated Time - also known as "The Epoch", that can be converted to QDateTime using QwtDate::toDateTime(). \sa QwtDate, QwtPlot::setAxisScaleEngine(), QwtAbstractScale::setScaleEngine() */ class QWT_EXPORT QwtDateScaleEngine: public QwtLinearScaleEngine { public: QwtDateScaleEngine( Qt::TimeSpec = Qt::LocalTime ); virtual ~QwtDateScaleEngine(); void setTimeSpec( Qt::TimeSpec ); Qt::TimeSpec timeSpec() const; void setUtcOffset( int seconds ); int utcOffset() const; void setWeek0Type( QwtDate::Week0Type ); QwtDate::Week0Type week0Type() const; void setMaxWeeks( int ); int maxWeeks() const; virtual void autoScale( int maxNumSteps, double &x1, double &x2, double &stepSize ) const; virtual QwtScaleDiv divideScale( double x1, double x2, int maxMajorSteps, int maxMinorSteps, double stepSize = 0.0 ) const; virtual QwtDate::IntervalType intervalType( const QDateTime &, const QDateTime &, int maxSteps ) const; QDateTime toDateTime( double ) const; protected: virtual QDateTime alignDate( const QDateTime &, double stepSize, QwtDate::IntervalType, bool up ) const; private: QwtScaleDiv buildScaleDiv( const QDateTime &, const QDateTime &, int maxMajorSteps, int maxMinorSteps, QwtDate::IntervalType ) const; private: class PrivateData; PrivateData *d_data; }; #endif connectome-workbench-1.2.3+git41-gc4c6c90/src/Qwt/qwt_dyngrid_layout.cpp000066400000000000000000000342151300200146000257600ustar00rootroot00000000000000/* -*- mode: C++ ; c-file-style: "stroustrup" -*- ***************************** * Qwt Widget Library * Copyright (C) 1997 Josef Wilgen * Copyright (C) 2002 Uwe Rathmann * * This library is free software; you can redistribute it and/or * modify it under the terms of the Qwt License, Version 1.0 *****************************************************************************/ #include "qwt_dyngrid_layout.h" #include "qwt_math.h" #include #include class QwtDynGridLayout::PrivateData { public: PrivateData(): isDirty( true ) { } void updateLayoutCache(); mutable QList itemList; uint maxColumns; uint numRows; uint numColumns; Qt::Orientations expanding; bool isDirty; QVector itemSizeHints; }; void QwtDynGridLayout::PrivateData::updateLayoutCache() { itemSizeHints.resize( itemList.count() ); int index = 0; for ( QList::iterator it = itemList.begin(); it != itemList.end(); ++it, index++ ) { itemSizeHints[ index ] = ( *it )->sizeHint(); } isDirty = false; } /*! \param parent Parent widget \param margin Margin \param spacing Spacing */ QwtDynGridLayout::QwtDynGridLayout( QWidget *parent, int margin, int spacing ): QLayout( parent ) { init(); setSpacing( spacing ); setMargin( margin ); } /*! \param spacing Spacing */ QwtDynGridLayout::QwtDynGridLayout( int spacing ) { init(); setSpacing( spacing ); } /*! Initialize the layout with default values. */ void QwtDynGridLayout::init() { d_data = new QwtDynGridLayout::PrivateData; d_data->maxColumns = d_data->numRows = d_data->numColumns = 0; d_data->expanding = 0; } //! Destructor QwtDynGridLayout::~QwtDynGridLayout() { for ( int i = 0; i < d_data->itemList.size(); i++ ) delete d_data->itemList[i]; delete d_data; } //! Invalidate all internal caches void QwtDynGridLayout::invalidate() { d_data->isDirty = true; QLayout::invalidate(); } /*! Limit the number of columns. \param maxColumns upper limit, 0 means unlimited \sa maxColumns() */ void QwtDynGridLayout::setMaxColumns( uint maxColumns ) { d_data->maxColumns = maxColumns; } /*! \brief Return the upper limit for the number of columns. 0 means unlimited, what is the default. \return Upper limit for the number of columns \sa setMaxColumns() */ uint QwtDynGridLayout::maxColumns() const { return d_data->maxColumns; } /*! \brief Add an item to the next free position. \param item Layout item */ void QwtDynGridLayout::addItem( QLayoutItem *item ) { d_data->itemList.append( item ); invalidate(); } /*! \return true if this layout is empty. */ bool QwtDynGridLayout::isEmpty() const { return d_data->itemList.isEmpty(); } /*! \return number of layout items */ uint QwtDynGridLayout::itemCount() const { return d_data->itemList.count(); } /*! Find the item at a specific index \param index Index \return Item at a specific index \sa takeAt() */ QLayoutItem *QwtDynGridLayout::itemAt( int index ) const { if ( index < 0 || index >= d_data->itemList.count() ) return NULL; return d_data->itemList.at( index ); } /*! Find the item at a specific index and remove it from the layout \param index Index \return Layout item, removed from the layout \sa itemAt() */ QLayoutItem *QwtDynGridLayout::takeAt( int index ) { if ( index < 0 || index >= d_data->itemList.count() ) return NULL; d_data->isDirty = true; return d_data->itemList.takeAt( index ); } //! \return Number of items in the layout int QwtDynGridLayout::count() const { return d_data->itemList.count(); } /*! Set whether this layout can make use of more space than sizeHint(). A value of Qt::Vertical or Qt::Horizontal means that it wants to grow in only one dimension, while Qt::Vertical | Qt::Horizontal means that it wants to grow in both dimensions. The default value is 0. \param expanding Or'd orientations \sa expandingDirections() */ void QwtDynGridLayout::setExpandingDirections( Qt::Orientations expanding ) { d_data->expanding = expanding; } /*! \brief Returns whether this layout can make use of more space than sizeHint(). A value of Qt::Vertical or Qt::Horizontal means that it wants to grow in only one dimension, while Qt::Vertical | Qt::Horizontal means that it wants to grow in both dimensions. \return Orientations, where the layout expands \sa setExpandingDirections() */ Qt::Orientations QwtDynGridLayout::expandingDirections() const { return d_data->expanding; } /*! Reorganizes columns and rows and resizes managed items within a rectangle. \param rect Layout geometry */ void QwtDynGridLayout::setGeometry( const QRect &rect ) { QLayout::setGeometry( rect ); if ( isEmpty() ) return; d_data->numColumns = columnsForWidth( rect.width() ); d_data->numRows = itemCount() / d_data->numColumns; if ( itemCount() % d_data->numColumns ) d_data->numRows++; QList itemGeometries = layoutItems( rect, d_data->numColumns ); int index = 0; for ( QList::iterator it = d_data->itemList.begin(); it != d_data->itemList.end(); ++it ) { ( *it )->setGeometry( itemGeometries[index] ); index++; } } /*! \brief Calculate the number of columns for a given width. The calculation tries to use as many columns as possible ( limited by maxColumns() ) \param width Available width for all columns \return Number of columns for a given width \sa maxColumns(), setMaxColumns() */ uint QwtDynGridLayout::columnsForWidth( int width ) const { if ( isEmpty() ) return 0; uint maxColumns = itemCount(); if ( d_data->maxColumns > 0 ) maxColumns = qMin( d_data->maxColumns, maxColumns ); if ( maxRowWidth( maxColumns ) <= width ) return maxColumns; for ( uint numColumns = 2; numColumns <= maxColumns; numColumns++ ) { const int rowWidth = maxRowWidth( numColumns ); if ( rowWidth > width ) return numColumns - 1; } return 1; // At least 1 column } /*! Calculate the width of a layout for a given number of columns. \param numColumns Given number of columns \param itemWidth Array of the width hints for all items */ int QwtDynGridLayout::maxRowWidth( int numColumns ) const { int col; QVector colWidth( numColumns ); for ( col = 0; col < numColumns; col++ ) colWidth[col] = 0; if ( d_data->isDirty ) d_data->updateLayoutCache(); for ( int index = 0; index < d_data->itemSizeHints.count(); index++ ) { col = index % numColumns; colWidth[col] = qMax( colWidth[col], d_data->itemSizeHints[int( index )].width() ); } int rowWidth = 2 * margin() + ( numColumns - 1 ) * spacing(); for ( col = 0; col < numColumns; col++ ) rowWidth += colWidth[col]; return rowWidth; } /*! \return the maximum width of all layout items */ int QwtDynGridLayout::maxItemWidth() const { if ( isEmpty() ) return 0; if ( d_data->isDirty ) d_data->updateLayoutCache(); int w = 0; for ( int i = 0; i < d_data->itemSizeHints.count(); i++ ) { const int itemW = d_data->itemSizeHints[i].width(); if ( itemW > w ) w = itemW; } return w; } /*! Calculate the geometries of the layout items for a layout with numColumns columns and a given rectangle. \param rect Rect where to place the items \param numColumns Number of columns \return item geometries */ QList QwtDynGridLayout::layoutItems( const QRect &rect, uint numColumns ) const { QList itemGeometries; if ( numColumns == 0 || isEmpty() ) return itemGeometries; uint numRows = itemCount() / numColumns; if ( numColumns % itemCount() ) numRows++; if ( numRows == 0 ) return itemGeometries; QVector rowHeight( numRows ); QVector colWidth( numColumns ); layoutGrid( numColumns, rowHeight, colWidth ); bool expandH, expandV; expandH = expandingDirections() & Qt::Horizontal; expandV = expandingDirections() & Qt::Vertical; if ( expandH || expandV ) stretchGrid( rect, numColumns, rowHeight, colWidth ); const int maxColumns = d_data->maxColumns; d_data->maxColumns = numColumns; const QRect alignedRect = alignmentRect( rect ); d_data->maxColumns = maxColumns; const int xOffset = expandH ? 0 : alignedRect.x(); const int yOffset = expandV ? 0 : alignedRect.y(); QVector colX( numColumns ); QVector rowY( numRows ); const int xySpace = spacing(); rowY[0] = yOffset + margin(); for ( uint r = 1; r < numRows; r++ ) rowY[r] = rowY[r-1] + rowHeight[r-1] + xySpace; colX[0] = xOffset + margin(); for ( uint c = 1; c < numColumns; c++ ) colX[c] = colX[c-1] + colWidth[c-1] + xySpace; const int itemCount = d_data->itemList.size(); for ( int i = 0; i < itemCount; i++ ) { const int row = i / numColumns; const int col = i % numColumns; QRect itemGeometry( colX[col], rowY[row], colWidth[col], rowHeight[row] ); itemGeometries.append( itemGeometry ); } return itemGeometries; } /*! Calculate the dimensions for the columns and rows for a grid of numColumns columns. \param numColumns Number of columns. \param rowHeight Array where to fill in the calculated row heights. \param colWidth Array where to fill in the calculated column widths. */ void QwtDynGridLayout::layoutGrid( uint numColumns, QVector& rowHeight, QVector& colWidth ) const { if ( numColumns <= 0 ) return; if ( d_data->isDirty ) d_data->updateLayoutCache(); for ( int index = 0; index < d_data->itemSizeHints.count(); index++ ) { const int row = index / numColumns; const int col = index % numColumns; const QSize &size = d_data->itemSizeHints[int( index )]; rowHeight[row] = ( col == 0 ) ? size.height() : qMax( rowHeight[row], size.height() ); colWidth[col] = ( row == 0 ) ? size.width() : qMax( colWidth[col], size.width() ); } } /*! \return true: QwtDynGridLayout implements heightForWidth(). \sa heightForWidth() */ bool QwtDynGridLayout::hasHeightForWidth() const { return true; } /*! \return The preferred height for this layout, given a width. \sa hasHeightForWidth() */ int QwtDynGridLayout::heightForWidth( int width ) const { if ( isEmpty() ) return 0; const uint numColumns = columnsForWidth( width ); uint numRows = itemCount() / numColumns; if ( itemCount() % numColumns ) numRows++; QVector rowHeight( numRows ); QVector colWidth( numColumns ); layoutGrid( numColumns, rowHeight, colWidth ); int h = 2 * margin() + ( numRows - 1 ) * spacing(); for ( uint row = 0; row < numRows; row++ ) h += rowHeight[row]; return h; } /*! Stretch columns in case of expanding() & QSizePolicy::Horizontal and rows in case of expanding() & QSizePolicy::Vertical to fill the entire rect. Rows and columns are stretched with the same factor. \param rect Bounding rectangle \param numColumns Number of columns \param rowHeight Array to be filled with the calculated row heights \param colWidth Array to be filled with the calculated column widths \sa setExpanding(), expanding() */ void QwtDynGridLayout::stretchGrid( const QRect &rect, uint numColumns, QVector& rowHeight, QVector& colWidth ) const { if ( numColumns == 0 || isEmpty() ) return; bool expandH, expandV; expandH = expandingDirections() & Qt::Horizontal; expandV = expandingDirections() & Qt::Vertical; if ( expandH ) { int xDelta = rect.width() - 2 * margin() - ( numColumns - 1 ) * spacing(); for ( uint col = 0; col < numColumns; col++ ) xDelta -= colWidth[col]; if ( xDelta > 0 ) { for ( uint col = 0; col < numColumns; col++ ) { const int space = xDelta / ( numColumns - col ); colWidth[col] += space; xDelta -= space; } } } if ( expandV ) { uint numRows = itemCount() / numColumns; if ( itemCount() % numColumns ) numRows++; int yDelta = rect.height() - 2 * margin() - ( numRows - 1 ) * spacing(); for ( uint row = 0; row < numRows; row++ ) yDelta -= rowHeight[row]; if ( yDelta > 0 ) { for ( uint row = 0; row < numRows; row++ ) { const int space = yDelta / ( numRows - row ); rowHeight[row] += space; yDelta -= space; } } } } /*! Return the size hint. If maxColumns() > 0 it is the size for a grid with maxColumns() columns, otherwise it is the size for a grid with only one row. \return Size hint \sa maxColumns(), setMaxColumns() */ QSize QwtDynGridLayout::sizeHint() const { if ( isEmpty() ) return QSize(); uint numColumns = itemCount(); if ( d_data->maxColumns > 0 ) numColumns = qMin( d_data->maxColumns, numColumns ); uint numRows = itemCount() / numColumns; if ( itemCount() % numColumns ) numRows++; QVector rowHeight( numRows ); QVector colWidth( numColumns ); layoutGrid( numColumns, rowHeight, colWidth ); int h = 2 * margin() + ( numRows - 1 ) * spacing(); for ( uint row = 0; row < numRows; row++ ) h += rowHeight[row]; int w = 2 * margin() + ( numColumns - 1 ) * spacing(); for ( uint col = 0; col < numColumns; col++ ) w += colWidth[col]; return QSize( w, h ); } /*! \return Number of rows of the current layout. \sa numColumns() \warning The number of rows might change whenever the geometry changes */ uint QwtDynGridLayout::numRows() const { return d_data->numRows; } /*! \return Number of columns of the current layout. \sa numRows() \warning The number of columns might change whenever the geometry changes */ uint QwtDynGridLayout::numColumns() const { return d_data->numColumns; } connectome-workbench-1.2.3+git41-gc4c6c90/src/Qwt/qwt_dyngrid_layout.h000066400000000000000000000044501300200146000254230ustar00rootroot00000000000000/* -*- mode: C++ ; c-file-style: "stroustrup" -*- ***************************** * Qwt Widget Library * Copyright (C) 1997 Josef Wilgen * Copyright (C) 2002 Uwe Rathmann * * This library is free software; you can redistribute it and/or * modify it under the terms of the Qwt License, Version 1.0 *****************************************************************************/ #ifndef QWT_DYNGRID_LAYOUT_H #define QWT_DYNGRID_LAYOUT_H #include "qwt_global.h" #include #include #include /*! \brief The QwtDynGridLayout class lays out widgets in a grid, adjusting the number of columns and rows to the current size. QwtDynGridLayout takes the space it gets, divides it up into rows and columns, and puts each of the widgets it manages into the correct cell(s). It lays out as many number of columns as possible (limited by maxColumns()). */ class QWT_EXPORT QwtDynGridLayout : public QLayout { Q_OBJECT public: explicit QwtDynGridLayout( QWidget *, int margin = 0, int space = -1 ); explicit QwtDynGridLayout( int space = -1 ); virtual ~QwtDynGridLayout(); virtual void invalidate(); void setMaxColumns( uint maxCols ); uint maxColumns() const; uint numRows () const; uint numColumns () const; virtual void addItem( QLayoutItem * ); virtual QLayoutItem *itemAt( int index ) const; virtual QLayoutItem *takeAt( int index ); virtual int count() const; void setExpandingDirections( Qt::Orientations ); virtual Qt::Orientations expandingDirections() const; QList layoutItems( const QRect &, uint numCols ) const; virtual int maxItemWidth() const; virtual void setGeometry( const QRect &rect ); virtual bool hasHeightForWidth() const; virtual int heightForWidth( int ) const; virtual QSize sizeHint() const; virtual bool isEmpty() const; uint itemCount() const; virtual uint columnsForWidth( int width ) const; protected: void layoutGrid( uint numCols, QVector& rowHeight, QVector& colWidth ) const; void stretchGrid( const QRect &rect, uint numCols, QVector& rowHeight, QVector& colWidth ) const; private: void init(); int maxRowWidth( int numCols ) const; class PrivateData; PrivateData *d_data; }; #endif connectome-workbench-1.2.3+git41-gc4c6c90/src/Qwt/qwt_event_pattern.cpp000066400000000000000000000151221300200146000255750ustar00rootroot00000000000000/* -*- mode: C++ ; c-file-style: "stroustrup" -*- ***************************** * Qwt Widget Library * Copyright (C) 1997 Josef Wilgen * Copyright (C) 2002 Uwe Rathmann * * This library is free software; you can redistribute it and/or * modify it under the terms of the Qwt License, Version 1.0 *****************************************************************************/ #include "qwt_event_pattern.h" #include /*! Constructor \sa MousePatternCode, KeyPatternCode */ QwtEventPattern::QwtEventPattern(): d_mousePattern( MousePatternCount ), d_keyPattern( KeyPatternCount ) { initKeyPattern(); initMousePattern( 3 ); } //! Destructor QwtEventPattern::~QwtEventPattern() { } /*! Set default mouse patterns, depending on the number of mouse buttons \param numButtons Number of mouse buttons ( <= 3 ) \sa MousePatternCode */ void QwtEventPattern::initMousePattern( int numButtons ) { d_mousePattern.resize( MousePatternCount ); switch ( numButtons ) { case 1: { setMousePattern( MouseSelect1, Qt::LeftButton ); setMousePattern( MouseSelect2, Qt::LeftButton, Qt::ControlModifier ); setMousePattern( MouseSelect3, Qt::LeftButton, Qt::AltModifier ); break; } case 2: { setMousePattern( MouseSelect1, Qt::LeftButton ); setMousePattern( MouseSelect2, Qt::RightButton ); setMousePattern( MouseSelect3, Qt::LeftButton, Qt::AltModifier ); break; } default: { setMousePattern( MouseSelect1, Qt::LeftButton ); setMousePattern( MouseSelect2, Qt::RightButton ); setMousePattern( MouseSelect3, Qt::MidButton ); } } setMousePattern( MouseSelect4, d_mousePattern[MouseSelect1].button, d_mousePattern[MouseSelect1].modifiers | Qt::ShiftModifier ); setMousePattern( MouseSelect5, d_mousePattern[MouseSelect2].button, d_mousePattern[MouseSelect2].modifiers | Qt::ShiftModifier ); setMousePattern( MouseSelect6, d_mousePattern[MouseSelect3].button, d_mousePattern[MouseSelect3].modifiers | Qt::ShiftModifier ); } /*! Set default mouse patterns. \sa KeyPatternCode */ void QwtEventPattern::initKeyPattern() { d_keyPattern.resize( KeyPatternCount ); setKeyPattern( KeySelect1, Qt::Key_Return ); setKeyPattern( KeySelect2, Qt::Key_Space ); setKeyPattern( KeyAbort, Qt::Key_Escape ); setKeyPattern( KeyLeft, Qt::Key_Left ); setKeyPattern( KeyRight, Qt::Key_Right ); setKeyPattern( KeyUp, Qt::Key_Up ); setKeyPattern( KeyDown, Qt::Key_Down ); setKeyPattern( KeyRedo, Qt::Key_Plus ); setKeyPattern( KeyUndo, Qt::Key_Minus ); setKeyPattern( KeyHome, Qt::Key_Escape ); } /*! Change one mouse pattern \param pattern Index of the pattern \param button Button \param modifiers Keyboard modifiers \sa QMouseEvent */ void QwtEventPattern::setMousePattern( MousePatternCode pattern, Qt::MouseButton button, Qt::KeyboardModifiers modifiers ) { if ( pattern >= 0 && pattern < MousePatternCount ) { d_mousePattern[ pattern ].button = button; d_mousePattern[ pattern ].modifiers = modifiers; } } /*! Change one key pattern \param pattern Index of the pattern \param key Key \param modifiers Keyboard modifiers \sa QKeyEvent */ void QwtEventPattern::setKeyPattern( KeyPatternCode pattern, int key, Qt::KeyboardModifiers modifiers ) { if ( pattern >= 0 && pattern < KeyPatternCount ) { d_keyPattern[ pattern ].key = key; d_keyPattern[ pattern ].modifiers = modifiers; } } //! Change the mouse event patterns void QwtEventPattern::setMousePattern( const QVector &pattern ) { d_mousePattern = pattern; } //! Change the key event patterns void QwtEventPattern::setKeyPattern( const QVector &pattern ) { d_keyPattern = pattern; } //! \return Mouse pattern const QVector & QwtEventPattern::mousePattern() const { return d_mousePattern; } //! \return Key pattern const QVector & QwtEventPattern::keyPattern() const { return d_keyPattern; } //! \return Mouse pattern QVector &QwtEventPattern::mousePattern() { return d_mousePattern; } //! \return Key pattern QVector &QwtEventPattern::keyPattern() { return d_keyPattern; } /*! \brief Compare a mouse event with an event pattern. A mouse event matches the pattern when both have the same button value and in the state value the same key flags(Qt::KeyButtonMask) are set. \param code Index of the event pattern \param event Mouse event \return true if matches \sa keyMatch() */ bool QwtEventPattern::mouseMatch( MousePatternCode code, const QMouseEvent *event ) const { if ( code >= 0 && code < MousePatternCount ) return mouseMatch( d_mousePattern[ code ], event ); return false; } /*! \brief Compare a mouse event with an event pattern. A mouse event matches the pattern when both have the same button value and in the state value the same key flags(Qt::KeyButtonMask) are set. \param pattern Mouse event pattern \param event Mouse event \return true if matches \sa keyMatch() */ bool QwtEventPattern::mouseMatch( const MousePattern &pattern, const QMouseEvent *event ) const { if ( event == NULL ) return false; const MousePattern mousePattern( event->button(), event->modifiers() ); return mousePattern == pattern; } /*! \brief Compare a key event with an event pattern. A key event matches the pattern when both have the same key value and in the state value the same key flags (Qt::KeyButtonMask) are set. \param code Index of the event pattern \param event Key event \return true if matches \sa mouseMatch() */ bool QwtEventPattern::keyMatch( KeyPatternCode code, const QKeyEvent *event ) const { if ( code >= 0 && code < KeyPatternCount ) return keyMatch( d_keyPattern[ code ], event ); return false; } /*! \brief Compare a key event with an event pattern. A key event matches the pattern when both have the same key value and in the state value the same key flags (Qt::KeyButtonMask) are set. \param pattern Key event pattern \param event Key event \return true if matches \sa mouseMatch() */ bool QwtEventPattern::keyMatch( const KeyPattern &pattern, const QKeyEvent *event ) const { if ( event == NULL ) return false; const KeyPattern keyPattern( event->key(), event->modifiers() ); return keyPattern == pattern; } connectome-workbench-1.2.3+git41-gc4c6c90/src/Qwt/qwt_event_pattern.h000066400000000000000000000135171300200146000252500ustar00rootroot00000000000000/* -*- mode: C++ ; c-file-style: "stroustrup" -*- ***************************** * Qwt Widget Library * Copyright (C) 1997 Josef Wilgen * Copyright (C) 2002 Uwe Rathmann * * This library is free software; you can redistribute it and/or * modify it under the terms of the Qwt License, Version 1.0 *****************************************************************************/ #ifndef QWT_EVENT_PATTERN #define QWT_EVENT_PATTERN 1 #include "qwt_global.h" #include #include class QMouseEvent; class QKeyEvent; /*! \brief A collection of event patterns QwtEventPattern introduces an level of indirection for mouse and keyboard inputs. Those are represented by symbolic names, so the application code can be configured by individual mappings. \sa QwtPicker, QwtPickerMachine, QwtPlotZoomer */ class QWT_EXPORT QwtEventPattern { public: /*! \brief Symbolic mouse input codes QwtEventPattern implements 3 different settings for mice with 1, 2, or 3 buttons that can be activated using initMousePattern(). The default setting is for 3 button mice. Individual settings can be configured using setMousePattern(). \sa initMousePattern(), setMousePattern(), setKeyPattern() */ enum MousePatternCode { /*! The default setting for 1, 2 and 3 button mice is: - Qt::LeftButton - Qt::LeftButton - Qt::LeftButton */ MouseSelect1, /*! The default setting for 1, 2 and 3 button mice is: - Qt::LeftButton + Qt::ControlModifier - Qt::RightButton - Qt::RightButton */ MouseSelect2, /*! The default setting for 1, 2 and 3 button mice is: - Qt::LeftButton + Qt::AltModifier - Qt::LeftButton + Qt::AltModifier - Qt::MidButton */ MouseSelect3, /*! The default setting for 1, 2 and 3 button mice is: - Qt::LeftButton + Qt::ShiftModifier - Qt::LeftButton + Qt::ShiftModifier - Qt::LeftButton + Qt::ShiftModifier */ MouseSelect4, /*! The default setting for 1, 2 and 3 button mice is: - Qt::LeftButton + Qt::ControlButton | Qt::ShiftModifier - Qt::RightButton + Qt::ShiftModifier - Qt::RightButton + Qt::ShiftModifier */ MouseSelect5, /*! The default setting for 1, 2 and 3 button mice is: - Qt::LeftButton + Qt::AltModifier + Qt::ShiftModifier - Qt::LeftButton + Qt::AltModifier | Qt::ShiftModifier - Qt::MidButton + Qt::ShiftModifier */ MouseSelect6, //! Number of mouse patterns MousePatternCount }; /*! \brief Symbolic keyboard input codes Individual settings can be configured using setKeyPattern() \sa setKeyPattern(), setMousePattern() */ enum KeyPatternCode { //! Qt::Key_Return KeySelect1, //! Qt::Key_Space KeySelect2, //! Qt::Key_Escape KeyAbort, //! Qt::Key_Left KeyLeft, //! Qt::Key_Right KeyRight, //! Qt::Key_Up KeyUp, //! Qt::Key_Down KeyDown, //! Qt::Key_Plus KeyRedo, //! Qt::Key_Minus KeyUndo, //! Qt::Key_Escape KeyHome, //! Number of key patterns KeyPatternCount }; //! A pattern for mouse events class MousePattern { public: //! Constructor MousePattern( Qt::MouseButton btn = Qt::NoButton, Qt::KeyboardModifiers modifierCodes = Qt::NoModifier ): button( btn ), modifiers( modifierCodes ) { } //! Button Qt::MouseButton button; //! Keyboard modifier Qt::KeyboardModifiers modifiers; }; //! A pattern for key events class KeyPattern { public: //! Constructor KeyPattern( int keyCode = Qt::Key_unknown, Qt::KeyboardModifiers modifierCodes = Qt::NoModifier ): key( keyCode ), modifiers( modifierCodes ) { } //! Key code int key; //! Modifiers Qt::KeyboardModifiers modifiers; }; QwtEventPattern(); virtual ~QwtEventPattern(); void initMousePattern( int numButtons ); void initKeyPattern(); void setMousePattern( MousePatternCode, Qt::MouseButton button, Qt::KeyboardModifiers = Qt::NoModifier ); void setKeyPattern( KeyPatternCode, int keyCode, Qt::KeyboardModifiers modifierCodes = Qt::NoModifier ); void setMousePattern( const QVector & ); void setKeyPattern( const QVector & ); const QVector &mousePattern() const; const QVector &keyPattern() const; QVector &mousePattern(); QVector &keyPattern(); bool mouseMatch( MousePatternCode, const QMouseEvent * ) const; bool keyMatch( KeyPatternCode, const QKeyEvent * ) const; protected: virtual bool mouseMatch( const MousePattern &, const QMouseEvent * ) const; virtual bool keyMatch( const KeyPattern &, const QKeyEvent * ) const; private: #if defined(_MSC_VER) #pragma warning(push) #pragma warning(disable: 4251) #endif QVector d_mousePattern; QVector d_keyPattern; #if defined(_MSC_VER) #pragma warning(pop) #endif }; //! Compare operator inline bool operator==( QwtEventPattern::MousePattern b1, QwtEventPattern::MousePattern b2 ) { return b1.button == b2.button && b1.modifiers == b2.modifiers; } //! Compare operator inline bool operator==( QwtEventPattern::KeyPattern b1, QwtEventPattern::KeyPattern b2 ) { return b1.key == b2.key && b1.modifiers == b2.modifiers; } #endif connectome-workbench-1.2.3+git41-gc4c6c90/src/Qwt/qwt_global.h000066400000000000000000000020701300200146000236220ustar00rootroot00000000000000/* -*- mode: C++ ; c-file-style: "stroustrup" -*- ***************************** * Qwt Widget Library * Copyright (C) 1997 Josef Wilgen * Copyright (C) 2002 Uwe Rathmann * * This library is free software; you can redistribute it and/or * modify it under the terms of the Qwt License, Version 1.0 *****************************************************************************/ #ifndef QWT_GLOBAL_H #define QWT_GLOBAL_H #include // QWT_VERSION is (major << 16) + (minor << 8) + patch. #define QWT_VERSION 0x060102 #define QWT_VERSION_STR "6.1.2" #if defined(_MSC_VER) /* MSVC Compiler */ /* template-class specialization 'identifier' is already instantiated */ #pragma warning(disable: 4660) /* inherits via dominance */ #pragma warning(disable: 4250) #endif // _MSC_VER #ifdef QWT_DLL #if defined(QWT_MAKEDLL) // create a Qwt DLL library #define QWT_EXPORT Q_DECL_EXPORT #else // use a Qwt DLL library #define QWT_EXPORT Q_DECL_IMPORT #endif #endif // QWT_DLL #ifndef QWT_EXPORT #define QWT_EXPORT #endif #endif connectome-workbench-1.2.3+git41-gc4c6c90/src/Qwt/qwt_graphic.cpp000066400000000000000000000631231300200146000243400ustar00rootroot00000000000000/* -*- mode: C++ ; c-file-style: "stroustrup" -*- ***************************** * Qwt Widget Library * Copyright (C) 1997 Josef Wilgen * Copyright (C) 2002 Uwe Rathmann * * This library is free software; you can redistribute it and/or * modify it under the terms of the Qwt License, Version 1.0 *****************************************************************************/ #include "qwt_graphic.h" #include "qwt_painter_command.h" #include #include #include #include #include #include #include static bool qwtHasScalablePen( const QPainter *painter ) { const QPen pen = painter->pen(); bool scalablePen = false; if ( pen.style() != Qt::NoPen && pen.brush().style() != Qt::NoBrush ) { scalablePen = !pen.isCosmetic(); if ( !scalablePen && pen.widthF() == 0.0 ) { const QPainter::RenderHints hints = painter->renderHints(); if ( hints.testFlag( QPainter::NonCosmeticDefaultPen ) ) scalablePen = true; } } return scalablePen; } static QRectF qwtStrokedPathRect( const QPainter *painter, const QPainterPath &path ) { QPainterPathStroker stroker; stroker.setWidth( painter->pen().widthF() ); stroker.setCapStyle( painter->pen().capStyle() ); stroker.setJoinStyle( painter->pen().joinStyle() ); stroker.setMiterLimit( painter->pen().miterLimit() ); QRectF rect; if ( qwtHasScalablePen( painter ) ) { QPainterPath stroke = stroker.createStroke(path); rect = painter->transform().map(stroke).boundingRect(); } else { QPainterPath mappedPath = painter->transform().map(path); mappedPath = stroker.createStroke( mappedPath ); rect = mappedPath.boundingRect(); } return rect; } static inline void qwtExecCommand( QPainter *painter, const QwtPainterCommand &cmd, QwtGraphic::RenderHints renderHints, const QTransform &transform, const QTransform *initialTransform ) { switch( cmd.type() ) { case QwtPainterCommand::Path: { bool doMap = false; if ( renderHints.testFlag( QwtGraphic::RenderPensUnscaled ) && painter->transform().isScaling() ) { bool isCosmetic = painter->pen().isCosmetic(); if ( isCosmetic && painter->pen().widthF() == 0.0 ) { QPainter::RenderHints hints = painter->renderHints(); if ( hints.testFlag( QPainter::NonCosmeticDefaultPen ) ) isCosmetic = false; } doMap = !isCosmetic; } if ( doMap ) { const QTransform tr = painter->transform(); painter->resetTransform(); QPainterPath path = tr.map( *cmd.path() ); if ( initialTransform ) { painter->setTransform( *initialTransform ); path = initialTransform->inverted().map( path ); } painter->drawPath( path ); painter->setTransform( tr ); } else { painter->drawPath( *cmd.path() ); } break; } case QwtPainterCommand::Pixmap: { const QwtPainterCommand::PixmapData *data = cmd.pixmapData(); painter->drawPixmap( data->rect, data->pixmap, data->subRect ); break; } case QwtPainterCommand::Image: { const QwtPainterCommand::ImageData *data = cmd.imageData(); painter->drawImage( data->rect, data->image, data->subRect, data->flags ); break; } case QwtPainterCommand::State: { const QwtPainterCommand::StateData *data = cmd.stateData(); if ( data->flags & QPaintEngine::DirtyPen ) painter->setPen( data->pen ); if ( data->flags & QPaintEngine::DirtyBrush ) painter->setBrush( data->brush ); if ( data->flags & QPaintEngine::DirtyBrushOrigin ) painter->setBrushOrigin( data->brushOrigin ); if ( data->flags & QPaintEngine::DirtyFont ) painter->setFont( data->font ); if ( data->flags & QPaintEngine::DirtyBackground ) { painter->setBackgroundMode( data->backgroundMode ); painter->setBackground( data->backgroundBrush ); } if ( data->flags & QPaintEngine::DirtyTransform ) { painter->setTransform( data->transform * transform ); } if ( data->flags & QPaintEngine::DirtyClipEnabled ) painter->setClipping( data->isClipEnabled ); if ( data->flags & QPaintEngine::DirtyClipRegion) { painter->setClipRegion( data->clipRegion, data->clipOperation ); } if ( data->flags & QPaintEngine::DirtyClipPath ) { painter->setClipPath( data->clipPath, data->clipOperation ); } if ( data->flags & QPaintEngine::DirtyHints) { const QPainter::RenderHints hints = data->renderHints; painter->setRenderHint( QPainter::Antialiasing, hints.testFlag( QPainter::Antialiasing ) ); painter->setRenderHint( QPainter::TextAntialiasing, hints.testFlag( QPainter::TextAntialiasing ) ); painter->setRenderHint( QPainter::SmoothPixmapTransform, hints.testFlag( QPainter::SmoothPixmapTransform ) ); painter->setRenderHint( QPainter::HighQualityAntialiasing, hints.testFlag( QPainter::HighQualityAntialiasing ) ); painter->setRenderHint( QPainter::NonCosmeticDefaultPen, hints.testFlag( QPainter::NonCosmeticDefaultPen ) ); } if ( data->flags & QPaintEngine::DirtyCompositionMode) painter->setCompositionMode( data->compositionMode ); if ( data->flags & QPaintEngine::DirtyOpacity) painter->setOpacity( data->opacity ); break; } default: break; } } class QwtGraphic::PathInfo { public: PathInfo(): d_scalablePen( false ) { // QVector needs a default constructor } PathInfo( const QRectF &pointRect, const QRectF &boundingRect, bool scalablePen ): d_pointRect( pointRect ), d_boundingRect( boundingRect ), d_scalablePen( scalablePen ) { } inline QRectF scaledBoundingRect( double sx, double sy, bool scalePens ) const { if ( sx == 1.0 && sy == 1.0 ) return d_boundingRect; QTransform transform; transform.scale( sx, sy ); QRectF rect; if ( scalePens && d_scalablePen ) { rect = transform.mapRect( d_boundingRect ); } else { rect = transform.mapRect( d_pointRect ); const double l = qAbs( d_pointRect.left() - d_boundingRect.left() ); const double r = qAbs( d_pointRect.right() - d_boundingRect.right() ); const double t = qAbs( d_pointRect.top() - d_boundingRect.top() ); const double b = qAbs( d_pointRect.bottom() - d_boundingRect.bottom() ); rect.adjust( -l, -t, r, b ); } return rect; } inline double scaleFactorX( const QRectF& pathRect, const QRectF &targetRect, bool scalePens ) const { if ( pathRect.width() <= 0.0 ) return 0.0; const QPointF p0 = d_pointRect.center(); const double l = qAbs( pathRect.left() - p0.x() ); const double r = qAbs( pathRect.right() - p0.x() ); const double w = 2.0 * qMin( l, r ) * targetRect.width() / pathRect.width(); double sx; if ( scalePens && d_scalablePen ) { sx = w / d_boundingRect.width(); } else { const double pw = qMax( qAbs( d_boundingRect.left() - d_pointRect.left() ), qAbs( d_boundingRect.right() - d_pointRect.right() ) ); sx = ( w - 2 * pw ) / d_pointRect.width(); } return sx; } inline double scaleFactorY( const QRectF& pathRect, const QRectF &targetRect, bool scalePens ) const { if ( pathRect.height() <= 0.0 ) return 0.0; const QPointF p0 = d_pointRect.center(); const double t = qAbs( pathRect.top() - p0.y() ); const double b = qAbs( pathRect.bottom() - p0.y() ); const double h = 2.0 * qMin( t, b ) * targetRect.height() / pathRect.height(); double sy; if ( scalePens && d_scalablePen ) { sy = h / d_boundingRect.height(); } else { const double pw = qMax( qAbs( d_boundingRect.top() - d_pointRect.top() ), qAbs( d_boundingRect.bottom() - d_pointRect.bottom() ) ); sy = ( h - 2 * pw ) / d_pointRect.height(); } return sy; } private: QRectF d_pointRect; QRectF d_boundingRect; bool d_scalablePen; }; class QwtGraphic::PrivateData { public: PrivateData(): boundingRect( 0.0, 0.0, -1.0, -1.0 ), pointRect( 0.0, 0.0, -1.0, -1.0 ), initialTransform( NULL ) { } QSizeF defaultSize; QVector commands; QVector pathInfos; QRectF boundingRect; QRectF pointRect; QwtGraphic::RenderHints renderHints; QTransform *initialTransform; }; /*! \brief Constructor Initializes a null graphic \sa isNull() */ QwtGraphic::QwtGraphic(): QwtNullPaintDevice() { setMode( QwtNullPaintDevice::PathMode ); d_data = new PrivateData; } /*! \brief Copy constructor \param other Source \sa operator=() */ QwtGraphic::QwtGraphic( const QwtGraphic &other ): QwtNullPaintDevice() { setMode( other.mode() ); d_data = new PrivateData( *other.d_data ); } //! Destructor QwtGraphic::~QwtGraphic() { delete d_data; } /*! \brief Assignment operator \param other Source \return A reference of this object */ QwtGraphic& QwtGraphic::operator=(const QwtGraphic &other) { setMode( other.mode() ); *d_data = *other.d_data; return *this; } /*! \brief Clear all stored commands \sa isNull() */ void QwtGraphic::reset() { d_data->commands.clear(); d_data->pathInfos.clear(); d_data->boundingRect = QRectF( 0.0, 0.0, -1.0, -1.0 ); d_data->pointRect = QRectF( 0.0, 0.0, -1.0, -1.0 ); d_data->defaultSize = QSizeF(); } /*! \return True, when no painter commands have been stored \sa isEmpty(), commands() */ bool QwtGraphic::isNull() const { return d_data->commands.isEmpty(); } /*! \return True, when the bounding rectangle is empty \sa boundingRect(), isNull() */ bool QwtGraphic::isEmpty() const { return d_data->boundingRect.isEmpty(); } /*! Toggle an render hint \param hint Render hint \param on true/false \sa testRenderHint(), RenderHint */ void QwtGraphic::setRenderHint( RenderHint hint, bool on ) { if ( on ) d_data->renderHints |= hint; else d_data->renderHints &= ~hint; } /*! Test a render hint \param hint Render hint \return true/false \sa setRenderHint(), RenderHint */ bool QwtGraphic::testRenderHint( RenderHint hint ) const { return d_data->renderHints.testFlag( hint ); } /*! The bounding rectangle is the controlPointRect() extended by the areas needed for rendering the outlines with unscaled pens. \return Bounding rectangle of the graphic \sa controlPointRect(), scaledBoundingRect() */ QRectF QwtGraphic::boundingRect() const { if ( d_data->boundingRect.width() < 0 ) return QRectF(); return d_data->boundingRect; } /*! The control point rectangle is the bounding rectangle of all control points of the paths and the target rectangles of the images/pixmaps. \return Control point rectangle \sa boundingRect(), scaledBoundingRect() */ QRectF QwtGraphic::controlPointRect() const { if ( d_data->pointRect.width() < 0 ) return QRectF(); return d_data->pointRect; } /*! \brief Calculate the target rectangle for scaling the graphic \param sx Horizontal scaling factor \param sy Vertical scaling factor \note In case of paths that are painted with a cosmetic pen ( see QPen::isCosmetic() ) the target rectangle is different to multiplying the bounding rectangle. \return Scaled bounding rectangle \sa boundingRect(), controlPointRect() */ QRectF QwtGraphic::scaledBoundingRect( double sx, double sy ) const { if ( sx == 1.0 && sy == 1.0 ) return d_data->boundingRect; QTransform transform; transform.scale( sx, sy ); QRectF rect = transform.mapRect( d_data->pointRect ); for ( int i = 0; i < d_data->pathInfos.size(); i++ ) { rect |= d_data->pathInfos[i].scaledBoundingRect( sx, sy, !d_data->renderHints.testFlag( RenderPensUnscaled ) ); } return rect; } //! \return Ceiled defaultSize() QSize QwtGraphic::sizeMetrics() const { const QSizeF sz = defaultSize(); return QSize( qCeil( sz.width() ), qCeil( sz.height() ) ); } /*! \brief Set a default size The default size is used in all methods rendering the graphic, where no size is explicitly specified. Assigning an empty size means, that the default size will be calculated from the bounding rectangle. The default setting is an empty size. \param size Default size \sa defaultSize(), boundingRect() */ void QwtGraphic::setDefaultSize( const QSizeF &size ) { const double w = qMax( qreal( 0.0 ), size.width() ); const double h = qMax( qreal( 0.0 ), size.height() ); d_data->defaultSize = QSizeF( w, h ); } /*! \brief Default size When a non empty size has been assigned by setDefaultSize() this size will be returned. Otherwise the default size is the size of the bounding rectangle. The default size is used in all methods rendering the graphic, where no size is explicitly specified. \return Default size \sa setDefaultSize(), boundingRect() */ QSizeF QwtGraphic::defaultSize() const { if ( !d_data->defaultSize.isEmpty() ) return d_data->defaultSize; return boundingRect().size(); } /*! \brief Replay all recorded painter commands \param painter Qt painter */ void QwtGraphic::render( QPainter *painter ) const { if ( isNull() ) return; const int numCommands = d_data->commands.size(); const QwtPainterCommand *commands = d_data->commands.constData(); const QTransform transform = painter->transform(); painter->save(); for ( int i = 0; i < numCommands; i++ ) { qwtExecCommand( painter, commands[i], d_data->renderHints, transform, d_data->initialTransform ); } painter->restore(); } /*! \brief Replay all recorded painter commands The graphic is scaled to fit into the rectangle of the given size starting at ( 0, 0 ). \param painter Qt painter \param size Size for the scaled graphic \param aspectRatioMode Mode how to scale - See Qt::AspectRatioMode */ void QwtGraphic::render( QPainter *painter, const QSizeF &size, Qt::AspectRatioMode aspectRatioMode ) const { const QRectF r( 0.0, 0.0, size.width(), size.height() ); render( painter, r, aspectRatioMode ); } /*! \brief Replay all recorded painter commands The graphic is scaled to fit into the given rectangle \param painter Qt painter \param rect Rectangle for the scaled graphic \param aspectRatioMode Mode how to scale - See Qt::AspectRatioMode */ void QwtGraphic::render( QPainter *painter, const QRectF &rect, Qt::AspectRatioMode aspectRatioMode ) const { if ( isEmpty() || rect.isEmpty() ) return; double sx = 1.0; double sy = 1.0; if ( d_data->pointRect.width() > 0.0 ) sx = rect.width() / d_data->pointRect.width(); if ( d_data->pointRect.height() > 0.0 ) sy = rect.height() / d_data->pointRect.height(); const bool scalePens = !d_data->renderHints.testFlag( RenderPensUnscaled ); for ( int i = 0; i < d_data->pathInfos.size(); i++ ) { const PathInfo info = d_data->pathInfos[i]; const double ssx = info.scaleFactorX( d_data->pointRect, rect, scalePens ); if ( ssx > 0.0 ) sx = qMin( sx, ssx ); const double ssy = info.scaleFactorY( d_data->pointRect, rect, scalePens ); if ( ssy > 0.0 ) sy = qMin( sy, ssy ); } if ( aspectRatioMode == Qt::KeepAspectRatio ) { const double s = qMin( sx, sy ); sx = s; sy = s; } else if ( aspectRatioMode == Qt::KeepAspectRatioByExpanding ) { const double s = qMax( sx, sy ); sx = s; sy = s; } QTransform tr; tr.translate( rect.center().x() - 0.5 * sx * d_data->pointRect.width(), rect.center().y() - 0.5 * sy * d_data->pointRect.height() ); tr.scale( sx, sy ); tr.translate( -d_data->pointRect.x(), -d_data->pointRect.y() ); const QTransform transform = painter->transform(); if ( !scalePens && transform.isScaling() ) { // we don't want to scale pens according to sx/sy, // but we want to apply the scaling from the // painter transformation later d_data->initialTransform = new QTransform(); d_data->initialTransform->scale( transform.m11(), transform.m22() ); } painter->setTransform( tr, true ); render( painter ); painter->setTransform( transform ); delete d_data->initialTransform; d_data->initialTransform = NULL; } /*! \brief Replay all recorded painter commands The graphic is scaled to the defaultSize() and aligned to a position. \param painter Qt painter \param pos Reference point, where to render \param alignment Flags how to align the target rectangle to pos. */ void QwtGraphic::render( QPainter *painter, const QPointF &pos, Qt::Alignment alignment ) const { QRectF r( pos, defaultSize() ); if ( alignment & Qt::AlignLeft ) { r.moveLeft( pos.x() ); } else if ( alignment & Qt::AlignHCenter ) { r.moveCenter( QPointF( pos.x(), r.center().y() ) ); } else if ( alignment & Qt::AlignRight ) { r.moveRight( pos.x() ); } if ( alignment & Qt::AlignTop ) { r.moveTop( pos.y() ); } else if ( alignment & Qt::AlignVCenter ) { r.moveCenter( QPointF( r.center().x(), pos.y() ) ); } else if ( alignment & Qt::AlignBottom ) { r.moveBottom( pos.y() ); } render( painter, r ); } /*! \brief Convert the graphic to a QPixmap All pixels of the pixmap get initialized by Qt::transparent before the graphic is scaled and rendered on it. The size of the pixmap is the default size ( ceiled to integers ) of the graphic. \return The graphic as pixmap in default size \sa defaultSize(), toImage(), render() */ QPixmap QwtGraphic::toPixmap() const { if ( isNull() ) return QPixmap(); const QSizeF sz = defaultSize(); const int w = qCeil( sz.width() ); const int h = qCeil( sz.height() ); QPixmap pixmap( w, h ); pixmap.fill( Qt::transparent ); const QRectF r( 0.0, 0.0, sz.width(), sz.height() ); QPainter painter( &pixmap ); render( &painter, r, Qt::KeepAspectRatio ); painter.end(); return pixmap; } /*! \brief Convert the graphic to a QPixmap All pixels of the pixmap get initialized by Qt::transparent before the graphic is scaled and rendered on it. \param size Size of the image \param aspectRatioMode Aspect ratio how to scale the graphic \return The graphic as pixmap \sa toImage(), render() */ QPixmap QwtGraphic::toPixmap( const QSize &size, Qt::AspectRatioMode aspectRatioMode ) const { QPixmap pixmap( size ); pixmap.fill( Qt::transparent ); const QRect r( 0, 0, size.width(), size.height() ); QPainter painter( &pixmap ); render( &painter, r, aspectRatioMode ); painter.end(); return pixmap; } /*! \brief Convert the graphic to a QImage All pixels of the image get initialized by 0 ( transparent ) before the graphic is scaled and rendered on it. The format of the image is QImage::Format_ARGB32_Premultiplied. \param size Size of the image \param aspectRatioMode Aspect ratio how to scale the graphic \return The graphic as image \sa toPixmap(), render() */ QImage QwtGraphic::toImage( const QSize &size, Qt::AspectRatioMode aspectRatioMode ) const { QImage image( size, QImage::Format_ARGB32_Premultiplied ); image.fill( 0 ); const QRect r( 0, 0, size.width(), size.height() ); QPainter painter( &image ); render( &painter, r, aspectRatioMode ); painter.end(); return image; } /*! \brief Convert the graphic to a QImage All pixels of the image get initialized by 0 ( transparent ) before the graphic is scaled and rendered on it. The format of the image is QImage::Format_ARGB32_Premultiplied. The size of the image is the default size ( ceiled to integers ) of the graphic. \return The graphic as image in default size \sa defaultSize(), toPixmap(), render() */ QImage QwtGraphic::toImage() const { if ( isNull() ) return QImage(); const QSizeF sz = defaultSize(); const int w = qCeil( sz.width() ); const int h = qCeil( sz.height() ); QImage image( w, h, QImage::Format_ARGB32 ); image.fill( 0 ); const QRect r( 0, 0, sz.width(), sz.height() ); QPainter painter( &image ); render( &painter, r, Qt::KeepAspectRatio ); painter.end(); return image; } /*! Store a path command in the command list \param path Painter path \sa QPaintEngine::drawPath() */ void QwtGraphic::drawPath( const QPainterPath &path ) { const QPainter *painter = paintEngine()->painter(); if ( painter == NULL ) return; d_data->commands += QwtPainterCommand( path ); if ( !path.isEmpty() ) { const QPainterPath scaledPath = painter->transform().map( path ); QRectF pointRect = scaledPath.boundingRect(); QRectF boundingRect = pointRect; if ( painter->pen().style() != Qt::NoPen && painter->pen().brush().style() != Qt::NoBrush ) { boundingRect = qwtStrokedPathRect( painter, path ); } updateControlPointRect( pointRect ); updateBoundingRect( boundingRect ); d_data->pathInfos += PathInfo( pointRect, boundingRect, qwtHasScalablePen( painter ) ); } } /*! \brief Store a pixmap command in the command list \param rect target rectangle \param pixmap Pixmap to be painted \param subRect Reactangle of the pixmap to be painted \sa QPaintEngine::drawPixmap() */ void QwtGraphic::drawPixmap( const QRectF &rect, const QPixmap &pixmap, const QRectF &subRect ) { const QPainter *painter = paintEngine()->painter(); if ( painter == NULL ) return; d_data->commands += QwtPainterCommand( rect, pixmap, subRect ); const QRectF r = painter->transform().mapRect( rect ); updateControlPointRect( r ); updateBoundingRect( r ); } /*! \brief Store a image command in the command list \param rect traget rectangle \param image Image to be painted \param subRect Reactangle of the pixmap to be painted \param flags Image conversion flags \sa QPaintEngine::drawImage() */ void QwtGraphic::drawImage( const QRectF &rect, const QImage &image, const QRectF &subRect, Qt::ImageConversionFlags flags) { const QPainter *painter = paintEngine()->painter(); if ( painter == NULL ) return; d_data->commands += QwtPainterCommand( rect, image, subRect, flags ); const QRectF r = painter->transform().mapRect( rect ); updateControlPointRect( r ); updateBoundingRect( r ); } /*! \brief Store a state command in the command list \param state State to be stored \sa QPaintEngine::updateState() */ void QwtGraphic::updateState( const QPaintEngineState &state) { d_data->commands += QwtPainterCommand( state ); } void QwtGraphic::updateBoundingRect( const QRectF &rect ) { QRectF br = rect; const QPainter *painter = paintEngine()->painter(); if ( painter && painter->hasClipping() ) { QRectF cr = painter->clipRegion().boundingRect(); cr = painter->transform().mapRect( br ); br &= cr; } if ( d_data->boundingRect.width() < 0 ) d_data->boundingRect = br; else d_data->boundingRect |= br; } void QwtGraphic::updateControlPointRect( const QRectF &rect ) { if ( d_data->pointRect.width() < 0.0 ) d_data->pointRect = rect; else d_data->pointRect |= rect; } /*! \return List of recorded paint commands \sa setCommands() */ const QVector< QwtPainterCommand > &QwtGraphic::commands() const { return d_data->commands; } /*! \brief Append paint commands \param commands Paint commands \sa commands() */ void QwtGraphic::setCommands( QVector< QwtPainterCommand > &commands ) { reset(); const int numCommands = commands.size(); if ( numCommands <= 0 ) return; // to calculate a proper bounding rectangle we don't simply copy // the commands. const QwtPainterCommand *cmds = commands.constData(); QPainter painter( this ); for ( int i = 0; i < numCommands; i++ ) qwtExecCommand( &painter, cmds[i], RenderHints(), QTransform(), NULL ); painter.end(); } connectome-workbench-1.2.3+git41-gc4c6c90/src/Qwt/qwt_graphic.h000066400000000000000000000131441300200146000240030ustar00rootroot00000000000000/* -*- mode: C++ ; c-file-style: "stroustrup" -*- ***************************** * Qwt Widget Library * Copyright (C) 1997 Josef Wilgen * Copyright (C) 2002 Uwe Rathmann * * This library is free software; you can redistribute it and/or * modify it under the terms of the Qwt License, Version 1.0 *****************************************************************************/ #ifndef QWT_GRAPHIC_H #define QWT_GRAPHIC_H #include "qwt_global.h" #include "qwt_null_paintdevice.h" #include #include #include class QwtPainterCommand; /*! \brief A paint device for scalable graphics QwtGraphic is the representation of a graphic that is tailored for scalability. Like QPicture it will be initialized by QPainter operations and can be replayed later to any target paint device. While the usual image representations QImage and QPixmap are not scalable Qt offers two paint devices, that might be candidates for representing a vector graphic: - QPicture\n Unfortunately QPicture had been forgotten, when Qt4 introduced floating point based render engines. Its API is still on integers, what make it unusable for proper scaling. - QSvgRenderer/QSvgGenerator\n Unfortunately QSvgRenderer hides to much information about its nodes in internal APIs, that are necessary for proper layout calculations. Also it is derived from QObject and can't be copied like QImage/QPixmap. QwtGraphic maps all scalable drawing primitives to a QPainterPath and stores them together with the painter state changes ( pen, brush, transformation ... ) in a list of QwtPaintCommands. For being a complete QPaintDevice it also stores pixmaps or images, what is somehow against the idea of the class, because these objects can't be scaled without a loss in quality. The main issue about scaling a QwtGraphic object are the pens used for drawing the outlines of the painter paths. While non cosmetic pens ( QPen::isCosmetic() ) are scaled with the same ratio as the path, cosmetic pens have a fixed width. A graphic might have paths with different pens - cosmetic and non-cosmetic. QwtGraphic caches 2 different rectangles: - control point rectangle\n The control point rectangle is the bounding rectangle of all control point rectangles of the painter paths, or the target rectangle of the pixmaps/images. - bounding rectangle\n The bounding rectangle extends the control point rectangle by what is needed for rendering the outline with an unscaled pen. Because the offset for drawing the outline depends on the shape of the painter path ( the peak of a triangle is different than the flat side ) scaling with a fixed aspect ratio always needs to be calculated from the control point rectangle. \sa QwtPainterCommand */ class QWT_EXPORT QwtGraphic: public QwtNullPaintDevice { public: /*! Hint how to render a graphic \sa setRenderHint(), testRenderHint() */ enum RenderHint { /*! When rendering a QwtGraphic a specific scaling between the controlPointRect() and the coordinates of the target rectangle is set up internally in render(). When RenderPensUnscaled is set this specific scaling is applied for the control points only, but not for the pens. All other painter transformations ( set up by application code ) are supposed to work like usual. \sa render(); */ RenderPensUnscaled = 0x1 }; /*! \brief Render hints The default setting is to disable all hints */ typedef QFlags RenderHints; QwtGraphic(); QwtGraphic( const QwtGraphic & ); virtual ~QwtGraphic(); QwtGraphic& operator=( const QwtGraphic & ); void reset(); bool isNull() const; bool isEmpty() const; void render( QPainter * ) const; void render( QPainter *, const QSizeF &, Qt::AspectRatioMode = Qt::IgnoreAspectRatio ) const; void render( QPainter *, const QRectF &, Qt::AspectRatioMode = Qt::IgnoreAspectRatio ) const; void render( QPainter *, const QPointF &, Qt::Alignment = Qt::AlignTop | Qt::AlignLeft ) const; QPixmap toPixmap() const; QPixmap toPixmap( const QSize &, Qt::AspectRatioMode = Qt::IgnoreAspectRatio ) const; QImage toImage() const; QImage toImage( const QSize &, Qt::AspectRatioMode = Qt::IgnoreAspectRatio ) const; QRectF scaledBoundingRect( double sx, double sy ) const; QRectF boundingRect() const; QRectF controlPointRect() const; const QVector< QwtPainterCommand > &commands() const; void setCommands( QVector< QwtPainterCommand > & ); void setDefaultSize( const QSizeF & ); QSizeF defaultSize() const; void setRenderHint( RenderHint, bool on = true ); bool testRenderHint( RenderHint ) const; protected: virtual QSize sizeMetrics() const; virtual void drawPath( const QPainterPath & ); virtual void drawPixmap( const QRectF &, const QPixmap &, const QRectF & ); virtual void drawImage( const QRectF &, const QImage &, const QRectF &, Qt::ImageConversionFlags ); virtual void updateState( const QPaintEngineState &state ); private: void updateBoundingRect( const QRectF & ); void updateControlPointRect( const QRectF & ); class PathInfo; class PrivateData; PrivateData *d_data; }; Q_DECLARE_OPERATORS_FOR_FLAGS( QwtGraphic::RenderHints ) Q_DECLARE_METATYPE( QwtGraphic ) #endif connectome-workbench-1.2.3+git41-gc4c6c90/src/Qwt/qwt_interval.cpp000066400000000000000000000203701300200146000245440ustar00rootroot00000000000000/* -*- mode: C++ ; c-file-style: "stroustrup" -*- ***************************** * Qwt Widget Library * Copyright (C) 1997 Josef Wilgen * Copyright (C) 2002 Uwe Rathmann * * This library is free software; you can redistribute it and/or * modify it under the terms of the Qwt License, Version 1.0 *****************************************************************************/ #include "qwt_interval.h" #include "qwt_math.h" #include /*! \brief Normalize the limits of the interval If maxValue() < minValue() the limits will be inverted. \return Normalized interval \sa isValid(), inverted() */ QwtInterval QwtInterval::normalized() const { if ( d_minValue > d_maxValue ) { return inverted(); } if ( d_minValue == d_maxValue && d_borderFlags == ExcludeMinimum ) { return inverted(); } return *this; } /*! Invert the limits of the interval \return Inverted interval \sa normalized() */ QwtInterval QwtInterval::inverted() const { BorderFlags borderFlags = IncludeBorders; if ( d_borderFlags & ExcludeMinimum ) borderFlags |= ExcludeMaximum; if ( d_borderFlags & ExcludeMaximum ) borderFlags |= ExcludeMinimum; return QwtInterval( d_maxValue, d_minValue, borderFlags ); } /*! Test if a value is inside an interval \param value Value \return true, if value >= minValue() && value <= maxValue() */ bool QwtInterval::contains( double value ) const { if ( !isValid() ) return false; if ( value < d_minValue || value > d_maxValue ) return false; if ( value == d_minValue && d_borderFlags & ExcludeMinimum ) return false; if ( value == d_maxValue && d_borderFlags & ExcludeMaximum ) return false; return true; } //! Unite 2 intervals QwtInterval QwtInterval::unite( const QwtInterval &other ) const { /* If one of the intervals is invalid return the other one. If both are invalid return an invalid default interval */ if ( !isValid() ) { if ( !other.isValid() ) return QwtInterval(); else return other; } if ( !other.isValid() ) return *this; QwtInterval united; BorderFlags flags = IncludeBorders; // minimum if ( d_minValue < other.minValue() ) { united.setMinValue( d_minValue ); flags &= d_borderFlags & ExcludeMinimum; } else if ( other.minValue() < d_minValue ) { united.setMinValue( other.minValue() ); flags &= other.borderFlags() & ExcludeMinimum; } else // d_minValue == other.minValue() { united.setMinValue( d_minValue ); flags &= ( d_borderFlags & other.borderFlags() ) & ExcludeMinimum; } // maximum if ( d_maxValue > other.maxValue() ) { united.setMaxValue( d_maxValue ); flags &= d_borderFlags & ExcludeMaximum; } else if ( other.maxValue() > d_maxValue ) { united.setMaxValue( other.maxValue() ); flags &= other.borderFlags() & ExcludeMaximum; } else // d_maxValue == other.maxValue() ) { united.setMaxValue( d_maxValue ); flags &= d_borderFlags & other.borderFlags() & ExcludeMaximum; } united.setBorderFlags( flags ); return united; } /*! \brief Intersect 2 intervals \param other Interval to be intersect with \return Intersection */ QwtInterval QwtInterval::intersect( const QwtInterval &other ) const { if ( !other.isValid() || !isValid() ) return QwtInterval(); QwtInterval i1 = *this; QwtInterval i2 = other; // swap i1/i2, so that the minimum of i1 // is smaller then the minimum of i2 if ( i1.minValue() > i2.minValue() ) { qSwap( i1, i2 ); } else if ( i1.minValue() == i2.minValue() ) { if ( i1.borderFlags() & ExcludeMinimum ) qSwap( i1, i2 ); } if ( i1.maxValue() < i2.minValue() ) { return QwtInterval(); } if ( i1.maxValue() == i2.minValue() ) { if ( i1.borderFlags() & ExcludeMaximum || i2.borderFlags() & ExcludeMinimum ) { return QwtInterval(); } } QwtInterval intersected; BorderFlags flags = IncludeBorders; intersected.setMinValue( i2.minValue() ); flags |= i2.borderFlags() & ExcludeMinimum; if ( i1.maxValue() < i2.maxValue() ) { intersected.setMaxValue( i1.maxValue() ); flags |= i1.borderFlags() & ExcludeMaximum; } else if ( i2.maxValue() < i1.maxValue() ) { intersected.setMaxValue( i2.maxValue() ); flags |= i2.borderFlags() & ExcludeMaximum; } else // i1.maxValue() == i2.maxValue() { intersected.setMaxValue( i1.maxValue() ); flags |= i1.borderFlags() & i2.borderFlags() & ExcludeMaximum; } intersected.setBorderFlags( flags ); return intersected; } /*! \brief Unite this interval with the given interval. \param other Interval to be united with \return This interval */ QwtInterval& QwtInterval::operator|=( const QwtInterval &other ) { *this = *this | other; return *this; } /*! \brief Intersect this interval with the given interval. \param other Interval to be intersected with \return This interval */ QwtInterval& QwtInterval::operator&=( const QwtInterval &other ) { *this = *this & other; return *this; } /*! \brief Test if two intervals overlap \param other Interval \return True, when the intervals are intersecting */ bool QwtInterval::intersects( const QwtInterval &other ) const { if ( !isValid() || !other.isValid() ) return false; QwtInterval i1 = *this; QwtInterval i2 = other; // swap i1/i2, so that the minimum of i1 // is smaller then the minimum of i2 if ( i1.minValue() > i2.minValue() ) { qSwap( i1, i2 ); } else if ( i1.minValue() == i2.minValue() && i1.borderFlags() & ExcludeMinimum ) { qSwap( i1, i2 ); } if ( i1.maxValue() > i2.minValue() ) { return true; } if ( i1.maxValue() == i2.minValue() ) { return !( ( i1.borderFlags() & ExcludeMaximum ) || ( i2.borderFlags() & ExcludeMinimum ) ); } return false; } /*! Adjust the limit that is closer to value, so that value becomes the center of the interval. \param value Center \return Interval with value as center */ QwtInterval QwtInterval::symmetrize( double value ) const { if ( !isValid() ) return *this; const double delta = qMax( qAbs( value - d_maxValue ), qAbs( value - d_minValue ) ); return QwtInterval( value - delta, value + delta ); } /*! Limit the interval, keeping the border modes \param lowerBound Lower limit \param upperBound Upper limit \return Limited interval */ QwtInterval QwtInterval::limited( double lowerBound, double upperBound ) const { if ( !isValid() || lowerBound > upperBound ) return QwtInterval(); double minValue = qMax( d_minValue, lowerBound ); minValue = qMin( minValue, upperBound ); double maxValue = qMax( d_maxValue, lowerBound ); maxValue = qMin( maxValue, upperBound ); return QwtInterval( minValue, maxValue, d_borderFlags ); } /*! \brief Extend the interval If value is below minValue(), value becomes the lower limit. If value is above maxValue(), value becomes the upper limit. extend() has no effect for invalid intervals \param value Value \return extended interval \sa isValid() */ QwtInterval QwtInterval::extend( double value ) const { if ( !isValid() ) return *this; return QwtInterval( qMin( value, d_minValue ), qMax( value, d_maxValue ), d_borderFlags ); } /*! Extend an interval \param value Value \return Reference of the extended interval \sa extend() */ QwtInterval& QwtInterval::operator|=( double value ) { *this = *this | value; return *this; } #ifndef QT_NO_DEBUG_STREAM QDebug operator<<( QDebug debug, const QwtInterval &interval ) { const int flags = interval.borderFlags(); debug.nospace() << "QwtInterval(" << ( ( flags & QwtInterval::ExcludeMinimum ) ? "]" : "[" ) << interval.minValue() << "," << interval.maxValue() << ( ( flags & QwtInterval::ExcludeMaximum ) ? "[" : "]" ) << ")"; return debug.space(); } #endif connectome-workbench-1.2.3+git41-gc4c6c90/src/Qwt/qwt_interval.h000066400000000000000000000157761300200146000242270ustar00rootroot00000000000000/* -*- mode: C++ ; c-file-style: "stroustrup" -*- ***************************** * Qwt Widget Library * Copyright (C) 1997 Josef Wilgen * Copyright (C) 2002 Uwe Rathmann * * This library is free software; you can redistribute it and/or * modify it under the terms of the Qwt License, Version 1.0 *****************************************************************************/ #ifndef QWT_INTERVAL_H #define QWT_INTERVAL_H #include "qwt_global.h" #include #ifndef QT_NO_DEBUG_STREAM #include #endif /*! \brief A class representing an interval The interval is represented by 2 doubles, the lower and the upper limit. */ class QWT_EXPORT QwtInterval { public: /*! Flag indicating if a border is included or excluded \sa setBorderFlags(), borderFlags() */ enum BorderFlag { //! Min/Max values are inside the interval IncludeBorders = 0x00, //! Min value is not included in the interval ExcludeMinimum = 0x01, //! Max value is not included in the interval ExcludeMaximum = 0x02, //! Min/Max values are not included in the interval ExcludeBorders = ExcludeMinimum | ExcludeMaximum }; //! Border flags typedef QFlags BorderFlags; QwtInterval(); QwtInterval( double minValue, double maxValue, BorderFlags = IncludeBorders ); void setInterval( double minValue, double maxValue, BorderFlags = IncludeBorders ); QwtInterval normalized() const; QwtInterval inverted() const; QwtInterval limited( double minValue, double maxValue ) const; bool operator==( const QwtInterval & ) const; bool operator!=( const QwtInterval & ) const; void setBorderFlags( BorderFlags ); BorderFlags borderFlags() const; double minValue() const; double maxValue() const; double width() const; void setMinValue( double ); void setMaxValue( double ); bool contains( double value ) const; bool intersects( const QwtInterval & ) const; QwtInterval intersect( const QwtInterval & ) const; QwtInterval unite( const QwtInterval & ) const; QwtInterval operator|( const QwtInterval & ) const; QwtInterval operator&( const QwtInterval & ) const; QwtInterval &operator|=( const QwtInterval & ); QwtInterval &operator&=( const QwtInterval & ); QwtInterval extend( double value ) const; QwtInterval operator|( double ) const; QwtInterval &operator|=( double ); bool isValid() const; bool isNull() const; void invalidate(); QwtInterval symmetrize( double value ) const; private: double d_minValue; double d_maxValue; BorderFlags d_borderFlags; }; Q_DECLARE_TYPEINFO(QwtInterval, Q_MOVABLE_TYPE); /*! \brief Default Constructor Creates an invalid interval [0.0, -1.0] \sa setInterval(), isValid() */ inline QwtInterval::QwtInterval(): d_minValue( 0.0 ), d_maxValue( -1.0 ), d_borderFlags( IncludeBorders ) { } /*! Constructor Build an interval with from min/max values \param minValue Minimum value \param maxValue Maximum value \param borderFlags Include/Exclude borders */ inline QwtInterval::QwtInterval( double minValue, double maxValue, BorderFlags borderFlags ): d_minValue( minValue ), d_maxValue( maxValue ), d_borderFlags( borderFlags ) { } /*! Assign the limits of the interval \param minValue Minimum value \param maxValue Maximum value \param borderFlags Include/Exclude borders */ inline void QwtInterval::setInterval( double minValue, double maxValue, BorderFlags borderFlags ) { d_minValue = minValue; d_maxValue = maxValue; d_borderFlags = borderFlags; } /*! Change the border flags \param borderFlags Or'd BorderMode flags \sa borderFlags() */ inline void QwtInterval::setBorderFlags( BorderFlags borderFlags ) { d_borderFlags = borderFlags; } /*! \return Border flags \sa setBorderFlags() */ inline QwtInterval::BorderFlags QwtInterval::borderFlags() const { return d_borderFlags; } /*! Assign the lower limit of the interval \param minValue Minimum value */ inline void QwtInterval::setMinValue( double minValue ) { d_minValue = minValue; } /*! Assign the upper limit of the interval \param maxValue Maximum value */ inline void QwtInterval::setMaxValue( double maxValue ) { d_maxValue = maxValue; } //! \return Lower limit of the interval inline double QwtInterval::minValue() const { return d_minValue; } //! \return Upper limit of the interval inline double QwtInterval::maxValue() const { return d_maxValue; } /*! A interval is valid when minValue() <= maxValue(). In case of QwtInterval::ExcludeBorders it is true when minValue() < maxValue() \return True, when the interval is valid */ inline bool QwtInterval::isValid() const { if ( ( d_borderFlags & ExcludeBorders ) == 0 ) return d_minValue <= d_maxValue; else return d_minValue < d_maxValue; } /*! \brief Return the width of an interval The width of invalid intervals is 0.0, otherwise the result is maxValue() - minValue(). \return Interval width \sa isValid() */ inline double QwtInterval::width() const { return isValid() ? ( d_maxValue - d_minValue ) : 0.0; } /*! \brief Intersection of two intervals \param other Interval to intersect with \return Intersection of this and other \sa intersect() */ inline QwtInterval QwtInterval::operator&( const QwtInterval &other ) const { return intersect( other ); } /*! Union of two intervals \param other Interval to unite with \return Union of this and other \sa unite() */ inline QwtInterval QwtInterval::operator|( const QwtInterval &other ) const { return unite( other ); } /*! \brief Compare two intervals \param other Interval to compare with \return True, when this and other are equal */ inline bool QwtInterval::operator==( const QwtInterval &other ) const { return ( d_minValue == other.d_minValue ) && ( d_maxValue == other.d_maxValue ) && ( d_borderFlags == other.d_borderFlags ); } /*! \brief Compare two intervals \param other Interval to compare with \return True, when this and other are not equal */ inline bool QwtInterval::operator!=( const QwtInterval &other ) const { return ( !( *this == other ) ); } /*! Extend an interval \param value Value \return Extended interval \sa extend() */ inline QwtInterval QwtInterval::operator|( double value ) const { return extend( value ); } //! \return true, if isValid() && (minValue() >= maxValue()) inline bool QwtInterval::isNull() const { return isValid() && d_minValue >= d_maxValue; } /*! Invalidate the interval The limits are set to interval [0.0, -1.0] \sa isValid() */ inline void QwtInterval::invalidate() { d_minValue = 0.0; d_maxValue = -1.0; } Q_DECLARE_OPERATORS_FOR_FLAGS( QwtInterval::BorderFlags ) Q_DECLARE_METATYPE( QwtInterval ) #ifndef QT_NO_DEBUG_STREAM QWT_EXPORT QDebug operator<<( QDebug, const QwtInterval & ); #endif #endif connectome-workbench-1.2.3+git41-gc4c6c90/src/Qwt/qwt_interval_symbol.cpp000066400000000000000000000175471300200146000261450ustar00rootroot00000000000000/* -*- mode: C++ ; c-file-style: "stroustrup" -*- ***************************** * Qwt Widget Library * Copyright (C) 1997 Josef Wilgen * Copyright (C) 2002 Uwe Rathmann * * This library is free software; you can redistribute it and/or * modify it under the terms of the Qwt License, Version 1.0 *****************************************************************************/ #include "qwt_interval_symbol.h" #include "qwt_painter.h" #include "qwt_math.h" #include #if QT_VERSION < 0x040601 #define qAtan2(y, x) ::atan2(y, x) #define qFastSin(x) qSin(x) #define qFastCos(x) qCos(x) #endif class QwtIntervalSymbol::PrivateData { public: PrivateData(): style( QwtIntervalSymbol::NoSymbol ), width( 6 ) { } bool operator==( const PrivateData &other ) const { return ( style == other.style ) && ( width == other.width ) && ( brush == other.brush ) && ( pen == other.pen ); } QwtIntervalSymbol::Style style; int width; QPen pen; QBrush brush; }; /*! Constructor \param style Style of the symbol \sa setStyle(), style(), Style */ QwtIntervalSymbol::QwtIntervalSymbol( Style style ) { d_data = new PrivateData(); d_data->style = style; } //! Copy constructor QwtIntervalSymbol::QwtIntervalSymbol( const QwtIntervalSymbol &other ) { d_data = new PrivateData(); *d_data = *other.d_data; } //! Destructor QwtIntervalSymbol::~QwtIntervalSymbol() { delete d_data; } //! \brief Assignment operator QwtIntervalSymbol &QwtIntervalSymbol::operator=( const QwtIntervalSymbol &other ) { *d_data = *other.d_data; return *this; } //! \brief Compare two symbols bool QwtIntervalSymbol::operator==( const QwtIntervalSymbol &other ) const { return *d_data == *other.d_data; } //! \brief Compare two symbols bool QwtIntervalSymbol::operator!=( const QwtIntervalSymbol &other ) const { return !( *d_data == *other.d_data ); } /*! Specify the symbol style \param style Style \sa style(), Style */ void QwtIntervalSymbol::setStyle( Style style ) { d_data->style = style; } /*! \return Current symbol style \sa setStyle() */ QwtIntervalSymbol::Style QwtIntervalSymbol::style() const { return d_data->style; } /*! Specify the width of the symbol It is used depending on the style. \param width Width \sa width(), setStyle() */ void QwtIntervalSymbol::setWidth( int width ) { d_data->width = width; } /*! \return Width of the symbol. \sa setWidth(), setStyle() */ int QwtIntervalSymbol::width() const { return d_data->width; } /*! \brief Assign a brush The brush is used for the Box style. \param brush Brush \sa brush() */ void QwtIntervalSymbol::setBrush( const QBrush &brush ) { d_data->brush = brush; } /*! \return Brush \sa setBrush() */ const QBrush& QwtIntervalSymbol::brush() const { return d_data->brush; } /*! Build and assign a pen In Qt5 the default pen width is 1.0 ( 0.0 in Qt4 ) what makes it non cosmetic ( see QPen::isCosmetic() ). This method has been introduced to hide this incompatibility. \param color Pen color \param width Pen width \param style Pen style \sa pen(), brush() */ void QwtIntervalSymbol::setPen( const QColor &color, qreal width, Qt::PenStyle style ) { setPen( QPen( color, width, style ) ); } /*! Assign a pen \param pen Pen \sa pen(), setBrush() */ void QwtIntervalSymbol::setPen( const QPen &pen ) { d_data->pen = pen; } /*! \return Pen \sa setPen(), brush() */ const QPen& QwtIntervalSymbol::pen() const { return d_data->pen; } /*! Draw a symbol depending on its style \param painter Painter \param orientation Orientation \param from Start point of the interval in target device coordinates \param to End point of the interval in target device coordinates \sa setStyle() */ void QwtIntervalSymbol::draw( QPainter *painter, Qt::Orientation orientation, const QPointF &from, const QPointF &to ) const { const qreal pw = qMax( painter->pen().widthF(), qreal( 1.0 ) ); QPointF p1 = from; QPointF p2 = to; if ( QwtPainter::roundingAlignment( painter ) ) { p1 = p1.toPoint(); p2 = p2.toPoint(); } switch ( d_data->style ) { case QwtIntervalSymbol::Bar: { QwtPainter::drawLine( painter, p1, p2 ); if ( d_data->width > pw ) { if ( ( orientation == Qt::Horizontal ) && ( p1.y() == p2.y() ) ) { const double sw = d_data->width; const double y = p1.y() - sw / 2; QwtPainter::drawLine( painter, p1.x(), y, p1.x(), y + sw ); QwtPainter::drawLine( painter, p2.x(), y, p2.x(), y + sw ); } else if ( ( orientation == Qt::Vertical ) && ( p1.x() == p2.x() ) ) { const double sw = d_data->width; const double x = p1.x() - sw / 2; QwtPainter::drawLine( painter, x, p1.y(), x + sw, p1.y() ); QwtPainter::drawLine( painter, x, p2.y(), x + sw, p2.y() ); } else { const double sw = d_data->width; const double dx = p2.x() - p1.x(); const double dy = p2.y() - p1.y(); const double angle = qAtan2( dy, dx ) + M_PI_2; double dw2 = sw / 2.0; const double cx = qFastCos( angle ) * dw2; const double sy = qFastSin( angle ) * dw2; QwtPainter::drawLine( painter, p1.x() - cx, p1.y() - sy, p1.x() + cx, p1.y() + sy ); QwtPainter::drawLine( painter, p2.x() - cx, p2.y() - sy, p2.x() + cx, p2.y() + sy ); } } break; } case QwtIntervalSymbol::Box: { if ( d_data->width <= pw ) { QwtPainter::drawLine( painter, p1, p2 ); } else { if ( ( orientation == Qt::Horizontal ) && ( p1.y() == p2.y() ) ) { const double sw = d_data->width; const double y = p1.y() - d_data->width / 2; QwtPainter::drawRect( painter, p1.x(), y, p2.x() - p1.x(), sw ); } else if ( ( orientation == Qt::Vertical ) && ( p1.x() == p2.x() ) ) { const double sw = d_data->width; const double x = p1.x() - d_data->width / 2; QwtPainter::drawRect( painter, x, p1.y(), sw, p2.y() - p1.y() ); } else { const double sw = d_data->width; const double dx = p2.x() - p1.x(); const double dy = p2.y() - p1.y(); const double angle = qAtan2( dy, dx ) + M_PI_2; double dw2 = sw / 2.0; const double cx = qFastCos( angle ) * dw2; const double sy = qFastSin( angle ) * dw2; QPolygonF polygon; polygon += QPointF( p1.x() - cx, p1.y() - sy ); polygon += QPointF( p1.x() + cx, p1.y() + sy ); polygon += QPointF( p2.x() + cx, p2.y() + sy ); polygon += QPointF( p2.x() - cx, p2.y() - sy ); QwtPainter::drawPolygon( painter, polygon ); } } break; } default:; } } connectome-workbench-1.2.3+git41-gc4c6c90/src/Qwt/qwt_interval_symbol.h000066400000000000000000000043141300200146000255760ustar00rootroot00000000000000/* -*- mode: C++ ; c-file-style: "stroustrup" -*- ***************************** * Qwt Widget Library * Copyright (C) 1997 Josef Wilgen * Copyright (C) 2002 Uwe Rathmann * * This library is free software; you can redistribute it and/or * modify it under the terms of the Qwt License, Version 1.0 *****************************************************************************/ #ifndef QWT_INTERVAL_SYMBOL_H #define QWT_INTERVAL_SYMBOL_H #include "qwt_global.h" #include #include class QPainter; class QRect; class QPointF; /*! \brief A drawing primitive for displaying an interval like an error bar \sa QwtPlotIntervalCurve */ class QWT_EXPORT QwtIntervalSymbol { public: //! Symbol style enum Style { //! No Style. The symbol cannot be drawn. NoSymbol = -1, /*! The symbol displays a line with caps at the beginning/end. The size of the caps depends on the symbol width(). */ Bar, /*! The symbol displays a plain rectangle using pen() and brush(). The size of the rectangle depends on the translated interval and the width(), */ Box, /*! Styles >= UserSymbol are reserved for derived classes of QwtIntervalSymbol that overload draw() with additional application specific symbol types. */ UserSymbol = 1000 }; public: QwtIntervalSymbol( Style = NoSymbol ); QwtIntervalSymbol( const QwtIntervalSymbol & ); virtual ~QwtIntervalSymbol(); QwtIntervalSymbol &operator=( const QwtIntervalSymbol & ); bool operator==( const QwtIntervalSymbol & ) const; bool operator!=( const QwtIntervalSymbol & ) const; void setWidth( int ); int width() const; void setBrush( const QBrush& b ); const QBrush& brush() const; void setPen( const QColor &, qreal width = 0.0, Qt::PenStyle = Qt::SolidLine ); void setPen( const QPen & ); const QPen& pen() const; void setStyle( Style ); Style style() const; virtual void draw( QPainter *, Qt::Orientation, const QPointF& from, const QPointF& to ) const; private: class PrivateData; PrivateData* d_data; }; #endif connectome-workbench-1.2.3+git41-gc4c6c90/src/Qwt/qwt_legend.cpp000066400000000000000000000515371300200146000241670ustar00rootroot00000000000000/* -*- mode: C++ ; c-file-style: "stroustrup" -*- ***************************** * Qwt Widget Library * Copyright (C) 1997 Josef Wilgen * Copyright (C) 2002 Uwe Rathmann * * This library is free software; you can redistribute it and/or * modify it under the terms of the Qwt License, Version 1.0 *****************************************************************************/ #include "qwt_legend.h" #include "qwt_legend_label.h" #include "qwt_dyngrid_layout.h" #include "qwt_math.h" #include "qwt_plot_item.h" #include "qwt_painter.h" #include #include #include #include #include #include class QwtLegendMap { public: inline bool isEmpty() const { return d_entries.isEmpty(); } void insert( const QVariant &, const QList & ); void remove( const QVariant & ); void removeWidget( const QWidget * ); QList legendWidgets( const QVariant & ) const; QVariant itemInfo( const QWidget * ) const; private: // we don't know anything about itemInfo and therefore don't have // any key that can be used for a map or hashtab. // But a simple linear list is o.k. here, as we will never have // more than a few entries. class Entry { public: QVariant itemInfo; QList widgets; }; QList< Entry > d_entries; }; void QwtLegendMap::insert( const QVariant &itemInfo, const QList &widgets ) { for ( int i = 0; i < d_entries.size(); i++ ) { Entry &entry = d_entries[i]; if ( entry.itemInfo == itemInfo ) { entry.widgets = widgets; return; } } Entry newEntry; newEntry.itemInfo = itemInfo; newEntry.widgets = widgets; d_entries += newEntry; } void QwtLegendMap::remove( const QVariant &itemInfo ) { for ( int i = 0; i < d_entries.size(); i++ ) { Entry &entry = d_entries[i]; if ( entry.itemInfo == itemInfo ) { d_entries.removeAt( i ); return; } } } void QwtLegendMap::removeWidget( const QWidget *widget ) { QWidget *w = const_cast( widget ); for ( int i = 0; i < d_entries.size(); i++ ) d_entries[ i ].widgets.removeAll( w ); } QVariant QwtLegendMap::itemInfo( const QWidget *widget ) const { if ( widget != NULL ) { QWidget *w = const_cast( widget ); for ( int i = 0; i < d_entries.size(); i++ ) { const Entry &entry = d_entries[i]; if ( entry.widgets.indexOf( w ) >= 0 ) return entry.itemInfo; } } return QVariant(); } QList QwtLegendMap::legendWidgets( const QVariant &itemInfo ) const { if ( itemInfo.isValid() ) { for ( int i = 0; i < d_entries.size(); i++ ) { const Entry &entry = d_entries[i]; if ( entry.itemInfo == itemInfo ) return entry.widgets; } } return QList(); } class QwtLegend::PrivateData { public: PrivateData(): itemMode( QwtLegendData::ReadOnly ), view( NULL ) { } QwtLegendData::Mode itemMode; QwtLegendMap itemMap; class LegendView; LegendView *view; }; class QwtLegend::PrivateData::LegendView: public QScrollArea { public: LegendView( QWidget *parent ): QScrollArea( parent ) { contentsWidget = new QWidget( this ); contentsWidget->setObjectName( "QwtLegendViewContents" ); setWidget( contentsWidget ); setWidgetResizable( false ); viewport()->setObjectName( "QwtLegendViewport" ); // QScrollArea::setWidget internally sets autoFillBackground to true // But we don't want a background. contentsWidget->setAutoFillBackground( false ); viewport()->setAutoFillBackground( false ); } virtual bool event( QEvent *event ) { if ( event->type() == QEvent::PolishRequest ) { setFocusPolicy( Qt::NoFocus ); } if ( event->type() == QEvent::Resize ) { // adjust the size to en/disable the scrollbars // before QScrollArea adjusts the viewport size const QRect cr = contentsRect(); int w = cr.width(); int h = contentsWidget->heightForWidth( cr.width() ); if ( h > w ) { w -= verticalScrollBar()->sizeHint().width(); h = contentsWidget->heightForWidth( w ); } contentsWidget->resize( w, h ); } return QScrollArea::event( event ); } virtual bool viewportEvent( QEvent *event ) { bool ok = QScrollArea::viewportEvent( event ); if ( event->type() == QEvent::Resize ) { layoutContents(); } return ok; } QSize viewportSize( int w, int h ) const { const int sbHeight = horizontalScrollBar()->sizeHint().height(); const int sbWidth = verticalScrollBar()->sizeHint().width(); const int cw = contentsRect().width(); const int ch = contentsRect().height(); int vw = cw; int vh = ch; if ( w > vw ) vh -= sbHeight; if ( h > vh ) { vw -= sbWidth; if ( w > vw && vh == ch ) vh -= sbHeight; } return QSize( vw, vh ); } void layoutContents() { const QwtDynGridLayout *tl = qobject_cast( contentsWidget->layout() ); if ( tl == NULL ) return; const QSize visibleSize = viewport()->contentsRect().size(); const int minW = int( tl->maxItemWidth() ) + 2 * tl->margin(); int w = qMax( visibleSize.width(), minW ); int h = qMax( tl->heightForWidth( w ), visibleSize.height() ); const int vpWidth = viewportSize( w, h ).width(); if ( w > vpWidth ) { w = qMax( vpWidth, minW ); h = qMax( tl->heightForWidth( w ), visibleSize.height() ); } contentsWidget->resize( w, h ); } QWidget *contentsWidget; }; /*! Constructor \param parent Parent widget */ QwtLegend::QwtLegend( QWidget *parent ): QwtAbstractLegend( parent ) { setFrameStyle( NoFrame ); d_data = new QwtLegend::PrivateData; d_data->view = new QwtLegend::PrivateData::LegendView( this ); d_data->view->setObjectName( "QwtLegendView" ); d_data->view->setFrameStyle( NoFrame ); QwtDynGridLayout *gridLayout = new QwtDynGridLayout( d_data->view->contentsWidget ); gridLayout->setAlignment( Qt::AlignHCenter | Qt::AlignTop ); d_data->view->contentsWidget->installEventFilter( this ); QVBoxLayout *layout = new QVBoxLayout( this ); layout->setContentsMargins( 0, 0, 0, 0 ); layout->addWidget( d_data->view ); } //! Destructor QwtLegend::~QwtLegend() { delete d_data; } /*! \brief Set the maximum number of entries in a row F.e when the maximum is set to 1 all items are aligned vertically. 0 means unlimited \param numColums Maximum number of entries in a row \sa maxColumns(), QwtDynGridLayout::setMaxColumns() */ void QwtLegend::setMaxColumns( uint numColums ) { QwtDynGridLayout *tl = qobject_cast( d_data->view->contentsWidget->layout() ); if ( tl ) tl->setMaxColumns( numColums ); } /*! \return Maximum number of entries in a row \sa setMaxColumns(), QwtDynGridLayout::maxColumns() */ uint QwtLegend::maxColumns() const { uint maxCols = 0; const QwtDynGridLayout *tl = qobject_cast( d_data->view->contentsWidget->layout() ); if ( tl ) maxCols = tl->maxColumns(); return maxCols; } /*! \brief Set the default mode for legend labels Legend labels will be constructed according to the attributes in a QwtLegendData object. When it doesn't contain a value for the QwtLegendData::ModeRole the label will be initialized with the default mode of the legend. \param mode Default item mode \sa itemMode(), QwtLegendData::value(), QwtPlotItem::legendData() \note Changing the mode doesn't have any effect on existing labels. */ void QwtLegend::setDefaultItemMode( QwtLegendData::Mode mode ) { d_data->itemMode = mode; } /*! \return Default item mode \sa setDefaultItemMode() */ QwtLegendData::Mode QwtLegend::defaultItemMode() const { return d_data->itemMode; } /*! The contents widget is the only child of the viewport of the internal QScrollArea and the parent widget of all legend items. \return Container widget of the legend items */ QWidget *QwtLegend::contentsWidget() { return d_data->view->contentsWidget; } /*! \return Horizontal scrollbar \sa verticalScrollBar() */ QScrollBar *QwtLegend::horizontalScrollBar() const { return d_data->view->horizontalScrollBar(); } /*! \return Vertical scrollbar \sa horizontalScrollBar() */ QScrollBar *QwtLegend::verticalScrollBar() const { return d_data->view->verticalScrollBar(); } /*! The contents widget is the only child of the viewport of the internal QScrollArea and the parent widget of all legend items. \return Container widget of the legend items */ const QWidget *QwtLegend::contentsWidget() const { return d_data->view->contentsWidget; } /*! \brief Update the entries for an item \param itemInfo Info for an item \param data List of legend entry attributes for the item */ void QwtLegend::updateLegend( const QVariant &itemInfo, const QList &data ) { QList widgetList = legendWidgets( itemInfo ); if ( widgetList.size() != data.size() ) { QLayout *contentsLayout = d_data->view->contentsWidget->layout(); while ( widgetList.size() > data.size() ) { QWidget *w = widgetList.takeLast(); contentsLayout->removeWidget( w ); // updates might be triggered by signals from the legend widget // itself. So we better don't delete it here. w->hide(); w->deleteLater(); } for ( int i = widgetList.size(); i < data.size(); i++ ) { QWidget *widget = createWidget( data[i] ); if ( contentsLayout ) contentsLayout->addWidget( widget ); if ( isVisible() ) { // QLayout does a delayed show, with the effect, that // the size hint will be wrong, when applications // call replot() right after changing the list // of plot items. So we better do the show now. widget->setVisible( true ); } widgetList += widget; } if ( widgetList.isEmpty() ) { d_data->itemMap.remove( itemInfo ); } else { d_data->itemMap.insert( itemInfo, widgetList ); } updateTabOrder(); } for ( int i = 0; i < data.size(); i++ ) updateWidget( widgetList[i], data[i] ); } /*! \brief Create a widget to be inserted into the legend The default implementation returns a QwtLegendLabel. \param data Attributes of the legend entry \return Widget representing data on the legend \note updateWidget() will called soon after createWidget() with the same attributes. */ QWidget *QwtLegend::createWidget( const QwtLegendData &data ) const { Q_UNUSED( data ); QwtLegendLabel *label = new QwtLegendLabel(); label->setItemMode( defaultItemMode() ); connect( label, SIGNAL( clicked() ), SLOT( itemClicked() ) ); connect( label, SIGNAL( checked( bool ) ), SLOT( itemChecked( bool ) ) ); return label; } /*! \brief Update the widget \param widget Usually a QwtLegendLabel \param data Attributes to be displayed \sa createWidget() \note When widget is no QwtLegendLabel updateWidget() does nothing. */ void QwtLegend::updateWidget( QWidget *widget, const QwtLegendData &data ) { QwtLegendLabel *label = qobject_cast( widget ); if ( label ) { label->setData( data ); if ( !data.value( QwtLegendData::ModeRole ).isValid() ) { // use the default mode, when there is no specific // hint from the legend data label->setItemMode( defaultItemMode() ); } } } void QwtLegend::updateTabOrder() { QLayout *contentsLayout = d_data->view->contentsWidget->layout(); if ( contentsLayout ) { // set tab focus chain QWidget *w = NULL; for ( int i = 0; i < contentsLayout->count(); i++ ) { QLayoutItem *item = contentsLayout->itemAt( i ); if ( w && item->widget() ) QWidget::setTabOrder( w, item->widget() ); w = item->widget(); } } } //! Return a size hint. QSize QwtLegend::sizeHint() const { QSize hint = d_data->view->contentsWidget->sizeHint(); hint += QSize( 2 * frameWidth(), 2 * frameWidth() ); return hint; } /*! \return The preferred height, for a width. \param width Width */ int QwtLegend::heightForWidth( int width ) const { width -= 2 * frameWidth(); int h = d_data->view->contentsWidget->heightForWidth( width ); if ( h >= 0 ) h += 2 * frameWidth(); return h; } /*! Handle QEvent::ChildRemoved andQEvent::LayoutRequest events for the contentsWidget(). \param object Object to be filtered \param event Event \return Forwarded to QwtAbstractLegend::eventFilter() */ bool QwtLegend::eventFilter( QObject *object, QEvent *event ) { if ( object == d_data->view->contentsWidget ) { switch ( event->type() ) { case QEvent::ChildRemoved: { const QChildEvent *ce = static_cast(event); if ( ce->child()->isWidgetType() ) { QWidget *w = static_cast< QWidget * >( ce->child() ); d_data->itemMap.removeWidget( w ); } break; } case QEvent::LayoutRequest: { d_data->view->layoutContents(); if ( parentWidget() && parentWidget()->layout() == NULL ) { /* We want the parent widget ( usually QwtPlot ) to recalculate its layout, when the contentsWidget has changed. But because of the scroll view we have to forward the LayoutRequest event manually. We don't use updateGeometry() because it doesn't post LayoutRequest events when the legend is hidden. But we want the parent widget notified, so it can show/hide the legend depending on its items. */ QApplication::postEvent( parentWidget(), new QEvent( QEvent::LayoutRequest ) ); } break; } default: break; } } return QwtAbstractLegend::eventFilter( object, event ); } /*! Called internally when the legend has been clicked on. Emits a clicked() signal. */ void QwtLegend::itemClicked() { QWidget *w = qobject_cast( sender() ); if ( w ) { const QVariant itemInfo = d_data->itemMap.itemInfo( w ); if ( itemInfo.isValid() ) { const QList widgetList = d_data->itemMap.legendWidgets( itemInfo ); const int index = widgetList.indexOf( w ); if ( index >= 0 ) Q_EMIT clicked( itemInfo, index ); } } } /*! Called internally when the legend has been checked Emits a checked() signal. */ void QwtLegend::itemChecked( bool on ) { QWidget *w = qobject_cast( sender() ); if ( w ) { const QVariant itemInfo = d_data->itemMap.itemInfo( w ); if ( itemInfo.isValid() ) { const QList widgetList = d_data->itemMap.legendWidgets( itemInfo ); const int index = widgetList.indexOf( w ); if ( index >= 0 ) Q_EMIT checked( itemInfo, on, index ); } } } /*! Render the legend into a given rectangle. \param painter Painter \param rect Bounding rectangle \param fillBackground When true, fill rect with the widget background \sa renderLegend() is used by QwtPlotRenderer - not by QwtLegend itself */ void QwtLegend::renderLegend( QPainter *painter, const QRectF &rect, bool fillBackground ) const { if ( d_data->itemMap.isEmpty() ) return; if ( fillBackground ) { if ( autoFillBackground() || testAttribute( Qt::WA_StyledBackground ) ) { QwtPainter::drawBackgound( painter, rect, this ); } } const QwtDynGridLayout *legendLayout = qobject_cast( contentsWidget()->layout() ); if ( legendLayout == NULL ) return; int left, right, top, bottom; getContentsMargins( &left, &top, &right, &bottom ); QRect layoutRect; layoutRect.setLeft( qCeil( rect.left() ) + left ); layoutRect.setTop( qCeil( rect.top() ) + top ); layoutRect.setRight( qFloor( rect.right() ) - right ); layoutRect.setBottom( qFloor( rect.bottom() ) - bottom ); uint numCols = legendLayout->columnsForWidth( layoutRect.width() ); QList itemRects = legendLayout->layoutItems( layoutRect, numCols ); int index = 0; for ( int i = 0; i < legendLayout->count(); i++ ) { QLayoutItem *item = legendLayout->itemAt( i ); QWidget *w = item->widget(); if ( w ) { painter->save(); painter->setClipRect( itemRects[index], Qt::IntersectClip ); renderItem( painter, w, itemRects[index], fillBackground ); index++; painter->restore(); } } } /*! Render a legend entry into a given rectangle. \param painter Painter \param widget Widget representing a legend entry \param rect Bounding rectangle \param fillBackground When true, fill rect with the widget background \note When widget is not derived from QwtLegendLabel renderItem does nothing beside the background */ void QwtLegend::renderItem( QPainter *painter, const QWidget *widget, const QRectF &rect, bool fillBackground ) const { if ( fillBackground ) { if ( widget->autoFillBackground() || widget->testAttribute( Qt::WA_StyledBackground ) ) { QwtPainter::drawBackgound( painter, rect, widget ); } } const QwtLegendLabel *label = qobject_cast( widget ); if ( label ) { // icon const QwtGraphic &icon = label->data().icon(); const QSizeF sz = icon.defaultSize(); const QRectF iconRect( rect.x() + label->margin(), rect.center().y() - 0.5 * sz.height(), sz.width(), sz.height() ); icon.render( painter, iconRect, Qt::KeepAspectRatio ); // title QRectF titleRect = rect; titleRect.setX( iconRect.right() + 2 * label->spacing() ); painter->setFont( label->font() ); painter->setPen( label->palette().color( QPalette::Text ) ); const_cast< QwtLegendLabel *>( label )->drawText( painter, titleRect ); } } /*! \return List of widgets associated to a item \param itemInfo Info about an item \sa legendWidget(), itemInfo(), QwtPlot::itemToInfo() */ QList QwtLegend::legendWidgets( const QVariant &itemInfo ) const { return d_data->itemMap.legendWidgets( itemInfo ); } /*! \return First widget in the list of widgets associated to an item \param itemInfo Info about an item \sa itemInfo(), QwtPlot::itemToInfo() \note Almost all types of items have only one widget */ QWidget *QwtLegend::legendWidget( const QVariant &itemInfo ) const { const QList list = d_data->itemMap.legendWidgets( itemInfo ); if ( list.isEmpty() ) return NULL; return list[0]; } /*! Find the item that is associated to a widget \param widget Widget on the legend \return Associated item info \sa legendWidget() */ QVariant QwtLegend::itemInfo( const QWidget *widget ) const { return d_data->itemMap.itemInfo( widget ); } //! \return True, when no item is inserted bool QwtLegend::isEmpty() const { return d_data->itemMap.isEmpty(); } /*! Return the extent, that is needed for the scrollbars \param orientation Orientation ( \return The width of the vertical scrollbar for Qt::Horizontal and v.v. */ int QwtLegend::scrollExtent( Qt::Orientation orientation ) const { int extent = 0; if ( orientation == Qt::Horizontal ) extent = verticalScrollBar()->sizeHint().width(); else extent = horizontalScrollBar()->sizeHint().height(); return extent; } connectome-workbench-1.2.3+git41-gc4c6c90/src/Qwt/qwt_legend.h000066400000000000000000000065621300200146000236320ustar00rootroot00000000000000/* -*- mode: C++ ; c-file-style: "stroustrup" -*- ***************************** * Qwt Widget Library * Copyright (C) 1997 Josef Wilgen * Copyright (C) 2002 Uwe Rathmann * * This library is free software; you can redistribute it and/or * modify it under the terms of the Qwt License, Version 1.0 *****************************************************************************/ #ifndef QWT_LEGEND_H #define QWT_LEGEND_H #include "qwt_global.h" #include "qwt_abstract_legend.h" #include class QScrollBar; /*! \brief The legend widget The QwtLegend widget is a tabular arrangement of legend items. Legend items might be any type of widget, but in general they will be a QwtLegendLabel. \sa QwtLegendLabel, QwtPlotItem, QwtPlot */ class QWT_EXPORT QwtLegend : public QwtAbstractLegend { Q_OBJECT public: explicit QwtLegend( QWidget *parent = NULL ); virtual ~QwtLegend(); void setMaxColumns( uint numColums ); uint maxColumns() const; void setDefaultItemMode( QwtLegendData::Mode ); QwtLegendData::Mode defaultItemMode() const; QWidget *contentsWidget(); const QWidget *contentsWidget() const; QWidget *legendWidget( const QVariant & ) const; QList legendWidgets( const QVariant & ) const; QVariant itemInfo( const QWidget * ) const; virtual bool eventFilter( QObject *, QEvent * ); virtual QSize sizeHint() const; virtual int heightForWidth( int w ) const; QScrollBar *horizontalScrollBar() const; QScrollBar *verticalScrollBar() const; virtual void renderLegend( QPainter *, const QRectF &, bool fillBackground ) const; virtual void renderItem( QPainter *, const QWidget *, const QRectF &, bool fillBackground ) const; virtual bool isEmpty() const; virtual int scrollExtent( Qt::Orientation ) const; Q_SIGNALS: /*! A signal which is emitted when the user has clicked on a legend label, which is in QwtLegendData::Clickable mode. \param itemInfo Info for the item item of the selected legend item \param index Index of the legend label in the list of widgets that are associated with the plot item \note clicks are disabled as default \sa setDefaultItemMode(), defaultItemMode(), QwtPlot::itemToInfo() */ void clicked( const QVariant &itemInfo, int index ); /*! A signal which is emitted when the user has clicked on a legend label, which is in QwtLegendData::Checkable mode \param itemInfo Info for the item of the selected legend label \param index Index of the legend label in the list of widgets that are associated with the plot item \param on True when the legend label is checked \note clicks are disabled as default \sa setDefaultItemMode(), defaultItemMode(), QwtPlot::itemToInfo() */ void checked( const QVariant &itemInfo, bool on, int index ); public Q_SLOTS: virtual void updateLegend( const QVariant &, const QList & ); protected Q_SLOTS: void itemClicked(); void itemChecked( bool ); protected: virtual QWidget *createWidget( const QwtLegendData & ) const; virtual void updateWidget( QWidget *widget, const QwtLegendData &data ); private: void updateTabOrder(); class PrivateData; PrivateData *d_data; }; #endif connectome-workbench-1.2.3+git41-gc4c6c90/src/Qwt/qwt_legend_data.cpp000066400000000000000000000052521300200146000251510ustar00rootroot00000000000000/* -*- mode: C++ ; c-file-style: "stroustrup" -*- ***************************** * Qwt Widget Library * Copyright (C) 1997 Josef Wilgen * Copyright (C) 2002 Uwe Rathmann * * This library is free software; you can redistribute it and/or * modify it under the terms of the Qwt License, Version 1.0 *****************************************************************************/ #include "qwt_legend_data.h" //! Constructor QwtLegendData::QwtLegendData() { } //! Destructor QwtLegendData::~QwtLegendData() { } /*! Set the legend attributes QwtLegendData actually is a QMap with some convenience interfaces \param map Values \sa values() */ void QwtLegendData::setValues( const QMap &map ) { d_map = map; } /*! \return Legend attributes \sa setValues() */ const QMap &QwtLegendData::values() const { return d_map; } /*! \param role Attribute role \return True, when the internal map has an entry for role */ bool QwtLegendData::hasRole( int role ) const { return d_map.contains( role ); } /*! Set an attribute value \param role Attribute role \param data Attribute value \sa value() */ void QwtLegendData::setValue( int role, const QVariant &data ) { d_map[role] = data; } /*! \param role Attribute role \return Attribute value for a specific role */ QVariant QwtLegendData::value( int role ) const { if ( !d_map.contains( role ) ) return QVariant(); return d_map[role]; } //! \return True, when the internal map is empty bool QwtLegendData::isValid() const { return !d_map.isEmpty(); } //! \return Value of the TitleRole attribute QwtText QwtLegendData::title() const { QwtText text; const QVariant titleValue = value( QwtLegendData::TitleRole ); if ( titleValue.canConvert() ) { text = qvariant_cast( titleValue ); } else if ( titleValue.canConvert() ) { text.setText( qvariant_cast( titleValue ) ); } return text; } //! \return Value of the IconRole attribute QwtGraphic QwtLegendData::icon() const { const QVariant iconValue = value( QwtLegendData::IconRole ); QwtGraphic graphic; if ( iconValue.canConvert() ) { graphic = qvariant_cast( iconValue ); } return graphic; } //! \return Value of the ModeRole attribute QwtLegendData::Mode QwtLegendData::mode() const { const QVariant modeValue = value( QwtLegendData::ModeRole ); if ( modeValue.canConvert() ) { const int mode = qvariant_cast( modeValue ); return static_cast( mode ); } return QwtLegendData::ReadOnly; } connectome-workbench-1.2.3+git41-gc4c6c90/src/Qwt/qwt_legend_data.h000066400000000000000000000042231300200146000246130ustar00rootroot00000000000000/* -*- mode: C++ ; c-file-style: "stroustrup" -*- ***************************** * Qwt Widget Library * Copyright (C) 1997 Josef Wilgen * Copyright (C) 2002 Uwe Rathmann * * This library is free software; you can redistribute it and/or * modify it under the terms of the Qwt License, Version 1.0 *****************************************************************************/ #ifndef QWT_LEGEND_DATA_H #define QWT_LEGEND_DATA_H #include "qwt_global.h" #include "qwt_text.h" #include "qwt_graphic.h" #include #include #include /*! \brief Attributes of an entry on a legend QwtLegendData is an abstract container ( like QAbstractModel ) to exchange attributes, that are only known between to the plot item and the legend. By overloading QwtPlotItem::legendData() any other set of attributes could be used, that can be handled by a modified ( or completely different ) implementation of a legend. \sa QwtLegend, QwtPlotLegendItem \note The stockchart example implements a legend as a tree with checkable items */ class QWT_EXPORT QwtLegendData { public: //! Mode defining how a legend entry interacts enum Mode { //! The legend item is not interactive, like a label ReadOnly, //! The legend item is clickable, like a push button Clickable, //! The legend item is checkable, like a checkable button Checkable }; //! Identifier how to interprete a QVariant enum Role { // The value is a Mode ModeRole, // The value is a title TitleRole, // The value is an icon IconRole, // Values < UserRole are reserved for internal use UserRole = 32 }; QwtLegendData(); ~QwtLegendData(); void setValues( const QMap & ); const QMap &values() const; void setValue( int role, const QVariant & ); QVariant value( int role ) const; bool hasRole( int role ) const; bool isValid() const; QwtGraphic icon() const; QwtText title() const; Mode mode() const; private: QMap d_map; }; #endif connectome-workbench-1.2.3+git41-gc4c6c90/src/Qwt/qwt_legend_label.cpp000066400000000000000000000216421300200146000253200ustar00rootroot00000000000000/* -*- mode: C++ ; c-file-style: "stroustrup" -*- ***************************** * Qwt Widget Library * Copyright (C) 1997 Josef Wilgen * Copyright (C) 2002 Uwe Rathmann * * This library is free software; you can redistribute it and/or * modify it under the terms of the Qwt License, Version 1.0 *****************************************************************************/ #include "qwt_legend_label.h" #include "qwt_legend_data.h" #include "qwt_math.h" #include "qwt_painter.h" #include "qwt_symbol.h" #include "qwt_graphic.h" #include #include #include #include #include #include #include static const int ButtonFrame = 2; static const int Margin = 2; static QSize buttonShift( const QwtLegendLabel *w ) { QStyleOption option; option.init( w ); const int ph = w->style()->pixelMetric( QStyle::PM_ButtonShiftHorizontal, &option, w ); const int pv = w->style()->pixelMetric( QStyle::PM_ButtonShiftVertical, &option, w ); return QSize( ph, pv ); } class QwtLegendLabel::PrivateData { public: PrivateData(): itemMode( QwtLegendData::ReadOnly ), isDown( false ), spacing( Margin ) { } QwtLegendData::Mode itemMode; QwtLegendData legendData; bool isDown; QPixmap icon; int spacing; }; /*! Set the attributes of the legend label \param legendData Attributes of the label \sa data() */ void QwtLegendLabel::setData( const QwtLegendData &legendData ) { d_data->legendData = legendData; const bool doUpdate = updatesEnabled(); setUpdatesEnabled( false ); setText( legendData.title() ); setIcon( legendData.icon().toPixmap() ); if ( legendData.hasRole( QwtLegendData::ModeRole ) ) setItemMode( legendData.mode() ); if ( doUpdate ) { setUpdatesEnabled( true ); update(); } } /*! \return Attributes of the label \sa setData(), QwtPlotItem::legendData() */ const QwtLegendData &QwtLegendLabel::data() const { return d_data->legendData; } /*! \param parent Parent widget */ QwtLegendLabel::QwtLegendLabel( QWidget *parent ): QwtTextLabel( parent ) { d_data = new PrivateData; setMargin( Margin ); setIndent( Margin ); } //! Destructor QwtLegendLabel::~QwtLegendLabel() { delete d_data; d_data = NULL; } /*! Set the text to the legend item \param text Text label \sa QwtTextLabel::text() */ void QwtLegendLabel::setText( const QwtText &text ) { const int flags = Qt::AlignLeft | Qt::AlignVCenter | Qt::TextExpandTabs | Qt::TextWordWrap; QwtText txt = text; txt.setRenderFlags( flags ); QwtTextLabel::setText( txt ); } /*! Set the item mode The default is QwtLegendData::ReadOnly \param mode Item mode \sa itemMode() */ void QwtLegendLabel::setItemMode( QwtLegendData::Mode mode ) { if ( mode != d_data->itemMode ) { d_data->itemMode = mode; d_data->isDown = false; setFocusPolicy( ( mode != QwtLegendData::ReadOnly ) ? Qt::TabFocus : Qt::NoFocus ); setMargin( ButtonFrame + Margin ); updateGeometry(); } } /*! \return Item mode \sa setItemMode() */ QwtLegendData::Mode QwtLegendLabel::itemMode() const { return d_data->itemMode; } /*! Assign the icon \param icon Pixmap representing a plot item \sa icon(), QwtPlotItem::legendIcon() */ void QwtLegendLabel::setIcon( const QPixmap &icon ) { d_data->icon = icon; int indent = margin() + d_data->spacing; if ( icon.width() > 0 ) indent += icon.width() + d_data->spacing; setIndent( indent ); } /*! \return Pixmap representing a plot item \sa setIcon() */ QPixmap QwtLegendLabel::icon() const { return d_data->icon; } /*! \brief Change the spacing between icon and text \param spacing Spacing \sa spacing(), QwtTextLabel::margin() */ void QwtLegendLabel::setSpacing( int spacing ) { spacing = qMax( spacing, 0 ); if ( spacing != d_data->spacing ) { d_data->spacing = spacing; int indent = margin() + d_data->spacing; if ( d_data->icon.width() > 0 ) indent += d_data->icon.width() + d_data->spacing; setIndent( indent ); } } /*! \return Spacing between icon and text \sa setSpacing(), QwtTextLabel::margin() */ int QwtLegendLabel::spacing() const { return d_data->spacing; } /*! Check/Uncheck a the item \param on check/uncheck \sa setItemMode() */ void QwtLegendLabel::setChecked( bool on ) { if ( d_data->itemMode == QwtLegendData::Checkable ) { const bool isBlocked = signalsBlocked(); blockSignals( true ); setDown( on ); blockSignals( isBlocked ); } } //! Return true, if the item is checked bool QwtLegendLabel::isChecked() const { return d_data->itemMode == QwtLegendData::Checkable && isDown(); } //! Set the item being down void QwtLegendLabel::setDown( bool down ) { if ( down == d_data->isDown ) return; d_data->isDown = down; update(); if ( d_data->itemMode == QwtLegendData::Clickable ) { if ( d_data->isDown ) Q_EMIT pressed(); else { Q_EMIT released(); Q_EMIT clicked(); } } if ( d_data->itemMode == QwtLegendData::Checkable ) Q_EMIT checked( d_data->isDown ); } //! Return true, if the item is down bool QwtLegendLabel::isDown() const { return d_data->isDown; } //! Return a size hint QSize QwtLegendLabel::sizeHint() const { QSize sz = QwtTextLabel::sizeHint(); sz.setHeight( qMax( sz.height(), d_data->icon.height() + 4 ) ); if ( d_data->itemMode != QwtLegendData::ReadOnly ) { sz += buttonShift( this ); sz = sz.expandedTo( QApplication::globalStrut() ); } return sz; } //! Paint event void QwtLegendLabel::paintEvent( QPaintEvent *e ) { const QRect cr = contentsRect(); QPainter painter( this ); painter.setClipRegion( e->region() ); if ( d_data->isDown ) { qDrawWinButton( &painter, 0, 0, width(), height(), palette(), true ); } painter.save(); if ( d_data->isDown ) { const QSize shiftSize = buttonShift( this ); painter.translate( shiftSize.width(), shiftSize.height() ); } painter.setClipRect( cr ); drawContents( &painter ); if ( !d_data->icon.isNull() ) { QRect iconRect = cr; iconRect.setX( iconRect.x() + margin() ); if ( d_data->itemMode != QwtLegendData::ReadOnly ) iconRect.setX( iconRect.x() + ButtonFrame ); iconRect.setSize( d_data->icon.size() ); iconRect.moveCenter( QPoint( iconRect.center().x(), cr.center().y() ) ); painter.drawPixmap( iconRect, d_data->icon ); } painter.restore(); } //! Handle mouse press events void QwtLegendLabel::mousePressEvent( QMouseEvent *e ) { if ( e->button() == Qt::LeftButton ) { switch ( d_data->itemMode ) { case QwtLegendData::Clickable: { setDown( true ); return; } case QwtLegendData::Checkable: { setDown( !isDown() ); return; } default:; } } QwtTextLabel::mousePressEvent( e ); } //! Handle mouse release events void QwtLegendLabel::mouseReleaseEvent( QMouseEvent *e ) { if ( e->button() == Qt::LeftButton ) { switch ( d_data->itemMode ) { case QwtLegendData::Clickable: { setDown( false ); return; } case QwtLegendData::Checkable: { return; // do nothing, but accept } default:; } } QwtTextLabel::mouseReleaseEvent( e ); } //! Handle key press events void QwtLegendLabel::keyPressEvent( QKeyEvent *e ) { if ( e->key() == Qt::Key_Space ) { switch ( d_data->itemMode ) { case QwtLegendData::Clickable: { if ( !e->isAutoRepeat() ) setDown( true ); return; } case QwtLegendData::Checkable: { if ( !e->isAutoRepeat() ) setDown( !isDown() ); return; } default:; } } QwtTextLabel::keyPressEvent( e ); } //! Handle key release events void QwtLegendLabel::keyReleaseEvent( QKeyEvent *e ) { if ( e->key() == Qt::Key_Space ) { switch ( d_data->itemMode ) { case QwtLegendData::Clickable: { if ( !e->isAutoRepeat() ) setDown( false ); return; } case QwtLegendData::Checkable: { return; // do nothing, but accept } default:; } } QwtTextLabel::keyReleaseEvent( e ); } connectome-workbench-1.2.3+git41-gc4c6c90/src/Qwt/qwt_legend_label.h000066400000000000000000000036601300200146000247650ustar00rootroot00000000000000/* -*- mode: C++ ; c-file-style: "stroustrup" -*- ***************************** * Qwt Widget Library * Copyright (C) 1997 Josef Wilgen * Copyright (C) 2002 Uwe Rathmann * * This library is free software; you can redistribute it and/or * modify it under the terms of the Qwt License, Version 1.0 *****************************************************************************/ #ifndef QWT_LEGEND_LABEL_H #define QWT_LEGEND_LABEL_H #include "qwt_global.h" #include "qwt_legend_data.h" #include "qwt_text.h" #include "qwt_text_label.h" #include class QwtLegendData; /*! \brief A widget representing something on a QwtLegend. */ class QWT_EXPORT QwtLegendLabel: public QwtTextLabel { Q_OBJECT public: explicit QwtLegendLabel( QWidget *parent = 0 ); virtual ~QwtLegendLabel(); void setData( const QwtLegendData & ); const QwtLegendData &data() const; void setItemMode( QwtLegendData::Mode ); QwtLegendData::Mode itemMode() const; void setSpacing( int spacing ); int spacing() const; virtual void setText( const QwtText & ); void setIcon( const QPixmap & ); QPixmap icon() const; virtual QSize sizeHint() const; bool isChecked() const; public Q_SLOTS: void setChecked( bool on ); Q_SIGNALS: //! Signal, when the legend item has been clicked void clicked(); //! Signal, when the legend item has been pressed void pressed(); //! Signal, when the legend item has been released void released(); //! Signal, when the legend item has been toggled void checked( bool ); protected: void setDown( bool ); bool isDown() const; virtual void paintEvent( QPaintEvent * ); virtual void mousePressEvent( QMouseEvent * ); virtual void mouseReleaseEvent( QMouseEvent * ); virtual void keyPressEvent( QKeyEvent * ); virtual void keyReleaseEvent( QKeyEvent * ); private: class PrivateData; PrivateData *d_data; }; #endif connectome-workbench-1.2.3+git41-gc4c6c90/src/Qwt/qwt_magnifier.cpp000066400000000000000000000270471300200146000246710ustar00rootroot00000000000000/* -*- mode: C++ ; c-file-style: "stroustrup" -*- ***************************** * Qwt Widget Library * Copyright (C) 1997 Josef Wilgen * Copyright (C) 2002 Uwe Rathmann * * This library is free software; you can redistribute it and/or * modify it under the terms of the Qwt License, Version 1.0 *****************************************************************************/ #include "qwt_magnifier.h" #include "qwt_math.h" #include #include class QwtMagnifier::PrivateData { public: PrivateData(): isEnabled( false ), wheelFactor( 0.9 ), wheelModifiers( Qt::NoModifier ), mouseFactor( 0.95 ), mouseButton( Qt::RightButton ), mouseButtonModifiers( Qt::NoModifier ), keyFactor( 0.9 ), zoomInKey( Qt::Key_Plus ), zoomInKeyModifiers( Qt::NoModifier ), zoomOutKey( Qt::Key_Minus ), zoomOutKeyModifiers( Qt::NoModifier ), mousePressed( false ) { } bool isEnabled; double wheelFactor; Qt::KeyboardModifiers wheelModifiers; double mouseFactor; Qt::MouseButton mouseButton; Qt::KeyboardModifiers mouseButtonModifiers; double keyFactor; int zoomInKey; Qt::KeyboardModifiers zoomInKeyModifiers; int zoomOutKey; Qt::KeyboardModifiers zoomOutKeyModifiers; bool mousePressed; bool hasMouseTracking; QPoint mousePos; }; /*! Constructor \param parent Widget to be magnified */ QwtMagnifier::QwtMagnifier( QWidget *parent ): QObject( parent ) { d_data = new PrivateData(); setEnabled( true ); } //! Destructor QwtMagnifier::~QwtMagnifier() { delete d_data; } /*! \brief En/disable the magnifier When enabled is true an event filter is installed for the observed widget, otherwise the event filter is removed. \param on true or false \sa isEnabled(), eventFilter() */ void QwtMagnifier::setEnabled( bool on ) { if ( d_data->isEnabled != on ) { d_data->isEnabled = on; QObject *o = parent(); if ( o ) { if ( d_data->isEnabled ) o->installEventFilter( this ); else o->removeEventFilter( this ); } } } /*! \return true when enabled, false otherwise \sa setEnabled(), eventFilter() */ bool QwtMagnifier::isEnabled() const { return d_data->isEnabled; } /*! \brief Change the wheel factor The wheel factor defines the ratio between the current range on the parent widget and the zoomed range for each step of the wheel. Use values > 1 for magnification (i.e. 2.0) and values < 1 for scaling down (i.e. 1/2.0 = 0.5). You can use this feature for inverting the direction of the wheel. The default value is 0.9. \param factor Wheel factor \sa wheelFactor(), setWheelButtonState(), setMouseFactor(), setKeyFactor() */ void QwtMagnifier::setWheelFactor( double factor ) { d_data->wheelFactor = factor; } /*! \return Wheel factor \sa setWheelFactor() */ double QwtMagnifier::wheelFactor() const { return d_data->wheelFactor; } /*! Assign keyboard modifiers for zooming in/out using the wheel. The default modifiers are Qt::NoModifiers. \param modifiers Keyboard modifiers \sa wheelModifiers() */ void QwtMagnifier::setWheelModifiers( Qt::KeyboardModifiers modifiers ) { d_data->wheelModifiers = modifiers; } /*! \return Wheel modifiers \sa setWheelModifiers() */ Qt::KeyboardModifiers QwtMagnifier::wheelModifiers() const { return d_data->wheelModifiers; } /*! \brief Change the mouse factor The mouse factor defines the ratio between the current range on the parent widget and the zoomed range for each vertical mouse movement. The default value is 0.95. \param factor Wheel factor \sa mouseFactor(), setMouseButton(), setWheelFactor(), setKeyFactor() */ void QwtMagnifier::setMouseFactor( double factor ) { d_data->mouseFactor = factor; } /*! \return Mouse factor \sa setMouseFactor() */ double QwtMagnifier::mouseFactor() const { return d_data->mouseFactor; } /*! Assign the mouse button, that is used for zooming in/out. The default value is Qt::RightButton. \param button Button \param modifiers Keyboard modifiers \sa getMouseButton() */ void QwtMagnifier::setMouseButton( Qt::MouseButton button, Qt::KeyboardModifiers modifiers ) { d_data->mouseButton = button; d_data->mouseButtonModifiers = modifiers; } //! \sa setMouseButton() void QwtMagnifier::getMouseButton( Qt::MouseButton &button, Qt::KeyboardModifiers &modifiers ) const { button = d_data->mouseButton; modifiers = d_data->mouseButtonModifiers; } /*! \brief Change the key factor The key factor defines the ratio between the current range on the parent widget and the zoomed range for each key press of the zoom in/out keys. The default value is 0.9. \param factor Key factor \sa keyFactor(), setZoomInKey(), setZoomOutKey(), setWheelFactor, setMouseFactor() */ void QwtMagnifier::setKeyFactor( double factor ) { d_data->keyFactor = factor; } /*! \return Key factor \sa setKeyFactor() */ double QwtMagnifier::keyFactor() const { return d_data->keyFactor; } /*! Assign the key, that is used for zooming in. The default combination is Qt::Key_Plus + Qt::NoModifier. \param key \param modifiers \sa getZoomInKey(), setZoomOutKey() */ void QwtMagnifier::setZoomInKey( int key, Qt::KeyboardModifiers modifiers ) { d_data->zoomInKey = key; d_data->zoomInKeyModifiers = modifiers; } /*! \brief Retrieve the settings of the zoom in key \param key Key code, see Qt::Key \param modifiers Keyboard modifiers \sa setZoomInKey() */ void QwtMagnifier::getZoomInKey( int &key, Qt::KeyboardModifiers &modifiers ) const { key = d_data->zoomInKey; modifiers = d_data->zoomInKeyModifiers; } /*! Assign the key, that is used for zooming out. The default combination is Qt::Key_Minus + Qt::NoModifier. \param key \param modifiers \sa getZoomOutKey(), setZoomOutKey() */ void QwtMagnifier::setZoomOutKey( int key, Qt::KeyboardModifiers modifiers ) { d_data->zoomOutKey = key; d_data->zoomOutKeyModifiers = modifiers; } /*! \brief Retrieve the settings of the zoom out key \param key Key code, see Qt::Key \param modifiers Keyboard modifiers \sa setZoomOutKey() */ void QwtMagnifier::getZoomOutKey( int &key, Qt::KeyboardModifiers &modifiers ) const { key = d_data->zoomOutKey; modifiers = d_data->zoomOutKeyModifiers; } /*! \brief Event filter When isEnabled() is true, the mouse events of the observed widget are filtered. \param object Object to be filtered \param event Event \return Forwarded to QObject::eventFilter() \sa widgetMousePressEvent(), widgetMouseReleaseEvent(), widgetMouseMoveEvent(), widgetWheelEvent(), widgetKeyPressEvent() widgetKeyReleaseEvent() */ bool QwtMagnifier::eventFilter( QObject *object, QEvent *event ) { if ( object && object == parent() ) { switch ( event->type() ) { case QEvent::MouseButtonPress: { widgetMousePressEvent( static_cast( event ) ); break; } case QEvent::MouseMove: { widgetMouseMoveEvent( static_cast( event ) ); break; } case QEvent::MouseButtonRelease: { widgetMouseReleaseEvent( static_cast( event ) ); break; } case QEvent::Wheel: { widgetWheelEvent( static_cast( event ) ); break; } case QEvent::KeyPress: { widgetKeyPressEvent( static_cast( event ) ); break; } case QEvent::KeyRelease: { widgetKeyReleaseEvent( static_cast( event ) ); break; } default:; } } return QObject::eventFilter( object, event ); } /*! Handle a mouse press event for the observed widget. \param mouseEvent Mouse event \sa eventFilter(), widgetMouseReleaseEvent(), widgetMouseMoveEvent() */ void QwtMagnifier::widgetMousePressEvent( QMouseEvent *mouseEvent ) { if ( parentWidget() == NULL ) return; if ( ( mouseEvent->button() != d_data->mouseButton ) || ( mouseEvent->modifiers() != d_data->mouseButtonModifiers ) ) { return; } d_data->hasMouseTracking = parentWidget()->hasMouseTracking(); parentWidget()->setMouseTracking( true ); d_data->mousePos = mouseEvent->pos(); d_data->mousePressed = true; } /*! Handle a mouse release event for the observed widget. \param mouseEvent Mouse event \sa eventFilter(), widgetMousePressEvent(), widgetMouseMoveEvent(), */ void QwtMagnifier::widgetMouseReleaseEvent( QMouseEvent *mouseEvent ) { Q_UNUSED( mouseEvent ); if ( d_data->mousePressed && parentWidget() ) { d_data->mousePressed = false; parentWidget()->setMouseTracking( d_data->hasMouseTracking ); } } /*! Handle a mouse move event for the observed widget. \param mouseEvent Mouse event \sa eventFilter(), widgetMousePressEvent(), widgetMouseReleaseEvent(), */ void QwtMagnifier::widgetMouseMoveEvent( QMouseEvent *mouseEvent ) { if ( !d_data->mousePressed ) return; const int dy = mouseEvent->pos().y() - d_data->mousePos.y(); if ( dy != 0 ) { double f = d_data->mouseFactor; if ( dy < 0 ) f = 1 / f; rescale( f ); } d_data->mousePos = mouseEvent->pos(); } /*! Handle a wheel event for the observed widget. \param wheelEvent Wheel event \sa eventFilter() */ void QwtMagnifier::widgetWheelEvent( QWheelEvent *wheelEvent ) { if ( wheelEvent->modifiers() != d_data->wheelModifiers ) { return; } if ( d_data->wheelFactor != 0.0 ) { /* A positive delta indicates that the wheel was rotated forwards away from the user; a negative value indicates that the wheel was rotated backwards toward the user. Most mouse types work in steps of 15 degrees, in which case the delta value is a multiple of 120 (== 15 * 8). */ double f = qPow( d_data->wheelFactor, qAbs( wheelEvent->delta() / 120.0 ) ); if ( wheelEvent->delta() > 0 ) f = 1 / f; rescale( f ); } } /*! Handle a key press event for the observed widget. \param keyEvent Key event \sa eventFilter(), widgetKeyReleaseEvent() */ void QwtMagnifier::widgetKeyPressEvent( QKeyEvent *keyEvent ) { if ( keyEvent->key() == d_data->zoomInKey && keyEvent->modifiers() == d_data->zoomInKeyModifiers ) { rescale( d_data->keyFactor ); } else if ( keyEvent->key() == d_data->zoomOutKey && keyEvent->modifiers() == d_data->zoomOutKeyModifiers ) { rescale( 1.0 / d_data->keyFactor ); } } /*! Handle a key release event for the observed widget. \param keyEvent Key event \sa eventFilter(), widgetKeyReleaseEvent() */ void QwtMagnifier::widgetKeyReleaseEvent( QKeyEvent *keyEvent ) { Q_UNUSED( keyEvent ); } //! \return Parent widget, where the rescaling happens QWidget *QwtMagnifier::parentWidget() { return qobject_cast( parent() ); } //! \return Parent widget, where the rescaling happens const QWidget *QwtMagnifier::parentWidget() const { return qobject_cast( parent() ); } connectome-workbench-1.2.3+git41-gc4c6c90/src/Qwt/qwt_magnifier.h000066400000000000000000000045721300200146000243340ustar00rootroot00000000000000/* -*- mode: C++ ; c-file-style: "stroustrup" -*- ***************************** * Qwt Widget Library * Copyright (C) 1997 Josef Wilgen * Copyright (C) 2002 Uwe Rathmann * * This library is free software; you can redistribute it and/or * modify it under the terms of the Qwt License, Version 1.0 *****************************************************************************/ #ifndef QWT_MAGNIFIER_H #define QWT_MAGNIFIER_H 1 #include "qwt_global.h" #include class QWidget; class QMouseEvent; class QWheelEvent; class QKeyEvent; /*! \brief QwtMagnifier provides zooming, by magnifying in steps. Using QwtMagnifier a plot can be zoomed in/out in steps using keys, the mouse wheel or moving a mouse button in vertical direction. */ class QWT_EXPORT QwtMagnifier: public QObject { Q_OBJECT public: explicit QwtMagnifier( QWidget * ); virtual ~QwtMagnifier(); QWidget *parentWidget(); const QWidget *parentWidget() const; void setEnabled( bool ); bool isEnabled() const; // mouse void setMouseFactor( double ); double mouseFactor() const; void setMouseButton( Qt::MouseButton, Qt::KeyboardModifiers = Qt::NoModifier ); void getMouseButton( Qt::MouseButton &, Qt::KeyboardModifiers & ) const; // mouse wheel void setWheelFactor( double ); double wheelFactor() const; void setWheelModifiers( Qt::KeyboardModifiers ); Qt::KeyboardModifiers wheelModifiers() const; // keyboard void setKeyFactor( double ); double keyFactor() const; void setZoomInKey( int key, Qt::KeyboardModifiers = Qt::NoModifier ); void getZoomInKey( int &key, Qt::KeyboardModifiers & ) const; void setZoomOutKey( int key, Qt::KeyboardModifiers = Qt::NoModifier ); void getZoomOutKey( int &key, Qt::KeyboardModifiers & ) const; virtual bool eventFilter( QObject *, QEvent * ); protected: /*! Rescale the parent widget \param factor Scale factor */ virtual void rescale( double factor ) = 0; virtual void widgetMousePressEvent( QMouseEvent * ); virtual void widgetMouseReleaseEvent( QMouseEvent * ); virtual void widgetMouseMoveEvent( QMouseEvent * ); virtual void widgetWheelEvent( QWheelEvent * ); virtual void widgetKeyPressEvent( QKeyEvent * ); virtual void widgetKeyReleaseEvent( QKeyEvent * ); private: class PrivateData; PrivateData *d_data; }; #endif connectome-workbench-1.2.3+git41-gc4c6c90/src/Qwt/qwt_math.cpp000066400000000000000000000031441300200146000236510ustar00rootroot00000000000000/* -*- mode: C++ ; c-file-style: "stroustrup" -*- ***************************** * Qwt Widget Library * Copyright (C) 1997 Josef Wilgen * Copyright (C) 2002 Uwe Rathmann * * This library is free software; you can redistribute it and/or * modify it under the terms of the Qwt License, Version 1.0 *****************************************************************************/ #include "qwt_math.h" /*! \brief Find the smallest value in an array \param array Pointer to an array \param size Array size */ double qwtGetMin( const double *array, int size ) { if ( size <= 0 ) return 0.0; double rv = array[0]; for ( int i = 1; i < size; i++ ) rv = qMin( rv, array[i] ); return rv; } /*! \brief Find the largest value in an array \param array Pointer to an array \param size Array size */ double qwtGetMax( const double *array, int size ) { if ( size <= 0 ) return 0.0; double rv = array[0]; for ( int i = 1; i < size; i++ ) rv = qMax( rv, array[i] ); return rv; } /*! \brief Normalize an angle to be int the range [0.0, 2 * PI[ \param radians Angle in radians \return Normalized angle in radians */ double qwtNormalizeRadians( double radians ) { double a = ::fmod( radians, 2.0 * M_PI ); if ( a < 0.0 ) a += 2.0 * M_PI; return a; } /*! \brief Normalize an angle to be int the range [0.0, 360.0[ \param radians Angle in degrees \return Normalized angle in degrees */ double qwtNormalizeDegrees( double degrees ) { double a = ::fmod( degrees, 360.0 ); if ( a < 0.0 ) a += 360.0; return a; } connectome-workbench-1.2.3+git41-gc4c6c90/src/Qwt/qwt_math.h000066400000000000000000000064551300200146000233260ustar00rootroot00000000000000/* -*- mode: C++ ; c-file-style: "stroustrup" -*- ***************************** * Qwt Widget Library * Copyright (C) 1997 Josef Wilgen * Copyright (C) 2002 Uwe Rathmann * * This library is free software; you can redistribute it and/or * modify it under the terms of the Qwt License, Version 1.0 *****************************************************************************/ #ifndef QWT_MATH_H #define QWT_MATH_H #include "qwt_global.h" #if defined(_MSC_VER) /* Microsoft says: Define _USE_MATH_DEFINES before including math.h to expose these macro definitions for common math constants. These are placed under an #ifdef since these commonly-defined names are not part of the C/C++ standards. */ #define _USE_MATH_DEFINES 1 #endif #include #include "qwt_global.h" #ifndef M_PI_2 // For Qt <= 4.8.4 M_PI_2 is not known by MinGW-w64 // when compiling with -std=c++11 #define M_PI_2 (1.57079632679489661923) #endif #ifndef LOG_MIN //! Minimum value for logarithmic scales #define LOG_MIN 1.0e-100 #endif #ifndef LOG_MAX //! Maximum value for logarithmic scales #define LOG_MAX 1.0e100 #endif QWT_EXPORT double qwtGetMin( const double *array, int size ); QWT_EXPORT double qwtGetMax( const double *array, int size ); QWT_EXPORT double qwtNormalizeRadians( double radians ); QWT_EXPORT double qwtNormalizeDegrees( double degrees ); /*! \brief Compare 2 values, relative to an interval Values are "equal", when : \f$\cdot value2 - value1 <= abs(intervalSize * 10e^{-6})\f$ \param value1 First value to compare \param value2 Second value to compare \param intervalSize interval size \return 0: if equal, -1: if value2 > value1, 1: if value1 > value2 */ inline int qwtFuzzyCompare( double value1, double value2, double intervalSize ) { const double eps = qAbs( 1.0e-6 * intervalSize ); if ( value2 - value1 > eps ) return -1; if ( value1 - value2 > eps ) return 1; return 0; } inline bool qwtFuzzyGreaterOrEqual( double d1, double d2 ) { return ( d1 >= d2 ) || qFuzzyCompare( d1, d2 ); } inline bool qwtFuzzyLessOrEqual( double d1, double d2 ) { return ( d1 <= d2 ) || qFuzzyCompare( d1, d2 ); } //! Return the sign inline int qwtSign( double x ) { if ( x > 0.0 ) return 1; else if ( x < 0.0 ) return ( -1 ); else return 0; } //! Return the square of a number inline double qwtSqr( double x ) { return x * x; } //! Approximation of arc tangent ( error below 0,005 radians ) inline double qwtFastAtan( double x ) { if ( x < -1.0 ) return -M_PI_2 - x / ( x * x + 0.28 ); if ( x > 1.0 ) return M_PI_2 - x / ( x * x + 0.28 ); return x / ( 1.0 + x * x * 0.28 ); } //! Approximation of arc tangent ( error below 0,005 radians ) inline double qwtFastAtan2( double y, double x ) { if ( x > 0 ) return qwtFastAtan( y / x ); if ( x < 0 ) { const double d = qwtFastAtan( y / x ); return ( y >= 0 ) ? d + M_PI : d - M_PI; } if ( y < 0.0 ) return -M_PI_2; if ( y > 0.0 ) return M_PI_2; return 0.0; } //! Translate degrees into radians inline double qwtRadians( double degrees ) { return degrees * M_PI / 180.0; } //! Translate radians into degrees inline double qwtDegrees( double degrees ) { return degrees * 180.0 / M_PI; } #endif connectome-workbench-1.2.3+git41-gc4c6c90/src/Qwt/qwt_matrix_raster_data.cpp000066400000000000000000000174771300200146000266130ustar00rootroot00000000000000/* -*- mode: C++ ; c-file-style: "stroustrup" -*- ***************************** * Qwt Widget Library * Copyright (C) 1997 Josef Wilgen * Copyright (C) 2002 Uwe Rathmann * * This library is free software; you can redistribute it and/or * modify it under the terms of the Qwt License, Version 1.0 *****************************************************************************/ #include "qwt_matrix_raster_data.h" #include #include class QwtMatrixRasterData::PrivateData { public: PrivateData(): resampleMode(QwtMatrixRasterData::NearestNeighbour), numColumns(0) { } inline double value(int row, int col) const { return values.data()[ row * numColumns + col ]; } QwtMatrixRasterData::ResampleMode resampleMode; QVector values; int numColumns; int numRows; double dx; double dy; }; //! Constructor QwtMatrixRasterData::QwtMatrixRasterData() { d_data = new PrivateData(); update(); } //! Destructor QwtMatrixRasterData::~QwtMatrixRasterData() { delete d_data; } /*! \brief Set the resampling algorithm \param mode Resampling mode \sa resampleMode(), value() */ void QwtMatrixRasterData::setResampleMode( ResampleMode mode ) { d_data->resampleMode = mode; } /*! \return resampling algorithm \sa setResampleMode(), value() */ QwtMatrixRasterData::ResampleMode QwtMatrixRasterData::resampleMode() const { return d_data->resampleMode; } /*! \brief Assign the bounding interval for an axis Setting the bounding intervals for the X/Y axis is mandatory to define the positions for the values of the value matrix. The interval in Z direction defines the possible range for the values in the matrix, what is f.e used by QwtPlotSpectrogram to map values to colors. The Z-interval might be the bounding interval of the values in the matrix, but usually it isn't. ( f.e a interval of 0.0-100.0 for values in percentage ) \param axis X, Y or Z axis \param interval Interval \sa QwtRasterData::interval(), setValueMatrix() */ void QwtMatrixRasterData::setInterval( Qt::Axis axis, const QwtInterval &interval ) { QwtRasterData::setInterval( axis, interval ); update(); } /*! \brief Assign a value matrix The positions of the values are calculated by dividing the bounding rectangle of the X/Y intervals into equidistant rectangles ( pixels ). Each value corresponds to the center of a pixel. \param values Vector of values \param numColumns Number of columns \sa valueMatrix(), numColumns(), numRows(), setInterval()() */ void QwtMatrixRasterData::setValueMatrix( const QVector &values, int numColumns ) { d_data->values = values; d_data->numColumns = qMax( numColumns, 0 ); update(); } /*! \return Value matrix \sa setValueMatrix(), numColumns(), numRows(), setInterval() */ const QVector QwtMatrixRasterData::valueMatrix() const { return d_data->values; } /*! \brief Change a single value in the matrix \param row Row index \param col Column index \param value New value \sa value(), setValueMatrix() */ void QwtMatrixRasterData::setValue( int row, int col, double value ) { if ( row >= 0 && row < d_data->numRows && col >= 0 && col < d_data->numColumns ) { const int index = row * d_data->numColumns + col; d_data->values.data()[ index ] = value; } } /*! \return Number of columns of the value matrix \sa valueMatrix(), numRows(), setValueMatrix() */ int QwtMatrixRasterData::numColumns() const { return d_data->numColumns; } /*! \return Number of rows of the value matrix \sa valueMatrix(), numColumns(), setValueMatrix() */ int QwtMatrixRasterData::numRows() const { return d_data->numRows; } /*! \brief Calculate the pixel hint pixelHint() returns the geometry of a pixel, that can be used to calculate the resolution and alignment of the plot item, that is representing the data. - NearestNeighbour\n pixelHint() returns the surrounding pixel of the top left value in the matrix. - BilinearInterpolation\n Returns an empty rectangle recommending to render in target device ( f.e. screen ) resolution. \param area Requested area, ignored \return Calculated hint \sa ResampleMode, setMatrix(), setInterval() */ QRectF QwtMatrixRasterData::pixelHint( const QRectF &area ) const { Q_UNUSED( area ) QRectF rect; if ( d_data->resampleMode == NearestNeighbour ) { const QwtInterval intervalX = interval( Qt::XAxis ); const QwtInterval intervalY = interval( Qt::YAxis ); if ( intervalX.isValid() && intervalY.isValid() ) { rect = QRectF( intervalX.minValue(), intervalY.minValue(), d_data->dx, d_data->dy ); } } return rect; } /*! \return the value at a raster position \param x X value in plot coordinates \param y Y value in plot coordinates \sa ResampleMode */ double QwtMatrixRasterData::value( double x, double y ) const { const QwtInterval xInterval = interval( Qt::XAxis ); const QwtInterval yInterval = interval( Qt::YAxis ); if ( !( xInterval.contains(x) && yInterval.contains(y) ) ) return qQNaN(); double value; switch( d_data->resampleMode ) { case BilinearInterpolation: { int col1 = qRound( (x - xInterval.minValue() ) / d_data->dx ) - 1; int row1 = qRound( (y - yInterval.minValue() ) / d_data->dy ) - 1; int col2 = col1 + 1; int row2 = row1 + 1; if ( col1 < 0 ) col1 = col2; else if ( col2 >= static_cast( d_data->numColumns ) ) col2 = col1; if ( row1 < 0 ) row1 = row2; else if ( row2 >= static_cast( d_data->numRows ) ) row2 = row1; const double v11 = d_data->value( row1, col1 ); const double v21 = d_data->value( row1, col2 ); const double v12 = d_data->value( row2, col1 ); const double v22 = d_data->value( row2, col2 ); const double x2 = xInterval.minValue() + ( col2 + 0.5 ) * d_data->dx; const double y2 = yInterval.minValue() + ( row2 + 0.5 ) * d_data->dy; const double rx = ( x2 - x ) / d_data->dx; const double ry = ( y2 - y ) / d_data->dy; const double vr1 = rx * v11 + ( 1.0 - rx ) * v21; const double vr2 = rx * v12 + ( 1.0 - rx ) * v22; value = ry * vr1 + ( 1.0 - ry ) * vr2; break; } case NearestNeighbour: default: { int row = int( (y - yInterval.minValue() ) / d_data->dy ); int col = int( (x - xInterval.minValue() ) / d_data->dx ); // In case of intervals, where the maximum is included // we get out of bound for row/col, when the value for the // maximum is requested. Instead we return the value // from the last row/col if ( row >= d_data->numRows ) row = d_data->numRows - 1; if ( col >= d_data->numColumns ) col = d_data->numColumns - 1; value = d_data->value( row, col ); } } return value; } void QwtMatrixRasterData::update() { d_data->numRows = 0; d_data->dx = 0.0; d_data->dy = 0.0; if ( d_data->numColumns > 0 ) { d_data->numRows = d_data->values.size() / d_data->numColumns; const QwtInterval xInterval = interval( Qt::XAxis ); const QwtInterval yInterval = interval( Qt::YAxis ); if ( xInterval.isValid() ) d_data->dx = xInterval.width() / d_data->numColumns; if ( yInterval.isValid() ) d_data->dy = yInterval.width() / d_data->numRows; } } connectome-workbench-1.2.3+git41-gc4c6c90/src/Qwt/qwt_matrix_raster_data.h000066400000000000000000000037331300200146000262460ustar00rootroot00000000000000/* -*- mode: C++ ; c-file-style: "stroustrup" -*- ***************************** * Qwt Widget Library * Copyright (C) 1997 Josef Wilgen * Copyright (C) 2002 Uwe Rathmann * * This library is free software; you can redistribute it and/or * modify it under the terms of the Qwt License, Version 1.0 *****************************************************************************/ #ifndef QWT_MATRIX_RASTER_DATA_H #define QWT_MATRIX_RASTER_DATA_H 1 #include "qwt_global.h" #include "qwt_raster_data.h" #include /*! \brief A class representing a matrix of values as raster data QwtMatrixRasterData implements an interface for a matrix of equidistant values, that can be used by a QwtPlotRasterItem. It implements a couple of resampling algorithms, to provide values for positions, that or not on the value matrix. */ class QWT_EXPORT QwtMatrixRasterData: public QwtRasterData { public: /*! \brief Resampling algorithm The default setting is NearestNeighbour; */ enum ResampleMode { /*! Return the value from the matrix, that is nearest to the the requested position. */ NearestNeighbour, /*! Interpolate the value from the distances and values of the 4 surrounding values in the matrix, */ BilinearInterpolation }; QwtMatrixRasterData(); virtual ~QwtMatrixRasterData(); void setResampleMode(ResampleMode mode); ResampleMode resampleMode() const; virtual void setInterval( Qt::Axis, const QwtInterval & ); void setValueMatrix( const QVector &values, int numColumns ); const QVector valueMatrix() const; void setValue( int row, int col, double value ); int numColumns() const; int numRows() const; virtual QRectF pixelHint( const QRectF & ) const; virtual double value( double x, double y ) const; private: void update(); class PrivateData; PrivateData *d_data; }; #endif connectome-workbench-1.2.3+git41-gc4c6c90/src/Qwt/qwt_null_paintdevice.cpp000066400000000000000000000320171300200146000262460ustar00rootroot00000000000000/* -*- mode: C++ ; c-file-style: "stroustrup" -*- ***************************** * Qwt Widget Library * Copyright (C) 1997 Josef Wilgen * Copyright (C) 2002 Uwe Rathmann * * This library is free software; you can redistribute it and/or * modify it under the terms of the Qwt License, Version 1.0 *****************************************************************************/ #include "qwt_null_paintdevice.h" #include #include class QwtNullPaintDevice::PrivateData { public: PrivateData(): mode( QwtNullPaintDevice::NormalMode ) { } QwtNullPaintDevice::Mode mode; }; class QwtNullPaintDevice::PaintEngine: public QPaintEngine { public: PaintEngine(); virtual bool begin( QPaintDevice * ); virtual bool end(); virtual Type type () const; virtual void updateState(const QPaintEngineState &); virtual void drawRects(const QRect *, int ); virtual void drawRects(const QRectF *, int ); virtual void drawLines(const QLine *, int ); virtual void drawLines(const QLineF *, int ); virtual void drawEllipse(const QRectF &); virtual void drawEllipse(const QRect &); virtual void drawPath(const QPainterPath &); virtual void drawPoints(const QPointF *, int ); virtual void drawPoints(const QPoint *, int ); virtual void drawPolygon(const QPointF *, int , PolygonDrawMode ); virtual void drawPolygon(const QPoint *, int , PolygonDrawMode ); virtual void drawPixmap(const QRectF &, const QPixmap &, const QRectF &); virtual void drawTextItem(const QPointF &, const QTextItem &); virtual void drawTiledPixmap(const QRectF &, const QPixmap &, const QPointF &s); virtual void drawImage(const QRectF &, const QImage &, const QRectF &, Qt::ImageConversionFlags ); private: QwtNullPaintDevice *nullDevice(); }; QwtNullPaintDevice::PaintEngine::PaintEngine(): QPaintEngine( QPaintEngine::AllFeatures ) { } bool QwtNullPaintDevice::PaintEngine::begin( QPaintDevice * ) { setActive( true ); return true; } bool QwtNullPaintDevice::PaintEngine::end() { setActive( false ); return true; } QPaintEngine::Type QwtNullPaintDevice::PaintEngine::type() const { return QPaintEngine::User; } void QwtNullPaintDevice::PaintEngine::drawRects( const QRect *rects, int rectCount) { QwtNullPaintDevice *device = nullDevice(); if ( device == NULL ) return; if ( device->mode() != QwtNullPaintDevice::NormalMode ) { QPaintEngine::drawRects( rects, rectCount ); return; } device->drawRects( rects, rectCount ); } void QwtNullPaintDevice::PaintEngine::drawRects( const QRectF *rects, int rectCount) { QwtNullPaintDevice *device = nullDevice(); if ( device == NULL ) return; if ( device->mode() != QwtNullPaintDevice::NormalMode ) { QPaintEngine::drawRects( rects, rectCount ); return; } device->drawRects( rects, rectCount ); } void QwtNullPaintDevice::PaintEngine::drawLines( const QLine *lines, int lineCount) { QwtNullPaintDevice *device = nullDevice(); if ( device == NULL ) return; if ( device->mode() != QwtNullPaintDevice::NormalMode ) { QPaintEngine::drawLines( lines, lineCount ); return; } device->drawLines( lines, lineCount ); } void QwtNullPaintDevice::PaintEngine::drawLines( const QLineF *lines, int lineCount) { QwtNullPaintDevice *device = nullDevice(); if ( device == NULL ) return; if ( device->mode() != QwtNullPaintDevice::NormalMode ) { QPaintEngine::drawLines( lines, lineCount ); return; } device->drawLines( lines, lineCount ); } void QwtNullPaintDevice::PaintEngine::drawEllipse( const QRectF &rect) { QwtNullPaintDevice *device = nullDevice(); if ( device == NULL ) return; if ( device->mode() != QwtNullPaintDevice::NormalMode ) { QPaintEngine::drawEllipse( rect ); return; } device->drawEllipse( rect ); } void QwtNullPaintDevice::PaintEngine::drawEllipse( const QRect &rect) { QwtNullPaintDevice *device = nullDevice(); if ( device == NULL ) return; if ( device->mode() != QwtNullPaintDevice::NormalMode ) { QPaintEngine::drawEllipse( rect ); return; } device->drawEllipse( rect ); } void QwtNullPaintDevice::PaintEngine::drawPath( const QPainterPath &path) { QwtNullPaintDevice *device = nullDevice(); if ( device == NULL ) return; device->drawPath( path ); } void QwtNullPaintDevice::PaintEngine::drawPoints( const QPointF *points, int pointCount) { QwtNullPaintDevice *device = nullDevice(); if ( device == NULL ) return; if ( device->mode() != QwtNullPaintDevice::NormalMode ) { QPaintEngine::drawPoints( points, pointCount ); return; } device->drawPoints( points, pointCount ); } void QwtNullPaintDevice::PaintEngine::drawPoints( const QPoint *points, int pointCount) { QwtNullPaintDevice *device = nullDevice(); if ( device == NULL ) return; if ( device->mode() != QwtNullPaintDevice::NormalMode ) { QPaintEngine::drawPoints( points, pointCount ); return; } device->drawPoints( points, pointCount ); } void QwtNullPaintDevice::PaintEngine::drawPolygon( const QPointF *points, int pointCount, PolygonDrawMode mode) { QwtNullPaintDevice *device = nullDevice(); if ( device == NULL ) return; if ( device->mode() == QwtNullPaintDevice::PathMode ) { QPainterPath path; if ( pointCount > 0 ) { path.moveTo( points[0] ); for ( int i = 1; i < pointCount; i++ ) path.lineTo( points[i] ); if ( mode != PolylineMode ) path.closeSubpath(); } device->drawPath( path ); return; } device->drawPolygon( points, pointCount, mode ); } void QwtNullPaintDevice::PaintEngine::drawPolygon( const QPoint *points, int pointCount, PolygonDrawMode mode) { QwtNullPaintDevice *device = nullDevice(); if ( device == NULL ) return; if ( device->mode() == QwtNullPaintDevice::PathMode ) { QPainterPath path; if ( pointCount > 0 ) { path.moveTo( points[0] ); for ( int i = 1; i < pointCount; i++ ) path.lineTo( points[i] ); if ( mode != PolylineMode ) path.closeSubpath(); } device->drawPath( path ); return; } device->drawPolygon( points, pointCount, mode ); } void QwtNullPaintDevice::PaintEngine::drawPixmap( const QRectF &rect, const QPixmap &pm, const QRectF &subRect ) { QwtNullPaintDevice *device = nullDevice(); if ( device == NULL ) return; device->drawPixmap( rect, pm, subRect ); } void QwtNullPaintDevice::PaintEngine::drawTextItem( const QPointF &pos, const QTextItem &textItem) { QwtNullPaintDevice *device = nullDevice(); if ( device == NULL ) return; if ( device->mode() != QwtNullPaintDevice::NormalMode ) { QPaintEngine::drawTextItem( pos, textItem ); return; } device->drawTextItem( pos, textItem ); } void QwtNullPaintDevice::PaintEngine::drawTiledPixmap( const QRectF &rect, const QPixmap &pixmap, const QPointF &subRect) { QwtNullPaintDevice *device = nullDevice(); if ( device == NULL ) return; if ( device->mode() != QwtNullPaintDevice::NormalMode ) { QPaintEngine::drawTiledPixmap( rect, pixmap, subRect ); return; } device->drawTiledPixmap( rect, pixmap, subRect ); } void QwtNullPaintDevice::PaintEngine::drawImage( const QRectF &rect, const QImage &image, const QRectF &subRect, Qt::ImageConversionFlags flags) { QwtNullPaintDevice *device = nullDevice(); if ( device == NULL ) return; device->drawImage( rect, image, subRect, flags ); } void QwtNullPaintDevice::PaintEngine::updateState( const QPaintEngineState &state) { QwtNullPaintDevice *device = nullDevice(); if ( device == NULL ) return; device->updateState( state ); } inline QwtNullPaintDevice *QwtNullPaintDevice::PaintEngine::nullDevice() { if ( !isActive() ) return NULL; return static_cast( paintDevice() ); } //! Constructor QwtNullPaintDevice::QwtNullPaintDevice(): d_engine( NULL ) { d_data = new PrivateData; } //! Destructor QwtNullPaintDevice::~QwtNullPaintDevice() { delete d_engine; delete d_data; } /*! Set the render mode \param mode New mode \sa mode() */ void QwtNullPaintDevice::setMode( Mode mode ) { d_data->mode = mode; } /*! \return Render mode \sa setMode() */ QwtNullPaintDevice::Mode QwtNullPaintDevice::mode() const { return d_data->mode; } //! See QPaintDevice::paintEngine() QPaintEngine *QwtNullPaintDevice::paintEngine() const { if ( d_engine == NULL ) { QwtNullPaintDevice *that = const_cast< QwtNullPaintDevice * >( this ); that->d_engine = new PaintEngine(); } return d_engine; } /*! See QPaintDevice::metric() \param deviceMetric Type of metric \return Metric information for the given paint device metric. \sa sizeMetrics() */ int QwtNullPaintDevice::metric( PaintDeviceMetric deviceMetric ) const { int value; switch ( deviceMetric ) { case PdmWidth: { value = sizeMetrics().width(); break; } case PdmHeight: { value = sizeMetrics().height(); break; } case PdmNumColors: { value = 0xffffffff; break; } case PdmDepth: { value = 32; break; } case PdmPhysicalDpiX: case PdmPhysicalDpiY: case PdmDpiY: case PdmDpiX: { value = 72; break; } case PdmWidthMM: { value = qRound( metric( PdmWidth ) * 25.4 / metric( PdmDpiX ) ); break; } case PdmHeightMM: { value = qRound( metric( PdmHeight ) * 25.4 / metric( PdmDpiY ) ); break; } default: value = 0; } return value; } //! See QPaintEngine::drawRects() void QwtNullPaintDevice::drawRects( const QRect *rects, int rectCount) { Q_UNUSED(rects); Q_UNUSED(rectCount); } //! See QPaintEngine::drawRects() void QwtNullPaintDevice::drawRects( const QRectF *rects, int rectCount) { Q_UNUSED(rects); Q_UNUSED(rectCount); } //! See QPaintEngine::drawLines() void QwtNullPaintDevice::drawLines( const QLine *lines, int lineCount) { Q_UNUSED(lines); Q_UNUSED(lineCount); } //! See QPaintEngine::drawLines() void QwtNullPaintDevice::drawLines( const QLineF *lines, int lineCount) { Q_UNUSED(lines); Q_UNUSED(lineCount); } //! See QPaintEngine::drawEllipse() void QwtNullPaintDevice::drawEllipse( const QRectF &rect ) { Q_UNUSED(rect); } //! See QPaintEngine::drawEllipse() void QwtNullPaintDevice::drawEllipse( const QRect &rect ) { Q_UNUSED(rect); } //! See QPaintEngine::drawPath() void QwtNullPaintDevice::drawPath( const QPainterPath &path ) { Q_UNUSED(path); } //! See QPaintEngine::drawPoints() void QwtNullPaintDevice::drawPoints( const QPointF *points, int pointCount) { Q_UNUSED(points); Q_UNUSED(pointCount); } //! See QPaintEngine::drawPoints() void QwtNullPaintDevice::drawPoints( const QPoint *points, int pointCount) { Q_UNUSED(points); Q_UNUSED(pointCount); } //! See QPaintEngine::drawPolygon() void QwtNullPaintDevice::drawPolygon( const QPointF *points, int pointCount, QPaintEngine::PolygonDrawMode mode) { Q_UNUSED(points); Q_UNUSED(pointCount); Q_UNUSED(mode); } //! See QPaintEngine::drawPolygon() void QwtNullPaintDevice::drawPolygon( const QPoint *points, int pointCount, QPaintEngine::PolygonDrawMode mode) { Q_UNUSED(points); Q_UNUSED(pointCount); Q_UNUSED(mode); } //! See QPaintEngine::drawPixmap() void QwtNullPaintDevice::drawPixmap( const QRectF &rect, const QPixmap &pm, const QRectF &subRect ) { Q_UNUSED(rect); Q_UNUSED(pm); Q_UNUSED(subRect); } //! See QPaintEngine::drawTextItem() void QwtNullPaintDevice::drawTextItem( const QPointF &pos, const QTextItem &textItem) { Q_UNUSED(pos); Q_UNUSED(textItem); } //! See QPaintEngine::drawTiledPixmap() void QwtNullPaintDevice::drawTiledPixmap( const QRectF &rect, const QPixmap &pixmap, const QPointF &subRect) { Q_UNUSED(rect); Q_UNUSED(pixmap); Q_UNUSED(subRect); } //! See QPaintEngine::drawImage() void QwtNullPaintDevice::drawImage( const QRectF &rect, const QImage &image, const QRectF &subRect, Qt::ImageConversionFlags flags) { Q_UNUSED(rect); Q_UNUSED(image); Q_UNUSED(subRect); Q_UNUSED(flags); } //! See QPaintEngine::updateState() void QwtNullPaintDevice::updateState( const QPaintEngineState &state ) { Q_UNUSED(state); } connectome-workbench-1.2.3+git41-gc4c6c90/src/Qwt/qwt_null_paintdevice.h000066400000000000000000000065431300200146000257200ustar00rootroot00000000000000/* -*- mode: C++ ; c-file-style: "stroustrup" -*- ***************************** * Qwt Widget Library * Copyright (C) 1997 Josef Wilgen * Copyright (C) 2002 Uwe Rathmann * * This library is free software; you can redistribute it and/or * modify it under the terms of the Qwt License, Version 1.0 *****************************************************************************/ #ifndef QWT_NULL_PAINT_DEVICE_H #define QWT_NULL_PAINT_DEVICE_H 1 #include "qwt_global.h" #include #include /*! \brief A null paint device doing nothing Sometimes important layout/rendering geometries are not available or changeable from the public Qt class interface. ( f.e hidden in the style implementation ). QwtNullPaintDevice can be used to manipulate or filter out this information by analyzing the stream of paint primitives. F.e. QwtNullPaintDevice is used by QwtPlotCanvas to identify styled backgrounds with rounded corners. */ class QWT_EXPORT QwtNullPaintDevice: public QPaintDevice { public: /*! \brief Render mode \sa setMode(), mode() */ enum Mode { /*! All vector graphic primitives are painted by the corresponding draw methods */ NormalMode, /*! Vector graphic primitives ( beside polygons ) are mapped to a QPainterPath and are painted by drawPath. In PathMode mode only a few draw methods are called: - drawPath() - drawPixmap() - drawImage() - drawPolygon() */ PolygonPathMode, /*! Vector graphic primitives are mapped to a QPainterPath and are painted by drawPath. In PathMode mode only a few draw methods are called: - drawPath() - drawPixmap() - drawImage() */ PathMode }; QwtNullPaintDevice(); virtual ~QwtNullPaintDevice(); void setMode( Mode ); Mode mode() const; virtual QPaintEngine *paintEngine() const; virtual int metric( PaintDeviceMetric metric ) const; virtual void drawRects(const QRect *, int ); virtual void drawRects(const QRectF *, int ); virtual void drawLines(const QLine *, int ); virtual void drawLines(const QLineF *, int ); virtual void drawEllipse(const QRectF &); virtual void drawEllipse(const QRect &); virtual void drawPath(const QPainterPath &); virtual void drawPoints(const QPointF *, int ); virtual void drawPoints(const QPoint *, int ); virtual void drawPolygon( const QPointF *, int , QPaintEngine::PolygonDrawMode ); virtual void drawPolygon( const QPoint *, int , QPaintEngine::PolygonDrawMode ); virtual void drawPixmap(const QRectF &, const QPixmap &, const QRectF &); virtual void drawTextItem(const QPointF &, const QTextItem &); virtual void drawTiledPixmap(const QRectF &, const QPixmap &, const QPointF &s); virtual void drawImage(const QRectF &, const QImage &, const QRectF &, Qt::ImageConversionFlags ); virtual void updateState( const QPaintEngineState &state ); protected: //! \return Size needed to implement metric() virtual QSize sizeMetrics() const = 0; private: class PaintEngine; PaintEngine *d_engine; class PrivateData; PrivateData *d_data; }; #endif connectome-workbench-1.2.3+git41-gc4c6c90/src/Qwt/qwt_painter.cpp000066400000000000000000001065031300200146000243650ustar00rootroot00000000000000/* -*- mode: C++ ; c-file-style: "stroustrup" -*- ***************************** * Qwt Widget Library * Copyright (C) 1997 Josef Wilgen * Copyright (C) 2002 Uwe Rathmann * * This library is free software; you can redistribute it and/or * modify it under the terms of the Qwt License, Version 1.0 *****************************************************************************/ #include "qwt_painter.h" #include "qwt_math.h" #include "qwt_clipper.h" #include "qwt_color_map.h" #include "qwt_scale_map.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #if QT_VERSION >= 0x050000 #include #endif #if QT_VERSION < 0x050000 #ifdef Q_WS_X11 #include #endif #endif bool QwtPainter::d_polylineSplitting = true; bool QwtPainter::d_roundingAlignment = true; static inline bool qwtIsClippingNeeded( const QPainter *painter, QRectF &clipRect ) { bool doClipping = false; const QPaintEngine *pe = painter->paintEngine(); if ( pe && pe->type() == QPaintEngine::SVG ) { // The SVG paint engine ignores any clipping, if ( painter->hasClipping() ) { doClipping = true; clipRect = painter->clipRegion().boundingRect(); } } return doClipping; } template static inline void qwtDrawPolyline( QPainter *painter, const T *points, int pointCount, bool polylineSplitting ) { bool doSplit = false; if ( polylineSplitting ) { const QPaintEngine *pe = painter->paintEngine(); if ( pe && pe->type() == QPaintEngine::Raster ) { /* The raster paint engine seems to use some algo with O(n*n). ( Qt 4.3 is better than Qt 4.2, but remains unacceptable) To work around this problem, we have to split the polygon into smaller pieces. */ doSplit = true; } } if ( doSplit ) { const int splitSize = 6; for ( int i = 0; i < pointCount; i += splitSize ) { const int n = qMin( splitSize + 1, pointCount - i ); painter->drawPolyline( points + i, n ); } } else { painter->drawPolyline( points, pointCount ); } } static inline QSize qwtScreenResolution() { static QSize screenResolution; if ( !screenResolution.isValid() ) { QDesktopWidget *desktop = QApplication::desktop(); if ( desktop ) { screenResolution.setWidth( desktop->logicalDpiX() ); screenResolution.setHeight( desktop->logicalDpiY() ); } } return screenResolution; } static inline void qwtUnscaleFont( QPainter *painter ) { if ( painter->font().pixelSize() >= 0 ) return; const QSize screenResolution = qwtScreenResolution(); const QPaintDevice *pd = painter->device(); if ( pd->logicalDpiX() != screenResolution.width() || pd->logicalDpiY() != screenResolution.height() ) { QFont pixelFont( painter->font(), QApplication::desktop() ); pixelFont.setPixelSize( QFontInfo( pixelFont ).pixelSize() ); painter->setFont( pixelFont ); } } /*! Check is the application is running with the X11 graphics system that has some special capabilities that can be used for incremental painting to a widget. \return True, when the graphics system is X11 */ bool QwtPainter::isX11GraphicsSystem() { static int onX11 = -1; if ( onX11 < 0 ) { QPixmap pm( 1, 1 ); QPainter painter( &pm ); onX11 = ( painter.paintEngine()->type() == QPaintEngine::X11 ) ? 1 : 0; } return onX11 == 1; } /*! Check if the painter is using a paint engine, that aligns coordinates to integers. Today these are all paint engines beside QPaintEngine::Pdf and QPaintEngine::SVG. If we have an integer based paint engine it is also checked if the painter has a transformation matrix, that rotates or scales. \param painter Painter \return true, when the painter is aligning \sa setRoundingAlignment() */ bool QwtPainter::isAligning( QPainter *painter ) { if ( painter && painter->isActive() ) { switch ( painter->paintEngine()->type() ) { case QPaintEngine::Pdf: case QPaintEngine::SVG: return false; default:; } const QTransform tr = painter->transform(); if ( tr.isRotating() || tr.isScaling() ) { // we might have to check translations too return false; } } return true; } /*! Enable whether coordinates should be rounded, before they are painted to a paint engine that floors to integer values. For other paint engines this ( PDF, SVG ), this flag has no effect. QwtPainter stores this flag only, the rounding itself is done in the painting code ( f.e the plot items ). The default setting is true. \sa roundingAlignment(), isAligning() */ void QwtPainter::setRoundingAlignment( bool enable ) { d_roundingAlignment = enable; } /*! \brief En/Disable line splitting for the raster paint engine In some Qt versions the raster paint engine paints polylines of many points much faster when they are split in smaller chunks: f.e all supported Qt versions >= Qt 5.0 when drawing an antialiased polyline with a pen width >=2. The default setting is true. \sa polylineSplitting() */ void QwtPainter::setPolylineSplitting( bool enable ) { d_polylineSplitting = enable; } //! Wrapper for QPainter::drawPath() void QwtPainter::drawPath( QPainter *painter, const QPainterPath &path ) { painter->drawPath( path ); } //! Wrapper for QPainter::drawRect() void QwtPainter::drawRect( QPainter *painter, double x, double y, double w, double h ) { drawRect( painter, QRectF( x, y, w, h ) ); } //! Wrapper for QPainter::drawRect() void QwtPainter::drawRect( QPainter *painter, const QRectF &rect ) { const QRectF r = rect; QRectF clipRect; const bool deviceClipping = qwtIsClippingNeeded( painter, clipRect ); if ( deviceClipping ) { if ( !clipRect.intersects( r ) ) return; if ( !clipRect.contains( r ) ) { fillRect( painter, r & clipRect, painter->brush() ); painter->save(); painter->setBrush( Qt::NoBrush ); drawPolyline( painter, QPolygonF( r ) ); painter->restore(); return; } } painter->drawRect( r ); } //! Wrapper for QPainter::fillRect() void QwtPainter::fillRect( QPainter *painter, const QRectF &rect, const QBrush &brush ) { if ( !rect.isValid() ) return; QRectF clipRect; const bool deviceClipping = qwtIsClippingNeeded( painter, clipRect ); /* Performance of Qt4 is horrible for a non trivial brush. Without clipping expect minutes or hours for repainting large rectangles (might result from zooming) */ if ( deviceClipping ) clipRect &= painter->window(); else clipRect = painter->window(); if ( painter->hasClipping() ) clipRect &= painter->clipRegion().boundingRect(); QRectF r = rect; if ( deviceClipping ) r = r.intersected( clipRect ); if ( r.isValid() ) painter->fillRect( r, brush ); } //! Wrapper for QPainter::drawPie() void QwtPainter::drawPie( QPainter *painter, const QRectF &rect, int a, int alen ) { QRectF clipRect; const bool deviceClipping = qwtIsClippingNeeded( painter, clipRect ); if ( deviceClipping && !clipRect.contains( rect ) ) return; painter->drawPie( rect, a, alen ); } //! Wrapper for QPainter::drawEllipse() void QwtPainter::drawEllipse( QPainter *painter, const QRectF &rect ) { QRectF clipRect; const bool deviceClipping = qwtIsClippingNeeded( painter, clipRect ); if ( deviceClipping && !clipRect.contains( rect ) ) return; painter->drawEllipse( rect ); } //! Wrapper for QPainter::drawText() void QwtPainter::drawText( QPainter *painter, double x, double y, const QString &text ) { drawText( painter, QPointF( x, y ), text ); } //! Wrapper for QPainter::drawText() void QwtPainter::drawText( QPainter *painter, const QPointF &pos, const QString &text ) { QRectF clipRect; const bool deviceClipping = qwtIsClippingNeeded( painter, clipRect ); if ( deviceClipping && !clipRect.contains( pos ) ) return; painter->save(); qwtUnscaleFont( painter ); painter->drawText( pos, text ); painter->restore(); } //! Wrapper for QPainter::drawText() void QwtPainter::drawText( QPainter *painter, double x, double y, double w, double h, int flags, const QString &text ) { drawText( painter, QRectF( x, y, w, h ), flags, text ); } //! Wrapper for QPainter::drawText() void QwtPainter::drawText( QPainter *painter, const QRectF &rect, int flags, const QString &text ) { painter->save(); qwtUnscaleFont( painter ); painter->drawText( rect, flags, text ); painter->restore(); } #ifndef QT_NO_RICHTEXT /*! Draw a text document into a rectangle \param painter Painter \param rect Traget rectangle \param flags Alignments/Text flags, see QPainter::drawText() \param text Text document */ void QwtPainter::drawSimpleRichText( QPainter *painter, const QRectF &rect, int flags, const QTextDocument &text ) { QTextDocument *txt = text.clone(); painter->save(); QRectF unscaledRect = rect; if ( painter->font().pixelSize() < 0 ) { const QSize res = qwtScreenResolution(); const QPaintDevice *pd = painter->device(); if ( pd->logicalDpiX() != res.width() || pd->logicalDpiY() != res.height() ) { QTransform transform; transform.scale( res.width() / double( pd->logicalDpiX() ), res.height() / double( pd->logicalDpiY() )); painter->setWorldTransform( transform, true ); unscaledRect = transform.inverted().mapRect(rect); } } txt->setDefaultFont( painter->font() ); txt->setPageSize( QSizeF( unscaledRect.width(), QWIDGETSIZE_MAX ) ); QAbstractTextDocumentLayout* layout = txt->documentLayout(); const double height = layout->documentSize().height(); double y = unscaledRect.y(); if ( flags & Qt::AlignBottom ) y += ( unscaledRect.height() - height ); else if ( flags & Qt::AlignVCenter ) y += ( unscaledRect.height() - height ) / 2; QAbstractTextDocumentLayout::PaintContext context; context.palette.setColor( QPalette::Text, painter->pen().color() ); painter->translate( unscaledRect.x(), y ); layout->draw( painter, context ); painter->restore(); delete txt; } #endif // !QT_NO_RICHTEXT //! Wrapper for QPainter::drawLine() void QwtPainter::drawLine( QPainter *painter, const QPointF &p1, const QPointF &p2 ) { QRectF clipRect; const bool deviceClipping = qwtIsClippingNeeded( painter, clipRect ); if ( deviceClipping && !( clipRect.contains( p1 ) && clipRect.contains( p2 ) ) ) { QPolygonF polygon; polygon += p1; polygon += p2; drawPolyline( painter, polygon ); return; } painter->drawLine( p1, p2 ); } //! Wrapper for QPainter::drawPolygon() void QwtPainter::drawPolygon( QPainter *painter, const QPolygonF &polygon ) { QRectF clipRect; const bool deviceClipping = qwtIsClippingNeeded( painter, clipRect ); QPolygonF cpa = polygon; if ( deviceClipping ) cpa = QwtClipper::clipPolygonF( clipRect, polygon ); painter->drawPolygon( cpa ); } //! Wrapper for QPainter::drawPolyline() void QwtPainter::drawPolyline( QPainter *painter, const QPolygonF &polygon ) { QRectF clipRect; const bool deviceClipping = qwtIsClippingNeeded( painter, clipRect ); QPolygonF cpa = polygon; if ( deviceClipping ) cpa = QwtClipper::clipPolygonF( clipRect, cpa ); qwtDrawPolyline( painter, cpa.constData(), cpa.size(), d_polylineSplitting ); } //! Wrapper for QPainter::drawPolyline() void QwtPainter::drawPolyline( QPainter *painter, const QPointF *points, int pointCount ) { QRectF clipRect; const bool deviceClipping = qwtIsClippingNeeded( painter, clipRect ); if ( deviceClipping ) { QPolygonF polygon( pointCount ); ::memcpy( polygon.data(), points, pointCount * sizeof( QPointF ) ); polygon = QwtClipper::clipPolygonF( clipRect, polygon ); qwtDrawPolyline( painter, polygon.constData(), polygon.size(), d_polylineSplitting ); } else { qwtDrawPolyline( painter, points, pointCount, d_polylineSplitting ); } } //! Wrapper for QPainter::drawPolygon() void QwtPainter::drawPolygon( QPainter *painter, const QPolygon &polygon ) { QRectF clipRect; const bool deviceClipping = qwtIsClippingNeeded( painter, clipRect ); QPolygon cpa = polygon; if ( deviceClipping ) cpa = QwtClipper::clipPolygon( clipRect, polygon ); painter->drawPolygon( cpa ); } //! Wrapper for QPainter::drawPolyline() void QwtPainter::drawPolyline( QPainter *painter, const QPolygon &polygon ) { QRectF clipRect; const bool deviceClipping = qwtIsClippingNeeded( painter, clipRect ); QPolygon cpa = polygon; if ( deviceClipping ) cpa = QwtClipper::clipPolygon( clipRect, cpa ); qwtDrawPolyline( painter, cpa.constData(), cpa.size(), d_polylineSplitting ); } //! Wrapper for QPainter::drawPolyline() void QwtPainter::drawPolyline( QPainter *painter, const QPoint *points, int pointCount ) { QRectF clipRect; const bool deviceClipping = qwtIsClippingNeeded( painter, clipRect ); if ( deviceClipping ) { QPolygon polygon( pointCount ); ::memcpy( polygon.data(), points, pointCount * sizeof( QPoint ) ); polygon = QwtClipper::clipPolygon( clipRect, polygon ); qwtDrawPolyline( painter, polygon.constData(), polygon.size(), d_polylineSplitting ); } else qwtDrawPolyline( painter, points, pointCount, d_polylineSplitting ); } //! Wrapper for QPainter::drawPoint() void QwtPainter::drawPoint( QPainter *painter, const QPointF &pos ) { QRectF clipRect; const bool deviceClipping = qwtIsClippingNeeded( painter, clipRect ); if ( deviceClipping && !clipRect.contains( pos ) ) return; painter->drawPoint( pos ); } //! Wrapper for QPainter::drawPoint() void QwtPainter::drawPoint( QPainter *painter, const QPoint &pos ) { QRectF clipRect; const bool deviceClipping = qwtIsClippingNeeded( painter, clipRect ); if ( deviceClipping ) { const int minX = qCeil( clipRect.left() ); const int maxX = qFloor( clipRect.right() ); const int minY = qCeil( clipRect.top() ); const int maxY = qFloor( clipRect.bottom() ); if ( pos.x() < minX || pos.x() > maxX || pos.y() < minY || pos.y() > maxY ) { return; } } painter->drawPoint( pos ); } //! Wrapper for QPainter::drawPoints() void QwtPainter::drawPoints( QPainter *painter, const QPoint *points, int pointCount ) { QRectF clipRect; const bool deviceClipping = qwtIsClippingNeeded( painter, clipRect ); if ( deviceClipping ) { const int minX = qCeil( clipRect.left() ); const int maxX = qFloor( clipRect.right() ); const int minY = qCeil( clipRect.top() ); const int maxY = qFloor( clipRect.bottom() ); const QRect r( minX, minY, maxX - minX, maxY - minY ); QPolygon clippedPolygon( pointCount ); QPoint *clippedData = clippedPolygon.data(); int numClippedPoints = 0; for ( int i = 0; i < pointCount; i++ ) { if ( r.contains( points[i] ) ) clippedData[ numClippedPoints++ ] = points[i]; } painter->drawPoints( clippedData, numClippedPoints ); } else { painter->drawPoints( points, pointCount ); } } //! Wrapper for QPainter::drawPoints() void QwtPainter::drawPoints( QPainter *painter, const QPointF *points, int pointCount ) { QRectF clipRect; const bool deviceClipping = qwtIsClippingNeeded( painter, clipRect ); if ( deviceClipping ) { QPolygonF clippedPolygon( pointCount ); QPointF *clippedData = clippedPolygon.data(); int numClippedPoints = 0; for ( int i = 0; i < pointCount; i++ ) { if ( clipRect.contains( points[i] ) ) clippedData[ numClippedPoints++ ] = points[i]; } painter->drawPoints( clippedData, numClippedPoints ); } else { painter->drawPoints( points, pointCount ); } } //! Wrapper for QPainter::drawImage() void QwtPainter::drawImage( QPainter *painter, const QRectF &rect, const QImage &image ) { const QRect alignedRect = rect.toAlignedRect(); if ( alignedRect != rect ) { const QRectF clipRect = rect.adjusted( 0.0, 0.0, -1.0, -1.0 ); painter->save(); painter->setClipRect( clipRect, Qt::IntersectClip ); painter->drawImage( alignedRect, image ); painter->restore(); } else { painter->drawImage( alignedRect, image ); } } //! Wrapper for QPainter::drawPixmap() void QwtPainter::drawPixmap( QPainter *painter, const QRectF &rect, const QPixmap &pixmap ) { const QRect alignedRect = rect.toAlignedRect(); if ( alignedRect != rect ) { const QRectF clipRect = rect.adjusted( 0.0, 0.0, -1.0, -1.0 ); painter->save(); painter->setClipRect( clipRect, Qt::IntersectClip ); painter->drawPixmap( alignedRect, pixmap ); painter->restore(); } else { painter->drawPixmap( alignedRect, pixmap ); } } //! Draw a focus rectangle on a widget using its style. void QwtPainter::drawFocusRect( QPainter *painter, const QWidget *widget ) { drawFocusRect( painter, widget, widget->rect() ); } //! Draw a focus rectangle on a widget using its style. void QwtPainter::drawFocusRect( QPainter *painter, const QWidget *widget, const QRect &rect ) { QStyleOptionFocusRect opt; opt.init( widget ); opt.rect = rect; opt.state |= QStyle::State_HasFocus; widget->style()->drawPrimitive( QStyle::PE_FrameFocusRect, &opt, painter, widget ); } /*! Draw a round frame \param painter Painter \param rect Frame rectangle \param palette QPalette::WindowText is used for plain borders QPalette::Dark and QPalette::Light for raised or sunken borders \param lineWidth Line width \param frameStyle bitwise OR´ed value of QFrame::Shape and QFrame::Shadow */ void QwtPainter::drawRoundFrame( QPainter *painter, const QRectF &rect, const QPalette &palette, int lineWidth, int frameStyle ) { enum Style { Plain, Sunken, Raised }; Style style = Plain; if ( (frameStyle & QFrame::Sunken) == QFrame::Sunken ) style = Sunken; else if ( (frameStyle & QFrame::Raised) == QFrame::Raised ) style = Raised; const double lw2 = 0.5 * lineWidth; QRectF r = rect.adjusted( lw2, lw2, -lw2, -lw2 ); QBrush brush; if ( style != Plain ) { QColor c1 = palette.color( QPalette::Light ); QColor c2 = palette.color( QPalette::Dark ); if ( style == Sunken ) qSwap( c1, c2 ); QLinearGradient gradient( r.topLeft(), r.bottomRight() ); gradient.setColorAt( 0.0, c1 ); #if 0 gradient.setColorAt( 0.3, c1 ); gradient.setColorAt( 0.7, c2 ); #endif gradient.setColorAt( 1.0, c2 ); brush = QBrush( gradient ); } else // Plain { brush = palette.brush( QPalette::WindowText ); } painter->save(); painter->setPen( QPen( brush, lineWidth ) ); painter->setBrush( Qt::NoBrush ); painter->drawEllipse( r ); painter->restore(); } /*! Draw a rectangular frame \param painter Painter \param rect Frame rectangle \param palette Palette \param foregroundRole Foreground role used for QFrame::Plain \param frameWidth Frame width \param midLineWidth Used for QFrame::Box \param frameStyle bitwise OR´ed value of QFrame::Shape and QFrame::Shadow */ void QwtPainter::drawFrame( QPainter *painter, const QRectF &rect, const QPalette &palette, QPalette::ColorRole foregroundRole, int frameWidth, int midLineWidth, int frameStyle ) { if ( frameWidth <= 0 || rect.isEmpty() ) return; const int shadow = frameStyle & QFrame::Shadow_Mask; painter->save(); if ( shadow == QFrame::Plain ) { const QRectF outerRect = rect.adjusted( 0.0, 0.0, -1.0, -1.0 ); const QRectF innerRect = outerRect.adjusted( frameWidth, frameWidth, -frameWidth, -frameWidth ); QPainterPath path; path.addRect( outerRect ); path.addRect( innerRect ); painter->setPen( Qt::NoPen ); painter->setBrush( palette.color( foregroundRole ) ); painter->drawPath( path ); } else { const int shape = frameStyle & QFrame::Shape_Mask; if ( shape == QFrame::Box ) { const QRectF outerRect = rect.adjusted( 0.0, 0.0, -1.0, -1.0 ); const QRectF midRect1 = outerRect.adjusted( frameWidth, frameWidth, -frameWidth, -frameWidth ); const QRectF midRect2 = midRect1.adjusted( midLineWidth, midLineWidth, -midLineWidth, -midLineWidth ); const QRectF innerRect = midRect2.adjusted( frameWidth, frameWidth, -frameWidth, -frameWidth ); QPainterPath path1; path1.moveTo( outerRect.bottomLeft() ); path1.lineTo( outerRect.topLeft() ); path1.lineTo( outerRect.topRight() ); path1.lineTo( midRect1.topRight() ); path1.lineTo( midRect1.topLeft() ); path1.lineTo( midRect1.bottomLeft() ); QPainterPath path2; path2.moveTo( outerRect.bottomLeft() ); path2.lineTo( outerRect.bottomRight() ); path2.lineTo( outerRect.topRight() ); path2.lineTo( midRect1.topRight() ); path2.lineTo( midRect1.bottomRight() ); path2.lineTo( midRect1.bottomLeft() ); QPainterPath path3; path3.moveTo( midRect2.bottomLeft() ); path3.lineTo( midRect2.topLeft() ); path3.lineTo( midRect2.topRight() ); path3.lineTo( innerRect.topRight() ); path3.lineTo( innerRect.topLeft() ); path3.lineTo( innerRect.bottomLeft() ); QPainterPath path4; path4.moveTo( midRect2.bottomLeft() ); path4.lineTo( midRect2.bottomRight() ); path4.lineTo( midRect2.topRight() ); path4.lineTo( innerRect.topRight() ); path4.lineTo( innerRect.bottomRight() ); path4.lineTo( innerRect.bottomLeft() ); QPainterPath path5; path5.addRect( midRect1 ); path5.addRect( midRect2 ); painter->setPen( Qt::NoPen ); QBrush brush1 = palette.dark().color(); QBrush brush2 = palette.light().color(); if ( shadow == QFrame::Raised ) qSwap( brush1, brush2 ); painter->setBrush( brush1 ); painter->drawPath( path1 ); painter->drawPath( path4 ); painter->setBrush( brush2 ); painter->drawPath( path2 ); painter->drawPath( path3 ); painter->setBrush( palette.mid() ); painter->drawPath( path5 ); } #if 0 // qDrawWinPanel doesn't result in something nice // on a scalable document like PDF. Better draw a // Panel. else if ( shape == QFrame::WinPanel ) { painter->setRenderHint( QPainter::NonCosmeticDefaultPen, true ); qDrawWinPanel ( painter, rect.toRect(), palette, frameStyle & QFrame::Sunken ); } else if ( shape == QFrame::StyledPanel ) { } #endif else { const QRectF outerRect = rect.adjusted( 0.0, 0.0, -1.0, -1.0 ); const QRectF innerRect = outerRect.adjusted( frameWidth - 1.0, frameWidth - 1.0, -( frameWidth - 1.0 ), -( frameWidth - 1.0 ) ); QPainterPath path1; path1.moveTo( outerRect.bottomLeft() ); path1.lineTo( outerRect.topLeft() ); path1.lineTo( outerRect.topRight() ); path1.lineTo( innerRect.topRight() ); path1.lineTo( innerRect.topLeft() ); path1.lineTo( innerRect.bottomLeft() ); QPainterPath path2; path2.moveTo( outerRect.bottomLeft() ); path2.lineTo( outerRect.bottomRight() ); path2.lineTo( outerRect.topRight() ); path2.lineTo( innerRect.topRight() ); path2.lineTo( innerRect.bottomRight() ); path2.lineTo( innerRect.bottomLeft() ); painter->setPen( Qt::NoPen ); QBrush brush1 = palette.dark().color(); QBrush brush2 = palette.light().color(); if ( shadow == QFrame::Raised ) qSwap( brush1, brush2 ); painter->setBrush( brush1 ); painter->drawPath( path1 ); painter->setBrush( brush2 ); painter->drawPath( path2 ); } } painter->restore(); } /*! Draw a rectangular frame with rounded borders \param painter Painter \param rect Frame rectangle \param xRadius x-radius of the ellipses defining the corners \param yRadius y-radius of the ellipses defining the corners \param palette QPalette::WindowText is used for plain borders QPalette::Dark and QPalette::Light for raised or sunken borders \param lineWidth Line width \param frameStyle bitwise OR´ed value of QFrame::Shape and QFrame::Shadow */ void QwtPainter::drawRoundedFrame( QPainter *painter, const QRectF &rect, double xRadius, double yRadius, const QPalette &palette, int lineWidth, int frameStyle ) { painter->save(); painter->setRenderHint( QPainter::Antialiasing, true ); painter->setBrush( Qt::NoBrush ); double lw2 = lineWidth * 0.5; QRectF r = rect.adjusted( lw2, lw2, -lw2, -lw2 ); QPainterPath path; path.addRoundedRect( r, xRadius, yRadius ); enum Style { Plain, Sunken, Raised }; Style style = Plain; if ( (frameStyle & QFrame::Sunken) == QFrame::Sunken ) style = Sunken; else if ( (frameStyle & QFrame::Raised) == QFrame::Raised ) style = Raised; if ( style != Plain && path.elementCount() == 17 ) { // move + 4 * ( cubicTo + lineTo ) QPainterPath pathList[8]; for ( int i = 0; i < 4; i++ ) { const int j = i * 4 + 1; pathList[ 2 * i ].moveTo( path.elementAt(j - 1).x, path.elementAt( j - 1 ).y ); pathList[ 2 * i ].cubicTo( path.elementAt(j + 0).x, path.elementAt(j + 0).y, path.elementAt(j + 1).x, path.elementAt(j + 1).y, path.elementAt(j + 2).x, path.elementAt(j + 2).y ); pathList[ 2 * i + 1 ].moveTo( path.elementAt(j + 2).x, path.elementAt(j + 2).y ); pathList[ 2 * i + 1 ].lineTo( path.elementAt(j + 3).x, path.elementAt(j + 3).y ); } QColor c1( palette.color( QPalette::Dark ) ); QColor c2( palette.color( QPalette::Light ) ); if ( style == Raised ) qSwap( c1, c2 ); for ( int i = 0; i < 4; i++ ) { QRectF r = pathList[2 * i].controlPointRect(); QPen arcPen; arcPen.setCapStyle( Qt::FlatCap ); arcPen.setWidth( lineWidth ); QPen linePen; linePen.setCapStyle( Qt::FlatCap ); linePen.setWidth( lineWidth ); switch( i ) { case 0: { arcPen.setColor( c1 ); linePen.setColor( c1 ); break; } case 1: { QLinearGradient gradient; gradient.setStart( r.topLeft() ); gradient.setFinalStop( r.bottomRight() ); gradient.setColorAt( 0.0, c1 ); gradient.setColorAt( 1.0, c2 ); arcPen.setBrush( gradient ); linePen.setColor( c2 ); break; } case 2: { arcPen.setColor( c2 ); linePen.setColor( c2 ); break; } case 3: { QLinearGradient gradient; gradient.setStart( r.bottomRight() ); gradient.setFinalStop( r.topLeft() ); gradient.setColorAt( 0.0, c2 ); gradient.setColorAt( 1.0, c1 ); arcPen.setBrush( gradient ); linePen.setColor( c1 ); break; } } painter->setPen( arcPen ); painter->drawPath( pathList[ 2 * i] ); painter->setPen( linePen ); painter->drawPath( pathList[ 2 * i + 1] ); } } else { QPen pen( palette.color( QPalette::WindowText ), lineWidth ); painter->setPen( pen ); painter->drawPath( path ); } painter->restore(); } /*! Draw a color bar into a rectangle \param painter Painter \param colorMap Color map \param interval Value range \param scaleMap Scale map \param orientation Orientation \param rect Traget rectangle */ void QwtPainter::drawColorBar( QPainter *painter, const QwtColorMap &colorMap, const QwtInterval &interval, const QwtScaleMap &scaleMap, Qt::Orientation orientation, const QRectF &rect ) { QVector colorTable; if ( colorMap.format() == QwtColorMap::Indexed ) colorTable = colorMap.colorTable( interval ); QColor c; const QRect devRect = rect.toAlignedRect(); /* We paint to a pixmap first to have something scalable for printing ( f.e. in a Pdf document ) */ QPixmap pixmap( devRect.size() ); pixmap.fill( Qt::transparent ); QPainter pmPainter( &pixmap ); pmPainter.translate( -devRect.x(), -devRect.y() ); if ( orientation == Qt::Horizontal ) { QwtScaleMap sMap = scaleMap; sMap.setPaintInterval( rect.left(), rect.right() ); for ( int x = devRect.left(); x <= devRect.right(); x++ ) { const double value = sMap.invTransform( x ); if ( colorMap.format() == QwtColorMap::RGB ) c.setRgba( colorMap.rgb( interval, value ) ); else c = colorTable[colorMap.colorIndex( interval, value )]; pmPainter.setPen( c ); pmPainter.drawLine( x, devRect.top(), x, devRect.bottom() ); } } else // Vertical { QwtScaleMap sMap = scaleMap; sMap.setPaintInterval( rect.bottom(), rect.top() ); for ( int y = devRect.top(); y <= devRect.bottom(); y++ ) { const double value = sMap.invTransform( y ); if ( colorMap.format() == QwtColorMap::RGB ) c.setRgba( colorMap.rgb( interval, value ) ); else c = colorTable[colorMap.colorIndex( interval, value )]; pmPainter.setPen( c ); pmPainter.drawLine( devRect.left(), y, devRect.right(), y ); } } pmPainter.end(); drawPixmap( painter, rect, pixmap ); } static inline void qwtFillRect( const QWidget *widget, QPainter *painter, const QRect &rect, const QBrush &brush) { if ( brush.style() == Qt::TexturePattern ) { painter->save(); painter->setClipRect( rect ); painter->drawTiledPixmap(rect, brush.texture(), rect.topLeft()); painter->restore(); } else if ( brush.gradient() ) { painter->save(); painter->setClipRect( rect ); painter->fillRect(0, 0, widget->width(), widget->height(), brush); painter->restore(); } else { painter->fillRect(rect, brush); } } /*! Fill a pixmap with the content of a widget In Qt >= 5.0 QPixmap::fill() is a nop, in Qt 4.x it is buggy for backgrounds with gradients. Thus fillPixmap() offers an alternative implementation. \param widget Widget \param pixmap Pixmap to be filled \param offset Offset \sa QPixmap::fill() */ void QwtPainter::fillPixmap( const QWidget *widget, QPixmap &pixmap, const QPoint &offset ) { const QRect rect( offset, pixmap.size() ); QPainter painter( &pixmap ); painter.translate( -offset ); const QBrush autoFillBrush = widget->palette().brush( widget->backgroundRole() ); if ( !( widget->autoFillBackground() && autoFillBrush.isOpaque() ) ) { const QBrush bg = widget->palette().brush( QPalette::Window ); qwtFillRect( widget, &painter, rect, bg); } if ( widget->autoFillBackground() ) qwtFillRect( widget, &painter, rect, autoFillBrush); if ( widget->testAttribute(Qt::WA_StyledBackground) ) { painter.setClipRegion( rect ); QStyleOption opt; opt.initFrom( widget ); widget->style()->drawPrimitive( QStyle::PE_Widget, &opt, &painter, widget ); } } /*! Fill rect with the background of a widget \param painter Painter \param rect Rectangle to be filled \param widget Widget \sa QStyle::PE_Widget, QWidget::backgroundRole() */ void QwtPainter::drawBackgound( QPainter *painter, const QRectF &rect, const QWidget *widget ) { if ( widget->testAttribute( Qt::WA_StyledBackground ) ) { QStyleOption opt; opt.initFrom( widget ); opt.rect = rect.toAlignedRect(); widget->style()->drawPrimitive( QStyle::PE_Widget, &opt, painter, widget); } else { const QBrush brush = widget->palette().brush( widget->backgroundRole() ); painter->fillRect( rect, brush ); } } /*! \return A pixmap that can be used as backing store \param widget Widget, for which the backinstore is intended \param size Size of the pixmap */ QPixmap QwtPainter::backingStore( QWidget *widget, const QSize &size ) { QPixmap pm; #define QWT_HIGH_DPI 1 #if QT_VERSION >= 0x050000 && QWT_HIGH_DPI qreal pixelRatio = 1.0; if ( widget && widget->windowHandle() ) { pixelRatio = widget->windowHandle()->devicePixelRatio(); } else { if ( qApp ) pixelRatio = qApp->devicePixelRatio(); } pm = QPixmap( size * pixelRatio ); pm.setDevicePixelRatio( pixelRatio ); #else Q_UNUSED( widget ) pm = QPixmap( size ); #endif #if QT_VERSION < 0x050000 #ifdef Q_WS_X11 if ( widget && isX11GraphicsSystem() ) { if ( pm.x11Info().screen() != widget->x11Info().screen() ) pm.x11SetScreen( widget->x11Info().screen() ); } #endif #endif return pm; } connectome-workbench-1.2.3+git41-gc4c6c90/src/Qwt/qwt_painter.h000066400000000000000000000137641300200146000240400ustar00rootroot00000000000000/* -*- mode: C++ ; c-file-style: "stroustrup" -*- ***************************** * Qwt Widget Library * Copyright (C) 1997 Josef Wilgen * Copyright (C) 2002 Uwe Rathmann * * This library is free software; you can redistribute it and/or * modify it under the terms of the Qwt License, Version 1.0 *****************************************************************************/ #ifndef QWT_PAINTER_H #define QWT_PAINTER_H #include "qwt_global.h" #include #include #include #include #include class QPainter; class QBrush; class QColor; class QWidget; class QPolygonF; class QRectF; class QImage; class QPixmap; class QwtScaleMap; class QwtColorMap; class QwtInterval; class QTextDocument; class QPainterPath; /*! \brief A collection of QPainter workarounds */ class QWT_EXPORT QwtPainter { public: static void setPolylineSplitting( bool ); static bool polylineSplitting(); static void setRoundingAlignment( bool ); static bool roundingAlignment(); static bool roundingAlignment(QPainter *); static void drawText( QPainter *, double x, double y, const QString & ); static void drawText( QPainter *, const QPointF &, const QString & ); static void drawText( QPainter *, double x, double y, double w, double h, int flags, const QString & ); static void drawText( QPainter *, const QRectF &, int flags, const QString & ); #ifndef QT_NO_RICHTEXT static void drawSimpleRichText( QPainter *, const QRectF &, int flags, const QTextDocument & ); #endif static void drawRect( QPainter *, double x, double y, double w, double h ); static void drawRect( QPainter *, const QRectF &rect ); static void fillRect( QPainter *, const QRectF &, const QBrush & ); static void drawEllipse( QPainter *, const QRectF & ); static void drawPie( QPainter *, const QRectF & r, int a, int alen ); static void drawLine( QPainter *, double x1, double y1, double x2, double y2 ); static void drawLine( QPainter *, const QPointF &p1, const QPointF &p2 ); static void drawLine( QPainter *, const QLineF & ); static void drawPolygon( QPainter *, const QPolygonF & ); static void drawPolyline( QPainter *, const QPolygonF & ); static void drawPolyline( QPainter *, const QPointF *, int pointCount ); static void drawPolygon( QPainter *, const QPolygon & ); static void drawPolyline( QPainter *, const QPolygon & ); static void drawPolyline( QPainter *, const QPoint *, int pointCount ); static void drawPoint( QPainter *, const QPoint & ); static void drawPoints( QPainter *, const QPolygon & ); static void drawPoints( QPainter *, const QPoint *, int pointCount ); static void drawPoint( QPainter *, double x, double y ); static void drawPoint( QPainter *, const QPointF & ); static void drawPoints( QPainter *, const QPolygonF & ); static void drawPoints( QPainter *, const QPointF *, int pointCount ); static void drawPath( QPainter *, const QPainterPath & ); static void drawImage( QPainter *, const QRectF &, const QImage & ); static void drawPixmap( QPainter *, const QRectF &, const QPixmap & ); static void drawRoundFrame( QPainter *, const QRectF &, const QPalette &, int lineWidth, int frameStyle ); static void drawRoundedFrame( QPainter *, const QRectF &, double xRadius, double yRadius, const QPalette &, int lineWidth, int frameStyle ); static void drawFrame( QPainter *, const QRectF &rect, const QPalette &palette, QPalette::ColorRole foregroundRole, int lineWidth, int midLineWidth, int frameStyle ); static void drawFocusRect( QPainter *, const QWidget * ); static void drawFocusRect( QPainter *, const QWidget *, const QRect & ); static void drawColorBar( QPainter *painter, const QwtColorMap &, const QwtInterval &, const QwtScaleMap &, Qt::Orientation, const QRectF & ); static bool isAligning( QPainter *painter ); static bool isX11GraphicsSystem(); static void fillPixmap( const QWidget *, QPixmap &, const QPoint &offset = QPoint() ); static void drawBackgound( QPainter *painter, const QRectF &rect, const QWidget *widget ); static QPixmap backingStore( QWidget *, const QSize & ); private: static bool d_polylineSplitting; static bool d_roundingAlignment; }; //! Wrapper for QPainter::drawPoint() inline void QwtPainter::drawPoint( QPainter *painter, double x, double y ) { QwtPainter::drawPoint( painter, QPointF( x, y ) ); } //! Wrapper for QPainter::drawPoints() inline void QwtPainter::drawPoints( QPainter *painter, const QPolygon &polygon ) { drawPoints( painter, polygon.data(), polygon.size() ); } //! Wrapper for QPainter::drawPoints() inline void QwtPainter::drawPoints( QPainter *painter, const QPolygonF &polygon ) { drawPoints( painter, polygon.data(), polygon.size() ); } //! Wrapper for QPainter::drawLine() inline void QwtPainter::drawLine( QPainter *painter, double x1, double y1, double x2, double y2 ) { QwtPainter::drawLine( painter, QPointF( x1, y1 ), QPointF( x2, y2 ) ); } //! Wrapper for QPainter::drawLine() inline void QwtPainter::drawLine( QPainter *painter, const QLineF &line ) { QwtPainter::drawLine( painter, line.p1(), line.p2() ); } /*! \return True, when line splitting for the raster paint engine is enabled. \sa setPolylineSplitting() */ inline bool QwtPainter::polylineSplitting() { return d_polylineSplitting; } /*! Check whether coordinates should be rounded, before they are painted to a paint engine that rounds to integer values. For other paint engines ( PDF, SVG ), this flag has no effect. \return True, when rounding is enabled \sa setRoundingAlignment(), isAligning() */ inline bool QwtPainter::roundingAlignment() { return d_roundingAlignment; } /*! \return roundingAlignment() && isAligning(painter); \param painter Painter */ inline bool QwtPainter::roundingAlignment(QPainter *painter) { return d_roundingAlignment && isAligning(painter); } #endif connectome-workbench-1.2.3+git41-gc4c6c90/src/Qwt/qwt_painter_command.cpp000066400000000000000000000130161300200146000260570ustar00rootroot00000000000000/* -*- mode: C++ ; c-file-style: "stroustrup" -*- ***************************** * Qwt Widget Library * Copyright (C) 1997 Josef Wilgen * Copyright (C) 2002 Uwe Rathmann * * This library is free software; you can redistribute it and/or * modify it under the terms of the Qwt License, Version 1.0 *****************************************************************************/ #include "qwt_painter_command.h" //! Construct an invalid command QwtPainterCommand::QwtPainterCommand(): d_type( Invalid ) { } //! Copy constructor QwtPainterCommand::QwtPainterCommand( const QPainterPath &path ): d_type( Path ) { d_path = new QPainterPath( path ); } /*! Constructor for Pixmap paint operation \param rect Target rectangle \param pixmap Pixmap \param subRect Rectangle inside the pixmap \sa QPainter::drawPixmap() */ QwtPainterCommand::QwtPainterCommand( const QRectF &rect, const QPixmap &pixmap, const QRectF& subRect ): d_type( Pixmap ) { d_pixmapData = new PixmapData(); d_pixmapData->rect = rect; d_pixmapData->pixmap = pixmap; d_pixmapData->subRect = subRect; } /*! Constructor for Image paint operation \param rect Target rectangle \param image Image \param subRect Rectangle inside the image \param flags Conversion flags \sa QPainter::drawImage() */ QwtPainterCommand::QwtPainterCommand( const QRectF &rect, const QImage &image, const QRectF& subRect, Qt::ImageConversionFlags flags ): d_type( Image ) { d_imageData = new ImageData(); d_imageData->rect = rect; d_imageData->image = image; d_imageData->subRect = subRect; d_imageData->flags = flags; } /*! Constructor for State paint operation \param state Paint engine state */ QwtPainterCommand::QwtPainterCommand( const QPaintEngineState &state ): d_type( State ) { d_stateData = new StateData(); d_stateData->flags = state.state(); if ( d_stateData->flags & QPaintEngine::DirtyPen ) d_stateData->pen = state.pen(); if ( d_stateData->flags & QPaintEngine::DirtyBrush ) d_stateData->brush = state.brush(); if ( d_stateData->flags & QPaintEngine::DirtyBrushOrigin ) d_stateData->brushOrigin = state.brushOrigin(); if ( d_stateData->flags & QPaintEngine::DirtyFont ) d_stateData->font = state.font(); if ( d_stateData->flags & QPaintEngine::DirtyBackground ) { d_stateData->backgroundMode = state.backgroundMode(); d_stateData->backgroundBrush = state.backgroundBrush(); } if ( d_stateData->flags & QPaintEngine::DirtyTransform ) d_stateData->transform = state.transform(); if ( d_stateData->flags & QPaintEngine::DirtyClipEnabled ) d_stateData->isClipEnabled = state.isClipEnabled(); if ( d_stateData->flags & QPaintEngine::DirtyClipRegion ) { d_stateData->clipRegion = state.clipRegion(); d_stateData->clipOperation = state.clipOperation(); } if ( d_stateData->flags & QPaintEngine::DirtyClipPath ) { d_stateData->clipPath = state.clipPath(); d_stateData->clipOperation = state.clipOperation(); } if ( d_stateData->flags & QPaintEngine::DirtyHints ) d_stateData->renderHints = state.renderHints(); if ( d_stateData->flags & QPaintEngine::DirtyCompositionMode ) d_stateData->compositionMode = state.compositionMode(); if ( d_stateData->flags & QPaintEngine::DirtyOpacity ) d_stateData->opacity = state.opacity(); } /*! Copy constructor \param other Command to be copied */ QwtPainterCommand::QwtPainterCommand(const QwtPainterCommand &other) { copy( other ); } //! Destructor QwtPainterCommand::~QwtPainterCommand() { reset(); } /*! Assignment operator \param other Command to be copied \return Modified command */ QwtPainterCommand &QwtPainterCommand::operator=(const QwtPainterCommand &other) { reset(); copy( other ); return *this; } void QwtPainterCommand::copy( const QwtPainterCommand &other ) { d_type = other.d_type; switch( other.d_type ) { case Path: { d_path = new QPainterPath( *other.d_path ); break; } case Pixmap: { d_pixmapData = new PixmapData( *other.d_pixmapData ); break; } case Image: { d_imageData = new ImageData( *other.d_imageData ); break; } case State: { d_stateData = new StateData( *other.d_stateData ); break; } default: break; } } void QwtPainterCommand::reset() { switch( d_type ) { case Path: { delete d_path; break; } case Pixmap: { delete d_pixmapData; break; } case Image: { delete d_imageData; break; } case State: { delete d_stateData; break; } default: break; } d_type = Invalid; } //! \return Painter path to be painted QPainterPath *QwtPainterCommand::path() { return d_path; } //! \return Attributes how to paint a QPixmap QwtPainterCommand::PixmapData* QwtPainterCommand::pixmapData() { return d_pixmapData; } //! \return Attributes how to paint a QImage QwtPainterCommand::ImageData* QwtPainterCommand::imageData() { return d_imageData; } //! \return Attributes of a state change QwtPainterCommand::StateData* QwtPainterCommand::stateData() { return d_stateData; } connectome-workbench-1.2.3+git41-gc4c6c90/src/Qwt/qwt_painter_command.h000066400000000000000000000073401300200146000255270ustar00rootroot00000000000000/* -*- mode: C++ ; c-file-style: "stroustrup" -*- ***************************** * Qwt Widget Library * Copyright (C) 1997 Josef Wilgen * Copyright (C) 2002 Uwe Rathmann * * This library is free software; you can redistribute it and/or * modify it under the terms of the Qwt License, Version 1.0 *****************************************************************************/ #ifndef QWT_PAINTER_COMMAND_H #define QWT_PAINTER_COMMAND_H #include "qwt_global.h" #include #include #include #include class QPainterPath; /*! QwtPainterCommand represents the attributes of a paint operation how it is used between QPainter and QPaintDevice It is used by QwtGraphic to record and replay paint operations \sa QwtGraphic::commands() */ class QWT_EXPORT QwtPainterCommand { public: //! Type of the paint command enum Type { //! Invalid command Invalid = -1, //! Draw a QPainterPath Path, //! Draw a QPixmap Pixmap, //! Draw a QImage Image, //! QPainter state change State }; //! Attributes how to paint a QPixmap struct PixmapData { QRectF rect; QPixmap pixmap; QRectF subRect; }; //! Attributes how to paint a QImage struct ImageData { QRectF rect; QImage image; QRectF subRect; Qt::ImageConversionFlags flags; }; //! Attributes of a state change struct StateData { QPaintEngine::DirtyFlags flags; QPen pen; QBrush brush; QPointF brushOrigin; QBrush backgroundBrush; Qt::BGMode backgroundMode; QFont font; QMatrix matrix; QTransform transform; Qt::ClipOperation clipOperation; QRegion clipRegion; QPainterPath clipPath; bool isClipEnabled; QPainter::RenderHints renderHints; QPainter::CompositionMode compositionMode; qreal opacity; }; QwtPainterCommand(); QwtPainterCommand(const QwtPainterCommand &); QwtPainterCommand( const QPainterPath & ); QwtPainterCommand( const QRectF &rect, const QPixmap &, const QRectF& subRect ); QwtPainterCommand( const QRectF &rect, const QImage &, const QRectF& subRect, Qt::ImageConversionFlags ); QwtPainterCommand( const QPaintEngineState & ); ~QwtPainterCommand(); QwtPainterCommand &operator=(const QwtPainterCommand & ); Type type() const; QPainterPath *path(); const QPainterPath *path() const; PixmapData* pixmapData(); const PixmapData* pixmapData() const; ImageData* imageData(); const ImageData* imageData() const; StateData* stateData(); const StateData* stateData() const; private: void copy( const QwtPainterCommand & ); void reset(); Type d_type; union { QPainterPath *d_path; PixmapData *d_pixmapData; ImageData *d_imageData; StateData *d_stateData; }; }; //! \return Type of the command inline QwtPainterCommand::Type QwtPainterCommand::type() const { return d_type; } //! \return Painter path to be painted inline const QPainterPath *QwtPainterCommand::path() const { return d_path; } //! \return Attributes how to paint a QPixmap inline const QwtPainterCommand::PixmapData* QwtPainterCommand::pixmapData() const { return d_pixmapData; } //! \return Attributes how to paint a QImage inline const QwtPainterCommand::ImageData * QwtPainterCommand::imageData() const { return d_imageData; } //! \return Attributes of a state change inline const QwtPainterCommand::StateData * QwtPainterCommand::stateData() const { return d_stateData; } #endif connectome-workbench-1.2.3+git41-gc4c6c90/src/Qwt/qwt_panner.cpp000066400000000000000000000302271300200146000242050ustar00rootroot00000000000000/* -*- mode: C++ ; c-file-style: "stroustrup" -*- ***************************** * Qwt Widget Library * Copyright (C) 1997 Josef Wilgen * Copyright (C) 2002 Uwe Rathmann * * This library is free software; you can redistribute it and/or * modify it under the terms of the Qwt License, Version 1.0 *****************************************************************************/ #include "qwt_panner.h" #include "qwt_picker.h" #include "qwt_painter.h" #include #include #include #include #include static QVector qwtActivePickers( QWidget *w ) { QVector pickers; QObjectList children = w->children(); for ( int i = 0; i < children.size(); i++ ) { QwtPicker *picker = qobject_cast( children[i] ); if ( picker && picker->isEnabled() ) pickers += picker; } return pickers; } class QwtPanner::PrivateData { public: PrivateData(): button( Qt::LeftButton ), buttonModifiers( Qt::NoModifier ), abortKey( Qt::Key_Escape ), abortKeyModifiers( Qt::NoModifier ), #ifndef QT_NO_CURSOR cursor( NULL ), restoreCursor( NULL ), hasCursor( false ), #endif isEnabled( false ) { orientations = Qt::Vertical | Qt::Horizontal; } ~PrivateData() { #ifndef QT_NO_CURSOR delete cursor; delete restoreCursor; #endif } Qt::MouseButton button; Qt::KeyboardModifiers buttonModifiers; int abortKey; Qt::KeyboardModifiers abortKeyModifiers; QPoint initialPos; QPoint pos; QPixmap pixmap; QBitmap contentsMask; #ifndef QT_NO_CURSOR QCursor *cursor; QCursor *restoreCursor; bool hasCursor; #endif bool isEnabled; Qt::Orientations orientations; }; /*! Creates an panner that is enabled for the left mouse button. \param parent Parent widget to be panned */ QwtPanner::QwtPanner( QWidget *parent ): QWidget( parent ) { d_data = new PrivateData(); setAttribute( Qt::WA_TransparentForMouseEvents ); setAttribute( Qt::WA_NoSystemBackground ); setFocusPolicy( Qt::NoFocus ); hide(); setEnabled( true ); } //! Destructor QwtPanner::~QwtPanner() { delete d_data; } /*! Change the mouse button and modifiers used for panning The defaults are Qt::LeftButton and Qt::NoModifier */ void QwtPanner::setMouseButton( Qt::MouseButton button, Qt::KeyboardModifiers modifiers ) { d_data->button = button; d_data->buttonModifiers = modifiers; } //! Get mouse button and modifiers used for panning void QwtPanner::getMouseButton( Qt::MouseButton &button, Qt::KeyboardModifiers &modifiers ) const { button = d_data->button; modifiers = d_data->buttonModifiers; } /*! Change the abort key The defaults are Qt::Key_Escape and Qt::NoModifiers \param key Key ( See Qt::Keycode ) \param modifiers Keyboard modifiers */ void QwtPanner::setAbortKey( int key, Qt::KeyboardModifiers modifiers ) { d_data->abortKey = key; d_data->abortKeyModifiers = modifiers; } //! Get the abort key and modifiers void QwtPanner::getAbortKey( int &key, Qt::KeyboardModifiers &modifiers ) const { key = d_data->abortKey; modifiers = d_data->abortKeyModifiers; } /*! Change the cursor, that is active while panning The default is the cursor of the parent widget. \param cursor New cursor \sa setCursor() */ #ifndef QT_NO_CURSOR void QwtPanner::setCursor( const QCursor &cursor ) { d_data->cursor = new QCursor( cursor ); } #endif /*! \return Cursor that is active while panning \sa setCursor() */ #ifndef QT_NO_CURSOR const QCursor QwtPanner::cursor() const { if ( d_data->cursor ) return *d_data->cursor; if ( parentWidget() ) return parentWidget()->cursor(); return QCursor(); } #endif /*! \brief En/disable the panner When enabled is true an event filter is installed for the observed widget, otherwise the event filter is removed. \param on true or false \sa isEnabled(), eventFilter() */ void QwtPanner::setEnabled( bool on ) { if ( d_data->isEnabled != on ) { d_data->isEnabled = on; QWidget *w = parentWidget(); if ( w ) { if ( d_data->isEnabled ) { w->installEventFilter( this ); } else { w->removeEventFilter( this ); hide(); } } } } /*! Set the orientations, where panning is enabled The default value is in both directions: Qt::Horizontal | Qt::Vertical /param o Orientation */ void QwtPanner::setOrientations( Qt::Orientations o ) { d_data->orientations = o; } //! Return the orientation, where paning is enabled Qt::Orientations QwtPanner::orientations() const { return d_data->orientations; } /*! \return True if an orientation is enabled \sa orientations(), setOrientations() */ bool QwtPanner::isOrientationEnabled( Qt::Orientation o ) const { return d_data->orientations & o; } /*! \return true when enabled, false otherwise \sa setEnabled, eventFilter() */ bool QwtPanner::isEnabled() const { return d_data->isEnabled; } /*! \brief Paint event Repaint the grabbed pixmap on its current position and fill the empty spaces by the background of the parent widget. \param pe Paint event */ void QwtPanner::paintEvent( QPaintEvent *pe ) { int dx = d_data->pos.x() - d_data->initialPos.x(); int dy = d_data->pos.y() - d_data->initialPos.y(); QRect r( 0, 0, d_data->pixmap.width(), d_data->pixmap.height() ); r.moveCenter( QPoint( r.center().x() + dx, r.center().y() + dy ) ); QPixmap pm( size() ); QwtPainter::fillPixmap( parentWidget(), pm ); QPainter painter( &pm ); if ( !d_data->contentsMask.isNull() ) { QPixmap masked = d_data->pixmap; masked.setMask( d_data->contentsMask ); painter.drawPixmap( r, masked ); } else { painter.drawPixmap( r, d_data->pixmap ); } painter.end(); if ( !d_data->contentsMask.isNull() ) pm.setMask( d_data->contentsMask ); painter.begin( this ); painter.setClipRegion( pe->region() ); painter.drawPixmap( 0, 0, pm ); } /*! \brief Calculate a mask for the contents of the panned widget Sometimes only parts of the contents of a widget should be panned. F.e. for a widget with a styled background with rounded borders only the area inside of the border should be panned. \return An empty bitmap, indicating no mask */ QBitmap QwtPanner::contentsMask() const { return QBitmap(); } /*! Grab the widget into a pixmap. \return Grabbed pixmap */ QPixmap QwtPanner::grab() const { #if QT_VERSION >= 0x050000 return parentWidget()->grab( parentWidget()->rect() ); #else return QPixmap::grabWidget( parentWidget() ); #endif } /*! \brief Event filter When isEnabled() is true mouse events of the observed widget are filtered. \param object Object to be filtered \param event Event \return Always false, beside for paint events for the parent widget. \sa widgetMousePressEvent(), widgetMouseReleaseEvent(), widgetMouseMoveEvent() */ bool QwtPanner::eventFilter( QObject *object, QEvent *event ) { if ( object == NULL || object != parentWidget() ) return false; switch ( event->type() ) { case QEvent::MouseButtonPress: { widgetMousePressEvent( static_cast( event ) ); break; } case QEvent::MouseMove: { widgetMouseMoveEvent( static_cast( event ) ); break; } case QEvent::MouseButtonRelease: { widgetMouseReleaseEvent( static_cast( event ) ); break; } case QEvent::KeyPress: { widgetKeyPressEvent( static_cast( event ) ); break; } case QEvent::KeyRelease: { widgetKeyReleaseEvent( static_cast( event ) ); break; } case QEvent::Paint: { if ( isVisible() ) return true; break; } default:; } return false; } /*! Handle a mouse press event for the observed widget. \param mouseEvent Mouse event \sa eventFilter(), widgetMouseReleaseEvent(), widgetMouseMoveEvent(), */ void QwtPanner::widgetMousePressEvent( QMouseEvent *mouseEvent ) { if ( ( mouseEvent->button() != d_data->button ) || ( mouseEvent->modifiers() != d_data->buttonModifiers ) ) { return; } QWidget *w = parentWidget(); if ( w == NULL ) return; #ifndef QT_NO_CURSOR showCursor( true ); #endif d_data->initialPos = d_data->pos = mouseEvent->pos(); setGeometry( parentWidget()->rect() ); // We don't want to grab the picker ! QVector pickers = qwtActivePickers( parentWidget() ); for ( int i = 0; i < pickers.size(); i++ ) pickers[i]->setEnabled( false ); d_data->pixmap = grab(); d_data->contentsMask = contentsMask(); for ( int i = 0; i < pickers.size(); i++ ) pickers[i]->setEnabled( true ); show(); } /*! Handle a mouse move event for the observed widget. \param mouseEvent Mouse event \sa eventFilter(), widgetMousePressEvent(), widgetMouseReleaseEvent() */ void QwtPanner::widgetMouseMoveEvent( QMouseEvent *mouseEvent ) { if ( !isVisible() ) return; QPoint pos = mouseEvent->pos(); if ( !isOrientationEnabled( Qt::Horizontal ) ) pos.setX( d_data->initialPos.x() ); if ( !isOrientationEnabled( Qt::Vertical ) ) pos.setY( d_data->initialPos.y() ); if ( pos != d_data->pos && rect().contains( pos ) ) { d_data->pos = pos; update(); Q_EMIT moved( d_data->pos.x() - d_data->initialPos.x(), d_data->pos.y() - d_data->initialPos.y() ); } } /*! Handle a mouse release event for the observed widget. \param mouseEvent Mouse event \sa eventFilter(), widgetMousePressEvent(), widgetMouseMoveEvent(), */ void QwtPanner::widgetMouseReleaseEvent( QMouseEvent *mouseEvent ) { if ( isVisible() ) { hide(); #ifndef QT_NO_CURSOR showCursor( false ); #endif QPoint pos = mouseEvent->pos(); if ( !isOrientationEnabled( Qt::Horizontal ) ) pos.setX( d_data->initialPos.x() ); if ( !isOrientationEnabled( Qt::Vertical ) ) pos.setY( d_data->initialPos.y() ); d_data->pixmap = QPixmap(); d_data->contentsMask = QBitmap(); d_data->pos = pos; if ( d_data->pos != d_data->initialPos ) { Q_EMIT panned( d_data->pos.x() - d_data->initialPos.x(), d_data->pos.y() - d_data->initialPos.y() ); } } } /*! Handle a key press event for the observed widget. \param keyEvent Key event \sa eventFilter(), widgetKeyReleaseEvent() */ void QwtPanner::widgetKeyPressEvent( QKeyEvent *keyEvent ) { if ( ( keyEvent->key() == d_data->abortKey ) && ( keyEvent->modifiers() == d_data->abortKeyModifiers ) ) { hide(); #ifndef QT_NO_CURSOR showCursor( false ); #endif d_data->pixmap = QPixmap(); } } /*! Handle a key release event for the observed widget. \param keyEvent Key event \sa eventFilter(), widgetKeyReleaseEvent() */ void QwtPanner::widgetKeyReleaseEvent( QKeyEvent *keyEvent ) { Q_UNUSED( keyEvent ); } #ifndef QT_NO_CURSOR void QwtPanner::showCursor( bool on ) { if ( on == d_data->hasCursor ) return; QWidget *w = parentWidget(); if ( w == NULL || d_data->cursor == NULL ) return; d_data->hasCursor = on; if ( on ) { if ( w->testAttribute( Qt::WA_SetCursor ) ) { delete d_data->restoreCursor; d_data->restoreCursor = new QCursor( w->cursor() ); } w->setCursor( *d_data->cursor ); } else { if ( d_data->restoreCursor ) { w->setCursor( *d_data->restoreCursor ); delete d_data->restoreCursor; d_data->restoreCursor = NULL; } else w->unsetCursor(); } } #endif connectome-workbench-1.2.3+git41-gc4c6c90/src/Qwt/qwt_panner.h000066400000000000000000000055741300200146000236610ustar00rootroot00000000000000/* -*- mode: C++ ; c-file-style: "stroustrup" -*- ***************************** * Qwt Widget Library * Copyright (C) 1997 Josef Wilgen * Copyright (C) 2002 Uwe Rathmann * * This library is free software; you can redistribute it and/or * modify it under the terms of the Qwt License, Version 1.0 *****************************************************************************/ #ifndef QWT_PANNER_H #define QWT_PANNER_H 1 #include "qwt_global.h" #include #include class QCursor; /*! \brief QwtPanner provides panning of a widget QwtPanner grabs the contents of a widget, that can be dragged in all directions. The offset between the start and the end position is emitted by the panned signal. QwtPanner grabs the content of the widget into a pixmap and moves the pixmap around, without initiating any repaint events for the widget. Areas, that are not part of content are not painted while panning. This makes panning fast enough for widgets, where repaints are too slow for mouse movements. For widgets, where repaints are very fast it might be better to implement panning manually by mapping mouse events into paint events. */ class QWT_EXPORT QwtPanner: public QWidget { Q_OBJECT public: QwtPanner( QWidget* parent ); virtual ~QwtPanner(); void setEnabled( bool ); bool isEnabled() const; void setMouseButton( Qt::MouseButton, Qt::KeyboardModifiers = Qt::NoModifier ); void getMouseButton( Qt::MouseButton &button, Qt::KeyboardModifiers & ) const; void setAbortKey( int key, Qt::KeyboardModifiers = Qt::NoModifier ); void getAbortKey( int &key, Qt::KeyboardModifiers & ) const; void setCursor( const QCursor & ); const QCursor cursor() const; void setOrientations( Qt::Orientations ); Qt::Orientations orientations() const; bool isOrientationEnabled( Qt::Orientation ) const; virtual bool eventFilter( QObject *, QEvent * ); Q_SIGNALS: /*! Signal emitted, when panning is done \param dx Offset in horizontal direction \param dy Offset in vertical direction */ void panned( int dx, int dy ); /*! Signal emitted, while the widget moved, but panning is not finished. \param dx Offset in horizontal direction \param dy Offset in vertical direction */ void moved( int dx, int dy ); protected: virtual void widgetMousePressEvent( QMouseEvent * ); virtual void widgetMouseReleaseEvent( QMouseEvent * ); virtual void widgetMouseMoveEvent( QMouseEvent * ); virtual void widgetKeyPressEvent( QKeyEvent * ); virtual void widgetKeyReleaseEvent( QKeyEvent * ); virtual void paintEvent( QPaintEvent * ); virtual QBitmap contentsMask() const; virtual QPixmap grab() const; private: #ifndef QT_NO_CURSOR void showCursor( bool ); #endif class PrivateData; PrivateData *d_data; }; #endif connectome-workbench-1.2.3+git41-gc4c6c90/src/Qwt/qwt_picker.cpp000066400000000000000000001161471300200146000242050ustar00rootroot00000000000000/* -*- mode: C++ ; c-file-style: "stroustrup" -*- ***************************** * Qwt Widget Library * Copyright (C) 1997 Josef Wilgen * Copyright (C) 2002 Uwe Rathmann * * This library is free software; you can redistribute it and/or * modify it under the terms of the Qwt License, Version 1.0 *****************************************************************************/ #include "qwt_picker.h" #include "qwt_picker_machine.h" #include "qwt_painter.h" #include "qwt_math.h" #include "qwt_widget_overlay.h" #include #include #include #include #include #include #include #include #include static inline QRegion qwtMaskRegion( const QRect &r, int penWidth ) { const int pw = qMax( penWidth, 1 ); const int pw2 = penWidth / 2; int x1 = r.left() - pw2; int x2 = r.right() + 1 + pw2 + ( pw % 2 ); int y1 = r.top() - pw2; int y2 = r.bottom() + 1 + pw2 + ( pw % 2 ); QRegion region; region += QRect( x1, y1, x2 - x1, pw ); region += QRect( x1, y1, pw, y2 - y1 ); region += QRect( x1, y2 - pw, x2 - x1, pw ); region += QRect( x2 - pw, y1, pw, y2 - y1 ); return region; } static inline QRegion qwtMaskRegion( const QLine &l, int penWidth ) { const int pw = qMax( penWidth, 1 ); const int pw2 = penWidth / 2; QRegion region; if ( l.x1() == l.x2() ) { region += QRect( l.x1() - pw2, l.y1(), pw, l.y2() ).normalized(); } else if ( l.y1() == l.y2() ) { region += QRect( l.x1(), l.y1() - pw2, l.x2(), pw ).normalized(); } return region; } class QwtPickerRubberband: public QwtWidgetOverlay { public: QwtPickerRubberband( QwtPicker *, QWidget * ); protected: virtual void drawOverlay( QPainter * ) const; virtual QRegion maskHint() const; QwtPicker *d_picker; }; class QwtPickerTracker: public QwtWidgetOverlay { public: QwtPickerTracker( QwtPicker *, QWidget * ); protected: virtual void drawOverlay( QPainter * ) const; virtual QRegion maskHint() const; QwtPicker *d_picker; }; class QwtPicker::PrivateData { public: PrivateData(): enabled( false ), stateMachine( NULL ), resizeMode( QwtPicker::Stretch ), rubberBand( QwtPicker::NoRubberBand ), trackerMode( QwtPicker::AlwaysOff ), isActive( false ), trackerPosition( -1, -1 ), mouseTracking( false ), openGL( false ) { } bool enabled; QwtPickerMachine *stateMachine; QwtPicker::ResizeMode resizeMode; QwtPicker::RubberBand rubberBand; QPen rubberBandPen; QwtPicker::DisplayMode trackerMode; QPen trackerPen; QFont trackerFont; QPolygon pickedPoints; bool isActive; QPoint trackerPosition; bool mouseTracking; // used to save previous value QPointer< QwtPickerRubberband > rubberBandOverlay; QPointer< QwtPickerTracker> trackerOverlay; bool openGL; }; QwtPickerRubberband::QwtPickerRubberband( QwtPicker *picker, QWidget *parent ): QwtWidgetOverlay( parent ), d_picker( picker ) { setMaskMode( QwtWidgetOverlay::MaskHint ); } QRegion QwtPickerRubberband::maskHint() const { return d_picker->rubberBandMask(); } void QwtPickerRubberband::drawOverlay( QPainter *painter ) const { painter->setPen( d_picker->rubberBandPen() ); d_picker->drawRubberBand( painter ); } QwtPickerTracker::QwtPickerTracker( QwtPicker *picker, QWidget *parent ): QwtWidgetOverlay( parent ), d_picker( picker ) { setMaskMode( QwtWidgetOverlay::MaskHint ); } QRegion QwtPickerTracker::maskHint() const { return d_picker->trackerRect( font() ); } void QwtPickerTracker::drawOverlay( QPainter *painter ) const { painter->setPen( d_picker->trackerPen() ); d_picker->drawTracker( painter ); } /*! Constructor Creates an picker that is enabled, but without a state machine. rubber band and tracker are disabled. \param parent Parent widget, that will be observed */ QwtPicker::QwtPicker( QWidget *parent ): QObject( parent ) { init( parent, NoRubberBand, AlwaysOff ); } /*! Constructor \param rubberBand Rubber band style \param trackerMode Tracker mode \param parent Parent widget, that will be observed */ QwtPicker::QwtPicker( RubberBand rubberBand, DisplayMode trackerMode, QWidget *parent ): QObject( parent ) { init( parent, rubberBand, trackerMode ); } //! Destructor QwtPicker::~QwtPicker() { setMouseTracking( false ); delete d_data->stateMachine; delete d_data->rubberBandOverlay; delete d_data->trackerOverlay; delete d_data; } //! Initialize the picker - used by the constructors void QwtPicker::init( QWidget *parent, RubberBand rubberBand, DisplayMode trackerMode ) { d_data = new PrivateData; d_data->rubberBand = rubberBand; if ( parent ) { if ( parent->focusPolicy() == Qt::NoFocus ) parent->setFocusPolicy( Qt::WheelFocus ); d_data->openGL = parent->inherits( "QGLWidget" ); d_data->trackerFont = parent->font(); d_data->mouseTracking = parent->hasMouseTracking(); setEnabled( true ); } setTrackerMode( trackerMode ); } /*! Set a state machine and delete the previous one \param stateMachine State machine \sa stateMachine() */ void QwtPicker::setStateMachine( QwtPickerMachine *stateMachine ) { if ( d_data->stateMachine != stateMachine ) { reset(); delete d_data->stateMachine; d_data->stateMachine = stateMachine; if ( d_data->stateMachine ) d_data->stateMachine->reset(); } } /*! \return Assigned state machine \sa setStateMachine() */ QwtPickerMachine *QwtPicker::stateMachine() { return d_data->stateMachine; } /*! \return Assigned state machine \sa setStateMachine() */ const QwtPickerMachine *QwtPicker::stateMachine() const { return d_data->stateMachine; } //! Return the parent widget, where the selection happens QWidget *QwtPicker::parentWidget() { QObject *obj = parent(); if ( obj && obj->isWidgetType() ) return static_cast( obj ); return NULL; } //! Return the parent widget, where the selection happens const QWidget *QwtPicker::parentWidget() const { QObject *obj = parent(); if ( obj && obj->isWidgetType() ) return static_cast< const QWidget *>( obj ); return NULL; } /*! Set the rubber band style \param rubberBand Rubber band style The default value is NoRubberBand. \sa rubberBand(), RubberBand, setRubberBandPen() */ void QwtPicker::setRubberBand( RubberBand rubberBand ) { d_data->rubberBand = rubberBand; } /*! \return Rubber band style \sa setRubberBand(), RubberBand, rubberBandPen() */ QwtPicker::RubberBand QwtPicker::rubberBand() const { return d_data->rubberBand; } /*! \brief Set the display mode of the tracker. A tracker displays information about current position of the cursor as a string. The display mode controls if the tracker has to be displayed whenever the observed widget has focus and cursor (AlwaysOn), never (AlwaysOff), or only when the selection is active (ActiveOnly). \param mode Tracker display mode \warning In case of AlwaysOn, mouseTracking will be enabled for the observed widget. \sa trackerMode(), DisplayMode */ void QwtPicker::setTrackerMode( DisplayMode mode ) { if ( d_data->trackerMode != mode ) { d_data->trackerMode = mode; setMouseTracking( d_data->trackerMode == AlwaysOn ); } } /*! \return Tracker display mode \sa setTrackerMode(), DisplayMode */ QwtPicker::DisplayMode QwtPicker::trackerMode() const { return d_data->trackerMode; } /*! \brief Set the resize mode. The resize mode controls what to do with the selected points of an active selection when the observed widget is resized. Stretch means the points are scaled according to the new size, KeepSize means the points remain unchanged. The default mode is Stretch. \param mode Resize mode \sa resizeMode(), ResizeMode */ void QwtPicker::setResizeMode( ResizeMode mode ) { d_data->resizeMode = mode; } /*! \return Resize mode \sa setResizeMode(), ResizeMode */ QwtPicker::ResizeMode QwtPicker::resizeMode() const { return d_data->resizeMode; } /*! \brief En/disable the picker When enabled is true an event filter is installed for the observed widget, otherwise the event filter is removed. \param enabled true or false \sa isEnabled(), eventFilter() */ void QwtPicker::setEnabled( bool enabled ) { if ( d_data->enabled != enabled ) { d_data->enabled = enabled; QWidget *w = parentWidget(); if ( w ) { if ( enabled ) w->installEventFilter( this ); else w->removeEventFilter( this ); } updateDisplay(); } } /*! \return true when enabled, false otherwise \sa setEnabled(), eventFilter() */ bool QwtPicker::isEnabled() const { return d_data->enabled; } /*! Set the font for the tracker \param font Tracker font \sa trackerFont(), setTrackerMode(), setTrackerPen() */ void QwtPicker::setTrackerFont( const QFont &font ) { if ( font != d_data->trackerFont ) { d_data->trackerFont = font; updateDisplay(); } } /*! \return Tracker font \sa setTrackerFont(), trackerMode(), trackerPen() */ QFont QwtPicker::trackerFont() const { return d_data->trackerFont; } /*! Set the pen for the tracker \param pen Tracker pen \sa trackerPen(), setTrackerMode(), setTrackerFont() */ void QwtPicker::setTrackerPen( const QPen &pen ) { if ( pen != d_data->trackerPen ) { d_data->trackerPen = pen; updateDisplay(); } } /*! \return Tracker pen \sa setTrackerPen(), trackerMode(), trackerFont() */ QPen QwtPicker::trackerPen() const { return d_data->trackerPen; } /*! Set the pen for the rubberband \param pen Rubber band pen \sa rubberBandPen(), setRubberBand() */ void QwtPicker::setRubberBandPen( const QPen &pen ) { if ( pen != d_data->rubberBandPen ) { d_data->rubberBandPen = pen; updateDisplay(); } } /*! \return Rubber band pen \sa setRubberBandPen(), rubberBand() */ QPen QwtPicker::rubberBandPen() const { return d_data->rubberBandPen; } /*! \brief Return the label for a position In case of HLineRubberBand the label is the value of the y position, in case of VLineRubberBand the value of the x position. Otherwise the label contains x and y position separated by a ',' . The format for the string conversion is "%d". \param pos Position \return Converted position as string */ QwtText QwtPicker::trackerText( const QPoint &pos ) const { QString label; switch ( rubberBand() ) { case HLineRubberBand: label.sprintf( "%d", pos.y() ); break; case VLineRubberBand: label.sprintf( "%d", pos.x() ); break; default: label.sprintf( "%d, %d", pos.x(), pos.y() ); } return label; } /*! Calculate the mask for the rubber band overlay \return Region for the mask \sa QWidget::setMask() */ QRegion QwtPicker::rubberBandMask() const { QRegion mask; if ( !isActive() || rubberBand() == NoRubberBand || rubberBandPen().style() == Qt::NoPen ) { return mask; } const QPolygon pa = adjustedPoints( d_data->pickedPoints ); QwtPickerMachine::SelectionType selectionType = QwtPickerMachine::NoSelection; if ( d_data->stateMachine ) selectionType = d_data->stateMachine->selectionType(); switch ( selectionType ) { case QwtPickerMachine::NoSelection: case QwtPickerMachine::PointSelection: { if ( pa.count() < 1 ) return mask; const QPoint pos = pa[0]; const int pw = rubberBandPen().width(); const QRect pRect = pickArea().boundingRect().toRect(); switch ( rubberBand() ) { case VLineRubberBand: { mask += qwtMaskRegion( QLine( pos.x(), pRect.top(), pos.x(), pRect.bottom() ), pw ); break; } case HLineRubberBand: { mask += qwtMaskRegion( QLine( pRect.left(), pos.y(), pRect.right(), pos.y() ), pw ); break; } case CrossRubberBand: { mask += qwtMaskRegion( QLine( pos.x(), pRect.top(), pos.x(), pRect.bottom() ), pw ); mask += qwtMaskRegion( QLine( pRect.left(), pos.y(), pRect.right(), pos.y() ), pw ); break; } default: break; } break; } case QwtPickerMachine::RectSelection: { if ( pa.count() < 2 ) return mask; const int pw = rubberBandPen().width(); switch ( rubberBand() ) { case RectRubberBand: { const QRect r = QRect( pa.first(), pa.last() ); mask = qwtMaskRegion( r.normalized(), pw ); break; } case EllipseRubberBand: { const QRect r = QRect( pa.first(), pa.last() ); mask += r.adjusted( -pw, -pw, pw, pw ); break; } default: break; } break; } case QwtPickerMachine::PolygonSelection: { const int pw = rubberBandPen().width(); if ( pw <= 1 ) { // because of the join style we better // return a mask for a pen width <= 1 only const int off = 2 * pw; const QRect r = pa.boundingRect(); mask += r.adjusted( -off, -off, off, off ); } break; } default: break; } return mask; } /*! Draw a rubber band, depending on rubberBand() \param painter Painter, initialized with a clip region \sa rubberBand(), RubberBand */ void QwtPicker::drawRubberBand( QPainter *painter ) const { if ( !isActive() || rubberBand() == NoRubberBand || rubberBandPen().style() == Qt::NoPen ) { return; } const QPolygon pa = adjustedPoints( d_data->pickedPoints ); QwtPickerMachine::SelectionType selectionType = QwtPickerMachine::NoSelection; if ( d_data->stateMachine ) selectionType = d_data->stateMachine->selectionType(); switch ( selectionType ) { case QwtPickerMachine::NoSelection: case QwtPickerMachine::PointSelection: { if ( pa.count() < 1 ) return; const QPoint pos = pa[0]; const QRect pRect = pickArea().boundingRect().toRect(); switch ( rubberBand() ) { case VLineRubberBand: { QwtPainter::drawLine( painter, pos.x(), pRect.top(), pos.x(), pRect.bottom() ); break; } case HLineRubberBand: { QwtPainter::drawLine( painter, pRect.left(), pos.y(), pRect.right(), pos.y() ); break; } case CrossRubberBand: { QwtPainter::drawLine( painter, pos.x(), pRect.top(), pos.x(), pRect.bottom() ); QwtPainter::drawLine( painter, pRect.left(), pos.y(), pRect.right(), pos.y() ); break; } default: break; } break; } case QwtPickerMachine::RectSelection: { if ( pa.count() < 2 ) return; const QRect rect = QRect( pa.first(), pa.last() ).normalized(); switch ( rubberBand() ) { case EllipseRubberBand: { QwtPainter::drawEllipse( painter, rect ); break; } case RectRubberBand: { QwtPainter::drawRect( painter, rect ); break; } default: break; } break; } case QwtPickerMachine::PolygonSelection: { if ( rubberBand() == PolygonRubberBand ) painter->drawPolyline( pa ); break; } default: break; } } /*! Draw the tracker \param painter Painter \sa trackerRect(), trackerText() */ void QwtPicker::drawTracker( QPainter *painter ) const { const QRect textRect = trackerRect( painter->font() ); if ( !textRect.isEmpty() ) { const QwtText label = trackerText( d_data->trackerPosition ); if ( !label.isEmpty() ) label.draw( painter, textRect ); } } /*! \brief Map the pickedPoints() into a selection() adjustedPoints() maps the points, that have been collected on the parentWidget() into a selection(). The default implementation simply returns the points unmodified. The reason, why a selection() differs from the picked points depends on the application requirements. F.e. : - A rectangular selection might need to have a specific aspect ratio only.\n - A selection could accept non intersecting polygons only.\n - ...\n The example below is for a rectangular selection, where the first point is the center of the selected rectangle. \par Example \verbatim QPolygon MyPicker::adjustedPoints(const QPolygon &points) const { QPolygon adjusted; if ( points.size() == 2 ) { const int width = qAbs(points[1].x() - points[0].x()); const int height = qAbs(points[1].y() - points[0].y()); QRect rect(0, 0, 2 * width, 2 * height); rect.moveCenter(points[0]); adjusted += rect.topLeft(); adjusted += rect.bottomRight(); } return adjusted; }\endverbatim\n \param points Selected points \return Selected points unmodified */ QPolygon QwtPicker::adjustedPoints( const QPolygon &points ) const { return points; } /*! \return Selected points \sa pickedPoints(), adjustedPoints() */ QPolygon QwtPicker::selection() const { return adjustedPoints( d_data->pickedPoints ); } //! \return Current position of the tracker QPoint QwtPicker::trackerPosition() const { return d_data->trackerPosition; } /*! Calculate the bounding rectangle for the tracker text from the current position of the tracker \param font Font of the tracker text \return Bounding rectangle of the tracker text \sa trackerPosition() */ QRect QwtPicker::trackerRect( const QFont &font ) const { if ( trackerMode() == AlwaysOff || ( trackerMode() == ActiveOnly && !isActive() ) ) { return QRect(); } if ( d_data->trackerPosition.x() < 0 || d_data->trackerPosition.y() < 0 ) return QRect(); QwtText text = trackerText( d_data->trackerPosition ); if ( text.isEmpty() ) return QRect(); const QSizeF textSize = text.textSize( font ); QRect textRect( 0, 0, qCeil( textSize.width() ), qCeil( textSize.height() ) ); const QPoint &pos = d_data->trackerPosition; int alignment = 0; if ( isActive() && d_data->pickedPoints.count() > 1 && rubberBand() != NoRubberBand ) { const QPoint last = d_data->pickedPoints[int( d_data->pickedPoints.count() ) - 2]; alignment |= ( pos.x() >= last.x() ) ? Qt::AlignRight : Qt::AlignLeft; alignment |= ( pos.y() > last.y() ) ? Qt::AlignBottom : Qt::AlignTop; } else alignment = Qt::AlignTop | Qt::AlignRight; const int margin = 5; int x = pos.x(); if ( alignment & Qt::AlignLeft ) x -= textRect.width() + margin; else if ( alignment & Qt::AlignRight ) x += margin; int y = pos.y(); if ( alignment & Qt::AlignBottom ) y += margin; else if ( alignment & Qt::AlignTop ) y -= textRect.height() + margin; textRect.moveTopLeft( QPoint( x, y ) ); const QRect pickRect = pickArea().boundingRect().toRect(); int right = qMin( textRect.right(), pickRect.right() - margin ); int bottom = qMin( textRect.bottom(), pickRect.bottom() - margin ); textRect.moveBottomRight( QPoint( right, bottom ) ); int left = qMax( textRect.left(), pickRect.left() + margin ); int top = qMax( textRect.top(), pickRect.top() + margin ); textRect.moveTopLeft( QPoint( left, top ) ); return textRect; } /*! \brief Event filter When isEnabled() is true all events of the observed widget are filtered. Mouse and keyboard events are translated into widgetMouse- and widgetKey- and widgetWheel-events. Paint and Resize events are handled to keep rubber band and tracker up to date. \param object Object to be filtered \param event Event \return Always false. \sa widgetEnterEvent(), widgetLeaveEvent(), widgetMousePressEvent(), widgetMouseReleaseEvent(), widgetMouseDoubleClickEvent(), widgetMouseMoveEvent(), widgetWheelEvent(), widgetKeyPressEvent(), widgetKeyReleaseEvent(), QObject::installEventFilter(), QObject::event() */ bool QwtPicker::eventFilter( QObject *object, QEvent *event ) { if ( object && object == parentWidget() ) { switch ( event->type() ) { case QEvent::Resize: { const QResizeEvent *re = static_cast( event ); /* Adding/deleting additional event filters inside of an event filter is not safe dues to the implementation in Qt ( changing alist while iterating ). So we create the overlays in a way, that they don't install en event filter ( parent set to NULL ) and do the resizing here. */ if ( d_data->trackerOverlay ) d_data->trackerOverlay->resize( re->size() ); if ( d_data->rubberBandOverlay ) d_data->rubberBandOverlay->resize( re->size() ); if ( d_data->resizeMode == Stretch ) stretchSelection( re->oldSize(), re->size() ); updateDisplay(); break; } case QEvent::Enter: { widgetEnterEvent( event ); break; } case QEvent::Leave: { widgetLeaveEvent( event ); break; } case QEvent::MouseButtonPress: { widgetMousePressEvent( static_cast( event ) ); break; } case QEvent::MouseButtonRelease: { widgetMouseReleaseEvent( static_cast( event ) ); break; } case QEvent::MouseButtonDblClick: { widgetMouseDoubleClickEvent( static_cast( event ) ); break; } case QEvent::MouseMove: { widgetMouseMoveEvent( static_cast( event ) ); break; } case QEvent::KeyPress: { widgetKeyPressEvent( static_cast( event ) ); break; } case QEvent::KeyRelease: { widgetKeyReleaseEvent( static_cast( event ) ); break; } case QEvent::Wheel: { widgetWheelEvent( static_cast( event ) ); break; } default: break; } } return false; } /*! Handle a mouse press event for the observed widget. \param mouseEvent Mouse event \sa eventFilter(), widgetMouseReleaseEvent(), widgetMouseDoubleClickEvent(), widgetMouseMoveEvent(), widgetWheelEvent(), widgetKeyPressEvent(), widgetKeyReleaseEvent() */ void QwtPicker::widgetMousePressEvent( QMouseEvent *mouseEvent ) { transition( mouseEvent ); } /*! Handle a mouse move event for the observed widget. \param mouseEvent Mouse event \sa eventFilter(), widgetMousePressEvent(), widgetMouseReleaseEvent(), widgetMouseDoubleClickEvent(), widgetWheelEvent(), widgetKeyPressEvent(), widgetKeyReleaseEvent() */ void QwtPicker::widgetMouseMoveEvent( QMouseEvent *mouseEvent ) { if ( pickArea().contains( mouseEvent->pos() ) ) d_data->trackerPosition = mouseEvent->pos(); else d_data->trackerPosition = QPoint( -1, -1 ); if ( !isActive() ) updateDisplay(); transition( mouseEvent ); } /*! Handle a enter event for the observed widget. \param event Qt event \sa eventFilter(), widgetMousePressEvent(), widgetMouseReleaseEvent(), widgetMouseDoubleClickEvent(), widgetWheelEvent(), widgetKeyPressEvent(), widgetKeyReleaseEvent() */ void QwtPicker::widgetEnterEvent( QEvent *event ) { transition( event ); } /*! Handle a leave event for the observed widget. \param event Qt event \sa eventFilter(), widgetMousePressEvent(), widgetMouseReleaseEvent(), widgetMouseDoubleClickEvent(), widgetWheelEvent(), widgetKeyPressEvent(), widgetKeyReleaseEvent() */ void QwtPicker::widgetLeaveEvent( QEvent *event ) { transition( event ); d_data->trackerPosition = QPoint( -1, -1 ); if ( !isActive() ) updateDisplay(); } /*! Handle a mouse release event for the observed widget. \param mouseEvent Mouse event \sa eventFilter(), widgetMousePressEvent(), widgetMouseDoubleClickEvent(), widgetMouseMoveEvent(), widgetWheelEvent(), widgetKeyPressEvent(), widgetKeyReleaseEvent() */ void QwtPicker::widgetMouseReleaseEvent( QMouseEvent *mouseEvent ) { transition( mouseEvent ); } /*! Handle mouse double click event for the observed widget. \param mouseEvent Mouse event \sa eventFilter(), widgetMousePressEvent(), widgetMouseReleaseEvent(), widgetMouseMoveEvent(), widgetWheelEvent(), widgetKeyPressEvent(), widgetKeyReleaseEvent() */ void QwtPicker::widgetMouseDoubleClickEvent( QMouseEvent *mouseEvent ) { transition( mouseEvent ); } /*! Handle a wheel event for the observed widget. Move the last point of the selection in case of isActive() == true \param wheelEvent Wheel event \sa eventFilter(), widgetMousePressEvent(), widgetMouseReleaseEvent(), widgetMouseDoubleClickEvent(), widgetMouseMoveEvent(), widgetKeyPressEvent(), widgetKeyReleaseEvent() */ void QwtPicker::widgetWheelEvent( QWheelEvent *wheelEvent ) { if ( pickArea().contains( wheelEvent->pos() ) ) d_data->trackerPosition = wheelEvent->pos(); else d_data->trackerPosition = QPoint( -1, -1 ); updateDisplay(); transition( wheelEvent ); } /*! Handle a key press event for the observed widget. Selections can be completely done by the keyboard. The arrow keys move the cursor, the abort key aborts a selection. All other keys are handled by the current state machine. \param keyEvent Key event \sa eventFilter(), widgetMousePressEvent(), widgetMouseReleaseEvent(), widgetMouseDoubleClickEvent(), widgetMouseMoveEvent(), widgetWheelEvent(), widgetKeyReleaseEvent(), stateMachine(), QwtEventPattern::KeyPatternCode */ void QwtPicker::widgetKeyPressEvent( QKeyEvent *keyEvent ) { int dx = 0; int dy = 0; int offset = 1; if ( keyEvent->isAutoRepeat() ) offset = 5; if ( keyMatch( KeyLeft, keyEvent ) ) dx = -offset; else if ( keyMatch( KeyRight, keyEvent ) ) dx = offset; else if ( keyMatch( KeyUp, keyEvent ) ) dy = -offset; else if ( keyMatch( KeyDown, keyEvent ) ) dy = offset; else if ( keyMatch( KeyAbort, keyEvent ) ) { reset(); } else transition( keyEvent ); if ( dx != 0 || dy != 0 ) { const QRect rect = pickArea().boundingRect().toRect(); const QPoint pos = parentWidget()->mapFromGlobal( QCursor::pos() ); int x = pos.x() + dx; x = qMax( rect.left(), x ); x = qMin( rect.right(), x ); int y = pos.y() + dy; y = qMax( rect.top(), y ); y = qMin( rect.bottom(), y ); QCursor::setPos( parentWidget()->mapToGlobal( QPoint( x, y ) ) ); } } /*! Handle a key release event for the observed widget. Passes the event to the state machine. \param keyEvent Key event \sa eventFilter(), widgetMousePressEvent(), widgetMouseReleaseEvent(), widgetMouseDoubleClickEvent(), widgetMouseMoveEvent(), widgetWheelEvent(), widgetKeyPressEvent(), stateMachine() */ void QwtPicker::widgetKeyReleaseEvent( QKeyEvent *keyEvent ) { transition( keyEvent ); } /*! Passes an event to the state machine and executes the resulting commands. Append and Move commands use the current position of the cursor ( QCursor::pos() ). \param event Event */ void QwtPicker::transition( const QEvent *event ) { if ( !d_data->stateMachine ) return; const QList commandList = d_data->stateMachine->transition( *this, event ); QPoint pos; switch ( event->type() ) { case QEvent::MouseButtonDblClick: case QEvent::MouseButtonPress: case QEvent::MouseButtonRelease: case QEvent::MouseMove: { const QMouseEvent *me = static_cast< const QMouseEvent * >( event ); pos = me->pos(); break; } default: pos = parentWidget()->mapFromGlobal( QCursor::pos() ); } for ( int i = 0; i < commandList.count(); i++ ) { switch ( commandList[i] ) { case QwtPickerMachine::Begin: { begin(); break; } case QwtPickerMachine::Append: { append( pos ); break; } case QwtPickerMachine::Move: { move( pos ); break; } case QwtPickerMachine::Remove: { remove(); break; } case QwtPickerMachine::End: { end(); break; } } } } /*! Open a selection setting the state to active \sa isActive(), end(), append(), move() */ void QwtPicker::begin() { if ( d_data->isActive ) return; d_data->pickedPoints.resize( 0 ); d_data->isActive = true; Q_EMIT activated( true ); if ( trackerMode() != AlwaysOff ) { if ( d_data->trackerPosition.x() < 0 || d_data->trackerPosition.y() < 0 ) { QWidget *w = parentWidget(); if ( w ) d_data->trackerPosition = w->mapFromGlobal( QCursor::pos() ); } } updateDisplay(); setMouseTracking( true ); } /*! \brief Close a selection setting the state to inactive. The selection is validated and maybe fixed by accept(). \param ok If true, complete the selection and emit a selected signal otherwise discard the selection. \return true if the selection is accepted, false otherwise \sa isActive(), begin(), append(), move(), selected(), accept() */ bool QwtPicker::end( bool ok ) { if ( d_data->isActive ) { setMouseTracking( false ); d_data->isActive = false; Q_EMIT activated( false ); if ( trackerMode() == ActiveOnly ) d_data->trackerPosition = QPoint( -1, -1 ); if ( ok ) ok = accept( d_data->pickedPoints ); if ( ok ) Q_EMIT selected( d_data->pickedPoints ); else d_data->pickedPoints.resize( 0 ); updateDisplay(); } else ok = false; return ok; } /*! Reset the state machine and terminate ( end(false) ) the selection */ void QwtPicker::reset() { if ( d_data->stateMachine ) d_data->stateMachine->reset(); if ( isActive() ) end( false ); } /*! Append a point to the selection and update rubber band and tracker. The appended() signal is emitted. \param pos Additional point \sa isActive(), begin(), end(), move(), appended() */ void QwtPicker::append( const QPoint &pos ) { if ( d_data->isActive ) { const int idx = d_data->pickedPoints.count(); d_data->pickedPoints.resize( idx + 1 ); d_data->pickedPoints[idx] = pos; updateDisplay(); Q_EMIT appended( pos ); } } /*! Move the last point of the selection The moved() signal is emitted. \param pos New position \sa isActive(), begin(), end(), append() */ void QwtPicker::move( const QPoint &pos ) { if ( d_data->isActive ) { const int idx = d_data->pickedPoints.count() - 1; if ( idx >= 0 ) { if ( d_data->pickedPoints[idx] != pos ) { d_data->pickedPoints[idx] = pos; updateDisplay(); Q_EMIT moved( pos ); } } } } /*! Remove the last point of the selection The removed() signal is emitted. \sa isActive(), begin(), end(), append(), move() */ void QwtPicker::remove() { if ( d_data->isActive ) { const int idx = d_data->pickedPoints.count() - 1; if ( idx > 0 ) { const int idx = d_data->pickedPoints.count(); const QPoint pos = d_data->pickedPoints[idx - 1]; d_data->pickedPoints.resize( idx - 1 ); updateDisplay(); Q_EMIT removed( pos ); } } } /*! \brief Validate and fix up the selection Accepts all selections unmodified \param selection Selection to validate and fix up \return true, when accepted, false otherwise */ bool QwtPicker::accept( QPolygon &selection ) const { Q_UNUSED( selection ); return true; } /*! A picker is active between begin() and end(). \return true if the selection is active. */ bool QwtPicker::isActive() const { return d_data->isActive; } /*! Return the points, that have been collected so far. The selection() is calculated from the pickedPoints() in adjustedPoints(). \return Picked points */ const QPolygon &QwtPicker::pickedPoints() const { return d_data->pickedPoints; } /*! Scale the selection by the ratios of oldSize and newSize The changed() signal is emitted. \param oldSize Previous size \param newSize Current size \sa ResizeMode, setResizeMode(), resizeMode() */ void QwtPicker::stretchSelection( const QSize &oldSize, const QSize &newSize ) { if ( oldSize.isEmpty() ) { // avoid division by zero. But scaling for small sizes also // doesn't make much sense, because of rounding losses. TODO ... return; } const double xRatio = double( newSize.width() ) / double( oldSize.width() ); const double yRatio = double( newSize.height() ) / double( oldSize.height() ); for ( int i = 0; i < int( d_data->pickedPoints.count() ); i++ ) { QPoint &p = d_data->pickedPoints[i]; p.setX( qRound( p.x() * xRatio ) ); p.setY( qRound( p.y() * yRatio ) ); Q_EMIT changed( d_data->pickedPoints ); } } /*! Set mouse tracking for the observed widget. In case of enable is true, the previous value is saved, that is restored when enable is false. \warning Even when enable is false, mouse tracking might be restored to true. When mouseTracking for the observed widget has been changed directly by QWidget::setMouseTracking while mouse tracking has been set to true, this value can't be restored. */ void QwtPicker::setMouseTracking( bool enable ) { QWidget *widget = parentWidget(); if ( !widget ) return; if ( enable ) { d_data->mouseTracking = widget->hasMouseTracking(); widget->setMouseTracking( true ); } else { widget->setMouseTracking( d_data->mouseTracking ); } } /*! Find the area of the observed widget, where selection might happen. \return parentWidget()->contentsRect() */ QPainterPath QwtPicker::pickArea() const { QPainterPath path; const QWidget *widget = parentWidget(); if ( widget ) path.addRect( widget->contentsRect() ); return path; } //! Update the state of rubber band and tracker label void QwtPicker::updateDisplay() { QWidget *w = parentWidget(); bool showRubberband = false; bool showTracker = false; if ( w && w->isVisible() && d_data->enabled ) { if ( rubberBand() != NoRubberBand && isActive() && rubberBandPen().style() != Qt::NoPen ) { showRubberband = true; } if ( trackerMode() == AlwaysOn || ( trackerMode() == ActiveOnly && isActive() ) ) { if ( trackerPen() != Qt::NoPen && !trackerRect( QFont() ).isEmpty() ) { showTracker = true; } } } QPointer< QwtPickerRubberband > &rw = d_data->rubberBandOverlay; if ( showRubberband ) { if ( rw.isNull() ) { rw = new QwtPickerRubberband( this, NULL ); // NULL -> no extra event filter rw->setObjectName( "PickerRubberBand" ); rw->setParent( w ); rw->resize( w->size() ); } if ( d_data->rubberBand <= RectRubberBand ) rw->setMaskMode( QwtWidgetOverlay::MaskHint ); else rw->setMaskMode( QwtWidgetOverlay::AlphaMask ); rw->updateOverlay(); } else { if ( d_data->openGL ) { // Qt 4.8 crashes for a delete if ( !rw.isNull() ) { rw->hide(); rw->deleteLater(); rw = NULL; } } else { delete rw; } } QPointer< QwtPickerTracker > &tw = d_data->trackerOverlay; if ( showTracker ) { if ( tw.isNull() ) { tw = new QwtPickerTracker( this, NULL ); // NULL -> no extra event filter tw->setObjectName( "PickerTracker" ); tw->setParent( w ); tw->resize( w->size() ); } tw->setFont( d_data->trackerFont ); tw->updateOverlay(); } else { if ( d_data->openGL ) { // Qt 4.8 crashes for a delete if ( !tw.isNull() ) { tw->hide(); tw->deleteLater(); tw = NULL; } } else { delete tw; } } } //! \return Overlay displaying the rubber band const QwtWidgetOverlay *QwtPicker::rubberBandOverlay() const { return d_data->rubberBandOverlay; } //! \return Overlay displaying the tracker text const QwtWidgetOverlay *QwtPicker::trackerOverlay() const { return d_data->trackerOverlay; } connectome-workbench-1.2.3+git41-gc4c6c90/src/Qwt/qwt_picker.h000066400000000000000000000224761300200146000236530ustar00rootroot00000000000000/* -*- mode: C++ ; c-file-style: "stroustrup" -*- ***************************** * Qwt Widget Library * Copyright (C) 1997 Josef Wilgen * Copyright (C) 2002 Uwe Rathmann * * This library is free software; you can redistribute it and/or * modify it under the terms of the Qwt License, Version 1.0 *****************************************************************************/ #ifndef QWT_PICKER #define QWT_PICKER 1 #include "qwt_global.h" #include "qwt_text.h" #include "qwt_event_pattern.h" #include #include #include #include #include class QWidget; class QMouseEvent; class QWheelEvent; class QKeyEvent; class QwtPickerMachine; class QwtWidgetOverlay; /*! \brief QwtPicker provides selections on a widget QwtPicker filters all enter, leave, mouse and keyboard events of a widget and translates them into an array of selected points. The way how the points are collected depends on type of state machine that is connected to the picker. Qwt offers a couple of predefined state machines for selecting: - Nothing\n QwtPickerTrackerMachine - Single points\n QwtPickerClickPointMachine, QwtPickerDragPointMachine - Rectangles\n QwtPickerClickRectMachine, QwtPickerDragRectMachine - Polygons\n QwtPickerPolygonMachine While these state machines cover the most common ways to collect points it is also possible to implement individual machines as well. QwtPicker translates the picked points into a selection using the adjustedPoints() method. adjustedPoints() is intended to be reimplemented to fix up the selection according to application specific requirements. (F.e. when an application accepts rectangles of a fixed aspect ratio only.) Optionally QwtPicker support the process of collecting points by a rubber band and tracker displaying a text for the current mouse position. \par Example \verbatim #include #include QwtPicker *picker = new QwtPicker(widget); picker->setStateMachine(new QwtPickerDragRectMachine); picker->setTrackerMode(QwtPicker::ActiveOnly); picker->setRubberBand(QwtPicker::RectRubberBand); \endverbatim\n The state machine triggers the following commands: - begin()\n Activate/Initialize the selection. - append()\n Add a new point - move() \n Change the position of the last point. - remove()\n Remove the last point. - end()\n Terminate the selection and call accept to validate the picked points. The picker is active (isActive()), between begin() and end(). In active state the rubber band is displayed, and the tracker is visible in case of trackerMode is ActiveOnly or AlwaysOn. The cursor can be moved using the arrow keys. All selections can be aborted using the abort key. (QwtEventPattern::KeyPatternCode) \warning In case of QWidget::NoFocus the focus policy of the observed widget is set to QWidget::WheelFocus and mouse tracking will be manipulated while the picker is active, or if trackerMode() is AlwayOn. */ class QWT_EXPORT QwtPicker: public QObject, public QwtEventPattern { Q_OBJECT Q_ENUMS( RubberBand DisplayMode ResizeMode ) Q_PROPERTY( bool isEnabled READ isEnabled WRITE setEnabled ) Q_PROPERTY( ResizeMode resizeMode READ resizeMode WRITE setResizeMode ) Q_PROPERTY( DisplayMode trackerMode READ trackerMode WRITE setTrackerMode ) Q_PROPERTY( QPen trackerPen READ trackerPen WRITE setTrackerPen ) Q_PROPERTY( QFont trackerFont READ trackerFont WRITE setTrackerFont ) Q_PROPERTY( RubberBand rubberBand READ rubberBand WRITE setRubberBand ) Q_PROPERTY( QPen rubberBandPen READ rubberBandPen WRITE setRubberBandPen ) public: /*! Rubber band style The default value is QwtPicker::NoRubberBand. \sa setRubberBand(), rubberBand() */ enum RubberBand { //! No rubberband. NoRubberBand = 0, //! A horizontal line ( only for QwtPickerMachine::PointSelection ) HLineRubberBand, //! A vertical line ( only for QwtPickerMachine::PointSelection ) VLineRubberBand, //! A crosshair ( only for QwtPickerMachine::PointSelection ) CrossRubberBand, //! A rectangle ( only for QwtPickerMachine::RectSelection ) RectRubberBand, //! An ellipse ( only for QwtPickerMachine::RectSelection ) EllipseRubberBand, //! A polygon ( only for QwtPickerMachine::PolygonSelection ) PolygonRubberBand, /*! Values >= UserRubberBand can be used to define additional rubber bands. */ UserRubberBand = 100 }; /*! \brief Display mode \sa setTrackerMode(), trackerMode(), isActive() */ enum DisplayMode { //! Display never AlwaysOff, //! Display always AlwaysOn, //! Display only when the selection is active ActiveOnly }; /*! Controls what to do with the selected points of an active selection when the observed widget is resized. The default value is QwtPicker::Stretch. \sa setResizeMode() */ enum ResizeMode { //! All points are scaled according to the new size, Stretch, //! All points remain unchanged. KeepSize }; explicit QwtPicker( QWidget *parent ); explicit QwtPicker( RubberBand rubberBand, DisplayMode trackerMode, QWidget * ); virtual ~QwtPicker(); void setStateMachine( QwtPickerMachine * ); const QwtPickerMachine *stateMachine() const; QwtPickerMachine *stateMachine(); void setRubberBand( RubberBand ); RubberBand rubberBand() const; void setTrackerMode( DisplayMode ); DisplayMode trackerMode() const; void setResizeMode( ResizeMode ); ResizeMode resizeMode() const; void setRubberBandPen( const QPen & ); QPen rubberBandPen() const; void setTrackerPen( const QPen & ); QPen trackerPen() const; void setTrackerFont( const QFont & ); QFont trackerFont() const; bool isEnabled() const; bool isActive() const; virtual bool eventFilter( QObject *, QEvent * ); QWidget *parentWidget(); const QWidget *parentWidget() const; virtual QPainterPath pickArea() const; virtual void drawRubberBand( QPainter * ) const; virtual void drawTracker( QPainter * ) const; virtual QRegion rubberBandMask() const; virtual QwtText trackerText( const QPoint &pos ) const; QPoint trackerPosition() const; virtual QRect trackerRect( const QFont & ) const; QPolygon selection() const; public Q_SLOTS: void setEnabled( bool ); Q_SIGNALS: /*! A signal indicating, when the picker has been activated. Together with setEnabled() it can be used to implement selections with more than one picker. \param on True, when the picker has been activated */ void activated( bool on ); /*! A signal emitting the selected points, at the end of a selection. \param polygon Selected points */ void selected( const QPolygon &polygon ); /*! A signal emitted when a point has been appended to the selection \param pos Position of the appended point. \sa append(). moved() */ void appended( const QPoint &pos ); /*! A signal emitted whenever the last appended point of the selection has been moved. \param pos Position of the moved last point of the selection. \sa move(), appended() */ void moved( const QPoint &pos ); /*! A signal emitted whenever the last appended point of the selection has been removed. \param pos Position of the point, that has been removed \sa remove(), appended() */ void removed( const QPoint &pos ); /*! A signal emitted when the active selection has been changed. This might happen when the observed widget is resized. \param selection Changed selection \sa stretchSelection() */ void changed( const QPolygon &selection ); protected: virtual QPolygon adjustedPoints( const QPolygon & ) const; virtual void transition( const QEvent * ); virtual void begin(); virtual void append( const QPoint & ); virtual void move( const QPoint & ); virtual void remove(); virtual bool end( bool ok = true ); virtual bool accept( QPolygon & ) const; virtual void reset(); virtual void widgetMousePressEvent( QMouseEvent * ); virtual void widgetMouseReleaseEvent( QMouseEvent * ); virtual void widgetMouseDoubleClickEvent( QMouseEvent * ); virtual void widgetMouseMoveEvent( QMouseEvent * ); virtual void widgetWheelEvent( QWheelEvent * ); virtual void widgetKeyPressEvent( QKeyEvent * ); virtual void widgetKeyReleaseEvent( QKeyEvent * ); virtual void widgetEnterEvent( QEvent * ); virtual void widgetLeaveEvent( QEvent * ); virtual void stretchSelection( const QSize &oldSize, const QSize &newSize ); virtual void updateDisplay(); const QwtWidgetOverlay *rubberBandOverlay() const; const QwtWidgetOverlay *trackerOverlay() const; const QPolygon &pickedPoints() const; private: void init( QWidget *, RubberBand rubberBand, DisplayMode trackerMode ); void setMouseTracking( bool ); class PrivateData; PrivateData *d_data; }; #endif connectome-workbench-1.2.3+git41-gc4c6c90/src/Qwt/qwt_picker_machine.cpp000066400000000000000000000333251300200146000256650ustar00rootroot00000000000000/* -*- mode: C++ ; c-file-style: "stroustrup" -*- ***************************** * Qwt Widget Library * Copyright (C) 1997 Josef Wilgen * Copyright (C) 2002 Uwe Rathmann * * This library is free software; you can redistribute it and/or * modify it under the terms of the Qwt License, Version 1.0 *****************************************************************************/ #include "qwt_picker_machine.h" #include "qwt_event_pattern.h" #include //! Constructor QwtPickerMachine::QwtPickerMachine( SelectionType type ): d_selectionType( type ), d_state( 0 ) { } //! Destructor QwtPickerMachine::~QwtPickerMachine() { } //! Return the selection type QwtPickerMachine::SelectionType QwtPickerMachine::selectionType() const { return d_selectionType; } //! Return the current state int QwtPickerMachine::state() const { return d_state; } //! Change the current state void QwtPickerMachine::setState( int state ) { d_state = state; } //! Set the current state to 0. void QwtPickerMachine::reset() { setState( 0 ); } //! Constructor QwtPickerTrackerMachine::QwtPickerTrackerMachine(): QwtPickerMachine( NoSelection ) { } //! Transition QList QwtPickerTrackerMachine::transition( const QwtEventPattern &, const QEvent *e ) { QList cmdList; switch ( e->type() ) { case QEvent::Enter: case QEvent::MouseMove: { if ( state() == 0 ) { cmdList += Begin; cmdList += Append; setState( 1 ); } else { cmdList += Move; } break; } case QEvent::Leave: { cmdList += Remove; cmdList += End; setState( 0 ); } default: break; } return cmdList; } //! Constructor QwtPickerClickPointMachine::QwtPickerClickPointMachine(): QwtPickerMachine( PointSelection ) { } //! Transition QList QwtPickerClickPointMachine::transition( const QwtEventPattern &eventPattern, const QEvent *event ) { QList cmdList; switch ( event->type() ) { case QEvent::MouseButtonPress: { if ( eventPattern.mouseMatch( QwtEventPattern::MouseSelect1, static_cast( event ) ) ) { cmdList += Begin; cmdList += Append; cmdList += End; } break; } case QEvent::KeyPress: { const QKeyEvent *keyEvent = static_cast ( event ); if ( eventPattern.keyMatch( QwtEventPattern::KeySelect1, keyEvent ) ) { if ( !keyEvent->isAutoRepeat() ) { cmdList += Begin; cmdList += Append; cmdList += End; } } break; } default: break; } return cmdList; } //! Constructor QwtPickerDragPointMachine::QwtPickerDragPointMachine(): QwtPickerMachine( PointSelection ) { } //! Transition QList QwtPickerDragPointMachine::transition( const QwtEventPattern &eventPattern, const QEvent *event ) { QList cmdList; switch ( event->type() ) { case QEvent::MouseButtonPress: { if ( eventPattern.mouseMatch( QwtEventPattern::MouseSelect1, static_cast( event ) ) ) { if ( state() == 0 ) { cmdList += Begin; cmdList += Append; setState( 1 ); } } break; } case QEvent::MouseMove: case QEvent::Wheel: { if ( state() != 0 ) cmdList += Move; break; } case QEvent::MouseButtonRelease: { if ( state() != 0 ) { cmdList += End; setState( 0 ); } break; } case QEvent::KeyPress: { const QKeyEvent *keyEvent = static_cast ( event ); if ( eventPattern.keyMatch( QwtEventPattern::KeySelect1, keyEvent ) ) { if ( !keyEvent->isAutoRepeat() ) { if ( state() == 0 ) { cmdList += Begin; cmdList += Append; setState( 1 ); } else { cmdList += End; setState( 0 ); } } } break; } default: break; } return cmdList; } //! Constructor QwtPickerClickRectMachine::QwtPickerClickRectMachine(): QwtPickerMachine( RectSelection ) { } //! Transition QList QwtPickerClickRectMachine::transition( const QwtEventPattern &eventPattern, const QEvent *event ) { QList cmdList; switch ( event->type() ) { case QEvent::MouseButtonPress: { if ( eventPattern.mouseMatch( QwtEventPattern::MouseSelect1, static_cast( event ) ) ) { switch ( state() ) { case 0: { cmdList += Begin; cmdList += Append; setState( 1 ); break; } case 1: { // Uh, strange we missed the MouseButtonRelease break; } default: { cmdList += End; setState( 0 ); } } } break; } case QEvent::MouseMove: case QEvent::Wheel: { if ( state() != 0 ) cmdList += Move; break; } case QEvent::MouseButtonRelease: { if ( eventPattern.mouseMatch( QwtEventPattern::MouseSelect1, static_cast( event ) ) ) { if ( state() == 1 ) { cmdList += Append; setState( 2 ); } } break; } case QEvent::KeyPress: { const QKeyEvent *keyEvent = static_cast ( event ); if ( eventPattern.keyMatch( QwtEventPattern::KeySelect1, keyEvent ) ) { if ( !keyEvent->isAutoRepeat() ) { if ( state() == 0 ) { cmdList += Begin; cmdList += Append; setState( 1 ); } else { if ( state() == 1 ) { cmdList += Append; setState( 2 ); } else if ( state() == 2 ) { cmdList += End; setState( 0 ); } } } } break; } default: break; } return cmdList; } //! Constructor QwtPickerDragRectMachine::QwtPickerDragRectMachine(): QwtPickerMachine( RectSelection ) { } //! Transition QList QwtPickerDragRectMachine::transition( const QwtEventPattern &eventPattern, const QEvent *event ) { QList cmdList; switch ( event->type() ) { case QEvent::MouseButtonPress: { if ( eventPattern.mouseMatch( QwtEventPattern::MouseSelect1, static_cast( event ) ) ) { if ( state() == 0 ) { cmdList += Begin; cmdList += Append; cmdList += Append; setState( 2 ); } } break; } case QEvent::MouseMove: case QEvent::Wheel: { if ( state() != 0 ) cmdList += Move; break; } case QEvent::MouseButtonRelease: { if ( state() == 2 ) { cmdList += End; setState( 0 ); } break; } case QEvent::KeyPress: { if ( eventPattern.keyMatch( QwtEventPattern::KeySelect1, static_cast ( event ) ) ) { if ( state() == 0 ) { cmdList += Begin; cmdList += Append; cmdList += Append; setState( 2 ); } else { cmdList += End; setState( 0 ); } } break; } default: break; } return cmdList; } //! Constructor QwtPickerPolygonMachine::QwtPickerPolygonMachine(): QwtPickerMachine( PolygonSelection ) { } //! Transition QList QwtPickerPolygonMachine::transition( const QwtEventPattern &eventPattern, const QEvent *event ) { QList cmdList; switch ( event->type() ) { case QEvent::MouseButtonPress: { if ( eventPattern.mouseMatch( QwtEventPattern::MouseSelect1, static_cast( event ) ) ) { if ( state() == 0 ) { cmdList += Begin; cmdList += Append; cmdList += Append; setState( 1 ); } else { cmdList += Append; } } if ( eventPattern.mouseMatch( QwtEventPattern::MouseSelect2, static_cast( event ) ) ) { if ( state() == 1 ) { cmdList += End; setState( 0 ); } } break; } case QEvent::MouseMove: case QEvent::Wheel: { if ( state() != 0 ) cmdList += Move; break; } case QEvent::KeyPress: { const QKeyEvent *keyEvent = static_cast ( event ); if ( eventPattern.keyMatch( QwtEventPattern::KeySelect1, keyEvent ) ) { if ( !keyEvent->isAutoRepeat() ) { if ( state() == 0 ) { cmdList += Begin; cmdList += Append; cmdList += Append; setState( 1 ); } else { cmdList += Append; } } } else if ( eventPattern.keyMatch( QwtEventPattern::KeySelect2, keyEvent ) ) { if ( !keyEvent->isAutoRepeat() ) { if ( state() == 1 ) { cmdList += End; setState( 0 ); } } } break; } default: break; } return cmdList; } //! Constructor QwtPickerDragLineMachine::QwtPickerDragLineMachine(): QwtPickerMachine( PolygonSelection ) { } //! Transition QList QwtPickerDragLineMachine::transition( const QwtEventPattern &eventPattern, const QEvent *event ) { QList cmdList; switch( event->type() ) { case QEvent::MouseButtonPress: { if ( eventPattern.mouseMatch( QwtEventPattern::MouseSelect1, static_cast( event ) ) ) { if ( state() == 0 ) { cmdList += Begin; cmdList += Append; cmdList += Append; setState( 1 ); } } break; } case QEvent::KeyPress: { if ( eventPattern.keyMatch( QwtEventPattern::KeySelect1, static_cast ( event ) ) ) { if ( state() == 0 ) { cmdList += Begin; cmdList += Append; cmdList += Append; setState( 1 ); } else { cmdList += End; setState( 0 ); } } break; } case QEvent::MouseMove: case QEvent::Wheel: { if ( state() != 0 ) cmdList += Move; break; } case QEvent::MouseButtonRelease: { if ( state() != 0 ) { cmdList += End; setState( 0 ); } } default: break; } return cmdList; } connectome-workbench-1.2.3+git41-gc4c6c90/src/Qwt/qwt_picker_machine.h000066400000000000000000000131361300200146000253300ustar00rootroot00000000000000/* -*- mode: C++ ; c-file-style: "stroustrup" -*- ***************************** * Qwt Widget Library * Copyright (C) 1997 Josef Wilgen * Copyright (C) 2002 Uwe Rathmann * * This library is free software; you can redistribute it and/or * modify it under the terms of the Qwt License, Version 1.0 *****************************************************************************/ #ifndef QWT_PICKER_MACHINE #define QWT_PICKER_MACHINE 1 #include "qwt_global.h" #include class QEvent; class QwtEventPattern; /*! \brief A state machine for QwtPicker selections QwtPickerMachine accepts key and mouse events and translates them into selection commands. \sa QwtEventPattern::MousePatternCode, QwtEventPattern::KeyPatternCode */ class QWT_EXPORT QwtPickerMachine { public: /*! Type of a selection. \sa selectionType() */ enum SelectionType { //! The state machine not usable for any type of selection. NoSelection = -1, //! The state machine is for selecting a single point. PointSelection, //! The state machine is for selecting a rectangle (2 points). RectSelection, //! The state machine is for selecting a polygon (many points). PolygonSelection }; //! Commands - the output of a state machine enum Command { Begin, Append, Move, Remove, End }; QwtPickerMachine( SelectionType ); virtual ~QwtPickerMachine(); //! Transition virtual QList transition( const QwtEventPattern &, const QEvent * ) = 0; void reset(); int state() const; void setState( int ); SelectionType selectionType() const; private: const SelectionType d_selectionType; int d_state; }; /*! \brief A state machine for indicating mouse movements QwtPickerTrackerMachine supports displaying information corresponding to mouse movements, but is not intended for selecting anything. Begin/End are related to Enter/Leave events. */ class QWT_EXPORT QwtPickerTrackerMachine: public QwtPickerMachine { public: QwtPickerTrackerMachine(); virtual QList transition( const QwtEventPattern &, const QEvent * ); }; /*! \brief A state machine for point selections Pressing QwtEventPattern::MouseSelect1 or QwtEventPattern::KeySelect1 selects a point. \sa QwtEventPattern::MousePatternCode, QwtEventPattern::KeyPatternCode */ class QWT_EXPORT QwtPickerClickPointMachine: public QwtPickerMachine { public: QwtPickerClickPointMachine(); virtual QList transition( const QwtEventPattern &, const QEvent * ); }; /*! \brief A state machine for point selections Pressing QwtEventPattern::MouseSelect1 or QwtEventPattern::KeySelect1 starts the selection, releasing QwtEventPattern::MouseSelect1 or a second press of QwtEventPattern::KeySelect1 terminates it. */ class QWT_EXPORT QwtPickerDragPointMachine: public QwtPickerMachine { public: QwtPickerDragPointMachine(); virtual QList transition( const QwtEventPattern &, const QEvent * ); }; /*! \brief A state machine for rectangle selections Pressing QwtEventPattern::MouseSelect1 starts the selection, releasing it selects the first point. Pressing it again selects the second point and terminates the selection. Pressing QwtEventPattern::KeySelect1 also starts the selection, a second press selects the first point. A third one selects the second point and terminates the selection. \sa QwtEventPattern::MousePatternCode, QwtEventPattern::KeyPatternCode */ class QWT_EXPORT QwtPickerClickRectMachine: public QwtPickerMachine { public: QwtPickerClickRectMachine(); virtual QList transition( const QwtEventPattern &, const QEvent * ); }; /*! \brief A state machine for rectangle selections Pressing QwtEventPattern::MouseSelect1 selects the first point, releasing it the second point. Pressing QwtEventPattern::KeySelect1 also selects the first point, a second press selects the second point and terminates the selection. \sa QwtEventPattern::MousePatternCode, QwtEventPattern::KeyPatternCode */ class QWT_EXPORT QwtPickerDragRectMachine: public QwtPickerMachine { public: QwtPickerDragRectMachine(); virtual QList transition( const QwtEventPattern &, const QEvent * ); }; /*! \brief A state machine for line selections Pressing QwtEventPattern::MouseSelect1 selects the first point, releasing it the second point. Pressing QwtEventPattern::KeySelect1 also selects the first point, a second press selects the second point and terminates the selection. A common use case of QwtPickerDragLineMachine are pickers for distance measurements. \sa QwtEventPattern::MousePatternCode, QwtEventPattern::KeyPatternCode */ class QWT_EXPORT QwtPickerDragLineMachine: public QwtPickerMachine { public: QwtPickerDragLineMachine(); virtual QList transition( const QwtEventPattern &, const QEvent * ); }; /*! \brief A state machine for polygon selections Pressing QwtEventPattern::MouseSelect1 or QwtEventPattern::KeySelect1 starts the selection and selects the first point, or appends a point. Pressing QwtEventPattern::MouseSelect2 or QwtEventPattern::KeySelect2 appends the last point and terminates the selection. \sa QwtEventPattern::MousePatternCode, QwtEventPattern::KeyPatternCode */ class QWT_EXPORT QwtPickerPolygonMachine: public QwtPickerMachine { public: QwtPickerPolygonMachine(); virtual QList transition( const QwtEventPattern &, const QEvent * ); }; #endif connectome-workbench-1.2.3+git41-gc4c6c90/src/Qwt/qwt_pixel_matrix.cpp000066400000000000000000000021331300200146000254220ustar00rootroot00000000000000/* -*- mode: C++ ; c-file-style: "stroustrup" -*- ***************************** * Qwt Widget Library * Copyright (C) 1997 Josef Wilgen * Copyright (C) 2002 Uwe Rathmann * * This library is free software; you can redistribute it and/or * modify it under the terms of the Qwt License, Version 1.0 *****************************************************************************/ #include "qwt_pixel_matrix.h" /*! \brief Constructor \param rect Bounding rectangle for the matrix */ QwtPixelMatrix::QwtPixelMatrix( const QRect& rect ): QBitArray( qMax( rect.width() * rect.height(), 0 ) ), d_rect( rect ) { } //! Destructor QwtPixelMatrix::~QwtPixelMatrix() { } /*! Set the bounding rectangle of the matrix \param rect Bounding rectangle \note All bits are cleared */ void QwtPixelMatrix::setRect( const QRect& rect ) { if ( rect != d_rect ) { d_rect = rect; const int sz = qMax( rect.width() * rect.height(), 0 ); resize( sz ); } fill( false ); } //! \return Bounding rectangle QRect QwtPixelMatrix::rect() const { return d_rect; } connectome-workbench-1.2.3+git41-gc4c6c90/src/Qwt/qwt_pixel_matrix.h000066400000000000000000000044311300200146000250720ustar00rootroot00000000000000/* -*- mode: C++ ; c-file-style: "stroustrup" -*- ***************************** * Qwt Widget Library * Copyright (C) 1997 Josef Wilgen * Copyright (C) 2002 Uwe Rathmann * * This library is free software; you can redistribute it and/or * modify it under the terms of the Qwt License, Version 1.0 *****************************************************************************/ #ifndef QWT_PIXEL_MATRIX_H #define QWT_PIXEL_MATRIX_H #include "qwt_global.h" #include #include /*! \brief A bit field corresponding to the pixels of a rectangle QwtPixelMatrix is intended to filter out duplicates in an unsorted array of points. */ class QWT_EXPORT QwtPixelMatrix: public QBitArray { public: QwtPixelMatrix( const QRect& rect ); ~QwtPixelMatrix(); void setRect( const QRect& rect ); QRect rect() const; bool testPixel( int x, int y ) const; bool testAndSetPixel( int x, int y, bool on ); int index( int x, int y ) const; private: QRect d_rect; }; /*! \brief Test if a pixel has been set \param x X-coordinate \param y Y-coordinate \return true, when pos is outside of rect(), or when the pixel has already been set. */ inline bool QwtPixelMatrix::testPixel( int x, int y ) const { const int idx = index( x, y ); return ( idx >= 0 ) ? testBit( idx ) : true; } /*! \brief Set a pixel and test if a pixel has been set before \param x X-coordinate \param y Y-coordinate \param on Set/Clear the pixel \return true, when pos is outside of rect(), or when the pixel was set before. */ inline bool QwtPixelMatrix::testAndSetPixel( int x, int y, bool on ) { const int idx = index( x, y ); if ( idx < 0 ) return true; const bool onBefore = testBit( idx ); setBit( idx, on ); return onBefore; } /*! \brief Calculate the index in the bit field corresponding to a position \param x X-coordinate \param y Y-coordinate \return Index, when rect() contains pos - otherwise -1. */ inline int QwtPixelMatrix::index( int x, int y ) const { const int dx = x - d_rect.x(); if ( dx < 0 || dx >= d_rect.width() ) return -1; const int dy = y - d_rect.y(); if ( dy < 0 || dy >= d_rect.height() ) return -1; return dy * d_rect.width() + dx; } #endif connectome-workbench-1.2.3+git41-gc4c6c90/src/Qwt/qwt_plot.cpp000066400000000000000000000746731300200146000237150ustar00rootroot00000000000000/* -*- mode: C++ ; c-file-style: "stroustrup" -*- ***************************** * Qwt Widget Library * Copyright (C) 1997 Josef Wilgen * Copyright (C) 2002 Uwe Rathmann * * This library is free software; you can redistribute it and/or * modify it under the terms of the Qwt License, Version 1.0 *****************************************************************************/ #include "qwt_plot.h" #include "qwt_plot_dict.h" #include "qwt_plot_layout.h" #include "qwt_scale_widget.h" #include "qwt_scale_engine.h" #include "qwt_text_label.h" #include "qwt_legend.h" #include "qwt_legend_data.h" #include "qwt_plot_canvas.h" #include #include #include #include #include #include static inline void qwtEnableLegendItems( QwtPlot *plot, bool on ) { if ( on ) { QObject::connect( plot, SIGNAL( legendDataChanged( const QVariant &, const QList & ) ), plot, SLOT( updateLegendItems( const QVariant &, const QList & ) ) ); } else { QObject::disconnect( plot, SIGNAL( legendDataChanged( const QVariant &, const QList & ) ), plot, SLOT( updateLegendItems( const QVariant &, const QList & ) ) ); } } static void qwtSetTabOrder( QWidget *first, QWidget *second, bool withChildren ) { QList tabChain; tabChain += first; tabChain += second; if ( withChildren ) { QList children = second->findChildren(); QWidget *w = second->nextInFocusChain(); while ( children.contains( w ) ) { children.removeAll( w ); tabChain += w; w = w->nextInFocusChain(); } } for ( int i = 0; i < tabChain.size() - 1; i++ ) { QWidget *from = tabChain[i]; QWidget *to = tabChain[i+1]; const Qt::FocusPolicy policy1 = from->focusPolicy(); const Qt::FocusPolicy policy2 = to->focusPolicy(); QWidget *proxy1 = from->focusProxy(); QWidget *proxy2 = to->focusProxy(); from->setFocusPolicy( Qt::TabFocus ); from->setFocusProxy( NULL); to->setFocusPolicy( Qt::TabFocus ); to->setFocusProxy( NULL); QWidget::setTabOrder( from, to ); from->setFocusPolicy( policy1 ); from->setFocusProxy( proxy1); to->setFocusPolicy( policy2 ); to->setFocusProxy( proxy2 ); } } class QwtPlot::PrivateData { public: QPointer titleLabel; QPointer footerLabel; QPointer canvas; QPointer legend; QwtPlotLayout *layout; bool autoReplot; }; /*! \brief Constructor \param parent Parent widget */ QwtPlot::QwtPlot( QWidget *parent ): QFrame( parent ) { initPlot( QwtText() ); } /*! \brief Constructor \param title Title text \param parent Parent widget */ QwtPlot::QwtPlot( const QwtText &title, QWidget *parent ): QFrame( parent ) { initPlot( title ); } //! Destructor QwtPlot::~QwtPlot() { setAutoReplot( false ); detachItems( QwtPlotItem::Rtti_PlotItem, autoDelete() ); delete d_data->layout; deleteAxesData(); delete d_data; } /*! \brief Initializes a QwtPlot instance \param title Title text */ void QwtPlot::initPlot( const QwtText &title ) { d_data = new PrivateData; d_data->layout = new QwtPlotLayout; d_data->autoReplot = false; // title d_data->titleLabel = new QwtTextLabel( this ); d_data->titleLabel->setObjectName( "QwtPlotTitle" ); d_data->titleLabel->setFont( QFont( fontInfo().family(), 14, QFont::Bold ) ); QwtText text( title ); text.setRenderFlags( Qt::AlignCenter | Qt::TextWordWrap ); d_data->titleLabel->setText( text ); // footer d_data->footerLabel = new QwtTextLabel( this ); d_data->footerLabel->setObjectName( "QwtPlotFooter" ); QwtText footer; footer.setRenderFlags( Qt::AlignCenter | Qt::TextWordWrap ); d_data->footerLabel->setText( footer ); // legend d_data->legend = NULL; // axis initAxesData(); // canvas d_data->canvas = new QwtPlotCanvas( this ); d_data->canvas->setObjectName( "QwtPlotCanvas" ); d_data->canvas->installEventFilter( this ); setSizePolicy( QSizePolicy::MinimumExpanding, QSizePolicy::MinimumExpanding ); resize( 200, 200 ); QList focusChain; focusChain << this << d_data->titleLabel << axisWidget( xTop ) << axisWidget( yLeft ) << d_data->canvas << axisWidget( yRight ) << axisWidget( xBottom ) << d_data->footerLabel; for ( int i = 0; i < focusChain.size() - 1; i++ ) qwtSetTabOrder( focusChain[i], focusChain[i+1], false ); qwtEnableLegendItems( this, true ); } /*! \brief Set the drawing canvas of the plot widget QwtPlot invokes methods of the canvas as meta methods ( see QMetaObject ). In opposite to using conventional C++ techniques like virtual methods they allow to use canvas implementations that are derived from QWidget or QGLWidget. The following meta methods could be implemented: - replot() When the canvas doesn't offer a replot method, QwtPlot calls update() instead. - borderPath() The border path is necessary to clip the content of the canvas When the canvas doesn't have any special border ( f.e rounded corners ) it is o.k. not to implement this method. The default canvas is a QwtPlotCanvas \param canvas Canvas Widget \sa canvas() */ void QwtPlot::setCanvas( QWidget *canvas ) { if ( canvas == d_data->canvas ) return; delete d_data->canvas; d_data->canvas = canvas; if ( canvas ) { canvas->setParent( this ); canvas->installEventFilter( this ); if ( isVisible() ) canvas->show(); } } /*! \brief Adds handling of layout requests \param event Event \return See QFrame::event() */ bool QwtPlot::event( QEvent *event ) { bool ok = QFrame::event( event ); switch ( event->type() ) { case QEvent::LayoutRequest: updateLayout(); break; case QEvent::PolishRequest: replot(); break; default:; } return ok; } /*! \brief Event filter The plot handles the following events for the canvas: - QEvent::Resize The canvas margins might depend on its size - QEvent::ContentsRectChange The layout needs to be recalculated \param object Object to be filtered \param event Event \return See QFrame::eventFilter() \sa updateCanvasMargins(), updateLayout() */ bool QwtPlot::eventFilter( QObject *object, QEvent *event ) { if ( object == d_data->canvas ) { if ( event->type() == QEvent::Resize ) { updateCanvasMargins(); } else if ( event->type() == QEvent::ContentsRectChange ) { updateLayout(); } } return QFrame::eventFilter( object, event ); } //! Replots the plot if autoReplot() is \c true. void QwtPlot::autoRefresh() { if ( d_data->autoReplot ) replot(); } /*! \brief Set or reset the autoReplot option If the autoReplot option is set, the plot will be updated implicitly by manipulating member functions. Since this may be time-consuming, it is recommended to leave this option switched off and call replot() explicitly if necessary. The autoReplot option is set to false by default, which means that the user has to call replot() in order to make changes visible. \param tf \c true or \c false. Defaults to \c true. \sa replot() */ void QwtPlot::setAutoReplot( bool tf ) { d_data->autoReplot = tf; } /*! \return true if the autoReplot option is set. \sa setAutoReplot() */ bool QwtPlot::autoReplot() const { return d_data->autoReplot; } /*! Change the plot's title \param title New title */ void QwtPlot::setTitle( const QString &title ) { if ( title != d_data->titleLabel->text().text() ) { d_data->titleLabel->setText( title ); updateLayout(); } } /*! Change the plot's title \param title New title */ void QwtPlot::setTitle( const QwtText &title ) { if ( title != d_data->titleLabel->text() ) { d_data->titleLabel->setText( title ); updateLayout(); } } //! \return Title of the plot QwtText QwtPlot::title() const { return d_data->titleLabel->text(); } //! \return Title label widget. QwtTextLabel *QwtPlot::titleLabel() { return d_data->titleLabel; } //! \return Title label widget. const QwtTextLabel *QwtPlot::titleLabel() const { return d_data->titleLabel; } /*! Change the text the footer \param text New text of the footer */ void QwtPlot::setFooter( const QString &text ) { if ( text != d_data->footerLabel->text().text() ) { d_data->footerLabel->setText( text ); updateLayout(); } } /*! Change the text the footer \param text New text of the footer */ void QwtPlot::setFooter( const QwtText &text ) { if ( text != d_data->footerLabel->text() ) { d_data->footerLabel->setText( text ); updateLayout(); } } //! \return Text of the footer QwtText QwtPlot::footer() const { return d_data->footerLabel->text(); } //! \return Footer label widget. QwtTextLabel *QwtPlot::footerLabel() { return d_data->footerLabel; } //! \return Footer label widget. const QwtTextLabel *QwtPlot::footerLabel() const { return d_data->footerLabel; } /*! \brief Assign a new plot layout \param layout Layout() \sa plotLayout() */ void QwtPlot::setPlotLayout( QwtPlotLayout *layout ) { if ( layout != d_data->layout ) { delete d_data->layout; d_data->layout = layout; updateLayout(); } } //! \return the plot's layout QwtPlotLayout *QwtPlot::plotLayout() { return d_data->layout; } //! \return the plot's layout const QwtPlotLayout *QwtPlot::plotLayout() const { return d_data->layout; } /*! \return the plot's legend \sa insertLegend() */ QwtAbstractLegend *QwtPlot::legend() { return d_data->legend; } /*! \return the plot's legend \sa insertLegend() */ const QwtAbstractLegend *QwtPlot::legend() const { return d_data->legend; } /*! \return the plot's canvas */ QWidget *QwtPlot::canvas() { return d_data->canvas; } /*! \return the plot's canvas */ const QWidget *QwtPlot::canvas() const { return d_data->canvas; } /*! \return Size hint for the plot widget \sa minimumSizeHint() */ QSize QwtPlot::sizeHint() const { int dw = 0; int dh = 0; for ( int axisId = 0; axisId < axisCnt; axisId++ ) { if ( axisEnabled( axisId ) ) { const int niceDist = 40; const QwtScaleWidget *scaleWidget = axisWidget( axisId ); const QwtScaleDiv &scaleDiv = scaleWidget->scaleDraw()->scaleDiv(); const int majCnt = scaleDiv.ticks( QwtScaleDiv::MajorTick ).count(); if ( axisId == yLeft || axisId == yRight ) { int hDiff = ( majCnt - 1 ) * niceDist - scaleWidget->minimumSizeHint().height(); if ( hDiff > dh ) dh = hDiff; } else { int wDiff = ( majCnt - 1 ) * niceDist - scaleWidget->minimumSizeHint().width(); if ( wDiff > dw ) dw = wDiff; } } } return minimumSizeHint() + QSize( dw, dh ); } /*! \brief Return a minimum size hint */ QSize QwtPlot::minimumSizeHint() const { QSize hint = d_data->layout->minimumSizeHint( this ); hint += QSize( 2 * frameWidth(), 2 * frameWidth() ); return hint; } /*! Resize and update internal layout \param e Resize event */ void QwtPlot::resizeEvent( QResizeEvent *e ) { QFrame::resizeEvent( e ); updateLayout(); } /*! \brief Redraw the plot If the autoReplot option is not set (which is the default) or if any curves are attached to raw data, the plot has to be refreshed explicitly in order to make changes visible. \sa updateAxes(), setAutoReplot() */ void QwtPlot::replot() { bool doAutoReplot = autoReplot(); setAutoReplot( false ); updateAxes(); /* Maybe the layout needs to be updated, because of changed axes labels. We need to process them here before painting to avoid that scales and canvas get out of sync. */ QApplication::sendPostedEvents( this, QEvent::LayoutRequest ); if ( d_data->canvas ) { const bool ok = QMetaObject::invokeMethod( d_data->canvas, "replot", Qt::DirectConnection ); if ( !ok ) { // fallback, when canvas has no a replot method d_data->canvas->update( d_data->canvas->contentsRect() ); } } setAutoReplot( doAutoReplot ); } /*! \brief Adjust plot content to its current size. \sa resizeEvent() */ void QwtPlot::updateLayout() { d_data->layout->activate( this, contentsRect() ); QRect titleRect = d_data->layout->titleRect().toRect(); QRect footerRect = d_data->layout->footerRect().toRect(); QRect scaleRect[QwtPlot::axisCnt]; for ( int axisId = 0; axisId < axisCnt; axisId++ ) scaleRect[axisId] = d_data->layout->scaleRect( axisId ).toRect(); QRect legendRect = d_data->layout->legendRect().toRect(); QRect canvasRect = d_data->layout->canvasRect().toRect(); // resize and show the visible widgets if ( !d_data->titleLabel->text().isEmpty() ) { d_data->titleLabel->setGeometry( titleRect ); if ( !d_data->titleLabel->isVisibleTo( this ) ) d_data->titleLabel->show(); } else d_data->titleLabel->hide(); if ( !d_data->footerLabel->text().isEmpty() ) { d_data->footerLabel->setGeometry( footerRect ); if ( !d_data->footerLabel->isVisibleTo( this ) ) d_data->footerLabel->show(); } else d_data->footerLabel->hide(); for ( int axisId = 0; axisId < axisCnt; axisId++ ) { if ( axisEnabled( axisId ) ) { axisWidget( axisId )->setGeometry( scaleRect[axisId] ); #if 1 if ( axisId == xBottom || axisId == xTop ) { // do we need this code any longer ??? QRegion r( scaleRect[axisId] ); if ( axisEnabled( yLeft ) ) r = r.subtracted( QRegion( scaleRect[yLeft] ) ); if ( axisEnabled( yRight ) ) r = r.subtracted( QRegion( scaleRect[yRight] ) ); r.translate( -scaleRect[ axisId ].x(), -scaleRect[axisId].y() ); axisWidget( axisId )->setMask( r ); } #endif if ( !axisWidget( axisId )->isVisibleTo( this ) ) axisWidget( axisId )->show(); } else axisWidget( axisId )->hide(); } if ( d_data->legend ) { if ( d_data->legend->isEmpty() ) { d_data->legend->hide(); } else { d_data->legend->setGeometry( legendRect ); d_data->legend->show(); } } d_data->canvas->setGeometry( canvasRect ); } /*! \brief Calculate the canvas margins \param maps QwtPlot::axisCnt maps, mapping between plot and paint device coordinates \param canvasRect Bounding rectangle where to paint \param left Return parameter for the left margin \param top Return parameter for the top margin \param right Return parameter for the right margin \param bottom Return parameter for the bottom margin Plot items might indicate, that they need some extra space at the borders of the canvas by the QwtPlotItem::Margins flag. updateCanvasMargins(), QwtPlotItem::getCanvasMarginHint() */ void QwtPlot::getCanvasMarginsHint( const QwtScaleMap maps[], const QRectF &canvasRect, double &left, double &top, double &right, double &bottom) const { left = top = right = bottom = -1.0; const QwtPlotItemList& itmList = itemList(); for ( QwtPlotItemIterator it = itmList.begin(); it != itmList.end(); ++it ) { const QwtPlotItem *item = *it; if ( item->testItemAttribute( QwtPlotItem::Margins ) ) { double m[ QwtPlot::axisCnt ]; item->getCanvasMarginHint( maps[ item->xAxis() ], maps[ item->yAxis() ], canvasRect, m[yLeft], m[xTop], m[yRight], m[xBottom] ); left = qMax( left, m[yLeft] ); top = qMax( top, m[xTop] ); right = qMax( right, m[yRight] ); bottom = qMax( bottom, m[xBottom] ); } } } /*! \brief Update the canvas margins Plot items might indicate, that they need some extra space at the borders of the canvas by the QwtPlotItem::Margins flag. getCanvasMarginsHint(), QwtPlotItem::getCanvasMarginHint() */ void QwtPlot::updateCanvasMargins() { QwtScaleMap maps[axisCnt]; for ( int axisId = 0; axisId < axisCnt; axisId++ ) maps[axisId] = canvasMap( axisId ); double margins[axisCnt]; getCanvasMarginsHint( maps, canvas()->contentsRect(), margins[yLeft], margins[xTop], margins[yRight], margins[xBottom] ); bool doUpdate = false; for ( int axisId = 0; axisId < axisCnt; axisId++ ) { if ( margins[axisId] >= 0.0 ) { const int m = qCeil( margins[axisId] ); plotLayout()->setCanvasMargin( m, axisId); doUpdate = true; } } if ( doUpdate ) updateLayout(); } /*! Redraw the canvas. \param painter Painter used for drawing \warning drawCanvas calls drawItems what is also used for printing. Applications that like to add individual plot items better overload drawItems() \sa drawItems() */ void QwtPlot::drawCanvas( QPainter *painter ) { QwtScaleMap maps[axisCnt]; for ( int axisId = 0; axisId < axisCnt; axisId++ ) maps[axisId] = canvasMap( axisId ); drawItems( painter, d_data->canvas->contentsRect(), maps ); } /*! Redraw the canvas items. \param painter Painter used for drawing \param canvasRect Bounding rectangle where to paint \param maps QwtPlot::axisCnt maps, mapping between plot and paint device coordinates \note Usually canvasRect is contentsRect() of the plot canvas. Due to a bug in Qt this rectangle might be wrong for certain frame styles ( f.e QFrame::Box ) and it might be necessary to fix the margins manually using QWidget::setContentsMargins() */ void QwtPlot::drawItems( QPainter *painter, const QRectF &canvasRect, const QwtScaleMap maps[axisCnt] ) const { const QwtPlotItemList& itmList = itemList(); for ( QwtPlotItemIterator it = itmList.begin(); it != itmList.end(); ++it ) { QwtPlotItem *item = *it; if ( item && item->isVisible() ) { painter->save(); painter->setRenderHint( QPainter::Antialiasing, item->testRenderHint( QwtPlotItem::RenderAntialiased ) ); painter->setRenderHint( QPainter::HighQualityAntialiasing, item->testRenderHint( QwtPlotItem::RenderAntialiased ) ); item->draw( painter, maps[item->xAxis()], maps[item->yAxis()], canvasRect ); painter->restore(); } } } /*! \param axisId Axis \return Map for the axis on the canvas. With this map pixel coordinates can translated to plot coordinates and vice versa. \sa QwtScaleMap, transform(), invTransform() */ QwtScaleMap QwtPlot::canvasMap( int axisId ) const { QwtScaleMap map; if ( !d_data->canvas ) return map; map.setTransformation( axisScaleEngine( axisId )->transformation() ); const QwtScaleDiv &sd = axisScaleDiv( axisId ); map.setScaleInterval( sd.lowerBound(), sd.upperBound() ); if ( axisEnabled( axisId ) ) { const QwtScaleWidget *s = axisWidget( axisId ); if ( axisId == yLeft || axisId == yRight ) { double y = s->y() + s->startBorderDist() - d_data->canvas->y(); double h = s->height() - s->startBorderDist() - s->endBorderDist(); map.setPaintInterval( y + h, y ); } else { double x = s->x() + s->startBorderDist() - d_data->canvas->x(); double w = s->width() - s->startBorderDist() - s->endBorderDist(); map.setPaintInterval( x, x + w ); } } else { const QRect &canvasRect = d_data->canvas->contentsRect(); if ( axisId == yLeft || axisId == yRight ) { int top = 0; if ( !plotLayout()->alignCanvasToScale( xTop ) ) top = plotLayout()->canvasMargin( xTop ); int bottom = 0; if ( !plotLayout()->alignCanvasToScale( xBottom ) ) bottom = plotLayout()->canvasMargin( xBottom ); map.setPaintInterval( canvasRect.bottom() - bottom, canvasRect.top() + top ); } else { int left = 0; if ( !plotLayout()->alignCanvasToScale( yLeft ) ) left = plotLayout()->canvasMargin( yLeft ); int right = 0; if ( !plotLayout()->alignCanvasToScale( yRight ) ) right = plotLayout()->canvasMargin( yRight ); map.setPaintInterval( canvasRect.left() + left, canvasRect.right() - right ); } } return map; } /*! \brief Change the background of the plotting area Sets brush to QPalette::Window of all color groups of the palette of the canvas. Using canvas()->setPalette() is a more powerful way to set these colors. \param brush New background brush \sa canvasBackground() */ void QwtPlot::setCanvasBackground( const QBrush &brush ) { QPalette pal = d_data->canvas->palette(); pal.setBrush( QPalette::Window, brush ); canvas()->setPalette( pal ); } /*! Nothing else than: canvas()->palette().brush( QPalette::Normal, QPalette::Window); \return Background brush of the plotting area. \sa setCanvasBackground() */ QBrush QwtPlot::canvasBackground() const { return canvas()->palette().brush( QPalette::Normal, QPalette::Window ); } /*! \return \c true if the specified axis exists, otherwise \c false \param axisId axis index */ bool QwtPlot::axisValid( int axisId ) { return ( ( axisId >= QwtPlot::yLeft ) && ( axisId < QwtPlot::axisCnt ) ); } /*! \brief Insert a legend If the position legend is \c QwtPlot::LeftLegend or \c QwtPlot::RightLegend the legend will be organized in one column from top to down. Otherwise the legend items will be placed in a table with a best fit number of columns from left to right. insertLegend() will set the plot widget as parent for the legend. The legend will be deleted in the destructor of the plot or when another legend is inserted. Legends, that are not inserted into the layout of the plot widget need to connect to the legendDataChanged() signal. Calling updateLegend() initiates this signal for an initial update. When the application code wants to implement its own layout this also needs to be done for rendering plots to a document ( see QwtPlotRenderer ). \param legend Legend \param pos The legend's position. For top/left position the number of columns will be limited to 1, otherwise it will be set to unlimited. \param ratio Ratio between legend and the bounding rectangle of title, canvas and axes. The legend will be shrunk if it would need more space than the given ratio. The ratio is limited to ]0.0 .. 1.0]. In case of <= 0.0 it will be reset to the default ratio. The default vertical/horizontal ratio is 0.33/0.5. \sa legend(), QwtPlotLayout::legendPosition(), QwtPlotLayout::setLegendPosition() */ void QwtPlot::insertLegend( QwtAbstractLegend *legend, QwtPlot::LegendPosition pos, double ratio ) { d_data->layout->setLegendPosition( pos, ratio ); if ( legend != d_data->legend ) { if ( d_data->legend && d_data->legend->parent() == this ) delete d_data->legend; d_data->legend = legend; if ( d_data->legend ) { connect( this, SIGNAL( legendDataChanged( const QVariant &, const QList & ) ), d_data->legend, SLOT( updateLegend( const QVariant &, const QList & ) ) ); if ( d_data->legend->parent() != this ) d_data->legend->setParent( this ); qwtEnableLegendItems( this, false ); updateLegend(); qwtEnableLegendItems( this, true ); QwtLegend *lgd = qobject_cast( legend ); if ( lgd ) { switch ( d_data->layout->legendPosition() ) { case LeftLegend: case RightLegend: { if ( lgd->maxColumns() == 0 ) lgd->setMaxColumns( 1 ); // 1 column: align vertical break; } case TopLegend: case BottomLegend: { lgd->setMaxColumns( 0 ); // unlimited break; } default: break; } } QWidget *previousInChain = NULL; switch ( d_data->layout->legendPosition() ) { case LeftLegend: { previousInChain = axisWidget( QwtPlot::xTop ); break; } case TopLegend: { previousInChain = this; break; } case RightLegend: { previousInChain = axisWidget( QwtPlot::yRight ); break; } case BottomLegend: { previousInChain = footerLabel(); break; } } if ( previousInChain ) qwtSetTabOrder( previousInChain, legend, true ); } } updateLayout(); } /*! Emit legendDataChanged() for all plot item \sa QwtPlotItem::legendData(), legendDataChanged() */ void QwtPlot::updateLegend() { const QwtPlotItemList& itmList = itemList(); for ( QwtPlotItemIterator it = itmList.begin(); it != itmList.end(); ++it ) { updateLegend( *it ); } } /*! Emit legendDataChanged() for a plot item \param plotItem Plot item \sa QwtPlotItem::legendData(), legendDataChanged() */ void QwtPlot::updateLegend( const QwtPlotItem *plotItem ) { if ( plotItem == NULL ) return; QList legendData; if ( plotItem->testItemAttribute( QwtPlotItem::Legend ) ) legendData = plotItem->legendData(); const QVariant itemInfo = itemToInfo( const_cast< QwtPlotItem *>( plotItem) ); Q_EMIT legendDataChanged( itemInfo, legendData ); } /*! \brief Update all plot items interested in legend attributes Call QwtPlotItem::updateLegend(), when the QwtPlotItem::LegendInterest flag is set. \param itemInfo Info about the plot item \param legendData Entries to be displayed for the plot item ( usually 1 ) \sa QwtPlotItem::LegendInterest, QwtPlotLegendItem, QwtPlotItem::updateLegend() */ void QwtPlot::updateLegendItems( const QVariant &itemInfo, const QList &legendData ) { QwtPlotItem *plotItem = infoToItem( itemInfo ); if ( plotItem ) { const QwtPlotItemList& itmList = itemList(); for ( QwtPlotItemIterator it = itmList.begin(); it != itmList.end(); ++it ) { QwtPlotItem *item = *it; if ( item->testItemInterest( QwtPlotItem::LegendInterest ) ) item->updateLegend( plotItem, legendData ); } } } /*! \brief Attach/Detach a plot item \param plotItem Plot item \param on When true attach the item, otherwise detach it */ void QwtPlot::attachItem( QwtPlotItem *plotItem, bool on ) { if ( plotItem->testItemInterest( QwtPlotItem::LegendInterest ) ) { // plotItem is some sort of legend const QwtPlotItemList& itmList = itemList(); for ( QwtPlotItemIterator it = itmList.begin(); it != itmList.end(); ++it ) { QwtPlotItem *item = *it; QList legendData; if ( on && item->testItemAttribute( QwtPlotItem::Legend ) ) { legendData = item->legendData(); plotItem->updateLegend( item, legendData ); } } } if ( on ) insertItem( plotItem ); else removeItem( plotItem ); Q_EMIT itemAttached( plotItem, on ); if ( plotItem->testItemAttribute( QwtPlotItem::Legend ) ) { // the item wants to be represented on the legend if ( on ) { updateLegend( plotItem ); } else { const QVariant itemInfo = itemToInfo( plotItem ); Q_EMIT legendDataChanged( itemInfo, QList() ); } } autoRefresh(); } /*! \brief Build an information, that can be used to identify a plot item on the legend. The default implementation simply wraps the plot item into a QVariant object. When overloading itemToInfo() usually infoToItem() needs to reimplemeted too. \code QVariant itemInfo; qVariantSetValue( itemInfo, plotItem ); \endcode \param plotItem Plot item \return Plot item embedded in a QVariant \sa infoToItem() */ QVariant QwtPlot::itemToInfo( QwtPlotItem *plotItem ) const { QVariant itemInfo; qVariantSetValue( itemInfo, plotItem ); return itemInfo; } /*! \brief Identify the plot item according to an item info object, that has bee generated from itemToInfo(). The default implementation simply tries to unwrap a QwtPlotItem pointer: \code if ( itemInfo.canConvert() ) return qvariant_cast( itemInfo ); \endcode \param itemInfo Plot item \return A plot item, when successful, otherwise a NULL pointer. \sa itemToInfo() */ QwtPlotItem *QwtPlot::infoToItem( const QVariant &itemInfo ) const { if ( itemInfo.canConvert() ) return qvariant_cast( itemInfo ); return NULL; } connectome-workbench-1.2.3+git41-gc4c6c90/src/Qwt/qwt_plot.h000066400000000000000000000202521300200146000233420ustar00rootroot00000000000000/* -*- mode: C++ ; c-file-style: "stroustrup" -*- ***************************** * Qwt Widget Library * Copyright (C) 1997 Josef Wilgen * Copyright (C) 2002 Uwe Rathmann * * This library is free software; you can redistribute it and/or * modify it under the terms of the Qwt License, Version 1.0 *****************************************************************************/ #ifndef QWT_PLOT_H #define QWT_PLOT_H #include "qwt_global.h" #include "qwt_text.h" #include "qwt_plot_dict.h" #include "qwt_scale_map.h" #include "qwt_interval.h" #include #include #include class QwtPlotLayout; class QwtAbstractLegend; class QwtScaleWidget; class QwtScaleEngine; class QwtScaleDiv; class QwtScaleDraw; class QwtTextLabel; /*! \brief A 2-D plotting widget QwtPlot is a widget for plotting two-dimensional graphs. An unlimited number of plot items can be displayed on its canvas. Plot items might be curves (QwtPlotCurve), markers (QwtPlotMarker), the grid (QwtPlotGrid), or anything else derived from QwtPlotItem. A plot can have up to four axes, with each plot item attached to an x- and a y axis. The scales at the axes can be explicitly set (QwtScaleDiv), or are calculated from the plot items, using algorithms (QwtScaleEngine) which can be configured separately for each axis. The simpleplot example is a good starting point to see how to set up a plot widget. \image html plot.png \par Example The following example shows (schematically) the most simple way to use QwtPlot. By default, only the left and bottom axes are visible and their scales are computed automatically. \verbatim #include #include QwtPlot *myPlot = new QwtPlot("Two Curves", parent); // add curves QwtPlotCurve *curve1 = new QwtPlotCurve("Curve 1"); QwtPlotCurve *curve2 = new QwtPlotCurve("Curve 2"); // connect or copy the data to the curves curve1->setData(...); curve2->setData(...); curve1->attach(myPlot); curve2->attach(myPlot); // finally, refresh the plot myPlot->replot(); \endverbatim */ class QWT_EXPORT QwtPlot: public QFrame, public QwtPlotDict { Q_OBJECT Q_PROPERTY( QBrush canvasBackground READ canvasBackground WRITE setCanvasBackground ) Q_PROPERTY( bool autoReplot READ autoReplot WRITE setAutoReplot ) #if 0 // This property is intended to configure the plot // widget from a special dialog in the deigner plugin. // Disabled until such a dialog has been implemented. Q_PROPERTY( QString propertiesDocument READ grabProperties WRITE applyProperties ) #endif public: //! \brief Axis index enum Axis { //! Y axis left of the canvas yLeft, //! Y axis right of the canvas yRight, //! X axis below the canvas xBottom, //! X axis above the canvas xTop, //! Number of axes axisCnt }; /*! Position of the legend, relative to the canvas. \sa insertLegend() */ enum LegendPosition { //! The legend will be left from the QwtPlot::yLeft axis. LeftLegend, //! The legend will be right from the QwtPlot::yRight axis. RightLegend, //! The legend will be below the footer BottomLegend, //! The legend will be above the title TopLegend }; explicit QwtPlot( QWidget * = NULL ); explicit QwtPlot( const QwtText &title, QWidget * = NULL ); virtual ~QwtPlot(); void applyProperties( const QString & ); QString grabProperties() const; void setAutoReplot( bool = true ); bool autoReplot() const; // Layout void setPlotLayout( QwtPlotLayout * ); QwtPlotLayout *plotLayout(); const QwtPlotLayout *plotLayout() const; // Title void setTitle( const QString & ); void setTitle( const QwtText &t ); QwtText title() const; QwtTextLabel *titleLabel(); const QwtTextLabel *titleLabel() const; // Footer void setFooter( const QString & ); void setFooter( const QwtText &t ); QwtText footer() const; QwtTextLabel *footerLabel(); const QwtTextLabel *footerLabel() const; // Canvas void setCanvas( QWidget * ); QWidget *canvas(); const QWidget *canvas() const; void setCanvasBackground( const QBrush & ); QBrush canvasBackground() const; virtual QwtScaleMap canvasMap( int axisId ) const; double invTransform( int axisId, int pos ) const; double transform( int axisId, double value ) const; // Axes QwtScaleEngine *axisScaleEngine( int axisId ); const QwtScaleEngine *axisScaleEngine( int axisId ) const; void setAxisScaleEngine( int axisId, QwtScaleEngine * ); void setAxisAutoScale( int axisId, bool on = true ); bool axisAutoScale( int axisId ) const; void enableAxis( int axisId, bool tf = true ); bool axisEnabled( int axisId ) const; void setAxisFont( int axisId, const QFont &f ); QFont axisFont( int axisId ) const; void setAxisScale( int axisId, double min, double max, double step = 0 ); void setAxisScaleDiv( int axisId, const QwtScaleDiv & ); void setAxisScaleDraw( int axisId, QwtScaleDraw * ); double axisStepSize( int axisId ) const; QwtInterval axisInterval( int axisId ) const; const QwtScaleDiv &axisScaleDiv( int axisId ) const; const QwtScaleDraw *axisScaleDraw( int axisId ) const; QwtScaleDraw *axisScaleDraw( int axisId ); const QwtScaleWidget *axisWidget( int axisId ) const; QwtScaleWidget *axisWidget( int axisId ); void setAxisLabelAlignment( int axisId, Qt::Alignment ); void setAxisLabelRotation( int axisId, double rotation ); void setAxisTitle( int axisId, const QString & ); void setAxisTitle( int axisId, const QwtText & ); QwtText axisTitle( int axisId ) const; void setAxisMaxMinor( int axisId, int maxMinor ); int axisMaxMinor( int axisId ) const; void setAxisMaxMajor( int axisId, int maxMajor ); int axisMaxMajor( int axisId ) const; // Legend void insertLegend( QwtAbstractLegend *, LegendPosition = QwtPlot::RightLegend, double ratio = -1.0 ); QwtAbstractLegend *legend(); const QwtAbstractLegend *legend() const; void updateLegend(); void updateLegend( const QwtPlotItem * ); // Misc virtual QSize sizeHint() const; virtual QSize minimumSizeHint() const; virtual void updateLayout(); virtual void drawCanvas( QPainter * ); void updateAxes(); void updateCanvasMargins(); virtual void getCanvasMarginsHint( const QwtScaleMap maps[], const QRectF &canvasRect, double &left, double &top, double &right, double &bottom) const; virtual bool event( QEvent * ); virtual bool eventFilter( QObject *, QEvent * ); virtual void drawItems( QPainter *, const QRectF &, const QwtScaleMap maps[axisCnt] ) const; virtual QVariant itemToInfo( QwtPlotItem * ) const; virtual QwtPlotItem *infoToItem( const QVariant & ) const; Q_SIGNALS: /*! A signal indicating, that an item has been attached/detached \param plotItem Plot item \param on Attached/Detached */ void itemAttached( QwtPlotItem *plotItem, bool on ); /*! A signal with the attributes how to update the legend entries for a plot item. \param itemInfo Info about a plot item, build from itemToInfo() \param data Attributes of the entries ( usually <= 1 ) for the plot item. \sa itemToInfo(), infoToItem(), QwtAbstractLegend::updateLegend() */ void legendDataChanged( const QVariant &itemInfo, const QList &data ); public Q_SLOTS: virtual void replot(); void autoRefresh(); protected: static bool axisValid( int axisId ); virtual void resizeEvent( QResizeEvent *e ); private Q_SLOTS: void updateLegendItems( const QVariant &itemInfo, const QList &data ); private: friend class QwtPlotItem; void attachItem( QwtPlotItem *, bool ); void initAxesData(); void deleteAxesData(); void updateScaleDiv(); void initPlot( const QwtText &title ); class AxisData; AxisData *d_axisData[axisCnt]; class PrivateData; PrivateData *d_data; }; #endif connectome-workbench-1.2.3+git41-gc4c6c90/src/Qwt/qwt_plot_abstract_barchart.cpp000066400000000000000000000221211300200146000274230ustar00rootroot00000000000000/* -*- mode: C++ ; c-file-style: "stroustrup" -*- ***************************** * Qwt Widget Library * Copyright (C) 1997 Josef Wilgen * Copyright (C) 2002 Uwe Rathmann * * This library is free software; you can redistribute it and/or * modify it under the terms of the Qwt License, Version 1.0 *****************************************************************************/ #include "qwt_plot_abstract_barchart.h" #include "qwt_scale_map.h" static inline double qwtTransformWidth( const QwtScaleMap &map, double value, double width ) { const double w2 = 0.5 * width; const double v1 = map.transform( value - w2 ); const double v2 = map.transform( value + w2 ); return qAbs( v2 - v1 ); } class QwtPlotAbstractBarChart::PrivateData { public: PrivateData(): layoutPolicy( QwtPlotAbstractBarChart::AutoAdjustSamples ), layoutHint( 0.5 ), spacing( 10 ), margin( 5 ), baseline( 0.0 ) { } QwtPlotAbstractBarChart::LayoutPolicy layoutPolicy; double layoutHint; int spacing; int margin; double baseline; }; /*! Constructor \param title Title of the chart */ QwtPlotAbstractBarChart::QwtPlotAbstractBarChart( const QwtText &title ): QwtPlotSeriesItem( title ) { d_data = new PrivateData; setItemAttribute( QwtPlotItem::Legend, true ); setItemAttribute( QwtPlotItem::AutoScale, true ); setItemAttribute( QwtPlotItem::Margins, true ); setZ( 19.0 ); } //! Destructor QwtPlotAbstractBarChart::~QwtPlotAbstractBarChart() { delete d_data; } /*! The combination of layoutPolicy() and layoutHint() define how the width of the bars is calculated \param policy Layout policy \sa layoutPolicy(), layoutHint() */ void QwtPlotAbstractBarChart::setLayoutPolicy( LayoutPolicy policy ) { if ( policy != d_data->layoutPolicy ) { d_data->layoutPolicy = policy; itemChanged(); } } /*! The combination of layoutPolicy() and layoutHint() define how the width of the bars is calculated \return Layout policy of the chart item \sa setLayoutPolicy(), layoutHint() */ QwtPlotAbstractBarChart::LayoutPolicy QwtPlotAbstractBarChart::layoutPolicy() const { return d_data->layoutPolicy; } /*! The combination of layoutPolicy() and layoutHint() define how the width of the bars is calculated \param hint Layout hint \sa LayoutPolicy, layoutPolicy(), layoutHint() */ void QwtPlotAbstractBarChart::setLayoutHint( double hint ) { hint = qMax( 0.0, hint ); if ( hint != d_data->layoutHint ) { d_data->layoutHint = hint; itemChanged(); } } /*! The combination of layoutPolicy() and layoutHint() define how the width of the bars is calculated \return Layout policy of the chart item \sa LayoutPolicy, setLayoutHint(), layoutPolicy() */ double QwtPlotAbstractBarChart::layoutHint() const { return d_data->layoutHint; } /*! \brief Set the spacing The spacing is the distance between 2 samples ( bars for QwtPlotBarChart or a group of bars for QwtPlotMultiBarChart ) in paint device coordinates. \sa spacing() */ void QwtPlotAbstractBarChart::setSpacing( int spacing ) { spacing = qMax( spacing, 0 ); if ( spacing != d_data->spacing ) { d_data->spacing = spacing; itemChanged(); } } /*! \return Spacing between 2 samples ( bars or groups of bars ) \sa setSpacing(), margin() */ int QwtPlotAbstractBarChart::spacing() const { return d_data->spacing; } /*! \brief Set the margin The margin is the distance between the outmost bars and the contentsRect() of the canvas. The default setting is 5 pixels. \param margin Margin \sa spacing(), margin() */ void QwtPlotAbstractBarChart::setMargin( int margin ) { margin = qMax( margin, 0 ); if ( margin != d_data->margin ) { d_data->margin = margin; itemChanged(); } } /*! \return Margin between the outmost bars and the contentsRect() of the canvas. \sa setMargin(), spacing() */ int QwtPlotAbstractBarChart::margin() const { return d_data->margin; } /*! \brief Set the baseline The baseline is the origin for the chart. Each bar is painted from the baseline in the direction of the sample value. In case of a horizontal orientation() the baseline is interpreted as x - otherwise as y - value. The default value for the baseline is 0. \param value Value for the baseline \sa baseline(), QwtPlotSeriesItem::orientation() */ void QwtPlotAbstractBarChart::setBaseline( double value ) { if ( value != d_data->baseline ) { d_data->baseline = value; itemChanged(); } } /*! \return Value for the origin of the bar chart \sa setBaseline(), QwtPlotSeriesItem::orientation() */ double QwtPlotAbstractBarChart::baseline() const { return d_data->baseline; } /*! Calculate the width for a sample in paint device coordinates \param map Scale map for the corresponding scale \param canvasSize Size of the canvas in paint device coordinates \param boundingSize Bounding size of the chart in plot coordinates ( used in AutoAdjustSamples mode ) \param value Value of the sample \return Sample width \sa layoutPolicy(), layoutHint() */ double QwtPlotAbstractBarChart::sampleWidth( const QwtScaleMap &map, double canvasSize, double boundingSize, double value ) const { double width; switch( d_data->layoutPolicy ) { case ScaleSamplesToAxes: { width = qwtTransformWidth( map, value, d_data->layoutHint ); break; } case ScaleSampleToCanvas: { width = canvasSize * d_data->layoutHint; break; } case FixedSampleSize: { width = d_data->layoutHint; break; } case AutoAdjustSamples: default: { const size_t numSamples = dataSize(); double w = 1.0; if ( numSamples > 1 ) { w = qAbs( boundingSize / ( numSamples - 1 ) ); } width = qwtTransformWidth( map, value, w ); width -= d_data->spacing; width = qMax( width, d_data->layoutHint ); } } return width; } /*! \brief Calculate a hint for the canvas margin Bar charts need to reserve some space for displaying the bars for the first and the last sample. The hint is calculated from the layoutHint() depending on the layoutPolicy(). The margins are in target device coordinates ( pixels on screen ) \param xMap Maps x-values into pixel coordinates. \param yMap Maps y-values into pixel coordinates. \param canvasRect Contents rectangle of the canvas in painter coordinates \param left Returns the left margin \param top Returns the top margin \param right Returns the right margin \param bottom Returns the bottom margin \return Margin \sa layoutPolicy(), layoutHint(), QwtPlotItem::Margins QwtPlot::getCanvasMarginsHint(), QwtPlot::updateCanvasMargins() */ void QwtPlotAbstractBarChart::getCanvasMarginHint( const QwtScaleMap &xMap, const QwtScaleMap &yMap, const QRectF &canvasRect, double &left, double &top, double &right, double &bottom ) const { double hint = -1.0; switch( layoutPolicy() ) { case ScaleSampleToCanvas: { if ( orientation() == Qt::Vertical ) hint = 0.5 * canvasRect.width() * d_data->layoutHint; else hint = 0.5 * canvasRect.height() * d_data->layoutHint; break; } case FixedSampleSize: { hint = 0.5 * d_data->layoutHint; break; } case AutoAdjustSamples: case ScaleSamplesToAxes: default: { const size_t numSamples = dataSize(); if ( numSamples <= 0 ) break; // doesn't work for nonlinear scales const QRectF br = dataRect(); double spacing = 0.0; double sampleWidthS = 1.0; if ( layoutPolicy() == ScaleSamplesToAxes ) { sampleWidthS = qMax( d_data->layoutHint, 0.0 ); } else { spacing = d_data->spacing; if ( numSamples > 1 ) { sampleWidthS = qAbs( br.width() / ( numSamples - 1 ) ); } } double ds, w; if ( orientation() == Qt::Vertical ) { ds = qAbs( xMap.sDist() ); w = canvasRect.width(); } else { ds = qAbs( yMap.sDist() ); w = canvasRect.height(); } const double sampleWidthP = ( w - spacing * ds ) * sampleWidthS / ( ds + sampleWidthS ); hint = 0.5 * sampleWidthP; hint += qMax( d_data->margin, 0 ); } } if ( orientation() == Qt::Vertical ) { left = right = hint; top = bottom = -1.0; // no hint } else { left = right = -1.0; // no hint top = bottom = hint; } } connectome-workbench-1.2.3+git41-gc4c6c90/src/Qwt/qwt_plot_abstract_barchart.h000066400000000000000000000051111300200146000270700ustar00rootroot00000000000000/* -*- mode: C++ ; c-file-style: "stroustrup" -*- ***************************** * Qwt Widget Library * Copyright (C) 1997 Josef Wilgen * Copyright (C) 2002 Uwe Rathmann * * This library is free software; you can redistribute it and/or * modify it under the terms of the Qwt License, Version 1.0 *****************************************************************************/ #ifndef QWT_PLOT_ABSTRACT_BAR_CHART_H #define QWT_PLOT_ABSTRACT_BAR_CHART_H #include "qwt_global.h" #include "qwt_plot_seriesitem.h" #include "qwt_series_data.h" /*! \brief Abstract base class for bar chart items In opposite to almost all other plot items bar charts can't be displayed inside of their bounding rectangle and need a special API how to calculate the width of the bars and how they affect the layout of the attached plot. */ class QWT_EXPORT QwtPlotAbstractBarChart: public QwtPlotSeriesItem { public: /*! \brief Mode how to calculate the bar width setLayoutPolicy(), setLayoutHint(), barWidthHint() */ enum LayoutPolicy { /*! The sample width is calculated by dividing the bounding rectangle by the number of samples. The layoutHint() is used as a minimum width in paint device coordinates. \sa boundingRectangle() */ AutoAdjustSamples, /*! layoutHint() defines an interval in axis coordinates */ ScaleSamplesToAxes, /*! The bar width is calculated by multiplying layoutHint() with the height or width of the canvas. \sa boundingRectangle() */ ScaleSampleToCanvas, /*! layoutHint() defines a fixed width in paint device coordinates. */ FixedSampleSize }; explicit QwtPlotAbstractBarChart( const QwtText &title ); virtual ~QwtPlotAbstractBarChart(); void setLayoutPolicy( LayoutPolicy ); LayoutPolicy layoutPolicy() const; void setLayoutHint( double ); double layoutHint() const; void setSpacing( int ); int spacing() const; void setMargin( int ); int margin() const; void setBaseline( double ); double baseline() const; virtual void getCanvasMarginHint( const QwtScaleMap &xMap, const QwtScaleMap &yMap, const QRectF &canvasRect, double &left, double &top, double &right, double &bottom) const; protected: double sampleWidth( const QwtScaleMap &map, double canvasSize, double dataSize, double value ) const; private: class PrivateData; PrivateData *d_data; }; #endif connectome-workbench-1.2.3+git41-gc4c6c90/src/Qwt/qwt_plot_axis.cpp000066400000000000000000000433271300200146000247310ustar00rootroot00000000000000/* -*- mode: C++ ; c-file-style: "stroustrup" -*- ***************************** * Qwt Widget Library * Copyright (C) 1997 Josef Wilgen * Copyright (C) 2002 Uwe Rathmann * * This library is free software; you can redistribute it and/or * modify it under the terms of the Qwt License, Version 1.0 *****************************************************************************/ #include "qwt_plot.h" #include "qwt_math.h" #include "qwt_scale_widget.h" #include "qwt_scale_div.h" #include "qwt_scale_engine.h" class QwtPlot::AxisData { public: bool isEnabled; bool doAutoScale; double minValue; double maxValue; double stepSize; int maxMajor; int maxMinor; bool isValid; QwtScaleDiv scaleDiv; QwtScaleEngine *scaleEngine; QwtScaleWidget *scaleWidget; }; //! Initialize axes void QwtPlot::initAxesData() { int axisId; for ( axisId = 0; axisId < axisCnt; axisId++ ) d_axisData[axisId] = new AxisData; d_axisData[yLeft]->scaleWidget = new QwtScaleWidget( QwtScaleDraw::LeftScale, this ); d_axisData[yRight]->scaleWidget = new QwtScaleWidget( QwtScaleDraw::RightScale, this ); d_axisData[xTop]->scaleWidget = new QwtScaleWidget( QwtScaleDraw::TopScale, this ); d_axisData[xBottom]->scaleWidget = new QwtScaleWidget( QwtScaleDraw::BottomScale, this ); d_axisData[yLeft]->scaleWidget->setObjectName( "QwtPlotAxisYLeft" ); d_axisData[yRight]->scaleWidget->setObjectName( "QwtPlotAxisYRight" ); d_axisData[xTop]->scaleWidget->setObjectName( "QwtPlotAxisXTop" ); d_axisData[xBottom]->scaleWidget->setObjectName( "QwtPlotAxisXBottom" ); #if 1 // better find the font sizes from the application font QFont fscl( fontInfo().family(), 10 ); QFont fttl( fontInfo().family(), 12, QFont::Bold ); #endif for ( axisId = 0; axisId < axisCnt; axisId++ ) { AxisData &d = *d_axisData[axisId]; d.scaleEngine = new QwtLinearScaleEngine; d.scaleWidget->setTransformation( d.scaleEngine->transformation() ); d.scaleWidget->setFont( fscl ); d.scaleWidget->setMargin( 2 ); QwtText text = d.scaleWidget->title(); text.setFont( fttl ); d.scaleWidget->setTitle( text ); d.doAutoScale = true; d.minValue = 0.0; d.maxValue = 1000.0; d.stepSize = 0.0; d.maxMinor = 5; d.maxMajor = 8; d.isValid = false; } d_axisData[yLeft]->isEnabled = true; d_axisData[yRight]->isEnabled = false; d_axisData[xBottom]->isEnabled = true; d_axisData[xTop]->isEnabled = false; } void QwtPlot::deleteAxesData() { for ( int axisId = 0; axisId < axisCnt; axisId++ ) { delete d_axisData[axisId]->scaleEngine; delete d_axisData[axisId]; d_axisData[axisId] = NULL; } } /*! \return Scale widget of the specified axis, or NULL if axisId is invalid. \param axisId Axis index */ const QwtScaleWidget *QwtPlot::axisWidget( int axisId ) const { if ( axisValid( axisId ) ) return d_axisData[axisId]->scaleWidget; return NULL; } /*! \return Scale widget of the specified axis, or NULL if axisId is invalid. \param axisId Axis index */ QwtScaleWidget *QwtPlot::axisWidget( int axisId ) { if ( axisValid( axisId ) ) return d_axisData[axisId]->scaleWidget; return NULL; } /*! Change the scale engine for an axis \param axisId Axis index \param scaleEngine Scale engine \sa axisScaleEngine() */ void QwtPlot::setAxisScaleEngine( int axisId, QwtScaleEngine *scaleEngine ) { if ( axisValid( axisId ) && scaleEngine != NULL ) { AxisData &d = *d_axisData[axisId]; delete d.scaleEngine; d.scaleEngine = scaleEngine; d_axisData[axisId]->scaleWidget->setTransformation( scaleEngine->transformation() ); d.isValid = false; autoRefresh(); } } /*! \param axisId Axis index \return Scale engine for a specific axis */ QwtScaleEngine *QwtPlot::axisScaleEngine( int axisId ) { if ( axisValid( axisId ) ) return d_axisData[axisId]->scaleEngine; else return NULL; } /*! \param axisId Axis index \return Scale engine for a specific axis */ const QwtScaleEngine *QwtPlot::axisScaleEngine( int axisId ) const { if ( axisValid( axisId ) ) return d_axisData[axisId]->scaleEngine; else return NULL; } /*! \return \c True, if autoscaling is enabled \param axisId Axis index */ bool QwtPlot::axisAutoScale( int axisId ) const { if ( axisValid( axisId ) ) return d_axisData[axisId]->doAutoScale; else return false; } /*! \return \c True, if a specified axis is enabled \param axisId Axis index */ bool QwtPlot::axisEnabled( int axisId ) const { if ( axisValid( axisId ) ) return d_axisData[axisId]->isEnabled; else return false; } /*! \return The font of the scale labels for a specified axis \param axisId Axis index */ QFont QwtPlot::axisFont( int axisId ) const { if ( axisValid( axisId ) ) return axisWidget( axisId )->font(); else return QFont(); } /*! \return The maximum number of major ticks for a specified axis \param axisId Axis index \sa setAxisMaxMajor(), QwtScaleEngine::divideScale() */ int QwtPlot::axisMaxMajor( int axisId ) const { if ( axisValid( axisId ) ) return d_axisData[axisId]->maxMajor; else return 0; } /*! \return the maximum number of minor ticks for a specified axis \param axisId Axis index \sa setAxisMaxMinor(), QwtScaleEngine::divideScale() */ int QwtPlot::axisMaxMinor( int axisId ) const { if ( axisValid( axisId ) ) return d_axisData[axisId]->maxMinor; else return 0; } /*! \brief Return the scale division of a specified axis axisScaleDiv(axisId).lowerBound(), axisScaleDiv(axisId).upperBound() are the current limits of the axis scale. \param axisId Axis index \return Scale division \sa QwtScaleDiv, setAxisScaleDiv(), QwtScaleEngine::divideScale() */ const QwtScaleDiv &QwtPlot::axisScaleDiv( int axisId ) const { return d_axisData[axisId]->scaleDiv; } /*! \brief Return the scale draw of a specified axis \param axisId Axis index \return Specified scaleDraw for axis, or NULL if axis is invalid. */ const QwtScaleDraw *QwtPlot::axisScaleDraw( int axisId ) const { if ( !axisValid( axisId ) ) return NULL; return axisWidget( axisId )->scaleDraw(); } /*! \brief Return the scale draw of a specified axis \param axisId Axis index \return Specified scaleDraw for axis, or NULL if axis is invalid. */ QwtScaleDraw *QwtPlot::axisScaleDraw( int axisId ) { if ( !axisValid( axisId ) ) return NULL; return axisWidget( axisId )->scaleDraw(); } /*! \brief Return the step size parameter that has been set in setAxisScale. This doesn't need to be the step size of the current scale. \param axisId Axis index \return step size parameter value \sa setAxisScale(), QwtScaleEngine::divideScale() */ double QwtPlot::axisStepSize( int axisId ) const { if ( !axisValid( axisId ) ) return 0; return d_axisData[axisId]->stepSize; } /*! \brief Return the current interval of the specified axis This is only a convenience function for axisScaleDiv( axisId )->interval(); \param axisId Axis index \return Scale interval \sa QwtScaleDiv, axisScaleDiv() */ QwtInterval QwtPlot::axisInterval( int axisId ) const { if ( !axisValid( axisId ) ) return QwtInterval(); return d_axisData[axisId]->scaleDiv.interval(); } /*! \return Title of a specified axis \param axisId Axis index */ QwtText QwtPlot::axisTitle( int axisId ) const { if ( axisValid( axisId ) ) return axisWidget( axisId )->title(); else return QwtText(); } /*! \brief Enable or disable a specified axis When an axis is disabled, this only means that it is not visible on the screen. Curves, markers and can be attached to disabled axes, and transformation of screen coordinates into values works as normal. Only xBottom and yLeft are enabled by default. \param axisId Axis index \param tf \c true (enabled) or \c false (disabled) */ void QwtPlot::enableAxis( int axisId, bool tf ) { if ( axisValid( axisId ) && tf != d_axisData[axisId]->isEnabled ) { d_axisData[axisId]->isEnabled = tf; updateLayout(); } } /*! Transform the x or y coordinate of a position in the drawing region into a value. \param axisId Axis index \param pos position \return Position as axis coordinate \warning The position can be an x or a y coordinate, depending on the specified axis. */ double QwtPlot::invTransform( int axisId, int pos ) const { if ( axisValid( axisId ) ) return( canvasMap( axisId ).invTransform( pos ) ); else return 0.0; } /*! \brief Transform a value into a coordinate in the plotting region \param axisId Axis index \param value value \return X or Y coordinate in the plotting region corresponding to the value. */ double QwtPlot::transform( int axisId, double value ) const { if ( axisValid( axisId ) ) return( canvasMap( axisId ).transform( value ) ); else return 0.0; } /*! \brief Change the font of an axis \param axisId Axis index \param font Font \warning This function changes the font of the tick labels, not of the axis title. */ void QwtPlot::setAxisFont( int axisId, const QFont &font ) { if ( axisValid( axisId ) ) axisWidget( axisId )->setFont( font ); } /*! \brief Enable autoscaling for a specified axis This member function is used to switch back to autoscaling mode after a fixed scale has been set. Autoscaling is enabled by default. \param axisId Axis index \param on On/Off \sa setAxisScale(), setAxisScaleDiv(), updateAxes() \note The autoscaling flag has no effect until updateAxes() is executed ( called by replot() ). */ void QwtPlot::setAxisAutoScale( int axisId, bool on ) { if ( axisValid( axisId ) && ( d_axisData[axisId]->doAutoScale != on ) ) { d_axisData[axisId]->doAutoScale = on; autoRefresh(); } } /*! \brief Disable autoscaling and specify a fixed scale for a selected axis. In updateAxes() the scale engine calculates a scale division from the specified parameters, that will be assigned to the scale widget. So updates of the scale widget usually happen delayed with the next replot. \param axisId Axis index \param min Minimum of the scale \param max Maximum of the scale \param stepSize Major step size. If step == 0, the step size is calculated automatically using the maxMajor setting. \sa setAxisMaxMajor(), setAxisAutoScale(), axisStepSize(), QwtScaleEngine::divideScale() */ void QwtPlot::setAxisScale( int axisId, double min, double max, double stepSize ) { if ( axisValid( axisId ) ) { AxisData &d = *d_axisData[axisId]; d.doAutoScale = false; d.isValid = false; d.minValue = min; d.maxValue = max; d.stepSize = stepSize; autoRefresh(); } } /*! \brief Disable autoscaling and specify a fixed scale for a selected axis. The scale division will be stored locally only until the next call of updateAxes(). So updates of the scale widget usually happen delayed with the next replot. \param axisId Axis index \param scaleDiv Scale division \sa setAxisScale(), setAxisAutoScale() */ void QwtPlot::setAxisScaleDiv( int axisId, const QwtScaleDiv &scaleDiv ) { if ( axisValid( axisId ) ) { AxisData &d = *d_axisData[axisId]; d.doAutoScale = false; d.scaleDiv = scaleDiv; d.isValid = true; autoRefresh(); } } /*! \brief Set a scale draw \param axisId Axis index \param scaleDraw Object responsible for drawing scales. By passing scaleDraw it is possible to extend QwtScaleDraw functionality and let it take place in QwtPlot. Please note that scaleDraw has to be created with new and will be deleted by the corresponding QwtScale member ( like a child object ). \sa QwtScaleDraw, QwtScaleWidget \warning The attributes of scaleDraw will be overwritten by those of the previous QwtScaleDraw. */ void QwtPlot::setAxisScaleDraw( int axisId, QwtScaleDraw *scaleDraw ) { if ( axisValid( axisId ) ) { axisWidget( axisId )->setScaleDraw( scaleDraw ); autoRefresh(); } } /*! Change the alignment of the tick labels \param axisId Axis index \param alignment Or'd Qt::AlignmentFlags see \sa QwtScaleDraw::setLabelAlignment() */ void QwtPlot::setAxisLabelAlignment( int axisId, Qt::Alignment alignment ) { if ( axisValid( axisId ) ) axisWidget( axisId )->setLabelAlignment( alignment ); } /*! Rotate all tick labels \param axisId Axis index \param rotation Angle in degrees. When changing the label rotation, the label alignment might be adjusted too. \sa QwtScaleDraw::setLabelRotation(), setAxisLabelAlignment() */ void QwtPlot::setAxisLabelRotation( int axisId, double rotation ) { if ( axisValid( axisId ) ) axisWidget( axisId )->setLabelRotation( rotation ); } /*! Set the maximum number of minor scale intervals for a specified axis \param axisId Axis index \param maxMinor Maximum number of minor steps \sa axisMaxMinor() */ void QwtPlot::setAxisMaxMinor( int axisId, int maxMinor ) { if ( axisValid( axisId ) ) { maxMinor = qBound( 0, maxMinor, 100 ); AxisData &d = *d_axisData[axisId]; if ( maxMinor != d.maxMinor ) { d.maxMinor = maxMinor; d.isValid = false; autoRefresh(); } } } /*! Set the maximum number of major scale intervals for a specified axis \param axisId Axis index \param maxMajor Maximum number of major steps \sa axisMaxMajor() */ void QwtPlot::setAxisMaxMajor( int axisId, int maxMajor ) { if ( axisValid( axisId ) ) { maxMajor = qBound( 1, maxMajor, 10000 ); AxisData &d = *d_axisData[axisId]; if ( maxMajor != d.maxMajor ) { d.maxMajor = maxMajor; d.isValid = false; autoRefresh(); } } } /*! \brief Change the title of a specified axis \param axisId Axis index \param title axis title */ void QwtPlot::setAxisTitle( int axisId, const QString &title ) { if ( axisValid( axisId ) ) axisWidget( axisId )->setTitle( title ); } /*! \brief Change the title of a specified axis \param axisId Axis index \param title Axis title */ void QwtPlot::setAxisTitle( int axisId, const QwtText &title ) { if ( axisValid( axisId ) ) axisWidget( axisId )->setTitle( title ); } /*! \brief Rebuild the axes scales In case of autoscaling the boundaries of a scale are calculated from the bounding rectangles of all plot items, having the QwtPlotItem::AutoScale flag enabled ( QwtScaleEngine::autoScale() ). Then a scale division is calculated ( QwtScaleEngine::didvideScale() ) and assigned to scale widget. When the scale boundaries have been assigned with setAxisScale() a scale division is calculated ( QwtScaleEngine::didvideScale() ) for this interval and assigned to the scale widget. When the scale has been set explicitly by setAxisScaleDiv() the locally stored scale division gets assigned to the scale widget. The scale widget indicates modifications by emitting a QwtScaleWidget::scaleDivChanged() signal. updateAxes() is usually called by replot(). \sa setAxisAutoScale(), setAxisScale(), setAxisScaleDiv(), replot() QwtPlotItem::boundingRect() */ void QwtPlot::updateAxes() { // Find bounding interval of the item data // for all axes, where autoscaling is enabled QwtInterval intv[axisCnt]; const QwtPlotItemList& itmList = itemList(); QwtPlotItemIterator it; for ( it = itmList.begin(); it != itmList.end(); ++it ) { const QwtPlotItem *item = *it; if ( !item->testItemAttribute( QwtPlotItem::AutoScale ) ) continue; if ( !item->isVisible() ) continue; if ( axisAutoScale( item->xAxis() ) || axisAutoScale( item->yAxis() ) ) { const QRectF rect = item->boundingRect(); if ( rect.width() >= 0.0 ) intv[item->xAxis()] |= QwtInterval( rect.left(), rect.right() ); if ( rect.height() >= 0.0 ) intv[item->yAxis()] |= QwtInterval( rect.top(), rect.bottom() ); } } // Adjust scales for ( int axisId = 0; axisId < axisCnt; axisId++ ) { AxisData &d = *d_axisData[axisId]; double minValue = d.minValue; double maxValue = d.maxValue; double stepSize = d.stepSize; if ( d.doAutoScale && intv[axisId].isValid() ) { d.isValid = false; minValue = intv[axisId].minValue(); maxValue = intv[axisId].maxValue(); d.scaleEngine->autoScale( d.maxMajor, minValue, maxValue, stepSize ); } if ( !d.isValid ) { d.scaleDiv = d.scaleEngine->divideScale( minValue, maxValue, d.maxMajor, d.maxMinor, stepSize ); d.isValid = true; } QwtScaleWidget *scaleWidget = axisWidget( axisId ); scaleWidget->setScaleDiv( d.scaleDiv ); int startDist, endDist; scaleWidget->getBorderDistHint( startDist, endDist ); scaleWidget->setBorderDist( startDist, endDist ); } for ( it = itmList.begin(); it != itmList.end(); ++it ) { QwtPlotItem *item = *it; if ( item->testItemInterest( QwtPlotItem::ScaleInterest ) ) { item->updateScaleDiv( axisScaleDiv( item->xAxis() ), axisScaleDiv( item->yAxis() ) ); } } } connectome-workbench-1.2.3+git41-gc4c6c90/src/Qwt/qwt_plot_barchart.cpp000066400000000000000000000264221300200146000255500ustar00rootroot00000000000000/* -*- mode: C++ ; c-file-style: "stroustrup" -*- ***************************** * Qwt Widget Library * Copyright (C) 1997 Josef Wilgen * Copyright (C) 2002 Uwe Rathmann * * This library is free software; you can redistribute it and/or * modify it under the terms of the Qwt License, Version 1.0 *****************************************************************************/ #include "qwt_plot_barchart.h" #include "qwt_scale_map.h" #include "qwt_column_symbol.h" #include "qwt_painter.h" #include class QwtPlotBarChart::PrivateData { public: PrivateData(): symbol( NULL ), legendMode( QwtPlotBarChart::LegendChartTitle ) { } ~PrivateData() { delete symbol; } QwtColumnSymbol *symbol; QwtPlotBarChart::LegendMode legendMode; }; /*! Constructor \param title Title of the curve */ QwtPlotBarChart::QwtPlotBarChart( const QwtText &title ): QwtPlotAbstractBarChart( title ) { init(); } /*! Constructor \param title Title of the curve */ QwtPlotBarChart::QwtPlotBarChart( const QString &title ): QwtPlotAbstractBarChart( QwtText( title ) ) { init(); } //! Destructor QwtPlotBarChart::~QwtPlotBarChart() { delete d_data; } void QwtPlotBarChart::init() { d_data = new PrivateData; setData( new QwtPointSeriesData() ); } //! \return QwtPlotItem::Rtti_PlotBarChart int QwtPlotBarChart::rtti() const { return QwtPlotItem::Rtti_PlotBarChart; } /*! Initialize data with an array of points \param samples Vector of points \note QVector is implicitly shared \note QPolygonF is derived from QVector */ void QwtPlotBarChart::setSamples( const QVector &samples ) { setData( new QwtPointSeriesData( samples ) ); } /*! Initialize data with an array of doubles The indices in the array are taken as x coordinate, while the doubles are interpreted as y values. \param samples Vector of y coordinates \note QVector is implicitly shared */ void QwtPlotBarChart::setSamples( const QVector &samples ) { QVector points; for ( int i = 0; i < samples.size(); i++ ) points += QPointF( i, samples[ i ] ); setData( new QwtPointSeriesData( points ) ); } /*! Assign a series of samples setSamples() is just a wrapper for setData() without any additional value - beside that it is easier to find for the developer. \param data Data \warning The item takes ownership of the data object, deleting it when its not used anymore. */ void QwtPlotBarChart::setSamples( QwtSeriesData *data ) { setData( data ); } /*! \brief Assign a symbol The bar chart will take the ownership of the symbol, hence the previously set symbol will be delete by setting a new one. If \p symbol is \c NULL no symbol will be drawn. \param symbol Symbol \sa symbol() */ void QwtPlotBarChart::setSymbol( QwtColumnSymbol *symbol ) { if ( symbol != d_data->symbol ) { delete d_data->symbol; d_data->symbol = symbol; legendChanged(); itemChanged(); } } /*! \return Current symbol or NULL, when no symbol has been assigned \sa setSymbol() */ const QwtColumnSymbol *QwtPlotBarChart::symbol() const { return d_data->symbol; } /*! Set the mode that decides what to display on the legend In case of LegendBarTitles barTitle() needs to be overloaded to return individual titles for each bar. \param mode New mode \sa legendMode(), legendData(), barTitle(), QwtPlotItem::ItemAttribute */ void QwtPlotBarChart::setLegendMode( LegendMode mode ) { if ( mode != d_data->legendMode ) { d_data->legendMode = mode; legendChanged(); } } /*! \return Legend mode \sa setLegendMode() */ QwtPlotBarChart::LegendMode QwtPlotBarChart::legendMode() const { return d_data->legendMode; } /*! \return Bounding rectangle of all samples. For an empty series the rectangle is invalid. */ QRectF QwtPlotBarChart::boundingRect() const { const size_t numSamples = dataSize(); if ( numSamples == 0 ) return QwtPlotSeriesItem::boundingRect(); QRectF rect = QwtPlotSeriesItem::boundingRect(); if ( rect.height() >= 0 ) { const double baseLine = baseline(); if ( rect.bottom() < baseLine ) rect.setBottom( baseLine ); if ( rect.top() > baseLine ) rect.setTop( baseLine ); } if ( orientation() == Qt::Horizontal ) rect.setRect( rect.y(), rect.x(), rect.height(), rect.width() ); return rect; } /*! Draw an interval of the bar chart \param painter Painter \param xMap Maps x-values into pixel coordinates. \param yMap Maps y-values into pixel coordinates. \param canvasRect Contents rect of the canvas \param from Index of the first point to be painted \param to Index of the last point to be painted. If to < 0 the curve will be painted to its last point. \sa drawSymbols() */ void QwtPlotBarChart::drawSeries( QPainter *painter, const QwtScaleMap &xMap, const QwtScaleMap &yMap, const QRectF &canvasRect, int from, int to ) const { if ( to < 0 ) to = dataSize() - 1; if ( from < 0 ) from = 0; if ( from > to ) return; const QRectF br = data()->boundingRect(); const QwtInterval interval( br.left(), br.right() ); painter->save(); for ( int i = from; i <= to; i++ ) { drawSample( painter, xMap, yMap, canvasRect, interval, i, sample( i ) ); } painter->restore(); } /*! Draw a sample \param painter Painter \param xMap x map \param yMap y map \param canvasRect Contents rect of the canvas \param boundingInterval Bounding interval of sample values \param index Index of the sample \param sample Value of the sample \sa drawSeries() */ void QwtPlotBarChart::drawSample( QPainter *painter, const QwtScaleMap &xMap, const QwtScaleMap &yMap, const QRectF &canvasRect, const QwtInterval &boundingInterval, int index, const QPointF &sample ) const { QwtColumnRect barRect; if ( orientation() == Qt::Horizontal ) { const double barHeight = sampleWidth( yMap, canvasRect.height(), boundingInterval.width(), sample.y() ); const double x1 = xMap.transform( baseline() ); const double x2 = xMap.transform( sample.y() ); const double y = yMap.transform( sample.x() ); const double y1 = y - 0.5 * barHeight; const double y2 = y + 0.5 * barHeight; barRect.direction = ( x1 < x2 ) ? QwtColumnRect::LeftToRight : QwtColumnRect::RightToLeft; barRect.hInterval = QwtInterval( x1, x2 ).normalized(); barRect.vInterval = QwtInterval( y1, y2 ); } else { const double barWidth = sampleWidth( xMap, canvasRect.width(), boundingInterval.width(), sample.y() ); const double x = xMap.transform( sample.x() ); const double x1 = x - 0.5 * barWidth; const double x2 = x + 0.5 * barWidth; const double y1 = yMap.transform( baseline() ); const double y2 = yMap.transform( sample.y() ); barRect.direction = ( y1 < y2 ) ? QwtColumnRect::TopToBottom : QwtColumnRect::BottomToTop; barRect.hInterval = QwtInterval( x1, x2 ); barRect.vInterval = QwtInterval( y1, y2 ).normalized(); } drawBar( painter, index, sample, barRect ); } /*! Draw a bar \param painter Painter \param sampleIndex Index of the sample represented by the bar \param sample Value of the sample \param rect Bounding rectangle of the bar */ void QwtPlotBarChart::drawBar( QPainter *painter, int sampleIndex, const QPointF &sample, const QwtColumnRect &rect ) const { const QwtColumnSymbol *specialSym = specialSymbol( sampleIndex, sample ); const QwtColumnSymbol *sym = specialSym; if ( sym == NULL ) sym = d_data->symbol; if ( sym ) { sym->draw( painter, rect ); } else { // we build a temporary default symbol QwtColumnSymbol sym( QwtColumnSymbol::Box ); sym.setLineWidth( 1 ); sym.setFrameStyle( QwtColumnSymbol::Plain ); sym.draw( painter, rect ); } delete specialSym; } /*! Needs to be overloaded to return a non default symbol for a specific sample \param sampleIndex Index of the sample represented by the bar \param sample Value of the sample \return NULL, indicating to use the default symbol */ QwtColumnSymbol *QwtPlotBarChart::specialSymbol( int sampleIndex, const QPointF &sample ) const { Q_UNUSED( sampleIndex ); Q_UNUSED( sample ); return NULL; } /*! \brief Return the title of a bar In LegendBarTitles mode the title is displayed on the legend entry corresponding to a bar. The default implementation is a dummy, that is intended to be overloaded. \param sampleIndex Index of the bar \return An empty text \sa LegendBarTitles */ QwtText QwtPlotBarChart::barTitle( int sampleIndex ) const { Q_UNUSED( sampleIndex ); return QwtText(); } /*! \brief Return all information, that is needed to represent the item on the legend In case of LegendBarTitles an entry for each bar is returned, otherwise the chart is represented like any other plot item from its title() and the legendIcon(). \return Information, that is needed to represent the item on the legend \sa title(), setLegendMode(), barTitle(), QwtLegend, QwtPlotLegendItem */ QList QwtPlotBarChart::legendData() const { QList list; if ( d_data->legendMode == LegendBarTitles ) { const size_t numSamples = dataSize(); for ( size_t i = 0; i < numSamples; i++ ) { QwtLegendData data; QVariant titleValue; qVariantSetValue( titleValue, barTitle( i ) ); data.setValue( QwtLegendData::TitleRole, titleValue ); if ( !legendIconSize().isEmpty() ) { QVariant iconValue; qVariantSetValue( iconValue, legendIcon( i, legendIconSize() ) ); data.setValue( QwtLegendData::IconRole, iconValue ); } list += data; } } else { return QwtPlotAbstractBarChart::legendData(); } return list; } /*! \return Icon representing a bar or the chart on the legend When the legendMode() is LegendBarTitles the icon shows the bar corresponding to index - otherwise the bar displays the default symbol. \param index Index of the legend entry \param size Icon size \sa setLegendMode(), drawBar(), QwtPlotItem::setLegendIconSize(), QwtPlotItem::legendData() */ QwtGraphic QwtPlotBarChart::legendIcon( int index, const QSizeF &size ) const { QwtColumnRect column; column.hInterval = QwtInterval( 0.0, size.width() - 1.0 ); column.vInterval = QwtInterval( 0.0, size.height() - 1.0 ); QwtGraphic icon; icon.setDefaultSize( size ); icon.setRenderHint( QwtGraphic::RenderPensUnscaled, true ); QPainter painter( &icon ); painter.setRenderHint( QPainter::Antialiasing, testRenderHint( QwtPlotItem::RenderAntialiased ) ); int barIndex = -1; if ( d_data->legendMode == QwtPlotBarChart::LegendBarTitles ) barIndex = index; drawBar( &painter, barIndex, QPointF(), column ); return icon; } connectome-workbench-1.2.3+git41-gc4c6c90/src/Qwt/qwt_plot_barchart.h000066400000000000000000000067461300200146000252240ustar00rootroot00000000000000/* -*- mode: C++ ; c-file-style: "stroustrup" -*- ***************************** * Qwt Widget Library * Copyright (C) 1997 Josef Wilgen * Copyright (C) 2002 Uwe Rathmann * * This library is free software; you can redistribute it and/or * modify it under the terms of the Qwt License, Version 1.0 *****************************************************************************/ #ifndef QWT_PLOT_BAR_CHART_H #define QWT_PLOT_BAR_CHART_H #include "qwt_global.h" #include "qwt_plot_abstract_barchart.h" #include "qwt_series_data.h" class QwtColumnRect; class QwtColumnSymbol; /*! \brief QwtPlotBarChart displays a series of a values as bars. Each bar might be customized individually by implementing a specialSymbol(). Otherwise it is rendered using a default symbol. Depending on its orientation() the bars are displayed horizontally or vertically. The bars cover the interval between the baseline() and the value. By activating the LegendBarTitles mode each sample will have its own entry on the legend. The most common use case of a bar chart is to display a list of y coordinates, where the x coordinate is simply the index in the list. But for other situations ( f.e. when values are related to dates ) it is also possible to set x coordinates explicitly. \sa QwtPlotMultiBarChart, QwtPlotHistogram, QwtPlotCurve::Sticks, QwtPlotSeriesItem::orientation(), QwtPlotAbstractBarChart::baseline() */ class QWT_EXPORT QwtPlotBarChart: public QwtPlotAbstractBarChart, public QwtSeriesStore { public: /*! \brief Legend modes. The default setting is QwtPlotBarChart::LegendChartTitle. \sa setLegendMode(), legendMode() */ enum LegendMode { /*! One entry on the legend showing the default symbol and the title() of the chart \sa QwtPlotItem::title() */ LegendChartTitle, /*! One entry for each value showing the individual symbol of the corresponding bar and the bar title. \sa specialSymbol(), barTitle() */ LegendBarTitles }; explicit QwtPlotBarChart( const QString &title = QString::null ); explicit QwtPlotBarChart( const QwtText &title ); virtual ~QwtPlotBarChart(); virtual int rtti() const; void setSamples( const QVector & ); void setSamples( const QVector & ); void setSamples( QwtSeriesData *series ); void setSymbol( QwtColumnSymbol * ); const QwtColumnSymbol *symbol() const; void setLegendMode( LegendMode ); LegendMode legendMode() const; virtual void drawSeries( QPainter *painter, const QwtScaleMap &xMap, const QwtScaleMap &yMap, const QRectF &canvasRect, int from, int to ) const; virtual QRectF boundingRect() const; virtual QwtColumnSymbol *specialSymbol( int sampleIndex, const QPointF& ) const; virtual QwtText barTitle( int sampleIndex ) const; protected: virtual void drawSample( QPainter *painter, const QwtScaleMap &xMap, const QwtScaleMap &yMap, const QRectF &canvasRect, const QwtInterval &boundingInterval, int index, const QPointF& sample ) const; virtual void drawBar( QPainter *, int sampleIndex, const QPointF& point, const QwtColumnRect & ) const; QList legendData() const; QwtGraphic legendIcon( int index, const QSizeF & ) const; private: void init(); class PrivateData; PrivateData *d_data; }; #endif connectome-workbench-1.2.3+git41-gc4c6c90/src/Qwt/qwt_plot_canvas.cpp000066400000000000000000000674511300200146000252440ustar00rootroot00000000000000/* -*- mode: C++ ; c-file-style: "stroustrup" -*- ***************************** * Qwt Widget Library * Copyright (C) 1997 Josef Wilgen * Copyright (C) 2002 Uwe Rathmann * * This library is free software; you can redistribute it and/or * modify it under the terms of the Qwt License, Version 1.0 *****************************************************************************/ #include "qwt_plot_canvas.h" #include "qwt_painter.h" #include "qwt_null_paintdevice.h" #include "qwt_math.h" #include "qwt_plot.h" #include #include #include #include #include class QwtStyleSheetRecorder: public QwtNullPaintDevice { public: QwtStyleSheetRecorder( const QSize &size ): d_size( size ) { } virtual void updateState( const QPaintEngineState &state ) { if ( state.state() & QPaintEngine::DirtyPen ) { d_pen = state.pen(); } if ( state.state() & QPaintEngine::DirtyBrush ) { d_brush = state.brush(); } if ( state.state() & QPaintEngine::DirtyBrushOrigin ) { d_origin = state.brushOrigin(); } } virtual void drawRects(const QRectF *rects, int count ) { for ( int i = 0; i < count; i++ ) border.rectList += rects[i]; } virtual void drawPath( const QPainterPath &path ) { const QRectF rect( QPointF( 0.0, 0.0 ), d_size ); if ( path.controlPointRect().contains( rect.center() ) ) { setCornerRects( path ); alignCornerRects( rect ); background.path = path; background.brush = d_brush; background.origin = d_origin; } else { border.pathList += path; } } void setCornerRects( const QPainterPath &path ) { QPointF pos( 0.0, 0.0 ); for ( int i = 0; i < path.elementCount(); i++ ) { QPainterPath::Element el = path.elementAt(i); switch( el.type ) { case QPainterPath::MoveToElement: case QPainterPath::LineToElement: { pos.setX( el.x ); pos.setY( el.y ); break; } case QPainterPath::CurveToElement: { QRectF r( pos, QPointF( el.x, el.y ) ); clipRects += r.normalized(); pos.setX( el.x ); pos.setY( el.y ); break; } case QPainterPath::CurveToDataElement: { if ( clipRects.size() > 0 ) { QRectF r = clipRects.last(); r.setCoords( qMin( r.left(), el.x ), qMin( r.top(), el.y ), qMax( r.right(), el.x ), qMax( r.bottom(), el.y ) ); clipRects.last() = r.normalized(); } break; } } } } protected: virtual QSize sizeMetrics() const { return d_size; } private: void alignCornerRects( const QRectF &rect ) { for ( int i = 0; i < clipRects.size(); i++ ) { QRectF &r = clipRects[i]; if ( r.center().x() < rect.center().x() ) r.setLeft( rect.left() ); else r.setRight( rect.right() ); if ( r.center().y() < rect.center().y() ) r.setTop( rect.top() ); else r.setBottom( rect.bottom() ); } } public: QVector clipRects; struct Border { QList pathList; QList rectList; QRegion clipRegion; } border; struct Background { QPainterPath path; QBrush brush; QPointF origin; } background; private: const QSize d_size; QPen d_pen; QBrush d_brush; QPointF d_origin; }; static void qwtDrawBackground( QPainter *painter, QwtPlotCanvas *canvas ) { painter->save(); const QPainterPath borderClip = canvas->borderPath( canvas->rect() ); if ( !borderClip.isEmpty() ) painter->setClipPath( borderClip, Qt::IntersectClip ); const QBrush &brush = canvas->palette().brush( canvas->backgroundRole() ); if ( brush.style() == Qt::TexturePattern ) { QPixmap pm( canvas->size() ); QwtPainter::fillPixmap( canvas, pm ); painter->drawPixmap( 0, 0, pm ); } else if ( brush.gradient() ) { QVector rects; if ( brush.gradient()->coordinateMode() == QGradient::ObjectBoundingMode ) { rects += canvas->rect(); } else { rects = painter->clipRegion().rects(); } #if 1 bool useRaster = false; if ( painter->paintEngine()->type() == QPaintEngine::X11 ) { // Qt 4.7.1: gradients on X11 are broken ( subrects + // QGradient::StretchToDeviceMode ) and horrible slow. // As workaround we have to use the raster paintengine. // Even if the QImage -> QPixmap translation is slow // it is three times faster, than using X11 directly useRaster = true; } #endif if ( useRaster ) { QImage::Format format = QImage::Format_RGB32; const QGradientStops stops = brush.gradient()->stops(); for ( int i = 0; i < stops.size(); i++ ) { if ( stops[i].second.alpha() != 255 ) { // don't use Format_ARGB32_Premultiplied. It's // recommended by the Qt docs, but QPainter::drawImage() // is horrible slow on X11. format = QImage::Format_ARGB32; break; } } QImage image( canvas->size(), format ); QPainter p( &image ); p.setPen( Qt::NoPen ); p.setBrush( brush ); p.drawRects( rects ); p.end(); painter->drawImage( 0, 0, image ); } else { painter->setPen( Qt::NoPen ); painter->setBrush( brush ); painter->drawRects( rects ); } } else { painter->setPen( Qt::NoPen ); painter->setBrush( brush ); painter->drawRects( painter->clipRegion().rects() ); } painter->restore(); } static inline void qwtRevertPath( QPainterPath &path ) { if ( path.elementCount() == 4 ) { QPainterPath::Element el0 = path.elementAt(0); QPainterPath::Element el3 = path.elementAt(3); path.setElementPositionAt( 0, el3.x, el3.y ); path.setElementPositionAt( 3, el0.x, el0.y ); } } static QPainterPath qwtCombinePathList( const QRectF &rect, const QList &pathList ) { if ( pathList.isEmpty() ) return QPainterPath(); QPainterPath ordered[8]; // starting top left for ( int i = 0; i < pathList.size(); i++ ) { int index = -1; QPainterPath subPath = pathList[i]; const QRectF br = pathList[i].controlPointRect(); if ( br.center().x() < rect.center().x() ) { if ( br.center().y() < rect.center().y() ) { if ( qAbs( br.top() - rect.top() ) < qAbs( br.left() - rect.left() ) ) { index = 1; } else { index = 0; } } else { if ( qAbs( br.bottom() - rect.bottom() ) < qAbs( br.left() - rect.left() ) ) { index = 6; } else { index = 7; } } if ( subPath.currentPosition().y() > br.center().y() ) qwtRevertPath( subPath ); } else { if ( br.center().y() < rect.center().y() ) { if ( qAbs( br.top() - rect.top() ) < qAbs( br.right() - rect.right() ) ) { index = 2; } else { index = 3; } } else { if ( qAbs( br.bottom() - rect.bottom() ) < qAbs( br.right() - rect.right() ) ) { index = 5; } else { index = 4; } } if ( subPath.currentPosition().y() < br.center().y() ) qwtRevertPath( subPath ); } ordered[index] = subPath; } for ( int i = 0; i < 4; i++ ) { if ( ordered[ 2 * i].isEmpty() != ordered[2 * i + 1].isEmpty() ) { // we don't accept incomplete rounded borders return QPainterPath(); } } const QPolygonF corners( rect ); QPainterPath path; //path.moveTo( rect.topLeft() ); for ( int i = 0; i < 4; i++ ) { if ( ordered[2 * i].isEmpty() ) { path.lineTo( corners[i] ); } else { path.connectPath( ordered[2 * i] ); path.connectPath( ordered[2 * i + 1] ); } } path.closeSubpath(); #if 0 return path.simplified(); #else return path; #endif } static inline void qwtDrawStyledBackground( QWidget *w, QPainter *painter ) { QStyleOption opt; opt.initFrom(w); w->style()->drawPrimitive( QStyle::PE_Widget, &opt, painter, w); } static QWidget *qwtBackgroundWidget( QWidget *w ) { if ( w->parentWidget() == NULL ) return w; if ( w->autoFillBackground() ) { const QBrush brush = w->palette().brush( w->backgroundRole() ); if ( brush.color().alpha() > 0 ) return w; } if ( w->testAttribute( Qt::WA_StyledBackground ) ) { QImage image( 1, 1, QImage::Format_ARGB32 ); image.fill( Qt::transparent ); QPainter painter( &image ); painter.translate( -w->rect().center() ); qwtDrawStyledBackground( w, &painter ); painter.end(); if ( qAlpha( image.pixel( 0, 0 ) ) != 0 ) return w; } return qwtBackgroundWidget( w->parentWidget() ); } static void qwtFillBackground( QPainter *painter, QWidget *widget, const QVector &fillRects ) { if ( fillRects.isEmpty() ) return; QRegion clipRegion; if ( painter->hasClipping() ) clipRegion = painter->transform().map( painter->clipRegion() ); else clipRegion = widget->contentsRect(); // Try to find out which widget fills // the unfilled areas of the styled background QWidget *bgWidget = qwtBackgroundWidget( widget->parentWidget() ); for ( int i = 0; i < fillRects.size(); i++ ) { const QRect rect = fillRects[i].toAlignedRect(); if ( clipRegion.intersects( rect ) ) { QPixmap pm( rect.size() ); QwtPainter::fillPixmap( bgWidget, pm, widget->mapTo( bgWidget, rect.topLeft() ) ); painter->drawPixmap( rect, pm ); } } } static void qwtFillBackground( QPainter *painter, QwtPlotCanvas *canvas ) { QVector rects; if ( canvas->testAttribute( Qt::WA_StyledBackground ) ) { QwtStyleSheetRecorder recorder( canvas->size() ); QPainter p( &recorder ); qwtDrawStyledBackground( canvas, &p ); p.end(); if ( recorder.background.brush.isOpaque() ) rects = recorder.clipRects; else rects += canvas->rect(); } else { const QRectF r = canvas->rect(); const double radius = canvas->borderRadius(); if ( radius > 0.0 ) { QSizeF sz( radius, radius ); rects += QRectF( r.topLeft(), sz ); rects += QRectF( r.topRight() - QPointF( radius, 0 ), sz ); rects += QRectF( r.bottomRight() - QPointF( radius, radius ), sz ); rects += QRectF( r.bottomLeft() - QPointF( 0, radius ), sz ); } } qwtFillBackground( painter, canvas, rects); } class QwtPlotCanvas::PrivateData { public: PrivateData(): focusIndicator( NoFocusIndicator ), borderRadius( 0 ), paintAttributes( 0 ), backingStore( NULL ) { styleSheet.hasBorder = false; } ~PrivateData() { delete backingStore; } FocusIndicator focusIndicator; double borderRadius; QwtPlotCanvas::PaintAttributes paintAttributes; QPixmap *backingStore; struct StyleSheet { bool hasBorder; QPainterPath borderPath; QVector cornerRects; struct StyleSheetBackground { QBrush brush; QPointF origin; } background; } styleSheet; }; /*! \brief Constructor \param plot Parent plot widget \sa QwtPlot::setCanvas() */ QwtPlotCanvas::QwtPlotCanvas( QwtPlot *plot ): QFrame( plot ) { setFrameStyle( QFrame::Panel | QFrame::Sunken ); setLineWidth( 2 ); d_data = new PrivateData; #ifndef QT_NO_CURSOR setCursor( Qt::CrossCursor ); #endif setAutoFillBackground( true ); setPaintAttribute( QwtPlotCanvas::BackingStore, true ); setPaintAttribute( QwtPlotCanvas::Opaque, true ); setPaintAttribute( QwtPlotCanvas::HackStyledBackground, true ); } //! Destructor QwtPlotCanvas::~QwtPlotCanvas() { delete d_data; } //! Return parent plot widget QwtPlot *QwtPlotCanvas::plot() { return qobject_cast( parent() ); } //! Return parent plot widget const QwtPlot *QwtPlotCanvas::plot() const { return qobject_cast( parent() ); } /*! \brief Changing the paint attributes \param attribute Paint attribute \param on On/Off \sa testPaintAttribute(), backingStore() */ void QwtPlotCanvas::setPaintAttribute( PaintAttribute attribute, bool on ) { if ( bool( d_data->paintAttributes & attribute ) == on ) return; if ( on ) d_data->paintAttributes |= attribute; else d_data->paintAttributes &= ~attribute; switch ( attribute ) { case BackingStore: { if ( on ) { if ( d_data->backingStore == NULL ) d_data->backingStore = new QPixmap(); if ( isVisible() ) { #if QT_VERSION >= 0x050000 *d_data->backingStore = grab( rect() ); #else *d_data->backingStore = QPixmap::grabWidget( this, rect() ); #endif } } else { delete d_data->backingStore; d_data->backingStore = NULL; } break; } case Opaque: { if ( on ) setAttribute( Qt::WA_OpaquePaintEvent, true ); break; } case HackStyledBackground: case ImmediatePaint: { break; } } } /*! Test whether a paint attribute is enabled \param attribute Paint attribute \return true, when attribute is enabled \sa setPaintAttribute() */ bool QwtPlotCanvas::testPaintAttribute( PaintAttribute attribute ) const { return d_data->paintAttributes & attribute; } //! \return Backing store, might be null const QPixmap *QwtPlotCanvas::backingStore() const { return d_data->backingStore; } //! Invalidate the internal backing store void QwtPlotCanvas::invalidateBackingStore() { if ( d_data->backingStore ) *d_data->backingStore = QPixmap(); } /*! Set the focus indicator \sa FocusIndicator, focusIndicator() */ void QwtPlotCanvas::setFocusIndicator( FocusIndicator focusIndicator ) { d_data->focusIndicator = focusIndicator; } /*! \return Focus indicator \sa FocusIndicator, setFocusIndicator() */ QwtPlotCanvas::FocusIndicator QwtPlotCanvas::focusIndicator() const { return d_data->focusIndicator; } /*! Set the radius for the corners of the border frame \param radius Radius of a rounded corner \sa borderRadius() */ void QwtPlotCanvas::setBorderRadius( double radius ) { d_data->borderRadius = qMax( 0.0, radius ); } /*! \return Radius for the corners of the border frame \sa setBorderRadius() */ double QwtPlotCanvas::borderRadius() const { return d_data->borderRadius; } /*! Qt event handler for QEvent::PolishRequest and QEvent::StyleChange \param event Qt Event \return See QFrame::event() */ bool QwtPlotCanvas::event( QEvent *event ) { if ( event->type() == QEvent::PolishRequest ) { if ( testPaintAttribute( QwtPlotCanvas::Opaque ) ) { // Setting a style sheet changes the // Qt::WA_OpaquePaintEvent attribute, but we insist // on painting the background. setAttribute( Qt::WA_OpaquePaintEvent, true ); } } if ( event->type() == QEvent::PolishRequest || event->type() == QEvent::StyleChange ) { updateStyleSheetInfo(); } return QFrame::event( event ); } /*! Paint event \param event Paint event */ void QwtPlotCanvas::paintEvent( QPaintEvent *event ) { QPainter painter( this ); painter.setClipRegion( event->region() ); if ( testPaintAttribute( QwtPlotCanvas::BackingStore ) && d_data->backingStore != NULL ) { QPixmap &bs = *d_data->backingStore; if ( bs.size() != size() ) { bs = QwtPainter::backingStore( this, size() ); if ( testAttribute(Qt::WA_StyledBackground) ) { QPainter p( &bs ); qwtFillBackground( &p, this ); drawCanvas( &p, true ); } else { QPainter p; if ( d_data->borderRadius <= 0.0 ) { QwtPainter::fillPixmap( this, bs ); p.begin( &bs ); drawCanvas( &p, false ); } else { p.begin( &bs ); qwtFillBackground( &p, this ); drawCanvas( &p, true ); } if ( frameWidth() > 0 ) drawBorder( &p ); } } painter.drawPixmap( 0, 0, *d_data->backingStore ); } else { if ( testAttribute(Qt::WA_StyledBackground ) ) { if ( testAttribute( Qt::WA_OpaquePaintEvent ) ) { qwtFillBackground( &painter, this ); drawCanvas( &painter, true ); } else { drawCanvas( &painter, false ); } } else { if ( testAttribute( Qt::WA_OpaquePaintEvent ) ) { if ( autoFillBackground() ) { qwtFillBackground( &painter, this ); qwtDrawBackground( &painter, this ); } } else { if ( borderRadius() > 0.0 ) { QPainterPath clipPath; clipPath.addRect( rect() ); clipPath = clipPath.subtracted( borderPath( rect() ) ); painter.save(); painter.setClipPath( clipPath, Qt::IntersectClip ); qwtFillBackground( &painter, this ); qwtDrawBackground( &painter, this ); painter.restore(); } } drawCanvas( &painter, false ); if ( frameWidth() > 0 ) drawBorder( &painter ); } } if ( hasFocus() && focusIndicator() == CanvasFocusIndicator ) drawFocusIndicator( &painter ); } void QwtPlotCanvas::drawCanvas( QPainter *painter, bool withBackground ) { bool hackStyledBackground = false; if ( withBackground && testAttribute( Qt::WA_StyledBackground ) && testPaintAttribute( HackStyledBackground ) ) { // Antialiasing rounded borders is done by // inserting pixels with colors between the // border color and the color on the canvas, // When the border is painted before the plot items // these colors are interpolated for the canvas // and the plot items need to be clipped excluding // the anialiased pixels. In situations, where // the plot items fill the area at the rounded // borders this is noticeable. // The only way to avoid these annoying "artefacts" // is to paint the border on top of the plot items. if ( d_data->styleSheet.hasBorder && !d_data->styleSheet.borderPath.isEmpty() ) { // We have a border with at least one rounded corner hackStyledBackground = true; } } if ( withBackground ) { painter->save(); if ( testAttribute( Qt::WA_StyledBackground ) ) { if ( hackStyledBackground ) { // paint background without border painter->setPen( Qt::NoPen ); painter->setBrush( d_data->styleSheet.background.brush ); painter->setBrushOrigin( d_data->styleSheet.background.origin ); painter->setClipPath( d_data->styleSheet.borderPath ); painter->drawRect( contentsRect() ); } else { qwtDrawStyledBackground( this, painter ); } } else if ( autoFillBackground() ) { painter->setPen( Qt::NoPen ); painter->setBrush( palette().brush( backgroundRole() ) ); if ( d_data->borderRadius > 0.0 && ( rect() == frameRect() ) ) { if ( frameWidth() > 0 ) { painter->setClipPath( borderPath( rect() ) ); painter->drawRect( rect() ); } else { painter->setRenderHint( QPainter::Antialiasing, true ); painter->drawPath( borderPath( rect() ) ); } } else { painter->drawRect( rect() ); } } painter->restore(); } painter->save(); if ( !d_data->styleSheet.borderPath.isEmpty() ) { painter->setClipPath( d_data->styleSheet.borderPath, Qt::IntersectClip ); } else { if ( d_data->borderRadius > 0.0 ) painter->setClipPath( borderPath( frameRect() ), Qt::IntersectClip ); else painter->setClipRect( contentsRect(), Qt::IntersectClip ); } plot()->drawCanvas( painter ); painter->restore(); if ( withBackground && hackStyledBackground ) { // Now paint the border on top QStyleOptionFrame opt; opt.initFrom(this); style()->drawPrimitive( QStyle::PE_Frame, &opt, painter, this); } } /*! Draw the border of the plot canvas \param painter Painter \sa setBorderRadius() */ void QwtPlotCanvas::drawBorder( QPainter *painter ) { if ( d_data->borderRadius > 0 ) { if ( frameWidth() > 0 ) { QwtPainter::drawRoundedFrame( painter, QRectF( frameRect() ), d_data->borderRadius, d_data->borderRadius, palette(), frameWidth(), frameStyle() ); } } else { #if QT_VERSION >= 0x040500 QStyleOptionFrameV3 opt; opt.init(this); int frameShape = frameStyle() & QFrame::Shape_Mask; int frameShadow = frameStyle() & QFrame::Shadow_Mask; opt.frameShape = QFrame::Shape( int( opt.frameShape ) | frameShape ); #if 0 opt.rect = frameRect(); #endif switch (frameShape) { case QFrame::Box: case QFrame::HLine: case QFrame::VLine: case QFrame::StyledPanel: case QFrame::Panel: { opt.lineWidth = lineWidth(); opt.midLineWidth = midLineWidth(); break; } default: { opt.lineWidth = frameWidth(); break; } } if ( frameShadow == Sunken ) opt.state |= QStyle::State_Sunken; else if ( frameShadow == Raised ) opt.state |= QStyle::State_Raised; style()->drawControl(QStyle::CE_ShapedFrame, &opt, painter, this); #else drawFrame( painter ); #endif } } /*! Resize event \param event Resize event */ void QwtPlotCanvas::resizeEvent( QResizeEvent *event ) { QFrame::resizeEvent( event ); updateStyleSheetInfo(); } /*! Draw the focus indication \param painter Painter */ void QwtPlotCanvas::drawFocusIndicator( QPainter *painter ) { const int margin = 1; QRect focusRect = contentsRect(); focusRect.setRect( focusRect.x() + margin, focusRect.y() + margin, focusRect.width() - 2 * margin, focusRect.height() - 2 * margin ); QwtPainter::drawFocusRect( painter, this, focusRect ); } /*! Invalidate the paint cache and repaint the canvas \sa invalidatePaintCache() */ void QwtPlotCanvas::replot() { invalidateBackingStore(); if ( testPaintAttribute( QwtPlotCanvas::ImmediatePaint ) ) repaint( contentsRect() ); else update( contentsRect() ); } //! Update the cached information about the current style sheet void QwtPlotCanvas::updateStyleSheetInfo() { if ( !testAttribute(Qt::WA_StyledBackground ) ) return; QwtStyleSheetRecorder recorder( size() ); QPainter painter( &recorder ); QStyleOption opt; opt.initFrom(this); style()->drawPrimitive( QStyle::PE_Widget, &opt, &painter, this); painter.end(); d_data->styleSheet.hasBorder = !recorder.border.rectList.isEmpty(); d_data->styleSheet.cornerRects = recorder.clipRects; if ( recorder.background.path.isEmpty() ) { if ( !recorder.border.rectList.isEmpty() ) { d_data->styleSheet.borderPath = qwtCombinePathList( rect(), recorder.border.pathList ); } } else { d_data->styleSheet.borderPath = recorder.background.path; d_data->styleSheet.background.brush = recorder.background.brush; d_data->styleSheet.background.origin = recorder.background.origin; } } /*! Calculate the painter path for a styled or rounded border When the canvas has no styled background or rounded borders the painter path is empty. \param rect Bounding rectangle of the canvas \return Painter path, that can be used for clipping */ QPainterPath QwtPlotCanvas::borderPath( const QRect &rect ) const { if ( testAttribute(Qt::WA_StyledBackground ) ) { QwtStyleSheetRecorder recorder( rect.size() ); QPainter painter( &recorder ); QStyleOption opt; opt.initFrom(this); opt.rect = rect; style()->drawPrimitive( QStyle::PE_Widget, &opt, &painter, this); painter.end(); if ( !recorder.background.path.isEmpty() ) return recorder.background.path; if ( !recorder.border.rectList.isEmpty() ) return qwtCombinePathList( rect, recorder.border.pathList ); } else if ( d_data->borderRadius > 0.0 ) { double fw2 = frameWidth() * 0.5; QRectF r = QRectF(rect).adjusted( fw2, fw2, -fw2, -fw2 ); QPainterPath path; path.addRoundedRect( r, d_data->borderRadius, d_data->borderRadius ); return path; } return QPainterPath(); } connectome-workbench-1.2.3+git41-gc4c6c90/src/Qwt/qwt_plot_canvas.h000066400000000000000000000115301300200146000246740ustar00rootroot00000000000000/* -*- mode: C++ ; c-file-style: "stroustrup" -*- ***************************** * Qwt Widget Library * Copyright (C) 1997 Josef Wilgen * Copyright (C) 2002 Uwe Rathmann * * This library is free software; you can redistribute it and/or * modify it under the terms of the Qwt License, Version 1.0 *****************************************************************************/ #ifndef QWT_PLOT_CANVAS_H #define QWT_PLOT_CANVAS_H #include "qwt_global.h" #include #include class QwtPlot; class QPixmap; /*! \brief Canvas of a QwtPlot. Canvas is the widget where all plot items are displayed \sa QwtPlot::setCanvas(), QwtPlotGLCanvas */ class QWT_EXPORT QwtPlotCanvas : public QFrame { Q_OBJECT Q_PROPERTY( double borderRadius READ borderRadius WRITE setBorderRadius ) public: /*! \brief Paint attributes The default setting enables BackingStore and Opaque. \sa setPaintAttribute(), testPaintAttribute() */ enum PaintAttribute { /*! \brief Paint double buffered reusing the content of the pixmap buffer when possible. Using a backing store might improve the performance significantly, when working with widget overlays ( like rubber bands ). Disabling the cache might improve the performance for incremental paints (using QwtPlotDirectPainter ). \sa backingStore(), invalidateBackingStore() */ BackingStore = 1, /*! \brief Try to fill the complete contents rectangle of the plot canvas When using styled backgrounds Qt assumes, that the canvas doesn't fill its area completely ( f.e because of rounded borders ) and fills the area below the canvas. When this is done with gradients it might result in a serious performance bottleneck - depending on the size. When the Opaque attribute is enabled the canvas tries to identify the gaps with some heuristics and to fill those only. \warning Will not work for semitransparent backgrounds */ Opaque = 2, /*! \brief Try to improve painting of styled backgrounds QwtPlotCanvas supports the box model attributes for customizing the layout with style sheets. Unfortunately the design of Qt style sheets has no concept how to handle backgrounds with rounded corners - beside of padding. When HackStyledBackground is enabled the plot canvas tries to separate the background from the background border by reverse engineering to paint the background before and the border after the plot items. In this order the border gets perfectly antialiased and you can avoid some pixel artifacts in the corners. */ HackStyledBackground = 4, /*! When ImmediatePaint is set replot() calls repaint() instead of update(). \sa replot(), QWidget::repaint(), QWidget::update() */ ImmediatePaint = 8 }; //! Paint attributes typedef QFlags PaintAttributes; /*! \brief Focus indicator The default setting is NoFocusIndicator \sa setFocusIndicator(), focusIndicator(), paintFocus() */ enum FocusIndicator { //! Don't paint a focus indicator NoFocusIndicator, /*! The focus is related to the complete canvas. Paint the focus indicator using paintFocus() */ CanvasFocusIndicator, /*! The focus is related to an item (curve, point, ...) on the canvas. It is up to the application to display a focus indication using f.e. highlighting. */ ItemFocusIndicator }; explicit QwtPlotCanvas( QwtPlot * = NULL ); virtual ~QwtPlotCanvas(); QwtPlot *plot(); const QwtPlot *plot() const; void setFocusIndicator( FocusIndicator ); FocusIndicator focusIndicator() const; void setBorderRadius( double ); double borderRadius() const; void setPaintAttribute( PaintAttribute, bool on = true ); bool testPaintAttribute( PaintAttribute ) const; const QPixmap *backingStore() const; void invalidateBackingStore(); virtual bool event( QEvent * ); Q_INVOKABLE QPainterPath borderPath( const QRect & ) const; public Q_SLOTS: void replot(); protected: virtual void paintEvent( QPaintEvent * ); virtual void resizeEvent( QResizeEvent * ); virtual void drawFocusIndicator( QPainter * ); virtual void drawBorder( QPainter * ); void updateStyleSheetInfo(); private: void drawCanvas( QPainter *, bool withBackground ); class PrivateData; PrivateData *d_data; }; Q_DECLARE_OPERATORS_FOR_FLAGS( QwtPlotCanvas::PaintAttributes ) #endif connectome-workbench-1.2.3+git41-gc4c6c90/src/Qwt/qwt_plot_curve.cpp000066400000000000000000000745601300200146000251140ustar00rootroot00000000000000/* -*- mode: C++ ; c-file-style: "stroustrup" -*- ***************************** * Qwt Widget Library * Copyright (C) 1997 Josef Wilgen * Copyright (C) 2002 Uwe Rathmann * * This library is free software; you can redistribute it and/or * modify it under the terms of the Qwt License, Version 1.0 *****************************************************************************/ #include "qwt_plot_curve.h" #include "qwt_point_data.h" #include "qwt_math.h" #include "qwt_clipper.h" #include "qwt_painter.h" #include "qwt_scale_map.h" #include "qwt_plot.h" #include "qwt_curve_fitter.h" #include "qwt_symbol.h" #include "qwt_point_mapper.h" #include #include #include #include static void qwtUpdateLegendIconSize( QwtPlotCurve *curve ) { if ( curve->symbol() && curve->testLegendAttribute( QwtPlotCurve::LegendShowSymbol ) ) { QSize sz = curve->symbol()->boundingRect().size(); sz += QSize( 2, 2 ); // margin if ( curve->testLegendAttribute( QwtPlotCurve::LegendShowLine ) ) { // Avoid, that the line is completely covered by the symbol int w = qCeil( 1.5 * sz.width() ); if ( w % 2 ) w++; sz.setWidth( qMax( 8, w ) ); } curve->setLegendIconSize( sz ); } } static int qwtVerifyRange( int size, int &i1, int &i2 ) { if ( size < 1 ) return 0; i1 = qBound( 0, i1, size - 1 ); i2 = qBound( 0, i2, size - 1 ); if ( i1 > i2 ) qSwap( i1, i2 ); return ( i2 - i1 + 1 ); } class QwtPlotCurve::PrivateData { public: PrivateData(): style( QwtPlotCurve::Lines ), baseline( 0.0 ), symbol( NULL ), attributes( 0 ), paintAttributes( QwtPlotCurve::ClipPolygons | QwtPlotCurve::FilterPoints ), legendAttributes( 0 ) { pen = QPen( Qt::black ); curveFitter = new QwtSplineCurveFitter; } ~PrivateData() { delete symbol; delete curveFitter; } QwtPlotCurve::CurveStyle style; double baseline; const QwtSymbol *symbol; QwtCurveFitter *curveFitter; QPen pen; QBrush brush; QwtPlotCurve::CurveAttributes attributes; QwtPlotCurve::PaintAttributes paintAttributes; QwtPlotCurve::LegendAttributes legendAttributes; }; /*! Constructor \param title Title of the curve */ QwtPlotCurve::QwtPlotCurve( const QwtText &title ): QwtPlotSeriesItem( title ) { init(); } /*! Constructor \param title Title of the curve */ QwtPlotCurve::QwtPlotCurve( const QString &title ): QwtPlotSeriesItem( QwtText( title ) ) { init(); } //! Destructor QwtPlotCurve::~QwtPlotCurve() { delete d_data; } //! Initialize internal members void QwtPlotCurve::init() { setItemAttribute( QwtPlotItem::Legend ); setItemAttribute( QwtPlotItem::AutoScale ); d_data = new PrivateData; setData( new QwtPointSeriesData() ); setZ( 20.0 ); } //! \return QwtPlotItem::Rtti_PlotCurve int QwtPlotCurve::rtti() const { return QwtPlotItem::Rtti_PlotCurve; } /*! Specify an attribute how to draw the curve \param attribute Paint attribute \param on On/Off \sa testPaintAttribute() */ void QwtPlotCurve::setPaintAttribute( PaintAttribute attribute, bool on ) { if ( on ) d_data->paintAttributes |= attribute; else d_data->paintAttributes &= ~attribute; } /*! \return True, when attribute is enabled \sa setPaintAttribute() */ bool QwtPlotCurve::testPaintAttribute( PaintAttribute attribute ) const { return ( d_data->paintAttributes & attribute ); } /*! Specify an attribute how to draw the legend icon \param attribute Attribute \param on On/Off /sa testLegendAttribute(). legendIcon() */ void QwtPlotCurve::setLegendAttribute( LegendAttribute attribute, bool on ) { if ( on != testLegendAttribute( attribute ) ) { if ( on ) d_data->legendAttributes |= attribute; else d_data->legendAttributes &= ~attribute; qwtUpdateLegendIconSize( this ); legendChanged(); } } /*! \return True, when attribute is enabled \sa setLegendAttribute() */ bool QwtPlotCurve::testLegendAttribute( LegendAttribute attribute ) const { return ( d_data->legendAttributes & attribute ); } /*! Set the curve's drawing style \param style Curve style \sa style() */ void QwtPlotCurve::setStyle( CurveStyle style ) { if ( style != d_data->style ) { d_data->style = style; legendChanged(); itemChanged(); } } /*! \return Style of the curve \sa setStyle() */ QwtPlotCurve::CurveStyle QwtPlotCurve::style() const { return d_data->style; } /*! \brief Assign a symbol The curve will take the ownership of the symbol, hence the previously set symbol will be delete by setting a new one. If \p symbol is \c NULL no symbol will be drawn. \param symbol Symbol \sa symbol() */ void QwtPlotCurve::setSymbol( QwtSymbol *symbol ) { if ( symbol != d_data->symbol ) { delete d_data->symbol; d_data->symbol = symbol; qwtUpdateLegendIconSize( this ); legendChanged(); itemChanged(); } } /*! \return Current symbol or NULL, when no symbol has been assigned \sa setSymbol() */ const QwtSymbol *QwtPlotCurve::symbol() const { return d_data->symbol; } /*! Build and assign a pen In Qt5 the default pen width is 1.0 ( 0.0 in Qt4 ) what makes it non cosmetic ( see QPen::isCosmetic() ). This method has been introduced to hide this incompatibility. \param color Pen color \param width Pen width \param style Pen style \sa pen(), brush() */ void QwtPlotCurve::setPen( const QColor &color, qreal width, Qt::PenStyle style ) { setPen( QPen( color, width, style ) ); } /*! Assign a pen \param pen New pen \sa pen(), brush() */ void QwtPlotCurve::setPen( const QPen &pen ) { if ( pen != d_data->pen ) { d_data->pen = pen; legendChanged(); itemChanged(); } } /*! \return Pen used to draw the lines \sa setPen(), brush() */ const QPen& QwtPlotCurve::pen() const { return d_data->pen; } /*! \brief Assign a brush. In case of brush.style() != QBrush::NoBrush and style() != QwtPlotCurve::Sticks the area between the curve and the baseline will be filled. In case !brush.color().isValid() the area will be filled by pen.color(). The fill algorithm simply connects the first and the last curve point to the baseline. So the curve data has to be sorted (ascending or descending). \param brush New brush \sa brush(), setBaseline(), baseline() */ void QwtPlotCurve::setBrush( const QBrush &brush ) { if ( brush != d_data->brush ) { d_data->brush = brush; legendChanged(); itemChanged(); } } /*! \return Brush used to fill the area between lines and the baseline \sa setBrush(), setBaseline(), baseline() */ const QBrush& QwtPlotCurve::brush() const { return d_data->brush; } /*! Draw an interval of the curve \param painter Painter \param xMap Maps x-values into pixel coordinates. \param yMap Maps y-values into pixel coordinates. \param canvasRect Contents rectangle of the canvas \param from Index of the first point to be painted \param to Index of the last point to be painted. If to < 0 the curve will be painted to its last point. \sa drawCurve(), drawSymbols(), */ void QwtPlotCurve::drawSeries( QPainter *painter, const QwtScaleMap &xMap, const QwtScaleMap &yMap, const QRectF &canvasRect, int from, int to ) const { const size_t numSamples = dataSize(); if ( !painter || numSamples <= 0 ) return; if ( to < 0 ) to = numSamples - 1; if ( qwtVerifyRange( numSamples, from, to ) > 0 ) { painter->save(); painter->setPen( d_data->pen ); /* Qt 4.0.0 is slow when drawing lines, but it's even slower when the painter has a brush. So we don't set the brush before we really need it. */ drawCurve( painter, d_data->style, xMap, yMap, canvasRect, from, to ); painter->restore(); if ( d_data->symbol && ( d_data->symbol->style() != QwtSymbol::NoSymbol ) ) { painter->save(); drawSymbols( painter, *d_data->symbol, xMap, yMap, canvasRect, from, to ); painter->restore(); } } } /*! \brief Draw the line part (without symbols) of a curve interval. \param painter Painter \param style curve style, see QwtPlotCurve::CurveStyle \param xMap x map \param yMap y map \param canvasRect Contents rectangle of the canvas \param from index of the first point to be painted \param to index of the last point to be painted \sa draw(), drawDots(), drawLines(), drawSteps(), drawSticks() */ void QwtPlotCurve::drawCurve( QPainter *painter, int style, const QwtScaleMap &xMap, const QwtScaleMap &yMap, const QRectF &canvasRect, int from, int to ) const { switch ( style ) { case Lines: if ( testCurveAttribute( Fitted ) ) { // we always need the complete // curve for fitting from = 0; to = dataSize() - 1; } drawLines( painter, xMap, yMap, canvasRect, from, to ); break; case Sticks: drawSticks( painter, xMap, yMap, canvasRect, from, to ); break; case Steps: drawSteps( painter, xMap, yMap, canvasRect, from, to ); break; case Dots: drawDots( painter, xMap, yMap, canvasRect, from, to ); break; case NoCurve: default: break; } } /*! \brief Draw lines If the CurveAttribute Fitted is enabled a QwtCurveFitter tries to interpolate/smooth the curve, before it is painted. \param painter Painter \param xMap x map \param yMap y map \param canvasRect Contents rectangle of the canvas \param from index of the first point to be painted \param to index of the last point to be painted \sa setCurveAttribute(), setCurveFitter(), draw(), drawLines(), drawDots(), drawSteps(), drawSticks() */ void QwtPlotCurve::drawLines( QPainter *painter, const QwtScaleMap &xMap, const QwtScaleMap &yMap, const QRectF &canvasRect, int from, int to ) const { if ( from > to ) return; const bool doAlign = QwtPainter::roundingAlignment( painter ); const bool doFit = ( d_data->attributes & Fitted ) && d_data->curveFitter; const bool doFill = ( d_data->brush.style() != Qt::NoBrush ) && ( d_data->brush.color().alpha() > 0 ); QRectF clipRect; if ( d_data->paintAttributes & ClipPolygons ) { qreal pw = qMax( qreal( 1.0 ), painter->pen().widthF()); clipRect = canvasRect.adjusted(-pw, -pw, pw, pw); } bool doIntegers = false; #if QT_VERSION < 0x040800 // For Qt <= 4.7 the raster paint engine is significantly faster // for rendering QPolygon than for QPolygonF. So let's // see if we can use it. if ( painter->paintEngine()->type() == QPaintEngine::Raster ) { // In case of filling or fitting performance doesn't count // because both operations are much more expensive // then drawing the polyline itself if ( !doFit && !doFill ) doIntegers = true; } #endif const bool noDuplicates = d_data->paintAttributes & FilterPoints; QwtPointMapper mapper; mapper.setFlag( QwtPointMapper::RoundPoints, doAlign ); mapper.setFlag( QwtPointMapper::WeedOutPoints, noDuplicates ); mapper.setBoundingRect( canvasRect ); if ( doIntegers ) { QPolygon polyline = mapper.toPolygon( xMap, yMap, data(), from, to ); if ( d_data->paintAttributes & ClipPolygons ) { polyline = QwtClipper::clipPolygon( clipRect.toAlignedRect(), polyline, false ); } QwtPainter::drawPolyline( painter, polyline ); } else { QPolygonF polyline = mapper.toPolygonF( xMap, yMap, data(), from, to ); if ( doFit ) polyline = d_data->curveFitter->fitCurve( polyline ); if ( doFill ) { if ( painter->pen().style() != Qt::NoPen ) { // here we are wasting memory for the filled copy, // do polygon clipping twice etc .. TODO QPolygonF filled = polyline; fillCurve( painter, xMap, yMap, canvasRect, filled ); filled.clear(); if ( d_data->paintAttributes & ClipPolygons ) { polyline = QwtClipper::clipPolygonF( clipRect, polyline, false ); } QwtPainter::drawPolyline( painter, polyline ); } else { fillCurve( painter, xMap, yMap, canvasRect, polyline ); } } else { if ( d_data->paintAttributes & ClipPolygons ) { polyline = QwtClipper::clipPolygonF( clipRect, polyline, false ); } QwtPainter::drawPolyline( painter, polyline ); } } } /*! Draw sticks \param painter Painter \param xMap x map \param yMap y map \param canvasRect Contents rectangle of the canvas \param from index of the first point to be painted \param to index of the last point to be painted \sa draw(), drawCurve(), drawDots(), drawLines(), drawSteps() */ void QwtPlotCurve::drawSticks( QPainter *painter, const QwtScaleMap &xMap, const QwtScaleMap &yMap, const QRectF &, int from, int to ) const { painter->save(); painter->setRenderHint( QPainter::Antialiasing, false ); const bool doAlign = QwtPainter::roundingAlignment( painter ); double x0 = xMap.transform( d_data->baseline ); double y0 = yMap.transform( d_data->baseline ); if ( doAlign ) { x0 = qRound( x0 ); y0 = qRound( y0 ); } const Qt::Orientation o = orientation(); const QwtSeriesData *series = data(); for ( int i = from; i <= to; i++ ) { const QPointF sample = series->sample( i ); double xi = xMap.transform( sample.x() ); double yi = yMap.transform( sample.y() ); if ( doAlign ) { xi = qRound( xi ); yi = qRound( yi ); } if ( o == Qt::Horizontal ) QwtPainter::drawLine( painter, x0, yi, xi, yi ); else QwtPainter::drawLine( painter, xi, y0, xi, yi ); } painter->restore(); } /*! Draw dots \param painter Painter \param xMap x map \param yMap y map \param canvasRect Contents rectangle of the canvas \param from index of the first point to be painted \param to index of the last point to be painted \sa draw(), drawCurve(), drawSticks(), drawLines(), drawSteps() */ void QwtPlotCurve::drawDots( QPainter *painter, const QwtScaleMap &xMap, const QwtScaleMap &yMap, const QRectF &canvasRect, int from, int to ) const { const QColor color = painter->pen().color(); if ( painter->pen().style() == Qt::NoPen || color.alpha() == 0 ) { return; } const bool doFill = ( d_data->brush.style() != Qt::NoBrush ) && ( d_data->brush.color().alpha() > 0 ); const bool doAlign = QwtPainter::roundingAlignment( painter ); QwtPointMapper mapper; mapper.setBoundingRect( canvasRect ); mapper.setFlag( QwtPointMapper::RoundPoints, doAlign ); if ( d_data->paintAttributes & FilterPoints ) { if ( ( color.alpha() == 255 ) && !( painter->renderHints() & QPainter::Antialiasing ) ) { mapper.setFlag( QwtPointMapper::WeedOutPoints, true ); } } if ( doFill ) { mapper.setFlag( QwtPointMapper::WeedOutPoints, false ); QPolygonF points = mapper.toPointsF( xMap, yMap, data(), from, to ); QwtPainter::drawPoints( painter, points ); fillCurve( painter, xMap, yMap, canvasRect, points ); } else if ( d_data->paintAttributes & ImageBuffer ) { const QImage image = mapper.toImage( xMap, yMap, data(), from, to, d_data->pen, painter->testRenderHint( QPainter::Antialiasing ), renderThreadCount() ); painter->drawImage( canvasRect.toAlignedRect(), image ); } else if ( d_data->paintAttributes & MinimizeMemory ) { const QwtSeriesData *series = data(); for ( int i = from; i <= to; i++ ) { const QPointF sample = series->sample( i ); double xi = xMap.transform( sample.x() ); double yi = yMap.transform( sample.y() ); if ( doAlign ) { xi = qRound( xi ); yi = qRound( yi ); } QwtPainter::drawPoint( painter, QPointF( xi, yi ) ); } } else { if ( doAlign ) { const QPolygon points = mapper.toPoints( xMap, yMap, data(), from, to ); QwtPainter::drawPoints( painter, points ); } else { const QPolygonF points = mapper.toPointsF( xMap, yMap, data(), from, to ); QwtPainter::drawPoints( painter, points ); } } } /*! Draw step function The direction of the steps depends on Inverted attribute. \param painter Painter \param xMap x map \param yMap y map \param canvasRect Contents rectangle of the canvas \param from index of the first point to be painted \param to index of the last point to be painted \sa CurveAttribute, setCurveAttribute(), draw(), drawCurve(), drawDots(), drawLines(), drawSticks() */ void QwtPlotCurve::drawSteps( QPainter *painter, const QwtScaleMap &xMap, const QwtScaleMap &yMap, const QRectF &canvasRect, int from, int to ) const { const bool doAlign = QwtPainter::roundingAlignment( painter ); QPolygonF polygon( 2 * ( to - from ) + 1 ); QPointF *points = polygon.data(); bool inverted = orientation() == Qt::Vertical; if ( d_data->attributes & Inverted ) inverted = !inverted; const QwtSeriesData *series = data(); int i, ip; for ( i = from, ip = 0; i <= to; i++, ip += 2 ) { const QPointF sample = series->sample( i ); double xi = xMap.transform( sample.x() ); double yi = yMap.transform( sample.y() ); if ( doAlign ) { xi = qRound( xi ); yi = qRound( yi ); } if ( ip > 0 ) { const QPointF &p0 = points[ip - 2]; QPointF &p = points[ip - 1]; if ( inverted ) { p.rx() = p0.x(); p.ry() = yi; } else { p.rx() = xi; p.ry() = p0.y(); } } points[ip].rx() = xi; points[ip].ry() = yi; } if ( d_data->paintAttributes & ClipPolygons ) { const QPolygonF clipped = QwtClipper::clipPolygonF( canvasRect, polygon, false ); QwtPainter::drawPolyline( painter, clipped ); } else { QwtPainter::drawPolyline( painter, polygon ); } if ( d_data->brush.style() != Qt::NoBrush ) fillCurve( painter, xMap, yMap, canvasRect, polygon ); } /*! Specify an attribute for drawing the curve \param attribute Curve attribute \param on On/Off /sa testCurveAttribute(), setCurveFitter() */ void QwtPlotCurve::setCurveAttribute( CurveAttribute attribute, bool on ) { if ( bool( d_data->attributes & attribute ) == on ) return; if ( on ) d_data->attributes |= attribute; else d_data->attributes &= ~attribute; itemChanged(); } /*! \return true, if attribute is enabled \sa setCurveAttribute() */ bool QwtPlotCurve::testCurveAttribute( CurveAttribute attribute ) const { return d_data->attributes & attribute; } /*! Assign a curve fitter The curve fitter "smooths" the curve points, when the Fitted CurveAttribute is set. setCurveFitter(NULL) also disables curve fitting. The curve fitter operates on the translated points ( = widget coordinates) to be functional for logarithmic scales. Obviously this is less performant for fitting algorithms, that reduce the number of points. For situations, where curve fitting is used to improve the performance of painting huge series of points it might be better to execute the fitter on the curve points once and to cache the result in the QwtSeriesData object. \param curveFitter() Curve fitter \sa Fitted */ void QwtPlotCurve::setCurveFitter( QwtCurveFitter *curveFitter ) { delete d_data->curveFitter; d_data->curveFitter = curveFitter; itemChanged(); } /*! Get the curve fitter. If curve fitting is disabled NULL is returned. \return Curve fitter \sa setCurveFitter(), Fitted */ QwtCurveFitter *QwtPlotCurve::curveFitter() const { return d_data->curveFitter; } /*! Fill the area between the curve and the baseline with the curve brush \param painter Painter \param xMap x map \param yMap y map \param canvasRect Contents rectangle of the canvas \param polygon Polygon - will be modified ! \sa setBrush(), setBaseline(), setStyle() */ void QwtPlotCurve::fillCurve( QPainter *painter, const QwtScaleMap &xMap, const QwtScaleMap &yMap, const QRectF &canvasRect, QPolygonF &polygon ) const { if ( d_data->brush.style() == Qt::NoBrush ) return; closePolyline( painter, xMap, yMap, polygon ); if ( polygon.count() <= 2 ) // a line can't be filled return; QBrush brush = d_data->brush; if ( !brush.color().isValid() ) brush.setColor( d_data->pen.color() ); if ( d_data->paintAttributes & ClipPolygons ) polygon = QwtClipper::clipPolygonF( canvasRect, polygon, true ); painter->save(); painter->setPen( Qt::NoPen ); painter->setBrush( brush ); QwtPainter::drawPolygon( painter, polygon ); painter->restore(); } /*! \brief Complete a polygon to be a closed polygon including the area between the original polygon and the baseline. \param painter Painter \param xMap X map \param yMap Y map \param polygon Polygon to be completed */ void QwtPlotCurve::closePolyline( QPainter *painter, const QwtScaleMap &xMap, const QwtScaleMap &yMap, QPolygonF &polygon ) const { if ( polygon.size() < 2 ) return; const bool doAlign = QwtPainter::roundingAlignment( painter ); double baseline = d_data->baseline; if ( orientation() == Qt::Vertical ) { if ( yMap.transformation() ) baseline = yMap.transformation()->bounded( baseline ); double refY = yMap.transform( baseline ); if ( doAlign ) refY = qRound( refY ); polygon += QPointF( polygon.last().x(), refY ); polygon += QPointF( polygon.first().x(), refY ); } else { if ( xMap.transformation() ) baseline = xMap.transformation()->bounded( baseline ); double refX = xMap.transform( baseline ); if ( doAlign ) refX = qRound( refX ); polygon += QPointF( refX, polygon.last().y() ); polygon += QPointF( refX, polygon.first().y() ); } } /*! Draw symbols \param painter Painter \param symbol Curve symbol \param xMap x map \param yMap y map \param canvasRect Contents rectangle of the canvas \param from Index of the first point to be painted \param to Index of the last point to be painted \sa setSymbol(), drawSeries(), drawCurve() */ void QwtPlotCurve::drawSymbols( QPainter *painter, const QwtSymbol &symbol, const QwtScaleMap &xMap, const QwtScaleMap &yMap, const QRectF &canvasRect, int from, int to ) const { QwtPointMapper mapper; mapper.setFlag( QwtPointMapper::RoundPoints, QwtPainter::roundingAlignment( painter ) ); mapper.setFlag( QwtPointMapper::WeedOutPoints, testPaintAttribute( QwtPlotCurve::FilterPoints ) ); mapper.setBoundingRect( canvasRect ); const int chunkSize = 500; for ( int i = from; i <= to; i += chunkSize ) { const int n = qMin( chunkSize, to - i + 1 ); const QPolygonF points = mapper.toPointsF( xMap, yMap, data(), i, i + n - 1 ); if ( points.size() > 0 ) symbol.drawSymbols( painter, points ); } } /*! \brief Set the value of the baseline The baseline is needed for filling the curve with a brush or the Sticks drawing style. The interpretation of the baseline depends on the orientation(). With Qt::Horizontal, the baseline is interpreted as a horizontal line at y = baseline(), with Qt::Vertical, it is interpreted as a vertical line at x = baseline(). The default value is 0.0. \param value Value of the baseline \sa baseline(), setBrush(), setStyle(), QwtPlotAbstractSeriesItem::orientation() */ void QwtPlotCurve::setBaseline( double value ) { if ( d_data->baseline != value ) { d_data->baseline = value; itemChanged(); } } /*! \return Value of the baseline \sa setBaseline() */ double QwtPlotCurve::baseline() const { return d_data->baseline; } /*! Find the closest curve point for a specific position \param pos Position, where to look for the closest curve point \param dist If dist != NULL, closestPoint() returns the distance between the position and the closest curve point \return Index of the closest curve point, or -1 if none can be found ( f.e when the curve has no points ) \note closestPoint() implements a dumb algorithm, that iterates over all points */ int QwtPlotCurve::closestPoint( const QPoint &pos, double *dist ) const { const size_t numSamples = dataSize(); if ( plot() == NULL || numSamples <= 0 ) return -1; const QwtSeriesData *series = data(); const QwtScaleMap xMap = plot()->canvasMap( xAxis() ); const QwtScaleMap yMap = plot()->canvasMap( yAxis() ); int index = -1; double dmin = 1.0e10; for ( uint i = 0; i < numSamples; i++ ) { const QPointF sample = series->sample( i ); const double cx = xMap.transform( sample.x() ) - pos.x(); const double cy = yMap.transform( sample.y() ) - pos.y(); const double f = qwtSqr( cx ) + qwtSqr( cy ); if ( f < dmin ) { index = i; dmin = f; } } if ( dist ) *dist = qSqrt( dmin ); return index; } /*! \return Icon representing the curve on the legend \param index Index of the legend entry ( ignored as there is only one ) \param size Icon size \sa QwtPlotItem::setLegendIconSize(), QwtPlotItem::legendData() */ QwtGraphic QwtPlotCurve::legendIcon( int index, const QSizeF &size ) const { Q_UNUSED( index ); if ( size.isEmpty() ) return QwtGraphic(); QwtGraphic graphic; graphic.setDefaultSize( size ); graphic.setRenderHint( QwtGraphic::RenderPensUnscaled, true ); QPainter painter( &graphic ); painter.setRenderHint( QPainter::Antialiasing, testRenderHint( QwtPlotItem::RenderAntialiased ) ); if ( d_data->legendAttributes == 0 || d_data->legendAttributes & QwtPlotCurve::LegendShowBrush ) { QBrush brush = d_data->brush; if ( brush.style() == Qt::NoBrush && d_data->legendAttributes == 0 ) { if ( style() != QwtPlotCurve::NoCurve ) { brush = QBrush( pen().color() ); } else if ( d_data->symbol && ( d_data->symbol->style() != QwtSymbol::NoSymbol ) ) { brush = QBrush( d_data->symbol->pen().color() ); } } if ( brush.style() != Qt::NoBrush ) { QRectF r( 0, 0, size.width(), size.height() ); painter.fillRect( r, brush ); } } if ( d_data->legendAttributes & QwtPlotCurve::LegendShowLine ) { if ( pen() != Qt::NoPen ) { QPen pn = pen(); pn.setCapStyle( Qt::FlatCap ); painter.setPen( pn ); const double y = 0.5 * size.height(); QwtPainter::drawLine( &painter, 0.0, y, size.width(), y ); } } if ( d_data->legendAttributes & QwtPlotCurve::LegendShowSymbol ) { if ( d_data->symbol ) { QRectF r( 0, 0, size.width(), size.height() ); d_data->symbol->drawSymbol( &painter, r ); } } return graphic; } /*! Initialize data with an array of points. \param samples Vector of points \note QVector is implicitly shared \note QPolygonF is derived from QVector */ void QwtPlotCurve::setSamples( const QVector &samples ) { setData( new QwtPointSeriesData( samples ) ); } /*! Assign a series of points setSamples() is just a wrapper for setData() without any additional value - beside that it is easier to find for the developer. \param data Data \warning The item takes ownership of the data object, deleting it when its not used anymore. */ void QwtPlotCurve::setSamples( QwtSeriesData *data ) { setData( data ); } #ifndef QWT_NO_COMPAT /*! \brief Initialize the data by pointing to memory blocks which are not managed by QwtPlotCurve. setRawSamples is provided for efficiency. It is important to keep the pointers during the lifetime of the underlying QwtCPointerData class. \param xData pointer to x data \param yData pointer to y data \param size size of x and y \sa QwtCPointerData */ void QwtPlotCurve::setRawSamples( const double *xData, const double *yData, int size ) { setData( new QwtCPointerData( xData, yData, size ) ); } /*! Set data by copying x- and y-values from specified memory blocks. Contrary to setRawSamples(), this function makes a 'deep copy' of the data. \param xData pointer to x values \param yData pointer to y values \param size size of xData and yData \sa QwtPointArrayData */ void QwtPlotCurve::setSamples( const double *xData, const double *yData, int size ) { setData( new QwtPointArrayData( xData, yData, size ) ); } /*! \brief Initialize data with x- and y-arrays (explicitly shared) \param xData x data \param yData y data \sa QwtPointArrayData */ void QwtPlotCurve::setSamples( const QVector &xData, const QVector &yData ) { setData( new QwtPointArrayData( xData, yData ) ); } #endif // !QWT_NO_COMPAT connectome-workbench-1.2.3+git41-gc4c6c90/src/Qwt/qwt_plot_curve.h000066400000000000000000000241151300200146000245500ustar00rootroot00000000000000/* -*- mode: C++ ; c-file-style: "stroustrup" -*- ***************************** * Qwt Widget Library * Copyright (C) 1997 Josef Wilgen * Copyright (C) 2002 Uwe Rathmann * * This library is free software; you can redistribute it and/or * modify it under the terms of the Qwt License, Version 1.0 *****************************************************************************/ #ifndef QWT_PLOT_CURVE_H #define QWT_PLOT_CURVE_H #include "qwt_global.h" #include "qwt_plot_seriesitem.h" #include "qwt_series_data.h" #include "qwt_text.h" #include #include class QPainter; class QPolygonF; class QwtScaleMap; class QwtSymbol; class QwtCurveFitter; /*! \brief A plot item, that represents a series of points A curve is the representation of a series of points in the x-y plane. It supports different display styles, interpolation ( f.e. spline ) and symbols. \par Usage

a) Assign curve properties
When a curve is created, it is configured to draw black solid lines with in QwtPlotCurve::Lines style and no symbols. You can change this by calling setPen(), setStyle() and setSymbol().
b) Connect/Assign data.
QwtPlotCurve gets its points using a QwtSeriesData object offering a bridge to the real storage of the points ( like QAbstractItemModel ). There are several convenience classes derived from QwtSeriesData, that also store the points inside ( like QStandardItemModel ). QwtPlotCurve also offers a couple of variations of setSamples(), that build QwtSeriesData objects from arrays internally.
c) Attach the curve to a plot
See QwtPlotItem::attach()
\par Example: see examples/bode \sa QwtPointSeriesData, QwtSymbol, QwtScaleMap */ class QWT_EXPORT QwtPlotCurve: public QwtPlotSeriesItem, public QwtSeriesStore { public: /*! Curve styles. \sa setStyle(), style() */ enum CurveStyle { /*! Don't draw a curve. Note: This doesn't affect the symbols. */ NoCurve = -1, /*! Connect the points with straight lines. The lines might be interpolated depending on the 'Fitted' attribute. Curve fitting can be configured using setCurveFitter(). */ Lines, /*! Draw vertical or horizontal sticks ( depending on the orientation() ) from a baseline which is defined by setBaseline(). */ Sticks, /*! Connect the points with a step function. The step function is drawn from the left to the right or vice versa, depending on the QwtPlotCurve::Inverted attribute. */ Steps, /*! Draw dots at the locations of the data points. Note: This is different from a dotted line (see setPen()), and faster as a curve in QwtPlotCurve::NoStyle style and a symbol painting a point. */ Dots, /*! Styles >= QwtPlotCurve::UserCurve are reserved for derived classes of QwtPlotCurve that overload drawCurve() with additional application specific curve types. */ UserCurve = 100 }; /*! Attribute for drawing the curve \sa setCurveAttribute(), testCurveAttribute(), curveFitter() */ enum CurveAttribute { /*! For QwtPlotCurve::Steps only. Draws a step function from the right to the left. */ Inverted = 0x01, /*! Only in combination with QwtPlotCurve::Lines A QwtCurveFitter tries to interpolate/smooth the curve, before it is painted. \note Curve fitting requires temporary memory for calculating coefficients and additional points. If painting in QwtPlotCurve::Fitted mode is slow it might be better to fit the points, before they are passed to QwtPlotCurve. */ Fitted = 0x02 }; //! Curve attributes typedef QFlags CurveAttributes; /*! Attributes how to represent the curve on the legend \sa setLegendAttribute(), testLegendAttribute(), QwtPlotItem::legendData(), legendIcon() */ enum LegendAttribute { /*! QwtPlotCurve tries to find a color representing the curve and paints a rectangle with it. */ LegendNoAttribute = 0x00, /*! If the style() is not QwtPlotCurve::NoCurve a line is painted with the curve pen(). */ LegendShowLine = 0x01, /*! If the curve has a valid symbol it is painted. */ LegendShowSymbol = 0x02, /*! If the curve has a brush a rectangle filled with the curve brush() is painted. */ LegendShowBrush = 0x04 }; //! Legend attributes typedef QFlags LegendAttributes; /*! Attributes to modify the drawing algorithm. The default setting enables ClipPolygons | FilterPoints \sa setPaintAttribute(), testPaintAttribute() */ enum PaintAttribute { /*! Clip polygons before painting them. In situations, where points are far outside the visible area (f.e when zooming deep) this might be a substantial improvement for the painting performance */ ClipPolygons = 0x01, /*! Tries to reduce the data that has to be painted, by sorting out duplicates, or paintings outside the visible area. Might have a notable impact on curves with many close points. Only a couple of very basic filtering algorithms are implemented. */ FilterPoints = 0x02, /*! Minimize memory usage that is temporarily needed for the translated points, before they get painted. This might slow down the performance of painting */ MinimizeMemory = 0x04, /*! Render the points to a temporary image and paint the image. This is a very special optimization for Dots style, when having a huge amount of points. With a reasonable number of points QPainter::drawPoints() will be faster. */ ImageBuffer = 0x08 }; //! Paint attributes typedef QFlags PaintAttributes; explicit QwtPlotCurve( const QString &title = QString::null ); explicit QwtPlotCurve( const QwtText &title ); virtual ~QwtPlotCurve(); virtual int rtti() const; void setPaintAttribute( PaintAttribute, bool on = true ); bool testPaintAttribute( PaintAttribute ) const; void setLegendAttribute( LegendAttribute, bool on = true ); bool testLegendAttribute( LegendAttribute ) const; #ifndef QWT_NO_COMPAT void setRawSamples( const double *xData, const double *yData, int size ); void setSamples( const double *xData, const double *yData, int size ); void setSamples( const QVector &xData, const QVector &yData ); #endif void setSamples( const QVector & ); void setSamples( QwtSeriesData * ); int closestPoint( const QPoint &pos, double *dist = NULL ) const; double minXValue() const; double maxXValue() const; double minYValue() const; double maxYValue() const; void setCurveAttribute( CurveAttribute, bool on = true ); bool testCurveAttribute( CurveAttribute ) const; void setPen( const QColor &, qreal width = 0.0, Qt::PenStyle = Qt::SolidLine ); void setPen( const QPen & ); const QPen &pen() const; void setBrush( const QBrush & ); const QBrush &brush() const; void setBaseline( double ); double baseline() const; void setStyle( CurveStyle style ); CurveStyle style() const; void setSymbol( QwtSymbol * ); const QwtSymbol *symbol() const; void setCurveFitter( QwtCurveFitter * ); QwtCurveFitter *curveFitter() const; virtual void drawSeries( QPainter *, const QwtScaleMap &xMap, const QwtScaleMap &yMap, const QRectF &canvasRect, int from, int to ) const; virtual QwtGraphic legendIcon( int index, const QSizeF & ) const; protected: void init(); virtual void drawCurve( QPainter *p, int style, const QwtScaleMap &xMap, const QwtScaleMap &yMap, const QRectF &canvasRect, int from, int to ) const; virtual void drawSymbols( QPainter *p, const QwtSymbol &, const QwtScaleMap &xMap, const QwtScaleMap &yMap, const QRectF &canvasRect, int from, int to ) const; virtual void drawLines( QPainter *p, const QwtScaleMap &xMap, const QwtScaleMap &yMap, const QRectF &canvasRect, int from, int to ) const; virtual void drawSticks( QPainter *p, const QwtScaleMap &xMap, const QwtScaleMap &yMap, const QRectF &canvasRect, int from, int to ) const; virtual void drawDots( QPainter *p, const QwtScaleMap &xMap, const QwtScaleMap &yMap, const QRectF &canvasRect, int from, int to ) const; virtual void drawSteps( QPainter *p, const QwtScaleMap &xMap, const QwtScaleMap &yMap, const QRectF &canvasRect, int from, int to ) const; virtual void fillCurve( QPainter *, const QwtScaleMap &, const QwtScaleMap &, const QRectF &canvasRect, QPolygonF & ) const; void closePolyline( QPainter *, const QwtScaleMap &, const QwtScaleMap &, QPolygonF & ) const; private: class PrivateData; PrivateData *d_data; }; //! boundingRect().left() inline double QwtPlotCurve::minXValue() const { return boundingRect().left(); } //! boundingRect().right() inline double QwtPlotCurve::maxXValue() const { return boundingRect().right(); } //! boundingRect().top() inline double QwtPlotCurve::minYValue() const { return boundingRect().top(); } //! boundingRect().bottom() inline double QwtPlotCurve::maxYValue() const { return boundingRect().bottom(); } Q_DECLARE_OPERATORS_FOR_FLAGS( QwtPlotCurve::PaintAttributes ) Q_DECLARE_OPERATORS_FOR_FLAGS( QwtPlotCurve::LegendAttributes ) Q_DECLARE_OPERATORS_FOR_FLAGS( QwtPlotCurve::CurveAttributes ) #endif connectome-workbench-1.2.3+git41-gc4c6c90/src/Qwt/qwt_plot_dict.cpp000066400000000000000000000105741300200146000247060ustar00rootroot00000000000000/* -*- mode: C++ ; c-file-style: "stroustrup" -*- ***************************** * Qwt Widget Library * Copyright (C) 1997 Josef Wilgen * Copyright (C) 2002 Uwe Rathmann * * This library is free software; you can redistribute it and/or * modify it under the terms of the Qwt License, Version 1.0 *****************************************************************************/ #include "qwt_plot_dict.h" class QwtPlotDict::PrivateData { public: class ItemList: public QList { public: void insertItem( QwtPlotItem *item ) { if ( item == NULL ) return; QList::iterator it = qUpperBound( begin(), end(), item, LessZThan() ); insert( it, item ); } void removeItem( QwtPlotItem *item ) { if ( item == NULL ) return; QList::iterator it = qLowerBound( begin(), end(), item, LessZThan() ); for ( ; it != end(); ++it ) { if ( item == *it ) { erase( it ); break; } } } private: class LessZThan { public: inline bool operator()( const QwtPlotItem *item1, const QwtPlotItem *item2 ) const { return item1->z() < item2->z(); } }; }; ItemList itemList; bool autoDelete; }; /*! Constructor Auto deletion is enabled. \sa setAutoDelete(), QwtPlotItem::attach() */ QwtPlotDict::QwtPlotDict() { d_data = new QwtPlotDict::PrivateData; d_data->autoDelete = true; } /*! Destructor If autoDelete() is on, all attached items will be deleted \sa setAutoDelete(), autoDelete(), QwtPlotItem::attach() */ QwtPlotDict::~QwtPlotDict() { detachItems( QwtPlotItem::Rtti_PlotItem, d_data->autoDelete ); delete d_data; } /*! En/Disable Auto deletion If Auto deletion is on all attached plot items will be deleted in the destructor of QwtPlotDict. The default value is on. \sa autoDelete(), insertItem() */ void QwtPlotDict::setAutoDelete( bool autoDelete ) { d_data->autoDelete = autoDelete; } /*! \return true if auto deletion is enabled \sa setAutoDelete(), insertItem() */ bool QwtPlotDict::autoDelete() const { return d_data->autoDelete; } /*! Insert a plot item \param item PlotItem \sa removeItem() */ void QwtPlotDict::insertItem( QwtPlotItem *item ) { d_data->itemList.insertItem( item ); } /*! Remove a plot item \param item PlotItem \sa insertItem() */ void QwtPlotDict::removeItem( QwtPlotItem *item ) { d_data->itemList.removeItem( item ); } /*! Detach items from the dictionary \param rtti In case of QwtPlotItem::Rtti_PlotItem detach all items otherwise only those items of the type rtti. \param autoDelete If true, delete all detached items */ void QwtPlotDict::detachItems( int rtti, bool autoDelete ) { PrivateData::ItemList list = d_data->itemList; QwtPlotItemIterator it = list.begin(); while ( it != list.end() ) { QwtPlotItem *item = *it; ++it; // increment before removing item from the list if ( rtti == QwtPlotItem::Rtti_PlotItem || item->rtti() == rtti ) { item->attach( NULL ); if ( autoDelete ) delete item; } } } /*! \brief A QwtPlotItemList of all attached plot items. Use caution when iterating these lists, as removing/detaching an item will invalidate the iterator. Instead you can place pointers to objects to be removed in a removal list, and traverse that list later. \return List of all attached plot items. */ const QwtPlotItemList &QwtPlotDict::itemList() const { return d_data->itemList; } /*! \return List of all attached plot items of a specific type. \param rtti See QwtPlotItem::RttiValues \sa QwtPlotItem::rtti() */ QwtPlotItemList QwtPlotDict::itemList( int rtti ) const { if ( rtti == QwtPlotItem::Rtti_PlotItem ) return d_data->itemList; QwtPlotItemList items; PrivateData::ItemList list = d_data->itemList; for ( QwtPlotItemIterator it = list.begin(); it != list.end(); ++it ) { QwtPlotItem *item = *it; if ( item->rtti() == rtti ) items += item; } return items; } connectome-workbench-1.2.3+git41-gc4c6c90/src/Qwt/qwt_plot_dict.h000066400000000000000000000031701300200146000243450ustar00rootroot00000000000000/* -*- mode: C++ ; c-file-style: "stroustrup" -*- ***************************** * Qwt Widget Library * Copyright (C) 1997 Josef Wilgen * Copyright (C) 2002 Uwe Rathmann * * This library is free software; you can redistribute it and/or * modify it under the terms of the Qwt License, Version 1.0 *****************************************************************************/ /*! \file !*/ #ifndef QWT_PLOT_DICT #define QWT_PLOT_DICT #include "qwt_global.h" #include "qwt_plot_item.h" #include /// \var typedef QList< QwtPlotItem *> QwtPlotItemList /// \brief See QT 4.x assistant documentation for QList typedef QList QwtPlotItemList; typedef QList::ConstIterator QwtPlotItemIterator; /*! \brief A dictionary for plot items QwtPlotDict organizes plot items in increasing z-order. If autoDelete() is enabled, all attached items will be deleted in the destructor of the dictionary. QwtPlotDict can be used to get access to all QwtPlotItem items - or all items of a specific type - that are currently on the plot. \sa QwtPlotItem::attach(), QwtPlotItem::detach(), QwtPlotItem::z() */ class QWT_EXPORT QwtPlotDict { public: explicit QwtPlotDict(); virtual ~QwtPlotDict(); void setAutoDelete( bool ); bool autoDelete() const; const QwtPlotItemList& itemList() const; QwtPlotItemList itemList( int rtti ) const; void detachItems( int rtti = QwtPlotItem::Rtti_PlotItem, bool autoDelete = true ); protected: void insertItem( QwtPlotItem * ); void removeItem( QwtPlotItem * ); private: class PrivateData; PrivateData *d_data; }; #endif connectome-workbench-1.2.3+git41-gc4c6c90/src/Qwt/qwt_plot_directpainter.cpp000066400000000000000000000203311300200146000266100ustar00rootroot00000000000000/* -*- mode: C++ ; c-file-style: "stroustrup" -*- ***************************** * Qwt Widget Library * Copyright (C) 1997 Josef Wilgen * Copyright (C) 2002 Uwe Rathmann * * This library is free software; you can redistribute it and/or * modify it under the terms of the Qwt License, Version 1.0 *****************************************************************************/ #include "qwt_plot_directpainter.h" #include "qwt_scale_map.h" #include "qwt_plot.h" #include "qwt_plot_canvas.h" #include "qwt_plot_seriesitem.h" #include #include #include #include static inline void qwtRenderItem( QPainter *painter, const QRect &canvasRect, QwtPlotSeriesItem *seriesItem, int from, int to ) { // A minor performance improvement is possible // with caching the maps. TODO ... QwtPlot *plot = seriesItem->plot(); const QwtScaleMap xMap = plot->canvasMap( seriesItem->xAxis() ); const QwtScaleMap yMap = plot->canvasMap( seriesItem->yAxis() ); painter->setRenderHint( QPainter::Antialiasing, seriesItem->testRenderHint( QwtPlotItem::RenderAntialiased ) ); seriesItem->drawSeries( painter, xMap, yMap, canvasRect, from, to ); } static inline bool qwtHasBackingStore( const QwtPlotCanvas *canvas ) { return canvas->testPaintAttribute( QwtPlotCanvas::BackingStore ) && canvas->backingStore() && !canvas->backingStore()->isNull(); } class QwtPlotDirectPainter::PrivateData { public: PrivateData(): attributes( 0 ), hasClipping(false), seriesItem( NULL ) { } QwtPlotDirectPainter::Attributes attributes; bool hasClipping; QRegion clipRegion; QPainter painter; QwtPlotSeriesItem *seriesItem; int from; int to; }; //! Constructor QwtPlotDirectPainter::QwtPlotDirectPainter( QObject *parent ): QObject( parent ) { d_data = new PrivateData; } //! Destructor QwtPlotDirectPainter::~QwtPlotDirectPainter() { delete d_data; } /*! Change an attribute \param attribute Attribute to change \param on On/Off \sa Attribute, testAttribute() */ void QwtPlotDirectPainter::setAttribute( Attribute attribute, bool on ) { if ( bool( d_data->attributes & attribute ) != on ) { if ( on ) d_data->attributes |= attribute; else d_data->attributes &= ~attribute; if ( ( attribute == AtomicPainter ) && on ) reset(); } } /*! \return True, when attribute is enabled \param attribute Attribute to be tested \sa Attribute, setAttribute() */ bool QwtPlotDirectPainter::testAttribute( Attribute attribute ) const { return d_data->attributes & attribute; } /*! En/Disables clipping \param enable Enables clipping is true, disable it otherwise \sa hasClipping(), clipRegion(), setClipRegion() */ void QwtPlotDirectPainter::setClipping( bool enable ) { d_data->hasClipping = enable; } /*! \return true, when clipping is enabled \sa setClipping(), clipRegion(), setClipRegion() */ bool QwtPlotDirectPainter::hasClipping() const { return d_data->hasClipping; } /*! \brief Assign a clip region and enable clipping Depending on the environment setting a proper clip region might improve the performance heavily. F.e. on Qt embedded only the clipped part of the backing store will be copied to a ( maybe unaccelerated ) frame buffer device. \param region Clip region \sa clipRegion(), hasClipping(), setClipping() */ void QwtPlotDirectPainter::setClipRegion( const QRegion ®ion ) { d_data->clipRegion = region; d_data->hasClipping = true; } /*! \return Currently set clip region. \sa setClipRegion(), setClipping(), hasClipping() */ QRegion QwtPlotDirectPainter::clipRegion() const { return d_data->clipRegion; } /*! \brief Draw a set of points of a seriesItem. When observing an measurement while it is running, new points have to be added to an existing seriesItem. drawSeries() can be used to display them avoiding a complete redraw of the canvas. Setting plot()->canvas()->setAttribute(Qt::WA_PaintOutsidePaintEvent, true); will result in faster painting, if the paint engine of the canvas widget supports this feature. \param seriesItem Item to be painted \param from Index of the first point to be painted \param to Index of the last point to be painted. If to < 0 the series will be painted to its last point. */ void QwtPlotDirectPainter::drawSeries( QwtPlotSeriesItem *seriesItem, int from, int to ) { if ( seriesItem == NULL || seriesItem->plot() == NULL ) return; QWidget *canvas = seriesItem->plot()->canvas(); const QRect canvasRect = canvas->contentsRect(); QwtPlotCanvas *plotCanvas = qobject_cast( canvas ); if ( plotCanvas && qwtHasBackingStore( plotCanvas ) ) { QPainter painter( const_cast( plotCanvas->backingStore() ) ); if ( d_data->hasClipping ) painter.setClipRegion( d_data->clipRegion ); qwtRenderItem( &painter, canvasRect, seriesItem, from, to ); if ( testAttribute( QwtPlotDirectPainter::FullRepaint ) ) { plotCanvas->repaint(); return; } } bool immediatePaint = true; if ( !canvas->testAttribute( Qt::WA_WState_InPaintEvent ) ) { #if QT_VERSION < 0x050000 if ( !canvas->testAttribute( Qt::WA_PaintOutsidePaintEvent ) ) #endif immediatePaint = false; } if ( immediatePaint ) { if ( !d_data->painter.isActive() ) { reset(); d_data->painter.begin( canvas ); canvas->installEventFilter( this ); } if ( d_data->hasClipping ) { d_data->painter.setClipRegion( QRegion( canvasRect ) & d_data->clipRegion ); } else { if ( !d_data->painter.hasClipping() ) d_data->painter.setClipRect( canvasRect ); } qwtRenderItem( &d_data->painter, canvasRect, seriesItem, from, to ); if ( d_data->attributes & QwtPlotDirectPainter::AtomicPainter ) { reset(); } else { if ( d_data->hasClipping ) d_data->painter.setClipping( false ); } } else { reset(); d_data->seriesItem = seriesItem; d_data->from = from; d_data->to = to; QRegion clipRegion = canvasRect; if ( d_data->hasClipping ) clipRegion &= d_data->clipRegion; canvas->installEventFilter( this ); canvas->repaint(clipRegion); canvas->removeEventFilter( this ); d_data->seriesItem = NULL; } } //! Close the internal QPainter void QwtPlotDirectPainter::reset() { if ( d_data->painter.isActive() ) { QWidget *w = static_cast( d_data->painter.device() ); if ( w ) w->removeEventFilter( this ); d_data->painter.end(); } } //! Event filter bool QwtPlotDirectPainter::eventFilter( QObject *, QEvent *event ) { if ( event->type() == QEvent::Paint ) { reset(); if ( d_data->seriesItem ) { const QPaintEvent *pe = static_cast< QPaintEvent *>( event ); QWidget *canvas = d_data->seriesItem->plot()->canvas(); QPainter painter( canvas ); painter.setClipRegion( pe->region() ); bool doCopyCache = testAttribute( CopyBackingStore ); if ( doCopyCache ) { QwtPlotCanvas *plotCanvas = qobject_cast( canvas ); if ( plotCanvas ) { doCopyCache = qwtHasBackingStore( plotCanvas ); if ( doCopyCache ) { painter.drawPixmap( plotCanvas->contentsRect().topLeft(), *plotCanvas->backingStore() ); } } } if ( !doCopyCache ) { qwtRenderItem( &painter, canvas->contentsRect(), d_data->seriesItem, d_data->from, d_data->to ); } return true; // don't call QwtPlotCanvas::paintEvent() } } return false; } connectome-workbench-1.2.3+git41-gc4c6c90/src/Qwt/qwt_plot_directpainter.h000066400000000000000000000061271300200146000262640ustar00rootroot00000000000000/* -*- mode: C++ ; c-file-style: "stroustrup" -*- ***************************** * Qwt Widget Library * Copyright (C) 1997 Josef Wilgen * Copyright (C) 2002 Uwe Rathmann * * This library is free software; you can redistribute it and/or * modify it under the terms of the Qwt License, Version 1.0 *****************************************************************************/ #ifndef QWT_PLOT_DIRECT_PAINTER_H #define QWT_PLOT_DIRECT_PAINTER_H #include "qwt_global.h" #include class QRegion; class QwtPlotSeriesItem; /*! \brief Painter object trying to paint incrementally Often applications want to display samples while they are collected. When there are too many samples complete replots will be expensive to be processed in a collection cycle. QwtPlotDirectPainter offers an API to paint subsets ( f.e all additions points ) without erasing/repainting the plot canvas. On certain environments it might be important to calculate a proper clip region before painting. F.e. for Qt Embedded only the clipped part of the backing store will be copied to a ( maybe unaccelerated ) frame buffer. \warning Incremental painting will only help when no replot is triggered by another operation ( like changing scales ) and nothing needs to be erased. */ class QWT_EXPORT QwtPlotDirectPainter: public QObject { public: /*! \brief Paint attributes \sa setAttribute(), testAttribute(), drawSeries() */ enum Attribute { /*! Initializing a QPainter is an expensive operation. When AtomicPainter is set each call of drawSeries() opens/closes a temporary QPainter. Otherwise QwtPlotDirectPainter tries to use the same QPainter as long as possible. */ AtomicPainter = 0x01, /*! When FullRepaint is set the plot canvas is explicitly repainted after the samples have been rendered. */ FullRepaint = 0x02, /*! When QwtPlotCanvas::BackingStore is enabled the painter has to paint to the backing store and the widget. In certain situations/environments it might be faster to paint to the backing store only and then copy the backing store to the canvas. This flag can also be useful for settings, where Qt fills the the clip region with the widget background. */ CopyBackingStore = 0x04 }; //! Paint attributes typedef QFlags Attributes; QwtPlotDirectPainter( QObject *parent = NULL ); virtual ~QwtPlotDirectPainter(); void setAttribute( Attribute, bool on ); bool testAttribute( Attribute ) const; void setClipping( bool ); bool hasClipping() const; void setClipRegion( const QRegion & ); QRegion clipRegion() const; void drawSeries( QwtPlotSeriesItem *, int from, int to ); void reset(); virtual bool eventFilter( QObject *, QEvent * ); private: class PrivateData; PrivateData *d_data; }; Q_DECLARE_OPERATORS_FOR_FLAGS( QwtPlotDirectPainter::Attributes ) #endif connectome-workbench-1.2.3+git41-gc4c6c90/src/Qwt/qwt_plot_grid.cpp000066400000000000000000000231731300200146000247070ustar00rootroot00000000000000/* -*- mode: C++ ; c-file-style: "stroustrup" -*- ***************************** * Qwt Widget Library * Copyright (C) 1997 Josef Wilgen * Copyright (C) 2002 Uwe Rathmann * * This library is free software; you can redistribute it and/or * modify it under the terms of the Qwt License, Version 1.0 *****************************************************************************/ #include "qwt_plot_grid.h" #include "qwt_painter.h" #include "qwt_text.h" #include "qwt_scale_map.h" #include "qwt_scale_div.h" #include "qwt_math.h" #include #include class QwtPlotGrid::PrivateData { public: PrivateData(): xEnabled( true ), yEnabled( true ), xMinEnabled( false ), yMinEnabled( false ) { } bool xEnabled; bool yEnabled; bool xMinEnabled; bool yMinEnabled; QwtScaleDiv xScaleDiv; QwtScaleDiv yScaleDiv; QPen majorPen; QPen minorPen; }; //! Enables major grid, disables minor grid QwtPlotGrid::QwtPlotGrid(): QwtPlotItem( QwtText( "Grid" ) ) { d_data = new PrivateData; setItemInterest( QwtPlotItem::ScaleInterest, true ); setZ( 10.0 ); } //! Destructor QwtPlotGrid::~QwtPlotGrid() { delete d_data; } //! \return QwtPlotItem::Rtti_PlotGrid int QwtPlotGrid::rtti() const { return QwtPlotItem::Rtti_PlotGrid; } /*! \brief Enable or disable vertical grid lines \param on Enable (true) or disable \sa Minor grid lines can be enabled or disabled with enableXMin() */ void QwtPlotGrid::enableX( bool on ) { if ( d_data->xEnabled != on ) { d_data->xEnabled = on; legendChanged(); itemChanged(); } } /*! \brief Enable or disable horizontal grid lines \param on Enable (true) or disable \sa Minor grid lines can be enabled or disabled with enableYMin() */ void QwtPlotGrid::enableY( bool on ) { if ( d_data->yEnabled != on ) { d_data->yEnabled = on; legendChanged(); itemChanged(); } } /*! \brief Enable or disable minor vertical grid lines. \param on Enable (true) or disable \sa enableX() */ void QwtPlotGrid::enableXMin( bool on ) { if ( d_data->xMinEnabled != on ) { d_data->xMinEnabled = on; legendChanged(); itemChanged(); } } /*! \brief Enable or disable minor horizontal grid lines \param on Enable (true) or disable \sa enableY() */ void QwtPlotGrid::enableYMin( bool on ) { if ( d_data->yMinEnabled != on ) { d_data->yMinEnabled = on; legendChanged(); itemChanged(); } } /*! Assign an x axis scale division \param scaleDiv Scale division */ void QwtPlotGrid::setXDiv( const QwtScaleDiv &scaleDiv ) { if ( d_data->xScaleDiv != scaleDiv ) { d_data->xScaleDiv = scaleDiv; itemChanged(); } } /*! Assign a y axis division \param scaleDiv Scale division */ void QwtPlotGrid::setYDiv( const QwtScaleDiv &scaleDiv ) { if ( d_data->yScaleDiv != scaleDiv ) { d_data->yScaleDiv = scaleDiv; itemChanged(); } } /*! Build and assign a pen for both major and minor grid lines In Qt5 the default pen width is 1.0 ( 0.0 in Qt4 ) what makes it non cosmetic ( see QPen::isCosmetic() ). This method has been introduced to hide this incompatibility. \param color Pen color \param width Pen width \param style Pen style \sa pen(), brush() */ void QwtPlotGrid::setPen( const QColor &color, qreal width, Qt::PenStyle style ) { setPen( QPen( color, width, style ) ); } /*! Assign a pen for both major and minor grid lines \param pen Pen \sa setMajorPen(), setMinorPen() */ void QwtPlotGrid::setPen( const QPen &pen ) { if ( d_data->majorPen != pen || d_data->minorPen != pen ) { d_data->majorPen = pen; d_data->minorPen = pen; legendChanged(); itemChanged(); } } /*! Build and assign a pen for both major grid lines In Qt5 the default pen width is 1.0 ( 0.0 in Qt4 ) what makes it non cosmetic ( see QPen::isCosmetic() ). This method has been introduced to hide this incompatibility. \param color Pen color \param width Pen width \param style Pen style \sa pen(), brush() */ void QwtPlotGrid::setMajorPen( const QColor &color, qreal width, Qt::PenStyle style ) { setMajorPen( QPen( color, width, style ) ); } /*! Assign a pen for the major grid lines \param pen Pen \sa majorPen(), setMinorPen(), setPen() */ void QwtPlotGrid::setMajorPen( const QPen &pen ) { if ( d_data->majorPen != pen ) { d_data->majorPen = pen; legendChanged(); itemChanged(); } } /*! Build and assign a pen for the minor grid lines In Qt5 the default pen width is 1.0 ( 0.0 in Qt4 ) what makes it non cosmetic ( see QPen::isCosmetic() ). This method has been introduced to hide this incompatibility. \param color Pen color \param width Pen width \param style Pen style \sa pen(), brush() */ void QwtPlotGrid::setMinorPen( const QColor &color, qreal width, Qt::PenStyle style ) { setMinorPen( QPen( color, width, style ) ); } /*! Assign a pen for the minor grid lines \param pen Pen \sa minorPen(), setMajorPen(), setPen() */ void QwtPlotGrid::setMinorPen( const QPen &pen ) { if ( d_data->minorPen != pen ) { d_data->minorPen = pen; legendChanged(); itemChanged(); } } /*! \brief Draw the grid The grid is drawn into the bounding rectangle such that grid lines begin and end at the rectangle's borders. The X and Y maps are used to map the scale divisions into the drawing region screen. \param painter Painter \param xMap X axis map \param yMap Y axis \param canvasRect Contents rectangle of the plot canvas */ void QwtPlotGrid::draw( QPainter *painter, const QwtScaleMap &xMap, const QwtScaleMap &yMap, const QRectF &canvasRect ) const { // draw minor grid lines QPen minorPen = d_data->minorPen; minorPen.setCapStyle( Qt::FlatCap ); painter->setPen( minorPen ); if ( d_data->xEnabled && d_data->xMinEnabled ) { drawLines( painter, canvasRect, Qt::Vertical, xMap, d_data->xScaleDiv.ticks( QwtScaleDiv::MinorTick ) ); drawLines( painter, canvasRect, Qt::Vertical, xMap, d_data->xScaleDiv.ticks( QwtScaleDiv::MediumTick ) ); } if ( d_data->yEnabled && d_data->yMinEnabled ) { drawLines( painter, canvasRect, Qt::Horizontal, yMap, d_data->yScaleDiv.ticks( QwtScaleDiv::MinorTick ) ); drawLines( painter, canvasRect, Qt::Horizontal, yMap, d_data->yScaleDiv.ticks( QwtScaleDiv::MediumTick ) ); } // draw major grid lines QPen majorPen = d_data->majorPen; majorPen.setCapStyle( Qt::FlatCap ); painter->setPen( majorPen ); if ( d_data->xEnabled ) { drawLines( painter, canvasRect, Qt::Vertical, xMap, d_data->xScaleDiv.ticks( QwtScaleDiv::MajorTick ) ); } if ( d_data->yEnabled ) { drawLines( painter, canvasRect, Qt::Horizontal, yMap, d_data->yScaleDiv.ticks( QwtScaleDiv::MajorTick ) ); } } void QwtPlotGrid::drawLines( QPainter *painter, const QRectF &canvasRect, Qt::Orientation orientation, const QwtScaleMap &scaleMap, const QList &values ) const { const double x1 = canvasRect.left(); const double x2 = canvasRect.right() - 1.0; const double y1 = canvasRect.top(); const double y2 = canvasRect.bottom() - 1.0; const bool doAlign = QwtPainter::roundingAlignment( painter ); for ( int i = 0; i < values.count(); i++ ) { double value = scaleMap.transform( values[i] ); if ( doAlign ) value = qRound( value ); if ( orientation == Qt::Horizontal ) { if ( qwtFuzzyGreaterOrEqual( value, y1 ) && qwtFuzzyLessOrEqual( value, y2 ) ) { QwtPainter::drawLine( painter, x1, value, x2, value ); } } else { if ( qwtFuzzyGreaterOrEqual( value, x1 ) && qwtFuzzyLessOrEqual( value, x2 ) ) { QwtPainter::drawLine( painter, value, y1, value, y2 ); } } } } /*! \return the pen for the major grid lines \sa setMajorPen(), setMinorPen(), setPen() */ const QPen &QwtPlotGrid::majorPen() const { return d_data->majorPen; } /*! \return the pen for the minor grid lines \sa setMinorPen(), setMajorPen(), setPen() */ const QPen &QwtPlotGrid::minorPen() const { return d_data->minorPen; } /*! \return true if vertical grid lines are enabled \sa enableX() */ bool QwtPlotGrid::xEnabled() const { return d_data->xEnabled; } /*! \return true if minor vertical grid lines are enabled \sa enableXMin() */ bool QwtPlotGrid::xMinEnabled() const { return d_data->xMinEnabled; } /*! \return true if horizontal grid lines are enabled \sa enableY() */ bool QwtPlotGrid::yEnabled() const { return d_data->yEnabled; } /*! \return true if minor horizontal grid lines are enabled \sa enableYMin() */ bool QwtPlotGrid::yMinEnabled() const { return d_data->yMinEnabled; } /*! \return the scale division of the x axis */ const QwtScaleDiv &QwtPlotGrid::xScaleDiv() const { return d_data->xScaleDiv; } /*! \return the scale division of the y axis */ const QwtScaleDiv &QwtPlotGrid::yScaleDiv() const { return d_data->yScaleDiv; } /*! Update the grid to changes of the axes scale division \param xScaleDiv Scale division of the x-axis \param yScaleDiv Scale division of the y-axis \sa QwtPlot::updateAxes() */ void QwtPlotGrid::updateScaleDiv( const QwtScaleDiv& xScaleDiv, const QwtScaleDiv& yScaleDiv ) { setXDiv( xScaleDiv ); setYDiv( yScaleDiv ); } connectome-workbench-1.2.3+git41-gc4c6c90/src/Qwt/qwt_plot_grid.h000066400000000000000000000046031300200146000243510ustar00rootroot00000000000000/* -*- mode: C++ ; c-file-style: "stroustrup" -*- ***************************** * Qwt Widget Library * Copyright (C) 1997 Josef Wilgen * Copyright (C) 2002 Uwe Rathmann * * This library is free software; you can redistribute it and/or * modify it under the terms of the Qwt License, Version 1.0 *****************************************************************************/ #ifndef QWT_PLOT_GRID_H #define QWT_PLOT_GRID_H #include "qwt_global.h" #include "qwt_plot_item.h" #include "qwt_scale_div.h" class QPainter; class QPen; class QwtScaleMap; class QwtScaleDiv; /*! \brief A class which draws a coordinate grid The QwtPlotGrid class can be used to draw a coordinate grid. A coordinate grid consists of major and minor vertical and horizontal grid lines. The locations of the grid lines are determined by the X and Y scale divisions which can be assigned with setXDiv() and setYDiv(). The draw() member draws the grid within a bounding rectangle. */ class QWT_EXPORT QwtPlotGrid: public QwtPlotItem { public: explicit QwtPlotGrid(); virtual ~QwtPlotGrid(); virtual int rtti() const; void enableX( bool tf ); bool xEnabled() const; void enableY( bool tf ); bool yEnabled() const; void enableXMin( bool tf ); bool xMinEnabled() const; void enableYMin( bool tf ); bool yMinEnabled() const; void setXDiv( const QwtScaleDiv &sx ); const QwtScaleDiv &xScaleDiv() const; void setYDiv( const QwtScaleDiv &sy ); const QwtScaleDiv &yScaleDiv() const; void setPen( const QColor &, qreal width = 0.0, Qt::PenStyle = Qt::SolidLine ); void setPen( const QPen & ); void setMajorPen( const QColor &, qreal width = 0.0, Qt::PenStyle = Qt::SolidLine ); void setMajorPen( const QPen & ); const QPen& majorPen() const; void setMinorPen( const QColor &, qreal width = 0.0, Qt::PenStyle = Qt::SolidLine ); void setMinorPen( const QPen &p ); const QPen& minorPen() const; virtual void draw( QPainter *p, const QwtScaleMap &xMap, const QwtScaleMap &yMap, const QRectF &rect ) const; virtual void updateScaleDiv( const QwtScaleDiv &xMap, const QwtScaleDiv &yMap ); private: void drawLines( QPainter *painter, const QRectF &, Qt::Orientation orientation, const QwtScaleMap &, const QList & ) const; class PrivateData; PrivateData *d_data; }; #endif connectome-workbench-1.2.3+git41-gc4c6c90/src/Qwt/qwt_plot_histogram.cpp000066400000000000000000000434171300200146000257620ustar00rootroot00000000000000/* -*- mode: C++ ; c-file-style: "stroustrup" -*- ***************************** * Qwt Widget Library * Copyright (C) 1997 Josef Wilgen * Copyright (C) 2002 Uwe Rathmann * * This library is free software; you can redistribute it and/or * modify it under the terms of the Qwt License, Version 1.0 *****************************************************************************/ #include "qwt_plot_histogram.h" #include "qwt_plot.h" #include "qwt_painter.h" #include "qwt_column_symbol.h" #include "qwt_scale_map.h" #include #include static inline bool qwtIsCombinable( const QwtInterval &d1, const QwtInterval &d2 ) { if ( d1.isValid() && d2.isValid() ) { if ( d1.maxValue() == d2.minValue() ) { if ( !( d1.borderFlags() & QwtInterval::ExcludeMaximum && d2.borderFlags() & QwtInterval::ExcludeMinimum ) ) { return true; } } } return false; } class QwtPlotHistogram::PrivateData { public: PrivateData(): baseline( 0.0 ), style( Columns ), symbol( NULL ) { } ~PrivateData() { delete symbol; } double baseline; QPen pen; QBrush brush; QwtPlotHistogram::HistogramStyle style; const QwtColumnSymbol *symbol; }; /*! Constructor \param title Title of the histogram. */ QwtPlotHistogram::QwtPlotHistogram( const QwtText &title ): QwtPlotSeriesItem( title ) { init(); } /*! Constructor \param title Title of the histogram. */ QwtPlotHistogram::QwtPlotHistogram( const QString &title ): QwtPlotSeriesItem( title ) { init(); } //! Destructor QwtPlotHistogram::~QwtPlotHistogram() { delete d_data; } //! Initialize data members void QwtPlotHistogram::init() { d_data = new PrivateData(); setData( new QwtIntervalSeriesData() ); setItemAttribute( QwtPlotItem::AutoScale, true ); setItemAttribute( QwtPlotItem::Legend, true ); setZ( 20.0 ); } /*! Set the histogram's drawing style \param style Histogram style \sa HistogramStyle, style() */ void QwtPlotHistogram::setStyle( HistogramStyle style ) { if ( style != d_data->style ) { d_data->style = style; legendChanged(); itemChanged(); } } /*! \return Style of the histogram \sa HistogramStyle, setStyle() */ QwtPlotHistogram::HistogramStyle QwtPlotHistogram::style() const { return d_data->style; } /*! Build and assign a pen In Qt5 the default pen width is 1.0 ( 0.0 in Qt4 ) what makes it non cosmetic ( see QPen::isCosmetic() ). This method has been introduced to hide this incompatibility. \param color Pen color \param width Pen width \param style Pen style \sa pen(), brush() */ void QwtPlotHistogram::setPen( const QColor &color, qreal width, Qt::PenStyle style ) { setPen( QPen( color, width, style ) ); } /*! Assign a pen, that is used in a style() depending way. \param pen New pen \sa pen(), brush() */ void QwtPlotHistogram::setPen( const QPen &pen ) { if ( pen != d_data->pen ) { d_data->pen = pen; legendChanged(); itemChanged(); } } /*! \return Pen used in a style() depending way. \sa setPen(), brush() */ const QPen &QwtPlotHistogram::pen() const { return d_data->pen; } /*! Assign a brush, that is used in a style() depending way. \param brush New brush \sa pen(), brush() */ void QwtPlotHistogram::setBrush( const QBrush &brush ) { if ( brush != d_data->brush ) { d_data->brush = brush; legendChanged(); itemChanged(); } } /*! \return Brush used in a style() depending way. \sa setPen(), brush() */ const QBrush &QwtPlotHistogram::brush() const { return d_data->brush; } /*! \brief Assign a symbol In Column style an optional symbol can be assigned, that is responsible for displaying the rectangle that is defined by the interval and the distance between baseline() and value. When no symbol has been defined the area is displayed as plain rectangle using pen() and brush(). \sa style(), symbol(), drawColumn(), pen(), brush() \note In applications, where different intervals need to be displayed in a different way ( f.e different colors or even using different symbols) it is recommended to overload drawColumn(). */ void QwtPlotHistogram::setSymbol( const QwtColumnSymbol *symbol ) { if ( symbol != d_data->symbol ) { delete d_data->symbol; d_data->symbol = symbol; legendChanged(); itemChanged(); } } /*! \return Current symbol or NULL, when no symbol has been assigned \sa setSymbol() */ const QwtColumnSymbol *QwtPlotHistogram::symbol() const { return d_data->symbol; } /*! \brief Set the value of the baseline Each column representing an QwtIntervalSample is defined by its interval and the interval between baseline and the value of the sample. The default value of the baseline is 0.0. \param value Value of the baseline \sa baseline() */ void QwtPlotHistogram::setBaseline( double value ) { if ( d_data->baseline != value ) { d_data->baseline = value; itemChanged(); } } /*! \return Value of the baseline \sa setBaseline() */ double QwtPlotHistogram::baseline() const { return d_data->baseline; } /*! \return Bounding rectangle of all samples. For an empty series the rectangle is invalid. */ QRectF QwtPlotHistogram::boundingRect() const { QRectF rect = data()->boundingRect(); if ( !rect.isValid() ) return rect; if ( orientation() == Qt::Horizontal ) { rect = QRectF( rect.y(), rect.x(), rect.height(), rect.width() ); if ( rect.left() > d_data->baseline ) rect.setLeft( d_data->baseline ); else if ( rect.right() < d_data->baseline ) rect.setRight( d_data->baseline ); } else { if ( rect.bottom() < d_data->baseline ) rect.setBottom( d_data->baseline ); else if ( rect.top() > d_data->baseline ) rect.setTop( d_data->baseline ); } return rect; } //! \return QwtPlotItem::Rtti_PlotHistogram int QwtPlotHistogram::rtti() const { return QwtPlotItem::Rtti_PlotHistogram; } /*! Initialize data with an array of samples. \param samples Vector of points */ void QwtPlotHistogram::setSamples( const QVector &samples ) { setData( new QwtIntervalSeriesData( samples ) ); } /*! Assign a series of samples setSamples() is just a wrapper for setData() without any additional value - beside that it is easier to find for the developer. \param data Data \warning The item takes ownership of the data object, deleting it when its not used anymore. */ void QwtPlotHistogram::setSamples( QwtSeriesData *data ) { setData( data ); } /*! Draw a subset of the histogram samples \param painter Painter \param xMap Maps x-values into pixel coordinates. \param yMap Maps y-values into pixel coordinates. \param canvasRect Contents rectangle of the canvas \param from Index of the first sample to be painted \param to Index of the last sample to be painted. If to < 0 the series will be painted to its last sample. \sa drawOutline(), drawLines(), drawColumns */ void QwtPlotHistogram::drawSeries( QPainter *painter, const QwtScaleMap &xMap, const QwtScaleMap &yMap, const QRectF &, int from, int to ) const { if ( !painter || dataSize() <= 0 ) return; if ( to < 0 ) to = dataSize() - 1; switch ( d_data->style ) { case Outline: drawOutline( painter, xMap, yMap, from, to ); break; case Lines: drawLines( painter, xMap, yMap, from, to ); break; case Columns: drawColumns( painter, xMap, yMap, from, to ); break; default: break; } } /*! Draw a histogram in Outline style() \param painter Painter \param xMap Maps x-values into pixel coordinates. \param yMap Maps y-values into pixel coordinates. \param from Index of the first sample to be painted \param to Index of the last sample to be painted. If to < 0 the histogram will be painted to its last point. \sa setStyle(), style() \warning The outline style requires, that the intervals are in increasing order and not overlapping. */ void QwtPlotHistogram::drawOutline( QPainter *painter, const QwtScaleMap &xMap, const QwtScaleMap &yMap, int from, int to ) const { const bool doAlign = QwtPainter::roundingAlignment( painter ); double v0 = ( orientation() == Qt::Horizontal ) ? xMap.transform( baseline() ) : yMap.transform( baseline() ); if ( doAlign ) v0 = qRound( v0 ); QwtIntervalSample previous; QPolygonF polygon; for ( int i = from; i <= to; i++ ) { const QwtIntervalSample sample = this->sample( i ); if ( !sample.interval.isValid() ) { flushPolygon( painter, v0, polygon ); previous = sample; continue; } if ( previous.interval.isValid() ) { if ( !qwtIsCombinable( previous.interval, sample.interval ) ) flushPolygon( painter, v0, polygon ); } if ( orientation() == Qt::Vertical ) { double x1 = xMap.transform( sample.interval.minValue() ); double x2 = xMap.transform( sample.interval.maxValue() ); double y = yMap.transform( sample.value ); if ( doAlign ) { x1 = qRound( x1 ); x2 = qRound( x2 ); y = qRound( y ); } if ( polygon.size() == 0 ) polygon += QPointF( x1, v0 ); polygon += QPointF( x1, y ); polygon += QPointF( x2, y ); } else { double y1 = yMap.transform( sample.interval.minValue() ); double y2 = yMap.transform( sample.interval.maxValue() ); double x = xMap.transform( sample.value ); if ( doAlign ) { y1 = qRound( y1 ); y2 = qRound( y2 ); x = qRound( x ); } if ( polygon.size() == 0 ) polygon += QPointF( v0, y1 ); polygon += QPointF( x, y1 ); polygon += QPointF( x, y2 ); } previous = sample; } flushPolygon( painter, v0, polygon ); } /*! Draw a histogram in Columns style() \param painter Painter \param xMap Maps x-values into pixel coordinates. \param yMap Maps y-values into pixel coordinates. \param from Index of the first sample to be painted \param to Index of the last sample to be painted. If to < 0 the histogram will be painted to its last point. \sa setStyle(), style(), setSymbol(), drawColumn() */ void QwtPlotHistogram::drawColumns( QPainter *painter, const QwtScaleMap &xMap, const QwtScaleMap &yMap, int from, int to ) const { painter->setPen( d_data->pen ); painter->setBrush( d_data->brush ); const QwtSeriesData *series = data(); for ( int i = from; i <= to; i++ ) { const QwtIntervalSample sample = series->sample( i ); if ( !sample.interval.isNull() ) { const QwtColumnRect rect = columnRect( sample, xMap, yMap ); drawColumn( painter, rect, sample ); } } } /*! Draw a histogram in Lines style() \param painter Painter \param xMap Maps x-values into pixel coordinates. \param yMap Maps y-values into pixel coordinates. \param from Index of the first sample to be painted \param to Index of the last sample to be painted. If to < 0 the histogram will be painted to its last point. \sa setStyle(), style(), setPen() */ void QwtPlotHistogram::drawLines( QPainter *painter, const QwtScaleMap &xMap, const QwtScaleMap &yMap, int from, int to ) const { const bool doAlign = QwtPainter::roundingAlignment( painter ); painter->setPen( d_data->pen ); painter->setBrush( Qt::NoBrush ); const QwtSeriesData *series = data(); for ( int i = from; i <= to; i++ ) { const QwtIntervalSample sample = series->sample( i ); if ( !sample.interval.isNull() ) { const QwtColumnRect rect = columnRect( sample, xMap, yMap ); QRectF r = rect.toRect(); if ( doAlign ) { r.setLeft( qRound( r.left() ) ); r.setRight( qRound( r.right() ) ); r.setTop( qRound( r.top() ) ); r.setBottom( qRound( r.bottom() ) ); } switch ( rect.direction ) { case QwtColumnRect::LeftToRight: { QwtPainter::drawLine( painter, r.topRight(), r.bottomRight() ); break; } case QwtColumnRect::RightToLeft: { QwtPainter::drawLine( painter, r.topLeft(), r.bottomLeft() ); break; } case QwtColumnRect::TopToBottom: { QwtPainter::drawLine( painter, r.bottomRight(), r.bottomLeft() ); break; } case QwtColumnRect::BottomToTop: { QwtPainter::drawLine( painter, r.topRight(), r.topLeft() ); break; } } } } } //! Internal, used by the Outline style. void QwtPlotHistogram::flushPolygon( QPainter *painter, double baseLine, QPolygonF &polygon ) const { if ( polygon.size() == 0 ) return; if ( orientation() == Qt::Horizontal ) polygon += QPointF( baseLine, polygon.last().y() ); else polygon += QPointF( polygon.last().x(), baseLine ); if ( d_data->brush.style() != Qt::NoBrush ) { painter->setPen( Qt::NoPen ); painter->setBrush( d_data->brush ); if ( orientation() == Qt::Horizontal ) { polygon += QPointF( polygon.last().x(), baseLine ); polygon += QPointF( polygon.first().x(), baseLine ); } else { polygon += QPointF( baseLine, polygon.last().y() ); polygon += QPointF( baseLine, polygon.first().y() ); } QwtPainter::drawPolygon( painter, polygon ); polygon.pop_back(); polygon.pop_back(); } if ( d_data->pen.style() != Qt::NoPen ) { painter->setBrush( Qt::NoBrush ); painter->setPen( d_data->pen ); QwtPainter::drawPolyline( painter, polygon ); } polygon.clear(); } /*! Calculate the area that is covered by a sample \param sample Sample \param xMap Maps x-values into pixel coordinates. \param yMap Maps y-values into pixel coordinates. \return Rectangle, that is covered by a sample */ QwtColumnRect QwtPlotHistogram::columnRect( const QwtIntervalSample &sample, const QwtScaleMap &xMap, const QwtScaleMap &yMap ) const { QwtColumnRect rect; const QwtInterval &iv = sample.interval; if ( !iv.isValid() ) return rect; if ( orientation() == Qt::Horizontal ) { const double x0 = xMap.transform( baseline() ); const double x = xMap.transform( sample.value ); const double y1 = yMap.transform( iv.minValue() ); const double y2 = yMap.transform( iv.maxValue() ); rect.hInterval.setInterval( x0, x ); rect.vInterval.setInterval( y1, y2, iv.borderFlags() ); rect.direction = ( x < x0 ) ? QwtColumnRect::RightToLeft : QwtColumnRect::LeftToRight; } else { const double x1 = xMap.transform( iv.minValue() ); const double x2 = xMap.transform( iv.maxValue() ); const double y0 = yMap.transform( baseline() ); const double y = yMap.transform( sample.value ); rect.hInterval.setInterval( x1, x2, iv.borderFlags() ); rect.vInterval.setInterval( y0, y ); rect.direction = ( y < y0 ) ? QwtColumnRect::BottomToTop : QwtColumnRect::TopToBottom; } return rect; } /*! Draw a column for a sample in Columns style(). When a symbol() has been set the symbol is used otherwise the column is displayed as plain rectangle using pen() and brush(). \param painter Painter \param rect Rectangle where to paint the column in paint device coordinates \param sample Sample to be displayed \note In applications, where different intervals need to be displayed in a different way ( f.e different colors or even using different symbols) it is recommended to overload drawColumn(). */ void QwtPlotHistogram::drawColumn( QPainter *painter, const QwtColumnRect &rect, const QwtIntervalSample &sample ) const { Q_UNUSED( sample ); if ( d_data->symbol && ( d_data->symbol->style() != QwtColumnSymbol::NoStyle ) ) { d_data->symbol->draw( painter, rect ); } else { QRectF r = rect.toRect(); if ( QwtPainter::roundingAlignment( painter ) ) { r.setLeft( qRound( r.left() ) ); r.setRight( qRound( r.right() ) ); r.setTop( qRound( r.top() ) ); r.setBottom( qRound( r.bottom() ) ); } QwtPainter::drawRect( painter, r ); } } /*! A plain rectangle without pen using the brush() \param index Index of the legend entry ( ignored as there is only one ) \param size Icon size \return A graphic displaying the icon \sa QwtPlotItem::setLegendIconSize(), QwtPlotItem::legendData() */ QwtGraphic QwtPlotHistogram::legendIcon( int index, const QSizeF &size ) const { Q_UNUSED( index ); return defaultIcon( d_data->brush, size ); } connectome-workbench-1.2.3+git41-gc4c6c90/src/Qwt/qwt_plot_histogram.h000066400000000000000000000103071300200146000254170ustar00rootroot00000000000000/* -*- mode: C++ ; c-file-style: "stroustrup" -*- ***************************** * Qwt Widget Library * Copyright (C) 1997 Josef Wilgen * Copyright (C) 2002 Uwe Rathmann * * This library is free software; you can redistribute it and/or * modify it under the terms of the Qwt License, Version 1.0 *****************************************************************************/ #ifndef QWT_PLOT_HISTOGRAM_H #define QWT_PLOT_HISTOGRAM_H #include "qwt_global.h" #include "qwt_plot_seriesitem.h" #include "qwt_column_symbol.h" #include #include class QwtIntervalData; class QString; class QPolygonF; /*! \brief QwtPlotHistogram represents a series of samples, where an interval is associated with a value ( \f$y = f([x1,x2])\f$ ). The representation depends on the style() and an optional symbol() that is displayed for each interval. \note The term "histogram" is used in a different way in the areas of digital image processing and statistics. Wikipedia introduces the terms "image histogram" and "color histogram" to avoid confusions. While "image histograms" can be displayed by a QwtPlotCurve there is no applicable plot item for a "color histogram" yet. \sa QwtPlotBarChart, QwtPlotMultiBarChart */ class QWT_EXPORT QwtPlotHistogram: public QwtPlotSeriesItem, public QwtSeriesStore { public: /*! Histogram styles. The default style is QwtPlotHistogram::Columns. \sa setStyle(), style(), setSymbol(), symbol(), setBaseline() */ enum HistogramStyle { /*! Draw an outline around the area, that is build by all intervals using the pen() and fill it with the brush(). The outline style requires, that the intervals are in increasing order and not overlapping. */ Outline, /*! Draw a column for each interval. When a symbol() has been set the symbol is used otherwise the column is displayed as plain rectangle using pen() and brush(). */ Columns, /*! Draw a simple line using the pen() for each interval. */ Lines, /*! Styles >= UserStyle are reserved for derived classes that overload drawSeries() with additional application specific ways to display a histogram. */ UserStyle = 100 }; explicit QwtPlotHistogram( const QString &title = QString::null ); explicit QwtPlotHistogram( const QwtText &title ); virtual ~QwtPlotHistogram(); virtual int rtti() const; void setPen( const QColor &, qreal width = 0.0, Qt::PenStyle = Qt::SolidLine ); void setPen( const QPen & ); const QPen &pen() const; void setBrush( const QBrush & ); const QBrush &brush() const; void setSamples( const QVector & ); void setSamples( QwtSeriesData * ); void setBaseline( double reference ); double baseline() const; void setStyle( HistogramStyle style ); HistogramStyle style() const; void setSymbol( const QwtColumnSymbol * ); const QwtColumnSymbol *symbol() const; virtual void drawSeries( QPainter *p, const QwtScaleMap &xMap, const QwtScaleMap &yMap, const QRectF &canvasRect, int from, int to ) const; virtual QRectF boundingRect() const; virtual QwtGraphic legendIcon( int index, const QSizeF & ) const; protected: virtual QwtColumnRect columnRect( const QwtIntervalSample &, const QwtScaleMap &, const QwtScaleMap & ) const; virtual void drawColumn( QPainter *, const QwtColumnRect &, const QwtIntervalSample & ) const; void drawColumns( QPainter *, const QwtScaleMap &xMap, const QwtScaleMap &yMap, int from, int to ) const; void drawOutline( QPainter *, const QwtScaleMap &xMap, const QwtScaleMap &yMap, int from, int to ) const; void drawLines( QPainter *, const QwtScaleMap &xMap, const QwtScaleMap &yMap, int from, int to ) const; private: void init(); void flushPolygon( QPainter *, double baseLine, QPolygonF & ) const; class PrivateData; PrivateData *d_data; }; #endif connectome-workbench-1.2.3+git41-gc4c6c90/src/Qwt/qwt_plot_intervalcurve.cpp000066400000000000000000000362031300200146000266510ustar00rootroot00000000000000/* -*- mode: C++ ; c-file-style: "stroustrup" -*- ***************************** * Qwt Widget Library * Copyright (C) 1997 Josef Wilgen * Copyright (C) 2002 Uwe Rathmann * * This library is free software; you can redistribute it and/or * modify it under the terms of the Qwt License, Version 1.0 *****************************************************************************/ #include "qwt_plot_intervalcurve.h" #include "qwt_interval_symbol.h" #include "qwt_scale_map.h" #include "qwt_clipper.h" #include "qwt_painter.h" #include #include static inline bool qwtIsHSampleInside( const QwtIntervalSample &sample, double xMin, double xMax, double yMin, double yMax ) { const double y = sample.value; const double x1 = sample.interval.minValue(); const double x2 = sample.interval.maxValue(); const bool isOffScreen = ( y < yMin ) || ( y > yMax ) || ( x1 < xMin && x2 < xMin ) || ( x1 > xMax && x2 > xMax ); return !isOffScreen; } static inline bool qwtIsVSampleInside( const QwtIntervalSample &sample, double xMin, double xMax, double yMin, double yMax ) { const double x = sample.value; const double y1 = sample.interval.minValue(); const double y2 = sample.interval.maxValue(); const bool isOffScreen = ( x < xMin ) || ( x > xMax ) || ( y1 < yMin && y2 < yMin ) || ( y1 > yMax && y2 > yMax ); return !isOffScreen; } class QwtPlotIntervalCurve::PrivateData { public: PrivateData(): style( QwtPlotIntervalCurve::Tube ), symbol( NULL ), pen( Qt::black ), brush( Qt::white ) { paintAttributes = QwtPlotIntervalCurve::ClipPolygons; paintAttributes |= QwtPlotIntervalCurve::ClipSymbol; pen.setCapStyle( Qt::FlatCap ); } ~PrivateData() { delete symbol; } QwtPlotIntervalCurve::CurveStyle style; const QwtIntervalSymbol *symbol; QPen pen; QBrush brush; QwtPlotIntervalCurve::PaintAttributes paintAttributes; }; /*! Constructor \param title Title of the curve */ QwtPlotIntervalCurve::QwtPlotIntervalCurve( const QwtText &title ): QwtPlotSeriesItem( title ) { init(); } /*! Constructor \param title Title of the curve */ QwtPlotIntervalCurve::QwtPlotIntervalCurve( const QString &title ): QwtPlotSeriesItem( QwtText( title ) ) { init(); } //! Destructor QwtPlotIntervalCurve::~QwtPlotIntervalCurve() { delete d_data; } //! Initialize internal members void QwtPlotIntervalCurve::init() { setItemAttribute( QwtPlotItem::Legend, true ); setItemAttribute( QwtPlotItem::AutoScale, true ); d_data = new PrivateData; setData( new QwtIntervalSeriesData() ); setZ( 19.0 ); } //! \return QwtPlotItem::Rtti_PlotIntervalCurve int QwtPlotIntervalCurve::rtti() const { return QwtPlotIntervalCurve::Rtti_PlotIntervalCurve; } /*! Specify an attribute how to draw the curve \param attribute Paint attribute \param on On/Off \sa testPaintAttribute() */ void QwtPlotIntervalCurve::setPaintAttribute( PaintAttribute attribute, bool on ) { if ( on ) d_data->paintAttributes |= attribute; else d_data->paintAttributes &= ~attribute; } /*! \return True, when attribute is enabled \sa PaintAttribute, setPaintAttribute() */ bool QwtPlotIntervalCurve::testPaintAttribute( PaintAttribute attribute ) const { return ( d_data->paintAttributes & attribute ); } /*! Initialize data with an array of samples. \param samples Vector of samples */ void QwtPlotIntervalCurve::setSamples( const QVector &samples ) { setData( new QwtIntervalSeriesData( samples ) ); } /*! Assign a series of samples setSamples() is just a wrapper for setData() without any additional value - beside that it is easier to find for the developer. \param data Data \warning The item takes ownership of the data object, deleting it when its not used anymore. */ void QwtPlotIntervalCurve::setSamples( QwtSeriesData *data ) { setData( data ); } /*! Set the curve's drawing style \param style Curve style \sa CurveStyle, style() */ void QwtPlotIntervalCurve::setStyle( CurveStyle style ) { if ( style != d_data->style ) { d_data->style = style; legendChanged(); itemChanged(); } } /*! \return Style of the curve \sa setStyle() */ QwtPlotIntervalCurve::CurveStyle QwtPlotIntervalCurve::style() const { return d_data->style; } /*! Assign a symbol. \param symbol Symbol \sa symbol() */ void QwtPlotIntervalCurve::setSymbol( const QwtIntervalSymbol *symbol ) { if ( symbol != d_data->symbol ) { delete d_data->symbol; d_data->symbol = symbol; legendChanged(); itemChanged(); } } /*! \return Current symbol or NULL, when no symbol has been assigned \sa setSymbol() */ const QwtIntervalSymbol *QwtPlotIntervalCurve::symbol() const { return d_data->symbol; } /*! Build and assign a pen In Qt5 the default pen width is 1.0 ( 0.0 in Qt4 ) what makes it non cosmetic ( see QPen::isCosmetic() ). This method has been introduced to hide this incompatibility. \param color Pen color \param width Pen width \param style Pen style \sa pen(), brush() */ void QwtPlotIntervalCurve::setPen( const QColor &color, qreal width, Qt::PenStyle style ) { setPen( QPen( color, width, style ) ); } /*! \brief Assign a pen \param pen New pen \sa pen(), brush() */ void QwtPlotIntervalCurve::setPen( const QPen &pen ) { if ( pen != d_data->pen ) { d_data->pen = pen; legendChanged(); itemChanged(); } } /*! \return Pen used to draw the lines \sa setPen(), brush() */ const QPen& QwtPlotIntervalCurve::pen() const { return d_data->pen; } /*! Assign a brush. The brush is used to fill the area in Tube style(). \param brush Brush \sa brush(), pen(), setStyle(), CurveStyle */ void QwtPlotIntervalCurve::setBrush( const QBrush &brush ) { if ( brush != d_data->brush ) { d_data->brush = brush; legendChanged(); itemChanged(); } } /*! \return Brush used to fill the area in Tube style() \sa setBrush(), setStyle(), CurveStyle */ const QBrush& QwtPlotIntervalCurve::brush() const { return d_data->brush; } /*! \return Bounding rectangle of all samples. For an empty series the rectangle is invalid. */ QRectF QwtPlotIntervalCurve::boundingRect() const { QRectF rect = QwtPlotSeriesItem::boundingRect(); if ( rect.isValid() && orientation() == Qt::Vertical ) rect.setRect( rect.y(), rect.x(), rect.height(), rect.width() ); return rect; } /*! Draw a subset of the samples \param painter Painter \param xMap Maps x-values into pixel coordinates. \param yMap Maps y-values into pixel coordinates. \param canvasRect Contents rectangle of the canvas \param from Index of the first sample to be painted \param to Index of the last sample to be painted. If to < 0 the series will be painted to its last sample. \sa drawTube(), drawSymbols() */ void QwtPlotIntervalCurve::drawSeries( QPainter *painter, const QwtScaleMap &xMap, const QwtScaleMap &yMap, const QRectF &canvasRect, int from, int to ) const { if ( to < 0 ) to = dataSize() - 1; if ( from < 0 ) from = 0; if ( from > to ) return; switch ( d_data->style ) { case Tube: drawTube( painter, xMap, yMap, canvasRect, from, to ); break; case NoCurve: default: break; } if ( d_data->symbol && ( d_data->symbol->style() != QwtIntervalSymbol::NoSymbol ) ) { drawSymbols( painter, *d_data->symbol, xMap, yMap, canvasRect, from, to ); } } /*! Draw a tube Builds 2 curves from the upper and lower limits of the intervals and draws them with the pen(). The area between the curves is filled with the brush(). \param painter Painter \param xMap Maps x-values into pixel coordinates. \param yMap Maps y-values into pixel coordinates. \param canvasRect Contents rectangle of the canvas \param from Index of the first sample to be painted \param to Index of the last sample to be painted. If to < 0 the series will be painted to its last sample. \sa drawSeries(), drawSymbols() */ void QwtPlotIntervalCurve::drawTube( QPainter *painter, const QwtScaleMap &xMap, const QwtScaleMap &yMap, const QRectF &canvasRect, int from, int to ) const { const bool doAlign = QwtPainter::roundingAlignment( painter ); painter->save(); const size_t size = to - from + 1; QPolygonF polygon( 2 * size ); QPointF *points = polygon.data(); for ( uint i = 0; i < size; i++ ) { QPointF &minValue = points[i]; QPointF &maxValue = points[2 * size - 1 - i]; const QwtIntervalSample intervalSample = sample( from + i ); if ( orientation() == Qt::Vertical ) { double x = xMap.transform( intervalSample.value ); double y1 = yMap.transform( intervalSample.interval.minValue() ); double y2 = yMap.transform( intervalSample.interval.maxValue() ); if ( doAlign ) { x = qRound( x ); y1 = qRound( y1 ); y2 = qRound( y2 ); } minValue.rx() = x; minValue.ry() = y1; maxValue.rx() = x; maxValue.ry() = y2; } else { double y = yMap.transform( intervalSample.value ); double x1 = xMap.transform( intervalSample.interval.minValue() ); double x2 = xMap.transform( intervalSample.interval.maxValue() ); if ( doAlign ) { y = qRound( y ); x1 = qRound( x1 ); x2 = qRound( x2 ); } minValue.rx() = x1; minValue.ry() = y; maxValue.rx() = x2; maxValue.ry() = y; } } if ( d_data->brush.style() != Qt::NoBrush ) { painter->setPen( QPen( Qt::NoPen ) ); painter->setBrush( d_data->brush ); if ( d_data->paintAttributes & ClipPolygons ) { const qreal m = 1.0; const QPolygonF p = QwtClipper::clipPolygonF( canvasRect.adjusted( -m, -m, m, m ), polygon, true ); QwtPainter::drawPolygon( painter, p ); } else { QwtPainter::drawPolygon( painter, polygon ); } } if ( d_data->pen.style() != Qt::NoPen ) { painter->setPen( d_data->pen ); painter->setBrush( Qt::NoBrush ); if ( d_data->paintAttributes & ClipPolygons ) { qreal pw = qMax( qreal( 1.0 ), painter->pen().widthF() ); const QRectF clipRect = canvasRect.adjusted( -pw, -pw, pw, pw ); QPolygonF p; p.resize( size ); ::memcpy( p.data(), points, size * sizeof( QPointF ) ); p = QwtClipper::clipPolygonF( clipRect, p ); QwtPainter::drawPolyline( painter, p ); p.resize( size ); ::memcpy( p.data(), points + size, size * sizeof( QPointF ) ); p = QwtClipper::clipPolygonF( clipRect, p ); QwtPainter::drawPolyline( painter, p ); } else { QwtPainter::drawPolyline( painter, points, size ); QwtPainter::drawPolyline( painter, points + size, size ); } } painter->restore(); } /*! Draw symbols for a subset of the samples \param painter Painter \param symbol Interval symbol \param xMap x map \param yMap y map \param canvasRect Contents rectangle of the canvas \param from Index of the first sample to be painted \param to Index of the last sample to be painted \sa setSymbol(), drawSeries(), drawTube() */ void QwtPlotIntervalCurve::drawSymbols( QPainter *painter, const QwtIntervalSymbol &symbol, const QwtScaleMap &xMap, const QwtScaleMap &yMap, const QRectF &canvasRect, int from, int to ) const { painter->save(); QPen pen = symbol.pen(); pen.setCapStyle( Qt::FlatCap ); painter->setPen( pen ); painter->setBrush( symbol.brush() ); const QRectF tr = QwtScaleMap::invTransform( xMap, yMap, canvasRect ); const double xMin = tr.left(); const double xMax = tr.right(); const double yMin = tr.top(); const double yMax = tr.bottom(); const bool doClip = d_data->paintAttributes & ClipSymbol; for ( int i = from; i <= to; i++ ) { const QwtIntervalSample s = sample( i ); if ( orientation() == Qt::Vertical ) { if ( !doClip || qwtIsVSampleInside( s, xMin, xMax, yMin, yMax ) ) { const double x = xMap.transform( s.value ); const double y1 = yMap.transform( s.interval.minValue() ); const double y2 = yMap.transform( s.interval.maxValue() ); symbol.draw( painter, orientation(), QPointF( x, y1 ), QPointF( x, y2 ) ); } } else { if ( !doClip || qwtIsHSampleInside( s, xMin, xMax, yMin, yMax ) ) { const double y = yMap.transform( s.value ); const double x1 = xMap.transform( s.interval.minValue() ); const double x2 = xMap.transform( s.interval.maxValue() ); symbol.draw( painter, orientation(), QPointF( x1, y ), QPointF( x2, y ) ); } } } painter->restore(); } /*! \return Icon for the legend In case of Tube style() the icon is a plain rectangle filled with the brush(). If a symbol is assigned it is scaled to size. \param index Index of the legend entry ( ignored as there is only one ) \param size Icon size \sa QwtPlotItem::setLegendIconSize(), QwtPlotItem::legendData() */ QwtGraphic QwtPlotIntervalCurve::legendIcon( int index, const QSizeF &size ) const { Q_UNUSED( index ); if ( size.isEmpty() ) return QwtGraphic(); QwtGraphic icon; icon.setDefaultSize( size ); icon.setRenderHint( QwtGraphic::RenderPensUnscaled, true ); QPainter painter( &icon ); painter.setRenderHint( QPainter::Antialiasing, testRenderHint( QwtPlotItem::RenderAntialiased ) ); if ( d_data->style == Tube ) { QRectF r( 0, 0, size.width(), size.height() ); painter.fillRect( r, d_data->brush ); } if ( d_data->symbol && ( d_data->symbol->style() != QwtIntervalSymbol::NoSymbol ) ) { QPen pen = d_data->symbol->pen(); pen.setWidthF( pen.widthF() ); pen.setCapStyle( Qt::FlatCap ); painter.setPen( pen ); painter.setBrush( d_data->symbol->brush() ); if ( orientation() == Qt::Vertical ) { const double x = 0.5 * size.width(); d_data->symbol->draw( &painter, orientation(), QPointF( x, 0 ), QPointF( x, size.height() - 1.0 ) ); } else { const double y = 0.5 * size.height(); d_data->symbol->draw( &painter, orientation(), QPointF( 0.0, y ), QPointF( size.width() - 1.0, y ) ); } } return icon; } connectome-workbench-1.2.3+git41-gc4c6c90/src/Qwt/qwt_plot_intervalcurve.h000066400000000000000000000076401300200146000263210ustar00rootroot00000000000000/* -*- mode: C++ ; c-file-style: "stroustrup" -*- ***************************** * Qwt Widget Library * Copyright (C) 1997 Josef Wilgen * Copyright (C) 2002 Uwe Rathmann * * This library is free software; you can redistribute it and/or * modify it under the terms of the Qwt License, Version 1.0 *****************************************************************************/ #ifndef QWT_PLOT_INTERVAL_CURVE_H #define QWT_PLOT_INTERVAL_CURVE_H #include "qwt_global.h" #include "qwt_plot_seriesitem.h" #include "qwt_series_data.h" class QwtIntervalSymbol; /*! \brief QwtPlotIntervalCurve represents a series of samples, where each value is associated with an interval ( \f$[y1,y2] = f(x)\f$ ). The representation depends on the style() and an optional symbol() that is displayed for each interval. QwtPlotIntervalCurve might be used to display error bars or the area between 2 curves. */ class QWT_EXPORT QwtPlotIntervalCurve: public QwtPlotSeriesItem, public QwtSeriesStore { public: /*! \brief Curve styles. The default setting is QwtPlotIntervalCurve::Tube. \sa setStyle(), style() */ enum CurveStyle { /*! Don't draw a curve. Note: This doesn't affect the symbols. */ NoCurve, /*! Build 2 curves from the upper and lower limits of the intervals and draw them with the pen(). The area between the curves is filled with the brush(). */ Tube, /*! Styles >= QwtPlotIntervalCurve::UserCurve are reserved for derived classes that overload drawSeries() with additional application specific curve types. */ UserCurve = 100 }; /*! Attributes to modify the drawing algorithm. \sa setPaintAttribute(), testPaintAttribute() */ enum PaintAttribute { /*! Clip polygons before painting them. In situations, where points are far outside the visible area (f.e when zooming deep) this might be a substantial improvement for the painting performance. */ ClipPolygons = 0x01, //! Check if a symbol is on the plot canvas before painting it. ClipSymbol = 0x02 }; //! Paint attributes typedef QFlags PaintAttributes; explicit QwtPlotIntervalCurve( const QString &title = QString::null ); explicit QwtPlotIntervalCurve( const QwtText &title ); virtual ~QwtPlotIntervalCurve(); virtual int rtti() const; void setPaintAttribute( PaintAttribute, bool on = true ); bool testPaintAttribute( PaintAttribute ) const; void setSamples( const QVector & ); void setSamples( QwtSeriesData * ); void setPen( const QColor &, qreal width = 0.0, Qt::PenStyle = Qt::SolidLine ); void setPen( const QPen & ); const QPen &pen() const; void setBrush( const QBrush & ); const QBrush &brush() const; void setStyle( CurveStyle style ); CurveStyle style() const; void setSymbol( const QwtIntervalSymbol * ); const QwtIntervalSymbol *symbol() const; virtual void drawSeries( QPainter *p, const QwtScaleMap &xMap, const QwtScaleMap &yMap, const QRectF &canvasRect, int from, int to ) const; virtual QRectF boundingRect() const; virtual QwtGraphic legendIcon( int index, const QSizeF & ) const; protected: void init(); virtual void drawTube( QPainter *, const QwtScaleMap &xMap, const QwtScaleMap &yMap, const QRectF &canvasRect, int from, int to ) const; virtual void drawSymbols( QPainter *, const QwtIntervalSymbol &, const QwtScaleMap &xMap, const QwtScaleMap &yMap, const QRectF &canvasRect, int from, int to ) const; private: class PrivateData; PrivateData *d_data; }; Q_DECLARE_OPERATORS_FOR_FLAGS( QwtPlotIntervalCurve::PaintAttributes ) #endif connectome-workbench-1.2.3+git41-gc4c6c90/src/Qwt/qwt_plot_item.cpp000066400000000000000000000371461300200146000247250ustar00rootroot00000000000000/* -*- mode: C++ ; c-file-style: "stroustrup" -*- ***************************** * Qwt Widget Library * Copyright (C) 1997 Josef Wilgen * Copyright (C) 2002 Uwe Rathmann * * This library is free software; you can redistribute it and/or * modify it under the terms of the Qwt License, Version 1.0 *****************************************************************************/ #include "qwt_plot_item.h" #include "qwt_text.h" #include "qwt_plot.h" #include "qwt_legend_data.h" #include "qwt_scale_div.h" #include "qwt_graphic.h" #include class QwtPlotItem::PrivateData { public: PrivateData(): plot( NULL ), isVisible( true ), attributes( 0 ), interests( 0 ), renderHints( 0 ), renderThreadCount( 1 ), z( 0.0 ), xAxis( QwtPlot::xBottom ), yAxis( QwtPlot::yLeft ), legendIconSize( 8, 8 ) { } mutable QwtPlot *plot; bool isVisible; QwtPlotItem::ItemAttributes attributes; QwtPlotItem::ItemInterests interests; QwtPlotItem::RenderHints renderHints; uint renderThreadCount; double z; int xAxis; int yAxis; QwtText title; QSize legendIconSize; }; /*! Constructor \param title Title of the item */ QwtPlotItem::QwtPlotItem( const QwtText &title ) { d_data = new PrivateData; d_data->title = title; } //! Destroy the QwtPlotItem QwtPlotItem::~QwtPlotItem() { attach( NULL ); delete d_data; } /*! \brief Attach the item to a plot. This method will attach a QwtPlotItem to the QwtPlot argument. It will first detach the QwtPlotItem from any plot from a previous call to attach (if necessary). If a NULL argument is passed, it will detach from any QwtPlot it was attached to. \param plot Plot widget \sa detach() */ void QwtPlotItem::attach( QwtPlot *plot ) { if ( plot == d_data->plot ) return; if ( d_data->plot ) d_data->plot->attachItem( this, false ); d_data->plot = plot; if ( d_data->plot ) d_data->plot->attachItem( this, true ); } /*! \brief This method detaches a QwtPlotItem from any QwtPlot it has been associated with. detach() is equivalent to calling attach( NULL ) \sa attach() */ void QwtPlotItem::detach() { attach( NULL ); } /*! Return rtti for the specific class represented. QwtPlotItem is simply a virtual interface class, and base classes will implement this method with specific rtti values so a user can differentiate them. The rtti value is useful for environments, where the runtime type information is disabled and it is not possible to do a dynamic_cast<...>. \return rtti value \sa RttiValues */ int QwtPlotItem::rtti() const { return Rtti_PlotItem; } //! Return attached plot QwtPlot *QwtPlotItem::plot() const { return d_data->plot; } /*! Plot items are painted in increasing z-order. \return setZ(), QwtPlotDict::itemList() */ double QwtPlotItem::z() const { return d_data->z; } /*! \brief Set the z value Plot items are painted in increasing z-order. \param z Z-value \sa z(), QwtPlotDict::itemList() */ void QwtPlotItem::setZ( double z ) { if ( d_data->z != z ) { if ( d_data->plot ) // update the z order d_data->plot->attachItem( this, false ); d_data->z = z; if ( d_data->plot ) d_data->plot->attachItem( this, true ); itemChanged(); } } /*! Set a new title \param title Title \sa title() */ void QwtPlotItem::setTitle( const QString &title ) { setTitle( QwtText( title ) ); } /*! Set a new title \param title Title \sa title() */ void QwtPlotItem::setTitle( const QwtText &title ) { if ( d_data->title != title ) { d_data->title = title; legendChanged(); #if 0 itemChanged(); #endif } } /*! \return Title of the item \sa setTitle() */ const QwtText &QwtPlotItem::title() const { return d_data->title; } /*! Toggle an item attribute \param attribute Attribute type \param on true/false \sa testItemAttribute(), ItemInterest */ void QwtPlotItem::setItemAttribute( ItemAttribute attribute, bool on ) { if ( d_data->attributes.testFlag( attribute ) != on ) { if ( on ) d_data->attributes |= attribute; else d_data->attributes &= ~attribute; if ( attribute == QwtPlotItem::Legend ) legendChanged(); itemChanged(); } } /*! Test an item attribute \param attribute Attribute type \return true/false \sa setItemAttribute(), ItemInterest */ bool QwtPlotItem::testItemAttribute( ItemAttribute attribute ) const { return d_data->attributes.testFlag( attribute ); } /*! Toggle an item interest \param interest Interest type \param on true/false \sa testItemInterest(), ItemAttribute */ void QwtPlotItem::setItemInterest( ItemInterest interest, bool on ) { if ( d_data->interests.testFlag( interest ) != on ) { if ( on ) d_data->interests |= interest; else d_data->interests &= ~interest; itemChanged(); } } /*! Test an item interest \param interest Interest type \return true/false \sa setItemInterest(), ItemAttribute */ bool QwtPlotItem::testItemInterest( ItemInterest interest ) const { return d_data->interests.testFlag( interest ); } /*! Toggle an render hint \param hint Render hint \param on true/false \sa testRenderHint(), RenderHint */ void QwtPlotItem::setRenderHint( RenderHint hint, bool on ) { if ( d_data->renderHints.testFlag( hint ) != on ) { if ( on ) d_data->renderHints |= hint; else d_data->renderHints &= ~hint; itemChanged(); } } /*! Test a render hint \param hint Render hint \return true/false \sa setRenderHint(), RenderHint */ bool QwtPlotItem::testRenderHint( RenderHint hint ) const { return d_data->renderHints.testFlag( hint ); } /*! On multi core systems rendering of certain plot item ( f.e QwtPlotRasterItem ) can be done in parallel in several threads. The default setting is set to 1. \param numThreads Number of threads to be used for rendering. If numThreads is set to 0, the system specific ideal thread count is used. The default thread count is 1 ( = no additional threads ) */ void QwtPlotItem::setRenderThreadCount( uint numThreads ) { d_data->renderThreadCount = numThreads; } /*! \return Number of threads to be used for rendering. If numThreads() is set to 0, the system specific ideal thread count is used. */ uint QwtPlotItem::renderThreadCount() const { return d_data->renderThreadCount; } /*! Set the size of the legend icon The default setting is 8x8 pixels \param size Size \sa legendIconSize(), legendIcon() */ void QwtPlotItem::setLegendIconSize( const QSize &size ) { if ( d_data->legendIconSize != size ) { d_data->legendIconSize = size; legendChanged(); } } /*! \return Legend icon size \sa setLegendIconSize(), legendIcon() */ QSize QwtPlotItem::legendIconSize() const { return d_data->legendIconSize; } /*! \return Icon representing the item on the legend The default implementation returns an invalid icon \param index Index of the legend entry ( usually there is only one ) \param size Icon size \sa setLegendIconSize(), legendData() */ QwtGraphic QwtPlotItem::legendIcon( int index, const QSizeF &size ) const { Q_UNUSED( index ) Q_UNUSED( size ) return QwtGraphic(); } /*! \brief Return a default icon from a brush The default icon is a filled rectangle used in several derived classes as legendIcon(). \param brush Fill brush \param size Icon size \return A filled rectangle */ QwtGraphic QwtPlotItem::defaultIcon( const QBrush &brush, const QSizeF &size ) const { QwtGraphic icon; if ( !size.isEmpty() ) { icon.setDefaultSize( size ); QRectF r( 0, 0, size.width(), size.height() ); QPainter painter( &icon ); painter.fillRect( r, brush ); } return icon; } //! Show the item void QwtPlotItem::show() { setVisible( true ); } //! Hide the item void QwtPlotItem::hide() { setVisible( false ); } /*! Show/Hide the item \param on Show if true, otherwise hide \sa isVisible(), show(), hide() */ void QwtPlotItem::setVisible( bool on ) { if ( on != d_data->isVisible ) { d_data->isVisible = on; itemChanged(); } } /*! \return true if visible \sa setVisible(), show(), hide() */ bool QwtPlotItem::isVisible() const { return d_data->isVisible; } /*! Update the legend and call QwtPlot::autoRefresh() for the parent plot. \sa QwtPlot::legendChanged(), QwtPlot::autoRefresh() */ void QwtPlotItem::itemChanged() { if ( d_data->plot ) d_data->plot->autoRefresh(); } /*! Update the legend of the parent plot. \sa QwtPlot::updateLegend(), itemChanged() */ void QwtPlotItem::legendChanged() { if ( testItemAttribute( QwtPlotItem::Legend ) && d_data->plot ) d_data->plot->updateLegend( this ); } /*! Set X and Y axis The item will painted according to the coordinates of its Axes. \param xAxis X Axis ( QwtPlot::xBottom or QwtPlot::xTop ) \param yAxis Y Axis ( QwtPlot::yLeft or QwtPlot::yRight ) \sa setXAxis(), setYAxis(), xAxis(), yAxis(), QwtPlot::Axis */ void QwtPlotItem::setAxes( int xAxis, int yAxis ) { if ( xAxis == QwtPlot::xBottom || xAxis == QwtPlot::xTop ) d_data->xAxis = xAxis; if ( yAxis == QwtPlot::yLeft || yAxis == QwtPlot::yRight ) d_data->yAxis = yAxis; itemChanged(); } /*! Set the X axis The item will painted according to the coordinates its Axes. \param axis X Axis ( QwtPlot::xBottom or QwtPlot::xTop ) \sa setAxes(), setYAxis(), xAxis(), QwtPlot::Axis */ void QwtPlotItem::setXAxis( int axis ) { if ( axis == QwtPlot::xBottom || axis == QwtPlot::xTop ) { d_data->xAxis = axis; itemChanged(); } } /*! Set the Y axis The item will painted according to the coordinates its Axes. \param axis Y Axis ( QwtPlot::yLeft or QwtPlot::yRight ) \sa setAxes(), setXAxis(), yAxis(), QwtPlot::Axis */ void QwtPlotItem::setYAxis( int axis ) { if ( axis == QwtPlot::yLeft || axis == QwtPlot::yRight ) { d_data->yAxis = axis; itemChanged(); } } //! Return xAxis int QwtPlotItem::xAxis() const { return d_data->xAxis; } //! Return yAxis int QwtPlotItem::yAxis() const { return d_data->yAxis; } /*! \return An invalid bounding rect: QRectF(1.0, 1.0, -2.0, -2.0) \note A width or height < 0.0 is ignored by the autoscaler */ QRectF QwtPlotItem::boundingRect() const { return QRectF( 1.0, 1.0, -2.0, -2.0 ); // invalid } /*! \brief Calculate a hint for the canvas margin When the QwtPlotItem::Margins flag is enabled the plot item indicates, that it needs some margins at the borders of the canvas. This is f.e. used by bar charts to reserve space for displaying the bars. The margins are in target device coordinates ( pixels on screen ) \param xMap Maps x-values into pixel coordinates. \param yMap Maps y-values into pixel coordinates. \param canvasRect Contents rectangle of the canvas in painter coordinates \param left Returns the left margin \param top Returns the top margin \param right Returns the right margin \param bottom Returns the bottom margin \return The default implementation returns 0 for all margins \sa QwtPlot::getCanvasMarginsHint(), QwtPlot::updateCanvasMargins() */ void QwtPlotItem::getCanvasMarginHint( const QwtScaleMap &xMap, const QwtScaleMap &yMap, const QRectF &canvasRect, double &left, double &top, double &right, double &bottom ) const { Q_UNUSED( xMap ); Q_UNUSED( yMap ); Q_UNUSED( canvasRect ); // use QMargins, when we don't need to support Qt < 4.6 anymore left = top = right = bottom = 0.0; } /*! \brief Return all information, that is needed to represent the item on the legend Most items are represented by one entry on the legend showing an icon and a text, but f.e. QwtPlotMultiBarChart displays one entry for each bar. QwtLegendData is basically a list of QVariants that makes it possible to overload and reimplement legendData() to return almost any type of information, that is understood by the receiver that acts as the legend. The default implementation returns one entry with the title() of the item and the legendIcon(). \return Data, that is needed to represent the item on the legend \sa title(), legendIcon(), QwtLegend, QwtPlotLegendItem */ QList QwtPlotItem::legendData() const { QwtLegendData data; QwtText label = title(); label.setRenderFlags( label.renderFlags() & Qt::AlignLeft ); QVariant titleValue; qVariantSetValue( titleValue, label ); data.setValue( QwtLegendData::TitleRole, titleValue ); const QwtGraphic graphic = legendIcon( 0, legendIconSize() ); if ( !graphic.isNull() ) { QVariant iconValue; qVariantSetValue( iconValue, graphic ); data.setValue( QwtLegendData::IconRole, iconValue ); } QList list; list += data; return list; } /*! \brief Update the item to changes of the axes scale division Update the item, when the axes of plot have changed. The default implementation does nothing, but items that depend on the scale division (like QwtPlotGrid()) have to reimplement updateScaleDiv() updateScaleDiv() is only called when the ScaleInterest interest is enabled. The default implementation does nothing. \param xScaleDiv Scale division of the x-axis \param yScaleDiv Scale division of the y-axis \sa QwtPlot::updateAxes(), ScaleInterest */ void QwtPlotItem::updateScaleDiv( const QwtScaleDiv &xScaleDiv, const QwtScaleDiv &yScaleDiv ) { Q_UNUSED( xScaleDiv ); Q_UNUSED( yScaleDiv ); } /*! \brief Update the item to changes of the legend info Plot items that want to display a legend ( not those, that want to be displayed on a legend ! ) will have to implement updateLegend(). updateLegend() is only called when the LegendInterest interest is enabled. The default implementation does nothing. \param item Plot item to be displayed on a legend \param data Attributes how to display item on the legend \sa QwtPlotLegendItem \note Plot items, that want to be displayed on a legend need to enable the QwtPlotItem::Legend flag and to implement legendData() and legendIcon() */ void QwtPlotItem::updateLegend( const QwtPlotItem *item, const QList &data ) { Q_UNUSED( item ); Q_UNUSED( data ); } /*! \brief Calculate the bounding scale rectangle of 2 maps \param xMap Maps x-values into pixel coordinates. \param yMap Maps y-values into pixel coordinates. \return Bounding scale rect of the scale maps, not normalized */ QRectF QwtPlotItem::scaleRect( const QwtScaleMap &xMap, const QwtScaleMap &yMap ) const { return QRectF( xMap.s1(), yMap.s1(), xMap.sDist(), yMap.sDist() ); } /*! \brief Calculate the bounding paint rectangle of 2 maps \param xMap Maps x-values into pixel coordinates. \param yMap Maps y-values into pixel coordinates. \return Bounding paint rectangle of the scale maps, not normalized */ QRectF QwtPlotItem::paintRect( const QwtScaleMap &xMap, const QwtScaleMap &yMap ) const { const QRectF rect( xMap.p1(), yMap.p1(), xMap.pDist(), yMap.pDist() ); return rect; } connectome-workbench-1.2.3+git41-gc4c6c90/src/Qwt/qwt_plot_item.h000066400000000000000000000202331300200146000243570ustar00rootroot00000000000000/* -*- mode: C++ ; c-file-style: "stroustrup" -*- ***************************** * Qwt Widget Library * Copyright (C) 1997 Josef Wilgen * Copyright (C) 2002 Uwe Rathmann * * This library is free software; you can redistribute it and/or * modify it under the terms of the Qwt License, Version 1.0 *****************************************************************************/ #ifndef QWT_PLOT_ITEM_H #define QWT_PLOT_ITEM_H #include "qwt_global.h" #include "qwt_text.h" #include "qwt_legend_data.h" #include "qwt_graphic.h" #include #include #include class QPainter; class QwtScaleMap; class QwtScaleDiv; class QwtPlot; /*! \brief Base class for items on the plot canvas A plot item is "something", that can be painted on the plot canvas, or only affects the scales of the plot widget. They can be categorized as: - Representator\n A "Representator" is an item that represents some sort of data on the plot canvas. The different representator classes are organized according to the characteristics of the data: - QwtPlotMarker Represents a point or a horizontal/vertical coordinate - QwtPlotCurve Represents a series of points - QwtPlotSpectrogram ( QwtPlotRasterItem ) Represents raster data - ... - Decorators\n A "Decorator" is an item, that displays additional information, that is not related to any data: - QwtPlotGrid - QwtPlotScaleItem - QwtPlotSvgItem - ... Depending on the QwtPlotItem::ItemAttribute flags, an item is included into autoscaling or has an entry on the legend. Before misusing the existing item classes it might be better to implement a new type of plot item ( don't implement a watermark as spectrogram ). Deriving a new type of QwtPlotItem primarily means to implement the YourPlotItem::draw() method. \sa The cpuplot example shows the implementation of additional plot items. */ class QWT_EXPORT QwtPlotItem { public: /*! \brief Runtime type information RttiValues is used to cast plot items, without having to enable runtime type information of the compiler. */ enum RttiValues { //! Unspecific value, that can be used, when it doesn't matter Rtti_PlotItem = 0, //! For QwtPlotGrid Rtti_PlotGrid, //! For QwtPlotScaleItem Rtti_PlotScale, //! For QwtPlotLegendItem Rtti_PlotLegend, //! For QwtPlotMarker Rtti_PlotMarker, //! For QwtPlotCurve Rtti_PlotCurve, //! For QwtPlotSpectroCurve Rtti_PlotSpectroCurve, //! For QwtPlotIntervalCurve Rtti_PlotIntervalCurve, //! For QwtPlotHistogram Rtti_PlotHistogram, //! For QwtPlotSpectrogram Rtti_PlotSpectrogram, //! For QwtPlotSvgItem Rtti_PlotSVG, //! For QwtPlotTradingCurve Rtti_PlotTradingCurve, //! For QwtPlotBarChart Rtti_PlotBarChart, //! For QwtPlotMultiBarChart Rtti_PlotMultiBarChart, //! For QwtPlotShapeItem Rtti_PlotShape, //! For QwtPlotTextLabel Rtti_PlotTextLabel, //! For QwtPlotZoneItem Rtti_PlotZone, /*! Values >= Rtti_PlotUserItem are reserved for plot items not implemented in the Qwt library. */ Rtti_PlotUserItem = 1000 }; /*! \brief Plot Item Attributes Various aspects of a plot widget depend on the attributes of the attached plot items. If and how a single plot item participates in these updates depends on its attributes. \sa setItemAttribute(), testItemAttribute(), ItemInterest */ enum ItemAttribute { //! The item is represented on the legend. Legend = 0x01, /*! The boundingRect() of the item is included in the autoscaling calculation as long as its width or height is >= 0.0. */ AutoScale = 0x02, /*! The item needs extra space to display something outside its bounding rectangle. \sa getCanvasMarginHint() */ Margins = 0x04 }; //! Plot Item Attributes typedef QFlags ItemAttributes; /*! \brief Plot Item Interests Plot items might depend on the situation of the corresponding plot widget. By enabling an interest the plot item will be notified, when the corresponding attribute of the plot widgets has changed. \sa setItemAttribute(), testItemAttribute(), ItemInterest */ enum ItemInterest { /*! The item is interested in updates of the scales \sa updateScaleDiv() */ ScaleInterest = 0x01, /*! The item is interested in updates of the legend ( of other items ) This flag is intended for items, that want to implement a legend for displaying entries of other plot item. \note If the plot item wants to be represented on a legend enable QwtPlotItem::Legend instead. \sa updateLegend() */ LegendInterest = 0x02 }; //! Plot Item Interests typedef QFlags ItemInterests; //! Render hints enum RenderHint { //! Enable antialiasing RenderAntialiased = 0x1 }; //! Render hints typedef QFlags RenderHints; explicit QwtPlotItem( const QwtText &title = QwtText() ); virtual ~QwtPlotItem(); void attach( QwtPlot *plot ); void detach(); QwtPlot *plot() const; void setTitle( const QString &title ); void setTitle( const QwtText &title ); const QwtText &title() const; virtual int rtti() const; void setItemAttribute( ItemAttribute, bool on = true ); bool testItemAttribute( ItemAttribute ) const; void setItemInterest( ItemInterest, bool on = true ); bool testItemInterest( ItemInterest ) const; void setRenderHint( RenderHint, bool on = true ); bool testRenderHint( RenderHint ) const; void setRenderThreadCount( uint numThreads ); uint renderThreadCount() const; void setLegendIconSize( const QSize & ); QSize legendIconSize() const; double z() const; void setZ( double z ); void show(); void hide(); virtual void setVisible( bool ); bool isVisible () const; void setAxes( int xAxis, int yAxis ); void setXAxis( int axis ); int xAxis() const; void setYAxis( int axis ); int yAxis() const; virtual void itemChanged(); virtual void legendChanged(); /*! \brief Draw the item \param painter Painter \param xMap Maps x-values into pixel coordinates. \param yMap Maps y-values into pixel coordinates. \param canvasRect Contents rect of the canvas in painter coordinates */ virtual void draw( QPainter *painter, const QwtScaleMap &xMap, const QwtScaleMap &yMap, const QRectF &canvasRect ) const = 0; virtual QRectF boundingRect() const; virtual void getCanvasMarginHint( const QwtScaleMap &xMap, const QwtScaleMap &yMap, const QRectF &canvasSize, double &left, double &top, double &right, double &bottom) const; virtual void updateScaleDiv( const QwtScaleDiv&, const QwtScaleDiv& ); virtual void updateLegend( const QwtPlotItem *, const QList & ); QRectF scaleRect( const QwtScaleMap &, const QwtScaleMap & ) const; QRectF paintRect( const QwtScaleMap &, const QwtScaleMap & ) const; virtual QList legendData() const; virtual QwtGraphic legendIcon( int index, const QSizeF & ) const; protected: QwtGraphic defaultIcon( const QBrush &, const QSizeF & ) const; private: // Disabled copy constructor and operator= QwtPlotItem( const QwtPlotItem & ); QwtPlotItem &operator=( const QwtPlotItem & ); class PrivateData; PrivateData *d_data; }; Q_DECLARE_OPERATORS_FOR_FLAGS( QwtPlotItem::ItemAttributes ) Q_DECLARE_OPERATORS_FOR_FLAGS( QwtPlotItem::ItemInterests ) Q_DECLARE_OPERATORS_FOR_FLAGS( QwtPlotItem::RenderHints ) Q_DECLARE_METATYPE( QwtPlotItem * ) #endif connectome-workbench-1.2.3+git41-gc4c6c90/src/Qwt/qwt_plot_layout.cpp000066400000000000000000001256151300200146000253030ustar00rootroot00000000000000/* -*- mode: C++ ; c-file-style: "stroustrup" -*- ***************************** * Qwt Widget Library * Copyright (C) 1997 Josef Wilgen * Copyright (C) 2002 Uwe Rathmann * * This library is free software; you can redistribute it and/or * modify it under the terms of the Qwt License, Version 1.0 *****************************************************************************/ #include "qwt_plot_layout.h" #include "qwt_text.h" #include "qwt_text_label.h" #include "qwt_scale_widget.h" #include "qwt_abstract_legend.h" #include #include class QwtPlotLayout::LayoutData { public: void init( const QwtPlot *, const QRectF &rect ); struct t_legendData { int frameWidth; int hScrollExtent; int vScrollExtent; QSize hint; } legend; struct t_titleData { QwtText text; int frameWidth; } title; struct t_footerData { QwtText text; int frameWidth; } footer; struct t_scaleData { bool isEnabled; const QwtScaleWidget *scaleWidget; QFont scaleFont; int start; int end; int baseLineOffset; double tickOffset; int dimWithoutTitle; } scale[QwtPlot::axisCnt]; struct t_canvasData { int contentsMargins[ QwtPlot::axisCnt ]; } canvas; }; /* Extract all layout relevant data from the plot components */ void QwtPlotLayout::LayoutData::init( const QwtPlot *plot, const QRectF &rect ) { // legend if ( plot->legend() ) { legend.frameWidth = plot->legend()->frameWidth(); legend.hScrollExtent = plot->legend()->scrollExtent( Qt::Horizontal ); legend.vScrollExtent = plot->legend()->scrollExtent( Qt::Vertical ); const QSize hint = plot->legend()->sizeHint(); int w = qMin( hint.width(), qFloor( rect.width() ) ); int h = plot->legend()->heightForWidth( w ); if ( h <= 0 ) h = hint.height(); if ( h > rect.height() ) w += legend.hScrollExtent; legend.hint = QSize( w, h ); } // title title.frameWidth = 0; title.text = QwtText(); if ( plot->titleLabel() ) { const QwtTextLabel *label = plot->titleLabel(); title.text = label->text(); if ( !( title.text.testPaintAttribute( QwtText::PaintUsingTextFont ) ) ) title.text.setFont( label->font() ); title.frameWidth = plot->titleLabel()->frameWidth(); } // footer footer.frameWidth = 0; footer.text = QwtText(); if ( plot->footerLabel() ) { const QwtTextLabel *label = plot->footerLabel(); footer.text = label->text(); if ( !( footer.text.testPaintAttribute( QwtText::PaintUsingTextFont ) ) ) footer.text.setFont( label->font() ); footer.frameWidth = plot->footerLabel()->frameWidth(); } // scales for ( int axis = 0; axis < QwtPlot::axisCnt; axis++ ) { if ( plot->axisEnabled( axis ) ) { const QwtScaleWidget *scaleWidget = plot->axisWidget( axis ); scale[axis].isEnabled = true; scale[axis].scaleWidget = scaleWidget; scale[axis].scaleFont = scaleWidget->font(); scale[axis].start = scaleWidget->startBorderDist(); scale[axis].end = scaleWidget->endBorderDist(); scale[axis].baseLineOffset = scaleWidget->margin(); scale[axis].tickOffset = scaleWidget->margin(); if ( scaleWidget->scaleDraw()->hasComponent( QwtAbstractScaleDraw::Ticks ) ) { scale[axis].tickOffset += scaleWidget->scaleDraw()->maxTickLength(); } scale[axis].dimWithoutTitle = scaleWidget->dimForLength( QWIDGETSIZE_MAX, scale[axis].scaleFont ); if ( !scaleWidget->title().isEmpty() ) { scale[axis].dimWithoutTitle -= scaleWidget->titleHeightForWidth( QWIDGETSIZE_MAX ); } } else { scale[axis].isEnabled = false; scale[axis].start = 0; scale[axis].end = 0; scale[axis].baseLineOffset = 0; scale[axis].tickOffset = 0.0; scale[axis].dimWithoutTitle = 0; } } // canvas plot->canvas()->getContentsMargins( &canvas.contentsMargins[ QwtPlot::yLeft ], &canvas.contentsMargins[ QwtPlot::xTop ], &canvas.contentsMargins[ QwtPlot::yRight ], &canvas.contentsMargins[ QwtPlot::xBottom ] ); } class QwtPlotLayout::PrivateData { public: PrivateData(): spacing( 5 ) { } QRectF titleRect; QRectF footerRect; QRectF legendRect; QRectF scaleRect[QwtPlot::axisCnt]; QRectF canvasRect; QwtPlotLayout::LayoutData layoutData; QwtPlot::LegendPosition legendPos; double legendRatio; unsigned int spacing; unsigned int canvasMargin[QwtPlot::axisCnt]; bool alignCanvasToScales[QwtPlot::axisCnt]; }; /*! \brief Constructor */ QwtPlotLayout::QwtPlotLayout() { d_data = new PrivateData; setLegendPosition( QwtPlot::BottomLegend ); setCanvasMargin( 4 ); setAlignCanvasToScales( false ); invalidate(); } //! Destructor QwtPlotLayout::~QwtPlotLayout() { delete d_data; } /*! Change a margin of the canvas. The margin is the space above/below the scale ticks. A negative margin will be set to -1, excluding the borders of the scales. \param margin New margin \param axis One of QwtPlot::Axis. Specifies where the position of the margin. -1 means margin at all borders. \sa canvasMargin() \warning The margin will have no effect when alignCanvasToScale() is true */ void QwtPlotLayout::setCanvasMargin( int margin, int axis ) { if ( margin < -1 ) margin = -1; if ( axis == -1 ) { for ( axis = 0; axis < QwtPlot::axisCnt; axis++ ) d_data->canvasMargin[axis] = margin; } else if ( axis >= 0 && axis < QwtPlot::axisCnt ) d_data->canvasMargin[axis] = margin; } /*! \param axisId Axis index \return Margin around the scale tick borders \sa setCanvasMargin() */ int QwtPlotLayout::canvasMargin( int axisId ) const { if ( axisId < 0 || axisId >= QwtPlot::axisCnt ) return 0; return d_data->canvasMargin[axisId]; } /*! \brief Set the align-canvas-to-axis-scales flag for all axes \param on True/False \sa setAlignCanvasToScale(), alignCanvasToScale() */ void QwtPlotLayout::setAlignCanvasToScales( bool on ) { for ( int axis = 0; axis < QwtPlot::axisCnt; axis++ ) d_data->alignCanvasToScales[axis] = on; } /*! Change the align-canvas-to-axis-scales setting. The canvas may: - extend beyond the axis scale ends to maximize its size, - align with the axis scale ends to control its size. The axisId parameter is somehow confusing as it identifies a border of the plot and not the axes, that are aligned. F.e when QwtPlot::yLeft is set, the left end of the the x-axes ( QwtPlot::xTop, QwtPlot::xBottom ) is aligned. \param axisId Axis index \param on New align-canvas-to-axis-scales setting \sa setCanvasMargin(), alignCanvasToScale(), setAlignCanvasToScales() \warning In case of on == true canvasMargin() will have no effect */ void QwtPlotLayout::setAlignCanvasToScale( int axisId, bool on ) { if ( axisId >= 0 && axisId < QwtPlot::axisCnt ) d_data->alignCanvasToScales[axisId] = on; } /*! Return the align-canvas-to-axis-scales setting. The canvas may: - extend beyond the axis scale ends to maximize its size - align with the axis scale ends to control its size. \param axisId Axis index \return align-canvas-to-axis-scales setting \sa setAlignCanvasToScale(), setAlignCanvasToScale(), setCanvasMargin() */ bool QwtPlotLayout::alignCanvasToScale( int axisId ) const { if ( axisId < 0 || axisId >= QwtPlot::axisCnt ) return false; return d_data->alignCanvasToScales[ axisId ]; } /*! Change the spacing of the plot. The spacing is the distance between the plot components. \param spacing New spacing \sa setCanvasMargin(), spacing() */ void QwtPlotLayout::setSpacing( int spacing ) { d_data->spacing = qMax( 0, spacing ); } /*! \return Spacing \sa margin(), setSpacing() */ int QwtPlotLayout::spacing() const { return d_data->spacing; } /*! \brief Specify the position of the legend \param pos The legend's position. \param ratio Ratio between legend and the bounding rectangle of title, footer, canvas and axes. The legend will be shrunk if it would need more space than the given ratio. The ratio is limited to ]0.0 .. 1.0]. In case of <= 0.0 it will be reset to the default ratio. The default vertical/horizontal ratio is 0.33/0.5. \sa QwtPlot::setLegendPosition() */ void QwtPlotLayout::setLegendPosition( QwtPlot::LegendPosition pos, double ratio ) { if ( ratio > 1.0 ) ratio = 1.0; switch ( pos ) { case QwtPlot::TopLegend: case QwtPlot::BottomLegend: if ( ratio <= 0.0 ) ratio = 0.33; d_data->legendRatio = ratio; d_data->legendPos = pos; break; case QwtPlot::LeftLegend: case QwtPlot::RightLegend: if ( ratio <= 0.0 ) ratio = 0.5; d_data->legendRatio = ratio; d_data->legendPos = pos; break; default: break; } } /*! \brief Specify the position of the legend \param pos The legend's position. Valid values are \c QwtPlot::LeftLegend, \c QwtPlot::RightLegend, \c QwtPlot::TopLegend, \c QwtPlot::BottomLegend. \sa QwtPlot::setLegendPosition() */ void QwtPlotLayout::setLegendPosition( QwtPlot::LegendPosition pos ) { setLegendPosition( pos, 0.0 ); } /*! \return Position of the legend \sa setLegendPosition(), QwtPlot::setLegendPosition(), QwtPlot::legendPosition() */ QwtPlot::LegendPosition QwtPlotLayout::legendPosition() const { return d_data->legendPos; } /*! Specify the relative size of the legend in the plot \param ratio Ratio between legend and the bounding rectangle of title, footer, canvas and axes. The legend will be shrunk if it would need more space than the given ratio. The ratio is limited to ]0.0 .. 1.0]. In case of <= 0.0 it will be reset to the default ratio. The default vertical/horizontal ratio is 0.33/0.5. */ void QwtPlotLayout::setLegendRatio( double ratio ) { setLegendPosition( legendPosition(), ratio ); } /*! \return The relative size of the legend in the plot. \sa setLegendPosition() */ double QwtPlotLayout::legendRatio() const { return d_data->legendRatio; } /*! \brief Set the geometry for the title This method is intended to be used from derived layouts overloading activate() \sa titleRect(), activate() */ void QwtPlotLayout::setTitleRect( const QRectF &rect ) { d_data->titleRect = rect; } /*! \return Geometry for the title \sa activate(), invalidate() */ QRectF QwtPlotLayout::titleRect() const { return d_data->titleRect; } /*! \brief Set the geometry for the footer This method is intended to be used from derived layouts overloading activate() \sa footerRect(), activate() */ void QwtPlotLayout::setFooterRect( const QRectF &rect ) { d_data->footerRect = rect; } /*! \return Geometry for the footer \sa activate(), invalidate() */ QRectF QwtPlotLayout::footerRect() const { return d_data->footerRect; } /*! \brief Set the geometry for the legend This method is intended to be used from derived layouts overloading activate() \param rect Rectangle for the legend \sa legendRect(), activate() */ void QwtPlotLayout::setLegendRect( const QRectF &rect ) { d_data->legendRect = rect; } /*! \return Geometry for the legend \sa activate(), invalidate() */ QRectF QwtPlotLayout::legendRect() const { return d_data->legendRect; } /*! \brief Set the geometry for an axis This method is intended to be used from derived layouts overloading activate() \param axis Axis index \param rect Rectangle for the scale \sa scaleRect(), activate() */ void QwtPlotLayout::setScaleRect( int axis, const QRectF &rect ) { if ( axis >= 0 && axis < QwtPlot::axisCnt ) d_data->scaleRect[axis] = rect; } /*! \param axis Axis index \return Geometry for the scale \sa activate(), invalidate() */ QRectF QwtPlotLayout::scaleRect( int axis ) const { if ( axis < 0 || axis >= QwtPlot::axisCnt ) { static QRectF dummyRect; return dummyRect; } return d_data->scaleRect[axis]; } /*! \brief Set the geometry for the canvas This method is intended to be used from derived layouts overloading activate() \sa canvasRect(), activate() */ void QwtPlotLayout::setCanvasRect( const QRectF &rect ) { d_data->canvasRect = rect; } /*! \return Geometry for the canvas \sa activate(), invalidate() */ QRectF QwtPlotLayout::canvasRect() const { return d_data->canvasRect; } /*! Invalidate the geometry of all components. \sa activate() */ void QwtPlotLayout::invalidate() { d_data->titleRect = d_data->footerRect = d_data->legendRect = d_data->canvasRect = QRect(); for ( int axis = 0; axis < QwtPlot::axisCnt; axis++ ) d_data->scaleRect[axis] = QRect(); } /*! \return Minimum size hint \param plot Plot widget \sa QwtPlot::minimumSizeHint() */ QSize QwtPlotLayout::minimumSizeHint( const QwtPlot *plot ) const { class ScaleData { public: ScaleData() { w = h = minLeft = minRight = tickOffset = 0; } int w; int h; int minLeft; int minRight; int tickOffset; } scaleData[QwtPlot::axisCnt]; int canvasBorder[QwtPlot::axisCnt]; int fw; plot->canvas()->getContentsMargins( &fw, NULL, NULL, NULL ); int axis; for ( axis = 0; axis < QwtPlot::axisCnt; axis++ ) { if ( plot->axisEnabled( axis ) ) { const QwtScaleWidget *scl = plot->axisWidget( axis ); ScaleData &sd = scaleData[axis]; const QSize hint = scl->minimumSizeHint(); sd.w = hint.width(); sd.h = hint.height(); scl->getBorderDistHint( sd.minLeft, sd.minRight ); sd.tickOffset = scl->margin(); if ( scl->scaleDraw()->hasComponent( QwtAbstractScaleDraw::Ticks ) ) sd.tickOffset += qCeil( scl->scaleDraw()->maxTickLength() ); } canvasBorder[axis] = fw + d_data->canvasMargin[axis] + 1; } for ( axis = 0; axis < QwtPlot::axisCnt; axis++ ) { ScaleData &sd = scaleData[axis]; if ( sd.w && ( axis == QwtPlot::xBottom || axis == QwtPlot::xTop ) ) { if ( ( sd.minLeft > canvasBorder[QwtPlot::yLeft] ) && scaleData[QwtPlot::yLeft].w ) { int shiftLeft = sd.minLeft - canvasBorder[QwtPlot::yLeft]; if ( shiftLeft > scaleData[QwtPlot::yLeft].w ) shiftLeft = scaleData[QwtPlot::yLeft].w; sd.w -= shiftLeft; } if ( ( sd.minRight > canvasBorder[QwtPlot::yRight] ) && scaleData[QwtPlot::yRight].w ) { int shiftRight = sd.minRight - canvasBorder[QwtPlot::yRight]; if ( shiftRight > scaleData[QwtPlot::yRight].w ) shiftRight = scaleData[QwtPlot::yRight].w; sd.w -= shiftRight; } } if ( sd.h && ( axis == QwtPlot::yLeft || axis == QwtPlot::yRight ) ) { if ( ( sd.minLeft > canvasBorder[QwtPlot::xBottom] ) && scaleData[QwtPlot::xBottom].h ) { int shiftBottom = sd.minLeft - canvasBorder[QwtPlot::xBottom]; if ( shiftBottom > scaleData[QwtPlot::xBottom].tickOffset ) shiftBottom = scaleData[QwtPlot::xBottom].tickOffset; sd.h -= shiftBottom; } if ( ( sd.minLeft > canvasBorder[QwtPlot::xTop] ) && scaleData[QwtPlot::xTop].h ) { int shiftTop = sd.minRight - canvasBorder[QwtPlot::xTop]; if ( shiftTop > scaleData[QwtPlot::xTop].tickOffset ) shiftTop = scaleData[QwtPlot::xTop].tickOffset; sd.h -= shiftTop; } } } const QWidget *canvas = plot->canvas(); int left, top, right, bottom; canvas->getContentsMargins( &left, &top, &right, &bottom ); const QSize minCanvasSize = canvas->minimumSize(); int w = scaleData[QwtPlot::yLeft].w + scaleData[QwtPlot::yRight].w; int cw = qMax( scaleData[QwtPlot::xBottom].w, scaleData[QwtPlot::xTop].w ) + left + 1 + right + 1; w += qMax( cw, minCanvasSize.width() ); int h = scaleData[QwtPlot::xBottom].h + scaleData[QwtPlot::xTop].h; int ch = qMax( scaleData[QwtPlot::yLeft].h, scaleData[QwtPlot::yRight].h ) + top + 1 + bottom + 1; h += qMax( ch, minCanvasSize.height() ); const QwtTextLabel *labels[2]; labels[0] = plot->titleLabel(); labels[1] = plot->footerLabel(); for ( int i = 0; i < 2; i++ ) { const QwtTextLabel *label = labels[i]; if ( label && !label->text().isEmpty() ) { // If only QwtPlot::yLeft or QwtPlot::yRight is showing, // we center on the plot canvas. const bool centerOnCanvas = !( plot->axisEnabled( QwtPlot::yLeft ) && plot->axisEnabled( QwtPlot::yRight ) ); int labelW = w; if ( centerOnCanvas ) { labelW -= scaleData[QwtPlot::yLeft].w + scaleData[QwtPlot::yRight].w; } int labelH = label->heightForWidth( labelW ); if ( labelH > labelW ) // Compensate for a long title { w = labelW = labelH; if ( centerOnCanvas ) { w += scaleData[QwtPlot::yLeft].w + scaleData[QwtPlot::yRight].w; } labelH = label->heightForWidth( labelW ); } h += labelH + d_data->spacing; } } // Compute the legend contribution const QwtAbstractLegend *legend = plot->legend(); if ( legend && !legend->isEmpty() ) { if ( d_data->legendPos == QwtPlot::LeftLegend || d_data->legendPos == QwtPlot::RightLegend ) { int legendW = legend->sizeHint().width(); int legendH = legend->heightForWidth( legendW ); if ( legend->frameWidth() > 0 ) w += d_data->spacing; if ( legendH > h ) legendW += legend->scrollExtent( Qt::Horizontal ); if ( d_data->legendRatio < 1.0 ) legendW = qMin( legendW, int( w / ( 1.0 - d_data->legendRatio ) ) ); w += legendW + d_data->spacing; } else // QwtPlot::Top, QwtPlot::Bottom { int legendW = qMin( legend->sizeHint().width(), w ); int legendH = legend->heightForWidth( legendW ); if ( legend->frameWidth() > 0 ) h += d_data->spacing; if ( d_data->legendRatio < 1.0 ) legendH = qMin( legendH, int( h / ( 1.0 - d_data->legendRatio ) ) ); h += legendH + d_data->spacing; } } return QSize( w, h ); } /*! Find the geometry for the legend \param options Options how to layout the legend \param rect Rectangle where to place the legend \return Geometry for the legend \sa Options */ QRectF QwtPlotLayout::layoutLegend( Options options, const QRectF &rect ) const { const QSize hint( d_data->layoutData.legend.hint ); int dim; if ( d_data->legendPos == QwtPlot::LeftLegend || d_data->legendPos == QwtPlot::RightLegend ) { // We don't allow vertical legends to take more than // half of the available space. dim = qMin( hint.width(), int( rect.width() * d_data->legendRatio ) ); if ( !( options & IgnoreScrollbars ) ) { if ( hint.height() > rect.height() ) { // The legend will need additional // space for the vertical scrollbar. dim += d_data->layoutData.legend.hScrollExtent; } } } else { dim = qMin( hint.height(), int( rect.height() * d_data->legendRatio ) ); dim = qMax( dim, d_data->layoutData.legend.vScrollExtent ); } QRectF legendRect = rect; switch ( d_data->legendPos ) { case QwtPlot::LeftLegend: legendRect.setWidth( dim ); break; case QwtPlot::RightLegend: legendRect.setX( rect.right() - dim ); legendRect.setWidth( dim ); break; case QwtPlot::TopLegend: legendRect.setHeight( dim ); break; case QwtPlot::BottomLegend: legendRect.setY( rect.bottom() - dim ); legendRect.setHeight( dim ); break; } return legendRect; } /*! Align the legend to the canvas \param canvasRect Geometry of the canvas \param legendRect Maximum geometry for the legend \return Geometry for the aligned legend */ QRectF QwtPlotLayout::alignLegend( const QRectF &canvasRect, const QRectF &legendRect ) const { QRectF alignedRect = legendRect; if ( d_data->legendPos == QwtPlot::BottomLegend || d_data->legendPos == QwtPlot::TopLegend ) { if ( d_data->layoutData.legend.hint.width() < canvasRect.width() ) { alignedRect.setX( canvasRect.x() ); alignedRect.setWidth( canvasRect.width() ); } } else { if ( d_data->layoutData.legend.hint.height() < canvasRect.height() ) { alignedRect.setY( canvasRect.y() ); alignedRect.setHeight( canvasRect.height() ); } } return alignedRect; } /*! Expand all line breaks in text labels, and calculate the height of their widgets in orientation of the text. \param options Options how to layout the legend \param rect Bounding rectangle for title, footer, axes and canvas. \param dimTitle Expanded height of the title widget \param dimFooter Expanded height of the footer widget \param dimAxis Expanded heights of the axis in axis orientation. \sa Options */ void QwtPlotLayout::expandLineBreaks( Options options, const QRectF &rect, int &dimTitle, int &dimFooter, int dimAxis[QwtPlot::axisCnt] ) const { dimTitle = dimFooter = 0; for ( int axis = 0; axis < QwtPlot::axisCnt; axis++ ) dimAxis[axis] = 0; int backboneOffset[QwtPlot::axisCnt]; for ( int axis = 0; axis < QwtPlot::axisCnt; axis++ ) { backboneOffset[axis] = 0; if ( !( options & IgnoreFrames ) ) backboneOffset[axis] += d_data->layoutData.canvas.contentsMargins[ axis ]; if ( !d_data->alignCanvasToScales[axis] ) backboneOffset[axis] += d_data->canvasMargin[axis]; } bool done = false; while ( !done ) { done = true; // the size for the 4 axis depend on each other. Expanding // the height of a horizontal axis will shrink the height // for the vertical axis, shrinking the height of a vertical // axis will result in a line break what will expand the // width and results in shrinking the width of a horizontal // axis what might result in a line break of a horizontal // axis ... . So we loop as long until no size changes. if ( !( ( options & IgnoreTitle ) || d_data->layoutData.title.text.isEmpty() ) ) { double w = rect.width(); if ( d_data->layoutData.scale[QwtPlot::yLeft].isEnabled != d_data->layoutData.scale[QwtPlot::yRight].isEnabled ) { // center to the canvas w -= dimAxis[QwtPlot::yLeft] + dimAxis[QwtPlot::yRight]; } int d = qCeil( d_data->layoutData.title.text.heightForWidth( w ) ); if ( !( options & IgnoreFrames ) ) d += 2 * d_data->layoutData.title.frameWidth; if ( d > dimTitle ) { dimTitle = d; done = false; } } if ( !( ( options & IgnoreFooter ) || d_data->layoutData.footer.text.isEmpty() ) ) { double w = rect.width(); if ( d_data->layoutData.scale[QwtPlot::yLeft].isEnabled != d_data->layoutData.scale[QwtPlot::yRight].isEnabled ) { // center to the canvas w -= dimAxis[QwtPlot::yLeft] + dimAxis[QwtPlot::yRight]; } int d = qCeil( d_data->layoutData.footer.text.heightForWidth( w ) ); if ( !( options & IgnoreFrames ) ) d += 2 * d_data->layoutData.footer.frameWidth; if ( d > dimFooter ) { dimFooter = d; done = false; } } for ( int axis = 0; axis < QwtPlot::axisCnt; axis++ ) { const struct LayoutData::t_scaleData &scaleData = d_data->layoutData.scale[axis]; if ( scaleData.isEnabled ) { double length; if ( axis == QwtPlot::xTop || axis == QwtPlot::xBottom ) { length = rect.width() - dimAxis[QwtPlot::yLeft] - dimAxis[QwtPlot::yRight]; length -= scaleData.start + scaleData.end; if ( dimAxis[QwtPlot::yRight] > 0 ) length -= 1; length += qMin( dimAxis[QwtPlot::yLeft], scaleData.start - backboneOffset[QwtPlot::yLeft] ); length += qMin( dimAxis[QwtPlot::yRight], scaleData.end - backboneOffset[QwtPlot::yRight] ); } else // QwtPlot::yLeft, QwtPlot::yRight { length = rect.height() - dimAxis[QwtPlot::xTop] - dimAxis[QwtPlot::xBottom]; length -= scaleData.start + scaleData.end; length -= 1; if ( dimAxis[QwtPlot::xBottom] <= 0 ) length -= 1; if ( dimAxis[QwtPlot::xTop] <= 0 ) length -= 1; if ( dimAxis[QwtPlot::xBottom] > 0 ) { length += qMin( d_data->layoutData.scale[QwtPlot::xBottom].tickOffset, double( scaleData.start - backboneOffset[QwtPlot::xBottom] ) ); } if ( dimAxis[QwtPlot::xTop] > 0 ) { length += qMin( d_data->layoutData.scale[QwtPlot::xTop].tickOffset, double( scaleData.end - backboneOffset[QwtPlot::xTop] ) ); } if ( dimTitle > 0 ) length -= dimTitle + d_data->spacing; } int d = scaleData.dimWithoutTitle; if ( !scaleData.scaleWidget->title().isEmpty() ) { d += scaleData.scaleWidget->titleHeightForWidth( qFloor( length ) ); } if ( d > dimAxis[axis] ) { dimAxis[axis] = d; done = false; } } } } } /*! Align the ticks of the axis to the canvas borders using the empty corners. \param options Layout options \param canvasRect Geometry of the canvas ( IN/OUT ) \param scaleRect Geometries of the scales ( IN/OUT ) \sa Options */ void QwtPlotLayout::alignScales( Options options, QRectF &canvasRect, QRectF scaleRect[QwtPlot::axisCnt] ) const { int backboneOffset[QwtPlot::axisCnt]; for ( int axis = 0; axis < QwtPlot::axisCnt; axis++ ) { backboneOffset[axis] = 0; if ( !d_data->alignCanvasToScales[axis] ) { backboneOffset[axis] += d_data->canvasMargin[axis]; } if ( !( options & IgnoreFrames ) ) { backboneOffset[axis] += d_data->layoutData.canvas.contentsMargins[axis]; } } for ( int axis = 0; axis < QwtPlot::axisCnt; axis++ ) { if ( !scaleRect[axis].isValid() ) continue; const int startDist = d_data->layoutData.scale[axis].start; const int endDist = d_data->layoutData.scale[axis].end; QRectF &axisRect = scaleRect[axis]; if ( axis == QwtPlot::xTop || axis == QwtPlot::xBottom ) { const QRectF &leftScaleRect = scaleRect[QwtPlot::yLeft]; const int leftOffset = backboneOffset[QwtPlot::yLeft] - startDist; if ( leftScaleRect.isValid() ) { const double dx = leftOffset + leftScaleRect.width(); if ( d_data->alignCanvasToScales[QwtPlot::yLeft] && dx < 0.0 ) { /* The axis needs more space than the width of the left scale. */ const double cLeft = canvasRect.left(); // qreal -> double canvasRect.setLeft( qMax( cLeft, axisRect.left() - dx ) ); } else { const double minLeft = leftScaleRect.left(); const double left = axisRect.left() + leftOffset; axisRect.setLeft( qMax( left, minLeft ) ); } } else { if ( d_data->alignCanvasToScales[QwtPlot::yLeft] && leftOffset < 0 ) { canvasRect.setLeft( qMax( canvasRect.left(), axisRect.left() - leftOffset ) ); } else { if ( leftOffset > 0 ) axisRect.setLeft( axisRect.left() + leftOffset ); } } const QRectF &rightScaleRect = scaleRect[QwtPlot::yRight]; const int rightOffset = backboneOffset[QwtPlot::yRight] - endDist + 1; if ( rightScaleRect.isValid() ) { const double dx = rightOffset + rightScaleRect.width(); if ( d_data->alignCanvasToScales[QwtPlot::yRight] && dx < 0 ) { /* The axis needs more space than the width of the right scale. */ const double cRight = canvasRect.right(); // qreal -> double canvasRect.setRight( qMin( cRight, axisRect.right() + dx ) ); } const double maxRight = rightScaleRect.right(); const double right = axisRect.right() - rightOffset; axisRect.setRight( qMin( right, maxRight ) ); } else { if ( d_data->alignCanvasToScales[QwtPlot::yRight] && rightOffset < 0 ) { canvasRect.setRight( qMin( canvasRect.right(), axisRect.right() + rightOffset ) ); } else { if ( rightOffset > 0 ) axisRect.setRight( axisRect.right() - rightOffset ); } } } else // QwtPlot::yLeft, QwtPlot::yRight { const QRectF &bottomScaleRect = scaleRect[QwtPlot::xBottom]; const int bottomOffset = backboneOffset[QwtPlot::xBottom] - endDist + 1; if ( bottomScaleRect.isValid() ) { const double dy = bottomOffset + bottomScaleRect.height(); if ( d_data->alignCanvasToScales[QwtPlot::xBottom] && dy < 0 ) { /* The axis needs more space than the height of the bottom scale. */ const double cBottom = canvasRect.bottom(); // qreal -> double canvasRect.setBottom( qMin( cBottom, axisRect.bottom() + dy ) ); } else { const double maxBottom = bottomScaleRect.top() + d_data->layoutData.scale[QwtPlot::xBottom].tickOffset; const double bottom = axisRect.bottom() - bottomOffset; axisRect.setBottom( qMin( bottom, maxBottom ) ); } } else { if ( d_data->alignCanvasToScales[QwtPlot::xBottom] && bottomOffset < 0 ) { canvasRect.setBottom( qMin( canvasRect.bottom(), axisRect.bottom() + bottomOffset ) ); } else { if ( bottomOffset > 0 ) axisRect.setBottom( axisRect.bottom() - bottomOffset ); } } const QRectF &topScaleRect = scaleRect[QwtPlot::xTop]; const int topOffset = backboneOffset[QwtPlot::xTop] - startDist; if ( topScaleRect.isValid() ) { const double dy = topOffset + topScaleRect.height(); if ( d_data->alignCanvasToScales[QwtPlot::xTop] && dy < 0 ) { /* The axis needs more space than the height of the top scale. */ const double cTop = canvasRect.top(); // qreal -> double canvasRect.setTop( qMax( cTop, axisRect.top() - dy ) ); } else { const double minTop = topScaleRect.bottom() - d_data->layoutData.scale[QwtPlot::xTop].tickOffset; const double top = axisRect.top() + topOffset; axisRect.setTop( qMax( top, minTop ) ); } } else { if ( d_data->alignCanvasToScales[QwtPlot::xTop] && topOffset < 0 ) { canvasRect.setTop( qMax( canvasRect.top(), axisRect.top() - topOffset ) ); } else { if ( topOffset > 0 ) axisRect.setTop( axisRect.top() + topOffset ); } } } } /* The canvas has been aligned to the scale with largest border distances. Now we have to realign the other scale. */ for ( int axis = 0; axis < QwtPlot::axisCnt; axis++ ) { QRectF &sRect = scaleRect[axis]; if ( !sRect.isValid() ) continue; if ( axis == QwtPlot::xBottom || axis == QwtPlot::xTop ) { if ( d_data->alignCanvasToScales[QwtPlot::yLeft] ) { double y = canvasRect.left() - d_data->layoutData.scale[axis].start; if ( !( options & IgnoreFrames ) ) y += d_data->layoutData.canvas.contentsMargins[ QwtPlot::yLeft ]; sRect.setLeft( y ); } if ( d_data->alignCanvasToScales[QwtPlot::yRight] ) { double y = canvasRect.right() - 1 + d_data->layoutData.scale[axis].end; if ( !( options & IgnoreFrames ) ) y -= d_data->layoutData.canvas.contentsMargins[ QwtPlot::yRight ]; sRect.setRight( y ); } if ( d_data->alignCanvasToScales[ axis ] ) { if ( axis == QwtPlot::xTop ) sRect.setBottom( canvasRect.top() ); else sRect.setTop( canvasRect.bottom() ); } } else { if ( d_data->alignCanvasToScales[QwtPlot::xTop] ) { double x = canvasRect.top() - d_data->layoutData.scale[axis].start; if ( !( options & IgnoreFrames ) ) x += d_data->layoutData.canvas.contentsMargins[ QwtPlot::xTop ]; sRect.setTop( x ); } if ( d_data->alignCanvasToScales[QwtPlot::xBottom] ) { double x = canvasRect.bottom() - 1 + d_data->layoutData.scale[axis].end; if ( !( options & IgnoreFrames ) ) x -= d_data->layoutData.canvas.contentsMargins[ QwtPlot::xBottom ]; sRect.setBottom( x ); } if ( d_data->alignCanvasToScales[ axis ] ) { if ( axis == QwtPlot::yLeft ) sRect.setRight( canvasRect.left() ); else sRect.setLeft( canvasRect.right() ); } } } } /*! \brief Recalculate the geometry of all components. \param plot Plot to be layout \param plotRect Rectangle where to place the components \param options Layout options \sa invalidate(), titleRect(), footerRect() legendRect(), scaleRect(), canvasRect() */ void QwtPlotLayout::activate( const QwtPlot *plot, const QRectF &plotRect, Options options ) { invalidate(); QRectF rect( plotRect ); // undistributed rest of the plot rect // We extract all layout relevant parameters from the widgets, // and save them to d_data->layoutData. d_data->layoutData.init( plot, rect ); if ( !( options & IgnoreLegend ) && plot->legend() && !plot->legend()->isEmpty() ) { d_data->legendRect = layoutLegend( options, rect ); // subtract d_data->legendRect from rect const QRegion region( rect.toRect() ); rect = region.subtracted( d_data->legendRect.toRect() ).boundingRect(); switch ( d_data->legendPos ) { case QwtPlot::LeftLegend: rect.setLeft( rect.left() + d_data->spacing ); break; case QwtPlot::RightLegend: rect.setRight( rect.right() - d_data->spacing ); break; case QwtPlot::TopLegend: rect.setTop( rect.top() + d_data->spacing ); break; case QwtPlot::BottomLegend: rect.setBottom( rect.bottom() - d_data->spacing ); break; } } /* +---+-----------+---+ | Title | +---+-----------+---+ | | Axis | | +---+-----------+---+ | A | | A | | x | Canvas | x | | i | | i | | s | | s | +---+-----------+---+ | | Axis | | +---+-----------+---+ | Footer | +---+-----------+---+ */ // title, footer and axes include text labels. The height of each // label depends on its line breaks, that depend on the width // for the label. A line break in a horizontal text will reduce // the available width for vertical texts and vice versa. // expandLineBreaks finds the height/width for title, footer and axes // including all line breaks. int dimTitle, dimFooter, dimAxes[QwtPlot::axisCnt]; expandLineBreaks( options, rect, dimTitle, dimFooter, dimAxes ); if ( dimTitle > 0 ) { d_data->titleRect.setRect( rect.left(), rect.top(), rect.width(), dimTitle ); rect.setTop( d_data->titleRect.bottom() + d_data->spacing ); if ( d_data->layoutData.scale[QwtPlot::yLeft].isEnabled != d_data->layoutData.scale[QwtPlot::yRight].isEnabled ) { // if only one of the y axes is missing we align // the title centered to the canvas d_data->titleRect.setX( rect.left() + dimAxes[QwtPlot::yLeft] ); d_data->titleRect.setWidth( rect.width() - dimAxes[QwtPlot::yLeft] - dimAxes[QwtPlot::yRight] ); } } if ( dimFooter > 0 ) { d_data->footerRect.setRect( rect.left(), rect.bottom() - dimFooter, rect.width(), dimFooter ); rect.setBottom( d_data->footerRect.top() - d_data->spacing ); if ( d_data->layoutData.scale[QwtPlot::yLeft].isEnabled != d_data->layoutData.scale[QwtPlot::yRight].isEnabled ) { // if only one of the y axes is missing we align // the footer centered to the canvas d_data->footerRect.setX( rect.left() + dimAxes[QwtPlot::yLeft] ); d_data->footerRect.setWidth( rect.width() - dimAxes[QwtPlot::yLeft] - dimAxes[QwtPlot::yRight] ); } } d_data->canvasRect.setRect( rect.x() + dimAxes[QwtPlot::yLeft], rect.y() + dimAxes[QwtPlot::xTop], rect.width() - dimAxes[QwtPlot::yRight] - dimAxes[QwtPlot::yLeft], rect.height() - dimAxes[QwtPlot::xBottom] - dimAxes[QwtPlot::xTop] ); for ( int axis = 0; axis < QwtPlot::axisCnt; axis++ ) { // set the rects for the axes if ( dimAxes[axis] ) { int dim = dimAxes[axis]; QRectF &scaleRect = d_data->scaleRect[axis]; scaleRect = d_data->canvasRect; switch ( axis ) { case QwtPlot::yLeft: scaleRect.setX( d_data->canvasRect.left() - dim ); scaleRect.setWidth( dim ); break; case QwtPlot::yRight: scaleRect.setX( d_data->canvasRect.right() ); scaleRect.setWidth( dim ); break; case QwtPlot::xBottom: scaleRect.setY( d_data->canvasRect.bottom() ); scaleRect.setHeight( dim ); break; case QwtPlot::xTop: scaleRect.setY( d_data->canvasRect.top() - dim ); scaleRect.setHeight( dim ); break; } scaleRect = scaleRect.normalized(); } } // +---+-----------+---+ // | <- Axis -> | // +-^-+-----------+-^-+ // | | | | | | // | | | | // | A | | A | // | x | Canvas | x | // | i | | i | // | s | | s | // | | | | // | | | | | | // +-V-+-----------+-V-+ // | <- Axis -> | // +---+-----------+---+ // The ticks of the axes - not the labels above - should // be aligned to the canvas. So we try to use the empty // corners to extend the axes, so that the label texts // left/right of the min/max ticks are moved into them. alignScales( options, d_data->canvasRect, d_data->scaleRect ); if ( !d_data->legendRect.isEmpty() ) { // We prefer to align the legend to the canvas - not to // the complete plot - if possible. d_data->legendRect = alignLegend( d_data->canvasRect, d_data->legendRect ); } } connectome-workbench-1.2.3+git41-gc4c6c90/src/Qwt/qwt_plot_layout.h000066400000000000000000000062241300200146000247420ustar00rootroot00000000000000/* -*- mode: C++ ; c-file-style: "stroustrup" -*- ***************************** * Qwt Widget Library * Copyright (C) 1997 Josef Wilgen * Copyright (C) 2002 Uwe Rathmann * * This library is free software; you can redistribute it and/or * modify it under the terms of the Qwt License, Version 1.0 *****************************************************************************/ #ifndef QWT_PLOT_LAYOUT_H #define QWT_PLOT_LAYOUT_H #include "qwt_global.h" #include "qwt_plot.h" /*! \brief Layout engine for QwtPlot. It is used by the QwtPlot widget to organize its internal widgets or by QwtPlot::print() to render its content to a QPaintDevice like a QPrinter, QPixmap/QImage or QSvgRenderer. \sa QwtPlot::setPlotLayout() */ class QWT_EXPORT QwtPlotLayout { public: /*! Options to configure the plot layout engine \sa activate(), QwtPlotRenderer */ enum Option { //! Unused AlignScales = 0x01, /*! Ignore the dimension of the scrollbars. There are no scrollbars, when the plot is not rendered to widgets. */ IgnoreScrollbars = 0x02, //! Ignore all frames. IgnoreFrames = 0x04, //! Ignore the legend. IgnoreLegend = 0x08, //! Ignore the title. IgnoreTitle = 0x10, //! Ignore the footer. IgnoreFooter = 0x20 }; //! Layout options typedef QFlags
12Y;u!#|Q6d.b-Ucc(.";u@I!9O 4 bqe> +n3uǼz79wleT5".b?n+!K|ٟaQ/9,Aieqo?NUWUoNG/H%lwft{z78-fnH7@8 ؿ7T1? tcqLfnK7)s x!%_4ȯ,NQN7tT4֩s'*7qn=N,q{]}=Z3=FƋuPV;PU9芍(43)N ?0RʄE%XZ.'3Z/(2V,_.?$=@?F@9 J On\ʧرc+SeTG꛲H9JwSu<:'`&][׷ϱ:yifXbhv#[CaB8ųXx'u[`;6N;ŗ\rül~:&'VPSoUWabb>He(oN*;%rB)i j5ǐ^.ׇb1u$ _zF-]E7; pXA,P>^rׯڄ뒣YGFDr¸ԕ=]}l+yu\1hFM!u;WO=ZOLD v:C8ԉ[b?o_PʜN+KBUҙm6(7-r ^(ױcOH?'xQ`S_~0  .NX-iIA)0wߝ6w{;!rk+>˜[E!8x8* K(sz[VneQi{ )‰by}]DsP[FFi54|p̝ȃXЋ/HQQ~N:Lbor nf6A̬LtaT Ƿn!CE (@ "-Q1 FtbrwA%a9t[; q*Xtۨ oL[CƥX8ۆ̀nÚES۫.*I'oqzRk{$uj?-`o8{E%tEdq$[N|gl/si}EDvim䮷9Z޽DaTu`EoL40^s?#=ͲMBG۶ QTT\IEc F%>:Ď>k /ڄ'wfI6F@|(+Tˇf/c6YR=ҤgFR^H#B×ZtfW>zqؾ{a'pOZ}u`;P3}<Xo<گ z7+dLu0c/QR.4.0 .S;MMλLVdCMw8BeߊrX߾}z\*x4E]xTCWazdM: ͧ?N;A&9ŀ/?O:('}f BdqqB}Y}l ۂ\@JxFK Xy⸇In֡@g}ƃCy0aK*8+$>έ jxOxnxHe`8{2eJP-{|,Q?^y~HgF~foc1;co`|&60< Px<ڇ.O-6ySmOc@G&7'4'?w;xg96̽-p|׎r8|pqd1m1}a.++*X-qD㱿/9{ݞ]{i;xCǻ?p 6;< ?;6m`;p1?1&YvǮuyvC<~*Q~B]ec\]|b|3gVv'qPN#Y6//q]Recc \\#O?]{6"]mKW͛wZۗsiw>ʠ?ހ_cB4G#}r"PW?gcL6Ա |gqg$v%ն1@ߙlHácϯ8ʴe:<%6Zy$n +88wK4d3MG%({yb9!,"("|%TQ~TuÇ iGӝ١rt8Syy<6 0bT'%}wB/'Dž2uŭr5i^}<C#—X^u ˬF=c'}6wnT8VkXMc +ƧªLƾe+|Q&5X=ΚkrX] ݾP%<KrlXG JƝ7ğC&}*[bH=zE8V?<c<K|v?R*8 8rhG+^zy'ېVUe`KK^pˠnm17FwlqNOOw d8a0nV?p ?FqƊtVzr 9dYcg\1:c~̠ۗ}ʱDZ \CO>2zFBp`RK81PjsVߏ7]~"u0)cr8S.a@ c!|5>Ğ2*0he̮} ¬WOX*y8ހ BBBKxZ˨g*[oE?` IֈӎZiC9R"v&US"!xUZ5;G4~x\+K) Ghfj%u&޾C[x^DD"LVK;=%6=ckںg=J洴4zj(!\Cjݞ=F;ECeguIq=F w+쨐ʜ͋$&ɿ7F일ݝnDQ bܸqB8CBUYTBA_\(}AW;+Ef?UbDa_ޥ 01NU#V DW\h#*TcG%>u `C㦞x fyEԴT {N`UvJr j-O€R={~衇\bˎDj;da([_M@􀽬| V]xAR6U4qr +G,EDD-|^{W^8!lƻԦUi3IG_bHw* ZRۯu JQtX|&zS`PQh(yǶFPD<}I;M44 >9 LF$8VlLQF[9:'wXt!Qd"$)=﷑ɡ'0(З|H?.; F'N?\|@0 H ` C-{Zўy;O"4c; H\ 8WߦW)d=bǩh*L.Or57unu)4ly;)46Uc>(nQ P0H-7t H$@ 7%,Wgci0ib`IU9ח^1EY rfgTTaikp`Hq d1@ _ ihU8 &,R`~ [RE t믿 -PJPEӦSTd!/p8q!FC-[&ҴiSE=(a#I5 7&i[u4yZ]XB>Gp<55a";sL7U,<:XyBەBcj&o/Q^n,-:튽@Ud/NԎ-,5Bx,5,d)3b\b`S_Hc L$XP-߀ g5~*|٢Z&Ϡ*#͛I,ZhP7} MUc{G&]@kQ8s&R#[p?aApQaHXi[y򴆇  KO5J4pD]5>_lZ{G8.L1A`}[X[y^Cﻁ$52/.ZY}N,b{u`=".eL8,3T;fE&Yz0&{<֕0q)$R@uDHWEx ;! ɢ1zXV-0ޫ-$.te;¡ ed~c MȾQ\sLl:` 7/w_cG,W3"KtqT | ȏf=!z1 E"EꜶt2+W1Mm?^K"RWlX^cAff}V{ػmK}@^dΛ'A,W>!D 6 ^= Ge4*7_b'wK$r:eU>5 i;AnG'Kyd?5xFaB}>f#Hm^rQrՖN\/b@SvQX1V\R̥8kx3J'6MF yg'v{)8#)RֹD 7nJsa/Id+Q 6 UZB1l>)$ !{I:[uV)3 č1H 6!xTPmq X_IdyCW^@d 4=p%1BjO߼90E:@!*7ӑ¨}_Y8 (yuջT-#;"ѣyB,?gf.cG%if UUsD-X_ُq u̻u `~u@@v+8 6JKMzoq AQ+Vp)w?k1u*tTD6*z2 MœL4Re"*K {(swX} { ވ[cA`j61`2@YLF9y<Yn_{Uan{T[pƤR3axgѢ\8䛚QB&Ve8=\8CaT:\ Kw}N.3~|cx_h˸ L l|*"?o 7&3ҡǛϧr$ǸJu~K%ڂ*5##ݣ,oǛh&6Di #%y[kgө\iil /nݺpDPq,bG:`u"4&`WX}}̅"b!&@Ƙh߾c ?8eHvP$+<":Pd:R(PYv'9*W+)|iO? :*,Bιg /8ߞ`ܬqU֭{Pf p wU2+^SP2OzĆ=&%-˫g D#$~"49 5&y5LWvET )x)on%x#-D} [[xNLlf=x vP:1_u_f}$x}=4_}Yo 0UюgT߅k=͸02+/- lLf+zLmht+-J)O+c5-3fc"ڵk@&<1CԴ|rA(թ lE ?bbJb PHoޢ˃[-󱎎/a!ԂM(-,x9cyV]P-Ql5{92N2^Iƹ18Pe16x{2?d{f|Z_CǪfLi>Jmf] .7!MMK jLHXIKټ̓LGXeMPRt"'dFC/d<~%^-ysLGmJorLjW5p!&` m!IDAT6'Rېyss7 d\CG]c3^_ٯ *B|3kCUє,} ڷy >^MkV)I69gK>𬆞78srryY V#.R0DL9psXBM*xPF#ćX3v:?£5##M-sa2aQTZCLldMԖdp"6a>.p hK )f@K=U9ѳ/ yJ8PE8Ǟ={z9# ~\Xȼ@EdńIy@"4Ƥ&=r9xaFPLf% \Otх9BdbKƖv3BdRbոr>}Ρެ/* m4UIIŠLm+9VW@@uP]2110`{o 94oD>O X']UoZb!>n zmOѐA*'j `b-_BȶEk)Pk݃fO bK-";eDES(4u)~ŎFx%2]G'g1\DM72'aؖS3l݄D<<fiN_}^Ի ޖ:S.Yc@c@h0F (6A;ӧ)S9H ]x0)K0:n&7hP MGlb g˯)lEuԪ%Tx37ZB<ģ"]L hb;W\etjB:trjsҁz`j;qzgxRȞÄqB{r7 Cی${e1Uz~Vg̨+h{g\=Htƀ {8xS#D ,W{܃ћ7E%D?بL0|9"Ԃ6 ][.[FΫh LƬY\aFvZjECTByh8 Tx@079 i$M5 Ҹa?lYH0ȫ%2BZO>r?*U s.HʣQx\*.8A>Z &JDߥAc@cDRE{=b 3nZ59^xAXGAg蹇|JC(!F7P@WS+MJckO;6G6`d}qJ2C\coM}]AU$E/[l)DRw ǜ߆?uiGobbd\b'k^!!ig7}zE9`Vx&tH {?o1l_`38C-[A}11paVXC~3Ye"_ea{Q aCÜWV7MYtyy+]ve:wlFOG0 JV\FUJᡕBװ>=ҳèj] *C0{"cdI9e~ ߰{,PZ;@ 8A$r\u,ȚSpV&BC k2A!﫯*j[{Li[j%&r B̆gaX~tهZ "P"Ӓ%i+&iӦyJE907я?A+E7"SRi[^fU<ɕQ,TLP1l(6A@$EaRR/1E x`@,E&c}GC ߸t Oy)ڳ96 ==Qn=lW^MVR]{vrrrEB}#lj|a{b_r\?S"nݻE=I _09۷oo֜+ kB7L}1u"h;q ЃdT6*wq7ڞ,E%ԬTucrD[),4Lxz@ l,l0-.-UA_wg/όX6n0nÄ e˖ŋ=Wq."Ͽ &BxF8L!yߊ^lWC3& <;׬YC+٨xgq4qHCKy9#19˺83'XDqBl1cƈ (f d ƅ \]\U&@F038 _ܣɓ'f:McL08 kAUT&9 pn.oI`R+%9ؒ8 ;[1wgw[:XQ k D&b&(Ap@PXXM",so|mƟm@.M@@0' a1bX VIgOh,q߹KM0ww(DG{n .8_3x#Ulٷ0%"|˟ XmPO<buGnníd`j CBX1m'[%_HR fSN܄њA&멂SG */sf 0LZ9&o{w}c课 {2ޘt4x0A㻔o#2uq)h]c:<(1ɛ!LDS똵E4@ 1&ڶ&!]\/0םqz XRԹ G%mvS;ML֔Y47'bj1Ɓ}@@@@pO ǎ3DOCѼ*F(P">>θC[;Y&:;g!`uH_Y9@evZ1iNJJB=EUœXX"%G?Zs w^[ZPAuH"H$E]<%AkrT9TJmPH}SRt^+DKhE9L\ $1QևhK^d_./E]pf6k ']$m@ٯC{!fr,=`WGܬ-xSC0ƱA8u9A$0Z6gڒ#4m=kNJK-߶M[+aäk8Ro5 4w*6e9h(ھ[?їӏ &t0V/!ߩ056 :Qc !.㳳h2xxL-#|le~hXyS`nlv6eCX (}z@C].zu-n"?l&`O?}m~ 7$p )n6Ax3d}k*iOI\v7!xEQ1 @(M5I$ o]I0D`G/|[l=ʭ]pB1&mWQ&^A,/&b( 駟ٳg{Lqq@  368@%@W&UR[A@[ mi#{ 1 L'YIDATX Q 0 @u8'S}y}t#5" bV3W^+0DS*p>1pΕ0x6pIKaDR%`+>ҪRw}: V{,?{s%CƤ{GӱOAR$ Hs$ x\xI<_H` Dڮ۶X.a 𲣄%Գ@jcvyճ44MvBLtҙqih%u sC͸IENDB`connectome-workbench-1.2.3+git41-gc4c6c90/src/Resources/GeneralResources/Fonts/000077500000000000000000000000001300200146000270575ustar00rootroot00000000000000connectome-workbench-1.2.3+git41-gc4c6c90/src/Resources/GeneralResources/Fonts/FtglFonts/000077500000000000000000000000001300200146000307655ustar00rootroot00000000000000connectome-workbench-1.2.3+git41-gc4c6c90/src/Resources/GeneralResources/Fonts/FtglFonts/VeraBd.ttf000066400000000000000000001625341300200146000326620ustar00rootroot00000000000000OS/2=@VPCLT,eϘ6cmapXcvt >-Rfpgmp9)gasp glyf4h)&hdmxEHheadO$6hhea. $hmtxy,kernlocaXz[maxp} nameYͿpostx<prep|a!\::N:: R^0p   t t  &   ; 0  0   C , [ `   0 & Copyright (c) 2003 by Bitstream, Inc. All Rights Reserved.Bitstream Vera Sans BoldRelease 1.10BitstreamVeraSans-BoldCopyright (c) 2003 by Bitstream, Inc. All Rights Reserved. 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 the Font Software, including without limitation the rights to use, copy, merge, publish, distribute, and/or sell copies of the Font Software, and to permit persons to whom the Font Software is furnished to do so, subject to the following conditions: The above copyright and trademark notices and this permission notice shall be included in all copies of one or more of the Font Software typefaces. The Font Software may be modified, altered, or added to, and in particular the designs of glyphs or characters in the Fonts may be modified and additional glyphs or characters may be added to the Fonts, only if the fonts are renamed to names not containing either the words "Bitstream" or the word "Vera". This License becomes null and void to the extent applicable to Fonts or Font Software that has been modified and is distributed under the "Bitstream Vera" names. The Font Software may be sold as part of a larger software package but no copy of one or more of the Font Software typefaces may be sold by itself. THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL BITSTREAM OR THE GNOME FOUNDATION BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL 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. Except as contained in this notice, the names of Gnome, the Gnome Foundation, and Bitstream Inc., shall not be used in advertising or otherwise to promote the sale, use or other dealings in this Font Software without prior written authorization from the Gnome Foundation or Bitstream Inc., respectively. For further information, contact: fonts at gnome dot org.http://www.bitstream.comCopyright (c) 2003 by Bitstream, Inc. All Rights Reserved.Bitstream Vera Sans BoldRelease 1.10BitstreamVeraSans-BoldCopyright (c) 2003 by Bitstream, Inc. All Rights Reserved. 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 the Font Software, including without limitation the rights to use, copy, merge, publish, distribute, and/or sell copies of the Font Software, and to permit persons to whom the Font Software is furnished to do so, subject to the following conditions: The above copyright and trademark notices and this permission notice shall be included in all copies of one or more of the Font Software typefaces. The Font Software may be modified, altered, or added to, and in particular the designs of glyphs or characters in the Fonts may be modified and additional glyphs or characters may be added to the Fonts, only if the fonts are renamed to names not containing either the words "Bitstream" or the word "Vera". This License becomes null and void to the extent applicable to Fonts or Font Software that has been modified and is distributed under the "Bitstream Vera" names. The Font Software may be sold as part of a larger software package but no copy of one or more of the Font Software typefaces may be sold by itself. THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL BITSTREAM OR THE GNOME FOUNDATION BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL 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. Except as contained in this notice, the names of Gnome, the Gnome Foundation, and Bitstream Inc., shall not be used in advertising or otherwise to promote the sale, use or other dealings in this Font Software without prior written authorization from the Gnome Foundation or Bitstream Inc., respectively. For further information, contact: fonts at gnome dot org.http://www.bitstream.comf3f=ffTbfTfmf3bq%fHZfm99Xm=fuff9{{X3fLfLJ#DDf?;Pw /X#/553X sf+j-j!f#^`3B3\fy```{j\{`bXP1L`%!JJ7{'}3Xy9bsA&%$!:$#"!:"!: d}}      Y    & Y @ &  .A@}>,,G}G  @ 2 d۠d%%%   %ё%Д #&̑ɻ]ɻɀ@%]@%dĐ::2  }& @ ]%]@..@   K%%%2 ~}|{zywvwvututsr}qpo,o,nmlkjihc h2gf2ed ed d@cb c b a`a``_ ^]\\[Z[ZZYXWV@VUTSRQRQQPOPONONMLKLKJKJIJIHGFGFEDCDCBA%BAA%@?@?>?>=< =< ;d:987656%54554 4432 33@2 10100/ .-,:-,%,:+d*d)(''& %$#@+$#" "!!@  %@ K}K%%dd   2     @   @d  d++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++, %Id@QX Y!-,%Id@QX Y!-,  P y PXY%%# P y PXY%-,KPX (EDY!-,%E`D-,KSX%%EDY!!-,ED-ff&&/10!%!!fsr) @ <2991/0!!!!h33h^h@1<20###h++)K@1     91/<2<<22<<220!3!!!!#!#!5!!5!!!`aaE````HFR`PF#*1s@? %$ + ,#,, (/($ +/ 2<<991/99999990#&&''&&546773&&'6654&}osy!dede GUNWWP-.);?7* "*/(BE5;CBBDCB '3c@5  % ."( 41+  1 +%49912<0KSXY""32654&'2#"&546#3!2#"&546"32654&3GNMHHLMGֺ%պպHNNHHMNh{rs{{sr{ؽ۽ ٽڽ٨|rs}}sr|{&06@Y     ,-./+0()'%0' - -! '*$ 0$*$  *  199999991/99990KSX999Y"']@   ' 0   0%/ / %&? ? @K K K/K0ZZUZ Z U(\.\0X2_2dig`i i d&2, ' '* 9 5005@J I'I(WW\ ['ggl ]] >7!!'# 5467.54632.#"3267577oc%Xbi*([k^PMU1ABwCt2>FnkmFDےj5j:0.;6"W/wGs))@ 10#+ @ 29910!&547!י);: @   29910654'!)?C)9F@(      <2<29912290%#'%%73%JLLNLNMXX "@   <<1/<<0!!#!5!    m9@ 10!#hduo10!!ot91/0!!h}B/99103#mb/ #@  10&#"326! ! i||jj|{j@'&@mstm (@  1/20!%!!!T[nT HH5@)% 9991/2990KSX9Y"K TX@878Y@&**"""555BJFF]]!!>54&#">3 N!IFuZzz )~B~DiMLH+-zӱ(L@+  #)& )999190!"&'32654&##532654&#"663 sqlg~]^rl#!%'%%)67jcfi[]V^*) \3 C@ !  ! %    <291/<290KSXY"!!3#!!Z@jRJ=@"  " 190!!663 !"&'32654&#"v,Y00{zaSl 12/FFuv+-# $7@ "% %$%190"32654&&&#"6632! !2eeeefeev_PB[uEgჃ-+11ir E@%91/0KSXY"]@ &5F]!!!e'1} #/G@( '-0 $*& '&!$0991990"32654&%&&54$! ! $54632654&#"lttlkrr|כc\ZbbZ\cvnnuunou)ž)*ސUY``YY_`j$7@  %%" $%19073267#"54! !"&2654&#"\RDZ9$@ieffeeff!++22 "vYN`@<21/0!!!!ii`}}N` %@  <210!#!!idiu}=@29190 5<'@ <210!!!!=@<91905511J!H@'    "<299991/9990!546776654&#"6632!!Bj@95`VQfy]N^@D*i1Rb:4\.FOCB:*(ǿbY9>K-o Ml@: 40LM3 30 07$CN34L **)(I(*)4=N<991299999032654&#"#"&54632536654&'&$#"3267#"$'&5476$32!#?iZYjkZXiYثY|:;_tZked~Yk}٘~~On{KM'{zyZGOPGKɝdIz=;bɵdbg^Pag}}IJ}|b~ ' @@     %    91/<90KSXY"K TX  @878Y@ / V f  t   %* IFGH XYVWhifg` t{zu{t      /]]!!!!!F_}))}+%R P@%    !299991/90@ ""/"P"]2654&+2654&+)! [^^[tutuH|B7fPNMQsbcaay$ռmf\;@    - +21990/_]%# !2.#"3267\j}Lu}jksskR78ef87IDDI9.@  -. 99991/0P]32654&#! )=TMwiffixjq#ateeta 0@   21/0 P p ]!!!!!!rg +@ 21/0 P p ]!!!!!rgfK@%    1 3/-+19990_]%# !2.#"3267#!ʥLy}|@   221/<<0@P ` p ]!!!!!!89+y=71/0KTKT[X@878Y@P]!!+f= L@   991990KTKT[X  @878Y @ P ]!!#3265N3IO@UZfi ]]!!!!mR+ff 2@  -7-+10@ /?]"3254 ! f°±hhgddjk 1@   - 299991/0]! !#!32654&#1pzzp_mddlffb@   - 7-+999190@,  '/V S f ` w w p  Y Y YXj i x ]]# ! !"3254fgk-¾lkh\@2%       29991/<9990KSX9Y"]@66EEVVPee`]2654&+!! !.#yiiyL'O}@f7q^?ZgfX֔-XspR-'@*% %( "(999919990@Tp)9999 JJJ X ]\^^ Z!joooh o n!t t t || |!  !  !(]].#"!"$'32654&/.54$!2{hYuӎ⏏ |~[ {78LP@881/20K TKT[X@878Y@ ]!!!! `N3@   91290@p]!3265!! yy6= '@'%91/290KSXY"K TKT[X@878Y@,  GGHHEJWX]]! !! 5N+= x@J 6  6 6 6   %     91/<2290KSXY"K TK T[K T[K T[X  @878Y@  % :?:?3 0 0 @ @ @ ^^a          '('(%* /66220002 4 6 ?IFHE J ]]ZZUURRRZ U ] ooonhheh k n i o wwx v x   K]]! ! !! !=qsnDD==+o' @E    %     91/<290KSXY"K TKT[KT[X  @878Y@X  /& <3 _P      ++%$%+ :55: P ejo  ]] ! ! ! !omGF@(%:: 91/290KSXY"K TK T[KT[X  @878Y@, %%0@P` %*5:0 O o ]]! !!TTu\q w@% 991/0KSXY"K TK T[X  @878Y@ %)69? FHO V_ o ]!!!5!s8!7@210!!!!mB/99103m@210!5!!5!m`@ 91290##fg--/10!5۾^fN10K TKT[X@878YK TX@878Y] #yfxX{ %@*   # # = ;&229991/9990@L/'= =!?'M M!] ]!n n!~ ~!p' ! ! ! !20C@SPc`]]"326=%!5#"&54$!354&#">3 pq[QeiH"ӆsUst/ LJDMm)f]ˢŸUO..^ 8@ B@ 221/0O`]%2654&#">32#"&'!!syyss{{{Ju uJf稠b]]bX5{7@ B ;210_].#"3267# !25IOT@TWV/X=202177\8@ @B ;221/0O`]!!5#"322654&#"hJu tsyysryyXc\II]ɨX {C@!    D ;9190/?]!3267# ! 4&#" q}K"=w`h3f~~CD015:“f}un'`@    E <<991/22990K TKT[X@878Y@ ]#"!!!#35463L<27DN`N\Fy(K@& #& @ B;)221/990O*`*]%#"54325!!"&'3265"32654&Ju uJhic^[o|xsp||b\CA\c !655@   G 21/<9990`]!54&'.#"!!>32 H.pfQnVon#'b])@ <21/0@ P ` p ]!!!!ff`F =@    <2991990@ P`p]!+53265!!fͱ>fLf`\y @   291/<90@`;IIZ]X_ogvv{:DGJV]g`ewpv|]]!! !!fNNK- 1/0@ P`p]!!f{%t@)   #  H H &<991/<<<29990KTX&&&@878Y@'0'P'p'''']>32!>54&#"!4&#"!!>32DpFNfo@RgphBgthmVH wkHk`_`p{5@   G 21/<9990`]!54&'.#"!!>32 H.pfQnVon#'`b]X'{ -@  BLB;107?G]"32654& ! w}}wu||u!EG{88V^{;@B @ 2210O`]%!!>32#"&"32654&fJu us{{ssyy b]]7\Vy ;@  @B;2210O`]"32654&#"325!!ryyrsyyyJu uJhw+c\IG\c{C@     21/990KTX@878Y.#"!!>32/]/fE}*(/`nejb{'@@  6  6% %( SRP"M(9999190KSX99Y" ]@^ #  ,. . . . . ) 9; ; ; : : K J J J H w w  %  7 ?)_) ]].#"!"&'32654&/.54632s_fcKa?o}ktijIm?c=0035+. ###44:90/ x@    T<<991/<2990KTKT[KT[KT[X@878Y@??PPP`` ]]!!;!"&5#33q>\Ա%N7>`;@  G 291/29990`]!3265!!5#"&hG.pfQmp[.w#&)b]`@'%91/290KSXY"K TKT[X@878Y@| 0@Vf  &$+)64990FFII`x$]]! !!fgGw`H` @J 4  4 4 4   %     91/<2290KSXY"K TK T[K T[X  @878Y@ 550 G @ @ _ l        &$+)*+ $ % /554;::78 ?GIFHGH YVV[ T Y _f`b```d ` upspppt p      []]!!!! !H\+\yy` ` @F    %    91/<290KSXY"K TKT[KT[KT[X  @878Y@  / 3< CL R\ bl sz         2     $++$ 4;;4 0 DKKD o       :]] !! ! !l{{l=#LbF`A@C %    9129990KSX9Y"K TKT[KT[X@878Y@ @Pet $$$5586699EEJJEEge    9]]! !+5326?f-f)Gp[S `6:K\F` @% 2991/0KSXY"K TK T[X  @878Y@DYVifyv &)/ 9? J_ ]]!!!5!uNN`f$^@1 %   ! % $  %<<29999999199999990#"&554&##5326554633#"3l==lEUZnoYUmutWW10#$`@2%   #%# %<2<999999919999999032655467&&554&##53233#"##FUZooZUFl==lmWW͖tuR#@  1990#"'&'&'&#"56632326j`k^Xbk`k^VRPE:=MSPE:=K 'k'$u 'm!{@S!! ! !%! !  UU "9999999991/<9990KSXY"K TX"""@878Y@/!/!:!o! !! # ///  /// "+ #EKUZ` ` ` ooo``ooo`fi `#tuyz{t    D]] !!!.54632%32654&#"!}^_}vtwM66MN56MJH"K+uu/L{6MM66MMRfo\'&sk'(um'15uffk'2Nuk'8'uXf'DXf'DCXf'DX1'DX9'DX'DXo5{'FX f'HX f'HCX f'HX 1'Hf'wf'Cwf'w<1'w9'QX'f'RX'f'RCX'f'RX'1'RX'9'Rf'Xf'XCf'X1'X5; *@  WV W <<1220!!!!!5!VJ#!/dL @  XYX10"32654&'2#"&546HdcIHdeGBz0/11-0|D\dHHbcGHd3/0xDCy-03#W@.    !$   B$<<222991<9990&&##667###$4%3NMMNJAY9S: GZ,lm*902i2/  (.##}@@!   <<1/222990&&#"!!!!3#5356!2FMvqu\'&} F=3?k@8@1:4 %+1@ =!+%74:!=\.!\=[.7[(@9999991999990&&#"#"&'532654'&'&&5467&&546326654&uc9KL ҟquMKUfs9AN$ˠoqKATDC{AF''1/CO Y}u0)qI)+2(FJWh33oKL2CbBO4Cj'` ]104676632#"&'&&'535II245633JI326J235624IJ336633;d &@ ^^9120!###&&54$\fN۲h0j@4.(" !++/"!(%  a%.(a_ . 199991/990@ /2O2p22]4$! #"&'532654&/.5467.#"! 1]EtkAJ8s6HX7bFXT`[efZG NJ%94%@uH9/D7'1Zt2UYnm 4Lb@8-*+'0!5 2+A'*,$0-+$!1g3f$cX;eX3cGM299991/29990"32676654&'&&#32654&'2#'&&###2#"$'&5476$yWWWWWVy{WWWWWXϲ##NOM+i`)Gok&: 1mmllmmmmllmm3WWWzyWVVUWWyzWXV5442wyVpP:NAD7nmmmmnnmmmmn1IH@(  2&>f,X c8e XhDJ21/990&&#"3267#"&54632'"32676654&'&&'2#"$'&5476$+9o9q~r@s.A>EyWWWWWVy{WWWWWXymmllmmmmllmmf%#rs~$#WWWzyWVVUWWyzWXVnmmmmnnmmmmn'R v@>  %    ji i ji91<<2<<9990KSXY"73#######5ww㪉LqKBMmf710K TKT[X@878Y]!#f;;1\@1<20K TK T[KT[KT[X@878YK TX@878Y3#%3#1 =@!      <291<2<2.990!3!!!'7#5!7!^P1}@7%     /<291/<90KSXY"K TK T[X@878Y@&W ] !!!!!!!!!{y}sfb^- +@> +,   )*&& &,+,* # )-#7-+,99999999199999990@p- -*'&!/-976!9)?-GYVT!Y(Y)jege!j%j($'))68)KFE I)Z^SVV T!V"[(j ejlaf c!k(x ]]3254&/.#".5!27!"&''\4SM3RJJgfqMLhfqs>;Du1:9@q.dkKMscdOOq /B@#  $'!-!0 $k*k099991<999032654&#"&&#"326#"&546326632#"&+vIZqgLHw+tKZqfMGzDaƯZcG_ů[1CDeOMeeCCdOMeia~q~n .@   l l <2<21/<<0!!#!5!!!  bb '@    <2291/90%!55PN '@   <<291/907!!55%y@B  %     nm n m<2<2999991/2<<<290KSXY"]@, $+6:FI   0@ ]]!!!5!5'!5!! !!!!N9:1k$! %j1`BV3VBT` :@!  !   !2912<990!3265!3267#"&'#"&'idfgdh!'!5]-Yq#/YJhT utqqtG8 KSOO/0;R)8@'! '!* $$*99919906654&#"#"&54632#"&54324&#"32;'#S0@eID`IFa~q9WzC2EqG Ur|tx)w O@    91990@ &#)  ) ( ) 8 ]]!! !!5 Bl_{Nw@pp120!!!!)JD/@    991/<22990#!#!#"663J'7: Ddd>Dǜ3,B*# @- *(&  qr q-9919026732#"&54&#"#"&54632jciRA@Ae '&>ÄkTF32-ӅhB:Yr 7^YUWO\K=4>3:rWT@LHt8;##uu 9A  @  uu 99102#"&546!!"32654&B7T[[TS[[޾ܾM~tt||tt~7F   @  xwx w 9991/<2990!!654&#"!!&5! #~˲˄~#~zx89xz#VˤUy9yǤX{>@B8>66'&# 6-*>;0*? - 6 & 7 3;?<9999912<<<9990@N>>?@MMO@^^_@nno@@@2=0>B=@>R=P>b=`>=>=>=>=>]]4&#""326=>32>3 !3267#"$'#"&54$!354&#"w`gpq[Qe^waGMz =q}~Heߋ"ӆsUf}unLJDMm)JMOMOf~~CD01kdkdŨŸUO..N) +@> )+ *& &&++, #* #)B#LB;,999999991/9999990@@:5 ;75!8)?-IF KGD!H)[VT!U(ikfe!e(5:)EJ)U^(i em( ]].#"32654&'.5!27!"&''XK/w}HO0u|;CDG"jKmFElMpD)A+CN{8,,eP~--^!M@*   "  "<2999919990!3267#"$54677665%!!iAm@84`VQew\N^@D*ii1Q~d:3\/FPDB*(ǾcX:=L-d @ <2991/0!!!33h=^qd@ 10!#!LZ n@*      %  @  9190KSXY"3##'%`w͑%hN7V7#w@I #"!   %   !$2299990KSX92Y"&&#"!!#"&'53267#5!766327.T*Zd!)AD.U)Ycu!3*Bs_sM#;C@!.9* 1 "9*1<-<<219999990#"'&'&'&#"56632326#"'&'&'&#"56632326j`k^Xbian ^Vgj`k ^Xbk`k^V#PE:=MSNE;=KPE:=LTPE:>K  @ /91/90!!!#-3mV?j' 5  @  y y<2991<299055%$'qsq' 5  @ yy <<991<29905-5%%%!$'^ #@   1/<<220!!!!!!hhh}}} 'k'$u 'm'$uffm'2NufP@"     -+ 299991/220@ !!?!O!_!]# !3!!!!!!"# !2i iZhsf / F& 0ihX^{'3t@2"  .(%4"1 1 +B;499912<9990@/5?5O5O5_5o5o55F"]]4&#"!3267#"&'# !2>3 %"32654&w`hA q}~~HRՂG"QRLJBcw}}wu||f}unwf~~CD01QWTT88RVWQ:/10!!/10!!X +@    1<20!3!3!ddXb`Xo + @  1<20!#!#!TeTe`^X@ 10!3'dX`X9@ 10!#Td`V 0@  z{z <<10!!!!!!33Xˁ#uv@A%91990KSXY"  9%-F1'\k'<uh+@55%10KSXY"#3 J=#/@ ! ! $A !* @&00   '}|~-} |022999999122999990'7&&5467'766327'#"&72654&#"ϙљ0l=6l9ϘϚ.j?:l[\[~ Ϛ1k??l.͚Ϛ7n6?i/ϙ\\\]~'y291905%'q'y<91905%%$'+Bu@&       ET<2<<991/<2<2990KTKT[KT[KT[X@878Y@]!!#"!!!!#35463iJK:k$7DN``N'Bl@!      ET<2991/<22990KTKT[KT[KT[X@878Y@]!!!"!!!#3546{L<)7DN`N3;?@!   W VW <<<<2912<220!!!!!!!5!!5!VJ###!<}910!!h}L@ 10!#Te`F + @  1<20!#!#TeTe`^B V #/3?K|@C3 2211 003%@ *$F4 :02$L3IC1!  C=!'= I7' -L9912<<2220KSXY""32654&'2#"&546"32654&'2#"&546#32#"&546"32654& HNNHGLLGֹHNNHHMNGպֺ׺GNMHHLMh{rs{{sr{ؽ۽8|rs}}sr|ٽڽ ؽ۽٨{rs{{sr{ 'k'$uk'(u 'k'$uk'(uk'(uk',duk',du)k',du=k',duffk'2Nuffk'2Nuffk'2Nuk'8'uk'8'uk'8'u` 1/0@ P`p]!!f`yf6@ 91290K TKT[X@878Y3#'#Dzf\9@  @  999919999990K TKT[X@878Y@T              ( ]]'&'&#"#4632326=3#"&7/$&g]$I)=%$(g]$CT%>;+@9X;E10K TKT[X@878YKTX@878Y!!vPF i@  1<0K TKT[KT[X@878YK TX@878Y@]332673#"& cSSc FFJJFw;1*10K TX@878Y!!w1 C @ : 10K TKT[X@878Y32654&#"4632#"&}M67LM67Lvvvv7LM66MM6vvvo5@   991/0K TX@878Y!#"&/32654&'Z:7{0f42S!:A+->j/_[ .(R<fE@991<20K TKT[X@878Y3#3#-fxVo@   991/0!33267#"&546ō2&;1'M(7^)s{6CI'1 \V5myf6@ 91<90K TKT[X@878Y 373Dzx `@2 %    <<.9991/90KSXY"!7!!'%s۔#` j ~@-   %    T <2.991/90KSXY" ]@ut@ P ` ` tp p  ]]!7!'7ho}o XV-k'6ujbf'Vb\qk'=u\Ff']T@ <210##  !L @   -. <291/<20@X!P!`!////////OOOOOOOO________(]]3#32654&#! )#3PULxhgghyk#ateetamX'(@Y&'('%$%(('"#" ! "! 5((5(%('&%"! ## #)'& !#(%" BB;)999919990KSX92Y"KTKT[X)@))878Y@6f!/*76"?*O*oooooooooo]].#"32654&! 4!2''%'!%7l4uru| uj-.N$%3`ox#y-\8 watr`k'<uFf'\ @  - 2299991/0K TK T[KT[KT[KT[X@878Y@,0000PPPP]]!!3 !32654&#=1pzzp]mcenV^;@B @ 2210O`]%!!>32#"&"32654&fJu us{{ssyyb]]7 10!!) /@   <291<290 '7NNNN3NPPN{ 7  @   129035733!9 41Zm]@%   "@99919990KSX9Y"!!56654&#"56632r_9=4I;>TWKGeD 5P(2>-/oHyVZ(W@ #  ""#@#)& )99919990#"&'532654&##532654&#"56632P\fQDB<_hkrJTbZNP4{FAWZ`nQ$%@;@=/3--piE`d @     % @    229991/222990KSXY"333##5!5#335733!9y+ I 41Zd'@   %!   $$& !&"@ #%# (9991/299990KSX9Y"%!!56654&#"56632#335733!qt];>3J<>UXJIc 5N'2?-0oH|T I 41Zh 6:@ : 9988 77: %.1* #! #!$-*$!#917@$$8'" :!'"4  '4-";229999991/2<299990KSXY"33##5!53#"&'532654&##532654&#"56632#3'y%]fQDB<_hjsJTb[OO5zGAVZ-D7#nQ$%@;@=/3--piE`q fk'* 1u\FF'J=k', duo-'6job{'Vbf\k'&fuXuf'Ff\k'&fuXLf'F\$K%@"      @"B;%<<1/<20O&]!5!5!3#!5#"322654&#"FhJu tsyysryyrr+c\II]ɨo10!!ot910!!h}1r@;.*(1.!2*("%!) 2 +) )% 2229999999999122<2990%# '#73&&5467#736!2&&#"!!!!3267_pKXbXMep_Qc-VY2~cTR78 87NO{v$$ zzOO;@ 1<203#%3#mN810K TX@878Y@ //]!#3\#@  @! $  $999991<29990K TX$$$@878Y@\             ##+]]'&'&#"#465463232653#"&8- (kW%J';'%'kW&F#<2j'<9j810K TX@878Y@ //]#yE@ 91<90K TX@878Y@/// ]!#'#f4߲DzyK@ 91290K TX@878Y@//// ]373f߲DzP S@  120K TX@878Y@///// / ]332673#"&`LL`=<<=w*10K TX@878Y!!w  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~>: ~1BSax~ & 0 : !""""+"H"e%  0AR^x}  0 9 !""""+"H"`%^ChVjq_8 (Bbcdefghjikmlnoqprsutvwxzy{}|~f+B{s/) mRo b\}j331 fwwf3ff)u 1 =+'\^fXX\mX{'\RVX\j7dH)7\1 1 fwffXfXfXfXfXfXXmXmXmXmX<XXXXX5}''m-Z;)L'3u'7bXNVL++1 1 f VfXBB  7VhJLL+'3  B B1 w1 ww)fffwV#j\\!X7{mZHdHdHhf\jfXfX\Ro mw   !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~    sfthyphenperiodcenteredEuroc6459c6460c6461c6462c6463c6466c6467c6468%%%%Nq] 6 N{U?u<_O K G E a > *2e:\u#p|$Z<~*Z@!I"dqjw   - : G T a n { !!]!"""##$.$%!%K%%&H''n''((()6){))*<*++p,5,-8-a-}-.B..//\/////00001161U1t112 2292233v344-4L4~5*575D5Q5^5k5x5555555556 67667H7k778'8W889-9:9G9T9a9::::;O;;;<>>>>>>??????@4@T@AA@AuAAB 79k:;Y<$&$&$&$&$7a$8$9u$:$<<$Y$\$h$D$D$$<$r$r$$$$<$%9%:%<%%&/&6&&K&K&&&&'&'nkm>lk llk k@jddjihihg]hhgf%g]g@f%eddeddcba`_.`_.^]\K[}ZYDXWVUSdRQ2POP}ONA@BL JdI"IH2GGFE EDCDkCBCBA BA@ A @ @@S?>->M=<=K<; <<@; :9:]98987 654543432 321 2 2@1 0/0D/.//. ..- d-,+,K+"++@* *d)(0)A(-(0'-'&:% %]$#$S#"##@"! !]     @#$0S-0 k@-B d-    @    @8k d } d2}-2- Sd+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++, %Id@QX Y!-,%Id@QX Y!-,  P y PXY%%# P y PXY%-,KPX EDY!-,%E`D-,KSX%%EDY!!-,ED-ff@ ]/10!%!!fsr)! $@a ` b  10%4632#"&!#5L97NN79LD{h8NN87NM@ b1<20#!#h++W@6 cc       991/<22<2222220!!!3!!!!#!#!5!!5!T32!53!5>54&#"pkh ou=Ŗq 9< mĖ*M@+ etee)te&ei`+  #) #  +99991906632!"&'332654&##532654&#"#u^unp _2 p,. ׫23"f~?@%d d M ss i  <<91/<<290KSXY"/ ] ]K TK T[K T[KT[X@878Y@ 8I( 68 GH ]] !53!53!!3Xtj%m ky@%xetee wb `     9190K TK T[KT[X  @878YKTKT[X @ 878Y@& //]]!>32!"&'332654&#"#T4Vgqq Z5VդT$$23"@C %c@" eely#ei`&    &190@%   ) ) NN N!]%2654&#">32#"!2#.#"DlB%O[q n¾FȽKJy^ejr@Mwb  91/90KSXY"K TX  @878YKTX @ 878Y] #!#!-Nuo1\ />@"$e ee*i`0$ ' -'! 09919904&#"3264&#"326#"$5467&&54632)|{{|lv͟ҟϴ%]@$ elyee i`& #  &190@ !"AAA ]#"5432!"&'5332"32654&CmO[p m¾KJ xRel%"ɽy @a a ` <21074632#"&4632#"&M88ML99LM89LL98Mh8NN88MM8ML99LMJy"@a  1990665534632#"&J^X?M88MM88ME%Z 8MN78MM^@29190 5Ѧf@rr<210!!!!^@<919055//y "<@ a ! ei `#! #10%4632#"&6632#6654&#"#hM97NN79MjT{~rah8NN87NMc/.ֶ3H+ʜ{o@Mm@;BAKE'N$ KE($ Ez Kzz@|$z+ |z+7N'(HA ==1N22991<9999990%#"&54632536654&'&$#"3267#"$'&5476$3254&#"326;]گ];DCjϳZVd_m+`5l}YawLNsyzrRPPR%oT&']gw}LLF]]{GF~b|i}@Qdddd  Mo ob    91/<290KSX22Y"(]@ ((( (,+]! 5333!53!3$H}}jkjjHjqd$E@'o obo ~ $ !$ !%229991/2290%!2654&#!532654&##53#5!2!+w埙jj~}jkäs8@ndn i`&' %10o]#"&'&5!2#.#"3267BapmIzq%0dap\@Aq=@ ob o  $ !299991/220@ 0O]%3 !#53#5! !#7ܺwRPjL66Hjkvtq3R@*  obo ~ $ !2221/00O]353#5!#5!!53#5!!53q{R{{{jk q7F@'  obo ~  $ !2221/20353#5!#5!!53#5!3q{>{{jk ~jsG@"  o ndni` &%1990 0 O ].# 3267!5!# !2#q]ͯiY88kMM_b;<q^@.  o bo ~ $ $$ $!2222221/<2<20@ p]353#5!#!#5!#3!53!3qGGjkkkkjj~jq 7@ obo $$! 221/220@ 0 P ` p ]%3!53#5!#GjjjkkTVm@ dn o b $ 12990K TX@878Y0]KTKT[X@878Y533265#5!##"&rXXw^qGca9kk"q(@Dd  dM  o bo $$!2<2991/<22<290KSX2Y"p]@  $ '6 F HW YYhzz    * % % & &'(: 6 6F F @ F @ F @ F@B@@@@@S X X U UPXXi i e efb```y  y  v vvyyC]]353#5!##5!# 3!3qGͪ3 HapmmpbEnpbNQJKQkdappbcVobcqC@!o ob o # $ !2299991/220_]!2654&#!53#5!2#!3wjkjs"@@nn i`#  %#99999190_$o$$] 47>3 ;.'2#"`jmpbE6mdapbcVm:C@QJKQq7$@H  Mo #ob o#   $ $ !%229999991/<2229990KSX9Y"3!&&##3!53#5!2%!2654&#!Fe(ٶCpbz'[REjۊQjjkп&)'@B#"$!   M !dn'dni'`* !)$)*9999190KSX99Y"+]@ +0+P+p++]KTKT[KT[X*@**878Y@t( ) ) ) )) )!)"8 9 : : : 99:: :!9"8#I I J I HI I!I"Y Y Y Y Y Y!Y"j l l l k kl l!l"i#{ { { { y{{ {!{"y#7++]]732654&/.54$32#.#"!"&sq֯hq|ɹ˭{HTlt7;A©-+ž{kz<7=2B{@  ob o $ $1/2<20K TX@878Y]K TK T[X@878Y@ /`]!53!#!#5!3I{.{Ij` j`bn@%   ob`$$ $$!1<299990`]KTKT[X@878Y]#5!#326#5!#! H廿jkkkkS@9dd  M o b   91/<290KSX22Y"]@"   (* * ']] #5!###5!f jzkkjkk 3@[d d    dd     M  o b 91/<<<290KSX22Y"]KTX@878Y@   %) $?< 5L FX\ Tm d~ t        &&&&)$ &546 4 7 IIEBFE G D H[[XW[X Y [_______feeehgff g g f d ehjjhusvuu v u w t y||yV]]!# ##5!# 3 #5!#y!7TZ9Bjkk9\kk @gdddd   M o bo  91/<2<290KSX2222Y"]]K TK T[KT[X@878Y@ )(%&65EFVUjicdyz        &&'& ()%%$$/6777 8997FCGGEE KIFIIUSSSSWSV V S [[WYYedeedede e d kllke``eee`xzzzz{yx x y {~||@{{}yuxxur]] 3!53 #5!# #5!# 3!53LP@Iuנjjskkkk?jjZ/@E  d d    M  o bo    $$999991/2<290KSX22Y"]]K TX@878YK TK T[KT[X@878Y@d 6 E [Zf xw       **/::7 8 ? 9 ? 9 ? ???F I V Y [ [ i h o h o `vvx x x )]]!53#5!# #5!#33+uu?jkk\kk,j\? m@#  M b     991/0KSXY"]@  O ]]35!#!!53\{^LzHZH$@ssq10!!!!/jjB*@Mb/9910KSXY"#mo@ssq9910!5!!5o/jNj@ b91290##}-m/10!5mPPsdv10K TKT[X@878YKTK T[K T[KT[KT[KT[X@878YKTX@878Y #oudxfD (~@/!  z!#&` z!-" ,' "*)22991/99990@$*o*x(*z( ]]5#"3263!5#"&5463!54&#"#5>32/퉆ts=kn_`VNvzojsJFIydb));!G@%!zqz` 5,'0"2221/990#]7#5!>32#"&'!532654&#"i6{{6ij@jmd__djuʿfD5@  `- *10o]#"5432#.#"3267'ްeekw?31/0|}f!O@& zqz` ,',5 *"<<1/990@ ####]%3!5#"5432#5!54&#"32636{{6fjjd_76_d)jifVDN@z  ` *2190@ @]]!32673#"5432.#"V碞y,}34ױJqq@& zq z - '6,0<2<91/222990]KTX@878Y``?]]#.#"!!3!53#5354632qaSOgT)CBKNqkjjRkf9D,f@3, )z) #`- -, '5&7*-22199990@ ....]#"&'53326=#"54325!4&#"32653iX`}6{{6h[&&h`ďd_76_dkJo@,  zqz =,,' :,',021/<299990/]KTX@878Y353#5!>323!534&#"3Th3l_zj@jVlnjjÏjJ` I@ z z , ',021/20]KTX@878Y4632#"&3!53#5!C/.CB//C갰hq.DD./BB(jjRk;9 i@"   z-6' , 01990/ ` p ]KTX@878Yss]4632#"&#5!#"&'533265C/.CA0/C-fëH>_UR[Wq.DD./BBzkq!!`Z{;@X    >>M z zqz   ,',0<9991/<2290KSX9Y"]@  &))899HVWggghw           ( ( ( &)-**+,)/6 6 6959?F D D DEY X X WVVVh f f gegaabef`x x x zxxvz T]])53#5!#5!# 3!533Ji‘j@j kkjj;R @@zqz,',0 21/20 ]KTX  @878Y%3!53#5!鱱ijjj@jJ^D0@A  +'z)%! z.#  = ", , '?=',?(,* '$,&01291/<<222990@ ?2_2o222]KTX111@878Y@/ / // 2 ]>323!534&#"3!534&#"3!53#5!>32%5n`o{`o{h3d|Xuwjj%jj,jjTijp{JDp@.  zz =,,' :,',021/<290/]KTX@878Y353#5!>323!534&#"3Th3l`yjRklnjjÑjfjD +@  `D *10 o]%2654&#""5432hFc32;VD #U@,  z"z ` $,5!,' 0$22212990%%]32654&#"'#5!>32#"&'3!53i6{{6Hiʵkd__dkkfVD#S@+# zz `$,',5 *$2<<12990%%]3!53#"54325!4&#"326536{{6hkkd_76_dkJD@" z z -,' ,021/2990@8/@@@@@@D@@@  ]]KTX@878Y#.#"3!53#5!>32jNKͦh6z-c)ONjjTioksD)@A#"$!>  > M !''`* !->'F$-E*9999190KSX99Y" +]@X'' '!'"'#Z Z Z Z ZZ Z!Z"X# !"# !"# !"#@++]]75332654&/.54632#.#"#"&sj|_{ֽTcjutwZg;wv]YFV1-,f,*gtRRCQ*-/o,;'qh@  z`, '/<291<2990K TX@878Y@&&/ ]#533!!32673#"&5ݢZ4FHBkJk]LU_7'u@"   z `,',:,' /<1/<2299990K TX@878Y]KTX@878Y!3!5#"&5#5!3265#X3k__z'Cjjo9k'@9    >>M z    91/<290KSX22Y" ]@H' Scv   %*** * *80HG GWXghvvwxx x x ]]!#5!# #5!#yy++wykk%kkD!'@[   >  >> > M  z  91/<<<290KSX22Y"`]@ $, (3= 9DJ IW ^di itz z          &() & # **%&:;??????:; 9 ; 8 8:9FEEE F F I H HHHHFQPPPPPPPPP R T XVSPb``dd````` b d fb`uxy}}yyyz@y v q u v vvwwwtw x]] #5!## ##5!#vęw'kkDkk->j'@g >  >>>Mz z    91/<2<290KSX2222Y"K TKT[X@878Y@ %*GIWWYXffhhyy       (),,% % ( '&&&))'9?9?HIGFGZYYYWY WUVVVVjjjgh h h gefx||||y wttttxvuuu []]#5!# 3!53 3!53 #5!#TߏLl%3kkw7jj>jjkk9'@Y  > >M z   - 9991<29990KSX229Y"]@ZYix))* * ''&XSUPUPSV V hd``dvwx~~tty x x yuuvvvv(]]7#5!# #5!##"&'53326Fsy++w2zo/c2^9<7Cñkk%kkT|[D;=R' j@" >> M z z  - - 991/0KSXY"K TK T[X@878Yif ]35!#!!53RjffBkVf#V$^@0 !   ! s ssq! %$ '%<29991/999999990#"&54&##532654633#"3>l==l>DVdbVititݓhXឈ"XG10#$[@/   ss#sq%' %<29991/9999999903265467&&54&##53233#"##FUbcUF?l>>l?W"Whtitݔ'>@    919999990#"''&'&#"56632326d]` _\Yd^` a\'XTB 9IMWQB:J\'$um )@a dd )('d&d  %"#$M %o  f'#o!$ ('& *%"   *99999991/<29990KSX22Y"]@://   / & ) "!   ///..)(%'&+/]]4&#"326! 53.546323!53!3wY?@WW@?Y#"HHKrrNH}}Z?YWA?XXj%zSrrP#jjHjsu'&Lq3k'(ud^'1us\'2Hu`b\'8uff'D9fd'DC9ff'D9f!'D9f7'D9f'D9fuD'FdfVf'H^fVd'HC^fVf'H^fV!'H^Jf'H`d'CH f'H!'HJ7'Qfjf'Rhfjd'RChfjf'Rhfj!'Rhfj7'Rh7f'XH7d'XCH7f'XH7!'XH9; 6@   b HH  <2<21<2<203%%#5#p##pFsu= @  i 10"32654&'2#"&546LhgMLhjJ@v+..fiMLfgKKk1.-rBPL"I@'  `# -" #222212<2<0%#&5473#&&'667u#dd\PjsdZt,+ .'{ i ldL@(  seis   I <2291/22990#&&#"!!!53!53#534632Ni q`ydt%UK_ekjRk'\= Ak@;39 (',o$ o?i$B3/9)'J1'*'"', 04999991/99990@D --./5``o55/0123-/0123]].#"!534632#"#"&'5332654&/.546wz 6IYwUOmofoPuVhYwZj8l]4M/7cr%#eiyhTuI6ARz$<T@TN N M  1%=1I 7" " PNPN"L7KCL+KCMOU2<99999991/2<229990KSX9Y"32654&##3#'&&##3!53#5!2"32676654&'&&'2#"$'&5476$}SSTR}*;tL#>1\TSS`׃^]__]^⃄^]]^\^ㄘmmllmmmmllmmLKJL3(DF/DDCpmS[j^^]僂^^__^]⃅]^^gnmmmmnnmmmmn2JM@+    ? 3?' REK!Q9K!M-K1/90#"&54632#&&#"32672#"$'&5476$"32676654&'&&`PWTriwyxvaqmmllmmmmllmm^]__]^⃄^]]^\^=%'mf_cnmmmmnnmmmmng^^]僂^^__^]⃅]^^(z@D  # '%!b) &S"7$S P PP TPS$T 7)2291<<22<22999903#3!53#3#53#5!!#5#3!53##^VVV+TVV}-DVVABBBB7VBBBhBBBhRfO10K TKT[X@878YK TK T[KT[X@878Y3#uf77! z@   1<20K TK T[KT[KT[X@878YK TK T[KT[X@878YK TX878Y2#"&546!2#"&546=0EB32BE/EB23BE!E03BB30EE03BB30E'<@!  r r  <291<2<29990!!!!!'7!5!7!}/H{}?f٠f٠#@Udd  ddMo!ob o~  $ "$ $<22991/<2220KSX2Y"]@ !"#0%O%o%%]!#53!3!53#5!#5!!53#5!!53dZ昤XN{P{{{MjHjjk d' +s@:, +&  ) *& nn&i`,,#* # )+#%,99999999199990_-o--].#"324&'7!"&''7&5!27A| "5@}!"{WWoeNWUC`PXVwQ`YYQJuRlSVVEiZUSG /D@$ !- $'!!0 $U*U0999919999032654&#"&&#"326#"&546326632#"&2TevYQ1UevYQG__KDa_/YYie9XXie~९{⦮u *@r  r   <2<21/<<0!!#!5!!!1Ϡ1yy &@r  <2291/90 5!!po &@ r  <<291/90%!555f$K@]dd  M s s sb"s# % #I! I%<<9999991/22<22<290KSX22Y"]@6iih     89969::9F K M MKKJ@@BBFIXVYYYYge g g gffgjhohojjhu y||yzF]]!53!5!5'!5!#5!# #5!#!!!!3hlR)WSGmjoiAikk\kkizTij;V'@3   zzz ` , ,',:, '0 2<91/<2299990!]K TX @ 878Y!]!3!5#"&'3!53#5!3265#X4Z9^'鱦^`y'Ahjo$$kkkh-)6@'! '!* $$*99919906654&#"#"&54632#"&54324&#"32IH7$$0e՘ݢe WOmVPmmWKt,>bFأ[t}t{wJ@#    <91990@  *]]!#'.#!!>?3!5 nNI =DN)u?$ HNh"%!%)/5w'=@"  V WV V WV22122<20!#3!53!3!53#56JJJJJJJJ'J@%z z =,' ,,:,',01/<2220/@]353#5!#3!53!3T!jRkkjjRj/%#@  & XX&1026732#"&'&&#"#"&546327j Pd@7*8  kOeD=!0 l9TA6?&#Hn!bSA8?S}(,q@<&+))&i-) #*#Y#Y -2299991999903#5#"&5463354&#"#566325#"326!!FP0}WtiWfLMBilea\oFTP30pr-T^FEPNQUah^d ,@ i Y Y99102654&#""&54632!!jklihmliԯP-Lװױbh}')M@( n!iw   '%*991/<22990%!53!565#"!3!&5476$32`PuuNsnccotӶF7SF-W`֗g[`_\gfD 8?@G,2*$ 29z*z2/*<'!6`@ $+-?+239**@22999912<2999990@(AoA*+,9?]]5#"326#"&5463!54&#"#5>32>32!32673#"&.#"/퉆tsmS}t_`V7Ju衟y+z\NvzoF[XIxcc))WZXY}[,L} +@?+*&  ) *& &`,,#* # )#+D#*,99999999199990@ -o-wx]].#"32654&'7#"&''7.54327H&pJ-(oJw>@^CL=>^@Lo99Hv1g=;Ly3Jv56?Ms232? "8@a ! e` i#! #10#"&54632#"$54675332673 M87NN79LjU{~q`m8NN87NM/.ֶ3+ʜ{! !@ ai   1/04632#"&53L97NN79LC{Dm8MN78NN5^@ r 10!#!^=} *@    91903##'%\sB}}`s-Pb;#P%@N%%  %!"!""!M!"se ei$&%#"!  &99912299990KSX2292Y"6632#&&#"!!#"&'533267#5!hɦ3vFbKIPW#1\˦2tGbJHPW\ oVRjVRj8l@9216/$#(!6/,(+! /(/6 6!921$#+9<2919999999999990#"'&'&'&#"56632326#"''&'&#"56632326c]\ _\Ye]` a\Xb^` _\Ye]` a\dZT?9ILZRB 9IѓYSB9ILZRA 9I3VM@)ddMwb91/90KSXY"%33^]<;A+%# :@     Z Z <21<222<22055%)+#)+#ssRssRH# :@     Z Z <<1<222<2205%5s+)N+(#^R^sXXs^R^sXX/ #&@a! `$ $1<<220%4632#"&%4632#"&%4632#"&%M87NN79LVM87NN79LVM87NN79Lh8NN87NM88NN87NM88NN87NMk'$u^'$us^'2Huwu!^@0 obo~    %"2299991/0 #0#]%# !! )#5!!53#5!!53q7#3P{Ryy{jjb_ fD ,3k@/ '--z  0*$`4'3 - !*429912<2<9990@P5 -3]]%2654&#"!32673#"&'#"5432>32.#"h碞y,IEςFIzF}ba`c32c``cױb/10!!bb/10!!b/@  i  991<290#667#667LE|ME}@?[P@?[r3@  b  991<29066553%66553NF~@LE}+?=[P??[%@ i  91990#667%ME}@?[@ b  9199066553NF~+?=[y '@a ar  <<104632#"&4632#"&!!M87NN79LM87NN79L8MN78MLU8NN87NM#u"@91990  9%-9!'\DZ\'<u+@M`i10KSXY"3#7Rh\#/o@= -'! 0 -!-'!0 *$0* $ $*099999991999999907'#"&''7&&5467'766324&#"326^+))-`8wE@}=_))*,_8xEBzQrpqq^z@Fw9^,*(pprs##@Z2105s)+#ssR##@Z<105+(#^R^sXXJ'"}@1z"q z - =,' , ,'0#<221/<222990@ $/$o$$]KTX###@878Y'.#"!3!53!3!53#5354632/^z{Ǯ갰WYVUdCjjRjjRk`Ju@. z zqz  =',, ',0<2<991/<222990/]KTX@878Y!3!53#"!!3!53#53546ף'ٮ갰Vjj@dkjjRk`9;\@1  b  H  H <222<22212<2<22<203%%%%#55#p##p##p##pFE%'BL  a  104632#"&M97NN79M8MN78MLZ@   9199066553Z_WE%ZZ`2@     991<29066553%66553Z_W;_XE%ZPE%Zq L #0<@L|@B?@=@=>?>M G$jjGj=*j1?7`A=iM$>0-'@!' :  - :4! D4 J M9912<<2290KSXY"2#"&546"32654&"32654&"32654&#'2#"&5463#2#"&546WddWVbcXcdWVbcXbdVVbaU"ZܻۻZݦ!\ܻۻ ۻۼk'$uq3k'(uk'$uq3\'(uq3k'(uqk',uWk',u_\',uqk',usk'2Husk'2Husk'2Hu`bk'8u`bk'8u`bk'8uJ`' @@zz,',0 21/20 ]KTX  @878Y%3!53#5!갰hjjjRk?f[@ 91290K TKT[X@878YK TK T[KT[KT[X@878Y3# #ttfJ7@  [[99991<299990@A            ]K TKT[KT[X@878YKTKT[KT[X@878Y'.#"#>3232673#"&9!*0`f[&@%9"+0`f[&@Z7OL!7PKb+(10K TX@878Y!!V)9H n@  [[120KTX@878YK TX@878YKTKT[KT[X@878Y332673#"&` hddh ` HOGGO7u! -  10K TK T[X @ 878Y2#"&5460EB33BE!E03BB30E \@  [[10K TK T[K T[X@878YK TK T[X@878Y#"&546324&#"326sssszX@AWWA@Xssss?XW@AWX#u"@    991/90!#"&'532654&'B@?~p*X.)O#9B,,@p1QY 5-X<f:@ 91<20K TKT[X@878Y3#3#rtfxLw&@   9991/9990!33267#"&546^WC8:$C q|<{/.8  YQ1i?fL@ 91<90K TKT[X@878YKTX@878Y33ttxV)Q@-  o bo  $ $ !<2299991/290353'7#5!#%!53{FF3F{jq\kk\-{a@% z qz, ' , 0<2<9991/290P]KTX@878Y%3!53'7#5!7ꮊ=Ǯf`VjbVk'6usf'V\?k'=uRf']@ G<210##  q M@%  obo   $!<2299991/22<200]%3 !#!!53#53#5! !#8ܼPyȾRPjL66H}1je}kvtfj-a@3-,+'$#"!( `(q.-, #"! '($+ D*.99999999199990 /o/].#"32654&#"5432.''%.'7%5-Q(/l#K4I2%%=_w\B% pاy/"5k7N:QV^DNZk'<u9f'\DqM@&oo ob o # $ !222299991/220_]!2654&#!53#5!#!2#!3wp9jkkj;V #S@+ "z zq` $,5!,' 0$2221299990%]32654&#"#5!>32#"&'3!53i6{{6Hijmd__dkk1@ r10!!ӢD /@   <291<290 '71s33r4rP13p4pq3 ?@Mh \ 12990KSXY"535733fTzj^TZk@6   M   h  \ 9999199990KSX9Y"#56632!53!5%6654&#"FBEJVJaTN^ "hzlKMzBUcLd*R@,& )&h+  #)#\\ +9999199906632#"&'532654&##532654&#"#}I;j_wyHDFb\^ffe5acQLRWFlcHdwdrzJMXR]_JJJCH@A''5 d?''5dd''5 dsm'* 3uf9H'Jq^', uu'6suD'Vsk'&5uff'F^sk'&5uff'F^f)d@2!z! 'zqz` $,',5$ **<2<91/<2990@ ++++]%3!5#"5432!5!5#5!3#54&#"32636{{6Ffjjd_76_dJjujjiZZs10!!ZsBL  a  104632#"&M97NN79M8MN78MLq4p@;-s+!s1edei`#5-+$(#,"5 .!, ",(4( 529999999999122<20#"#73&'&5467#7332#&&#"!!!!3267q+筽/--/c|jz/1w/t0h1"0h.>Eh3 Dh吏7 k@   1<20K TKT[X@878YK TK T[X@878YKTX@878Y2#"&546!2#"&546=0EC21CE/EC12CEF.2CC2.FF.2CC2.FP10@ //]K TX@878YK TX@878Y3#?uJ@!  [[999991<<99990@2         ]]K TK T[KT[X@878Y@)   ]K TK T[K T[X@878YKTKT[X@878Y'.#"'>3232653#"'0)'4`fU$>71,)/ag^CH> 9.dv 7/ir-qP10@ //]K TX@878YKTX@878Y#u?u@ 91290K TX@878Y@/// ]KTX@878YKTX@878Y3#'#tt?c@ 91<90K TX@878Y@//// ]KTX@878Y373tt5P@'  M  h  \<<91<<290KSXY"!535!533#3TNriTTR7 N@ [[1<0K TK T[K T[X@878Y@  ]332673#"&^k\\k^7667u}|u -  10K TK T[X  @878Y2#"&546/FC22CFF.2CC2.F  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~>: ~1BSax~ & 0 : !""""+"H"e%  0AR^x}  0 9 !""""+"H"`%^ChVjq_8 (Bbcdefghjikmlnoqprsutvwxzy{}|~f7q3!JZ?JJqsjqqqdsq)q5TqPq1fdsbqsq{V`9  H\f;{fffJf'JJ{;;;J'Jf;fJs7;'7!7Rsqds`ffffff{fffffJ 'Jfffff'7'7'7'79\3X;d3;#h^5BJ+/}^}fLJ7=#3s wfHVh33VJVJ9Z%Z qqqq)q)W)_)qsss```J#LZV-{s\7RuqfHhq;55Z5dddsf)q{ss{fs{ffZ55+   !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~     sfthyphenperiodcenteredEuroc6459c6460c6461c6462c6463c6466c6467c6468c6469""""Tu_Ox2A?c9^  t  \ U ~(uR%~H;f"sa4{-i4/n/J !"G""##r##$$$$$$$$$%% %%'%4%A%N%[%h%u%%%%%%%%%%&&&H&&'5''(()*%**+7+{,,--6-f-.r./=//00`0112>233H3d344455`555556;6667!7\7~778 88%8I88999:M:l::;t;;;;;;;;;;<<<<*<7/>a>>??P??????@S@@@A?AAAB$BBBC CC*C7CDCQC^CkCxCCDDD5DEEQFF9FFGGTGX7 J79k:;<$7$9$:$<$I$W$Y$Z$\$$$$$$$$%&%&&%*&%2&%<%d&%g&%&%&%&%%&%&%&%%&%&%&&&''&''9(&))))))$N)Du)H)R)bN)iu)ju)ku)lu)mu)nu)p)q)r)s)y)z){)|)})u))N)N))N)N**&**<**--a--.k.$.&.2.8.:.<.H.R.X.\}.b.d.g.h.p.q.r.s.y.z.{.|.}.~...........}...........}../7Y/8/9 /:N/<}/\/h////}////}/1}1}1122K2292;3a33a333$D383D3H3R3V3bD3h3i3j3k3l3m3n3p3q3r3s3y3z3{3|3}333D3D33D3D3333344K44&4&57595:5<5D/5\5i/5j/5k/5l/5m/5n/5/5&55555566K66666777777$77&7Da7Fa7Ha7Ra7Vk7Z7b7ia7ja7ka7la7ma7na7oa7pa7qa7ra7sa7ya7za7{a7|a7}a7a7a777a777k7k7a7a8D88D888$8-8b888899D992929$u929DD9HD9L9RD9X}9\9bu9g9iD9jD9kD9lD9mD9nD9pD9qD9rD9sD9yD9zD9{D9|D9}D9~}9}9}9}99D9D9u9u999D9K9K99u9u9999::k::N:N:$:DN:HY:L:Ru:U:X:\:b:iN:jN:kN:lN:mN:nN:pY:qY:rY:sY:yu:zu:{u:|u:}u:~::::u:u:::u:&:&::::;;$;&;2;b;d;g;;;;;;;;;;;;<<<<<<$a<&<Da<HN<L<RN<XN<ba<d<ia<ja<ka<la<ma<na<pN<qN<rN<sN<yN<zN<{N<|N<}N<~N<N<N<N<<<N<a<a<)<a<a<<==IIII&IINRUUY Y Z Z [\\b7b9b:b<bIbWbYbZb\bbbbbbbbdde&f}f}ffggKgg9g;hDhhDhhh$h-hbhhhhyz{|}&K9;79:<IWYZ\79:<IWYZ\K9;&$-/99:9;9<9b99$-/b$a&DaHNLRNXNbadiajakalamanapNqNrNsNyNzN{N|N}N~NNNN<Naa)aa79:;9<YZ79:<IWYZ\&79:<IWYZ\&&K9;K9;K9;DD$-bDD$-bDD$-b7Y89 :N<2\h22K6K$9<b$a&DaHNLRNXNbadiajakalamanapNqNrNsNyNzN{N|N}N~NNNN<Naa)aa&&<K6 UE@ ^m L GBGSf JBits@ mB'#sVeraSerif6628R00                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                     "                      "        #                       #         $                   $        &                   &        _<K Lmconnectome-workbench-1.2.3+git41-gc4c6c90/src/Resources/GeneralResources/Fonts/LinuxLibertine/000077500000000000000000000000001300200146000320145ustar00rootroot00000000000000GPL.txt000066400000000000000000000442731300200146000331320ustar00rootroot00000000000000connectome-workbench-1.2.3+git41-gc4c6c90/src/Resources/GeneralResources/Fonts/LinuxLibertine GNU GENERAL PUBLIC LICENSE (with font exception) Version 2, June 1991 Copyright (C) 1989, 1991 Free Software Foundation, Inc. 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This General Public License applies to most of the Free Software Foundation's software and to any other program whose authors commit to using it. (Some other Free Software Foundation software is covered by the GNU Library General Public License instead.) You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things. To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the software, or if you modify it. For example, if you distribute copies of such a program, whether gratis or for a fee, you must give the recipients all the rights that you have. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. We protect your rights with two steps: (1) copyright the software, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the software. Also, for each author's protection and ours, we want to make certain that everyone understands that there is no warranty for this free software. If the software is modified by someone else and passed on, we want its recipients to know that what they have is not the original, so that any problems introduced by others will not reflect on the original authors' reputations. Finally, any free program is threatened constantly by software patents. We wish to avoid the danger that redistributors of a free program will individually obtain patent licenses, in effect making the program proprietary. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all. The precise terms and conditions for copying, distribution and modification follow. GNU GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License applies to any program or other work which contains a notice placed by the copyright holder saying it may be distributed under the terms of this General Public License. The "Program", below, refers to any such program or work, and a "work based on the Program" means either the Program or any derivative work under copyright law: that is to say, a work containing the Program or a portion of it, either verbatim or with modifications and/or translated into another language. (Hereinafter, translation is included without limitation in the term "modification".) Each licensee is addressed as "you". Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running the Program is not restricted, and the output from the Program is covered only if its contents constitute a work based on the Program (independent of having been made by running the Program). Whether that is true depends on what the Program does. 1. You may copy and distribute verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and give any other recipients of the Program a copy of this License along with the Program. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Program or any portion of it, thus forming a work based on the Program, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) You must cause the modified files to carry prominent notices stating that you changed the files and the date of any change. b) You must cause any work that you distribute or publish, that in whole or in part contains or is derived from the Program or any part thereof, to be licensed as a whole at no charge to all third parties under the terms of this License. c) If the modified program normally reads commands interactively when run, you must cause it, when started running for such interactive use in the most ordinary way, to print or display an announcement including an appropriate copyright notice and a notice that there is no warranty (or else, saying that you provide a warranty) and that users may redistribute the program under these conditions, and telling the user how to view a copy of this License. (Exception: if the Program itself is interactive but does not normally print such an announcement, your work based on the Program is not required to print an announcement.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Program, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Program, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Program. In addition, mere aggregation of another work not based on the Program with the Program (or with a work based on the Program) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may copy and distribute the Program (or a work based on it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you also do one of the following: a) Accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, b) Accompany it with a written offer, valid for at least three years, to give any third party, for a charge no more than your cost of physically performing source distribution, a complete machine-readable copy of the corresponding source code, to be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, c) Accompany it with the information you received as to the offer to distribute corresponding source code. (This alternative is allowed only for noncommercial distribution and only if you received the program in object code or executable form with such an offer, in accord with Subsection b above.) The source code for a work means the preferred form of the work for making modifications to it. For an executable work, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the executable. However, as a special exception, the source code distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. If distribution of executable or object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place counts as distribution of the source code, even though third parties are not compelled to copy the source along with the object code. 4. You may not copy, modify, sublicense, or distribute the Program except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense or distribute the Program is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 5. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Program or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Program (or any work based on the Program), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Program or works based on it. 6. Each time you redistribute the Program (or any work based on the Program), the recipient automatically receives a license from the original licensor to copy, distribute or modify the Program subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties to this License. 7. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Program at all. For example, if a patent license would not permit royalty-free redistribution of the Program by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Program. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system, which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 8. If the distribution and/or use of the Program is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Program under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 9. The Free Software Foundation may publish revised and/or new versions of the General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of this License, you may choose any version ever published by the Free Software Foundation. 10. If you wish to incorporate parts of the Program into other free programs whose distribution conditions are different, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. As a special exception, if you create a document which uses this font, and embed this font or unaltered portions of this font into the document, this font does not by itself cause the resulting document to be covered by the GNU General Public License. This exception does not however invalidate any other reasons why the document might be covered by the GNU General Public License. If you modify this font, you may extend this exception to your version of the font, but you are not obligated to do so. If you do not wish to do so, delete this exception statement from your version. NO WARRANTY 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) This 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 Also add information on how to contact you by electronic and paper mail. If the program is interactive, make it output a short notice like this when it starts in an interactive mode: Gnomovision version 69, Copyright (C) year name of author Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, the commands you use may be called something other than `show w' and `show c'; they could even be mouse-clicks or menu items--whatever suits your program. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the program, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the program `Gnomovision' (which makes passes at compilers) written by James Hacker. , 1 April 1989 Ty Coon, President of Vice This General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Library General Public License instead of this License. LICENCE.txt000066400000000000000000000022341300200146000335410ustar00rootroot00000000000000connectome-workbench-1.2.3+git41-gc4c6c90/src/Resources/GeneralResources/Fonts/LinuxLibertine- Lizenz / Licence - Unsere Schriften sind frei im Sinne der GPL, d.h. (stark vereinfacht) dass Veränderungen an der Schriftart erlaubt sind unter der Bedingung, dass diese wieder der Öffentlichkeit unter gleicher Lizenz freigegeben werden. Querdenker behaupten oft, dass bei der Verwendung einer GPL-Schrift eingebettet in beispielsweise eine PDF auch diese freigestellt werden müsse. Deshalb gibt es die sogenannte "Font-exception" der GPL (welche diesem Lizenztext hinzugefügt wurde). Weitere Informationen zur GPL (Lizenztext mit Font-Exzeption als GPL.txt in diesem Paket). Zusätzlich stehen die Schriften unter der Open Font License (siehe OFL.txt). Our fonts are free in the sense of the GPL. In short: Changing the font is allowed as long as the derivative work is published under the same licence again. Pedantics keep claiming that the embedded use of GPL-fonts in i.e. PDFs requires the free publication of the PDF as well. This is why our GPL contains the so called "font exception". Further information about the GPL (licence text with font exception see GPL.txt in this package). Additionally our fonts are licensed under the Open Fonts License (see OFL.txt).LinLibertine_RBIah.ttf000066400000000000000000020220341300200146000360440ustar00rootroot00000000000000connectome-workbench-1.2.3+git41-gc4c6c90/src/Resources/GeneralResources/Fonts/LinuxLibertine FFTMY ",GDEF ݭHGPOS>UL(&ZGSUB2)6OS/2N<`cmap<F=cvt |0fpgm蕏gasptglyf_'D,Vhead#jF6hhea32$hmtxZ loca]maxp. Ь namep`:post#٨@preptH#oB_>2yzz{}~QRRS_``adegh\]]^| $,4<DLT\dlt  ds= fg DFLT cyrl0grek@hebrPlatn\(AZE (CRT (DEU (MOL (ROM (TRK (cpspkernmark $*\ X ft " x                                                                                                                        x$%&'()*+,-./0123456789:;<=   "$&(*,.02468:;?D6N  $+\++{+=}R+n0&6 VYZb U $*06<BHNTZ`flrx~ &,28>DJPV\bhntz)sjow j5qLqom-))u)R)+-)'+)+)+)+++''xJL+#)'ob)) +q)d)b3+m++w)w)N)b))w)b)){))))))N)b)N)N))b))w)q)Ps))g +)$$&&(*-.014= DDGKNNPVXZ!\\$%&'()*  +BC,CC./128ccRffST+4567 &,28>DJPV\bhntz "h^N\h?fi)8`wu8T)i)+)q)) )))))@)))))H))q)y)+)9'bGRq+7v "(.4:@FLRX^dhH!J^^JJ &*0178"(.fnht`>H >DJPV\bhntzN!)TjZRB'TuZZ`ZuZZZy1ZZ`ZuZZZL DGHKLRVWXwf1 ? "(.4:@FLRX^djpv| -/wwou B#R%/LH^?Thh+shTh?/y?$&(*.0156789;<=DKLNOPQRUVWX fz `*v Q "(.4:@FLRX^djpv| $*06<BHNTZ`flrx~-%V#%%w%d##%J#-s%=%%){%b`%%b##3%}!%L%%%/DXmm7mXXmD=}$$&&(*..015=DEGLNZ\\&'()*+00,-./567GffP   #%)+, $*06<BHNTZ`flrx~{ 'FBBB#.9Lq #]?I rAKx ~t1Rq11L111o1111Lq1111Lj3jjjjL111L11111d}     ""##$$ ''))+,..// 1122334477889:;;<<>>BB EE HH IIJJNNRS UUYZ[[\\^^aamm}}         $$&&**,,..00224466778899::AA??08 <ADEFF GIJJ KKRR ]^y aa    !""$$&&**--224477889:<<@@ DD EEFH JJ KKMMNOPQ RR SS TT UU VV XXYZ[[\\]] `` aammoorrtuyy{{}}       $$&&**,,..0022446677::<ADEFF GIKKPR ]^`aegkx WW`` "#$')+,./1234789:;<>BEHIJNRSUYZ[\^am}  $&*,.0246789:A?012345678<=>?@ADEFGHIJKR]^yz{|}~a =3q[L ==)))))%      ""##$$%%))114488==??DDJJMMWWYY[[]]__OV""##))**//1155 78 ::;;==??@ACC DDEEGGJJLL5   "#$%)148=?DJMWY[]_OPQRSTUV4 ) ))$$.. 9:;; <<::   "##%%,-AB    "##%%,-AB7$.9:;<:    !"#%,-AB vDFLT cyrlPgrekhebrlatn   (AZE TCRT TDEU (MOL ROM TRK T   aaltc2sccaseccmpfrachligligaligalnumloclonumpnumsaltsinfsmcpsmcpss01ss02ss03supstnumzero      8@HPX`hpx  `  *  P `   4 p  *. \]I LMXVZ^fjnz  $(,048<@DHLPTX\`dhlptx|  $(,048<@DHLPTX\`dhlptz~ &,28>DHLPs#{tumnopqruab`_J      !"#$     !"#$%,-./0+123J&&4F56789:;<>G@ABCDEt "K$D  .59:>@DEFGHIJLMNOPQRSTUVWXYZ[\]^`cm}  #')13<>@]^  IH=?L[(      !"#$%,-./0+123&4F56789:;<=>?G@ABCDE[DEFGHIJKMNOPQRSTUVWXYZ[\]  !#%')13<>@ALF(> "*2:BJRZbjpdddd~d "dd&.6dddddd#& >@C^`cm}]^\+vwsk{tumnopqruLMNOPQRDlFSuTxUVWGvXYZKwL[tqk  D]  ()*Z*yz{|}~wxyz{|}~y  D]  ()i      !"#$,-./0+123J&4F56789:;<=>?G@AB%CDE( "i $%&'()*+,-./0123456789:;<=>@^`cm}   "$&(02:;=?]^LI&2DNhr g"`R fe"X]\WIW )&"4IWrWF.js 4gjklmnopqrs 4gg   jq ss2jklmnopqrs  `  ab.5("**BTILM. ""779:<<@@IILM_`hhlltuxx{{--//DEHIef LM \]I|3;34fP PfEd! ''`o) 66~)NouEMWY[]} ' 7 > B D K O q !!! !!!!!!! !"!$!'!.!9!!!!!!!" """+"6"<"A"E"I"Y"b"e"k"q""""###!#*#%%%%%%%%&&&&&'&/&S&`&c&f&l&&&'g',l,w!)/>Ijm!08KU^ad 8 (Pt HPY[]_  / 9 B D G O p t !!! ! !!!!!! !"!$!&!.!5!S!!!!!!""""#"6"<"A"E"H"Y"`"d"j"n""""### #)#%%%%%%%%&&&&&'&/&9&`&c&e&i&&&'g',`,t ,9HQmp08HMW`X 8~MGECA?>=<;98754210,%$! i^RM@>5 bPD5.ߺߪߨߤߢߞߝߚmZYWOH?310.9ݻE>^%%%%%%%%%%%%%%%%%|%v%q%p%n%m%i%R%D%=%.%-%,%+%$#8"}f    !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`aLrdeiNxpkvj=+s@Agw>5l|'cn(:m}Rb?@HIDEg:d]^MyFJUZpqlmnzqo[ @ "+ !7 !!f]=]f\H  \R fxD*@'b =M>  +2#"&76767>462"&:? >$4# eZ{VZ{VDPJ;V:5J2kCNL3;VR;9VRJ)#9K&PX >[Y-% +'7>32'7>32*  R9&$M%& R;6 M7J\ (G"[[ \ )F)#.g*KPX@)  dS  K  = >K&PX@)  deS  K  >@1  de    TGK?YY@)**)('&#"!   +7#%3#####7367#73>733>733P8oP,UfVj.E .WlUd.&&ۇ7?b8?b;<;<78 Ih@ 2 @be=M >Y@HGFECB.-,+*)'& +>76'.'#7.#&76.'&7>76?63a@cUdHk E:.Bn& % # ~.>1c&(@dNrI#:'*Z>u+5.@#p[I_1 T>pci ic7_Crc  } n213UC~,S )D &4A@ <:K PX@+UUUU M  >K PX@+UUUU M >@+UUUU M  >YY@ ?=%$%$&#%$" +327654#"'632327'#"'#"74327654#"'632#"542?`85>%#0dO%"s-oRcvdB= ncy2?`85>%#0d?Qycx$RzBj?de(!f+}/&x$RzBj?de:Cx ; Q\Z@W[R<3<b`M ==N=M>YWEC641/,+(&3( +676&#"6'&?3732>72#"&''"'&7>7.7>3276%3267&68K)5n yM *K6c  !:Ou'ϵQW%1 KmU{x'^_TI% +'7>32  S9&$M8J\ (G".L{ "+7&.69f'quHc|+ FAq dF "+'79'fruGc}*E`&SAqe";8_@  .#@dVNBY@ &'#''%+.&7>326&7>32>32#"#".'#"&7>#ET( $^8(K+u"'E$O( /* 463 =b ?)[+v0%"]/R14.!?uw9!140B4w @eT>+!!#!7!3A!GG AG'MP 3 9K!PX@ d >@ d[Y@  +2&7>76.#.7>&Q? ]549B    f|4-,C *-  P)"LZ @IMA   +!"5463!2jL@JA+Z+^R} @M> +7462"&RZ{VZ{Vw;VR;9VR2>uWK PX@ = >KPX@ =>K-PX@ d>@ d[YYY@  + #>nzu`e!*@'UM>!! +"327654&"&54762 ZI8xcaX,f3ԟn?o`9\Tv'me`gyn'Vk|ja]*&@#<ddK >')C+%&#"&67>7>&#"&76$762 .  OM )-A&&  W|! >>JC$!($7&  4  3 %E121 3:{!#ul?33@0<b`UK >&+/!+632!2672!&676?>54&#"#"&54tt6U&toJK!B;6-%I:-ݨYK`Do" L/+17s 8;'B Q#jeDU2gs 0t[jM_@\-.7%}<;@84<b``UM>&#&/%#+"#"&547632!"&'&54632327>54&#"&767>54wM "X%7kNzB!nyciKJF..90O$V3pI($K>(qN]Df$8,&AO1MO$e@"XjI'.*AK7Z1XPkt9&-td:4;@8,<dVK >32&$ 44 +)2+.#"&67>?!"/.7676673}b. .# =J"M 25@' 2  #[U4!,R*='  4  3  "K>R% )Tn2i@<:KPX@!bXUN>@"b`UN>Y$%-54+4'./32>7"'#"'&546323276,8".)T8t$&J-d/ 8R`AfY:<32H=\:Z(P   y_ (%nzW;99%7-C.!&6DhU.)@&<-:ddM>)'$+3267>54&#"67#".547>75[.#R)JB?R"t`\AVN@f?wf?ʟ\jm00.PQt?Ey,++9,9&$   +">54&'"&54767'&'4763227>54&/^%.NU7J>If]k\_˜ΊZ#}Ύ/kA!01<\{PX)3r]15 R@PEDNlDhwS?hmnp[FZI&|C.K)@OuD[;.0@-<-9bcIMA)'$+654&#"3667>7.5467>32& 5\.#R)KA?R"t`\AVN@e?xe?ʟqjm00.OQuEx+7462"&462"&[zVZ{VyZ{VZ{V;WR<9VR;WR;9WR /w #P9K!PX@bM= >@bcM>Y@  # # +462"&2&7>76.#.7>Z{VZ{V6Q@ ]648B    f;WR<9VRX|4-,C *-  P)"LdX"+71 ~?[?#k/@,SGK? +!7!74zssuu{ T"+%77?ZD.8/@,b`M =M>,.&%+#"&7>7632#"&?>7676&#">2"&C-'8 A@k 9`?;*4A '$ *Q:2M?f ft? gt?=&.A63N/P6^V0'-hD(!$,7^^/da@K(PX@* U  UUIMA@/ U  UIUIMAYY@ KI%%&$*(%$" +6&#"27632>76.#"327# 6,32#"'&7#"'&7>$3278 !XU1(l^~aeth8Zr֧$Lsr~J"Q(q).w 8*~2=nz*V/16GNU 4agHaNn?A?87641-*) +!#&#"&67>7273&#"&672>&7!"+C  / u' 3;L%wJ %- x % -9D5 ^  2  1 =>[l  2  1  N# - ?K!PX@!UM  =M >K$PX@'ZUM  =M >K(PX@-ZZUN  =M >@+ZZ UUM >YYY@ :90/+&?>  '#! +32654#" 32>54&#263 !"&#&547>76'4&'&547In|X:dd3CfQ%xz6m?! 118! =K ' DtHfs}p.E!  #K>51">#" D7@4 <bM =M>  + '&7!2'.#"3272Ց`B?-jp# 1'//%V)Fy)/r3-5K PX@M =M >KPX@ZN =M >KPX@M =M >K!PX@ZN =M >K(PX@$ZZN =M >@"ZZUM >YYYYY@,&"!52($ +%32>54.#"&747>7674&'&7473263 !"&#JYZW I`y 117! ?L O#8*0k'%:(y+P}h7"  #K>51!?#" S1B@8  @8 ZbZ   U U=L >Y@?<432/,+&%" BB +26762"'6&+;26&#!&67>76&'&67!272'.+"fP B!  B AbZ$!y5!Y_2a oI1m 0C3/%1Hg#9J-R 7} H7/)9 = 1 ;y3{; 2  q%wd='1C@'8? @4Zb bU  UK >Y@CA>=#83C& +%&#"&67>76&'&67!272'.+"326762"'6&++  118 1m 0M30%1Hf#9RfP A   B Ac#7' 65  #K>3{; 2  u%yl='X-R 7~ H7D4H@E)<bUM =M>-+  44 +23.#"32676&'&6727# '&7%6%%-Zƶ:)TdPf<; ?Z w /5 -sX=?DH} TyI$219# 8  8 +3!T`XBο4-]kK(PX@%  S K  = K >@#   U  S K >Y@]\WVSOLK@?33C+>.'&67327& &67>7!& &67>7>.'&67327!)#  127! + !  118! NN+!!  117! )#!  117! >/+<& 65  #K>+<& 65  #K>m+<& 65  #K>5+<& 65  #K>4-,AK(PX@K =K >@UK >Y3C+>.'&67327& &67>7)#!  117! +!!  117! /+<& 65  #K>+<& 65  #K>L-(tKPX@ZRK >K(PX@bRK >@!bUINBYYC%$'+#"&7>323266&'&67327B\@37z `$ 9?@57{ Łd `C-hq9'wR?)J+/{; 2  1 =4Q-\S@UM >Y@XWTQNM?:OC+>.'&67327>7676'&67327&#""56'.'& &67>7)#!  117! D.imJ 9 %H3L'\/G/Y !N/#$J+!!  117! /+<& 65  #K>BWA 2  1 5I%HJ 2  9W) +<& 65  #K>4-/a@ZUK >Y@('$ /. +%2676&#!&67>7>.'&6732>33937>9I 117! )#! O#I@ 117! !FV}5  #K>5+<& 65  #K>ɢr-=K PX@/bX  `K =  K  >K!PX@0b`  `K =  K  >K(PX@4b`  `K =  K  = >@2b`  `U  K  = >YYY@<;8410G" +%#'"&'#&#"&67>76&'&673%&#"&67>Frs  #Z wi3w RIg  ' b7 le T b.X V\ 2  1 N`Ls* 2 s 1 5V3 2  1 --<K PX@bK = = >K(PX@bK = =>@bU =>YYYMJ$+!#&'&#"&#"&67>7.'&677>.'&673273^!   v'1< & qKF; P)n'1< ( qI'4/))HK)  2  1 ?V-P 2 BH@HK)  2  1 ?D,@)M =M>  +"32654&"56%2~:\xw )(eqe {4H;9?@< <UM =K >43('$! 97 +2"'&73267#"& &67>7>.'&672$`$))# '9y;L9+!!  117! )#! '!;C^pb;+Im+<& 65  #K>5+<& 6+D7V@S)*65<b``M =M>20-+(&$!  +"32654&67&56%2232327#".#"'6~:\xwݜþ )(cs\EqJV^}<"..*?eq _ e "H^^K-DOD,/>3z; GN@K<U M  =K =M> A@762/+*%$  GE#" +4&#"32>23#"'.#& &747>7674&'&7472$-yXL9NTJxh: %V@o? >K  117! ?L '!q^Imi%SVb?RH? %d'1">#! "  #K>51!?#" [5D8|@&@,b`bM =M>Y@ ,%.%#+#"'.#"#"&#&5476322654&'.547>325''R1Z\8gU%/̌ekvz;Sm v=V`2w_F$GeCoDh(%#w t`VtIQq}*269/7K PX@ $&<KPX@ $&<KPX@ $&<@ $&KPX@&b = M =K >KPX@"b M =K >K&PX@&b = M =K >@$b U =K >YYYY@ -*153 +%& &67>76&+""'6743) 72#.+"N8z  }N5ad33B( 9is-39{; 2  1 ;{^mbw  s`fk-<AK(PX@K =M>@UM>YC-O%+32>764.'&67327# '&6&'&67327d4R`\H!c 4(8 4 7+3 ]_m' 2  1  %E@/۬ZVu,Jy= 2  1 ;X-.rK PX@K = >K(PX@K =>@U>YYY@ -+"F +>7>'#"''&>7>'#"'27h1cL 'u %- b1 d,<; y 1/3 -1XT5 2  1   'P\!"  2  1  1-/0-<b9K PX@  = >K(PX@  =>@ M>YYYJ%(M+76.'&67327"&'"&'.'&673277^u 'w /# v' Ne-1#N3#411 zǓ 7A-\<VJ   2  1 5T/1/b-/  2  1  #$A59 >-QOK(PX@K = K >@U K >Y@ POCCCC +%.&#"&6767>'.'&673277676'&67327&#"&67>ywZ= Sdx O'A@ w P%d u= HRv N!%KD w NB# d\ 2  1 T!!+P36 1 59! s\ 2  1 V#)3-N5 2  1 9-=EK(PX@K =K >@UK >Y@ MCC+%&#"&67>76&'.'&67327676'&67327J9{ r }N6 =A wV P# ; =Bv JI)){; /  / <{90-R1 2  1 59 Z 2  1 #9J3w-9/K PX@<KPX@<KPX@<K$PX@<@KPX@+b`M =N = >KPX@'b`M =N >K$PX@+b`M =N = >K(PX@/b` =M =N = >@-b`U =N = >YYYYY@&  /. +%2672#&)"767>&#!""'6743)26323\^85N- % q5=: Z7x Z| jT   BAu@K> +!A > !H[J u+ ) (T^0<5  + @e> + #Qu@K> + !.7>7>.'&67z ? !G\J  ) (T_0;5  , W3@<e > + #3#n~Qin8n@GK? +!7/ZZN@e>'% +'"/?>32hc#1) +9N@K0<bM =M=N>9731*)'% ++ +232672#"'&74?'#"&547>3226>?&#"32* WX%#N ZCkpY|n &S"@fhZX}+9]n<&7)'C3H%q׾r  R:01'ng *-@*b=M=N>%($##+3254'"'632#"&76&#&7>32"E8yCGk/@X(|f2d {=9hZMhC!.LD&tkf4(Dc@$b`M=M>Y$$%"+%#"'&7632#"&76&#"272^J\T\--E J-/6"/N%#cā Vpjw{Z1A7)9Xk2w-0@O@L420.-<bb=M=M=M>#+&&#'% +6&#&7>3232672'"747'#"&547632677474&#"726763e {=X%#h^Vw7X,D7}T9{N)f4(DZ<%7)&t8X}{+ef?2I`;cK (1@.<bM=M>'$,!+4#">32672#"&54>32A"5;8/[`:mJvV(Vm'V}su@l9Ah"=h6@X!opR{NV9h>f/"@K*PX@1b  bM=M= N>@/b  bUM= N>YY@ 86$)##$$#$ +#"5476?7632#".'&"32+#"&546323267>76 1;}m73'3MRZ# /!=%`=09Nrw./J-!"A -!)wmK(F-0$Ot =/fALOU1%:J7, 2s%m A`@2@*&@(UM=N=M>Y@XVFD?=;9531/*)% +2654&'&#"'#".54%67&547.54632672#"'&"4&#"32><^j?u\)RӍT7bFVJ7<Ť@bj)6+!+@%=# %  L5&%[;--'`w#5H7L\+V;s \Z!HLY2"#.*:6jyd "1? >1 #1<=D@A-<bb`=M=>($&%%&+%#"&54>76&#&7>3263232672#"'&7654&#" -P71e {=wHnN'Y%#FmL L#$2l?. !4f4(DPT+p}7)'ZbC3GP'-D ,=@:<b``M ==>%$$$"+4632#"&32672#"'&7654/&67632KD;JR?;HW%Y%#FmLZ $R я* 3LT4-RSn}7)'ZbC-9'4 +9]$'#$/%+654/&67632#"&54632326764632#"&!= $R я* #=2QXyHd2.;"%9"8LC;JR?;Hh);'4 +;YsG5>C,0.8.hR93LT4-RSIV@S <<bb  b``=M=>HF#)"$)%& +%#"&54>76&#&7>326?>32#"'&#"3272#"&'.'&#" .P72d {=*>Rj3?G?0&.JRjP77 .<+T=Y42 ?. !4f4(D6F^sK.7X6I^l9)%W#ZX`r-"!)@&b`=M>#'%+6&#&7>3232672'"5474d {=Y%#hf4(DZ<%7)&t9Y^H@E(<bb` M= >XVLJ)'$&%'% +654/&67632>326232672#"'&7674#"#"&54>7674'"#"&54>7+%R уVHnHnM'X%#FlL!K3:ZW -P7\)#dRPN -P7V;4 VyjPTPT+p}7)'ZbC3GP'Jrr<+ $5R95Teh 8* $5@=@:!<b```=>)($&'%+6'4/&67632>3232672#"'&7654&#"#"&54>7$R уjHoN'Y%#FmL L"%3nN ,P7 V;4 VylPT+p}7)'ZbC3GP'- 8) %4 @M=M>&%%!+724.#"47632#".y1'}b\?\9O`1MFI1Gsm0;N@K%!6 <9dbM=N=>#&&;&!+632'"'&#'54?67"'6?>324#"32>yspR@P' P'\f-%Fp8V  fPl?XR9;Hq`qC!' = ; NT;IXwe=k8 -8FXe 54@1*:$&'$"+4&#"32676'#"&5476327>32&#'54?>=E76&9#GۇqwNM@) T' P'11?WRZcsSilDA6H ) ' )/1@.<b`M=>"$'%'+#"&54>7654/&67632>32#"'&#")N -P7 L%R у@K?A?0&-)` 8) %3_R;4 VyJ/7X6th4'@$bM=M>+"$*.+&'.#"#"&5463232654'.54>32h5#>  +D;#纲<P3+q'ChEsq=b+= (# 5J=2{odb-@bUM>Y@ #$$!+7#"5476?>7>3#32672'"54} 2l =A+)zX%#h !f<' \ L :%7)&t<;FK@H<b```=N >#*)'$&' +%067'#"&5476#""&'>323267>3232672#"'&74s ZFpN'X$#Fl+- "L42osO ,P7 kX%#N %pSUSDF}7)'Zb!"4?ir? 9* %3@&7)'Co#`K PX@b= >@b=>YY&#()+%676'&5432#"'&'"&'>32^ 2 |7D7>DK PX@b=M>KPX@b=M >@b=M>YYY),&##+''"'&'"&'>32>76676'&'432#".'_AE +N#2N@=@ 1@N;KOm2 |7E7P^#_+# h:+ /]l/!D@lAKJ6+Y1s|/106=R@O<+ $<#;Z bN=M>97#'$$"$&%! +&#"&'>327>32#"'&#"327#"&/#"&543232767&,*HI/$%51KyY9U7(..763UM:?'B.SVK7Zc,d )z%#"4*^5UDni4./J$163^@UZwb4^3<2d754lT|37@4<bb=M>$$(-%"+.#"&'>326'4&54632#"&546323276 +MH0%/7C7L)0T.:y]^-`C'8 ;?rv $4*`DSoqe(/0L^9>?R $1O3g@d +<:9  bZ````M=>21#!!#) +632>76#"'&&'677"#"&#""'>3236+" ;]G706.Ibn]TA$#" +3".76'&67>7>39~s`H P;C >`V*V1 etD] &1B-jx!/w΁  -LsP\ ( zWym9  7h#@=> +#3t t#sP*@eM>#" +>?.76&#&672#&67>9~r`HQ;C >aV*V1 etE^ &1B-sTx!/w1  -LsP ( zWm9  7g+@( <IUMA!%!"+>323276#"&#")V/-B!D576Rmju M5FMK&PX@bM=>@bcM>Y@  +"&7677632"&5462:@ <$4# eZZ{VZ{V5PJ<^V;4JkBNL;VR;9VR &@ " ! K PX@)db``U >@)db``U>YY@ &+3"&76'272#7&'&767A} K\9 -!OBR B N^,,C(J0C7)d!\ u?Mm cwrCzJ-@ 12K$PX@7bb   bU U M   >K1PX@;bb   bU U M =  >@;bb   b eU U M  >YYY@A?:864/-*)'%  JJ +"&?6;7>762#"'.#"32+23267#".#"#"&767>?' Z; {c1>  K1*' rF ?">Ii,9-{/(3c@|d"'  A!#p 3LV )1So."4A5 0 !"?3\@?)-#R<>s ,I@F"'#+)TTSRQPGC@?430,C+#&#"&67>?#737#73&'.'&673277>.'&673273#  /., g C=9bC; sK N$ L  &" (,u CW&0jV,9* 4  3 'DBVVDHOH 4  4 )&#I 4  4 4@ddd[Y@ +# #Fslsds$DH\8@5PIH"<b`QM >970.*()%" +$#".7>3232676.'.767&7>7672#"&7>76&#"67>.'&@3ԷKQQ"55+3'CjC1-\/ R7s U$6-% .EMe6E54S&/  X`<+-,H@1(C9U&?O9 $kc%EA%_y[u5~,\)=M$2 (6tWBI'!;+DD[5W+IQM6O0K 83*-$-%p' "@<IMA$$$+>2#"&%>32#"&CX:C,-8D+-8B-,90@A/.AA./AA/.AA/*5@ @2b UUUIM AY@,+! 20+5,5'& *!*#"&" +#"&'&7>32#.#"327"&76 "32({B9h:r+A# -;3_/d;ʔX!3fz--[Z3G-)X| a/J1kzGX R!F-- !-5d@ /$@#bUIMAY@ +#$'$&#+>&#"#"&7>32327#"&7#"'&7>77326YU6! Cb#:"&   W($NQD $ h&@ 5HN+ "! 9['=*, # $D Q;[Ǥ3VJ/kB! "+76767.%76767. &m=A-/e2 & nED:9KIDNEN@?9C:C54#$$233"+3#"/&#;2&#76;276"&?6326"2>"&76 "32 H9 a: )(  gl_ FaBV(2&6$X!3fz--[Z/N"N  _:q9b R!F--;;$@! <IMA  +!"'47>3!2F !%;(,\ >KPX@UM>@UIMAY+4&"2%462"&sDwEGs}}jXV75RZZX]NoKPX@#dbTK >@)dbTGK?Y@  +%!7!!#!7!3++YA!FG AGLMP O-K/PX@!b`OM>@'b`UIK?YY*(*"+>3232672!6?676'&#"#"w hs)/F0Sb8*S( W:V %< 7B3w*.ZH?,AM(3K4 @%b`UR>Y@/-*( 44 +2654"7>54&#"#"747>32#"546329n{ # N$"? 3&5 Uf|mq4$ JT> fI#-#6,.x?6[K%\YNZ!5!' 4h %KPX@ e >@ d[Y$! +632#"&767 M!+' d1/! u%;tK$PX@ <5/$:@ <5/$:YK$PX@bM=>@dbM=>Y@ :842+)(% +%#"'#"&76767>7>7>?%3267%327'"&  57Y.9 V2&U "/  Q P 0@( .)A>c'# #k(>ER;5]0H}q-.Aaz0,nK>s:!+=k$@bUK>Y@;860'%!  +267654&'"732!76;>7#".5476$323?+")#) %=  a <3%5m1^dJ1#Z =3' X7H+#+-)n D)!++/P3L}M;4 +4Kjd @IMA   +"&54632J^eCBfbjcCBd`FDbHZ@@ddINBY@ (% +'73#".'6732676. F7 d $I*/=<1 b&Mk, +)%2Y@ddIK?Y@  +"&767>&#"&67>76&X ] ,s'99: $(1B 4F,H(  ) /&31#m (@%UIMA&" +>32#"&"32676&vY14,}vVA:  .).Co~#%CpNcf-?D#Ee{w/B! "+&'67&'67&'67&'67 &m=A,/f2 &m@7 bb ` eS   U >Y@ FE@?=9650/+)'&%$ "" +'"&767>&#"&67>76&737#6'#!6726'.#Erq4W ^ ,s'99: $(1B  ntVa(A 0($ :97 > % )3F-H'  ) /%31#+V-L+&/ ) !'++^X"Pd@a7< b  b `S  UO >PNDB:96531'% "" +'"&767>&#"&67>76&>3232672!6?676'&"#"Erq9X ^ ,s'99: $(1B  hs)/F0Sb8*S( W:V %< 6B)3F-H'  ) /%31#3w*.ZH@,@M(3K4 @P b  ` ` `  b `e  VU M >Y@$#]\SQNLGF?=8620)'#X$XC"+'%737#6'#!6326'.2654#"7>54&#"#"747>323#"54632#Erq ntV`(A 1($ :: 7 9n{ $N$!@ 3&5 Uf|mq4#  %  +V-L+&/ ) '_IT= fJ"."6,.x?6[K%\YNZ!5 'n++3-!.8/@,b`M=M>,.&%+6&7>32#"&7>7>?>32326"&7>2&D,'9 A?k 8`?;*4A ($ +P;2M?g ft? gs?>&.A63N/P6_V/(,hE(!%,7^^/da?D NLEDCA>:76+*" % +32676'&!#&#"&67>7273&#"&672>&7!"_`5 %R+C  / u' 3;L%wJ %- x % -9+Fw Q5 ^  2  1 =>[l  2  1  N#  PM@J-<:ddd   VK >OMFEDB?;87,+#& +#"&7676!#&#"&67>7273&#"&672>&7!"6$B &$f+C  / u' 3;L%wJ %- x % -9? w O5 ^  2  1 =>[l  2  1  N# BOD@ANIGE:dd VK >A?87641-*) +!#&#"&67>7273&#"&672>&7!"&'&76?+C  / u' 3;L%wJ %- x % -9#EemD5 ^  2  1 =>[l  2  1  N# &6[YRQPNKGDC87/+    +272#"'&#""&7>32!#&#"&67>7273&#"&672>&7!"B/&v:L31A&%l4W: P+C  / u' 3;L%wJ %- x % -9XXf+#%%Rl345 ^  2  1 =>[l  2  1  N# RU@R/<b`U   V K  >QOHGFDA=:9.-%!+462"$462"!#&#"&67>7273&#"&672>&7!"LAcAAcHDb??bp+C  / u' 3;L%wJ %- x % -9bcCCcAAcCCcA#5 ^  2  1 =>[l  2  1  N# BMWf@c <  b `  U V M =K >DCWUQPIHCMDMA?87641-*) +!#&#"&67>7273&#"&672>&7!""2676&>2#"+C  / u' 3;L%wJ %- x % -9.<4\=6qdoIGD5 ^  2  1 =>[l  2  1  N#C0-FE.1BdcGEe1ah/@$+ :K(PX@PZb  bU  SN == L  =  K  >@NZb  bUU  S= L  =  K  >YY@gecb`_\XUTMLGFC>3##:3#+%6&#"?!272'.+"326762"'6&+;2676&#!&67>7!&#"&67>!6&/K c60(2 Kk$=RlT F  F Bhc &"o:7 2,bd >:8>,! y) Ja7d  713o$ve>&-R {6+ G8/)9w t. 3  %FA' 4  3 6 /+HD:@$1/@.b bQM =M >Y@::(%#$)& +'7&'&7!2'.#"3272!##".'6732676.K q~`B?-jp# 1'//%# d $I*/==0 pV)Fy)/r@&Mk, +)% O@E '<:K(PX@? ZbZ U= N  ==L >@= ZbZ   V U==L >Y@ LIA@?<9832/*$! OO% +32676'&26762"'6&+;26&#!&67>76&'&67!272'.+"`5 &SfP B!  B AbZ$!y5!Y_2a oI1m 0C3/%1Hg#9J+Fw Q-R 7} H7/)9 = 1 ;y3{; 2  q%wd=' P@F (<:K(PX@? d ZbZ U N  ==L >@= d ZbZ   U U=L >Y@MJBA@=:9430+%"PP& +#"&767626762"'6&+;26&#!&67>76&'&67!272'.+"$B &#f2fP B!  B AbZ$!y5!Y_2a oI1m 0C3/%1Hg#9J? w O-R 7} H7/)9 = 1 ;y3{; 2  q%wd=' O@E '< :K(PX@: ZbZ U N  ==L >@8 ZbZ   U U=L >Y@ LIA@?<9832/*$! OO +&'&76?26762"'6&+;26&#!&67>76&'&67!272'.+"#EelfP B!  B AbZ$!y5!Y_2a oI1m 0C3/%1Hg#9J&676&'&67!272'.+"=BbBBb=Db@@bfP B!  B AbZ$!y5!Y_2a oI1m 0C3/%1Hg#9JcCCcAAcCCcA-R 7} H7/)9 = 1 ;y3{; 2  q%wd='4 9R:K(PX@=K =K >@U=K >Y@ 3C%+32676'&>.'&67327& &67>7*`6 &R)#!  117! +!!  117! +Fw Q+<& 65  #K>+<& 65  #K>4 :S:K(PX@dK =K >@dUK >Y@ 3C&+#"&7676>.'&67327& &67>7$B &$f|)#!  117! +!!  117! ? w O+<& 65  #K>+<& 65  #K>4 9P :K(PX@K =K >@UK >Y@541.+* +&'&76?>.'&67327& &67>7#Eem)#!  117! +!!  117! &6+<& 65  #K>4<[K(PX@!UK = K >@UU K >Y@ 873C +462"$462">.'&67327& &67>7RAcAAc>Cc??c)#!  117! +!!  117! bDDbBBbDDbB+<& 65  #K>+<& 65  #K>3-A6K PX@"S M =M  >KPX@( ZS N =M  >KPX@"S M =M  >K!PX@( ZS N =M  >K(PX@. ZZS N =M  >@, ZZ  USM  >YYYYY@82.-(&%#A>!"($ +%32>54.#"67&'&747>77674&'&7473263 !"&#JYZW I`yP|gfz 117! @\VNbN ?L O#8*0k'%:(y+P}h7ag"  #K>Jg1!?#" ShV7 K PX@1  bU  U K  = = >K(PX@1  bU  U K  = =>@/  bU  U   U =>YYY@OK>=:90,"   +272#"'&#""&7>32#&'&#"&#"&67>7.'&677>.'&67327B/&u:L31A&%l4W: 3^!   v'1< & qKF; P)n'1< ( qIXXf+#%%Rl3'4/))HK)  2  1 ?V-P 2 BH@HK)  2  1 ? )6@3:=M =M> $")) % +32676'&"32654&"56%2`5 &S~:\xw )(+Fw Qeqe { *7@4:dM =M>%#**& +#"&7676"32654&"56%2W$B &$f ~:\xw )(? w Oeqe { )3@0 :M =M> $"))  +&'&76?"32654&"56%2#Eemµ~:\xw )(&6*)1/)6*6#!((  +272#"'&#""&7>32"32654&"56%2B/&v:L31A&%l4W: ~:\xw )(XXf+#%%Rl3eqe {1,:@7UM =M > '%, , +462"$462""32654&"56%2qAbBBbkCc??cF~:\xw )(cCCcAAcCCcA\eqe {% "+''%'7%kB$o!HVV`V{\'7@4  <: 9M =M>*)'+#"''7&56%2& 72656]h^y )׏~:\ QKxw\3{e3e v e>9y/q, IR:K(PX@=K =M>@U=M>Y@ C-O+%+32676'&32>764.'&67327# '&6&'&67327`5 %RHd4R`\H!c 4(8 4 7+3 ]_m' 2  1  %E@/۬ZVu,Jy= 2  1 ; JS:K(PX@dK =M>@dUM>Y@ C-O+&+#"&767632>764.'&67327# '&6&'&67327$B &$fd4R`\H!c 4(8 4 7+3 ]_m' 2  1  %E@/۬ZVu,Jy= 2  1 ; IP :K(PX@K =M>@UM>Y@HGD@=<64'# +&'&76?32>764.'&67327# '&6&'&67327/#Eeld4R`\H!c 4(8 4 7+3 ]_m' 2  1  %E@/۬ZVu,Jy= 2  1 ;NWK(PX@ UK =M>@UUM>Y@ C/L' +462"$462"32>76&'&67327# '&>.'&67327FAbBBbCc??cn%UdaK%j M' ) 3 cgs@}#/5 ^qO bcCCcAAcCCcAVm-f?\yQ- 4  3 )9+/۬ZVt-JAG&  4  3 < KV:K(PX@dK =K >@dUK >Y@ MCC&+#"&7676&#"&67>76&'.'&67327676'&67327$B &$f|9{ r }N6 =A wV P# ; =Bv JI))? w O{; /  / <{90-R1 2  1 59 Z 2  1 #9J3w4-=Is@ > '@%U  UUK >Y@ IE&3*5C +>.'&67327632#"'& &67>7%32676&#"#)#!  117! L[U= Tۀw* +!!  117! /+Vq#@[/+<& 65  #K>":KRS%>|O C+<& 65  #K> "$]@ V,KPX@4bZM== N=N>@5bbM== N=N>YY@QOCA=;64%# ]] +%"&7>76&"32>54&'5>54&'&'"#"#".'.#"3267676?67632@..3NB\wui.)Fu+`!`J ()@? /2wrO7:'@4 C"Ad1FBA#/B [c%A|'W/TӞX5  uD? */;$1UOLPTf@NF94Jh:H?) @6dd  b M = M=N>Y@HFB@9864.,#!::%% +#"/7>32232672#"'&74?'#"&547>3226>?&#"32i)X0* WX%#N ZCkpY|n &S"@fhZXd ),+9]n;&7)'C3H%q׾r  R:01'nh+9G0@6 d dbM =M=N>Y@CA=;9731*)'% ++ +232672#"'&74?'#"&547>3226>?&#"32632#"&767* WX%#N ZCkpY|n &S"@fhZX M!+( }+9]n<&7)'C3H%q׾r  R:01'n1/! N+9Db@_BA?=< 0<b  =M =M=N>:::D:D9731*)'% ++ +232672#"'&74?'#"&547>3226>?&#"32&''67* WX%#N ZCkpY|n &S"@fhZX5{9}FX.H}+9]n<&7)'C3H%q׾r  R:01'np'SWO['_ESq@nJ4 <  bUU M = M  = N   >SQMKDCA?97.,)(&$EE  +272#"'&#""&7>32232672#"'&74?'#"&547>3226>?&#"32B/&v9L31B%%l3W: * WX%#N ZCkpY|n &S"@fhZXXXf+#%%Rl3+9]n;&7)'C3H%q׾r  R:01'n+9EP`@]0<b   UM =M=N>ONJHDB><9731*)'% ++ +232672#"'&74?'#"&547>3226>?&#"32>32#"&%>32"&* WX%#N ZCkpY|n &S"@fhZXB-,9D+-8C,-8CX9}+9]n<&7)'C3H%q׾r  R:01'n0@A/.AA./AA/.AAh AOF0 @=  b UU M = M=N>Y@$OMIG@?=;53*(%$" AA    +"2676&>32#"232672#"'&74?'#"&547>3226>?&#"32+=-V@1tHF\ vHGc* WX%#N ZCkpY|n &S"@fhZX#9,);;),9ccGEe+9]n<&7)'C3H%q׾r  R:01'n} QK(PX=<=@5bU  M= M  = M   >Y@ HFA?:843-+#!QQ  +">7654&6732"#"&54$326>32327#"&/#"&5476%65&P+^U_63:[8m`V]7: #Jd;ZH=ZL!Bup;Sif-} *O)=Z8Ldq&H8!f :1=@6b`` bQM= >Y@99(*$$% +'7&'&7632#"&76&#"272#".'6732676.S kK\--E J-/6"/N%#cā CZ c $I*/>=0 `w{Z1A7)9Xk1On9&Mk, ,(%h (7t@ @'ddbM=N>Y@ %+'$,!+4#">32672#"&54>32#"/7>32A"5;8/[`:mJvV(Vm'V}su@lj)X19Ah"=h6@X!opR{NV9h>f| ),h (6t@ @'ddbM=M>Y@ $''$,!+4#">32672#"&54>32632#"&767A"5;8/[`:mJvV(Vm'V}su@lO M! +( 9Ah"=h6@X!opR{NV9h>f|1/! N (3E@B10.,+<b =M=M>)))3)3'$,!+4#">32672#"&54>32&''67A"5;8/[`:mJvV(Vm'V}su@l5{9}FX-G9Ah"=h6@X!opR{NV9h>ffp'SWO['_ (4@?@<<bUM=M >$$$('$,! +4#">72672#"&54>32>32#"&%>32#"&A"5;8/[`:mJvV(Vm'V}su@lB-,9C,-8D+-8B-,99Ah"=h6?X!opRzNV9h>f0@A/.AA./AA/.AAh /z @.bb``=M>Y@ %*%$"+32672#"'&7654/&67632#"/7>32YW%Y%#FmLZ $R я* ^j)X1n}7)'ZbC-9'4 +9] ),h .w @+ddb``=>Y@ $&%$"+32672#"'&7654/&67632632#"&767YW%Y%#FmLZ $R я*  M!+' n}7)'ZbC-9'4 +9]1/! N +E@B)(&$# <b`` ==>!!!+!+%$"+32672#"'&7654/&67632&''67YW%Y%#FmLZ $R я* "5{8}GX-Hn}7)'ZbC-9'4 +9]p'SWO['_' +7?@< <b``U=>$$$%$" +32672#"'&7654/&67632>2#"&%>32#"&YW%Y%#FmLZ $R я*  CX9C,-8D+-8B-,9n}7)'ZbC-9'4 +9]0@A/.AA./AA/.AA#4=@:+< :M=M>%$.,$4%4!$ +67>32&''7&'7>7#".2>767&#"1[R/7Nt W3CTRIG?=9864.,%#  +272#"'&#""&7>326'4/&67632>3232672#"'&7654&#"#"&54>7B/&u:L31A&%l4W9!o$R уjHoN'Y%#FmL L"%3nN ,P7 XXf+#%%Rl3@ddM=M>Y%)&%%!+724.#"47632#".#"/7>32y1'}b\?xj)X1\9O`1MFI1GsmQ ),h *VKPX@"b =M=M>@ddM=M>Y$%&%%!+724.#"47632#".632#"&767y1'}b\?` M!+' \9O`1MFI1GsmQ1/! N '8@5< =M=M>$"  +&''67724.#"47632#". 5{9}FX-Gy1'}b\?Np'SWO['_9O`1MFI1Gsm$6E@BU UM= M  >31+)$"  +272#"'&#""&7>32724.#"47632#".9B/&v9L31A %%l3W: y1'}b\?XXf+#%%Rl39O`1MFI1Gsm (4,@)UM=M>$$$&&%%!+724.#"47632#".>32#"&%>32#"&y1'}b\?B-,9C,-8C,-8B-,9\9O`1MFI1Gsm0@A/.AA./AA/.AAF/ WKPX@SQM>@!USIMAY@ $$$"+%4632#"&4632#"&!7N1#-P/#-N1#-P/%+/L+"3H*/N-#1J+dd%:@7% <: 9M=M>$$*(+#"''7&547632724'&#"F]Nb`DVDVR}kJwJ M4nWI1'g:faMF*koo/;hFU@@9 d  db```=N >Y@USNL#*)'$&' +%067'#"&5476#""&'>323267>3232672#"'&74#"/7>32s ZFpN'X$#Fl+- "L42osO ,P7 kX%#N.j)Y0 %pSUSDF}7)'Zb!"4?ir? 9* %3@&7)'C ),;hFT@@9 d  db```=N >Y@PNJH#*)'$&' +%067'#"&5476#""&'>323267>3232672#"'&74632#"&767s ZFpN'X$#Fl+- "L42osO ,P7 kX%#Nb M!+'  %pSUSDF}7)'Zb!"4?ir? 9* %3@&7)'C1/! ;N Qh@e! <b```  ==N  >MKHGEC97.,%# +&''67067'#"&5476#""&'>323267>3232672#"'&745{8}FX.H ZFpN'X$#Fl+- "L42osO ,P7 kX%#NNp'SWO['_ %pSUSDF}7)'Zb!"4?ir? 9* %3@&7)'C;FQ]a@^L <b```    U=N >\ZVTPNJI#*)'$&' +%067'#"&5476#""&'>323267>3232672#"'&74>2#"&%>32#"&s ZFpN'X$#Fl+- "L42osO ,P7 kX%#NCX:C,-8D+-8B-,9 %pSUSDF}7)'Zb!"4?ir? 9* %3?&7)'C0@A/.AA./AA/.AAT|h3A~@ @+ddbb=M>Y@ $%$$(-%"+.#"&'>326'4&54632#"&546323276632#"&767 +MH0%/7C7L)0T.:y]^-`C'8 ;?rv M!+(  $4*`DSoqe(/0L^9>?R $11/! 7B[@X40=$<)9bb=M=M=>#&;&&%+6&#&7>32632'"'&#'54?67"'674#"32>2d {=5yspR@P' P'\f-%F\fPl?XR9f4(DHq`qC!' = ; NT;IXwFXT|3?JJ@GF<bb U=M>IH$$&$$(-%" +.#"&'>326'4&54632#"&546323276>32#"&%>32"& +MH0%/7C7L)0T.:y]^-`C'8 ;?rvB-,9D+-8C,-8BX9 $4*`DSoqe(/0L^9>?R $1(0@A/.AA./AA/.AA 3BNX@U <  b `   U VK >ECKHCNENA?87641-*) +!#&#"&67>7273&#"&672>&7!"!"7>3!2+C  / u' 3;L%wJ %- x % -9 %'D5 ^  2  1 =>[l  2  1  N#(*+9E_@\0<b  UM =M=N><:B?:E3226>?&#"32!"7>3!2* WX%#N ZCkpY|n &S"@fhZXe %'}+9]n<&7)'C3H%q׾r  R:01'n() BPZ@W NLGEA?87641-*) +!#&#"&67>7273&#"&672>&7!"#"&?3267+C  / u' 3;L%wJ %- x % -9ho:rXTD5 ^  2  1 =>[l  2  1  N#kl9678V:H_@\?) <:  bU M = M=N>HFB@9864.,#!::&" +3267#"&7232672#"'&74?'#"&547>3226>?&#"323jdX?y{:z$* WX%#N ZCkpY|n &S"@fhZXVQQIYd5;+9]n<&7)'C3H%q׾r  R:01'n !DNUR@ON  <7: d d   VK = M>PORQOUPUMK#L'#F! +'"'&7467"#"&672>&7!"&#"&67>7273&#327!#R34P|V % -9 / u' 3;L%wJ %- LoHN$#+C q!5nI1 1  N#  2  1 =>[l  2 bV%5 %=Kj@gB,  <#;  b  b M = M=N>KIEC<;971/&% == +232672327#"&547'"'&74?'#"&547>3226>?&#"32* WX%#PlLN+N%RPf N ZCkpY|n &S"@fhZX}+9]n<&7)'g0F^"%/q^RC3H%q׾r  R:01'n-C@@ <# :dbM =M>(&  + '&7!2'.#"3272#"&7676Ց`B?-jp# 1'//%$B &$fV)Fy)/r? w Oh-KPX@1bb` =M=M>@.ddb`M=M>YY@ $#$$%"+%#"'&7632#"&76&#"272632#"&767^J\T\--E J-/6"/N%#cā u M!+' Vpjw{Z1A7)9Xk21/! ,>@; <+&$":bM =M>  + '&7!2'.#"3272&'&76?Ց`B?-jp# 1'//%#EemV)Fy)/r&6@*b` =M=M>Y@)('&"   +&''67#"'&7632#"&76&#"272 5{9}FX-GJ\T\--E J-/6"/N%#cā Np'SWO['_dVpjw{Z1A7)9Xk2'A@><bUM =M> $#" ' ' +462" '&7!2'.#"3272{LfLLfՑ`B?-jp# 1'//%fLLfL@V)Fy)/rD+z@.b`M =M=M>Y@ $$$$%"+%#"'&7632#"&76&#"2724632#"&^J\T\--E J-/6"/N%#cā KD;JR?;HVpjw{Z1A7)9Xk23LT4-RS-=@: <,'%:bM =M>  + '&7!2'.#"3272>767&Ց`B?-jp# 1'//%DemV)Fy)/r6=C0 *U ~h*x@<)(&$#:K PX@(dZ`M=M>@)db`M=M>Y@ $$%"+%#"'&7632#"&76&#"272#&'767^J\T\--E J-/6"/N%#cā wg,6rGR,Vpjw{Z1A7)9Xk2{[$XTKa$35CB=;:K PX@M =M >KPX@ZN =M >KPX@M =M >K!PX@ZN =M >K(PX@$ZZN =M >@"ZZUM >YYYYY@,&"!52($ +%32>54.#"&747>7674&'&7473263 !"&>767&#JYZW I`y 117! ?L O#8*0k'-Dem%:(y+P}h7"  #K>51!?#" Sw6=C0 *U ~w-?BRe@b54FDB@- <b  bM = M=M= M>/.OMJH;9.?/?&&#'% +6&#&7>3232672'"747'#"&5476322'>76#"&7>677474&#"726763e {=X%#h^Vw7XV71 d>W 88D7}T9{N)f4(DZ<%7)&t8X}{+ef?2[;B # W)+-!+I`;cK3-=6K PX@"S M =M  >KPX@( ZS N =M  >KPX@"S M =M  >K!PX@( ZS N =M  >K(PX@. ZZS N =M  >@, ZZ  USM  >YYYYY@4.*)$#"!=:($ +%32>54.#"3#&747>7#73674&'&7473263 !"&#JYZW I`yP 117! BN ?L O#8*0k'%:(y+P}h7cR"  #K>RR1!?#" Sw47Gf@c;975" <  b  b T = M=M= M>DB?=440.&&#$+#32672'"747'#"&547632#736&#&7>32677474&#"72676X%#h^Vw7XF4a {=D7}T9{N)O<%7)&t8X}{+ef?22OX0(DI`;cK7BN@8  @A ZbZ   U   U U=L >Y@$ECKHCNEN?<432/,+&%" BB +26762"'6&+;26&#!&67>76&'&67!272'.+"!"7>3!2fP B!  B AbZ$!y5!Y_2a oI1m 0C3/%1Hg#9J %'-R 7} H7/)9 = 1 ;y3{; 2  q%wd='(* (4B@?<bUM=M>+)1.)4+4'$,!+4#">32672#"&54>32!"7>3!2A"5;8/[`:mJvV(Vm'V}su@l %'9Ah"=h6@X!opR{NV9h>ff()BP@8  @@ ZbZ  U   U U=L >Y@ NLGE?<432/,+&%" BB +26762"'6&+;26&#!&67>76&'&67!272'.+"#"&?3267fP B!  B AbZ$!y5!Y_2a oI1m 0C3/%1Hg#9JGgo:rXT-R 7} H7/)9 = 1 ;y3{; 2  q%wd='kl9678V7B@?%<:bUM=M>'$,$&"+3267#"&74#">32672#"&54>32jdX?y{:z$A"5;8/[`:mJvV(Vm'V}su@lVQQIYd59Ah"=h6@X!opR{NV9h>fJ@@ "@@ ZbZ U   U U=L >Y@ GD<;:743.-*% J J +462"26762"'6&+;26&#!&67>76&'&67!272'.+"KgKKg3fP B!  B AbZ$!y5!Y_2a oI1m 0C3/%1Hg#9JfLLfLD-R 7} H7/)9 = 1 ;y3{; 2  q%wd='D 4=@:"<bM =M=M>'$,#$"+4632#"&4#">32672#"&54>325LD;JR@;HA"5;8/[`:mJvV(Vm'V}su@l3LT4-RSf9Ah"=h6@X!opR{NV9h>f!1U@$5< I U  @BZ b  ZU  U= K  = M>Y@TRNLFC@>;:#83V!+'"'&5467"#!&67>76&'&67!272'.+"326762"'6&+;26"'327R33P}U22a oI1m 0C3/%1Hg#9JfP B!  B AbZ$!y5!YKlGN%q!5nI1 1 ;y3{; 2  q%wd='-R 7} H7/)9 = aV%F.:z@6$.@$bQM=M>Y@ #))'%"+#"&547'"&54>32326723274#">'z>Og'V}su@mmJvV(QmGH+N$A"5;8/[`:6:^RsR{NV9h>fP6@X!i;CZ"$9Ah"=hBP@8  @8 ZbZ   U U=L >Y@?<432/,+&%" BB +26762"'6&+;26&#!&67>76&'&67!272'.+" >767&fP B!  B AbZ$!y5!Y_2a oI1m 0C3/%1Hg#9JEel-R 7} H7/)9 = 1 ;y3{; 2  q%wd='6=C0 *U ~h 3?@<!< :dbM=M>'$,++#&'7674#">32672#"&54>32f,6rGR,gA"5;8/[`:mJvV(Vm'V}su@l;[$XTKa$N9Ah"=h6@X!opR{NV9h>f4AO@L)<@;97:bUM =M>-+  44 +23.#"32676&'&6727# '&7%6%&'&76?%%-Zƶ:)TdPf<; ?Z w /5 -sX=?>$DemDH} TyI$219# 8  8 +3!T`XBο&6@.  V  =M=N=M>Y@caQOJHFD@><:)' +&''672654&'&#"'#".54%67&547.54632672#"'&"4&#"32><5{9}FX.Hj?u\)RӍT7bFVJ7<Ť@bj)6+!+@%=# %  Np'SWO['_L5&%[;--'`w#5H7L\+V;s \Z!HLY2"#.*:6jyd "1? >1 #1<4BZ@W)@>97-+  44 +23.#"32676&'&6727# '&7%6#"&?3267%%-Zƶ:)TdPf<; ?Z w /5 -sX=?9go:qXTDH} TyI$219# 8  8 +3!T`XBο`kl9678mVPo@AO9" 5<:K&PX@2U  U M=N=M>@0U  U M=N=M>Y@geUSNLJHDB@>*)(&" +3267#"&72654&'&#"'#".54%67&547.54632672#"'&"4&#"32>< jdX?y{:zej?u\)RӍT7bFVJ7<Ť@bj)6+!+@%=# %  VQQIYd5L5&%[;--'`w#5H7L\+V;s \Z!HLY2"#.*:6jyd "1? >1 #1<<R@O 1<bUUM  =M> 53(%"! < < +462"23.#"32676&'&6727# '&7%6LfLLf5%-Zƶ:)TdPf<; ?Z w /5 -sX=?fLLfLhH} TyI$219# 8  8 +3!T`XBοmD A`l@2@*&@2U M =M=N=M>Y@kiecXVFD?=;9531/*)% +2654&'&#"'#".54%67&547.54632672#"'&"4&#"32><4632#"&^j?u\)RӍT7bFVJ7<Ť@bj)6+!+@%=# %  LD;JR?;IL5&%[;--'`w#5H7L\+V;s \Z!HLY2"#.*:6jyd "1? >1 #1<}3LT4-RSD4JY@V)655J6J-+  44 +23.#"32676&'&6727# '&7%62&7>76.5&7>%%-Zƶ:)TdPf<; ?Z w /5 -sX=? @1   UUM=N=M>Y@bauraybyXVFD?=;9531/*)% +2654&'&#"'#".54%67&547.54632672#"'&"4&#"32><"&76767>32^j?u\)RӍT7bFVJ7<Ť@bj)6+!+@%=# %  -93 :JrT]   7&CL5&%[;--'`w#5H7L\+V;s \Z!HLY2"#.*:6jyd "1? >1 #1<Z;M:I O) 3!64 j| :K(PX@%  S K  = K >@#   U  S K >Y@jidc`\YXMLIFCB;:541.+* +&'&76?>.'&67327& &67>7!& &67>7>.'&67327!##Eem*)#  127! + !  118! NN+!!  117! )#!  117! >&6+<& 65  #K>m+<& 65  #K>5+<& 65  #K>=JK@H-($&%%&+%#"&54>76&#&7>3263232672#"'&7654&#"&'&76? -P71e {=wHnN'Y%#FmL L#$2l#Eem?. !4f4(DPT+p}7)'ZbC3GP'-&6@/US  S  K  >Y@!ba^ZWVPONMHGDA>=650/,)C+!!!>.'&673273#& &67>7!& &67>7#73>.'&673271?*"  027! lj+ !  118! NN+!!  117! {z*"  018 E):& 65  "Hm+<& 65  #K>E):& 65  "HDU@R$ 4 <b  b `T= M= >B@8621&"%& +%#"&54>7#736&#&7>323#63232672#"'&7654&#" -P7҂3b {=_HnN'Y%#FmL L#$2l?. !4P]1(DPNPT+p}7)'ZbC3GP'-4{F~K(PX@*U UK =  K >@(U U U  K >Y@BA>;87,+($!   +272#"'&#""&7>32>.'&67327& &67>7%B/&u:L31A&%l4W9!)#!  117! +!!  117! #XXg+#&%Rm4 +<& 65  #K>+<& 65  #K>%:Y@V#<  b ` `U  U =>64/.'%!   +272#"'&#""&7>3232672#"'&7654/&67632B/&u:L31A&%l4W9!$W%Y%#FmLZ $R я* XXf+#%%Rl3n}7)'ZbC-9'4 +9]43,8\K(PX@ UK =K >@UUK >Y@/-52-8/83C +>.'&67327& &67>7!"7>3!2)#!  117! +!!  117! z %'/+<& 65  #K>+<& 65  #K>(*< ,B@? <b``U=>#!)&!,#,%$"+32672#"'&7654/&67632!"7>3!2YW%Y%#FmLZ $R я* >!"n}7)'ZbC-9'4 +9]()4,:\:43-:K(PX@UK =K >@UUK >Y@ %'3C+>.'&67327& &67>7#"&?3267)#!  117! +!!  117! go:rXT/+<& 65  #K>+<& 65  #K>kl9678V/B@?<:b``U=>%$%&"+3267#"&732672#"'&7654/&67632jdX?y{:zW%Y%#FmLZ $R я* VQQIYd5Nn}7)'ZbC-9'4 +9]4!-@g@ @@ UK =M>Y@ $#CF! +'"'&5467&#"&67>7>.'&67327&#327R33P|U7* 117! )#!  117! +!! KmHN%q!5nI05  #K>5+<& 65  #K>+<& 6 aV%FD2>@(2@2b``RM ==>Y@ $$)'%%" +#"&547#"'&7654/&67632326723274632#"&'y?OgLZ $R я* W%Y%#5INP+N%KD;JR?;H6:^RsC-9'4 +9]n}7)'D.H_"$3LT4-RS4,4TK(PX@UK =K >@UUK >Y@ 3C+>.'&67327& &67>7462")#!  117! +!!  117! JkIIk/+<& 65  #K>+<& 65  #K>fLLfL} 1@. <b``=>%$"+32672#"'&7654/&67632YW%Y%#FmLZ $R я* n}7)'ZbC-9'4 +9]4-,\KPX@(ZR K  =K >K(PX@)bR K  =K >@'b  URK >YY@YXUQNM%$,3C +>.'&67327& &67>7#"&7>3232>7>.'&67327)#!  117! +!!  117! HaD6:} h& :.64+  12. ̇f 553/+<& 65  #K>+<& 65  #K>3hq;&wQ@)J*/A,9+ 4  3  &GXDIUaS@PA<b`bR  M  == >`^ZXTRNL&#$/%&% +674/&67632326?674/&676320#"&5463232676#"&5474632#"&%4632#"& %R я*)N42mn3 %R я* #=2QWzHd2.;"%9"4{VFpLC;KR@;HLC;JR?;H3!'4 +?io;'4 +;Y sG5>C,0.8.h9 fSURE3LT4-RS53LT4-RSL(5|4/-+:KPX@ZRK >K(PX@bRK >@!bUINBYYC%$'+#"&7>323266&'&67327&'&76?B\@37z `$ 9?@57{ Łd `C#Eem-hq9'wR?)J+/{; 2  1 =%&6<b`Q =>31.,(&  +&''67654/&67632#"&5463232676z5|9}FX.H= $R я* #=2QXyHd2.;"%9"8Np'SWO['_);'4 +;YsG5>C,0.8.hR94Q-\rj@ @ eUM >Y@^]]r^rXWTQNM?:OC +>.'&67327>7676'&67327&#""56'.'& &67>72&7>76./&7>)#!  117! D.imJ 9 %H3L'\/G/Y !N/#$J+!!  117! BWA 2  1 5I%HJ 2  9W) +<& 65  #K>IWUw)@  M4#I_f@c <KJJ_K_HF#)"$)%& +%#"&54>76&#&7>326?>32#"'&#"3272#"&'.'&#"2&7>76./&7> .P72d {=*>Rj3?G?0&.JRjP77 .<+T=Y42 0@:b   `b N=M=M>Y@IGCA8610&$ KK +"3272#"&'.'&##"&54>7674/&676326?632#"'&1-1%"(7%.<+T@!dZUK >Y@86('$ /. +%2676&#!&67>7>.'&6732>33#"&7676937>9I 117! )#! O#I@ 117! !F$B &$fV}5  #K>5+<& 65  #K>ɢT? w O0,4@1":db`=M>*#'%+6&#&7>3232672'"547#"&76764d {=Y%#h$B &$ff4(DZ<%7)&t9Y?w N4-/Ex@ <69K(PX@$ZeK =K >@"ZeUK >Y@100E1E('$ /. +%2676&#!&67>7>.'&6732>332&7>76./&7>937>9I 117! )#! O#I@ 117! !F5+<& 65  #K>ɢHWUw*?  N34G@D9b`bc=M>1/,+)'  +2&7>76.#&7>6&#&7>3232672'"547P@'ZUM =K >Y@10=;0A1A('$ /. +%2676&#!&67>7>.'&6732>332'>76#"&7>937>9I 117! )#! O#I@ 117! !F$71 d>W 88V}5  #K>5+<& 65  #K>ɢ@[;B # W)+-!+0@@=&%<bbM=M> ,*0 0#'%+6&#&7>3232672'"5472'>76#"&7>4d {=Y%#h72 d>W 87f4(DZ<%7)&t9Y[;B # W)+-!+4-/;y@&ZUM=K >Y@:842('$ /. +%2676&#!&67>7>.'&6732>334632#"&937>9I 117! )#! O#I@ 117! !F)KD;JR?;HV}5  #K>5+<& 65  #K>ɢ3LT3-RR )4@1bbU=M>#'%$"+4632#"6&#&7>3232672'"547<'%=9)+4d {=Y%#hN><)'9f4(DZ<%7)&t9Y4-7[@ 7 @ZUK >YcF4+;2676&#!&67>7'7>.'&6732>3%d!Fv937>9I 117! DK)#! O#I@ 117! ,>`}5  #K>^d?x+<& 65  #K> &4@1&<b`=M>%#&+32672'"547'76&#&7>32lY%#hVh4d {=R=[<%7)&t9YyB;Tf4(D<J@ <@=:K PX@#dbK = =>K PX@#dbK = = >K(PX@#dbK = =>@!dbU =>YYY@ .MJ$+!#&'&#"&#"&67>7.'&677>.'&67327 #"&76763^!   v'1< & qKF; P)n'1< ( qIl$B &#e'4/))HK)  2  1 ?V-P 2 BH@HK)  2  1 ?? w Oh@N!@4 d  db```=>Y@ JH()($&'% +6'4/&67632>3232672#"'&7654&#"#"&54>7632#"&767$R уjHoN'Y%#FmL L"%3nN ,P7  M!+( V;4 VylPT+p}7)'ZbC3GP'- 8) %41/! -<R@ K PX@$beK = = >K(PX@$beK = =>@"beU =>YYY@>==R>RMJ$+!#&'&#"&#"&67>7.'&677>.'&673272&7>76./&7>3^!   v'1< & qKF; P)n'1< ( qIBARQAVBV)($&'% +6'4/&67632>3232672#"'&7654&#"#"&54>72&7>76.#&7>$R уjHoN'Y%#FmL L"%3nN ,P7 xK PX@bK = = >K(PX@bK = =>@bU =>YYYMJ$+!#&'&#"&#"&67>7.'&677>.'&67327>767&3^!   v'1< & qKF; P)n'1< ( qIEem'4/))HK)  2  1 ?V-P 2 BH@HK)  2  1 ?6=C0 *U ~h KK@H,< :db```=>)($&'% +#&'7676'4/&67632>3232672#"'&7654&#"#"&54>7vf,6rGR,V$R уjHoN'Y%#FmL L"%3nN ,P7 ;[$XTKa$JV;4 VylPT+p}7)'ZbC3GP'- 8) %4J@RU@RHG !<b``` M ==>BANLARBR)($&'% +654/&67632>3232672#"'&7654&#"#"&54>72'>76#"&7>b%R уkHnN'X%$FlL!L#%3oN -P7 72 d>X 87V;4 VylPT+p}7)'ZbC3GP'- 8) %4[;B # W)+-!+$/DWNK(PX@0bRM  = M =K >@.b UR M =K >YY@SQIH3C*%$& +%#"&7>3232>7'.#"&#"&67>7>.'&67327276$32G`E4:} g& :%!"!q<<?D&  ./2 Z >:8  -01 ̍?& rta(bQgp=%wQ@)J*/,lx}@.$)|4@) 4  3  %FA54@) 4  R&{I?$KFKPX@1b`b===N>KPX@-b`b==N>@1b`b===N>YYY@ %%.('$&+#"&7>32327>76&#"#"&54>7654/&67632>32N={[Nu/U L5;D c\Hif%F  (P7 o %R щ* h6nD>7=?4"C  9p&PJ8  1' %39'4 +/4J1(=@:UM =M>%"((  +"32654&"56%2!"7>3!2~:\xw )(! %&eqe {() (/@,UM=M>%"((&%%!+724.#"47632#".!"7>3!2y1'}b\?4 %'\9O`1MFI1Gsm;()*?@<*$#:UM =M>(&!  +"32654&"56%2#"&?3267~:\xw )(}go:rXTeqe {kl9678 V+/@,:UM=M>&%%$&"+3267#"&7724.#"47632#".jdX?x|:zrK-PX@#ddM =M >@'dddM =M >Y@><54-+%#  +"32654&"56%2#"&76?63"'&76?632~:\xw )(#  !  'eqe {$ '  $ ,;]K1PX@UM=M>@&bUM=M>Y@ &&(&%%!+724.#"47632#".#"&767632"&76762y1'}b\? #% ( %\9O`1MFI1Gsmt /  ++1=K@ @-b U U= N >Y@ @>FC>K@K:7-,+'# == +26762"'6&+;2676&#! !!272'.+"3276+"lT F  F Bgg %"o<7" 3-aC??(\60)1 Kk$> U;%%;FB/9-R y8 G8/)9w  u.to$ve>&b3y'9EE@BA50<bM=M=M>#"$)"'-" +32>5<.#"43263232672#"''"&4#">G-Nk+ !D_4+Ru@mmJvV(Vm@A"5;8/[`:slF >3';RbWVV9h>fP6@X!op~69Ah"=h3z GUZ@W PNA@762/+*%$  GE#" +4&#"32>23#"'.#& &747>7674&'&7472$#"&7676-yXL9NTJxh: %V@o? >K  117! ?L '!$B &$fq^Imi%SVb?RH? %d'1">#! "  #K>51!?#" o? w Oh/=v@*ddb`M=>Y@ $#"$'%'+#"&54>7654/&67632>32#"'&#"632#"&767)N -P7 L%R у@K?A?0&-)` M!+'  8) %3_R;4 VyJ/7X6t1/! 3z; G]^@[IH H]I]A@762/+*%$  GE#" +4&#"32>23#"'.#& &747>7674&'&7472$2&7>76./&7>-yXL9NTJxh: %V@o? >K  117! ?L '!#! "  #K>51!?#" ]IWUw)?  M4/EA@><69b`eM=>100E1E"$'%'+#"&54>7654/&67632>32#"'&#"2&7>76./&7>)N -P7 L%R у@K?A?0&-)` A@762/+*%$  GE#" +4&#"32>23#"'.#& &747>7674&'&7472$>767&-yXL9NTJxh: %V@o? >K  117! ?L '!Eemq^Imi%SVb?RH? %d'1">#! "  #K>51!?#" <6=C0 *U ~h :E@B*< :db`M=>9753/-&$ +#&'767#"&54>7654/&67632>32#"'&#"g,7rFR,N -P7 L%R у@K?A?0&-)`;[$XTKa$+ 8) %3_R;4 VyJ/7X6t[58F@&<<9:KPX@.db`M = =M>@1db`bM =M>Y@ (,%.%#+#"'.#"#"&#&5476322654&'.547>32#"&76765''R@'ddbM=M>Y@A?420.*($! +632#"&767&'.#"#"&5463232654'.54>32 M +( e5#>  +D;#纲<P3+q'ChEsq=bd1/! += (# 5J=2{odb-@,b`bM =M>Y@ ,%.%#+#"'.#"#"&#&5476322654&'.547>327&'&76?5''R><1/-+'%  +&''67&'.#"#"&5463232654'.54>325|9}FX.H5#>  +D;#纲<P3+q'ChEsq=bNp'SWO['_+= (# 5J=2{odb-KPX@;b`b M = =M=M>@>b`bb M =M=M>YY@QOCB?=876532,*" %# +#"'.#"#".'6732676./7.#&5476322654&'.547>325''R=0$ &/̌ekvz;Sm v=V`2w_F$GeCoDZ ]&Mk , ,(% '%#w t`VtIQq}*263hN@8EC@.b bQM=M >Y@NNHF><76,*+"$ +'7.5463232654'.54>32&'.#"#".'6772676.0 ~z<P3+q'ChEsq=b5#>  +D;#筧/ d $I)/>=0 dY-:KPX@)b`M = =M>@,b`bM =M>Y@ ,%.%#+#"'.#"#"&#&5476322654&'.547>32>767&5''R><1/-+'% +#&'767&'.#"#"&5463232654'.54>32f,6rGR,C5#>  +D;#纲<P3+q'ChEsq=b;[$XTKa$/+= (# 5J=2{odb-KPX@6 bbQ  =  M =K >KPX@2 bbQ  M  =K >K&PX@6 bbQ  =  M =K >@4 bb  UQ  =K >YYYY@HECB>=<9875"(%#+%&##".'6732676./7"&67>76&+""'6743) 72#.+"N8z u* c $I*/>=0$ xv }N5ad33B( 9is-39{; 2 L&Mk, ,(%  1 ;{^mbw  s`fkz3PG@C+ 86K*PX@0b  `  bQM=  >@.b  `  bUQ  >YY@ ED()$$! +7#"5476?>7>3#32672#".'6772676./7&54} 2l =A+)zX%#3 c $I*/>=0$ }f !f<' \ L :%7)& Z&Mk , +)% s</=OK PX@$&<<75:KPX@$&<<75:KPX@$&<<75:@$&<<75:YYYK PX@"b M =K >KPX@&b = M =K >KPX@"b M =K >K&PX@&b = M =K >@$b U =K >YYYY@ -*153 +%& &67>76&+""'6743) 72#.+">767&N8z  }N5ad33B( 9is-39Eem{; 2  1 ;{^mbw  s`fkq6=C0 *U ~-?y54@(bUM =M>Y@/.;9.?/?#$$! +7#"5476?>7>3#32672'"542'>76#"&7>} 2l =A+)zX%#h72 d>W 87 !f<' \ L :%7)&t<[;B # W)+-!+9?qK PX@ ,#.!<KPX@ ,#.!<KPX@ ,#.!<@ ,#.!KPX@0 b  U  = M =K >KPX@, b  U M  =K >K&PX@0 b  U  = M =K >@. b U  U  =K >YYYY@?=:8520/+*13##3+%& &67>7#"7>;6&+""'6743) 72#.+"32+N8z  }NS75ad33B( 9is-395{; 2  1 ;{$^mbw  s`fk#P=sK*PX@*b  UM=M>@(bU  UM>Y@=<97$!#$#$ +32672'"547#"7>;7#"5476?>7>3#32#)9X%#h<w/ 2l =A+)/!:%7)&t;W$ !f<' \ L #s<VxK(PX@*   U  UK =M>@(   U  UUM>Y@>=TRNMJHFD@?=V>VC-O% +32>764.'&67327# '&6&'&67327272#"'&#""&7>32d4R`\H!c 4(8 4 7+3 ]_m' 2  1  %E@/۬ZVu,Jy= 2  1 ;sXXg+#&%Rl3;`w@t0  <  b  ` ` `U U  = N >\ZWVTRHF=;42.-+)#!  +272#"'&#""&7>32067'#"&5476#""&'>323267>3232672#"'&74B/&u:L31A&%l4W9!A ZFpN'X$#Fl+- "L42osO ,P7 kX%#NXXf+#%%Rl3% %pSUSDF}7)'Zb!"4?ir? 9* %3@&7)'C7<H\K(PX@ UK =M>@UUM>Y@?=EB=H?HC-O% +32>764.'&67327# '&6&'&67327!"7>3!2d4R`\H!c 4(8 4 7+3 ]_m' 2  1  %E@/۬ZVu,Jy= 2  1 ;5(*;FR\@Y<b```   U=N >IGOLGRIR#*)'$&' +%067'#"&5476#""&'>323267>3232672#"'&74!"7>3!2s ZFpN'X$#Fl+- "L42osO ,P7 kX%#N $& %pSUSDF}7)'Zb!"4?ir? 9* %3@&7)'C()<J\JDC=:K(PX@UK =M>@UUM>Y@ %$C-O%+32>764.'&67327# '&6&'&67327#"&?3267d4R`\H!c 4(8 4 7+3 ]_m' 2  1  %E@/۬ZVu,Jy= 2  1 ;kl9678;VU^@[%  <:b  `  ``U=N  >QOLK*)'$&*&" +3267#"&7067'#"&5476#""&'>323267>3232672#"'&74jdX>x{:z  ZFpN'X$#Fl+- "L42osO ,P7 kX%#NVQQIYd5 %pSUSDF}7)'Zb!"4?ir? 9* %3@&7)'C<GQrK(PX@* U M=K =M>@& U  UUM>Y@>=QOKJCB=G>GC-O% +32>764.'&67327# '&6&'&67327"2676&>2#"d4R`\H!c 4(8 4 7+3 ]_m' 2  1  %E@/۬ZVu,Jy= 2  1 ;C0-FE.1BdcGEe;FQ[h@e<b```  U  U=N >HG[YUTMLGQHQ#*)'$&'+%067'#"&5476#""&'>323267>3232672#"'&74"2676&>2#"s ZFpN'X$#Fl+- "L42osO ,P7 kX%#N.<5[=6 qdpIG %pSUSDF}7)'Zb!"4?ir? 9* %3@&7)'CYC0-EE-1BdcGEe<M^K(PX@# ddK =M>K-PX@! ddUM>@%  dddUM>YY@ ^\&(C-O% +32>764.'&67327# '&6&'&67327#"&76?63"'&76?632d4R`\H!c 4(8 4 7+3 ]_m' 2  1  %E@/۬ZVu,Jy= 2  1 ;$ '  ;FUd@@@ bb```   U=N >Y@ca[ZTRLK#*)'$&' +%067'#"&5476#""&'>323267>3232672#"'&74"&767632"&76762s ZFpN'X$#Fl+- "L42osO ,P7 kX%#Nr#% ( % %pSUSDF}7)'Zb!"4?ir? 9* %3@&7)'C /  ++R-Lc@L@UQM>Y@ KIO'C%"+#"&547# '&6&'&6732732>764.'&67327327l'z?OgAJm' 2  1  %E@/kF]"%F;Y@& O Y  @8b  `  ``` R=>Y@XVML*)'$&,%" +#"&547#"'&747067'#"&5476#""&'>323267>3232672327'z>OhN ZFpN'X$#Fl+- "L42osO ,P7 kX%#;KJK*N%6:^RsCd %pSUSDF}7)'Zb!"4?ir? 9* %3@&7)'L/E\"$<Ij@9K PX@  = >K(PX@  =>@ M>YYYJ%(M+76.'&67327"&'"&'.'&673277%&'&76?^u 'w /# v' Ne-1#N3#411 zǓ 7A-\<$EemVJ   2  1 5T/1/b-/  2  1  #$A59 &6K PX@ b ==N>KPX@ b ==N >@ b ==N>YYY@?=42&%  +&''67''"'&'"&'>32>76676'&'432#".'R5{9}FX.H__AE +N#2N@=@ 1@N;KOm2 |7E7P^#_+# Np'SWO['_ph:+ /]l/!D@lAKJ6+Y1s|/10=JMIDB@:K(PX@K =K >@UK >Y@ MCC+%&#"&67>76&'.'&67327676'&67327&'&76?J9{ r }N6 =A wV P# ; =Bv JI))V#Eel{; /  / <{90-R1 2  1 59 Z 2  1 #9J3w&6Q@N<bb ==M>;953/-%#  +&''67.#"&'>326'4&54632#"&546323276\5{9}FX.H +MH0%/7C7L)0T.:y]^-`C'8 ;?rvNp'SWO['_f $4*`DSoqe(/0L^9>?R $1M_K(PX@"U K  =K >@ U  UK >Y@D@32CC +462"$462"&#"&67>76&'.'&67327676'&67327BbBBb=Db@@b9{ r }N6 =A wV P# ; =Bv JI))bDDbBBbDDbBL{; /  / <{90-R1 2  1 59 Z 2  1 #9J3w- =K PX@+)<:KPX@+)<:KPX@+)<:K$PX@+)<:@+)<:YYYYK PX@,db`M = N >KPX@0db`M = N = >KPX@,db`M = N >K$PX@0db`M = N = >K(PX@4db` =M = N = >@2db`U = N = >YYYYY@4.-,('&#=<& +#"&76762672#&)"767>&#!""'6743)26323$B &$f^85N- % q5=: Z7x ? w O&| jT   BOh3A@  +<9KPX@I  b  bZ```` =M=>@F d  d  bZ````M=>Y@=;7521#!!#) +632>76#"'&&'677"#"&#""'>3236632#"&767+" ;]G706.Ibn]TA$KPX@3b`UM  = N = >KPX@/b`UM  = N >K$PX@3b`UM  = N = >K(PX@7b`U = M = N = >@5b`U  U = N = >YYYYY@ .('&"!  7 6 +462"2672#&)"767>&#!""'6743)26323)LfLLf^85N- % q5=: Z7x fLLfL| jT   BOD3?u@r  +<9  bZ```` M =M=>><8621#!!#) +632>76#"'&&'677"#"&#""'>32364632#"&+" ;]G706.Ibn]TA$KPX@+b`M =N = >KPX@'b`M =N >K$PX@+b`M =N = >K(PX@/b` =M =N = >@-b`U =N = >YYYYY@&  /. +%2672#&)"767>&#!""'6743)26323>767&\^85N- % q5=: Z7x EemZ| jT   B6=C0 *U ~Oh >~@{   6'< :%9 d  bZ```` M =>=<:85420/-,+#"  +#&'767632>76#"'&&'677"#"&#""'>3236g,6rGR,o+" ;]G706.Ibn]TA$@/bbM=M=N>Y@ $$)(&'#3+6&+"?63267>7>32#".'.#"'"&546323267>/DZ  -1. xFHtTLh-L7 ")7#%l5:mwG/=0 ?9- #)0s794%:-3N +?%-)PPV9#?M)!1ÏH1]G@S 9!.,K(PX@N  ZbbUQ N  ==L  = K  >@L  Zbb   UUQ=L  = K  >YY@$ZWONMJGFA@=:871/'%  ]] +26762"'6&+;26&+#".'6732676./7#&67>76&'&67!272'.+"fP B!  B AbZ$!y5!Y_/ c $I*/>=0$ }a oI1m 0C3/%1Hg#9J-R 7} H7/)9 =T&Mk, +)%  1 ;y3{; 2  q%wd='= C@1&$@,bbQM=M>Y@ '2(),!+4#">32672#".'6732676./7"#"&54>32A"5;8/[`:mJvV(. c $I*/>=0$ v 'V}su@l9Ah"=h6@X!(S&Ml , +)% R{NV9h>f<8@5<ddUIK?:2)9"+632&'76?>?6'"2&'76?>76&#'"&?672#qR'3 N0 0f1_9 R5}$c *|@Ph%% ] %% !A$% 5GL@IA(<bUU I K ?FC@=4$334%$$% +>32#".'&#"632;2&#'76;276&#";2&#'76;26\8C8RA -7 jj=8< /\+o/ 9 !&`+B / ^(o;Ec-2$ 'P&6ZXYQ/ ## />A,$/ ## {U +@ &K!PX@.b`XUINB@/b``UINBYY@)'%#     +2"&7>6&#'"&57672#"&76323267$*4)_ +|? =/38=-H +&,:""""{A$% 77Յq-3&3;u/e@ (/@$bUIK?Y@ 5#26"!+632#"'&'"2&#76?>76&#'"&57672kGL ;" A5% \; %#8 +|? y@5 H $$ A$% 17 )q@+bbUIMAY@ ""!$2"3 +6&+"?7+"327#7##"763232764$  %#B .~>kJL ;  @" '' !7)yy@7 H 2KPX@3bbb UQM>@9bbb UUIMAY@ 0-"6""!$"" +327672#"'.7##"7632327676&+"?7+" , %X-=#kJL ;  @4$  %#-w;(3/ry@7 H" '' !"+4FK PX@ 1<:KPX@ 1<KPX@ 1<:@ 1KPX@+I UU I MA@,U UU I MAYYY@420.&#! ++ +"&?37#"37632"#!76372676&6&+37G/i `IC\85I0/ MK  '=F.'#!w%aK4VN#o .G} J?@323267676'.#"&5?37+5I U-Y823a>7.  E%  UI'  1  #! LPX%S=4[# ZF%KPX@ e>@ d[Y)& +#"'&7676703/   %(  +\u?K1PX@IMA@bIMAY&&$+#"&767632"&76762 #% ( % /  ++3#@ :IMA  +"&7>732z71 d>W 88Z;B # W)+-!*7@9M >  +2'>76#"&7>71 d>W 887Z;B # W)+-!*'FI@  @eM >Y@   +2#".67>3%^ /94K! WF)V%*V #%3<2:\Jy5K@@QM >Y#$#"+#"'732676&#"'632yb#KV?C&0hi5p{ TCPL\T >J5K@@QM >Y#$#"+>32&#"327#"&>a% KX@C&0gaJqz TCPL\T } @bcM>"&"+>32#767>&#"#"&} y\GN TV P^J(i ! 3NUF:7:=9HBA4T6\x@KPX@ZcM>@bcM>Y'#"+#"76&#"#76'&7>32#"%>(5 6D P =? pG\Y (66*+3BAH9<;6;GTOJ+"+5ouVum"+"+%771:]\V\v3@<e > + #3# \V\nN3@<e > + 3#38\~V\9nG3OX @9 >  +&''67-l1{8m#TXmkN]\cp%vJ @:[  +&'767 4e/{8]!^%~kNOjq5N-@ d[Y&% +#"'676763  "  *!x}@ "z3iN @IMA   +!"7>3!2r %&N()Db -@ d[Y$! +632#"&7673 ?(/ /' $` %K*PX@ e>@ d[Y$% +#"/7632q[ # HG)/- @ <d>#! +#"'632!#E!"- R gYN @IMA   +!"7>3!2\$%()?i` %KPX@ d>@ d[Y&! +632#"/EM &T  Ju KPX>[Y   +"767% 8!J,.G% s YL=$@!<GK? +!7]=D;@ 9[ +!;/ O@@UIMAY#$#"+6&#"'632#"'7326 46 !'PXO 3S1H@ nOMm@DF9O@@UIMAY#$#"+>32&#"327#"&N(%3U 73 MYMo=H13F?l-myE @dL>Y@ " +&767376723 .----y$@! <S>" +##"'7#&767 . -=r@ KPX@dT>@deGL@YY"!+#"'7#&767376323#!  T }Z @GK?  +!&767  @;ih @ :IMA% +267#"&7|e 3+`luhRfbV~=D @M >$" +4632#"&LC;JR?;H3LT4-RS< *@'UIMA    +"2676&>2#".<4\=6qdoIGC0-EE-1BdcGEeOf?(@%< :dIMA*! +"&7>732>76qSR~ng7$"IOPU"XoB`X\6@3<: 9QM >  +267#"'&#"'>32$7".J3FA$'70)`@AG3,> 5-.4 ]Y9-\u?K1PX@IMA@bIMAY&&$+#"&767632"&76762 #% ( % /  ++J@ < :d[$( +67327#"&767#21FYZ'$ \ Ta"$A#N#)9):?2CF"+'&'.'7'6767%ɓ  Е    3xw,7c@ 6(@UIMAY@1/%$" ,, +"&76?&#&'?327"?6&#?37"'32676/7: /X_ B=V< <E% E+e 89\k*!X1+"=f+'- -- I%Rm (&Dk  @dd[;2 +6&#'"&?672&'76?>7 *|@h N3}%5A$% 5V %% !-@ ,KPX@"bUQK>@(bUIUMAYY@ !-")#+332676&'.>32.#"#"&#"6- "$8)B%@L>M-a 0B()>1/.: \[*![K*%''%="C~Y O<*($&-8%:KGdXN@K+I3X@B@?+<bUIMA1/&%! >> +32&'07673>?6.'.67>32"&7>5676&#" P2 !!   }`Xy-*:/5F   %%  .! "'NYZK  &0H2,E) ;0(+)8K&PX@K = >@S >Y@  + #!7)s)AK&PX@S = >@dS >Y@  + #!7!7C)'s)AK&PX@S = >@dS >Y@  + #!7!Ds)s3R)AK&PX@S = >@dS >Y@  + #7!7!D)׍s)5):K&PX@ =K >@dK >Y@  + !7!;C)s#@ dGL@ +73!{hhZ\|h9#@ deGL@+7#3!!jk?>h^O@<d[ +33#Omu\@u!@SGK?+!7!7!7!/'/XZ1x11@. 9IMA-*11 +2&7>7>.#"&7>%2&7>764.#"&7>93 :JrT]  7%Co94 ;JrT]   7%CZ;M:I O) 4 7Z;L;I O) 3!60/@ d[Y +3#3L3`Lu0G/@ d[Y +#3#M4\Mu."+%7%5H |L9M."+%77A {L7L fq *@'UIMA    +"2676&>2#".F +[G ,W}JG}C0.DE-1BdcGEeV5@ d[Y@   +2#"/&7>A"E 4V /VE%2#"/&7>6%f 4;"E 4J -  /VZ32$7!/J3EA$'7/)`@AG3-> 5-.4 ]Z:-"i@M=M>+>2">2"MY3MYMY3MYZ@@Z@&Y@@Y@ @eK>" +73##F"<7V [ "@ <eK>! +'"'#&7673D;+7# Hw C@  @dGL@Y! +6323#'<, L 7KPX@dK >@dGK?Y" +#&7673672F"; " H@deL >Y@  +%!#.'76734 @FJYmD."&+=W=3B5%6 "3vh%KPX@ e >@ d[Y%% +#"/7>32j)X1d ), h %KPX@ e >@ d[Y$! +632#"&767 M +' d1/! wN @9 >  +&''675{9}FX-GNp'SWO['_#1@.IUMA  +272#"'&#""&7>32B/&u:L31A&%l4W9!XXf+#%%Rl3#X @IMA   +!"7>3!2 '5'X%-".9b  @IMA   +!"7>3!22$&b!*,qV@:IMA&" +3267#"&7jdX?y{:zVQQIYd5)==D @M >$" +4632#"&LC;JQ@;H3LT4-RS9' @IMA$$$"+>32#"&%>32#"&J/.:L./8K/07J0.90@A/.AA./AA/.AABo%@"bcIMA$'&+#67676&#"#"&7>32DG ? = 1 'j38;BIQ( # %%MNT *@'UIMA    +"2676&>32#"+=-V?0tHF\ vHG9,);;),9ccGEeu-K1PX@ d[@dd[Y%&&$+#"&54?632'"74762+  $%/ x 2#3z %3   -;h @ :[ +#&'767g,6rGR,;[$XTKa$P;,KPX@ K>@GK?Y +#3s4sPq]P3KPX@ K>@GK?Y+#3#3q4qq4pPqo %4 2"'267#"&7AM*AMf!2+aluqM88M6-RfbV~?) 4 9K&PX@ M >@IMAY% +4&"'>32f!2+aluRfbV~3"@:IMA +"&76767>3293 :JrT]  7%C[;M:I O) 3!77@9M > +2&7>764.#"&7>f93 :JrT]  7&C7Z;M:I N* 4 7B!@9M >  +2#"&"&'&7>1,98  =L l,# YB7!3 )O I;L;[;9KPX@ M >@IMAY@  +2&7>764.#"&7>P94 ;JrT^  7%B[;L;I O) 3!7?i` %KPX@ d>@ d[Y&! +632#"/EM &T 2Ju KPX>[Y   +"767%I8!J,.G% s &@#<dS>"!+#"'7#&76737632H  +  kF@ @dT>Y"!+6323##"'B    dO I@ 76&'.7>32 qT1 2+71%;+95RO[5'6"%+j9 B&@#<UM>!$""+3263#"&7>32"&#" 0A  QYU ?I3@PqRTsTB-eyE @dL>Y@ " +&767376723 / ,~-%-y$@! <S>" +##"'7#&767 . - L@ @dT>Y"!+#"'7#&767376323# " -  @GK?  +!&767  *@'ddM>  +2?0303#"&7>32u`--)'7bYVr $!*dtn@ %!9#'@$ddN>  +".7;267>32c=< 6'),-a%@1 @lG : & Bl @IMA$ +462#"&B^?A-1@1=A-/@B} @IMA$"+4632"&%462#"&A-1@B^?jB^?A-1@1=A-1>B-1=A-/@Bd !@UIMA#"+4&#"26%462#"&A-/@?_?figIHf/DFZGG-Fddgdv@9[ +2&7>76.'&/&7>@ddINBY@ (% +'73#".'6732676. F7 c $I*/>=0 b&Mk, +)%F%?@<:M>)! +#"&54%327RPf/++N$q^R+"%\ @ <d>#! +#"'632!#E!"+ R TLH3!2#"'7! $/{%$m 3jK@  @dIMAY""&&$+#".'#"&'>732763232! 4v4$6a 3>- JTT  ROy G3^H7==b (:KPX>[Y  +&'767*n.rB]"]=f`XOiq3X @9[  +&''67%t14c#b[pITeq)V @ :M>% +267#"&7e 3+`luRfbV~1^ @ 9IMA% +4&"'>32}e 3+`lu1RfaW~B}1@.IUMA  +272#"'&""&7>32B/&v:L31A&%l4W: XXf+#&%Rm3N @IMA   +!"7>3!2]$%()_pN @IMA   +!"7>3!2=$%()Pb /@,UIMA     +!"7>3!2!"7>3!23$%83#$'*')B1@.IUMA  +272#"'&""&7>32B/&v:L31A&%l4W: }XXf+#&%Rm3 @IMA   +!"7>3!2u\#%() @IMA   +!"7>3!2B>$%()aWTKPX@ =>K!PX@ e>@ d[YYY +2"&7"1=/T\D@< => +"&52*"2")& &@#<UM>!$""+6&#"#7672#"'72326u 0B  QXU ?I3@OqRTsTBTLH +37%!!b**tEBi3jI@  @eIMAY""&&$+&'>32>32&#"#"'&#"! 4w4%6a 3>- KTS  RO9y G3^G# "+''7'77xEw/yGw.R99::TN"+6&'7.676 3<3!+NB?1$'p)@;$ '=hB0] /@,UIMA     +!"7>3!2!"7>3!2X=#$9>$%'*')2`E %K*PX@ e>@ d[Y$% +#"/7632[ # HG )/bv %K*PX@ e>@ d[Y$! +632#"&767 ?(/ /' $S1@.IUMA  +272#"'&""&7>32B/&v:L31A&%l4W: 'XXf+#&%Rm37@9M > +2&7>764.#"&7>f93 :JrT]  7&C7Z;M:I N* 4 7^;J2@/dbINB+>2"$>2"2"&7676VCM,CMCM,CMq .sM88M88M88M8*  %#[@ :M>%$ +73267#"&7r+!B:0'w5ORk=,^LSUH3!2#"'7!( %.{$$ >Ptb /@,UIMA     +!"7>3!2!"7>3!2>$%8=#$'*')|3 6@  @ d[Y&$+#"'672#"'672  "E!"  #E!#D R   R  z I@ @eUV >Y@ $#%#!+"#"'7&#"'>327632267#"' 6:#'7.)_@834<$G8!.J"%t(.4 ]Y"o,>  h#}@<;KPX@%UQM=M >@#UUQM>Y@! ## +"&>2"&>2267#"'&#"'>27 08 7 17 ;$7!.J3EA%'7/)_F4(8((80(8((8-> 5-.4 ]Z:-<^1( K(PX@(  U UQM  >@.  U  U UIMAYY@"/-,*%#!11  +2#"&#"#"&767232>72'"&#"#"'&?63232> O[! 8! U^ (@!* QY!"!8  V](@! mH&& hD## mH%% jE#$w;s lKPX@dT>@deGL@YY+673!.'3#>7!#.'{mD."&& FLg}zH*>d@FJY$5!"5= ~( +77673.'1>F">%Y*+1)2+ FgnrcH !2kt"+%77 {L7L8N*@'<QM>#$#"+>32&#"327"&O 3P 24 ")O[Mm?F32G=o*@' <UIMA%+"&>24&"'>32AL+ALe 3+altM88M6-RfbV. "+''7'77y=w)y>w(2233_.h"+%7%6H |L9M<.E"+%77 {L7LF. :@<:9K/PX@ d>@ d[Y +%77%#3# +L3\L{M6Lu7'a*@'<QM>#$#"+#"'72676&#"'632aO(%4O 33 N[Nn>H13F@nY@M> +>2"{MV2MVV??V@7}q7@ -<";KPX@"XYINBKPX@!XeINB@ deINBYY@ &'"''"!! +"&#"#"76326&7>32>32#"#".'#"&7> / &7!,E+ L3 .   #8-%3C M 3'-!AC1J (\@UIMAY@$#(("$"$$ +2676&#">32632#"'#""2676& +[G ,-.FHV)DUHV}JU)DVG.F *[G ,-FE.1ABvdBBcGEeBBB0-FE.1Au m@ KPX@eV>@deINBYY6%6"+"&?656+"76?632;2f ]! #Y     w @ :M>  + 732>7c*1gލvV7%5LI&$NC;%)%@" :IMA  + 732>7*1fލvV7%5LI&$ND:%= @IMA   +"7>3!2#-+=#/#.R @IMA   +"7>3!2#-,"/#-@XjN@ < :9KPX@QM >@UIMAY"'""+6$3232$7#"'&#"@EՅǘyV9D8¤_`hADDE Mj.DBSC @9M>  +2.#"'6$#`Z+*b⌎|G%;[[23bS;%;dKPX@dT>@deGL@YY+!.'3#>7!'FLh}{G)=P5= ~( 54/-)'! 8 8# +32>7"5 #"&76?6&#"#"&7>3227Ed %&+    $'v7- U'1:   4+L+  ,"B=   09FL?avL<@9<SQM >  +27"'&7>32#76#"7,9JF' dAH) !a1' ?';2!O:^X>;"!% +&#"&72>?6&'&7673&"&>2" +#*   @)% %V   G%%7F $@!QM >    +2"&>6&#"32A8> a;^j";L8F8F $%$#5"+726?6&+"5?7#7'#"?6&'#"&5757p%/ :  1)1*ER)#?F 8'g *L4@1<bQM >  +2#"&76#"32>7#"&>@5$!&;  " >FC9gL%'-C:%+ BVe (\@#($@dQM >Y%"#"+7&#"323#"'&7>327&6.'7673#7y!<  .$6 W<1 @)B,A$ Z%mK2#%NKPX@( bbc =M >@%  ddbcM >YY@ :92':" +672&#7637>?6&#"2&#7637>?6#'"57672>-+3 5"J 5  9%F  2 T% dR956  )4  9FNHVn@)$@ bcM >Y@RQNLCA97,*(&#"'# +76&#"&#"&72>?6&'&7673672672&#"&76?6&#"&#"&727> .# +#* @* :(= >/'( ("+"' *# +) Zw+-   459865  w+&  L-6@3<9beM >,*'%#! +7#6326'&?67623276&#"*?  *#+ " %,6:    ##<8@5<:QM >$#$"+27#"&?#"57672?32#} #,<(/=AK?'"" M huB-N@ " @eK >Y@ %#3(34+'.#&5?7+"7676&#"?7#"&. 7P "Z '7  ! SHGz@<'B/<  :GC409K PX@ b WM >@ b cM >Y@FD@>86$'##$"$%! +632767'&#"&?27#"76#"?327#"32&'"76326'&'2&'"X  TB  sD ')R? ,!;"@,*)Pu?J Z` 7?qN% @ 9[   +2'6) #)E%'+'Js @ 9[   +%2'6D&'+D')+HuTbs@d[  +2"&5476!\  'f F  %#p'J &8@5<dbINB &&$$$+>2#"&%>32#"&2#"&5476CX:C,-8D+-8B-,9!\  'f 0@A/.AA./AA/.AAF  %# Rb@_/<b``   V K = K >QOHGFDA=:9.-%!  +2#"&5476!#&#"&67>7273&#"&672>&7!"!\  'g 5+C  / u' 3;L%wJ %- x % -9F  %#w5 ^  2  1 =>[l  2  1  N#{ @IMA   +"&546323DF1-JFF/1HH1/FTBR@8  @F  d Z  bbZ   U U=L >Y@$DCLJCRDR?<432/,+&%" BB +26762"'6&+;26&#!&67>76&'&67!272'.+"2#"&5476fQ B  A AbZ% y5!X^1` oI2m /C30%1Hf#:I!\  'f -R 7} H7/)9 = 1 ;y3{; 2  q%wd='F  %#Z6]mK(PX@3d  b  S K  = L >@1d  b  U  S L >Y@_^ge^m_m]\WVSOLK@?33C+>.'&67327& &67>7!& &67>7>.'&67327!2#"&5476H)#  127! +!  127! NN+!!  117! )#!  117! >!\  'g /+<& 65  #K>+<& 65  #K>m+<& 65  #K>5+<& 65  #K>F  %#-,<fK(PX@%dbK =L >@#dbUL >Y@.-64-<.<3C +>.'&67327& &67>72#"&5476*#  128 *"  118! q ] (g /*<(  4  3  $J>*<(  4  3  $I?E  %#,B@?dbM =M>&$,,  +"32654&"56%22#"&5476ӵ~:\xw )(x!\  'f eqe {F  %#Z=MiK(PX@& dbK =K >@$ dbUK >Y@?>GE>M?MMCC +%&#"&67>76&'.'&67327676'&673272#"&54769{ r }N5 =A wV P# ; =Bv JJ()!\  'g {; /  / <{90-R1 2  1 59 Z 2  1 #9J3wF  %#%7G@ 3+@. d  b`M = N >Y@98A?8G9G54/-$" 77 +%37.5%6!23272!"7654&#"#!&547632#"&5476`/%#r?% R|iE[DQ+!7// H#!\ 'f l7ؕѤuΘm!lo!']<󉑁TՑUA!Q#hZ'F  %#NF'3CE@B< dbbV=>54=;4C5C$$$'%" +327#"'&76&'&7$72>32#"&%>32#"&2#"&5476##;)'J\1+\K/%!AB-,9C,-8C,-8B-,9![ 'f HA!-E95}X+%!+sf0AA0.@@./BA0.@@F  %# DB@@=:dd VK >A?87641-*) +!#&#"&67>7273&#"&672>&7!"+C  / u' 3;L%wJ %- x % -9D5 ^  2  1 =>[l  2  1  N# - ?K!PX@!UM  =M >K$PX@'ZUM  =M >K(PX@-ZZUN  =M >@+ZZ UUM >YYY@ :90/+&?>  '#! +32654#" 32>54&#263 !"&#&547>76'4&'&547In|X:dd3CfQ%xz6m?! 118! =K ' DtHfs}p.E!  #K>51">#" -0a.@ZUK >Y@*&#" 00 +#"".#&67>7>.'&673!7'.wD + ! ?I# 11: +!! L)/sӢ+<& 65  #K>5+<& 6}'Z5%@" < =N >5 +!#!"&74>7ZL# )# /q    1B@8  @8 ZbZ   U U=L >Y@?<432/,+&%" BB +26762"'6&+;26&#!&67>76&'&67!272'.+"fP B!  B AbZ$!y5!Y_2a oI1m 0C3/%1Hg#9J-R 7} H7/)9 = 1 ;y3{; 2  q%wd='-9/K PX@<KPX@<KPX@<K$PX@<@KPX@+b`M =N = >KPX@'b`M =N >K$PX@+b`M =N = >K(PX@/b` =M =N = >@-b`U =N = >YYYYY@&  /. +%2672#&)"767>&#!""'6743)26323\^85N- % q5=: Z7x Z| jT   B4-]kK(PX@%  S K  = K >@#   U  S K >Y@]\WVSOLK@?33C+>.'&67327& &67>7!& &67>7>.'&67327!)#  127! + !  118! NN+!!  117! )#!  117! >/+<& 65  #K>+<& 65  #K>m+<& 65  #K>5+<& 65  #K>D#2@@ @2bb U  M =N >Y@"43%$;93@4@-+$2%2 ## +326762"'6.+""'676762"32654&"56%2B3wA 0 /JP3wD /# .L~:\xw )(-Jf[!#5.I*!#5eqe {4-,AK(PX@K =K >@UK >Y3C+>.'&67327& &67>7)#!  117! +!!  117! /+<& 65  #K>+<& 65  #K>4Q-\S@UM >Y@XWTQNM?:OC+>.'&67327>7676'&67327&#""56'.'& &67>7)#!  117! D.imJ 9 %H3L'\/G/Y !N/#$J+!!  117! /+<& 65  #K>BWA 2  1 5I%HJ 2  9W) +<& 65  #K>cD.#@ < =L >-+"F +7632>'.77632>'.'&'"1cK (u $. 1 e-<; y 1/3 .1T5 2  1   P\%!#  2  1  1-b/0r-=K PX@/bX  `K =  K  >K!PX@0b`  `K =  K  >K(PX@4b`  `K =  K  = >@2b`  `U  K  = >YYY@<;8410G" +%#'"&'#&#"&67>76&'&673%&#"&67>Frs  #Z wi3w RIg  ' b7 le T b.X V\ 2  1 N`Ls* 2 s 1 5V3 2  1 --<K PX@bK = = >K(PX@bK = =>@bU =>YYYMJ$+!#&'&#"&#"&67>7.'&677>.'&673273^!   v'1< & qKF; P)n'1< ( qI'4/))HK)  2  1 ?V-P 2 BH@HK)  2  1 ?o1#>Y@8 S <;:V 9KPX@3  bUM  == N  >K&PX@6b  bUM  = N  >@4b  b UU N  >YY@*A?&$XWNKCB?YAY=<30('$>&> ## +326762"'6.+""'676762!272'.#!"'6743!""54763!2>76#&wA 0 /JPwA / 0JqB3/+ !# 3iJM)+' T +u3/+ !" 4h+JN)+' V+-Jf[!#5.IfZ!#5L;s>#:T9FF;s>#:T7D,@)M =M>  +"32654&"56%2~:\xw )(eqe {4-a]K(PX@!ZL = K >@ZU K >Y@]\YVSR^C +>.'&6732$32327&#"&67>7>.+"& &67>7)#! =;== 117! +!!  117!  # /- % +!!  117! /+<& 65  #K>+<& 65  #K>5)   )+<& 65  #K>4H;9?@< <UM =K >43('$! 97 +2"'&73267#"& &67>7>.'&672$`$))# '9y;L9+!!  117! )#! '!;C^pb;+Im+<& 65  #K>5+<& 6(1.R&@UK >Y@  ., +272'&'&#!%2>76&#!&6767 &'&673;52/)/$dad?JeK!9!o*c Dj  ))@%D; !PB X 3 !~Rg/ 2 9/7K PX@ $&<KPX@ $&<KPX@ $&<@ $&KPX@&b = M =K >KPX@"b M =K >K&PX@&b = M =K >@$b U =K >YYYY@ -*153 +%& &67>76&+""'6743) 72#.+"N8z  }N5ad33B( 9is-39{; 2  1 ;{^mbw  s`fk-=EK(PX@K =K >@UK >Y@ MCC+%&#"&67>76&'.'&67327676'&67327J9{ r }N6 =A wV P# ; =Bv JI)){; /  / <{90-R1 2  1 59 Z 2  1 #9J3w->FM@ ? @/  b  `U UK >Y@ GGGMGMIH:962/.'&>> +& &67>7.5476764.'&673273$4'Nk{l("  /17 |b,&  //7 P9R`FUarIf&6$ 65  C8Eiq@g 0" 65 A6BJ5s-+>$>-QOK(PX@K = K >@U K >Y@ POCCCC +%.&#"&6767>'.'&673277676'&67327&#"&67>ywZ= Sdx O'A@ w P%d u= HRv N!%KD w NB# d\ 2  1 T!!+P36 1 59! s\ 2  1 V#)3-N5 2  1 9-bcbF@bUL >Y@[XA@=:76<C+>.'&67327>7>;2& &67>?.547654.'&546;2J)#  118! pEnF(jd#6)yS#+!  127! "Fyk= !/+<& 65  #K>q~D   -aIzG+<& 65  #K> 4XZ6>Y:*9 9KcP<{D7p@ 3+@!bM =N >Y@54/-$" 77 +%37.5%6!23272!"7654&#"#!&54763q`/%#r?% R|iE[DQ+!7// Hl7ؕѤuΘm!lo!']<󉑁TՑUA!Q#hZ4<[K(PX@!UK = K >@UU K >Y@ 873C +462"$462">.'&67327& &67>7NAcAAc>Cc??c)#!  117! +!!  117! bDDbBBbDDbB+<& 65  #K>+<& 65  #K>M_K(PX@"U K  =K >@ U  UK >Y@D@32CC +462"$462"&#"&67>76&'.'&67327676'&67327AbBBb>Cb@@b9{ r }N6 =A wV P# ; =Bv JI))bDDbBBbDDbBL{; /  / <{90-R1 2  1 59 Z 2  1 #9J3wbu 1AK(PX@! <@! K(PX@. ddM =M =M >@2 dd=M =M =M >YY@32;92A3A/-&$   +2327327'"'#"'&7>'.#"32672#"&5476ÇhF!5\X L3 wj^fdWo2G@#b)7!KPX@5  bbU  =M=M>K&PX@2  d  dbUM=M>K1PX@7  d  dbIUM=M>@8  d  dbUUM=M>YYYY@54=;4C5C!"!$"$+$" +%327#"'&547&5467632#"'&#"32632'"&#"2#"&5476z>6TƂ8djPW]e};$U)HB*!@SkF:+:I![  'f @bU=>Y@,+42+:,:**',%$ +&76$32>76'66&#"#"&776&2#"&5476 3+f J7w`$}?2;u:b~y 3:P7R),!\ (g +D\4a݁Adq`oU93 )P-F  %#w+a@ddb=>Y@ %#++%" +327#"'&76&'&7$722#"&5476##;)'J\1+\K/%!!\  'g HA!-E95}X+%!+sfF  %#+.#@ b=N>,'$"+#"&'&767676&'&7>3232676'&'&'&7>32+3ךoj3j/( 5F )/f4/WNZ' 3/ =)s%3)X{o?'#+?\Zwow4 93Ecb 1K(PX@! <@! @'=M=M =M >Y@/-&$    +2327327'"'#"'&7>'.#"3267ÇhF!5\X L3 wj^fdWo2G@#b)7!KPX@)M=M=M=M>KPX@!M=M=M>K&PX@)M=M=M=M>@'UM=M=M>YYYY@ /%#!"!"&+6732"#"76723276&# 7672#"&'R5X  ^:`}DJ L? B)= Jsn(@++%Pri= P>A=f%PZh0!%ALoAG<^+/@, <bM=>.)'+#"76?6.'&#""'>2676&'.7632!y\%")f3#/D+j m=D}JZȃpFR\CPtuG1 P#\]H ..@+ <bM=M>*$#*&+'326&/.7>32#"&'.#"#"&77FAQ%bEy m@ 7mKH#q'A'fV|tNǟoCqs=R+134}'aC`8u3K&PX@ <@ K&PX@'bUM=M>K1PX@,bIUM=M>@-bUUM=M>YYY@ !"!$"$+$" +%327#"'&547&5467632#"'&#"32632'"&#"z>6TƂ8djPW]e};$U)HB*!@SkF:+:K&PX@0b V= =M =N>@-b VR= =M >YY@1/-+'%"  88 +"&767332767232#"&76323276'".7)T}F9+X%Uf9`mybf` )-L91Re;Rs ]Z ٠dJm?qTF`?ԉCqPm*5@2 <9b=>**',%$+&76$32>76'66&#"#"&776& 3+f J7w`$}?2;u:b~y 3:P7R),+D\4a݁Adq`oU93 )P-s XKPX@SM=M>@USM>Y@    +!2"!632#"Z{/'V01-jHZPk%)P}VSPwHɼK`!@<b=>%" +327#"'&76&'&7$72##;)'J\1+\K/%!HA!-E95}X+%!+sf?C@@ 4<bVM=M>%($$$" +%#"&776&'4767272?>32#"'.#"327'"&/&# 39P8 R(L # 1;Z}>9^ >/ +D9%H0 joVX?:X93 )X/-,#Ne. >`b5;#J'"J@L+4's?VI%Q@$  < 9K PX@M=M >@M=M>Y'%-"+%#" ''.#"&7>32327#p2{%;)T<+y15C-+ !/D;ZWT ;Rq740?c/uT)d@u%;tK$PX@ <5/$:@ <5/$:YK$PX@bM=>@dbM=>Y@ :842+)(% +%#"'#"&76767>7>7>?%3267%327'"&  57Y.9 V2&U "/  Q P 0@( .)A>c'# #k(>ER;5]0H}q-.Aaz0,nK>s:W//@,,<b`=>**&%"+.#"&'>32277654&54632#"'6'&~% WB91 # TP"+EARs 3..BF!+5HՐto!"O7C2%9kY'o5TS.@ K PX@BbZ ZV =K=M = N  >KPX@BbZ ZV =K=M= N  >K&PX@CbZ bV =K=M= N  >K(PX@@bZ bV R =K=M>K-PX@Bb`Z bV RK=M>@Cb`b bV RK=M>YYYYYY@SQMKHFB@:85320"$+" +6&#"&7>7.76%#"&767332763232632#"32#"&76323ALbD3 !'+T}D;+-%" L;+ "%)+#5'DA}bf^ +D9ǔR+g?e;Rs! FN;30--/99!VT\VqTF`? @M=M>&%%!+724.#"47632#".y1'}b\?\9O`1MFI1Gsmu}07@4-<bM=>0/+"&$"++327#"&76# #"&7>7>7#"&5>3Po*!X\ 5EX+fN!71#$>/;;DP!1't}Z=D?-b\tmhZ+; 9Xp/5 s# #-@*< 9M=M>$,$"+6&#"326'6>762#"''B`J1 Y/X?FNV?0+1TB"=u{Z=sEoH'm+!P%1KPX@"ZXM=N>KPX@#Z`M=N>@$b`M=N>YY$"-#""+#"'&'"32>76'&'.7>3232676&#"Ja?)'^f`wh:<){gh+#-#X b}'!R!G>HV\Z?aFSC{RT;&5bHy@3m-5^ö/ +8o}!Y@M=M>Y@!! +##"&7676$3!2676&'" du!)+?x+&&Nw /!A)Dž7X A-;y"5@2 <b`M=>&$#% +#""'>3!+327#"&76ZH?)!.Ii='j43!X\ 5EX+ B1_j@N;=D?-b\tm+.#@ b=N>,'$"+#"&'&767676&'&7>3232676'&'&'&7>32+3ךoj3j/( 5F )/f4/WNZ' 3/ =)s%3)X{o?'#+?\Zwow4 93Ec(%@"<: 9M> ! +" >'$>7>7632LRB\oZF^mo9'5Fb#?ebCHB#RZmL#3/RsDA)@ts@;m9 Z+@%<'9K!PX@M=M>K(PX@M==M>@bM=M>YY'%%$#+&'&'"&7632373267#"&//7 =3qRu\ D% -*)I%=H1hs …/ '-3 VW!3;'-!q=I5H@de= >Y*-+67>76'&'&7>32673>76'&7>32#"'.'b )/fB% M`3{T+B? 7Fo^1#f>B' +?\bRS{k%b\o^V!;Ei?%#n^1-)@&!<)' :dN>$%-""+#"'#"&767327&7>3272'67+3m-!$!F'1'bb  B3jJaL9)+_|:Ϙ;d-%{?sw5Hqm` #CF'3/@,<bU=>$$$'%"+327#"'&76&'&7$72>32#"&%>32#"&##;)'J\1+\K/%!9B-,9D+-8C,-8B-,9HA!-E95}X+%!+sf0AA0.@@./BA0.@@8.:E7@4A<bU=N>$$%,'$" +#"&'&767676&'&7>3232676'&'&'&7>32>32#"&%>32"&+3ךoj3j/( 5F )/f4/WNZ' 3/ =)s%3B-,9D+-8C,-8BX9)X{o?'#+?\Zwow4 93Ec0AA0.@@./BA0.@@u ,_KPX@#b=M=M>@ ddM=M>Y@&$,,&%%!+724.#"47632#".2#"&5476y1'}b\?y!\ (g \9O`1MFI1GsmbF  %#+u.>hKPX@'bb==N>@$ddb=N>Y@0/86/>0>,'$"+#"&'&767676&'&7>3232676'&'&'&7>322#"&5476+3ךoj3j/( 5F )/f4/WNZ' 3/ =)s%3!\  'g )X{o?'#+?\Zwow4 93EcF  %#y-=k@ )' !@dddN>Y@/.75.=/=$%-""+#"'#"&767327&7>3272'672#"&5476+3m-!$!F'1'bb  B3jJaL9)+_|:^!\  'g Ϙ;d-%{?sw5Hqm` #CkF  %# HDBMW:KPX@4dd V  UK = M  >@1dd V  U QK >Y@$DCWUQPIHCMDMA?87641-*) +!#&#"&67>7273&#"&672>&7!""2676&>2#"+C  / u' 3;L%wJ %- x % -9F.=5[=5 qdpIGD5 ^  2  1 =>[l  2  1  N#B0-FE.1AdcGEeF+9DN0@<b  U QM =M=N>Y@$;:NLHG@?:D;D9731*)'% ++ +232672#"'&74?'#"&547>3226>?&#"32"2676&>2#"* WX%#N ZCkpY|n &S"@fhZX.<4\=6qdoIG}+9]n<&7)'C3H%q׾r  R:01'nC/-FE.1AdcGEe  ?GK!PX@)  U UM  =M >K$PX@/Z  U UM  =M >K(PX@5ZZ  U UN  =M >@3ZZ  U U UM >YYY@ GFCB:90/+&?>  '#! +32654#" 32>54&#263 !"&#&547>76'4&'&5476462"In|X:dd3CfQ%xz6m?! 118! =K LfLLf' DtHfs}p.E!  #K>51">#" fLLfLg 6nKPX@,b=M =M=N>@*bU=M=N>Y@ %($#%$"+4632#"&3254'"'632#"&76&#&7>327LD;JR@;HE8yCGk/@X(|f2d {=3LT3-RRhZMhC!.LD&tkf4(D3 - ?KK!PX@+ UM  =M = M  >K$PX@1Z UM  =M = M  >K(PX@7ZZ UN  =M = M  >@2ZZ U U  QM >YYY@ JHDB:90/+&?>  '#! +32654#" 32>54&#263 !"&#&547>76'4&'&5474632#"&In|X:dd3CfQ%xz6m?! 118! =K ߾KD;JR?;H' DtHfs}p.E!  #K>51">#" 3LT3-RRXg 66@3bQ=M=N>%($#%$"+4632#"&3254'"'632#"&76&#&7>32VLC;JQ@;HE8yCGk/@X(|f2d {=3LT3-RRhZMhC!.LD&tkf4(D - ?KK!PX@) U  QM  =M >K$PX@/Z U  QM  =M >K(PX@5ZZ U  QN  =M >@3ZZ U U  QM >YYY@B@ HE@KBK:90/+&?>  '#! +32654#" 32>54&#263 !"&#&547>76'4&'&547!"7>3!2In|X:dd3CfQ%xz6m?! 118! =K ߖ\$%' DtHfs}p.E!  #K>51">#" ()g 6C@@bQ=M=N>31,+&$  +!"7>3!2 3254'"'632#"&76&#&7>32\#%yE8yCGk/@X(|f2d {=()EhZMhC!.LD&tkf4(DHD:@$1/@.b bQM =M >Y@::(%#$)& +'7&'&7!2'.#"3272!##".'6732676.K q~`B?-jp# 1'//%# d $I*/==0 pV)Fy)/r@&Mk, +)%Ph9G@#0.KPX@C  bb`` bQ =M= >@@ d  db`` bQM= >YY@CA=;99(*$$% +'7&'&7632#"&76&#"272#".'6732676.632#"&767S kK\--E J-/6"/N%#cā CZ c $I*/>=0 M!+(  `w{Z1A7)9Xk1On9&Mk, ,(%-1/! 3=&K PX@ UM =M >KPX@&ZUN =M >KPX@ UM =M >K!PX@&ZUN =M >K(PX@,ZZUN =M >@*ZZUUM >YYYYY@4.*) =:(% +462"32>54.#"&747>7674&'&7473263 !"&LgKKgJYZW I`y 117! ?L O#8*0k'fLLfLH%:(y+P}h7"  #K>51!?#" Sw-9<L[@X@><:- <b  bU= M=M= M>IGDB$%&&#'% +6&#&7>3232672'"747'#"&5476324632#"&677474&#"726763e {=X%#h^Vw7XuLC;KR@;H_D7}T9{N)f4(DZ<%7)&t8X}{+ef?2%3LT3-RR?I`;cK33-5A1K PX@"M =M =M>KPX@(ZN =M =M>KPX@"M =M =M>K!PX@(ZN =M =M>K(PX@.ZZN =M =M>@)ZZUQM >YYYYY@@>:8,&"!52($ +%32>54.#"&747>7674&'&7473263 !"&4632#"&#JYZW I`y 117! ?L O#8*0k' LC;KR@;H%:(y+P}h7"  #K>51!?#" S3LT3-RRw3-9<L@ @><:- @=b  bQ= M=M= M>Y@IGDB$%&&#'% +6&#&7>3232672'"747'#"&5476324632#"&677474&#"726763e {=X%#h^Vw7XKD;JR?;H#D7}T9{N)f4(DZ<%7)&t8X}{+ef?23LT3-RRI`;cK3-5A,K PX@  QM =M >KPX@&Z QN =M >KPX@  QM =M >K!PX@&Z QN =M >K(PX@,ZZ QN =M >@*ZZU QM >YYYYY@86>;6A8A,&"!52($ +%32>54.#"&747>7674&'&7473263 !"&!"7>3!2#JYZW I`y 117! ?L O#8*0k'C\$$%:(y+P}h7"  #K>51!?#" S()w-9<La@^@><:- <b  b Q= M=M= M>0.IGDB63.909&&#'% +6&#&7>3232672'"747'#"&547632!"7>3!2677474&#"726763e {=X%#h^Vw7X\$%D7}T9{N)f4(DZ<%7)&t8X}{+ef?2()MI`;cK3-5KO<9K PX@% bcM =M >KPX@+Z bcN =M >KPX@% bcM =M >K!PX@+Z bcN =M >K(PX@1ZZ bcN =M >@/ZZ bcUM >YYYYY@76GF6K7K,&"!52($ +%32>54.#"&747>7674&'&7473263 !"&2&7>76.#&7>#JYZW I`y 117! ?L O#8*0k'51!?#" ShIWUw)?  M4w-CFVa@^JHFD-<49bb e=M=M= M>/.SQNL.C/C&&#'% +6&#&7>3232672'"747'#"&5476322&7>76./&7>677474&#"726763e {=X%#h^Vw7X=;989K PX@eM =M >KPX@$ZeN =M >KPX@eM =M >K!PX@$ZeN =M >K(PX@*ZZeN =M >@(ZZeUM >YYYYY@666@6@,&"!52($ +%32>54.#"&747>7674&'&7473263 !"&&''67#JYZW I`y 117! ?L O#8*0k'-l1{9m#S%:(y+P}h7"  #K>51!?#" SumkN]\cMf-8;Ke@b?=;9-<653109bb e=M=M= M>..HFCA.8.8&&#'% +6&#&7>3232672'"747'#"&547632&''67677474&#"726763e {=X%#h^Vw7X-l1{8m#TD7}T9{N)f4(DZ<%7)&t8X}{+ef?2ymkN]\cI`;cK O[*@E '<:K(PX@I X ZbZ  VU N  ==L >K-PX@G X ZbZ  V   UU=L >@F d ZbZ  V   UU=L >YY@%RP XUP[R[LIA@?<9832/*$! OO% +72676'&26762"'6&+;26&#!&67>76&'&67!272'.+"!"7>3!2J) @%fP B!  B AbZ$!y5!Y_2a oI1m 0C3/%1Hg#9J %'&>i  H-R 7} H7/)9 = 1 ;y3{; 2  q%wd='/))" (4CP@M<ddb VM=M>+)CA<:1.)4+4'$,! +4#">32672#"&54>32!"7>3!2#"/7>32A"5;8/[`:mJvV(Vm'V}su@l %'T   H&9Ah"=h6@X!opR{NV9h>ff() %( O[@E '<:K(PX@H d ZbZ  UU N  ==L >@F d ZbZ  U   UU=L >Y@%RP XUP[R[LIA@?<9832/*$! OO% +"&767626762"'6&+;26&#!&67>76&'&67!272'.+"!"7>3!2@ P!fP B!  B AbZ$!y5!Y_2a oI1m 0C3/%1Hg#9J %'$?h  F-R 7} H7/)9 = 1 ;y3{; 2  q%wd='/))> (4BP@M<ddb VM=M>+)><861.)4+4'$,! +4#">32672#"&54>32!"7>3!262"&767A"5;8/[`:mJvV(Vm'V}su@l %' > " 9Ah"=h6@X!opR{NV9h>ff(),* f1BM@8  @> ZbZ   e   U U=L >Y@"CCCMCM?<432/,+&%" BB +26762"'6&+;26&#!&67>76&'&67!272'.+" &''67fP B!  B AbZ$!y5!Y_2a oI1m 0C3/%1Hg#9JC-l1{9m#S-R 7} H7/)9 = 1 ;y3{; 2  q%wd='mkN]\cYf (3E@B<10.,+9beM=M>)))3)3'$,!+4#">32672#"&54>32&''67A"5;8/[`:mJvV(Vm'V}su@l-m1{9m$T9Ah"=h6@X!opR{NV9h>fmkN]\c1BV@"8  FG Q @H ZbZ   UU  U Q=L >Y@(DCTSNLJHCVDV?<432/,+&%" BB +26762"'6&+;26&#!&67>76&'&67!272'.+" 267#"'&#"'>2fP B!  B AbZ$!y5!Y_2a oI1m 0C3/%1Hg#9JL$7!/J3EA%'7/)_G3-R 7} H7/)9 = 1 ;y3{; 2  q%wd='-> 5-.4 ]Z:-V (<]@Z,-7<69bU QM=M>*):9420.)<*<'$,! +4#">32672#"&54>32267"'&'"'>2A"5;8/[`:mJvV(Vm'V}su@l$7!.J3EA%'7/)_F49Ah"=h6@X!opR{NV9h>f&-> 5-/4 ]Z:-H kh@&a(G/ < : < :KPX@Y  Zb   bUUN ==L  = K  = M>K(PX@V  Zb   bUU  QN ==L  = K  >@T  Zb   bUUU  Q=L  = K  >YY@&he]\[XUTONKHFE?=53.+%"kk%" +#"&?326726762"'6&+;26&+#".'6732676./7#&67>76&'&67!272'.+"go:rXTfP B!  B AbZ$!y5!Y_/ c $I*/>=0$ }a oI1m 0C3/%1Hg#9Jkm9678#-R 7} H7/)9 =T&Mk, +)%  1 ;y3{; 2  q%wd='=h CP@1&$@4bb  UQM=M>Y@ NL'2(),! +4#">32672#".'6732676./7"#"&54>32267#"&7A"5;8/[`:mJvV(. c $I*/>=0$ v 'V}su@le 3+`lu9Ah"=h6@X!(S&Ml , +)% R{NV9h>fRfbV~CK@'8? Zb b  U  UN =K >@<Zb b  UU  UK >Y@KJGFCA>=#83C& +%&#"&67>76&'&67!272'.+"326762"'6&+462"+  118 1m 0M30%1Hf#9RfP A   B AcKgKKg#7' 65  #K>3{; 2  u%yl='X-R 7~ H7fLLfL/"@L׵K*PX@9b  b  UM=M= N>@7b  b  UUM= N>YY@KIEC86$)##$$#$ +#"5476?7632#".'&"32+#"&546323267>764632#"& 1;}m73'3MRZ# /!=%`=09Nrw./J-!"A -KD;JR?;H!)wmK(F-0$Ot =/fALOU1%:J7, 2s%3LT3-RRy4@Y@V)<b UUM  =M>75=:5@7@-+  44 +23.#"32676&'&6727# '&7%6%!"7>3!2%%-Zƶ:)TdPf<; ?Z w /5 -sX=? %'DH} TyI$219# 8  8 +3!T`XBο))m A`l@2@*&@1   UUM=N=M>Y@caifalclXVFD?=;9531/*)% +2654&'&#"'#".54%67&547.54632672#"'&"4&#"32><!"7>3!2^j?u\)RӍT7bFVJ7<Ť@bj)6+!+@%=# %   %'L5&%[;--'`w#5H7L\+V;s \Z!HLY2"#.*:6jyd "1? >1 #1<G(*4eK(PX@-US K  = K  >@+U  US K  >Y@ed_^[WTSHGDA>=3C+462">.'&67327& &67>7!& &67>7>.'&67327!{LfLLf)#  127! + !  118! NN+!!  117! )#!  117! >fLLfL+<& 65  #K>+<& 65  #K>m+<& 65  #K>5+<& 65  #K>=I@ -@5  bb`  U=M=>Y@ HF%($&%%& +%#"&54>76&#&7>3263232672#"'&7654&#"4632#"& -P71e {=wHnN'Y%#FmL L#$2lBLC;KR@;H?. !4f4(DPT+p}7)'ZbC3GP'-3LT3-RR43- iK(PX@/S K  = K  =M>@*  USQ K  >Y@ihcb_[XWLKHEBA3C$"+4632#"&>.'&67327& &67>7!& &67>7>.'&67327!LD;JR@;H)#  127! + !  118! NN+!!  117! )#!  117! >3LT3-RR+<& 65  #K>+<& 65  #K>m+<& 65  #K>5+<& 65  #K>1=I@ -@4bb`  Q=M=>Y@ HF%($&%%& +%#"&54>76&#&7>3263232672#"'&7654&#"4632#"& -P71e {=wHnN'Y%#FmL L#$2lKD;JR?;H?. !4f4(DPT+p}7)'ZbC3GP'-3LT3-RR4mK(PX@/U  SK = K  >@-UU  S K  >Y@mlgfc_\[POLIFE>=873C+462"$462">.'&67327& &67>7!& &67>7>.'&67327!AcAAc>Cc??c )#  127! + !  118! NN+!!  117! )#!  117! >bDDbBBbDDbB+<& 65  #K>+<& 65  #K>m+<& 65  #K>5+<& 65  #K>MU@R- = <b  b `U= M= >KIA?;:&%%' +462"$462"#"&54>76&#&7>3263232672#"'&7654&#"H5N77N5N55Nk -P71e {=wHnN'Y%#FmL L#$2lmM88M66M88M6\?. !4f4(DPT+p}7)'ZbC3GP'-4H-y@cpnK(PX@6b  SQK = K  >@4bU  SQ K  >YY@!yysqigb_\[TSNMJGDCCC"+'7&67>7>.'&67327!>.'&67327& &67>7!&+#".'6732676. |J 117! )#!  117! >>)#  127! + !  118! NN+!! . d $I*/==0 5  #K>5+<& 65  #K>?+<& 65  #K>+<& 65  #K>m+<& 6T&Mk, +)%HX@8 H#" @<b  b `bQ= M= >Y@VTLJ&%%(%6 +%#"'#".'6732676./7&54>76&#&7>3263232672#"'&7654&#" -$ d $I*/==1$ x1e {=wHnN'Y%#FmL L#$2l?.A&Mk, +)%  !4f4(DPT+p}7)'ZbC3GP'-4h- j@ @*  USQ K  >Y@jidc`\YXMLIFCB3C%+267#"&7>.'&67327& &67>7!& &67>7>.'&67327!e 3+`lu^)#  127! + !  118! NN+!!  117! )#!  117! >jRgbW~+<& 65  #K>+<& 65  #K>m+<& 65  #K>5+<& 65  #K>T=JU@R-JDC><bb`  R=M=>HF($&%%& +%#"&54>76&#&7>3263232672#"'&7654&#"267#"&7 -P71e {=wHnN'Y%#FmL L#$2le!2+alu?. !4f4(DPT+p}7)'ZbC3GP'-RfbV~-,@@0 1 ;<:9K(PX@'  U QK =K >@%U  U QK >Y@.->=8642-@.@3C +>.'&67327& &67>7267#"'&#"'>2)#!  117! +!!  117! $7!/J3EA%'6/)_G3/+<& 65  #K>+<& 65  #K>-> 5-.4 ]Z:-5D ,@i@f0 1 ;<:9b``  U RM ==>.->=8642-@.@%$$$" +4632#"&32672#"'&7654/&67632267#"'&#"'>2KD;JR?;HW%Y%#FmLZ $R я* x$7!/J3EA%'7/)_G33LT4-RSn}7)'ZbC-9'4 +9]^-> 5-.4 ]Z:-4<Im?= :K(PX@& dUK = K >@$ dUU K >Y@DB873C +462"$462">.'&67327& &67>7#"&7676RAcAAc>Cc??c)#!  117! +!!  117! I@ PbDDbBBbDDbB+<& 65  #K>+<& 65  #K>!$?i  F(V +7EM@J < d  db``V=>A?;9$$$%$" +32672#"'&7654/&67632>2#"&%>32#"&632'"&767YW%Y%#FmLZ $R я*  CX9C,-8D+-8B-,9 < ! n}7)'ZbC-9'4 +9]0@A/.AA./AA/.AA+* 4Q\je@ <`]:K(PX@dK =M >@dUM >Y@ecXWTQNM?:OC +>.'&67327>7676'&67327&#""56'.'& &67>7"&7676)#!  117! D.imJ 9 %H3L'\/G/Y !N/#$J+!!  117! $B &#e/+<& 65  #K>BWA 2  1 5I%HJ 2  9W) +<& 65  #K>#@v OIWb@_ <<  b  `b  b`` =M=>SQMKHF#)"$)%& +%#"&54>76&#&7>326?>32#"'&#"3272#"&'.'&#"672#"&767 .P72d {=*>Rj3?G?0&.JRjP77 .<+T=Y42  M!+' ?. !4f4(D6F^sK.7X6I^l9)%W#ZX`r-"!2/! 43Q-\hh@U  QM >Y@gea_XWTQNM?:OC +>.'&67327>7676'&67327&#""56'.'& &67>74632#"&)#!  117! D.imJ 9 %H3L'\/G/Y !N/#$J+!!  117! LC;JR?;H/+<& 65  #K>BWA 2  1 5I%HJ 2  9W) +<& 65  #K>3LT3-RR3IU@  <@Cbb  b`` Q=M=>Y@TRNLHF#)"$)%& +%#"&54>76&#&7>326?>32#"'&#"3272#"&'.'&#"4632#"& .P72d {=*>Rj3?G?0&.JRjP77 .<+T=Y42 LC;JR?;H?. !4f4(D6F^sK.7X6I^l9)%W#ZX`r-"!3LT3-RR4Q-\hk@U  QM >Y@_]eb]h_hXWTQNM?:OC +>.'&67327>7676'&67327&#""56'.'& &67>7!"7>3!2)#!  117! D.imJ 9 %H3L'\/G/Y !N/#$J+!!  117! \#$/+<& 65  #K>BWA 2  1 5I%HJ 2  9W) +<& 65  #K>()IUf@c <<bb  b``  R=M=>LJROJULUHF#)"$)%& +%#"&54>76&#&7>326?>32#"'&#"3272#"&'.'&#"!"7>3!2 .P72d {=*>Rj3?G?0&.JRjP77 .<+T=Y42 \#$?. !4f4(D6F^sK.7X6I^l9)%W#ZX`r-"!()43-/;v@#ZUQK >Y@:842('$ /. +%2676&#!&67>7>.'&6732>334632#"&937>9I 117! )#! O#I@ 117! !FLD;JR?;IV}5  #K>5+<& 65  #K>ɢd3LT3-RR3 *fK(PX@)b`=M=M>@&b`Q=M>Y@ #'%$"+4632#"&6&#&7>3232672'"547LC;JQ@;H+4d {=Y%#h3LT3-RRf4(DZ<%7)&t9Y43y/;G@,Z  UUQ K >Y@><DAG:842('$ /. +%2676&#!&67>7>.'&6732>334632#"&!"7>3!2937>9I 117! )#! O#I@ 117! !FLD;JR?;IQ %'V}5  #K>5+<& 65  #K>ɢd3LT3-RR))3 6K(PX@2b` U=M=M>@/b` UQ=M>Y@31.-+)"   +!"7>3!24632#"&6&#&7>3232672'"547r %&LC;JQ@;H+4d {=Y%#hN()l3LT3-RRf4(DZ<%7)&t9Y4-/;y@$ZU QK >Y@20850;2;('$ /. +%2676&#!&67>7>.'&6732>33!"7>3!2937>9I 117! )#! O#I@ 117! !Fs\$%V}5  #K>5+<& 65  #K>ɢ()6 *>@;b`Q=M>'%"!   +!"7>3!26&#&7>3232672'"547\$%U4d {=Y%#h()f4(DZ<%7)&t9Y4f-/:|@<875329K(PX@$ZeK =K >@"ZeUK >Y@000:0:('$ /. +%2676&#!&67>7>.'&6732>33&''67937>9I 117! )#! O#I@ 117! !F9-l1{9m#SV}5  #K>5+<& 65  #K>ɢmkN]\c= )B@?9b`e=M>&$!   +&''676&#&7>3232672'"547d-l1{8m#T4d {=Y%#hmkN]\cf4(DZ<%7)&t9Yr=KA> :K PX@4  dbX  `K =  K  >K!PX@5  db`  `K =  K  >K(PX@9  db`  `K =  K  = >@7  db`  `U  K  = >YYY@FD<;8410G" +%#'"&'#&#"&67>76&'&673%&#"&67>#"&7676Frs  #Z wi3w RIg  ' b7 le T b.$B &$fX V\ 2  1 N`Ls* 2 s 1 5V3 2  1 -?w NF^lY@V(<  bbb` = M= >hfb`XVLJ)'$&%'% +654/&67632>326232672#"'&7674#"#"&54>7674'"#"&54>7632#"&767+%R уVHnHnM'X%#FlL!K3:ZW -P7\)#dRPN -P7 M!+' V;4 VyjPTPT+p}7)'ZbC3GP'Jrr<+ $5R95Teh 8* $52/! r=IK PX@7bX  `  UK =  K  >K!PX@8b`  `  UK =  K  >K(PX@<b`  `  UK =  K  = >@:b`  `  UU  K  = >YYY@HFB@<;8410G"+%#'"&'#&#"&67>76&'&673%&#"&67>4632#"&Frs  #Z wi3w RIg  ' b7 le T b.LC;JR?;HX V\ 2  1 N`Ls* 2 s 1 5V3 2  1 -)3LT4-RS5^jV@S(<bb` M = M= >igcaXVLJ)'$&%'% +654/&67632>326232672#"'&7674#"#"&54>7674'"#"&54>74632#"&+%R уVHnHnM'X%#FlL!K3:ZW -P7\)#dRPN -P7LC;JR?;HV;4 VyjPTPT+p}7)'ZbC3GP'Jrr<+ $5R95Teh 8* $5#3LT3-RR3r-=IK PX@9bX  `K =  K  = M  >K!PX@:b`  `K =  K  = M  >K(PX@>b`  `K =  K  = = M  >@9b`  `U Q  K  = >YYY@HFB@<;8410G"+%#'"&'#&#"&67>76&'&673%&#"&67>4632#"&Frs  #Z wi3w RIg  ' b7 le T b.LC;JR?;HX V\ 2  1 N`Ls* 2 s 1 5V3 2  1 -G3LT3-RR3^j@ (@3bb` Q M= >Y@igcaXVLJ)'$&%'% +654/&67632>326232672#"'&7674#"#"&54>7674'"#"&54>74632#"&+%R уVHnHnM'X%#FlL!K3:ZW -P7\)#dRPN -P7LC;JR?;HV;4 VyjPTPT+p}7)'ZbC3GP'Jrr<+ $5R95Teh 8* $5'3LT3-RRm<HǵK PX@&bUK = = >K(PX@&bUK = =>@$bUU =>YYY@ $*MJ$+!#&'&#"&#"&67>7.'&677>.'&673274632#"&3^!   v'1< & qKF; P)n'1< ( qIKD;JR?;H'4/))HK)  2  1 ?V-P 2 BH@HK)  2  1 ?3LT4-RS@L!@2b```  U=>Y@ KI))($&'% +6'4/&67632>3232672#"'&7654&#"#"&54>74632#"&$R уjHoN'Y%#FmL L"%3nN ,P7 KD;JR?;HV;4 VylPT+p}7)'ZbC3GP'- 8) %4 3LT3-RR3-<H̵K PX@(bK = = =M>K(PX@(bK = ==M>@#bUQ =>YYY@ $*MJ$+!#&'&#"&#"&67>7.'&677>.'&673274632#"&3^!   v'1< & qKF; P)n'1< ( qI LD;JR?;I'4/))HK)  2  1 ?V-P 2 BH@HK)  2  1 ?3LT3-RR\@LG@D!<b```  R=>KI))($&'% +6'4/&67632>3232672#"'&7654&#"#"&54>74632#"&$R уjHoN'Y%#FmL L"%3nN ,P7 LD;JR@;HV;4 VylPT+p}7)'ZbC3GP'- 8) %4P3LT3-RR-<H͵K PX@&bQK = = >K(PX@&bQK = =>@$bUQ =>YYY@?=EB=H?HMJ$ +!#&'&#"&#"&67>7.'&677>.'&67327!"7>3!23^!   v'1< & qKF; P)n'1< ( qIa\$%'4/))HK)  2  1 ?V-P 2 BH@HK)  2  1 ?#()@LM@J!<b```  R=>CAIFALCL)($&'% +6'4/&67632>3232672#"'&7654&#"#"&54>7!"7>3!2$R уjHoN'Y%#FmL L"%3nN ,P7 S]$%V;4 VylPT+p}7)'ZbC3GP'- 8) %4a()f-<G@K PX@$beK = = >K(PX@$beK = =>@"beU =>YYY@===G=GMJ$+!#&'&#"&#"&67>7.'&677>.'&67327&''673^!   v'1< & qKF; P)n'1< ( qI-l1{9m#S'4/))HK)  2  1 ?V-P 2 BH@HK)  2  1 ?mkN]\cR@KQ@N!AAAKAK)($&'% +6'4/&67632>3232672#"'&7654&#"#"&54>7&''67$R уjHoN'Y%#FmL L"%3nN ,P7 -l1{9m#SV;4 VylPT+p}7)'ZbC3GP'- 8) %4mkM][cm )>g@d-.8<7;:dU U M =M >+* <:531/*>+>$")) % +#"&7676"32654&"56%2267#"'&"'>32? Q~:\xw )(h0(?y,:7!/'#P78<*m$@i  Geqe { 1D ;239 fc?2% *DM@Jdd   U  UM=M>,+B@<;8642.-+D,D&%%&$! +632#"&767724.#"47632#".272#"'&#""&7>32{ < ! y1'}b\?B/&u:L31A&%l4W9!!,) '9O`1MFI1Gsm\XXg+#%%Rl3j,Ai@f1 ; <: ;0:  I  U  U M =M >.- ?=8642-A.A'%, ,+462"$462""32654&"56%2267#"'&"'>327N77N7N77N~:\xw )(h0(?y,:7!/'#P78<*N77N77N77N7eqe { 1D ;239 fc?2) !3MU@R< U  U   UM=M>54KIEDA?=;764M5M&%%#$$$+>2#"&%>32#"&724.#"47632#".272#"'&#""&7>32UCX:C,-8D+-8B-,9[y1'}b\?B/&u:L31A&%l4W9!0AB/.AA./BA0.AA9O`1MFI1GsmbXXg+#&%Rm4k )5C@@: UM =M>,* 2/*5,5$")) % +32676'&"32654&"56%2!"7>3!2J) @+~:\xw )(W!k&=h  Heqe {3)) (7=@:ddVM=M>750.%"((&%%! +724.#"47632#".!"7>3!2#"/7>36y1'}b\?4 %'Q   D&\9O`1MFI1Gsm;() $'m )5H@E:d UM =M>,* 2/*5,5$")) % +#"&7676"32654&"56%2!"7>3!2? R~:\xw )(W!m$@i  Geqe {3))' (6=@:ddVM=M>20,*%"((&%%! +724.#"47632#".!"7>3!2632#"&767y1'}b\?4 %' < ! \9O`1MFI1Gsm;(),) 4H9GK@H <=::dUM  =K >B@43('$! 97 +2"'&73267#"& &67>7>.'&672$"&7676`$))# '9y;L9+!!  117! )#! '!D$B &#e;C^pb;+Im+<& 65  #K>5+<& 6@v O0;I@%!6 <9KPX@6b`b=M=N=>@1dddbM=N=>Y@ $$#&&;&! +632'"'&#'54?67"'6?>324#"32>672#"&767yspR@P' P'\f-%Fp8V  fPl?XR9 M +( ;Hq`qC!' = ; NT;IXwe=k8 -8FX2/! 4H9EK@H <  UUM  =K >DB><43('$! 97 +2"'&73267#"& &67>7>.'&672$4632#"&`$))# '9y;L9+!!  117! )#! '!LC;JR?;H;C^pb;+Im+<& 65  #K>5+<& 6#3LT3-RRF0;G]@Z%!6 <9bbM =M=N=>$%#&&;&! +632'"'&#'54?67"'6?>324#"32>4632#"&yspR@P' P'\f-%Fp8V  fPl?XR9LC;JR?;H;Hq`qC!' = ; NT;IXwe=k8 -8FX3LT4-RS3z GSZ@W<  UU M  =K =M> RPLJA@762/+*%$  GE#" +4&#"32>23#"'.#& &747>7674&'&7472$4632#"&-yXL9NTJxh: %V@o? >K  117! ?L '!3LC;JR?;Hq^Imi%SVb?RH? %d'1">#! "  #K>51!?#" 3LT3-RRF/;=@:<b`M =M=>$$"$'%'+#"&54>7654/&67632>32#"'&#"4632#"&)N -P7 L%R у@K?A?0&-)`8LD;JR?;I 8) %3_R;4 VyJ/7X6tk3LT4-RS33z; GS@4U Q M  =K =M>Y@ RPLJA@762/+*%$  GE#" +4&#"32>23#"'.#& &747>7674&'&7472$4632#"&-yXL9NTJxh: %V@o? >K  117! ?L '!KD;JR?;Hq^Imi%SVb?RH? %d'1">#! "  #K>51!?#" 3LT3-RR3/;p@'b`RM=>Y@ $$"$'%'+#"&54>7654/&67632>32#"'&#"4632#"&)N -P7 L%R у@K?A?0&-)`LD;JR?;I 8) %3_R;4 VyJ/7X6t^3LT3-RR33zy GS_@=   UU Q M =K =M>Y@$VT \YT_V_RPLJA@762/+*%$  GE#" +4&#"32>23#"'.#& &747>7674&'&7472$4632#"&!"7>3!2-yXL9NTJxh: %V@o? >K  117! ?L '!KD;JR?;HY %&q^Imi%SVb?RH? %d'1">#! "  #K>51!?#" 3LT3-RR))3/;G@0b`  URM=>Y@>G$$"$'%' +#"&54>7654/&67632>32#"'&#"4632#"&!"7>3!2)N -P7 L%R у@K?A?0&-)`LD;JR?;I %' 8) %3_R;4 VyJ/7X6t^3LT3-RR))3z; GS^@[<U  Q M  =K =M>JH PMHSJSA@762/+*%$  GE#" +4&#"32>23#"'.#& &747>7674&'&7472$!"7>3!2-yXL9NTJxh: %V@o? >K  117! ?L '!n\$%q^Imi%SVb?RH? %d'1">#! "  #K>51!?#" ()/;A@><b`RM=>20850;2;"$'%' +#"&54>7654/&67632>32#"'&#"!"7>3!2)N -P7 L%R у@K?A?0&-)`\#% 8) %3_R;4 VyJ/7X6t()[58D@&@4b`bUM =M>Y@ $$,%.%# +#"'.#"#"&#&5476322654&'.547>324632#"&5''R1Z\8gU%/̌ekvz;Sm{LC;JR?;H v=V`2w_F$GeCoDh(%#w t`VtIQq}*26e3LT4-QRh @6@3bUM=M>?=20.,(&$" +4632#"&&'.#"#"&5463232654'.54>32LC;JR?;H5#>  +D;#纲<P3+q'ChEsq=b73LT3-RR+= (# 5J=2{odb-K(PX@6b`bM =M=M>@3b`bQM =M>YY@ $$,%.%# +#"'.#"#"&#&5476322654&'.547>324632#"&5''R@$bQM=M>Y@?=20.,(&$" +4632#"&&'.#"#"&5463232654'.54>32LD;JR@;H5#>  +D;#纲<P3+q'ChEsq=b3LT3-RRR+= (# 5J=2{odb-@9db`b  UM =M>Y@ QO((,%.%# +#"'.#"#"&#&5476322654&'.547>32#"&76764632#"&5''RK(PX@1b`b =M=N>@,dddbM=N>YY@JH=;9731'%$#" +46327632#"&76?.&'.#"#"&5463232654'.54>326D=4# M!+' 0 &25#>  +D;#纲<P3+q'ChEsq=b.D%10! =>B+= (# 5J=2{odb-<&@1db`bM =M>Y@ +,%.%#+#"'.#"#"&#&5476322654&'.547>32>7&5463267&5''RFD9753/-#!' +#&'763267&'.#"#"&5463232654'.54>32f,6  V6B  +D;#纲<P3+q'ChEsq=b;[$ BL.0L$/+= (# 5J=2{odb-K(PX@9b`bU =M= M  >@6b`bU  Q =M>YY@LJFD@?;9%.%# +#"'.#"#"&#&5476322654&'.54767&746324632#"&5''R@,bUQM=M>Y@KI><:842(&$$$" +4632#"&4632#"&&'.#"#"&5463232654'.54>32LC;KR@;HLD;JR@;H5#>  +D;#纲<P3+q'ChEsq=b73LT3-RR3LT3-RRR+= (# 5J=2{odb-KPX@,d b  = M =L >KPX@(d b M  =L >K&PX@,d b  = M =L >@*d b U  =L >YYYY@9643/.%%53 +%& &67>76&+""'6743!.546323 72#.+"N8z  }N5ad33B( !%KD;J+$9is-39{; 2  1 ;{^mbw B&3LT3A s`fkP-9iK*PX@)bM=M=M>@'bUM=M>Y@ $%#$$! +7#"5476?>7>3#32672'"544632#"&} 2l =A+)zX%#hKD;JR?;H !f<' \ L ;%7)&t<}3LT4-RS39/;K PX@ $&<KPX@ $&<KPX@ $&<@ $&KPX@0b = M =K = M  >KPX@,b M =K = M  >K&PX@0b = M =K = M  >K(PX@.b U =K = M  >@+b U Q =K >YYYYY@:842-*153 +%& &67>76&+""'6743) 72#.+"4632#"&N8z  }N5ad33B( 9is-39KD;JR?;H{; 2  1 ;{^mbw  s`fk3LT3-RRj3P-9K(PX@)bM=M=M>K*PX@&bQM=M>@$bUQM>YY@ $%#$$! +7#"5476?>7>3#32672'"544632#"&} 2l =A+)zX%#htLD;JR?;I !f<' \ L ;%7)&t<3LT3-RR9/;gK PX@ $&<KPX@ $&<KPX@ $&<@ $&KPX@.b  R = M =K >KPX@*b  R M =K >K&PX@.b  R = M =K >@,b U  R =K >YYYY@20850;2;-*153 +%& &67>76&+""'6743) 72#.+"!"7>3!2N8z  }N5ad33B( 9is-39]$%{; 2  1 ;{^mbw  s`fk()*P-9kK*PX@'b QM=M>@%bU QM>Y@0.63.909#$$! +7#"5476?>7>3#32672'"54!"7>3!2} 2l =A+)zX%#h\#% !f<' \ L ;%7)&tKPX@,b   e = M =K >KPX@(b   e M =K >K&PX@,b   e = M =K >@*b   e U =K >YYYY@000:0:-*153 +%& &67>76&+""'6743) 72#.+"&''67N8z  }N5ad33B( 9is-39s-l1{9m#S{; 2  1 ;{^mbw  s`fkmkN]\cfP-8n653109K*PX@%beM=M>@#beUM>Y@...8.8#$$! +7#"5476?>7>3#32672'"54&''67} 2l =A+)zX%#h]-l1{8m#T !f<' \ L ;%7)&t@U QM>Y@ RQ$$$C-O% +32>764.'&67327# '&6&'&67327>32#"&%>32"&d4R`\H!c 4(8 4 7+3 ]_m' 2  1  %E@/۬ZVu,Jy= 2  1 ;q0@A/.AA./AA/.AA;FR]@Y @8b```   R=N >Y@\[WUQOKI#*)'$&' +%067'#"&5476#""&'>323267>3232672#"'&74>32#"&%>32"&s ZFpN'X$#Fl+- "L42osO ,P7 kX%#NB-,9D+-8C,-8BX9 %pSUSDF}7)'Zb!"4?ir? 9* %3?&7)'C0@A/.AA./AA/.AA-<P@@ A K@%U  U QM>Y@>=NMHFDB=P>PC-O% +32>764.'&67327# '&6&'&67327267#"'&#"'>2d4R`\H!c 4(8 4 7+3 ]_m' 2  1  %E@/۬ZVu,Jy= 2  1 ;b-> 5-.4 ]Z:-r;F[w@tJ K U HGYWRPNLG[H[#*)'$&'+%067'#"&5476#""&'>323267>3232672#"'&74267#"'&#"'>32s ZFpN'X$#Fl+- "L42osO ,P7 kX%#N$7!/J3EA$'7/)`@AG3 %pSUSDF}7)'Zb!"4?ir? 9* %3?&7)'C-> 5-.4 ]Z:-d-<G]EDB@?9K(PX@eK =M>@eUM>Y@===G=GC-O%+32>764.'&67327# '&6&'&67327&''67d4R`\H!c 4(8 4 7+3 ]_m' 2  1  %E@/۬ZVu,Jy= 2  1 ;mkN]\cf;FQ_@\GGGQGQ#*)'$&' +%067'#"&5476#""&'>323267>3232672#"'&74&''67s ZFpN'X$#Fl+- "L42osO ,P7 kX%#N-m1{9m#T %pSUSDF}7)'Zb!"4?ir? 9* %3?&7)'CmkN]\c Wb@ R;:K(PX@1d  U M= K  =N>K-PX@,d  U G M=N>@*d I  U  UN>YY@`^[XVU"$#-O+% +"&767632>764.'&67327# '&6&'&67&7>327272'327&'&'" @ Pd4R`\H!c 4(8 4 7+3 ]_m' 2  1  %E@/۬ZVu,Jy= 2 Zx9 a6 0 ; &;`n@l0  @Odb  b  ` ` `V U  = N >Y@(jhdb\ZWVTRHF=;42.-+)#!  +272#"'&#""&7>32067'#"&5476#""&'>323267>3232672#"'&74632'"&567B/&u:L31A&%l4W9!A ZFpN'X$#Fl+- "L42osO ,P7 kX%#N < ! XXf+#%%Rl3% %pSUSDF}7)'Zb!"4?ir? 9* %3?&7)'C+* u S_K(PX@+  U U K =M>@)  U U UM>Y@ VT \YT_V_RQNJGF@>1-  $ +"&54632"&5463232>764.'&67327# '&6&'&67327!"7>3!2N>5/%!3%//%#11Md4R`\H!c 4(8 4 7+3 ]_m' 2  1  %E@/۬ZVu,Jy= 2  1 ;s));FR^jn@k<b```   U   U=N >IGigca][WUOLGRIR#*)'$&'+%067'#"&5476#""&'>323267>3232672#"'&74!"7>3!2>32#"&%>32#"&s ZFpN'X$#Fl+- "L42osO ,P7 kX%#N $&B-,9C,-8C,-8B-,9 %pSUSDF}7)'Zb!"4?ir? 9* %3?&7)'C()'0@A/.AA./AA/.AAX^.B@3=<<;2:K PX@'dXd=K =>K PX@'dXd=K = >K(PX@&ddd=K =>@$dddV=>YYY@0/@?:864/B0B-+"F +>7>'#"''&>7>'#"'27267#"'&#"'>2h1cL 'u %- b1 d,<; y 1/3 -1&$7!/J3EA%'7/)_G3XT5 2  1   'P\!"  2  1  1-/0,> 5-.4 ]Y9-o#8@(2<1;':K PX@%bUU=>K PX@%bUU= >@%bUU=>YY@%$64/-+)$8%8&#() +%676'&5432#"'&'"&'>32267"'&'"'>32^ 2 |7D7>D 5-/4 ]Z:-3X-.:K PX@bcK = >K(PX@bcK =>@bcU>YYY@ 9731-+"F +>7>'#"''&>7>'#"'274632#"&h1cL 'u %- b1 d,<; y 1/3 -1LD;JR@;HXT5 2  1   'P\!"  2  1  1-/03LT3-RR3o#/K PX@b= =M>K(PX@b==M>@bQ=>YYY$$&#()+%676'&5432#"'&'"&'>324632#"&^ 2 |7D7>DK PX@d = >K(PX@d =>@dM>YYYDBJ%(M+76.'&67327"&'"&'.'&67327732676'&^u 'w /# v' Ne-1#N3#411 zǓ 7A-\<`5 &SVJ   2  1 5T/1/b-/  2  1  #$A59 +Fw Q67F@  K PX@'bb ==M>KPX@'bb ==M >KPX@'bb ==M>@$ddb=M>YYYY@ %)),&##+''"'&'"&'>32>76676'&'432#".'#"/7>32_AE +N#2N@=@ 1@N;KOm2 |7E7P^#_+# j)X1h:+ /]l/!D@lAKJ6+Y1s|/10 )-<J~@ 9<@=:K PX@d =>K PX@d = >K(PX@d =>@dM>YYYECJ%(M+76.'&67327"&'"&'.'&673277#"&7676^u 'w /# v' Ne-1#N3#411 zǓ 7A-\<$B &$fVJ   2  1 5T/1/b-/  2  1  #$A59 ? w O67E@  K PX@'bb ==M>KPX@'bb ==M >KPX@'bb ==M>@$ddb=M>YYYY@ $%),&##+''"'&'"&'>32>76676'&'432#".'632#"&767_AE +N#2N@=@ 1@N;KOm2 |7E7P^#_+# M +' h:+ /]l/!D@lAKJ6+Y1s|/1010! }<HS@ O9K PX@U = >K(PX@U =>@UM>YYY@ $$/J%(M+76.'&67327"&'"&'.'&673277>32#"&%>32"&^u 'w /# v' Ne-1#N3#411 zǓ 7A-\K PX@$b U=M>KPX@$b U=M >@$b U=M>YYY@ MK$$),&## +''"'&'"&'>32>76676'&'432#".'>2#"&%>32#"&_AE +N#2N@=@ 1@N;KOm2 |7E7P^#_+# CX:C,-8D+-8B-,9h:+ /]l/!D@lAKJ6+Y1s|/100AA0.@@./BA0.@@ <H9K PX@U = >K(PX@U =>@UM>YYY$/J%(M+76.'&67327"&'"&'.'&673277%4632#"&^u 'w /# v' Ne-1#N3#411 zǓ 7A-\K PX@"bU=M>KPX@"bU=M >@"bU=M>YYY@ $&),&##+''"'&'"&'>32>76676'&'432#".'4632#"&_AE +N#2N@=@ 1@N;KOm2 |7E7P^#_+# LD;JR@;Hh:+ /]l/!D@lAKJ6+Y1s|/103LT3-RR3-<H9K PX@ = =M>K(PX@ ==M>@QM>YYY$/J%(M+76.'&67327"&'"&'.'&6732774632#"&^u 'w /# v' Ne-1#N3#411 zǓ 7A-\K PX@$b=M=M>KPX@$b=M =M>K(PX@$b=M=M>@!bQ=M>YYYY@ $&),&##+''"'&'"&'>32>76676'&'432#".'4632#"&_AE +N#2N@=@ 1@N;KOm2 |7E7P^#_+# `LC;JR?;Hh:+ /]l/!D@lAKJ6+Y1s|/10C3LT3-RR>![d.@IU K  >Y@ZYVRC$CC +%.&#"&6767>'.'&67327&746327676'&67327&#"&67>ywZ= Sdx O'A@ wQpCKD;J<2P%d u= HRv N!%KD w NB# d\ 2  1 T!!+P36&Q3LT3&J &59! s\ 2  1 V#)3-N5 2  1 96 I^@[H7* 0 ECB@=;$$"$&%#$" +4632#"&&#"&'>327>32#"'&#"327#"&/#"&543232767&LD;JR@;Ht,*HI/$%51KyY9U7(..763UM:?'B.SVK7Zc,d )z%73LT3-RR#"4*^5UDni4./J$163^@UZwb4^3<2d754l>QYakK(PX@%    UK = K >@#    UU K >Y@a`]\YXUTPOCCCC+%.&#"&6767>'.'&673277676'&67327&#"&67>462"$462"ywZ= Sdx O'A@ w P%d u= HRv N!%KD w NAcAAc>Cc??cB# d\ 2  1 T!!+P36 1 59! s\ 2  1 V#)3-N5 2  1 9sbDDbBBbDDbB6 Th@eSB5$ ; <: ;Z  bUN= M   >PNMKHF?=97"$&%#$$"+>32#"&%>32"&&#"&'>327>32#"'&#"327#"&/#"&543232767&jB-,9D+-8C,-8BX9o,*HI/$%51KyY9U7(..763UM:?'B.SVK7Zc,d )z%0AA0.@@./BA0.@@H#"4*^5UDni4./J$163^@UZwb4^3<2d754l!I^,@UUK >Y@ M$$CC +%&#"&67>76&'.'&67327&54632#"'676'&67327J9{ r }N6 =A wL~ALC;JR? P# ; =Bv JI)){; /  / <{90-R1 2 &P3LT3-R%59 Z 2  1 #9J3wT|3?A@><bbU=M>$&$$(-%"+.#"&'>326'4&54632#"&5463232764632#"& +MH0%/7C7L)0T.:y]^-`C'8 ;?rv]KD;JR?;H $4*`DSoqe(/0L^9>?R $13LT3-RR-/<K PX@<;642:KPX@<;642:KPX@<;642:K$PX@<;642:@<;642:YYYYK PX@'b`M =N >KPX@+b`M =N = >KPX@'b`M =N >K$PX@+b`M =N = >K(PX@/b` =M =N = >@-b`U =N = >YYYYY@&  /. +%2672#&)"767>&#!""'6743)26323&'&76?\^85N- % q5=: Z7x #EelZ| jT   Bq&6@ <;976  +<9K(PX@A  bb```M= K  =>@?  bb```   UM=>Y@444>4>21#!!#) +632>76#"'&&'677"#"&#""'>3236&''67+" ;]G706.Ibn]TA$KPX@5b`M = N = = M  >KPX@1b`M = N = M  >K$PX@5b`M = N = = M  >K(PX@9b` =M = N = = M  >@4b`U  Q = N = >YYYYY@:842&  /. +%2672#&)"767>&#!""'6743)263234632#"&\^85N- % q5=: Z7x /LC;JR?;HZ| jT   B`3LT3-RRO33?@ + <:K(PX@F  bZ````M== M  >@C  bZ```` QM=>Y@><8621#!!#) +632>76#"'&&'677"#"&#""'>32364632#"&+" ;]G706.Ibn]TA$KPX@3b`  QM = N = >KPX@/b`  QM = N >K$PX@3b`  QM = N = >K(PX@7b`  Q =M = N = >@5b`U  Q = N = >YYYYY@20850;2;&  /. +%2672#&)"767>&#!""'6743)26323!"7>3!2\^85N- % q5=: Z7x 6\#%Z| jT   B()O3?w@t + <:  bZ````  QM=>64<94?6?21#!!#) +632>76#"'&&'677"#"&#""'>3236!"7>3!2+" ;]G706.Ibn]TA$@>FC>I@I($&%%& +%#"&54>76&#&7>3263232672#"'&7654&#"!"7>3!2 -P71e {=wHnN'Y%#FmL L#$2l\$%?. !4f4(DPT+p}7)'ZbC3GP'-()-8D3K*PX@)b  UM=M>@'b  UUM>YY@CA=;$#$$! +7#"5476?>7>3#32672'"54>2#"&%>32#"&} 2l =A+)zX%#h!CX:C,-8D+-8B-,9 !f<' \ L ;%7)&t<0AB/.AA./BA0.AA6T7BM@  K PX@-b  U M ==M>KPX@-b  U M ==M >@-b  U M ==M>YYY@98MKGE>=8B9B),&## +''"'&'"&'>32>76676'&'432#".'"2676&>32#"_AE +N#2N@=@ 1@N;KOm2 |7E7P^#_+# O+=-V@0tHF\ vHGh:+ /]l/!D@lAKJ6+Y1s|/109+);;),8ccGEeT|R3>IV@S<bb  U M ==M>54IGCA:94>5>$$(-%" +.#"&'>326'4&54632#"&546323276"2676&>32#" +MH0%/7C7L)0T.:y]^-`C'8 ;?rv`+=-V@1tHF\ vHG $4*`DSoqe(/0L^9>?R $19+);;),8ccGEe+9N@K0<bM =M=N>9731*)'% ++ +232672#"'&74?'#"&547>3226>?&#"32* WX%#N ZCkpY|n &S"@fhZX}+9]n<&7)'C3H%q׾r  R:01'n#{COKPX@6bZ  UM=M=N>@7bb  UM=M=N>Y@ NL)$$)(&'#3 +6&+"?63267>7>32#".'.#"'"&546323267>4632#"&/DZ  -1. xFHtTLh-L7 ")7#%l5:mwG/=0 ?9-?LD;JR?;I #)0s794%:-3N +?%-)PPV9#?M)!1Ï3LT3-RR#{K@K @/bbM=M=N>Y@ (&'#>$$(+'"&546323267>?'?6&+"?63267>7>32#".'.#"#m%l5:mwG/=0 ?9-(" Z  -1. xFHtTLh-L7 ")7# >aPPV9#?M)!1ÏO@:  bb S M=M=N>Y@KKIG?=$#:$$( +#'"&546323267>76&+"?6326767#5367>32#".'.#"V %l5:mwG/=0 ?9-DZ  -1. @ZHtTLh-L7 "7#jO")PPV9#?M)!1Ï #)0-+OwH94%:-3N +?%V?DDcK PX@'&<KPX@'&<KPX@'&<@'&KPX@5b`M = =K =M>KPX@1b`M =M =M>@8b`bM =K =M>YYY@ K'&'$$ +76$32#"&"47632>.#"5676&#" &#"&67>K-} {#!>%/J^> +qV+h K>@V  O 118!`F<1 ͏"{v %/e;3>wS!#mBG%65  %J; 2| K PX@bM=M >@bM=M>YY,"%/#+3 6&'67.7>32#"'&#"#".+jdRLf#hJjLJ J)%hL p]uPo/PsibZVLmPyeBF%)(\VBP7{h\큃a)T} 3DBN:K(PX@+dd VK = M  >@(dd V QK >Y@MKGEA?87641-*) +!#&#"&67>7273&#"&672>&7!"4632#"&+C  / u' 3;L%wJ %- x % -9FLD;JR?;ID5 ^  2  1 =>[l  2  1  N#3LT3-RR3+9E0@3b QM =M=N>Y@DB><9731*)'% ++ +232672#"'&74?'#"&547>3226>?&#"324632#"&* WX%#N ZCkpY|n &S"@fhZXKD;JR?;H}+9]n<&7)'C3H%q׾r  R:01'n 3LT3-RR ?Be @< b  ` ` ` U VK >Y@ db^\TRLKA?87641-*) +!#&#"&67>7273&#"&672>&7!"#67676&#"#"&7>32+C  / u' 3;L%wJ %- x % -96 ;(""" "gLVAD5 ^  2  1 =>[l  2  1  N#$(J/##$  !&JM+9Wm@j0< b `b UM =M=N>VTPNGE?>9731*)'% ++ +232672#"'&74?'#"&547>3226>?&#"32#67676&#"#"&7>32* WX%#N ZCkpY|n &S"@fhZX7CG @ = 1 'j=I=}+9]n<&7)'C3H%q׾r  R:01'n38;BIQ( # %%MN  O\Q@N[VTR,<:ddd   VK > NLEDCA>:76+*" % +"&7676!#&#"&67>7273&#"&672>&7!"&'&76?: I+C  / u' 3;L%wJ %- x % -9#Eem!9_  A5 ^  2  1 =>[l  2  1  N# &6b  b= = M  = M=N>@>db  b = M  = M=N>Y@PNJHA@><64+)&%#!BB* +672&'"''673232672#"'&74?'#"&547>3226>?&#"322 6 @.U9n<" .m.HI* WX%#N ZCkpY|n &S"@fhZX}(%;Np'IE"B'_/+9]n;&7)'C3H%q׾r  R:01'n BO\Q@NNIGE WUA?87641-*) +!#&#"&67>7273&#"&672>&7!"&'&76?2676'&+C  / u' 3;L%wJ %- x % -9#EemC%  8D5 ^  2  1 =>[l  2  1  N# &6@? d bb  =M =M=N>Y@"::SQLJ:D:D9731*)'% ++ +232672#"'&74?'#"&547>3226>?&#"32&''67%#"/7>3* WX%#N ZCkpY|n &S"@fhZX5{9}FX.H!J  >"}+9]n<&7)'C3H%q׾r  R:01'np'SWO['_/ !$ B]n@ca^\RLGC NE @5  b ` ` U VK >Y@igXVJIA?87641-*) +!#&#"&67>7273&#"&672>&7!"&'#67&767&7>32'6?676&#"+C  / u' 3;L%wJ %- x % -9#sO 5eYg*gLV@1K !"#"D5 ^  2  1 =>[l  2  1  N# &%/$B. *5O.&KN%* D "#%  NQ_|@yFC>;2D=V <b`  bK = M = M=N>_]YWPOMJA@:8/-'& QQ +232672#"'&74?'#"&547>7#67676&#"#"''673&'63226>?&#"32* WX%#N ZCkpOn( <  ' <.Hk5{9)1F n &S"@fhZX}+9]n<&7)'C3H%q׾dIQ(# $'_p'"(;?  R:01'n %aq@n>< ;:db` U   V K > `^WVUSPLIH=<40"!% %  +267#"'&"'67&'&76?!#&#"&67>7273&#"&672>&7!"'=$4R8MG(+=42:0eJn7A8(+C  / u' 3;L%wJ %- x % -9*; 3+,1 b(C0 +V V[.*5 ^  2  1 =>[l  2  1  N#X+9DY@I S BA?=< 0FE::WUPNLJEYFY:D:D9731*)'% ++ +232672#"'&74?'#"&547>3226>?&#"32&''6?267"'&#"'>32* WX%#N ZCkpY|n &S"@fhZX5{9}FX.H$7".J3FA$'70)`@AG3}+9]n<&7)'C3H%q׾r  R:01'np'SWO['_,> 6-/4 ]Y9- 3BN[ZUSQ:K(PX@+dd VK = M  >@(dd V QK >Y@MKGEA?87641-*) +!#&#"&67>7273&#"&672>&7!"4632#"&&'&76?+C  / u' 3;L%wJ %- x % -9FLD;JR?;I2$DemD5 ^  2  1 =>[l  2  1  N#3LT3-RR&6=C0 +V ~3)+9EP@NMKIH 0K(PX@<  dbM =M=N= M  >@9  db QM =M=N>YY@"FFFPFPDB><9731*)'% ++ +232672#"'&74?'#"&547>3226>?&#"324632#"&&''67* WX%#N ZCkpY|n &S"@fhZXKD;JR?;H&-l1{8m#T}+9]n<&7)'C3H%q׾r  R:01'n 3LT3-RRmkM][c BP]f@cPJIC  XVNLGEA?87641-*) +!#&#"&67>7273&#"&672>&7!"#"&?3267#"&7676+C  / u' 3;L%wJ %- x % -9ho:rXT@ OD5 ^  2  1 =>[l  2  1  N#kl9678s$?i  F+9X@WUFE@? 0@A d bb VM =M=N>Y@ PNKICA=;9731*)'% ++ +232672#"'&74?'#"&547>3226>?&#"326323267#"'#"&76?&?* WX%#N ZCkpY|n &S"@fhZX < 3nX?y{:@! );}+9]n<&7)'C3H%q׾r  R:01'n+*z/IYd5 =!" BP]e@bPJIC  XVNLGEA?87641-*) +!#&#"&67>7273&#"&672>&7!"#"&?326732676'&+C  / u' 3;L%wJ %- x % -9ho:rXTJ( @D5 ^  2  1 =>[l  2  1  N#kl9678k&=h  HCQ@H2 @9d  bU M  = N=N>Y@QOKIBA?=75,*'&$"CC'&" +3267#"'&/7>32232672#"'&74?'#"&547>3226>?&#"323jdX?y{:AD% + * WX%#N ZCkpY|n &S"@fhZXVQQIYd5z'x4+9]n<&7)'C3H%q׾r  R:01'n %Bq@qRPOC lM H  @F  b   b  b ` U  UVK >Y@$omhfb`XVKJGEA?87641-*) +!#&#"&67>7273&#"&672>&7!"#"'#67&?676&#"#"&7>32267+C  / u' 3;L%wJ %- x % -9hC5 : "#" "gLV@D6cTD5 ^  2  1 =>[l  2  1  N#kr:$'+ #%  "&KN%.)78V,Xf|@y* ]G <,:  b  bUU M = M  = N   >.-fd`^WVTRLJA?<;97-X.X$'&&"+3267#"'#67676&#"#"&7>32&7232672#"'&74?'#"&547>3226>?&#"323jdX?y{:Q<4G @ = 1 (j= $* WX%#N ZCkpY|n &S"@fhZXVQQIYd5'%+;BIP( #%%N5>;+9]n<&7)'C3H%q׾r  R:01'n !(d@A< ;:bb`UU  V  K  >#"caZYXVSOLK@?73%$"(#( !! +267#"'&"'67#"&?3267!#&#"&67>7273&#"&672>&7!"'=$3R8LG)+<4o:rXT6=0;82+C  / u' 3;L%wJ %- x % -9*; 3+,1 !l9568QA **5 ^  2  1 =>[l  2  1  N#V:H\@M W  ?) JIZYTRPNI\J\HFB@9864.,#!::&" +3267#"&7232672#"'&74?'#"&547>3226>?&#"32267"'&#"'>23jdX?y{:z$* WX%#N ZCkpY|n &S"@fhZX&$7!/J3EA%'6/)_G3VQQIYd5;+9]n<&7)'C3H%q׾r  R:01'n?,> 6-/4 ]Y9- 3BN\@  <\VUO :K(PX@8  b `  U VK = M  >@5  b `  U V QK >Y@ ZXSQMKGEA?87641-*) +!#&#"&67>7273&#"&672>&7!"4632#"&#"&?3267+C  / u' 3;L%wJ %- x % -9FLD;JR?;I?go:rXTD5 ^  2  1 =>[l  2  1  N#3LT3-RR Lkl96783++9ER@0b UM =M=N= M  >@;b U QM =M=N>Y@ PNIHDB><9731*)'% ++ +232672#"'&74?'#"&547>3226>?&#"324632#"&267#"&7* WX%#N ZCkpY|n &S"@fhZXKD;JR?;He 2+`lv}+9]n<&7)'C3H%q׾r  R:01'n 3LT3-RRRfbV~31BN@8  @? ZbZ   U U Q=L >Y@ MKGE?<432/,+&%" BB +26762"'6&+;26&#!&67>76&'&67!272'.+"4632#"&fP B!  B AbZ$!y5!Y_2a oI1m 0C3/%1Hg#9JLD;JR@;H-R 7} H7/)9 = 1 ;y3{; 2  q%wd='3LT3-RR3 (4n@ @$bQM=M>Y@ $('$,!+4#">32672#"&54>324632#"&A"5;8/[`:mJvV(Vm'V}su@lLD;JR@;H9Ah"=h6@X!opR{NV9h>f3LT3-RRBd?@8  K(PX@Q b  ` ZbZ  UU N  ==L >@O b  ` ZbZ  U   UU=L >YY@$ca][TRLK?<432/,+&%" BB +26762"'6&+;26&#!&67>76&'&67!272'.+"#67676&#"#"&7>32fP B!  B AbZ$!y5!Y_2a oI1m 0C3/%1Hg#9J5 :("#" "gLV@-R 7} H7/)9 = 1 ;y3{; 2  q%wd='"#'J/##% "&KN BH@E:<bbUM=M>%$'+$,!+4#">32672#"&54767676&#"#"&7>32632A"5;8/[`:mJvV(Vm 1 (j=I= AHu@l9Ah"=h6@X!op&S ( # &%MO#V9h>fHBW@#G Q 8  @I ZbZ  U  U   UU=L >Y@(DCUSNLJHCWDW?<432/,+&%" BB +26762"'6&+;26&#!&67>76&'&67!272'.+"267#"'&#"'>32fP B!  B AbZ$!y5!Y_2a oI1m 0C3/%1Hg#9JA$7!/J3EA$'7/)`@AG3-R 7} H7/)9 = 1 ;y3{; 2  q%wd='-> 5-.4 ]Z:- (=_@\-7<6;,:bU UM=M>*);9420.)=*='$,! +4#">32672#"&54>32267"'&'"'>32A"5;8/[`:mJvV(Vm'V}su@l.$7!/J3EA$'7/)`@AG39Ah"=h6@X!opR{NV9h>f-> 5-/4 ]Z:- O\@  E '@=  d ZbZ   U U=L >Y@ WULIA@?<9832/*$! OO +&'&76?26762"'6&+;26&#!&67>76&'&67!272'.+""&7676#EelfP B!  B AbZ$!y5!Y_2a oI1m 0C3/%1Hg#9J: I&632672#"&54>32672&'#"&767&''673A"5;8/[`:mJvV(Vm'V}su@lP 6 $89O,  X-Gk9Ah"=h6@X!opR{NV9h>f(%x6K'4) O['_ O\@  E '@=  d ZbZ   U U=L >Y@ WULIA@?<9832/*$! OO +&'&76?26762"'6&+;26&#!&67>76&'&67!272'.+"2676'&#EelfP B!  B AbZ$!y5!Y_2a oI1m 0C3/%1Hg#9JmC%  8&632672#"&54>32&''67%#"/7>3A"5;8/[`:mJvV(Vm'V}su@l5{9}FX-GeJ  ?"9Ah"=h6@X!opR{NV9h>ffp'SWO['_/ !$!dqE@kgZ(/<K(PX@Qb  `  Zb   ZUU N  == L >@Ob  `  Zb   ZU   UU= L >YY@ #"a^VUTQNMHGD?9631.-'&"d#d$'&+#67676&#"#"&7>3226762"'6&+;26&#!&67>76&'&67!272'.+"&'&76?5 :("#" "gLV@fP B!  B AbZ$!y5!Y_2a oI1m 0C3/%1Hg#9J#Eem#'J/##% "&KN@-R 7} H7/)9 = 1 ;y3{; 2  q%wd='/&6=C0 +V ~N N[@XC@;8A:<bbbK =M=M>)')$,! +4#">32672#"&54>7#67676'&'&#"#"''673&'32A"5;8/[`:mJvV(Vm!Lmc =  1 '(P-Gk5{9&F u@l9Ah"=h6@X!opL{W IQ( *1'_p':?V9h>fa@$W %,9< ;:K(PX@Hd Zb  Z UU N  ==L >@Fd Zb  Z U   UU=L >Y@& ^[SRQNKJEDA<630.+*$#a a  +267#"'&"'67&'&76?26762"'6&+;26&#!&67>76&'&67!272'.+" '=$3R8LG)+<407%eDg:C8fP B!  B AbZ$!y5!Y_2a oI1m 0C3/%1Hg#9J*; 3+,1 ^(C0 *V OV0*7-R 7} H7/)9 = 1 ;y3{; 2  q%wd='P (3Gs@p8 B10.,+54))ED?=;94G5G)3)3'$,! +4#">32672#"&54>32&''6?267"'&#"'>2A"5;8/[`:mJvV(Vm'V}su@l5{9}FX-G$7!/J3EA%'7/)_G39Ah"=h6@X!opR{NV9h>ffp'SWO['_-> 6-/4 ]Y:-3BN[@8  @? ZbZ   U U Q=L >Y@ MKGE?<432/,+&%" BB +26762"'6&+;26&#!&67>76&'&67!272'.+"4632#"&&'&76?fP B!  B AbZ$!y5!Y_2a oI1m 0C3/%1Hg#9JLD;JR@;H#Eem-R 7} H7/)9 = 1 ;y3{; 2  q%wd='3LT3-RR&6@*dbQM=M>Y@555?5?$('$,! +4#">32672#"&54>324632#"&&''67A"5;8/[`:mJvV(Vm'V}su@lLD;JR@;H?-m1{9m#T9Ah"=h6@X!opR{NV9h>f3LT3-RRmkM][c4,NK PX@-bX  UK =K >K(PX@.b`  UK =K >@,b`  UUK >YY@ MK'&3C +>.'&67327& &67>7#67676&#"#"&7>32)#!  117! +!!  117! 5 :("#" "gLV@/+<& 65  #K>+<& 65  #K>#'J/##% "&KNb@@ 9 KPX@>  bb```  U ==>@?  b ` ` ` ` `  U =>YY@<:53'$$" +32672#"'&7654/#6767263676&#"#"&7>32632YW%Y%#FmLZ $'%@ .  1 'j=I=B* n}7)'ZbC-9')0DA # &%MO# +9]43-,8UK(PX@!K =K =M>@UQK >Y@ $'3C+>.'&67327& &67>74632#"&)#!  117! +!!  117! LD;JR?;I/+<& 65  #K>+<& 65  #K>3LT3-RRu3D ,8@2b``QM ==>Y@ $'%$$$" +4632#"&32672#"'&7654/&676324632#"&KD;JR?;HW%Y%#FmLZ $R я* LC;JR?;H3LT4-RSn}7)'ZbC-9'4 +9]3LT3-RR3D(cK(PX@!M =M=M>@QM =M>Y@'%!  +"32654&"56%24632#"&~:\xw )(:LD;JR@;Heqe {3LT3-RR3 (PK(PX@M=M=M>@QM=M>Y$&&%%!+724.#"47632#".4632#"&y1'}b\?`LD;JR?;I\9O`1MFI1Gsm3LT3-RR>K PX@-bXUM =M >@.b`UM =M >Y@=;75-+%$  +"32654&"56%2#67676&#"#"&7>32~:\xw )(=26 ;(""" "gLVAeqe {X$(J/##%  "&JM 77@4&<bUM=M>&%$''%!+724.#"67676&#"#"&7>32632#".546y1' 1 (j=I=MMb\?~\9O`1(( # &%MO#I1Gsl:w )6A@> <,*:dM =M> 1/$"))  +&'&76?"32654&"56%2#"&7676#Eemµ~:\xw )(: I&6@$dK =M=M>Y@ )&%%!+724.#"47632#".672&'#"&76?&''673y1'}b\? 6 t'?9U0   X-Gk \9O`1MFI1Gsmj(%j;T'8. O['_ )6B@? <-,*:dM =M> 1/$"))  +&'&76?"32654&"56%232676'&#Eemµ~:\xw )(C%  8&6J '6@ @(db =M=M>Y@64/-$"  +&''67724.#"47632#".#"/7>3 5{9}FX-Gy1'}b\?RJ  ?"Np'SWO['_9O`1MFI1Gsmj !$)7G@=;8  @'bUM =M>Y@+*CA20*7+7$")), +&'#67&767&7>32"32654&"56%26?676&#"#sP5 fZo/gKVA8J~:\xw )(!!#""&%/ B. *5V2&KM&,#Deqe {  ! #% N:EV@S85,*7<bb`K ==M>EC><::,&( +&'2#".54767#67676&#"#"''67724.#" 5{9 )EZG!b\?_` =  '! @-Gy1'Np'#:?:bl/. 64.;/;(&- -  +267#"'&"'67&'&76?"32654&"56%2'=%3R8LG)+<418.eHl:C8~:\xw )(*; 3+,1 `(C0 *V TZ0*5eqe {1Z '<f@c,6<5;+:U U  =M=M>)(:831/-(<)<$" +&''67724.#"47632#".267#"'&#"'>32 5{9}FX-Gy1'}b\?$7!.J3FA$'7/)_@AG4Np'SWO['_9O`1MFI1Gsm,> 5-.4 ]Y9-3(5k4/-+:K(PX@!M =M=M>@QM =M>Y@'%!  +"32654&"56%24632#"&&'&76?~:\xw )(:LD;JR@;H#Eeleqe {3LT3-RR&6K-PX@"Q =M=M>@"dQM=M>YY@)))3)3$&&%%!+724.#"47632#".4632#"&&''67y1'}b\?`LD;JR?;I-m1{9m#T\9O`1MFI1Gsm3LT3-RRmkN]\c39 ,K(PX@ M>@IMAY$" +4632#"&LD;JR@;H3LT3-RR %KPX@ e >@ d[Y$! +632#"&767h M!+' 10! E @ :[% +32676'&N`5 &S+Fw QY%KPX@ e >@ d[Y%% +#"/7>32j)Y0 )-=T!LK PX@bWIMA@bcIMAY$'&+#67676&#"#"&7>325 :("#" "gLV@#'J/##% "&KN+%@"bcIMA$'&+#67676&#"#"&7>32+CG @ = 1 'j=I=38;BIQ( # &%MNV=@:<: 9UIMA  +267"'&#"'>2?$7!/J3EA%'7/)_G3,> 6-/4 ]Y9-.=@:<: 9UIMA  +267#"'&#"'>32 2*C.?:!#2+%V:;?.1D :239 fc?239 ,K(PX@ M>@IMAY$" +4632#"&LD;JR@;H3LT3-RR#379 ,K(PX@ M>@IMAY$" +4632#"&#LC;JR?;H3LT3-RR3-<HUK(PX@!K =M=M>@UQM>Y@ $$C-O%+32>764.'&67327# '&6&'&673274632#"&d4R`\H!c 4(8 4 7+3 ]_m' 2  1  %E@/۬ZVu,Jy= 2  1 ;3LT3-RR3;FR@@6b``` Q=N >Y@QOKI#*)'$&' +%067'#"&5476#""&'>323267>3232672#"'&744632#"&s ZFpN'X$#Fl+- "L42osO ,P7 kX%#NKD;JR?;H %pSUSDF}7)'Zb!"4?ir? 9* %3?&7)'CB3LT3-RR<^K PX@-bX  UK =M>K(PX@.b`  UK =M>@,b`  UUM>YY@ ][(&C-O% +32>764.'&67327# '&6&'&67327#67676&#"#"&7>32d4R`\H!c 4(8 4 7+3 ]_m' 2  1  %E@/۬ZVu,Jy= 2  1 ;j#'J/##%  "&KN;O^b@_P#<b````  U=N  >XVKI*)+$&' +%067'#"&5476#""&'67.7>323267>3232672#"'&7467676&#"s ZFpN'X$#Owj=I= "L42osO ,P7 kX%#NB021  %pSUSDF}7)'f1%%MO#$4?ir? 9* %3?&7)'C#  @ :[& +#"&7676U$B &$f? w O} %KPX@ e >@ d[Y$! +632#"&767d M!+' 10! ? @ :[% +32676'&H`5 &S+Fw Q)k%KPX@ e >@ d[Y%% +#"/7>32i)X0 )-$Tn!LK PX@bWIMA@bcIMAY$(&+#67676&#"#"&7>32n26 ;(""" "gLVA#'J/##%  "&KN`%@"bcIMA$'&+#67676&#"#"&7>32CG @ = 1 'j=I=38;BIQ( # &%MNR\=@:<: 9UIMA  +267#"'&#"'>32$7".J3FA$'7/)_@AG3,> 5-.4 ]Y9-5 =@:<: 9UIMA  +267"'&'"'>2b$7!.J3EA%'7/)_F4-> 5-/4 ]Z:-39 ,K(PX@ M>@IMAY$" +4632#"&LD;JR@;H3LT3-RR%399 ,K(PX@ M>@IMAY$" +4632#"&%LC;JR?;H3LT3-RR=JU>:K(PX@dK =L >@dUL >Y@ /MCC+%&#"&67>76&'.'&67327676'&6732732676'&J9{ r }N6 =A wV P# ; =Bv JI))F`5 &S{; /  / <{90-R1 2  1 59 Z 2  1 #9J3w+Fw QT|3B~@ @+ddbb=M>Y@ %)$$(-%"+.#"&'>326'4&54632#"&546323276#"/7>32 +MH0%/7C7L)0T.:y]^-`C'8 ;?rv.j)Y0 $4*`DSoqe(/0L^9>?R $1 )-3-=IXK(PX@"K =K =M>@UQK >Y@ $,MCC +%&#"&67>76&'.'&67327676'&673274632#"&J9{ r }N6 =A wV P# ; =Bv JI))LC;JR?;H{; /  / <{90-R1 2  1 59 Z 2  1 #9J3w3LT3-RRT|3?y@ @)bbU=M>Y@ $&$$(-%"+.#"&'>326'4&54632#"&5463232764632#"& +MH0%/7C7L)0T.:y]^-`C'8 ;?rv{LD;JR@;H $4*`DSoqe(/0L^9>?R $1L3LT3-RR=_K PX@.  bX  UK =K >K(PX@/  b`  UK =K >@-  b`  UUK >YY@^\XVNLFEMCC +%&#"&67>76&'.'&67327676'&67327#67676&#"#"&7>32J9{ r }N6 =A wV P# ; =Bv JI))q26 ;(""" "gLVA{; /  / <{90-R1 2  1 59 Z 2  1 #9J3w#'J/##%  "&KNT|PM@J',<bbbU=M>MKGEA?75$',"+.#"&'>767676&#"#"&7>326'4&54632#"&546323276 +M6=4 1 (j=I=C7)7C7L)0T.:y]^-`C'8 ;?rv $4 P:F( # %%MO#38..EJoqe(/0L^9>?R $1\=R@B L@'  U UUK >Y@?>PNIGEC>R?RMCC +%&#"&67>76&'.'&67327676'&67327267#"'&#"'>32J9{ r }N6 =A wV P# ; =Bv JI))G$7!/J3EA$'7/)`@AG3{; /  / <{90-R1 2  1 59 Z 2  1 #9J3w,> 5-.4 ]Y9-T|3Ge@b8 B54ED?=;94G5G$$(-%" +.#"&'>326'4&54632#"&546323276267"'&'"'>2 +MH0%/7C7L)0T.:y]^-`C'8 ;?rv`$7!/J3EA%'7/)_G3 $4*`DSoqe(/0L^9>?R $1!-> 5-/4 ]Z:-q1B@: $K PX@4  Z M =M=M =M >@5  b M =M=M=M>YY@32@>982B3B&"$$'%% +54.# 32>?327327#"7"&7>3225>76#"76q+T?T "_C#D\7Bn-$./K+)VJ1FnDsͩ)xLs71 -}V=R\ HPtg5}?T)nL^\ao{eŇj3>[;'R\# V+%TFq1D@<  $K PX@4  ZM =M=M =M >@5  bM =M=M=M>YY@32>=862D3D&"$$'%% +54.# 32>?327327#"7"&7>322#".7>q+T?T "_C#D\7Bn-$./K+)VJ1FnDsͩ)xLs3#a-7?S VHPtg5}?T)nL^\ao{eŇj3>)T')X #7FF;[q1BQ9@J : $K PX@; M = M =M=M =M >K$PX@; M = M =M=M=M>@8 M =  M =M=M=M>YYY@32PNIG@>982B3B&"$$'%% +54.# 32>?327327#"7"&7>322'>76#"76&7>32'"'q+T?T "_C#D\7Bn-$./K+)VJ1FnDsͩ)xLs70 -}V=T ` 73#3 HPtg5}?T)nL^\ao{eŇj3>[;%T\# X)%TFR +%q1DR:@LN<  $K PX@;M =  M =M=M =M >K!PX@;M =  M =M=M=M>@8M =  M =M=M=M>YYY@32QOKI>=862D3D&"$$'%% +54.# 32>?327327#"7"&7>322#".7>&7>32'"'q+T?T "_C#D\7Bn-$./K+)VJ1FnDsͩ)xLs3#_-7?R  Y4"3  HPtg5}?T)nL^\ao{eŇj39)T%+X #7FE;ZR )% q0OK-PX@6B=<71<@6B=<71K PX@7ZM  =M = N  = M  >K!PX@8bM  =M = N  = M   >K-PX@<b =M =M = N  = M   >@Bb` =M =M = N  = M   >YYYY@"MKECA?;953,*%#  +2'>76#"76%2#"75654.# 32>?327327#"7"&7>3270 {=S `'! A+T?T "_C#D\7Bn-$./K+)VJ1FnDsͩ)xLsX;B# X)'TC- @)HPtg5}?T)nL^\ao{eŇj3q1DTTK&PX@RQ<  $<@RQ<  $K PX@7   ZM =M=N =M >K&PX@8   bM =M=N=M>@>   b  `M =M=N=M>YYY@FE32NLETFT>=862D3D&"$$'%%+54.# 32>?327327#"7"&7>322#".7>%2#".756q+T?T "_C#D\7Bn-$./K+)VJ1FnDsͩ)xLs3%] ,8?T U' HPtg5}?T)nL^\ao{eŇj39)T%)Z #7FE;Z - @)q1!4S5@$ : FA@;5" <;:K PX@F  bUUM = M  = M  = M   >K PX@F  bUUM = M  = M  = M  >@F  bUUM = M  = M  = M   >YY@&QOIGEC?=970.)' !!  +2'>76#"76%27#"&#"'63254.# 32>?327327#"7"&7>3270 9hB=R ` ;:+H+'=4+Nq;y+T?T "_C#D\7Bn-$./K+)VJ1FnDsͩ)xLsJZ<?J5% X)'TF\ VV\HPtg5}?T)nL^\ao{eŇj3q11AT,@$5 =L  $<<;4 :K PX@E  Z  U U M =M=M=M>K PX@E  Z  U U M =M=M =M >@F  b  U U M =M=M=M>YY@CB32NMHFBTCT@>;9862A3A&"$$'%%+54.# 32>?327327#"7"&7>3227#"&#"'6322#".7>q+T?T "_C#D\7Bn-$./K+)VJ1FnDsͩ)xLs=6-H+'=4+Lq;{w3#^-7?S VHPtg5}?T)nL^\ao{eŇj3}\ XX\'T%)X #6EF;[29J@ B5 &@.  b  U V =K >Y@;:43HFA@:J;J3949$334#334+%;2&#'76;27672;2&#'76;276'!"7!2'>76#"76;--90hD%1 <9P9\?R;oa92 ){V=R^  ''m`=>=:''8a#s" Z<%T\# X)%TF29L@ D5 &@.  b  U V =K >Y@;:43FE@>:L;L3949$334#334+%;2&#'76;27672;2&#'76;276'!"7!2#".7>;--90hD%1 <9P9\?R;oa3%_.8?T  S ''m`=>=:''8a#s" 'T%)X #6EF;[Z3:LZ@VD6 9 'K$PX@. I    UV =K >@/ U   UV =K >YY@<;54YWSQJHCB;L76#"76&7>32#"'+-;3;L#%/ <;P9` =P;ma73 7gA;RZ 53%5 ''67`=>=:''8a#s" [;=L3# V+%TFR+' #3;N\@XF6 9 'K!PX@. I    UV =K >@/ U   UV =K >YY@=<54[YUSHGB@&7>32'"'Z+-;3;L#%!- <;P:` =P9l`3%`/7?U R3#5  ''67`=>=:''8a#s"  z&V%)X #7FF;XO )% 2:JYK-PX@B 8 &<5;@B 8 &<5;YK PX@9  b   XV = M  =K >KPX@:  b   `V = M  =K >K$PX@8  b   `   UV =K >K-PX@< d  b   `  UV =K >@B d  b `  `  UV =K >YYYY@$LK<;43WVTRKYLYHFA@;J76#"76%2#"5436J+-<1 hD%/ m  O7^@P9o`73 }{=P b'   ''m`=>w''8a#s"  }[;B# X)'TD- @)2:M\FK&PX@Q ZYE5 8 &<@Q ZYE5 8 &KPX@3   bV = M  =K >K&PX@1   b  UV =K >@7 b  `  UV =K >YYY@"ON<;43WUN\O\GFA?;M%2#"&756J+-<1 hD%/ m  O7^@P9o`3%[/8?T  U'    ''m`=>w''8a#s"  )T'+V #7FF;[- @)/3;L\*@(PX 6 D 9 'KPX@A  b U UV = M =K >@?  b U U UV =K >YY@&NM=<54[YVTSQM\N\JHCB76#"76%27#"&#"'632f+-;3;L#%/ <;P9` =P;ma74 +{V=S^ ;6-F+'?0+Hq;{''67`=>=:''8a#s"  Z;'R\" W+%TE] XX ]33:K^v@$> F 6V9 'KPX@D  bU  V M = = M =K >KPX@B  b UU  V = M =K >@@  b U  UU  V =K >YYY@'ML<;54XWRPL^M^JHDBA?;K322#".7>j\;3;M"%/ <;O9` ?R;ma'=6+F-%;3+'U>;{u3&\/7?T  T3''67`=>=:''8a#s" $]ZZ XT])T%+V #5FE;Zq3EK1PX@= <@= K PX@:  bbU M  =M=M >K-PX@:  bbU M  =M=M>K1PX@?  bbIU M  =M=M>@@  bbUU M  =M=M>YYYY@54CA<;4E5E!""%$+#" +%327#".767.7>32#"&'.#"7632#"&#"2'>76#"76/VXud-+H7s 8 1%9u'229 ;T Vw7/ 9gA=R ZTu^'{4O[)=ZITZ9'%#! !Z\%) dL[;;L5# X)'TDq3FK1PX@> <@> K PX@9  ZbU M  =M=M >K-PX@:  bbU M  =M=M>K1PX@?  bbIU M  =M=M>@@  bbUU M  =M=M>YYYY@54@?:84F5F!""%$+#" +%327#".767.7>32#"&'.#"7632#"&#"2#".7>/VXud-+H7s 8 1%9u'229 ;T Vw1%Z+7?S VTu^'{4O[)=ZITZ9'%#! !Z\%) dL)T')X #7FF;[3DRK1PX@L N< <@L N< K PX@@bU M  =  M  =M=M >K!PX@@bU M  =  M  =M=M>K-PX@=bU M =  M =M=M>K1PX@BbIU M =  M =M=M>@CbUU M =  M =M=M>YYYYY@54QOKIB@;:4D5D!""%$+#"+%327#".767.7>32#"&'.#"7632#"&#"2'>76#"76&7>32'"'/VXud-+H7s 8 1%9u'229 ;T Vw72 -}V=T \13%1 Tu^'{4O[)=ZITZ9'%#! !Z\%) dEZ;'R\# X)%TER )% 3FTK1PX@N P> <@N P> K PX@@bU M  =  M  =M=M >K!PX@@bU M  =  M  =M=M>K-PX@=bU M =  M =M=M>K1PX@BbIU M =  M =M=M>@CbUU M =  M =M=M>YYYYY@54SQMK@?:84F5F!""%$+#"+%327#".767.7>32#"&'.#"7632#"&#"2#".7>&7>32'"'/VXud-+H7s 8 1%9u'229 ;T Vw3#^+7?U V3#3!Tu^'{4O[)=ZITZ9'%#! !Z\%) dC)T%)X #5FE;ZR +% 3DT7K1PX@H < P <@H < P K PX@C  b   XbU M  =M=M >K&PX@D  b   `bU M  =M=M>K-PX@H  b   `bU = M =M=M>K1PX@M  b   `bIU = M =M=M>@N  b   `bUU = M =M=M>YYYYY@FE54RQNLETFTB@;:4D5D!""%$+#"+%327#".767.7>32#"&'.#"7632#"&#"25>76#"76%2#"&7436/VXud-+H7s 8 1%9u'229 ;T Vw72 -}V;T \'   Tu^'{4O[)=ZITZ9'%#! !Z\%) dEZ;'R\# X)%TE -  @'3FTK1PX@R> <@R> K PX@<   ZbU M  =M=M >K&PX@=   bbU M  =M=M>K-PX@C b  `bU M  =M=M>K1PX@H b  `bIU M  =M=M>@I b  `bUU M  =M=M>YYYYY@HG54PNGTHT@?:84F5F!""%$+#"+%327#".767.7>32#"&'.#"7632#"&#"2#".7>%2#"=6/VXud-+H7s 8 1%9u'229 ;T Vw3#`-7?U W'  Tu^'{4O[)=ZITZ9'%#! !Z\%) dE'V%)X #7FE9Z + @';M@E  '@H Z  bbZ U  UU=L >Y@=<KIDC76#"767NJHT11.#)/%1e!%yf5t>h ';;  73 7gA;R^ +NAK)1>^`@/+' -Z1++Z<=L3# V+%TFo=P@H  )(K(PX@I Z  bbX UU L  ==L >@G Z  bbX U  UU=L >YY@?>JIDB>P?P=;4243$&4"4+6&#!"!26?6;2#"=4&#!3!267!'76;276&+"?7%2#".7>o9NJL T32 - ) /%1g!%x^33s;j )9> 3%]/9?U T+NAK+/>AJ{@/+'} -Z1++'T%+V #8CH;[<M[@U 9 WD  (K$PX@I ZbX  I    UU L ==L >K(PX@J ZbX  U   UU L ==L >@H ZbX  U U   UU=L >YYY@>=ZXTRJHCB=M>M<:4233$&4"4+6&#!"!26?6;2#"=4&#!3!2!'76;276&+"577%2'>76#"7>&7>32'"'7NJJT32 -) / %1d!$yk3u=j'9> `74 ~z=Pb5 4#3 +NAK+/>AuP@/+' -Z1++[;B# X)'T)P )% =P^@ZH  )(K!PX@I ZbX  I    UU L ==L >K(PX@J ZbX  U   UU L ==L >@H ZbX  U U   UU=L >YYY@?>][WUJIDB>P?P=;4243$&4"4+6&#!"!26?6;2#"=4&#!3!267!'76;276&+"?7%2#".7>&7>32'"'7NJL T32 - ) 0%1g!%x^33s =j )9= y3# b -7 ?W R3#5 +NAK+/>AJ{@/+'} -Z1++(T%)Z #7FE;ZQ +% =N]@R [ZF  )(KPX@N Z  bbXU M  = L ==L >K&PX@L Z  bbX   UU L ==L >K(PX@P d Z  bbX  UU L ==L >@N d Z  bbX  U UU=L >YYYY@#PO?>XVO]P]LJED>N?N=;4243$&4"4+6&#!"!26?6;2#"'54&#!3!267!'76;276&+"?7%2'>76#"76%2#"&7567NJL V31 - ) /%1g!%y^33r >j )9=  }74 ,zV=S^'  +NAK+/>AJ{@/+'} -Z1++Z;'R\# V+%TE +  @'=P_K(PX@T : ]\H  )(<@T : ]\H )(KPX@N Z  bbXU M  = L ==L >K(PX@L Z  bbX   UU L ==L >@P Z  b`bX   U UU=L >YYY@#RQ?>ZXQ_R_JIDB>P?P=;4243$&4"4+6&#!"!26?6;2#"574&#!3!267!'76;276&+"577%2#".7>%2#"&756:MHHS31-)  /%1d%y^36u;j )9>  {3%a.8?T  S'  +NAK)1>AJ{@/+'} -Z1++)V%)Z #5FF;[-  @')!3Z@W+< 9bbcM =M>#"1/*)"3#3! &*! +3632 '&6#"'6#"767>32'>76#"763-E{??y3-)/^5; 71 :hB=S Zy%F^-;=#\ 9    X;;L5# X)'TC)!4@,< 9K PX@+ZbcM =M>@,bbcM =M>Y@#".-(&"4#4! &*! +3632 '&6#"'6#"767>32#".7>3-E{??y3-)/^5; o3#a,8?R  Wy%F^-;=#\ 9    )T')X #7FF;[)!2@@<)< 9K!PX@2bcM = M =M >@/bcM = M=M >Y@#"?=970.('"2#2! &*! +3632 '&6#"'6#"767>32'>76&#"76&7>32'"'3-E{??y3-)/^5;  7/ x=R^33#5 y%F^-;=#\ 9   X;B# Z)TCP )%  )!3A@1=,< 9K!PX@2bcM = M =M >@/bcM = M=M >Y@#"@>:8.-(&"3#3! &*! +3632 '&6#"'6#"767>32#".67>&7>32'"'3-E{??y3-)/^5; !3#^ -7RZ T3#5 y%F^-;=#\ 9   )T%)X "Z\;ZR )% )!2@@>=*< 9K PX@. ZbcM  =M >K&PX@/ bbcM  =M >@3 bbc =M =M >YY@43#"<:3@4@0.)("2#2! &*! +3632 '&6#"'6#"767>32'>76#"76%2#"7563-E{??y3-)/^5; 90 -}V=T ` '  !y%F^-;=#\ 9   Z;%T\# X)%TE + @')!4C@, < 9K PX@5  b XbcM  =M >K$PX@6  b `bcM  =M >@<  b  `  `bcM  =M >YY@ 65#"A@>=5C6C.-(&"4#4! &*!+3632 '&6#"'6#"767>32#".7>%2"74363-E{??y3-)/^5; %3#`+7?U V' y%F^-;=#\ 9   'V%+V #7FE9Z+ @&)1!3C@7 ? +<> ;6 : 9bbc  U  UM  =M >54#"B@=;:84C5C1/*)"3#3! &*!+3632 '&6#"'6#"767>32'>76#"76%27#"&#"'6323-E{??y3-)/^5; 71 :hB=U\ =6+F+%?1+Jp=yy%F^-;=#\ 9   X>;L5% X)%TF\ XX \)1!2E@&%-= <,;$: 9K PX@<  ZbcU  U M =M >@=  bbcU  U M =M >Y@"43#"?>973E4E1/+)(&"2#2! &*!+3632 '&6#"'6#"767>327#"&#"'>322#".7>3-E{??y3-)/^5; =6-H+%=3+'X=;{v1%Z-7?S  Wy%F^-;=#\ 9   L\ XXXT\'T%)X #6EF;[1Oa@7"YJ@4bU  US K  >Y@QP_]XWPaQaOLIFC@<9633334333+%!;2&#'76;276&+"?3%+"!6&+"?3%+";2&#'76;22'>76#"76dbb +;7Q5q +=6P  7qTR *>5P8q *>5P8oh73 7gA;R^ /+''Z/+''ZO/+''Z/+''Z<=L3# V+%TF1Ob@7"ZJK(PX@6bUS K  = K  >@4bU  US K  >YY@QP\[VTPbQbOLIFC@<9633334333+%!;2&#'76;276&+"?3%+"!6&+"?3%+";2&#'76;22#".7>dbb +;7Q5q +=6P  7qTR *>5P8q *>5P8oh3%_/8?T  U/+''Z/+''ZO/+''Z/+'''T%)X #6EF;[Oao6@7"kYJSM= K  =M= K  >K!PX@6IUS K  = K  >K(PX@7UUS K  = K  >@5U  UUS K  >YYY@#QPnlhf_]XWPaQaOLIFC@<9633334333+%!;2&#'76;276&+"?3%+"!6&+"?3%+";2&#'76;22'>76#"76&7>32'"'Hdbd -;7Q5q -;6P  7qTR *>5P8q *>5P8o 73 7gA=R^ 73%5 /+''Z/+''ZO/+''Z/+''Z;=L3# V+%TEQ +% qObq=@j7"1ZJSM= K  =M= K  >K!PX@6IUS K  = K  >K(PX@7UUS K  = K  >@5U  UUS K  >YYY@#QPpnig\[VTPbQbOLIFC@<9633334333+%!;2&#'76;276&+"?3%+"!6&+"5?3%+";2&#'76;22#".7>&7>32'"'1edd *>5P8o *>5 P7qTT -;8P  5q +=5Q5q3%^/7?U  T3#3/+''Z/+''ZO/+''Z1)''&V%+V #7FE9ZO )%wO`o@7"mlXJKPX@;bSM= K  = L  >K&PX@9bUS K  = L  >K(PX@=dbUS K  = L  >@;dbU  US L  >YYYY@'baQPjhaobo^\WVP`Q`OLIFC@<9633334333+%!;2&#'76;276&+"?3%+"!6&+"?3%+";2&#'76;22'>76#"76%2#"&7567edd *>5P8o *>5P8qTT -;8P 5s +=6P 5o 74 +{V=S^'  /+''Z/+''ZO/+''Z1)''Z;'R\# V+%TE - @'dObp@7"nmZJ KPX@;bSM= K  = L  >K&PX@9bUS K  = L  >K(PX@?b`US K  = L  >@=b`U  US L  >YYYY@'dcQPljcpdp\[VTPbQbOLIFC@<9633334333+%!;2&#'76;276&+"?3%+"!6&+"?3%+";2&#'76;22#".7>%2#"756%dce -;8P 5q +=5Q5qTR +>5P8q +=5 P7o3%^/7?U T'! /+''Z/+''ZO/+''Z/+'')T'+V #7FF;[- @)BOaq@em7"YJKPX@IbUUSM = K  = K  >K(PX@GbUUUS K  = K  >@EbUU  UUS K  >YYY@+cbQPpnkihfbqcq_]XWPaQaOLIFC@<9633334333+%!;2&#'76;276&+"?3%+"!6&+"?3%+";2&#'76;22'>76#"76%27#"&#"'632dee +>5P7o +;7 P7qTT -;8P 5q +=5P5q71 7hB=P ^;5+B-%?0+Hq;}/+''Z/+''ZO/+''Z1)''X;=L3" Y)%TC]ZZ ]VO`s@S[7"kJKPX@KbUSM=M = K  = K  >KPX@IbUUSM = K  = K  >K(PX@GbUUUS K  = K  >@EbUU  UUS K  >YYYY@+baQPmlgeasbs_]YWVTP`Q`OLIFC@<9633334333+%!;2&#'76;276&+"?3%+"!6&+"?3%+";2&#'76;227#"&#"'>322#".7>dbd +=5 P5q -<7P5qRT -;5 P5q -<7P6q2=6+B-%?/+%V=;{r1'[18?T  U1)''Z/+''ZO/+''Z/+''/] XX XR]'V%)X #7FE9Z*@"K PX@+ZbM==M >K1PX@,bbM==M>@*bbU=M>YYY@(&! **%$" +327#"&76&#"767>32'>76"761Pb1K PX@+ZbM==M >K1PX@,bbM==M>@*bbU=M>YYY@&% ,,%$" +327#"&76&#"767>32#".7>1Pb1K PX@2bM =M = =M >K!PX@2bM =M = =M>@/bM =M= =M>YYY@751/(&! **%$" +327#"&76&#"767>325>76#"76&7>32'"'1Pb1K PX@2bM =M = =M >K!PX@2bM =M = =M>@/bM =M= =M>YYY@9731&% ,,%$" +327#"&76&#"767>32#".7>&7>32'"'1Pb1K PX@6  b`bM  = =M >K&PX@6  b`bM  = =M>@:  b`b =M = =M>YYY@,+8742+:,:(&! **%$" +327#"&76&#"767>32'>76#"76%2#"&74361Pb1K PX@.ZbM  = =M >K&PX@/bbM  = =M>@5b`bM  = =M>YYY@.-64-:.:&% ,,%$" +327#"&76&#"767>32#".7>%2#"=61Pb1K PX@=bb  U UM  = =M >@=bb  U UM  = =M>YY@!-,:85320,;-;)'"!++%$" +327#"&76&#"767>32'>76#"76%27#"&#"'6321Pb1K PX@<  ZbU U M  = =M >@=  bbU U M  = =M>YY@!+*650.*<+<(&#! ))%$" +327#"&76&#"767>327#"&#"'6322#".7>1Pb1@&b UUK >Y@'&53.-&7'7334333 +;2&#'76;276&+"?3%+"%2'>76#"76 -;7Q5q +=6P  7q74 8hA=S^ /)''X/+''Z<=K4# V+%TF%8@ 0 K(PX@(b UK =K >@&b UUK >YY@'&21,*&8'8334333 +;2&#'76;276&+"?3%+"%2#".7> -;8P 5q +=6P 5q/3%Z/7?U  T/)''X/+'''T%)X #6EF;[H%5C@ ?- K!PX@(I  UK =K >K(PX@) U  UK =K >@' UU  UK >YYY@'&B@<:31,+&5'5334333 +;2&#'76;276&+"?3%+"%2'>76#"76&7>32'"'u -;8P5q +=6P  5q74 |z;S\54$5 /)''X/+''X;D# X)'TCO)% %8F@@B0 < ;KPX@0M =K = M =K >K$PX@(I  UK =K >K(PX@) U  UK =K >@' UU  UK >YYY@'&EC?=21,*&8'8334333 +;2&#'76;276&+"?3%+"%2#".7>&7>32'"'; -=5 P7o +>5P8q3%[.8?T S3#3 /)''X/+'')T')X #7FF;[R +' #%7E@ CB/ K(PX@+ b UK =L >@-   d b UUL >YY@98'&A?8E9E53.-&7'7334333 +;2&#'76;276&+"?3%+"%2'>76#"76%2#"756P -;8P 5q +=5Q5q71 7hB=R^ '  !/)''X/+''[;=L3# X)%TF -@'!%8G@< ED0 KPX@- bM =K =L >K(PX@+ b UK =L >@/  b  ` UUL >YYY@:9'&B@9G:G21,*&8'8334333 +;2&#'76;276&+"?3%+"%2#".7>%2#"&756N -;8P 5q -;6P 5q3%]/7?U R'   /)''X/+'''T%)X %7DH;[- @'%7G@; C  / K(PX@9b U  U UK =K >@7b U  UU UK >YY@98'&FDA?><8G9G53.-&7'7334333+;2&#'76;276&+"?3%+"2'>76#"76%27#"&#"'632 +=5P5q -;8P 5qX92 7hB=R ^ =6+F+'=4+Jq;{/)''X/+'' Z;=L3# V+%TE\ XX \%5HE@ ) 1 @ <0;( :K PX@: Z  U UM  =K =K >KPX@; b  U UM  =K =K >K(PX@9 b  U U UK =K >@7 b  U U U UK >YYY@76'&BA<:6H7H42/-,*&5'5334333+;2&#'76;276&+"5?3%+"%27#"&#"'6322#".7>H +=5Q5q -;8P  5q;8+F+'?/+Hp={t3%_ /8?T  U/)''X/+''{\[[ \)T%+X #5FE;Z +ߵ#K PX@(ZM=M=M >K1PX@)bM=M=M>@'bUM=M>YYY@)'"!++  +"32676&632#"2'>76"76s m^Z'/f-m>'71 .|V=U\FV^VPZ;'R\# Y)%TF -ߵ%K PX@(ZM=M=M >K1PX@)bM=M=M>@'bUM=M>YYY@'&!--  +"32676&632#"2#".7>s m^Z'/f-m>'3%[ ,8?R  WFV^VP)T$)X #6EF;Z +:/#K PX@/M =M = M=M >K$PX@/M =M = M=M>K(PX@,M =M= M=M>@* UM= M=M>YYYY@9720)'"!++  +"32676&632#"2'>76#"76&7>32#"'s m^Z'/f-m>'71 -}V=T\53%5 FV^VPZ<'Q]# Y(%TFR (%  -</%K PX@/M =M = M=M >K$PX@/M =M = M=M>K(PX@,M =M= M=M>@* UM= M=M>YYYY@;942'&!--  +"32676&632#"2#".7>&7>32#"'s m^Z'/f-m>'1%] ,8?R  W3%5 FV^VP)T%)X #6EF;[R (%   ,:3@ 087$K PX@,bM  = M=M >K!PX@,bM  = M=M>K(PX@*b  U M=M>@.b U = M=M>YYYY@ .-64-:.:*(#",,  +"32676&632#"2'>76#"76%2#"756s m^Z'/f-m>'71 9iA=T` '  FV^VPZ<;L3# X)%TF - ?'# -;698%K PX@+ZM  = M=M >K&PX@,bM  = M=M>K(PX@2b`M  = M=M>@0b`  U M=M>YYYY@ /.75.;/;'&!--  +"32676&632#"2#".7>%2#"756s m^Z'/f-m>'1%a,8?R U' !FV^VP)V%)X #8EF;[- ?' +#K PX@'bUM =M >@'bUM =M>YY@ )'"!++  $" +! ! "32%2'>76#"76;Q9;3Nx9Ӵ'5;73 7gA;R^ zm'/5HB 1RZ<=L3# V+%TF3 ,$K PX@&ZUM =M >@'bUM =M>YY@ &% ,,  $" +! ! "32%2#".7>3;R;;3Ny7Ѵ'6;Z3%\ /7?U Tzm'/5H@1R'T%)X #6EF;[` )8+@ 1!K PX@/M = M =M =M >KPX@/M = M =M =M>K!PX@'I U M =M>@( UU M =M>YYYY@ 750.'% ))  $" +! ! "32%2'>76#"76&7>32'"'`;R9;3Ny7Ѵ'6;'74 }{=P \73#3zk)/5H@1RX;D# X+%TCO )%q *9+@ 2"K PX@/M = M =M =M >KPX@/M = M =M =M>K!PX@'I U M =M>@( UU M =M>YYYY@ 861/$#**  $" +! ! "32%2#".7>&7>32'"'q=U9;3;7Ѵ)3;1%_/7?T  U3#3zk)/5B 1R&V%)X #7FE9ZO )%= )8l@ -65!K PX@+ZM  = M =M >KPX@,bM  = M =M>K$PX@*b  U M =M>K-PX@. db U M =M>@4 db` U M =M>YYYYY@+* 31*8+8'% ))  $" +! ! "32%2'>76#"76%2#"&756=;R9;3Nx9Ҵ(5;\74 |z;P \'  zm'/5H@1R[;B# X)'TD- @) ,:+7$K PX@+ZM  = M =M >KPX@,bM  = M =M>K&PX@*b  U M =M>@0b`  U M =M>YYYY@.- 64-:.:&% ,,  $" +! ! "32%2#".7>%2#"?6=U9;3N{7Ѵ)5;r3%_/7?U T' zk)/5H@1R'V%+V #7DH;[- @'$5@-  K PX@+ZbM==N >K1PX@,bbM==N>@*bbU=N>YYY@&%31,+%5&5('$" +#"&7>76''>3232676'&7>322'>76"765Ӌ%X 5a)7%# iY}#3wR 8'71 -}V=T`զ\);'93/HiuC-Z;'R\# Y)%TF$7@/  K PX@+ZbM==N >K1PX@,bbM==N>@*bbU=N>YYY@&%10+)%7&7('$" +#"&7>76''>3232676'&7>322#".7>5Ӌ%X 5a)7%# iY}#3wR 8'3#_ -7?U Tզ\);'93/HiuC-)T$)X #6EF;Z$5DD@=-  K PX@2bM = M ==N >K$PX@2bM = M ==N>K(PX@/bM = M==N>@-b U M==N>YYYY@&%CA<:31,+%5&5('$" +#"&7>76''>3232676'&7>322'>76#"76&7>32#"'5Ӌ%X 5a)7%# iY}#3wR 8'7/ -}V=T` 93#3 զ\);'93/HiuC-Z<%S]# Y(%TFR (% $7FD@?/  K PX@2bM = M ==N >K$PX@2bM = M ==N>K(PX@/bM = M==N>@-b U M==N>YYYY@&%EC><10+)%7&7('$" +#"&7>76''>3232676'&7>322#".7>&7>32#"'5Ӌ%X 5a)7%# iY}#3wR 8'3#_ -7?U V3#3 զ\);'93/HiuC-)T%)X #6EF;[R (% $5C=@A@-  K PX@. ZbM  ==N >K!PX@/ bbM  ==N>K(PX@- bb  U=N>@1 bb U ==N>YYYY@76&%?=6C7C31,+%5&5('$" +#"&7>76''>3232676'&7>322'>76#"76%2#"7565Ӌ%X 5a)7%# iY}#3wR 8'7/ -}V=T` '  !զ\);'93/HiuC-Z<%T\# X)%TF - ?'+$7FG@DC/  K PX@. ZbM  ==N >K&PX@/ bbM  ==N>K(PX@5  b  `bM  ==N>@3  b  `b  U=N>YYYY@98&%A?8F9F10+)%7&7('$" +#"&7>76''>3232676'&7>322#".7>%2#"&7565Ӌ%X 5a)7%# iY}#3wR 8'3#a -7?U T' զ\);'93/HiuC-)V%)X #8EF9]+ ?'w3$6F @: B .  K PX@=bb  U   UM  ==N >@=bb  U   UM  ==N>YY@87&%EC@>=;7F8F42-,%6&6('$"+#"&7>76''>3232676'&7>322'>76#"76%27#"&#"'6325Ӌ%X 5a)7%# iY}#3wR 8'72 9hB=U \?6+H+%?1+Jr;yզ\);'93/HiuC-Z<;L5% X)%TF\ XX\V+$4G @(0?  K PX@<  ZbU  U M  ==N >@=  bbU  U M  ==N>YY@65&%A@;95G6G31.,+)%4&4('$"+#"&7>76''>3232676'&7>3227#"&#"'6322#".7>5Ӌ%X 5a)7%# iY}#3wR 8'˵=6+F+%=3+Nn=yv1%a-7?R Uզ\);'93/HiuC-HZ ZZ Z)V%)X %7FF;[BU@(M  =K(PX@,  b   UK = K >@*  b   U U K >YY@DCONIGCUDUB?363393#5+%&'&'#"?307+"676&+"?37+";2&#'76;22#".7>3R + <1b!> +-;%^X -;8P5q!3%\/7?T  UY; )' /-  ))'3;/+'''T%)X #6EF;[BUc@] ( _M  =K!PX@+ I    UK =K >K(PX@, U   UK =K >@* U U   UK >YYY@DCb`\ZONIGCUDU336339345+%&'&'#"&?37+"676&+"?37+";2&#'76;22#".7>&7>32'"'yT)  93c!; --<$`X *>5 P7o3%^/7?U  T3#3 Y; )' /-  ))'3;/+''&V%+V #7FF9ZO)% BUdpK&PX@(baM  =<@(baM  =KPX@0   b M  =K =K >K&PX@.   b   UK =K >K(PX@4 b  `   UK =K >@2 b  `   U UK >YYYY@WVDC_]VdWdONIGCUDU336339345+%&'&'#"&?37+"676&+"?37+";2&#'76;22#".7>%2#"&756yT)  93c!; --<$`X *>5 P7o3# b/7?U  T'  Y; )' /-  ))'3;/+'')T')X #7FF;[- @)yBSf@"F N( ^=KPX@AbU M = M =K = K >KPX@?b UU M =K = K >K(PX@=b U UUK = K >@;b U UUU K >YYYY@#UTDC`_ZXTfUfRPLJIGCSDSB?363393#5+%&'&'#"?307+"676&+"?37+";2&#'76;227#"&#"'>322#".7>R + ;1b!> +-;%^V +;7P5qK=6+F-$?0+%V=;{r1'[18?T  UY; )' /-  ))'3;/+''/] XX XR]'V%)X #7FE9Z/7H@@'&6 K PX@'ZUM =M >@(bUM =M>YY@98FD?>8H9H.$$-"" +#"'#".767327&7632326'7%6'"62'>76#"76+}JpZ!T''ZynRTZS1'"=-> # :71 .|V=U\ 'Xn#b))݁kwk^''+@CY^;F;uX>'R\# X)%TF/7J@B'&6 K PX@'ZUM =M >K1PX@(bUM =M>@&b UUM>YYY@98DC><8J9J.$$-"" +#"'#".767327&7632326'7%6'"62#".7>+}JpZ!T''ZynRTZS1'"=-> # :1%_-8?R  W'Xn#b))݁kwk^''+@CY^;F;u)T$)X #6EF;Z/7HV8@PR@'&6 K PX@.UM = M =M >K$PX@.UM = M =M>K(PX@+UM =  M =M>@) UU  M =M>YYYY@98USOMFD?>8H9H.$$-"" +#"'#".767327&7632326'7%6'"62'>76#"76&7>32#"'+}JpZ!T''ZynRTZS1'"=-> # :7/ .|V=S ` 74#3  'Xn#b))݁kwk^''+@CY^;F;uZ<%S]# Y(%TFR (% /7JX8@RTB'&6 K PX@.UM = M =M >K$PX@.UM = M =M>K(PX@+UM =  M =M>@) UU  M =M>YYYY@98WUQODC><8J9J.$$-"" +#"'#".767327&7632326'7%6'"62#".7>&7>32#"'+}JpZ!T''ZynRTZS1'"=-> # :3#_-8?R  W4#3  'Xn#b))݁kwk^''+@CY^;F;u)T%)X #6EF;[R (% /7HW0@UT@'&6 K PX@* ZUM =N >K!PX@+ bUM =N>K(PX@) b UUN>@- b UU  =N>YYYY@JI98RPIWJWFD?>8H9H.$$-"" +#"'#".767327&7632326'7%6'"62'>76#"76%2#"&756+}JpZ!T''ZynRTZS1'"=-> # :71 .|V=U\ ' 'Xn#b))݁kwk^''+@CY^;F;uZ<'R\# X)%TF - ?'/7IXaK$PX@GVUB'&6 <@GVUB'& 6 K PX@* ZUM =N >K$PX@+ bUM =N>K(PX@1  b  `UM =N>@/  b  ` UUN>YYYY@KJ98SQJXKXDC><8I9I.$$-"" +#"'#".767327&7632326'7%6'"62#".47>%2#"&756+}JpZ!T''ZynRTZS1'"=-> # :3%] -8RZ W' 'Xn#b))݁kwk^''+@CY^;F;u)V%)X #X_9]- ?''/7HX@%L T @'&6 K PX@9b U  UUM  =M >@9b U  UUM  =M>YY@JI98WURPOMIXJXFD?>8H9H.$$-""+#"'#".767327&7632326'7%6'"625>76#"76%27#"&#"'632+}JpZ!T''ZynRTZS1'"=-> # :7/ -}V;T ` ;:+H-}%?4+Nq;y'Xn#b))݁kwk^''+@CY^;F;uZ;%T\# X)%TE\ XX \+/7GZ@%; CR '& 6 K PX@8  Z  U  UU M =M >@9  b  U  UU M =M>YY@IH98TSNLHZIZFDA?><8G9G.$$-""+#"'#".767327&7632326'7%6'"627#"&#"'6322#".7>+}JpZ!T''ZynRTZS1'"=-> # :A=8+H+}'=4+Lq;yw3#`+7?U V'Xn#b))݁kwk^''+@CY^;F;u%\ZZ \)V%)X %7FF9]\->@6  "KPX@/  bX   UM =N >@0  b`   UM =N >YY@/.<:54.>/>&"''!! +%32673!"7>76#"#!73;7&76! 2'>76#"76m!TH5>V!E!++R}#%P!57T$+%/#`92 ){V=S^ w)R!RѨb!N-w;aZ<%T\# X)%TF-@@8  "KPX@/  bX   UM =N >@0  b`   UM =N >YY@/.:942.@/@&"''!! +%32673!"7>76#"#!73;7&76! 2#".7>#RI5=V!F!+)R}#%P!39T%+#0#B1'[ 18?T  Uw)R!PѨb!N-w;a'T%)X #6EF;[->L@H6  "KPX@9 b M =M =  M =N >K!PX@1 b  I    UM =N >@2 b   U   UM =N >YYY@/.KIEC<:54.>/>&"''!!+%32673!"7>76#"#!73;7&76! 2'>76#"76&7>32'"'!TG5=V!F!+)R}#%P!39T%+#/#92 ){V=R ^ 73%5 w)R!PѨb!N-w;aZ;%T\# X)%TEQ +% m-@N!@HJ8  "KPX@9 b M =M =  M =N >K!PX@1 b  I    UM =N >@2 b   U   UM =N >YYY@/.MKGE:942.@/@&"''!!+%32673!"7>76#"#!73;7&76! 2#".7>&7>32'"'{!RG5=X#F +)!R}##N!57T%+#/%3%a/8?T U4#3 w)R!RѨb!N-w;a&V%)X #7FE9ZO )% -?N@7  "K(PX@:   b   ``   UM =N >@>  d   b   ``  UM =N >YY@A@/.LKIG@NAN=;65.?/?&"''!!+%32673!"7>76#"#!73;7&76! 2'>76#"76%2#"7436+#RJ5=V% F +)!R#%P57V%+#/#V74 8hB=S^ '  w)R!PѨb!N-w;a[;=L3# V+%TF - @'#-@N8K&PX@K8  "<@K8  "KPX@4   bX M =M =N >K&PX@3   b`   UM =N >@9   b  ``   UM =N >YYY@BA/.JHANBN:942.@/@&"''!!+%32673!"7>76#"#!73;7&76! 2#".7>%2#"5761!RJ6@X% C!+)!R"%P58V%+#/#N3%^/7?U  T' w)R!RѨb!N-w;a'V%+V #7DH;[- @'-?O @C K 7  "KPX@C  b `  U  U M =M =N >@A  b `  U  U  UM =N >YY@ A@/.NLIGFD@OAO=;65.?/?&"''!!+%32673!"7>76#"#!73;7&76! 2'>76#"76%27#"&#"'632!RJ5@Y% C!+)!R#%P57V$+#/%74 7iA=P^?4-F+'?/+Hp;{w)R!RѨb!N-w;aX;=L3" Y)%TC]ZZ ]?-=Pl@1 9 H "<8 ;0 :K PX@C  ZX  U M = M =M =N >KPX@D  bX  U M = M =M =N >KPX@C  b `  U  U M =M =N >@A  b `  U  U  UM =N >YYY@ ?>/.JIDB>P?P<:7542.=/=&"''!!+%32673!"7>76#"#!73;7&76! 27#"&#"'6322#".7>N!RG3=X#F!+)!R}##N!57T%+#0%J;6+B-%?2+Jq;}w3%^-7?U Tw)R!RѨb!N-w;aA] XX ]'V%)X #7FE9Zqm1D@:9$K PX@/b =M=M =M >KPX@/b =M=M=M>@,ddM=M=M>YYY@ ($&"$$'%% +54.# 32>?327327#"7"&7>32672#"/&76q+T?T "_C#D\7Bn-$./K+)VJ1FnDsͩ)xLs?  HPtg5}?T)nL^\ao{eŇj3   &qu1A@$K PX@0b =M=N =M >KPX@0b =M=N=M>@- ddM=N=M>YYY@32;92A3A&"$$'%% +54.# 32>?327327#"7"&7>322#"&7676q+T?T "_C#D\7Bn-$./K+)VJ1FnDsͩ)xLs-!+  HPtg5}?T)nL^\ao{eŇj3/  %#qm3FK1PX@<; <@<; K PX@4  bbV =M=M >KPX@4  bbV =M=M>K-PX@1 d  dbVM=M>K1PX@6 d  dbIUM=M>@7 d  dbUVM=M>YYYYY@A?75!""%$+#" +%327#".767.7>32#"&'.#"7632#"&#"672#"/&76/VXud-+H7s 8 1%9u'229 ;T Vw) ? Tu^'{4O[)=ZITZ9'%#! !Z\%) d   &qq3CK1PX@? <@? K PX@2  bUM= M  =M >KPX@2  bUM= M  =M>K-PX@0  b   UUM=M>K1PX@5  b   UIUM=M>@6  b   UUUM=M>YYYYY@54=;4C5C!""%$+#" +%327#".767.7>32#"&'.#"7632#"&#"2#"&7476/VXud-+H7s 8 1%9u'229 ;T Vw+  Tu^'{4O[)=ZITZ9'%#! !Z\%) d'/  ##)w!4~@)< 9K!PX@&bbc=M>@#ddbcM>Y@/-%#! &*!+3632 '&6#"'6#"767>3632#"/&763-E{??y3-)/^5;  @  y%F^-;=#\ 9      %)s!1@-< 9KPX@'bbc=M>@$ddbcM>Y@#"+)"1#1! &*! +3632 '&6#"'6#"767>32#"&74763-E{??y3-)/^5; !+  y%F^-;=#\ 9   /  %#dq+@! K PX@&bb==M >KPX@&bb==M>@#ddb=M>YYY@&$%$" +327#"&76&#"767>3672#"/&761Pb1K PX@'bb==M >K!PX@'bb==M>@$ddb=M>YYY@"!((%$" +327#"&76&#"767>32"&74761Pb1K PX@#b =M=M >KPX@#b =M=M>@ ddM=M>YYY@(&   +"32676&632#"632#"/&56s! n_Z&/d-m>% @  FޕV^ VPb   &u )K PX@$b=M=M>K PX@$b=M=M >KPX@$b=M=M>@!ddM=M>YYY@$"))   +"32676&632#"2#"7676s! n_Z&/d-m>%+ FޕV^ VP}/ %#b$6@,  K PX@&bb ==N >K!PX@&bb ==N>@#ddb=N>YYY@ '"('$"+#"&7>76''>3232676'&7>32632#"/&765Ӌ%X 5a)7%# iY}#3wR 8'5=  զ\);'93/HiuC-   )u$3@  K PX@'bb==N >KPX@'bb==N>@$ddb=N>YYY@&%-,%3&3('$"+#"&7>76''>3232676'&7>322"&76765Ӌ%X 5a)7%# iY}#3wR 8'+  զ\);'93/HiuC-/  %#\/7J@?'&6 K PX@"bU =N >K*PX@"bU =N>@ddUN>YYY@ (&.$$-""+#"'#".767327&7632326'7%6'"6632#"/&76+}JpZ!T''ZynRTZS1'"=-> # :  =  'Xn#b))݁kwk^''+@CY^;F;u    )y/7G@'&6 K PX@#bU=N >K$PX@#bU=N>@ ddUN>YYY@98A?8G9G.$$-"" +#"'#".767327&7632326'7%6'"62#"&7676+}JpZ!T''ZynRTZS1'"=-> # :+  'Xn#b))݁kwk^''+@CY^;F;u/  %#q1BR@: $LKED   Z M =M=M=M= M  >K PX@>  Z M =M=M =M = M  >@?  b M =M=M=M= M  >YY@32PNIG@>982B3B&"$$'%% +54.# 32>?327327#"7"&7>3225>76#"7673267#"&7q+T?T "_C#D\7Bn-$./K+)VJ1FnDsͩ)xLs71 -}V=R\ n)!D;/'v6NQHPtg5}?T)nL^\ao{eŇj3>[;'R\# V+%TF m>+^NVTq1DT@<  $NMGF   ZM =M=M=M= M  >K PX@>  ZM =M=M =M = M  >@?  bM =M=M=M= M  >YY@32RPKI>=862D3D&"$$'%% +54.# 32>?327327#"7"&7>322#".7>73267#"&7q+T?T "_C#D\7Bn-$./K+)VJ1FnDsͩ)xLs3#a-7?S Vn)!D;/'v6NQHPtg5}?T)nL^\ao{eŇj3>)T')X #7FF;[ m>+^NVTq1BQal@J : $[ZTS K PX@E M = M =M=M =M = M  >K$PX@E M = M =M=M=M= M  >@B M=  M =M=M=M= M  >YYY@32_]XVPNIG@>982B3B&"$$'%%+54.# 32>?327327#"7"&7>322'>76#"76&7>32'"'73267#"&7q+T?T "_C#D\7Bn-$./K+)VJ1FnDsͩ)xLs70 -}V=T ` 73#3 n)!D;/'v6NQHPtg5}?T)nL^\ao{eŇj3>[;%T\# X)%TFR +%7 m>+^NVTq1DRbm@LN<  $\[UT K PX@EM =  M =M=M =M = M  >K!PX@EM =  M =M=M=M= M  >@BM=  M =M=M=M= M  >YYY@32`^YWQOKI>=862D3D&"$$'%%+54.# 32>?327327#"7"&7>322#".7>&7>32'"'73267#"&7q+T?T "_C#D\7Bn-$./K+)VJ1FnDsͩ)xLs3#_-7?R  Y4"3  n)!D;/'v6NQHPtg5}?T)nL^\ao{eŇj39)T%+X #7FE;ZR )% ; m>+^NVTq0O_K-PX@6B=<71YXRQ <@6B=<71YXRQ K PX@AZM=M = N  = M  = M  >K!PX@BbM=M = N  = M  = M  >K-PX@Fb=M=M = N  = M  = M  >@Lb`=M=M = N  = M  = M  >YYYY@&][VTMKECA?;953,*%#  +2'>76#"76%2#"75654.# 32>?327327#"7"&7>3273267#"&770 {=S `'! A+T?T "_C#D\7Bn-$./K+)VJ1FnDsͩ)xLsn)!D;/'v6NQX;B# X)'TC- @)HPtg5}?T)nL^\ao{eŇj3 m>+^NVTq1DTdK&PX@RQ<  $^]WV <@RQ<  $^]WV K PX@A   ZM =M=N =M = M  >K&PX@B   bM =M=N=M= M  >@H   b  `M =M=N=M= M  >YYY@FE32b`[YNLETFT>=862D3D&"$$'%%+54.# 32>?327327#"7"&7>322#".7>%2#".75673267#"&7q+T?T "_C#D\7Bn-$./K+)VJ1FnDsͩ)xLs3%] ,8?T U' n)!D;/'v6NQHPtg5}?T)nL^\ao{eŇj39)T%)Z #7FE;Z - @) m>+^NVTq1!1Dc\@+%-J VQPKE2  <,;$:K PX@P  bUUM = M = M = M =M>K PX@P  bUUM = M = M = M =M>@P  bUUM = M = M = M =M>YY@(#"a_YWUSOMIG@>970.+)(&"1#1!!%$ +73267#"&72'>76#"76%27#"&#"'63254.# 32>?327327#"7"&7>32To)#F;/'x4PO70 9hB=R ` ;:+H+'=4+Nq;y+T?T "_C#D\7Bn-$./K+)VJ1FnDsͩ)xLsj>+^NTVZ<?J5% X)'TF\ VV\HPtg5}?T)nL^\ao{eŇj3q11ATdU@+5 =L  $^]WV<<;4 :K PX@O  Z  U U M =M=M=M=M>K PX@O  Z  U U M =M=M =M =M>@P  b  U U M =M=M=M=M>YY@#CB32b`[YNMHFBTCT@>;9862A3A&"$$'%%+54.# 32>?327327#"7"&7>3227#"&#"'6322#".7>73267#"&7q+T?T "_C#D\7Bn-$./K+)VJ1FnDsͩ)xLs=6-H+'=4+Lq;{w3#^-7?S Vn)!D;/'v6NQHPtg5}?T)nL^\ao{eŇj3}\ XX\'T%)X #6EF;[ m>+^NVT)29JZGK-PX@B5 ML UT&<@B5 ML UT &K-PX@0  b UV = K  >K1PX@;  b U V = K  =K  >@8  b U V =K = M >YYY@;:43XWRPHFA@:J;J3949$334#334+%;2&#'76;27672;2&#'76;276'!"7!2'>76#"7673267"&7;--90hD%1 <9P9\?R;oa92 ){V=R^ 7P'+1'dG ''m`=>=:''8a#s" Z<%T\# X)%TF;#]`H17 bJVV29L[K PX@D5 N VU&<KPX@D5 N VU &<KPX@D5 N VU&<KPX@D5 N VU &<KPX@D5 N VU&<@D5 N VU &K PX@:  Z U V = K  =K  >KPX@;  b U V = K  =K  >KPX@0  b UV = K  >KPX@;  b U V = K  =K  >KPX@0  b UV = K  >@;  b U V = K  =K  >YYYYYY@;:43YXSQFE@>:L;L3949$334#334+%;2&#'76;27672;2&#'76;276'!"7!2#".7>73267"&7;--90hD%1 <9P9\?R;oa3%_.8?T  SP!<'+1'dH ''m`=>=:''8a#s" 'T%)X #6EF;[=%]/7dHVVZ3:LZjK PX@VD6 ]\9 ed'<KPX@VD6 ]\9 ed'<KPX@VD6 ]\9 ed'<KPX@VD6 ]\9 ed'<KPX@VD6 ]\9 ed'<@VD6 ]\9 ed'KPX@CV = M  =  M  =K =K >KPX@8V = M  =  M  =K >KPX@CV = M  =  M  =K =K >KPX@; I    UV =K =K >KPX@0 I    UV =K >K$PX@; I    UV =K =K >@< U   UV =K =K >YYYYYYY@"<;54hgb`YWSQJHCB;L76#"76&7>32#"'73267"&7+-;3;L#%/ <;P9` =P;ma73 7gA;RZ 53%5 R!%+1'dG''67`=>=:''8a#s" [;=L3# V+%TFR+' =%]^H/7dHVV#X3;N\mK PX@XF6 _^9 gf'<KPX@XF6 _^9 gf'<KPX@XF6 _^9 gf'<KPX@XF6 _^9 gf'<KPX@XF6 _^9 gf'<@XF6 _^9 gf'KPX@CV = M  =  M  =K =K >KPX@8V = M  =  M  =K >KPX@CV = M  =  M  =K =K >KPX@0 I    UV =K >K!PX@; I    UV =K =K >@< U   UV =K =K >YYYYYY@"=<54kidb[YUSHGB@&7>32'"'73267#"&7Z+-;3;L#%!- <;P:` =P9l`3%`/7?U R3#5 R!%-/'dJHI ''67`=>=:''8a#s"  z&V%)X #7FF;XO )% #=%]^H/7dHVVT2:JYjK PX@B \[8 dc&<5;KPX@B \[8 dc&<5;KPX@B \[8 dc&<5;KPX@B \[8 dc&<5;KPX@B \[8 dc&<5;K-PX@B \[8 dc&<5;@B \[8 dc&<5;YYYYYYK PX@;  b   XV = M  =K >K PX@F  b   XV = M  =K =K >KPX@G  b   `V = M  =K =K >KPX@<  b   `V = M  =K >KPX@G  b   `V = M  =K =K >KPX@E  b   `   UV =K =K >KPX@:  b   `   UV =K >K$PX@E  b   `   UV =K =K >K-PX@I d  b   `  UV =K =K >@O d  b `  `  UV =K =K >YYYYYYYYY@(LK<;43hfa_WVTRKYLYHFA@;J76#"76%2#"543673267#"&7J+-<1 hD%/ m  O7^@P9o`73 }{=P b'  R!%,/'dJHI ''m`=>w''8a#s"  }[;B# X)'TD- @)=%]^H/7dHVVH2:M\kK PX@Q ZYE5 _^8 fe&<KPX@Q ZYE5 _^8 fe&<KPX@Q ZYE5 _^8 fe&<KPX@Q ZYE5 _^8 fe&<KPX@Q ZYE5 _^8 fe&<K&PX@Q ZYE5 _^8 fe&<@Q ZYE5 _^8 fe&K PX@?   ZV = M  =K =K >KPX@@   bV = M  =K =K >KPX@5   bV = M  =K >KPX@@   bV = M  =K =K >KPX@>   b  UV =K =K >KPX@3   b  UV =K >K&PX@>   b  UV =K =K >@D b  `  UV =K =K >YYYYYYYY@&ON<;43ihcaWUN\O\GFA?;M%2#"&75673267"&7J+-<1 hD%/ m  O7^@P9o`3%[/8?T  U'   O!;'+1'eH ''m`=>w''8a#s"  )T'+V #7FF;[- @)=%]/7dHVV/b3<M]mK PX@+QY 6 E _9 hg'KPX@P  b UVM= = M =K =K >KPX@E  b UVM= = M =K >KPX@P  b UVM= = M =K =K >KPX@N  b U UV = M =K =K >KPX@C  b U UV = M =K >@L  b U U UV =K =K >YYYYYY@*ON>=54kjec\ZWUTRN]O]KIDC=M>M4<5<$334$334+%;2&#'76;267672;2&#'76;276'!"7!2'>76#"76%27#"&#"'63273267"&7d+/;3;L#%/ <;P9` =P;oa92 ){V=S^ ;6+B+'?2+Jq;{fP!%+1'dH ''67`=>=:''8a#s" Z;%T\" Y)%TE] XX ]=%]^H/7dHVV3s3:K^oK PX@+> F 6Va`9 ih' F 6Va`9 ih' F 6Va`9 ih' F 6Va`9 ih' F 6Va`9 ih' F 6Va`9 ih'K PX@Q ZU  V M = = M =M =M >KPX@R  bU  V M = = M =M =M >KPX@F  bU  V M = = M =M >KPX@R  bU  V M = = M =M =M >KPX@P  b UU  V = M =M =M >KPX@D  b UU  V = M =M >@N  b U  UU  V =M =M >YYYYYYY@+ML<;54mkfdXWRPL^M^JHDBA?;K322#".7>73267#"&7j\;3;M"%/ <;O9` ?R;ma'=6+F-%;3+'U>;{u3&\/7?T  TQ !%-/'eIHJ3''67`=>=:''8a#s" $]ZZ XT])T%+V #5FE;Z=%]^H/7dHVV)!3B@+<;65  <  9KPX@9bb`M =M = M  >@6bb`  QM =M >Y@#"@>:81/*)"3#3! &*! +3632 '&6#"'6#"767>32'>76#"767327#"&73-E{??y3-)/^5; 71 :hB=S Zbp)#E54/)w3NRy%F^-;=#\ 9    X;;L5# X)'TCmi^NVT)!4C@,=<76  <  9K PX@8Zb`M =M = M  >KPX@9bb`M =M = M  >@6bb`  QM =M >YY@#"A?;9.-(&"4#4! &*! +3632 '&6#"'6#"767>32#".7>7327#"&73-E{??y3-)/^5; o3#a,8?R  Wyp)#E54/)w3NRy%F^-;=#\ 9    )T')X #7FF;[mi^NVT)!2@O @ <)IHCB  <  9KPX@?b  `M = M =M = M  >K!PX@<b  ` QM = M =M >@9b  ` QM = M=M >YY@#"MKGE?=970.('"2#2! &*!+3632 '&6#"'6#"767>32'>76&#"76&7>32'"'7327#"&73-E{??y3-)/^5;  7/ x=R^33#5 ep)#E54/)w3NRy%F^-;=#\ 9   X;B# Z)TCP )%  `mi^NVT)!3AP@$1=,JIDC  <  9KPX@?b  `M = M =M = M  >K!PX@<b  ` QM = M =M >@9b  ` QM = M=M >YY@#"NLHF@>:8.-(&"3#3! &*!+3632 '&6#"'6#"767>32#".67>&7>32'"'7327#"&73-E{??y3-)/^5; !3#^ -7RZ T3#5 [p)#E54/)w3NRy%F^-;=#\ 9   )T%)X "Z\;ZR )% hmi^NVT)!2@OR@!>=*IHCB  <  9K PX@; Zb  `M =M = M  >KPX@< bb  `M =M = M  >K&PX@9 bb  ` QM =M >@= bb  ` Q=M =M >YYY@"43#"MKGE<:3@4@0.)("2#2! &*!+3632 '&6#"'6#"767>32'>76#"76%2#"7567327#"&73-E{??y3-)/^5; 90 -}V=T ` '  !op)#E54/)w3NRy%F^-;=#\ 9   Z;%T\# X)%TE + @'mi^NVT)!4CRp@, LKFE  <  9K PX@B  b Xb  `M=M = M  >KPX@C  b `b  `M=M = M  >K$PX@@  b `b  ` QM=M >@F  b  `  `b  ` QM=M >YYY@$65#"PNJHA@>=5C6C.-(&"4#4! &*!+3632 '&6#"'6#"767>32#".7>%2"74367327#"&73-E{??y3-)/^5; %3#`+7?U V' \p)#E54/)w3NRy%F^-;=#\ 9   'V%+V #7FE9Z+ @&mi^NVT#10BR@F N : *)#DC21QOLJIGCRDR@>981B2B0/&*!$$+7727#"&73632 '&6#"'6#"767>32'>76#"76%27#"&#"'632n)!C541)y3NR-E{??y3-)/^5; 71 :hB=U\ =6+F+%?1+Jp=ykh^LTTy%F^-;=#\ 9   X>;L5% X)%TF\ XX \)1!2ETI@0%-= NMHG  <,;$:  9K PX@I  Zb  `U U M =M= M  >KPX@J  bb  `U U M =M= M  >@G  bb  `U U Q M =M>YY@&43#"RPLJ?>973E4E1/+)(&"2#2! &*!+3632 '&6#"'6#"767>327#"&#"'>322#".7>7327#"&73-E{??y3-)/^5; =6-H+%=3+'X=;{v1%Z-7?S  Wdp)#E54/)w3NRy%F^-;=#\ 9   L\ XXXT\'T%)X #6EF;[=mi^NVTOaq@7"YclkdJKPX@EbUS K  = K  = M >KPX@8bUS K  = K  >K(PX@EbUS K  = K  = M >@CbU  US K  = M >YYYY@#QPonig_]XWPaQaOLIFC@<9633334333+%!;2&#'76;276&+"5?3%+"!6&+"?3%+";2&#'76;22'>76#"7673267"&7ddd +=5Q5q-;6P  7oTT ,>5P8o ,<5R6qh71 7iA=P^ $R!%+1'dG1)''Z/+''ZO/+''Z/+''X>=L3# X)%TF;%\^H27 bJVVObrK PX@7"ZdmlJ<KPX@7"ZdmlJ<KPX@7"ZdmlJ<KPX@7"ZdmlJ<KPX@7"ZdmlJ<@7"ZdmlJK PX@BZUS K  =K  = K  >KPX@CbUS K  =K  = K  >KPX@8bUS K  = K  >KPX@CbUS K  =K  = K  >KPX@8bUS K  = K  >K(PX@CbUS K  =K  = K  >@AbU  USK  = K  >YYYYYYY@#QPpojh\[VTPbQbOLIFC@<9633334333+%!;2&#'76;276&+"?3%+"!6&+"?3%+";2&#'76;22#".7>73267"&7dbb +;7Q5q +=6P  7qTR *>5P8q *>5P8oh3%_/8?T  UP!%+1'dH/+''Z/+''ZO/+''Z/+'''T%)X #6EF;[=%]^H/7dHVV ROaoK PX@%id7"8kYqzyrJ<KPX@%id7"8kYqzyrJ<KPX@%id7"8kYqzyrJ<KPX@%id7"8kYqzyrJ<KPX@%id7"8kYqzyrJ<@%id7"8kYqzyrJKPX@KSM= K  =M=K  = K  >KPX@@SM= K  =M= K  >KPX@KSM= K  =M=K  = K  >KPX@8IUS K  = K  >K!PX@CIUS K  =K  = K  >K(PX@DUUS K  =K  = K  >@BU  UUSK  = K  >YYYYYYY@'QP~|wunlhf_]XWPaQaOLIFC@<9633334333+%!;2&#'76;276&+"5?3%+"!6&+"?3%+";2&#'76;22'>76#"76&5>32'"'73267#"&7Hddf+=5Q5q-;6P  7oTT ,>5P8o ,<5R6q 73 7gA;R^ 93#3 ͋R %-/'dJHI1)''Z/+''ZO/+''Z/+''Z;=L3# V+%TEQ+% #=%]^H/7dHVV ?Obq}K PX@"j7"1Zs|{tJ<KPX@"j7"1Zs|{tJ<KPX@"j7"1Zs|{tJ<KPX@"j7"1Zs|{tJ<KPX@"j7"1Zs|{tJ<@"j7"1Zs|{tJKPX@KSM= K  =M=K  = K  >KPX@@SM= K  =M= K  >KPX@KSM= K  =M=K  = K  >KPX@8IUS K  = K  >K!PX@CIUS K  =K  = K  >K(PX@DUUS K  =K  = K  >@BU  UUSK  = K  >YYYYYYY@'QP~ywpnig\[VTPbQbOLIFC@<9633334333+%!;2&#'76;276&+"?3%+"!6&+"5?3%+";2&#'76;22#".7>&7>32'"'73267#"&71edd *>5P8o *>5 P7qTT -;8P  5q +=5Q5q3%^/7?U  T3#3ՋR!%-/'dJHG/+''Z/+''ZO/+''Z1)''&V%+V #7FE9ZO )%%=%]^H/7dHVV \O`oK PX@7"mlXqzyrJ<KPX@7"mlXqzyrJ<KPX@7"mlXqzyrJ<KPX@7"mlXqzyrJ<KPX@7"mlXqzyrJ<@7"mlXqzyrJK PX@GZSM= K  =K  = L  >KPX@HbSM= K  =K  = L  >KPX@=bSM= K  = L  >KPX@HbSM= K  =K  = L  >KPX@;bUS K  = L  >K&PX@FbUS K  =K  = L  >K(PX@JdbUS K  =K  = L  >@HdbU  USK  = L  >YYYYYYYY@+baQP}|wujhaobo^\WVP`Q`OLIFC@<9633334333+%!;2&#'76;276&+"?3%+"!6&+"?3%+";2&#'76;22'>76#"76%2#"&75673267"&77edd *>5P8o *>5P8qTT -;8P 5s +=6P 5o 74 +{V=S^'  R!%+1'dG/+''Z/+''ZO/+''Z1)''Z;'R\# V+%TE - @'=%]^H/7dHVV BObpK PX@7"nmZrzyJ <KPX@7"nmZrzyJ <KPX@7"nmZrzyJ <KPX@7"nmZrzyJ <KPX@7"nmZrzyJ <@7"nmZrzyJ K PX@GZSM= K  =K  = L  >KPX@HbSM= K  =K  = L  >KPX@=bSM= K  = L  >KPX@HbSM= K  =K  = L  >KPX@FbUS K  =K  = L  >KPX@;bUS K  = L  >K&PX@FbUS K  =K  = L  >K(PX@Lb`US K  =K  = L  >@Jb`U  USK  = L  >YYYYYYYYY@+dcQP}|wuljcpdp\[VTPbQbOLIFC@<9633334333+%!;2&#'76;276&+"?3%+"!6&+"?3%+";2&#'76;22#".7>%2#"75673267"&7%dce -;8P 5q +=5Q5qTR +>5P8q +=5 P7o3%^/7?U T'! P!<'+2'eH/+''Z/+''ZO/+''Z/+'')T'+V #7FF;[- @)=%]/7dHVV /Oaq#K PX@(em7"Ys|{tJKPX@XbUSM=M = K  =K  = K  >KPX@MbUSM=M = K  = K  >KPX@XbUSM=M = K  =K  = K  >KPX@VbUUSM = K  =K  = K  >KPX@KbUUSM = K  = K  >K(PX@TbUUUS K  =K  = K  >@RbUU  UUSK  = K  >YYYYYYY@/cbQP~ywpnkihfbqcq_]XWPaQaOLIFC@<9633334333+%!;2&#'76;276&+"?3%+"!6&+"?3%+";2&#'76;22'>76#"76%27#"&#"'63273267#"&7dee +>5P7o +;7 P7qTT -;8P 5q +=5P5q71 7hB=P ^;5+B-%?0+Hq;}R!%-/'dJHI/+''Z/+''ZO/+''Z1)''X;=L3" Y)%TC]ZZ ]=%]^H/7dHVV ?O_rK PX@(S[7"jt}|uJK PX@WZUSM=M = K  =K  = K  >KPX@XbUSM=M = K  =K  = K  >KPX@MbUSM=M = K  = K  >KPX@XbUSM=M = K  =K  = K  >KPX@VbUUSM = K  =K  = K  >KPX@KbUUSM = K  = K  >K(PX@TbUUUS K  =K  = K  >@RbUU  UUSK  = K  >YYYYYYYY@/a`QPzxlkfd`rar^\YWVTP_Q_OLIFC@<9633334333+%!;2&#'76;276&+"?3%+"!6&+"?3%+";2&#'76;227#"&#"'6322#".7>73267#"&7dbd -;7P7o -<7P 5qTT -;5 P5q ->5P8o4;6+B-%?/+Hp={t1'[/8?T  S݋R!%-/'dJHG/+''Z/+''ZO/+''Z/+''/] XX ]'V%)X #7FE;X=%]^H15dHVV/7HW@@'&6 QPKJ K PX@1ZUM =M = M  >@2bUM =M= M  >YY@98USOMFD?>8H9H.$$-"" +#"'#".767327&7632326'7%6'"62'>76#"767327#"&7+}JpZ!T''ZynRTZS1'"=-> # :71 .|V=U\ q)!F530)w3PP'Xn#b))݁kwk^''+@CY^;F;uX>'R\# X)%TF mi`LSW/7JY@B'&6 SRML K PX@1ZUM =M = M  >K1PX@2bUM =M= M  >@0b UUM= M  >YYY@98WUQODC><8J9J.$$-"" +#"'#".767327&7632326'7%6'"62#".7>7327#"&7+}JpZ!T''ZynRTZS1'"=-> # :1%_-8?R  Wq)!F530)w3PP'Xn#b))݁kwk^''+@CY^;F;u)T$)X #6EF;Z mi`LSW/7HVeu@ PR@'&6 _^YX K PX@8UM = M =M = M  >K$PX@8UM = M =M= M  >K(PX@5UM =  M =M= M  >@3 UU  M =M= M  >YYYY@98ca][USOMFD?>8H9H.$$-""+#"'#".767327&7632326'7%6'"62'>76#"76&7>32#"'7327#"&7+}JpZ!T''ZynRTZS1'"=-> # :7/ .|V=S ` 74#3  q)!F530)w3PP'Xn#b))݁kwk^''+@CY^;F;uZ<%S]# Y(%TFR (% O mi`LSW/7JXgu@ RTB'&6 a`[Z K PX@8UM = M =M = M  >K$PX@8UM = M =M= M  >K(PX@5UM =  M =M= M  >@3 UU  M =M= M  >YYYY@98ec_]WUQODC><8J9J.$$-""+#"'#".767327&7632326'7%6'"62#".7>&7>32#"'7327#"&7+}JpZ!T''ZynRTZS1'"=-> # :3#_-8?R  W4#3  q)!F530)w3PP'Xn#b))݁kwk^''+@CY^;F;u)T%)X #6EF;[R (% O mi`LSW/7HWfm@UT@'&6 `_ZY K PX@4 ZUM =N = M  >K!PX@5 bUM =N= M  >K(PX@3 b UUN= M  >@7 b UU =N= M  >YYYY@JI98db^\RPIWJWFD?>8H9H.$$-""+#"'#".767327&7632326'7%6'"62'>76#"76%2#"&7567327#"&7+}JpZ!T''ZynRTZS1'"=-> # :71 .|V=U\ ' q)!F530)w3PP'Xn#b))݁kwk^''+@CY^;F;uZ<'R\# X)%TF - ?' mi`LSW/7IXgK$PX@!GVUB'&6 a`[Z <@!GVUB'& 6 a`[Z K PX@4 ZUM =N = M  >K$PX@5 bUM =N= M  >K(PX@;  b  `UM =N= M  >@9  b  ` UUN= M  >YYYY@KJ98ec_]SQJXKXDC><8I9I.$$-""+#"'#".767327&7632326'7%6'"62#".47>%2#"&7567327#"&7+}JpZ!T''ZynRTZS1'"=-> # :3%] -8RZ W' q)!F530)w3PP'Xn#b))݁kwk^''+@CY^;F;u)V%)X #X_9]- ?' mi`LSW'/7HXh0@,L T @'&6 ba[Z K PX@Cb U  UUM =M = M>@Cb U  UUM =M= M>YY@"JI98fd_]WURPOMIXJXFD?>8H9H.$$-""+#"'#".767327&7632326'7%6'"625>76#"76%27#"&#"'63273267#"&7+}JpZ!T''ZynRTZS1'"=-> # :7/ -}V;T ` ;:+H-}%?4+Nq;yq)!D;/)v3NR'Xn#b))݁kwk^''+@CY^;F;uZ;%T\# X)%TE\ XX \ m>+`LVT+/7GZi.@,; CR '& 6 cb]\ K PX@B  Z  U UU M =M = M>@C  b  U UU M =M= M>YY@"IH98gea_TSNLHZIZFDA?><8G9G.$$-""+#"'#".767327&7632326'7%6'"627#"&#"'6322#".7>7327#"&7+}JpZ!T''ZynRTZS1'"=-> # :A=8+H+}'=4+Lq;yw3#`+7?U Vq)!F530)w3PP'Xn#b))݁kwk^''+@CY^;F;u%\ZZ \)V%)X %7FF9] mi`LSWs-?P@7 BA JI"@=  b`   UM =N  = M  >Y@/.NLGE=;65.?/?&"''!!+%32673!"7>76#"#!73;7&76! 2'>76#"7673267#"&7#RJ5=V% F +)!R#%P57V%+#/#a73 7gA;R^ R %-/'eIHJw)R!PѨb!N-w;aZ<=L3# V+%TF="\^J17 dHVV\-@Q@8 CB KJ"KPX@<  bX   UM =N  = M  >@=  b`   UM =N  = M  >YY@/.OMHF:942.@/@&"''!!+%32673!"7>76#"#!73;7&76! 2#".7>73267#"&7#RI5=V% F +)!R#%P59T%+#/#B1'[ 18?T  UR!%-/'dJHIw)R!PѨb!N-w;a'T%)X #6EF;[="\^J17 bJVV->L\Y@H6 ON WV"KPX@F b M =M =  M =N = M >K!PX@> b  I    UM =N = M >@? b  U   UM =N = M >YYY@/.ZYTRKIEC<:54.>/>&"''!!+%32673!"7>76#"#!73;7&76! 2'>76#"76&7>32'"'73267"&7!TG5=V!F!+)R}#%P!39T%+#/#92 ){V=R ^ 73%5 \P'+1'dHw)R!PѨb!N-w;aZ;%T\# X)%TEQ +% '="\^J17 bJVV-@N^\@HJ8 P YX"KPX@F b M =M =  M =N = M >K!PX@> b  I    UM =N = M >@? b  U   UM =N = M >YYY@/.\[VTMKGE:942.@/@&"''!!+%32673!"7>76#"#!73;7&76! 2#".7>&7>32'"'73267"&7{!RG5=X#F +)!R}##N!57T%+#/%3%a/8?T U4#3 kP'+2'eHw)R!RѨb!N-w;a&V%)X #7FE9ZO )% )="\^J17 bJVV-?N_'@7 QP YX"K(PX@G   b   ``   UM =N =M >@K  d   b   ``  UM =N =M >YY@"A@/.][VTLKIG@NAN=;65.?/?&"''!!+%32673!"7>76#"#!73;7&76! 2'>76#"76%2#"743673267#"&7+#RJ5=V% F +)!R#%P57V%+#/#V74 8hB=S^ '  IR %-/'dJHJw)R!PѨb!N-w;a[;=L3# V+%TF - @'="\^J17 dHVV-@N^vK&PX@K8 P YX"<@K8 P YX"KPX@A   bX M =M =N = M >K&PX@@   b`   UM =N = M >@F   b  ``   UM =N = M >YYY@ BA/.\[VTJHANBN:942.@/@&"''!!+%32673!"7>76#"#!73;7&76! 2#".7>%2#"57673267"&71!RJ6@X% C!+)!R"%P58V%+#/#N3%^/7?U  T' bP'+2'eHw)R!RѨb!N-w;a'V%+V #7DH;[- @'="\^J17 bJVV{-?O_O@#C K 7 RQ YX"KPX@P  b `  U  U M =M =N =M >@N  b `  U  U  UM =N =M >YY@$A@/.][VTNLIGFD@OAO=;65.?/?&"''!!+%32673!"7>76#"#!73;7&76! 2'>76#"76727#"&#"'63273267#"&7!TG5=V!F!+)R}#%P!39T%+#/#73 7gA=RZ =4-F+';3+Hp={R<'+/%fJHGw)R!PѨb!N-w;aX;=L3" W+%TC]ZZ ]="\17 dHVV-=P`@#1 9 H SR[Z"<8 ;0 :K PX@P  ZX  U M = M =M =N =M >KPX@Q  bX  U M = M =M =N =M >KPX@P  b `  U  U M =M =N =M >@N  b `  U  U  UM =N =M >YYY@$?>/.^]XVJIDB>P?P<:7542.=/=&"''!!+%32673!"7>76#"#!73;7&76! 27#"&#"'6322#".7>73267"&7+#RJ5=V% F +)!R#%P57V%+#/#H=4-F+'?/+Hp;}u1'Z/7?U  R`R!%+1'dGw)R!PѨb!N-w;aA] XX ]'V%)X #7FE;X="\^J17 bJVVq11?@$<982:K PX@*UM=M=M>K PX@*UM=M =M >@*UM=M=M>YY@ %%&"$$'%% +54.# 32>?327327#"7"&7>323267#"&7q+T?T "_C#D\7Bn-$./K+)VJ1FnDsͩ)xLsoh\P/+\uuHPtg5}?T)nL^\ao{eŇj3NlbX{q1=@$K PX@+ UM=M =M >@+ UM=M=M>YY@42:72=4=&"$$'%% +54.# 32>?327327#"7"&7>32!"7>3!2q+T?T "_C#D\7Bn-$./K+)VJ1FnDsͩ)xLs#  #'HPtg5}?T)nL^\ao{eŇj3''qm1DT-@:9$NMGF K PX@9b =M=M =M = M  >KPX@9b =M=M=M= M  >@6ddM=M=M= M  >YYY@RPKI($&"$$'%% +54.# 32>?327327#"7"&7>32672#"/&7673267#"&7q+T?T "_C#D\7Bn-$./K+)VJ1FnDsͩ)xLs?  n)!D;/'v6NQHPtg5}?T)nL^\ao{eŇj3   &! m>+^NVTq1A@$;:43<:K PX@,M=M=M=M>K PX@,M=M =M =M>@,M=M=M=M>YY@ %'&"$$'%% +54.# 32>?327327#"7"&7>3273267#"&7q+T?T "_C#D\7Bn-$./K+)VJ1FnDsͩ)xLsn)!D;/'v6NQHPtg5}?T)nL^\ao{eŇj3 m>+^NVTqu1AQ2@$KJDC K PX@:b =M=N =M = M  >KPX@:b =M=N=M= M  >@7 ddM=N=M= M  >YYY@32OMHF;92A3A&"$$'%% +54.# 32>?327327#"7"&7>322#"&767673267#"&7q+T?T "_C#D\7Bn-$./K+)VJ1FnDsͩ)xLs-!+  !n)!D;/'v6NQHPtg5}?T)nL^\ao{eŇj3/  %# m>+^NVTq#1A)@ 5 =$<<;4 :K PX@5 U M =M=M=M>K PX@5 U M =M=M =M >KPX@5 U M =M=M=M>@3  U UM=M=M>YYY@32@>;9862A3A&"$$'%% +54.# 32>?327327#"7"&7>3227#"&#"'632q+T?T "_C#D\7Bn-$./K+)VJ1FnDsͩ)xLs=;:+H+}'=4+Js;yHPtg5}?T)nL^\ao{eŇj3o\ XX \q#1AQ\@'5 =$KJDC <<;4 :K PX@? U M =M=M=M= M  >K PX@? U M =M=M =M = M  >KPX@? U M =M=M=M= M  >@=  U UM=M=M= M  >YYY@32OMHF@>;9862A3A&"$$'%%+54.# 32>?327327#"7"&7>3227#"&#"'63273267#"&7q+T?T "_C#D\7Bn-$./K+)VJ1FnDsͩ)xLs=;:+H+}'=4+Js;y)n)!D;/'v6NQHPtg5}?T)nL^\ao{eŇj3o\ XX \ m>+^NVTq29GV@S5&<  d  U V =K >::43:G:GECA@><3949$334#334+%;2&#'76;27672;2&#'76;276'!"7!#"&733267;--90hD%1 <9P9\?R;oajs 9oTP ''m`=>=:''8a#s" -h}k9DC:'29EL@I5&<   U V =K ><:43B?:E3!2;--90hD%1 <9P9\?R;oa %& ''m`=>=:''8a#s" ))29ML@I5 &<  d  b V =K >43HF=;3949$334#334 +%;2&#'76;27672;2&#'76;276'!"7!6320#"/&76;--90hD%1 <9P9\?R;oaC   ''m`=>=:''8a#s" }    %29HR@OD5 &<   d  b V =K >;:43BA:H;H3949$334#334 +%;2&#'76;27672;2&#'76;276'!"7!2"&7476;--90hD%1 <9P9\?R;oa#,  ''m`=>=:''8a#s" /   %#TD29JK-PX@<;5DC&<@<;5DC &K1PX@*  V = K  =K  >@'  V =K = M >YY@43HFA?3949$334#334 +%;2&#'76;27672;2&#'76;276'!"7!%73267#"&7;--90hD%1 <9P9\?R;oa R!%,/'dJHI ''m`=>=:''8a#s" 3;#]`H17 bJVV7D@eM >Y@   +2'>76#"7670 .}V=U` 7Z;%T\# X)%TE4 :KPX@ M>@IMAY%& +%73267#"&7#X %//)fJHEy;#]?K17 dHVV7D@eM >Y@   +2'>76#"7670 .}V=U` 7Z;%T\# X)%TE`]@ <: 9K$PX@QM >@UIMAY@  +27#"&#"'632F;9+H+}'=6+Nq;y\ XX \r9v@<;:KPX@UQM>@$UUIMAY@ +>2"$>2"27#"&#"'632j?N+BNAN+BNN;:+H+'?2+Nq;yoM88M66M88M6\ YY \)w!4C@)=<76 < 9KPX@3bb`=M =M>K!PX@0bb`Q=M >@-ddb`QM >YY@A?;9/-%#! &*! +3632 '&6#"'6#"767>3632#"/&767327#"&73-E{??y3-)/^5;  @  p)#E54/)w3NRy%F^-;=#\ 9      %=mi^NVT)!0@*)$# < 9KPX@&b`M=M>@#b`QM>Y@.,(&! &*!+3632 '&6#"'6#"767>37327#"&73-E{??y3-)/^5; p)#E54/)w3NRy%F^-;=#\ 9   mi^NVT)s!1@@-:943 < 9KPX@4bb` =M =M>KPX@1 ddb`M =M>@. ddb`QM >YY@#"><86+)"1#1! &*! +3632 '&6#"'6#"767>32#"&74767327#"&73-E{??y3-)/^5; !+  p)#E54/)w3NRy%F^-;=#\ 9   /  %#mi^NVT)j!1@"%-<,;$: 9KPX@,bc UM =M >@*bcU UM >Y@#"0.+)(&"1#1! &*! +3632 '&6#"'6#"767>327#"&#"'6323-E{??y3-)/^5; ;9+H+%B1+Nq;yy%F^-;=#\ 9   :\ YY \)j!1@@,%-:943  <,;$:  9KPX@9b  ` UM =M = M  >KPX@6b  ` U QM =M >@4b  `U U QM >YY@#"><860.+)(&"1#1! &*! +3632 '&6#"'6#"767>327#"&#"'6327727#"&73-E{??y3-)/^5; ;9+H+%B1+Nq;yn)#C74-'w5NPy%F^-;=#\ 9   :\ YY \ mh^NVT=N@D  )(K(PX@E d Z  bbXU L  ==L >@C d Z  bbX  UU=L >YY@IHA?=;4243$&4"4 +6&#!"!26?6;2#"=4&#!3!267!'76;276&+"?7%672"/&767NJL T32 - ) 0%1g!%x^33s =j )9= VD +NAK+/>AJ{@/+'} -Z1++   &;K@G  '@E  d Z  bbZ  UU=L >Y@=<ECh ';;  *  +NAK)1>^`@/+' -Z1++/  %#LOb@7"XJ@0db  US L  >Y@][RQOLIFC@<9633334333+%!;2&#'76;276&+"?3%+"!6&+"?3%+";2&#'76;262#"/&76 edd *>5P8o *>5P8qTT -;8P 5s +=6P 5o  D  /+''Z/+''ZO/+''Z1)''    #9O^@ 7"J@1db  US L  >Y@QPYWP^Q^OLIFC@<9633334333+%!;2&#'76;276&+"?3%+"!6&+"?3%+";2&#'76;22#"&?6bee +=6P 7o +;7Q5sRT -<7P6q +=5 P5q+  1)''Z/+''ZO/+''Z1)''/  %#%D+O_@7"QYXJD@0  USK  = K  >Y@][VTOLIFC@<9633334333+%!;2&#'76;276&+"?3%+"!6&+"?3%+";2&#'76;273267#"&79ebd ,<5R6q ,<5R6qRT -;5Q5q -;8P 5q#R<'+2)eGJH/+''Z/+''ZO/+''Z/+''F;"\17 dHVV7B\@ @M =M >Y@  +2'>76#"76&7>32'"'7/ -}V=T`53#3  3X;%T\" Y)%TCP )% \;K$PX@eM >K-PX@e =M >@bcU >YYY@  +2'>76#"76%2"?671 .|V=U\' -X;'R\" Y)%SD+ =)\7 V@S<;:eUM=M >    +25>76#"76%27#"&#"'632P7/ -}V;T` ;9+H-{'B1+Nq;yZ<%S]# X)%TF\ XX \H1'@K PX@!bU=M >@!bU=M>YY@%#%$" +327#"&76&#"767>33267#"&71Pb1;VJq )/`6; h\P/+\uuPb'H`cZ;! 9   NlbX{5%@ K PX@"bU=M >@"bU=M>YY@"%%%$" +327#"&76&#"767>3%!"7>3!21Pb1K PX@0d  bbU =M >@0d  bbU =M>YY@;9/-+)&%"!%$" +327#"&76&#"767>3&>32"$>2#"632#"/&761Pb1K PX@1 d  bbU =M >@1 d  bbU =M>YY@-,53,:-:+*&%"!%$" +327#"&76&#"767>3&>32"$>2"2#"74761Pb1K PX@,b UM ==M >KPX@,b UM ==M>@*bU U=M>YYY@(&#! ))%$" +327#"&76&#"767>327#"&#"'6321Pb1\ XX \!)94@- 5 <4 ;, :K PX@6b  UU M = =M>K PX@6b  UU M = =M >KPX@6b  UU M = =M>@4b  U   UU =M>YYY@+*86310.*9+9)(%$! $$" +327#"&76#"767>3&>2"$>2"27#"&#"'63250Pb1@$ dUUK >Y@&&&3&3"#334333 +;2&#'76;276&+"?3%+"#"&733267m +=5 P5q -<7P5qhs :pTP/)''X/+''h}k9DC:%y-%1h@  @UUK >Y@(&.+&1(1334333 +;2&#'76;276&+"?3%+"7!"7>3!2b ,<5R6q ,<5R6q %&/)''X/+''))%9n@ / @"dbUL >Y@ )"334333+;2&#'76;276&+"?3%+"%632#"/&765 ,>5P8o *>5P8qD  /)''X/+''   %%4r@  @#dbUL >Y@'&/-&4'4334333 +;2&#'76;276&+"?3%+"%2#"&?6# -;8P 5q -;6P 5qc*  /)''X/+''/ %#47B \@  @M =M >Y@  +2#".7>&7>32'"'3#a,8?R U3#3  3'V%)X "7FE;XP )% 2s; v K!PX@eM >@bcM >YY@   +2#".7>%2"5761%] -8?R  W'  3)T%)X "5FE;Z- =)L"@ <:K PX@#YUUM >KPX@"eUUM >@(eUUIMAYY@""  +27'"&#"'6322#".7>/?6+H-}%=3+Lp;yw3&\ -7?U V\ZZ \)T%+X #7FE;Z)1$2@  <,+%:K PX@!bU=N>K PX@!bU=N >@!bU=N>YY@ %#('$"+#"&7>76''>3232676'&7>323267#"&75Ӌ%X 5a)7%# iY}#3wR 8'i\P/+\utզ\);'93/HiuC-NlbX{-$0@  K PX@"bU=N >@"bU=N>YY@'%-*%0'0('$"+#"&7>76''>3232676'&7>32'!"7>3!25Ӌ%X 5a)7%# iY}#3wR 8'{ %%զ\);'93/HiuC-))3$-6I@?>=  K PX@0  d  bbU=N >@0  d  bbU=N>YY@DB:8('$" +#"&7>76''>3232676'&7>32$>2"$>2"632#"/&765Ӌ%X 5a)7%# iY}#3wR 8'BM+ ?N?N+DN? զ\);'93/HiuC-N55''55N55''5    '7J$/:I@  K PX@1   d  bbU=N >@1   d  bbU=N>YY@<;CB;I76''>3232676'&7>32>2#"&%>32"&2"&76765Ӌ%X 5a)7%# iY}#3wR 8'BN+ B'%+PD'%+BN+* զ\);'93/HiuC- '78&%88%'78&%88/  %!`%6@.!<9K PX@'ZM=M=M >K PX@'ZM=M=M>@(bM=M=M>YY@'&42-,&6'6&($*+'6>7632#"'" >73262'>76#"76հNATM8+%1oUXL\/ RM!70 -}V=T ` N@#8>$y.PkK/V/LZ<%T\# X)%TF`%8@0!<9K PX@'ZM=M=M >K PX@'ZM=M=M>K1PX@(bM=M=M>@&bUM=M>YYY@'&21,*&8'8&($*+'6>7632#"'" >73262#".7>հNATM8+%1oUXL\/ RM1%^-7?U TN@#8>$y.PkK/V/L)T$)X #6EF;ZF#$4@(0/  <':K PX@,b UM ==N>K PX@,b UM ==N >KPX@,b UM ==N>@*bU U=N>YYY@&%31.,+)%4&4('$" +#"&7>76''>3232676'&7>3227'"&#"'6325Ӌ%X 5a)7%# iY}#3wR 8';8+H+}'=4+Lq;{զ\);'93/HiuC->\ZZ \X$-6F@: B  K PX@6b UU M  ==N >@6b UU M  ==N>YY@87EC@>=;7F8F('$"+#"&7>76''>3232676'&7>32%>2"&%>2"&27#"&#"'6325Ӌ%X 5a)7%# iY}#3wR 8' BN)AN+R ?N+BM+x=6+F+$=4+Jp=yզ\);'93/HiuC-'77N55''77N55R\ XX \qBP@(=@'  d  UVK >Y@CCCPCPNLJIGE336339345+%&'&'#"&?37+"676&+"?37+";2&#'76;2#"&733267%T)  93c!; --<$`X +=5 P7ois 9qTPY; )' /-  ))'3;/+''8h}k9DC:BNw@(=@!   UUK >Y@ECKHCNEN336339345 +%&'&'#"&?37+"676&+"?37+";2&#'76;2!"7>3!2%T)  93c!; --<$`X +=5 P7oS %(Y; )' /-  ))'3;/+''()BU@(J  =K(PX@(  d  bK = K >@&  d  b U K >YY@PNFDB?363393#5 +%&'&'#"?307+"676&+"?37+";2&#'76;2672#"/&76-T + 93c!= +-;%`X +;7P5q D Y; )' /-  ))'3;/+''    &BR@F (N  =@&   d  b UK >Y@DCLJCRDR336339345 +%&'&'#"&?37+"676&+"?37+";2&#'76;22#"&7476 T!* :3` <-+;'`X +=6P5q- Y9 )'+' ))'3;1)''/   %#i,5H@ @ . 'KPX@2  bU M =M =K >@0  b   UUM =K >YY@76BA<:6H7H"#334*BR +%6#"#"5?632#"';2&#'76;23 %"%2#".7>Y @hZR9 HӅZ?X ,>5P8oq#l9>=X3%]/7?U R Z) "8ILJHO;/)'' 97'T%)X %7DH;[j9mL %2@/<dbIMA("$"+>32#"$>2"672#"/&76bA%'+D'%BN+AN @  oM88&'66M88M6   &j9J "8@5<dbIMA""$"+>32#"$>2"2#"&7476bA%'+D'%BN+ANu!+   oM88&'66M88M6/   %#jh@ <d[! +632"/&76=      )\/7JY@?'&6 SRMLK PX@,bU =N = M  >K*PX@,bU =N= M  >@)ddUN= M  >YYY@ WU*(&.$$-"" +#"'#".767327&7632326'7%6'"6632#"/&767327#"&7+}JpZ!T''ZynRTZS1'"=-> # :  =  q)!F530)w3PP'Xn#b))݁kwk^''+@CY^;F;u    )/ mi`LSW/7F@6 @?:9<'&:K PX@UM=M>K PX@UM =M>@UM=M>YY@ $).$$-""+#"'#".767327&7632326'7%6'"67327#"&7+}JpZ!T''ZynRTZS1'"=-> # :q)!F530)w3PP'Xn#b))݁kwk^''+@CY^;F;u mi`LSWy/7GV@'&6 POJIK PX@-bU =N = M  >K$PX@-bU =N= M  >@* ddUN= M  >YYY@98TRNLA?8G9G.$$-"" +#"'#".767327&7632326'7%6'"62#"&76767327#"&7+}JpZ!T''ZynRTZS1'"=-> # :+  3q)!F530)w3PP'Xn#b))݁kwk^''+@CY^;F;u/  %# mi`LSW/7G@!; C'&6 K PX@&  U UUM >@&  U UUM>YY@98FDA?><8G9G.$$-"" +#"'#".767327&7632326'7%6'"627#"&#"'632+}JpZ!T''ZynRTZS1'"=-> # : =5+F+}'=4+Js;{'Xn#b))݁kwk^''+@CY^;F;u\ XX \/7GV@(; C'&6 POJI K PX@0  U UUM = M  >@0  U UUM= M  >YY@98TRNLFDA?><8G9G.$$-"" +#"'#".767327&7632326'7%6'"627#"&#"'6327327#"&7+}JpZ!T''ZynRTZS1'"=-> # : =5+F+}'=4+Js;{5q)!F530)w3PP'Xn#b))݁kwk^''+@CY^;F;u\ XX \ mi`LSWZ ,"!K PX@#dbM =M >@#dbM =M>YY@ '%  $" +! ! "32%632#"/&76Z=V9;3N{7Ѵ)6; F  zm'/5H@1R   ' )@ %K PX@$dbM =M >@$dbM =M>YY@ #!))  $" +! ! "32%2#"&7476;R;;3Ny7Ѵ)3;!+  zm'/5HB 1R/  %#f-@@5  "@,d  b`M =N >Y@ ;9&&"''!! +%32673!"7>76#"#!73;7&76! 632#"/&76u!RG3=X#F!+)!R%#N!57T%+#0%C w)R!RѨb!N-w;a    %-=@  "@- d  b`M =N >Y@/.75.=/=&"''!! +%32673!"7>76#"#!73;7&76! 2#"&7676!TH5=V!F!++R}#"M!58S%+#-%,  w)R!RӨb!N-w;a/ %#d3D-=@0/76"@-bM =N  =M  >Y@ ;9)&"''!! +%32673!"7>76#"#!73;7&76! 73267#"&7!RJ5=V% F +)!R#%P57V%+#/#R!>')/'dJHGw)R!PѨb!N-w;aV="\/9 dHVVT'5 @ d[Y@   +2"&7476+  '/   %#0'd K$PX@eM >@eIMAYY@   +2#".7>3#a-8?R  Y')V%)X #8EF9]Z @IMA   +!"5463!2jL@JA+Z+^Z @IMA   +!"5463!2jL@JA+Z+^8 @IMA   +!"7>3!2y\*,8+"=+Dj\ @IMA    +!"&5463!25 !o#!;)G'`@GK? +!!y``@GK? +!!`1}@d[+#3#3TRRPR}^^/@,SGK? +!7%!7-/ZZZZu@ :[ +"&7>7>7Q? ]548B    g{4-,C +-  P("M,}@ 9[ +2&7>76./.7>GQ? ]548B    g׀|4-,C *- P)"L>3 9K!PX@ d >@ d[Y@  +2&7>76.#.7>YQ@ \649B    f|4-,C *-  P)"Lxe@ 9[% +.7>32'[oC2= PR 90e&|q]2'E 43, 3@.# :[33 +"&7>7>7!"&7>7>7Q? ]548B    fuQ@ ]648B    f{4-,C ,-  P("M{4-,C ,-  P("M}3$@!# 9d[33 +2&7>76.#.7>!2&7>76./.7>Q@ ]648B    fQ? ]548B    g׀|4-,C *-  P)"L|4-,C *- P)"L3># 9K$PX@d >@ d[Y@.-33 +2&7>76./.7>!2&7>76.#.7>Q? ]548B    gQ? ]549B    f|4-,C *- P)"L|4-,C *-  P)"LHe%@$9[% +.7>32.7>32[oC2= P R 90C2< QR :0e&|q]2'E 43,&|q]2'E 43,uX78@56' <eV>/.&$ 77 +2'"'#4'6#"'&5476763&5476766/ ('-r%%sA/%%/f %#I 7k-PyZh@}+ :/fP/L9Rm/)#"TXbT@Q[M>0_.a* <e V= M >ZXPNFE=;31)' bb +%2#"'#"'&54767#"'&'476763654'6#"'&5476763&54767663'"'6 - ''-r '&% 5m- ()+^#sA/%%/f %#I 7k- ('-r%y= -R1X{{/'%% R- D\b   +"&54632\p\s{q\oZ9?"+ݍ9_R} @M> +7462"&RZ{VZ{Vw;VR;9VRR! @M>+7462"&%462"&RZ{VZ{VZ{VZ{Vw;VR;9VR9;VR;9VRR @M>+%462"&%462"&%462"&Z{VZ{VZ{VZ{VZ{VZ{Vw;VR;9VR9;VR;9VR9;VR;9VR @IMA +462"&Z{VZ{V?;WR;9WR2 (AO\@3:65<2:K PX@/UU  U  U M >K PX@/UU  U  U M  >@/UU  U  U M >YY@ZXSQLJFD?=&#%$%%%$"+%327674#"'632#"54327654#"'632327'#"'#"74327654#"'632#"54 2?`85?%#0d?Rycy2?`85>%#0dO%"s-oRcvdB= ncy2?`85>%#0d?Qycxc$RzBj?de:Cx$RzBj?de(!f+}/&x$RzBj?de:Cx > (5C\jw@N UQ PK PX@3 U  U  UUM >@3 U  U  UUM>YY@usnlgea_ZXTRLJGE@>%%%$%%%$"+%327654#"'632#"74327674#"'632#"54327654#"'632327'#"'#"74327654#"'632#"542?`85>%#0d?Rycy 2?`85?%#0d?Rycy2?`85>%#0dO%"s-oRcvdB= ncy2?`85>%#0d?Qycxc$RzBj?de:Cx$RzBj?de:Cx$RzBj?de(!f+}/&x$RzBj?de:CxZ!@ <e>  +2#"&7476F,  0   %#!V+@( <e>  +2#"74762#"&7476+ -   / ##0   %#Z!/7@4+ <e>! )' /!/  +2#"&7476#2#"&7476#2#"&7476Z-  -   ,  0   %#0   %#0   %#}=P@ <e>! +632"/&76D   %d)-'LK1PX < @bc=>Y)')!+672#"/&76%6720#"/&76 C & D      $'   $}9j';!@1 <e>''!+632"/&76'6320"/&76'632"/&76 C  F  D     )!    %  %q "+'76ÛsjqoumopPo "+3767&'7qnoɀjowPw &2L@I('2/,)10-<+*:.9UUIMA$+%>2"&>2"&%>32"&>2"%7   ' L[3 L[3 JZ5 LZ3L/-3NZ5IZ6 LZ9 +,9B+ /@B--??-??--B@1-??ZB@Z??--@'"%% )'H4<B@?,)<b  =M> <;87+*4 4  +2#"7>7>7>>2#"2"747>7>>2"f-8`LVNZ3L/-?-5 a/ 5" WNZ6LZHNHL=) FJPLJZBBZ?XNHL=bi)!`JPLJZ@@Z@D,5A@ 6*;K PX@b` =M >@b` =M>YY.(&+#"&76767>32#"7>7>7>2"67676& 7!- y<_G!"2wt!^ 1CjNZ3 LZ] %} &N##%+XN)11.,`7#c-) FGN1Z@@--@E="X2`J'-5K1PX@ K>@GK?Y@  +!7-/ZZjA~@5Sc]LG%#"  {sgB><;4-) KPX@7Xb XV  V  >KPX@7db `V  V  >KPX@8Xb `V  V  >@7db `V  V  >YYYY@#xvpnfda_WU#*&'%(,$!+%&#"505672654&5465>32>32'"#".'#"&5765>%&#"=67254&5465>32>32'"#".'#"&=>&#"=67254&5465>32>32'"#".'#"&=>L"(/5Z}"(/5Zo"(/5Z7 3L P '/wL-BS" 9gl3!-37 3L P '/yL-BS"9gj3!-37 3L O '- xM-BU#9gj3!-3!!@ d[ +3#VTTD.8gq>@;b` M = M >pokjfd.&',.&% +#"&7>7632#"&?>7676&#">2"&#"&7>7632#"&?>7676&#">2"&D,'9 A?k 8a?;*3A ($ +P;3L?g gt? gs@ D,'9 A?k 8a?;*3A ($ +P;3L?g gt? gs@=&.A63N/P6^V0'-hD(!$,7^^/da@KPX@/b``M  =M >KPX@)b`M  =M >@/b``M  =M >YYY@:9UTPOCA9L:L,.&% +#"&7>7632#"&?>7676&#">2"&2#"&76767>462"&C-'8 A@k 9`?;*4A '$ *Q:2M?f ft? gt?:@ <$4# eZ{V[zV=&.A63N/P6^V0'-hD(!$,7^^/da@K PX@)b`M  =M  >KPX@/b``M  =M >KPX@)b`M  =M >@/b``M  =M >YYYY@:9UTPOCA9L:L,.&% +#"&7>762#"&?>7676&#">2"&2#"&76767>462"&[D,'9 A?k 8`?;*4A '$ +P;2M?g ft? gs?F:? =$4# eZ{VZ{V=&.A53O/P6^W/'-hD(!$,7^^/da@&""+7327632'47'j{?, !u22JkV93=阗 +߮'!`+PmK&PX@'M =M =M >@IUM >Y@ONLJB@864210.+PP +76767676'&'76;676767676'&'#"?327632#"'32'0+ J  l  >/  J+> FG1h=)1m-+>  X(55!%%d)!9+! %+yR\7Lw "! +7N)=c@ "@bcUM >Y@ =;31&/#+3#&'&'&'4&5476767672#"'#"'&54767632!! 1-"5)- %%''+%+#)"'%1-D''#!5 )'''!BL%7NKPX@QM>@UIMAY@'&0.&7'7%% +"32>767>."'&7>7632      g.F*]@ bUIL@Y@ -+'%C +&#"&67>?6&'&67672>32#"&\ $- 9G .') F6zF1,&I/+(:;"..";:fK*.eZ'T!]@  @!dTIK?Y@ C"+737#6+!6326'. # ntV`(A 0($ :: 7 >7+V-L+&/ ) !'\'QK(PX@bRK=>@bSR>Y+%%+6&'&/!!#".7>32 32P &636kd8#8;dJZ3   Q-W 9g79|d$,   ;d$+@(<:UIMA,$%$+>.#"7267#"'&7>7632q.) 1e*-sm:A&Q{ o/+|$-9#*SSPK(PX@eIMA@deIMAYY3 +#"'>763;276@: +  [<pO]5yjL%na E 0h@ $ @!bUIMAY@-+&%   +">5&32654'7#".5467&#&54762-B2(#2*O3$7V>`2[2\Kax">GS|GcH5$*%<+Og5,SfD3L`D+A]@UIMAY,$%$+327>76#">32767".6-* 1d*-sn9A&Q{ p4&@eT >Y+3##7#7373x0h0/ibRR5 Nm@GK? +!7Nmcc&U/@,SGK? +!7%!7-  XXZZ3  "+>7&3ȑj^F%LjW%_woc'b  "+'>76&'7bȑj^F&NjV%_woc' *>F*@' <bP >*C'F+&#"&67>?6&'&67>72>32&#"&67>?6&#"Y ' iG .(( '>7Z1:Y+! #- /4i  17%-  q+..":;I0.    qm:;"..FHM3 ,0Wa%7/@,UIMA'&0.&7'7%% +"32>767>."'&7>762       g/F+];M[a/S,lBAN 1&;#; )+J). 2$B'L ')F&+SDgY3B5_t/.i56@3<ddIK?  +%"&767>&#"&67>76&PW ] ,t':99 $(0A 3F-H'  ) .&31#Yq-8@5<b`UIK?*(*"+7>3232672!6?676'&#"#" hs)/F0Sb8*S( W:V %< 6B3w*.[H?,@M(3K4 @-b`bUR >Y@0.+)$# 55 +2654#"7>54&#"#"547>323#"546329o| # N$!A 3%5 Uf{mq4# ߄IT= fJ"."6,.x?6[K%\YNZ!5 '6!8@5 <dTIK?C"+737#6+!6326'. # ntV`(A 1'% :: 7 >7+V-K+&. (  '?}v'2@/b`SINB+%%+6&'&/!!#".7>32 32q &735ke8#7;cJ[3  Q{-V 9g79|d$,   a$I@ <:KPX@QM >@UIMAY,$%$+>.#"3267#"'&7>7632.) 1e*-tm9A%Rz o/+|$-8$+TSP763;276$@: +  [<pO]yjL&na4l 02@/ <UIMA-+   +">5432654'7#".54670'&547632-B2(#1*O2%7V>`1[2[Kaw">GS|GcHN5%*$<+Pg6,TfD4L`D+@]@i%@ <9K PX@UM>K PX@UM >KPX@UM>@UIMAYYY,$%$+%327>76'">32767#".63-+ 1d*-sn9A&Q{ p4&K PX@dYL >@deL>YY+%3##7#73730h00h9RRjD@K> +%!7Dcc_}/@,SGK? +!7%!7f  +XXZZfj  "+7>7&fȒj_E%jV%_xoc'q  "+%'>76&'7Ȓj_E%jV%_wob'.J)4;@8*<bUIMA($)$#'"+74632327#"&7#"&54%76'4&#"#"327yI "  53%#>N2;I~ /#>J $%+'08Q+!L7^FD.J<@9<USIMA # +%654#"3267#"&54632!M-[(M(4N/"sxfg} RW1FG-+!wjX#4 N5LJ +@(UIMA   +2654&#"'4>32#"&"Ff6/;Y%?3HuFo]Ňch{LZt;IP"XfS8tVw?SM@J)F1S><99U I M ARNKIB@5%$D&#D&! +6726767'&#"&?327"76&#"&?327#"32&#"'76326'&'32&#"''%c# )QT24  IW $)Y2&LX; 6t)B/J")+ q   *9 dr R=JK PX@UQK >KPX@UQK>@!USIMAYYY@ # +726?4&#"'632#"547!>M-[(M'4O."sxfg} \5RV1FG-+!wjX$3 NI<=@:6<ddbcIMA:2)9"+632&#76?>?6#"2&#76?>76&#'"&57672+#qS'4 N1 /f1^: R5}$b +|?h$$ \ $$ !A$% 5LY@V/7  E<dd  b U I K ?LJDA=;3'&#"4 +7732�7473>7654'"767367676&#"?37"72&#'76/&#"&) J1y !{! ZKjVh 9+ TN\PAJ/L9& $$ #- #6$`  "" GV J  *-/%@dd[2 +6&#'"&?672&#""+76?>7 *|@h N2$ %fA$% 5V $$ !J`K&PX@`\S<@`\S32;2&#7473>?654#"732&#7673>?674#"732&#7473>7674'"{ZLjVo&k-s > J1 :J-W@) J.9 L1\F'  N3y #K #kji%Dq/  $$ #NR $$ #)N\  $$ #BYvpF-@* <bIK?*K'F+&#"&67>?6&'&67>72>32&#"&67>?6&#" ' iG .') '>7 Z0:Y+! $- /4i  26%-  q+.-";:I0. !  qn:;".-FHM4 ,0FJ+:K&PX@ <@ @"dUVK>Y@ %'225&## +7654'"7673632#"';2&#76;>7327654&#"\Jb?DJ a;%/)  N3x#1!/ ';P- # ?=hR-#q) %'#`-)=+;@BJ->@;,<bUIUMA!-")#+372676&'.>32.#"#"&#"6- !$9)B%@L>L-b /B()>1/.: \[*![%K*%'&%="C~Y O=+'$'-8%:KGL!?@<<;:UIMA! %#$#$+327#"547#"574326?32#G)3RULVN 9:R- =###HNw1DN%H@& %FEC@7b  b`bUU >Y@@><:42-,+)$" HH +"&54732327657654&#"654#"'676;3>32#"'&"67稸1G9w55wG'E-PQ##;5/)-07 w7I03!ZRZ1  hw݉i0 /3#F1%3+-#m);[@X54 < Z U   SSK >;;963/,)$#335+#3#;2&#'76;267#737#73674&+"?3!+";b5 J5=: 93)6;{7L 3P`PD'')1LP`P#!$B%C@, BK PX@?  Zb` U  SSM >KPX@?  Zb` U  SSM>K1PX@@  bb` U  SSM>@D  bb` U  SSM=>YYYY@&?>=<:987420.)'$#"!  CC +%2327#"'&#"#"&=676767#737#7367672#"'&#"3#3#6NN1=J \PfX8-/7 om2  -{-^5#!)/D  9w 9'R B5!%';6 bP`P͋j)' #*%-}DP;%PN!/@%G rse @QbU  U UU G  U M  = M  >Y@#}zwqonmigdca_OMIHEC%$#,BU+74654'"#"'?632>?32'#32?3267654&'.547>32.#"#"'&"7'"747#!"'32&#'76;232654'"٦F9  LDCq4 +R -=8+'/X=u JZ338q?++8+?e 3G;@L!ibD 1 d]mbo5%GZ/H 1eP\36$; # g/7 dc$# %+B$B#`/5GB 1N++5#\F^)BG '-!":C+ {j3Ht#!-h9##+ /+!T:GK PX@c   " <KPX@c   " <KPX@c   " <K$PX@c   " <K(PX@c   " <@c   "< ;YYYYYK PX@@  b  U  U  U M = M >KPX@<  b  U  U  U M = M >KPX@@  b  U  U  U M = M >K$PX@<  b  U  U  U M = M >K(PX@G  b  U  U  UM = M = M >K1PX@F  b  U  U  U M =M = M >@L Z  b  U  U  U M =M = M >YYYYYY@~|kiedb`PNIHDB@>#!%335#%+327#"&'.#32&#'76;27654#""?62654#"3267673267654&'.547>32.#"#"'&#"Tq'N%'F^o 9K PX@B   bZ TP = =M=M >K$PX@C   bb TP = =M=M>@E b   `b TP =M=M>YYY@DCBA@@?>;:8765'"''#+%.#"32#327#7"&547676327!7!7654#"'727673!!^PEyXP! qMSv #H81{ )`L)%-A #oB...\=VOJ:%+=2{-OBD 3 _:b@_ * <b  bU S   S M  >::8754/-)'#%$#+?3762'"'674&#"!!!#2632#"'&547#?3671|U}u;Rx\D.B5 4 T 0}O1x =s+hkipP+3PTD +?f+.@1-?wf 6O]K1PX@YW$N<:89@Y$WN<:89YK1PX@UUM >@(UUM =M >Y@\ZUSMKCA53+)2V! +032!"&#76;277'676732>7654&+'6>7632#"'654#" 6732941\b7`dq su3RT VŪ\u1e;-{ohB!ǃ?7J5+u#T5 n)IN   mu=/ċ:!wlw9!jxg%Tn'e!8D4+! O@~F  . <USS  U  M  > OONGE@<;:9876530-,+)&#  %"+!32!!6567&#"!#3#!"'32�'76;27#737#737654'"#"572>763  3P  )B'GRk5#HX2Hf hF#  <`%I%Շ ?^5aR1+Ph9##PRP^RN; #w,N @ 2- "+ ' >76&#"32647#"&76327327#""&76$32#"&7>76&#"3267wjE- L -}#hR^@)yX >d 7#]G\d fN B) F!p/+1s)>l5c TyG FFT+7"{HT76&#"32647#"&76327327#""&#?32676&'.7>32'6&"wjE- L -}#hR^@)yX >d 7#]G#G7B+C 72;+ibB#J-V? /1s)>l5c TyG FFT+7"{L^23)?!'?+R_@DW9%?dXZeD  "+".7!2!"3267Ӡq-9%B#buWz);*Gu{)[qVDAkyوX7D(0 /+& "+ 7323"32676&#"26>2"R-9}T>F{+) 7?=] 9{\!b !g!R%)D-lޙc7^\99YYyyyw%2@ @ ;5/) "+ ' "&76$32#"&7>76&#"326732>76#">32#".wjE\g! fO A' F p--!?p5 #^X}PDW eHT:!"JO>).|PT5wY6KPw%W ;2 "+ ' "&76$32#"&7>76&#"326732676727327#"?>7#"&76?676#"'632wjE\g! fO A' F p-- d )4J$;!]H  mF- i7!aF eHT:!"JO>).|V95sTR- 7#}D  y-6O+ 7%{B 7D@KS RNIC3!"+!26?6732#"574&#!;2%'76;276&+"5?7%.+"%6&#"26>2"N31-- /%1O ,<56q#?> ='E8J 7?=] 9{\!b o+1; 0H}?/+b/+''Zu/+)D?R!)7^\99YYyyy?@/"+&'"'>3266#"''7676.7>727#"'67q}`tJL5Lt4^b5F`ypHpZ 7N5gf)LpdBVlYoZmr\_+H1>9nTkL|h1f7J?fPLPu#hT`ZooQ7%+OVVP "+%!;2&#'76;27&'#"?30%+"!6&+"?3%+";2&#'76;2%676&'bdd +=6P65O 5sRS ,<7P6q ,<5P5qXn3-1)''''ZO/+''Z/+''.& T- "+3676#"572767332327#"&767>&#" TP )C %nF1-č#f)l)u'1#i)${'); 3 7nV+%y#2+1tJh[/2T54"+732327#"&767>&#" '?676#"57276731vč#f)l)u'1#i)${! )C %nF1-t4R8_ 7nV+%y#2+1tJh[/2,R0O'); 35D="+6$32#"/&"'76232676/&76?&#".'Ps1 LB!%1%!QF%)9#7l4ٔy?'>\ J\k"ۚRJ/7/D'7d{H!!" a#TXN/FPJ/skfH+) Db" "+676&#"27#"&?'6?32 $!7HP=ob+)fJ'\VH ;ŝ=FZu!}i,F4`@yyo}+DKKE$"+#&'&";2&#'76;&+"&?!32?6&+"&?37+">7&' +3) )95PD XV   %98 H59F7B Su;)Dd13H'% !0 01L ''JI/.6#D=JV @ SMD>'"+%!!%.+"&?!327>32#"'&#" #"'&#"#"&7632322#"&7>6&#"326V#/?/ j5p)sL`/9 d73J i){hLhD?mbk{uj8B3\DB/[Z9/] 'yTAۻ<-%+P+?'6+LϴB-HZ!i;mkq{iyq`#7; 0< 81%"+6.#"3276;2&#'76;"?6$3 32676&#" RX3dRuf_ST ,<5R{uW#XZVuZ-^9' % ZwCi^M/+'''fhLV3')1-1dD).9 60+*&"+!"'327#"'.#"'>76767&! 32#"=U{fpuJ Po;!f'H4uA+3;$>7g)3=khz VLwFA9=&0F ']/`B 7L?7D\"+6$32%632327.>'.+'%'&#"'&"'7632>7>&#"'>76.!VF%%/# #>B- #'y%3+-#DXF0;+J7H }d_'#el}'!PT$/ZX-G` --?s#7; 8D @94"+6.#"327677#"&'&#;2&#'76;"?6$3 32676&#" RX3dRuf_Sd5k#7Xu T ,<5R{uW#XZ9' % ZwCi^{\ + `NM/+'''fh)1-1D91]5."+&#"#"'.#>?32676&'.7>32"'';2&#'76;27>7576#.?3673#"32&#'76;276 <u-I +9< i\)@ =F57T +19JH XRN+2B*=.  P+b%<3!{';#; Z- %  E2d/3J/Z 3BK?iI@F?(b;ݺN# Dxb   =  /N^*yW."+;2&#'76;276+"'7#!"57.+"#"'';2&#'76;27>7576#&5?373#"32&#'76;2746/  )? h g!&+L/5/ [V+B-/9  P-`% <3!!{'#< Z- %   1)%C  D$+%þ/  >;ݺN#  0   =  / %:D/"+%#!"&767676&#!"'673!63!2671y2 i7UE'#9B,N53w )=3j>;T;d;T!PAD   !D dD-'"+%32673!"7>76#"#!73;7&76! !RJ5=V% F +)!R#%P57V%+#/#w)R!PѨb!N-w;aV=6"+!"5476?#"#7!2327654'63!#54&+ -} %TJ6@D '+'#69L1=,/7=w/L d֪B=V4Ճ!=#) "+!76.#"327#"7632FE);Lq4-r@Pm$'B+H>y'+qR` BNyDGGE;"+#"&'#6'+&67>76&7>7'&767367&7>?N6 # k= E0 $^0> N% 3%t\2;S %+Ah 2 37!'-{Ps3.Y_7`N3Ht2=?1N'/]6EZ_XJV8Zg%w?N   /)$""+23!7!67>.#!"&7>?39YA+ 3sXXsN )DCHE !## LD+%J3fF@~~yHT@)A!\1o'"+".'#<6767>76&'&7>?32VG%))6)>X ;+'$NR>^ZDd-y%#'b ˉT n=P !!=V% o$ "+/67>77!"7>?3!2! ! !9 <TƑ FGJTHH!3% '?F+}DQF7N%bV %$"+>2"&;2&#'76732676#"'673Vg9Tf9A1 O 1/ j N}L3HGgGG>-+//+- LG+)Z5TX @V8 XK/PX@>   bb``   SU R >@Dd  db``   SU I NBYY@"76MLIECB>=6T7T0.+) 55 +2654#"7>54&#"#"747>32"54632"&767>&#"&67>76&%'9n{ $N$!@ 3&5 Ue{mq4# 5W ] ,t(99: $(0A 0rq݃JT > fI#."6,.x?6[J%[YNZ!6 '3F-H'  ) /%31#1f@9 < ;KPX@Nb`  b  `  XU S  U  I  N  B@Ob`  b  `  `U S  U  I  N  BY@32a_\ZNLGEA?862f3f*(*&+'>3232672!6?676'&#"#"2654"7>54&#"#"547>32"54632Erqn hs)/F0Sb8*S( W:V %< 7B|9o| # N$!A 3%5 Uf|mq4$ 3w*.ZH?,AM(3L3 fI#."6,.x?6[K%\XNZ!5 'Z'FJ@ H*J@;d db`   SSINBY@)(?>;7540/(F)F+%% +6&'&/!!#".7>32 3"&767>&#"&67>76&%'U '635kd7#8;dJZ3   RW ] ,t(99: $(0A 0rq<-W 8g79{d%,   3F-H'  ) /%31#J+Y`@] @<  bb`  SSR M =>YWMKCB*#+%% +'%6&'&/!!#".7>32 3>3232672!6?676'&"#"Erqd &735kd7$8;cJ[3  QY hs)/F0Sb8*S( W:V %< 7B5-W 9g79{d%,   |3w*.ZH@,@M(3K4 54&#"#"747>32"546326&'&/!!#".7>323Erq9n{ # N$!@ 3&5 Uf|mq4$  &636kd8#8;cJZ3   QJT> fI#."6,.x?6[K%\XNZ!5 '|-W 9g79{d%,   \"JM@K d db   b  ` S RK>@Dd db   b  `U S I N BY@MLJH=;64/.C"+'737#6+!6326'.6&'&/!!#".7>32 3#Erq ntV`(A 0($ :: 7  &735kd7#7;cJ[3  QX7+V-L+&. ) !&-W 9g79{d%,   Z$CG@E'G@,d dSUIMAY@&%<;8421-,%C&C,$%$ +>.#"7267#"'&7>7632"&767>&#"&67>76&%'i.) 1d*.tm9A%Q{ p/,|$W ] ,t(99: $(0A 0rqB-9#*SSPMK$%%+%% +'6&'&/!!#".7>32 32>.#"7267#"'&7>7632Erqp &636kd8#8;dJZ3   Q;.) 1d*.tm9A%Q{ p/,|$-V 9g79|d$,   -9#+SSP@-d dS UIMAY@21HGD@>=981O2O-+  +">5432654'7#".54670'&547632"&767>&#"&67>76&%'-B2(#1*O2%7V>`1[2[Kaw">GS|GcH W ] ,t(99: $(0A 0rq5$*%<+Og5,SfD3L`D+A]54&#"#"547>32#"54632">5432654'7#".5467'&547632Erq9o| # N%!@ 3&5 Ue{mq4# -B2(#1*O2%7V>_2[2[Kaw">GS|GcIJT> fI#."6,.x?6[K%\YNZ!5!'5$*%<+Of6,SfD3LaC+A]-,YWJH><,6-6+%% +'6&'&/!!#".7>32 32">5432654'7#".54670'&547632Erqj &636je8#7;cJZ3  QT-B2(#1*O2$7W>_2[2[Kaw">GS|GbH-V 9g79|d$,   5$*%<+Og5,SfD3L`D+A]K/PX@5bb  UQM =M >@3bb S  UQM >YY@JHCB;9/-''3$ +'#"'>763;276">5&32654'7#".5467&#&547632Erq^?; ,  [<pN]-B3'#2*O3$7W?`2[2\Kax">GS}GbHByjL&na5$*%<+Of6,SfD3LaC+A]@ddIK?Y@ "" +'"&767>&#"&67>76&Erq&W ] ,t(99: $(0A +3F-H'  ) /%31#5/-@* <UM >2";###+4'&+&'77#"32�76372767676 ); >)   '>_=% 7 '% ''%!5N*@'$<UK >2C+#3+<3+4'&+"32767676%4'&+&'7!7#";&#%7637276767>X#) )' J );   ='&;}^7=% 7  '  '% } %'  5R8k9@6@+ Z<U K >daB.#3+;=+3 +6'&+"36767>76%4'&+";2767676%4'&+&'7!7#"32&#%76372767676 '% (- F *)   '' J ); ;+ +=  ^}=% 7  !  !  '' } %'%!5^M@J\O4<   UM >[VUTSPMKDA?<:8/,(& ^^ +"76&#'&?32376;"#"'.+"2&#7637>7654&+&'7670326;/Bh3; LL ' "%"e/%>^=/% %; 7  h- 6  #%  //!&6'';! '%jN.@+7.(<U >HF@841%6!+%#"''&'&#'"'&?532326;">4656'&#'#&?532326;"$ ()   P) h ;<   %)#%//-  #!  !    #! mR@OA@'/U3  \ <  b U  M  >db`]ZXMJd#$6(3 +4'&#"#"''&'&#'"'&57532326;"672436'&#'&575323!7#"3&#76;6767>76 'u+! ()  N+ h 7> j=');\>-  7 ! //- #!   #   #% }%'!pX@UJA@'/qWT3  <  b U  M  >wtgec_]ZNLt#$6(3+4'&#"#"''&'&#'"'&57532326;"672436'&#'&575323)7#"32&#!76;6767>76%&'&+"36767>7> 'u+! ()  N+ h 7> j;' '=]8>-   '')- 7 ! //- #!   #   #% !''!  }!  ^d@aka`G=<:OxuS)<b  U M  >}{oljb^\$6(=.3+&'&+"3676767>%4'&#"#"''&'&#'"'&57532326;"672436'&#'&575323)7#"3&#)76;6767>76%4'&+"36767676  )q' (r/  'u+! ()  N+ h 7> j>' '>_e\>-  &z')y- 7 }!  !  ! //- #!   #   #% }!%'!  } !5\#K-PX@#yx][ZNkfcb* 2/<@*yx][ZNb kf*  2/@)  I    UM >Y@#{wtsra^YX+2!F-K1)+.'&'&/*#"372767673#'&#!7637276767654'&+&'76723273#"67676'#&#'"'5?323273#"32#"&#"#574656372725656'&'` /#)   'q#'%i +Ejj =%  ); ՓQ PR ' %C 8  5'  1  PN#=   ) !'%!  '!   I!! !o !! %g#K-PX@d[X2qQLGC% }| <@!d[X2CqQLG% }| @&I  U M >Y@~{zkhcba\WT5U5V'+%30#"&#"#?676372767>7&'&'&/"'&?53232730#"#67674'&#'"'4?5323273#"2#"&#"#?65637272574/&'} 'G {1  1'# }  /  JR $ %C = 6)#  1 JN#%  !!   !  !! !J"  !!  n !!  f#Da@^wtNmhc_A! )(<  UM >~xspa`^[U5U.=+%36767>7654'&'" 3#"&#"#?676372767>7&'&'&/"'&?53232730#"#67674'&#'"'4?5323%7+"3&#!"#?65637272574/&'/ m+  ')#J 'G {1  1'# }  /  JR % %C  ='  *;\JN#!    o%  !!   !  !! !I"  !% '}%!  g#K1PX@wtNʭmhc_A  )'<@"wtN_ ʭmhcA   )'@*   Z   UM >Y@*xspa`^[VUTOJI:7210+%#3 +4'&'#"3676767630#"&#"#?676372767>7&'&'&/"'&?53232730#"#67674'&#'"'4?5323%!7+"3&#)"#?65637272574/&'36767>7654'&'" )'  *+  'G {1  1'# }  /  JR % %C ='  );]XJN#w m+  ')#;  '} !Z%  !!   !  !! !I"  !% '}%!  g!    o/=@B@? )4<bZUK >!36&5SF +>54'&#'#"?323273#"!2767675;#!#5763037676767 '; I >%'+'B2%hf =-  !  H 2T`#  ! K PX@bUM >@bUM>YY@  +"&732!"3267F!3$?'?w>kۉ)PZq V/fŅb˦ki{)m5Q@ 5@ZUM >Y93<")"+327>76&'.'"637676767654'&/"?3!2#!#5=Vkqv#*Z?pM=+  '>Z5-{XLLbH?  !!  !Vw\VKr@J)#"@  U K = >Y@KKIF>;3*(2#'# +!#3�7637276767'"' 3&#7637276764.#'"57h8V Z (;^ >% K)<qL@' : E'Z # '' /N    ''@.'7/6@3(<UK=K >334$33+>2"6&+"?37#";2&#'76;267CR-FRh 1'J )Vq 2&Q'1/ hR:<')9--''Z/+''+/7 Uj\@Y(-O= <UK= M  >ZVe`VjZjTQNKJHGDC?:7321E5"+>32"& >2"654+"745?32?227?#";2&#""'"+'&#'76;26;267654+" D')/DR/CR-FRqJ'JAh  f: 'XpI'P:f  i@'1/1/sK Vq)::R99R:<')9dJ8 ''Z: ''+++/J;Z97 &9Bp@mT ` <9  U  K  =M >~}|{vutsolieb_[YUSRQPOKHEBATT%TS"+>32"6&+";267>32"&6&+";267>2"6&+"?32?2277276737#";2&#"#0#&#""'"'&#'76;267F')/DRk 1##Xq 2""10 C')/DQ/< 2Vq 21- CR-FRh 1'J@o n8Fz w?'Xq 1'P@x zE8n  q>'1/ hR:<')9--Z/++/)::R99u--Z/++/R:<')9--''Z/+''+/7H[@60& T K PX@)U K=  K = >@)U K=  K =  >YY@YVSPMJFB?=#3;31c3+>2"6&+"?32723737#7676&+"'?37""'&+";2&#'76;267CR-FRh 1'JI{ _:"= Z5 '3 uK`B^ #_*Vq 2&Q'1/ hR:<')9--''! /B_X%)%jJ/+bZ/+''+/s0|@ *K PX@K= >@K=>YY@%" 00 +#"?307""'&#"&?37#7676&D uL^A^!^ mE#; Z6 %7)%jJ/+b)'! /B_X'\@=B) U K PX@*U K = K = >@*U K = K =>YY@ZWTQNKGDA><:C;34###+>2"6&#""'&#"&?37#7676&+"?32?737#";2&#'76;267CR-DQi 1]A^!^ mE#; Z6 %3 uL+Z0'Vs1'Q '1/ hR:<')9--jJ/+b)'! /B_X')'Z/+''+/#@dZ i; EK PX@5 U  M  =K = >@5 U  M  =K =>YY@-}|xurnkhecba`_[YXWVUQNK@=:7##4%SR+>2"6+";267>32"&6&+""'&#"&?37#7676&+"?32?22732?22737#";2&#""#0#'&#'76;267DR- CRiW XqX 1/ C')/DR.:1#^A^!^ mE#; Z6 %3 uL(? s=Aw u:'Xs1'PK PX@<  U  K = M = >@<  U  K = M =  >YY@;~}ywtsoli^[XUQOLJGDA@%TT%SS"!+>32"6&+";267>32"&6&+";267>32"6&+""'&#"&?37#7676&+"?32?22732?2277276737#";2&#"#0#&#""'"+'&#'76;267!F')/CRk 1##XqX"#1/ C')/DR.:1Xp 11- E')-FRh 1^A^!^ mE#; Z6 %3 uL!?  u7@o n8Fz w?'Xq 1'P@x zE8n  q>'1/ hR::R9--ZZ+/)::R99u--Z/++/R:<')9--jJ/+b)'! /B_X')'Z/+''+/7lul@iiP+wvvwutpoh`]ZUROLJGFB?<0-*'#! lk +"?6'&+"?37"32&#'76;26./&;2&#""'"'&#'76;2676&+"?327377>2"26?6'.+"3/X J0 ! b>7LJ 1:7 mGX8 hD/@  x='1/ q 1'J>|CR-FR)-XH 5#*Vq 270S''/J \,'' "!#@ ''+/J--'%1R:<')9 -L3/#Z/+-os_U@R\H+<b  K = K  >[YUSLJGDA>870-*'#! __ +"?6'&+"?37"32&#'76;26./."32&#'7632?6'.#"&?731X J0 `@ 7LJ /9: oEX : hDVw #91   70S''/J \,'' "!# B '#yH='%-oxz@wlX;<  bUK= K  >}zxwsrkiec\ZWTQNHG@=:7632.+($! oo +"?6'&+"?32?227?#";2&#""''&#'76;26./."32&#'7632?6'.#"&?7>2"6+";26731X J0 `@5F  n='Vs1'Q8\ "J+X : hDVw #91   CR-DQiX 7LJ /9:1/ 70S''Z/+'' "!# B '#yH='%1R:<')9Z/J \,+/-@$)mP <  bU !M=  K  >~zxqolifc]\UROLKHGDCA@?>=<852.+(%#"!  " +"?6'&+"?32?22732?22737#";2&#""'"+'&""''&#'76;26./."32&#'7632?6'.#"&?7>2"6+";267>32"6&+";26731X J0 `@3L u:=v t7'Xq 1'P=n  xv^ R'X : hDVw #91   DR- CRiW%7LJ /9:1/ 3F')/CRk 1Xs110 70S''Z/+'' "!# B '#yH='%1R:<')9Z/J \,+/R::R9--Z/++/+q!^@!b`=L >Y@ 333+76&#"'727673;2&#'76;26!- #o=6- 10P/1/X= 3 /+''+{K PX@bM=M >@bM=M>YY$%$$!+%#"&7632#"&7<&#"3275')7!%+77f #{y} {V+/%%#E4ɤ\ / @+ ,K PX@1bb= =M=M  >K$PX@1bb= =M=M >@3b`b=M=M >YYY@ /.$%"%" +%&#"327"&7676326&#"5727673327#f5}yZR#%u_O{%-`J= D#n=6-#F!:/yg^8{-:V? 3 `V:')/b[@X< I; \-<  b  ` M  = L >a^[XURLJGECBA?&333$337"+%6'";2&#'76;2676#";2&#'76;2676.#"7676;3>32632;2&#'76;26Z%=z\R 2&Q'1/ Z%ZbW' P'1/d +-G')fXPVˠVPR1'P'10׼DR))T/+''+/Z''+/98 9#ZXfJZ_V/+''+ +%9NK(PX@M =M >@UM >Y@97.,   +) >$3 6.#"676'&#"36sߒh3 9V:|dVXy#d#'VPbB  HZmϟ%C^u+9Vw{u7J-uه! [$511 4C{vH#+7@w@ @8K-PX@UM >@!ZZUM >YYWB56%++'3 6.#"6;2676&+"?63 #"&#67676'&'Z@vm  G#bZڝX\ ;>98 "@9'N8~Ƕ^{eM3 b'Lwo-[3wx1Q -27()kZ!?mN\i + )Fc\@ bONF*)@UM >Y@^\USB@75  +) >$7>76.'#.776?6'&#"3276?' 6.#"sߒh3 9V:F;lN %+ o3A T=d3+C ?emџ%E^wb 3Xsj3 Xy!dE]zBbB E+9Vw{u7J-Td+k)V1;!1 \3qAj+!'Ѧ  N5E{tF1o+Xb=o1Ϙf>5N ^K PX@bUN >@bUN>YY@ + 732>76.# '676$325X qv@$%#$"+?32676&#"#".7>32#"LN%!enZTSŚ%%ݺXHD ) Rü  "+3!!#&'&'7676P+Z1a#%+!RBr RBk7^k/`R`/N3/\_7"+&'&'#7676737HP1LkkBh^k1dN0?=DP#;'H 5<3-R!X{Twt  "+%#6767!7!&'&'3P;J1a"-!%RHo TTX7^k?P\W+N3/^Rb=Z"+736767#&'&b=X5HjkL\^j=dN/@?N?+D#;43-R'X{Twy#"+&'&'3#67!#&'&'767673/) PVdbO h8TxV2R!Dp RN+[+fTX@fD_X/4M7jm9P1/\\Bk5`\V!# "+767673&'67#&'&'7bNqPuy-/>+P9fJ/ >;qBS'VD*=!O{u}NyZ(RfkymbR%\wTtu}P?RD/"+'6654'732+J}}C\D0'?-. )D\mT:$KHlVm;9 =2=D % B{ "+&'47'"'&'732767C 9JfLhH@fw=277HrBy#K):3!\-o#"+'676776767&'&'"+ZoLtD\+/:-/ +DTsV9L%Pdb^99-A=D!';3w#"+72&#"'676F :N``VG@hy7877HrBxL% 94!NL #"+!!+7!#&'&'767673!7Z zk%+!RBr RP+Z1a{ mkg/`R`/N3/\^Bk7^L #"+!&'&'3#6767!+7!7!7-!%RHo TP;J1aKZ z-{ m?P\W+N3/^RTX7^gkX)!$ "+!77$73&'67#.'7))?k-9:W:JTON-twddVEOb=>RdmmdR 0?PTb-TyPNL0"+!&'&'3#67!+7!#&'&'767673!7/) PVdbO h8TX Z{vV2R!Dp RN+[+f{ mX@fD_X/4M7jgm9P1/\\Bk5`3-   "+767673!3wnmR/H3NHpkT "+%!#&'&'wR1P`TP\RkuΏTP13-  "+7!&'3?R1PfNP\/kuΚIP1T  "+#67!7wnyRuRH3NPpk   "+767673!#67!79wnyR7urwPt/H3NPpkH3NPpk   "+%!#&'&'7!&'3wR1P`TP\?R1PfNP\RkuΏTP1%kuΚIP1L*"+!!!!!+7!#&'&'767673!75T;'=XZx RBr RPj=5H5TV #hm#F?/N3/\^N  ; # "+!67&')!!&53#67!+7!#&'&'767673!7VH+'7V:(>D+PVf^P o)QA)Z RHn ^PjV/;H7J4SR#]T#//P+-^;N)N1/#T^Lw/!"+!67&'!!&'3#07>7!+7!7%!7%7ZV3;'PRHn TP&Z'R+5TL )1U+N3/^R0kj "+!!!#&'&'7676735T;'' RBr RP h5H5TmF?/N3/\^JL"+ #&'#767673"jl#ALZmX)j?^T/@=q51KL5*R+TRwyyPw "+!7%67&'!7%&'3#07>{/V3;'RHn TP'1kR+5Tj1U+N3/^R/dJ+"+7367367#&'&'72^j13DbhmGL7dN/@;s-@G:'"R#X{TvoP  )'"+!67&'%!&53#67!#&'&'7676737V:(VH+'R+PVf^P o)QA RHn ^P7J4SV/;Hf]T#//P+-^;N)N1/#T^=u )""+&'67 63#&'&'7767673&33G_F"LRwr4/9.J1?7`?dN/@=o)%/?D=9F98{JPywuUw\(R!f%R+ZyP{u{O9 "+ '6654'7327"P7\J^+R_)+-4 )BXoV-+?K^TgL#JA;33>5L % :h"+#"''&'732767&''61=1<B;H1m3<7C:` K#<3 H7Ic,ZV "+76767767&'&'"'2\a?h*^/)++- )HTtT-+/LRhPNL=9J=1=D!'9FZ{"+%632&#"'676?=1-BP;;AG1{957F'9+ RL; 2#P73b/L-  "+!#3!3 !F%ۚV6{- G "+".76>3 #&#"2733R])##yy? ]^ݓ_Lۜw- )#"+"327&'2>.#"#"&7>32#"&76$sYRqBDGS@o 90q2#!&V9mNq{d)J3HˠH "+!7!!7!!7ZRFm\#\/ "+!'7#73#7!!7!73![EM(ZlWFJJFnRm\\Xw`' ' "+"&&76327#"''%3276&'d'T`cug /wg=jeV1Zj>Ug\)BP=τCc9 QbH"T4%'%yF "+ !30#!"&747Xw  J1 "+%!0&543!3D%A /ىG !"+)"3!!"!!3!^6ì7+ ş@ZZts\/ & "!"+)"''7&76;73#3!3!%!#"MEfEf"/WEJ.U.3@}bh喬7D'Ytdsr )r{{Tu"+% !3#!!;\JLȦ'/u\{}}qodn/7!7!4.+7LH]ɦ&2tZunodo/+J8o3/; L=J=;7: +<7H+=; --' }+-%%X--%'#7+-%%)/3*+/"+.#!!2>7!727-J5->1bF%+'+' 5D !P\Ec? -''!^/+5"+!7+++5%!  "+#!7!3!!7!FdCcEeFbl+debe1g! "+>32#"&!!#!7!3E)'1D+-,.cDfFbHd1)<326&7>32>32#"#".'#"&7>(.RN+!'f#-B B+%D 1BS+ +## J7#?k!-%Z+w!.EY>J =3%"A{;"'5;o-  "+6&#"26%>2"&+ 7@=Z 9{\`!`^7_Z<9XZ9Tyyyy{X "+"&7>32N11 P-)7 N{?--DA0-?-  "+ '%73 kXf#%>Hybu P- ;$ "+ '%736#"#".7>32#"&7>3232676.#"7> kXfT%O 1uPP s;V|=t 'I1y 3<"%H`#%>Hybu PR%#;'h?:R9\Rh-%L\5@1 O- 5' "+ '%733#;2&'747326?!'73767632 kXf+qh! V9} 9F +#%>Hybu PHd )+fT +j} , $"+.#"326%6&#"3267#"#"&7>3232=!T^UfHQiF>w\}׌^iys/ufdq9jǕJ}hn"+3!!1eVJ#"+#3Zr[#s}# "+##33Z}T{[V#0:J#"+#3#3JZrZZr[#ss# "+##'33yTZOqZUTZ}Zb&MHtm"+ #3#BcHVcu"+%3#3eVcfs"+3#>32#>&#"eV:gsTdV^d&hH}υNjn"+3#"&4733267dT232TZ-6>%#1R@tP7:BBCXX!1'!/Dp=z7+#3H% "+>2">2"&?LZ3NZ JZ6 JZ3Z??Z@R/@@/-@@ "+>323267#"'&'Z/Z`)5-1O#9URr1hADDE dwDBRA"+>323267#"''&"'Z.TI0"5-1O#2;JD<391h<DE dw+b,RA)q  "+>327267#"'&#"!7!7)Z3X^+5-%ZT:7WX391fDBCFLj-ADTBTdddd%-" "+7>323267#"'&"'>323267#"'&")Z/\Z-5-/R#9TZ19/F'Z-_\+5-/R#5XR89fCBDEbwDBTA-fADDE dwDBQ%92"+7>327&"'>323267#"'3267#"'.#'&")Z&2NN89/'Z-_ J5-/R#',NP,5-/R#9TG$ 9/f ?RAfA DE dw:DEbwDyTA'/^   "+&''67!7!7't/5h#P}^ZqHZ_`-dddd/"+!!!'!7!7!7!/SUGJRJddl{ddB "+!7!7!7BZZZZZZB "+!!!!!'!7!7!7!7!7!BE_D9G,DuE^JZZZ+ZZZtRV  "+?!7\X``g%`!dV  "+?!%775``f`g;  "+7 7\V\Xg#b!df"c fq99  "+%77-77;fafd`gL "+%'%7%7 C)G"Luf}cDfvv\"+%'7%7'7V~pI0J`Mfwb>YgR"+?!7%7%7%!!'7 wLDh#FG8=`~`Rdag`kViR "+?377%7%7!!'7%'L%iL8 _FG8%(`Vgcf8z``kdOX}+ "+%!".7>3!! 3Kh-eoN5VNsmGV{`X8+ "+7!2#!7! 6&#h-emL{5VKsmEVyo} "+%!'7.7>;73### d}Hq<`^;/ eooIbU&5V#6RoImGV'~`oA "+7!37+'7#733 6&nJdDh_0em}Hp&U~{5V5R{SmEV'\} @  "+ 7632!6&>7!!!#%=J@@ &A 'G  /*L\}   "+ 7632%!6&#" 67#%=J: !+  ގŽ^} @  "+$ '&76 %%&$6''%9jmJvkU3b/=iVd/@ypSgeR`gf^}   "+$ '&76 $&$6'9jmJvEFw]\#HDyn[[Z n "+"&7>32R11R-)5NA-+DBZA ' " "+"&7>32!"&7>32%#"&547>3214 P.+5 L312 P-)7 NN-+1R-$4A-+DB--AA-+DB--An-A3' +D4& + "+! ! C}@BzFu"+ #3#-b!Vb+Gf5"+#!!omT%Z1"+37%2"&#" )Z+5=J1T;V1'!/Ay"+ #"&7>3232P79 ?#!DX;i=|7)%1HL<o"+'&/7672J#N7c5TuNj# *NM'm"+7'&'63"'6)J"N7b6TNj[ ׸VMNS3"+!7#"&733267j!ŧ9NggyyDdgAq"+!!pqq"+!%!!p+FZLq"+%!^L%ZLq"+%! ^uL%{"+&2"+ &{2urZHo"+!Z/oZLq"+! Z/uuq}Z"+_Z"+ {_t;J'"+ ;ooo;I&"+  no nd!) &  "+$7654'&#"7632"'4632#"&z}a{yy6ˋō}zVX{zYVz}{|x{ŋTX{{XV{{o"+ fi;=!d! "+$7654'&#"7632"'z}a{yy6ˋō}{|x{ŋd! "+67632"'dˋōŋd! "+0027654'&7632"'B{ypˋōh{{|ŋd! "+%050"#"'&76$B~yx|Ǐ|}ˌd!  "+0#0! 762#"'&'476{}XVˋŋϲ}{yˋÏd!  "+030!4'&#""'&'476 }yɍyy{oǏɉd!  "+$765!"7632"'z}a{Vy6ˋō}{x{ŋd! "+$7654'&#"$7632"'z}a{yFxˋō}{|VŋdB"+0"'&76BŏCu "+02ˈP  "+%!!%uo Pck#Azif_WG"+%4.'"632676>32&747'"&#"632#"32#"'32673&547#"&%#"'3>54&'27&#"#"&' >32737%%'&'."72654&"&547632>32#"&#3>54&'&'&'&#" 32>7676767674%#63232632#"&'"'&5472674'"'&=#"/#"&54;."32>54'62&5#"'>324>7#"'654#"326767#"'&54632763262674'''3%5Xb:DhJ1+Kd3R/7DQP#)=P5?5#)baJ-2L|: 681lBYn58B;=:Dc^_{G{Hyfm{q7Re/SO.r<TruR(V>=6fXXJA%#R`3F 5%BEF !&' #3' )fd>F=R$^F=B:f1M?<;-m !''!LD~=*67' sF;ZN-'56X!-%5!5  #\w!'Pr!)I+13) \' 9J1.30-939# 4PDT 9+ '%9;0= !!?;7o!$ +/C6/? --% B+#l#'#- +!!#1^EM GF:+"+327#0#%5&54675%632&'"%#"'.'&547327>32432#t. H/-<)=Fh]5L+Dυ?ZJ^eXZL '#Th/ )r97=0)-;=BmZk$1H)$5>1X,9`d@gdbI?6.# "+"32$54'#"&%4!"#; 2654#74#"326'2654&#"#237&547#"75#"'5327'7%726323 +#"'5#".##%{!?KN5L;_'!b5{FH-J'.5m';4"#PCI7|GDa>N q7$HK\3YSb#5VH9=jJw^)V%F3fR')%^DZ1XR1Hg5#+-1h`FA)/  3gMPR}+1+L q =Pn#)6%%!?#!5HZ:+1+c Fm- ^ "+32654.#""4#"#"=27632#"'6?654&=737+";2&#'54;254/;2&#'54;2=;2&#'54;2?675&'%&+"7577-'{'NC50-gR:ہLmH1-/)-Yt"#e`)T5Nq>NGJ1N1s'g`%;`Tj- 'RR % 7ZX0 %  H J  BTTP##Pk}'  J H  f $0 @ *% "+3264&#"462"2654#"32 532/!/0 /B//B{%m<\Xa^TB//B-fA--A/ͤx:Nv:\Z}(08Y@B951/+&!"+7/232'"'#"&5''>4632#"$462"  6 5467&'73'723>7325  V! $?%F#6E))GG͛-P3)5// # 4-^XV'9F#V '#-)--)H ! w-= `D)}"*2P@I3/+)% "+%7'0#0527#"&5'&'74632#"$462"  6 5467&'6732?3675 oC)1X?%R>,!E))GGǙ/+/ 05)3R0TO4hXDF#Dn9;-)--)H #9d3=-ϲw '}0BF@ FC:4) "+264&"3264&#" 5467&'6732637367%"&'32?67' )!!) hǙ5+/05)3R0/;F ,=S%?Z1)Cq b----o #;b3=-ϲw ';9oD#FDWh8I;&"+"3267654&#7&''7&547#7367'76?373#'# ! R'R-1+uVEY}%R%V0/1oXF\͑/!wΐ-#u;Uq9sTh'Top7&5473326733##7#73"3267654&3 } fngmPV '9?a6'# #169T/b`X6-`31^^͏/#uΒ#+uf*%"+"3267654&.5476323##7#73)# # +Bè '9?a6͏/#uΒ#+u16ʔ31^^d*%"+%267654&#"#"&5476?#73733#D# ! t +ê ';B^37͒/"uϑ-!w51˓32__!1)""+#"&547632'>7'&547"3267654&Z) +AP/V+כ'X}# ! 13 ^%E-TTsZ5BFV3ϒ-#uϏ-#uV?)"+"#"&547>32!257'6'"=7>;27>7674&}LE 3'%ʉsr!Rj+y }VX  ' TT^<7++!^HM1 7 ! @% + !%ŃXXfH7"+732+>32'&7476767654&#"'#"7>;f/+-N5Ib ^)RF o G-w )w47Xf^2uă#'VRL\-#o 9P?%%"FuIYVN5 "+654+"?37373307#"32&###"&54767##'76;273267654&#"` H9bT-B Tb:qaJ:_hmRFh!i\:o0&-J+)/L ='Xd'Z='>b XbMB%m%Z '/D7#1D-=%"+#7#73?$47654#"'7654#"'&#"$27&#"3`?c8!m'BA))# UDNG3QJ!"D'^^)mBi`R%RL!'dB;^d}Z$ "+674&+?> %''%32gHg-q'3Tf$g-#LKH# J +wm)#!44"+#"'&747>3 3>>2'67674&#" hmnJw #1k !{5-]с v4fw >9L7}d11T\`T17}ʼnk!-~3!FLG@f .>6/&"+./&#7323276?673#"#"&547>2$7654&#"FH#5i:L%(:9qmOTTZ7jyBNtX /ʬ!ϧ# % !ZkRalo+++-mlaRda!w3:ߤ+=ۚ3%}٠#3}Rw "+%62#"'&'7! %$ 76)fbVuxSuvymヅ`V # +cJJc-&`NN`(;1'@P @ HA1("+2! %?!27&547>"3267654&"&547>7$! $!"'267654&#"o)59>ۜl^`NX^ o g33 5ݜt\bL\^)f-+D;N#!TZH`y^J^k%`E'F7654&#"#"&547>32&5476$32"3267654&3)u%bw1!bk# {51o3!7 'T DX\LVZB37f'iVV- {{d@`]XN'Ņm+#fR!-̒-#}ZJX{\H^oDENLF5"+%'654''654'?06?6?6?&''6767654'6674'ђ ja1%Rl7@L?uJ4PH1JaB+{ q?yY@%S3F@%FH;NGJt%^%a uS!݃K#u~PL/-\gX=C+L9\^L/)g BIH9su ="+7"505>3!2#"547>3!&7476$32!2#!7674654&#"-1-CB#X/!#'sVhR#u 0#7^3VOڞ})"ho 8f;, Vyk#d4fF! "+?6?6?32?'73274/&547654''654''674\`0'Vg-Ly') )+)/9Fhh^ZiZJo''}qN`B;1HB&4D # JZx=OZJ9c@%D1%H@%7VHPTf"+&547'''7"'772T] !gC;d{1wh$hdT5+Dh]RRgR)V%y;GE@."+'654'.'7?>32#"'+73267&547&654&#"26q7]- ,9Hl  9Pdfs yRJN8j3!!?32%>%672'&"0"/&#"0"/&#"672%672%>'&"#"/&#"#"/&#"g+ b%  xhb ^ `jd+d%yib \ ^ Ϯ  7    7  (("+&547#'#73654'73%73#!œ?F GV^MRTVMSƅ  >^:;98% ^TVL}""+"!5674&#"#"&77#".h?dh%2+iJXhdm^Jf+/u++?@>K>ouju>K>qA!"+%!5674#"#"&54>323254.5463232632#".#"o/1`#X\s)dL%FVEGTHKRs1v;eD51  L{ ++w\_yBzkg#75oG`gHp57iDhV'y'67'7P "+2'&'&5463232>Ry7HTLPqVD--FŔJ}\NuȞ767674654#"7672#"?>"67>32"547>7>32'7;/#"&#"2767#""&5476!2>7654'7#"  CfS<\%#7LJ7 +/B,'  8+?[ :'?  J=fx8+ # /  %,Z32>7"7674.'.54763"3267654A;JRBZ}J?Rj +5H )!-B /Y 5'-\ P<PpRAPf 懰Pg!M-m))X93H%@ #5 !L3 );E:Zf /] @ I7% "+67654'3267654&#""327&54767&.5476326323##7#737&'3##7#73b b [Nn! oh1 '# oj1 %I +BÜ_ '9@`5/sMuy9?a6\#+P;bw/#L?Β-!u=Ri31͏/#u?Tf16{=16ZZʔ31^^HJ^^J,Zl@ b[F/% "+&547632'>74653267674&#"%#"&'#"&547632'>767'&547"32767>7.+ )4N1V+^\ u ##))5 +AP/V+כ''X# %  !`X5BFNZ)H-b -b fhϒ-"u%>13 ^%E-TTZbj7{} df5DDT;Fϒ-#u&5d <7!"+"3267654&.547632'>7'&7473##7#73# }# +@H7V-י'X)ߪ3/`% Ȓ-%uӑ')sƉ7+Z)G-TT}sZ3DCX/D`` Pck#Azie_WG"+267#"'4654'#"'254'727#"'4326?67&'.#"'432&#"654&#672472.#654&%&#"32632 #"'.'&'.54767#57'5&54>767672>7&#"27"'47&'#"&546323&54>7#6727 &"&'.'3&="&#"#"&5463267&5432'#"&=632632&54632+27654&#""&54654'672#"'674&546323254'&#"32#"547#"'#"'546&'3%5Yb:hJ1+Ld4R/7DRP#(=Q5?6%)b`J-1L|.9 591mAX57B;=9F^^{HHyfm{q7Re/SO.s;TrtR)V=>5fYXJA%#R`3F5%A FF !''!#3' )ed>F=R%^==B9e2M?=;-m !&' LD~=*58' sF;ZN-'56X!-%5!5  #]w!'Pr!(J+14) ]' 9J0-3/-91>N-%7R#%# ?!u-/IbPC')f B1-;+\!/!;+HN}y%'B? :W5RaN)+t97-')oCF/B!2-Eh@l, QG+-J\ Be>716fh}%F7# =\"6+( 9m- 3XK~'D19D^;;4!\#d!TXD2,99kd"-+6%u'!=9# 4PDS 9+ '%9;0= " !?;7o!$ +/D5/? --% B+#l$'"- +!!#qf  "+ # !!!q\u-qjX[f "+3#%!!7!!ot\rn}p-\jX[jo"+'&/7672J#N7c5TuNj# *NM'm"+7'&'63"'6)J"N7b6TNj[ ׸VMNS#5+;@4 ( @2Z  US K  =K >Y@;;9630-*'&3d4+!!!;267!'"#'74;267#737#7354+"?3%+"\?GP`-5b)w\>?/ N5`8P 5q3oPPjNs %%7PP)B''Z+ K PX@2S =  M =K =M >K$PX@2S =  M =K =M>@0   US =K =M>YYY@++*)('$#" #$+#3#327#"547#737#73654#""547273N^)VPH ); -oPPwq!]1{\PP17;!65+E@ %12,9CB @, ZUU U K  >Y@@=86/-*'$! ED +%"#'74;267&#"'>3254+"?3%+"3267#"';267!+)w\>?/ b9/%Z=`8P 5qN?'5-#YR:)8IP`-5b%%7.TBf]B''ZK)DFLj- TNs #;3C]@Z+"7  <9  S  U M =K >444C4CA?:8CS345% +#"';2&#'74;27#73754#"#"=72$32#32>54#"!Rx{Z@Ob5R6qL =wsR#w/%jsIXV)1ukE;B ''Z PD )5Z\bP T{@=;BP@5 :K(PX@.b  UQ M =M >@4 Zb  UQ M =M >YY@PNLJ"+C,'#% +%3267632#"'&'4&547654+"?6763 ;#"&'&#%67654'&#"36-!!-9%!sC\H? !1 @5ô3[BLI5\+1Vu V1?sDvhq ^!%-YV! q/% hJj'#u3])`P`w#/Z9Ho18@82" *'K PX@(dbeM=M >@(dbeM=M>YY@ %%%)(!+%3267>5 &#"327"547#"'#7&5432?3s#C;1' 'VЛ^'N/HDV#TJ7-dRPb5e %AR9`#LZThk^ qh';]Ft}R5):RJq#&@ & <:K PX@!deM=M>K PX@!deM=M >@!deM=M>YY@ $%+ 327#"547##7276?373#%9s1hDdTNws3% ILTmm7%yy+}Z_%(P91')^  o+YK1PX@ F/<@ F/K1PX@-   U  SCM >@.   U  SOM >YY@#YYWVSPKHEDCA>;7641.-#53#43+0/76;27!32�'76;27654'#"?30%+"!654'#"?30%+";2#.5qgbga7P5q`5 P5qPPb6P 7q=!/)'Z =''Z ?''ZZ?''Z) /5%IK1PX@ 4<@ 4 K1PX@6  b  `  D = M = N >@7  b  ` O = M =N >YY@IIGFA>7532/.3#5%3+%0'76;27674#"32�'76;27654#"?27673632;2#.'V`cmtH'P&Z )D #oB2.Ӻb >! 7)i'ZR-u5''Z 1!9 3E?dQ%+7++_@2O9 [@$  UC M >Y@__]\VSKHFCA>74#53#;2+%076;27054'32�'76;27654'#"?30%+"676&+"?37+" 32#.b +-!Ra7P5q`5 P5qbH ! ^J/!-N%!1 0)o- 2u` =''Z ?''Z3 ++9z77%Z@< I2  @7  b  `D =  K = N >Y@ZZVUPMEB@=;8103#;2+%076;27654'32�'76;27654#"?27673676.+"5737#"32#4./  '13H'P&Z )D #oB2.T' H ;#/)R%! 7)%F- #Mt7''Z 1; 3-9--8T;FV9X5V/@<:K PX@YM =M >K(PX@eM =M >@eUM >YY@-,)% // +)"50567676&#!"'3!27!32#.# d6y64f-=;5#/)uT\A1H5#g@ <:K PX@YM=M >@eM=M >Y@!  ## +)"70747!"'3!2!2#.)bD7c53/#/ 9)_!Pw 8  373Mm@ GEDB$#@M=K=>Y@>=8530.,MM +">7654&'2767654'#"?37#'&5'67&547>R%D 1-' 5<3Y//$=#!3!  jD !$-;E17Zm- ^G:3!%"!5- #GJ%+mf3-) +)>-)).#3$="='%3Nn+?}@ :&@*b UU=K >Y@ ?<35334$&%! +!26?6732#"=4&#!32&#'76;27654'#"?3%+"XR!31 I 3L%1ia7P5q`5 P5qV+/= '@/+ =''Z ?'';sBL@I< <bbUM=  K >A>;84%'%"3#3 +654+"?307#"326?6732#"5754&+32&#'76;27sJ'H 'V534JL%-DH'Q 'Z5''Z+1; >^%;? )%7''Z#+6V,K-PX@M=>@eM>YY53%# +>767>7632#".546767654'"StF8 (J;35tDEJ^ }=B{{Z9f%NX^++#V\V8oSRw:977632&542674&#"2#"'32?654'.#"2772>767>54.'&'.#"#"'&'&'"327654'&'.'#"467'#"3274&5432654'.5.'#"'&"#"'&'.54654&5476727>7&'476767>7654&5322#"'& g   8!) !7&)*#J'/7% S 1'.!' MqC=7T%;T3 !u /;Bn'G>!T#O3  [+= -aQh#;,$F K4!1'5 *B@I!$J>)-'ol75(5R=;< %-'s?'L/o(Z2'-+)-*  e!J\A  71LT  >+I  f1-  - X'9) 5`!' - PBTb s75_ 3 R F-+ -)14')dP3#A=' CC# /P `BF " G ^'+]F!Hh !*32A`/ J 'B%B!=$ 71%&1)/!)%1- q 3DX +.-\!'=;;?\7G( 7- GZ 2    (E0'W! sOV1lP/:\T;^D1$0!  ?L '`mea2"+"&'".547>32#"3274>763232654&#">32#"'#"&54732654'&#"632'2654"߲-9g`9/ +1JTCoP+݋V1;e5+!+!l@Xdm{s/!?P 4''1{޼eɔMqh<`Jw/^@uS5w^5##J=qÃGZ7235PX1HHn`qn#+ 87!Vm8\PZ`8H{LN>do}wqJ"+'.7>676'&'&'&'&'&'.7>.'67>.'&767.'.67>&676'.Tq(()EB 1O3'3EjC'sk}L&M^H-.z&.>9Q<#!nZc/dž0[ /MPH5+'@$APʪsZa7EVvtk T^6U*'1-AGY/j8#6q)-  7` SI3 '+; Od.9 1Jl= !G& N_P $ ]aX#BL Z/)Ed,tQ9g ;^je_-"+".54632&'&'"32>7#"&547"'>7.4632&#"327623254&#"&54632#"''27675&#"%?jݕ}3%(4 PB#3o{jgZ=o!+#+3d32!"$#"! $#"!"$ ,32 ! ,32#"'.#"! ,#" ! %.54632 y! q "~h3!]-5!Xoi)T}J!  !7'ju+@+?IN!x8˶ s TǑf!i 3FH3JH-8-Ѱ^^GXHDTC! ooHXH:G:YVGH-5-n . }`'!XCmdeedFE-N ;2edde>!#P+:"+327#"7!''54;2654+"=73%+";26=732#'HJ$u>?)o7P5q3-ZVu/ydX9%'#8Z''Z"/7V`') ) #P Z:E[ 2>[;AZF;5 }jNH@; "+327#"7!''54;2654+"=73%+";26=732#3254&#"#"&54632#!32%4&#";2&#'54;254"'676;3632;2&#'54;2462"&;2&#'54;254#"'676;4#"'676733632#"'&#";2&#'547323274&#"'"&54632#!32%32654&#"632#"''654#"5'27674632#"&;2&#'5473254#"'67673'HJ$u>?)o7P5q3-ZVu/?)!/'=R}wy^oZmDZ#-RR6 N1 5 R-\mN76 T/y 5-#1##1#w5 T1 5!R/ L3 NAF5! /5% `/3 >)1%>Pwx^mZjFX9T\Q:J3BVT}fN1%%'='t#"##u5 T/3P/ ydX9%'#8Z''Z"/7V`') )NAHon^\% F9]5  54L!  mmd_5  "###5  51L#  R1L#  oo:1 B5  7NBHmoa\'n\|6Dl/+EgX ##""5  53J   7 1AN[jy@woe`XRLG?5/' "+"'./472632#47632"&&'&'4632#&47632'"%4&54632'54632#"&%&54632#".'&74632'%4767632"4767>7632#"&'&5432#" !))!3%  % !$ '  #{#'}'%'  '  ##%  '1 )%#;#1#)   %; #!=)%)FT )NT +L- #% %:A   #'F/&0 +'# "+327654&#"3>32#"'  "32rFPRy-G!+%;@7Lj/!TR--3GVX} b.J1j{GXTR!V--F//A @ 61 "+$  %32767>54#"672#.#"/327''"'&54y!TR2a qVc|m7>&#"&76$762 .  OM )-A&&  W|! >>JC$!($7&  4  3 %E121 3:{!#uls33"+632!2672!&676?>54&#"#"&54tt6U&unJL"B;5-&I;.ݨYK`Eo! M.+17s 8;'B Q#jeDU2gs 0t[jM_@\-.7%s < "+"#"&547632!"&'&74632327>54&#"&767>54xL "X%7jNzB!nycjKJF/.90O$U4pI($K>(qN]Df$8,&AO1MO$e@"XjI'.*AK7Z1XPkt9&-td:42"+)2+.#"&67>?!"/.7676673}c. .# =J"M 24@' 2  #[U4!,R*='  4  3  "K>R% )Tf2!"+4'./32>7"'#"'&746323276Հ,9"-)U8t#'J-d/ 8S_AfZ:<32H=\;Z(P   y_ (%nzW;99%7-C.!&6Dhc.-! "+3267>54&#"67#".547>75\.#Q)JA?R"ta\AUO@e?xf>ʟ\jm00.PQt?Ey54&'"&54767'&'4763227>54&/^%.NU7J>If]k\_˜ΊZ#}Ύ/kA!01<\{PX)3r]15 R@PEDNlDhwS?hmnp[FZI&|C.K)@OuD[7.-! "+6'4&#"3667>7.5467>32& 5[.#R)JB?R"t`\AVN@e?xe?ʟqjm00.OQuEx?#737#73&'.'&6732776&'&67327!#8{ j }N}?; wL P& f %F (-w ?U/  jVy; 2  1 9{VV=HRE 2  1 !'!!I) 2  1 5<(3(VP@ "+7327654#"'632#"742?`85>%#0d?Rycyc$RzBj?de:CxP!"+"727654&"&5467632Z?.^fJ&LKƁeFrDpUT7R<׃Bjg^˝Lt"J\KIFM'" "+%$#&67>764&#"&7$762 /!  3:I- ? D# =}D%! '#9% 65  #K>912 3!1}7"um^(%"+767263!67&+&?>76&#" (#-N|15C7uR+<f#s54&#"&767>54xL "X%7jNyC!nycjLIF/.90O$U4pI($K>(qN]D$8,&AO1MO$e@"XjI'.*AK7Z1XPkt9&-td:a42"+)2+.#"&67>?!"/.7676673}b. .# =~J"M 25@' 2  #[T4!,R*=&  4  4  "L=R%  )U$2!"+%4'./32>7"'#"'&746323276v,9"-)U8t#'J-d/ 8S_AfZ:;32I>\;Y8'P   y_ (%mzW;99%7-C.!&6DiQ.-! "+3267>54&#"67#".547>75\.#R)KA?R"t`\AVN@f?xe?ʟ\jm00.PQt?Ey54&'"&54767'&'4763227>54&/^%.NU7J>If]k\_˜ΊZ#}Ύ/kA!01<\{PX)3r]15 R@PEDNlDhwS?hmnp[FZI&|C.K)@OuD[G^.-! "+654&#"3667>7.5467>32&5\.#Q)JA?R"ta\AUO@e?xf>ʟjm00.OQuEx7273&#"&672>&7!"BbBBb=Db@@b +C  / u' 3;L%wJ %- x % -9bCCbBBbCCbB5 ^  2  1 =>[l  2  1  N#, @ & "+462"$462""32654&"56%2BbBBb=Db@@bѵ~:\xw )(cCCcAAcCCcAeqe {L 7& "+462"$462"32>764.'&67327# '&6&'&67327AcAAc>Cc??cd4R`\H!c 4(8 4 7+3 ]_m' 2  1  %E@/۬ZVu,Jy= 2  1 ; 9-b+"+&673276&'&67327 "'"&'&'&'&67327'.'&673277>& *-b Hd<7Z # #  Z%351J ' \+\+ j^ ;'m!" 0  / ^VRV 0  /   "+1-.- 0  / AXwXN1%B 0  / CX/:-/"n7"+%0!#"&546323267>76#"5476?7>32#"'.#"!7>7>3#32672'"7&}RC EUK)P(:RP{tZ;'  O L :%7)&t<IiSL"+%#"&54>76&#&7>326?>32#"'&#"3272#"&'.'&#"#"'&7632#"&76&#"272 -P72d {=*?Rj3?F?/&.JRkP87 .<+S=Z32 1J\T\--E J-/6"/N%#cā ?. !4f4(D6F^sK.7X6I^l9)%W#ZX`r-"!Vpjw{Z1A7)9Xk2%=]G@"+%#"&54>76&#&7>3263232672#"'&7654&#"#"'&7632#"&76&#"272 -P71e {=wHnN'Y$$FmL L#%2l!J\T\--E J-/6"/N%#cā ?. !4f4(DPT+p}7)'ZbC3GP'-Vpjw{Z1A7)9Xk2O8"+7#"5476?>7>!7>7>3#32672'"'47!32672'"'6} 2l #=A+ "=B+){X%$h}tzX%#h !f R' JZ \f R' \ L D%7)&t3_D%7)&t4\)"+%#"'&767&54>323#32672'"747#"5476?>76&#"#"&76&#"272^J\T\-( .z!{X%#h} 1my%[nX<6Fod J-/6"/N%#cā Vpjw @RvVQL\ L :%7)&t;W !f J*@lC9G*uM1A7)9Xk2- D3z {tL" "+327#"'$#"'>767&56%2632>067'#"&5476#""&'>323267>3232672#"'&74"32654&##Ox3͸mڔ&6:{F+j>Y )(ry  igU ZFpN'X$#Fl+- "L42osO ,P6jX%#N~:\xw >b1\mb+DL;F *%$ e $Pr&pSUSDF}7)'Zb!"4?ir? 9* %3@&7)'Cveqca<$"+%& &67>76&+""'6743)27>3263232672#"'&7654&#"#"&54>76&+"N8z  }N5ad33B( ==lHnN'Y%#FmL L#$2kH -P70f39{; 2  1 ;{^mbw 0CPT+p}7)'ZbC3GP'-?. !4~j\fkDD"+3# 3&#"&672>&7'!"&#"&67>7267-    % s &!)Z . u 3;N%!^   2  1    2  1 =>5 H I 5  "+3267654&#" 32>7674&#"&547>7654&'&5473263 ##"&3sXC::H1M@)7Fd7Rcq^HV6Go52jV ?!N`#6+# $B=)+?:1 %C{E%4! % 0ZG&7L%"+2#"'.#"327#"&547$#P  /T=K]VlLʼn 5WF=:L'TsYFh{:BH!1.&"+!"&547>7654&'&5473632!"&73267654&#"dq^HV6GoXnl ^&\BW+mo %C{E%4! %he7:9 #$ӘIjuCJB3"+267>"'6&+;2676&#!&67>76&'&67!272'.+"mE < ; ;f+<-!Rj7/!b^` oIZ1m -3/ #- Lh^#B+P  R=u D+)9u B 1 ;y{; 1  o!uf?%J="+!"&67>76&'&67!272'.+"3267>'6&+&ym oIZ1m -3/ #- Lh^#= 3/j@ ;  < 7d/#1B  1 ;y{; 1  o!uf='P  R=t  F(RV 2 2"+6&'&54737% 47632#"'.#"327PH9<1/!81qJ k)gxZ:-# # #oW;E'v dGDxJI"+6&'&67327&#"&67>?$&#"&67>76&'&673277X \o ^HV)^ o\ Z5))X Zq ^HV*\ o\ Z8? 1  1 D{Z? 2  1 =? 2  1 C{? 1  1 @J= "+6&'&6727&#"&67>7o*\ ݨ ^HV)^ oq ^H? 1  1 D{Z? 2  1 C{`h'"+%6&'&6727'"&7>3232>/k*\ ݨ ^HcjR?5o ]! 8)/-? 1  1 D{N/oK<'C)+>J1RH8"+!0'76'&'�&'&67327&6&'&6727&#"&67>7ۨ 1NV+y\3R _V !55375 4 *\ ݨ ^HV)^ oq ^H556f'R'bg=) 1  1  ' 3+' 2 ? 1  1 D{Z? 2  1 C{4#."+%2676&#!&67>76.'&6732>33\;35D: 119! [ +'& N#Ey@ 119! ]!FV}5  #K>3B'55 #K>D!@!"+ "'#&#"&67>76&'"'&673%&#"&67>7J' s'P NH3b =:%. sPN 1' EI `N P$Rq? 2  1 ;wN  1 3 1 !}5 2  1 1C}:8"+%.&#"&67>7.'"&?76&'&67327#&-  N!\ wDXv ^>s )4  A N$\ wUHw `<b!3"BT%3{3 /  / :{1 15 /  / 8{("+"3267654&#"&547632q0TZw)UO1 /VySieǙWf^h8A<J 2&"+327654#"&#"&67>76&'&6732$32#"'M+P( ~N)^ oq ^HV*\ o&@7u/`)!? 2  1 C{? 1 71ZT<."+"3267654&.54763232327#".#"'676q0TZw)U /V'ߢ d{DXr6jDPVu7'/+;#yRifǙWf^t 8A7654&'&54732$32#"&/&'&#"2>7654'+2Hq^HV6Go&s d-P {Nw R ! ZM9HT3+ ~RA(3! % %C{E%4! %vUT"j$`H) %F1)!w*%"+632676'&'&767>32'&#"#"&'&-# =e _/7R9Rd0!5Un/3J!? 71o9dLTRfL3<6bB+X>VOVu j-"+&"&67>76&#""'6743;272#.#"/s  uIR#V\U95PŠ˴ 86j?.K 2  1 IuGV m TAL5"+6&'&67327#".76&'&6732732>73a 3% bP6;huX{-G)^ oq ^J;FqRrD#{E 1  1 F{j{;Fk`? 1  1 D}Ս5jcI1"+&67327"'.'&6732776& J-V 3J)"M;5   cd T> 1m 1  1 039J/+ +   1  1 BJ^N>< "+76&'&67327"''"'&'&'&67327677T=/L W+V 5R)#q53Z71wF em V5 )1;!#wLQ 1  1 :9:[/+/+c 1  1 BJo  ZM"+%&#"&676?6'.#&67327?6'&67327&#"&67>/&;^ NVN Ry%/ NM =#7Lg NtXN wZ t3u NN 9 8H 2  1 Z  +%3 1  1 #8Z 1  1 a  2  1  4;"+%&#"&67>?6'.'&67327?6&'&673275w r }R}%:? wV P'\#,C =Bw FI/1m4 2  1 7jJZ<R1 1  1 69//-3 1  1 %87l9+)"+!2>32326?2%"&7677''676;R`M /L^7)97F9o\/%B @jDX? L s 3 "+!"5463!2jL@JA+Z+^hS S$ "+#"/7>323# 3&#"&672>&7'!"&#"&67>7267j)X1-    % s &!)Z . u 3;N%!d ),:^   2  1    2  1 =>5 h R R#"+632#"&7673# 3&#"&672>&7'!"&#"&67>7267 M!+' -    % s &!)Z . u 3;N%!d1/! J^   2  1    2  1 =>5 N O O "+&''673# 3&#"&672>&7'!"&#"&67>72675{9}FX.H-    % s &!)Z . u 3;N%!Np'SWO['_T^   2  1    2  1 =>5  ^ ^/"+272#"'&#""&7>323# 3&#"&672>&7'!"&#"&67>7267B/&u:L31A&%l4W9!-    % s &!)Z . u 3;N%!XXf+#%%Rl3^   2  1    2  1 =>5  [ @ [, "+>2#"&%>32#"&3# 3&#"&672>&7'!"&#"&67>7267BX:C,-7D+-9B-,94-    % s &!)Z . u 3;N%!0@A/.AA./AA/.AA:^   2  1    2  1 =>5  Y @ Y* "+"2676&>2#"3# 3&#"&672>&7'!"&#"&67>7267V.=5[=5 qeoJG-    % s &!)Z . u 3;N%!VC0-EE-1BdcGEei^   2  1    2  1 =>5 ^ ]#"+>&'3!"&#"&67>7!272'.+"3267>"'6&+;2676&#!&67>7'7%kJB x FZA>3/ #. Li\#A ++mC < =:f+;*!Tj7/!b^` oHTPG R+/ 2  1 7T o!uf?%P  R=)7 F))9w B 1 9{HL?#"+2#"'.#"327#".'6732676./7.547$#P  /T=K]VlLʼn$ d $I*/==0$ p 5WF=:L'TsYFh{@&Mk, +)% :BJhQB, "+#"/7>32267>"'6&+;2676&#!&67>76&'&67!272'.+"Ii)X0mE < ; ;f+<-!Rj7/!b^` oIZ1m -3/ #- Lh^#B+d ),P  R=u D+)9u B 1 ;y{; 1  o!uf?%Jh PA+"+632#"&767267>"'6&+;2676&#!&67>76&'&67!272'.+" M!+' 2mE < ; ;f+<-!Rj7/!b^` oIZ1m -3/ #- Lh^#B+d1/! P  R=u D+)9u B 1 ;y{; 1  o!uf?%JN M>("+&''67267>"'6&+;2676&#!&67>76&'&67!272'.+"5{9}FX.HmE < ; ;f+<-!Rj7/!b^` oIZ1m -3/ #- Lh^#B+Np'SWO['_P  R=u D+)9u B 1 ;y{; 1  o!uf?%J Y J4 "+>2#"&%>32#"&267>"'6&+;2676&#!&67>76&'&67!272'.+"BX:C,-7D+-8B-,9nmE < ; ;f+<-!Rj7/!b^` oIZ1m -3/ #- Lh^#B+0@A/.AA./AA/.AAP  R=u D+)9u B 1 ;y{; 1  o!uf?%J=h/% "+#"/7>326&'&6727&#"&67>7j)Y0*\ ݨ ^HV)^ oq ^Hd ),B? 1  1 D{Z? 2  1 C{Jxh .$"+632#"&7676&'&6727&#"&67>7 M!+( *\ ݨ ^HV)^ oq ^Hd1/! R? 1  1 D{Z? 2  1 C{J?N +!"+&''676&'&6727&#"&67>75{8}GX-G*\ ݨ ^HV)^ oq ^HNp'SWO['_\? 1  1 D{Z? 2  1 C{J{ 8 ."+>32#"&%>32#"&6&'&6727&#"&67>7B-,9C,-8D+-8B-,91*\ ݨ ^HV)^ oq ^H0@A/.AA./AA/.AAB? 1  1 D{Z? 2  1 C{H?1 "+&'3267654&#"6"&547>?&77654&'&5473632!"&w<3BW+mo32.&#"&67>7.'"&?76&'&67327#&B/&u:L31A&%l4W9!  N!\ wDXv ^>s )4  A N$\ wUHw `<b!3"XXf+#%%Rl3T%3{3 /  / :{1 15 /  / 8{(h. )! "+#"/7>32"3267654&#"&547632Ai)Y0Eq0TZw)UO1 /Vd ),RifǙWf^h8A<h - ( "+632#"&767"3267654&#"&547632 M!+' 1q0TZw)UO1 /Vd1/! ߸RifǙWf^h8A<N * % "+&''67"3267654&#"&547632p5|9}FX.Hq0TZw)UO1 /VNp'SWO['_+RifǙWf^h8A<)9 4,!"+272#"'&#""&7>32"3267654&#"&547632B/&u:L31A&%l4W9 q0TZw)UO1 /VXXf+#%%Rl3øRifǙWf^h8A< &6 @ 1)"+>32#"&%>32"&"3267654&#"&547632B-,9C+-8C,-8BX9q0TZw)UO1 /V0@A/.AA./AA/.AARifǙWf^h8A<yW"+%267654&#""&54763!272#"'.+";267>"'654&+;267632&#!PVbZ @Yu.RM /T/30 # Nh\#B++ bB = =AY+<*Rj8 #b_BCu;!7݉Rd^F6? o! uf?%!M  R=u . ".w B#) # "+"&#"''7&5476327%267654q0':1dxDse /VՈauDlt$yw)y}S#Gh39]8A,:y^<^ǙWLhD$ "+#"/7>326&'&67327#".76&'&6732732>7i)X03a 3% bP6;huX{-G)^ oq ^J;FqRrD#d ),@{E 1  1 F{j{;Fk`? 1  1 D}Ս5jcILh C#"+632#"&7676&'&67327#".76&'&6732732>7 M +'  3a 3% bP6;huX{-G)^ oq ^J;FqRrD#d1/! P{E 1  1 F{j{;Fk`? 1  1 D}Ս5jcILN @ "+&''676&'&67327#".76&'&6732732>75{9}FX.H3a 3% bP6;huX{-G)^ oq ^J;FqRrD#Np'SWO['_Z{E 1  1 F{j{;Fk`? 1  1 D}Ս5jcIL L ,"+>32#"&%>32"&6&'&67327#".76&'&6732732>7B-,9D+-8C,-8BX93a 3% bP6;huX{-G)^ oq ^J;FqRrD#0@A/.AA./AA/.AA@{E 1  1 F{j{;Fk`? 1  1 D}Ս5jcIh;IC="+%&#"&67>?6'.'&67327?6&'&67327632#"&7675w r }R}%:? wV P'\#,C =Bw FI/1 M!+( m4 2  1 7jJZ<R1 1  1 69//-3 1  1 %87l 1/! Hx B7"+327654#"654&'&547273632#"'&#"&547>7!+P) |N(26GݨPC\_@7s/6Goq^H-+" ?>E%4! % %*E71^}(5! % %C{;GR PJD>"+%&#"&67>?6'.'&67327?6&'&67327>32#"&%>32"&5w r }R}%:? wV P'\#,C =Bw FI/1B-,9C+-8D,-8BX:m4 2  1 7jJZ<R1 1  1 69//-3 1  1 %87lM0AA0.@@./BA0.@@H H7("+6&'&6727&#"&67>76&'&6727'"&7>3232>m*\ ݨ ^HV)^ oq ^Hk*\ ާ ^HcjR?5o ]! 8)/-? 1  1 D{Z? 2  1 C{n? 1  1 D{N/oK<'C)+>w*UP<%"+632676'&'&767>32'&#"#"&'&%632676'&'&767>32'&#"#"&'&-# =e _/7R9Rd0!5Un/3J!m-# =e _07R9Qe0!5Un/3J!? 71o9dLTRfL3<6bB+X>VOVu j 71o9dLTRfL3<6bB+X>VOVu jJ"+76$!23#"'&5476323267654#"5>54&# &#"&547>03NH5  ܊oK -E36- d6L$I,N6'8Fq^G+ ).m.2:G\E\f'"% k=V4 u % %Cq AD5Do gP=6"+6&'&67327#".76&'&6732732>7"32654&327# '&$#".'>?&56%2# 4` 3% bP5;huX{-G)^ nq ^J;FpRsD"۵~:\xwdoϘϾ;!l' 2/3\ )({E 1  1 F{j{;Fk`? 1  1 D}Ս5jcIaeq ( Z\p1ZlMDT7>)=WM(e {R Et lQ%"+"3267654&6&'&67327#".76&'&6732732>767'$'.#".'6767"#"&547632q0TZw)U3a 3% bP5;huX{-G)^ oq ^J;FpRsD"My4'˸ uo/_!ZdEK /V'yRifǙWf^{E 1  1 F{j{;Fk`? 1  1 D}Ս5jcIv=;N9/2bhXF/C)%b;*8A?&77654&'&5473632!"&w<3BW+mo&7'!"&#"&67>7267267#"&7-    % s &!)Z . u 3;N%!f!2+`lu^   2  1    2  1 =>5 }RgbW~5SZVT6"+"&7>7*#"&672>&7'!"&#"&67>7267 3"&'3273#VTbg &!)Z . u 3;N%!n  % 0_2Z9--  5^PI3 1    2  1 =>5   2 T135DEm^ Lh%3-'"+2#"'.#"327#"&547$632#"&767#P  /T=K]VlLʼn 5W M! +( F=:L'TsYFh{:B1/! Lh 0( "+#&'7672#"'.#"327#"&547$9g,6rGR,iP  /T=K]VlLʼn 5W;[$XTKa$F=:L'TsYFh{:BHh ,< 91 "+#&'767"&547>7654&'&5473632!"&73267654&#"bg,7rFR,q^HV6GoXnl ^&\BW+mo;[$XTKa$X %C{E%4! %he7:9 #$ӘIjuCJ5V"+"&7>7"#!&67>76&'&67!272'.+"3267>"'6&+;2676&#327Tbg` oIZ1m -3/ #- Lh^#B++mE < ; ;f+<-!Rj7/!b%^1HO-5^PI3 1 ;y{; 1  o!uf?%P  R=u D+)9u BT1HJDEJh M>("+#&'767267>"'6&+;2676&#!&67>76&'&67!272'.+"9g,6rGR,mE < ; ;f+<-!Rj7/!b^` oIZ1m -3/ #- Lh^#B+;[$XTKa$wP  R=u D+)9u B 1 ;y{; 1  o!uf?%h2?;3"+6&'&54737% 47632#"'.#"327267#"&7PH9<1/!81qJ k)gxZ:f!2+`lu-# # #oW;E'v dGDxRfbV~4#h.<60"+%2676&#!&67>76.'&6732>33632#"&767\;35D: 119! [ +'& N#Ey@ 119! ]!F M!+' V}5  #K>3B'55 #K>D1/! 4#6' "+;2676&#!&67>?'?6.'&6732>34!F';35D: 119! 4 +'& N#Ey@ 119! =`}5  #K>mU>g3B'55 #K>rC}h:HB<8"+%.&#"&67>7.'"&?76&'&67327#&632#"&767-  N!\ wDXv ^>s )4  A N$\ wUHw `<b!3"~ M!+' BT%3{3 /  / :{1 15 /  / 8{(U1/! C}h EC*"+#&'767.&#"&67>7.'"&?76&'&67327#&g,7rFR,  N!\ wDXv ^>s )4  A N$\ wUHw `<b!3";[$XTKa$T%3{3 /  / :{1 15 /  / 8{(C}M;"+%.&#"&67>7.'"&?76&'&67327#"&7>323267&-  N!\ wDXv ^>s )4  A N$\ wUHw `<d)THBT5n ^ 539BT%3{3 /  / :{1 15 /  / 8{ ͻ51=K<'C)+w{/> @ ;4,$"+"3267654&#"&547632#"&767632"&76762q0TZw)UO1 /V #& ( %yRifǙWf^h8A<b /  ++Hh@M[ UOLB5""+&#"&547>7654&'&54732$32#"&/&'&#"2>7654'632#"&767+2Hq^HV6Go&s d-P {Nw R ! ZM9HT3+ ~R M!+( A(3! % %C{E%4! %vUT"j$`H) %F1)!1/! Hh KX WM@-"+#&'767&#"&547>7654&'&54732$32#"&/&'&#"2>7654'bg,7rFR,]+2Hq^HV6Go&s d-P {Nw R ! ZM9HT3+ ~R;[$XTKa$\A(3! % %C{E%4! %vUT"j$`H) %F1)!wh*82,%"+632676'&'&767>32'&#"#"&'&632#"&767-# =e _/7R9Rd0!5Un/3J! M! +( ? 71o9dLTRfL3<6bB+X>VOVu j1/! wyE:"+"'7*#"&'47632676'&'&767>32'&#"632#"'732654&5'KJ!-# =e _/7R9Rd0!5Un/3-?Q{^NLN'1J/ j 71o9dLTRfL3<6bB+X>VOVuspC32'&#"#"&'&g,6rGR,U-# =e _/7R9Rd0!5Un/3J!;[$XTKa$ 71o9dLTRfL3<6bB+X>VOVu jFI7 "+&'#".'6732676./7#"&67>76&#""'6743;272#.#"/s 90 d $I*/==1$ ~h uIR#V\U95PŠ˴ 86j?.K 2 V&Mk, +)%  1 IuGV m TA5" "+#&"&67>?#7376&#""'6743;272#.#""/s  uI"#V\U95PŠ˴ 86j?.PK 2  1 IPuGV m TAL5@K IC;6"+6&'&67327#".76&'&6732732>7"2676&>32#"3a 3% bP6;huX{-G)^ oq ^J;FqRrD#V+>-W?0sIF\ vHG{E 1  1 F{j{;Fk`? 1  1 D}Ս5jcI9,);;),9ccGEeL{5ET QKB:"+6&'&67327#".76&'&6732732>7#"&767632&7676323a 3% bP6;huX{-G)^ oq ^J;FqRrD# #& ( %{E 1  1 F{j{;Fk`? 1  1 D}Ս5jcI /  ++9+h)71+"+!2>32326?2%"&7677''676632#"&767;R`M /L^7)97F9o\/%B M!+'  @jDX? L s 1/! 9+D)52,"+!2>32326?2%"&7677''676%4632#"&;R`M /L^7)97F9o\/%B5LD;JR@;H @jDX? L s 3LT4-RS9+h 4# "+#&'767!2>32326?2%"&7677''676g,6rGR,R`M /L^7)97F9o\/%B;[$XTKa$ @jDX? L s 4}f.@5/"+%2676&#!&67>76.'&6732>332'>76#"&7>\;35D: 119! [ +'& N#Ey@ 119! ]!F72 d>X 87V}5  #K>3B'55 #K>DZ;B # W)+-!*h 8&"+#&'767&"&67>76&#""'6743;272#.#"Mf,6rGR,/s  uIR#V\U95PŠ˴ 86j?.;[$XTKa$hK 2  1 IuGV m TAC1"+2&7>76.'&7>&"&67>76&#""'6743;272#.#"y32'&#"#"&'&2&7>76.5&7>-# =e _/7R9Rd0!5Un/3J!VOVu jIWUw)?  M4J=D ,""+4632#"&6&'&6727&#"&67>7LC;KR@;Hj*\ ݨ ^HV)^ oq ^H3LT4-RS? 1  1 D{Z? 2  1 C{GVNH! "+;2�'7632767654'"&?327#"32&#576;27657&+"0;274.#q=  `> L1^# NV  b +1 < R FT &CF; ! +'P+!)) / i5' ++ <D9%(  !-52/ "+>&#"#"&7>32327#"&7#"'&7>77326WU6! Cb#:"&   W($NQD $ h'@5HN+ "! 9['=*, # $D Q;[Ǥ3VJ ,*# "+654'"7673632#"''67327654&#"\J@VFHI ^X'%;2 2-#;b - #3dP'/s1#)BP-)7.=B%"+2#"=4654#"327#"&547>`NT4-E5w FL`ot`a B/;)=wr-#%NX!yiV1#yV -,!"+&#"727"&547>327654'"767327#^F/32#"'&#"32#732&''76732RHT@!T;(## 1'D sJ))j0}  6R;"'7V%# "" ^1@Ud `ZFA;#"+"'&'"#"'3272>32#"54767&546567&547>3262327654'&""#&654'"32>'RD$ 9! !s-s+J'RB)={!31/L !V3>V&2/)Vd, +5#'`ih PD0B;33 Ti7+)H Nk t-Z7654'"767367676&#"?37#"72&''76/&#"&) J1y !{!ZLjVh 9+ TN\PAJ/L9 %% # - #7$`  ## GW J  *-Z`"+673672>32;2&'7473>?654#"732&'7673>?674#"732&'7473>7654'"NZLjVo&k-s > J1 :J-X@) J.9 L1]E'  N3y #K #kji%Dq/  %% #NR %% #)N\  %% #A % "+2654&#"'4>32#"&Ff6/;Y%?3IuEo]ĈchzLZs7327654&#"}\Jb@DJ a;%/)  N3y #1!/ ';P . # ?=hR-#q) %'#`-)=,;@B! 1 "+&#"32672032�76;2?"&547>32^F/ '% 87cT'"o51! "+327#"547#"574326?32#H)3RVLVN 99R- =###HNw1DN%5/ 6 "+327654+"?30727#7"54764&+"?307PI-cC) N1-`dBjTs > J/+ !LZ) #Z- !kjq)# ! #9} -+"+.#"?37+"6754"5?307#"#"A  L$o 98D+P+15 ## 4 ## Jg!"+326?2326?!"547#"'673/%35!)7}J3+%) +R!3#hE gVX/"pL0 "+#"5476?7>32#"'.#"32+#"&546323267>76 176:33b(%0 /C 76+#"&546323267>76#"5476?67>3267>32#"'.#&;'3+,v3/ ! /D R824Mrw2/!F @% <C =J-%8Nrw2/"C @ -L 1-4Ak6834b'Fo.0#$u7tTd|t ="`@W;=RT1$;0* )X9M"`@ˑH2LOU1$:-*1y'=!ݨ=EL;!>EjM)H+s; B0 "+"3&' #'#";2&#'76;267"'"'767>7.'&76${3ejP-3h?Njk ;% )=8G 5=< ]Z5!Lw'7uJ7{'=}Vw`j_;%+1--''+/u) 9DR<w;+N "+%"'.+"?30%+"7'&'#"?307+"7676&+"5737+"#"'Q#1+ yM  N7O! j) R ;#hu !P V ))')--v5*-+%4˪N\+- 8)'#7}+-C -3 "+>.+"?37+""'.+"?3%+"7fQ!1> #2;P7\f% '')1/0|/,%%- "'%+HF"+&;2&#'76;276&+"?3%+"2767&'#"?37+".Xl *>5 R8q -;5Q5q\VZ{Z "A O'V1H1!B1)''Z/+''ZOJ+F1''}wEd#3%; C* "+676'&#"366&#""?6763 7.'&#;2&#'76;2V!F9dSi % @FXYT%RH;P!7XPVQ! \ *<5R6q^yMJ-1'ffw7=×) 'GfM/+''<-/$"+#"&7>3232>7>.'&67327,HaE5:| g % :.74+  12/ ̇f 553-hq;&wQ@)J*/A,9+ 4  3  &G11=82%"+67&'"&#"'>323276327#"&54?&#"'3254ZJ>%5'DE#7x4`PC3:7ZLL?B)HPv+=J/]FfR@G=L^%Am!+t7jReNN<'}f}N#/!;,!"+%6'&'7>7&'"&#"'>323276&54>32326y'A5J+Z/!'HE7v6ZV53:5wR'^Z `k%#/)NL/3:-K6p5+GZC"1#kT<1='C15A"+>32#".5463232#"&54774#""'6323267 Vq9]\s#GP37#'1+%hJ FU$# J'I !B% D+7wb{+#-#'##\Y!ANj#3)#D B'/d)xe%8 +&"+.7>32.7>32.7>32'[oC2= PR 90][oC2= PR 90a[oC2= PR 91e&|q]2'E 43,&|q^2'D 43,&|q]2'D 43,,}3M =4# "+2&7>76./.7>!2&7>76./.7>!2&7>76./.7>GQ? ]548B    gQ? ]548B    gQ? ]549B    g׀|4-,C *- P)"L|4-,C *- P)"L|4-,C *- P)"LF/ "+$  "32y!TR--/TR!V--+"+"327654&' # 476f3$O3=r7!E5.afGiR7bFdABQD[@>VΑI9M'" "+%$#&67>7>&#"&7$762 /!  3:J- ?  D# =}D%! '#9% 65  #K>912 3!1}7"um(%"+767263!67&+&?>76&#"B(#-N}14C7uR+;f#s54&#"&767>54xL "X%7jNyC!nyciKIF/.:0O$U4pI($J>(qM]E$8,&AO1MO$e@"XjI'.*AK7Z1XPkt9&-td:S42"+)2+.#"&67>?!"/.7676673}b- .# =J"M 24@' 2  #[T4!,R*=&  4  4  "L=R%  )U*2!"+%4'./32>7"'#"'&746323276},9"-)U8t#'J-d/ 8S_AfZ:<32H=\;Z8'P   y_ (%mzW;99%7-C.!&6DiU.-! "+3267>54&#"67#".547>75[.#R)JB?R"t`\AVN@f?wf?ʟ\jm00.PQt?Ey54&'"&54767'&54763227>54&/^%.NU7I>If^k]_˜͉Z$|ώ/k@!01<[{PX)3r]15 R@PEDNlDhwS?hmnp[FZI&|C.K)@OuD[z^.-! "+654&#"3667>7.5467>32&5\.#Q)JB>R"ta\AUO@e?xf?˟jm00.OQuEx;850.(%" "+747!22#"'".7 $7>7! "&!6RTL F8m\ -Κ w !>TJm /> *ql7; %RGyNj7Z%du2:+/,*# "+654'"7673632#"''67327674&#"\J@VFHI ^X'%<1!1-#;- #3dP'/s1# )AP-)7-=B-J%"+2"=4654#"327#"&547>{NT4-F5w EL`ot`a JB/;)=vs-#%NX!yiV1#y;{ -,!"+&#"327#"&547>327654'"767327#F/P\ hT#\HdBw/P?7y;bT''o+- !{- !F8/ "+#"576;>32#"'&#"32#;2&#'76732bHT?!T;'## 1'DsI)) j/} 5};# '7V%# ""  f@Ud `YFA;#"+"'&#"#"'3272>32#"54767&546567&547>3262327654'&#""#&654#"32>^RD% :! !s-s+J'RB(=|"31/L !V3=V'1/)Vd- +6#&`ii PC1B;33 Ti7+)HNj t-Z?6#"2&#76?>76&#'"&57672+#qS'4 N1 /f1^: R5}$b +|?h$$ \ $$ !A$% 5"L".+%"+&#"&67>?6&'&67672>32#"&w $, 8G .'(  E5zF0,'I/+( :;".-";:fK*.eZ'L +"+2"&7>6&#'"&?672#"&76723267e+3(^ *|@ =028=-H *&,:#""#{A#% 77Յq-3&3<uL "+7732�7473>7654'"767367676&#"?37"72&#'76/&#"&) J1y !{! ZKjVh 9+ TN\PAJ/L9& $$ #- #6$`  "" GV J  *-/% "+6&#'"&?672&#""+76?>7 *|@h N2$ %fA$% 5V $$ !J`"+673672>32;2&#7473>?654#"732&#7673>?674#"732&#7473>7674'"{ZLjVo&k-s > J1 :J-W@) J.9 L1\F'  N3y #K #kji%Dq/  $$ #NR $$ #)N\  $$ #BYvpF$"+&#"&67>?6&'&67>72>32&#"&67>?6&#" ' iG .') '>7 Z0:Y+! $- /4i  26%-  q+.-";:I0. !  qn:;".-FHM4 ,0FJ+:70! "+7654'"7673632#"';2&#76;>7327654&#"\Jb?DJ a;%/)  N3x#1!/ ';P- # ?=hR-#q) %'#`-)=+;@B;FJ 1 "+&#"32672032�76;2?#"&547>32F/ '% 87bT'#o5J0"+%632#"'&#"2&#"7637>76&#'"&57672kGL ;" A5$ \;9] %#8 +|? y@5 H %% A#% 17J-&"+372676&'.>32.#"#"&#"6- !$9)B%@L>L-b /B()>1/.: \[*![%K*%'&%="C~Y O=+'$'-8%:KGL! "+327#"547#"574326?32#G)3RULVN 9:R- =###HNw1DN%b\=6 "+327654#"?30727#7"54764&+"?307!PI-cC( N1-`dAjTs = J/+;!LZ) "[- !kjq'% ! "T=-+"+.#"?37#"6754"5?307"#"A L%n 97D+P +15 "" 3 "" Ifh=J4"+#"'.#"?37"676&+"?37"#"' #"'.#"&?37S#7  M'q 5R*U2+5!G71 /) ""  3  "" If/*"?A "+"676&+"&?37"#"&7>327267676'.#"&?37P5H U-Y823a>7.  E%  UI'  1  " KPX$S<5Z"-L!"+326?2326?!"547#"'6730%35!(7}J3+%)=+R!3#hD fVX' !  "+3##5#53534632#"&3264&#"@@ɋǍ;uwwu`??ӋȌ馦'  "+!5!4632#"&3264&#"L^ɋǍ;uwwu!?!Ȍ馦:"+4'.54632#"'&'467654+"'6763!232>+ N/!+IDkX{yK`R}'R17)/!#Q)''9bH)lH{b%\%+VZ7L?5BT-##`T )O#b>#5 "+32676'&>`5 &S+Fw QG "+#"&7676$B &$f? w O  "+&'&76?#Eel&6767&Eemw6=C0 *U ~ "+#"&?3267$go;qXTkl9678y:!"+#"&76?63"'&76?632 !  ($ '  2y!! "+672#"/&76%632"/&76U ^  0( T   $"  &It "+>32.#"go:rXTkl9568q"+#"&76723267465>32 jm P;1/H& ("3KROL?& -)!&^N"+#"&7>322676'&7>32 fg 2# !2XB/$%QcbP($  #64#  &$J "+462"$462"BbBBb=Db@@bbDDbBBbDDbB U?""+#67676&#"#"&7>325 :("#" "gLV@$(J/##$  !&JM"+462"LfLLffLLfL^ 7!L5"+7!67>7;!!!!!3!!!!!!!!!!!+>7!7!>7!7!7#73679  !  Q L  3~ QK5 L4t 3 3J C 1 2+ *&0+98/> 07'+/ $2$Q/}/0/3-8<0/g&4 0' "+#"''7&54732732>76654'"&'&?v=jHR>i;1fL\>1^;^B/%LZRqLfBTsZPSVw[GxY1-qus'9yH"+67.6-F\!';%+f1 +o˝}e5X۬Ry  "+'7-sY\)X/3o}"+!!!+H1L{L'}"+7!!7!'+σLL^! "+&'&76&'7676767\=RTP!99)+;(J!BFD=5'sd!|mo59 >=3Ry #N>; <,^`! "+7>7.76'&'77676=RTP!79'+;)I%?DH=6&\@ud}o q3; 562"&7>2-5 a/ 5W<NZ6 LZ3NHJ?`i) FJPLJ-@@-/@@1F)21, "+%6'&7>32#"&7>7676?6226"&7>2 :!* yjgXJ^!35 ZGNP YLZ3 LZ!!')/dWNLA/;,HMZ\;R89iEdHZ@@-/@  "+'76''76eð`dh́qfpNh́qftN;  "+767&'%767&'V`iNsfqhNsfu{ "+'76ÙufnsultN "+?67&'7skm{sotN31 "+!"7463!2jL?JA3+Z+^C,0.8.hR9/GE#KPX@Ub`  b`  V M=M= M =N>@Sb`  b`U  V M= M =N>YY@,}ywsqfcYWSQMKA?<:64/-)'!  +367"".'.#"3#"5476?67>3267>32#".'.#"32+#"&546323267>76+#"&546323267>76('3+,v3/  1-4Ak4>13'3+,a4 /D R824Mrw2/!F @% <C =J-%8Nrw2/"C @ -od.0#$u7ta!ݨ=EL;>ElK)E-0#$Pt ="`@W;=RT1$;0* )X9M"`@ˑH2LOU1$:-*1y'=9"i K*PX@J bb`b M = =M ==N>@H bb`b U M = ==N>YY@ecba\ZTRMK$(*$+'$"+32672#"'&7654'!#"&546323267>76#"5476?>7>32#".'.#"!632*W%Y$$FmLZ $C %{'1e?Q!I!!ZJL-7?KXhLf3NNXb'!#'1qJEo3=Dh_9@"+'55V<&UT&7q_<1^'2-e1^d v"K R^2}  hpV f3$44L4j44|3[^--At-Q$"v+x|wE-/<zd?hV+"6TOtt MQQCsQVsGp pz-;"7 uS2pp3      f444437{4vvvvvv}xEEEEx7++++TfT v v vfxfxfxfx3w3|wEEEEE44444444L<4zzj4j4j4j4j4Qp$f|3h|3h|3h[V[V[V[Vz$++++++T-O-O-OEtt{ 5 \XfX'>l}lx$$m$v$Gp 5fi1D1-g& Y-11@SOV\Fp3It-~+~~~R~5|~O1A- "[HLh*3w#9q)9B]29%#vF\7B_Bai]2^>| w4k8_<F772@a% FFuSqTGp AT\Z-Z 9'-444^o44=(E{47z77zQuoEu/Po/~~ v+++fx3|w3|w3|w3|w3|MEEEYEVE$-/44444544z4z4zj4j4j46j44d4d|3h|3h|3h|3h[V[V[V[V[Vj*++r+++^^"6"6T-O-O-OTv v v v vY v v v v v v v vEEEEEJEEE4b4uJ=#++d}d)$d`Rdd%TTTTZ#/3777777f1zIIIIIIIISSdxKKKKKKKKh/CA=&7v VS/77IIKK lZ=#9+G/X3IIIIIIII 7 $ A &  $s\n{9rt^rIIIIIhn\&%\KKKKKvn-X%(4299nnvSjj"(dX0SSE`91$u$,$>$x HuRdRR?S KZ&Z}d}wIbJZ!?M7BV;p -5&3 Wi6?a4@`j_ft.tDt5ttRtIYpL)%!G!~+&d7  j7?%OTOTX5bf#?#?#\%SdSB)1bV;555V5l" 5##K#//?)7~777Zd7-n---+\-=/ # ^^L  b\DB; X33 d =9hZF3H3#"   Z1TV)7777#7~GSJ}IJnn777777777777EEq7777--`--`Onuzfy7SZSZSZSZZZS;S;dVdddddddddP1&1F$}}} tttV(zd"!MfRl+Ty(L7qP(FPEPPPAdVfqf5#?5#o"%\7%XIHZ; A A Ajjsd#^2-y# 9#A7FFFIIss=P(p^Da$3G Q/v; HHJJxJJ`?J4l!C7JHEwnO"Mr9zJJJJJJJJHCrOHrHw HHJJ44CCCHHEwEwEwnn9994nnEwJ G (\t+jptSZ1r5h9v//$%%<dx,F=S*zSRG+(-\;tV"pIYpv;LrbhTh''Q2I^ S^\\jj'//=II</9x{dddd(x  t  P  L 0<4T`@( !"x#p$%'))*+,-/0124@5P788P88989:;<<==?<@ABChDE FtGpGHIJKPL(M@NO`PhQ@RHRSST0T0TVX XZLZ\\]^_`__aLab(bde ehfgh hij jl(moplqrt8uwx|z|8}h0<LXptX( LhP@t,hT`T<l8p0 Ш8٠ڈ4 LH,\   , hDt(48L  !"T#(#%(&H'(*D+p,-/T0L124$5 78x9:=`>AB@DpEGHIK,LpMO8PR\T4UW,XZ([l\]`b8dfhjkDmnoqrLsHt<u\wHxhylyzz{X{|}}~0~`~~dl,pt| ` ,,TlD4t<l<< | x HPP(p`LTlpL8  P8dlT,|(X8 tT8L0DL4pL4LİPTx ɌT4Τd,x(tH D ܄  8$0<4(d   l d,|PHhdTl !@"#,$%L&|($)+,./d1H2435d7L8:<>?XABDF<HIKLN|O`QXRlUVX4Y[\^`ac$df,hj4lm0n|opr$su4vx|z${|}4h,d,8Td(@(HX||4(xd<Dǀɠʸ`ҐP֠،ڴXߴx pL$(pD`\   `,pT !x#8%4&(d*+-/02X36 79|;<>@@ALCDFGIK,MN|Q RdT|UWXZ\H]L^_p`@ablcldefhDipjklnnxnooxp4pq@qr$rsu@wxxy$yhyz{{||t|~dt$X,T( |xh| ɤʨ\ЈXӸd,4۴@xL h(tHh\ L @40@ `"$&(*,\.10457d9;>,@BDFdHJL4N,PdRTVX[]p^_a0bceHfhHikmor@twz}4hD0Xp,88hԌڐ|ޤPd,,t, \ L8p$ < !#%@&()|*+D,d-/0284$586<7\8t9@:(;L<=?tA,BD0EGIJL<MOtP(PQ<SDTVX\ZL[\^(_|`a@aaaaaaaaaaaaab4bbc cTccd$deeefghhhikPkkklTlmmnq$qrrsHttu0uhvpw|xy,||~td xpDPXT<Tp<xLht,0X$P\T$4dD`h4@PĨ|Ɯǀd˴dDHpڌH,$hldhtt  \|Xp` L !0!!"<"#@#$$p$%T%&`' 'D'|''(H()H**+,+,,d,-. ../`//001D128223t3445567(7L7788@8989p9:x;4;;<<\<<= =L==>X>>?d?@pA(AAB BBCC`CDDpDEXEEFpFGtGHlHI IIJ J4JtJKKlKKLL4LhLLLM M0MdMMNXNOO<OPPdPQ0QQQR8VWYHZ[x\]^_T_`pa,abLbcdpefPfghXhijklmdmnopdpqqr0rtrshsuvwy8z~t~X0D(4T0`h @x p x,l(,,l `$4l„tĈĈ$Dx˴XHHPѐҬlX|,פ؜d,00,Lltx|x@H(dt|dtdtP H XL<8  ( <   d P$$ dp ` !p""x#T$%&'8(()*X++,--.l./|00012$23D345 5 56(67d89L9:;l;==>?(?@X@AlBBCD0DEEEF,FhFFG`GHH|HI,IIJKKL LTLMMMNOOlOOPPS\UVpV DQh> +66l"    8CG 8   l 6b D    :4 :n p Linux Libertine by Philipp H. Poll, Open Font under Terms of following Free Software Licenses: GPL (General Public License) with font-exception and OFL (Open Font License). Created with FontForge (http://fontforge.sf.net) Sept 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011,2012Linux LibertineBold ItalicFontForge 2.0 : Linux Libertine Bold Italic : 2-7-2012Linux Libertine Bold ItalicVersion 5.1.8 ; ttfautohint (v0.9)LinLibertineBIPhilipp H. PollPhilipp H. Pollhttp://www.linuxlibertine.orghttp://www.linuxlibertine.orgGPL - General Public License AND OFL - Open Font Licensehttp://www.fsf.org/licenses/gpl.html AND http://scripts.sil.org/OFLLinux Libertine by Philipp H. Poll, Open Font under Terms of following Free Software Licenses: GPL (General Public License) with font-exception and OFL (Open Font License). Created with FontForge (http://fontforge.sf.net) Sept 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011,2012Linux LibertineBold ItalicFontForge 2.0 : Linux Libertine Bold Italic : 2-7-2012Linux Libertine Bold ItalicVersion 5.1.8 ; ttfautohint (v0.9)LinLibertineBIPhilipp H. PollPhilipp H. Pollhttp://www.linuxlibertine.orghttp://www.linuxlibertine.orgGPL - General Public License AND OFL - Open Font Licensehttp://www.fsf.org/licenses/gpl.html AND http://scripts.sil.org/OFLQ  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghjikmlnoqprsutvwxzy{}|~      !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~      !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~      !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~      !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~      !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~      !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~uni00A0uni00AD two.superiorthree.superioruni00B5 one.superiorAmacronamacronAbreveabreveAogonekaogonek Ccircumflex ccircumflex Cdotaccent cdotaccentDcarondcaronDcroatEmacronemacronEbreveebreve Edotaccent edotaccentEogonekeogonekEcaronecaron Gcircumflex gcircumflex Gdotaccent gdotaccent Gcommaaccent gcommaaccent Hcircumflex hcircumflexHbarhbarItildeitildeImacronimacronIbreveibreveIogonekiogonekIJij Jcircumflex jcircumflex Kcommaaccent kcommaaccent kgreenlandicLacutelacute Lcommaaccent lcommaaccentLcaronlcaronLdotldotNacutenacute Ncommaaccent ncommaaccentNcaronncaron napostropheEngengOmacronomacronObreveobreve Ohungarumlaut ohungarumlautRacuteracute Rcommaaccent rcommaaccentRcaronrcaronSacutesacute Scircumflex scircumflex Tcommaaccent tcommaaccentTcarontcaronTbartbarUtildeutildeUmacronumacronUbreveubreveUringuring Uhungarumlaut uhungarumlautUogonekuogonek Wcircumflex wcircumflex Ycircumflex ycircumflexZacutezacute Zdotaccent zdotaccentlongsuni0228uni0229 h.superiorhhook.superior j.superior r.superiorrturned.superiorrhookturned.superiorRsmallinverted.superior w.superior y.superioruni02B9uni02BAuni02BB afii57929 afii64937uni02BEuni02BFuni02C0uni02C1uni02C2uni02C3uni02C4uni02C5uni02C8uni02C9uni02CAuni02CBuni02CCuni02CDuni02CEuni02CFuni02D0uni02D1uni02D2uni02D3uni02D4uni02D5uni02D6uni02D7uni02DEuni02DFgammalatin.superior l.superior s.superior x.superiorglottalstopreversed.superioruni02E5uni02E6uni02E7uni02E8uni02E9uni02EAuni02EBuni02ECuni02EDuni02EEuni02EFuni02F0uni02F1uni02F2uni02F3uni02F4uni02F5uni02F6uni02F7uni02F8uni02F9uni02FAuni02FBuni02FCuni02FDuni02FEuni02FF gravecomb acutecombcircumflexcomb tildecombuni0304uni0305 brevecombuni0307uni0308 hookabovecombuni030Auni030B caroncombuni030Duni030Euni030Funi0310uni0311uni0312uni0313uni0314uni0315uni0316uni0317uni0318uni0319uni031Auni031Buni031Cuni031Duni031Euni031Funi0320uni0321uni0322 dotbelowcombuni0324uni0325uni0326uni0327uni0328uni0329uni032Auni032Buni032Cuni032Duni032Euni032Funi0330uni0331uni0332uni0333uni0334uni0335uni0336uni0337uni0338uni0339uni033Auni033Buni033Cuni033Duni033Euni033Funi0340uni0341uni0342uni0343uni0344uni0345uni0346uni0347uni0348uni0349uni034Auni034Buni034Cuni034Duni034Euni0350uni0351uni0352uni0353uni0354uni0355uni0356uni0357uni0358 acute.capcircumflex.cap caron.capuni035Chungarumlaut.capspace_uni030F.capbreveinvertedcmb.cap breve.cyrcap breve.cyr dieresis.caphookabovecomb.capuni0364uni0365uni0366uni0367uni0368uni0369uni036Auni036Buni036Cuni036Duni036Euni036Funi0374uni0375tonos dieresistonos Alphatonos anoteleia EpsilontonosEtatonos Iotatonos Omicrontonos Upsilontonos OmegatonosiotadieresistonosAlphaBetaGammaEpsilonZetaEtaThetaIotaKappaLambdaMuNuXiOmicronPiRhoSigmaTauUpsilonPhiChiPsi IotadieresisUpsilondieresis alphatonos epsilontonosetatonos iotatonosupsilondieresistonosalphabetagammadeltaepsilonzetaetathetaiotakappalambdanuxiomicronrhosigma1sigmatauupsilonphichipsiomega iotadieresisupsilondieresis omicrontonos upsilontonos omegatonosuni1E00uni1E01uni1E02uni1E03uni1E04uni1E05uni1E06uni1E07uni1E08uni1E09uni1E0Auni1E0Buni1E0Cuni1E0Duni1E0Euni1E0Funi1E10uni1E11uni1E12uni1E13uni1E14uni1E15uni1E16uni1E17uni1E18uni1E19uni1E1Auni1E1Buni1E1Cuni1E1Duni1E1Euni1E1Funi1E20uni1E21uni1E22uni1E23uni1E24uni1E25uni1E26uni1E27uni1E28uni1E29uni1E2Auni1E2Buni1E2Cuni1E2Duni1E2Euni1E2Funi1E30uni1E31uni1E32uni1E33uni1E34uni1E35uni1E36uni1E37uni1E38uni1E39uni1E3Auni1E3Buni1E3Cuni1E3Duni1E3Euni1E3Funi1E40uni1E41uni1E42uni1E43uni1E44uni1E45uni1E46uni1E47uni1E48uni1E49uni1E4Auni1E4Buni1E4Cuni1E4Duni1E4Euni1E4Funi1E50uni1E51uni1E52uni1E53uni1E54uni1E55uni1E56uni1E57uni1E58uni1E59uni1E5Auni1E5Buni1E5Cuni1E5Duni1E5Euni1E5Funi1E60uni1E61uni1E62uni1E63uni1E64uni1E65uni1E66uni1E67uni1E68uni1E69uni1E6Auni1E6Buni1E6Cuni1E6Duni1E6Euni1E6Funi1E70uni1E71uni1E72uni1E73uni1E74uni1E75uni1E76uni1E77uni1E78uni1E79uni1E7Auni1E7Buni1E7Cuni1E7Duni1E7Euni1E7FWgravewgraveWacutewacute Wdieresis wdieresisuni1E86uni1E87uni1E88uni1E89uni1E8Auni1E8Buni1E8Cuni1E8Duni1E8Euni1E8Funi1E90uni1E91uni1E92uni1E93uni1E94uni1E95uni1E96uni1E97uni1E98uni1E99uni1E9Auni1E9Buni1E9Cuni1E9D Germandblsuni1E9Funi1EA0uni1EA1uni1EA2uni1EA3uni1EA4uni1EA5uni1EA6uni1EA7uni1EA8uni1EA9uni1EAAuni1EABuni1EACuni1EADuni1EAEuni1EAFuni1EB0uni1EB1uni1EB2uni1EB3uni1EB4uni1EB5uni1EB6uni1EB7uni1EB8uni1EB9uni1EBAuni1EBBuni1EBCuni1EBDuni1EBEuni1EBFuni1EC0uni1EC1uni1EC2uni1EC3uni1EC4uni1EC5uni1EC6uni1EC7uni1EC8uni1EC9uni1ECAuni1ECBuni1ECCuni1ECDuni1ECEuni1ECFuni1ED0uni1ED1uni1ED2uni1ED3uni1ED4uni1ED5uni1ED6uni1ED7uni1ED8uni1ED9uni1EDAuni1EDBuni1EDCuni1EDDuni1EDEuni1EDFuni1EE0uni1EE1uni1EE2uni1EE3uni1EE4uni1EE5uni1EE6uni1EE7uni1EE8uni1EE9uni1EEAuni1EEBuni1EECuni1EEDuni1EEEuni1EEFuni1EF0uni1EF1Ygraveygraveuni1EF4uni1EF5uni1EF6uni1EF7uni1EF8uni1EF9uni1F00uni1F01uni1F02uni1F03uni1F04uni1F05uni1F06uni1F07uni1F08uni1F09uni1F0Auni1F0Buni1F0Cuni1F0Duni1F0Euni1F0Funi1F10uni1F11uni1F12uni1F13uni1F14uni1F15uni1F18uni1F19uni1F1Auni1F1Buni1F1Cuni1F1Duni1F20uni1F21uni1F22uni1F23uni1F24uni1F25uni1F26uni1F27uni1F28uni1F29uni1F2Auni1F2Buni1F2Cuni1F2Duni1F2Euni1F2Funi1F30uni1F31uni1F32uni1F33uni1F34uni1F35uni1F36uni1F37uni1F38uni1F39uni1F3Auni1F3Buni1F3Cuni1F3Duni1F3Euni1F3Funi1F40uni1F41uni1F42uni1F43uni1F44uni1F45uni1F48uni1F49uni1F4Auni1F4Buni1F4Cuni1F4Duni1F50uni1F51uni1F52uni1F53uni1F54uni1F55uni1F56uni1F57uni1F59uni1F5Buni1F5Duni1F5Funi1F60uni1F61uni1F62uni1F63uni1F64uni1F65uni1F66uni1F67uni1F68uni1F69uni1F6Auni1F6Buni1F6Cuni1F6Duni1F6Euni1F6Funi1F70uni1F71uni1F72uni1F73uni1F74uni1F75uni1F76uni1F77uni1F78uni1F79uni1F7Auni1F7Buni1F7Cuni1F7Duni1F80uni1F81uni1F82uni1F83uni1F84uni1F85uni1F86uni1F87uni1F88uni1F89uni1F8Auni1F8Buni1F8Cuni1F8Duni1F8Euni1F8Funi1F90uni1F91uni1F92uni1F93uni1F94uni1F95uni1F96uni1F97uni1F98uni1F99uni1F9Auni1F9Buni1F9Cuni1F9Duni1F9Euni1F9Funi1FA0uni1FA1uni1FA2uni1FA3uni1FA4uni1FA5uni1FA6uni1FA7uni1FA8uni1FA9uni1FAAuni1FABuni1FACuni1FADuni1FAEuni1FAFuni1FB0uni1FB1uni1FB2uni1FB3uni1FB4uni1FB6uni1FB7uni1FB8uni1FB9uni1FBAuni1FBBuni1FBCuni1FBDuni1FBEuni1FBFuni1FC0uni1FC1uni1FC2uni1FC3uni1FC4uni1FC6uni1FC7uni1FC8uni1FC9uni1FCAuni1FCBuni1FCCuni1FCDuni1FCEuni1FCFuni1FD0uni1FD1uni1FD2uni1FD3uni1FD6uni1FD7uni1FD8uni1FD9uni1FDAuni1FDBuni1FDDuni1FDEuni1FDFuni1FE0uni1FE1uni1FE2uni1FE3uni1FE4uni1FE5uni1FE6uni1FE7uni1FE8uni1FE9uni1FEAuni1FEBuni1FECuni1FEDuni1FEEuni1FEFuni1FF2uni1FF3uni1FF4uni1FF6uni1FF7uni1FF8uni1FF9uni1FFAuni1FFBuni1FFCuni1FFDuni1FFEenquademquadenspaceemspacethreeperemspacefourperemspace sixperemspace figurespacepunctuationspace thinspace hairspacezerowidthspace hyphentwo hyphennobreak figuredash horizontalbaruni2016 underscoredbl quotereversed quotedblrevtrianglebulletonedotenleadertwodotenleader hyphendotuni202Funi2031minutesecond primetriple primereverseduni2036uni2037uni203B exclamdbl interrobanguni203Euni2042question_questionquestion_exclamexclam_questionuni204Auni204Buni204F zero.superior i.superior four.superior five.superior six.superiorseven.superioreight.superior nine.superior plus.superiorminus.superiorequal.superiorparenleft.superiorparenright.superior n.superior zero.inferior one.inferior two.inferiorthree.inferior four.inferior five.inferior six.inferiorseven.inferioreight.inferior nine.inferior plus.inferiorminus.inferiorequal.inferiorparenleft.inferiorparenright.inferior a.inferior e.inferior o.inferior x.inferioruni2094uni2095uni2096uni2097uni2098uni2099uni209Auni209Buni209Cuni20A2lirapesetauni20A8dongEurouni20AFuni20B1uni2100uni2101uni2102 centigrade afii61248uni2106 fahrenheituni210Cuni210Duni210Euni210FIfraktur afii61289uni2115 afii61352uni2119uni211ARfrakturuni211Duni2120uni2124uni2126uni2127 estimatedalephuni2136uni2137uni2138uni2139onethird twothirdsonefifth twofifths threefifths fourfifthsonesixth fivesixths oneeighth threeeighths fiveeighths seveneighths onenumeratorOneromanTworoman Threeroman Fourroman FiveromanSixroman Sevenroman Eightroman NineromanTenroman Elevenroman Twelveromanuni216Cuni216Duni216Euni216Foneromantworoman threeroman fourroman fiveromansixroman sevenroman eightroman nineromantenroman elevenroman twelveromanuni217Cuni217Duni217Euni217Funi2180uni2181uni2182uni2183uni2184 arrowleftarrowup arrowright arrowdown arrowboth arrowupdnuni2196uni2197uni2198uni2199uni219Auni219B arrowupdnbseuni21AEuni21BCuni21BDuni21C0uni21C1uni21CBuni21CCuni21CDuni21CEuni21CF arrowdblleft arrowdblup arrowdblright arrowdbldown arrowdblbothuni21D5NwarrowNearrowSearrowSwarrow universaluni2201 existentialuni2204emptysetuni2206gradientelement notelementuni220Asuchthatuni220Cuni220Duni2210uni2213uni2214uni2215uni2216 asteriskmathuni2218uni2219uni221Buni221C orthogonaluni2223uni2224uni2225uni2226 logicaland logicalor intersectionunionuni2236similaruni2241 congruentuni2249uni2259 equivalenceuni2262uni226Auni226Buni226Euni226Funi2270uni2271 propersubsetpropersuperset notsubsetuni2285 circleplusuni2296circlemultiplyuni2298dotmathuni22EFuni2302uni2303uni2310uni2320uni2321uni2329uni232Auni23D3 filledboxH22073triagupuni25B3uni25B6uni25B7triagdnuni25BDuni25C0uni25C1uni25C6uni25C7uni25C9circleH18533uni25D0uni25D1uni25D2uni25D3uni25D4uni25D5uni25D6uni25D7uni2605uni2619uni261Buni261Euni2627uni262Funi2639uni263Auni263Bsununi263Duni263Euni263Ffemaleuni2641maleuni2643uni2644uni2645uni2646uni2647uni2648uni2649uni264Auni264Buni264Cuni264Duni264Euni264Funi2650uni2651uni2652uni2653uni2660uni2663uni2665uni2666uni2669 musicalnotemusicalnotedbluni266Cuni2695uni2698uni26A2uni26A3uni26A4uni2767uni27E6uni27E7uni27E8uni27E9uni2C60uni2C61uni2C62uni2C63uni2C64uni2C65uni2C66uni2C67uni2C68uni2C69uni2C6Auni2C6Buni2C6Cuni2C74uni2C75uni2C76uni2C77uniA720uniA721TuxuniE001uniE002uniE003uniE004uniE005uniE006uniE007uniE008uniE009uniE00AuniE00BuniE00CuniE00DuniE00Ezero.slashfitted zero.fitted one.fitted two.fitted three.fitted four.fitted five.fitted six.fitted seven.fitted eight.fitted nine.fitted Euro.fitted Yen.fittedperthousandzero zero.oldstyle one.oldstyle two.oldstylethree.oldstyle four.oldstyle five.oldstyle six.oldstyleseven.oldstyleeight.oldstyle nine.oldstyle Adieresis.alt Odieresis.alt Udieresis.altuniE02Ff_tc_kc_ht_tc_tuniE03EQ_uT_ha.scb.scc.scd.sce.scf.scg.sch.sci.scj.sck.scl.scm.scn.sco.scp.scq.scr.scs.sct.scu.scv.scw.scx.scy.scz.sc hyphen.sc agrave.sc aacute.scacircumflex.sc atilde.sc adieresis.scaring.scae.sc ccedilla.sc egrave.sc eacute.scecircumflex.sc edieresis.sc igrave.sc iacute.scicircumflex.sc idieresis.sceth.sc ntilde.sc ograve.sc oacute.scocircumflex.sc otilde.sc odieresis.scoe.sc oslash.sc ugrave.sc uacute.scucircumflex.sc udieresis.sc yacute.scthorn.sc ydieresis.scij.scgermandbls.scalt germandbls.scuniE093uniE094 dcroat.sc abreve.sc aogonek.sc cacute.sc ccaron.sc dcaron.sc eogonek.sc ecaron.sc gbreve.sc lacute.sc lslash.sc nacute.sc ncaron.sceng.scohungarumlaut.sc racute.sc rcaron.sc sacute.sc scedilla.sc scaron.sctcommaaccent.sctbar.scuring.scuhungarumlaut.sc zacute.sc zdotaccent.sc zcaron.sc lcaron.sc tcaron.sc uni021B.scscommaaccent.sc idotaccent.sca.scalt a.superior b.superior c.superior d.superior e.superior f.superior g.superior k.superior m.superior o.superior p.superior q.superior t.superior u.superior v.superior z.superiorf.short f_f.shortuniE0E8W.altV.altK.altR.altJ.altz.altuniE0F4y.altuniE0F9uniE0FBkreiszero.taboldstyleone.taboldstyletwo.taboldstylethree.taboldstylefour.taboldstylefive.taboldstylesix.taboldstyleseven.taboldstyleeight.taboldstylenine.taboldstyleuniE130uniE138uniE148 b.inferior c.inferior d.inferior f.inferior g.inferior h.inferior i.inferior j.inferior k.inferior l.inferior m.inferior n.inferior p.inferior q.inferior r.inferior s.inferior t.inferior u.inferior v.inferior w.inferior y.inferior z.inferioruniE188uniE189v.alt grave.cap acute.capcircumflex.cap caron.cap breve.caphungarumlaut.capspace_uni030F.capbreveinvertedcmb.cap breve.cyrcap breve.cyr dieresis.caphookabovecomb.cap dotaccent.capuniE420 zero.slash parenleft.sc parenright.scbracketleft.scbracketright.sc braceleft.sc braceright.sc exclamdown.scquestiondown.scguillemotleft.scguillemotright.scguilsinglleft.scguilsinglright.sc hyphen.capdotlessjf_ff_iuniFFFDuni0350UUVDD, d `f#PXeY-, d P&ZE[X!#!X PPX!@Y 8PX!8YY Ead(PX! E 0PX!0Y PX f a PX` PX! ` 6PX!6``YYY+YY#PXeYY-,#B#B#BCCQXC+C`BeY-,C E EcEb`D-,C E +#%` E#a d PX!0PX @YY#PXeY%#aDD-,EaD-,` CJPX #BY CJRX #BY-, b c#a C` ` #B#-, CUX CaB+YC%BC`B %B %B# %PXC%B #a*!#a #a*!C%B%a*!Y CG CG`b EcEb`#DC>C`B- ,ETX #B `a  BB`+g+"Y- , +- , +- , +- , +-, +-, +-, +-, +-, +-, +-,+ETX #B `a  BB`+g+"Y-,+-,+-,+-,+-,+-,+-,+-,+-,+-, +-, ` ` C#`C%%QX# <`#e!!Y- ,+*-!, G EcEb`#a8# UX G EcEb`#a8!Y-",ETX!*0"Y-#,+ETX!*0"Y-$, 5`-%,EcEb+EcEb+D>#8$*-&, < G EcEb`Ca8-',.<-(, < G EcEb`CaCc8-),% . G#B%IG#G#ab#B(*-*,%%G#G#a+e.# <8-+,%% .G#G#a #B+ `PX @QX  &YBB# C #G#G#a#F`Cb` + a C`d#CadPXCaC`Y%ba# &#Fa8#CF%CG#G#a` Cb`# +#C`+%a%b&a %`d#%`dPX!#!Y# &#Fa8Y-,, & .G#G#a#<8--, #B F#G+#a8-.,%%G#G#aTX. <#!%%G#G#a %%G#G#a%%I%aEc#bcEb`#.# <8#!Y-/, C .G#G#a ` `fb# <8-0,# .F%FRX ,#B=+-7,*+. +-C,7+-D,7+-E,7+-F,7+-8,++!# <#B#8 +C. +-O,8+-P,8+-Q,8+-R,8+-=,E# . F#a8 +-W,,+. +-X,,+0+-Y,,+1+-Z,,+2+-[,-+. +-\,-+0+-],-+1+-^,-+2+-_,.+. +-`,.+0+-a,.+1+-b,.+2+-c,/+. +-d,/+0+-e,/+1+-f,/+2+-g,+e$Px0-KRXYc #D #pE KQKSZX4(Y`f UX%aEc#b#D + ++Y(ERD +DLinLibertine_RBah.ttf000066400000000000000000026660701300200146000357510ustar00rootroot00000000000000connectome-workbench-1.2.3+git41-gc4c6c90/src/Resources/GeneralResources/Fonts/LinuxLibertine FFTMYft,GDEF4|H@GPOS6=@GSUBIDOS/2\V`cmapdVcvt 0 b0fpgm蕏 bgasp bglyfM^ Shead"j 6hhea/6 $hmtxny 0%hloca2 R ט%lmaxp  name4 $postG7R \preptH koB6881yzz{}~<==>"#01mnwxxy{||}}~~56BC - . 6 7 7 8 8 9 9 : : ; Q R U V W X X Y Y$LT\dlt|"*8@HPX`hpx# ? P = o h !   7^b`/ B 5J$>DEFHIJKMNTUVXYZ[^ R S T U V W X DFLT cyrl0grek@hebrPlatn\(AZE (CRT (DEU (MOL (ROM (TRK (cpspkernmark *B $4(,/ 4Fy                                                                                                                         y$%&'()*+,-./0123456789:;<=   "$&(*,.02468:;? 1Rq11L111o1111L\1111Lj{jjjjL111L11111d     ""##$$ ''))+,..// 1122334477889:;;<<>>BB EE HH IIJJNNRS UUYZ[[\\^^aamm}}         $$&&**,,..00224466778899::AAHHKLQQff   IIJJKKLLMMNN    pp  P P R R    !""$$&&**--224477889:<<@@ DD EEFH JJ KKMMNOPQ RR SS TT UU VV XXYZ[[\\]] `` aammoorrtuyy{{}}       $$&&**,,..0022446677::BBIIJJ OO UUabcc   IIJJKKLLMMNN    hioo "#$')+,./1234789:;<>BEHIJNRSUYZ[\^am}  $&*,.0246789:AHKLQfIJKLMNp P Rj =3q.    %%++,,::PPQQ[[]]bbddffjj [[,,//IIMMQQ]].  %+,:PQ[]bdfj ==)))))+::??@@CCEEFFJJLLMMQQSSUUVVWWXX\\ddggkkpprrww}}FFil"CCFFMMUUVV\\]]bbddhh jk mmnnpprrstvv wwxxzz}}FF%%''--ijmm Q Q =:?@CEFJLMQSUVWX\dgkprw}Fijkl4 ) ))$$.. 9:;; <<::   L O  L O7$.9:;<: L M N O.4 6ntz "(.4:@FLRX^djpv|1 %yo\HoIo;1''\P7%o6%&'()*+./345679:;=EFGIJKNOSTUVWYZ[]DEFGMNRZf `o_% 28>DJPV\bhntzf+\+\+\+\+\+\++{+=}*24FGHR $++y  . (.4:@FLRX^djpv| $*06<BHNTZ`flrx~ &,28>DJPV\bhntz "(.4:@FLRX^djpv| $*06<BHNTZ`flrx~ &,28>DJPV\bhntz "(.4:@FLRX^djpv|#s9?3u1s7uR)))))d)\rE#)+)++)+) ) ++ +)+''''mq^'''/''''''''''''?     '        \          9+ )\\\\/\/)\+)'o+^\+q#sq qd)sh:+o+3)')/)+)+o)))))))))))dd))B))+))++)9J9JqqXqqDq)fjHd))%+)))d9/q///q//qq/q//q/qDq/q1q))+/ ))+3)mq7q)d)b3+m++m)w)N)b))w)b)){))))))N)b)N)N))b)))})Ps))/q)$%&'()*+,-./0123456789:;<=DFGHJKLNOPQRSTUVXYZ[\]  #,-4CDFLMRVWZ^`defv  $'()+,1345>MT`bpquyzvIM];<o_gLMNOPQ Q== !$%$0&ww3}}45 . 67 8 8@ : :AB "(.4:@FLRX^djpv| $*06<BHNTZ`flrx~5wyuT)\)sVR)?jh f\);dwyu+;H)\))d)))))) )3))))y);)s)d)m))-'\PPRyD1=Dyqu+} \ &,28>DJPV\bhntz "(.4:@FLRX^djpv|+{w/}JhJH?HH#;9{#+{{^{o 3F;-/jb1FN9b1%5L%;9qh;JJ^^JJ\%&'*+,-/012346789;=EFGHIJKLMOPQRSTUWXY[] ,4DEFGMNRf  o_z &,28ff`R 2flrx~ &,28>DJPV\bhntzH!V)TbZRX=H!VVVVVVT!!15))TXHHb'ZZ`ZuZZZy1ZZ`ZuZZZ2DFGHKLRVWXo ()343  $*06<BHNTZ`flrx~ &,28>DJPV\bhntz "(.4:@FLRX^djpv| ;F33-#3mLV Rd;;;;VV;;#{  #/!/RR%)"/. 3 /L?Thh+shThL;y$%&'()*+,./012356789:;<=DFHLNOPQRUVWX[]  #,-4CDFLMRZ[^`defoq 3MQVW`E 4ko4_MNOPz sZ  &,28>DJPV\bhntz "(.4:@FLRX^djpv| $*06<BHNTZ`flrx~ &,28>DJPV\bhntz "(.4:@FLRX^djpv|w%;F-%wuLssRIDossNsd%;%;%;%;%!#+#;;###!w#%%#%#%s %#=N%N!!jy j9+ HZ)%%%%VV3})%%b +NV%!%}!%L%%DXmm7mXXmPJ}@$,.3 5=D]23789:;<=>CDE  F##H,-I44KCGLLNQRRTWWUZ[V^^X``YddZff[oo\]_  ` b$$q&.r16{99;<>>LMQQTTVW``pqcc]]oo44__ggLQ   #%)##+nv,yy5{{67 &,28>DJPV\bhntz ".?R5F'FBBX#^qLq   F# mw9/oy%1dVs fjDFLT cyrlNgrek|hebrlatn   (AZE RCRT RDEU (MOL |ROM |TRK R   aaltc2sccasefinafrachligligaligalnumloclonumpnumsaltsinfsmcpsmcpss01ss03supstnumzero     .6>FNV^fnv~048  @  n   < r BL^lzhlpx|"&*.26:>BFJNRVZ^bfjnrvz~ "&*.26:>BFJNRVZ^bfjnrvz~ $*06<BHNTZ`flrx~ D E wi Chvx{jytkzul{m|n}o~pqr F G H I J L M K!s N Os iwhxyz{|}ijklmnpr  .5>@DEFGHIJLMNOPQRSTUVWXYZ[\]^`cm}   !#')13<>@cikrstwehjklmnoqwxyz{|~ + !$%KL^^DEFGHIJKMNOPQRSTUVWXYZ[\]  !#%')13<>@ALtF(> "*2:BJRZbjp<<>>8866~BB "9977&.6@??::;;==@AAh1{turtuyzsvwx  D]%%((();;*==+CC,WW-./ C C0Z*              ! " # $ % & ' (  D]( C C)k D E F G H I J L M K N Ok $%&'()*+,-./0123456789:;<=>@^`cm}   "$&(02:;=?e UIL SLI&2DV` " " XK MW TO RI"47I W XWFV.ijklmnopqrw   4 Cg     htw 4ijklmnopqrtsqwxyz{|}~hgg       C C2     wwxyz{|}~hs   Chicikw  e D E P F G H I J L M K N O >@^`cm}33f P PfEd ''`w) jj~Nou~_cw_gEMWY[]} ' 7 > B D K O q !!! !!!!!!! !"!$!'!.!9!O!!!!!!!" """+"6"<"A"E"I"Y"b"e"k"q""""###!#'#+#$s$$%%%%%%%%&&&&&'&/&S&`&c&f&l&&&'g'',l,w.!)/79=BINjm!0KU^ak 8 PtzbpCb HPY[]_  / 9 B D G O p t !!! ! !!!!!! !"!$!&!.!5!O!S!!!!!!""""#"6"<"A"E"H"Y"`"d"j"n""""### #&#)#$`$$%%%%%%%%&&&&&'&/&9&`&c&e&i&&&'g'v',`,t. ,79=@HMQmp0HMW`X 8ywt`^*' trqljhfe zuqnl]WVRP@1wM=;7510.Z>a7(Y(W(U(N(M(J(H(C(@(>(<(:(8(/(,(+(*(((%((('''''''''%%" R \   !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`ardeixpk+vjsgwql|ZcnTm}b: S Tyqz @ "+ !7 !!f]=]f\H  \R f{D'@$M =M >  +2#"&'&&546462"&=W98%3Z\Z{VZ{VDPJ;V3<DNL3;VR;9VRJ) $ 9K%PX >[Y*% +'54632'74632BH=)+KBG>)+LJ\ )Fy \ )Fy:*KPX@)  dS  K  =>KPX@)  deS  K  >@1  de    TGK?YY@)**)('&#"!   +7#%3#####7367#73>733>733O#T# % m; ( p9 ' s> % k7&&ۇ4Ab8?b;<;<w7 @@;.-(  K!PX@e==N >@be=N >YY@?>=<:9&%$#"! +>5&'.'#7.#&56.'&'476?63CUK_#;+?( Abo B j(%BV Y,H5?{i 9 E#p[I_1 T>pci1EQ5 chJmlrc  } n213TbS )h $>@073 2@8bU  U  UUIMAY@=;64!&$##&$! +%327654"4632#"&327674"4632327'#"'#"&)?03P5vq^1)?/3P5\01 a`nsjFIwq^mDRXou;/LFú{DRXou;/LE-1f+}/&|PD EQL@II" <bM ==N =M >PN@<)##*%+3267&3272#"&'' 4%.5476327676'&47327>54&#"V8qEPxADj%AE$UOmbFugI9Tmƅu33ZCDo`d[J\e ?RE3=KHYw2G06L&_1<_;gxC\wT{ GGj 66 Kg\ 1:D^\hJJ)  9K%PX >% +'54632BH>)+LJ\ )FyJLb "+7&.J 9-=Z5 FAq d#F; "+'7;9-=Z5E`'RAqe6x@  ,!KPX@deM >@deIMAYY&'#'''+&&54632.54632>32#"#".'#"&54>J F_"!'w%:131)h%)'J%`5)-0$-5!6[ ?)Z+w0$#]/R13/ ?uv9"131}Bw DK.PX@SK>@GSK?Y+!!#!5!3s^^'MP;@9[ +2&7>54'.546T_B-d{BJ3Z{3-d+-1J%#^NZ @IMA   +!"5463!2/kL?JA+Z+^d @M > +7462"&dZ{VZ{Vw;VR;9VRjuBKPX@ = >K(PX@ d >@ d[YY@  + #ju`=*@'UM >  +"324."32 #=)5H5 TTX=Q^b1\Zal +T=E$+@("<ddL>''3+%$#&47>54&#"&'6$762F965:F\y+;8H?'#=L#  4 1  #K>H&39{!#ulP4d@ @"b`UL>Y(+/"+4632!2672!'>?>54'&"#"&fɼsX )J!pxU  F^!/-$qL)68CDf H/5DqmT%>-;JgoNhZhsB-HtpEHd) />F^9P@M1<b```UM >0.+)'%  99 +"#"547>32#"'&546323265%"&547>54!-7% 6?N9+DbW+=}hJm~D:+N7BSN)98-79KF5'/-Zdc1r}Zd?#=9KV* t-2:@7<dUL>10#! 22 +)2+".#&47>=!"/.5476>73dy';65BN$56;5)t73!-R=L#  4 1  #K>R% ?Aj*@<; :KPX@'dZUUN >@(dbUUN >Y@ "%$$3!"+4&#"'32%"'632#"'&54632326_KH^{Q%1F0'0mjlR63f4;5Vc-b  /ɝ9+A':P\Z &,@)<UUM >($#+2654&#"67# 476$7^;PtT@Hb nduR#1cr?onw PILb o;n: Job-HS $afZ{1@.<9dIMA  +"'67433! 'y)7BH%B) L{v8 :QN#CF %33@0-<UM >'&&3'3$"   +"654.#"&5467'.546322654&/Z9wfPBUas+oV;hV=en1>[V=51'9B#^DfυݥqK9wZeVs5;#Tf+)\ &,@)<UUM >($#+65#"3267>7.54>3 &;PtT@Hb nduR#1cronw}Ib o;o: Job-HT`fZ{y @M=M>+462"&462"&{Z{VZ{VZ{VZ{V;WR;9WR;WR<9VRRy "@9eM>   +462"&2&7>54'.546{Z{VZ{VT^A.d{BJ3Z;WR;9WRV{3-d+-1J%#^VX"+59o?[?y#/@,SGK? +!5!5yssuuV{T"+%55woZ55D,6/@,b`M =M >+.%%+#"&547632#"&=4>7654&#"462"&>/)Lm`'R95'--+' DCqRRD^ Z{VZ{V>%/A6VZP5^X/'-hD)!$;L>d`@;VR;9VR K@IH G;'&54.#"327# 4$3 #"'&5#"'&54327;^HsdN;o%!^fHRLyq-j+s߮'V!)/+BPZ?L7y}NT3afېd-`^Z@;:S UK>=;4320-)%$ +!#&#"&47>7272&#"&472>&'!"/71x) 5[l)65  N-;K(PX@-ZZUN  =M>@+ZZ UUM>YY@ 76-,)$;:  %"! +3254#"32>54&#263 !"&#&47>54.'&47r\31V`>7qTD#313313'1)9a^5}N%+ 3  #K>5=L#  2 L5D-@* <bM =M >$(&+ '&5!2'.#"3 72bw;# -+/+T+Fy[f1}-2KPX@ZN =M>KPX@M =M>KPX@ZN =M>KPX@M =M>K!PX@ZN =M>K(PX@$ZZN =M>@"ZZUM>YYYYYYY@)# 2/)" +%32>54.#"&47>54.'&473263 !"&Tq?}uJ7\u323314S%9jZL)b@5o˅jyP5  #K>5=L#  2 {ӚA1D@:  <6:K(PX@7 ZZ U N  =M=L>@5 ZZ   U UM=L>Y@A>5430-,'&# DD +26762"'.+;26&#!.7>54&'.7!272'.+"jLF  FPf1#J3) cd  s?@r  ;\50 %3%bj#11R {6% H;+)9 u-5 ;y3{; 2 q#wd='}?1?@#41;8 @,ZU  U  UK>Y@?=:9#73C +%&#"&47>54&'.7!272'.+"326762"'.+<55313@r  ;`50 !&%mj#1jLF  FPf=L# 6 3  #K>3{; 2 ;wn='T1R P`mH H;DD5H@E*<bUM =M >.,! 55 +23'.#"32674&'&47327# '&576)##%5+߻of/d{N\1FZwur  -+bɭfqDHXm˶n$219# :: +3T`V N¿1Z-WkK(PX@%  S K  = K>@#   U  S K>Y@WVQPMIFE<;CCC+4.'&47327&#"&47>5!&#"&47>54.'&47327!413323323314313323314313{/=L#  2  1  #K>=L#  2 5  #K>m=L# 65  #K>5=L#  2  1  #K>1-)AK(PX@K =K>@UK>YCC+4.'&47327&#"&47>5314313313323/=L#  2  1  #K>=L# 65  #K>q-*QK(PX@bQK >@!bUIMAYC%$'+#"&5463232>54&'&47327?$H7-9V +%9#%F͇fb<-ho=%wR?)J)/e{; 2  1 =%-VTA@UM>Y@RQNJGF:5OC+4.'&47327>7676'&47327&#""'&'.'&#"&47>53233143kd?b\f 9[L>=cRRD"7,413323/=L#  2  1  #K>KZA 2  1 IJD%JJ 6 9W,* n=L# 65  #K>1o-.a@ZUK>Y@'&# .- +%2676&#!"&47>54.'&4732>33˖#3%";6O323314S%LA313HV}5  #K>5=L#  2  1  #K>ɢ  -D@ ,-  K!PX@0b`  `K =  K  >K(PX@4b`  `K =  K = >@2b`  `U  K = >YYY@@>;743G" +%#'"'#&#"&47>76&'.73%&#"&47>&M44  m61^{n5|T< R )l  n  e.>)'( X ) * X`R65 NaLq, 2 s 1 5V01065 ,0)-=k@)@!bU=N >Y@ II$+!#&'&#"&#"&47>5.'.774.'&47327 3N*(R"#=RR>" Z= M#>R ! R=#'4/))^R65 R\V-P 2 $@^R 2  1 R\LD '@$M =M >   +"32% 76$3 ϓ㮚s[3-Jӿh% lz1;6?@< <UM =K>10'&# 64 +2"'&53265!"&#"&47>54.'&472$sw+)+<}fN-313323314)4;C^pb;+Im=L# 65  #K>5=L#  2 L+D 7V@S+'&<b``M =M>64#!   +"32232327#".#"'6767.576$3 ϓ㮚h^ɶJu=#Zs?#('#Pxץ`[3-Jӿ(W^^K-DOD,1>` f lz1; BN@K<U M  =K=M > =<32/+('"! B @#! +!"32>23#"'.#&#"&47>54.'&472$THXHhc1sl/TB!}Gs313323314)T+Ogi;yC^+RH? 0 d%=L# 65  #K>5=L#  2 7D2S@bM =M >Y-&,(+'.#"'"&#&5632654'.'&547672^#D#=_=P^RYj}%'F!j^w+D{q }=V`2cVN)``u}p( ^L\^:Zjb6+902KPX@ '<KPX@ '<KPX@ '<@ 'KPX@# M =M =K>KPX@& M =M =K>K%PX@# M =M =K>@! UM =K>YYYY@ .+15C +%&#"&47>54&+""'6743) 72#.+"=F͍F+6dj}7)  9v-5*{;65 ;{de`w { s^fi -8AK(PX@K =N >@UN >YC+L&+32>54&'&47327! '&4&'&473271/jdN- @} I; 7.- 煲;c^  {A-Vm-f?\xRy7 2  1  %E@/Vu,Jy= 2  1 ;-.:@U >YOI"+ "'.'&4732776.'&47327K31B@81{ї+33 "-=2 yFz 5=CX/0b+1 2  1 $\P%+  2  1 @ --6?3@U >YH(J+ 76&'&47327"' '.'&473277?W hJFq 61g[J{ї -11$3VJ:B 2  1 \X/00/0b;+ 2  1 $A59 -SKK(PX@K =K>@UK>Y@ CCCK +%.&#"&47>76&'.'.73277676'&47327&#".7>yQ1 Vh|Og'Q9OB  {‰R$_n- JV{ L_(!=5ZG { PB# ϙZ 2  1 '3!!+O4 2  1 59 m\ 2  1 (3!,1-N4 2  1 9-<EK(PX@K =K>@UK>Y@ CMC+%&#"&47>54&'.'&4732776&'&47327ExF0KE {Z R%9H ?D{ JG!#{965 9{70/R0 2  1 59-'-3 2  1 #8J:q)9*KPX@<KPX@<KPX@<K#PX@<@KPX@,M =M =M=M >KPX@.M =M =M =M >K#PX@,M =M =M=M >K(PX@*M =M =M=M >@(UM =M=M >YYYYY@# *) +%2672#&)"5476#!""'6743)26323od7 '!%y?5%!\ #-+ -Z| pT/ Bu@eK> +! w! ?on u+ ) +NbLD; ) s@e> +#ׅ)jqj@P> +!.7>54.'.7j  w! ?on   ) +NbLC< ) B3@<e > +#3#uVu8n -@GK? +!5/hh5h%KPX@ e >@ d[Y%% +#"/7>32.R2d!)-Z(4;@84)# <bbM=M >)$$$&'#+%# 546?4&#"#"54>3 327'327] 7)RLP J5jO^ 1%yk3A9>/\c+Kov;+w.D &9h?i4L=#s"*5)#5FJ's@  @&=M =M=N >Y@ #%%%+4&'&767262#"'"'6724&#")c !I]oޱj&+  ;VaORf,$ ,LqshQ7!taHL`5@2<b`M=M >#$%"+%#"'&5432"&'.#"327`;Zp ?cI83{V}VpjwN1A7);VsLb6@)5@+bb=M=M >Y@ '"%%+%&5.#"326##"&'.54324&'&7672"'&"G1b53gP}%3%Pb>HG)RB)b !3R -.XT6G#S&07c, h) ( ,LfX?',"$L %7@4 <bUM=M >%%%"$+!654&#"2#"'&5432!3jP3A%pmK!;{3'oj% 8N?fw?owG?%7nK PX@)ZM= M=K>@*bM= M=K>Y@ 75&$%##C +&#"&47>5#"=4;5467632"&'.#"32+;c sw b@7 b  UU U M =M>Y@ PNKI64.,&$SS  #! +4"326"'327>54"&'&"#"'32#"'&7467&'&5467.54762>32R=81>;bG04#5 L ɪg/ \K6faPt>;k7HVd-3?7MN^pX )uRz(m;sZ-+h&V #FuC7%''7/(M[B-LB!d%7t@03 @&  b= M =L>Y@ 64C$C +&#"&47>54#"&#"&47>54&'&767263 9$&C+%&#"&47>54&'&7$724632#"&1cimb2/V F&/KD;JR?;H{765 5{V1'+#se33LT4-RSN%D'3/@,b`M ==>$('$)+4&'&7$72#"&5463232654&54632#"&/V F&/TRDl9wJ! %+5KD;JR?;HV1'+#sfPA9:L=)R  %\8F8)3LT4-RS%P@ 9@-bU=K= L >Y@ONKG@?85210/C +4&'&7672267676&'&4732723&#'54'&'&#&#"&47>)b !,uBA)'V`[8SI'e   ' { #ir+5  bw b<f,$ ,LT'9Z=)66:*p 65%FJ'wE65 C%J @b=L>YC+4&'&7672&#"&47>)b !;c sw b<f,$ ,LZ{C65 C-#X=@:!V>< M= K >USONKG$C$$F +%&#"&47>54&'&7672632>32&#"&47>54#"&#"&47>54#"%E ?mb2/V -VXyg4};c sN D1t61DJND1Hry965 5{V1',#/=PD:Z}C65 GyV$$LyG65 GyVF -7+@(6<M=K>$C$F+%&#"&47>54&'&76726 &#"&47>54#"%E ?mb2/V - a   +"32654&4762'"$yqDfp 7q́}u%!X*8>@; 6+<bM=N =>$(H&"+4&'&76723>32#"'&#"&47>572654&#"0V - ;)f9BFC1b hm b2 ?>{tnH'f*yV1',#-95Tf9X"{7556{fDו+&J!{$0@@=&%<bM=M =L>$&#%!C+&#"&47>5#'"'&5432>32&"3261c im b2 5m5sa"]/=Nb~^V{7556{NMuK.#se\KƜͪ-T/1@.b`M=L>"$!C+&#"&47>54&'&7672362#"'&#"1b hmb21T w+ k5RH.C $%{765 5{V3' 7#d=I)?V"'#6b)%@"<M=M >*&+$+632654'&'&'47632'&#"#"'&b-%H-Lib?Fhuq5Nr+8oO?݅+ o6/q.?kFTXHj1^{1%q.EYFj}-V+.E@B- <dbM= >+)$" .. +347>7>3232+327#"'&'#"=69%6 Y 14'P$%!RfD= #y `E @s'HA-E95}/ ;%}80@-& <bK= >J(C%+"'&'# 4&'&47327327&54&'&473274R -\6;N^+JEJcY5T n+JX?',"'34HG@yG62Vfb87!|{A62Vf 1}9@= >O*C +&47327"'.'&4732776&o`BZ (6(<9 dj  !  #!7660*+/+ - !  66! 1  wN=%}?V@b`= >Y@ J$*H+276&'&47327"' "'&'.'.7327277ۇv "OW-[ )=+5?:34L duTu 5>BLQ668.+/+/+ L4 66:@  [f}O@K=>FB40CI +%&#"&476?6'.#&47327?6&'&47327&#"&47>/&w  RXR >3L@ RR ?[ n7R{ZR{H P RR 9c  65 N J?668+)66V u65 3 %j}E%@"K=M><8'&#!%# +#"'&74632326?>.'.'&47327476.'&47327)R+J1C7 *(/1 +3DRyV# ' A+f5X1H'%5#;/5q !% ^`166?>T! 66L1+|#k(@ %KPX@#b=M=N>KPX@#b=M=N>KPX@#b=M=N>KPX@#b=M=N>@#b=M=N>YYYYY@ ((&#%s+3!2>32326?2%"547''674} '+bR$ܻNR);*//q5/) ==T< |S q NV&7@ R>Y +3"&76'.7>5&63jsqmbH  h` ݞ%/<jy!/v{!\ ' {Vy  7g#@=> +#3{{#sPe&:@eM>Y +&675.76&#&472#&47>krqlbH   ha ݞ %/;sTy!/w1{! ' {V  7f)<@9<:9IUMA  +"'&'>3227#"&'&77 xi1B;57 'T)o%5%$RiA;$w`)#1{5'@$M=M>  +"&5477632"&5462=V97%3[]Z{VZ{V5PJ;^V3&+3"&'&'272#5&'&547E?bJ 9!#T5UEjﯓPJ1B7)d!\ u?Mm cwm%J@ 02KPX@3bU U  U M = >KPX@/bU U  U M  >KPX@3bU U  U M = >KPX@/bU U  U M  >@3bU U  U M = >YYYYY@@>9754.,*)$" JJ +"=4;546762#"'.#"32+23267"'&#"#"&546767659`?ZV{%IR?6-5 y?=%#=2$m=+1t'#~H;!$ K2!$T3 GV )1To 5+5@5 2!O~Y5;!@3^P.)/')^S'/ *B@?!&"*( < :)9UIMA/$+2654&#""''7&547&'763267'̈\d_\F}mXZH#qf`qja^ wqVS)Aohfjhj,3X yXonum5tp35o}]hshq!9wwJJL@I   U SSK>JJIHGF=965,+($C+!&#"&47>=#535#53&'.'.732776&'&47327!!E nF%)X?  {P R#-I+/{FQ#jV{965 9{VVCHOH 2 6#%&#I+664@SGK?Y@ +##{{{d=DBP8@5JCB <b`QM >64.,(&($" +$#"&5463232654.'.547&5467672#"&54654#"654&'&FH%9J!E6HW)V=5G92fL'9BRRJRoh3 rR#/C}'>P9 '-jd%F?'^y[u5}-\)=N#3 ^sXBI'5>lZW,HRN6O/Lwiu'@IMA+462"%462"&A^BB^BA^BB^Af_AA/-Bo/AA_?BB#'12@ K PX@,b UU QM>K PX@/b UUM=M >KPX@/b UUM=M >@,b UU QM>YYYY@)( .,(1)1$#' '#"%" +#"'&54632#.#"327" "32rFPRy-G!+%;@7Lj/\Z3GVX} b.J1j{GL\[9o!!D)+3f@-,"@#bUIMAY@ ,"#($'!+6#"#"&54632327#"''"'&5467736ZH-3 1+Rh3+ BJTD\H(-fPZJD+' "! ;a'!`f++ !9CD!P;gάP%J/B! "+56767.%56767.e +{ZZ{,DCe +{ZZ{,DD!q"' pqP@Z!q"' pqP@1L5#@ eGK? +!5!}nNZ @IMA   +!"5463!2/kL?JA+Z+^B#*4=Gu@/&% K PX@0   U  U U  QM>K PX@3   U  U UM= M  >KPX@3   U  U UM= M  >KPX@3   U  U UM= M  >KPX@3   U  U UM= M  >KPX@0   U  U U  QM>KPX@0   U  U U  QM>KPX@0   U  U U  QM>@0   U  U U  QM>YYYYYYYYY@?>65DB>G?G:95=6=10##$222"+3#"/&#;2&#54;274"'56324"2>" "32b;b%-f&b +os!Hf\\)!'3R\Z/N?N    ^;q:V\[9o!!y' @IMA   +!"7463!2 ')2'49\ ZKPX@UM>K#PX@UM>@UIMAYY+4&"2%462"&CwFHs||jXV75RZZX}#% K PX@SSK>K PX@$SSGK?KPX@SSK>@$SSGK?YYY@   +!!#!5!3!7s^^V՘MPy{{Df-K.PX@!b`OM>@'b`UIK?YY*(*"+463232672!4?654'&#"#"Doz5<;)JW2-H)5yxL(?2 F3w*.ZH?+BM(3L3 ;uvoFTJ)% '"5y2@ 0/KPX@&b`RM=>K.PX@&b`RM=>@$b`UR>YYY@ #%#*%$"+4&#"#"74>32#"'&63232654"5>1%#;+'D Z )h ,@ d[Y$! +632#"&547 N&'' d1/! J%s3K#PX@0<.)(':@0<.)(':YK#PX@bM =>@$bM =M =>Y@ 31-+%#(# +%#"'#"&547654&5.5'%3267%327'h/.7=!L5)mYD! B1%%ycT #j)=FR;3_/H}B1+ns7!+'6j@ @bUK>Y@)(10(6)62g$22 +32!54;>5#".546323?+"2654.'"3; ^ ;31l7qw\@UIMAY@ $% +'73#"'47327& hF)dBAN)f b&Mk"+ TPV_@ @ddIL@Y@ +"&'6762&#".7>54&UX - # ==33+ 4F,'-! (  3233!Pm (@%UIMA&" +4632#"&"32654&P}^>?L!F'9+187o#%CqNcf-?D#Fdq/B! "+&'67&'67&'67&'67f +{ZZ{+DDf +{ZZ{+DD!q"'pqP?Z!q"'pqP?TX"BE@,'% C@2   b  b   T SP >Y@$#ED<;8421+*#B$B$B+'%&'".7>=!'67323#"&'6762&#".7>54&3VTT%(==33+3!g{u5UX - "!== 33+  32 ! (  32+Lj.oV3F-'-! '  3133!d^DX1Qc@`;64< bb`   TUO >32KJGCA@:92Q3Q*(*& +'463232672!4?654'&"#""&'6762&#".7>54&jWSS`oz5<<)JW2-H)6yxL(?2 FUX - "!==33+ 3w*.ZH@+AM(3L3 ;uvnFUJ)% &"3F-'-! '  3133!1\D6UX@43V K  KPX@Eb``   b V PM = K  >@Cb``   b V   S PM >YY@XWUTSRPNJIEDB#%#*%$&+'4&#"#"74>32#"'&63232654#"5>&#".7>=!'67323# 3VTT1%#;+'D+.%%+4&54632#"&54>7>=4632326"&5462;=0)Kl`'R96'--+' DDqRRD] Z{VZ{V>%/B5VZP5_X/'-hD)!%;M=d`?D;WR<9VR?LL@I GE=;4320-)%$ +!#&#"&47>7272&#"&472>&'!"32654'&/71x) 5[l)65  N+Fw P?LL@I GE=;4320-)%$ +!#&#"&47>7272&#"&472>&'!"#"&5476/71x) 5[l)65  N)Hw P LD@A, :S UK> JHA@?=:621+*"  +&'&76?!#&#"&47>7272&#"&472>&'!"%!N\71x) 5[l)65  Nq?Xk@hE R  < U   USUL>A@VTQPMKIGCB@XAX=;4320-)%$ +!#&#"&47>7272&#"&472>&'!"272#"'&#""5>32/71x) 5[l)65  N[XXg+#%%%Rl3ON@K/<US   U K >MKDCB@=954.-%!+462"$462"!#&#"&47>7272&#"&472>&'!"VBbAAbGDb@@b 71x) 5[l)65  N T_@\4< US  UM= K >RPIHGEB>:932*&    +"2654&4632"!#&#"&47>7272&#"&472>&'!"+66V79fHFji{71x) 5[l)65  NV1\c@# *' 7<:K(PX@DZU  SN =M= L  =  K   >@BZUU  SM= L  =  K   >Y@^][ZWSHGDC@;3##93#+76&#"=7!272'.+"326762"'.+;26&#!&47>5!&#".7>!4&;Lb5/ %3%bk#1mI E F Ni1#J3) ces@/!  x) JU8533q#wd='-R {6% H7/)9 u-5 ;y' 65 7 L?5D2@$-+K(PX@0bbM =M =M>@-bbQM =M >YY@22$%3$() +'7&'&5!2'.#"3 72!"##"'47327&h ^w;# -+/+dBAN(f jT+Fy[fH&Ml#+ TPDQ@:  <6;GE :K(PX@< ZZ U = N  =M=L>@: ZZ   V U =M=L>Y@LJA>5430-,'&# DD +26762"'.+;26&#!.7>54&'.7!272'.+"32654'&jLF  FPf1#J5' cd  s?@r  ;\50 %3%bj#1+Z,'!&\1R {6% H;+)9 u-5 ;y3{; 2 q#wd='}+Fw PDQ@:  <6;GE :K(PX@<  d ZZ U N  =M=L>@:  d ZZ   U UM=L>Y@LJA>5430-,'&# DD +26762"'.+;26&#!.7>54&'.7!272'.+"#"&5476jLF  FPf1#J5' cd  s?@r  ;\50 %3%bj#1X.%#\1R {6% H;+)9 u-5 ;y3{; 2 q#wd='})Hw PDQ@:  @5 ZZ   U UM=L>Y@A>5430-,'&# DD +26762"'.+;26&#!.7>54&'.7!272'.+"&'&56?jLF  FPf1#J5' cd  s?@r  ;\50 %3%bj#1!N\1R {6% H;+)9 u-5 ;y3{; 2 q#wd='}'5=D. +T DLT@:  <6;K(PX@A ZZ    UU N  =M=L>@? ZZ    U   UUM=L>Y@$TSPOLKHGA>5430-,'&# DD +26762"'.+;26&#!.7>54&'.7!272'.+"462"$462"jLF  FPf1#J5' cd  s?@r  ;\50 %3%bj#1BbBBb=Db@@b1R {6% H;+)9 u-5 ;y3{; 2 q#wd='}&cCCcAAcCCcA1)6S,*:K(PX@=K =L>@U=L>Y@ *CC+4.'&47327&#"&47>532654'&314313313323#Z+'!'\/=L#  2  1  #K>=L# 65  #K>+Fw P1)6S,*:K(PX@dK =K>@dUK>Y@ *CC+4.'&47327&#"&47>5#"&5476314313313323X-#%\/=L#  2  1  #K>=L# 65  #K>)Hw P1)6L@ 520.,*:K(PX@K =K>@UK>YCC+4.'&47327&#"&47>5&'&56?314313313323!N\~/=L#  2  1  #K>=L# 65  #K>'5=D. +T 1)19[K(PX@! UK =K>@ UUK>Y@ 98CC +4.'&47327&#"&47>5462"$462"314313313323AcAAc>Cc??c/=L#  2  1  #K>=L# 65  #K>bDDbBBbDDbB#}->K PX@"S M =M >KPX@( ZS N =M >KPX@"S M =M >KPX@( ZS N =M >KPX@"S M =M >K!PX@( ZS N =M >K(PX@. ZZS N =M >@, ZZ  USM >YYYYYYY@5/,+&$#!>;!")" +%32>54.#"67&'&47>574.'&473263 !"&Tq?}uJ7\uqdXi323eaV\314S%9jZL)b@5o˅jyPbg5  #K>Jg=L#  2 {ӚA)=V@CP )@4b   U    UU=N >Y@?>TRONKIGEA@>V?VII$+!#&'&#"&#"&47>5.'.774.'&47327272#"'&#""5>32 3N*(R"#=RR>" Z= M#>R ! R=#sB""f9L95B! 5?%'4/))^R65 R\V-P 2 $@^R 2  1 R\sXXg+#%%%Rl3L '3@0:=M =M >"    +"32% 76$3 32654'&ϓ㮚s[Z+'!'\3-Jӿh% lz+Fw PL '3@0:dM =M >"    +"32% 76$3 #"&5476ϓ㮚s[X-%"\3-Jӿh% lz)Hw PL '0@-&#!:M =M >   +"32% 76$3 &'&56?ϓ㮚s[{!N\~3-Jӿh% lz'5=D. +T Lq$3T@Q<U  U  M =N >20*( $$  +272#"'&#""5>32"32% 76$3 fB#"f9L95B 5\3X@$K㮚s[XXg+#%%%Rl33-Jӿh% lzL1*5@2UM =M >)'! +462"$462""32% 76$3 AcAAckCc??c<㮚s[cCCcAAcCCcA\3-Jӿh% lzh% "+''7'77sXTZZVV`VL\'?@<   < :9M =M >$")( +'7&76$327%"'"&324J[۬oLoz-Dl6`\?ĸ lzq;ӿ}תʳ3ci3 8ES;9:K(PX@=K =N >@U=N >Y@ 'C+L&+32>54&'&47327! '&4&'&4732732654'&1/jdN- @} I; 7.- 煲;c^  {AZ+'!'\-Vm-f?\xRy7 2  1  %E@/Vu,Jy= 2  1 ;+Fw P 8ES;9:K(PX@dK =N >@dUN >Y@ 'C+L&+32>54&'&47327! '&4&'&47327#"&54761/jdN- @} I; 7.- 煲;c^  {AX-#$\-Vm-f?\xRy7 2  1  %E@/Vu,Jy= 2  1 ;)Hw P 8EL@ DA?=;9:K(PX@K =N >@UN >YC+L&+32>54&'&47327! '&4&'&47327&'&56?1/jdN- @} I; 7.- 煲;c^  {A`!N\~-Vm-f?\xRy7 2  1  %E@/Vu,Jy= 2  1 ;#'5=D. +T  HWK(PX@ UK =M >@UUM >Y@ C*M' +462"$462"32>54.'&47327! '&4&'&47327AbBBbCc??c1/jdN-  N' J3!煲;c^qN bcCCcAAcCCcAVm-f?\xRRN 2  1 LT/Vu,Jy= 2  1 =<IV?=:K(PX@dK =K>@dUK>Y@ /CMC+%&#"&47>54&'.'&4732776&'&47327#"&5476ExF0KE {Z R%9H ?D{ JG!#5X-%#\{965 9{70/R0 2  1 59-'-3 2  1 #8J:q)Hw P1-7Cs@ 8 "@%U  UUK>Y@ C?&C'5C +4.'&47327632#"'&#"&47>5%32654&#"#314313K~b+7qɁ)313323)bqo_\/=L#  2  1  #K>Hvu{{P E=L# 65  #K> %N@ 76E@.bM==N=N >Y@ 9)'.&*+4&/&=463254>32#"&54623274&'5>54#"&#"&47> Z!VJp}4M=H\/ϐwx3N=4;k}^,{f_7  /w b<  )b``B)!{3_/C7փZ!A/#B_/Jts65 CZh(4C@ 4)# @0ddbbM=M >Y@ %')$$$&'# +%# 546?4&#"#"54>3 327'327#"/7>32] 7)RLP J5jO^ 1%yk3A9>/\.R1c+Kov;+w.D &9h?i4L=#s"*5)#5FJ!)-Zh 6B@ B71@0ddbbM=N >Y@ )$$$&'($! +632#"&547# 546?4&#"#"54>3 327'327 N'''  7)RLP J5jO^ 1%yk3A9>/\d1/!  +Kov;+w.D &9h?i4L=#s"*5)#5FJZN 3?V@S?4.<bb =M=M >><31-+'%! +&''67# 546?4&#"#"54>3 327'327'F3RL4; 7)RLP J5jO^ 1%yk3A9>/\Nq'RXN\'_+Kov;+w.D &9h?i4L=#s"*5)#5FJZAMm@jMB<# <  b  bU  U M = N  >LJA?;953/-'%  +272#"'&#""5>32# 546?4&#"#"54>3 327'327bB#"f9L95B 5\3X@$G 7)RLP J5jO^ 1%yk3A9>/\XXf+#%%%Rl3+Kov;+w.D &9h?i4L=#s"*5)#5FJZ:FK@HF;5<b  bUM=M  >EC:8$$&'% +462"%462"&# 546?4&#"#"54>3 327'3275B^BB^AB^BB^B 7)RLP J5jO^ 1%yk3A9>/\f_AA/-Bo/AA_?B+Kov;+w.D &9h?i4L=#s"*5)#5FJZR =Ia@^I>8<b  bU M =M=M  >HF=;751/+)#!   +"2654&4632"# 546?4&#"#"54>3 327'327+66V79fHFji 7)RLP J5jO^ 1%yk3A9>/\ 9+);;)-7dbHFde+Kov;+w.D &9h?i4L=#s"*5)#5FJLy V~@{I P6  +<b  b  U  M  = M =M >TQLJGEA?97/-!VV   +">74&67726%2672#"'.'#"'.54$%#"#"&5463263 #T)D%-N  =/iF9ӦQ ;9B*68=37w`%d^Le#y7  hmJ3B3 <;?9 LQ,`6;?7 7?+XCLC3LH`1@#,*KPX@7b` bM=M =M>@4b` bQM=M >YY@11$%3#$( +'7&'&5432"&'.#"3272#"##"'47327&b Z[ ?cI83{V};ZdBAN)f VwN1A7);Vs@Vp@&Mk"+ TPLh4@/ddbVM=M >Y@ %%%"$%% +#"/7>32!654&#"2#"'&5432!3׍/R1]P3A%pmK!;{d!)-'oj% 8N?fw?owG?Lh 3@ @/ddbVM=M >Y@ %%%"$$! +632#"&547!654&#"2#"'&5432!3B N''' .P3A%pmK!;{d1/! 'oj% 8N?fw?owG?LN 0R@O<bU =M=M >0.)'"  +&''67!654&#"2#"'&5432!3'F3RL4;RP3A%pmK!;{Nq'RXN\'_'oj% 8N?fw?owG?L7 K PX@/  bU  UM= M>KPX@/  bU  UM= M>KPX@/  bU  UM= M>KPX@/  bU  UM= M>KPX@/  bU  UM= M>@/  bU  UM= M>YYYYYY@750.%"$ +462"%462"&!654&#"2#"'&5432!3BA^BB^BA^BB^AP3A%pmK!;{f_AA/-Bo/AA_?B'oj$!8N?fw@ovH?1wh+KKPX@b ==L>@dd=L>YC%%+#"/7>32&#"&47>54&'&7$72/S11cimb2/V F&/d!)-{765 5{V1'+#se5wh *R@dd=L>YC$!+632#"&547&#"&47>54&'&7$72f N''' 1cimb2/V F&/d1/! {765 5{V1'+#se+wN '1@.< ==L>$#  +&''67&#"&47>54&'&7$72oF4RL3;1cimb2/V F&/Nq'RXN\'_{765 5{V1'+#sew.&@#U=L>C+462"%462"&&#"&47>54&'&7$72B^AA^AB^AA^B1cimb2/V F&/f_AA/-Bo/AA_?B{765 5{V1'+#seH!,9@6<  :M=M >! (& ,!,# +47632&''7&'7>7#"2654'&#"H/9=ef{1o=3J)1GJpNXsm݅ eHPJo+FX:r Jk²}T݉-P]@ZO5 <U U M=  L >NLHGD@=<8621#  +272#"'&#""5>32&#"&47>54&'&76726 &#"&47>54#"B#"f9L95B 5\3X@$%E ?mb2/V - a@ ddM=M >Y@&$%% +#"/7>32"32654&4762'"$/S10yqDfp d!)-q́}uLh &h@ ddM=M >Y@%#$! +632#"&547"32654&4762'"$B N''' |yqDfp d1/! q́}uLN #=@:< =M=M > "     +&''67"32654&4762'"$mF4RL3;yqDfp Nq'RXN\'_q́}uL#1T@Q<U U M= N >0.)'##  +272#"'&#""'>32"32654&4762'"$B""f9L95B! 5\3X@%:yqDfp XXf+#%%%Rl3q́}uL*5@2UM=M >)'"  +462"%462"&"32654&4762'"${A_AA_BA_AA_AyqDfp f_AA/-Bo/AA_?Bq́}u9 QKPX@SQM>@SQM>Y@ +462"&462"&!7KhHLgHKhHLgH!2HE21GD2HD21GC{{V'$B@?  <: 9M=M >!)' +'"''7&54762"&72654K[ܟydJ`pm??hBsDf@$ddbK= >Y@ J(C%%%+#"/7>32"'&'# 4&'&47327327&54&'&47327.R14R -\6;N^+JEJcY5T n+Jd!)-X?',"'34HG@yG62Vfb87!|{A62Vf%h Ft@4@$ddbK= >Y@ J(C%$!+632#"&547"'&'# 4&'&47327327&54&'&47327y N&'' E4R -\6;N^+JEJcY5T n+Jd1/! X?',"'34HG@yG62Vfb87!|{A62Vf%N CJ@G1<b =K= >>:0.&"  +&''67"'&'# 4&'&47327327&54&'&47327uF3SL3; 4R -\6;N^+JEJcY5T n+JNq'RXN\'_X?',"'34HG@yG62Vfb87!|{A62Vf%J?@<8<bUK = >EA(C% +462"%462"&"'&'# 4&'&47327327&54&'&47327A^BB^BA^BB^A4R -\6;N^+JEJcY5T n+Jf_AA/-Bo/AA_?BX?',"'34HG@yG62Vfb87!|{A62Vf %jh Sg@!ddK=M>Y@ JF541-!%($!+632#"&547#"'&74632326?>.'.'&47327476.'&47327 N''' )R+J1C7 *(/1 )5DRyV# ' A+f5X1d1/! PH'%5#;/5q !% ^`166?>T! 66L1+|5!f-8D@A/.-<b=M=M =L>$$&"C+&#"&47>54&'.7$7273>32#"'72654&#"1cimb2/VF&/7+g9AFC?>{uoHf{7556{lV- 1 +#sf R3Nf9X"Dו %jW3@0UK =M>NJ9851!%% +462"%462"&#"'&74632326?>.'.'&47327476.'&47327A_AA_BB^AA^B)R+J1C7 *(/1 )5DRyV# ' A+f5X1f_AA/-Bo/AA_?B?H'%5#;/5q !% ^`166?>T! 66L1+|q KQ@N+< US   U K> IG@?><9510*)!    +!"7463!2!#&#"&47>7272&#"&472>&'!" 5" 71x) 5[l)65  NZ 4@S@P@5/<bb UM=M >?=42.,(&"   +!"5463!2# 546?4&#"#"54>3 327'327!5! 7)RLP J5jO^ 1%yk3A9>/\X%-"- +Kov;+w.D &9h?i4L=#s"*5)#5FJ LQ@N,< :US   U K> JHA@?=:621+*" $" +#"&'73267!#&#"&47>7272&#"&472>&'!"fo:T ^71x) 5[l)65  NZV(4AL@I4)# $$)$$$&'# +%# 546?4&#"#"54>3 327'327327#"&'] 7)RLP J5jO^ 1%yk3A9>/\weA^y c+Kov;+w.D &9h?i4L=#s"*5)#5FJPR%DIPP@MI  <6:  S   UK= M>KJMLJPKPHF$M'#&! +#"'&5467&472>&'!"&#"&47>7272&'327!#jR33PxSs ( s{% l1x) 5[l)6]V%6 Z%=IX@UI>/31<<b``bM= =>++$$&'$4" +#"&547"#"'# 546?4&#"#"54>3 327327327'?Pm 7)RLP J5jO^ 1%1/RL)N%mk3A9>/\5<^R|w+Kov;+w.D &9h?i4L=#.LtT'%%"*5)#5FJL5 *8@5<:dbM =M >$(&%+#"&5476 '&5!2'.#"3 72`X-#$\w;# -+/+)Hw PT+Fy[fL`h +@ @.ddb`M=N >Y@ #$%'$!+632#"&547#"'&5432"&'.#"327! N&'' ;Zp ?cI83{V}d1/! ZVpjwN1A7);VsL5*6@3 <)&$" :bM =M >$(&+ '&5!2'.#"3 72&'&56?bw;# -+/+ N\~T+Fy[f'5=D. +T L`N(I@F&%#! <b` =M=M >((#$%"+%#"'&5432"&'.#"327&''67`;Zp ?cI83{V}F3RL4;VpjwN1A7);Vs]q'RXN\'_L5%7@4<bUM =M >$(&+462" '&5!2'.#"3 72LfLLfw;# -+/+fLLfL@T+Fy[fL`D )A@> <b`M =M=M >#$%$$"+4632#"&#"'&5432"&'.#"327{LC;JR?;H;Zp ?cI83{V}3LT4-RS"VpjwN1A7);VsL5*6@3 <)&$" :bM =M >$(&+ '&5!2'.#"3 724767&bw;# -+/+!N\~T+Fy[fw'5>D/ +T L`h (C@@ < :db`M=M >#$%,+#&'767#"'&5432"&'.#"327g;1RH1;Zp ?cI83{V};`$XTL`$ VpjwN1A7);Vs1} ?G@ :K PX@M =M>KPX@ZN =M>KPX@M =M>KPX@ZN =M>KPX@M =M>K!PX@ZN =M>K(PX@$ZZN =M>@"ZZUM>YYYYYYY@60-,#"?<)/ +4767&32>54.#"&47>54.'&473263 !"& N\~(Tq?}uJ7\u323314S%9jZL)w'5>D/ +T b@5o˅jyP5  #K>5=L#  2 {ӚAL!H@; 1GKPX@7bb =M =M=M  >@:bbbM =M=M  >YY@FE:9870.'%#"   +2'>54#"54&5.#"326##"&'.54324&'&7672"'&7D+\B;@^"G1b53gP}%3%Pb>HG)RB)b !3R -[;=L3# X)%TF,.XT6G#S&07c, h) ( ,LfX?',"$#}->K PX@"S M =M >KPX@( ZS N =M >KPX@"S M =M >KPX@( ZS N =M >KPX@"S M =M >K!PX@( ZS N =M >K(PX@. ZZS N =M >@, ZZ  USM >YYYYYYY@5/,+&$#!>;!")" +%32>54.#"67&'&47>574.'&473263 !"&Tq?}uJ7\uqdXi323eaV\314S%9jZL)b@5o˅jyPbg5  #K>Jg=L#  2 {ӚALqC@0B@5bb  T=M=M  >Y@A@8653!"'"%% +%&5.#"326##"&'.5432574.'&767267&'"'&"G1b53gP}%3%Pb>HG)RBzYXi$*7 !OI9L3R -.XT6G#S&07c,f61!( ,L!fX?',"$q P@F &@> ZZ  U   UUM=L>Y@$ MJA@?<9832/*#  P P   +!"5463!226762"'.+;26&#!.7>54&'.7!272'.+"L!5!jLF  FPf1#J5' cd  s?@r  ;\50 %3%bj#1 %.#-1R {6% H;+)9 u-5 ;y3{; 2 q#wd='}L 1O@L<b UUM=M >1/*(#!   +!"5463!2!654&#"2#"'&5432!3!6!XP3A%pmK!;{X%-"-'oj% 8N?fw?owG? Q@ G '@= ZZ U   U UM=L>Y@ NKBA@=:9430+$! QQ$" +#"&'7326726762"'.+;26&#!.7>54&'.7!272'.+"ygo9T jLF  FPf1#J5' cd  s?@r  ;\50 %3%bj#1jmo87B1R {6% H;+)9 u-5 ;y3{; 2 q#wd='}LV 2H@E< :bUUM=M >%%%"$$" +327#"&'!654&#"2#"'&5432!3%wdB^y P3A%pmK!;{VPR'oj% 8N?fw?owG?L@B  "<> ;K(PX@? ZZ U U N  =M=L>@= ZZ U   U UM=L>Y@ IF=<;854/.+& L L +462"26762"'.+;26&#!.7>54&'.7!272'.+"LfLLfTjLF  FPf1#J5' cd  s?@r  ;\50 %3%bj#1fLLfL@1R {6% H;+)9 u-5 ;y3{; 2 q#wd='}LD 1C@@<bUM =M=M >%%%"$$" +4632#"&!654&#"2#"'&5432!3hLD;JR?;IP3A%pmK!;{3LT4-RS'oj% 8N?fw?owG?%1U@ #41;8 H U  <:K(PX@AZ  Z  UN = M= L = M>@?Z  ZU  U M= L = M>Y@TRNLEB?=:9#936!+#"'&5467!.7>54&'.7!272'.+"326762"'.+;26&'327R34PxT,d  s?@r  ;\50 %3%bj#1jLF  FPf1#J5' BJGdGN%q!5nH15 ;y3{; 2 q#wd='}1R {6% H;+)9 u-\V%LB 7@ @1bbcUM=M >Y@ %%%%($ +!654&#"2327#"&547&'&5432!3jP3A%a98)N%'?PmomK!;{3'oj% 8N?48kG'%%05;^Rqew?owG? Q@G '@5 ZZ   U UM=L>Y@ NKBA@=:9430+$! QQ +4767&26762"'.+;26&#!.7>54&'.7!272'.+"Z!N\jLF  FPf1#J5' cd  s?@r  ;\50 %3%bj#1w'5>D/ +T 1R {6% H;+)9 u-5 ;y3{; 2 q#wd='}LJ 0E@B< :dbUM=M >%%%"$+#&'767!654&#"2#"'&5432!3+f;1RH1P3A%pmK!;{`%XTL`%'oj% 8N?fw?owG?D BQ@N7#< :bUM =M > ;9.*'&! BB +&'&56?23'.#"32674&'&47327# '&576u!N\##%5+߻of/d{N\1FZwur  -+bɭfq'5=D. +T HXm˶n$219# :: +3T`V N¿N "^@ W+N2G@=  b  UU U  = M = M  >Y@&$#[YVTA?971/)'#^$^""  +&''674"326"'327>54"&'&"#"'32#"'&7467&'&5467.54762>32%F3RL3;=81>;bG04#5 L ɪg/ \K6faPt>;k7HVd-3?7MNq'RXN\'_^pX )uRz(m;sZ-+h&V #FuC7%''7/(M[B-LB!dD BY@V7#< :bUUM  =M > ;9.*'&! BB$" +#"&'7326723'.#"32674&'&47327# '&5761fo9T ё##%5+߻of/d{N\1FZwur  -+bɭfqjmo87HXm˶n$219# :: +3T`V N¿ $`@Y-P4I < :KPX@A  b U  U U M = M  = M  >@?  b U  U U  U M = M  >Y@ &%][XVCA;931+)%`&`$$#$$"+327#"&'4"326"'327>54"&'&"#"'32#"'&7467&'&5467.54762>32weA^y =81>;bG04#5 L ɪg/ \K6faPt>;k7HVd-3?7MPQN^pX )uRz(m;sZ-+h&V #FuC7%''7/(M[B-LB!dD=R@O 2<bUUM  =M > 64)%"! = = +462"23'.#"32674&'&47327# '&576LfLLf'##%5+߻of/d{N\1FZwur  -+bɭfqfLLfLhHXm˶n$219# :: +3T`V N¿D #_@X,O3H @A  b  U U  UM = M = M  >Y@ %$\ZWUB@:820*($_%_####$"+4632#"&4"326"'327>54"&'&"#"'32#"'&7467&'&5467.54762>32PLC;JR?;H=81>;bG04#5 L ɪg/ \K6faPt>;k7HVd-3?7M3LT4-RS^pX )uRz(m;sZ-+h&V #FuC7%''7/(M[B-LB!dDDKX@U@,<9beUM  =M >DB730/*(!KK +2&7>54./&54623'.#"32674&'&47327# '&576;Xl;J DFT##%5+߻of/d{N\1FZwur  -+bɭfqhHXTy)@  M3HXm˶n$219# :: +3T`V N¿-i@b6Y=R"<:KPX@?  d  b  UU M =M  = M  >@=  d  b  UU U M = M  >Y@&/.! fda_LJDB<:42.i/i(& -!- +"&54674"326"'327>54"&'&"#"'32#"'&7467&'&5467.54762>32;Xl;J DFT=81>;bG04#5 L ɪg/ \K6faPt>;k7HVd-3?7MHXTy)@  M3>^pX )uRz(m;sZ-+h&V #FuC7%''7/(M[B-LB!d1Z d@ :K(PX@%  S K  = K>@#   U  S K>Y@dc^]ZVSRIHEA>=8721.*'& +&'&56?4.'&47327&#"&47>5!&#"&47>54.'&47327!7 N\~413323323314313323314313{'5=D. +T =L#  2  1  #K>=L#  2 5  #K>m=L# 65  #K>5=L#  2  1  #K>% B@; > @(  b = M =L>Y@A?:987/.+'$# +&''67&#"&47>54#"&#"&47>54&'&767263 9F3RL3;j@/US  S  K   >Y@!feb^[ZVTSQLKHDA@;:541-"CA@+&#"!327.'&4732767&'&#"&47>5!&#"&47>57.'&47327w{_200/02C7323314313323) 2000/3[4?  2  1 =3f =L#  2 5  #K>m=L# 65  #K>f3>  2  1 ?D@7 @ @0  b   T = M =L>Y@CA?=<:6543#C$C+&#"&47>54#"&#"&47>574.'&767267&'63 9@(U U V  K  >Y@>=:632)(%!  +272#"'&#""5>324.'&47327&#"&47>5B#"f9L95B 5\3X@$314313313323XXg+#%%%Rl3=L#  2  1  #K>=L# 65  #K>"w5H@E<U U=L>21#  +272#"'&#""5>32&#"&47>54&'&7$72; ]4D30; 0T.O:!v1cimb2/V F&/XXf+#%%%Rl3D{765 5{V1'+#se1q 5bK(PX@ UK =K>@UUK>Y@10-)&%  +!"5463!24.'&47327&#"&47>5!6!T314313313323 %.#-%=L#  2  1  #K>=L# 65  #K>w (,@)U=L>%$   +!"5463!2&#"&47>54&'&7$722i1cimb2/V F&/X%-".{765 5{V1'+#se1 6\ :K(PX@UK =K>@UUK>Y@ CC$"+#"&'732674.'&47327&#"&47>5fo:T 314313313323jmo87=L#  2  1  #K>=L# 65  #K>2wV *)@& :U=L>C%"+3267#"&'&#"&47>54&'&7$72njZOs; Vm 1cimb2/V F&/VQQIY{765 5{V1'+#se1%-:g@ :@ UK=M>Y@ $#C&! +#"'&5467"&47>54.'&47327&'3277R33PxS323314313313SUGeHN%q!5nH15  #K>5=L#  2  1  #K>=L# 6]V%5%wD.:<@9.<M ==K=N>$$$#6!+#"'&5467#"&47>54&'&7$72&#3274632#"&7R33PxSmb2/V F&/1cH FcHN%KD;JR?;Hq!5nH15 5{V1'+#seh{76\V%3LT4-RS11TK(PX@UK =K>@UUK>Y@ CC+462"4.'&47327&#"&47>5LfLLf[314313313323fLLfL=L#  2  1  #K>=L# 65  #K>5w@=L>C +%&#"&47>54&'&7$721cimb2/V F&/{765 5{V1'+#se1-*ToK(PX@)  bQK =  K  >@'  b UQ  K  >Y@POLHEDCC%$' +#"&5463232>54&'.73274.'&47327&#"&47>5/%G8-9V!+%9#%E  ͇f b<314313313323-ho=%wR?)J)/e{; 2  1 =w=L#  2  1  #K>=L# 65  #K>5%D'3P\C@@b` M  ==L=>[YUSC$('$) +4&'&7$72#"&5463232654&54632#"&&#"&47>54&'&7$724632#"&s/V F'/TRDl9wI! %+5LD;JR@;Hw1cimb2/V F&/KD;JR?;HV1'+#sfPA9:L=)R  %\8F8)3LT4-RSj{765 5{V1'+#se33LT4-RSq 7c@ :K(PX@bQK >@!bUIMAY@652.+*#! +&'&56?#"&5463232>54&'&47327!N\$H7-9V +%9#%F͇fb<'5=D. +T ho=%wR?)J)/e{; 2  1 =N%ZN 2=@:<b` ==>-+$"  +&''674&'&7$72#"&5463232654&5F3RL4@ eUM>Y@hgd`]\PK95&%" +2&7>54./&5464.'&47327>7676'&47327&#""'&'.'&#"&47>5;Xl;J DF3233143kd?b\f 9[L>=cRRD"7,413323hHXTy)@  M3=L#  2  1  #K>KZA 2  1 IJD%JJ 6 9W,* n=L# 65  #K>%f@ O<9KPX@0 e  U= =K= L >@3b e  U=K= L >Y@eda]VUNKHGFE2.+*$# +2&7>54./&5464&'&7672267676&'&4732723&#'54'&'&#&#"&47>j;Ym;J DE)b !,uBA)'V`[8SI'e   ' { #ir+5  bw b@;.)E@<U=K=L>>$5C +%&#"&47>54&'&7$72>7676&/&75737#&#'54'&'&#1cimb2/V F&/*^2Q+ H=`' 5>;P!#hZ{765 5{V1'+#se&,g/7-- ;'R 55%FJ'1o ;r@ <:K(PX@#dZK =K>@!dZUK>Y@ 430*'& ;:% +#"&54762676&#!"&47>54.'&4732>33X-#%\o#3%";6O323314S%LA313H)Hw P!}5  #K>5=L#  2  1  #K>ɢ% )[@ <:KPX@d= =L>@db=L>YC%+#"&54764&'&7672&#"&47>X-%"\)b !;c sw b<)Gw Of,$ ,LZ{C65 C1o-Dx@ <9K(PX@$ZeK =K>@"ZeUK>Y@=<930/&%"DC +2&7>54./&54672676&#!"&47>54.'&4732>33;Xm;J DFӖ#3%";6O323314S%LA313HhHXTy)@  M3}5  #K>5=L#  2  1  #K>ɢ%2b@ <#9KPX@e= =L>@be=L>Y@ 22C+4&'&7672&#"&47>2&7>54./&546)b !;c sw bK(PX@1bZM =K = K>@/bZUM = K>YY@985/,+"!@?  +2'>54#"'62676&#!"&47>54.'&4732>33q7D+\B;@^F#3%";6O323314S%LA313H[;=L3# X)%TF}5  #K>5=L#  2  1  #K>ɢ%+.@  KPX@%b =M=L>@(bbM=L>YY@-,)%"!  +2'>54#"544&'&7672&#"&47>7D+]A;@^})b !;c sw b<[;=L3# X)%TFnf,$ ,LZ{C65 C1o- :K PX@(ZK =M=K>KPX@(ZK =M=K>KPX@(ZK =M=K>KPX@(ZK =M=K>KPX@(ZK =M=K>KPX@(ZK =M=K>KPX@(ZK =M=K>K(PX@(ZK =M=K>@&ZUM=K>YYYYYYYYY@ 32/)&% : 9$" +4632#"&2676&#!"&47>54.'&4732>33LC;JR?;H-#3%";6O323314S%LA313H3LT3-RRw}5  #K>5=L#  2  1  #K>ɢ%- (WK PX@ = =M=L>KPX@ = =M=L>KPX@ = =M=L>KPX@ = =M=L>KPX@ = =M=L>KPX@ = =M=L>@#b=M=L>YYYYYYY@ C$"+4632#"&4&'&7672&#"&47>KD;JR?;H)b !;c sw b<3LT4-RS"f,$ ,LZ{C65 C1o-:j@7542 @ZUK>Y@-,)#  :9 +%2676&#!"&47>5'674.'&4732>3673˖#3%";6O323Z8$~8314S%LA313Y;%TeHV}5  #K>58'bE =L#  2  1  #K>:)b.<(V@!'&$@b=L>YC+&#"&47>5'674&'&767267;c sw b<@J$Q])b !FB$N{C65 C{X*3b,8f,$ ,L-.b*) J{@*6 <:K(PX@(dbK ==N >@&dbU=N >Y@ II$%+#"&5476#&'&#"&#"&47>5.'.774.'&47327LX-#%\h3N*(R"#=RR>" Z= M#>R ! R=#)Hw P'4/))^R65 R\V-P 2 $@^R 2  1 R\-h Em@ D*@"ddM=K>Y@ $C$K$! +632#"&547&#"&47>54&'&76726 &#"&47>54#" N''' %E ?mb2/V - a@'beU=N >Y@KG>=:965+' +2&7>54./&746%#&'&#"&#"&47>5.'.774.'&47327;Xl;K  DEZ3N*(R"#=RR>" Z= M#>R ! R=#hHXTy)@  M3h'4/))^R65 R\V-P 2 $@^R 2  1 R\-MB@?L2<9eM=K>KIEDA=:953/.  +2&7>54./&546&#"&47>54&'&76726 &#"&47>54#"L;Xm;J DFV%E ?mb2/V - a@!bU=N >Y@ II$+4767&#&'&#"&#"&47>5.'.774.'&47327 N\~3N*(R"#=RR>" Z= M#>R ! R=#w'5>D/ +T '4/))^R65 R\V-P 2 $@^R 2  1 R\-J B@@=A'< :dM=K>@>:962/.*($# +#&'767&#"&47>54&'&76726 &#"&47>54#"g;2QH2%E ?mb2/V - a@+bM = M=K>Y@GEA@=9651/+*  +2'>54#"54&#"&47>54&'&76726 &#"&47>54#"7D+\B;@^$F @m b1/V - a;csND1=[;=L3# X)%TFdy965 5{V1',#=G}C65 GyV`DN@?E ! @6  bb UQ M =L>Y@JHCB>=3C'%$& +%#"&54632326'.#"&#"&47>54&'&47327226?>32%H7-9V!*%:B V-HC?s Z s??s ͍?% Xu`6fq=%wR?)J)/}?/$)|y;65 ;y5y; 2 R&{J>-$+D9@6A"<bM==M>$J('$&+#"&5463232654&54&#"&#"&47>54&'&76726$+DMBt/d> 5%5^FUV%E ?mb2/V -dD?7=?4"C  n %#     +!"5463!2"32% 76$3 !5!œ㮚s[ %.#-3-Jӿh% lzL $8@5UM=M > #!     +!"7463!2"32654&4762'"$B 5!yqDfp X%-"-q́}uL '8@5 :UM =M > &$ $" +#"&'73267"32% 76$3 go9T 퓻㮚s[jmo87D3-Jӿh% lzLV %8@5 :UM=M > $" $" +327#"&'"32654&4762'"$PwdB^y yqDfp VPRq́}uL ,;hK,PX@"ddM =N >@&dddM =N >Y@"!:820(&!,",'& +#"&54?6"'&54?632"32% 76$3  B  u) 㮚s[% (  D3-Jӿh% lzL9)7pK1PX@%b =M=M >@+b` =M=M >Y@ 64/-%#) )%&&$ +#"&54?672#"74762"32654&4762'"$  $%/ x 2#3iyqDfp  %4   ,*q́}uX1:G@0 <,:K(PX@, U M =M= N>@* U UM= N>Y@ =;C@;G=G74+*)%" :: +26762"'.+;26&#! !!272'.+"3254+"mIF  FNh1#I3* bCq'\50 %4%bj#1;;F-R {6% H7/)9 u-s5q#wd='b3L^ 0;]@Z &<b  U M=M =M >8621/-)'%$#!  +"32654&4762>32!3272#"'&'#"$!654&#"yqDfpBTmL!;{jpP3A%7q́EJ?owG?l?frR'oj% 81 OY@V <:dU M  = K= M >JI@?<854/.*($#OM#'% +#"&5476!"32>23#"'.#&#"&47>54.'&472$X-%"\>THXHhc1sl/TB!}Gs313323314)T)Hw P+Ogi;yC^+RH? 0 d%=L# 65  #K>5=L#  2 -T =~@.bbUM=L>Y@ "$!C$! +632#"&547&#"&47>54&'&7672362#"'&#" N''& w1b hmb21T w+ k5RH.C $10! {765 5{V3' 7#d=I)?V"'#61;!X`@])<9 eU M  = K= M >#"SRIHEA>=8731-,"X#V +2&7>54./&546!"32>23#"'.#&#"&47>54.'&472$;Xm;J DF+THXHhc1sl/TB!}Gs313323314)ThHXTy)@  M3$+Ogi;yC^+RH? 0 d%=L# 65  #K>5=L#  2 -TEH@E9b`eM=L>A?=;75430/! +2&7>54./&746&#"&47>54&'&7672362#"'&#"9;Ym;J DE1b hmb21T w+ k5RH.C $hHXTy)@  M3{765 5{V3' 7#d=I)?V"'#61 OW@T < :U M  =K=M >JI@?<854/.*($#OM#. +4767&!"32>23#"'.#&#"&47>54.'&472$!N\THXHhc1sl/TB!}Gs313323314)Tw'5>D/ +T +Ogi;yC^+RH? 0 d%=L# 65  #K>5=L#  2 -TJ :?@< :db`M=L>"$!C+#&'767&#"&47>54&'&7672362#"'&#"f;1RH1V1b hmb21T w+ k5RH.C $`%XTL`%{765 5{V3' 7#d=I)?V"'#672?d@ <53:KPX@dM ==M >@"dbM =M >Y'-&,(+'.#"'"&#&5632654'.'&547672#"&5476^#D#=_=P^RYj}%'F!j^w+D{qX-%#\ }=V`2cVN)``u}p( ^L\^:Zjb6)Hw Pbh)7c@ /@ddM=M >Y$$*&+$+632654'&'&'47632'&#"#"'&632#"&547b-%H-Lib?Fhuq5Nr+8oO?݅y N''' + o6/q.?kFTXHj1^{1%q.EYFj}-V1/! 7 ?c@)< :KPX@M ==M >@bM =M >Y@ >***4*4*&+$+632654'&'&'47632'&#"#"'&&''67b-%H-Lib?Fhuq5Nr+8oO?݅F3SL3;+ o6/q.?kFTXHj1^{1%q.EYFj}-Vq'RXN\'_7HDD@ 6?=KPX@. bM ==M =M>KPX@+ bQM ==M >@.b bQM =M >YYY@DD$%*-& +'7.#&5632654'.'&547672'.#"#"'473254{ W*'F!j^w+D{q #D#=_=P^RYjtcBBN)f & ^L\^:Zjb6}=V`2cVN)``u}h@&Mk"+ TPbH;>@7'0.KPX@(bM=M =M>KPX@(bM=M=M>KPX@(bM=M =M>KPX@(bM=M=M>@%bQM=M>YYYYY@ $%&+$+632654'&'&'47632'&#"#"'473254/7&'&b-%H-Lib?Fhuq5Nr+8oO?cBAN)f We+ o6/q.?kFTXHj1^{1%q.EYFjt A&Mk"+ TP $V7 ?c@)< :KPX@M ==M >@bM =M >Y@ >D/ +T }=V`2cVN)``u}p( ^L\^:Zjb6bJ)43@0<320.-:dM=M >*&+$+632654'&'&'47632'&#"#"'&#&'767b-%H-Lib?Fhuq5Nr+8oO?݅if;1RH1+ o6/q.?kFTXHj1^{1%q.EYFj}-V`%XTL`%H+9BKPX@!4 = ; <KPX@!4 = ; <KPX@!4 = ; <@!4 = ; KPX@7 bM =M = K = M  >KPX@: bM =M = K = M  >KPX@7 b QM =M = K >K%PX@4 b QM =M = K >@2 bU QM = K >YYYYY@BB@>:831.-215"+'7"&47>54&+""'6743) 72#.+"&##"'47327&= asF+6dj}7)  9v-5*Fx#dBAN(f 5 ;{de`w { s^fi{;6T&Mk"+ TP+H@@ ,.2 ; 9@/db   bQM= >Y@@@$%%#$(#( +'7&'&'#"=6;47>7>3232+327#"'47327& [C(= #39%6 Y 14'P$%QdBAN)f #5}/ ; `E @s'HA,E@&Mk"+ TP+ =VKPX@)4'< :KPX@)4'< :KPX@)4'< :@)4'< :YYYKPX@& M =M =K>KPX@# M =M =K>KPX@& M =M =K>K%PX@# M =M =K>@! UM =K>YYYY@ ;815C +4767&&#"&47>54&+""'6743) 72#.+"!N\F͍F+6dj}7)  9v-5*w'5>D/ +T {;65 ;{de`w { s^fi+@@ ?02@5b`bM = M = >Y@=;64/-(&#!@@  +2'>54#"54347>7>3232+327#"'&'#"=6j7D+\B;@^w9%6 Y 14'P$%!RfD= #[;=L3# X)%TF `E @s'HA-E95}/ ;+9<lKPX@ "- <KPX@ "- <KPX@ "- <@ "- KPX@-  S M = M  =K>KPX@0  S M  = M  =K>K%PX@-  S M = M  =K>@+ U  S M  =K>YYYY@<:9741/.*)13!#C+%&#"&47>574&+""'6743) 72#.+"67&'=F͍FifPk+6dj}7)  9v-5*idNk{;65 ;{g'de`w { s^fig+;W@T:$&<db S M = >86421/*(#! ;; +347>7>3232+67&'327#"'&'47767#"=69%6 Y 1x{mq4'P$%!RfD=@(U U    UN >Y@POLHED><1-!  +272#"'&""'>3232>54&'&47327! '&4&'&47327B""f9L95B! 5\3X@%\1/jdN- @} I; 7.- 煲;c^  {ARXXf+#%%%Rm3Vm-f?\xRy7 2  1  %E@/Vu,Jy= 2  1 ;%Qa@^? $" <  bU  U K  = >LH><40-,(&!   +272#"'&#""5>32"'&'# 4&'&47327327&54&'&47327B#"f9L:5A 5\3X@$4R -\6;N^+JEJcY5T n+JXXf+#%%%Rl3ZX?',"'34HG@yG62Vfb87!|{A62Vf m DbK(PX@ UK =N >@UUN >Y@CB?;871/$   +!"7463!232>54&'&47327! '&4&'&47327? 5"~1/jdN- @} I; 7.- 煲;c^  {A%.#-'Vm-f?\xRy7 2  1  %E@/Vu,Jy= 2  1 ;% DG@D2<bUK= >?;1/'#   +!"5463!2"'&'# 4&'&47327327&54&'&47327N!5!4R -\6;N^+JEJcY5T n+JX%-"-X?',"'34HG@yG62Vfb87!|{A62Vf  E\ :K(PX@UK =N >@UUN >Y@ C+L)$"+#"&'7326732>54&'&47327! '&4&'&47327Dgo9T 1/jdN- @} I; 7.- 煲;c^  {Ajmo87Vm-f?\xRy7 2  1  %E@/Vu,Jy= 2  1 ;%V EA@>3< :bUK= >J(C%$"+327#"&'"'&'# 4&'&47327327&54&'&47327dweA^y 4R -\6;N^+JEJcY5T n+JVPRX?',"'34HG@yG62Vfb87!|{A62Vf s LK PX@)UM = K =N >K(PX@'UU K =N >@%UU UN >YY@ KJC+L'& +"26544632"32>54&'&47327! '&4&'&47327`V55V8gGFjh1/jdN- @} I; 7.- 煲;c^  {A-9+)<<)+qecGFdVm-f?\xRy7 2  1  %E@/Vu,Jy= 2  1 ;%T MU@R; <bU M =K = >HD:80,)($"   +"2654&4632""'&'# 4&'&47327327&54&'&47327Z+55V79fHFjh4R -\6;N^+JEJcY5T n+J9+);;)+9dbHFdX?',"'34HG@yG62Vfb87!|{A62Vf !ZK(PX@#dd K =N >K,PX@!dd UN >@%ddd UN >YY@ YXC+L''&& +#"&54?63"'&54?63232>54&'&47327! '&4&'&47327h   u'1/jdN- @} I; 7.- 煲;c^  {A% (  Vm-f?\xRy7 2  1  %E@/Vu,Jy= 2  1 ;%9W@ E*(@/b`b =K = >Y@ RN(C%%&&$ +#"&54?672#"74762"'&'# 4&'&47327327&54&'&47327}  $%/ x 2#34R -\6;N^+JEJcY5T n+J %4   ,X?',"'34HG@yG62Vfb87!|{A62Vf X-Km@J@!bcUM >Y@ IGL(C%"+#"&547# '&4&'&4732732>54&'&47327327'?Pl=D煲;c^  {A1/jdN- @} I; 7.- NH)N%5<^Rl Vu,Jy= 2  1 ;{Vm-f?\xRy7 2  1  %E@/sHtR'%%%%}IC@@( I<bK= =N>HFJ(C,!+#"'&5467&5&'# 4&'&47327327&54&'&47327327R34PlM\6;N^+JEJcY5T n+J4R x9LGN%q!5nD7'34HG@yG62Vfb87!|{A62VfX?'"PV% - CO@@'< :K(PX@K = >@U >Y@ :962*)%# +&'&56?76&'&47327"' '.'&473277o!N\>?W hJFq 61g[J{ї -11$3'5=D. +T VJ:B 2  1 \X/00/0b;+ 2  1 $A59%N Jx@(@#b` == >Y@IHED:6,*&$ +&''67276&'&47327"' "'&'.'.7327277=F3RL3;v "OW-[ )=+5?:34L duTu 5>Nq'RXN\'_XBLQ668.+/+/+ L4 66:@  [ IP@ :K(PX@K =K>@UK>Y@ CMC+&'&56?&#"&47>54&'.'&4732776&'&47327 N\~4ExF0KE {Z R%9H ?D{ JG!#'5=D. +T {965 9{70/R0 2  1 59-'-3 2  1 #8J:q %jN P>@;< =K=M>GC21.*  +&''67#"'&74632326?>.'.'&47327476.'&47327F3RL4;O)R+J1C7 *(/1 )5DRyV# ' A+f5X1Nq'RXN\'_ZH'%5#;/5q !% ^`166?>T! 66L1+|oN_K(PX@"U K  =K>@ U  UK>Y@EA>=MC +462"%462"&&#"&47>54&'.'&4732776&'&47327A^BB^BA^BB^A{ExF0KE {Z R%9H ?D{ JG!#^BB/-Bo/BB^@B){965 9{70/R0 2  1 59-'-3 2  1 #8J:q) 7KPX@'%<:KPX@'%<:KPX@'%<:K#PX@'%<:@'%<:YYYYKPX@3dM =M =M = M >KPX@1dM =M = M=M >KPX@3dM =M =M = M >K#PX@1dM =M = M=M >K(PX@/dM =M = M=M >@-dUM = M=M >YYYYY@ 0*)($#" 76% +#"&54762672#&)"5476#!""'6743)26323X-%#\d7 '!%y?5%!\ #-+ -)Hw P%| pT/ B#kh 6m@3$KPX@0bb ==M=N>KPX@0bb ==M=N>KPX@0bb ==M=N>KPX@0bb ==M=N>@-ddb=M=N>YYYYY@66&#%x$! +632#"&5473!2>32326?2%"547''674% N&''  '+bR$ܻNR);*//q5/)d1/!  ==T< |S q )2KPX@"  <KPX@"  <KPX@"  <K#PX@"  <@"   KPX@4UM  =M  = M=M >KPX@6UM  =M  =M = M >K#PX@4UM  =M  = M=M >K(PX@2U M =M = M=M >@0U  UM = M=M >YYYYY@ +%$# 2 1 +462"2672#&)"5476#!""'6743)26323 LfLLfd7 '!%y?5%!\ #-+ -fLLfL| pT/ B#kD 4Z@ 1"KPX@-bM ==M=N>KPX@-bM ==M=N>KPX@-bM ==M=N>KPX@-bM ==M=N>@-bM ==M=N>YYYYY@ 4 4&#%u$" +4632#"&3!2>32326?2%"547''674JLC;JR?;H '+bR$ܻNR);*//q5/)3LT4-RS ==T< |S q ) 7KPX@'%< :KPX@'%< :KPX@'%< :K#PX@'%< :@'%< :YYYYKPX@.M =M =M =M >KPX@,M =M =M=M >KPX@.M =M =M =M >K#PX@,M =M =M=M >K(PX@*M =M =M=M >@(UM =M=M >YYYYY@ 0*)($#" 76 +4767&2672#&)"5476#!""'6743)26323b!N\ud7 '!%y?5%!\ #-+ -w'5>D/ +T E| pT/ B#kJ 3C@0!< :K PX@'dZ=M=N>KPX@(db=M=N>KPX@(db=M=N>KPX@(db=M=N>KPX@(db=M=N>@(db=M=N>YYYYY@ 3 3&#%}+#&'7673!2>32326?2%"547''674f;1RH19 '+bR$ܻNR);*//q5/)`%XTL`% ==T< |S q q0n@(bM=M=K>Y@ #&$5C+&#"&47>54&+"'546326547>32#"'.";b rwb; Z 1(n?wR<7d8%1{C65 Cy #0j?C_vJhF- 3@+0 @-b  T=M=N >Y@31/.$%"#" +724&#"'62#"'"'67754&'&767267&';VaORNI]oޱj&+ gJEY)c !oKK]HshQ7!u gf,$ ,LEg-.;D K(PX@/bZ UM  =M>K*PX@-bZ U UM>@3ZbZ U UM>YYY@//CA?=/;/:42(&., +263 !"&#.7>54#"#"&546332>54&#3254#"X7pTD#313v3a"A+#Ap2V`=s\4)N%+ 1  #K>5F, 6(566I1)9a^5'11;@ &<":K!PX@'Z UN =M>K(PX@-ZZ UN =M>@+ZZU UM>YY@222;2:$"9CU +!"&#&47>54.'&473!272'.+"323 4&#D#313313R{5/ %3)`iJ^+G5+ 3  #K>5=L#  2 #yTV1)- 7G@D#'62<:M=M=M >%&98#"+724&#"4&'&7$7!272'.+"62#"'"'6;VaOR)c F5/ %3)`ixBI]oޱj&+ Hf,$5#xC0"!shQ7!tD -x@)bZU =N>Y@ )(!  -- # +32>54&#23 !"&#&47>54&'&47$1V`<!5D#313TV\1)?k`7LϿ 3  #K>}t 1 iN'C@@  <b=M=N >#&%%$+'&47$3262#"'"'65724&#"ժHR!J\oݰj'*  ;W`PR F }LqshQ7!uH?-@* <bM =M >$(&+ !"$'&'63254# "&'jwXœ# -+/+?Tՙ[fPdL*J@G( <bUM =M >'% ** +2#"#'.#"3 72 '&5!2>V'#gB2, # -+/+w;1F34fT+Fy8k{L-^@[%<bb` UM=M >)'$" -- +"&#""&'.#"3272#"'&5432>32H. 7K PX@"S M =M >KPX@( ZS N =M >KPX@"S M =M >KPX@( ZS N =M >KPX@"S M =M >K!PX@( ZS N =M >K(PX@. ZZS N =M >@, ZZ  USM >YYYYYYY@5/,+&$#!>;!")" +%32>54.#"67&'&47>574.'&473263 !"&Tq?}uJ7\uqdXi323eaV\314S%9jZL)b@5o˅jyPbg5  #K>Jg=L#  2 {ӚAm-/@K!PX@!bM =M>K(PX@2ZbN =M=M>@0ZbUM=M>YY@?=42/,'' +!0&47>54&#"#"&54>323263 !"&732>54.#"j3145iLF- 6(566$VR6{ӚAb@5o˅jyPF11;w@  @,ZZUN =M>Y@ #%c12! +6;4+"'>7433!%&#"# $546#"!265y_Jha)3% /5{)313313R#CB}5H+#}Ty# 1  #K>=L#  2 Ӯ)1-L6FP@M :7&<bM=M=M >%&'"15 +505&#"'>7433!2"'&'##"&'.5432&5.#"326Dxha)3%  /5 b*4Q -3%Pb>HG)QC"F1b63fP}v!RDx#6%)iX=',"$0#S&07c,5.XT67 / KPX@bQM=>@bQM=>YY+"$%(+64#"##"&54632254.'.54$32/\J+ymV!lN+^/5>}J :'T;%hrjCsq>+'\f}.A-q1Cn@k  A> :7 * <ZZ  UN = M =L>@?98530-&# CC +34&+"'>7433!67%!.'6;265#""'654'62B1#jb%3% /6\f s?@r H '5Jץ#1hO FF J'=dw#q 1 ;{y;6-u 9)7H %5| R-RTD3@0<SM =M >$&$"+%32'>32! 765&# r7u=uNѨ9w~:NF=D2|@.@&bUM =M >Y@10*(%# 22 + $54675.54>32'#";#"!2676"qX)eΔTw$#CPT{u  %j!E;%ᰍ)'L;mlB}%}urBã (?d1F@")&< :K(PX@2Z   bU U  QN >@8Z   bUU U  I M AY@CA<:'##93 +74&'&47!272'.+"326762"'.+#"&5463232>@s<f50 %3'`j#1mI E F Ni%G8-9W +%9#%{; 2 u#yl='X-R {5% H7sho=%wR?)J)/e{8@ 1(@'bRM =K>Y@5432+)#! 88 +2&'&'"3##"&546323676767'?>ه))/V)##-9=3#U-3hm719% 3/ #[7'E3%jm=E Б}DXH1-3%+b L bDBIN@K7#< UUM =M >DB;9.*'&! II +2#"&#"'.#"32674&'&47327# '&576%2>5*{ +1(%5+߻of/d{N\1FZwur  -+bɭfq9f!?!ZR9Km˶n$219# :: +3T`V N¾%|R+\@6/. Z% <+;KPX@%b  ` K =  >K(PX@$b  ` c K >@+b  ` cGM AYY@YWHEN#q1 +%'3&#&#'"'&75303326;"5#>7656=&#"&#&'5;226;"#"'54b +%1=s!40R1  %3 /2?#;< 4 !  `%{h%-#-+ }-   -  *'A)bL%hF@!$@1b=M=L= N >Y@ CA-$%C# +54&#"&#"&47>54&'&76726 327654'&'&'&54632#"&'&#^FM[/F Jw b<)b !ZRbXPR!;7!7-:P{}AdPJ:HyE65 C{}f+ ( ,Ldy|073Eb)R;U@ @b`= >Y%&+74&'&7672327#"'&)b !3'F%%!RfD=h) ( ,LZHA-E95#-5[K(PX@! SK =K>@U SK>Y@ 53%C!%C +4.'&4732767&'&#"&47>57314313qdXi313323eaV\/=L#  2  1  #K>ng=L# 65  #K>Jg%Dc^3K(PX@*b K =M =M>@(b UM =M>YY@YXUQNMDC@<98$#$" +>32#"'.#"&#""'&'.'&'&#"&47>54.'&47327>7{FzZuT)S 5# >)NC+(waC9 KR ) '/9Lb413323323314&8QM"Ol]K+K8?=U$!%o/6 3]01#17?V=L# 65  #K>5=L#  2  1  #K> 'WB%XR@O@<b  UM=K= K >VURNKJGF?<9876C#&# +47632#"'.#"267676&'&4732723&#'54'&'&#&#"&47>5͑mLw;9-X'w,uBA)'V`[8SI'e   ' { #ir;c sw b<۞w%6-9Bf5!'9Z=)66:*p 65%FJ'{C65 C{(c&@#bV=L>Y@ !#C +67&'&#"&47>574&'&7672fHDX;c sw b@;$,#   <9M=M >(&"!$& +7327#"&''&/4''7&'>3265HG%?s!uI`u-k-+N ;|5Roh5)A=PN\ZCdmbpd:m-5#GZwT\;I1iI3+-Z@UO,'7 C>8 @&b  U M  >Y@ZWTSRPMJFDA?=;334$336+27&'4+"=737+"3274+"=737+"727#7#"&'#"&54+"=7307+"Ր /+=V@)1:/V1+=V>+/dL%#NVx2)=W;)4y!3\RR}\ZRRRsN"ssH7ZR-D@,BKPX@"bQK =>KPX@"bQK = >K*PX@"bQK =>@ bUQ>YYYY@ $/$3363 +.+"7574#"75737+"'&'3'.'#"&546326 : F; A6H+G>5!#XP""<`BVxI%'> Pb+X!u#C1D+;XfX^)H%- 3%-!:3@08<M==K>%C$F+%&#"&47>54&'.7$7263 &#"&47>54&#"%E ?mb2/VF'/Z;csND1^FM]y965 5{V- 2 +#(4}D55Hy1PJ;LD0@-SM =M >$&# +% 76$3  #"!32s[؝* ৚ӿh% lzZ3L".5@2dVM =M >$#*(#.$.'"&%2+#"#% 76$323264'.5432"32m^İs[ƠI5A+1/^RP3㮚NVoӿh% lz]+Z8"Ph3-JLTs *=@:bUM=M >)'"!   +"32654&47623264'.5432'"$yqDfpr< .+1/^RPRTo7q́>+Z7#PiRTk~}u'L <@*  @)U  P M =M >Y@;:730/)(&%$"  +"327&4&''# !267323672&#"&47>ÑZVf&]mLk#ρ!AF[!;c}b<E_Pf,LUj'-<L{C 2  2 C%+N 6G@D*) <   UM=M =L> 6 6$$&C#" +.#"327672&#"&47>54.#"#"5432+H3XD3r/;c sw b<-)!V'L`̓PJ;'$,#sd}C 2  1 C}k%'5P+ ;G  @1  b  bUM  =K>Y@BA=;420/'&# GC +2"'&53265!"&#"&47>54&'"'&#"#"&54676236$sw+)+<}fN-413323,16c"A+#A 6;C^pb;+Im=L# 6 1  #K>5ZL F- 6(566O !!R9DM@J;:<bbM=M=M =>$)#&+H&" +3>32#"'&#"&47>5467632#"'.#"172654&#"7+g9BFC1bhmb1LFmLv<:-X'"?>{tnHf 3Nf9X"{7556{Lw%6-9Bf5! -IXDו/-F:K#PX@83  &!<@83  &!K%PX@1e  UU  K ==K>K(PX@4be  UU  K =K>@2be  U  UUK>YYY@C?=:743332$"" +&#"32%#7#"'&';2&#'54;254+"=73%+"232=^F=9f)Rl ?E`rA5?O=3DD3=P >5BHjV j"y[ 3PPRR' H!D2S@bM =M >Y-&,(+7>32726365&#"&547>7654'&'"w#D#=_=P^RYj}%'F!j^w+D{q }=V`2cVN)``u}p( ^L\^:Zjb6941@.<M==M >,)*!!$+".#"#".54767654#"'4'>3232676  'X}IFqkFV5dc3hF@+5hT/\i%+3}J1/ZkQNN1hRTjHjH`TF1\/l-8q!1.Z@&<:K(PX@M =K>@UK>Y@  ., +272'&'&#!%2>76%!&4767 &'&473;50%3.1c`3:R335(/ ='')) F%D;%G6 f5 #|Rg/ 2 T- .X@ *@UQM>Y)#,#$"+4&#"32%4632&7>32#"&'.547#"&R/+)11)'דq! +"ob'y#H(wkVT75R^o 9')N'C)HЮ DUq+=M@J57 <dbeM= >42-+(&"  == +"&54>767#"'&'#"=6;47>7>3232+327P!)<^5'&fD= #39%6 Y 14'P$%(h-) V95}/ ; `E @s'HA8# {h99K(PX@+b` =M =K>@)b`U =K>YY@ 5C2f% +#"&54>323! 72#.+"&#".7>54&+""A+#A'JMY!8w-5)F͎ E+58eq 6(566#US7 s`hi{;65 ;{dg:);H@E!<bbM=M= >#&%#'%%#! +32'#327#"&'&545#"=6;5467632#"'.#"1F)F$%!R+u=y =: /9mLw;:-X'j :s'FC-E5}'s !3)>w%6-9Bf5!L+98FKPX@ $/"<KPX@ $/"<KPX@ $/"<@ $/"KPX@'bQ M =M >KPX@*bQ M =M >K%PX@'bQ M =M >@%b UQM >YYYY@ 6317$%% +%327>32#"'.54&+""'6743) 72#.+"= &9%+!V9PX+6dh}7)  9v-5*W~/)J)?RwD9dgbw { s`hi)Ms:@  U UM >Y@IGCA>;85)'  MM +"#".54&+"=73%#"32>5454&+"=732654#"&54632?V-lњ={R3;8P35q43fFmE1 3;8P`?108/9NPZ˓Ӱ[Fjw/+11ZVe3f-T@wCA Z/+1'1#%+dHDR%}G?@<& <bUM = >BA##*(C% +"'&'# 4&'&47327327&54&'&473674'.54322634R -\6;N^+JEJcY5T n9811^9H X?',"'34HG@yG62Vfb87!|{A6)3#PkI*#2Vf%R+8KPX@ ZM =M >K(PX@!bM =M >@bUM >YY@/-" 88 +"#323254&546;#.#"# 54>7654!9. ^Y ˬÅN.;1L;@IRL?@N+PBXͺ (9:N-'%qXfJh˨gLL`\o)%D8n3.@&bUM =M >Y@ 336&$#'%+32>5#"72>7632#".54;2='#%;2NsLZXj/>3I Rl4A6;H=5B6g+!g'HDJF+")͖9DfR`FBK%PX@% <. <@%  <. K%PX@-b M = K =M>K(PX@*b M =K =M>@(bU M =M>YYY@ B@$'$373#4 +'+"630754+"&5>;2='#'32'.#"3274'4&5432ϴB5;J=6%-"+?5?Ho}ku75h%uXR'+71%!%$ӹw9/R #N7y;@!+K.PX@"ZUQK>@(ZUUIMAYY@ "#)43:$%+ #"&546326?&'&+"=73%+"67>32#"'&#&q)MgE5mP/B O5;-B; -% )=qI0V !RHLb+33-);9 R#qbi#5! )92KPX@<KPX@<KPX@<K#PX@<@ KPX@6  SM  =M  = M=M >KPX@8  SM  =M  =M = M >K#PX@6  SM  =M  = M=M >K(PX@4  S M =M = M=M >@2  U  SM = M=M >YYYYY@.-,+'!  21 +%2672#&)"547#5!6#!""'6743)2632!!3od7 'xO!%y?5%!\ #-  -Z| pO/ BO&#k2\@ /KPX@-bS  =M=N>KPX@-bS  =M=N>KPX@-bS  =M=N>KPX@-bS  =M=N>@-bS  =M=N>YYYYY@22###s +3!2>323#326?2%"54767#5367''674} '+bR$hfIv]NR);*//myWq5/) =Ew=T< |E q HVd/@ '&!KPX@%b`M =M>K(PX@"b`QM >@(b`UIMAYYY@$"  /. +2!".5463232654.#"!'3Bf)`AF{5-<{Aij3-[LZ r-" D+# N/-f@-@J?Uq^-^  RVd/@( <:K PX@"b`QM >KPX@%b`M =M>K(PX@"b`QM >@(b`UIMAYYY@#! /. +2767.'!&#"3267>32# 547&'&543H+L-XN8J"%X/uu;2d6FC`19'd+] ^EՏZD!)@-?g-B׬/\jJ#!/o+@)$ <:K.PX@*db`M=M>@(db`UM>Y@ +* +"5057.+&"327632#"$5467&'&743C7#D)fPH7#jh͖++\w yR{-|X=E9cNKPX@(bb`M=>K.PX@'bb`cM>@,bb`cIMAYYY@<<96/-*)(& +#"&5467>7>54.#""!"'3!2B/7gP7; P)/Hdo)')L>fR?)!#ZJ2AL{T/ '7!)7+)JlH!ub?mc3`  +P6@< ;K PX@+bZUSL>@,bbUSL>Y@ ('%" +4632#!2672!'>?#5!7>54'&"#"&fɼsXNoX (xU  F^!/-$qTHL)68CDf H/5DqmTjfP%oNhZhsB-PPHtpEHd) />FB&:@7 <bSUM >"$$C"+4&#".'!!2632#"&54632326Z\H1p%jD/T"3`qu+X -+];+>PD1w&l@ @#bSUM >Y@ "$$S"+4&#"&'!!2>32#"&54632326LkKM BEp9+ľmD/X+:N^^/;B F];+BLqB'A@>< :  bSU M >&$$$" +4&#"#53>73#272#"&54632326Z^9#'jD-T&)#^su/ ^#^9<ᗶ];+DJ!#%@ < 9KPX@e=M>KPX@e=M>KPX@beM>@beM>YYY@%$"!  +"7%627'4?655"'672763NfŤ (!1bXR>))D3%fxy`I cTI @=> +!#3zz{@=>+3#3#3{{{{j3}aK#PX@" S=K=>@ S S=>Y@ +###535#5333#}zzPPPP%P{D'@$M =M >  +2#"&'&&546462"&=W98%3Z\Z{VZ{VDPJ;V3<DNL3;VR;9VR1  7HjKPX@' % < :KPX@' %< :KPX@' % < :K#PX@' %< :@'%< :YYYYKPX@D  Z  Z N  =M  =M  =M  >KPX@N  Z  Z N  =M  =M  =M=M  >KPX@D  Z  Z N  =M  =M  =M  >K#PX@N  Z  Z N  =M  =M  =M=M  >K(PX@=  Z  Z N  =M =M =M >@;  Z  Z  UM =M =M >YYYYY@&JI a[XWNMIjJgGE<:0*)($#" 76 +4767&2672#&)"5476#!""'6743)26323%32>54.#"&47>54.'&473263 !"& N\~td5'!&yA5% ![ #-+ -Tq?}uJ7\u323314S%9jZL)w'5>D/ +T E| pT/ Bb@5o˅jyP5  #K>5=L#  2 {ӚA1 -J 3DfK PX@  0!< :KPX@  0!< :KPX@  0!< :KPX@  0!< :KPX@  0!< :@  0!< :YYYYYK PX@9bZ  M = =M= N >K PX@?  ZbZ N = =M= N >KPX@@  Zbb N = =M= N >KPX@:bb  M = =M= N >KPX@@  Zbb N = =M= N >KPX@@  Zbb N = =M= N >KPX@@  Zbb N = =M= N >KPX@:bb  M = =M= N >K%PX@@  Zbb N = =M= N >K(PX@F  Zbb  Z N = =M=N >@D  Zbb  Z  U =M=N >YYYYYYYYYY@FE ]WTSJIEfFcCA86 3 3&#%}+#&'7673!2>32326?2%"547''67432>54.#"&47>54.'&473263 !"&g;1RH18 (+bQ%ܺNQ);+//q4/*Tq?}uJ7\u323314S%9jZL)`%XTL`% ==T< |S q cb@5o˅jyP5  #K>5=L#  2 {ӚAL 3CjlK!PX@$]  2S704!i<@$]  2S704!iK PX@I  bZ  b = ==M =N= M >KPX@J  bb  b = ==M =N= M >KPX@M  bb  b = ==M =M =N >KPX@J  bb  b = ==M =N= M >KPX@J  bb  b = ==M =N= M >KPX@J  bb  b = ==M =N= M >KPX@M  bb  b = ==M =M =N >KPX@J  bb  b = ==M =N= M >K!PX@L b `b  b ==M =N= M >K#PX@V b `b  b ==M =M =N= M >@P b `b  b =M =M=N= M >YYYYYYYYYYY@ hg\[ZYRPIGEDB@;9 3 3&#%}+#&'7673!2>32326?2%"'47''676&5.#"326##"&'.54324&'&7672"'&g;2QH19 '+bR%ܺNQ);+0/q5/)"G1b53gP}%3%Pb>HG)RB)b !3R -`%XTL`% ==T< |S q 1.XT6G#S&07c, h) ( ,LfX?',"$1P1*Y0@.Zb  UQ K>Y@,+RQNHED;:72+Y,XC%$' +#"&5463232>54&'.73272676&#!"&47>54.'&4732>33%H7-9V!+%9#%E ͇fb}5  #K>5=L#  2  1  #K>ɢ1/N'3b9KPX@AZb`  K =M == K=>KPX@AZb`  K =M == K=>K(PX@AZb`  K =M == K=>@?Zb`   UM == K=>YYYY@54[ZWQNMDC@;4b5a$('$) +4&'&7$72#"&5463232654&54632#"&2676&#!"&47>54.'&4732>33/V F&/TRDm9wJ! %+5KD;JR?;HZ#3%";6O323314S%LA313HV1 '+#sfPA9:L=)R  %]7F7)3LT3-RR}5  #K>5=L#  2  1  #K>ɢ%%TDP @<  bb`= M ==L=>Y@OMIG'$)C +4&'&7672&#"&47>4&'&7$72#"&5463232654&54632#"&)b !;c sw b<u0V F'/TRDl9xJ! %+5LD;JR@;Hf,$ ,LZ{C65 CV1'+#sfPA9:L=)R  %\8F8)3LT4-RS)-*h@HT + @3  bb  UQ= N >Y@`\SRONKJI$C%$' +#"&5463232>54&'&47327#&'&#"&#"&47>5.'.774.'&47327%G8-9V!+%9#%E͇fb" Z= M#>R ! R=#-ho=%wR?)J)/e{; 2  1 =Z'4/))^R65 R\V-P 2 $@^R 2  1 R\)%D'3q@Q ] 4 @Dbb`   UM === N =>Y@ie\[XWTSI$$('$) +4&'&7$72#"&5463232654&54632#"&#&'&#"&#"&47>5.'.774.'&473270V F'/TRDl9wI! %+5LD;JR@;H3N*(R"#=RR>" Z= M#>R ! R=#V1'+#sfPA9:L=)R  %\8F8)3LT4-RSp'4/))^R65 R\V-P 2 $@^R 2  1 R\-%D'3kT@QjP <b`M = M=  K =>igcb_[XW$H$('$) +4&'&7$72#"&5463232654&54632#"&&#"&47>54&'&76726 &#"&47>54#"0V F'/TRDl9wI! %+5LD;JR@;HH%E ?mb2/V - a=;4320-)%$ +!#&#"&47>7272&#"&472>&'!"4767&/71x) 5[l)65  N'5>D/ +T ZJ 3?I@F?4.< :dbbM=M >)$$$&'-+#&'767# 546?4&#"#"54>3 327'327g;1RH1 7)RLP J5jO^ 1%yk3A9>/\`%XTL`%+Kov;+w.D &9h?i4L=#s"*5)#5FJ1)6L@ 520.,*:K(PX@K =K>@UK>YCC+4.'&47327&#"&47>54767&314313313323!N\/=L#  2  1  #K>=L# 65  #K>}'5>D/ +T 5wJ '&@# :d=L>C+#&'767&#"&47>54&'&7$72f;1RH11cimb2/V F&/`%XTL`%p{765 5{V1'+#seL '0@- :M =M > &$  +4767&"32% 76$3  N\~p㮚s[w'5>D/ +T 3-Jӿh% lzLJ #5@2 :dM=M > "    +#&'767"32654&4762'"$sg;2QH2yqDfp `%XTL`%q́}u 8EL@ DA?=;9:K(PX@K =N >@UN >YC+L&+32>54&'&47327! '&4&'&473274767&1/jdN- @} I; 7.- 煲;c^  {A3!N\-Vm-f?\xRy7 2  1  %E@/Vu,Jy= 2  1 ;'5>D/ +T %J CD@A1< :dbK= >>:0.&" +#&'767"'&'# 4&'&47327327&54&'&47327{g;1RH1U4R -\6;N^+JEJcY5T n+J`%XTL`%X?',"'34HG@yG62Vfb87!|{A62Vf HTqK(PX@)  UUK =M >@'  UUUM >Y@KIQNITKTC*M' +462"$462"32>54.'&47327! '&4&'&47327!"7463!2AbBBbCc??c1/jdN-  N' J3!煲;c^qN s 5!bcCCcAAcCCcAVm-f?\xRRN 2  1 LT/Vu,Jy= 2  1 =%-"-% VY@VD )' <  b U U K  = >QMCA9521-+&%  +!"5463!2462"%462"&"'&'# 4&'&47327327&54&'&473271!6!A^BB^BA^BB^A4R -\6;N^+JEJcY5T n+J%.#-_AA/-Bo/AA_?BX?',"'34HG@yG62Vfb87!|{A62Vf  Ui:K(PX@%dUK  =M >@#dU UM >Y@ PL*M'% +"&5476462"$462"32>54.'&47327! '&4&'&47327D" JAbBBbCc??c1/jdN-  N' J3!煲;c^qN $?h  HcCCcAAcCCcAVm-f?\xRRN 2  1 LT/Vu,Jy= 2  1 =%& XT@QF +) <db  b V K  = >SOEC;7%$! +632"&547462"%462"&"'&'# 4&'&47327327&54&'&47327 >   zA^BB^BA^BB^A4R -\6;N^+JEJcY5T n+J"-*_AA/-Bo/AA_?BX?',"'34HG@yG62Vfb87!|{A62Vf  Ub@ :K(PX@ UK =M >@UUM >Y@ C*M' +4767&462"$462"32>54.'&47327! '&4&'&47327aPLB@C% +#&'767462"%462"&"'&'# 4&'&47327327&54&'&47327}R0x(fA9p'oA^BB^BA^BB^A4R -\6;N^+JEJcY5T n+J4W!OKDV!_AA/-Bo/AA_?BX?',"'34HG@yG62Vfb87!|{A62Vf  Ui:K(PX@%dUK  =N >@#dU UN >Y@ PL*M'% +32654'&462"$462"32>54.'&47327! '&4&'&47327rF! JAbBBbCc??c1/jdN-  N' J3!煲;c^qN  '<h  HcCCcAAcCCcAVm-f?\xRRN 2  1 LT/Vu,Jy= 2  1 =%U YM@JG ,* <dd  b V K  = >TPFD<8%%% +#"/7>32462"%462"&"'&'# 4&'&47327327&54&'&47327q %B&A^BB^BA^BB^A4R -\6;N^+JEJcY5T n+JQ %(_AA/-Bo/AA_?BX?',"'34HG@yG62Vfb87!|{A62Vf; %7@4 <bSM=M >%%%"$+!32676"5632#".547!4654&#&bP3A%qmL ;{D'ok%!8?fw@ovH?O[_@\/<  UUS  U K >RPXUP[R[MKDCB@=954.-%!+462"$462"!#&#"&47>7272&#"&472>&'!"!"5463!2VBbAAbGDb@@b 71x) 5[l)65  N%.#-Z FRe@bRGA( <  b  b U U M = M  >QOFD@>:842,*#!   +!"5463!2462"%462"&# 546?4&#"#"54>3 327'327!5!B^BB^AB^BB^B 7)RLP J5jO^ 1%yk3A9>/\%.#-_AA/-Bo/AA_?B+Kov;+w.D &9h?i4L=#s"*5)#5FJO[Z@W/<U  US  U K >ZXTRMKDCB@=954.-%!#!# +32+!#"546;!#&#"&47>7272&#"&472>&'!"4632#"&71x) 5[l)65  Nm3LT3-RRZ 4@La@^@5/<bb  U M =M=M >KIEC?=42.,(&"   +!"5463!2# 546?4&#"#"54>3 327'3274632#"&!5! 7)RLP J5jO^ 1%yk3A9>/\LC;JR?;H%.#-+Kov;+w.D &9h?i4L=#s"*5)#5FJ3LT4-RSV ho@/,63 C <;K(PX@MZU  U  SN =M= L = K  >@KZUU  U  SM= L = K  >Y@(jigfc_TSPOLG@=:854.-*(%"   +!"5463!2 6&#"=7!272'.+"326762"'.+;26&#!&47>5!&#".7>!4&!5!3;Lb5/ %3%bk#1mI E F Ni1#J5' ces@/!  x) JU8)%-"-533q#wd='-R {6% H7/)9 u-5 ;y' 65 7 Ly $b@U  \B  *7<  b  b U  U  M  =M =M >&% `]XVSQMKEC;9-+)(%b&b#!     +!"5463!2">74&67726%2672#"'.'#"'.54$%#"#"&5463263 !5!#T)D%-N  =/iF9ӦQ ;9B*68=37w`%d^Le#y$.#,  hmJ3B3 <;?9 LQ,`6;?7 7?+XCLC3DD=Z@W2<bU SM  = M >64/.-,%! == +23'.#"32675#5354&'&473273## '&576)##%5+߻of/d{N\1FZwur  -+bɭfqDHXm˶n$2P)9# :: +3)PwT`V N¿# V^@OF"? @C bUUU    S M = M  >Y@* \ZXWSQNL<;:964210/)'!VV  #! +4"326"'!6'4"&'&"#"'323##"'&'#53>7&'&5467.54762>32!3276R=81>;=#5 L ɪg/ \K6f ,TKça@ $`1;k7HVd-3?7MuMG8N^pX )h sZ-+h&V #Fu$#Lg|C-sL)e''7/(M[B-LB!d=P(D BQ@N7#< :bUM =M > ;9.*'&! BB +4767&23'.#"32674&'&47327# '&576 N\~v##%5+߻of/d{N\1FZwur  -+bɭfqw'5>D/ +T HXm˶n$219# :: +3T`V N¿5 "^@W+N2G< :KPX@> d  b  UU M =M  = M  >@< d  b  UU U M = M  >Y@$#[YVTA?971/)'#^$^""#+ +#&'7674"326"'327>54"&'&"#"'32#"'&7467&'&5467.54762>32f;1RH1=81>;bG04#5 L ɪg/ \K6faPt>;k7HVd-3?7M`%XTL`%^pX )uRz(m;sZ-+h&V #FuC7%''7/(M[B-LB!d% cb@N"< :K(PX@K =M>@UM>Y@_^[WTSGB0, +4767&4.'&47327>7676'&47327&#""'&'.'&#"&47>5!N\j3233143kd?b\f 9[L>=cRRD"7,413323w'5>D/ +T =L#  2  1  #K>KZA 2  1 IJD%JJ 6 9W,* n=L# 65  #K>%L [@D< :KPX@2b  U= =K= L >@4b`  U=K= L >Y@ZYVRKJC@=<;:'#  +#&'7674&'&7672267676&'&4732723&#'54'&'&#&#"&47>g;2QH2)b !,uBA)'V`[8SI'e   ' { #ir+5  bw b<`%XTL`%xf,$ ,LT'9Z=)66:*p 65%FJ'wE65 CL5D".B@?!<bM =M =>$#*(#.$.+&%"+#"&547# 76$3 327"32H'@Pl9=s[X{RI)N%㮚5<^Ryh% lzӿ_4KtT'$$3-JLT!,q@ @"bcM=M >Y@#"(&",#,+%%"+#"&547'"$54762327"32654&'@Pl17p QL)N$jyqDf5<^Rḿ}LuT'%%CL5 .:W@T- <bU M =M =>0/64/:0:,*  +!"5463!2#"&547# 76$3 327"32!5!B'@Pl9=s[X{RI)N%㮚)%-"-}5<^Ryh% lzӿ_4KtT'$$3-JLT -8@, @+bcU M=M >Y@/.42.8/8+)  +!"5463!2#"&547'"$54762327"32654&!6!'@Pl17p QL)N$jyqDf$.#,+5<^Rḿ}LuT'%%CHV <@43.< :K PX@"b`QM >KPX@%b`M =M>K(PX@"b`QM >@(b`UIMAYYY@ 1/-+%#  <; +4767&2!".5463232654.#"!'3`!N\Jf)`AF{5-<{Aij3-[LZ r-" Dw'5>D/ +T # N/-f@-@J?Uq^-^  +/wJ 3@10/*< :K.PX@.ddb`M=M>@,ddb`UM>Y@ "$"#) +#&'767#!2#"&54232654&#"#"'f;1RH1^fj&3JPg)F#7lDh`%XTL`%#5(șb:F=X}NRy JN%DJ 25@2 :db`=>-+$" +#&'7674&'&7$72#"&5463232654&5ug;2QH2/V F&/TRDl9wJ! %+5`%XTL`%V1'+#sfPA9:L=)R  %\8F81 9*;]tKPX@  <KPX@ <KPX@  <K#PX@ <@KPX@N  Z  Z N  =M  =M  =M=M  >KPX@D  Z  Z N  =M  =M  =M  >K#PX@N  Z  Z N  =M  =M  =M=M  >K(PX@=  Z  Z N  =M =M =M >@;  Z  Z  UM =M =M >YYYYY@&=<TNKJA@<]=Z:8/-# *) +%2672#&)"5476#!""'6743)26323%32>54.#"&47>54.'&473263 !"& )d7'!%x?7%!\ #-+ -yTq?}uJ7\u323314S%9jZL)Z| pT/ Bb@5o˅jyP5  #K>5=L#  2 {ӚA1 %-(9[@ %K PX@7  ZZ N = =M= N >KPX@8  Zb N = =M= N >KPX@2b  M = =M= N >KPX@8  Zb N = =M= N >KPX@8  Zb N = =M= N >KPX@8  Zb N = =M= N >KPX@2b  M = =M= N >K%PX@8  Zb N = =M= N >K(PX@>  Zb  Z N = =M=N >@<  Zb  Z  U =M=N >YYYYYYYYYY@;:RLIH?>:[;X86-+((&#%s+3!2>32326?2%"547''67432>54.#"&47>54.'&473263 !"&7 '+bR%ܻNR);+//q5/)Tq?}uJ7\u323314S%9jZL) ==T< |S q cb@5o˅jyP5  #K>5=L#  2 {ӚAL(8_K!PX@R H,%)^<@R H,%)^K PX@AZb = ==M =N= M  >KPX@Bbb = ==M =N= M  >KPX@Ebb = ==M =M =N >KPX@Bbb = ==M =N= M  >KPX@Bbb = ==M =N= M  >KPX@Bbb = ==M =N= M  >KPX@Ebb = ==M =M =N >KPX@Bbb = ==M =N= M  >K!PX@E  bbb ==M =N= M  >K#PX@O  bbb ==M =M =N= M  >@I  bbb =M =M=N= M  >YYYYYYYYYYY@]\QPONGE><:9750.((&#%s+3!2>32326?2%"547''674&5.#"326##"&'.54324&'&7672"'& '+bR%ܻNR);+//q5/)"G1b53gP}%3%Pb>HG)RB)b !3R - ==T< |S q 1.XT6G#S&07c, h) ( ,LfX?',"$D5BT@Q*<86:dbUM =M >=;.,! 55 +23'.#"32674&'&47327# '&576#"&5476)##%5+߻of/d{N\1FZwur  -+bɭfqX-%#]DHXm˶n$219# :: +3T`V N¿f)Hw Ph %a@Z.Q5J KPX@Cd d  b  U V M = M  = M  >@Ad d  b  U V  U M = M  >YY@ '&^\YWDB<:42,*&a'a %%#&$!+632#"&5474"326"'327>54"&'&"#"'32#"'&7467&'&5467.54762>32\ N''& =81>;bG04#5 L ɪg/ \K6faPt>;k7HVd-3?7Md1/! ^pX )uRz(m;sZ-+h&V #FuC7%''7/(M[B-LB!d-R_@71" KPX@7  S M  ==N=N >K#PX@7  S M  ==N=N >K(PX@4  S M  ==M=N >@2  U  S=M=N >YYYY@QOLJB@<96542/,*)3#33#2+!;2�'54;254+"=730%+"!4+"=730%+"32654'&74632#"&A6=O;5DD5=P =6B;A6=P#;5DJ7\VyC<} RRRR_RR}Z^F!1q+HD 2?@<,<&  9bM >  2 2/.   +"7654&%6327'767>7654'&5&527671wAìTyͮ 3 ' 'bNJ)^uͰ'üq\äĐdX^P  `Bs !)=J{@)<@>:K(PX@(b=K ==N >@&bU==N >Y@ .II$+!#&'&#"&#"&47>5.'.774.'&4732732654'& 3N*(R"#=RR>" Z= M#>R ! R=#=Z+&!']'4/))^R65 R\V-P 2 $@^R 2  1 R\+Fw P-hFhE+@"ddM=K>Y@ $C$G%% +#"/7>32&#"&47>54&'&76726 &#"&47>54#"T/R1q%E ?mb2/V - a[YRQPNKGCB<;3/! $$# +"&46326"2654&!#&#"&47>7272&#"&472>&'!"mX-iffHQ5k+66V7971x) 5[l)65  NZ =IWs@pO I>8< d  db  bV M =M=M  >SQMKHF=;751/+)#!    +"2654&4632"# 546?4&#"#"54>3 327'327632#"&547+66V79fHFji 7)RLP J5jO^ 1%yk3A9>/\f >  9+);;)-7dbHFde+Kov;+w.D &9h?i4L=#s"*5)#5FJ-*V\cp@"# *' 7<;fd:K(PX@IdZU  SN =M= L  =  K   >@GdZUU  SM= L  =  K   >Y@ki^][ZWSHGDC@;3##93#+76&#"=7!272'.+"326762"'.+;26&#!&47>5!&#".7>!4&#"&5476;Lb5/ %3%bk#1mI E F Ni1#J5' ces@/!  x) JU8X-%#\533q#wd='-R {6% H7/)9 u-5 ;y' 65 7 L)Hw PLyh &d@ W  ^D  ,9@Gd d  b  b  U  M  =M =M >Y@$('b_ZXUSOMGE=;/-+*'d(d%#$! +632#"&547">74&67726%2672#"'.'#"'.54$%#"#"&5463263 Z N''& T#T)D%-N  =/iF9ӦQ ;9B*68=37w`%d^Le#yd1/!   hmJ3B3 <;?9 LQ,`6;?7 7?+XCLC3L+4K@H  -, 1/&$)( +'7&76$327%"'"&#"&5476 324J[۬oLoz-DlcX-%#\6`\?ĸ lzq;ӿ}תʳ)Hw P3ci3V'h #*2@!#,+)(<9KPX@#b =M=M >@ ddM=M >Y@%$/-$*%*),$!+632#"&547'"''7&54762"&72654h N''' K[ܟydJ`pm??hBsDfd1/! @/ d d   dS VK>Y@ [ZSQJHBA=;4320-)%$ +!#&#"&47>7272&#"&472>&'!">#"/&'4%632"/&5&/71x) 5[l)65  N %!  (Z3FR@ RGA(K1PX@2ddb  bM=M  >@6dddb  bM=M  >YY@QOFD$$&'%%&%$ +3254/&#"7254'&"# 546?4&#"#"54>3 327'327 %/ x 2#3 7)RLP J5jO^ 1%yk3A9>/\%3  -]+Kov;+w.D &9h?i4L=#s"*5)#5FJ?LP@MLGF@ <  US UK>JHDB=;4320-)%$ +!#&#"&47>7272&#"&472>&'!">32&"/71x) 5[l)65  Njmo77ZH 5AN@K A60<bbM =M=M >)$$$&'&$" +.#"'>32# 546?4&#"#"54>3 327'327wdB^y 7)RLP J5jO^ 1%yk3A9>/\PR@+Kov;+w.D &9h?i4L=#s"*5)#5FJDUf@:  <6;K(PX@C  d  d ZZU N  =M=L>K,PX@A  d  d ZZ   VUM=L>@E d d  d ZZ   VUM=L>YY@$a`YWPNHFA>5430-,'&# DD +26762"'.+;26&#!.7>54&'.7!272'.+"672#"/&54%632"/&'4jLF  FPf1#J5' cd  s?@r  ;\50 %3%bj#1  +'u 1R {6% H;+)9 u-5 ;y3{; 2 q#wd='} %!  (LB(C+ @5ddd  b  VM= M >Y@CA<:%"$%&% +254/&#"3254'&#"!654&#"2#"'&5432!3!%/ y 1#3,P3A%pmK!;{%3   -'oj% 8N?fw?owG?DQ@ QLKE :  <6;K(PX@? ZZ  U U N  =M=L>@= ZZ  U   U UM=L>Y@ OMIGA>5430-,'&# DD +26762"'.+;26&#!.7>54&'.7!272'.+">32&"jLF  FPf1#J5' cd  s?@r  ;\50 %3%bj#1go9T 1R {6% H;+)9 u-5 ;y3{; 2 q#wd='}jmo77LJ 2J@G <bUM =M=M >%%%"$$" +.#"'>32!654&#"2#"'&5432!3wdB^y IP3A%pmK!;{PR'oj% 8N?fw?owG?)9J0K,PX@!d dVK>@%dd dVL>YY@ ED'&CC +4.'&47327&#"&47>5>#"/&'4%632"/&5&314313313323A~ +)u  /=L#  2  1  #K>=L# 65  #K> %!  (wy:zKPX@"b``c>K1PX@dddd[@ddddd[YY@ C%&%$+3254/&#"3254'&#"&#"&47>54&'&7$72X %/ x 2#3+1cimb2/V F&/%3  -{765 5{V1'+#se1)6_@ 610*@UUK>Y@ $'CC+4.'&47327&#"&47>5>32&"314313313323go9T /=L#  2  1  #K>=L# 65  #K>jmo775wH *-@* <M ==L>C%"+.#"'>32&#"&47>54&'&7$72jZOr; Um 1cimb2/V F&/QQIY{765 5{V1'+#seL *;lK,PX@"ddM =N >@&dddM =N >Y@65.,%#  +"32% 76$3 >#"/&'4%632"/&'4ϓ㮚s[A +)t  3-Jӿh% lz %!  (LB(6KPX@#dXM=M >K,PX@"ddM=M >@&dddM=M >YY@53.,$"((%&%$ +254/&#"32'4'&#""32654&4762'"$  %/ y 1#3=yqDfp %3   -"q́}uL '<@9'"!<UM =M >%#   +"32% 76$3 >32&"ϓ㮚s[<fo:T 3-Jӿh% lzjmo77LH %<@9 <M =M=M > $" $" +.#"'>32"32654&4762'"$weA^y yqDfp PRq́}u1 BRc@= d d   dU M =L=N >Y@ ^]VTMKED=<32/+('"! B @#! +!"32>23#"'.#&#"&47>54.'&472$>#"/&5&%632"/&54THXHhc1sl/TB!}Gs313323314)TA ,)u  +Ogi;yC^+RH? 0 d%=L# 65  #K>5=L#  2 P %!  (-TDM{K,PX@-d  b  U M=L>@1dd  b  U M=L>Y@IGEC!C%&% +254/&#"3254'&#"&#"&47>54&'&7672362#"'&#"`!%/ y 2#3k1b hmb21T w+ k5RH.C $%3  -{765 5{V3' 7#d=I)?V"'#61 BOa@^OJIC <  UU M  =K=M > MKGE=<32/+('"! B @#! +!"32>23#"'.#&#"&47>54.'&472$%>32&"THXHhc1sl/TB!}Gs313323314)Tgo9T +Ogi;yC^+RH? 0 d%=L# 65  #K>5=L#  2 ojmo77-TH <F@C <b`M =M=L>"$!C$" +.#"'>32&#"&47>54&'&7672362#"'&#"wdB^y 1b hmb21T w+ k5RH.C $PR{765 5{V3' 7#d=I)?V"'#6 8HYK(PX@#d dK =N >K,PX@!d dVN >@%dd dVN >YY@ TS'&C+L& +32>54&'&47327! '&4&'&47327>#"/&54%632"/&'41/jdN- @} I; 7.- 煲;c^  {AA +)u -Vm-f?\xRy7 2  1  %E@/Vu,Jy= 2  1 ; %!  (%BV@ D)'K,PX@&ddbK = >@*dddbK = >YY@ QM(C%%&%$ +254/&#"32'4'&#""'&'# 4&'&47327327&54&'&47327  %/ y 1#3A4R -\6;N^+JEJcY5T n+J%3   -X?',"'34HG@yG62Vfb87!|{A62Vf 8E_@ E@?9@UUN >Y@ $$C+L&+32>54&'&47327! '&4&'&47327>32&"1/jdN- @} I; 7.- 煲;c^  {ALfo :T -Vm-f?\xRy7 2  1  %E@/Vu,Jy= 2  1 ;jmo77%H EC@@ 3<bM =K= >J(C%$"+.#"'>32"'&'# 4&'&47327327&54&'&473277wdB^y 4R -\6;N^+JEJcY5T n+JPRX?',"'34HG@yG62Vfb87!|{A62Vf7D2Hk@ <99KPX@ eM ==M >@#beM =M >Y@ 433H4H-&,(+'.#"'"&#&5632654'.'&5476722&7>54./&546^#D#=_=P^RYj}%'F!j^w+D{q;Ym;J DE }=V`2cVN)``u}p( ^L\^:Zjb6oHXTy)@  M3b?9@6,<9eM=M >=;1/)' +2&7>54./&546632654'&'&'47632'&#"#"'&u;Xm;J DF-%H-Lib?Fhuq5Nr+8oO?݅sGXTy*@  M3 o6/q.?kFTXHj1^{1%q.EYFj}-V+9FoKPX@2=0<9KPX@2=0<9KPX@2=0<9@2=0<9YYYKPX@, e M = M =K>KPX@) e M = M =K>KPX@, e M = M =K>K%PX@) e M = M =K>@' e U M =K>YYYY@DA?>:98543/.,)$#  +2&7>54./&546&#"&47>54&+""'6743) 72#.+";Ym;J DEF͍F+6dj}7)  9v-5*HXTy)@  M3}{;65 ;{de`w { s^fi+DU@RC46<9dbeM = >A?:831,*'%!DD +2&7>54./&746347>7>3232+327#"'&'#"=6V;Xl;K  DE9%6 Y 14'P$%!RfD= #HXTy)@  M3  `E @s'HA-E95}/ ;D1m@ "+@!beUM >Y@ 11 +"&5467>54&#'2654&#"'7672+1JX`ˢ hV8?X=Hje?uF۸{R1+B9!qLXfWyg +yXlF7+d #351/@  + KPX@b=M=>@b=M=>YY@ // +"&5467674'"5>4&#"'77632'H|aqL1HG;;sNb˘oF?D1%);33HR`h }bwj#^r=5 %31Z d@ :K(PX@%  S K  = K>@#   U  S K>Y@dc^]ZVSRIHEA>=8721.*'& +4767&4.'&47327&#"&47>5!&#"&47>54.'&47327!!N\413323323314313323314313{w'5>D/ +T =L#  2  1  #K>=L#  2 5  #K>m=L# 65  #K>5=L#  2  1  #K>%L B@; > <  :KPX@+  b = = M =L>@- b ` = M =L>Y@A?:9C$C +#&'767&#"&47>54#"&#"&47>54&'&767263 f;1RH1G@+I M=  L =K>Y@EB?<9620339" +74'"#"5&527673672;2&#'54;254&#"7;2&#'54;2 !5Nb=1k۬DhF/+/=O>/+9Hs)/=P;/+ ! !1THoC8 PPcR9/C:@%872 @&$<9KPX@6b  U= M=M = M >KPX@6b  U= M=M = M >KPX@6b  U= M=M = M >@3b  U Q= M=M >YYY@ CA=<641/)'   +%2654'""327&+'>767&'#"54>324#"567673623k)p Nd!RHO @ә {v %)ZZZL\D!j! s\m{u7_A?tXV)t+D=)Bh3R8fFTL 92@/7% <=M =M >.,*+4&'">3 54&'46752!".54>7.!\X}dNkn2 d-EoX'Jf}DZVJri/=bBR{Fnd^{%sD' CCkd/HX5!9`J#J^FZ<JDJ 9+@(821(:dM >    +%254&'34'7#".5467.'.5476y'wL/`c^/BQk3hX9sLLR@UM>Y@ ## +%!767&#!"/6567!3!2>54&w1 U b,e#rTb[1XTTmD ')To;A)DtPL5#j@ < :9K.PX@bM=M>@bUM>Y@## +%!654#!"/>;33!2654&)M3(+PPqBz'X^? mTy'w'^g9A+yzR KJ@G+<US   U K> IG@?><9510*)!  $" +4632#"&!#&#"&47>7272&#"&472>&'!"dLD;JR@;H571x) 5[l)65  NZD 4@G@D@5/<bbM =M=M >)$$$&'%$" +4632#"&# 546?4&#"#"54>3 327'327LLC;JR?;H 7)RLP J5jO^ 1%yk3A9>/\3LT4-RS+Kov;+w.D &9h?i4L=#s"*5)#5FJH1W@%-*41 A IR P <:K PX@MZ Z  UN =M= L =K = M  >KPX@NZ  b  UN =M= L =K = M  >K(PX@KZ  b  U QN =M= L =K >@IZ  bU  U QM= L =K >YYY@WWUSOMHE>;86#932+'7#.7>54&'.7!272'.+"326762"'.+;26&+#"'473254 ad  s?@r  ;\50 %3%bj#1jLF  FPf1#J5' c#cBAN)f 5 ;y3{; 2 q#wd='}1R {6% H;+)9 u-T&Mk"+ TPL=.9@ )'K*PX@8b bU M=M =M>@5b bUQ M=M >YY@640/..$%2%%( +'7&'&5432!3272#"'#"'473254!654&#"N `SmK!;{kcBBN)fP3A% Kw?owG?l?K&Ml#+ TP'oj% 8L*6F@C  UU M =M >-+30+6-6)'! +462"$462""32% 76$3 !"5463!2AcAAckCc??c<㮚s[9#cCCcAAcCCcA\3-Jӿh% lz\%-".L (6J@G UU M= M >53.,$"((  +!"7463!2462"%462"&"32654&4762'"$!5!A_AA_BA_AA_AyqDfp %.#-_AA/-Bo/AA_?Bq́}uL(4C@ "@2 IU U M = N >Y@"*)B@:80.)4*4&$! ((#!# +32+!#"546;272#"'&'""5>32"32% 76$3 !}{o}8W2A0.7 -N,J7 @㮚s[5*) a`r/& )((Zx93-Jӿh% lzL $/=e@b< UU  U M = N >&% <:53+)%/&/"  $ $   +!"5463!2272#"'&#""'>32"32654&4762'"$ !5"B""f9L95B! 5\3X@%:yqDfp %.#-XXf+#%%%Rl3q́}uL &1@.UM =M > %#  $" +4632#"&"32% 76$3 NLC;JR?;H㮚s[+3LT3-RR3-Jӿh% lzLD $3@0M =M=M > #!  $" +4632#"&"32654&4762'"$KD;JR?;HxyqDfp 3LT4-RSq́}uL'6?@<U U  M =M >53-+#!''$##!# +32+!#"746;4632#"&"32% 76$3 }PLC;JR?;H㮚s[+')3LT3-RR3-Jӿh% lzLZ "0F@CUM = M=M >/-(&""  +!"5463!24632#"&"32654&4762'"$/!6!YKD;JR?;HxyqDfp %-#,3LT4-RSq́}u} HfK(PX@! UK =K>@ UUK>Y@?;87/.+'  +!"5463!2&#"&47>54&'.'&4732776&'&47327!6!ExF0KE {Z R%9H ?D{ JG!#$.#,{965 9{70/R0 2  1 59-'-3 2  1 #8J:q %j Q9@6UK=M>HD32/+  +!"5463!2#"'&74632326?>.'.'&47327476.'&47327J!5!)R+J1C7 *(/1 )5DRyV# ' A+f5X1$.#,H'%5#;/5q !% ^`166?>T! 66L1+|#? 3@$<9K!PX@U=M >K,PX@UQ>@bUQ>YY@ 31*('&! # +%27654'.'7&'4'&75676767367#632#"\\-IdgaBG#/B)7o -1?UR"1Z-;3J;#2RTG $o/H/;#13PZ-/!5dDKPX@,&% 4BKPX@2  b  UM=L= M >KPX@*  b  UM=N >KPX@*  b  UM=N >@2  b  UM=L= M >YYYY@=;75/-+)$#   +"32644&#"7;2&#'54;255"'676;632632#"''7&5@J1c#3j +?M;%+d 7%%Lys qTOp19-ib7V} d{m\RRuI!jjb=V1md[3N'5 5K1PX@!40 <:32 9@!40 <:32 9YK.PX@%dUM= M >K1PX@#dSU M >@'ddSU M >YY@ /-&&# +327654'&#"#527#7#67573#67672#"''7&RZ!+L 1u"  +3;VP%1Z-93Hf` G!:%J6`+ !%F`Es-F3;%-6N^+193RN%'#@ b`=>'$)+4&'&7$72#"&5463232654&5/V F&/TRDl9wJ! %+5V1'+#sfPA9:L=)R  %\8F89 BE@A 9 -)= M =M =M =M  =M  >KPX@>= M =M =M =M  =M  >K!PX@>= M =M =M =M  =M  >@<  U=M =M =M  =M  >YYY@=<860.$"$%$" +%'32654&#"7&"3277273632#"'#5#"54>324'"5& 9>woMLqBfsbRNI +urk^tչ}RnNsZL\B ( =uL첖X uӏN492--h3#w!=- K @2+=KPX@= M= M=M  =M  =  L  >@= M= M=M  =M  =  L  >YY@&HEC@<:53/-*(" KJ    +"7'32654&%"327&265#".5432763262#"';2&#543?+f HE{igmbm^L;V- H^sd4ɓ[=' `הnX6 '=P;#XJRܙĞͺ+;B%+7VL7/'Lk-#55 I`@]) *- <eSU = L >IIBA@>;732,+1#"#+3'#;#"7&'#7#"&47>7273 2&#"&472>&',n^I"'% KKSh ) 5[$v)65  NL5$,J@G&%$  <be=M =N >.&#"+3 72! '#&5!273'&' &#"<]+fit;hZ&#%''+"&'7&54322#"&'3272#"'&'&#"Z+o3 YH)U?1*C \V};Zp 7 )d{%3?P1A(!ghs@Vpj k.6 o->@(ZU M= K>Y@;964/.+%"! >= +%2676&#!"&47>5#"746;54.'&4732>332+3˖#3%";6O323314S%LA313 HV}5  #K>+!)=L#  2  1  #K>+Ӣ+69KPX@ 9706<KPX@ 9706<KPX@ 9706<@ 9706KPX@2de M  = M  =K>KPX@.de M  = M  =K>KPX@2de M  = M  =K>K%PX@.de M  = M  =K>@,de  V M  =K>YYYYY@5421-,+*A4C +%&#"&47>=#4&+""'6743)2?3672#.'=F͍Ff+6dj}7)  IhP9ym/ {;65 ;{zde`w { n_'bL<@9H(<bQM=M >&$'*&+$+632654'&'&'47632'&#"#"'32767672#"'.'7&'&b-%H-Lib?Fhuq5Nr+8oO?݅&. '=) $=BH  4+ o6/q.?kFTXHj1^{1%q.EYFj}%1 fD)!% 3%  VLVL7 KPX@,Z=M=N=N>KPX@)ZR=M=N>K#PX@*bR=M=N>@(bUR=N>YYYY@ E""#s% +3"547'67433!2>32327632#"'&/&+&{/3s3#! (+w9)%ZFPFA!"%'BR;1,P;CZKX  =P55Gq/)RmD89@6b``M =L>$$&22<+;2&#54;2652>54&#"##"&54$32C-1363 );T=) jDpwdB RJ3V 3WR/+-7oC/+31)/#Ak`1V; 1" +?9+7/7@4/<b`M=L>$&$D+%&#"&547>=>54&#"#"&54632;j 9; jK(PX@7ZZ  U  UN  =M>@5ZZ U  U  UM>YY@ 77 7K7JGEDB<:21,*'%  6 5"! +3254#"'263 !"&#&47>=#"746;4.'&4732>54&+32#r\37qTD#3133131V`> 'yN%+ 3  #K>!)%=L#  2 1)9a^5, -=LcK(PX@#  VK = M >@!U  V M >Y@GE?>:8#)GC# +#"746;4&'&47327!4&'&4732732+! '&%!32>5}j;c^  {A@} I; 7.- R=煲+1/jdN- *?y= 2  1 ;{?y7 2  1  %E@*7Vu,lVm-f?\xRD. @< =K>OI"+7672&#"&47>''&#"&47>X41@71  zј +43 "-=1xF{ 5>CA/0+1 2  1 \P'%+  2  1 @DCLP@"E %"1,)P:M @8 Z  U U =M= L = >Y@DDDLDLIFC>4#-C +"&'4&'.7!2372'&'326762"'.+;26&#! &+">7* @r  ;\ %3jLF  FPf_G1#J5' ceq1T#1n?0)H{; 2 J&1R {6% H;X)9 u-!='}Z8N!(28@8* ( KPX@2b   V=M=M = >KPX@2b   V=M=M = >KPX@2b   V=M=M = >KPX@2b   V=M=M = >KPX@2b   V=M=M = >@2be   V=M=M >YYYYYY@))43)2)2)"%'" +2#"'&'"&'7&543272!3#?.#";6545spu }J{S412b;—9G>!A%-?fp*?/>]?F10% 7' L-:kK(PX@%  bU  QK >@+  bUU  I M AY@ 64$'##C# +#"746;4&'.732732+#"&5463232>5E ͆fb<\H%H7-9V!+%9#$){; 2  1 =y)ho=%wR?)J)/eN%;D7C>@;b`V M ==>B@##&'$&#$ +4&'&7$7232+#"&5463232654&=#"746;4632#"&/V F&/H5TRDl9wJ! %+5KD;JR?;HV1'+#sf#A9:L=)R  %\8F8#3LT4-RSJwD 0}@   @&bRM =M >Y@-+(&"    +"3275&# !267232>7632#".{^}~uj#A'!" NH{y1VP3L}q-h'/H <@6RK;e(ZH<h@%bRM=M >Y@ $$'%$*&!+&#"327>=4&'"54326763232>7632#".C^w9^a1S4 bs\%!$ L!$vw0VP1KȤoT' jm'5X>< '35P'"9c)[%; JqKPX<KPX <KPX< KPX@4  I U M  =K=M >KPX@/  U M  =K=M >K1PX@4  I U M  =K=M >@5  UU M  =K=M >YYYY@ ED?=:832/+('"! J H#! +!"32>23#"'.#&#"&47>5#"746;4.'&472$THXHhc1sl/TB!}Gs313323314)T+Ogi;yC^+RH? 0 d%=L# 65  #K>Z#=L#  2 %T<A@>  b  `U M=L>:864!#'C# +32+&#"&47>=#"746;54&'&7672362#"'&#"yf1b hmb2}1T w+ k5RH.C $1)#{765 5{#PV3' 7#d=I)?V"'#9-JOjA@! U UK>Y@LKGE:6G#'C +%&#"&47>54&'#"546;'.'&47327!76&'&47327+!7ExF90KE {Z R%&*9H ?D{ JG!7#G}{965 9{70/1)-R0 2  1 59BP-3 2  1 #8[ *:q-' %j}SZ7@4  VK=M>UTPNKIJG#*!%# +#"'&74632326?>.'#"546;'.'&47327!76.'&4732732+#47)R+J1C7 *(/1 4)5DRyV#>P ' A+f5X&BTx1]H'%5#;/5q !% #w`166?>! 66L1P=#|X(4;@84)# <bbM=M >)$$$&'#+7>3 3276&54632# 4&#"&'6327>54&#"V 7)RLP J5jO!1%yk3A9=/] +Lov<+w/C &8g?j4c'L="s-#)6)"5FI73 #)K1PX@<@KPX@!ZM=N  >KPX@"bM=N  >K1PX@"bM=N  >@-bM=N  =M  >YYYY@ !  ##  +%27&#""'#"54$32733253?T DjNh2jy!uXVmnNX#{gDD 11q{g $)K*PX@<@KPX@!ZM =M >KPX@"bM =M >K*PX@"bM =M >@-bM =M =M >YYYY@ "!  $$  +"7'32654&%262#"'#4#"#46BT BlNff4h{߮yT9ln)XyfEF0!yh /@ #KPX@(bM=M=M >@(bM=M=M >YY@ &%##%$"+%'32654&#"7#"&'&#"632#"'#4>32o ;>uoM`Y=3?8fTsySm5LhL)uLT%+'%V7붠s0w^7c5@2<b`M=M >#$%"+>32#"&5462324&#"";Zq@bJ73zV}VqjwN1B7);WTs;1{7@4<9KPX@%bUM=M >KPX@%bUM=M >@%bUM=M >YY@ 75/-'&" $"! +4#"3267#"''>7>7.5432".'.#"6325 ;!Js! }} uRk= +fm@=!+gQ} y 7 (/;pa'7#%%%+)Ɂd196@61 %KPX@2bbR= M=M >KPX@2bbR= M=M >@2bbR= M=M >YYY@530.(&#!  +"327&7673727632#"&'#"54>324#";X+1NH#RE @Q&}mm7r3>u RsZJ\D!j9CqwAoM#L3t <NV-%;g+l53 6@. "KPX@1bbM= M=M >KPX@1bbM= M=M >@1bbM= M=M >YYY@53-+%#!   +"327&#"'&#"327#5#"54>325467672qbQTF @>5 # %oujPq\J\BIVPZ9ُϴL3t%+PRN{ X;++yq9CA=`=q $$KPX@-bbUM=M >@-bbUM=M >YY@ $&$!$+#!.#"332654'!"74>32#"'= B 2=+3J=hs`P5PV TPn+jqӔ +9ejL/߸; %7@4 <bSM=M >%%%"$+!32676"5632#".547!4654&#&bP3A%qmL ;{D'ok%!8?fw@ovH?+ >u@310,&%#" <:KPX@M=M >KPX@M=M >@M=M >YY;9$(' +4'3264>7&#"'>326767&'4&='#".{ K%LyTgHomB9otbV' ^8B\51Y --+e\?T9TaJhDwX5wAT`o-N;;LHF93C\l\_:VGJ5)K*PX@ <@  KPX@(b UM=M >K*PX@(b UM=M >K1PX@-b  IUM=M >@.b  UUM=M >YYYY@ 42!$$-#" +%327#".547.54>32#"'&'&#"363632#'&#"BVC\:qL RK#KbI)X#- +Ds 5#fi7!?JPmwH4VT-I!XE#PT5^;+'EL TV+3`P#2K*PX@ $.- <K1PX@ $.- <@ $.- KPX@(b UM= M >KPX@'Z UM= M >KPX@(b UM= M >KPX@(b UM= M >K*PX@(b UM= M >K1PX@-bI UM= M >@.bU UM= M >YYYYYYY@ 1/+$"#"!" +%4&#""#"54322'4&#"'"&54632#"'7326+J?!,hf#5sC+)1'T)LLT!Nqˋ7\DVR`3+VT[F'+;^VFW!J-TV4HwmRKK*PX@841(=GF <+*"!:K1PX@841(=GF <+*"!:@841(=GF <+*"!:YYKPX@'Z UM= M >KPX@(b UM= M >KPX@'Z UM= M >KPX@(b UM= M >KPX@(b UM= M >K*PX@(b UM= M >K1PX@-bI UM= M >@.bU UM= M >YYYYYYY@JHEC$""!" +%4&#""#"54322'4&"'"&546326767&'4&'#"'7326+H?+hf9qCR1'T+J;3adRL5J61`9>!Lqˋ7\DTT^3+VT[F'+;^ w';/+' XELB)C+k'?J-TV4Hwm7-#KPX@UM=M >@UM=M >YY@)'--  +%2>54'#"5432654&#"2#"$54$'?LP%oYV}LyD-T{R!Ź'w;/o'=B%'dRH3J^L!3XV1BHd+{+@ $K1PX@)b SK=N>@'bU SN>YY@++332$$ +##"&5463272#5354+"=737#"Jr`9/7>J+:9<9+B_!J76%,9!p_5""5=h# A5@KPX@8b  b QM=M= M >KPX@8b  b QM=M= M >@8b  b QM=M= M >YYY@ =;75,*(&"  AA  +%27&#"267"&54325467672#"'&#"#"&54632`V =KZZ_@3d%3 LZ^m@3=4BPXZJ򮜶h7HA=c?%-NV\JN{e@DuL#-%%)N;+ .@KPX@*bM=M = M>@*bM=M = M>YY@ +)%# ..  +%27&"267"&54>32673#"&54632dR =5=17PVXJ嬋+7yyA/+yBsK%+"'u72@ 21 KPX@0  bUM=K= M >K%PX@0  bUM=K= M >@.  bSUM= M >YYY@ /-#$#3$ +4.+&=37#2"5432653#'.#"3267 5=A #udJ^NP9^%V7 5  '.+`\Ɯ^>/fT{@b@ <&@UIMAY@ 9641.-!@ @! +72'4'"&54?&'.+"'537+"67674#&'537+"Xp%\13?G9! #% "5BD fB9?9L>/Eyy#53T/JX'55#3{{D`-y/-@*)#<=M > "!/ /! +%3274'"&546?.'.'52>7>3wXs:"a-94o>$' ) 9{7N-Y75 qF>9:HN>-Td@+-P;D/BAH)NfFB HM'b{/%1u%'Ly<z@$ @$b UM =>Y@ :726"225" +3274&+"=37+"2?#"&54.+"=37+"&8mv %=P<' =>Wnl';N>% =ov/!11 T R!/%d11%+LE@43@;(#KPX@+bM=M= K >@+bM=M= K >YY@EB?<4#333&#$& +7467672#"&'&#"63 ;2&#'54;254'"7;2&#'54;2L\Vb=4=7#3 +'=M<'+qB('=O=')"A=c?%+)%TZLN{fTRRR'+Hu@ FEKPX@:b ZM= M=  K =N>KPX@:b ZM= M=  K =N>KPX@;b  bM= M=  K =N>@;b  bM= M=  K =N>YYYY@A?=:86.,(&#! HH +2#"'&#"32>54&#"54>3232654&#"#"6354+.5>o1-%7+ gmX1^fL3m%)$:=3>j+ ;N?;-^5H3RFJETX4N)++%9i}Jq3!51 4ZX wD$0:@7 SM ==L>/-)'$$C +#&#"&47>=#5354&'&7$724632#"&-1cimb2/V F&/KD;JR?;H7P{765 5{PLV1'+#se[3LT4-RS{@ KPX@b=N >@b=N >YY@ $" +327#"&55"'676763+jT#Ljsh m= 7)HVJ`e^TIB}!"@K=K>CC+&#"&47>54&'&47327;b  rw b;;b sw  b;u{C65 C{q{C66C1~@  -&@*bbV=L>Y@ #C +4&'&7672672&#".7>5&"5>32)b !("%S;b  sw  b;4! 5[4f,$ ,L=~/{C65 C{%%%Rm%7 5@ 2 (KPX@*U V = =L>@-  bU V =L>YY@ 5 510/.'%! $ +.#"3!#&#"&47>5#"&546324&'&7672{/B'ZVJ6&$"$+6732762#"'&4&#"X\-+-/4'IN#!5+J8;5^>B);BX TV6%//9>@ ;: *K.PX@=b  bb= M= L=M>@;b  bb  U= L=M>YY@><87C$"#)$ +4&'&7672!2#"&54232654&#"&#"&47>#")b !^gj%3JPfO>Z sw b<  f,$ ,L#5(șb:F=X}[565 C%uXf@ V>!@  U M >Y@USONKG$C$$F +4&'&47327"'&'#"&'#"&54&'&473277267&54&'&4732772745F%F@m b1/V -UXyg4} ? @.   b  UM =  >Y@.$$$Y$XURNLIGDB=;85210.,+*(&%#   +"'+"3274#327&54+"'+"'0773077307+"327#7"&'#"&54+"'57V%*&)T; +TP )'+ 0'+rPN<&+^ J&L{Nqi)'=-P1PP-9PHPPsN$jD5yP%/[@<6 A 5  +%KPX@4  bb M  = N=>@4  bb M  = N=>YY@&SQMKDB?=;9430-*)(&$#"   +%2673254'"734#"7;2�'�'54;255"'676;632632#"&546327676)-'+T= +TP )'=PN;')^ I!'L{Lri);F\q5)=  !HRδ R%8XR RuK%jjE6{>FL7%#B~-R8@ )(KPX@0b ZM=K= N>K!PX@0b ZM=K= N>@1b  bM=K= N>YYY@ 87#&$334"% +75"'676;632;2&#'54;254&#"7#"&54727b;!)Lys+&=M;+#3j sHydh\JD uI!jjTRRm\~D@"54+;@ +*@8b  b  `M=K= N>Y@;:87#&$334$% +%527&'&+&#"+"63754+"54632'32654'"3'b@%"Jyr)'=P>+"4j pHyfg\J DuI!HjRRRm\zD?#54/w{6j( @U K= >Y@30.+$" 65 +72654&+&=!4&+"=37+"#"'&';2&#543 F5H ;=K<; 5'F(HR ;=M>H 4A11/%77!3X!)8\The3!35L )I@F#<bbUM=N >!%$!"$%#+4762'"$.#"6323232654'"&#"Lp xY<0+u!*1jGDfgM+́}u-0Tw M@9w 3K#PX@5Z  b USM= N  >K.PX@;Z  b Z USM= N  >@9Z  b ZU US N  >YYY@/-,+!#!%&$" +4'#";4654$3!.+326=3#54&+327!".;A1uN%}'TTP-#%Zy^B?V-! DI%#"):\9, KPX@bM=N >@bM=N >YY@ $"&&$$!+327&5432324.#"#"'"&54!2'u?3}@bP>Y@ 9876! +>5;2&''5432=.54>74'&'273?U" Mg+'=N<&+jZ9\?%?sh)'d{R&!oR$#PPTd{_(hh@ y6 4u/_K(PX@%b`K=M >@#b`UM >Y@ "$!C+4&'.7327"'&'##"&54632327>1b  hm b22T w+ k5RH.C $K,{7665{V3 % 8#d>J)?U!'#51,t@'" @%b`UM >Y@ 335$"% +27#7"&54632#27654+"'5737+"dH(NylRdK:s #P+9=N=%+jYRL&ed:-)C7u%IRy88@6 *)KPX@;b``ZQ  K =M >K.PX@<b``bQ  K =M >@:b``b  UQM >YYY@520-'&&%! 87 +"327632#"&'3#"&5432#2674&+"=37#  c3Lwo Lto\;J P !=R7-)-}{R)H6D= FF+`C lB1%33o1,@ '"KPX@bPM>@bPM>YY@ 339$"%+5"'676;632#"'&7;2&#'5432bM#N{lPdJ9w%L+;=P=%+TI)ff;+)D8p)P!!-@KPX@,b`ZM=N>KPX@-b`bM=N>@-b`bM=N>YYY@ #"'$"+"567362#"&'.637327632#".'5YWl\mZmL@7C R%/ imL^D\7KL95/:D! q7kצ%NB?RMs$KPX@ZM=M>KPX@ZM=M>KPX@bM=M>@bM=M>YYY@ "$&2"1+%;2�54;25467632#"'&#"9;R;7;PVZjG-j T31T@=G@-+J;'"KPX@ZM=K>KPX@ZM=K>KPX@bM=K>@bM=K>YYY226$"+%4.##"&54632;2&#54;2`k+JkXVR;8;V;:?';J+-?H=BĒT13/w 5@# @(US  K =M >Y@ 30.,)(&$"! 5 5"" +4&#"3262654'"&#"'5!273"/#32&#54yXc?PT%%3Vss9J3aPALC 9@uHtn2l3-)}bFޓ7/!79w-7^K,PX@ 3 <@ 3 KPX@.b  U= M= M>K,PX@.b  U= M= M>K1PX@4b  Z  U= M= M>@2b  Z U  U= M>YYYY@6420(&$" -- +"=307"367632"'#!54;654&4&+326RBL9 C!)B%J/LD;srX@31TP?bX-<91Z3RD~Fb{)-0l3Bntb;Q@N.<bbRM=M >7620-+! ;; +"53&'632654'&'&'47632'&#"#"'3265462-%H-Lib?Fhuq5Nr+8oO?݅n`%6)-;Z o6/q.?kFTXHj1^{1%q.EYFj} T9!%!BloP&KPX@$Z`M=M>KPX@%b`M=M>@"b`QM>YY@"  && +232467672#"&'&"#"&546%+1D3^Ro{?-9, \6XNbV=7^sL?]3!-!!FN=P=/oP6KPX@/Z bUM= M  >KPX@0b bUM= M  >@-b bU  QM>YY@20+)&$"  66 +232#"546;467672#"&'&"32+#"&546%+1DwJ6#h3^Ro{?-9, \tJ9#b6XNbV=7^%)1L?]3!-!!F!1N=P=/oP'KPX@$Z`M=M>KPX@%b`M=M>@"b`QM>YY@#! '' +2#"'.5"#"&54632327>/=V`PX5\1'-?}qM^4C4/=P=NTyE-!3]@L׶795_@ ,+! @bM=M>Y@ 42'%#'" +32>7#"&'&">77#"&54%5467672!%%!=/9, /D !8Bdg7Js03^Ro{#'pVX!-!!Fb/pE  wV'+)$" .. +##"56?#"'543454&#"&5>3232:%6 X 13'P%%!RfD= # `F @su'HA-F95} ;7:KPX@ZM=N>K.PX@ bM=N>@bSN>YY@  +2# #52767573#327>T/Pwٍ{ %  /1DL'`5%%F`w1%}6@?@<>   <  b TK= >=;GC% +"'&'# 5#5354&'&47327!54&'&47327!327&54R -\6;N^+JI5T n+JEJcYX?',"'34HGPK,PX@bM=M >@bUM >YY@ '!)&$!+47!.+'#"&54>75#"'7!232>54'mK>#N 9kH7L #)/)hVy37?F"򚮷9{{H1!C)tuDdLt@ KPX@b=N >@b=N >YY%#("+32654'&54632#"&55"'676;^bmV{D;f7%%L`sF!1qר+uI!9@=>O*C +%&#"&47>7632&#"&47>.'.5 o`BY(5)<9   dj!  # 9551*+/+y -  55! 0 l N>?R@b`=>YJ$*H+%.&#"&47>7632632&#".7>'&'&&&v "PX-Z )<+6?:33Mdu Tv 5>VLQ559.*p/+/+y L5 55;@ \ \LE%@"M =K><8'&#!%# +67672#"&#"&#"&47>'&5&#"&47>76)R+I1D7%(.0 )5D Ry V# 'B+f 5X1H&%5#;04q % `255@=&! 55L1y}y<V@ <(@UK>Y@ 227229"2# +%32&#5473>=&.+"=27#"764+"'563#7}99Z7= H =-3# u 13ۍ0> $777 61:7%:7!)}#D`@3.KPX@.b=M=M=N>KPX@.b=M=M=N>KPX@.b=M=M=N>KPX@.b=M=M=N>@.b=M=M=N>YYYYY@ %s&%+.6?%"547''67433!2>32327>726?2#67>;5/q5/) '+bR$ܻS- ;*E;]#C 1TiHeS q  =&/< |H ) '?D1{ 5w@<: 9K.PX@ UM=M>@UUM>Y@ 1/,+$! 5 5#! +%4#"326#'67#"547#"'3!2367632;'HV=HR3 +u! # D} N%U13@JD{?C4/8qyh#s9F3 σw9JFKZm+/w(y@%$<&:K.PX@)db`M=M>@'db`UM>Y@ "$"#) +#!2#"&54232654&#"#"'^fj&3JPg)F#7lDh#5(șb:F=X}NRy JJ` 8@#"%1, <:/.9K!PX@0b`M= M =M>K.PX@.b` UM=M>@,b`U UM>YY@ 42 8 8  +267&#"265#"!"'3!27&'#"&546L}u7P^)}/ %xH/ P5cb7420" :9 +72>5467>54&#"##"&5463232&#543 ZR%+$K@9XT/1R7:5'+' %;M=HZ`V':\3NV9-)7+Bp>-#/b?1%31#+D97@4bM =M>,*"  99 +%32�54;>54.'.54632#"&547654&#"P%=R>% ())3:5±T1/TX9?M<95>9H13%1}?b.$->qA+7)-  9VNJh>9LC!#+-;pK(PX@'b`M =M >@%b`UM >Y@-+'&"  ;; +#"=307+#"&54632332654&'.54.'=R;' '+'5:7ŰP3/TX9?L;95>9 13%1?b0"->qA-5)-9VNJh>9LCZVD@  KPX@M=M>K!PX@M=M>@QM>YYY%%$$+34>32&"3267#"$Vk嗑f^yTk^J馕=1\_j?wQ4D '8@5UM =M > &$     +"&4632"32%".57>3 =OT85VR,ԢzgU kRoRP:7R2-JҾ[lz-} >A@>3<UM=M > -'$#>;  && +32>54&#"32654&#"&47>54&'&4732632#"&i3?A[9'!?fOJbw b<;c s)ۭX])Fsw=B2)'LM/9#^\Z`J5 C{q{C6bw=]# mbFf8-@@=#<UM=M >)'--  +"632'"'32>5".547&54632?LP%oYV}LyD-{R!Ź3;/o'>B%'eRG3J^L <3YV1AHd7oB@ = 32 KPX@;  bU  U M =K = M >K#PX@;  bU  U M =K = M >K%PX@AZ  bU  U M =K = M >@?Z  bU S  U M = M >YYYY@"@?<:6410.-+(%# BB +2#"'&'"3#'.#"326754.+&=37#2"5432653>%+%!Y ^NP9^%V 5=A #ud+!)u+`\Ɯ^>/j7 5  '.x~-}G=@:  S K = K>GFCB?;8721CCC+4&'&47327&#"&47>=!&#"&47>54&'&47327!11DZsb<20,*%$  +2>77#"54%&54#"567364632#"&&!%4 ! }/)byj!LC;JR?;H'sVXT#!7 uZVV< 3LT3-RR'+syN@M10,H I @.b   b  UM=  >Y@LKGEA><9>3362"  +%#32�54;2?0&+"&=3%+">754&+"=37+"327#3%7GB9)s9#!-P#'%w%!'  %=P;' q Rj+13^X 11 !'78 !1!11%-ZP-%F!`yK.PX@%bXK=L>@#bXUL>YY@#226+7>54&+"=3%+"327!563 >;Z?9 N!9H 2C-!33/-;-) B@>= : .KPX@3b M = M=M =K>@3b M = M=M =K>YY@A?971/+(&#!  +"327&#".'&#";2&#54;265#".5432>7672`m`L;X=6-7 '1  #=R=% D`ud4VJ9cX`9ʞʹ+=%+TZL9B755%1/TLF=c#+DGO@Lb`SM =  K  >DA?=9843)'#! GF +72>5#&5473>7>54&#"##"&546323#32&#543  :5-%+$K@9XT/1R7:5%('  %;M=HM2f@.':\3NV9-)7+Bp>- +W71%31#+DEI@Fb  SM = M>A@<;20(&"  EE +%32�54;>5#&5473.'.54632#"&547654&#"3#P%=R>% *%'3:5±T1/TX9?M<9,69  H13%1G6X(#->qA+7)-  9VNJh>/?e3!HJSK PX@),Q @ I<KPX@),Q @ I <KPX@),Q @ I<KPX@),Q @ I <KPX@),Q @ I<K!PX@),Q @ I <K#PX@),Q @ I <@),Q @ I   Zb= = M=M  =N  >K PX@;  Zb= = M= N =M  >KPX@<  bb= = M= N =M  >KPX@?  bb= = M=M  =N  >KPX@<  bb= = M= N =M  >KPX@?  bb= = M=M  =N  >KPX@<  bb= = M= N =M  >K!PX@?b  bb= M= N =M  >K#PX@Ib  bb=M= M= N =M  >@Gb  bb=M= M= N =M  >YYYYYYYYY@PNHGDB?>%u'"%% +%&5.#"326##"&'.54324&'&76723!2>32326?2%"'"'&%'"G1b53gP}%3%Pc=HG)RB)c !%bR%ܺNR);*/ /(-ʥg5.XT6G#S&07c, h) ( ,L==T< | "$"L/SV6@)U2 I RK.PX@Pb  bb   b=M= M=M  = N>@Nb  bb   b  S=M=M  = N>YY@TTTVTVQPHFB@><)$'"%%+%&5.#"326##"&'.54324&'&7672!2#"&54232654&#""'&"G1b53gP}%3%Pb>HG)RB)b !^fj&3JPg:8E -.XT6G#S&07c, h) ( ,L#5(șb:F=X} 7-',"$JL K[f9@#2 5( dcO \L< 9KPX@Gb  U = = M= M = N=M >K.PX@J  bb  U = M= M = N=M >@H  bb  U  U = M= N=M >YY@$ _]ZXSQGEBA:710/.'% K K#! +%4#"326#'67#"'"'&'##"&'.54324&'&76723!2367632%&5.#"326%#"';'HV=HR3 +697-3%Pb>HG)RB)b !B N%U13?JD{"G1b53gP}*s! # ?C4/8qyh "$0#S&07c, h) ( ,L3 σw9JFKZm.XT6:4s9)R@f@ 43<:K1PX@!M=M=M >@UM=M >Y@ &,5#$#%!+ '.5#"=47265732'#32>54&'.54632.#"bRVZh%NH3-L-=fN8\n66Rfu PbH/Cp ZgP?@)#5J' <-=>%lj9fk hV;9m)7n+=yDO@   K*PX@G  b `  b b M= M= = N  >@D  b `  b b  R M= M= >YY@$EEEOELGF@>8631-+'&"  DD +2325#"'&'#"=6;47>7>323467632#"&'&#""&546#3:R+1 DDAfD= #39%6 Y e5\Ro{@-9+ \5XNbV>e4'7^ 95}/ ; `EL?\3!-! FN=P>/ds'HA+1%Wd@* ? XT<9KPX@8d   b  U M= M= M >KPX@8d   b  U M= M= M >@8d   b  U M= M= M >YY@db]\WUOMGFB@=;75-+(&!$"! +4#"3267#"''>7>7&'#"'&'#"=6;47>7>323262".'.#"632&'47#325 ;!Js# ,$,AfD= #39%6 Y vuRk= +fm@=%d4'!+gQ} y 7 (/"-95}/ ; `E (Kpa'7#%%%+)Ɂd1}ۋs'HAFs4@<ZY  Q KPX@E  bM= M  =M =N= M  >KPX@E  bM= M  =M =N= M  >KPX@E  bM= M  =M =N= M  >KPX@E  bM= M  =M =N= M  >KPX@E  bM= M  =M =N= M  >K!PX@B  b QM= M  =M =N>K.PX@F  b QM= = M =M =N>K1PX@@  b I QM= = M =N>@B  b  ` I QM= M =N>YYYYYYYYY@pnlb`^XVNLGE?=;:97$%%#!+4#!323"'&#""54?>5#5354>32#"&'.#"!27363 #"&54>32654'"732"&#&#""#54;>5 93F/RL/ a 5' PJXH1?N > GFHly7XN{F1) \-:mJ !=dba a 7' D5!136`h'FqK+1=)P`J5+iZjVP91!'X511"2%50?@ 21 @.b=M=L=N >Y@ 'A*&'+4&'&767267632'&#"#"'&#"&47>32654'&'&)b !huq5Nr+7nP?݅Mbt^w b< H-Libf,$ ,LPj1^{1%q.EYFj}5 Co6/q.?k%5@@/4>2 6KPX@/  b= = = M=L>KPX@/  b= = = M=L>KPX@/  b= = = M=L>KPX@/  b= = = M=L>KPX@/  b= = = M=L>K*PX@2b  b= = M=L>@<b  b= = M=L=L>YYYYYYY@;955B%s +3!2>32326?2%&#"&47>54&'&7672676'' '+bR%ܺNQ);+sw b<)b ! q5) ==T< |5 C{f,$ ,LNLA ERM1) E@E@)$#4 oji[V zbON <  b e  U G M  A~|xvsqnkhe_]ZWTRJGDA><$##36#3(1++"'.#"=737#"6&+"'5737#"#"' #"'.#"=737+"'.#"=737#"6&+"'5737#"#"' #"'.#"=737  va N'q +uy'R+T1  sq ! 72  va N'q +uy'R+T1  sq ! 72  /' ## -4) ## Jg/) #) /' ## .3) ## Jg/) #}/4@1beSGK?+#!#!#!#!9 9f9 9f+-1C@1  'KPX@6  bbM= M =M =>K.PX@6  bbM= M =M =>@4  bbU M =M =>YYY@><75"3#4! +3274+"=7307+">76"#".5'3#".54632qB +%?N;') ' =Pb11iHg5Z '4H?\:#XPPNu 5! f2GeRV'!19LPx7/;B@<70/KPX@*K=M=M =M>K.PX@*K=M=M =M>@(UM=M =M>YYY@ 334'%'%%% +%&7632#".5".5#".546323274+"=737+"+/^ '3HVu;Li5Y *1H7P1 s= +'=O;'+sX' 1:Lf=4'";2&'5473>54'"\E\R! N1 f1L R5}# !h% %% #]! %% #5 XAO@L0<7$<bUU I K ?A>;84#334%"$# +4632#"'&#"632;2&#'54;254#";2&#'54;2^o=RL.7Xj=J/\+o/TV$/ ^)n -;Łf3#+'P'5ZXXR/ ## /+%/ ## ) $@ @/b``UIMAY@#!    +2"&5464'"'673#"&5432326=#5#! !'u?)-=-P+'+#;"#"_K7)Xq-3'3;uB=.<@9.<b``PM >"$!F+&#"&47>=4&'&476723632#"'&#"w+X9EX+3Dyu fP)93#3j0- ./mJ/-b5!/?#$ )>@;<bZUIMA""!$2"3 +4&+"=7+"327#7##"74323276% %#+y9RJL;! 1# '' !7)yy@7 H/ 1H@EbZb UUIMA/,"6""!#"" +327672#"'.5##"7432327654&+"=7+"h#-%+P-=-)RJL;! 1% %#-w;5'3/oy@7 H# '' !-+4PKPX@  1 <:KPX@  1 <KPX@  1 <:@  1 KPX@,I U  U I MA@-U U  U I MAYYY@420.&#! ++ +"=7307#"37632"#!54372654&4&+39 H/h!`/+]7$/1@L /5=F'#!w%aK4VN#o#-Hw EC@@E@)$4 <e G M ADA><$##36#3(1 ++"'.#"=737#"6&+"=737#"#"' #"'.#"=737 w` N&q +ux'R+T1  rq ! 71  /' ## -4) ## Jg/) ##>F@C>9<::b UIMA=;("$'##8! +#"654#"=7307#"#"&54632327654'.#"=737+53 V-X9!H-5!A)J' 7'  ;  #! L/1H!%6e+ ##Z)%KPX@ e>@ d[Y%$ +#"747632) } +1y +u-K1PX@ d[@dd[Y%&&$+#"&54?632'"54762  $%. x 2#3z %3   -;3#@ <R >  +"&54>7327D+\A ;@^Z;=L3# X)%TE;7D @eM >Y@   +2'>54#"547D+\A ;@^7Z;=L3# X)%TE7FD @eM >Y@   +2#".546`^?< B[+DFFV%)X #5L<9]dF9i@K#PX@M =M>@QM >YY###"+#"'732654#"'632b) L=!-h5q~ b ;Pb ZF9i@K#PX@M =M>@QM >YY#$#"+4632&#"327#"&Za+L@JB!,fJq~ b ;PLTb s @bcM>$&"+4632#547654#"#"&sl\HbGL^R?H+/ +# 3RZE98;<9HBA3+Xs @bcM>$&"+4&#"354'&5432326l\HbGL^R?H+/ +# 3RZE98;<9HBA3+XJ+"+5ouVuJ+"+%557ouVuB3@<e > +#3#uVu8n?3@<e > +3#3tVujnwN @9 >  +&''67F3RL3;Nq'RXN\'_yJ @ :[ +#&'767f;1RH1`%XTL`%P,KPX@ K>@GK?Y +#3rrPqyX @IMA   +!"5463!2!5!X%-"-j h ,@ d[Y$! +632#"&547% N&'' d1/! h%KPX@ e >@ d[Y%% +#"/7>32s/S1d!)-u@:> +"'6')'H' RT @IMA   +!"5463!2h2 "-!.J?` - @ d[Y! +632"'%J?L2T\Ju KPX>[Y   +"54?y/% J,9<% s JLf=$@!<GK? +!7J=JDf;@ 9[ +!J;B+J+@(<UM>###"+4&#"'632#"'7326<5,PssP! 3>1>N sqP;B?JO@@UIMAY#$#"+4632&'"327"BrN%#3<<3 Ns N>13<PZ-j @:K>  +&73563b3%-)1%5%Z;y %@"9GK?  +#'5#&73%)1&5%^J&@# :9GK?+'5#&73563#H)1!--!T#5%}--TZ@GK? +!&7%/+5%NV @ :IMA$" +327#"&'weA^y VPR=D @M >$" +4632#"&LD;JR@;H3LT4-RS; !@UIMA&+"26544632"V55V8gGFjh:+);;)+pdbHFdu5P@< :d>-" +#"&54>7327'@Pl;uRA+RK)N%5<^R3if<-+LuT'$$#h;@8<IUMA  +272#"'&#""5>32B""f9L95B! 5?%XXf+#%%%Rl3u-K1PX@ d[@dd[Y%&&$+#"&54?632'"54762  $%. x 2#3z %3   -PJ=&@# <:IMA# +6727#"&547PuBb@A/Xt Ta`JN#)b9@3Dd}"+'&'0'7'67677˲    ! !\w-7i@$ 6@UIMAY@1/&%# -- +"&54?&#"=7327"769327"'2654/-7D#DD=V;oy !-F+I7VL;!/ A1+#54'"'67 R5}#\Eg! %% #5 - !9*)K#PX@"bUQK>@(bUIUMAYY@ !-(#+332654'.4632."#"&#"69-*+9)9wTIrNo70NR5%533DNT+!`L)%''N1#BX#P;+'%$-7%9L1BJ@G 9'<;.9U I M AB><;3")! +436767.'&'#"=!765&=!'"&#"5474'&'2&#"E f  HN1 81'B-Hb+ HNH+-/ ?5N?TE)) 2P )%1\7-+ FL+56F@C&<bUIMA,*"  66 +32&'05473>=4.'.54632#"5465654&#"{ P3 !k`X'7D/57Od %% !%=J+NYZK/ %1G3FThc5)8K%PX@K =>@S>Y@  +#!5)s5)AK%PX@S =>@SK>Y@  +#!5!5D)'s5)AK%PX@S =>@SK>Y@  +#!5!D)s35)AK%PX@S =>@SK>Y@  +#5!5!D)׍s)5):K%PX@ =L>@dL>Y@  +!5!;D)sf#@ dGL@ +73!fiZ\fh9!@GSK?+7#3!!kk>h^X @<d[ +7'373)l\!V?XF .@+SGK? +5!5!//XXZZZ+19KPX@  >[Y@++ +2&7>54./&546%2&7>54./&746;Xm;J DF`;Yl;J  DEHXTy)@  M3HXTy)@  M30@<d> +3#3|L4Mu0@<d> +#3#|M4Lu)."+%5%N%|L9M)."+%55w%{L7LZf !@UIMA&+"26544632"3V55V7gGFjh:+);;)+pdbHFd=FV@d[  +2#"/&'46#   +V  /=TV >K*PX@ d[@dd[Y@   +2#"/&'46%2#"/&546% +:#   +J -   /=TV>K*PX@ d[@dd[Y@  +2#"&5476'2#"&5476#+ ++  J-  / %)b=@:<: 9UIMA  +267#"'&#"'>2#/1%3PJ%'-1KR<-> 5-/3 \[:-\@M=M>+462"462"@Z??Z@@Z??ZZ@@Z@%Z@@Z?=h #@  <eK>" +73##=' 7V =h #@ <eK>! +'"'#&5473h'7# =h D@  @dGL@Y! +6323#'=& =h D@ @dGL@Y" +#&5473672h'  "= K@  @deL >Y@  +%!#.'5673+RFXdfqD $+=V>3B5%5#'h%KPX@ e >@ d[Y%% +#"/7>32.R2d!)-' h ,@ d[Y$! +632#"&547 N''' d1/! N @9 >  +&''67F3SL3;Nq'RXN\'_#;@8<IUMA  +272#"'&#""5>32B""f9L95B! 5@%XXf+#%%%Rl3%X @IMA   +!"5463!2!5!X%-"-;b @IMA   +!"5463!2+!b!*,^V @ :IMA$" +327#"&'wdB^y VPR!=5D @M >$" +4632#"&!LC;JR?;H3LT4-RS9'@IMA+462"%462"&B^AA^MB^BB^Bf_AA/-Bo/AA_?B9f%@"bcIMA$'%+#67654#"#"&54632;??151%_=JG38;BJP'!? %%MOT *@'UIMA    +"2654&4632"+55V79fHFjh:+);;)+:dbHFdu-K1PX@ d[@dd[Y%&&$+#"&54?632'"547626  $%. x 2#3z %3   -s;yh @ :[ +#&'767f;1RH1;`$XTL`$P!,KPX@ K>@GK?Y +#3ssPqwP3KPX@ K>@GK?Y+#3#3qqppPqo -K1PX@ d[@dd[Y%&%$+3254/&#"3254'&"!%/ y 1#3%3  ,*@' <UIMA$#+462"'327#"&'o5N77NveB^y qM88M6PRqH @ 9M >$" +.#"'>32veA^y PR7@:[ +"&5467;Ym;J DEHXTy)@  M3=@9 > +2&7>54./&546;Xl;J DF=GXTy*@  N2=@9 > +2.546#EC J;mY=3M  ?)zTXG=@9 > +2&7>54./&546;Ym;J DE=GXTy*@  N2J?` - @ d[Y! +632"'%J?L2TJu KPX>[Y   +"54?/% J,9<% s j# !@ :9GK? +'5#&7356)1&5%+3%\% !@: 9GK? +63#'\)1&5%3%H @@ @eK>Y +'5!.67!{!j!dH@dINB)!" ++532>54'.5432_S22)/712_9JRPZ5')5#Pjw XQ@ @UIMAY!$"!+3263#"&54632"&#"  RnwT ?  +&73563Z3%-)1%5%R;y %@"9GK?  +#'5#&73%)1&5%B#&@# :9GK?+'5#&73563#+)1#--!+!5#-+B@GK? +!&7!#-+5#*@'ddN>  +2=23263'"&5462` V-)5bn@!%!9u.@+:ddN>  +"703033265462')`%5)-: %!Bm @IMA$ +462#"&B^?A-1@1=A-/@B} @IMA$"+4632"&%462#"&A-1@B^?jB^?A-1@1=A-1>B-1=A-/@Bd !@UIMA#"+4&#"26%462#"&A-/@?_?figIHf/DFZGG-Fddgd@9[ +2&7>54./&546;Xl;J DFhHXTy)@  M3H3W@  @UIMAY@ $% +'73#"'473254 hF)cBAN)f b&Mk"+ TP1%?@< :d>-" +#"&54>7327'?Pm +"'6+')'H' RTL@@  @eIK?Y5 +'5463!2"'5!F%5&#m#!3;j&@# <dM>""%%$+#".'#"&'6732763232+Y4+B#R!3Z /IT/ 1PN yRA^H\=bb (:K*PX>[Y  +&'767+=)TN'H=d`XPhqm3uX @9[  +&''677+AR)K\qHTep D 4 :K,PX@ M>@IMAY$" +327#"&'weA^y PRDV} @ 9IMA$" +.#"'>32veA^y DPQN};@8<IUMA  +272#"'&""5>32B"#f:L95B! 5\3X?%XXf+#%%%Rm3#T @IMA   +!"5463!21"-!.N @IMA   +!"5463!2>() Pb OKPX@UM>@UIMAY@     +!"5463!2!"5463!233'*')N;@8<IUMA  +272#"'&""5>32B"#f:L95B! 5\3X?%}XXf+#%%%Rm3{f @IMA   +!"7463!2\() @IMA   +!"5463!2>()bK PX@ = >KPX@ = >KPX@ = >KPX@ = >KPX@ = >KPX@ = >@ e>YYYYYY +2"&'1!/TVD@ = > +"&'2/+'))%# Q@ @UIMAY!$"!+4#"#7672#"'72326  RnwT ?KPX@dN>@dGNBYY4!+672#!"&=62! "'%7!Bm$@!SK> +35%!!tB3;j$@! <U>""%%$+&'>32>32&#"#"'&#"% Z3.?#R 3[/JT/ /RM9yRA^G# "+''7'77#:;;:R99::]N"+4&'7.476 '6 /(). RO3'$'q)@;$ '=hB0 /@,UIMA     +!"5463!2!"5463!2>>'*')`\ %K*PX@ e>@ d[Y$% +#"/7632  +?F)/b\ %K*PX@ e>@ d[Y$! +632#"&547 ?2' /' %H;@8<IUMA  +272#"'&""5>32B"#f:L95B! 5\3X?%'XXf+#%%%Rm3;7K#PX@dM>@dINBY)!" ++532>54'.5432%^T11)/711^9JPZ6')5#Pku'J!6@3dbINB   +2#"&5476462"%462"&%l  /f A^BB^BA^BB^AJ1  %#_AA/-Bo/AA_?B9@ :M>%$ +73267#"&59m-A- 4i3Pck>+^LTTK@  @UIMAY@     +!"5463!2!"5463!2>>'*')3 #@  <d>+"'672"'672) %% %%D R   R  z J@ $###! +"#"'7&'""5>327632272#"'H $(! 5\36+$#"f9=1[ %%%Rl_mXXf5(H@E"< U UQM>&$! (( +"&462"&462272#"'&#""5>327((7(7((7)B#"f9L95B! 5\3X@$(8((80(8((8XXf+#%%%Rl3+^1@) K(PX@(  U UQM  >@.  U  U UIMAYY@"/-,*%#!11  +2#"&#"#"&567232>72'"&#"#"'&=63232> 9Z!!/ ?_!': ;X!"!1 B\': mH'% hD## mH%% jE$#; p@ K.PX@dT>@deGL@YY+673!.'3#>7!#.'fqD #/- FfoqdH !1dRFXd%5#6;!';o15V>3B5y)#@  <K> +55673.'5;!';o05)0- FfoqdH !2v"+%55%{L7L=F*@'<QM>#$#"+4632&#"327"=qP 3BB3)Nl?F31H=V*@' <UIMA$#+"&462.#"'>325N55NveA^y N88N5PR". "+''7'7723322233.v"+%5%'O%|L9M.v"+%55%{L7L. '@$<:9d> +%55%#3#Nr|M3M{M6Lu\'b*@'<QM>#$#"+#"'72654&#"'632pN)3BB3 NpNn>H13F@oyN@M> +462"?V@@VV??V@b}5@ ,32#"#".'#"54>/%A-<+L3 4!4-%3A'L 3'-!AD!hJ (\@UIMAY@$#(("$"$$ +2654&#"4632632#"'#""2654&9Z::-/7kHV67UHjhJU76VHH/8:Z99-FF-1ACueBBcGFdBBC/-FF-1Aso@ KPX@eV>@deINBYY5%5+57654+"74?632;2:>!#9!     w @ :M>   + 7!2$67#D !e#7%qLVN%) %@" :IMA   + 7!2$67#D !e#7%qLVN%= @IMA   +"5463!2##"=#0#.R @IMA   +"5463!2##""0#-\XjN@ < :9KPX@QM >@UIMAY"&""+6$3232$7#"'&#"\/aՅ՘yH;DRϤR`hADDE dwDBRD @ 9M> +2!"'6$#9j<#%#3aT;%;9e K.PX@dT>@deGL@YY+!.'3#>7!/- EfoqdG !1P6;!';o15#7H .A@>, -<bQM > +)&$  . .# +32>7"'"&54?4&#"#"&5463227^% 5+=%/p4- L'w 3+L+(+#B7!   !/L?mL<@9<SQM >  +27"'&74632#74#" 7#-JF1!OB-B1?';2!O9_;% +&#"&7676=4.'&5673&"&462P%!+#)$ ='$$V    G%%DF $@!QM >    +2"&464'"32PHNKGH;L8Fe9;[VwgsmB+6@3 <+:RK >#%##4"+726754+"=77#7'"754&'#"=7/4 101%34R )#?F89g%L5@2<bQM >  +2#"'&#"32>7#"&465)!#'-!  1FDIRL$'!-D9-5 BVe'o@'#< ;KPX@ bQ=M >@ddQM >Y@ %"""+5&#"323#"'&5463254.#'673#7!;B'%5'D;1 ='-?"Z%m}#%N;c; '#h;p@ 65 @!  d dPM >Y@98432':" +672&#5437>=4&#"2&#5437>=4#'"'56723.+= 5#J  + 9%E  T! dR956  )4 9hsHPP@MA% IG><42/.(&$"'# +54&#"&#"&7676754.'&'6736726723&#"&76=4&#"&#"&7>N%%!+#($ =(/)>3/'3")#+!%#$!*) Zw+-    459865 w+&   L(@@=<9ZbN =M >""#+7##6326'&=47623254#"N'= #)#+!%   %/++:   ##'<4@1<:QM >$"$"+27#"=#"=472=732#y%;B/=>L?'D M hhB+S@" @eK >Y@ ###'33#+'.#"'577+"7674#"=07#"&Z 7P ?=!'7 `!  #yHBu@7&< :B>0,9K PX@ b WM >@ b cM >Y@A?<:42#%##$"#%! +432767'&#"=27#"76#"=327#"32&'"5432'&'02&'" DZr !5  ')?Q-!;! !4 -))Pu?J Z`!7?jN% @9[   +2'6N32w'%/))Js1f @9[   +%2'631w&/))JssE@ <:KPX@dM>@dIMAY%$ +73276#"&5-C;+   +%2654&#""'632#"'67Zm\`q5'yeRi%3}B7;$ʼnf-?1X 'M@J%#<bUM=M > "  ' '  +"&54632"32672'"&54>32.>PT:7XSZl\`q5'ydRh$3~:T8:WU<8T7;%ʼnf-?19u 'M@J%#<bUM=M > "  ' '  +"&546322654&#""'632#"'675>PT:7XSZm\`q5'yeRi%3}2T8:WV;8T7;$ʼnf-?1Ry "@9eM>   +462"&2&7>54'.546{Z{VZ{VT^A.d{BJ3Z;WR;9WRV{3-d+-1J%#^Tb@d[  +2"&5476%l  /f 1  %#u'J!6@3dbIMA   +2#"&5476462"%462"&%m  /f A^BB^BA^BB^AJ1  %#_AA/-Bo/AA_?B?O\@Y <  bS U  K=K>A@IG@OAO=;4320-)%$ +!#&#"&47>7272&#"&472>&'!"2#"&5476/71x) 5[l)65  N1  %#v @IMA   +"&54632>PT:7XST8:WV;8TDT@J*@C  d Z  bZ   UUM=L>Y@$QNEDC@=<763.'$!TT  +2#"&547626762"'.+;26&#!&47>54&'&47!272'.+"R%l 0g %jLF  FPf1#I5( bd s??s ;\50 %3%cj#11  %#1R {6% H;+)9 u-5 ;y3{; 2 q#wd='}gK(PX@3dbS K  = K >@1db  US K >Y@&gfa`]YVULKHDA@;:541-*)   +2#"&54764.'.7327&#".7>5!&#"&47>54.'&47327!m%l  /f 313313313313323314413323{1  %#b=L#  2  1  #K>=L#  2  1  #K>m=L# 63  #K>5=L#  2  1  #K>m9lK(PX@%dbK =K>@#dbUK>Y@541-*)   +2#"&54764.'&47327&#"&47>5T%l 0g l3143133133231  %#b=L#  2  1  #K>=L# 65  #K>*=@:dbM =M >)'!  +2#"&5476"32% 76$3 %l 0g 㮚r[1  %#3-Jӿh% lzLpK(PX@& dbK =K>@$ dbUK>Y@C?<;32/+  +2#"&5476&#".7>54&'.'.732776&'&47327X%l 0g Fy E0KE  {Z  R%:H @Dz JF"#1  %#-{965 9{70/R0 2  1 59-'-3 2  1 #8J:qEX@UA;'< db `M = M>CB?=42+)&%#!EE  +2#"&54763'&54>3 32672!"7>54&#"#!&'63m%l  /f M\)h F]!/- L-%TfGV!%L -/!]1  %#l?fɠe>lZhd!'NƴHk\!dhZF!=P@M')< dbbU= >:9-+&$   +2#"&5476462"%462"&327#"'&54&'.7$72%m 0f A^BB^BA^BB^A3'F%%!RfD=/VF&/F2  %#_AA0-An/BA_?AHA-E95}V- 2 +#seD?>@;:S UK>=;4320-)%$ +!#&#"&47>7272&#"&472>&'!"/71x) 5[l)65  N-;K(PX@-ZZUN  =M>@+ZZ UUM>YY@ 76-,)$;:  %"! +3254#"32>54&#263 !"&#&47>54.'&47r\31V`>7qTD#313313'1)9a^5}N%+ 3  #K>5=L#  2 R-,a(@ZUK>Y@&" ,, +#"".#.7>54.'.73!7'.{H314BL%313313O!%3#Ӣ=L# 6 3  #K>5=L#  2 }%X5@ =N>5 +!#!"547}WL+#+ 3q   1D@:  <6:K(PX@7 ZZ U N  =M=L>@5 ZZ   U UM=L>Y@A>5430-,'&# DD +26762"'.+;26&#!.7>54&'.7!272'.+"jLF  FPf1#J5' cd  s?@r  ;\50 %3%bj#11R {6% H;+)9 u-5 ;y3{; 2 q#wd='})9*KPX@<KPX@<KPX@<K#PX@<@KPX@,M =M =M=M >KPX@.M =M =M =M >K#PX@,M =M =M=M >K(PX@*M =M =M=M >@(UM =M=M >YYYYY@# *) +%2672#&)"5476#!""'6743)26323od7 '!%y?5%!\ #-+ -Z| pT/ B1Z-WkK(PX@%  S K  = K>@#   U  S K>Y@WVQPMIFE<;CCC+4.'&47327&#"&47>5!&#"&47>54.'&47327!413323323314313323314313{/=L#  2  1  #K>=L#  2 5  #K>m=L# 65  #K>5=L#  2  1  #K>LD#/>@ KPX@+ U  M =M=M >@) UU  M =M >YY@%$=;53+)$/%/ ## +326762"'.+""'654'62"32% 76$3 5{;2  1 TR5{;1  1TR㮚s[-J{F9+)#.I{E9+)#3-Jӿh% lz1-)AK(PX@K =K>@UK>YCC+4.'&47327&#"&47>5314313313323/=L#  2  1  #K>=L# 65  #K>%-VTA@UM>Y@RQNJGF:5OC+4.'&47327>7676'&47327&#""'&'.'&#"&47>53233143kd?b\f 9[L>=cRRD"7,413323/=L#  2  1  #K>KZA 2  1 IJD%JJ 6 9W,* n=L# 65  #K>D. @< =K>OI"+7672&#"&47>''&#"&47>X41@71  zј +43 "-=1xF{ 5>CA/0+1 2  1 \P'%+  2  1 @  -D@ ,-  K!PX@0b`  `K =  K  >K(PX@4b`  `K =  K = >@2b`  `U  K = >YYY@@>;743G" +%#'"'#&#"&47>76&'.73%&#"&47>&M44  m61^{n5|T< R )l  n  e.>)'( X ) * X`R65 NaLq, 2 s 1 5V01065 ,0)-=k@)@!bU=N >Y@ II$+!#&'&#"&#"&47>5.'.774.'&47327 3N*(R"#=RR>" Z= M#>R ! R=#'4/))^R65 R\V-P 2 $@^R 2  1 R\\1#=W&@"7, QF <:):TC 9K PX@.U UM  = M   >KPX@0UM  =M= M   >K%PX@.U UM  = M   >@, UU U M   >YYY@*@>&$VUMJBA>W@W<;30('$=&= ## +326762"'.+""'654'62!272'.#!"'4743!""5&563!2676#&!{; 11 TR{< 11 Te5/'%AmmA%'/5/'%AmAmA%'/-J{F9+)#.I{E9+)#L=s>>s;FD=s>>s9LD '@$M =M >   +"32% 76$3 ϓ㮚s[3-Jӿh% lz1-K[K(PX@!ZL = K>@ZU K>Y@ GFC7C +4.'&4732$2327&#"&47>54&'#&#"&47>5314?!'B314413323L^^L313323/=L#  2  1  #K>=L#  2 5  #K>5d:1;6?@< <UM =K>10'&# 64 +2"'&53265!"&#"&47>54.'&472$sw+)+<}fN-313323314)4;C^pb;+Im=L# 65  #K>5=L#  2 !1.Z@&<:K(PX@M =K>@UK>Y@  ., +272'&'&#!%2>76%!&4767 &'&473;50%3.1c`3:R333*/ ='')) F%D;%G6 f5 #|Rg/ 2 +902KPX@ '<KPX@ '<KPX@ '<@ 'KPX@# M =M =K>KPX@& M =M =K>K%PX@# M =M =K>@! UM =K>YYYY@ .+15C +%&#"&47>54&+""'6743) 72#.+"=F͍F+6dj}7)  9v-5*{;65 ;{de`w { s^fi-<EK(PX@K =K>@UK>Y@ CMC+%&#"&47>54&'.'&4732776&'&47327ExF0KE {Z R%9H ?D{ JG!#{965 9{70/R0 2  1 59-'-3 2  1 #8J:qL-@If@ IA-@UUL>Y@ CC +.54>7.'.7327&#".7>%>54&'ozwO;zܑ2101/3}Vd\R301103 \y4'7B?Xpk6I}T7A  2  1 A6S9>d9B  2  1  B{Nq`> -SKK(PX@K =K>@UK>Y@ CCCK +%.&#"&47>76&'.'.73277676'&47327&#".7>yQ1 Vh|Og'Q9OB  {‰R$_n- JV{ L_(!=5ZG { PB# ϙZ 2  1 '3!!+O4 2  1 59 m\ 2  1 (3!,1-N4 2  1 9--We@ WC-@bUK>Y@TQ>=:632<C+4.'&47327>546;2&#"&47>=.54.'&54;2314313C]+Ri/:[~zB313323VR+)/=L#  2  1  #K>rD -aIiuW. =L#  2  1  #K>AqwLl5%!U?D5C@@1+<bM =M>32/-$" 55 +%3'&54>3 32672!"7>54&#"#!&'63`\)h F^!/- L-%TfGV %L -0!]l?fɠe>lZhd!'NƴHk\!dhZ1m;[K(PX@!UK = K>@UU K>Y@ 76CC +462"%462"&4.'&47327&#"&47>5A^BB^BA^BB^A314313313323^BB/-Bo/BB^@B`=L#  2  1  #K>=L# 65  #K><DL_K(PX@"  UK =K>@  UUK>Y@LKHGCMC +%&#"&47>54&'.'&4732776&'&47327462"$462"ExF0KE {Z R%9H ?D{ JG!#vBbBBb=Db@@b{965 9{70/R0 2  1 59-'-3 2  1 #8J:qbDDbBBbDDbBJ!u1BK*PX@2% <@2% K*PX@. ddM =N =N >@2 dd=M =N =N >YY@@>75)'#!11  +2#"&54762327327#"&'#"'&54>'.#"3267%l 0g B'N';cIU* ^w;MXqtAi1&wG']+#cG9[;u1  %# @I5!YQkCVodoM#/FD;S\Z;qBpK*PX@ "  <@ "  KPX@5bb   U =M=M >K*PX@2 ddb   UM=M >K1PX@7 ddb  I  UM=M >@8 ddb  U  UM=M >YYYY@A?><:87520.,)'  +2#"&5476327#"&54>7.54632#"'&#"32632#"&#"%l  /f eZ>w#ˬ'O8RJaT&+0o?4TfhR?Iq2  %#TVd1`#A18!XEqVCREP\\ /7 \?bs8i@ 6#<-,9KPX@b=M= >@ddM= >Y@53'%   +2#"&5476#"&554&'&7672>76'&4&#"%l  /f +AXG/V ' CA-'1@oxs1  %#94*V1',#1K5kyAd^##fiV Gw+i@ @ddb= >Y@('  +2#"&5476327#"'&54&'.7$72%l  /f p3'F%%!RfD=/VF&/w1  %#HA-E95}V- 2 +#se7'F!SL@I5< dbU =N >QOB@97&$   +2#"&5476462"%462"&#"&'&547>54'&'&'>32327654'&'&'&54632%m  /f A^BB^BA^BB^A{}B l'3qVbNL#<7!8-9PF2  %#_AA0-An/BA_?A)XfB% (?\uwxw473EcJ!!2K*PX@"<@"@'=M=N =M >Y@0.'% !! +2327327#"&'#"'&54>'.#"32677'N';cIU* ^w;MXqtAi1&wG']+#cG9[; @I5!YQkCVodoM#/FD;S\#79}K PX@+7<989KPX@+7<989KPX@+7<989KPX@+7<989KPX@+7<989KPX@+7<989KPX@+7<989@+7<989YYYYYYYK PX@!M=M=M >KPX@+M=M=M=M >KPX@)M=M=M=M >KPX@!M=M=M >KPX@+M=M=M=M >KPX@&IM=M=M >KPX@!M=M=M >KPX@&IM=M=M >@'UM=M=M >YYYYYYYY@ -'%!#!"%+6732#"#"5463232654.#47>32#"&'-\^#mE#T1#I &#@/X]4Xȕp0IjD3ݲZ?A!qIK|[%N,0j0 =QZG<^%}..@+!<bM=>/#()+#"'4?&'&#"&'>32654&'.5432}Ѷ %R7-ub)/r#+oN^)i?Z5{F`};#SiC FQ\&67xuG1 P#\]N +.@+ <bM=M >("$*&+'324&/.54632#"'&'"#"$5m.)hJQmL-^05J9A'fXtXmCqs=+'\f@{8Z;2K*PX@ <@ K*PX@'bUM=M >K1PX@,bIUM=M >@-bUUM=M >YYY@ !"!#"#-$" +%327#"&54>7.54632#"'&#"32632#"&#"dZ>w#ˬ'O8RJaT&+0o?4TfhR?ITVd1`#A18!XEqVCREP\\ /7 \B59@ 8@0b V= =N=N>Y@530.*(%# 99 +"&'>7332767232#"&54323254&# ^\z/^/\+^sb}3 1n]ntl-5JL3Re, Rs ]Z }ܶuIBGL!qUEa@ =M?b(&@#&<9M= >,%"+%#"&554&'&7672>76'&4&#"+AXG/V ' CA-'1@oxY94*V1',#1K5kyAd^##fiV P=s XKPX@SM=M >@USM >Y@   " +!32"!&32#".P{^\w`n)XZ?fE\X}XNPwLᾘj+OG%@"<b= >%" +327#"'&54&'.7$723'F%%!RfD=/VF&/HA-E95}V- 2 +#se?Cm3@'bUM=M >Y@ %$$$" +%#"&554&'&76722?>32#"'.#"27'"&/&#+AXG/V ' 91NjC?v4"4+(:1F++A+#Y|`o6'AY94*V1',#N[>``5;$H&"J?L+4(r?VI1lVG(R%KPX@$ < 9KPX@$ < 9KPX@$ < 9@$ < 9YYYKPX@M=M >KPX@M==M >KPX@M=M >@M==M >YYY(%("+%#" ''.#"&'>32327Ri5T'G;@&\6!n7/F;=9>3L3ZWT ;d}/ 40?c 6fF)d@J%s3K#PX@0<.)(':@0<.)(':YK#PX@bM =>@$bM =M =>Y@ 31-+%#(# +%#"'#"&547654&5.5'%3267%327'h/.7=!L5)mYD! B1%%ycT #j)=FR;3_/H}B1+ns%#%@"b`= >+"+./.76$722767>54&54632'%%L-%#kOPXE-Bj+ 3 /Z 6Hr17E-H5SC@K%PX@>Z bVV= =M = N  >K,PX@AbZ bVV=M = N  >@Bbb bVV=M = N  >YYY@SQMKHFB@:86431"$,! +4# &5467.5467#"&'>7332763232632#"32#"&54323bPP+#\{/^/2%".I9 ' 4+-^5''Zntl-5}ǔR-eAQEe, Rs" EO?5/.,089@VT^TqUEa@ L '@$M=M >   +"32654&4762'"$yqDfp 7q́}u'}04@1<M=M >0/+"&$"++327#"&54##"&5467>7#"&'>3}dX{1%bV#N`F}#U)$%7!CLN>}J=D?-bZvTZ+= 9Wo/5 sD+ #-@*< 9M=M >$,$"+4&#"326'64>762#"' jz6`d^$-37)y^R?s{Z=uCoJ%m+y-%`0KPX@"ZXM=N>KPX@#Z`M=N>@$b`M=N>YY$".#""+#"'&#"32>54'&'.5463232654&#"TG/?-ktlk[7V9˭rsu?32)Gq(VV\[@aFT/JniAV9(5bHy@3m-5^r/:+h} 5@2<M=M >   +!#"$5476$3!26&'"{Յ7 !"9V`+C)Dž7X A-;7uy!7@4 <bM=M >&$#$ +#""'>3!+327#"&54weN?% )yX#Z71%bV#N`F BF}wLJ=D?-bZv7'1#@ <=N >/- " +#"&'&547>54'&'&'>32327654'&'&'&54632'{}B l'3qVbNL#<7!8-9P)XfB% (?\uwxw473EcL*R@<:*) 9KPX@ M>KPX@ M>@ M>YY!! +">'$4>7467632XqX+ Tk9;%-A!sv+@;HmFmL#3/RsDA/^Xs@;hkK%Z)f+q@+%<'9K!PX@bM=N>@#bM==N>Y@ $%%$#+&'&'"&'632373267#"&//7oO%F-`[8+)Е†7C.G ;D7u7/`5)-3 VW!3;'-!q=/5H@ , @O= >Y*-+47>54'&'&5>32673>54'&54632!'.o  m'3q}s #)91N/jH DGfRB% (?\\RKk%b^m\X!;n%=N`1T-*@' <( :dN >$%,""+#"'#"&547327&5463272'67ͲVZ)FdDm=)99u1Bl''ZiN󁁴Ϙ;d-#sw5Hqo` #.`j-3@0<bU= >%$+462"%462"&327#"'&54&'.7$72B^BB^AB^BB^B3'F%%!RfD=/VF&/b_AA0-An/BA_?AHA-E95}V- 2 +#se7'C1@.%<U=N >A?20)'$+462"%462"&#"&'&547>54'&'&'>32327654'&'&'&54632B^AA^AB^AA^B%{}B l'3qVbNL#<7!8-9Pb_AA0-An/BA_?A)XfB% (?\uwxw473EcLu(iKPX@$b=M=M >@!ddM=M >Y@'%   +2#"&5476"32654&4762'"$5%m 0f yqDfp u1  %#q́}u7'uAb#@dd=N >Y@?=0.'%  +2#"&5476#"&'&547>54'&'&'>32327654'&'&'&54632o%l  /f {}B l'3qVbNL#<7!8-9Pu1  %#)XfB% (?\uwxw473EcTy=q@80'@dddN >Y@31-+&$  +2#"&5476#"'#"&547327&5463272'67%l  /f ͲVZ)FdDm=)99u1Bl''ZiNy1  %#/󁁴Ϙ;d-#sw5Hqo` #.`jJ .е!K PX@(UM=M=M >K PX@(UM=M=M >KPX@(UM=M=M >KPX@(UM=M=M >KPX@(UM=M=M >KPX@(UM=M=M >K PX@(UM=M=M >@&UUM=M >YYYYYYYY@*($"   +">54&26&#">32632#"5^X1)ih`fy Ji{ĴlHvg7 f/-@BFo\sN}u۾[99s 8@653 @9  b   ```UUM >Y@ 8 8#(!+" +3.#"4'#"&54&#'7#63232$46325;P=W4t7-d X+w\'p:ĆmP-Fu1`3jumsE<B)$P38ud1D.g@@ bM =K>Y@ $#%%C+%&#"&47>5#"&'>32>32"&'.#"E ͍ Fu^bY%Rn/Vt:RH3B~}; 4  3 ;};F >/:OD'<+!'>@(&-@' db M =K>Y@<:651/,*%#  +2#"&5476&#"&47>5#"&'>32>32"&'.#"b%m /f E  ͍ Fu^dV%Rn/Vt:RH3B~1  %#3}; 4  3 ;};F >/:OD'<+!'.6>@@*b    UM =K>Y@>=:965$#%%C +%&#"&47>5#"&'>32>32"&'.#"462"$462"E ͍ Fu^bY%Rn/Vt:RH3B~hBbAAb=Db??b}; 4  3 ;};F >/:OD'<+!'+bDDbBBbDDbB9=- ,_@+& K,PX@ =M>@dM>YY*) +67%676'&&54767673#&+s-1?K@H90 <:64/-$%"" +#"'#"&5467#"#>3!67!67!#%!327&54632724ͲVZpdz)?) )5ZF  -UpndDm=)99u1Bl󁁴b]7-#TV^'  ժsw5Hqo`yG@ FE:&% 621KPX@/b`bRM= >@/b`bRM= >YY@ A?!$'((! +4#"'3&/0'63267676327727#"432#6767&47#"54676D%1a#" }'H%%7Lbsd/3+ H NZ0^\##  Th+Z`P^LÑ}PB 9'BhR+RN]'S@LD 08@5&<bPM >/-!    +"32&#"&47>=&576$3 ϓ㮚323314զ[3-Jӿ'F=L# 66 #L=H.U lzL ([&@bPM>Y@    +"32654&4762&#".7>7.yqDfp Rz>^  sN  A27q́}IwH55Jr"`? :KPX@$ Z  URM >K!PX@* ZU  UINBK%PX@+  bU  UINBK,PX@2b  bU  UINB@7b  bIU  UINBYYYY@ :8!#'(!6% +4>3227#"'&#"#"&543232654.'.'&Aah=F 1>R7cMuJ#mI-1\7kT?ٜ{9N}#NA5^7yJ8K1PX@ 2<1:@ 2;1:YK,PX@!ZRM= >K1PX@"bRM= >@*bRM=M= >YY@640.,*!88 +"#"546323254'&'".'&5467632327#"'&{kC)+{1D3+X?#\FZ-qXcyC-)3P\q-!  FuV&=/-mCT55PA#T-/@- K*PX@6 ZbbU L  =K>@4 Zbb   UUK>YY@/.5333#!" +#.#!!2#54&#!;2&#'54;2654&+"=77Q%yTf ;=^;> >;+mZG ׅ'1' ! 01^I2?K*PX@ <2:@ <2:YKPX@UM=>KPX@U=M=>KPX@UM=>KPX@eUM>K#PX@eU=M>K*PX@ ZeUM>@&ZZeUM>YYYYYY@ "("$63++&6;27#"'&#"&'&54>32327qlgBZ/7J)6V~^ J)%11%5)+'Pd/ZV'/' Z\/>vRL#A$VkbH/r3?()XTRKT+!:K@H ()<b`USIMA'&#'+654&5'&"'2>7#67672!327"'&'&547!71'h )/;@BZ5?% '/%b +0B6:#X9F/? !B  913=an=1!9 /4/f:lTb+^ @K(PX@eT >@deGL@Y+ '!7!S/?yHlZ3,;@8<+9b`cM>,,(+01764'#476767&'&'#.'!2&H +\q>'hBZРbWJ%#bmf`J+ZL?s#zmNi7!\%E@ !  @eIMAY%$" +76320#654'&''%0'&''%&'&'H')Ǯhw Rw! !\# ')ZB&VCV'ut?Hf6l%1 !m5Z -J8=r@;76,$  =<<&;K1PX@bUN >@!bUN = >Y@ (%'(*!+4#"/0'76326767632732?#"47#"547625+/V#%&w)6!/V#%'u%3-1uV##'H\/BNJVF{uV##'DO+?HLVHq7 !@ <9KPX@QM=M >KPX@QM=M >@QM=M >YY@    +%2654&#"#"'3 &# 32RskLxՖl'y+;ÞsN)q7+X<@9 <bM=M >  +"32672'"&54>32.=Zl\`q5'ydRh$3~77;%ʼnf-?1N%D'3/@,b`M ==>$('$)+4&'&7$72#"&5463232654&54632#"&/V F&/TRDl9wJ! %+5KD;JR?;HV1'+#sfPA9:L=)R  %\8F8)3LT4-RSLD0@-SM =M >$&# +% 76$3  #"!32s[ٟ* ߥӿh% lz]*;X@ KPX@%bSM=M >@%bSM=M >YY@ &#"!+#&#"!!327#"54>32@\f\B; GlbDh]FpBD7hH ym9}?^@ KPX@%bSM=M >@%bSM=M >YY@ &#"!+?726767!5!.#"'62#"'=^S=L)FD}g^Fm^FjB)Fh{Fwl8{m9 %u@ <: 9KPX@M=M >KPX@M=M >@M=M >YY$/#!+"326'6?632#"'12hH$-'/d}VLmdZ>uCdd@}c/ 1 hBF!&@#!<M =M >'(%#+#"$5!2.#"3267?Օp XeK!{w/X`H/;q5+'6;'b9TZo79@  : 9># +%#"''7?V8/n3aM:%%7 "!)'97F!'@$ <M =M >)%$#+'6$3 !"&'732>54.#"w  sޮBR=hL#0G`Y/FzN; b7{vuT^o^T+@=BF ->@;- <UM =M >+)"    +"&54632#"$5!2.#"3267>QU:7XS?Օp XeK!{w/X`H/;T8:WU<8Tq5+'6;'b9TZ7F -?@< <UM =M >)'   +"&54632'6$3 !"&'732>54.#">QT;7WRw  sޮBR=hL#0G`Y/FzN; T8:WV;8Tb7{vuT^o^T+@= DQ@:  <6;GE :K(PX@< ZZ U = N  =M=L>@: ZZ   V U =M=L>Y@LJA>5430-,'&# DD +26762"'.+;26&#!&47>54&'&47!272'.+"32654'&jM FF Pg2"J5' ces@@s<\5/ %3%bk#1Z+'!&\1R {6% H;+)9 u-5 ;y3{; 2 q#wd='}+Fw P DLT@:  <6;K(PX@A ZZ    UU N  =M=L>@? ZZ    U   UUM=L>Y@$TSPOLKHGA>5430-,'&# DD +26762"'.+;26&#!&47>54&'&47!272'.+"462"$462"jM FF Pg2"J5' ces@@s<\5/ %3%bk#1BbBBb=Db@@b1R {6% H;+)9 u-5 ;y3{; 2 q#wd='}bDDbBBbDDbBH9QKPX@'Q1<KPX@'Q1<KPX@'Q1<@'Q1KPX@< b Q M =M = M =K>KPX@? b Q M =M = M =K>K%PX@< b Q M =M = M =K>@: b U QM = M =K>YYYY@PNIGEC?=53.+15C+%&#"&47>54&+""'6743) 72#.+">32#"&5463224.#"=F͍F+6dj}7)  9v-5*]mRyR'9P+)!-f2Z9{;65 ;{de`w { s^fiXFR,9'#;=frR,9s@ (@!dZUK>Y@42&" ,, +#"".#.7>54.'.73!7'.#"&5476{H314BL%313313O!%3#X-%#]Ӣ=L# 6 3  #K>5=L#  2 })Hw PPD7a@^3<bbUU  M =M >/-'&! 77 +"63232?2#"&#"3267632 4>$32'.fL%oB@bM =M >Y-&,(+'.#"'"&#&5632654'.'&547672^#D#=_=P^RYj}%'F!j^w+D{q }=V`2cVN)``u}p( ^L\^:Zjb61-)AK(PX@K =K>@UK>YCC+4.'&47327&#"&47>5314313313323/=L#  2  1  #K>=L# 65  #K>1)19[K(PX@! UK =K>@ UUK>Y@ 98CC +4.'&47327&#"&47>5462"$462"314313313323AcAAc>Cc??c/=L#  2  1  #K>=L# 65  #K>bDDbBBbDDbBy-(uKPX@ZK =M >K(PX@bK =M >@bUM >YYC#$(+#"&5463232>54&'.7327%H7-9l9V!Z9#'E ͇b<-hn>%1Q@)J]:°{; 2  1 =>/ YK#PX@8ZZ  UL =  M =M >K*PX@9Zb  UL =  M =M >@7Zb U  U  M =M >YY@VURKDB=<94/.'%#!  +%2>5+%4&+"#"&546323267674.'&54?)2732"&#".7>h5/+@?$$%\9e=2="!?.5JZ9313ޏf7 ^313F3fN@1\qCs/+PuꡙPS}MC2?(#}k 3  #I>3DrN> 3  %J1- gK(PX@)  U  K  =M>@'    U  UM>Y@* \ZUTQMJIDC>=:632)(%! gc   +%2>5+"&47>5!&#"&47>54.'&47327!4.'&4732732"&j6 /314313323314313{413323ݏg7 ^F3fN@1\qCF5  #K>\=L# 65  #K>5=L#  2  1  #K>/=L#  2  1  #I>3DrN>9MKPX@'1 M<KPX@'1 M<KPX@'1 M<@'1 MKPX@0 M =M = M = K >KPX@3 M =M = M = K >K%PX@0 M =M = M = K >@. UM = M = K >YYYY@KIEDA=:953.+15C+%&#"&47>54&+""'6743) 72#.+"6$3 &#".7>54#"=@sdF+6dh}7)  9v-5*R>?;b  rN  D0-8{965 ;{dgbw { s`hi3b}C65 GyV+%1Xe@/C0<[Y :K(PX@5  dZ`K =M = N >K1PX@3  dZ`UM = N >@7  dZ`UM =  L =>YY@`^TSPLIH=;!$'C +4.'&47327>7>7>32#"&#"#"'.''&#"&47>5#"&5476314313p7M2RFf;/-+B'(=.6W2(ctHwXA\{Z{313323%X-%"\/=L#  2  1  #K>Ep"WsB%7f1//4+? d94 2 ( =L# 65  #K>)Hw P1RS`q@ @@ U = K >Y@[YONKGDCCCC +4.'&47327.'&47327&#"&47>5&#"&47>532654'&314313p1-,323314313 HR323Z+'!&\/=L#  2  1  #K>#(1 2  1  #K>=L#  2 5  #K>D'65  #K>+Fw P?9U KPX@, XZVK =N >KPX@+ dZVK =N >KPX@( dZVRK >K(PX@) dbVRK >@/ dbVUINBYYYY@ US'#)NK"$" + #"&546322>7.'.7327676.'&47327# 5463232654&54632jјX1/H #)')7\@51  zј ZP  -=2 yVj5=Du7'%-';9*3!\X{Z3/F-/ ;<-/  2  1 + 3%+  2  1 @^R!'# !%1F-Pt9K(PX@# Z K =M >@! Z UM >Y@KJA@=965.+$#  PN +"&'&#"&47>54.'&473273!2654.'.7327&#38 =Z^f323314313L` `L3133133135  #K>5=L#  2  1  #K>f<;g5=L#  2  1  #K>=L#  2 D?>@;:S UK>=;4320-)%$ +!#&#"&47>7272&#"&472>&'!"/71x) 5[l)65  N1->:@+ZZ UUM>Y@8410'&#>>  +%2>54.+#"32"&#&47>54.'&473!7'.j5 0\A/Hݏf7 ^323314P%3#V/^J@/?b<-dqC}3DrN>5  #K>5=L#  2 }-;K(PX@-ZZUN  =M>@+ZZ UUM>YY@ 76-,)$;:  %"! +3254#"32>54&#263 !"&#&47>54.'&47r\31V`>7qTD#313313'1)9a^5}N%+ 3  #K>5=L#  2 R-,a(@ZUK>Y@&" ,, +#"".#.7>54.'.73!7'.{H314BL%313313O!%3#Ӣ=L# 6 3  #K>5=L#  2 }--?q@ <9K(PX@ ZL =M>@ZUM>Y@0.;8.?0?(' -+ +2732'.#!"'6767>754&'&473#"3!274&{ 313V7 )-pq-) ;*&8/F>T >5J748x^) 1  #K>ˤ)yyD! .;h- 2 ZPb#g11D@:  <6:K(PX@7 ZZ U N  =M=L>@5 ZZ   U UM=L>Y@A>5430-,'&# DD +26762"'.+;26&#!.7>54&'.7!272'.+"jLF  FPf1#J5' cd  s?@r  ;\50 %3%bj#11R {6% H;+)9 u-5 ;y3{; 2 q#wd='}1L@ }3 ~2 K1PX@5 Z   U  U M =M>@9 Z   U  U M =K=>YY@vtrpljdc^]ZVSRMLFD@><:%C!+#"'.''&#"&47>5#"'&476767675.'&'&#"'"&5463234.'&47327267>7632#"'&#")Hb\>\{Z|314314}Z{\=\bHsd)1X5-52'-+/;?DpK7q413323p8JqD?</+-&16-5Y1)ds *=L#  2 5  #K>֬ 1 59b @-3<-f7)>!qE^=L#  2  1  #K>Fp!>)7f-;3-@ c95 2=HD7H@E2<bUM =M >.,&#! 77 +2#"'&'46323265%'"7437>54&#"'47>=`Lgo݋X0"j#pZw! aVl-eVw!D"D{!^h`1lDV)7\hL7+-/VH5hzy}!1R-S_@@U K >Y@ONKGDCCCC +4.'&47327.'&47327&#"&47>5&#"&47>5314313p1-,323314313 HR323/=L#  2  1  #K>#(1 2  1  #K>=L#  2 5  #K>D'65  #K>1RSo@K(PX@+  d  VK = K >@)  d  VU K >YY@omfd][XVONKGDCCCC+4.'&47327.'&47327&#"&47>5&#"&47>5# 5463232654&54632314313p1-,323314313 HR323Yu7'%-';9*4 ]/=L#  2  1  #K>#(1 2  1  #K>=L#  2 5  #K>D'65  #K>A^R!'# !%1LX@ N O K1PX@.  Z ` U M =N>@2  Z ` U M =L=>YY@GEDB'CC! +#"'.''&#"&47>54.'&47327>7>7>32#"&#"HwXA\{Z{313323314313p7M2RFf;/-+B'(=.6W2(ct ( =L# 65  #K>5=L#  2  1  #K>Ep"WsB%7f1//4+? d94 2/IK!PX@0ZZ L = K=M >K*PX@1Zb L = K=M >@/Zb U K=M >YY@A@=965,+(# IH +"#"&546323267654.'&54?)27&#"&47>54&#?$%%\9e>3=!!?-5JZ+314413323;<PuPT|MD1@)#}k  1  #K>=L#  2 5  #K>5`:  -D@ ,-  K!PX@0b`  `K =  K  >K(PX@4b`  `K =  K = >@2b`  `U  K = >YYY@@>;743G" +%#'"'#&#"&47>76&'.73%&#"&47>&M44  m61^{n5|T< R )l  n  e.>)'( X ) * X`R65 NaLq, 2 s 1 5V01065 ,01Z-WkK(PX@%  S K  = K>@#   U  S K>Y@WVQPMIFE<;CCC+4.'&47327&#"&47>5!&#"&47>54.'&47327!413323323314313323314313{/=L#  2  1  #K>=L#  2 5  #K>m=L# 65  #K>5=L#  2  1  #K>LD '@$M =M >   +"32% 76$3 ϓ㮚s[3-Jӿh% lz1-K[K(PX@!ZL = K>@ZU K>Y@ GFC7C +4.'&4732$2327&#"&47>54&'#&#"&47>5314?!'B314413323L^^L313323/=L#  2  1  #K>=L#  2 5  #K>5d:1;6?@< <UM =K>10'&# 64 +2"'&53265!"&#"&47>54.'&472$sw+)+<}fN-313323314)4;C^pb;+Im=L# 65  #K>5=L#  2 L5D-@* <bM =M >$(&+ '&5!2'.#"3 72bw;# -+/+T+Fy[f+902KPX@ '<KPX@ '<KPX@ '<@ 'KPX@# M =M =K>KPX@& M =M =K>K%PX@# M =M =K>@! UM =K>YYYY@ .+15C +%&#"&47>54&+""'6743) 72#.+"=F͍F+6dj}7)  9v-5*{;65 ;{de`w { s^fi?-9KPX@ZK =N >KPX@ZRK >K(PX@bRK >@!bUINBYYYNK"$"+ #"&546322>7.'.7327676.'&47327jјX1/H #)')7\@51  zј ZP  -=2 yVj5=DX{Z3/F-/ ;<-/  2  1 + 3%+  2  1 @5-Fu@ +@U UL>Y@FF@?;:730/%$  +>54&'.'&47327&#"&47>7.54$w\w5ᅔ301102"Zp311112x5+Hm`H+7A  2  1 A6$oe7 9B  2  1  C8 LzU -SKK(PX@K =K>@UK>Y@ CCCK +%.&#"&47>76&'.'.73277676'&47327&#".7>yQ1 Vh|Og'Q9OB  {‰R$_n- JV{ L_(!=5ZG { PB# ϙZ 2  1 '3!!+O4 2  1 59 m\ 2  1 (3!,1-N4 2  1 91-Kv@ F@ ZU M >Y@EC=<9521*'  KK +!#"$#"&47>54.'&473273>54.'&4732732'.?A323314313L^^L323314V9 '/5  #K>5=L#  2  1  #K>d:ˤ)y!-Ik@ I * @   U  UK>Y@HF?>C&CC +4.'&47327&#"&47>5 54.'&47327327413323323314323323Cm/=L#  2  1  #K>=L#  2 5  #K>sX=L#  2  1  #K>Ӂ^\1 P-mzK(PX@& Z K  = K>@$ Z  U K>Y@" ih_^[WTSLIBA>:76/,%$!m l +!#"$#""$#"&47>54.'&47327;2654.'&47327;2654.'&47327& ?B?A323314313N\\N323314N\\N3133233145  #K>5=L#  2  1  #K>d@@d5=L#  2  1  #K>d@@d5=L#  2  1  #K>=L# 61 P-mw@ !<"9K(PX@%Z K  = M>@#Z  U M>Y@ihea^]VSLKC&C2+%;2654.'&4732732'.#"$#""$#"&47>54.'&47327;2654.'&47327PN\\N313323V9 '/n?B?A323314313N\\N323314d@@d5=L#  2  1  #K>ˤ)y5  #K>5=L#  2  1  #K>d@@d5=L#  2  1  #K>!- 9K(PX@,ZZUL =M>@*ZZUUM>YY@541,&$ # +32>54&#4+"'673!%32!"&#&47>51V`>PHz#3%!<<314sǀD"3231)9a^5} 1  #K>#+ 1  #K>u/)1[K%PX@&U K  = M >K,PX@0U K  =M = M >@.  UUM = M >YY@**XWTPMLCB?:53*1*0(CC+4.'.7327&#".7>53 !32!"&#&47>54.'&47327313313313313R1V-ݑrw'D#3133133231=L# 65  #K>=L#  43  #K>1)Z3¨ 3  #K>5=L#  2  1  #L-1K!PX@ UK =M>K(PX@&ZUK =M>@$ZUUM>YY@.-*&#" # +3 !32!"&#&47>54.'&473271V-ݑrw'D#3133133231)Z3¨ 3  #K>5=L#  2  1  #LPD4b@_<;b  bU UM = M >1/*($" 44 +%27#"&#""54632327.#"'676$3 !"'&54632%\L?;/((XF:!5HmvBd+-}݌X/#j#qB;-N#'P@{:[lDV)7\hL1D CK(PX@;S M = K =  K = M >@4GS M =  K = M >Y@?>;743.-+)%#!   +"3 4.'&473273! ! #&#"&47>5{1314313P!M3133239F-=L#  2  1  #K>eX"=L# 65  #K> ; CN@K=<UM  = K= M > :953/.)(%! CC#" +;4&#"23&#"&47>5"#"'&67267&54676-X-N5PV/))314413323sG}!BT/RXrѰmI5w 1  #K>=L#  2 5  #K>J% / ?H`T3CZ(4;@84)# <bbM=M >)$$$&'#+%# 546?4&#"#"54>3 327'327] 7)RLP J5jO^ 1%yk3A9>/\c+Kov;+w.D &9h?i4L=#s"*5)#5FJV -ɵ+K PX@#U=M=M >KPX@%=M =M=M >@#U=M=M >YYY@ &$"! - -$" +32654&#"2#".547>32673#">`w`?ira;qTRd-PsG;#mq3!$'ȹ 8}BPh56L#=J?N-} >A@>3<UM=M > -'$#>;  && +32>54&#"32654&#"&47>54&'&4732632#"&i3?A[9'!?fOJbw b<;c s)ۭX])Fsw=B2)'LM/9#^\Z`J5 C{q{C6bw=]# mbFf8-)@@=%  )) +"&#"&47>54&'&47!272'.91)?R51+%;2654&+"6&'&73!67'.#!"'47>762?!"BN 2!+^W b;9R+--N77N--+95 Fs+99,y o?##6C{j:>BIT''TJA =#L %7@4 <bUM=M >%%%"$+!654&#"2#"'&5432!3jP3A%pmK!;{3'oj% 8N?fw?owG? Xk\@YOd hg][FD@?<;8521.-)'kk +(&47>=&#'&7>7>7675&'&&7>3254&'&47 76767632'&"&'&'._ D1)^`)gO=@+17Z1'FY3]:5d_Z9LX1D Z@Z D1XL9[^d5:\3ZF'1Z72+@=Pf)a^)1D 5 Ey61L/%3P+d<J +8dH;3)5HoyE66EyoH5)3;Hd7, I'% 33 +"&546322654+"&546;2>54#"'4763 -)R;kRyq  @3?}N5qhf\AS5vD+9T=7Qa)3))ml1Bn0Y9y-}AhKPX@#   Z K=L>@$   b K=L>Y@AA=54&#"&47>54&'&47327 ZNsb<@2 d  b U K=L>Y@ZXRQKIEDAA=54&#"&47>54&'&47327"&54632254&54632 ZNsb<@>$CC +&#"&47>54&'&473276767632'&"&'&'.1D ^w b<;c sZ D1XL9[^dj \31HN1Z72+@=Pg(a^ÿyE65 C{q{C66EyoH5\;H o+ IK*PX@1Z`K =L=N >@/Z`K =K=N >YY@871/-+'%  <: +67&#"&47>54.#"#"&54632326764&'&73^X b<K,PX@/  b  `K== K >@2  b  `bK= K >YY@9851.-('C +7676&'&7%&#".7>7###&#"&47> +^xX) ;`  v^  D.5 05^^`YN@bmA## !%3\b{C65 Ey+Ӱ/9&65 -}G=@:  S K = K>GFCB?;8721CCC+4&'&47327&#"&47>=!&#"&47>54&'&47327!11DZsb<   +"32654&4762'"$yqDfp 7q́}u-}=1@.ZK= K>76CCC +0#&#"&47>54&'&47!67&#"&47>54.h1)1D ^w b<;c #\Xb<@; 6+<bM=N =>$(H&"+4&'&76723>32#"'&#"&47>572654&#"0V - ;)f9BFC1b hm b2 ?>{tnH'f*yV1',#-95Tf9X"{7556{fDו+&L`5@2<b`M=M >#$%"+%#"'&5432"&'.#"327`;Zp ?cI83{V}VpjwN1A7);Vs5(7@4< :bM=K>C#7!+%#"'673!27'.+&#"&47>m[3%Zo#2bpwb11^ \{965 9 %j}E%@"K=M><8'&#!%# +#"'&74632326?>.'.'&47327476.'&47327)R+J1C7 *(/1 )5DRyV# ' A+f5X1H'%5#;/5q !% ^`166?>T! 66L1+|N! T@F I<T2@2  b =M = M  =L>Y@ SQMKEDCB;953/.+'$# +%267&#""32>54.&#"&47>="&54324&'&7672>32#"'-/'?HZ' )N^6581H+ #D %s5?{eK9UtL5A9>;^@3LhB{D55D{=1)f,$ ,L''f}O@K=>FB40CI +%&#"&476?6'.#&47327?6&'&47327&#"&47>/&w  RXR >3L@ RR ?[ n7R{ZR{H P RR 9c  65 N J?668-'66V u65 3-}B4@1<ZK=N>CCL +'.#!&47>54&'&473273>54&'&47327;:R-,/N8X b<;c w^ D1#5h5#1D^wb<j:<DEV'5 C{q{C66EyRJ  JRqyE66C{}>8@5# <  UK=K>=;I$CC +54&'&47327&#"&47>='"&=4&'&473273261DZsb<;cswb<7R^D1uFhyE66C{{C65 C{RyC66Ey11-?}VK@H Z K = K>QPKJGC@?8610-*'& VT +!"&47>54&'&473273>54&'&47 73>54&'.7327&#Zs b<;c w^ D1)1h1*1D^8^D1/Fi1)1C  ^w b;;b s5 C{q{C66EyNGGNqyE66Eym9GNqyE66C{{C6-?}[@@=<Z K = M>YXUQNMFD3CL +'.#!"&47>54&'&473273>54&'&47 73>54&'.73279R-+/N7ls b<;c w^ D1)1h1*1D^8^D1/Fi1)1C  ^w b;j:<DEV'5 C{q{C66EyNGGNqyE66Eym9GNqyE66C{3>@;<ZUM=M>D#A##! +4+34&#"'>7433!6732#%".7>5x)G)1LX%3% /6zX b;s  b;7!,`Nkp#q6C{Hq5 C{-}!*LC@@  U K = M>-+GEBA>:7610+L-K##CC+&#".7>54&'.73274+3"&47>54&'&4732732#;b sw  b;;b  rw b;y+F^s b<;c ws b<u{C65 C{q{C66C7!I5 C{q{C66C{Hq-}*7@4UK=M> %#  * )#! +4+3"&47>54&'&4732732#y+F^s b<;c ws b<7!I5 C{q{C66C{HqH3K#PX@0 )<@0 )@A  b ` ` ` ` ` M =N >Y@-+(&%#  33 +2#"&5463232>5"&#"#"5463232.#"'476-)R;59Q'T9/! ) sA/w%!ydN5pvD+9T=7-=mI>;H#92׾ml1-h @a@^   S  M = K=K= M >@@><7521.-*&#"    +"324&&#"&47>54&'&4732736762'"$'u1@!fNm@; UM= M><;850.+*'#  # +54&#"7'.54632327&#"&47>=#"&47>7>CNZ\ɥ(>sb<;cw^D1)-<-5P/=L/DϦ9VHEkw6C{{C65 EyuA8;1 ^s9,Lh4@/ddbVM=M >Y@ %%%"$%% +#"/7>32!654&#"2#"'&5432!3׍/R1]P3A%pmK!;{d!)-'oj% 8N?fw?owG?L7 K PX@/  bU  UM= M>KPX@/  bU  UM= M>KPX@/  bU  UM= M>KPX@/  bU  UM= M>KPX@/  bU  UM= M>@/  bU  UM= M>YYYYYY@750.%"$ +462"%462"&!654&#"2#"'&5432!3BA^BB^BA^BB^AP3A%pmK!;{f_AA/-Bo/AA_?B'oj$!8N?fw@ovH?%d8@5 < 9KPX@+ T = =M=L>@.  b T =M=L>Y@8843C-" +!632'>54&#"&#"&47>5#5354&'&7672s̾-5PP+eO}o;c sw b<)b !9PyR8)bӾyT{C65 C{PLf,$ ,Lq- 7@3@)ddZ N=K>Y@.-,)&% 77$! +632#"&547"&#"&47>54&'&47!272'.- N''' 1)@A  b ` ` ` ` ` M =N >Y@,*&$#! 33 +"32654&#"'"&'26323274&#"#">3274'&#T`-)P+Jmt T9/!)sA/w%#PF?#5p{j3tF+9TuҌ;H#92jbhVml1b)%@"<M=M >*&+$+632654'&'&'47632'&#"#"'&b-%H-Lib?Fhuq5Nr+8oO?݅+ o6/q.?kFTXHj1^{1%q.EYFj}-V5wD($@!M ==L>$&C+%&#"&47>54&'&7$724632#"&1cimb2/V F&/KD;JR?;H{765 5{V1'+#se33LT4-RSw.&@#U=L>C+462"%462"&&#"&47>54&'&7$72B^AA^AB^AA^B1cimb2/V F&/f_AA/-Bo/AA_?B{765 5{V1'+#seN%D'3/@,b`M ==>$('$)+4&'&7$72#"&5463232654&54632#"&/V F&/TRDl9wJ! %+5KD;JR?;HV1'+#sfPA9:L=)R  %\8F8)3LT4-RS }EKPX@: ZZ  UK=  N = N >KPX@; Zb  UK=  N = N >KPX@: ZZ  UK=  N = N >@; Zb  UK=  N = N >YYY@CB?;75R"$%%#! +4+34.#"#"&54632326764&'&73!6732#%"&47>5y)H $)0X0'=Eu(*R'\V}Xb:s b<7!,?B"VgZ0) KIFEB>;:7632/+('"! P O#! +4+3&47>=!&#"&47>54&'&47327!54&'&4732732#Xy+F^/D11D ^w b<;c sZ D1R1DZsb<7!I5 EyyE65 C{q{C66EyssyE66C{Hq%?@< @1 b   T =M=L>Y@??;:98210/C$C"+!63 &#"&47>54#"&#"&47>5#5354&'&7672 g@/d db =K=N >Y@NL86CC$! +632#"&547&#"&47>54&'&473276767632'&"&'&'.j N''' 1D ^w b<;c sZ D1XL9[^dj \31HN1Z72+@=Pg(a^10! yE65 C{q{C66EyoH5\;H o+ IKPX@-dd   Z K =L>@.dd   b K =L>YY@PPLKHDA@HCD%%+#"/7>3267327&#"&47>54&#"&47>54&'&47327N/S1R ZNsb<^\VUOMIH<8'&#!%# +#"'&74632326?>.'.'&47327476.'&47327"&5463254&54632)R+J1C7 *(/1 )5DRyV# ' A+f5X1dٚ4%#.5'/H'%5#;/5q !% ^`166?>T! 66L1+|bxu`#/+!^_,*-}@D@AO K = M >=<763/,+$# @@ +!"#&47>54&'&473273>54&'&47327-LJY b<;c w^ D1)1h1*1D^wb<K(PX@1  Z U   UK = M>@/  ZU U   U M>YY@">>>E>DCA874/*('% == +"'673!.'.7327!257.+32!"&#.7>53 !TALC- HT+-12-CKBTrw'B%3131V-D{ 1I) 2  1 /'1 {D3¨ 1  #K>1)Zu3<C@@/.<'&# :UK=M><:7531,+#d +32#"&#"&47>5#"'67!54&'7>7!.+4+3q99sb;wVG1;`hk`=VLS{9x+E-q5 C{\f@bUK>Y@TQ>=:632<C+4.'&47327>546;2&#"&47>=.54.'&54;2314313C]+Ri/:[~zB313323VR+)/=L#  2  1  #K>rD -aIiuW. =L#  2  1  #K>AqwLl5%!U=H5H@ , @O= >Y*-+47>54'&'&'>32673>54'&54632!'.  l'3q|s #)91M0jH DGfRB% (?\\RKk%b^m\X!;n%=N`1LD(>@;#<UUM =M >#!"!#$&#+% 76$3  #"6323#"&#"32s[ء ULo7RZ`qbHB< কӿh% lz $^:<^ "L ,F@C&<bbUM=N >!$%!%$&"+432#"%.#"632323245#"&#"LV\˅l dg4H#9H+m!%/Z>YB+ qmИﲯL3@ #0.K*PX@)ZbUN = >@/Z`bUN = >YY@ 334$"$! +32#"'&#""'3.+"=73%+"#>7yD]K)F !9ŮJH$=N=57 75 +/@ &!/KPX@*ZbK=N= >K.PX@*ZbK=N= >@(ZbUN= >YYY@ #35"$"+>32#"'&#"3'.+"=737+Bb5mD)9! D%%B#}C";% ^3-%+ ^^10oK5%" %TX3CT5@ #0.K*PX@8  bZbVN =  M >K,PX@>  bZ`bVN =  M >@D b  `Z`bVN = M >YYY@ONGE><65334$"$! +32#"'&#""'3.+"=73%+"#>7>#"/&54%632"/&'4yD]K)F !9ŮJH$=N=57 7A +)u 5 + %!  %DM[@ D? MKPX@6d Zb  K =N=M >K,PX@6d Zb  K =N=M >K.PX@:dd Zb  K =N=M >@8dd Zb   UN=M >YYYY@HFC@=:"$$%&%$ +2'4/&#"3254'&#">32#"'&#"3'.+"=737+!%/ y 1#3Bb5mD)9! D%%B#}C";% %3  -G3-%+ ^^10oK5%" %T+x@(@!dZUK>Y@&# +) +7!2673.#!";2&#'54;254&+"=72+3F 9FB7;H;5D'<;)^9R7:PP1!Z#h@#ZUSK>Y@ 332!"$+74&'#"'5!2673#&+;2&#'54;2;75>,+/=N>/+;7!3\s?RR-4}0@& Z  USK>Y@.*'&!  44 +#"3#".#.7>5#534.'.73!7'.{Hdd314BL%313313O!%3#ӢkP=L# 6 3  #K>RP=L#  2 }+1R@O-<) : ZS  N  =K>('&#  11 +"3#&#"&47>=#5354&'&47!272'.91)RR@A Zb` U  UUO K>Y@CB@=:741-*32#"+%#"'&"'3#372654.';2&#'54;2'4&+"=73!#!"σ`1FdjKjD5;H=6B%>;)5jˑ^+PC^-Bs5RR1#K.PX@6Z`bK= L=N>@4Z`bU L=N>YY@ <935"#&%S3 +74&+"=7263!.+#"&543232%;2&#'54;2 >74 5%deªys\)1=P;1);7!" LRP-bÇP^N\%R9BK#PX@3.nP zu<@3.nP zuK(PX@F b  b  `DK = M  = N>@D b  b  `UD M  = N>YY@%|yvspgedc`]SRMKIGDB332#".3+'54;2>7>?67.'&#"'"&543234+"=73%+"2767>32#"'&#"#;2#&#"'.'.';2&#'54;2';E3-/[Hs// 15,3TV|F!Rg7fA6=P#;5Do1qE HZT1/15 -fX3d %#3'#;Bw7W/D5=P=6BVdKTP+VhN5'f/X=-;RRo?'5b7f'5CV''@94H/^\PPv@PK 5 i1  (@A Z   bD  M =N =N>Y@,tspmedb`][XWUROLIFDC@>;976-*'$ vv +!".'.';2&#'54;25"'54;2>7&'&7"#"&543254+"'5737+">7632#"'&#;2#'&1R9 J7)=P>+9J!;P1NbF)ktHuJ' /5H^=iH+=H=)Hd=`H7!+)Hv3W##;*-V9-B=:RR<=B-7V-=H+H  3#XyNLLNyX!3H-rV##'3FB@,6 @7bSUM = M = M  >Y@B@;:$#16"* +72654&'.5463232654.+"6;264&#"#367672#"XVB-JB5/bPw3jHNMswdFo5 ZIFFPh#V>$^~:'-8+bjFVͅuT3 5f)d 7FT^1'+F>@ :@KPX@E  bb`U =M =M = N  >KPX@E  bb`U =M =M = N  >KPX@E  bb`U =M =M = N  >KPX@E  bb`U =M =M = N  >K#PX@E  bb`U =M =M = N  >@F  b  Xb`UM =M = N  >YYYYYY@FD420/$14")" +732654&'.54322654&+"4;2654&#"#2#27632#"XV!/FTob s5hE6rJJAL9T1#5# T .u1Ts_CRi#VP)yj /TAchH}Ic7JZ'N93C "mT3kzbTVbg9S@/*IK(PX@7  Z bDK = N = L>@5  Z b UD N = L>YY@FDB@=;7641.+(%"SS +%32#&'.'.'.';2&#'54;2'4+"=73%+"276762#"'&#"#;;1T)w7V/D5;P=6BA6=P#;5Do1qEDT1-51 -fZ/d %;NH}A/^\PPRRo?f'5CV''@je}K@ 9%E@ K.PX@9bbDM=M=  L  >@2bbIUD  L  >YY@JGDA>;8##332 +4&+"=737+">7632#"'&#;2#&'&'&'.';2&#'54;25;H>)Hf>^H5)%$LuDxo$!`|5AK J7)=P;))#LNyX!3J+ J9+ +B=:RR9]@%1. IHG R K(PX@<  Z b  SM = N =N>@:  Z b U  S N =N>YY@USPM?=;9640/-,3#33#6+%#&';2�'54;2'4+"=730%+"27367632#"'&#"7732'"./&5)9D5;P;8BA8;P!;5D%#9bT132+A\:T^+L=FjT6)  PPRRM/f'5!59HpR xe^3uha oUF@#@ / D UL KPX@<  b `  S M= M =N>K.PX@<  b `  S M= M =N>@:  b `  U  S M =N>YYY@OMJG=;75.-,+'%#333"+%#"&#72&#'54;254+"=7307#"35367>32#"'0';2'".'&'}9?P;'++';H=--/8K2NME23!%?o3s#D1P;%LKJP%#߲'l14J"7+#3[)-V9-`9Z@,'P7 ZK(PX@4  Z  SK = N =M>@2  Z U  S N =M>YY@XUHFDB?=654333233: +'".'&'&';2&#'54;2'#5354+"=73%+"3#6767>32#"'&#"732ѲFjT6)I#;gD5;P;8BA8;P!;5Dd0\`-T132+?Z:Bd-!L=3uha.M PPC^XRRX^7mvf'5!5;FpP TRH^J@5 J9$ A <:KPX@#S M= M >KPX@#S M= M >@#S M= M >YY@DB?<10'3#1 +%732�'54;25#53&'767!!67>32"'.';2'".'&'+=N;') />H2;hH7L/NPHb!%?m5j)B/P<DOLR-Vb?VRVTAfV;14J"7+#3])-V7/+9T"@,I3 TK#PX@9 Z X  bM = N = M>K(PX@7 Z X  bK = N = M>@5 Z X  bU N = M>YYY@RODB@>(3Q$33: +'".'&'&';2&#'54;2'4'#"#!33%+"6767>32#"'&#"732FjT5)I$;gB5=N>5B1)1y'?w/X#>5Bb0\`-T17-+ +)uK3N;O=3uha.M PP5 NTb`R7mvf'5oT\+R9:^ P+@!; P?(G KPX@, bK= M= M >KPX@, bK= M= M >K.PX@, bK= M= M >@* b U M= M >YYYY@JHEB75)3Q#3#1 +%732�'54;254'#3;7+">7>32#"'.';2'".'&'B+=P='+-1;y%I4'=+=N#7L1LPF13! Dj3j)D/P9FMLKJN^^!LaM14J"7+#3_)-V9--R@@:+%@-   U  SCM>Y@#RRPOLIEB?>=;85320-*)#33#23+%0'54;25!;2�'54;2'4+"=730%+"!4+"'5730%+";2#.>5BD5=N;6BA6=N#;5D>B5=O!<5D #;R RRRR_RR 3!VfyU@NI82 # @(    U  SCM>Y@SPMJGDA@=:736#C$3!2+%;2#&#'5473265!32%"#'5473>54&+"=7>3#"!54&+"=727#""V?7+ +7# 73 37%"7+E+7f73 5!/%87+  +875!. "75#%"9-]@1"DC XS @3  Z  `   U  SK>Y@]ZWTQNJIHEB<;2/,*)3#33#2+%!;2�'54;2'4+"=730%+"!4+"'573263723326363+"3;2&#'54;2D5=P=6BA6=P#;5D>D3=O!L ) H#g'9}B7=O>3D RRRR_R Ry`@\KE  60@*   I  U  S K>Y@$ZWTSPMJFC@:851.,('$! `] +72326363'#";2%"'5473265!32%"#'5473>54&+"=7>3#"!54&+"=726b+ K!f)9{ 37! 7+ )7# 73 37%$7+E+7tw47) %87+  +875!. "75#%w-n@b@;'" @E Zb`  UUUO K >Y@jigdaNLIEB?<962/,)32#""+%#"'4#"'3#37265$';2&#'54;254#!";2&#'54;2'4&+"=7332232636376237+σa1FdjA6=P=+BB D+=P=6B<=/^B^`/!)%#1}W!o=:=jɏ\+PC^-B RRR#1%RR/#-NsNz'yY@+RM83K.PX@; Z  bb L= L =N>@9 Z  bb  U L =N>YY@WTQNKHDA=:36"#&3S2+4&+"=73!$7#"#"&543232%;2%"'54732654+";2%"'5473265 ;9RM:= ddŨ{s\ =9h78 $'39j7=;%"9-bÇN^N\%5!%% 61HH5!%%"4!}H B@A4<9KPX@-bbQM =M>KPX@-bbQM =M>@-bbQM =M>YY@ :80.)'"  B B' +>54&#"2654+ $!2#"&'.#"&5432B^E[bX> d`>3;B;+ 9qX)k!ZJ!=DFG J%h#PJ5#5+1/1 ;yMV ! ;Z@)(0:KPX@*ZU UOM>KPX@*ZU UOM>KPX@+bU UOM>KPX@+bU UOM>@+bU UOM>YYYYY@ 54,*'%  ; ;' +>54&#"254&+ '.5432#"'&#"&546 s/Rd1D5;}s߁=ߠ^X71\)3Xo'LתRlkэ;\!j D!8=$ALbdZħ%'D.J@G&%<bbM =M =M>$'$&+72654&'3.5!23.#"3267#"XVA-RIj^J-+9TZLJ{w^Öh#V>$d'':!C菘r= yT^#15,@$#&KPX@/bbM=M =N>KPX@/bbM=M =N>@/bbM=M =N>YYY@ ($$$$"+732654'3.5432#"'.#"327#" RZ#!-`G4 +%RjdX{ZRk+ZN)  X-3N+53žݰD5Nbgf-c@&% <$#:KPX@CK =M>@UCM>Y@ 743"1+%;2#.#'54;254&+"'!&'7.+"#;X>5B=8FbF7>\wR)C{M)' {C -)/o@ +<&% :K#PX@%bCK=M>@#bUCM>Y@ %$"C2 +%;2#&'&"#5473>5#"#!&57#.5.+V #!5},G-98T n^ +d5%3 3"8tDF0  (J&-Ff@,& 5A<@U K>Y@ FC363$*34( +%&'&'&+&=73%+"67674+&=7307+"7;2&#'54;29:) ;:;>rk =5?+"D5=N;6AbbH!i"!%1BR-yEl@+% 3@: @U  M   >Y@EB?>=;53$)3$' +&'&+&=7307+"0654+&=7307+"7;2�'54;2{ ;;: @5@-'")'=O;'+'D  ! T+P-O@C=)$L3 @%  U SM>Y@OONMHEBA@>:834(3#2+;2�'54;2'!5!5&'&'&+&=73%+"67674+&=7307+"7!D5=N;6BV9:) ;:;>rk =5?+"7RR^:bbH!i"!%1P^-yL@0* GA @&U K = M   >Y@LIFEDB?<:9873$)34&+0'!5!&'&'&+&=737+"0654+&=7307+"!!;2�'54;2D5 ;;: @5@-'"D)'=O;'+'^)}D  ! T^PN/k@RK3B  @:  b`b   UCM>Y@!kkjifeb`ZYWTQLHG><C;34(3+%0'54;274'3.';&#'54;277654'.+"=73273+676'5#&=72327+"332#.53&'%91!--+)+3!B9Q` "D+fr~5RkC3so?;LgN ;9#+;=99=!!h+}%!%/)Ñ!!!f}#+fyh@OJ72Y@,+ (& @8  b  `  `   UCM>Y@hhedb_[ZTQNKHFFE33:33=#+!0'54;054'.';2&#'54;2?'07.'&+"75727+"7#6'43#"=737+"#;2#4'&;-F Q ;BE#E!!=0F h;ks;;F1'##? !X- H^n)\ 11Z##! %511\/RK#PX@/)K < :@/)K < :YK#PX@! OK = M  >K(PX@- OK =K = M  >@&G OK = M  >YY@OMJA43C5572 +4&+"'!&'7.+"3!2654&+"=7627#";2#.'"$#""'54;>5=8E'H7> ! <7)'!:; !;~Vdc##=5 +#C}N)'!}C!-#7%11%%%"4\u8!M@.@0   bGO   K =M>Y@ HGDC@?;830-*)(%" ML +"3!2654&+"=727#";2#&#!'54732654&+"#!'7#&'.#j +5h75 !!79m7! A `+-75')37!%"95%%6?5!?:/)J)-Fx@ 32@%  UUCM>Y@DA?;96%2B5$2"1 +%;2#.#5473265#"&54&+"=263#"3274&+"=267#"d#;|X8P Ӻ#9%%9#\^} <7%'7< Zw3$4qǞP/33-ubp/533%yK@D?)#76@%  UUCM>Y@IFC@=:%3C4$3#2 +%;2#&'&'547326=# 54&+"=7267#"32754&+"=727#" #!5},G-97# `7!":'9wZ !7h855%3 # :995!. "-ojl7!%"9-L@ LKI"!@(bU S  K  >Y@FCA=;82B72B5! +#5#"&54&+"=263#"3674&+"=267#";2%"#54732657ų#9%%9#vFp <7%'7< <7+)8P ^bǞP/33-ZI/5335#13$47!yO@0+ ONL#" D? @(bU S  K  >Y@IFC@=:3373C4! +%#5# 54&+"=7267#"36754&+"=727#";2&#'547326=7f7!":9"" !7h85 #5J7# '/q95!. "-*7!%"97#%# :H-D@ D' 82 @%U M= M >Y@B@=:76530-#3#33#2+;2�'54;2'4+"=730%+"63 ;2�'54;254#"D'=Q;6BA6=P#;5DV1)B!V;/+b PPRRyVRRPFdyD@& DC 72 @"U  U K >Y@B@<9630-#3#33#1 +%;2�'54;254+"=7307+"672;2&#'54;254&#"7) =O;)++);P< )+);P>&)CD=RRCPPsfRRoS5D1 @(U U M = M >Y@0.*)'%#"  +"!.#"$.547# 54632#"6$3 !326-]R䮨RŗV)@B1#9H'1qޛ5V}kwJ3b#17%^/y ) 3@ #  KPX@0b UM=M= M >K PX@0b UM=M= M >@.bU UM= M >YYY@ 31%&##&# +3!"54&#""&547&54632#">32#!32C L3)>3TP%>>/b+jN#R`wB{j +nSE,X!5./ FEwH+ X5DC@98  C @/U  U  Q M = M >Y@B@640/-+)(%#  +"!.#"&547#"$.547# 54632#"6$3 !3267327-FZh;RŗV)@B1#9H'1qwRhcRT+!%Xޛ ^Tb}wJ3b#17%^/yqVb:BZ%KPX@?b  b  U  QM=M= M >K PX@?b  b  U  QM=M= M >@=b  bU  U  QM= M >YYY@DB>=9742&##$&# +3!"54&#"#"&547#"&547&54632#">32#!3273327A L3'@1%XhuP%>>/b+jN#R`wT%N+ 'VB{j +n^ ^T^},X!5./ FEwH+ ԍE1<'@UK>YCC+4.'&47327&#"&47>5413323323314/=L#  2  1  #K>=L# 6 3  #K>1j@ }3 ~2 K(PX@Ed Z V  U  K = M =M>K1PX@Cd Z V   U  U M =M>@Gd Z V   U  U M =K=>YYY@'vtrpljdc^]ZVSRMLFD@><:%C!+#"'.''&#"&47>5#"'&476767675.'&'&#"'"&5463234.'&47327267>7632#"'&#"# 5463232654&54632)Hb\>\{Z|314314}Z{\=\bHsd)1X5-52'-+/;?DpK7q413323p8JqD?</+-&16-5Y1)dsJu8&%-'<9)3!\ *=L#  2 5  #K>֬ 1 59b @-3<-f7)>!qE^=L#  2  1  #K>Fp!>)7f-;3-@ c95 23^R!'# !%dkv@Od KPX@7b  U = =K= N >KPX@7  db  U =K= N >KPX@7b  U = =K= N >@7  db  U =K= N >YYYY@&|{usonhg][FD@?<;8521.-)'kk +(.7>=&#'&7>7>7675&'&&7>3254&'.7 76767632'&"&'&'."&54632254&54632^  D0)^`)fP=?+17[1'FZ3] 95d_Z9LX1C  Z?Z  D0XK9[^d5:\3ZF'1Z81+@=Pf)a])1C  {ٚ3%#/6'/5 Ey61L/%3P+d<J +8dH;3)5HoyE66EyoH5)3;Hd7, IK(PX@N Z  b `bVO  K = N =K>@L Z  b `b   UVO N =K>YY@YXPNLJGE=:741.332#"+%#"'&"'#372654.';2&#'54;2'4+"=73%+"6767>32#"'&#"2'х\1JboNmD5;P;8BA8;P!;5Dd0\`-T132+$?Z8!+PC^-Bo1PPRR7mvf'5!57JnPf'}OE@94 '"K*PX@>b  U M =  M =K=N>K.PX@E   bb  U M = M =K=N>@>   bb  I   U  UK=N>YYY@NMKIFDA@>;33332"#)+#"&543232654.'732&#'54;254+"=737+">7632#"'&#LXNŪxs]Zk?muC+=P;'++';H=+Hf=`E8!+)>hoP^Nw^-LKJPLNyX!3N-Q@71" FK(PX@<b  SUO M  =M>@:b   U  SUOM>YY@PNKJIHEDB@<96542/,*)3#33#2+%!;2�'54;2'4+"=730%+"!4+"'5730%+"#"'&##3326D5=N;6BA6=N#;5D>B5=O!<5Dυ\1Ffh RRRR_RR +NAZ1?qbyR@;6% @/   b  US QK>Y@QOLJGE@=:733C6#C$+%!32%"#'5473>54&+"=7>3#"!54&+"=727#"#"&543232+7# 73 37%"7+E+7f73{5H\ZZ7+  +877. "75#%"9χP`+%-N@71"I @,   U  SCM>Y@NKHGFDCBA?<96542/,*)3#33#2+%!;2�'54;2'4+"=730%+"!4+"'5730%+";#7�'54;2D5=N;6BA6=N#;5D>B5=O!<5DDq-P>5B RRRR_RR RyV@# OI8 @(U  SC  K   >Y@SQNJGEA@=:72%3333C2+4&+"=7>3#"!54&+"=727#";#7"'"'5473265!32%"#'5473>537%$7)E+7f73 pF"! 7+ )7# 715!. "75#%"97%87+  +8-Fx@ 32@%  UUCM>Y@DA?;96%2B5#3"2 +%;2'"#46;2'#"&54&+"=263#"3274&+"=267#"d P7V};!Ӹ#9%%9#\^} <7%'7< 3%3w9ZqǞP/33-ubp/533!yK@D?)#76@%  UUCM>Y@IFC@=:%3C4$3#2 +%;2'&#46;26=# 54&+"=7267#"32754&+"=727#" !9?}5!# `7!":'9wZ !7j859!#%5995!. "-ojl7!%"9?[ @-  d  VS UK>Y@ [YRPIGDB=;4320-)%$ +!#&#"&47>7272&#"&472>&'!"# 5463232654&54632/71x) 5[l)65  N}^R!'# !%Z'(4N@ 4)# @/b  U UM=N >Y@MKED$)$$$&'# +%# 546?4&#"#"54>3 327'327"&54632254&54632] 7)RLP J5jO^ 1%yk3A9>/\bڙ3%#/5'/c+Kov;+w.D &9h?i4L=#s"*5)#5FJ)bwu`#/+!^^-+}QN@K1<US   U K >OMFEDB?;760/'#+462"%462"&!#&#"&47>7272&#"&472>&'!"\B^AA^AB^BB^BP71x) 5[l)65  NZ:FK@HF;5<b  bUM=M  >EC:8$$&'% +462"%462"&# 546?4&#"#"54>3 327'3275B^BB^AB^BB^B 7)RLP J5jO^ 1%yk3A9>/\f_AA/-Bo/AA_?B+Kov;+w.D &9h?i4L=#s"*5)#5FJV1\c@# *' 7<:K(PX@DZU  SN =M= L  =  K   >@BZUU  SM= L  =  K   >Y@^][ZWSHGDC@;3##93#+76&#"=7!272'.+"326762"'.+;26&#!&47>5!&#".7>!4&;Lb5/ %3%bk#1mI E F Ni1#J5' ces@/!  x) JU8533q#wd='-R {6% H7/)9 u-5 ;y' 65 7 Ly V~@{I P6  +<b  b  U  M  = M =M >TQLJGEA?97/-!VV   +">74&67726%2672#"'.'#"'.54$%#"#"&5463263 #T)D%-N  =/iF9ӦQ ;9B*68=37w`%d^Le#y7  hmJ3B3 <;?9 LQ,`6;?7 7?+XCLC3D`$@:  <6;KPX@F X ZZ  VU N  =M=L>K(PX@E d ZZ  VU N  =M=L>@C d ZZ  V   UUM=L>YY@$`^WUNLIGA>5430-,'&# DD +26762"'.+;26&#!.7>54&'.7!272'.+"# 5463232654&54632jLF  FPf1#J5' cd  s?@r  ;\50 %3%bj#1Mu7'%-';9)4 \1R {6% H;+)9 u-5 ;y3{; 2 q#wd='}O^R!'# !%L' %? @3  db  UVM=M >Y@><65$%%%"$ +!654&#"2#"'&5432!3"&54632254&54632jP3A%pmK!;{6ٙ3%#/5'/3'oj% 8N?fw?owG?>bwu`#/+!^^-+RTD3@0<SM =M >$&$"+%32'>32! 765&# r7u=uNѨ9w~:N; %7@4 <bSM=M >%%%"$+!32676"5632#".547!4654&#&bP3A%qmL ;{D'ok%!8?fw@ovH?RT%-B@?< USM =M >-,$&$" +%32'>32! 765&# 462"$462"r7u=uNѨ AcAAc>Cc??c9w~:NRbDDbBBbDDbB; %.7G@D <b  USM=M >6521%%%"$ +!32676"5632#".547!4654&#&462"%462"&bP3A%qmL ;{HB^BB^AB^BB^BD'ok%!8?fw@ovH?_AA/-Bo/AA_?B1o @ E DK1PX@?  Z U U U M =M >@C  Z U U U M =K= >YY@%~|vupolhed_^XVRPNL9721C#+462"%462"&#"'.''&#"&47>5#"'&476767675.'&'&#"'"&5463234.'&47327267>7632#"'&#"A_AA_BB^AA^BHb\>\{Z|314314}Z{\=\bHsd)1X5-52'-+/;?DpK7q413323p8JqD?</+-&16-5Y1)ds^BB/-Bo/BB^@B- *=L#  2 5  #K>֬ 1 59b @-3<-f7)>!qE^=L#  2  1  #K>Fp!>)7f-;3-@ c95 2 Xkt}n@kOd |{xwtsonhg][FD@?<;8521.-)'kk +(&47>=&#'&7>7>7675&'&&7>3254&'&47 76767632'&"&'&'.462"%462"&_ D1)^`)gO=@+17Z1'FY3]:5d_Z9LX1D Z@Z D1XL9[^d5:\3ZF'1Z72+@=Pf)a^)1D A_AA_BB^AA^B5 Ey61L/%3P+d<J +8dH;3)5HoyE66EyoH5)3;Hd7, IHGDC@?;:.,&#! 77 +2#"'&'46323265%'"7437>54&#"'47>462"%462"&=`Lgo݋X0"j#pZw! aVl-eVw!D"A^BB^BA_AA_AD{!^h`1lDV)7\hL7+-/VH5hzy}!^BB/-An/BB^?AH13<EZ@W",+<b  UUM=M  >DC@?<;76'% 33 +"&546322654+"&546;2>54#"'4763 462"%462"&-)R;kRyq  @3?}N5qhf\AS5NA^BB^BA^BB^AvD+9T=7Qa)3))ml1Bn0Y9yv_AA0-An/BA_?AHVd/@ '&!KPX@%b`M =M>K(PX@"b`QM >@(b`UIMAYYY@$"  /. +2!".5463232654.#"!'3Bf)`AF{5-<{Aij3-[LZ r-" D+# N/-f@-@J?Uq^-^  +/w(y@%$<&:K.PX@)db`M=M>@'db`UM>Y@ "$"#) +#!2#"&54232654&#"#"'^fj&3JPg)F#7lDh#5(șb:F=X}NRy J1RS_y@@$   UU K >Y@VT\YT_V_ONKGDCCCC+4.'&47327.'&47327&#"&47>5&#"&47>5!"5463!2314313p1-,323314313 HR323>!5!/=L#  2  1  #K>#(1 2  1  #K>=L#  2 5  #K>D'65  #K>/%-"--AMKPX@,   Z  U K=L>@-   b  U K=L>Y@DBJGBMDMAA=54&#"&47>54&'&47327!"5463!2 ZNsb<@%   UU K >Y@cb_^[ZWVONKGDCCCC+4.'&47327.'&47327&#"&47>5&#"&47>5462"$462"314313p1-,323314313 HR323AbBBb>Cb@@b/=L#  2  1  #K>#(1 2  1  #K>=L#  2 5  #K>D'65  #K>bDDbBBbDDbB-SKPX@-ZU K = L >@.bU K = L >Y@SSONKGDC>=:6CE+462"%462"&67327&#"&47>54&#"&47>54&'&47327B^BB^AB^BB^B ZNsb<*)&%"!  +"32% 76$3 462"$462"ϓ㮚s[BbBBb=Db@@b3-Jӿh% lz'bDDbBBbDDbBL !*9@6UM=M >)(%$!   +"32654&4762'"$462"%462"&yqDfp )A^BB^BA_AA_A7q́}u_AA0-An/BA_?ALD(>@;#<UUM =M >#!"!#$&#+% 76$3  #"6323#"&#"32s[ء ULo7RZ`qbHB< কӿh% lz $^:<^ "L )I@F#<bbUM=N >!"&$!#%#+4762'"$%"&#"32654'.#"63232Lp gM+1jGDfxY<0+u!*́}uM@Tw\-L+8O@L%3, <U  U  UM = M >7520/-!##&# +% 76$3 462"$462" #"6323#"&#"32s[BbBBb=Db@@bء ULo7RZ`qbHB< কӿh% lz+bDDbBBbDDbB $^:<^ "L/;Z@W50  '< b  bU  U M=N >;98642$!#%% +462"%462"&4762'"$%"&#"32654'.#"63232yA_AA_BA_AA_Ap gM+1jGDfxY<0+u!*b_AA0-An/BA_?AD́}uM@Tw\-P4<Dt@q<;b  b    UU UM =M >DC@?<;871/*($" 44 +%27#"&#""54632327.#"'676$3 !"'&54632462"$462"%\L?;/((XF:!5HmvBd+-}݌X/#j#qBbBBb=Db@@bB;-N#'P@{:[lDV)7\hLbDDbBBbDDbBHEK#PX@B ; ) <@B ; ) @K b ` `  ` ` `U M=N >Y@?=:87520/-,*$"EE+462"%462"&2#"&5463232>5"&#"#"5463232.#"'4765B^BB^AB^BB^B-)R;59Q'T9/! ) sA/w%!ydN5pb_AA0-An/BA_?AvD+9T=7-=mI>;H#92׾ml1?9EKPX@&ZUK =N >KPX@#ZURK >K(PX@$bURK >@*bUUINBYYY@<:B?:E7.'.7327676.'&47327!"7463!2jјX1/H #)')7\@51  zј ZP  -=2 yVj5=D!5!X{Z3/F-/ ;<-/  2  1 + 3%+  2  1 @%-"- %jEQ6@3UK=M>HFNKFQHQ<8'&#!%# +#"'&74632326?>.'.'&47327476.'&47327!"5463!2)R+J1C7 *(/1 )5DRyV# ' A+f5X15!5!H'%5#;/5q !% ^`166?>T! 66L1+|$.#,?9AIKPX@'Z UK =N >KPX@$Z URK >K(PX@%b URK >@+b UUINBYYY@ IHNK"$" + #"&546322>7.'.7327676.'&47327462"$462"jјX1/H #)')7\@51  zј ZP  -=2 yVj5=DBbAAb=Db??bX{Z3/F-/ ;<-/  2  1 + 3%+  2  1 @cbDDbBBbDDbB %jW3@0UK =M>NJ9851!%% +462"%462"&#"'&74632326?>.'.'&47327476.'&47327B^BB^AB^BB^B)R+J1C7 *(/1 )5DRyV# ' A+f5X1b_AA0-An/BA_?ADH'%5#;/5q !% ^`166?>T! 66L1+|?d9IZTKPX@& ddZRK >K(PX@' ddbRK >K1PX@- ddbVINB@1  dddbVINBYYYY@ ZX-NK"$" + #"&546322>7.'.7327676.'&47327#"&54?6"'&'4?62jјX1/H #)')7\@51  zј ZP  -=2 yVj5=D A  u)X{Z3/F-/ ;<-/  2  1 + 3%+  2  1 @& '   %jBdmK,PX@&b =K =M>@*b = =K =M>Y@[WFEB>!%%%&&$ +#"&54?672#"547632#"'&74632326?>.'.'&47327476.'&47327  $%. x 2#3)R+J1C7 *(/1 )5DRyV# ' A+f5X1 %3   -H'%5#;/5q !% ^`166?>T! 66L1+|!IQY@ I * @*    U  U  UK>Y@YXUTQPMLHF?>C&CC+4.'&47327&#"&47>5 54.'&47327327462"$462"413323323314323323CmAcAAc>Cc??c/=L#  2  1  #K>=L#  2 5  #K>sX=L#  2  1  #K>Ӂ^\bDDbBBbDDbBPJ@G5 <U  U K = K>OMIHEA8621CC+462"%462"&54&'&47327&#"&47>='"&=4&'&47327326B^AA^AB^AA^B1DZsb<;cswb<7R^D1uFhb_AA0-An/BA_?AyE66C{{C65 C{RyC66Ey11/&K,PX@<@@"ZUOM>Y@$#  && +3#'54;2'4&+"=73!#!"32#.÷;8B%<;);{ ;|R1#< \yTy)K,PX@ <@ K,PX@*Z`CK=M>K.PX@+Z`OK=M>@)Z`UOM>YYY@('$! )) +3'54;254&+"'57263!#.+;2#&ٲJ 5(u)1[ckK%PX@0UU K  = M >K,PX@:UU K  =M = M >@8U  UUM = M >YY@#**kjgfcb_^XWTPMLCB?:53*1*0(CC+4.'.7327&#".7>53 !32!"&#&47>54.'&47327462"$462"313313313313R1V-ݑrw'D#313313323AcAAc>Cc??c1=L# 65  #K>=L#  43  #K>1)Z3¨ 3  #K>5=L#  2  1  #LbDDbBBbDDbB-!*LU^U@RU  U K = M>-+]\YXUTPOGEBA>:7610+L-K##CC+&#".7>54&'.73274+3"&47>54&'&4732732#462"%462"&;b sw  b;;b  rw b;y+F^s b<;c ws b7.'67/\L0'PTN$%BL`((=595Pf)ZVVJ"#NhmC B*&qLT=VZs# "+4'&'467272!!&5476\{mX =-'3 QH #/7{jHT<J>+?T C1ow9-s+7P+sOH"+#"'3&70###&'3&'+&67>54'&5467.'&547367&746?B/ N -[ZcV !XV#ZoZ6 /TT ( XTd#53!C? +)'-! sd;!/bfU9d^%```;`Dn83R #ay-R> + jQJ^d*=9d\+o=T   -! "+23!7!654.#!"&54>?3RyT7V3w3m%54&'&546?3 F5/A% oR|=FX++#`buX1')@-/5bƃR u=X%''}y#"+'#6'4'4&57!"746?3!2 17V++'!V\u#%JZDW L!;R6,"+'674&5&54632%'654'&547!"&46?3!2#unb^')19t^lX,,%fF)JBQ DFJmzR1##'BLb-ٗR[ ^9D15J "+6&'.546?B7D\s_00%;?"'/6N#1/~ %)++2GHq5hN+D$F?`)+R5H\ %!}5-"+#'654547!#0'6'4'05&5467.546?3!2VhBFxJNhBI=R^`^// 1)dV{7mM#H+-۞==*wV 'dF/PI}T ZFBGR=O=,("+47&'46?#!654#"'67632!&'&\BR**:;1)juX3dLuhDPo-OV4w%-5mpJTL-)@^3MZ"XlJ "+'654'.546?32%!VwfoT++#!X^)B`PE?O%'X\j"+#!"&46?7!2"L!4T3+\`V,,-5'u9PNNF}'= m} "+4.#!"546?3!2#!7!26 @1V**) ZR<\PPfc/BY P=ْy2)"+3!2"547>54&#!"547674'&5473-+XX5uLFJ+:X7#b q$s+}nTi3R[oLX`PLh)) ')R!7<-7{  "+!!.'.67#"&546?3!2R@EZgX--$;@N++ݔjVEBHFEF%7)"+7!7!654&#"#"&54&'.546?76'1B )P1i??41 -NTN&%8-83{? !՜v٘pao 35' T89L F  +%  Ufy" "+67654'&746?37TQ**M-5L))!!'>fBf$- BU T#?-{\u+%=[+\s#"+!7!&'&547654'.546?3# GR 5X,, XFXJ/ms+P7?_9wo1D U?DKJC767654'&546??&'.547.546?&'#>5!A3P(( '/IbHG-9=>-BTP''6%/A!123-#H):yh/9+hDT-#\=" 8 '>3Z:;{R/?SJA1#F =V!%E1  F2$"+#0#""'47.546?;2#"4'&+P/B!%"f3Nf33!`! :&B+'m5!/ `/1ToQyo93@ZFX>/)Ig }3.$"+4.#!#53+""'47&546?3!2#!7!2 @1-!/\/B!%"fHT++) ZR<ӬPfc/NF/)If +DBY P=g="+%&476'4'.54>?67&546?3&'5'5?0 #d/. 2/:E#-7yĢP''>`iBFpݬDv)9R!w/mZ)1?D'E-#38 8)+lF>%oH` `?&'#!7!&'&'&74656#"&546?3#?gFN%14F5B71D-F+^/?m+CR)`uV++.bP')N1-BH81XD#-Cb,B//; Xu=DboPE?SR5TFJd#/;u5$ "+'4730'"54%>54.#!"&546?3!2G^Xqh39I/"f\s`/0)yRt:PDIB;1;NN!LV3{B!+?%\C=HH3DrH;Z~^1-'-P"+054&#!"&546?3!2TeZm\/.%c26ZA9P R)3^2"+!&5&'.'&'47&54>"#6&54>?232&'36654'&546?#3˃9DN X 34} 21 + ZZ}b}!/1 !)5 =J5-)}V++%1<Zd{5/BNAu-V9L #+%{fu5X)@"F!7'c+ ;>7-NJZ;  C,""+654&+!73&'.547"&546?3!2#Tdf# #{Xm\..*b1$zb{!Y>E-ZA9P R)3%2% "+6&'.546?!6&'.546?;5F\s^10#;!;qj#0J!#9)T^f "+2#"547>!2#"547>y;qj#0J;qj#0J!#9)T^!#9)T^D)+3f@-,"@#bUIMAY@ ,"#($'!+6#"#"&54632327#"''"'&5467736ZH-3 1+Rh3+ BJTD\H(-fPZJD+' "! ;a'!`f++ !9CD!P;gάP%J>c-.7@$0/K#PX@RM =>K,PX@bRM >@"bUINBYYY@ ,#$($'"+32654&54632#"'.?6.#"'>32>32'>54"'"%,4 2*Rh4+ TT"[#H(-fPZJD$lY "!!;`' `g*, !"D"" PKPX@7  b  U   QM = M =>KPX@7  b  U   QM = M =>KPX@7  b  U   QM = M =>KPX@0  bI U  U   Q>KPX@0  bI U  U   Q>K.PX@0  bI U  U   Q>@=  bbI U  U  I  M  AYYYYYYY@"?>97530.)'!AA +27>54'&#&""5>32>323254&5432#"'#"&5476!4&BR>7 Xe0EhJ-}3i$6%u#T';]X F;G,?q{\9L;] D-)KBF&!/2)D)#3B5PXXyD"%\fD(D@A <9ddUIMA&*$"!+4#"'673632#"''6532>54&#"Vy;+PN\obo71b!';1% X573ms1#LF %XCjp /0L@I#,<ddbUIMA"%$#$(+.#"326"&5463254.#"'67327#  77=;'#DEh{o\-^C@w91b  Zmd;to+w7 !5 8#k@#USIMAY@  +27#"&4632!4&#"BbP)Xyb1*O)+-9)X+wr!?['5L1L1#j@"USIMAY@  +"'632#"&'!54.3267bP)Xybb1)O(+-:X+ws!?[&L1K2s00y-@&bUUQ>Y@/.+) 00 +";2+"327>32#"&546?&54632#&T)7?-13jtF $*;Yb@5d}Vde 6'%+9myB4 ^T5I]BDB^hs00y@&bUUQ>Y@,)&#!00 +"#'7632#"'.5463232'4'#"=4;2654&H\5!dkSzd5AdP;*" Nmj31,@;h^BICXN6RZ 4B{j;+%%;y77DS@0  8# <) ;KPX@6  Z  U  U  RM = M >K1PX@4  Z  U  U  U  RM >@:  Z Z  U  U  U  RM >YY@ PNIGDB=;31/- 77 +"'&'"#"'272>32#"&547&546?&5463263232>54&#&4&#"32>D  FVB&=%%=5׊T}iB!JVB37:'/!w#DNEw?J-:3%10 7LVd$?5/7`i@KRB?AFATi=%!%R\-+;zN?1H-337O!-*@'<UIMA,*&$H +4.'&67327"567#"&54632#(12.93:;".-";:fK*.eZ~';A(#??E[@X,' @;<dd   b UIL @EE?<'#3&&223 +;2&'54;>54.#"'67367654#"=737#"32&''54/&  Z;} %y=+EH' F- ^7N-HV1XR# 00 !7=R#--?W? ++)x,Pd<#@# U I L @Y@POJIGD$B#&H +&#"&7>=4&'&76727632>32&#"&7>=4#"&"&7>=4&"5=B^=B*!5q5   N`n ^0#?=T?>3s/G1=>3-b?;;# Iqi&C=?ËT;?L?#\9dKPX@#bTQM >@)bUTIMAY@ "#&&I!+4#"&#"&7>=4&'&76727632#"&5432265s=F5=B^=B* 6q5   Nl)->-P+'-#HT?;;# Iqq-3'3;w/? HKPX@QM >@UIMAY@   +2#"&5464&#"3267ywZB9J`D1J{mkyylc7@ KPX@QM =>KPX@QM =>KPX@QM =>KPX@QM =>KPX@UQ>@UQ>YYYYYY@  +"&547232654'&#"'632\i35 )%7_!/L%=!'Vy}R7;/#wsX3N!ywy/?BKPX@eM >@eIMAY@ "" +.#"#>32W;9Jy}hlsvy/?%@"dIMA"$ +0#"&=332654'?ywy`A1Jmk lzcXN'2@ 2('K%PX@#UUIL@@'dUUIL@YY@ #$$'2+&"&7>54&'&76727>32#"&'532654#"-;=>B+!5m6  f+Zea!Zt-Lh'q 9!;;# I '=tiwh\oV)@@=<:UIMA%#"#"+327#"5#"574326=732#+J! FlfR7+N s#H}w1FL%w3,@)<IINB'(%&+4&'&4727232754&'.7272"'&'4#"7q/^B f7J/^A  6m7   \3jF+#h+ʼnVF+#h+;$ 5!;?1?KPX@ /+(<KPX@ /+(<KPX@ /+(<@ /+(KPX@+ UU =M =M>KPX@' UUM =M>KPX@$ UUQM >@+b UUQM >YYYY@7410*)&# ?= +""'674'62;26762+""'674'62;2654.'#h4- ./mN-'  *ium/- .3iN3,0)9WEX+'PyuzB^A+Y{Ro7+?;&,  Pb<#@!  U I MAY@POJIGD$B#&H +6=4&'&727"'&54#"'#"754&'&727327&'54&'&7725=B^>B+!5q4  N`n _0#@=U?>3s/G2==3-b?;;# Iqi&C=?ËT;?L? w,@de>H*C+&47327#"'.'&47327?6&^9)  "%^)^/W`## '|J##+1,)1+^5,@)*#<dQ>#&#,'+&'&5476772767#"'&'#"/?727676'qqbJJbqq+'%'1?"%P{RR{P%?1'%  1]Z3++3Z\2 #BbF55FbB#d)=@: <9UUIMA$"$$('+'47632#"'57274&'#"543>54#"o:D^ӓqh%/%Cf<3B332+)gn4'oQdXGh<'fF1Ll O(u! ;X@4,"!@bIMAY@ 97'% +76'&/#"'&5675'&#'67672654'&'&'47632d%  7io1?%/D^;f 'P+^^#Ay + > A "%#?^wXX-;`>3LN W@T!/ +L ,`@ !=4&'&476724632#"&+X;LX++L4)>3/7?1-: m/--/mfL)-d['=4&'&476723672#"'&"w+X9EX+3Dyu fP)93#3 j0--/mJ/-b6 /@#%-=4K*PX@0$  <@0$  74&+"=737327#5"74&'#"=737(0   Z54"o:D^ӓqh%/%Cf<3B332+)gn33'oQdXHh<'fF1Ll N)u!J ;7@44,"!<bIMA97'% +76'&/#"'&5675'&#'67632654'&'&'47632d%  7io1?%/D^;f 'P+^^#Ay + > A "%#?_wXX-;`>3LN  V@T!/ +7g@ @!bUIMAY@  +2#"'.#"32>7#"&546B\h35 )%7^!/L%=!'Vy}R8;/#wrX3N!ywy72<@9,'<beUIMA8#""%"#!+#"=4;4632#"'&#"32#&''54?>V?X?0)%3Z y5+ o0 V7"#'7%5 ""-#@<"!:K PX@UQ>KPX@UQ>KPX@UQ>KPX@UQ>@UQ>YYYY@ ## +326?2326?2!"547#"'6?0ݘ7/ m +N   'R!3#^O g o;HD T4:KPX@2S  U U K =M>@/S  U UQ K >Y@$RPIHGEB>:932*&    +"2654&4632"!#&#"&47>7272&#"&472>&'!"+66V79fHFjiR71x) 5[l)65  NZF =I@ I>8KPX@9b  b VM=M  =M>@6b  b VQM=M  >YY@HF=;751/+)#!   +"2654&4632"# 546?4&#"#"54>3 327'327+55V8:gGFjh 7)RLP J5jO^ 1%yk3A9>/\9+)<<)+9ecGFd+Kov;+w.D &9h?i4L=#s"*5)#5FJCɶ'&K(PX@5  ZZU UN  =M>@3  ZZU U UM>YY@"?>541,C"B%"" +462"3254#"32>54&#263 !"&#&47>54.'&47LfLLf=r\31V`>7qTD#313313fLLfL'1)9a^5}N%+ 3  #K>5=L#  2  )3@+*($@0=M =M =M=N >Y@ #%%%$" +4632#"&4&'&767262#"'"'6724&#"+LC;JQ@;Hm)c !I]oޱj&+  ;VaOR3LT3-RRf,$ ,LqshQ7!taH3- !G϶+*K(PX@7  ZZ UN  =M=M>@5  ZZ U UM=M>YY@&"CB9850"G&F! %"#$" +4632#"&3254#"32>54&#263 !"&#&47>54.'&47LD;JR@;H6r\31V`>7qTD#3133133LT3-RR'1)9a^5}N%+ 3  #K>5=L#  2 3 )3@+*($@0=M =M=N =M>Y@ #%%%$" +4632#"&4&'&767262#"'"'6724&#"LC;KR@;H)c !I]oޱj&+  ;VaOR3LT3-RRf,$ ,LqshQ7!taH- !GҶ+*K(PX@5  ZZ U QN  =M>@3  ZZ U U QM>YY@"&"CB9850"G&F!    +!"5463!23254#"32>54&#263 !"&#&47>54.'&472 r\31V`>7qTD#313313"-!.'1)9a^5}N%+ 3  #K>5=L#  2  )3@+*($@. Q=M =M=N >Y@31.,'&#!  +!"5463!24&'&767262#"'"'6724&#"2 t)c !I]oޱj&+  ;VaOR"-!.f,$ ,LqshQ7!taHL?52?@$-+<53:K PX@4db ZM =M =M>K(PX@5db bM =M =M>@2db bQM =M >YY@:822$%3$() +'7&'&5!2'.#"3 72!"##"'47327&#"&5476h ^w;# -+/+dBAN(f&X-%#] jT+Fy[fH&Ml#+ TP)Hw PLH` ?P@+1 : 8 KPX@Bbb    bM=M =M = M>KPX@@bb    bUM=M = M>@=bb    bU  QM=M >YYY@??=;%3#$-$! +632#"&547'7&'&5432"&'.#"3272#"##"'47327&b N''' V Z[ ?cI83{V};ZdBAN)f10! % VwN1A7);Vs@Vp@&Mk"+ TP1}:~K PX@ UM =M>KPX@&ZUN =M>KPX@ UM =M>KPX@&ZUN =M>KPX@ UM =M>K!PX@&ZUN =M>K(PX@,ZZUN =M>@*ZZUUM>YYYYYYY@1+(':7)# +462"32>54.#"&47>54.'&473263 !"&KgLLgbTq?}uJ7\u323314S%9jZL)fLLfLHb@5o˅jyP5  #K>5=L#  2 {ӚALb B@5+ A@3bbU=M=M  >Y@ @?'"%'$" +4632#"&&5.#"326##"&'.54324&'&7672"'&LC;KR@;H"G1b53gP}%3%Pb>HG)RB)b !3R -u3LT3-RR.XT6G#S&07c, h) ( ,LfX?',"$1q}- >vK PX@QM =M>KPX@%ZQN =M>KPX@QM =M>KPX@%ZQN =M>KPX@QM =M>K!PX@%ZQN =M>K(PX@+ZZQN =M>@)ZZUQM>YYYYYYY@5/,+"!>;)$$" +4632#"&32>54.#"&47>54.'&473263 !"&LD;JR@;H%Tq?}uJ7\u323314S%9jZL)3LT3-RR1b@5o˅jyP5  #K>5=L#  2 {ӚAL\b B@5+ A@2bbQ=M=M  >Y@ @?'"%'$" +4632#"&&5.#"326##"&'.54324&'&7672"'&sKD;JR?;H]"G1b53gP}%3%Pb>HG)RB)b !3R -3LT3-RR.XT6G#S&07c, h) ( ,LfX?',"$1}- >K PX@ QM =M >KPX@&ZQN =M >KPX@ QM =M >KPX@&ZQN =M >KPX@ QM =M >K!PX@&ZQN =M >K(PX@,ZZQN =M >@*ZZUQM >YYYYYYY@5/,+"!>;  +!"7463!232>54.#"&47>54.'&473263 !"&1FTq?}uJ7\u323314S%9jZL)"-!.b@5o˅jyP5  #K>5=L#  2 {ӚALb B@5+ A@3bb Q=M=M  >Y@@?4321*(!  +!"5463!2&5.#"326##"&'.54324&'&7672"'&2 "G1b53gP}%3%Pb>HG)RB)b !3R -"-!..XT6G#S&07c, h) ( ,LfX?',"$1}-2Hw99K PX@eM =M>KPX@$ZeN =M>KPX@eM =M>KPX@$ZeN =M>KPX@eM =M>K!PX@$ZeN =M>K(PX@*ZZeN =M>@(ZZeUM>YYYYYYY@433H4H)# 2/)" +%32>54.#"&47>54.'&473263 !"&2&7>54./&746Tq?}uJ7\u323314S%9jZL);Yl;J DEb@5o˅jyP5  #K>5=L#  2 {ӚAhHXTy)@  M3Lb%L@?5K<9KPX@.b e= =M=M >@1bb e=M=M >Y@JI>=<;42+)'&$" +2&7>54./&546&5.#"326##"&'.54324&'&7672"'&;Xl;J DF$"G1b53gP}%3%Pb>HG)RB)b !3R -hHXTy)@  M3*.XT6G#S&07c, h) ( ,LfX?',"$1H}- =}9K PX@eM =M>KPX@$ZeN =M>KPX@eM =M>KPX@$ZeN =M>KPX@eM =M>K!PX@$ZeN =M>K(PX@*ZZeN =M>@(ZZeUM>YYYYYYY@4.+*! =: +&''6732>54.#"&47>54.'&473263 !"&qF4RL3;Tq?}uJ7\u323314S%9jZL)q'RXN\'^sb@5o˅jyP5  #K>5=L#  2 {ӚALHb A@4* @<9KPX@.b e= =M=M >@1bb e=M=M >Y@?>3210)'  +&''67&5.#"326##"&'.54324&'&7672"'&F3RL3;2"G1b53gP}%3%Pb>HG)RB)b !3R -q'RXN\'^A.XT6G#S&07c, h) ( ,LfX?',"$( Q]@G '54&'.7!272'.+"!"7463!2AF! JjLF  FPf1#J5' cd  s?@r  ;\50 %3%bj#1 5"('<i  H)1R {6% H;+)9 u-5 ;y3{; 2 q#wd='}%.#-L6%@[@X( <dd  b V  UM= M >@>9720+)'&" %% +#"/7>32!"5463!2!654&#"2#"'&5432!3q %B'8!6!XP3A%pmK!;{2 %("%-"-'oj% 8N?fw?owG?' Q]@G '@F b ZZ  U   UUM=L>Y@%TR ZWR]T]NKBA@=:9430+$! QQ% +"&547626762"'.+;26&#!.7>54&'.7!272'.+"!"5463!2HD" JjLF  FPf1#J5' cd  s?@r  ;\50 %3%bj#1/!5!'$?h  H*1R {6% H;+)9 u-5 ;y3{; 2 q#wd='}%.#-L> $?_@\' <dd  b V  UM= M >?=861/*(&%!$! +62"&547!"5463!2!654&#"2#"'&5432!31 >   U!6!XP3A%pmK!;{;,* %-"-'oj% 8N?fw?owG?H1 O@ E %@; ZZ e   U UM=L>Y@" LI@?>;8721.)" O O  +&''6726762"'.+;26&#!.7>54&'.7!272'.+"F3RL4<jLF  FPf1#J5' cd  s?@r  ;\50 %3%bj#1q'RXN\'^k1R {6% H;+)9 u-5 ;y3{; 2 q#wd='}LH 0R@O<9beUM=M >0.)'"  +&''67!654&#"2#"'&5432!3=F3RL3;iP3A%pmK!;{q'RXN\'^'oj% 8N?fw?owG?1]@ S&# 3 @G  Z ZU  UUQM= L  >Y@,ZWNMLIFE@?<70-*(%$]]  +272#"'&#""5>3226762"'.+;26&#!.7>54&'.7!272'.+"B"#f:L95B! 5?%jLF  FPf1#J5' cd  s?@r  ;\50 %3%bj#1XXg+#%%%Rm41R {6% H;+)9 u-5 ;y3{; 2 q#wd='}L}#>h@e& <  b  UU Q M = M >><750.)'%$   +272#"'&#""'>32!654&#"2#"'&5432!3mB""f9L95B! 5\3X@%P3A%pmK!;{XXf+#%%%Rl3#'oj% 8N?fw?owG?HWd@--*41 A IR P <;d_^X:K PX@UZ ZU  UN =M= L =K = M  >KPX@VZ  bU  UN =M= L =K = M  >K(PX@SZ  bU  U QN =M= L =K >@QZ  bUU  U QM= L =K >YYY@b`\ZWWUSOMHE>;86#932+'7#.7>54&'.7!272'.+"326762"'.+;26&+#"'473254#"&'73267 ad  s?@r  ;\50 %3%bj#1jLF  FPf1#J5' c#cBAN)f-fo :T 5 ;y3{; 2 q#wd='}1R {6% H;+)9 u-T&Mk"+ TP}jmo87L=V ;F@(- 6 4< :K PX@?b   ZU  U M=M =M>K*PX@@b   bU  U M=M =M>@=b   bU  UQ M=M >YY@ CA=< ; ;$%2%%+$" +327#"&''7&'&5432!3272#"'#"'473254!654&#"weA^y } `SmK!;{kcBBN)fP3A%VPR Kw?owG?l?K&Ml#+ TP'oj% 8?G@+ <9 C@ <);K(PX@6ZU U  UN =K>@4ZU U U  UK>Y@GEBA;:7573C +462"&#"&47>54&'.7!272'.+"326762"'.+LfLLf=<55313@r  ;`50 !&%mj#1jLF  FPffLLfLN=L# 6 3  #K>3{; 2 ;wn='T1R P`mH H;%?K PX@1  ZU M= M =K>@2  bU M= M =K>Y@?=:820%##C +462"&#"&47>5#"=4;5467632"&'.#"32+LfLLfB;c sw b :8-)&%  A A  +!"7463!223'.#"32674&'&47327# '&576= 5"ؑ##%5+߻of/d{N\1FZwur  -+bɭfq)%-"-HXm˶n$219# :: +3T`V N¿ #_@X,O3H KPX@B  b  U  U U M = M = M  >@@  b  U  U U  U M = M  >YY@(%$\ZWUB@:820*($_%_##    +!"7463!24"326"'327>54"&'&"#"'32#"'&7467&'&5467.54762>32 5"։=81>;bG04#5 L ɪg/ \K6faPt>;k7HVd-3?7MX%-"-^pX )uRz(m;sZ-+h&V #FuC7%''7/(M[B-LB!d1Z_K(PX@-US K  = K >@+U  US K >Y@_^YXUQNMDC@<98CC+462"4.'&47327&#"&47>5!&#"&47>54.'&47327!KgLLgu413323323314313323314313{fLLfL=L#  2  1  #K>=L#  2 5  #K>m=L# 65  #K>5=L#  2  1  #K>% C@< ? !KPX@+ U = = M =L>@. b U = M =L>YY@B@;:98C$C$" +4632#"&&#"&47>54#"&#"&47>54&'&767263 oKD;JR?;H@*   U  SQ K>Y@b`\ZWVQPMIFE<;CCC+4.'&47327&#"&47>5!&#"&47>54.'&47327!4632#"&413323323314313323314313{5LD;JR@;H/=L#  2  1  #K>=L#  2 5  #K>m=L# 65  #K>5=L#  2  1  #K>3LT3-RR%m C@< ? !@- bQ = M =L>Y@B@;:98C$C$" +4632#"&&#"&47>54#"&#"&47>54&'&767263 LC;JR?;HA@-UU  S K >Y@gfa`]YVULKHDA@;:54CC+462"$462"4.'&47327&#"&47>5!&#"&47>54.'&47327!BbBBb=Db@@b413323323314313323314313{bDDbBBbDDbB=L#  2  1  #K>=L#  2 5  #K>m=L# 65  #K>5=L#  2  1  #K>% I@B E '@0 b U = M = L >Y@HFA@?>652.$C#+462#"&462"&#"&47>54#"&#"&47>54&'&767263 FAZBB-/?A[AA[K(PX@5   bS QK = K >@3   bUS Q K >YY@jidc`\YXONKIGFDB><3CC+4.'&47327&#"&47>5!&+#"'473254/7&47>54.'&47327!413323323314313dBAN)f ]K323314313{/=L#  2  1  #K>=L#  2 5  #K>m=L# 6L&Mk#+ TP 5  #K>5=L#  2  1  #K>%TJ@1< E C KPX@4 b Q= =M= L >@7b b Q=M= L >YY@JJHFB@;854$C%"+'7&47>54&'&767263 &#"&47>54#"&+#"'47327& [`v b<)b !gK(PX@,  SQ K  = K>@*   U  SQ K>YY@b`\ZWVQPMIFE<;CCC+4.'&47327&#"&47>5!&#"&47>54.'&47327!327#"&'413323323314313323314313{weA^y /=L#  2  1  #K>=L#  2 5  #K>m=L# 65  #K>5=L#  2  1  #K>PR%5 D@= @ " @0 b = M =L=M>Y@CA<;:9C$C$" +327#"&'&#"&47>54#"&#"&47>54&'&767263 wd A_y @' UU Q  K  >Y@>=:632)(%!  +272#"'&#""5>324.'&47327&#"&47>5B"#f:L95A! 5\3X@$314313313323XXg+#%%%Rm4=L#  2  1  #K>=L# 65  #K>wD5AU@R<U Q M ==L>@>:821#  +272#"'&#""5>32&#"&47>54&'&7$724632#"&B"#f:L95B! 5\3X?%V1cimb2/V F&/KD;JR?;HXXg+#%%%Rm4{765 5{V1'+#se33LT4-RS1')19Fs<::K(PX@)  b UK =K>@'  b UUK>Y@A?98CC +4.'&47327&#"&47>5462"$462""&5476314313313323AcAAc>Cc??cD# J/=L#  2  1  #K>=L# 65  #K>bDDbBBbDDbB$?h  Hw- <;@8<dbV=L>C$! +632#"&547462"%462"&&#"&47>54&'&7$72 ;  `B^AA^AB^AA^B1cimb2/V F&/)+* _AA/-Bo/AA_?B{765 5{V1'+#se%Vcf@ A@dUM>Y@^\RQNJGF:5OC +4.'&47327>7676'&47327&#""'&'.'&#"&47>5#"&54763233143kd?b\f 9[L>=cRRD"7,413323iX-#%\/=L#  2  1  #K>KZA 2  1 IJD%JJ 6 9W,* n=L# 65  #K>)Hw P% ^@G @5b`  U=K=  L   >Y@]\YUNMFC@?>=C$! +672#"&5474&'&7672267676&'&4732723&#'54'&'&#&#"&47> N''' )b !,uBA)'V`[8SI'e   ' { #ir+5  bw b<j2/! f,$ ,LT'9Z=)66:*p 65%FJ'wE65 C%- bdM!@UQ M>Y@^]ZVSRFAOC$" +4632#"&4.'&47327>7676'&47327&#""'&'.'&#"&47>5VLC;JQ@;H3233143kd?b\f 9[L>=cRRD"7,4133233LT3-RRX=L#  2  1  #K>KZA 2  1 IJD%JJ 6 9W,* n=L# 65  #K>%q \@ E @4b  UQ=K=  L   >Y@[ZWSLKDA>=<;C$" +4632#"&4&'&7672267676&'&4732723&#'54'&'&#&#"&47>KD;JR?;H)b !,uBA)'V`[8SI'e   ' { #ir+5  bw b<3LT3-RRAf,$ ,LT'9Z=)66:*p 65%FJ'wE65 C%- bpM!@U Q M>Y@^]ZVSRFA/+  +!"5463!24.'&47327>7676'&47327&#""'&'.'&#"&47>513233143kd?b\f 9[L>=cRRD"7,413323"-!.A=L#  2  1  #K>KZA 2  1 IJD%JJ 6 9W,* n=L# 65  #K>% \@ E @5b  U Q=K=  L   >Y@ [ZWSLKDA>=<;($!    +!"7463!24&'&7672267676&'&4732723&#'54'&'&#&#"&47>\1])b !,uBA)'V`[8SI'e   ' { #ir+5  bw b<"-!.f,$ ,LT'9Z=)66:*p 65%FJ'wE65 C1qo- :q@#ZUQK>Y@ 32/)&% : 9$" +4632#"&2676&#!"&47>54.'&4732>33KD;JR?;H#3%";6O323314S%LA313H3LT3-RR}5  #K>5=L#  2  1  #K>ɢ%q([ @ bQ=L>Y@ $$C+4&'&7672&#"&47>4632#"&)b !;c sw b<KD;JR?;Hf,$ ,LZ{C65 Co3LT3-RRqo= F@,Z U UQ K>Y@?>;521('$FE  +!"7463!24632#"&2676&#!"&47>54.'&4732>33o 5!KD;JR?;H#3%";6O323314S%LA313H%-"-!3LT3-RR}5  #K>5=L#  2  1  #K>ɢq(4u @)b UQ=L>Y@+)1.)4+4$$C +4&'&7672&#"&47>4632#"&!"5463!2)b !;c sw b<KD;JR?;H!5!f,$ ,LZ{C65 Co3LT3-RRX$.#,1o- :y@$ZUQ K>Y@ 32/)&% : 9  +!"5463!22676&#!"&47>54.'&4732>33D1#3%";6O323314S%LA313H"-!.h}5  #K>5=L#  2  1  #K>ɢ%(c @!bQ=L>Y@%"((C+4&'&7672&#"&47>!"7463!2)b !;c sw b<j1f,$ ,LZ{C65 Ce"-!.1Ho- 9|@<9K(PX@$ZeK =K>@"ZeUK>Y@ 21.(%$ 9 8 +&''6?2676&#!"&47>54.'&4732>33F3RL3;#3%";6O323314S%LA313Hq'RXN\'^Չ}5  #K>5=L#  2  1  #K>ɢH'f@ <%$" 9KPX@e= =L>@be=L>Y@ ''C+4&'&7672&#"&47>&''67)b !;c sw bK!PX@5  db`  `K =  K  >K(PX@9  db`  `K =  K = >@7  db`  `U  K = >YYY@LJ@>;743G" +%#'"'#&#"&47>76&'.73%&#"&47>&#"&5476M44  m61^{n5|T< R )l  n  e.>)'( X ) * fX-%"\X`R65 NaLq, 2 s 1 5V01065 ,0)Hw P-# f@/* dL @'dd  M= L >Y@ca]\YURQJHC$$K$!+632#"&547&#"&47>54&'&7672632>32&#"&47>54#"&#"&47>54#"V N''' %E ?mb2/V -VXyg4};c sN D1t61DJND1Hr10! y965 5{V1',#/=PD:Z}C65 GyV$$LyG65 GyVF   L'@ 45 K!PX@8b`  `U K  =  K  >K(PX@<b`  `U K  =  K = >@:b`  `U  U  K = >YYY@HFC?<;21.-G"+462"#'"'#&#"&47>76&'.73%&#"&47>&LfLLf44  m61^{n5|T< R )l  n  e.>)'( X ) * fLLfLBX`R65 NaLq, 2 s 1 5V01065 ,0-# dI@F-( bJ <U  M= K >a_[ZWSPOHFC$$H$"+4632#"&&#"&47>54&'&7672632>32&#"&47>54#"&#"&47>54#"LC;JR?;H%E ?mb2/V -VXyg4};c sN D1t61DJND1Hr;3LT3-RRy965 5{V1',#/=PD:Z}C65 GyV$$LyG65 GyVF  3 - P/@ 89 K!PX@:b`  ` K  =  K  =M>K(PX@>b`  ` K  =  K = =M>@<b`  `  U  K = =M>YYY@LJGC@?6521G"$"+4632#"&#'"'#&#"&47>76&'.73%&#"&47>&LD;JR@;H44  m61^{n5|T< R )l  n  e.>)'( X ) * 3LT3-RRiX`R65 NaLq, 2 s 1 5V01065 ,0-3# dK@H-( bJ <  M= K =M>a_[ZWSPOHFC$$H$"+4632#"&&#"&47>54&'&7672632>32&#"&47>54#"&#"&47>54#"LC;JR?;H%E ?mb2/V -VXyg4};c sN D1t61DJND1Hr3LT3-RRuy965 5{V1',#/=PD:Z}C65 GyV$$LyG65 GyVF )E}@%1@)bUU=N >Y@ II$ +462"#&'&#"&#"&47>5.'.774.'&47327LfLLf;3N*(R"#=RR>" Z= M#>R ! R=#fLLfLT'4/))^R65 R\V-P 2 $@^R 2  1 R\- C5@2B(<UM=K>$C$H$" +4632#"&&#"&47>54&'&76726 &#"&47>54#"LC;KR@;H%E ?mb2/V - a@+bU=N =M>Y@ II$$" +4632#"&#&'&#"&#"&47>5.'.774.'&47327^LD;JR@;H3N*(R"#=RR>" Z= M#>R ! R=#3LT3-RR{'4/))^R65 R\V-P 2 $@^R 2  1 R\-3 C7@4B(<M=K=M>$C$H$" +4632#"&&#"&47>54&'&76726 &#"&47>54#"LC;KR@;H#%E ?mb2/V - a@)bU Q=N >Y@A=430/,+!   +!"7463!2#&'&#"&#"&47>5.'.774.'&473271D3N*(R"#=RR>" Z= M#>R ! R=##, /V'4/))^R65 R\V-P 2 $@^R 2  1 R\- CB@?B(< QM=K>A?;:730/+)%$  +!"5463!2&#"&47>54&'&76726 &#"&47>54#"1%E ?mb2/V - a@'beU=N >Y@@<32/.+*  +&''67%#&'&#"&#"&47>5.'.774.'&47327F3SL3;Z3N*(R"#=RR>" Z= M#>R ! R=#q'RXN\'^'4/))^R65 R\V-P 2 $@^R 2  1 R\-H BF@CA'<9eM=K>@>:962/.*($# +&''67&#"&47>54&'&76726 &#"&47>54#"uF3SL3;7%E ?mb2/V - a)( ?=98531/+*(A)A&$ % +"&5476"32% 76$3 272#"'&#""&5>32D# Jp㮚s[8W1A1.6 O,J6 %$?h  H.3-Jӿh% lzba`r0& )(Zx9L#1?$@7 K PX@3 d  dU U M= N >KPX@6  bU U = M= N >@3 d  dU U M= N >YYY@";9530.)'##  +272#"'&""5>32"32654&4762'"$62#"&547B#"f9L95B 5\3X@$-yqDfp  ;  'XXf+#%%%Rm3q́}u+)L19C@ 5:7)' @1  b   U U  M =N >Y@A?<;321/-+! +462"$462""32% 767&5>32327267&'&#"AcAAckCc??c<㮚siN,J7A8='Y`.7cCCcAAcCCcA\3-Jӿh% }< Zx8 a{8/}&"L#1:Cf@c<    UUUM= N >BA>=:9540.)'##  +272#"'&""5>32"32654&4762'"$462"%462"&B#"f9L95B 5\3X@$-yqDfp !A^BB^BA^BB^A'XXf+#%%%Rm3q́}ua^AA/-Bo/AA^@BKk '3I@F:ddeUIMA*( 0-(3*3&$ % +32654'&"32% 76$3 !"5463!2F! J㮚s[=#k'<h  H3-Jӿh% lz\%-".L%3iK PX@%XVM=M >@$dVM=M >Y@20+)!%%% +!2#!"746;&/7>32"32654&4762'"$l ! ?%KyqDfp "-%-'`q́}uLm '3F@C:bUM =M >*( 0-(3*3&$ % +#"&5476"32% 76$3 !"5463!2D# I{㮚s[#m%?i  G3-Jӿh% lz\%-".L$2@@=<dVM=M >1/*( $$#3%! +62!2#!"746;&'47"32654&4762'"$B ; l!  UyqDfp +* "-%->q́}u16CK@H <97:dUM  =K>><10'&# 64 +2"'&53265!"&#"&47>54.'&472$#"&5476sw+)+<}fN-313323314)4%X-#%];C^pb;+Im=L# 65  #K>5=L#  2 o)Hw P%!X 8F@D9'@-ddbM=N =>Y@ $(H&"$! +632#"&5474&'&76723>32#"'&#"&47>572654&#" N''' 0V - ;)f9BFC1b hm b2 ?>{tnH'f*10! mV1',#-95Tf9X"{7556{fDו+&1>I@F<UU M  =K> 98/.+'$#> < +462"2"'&53265!"&#"&47>54.'&472$ LfLLfosw+)+<}fN-313323314)4fLLfLqC^pb;+Im=L# 65  #K>5=L#  2 %!X 6DH@EB7%<bUM=N =>$(H&"$" +4632#"&4&'&76723>32#"'&#"&47>572654&#"LC;JR?;H0V - ;)f9BFC1b hm b2 ?>{tnH'f*73LT3-RRwV1',#-95Tf9X"{7556{fDו+&1JX@U<UU M  =  K = M >ED;:730/*)%#JH#" +462"!"32>23#"'.#&#"&47>54.'&472$LfLLfbTHXHhc1sl/TB!}Gs313323314)TfLLfL+Ogi;yC^+RH? 0 d%=L# 65  #K>5=L#  2 -T ;;@8b`UM=L>"$!C$" +4632#"&&#"&47>54&'&7672362#"'&#"+LC;JQ@;H1b hmb21T w+ k5RH.C $73LT3-RR#{765 5{V3' 7#d=I)?V"'#613; NZ@W<U M  =  K = M =M>IH?>;743.-)'#"NL##$" +4632#"&!"32>23#"'.#&#"&47>54.'&472$LD;JR?;ITHXHhc1sl/TB!}Gs313323314)T3LT3-RR7+Ogi;yC^+RH? 0 d%=L# 65  #K>5=L#  2 -3T ;=@:b`M=L=M>"$!C$" +4632#"&&#"&47>54&'&7672362#"'&#"LD;JR?;I@1b hmb21T w+ k5RH.C $3LT3-RR{765 5{V3' 7#d=I)?V"'#613 #Zo@l+ <U  U M =  K = M =M>%$UTKJGC@?:953/.$Z%X    +!"5463!24632#"&!"32>23#"'.#&#"&47>54.'&472$P!5!LD;JR?;ITHXHhc1sl/TB!}Gs313323314)T)%-"-3LT3-RR7+Ogi;yC^+RH? 0 d%=L# 65  #K>5=L#  2 -3T GW@T  b  ` U M=L=M>CA?=976521#  +!"5463!24632#"&&#"&47>54&'&7672362#"'&#"!6!LD;JR?;I@1b hmb21T w+ k5RH.C $%-#,3LT3-RR{765 5{V3' 7#d=I)?V"'#61; N`@]<U Q M  =  K = M >IH?>;743.-)'#"NL    +!"7463!2!"32>23#"'.#&#"&47>54.'&472$b1 .THXHhc1sl/TB!}Gs313323314)T#-!/+Ogi;yC^+RH? 0 d%=L# 65  #K>5=L#  2 -T ;H@Eb` QM=L>7531-+*)&%  +!"5463!2&#"&47>54&'&7672362#"'&#"1^1b hmb21T w+ k5RH.C $#-!/m{765 5{V3' 7#d=I)?V"'#67:f$ @%bUM =M >Y@ -&,)+462"'.#"'"&#&5632654'.'&547672LgKKg#D#=_=P^RYj}%'F!j^w+D{qfLLfL}=V`2cVN)``u}p( ^L\^:Zjb6b)5/@,<UM=M >$%*&+$+632654'&'&'47632'&#"#"'&4632#"&b-%H-Lib?Fhuq5Nr+8oO?݅KD;JR?;H+ o6/q.?kFTXHj1^{1%q.EYFj}-V3LT3-RR73D >j(@'bM =M =M>Y@ -&,*$"+4632#"&'.#"'"&#&5632654'.'&547672dLD;JR@;H#D#=_=P^RYj}%'F!j^w+D{q3LT3-RR}=V`2cVN)``u}p( ^L\^:Zjb6b3)51@.<M=M =M>$%*&+$+632654'&'&'47632'&#"#"'&4632#"&b-%H-Lib?Fhuq5Nr+8oO?݅KD;JR?;H+ o6/q.?kFTXHj1^{1%q.EYFj}-VK3LT3-RR7':G}@ $ <=;:KPX@*bUM ==M >@-bbUM =M >Y@ '-&,)+462"'.#"'"&#&5632654'.'&547672#"&5476\LfLLf#D#=_=P^RYj}%'F!j^w+D{qߪX-%#\fLLfL}=V`2cVN)``u}p( ^L\^:Zjb6)Hw Pb+)7C@ /K#PX@&bUM=M >K(PX@/bbM =M=M >@-bbUM=M >YYY@ $'$$*&+$+632654'&'&'47632'&#"#"'&632#"&547'4632#"&b-%H-Lib?Fhuq5Nr+8oO?݅ N''' D=6BI:5A+ o6/q.?kFTXHj1^{1%q.EYFj}-V10! .DK.(KJ7 ?Gz@ )@%bUM =M >Y@GFCB>D/ +T }=V`2cVN)``u}p( ^L\^:Zjb6gKKgLb)>F|@C?=<:0.-A@'bN=M=M >Y@ )*&+$+632654'&'&'47632'&#"#"'&#&'7&7463267'567"b-%H-Lib?Fhuq5Nr+8oO?݅if;1<#D=6A<1 + o6/q.?kFTXHj1^{1%q.EYFj}-V`%).DL. *%' 73F|0@/bUM =M =M>Y@ -&,*$# +462"4632#"&'.#"'"&#&5632654'.'&547672LgKKgwLD;JR@;H#D#=_=P^RYj}%'F!j^w+D{qfLLfL3LT3-RR}=V`2cVN)``u}p( ^L\^:Zjb6b3)5A;@8<UM=M =M>$$$%*&+$+632654'&'&'47632'&#"#"'&4632#"&4632#"&b-%H-Lib?Fhuq5Nr+8oO?݅KD;JR?;HKD;JR?;H+ o6/q.?kFTXHj1^{1%q.EYFj}-VK3LT3-RR3LT3-RR+8^KPX@ $/"<KPX@ $/"<KPX@ $/"<@ $/"KPX@+U M = M  =K>KPX@.U M  = M  =K>K%PX@+U M = M  =K>@)U U M  =K>YYYY@6310,+15C +462"&#"&47>54&+""'6743) 72#.+"3LfLLfF͍F+6dj}7)  9v-5*fLLfLP{;65 ;{de`w { s^fi+9L@I- <bbM=M= >&#%%%#$%" +463263232+327#"'&'#"=6;47>7#"&LD;J6 14'P$%!RfD= #39!6 ;H3LT48) `E @s'HA-E95}/ ;S3+9 <hKPX@ (3&<KPX@ (3&<KPX@ (3&<@ (3&KPX@- M = M  =K=M>KPX@0 M  = M  =K=M>K%PX@- M = M  =K=M>@+ U M  =K=M>YYYY@:7540/15C$" +4632#"&&#"&47>54&+""'6743) 72#.+"LD;JR@;HXF͍F+6dj}7)  9v-5*3LT3-RRw{;65 ;{de`w { s^fi+3 :Q@N9*,<dbM = =M> 750.)'"  : :$" +4632#"&347>7>3232+327#"'&'#"=6LD;JR@;H9%6 Y 14'P$%!RfD= #3LT3-RR `E @s'HA-E95}/ ;+9 <kKPX@ (3&<KPX@ (3&<KPX@ (3&<@ (3&KPX@+ Q M = M  =K>KPX@. Q M  = M  =K>K%PX@+ Q M = M  =K>@) U Q M  =K>YYYY@:7540/.+*)%$"  +!"7463!2&#"&47>54&+""'6743) 72#.+"/1F͍F+6dj}7)  9v-5*#, /R{;65 ;{de`w { s^fi+ :U@R9*,<db RM = > 750.)'"  : :  +!"7463!2347>7>3232+327#"'&'#"=6+1B9%6 Y 14'P$%!RfD= ##, / `E @s'HA-E95}/ ;F+9 ;KPX@'2%<9KPX@'2%<9KPX@'2%<9@'2%<9YYYKPX@, e M = M =K>KPX@) e M = M =K>KPX@, e M = M =K>K%PX@) e M = M =K>@' e U M =K>YYYY@9643/.-*)($#! +&''67&#"&47>54&+""'6743) 72#.+"F3RL3;F͍F+6dj}7)  9v-5*q'RXN\'^}{;65 ;{de`w { s^fi+H 9Y@V8)+<9dbeM = > 64/-(&! 9 9 +&''67347>7>3232+327#"'&'#"=6F3RL3;9%6 Y 14'P$%!RfD= #q'RXN\'^ `E @s'HA-E95}/ ; -JYK(PX@ Q K =N >@ UQN >Y@ IHC+L( +462"%462"&32>54&'&47327! '&4&'&47327)A_AA_BB^AA^B%1/jdN- @} I; 7.- 煲;c^  {A_AA/-Bo/AA_?BAVm-f?\xRy7 2  1  %E@/Vu,Jy= 2  1 ;%}J>@;8<bQK = >EA(C% +462"%462"&"'&'# 4&'&47327327&54&'&47327qA^BB^BA^BB^A 4R -\6;N^+JEJcY5T n+J_AA/-Bo/AA_?B$X?',"'34HG@yG62Vfb87!|{A62Vf ;-Q@ @'    UU QN >Y@POLHED><1-!  +272#"'&#""5>3232>54&'&47327! '&4&'&47327XB"#f:L95B! 5\3X?%1/jdN- @} I; 7.- 煲;c^  {AXXg+#%%%Rm3^Vm-f?\xRy7 2  1  %E@/Vu,Jy= 2  1 ;%;}Q@? $" @,  bU R K  = >Y@LH><40-,(&!   +272#"'&#""5>32"'&'# 4&'&47327327&54&'&47327B""f9L95B! 5?%4R -\6;N^+JEJcY5T n+JXXg+#%%%Rm3AX?',"'34HG@yG62Vfb87!|{A62Vf F- Cc9K(PX@eK =N >@eUN >Y@BA>:760.#  +&''6732>54&'&47327! '&4&'&47327F3RL4;1/jdN- @} I; 7.- 煲;c^  {Aq'RXN\'^Vm-f?\xRy7 2  1  %E@/Vu,Jy= 2  1 ;%H} CJ@G1<9beK= >>:0.&"  +&''67"'&'# 4&'&47327327&54&'&47327=F3RL3;A4R -\6;N^+JEJcY5T n+Jq'RXN\'^X?',"'34HG@yG62Vfb87!|{A62Vf  a@NB <:K(PX@0d  U M= K  =N >K,PX@.d  U  U M=N >@,d  U  U  UN >YY@`_\YVTRP"$C+L,% +"&547632>54&'&47327! '&4&'&47327&5>327272#"'&'"263D" J1/jdN- @} I; 7.- 煲;c^O[N,J7A8W1A1.7 S  {A%>h  GVm-f?\xRy7 2  1  %E@/Vu,Jy= 2 Zx9 a`r/& 1 ;%%^`@]#L 1/ <d  bU V K  = >YUKIA=:9%#"$(! +6323272#"'&#""5>3267"'&'# 4&'&47327327&54&'&47327 ;%KB#"f9L:5A 5\34R -\6;N^+JEJcY5T n+J+* XXf+#%%%Rl X?',"'34HG@yG62Vfb87!|{A62Vf VzK(PX@* UU  K  =N >@( UU    UN >Y@UTQMJICA62&$ +462"& 462"%!"5463!232>54&'&47327! '&4&'&473271BZAAZBbBZBBZ#1/jdN- @} I; 7.- 煲;c^  {A/@BZ??ZB@/-?%-#-Vm-f?\xRy7 2  1  %E@/Vu,Jy= 2  1 ;%VU@RD )' <  bU  U K  = >QMCA9521-+&% +462"%462"&!"5463!2"'&'# 4&'&47327327&54&'&47327A^BB^BA^BB^A1!5!4R -\6;N^+JEJcY5T n+JF^AA/-Bo/AA^@B%-"-X?',"'34HG@yG62Vfb87!|{A62VfI@CK(PX@$ U UK = >@" U UU >YY@GEA=42)% II +272#"'&#"6776.'&47327"'.'&47327&5>325B#"f9L:5A! 3+33 "-=2 yFz 5=DK31B@81{1K\3X@$XXf+### 1 $\P%+  2  1 @;/0b+1 2  Rl3 1RD@A<U U= >?;,*   +272#"'&""5>32&47327"'.'&4732776&B#"f9L95B 5\3X@$Po`BZ (6(<9 dj  !  #!TXXf+#%$$Rm3660*+/+ - !  66! 1  wN=3- :P'@U =M>YOI$$"+4632#"& "'.'&4732776.'&47327LC;JQ@;HK31B@81{ї+33 "-=2 yFz 5=C3LT3-RR/0b+1 2  1 $\P%+  2  1 @ 31} E @= =M>O*E$"+4632#"&&47327"'.'&4732776&LC;KR@;H;o`BZ (6(<9 dj  !  #!3LT3-RR660*+/+ - !  66! 1  wN= -6CP@ 3<97:K(PX@dK = >@dU >Y/H(J+ 76&'&47327"' '.'&47327732654'&?W hJFq 61g[J{ї -11$3Z+'!'\VJ:B 2  1 \X/00/0b;+ 2  1 $A59+Fw P%N,KPX@*bb` == >@'ddb`= >YY@ J$*H%% +#"/7>32276&'&47327"' "'&'.'.7327277.R13v "OW-[ )=+5?:34L duTu 5>!).BLQ668.+/+/+ L4 66:@  [ -6CP@ 3<97:K(PX@dK = >@dU >Y/H(J+ 76&'&47327"' '.'&473277#"&5476?W hJFq 61g[J{ї -11$3=X.%#\VJ:B 2  1 \X/00/0b;+ 2  1 $A59)Hw P% M@ +KPX@*bb` == >@'ddb`= >YY@ J$*H$! +632#"&547276&'&47327"' "'&'.'.7327277q N''' v "OW-[ )=+5?:34L duTu 5>10! BLQ668.+/+/+ L4 66:@  [ -}HXE,@UU >Y@ H(L +462"%462"&76&'&47327"' '.'&473277!A_AA_BA_AA_A?W hJFq 61g[J{ї -11$3^BB/-An/BB^?AVJ:B 2  1 \X/00/0b;+ 2  1 $A59%Qp/ @'  b `U= >Y@POLKJ$*H +462"%462"&276&'&47327"' "'&'.'.7327277sA^BB^BA_AA_Av "OW-[ )=+5?:34L duTu 5>b_AA0-An/BA_?ABBLQ668.+/+/+ L4 66:@  [ ->R;" @UU >Y@ H(K+462"76&'&47327"' '.'&473277LfLLfl?W hJFq 61g[J{ї -11$3fLLfLVJ:B 2  1 \X/00/0b;+ 2  1 $A59% Kh)@%b`U= >Y@ J$*H$" +4632#"&276&'&47327"' "'&'.'.7327277LC;KR@;HVv "OW-[ )=+5?:34L duTu 5>73LT3-RRBLQ668.+/+/+ L4 66:@  [ 3-- BV?&@U =M>Y@ H(L$"+4632#"& 76&'&47327"' '.'&4732773LD;JR@;Hf?W hJFq 61g[J{ї -11$33LT3-RRbVJ:B 2  1 \X/00/0b;+ 2  1 $A593%} Kl)@'b`= =M>Y@ J$*H$" +4632#"&276&'&47327"' "'&'.'.7327277LC;KR@;HXv "OW-[ )=+5?:34L duTu 5>3LT3-RR!BLQ668.+/+/+ L4 66:@  [ [_K(PX@"UK = K >@ UU K >Y@ZYVRCCL +462".&#"&47>76&'.'.73277676'&47327&#".7>LfLLfQ1 Vh|Og'Q9OB  {‰R$_n- JV{ L_(!=5ZG { PfLLfL B# ϙZ 2  1 '3!!+O4 2  1 59 m\ 2  1 (3!,1-N4 2  1 9fO[*@'UK=>ZXTRFB40CI +%&#"&476?6'.#&47327?6&'&47327&#"&47>/&4632#"&w  RXR >3L@ RR ?[ n7R{ZR{H P RR 9c ^KD;JR?;H 65 N J?668-'66V u65 33LT3-RR S[cgK(PX@$    UK =K>@"    UUK>Y@cb_^[ZWVCCCK +%.&#"&47>76&'.'.73277676'&47327&#".7>462"$462"yQ1 Vh|Og'Q9OB  {‰R$_n- JV{ L_(!=5ZG { PAcAAc>Cc??cB# ϙZ 2  1 '3!!+O4 2  1 59 m\ 2  1 (3!,1-N4 2  1 9sbDDbBBbDDbBfOXa0@-UK=>`_\[XWSRFB40CI +%&#"&476?6'.#&47327?6&'&47327&#"&47>/&462"%462"&w  RXR >3L@ RR ?[ n7R{ZR{H P RR 9c B^AA^AB^AA^B 65 N J?668-'66V u65 3_AA0-An/BA_?ADWK(PX@ UK =K>@UUK>Y@ CMC +462"&#"&47>54&'.'&4732776&'&47327HKgLLgExF0KE {Z R%9H ?D{ JG!#fLLfLN{965 9{70/R0 2  1 59-'-3 2  1 #8J:q %j Q/@,UK=M>HD32/+!%%$"+4632#"&#"'&74632326?>.'.'&47327476.'&47327LD;JR@;H4)R+J1C7 *(/1 )5DRyV# ' A+f5X173LT3-RRH'%5#;/5q !% ^`166?>T! 66L1+|)*7KPX@<631/-+:KPX@<631/-+:KPX@<631/-+:K#PX@<631/-+:@<631/-+:YYYYKPX@.M =M =M =M >KPX@,M =M =M=M >KPX@.M =M =M =M >K#PX@,M =M =M=M >K(PX@*M =M =M=M >@(UM =M=M >YYYYY@# *) +%2672#&)"5476#!""'6743)26323&'&56?od7 '!%y?5%!\ #-+ - N\~Z| pT/ B'5>D/ ,T #k! 3S@0!KPX@)b ==M=N>KPX@)b ==M=N>KPX@)b ==M=N>KPX@)b ==M=N>@)db=M=N>YYYYY@ 3 3-+%#  +&''673!2>32326?2%"547''674 F3RL3; '+bR$ܻNR);*//q5/)!q'RXN\'_p ==T< |S q )39 6 KPX@&$<KPX@&$<KPX@&$<K#PX@&$<@& $KPX@6M  =M  = M=M =M>KPX@8M  =M  =M = M =M>K#PX@6M  =M  = M=M =M>K(PX@4 M =M = M=M =M>@2  UM = M=M =M>YYYYY@ /)('#"! 6 5$" +4632#"&2672#&)"5476#!""'6743)26323KD;JR?;HXd7 '!%y?5%!\ #-+ -3LT3-RRՔ| pT/ B#3k 4Z@ 1"KPX@-b=M=N=M>KPX@-b=M=N=M>KPX@-b=M=N=M>KPX@-b=M=N=M>@-b=M=N=M>YYYYY@ 4 4&#%u$" +4632#"&3!2>32326?2%"547''674;LD;JR@;H '+bR$ܻNR);*//q5/)3LT3-RR  ==T< |S q )9 6KPX@&$<KPX@&$<KPX@&$<K#PX@&$<@& $KPX@4 QM  =M  = M=M >KPX@6 QM  =M  =M = M >K#PX@4 QM  =M  = M=M >K(PX@2 Q M =M = M=M >@0  U QM = M=M >YYYYY@ /)('#"! 6 5  +!"5463!22672#&)"5476#!""'6743)26323T1d7 '!%y?5%!\ #-+ -#, /| pT/ B#k 4Y@ 1"KPX@+bQ =M=N>KPX@+bQ =M=N>KPX@+bQ =M=N>KPX@+bQ =M=N>@+bQ =M=N>YYYYY@  4 4.,&$!   +!"5463!23!2>32326?2%"547''674u1 '+bR$ܻNR);*//q5/)#, / ==T< |S q % C@< ? !@. b Q = M =L>Y@B@;:980/,(%$   +!"5463!2&#"&47>54#"&#"&47>54&'&767263 1/KPX@/b  bM= M = >@-b  bU M = >YY@=;64/-(&#!@@ +462"%462"&347>7>3232+327#"'&'#"=6A^BB^BA_AA_A^9%6 Y 14'P$%!RfD= #'^BB/-Bo/BB^@BP `E @s'HA-E95}/ ;%T T2 @0  b `U M == >Y@SROND@640.$    +"2654&4632"276&'&47327"' "'&'.'.7327277+55V8:gGFjhv "OW-[ )=+5?:34L duTu 5>9+);;)+9dbHFdBLQ668.+/+/+ L4 66:@  [ %jR Y;@8UM =K =M>PL;:73!%$& +"26544632"#"'&74632326?>.'.'&47327476.'&47327jV55V8gGFki)R+J1C7 *(/1 )5DRyV# ' A+f5X1 9+);;)-sdbHFdH'%5#;/5q !% ^`166?>T! 66L1+|`>J`@]J?9 <b  bUM=M=M  >IG><$$&'%#$#" +#"'72654&#"'632# 546?4&#"#"54>3 327'327fpN)3BB3 Np 7)RLP J5jO^ 1%yk3A9>/\Nn>H13F@o4+Kov;+w.D &9h?i4L=#s"*5)#5FJq <#@0  bU M=M=K>Y@ 98#&$5C$" +4632#"&&#"&47>54&+"'546326547>32#"'."LC;JQ@;H;b rwb; Z 1(n?wR<7d8%1s3LT4-QR{C65 Cy #0j?C_vJhF-DCM@&%KPX@5b`M ==L=N >KPX@1b`M =N=N >K%PX@5b`M ==L=N >@8b`bM =L=N >YYYY@ K'&'$# +7!2#"&"&'6324.#"'654&#"&#".7>#B#3jb!;Z/ TXB'T313`H<1 ͏"uv '+g;3>wS!#m=N! 6 3  %J3D KJ@G+:S   U K=M> IG@?><9510*)!  $" +4632#"&!#&#"&47>7272&#"&472>&'!"FKD;JR?;H71x) 5[l)65  NZ3 4@G@D@5/<bbM=M =N>)$$$&'%$" +4632#"&# 546?4&#"#"54>3 327'327JLC;JR?;H 7)RLP J5jO^ 1%yk3A9>/\3LT3-RR+Kov;+w.D &9h?i4L=#s"*5)#5FJ?[ @6 b  ` US UK>Y@ ZXUSLJED=;4320-)%$ +!#&#"&47>7272&#"&472>&'!"#47654#"#"'4632/71x) 5[l)65  NT/+/5J/#H 7'JNZ(4QX@U4)# <  b`bb  UM=M >PNJH%)$$$&'# +%# 546?4&#"#"54>3 327'327#67654#"#"&54632] 7)RLP J5jO^ 1%yk3A9>/\3;??161%^>JGc+Kov;+w.D &9h?i4L=#s"*5)#5FJ38;BJP'!? %%MO LYR@O,   TRJHA@?=:621+*"  +&'&76?!#&#"&47>7272&#"&472>&'!""&5476%!N\71x) 5 A'5=D. +T 6 ^ 65 =>[l)65  N!9_  @Z@L@ LA;"@0dbb =M=N >Y@ )$$$&'( +672&''67367# 546?4&#"#"54>3 327'327! 6  3RL4;k2MM 7)RLP J5jO^ 1%yk3A9>/\}(% $'RXN\'_R\+Kov;+w.D &9h?i4L=#s"*5)#5FJ' LYR@O,  TRJHA@?=:621+*"  +&'&76?!#&#"&47>7272&#"&472>&'!"32654'&%!N\71x) 5[l)65  Ni#7_  ?BN@NC=$@4b  bU  =M=M >Y@MKB@<:640.(&%% +#"/7>3&''67# 546?4&#"#"54>3 327'327c    :"F3RL4; 7)RLP J5jO^ 1%yk3A9>/\} !$2q'RXN\'_+Kov;+w.D &9h?i4L=#s"*5)#5FJ?h@^P RMI KG @6 b  ` US UK>Y@ geb`YWED=;4320-)%$ +!#&#"&47>7272&#"&472>&'!"#&'&76?67654#"#"54632/71x) 5[l)65  N/+/5$ 5=D. +T nc;)#H 7'JNZ(4^k@hG? BA=<4)# <  b`bb  U =M=M >][WUNL)$$$&'# +%# 546?4&#"#"54>3 327'327#&''67367654'"#"&54632] 7)RLP J5jO^ 1%yk3A9>/\;?# -3RL4;k3P)51%_=JGc+Kov;+w.D &9h?i4L=#s"*5)#5FJ37;B3'RXN\'_S`8D)?%%NPXet@q_[8 VTMLKIFB>=76.*  +272#"'&#""7>32!#&#"&47>7272&#"&472>&'!"&'&76?I&&q@S@;G!$ ;f9`G(71x) 5!N\TSb)!$##Ng16 ^ 65 =>[l)65  N+'5>D/ ,T Z (4W@:Q FECA@ 4)# @>bb   U   U =M=N >Y@65USPOLJIH><875W6W)$$$&'#+%# 546?4&#"#"54>3 327'327272+&''673&#""5>32] 7)RLP J5jO^ 1%yk3A9>/\B#"f9?|3RL4;5B 5\3X@$c+Kov;+w.D &9h?i4L=#s"*5)#5FJXXf]'RXN\'_# $%%Rl33 KXP@MWTRPNL+:S   U K=M> IG@?><9510*)!  $" +4632#"&!#&#"&47>7272&#"&472>&'!"&'&76?FKD;JR?;H71x) 5!N\3LT3-RR6 ^ 65 =>[l)65  N_'5>D/ ,T Z3 4@K@IHFDC @5/@6   dbbM=M =N>Y@AAAKAK)$$$&'%$" +4632#"&# 546?4&#"#"54>3 327'327&''67JLC;JR?;H 7)RLP J5jO^ 1%yk3A9>/\;F4RL3;3LT3-RR+Kov;+w.D &9h?i4L=#s"*5)#5FJoq'RXN\'_ LY]@Z  , TRJHA@?=:621+*" $" +#"&'73267!#&#"&47>7272&#"&472>&'!"#"&5476fo:T ^71x) 5[l)65  N_$?i  HZ(4I@IGF@?:4)# @3dbb  VM=M >Y@ DB3#)$$$&'# +%# 546?4&#"#"54>3 327'3276322327#"&'7] 7)RLP J5jO^ 1%yk3A9>/\R ; A^y Cic+Kov;+w.D &9h?i4L=#s"*5)#5FJ+*l% LY@  ,@,  dVS   U K>Y@ TRJHA@?=:621+*" $" +#"&'73267!#&#"&47>7272&#"&472>&'!"32654'&fo:T ^71x) 5[l)65  NW'<h  HZ(4K@BA;: 4)# @3  dbbUM=M >Y@ KI$)$$$&'# +%# 546?4&#"#"54>3 327'32767#"&'7/7>36] 7)RLP J5jO^ 1%yk3A9>/\m A^y C?%c+Kov;+w.D &9h?i4L=#s"*5)#5FJ &% Lh@  ,  b  Z  UUS  U K>@?  b  b  UUS  U K>Y@" geb`YWRQJHA@?=:621+*" $" +#"&'73267!#&#"&47>7272&#"&472>&'!"#47654#"#"54632fo:T ^71x) 5[l)65  N/+/5J/#H 7'JNZ^(4A^k@hA;:5 4)# < b  `bb UUM=M >][WUNLGF$$)$$$&'# +%# 546?4&#"#"54>3 327'327327#"&'%#676'4'"#"&54632] 7)RLP J5jO^ 1%yk3A9>/\weA^y ;??151$^>JGc+Kov;+w.D &9h?i4L=#s"*5)#5FJPR38;BJP)?%%MO!)0i@&I +*ge^]\ZWSONHG?;-,*0+0%# )) +272#"'&#""5>32.'73267#"'!#&#"&47>7272&#"&472>&'!"I%&q@S?;H!$ ;f9Qk9T 8g&#)71x) 5[l)65  NZ(4X@O XC;:5 4)# @@bb   U   UUM=N >Y@WUSQMLKIGEBA$$)$$$&'#+%# 546?4&#"#"54>3 327'327327#"&'"5>323272#"'&#"] 7)RLP J5jO^ 1%yk3A9>/\weA^p4\3X?%LB"#f:L95B<c+Kov;+w.D &9h?i4L=#s"*5)#5FJPRr%Rl3XXf+#&3 KX_@\+  VTPNIG@?><9510*)!  $" +4632#"&!#&#"&47>7272&#"&472>&'!"#"&'73267FKD;JR?;H71x) 5[l)65  Njmo87Z3 4@MZ@W@5/KIEC)$$$&'%$" +4632#"&# 546?4&#"#"54>3 327'327327#"&'JLC;JR?;H 7)RLP J5jO^ 1%yk3A9>/\wd A_y 3LT3-RR+Kov;+w.D &9h?i4L=#s"*5)#5FJiPR31DP@:  <6:K(PX@A ZZ U N  =M=L= M  >@? ZZ   U UM=L= M  >Y@ OMIGA>5430-,'&# DD +26762"'.+;26&#!.7>54&'.7!272'.+"4632#"&jLF  FPf1#J5' cd  s?@r  ;\50 %3%bj#1 LD;JR@;H1R {6% H;+)9 u-5 ;y3{; 2 q#wd='}3LT3-RRL3 %1C@@ <bUM=M =M>$#%%%"$ +!654&#"2#"'&5432!34632#"&jP3A%pmK!;{[LD;JR@;H3'oj% 8N?fw?owG?3LT3-RRD`=@:  <6;K PX@M b  X ZZ  UU N  =M=L>K(PX@N b  ` ZZ  UU N  =M=L>@L b  ` ZZ  U   UUM=L>YY@$_]ZXQOJIA>5430-,'&# DD +26762"'.+;26&#!.7>54&'.7!272'.+"#47654#"#"'4632jLF  FPf1#J5' cd  s?@r  ;\50 %3%bj#1CF53#J 7]KVL1R {6% H;+)9 u-5 ;y3{; 2 q#wd='}&/+/5J/#H 7'JNL %BT@Q <  b`b  UUM=M >A?;9%%%%"$ +!654&#"2#"'&5432!3#67654#"#"&54632jP3A%pmK!;{x;??151%_=JG3'oj% 8N?fw?owG?38;BJP'!? %%MO`D]@!J W :  <6;K(PX@J ZZ  U   UU N  =M=L>@H ZZ  U   U   UUM=L>Y@,FE[YVURPNLHGE]F]A>5430-,'&# DD +26762"'.+;26&#!.7>54&'.7!272'.+"272#"'&#""5>32jLF  FPf1#J5' cd  s?@r  ;\50 %3%bj#19B"#f:L95B! 5\3X?%1R {6% H;+)9 u-5 ;y3{; 2 q#wd='}XXf+# $%%Rl3L %>b@_+8 <b   U    UVM=M >'&<:7631/-)(&>'>%%%"$+!654&#"2#"'&5432!3272#"'&#""'>32jP3A%pmK!;{B""f9L95B! 5\3X@%3'oj% 8N?fw?owG?XXf+#%%%Rl3DQ^@'PMKIGE :  <6;TR :K(PX@<  d ZZ U N  =M=L>@:  d ZZ   U UM=L>Y@YWA>5430-,'&# DD +26762"'.+;26&#!.7>54&'.7!272'.+"&'&56?"&5476jLF  FPf1#J5' cd  s?@r  ;\50 %3%bj#1!N\> A1R {6% H;+)9 u-5 ;y3{; 2 q#wd='}'5=D. +T !:_  @L %=@;65310.+ @/dbV =M=M >Y@ %%%"$ +!654&#"2#"'&5432!3672&''67367jP3A%pmK!;{0 63RL4;k7Z3'oj% 8N?fw?owG?(%'RXN\'_ZiDQ^@'PMKIGE :  <6;TR :K(PX@<  d ZZ U N  =M=L>@:  d ZZ   U UM=L>Y@YWA>5430-,'&# DD +26762"'.+;26&#!.7>54&'.7!272'.+"&'&56?2654'&jLF  FPf1#J5' cd  s?@r  ;\50 %3%bj#1!N\? A1R {6% H;+)9 u-5 ;y3{; 2 q#wd='}'5=D. +T #7_   @B#>@ &@0dbU  =M=M >Y@><750.)'%$  +&''67&/7>3267!654&#"2#"'&5432!3'F3RL4" :" cU1RP3A%pmK!;{Nq'RXN\'&$ eN'oj% 8N?fw?owG?*oG@&&e1.85EK(PX@Nb  `  Z   ZUU N  =M= L  >@Lb  `  Z   ZU   UUM= L  >YY@!,+li`_^[XWRQNIB?<:760/+o,o)''% +#476'4#"#"'467&'&56?63226762"'.+;26&#!.7>54&'.7!272'.+"DF64#I 7,&I\VLjLF  FPf1#J5' cd  s?@r  ;\50 %3%bj#1/+/5J/#H 7869D/ ,T bN<1R {6% H;+)9 u-5 ;y3{; 2 q#wd='}LN %Km@jG? B853 A6 <  bbb  UU =M=M >JHED;:%%%%"$ +!654&#"2#"'&5432!3#67654'"&'"&'&''67362jP3A%pmK!;{~;??16(33$ 74L4;k+@.6JG3'oj% 8N?fw?owG?37;BJP)?9')7N\'_FO!P]j@*d`S&# 3 @H  Z ZUUU  UM= L  >Y@,ZWNMLIFE@?<70-*(%$]]  +272#"'&#""7>3226762"'.+;26&#!.7>54&'.7!272'.+"&'&76?I&&r?S@;G!$ ;f9`F)*jLF  FPf1#J5' cd  s?@r  ;\50 %3%bj#1!N\TSb)!$##Ng1l1R {6% H;+)9 u-5 ;y3{; 2 q#wd='}'5>D/ +T Ld 0I}@z6 C <b  U   UV =M=M >21GEBA><:8431I2I0.)'"   +&''67!654&#"2#"'&5432!3272#"'&#""5>32'F3RL4;RP3A%pmK!;{~B#"f9L:5A 5\3X@$Nq'RXN\'_'oj% 8N?fw?owG?qXXf+#%%%Rl33DP]@:  <\YWUSQ6:K(PX@A ZZ U N  =M=L= M  >@? ZZ   U UM=L= M  >Y@ OMIGA>5430-,'&# DD +26762"'.+;26&#!.7>54&'.7!272'.+"4632#"&&'&76?jLF  FPf1#J5' cd  s?@r  ;\50 %3%bj#1 LD;JR@;Hu!N\~1R {6% H;+)9 u-5 ;y3{; 2 q#wd='}3LT3-RR'5=D. +T L3! %1<@:9754 @5   dbUM=M =M>Y@222<2<$#%%%"$ +!654&#"2#"'&5432!34632#"&&''67jP3A%pmK!;{[LD;JR@;HF3SL3;3'oj% 8N?fw?owG?3LT3-RRq'RXN\'_1)EK PX@-bX  UK =K>K(PX@.b`  UK =K>@,b`  UUK>YY@ DB'%CC +4.'&47327&#"&47>5#47654#"#"'4632314313313323DF53#J 7\LVL/=L#  2  1  #K>=L# 65  #K>/+/5J/#H 7'JN5w93@0b`U=L>$'%C+%&#"&47>54&'&7$72#67654#"#"&546321cimb2/V F&/@UK=M>Y@ $'CC+4.'&47327&#"&47>54632#"&314313313323"KD;JR?;H/=L#  2  1  #K>=L# 65  #K>3LT3-RR53wD(40@-M ==L=M>$$$&C+%&#"&47>54&'&7$724632#"&4632#"&1cimb2/V F&/KD;JR?;HLC;JQ@;H{765 5{V1'+#se33LT4-RS*3LT3-RRL3D &5@2M =M =M>%#   +"32% 76$3 4632#"&ϓ㮚s[LD;JR?;I3-Jӿh% lz3LT3-RRL3 $5@2M=M =M>#!   +"32654&4762'"$4632#"&yqDfp 9LC;KR@;H7q́}u3LT3-RRL 6}K PX@,bXUM =M >@-b`UM =M >Y@530.'%   +"32% 76$3 #47654#"#"54632ϓ㮚s[CF53#J 7\LVK3-Jӿh% lzw/,/5J/#H 8'IML 5F@Cb`UM=M >42.,%#  +"32654&4762'"$#67654#"#"&54632yqDfp ;??151%_=JG7q́}u38;BJP'!? %%MOL '4>@;&#!<*(:dM =M >/-   +"32% 76$3 &'&56?#"&5476ϓ㮚s[{!N\~> A3-Jӿh% lz'5=D. +T !9`  ?L .o@ .)(&$#!@ d =M=M >Y@,+   +"32654&4762'"$672&''673yqDfp  6'4RL3;k-A7q́}u(%*'RXN\'_IPL '4>@;&#!<*(:dM =N >/-   +"32% 76$3 &'&56?32654'&ϓ㮚s[{!N\~? A3-Jӿh% lz'5=D. +T #7_  ?B #2@ @)db =M=M >Y@ 20+)"    +&''67"32654&4762'"$#"/7>3mF4RL3;yqDfp ~c    :"Nq'RXN\'_q́}u !$L F@4&(#!@-b`UM =M >Y@DC=;86/-  +"32% 76$3 &'&56?67654#"#"54632#ϓ㮚s[{!N\d-#I 7\LVLDF3-Jӿh% lz'5=D. +T db8*#H 7'JN%/+/5L #@`@]<bbU  = M=M > ?=970.)("    +&''67"32654&4762'"$#676'4'"#"&54632mF4RL3;yqDfp T20*( $$  +272#"'&#""5>32"32% 76$3 &'&56?I%&q@S?;H $ ;f9`G)d㮚s[X N\~TSb)!$##Ng1n3-Jӿh% lz'5>D/ +T Lo #<h@e)6< U  U  = M=M >%$ :8541/-+'&$<%<"     +&''67"32654&4762'"$272#"'&#""'>32mF4RL3;yqDfp wB""f9L95B! 5\3X@%Nq'RXN\'_q́}u6XXg+#%%%Rl3L3 &3>@;2/-+)':M =M =M>%#   +"32% 76$3 4632#"&&'&56?ϓ㮚s[LD;JR?;I N\~3-Jӿh% lz3LT3-RR'5=D. +T L3# $/~@ -,*('@&dM=M =M>Y@%%%/%/#!  +"32654&4762'"$4632#"&&''67yqDfp 9LC;KR@;HF4RL3;7q́}u3LT3-RRq'RXN\'_L".;A@>1/:ddVM =M >$#64*(#.$.'"&%2 +#"#% 76$323264'.5432"32#"&5476m^İs[ƠI5A+1/^RP3㮚NX-#%\NVoӿh% lz]+Z8"Ph3-J)Hw PLT *80@3dbbU M=M >Y@42.,)'"!  +"32654&47623264'.5432'"$632#"&547yqDfpr< .+1/^RPRTo N&'' 7q́>+Z7#PiRTk~}u810! L".;A@>1/:ddVM =M >$#64*(#.$.'"&%2 +#"#% 76$323264'.5432"3232654'&m^İs[ƠI5A+1/^RP3㮚Z+'!'\NVoӿh% lz]+Z8"Ph3-J+Fw PLT *9KPX@3bbU = M=M >@3dbbU M=M >Y@9720)'"!  +"32654&47623264'.5432'"$#"/7>32yqDfpr< .+1/^RPRTo/S17q́>+Z7#PiRTk~}u8!).L".JK PX@;  b  `X  UV M =M >@<  b  ``  UV M =M >Y@$#IGDB;943*(#.$.'"&%2 +#"#% 76$323264'.5432"32#47654#"#"'4632m^İs[ƠI5A+1/^RP3㮚DF53#J 7\LVLNVoӿh% lz]+Z8"Ph3-J$/+/5J/#H 7'JNLT *G\@Y  b`b  UU M=M >FD@>750/)'"!  +"32654&47623264'.5432'"$#676'4#"#"&54632yqDfpr< .+1/^RPRTob;??151%^>JG7q́>+Z7#PiRTk~}u38;BJP'!? %%MOLj".GK1PX@ 4A <@ 4A @9  b   U   UV M =N >Y@0/$#EC@?<:8621/G0G*(#.$.'"&%2+#"#% 76$323264'.5432"32272#"'&#""5>32m^İs[ƠI5A+1/^RP3㮚B#"f:L95A! 5\3X@$NVoӿh% lz]+Z8"Ph3-J$XXg+#%%%Rl3LT *C@ 0= KPX@;b   UU M  = M=N >@9b   U   UU M=N >YY@$,+A?<;8642.-+C,C)'"!   +"32654&47623264'.5432'"$272#"'&#""5>32yqDfpr< .+1/^RPRTo; \4D40; 0S/O9!7q́>+Z7#PiRTk~}ua`r0&*((Zx8 L3".:C@@dV M =M =M>$#9731*(#.$.'"&%2 +#"#% 76$323264'.5432"324632#"&m^İs[ƠI5A+1/^RP3㮚LD;JR?;INVoӿh% lz]+Z8"Ph3-J3LT3-RRL3Ts *6K@HbU M=M =M>53/-)'"!  +"32654&47623264'.5432'"$4632#"&yqDfpr< .+1/^RPRTo5LC;KR@;H7q́>+Z7#PiRTk~}u3LT3-RR 3-8DXK(PX@!K =N =M>@UN =M>Y@ $$C+L&+32>54&'&47327! '&4&'&473274632#"&1/jdN- @} I; 7.- 煲;c^  {ALC;JR?;H-Vm-f?\xRy7 2  1  %E@/Vu,Jy= 2  1 ;3LT3-RR%3}8D<@9& <bK= =N>$(J(C%+"'&'# 4&'&47327327&54&'&473274632#"&4R -\6;N^+JEJcY5T n+JuLD;JR@;HX?',"'34HG@yG62Vfb87!|{A62VfG3LT3-RR 8TK PX@-bX  UK =N >K(PX@.b`  UK =N >@,b`  UUN >YY@ SQ'%C+L& +32>54&'&47327! '&4&'&47327#47654#"#"546321/jdN- @} I; 7.- 煲;c^  {ACF53#J 7\LVK-Vm-f?\xRy7 2  1  %E@/Vu,Jy= 2  1 ;j/+/5J/#H 7'JN%8UL@I& <b`b  UK= >TR'%J(C% +"'&'# 4&'&47327327&54&'&47327#67654#"#"&546324R -\6;N^+JEJcY5T n+J@% d  U UM >Y@USIGCA>;85)'  MM +"#".54&+"=73%#"32>5454&+"=732654#"&54632#"&5476?V-lњ={R3;8P35q43fFmE1 3;8P`?108/9NPX-#%]Z˓Ӱ[Fjw/+11ZVe3f-T@wCA Z/+1'1#%+dHDR)Hw P%GU@M & @1  d  bb UM = >Y@QOKIBA##*(C% +"'&'# 4&'&47327327&54&'&473674'.5432263632#"&5474R -\6;N^+JEJcY5T n9811^9H 8 N&'' X?',"'34HG@yG62Vfb87!|{A6)3#PkI*#2Vf10! MZ@ :@% d  U UN >Y@USIGCA>;85)'  MM +"#".54&+"=73%#"32>5454&+"=732654#"&5463232654'&?V-lњ={R3;8P35q43fFmE1 3;8P`?108/9NPZ+'!'\Z˓Ӱ[Fjw/+11ZVe3f-T@wCA Z/+1'1#%+dHDR+Fw P%GV'@ & KPX@1  bb U =M = >KPX@*b U =M = >KPX@*  db UM = >@1  d  bb UM = >YYYY@VTOMBA##*(C% +"'&'# 4&'&47327327&54&'&473674'.5432263#"/7>324R -\6;N^+JEJcY5T n9811^9H ˎ/R1X?',"'34HG@yG62Vfb87!|{A6)3#PkI*#2Vf!).Mi:K(PX@:  b  b U  UM =M >@8  b  b U  UUM >YY@"hfcaZXSRIGCA>;85)'  MM +"#".54&+"=73%#"32>5454&+"=732654#"&54632#47654#"#"54632?V-lњ={R3;8P35q43fFmE1 3;8P`?108/9NPCF53#J 7\LVKZ˓Ӱ[Fjw/+11ZVe3f-T@wCA Z/+1'1#%+dHDR+/+/5J/#H 7'JN%Gd_@\& <  b  bb  U UM = >ca][TRMLBA##*(C%+"'&'# 4&'&47327327&54&'&473674'.5432263#676'4#"#"&546324R -\6;N^+JEJcY5T n9811^9H ;??151%^>JGX?',"'34HG@yG62Vfb87!|{A6)3#PkI*#2Vf+38;BJP'!? %%MO)m@)Z;KPX@6UUM= K  = M >KPX@5IUM= K  = M >K PX@6UUM= K  = M >K(PX@4UUU K  = M >@2UUU   V M >YYYYY@&igca^[XUIG@=:41/-+'&%#! mm +"#".54&+"=7327&5>323272#"'&#"26:;#"32>5454&+"=732654#"&54632?V-lњ={R3;8PFr\3X@%KB#"f:L95A 5q43fFmE1 3;8P`?108/9NPZ˓Ӱ[Fjw/+1 Rl3XXf+#""1ZVe3f-T@wCA Z/+1'1#%+dHDR%G`j@gM Z & <b U   UUM = >IH^\YXUSQOKJH`I`BA##*(C%+"'&'# 4&'&47327327&54&'&473674'.5432263272#"'&#""5>324R -\6;N^+JEJcY5T n9811^9H kB"#f:L95B! 5\3X?%X?',"'34HG@yG62Vfb87!|{A6)3#PkI*#2VfCXXf+#%%%Rl33)MY:@*  U UM = M  >Y@XVRPIGCA>;85)'  MM +"#".54&+"=73%#"32>5454&+"=732654#"&546324632#"&?V-lњ={R3;8P35q43fFmE1 3;8P`?108/9NPLD;JR?;IZ˓Ӱ[Fjw/+11ZVe3f-T@wCA Z/+1'1#%+dHDR3LT3-RR%3}GSM@J& <bUM = = N  >RPLJBA##*(C% +"'&'# 4&'&47327327&54&'&473674'.54322634632#"&4R -\6;N^+JEJcY5T n9811^9H oLC;KR@;HX?',"'34HG@yG62Vfb87!|{A6)3#PkI*#2VfG3LT3-RR<IV?=:K(PX@dK =L>@dUL>Y@ /CMC+%&#"&47>54&'.'&4732776&'&4732732654'&ExF0KE {Z R%9H ?D{ JG!#Z+'!'\{965 9{70/R0 2  1 59-'-3 2  1 #8J:q+Fw P %jETbKPX@$b =K=M>@!ddK=M>Y@TRMK<8'&#!%# +#"'&74632326?>.'.'&47327476.'&47327#"/7>32)R+J1C7 *(/1 )5DRyV# ' A+f5X1/S1H'%5#;/5q !% ^`166?>T! 66L1+|-!).3-<H[K(PX@"K =K=M>@ UK=M>Y@ $,CMC +%&#"&47>54&'.'&4732776&'&473274632#"&ExF0KE {Z R%9H ?D{ JG!#KD;JR?;H{965 9{70/R0 2  1 59-'-3 2  1 #8J:q3LT3-RR %j}EQaK#PX@#K=N=M>@!K=N=M>Y@PNJH<8'&#!%# +#"'&74632326?>.'.'&47327476.'&473274632#"&)R+J1C7 *(/1 )5DRyV# ' A+f5X1hLC;JQ@;HH'%5#;/5q !% ^`166?>T! 66L1+|23LT3-RR<XN K(PX@/  b`  UK =K>@-  b`  UUK>YY@WURP%CMC +%&#"&47>54&'.'&4732776&'&47327#47654#"#"54632ExF0KE {Z R%9H ?D{ JG!#3DF63#I 7\LVL{965 9{70/R0 2  1 59-'-3 2  1 #8J:q/+/5J/#H 7'JN %jEbD@Ab`  UK=M>a_[YRPKJ<8'&#!%# +#"'&74632326?>.'.'&47327476.'&47327#67654#"#"&54632)R+J1C7 *(/1 )5DRyV# ' A+f5X1;?@151%_=JGH'%5#;/5q !% ^`166?>T! 66L1+|38;BJP'!? %%MOqU@ @)U  U    UL>Y@ LHED<;84'&#  +272#"'&#""5>32&#"&47>54&'.'&4732776&'&47327;B#"f9L95B 5\3X@$*ExF0KE {Z R%9H ?D{ JG!#XXg+#%%%Rl3{965 9{70/R0 2  1 59-'-3 2  1 #8J:q %j^U@R<U  U K  =M>UQ@?<8(&%#  +272#"'&#""5>32#"'&74632326?>.'.'&47327476.'&47327B"#f:L95A! 5\3X?%)R+J1C7 *(/1 )5DRyV# ' A+f5X1XXf+#%%%Rl3H'%5#;/5q !% ^`166?>T! 66L1+|J!3D K*PX@ 4'" <@ 4'" K*PX@6bM =M =N = M >@:bM ==M =N = M >YY@B@97+)%#33  +2'>54#"'62327327#"&'#"'&54>'.#"32677D+\A ;@^'N';cIU* ^w;MXqtAi1&wG']+#cG9[;[;=L3# X)%TF @I5!YQkCVodoM#/FD;S\J!3D K*PX@ 4'" <@ 4'" K*PX@6bM =M =N = M >@:bM ==M =N = M >YY@B@97+)%#33  +2#".5462327327#"&'#"'&54>'.#"3267`^?;B[+D'N';cIU* ^w;MXqtAi1&wG']+#cG9[;FV%)X #5L;9] @I5!YQkCVodoM#/FD;S\J! BSK*PX@ ) C61/$ <@ )C61/$ K*PX@8bM = M =N  = M  >@@b=M == M =N  = M  >YY@""!QOHF:842.,(&!B"B  +2'>54#"54&54632'"'2327327#"&'#"'&54>'.#"3267+7D,\A;?^G+!5 O 'N';cIU* ^w;MXqtAi1&wG']+#cG9[;[;=L3# X)%TFR )%  @I5!YQkCVodoM#/FD;S\J! BS\K*PX@ ) C61/$ <@ )C61/$ K%PX@8bM = M =N  = M  >K*PX@<b=M = M =N  = M  >@@b=M == M =N  = M  >YYY@""!QOHF:842.,(&!B"B  +2#".546&'4632'"'2327327#"&'#"'&54>'.#"3267B`^@;B\+D)%3 P 'N';cIU* ^w;MXqtAi1&wG']+#cG9[;EV%)X #5L;9\R )%  @I5!YQkCVodoM#/FD;S\J!!2DRpK*PX@P< "<@P< "K*PX@9  b M =M =N =N >K1PX@A  b = M ==M =N =N >@G  b ` = M ==M =N =N >YYY@&FE43NLERFRB@;:3D4D0.'% !! +2327327#"&'#"'&54>'.#"32672'>54#"54%2#"=67'N';cIU* ^w;MXqtAi1&wG']+#cG9[;X7D+]A;@^'%D  @I5!YQkCVodoM#/FD;S\>[;=L3# X)%TF - >)J!ARjK*PX@ ( B50.# <@ (B50.# K%PX@9bM  = M=N  = N  >K*PX@?b`M  = M=N  = N  >@Cb`M  == M=N  = N  >YYY@&! PNGE9731-+'% A!A  +2#".546%2#"=62327327#"&'#"'&54>'.#"3267P`^@;B\+D'% D F'N';cIU* ^w;MXqtAi1&wG']+#cG9[;EV%)X #5L;9\ - >) @I5!YQkCVodoM#/FD;S\J!H!2DWbK*PX@I RQ < "K*PX@G b U  U M =M=N =M >@K b U  U M ==M=N =M >YY@*FE43USONLJEWFWB@;:3D4D0.'% !! +2327327#"&'#"'&54>'.#"32672'>54#"547267#"'&"'6327'N';cIU* ^w;MXqtAi1&wG']+#cG9[;7D+\B;@^#*5)/THN+5-uBS@ @I5!YQkCVodoM#/FD;S\Z<=L3# X)%TF/F<3/:@3J!F$FWbK*PX@ - G:53( <:@ -G:53( <:YK PX@FZUUM = M= N  = M  >K*PX@GbUUM = M= N  = M  >@KbUUM == M= N  = M  >YY@*&%USLJ><8620,*%F&F$$  +267#"'&"'6322#".5462327327#"&'#"'&54>'.#"3267f#+5)/THN+5-tBT?T`_?<B\+C'N';cIU* ^w;MXqtAi1&wG']+#cG9[;/F<3/:@3FV%)X #6K<9]? @I5!YQkCVodoM#/FD;S\Q@ 1 @0b US   U K>Y@"OMFEDB?;760/'#  +2'>54#"54!#&#"&47>7272&#"&472>&'!"77D+\B;@^X71x) 5[l)65  NQ@ 1 @0b US   U K>Y@"OMFEDB?;760/'#  +2#".546!#&#"&47>7272&#"&472>&'!"/`^?< B[+D771x) 5[l)65  N5 '`'@ @ KPX@4bS  UM=  K >K*PX@2bUS  U  K >@<bUS  UK =  K >YYY@&"!^\UTSQNJFE?>62$#!'"'  +2'>54#"54&54632#"'!#&#"&47>7272&#"&472>&'!"%7D+\A ;@^G+!5 P 71x) 5[l)65  N '`(@ @ KPX@4bS  UM=  K >K*PX@2bUS  U  K >@<bUS  UK =  K >YYY@&"!^\UTSQNJFE?>62$#!'"'  +2#".546&'4632'"'!#&#"&47>7272&#"&472>&'!"`_?<B\+C)%3 P 7 1 x)5[l)65  N&_r@ ? KPX@5bS  UM=  K >K*PX@3bUS  U  K >K1PX@8XbVS  U  K >@>Xb`VS  U  K >YYYY@*! ][TSRPMIED>=51#" &!&  +2'>54#"54%2#"=6!#&#".7>7272&#"&472>&'!"37D+\B;@^'%D 81 y) 5;DL #A +  zǗ ' rz%[;=L3# X)%TF - >)u6 ^ 65 =>[l)65  N&_,@ ? KPX@5bS  UM=  K >K%PX@3bUS  U  K >@9b`US  U  K >YYY@*! ][TSRPMIED>=51#" &!&  +2#".546%2#"=6!#&#".7>7272&#"&472>&'!"T`^@;B\+D'% D 81 y) 5;DL #A +  zǗ ' rz%FV%)X #5L;9]- >)y6 ^ 65 =>[l)65  Nw $+dh@D <:K PX@AZUI S UM =  K   >KPX@BbUI S UM =  K   >K#PX@@bUUI S U  K   >@AbU UU  S U  K   >YYY@.&%b`YXWURNJICB:6('%+&+" $$  +2'>54#"547267#"'&"'632!#&#"&47>7272&#"&472>&'!"7D+\B;@^#*5)/THN+5-uBS@l71 x)5[l)65  N $+d@D <:K PX@AZUI S UM =  K   >K PX@BbUI S UM =  K   >@@bUUI S U  K   >YY@.&%b`YXWURNJICB:6('%+&+$$  +267#"'&"'6322#".546!#&#"&47>7272&#"&472>&'!"J#+5)/THM+5-uBS@T`^@;B\+D71x) 5[l)65  NZ;DK*PX@ $  <@ $  KPX@9bZ    UM =M=M >K*PX@:bb    UM =M=M >K1PX@?bb  I   UM =M=M >@@b  b  U  UM =M=M >YYYY@CA@><:97420.+)  +2'>54#"54327#"&54>7.54632#"'&#"32632#"&#"7D+\A ;@^"Z>w#ˬ'O8RJaT&+0o?4TfhR?I[;=L3# X)%TFbTVd1`#A18!XEqVCREP\\ /7 \Z;DK*PX@ $  <@ $  KPX@9bZ    UM =M=M >K*PX@:bb    UM =M=M >K1PX@?bb  I   UM =M=M >@@b  b  U  UM =M=M >YYYY@CA@><:97420.+)  +2#".546327#"&54>7.54632#"'&#"32632#"&#"`^?< B[+DZ>w#ˬ'O8RJaT&+0o?4TfhR?IFV%)X #5L;9]`TVd1`#A18!XEqVCREP\\ /7 \Z; SK*PX@ 3 (& <@ 3 (& KPX@;b Z    UM= M=M >K%PX@<b  b    UM= M=M >K*PX@@b  b    U=M= M=M >K1PX@Eb  b I   U=M= M=M >@Fb  b U  U=M= M=M >YYYYY@"RPOMKIHFCA?=:8+)%#  +2'>54#"54&54632'"'327#"&54>7.54632#"'&#"32632#"&#"7D+\A;?^G+!5 O Z>w#ˬ'O8RJaT&+0o?4TfhR?IZ;=L3# X)%TER )% TVd1`#A18!XEqVCREP\\ /7 \Z; SK*PX@ 3 (& <@ 3 (& KPX@;b Z    UM= M=M >K%PX@<b  b    UM= M=M >K*PX@@b  b    U=M= M=M >K1PX@Eb  b I   U=M= M=M >@Fb  b U  U=M= M=M >YYYYY@"RPOMKIHFCA?=:8+)%#  +2#".546&54632'"'327#"&54>7.54632#"'&#"32632#"&#" `^?< B[+D)%3 P Z>w#ˬ'O8RJaT&+0o?4TfhR?IEV%)X #5L;9\R )% TVd1`#A18!XEqVCREP\\ /7 \Z;R K*PX@ 2 '% <@ 2 '% KPX@<b Z    UM= M=M >K%PX@=b  b    UM= M=M >K*PX@Ab  b    U=M= M=M >K1PX@Fb  b I   U=M= M=M >@Mb`  b U  U=M= M=M >YYYYY@&QONLJHGEB@><97*($"  +2'>54#"54%2#"=6327#"&54>7.54632#"'&#"32632#"&#"7D+\A;?^'%D Z>w#ˬ'O8RJaT&+0o?4TfhR?IZ;=L3# X)%TE - >)\TVd1`#A18!XEqVCREP\\ /7 \Z;R K*PX@ 2 '% <@ 2 '% KPX@<b Z    UM= M=M >K#PX@=b  b    UM= M=M >K*PX@Cb`  b    UM= M=M >K1PX@Hb`  b I   UM= M=M >@Ib`  b U  UM= M=M >YYYYY@&QONLJHGEB@><97*($"  +2#".546%2"756327#"&54>7.54632#"'&#"32632#"&#"`_?<B\+C'% C Z>w#ˬ'O8RJaT&+0o?4TfhR?IEV%)X #5L;9\ . >)`TVd1`#A18!XEqVCREP\\ /7 \bV1@  L,K(PX@H Z  b  Z UU N  =M=L>@F Z  b  Z U   UUM=L>YY@&SPGFEB?>9850)&#!VV  +2'>54#"5426762"'.+;26&#!.7>54&'.7!272'.+"%7D+\A ;@^jM FF Pg1#J5' cd s?@r ;\50 %3%bj#1Z<=L3# X)%TF!1R {6% H;+)9 u-5 ;y3{; 2 q#wd='}-V1@  L,K(PX@H Z  b  Z UU N  =M=L>@F Z  b  Z U   UUM=L>YY@&SPGFEB?>9850)&#!VV  +2#".54626762"'.+;26&#!&47>54&'&47!272'.+"`_?<B\+D\jL E F Pg1#J5' ces@@s<\5/ %3%bk#1FV%)X #5L<9]!1R {6% H;+)9 u-5 ;y3{; 2 q#wd='} e@ ['$.+; KPX@L  Zb ZUM= N =M= L  >K(PX@J  Zb Z UU N =M= L  >K*PX@H  Zb Z U  UUM= L  >@Ld  Zb Z U  UUM= L  >YYYY@*"!b_VUTQNMHGD?8520-,&%!e"e  +2'>54#"'6&54632'"'26762"'.+;26&#!&47>54&'&47!272'.+"7D+\A ;@^G+!5 P jL F F Og1#J5' ces@@s<\5/ %3%bk#1[;=L3# X)%TFR )% 1R {6% H;+)9 u-5 ;y3{; 2 q#wd='} e@! ['$.+; KPX@L  Zb ZUM= N =M= L  >K%PX@J  Zb Z UU N =M= L  >K(PX@Nd  Zb Z UU N =M= L  >@Ld  Zb Z U  UUM= L  >YYYY@*"!b_VUTQNMHGD?8520-,&%!e"e  +2#".546&'4632'"'26762"'.+;26&#!.7>54&'.7!272'.+"`_?<B\+C)%3 P jMF  FPg2"J5' cd  s?@r  ;\5/ %3%bk#1EV%)X #5L;9\Q )% 1R {6% H;+)9 u-5 ;y3{; 2 q#wd='}dzK1PX@ Z&#-*: KPX@M  Zb ZUM= N =M= L  >K%PX@K  Zb Z UU N =M= L  >K(PX@Od  Zb Z UU N =M= L  >K1PX@Md  Zb Z U  UUM= L  >@Sd  Zb` Z U  UUM= L  >YYYYY@.! a^UTSPMLGFC>741/,+%$ d!d  +2'>54#"'6%2#"75626762"'.+;26&#!.7>54&'.7!272'.+"7D+\B;@^'%C OjMF  FPg1"J5' cd  s?@r  ;\50 %3%bj#0Z;=L3# X)%TE - >)!1R {6% H;+)9 u-5 ;y3{; 2 q#wd='}ud"K%PX@ Z&#-*: KPX@M  Zb ZUM= N =M= L  >K%PX@K  Zb Z UU N =M= L  >K(PX@Q  Zb` Z UU N =M= L  >@O  Zb` Z U  UUM= L  >YYYY@.! a^UTSPMLGFC>741/,+%$ d!d  +2#".546%2#"=626762"'.+;26&#!&47>54&'&47!272'.+"`^?< B[+D'% D HjL FF Pf1#J5' bes??s;]5/ %3%bk#1FV%)X #5L;9]- >)!1R {6% H;+)9 u-5 ;y3{; 2 q#wd='}?b:{@ 8%@$bM=M= >Y@75)'"!  +2'>54#"54#"&554&'&7672>76'&4&#"7D+\B;@^]+AXG/V ' CA-'1@oxZ;=L3# X)%TE94*V1',#1K5kyAd^##fiV ?b:{@ 8%@$bM=M= >Y@75)'"!  +2#".546#"&554&'&7672>76'&4&#"R`^@;B\+DG+AXG/V ' CA-'1@oxFV%)X #5L;9]94*V1',#1K5kyAd^##fiV ?b I@ G4<>=9K PX@%ZM =M= >K%PX@&bM =M= >@*b=M =M= >YY@FD8610%#  +2'>54#"56&54632'"'#"&554&'&7672>76'&4&#"7C+\B;@^G+!5 P Y+AXG/V ' CA-'1@oxZ;=L3# X)%TER )% .94*V1',#1K5kyAd^##fiV ?b I@ G4<>=9K PX@%ZM =M= >K%PX@&bM =M= >@*b=M =M= >YY@FD8610%#  +2#".546&54632'"'#"&554&'&7672>76'&4&#"`^?< B[+D)%3 P M+AXG/V ' CA-'1@oxEV%)X "5L;9\R )% 494*V1',#1K5kyAd^##fiV ?bH@ F3<=<9K PX@&ZM  =M= >K%PX@'bM  =M= >K1PX@+b =M =M= >@1b` =M =M= >YYY@EC750/$"  +2'>54#"54%2#"=6#"&554&'&7672>76'&4&#"7D+\A;?^'%D +AXG/V ' CA-'1@oxZ;=L3# X)%TE - >)94*V1',#1K5kyAd^##fiV ?bH@ F3<=<9K PX@&ZM  =M= >K#PX@'bM  =M= >@-b`M  =M= >YY@EC750/$"  +2#".546%2"=6#"&554&'&7672>76'&4&#" `^?< B[+D'$ D +AXG/V ' CA-'1@oxEV%)X "5L;9\- =)94*V1',#1K5kyAd^##fiV ?bH$M@ K8 <:BA9K PX@4ZU UM  = M = >@5bU UM  = M = >Y@ JH<:54)'" $$  +2'>54#"'67267#"'&"'632#"&554&'&7672>76'&4&#"u7D+\A ;@^#+5)/THM+5-uBS@+AXG/V ' CA-'1@oxHZ<=L3# X)%TF/F<3/:@394*V1',#1K5kyAd^##fiV ?bF$M@ K8 <:BA9K PX@4ZU UM  = M = >@5bU UM  = M = >Y@ JH<:54)'$$  +267#"'&"'6322#".546#"&554&'&7672>76'&4&#"#+5)/THM+5-uBS@T`^@;B\+Dj+AXG/V ' CA-'1@ox/F<3/:@3FV%)X #6K<9] 94*V1',#1K5kyAd^##fiV i K(PX@6bU  S K = K >@4bU U  S K >YY@(ihcb_[XWNMJFCB=<763/,+"!  +2'>54#"544.'&47327&#"&47>5!&#".7>54.'.7327!'7D+\A;?^t314313313323313313313313zZ<=L3# X)%TFd=L#  2  1  #K>=L#  2  1  #K>m=L# 63  #K>5=L#  2  1  #K>i K(PX@6bU  S K = K >@4bU U  S K >YY@(ihcb_[XWNMJFCB=<763/,+"!  +2#".5464.'&47327&#"&47>5!&#".7>54.'.7327!'`^@;B\+DK314313313323313313313313zFV%)X #6K<9]b=L#  2  1  #K>=L#  2  1  #K>m=L# 63  #K>5=L#  2  1  #K>j x} KPX@:b  SM=K =  K   >K%PX@8bU  SK =  K   >K(PX@<dbU  SK =  K   >@:dbUU  S  K   >YYYY@,xwrqnjgf]\YURQLKFEB>;:10-)&%  +2'>54#"54&54632'"'4.'&47327&#"&47>5!&#".7>54.'.7327!#7D+\A ;@^G+!5 P 314313313323314313313323zZ;=L3# X)%TEQ )% )=L#  2  1  #K>=L# 6 1  #K>m=L#  2 3  #K>5=L#  2  1  #K>V x@  KPX@:b  SM=K =  K   >K%PX@8bU  SK =  K   >K(PX@<dbU  SK =  K   >@:dbUU  S  K   >YYYY@,xwrqnjgf]\YURQLKFEB>;:10-)&%  +2#".546&'4632'"'4.'&47327&#"&47>5!&#"&47>54.'&47327!`^@;B\+D)%3 P 313323314313413323314314{EV%)X #5L;9\Q )% )=L#  2  1  #K>=L# 6 1  #K>m=L#  2 3  #K>5=L#  2  1  #K>ZwѶ KPX@;b  SM=K =  K   >K%PX@9bU  SK =  K   >K(PX@=dbU  SK =  K   >K1PX@;dbUU  S  K   >@Adb`UU  S  K   >YYYYY@0wvqpmife\[XTQPKJEDA=:90/,(%$  +2'>54#"54%2#"=64.'&47327&#"&47>5!&#"&47>54.'&47327!7D+\B;@^'%D N413323323314313323314313{Z;=L3# X)%TE - >)d=L#  2  1  #K>=L# 6 1  #K>m=L#  2 3  #K>5=L#  2  1  #K>Fw KPX@;b  SM=K =  K   >K%PX@9bU  SK =  K   >K(PX@?b`U  SK =  K   >@=b`UU  S  K   >YYYY@0wvqpmife\[XTQPKJEDA=:90/,(%$  +2#".546%2#"7564.'.7327&#".7>5!&#"&47>54.'&47327!`_?<B\+C'% C /313313313313314314413323{FV%)X #5L;9]- >)b=L#  2  1  #K>=L# 6 1  #K>m=L#  2 3  #K>5=L#  2  1  #K>%$|e@ <:K PX@FUU  SM =K =  K  >K(PX@>IUU  SK =  K  >K1PX@<IUUU  S  K  >@=UUUU  S  K  >YYY@2|{vurnkja`]YVUPOJIFB?>541-*)" $$  +2'>54"547267#"'&"'6324.'&47327&#"&47>5!&#"&47>54.'&47327!7D+\B;@^#+5)/THN+5-tBT?>313323314313413323323314{Z;=L3# Y)%TF/F;3/9?4=L#  2  1  #K>=L# 4  1  #K>m=L#  2  1  #K>5=L#  2  1  #K>9$|@ <:K PX@MZUU  S M = K =  K  >KPX@NbUU  S M = K =  K  >K(PX@FbIUU  S K =  K  >K*PX@DbIU UU  S  K  >@EbUU UU  S  K  >YYYY@4|{vurnkja`]YVUPOJIFB?>541-*)$$  +267#"'&"'6322#".5464.'&47327&#"&47>5!&#".7>54.'.7327!+#+6)/THM+5-uBT?T`^?;B[+D314314413323313313313313{f0E;3/9?4EV%)X #5L;9\=L#  2  1  #K>=L# 6 1  #K>m=L#  2 3  #K>5=L#  2  1  #K>G-i@ @bU= >Y@*)  +2'>54"54327#"'&54&'.7$727D+\A;?^=3'F%%!RfD=/VF&/Z;=L3# Y)%TFHA-E95}V- 2 +#seG-{@ @&bbM== >Y@*)  +2#".546327#"'&54&'.7$72`_?<B\+D3'F%%!RfD=/VF&/FV$)X #6L;9\HA-E95}V- 2 +#se <@ &(K%PX@(bbM== >@,bb=M== >YY@98,*%#  +2'>54#"54&'4632'"'327#"'&54&'.7$72\7D+]A;@^H+!5 P 3'F%%!RfD=/VF&/Z;=L3# X)%TER )% HA-E95}V- 2 +#se <@ &(K%PX@(bbM== >@,bb=M== >YY@98,*%#  +2#".546&54632'"'327#"'&54&'.7$72f`_?<B\+C)%3 O  3'F%%!RfD=/VF&/EV%)X #5L;9\R )% HA-E95}V- 2 +#se;@ %'K%PX@)bbM == >K1PX@-bb =M== >@3b`b =M== >YYY@87+)$"  +2'>54#"54%2#"=6327#"'&54&'.7$72X7D+]A;@^'%D ^3'F%%!RfD=/VF&/Z;=L3# X)%TE - >)hHA-E95}V- 2 +#se;@ %'K#PX@)bbM == >@/b`bM == >YY@87+)$"  +2#".546%2"756327#"'&54&'.7$72h`_?<B\+C'% C E3'F%%!RfD=/VF&/EV%)X #5L;9\ . >)lHA-E95}V- 2 +#seF$@@ * ,<:K PX@6 Z  bU UM  = = >@7  b  bU UM  = = >Y@=<0.)'" $$  +2'>54#"'67267#"'&"'632327#"'&54&'.7$727C+\B;@^#+5)/THN+5-uBS@h3'F%%!RfD=/VF&/FZ<=L3# X)%TF/F<3/:@33HA-E95}V- 2 +#seF$@@ * ,<:K PX@6 Z  bU UM  = = >@7  b  bU UM  = = >Y@=<0.)'$$  +267#"'&"'6322#".546327#"'&54&'.7$72#*5)/THN+5-uBS@T`^@;B\+D3'F%%!RfD=/VF&//F<3/:@3FV%)X #6K<9]HA-E95}V- 2 +#sey; K(PX@(b UK =K>@&b UUK>YY@763/,+"!  +2'>54#"'64.'.7327&#".7>57D+\B;@^313313313313Z<=K4# X)%TFb=L#  2  1  #K>=L# 63  #K>T; K(PX@(b UK =K>@&b UUK>YY@763/,+"!  +2#".5464.'&47327&#"&47>5`^@;B\+D313323314313FV%)X #6K<9]b=L#  2  1  #K>=L# 65  #K> J! KPX@,bM =K =  K  >K(PX@*b UK =  K  >K*PX@(b UU  K  >@,db UU  K  >YYYY@FEB>;:10-)&%  +2'>54#"'6&54632'"'4.'&47327&#"&47>57D+\B;@^G+!5 P P313323314313[;=L3# X)%TFR )% +=L#  2  1  #K>=L#  2 3  #K> J! KPX@,bM =K =  K  >K(PX@*b UK =  K  >K*PX@(b UU  K  >@,db UU  K  >YYYY@FEB>;:10-)&%  +2#".546&54632'"'4.'.7327&#".7>5`^?;B[+D)%3 P <313313313313FV%)X #5L;9]R )% -=L#  2  1  #K>=L#  2 3  #K>Ig KPX@-bM  =K =  K  >K(PX@+b  UK =  K  >K*PX@)b  UU  K  >K1PX@- db UU  K  >@3 db` UU  K  >YYYYY@ EDA=:90/,(%$  +2'>54#"54%2#"=64.'&47327&#"&47>57D+]A;@^'%D 413323314314[;=L3# X)%TF - >)^=L#  2  1  #K>=L#  2 3  #K>Ih K PX@,ZM  =K =  K  >KPX@-bM  =K =  K  >K%PX@+b  UK =  K  >K(PX@1b`  UK =  K  >@/b`  UU  K  >YYYYY@ EDA=:90/,(%$  +2#".546%2#"7564.'&47327&#"&47>5`_?<B\+C'% C 413323314314FV%)X #5L<9]- >)`=L#  2  1  #K>=L#  2 3  #K>sw$N>@ <:K PX@: ZU UM= K =  K  >KPX@;  bU UM= K =  K  >K(PX@9  bUU U K =  K  >@7  bUU U U  K  >YYY@$JIFB?>541-*)" $$  +2'>54#"547267#"'&"'6324.'.7327&#".7>57D+\B;@^#+5)/THN+5-tBT?313313313313Z;=L3# X)%TE/E;3/9?3=L#  2  1  #K>=L#  2  1  #K>$N@ <:K PX@= ZUU M  = K =  K  >KPX@>  bUU M  = K =  K  >K#PX@8  b IUU K =  K  >K(PX@9  bU UU K =  K  >@7  bU U UU  K  >YYYY@$JIFB?>541-*)$$  +267#"'&"'6322#".5464.'.7327&#".7>5'#+5)/THM+5-uBS@T`^@;B\+D^313323314313h0E;3/9?4EV%)X #5L;9\=L#  2  1  #K>=L#  2 3  #K>L*l @UM=M >Y@)'"   +2'>54"54"32654&4762'"$7D+\B;@^yqDfp Z;=L3# Y)%TFq́}uL*~ @)bM=M=M >Y@)'"   +2#".546"32654&4762'"$`^@;B\+DYyqDfp FV$)X #6L;9\q́}uL +9 K*PX@+bM = M=M >K,PX@/b=M = M=M >@-b U= M=M >YYY@"!861/'%!+"+  +2'>54"54&54632#"'"32654&4762'"$;7D+\B;@^H+ 5 P yqDfp Z<=K4# Y)%TFR (% q́}uL +9 K*PX@+bM = M=M >K,PX@/b=M = M=M >@-b U= M=M >YYY@"!861/'%!+"+  +2#".546&54632#"'"32654&4762'"$J`^@;B\+D)$3 P yqDfp FV%)X #6K<9]R (% q́}uL*84 K#PX@,bM  = M=M >K*PX@*b  U M=M >K1PX@.b U = M=M >@4b` U = M=M >YYYY@ ! 750.&$ *!*  +2'>54#"54%2#"=6"32654&4762'"$=7D+\B;@^'$D OyqDfp yZ<=K4# X)%TF - =)q́}uL*8 K%PX@,bM  = M=N >K(PX@2b`M  = M=N >@0b`  U M=N >YYY@ ! 750.&$ *!*  +2#".546%2#"=6"32654&4762'"$J`^@;B\+D'% D cyqDfp }FV%)X #6K<9]- =)q́}u,z @'bUM =M >Y@+)#!  +2'>54#"54"32% 76$3 #7D+\A ;@^V㮚s[Z<=L3# X)%TF3-Jӿh% lz,z @'bUM =M >Y@+)#!  +2#".546"32% 76$3 `_?<B\+CZ䮚r[FV%)X #6K<9]3-Jӿh% lz ,; KPX@+bM = M =M >K%PX@)b U M =M >@-db U M =M >YYY@"!:820(&!,",  +2'>54#"54&54632'"'%"32% 76$3 7D+\B;@^H+ 5 P ē䮚r[Z;=L3# X)%TEQ )% 3-Jӿh% lz/ ,; KPX@+bM = M =M >K%PX@)b U M =M >@-db U M =M >YYY@"!:820(&!,",  +2#".546&54632'"'%"32% 76$3 `^?< B[+D)%3 P 擻㮚s[EV%)X #5L;9\Q )% 3-Jӿh% lz+:4 KPX@,bM  = M =M >K*PX@*b  U M =M >K1PX@. db U M =M >@4 db` U M =M >YYYY@ ! 971/'% +!+  +2'>54#"'6%2#"756"32% 76$3 7D+\A ;@^'%C 㮚r[[;=L3# X)%TF - >)3-Jӿh% lz+: KPX@,bM  = M =M >K%PX@*b  U M =M >@0b`  U M =M >YYY@ ! 971/'% +!+  +2#".546%2#"756"32% 76$3 `_?<B\+C'% C ۓ㮚s[FV%)X #5L;9]- >)3-Jӿh% lz7'Cc@ %@U=N >Y@A?20)'  +2'>54"54#"&'&547>54'&'&'>32327654'&'&'&546327D+\A ;@^{}B l'3qVbNL#<7!8-9PZ;=L3# Y)%TF)XfB% (?\uwxw473Ec7'Cu@ %@$bM==N >Y@A?20)'  +2#".546#"&'&547>54'&'&'>32327654'&'&'&54632`^?< B[+DX{}B l'3qVbNL#<7!8-9PFV$)X #6L;9\})XfB% (?\uwxw473Ec7' R@ 4K*PX@&bM ==N >K,PX@*b=M ==N >@(b U==N >YYY@PNA?86%#  +2'>54"54&54632#"'#"&'&547>54'&'&'>32327654'&'&'&54632P7D+\A ;@^G+!5 P H{}B l'3qVbNL#<7!8-9PZ<=K4# Y)%TFR (% )XfB% (?\uwxw473Ec7' R@ 4K*PX@&bM ==N >K,PX@*b=M ==N >@(b U==N >YYY@PNA?86%#  +2#".546&54632#"'#"&'&547>54'&'&'>32327654'&'&'&54632^`^?< B[+D)%3 P H{}B l'3qVbNL#<7!8-9PFV%)X #6K<9]R (% )XfB% (?\uwxw473Ec7'Q@  3K#PX@'bM  ==N >K*PX@%b  U=N >K1PX@)b U ==N >@/b` U ==N >YYYY@OM@>75$"  +2'>54#"54%2#"=6#"&'&547>54'&'&'>32327654'&'&'&54632R7D+\A;?^'%D {}B l'3qVbNL#<7!8-9PyZ<=K4# X)%TF - =){)XfB% (?\uwxw473Ec7'Q@  3K%PX@'bM  ==N >K(PX@-b`M  ==N >@+b`  U=N >YYY@OM@>75$"  +2#".546%2#"=6#"&'&547>54'&'&'>32327654'&'&'&54632^`^?< B[+D'$ D {}B l'3qVbNL#<7!8-9P}FV%)X #6K<9]- =){)XfB% (?\uwxw473Ec7'J$V@ 8 <:K PX@4ZU UM  = = N >@5bU UM  = = N >Y@ TREC<:)'" $$  +2'>54#"547267#"'&"'632#"&'&547>54'&'&'>32327654'&'&'&546327D+\B;@^#*5)/THN+5-uBS@{}B l'3qVbNL#<7!8-9PJZ<=L3# X)%TF/F<3/:@3+)XfB% (?\uwxw473Ec7'B$V@ 8 <:K PX@4ZU UM  = = N >@5bU UM  = = N >Y@ TREC<:)'$$  +267#"'.'622#".546#"&'&547>54'&'&'>32327654'&'&'&54632{#+5)/THM+5-uBS@T`^@;B\+Db{}B l'3qVbNL#<7!8-9P/F;3/:?3FV%)X #5L<9])XfB% (?\uwxw473Ec N K(PX@)b UK  =K>@'b U UK>YY@EA>=541-   +2#".546&#".7>54&'.'.732776&'&47327!`^@;B\+D=Fy E0KE  {Z  R%:H @Dz JF"#FV%)X #6K<9]-{965 9{70/R0 2  1 59-'-3 2  1 #8J:qP ]-@   KPX@-  bM =  K  =K>K(PX@+  b  U  K  =K>K*PX@)  b  U    UK>@-d  b  U    UK>YYYY@TPMLDC@54&'.'&4732776&'&47327`^@;B\+D)%3 P E x F0KE {ZR%9H?D{JG!#FV%)X #5L;9]R )% {965 9{70/R0 2  1 59-'-3 2  1 #8J:qP\<  KPX@.  bM  =  K  =K>K%PX@,  b   U  K  =K>K(PX@2  b `   U  K  =K>@0  b `   U    UK>YYYY@"SOLKCB?;.-*&#"  +2#".546%2#"756&#"&47>54&'.'&4732776&'&47327`^?<B\+C'% C +E x F0KE {ZR%9H?D{JG!#FV%)X #5L;9]- >)-{965 9{70/R0 2  1 59-'-3 2  1 #8J:q$a@  <:K PX@>Z UU M =  K  = K>KPX@?b UU M =  K  = K>K%PX@9bI UU  K  = K>K(PX@:b U UU  K  = K>@8b U U    UU K>YYYY@&XTQPHGD@32/+('$$  +267#"'&"'6322#".546&#"&47>54&'.'.732776&'&47327/#+6)/THM+6-uBT?T`^?< B[+DE xF0JE {Z R%:H ?D{ JF"#f0E;3/9?4EV%)X #5L;9\{965 9{70/R0 2  1 59-'-3 2  1 #8J:qT?@ :!2)@'b`M=N >Y@53/-(&  +2'>54#"54#"'#"&547327&5463272'677D+\B;@^gͲVZ)FdDm=)99u1Bl''ZiNZ<=L3# X)%TF󁁴Ϙ;d-#sw5Hqo` #.`jT?@ :!2)@'b`M=N >Y@53/-(&  +2#".546#"'#"&547327&5463272'67`^@;B\+D-ͲVZ)FdDm=)99u1Bl''ZiNFV$)X #6L;9\%󁁴Ϙ;d-#sw5Hqo` #.`jT N@ I0.A8&K*PX@)b`M = N >K,PX@-b`=M = N >@+b` U= N >YYY@DB><75)'%#  +2'>54"54&54632#"'#"'#"&547327&5463272'677D+\B;@^H+!5 P ͲVZ)FdDm=)99u1Bl''ZiNZ<=K4# Y)%TFR (% 󁁴Ϙ;d-#sw5Hqo` #.`jT N@ I0.A8&K*PX@)b`M = N >K,PX@-b`=M = N >@+b` U= N >YYY@DB><75)'%#  +2#".546&54632#"'#"'#"&547327&5463272'67#`^@;B\+D)%3 P ͲVZ)FdDm=)99u1Bl''ZiNFV%)X #6K<9]R (% 󁁴Ϙ;d-#sw5Hqo` #.`jTMQK1PX@ H/-@7%<@ H/-@7%K#PX@*b`M  = N >K*PX@(b`  U N >K1PX@,b` U = N >@2b`` U = N >YYYY@CA=;64(&$"  +2'>54#"56%2#"756#"'#"&547327&5463272'677C+\B;@^'$C ͲVZ)FdDm=)99u1Bl''ZiNyZ<=K4# X)%TF - =)#󁁴Ϙ;d-#sw5Hqo` #.`jTM K%PX@ H/-@7%<@ H/-@7%K%PX@*b`M  = N >K(PX@0b``M  = N >@.b``  U N >YYY@CA=;64(&$"  +2#".546%2#"=6#"'#"&547327&5463272'67#`^@;B\+D'% D qͲVZ)FdDm=)99u1Bl''ZiN}FV%)X #6K<9]- =)#󁁴Ϙ;d-#sw5Hqo` #.`jT=$R@ M42 E<* <:K PX@7 Z  `U UM  =  N >@8  b  `U UM  =  N >Y@"HFB@;9-+)'" $$  +2'>54#"547267#"'.'632#"'#"&547327&5463272'677D+\B;@^#+5)/THN+5-tBT?ͲVZ)FdDm=)99u1Bl''ZiN=Z;=L3# X)%TE/E;309?3󁁴Ϙ;d-#sw5Hqo` #.`jT?$R@ M42 E<* <:K PX@7 Z  `U UM  =  N >@8  b  `U UM  =  N >Y@"HFB@;9-+)'$$  +267#"'.'622#".546#"'#"&547327&5463272'67B#*5)/THN+5-uBS@T`^@;B\+D5ͲVZ)FdDm=)99u1Bl''ZiN/E;3/:?3FV%)X #5L<9]`󁁴Ϙ;d-#sw5Hqo` #.`jJG@ C=)@1b ` UM = M >Y@ EDA?64-+('%#GG  +2'>54#"543'&54>3 32672!"7>54&#"#!&'63f7D+\B;@^\)i F^!/- L-%TfHV!%L -/!^Z<=L3# X)%TFl?fɠe>lZhd!'NƴHk\!dhZG@ C=)@1b ` UM = M >Y@ EDA?64-+('%#GG  +2#".5463'&54>3 32672!"7>54&#"#!&'63{`^@;B\+D])h F^!/- L-%TfHV!%L -/!^FV%)X #6K<9]l?fɠe>lZhd!'NƴHk\!dhZ} V&@  RL8#KPX@5  b  `M = M = M   >K%PX@3  b  `  U M = M   >@7d  b  `  U M = M   >YYY@$"!TSPNEC<:7642,*!V"V  +2'>54#"54&54632'"'3'&54>3 32672!"7>54&#"#!&'637D+\B;@^H+ 5 P \)h F^!/- L-%TfHV!%L -/!^Z;=L3# X)%TEQ )% Nl?fɠe>lZhd!'NƴHk\!dhZZ V&@  RL8#KPX@5  b  `M = M = M   >K%PX@3  b  `  U M = M   >@7d  b  `  U M = M   >YYY@$"!TSPNEC<:7642,*!V"V  +2#".546&54632'"'3'&54>3 32672!"7>54&#"#!&'63`^?;B[+D)%3 P ])h F^!/- L-%TfHV $L -0!]EV%)X #5L;9\Q )% Nl?fɠe>lZhd!'NƴHk\!dhZUv@  QK7"KPX@6  b  `M = M = M   >K*PX@4  b  `  U M = M   >K1PX@8d  b  `  U M = M   >@>d  b `  `  U M = M   >YYYY@(! SROMDB;96531+) U!U  +2'>54#"'6%2#"7563'&54>3 32672!"7>54&#"#!&'637D+\B;@^'%C t\)h F^!/- L-%TfGV %L -0!][;=L3# X)%TF - >)l?fɠe>lZhd!'NƴHk\!dhZU1@  QK7"KPX@6  b  `M = M = M   >K%PX@4  b  `  U M = M   >@:  b `  `  U M = M   >YYY@(! SROMDB;96531+) U!U  +2#".546%2#"=63'&54>3 32672!"7>54&#"#!&'63`^@;B\+D'% C v\)i F]!/- L-%TfGV!%L -/!]FV%)X #5L;9]- >)l?fɠe>lZhd!'NƴHk\!dhZ$Z+@ VP<' <:K PX@E Z  `UU M = M =  M   >KPX@F  b  `UU M = M =  M   >@A  b  `IUU M =  M   >YY@,&%XWTRIG@>;:860.%Z&Z" $$  +2'>54#"'67267#"'&"'6323'&54>3 32672!"7>54&#"#!&'637D+\A ;@^#+5)/THM+5-uBS@\)i F^!/- L-%TfHV!%L -/!^Z;=L3" Y)%TE0E;3/9?4@l?fɠe>lZhd!'NƴHk\!dhZ-$Z+@ VP<' <:K PX@E Z  `UU M = M =  M   >KPX@F  b  `UU M = M =  M   >@A  b  `IUU M =  M   >YY@,&%XWTRIG@>;:860.%Z&Z$$  +267#"'&"'6322#".5463'&54>3 32672!"7>54&#"#!&'63?#+5)/THN+5-tBT?S`^@;B\+DO])h F^!/- L-%TfHV $L -/!^h0E;3/9?4EV%)X #5L;9\l?fɠe>lZhd!'NƴHk\!dhZJ!m6GK*PX@ 7*%#<@ 7*%#K*PX@-ddM =N =N >@1dd=M =N =N >YY@EC<:.,(&" 66*! +6720#"/&542327327#"&'#"'&54>'.#"3267{   嘑'N';cIU* ^w;MXqtAi1&wG']+#cG9[;b   &H @I5!YQkCVodoM#/FD;S\J!u1BK*PX@2% <@2% K*PX@. ddM =N =N >@2 dd=M =N =N >YY@@>75)'#!11  +2#"&54762327327#"&'#"'&54>'.#"3267%l 0g B'N';cIU* ^w;MXqtAi1&wG']+#cG9[;u1  %# @I5!YQkCVodoM#/FD;S\Z;mGfK*PX@ '  <@ '  KPX@4bb   U =M=N >K*PX@1ddb   UM=N >K1PX@6ddb  I  UM=N >@7ddb  U  UM=N >YYYY@FDCA!#"#-$(*! +6720#"/&54327#"&54>7.54632#"'&#"32632#"&#"F   GZ>w#ˬ'O8RJaT&+0o?4TfhR?Ib   &TVd1`#A18!XEqVCREP\\ /7 \Z;qBpK*PX@ "  <@ "  KPX@5bb   U =M=M >K*PX@2 ddb   UM=M >K1PX@7 ddb  I  UM=M >@8 ddb  U  UM=M >YYYY@A?><:87520.,)'  +2#"&5476327#"&54>7.54632#"'&#"32632#"&#"%l  /f eZ>w#ˬ'O8RJaT&+0o?4TfhR?Iq2  %#TVd1`#A18!XEqVCREP\\ /7 \?bw;`@ 9&<0/9K!PX@b=M= >@ddM= >Y,%((!+632#"/&54#"&554&'&7672>76'&4&#"   +AXG/V ' CA-'1@oxm   ' 94*V1',#1K5kyAd^##fiV ?bs8i@ 6#<-,9KPX@b=M= >@ddM= >Y@53'%   +2#"&5476#"&554&'&7672>76'&4&#"%l  /f +AXG/V ' CA-'1@oxs1  %#94*V1',#1K5kyAd^##fiV Gq0a@ @ddb= >Y%(*!+6720#"/&54327#"'&54&'.7$72   }3'F%%!RfD=/VF&/f   &HA-E95}V- 2 +#seGw+i@ @ddb= >Y@('  +2#"&5476327#"'&54&'.7$72%l  /f p3'F%%!RfD=/VF&/w1  %#HA-E95}V- 2 +#seLd-h @ ddM=M >Y@,*%#*! +6320#"/&5&"32654&4762'"$j   ӪyqDfp Z   &q́}uLu(iKPX@$b=M=M >@!ddM=M >Y@'%   +2#"&5476"32654&4762'"$5%m 0f yqDfp u1  %#q́}u7'`F^@ (@dd=N >Y@ DB53,*(*! +6320#"/&54#"&'&547>54'&'&'>32327654'&'&'&54632   {}B l'3qVbNL#<7!8-9PV   ')XfB% (?\uwxw473Ec7'uAb#@dd=N >Y@?=0.'%  +2#"&5476#"&'&547>54'&'&'>32327654'&'&'&54632o%l  /f {}B l'3qVbNL#<7!8-9Pu1  %#)XfB% (?\uwxw473EcT\Bh@ =$"5,@dddN >Y@ $%,"(*!+6320#"/&54#"'#"&547327&5463272'67'   ͲVZ)FdDm=)99u1Bl''ZiNR   's󁁴Ϙ;d-#sw5Hqo` #.`jTy=q@80'@dddN >Y@31-+&$  +2#"&5476#"'#"&547327&5463272'67%l  /f ͲVZ)FdDm=)99u1Bl''ZiNy1  %#/󁁴Ϙ;d-#sw5Hqo` #.`jJ!#EV\K*PX@, F942'  <@,F942'  K*PX@Jb b `M = M= N  = M  =>@Nb b `M == M= N  = M  =>YY@!%$TRKI=;751/+)$E%E!##%$ +73276#"&52'>54#"'62327327#"&'#"'&54>'.#"3267m-D;+ ;H9X7D+\A ;@^'N';cIU* ^w;MXqtAi1&wG']+#cG9[;T1G VR[;=L3# X)%TF @I5!YQkCVodoM#/FD;S\J!#EV\K*PX@, F942'  <@,F942'  K*PX@Jb b `M = M= N  = M  =>@Nb b `M == M= N  = M  =>YY@!%$TRKI=;751/+)$E%E##%$ +73276#"&52#".5462327327#"&'#"'&54>'.#"3267m-D;+ ;H9Xj`^?;B[+D'N';cIU* ^w;MXqtAi1&wG']+#cG9[;T1G VRFV%)X #5L;9] @I5!YQkCVodoM#/FD;S\J!#2TejK*PX@; UHCA6  <@; UHCA6  K*PX@Lb b `M= M = N  = M  =>@Tb b `=M= = M= N  = M  =>YY@%43caZXLJFD@>:83T4T1/*(!##%$ +73276#"&52'>54#"54&54632'"'2327327#"&'#"'&54>'.#"3267m-D;+ ;H9XB7D,\A;?^G+!5 O 'N';cIU* ^w;MXqtAi1&wG']+#cG9[;T1G VR[;=L3# X)%TFR )%  @I5!YQkCVodoM#/FD;S\J!#2TeK*PX@; UHCA6  <@; UHCA6  K%PX@Lb b `M= M = N  = M  =>K*PX@Pb b `=M= M = N  = M  =>@Tb b `=M= = M= N  = M  =>YYY@%43caZXLJFD@>:83T4T1/*(##%$ +73276#"&52#".546&'4632'"'2327327#"&'#"'&54>'.#"3267m-D;+ ;H9X+`^@;B\+D)%3 P 'N';cIU* ^w;MXqtAi1&wG']+#cG9[;T1G VREV%)X #5L;9\R )%  @I5!YQkCVodoM#/FD;S\J!3DVdK*PX@bN 4'"  <@bN 4'"  K*PX@M  bb` M  =M=N = N =>K1PX@U  bb` = M ==M=N = N =>@[  b `b` = M ==M=N = N =>YYY@)XWFE`^WdXdTRMLEVFVB@97+)%#33%$ +73276#"&52327327#"&'#"'&54>'.#"32672'>54#"54%2#"=6m-D;+ ;H9Xʘ'N';cIU* ^w;MXqtAi1&wG']+#cG9[;X7D+]A;@^'%D T1G VR̃ @I5!YQkCVodoM#/FD;S\>[;=L3# X)%TF - >)J!#1SdK*PX@/: TGB@5  <@/: TGB@5  K%PX@Mb b `M= M = N  = N  =>K*PX@Sb` b `M= M = N  = N  =>@Wb` b `M= = M= N  = N  =>YYY@)32%$b`YWKIEC?=972S3S-+$1%1##%$ +73276#"&52#".546%2#"=62327327#"&'#"'&54>'.#"3267m-D;+ ;H9X`^@;B\+D'% D F'N';cIU* ^w;MXqtAi1&wG']+#cG9[;T1G VREV%)X #5L;9\ - >) @I5!YQkCVodoM#/FD;S\J!H!2DWiK*PX@)I RQ < "ZYXaK(PX@[ bb` U  U M =M=N =M =>K*PX@Z bb`c U  U M =M=N =M >@^ bb`c U  U M ==M=N =M >YYY@0FE43ge`_^\USONLJEWFWB@;:3D4D0.'% !! +2327327#"&'#"'&54>'.#"32672'>54#"547267#"'&"'63273276#"&57'N';cIU* ^w;MXqtAi1&wG']+#cG9[;7D+\B;@^#*5)/THN+5-uBS@-D;+ ;H9X @I5!YQkCVodoM#/FD;S\Z<=L3# X)%TF/F<3/:@3T1H VRJ!F$6XiK*PX@). ? YLGE:  <:@). ? YLGE:  <:YK PX@Z   Z b `UUM = M  = N = M =>K*PX@[   b b `UUM = M  = N = M =>@_   b b `UUM = = M = N = M =>YY@-87&%ge^\PNJHDB><7X8X0/*(%6&6" $$%$ +73276#"&5267#"'&"'6322#".5462327327#"&'#"'&54>'.#"3267m-D;+ ;H9X#+5)/THN+5-tBT?T`_?<B\+C'N';cIU* ^w;MXqtAi1&wG']+#cG9[;T1G VR/F<3/:@3FV%)X #6K<9]? @I5!YQkCVodoM#/FD;S\!(a@A  @:bUS  U  K =M >Y@$#"_]VUTROKGF@?73%$"(#(!!$% +7327#"&52'>54#"54!#&#"&47>7272&#"&472>&'!"!9'- PNd7D+\B;@^X71x) 5[l)65  N!(a@A  @:bUS  U  K =M >Y@$#"_]VUTROKGF@?73%$"(#(!!$% +7327#"&52#".546!#&#"&47>7272&#"&472>&'!"!9'- PNd`^?< B[+D771x) 5[l)65  N5!07p[@P  KPX@>  b  SUM=  K  =M >K*PX@<  b U  SU  K  =M >@F  b U  SU K  =  K  =M >YYY@(21nledca^ZVUONFB431727/-(&!!$% +7327#"&52'>54#"54&54632#"'!#&#"&47>7272&#"&472>&'!"h!9'- PNe7D+\A ;@^G+!5 P 71x) 5[l)65  N!07p\@P$  KPX@>  b  SUM=  K  =M >K*PX@<  b U  SU  K  =M >@F  b U  SU K  =  K  =M >YYY@(21nledca^ZVUONFB431727/-(&!!$% +7327#"&52#".546&'4632'"'!#&#"&47>7272&#"&472>&'!"%!9'- PNd`_?<B\+C)%3 P 7 1 x)5[l)65  N!/6o@O-   Z  SUM=  K  =M >KPX@?  b  SUM=  K  =M >K*PX@=  b U  SU  K  =M >K1PX@BX  b V  SU  K  =M >@HX  b ` V  SU  K  =M >YYYY@,10#"mkdcb`]YUTNMEA320616+)"/#/!!$% +7327#"&52'>54#"54%2#"=6!#&#".7>7272&#"&472>&'!"5 9(- PNd7D+\B;@^'%D 81 y) 5;DL #A +  zǗ ' rz%m;#^JJdq[;=L3# X)%TF - >)u6 ^ 65 =>[l)65  N!/6o`@O-   Z  SUM=  K  =M >KPX@?  b  SUM=  K  =M >K%PX@=  b U  SU  K  =M >@C  b ` U  SU  K  =M >YYY@,10#"mkdcb`]YUTNMEA320616+)"/#/!!$% +7327#"&52#".546%2#"=6!#&#".7>7272&#"&472>&'!")!9'- PNd+`^@;B\+D'% D 81 y) 5;DL #A +  zǗ ' rz%m;#^JJdqFV%)X #5L;9]- >)y6 ^ 65 =>[l)65  Nw9!4;t@&T/.   <%:K PX@K ZU I   S  UM = K =M >KPX@L  bU I   S  UM = K =M >K#PX@J  bUU I   S  U K =M >@K  bU U U  S  U K =M >YYY@065#"rpihgeb^ZYSRJF875;6;20,+)'"4#4!!$% +7327#"&52'>54#"547267#"'&"'632!#&#"&47>7272&#"&472>&'!"m!9'- PNd&7D+\B;@^#*5)/THN+5-uBS@l71 x)5[l)65  N "4;tH@T,   <:K PX@K ZU I   S  UM = K =M >K PX@L  bU I   S  UM = K =M >@J  bUU I   S  U K =M >YY@065$#rpihgeb^ZYSRJF875;6;.-($4 ""$% +7327#"&5267#"'&"'6322#".546!#&#"&47>7272&#"&472>&'!"?!9'- PNe #+5)/THM+5-uBS@T`^@;B\+D71x) 5[l)65  N?b#L@J7  @@6bbM = M= =M>Y@IG;943(&!##%$ +73276#"&52'>54#"54#"&554&'&7672>76'&4&#"-C;+ K*PX@6bbM = M= =M>@3bbQM = M= >YY@IG;943(&##%$ +73276#"&52#".546#"&554&'&7672>76'&4&#"-C;+ @YF  OK%PX@8  bbM = M  = =M>K*PX@<  bb=M = M  = =M>@9  bbQ=M = M  = >YYY@XVJHCB751/*(!##%$ +73276#"&52'>54#"56&54632'"'#"&554&'&7672>76'&4&#"-C;+ 7C+\B;@^G+!5 P Y+AXG/V ' CA-'1@ox^T1H VRZ;=L3# X)%TER )% .94*V1',#1K5kyAd^##fiV ?b#2[>@YF  OK%PX@8  bbM = M  = =M>K*PX@<  bb=M = M  = =M>@9  bbQ=M = M  = >YYY@XVJHCB751/*(##%$ +73276#"&52#".546&54632'"'#"&554&'&7672>76'&4&#"-C;+ K%PX@9  bbM  = M  = =M>K*PX@=  bb =M = M  = =M>K1PX@:  bbQ =M = M  = >@@b  `bQ =M = M  = >YYYY@%$WUIGBA64-+$1%1!##%$ +73276#"&52'>54#"54%2#"=6#"&554&'&7672>76'&4&#"-C;+ )94*V1',#1K5kyAd^##fiV ?b#1ZK@/XE  NK#PX@9  bbM  = M  = =M>K*PX@?b  `bM  = M  = =M>@<b  `bQM  = M  = >YYY@%$WUIGBA64-,$1%1##%$ +73276#"&52#".546%2"=6#"&554&'&7672>76'&4&#"-C;+ @Gb   bUUM = M = = M  >Y@&][VUTRJH<:54)'" $$  +2'>54#"'67267#"'&"'632#"&554&'&7672>76'&4&#"73276#"&5u7D+\A ;@^#+5)/THM+5-uBS@+AXG/V ' CA-'1@ox-C;+ K*PX@G   b bUUM = M  = =M>@D   b bUUQM = M  = >YY@#&%\ZNLGF;90/*(%6&6" $$%$ +73276#"&5267#"'&"'6322#".546#"&554&'&7672>76'&4&#"-C;+ K(PX@@bU  S K = K =M >@>bU U  S K =M >YY@,wuqoihcb_[XWNMJFCB=<763/,+"!  +2'>54#"544.'&47327&#"&47>5!&#".7>54.'.7327!7327#"&5'7D+\A;?^t314313313323313313313313z!9'- PNdZ<=L3# X)%TFd=L#  2  1  #K>=L#  2  1  #K>m=L# 63  #K>5=L#  2  1  #K>;#^JJdqiy@ k tlj K(PX@@bU  S K = K =M >@>bU U  S K =M >YY@,wuqoihcb_[XWNMJFCB=<763/,+"!  +2#".5464.'&47327&#"&47>5!&#".7>54.'.7327!7327#"&5'`^@;B\+DK314313313323313313313313z 9(- PNdFV%)X #6K<9]b=L#  2  1  #K>=L#  2  1  #K>m=L# 63  #K>5=L#  2  1  #K>;#^JJdq = x@ z {y KPX@Db  SM=K =  K  =M >K%PX@BbU  SK =  K  =M >K(PX@FdbU  SK =  K  =M >@DdbUU  S  K  =M >YYYY@0~xwrqnjgf]\YURQLKFEB>;:10-)&%  +2'>54#"54&54632'"'4.'&47327&#"&47>5!&#".7>54.'.7327!7327#"&5#7D+\A ;@^G+!5 P 314313313323314313313323z!9'- PNdZ;=L3# X)%TEQ )% )=L#  2  1  #K>=L# 6 1  #K>m=L#  2 3  #K>5=L#  2  1  #K>;#^JJdq + x@ z {y KPX@Db  SM=K =  K  =M >K%PX@BbU  SK =  K  =M >K(PX@FdbU  SK =  K  =M >@DdbUU  S  K  =M >YYYY@0~xwrqnjgf]\YURQLKFEB>;:10-)&%  +2#".546&'4632'"'4.'&47327&#"&47>5!&#"&47>54.'&47327!7327#"&5`^@;B\+D)%3 P 313323314313413323314314{ 9(- PNdEV%)X #5L;9\Q )% )=L#  2  1  #K>=L# 6 1  #K>m=L#  2 3  #K>5=L#  2  1  #K>;#^JJdq Hw@ y zx KPX@Eb  SM=K =  K  =M >K%PX@CbU  SK =  K  =M >K(PX@GdbU  SK =  K  =M >K1PX@EdbUU  S  K  =M >@Kdb`UU  S  K  =M >YYYYY@4}wvqpmife\[XTQPKJEDA=:90/,(%$  +2'>54#"54%2#"=64.'&47327&#"&47>5!&#"&47>54.'&47327!7327#"&57D+\B;@^'%D N413323323314313323314313{!9'- PNdZ;=L3# X)%TE - >)d=L#  2  1  #K>=L# 6 1  #K>m=L#  2 3  #K>5=L#  2  1  #K>;#^JJdq -w@ y zx KPX@Eb  SM=K =  K  =M >K%PX@CbU  SK =  K  =M >K(PX@Ib`U  SK =  K  =M >@Gb`UU  S  K  =M >YYYY@4}wvqpmife\[XTQPKJEDA=:90/,(%$  +2#".546%2#"7564.'.7327&#".7>5!&#"&47>54.'&47327!7327#"&5`_?<B\+C'% C /313313313313314314413323{!9'- PNdFV%)X #5L;9]- >)b=L#  2  1  #K>=L# 6 1  #K>m=L#  2 3  #K>5=L#  2  1  #K>;#^JJdq $|@ ~ } <:K PX@PUU  SM =K =  K =M >K(PX@HIUU  SK =  K =M >K1PX@FIUUU  S  K =M >@GUUUU  S  K =M >YYY@6|{vurnkja`]YVUPOJIFB?>541-*)" $$  +2'>54"547267#"'&"'6324.'&47327&#"&47>5!&#"&47>54.'&47327!7327#"&57D+\B;@^#+5)/THN+5-tBT?>313323314313413323323314{!9'- PNdZ;=L3# Y)%TF/F;3/9?4=L#  2  1  #K>=L# 4  1  #K>m=L#  2  1  #K>5=L#  2  1  #K>;#^JJdq +$|@ ~ } <:K PX@WZUU  S M = K =  K =M >KPX@XbUU  S M = K =  K =M >K(PX@PbIUU  S K =  K =M >K*PX@NbIU UU  S  K =M >@ObUU UU  S  K =M >YYYY@8|{vurnkja`]YVUPOJIFB?>541-*)$$  +267#"'&"'6322#".5464.'&47327&#"&47>5!&#".7>54.'.7327!7327#"&5+#+6)/THM+5-uBT?T`^?;B[+D314314413323313313313313{ 9(- PNdf0E;3/9?4EV%)X #5L;9\=L#  2  1  #K>=L# 6 1  #K>m=L#  2 3  #K>5=L#  2  1  #K>;#^JJdqT#Q@L31 D;)  @9  b  `bM = N =M>Y@GEA?:8,*(&!##%$ +73276#"&52'>54#"54#"'#"&547327&5463272'67J.C;+ ;H9Xp7D+\B;@^gͲVZ)FdDm=)99u1Bl''ZiNT1G VRZ<=L3# X)%TF󁁴Ϙ;d-#sw5Hqo` #.`jT#Q@L31 D;)  @9  b  `bM = N =M>Y@GEA?:8,*(&##%$ +73276#"&52#".546#"'#"&547327&5463272'67J.C;+ ;H9X`^@;B\+D-ͲVZ)FdDm=)99u1Bl''ZiNT1G VRFV$)X #6L;9\%󁁴Ϙ;d-#sw5Hqo` #.`jT#2`L@[B@ SJ8  K*PX@;  b  `bM =  N  =M>K,PX@?  b  `b=M =  N  =M>@=  b  `b U=  N  =M>YYY@VTPNIG;9751/*(!##%$ +73276#"&52'>54"54&54632#"'#"'#"&547327&5463272'67J.C;+ ;H9X67D+\B;@^H+!5 P ͲVZ)FdDm=)99u1Bl''ZiNT1G VRZ<=K4# Y)%TFR (% 󁁴Ϙ;d-#sw5Hqo` #.`jT#2`L@[B@ SJ8  K*PX@;  b  `bM =  N  =M>K,PX@?  b  `b=M =  N  =M>@=  b  `b U=  N  =M>YYY@VTPNIG;9751/*(##%$ +73276#"&52#".546&54632#"'#"'#"&547327&5463272'67J.C;+ ;H9X'`^@;B\+D)%3 P ͲVZ)FdDm=)99u1Bl''ZiNT1G VRFV%)X #6K<9]R (% 󁁴Ϙ;d-#sw5Hqo` #.`jT#1_K1PX@/ZA? RI7  <@/ZA? RI7  K#PX@<  b  `bM =  N  =M>K*PX@:  b  `b U  N  =M>K1PX@>  b  `b U=  N  =M>@Db  `  `b U=  N  =M>YYYY@!%$USOMHF:864-+$1%1!##%$ +73276#"&52'>54#"56%2#"756#"'#"&547327&5463272'67J.C;+ ;H9X37C+\B;@^'$C ͲVZ)FdDm=)99u1Bl''ZiNT1G VRZ<=K4# X)%TF - =)#󁁴Ϙ;d-#sw5Hqo` #.`jT#1_K%PX@/ZA? RI7  <@/ZA? RI7  K%PX@<  b  `bM =  N  =M>K(PX@Bb  `  `bM =  N  =M>@@b  `  `b U  N  =M>YYY@!%$USOMHF:864-+$1%1##%$ +73276#"&52#".546%2#"=6#"'#"&547327&5463272'67J.C;+ ;H9X'`^@;B\+D'% D qͲVZ)FdDm=)99u1Bl''ZiNT1G VRFV%)X #6K<9]- =)#󁁴Ϙ;d-#sw5Hqo` #.`jT=#6d@)( 10_FD WN<  <' :K PX@I Z  ` b  UUM = N  =M>@J  b  ` b  UUM = N  =M>Y@%%$ZXTRMK?=;942.-+)$6%6!##%$ +73276#"&52'>54#"547267#"'.'632#"'#"&547327&5463272'67J.C;+ ;H9Xl7D+\B;@^#+5)/THN+5-tBT?ͲVZ)FdDm=)99u1Bl''ZiNT1G VR|Z;=L3# X)%TE/E;309?3󁁴Ϙ;d-#sw5Hqo` #.`jT?$6d@). _FD WN<  <:K PX@I   Z  ` bUUM = N  =M>@J   b  ` bUUM = N  =M>Y@%&%ZXTRMK?=;90/*(%6&6" $$%$ +73276#"&5267#"'.'622#".546#"'#"&547327&5463272'67J.C;+ ;H9X#*5)/THN+5-uBS@T`^@;B\+D5ͲVZ)FdDm=)99u1Bl''ZiNT1G VR /E;3/:?3FV%)X #5L<9]`󁁴Ϙ;d-#sw5Hqo` #.`jGW@ JIHC=)R @;b ` UM =M = M >Y@$USOMEDA?64-+('%#GG  +2'>54#"543'&54>3 32672!"7>54&#"#!&'637327#"&57D+\A ;@^\)h F^!/- L-%TfGV %L -0!] 9(- PNdZ<=L3# X)%TFl?fɠe>lZhd!'NƴHk\!dhZ;#^JJdq\GW@ JIHC=)R @;b ` UM =M = M >Y@$USOMEDA?64-+('%#GG  +2#".5463'&54>3 32672!"7>54&#"#!&'637327#"&5)`^?;B[+D\)h F^!/- L-%TfHV $L -0!]!9'- PNeFV%)X #6K<9]l?fɠe>lZhd!'NƴHk\!dhZ;#^JJdq  Vf\@  YXWRL8#a  Z  `M= M = M  = M >KPX@?  b  `M= M = M  = M >K%PX@=  b  ` U M = M  = M >@Ad  b  ` U M = M  = M >YYY@("!db^\TSPNEC<:7642,*!V"V  +2'>54#"54&54632'"'3'&54>3 32672!"7>54&#"#!&'637327#"&57D+\B;@^H+ 5 P \)h F^!/- L-%TfHV!%L -/!^!9'- PNdZ;=L3# X)%TEQ )% Nl?fɠe>lZhd!'NƴHk\!dhZ;#^JJdq m Vf\@  YXWRL8#a  Z  `M= M = M  = M >KPX@?  b  `M= M = M  = M >K%PX@=  b  ` U M = M  = M >@Ad  b  ` U M = M  = M >YYY@("!db^\TSPNEC<:7642,*!V"V  +2#".546&54632'"'3'&54>3 32672!"7>54&#"#!&'637327#"&5`^?;B[+D)%3 P ])h F^!/- L-%TfHV $L -0!]!9'- PNdEV%)X #5L;9\Q )% Nl?fɠe>lZhd!'NƴHk\!dhZ;#^JJdq ;UeK1PX@  XWVQK7"` <@  XWVQK7"` KPX@@  b  `M= M = M  = M >K*PX@>  b  ` U M = M  = M >K1PX@Bd  b  ` U M = M  = M >@Hd  b `  ` U M = M  = M >YYYY@,! ca][SROMDB;96531+) U!U  +2'>54#"'6%2#"7563'&54>3 32672!"7>54&#"#!&'637327#"&57D+\B;@^'%C t\)h F^!/- L-%TfGV %L -0!]!9'- PNd[;=L3# X)%TF - >)l?fɠe>lZhd!'NƴHk\!dhZ;#^JJdq /UeK%PX@  XWVQK7"` <@  XWVQK7"` KPX@@  b  `M= M = M  = M >K%PX@>  b  ` U M = M  = M >@D  b `  ` U M = M  = M >YYY@,! ca][SROMDB;96531+) U!U  +2#".546%2#"=63'&54>3 32672!"7>54&#"#!&'637327#"&5`^@;B\+D'% C v\)i F]!/- L-%TfGV!%L -/!]!9'- PNdFV%)X #5L;9]- >)l?fɠe>lZhd!'NƴHk\!dhZ;#^JJdq^$ZjW@$ ]\[ VP<' e<:K PX@O Z  `UU M = M =  M  =M >KPX@P  b  `UU M = M =  M  =M >@K  b  `IUU M =  M  =M >YY@0&%hfb`XWTRIG@>;:860.%Z&Z" $$  +2'>54#"547267#"'&"'6323'&54>3 32672!"7>54&#"#!&'637327#"&5{7D+\A ;@^#+6)/THM+5-uBT?\)h F^!/- L-%TfHV!%L -/!^ 9(- PNdZ;=L3" Y)%TE0E;3/9?4@l?fɠe>lZhd!'NƴHk\!dhZ;#^JJdqy J$ZjW@$ ]\[ VP<' e<:K PX@O Z  `UU M = M =  M  =M >KPX@P  b  `UU M = M =  M  =M >@K  b  `IUU M =  M  =M >YY@0&%hfb`XWTRIG@>;:860.%Z&Z$$  +267#"'&"'6322#".5463'&54>3 32672!"7>54&#"#!&'637327#"&5#*5)/THN+5-uBS@T`^@;B\+DO\)h F^!/- L-%TfGV %L -0!]!9'- PNdh0E;3/9?4EV%)X #5L;9\l?fɠe>lZhd!'NƴHk\!dhZ;#^JJdqJ!V .?K*PX@/"< :@/"< :YK*PX@+UM =N =M >@/U=M =N =M >Y@ =;42&$  ..$" +327#"&'2327327#"&'#"'&54>'.#"3267LwdB^y /'N';cIU* ^w;MXqtAi1&wG']+#cG9[;VPRK @I5!YQkCVodoM#/FD;S\J! ->K*PX@.!<@.!@0 U=M =N =M >Y@ <:31%# - -  +!"5463!22327327#"&'#"'&54>'.#"3267#!5!'N';cIU* ^w;MXqtAi1&wG']+#cG9[;X%-"-˃ @I5!YQkCVodoM#/FD;S\J!m%GX=K*PX@. H;64)  <@.H;64)  K*PX@Addb` M =N  = N  =>@Eddb`= M =N  = N  =>YY@'&VTMK?=9731-+&G'G)$%$ +73276#"&5672#"/&542327327#"&'#"'&54>'.#"3267m-D;+ ;H9X   嘑'N';cIU* ^w;MXqtAi1&wG']+#cG9[;T1G VR   &H @I5!YQkCVodoM#/FD;S\J!3DK*PX@4'"  <@4'"  @;b`=M =N = M =>Y@B@97+)%#33%$ +73276#"&52327327#"&'#"'&54>'.#"3267m-D;+ ;H9Xʘ'N';cIU* ^w;MXqtAi1&wG']+#cG9[;T1G VR̃ @I5!YQkCVodoM#/FD;S\J!u!CT>K*PX@* D720%  <@*D720%  K*PX@B ddb` M =N  = N  =>@F ddb`= M =N  = N  =>YY@#"RPIG;953/-)'"C#C!!%$ +73276#"&52#"&54762327327#"&'#"'&54>'.#"3267m-D;+ ;H9X9%l 0g B'N';cIU* ^w;MXqtAi1&wG']+#cG9[;T1G VR1  %# @I5!YQkCVodoM#/FD;S\J!94EK*PX@  5(#! <:@ 5(#! <:YK*PX@6 UM = M =N = M >@: UM == M =N = M >Y@ CA:8,*&$ 44  +267#"'&"'6322327327#"&'#"'&54>'.#"3267s#+5)/THN+5-uBS@'N';cIU* ^w;MXqtAi1&wG']+#cG9[;/E;3/9?3ȃ @I5!YQkCVodoM#/FD;S\J!9$FWK*PX@%- G:53(  <:@%-G:53(  <:YK*PX@J b `UM = M= N  = M  =>@N b `UM == M= N  = M  =>Y@#&%USLJ><8620,*%F&F" $$%$ +73276#"&5267#"'&"'6322327327#"&'#"'&54>'.#"3267m-D;+ ;H9X#+5)/THN+5-uBS@'N';cIU* ^w;MXqtAi1&wG']+#cG9[;T1G VR/E;3/9?3ȃ @I5!YQkCVodoM#/FD;S\?LS@P JHDB=;4320-)%$ +!#&#"&47>7272&#"&472>&'!"#"&'73267/71x) 5[l)65  Njmo87 KQ@N+< US   U K> IG@?><9510*)!    +!"5463!2!#&#"&47>7272&#"&472>&'!"!5!%71x) 5[l)65  NTY@V4 <bS   UK= K>RPIHGEB>:932*&*! +6320#"/&54!#&#"&47>7272&#"&472>&'!"   71x) 5[l)65  NO\@Y/<bS   U K= K>MKDCB@=954.-%!  +2#"&5476!#&#"&47>7272&#"&472>&'!"F%l  /f 71x) 5[l)65  ND?OX@UBA@J <:S  UK= M >MKGE=;4320-)%$ +!#&#"&47>7272&#"&472>&'!"%7327#"&5/71x) 5[l)65  N;#^JJdq;7D @eM >Y@   +2'>54#"547D+\A ;@^7Z;=L3# X)%TE@ :M >$% +7327#"&5!9'- PNem;#^JJdq;7D @eM >Y@   +2'>54#"547D+\A ;@^7Z;=L3# X)%TEwZ@ <:KPX@QM>@IUMAY@  +267#"'&"'6323#+5)/THN+6-uBT?/F<3/:@3b'3$v@ <:KPX@UQM>@$UUIMAY@#"  +267#"'&"'632462"%462"&#+6)/THM+6-uBT?!A_AA_BA_AA_A0E;3/9?4_AA/-Bo/AA_?B?bw$M@K8 AK*PX@-ddbM= =M>@*ddbQM= >YY@ ,%(($%$ +73276#"&5632#"/&54#"&554&'&7672>76'&4&#"-C;+   +AXG/V ' CA-'1@ox^T1H VR   ' 94*V1',#1K5kyAd^##fiV ?b:u@8% .@ bQM= >Y@ ,%%%$+73276#"&5#"&554&'&7672>76'&4&#"-C;+ K*PX@. ddbM= =M>@+ ddbQM= >YY@GE9721&$!!%$ +73276#"&52#"&5476#"&554&'&7672>76'&4&#"-C;+ 86*(#"  +267#"'&"'632#"&554&'&7672>76'&4&#"#*5)/THN+5-uBS@+AXG/V ' CA-'1@ox/E;3/9?394*V1',#1K5kyAd^##fiV ?b5$M@&K8  A<:B9KPX@6b UM = M = =M>@3b UQM = M = >Y@JH<:54)'" $$%$ +73276#"&5267#"'&"'632#"&554&'&7672>76'&4&#"-C;+ K(PX@D d Z  bZ U N  =M=L>@B d Z  bZ   U UM=L>YY@TQHGFC@?:961*'$"WW(! +672#"/&5426762"'.+;26&#!.7>54&'.7!272'.+"   jM FF Pg2"J5' cd s?@r ;\5/ %3%bk#1   &S1R {6% H;+)9 u-5 ;y3{; 2 q#wd='}dT@J*@C  d Z  bZ   UUM=L>Y@$QNEDC@=<763.'$!TT  +2#"&547626762"'.+;26&#!.7>54&'.7!272'.+"q%l  /f &jMF  FPg1#J5' cd  s?@r  ;\50 %3%bj#11  %#!1R {6% H;+)9 u-5 ;y3{; 2 q#wd='}/l @0db  US K >Y@lkfeb^[ZQPMIFECC*!+6320#"/&5&4.'&47327&#"&47>5!&#"&47>54.'&47327!   413323323314313323314313{   '=L#  2  1  #K>=L#  2  1  #K>m=L# 6 1  #K>5=L#  2  1  #K>gK(PX@3dbS K  = K >@1db  US K >Y@&gfa`]YVULKHDA@;:541-*)   +2#"&54764.'.7327&#".7>5!&#"&47>54.'&47327!N%l  /f %313313313313323314413323{1  %#`=L#  2  1  #K>=L#  2  1  #K>m=L# 63  #K>5=L#  2  1  #K>1j-Wg@ Y bZX@-   U  S K=M >Y@ec_]WVQPMIFE<;CCC+4.'&47327&#"&47>5!&#"&47>54.'&47327!7327#"&5413323323314313323314313{!9'- PNd/=L#  2  1  #K>=L#  2 5  #K>m=L# 65  #K>5=L#  2  1  #K>;#^JJdqBfB jK%PX@eM >@e =M >YY@  %$ +&54632'"'2'>54#"54+!5 P q7D+\A;?^ )% fZ;=L3" Y)%TEBN; K,PX < K*PX@eM >K,PX@e =M >K1PX@bc =M >@bcU >YYYY@    +2"=62'>54#"54'%D 7D+\A;?^;- =) Z;=L3" Y)%SF57o$@ <:K PX@%YUM=M >@$eUM=M >Y@" $$  +267#"'&"'6322'>54#"54#+6)/THM+5-uBT?Z7D+\A;?^1/F<3/9@3Z<=K4# X)%TF- (6@3< :bU= >%%$"+327#"&'327#"'&54&'.7$72qveA^y 3'F%%!RfD=/VF&/PRHA-E95}V- 2 +#se '9@6<bU= >$#   +!"7463!2327#"'&54&'.7$72F!5!3'F%%!RfD=/VF&/%-#,hHA-E95}V- 2 +#se^%A@$+-@*dbbV= >Y@ %()# +462"%462"&632#"/&'4327#"'&54&'.7$72A^BB^BA^BB^AG   3'F%%!RfD=/VF&/{^BB/-Bo/BB^@B   'HA-E95}V- 2 +#seF!=P@M')< dbbU= >:9-+&$   +2#"&5476462"%462"&327#"'&54&'.7$72%m 0f A^BB^BA^BB^A3'F%%!RfD=/VF&/F2  %#_AA0-An/BA_?AHA-E95}V- 2 +#se9.T@Q <:bUM == >+*  +267#"'&"'632327#"'&54&'.7$72#+6)/THM+6-uBT?3'F%%!RfD=/VF&//E;3/9?3?HA-E95}V- 2 +#se3$@@ * , <:KPX@0  bU UM = = >@.  bU U U = >Y@=<0.)'#"  +267#"'&"'632462"%462"&327#"'&54&'.7$72#+6)/UHM+6-uBT?!A_AA_BA_AA_A3'F%%!RfD=/VF&/0E;3/9?4_AA/-Bo/AA_?BHA-E95}V- 2 +#se9)6\610*:K(PX@UK =K>@UUK>Y@ $'CC+4.'.7327&#".7>5#"&'73267313313313313go9T /=L#  2  1  #K>=L# 6 3  #K>jmo871 5bK(PX@ UK =K>@UUK>Y@10-)&%  +!"5463!24.'&47327&#"&47>5!6!T314313313323%-#,=L#  2  1  #K>=L# 65  #K>=l@  @"dbUK>Y@ CC)!+632#"/&'44.'&47327&#"&47>5   314313313323   '=L#  2  1  #K>=L# 6 1  #K>9lK(PX@%dbK =K>@#dbUK>Y@541-*)   +2#"&54764.'&47327&#"&47>5j%m  /f k4133233143141  %#b=L#  2  1  #K>=L# 63  #K>;fB jK%PX@eM >@e =M >YY@  %$ +&54632'"'2#".546)%3 P `_?< B[+D )% fEV%)X "5L;9\9d; K#PX < K#PX@eM >@bcM >YY@    +2"=62#".546'$ D `^?< B[+D;- =)EV%)X "5L;9\J`$@ <:K PX@#YUUM >KPX@"eUUM >@(eUUIMAYY@" $$  +2#".5467267#"'&"'632m`^@;B\+D#*5)/THN+5-uBS@hEV%)X #5L;9\/E;3/9?37' >4@1 < :U=N ><:-+$"%$" +327#"&'#"&'&547>54'&'&'>32327654'&'&'&54632RwdB^y {}B l'3qVbNL#<7!8-9PPR)XfB% (?\uwxw473Ec7' =5@2<U=N >;9,*#!   +!"7463!2#"&'&547>54'&'&'>32327654'&'&'&54632= 5"{}B l'3qVbNL#<7!8-9P%-#,d)XfB% (?\uwxw473Ec7'3$VD@A8<dbV =N >TREC<:((# +462"%462"&632#"/&54#"&'&547>54'&'&'>32327654'&'&'&54632B^AA^AB^BB^BH   {}B l'3qVbNL#<7!8-9PP^BB/-Bo/BB^@B   ')XfB% (?\uwxw473Ec7'F!SL@I5< dbU =N >QOB@97&$   +2#"&5476462"%462"&#"&'&547>54'&'&'>32327654'&'&'&54632%m  /f A^BB^BA^BB^A{}B l'3qVbNL#<7!8-9PF2  %#_AA0-An/BA_?A)XfB% (?\uwxw473EcD+5@ 1< 9K PX@'ZM=M=M >@(bM=M=M >Y@0.*(  +2'>54#"544&#"326'64>762#"''7D+\A;?^Ejz6`d^$-37)y^RZ<=L3# X)%TF?s{Z=uCoJ%m+yD+5@ 1< 9K PX@'ZM=M=M >@(bM=M=M >Y@0.*(  +2#".5464&#"326'64>762#"'7`_?<B\+D jz6`d^$-37)y^RFV$)X #6L;9\?s{Z=uCoJ%m+y7'7DP@M &<:UM ==N >B@31*(  +267#"'&"'632#"&'&547>54'&'&'>32327654'&'&'&54632#+5)/THM+5-uBS@{}B l'3qVbNL#<7!8-9P/E;3/9?3=)XfB% (?\uwxw473Ec7'$Vb@_ 8 <:U UM =  = N >TREC<:)'#"  +267#"'.'62462"%462"&#"&'&547>54'&'&'>32327654'&'&'&54632#*5)/THN+5-uBS@ B^BB^AB^BB^B{}B l'3qVbNL#<7!8-9P/E;309?3^AA/-Bo/AA^@B)XfB% (?\uwxw473Ec<I_IDC=:K(PX@ UK =K>@UUK>Y@ $,CMC +%&#"&47>54&'.'&4732776&'&47327#"&'73267ExF0KE {Z R%9H ?D{ JG!#fo:T {965 9{70/R0 2  1 59-'-3 2  1 #8J:qjmo87s HfK(PX@! UK =K>@ UUK>Y@?;87/.+'  +!"5463!2&#"&47>54&'.'&4732776&'&47327!6!zExF0KE {Z R%9H ?D{ JG!# %.#-{965 9{70/R0 2  1 59-'-3 2  1 #8J:qP@  K(PX@%dbK =K>@#dbUK>YY@ CMC)! +672#"/&'4&#"&47>54&'.'&4732776&'&47327   iE  x F0JF {ZR%:H?D{JF"#   &]{965 9{70/R0 2  1 59-'-3 2  1 #8J:qLpK(PX@& dbK =K>@$ dbUK>Y@C?<;32/+  +2#"&5476&#".7>54&'.'.732776&'&47327X%l 0g Fy E0KE  {Z  R%:H @Dz JF"#1  %#-{965 9{70/R0 2  1 59-'-3 2  1 #8J:q`H@ @1b UU M  = K>Y@ CB9851.-&$!HF  +2#".5462"'&53265!"&#"&47>54.'&472$;`_?<B\+Cisw+)+<}fN-313323314)4FV%)X #5L<9]C^pb;+Im=L# 65  #K>5=L#  2 u'J&5@2 <dbINB*!+6720#"/&'4462"%462"&   _A^BB^BA^BB^A?   &D_AA/-Bo/AA_?Bu'J!6@3dbIMA   +2#"&5476462"%462"&%m  /f A^BB^BA^BB^AJ1  %#_AA/-Bo/AA_?Bmjs@ <d[)! +632#"/&54      'T\%S@N53F=+ @.dddb N =M>Y@ IG%,"()$%$ +73276#"&5632#"/&54#"'#"&547327&5463272'67J.C;+ ;H9X#   ͲVZ)FdDm=)99u1Bl''ZiNT1G VR   's󁁴Ϙ;d-#sw5Hqo` #.`jT?I@F2) <:!:dbN =M>$%,"%%$+73276#"&5#"'#"&547327&5463272'67J.C;+ ;H9XwͲVZ)FdDm=)99u1Bl''ZiNT1G VR󁁴Ϙ;d-#sw5Hqo` #.`jTy!O@J1/B9' @/ dddb N =N>Y@EC?=86*(&$!!%$ +73276#"&52#"&5476#"'#"&547327&5463272'67J.C;+ ;H9X%l  /f ͲVZ)FdDm=)99u1Bl''ZiNT1G VR1  %#/󁁴Ϙ;d-#sw5Hqo` #.`jT@\@Y ;" 3*<:bU UN >640.)'  +267#"'.'632#"'#"&547327&5463272'67J#+5)/THM+5-uBS@ͲVZ)FdDm=)99u1Bl''ZiN/E;309?3 󁁴Ϙ;d-#sw5Hqo` #.`jT$R{@xM42 E<*  <:   bbU  U  N =M>HFB@;9-+)'" $$%$ +73276#"&5267#"'.'632#"'#"&547327&5463272'67J.C;+ ;H9X#+5)/THM+5-uBS@ͲVZ)FdDm=)99u1Bl''ZiNT1G VR/E;309?3 󁁴Ϙ;d-#sw5Hqo` #.`j /@@= <dbM =M >.,&$  *! +6320#"/&'4"32% 76$3    䮚r[   '3-Jӿh% lz*=@:dbM =M >)'!  +2#"&5476"32% 76$3 \%m 0g 㮚s[1  %#3-Jӿh% lzTJU@R F@,<db `M = M>HGDB970.+*(& JJ*! +6320#"/&543'&54>3 32672!"7>54&#"#!&'63-   V\)h F^!/- L-%TfHV!%L -/!^   'l?fɠe>lZhd!'NƴHk\!dhZEX@UA;'< db `M = M>CB?=42+)&%#!EE  +2#"&54763'&54>3 32672!"7>54&#"#!&'63m%l  /f M\)h F]!/- L-%TfGV!%L -/!]1  %#l?fɠe>lZhd!'NƴHk\!dhZ?D5E[@X8761+@<bM = M= M >CA=;32/-$" 55 +%3'&54>3 32672!"7>54&#"#!&'637327#"&5`\)h F^!/- L-%TfGV %L -0!]!9(- PNdl?fɠe>lZhd!'NƴHk\!dhZ;#^JJdqL'.K#PX@ e >@ d[Y@   +2"&5476%l  /f '1  %#;'d K#PX@eM >@eIMAYY@   +2#".546`_?< B[+D'FV%)X #6K<9]NZ @IMA   +!"5463!2/kL?JA+Z+^NZ @IMA   +!"5463!2/kL?JA+Z+^D/; @IMA    +!"&5463!2\ "#/E;:D\ @IMA    +!"&5463!2  n#!;)G;V@GK? +!!;yVy;'V@GK? +!!;Vy{}@GK?+#3#3{{{{}`` /@,SGK? +!5%!5//eebbZ@:[ +"&546767 T^B-d{BI3Z{3-d+.1J%#^P;@9> +2&7>54'.546T^B-d{BI3Zz3-d,-1J$#_P@9[ +2&7>54'.546T^B-d{BI3Z{3-d+-1J%#^Z'@ 9>% +.54632hjuH5VF!Tc9 ''{s_1'C83-Z5)@:[)) +"&546767!"&546767T^B-d{BI3ZZT^B-d{BI3Z{3-d+.1J%#^{3-d+.1J%#^P;+)@9>)) +2&7>54'.546!2&7>54'.546T^B-d{BI3ZT^B-d{BI3Zz3-d,-1J$#_z3-d,-1J$#_P+N)@9[)) +2&7>54'.546!2&7>54'.546T^B-d{BI3ZT^B-d{BI3ZN{3-d+-1J%#^{3-d+-1J%#^P'+%@9>% +.54632.54632juH5VF Tb9 GjuG5WF!Tb: ''{s_1'C83-'{s_1'C83-jX5'9@6#<UM>  '' +2632#"'#6#"&4632&546P=Nf;HG<b`L^^;GH:FD3fN==}V<=Py}fyP{T++'ժ;?jX5Er@D:0%#! @ UUM>Y@ CA%'#&$%'## +632#".'#"&547#"&546326'6#"&4632&54632632#"'^;FG:FD3fN?=Nf;IH<b^;GH:FD3fM@=Nf;HG<fyP{T++'ժ;@>=}V;=Qy}fyP{T++'ժ;?==}V<=Pydj @IMA$" +4632#"&dfhsjhmd "+7^d @M > +7462"&dZ{VZ{Vw;VR;9VRd3 @M >+%462"&%462"&Z{VZ{V\Z{VZ{Vw;VR;9VR9;VR;9VRd% @M >+7462"&%462"&%462"&dZ{VZ{VJZ{VZ{VLZ{VZ{Vw;VR;9VR9;VR;9VR9;VR;9VRd @IMA +462"&dZ{VZ{V?;WR;9WRh $/<V@H OK J@= b U  UUUIMAY@USNLFDCBA?97##&$##&$!+%327654"4632#"&%327654"4632#"&327674"4632327'#"'#"&)?03P5wq^)?03P5vq^1)?/3P5\01 a`nsjFIwq^mDRXou;/LFú{DRXou;/LFú{DRXou;/LE-1f+}/&|h  $/<GTn@`gcb<_:K1PX@;  b  U U  U  UM  >@B  b  U U  U  UIM AY@mkfd^\[ZYWQOKIFDA?97##&$##&$!+%327654"4632#"&%327654"4632#"&%327654"4632#"&327674"4632327'#"'#"&)?03P5wq^)?03P5wq^)?03P5vq^1)?/3P5\01 a`nsjFIwq^mDRXou;/LFú{DRXou;/LFú{DRXou;/LFú{DRXou;/LE-1f+}/&|\!@e>  +2#"&5476%l 0g 2  %#\!$@!e>  +2#"&54762#"&54765%m 0f %l 0g 2  %#2  %#\!//@,e>! )' /!/  +2#"&5476!2#"&5476!2#"&5476d%m  /f %m 0g %l 0g 2  %#2  %#2  %#\!@e>  +2#"/&546- f/ m#  1\!$@!e>  +2#"/&'46%2#"/&546%- g/ m- f/ m#  1#  1\!//@,e>! )' /!/  +2#"/&546!2#"/&'46!2#"/&546T- g/ l- g0 m- f/ m#  1#  1#  1j^ "+'56^`žgdkC{o "+567&'5{`žg17kC=;N '3F@C,+)(30-*21/.<UUIMA+462"&462"&%462"&462"&%7   'Z{VZ{VZ{VZ{V[zVZ{VZ{VZ{VE}vE{pDDw9;VR;9WS5;VR;9WS9;VR;9WS2;WR;9WRB444"4{H/98@5M  =M >8732'%//  +2#"&'&&746462"&2#"&'&&546462"&=W98%4Z\Z{VZ{V=W98%3Z\Z{VZ{VHPJ;V3<DNL3;VR;9VRPJ;V3<DNL3;VR;9VRNfD/9E6@3:.?(<bM =M >8732'%& +#"54>767>2#"5&'.'462"&67654&5%\+!/0U EF@)`-;T "',gXZ{VZ{V*m4N#'X%C#&22/,]3[V '7##@() ZFJ;VR;9VROB:Z/VJ'5K1PX@ K>@GK?Y@  +!5/ZZ)8qz@wrRF g\9.#<db `U  V >}{vuljdb[YVTMK%&'#''%+%.&54632.54632>32#"#".'#"&54>%.&54632.54632>32#"#".'#"&54>.&54632.54632>32#"#".'#"&54>!;S!f1++*$Y$ x@Q.$&( '.-Oz";S!g0*+*$Y$!y@R-$&( &/,Pi";S!f0*+*$X$!y@R-$&( &/,P6"N %e* uP)F*,(6ef1*,+6"N %e* uP)F*,(6ef1*,+6"N %e* uP)F+,'6ef1*,*!!@ d[ +3#VTT5`D,6cm>@;b` M = M  >lkgfb`.%'+.%% +#"&547632#"&=4>7654&#"462"&#"&547632#"&=4>7654&#"462"&/>/)Lm`'R95'--+' DCqRRD^ Z{VZ{V>/)Lm`'R95'--+' DCqRRD^ Z{VZ{V>%/A6VZP5^X/'-hD)!$;L>d`@;VR;9VRQ>%/A6VZP5^X/'-hD)!$;L>d`@;VR;9VR5HISKPX@.bM  =M  =M  >KPX@5bbM  =M  =M  >KPX@.bM  =M  =M  >@5bbM  =M  =M  >YYY@RQMLHF;9+)$"  +2#"&'&&746462"&#"&547632#"&=4>7654&#"462"&+=V97%4Z\ZzW[zVn>/)Lm`'R95'--+' DCqRRD^ Z{VZ{VHPJ;V3<DNL3;VR;9VRM>%/A6VZP5^X/'-hD)!$;L>d`@;VR;9VR{DISKPX@.bM  =M  =M  >KPX@5bbM  =M  =M  >KPX@.bM  =M  =M  >@5bbM  =M  =M  >YYY@RQMLHF;9+)$"  +2#"&'&&546462"&#"&54762#"&=4>76'4&#"462"&=W98%3Z\Z{VZ{V'=/)Ll`'R95'-.+' CDqQRD^ ZzW[zVDPJ;V3<DNL3;VR;9VRM>%/A5V[P5^X/'-iC) %;L>d`@;VR;9WSLd@<:K PX@eU>K PX@eU>KPX@eU>KPX@eU>KPX@eU>KPX@eU>KPX@eU>KPX@eU>@eU>YYYYYYYY$#"+'327632'#"`mh5H=Bf FjmL5&1홚 V!+)5j@ )@bUK>Y@+*0/*5+5$6"2# +32'#54;>54&+"=232632#"'254.^1;  >33> s!;-o1!P-++)RJ5+ .Kd)-+`%@" 9eM> +72.54462"&7@UIMAY@'&0.&7'7%% +"32>'4545<."'&'467632V "   mAb?4Cag>nK<;1&;#< ;"8"- (8AJ&$ ')E'+SDgZ3B6_t/-V!)K PX@"bUIL@KPX@UP >@"bUIL@YYY$&C+&#"&47>=4&'&476724632#"&+X;LX++L4)>3/7?1-:m/- ./mfL)-d['@!dSIL@Y@ $B+&#".7>=!'67323# 3%)=>33+3 g{u32 ! (  32+Lj-oVYc?'K1PX@bSR>@'b`SINBYY+%%+4&'&/!!#".54632 32=>:: +{ILjOf#,      V-W 9g79|d$,   R\ $0@-<:UIMA,&#$+4.#"7267#"'&54>7632)1%j,7eAtJT6nŀC..1%-9#6SS"LJ0K*PX@eIMA@deIMAYY1 +#"'>543;27ט)? J`>!V/5yhM% lTV 4Q@UIMAY@31%#   +"674&.'32654#".5467&'&54632u'>N+;/#4I-5BuBI=;q^@n}`V}+7.K)7 S+5PA;SC)7%%LjVTA9JFBhLlST^ N@<9KPX@QM>@UIMAY*###+32754#"4632567". -'/'jJ}{uITC3)?a1!=A6P;D++%1B)=1jB @SK >+3##5#5353nnbRRJ om@GK? +!5omccRf/@,SGK? +!5%!5fXXZZB  "+467$B#Z?9`LjV%^wb'/{  "+'>54&'7{#Z@:`-NjV%^wc'B)?]@=@ddINBY@ *3&F+&#"&47>=4&'&47672>32&#".7>=4&#"w) 9VEX+'Pyu {B^B+X{Ro6 +?<%, h4--0lN-'  *hum/--4hN4 ,0?h%7/@,UIMA'&0.&7'7%% +"32>'4545<."'&'46762V "   mAb?4Cag>nK<;N 1%<#; ;"8#- )7BJ&$ ')F'*SDgZ3B5_t/.Z;@8 <ddIL@ +%"&'6762&#".7>54&VX - # ==33+ 3F-'-! '  3133!Dfq-8@5<b`UIK?*(*"+7463232672!4?654'&#"#"Doz5<;)JW2-H)5yxL(?2 F3w*.[H?+AM(3L3 ;uvnFUJ(% &"5yo2l@ 0/@%bbUR>Y@ #%#*%$"+%4&#"#"74>32#"'&63232654#"5>1%#;+'D=!'67323# 3%)=>33+3 g{u¤31 ! '  31+Lj-nVZP-v(8@5<b`SINB,%%+4&'&/!!#".5463232w=>:; +{HLjOg#-      V{-V 9g79|d$,   FP $N@<:KPX@QM>@UIMAY,&#$+4.#"3267#"'&54>7632)1%j,6eAtJT6nŀC..0%-8$6TS"LK0543;27ߗ*? J`=!V/yhN% lTJy 42@/<UIMA31%#   +"674&.'32654#".5467&'&54632h'=N+;0"5J-5BuBJ>;q^?o}aV}N+7-L)7 T+5PB;RD)8%$LkVTB9JFBhLlSFP O@<9KPX@UM >@UIMAY*###+732754'"4632567#". -'/'kI}zuJTD3*?`2!=B5P;D++%1A*=j @OK >+%3##5#5353nn9RRJoD@K > +%!5oDccR}f/@,SGK? +!5%!5f+XXZZBj  "+7467$B#Z?9`jV%^wc'/q{  "+%'>54&'7{#Z@:`-jV%^vb'\{)0a@+*!@#bUIMAY@ *"#'$&"+6&#"#"&54632327#"''"'&547772 'Z 1+Hh-L BIT DUH)-IC%hF+ #!!9Z'=++ !9CD!Ou=Ƥ3WJ1+JC@@<USIMA  +27#"&4632!4&#"PbP)Xxc1*O)+-9X+wr!?['5L1K23^ *@'UIMA    +2 &464&#"326sͤ#R@3CVA+F^{syuk!7BJ@G 9'<;.9U I M AB><;3")! +436767.'&'#"=!765&=!'"&#"5474'&'2&#"F f  HN1 81'B.Jb+ HNH+-/ ?5N?TF)) 2O )%1\7-+ FL+L!^D b @!USIMAY@  " +#326"'>32#".5!54&'/e5!X5kVRx:yLf2XP);6DJ"5?::ZP's+PR7 Zih6D@A6 < d  dUIL@53#24$#24" +67363232&#5473>=4#";2&#5473>54'"\E\R! N1 f1L R5}# !h% $$ #\! $$ #5E@,' @;@1dd   b UIL @Y@EE?<'#3&&223 +7;2&#54;>54.#"'67367654#"=737#"32&#'54/&  Z;} %y=+EH' F- ^7N-HV1XR# // !8<R#--?V? ++)y+)2@/<ddIL@$#23+;2&#5473>54'"'67 R5}#\Ef! $$ #5- !B![cF@C$aE=4&'&47672>32>32&#"&47>=4&#"&#".7>=4&#"w+ LCEX+3D}q X1:h"f1A^C+XyTo7A:U--co6 @;%- h3--0lJ.-   F"ium.--FGM3$7j/ --FGM3,0BZ?9@6==4&'&47672>32&#".7>=4&#"w) 9VEX+'Pyu {B^B+X{Ro6 +?<%, h3--0lN-'  )hum.--4hN3 ,0B  o&3z@1'&@+bUUIL@Y@ $##$C +&#"&47>54&'&47672632#"'532654&#"w'\LEX+3D}q UOs@0 R-H):-Do-../mJ/- ;'qYbs{yA%,9J*:@7)<bUIUMA!-(#+372654'.4632."#"&#"69-*+9)9wTIrNo70NR5%533DNT+!`%L)%&'N1#BX#P<+'%%-7%9L1>@;<:UIMA%###"+%327#"&5#"=4326=732#+%1!Fl1FZ=6p u%-H>?m;HJ/f?F@C!<b`UIM >"$!'&++676#"'&762'.#"6'54&'&76723632#"'&#"G3 }b˅χ 3L`3GR@ 1+' o5,6+?js+~ZiQ4!<%v,&3D[@X@< Z  U  S SK>>:7610-,+*'&!  DD +#"!!!!&#.7>=#&6735#&67354.'.73!7'.fH#- #- 413313%%&e%%&e313O!%3%y1 X1 =L# 6 3  #K>F XF =L#  2 %S@ 24 KPX@=bUS S  U M = >KPX@9bUS S  U M  >KPX@=bUS S  U M = >KPX@9bUS S  U M  >@=bUS S  U M = >YYYYY@SSPOLKB@;9760.$%%+&673546762#"'.#"3#3#3267"'&#"#"&54676767#&6734=V%%'ZV{%IR?6-5 y- - &yl=+1t'#~H;!$ K2!$T3 D %%'F To 5+5@5 2q1  ($1 a:!@3^P.)/')^4F 9 #@>=:  B p |wed <  b U  U  SUM= M =M= M >~{xurnlkjhfca_]OMIHECA?R!J#"""+32#"47#327.5732654&'.4"##"=27632675732632.#"#"'&#"''"5#!"'732&#'54; Ѿ=)9 +98+LJ^k# 9syR 3NfH)3/!+=/DPOc+Djg3jF P=H^8/;F9/8Z =67!h+3D;?Y'7ZJ- Ai`/ 9P!D+)D])>C72#)DR3Bb1H1V; ^]@ZO:<UM = M = K= M  > TRLJ?=40-,#" ^ ^#! +!"326"'&'.#&#"&47>54.'&472$3232654'&'&547632'&#"N-XR9bGs313323314)4)sb+E1\hc?Fhup5Nr+8oO?+Imit d%=L# 65  #K>5=L#  2 C^)R?O8-q.?kFTXHj1^{1%q.EYFj{LqG@4 #F@<  bb  TP =M=M  >Y@ED<:973210!"'"%&+!!&5.#"326##"&'.5432574.'&767267&'"'&fZj"G1b53gP}%3%Pb>HG)RBzYXi$*7 !OI9L3R -^.XT6G#S&07c,f61!( ,L!fX?',"$ 4R@O  .$<U S  SM >440/-,'"'# +?3762'.#"!!!#32676#"'&#573&547 'w#χ 3KaW''wn^U3 }b!'r= s+~?P %PЌ~+?y5@53} 3WK%PX@UTRQO1@*UUM= M>Y@ KHAE&&-(#! +4#" 2>32>54&''67>7632#"'32#"&#&#"7632767'67675H?b+^AnBe-6=J9K7+àRX3 = X;:; -%'373o/Rm!T`  hq yP5b0bq=%mTp=D\۶ξ-L1!oD#wj1;JQWy@v< S  SUM =  K  >RRKKRWRWUSKQKQPOEDBA>=<;8721.*'&!  JH +23#3#"'&5327!&#"&47>5#&6735#&673.'&472$654'!%&"sd&i- 4g- m 0w+)+F F ^2 2 *8>WF\,L @ 2-"+ ' 654&#"3265#"&54327327#""&54632#"74654&#"3267Vl+#L dVT^VyV1bd -)y^Hs\f\9HFy<$f%6yjJ)>l%%5TyG FFT- 7"{HTl%%5TyG FFT- 7"{L^23BG'?+R_@BY7'?bZXgL5D!&*1@ ,+*(&# "+ '&5!2'.#"3 726'&5&"5bw;# -+/YMWa+> F#'Q[\T+Fy[f.<I $-2ONP'0 .*&!"+ 5!23.#"32674&"2%462"&Vl}[#N)FwwEϼg/`DvFHr}}!kV%)+hڙc)`kXV75RZ}q%3@ @ <60) "+ ' "&54632#"74654&#"326732>54#"4632#".Vlg\݃d^;F Hy%'f%7=s`''/J%^X\Ϩ{lXn!eHT:!"/ FU<+.;ALZIob%wY]bq%T ;2 "+ ' "&54632#"74654&#"326732676727327#"547#"&54?654#"'632Vlg\݃d^;F Hy%'f%7=s: ))!zf -)}ZJjqF5< /){\JeHT:!"/ FU<+.;AV/'5sTR)7#}D +y-6M)7%{B N9P@IR PLHC3!"+326?4732#"/.+;2%'54732'4&+"=77%.+"4&"2%462"&1+ ?A -13;86q+?>%M5L9IDvFHr}}y-/; }FL@/+u/+//Zb/+4BAJkXV75RZ}1\@/"+&'"'>3264#"''7654.54>727#"'67\Nvyk{wJ=->j6bsչD}^?ÅPReQIZ"`qd2uTo^Zmr\_+H1<;nTkL|h1f7J?fPLPu#hT`^koQ7#+OUUP "+%!;2&#'54;27&'#"=730%+"!4+"=730%+";2&#'54;2%674'd4;7P77O 7on8P5q3>5P8nVhi/+''''ZOZ''Z/+''/1.+"+62327#"747654#"'654#'"57$72gL'D?bPoN#+@ XF+z=!V7bZd5oAf,RB)u?'9 ="T62 "+7654#'"57$72762327#"747654#"'5 +@ X GL'D?bPoN#C2?'9 ="T1/R3͞+z=!V7bZd5oAf,RB)R D="+4632#"'%&"'76232654/&54?&#".fP+9y=%'7'P=%1?#7))yN-F^Lbw>ۚRJ15/D'7d{H!!" a#TXN/FPN+skfH+) D% "+6'4&#"27#"&='675!27wq=5%dLssXB 47i  dk 9>ىBD:N+VfBu}wf+BIIC#"+#&'&;2�'54;&'#"757!32754&+"=737+">5&'+333%787PB Z ! 697 J69763 ^u;)Dd13H'%!0 01L''JI/.69DR @ @. "+!!4&#"32>2#"&54>+"=7!4632#".'&7#"'#"&543261t=-NJ%^X<-w7' DNq\=0);)F&m#k\nf?hj^Rv;\T'jbDt^u} !5ݻ@/)-)J!+:7ӵF/P^#y; /< 80%"+4.#"3276;2&#'54;"'56$3 32>54&#"7Za3dNhJD)3>5R{sV!>bP+T)'$  V}?i^M/+'''̚LV1'"3+L+D+0; 82-,("+232327#".#"'6767.576$3 32#"h^ɶJu=#Zs?#('#Pxץ`[R`ݬ<3ӿ(W^^K-DOD,1>` f lz53-J/qDZ"+46327632327.'.+'%'&#"'&#"'762>5#"'>54./װVN-' !)LO $)8^@#,'b14&s!#F9)F5#%1%= N-wDd/5'XB) iziͨ'(B`+/B`/%%1IӬZJ:'#o7fs3J=/ZX-G` =6#; 7D @84"+4.#"327677#"&'&#;2&#'54;"'56$3 32>54&#"7Za3dNhJD`Ze9V)3>5R{sV!s)'$  V}?i^{\ + `NM/+'''"3+-D90zY4-"+&#"#"/4.'732654&'.54632 #"'';2&#'54;2767=4#&=73673#"32&#'54;254;!v->/>H"hX4 = >X57F55-TrZ#  1f   =  / N=^/xX3"+;2�'54;274+"'7!'.57.+" #"'';2&#'54;2767=4#&=73673#"32&#'54;25= ) ?h!-'13'/"X " {R #P,a %!# )#/' Z+ " 1  1)%C    D$% /  >TrZ#  1f   =  /BD)"+7#!"54764#!"'673!67!267Kv:'fhZ'>! R5+s-13mr#>,;d;T u  F  ?D5 "+%3'&54>3 32672!"7>54&#"#!&'63`\)h F^!/- L-%TfGV %L -0!]l?fɠe>lZhd!'NƴHk\!dhZ?T5 "+33 54/3227&'!"#".54674#!>`\)h F^!/- L-%TfGV %L -0!]m?fɠdH=mZid!NǴHj\'!dh[="+!54&#"327#"5432FmqroF%uy'+q OH"+#"'3&70###&'3&'+&67>54'&5467.'&547367&746?B/ N -[ZcV !XV#ZoZ6 /TT ( XTd#53!C? +)'-! sd;!/bfU9d^%```;`Dn83R #ay-R> + jQJ^d*=9d\+o=T   -! "+23!7!654.#!"&54>?3RyT7V3w3m%54&'&546?3 F5/A% oR|=FX++#`buX1')@-/5bƃR u=X%''}y#"+'#6'4'4&57!"746?3!2 17V++'!V\u#%JZDW L!;R=; #""+462";2&#'54732654"'673EgGGg&2O1'5J~J^gGGgG-+//+- LG)+`S @ 8 "+"654!"6'6!"654&74632327&54>32&'#"'#"'#'67&546232^cN`@=   bb``   TUR >Y@87POLHFE?>7V8V#%#*%$& +'4&#"#"74>32#"'&63232654#"5>"&56762&#".7>54&BVTT2$#<+'D;uId`Ϗ +'!#3+\##JsVW - #!=>33+ +/-#7949)N?N?-qT#:!'ZTs > [3F-'-! '  3133!DJ1d@b a < ;KPX@Lb`  b  ` XUSU I N B@Mb`  b  `  `USU I N BY@`^[YTROM%$#*(*& +'463232672!4?654'&#"#"4&#"#"74>32#"'&63232654"5>BVTTToz5<;)JW2-H)5yxL(?2 F 1%#;+'D [}Z+K@50.@;d db`   TSINBY@-,EDA=;:43,K-K+%% +'%4&'&/!!#".54632 3"&56762&#".7>54&BVTT=>:; +{HLiOg#-      VVX - #!=>34+ 3-W 8g79{d%,   3F-'-! '  3133!FJ+Y`@] @<  bb`  SSR M =>YWMKCB*#+%% +'%4&'&/!!#".54632 3463232672!4?654'&"#"BVTT=>:; +{HLiOg#-      Voz5<;)JW2-H+5yxL(?2 F5-W 9g79{d%,   |3w*.ZH@+AM(3L3 ;uvnFUJ)% &"36^@43 32#"'&63232654"5>4&'&/!!#".54632 3BVTT1%#;+'D:; +{HLiOg#,      V+/-#7949)N?N@-qT#9 'ZTs> [-W 9g79{d%,   \"JM@K K*PX@7   b  ` ST S R >@?d   b  ` ST S I N BYY@MLJH=;64/.$B+'&#".7>=!'67323#4&'&/!!#".54632 33BVTTf$)== 33+3!f{uL=>:; +{HLjOg#-      V 31 ! '  31+Lj-nVf-W 9g79{d%,   !Z(H@2-+ " @,d dTUIMAY@*)BA>:8710)H*H,&#( +'%4.#"7267#"'&74>7632"&'6762&#".7>54&BVTT)1%k,7e@tKT6nŀC..0%UX - "!== 33+ --9#6SS"LJ0NL&#%,%% +'4&'&/!!#".54632324.#"7267#"'&54>7632BVTT=>:: +{ILjOf#,      V)1%k,7e@tKT7nŀC..0%-V 9g79|d$,   -9#7SS"LJ0:9RQNJHGA@9X:X75)' +'"674&.'32654#".5467&'&54632"&56762&#".7>54&BVTT'=N+;0"5J-5BuBJ>;q^?o}aV}VX - #!=>34+ +7.K)7 S+5PAb`XU V  U I M  A@?b``U V  U I M  AY@87jh\ZNL7B8B#%#*%$& +'4&#"#"74>32#"'&63232654"5>"654&.'32654#".5467&'&54632BVTT2$#<+'D;q^?o}aV|+0-#7949)N?N@-qT#9 'ZTs> Z+7.K)7 S+5PA.-`^RPDB-8.8,%% +'4&'&/!!#".5463232"654&.'3265&#".5467&'&54632BVTT=>:; +{ILjOf#,      V'=N+;/#5J-5BuAJ=;q_@o}`V}-V 9g79|d$,   +7.K)7 S+5PAK.PX@-b UQM =M >@+bS UQM >YY@MK?=1/%%1$ +'#"'>543;27"674&.'32654#".5467&'&54632BVTTA*? J`>!V/ '>N+;/#5J-5BuBJ>;q^?o}`V}ByhN% lT+7.K)7 S+5PA@ddIL@Y@ ## +'"&56762&#".7>54&BVTThVW . #!=>34+ +3F-'-! '  3133!1) @UK>CC+4.'&47327&#"&47>5314323314323=L# 6 4 #L='=L# 65  #K>1;Q-@*U K>=5>54.'314{~323314~z32301001002002=L# 6 4 #L='=L#  2 5  #K> $J==J#  #J='=J$ 1PMcy<@9 U K>edONpndyeyZXNcOcIHE/qS+4.'&4732726:332726:3327&#"*#&#"*#&#"&47>5>54.'>54.'314{~{}323314~z~z3230200200100101001002002=L# 6 4 #L='=L#  2 5  #K> $J==J#  #J='=J$  $J==J#  #J='=J$ 1X0@-E<UK= >OC" + "'.'&#"&47>54.'&47327:332776.'&47327+L#B$6(&011314323314hј +34 ",>1y5>C/0"+ $J='=L# 65  #K>=L# 66\P}%+ 66@.@<U >OI"+ "'.'&4732776.'&47327L#A@33fї+33 "-=2 y 5>C/0-1 66\P}%+ 66@ U0@-<UK= >CI" + "'.'&4732776.'&47327:3 7&#".7>54.'L#B@34fј+34 ",>1 y323323313020MZ/0-1 66\P}%+ 6 4 #L='=L# 4  1  #K>=J# 6 -h~>@;< U K= >jiusi~j~feqI" + "'.'&4732776.'&47327:332726:3327&#"*#&#".7>54.'>54.'L#B@34fј+34 ",>1 y{~323314~z313020MZ01001002002/0-1 66\P}%+ 6 4 #L='=L# 6 1  #K>=J# 6 $J==J#  #J='=J$  zM@J< U  K= >|{{|xwnmjTqI"+ "'.'&4732776.'&47327:332726:332726:3327&#"*#&#"*#&#".7>54.'>54.'>54.'L#B@34fј+34 ",>1 y{~{~323314~z~z313020MZ0100100200201001002002/0-1 66\P}%+ 6 4 #L='=L# 6 1  #K>=J# 6 $J==J#  #J='=J$  $J==J#  #J='=J$ 1i|;@8j<  U K >utgfc_\[MIqSBC+%.&#"&#"&47>54.'&4732726:33277>76'&47327&#"&47>&676&'.'+- V2j323314vu  R%/! :-! -IV{ L_'=;XC { 7&HP!7M?/2/7# %F\ 2 5  #K>=L# 6659C& C6#0\66(3,!aM6 2  1 )HR{$)wN4 $I='eI Z)@&U K>XWCCCC +%.&#"&47>76&'.'.73277>76'&47327&#".7>&y+- Vh|Kg+Q!9OB  {uR%/! :- - JV{ L_( =;XC { 7%# %F\ 2  1 *1{$)wO4 2 659C& C6#0\66(3,!aM6 2  1 )f{;@8g< U K >podc`\ZVSRaCCC+%.&#"&47>76&'.'.73277>76'&472726:3 7&#"&'&#".7>&>54.'u+- Vh|Kg+P!9OB  {uR%/! :- - |323314|a 7%I;010J^' =9X# %F\ 2  1 *1{$)wO4 2 659C& C6#0\6 4 #L='=L#  2  1 )IFj=J# (3,!aK7 %yI@Fz< U K >wvsom`]\SRqCCC+%.&#"&47>76&'.'.73277>76'&472726:332726:3327&#"*#&#"&'&#".7>&>54.'>54.'u+- Vh|Kg+P!9OB  {uR%/! :- - |{}313313~z|a 7%I;010J^' =9Xr02002001001# %F\ 2  1 *1{$)wO4 2 659C& C6#0\6 4 #L='=L# 6 1 )IFj=J# (3,!aK7 $J==J#  #J='=J$ 1Z.7@4<ZUK>'&# .- +%2676&#!"&47>54.'&4732>33#3%!<7O323314R%LB323GV}5  #K>=L# 6 4 #L=%F5@2 <bUM >  + '&5!2'.#"3272ߦox% -%+)vP/^Td Fh1K PX@UM>KPX@ZUM>KPX@UM>KPX@ZUM>KPX@UM>K!PX@ZUM>@"ZZUM>YYYYYY@)# 1.)" +%32>54.#"&47>54.'&473263 !"&TqBxyH7^u314413R%9ml)b@%\jlH! 3  #K>=L# 6釬 DK PX@.bX` U K  >K!PX@/b`` U K  >@3b`` U K = >YY@@>;743*)C" +%#'"'#&#"&47>76&'.73%&#"&47>&953  m61^{n5|T; R )l  o  e.=('( X )!* !\`T65 Mbq-6165V1/165 ,1BD!-.@+M =K=K>$$CC+&#"&47>54&'&473274632#"&;b  rw b;;b sw  b;LD;JR@;Hu{C65 C{q{C66C3LT4-RSBD -9[E@B M = K= K >ZYVRONIHEA>=86$CC$"+4632#"&&#"&47>54&'&473274632#"&&#"&47>54&'&47327BKD;JR?;H;c sw b<~~|{xtqpkjgc`_ZXTRNMJFCB=<95CC$"+4632#"&&#"&47>54&'&47327&#"&47>54&'&473274632#"&&#"&47>54&'&47327"&54632LC;JR?;Hhfb`JFB>;:541-*)$#*C +&47327"'.'&'&#"&47>54&'&473276732776&4632#"&?o`BZ (6(<9 b;;b  rw b;;b swcj "  #!RLD;JR@;H7660*+/+ - !  C{{C65 C{q{C66! 1  wN=3LT4-RS 1}9@= >O*C +&47327"'.'&4732776&o`BZ (6(<9 dj  !  #!7660*+/+ - !  66! 1  wN= {D]iD@AR< M =K=K= >hfb`\[XTPLO+C +&#"&47>54&'&'"'.'&4732776&'&47327673274632#"&;cswb<xtecXWRQNJGFA@=9E$$CC+&#"&47>54&'&473274632#"&&4732767327&#"&47>54&'&'"'.'&4732776&"&546323Żŵ~zc_PNCB=<95CC$"+4632#"&&#"&47>54&'&47327&#"&47>54&'&'"'.'&4732776&'&4732767327&#"&47>54&'&47327"&54632!"&54632 LC;KR@;Hvrd`PKIEK$$ +6?6'.#0'14632#"&&#"0*#*#&#"&47>54&'&4732703327?6&'&47327&#"&47>/&7V=3L@c;LD;JR@;H   RHK sw b;;b swRQ?\ n7 R{ZR {H PRR9c uvFL J?C3LT4-RS 65 C{q{C668-'66V u65 3f}O@K=>FB40CI +%&#"&476?6'.#&47327?6&'&47327&#"&47>/&w  RXR >3L@ RR ?[ n7R{ZR{H P RR 9c  65 N J?668-'66V u65 3 D_kzA@> M = K= K>srmljhdb^]ZP@<98-)AQC +&#""#*#'&#"&47>/&&#"&476?6'.#&47327?6&'.7327;3274632#"&>54&'3L@RR?\ n7  RzZNqw b~njgf[WD@?:95$$CC+&#".7>54&'.73274632#"&&#""#*#'&#"&47>/&&#"&476?6'.#&47327?6&'.7327;327"&54632>54&'=3L@RR?\ n7  RzZNqw b<;HLC;JRV7;`{F Nu{C65 C{q{C66C3LT4-RS{C65 3 65 N J?668-'66CMR63LT4-REvqzCV s%&@# <ddL>C+4&'&7672&#"&47>)b !;c sw b<f+% +L{C65 CL`5@2<b`M=M >#$%"+%#"'&5432"&'.#"327`;Zp ?cI83{V}VpjwN1A7);VsLb6H@E)5<ddbM=M >'"%%+%&5.#"326##"&'.543254&'&7672"'&"G1b53gP}%3%Pb>HG)RB)b !3R -.XT6G#S&07c,Wh)) +LX?',"$-#X=@:!V>< M= K >USONKG$C$$F +%&#"&47>54&'&7672632>32&#"&47>54#"&#"&47>54#"%E ?mb2/V -VXyg4};c sN D1t61DJND1Hry965 5{V1',#/=PD:Z}C65 GyV$$LyG65 GyVF b-->6@3UM >=;20&%#-+  +%2>54.#""# '&%672?!"%4#"326ByyG7_uQR1ĜZYhYMAMYZ1iu^8HyxBsRR%\jlH'dDR!]  釬٦Hlk\%BFh!)CK PX@ *)4"<KPX@ *)4"<KPX@ *)4"<KPX@ *)4"<KPX@ *)4"<K!PX@ *)4"<@ *)4"KPX@%ZbUM>KPX@bUM>KPX@%ZbUM>KPX@bUM>K!PX@%ZbUM>@0ZbUM=M>YYYYYY@CA86%$!c +!0&47>54.'&473263 !"&67#6'&'32>54.#"{314413R%9ml)d/#a2\N V`BxyH7^ua 3  #K>=L# 6釬9;LV[ w`6>*%\jlHb-6>X`T@QZI>< Y?7< bU M >_^WULJ:9/.,&$#64  +%2>54.#""# '&%672?!"67#6'&'&'&'476767&'"3267#ByyG7_u_1\N TR1ĜZYhYMAMYZ1/#`M\0^u^8HyxB`U` #R%\jlH] w_7=+R!]  釬!9;LV[6`w  \Hlk\%+ZVL;-5@2<bUM >  + >33254#"'676$3 )Ө+'+ %yo L6dTэPv7L5@2<b`M=M >%$#!+7473265#""&54632#"&7Tw38Jb@ qZ?ušTV;)7A1Nujp} "+3!!#&'&'5676=H#AA3/725T^wh?x3T}5HYeFP7/#XNLs"+&'&'#567673soq?#5B5F7k!Q];h-D+d +Ll+L`m{} "+%#6767!5!&'&'3GG@3/9-/R^woBt/X}5H^a9L;/\T`DV"+7536767#&'&`ZLHB;H3k!Q[i985k-+Lo+N`jwh'"+2$3&'&'3#67"##&'&'567673lX Jypu`{`)Xpj^3V\yh!A^u{>WZ#/1T7zqDN9/!ZNLk%`%% "+0567673&'67#&'&'5B js[R k7D$db)F7k#Obyf>f-B.fwjaNl`PLhqPferk3@(f "+'654'&'732DXy1hT 6l28hoZD%k/FVj%;3/9-/R^woBt/XoFl}or5H^a9L;/\T#-#"+!5!&%55$73&'67"sLgO|^_Vmm-uui\L^PhBmZn^NL`otjN/"+3$3&'&'3#67"###&'&'5676732e݁X Jypu`{`)Xqm߁j_3V\yh!BK_qu{>WZ#/1T7zlqDN9/!ZNLk%b}#f "+5!67#}jaRmf%}uʢAV3j^ "+!!#&'&'o\Rji^}wƠCT3}#f "+5!&'3flsURmh%}AV3j^ "+!!36767joqZWjh^}yĠCT3}j  "+!5!67#!!36767jaRqbloYRfo#}uʢAZ/}}CR5}j  "+5!&'3!!#&'&'flsURmho\Rji%}AV3}wƠCT3}NP( "+!>73!!!#!#&'&'567673B#?H1:0N7 FZ!+RX}dF+F7F/&|P}5N;L;/!ZN |P ; ( "+!67&')!>73!&/35#67!#!#&'3&'56767P-HPATF3\ 67iw^\*)t%)`{^wdu5?8Z\7D)'*@NX#/5R7^+#+\;T3/#XN%^NN#)("+%67&'!!5!>7!5!63!&'3#47!#ZKD5Nw9:7g7/ )T\yfFT7F:}//+h7N9/^L {#}P "+!!#&'&'567673#?H1A!+RX}d|+F7F}5N;L;/!ZN `P"+#&'7#567673")9/H+X1H3jT`/ D%417#h'N^mwVN# "+!5%67&'!5!&'3#4/%KD5/ )T\yf-}T7Fh7N9/^L `P"+73'673>7#&'&'52X 5A'<1H3k!QZ/7H 9. Rk%N`kwT  -+"+!!67&%!&'0'35#67!#&'3&'56767=GF3-HOt'iw^\*)%)`{^wdu)L7D?8Z*@NX#/5R7^++\;T3/#XN%` 1* "+&'7'6767>?#&'&'5567673"V ?%'NL'%A 1dP%h5F.u{ +H3h#Rb+ /%-7+7-%1 Tum`N m1 1jT\osVf"+'654'&'7327+A ?jh28hoZ{<)cj/;4mT/k9fwt61Dkh"+&'0#"''&'732767&'''..mN9G-4Rl5# X%hTj9]kH ;6FN!UW^kXhN"+%67&5477>7&'&'"'4775#J k% k35loY9k/-;3Bk [k3 #&#"273BnwF1HiX3 J1ߓ1H.vM;h ,&"+"32>7&'2654.#"#"&54632#"&546syR;c=% V_!'#!")\X7w ! 2G ;"+)"3!!"!!3!T3ì+=ZZts;\/ & "!"+)"''7&54;73#3#3!%!#"LIBLCT9K0*T12=~D(Ytdsr )r{;{u"+% !3#!!;\ɦ ={\{qodn1:J#nZ"+!2#!5!267!5!.#!Z3R>+!usLd7 & "!"+!27+'7#53#5!&#!!.3267R3LIBLC8L0*T12픕Dٱus *{Me;ys"+ !#53>7!5!.+5\ɦ >{\spodo1:I#o;VF""+4&#!";2&#'54;254&+"'523!2726?+;2&#'54;2-1-n7P5q45=AB764'#"'#.54>7>'#"'313?!'B313313323K_^L313313=L#  1  4 #L=5=M# 55 #L>d9!{."+272'&'&#!%2>76%!&4767 &'&473;50%3.1c`3:R335(/ ='') F%D;%G6 f 5 #|Rg/6!"+!7!{{}!  "+!5!#!!3'!s^^VoN{{  "+4632#"!!#!5!3;)';7+-^_R<32#"#".'#"&54>;J F_"!'w%:131)h%)'J%`5)-0$-5#6[ ?)Z+w0$#]/R13/ ?uw9 13159 "+4&"2%462#"&CwFHs}XZ)jYV85RZ}yTf "+"&4632=NT75VRTRnRO:7RP8  "+ '% 5!v/O o%"T i582><5"+4&#"#"74>32#"'&63232654"5>%'% 5!1%#;+'D Zo%"T i#8*- ,+(!"+&#".7>=!'67323# '% 5!3%)=>33+3 g{uv/O .32 ! (  32+Lj-oVo%"T iuL- + ( "+4&#"326267.#"#"'#"&63262ogT;-bLPZ;+5DNReѦݴPiըsJɘdFw`Fbw^m`?d"+3!!dV#"+#3{{#s# "+##33{s{w# `!#"+#3#3{{Z{{#ss)# "+##'33X{{wV{+{I-A 6m"+ #3#cVcum"+%3#3`cfVhcfu^"+3#4>32#4.#"eNsmFd1fLnhHDmNXs={N"+3#".533265eNsmEd1fLnNhHDmNXs=7"+%!2#"&#"#"&5463232D X-@5%#>P 7D8!#QVX!3%!/Dp=z7+#3HH  "+462"&462"&Z{VZ{VZ{VZ{V/;WR<9VR;VR;9VRf"+"'&'>3227#"&'&u7 yh1B;67 'T)o$5%$RiA;$w`)#1f "+"'&'>32327#"''&u7 yh1E=N@7 'T@GM-%$Ri$w`&U$y%! "+"'&'>32327#"&'&!5!57 xi1B;57 'T)o%5yb%RhB;%w`)#16ssuub3+"+"'&'>3227#"&'&7&#"'&'>3227#"&q7 yh1B;67 'T)o%4=5D7 yh2A;67 'T)n#$RiA;$w`)#11$RiA;$w`)b@="+"'&'>327&'&#"'&'>3227#"'27#"&''&q7 yh1''! 5D7 yh2\DN0"7 'T/9;67 'T(h&sN|#$Rib1$Ri&!$w`8;$w`%!oy#   "+&''67!5!5F3SL3;gyq'RXN\'_}ssuuy "+!'!5!7!5!!!N~[/vNrf.spwsu!u "+!5!5!5))ZZZZZZ "+!!!!!'!5!7!5!7!5!,^+:mNe+,u,_NZZZ+ZZZts  "+75!5o``?Z@s  "+75!%55wo``ZVw%X  "+5 5oo?Z@?[?#  "+%55-55unuo[Z "+%'%5%%L|^NNoj|[][H"+7'7%5'5 ęMNxMc\H+Z*["+75!7%5%%%!!'7 KNz$CBB$,N$֨`Zj|˔`k=N= "+75375%7%5!!'7%')#NN5 ,N$`zWjMZÝ`k_KLX+ "+%!".54>3!! 3NhLGoNVNsmGV{PX+ "+5!2#!5! 4&#PhKGoN}VMsmGV{Lo"+%!'7.54>;73### fONH\z@GoGN?VV%PimGV'Po "+5!037+'7#533 4&PGN@Vo;GoONGU|}V(QemGV'}} @  "+ 32!.>7!!!  oA e E y /*M}}  "+ 32%!. 3267  Ց y ߎŽ| | @  "+$$76 %7&$6''7 xWeMQeWPS xpRfeR_ge| |   "+$$76 $&$6' x[om"Y xm[\Z"ou} "+"&4632=NT75VR}RnRP97Ru}   "+"&4632!"&4632%#"&4632D=OT85VR'=NQ:5VRR9=NT75V}RnRP97RRnRP97R7RRnRPV+ "+! !@BzFm"+ #3#cVc+I1L5"+#!!oT%Z"+3'%2#"&#"X-?5%#=P3%!/A"+#"&5463232 7D8!#QV ?z7)#3HL<q  @  "+%>7.'# !#3  +d7+C\+v  !^1_1%q9Z/NqaNX'q @  "+)!!#3 3#!'o0|qZNX`Jo"+'&/76725hG L^߁CC!^L H# (NM'm"+7'&'63"'6hH L^!DD_L H ٸTMNT;hq @  "+!!'56 #!! 3l!v)^E.#=`+0]!qNXZ/Z;m'z3_1bu"+!5#"&'33267 ѿ7 NggyyDdgA]Y @@ >75@0b`UTIMAY@ ''S& + $$  32$".#&47>54&#"&'>7632cGuvmDFY.$$ *a< #%. =M&/) wvGFE(1!!1(/!%PLFPY N@ 37K#PX@1  b` UTQM>@7  b`U UTIMAYY@ MK+/'& + $$  32$463232672!'>?>54'&'"#"&WGuvmCFzL9 0IN7->Htq1#%*,C/"-wvGFEIH7X('0BH3C;C L*Zol.[LI,.A )-PY R@ 0 J @?  b  `  `U U  UINBY@IGDB@>97*(" RR& + $$  32$"#"547>32#"'&5463272654"&547>54WGuvmCF%*#)2%,dAz]8'Q]D1GeS,%2$+73X% ]nwvGFE)%$%1."Y;A@ kKhQ:A)('28Tf  rL^PY I @4  b U  U TIMAY@HG:832/+('$"II& + $$  32$3!2+.#&67>=#"/.547>73WGuvmCFB( p9C /  "#(#   wKc$wwvGFEZ 5K)!!1(5  (s+PY D@'-/!  <(;K#PX@2   bU U  URM>@8   bUU U  UINBY@CA?=$$3&'& + $$  32$4&#"'327#"'632#"'&54632326WGuvmCF9=1/=P5v+z{FEXt}F5""B!&$8?wvGFEb`h  r!f|&*&4@7b` U  UVIMAY@ ?>($(& + $$  32$32654&#"67# 467>7WGuvmCF_'4L7):X/@GALp5 @sJHHMwvGFE/kf?mRH&H&/H@/_X7Ug?B:PY 2x@ (/@)bUUIMAY@'%#"22&+ $$  32$"'67433!'WGuvmCF#+ /*1QL%P ˌwvGFE 62h h\ w PY %3?M@ -@*UU UIMAY@'&IGA@;:54&3'3$"  +"654.#"&5467'.546322654&/ $$  32$:&MB4+W>Jq`eH8|y8R'BH ( KGuvmCF;8R(#!o$+=^+CWleJj1&M:Usc^AV8J#'8BFwvGFEPY %@@ +@0UUU  UIMAY@ ?>($(& + $$  32$67#"3267>7.54>3 &WGuvmCF{'4K8)ƞX/?GALp5 @sJ$GHMwvGFE/kf?mH&H&0GA/_X7g?B:PY&2@O^K*PX@$  <@$   K*PX@6 bU    U  VIMA@B  b  b U   U  I VIMAYY@QPBAWUP^Q^KIAOBO&''S+".#&47>54&#"&'>7632 $$  32$"324."&54632-%# *a; #%. =M&/) yGuvmCFt(r"/#6c[69]f(1!!1(/!%PLFwvGFE(3\=? o;(W],PYO[i@MF-&D$7@5   b `  U T I M AY@ec]\WVQPLJ'''TS+".#&'".#&47>54&#"&'>763267>54&#"&'>7632 $$  32$.%# *a< *a< #%- =M&.) .%##&- =M&.) kGuvmCF(1!!1(/!%PLF(11(/!%PLFwvGFEPY t@U N L 37 KPX@A  b ` `   U I   VQM>KPX@@  b `X   U I   VQM>K#PX@A  b ` `   U I   VQM>@G  b ` `U   U I   VIMAYYYY@sqig]\TRKIBAb/'&+ $$  32$463232672!'#".#&47>54&#"&'>7632>?>54'&'"#"&WGuvmCFzK9 1IN7-=+`< #%. =M&/) ,&"Kpq1#%+,B/",wvGFEIH7X('0BH3C;C L!1(/!%PLF'2[kl.[LI,.A )-PY&2@y@$   W q @J  b  `  ` `U   U VINBY@BApnkige`^QOIGAyBy&''S+".#&47>54&#"&'>7632 $$  32$"#"547>32#"'&7463232654"&547>56-%# *a< #%- =N&/) GuvmCF&*#)2%,dAz]8'Q]D1GeS,$2$+73X% ]n(1!!1(/!%PLFwvGFE(%$&2."Z:A@ kKhQ:B*('28Uf  rK_PY&2@Dp@ A$ @?b  `U   U  TIMAY@FEona_ZYVRONKIEpFpDC&''S+".#&47>54&#"&'>7632 $$  32$3!2+.#&47>=#"/.547>73.$$ *a< #%. =M&.) GuvmCFB' p9C / "#'#   vLc#x(1!!1(/!%PLFwvGFEZ 5K)!!1(5  (s+PY&2@kFK1PX@NTO$   V HG <@!NT$   V HG   b b   U  U VRM>K1PX@D  b bU   U  U VINB@K  b  b bU  U  U VINBYY@jhfd_]YWSPMKEC&''S+".#&47>54&#"&'>7632 $$  32$4&#"'327#"'632#"'&54632326.$$ *a< #%. =M&.) GuvmCF>1/=P5v +z{FEXt}G5#!B"@(1!!1(/!%PLFwvGFEb`h  r!f|&*&4KPX@A  b ` `  U H   VQM>KPX@<  b ` `  U  VQM>K#PX@A  b ` `  U H   VQM>@G  b ` `U  U H   VIMAYYYY@fe_]UTNMLJFD&''S+".#&47>54&#"&'>7632 $$  32$32654&#"67# 467>7.$$ *a< #%. =M&.) GuvmCFs'3K8):X/?GBLp4 ?sJGIM(1!!1(/!%PLFwvGFE/kf?mRH&H&/H@/_X7Ug?B:PY&2@Y@O$ FEVK,PX@4 bU    UTIMA@;  b b U    UTIMAYY@BANLJIAYBY&''S +".#&47>54&#"&'>7632 $$  32$"'67433!'.$$ *a< #%. =M&/) GuvmCF#*/*1QL%P ˌ(1!!1(/!%PLFwvGFE 62h h\ w PY %3Zftf@XQO-KPX@5b UH V Q M  >KPX@0b U V Q M  >K#PX@5b UH V Q M  >@;b  U UH V I M AYYYY@$'&pnhgba\[WUNLEDA<98&3'3$"   +"654.#"&5467'.546322654&/%".#&47>54&#"&'>7632 $$  32$u:&MC4+W>KqadH8|y8S(BG ) .$$ *a< #%. =M&/) GuvmCF;8R(#!o$+=^+CWleJj1&M:Usc^AV8J#'8B(1!!1(/!%PLFwvGFEPY @Lg@> 7 A5 R @B  b `  `U   U VIMAY@fe_]UTNMLJFD''S&+ $$  32$".#&47>54&#"&'>763267#"3267>7.54>3 &WGuvmCFS-%# *a< #%- =N&/) .'4L7)ƞX/@GALp5 @sJ$GHMwvGFE(1!!1(/!%PLF/kf?mH&H&/H@/_X7g?B:PY (7lO@ Q UK#PX@<   b  `  U  I UQM>K*PX@B   b  `U  U  I UIMA@H b `U U  U  I UIMAYYY@"*)kia_TSPOMK<:0.)7*7$"((&+ $$  32$"324."&54632463232672!'>?>54'&'"#"&WGuvmCFt(r"/#6c[69]fbzK9 0IN7-=Itq1#%*,C/"-wvGFE(3\=? o;(W],]IH7X('0BH3C;C L*Zol.[LI,.A )-PY)7vK#PX@$U UQM>@*UU UIMAY@31+*%$  +"324."&54632 $$  32$t(r"/"6c[69]eGuvmCF(3\=? o;(W],jwvGFEd+.@0K PX@!M =M=M >K PX@!M =M=M >KPX@!M =M=M >KPX@!M =M=M >KPX@!M =M=M >K(PX@UM =M >@UUM >YYYYYY@0/ 97/@0@ . . +  %"32>'4'45<."'&5467632v-d"   mAb?3Cag>nL<;+uu11&;#< 7>7627367676?4'.'327&#">32737%%'&'.'&'.'%"32654.6763>32#".'&#3767674&'.'.'&'&72>76767>7654'&'#67>32'"&'".'&76326'&'"#&'.7"/#"'&74767&'&'"76'26.'&''&'>76726467>7'&3276767"'&5476327632676>76'#"'&71+ F ? +91' <!R& d4D"/!5)!#T#    2 o+5'%' /-@%#-1D;@ _< E 9B1w X9) @ 39B;=:ALwF{q7Xoq1TsuR1+ 5! ! 1h'ZN-'6 !WL95   7   !w1461D1 )%:%  9) @`! 91>N, :?  #   =X1n'-*^2 8'-VP5$^)B$6% >)   ' #5>'JD4-JRT%-'.-  / #R'%'/#Hq+W< -77-DF/ B!X%\j% TH+-JZ !CPpM,70fh}%3'oE }:r4K 5!0 <7F\"4  ;)9;-5X^Fy%+32327+%5&54675%632&'"'ph]5L+Dυ?ZJ^eXZLt. H/-<)=F>97=0)-;=BmZk$1H)$5#Th/ )1X.2Xfs@~uogd[RI20+"+'6%727232772654'674'674';2>54#&#"%7"&'#"'327;".#2>32#".5476$32"&54>?2!+"'&'263 232654'#"746X1ZD^%)'Rf4F%V)^wJ=9H9#bSY3\LG#8r N>bCH{8JCP#"4);'l6I'3HF{5b!_;L5NL?FL c+1+9ZH5!#?!%%6) !#nP= y L+1+RP7Mg /)AVJF`1-+#5gH1Fm- ^ "+32654.#""4#"#"=27632#"'6?654&=737+";2&#'54;254/;2&#'54;2=;2&#'54;2?675&'%&+"7577-'{'NC50-gR:ہLmH1-/)-Yt"#e`)T5Nq>NGJ1N1s'g`%;`Tj- 'RR % 7ZX0 %  H J  BTTP##Pk}'  J H  ; '/ @ .*%  "+ 5#"".54&#"632&4632#"$462"Sm+X\Z@ K?;6) "+462"&%462"&2332#""5#"&'4''676  6 5467&'77>7#6V7N77N78N77N8=!) 'Fl/>+3 Tˎgš3N#{/79DN"###"### #N'-%=ߠ? '?'j9_TN1풼V/ '0M@ C1-(% "+462"&%462"&277"/&'7  6 5467&'>#6767f8N77N87N77N7JRd3n!s7h)=ˎgǛD? ~6{#M6N"###"###}#6767B ' ;-"NZ'@sgǛD? ~6{#M6{ZZZZ56 -HQ#_9hj'?3+B 4! "+"32654#5&''7&'#5367'7675373#'5תqQY ^nZ ^lZwVrqTu`qXYs^{PqNjǍ6T`quwbqldx99r^oo`ks`{7f"+32$65!"4'63*^ ubo8ͤ)/2+y-9Fd#hDf"+5&547&#"!2&547ۺC\uRoˤ)1{1-y^e#mzhP'40"+" 6544&5&5467.53326533##'#5m͉wjijyqbq׬ɋ#"'{?=Rj`T>Dl'WHn')nP'j&""+" 6544&5&54323##'#5m׬ɋ#"' 첚'WHn')nL#""+%26& #"546?#53467337}٪HJ%'o'&oR % "+" 654& 543267'>7'&';}mkB#FmLѕ\Z5Ǝɋ 1)V/[^/ m`=9'"+"#"&54632!"5723'"&=46327>543) ?>7FhHT^B +!h-c%A;RJPP+'#/1'`{=/^Fv  + )1"+732+632'&5476'4&#"7'#"&546;ч`JB!}w{m=5CeFE'c+^#'9#L"5JoPj5!qhÏ?e`E&+Hy M:"+%72654&"4+"=73737337+"732&##'#"&547#''54;2'F? 6"hA;Hgt8r>hg>BA=Iej!Lbd`!wd;B q=3%><P^R-/PRJ\3&#'%6pD)%Z``7- -!_ӲdeZ-E%N @3-+"+7327654&#".#"7&54632/\r5@RRTŏs{PU{iXNRA3s]F\=muf|썘w`wR? 52"+ 654&#"'%./&+532276?673#"#"R`5-X@Be ) F.BB-G@d?BX #V`ՓӜ #\aRoHy))yjo;@$? "+672#"&'5! %$! 56D^=`<-ods9gh:p^Q?)wVVw-/+uZZu$?;s ,@ @ 4- "+264&#"%"32654&"&5467#6! $" 3! %'7!27&54JbgEH`bJbeGFbbT܌wHj5ŁD#9%1ywwVXw{VR{sZXy\Lf3o`!`2o+ ;4"+"32654&327#"&54>54&#" &546;&54$32D^_ED`^5-!TVD={w)?7&''67&'4'7ys/)+T)+Cp^--'FxV'= ˉ1^EO}R^E8x';BD+3u'F?#b'4by=6h'O+R?YmQuDs 1 "+7"&5463!2#"&5463!&74$32!2#!7654&#"#&?!JB!N!(?!mm)JB {TV{{ s7+;\7wQәZs);f9dgCf9;3"+?6?6?057%754/&54''4'7'49o`)+-;'/j:u3+L;+R'+X'=@D3!DT?8!99 {3HuC#^EP{T^E)'}""+&'''&'76#"'73+9#Rdv95udStV\!V^}){s#Rdw95vaSu1o7X C5"+$4&#"'2&''4'.'76?>32#"'+53267.'.'&A5hp Z+!9L#<7f%!BhD1 D[ωj%\Jo15^qoN' Ѩwd'/>?935+Dy-o'T/LXDVy:l B"h)=!*WW.*"+7632#67632>7632'#"'&'#"'.'632>767267672'3#"/3#"/he) \!hg !'m!#% ' /N# aHV!% {##+-'bk+ "% }#%}1 +ZP !pZӰ Dh ! q ?3RVZӰ  'b!!"+'#'6#53'73?3#y<ow!m{{!͊=l lL}""+"!5674&#"#"&7#".h?dh%2+iJXhTm^Jf+/u++?@>K>odju>K>qA!"+%!5674#"#"&54>323254.5463232632#".#"o/1`#X\s)dL%FVEGTHKRs1v;eD51  L{ ++w\_yBzkg#75oG`gHp57iDhV'y'67'7P "+2'&'&5463232>Ry7HTLPqVD--FŔJ}\NuȞ323'654'&'&'#"&)1F,?VR7Nm?c'=c/1L%3'{BR%obP+"+2$'#"&54632#"&546\H1NmZF0m!NTbPF\m5p`PF\l+""+ 5$'2#"&54632#"&546PNH1mZF0mHnNl!bPF\m5p`PF\lT^$R^g@~ma_]W1'""+%"&54!2>54&'7#" 67&'"&54>7>2'7;/#"&#"327654&#"6#"5'>''4>767>54#"76727254#";&BW)#)A#BC5HK%lB͐P9$_D?\PN}r8-- , 1  #-`9dVE$2'(Q:+% 4Q=-F=7oA ,5Td _? )-\$L7BXZPPiC?_5)   FJNJ!AFAB)?NlZF+T 1 ;J'5 C D9w   1 6/"+2#"&46'"325>76'&#>54&#"5BL+/HHV !-!h5-)+u#m j3HdtLRqcIuD;7HLfL5!@"+'M1h))m-N fQgPTlpPNmPNj DLZ@ TNIE8'"+%4&5&';"327&547&4&5&5432623##'+#'#56'632654&#"`NM[N]LmiKӚx{׬NNOL^[Jj#"21WH9{7#"' KL첚'WHn))')nBHJCCJH9ɋ7R#19GU@ OHE<62*$"+%#"&'&543267'>767'&'"&'467.>57267'67&'32654&˜4B#FmLѕ\9v\Z5O}s#FSGR6#FmK+)&S"`q 1)V/[^/ܭ2c/ m`vƎ0[p,],]1*W URS2^uȌ~PH! FU_ @ [VNHB%"+"327.547&4&5&543263267'>7'&'#"'3##'#532654&#">4&';NYq]TWaB"FmKЖ\Z55?YS'+NT8?A:wAd>eʅ##' 1)V/[^/ m`1k" XGo)&oDȋB_L0/fRV :6"+"326544'.54327'>7'&'3##'#5l?#bJϑ_151jm|ߨϋBw% 71/+V/Z\/ʑ hc75=}"{lM=l+Vv #A6{]""+276747'.7>76'27>'32767'&767626?67>767&'.'.'&#&'&76&"6'&'&'+632#&'&#'&#6'.'&676%&#"32#"'.'&'.76767#'7'5&'&547>767676762>7.#"#&'&7&'#"'&'476323&56767#67767&'&"./&'.'3&'&7&#"'&747626767&7>72'#"'&72636632&762#32746&'&"'&747676.'62"'>'&'&7476376/&"2"'&7#"''&762+ F ? +91' <!R& d4D"/!5)!#T#    2 o+5'%' /-@%#-1D;@ _< E 9B1w X9) @ b39B;=:ALwF{q7Xoq1TsuR1+ 5! ! 1h'ZN-'6 !WL95   7   !w1461D1 (%:%  9) @`! 91>N, :?  #  1 >X1o'-+^1 8'-VP6%^)B%5%  >)  ( #5='ID5-IRT%-'--  0#Q&%'/#Hq*X< .77-F/B Y%\k$ TH+-JZ !DPpL-7/fh}%3'oF }9s3L 60 <7E\!3  <)9;-5X_Fx%+54&v-VX - # ==33+ +uu1F4F-'-! '  3133!d+6 "+  463232672!4?654'&"#"v-\nz5<;)JX2-H+5yyL'?2!F+uu1L3w*.ZH?+BM(3L3 ;uvnFUJ)$ '"d+;$"+  4&#"#"54>32#"'&63272654"5>v-1%#;+'D;uJd`Ϗ *'"#3+\#"Jr+uu1:+/-"7949)N?N@-qT#9 'ZTs> Zd+.1 0/ "+  &#".7>=!'67354>323# 3v-%)=>33+3 g'kh{u+uu1132 ! '  41+Lk-UoVYd+1"+  4&'&/!!#".5463232v-<>:; +zHLiOg#,      V+uu1V-V 9g79{d%,   d+- $ "+  4.#"267#"'&54>762v-(1%k,7e@tKT6oāC..0%+uu1Q-9$6SS"LJ0543327v-)@ J`> V/+uu1ZyhN% lTd+#= @ :, "+  %"654&.'3265&#".5467&'&54632v-f'=N+;/#5J-5BuAJ>;q^?o}`V}+uu1 +7-L)7 T+5PB;RD)8$%LkVTA9JFBhLlSd+(  "+  32754'"4632567#".v- -&/'jJ}{uITC3)?a1+uu1!=B5P;D++%1A*d+(N` @ WO<)"+  "&56762&#".7>54&%"32>'45454."'&5467632v-VW - #!=>33+ 8"  mAb?4Cag=nL<;+uu1@4F-'-! '  3133!i1&;#< ;"8#- (8BI&$ ')F&+SDgZ2B5_t/.f  "+#!!!u5+6Vbef "+3#%!!5!!uu=3-VbeVJo"+'&/76725hG L^߁CC!^L H# (NM'm"+7'&'63"'6hH L^!DD_L H ٸTMNT)o->͵K(PX@2Z   SS  K =K>@0Z  U   SSK>YY@>>985/,+&%V2+#3#;2676&#!"&47>5#535#5354.'&4732>3H{#3%";6O323314S%LA313oPP}5  #K>RPP=L#  2  1  #K>,) K!PX@0 bS = K  =L>@. b   TS =L>YY@,,('&%C+#3#&#"&47>5#535#534&'&7672!!!;c sw b<)b !oPP{C65 C{HPPf,$ ,Lo-G@&) F6-@,bZU UL>Y@A@=82/%# GG +"'&'>3254.'&4732>3327;2676&#!"&47>5&7 xi1 314S%LA3137 g^3H{#3%";6O323%Rh=L#  2  1  #K>%Kc#â}5  #K>);>H@E3 <  S  U M =K>>>;964&CC +#&#"&47>5#5354.'&472$32"'&53265!"bZ313323314)4)sw+)+<}fN-P\=L# 65  #K>PA=L#  2 C^pb;+ImCD; K@.; @? KPX@Ab  bb`  UM ==M>KPX@Ab  bb`  UM = =M>K!PX@Ab  bb`  UM ==M>@>b  bb`  UQM =>YYYY@IHGF-D($!"% +674'&#"32#32767632'"'&'&54'"'5>763 7'"&'3&'9FP\H=9E#%>1% T7R{XZ%#J??Xwy9BHTj ?Dh+ qR}=H F6''!##XZR#h}+3bPFZ3:AV@S&#<;984. <dbbeM=N >"$&$&*# +%#"'#7&546?.#"#"54>3273327'%277] 7)!?RHy$]K@,ddbe M = >Y@:942/.*(#! 77 +347>7>3237332+327#"'&'#7#"=67#9%6 Y N1T1N1gg4'P$%!RfD*T #ny `EVV @7i HA-E9%K ;"-RK,PX@@:+%<@@:+%K,PX@-   U  SCM>@.   U  SOM>YY@#RRPOLIEB?>=;85320-*)#33#23+0/54;2'!;2�'54;254+"=730%+"!4+"=730%+";2#.=6BA6=P;5DD5=P!;6B;A6=O#;5D !9}RRRRRiRR 5(59DK,PX@ 76 B/*<@ 76 B/*K,PX@4b`D= M= N >@5b`O= M= N >YY@ @=:841.+(%!  DC +%2354&+"&54&#"#72>3+"63754+"5672+"61y ;  qk7-\R@ +';H=')SZ)';5'6hs +wRRgR/\@0*N7@$  UC M>Y@\\ZYVSJGEDCA?=3#333="+%054;054'&'7;2&#'54;254+"=730%+"66547#"=307+";2#.\;) eBA6=P;5DD5=P!;6BE/ =GD(NDd; ? {58eRRRRF)55 @_B%5`_@P4 K1PX@Eb  ` b  `D=  K =N>@Cb  ` b  `  UD=N>YY@__]\YVKHEB@>>=:933:3+%0546;254'7;2&#'54;254'"#'4&527673>?#6543#"'537+";2#.+#'B+%=P=%+ ?VX13khV' -"9H+9!RoC7/?k5 '& 9 RRw+ rXH'35  B\3#RF'?-7\-@ <:K PX@YM =M>K(PX@eM =M>@eUM>YY@+*'% -- +)"547676547!"'3!2!2#.Rd 5R"+ \^5 1; y'#kt8 LB17J @ < :K PX@YM=M>K.PX@eM=M>@eUM>YY@    +)"747#"'!2!2#.'fs#8CN%;}i#RyH7 N5/D@@?;:6 KPX@%bM=K= >K1PX@%bM=K= >@#bUM= >YYY@ 43/-(&$! D D +"654'27'64+&=37+"'.'3&''6767&546LR_P@7#+ #5AD JF P/'2 +#; /TB53H=3F>)+l8-)Xb+%519D107 A4oMPs-C@>8 +%K(PX@:b`U  M  =M= M>@8b`    UUM= M>YY@C@=<;9630-#2"#6!+327>76;2#0#"'3'&+;2�'54;254+"=730%+"% 7J3  q9 *C6;N;7BB7;N#;6CeT' CB97c%@RRRR9yC@ #(=8 @5  b `U  U U  K  >Y@B?<9631/"6"3#1 +4+"'57307+"327>76;2##"'3'&+;2&#'54;25+%=G>'+% 7J3  q9 .+'=P>%+PPT )  7Q%=RRR%+=@eM>Y(&& +6765&4>767467632#".P>dPD11s!; @b=P)@;}XAhúq9J}F}I\"FhwDs@;woK/m7  "+'67%>7'67%>7uTF/5D##41HTJ/5D!% 8/R1 !4 NfD/9E ?:72%"+%4'&'4632"&'.54>76767>76326"&5462>7675%\+ /0U FEA)`-;T #',gXZ{VZ{V+m3#'X%C#&21.,]3[V '7$#?() [FJ;VR;9WSB:Z/W7- @:d[ +%#5%#7yۚrB`7+ @9d[ +%53%3%?Ѯud %:\t7AP {miG;94$  "+#"542'4#"632723&#"0432&'&74327654&#"6274&'&654&'32>7632&542674&#"2#"'32?654'.#"2772>767>54.'&'.#"#"'&'&'"327654'&'.'#"467'#"3274&5432654'.5.'#"'&"#"'&'.54654&5476727>7&'476767>7654&5322#"'& g   8!) !7&)*#J'/7% S 1'.!' MqC=7T%;T3 !u /;Bn'G>!T#O3  [+= -aQh#;,$F K4!1'5 *B@I!$J>)-'ol75(5R=;< %-'s?'L/o(Z2'-+)-*  e!J\A  71LT  >+I  f1-  - X'9) 5`!' - PBTb s75_ 3 R F-+ -)14')dP3#A=' CC# /P `BF " G ^'+]F!Hh !*32A`/ J 'B%B!=$ 71%&1)/!)%1- q 3DX +.-\!'=;;?\7G( 7- GZ 2    (E0'W! sOV1lP/:\T;^D1$0!  ?Lq`nfa2"+"&'".547>32#"3274>763232654&#">32#"'#"&54732654'&#"632'2654&#"-9g`9/~!+1JTCoP+݋V1;e5+!+!n>Xdm{s/!?P 4&'1{޼eǕMqlhdn~xpI"+'.7>676'&'&'&'&'&'.7>.'67>.'&767.'.67>&676'.Tq(REB1N3'3EiC'rk~L&L\~H,.{'->7Q<"!nZd/DŽ1] /LQH5+'@$APʫr[a6EVwtk MP^ 6S'1&.@GY/j8#6p*-  7_ SH3 '+;o2 ,2C:"&CFVaש>: Od.9 1Jl= !G& N_P $ ]aW$BL Z/)E7`\%,tQ:fq_lg`-"+".54632&'&'"32>7#"&547"'>7.4632&#"3276323254&#".54632#"''27654'&#"?jߖ{1''3 OB#3qzjg[=m '$+6e;1V͇+RlDTG1+!1iP-ޡy`;lJ8`ZP^7mV!78 +#npaqEH1XP5318ZGq=J##5^v6RvTk#wHd=NL{H "+!5!5!5eHRRPPPPNH "+!5!5!5N5-5HRRPPPPdX) '""+2"#!"$#52$3!2"#!"$#52$3462"&95hdn)BiFNH5hdn)BiFNH^Z{VZ{V'! ## #! ## #;VR;9VR#`) '""+2"#!"$#52$3!2"#!"$#52$3462"&!^P;Ӎ^R;Ӎ Z{V[zV/')))'))) ;VR;9VRy1^)N se/"+4632 3 ,323 $! ! ,32 32,32# #",#" # ,# !",!"! $%&"!"&463 $3 3 $32! $3 32$>32!"$#"! $#"!"$ ,32 ! ,32#"'.#"! ,#" ! %.54632 y! q "~h3!]-5!Xoi)T}J!  !7'ju+@+?IN!x8˶ s TǑf!i 3FH3JH-8-Ѱ^^GXHDTC! ooHXH:G:YVGH-5-n . }`'!XCmdeedFE-N ;2edde>!#P+:"+327#"7!''54;2654+"=73%+";26=732#'HJ$u>?)o7P5q3-ZVu/ydX9%'#8Z''Z"/7V`') ) h ';F],IU_;A]XRLE3)vdQJA<  "+327#"5!''54732654&+"'5737+";26=732#3654&#"2#"'&54632#!32%&#"&47>=4&'&7672632&#"&47>=4#"&#"&47>=4&'&76724632#"&&'"&47>=4&'&76723632#"'&#"3654&#"2#"'&54632#!32%4&'&7672632#"'"'6'&'"&47>=4&'&76724632#"&32'4&#"%HC#q9?(293K3j1+TQo/. & &VxAKc"^'\,FZ81a`6"8\I5Z'R#O8\DFZ81,&"*.%!*8\DFZ80BN=/)&ʟ. ' 'Vx@Kc"^k9 KZ*l3?en<8\DFZ81+'"*.%!*"1m7-/\T5#$!3-)%%VH-6R\%' )@<$~:Du$ 2E F1 #)`G&'FW6F F1 A;A+0..FF19#p*$1"@<$~:Du$ >;+MA;Hhy/ALFF1 A:@,0/.)RU 7 1AN[jy@woe`XRLG?5/' "+"'./472632#47632"&&'&'4632#&47632'"%4&54632'54632#"&%&54632#".'&74632'%4767632"4767>7632#"&'&5432#" !))!3%  % !$ '  #{#'}'%'  '  ##%  '1 )%#;#1#)   %; #!=)%)FT )NT +L- #% %:A   #'B#'1 ,(# "+327654&#"3>32#"'" "32rFPRy-G!+%;@7Lj/\Z3GVX} b.J1j{GL\[9o!!B#0B @ 72 "+%"  32767>54#"672#.#"/327''"'&54\Za eR]wio)5H5 TTX=Q^b1\Zal +T=ERh$"+%$#&47>54&#"&'6$762E:55:E\y+;8G?(#=L# 65  #K>H&39{!#ulD4"+4632!2672!'>?>54'&"#"&ZȼsX )J!qyU  F^!/-%pL(68BDf H/5DqmT%>-;JgoNhZhsB-HtpEHd) />FT9"+"#"547>32#"'&546323265%"&'47>56!-7% 5?N9+DcV+=}hJm~D9+N7BTN)98-79KF5'/-Zdc1r}Zd?#=9KV* tD20"+)2+".#.7>=!"/.5476>73-dy&;55BN%55;5)u83!-R=L#  4 1  #K>R% ?AR* "+4&#"'32%"'632#"'&54632326}^LH^{Q%1F/'/mjlR53f4;6Vb-b  /ɝ9+A':P\V &% "+2654&#"67# 476$7Z;PtT@Hb nduR#1cr?onw PILb o;n: Job-HS $afZ3"+"'67433! '1)7BH%B) Lzw7 :QN#CF %3 -&""+"654.#"&5467'.546322654&/Z9wfPBUas+oV;hV=en1>[V=51'9B#^DfυݥqK9wZeVs5;#Tf+)f &% "+65#"3267>7.54>3 &;PuT?Ha nduR#2bsnow}Ib o;o: Job-HT`fZ'5("+?3762'.#"!!!!32676# '&#573&547''z/%))'e}'|c'd'))%}&'q= s+>P $P֔=+V~@%K$ "+#&#"&47>=!735!73&'.'&4732776&'.7327!#E  n E'2Q= {P R$1I  */{  BJ% jV{965 9{VV?HQF 2 6#%! I'!665<'2*VJ*) "+32654&#"67# 47>7^ )LE-;PtT@{oCnduR#9if/yzm)P3D!wHb@Rym: Job-FVΙ&ym{$) "+7327654"4632#"&)?03P5wq^mDRXou;/LFú{H  "+"324&"54$323%;B$sTTZ^7L -d}h1jm#"+%$#&47>54&#"&'$762F955:EHx+)}?(#=L# 65  #K>9H'33{9"umM+("+767263!67&+&?>'.#"u17+7/B%3b0 ?N .=P;=*M}xsX)JB),9o-/5 b o(">e]ZN9"+"#"747>32#"'&746323265%"&'47>56!-8$ 5@N9+CbW+<}hJmD9+N7BTN)978-79KF5'/-Zdc1r}Zd?#=9KV* t= 20"+)2+".#&47>=!"/.5476>73'dy'<55BN%56;5 )u83!-R=L# 4  4 #L=R% ?BB* "+%4&#"'32%"'632#"'&74632326m_KH^{Q%1F0'2mjmR53f4;5Vc--b  /ɝ9+A':P\V &% "+2654&#"67# 476$7Z;PtT@Hb nduR#1cr?onw PILb o;n: Job-HS $afZJ "+"'67473! 'H)8AG%B( L{w7 :RN#CF %3 -&""+"654.#"&5467'.546322654&/Z9wfPBUas+oV;hV=en1>[V=51'9B#^DfυݥqK9wZeVs5;#Tf+)u &% "+65#"3267>7.54>3 &q;QuT@Hb nduR#1bronw%Ib o;o: Job-HT`fZ?GO @ NJFB"+!#&#"&47>7272&#"&472>&'!"462"$462"/71x) 5[l)65  N2bDDbBBbDDbBL "* @ )%!"+"32% 76$3 462"$462"ϓ㮚s[AcAAc>Cc??c3-Jӿh% lz%bDDbBBbDDbB 8@H GC?;#"+32>54&'&47327! '&4&'&47327462"$462"1/jdN- @} I; 7.- 煲;c^  {A'AcAAc>Cc??c-Vm-f?\xRy7 2  1  %E@/Vu,Jy= 2  1 ;JbDDbBBbDDbB-`'"+.73276&'.7327"' "&'&'.'&47327'&'.'&473277>&  (/gHT.l+#`  %  +=-'558.)4M (^*TI*/ qd =+KR#2  1 ZYRV2  1 8** -.- `6 0  1 BWw ()7 0  1 BX?D'-%q[E%"+4&'!&#"&47>5#"=46;54>7672#"&'&"!672#"&5463232654&5^*G6S ^wb5#"=46;54>767232+327#"'&52>54&#"Q6S ^wb3232+327#"'&5#"=6;47&# 632"&'.#"3272#"'&5467&1&΢BY 13'P%%!RfD= #397{I?cI83{V};ZpTXX  `E @s'HA-E95}/ ;q9L/N1A7);Vs@Vpjwϰ/&'j(2:DekiGA"s?zlifWO?;74-)&"+2>4&5463232632.#"'.'&'#"&54654'"3#"&54654'327#"'.6&#"2327#"&#">7#"&#">7#"&#"#"&"#"=4'&#"#"&54732&5&#"#"&547674#"#"&54767654#"&547>7674&54632676'4&54632632432327'>54#"#4+"26"5463274"326"7463274'"#"54>32'"5423254'#52"572""32>54.725432>32&54243632632>#"&/ #"47>54&5.##".547&543&546&54726262&5"'.42'&'42'&'4324&4274&54323267&'&'&5432>7.'&7472&542254&423'&54+67#"'#''54732654+"'5737#";2=732   !"  # "5432  '% !(    D3!!%dL!)#-&%RLF<  B6')#)% 3s+#"  7'+/ P %#!- +O#!-P# ! *1%1)H!L+5!0+%`g'!! !3 D+D o !k i   P -@?-lB )6--?D #H     !  ZF\@'# 1- !    ;%' s1  vV/HUZb ''F @^ !F4&546322632&'"#"'.'&'#"&5465&'";#"&54654&+2327#".6&#"32327#"&#">7#"&#">7#"&#"#"&#"#"54654'&##"&54732&'&#"#"&5476754#"#"&54767654+"&5476?654&546367654&54632632243237'>54#"#4+"26"7463274"326"5463274'"#"54>32'"5423254'#52"546""32>54.'624&5432>324&543243422?2672#"&/#"47>54&5.##".547&543&5472&547243262&5"'&54323'&542'&5432;4&4254&5723267&'./&7432>7&'&547254&572374&572'&5"54327f   1.P  $%!!)   D2#!$dM%)  ' PKG @!  B7+)"(' 3s )#" :&+/R  #!- +P"!-M!!+3#2-C !N-J;/)!$%`f'!-!/.  E -F l !k k ! O -?@-mA )5- 'aI  !H !  ZF\@&$   )1- !=  !;  u1   J  g7 /0M  )++) # 3%' + ) 1!P5H%-9Nj-!*$?BJ-'''#)XU': /#3HQ+)"#9mU # ++#'1%fJ /;IF# !)%5  -&32;  !  51 08"* /06!% &1   !  'LjiAVJ=]F`- " 0V 6   @ / :}\=/!     B qG?     ( +=5+ %   LN uD-9r QA3." "+632327# '&$#"&'>7&76$3 "32"'&'# 4&'&47327327&54&'&47327#R׮"dٙϲL/5<2 54&#"&#".7>54&+"&#"&47>54&+""'6743)2>726 V;c sN D1^FM[/F Jb  R7)`5*BqyF+6dh}7)  %B !Z}C65 GyVPJ:HyE65 Ey};hi{;65 ;{dgbw {)?Ldb-)SO;%"+632654'&'&'47632'&#"#"'&%632654'&'&547632'&#"#"'&b-%H-Lib?Fhuq5Nr+8oO?݅-%H-Khc?Fhup5Nr+8oO?݅+ o6/q.?kFTXHj1^{1%q.EYFj}-V o6/q.?kFTXHj1^{1%q.EYFj}-V7D2ecI0"+'.#"'"&#&5632654'.'&547672'.#"'"&#&5632654'.'&747672^#D#=_=P^RYj}%'F!j^w+D{q##C#>^=P^RXj}%'F k^w+D{q }=V`2cVN)``u}p( ^L\^:Zjb6}=V`2cVN)``u}p( ^L\^:Zjb677"+3#&#".7>/!"&#"&47>7267y Z833 j  XMC) > % o7u LR%'#^ // 65 -65 1X5 Jm : , "+32654&#"32>54&#"&47>54&'&4732632!"&y\3HIL+ 33J`+\wb<yH+'BdN`1RP-v! HKHuN!{ ӮeJ/+"+!"&47>54&'&473632!"&732>54.#"uwb<"'.+;2676&#!&47>54&'&47!272'.+"3oCBA Gk-\Vo!/'!cds??s;5/#/%fla%7#K  )f+i D+u h5 ;y{;57uf?%J ="+!"&47>54&'&47!272'.+"3267>'.+&os??s;5/ #/%fla%31j@BA =m1@b5 ;y{;5o#uf='%I  DKPC  J${C6N+"+4&'&7327% 4!2'.#"32757XI\{^R?'5qBV\91 !$#"Po%bdFPJmI"+4&'&47327&#"&47>=&&#"&47>54&'&473277+^kw b;;b rm^++^lwb<554&'&47327267>76&'.7327&!-'\;cswb<54.'&473273d#3%!<#y323314SX323GV}5  #K>=L# 55 #K>DuB "+ "'#&#"&47>76&'.73%&#"&47>&'7T RKBZ-& >7<   N()'0  bT  q?65 MT&#'5356TI65 4,3B64"+%&&#"&47>5&'&47'4&'&47327#&CD% 1b  {I\{b15R 1b {\J{b2 3+BT2G{{565 7{1? 1 '+{7556{'Nh "+"32654&#"$5432Pwlp{oi'$yhJ5/$"+3254"&#"&47>54&'&4732$32#"'1RP);cswb<54&'&4732$32#"&/&'&2>54#5Fqwb<32'&#"#"&'&T-%DB[rDJE/Tw5L7JBJL%? 75m7`PTRfL3>8dB+X>XMZq f9f."+&#"&47>54&#""'6743;272#.#">x  nj y=1Z`F!7) Ϩѻ8LoDI65 IwET { T?4"+4&'&47327#".54&'.732732>5Fd 1=  dFJtV8;b  sw  b;F`5{E55F{ )GVk}C55D}&<^R9w,"+&47327"'.'.732776&K-[ (6)<7%8G  pjO" # m551*+J/+V< 55 @Ii N>^= "+76&'&47327#"'##"'.'.727677!#P5PZ );+-703#:I  R 5>#%wLQ55:-+[+ +X= 55HDo  ZfM"+%&#"&476?6'.'&47327?6'&47327&#"&47>/&w=R{mQ L 3N> hy ?\ o9jyTDhFP- 1_C RR 9^ /)65 dNE55<Z 550; NA65 8>"+%&#"&47>=4&'.'&47327?6&'&47327FyF 5JA {Z  T'7)"+3%2>32326?2%"5477''674} ))/d<$NT'=-/wq5/)BdDX?? { N3 "+!"5463!2/kL?JA+Z+^h7F D=7"+3#&#".7>/!"&#"&47>7267#"/7>32y Z833 j  XMC) > % o7u LR%'#o/R1^ // 65 -65 1X5 !)-h7E ?97"+3#&#".7>/!"&#"&47>7267632#"&547y Z833 j  XMC) > % o7u LR%'# N''' ^ // 65 -65 1X5 1/! N7B ;87"+3#&#".7>/!"&#"&47>7267&''67y Z833 j  XMC) > % o7u LR%'#RF4RL3;^ // 65 -65 1X5 q'RXN\'_#7P H:7"+3#&#".7>/!"&#"&47>7267272#"'&#""5>32y Z833 j  XMC) > % o7u LR%'#B#"f9L:5A 5\3X@$^ // 65 -65 1X5 XXg+#%%%Rm47@I @ GC?:7"+3#&#".7>/!"&#"&47>7267&462"%462"&y Z833 j  XMC) > % o7u LR%'#!A_AA_BA_AA_A^ // 65 -65 1X5 _AA/-Bo/AA_?B7AK @ JD=87"+3#&#".7>/!"&#"&47>7267"26544632"y Z833 j  XMC) > % o7u LR%'#JV55V7fHFjh^ // 65 -65 1X5 }:+);;)+pdbHFd ;`@"+36&'.73!272'.+"326762"'.+;2676&#!&47>=!"&#"&47>7 ((8x3Z  ;50#.%gla%7-oD =  > Hk-%7Vo 1%$ce s?%%X S9tHP3u*b %--5?uf?%#LBMX<D+;'u х5 ;y--65 5V^H4*"+'7&54>32'.#"327#"##"'473254 ZКH+'BdN`1RP-v! dBAN)f eHKHuN!{@&Mk"+ TPJ5h@OMF2"+267>"'.+;2676&#!&47>54&'&47!272'.+"#"/7>323oCBA Gk-\Vo!/'!cds??s;5/#/%fla%7!/S1#K  )f+i D+u h5 ;y{;57uf?%E!)-J5h@NHB2"+267>"'.+;2676&#!&47>54&'&47!272'.+"632#"&5473oCBA Gk-\Vo!/'!cds??s;5/#/%fla%7 N''' #K  )f+i D+u h5 ;y{;57uf?%E1/! J5N@KDA2"+267>"'.+;2676&#!&47>54&'&47!272'.+"&''673oCBA Gk-\Vo!/'!cds??s;5/#/%fla%7F3RL4;#K  )f+i D+u h5 ;y{;57uf?%/q'RXN\'_J5@IR PLHC2"+267>"'.+;2676&#!&47>54&'&47!272'.+"462"%462"&3oCBA Gk-\Vo!/'!cds??s;5/#/%fla%7B^AA^AB^BB^B#K  )f+i D+u h5 ;y{;57uf?%G_AA/-Bo/AA_?BJh!0.'"+4&'&47327&#"&47>5#"/7>325632#"&5475&''675462"%462"&54.#"6"&47>=&754&'&473632!"& ]]Ekbg9-Dd?=8\wb32&&#"&47>5&'&47'4&'&47327#&B""f9L95B! 5?%pD% 1b  {I\{b15R 1b {\J{b2 3+XXf+#%%%Rl3T2G{{565 7{1? 1 '+{7556{'Nhh & $"+"32654&#"$5432#"/7>32Pwlp{oi'$/S1yh!)-Nhh % "+"32654&#"$5432632#"&547Pwlp{oi'$) N''' yh1/! NhN " "+"32654&#"$5432&''67Pwlp{oi'$+F3RL3;yh}q'RXN\'_Nh 0 ("+"32654&#"$5432272#"'&#""5>32Pwlp{oi'$mB"#f:L95B! 5\3X?%yhXXf+#%%%Rl3Nh  ) @ '#"+"32654&#"$5432462"%462"&Pwlp{oi'$@A_AA_BB^AA^Byh_AA/-Bo/AA_?BN? G "+%2654&#""$3!272'.+"3267>"'.+;2676&#!\ZXAu{ph+<50 #/%gl`%8-s@ =  > Hk-:#Vo /'+bBCuy5JFo#uf?%P  { D+)9u ?Nh# "+"&#"''7&543272654Pwl8y_;]'ߔq[;Vr9{oyV)Oh;'~0'z~qǾh4CA:"+4&'&47327#".54&'.732732>5#"/7>32Fd 1=  dFJtV8;b  sw  b;F`5/S1{E55F{ )GVk}C55D}&<^R9!)-h4B<6"+4&'&47327#".54&'.732732>5632#"&547Fd 1=  dFJtV8;b  sw  b;F`5 N&'' {E55F{ )GVk}C55D}&<^R91/! N4?85"+4&'&47327#".54&'.732732>5&''67Fd 1=  dFJtV8;b  sw  b;F`5F3SL3;{E55F{ )GVk}C55D}&<^R9q'RXN\'_4=F D@<7"+4&'&47327#".54&'.732732>5462"%462"&Fd 1=  dFJtV8;b  sw  b;F`5B^AA^AB^AA^B{E55F{ )GVk}C55D}&<^R9_AA/-Bo/AA_?Bh L)"+632#"&547&#"&47>=4&'.'&47327?6&'&47327 N''' FyF 5JA {Z  T'J55+"+3254#"4&'&47327632#"'&#"&47>51RP)\swb<- {C55+E7#o5}a865 C{P - "+462"%462"&&#"&47>=4&'.'&47327?6&'&47327A_AA_BA_AA_AFyF 5JA {Z  T'J'I?."+%4&'&47327'"&546323264&'&47327&#"&47>532'&#"#"&'&%632654'&'&747>32'&#"#"&'&-%DB\sCJE/Tw5L7JBJL%-%DB[rDJE/Tw5L7JBJL%? 75m7`PTRfL3>8dB+X>XMZq f 75m7`PTRfL3>8dB+X>XMZq f<"+54$!232#"'&'632654&#"'74&# &#".7>+J=ĐwO)!"?H5'Zu/ V0"R  Iw  b;+mX \E^dZ%+!=65 CLq D 8m L?5"+"32%"'!267# '&$#".'>?&576$3 4&'&47327#".54&'&4732732>5ϓ㮚! %R`#`؝ظ>#f ; %3Pڦ[Fd1=dFJuV7;cswb)=WM0T lz{E55F{ )GVk}C55D}&<^R9NR 4@k eL:5"+4&'&47327#".54&'.732732>5"32654&67'$'.#".'6767"#"$5432wFd 1= dFJtV8;b  rw b54.#"6"&47>=&754&'&473632!"& ]]Ekbg9-Dd?=8\wb/!"&#"&47>7267wdB^y my Z833 j  XMC) > % o7u LR%'#5PR^ // 65 -65 1X5 5DKGE/"+"&5467"#".7>/!"&#"&47>7267&#3273#X{|`  XMC) > % o7u LR%'#833 ^f3\2!y 5^PI35 -65 1X5 // 6T7-5DEm^ ^h .'"+632#"&5472'.#"327# 54>\ N''& H+'BdN`1RP-v! d1/! HKHuN!{ Ӯe^? +$ "+#&'7672'.#"327# 54>ug;2QH2@H+'BdN`1RP-v! `$XTL`$HKHuN!{ ӮeJ? ': 6* "+#&'767"&47>54&'&473632!"&732>54.#"wg;2RH2wb<54&'&47!272'.+"3267>"'.+;2676&#327 X{|`ds??s;5/#/%fla%7-oCBA Gk-\Vo!/'!'_f%"JD!5^PI35 ;y{;57uf?%#K  )f+i D+u hT)+%JDEJ5J K='"+#&'767267>"'.+;2676&#!&47>54&'&47!272'.+"g;1RH1oCBA Gk-\Vo!/'!cds??s;5/#/%fla%7`%XTL`%#K  )f+i D+u h5 ;y{;57uf?%NV 8#"+327#"&'4&'&7327% 4!2'.#"327wdB^y 7XI\{^R?'5qBV\9VPR1 !$#"Po%bdFP1h <-"+632#"&5472676&#!"3&47>54.'&473273= N''' і#3%!<#y323314SX323Gd1/! }5  #K>=L# 55 #K>D#6) "+;2676&#!"3&47>='754.'&47327#G)#3%!<#y323#314SX323=_}5  #K>nV>h=L# 55 #K>rBh DB*"+632#"&547&&#"&47>5&'&47'4&'&47327#& N''' D% 1b  {I\{b15R 1b {\J{b2 3+d1/! T2G{{565 7{1? 1 '+{7556{'BJ A?'"+#&'767&&#"&47>5&'&47'4&'&47327#&f;1RH1AD% 1b  {I\{b15R 1b {\J{b2 3+`%XTL`%T2G{{565 7{1? 1 '+{7556{'BJ8"+%&&#"&47>5&'"=774&'&47327#"&546323265.CD% 1b  {I\{b1!X1b {\J{b29`V;F"&%H:>5BT2G{{7 11 :{1= 1{9 11 8{ 2K<'EB%mNh)5 @ 2,# "+#"&54?62'"54762"32654&#"$5432  J. x 1#3=wlp{oi'$z %3   -ɸhJh BM KD9)"+632#"&547&#"&47>54&'&4732$32#"&/&'&2>54# N''& 5Fqwb<54&'&4732$32#"&/&'&2>54#{g;1RH15Fqwb<32'&#"#"&'& N''' -%DB[rDJE/Tw5L7JBJL%d1/!  75m7`PTRfL3>8dB+X>XMZq fTF!<2"+'7.'&'632654'&'&747>32'&#"#"'473274D XL#-%DB[rDJE/Tw5L7JBJdBAN(f f 75m7`PTRfL3>8dB+X>XMZqx D&Mk"+ TPT!J 50"+#&'767632654'&'&747>32'&#"#"&'&f;1RH1-%DB[rDJE/Tw5L7JBJL%`%XTL`% 75m7`PTRfL3>8dB+X>XMZq f9Ff@6"+'7"&47>54&#""'6743;272#.#"&'#"'47327& bV y=1Z`F!7) Ϩѻ8LoD>x  k$dBAN(f 5 IwET { T?vI6V&Mk"+ TP9f6# "+#&#"&47>=#5354&#""'6743;272#.#"s>x  nj y=1Z`F!7) Ϩѻ8LoDPI65 IPwET { T? I ( "+"2654&4632"4&'&47327#".54&'.732732>5+55V79fHFjhNFd 1=  dFJtV8;b  sw  b;F`5s:+);;)-8dbHFdD{E55F{ )GVk}C55D}&<^R9{R 1$ "+#"&54?62#"5476324&'&47327#".54&'.732732>5  J. x 1#3Fd 1=  dFJtV8;b  sw  b;F`5 %3   -Z{E55F{ )GVk}C55D}&<^R97h 7'"+632#"&5473%2>32326?2%"5477''674 N'''  ))/d<$NT'=-/wq5/)d1/! BdDX?? { 7D 5% "+4632#"&3%2>32326?2%"5477''674}LC;JR?;H ))/d<$NT'=-/wq5/)3LT4-RSBdDX?? { 7J 4$ "+#&'7673%2>32326?2%"5477''6747f;1RH1 ))/d<$NT'=-/wq5/)`%XTL`%BdDX?? { 1f@1"+2'>54#"542676&#!"3&47>54.'&473273b7D+\B;@^#3%!<#y323314SX323GfZ;=L3# X)%TE}5  #K>=L# 55 #K>D9fJ 9'"+#&'767&#"&47>54&#""'6743;272#.#"f;1RH1 >x  nj y=1Z`F!7) Ϩѻ8LoD`%XTL`%I65 IwET { T?9f.D5/"+&#"&47>54&#""'6743;272#.#"2&7>54./&746>x  nj y=1Z`F!7) Ϩѻ8LoD;Yl;J DEI65 IwET { T?HXTy)@  M3T!@;'"+2&7>54./&746632654'&'&747>32'&#"#"&'&;Xl;K  DE-%DB[rDJE/Tw5L7JBJL%HXTy)@  M3 75m7`PTRfL3>8dB+X>XMZq fJD!-*$"+4&'&47327&#"&47>54632#"&54&#"Vy;+PN\obo71b!';1% X573ms1#LF %XCjp V9"+2#"&'.#"327#"&546`\}# +$7O1)RK)Z}T6& mr?Y'X+ywy/0/&"+.#"326"&5463254.#"'67327#  77=;'#DEh{o\-^C@w91b  Zmd;to+w7 !5 8# "+27#"&4632!4&#"BbP)Xyb1*O)+-9)X+wr!?['5L1L1VH3* "+#"=4;4632#"'."32+;2&''54;2XAZX)+)5! w6+ 1 5Z)'!/)#cZ07 -- ;y77DS NG;81"+"'&'"#"'272>32#"&547&546?&5463263232>54&#&4&#"32>D  FVB&=%%=5׊T}iB!JVB37:'/!w#DNEw?J-:3%10 7LVd$?5/7`i@KRB?AFATi=%!%R\-+;zN?1H-337E"+;2&'54;>54.#"'67367654#"=737#"32&''54/&  Z;} %y=+EH' F- ^7N-HV1XR# 00 !7=R#--?W? ++)x,B!*c!"+&#"&47>=4&'&47672>32>32&#"&47>=4&#"&#".7>=4&#"w+ LCEX+3D}q X1:h"f1A^C+XyTo7A:U--co6 @;%- h4--0lJ/.   F"ium/--EHM4$7j/ --EHM4,03-  "+2 &464&#"326sͤ#R@3CVA+F-{syukB =&3.("+&#"&47>54&'&4767262#"'532654&#"w'\LEX+3D}q UOs@0 R-H):-Do,.- /lJ/- ;(qYbs{yA%,;3"+.#"32>>32&#547326="&54632   5/;<}"o1 =Dh{ou/b  Xo^H! 1/ !7to5"+327#"&5#"=4326=732#+%1!Fl1FZ=6p u%-H>?m;HJ/- 4""+32>74&+"=737327#5"74&'#"=737(0   Z707!N %' 75 +Z1#fG .)o?Z'%8 +&"+.54632.54632.54632ojuH5VF Tb9 UjuH5VF!Tc9 ZjuH5VF!Tc9 +'{s_1'C83-'{s^2'C83.'{s_1'C83-P;)> 2*"+2&7>54'.546!2&7>54'.546!2&7>54'.546T^A.d{BJ3ZT^A.d{BJ3ZT^B-d{BI3Zz3-d,-1J$#_z3-d,-1J$#_z3-d,-1J$#_B# "+%" "32\Z#\[9o!!f+  "+     u1u ^^ut^N+  $(,048<?E'@$CA>=;97531/-+)'%#! "+!!!!%!!!!%!!!!%!!!!%!!!!%!!!!%!!3#!!'3!!FHuuuuuu_kkdq5+777?888A8887777588877776715o7V )`jt@ϊqmhcD4 "+!!'3077637+"'.+5"'.=>732326;+73267.'#"#"'&+2654&"462#"&3233676323.'77676?#3276767>36;'"#"#'6=&'&#"+'#.+'7!!XB# 81     9b@+9- Pq D 3iN@ &-1!>/k!!+1E22"!3r) -- 9#9'A-L !C%5#  5/+a9-5T R,1R%+1  1'))y)D= d {w3  3^D+" !Z P??]1H+%9+% 51G#11E12V 1yF #1'#/XNN -#+ 1%+  *%= 21 L#'/37;?CGKO@MLIHFDB@><986410,*%$ "+!!335#535335335335!5#33#5!#5#!35#5#!35#35#535#5##5#LE5dd555V5H!9%/+ ))55FpJ7BB>>>>1PB884F-7L6@C]@FDBA=8"  "+!!#332654#"4632#"&27'"'#"&5463232654#"'7!##########!!L?"#?=6J;1#-%T'))3#$>1#+@1) -) @5V5>7;8;7<7+F7/F$+C3PK9Y7F)qZ 7X7#1Pd-E#+CcO{\P! "+"2>5<.' # 46h7#7N8" #3!B7JJl4994lJJ?>`-;V8#"+%$#.7>54&#"&'$762E:559FHy+)}?'#=L# 63  #K>9H'33{9"um\+("+767263!67&+&?>'.#"17+7/B%3b0 ?N .=P;=*M}xsX)JB),9o-/5 b o(">e]ZH9"+"#"547>32#"'&746323265%"&547>54!,8% 5@N:+CbW+<}iJlD9+N7BTN):78-79KF5'/-Zdc1r}Zd?#=9KV* t/20"+)2+".#&47>=!"/.5476>73dy';65BN$56;5)t73!-R=L# 4  4 #L=R% ?BH* "+%4&#"'32%"'632#"'&54632326s_KH^{Q%1F/'2mjlR53f4;5Vc--b  /ɝ9+A':P\F &% "+2654&#"67# 476$7J;PuT?Ha nduR"1bs?now PILb o;n: Job-HS $afZq "+"'67473! 'o)8AG%A) L{w7 :RN#CF %3 -&""+"654.#"&5467'.546322654&/Z9wfPBUas+oV;hV=en1>[V=51'9B#^DfυݥqK9wZeVs5;#Tf+)X &% "+65#"3267>7.54>3 &;QuT@Hb ncuR#1cronw%Ib o;o: Job-HT`fZN "+!"5463!2/kL?JA+Z+^D($"+4#"'673632#"''6532>54&#"Vy;+PN\obo71b!';1% 583ms1# KF %XDjo V9J"+2#"&'.#"327#"&546`\}# +$7O1)RK)Z}JT5'!ls?Y'X+ywy/0/&"+.#"326#"&5463254.#"'67327#  77=;'#DEh{o\-^C@w91m  Zmd;uo+w8 !5 8VH3* "+#"=4;4632#"'."32#;2&#'54;2XAZX)+)5! w6+ 1 5u)'!/)#cZ/7 -- ; ym7DS NG;82"+%"'&#"#"'272>32#"&547&546?&5463267232>54&#&4&#"32>D  FVB&=%%=5׊T}iB!JVB37:'/!w#DNEw?J-:3%10 7LVd$?6/7`i@LRA?AFATi=%!%R\-+;zN@2G-437h6"+67363232&#5473>=4#";2&#5473>54'"\E\R! N1 f1L R5}# !h% $$ #\! $$ #5V!L)& "+&#"&47>=4&'&476724632#"&+X;LX++L4)>3/7?1-: m/--/mfL)-d['54.#"'67367654#"=737#"32&#'54/&  Z;} %y=+EH' F- ^7N-HV1XR# // !8<R#--?V? ++)y+)"+;2&#5473>54'"'67 R5}#\Ef! $$ #5- !B![c!"+&#"&47>=4&'&47672>32>32&#"&47>=4&#"&#".7>=4&#"w+ LCEX+3D}q X1:h"f1A^C+XyTo7A:U--co6 @;%- h3--0lJ.-   F"ium.--FGM3$7j/ --FGM3,0BZ?"+&#"&47>=4&'&47672>32&#".7>=4&#"w) 9VEX+'Pyu {B^B+X{Ro6 +?<%, h3--0lN-'  )hum.--4hN3 ,0B  o&3.("+&#"&47>54&'&47672632#"'532654&#"w'\LEX+3D}q UOs@0 R-H):-Do-../mJ/- ;'qYbs{yA%,;J3"+.#"32>>32&#547326=#"&54632   5/;<}"o1 =Dh{ou/m  Xo^H! 1/ !7uo5Bo."+&#"&47>=4&'&476723672#"'&"w+X9EX+3Dyu fP)93#3 j0--/mJ/-b6 /@#%9J*#"+372654'.4632."#"&#"69-*+9)9wTIrNo70NR5%533DNT+!`%L)%&'N1#BX#P<+'%%-7%9L1"+%327#"&5#"=4326=732#+%1!Fl1FZ=6p u%-H>?m;HJ/-=4""+32>74&+"=737327#5"74&'#"=737(0   Z!"+"654#"=7307"#"&54632727654'.#"=737+53 V-X9!H-5!A)J' 7'  ;  " K/1H $5e+ "-%L#!"+326?2326?!"547#"'673>707!N %' 75=+Z1#fF -)o?9 !  "+3264&#"4632#"&%3##5#5353utwwuɋƎu??馦ȌƮ??9   "+3264&#"4632#"&%!5!utwwuɋƎ%^馦Ȍo?N'u O ;$"+%267&#""32>54.&''6'5"&543254'767>32#"'-/'?HZ' )N^6581H+ ##7{$B^7${;#'XN\>D %s5?{eK9UtL5A9>;^@3Lhl/jo1y//j͘=1k//}3wh/o''-dB{B8"+!>32'>5#";2&#'5473265#5354'7>7JxRBxoDDa- cF{p&2&P'1'!hiOJ./hg)8C`m{yy/+//+/Owm5111}75ow7{H>"+!632;2&#'54732654&#";2&#'5473265#5354'7>7 q'1'P'1'7Vun'1'P'1'jhOꃲ/+//+/}Pq/+//+/Owm5111}75owD "+32654'&+Z+'!'\+Fw PD "+#"&5476X.#%\)Hw P  "+&'&56? N\~'5=D. +T s  "+4767&s N\~w'5>D/ +T  "+#"&'732675go9T jmo87)y1 "+#"&54?6"'&54?632 B  u'% (  )y1 "+>#"/&54%632"/&'4DA +'u  %!  ( "+>32&"fo:T jmo77j"+# '463232654&54632yu8'%-&<9)3!\;^R!'# !%FV'"+"&54632254&54632ٙ3%#/5'/bwu`#/+!^^-+J "+462"$462"BbBBb=Db@@bbDDbBBbDDbBP?"+#476'4"#"54632DF64#I 7\LVL/+/6J/#H 7'IM"+462"LfLLffLLfL# !8("+5!50;!!!!!3!!!!!!!!!!!+5!5!!5!#53#PN1PLLsLD++0E/\0^E/52y/w/w0R/D-l0/=)% $ "+".724#"''7&5327-Dy)d{XRcCTOOT wb>R}HlMK\jiED'W+T9'Jfb "+47.J 9-=naA;3'8RD#g; "+'7;9-=naAN<.$8RD"+!  J[G#"G\J  y*$ 0/,,.1 %"+!.7>54.'.7  J\G"#G\J  % 00+4,/0 %\W+#"+3".76&'.7>5&>3jsqmcH Sl8 ^m i_ ?nI &/:ng(ygj>tS+Tb ! iKDHk= 0Yf+#"+7&675.76&#&472#&47>krqldG Sm7 _m i` ?nI &/9$ng(ygj=uRTb ! iJHl= 0Y{H"+"&5477632"&5462=V97%3[]Z{VZ{VOJ;^V3;DNK;VR;9WS  B,64/ "+%4&54632#"&54>7>=4632326"&5462;=0)Kl`'R96'--+' DDqRRD] Z{VZ{V>%/A6VZP5^X/'-hD) %;L>d`@C;VR;9WS! "+56767.%56767.e +{ZZ{,DCe +{ZZ{,DDB r"&pqP@Z r"&pqP@! "+5&'&'>%5&'&'>f +{ZZ{+DDf +{ZZ{+DD?!r"&pqP?Z!r"&pqP?j^^= "+'56^`žg=dkD{Zo9 "+7567&'5{`žg1ZkCN3 "+!"5463!2/kL?JA3+Z+^N%'"+4&'&7$72#"&5463232654&5/V F&/TRDl9wJ! %+5V1'+#sfPA9:L=)R  %\8F8-1Td"K1PX@@b`M=M= M = K >@>b`UM= M = K >YY@b`[YVUTQNMJFCB?=:8$%%$#C+&#"&47>5#"=46;546763267632"&'.#"32+&#"&47>5+!&'.#"!354POEA>=%#&&$#F +&#"&47>5#"=46;54>7672#"&'&"!672&#"&47>54&'!6S ^wbK1PX@2  b= M= M = L>@0  b  U= M = L>YY@NLIGB@=;76C%$#C+&#"&47>5#"'546;5467632>72&#"&47>5#"'.#"32+  Z M=M= =M = L>K1PX@?   b M=M= =M = L>@=   b U M= =M = L>YY@!kjtrjykyih^ZWVPONLGEB@%%$#H#F+&#"&47>5#!&#"&47>5#"=46;546763267632#"'.#"!672&#"&47>54&'#%3547&'.#"\5T ^bT5^/= ^wbDO{;;HjZ #seh{765 7yV1gfb \19GkE/iy@A< U6nK1PX@>b M  = M =M= L >@<b  U M  =M= L >YY@'kjtrjykyigdb][XVRQNJGF@?;942$#C#C+&#"&47>5#!&#"&47>5#"=46;546763267632>72&#"&47>5'"'.#"32+%3547&'.#"\ <  bM= M=K= >GFB@;964#%$#C% +32>54'"&#"&47>5#"=46;467>3232+327#"'&7#Z!+P;c sw bWURPMKIGDB?=96+)" ZZ +2>5!"'&#"#"'&'632654'&'&'476323&5463232+727'"&5#"=41?y-Q5Nr+8oO?݅-%H-Lib?FhMհ -L5L?/sXh%y!;7%}R_{2 ^{1%q.EYFj}-V o6/q.?kFTXHjYu/#3/hhVE-)1 0,"+ 463272=47654&#"32654'&264&"-@sRTu{=@ 鸠7!#5B;R<111Vt1G1;hK=1111 1L911L7V5 C1! 1 1!P1  ZKV^--MLb xH--- \-hL-%L5v  N|-l-l--$-H-LL%-Ljb5~NC -%--v -E-LnL-+!bj#G1t |Kh!t!&%C#~)SS(!  CXtf tt Jb;||  Z ZLLR;R;b =xHH+1-1-LhLLhLLhLPHVv Vv Vv !Z-RN!FF;)S}GO%!(77(%A1  /t\Cff`t>R\`/t#t#ss;#t/A7t/t/?  ^d!LVVB-~d!A771- Z;V;V;VLL1|L1|L1|L1|L1|LLLLLL\ %D+1%1%1%1%1%11%%%%%%1%1%11 =-1 =-1 =-)-)-)-)-LhLLhLKhLLhL1%1%1l-1l-1l-1l-7jb7jb7jb7jb7jb7+7+7+7+ % % % % %; ; 9 79 79 79 79 7 | |v )#)#)#%#7v  `/ Z Z Z  Z Z Z Z Z Z Z ZLLLLBLLL1515LhLLhLLhLLhBLhLLhLLhLLLLLLLLLLL % %?%?%?%?%?%v v v v ;J;J;J;J;J;J;J;JwZZZZZZ`+t????????SSdxGGh/CAs=hLhLhLhLhLhL&7f7f7f7f7f7f7f7f7vTTTTTTTT VS/;J;JZZ??GGhLhLf7f7TT;J;J;J;J;J;J;J;JI Mw ????????   Q ? \ A / ?TTTTTTTT\ O C ^ ^y;J;J;J;J;J;J;Jr;t;^b?????bn\~1BB5-91(;9Jf7f7f7f7|D|Df7f7uumTTTTT"(?LX;$ SS5NNDED;`;9{ ZPPZZPPPjjdddddd?Mh h\"\Q\\"\Q\j{=I{NbJ)Z!55{LV`?VV#cRLVT=JRB/B?D5#PFTJF=JRB/\t13LtIBBpB9fhQ%1|L ~d1FFLdN q qjN1#~~Xf?#L/?#-\vS?S?=b=xGoGDG}GFG3GGGaG}G9G[GNw1=11 1 \ 1  S1"FF1 B;BBB;   z B| & %L|L=-bFb"-7 ;}L ;}`h`fhhf ;} ;}#}}}}} $N}`N` $`f hh f`;3H3="5 ; ;; ZL;;99=!Q7}77#7#ByP5#xLSI)nn{77f7f7y7b7b7y7y7777EVE#7777-L-P-L-P}}||uunz1J;7u]PPPPPPPPPPPPPPPPPPPPdSZSZSZSZZZS;S;VV(VdddbbddddP11&1F$;VVV+tPtPtLVR(=)+z;"-M???+DDl9+'Xh(bL7qP(F'E)++TA1PdRjPVV+ddddddddddJ)?) Z"\7-I/Z%"7N77 Aq A AqjjNd#^2-y# 9hA7 B B BAPAPRDTVDRV3F9f'%JA)ZHjMN5=B3VJFL  --L n nX L b 7J1^$JJ?JNJJ?JK17BNfJNJjT9r|7N 1^JJJJJJJF$=7BNNNNNNNJJT L N$=1^1^$JJJNK1K#7B7BKBNJJjTjTjT99777K199jTJ``V/t#V;-B3IB;-Z-ZP BfLLP\H/HFqFXN`V/V;tVVI-BBpBv;B9-#Z-99 N-7s))jFS#=J#--tt {p ppj{N~N- -p1-/I% b-dddddtP $  l dht  <\@p !d"#%|&'8()$*<+$,-./014H445D556678|9:T;\<>>?@ABCD EEFGDHHIJKLN|O4O`PPPQ4RTLUVDVWX0Z [ [[\_p_`\aPbhcd$eDfLfg0ghxhjkmnoq4rhsuvxxdy{}8~X 4d,|4PtLTD\x@T¬x0ŸƀȈT̤ШdHׄظdD`@dDP(`XPpX\ h$l\ @  0 h(P\` p!"$\%t&'()++-/014P579;4<8=?@4ADBtCEF8GITJKLNLOPRTVYx[x^ ` ab8ce\fTgXhhijhlno@p`qtrsPtduvx y{0{|~$h8L<\T84|hPp@(P,P0ôŀDŽ <H<ͨXxЈLԸt,ۀ,ݠ`|xlllL d   $(PX l!T"#$%&( ()+,.0/002p34569(:@;D?\@BCD`E8FGDHLJLdMdN\OPQR\SPT0UxVWZH[\P^X`<abhcPdghijk\mnHops,t<u4vLwdyz{}4~ D|\4@|DPdT88 D@<DœèĬ|hdȄX400hЄ|Ը֤h`(p|ddtxd$ `\P   |  \   < $T X0$p$l8(h4T(l< l |  !(!T!" "#X#$4$$%\%&`&'p'(,(x))P))*0*++x, ,P,,--.D.//d/0$0x0111223$3348445L56677L78L889X9:4::;h;<@<=<=x>>???@@@ADABHBC@CDhDETFFH,IIlIJJ|JJKKtKL MLN$NOHOOP@PQ<QRS<ST4TU|V`WtXYdYZ[\0\t\]^P___`ab,cefgij,k8l<mnnphrt uvpwxxzT{|}~8hLDL0$Xt(|Lp,L4D@  |l pd`DtèĬ$ƴl4T\(Ҭp HP8t| llpD@,x  X XLPp  "(#$%4&''(P)L*+,-/ 0h1D2h3 4567:;8p??@BxCDFH4IJL8M0NOpP@QR`SUX<Y0Z[\]_acehikmoqsuwxy{}<( 4``@pDtl`PPHˆ,ĸŨhHt̐||֔d0@Tۘ@  @ppd8$h Lx DdL$,H(|$TT<x (   `$XD !|"P#$%%&'(`)* *+,.\0124x57H8:(<0>`?ACDEFI<JLNdQR$TU8VWYZ]_a bPcpeg(hj@km8nprptPuvxyXzL{}44HlP|d,D<XdxT@th`tDd4,|,,`dќҸ4ռ\(ߴ4X4488    ,\pTplP t"4#%&')+ ,.x0413\46P7,9|:<=?@BD(FHJ0KMpNP4QRRSTLU|VhW`XlYhZ\ ]8^l_`abcdf(ghjdl mn o0p8qstvwy{},hXLlh@(|D4xP(p´ 0<dلTވ ,80L< d\X   `TX4H\ "x#$&(|*,`. /1X3589;<>@BDFTGI4KXM|ORTPVXHZ\,^T_h`|atbdc`dPe|fghkLmp4svy |\D HD hTXp4,ň\ҀאڔD\@$`8xX t,PP    | 0|l` "8#|$&\')()*|*,`-`.01p2T3(4\567777777777777788P889$9X99:P:; ;`<<=@=>??@@T@A,AlAlCEEhEFG GH\HHIJKL NLNxOQSU V VWXYpZp[[\]]^^\^^_`a\bccdte0efghggh<hxhij@jklmdno4pq|rsHstvxz|(}~`x 4hP`LP84`|< \hL<\TĤd(ˠͰ\ׄ<4T,\H  D|4(HLl\l08(P,p0PtdP ,hLTt h  T   4   (   \   `  X  L   `    8  | , d $  L  8 d  @ h L L " $ '$ * , . 0 3` 4 6 6 6 6 70 7P 7 7 7 7 80 8X 8 9 9L 9 :< :x : ;4 ; ;  >T > ? ?D ? ? ? ? ? ? ? ? ? @p A AL A A B@ B Ch C Dt D Ep E E F F H I Kd M O Q Rp S Z:Gh>+//^"r     5C. 8q   ^ (- DU    : :) jc Linux Libertine by Philipp H. Poll, Open Font under Terms of following Free Software Licenses: GPL (General Public License) with font-exception and OFL (Open Font License). Created with FontForge (http://fontforge.sf.net) Sept 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011,2012Linux LibertineBoldFontForge 2.0 : Linux Libertine Bold : 2-7-2012Linux Libertine BoldVersion 5.1.4 ; ttfautohint (v0.9)LinLibertineBPhilipp H. PollPhilipp H. Pollhttp://www.linuxlibertine.orghttp://www.linuxlibertine.orgGPL- General Public License AND OFL-Open Font Licensehttp://www.fsf.org/licenses/gpl.html AND http://scripts.sil.org/OFLLinux Libertine by Philipp H. Poll, Open Font under Terms of following Free Software Licenses: GPL (General Public License) with font-exception and OFL (Open Font License). Created with FontForge (http://fontforge.sf.net) Sept 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011,2012Linux LibertineBoldFontForge 2.0 : Linux Libertine Bold : 2-7-2012Linux Libertine BoldVersion 5.1.4 ; ttfautohint (v0.9)LinLibertineBPhilipp H. PollPhilipp H. Pollhttp://www.linuxlibertine.orghttp://www.linuxlibertine.orgGPL- General Public License AND OFL-Open Font Licensehttp://www.fsf.org/licenses/gpl.html AND http://scripts.sil.org/OFLQ Z  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghjikmlnoqprsutvwxzy{}|~      !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~      !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~      !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~      !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~      !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~      !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~      !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~      !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~                           ! " # $ % & ' ( ) * + , - . / 0 1 2 3 4 5 6 7 8 9 : ; < = > ? @ A B C D E F G H I J K L M N O P Q R S T U V W X Y Z [ \ ] ^ _ ` auni00A0uni00AD two.superiorthree.superioruni00B5pilcrow one.superiorAmacronamacronAbreveabreveAogonekaogonek Ccircumflex ccircumflex Cdotaccent cdotaccentDcarondcaronDcroatEmacronemacronEbreveebreve Edotaccent edotaccentEogonekeogonekEcaronecaron Gcircumflex gcircumflex Gdotaccent gdotaccent Gcommaaccent gcommaaccent Hcircumflex hcircumflexHbarhbarItildeitildeImacronimacronIbreveibreveIogonekiogonekIJij Jcircumflex jcircumflex Kcommaaccent kcommaaccent kgreenlandicLacutelacute Lcommaaccent lcommaaccentLcaronlcaronLdotldotNacutenacute Ncommaaccent ncommaaccentNcaronncaron napostropheEngengOmacronomacronObreveobreve Ohungarumlaut ohungarumlautRacuteracute Rcommaaccent rcommaaccentRcaronrcaronSacutesacute Scircumflex scircumflexTcedillatcedillaTcarontcaronTbartbarUtildeutildeUmacronumacronUbreveubreveUringuring Uhungarumlaut uhungarumlautUogonekuogonek Wcircumflex wcircumflex Ycircumflex ycircumflexZacutezacute Zdotaccent zdotaccentlongsuni0180uni0181uni0182uni0183uni0184uni0185uni0186uni0187uni0188uni0189uni018Auni018Buni018Cuni018Duni018Euni018Funi0190uni0191uni0193uni0194uni0195uni0196uni0197uni0198uni0199uni019Auni019Buni019Cuni019Duni019Euni019FOhornohornuni01A2uni01A3uni01A4uni01A5uni01A6uni01A7uni01A8uni01A9uni01AAuni01ABuni01ACuni01ADuni01AEUhornuhornuni01B1uni01B2uni01B3uni01B4uni01B5uni01B6uni01B7uni01B8uni01B9uni01BAuni01BBuni01BCuni01BDuni01BEuni01BFuni01C0uni01C1uni01C2uni01C3uni01C4uni01C5uni01C6uni01C7uni01C8uni01C9uni01CAuni01CBuni01CCuni01CDuni01CEuni01CFuni01D0uni01D1uni01D2uni01D3uni01D4uni01D5uni01D6uni01D7uni01D8uni01D9uni01DAuni01DBuni01DCeturneduni01DEuni01DFuni01E0uni01E1uni01E2uni01E3uni01E4uni01E5Gcarongcaronuni01E8uni01E9uni01EAuni01EBuni01ECuni01EDuni01EEuni01EFuni01F0uni01F1uni01F2uni01F3uni01F4uni01F5uni01F6uni01F7uni01F8uni01F9 Aringacute aringacuteAEacuteaeacute Oslashacute oslashacuteuni0200uni0201uni0202uni0203uni0204uni0205uni0206uni0207uni0208uni0209uni020Auni020Buni020Cuni020Duni020Euni020Funi0210uni0211uni0212uni0213uni0214uni0215uni0216uni0217 Scommaaccent scommaaccent Tcommaaccent tcommaaccentuni021Cuni021Duni021Euni021Funi0220uni0221uni0222uni0223uni0224uni0225uni0226uni0227uni0228uni0229uni022Auni022Buni022Cuni022Duni022Euni022Funi0230uni0231uni0232uni0233uni0234uni0235uni0236dotlessjuni0238uni0239uni023Auni023Buni023Cuni023Duni023Euni023Funi0240uni0241uni0242uni0243uni0244uni0245uni0246uni0247uni0248uni0249uni024Auni024Buni024Cuni024Duni024Euni024Faturneduni0251uni0252uni0253cturneduni0255uni0256uni0257uni0258uni0259uni025Auni025Buni025Cuni025Duni025Euni025Funi0260uni0261uni0262 gammalatinuni0264uni0265hhookuni0267uni0268uni0269uni026Auni026Buni026Cuni026Duni026Emturneduni0270uni0271uni0272uni0273uni0274uni0275uni0276uni0277uni0278rturneduni027A rhookturneduni027Cuni027Duni027Euni027F RsmallcapRsmallinverteduni0282uni0283uni0284uni0285uni0286uni0287uni0288uni0289uni028Auni028Buni028Cuni028Duni028Euni028Funi0290uni0291uni0292uni0293uni0294glottalstopreverseduni0296uni0297uni0298uni0299uni029Auni029Buni029Cuni029Duni029Euni029Funi02A0uni02A1uni02A2uni02A3uni02A4uni02A5uni02A6uni02A7uni02A8uni02A9uni02AAuni02ABuni02ACuni02ADuni02AEuni02AF h.superiorhhook.superior j.superior r.superiorrturned.superiorrhookturned.superiorRsmallinverted.superior w.superior y.superioruni02B9uni02BAuni02BB afii57929 afii64937uni02BEuni02BFuni02C0uni02C1uni02C2uni02C3uni02C4uni02C5uni02C8uni02C9uni02CAuni02CBuni02CCuni02CDuni02CEuni02CFuni02D0uni02D1uni02D2uni02D3uni02D4uni02D5uni02D6uni02D7uni02DEuni02DFgammalatin.superior l.superior s.superior x.superiorglottalstopreversed.superioruni02E5uni02E6uni02E7uni02E8uni02E9uni02EAuni02EBuni02ECuni02EDuni02EEuni02EFuni02F0uni02F1uni02F2uni02F3uni02F4uni02F5uni02F6uni02F7uni02F8uni02F9uni02FAuni02FBuni02FCuni02FDuni02FEuni02FF gravecomb acutecombcircumflexcomb tildecomb macroncombuni0305 brevecombuni0307uni0308 hookabovecombuni030Auni030Buni030Cuni030Duni030Euni030Funi0310uni0311uni0312uni0313uni0314uni0315uni0316uni0317uni0318uni0319uni031Auni031Buni031Cuni031Duni031Euni031Funi0320uni0321uni0322 dotbelowcombuni0324uni0325uni0326uni0327uni0328uni0329uni032Auni032Buni032Cuni032Duni032Euni032Funi0330uni0331uni0332uni0333uni0334uni0335uni0336uni0337uni0338uni0339uni033Auni033Buni033Cuni033Duni033Euni033Funi0340uni0341uni0342uni0343uni0344uni0345uni0346uni0347uni0348uni0349uni034Auni034Buni034Cuni034Duni034Euni0350uni0351uni0352uni0353uni0354uni0355uni0356uni0357uni0358uni0359uni035Auni035Buni035Cuni035Duni035Euni035Funi0360uni0361uni0362uni0363uni0364uni0365uni0366uni0367uni0368uni0369uni036Auni036Buni036Cuni036Duni036Euni036Funi0374uni0375uni037Auni037Buni037Cuni037Duni037Etonos dieresistonos Alphatonos anoteleia EpsilontonosEtatonos Iotatonos Omicrontonos Upsilontonos OmegatonosiotadieresistonosAlphaBetaGammaEpsilonZetaEtaThetaIotaKappaLambdaMuNuXiOmicronPiRhoSigmaTauUpsilonPhiChiPsi IotadieresisUpsilondieresis alphatonos epsilontonosetatonos iotatonosupsilondieresistonosalphabetagammadeltaepsilonzetaetathetaiotakappalambdanuxiomicronrhosigma1sigmatauupsilonphichipsiomega iotadieresisupsilondieresis omicrontonos upsilontonos omegatonosuni03D0theta1Upsilon1uni03D3uni03D4phi1omega1uni03D7uni03D8uni03D9uni03DAuni03DBuni03DCuni03DDuni03DEuni03DFuni03E0uni03E1uni03F0uni03F1uni03F2uni03F3uni03F4uni03F5uni03F6uni03F8uni03F9uni03FBuni03FDuni03FEuni03FFuni0400 afii10023 afii10051 afii10052 afii10053 afii10054 afii10055 afii10056 afii10057 afii10058 afii10059 afii10060 afii10061uni040D afii10062 afii10145 afii10017 afii10018 afii10019 afii10020 afii10021 afii10022 afii10024 afii10025 afii10026 afii10027 afii10028 afii10029 afii10030 afii10031 afii10032 afii10033 afii10034 afii10035 afii10036 afii10037 afii10038 afii10039 afii10040 afii10041 afii10042 afii10043 afii10044 afii10045 afii10046 afii10047 afii10048 afii10049 afii10065 afii10066 afii10067 afii10068 afii10069 afii10070 afii10072 afii10073 afii10074 afii10075 afii10076 afii10077 afii10078 afii10079 afii10080 afii10081 afii10082 afii10083 afii10084 afii10085 afii10086 afii10087 afii10088 afii10089 afii10090 afii10091 afii10092 afii10093 afii10094 afii10095 afii10096 afii10097uni0450 afii10071 afii10099 afii10100 afii10101 afii10102 afii10103 afii10104 afii10105 afii10106 afii10107 afii10108 afii10109uni045D afii10110 afii10193 afii10146 afii10194uni0470uni0471 afii10147 afii10195 afii10148 afii10196uni0476uni0477 afii10050 afii10098uni0492uni0493uni0494uni0495uni0496uni0497uni0498uni0499uni049Auni049Buni049Cuni049Duni049Euni049Funi04A0uni04A1uni04A2uni04A3uni04A4uni04A5uni04A6uni04A7uni04A8uni04A9uni04AAuni04ABuni04ACuni04ADuni04AEuni04AFuni04B0uni04B1uni04B2uni04B3uni04B4uni04B5uni04B6uni04B7uni04B8uni04B9uni04BAuni04BBuni04BCuni04BDuni04BEuni04BFuni04C0uni04C1uni04C2uni04C3uni04C4uni04C7uni04C8uni04C9uni04CAuni04CBuni04CCuni04D0uni04D1uni04D2uni04D3uni04D4uni04D5uni04D6uni04D7uni04D8 afii10846uni04DAuni04DBuni04DCuni04DDuni04DEuni04DFuni04E0uni04E1uni04E2uni04E3uni04E4uni04E5uni04E6uni04E7uni04E8uni04E9uni04EAuni04EBuni04ECuni04EDuni04EEuni04EFuni04F0uni04F1uni04F2uni04F3uni04F4uni04F5uni04F6uni04F7uni04F8uni04F9 afii57799 afii57801 afii57800 afii57802 afii57793 afii57794 afii57795 afii57798 afii57797 afii57806uni05BA afii57796 afii57807 afii57839 afii57645 afii57841 afii57842 afii57804 afii57803 afii57658uni05C6 afii57664 afii57665 afii57666 afii57667 afii57668 afii57669 afii57670 afii57671 afii57672 afii57673 afii57674 afii57675 afii57676 afii57677 afii57678 afii57679 afii57680 afii57681 afii57682 afii57683 afii57684 afii57685 afii57686 afii57687 afii57688 afii57689 afii57690 afii57716 afii57717 afii57718uni05F3uni05F4 a.superioraturned.superioruni1D45uni1D46 b.superior d.superior e.superioreturned.superioruni1D4Buni1D4C g.superioriturned.superior k.superior m.superioruni1D51 o.superiorcturned.superioruni1D54uni1D55 p.superior t.superior u.superioruni1D59mturned.superior v.superioruni1D5Cuni1D5Duni1D5Euni1D5Funi1D62uni1D63uni1D64uni1D65uni1D66uni1D67 c.superior f.superior z.superioruni1E00uni1E01uni1E02uni1E03uni1E04uni1E05uni1E06uni1E07uni1E08uni1E09uni1E0Auni1E0Buni1E0Cuni1E0Duni1E0Euni1E0Funi1E10uni1E11uni1E12uni1E13uni1E14uni1E15uni1E16uni1E17uni1E18uni1E19uni1E1Auni1E1Buni1E1Cuni1E1Duni1E1Euni1E1Funi1E20uni1E21uni1E22uni1E23uni1E24uni1E25uni1E26uni1E27uni1E28uni1E29uni1E2Auni1E2Buni1E2Cuni1E2Duni1E2Euni1E2Funi1E30uni1E31uni1E32uni1E33uni1E34uni1E35uni1E36uni1E37uni1E38uni1E39uni1E3Auni1E3Buni1E3Cuni1E3Duni1E3Euni1E3Funi1E40uni1E41uni1E42uni1E43uni1E44uni1E45uni1E46uni1E47uni1E48uni1E49uni1E4Auni1E4Buni1E4Cuni1E4Duni1E4Euni1E4Funi1E50uni1E51uni1E52uni1E53uni1E54uni1E55uni1E56uni1E57uni1E58uni1E59uni1E5Auni1E5Buni1E5Cuni1E5Duni1E5Euni1E5Funi1E60uni1E61uni1E62uni1E63uni1E64uni1E65uni1E66uni1E67uni1E68uni1E69uni1E6Auni1E6Buni1E6Cuni1E6Duni1E6Euni1E6Funi1E70uni1E71uni1E72uni1E73uni1E74uni1E75uni1E76uni1E77uni1E78uni1E79uni1E7Auni1E7Buni1E7Cuni1E7Duni1E7Euni1E7FWgravewgraveWacutewacute Wdieresis wdieresisuni1E86uni1E87uni1E88uni1E89uni1E8Auni1E8Buni1E8Cuni1E8Duni1E8Euni1E8Funi1E90uni1E91uni1E92uni1E93uni1E94uni1E95uni1E96uni1E97uni1E98uni1E99uni1E9Auni1E9B Germandblsuni1EA0uni1EA1uni1EA2uni1EA3uni1EA4uni1EA5uni1EA6uni1EA7uni1EA8uni1EA9uni1EAAuni1EABuni1EACuni1EADuni1EAEuni1EAFuni1EB0uni1EB1uni1EB2uni1EB3uni1EB4uni1EB5uni1EB6uni1EB7uni1EB8uni1EB9uni1EBAuni1EBBuni1EBCuni1EBDuni1EBEuni1EBFuni1EC0uni1EC1uni1EC2uni1EC3uni1EC4uni1EC5uni1EC6uni1EC7uni1EC8uni1EC9uni1ECAuni1ECBuni1ECCuni1ECDuni1ECEuni1ECFuni1ED0uni1ED1uni1ED2uni1ED3uni1ED4uni1ED5uni1ED6uni1ED7uni1ED8uni1ED9uni1EDAuni1EDBuni1EDCuni1EDDuni1EDEuni1EDFuni1EE0uni1EE1uni1EE2uni1EE3uni1EE4uni1EE5uni1EE6uni1EE7uni1EE8uni1EE9uni1EEAuni1EEBuni1EECuni1EEDuni1EEEuni1EEFuni1EF0uni1EF1Ygraveygraveuni1EF4uni1EF5uni1EF6uni1EF7uni1EF8uni1EF9uni1F00uni1F01uni1F02uni1F03uni1F04uni1F05uni1F06uni1F07uni1F08uni1F09uni1F0Auni1F0Buni1F0Cuni1F0Duni1F0Euni1F0Funi1F10uni1F11uni1F12uni1F13uni1F14uni1F15uni1F18uni1F19uni1F1Auni1F1Buni1F1Cuni1F1Duni1F20uni1F21uni1F22uni1F23uni1F24uni1F25uni1F26uni1F27uni1F28uni1F29uni1F2Auni1F2Buni1F2Cuni1F2Duni1F2Euni1F2Funi1F30uni1F31uni1F32uni1F33uni1F34uni1F35uni1F36uni1F37uni1F38uni1F39uni1F3Auni1F3Buni1F3Cuni1F3Duni1F3Euni1F3Funi1F40uni1F41uni1F42uni1F43uni1F44uni1F45uni1F48uni1F49uni1F4Auni1F4Buni1F4Cuni1F4Duni1F50uni1F51uni1F52uni1F53uni1F54uni1F55uni1F56uni1F57uni1F59uni1F5Buni1F5Duni1F5Funi1F60uni1F61uni1F62uni1F63uni1F64uni1F65uni1F66uni1F67uni1F68uni1F69uni1F6Auni1F6Buni1F6Cuni1F6Duni1F6Euni1F6Funi1F70uni1F71uni1F72uni1F73uni1F74uni1F75uni1F76uni1F77uni1F78uni1F79uni1F7Auni1F7Buni1F7Cuni1F7Duni1F80uni1F81uni1F82uni1F83uni1F84uni1F85uni1F86uni1F87uni1F88uni1F89uni1F8Auni1F8Buni1F8Cuni1F8Duni1F8Euni1F8Funi1F90uni1F91uni1F92uni1F93uni1F94uni1F95uni1F96uni1F97uni1F98uni1F99uni1F9Auni1F9Buni1F9Cuni1F9Duni1F9Euni1F9Funi1FA0uni1FA1uni1FA2uni1FA3uni1FA4uni1FA5uni1FA6uni1FA7uni1FA8uni1FA9uni1FAAuni1FABuni1FACuni1FADuni1FAEuni1FAFuni1FB0uni1FB1uni1FB2uni1FB3uni1FB4uni1FB6uni1FB7uni1FB8uni1FB9uni1FBAuni1FBBuni1FBCuni1FBDuni1FBEuni1FBFuni1FC0uni1FC1uni1FC2uni1FC3uni1FC4uni1FC6uni1FC7uni1FC8uni1FC9uni1FCAuni1FCBuni1FCCuni1FCDuni1FCEuni1FCFuni1FD0uni1FD1uni1FD2uni1FD3uni1FD6uni1FD7uni1FD8uni1FD9uni1FDAuni1FDBuni1FDDuni1FDEuni1FDFuni1FE0uni1FE1uni1FE2uni1FE3uni1FE4uni1FE5uni1FE6uni1FE7uni1FE8uni1FE9uni1FEAuni1FEBuni1FECuni1FEDuni1FEEuni1FEFuni1FF2uni1FF3uni1FF4uni1FF6uni1FF7uni1FF8uni1FF9uni1FFAuni1FFBuni1FFCuni1FFDuni1FFEenquademquadenspaceemspacethreeperemspacefourperemspace sixperemspace figurespacepunctuationspace thinspace hairspacezerowidthspace hyphentwo hyphennobreak figuredash horizontalbaruni2016 underscoredbl quotereversed quotedblrevtrianglebulletonedotenleadertwodotenleader hyphendotuni202Fpertenthousandminutesecond primetriple primereverseduni2036uni2037uni203B exclamdbl interrobanguni203Euni2042question_questionquestion_exclamexclam_questionuni204Auni204Buni204F zero.superior i.superior four.superior five.superior six.superiorseven.superioreight.superior nine.superior plus.superiorminus.superiorequal.superiorparenleft.superiorparenright.superior n.superior zero.inferior one.inferior two.inferiorthree.inferior four.inferior five.inferior six.inferiorseven.inferioreight.inferior nine.inferior plus.inferiorminus.inferiorequal.inferiorparenleft.inferiorparenright.inferior a.inferior e.inferior o.inferior x.inferioruni2094uni2095uni2096uni2097uni2098uni2099uni209Auni209Buni209Cuni20A2lirapesetauni20A8dongEurouni20AFpesouni2100uni2101uni2102 centigrade afii61248uni2106 fahrenheituni210Cuni210Duni210Euni210FIfraktur afii61289uni2115numerouni2119uni211ARfrakturuni211Duni2120uni2124uni2126Omegainv estimatedalephuni2136uni2137uni2138uni2139uni214Fonethird twothirdsonefifth twofifths threefifths fourfifthsonesixth fivesixths oneeighth threeeighths fiveeighths seveneighths onenumeratorOneromanTworoman Threeroman Fourroman FiveromanSixroman Sevenroman Eightroman NineromanTenroman Elevenroman Twelveromanuni216Cuni216Duni216Euni216Foneromantworoman threeroman fourroman fiveromansixroman sevenroman eightroman nineromantenroman elevenroman twelveromanuni217Cuni217Duni217Euni217Funi2180uni2181uni2182uni2183uni2184 arrowleftarrowup arrowright arrowdown arrowboth arrowupdnuni2196uni2197uni2198uni2199uni219Auni219B arrowupdnbseuni21AEuni21BCuni21BDuni21C0uni21C1uni21CBuni21CCuni21CDuni21CEuni21CF arrowdblleft arrowdblup arrowdblright arrowdbldown arrowdblbothuni21D5NwarrowNearrowSearrowSwarrow universaluni2201 existentialuni2204emptysetuni2206gradientelement notelementuni220Asuchthatuni220Cuni220Duni2210uni2213uni2214uni2215uni2216 asteriskmathuni2218uni2219uni221Buni221Cuni221Funi2223uni2224uni2225uni2226 logicaland logicalor intersectionunionuni2236similaruni2241 congruentuni2249uni2259 equivalenceuni2262uni226Auni226Buni226Euni226Funi2270uni2271 propersubsetpropersuperset notsubsetuni2285 circleplusuni2296circlemultiplyuni2298dotmathuni22EFuni2302uni2303uni2310uni2320uni2321uni2326uni2327uni2329uni232Auni232Buni23D3uni2460uni2461uni2462uni2463uni2464uni2465uni2466uni2467uni2468uni2469uni246Auni246Buni246Cuni246Duni246Euni246Funi2470uni2471uni2472uni2473uni24EAuni24FF filledboxH22073triagupuni25B3uni25B6uni25B7triagdnuni25BDuni25C0uni25C1uni25C6uni25C7uni25C9 bigcircleuni25CEH18533uni25D0uni25D1uni25D2uni25D3uni25D4uni25D5uni25D6uni25D7uni2605uni2619uni261Buni261Euni2627uni262Funi2639uni263Auni263Buni263Cuni263Duni263Euni263Ffemaleuni2641maleuni2643uni2644uni2645uni2646uni2647uni2648uni2649uni264Auni264Buni264Cuni264Duni264Euni264Funi2650uni2651uni2652uni2653uni2660uni2663uni2665uni2666uni2669 musicalnotemusicalnotedbluni266Cuni2695uni2698uni26A2uni26A3uni26A4uni26A5uni2767uni2776uni2777uni2778uni2779uni277Auni277Buni277Cuni277Duni277Euni277Funi27E6uni27E7uni27E8uni27E9uni2C60uni2C61uni2C62uni2C63uni2C64uni2C65uni2C66uni2C67uni2C68uni2C69uni2C6Auni2C6Buni2C6Cuni2C74uni2C75uni2C76uni2C77uni2E17uni2E18uniA720uniA721TuxuniE001uniE002uniE003uniE004uniE005uniE006uniE007uniE008uniE009uniE00AuniE00Bcopyleft publicdomaincreativecommonszero.slashfitted zero.fitted one.fitted two.fitted three.fitted four.fitted five.fitted six.fitted seven.fitted eight.fitted nine.fitted Euro.fitted Yen.fitteduniE01Cperthousandzero zero.oldstyle one.oldstyle two.oldstylethree.oldstyle four.oldstyle five.oldstyle six.oldstyleseven.oldstyleeight.oldstyle nine.oldstyle Adieresis.alt Odieresis.alt Udieresis.altW.altf_jf_tc_tuniE040uniE041uniE042Q_uT_hgermandbls.ss03Germandbls.alta.scb.scc.scd.sce.scf.scg.sch.sci.scj.sck.scl.scm.scn.sco.scp.scq.scr.scs.sct.scu.scv.scw.scx.scy.scz.sc hyphen.sc agrave.sc aacute.scacircumflex.sc atilde.sc adieresis.scaring.scae.sc ccedilla.sc egrave.sc eacute.scecircumflex.sc edieresis.sc igrave.sc iacute.scicircumflex.sc idieresis.sceth.sc ntilde.sc ograve.sc oacute.scocircumflex.sc otilde.sc odieresis.scoe.sc oslash.sc ugrave.sc uacute.scucircumflex.sc udieresis.sc yacute.scthorn.sc ydieresis.scij.scgermandbls.scalt germandbls.scQ_u.sc q.sc_u.sc dcroat.sc abreve.sc aogonek.sc cacute.sc ccaron.sc dcaron.sc eogonek.sc ecaron.sc gbreve.sc lacute.sc lslash.sc nacute.sc ncaron.sceng.scohungarumlaut.sc racute.sc rcaron.sc sacute.sc scedilla.sc scaron.sc tcedilla.sctbar.scuring.scuhungarumlaut.sc zacute.sc zdotaccent.sc zcaron.sc lcaron.sc tcaron.sctcommaaccent.scscommaaccent.sc idotaccent.scuniE0C0uniE0C1uniE0C2uniE0C3uniE0C4uniE0C5uniE0C6uniE0CAuniE0CCuniE0CEuniE0CF q.superioruniE0D3uniE0D4uniE0D5uniE0D9uniE0F9uniE0FBkreisuniE101uniE104uniE105uniE106uniE107zero.taboldstyleone.taboldstyletwo.taboldstylethree.taboldstylefour.taboldstylefive.taboldstylesix.taboldstyleseven.taboldstyleeight.taboldstylenine.taboldstyleuniE130uniE148 b.inferior c.inferior d.inferior f.inferior g.inferior h.inferior i.inferior j.inferior k.inferior l.inferior m.inferior n.inferior p.inferior q.inferior r.inferior s.inferior t.inferior u.inferior v.inferior w.inferior y.inferior z.inferioruniE188uniE189affii10086.altuniE19DuniE19E grave.cap acute.capcircumflex.cap caron.cap breve.caphungarumlaut.capspace_uni030F.capbreveinvertedcmb.cap breve.cyrcap breve.cyr dieresis.caphookabovecomb.cap dotaccent.capuniE365uniE366uniE367uniE368uniE369uniE36AuniE36Bmetric zero.slash parenleft.sc parenright.scbracketleft.scbracketright.sc braceleft.sc braceright.sc exclamdown.scquestiondown.scguillemotleft.scguillemotright.scguilsinglleft.scguilsinglright.sc hyphen.capuniF6BEf_ff_if_lf_f_if_f_llongs_ts_tuniFFFDVVWD%D!, d `f#PXeY-, d P&ZE[X!#!X PPX!@Y 8PX!8YY Ead(PX! E 0PX!0Y PX f a PX` PX! ` 6PX!6``YYY+YY#PXeYY-,#B#B#BCCQXC+C`BeY-,C E EcEb`D-,C E +#%` E#a d PX!0PX @YY#PXeY%#aDD-,EaD-,` CJPX #BY CJRX #BY-, b c#a C` ` #B#-, CUX CaB+YC%BC`B %B %B# %PXC%B #a*!#a #a*!C%B%a*!Y CG CG`b EcEb`#DC>C`B- ,ETX #B `a  BB`+g+"Y- , +- , +- , +- , +-, +-, +-, +-, +-, +-, +-,+ETX #B `a  BB`+g+"Y-,+-,+-,+-,+-,+-,+-,+-,+-,+-, +-, ` ` C#`C%%QX# <`#e!!Y- ,+*-!, G EcEb`#a8# UX G EcEb`#a8!Y-",ETX!*0"Y-#,+ETX!*0"Y-$, 5`-%,EcEb+EcEb+D>#8$*-&, < G EcEb`Ca8-',.<-(, < G EcEb`CaCc8-),% . G#B%IG#G#ab#B(*-*,%%G#G#a+e.# <8-+,%% .G#G#a #B+ `PX @QX  &YBB# C #G#G#a#F`Cb` + a C`d#CadPXCaC`Y%ba# &#Fa8#CF%CG#G#a` Cb`# +#C`+%a%b&a %`d#%`dPX!#!Y# &#Fa8Y-,, & .G#G#a#<8--, #B F#G+#a8-.,%%G#G#aTX. <#!%%G#G#a %%G#G#a%%I%aEc#bcEb`#.# <8#!Y-/, C .G#G#a ` `fb# <8-0,# .F%FRX ,#B=+-7,*+. +-C,7+-D,7+-E,7+-F,7+-8,++!# <#B#8 +C. +-O,8+-P,8+-Q,8+-R,8+-=,E# . F#a8 +-W,,+. +-X,,+0+-Y,,+1+-Z,,+2+-[,-+. +-\,-+0+-],-+1+-^,-+2+-_,.+. +-`,.+0+-a,.+1+-b,.+2+-c,/+. +-d,/+0+-e,/+1+-f,/+2+-g,+e$Px0-KRXYc #D #pE KQKSZX4(Y`f UX%aEc#b#D + ++Y(ERD +DLinLibertine_RIah.ttf000066400000000000000000026006741300200146000357560ustar00rootroot00000000000000connectome-workbench-1.2.3+git41-gc4c6c90/src/Resources/GeneralResources/Fonts/LinuxLibertine FFTMY ,GDEFdH\GPOS.>GSUBW1lOS/2WGP`cmapjGcvt (r 0fpgm蕏 Lgasp glyf@O8 head%h J6hhea35' K$hmtx!nzp K,$loca/~˿ o$maxp x namedC post2| DZpreptH LoB_2T0yzz{}~/001#$wx()BCFGHIJKKLNO  & ' ' ( (08@HP^fnv~   jLsb^B= =N Du *DEFGHIJLM ! " # $ % & DFLT cyrl0grek@hebrPlatn\(AZE (CRT (DEU (MOL (ROM (TRK (cpspkernmark $*"$ 2 ^|$'*  y$%&'()*+,-./0123456789:;<=   "$&(*,.02468:;?@ABv "(.4:@FLRX^d+\+\+\+\+\+ 24R $+ny&,  HIZXF "(.4:@FLRX^djpv| $*06<BHNTZ`flrx~ &,28>DJPV\bhntz "(.4 +N?#uhZ7{-))))F)=+-)'+)J+)+)+++'+mqJL'9+#)'o+^q#qsq3qd)sh+o+)')++)))))d))B))+))+ +)9J9J')fj)%+)d9 +q)d)b3+m++++'+B +)$%&'()*+,-./0123456789:;<=DFGHJKNPQRSTUVXZ[\]  CFLNVW^`dev   $&'(1@GSUcd^ig M #$%Nk  00!  $%#&345 ( (78 $*06<BHNTZ`flrx~ &,5wua)i)s_)ivh6fi)8`wu,8T)i)+)q)) )))))@)))))H))q)y)+)9'\PP7 ,28>DJPV\bhntz11H1111 111%1D111511111'1 &,28nht`R 28>DJPV\bhntzN!V)T^Z5=T!))TH 'FDFGHKLRVWXo  =/~& N "(.4:@FLRX^djpv| $*06<BHNTZ`fl ;;)3)/3/bmRXLU B{1d{/!RR%#/L-N$%&'()*+,./012356789:;<=DFKLNOPQRUVWX[]  CFL[^`deoq&@DIJS8^>uEkz sp. JPV\bhntz "(.4:@FLRX^djpv| $*06<BHNTZ`flrx~ &,28>DJPV\bhntz w##;%'% %!V!{7!w!%L%)!#%d##N%D#s%%%)}%ZZ%%d%!##!#!%##%%%#N%N!!`y j9+ HZ)%%%w%VV3#}%%b +mV7!%}!%L%%J%5$,.3 5=DLNX!Z],0123  4CC6EG7LL:NN;WW<[[=^^>``?dd@ooABDETU!X$)],,c./d11f?@gDDiGGjIJkSSmcdnVVp^^qrstgguvMM #%EENNkk  # % )+x,567 &,28>DJPV\bhntz "{ 'X#.9/q #]?I rAKxds 1Rq11L111o1111Lq1111Lj3jjjjL111L11111d     ""##$$ ''))+,..// 1122334477889:;;<<>>BB EE HH IIJJNNRS UUYZ[[\\^^aamm}}         $$&&**,,..00224466778899::AAHHKLQQff   --..//001122    //@@ AAEEGG       !""$$&&**--224477889:<<@@ DD EEFH JJ KKMMNOPQ RR SS TT UU VV XXYZ[[\\]] `` aammoorrtuyy{{}}       $$&&**,,..0022446677::BBIIJJ OO UUabcc  --..//001122  '(..@@GH  "#$')+,./1234789:;<>BEHIJNRSUYZ[\^am}  $&*,.0246789:AHKLQf-./012/@AEG  j =3q.!!""##$$//5566DDZZ[[eeggllnnpptt [[6699SSWW[[gg.!"#$/56DZ[eglnpt ==)))))+--2233668899==??@@DDFFHHIIJJKKOOWWZZ^^cceejjppuwxx~~PPPS|!6699@@HHIIOOPPUUWW[[ ]^ ``aacceefgii jjkkmmpprrxxPP  PQTT=-23689=?@DFHIJKOWZ^cejpuvwx~PPQRS|}~4 ) ))$$.. 9:;; <<::TTWWYY^^__bbcc ddgghhijkkllnn ot   TTUUVVWWYYZZ[\^_aabbccddeegghhijkkllnnotvv  7$.9:;<:TWY^_bcdghijklnopqrst     DFLT cyrlTgrekhebrlatn  SRB :    (AZE XCRT XDEU (MOL ROM TRK X   aaltc2sccaseccmpdligfinafrachligligaligalnumloclloclonumpnumsaltsinfsmcpsmcpss01ss02ss03sups"tnum(zero.     >FNV^fnv~&Tn N  H  v tn\f I   LMz~ "&,06:>BFJNRVZ^bfjnrvz~ $(.26:>BFJNRVZ^bfjnrvz~ $(,048<BFJNRV^flrx~S  n6( '57{)8t*9u+:,;-<.=/>0?1^e  TUVWXYZ[O\]^_`abcdefghijklm      opqrstuvwxyz{|}~PQopqrstuKvwxyz{|}~\!stfxR  2(6'789:;<>()*+,-/1sK $D  .59:>@DEFGHIJKLMNOPQRSTUVWXYZ[\]^`cm}   !#$%')13<>@V\^egjK')*+,-.06789:;=?@ABKOST !$%    eL]SnTUVWXYZ[]^_`abcdefghijklmopqrstuvwxyz{|}~^f] DEFGHIJKMNOPQRSTUVWXYZ[\]  !#%')13<>@ALF(> "*2:BJRZbjp""$$~(( "&.6&%%  !!##&''&              >@C^`cm}h1{tueghlmfijk  D]()..*00+66,JJ-.>>/ 0Z*  D]( )kS nTUVWXYZ[\]^_`abcdefghijklm        opqrstuvwxyz{|}~  sk $%&'()*+,-./0123456789:;<=>@^`cm}   "$&(02:;=?K@AB #IL !LI ,8JT^ " "LXMK &,28CIM $IOENDMFW "O IIW )& %W CM $O "47IWrA N]W JW &WFV.()*+,-./016? 4 g'36? 4()*+,-./013206789:;<=?'gg  >>   266789:;<=>?'2   '(SOstxK KV\^jOT @AB .5 QRKg"**BTILM . ""779:<<@@IILM_`hhlltuxx{{--//      LM I O3;34f P PfEd ''`o) |`TT~ACNou~wEMWY[]} ' 7 > B D K O q !!! !!!!!!! !"!$!'!.!9!!!!!!!" """+"6"<"A"E"I"Y"b"e"k"q""""###!#*#%%%%%%%%&&&&&'&/&S&`&c&f&l&&&'g',l,w!).3=FNjm!08KU^ac 8 CPPtz| HPY[]_  / 9 B D G O p t !!! ! !!!!!! !"!$!&!.!5!S!!!!!!""""#"6"<"A"E"H"Y"`"d"j"n""""### #)#%%%%%%%%&&&&&'&/&9&`&c&e&i&&&'g',`,t ,37FHPmp08HMW`X 8~~{[YXSQOMLonmj`[WTRC=<86& fd[ۥ۞`((((( (((('''''''''''''''''''''t'%$_\W *   !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`ardeixpkvjsgwq}dl|McnTum}b: ! "y{qz| @ "+ !7 !!f]=]f\H  \R f`R-4@1$" <b =M> --'" +7>32#"&5462#"757674&547> J-'5L-'5P'5 #9B RR-?3' -@4& 96;sBߔ)7JR9<ZNLJPh) " 9K&PX >[Y)$ +7>3207>32TL+;J-;jF%A5 F%A5 s{K!PX@'   d  TS >@1   de  TGK?Y@!+3#####737#7333337#LwDuuDs J!sFuuDu!ILb\\be\\esZ.9?m@?:/"  @ ddde =>Y@.-,+*) +6"#6?.7>?37'.'#7&7>7>.'% 8mba+O-".O~Nf "AEe:S>rqN%hiJ=hN@B fd*>6$  Q pVrT 1=X@U #<:  U UUUM><:64/-)'" 11$$%" +%32654.#"4632#"&27'#"'#"&5463232654&#"9#Pk%%HrhoJduX`bfD)Ll^u\ItZam\+ne9%Nj9#FtHBc1;ByVd{n+'+wXy33H?eD7'ER@QF>6.-(K$PX@+U M =M=M>@) UUM=M>YY@OMDB9720,*#  +"$5654+"?37+"327#"./#"&5467&546323267yB)ٖP:; jF97FFH6H=`R39'7R!/ۜ7ٓb~?[t@qZdjX=jnfN6=dDK#!#VdW\uhkA>NہZb;l^faevZ;h' 9K$PX >$ +7>320;K+;hF%A5^7"+3&547767&-1uy-%}+\m%|qd#%`"+'676654'&'7-3uy-%}+\m#}mh#u=@" :2&KPX@e=M >KPX@eV>@deINBYYY@ &(#'+%!+&#"705>3254&5465>32>32#"#".'#"&=>b!5'!'j#.7>)'9 ?2{+#$ H5#=h@ %Z^!.7[6Lb'  Ay;'5=Fq @eT>+!!#!7!3b@d?e=d dbde.f@ 9[ +7462&7>54.'&Hb=-oOF\-:H   +%2#"&546)5L3'7J4'/P8'-N7uBKPX@ =>K-PX@ d>@ d[YY@  + #7Vu`$*@'UM>$$  +"32>76'&"'&732%LZX#D?9^#9:L_sC?&!7)D`;!2vTm\L}(a#RVT #dǪCR&@#<ddK >$C+%&#"&67>76"&7$722l *R sX*5/{  /{; /  / @ww3#F )b0/@,'<bUK >((F+"+>32!2676&#!>?676&#"#"&/TVR?d^5L/%#KX@|"RDbo J'fJRE<+VobsN//3.:K@H54<b``UM>31,*! :: +""&7>32#"&7>3232>7!"7>76&#P\ J!ܼ>1uj'Ѷb @/ !N`R3;#ML;j.NPB_V:O:.5 ?`BV\]-?@<*$<dbV >)'#! -- +)2+3&#"&67676?!"563w90%/*  Vj m%o;@%ux!.## /  / /V*B@?< :bUUM>'$$#$#"+6&#"327#"'632#"&7>32326af}qbs :{VBhXn%\ 85#Z1N  !ݜ_;-B!   '&@#<#:UM>&"%$+3267>&#"'632#".7676% /\mN_X^.e_Il\3Xa3ouSM𴨠9m,FV@n{}mQ'FV=@:<dbU>  +"'67633!27'J'5N#V/'PK HRMO9 #.3@0*<UM>%$$.%."    +"676&#"&76767'&7>322676/Vpz5Bdy%Jj-!֔ZR%ucI{\'yzNiX/X`ۥi97!qyil:}{'+@(<#9UIMA&"&$+676&#"32#"&7>32&76 /]lN XC_.e_Hm\3X`15 VSM𴨠'f]A-FV@nzR(G PB 0@-UIMA      +2#"&5462#"&546)5L3)5J))5L3)5JB4'/P8'-N3'/P7'-NAB)@& 9eIMA +7462&7>54.'&2#"&546Hb>-oOF)5L3'7J\-:H+*1/*5+5(+,"+4632"&?>54&#"#"2#"&546+մCklg1 Yb^>^% /R7X^-`/Jk19HRqyNb˚b%RTNJ^a6%\jiap-<1'1 {TD<J@G0<b   V =K >;942/+('   +!#2&#"'763>76322&#"'7637654'!"uVTJ7q#H^5+%8;Yq9d3g9D"^1''>X)J!;6''= [#!- Fp@*98F@UUM >Y@ E?2,)'  (! +3 7654&" 3267674&#637>7654&/"5?3263 !"&#"'RR/Kfg@Uϲ "! $$ +".54732!"32672x<9hl)K-PX@UM >@bUM >YY'&hc1+763767674&/"?32632#"&#"'%3 654.#"'K(PX@9 ZbZU N  ==L >@7 ZbZ   UU=L >YY@JIHD(3T4&&#"5 +.+"32?6;#"?654&+3!2&#!"'7637>7654&/"?3!272m9DgHRX+"  +'.d= z;r^ZB<93 (/9 ?// + yfFRV4{>XZ4 " . ;))+ )m1I@- < <*:KPX@4Z  Z  UN = =K >K(PX@5Z  b  UN = =K >@3Z  b U  U =K >YY@CA;9642/)('#  II +#2&#"'7637>7654&/"?3!272.+"32?6;#"?654&b+21 D?5=6(/9 ?-19DgHRX+"  +' ''+.)J yfFRV4{>XZ4 "D6R@O$-<b``M =N>0.#  66 +23.#"327654&/"?27!"547->4 4elx=,31 w 1// ?D]} <>^Vc9 66 #1$@CD'7-O?K(PX@T = >@dT >YOHOG+6&/&67327&#"&6?>7!&#"&6?>76&/&67327!';FP=?=8 );FQ=?=:cc );FP=@=9 &@ K >YOG +6&/&67327&#"&6?>7&KPX@4b```R =M >@4db```RM >YY@ ##&$&%$* +!"&5463232'5%7>7'#"'.#"#"54632>32{ -&8m;qLy)%?"  \C 22 3:jJ'!*ACLT>-LJ%3 `D55/ !+)l 3%B?ZLg/Tr'-Y_@$F=@UV >Y@ RNED<8CG+6&/&6732767654/"?327#&#"'7654'&#"&6?>7&@dL >YU&K-PX@0b  `  ` U L >@4b  `  ` U L  =>YY@NMHD@?21/-C"+%#'"'#2&#"'763767654&/"&5?327!2&#"'76?>={s61sA7w6J!$7 ' m5R }=6 %5%-';''q)) PE''HN-AV@ < @U =N>YK"K"+#"'&"&#"'76?67.'&?!327654/&?327'/)X3)4j>-? W1*7ju?1Dd1R'%q-[ 'sq>S''D'@$M =M>  +"32654&!"547!2}9Ҝ5p;a; ZQ> n_ JQ/J!; @|@*:@&IUM = K >Y@ ?>C6*!3($" +3 674#"654&/"?32632#"'2&#"'7637>Td#f'= bH,L k+lM!HzV530.+)'%  +"32654&67&547!2632327#".#"'6}9Ҝ5ܘ; 4錜\EqJV`; *1-DZQ> n__JQ/JM-#hTTK=H>-3)!; H(@ 2KPX@8U M  =  M = K =M>K(PX@8 U M  =  M = K =M>@2 IU M  = K =M>YYY@" GDA?76541.+*#" H H   +"326767&'23#"'.#2&"'7637>7654&/"?326 bNdb# q3? `D;7IXqX+244=7 ,L @+-11(E'?Mf#*RPN1d)%? '')+ !  +hD58@5 <bM = =M>/$/$+'.#"#"?!27654&'.5476$32?-ZD`;XV-X6 >-ZP@[`3 j!9;NP's^d7QlB#*u*"Un( :UnA!#>?D1p@-<:K&PX@!b`M = >@b`U >Y@,+)& 11 +!2>7#.+"&#"&6?>76&+""'67!3=$ELo5/ ';FP=@=7 @ UM >Y@A@?>;743'%FF +".547654&/"?327#2>7654&/"?327#s3_gL0+23@D3=8 y8R`\H u+06!3=:o92MUAN '',+=pIf@\xRX ''.--*3K(PX@K =>@U>Y*CE+6764'#"''&67>'#"'27B )= T="XA  p5-3mu)  )69/,)  )1)/0R-@^@bUM>Y@ &(CF+.'.73277376&'.7327#"'.'&##"&'ER  Jo BTFNYBLLV  CBB V  4o  ^N7)  )+0+E}78)  )w-&O a#-l@&%\?8@)  b  `U K  >Y@`^[WLKBAC#C&'"B +?3273#327654/"?327#2&#"'7637>54'&"&#"57636767654'&'& /7 B  C+%9#!7\3+!Hj /= )% &0%  %/wyX;Y''+f0)))/.%$R )) /. ))}M-A?K(PX@b = >@bK >Y@ 73+*"G +%&#"&6?67654'./.732736&/&67327 );FP=?qH!)3sb;D1  C%B-w615' /)-' Uv"T3+-#)-/-.7;TcD)T@ ('@U =N >Y7"!7@+&#!"547676#!"'673!>327!267923!;dm?:5:I4v!T )FP1f<;PTs $O  mfd!@OK> +!!!d+H?X[}@e> + #mZf!@OK> +7!!7!-!f[jXb3@<e > + #3#ZqVZ l@GK? +!7/ZZN@e>'% +'"/?>32hc#1) )w@  @.b=M=N=M>Y@ #&#&"$+33272'"74?''"432>?&#"326H#PZTǔZ5%=BZ1wbm!\-CV>9:;' 0d@"U=M=M>Y@ )$6&&+>32'"&7654/"'76724'"32Hq+)\\2Vohe+? lm)-!5= E3h1-R%{ygLf?'9 1 *R9 !9J bb(:@7b`M=M>$#! (( +"'&5467>32#"&4654#"32672F=ZN=ANd) ?+V}3%BLCObVTAO#E6'%7'5!N݉B/QLn` ;@*% 5 @=  b  `U=M=M =M >Y@8643'4%%$$ +654&#"326'#".54323674/"576723272'"547^J7ZZP9N^NTbJ7XL+@ m#PZTN;wo}jwE?2oF 9 1 *RLbm!\Qd (1@.<bM=M>'""+4&#">2672#"&54>32-7XBvuFPgT!{#Prgb`%Ll-!#=g6i4V!㮉P{Nd3=+>/4_0G!KPX@0b ZM=M= N>K*PX@1b  bM=M= N>@/b  bUM= N>YYY@ 97$'#%#$$$ +#"5476?7>32#"'.#"32'# #"&546323267> 76 1?b98j%)L$%#d#' 0"?0)$@1bUM=N=M>Y@ #&"#".,)$ +32654&'&#"'%".54%&5467&54632632#"'&#"4&#"32J}|KX}T8/DAq%.^5C+^J^m#-%#  @)E@^A`Xe^B5L)9u#X%& = cMD3-qVV)9'#;om9:@+@+bU=M=M>Y@ ("'&7% +%#"547654/"57672>3232672#"547674&#"h T+? m|XNN'# T#M#%5e>!(7l?'9 1 *Rԙl'tb#<+#C1GV3-`'1@.<bU=M>,"%+462"32672#"547674/&7632;N;;N\N'# T#`%R-* fR::R9cnb#<+#CN%!+6g`oj#+NKPX@ZUR>@bUR>Y.!$&+#"&54632326767674/&7632462"&%9TZJH`%7D%T@A%RL*9R99R\3/ XatR@:Z  bU  U=N= M   >Y@IG@>864320!##4 +7654/"576722?632#"&#"3272#"&'.'&#"#"54+@ m 0IRfdD%57j/% E!   'Cfi=Y4?J T?'9 1 *R?FH+;_ +7? b`r*$! B*7}c@  @ bU=M>Y7"$+32672'"547654/"'7672JX+}V+? lZmB5#Z-?'9 1 *RJV9@6<bM=M >SQ(()"(%(( +654/&763232>323272#"&547654&#"#"747674&#" #"747u%R-* wmb5H` ;U'^*$P3}N TbR TV%!+7d5+XZHN'+''BHI+!>!(7mHA-<"'7l)95@21 <bM=M>','("#+%32672#"5476'4&#" #"547654/&7632>32wN'# T#N"%^N Xt%RP*+Vb#<+#C1Gq->!(7lV%!+Hzj't@M=M>%'&"+327654'&#"4>3 '".J^5RX.Nd5?w{!No\{5\}}?Zx`êjF'K{e1>N@K#7 <9dbM=N=>#&);$!+63 #"'&#'54?67"'6?>324#"3267>!yrV?Z' P&\f-3 F V GPm=[9r*8>;H˾C!& ) ' NT;+Xwy D+8 -8FVQBW69@6+*<09=M=M=>;#&($"+4&#"3267>'#"&54>327632&#'54?>=E778;#0mhRwN;%S' P'11?VɾdcT+ḉZlS' XH ) ' )o+)@&<bM=>.'"$#+>32#"'&#"#"54>7654/&7632DL566'!$9< N r%R%,#H;%+H+@(7#090%!3y+:@7b`M=M>! +* +.5463232654'&54632'"&'&#"#df%')1)+Lb}\J%5T=DRm^;/B+!HPdTydj:FA);V;J7ZMu}(o@%dbM=M>Y@('%##% +7>323#3272#"&547#"54767} !Ry3q%3H31| 1ofP%#7w\ <d:'\v(6)- !L=C@@$ <b`M=M>''"('"$ +32672#"54?'#"&547654#""'632326?>32 rN'# L#}o;NZX%# J+X=H}< %V -b#<+#CB{OQR=MXb#;+#DH;LR##7 .7(*%@"$"<b=>&-++%67>54'&5432#"'&"&'>32p #]>\+ _k8%/.EfC+V5@5# :173}S@:ALaY G3GAbqK}03 Ys9;@8 <b`=>1/'% 99 +"'&"&'>32676767>54'&5432#"'&'!V5@5# :153F+:-7!+/-9J+ _k7S\`:=!2 h1}03 YsLB9,4;MG:ALaYÂILx$,HJ<KPX@<,$& <@<,$& @7Z`  ZN=M=M>Y@ :9$&$$!$&%# +&'&"&'>327>32#"&#"327#"&/#"&5463227 +)]<"%-:fJtP'@+)5#;%o%A#V1/OBY6)M+#=y@,+,^?ThL-%#5)7LT!1A^A(%!- 'd!e99@6%$<bb=M>31%("##+!#"&463232676'&'&"&'>32676?674'&5432]bR'V<#' -t<J- +)]<"%/*# (&+ _k7,)Z;%b)5A,+,^DS">@,,p:ALaY42#!*"## +232>72#"'&#"&'&'67#"&#""'>327;jD@Z&7"#1J%!FpFZVVFD u&3HC!'x496Tc#=;r3594 '-E^D&CK*PX@ e>@ d[YY"! +&'&547654'767>7;APTd; 9'? f%FD/)u!/y3\D)/#BE+L-`g9' 'B3R#7@ => +#3Z?[#D9)F@ K*PX@ e>@ d[YY%$ +>7&547674'.'77676?RTd9  %'@d'DCJD.)sw!+{7XPA.1!'F-I?O11 '{FFB90@- < :9UIMA!&!"+>323267#"&#"'Z/-5+!/P#769-fDE dwRA='+2@/$<bM=>++&" +#"&547>32"&547>7>7632I.'5J-)4'6Z-  R'-B4& -?3) 75 LBb^)X7S\TRLIw%-/@,'<dddd>'+3#"&4654'672#7"'&'467>7FAS! @+ & 'bVS:LljB!'=@ <<;KPX@5  Z b`  U  SM>K1PX@6  b b`  U  SM>@:  b b`  U  SM=>YY@65430.,*&$  == +%23267#"'&#"#"&7676767#73767672#"'&#"!!6PN1;1}+!^PfT:V ; i7 -}1c 6%-+F 9  9'1/B55%'=2 o5Rj-.%(%-}sR cj/K@H  < :9UIMA.,&$ +%"''7&54767'76327'3267654&#"{VkF}/`ZZJsvyXsB+YZXLpk\fkThJoRXLV"aTOfHLqTZDV""}`ZNkLh^yp%^{Ob@_4OONMLKIFB@<90-)'#333+!;2&#'76;2?!7!7!7!.+&?27#"676&+"?27#"!! -;8P5q &&!.1 {1oZ9R; TPq/+''ZqPR1- %# 4L7  %#`%R\ddOKPX@b`c >@ddd[Y@ +##DedJDE^d@ RF#@!b`RM >Y@ 42/-)'##$ +&54632#"'.#"#"&5463232654'.54>>54.'&'&':í{VD->VxFGND0+bF4ۡz+2=)=U}NC/-cM@E!K86 K1-1+!K76 QTOWqCiTDEiX;s+-/50B#&Ojp0DPoE(@TDE}ZoT-/60A#%Pjq-gN1RI*$13!/Z91QI*$5=7D ,@)IMA      +2#"&546%2#"&546!-H'#+Fs!-F)!-F+!/@+#-@-!-@+!/@F/#-x@  @& UUUR>Y@%$*($-%-$&$" +#"&546323.#"327  "32#{NPk7 #E6JzjesG%!TR--7Lw+)1{mhVTR!V-- ?u@/#=>@$bUIMAY@ <:31+) ? ?& +327"&547#"&5476?674#"#"5465>32727Vh F1X3 =J-5Bv Jj ) A #  .!?1 9C+/!<1 {5))X7 % 9]+);  !-/B "+&'&'70767&'&'70767b^eFu 2/^eDu 1-u1q ! {2ju1q ! {25#@ eGK? +!7!PT`ZR&@# <IMA  +!"765>3!2/+.3 8F/*5=GK1PX@0& <@&0 @6  b   U  U U QM>Y@?>DB>G?G;:7621$$222"+3#"/&#;2&#54;254&=6324&#"2>  "32V;c%-Z%b -Vq!Hh[f63!+7!TR--/N?N    ^;5B:bTR!V--;;$@! <IMA  +!"'47>3!2F !%;(, !@<QM>&)&"+>32#"&54%4654&#"326TJ`TL`X:7=[ >5;[Tw^J Tx^J 1JZ<1DX%+oKPX@#dbTK >@)dbTGK?Y@  +%!7!!#!7!3+`=g@b@ddd1dbde *b@!bUIK?Y@$" ** +"&547>32327!67>7654&#" mPn mlFI/)![ZN7!q,5i?FBnD`X Zgq=\N /-<  4@ 1KPX@&b`QM=>K1PX@(b``QM>@.b``UIMAYYY@ %"$-##$+4654'"#"5>32#"&546323267654#"7>{F'O 1)uHPs3R|;o(G1{ !H`;%#;%)f3/ R9HA h+%L\ d1 OV @<e>&! +632#"47d =1%)-!#F@ -&@2bb=M=N=>Y@ ('")'%% +>32326?>3232672#"54?'#"'"#"&547>7 V`3D< %V rN'# L#}o9J/7c@!'7w\R##7 .7 -b#<+#CB{O/;^Ru^;-)<C!+=k$@bUK>Y@;860'%!  +267654&'"732!76;>7#".5476$323?+")#) %=  a <3%5m1^dJ1#Z =3' X7H+#+-)n D)!++/P3L}M;4 +4Ku@IMA +"&462VDDVCCVDDVZH@E<;bUIMA  +"'73632#"'732654&3+3 yEL5DjHJ;79Y) v10Ld#%7/^@ @ddIL@Y@  +"'673;2&'7673267>./L}!i9 jG J-;!7/D!* ++ + 8m+ (@%UIMA   $" +>32#"&"32676&isPJf?V9kDosqTyXVRby}/B "+&'70'6'&'70'6^eFu 2/^eDu 1-u2r !!{1ju2r !!{1ZX!%K@" KPX@2 bbT  V  O >@8d dbT  V  I  K ?YY@ JIFB@?<;:953)'%$ !! +'"'673;2&#76;267>.37#"'476767676323#&#"&67>TV-/L}!i9  jF J, ;' %  Z/(Rsf %;=ad= D1Tf 7/D!+ ++ +!7Bv1  m+ E15%!' ^F!L@87@5d d   bT  UIK?Y@#"FD;:64+)"L#L !! +'"'673;2&#76;267>."&547>32327!67>7654&#"TVZ/L}!i9  jF J-; lPo mmFI0)!ZZN7!q,Tf 7/D!+ ++ +!75i@EBnD`X Zgq=]N /.< ^X-bg@:. D _ KPX@E  b  `  ` b  UO M =N >KPX@C  b  `  ` b  UVO M >@J  b  `  ` b  U  UVIK?YYY@^\WUSQMK><97&B*"+'37#"5476767676323#&#"&67>4654#""5>32#"&546323267654#"7>TV' %  Z/'Rsg %;=ad> D1F'P1)uHPt3S};o)G1{ !H`TfAw1  m+ F15%!' ;$#;%)g4/R:GB h+%L\ d1 O+7C@ (K-PX@!b`QM>@'b`UIMAYY,/'+'"+#"&5465>326'&7>32#"&5465>7676?672326L-'5 J-'5 8!Fum gTLb5  XG\JV}--?3' -@4$#!'BfRPq /LB3?(JKZ^;R8ux%VtETEU@R9<:db   V = K > DB=;8410)'#!  ! +#"'%7!#2&#"'763>76322&#"'7637654'!"'7߲VTJ7q#H^5+%8;Yq9d3g9'\q"^1''>X)J!;6''= [#TE[@X9<: db   V = K > DB=;8410)'#!   +"547%!#2&#"'763>76322&#"'7637654'!"++!JVTJ7q#H^5+%8;Yq9d3g9\"^1''>X)J!;6''= [#T G^@[;!< db   V = L > FD?=:632+)%#    +&''67!#2&#"'763>76322&#"'7637654'!"J=e+sJwyVTJ7q#H^5+%8;Yq9d3g9hX+;DL3+Nr"^1''>X)J!;6''= [#TbPx@uD*#< ;:  bU U  V = K  >OMHFC?<;42.,)%"!  +267#"'&#"'>2!#2&#"'763>76322&#"'7637654'!"^$7!.J3EA%'7/)_F4EVTJ7q#H^5+%8;Yq9d3g9-= 5-.4 ]Y9-L"^1''>X)J!;6''= [#TLX@U@&<  b U  V = K  >KIDB?;870.*(%!+462"$462"!#2&#"'763>76322&#"'7637654'!";N<X)J!;6''= [#Tf JR_@\H ?8$<   b U   V = K >LKNMKRLRCA>:76/-(&#    +"2676&>322&#"'7637654'!"2&#"'763>767.!#.F +[G ,GHV R68;Yq9d3g9J7q#H^5 :?VT/C0.DE-1BsFdcG5W;6''= [#1''>X) ]"+b@43 S `_ @I Z  b  U U S =L =K >Y@bb]ZXVRPHEA?<93;3+!6'0'763767!2&#'76?>76&/"?3!.+"326?6;2#"'7.+3!2678l6o N '7  N-732!; X; #=+!1P1/ +)-#/e Ff<9R#q)+7%Uj'  +' 'H%)) 76J"5%j)2=APN@/+H ˋf\D<@*6 4 <;KPX@:b`b  X  QM =M>@;b`b   `  QM =M>Y@9731-+&%#! << +"'7.54732!"32672632#"'67726545* mv<9hl)K(PX@> d  ZbZU N  ==L >@< d  ZbZ   UU=L >YY@SRQMJH3T4&&#";! +'"'%7.+"32?6;#"?654&+3!2&#!"'7637>7654&/"?3!272'7߲9DgHRX+"  +'.d= z;r^ZB<93 (/9 ?// '\ yfFRV4{>XZ4 " . ;))+ )mR@ 2:<:KPX@>  d  ZZZU N  ==L >K(PX@?  d  ZbZU N  ==L >@=  d  ZbZ   UU=L >YY@RQPLIG?<940-)'!  +"67%.+"32?6;#"?654&+3!2&#!"'7637>7654&/"?3!2723+ J&9DgHRX+"  +'.d= z;r^ZB<93 (/9 ?// -\f yfFRV4{>XZ4 " . ;))+ )m} U @ 5=  d  ZZZU N  ==L >K(PX@?  d  ZbZU N  ==L >@=  d  ZbZ   VU=L >YY@UTSOLJB?<730,*$" +&''67.+"32?6;#"?654&+3!2&#!"'7637>7654&/"?3!272=e+sIwy89DgHRX+"  +'.d= z;r^ZB<93 (/9 ?// }hY+;DL3+Ns yfFRV4{>XZ4 " . ;))+ )qN!l @ %4L T K(PX@C  Z  b Z UU N == L >@A  Z  b Z U  UU= L >YY@lkjfcaYVSNJG&&#"9&''"+>32#"&546%>32#"&54.+"32?6;#"?654&+3!2&#!"'7637>7654&/"?3!2721B'!+@'!-@'!+@'#+9DgHRX+"  +'.d= z;r^ZB<93 (/9 ?// '5)! '6+! %7+'8+# yfFRV4{>XZ4 " . ;))+ )'.8:K(PX@d = >@dK >YOM! +#"'%76&/&67327&#"&6?>7'7߲&@dK >Y@ &" +"47%6&/&67327&#"&6?>7+!J &@dK >Y@ )%  +&''676&/&67327&#"&6?>7=e+sIwy&@UK >YOK&''"+>32#"&546%>32#"&546&/&67327&#"&6?>7-B'!*?'!-@'!+?'!-&K-PX@U SM >@&bU SM >YY@ ?>'&hc1 +763767#73674&/"?32632#"&#"'%3 654.#"3#'@*U U U =N>Y@OK@><;,(  +267"'&#"'>2#"'&"&#"'76?67.'&?!327654/&?327G$7!/J3EA%'6/)_G3f'/)X3)4j>-? W1*7j,> 6-/4 ]Y9-?1Dd1R'%q-[ 'sq>S''(2@/:dM =M> %#  ! +#"'%7"32654&!"547!2'79Ҝ5p;a; '\ZQ> n_ JQ/J'8@5:dM =M> $"  +"67%"32654&!"547!2 + J]9Ҝ5p;a; -\ZQ> n_ JQ/J} *=@:<dM =M> '%    +&''67"32654&!"547!2=e+sJwy-9Ҝ5p;a; }hY+;DL3+NsZQ> n_ JQ/JX#3W@T< ;:UU M =M>0.(&##  +267"'&#"'>2"32654&!"547!2A$7!/J3EA%'7/)_G39Ҝ5p;a; ,> 6-/4 ]Y9-ZQ> n_ JQ/J/5@2UM =M>,*$" +462"$462""32654&!"547!2;N< n_ JQ/J "+''%'7%T;'T)9GGHH\!+?@<#"  <: 9M =M>&$!!*( +!"''7&547!2%"&326563U;a迂6{;Y95\yh5\3JMd3JQ/WZQjCȉ> n_^Ow@ E!<:K(PX@!d K =M >@d UM >Y@ JIHGD@=<0.&%$#  O O! +#"'%7".547654&/"?327#2>7654&/"?327#'73_gL0+23@D3=8 y8R`\H u+06!3=:o9'\$2MUAN '',+=pIf@\xRX ''.-N~@ D <:K(PX@" d K =M >@ d UM >Y@ IHGFC?<;/-%$#"N N +"67%".547654&/"?327#2>7654&/"?327#+!K3_gL0+23@D3=8 y8R`\H u+06!3=:o9-\[2MUAN '',+=pIf@\xRX ''.-} Q@G#@ d UM >Y@ LKJIFB?>20('&%" Q Q +&''67".547654&/"?327#2>7654&/"?327#3=e+sIwy3_gL0+23@D3=8 y8R`\H u+06!3=:o9}hY+;DL3+Nso2MUAN '',+=pIf@\xRX ''.-:BJq0@  UUN >Y@JIFEBA>=32/*  :: +".76&'"?32722>76'&763327462"6462"q9muD" E9VBbXs 'Nud3hN b31jfR?9N99N9N99NBp}74%jZc3fYf!%}ӰZN99N99N99N9MIX:K(PX@db = >@dbK >Y@?;32*& +"67%&#"&6?67654'./.732736&/&67327?+!K );FP=?qH!)3sb;D1  C%B-w615' -\ /)-' Uv"T3+-#)-/-.7;R+ A@-: @(UUU  K >Y@?>=<9632)33#" +3 6&#"'6&/"?37#632#"'2&#'7637>7un)@!lb);3M8;7FjhJ- J{}m$#'95 N 5=6  G-"''%+s/N`b.?{N/%'')+XU@ 10KPX@8bZ M=M=N=N>@9bb M=M=N=N>YY@ PN$!,"#<$$& +'"&546323267>774&+"546327!2#"3232654'7>54#"_2^3T}_M%"8  9!"#DT P{p;D\+T9-\JIkF}=c@5j*Js<&'23'#m^ yn.boN/>fwBR@ -}Ӻ>lH{9NiRb )7@ . K1PX@.ddb=M= N>@8ddb=M=N= M>YY@ 64&#&"$$% +#"/763233272'"74?''"432>?&#"326[ #HG\#PZTǔZ5%=BZ1^)/bm!\-CV>9:;'X )7@ . @;bb ==M=N= M>Y@ 64&#&"$$! +632#"&76733272'"74?''"432>?&#"326 ?(/ ##PZTǔZ5%=BZ1T/' $bm!\-CV>9:;'9 &4@+@4b  ==M=N=M>Y@31.,&$! +&''6733272'"74?''"432>?&#"326-l1{8m#T#PZTǔZ5%=BZ19mkN]\c>bm!\-CV>9:;'0>@5' < ;:K1PX@5  bU  U= M = N>@?  bU  U= M =N= M>Y@=;860.+)#!  +267#"'&#"'>3233272'"74?''"432>?&#"326*$7".J3FA$'70)`@AG3L#PZTǔZ5%=BZ1-> 5-.4 ]Z:-bm!\-CV>9:;'+9@ 0" @8  b U= M =N= M>Y@8631+)&"$ +462"$462"33272'"74?''"432>?&#"3267N77N7N77N #PZTǔZ5%=BZ1oM88M88M88M8bm!\-CV>9:;'h 0>@ 5' K1PX@5  b U U= M = N>@?  b U U= M =N= M>YY@=;860.+)#!   +"2676&>2#"33272'"74?''"432>?&#"326.E *[G -V}IG#PZTǔZ5%=BZ11C0.DE-1BdbHEebm!\-CV>9:;'{ M\@Y,D?> <b  bUM= M  = N   >LKHFCA$"#(%)' +>54&#"72764&#"#"54632632327#"'#"&5%6sRjL5L?d-XbHPTX0)RJ;)B/Lrk q`}BbT;T^ 'D-/G %3^@6b``  V  QM=>Y@><8620*)'% AA +"'70#"'&5467>32#"&4654#"32672632#"'67326545* hF=ZN=ANd) ?+V}3%BLCE7CcHHH)8C bVTAO#E6'%7'5!N݉B/QL+w6.Oc"!9/2db 6t@ !@'ddbM=M>Y@ '"#$%+#"/76324&#">2672#"&54>32[ # HF7-7XBvuFPgT!{#Prgb`%Ll^)/-!#=g6i4V!㮉P{Nd3=+>/4X 6E@B!<bb =M=M>'"'$!+632#"&7674&#">2672#"&54>32 ?(/  -7XBvuFPgT!{#Prgb`%LlT/' $-!#=g6i4V!㮉P{Nd3=+>/4f9 3J@G<b =M=M>*(!  +&''674&#">2672#"&54>32-m1{9m#Tq-7XBvuFPgT!{#Prgb`%Ll9mkN]\c-!#=g6i4V!㮉P{Nd3=+>/48?@<#<bUM=M>'"# +462"$462"4&#">2672#"&54>328M88M7N77N5-7XBvuFPgT!{#Prgb`%LloM88M88M88M8-!#=g6i4V!㮉P{Nd3=+>/4Lb -c@"ddb=N>Y,"%$%+#"/763232672#"547674/&7632[ # HG R\N'# T#`%R-* ^)/.nb#<+#CN%!+6gu -c@"ddb=M>Y,")$!+632#"&76732672#"547674/&7632q ?(/ R\N'# T#`%R-* q/' $nb#<+#CN%!+6g9 *?@<<b ==M>&$  +&''6732672#"547674/&7632?-l1{8m#T\N'# T#`%R-* 9mkN]\cWnb#<+#CN%!+6g/5@2<bU=M>,"%+462"$462" 32672#"547674/&76327N77N7N88Nt\N'# T#`%R-* oM88M88M88M8Ynb#<+#CN%!+6g-!5=@:*< :M=M>#"-+"5#5$ +>32&''7'&'77#"&542676'4&5&'"CX@T1>)jN9º^^45J}R3g}k5B#?8+BNB#ml1յvbFwwAJAy)Nj@gF< ;:bU  U M  =M>KIB@42+)!  +267"'&'"'>3232672#"5476'4&#" #"547654/&7632>32~$7!/J3FA$'7/)`@AG3'N'# T#N"%^N Xt%RP*+V-> 5-/4 ]Z:-ob#<+#C1Gq->!(7lV%!+Hzj'tb-VK!PX@"b =M=M>@ddM=M>Y$)%'&"+327654'&#"4>3 '".#"/7632J^5RX.Nd5?w{!No\{59[ # HF\}}?Zx`êjF'K{eG)/X--@*b =M=M>$%%'&"+327654'&#"4>3 '".632#"&767J^5RX.Nd5?w{!No\{5 ?(/ \}}?Zx`êjF'K{e=/' $9*4@1('%#"< =M=M> * *%'&"+327654'&#"4>3 '".&''67J^5RX.Nd5?w{!No\{5E-l1{8m#T\}}?Zx`êjF'K{e"mkN]\c4N@K$.<-;#:UUM=M>! 20+)'% 4!4%'&" +327654'&#"4>3 '".267"'&'"'>32J^5RX.Nd5?w{!No\{5W$7!/J3EA$'7/)`@AG3\}}?Zx`êjF'K{ex-> 5-/4 ]Z:-/,@)UM=M>%'&#+462"$462"327654'&#"4>3 '".7N88N7N77NF^5RX.Nd5?w{!No\{5oM88M88M88M8%}}?Zx`êjF'K{eF/ WKPX@SQM>@!USIMAY@ $$$"+%4632#"&4632#"&!7N1#-P/#-N1#-P/%+/L+"3H*/N-#1J+dd +:@7+ <: 9M=M>($*(+'"''7&74>3227654'&'&#"Cz,No[?_DcE?w{xEIn*9RX.Nd5 1NF''u7y\`êj8F}% ZiH9Lb=K@$ @1 d  db`M=M>Y@KIEC''"('"$ +32672#"54?'#"&547654#""'632326?>32#"/7632 rN'# L#}o;NZX%# J+X=H}< %V [ # HG -b#<+#CB{OQR=MXb#;+#DH;LR##7 .7)/LX=KT@Q$ <  bb` =M=N>GEA?''"('"$ +32672#"54?'#"&547654#""'632326?>32632#"&767 rN'# L#}o;NZX%# J+X=H}< %V  ?(/ -b#<+#CB{OQR=MXb#;+#DH;LR##7 .7w/' $L9=HW@TFECA@ $ <b`  =M=N>>>>H>H''"('"$ +32672#"54?'#"&547654#""'632326?>32&''67 rN'# L#}o;NZX%# J+X=H}< %V -m1{9m#T-b#<+#CB{OQR=MXb#;+#DH;LR##7 .7\mkN]\cLMU@R4 ! <  b` U M  = M>IG@>7532('"% +462"$462"32672#"54?'#"&547654#""'632326?>328N77N7N88NrN'# L#}o;NZX%# J+X=H}< %V oM88M88M88M8-b#<+#CB{OQR=MXb#;+#DH;LR##7 .7d!eZ8F@ %$@+ddbb=M>Y@ B@<:20%("##+!#"&463232676'&'&"&'>32>?674'&5432632#"&767]bR'V<#' -t<J- +)]<"%/*# O *+ _k7 ?(/ )Z;%b)5A,+,^DS"}Bp:ALaY<"d/' $15B@!<"+<09KPX@)=M =M=M=>@'U=M=M=>Y@ #);&%:+'67654/"57672632#"'&#'54?64&#"32>md:';e+@ mQ}{PuhDZ' P'[.P^oj5fbR)PRf{=)9 1 *RHXѽ~C!& ) ' ^7Vod!eHG@D+54)<bbU =M>B@%("#$ +462"$462"#"&463232676'&'&"&'>32>?674'&54328M88M7N77N]bR'V<#' -t<J- +)]<"%/*# O *+ _k7oM88M88M88M8ɏ)Z;%b)5A,+,^DS"}Bp:ALaY<"Tj<H[@X0<b   U   V =K >?=EB=H?H;942/+('   +!#2&#"'763>76322&#"'7637654'!"!"7>3!2uVTJ7q#H^5+%8;Yq9d3g9 %'D"^1''>X)J!;6''= [#E(*)5@  @7b  U=M=N=M>Y@,*2/*5,5#&#&"$ +33272'"74?''"432>?&#"326!"7>3!2H#PZTǔZ5%=BZ1 %'wbm!\-CV>9:;'()To<Ne@b0<  db   U  V =K >>=LKIGBA=N>N;942/+('   +!#2&#"'763>76322&#"'7637654'!""&=332673uVTJ7q#H^5+%8;Yq9d3g9ux9'P7R7D"^1''>X)J!;6''= [#u\)!E8m{-)6@  <60/*:K1PX@,b  U=M=N>@6b  U=M=N=M>Y@ 42#&#&"$ +33272'"74?''"432>?&#"326267#"&7H#PZTǔZ5%=BZ1Ƴf!2+aluwbm!\-CV>9:;'RfbV~HLK@ I60@& VQ = K  >Y@"HE@>;852/,'%! KK +!"327#"&5467#'76;254'!";2&#'76;27632;2ylh5{5/JR+>LWl+9jCR;V;2h@) <;D" X+5GDEIBR3'Da#+''m`F'=:'Z.<@@=3+$#<Q=M=M>#-$+%##+%7'"432332?673267#"&547&54>?&#"326!׊Z;o)fA'L191+>/sL^1G'=HZ1ƴo ^  Qd+a%5#'JAyD9D9;<'\$-Q@N<+*:db`M =M>&%%-&-"! $$ +".54732!"32672"747%x<9hl)@/ddb`M=M>Y@20,*$#! (( +"'&5467>32#"&4654#"32672632'"&767F=ZN=ANd) ?+V}3%BLCO ?(/ bVTAO#E6'%7'5!N݉B/QLnr/' $\s$/T@Q-,*('<db`M =M>%%%/%/"! $$ +".54732!"32672&''67x<9hl)@+db`M=M>Y@)))3)3$#! (( +"'&5467>32#"&4654#"32672&''67F=ZN=ANd) ?+V}3%BLCOø-l1{9m#SbVTAO#E6'%7'5!N݉B/QLn?mkM][c\`$,L@I<b`UM =M>,+('"! $$ +".54732!"32672 >2"x<9hl)0/,+$#! (( +"'&5467>32#"&4654#"32672>2"F=ZN=ANd) ?+V}3%BLCOGP.GObVTAO#E6'%7'5!N݉B/QLn}P;;P:\$/T@Q<-,*(':db`M =M>%%%/%/"! $$ +".54732!"32672&'767x<9hl))))3)3$#! (( +"'&5467>32#"&4654#"32672&'767F=ZN=ANd) ?+V}3%BLCO#4e/{8]!]bVTAO#E6'%7'5!N݉B/QLn~kMOiq!%7BK-PX@%<@?=;::@%<@?=;::YK(PX@dM =M >K-PX@dUM >@"dbUM >YY@888B8B'&hc1+763767674&/"?32632#"&#"'%3 654.#"7&'767'@D  b  ` U M =M=M =M >Y@=76#"&7>^J7ZZP9N^NTbJ7XL+@ m#PZTb71 d>W 88N;wo}jwE?2oF 9 1 *RLbm!\Q[;B # W)+-!+!-)?K-PX@ )<@ )K-PX@U SM >@&bU SM >YY@ ?>'&hc1 +763767#73674&/"?32632#"&#"'%3 654.#"!!'@H  b `  U T = M=M=M>Y@A?;9440/.-+(%'"$+#3272'"54?'#".54323!7!654/"57672654&#"326#PZT^NTbJ7X@ +@ m^J7ZZP9Obm!\Q6}jwE?22O79 1 *RN;wom7JV@*2K(PX@B ZbZ  UU N  ==L >@@ ZbZ  U   UU=L >YY@MKSPKVMVJIHD(3T4&&#"5+.+"32?6;#"?654&+3!2&#!"'7637>7654&/"?3!272'!"7>3!2m9DgHRX+"  +'.d= z;r^ZB<93 (/9 ?// E %'+ yfFRV4{>XZ4 " . ;))+ )(* (4B@?<bUM=M>+)1.)4+4'""+4&#">2672#"&54>32!"7>3!2-7XBvuFPgT!{#Prgb`%Ll %'-!#=g6i4V!㮉P{Nd3=+>/4()mJ\@*2K(PX@H d ZbZ  UU N  ==L >@F d ZbZ  U   VU=L >YY@LKZYWUPOK\L\JIHD(3T4&&#"5+.+"32?6;#"?654&+3!2&#!"'7637>7654&/"?3!272%"&=332673m9DgHRX+"  +'.d= z;r^ZB<93 (/9 ?// uy:'P7R7+ yfFRV4{>XZ4 " . ;))+ )Tu\)!E8m{/ (5B@?<5/.):bUM=M>%'""+4&#">2672#"&54>32267#"&7-7XBvuFPgT!{#Prgb`%Ll2e 3+`lu-!#=g6i4V!㮉P{Nd3=+>/4RfbV~mNJR@*2K(PX@A ZbZ UU N  ==L >@? ZbZ U   UU=L >YY@RQNMJIHD(3T4&&#"5 +.+"32?6;#"?654&+3!2&#!"'7637>7654&/"?3!272$>2"m9DgHRX+"  +'.d= z;r^ZB<93 (/9 ?// qFP/FP+ yfFRV4{>XZ4 " . ;))+ )O;;O;d0;@8 <bUM=M>'"#+462"4&#">2672#"&54>32`/45m1_l@(7O Q ]\ <%:KPX@DZ Z  Z  UN == M  = M>K(PX@EZ b  Z  UN == M  = M>K*PX@CZ b  ZU  U= M  = M>@@Z b  ZU  U  Q= M  >YYY@"[YUTMJFD><641/-*$#" __ +"&5467&#!"'7637>7654&/"?3!272.+"32?6;#"?654&+3!267"327;TjpZ]0ZB<93 (/9 ?//9DgHRX+"  +'.d= z;'O +sA6JE#5\RI2))+ ) yfFRV4{>XZ4 " . ]3V3FJDEd2<I@F8&<b`bRM=>#''4!+#"&767"#"&54>32267232>724&#">qSR{{#Prgb`%LloPgT!muGI g7$"(-7XBvuFޅIOW`P{Nd3=+>/56i4V!56Z3`-!#=goJU@*2  d ZZZU N  ==L >K(PX@?  d ZbZU N  ==L >@=  d ZbZ   UU=L >YY@KKKUKUJIHD(3T4&&#"5 +.+"32?6;#"?654&+3!2&#!"'7637>7654&/"?3!272%&'767m9DgHRX+"  +'.d= z;r^ZB<93 (/9 ?// 5m)jTw{+ yfFRV4{>XZ4 " . ;))+ )b\g)3JP-)Rq' (3E@B<10.,+:dbM=M>)))3)3'""+4&#">2672#"&54>32&'767-7XBvuFPgT!{#Prgb`%Ll4e.{9]!^-!#=g6i4V!㮉P{Nd3=+>/4S~kMOipq6Af@c?><:9$-< db``M =N>777A7A0.#  66 +23.#"327654&/"?27!"547&''67->4 4elx=,31 w 1// ?k=d+sIwyD]} <>^Vc9 66 #1$@CD-hY+;DL3+Ns!) ?IT@RQOML >0)$K/PX@6   dZVM=N=M>@7   dbVM=N=M>YY@JJJTJT#&"#".,)$ +32654&'&#"'%".54%&5467&54632632#"'&#"4&#"32&''67J}|KX}T8/DAq%.^5C+^J^m#-%#  @)E@^A)-l1{8m#T`Xe^B5L)9u#X%& = cMD3-qVV)9'#;ommkM][cs6Hm@j$-<  db``  UM  =N>87FECA<;7H8H0.#  66 +23.#"327654&/"?27!"547%"&=332673->4 4elx=,31 w 1// ?-ux9'P8R8D]} <>^Vc9 66 #1$@CDGu\)!E8m{!' ?IV@>0)$@9b  UUM=N=M>Y@TRML#&"#".,)$ +32654&'&#"'%".54%&5467&54632632#"'&#"4&#"32267#"&7J}|KX}T8/DAq%.^5C+^J^m#-%#  @)E@^Ae 3+`lu`Xe^B5L)9u#X%& = cMD3-qVV)9'#;omRfaW~h6>^@[$-<b``UM  =N>>=:90.#  66 +23.#"327654&/"?27!"547>2"->4 4elx=,31 w 1// ?GP.FPD]} <>^Vc9 66 #1$@CDP::P:! ?IQ@>0)$@9b  UUM=N=M>Y@QPML#&"#".,)$ +32654&'&#"'%".54%&5467&54632632#"'&#"4&#"32462"J}|KX}T8/DAq%.^5C+^J^m#-%#  @)E@^A877I8I0.#  66 +23.#"327654&/"?27!"5472&7>76&'&7>->4 4elx=,31 w 1// ?:1b J` SDD]} <>^Vc9 66 #1$@CD=`PWe!?A%3! ?Ib@>0)$@:b   UUM=N=M>Y@KJ^[JbKb#&"#".,)$ +32654&'&#"'%".54%&5467&54632632#"'&#"4&#"32"&76767>32J}|KX}T8/DAq%.^5C+^J^m#-%#  @)E@^AA93 :JrT]   7&C`Xe^B5L)9u#X%& = cMD3-qVV)9'#;omZ;M:I O) 3!6'7m Zd@ @ddT >Y@ZYQM>:21)%  +&''676&/&67327&#"&6?>7!&#"&6?>76&/&67327!L=e+sJwy';FP=?=8 );FQ=?=:cc );FP=@=9 &= +@1   dbU=M=M>Y@;;;E;E("'&7% +%#"547654/"57672>3232672#"547674&#"&''67h T+? m|XNN'# T#M#%5e=d+sIwy>!(7l?'9 1 *Rԙl'tb#<+#C1GV3-hY+;DL3+Ns'7-W[gK(PX@"   T   S = >@"d   T   S >Y@XXX[X[ZYWVHHHG +6&/&673273#&#"&6?>7!&#"&6?>7#7376&/&67327!!';FP=?=8  );FQ=?=:cc );FP=@=9 II &@5 bUT= M= M >Y@?=5310.,#3% +%#"547#736'4/"57672!!>3232672#"547674&#"h T|| +? mAdXNN'# T#M#%5e>!(7lP7#9 1 *RPAl'tb#<+#C1GV3-'`X%9k@*4<3;):K(PX@UU = >@UUK >Y@'&761/-+&9'9OG +6&/&67327&#"&6?>7267"'&#"'>2& 6-/4 ]Y9-4U@R$. <-;#:bUU=M>! 20+)'% 4!4,"$ +32672#"547674/&7632267"'&'"'>32\N'# T#`%R-* f 2*C.>:!#2+%V:;?.nb#<+#CN%!+6g-> 5-/4 ]Z:-'y%1BK(PX@U = >@UK >Y@ (&.+&1(1OG +6&/&67327&#"&6?>7!"7>3!2&" (% +"+,"$+32672#"547674/&7632!"7>3!2\N'# T#`%R-* ? #nb#<+#CN%!+6g(*'q%7RK(PX@dU = >@dUL >Y@'&5420+*&7'7OG +6&/&67327&#"&6?>7"&=332673&%,"$+32672#"547674/&7632267#"&7\N'# T#`%R-* [-'Vajnb#<+#CN%!+6gRfbV~'3->|@ +<;0K/PX@K =M>@QK >YY@:831., >> +"&5467&#"&6?>76&/&67327"#0"#327`Tjs]*'=@=9 &,$)%+462"326723267#"&54>7&547674/&7632;N;;N\N'# 51 91+>/sJ^576J#`%R-* fR::R9cnb#<+#C+a%5#'JA-c?8?N%!+6g'b%-9K(PX@U = >@UK >YOG+6&/&67327&#"&6?>7>2"&,"$+32672#"547674/&7632\N'# T#`%R-* nb#<+#CN%!+6g'-%LKPX@ ZRK = >K(PX@!bRK = >@"bIRK >YY@ C"$/OG+6&/&67327&#"&6?>7#"&5463232674&'&67327&1o&ZCB)T E[L)ZE/)-',-/)-',-hq9'V6+&7=%?"8&%%DojFNVw?@*b  UR=M>Y@VURQ)!$)/() +7674/&763232>?674/&7632#"&54632326767#"&462"$462"`%R-* \#47?6M4+%RL*'&%9TZJH`%7D%T< ANkg;XR;N;;N9R99RV%!+8bnbFDEwTIL%!+\3/ XatP5=_hn.{R::R99R::R9q M)@ G0)(KPX@5b`X UR =M >KPX@6b`` UR =M >@9  bb`` URM >YYY@KIFDA?9731+*%#! +&''67#"&54632326'5%7>7'#"'.#"#"74632>329i)oOqy .'7l<_i}\)HnB(`-D31 3:jJ'!+DFLT>/Jqb_+7HH7+Ns3J%3 PF1$=+)l 3%B?ZLg/T9=`o'#.@ ,+)'&K$PX@bR =>@dbR>YY@ $$$.$..!$&+#"&54632326767674/&7632&''67&%9TZJH`%7D%T@A%RL*-l1{9m#S\3/ XatR@ eUV >Y@[ZZl[lRNED<8CG +6&/&6732767654/"?327#&#"'7654'&#"&6?>72&7>76&'&7>&@@Z  b  eU  U=N= M   >Y@MLL^M^IG@>864320!##4+7654/"576722?632#"&#"3272#"&'.'&#"#"542&7>76&'&7>+@ m 0IRfdD%57j/% E!   'Cfi=Y4?J T~:1b J` SD?'9 1 *R?FH+;_ +7? b`r*$! B*7q`PWd">A%3FDH@E +<ZbVN=M >A?%"'!##( +654/&76322?632#"&#"32672#"&'.#"#"747E%R1*&,9hdD%59wi  @N-1<  6 TV%!+H+;o, -%5d =!(7l)#+4S@ <21:K(PX@d =L >@ddL >Y@ -,,4-4U"547%&@&dbU=M>Y@%$#"   +"547%32672'"547654/"'7672+!JX+}V+? l]ZmB5#Z-?'9 1 *R)-#-+>R@ <29K(PX@e =L >@deL >Y@ -,,>->U2&7>76&'&7>&@&beU=M>Y@/.-,*'  +2&7>76&'&7>32672'"547654/"'7672,:2c J_S DJX+}V+? lf`QWd!?A%3ZmB5#Z-?'9 1 *R)+=_@ 32@bM=L >Y@ -,97,=-=U2'>76#"&7>&X 87/)-',-) #( с','[;B # W)+-!+0@&% @'bUM=M>Y@ ,*0 07"$ +32672'"547654/"'767272'>76#"&7>JX+}V+? l72 d>W 87ZmB5#Z-?'9 1 *Rg[;B # W)+-!+)#-+3L@dVK >YU"&462&@(bUV=M>Y@ 7"%$"+4632#" 32672'"547654/"'7672+;'%>:)+X+}V+? lN><)'9)ZmB5#Z-?'9 1 *R)#-3B@ 3 @dL >Y+'U7 +;26&#!"&6?>7'76&/&67327%c(${#T#5`=@=9 T_&  #( с',-[?p/)-',-r&l@#& @ bU=M>Y;"&+32672'"547'7654/"'7672iX+}VWb+? lV=k+mB5#Z-P;e?'9 1 *RNAIn@< @dU =N>Y@CBBICIK"K"+#"'&"&#"'76?67.'&?!327654/&?327%"47%'/)X3)4j>-? W1*7j+!Ju?1Dd1R'%q-[ 'sq>S''-\)b9G@?>1 @)ddbM=M>Y@ $%','("# +%32672#"5476'4&#" #"547654/&7632>32632'"&767wN'# T#N"%^N Xt%RP*+VB ?(/ b#<+#C1Gq->!(7lV%!+Hzj't/' $7N-ATm@< @eU =N>Y@CBBTCTK"K"+#"'&"&#"'76?67.'&?!327654/&?3272&7>76&'&7>'/)X3)4j>-? W1*7j:1c J`S Eu?1Dd1R'%q-[ 'sq>S''`PWe!?A%31)9LE@B1 <@9beM=M>;::L;L','("# +%32672#"5476'4&#" #"547654/&7632>322&7>76&'&7>wN'# T#N"%^N Xt%RP*+VK:1c J`S Eb#<+#C1Gq->!(7lV%!+Hzj'th`PWe!?A%3NALq@< @dU =N>Y@BBBLBLK"K"+#"'&"&#"'76?67.'&?!327654/&?327%&'767'/)X3)4j>-? W1*7j5m)jUw{u?1Dd1R'%q-[ 'sq>S''\g)3JP-)Rq)+9DI@F1 :::D:D','("# +%32672#"5476'4&#" #"547654/&7632>32&'767wN'# T#N"%^N Xt%RP*+V4e.{9]!^b#<+#C1Gq->!(7lV%!+Hzj't~kMOiqD9KM@JA@1 <bM =M=M>;:GE:K;K','("# +%32672#"547654&#" #"547674/&7632>322'>76#"&7>O&# T#M#%^N Xs%RP*,V72 d>X 87b#<+#C1Gq->!(7lV%!+Hzj'tR[;B # W)+-!+P+L@F1*#K(PX@;b`bR K  =K  = >@4b`b I UR >YY@IHEB>=86433"$$ +%#"&7>327267&"2&#'76?67.'.5?!3276/"&?37#'ih'L <B5b1!)k3N 4j>-  ? h1 H7jǾJP>1+8%RDd1q'%q-[%uq>q ''q#Hc%@#bM==N>Y@ *$)),'&+>754" #"547654/&7632>32#"&54632326pT!O[\N Xt%R1* VGT/-Jt'=a]%'6 6.8>!(7lV%!+7djbÖ5L7',#  #y+8@5UM =M>" (% +"+  +"32654&!"547!2!"7>3!2}9Ҝ5p;a; $&ZQ> n_ JQ/J0))+/@,UM=M>" (% +"+%'&"+327654'&#"4>3 '".!"7>3!2J^5RX.Nd5?w{!No\{5 %'\}}?Zx`êjF'K{ex(*q1B@?d UM =M>! /.,*%$ 1!1  +"32654&!"547!2"&=332673}9Ҝ5p;a; uy9'P7R7ZQ> n_ JQ/Ju\)!E8m{),/@,,&% :UM=M>%%'&"+327654'&#"4>3 '".267#"&7J^5RX.Nd5?w{!No\{5Jf!2+alu\}}?Zx`êjF'K{eRfbV~.<rK1PX@# ddM =M>@' dddM =M>Y@! :831)' .!.  +"32654&!"547!22#"74?6#"74?632}9Ҝ5p;a; e) V DZQ> n_ JQ/J##  @)9/>KPX@#Z =N=M>K1PX@$b =N=M>@*b` =N=M>YY@ &&(%'&"+327654'&#"4>3 '".#"&76762"&76762J^5RX.Nd5?w{!No\{5S #& ( %\}}?Zx`êjF'K{e /  +*+EYKPX@ C <KPX@ C <K(PX@ C <@ C KPX@9Z  b XUM == N  >K(PX@3  b XUM == N  >@7Z  b XUU= N  >YYY@A>;953+)$" ED  +%32676&+"$!;!6&#!"326?6732#"57.+3!2!94%9F<977uX7JHFQ10 +' -#/f#5g3s\B$--Fq!LAX)1= %+@/+' 9CE@B?3.<bM=M=M>&#""((! +32>54.#"4>326322672#"&'#".4&#">LRw:A3Ty:=uz4ba%LloPfT!XuR{@-7XBvuF^u=;L\-^^`jd3=+>/56i4V!\QMwo-!#=g! HPK@2  d U M =  M = K =M>KPX@>  dU M =  M = K =M>K(PX@>  d U M =  M = K =M>@8  d IU M = K =M>YYY@(JI IPJPGDA?76541.+*#" H H   +"326767&'23#"'.#2&"'7637>7654&/"?326'"67% bNdb# q3? `D;7IXqX+244=7 ,L @+x+!K-11(E'?Mf#*RPN1d)%? '')+ !  +[-\b+9g@#ddbM=>Y@ $%.'"$#+>32#"'&#"#"54>7654/&7632632'"&767DL566'!$9< N r%R%,#> ?(/ H;%+H+@(7#090%!3/' $!-; H[J@2  e U M =  M = K =M>KPX@>  eU M =  M = K =M>K(PX@>  e U M =  M = K =M>@8  e IU M = K =M>YYY@(JI I[J[GDA?76541.+*#" H H   +"326767&'23#"'.#2&"'7637>7654&/"?3262&7>76&'&7> bNdb# q3? `D;7IXqX+244=7 ,L @+I:1c J` S E-11(E'?Mf#*RPN1d)%? '')+ !  +m`PWe!?A%3E1o+>9@6<29beM=>-,,>->.'"$#+>32#"'&#"#"54>7654/&76322&7>76&'&7>DL566'!$9< N r%R%,#:2c J`S DH;%+H+@(7#090%!3`PWe!?A%3! HSN@2  d U M =  M = K =M>KPX@>  dU M =  M = K =M>K(PX@>  d U M =  M = K =M>@8  d IU M = K =M>YYY@(II ISISGDA?76541.+*#" H H   +"326767&'23#"'.#2&"'7637>7654&/"?326'&'767 bNdb# q3? `D;7IXqX+244=7 ,L @+5m)jTw{~-11(E'?Mf#*RPN1d)%? '')+ !  +X\g)3JP-)Rq++6=@:<431/.:dbM=>,,,6,6.'"$#+>32#"'&#"#"54>7654/&7632&'767DL566'!$9< N r%R%,#h4e/{9]"^H;%+H+@(7#090%!3v~kMOiqh5=I@F <;::dbM = =M>766=7=/$/$+'.#"#"?!27654&'.5476$32%"47%?-ZD`;XV-X6 >-ZP@[`3 j}+!J!9;NP's^d7QlB#*u*"Un( :UnA!#>-\yb 9K!PX@0bbM=M =M>@.bbUM=M>Y@/-*(%#98$! +632'"&767.5463232654'&54632'"&'&#"# ?(/ ef%')1)+Lb}\J%5T=DRm^/' $^;/B+!HPdTydj:FA);V;J7ZMuh5@L@I>=;98 <dbM = =M>666@6@/$/$+'.#"#"?!27654&'.5476$32&''67?-ZD`;XV-X6 >-ZP@[`3 j=e+sJwy!9;NP's^d7QlB#*u*"Un( :UnA!#>yhY+;DL3+Nsy,+ 6@ @+db`M=M>Y@ ,*'%"  6 5 +&''67.5463232654'&54632'"&'&#"#-l1{8m#Tf%')1)+Lb}\J%5T=DRm+mkM][c^;/B+!HPdTydj:FA);V;J7ZMuhsDN@ 980&%<1;KPX@7bbXQ M = =M>@8bb`Q M = =M>Y@MK<:6542/-)'$"$ +'.#"632#"'732654&#"'7#"?!27654&'.5476$32?-ZD`;XV-K5DjHJ;89X))5 s X6 >-ZP@[`3 j!9;NP's^d7QlB#*v1/Ld#$7/ u*"Un( :UnA!#>yyB@0; : <;KPX@:b`b  X  RM=M>@;b`b   `  RM=M>Y@><9731/.%#  BB +"'7.5463232654'&54632'"&'&#"632#"'72654&V+3 od%')1)+Lb}\J%5T=DRmC5CkHI;79Y) ]:/B+!HPdTydj:FA);V;J7ZMmj1/Ld#%7/h5@L@I <>=;98:dbM = =M>666@6@/$/$+'.#"#"?!27654&'.5476$32%&'767?-ZD`;XV-X6 >-ZP@[`3 j5m)jUw{!9;NP's^d7QlB#*u*"Un( :UnA!#>\g)3JP-)Rqyl) 6N@K:db`M=M> ,*'%"  6 5 +&'767.5463232654'&54632'"&'&#"#4e.{9]!^f%')1)+Lb}\J%5T=DRm~kMOiq^;/B+!HPdTydj:FA);V;J7ZMu?DJ@ F  0&%<1;:K&PX@8  b`b`Q M  = >@6  b`b`   UQ >Y@EDB?52/-)'$" JJ +!2>7#.+"&'632#"'732654&#"'7#"&6?>76&+""'67!3=$ELo5/ ';F|?=5DkHI;89Y))5 j=@=7 Xb  `  b `Q M =  >@=db  `  b `Q M =  >Y@@?;943$#$&#% +7>323#3272632#"'72654&#"'7.547#"54767} !Ry3q%(AJ5DkHI;89X)+3 p,+| 1ofP%#7w\ <d:'Hjt1/Ld#%7/ +1)- !?1<@-<:9754:K&PX@'db`M = >@%db`U >Y@222<2<,+)& 11 +!2>7#.+"&#"&6?>76&+""'67%&'767!3=$ELo5/ ';FP=@=7 @3  bb M =M =M>Y@*)64):*:('%##% +7>323#3272#"&547#"547672'>76#"&7>} !Ry3q%3H31| 172 d>W 87ofP%#7w\ <d:'\v(6)- !.[;B # W)+-!+?DA@=  <:K&PX@+  b`UM  = >@)  b`  UU >Y@<;9631.,$  AA +!2>7#.+"!2#!&#"&6?>7!"7>;6&+""'67!3=$ELo5/ Ne';FP=@=7 dO@1db` U M =>Y@87310.%###!% +7>323#32+3272#"&547#"7>;7#"54767} !R1!63q%3H31:q0 1ofP%#7w\ <#d:'\v(6)$ !ZF[@K U <@+ U  U UM>Y@&HGYWRPNLG[H[A@?>;743'%FF +".547654&/"?327#2>7654&/"?327#267#"'&#"'>32s3_gL0+23@D3=8 y8R`\H u+06!3=:o9$7!/J3EA$'7/)`@AG32MUAN '',+=pIf@\xRX ''.-,> 5-.4 ]Y9-L=Qq@nB L $ ?>ONIGEC>Q?Q''"('"$+32672#"54?'#"&547654#""'632326?>32267"'&'"'>2 rN'# L#}o;NZX%# J+X=H}< %V u$7!/J3EA%'7/)_G3-b#<+#CB{OQR=MXb#;+#DH;LR##7 .7-> 5-/4 ]Z:-wFR<@#   U UM >Y@"IGOLGRIRA@?>;743'%FF +".547654&/"?327#2>7654&/"?327#!"7>3!2s3_gL0+23@D3=8 y8R`\H u+06!3=:o98 %&2MUAN '',+=pIf@\xRX ''.-1))L=IT@Q$ <b`   UM=M>@>FC>I@I''"('"$ +32672#"54?'#"&547654#""'632326?>32!"7>3!2 rN'# L#}o;NZX%# J+X=H}< %V , %'-b#<+#CB{OQR=MXb#;+#DH;LR##7 .7(*oFX<@)  d   U UM>Y@&HGVUSQLKGXHXA@?>;743'%FF +".547654&/"?327#2>7654&/"?327#"&=332673s3_gL0+23@D3=8 y8R`\H u+06!3=:o9Guy:'P7R72MUAN '',+=pIf@\xRX ''.-u\)!E8m{L+=JV@S$  :b`  UM=M>HFA@''"('"$ +32672#"54?'#"&547654#""'632326?>32267#"&7 rN'# L#}o;NZX%# J+X=H}< %V ,f!2+alu-b#<+#CB{OQR=MXb#;+#DH;LR##7 .7NRfbV~sFQ[< @-  U U M =M>Y@&HG[YUTMLGQHQA@?>;743'%FF +".547654&/"?327#2>7654&/"?327#"2676&>2#"s3_gL0+23@D3=8 y8R`\H u+06!3=:o9h.F *[G ,V}IG2MUAN '',+=pIf@\xRX ''.-OB0.EE.1AdcGEeLT=HRb@_$ <b`  U  M =M=M>?>RPLKDC>H?H''"('"$+32672#"54?'#"&547654#""'632326?>32"2676&>2#" rN'# L#}o;NZX%# J+X=H}< %V .F +[G ,V~IG-b#<+#CB{OQR=MXb#;+#DH;LR##7 .7@C0.EE.1BdcGEeFUc<K1PX@'  d   d UM>@+ d d   d UM>YY@&HGa_ZXPNGUHUA@?>;743'%FF +".547654&/"?327#2>7654&/"?327#2#"54?6#"54?632s3_gL0+23@D3=8 y8R`\H u+06!3=:o9( V D2MUAN '',+=pIf@\xRX ''.-##  @)9=L[@$ @: bb`  M =M=M>Y@ZXRQKICB''"('"$ +32672#"54?'#"&547654#""'632326?>32"&76762"&76762 rN'# L#}o;NZX%# J+X=H}< %V t#% ( $-b#<+#CB{OQR=MXb#;+#DH;LR##7 .7 /  +*-Z@<GFYR @) b U QM >Y@ USQPMKA@?>;743'%ZZ +".547654&/"?327#2>7654&/"?327#32>72#"&767s3_gL0+23@D3=8 y8R`\H u+06!3=:oI ~o f8##qSRQ2MUAN '',+=pIf@\xRX ''.-XoB`IOhuZLLT@Q3  <b`RM =M>HF'"(,#'$ +32672327#"&467&54?'#"&547654#""'632326?>32 rN'# OJ91?YoL\RQ/#}o;NZX%# J+X=H}< %V -b#<+#f/d%5JJuR 7B{OQR=MXb#;+#DH;LR##7 .7Rq@Ky@IHFDC@$ dbUN>Y@AAAKAK&(CF +.'.73277376&'.7327#"'.'&##"&'&''67ER  Jo BTFNYBLLV  CBB V  4o  =e+sIwy~^N7)  )+0+E}78)  )w-&O a#XhY+;DL3+Ns)9D@BA?=< @#db`=>Y@:::D:D1/'% 99 +"'&"&'>32676767>54'&5432#"'&'&''67!V5@5# :153F+:-7!+/-9J+ _k7S\`:=!2 h1-l0{9m#T}03 YsLB9,4;MG:ALaYÂILx$,BmkM][cMqAL]@ JIGED@dbK >Y@BBBLBL73+*"G +%&#"&6?67654'./.732736&/&67327&''67 );FP=?qH!)3sb;D1  C%B-w615' =e+sIwy/)-' Uv"T3+-#)-/-.7;LhY+;DL3+Nsd!e+8C@A@><;%$@'dbb=M>Y@999C9C20%("##+!#"&463232676'&'&"&'>32>?674'&5432&''67]bR'V<#' -t<J- +)]<"%/*# O *+ _k7>-l1{9m#S)Z;%b)5A,+,^DS"}Bp:ALaY<"9mkM][cM\AMYgK(PX@ b U = >@ b UK >Y@ONCBUSNYOYIGBMCM73+*"G +%&#"&6?67654'./.732736&/&673272#"&546%2#"&546 );FP=?qH!)3sb;D1  C%B-w615' !-H'#*Es!-F)!-F/)-' Uv"T3+-#)-/-.7;5+!/?+#-?-!-?+!/?Tc1r@0/<:K&PX@!d =M =N >@dU =N >Y@-*#!  +"47%&#!"547676#!"'673!>327!267+!JN23!;dm?:5:I4v!T )FP1f<;P-\fTs $O  ob4B@:9   ,@E d  d  bb``UM=M>Y@><8642#!*"## +232>72#"'&#"&'&'67#"&#""'>327632'"&767;jD@Z&7"#1J%!FpFZVVFD u&3HC!'x496H ?(/ Tc#=;r3594 '-E^/' $Tcb)1g@ ('@!UU =N >Y@ 7"!7@+&#!"547676#!"'673!>327!267>2"923!;dm?:5:I4v!T )FP1f<;PGP.GOTs $O  OP::P:o4<m@j  ,<  bb``  UUM=M><;8742#!*"## +232>72#"'&#"&'&'67#"&#""'>327>2";jD@Z&7"#1J%!FpFZVVFD u&3HC!'x496FP/GPTc#=;r3594 '-E^P;;P:Tc)4o@('<21/-,:K&PX@!d =M =N >@dU =N >Y@***4*47"!7@+&#!"547676#!"'673!>327!267&'767923!;dm?:5:I4v!T )FP1f<;P5m)jUw{Ts $O   \g)3JP-)Rqo'4?u@r  ,<=<:87 :   d  bb``UM=M>555?5?42#!*"## +232>72#"'&#"&'&'67#"&#""'>327'&'767;jD@Z&7"#1J%!FpFZVVFD u&3HC!'x4964e.{9]!^Tc#=;r3594 '-E^~kMOipy#2yKPX@+b`XM=N>@,b``M=N>Y@.,%# 22 +27267>?6&/&7676767632#"&6&#" #"&546D9$%!*! A*%Jg/DdRwT))Jw.0V-J5`*B,I+ qY7\3@3@4  b ` T =M=M>Y@ 5 543&"&! +%3267654&#"#62#"''67#737654#&?27673J'^)ZJ}hJjVOq+HL!+ 5jO,=!33-2=H@ *)@(b U UM >Y@33FDA?3=3<86.+(" 22 +"'"&7>3232632!"&#"'7437>7632676&# 3 76&#"-K FDž +,'!hJ)T4d?@$ZUUM >Y@420-(&#  % +3267654&#''74;27654'#"?!5#!"3 qJ X! !y%*bפ'\ 7)  ?d+ +@ <9K1PX@(bM=M=M>@&bUM=M>Y@ (% + +$! +%2676&#"!.#!632#"''676&/"?63!\'!UZu^~"4?Dvy'/͠{;3% !;o=ᷚVXqG- =bX) /!#rD57@4.<V =M >3210-( # +32>76.#6&'&767232#"&#'7637>7g 1Tm^1 ){^0F)DsR/yLX/ 6;7 9.$%$!+%32676&#"2632#"'"'676&'&76F-K'!T\u`Vu'1˞uC @+C'T涚VRjoI @ ZHZP##F 9@6 <bM =M>    + !"&#732>76'4&#"'F-!?3}o(FXRj]R)^wZkkw1B@?/!  <UM =M>,*$"11 +2#".#"3267! 547!2>;g#'=+ B3}o)F`s%8 HE7 )^wbakyVV)!1y7@5-! @.bbUM=M>Y@1/,*$" 77 +"&#"#"54654654'"327#"&547632>32qDD3: Llf'ra}1)wF9=J/mJ%3 +/>#hɤL9}Ӫ;L /PT7 !&#+8K(PX@!SM = M >K-PX@US M >@+Z ZUS M >YY@ 83B52%$ +%3 654#"3#6;27#73674&+"?632#"&#T!vX\ t%>jdt)2; 9)v{ff1P_P)HQ;-:Z:@bUM >Y@ f'$4&"+%3 >.#"637676&#"#"7>323263 #"&#"'+ E d :b{TV  ;h 715Y -H[(#D=9#ruB3!`O1W5&B5<!$TXi;+D 4K-PX@"<:@"<:YK(PX@&ZUN =M >K-PX@$ZUUM >@+ZbUUM >YY@ /-)#!  4 3#! +%# 326#"'3!7#2&#"#"&76$!3>.j?!bI2Pw=2Y^{ >1# +9 3?A#-9zJ$>'Ϭ)pX:351+ 1\ 5m@5#<:K1PX@ M=M=M>@UM=M>Y@ %#%3%#+%.#"326&+"'!67#7'#"&767632oGBsTH&'RTI Dw)1\Ru/ZXH+#^t)1b=;fTX4 RN% 0#3{b#)</@,+<bQM>.,"&(+%#"&546562327654'.5476$32'$654&#"XLuM9d 5JL3B`g-\h]lVp:ons;JXj5{)5+H` hD9w!Pw*ij+A@A@ 1+'@6  ZbZ UV=K >Y@ >;&6#"52520 +!%+"32%!703!27!"+'67674;203!654#!"'  =jX= 7yLyV$/!  - !1e>H7++\;-H C Z@Nw9>+)+LD"9@6<SM =M>! " +32$7'>32!"&547!67654&#"hD#H] u1{iX^~%Q/mjmfD9G@D1#"<UM =M>(& 99 +2.#";2+"32>7#".547>7.5476$LP'A[u% !ɖmN+9V`PN#DE/D: {!\)'w/-}w7jD;yT"(!-mC&1F@ #K(PX@6Z  b  `U  RN =>@4Z  b  `UU  R>YY@CA=;%$&$"6C3 +76&/&?3!272.+"!26?6;#"?6&#!#"&7>3232> $;<D6,> EjJ[46- .&4_&wp*L =( ;JG0) )r zeFR*,4jOCo4,"=QP<2+% S8µKPX@.bZRM =M >@/bbRM =M >YY@5321+)"  88 +2#"'.#"3# #"&5465>3272>?#7327>b[.9 XbR D=// 1VN9TLJ`)ND1!+4?#P#TN=%++`»VPlGT@QE +63< UUM =M>B@970-*'$! GG +2#"&#".#"327654+"?37+"! 5476$32>= d `? 0^ؗ7b6P 3/+ 'Z`o!< H />g9B66-- 41BN!1yN+ h@N'[;951. K&PX@QK >@UIMAYY@b`URMEA>5:'+3276767'&'&#'"'&5?32326;#"767656'&#'#&'?32376;#"#"754676'H!  %+  P+ \<;  KL   %) -?WLb%7A 2- ##  )X'  ##)#`NyCo@ <?@$U=M=M>Y@ &7(&%+$ +3267654'.54632 #"5454&#"#"547654/"'7672>32imh!L-6*7\"%5dN T+? m}YDP)vI6y$ "/Rx6.->!(7l?'9 1 *Rԙl}c@  @ bU=M>Y7"$+32672'"547654/"'7672JX+}V+? lZmB5#Z-?'9 1 *R'-5?K(PX@V = >@dV >Y#(H#(G+6&/&6732732+&#"&6?>7#"7>;&KPX@-  b M =M = K  >K(PX@0  b M = M = K  >@+  b U M = K  >YYY@VSOL?>;:*3#53#3 +%;2�'76;27054+"?30%+"6767>32"."32&#'7654'&'b5R8o`8P 5qR;N#{Ad2Tp5:#FR"!T56DN   O{1E B''ZB''Z/B{#A:R1+.9-B#!O34$!\' !/!D 'q^@$LW<@3bbM=K= K  >Y@\YVSPM*4833:"&" +>32#"'4#"6767654#"?37#";2&#'7654/.#"32&#'76;267)Ș?i1=Fwu35bH )/ J' hQ d! =H hP&10 5'#+Z!3-#''9))4%#7  7''+/.}@ (@*bUV= M  >Y@ +)$#$4# +#"7>;654/"'767232+32672'"547e" X+? la!^Z+}X#?'9 1 *RN#\w D3#Z#o(9@6('  <;9M=M>$.$%+327#"&'''&''7&#"'>32$,1NN'5~7JE $#`LR3329->ytb--{bq>w449D1sP+j@e4 C D @.   b  `  U M   >Y@jgda^[TRNLKJIGB@96#5%3#7$+32747654'#"?307+"327654'#"?307+"327#7#"&="547654'#"?37+"D{ P+ W+^ywR+ U+5/ -1K+ %׬Vf R+U)^l#!3g ?''Zp#?''--G9 7"cG'Ze?''ss+K@%A KPX@.b  Z  RK =M>K(PX@/b   b  RK =M>@-b   bU  RM>YYY@IGDB($346'3 +7.+"&?!32?674'#"&?37+"#&'&#"&=632326B/  `   ]7 I 57F +1'%)-A[7?RBN/] !0 0` ''J3;+Dd3XfX\);#H1!#9'@$$<M==>*),'%+6'4&#" #"547654/&7632>32#"74>q"%^N Xt%R1* V Xh ->!(7lV%!+7dj 5'8 #3D!0@-SM =M>   &$ +!32654&#"!"547!2wٲ#;׬<<5Y 54y9B@f=1zDE/=)&6;@8<dVM =M>('0.'6(6+"&"+!"547!2326754&'&547632"32654&5Y 5{//X!R ^1@#5ٲ#/zDE/F+-H PT?=PRB fN}6]KPX@$dM=M=M>@"dVM=M>Y@ $*"&%$+32>5#"4632327654'&747632##".L)C:XCm=yZ>;5j/N\-3sTBJtD+^\;w;vL@PF7R^X8PfTND-7@ 7 K(PX@+ VO = M = M>@-Z VO M = M>YY@53/.D332$! +% !2>737673;2&#'7673276&#/  327}55y< /B/-W2Q1V!T3\53{p{m'/H'-Z''ZX7 P'1>D@A2  < UM= M=K>=;"!"&$8332 +;2&#'76;26746."#"#".7632326;&'"327 1' P )10  +3=TRj71'hO9V 7{w'NX1hF/+''+/ 3JVH+#NٓϤu]$-;;=HC@@?*8<bUM =K >$#335*f(% +%654&##"'47>737632#"'32&#'76;23 654#"D| 0= JmohuP" H}ӅZ?N`7P5qvh%l98 Z >;&= 5qd 5Z\3!HO;?'' 99$'6C@8  @:b bM= M= M=K>Y@B@;9"&$335'! +3632#"'32&#'76;27>32#"54#" 3 654&#"++)s 7dHPJ'P'Z5)ɗ?i1?Fvu3d]TG95;>w-7''Z<5'#+Z!X}ZFjw\%-+?M@3 @-  U U  UQK >Y@JHFD=:85#5334"% +;#"&'&'32&#'76;27054+"?30%+"3 654&#"32>a5a #7Vw 7b5R8o`8P 5qRIbsDXgRzuLu)bN?''XB''Z.#qh3e'D;;@8;:<bM = =M>86'%#! " +>32326?'6#"#"&547>7$7654&#"'ngpPyks$ 92@A}XTqT5gV:= 4*T`3-w`ky3#u-#%Í!!XZ3fPj {+5A@>+<b`M= =M>55/#.""+&#"#".547>7>7654'"'6763232767P!Vf3mb\RMs%1#%qj}s\5fFT#!bH!q6G=dbL{V3fT!je73cD9H;H}+t@@"ZZUK >Y@ #$+.#!!267'?27 &#'?36;Nba76[ 13F +heo1'!V3N9e3@"bUQM>Y@ *%!*$&$+654&#"326%>3232632#"&'&54767#"&5455=a 73=a^TV/ -J#5 uP!Z-4 71T^/AZ9 -@Y9ZmTcPrN'5q1 %?'5cJyC!PF5H@E/.1 <:eM=M>-+'%" 55 +"&5465>76?"547#"?6726?32'#27 D`?XPq `e }G/y/FX% # ) !X/s!7o?p{') )L0?9"#%,{Z7e@&<:KPX@bM =K >@bUK >Y@ 533675"+#"'47>7!2657.+";2&#'76;27654+H0=: 1>X71c7P6qV'>X" wD#3 B''Z 7c3y@*bM= M=M>Y@32#"&&$#$ +27"547#"?672>7>32#"'4#"32'y/FXq /D )Ș?i1=Bp!5/!L0?9"s!7o?85'#+Z!} )|?D6@ <:K&PX@)b``QM >@/b``UIMAY@ $"&255+%654&+""'673!2>7#.+"327632#"&56&0Q^,'Fl!3=$ELo5/ "z3!D&nu Jecg} mQ#3c@ U UM >Y@PLFD?=730-$" VV +".547654+"?3%+"2>7654+"?373676&'#"547>32+"5doP5 {`7P7oo=Xd^E!j`7PR LB-Z eK'qdR3PXF; B''Z{>{Mf@\yRe B' @% C #1{JZZӰ[}B;@8('<db==N>)$)$$)"+46323267#"547#"&5476'4&#"'>3232727654.!99-2 1/'uV-R?\^+793!Z#17^'sJ'/'%?D7f%7F!]LBNLQi\pLz#)-FA/)9T '+9KPX@ ZM =M>K(PX@!bM =M>@bUM>YY@/-$"99 +"#732327654546;#654&#"!"547>54RH59Ţ % l|5:MhXX' m+PfPf5H#B1`-DC 9#7#d'%3D^Ւ%}D:u@ )@'bUM =M>Y@ ($"%&3#4 +654+"?30%+"3265#"#"=>32#"&54`7P7oupd7=!@5J#HB''ZfuR}g;)/X/H@q~٣g!> FDKPX@'D=<@'D=K(PX@)bM =K =K >@'bUM =K >YY@ 337339%%$ +.#"#"547>3267654'#"?37+";2&#'76;27w+-@39 >5bXZjX/= +-<%kPb5P5s N{jg3+# J79s%))'3; B''Zd!R>H@E3<1<bbbU=M>%("#*"#&+>32#"'&#"#"&463232676'&'&"&'>32 43,B%5#= ) 'd<_dR'V<#' -t<J- +)]<"%/*# z;X*!j"6 R2BmZ)Z;%b)5A,+,^DSqTcD9q@ 87 @#U V = M >Y@ 52#$"!4#$@ +&#!"547#"7>;676#!"'673!>32!2#!7!267923!$;dm?:5:I4v!T )F@ %`1f<;P(s $O *+  oD@| 2 < '< : b b `b`bU M =M>DB@>;:8653#)"###%+32+232>72#"'&#"&'&'6?#"7>;7#"&#""'>327;j>v%3@Z&7"#1J%!FpFZVVFD u̗$&3HC!'x496M):=;r3594 '(-E^+PLV.r+@(b`UIMAY@(&$" .. +!2#"$547>32727654.#"!'?51= %d8/\#N* hF7d5st+D#͑-8jT )=H'/9s;^)jP'V1{@+ <:K(PX@"b`QM >@(b`UIMAY@$" 10 +26754&'!&#"327632#"54767.763u5PZ7;kw9_L [!:+)s^+/`s%HN?c- '& 'lD +.N@K) <:b`M=M>"  .- +2?54&#!&#"327632#"&547>7&763V+/7>5ݻ9:{ NsI/F1 ̓ % 1q" oN3-u}_9- 5^1%-:4@180*<eUM>63-+)' +#"&547>7>7654#"!"'73!2{jw zi T>"uuN->- D;')Z!) # 'j5+d66&''% +#!267!>?#7!>7674&#"#"&547>32J=`Z5H$2VkuTVg;^c L%ѕe`DP9{+?TbgPNV\XL/J#di}! `Ow#>@; <bSUM>"%&!+654&"'!!72#"&=>32326Bs$lPi%Z/1Huq!sqw""P8 $9T}q'<@9 <bUK=M>#&&1$+654&#"7'!!632#"&547>32326^Pf`p+oB&y !۶Z11 A/b"T{qw i)!P6$=!/w}f)>@;< :bSUM>"&&!$ +654&#"7#7373#72#"&547>32326^RRZFP@1o +ߟV1-Iuq!(oP P}1%N5 $?N+w!*0@-<% 9eM>#""*#*!! +63 74?>7654#"767>;"674 1T3%/3 !+-PE< $j?NӶ11J3 :3!% 9  X #GP=+7@ = > +7#3P1R57@ = >+%#3#3P1RP1R557/@,T S = > +%##737#7333#3#{RyyPw PP PPbPAQ#@ddM>Y@ +*A?(! +762'"'&'476"=07>7676746767676762+#+%%'%+    *-Z 5#%! ))HV'>9/6F&))"L1%-/+Rq{ ! )4Zl@=('Z<21/-,:K&PX@; d = M  =M  =N  = M  >K-PX@4 d I U =N  = M  >@; d  b  I U =N  = M  >YY@**jha_YSKEBA96*4*47"!7@+&#!"547676#!"'673!>327!267&'76763767674&/"?32632#"&#"'%3 654.#" 23!;dm?:5:I4v!T )FP1f<;P5m)jUw{DK-PX@X  b  bb``  U UM= M =M>@_  b  b  bb``  U UM= M =M>YY@55usljd^VPMLDA5?5?42#!*"##+232>72#"'&#"&'&'67#"&#""'>327'&'76763767674&/"?32632#"&#"'%3 654.#";lD@Z&7"#1J%!FpFZVVFD u&1HC!'x4964e.{9]!^@  b   b  bb`` U U= M=M=M =M = M >Y@)55xvtsrpihgfda]\ZXSQLJFD5?5?42#!*"##+232>72#"'&#"&'&'67#"&#""'>327'&'767654&#"326'#".54323674/"576723272'"547j<kD@Y&8!#1J%!FpFZVVED u'1HD 'y3954e/{8]!]$^J7ZZP9N^NTbJ7XL+@ m#PZTTc#=;r3594 '-E^~kMOipN;wo}jwE?2oF 9 1 *RLbm!\Q)-&RE=K(PX@&bRK =L >@$bURL >YY@ UB]=1o&ZCB*T E[M)ZE&K(PX@,bUR ==L >@,dbUR=L >YY@ U &%9TZJH`%7D%T@B%RL*;N<KPX@>bbUR = M ==M>@<bb  UUR ==M>YY@HGFEC@"%.!$& +#"&54632326767654/&7632462"%32672'"547654/"'7672%%:TZIH`%7D%S@B%QK*:R99RVX+}V+? l\3/ XatRK(PX@8bRK  =K  = = M>@0bI  UR = M>YY@b^SQONK$C"$' +#"&5463232654&'&67327#"'&"&#"'76?67.'&?!327654/&?327JB\>1o&ZCB)T D\ L)ZE'/)X3)4j>-? W1*7j/hq9'V6+&7=%?"8&%%D5?1Dd1R'%q-[ 'sq>S''oH-#+m@h LE K-PX@7bUR K  == = M>@5bU   UR= = M>YY@gcXVTSK#.!$& +#"&54632326767654/&7632462"%#"'&"&#"'76?67.'&?!327654/&?327&%9TZJH`%7D%T@B%RL*;N<-? W1*7j\3/ XatRS''o#+e@ ] 5@7  bbUR M =M >Y@b`YWKIB@"$.!$& +#"&54632326767654/&7632462"32672#"5476'4&#" #"547654/&7632>32<%%:TZIH`$7D%T@B%RL*:Q::Q^N'# T#N"%^N Xt%RP*+V\3/ XatR!(7lV%!+Hzj'tT<G^@[0===G=G;942/+('   +!#2&#"'763>76322&#"'7637654'!"&'767uVTJ7q#H^5+%8;Yq9d3g95m)jUv{D"^1''>X)J!;6''= [#\g)3JP-)Rq+)4@  <21/-,:K1PX@* db=M=N>@4 db=M=N=M>Y@***4*4#&#&"$ +33272'"74?''"432>?&#"326&'767H#PZTǔZ5%=BZ1:4e/{8]!^wbm!\-CV>9:;'~kMOiq%B 0n@+<:K(PX@dK =K >@dUK >Y@0-*'$!  +&'767;2&#'76;276&+"?3%+"?c)qK{}b ,>5R8o-;8P 5qhX)7FN/)=/)''X/+''%*;@8 <('%#":db=M> * *,"$+32672#"547674/&7632&'767\N'# T#`%R-* L4e/{8]!^nb#<+#CN%!+6g[~kMOip*;@8('%#":dM =M>  * *  +"32654&!"547!2&'767}9Ҝ5p;a; 5l)jTw{~ZQ> n_ JQ/J\g)3JP-)Rq)*2@/('%#":dM=M> * *%'&"+327654'&#"4>3 '".&'767J^5RX.Nd5?w{!No\{54e.{9]!^\}}?Zx`êjF'K{e~kMOiqFQ@<@   d UM >Y@ GGGQGQA@?>;743'%FF +".547654&/"?327#2>7654&/"?327#&'767s3_gL0+23@D3=8 y8R`\H u+06!3=:o95l)jTw{~2MUAN '',+=pIf@\xRX ''.-\g)3JP-)RqL)=HW@T$ >>>H>H''"('"$ +32672#"54?'#"&547654#""'632326?>32&'767 rN'# L#}o;NZX%# J+X=H}< %V 4e/{8]!^-b#<+#CB{OQR=MXb#;+#DH;LR##7 .7'~kMOiq FNV<@) U    UUN >Y@" VURQNMJI?>;6,* F F   +!"7>3!2".76&'"?32722>76'&763327462"6462" %'9muD" E9VBbXs 'Nud3hN b31jfR?9N99N9N99Ns))yBp}74%jZc3fYf!%}ӰZN99N99N99N9LMYf@c4 ! <  b`  U U M  = M>PNVSNYPYIG@>7532('"%+462"$462"32672#"54?'#"&547654#""'632326?>32!"7>3!28N77N7N88NrN'# L#}o;NZX%# J+X=H}< %V 4 %'oM88M88M88M8-b#<+#CB{OQR=MXb#;+#DH;LR##7 .7()CKS@ 9<:K(PX@+ b  UM =N >@) b  UUN >Y@ SRONKJGF<;83)' C C +"54?".76&'"?32722>76'&763327462"6462"!g89muD" E9VBbXs 'Nud3hN b31jfR?9N99N9N99NQrBp}74%jZc3fYf!%}ӰZN99N99N99N9LM[f@c4 ! < db  b` U M  = M>WUQOIG@>7532('"%+462"$462"32672#"54?'#"&547654#""'632326?>32632#"&7678N77N7N88NrN'# L#}o;NZX%# J+X=H}< %V c 3% oM88M88M88M8-b#<+#CB{OQR=MXb#;+#DH;LR##7 .7*# " EMU@;<:K(PX@+ b  UM =N >@) b  UUN >Y@ UTQPMLIH>=:5+) E E +&'767".76&'"?32722>76'&763327462"6462")SS@b]^b9muD" E9VBbXs 'Nud3hN b31jfR?9N99N9N99NRY$-AE)$GdBp}74%jZc3fYf!%}ӰZN99N99N99N9L/MXi@f4 ! NNNXNXIG@>7532('"%+462"$462"32672#"54?'#"&547654#""'632326?>32&'7678N77N7N88NrN'# L#}o;NZX%# J+X=H}< %V *P%b.JKoM88M88M88M8-b#<+#CB{OQR=MXb#;+#DH;LR##7 .7Jr~`FG_e DLT@ :<:K(PX@*b  UM =N >@(b  UUN >Y@ TSPOLKHG=<94*( D D" +#"/7".76&'"?32722>76'&763327462"6462"+މ9muD" E9VBbXs 'Nud3hN b31jfR?9N99N9N99N rQBp}74%jZc3fYf!%}ӰZN99N99N99N9LM[f@c4 ! <d  b  b` U M  = M>[YUSIG@>7532('"%+462"$462"32672#"54?'#"&547654#""'632326?>32#"/76728N77N7N88NrN'# L#}o;NZX%# J+X=H}< %V I :8 oM88M88M88M8-b#<+#CB{OQR=MXb#;+#DH;LR##7 .78 %* (1@.<bM=M>'""+732654&""'632#"&54>-7XBvvEPfT!{#Psfba%Kmy-!]#=f6h3V!㮉P{Md3>+=/5T #Xm@jL2+ <  bU UV =  K  >WUPNKGDC<:641-*)##   +!"7>3!2462"$462"!#2&#"'763>76322&#"'7637654'!" %&;N<X)J!;6''= [#?+9E@ 0" @A  b  U U= M =N= M>Y@<:B?:E?&#"326!"7>3!27N77N7N77N #PZTǔZ5%=BZ1l %'oM88M88M88M8bm!\-CV>9:;'E()X3:FPYf@cU 6 '!<  U  U  V =K ><;54YWSQPNIGB@;F;32+9X;2h@) <;P9jCR?lh#1D-%1D{#%+''m`F)=:''Da#s" /%-D0$-D%#A#1=@ (@?b  UU=M=N= M>Y@42:72=4=0.&#&"$ +>2"33272'"74?''"432>?&#"326!"7>3!2[FP.FP#PZTǔZ5%=BZ1n %'gP;;P:bm!\-CV>9:;'C()\#io@ o>=0 YUg!@K ZbX U U  US =L >Y@.kjeb^\XVNLGEC@<963+(&%$" ih  +!"505>3!2'76;27!32�76;2676754&+"&?7!4&#!"326?6732#"/4&+3!2!!>5 $)5oH%=< P/-7% #?  f7PJHI32- /%1^?:b5n:Cb ()9'Zj1 /+!5  -NAV+/= M@9@/+ / q){ #Yv@s8PKJ <b   bU  UM= M  = N   >XWTROMIGCB;97520(&!    +!"7>3!2>54&#"72764&#"#"54632632327#"'#"&5%6 %&RjL5L?d-XbHPTX0)RJ;)B/Lrk q`}BbT;T^5(* 'D-/G %3^b@_%41<bV  SM  = M  >750/.-*'$! >> +23.#"32?#737654+"?37+"3#! 5476$.? 0^ؗ b6P 3/ '[D] />gT-B66---Tm 41BN!:DKR@9+ $  I@>  b  U  S M=N=M>Y@EEQOMLEKEKDB?=75#"+!"+#"'3#!".547#5367&5467&54632632#"'&#"4&#"32&'&'!326RT8/DAq% NS,_^;X?5C+^J^m#-%#  @)E@^A"i}{D}|T#X%& :L cM LddD3-qVV)9'#;om-19eMLKU^ Ad@a,74<: dbVM  =M> :81.+(%" A A +&'76723.#"327654+"?37+"! 5476$?c)oPub.? 0^ؗ7b6P 3/+ '[hX)5HL1)=R] />g9B66-- 41BN!5 ?IT@>0)$@7   dbUM=N=M>Y@JJJTJT#&"#".,)$ +32654&'&#"'%".54%&5467&54632632#"'&#"4&#"32&'767J}|KX}T8/DAq%.^5C+^J^m#-%#  @)E@^A4e.{9]!^`Xe^B5L)9u#X%& = cMD3-qVV)9'#;om~kMOir% f@F.5^<:K(PX@$d K  = K  >@"d  U K  >Y@"]ZVSIHEB?<30-,+)&#  +&'767;2�'76;27054+"?30%+"676'4&+"?37#32&#'7654'&'?b)qKybb5R8o`8P 5qR7RF#+OJ{P`DN   O{1EhX)7FL1)= B''ZB''Z/ Dh9 ''2AL!\' !/!D LKV@Q !  5Z  b  U= M =N= M   >@<Z  b U  U=N= M   >Y@LLLVLVIG@>864320!##4+7654/"576722?632#"&#"3272#"&'.'&#"#"54&'767+@ m 0IRfdD%57j/% E!   'Cfi=Y4?J T4e.{9]!^?'9 1 *R?FH+;_ +7? b`r*$! B*7~kNOjpTD!19@6!<QM =M>#"+)"1#1,&%!+#"&547#"547!2327"32654&#XVo25; /ILF5BI9Ҝ5݉ZRu~ JQ/JMF=|N)AJZQ> n_36@3&<bQM=M>4"*'&"+327654'&#"4>3 32>72#"&767'".J^5RX.Nd5?w{!WV g7$" qSR\{5\}}?Zx`êjF@`9`IObmK{eTy!1=J@G!< UQM =M>42#":72=4=+)"1#1,&%! +#"&547#"547!2327"32654&!"7>3!2#XVo25; /ILF5BI9Ҝ5s %&݉ZRu~ JQ/JMF=|N)AJZQ> n_!))3?G@D&<b UQM=M>64<94?6?4"*'&" +327654'&#"4>3 32>72#"&767'".!"7>3!2J^5RX.Nd5?w{!WV g7$" qSR\{5 %'\}}?Zx`êjF@`9`IObmK{ex(*+PL.9@+<76421:K(PX@(db`QM >@.db`UIMAY@///9/9(&$" .. +!2#"$547>32727654.#"!'%&'767?51= %d8/\#N* hF7d5st 5m)jUv{+D#͑-8jT )=H'/9s;^)h\g)3JP-)Rq3#%7B@"2 3 )<@?=;: :KPX@A   d  bb`ZUM=N>@B   d  bb`bUM=N>Y@888B8B1/#!&&#$$ +2#"&546323267654&#"'67#"&#""'>32727&'767㢏jm##F'%9}+\^`FI&o3HC!'x45:;d4e.{9]!^FwR568)-<7uo F)^-E^A h~kMOip`o7#.X,+)'&:KPX@dZR>@dbR>Y@ $$$.$..!$&+#"&54632326767674/&7632'&'767&%9TZJH`%7D%T@A%RL*{4e/{8]!^\3/ XatRK-PX@.I  U  = N =M >@5bI  U  = N =M >YY@]ZSQOM7C'&hc1 +763767674&/"?32632#"&#"'%3 654.#"&#!"547676#!"'673!>327!267'K-PX@O  b b  ` `U  U M=M = M  >@Vb  b b  ` `U  U M=M = M  >YY@ljhfcb`^][QOMK#'&hc1+763767674&/"?32632#"&#"'%3 654.#"232>72#"'&#"&'&'67#"&#""'>327'@wbbb  `  `U   U=M=M= M =M =M >Y@#pnljgfdba_USQOLKHFCB8643'4%%$$+654&#"326'#".54323674/"576723272'"547232>72#"'&#"&'&'67#"&#""'>327^J7ZZP9N^NTbJ7XL+@ m#PZT<kD@Z&7"#1J%!FpFZVVFD u&1HC!'x495N;wo}jwE?2oF 9 1 *RLbm!\QTc#=;r3594 '-E^6>c@`$-<<;: db``M =N>877>8>0.#  66 +23.#"327654&/"?27!"5477"47%->4 4elx=,31 w 1// ?+!JD]} <>^Vc9 66 #1$@CDR-\!b ?IW@>0)$K/PX@: d  dZUM=N=M>@; d  dbUM=N=M>YY@SQMK#&"#".,)$ +32654&'&#"'%".54%&5467&54632632#"'&#"4&#"32632'"&767J}|KX}T8/DAq%.^5C+^J^m#-%#  @)E@^A ?(/ `Xe^B5L)9u#X%& = cMD3-qVV)9'#;om/' $%=+b@ =&@2  U  S=M =M>Y@_]XVJHB?<;:852.-3#53#4+!;2�'76;27054+"?30%+"!654+"?30%+"327654'&747>32#"&54LEcZb5R8o`8P 5qLLc5P6qQBo XJ5)- B''ZB''ZOB''Zr%HGD)5 -Nbyq1D"/7@4'9bM >$##/$/""" +$7 '4?>7654#"?2767">7674;/ L +// +C #nZ'gDuD3Pu}RL3 :3P+D 3ebub`FNAJg@< @dU =N>Y@ (K"K"+#"'&"&#"'76?67.'&?!327654/&?327%#"'%7'/)X3)4j>-? W1*7j'7u?1Dd1R'%q-[ 'sq>S'''\)b9Gz@ 1 @)ddbM=M>Y@ $)','("# +%32672#"5476'4&#" #"547654/&7632>32#"/7632wN'# T#N"%^N Xt%RP*+V[ # HFb#<+#C1Gq->!(7lV%!+Hzj't)/3 RZg@dP G@,<:   b U   V = K >TSVUSZTZKIFB?>750.+'$#    +"2676&>32>7%2&#"'7637654'!"2&#"'763>767.!#.F +[G ,G9Q!@N28;Yq9d3g9J7q#H^5 :?VT/C0.DE-1BsFd@3\0N;6''= [#1''>X) ]" )7BL@. @Kd db  U M ==M=N= M>Y@98LJFE>=8B9B64&#&"$$!+632#"&76733272'"74?''"432>?&#"326"2676&>2#" 3% F#PZTǔZ5%=BZ1@.E *[G -V}IG*# "bm!\-CV>9:;'C0.EE.1BdcGEebk@43 S `_ @Od Z  b  U U S =L =K >Y@#dcckdkbb]ZXVRPHEA?<93;3+!6'0'763767!2&#'76?>76&/"?3!.+"326?6;2#"'7.+3!267"547%8l6o N '7  N-732!; X; #=+!1P1/ +)-#/e Ff<9R#+!Jq)+7%Uj'  +' 'H%)) 76J"5%j)2=APN@/+H ˋ\{b M[@ ,D?> @E d  db  bUM= M  = N   >Y@WUQOLKHFCA$"#(%)'+>54&#"72764&#"#"54632632327#"'#"&5%6632'"&767sRjL5L?d-XbHPTX0)RJ;)B/Lrk q`}BbT;T^v ?(/  'D-/G %3^#".,")#)!!*( +!"''7&547!2%"&%"67% 326563U;a迂6{;Y95\+ Jh5\3JMd3JQ/WZQjC-\\ȉ> n_^b%.9y@!9'& < 9K!PX@"b =M=M>@ddM=M>Y(%&#*(+'"''7&74>32632#"4727654'&'&#"Cz,No[?_DcE?w{xE =1n*9RX.Nd5 1NF''u7y\`êj8%).F}% ZiH9T<L[K-PX@SIB 0<@SIB 0@6 d d   db  V =L >Y@&>=XVQOFD=L>L;942/+('   +!#2&#"'763>76322&#"'7637654'!""/&'4632%4632#"/&uVTJ7q#H^5+%8;Yq9d3g9<' 78D'L  D"^1''>X)J!;6''= [# @' %9  5)9H@  KPX@9   bb  =  ==M=N>K&PX@5   bb  ==M=N>K(PX@2  d   db=M=N>K1PX@;   b   `b  ==M=N>@E   b   `b  ==M=N=N>YYYYY@;:+*B@:H;H31*9+9#&#&"$+33272'"74?''"432>?&#"3262#"/&7>%2#"/&7>H#PZTǔZ5%=BZ1Ƭ%e 5;"E 4wbm!\-CV>9:;' -  .Tq<Lh@e0<   bb U  V =K >>=JIGFCB=L>L;942/+('   +!#2&#"'763>76322&#"'7637654'!"2#54&"#>uVTJ7q#H^5+%8;Yq9d3g9#{j;o5D"^1''>X)J!;6''= [#yN 5BC:m}))6@60/*  K1PX@,b  U=M=N>@6b  U=M=N=M>YY@ 42#&#&"$ +33272'"74?''"432>?&#"3264&"'>32H#PZTǔZ5%=BZ1e 2+`luwbm!\-CV>9:;'RfbV~mJZiK1PX@aW *2<@aW *2K(PX@F  d d ZbZU N  ==L >K1PX@D  d d ZbZ   VU=L >@H d d d ZbZ   VU=L >YYY@LKfd_]TRKZLZJIHD(3T4&&#"5+.+"32?6;#"?654&+3!2&#!"'7637>7654&/"?3!272'"/&54632%4632#"/&m9DgHRX+"  +'.d= z;r^ZB<93 (/9 ?// :<& 78C'L  + yfFRV4{>XZ4 " . ;))+ )J ?' %:  D (8H@>@2bb  =  =M=M>Y@:9*)B@9H:H20)8*8'"" +4&#">2672#"&54>322#"/&7>%2#"/&7>-7XBvuFPgT!{#Prgb`%Ll2%f 4;"E 4-!#=g6i4V!㮉P{Nd3=+>/4 ,  /mqJZ$@*2K(PX@K b ZbZ UU N  ==L >@I b ZbZ U   UU=L >YY@LKXWUTQPKZLZJIHD(3T4&&#"5+.+"32?6;#"?654&+3!2&#!"'7637>7654&/"?3!2722#54&"#>m9DgHRX+"  +'.d= z;r^ZB<93 (/9 ?// {j;o5+ yfFRV4{>XZ4 " . ;))+ )@yN 5BC:m}z+ (5v@5/.)@%bUM=M>Y@ %'""+4&#">2672#"&54>324&"'>32-7XBvuFPgT!{#Prgb`%Llye 2+`lu-!#=g6i4V!㮉P{Nd3=+>/4ORfbV~'+%5DK1PX<2<<2K1PX@ddL >@dddL >YY@'&A?:8/-&5'5OG +6&/&67327&#"&6?>7"/&54632%4632#"/&&KPX@-bb ===N>KPX@)bb ==N>K(PX@& ddb=N>@* dddb=N>YYYY@10! 970?1?)' /!/,"$ +32672#"547674/&76322#"/&7>%2#"/&7>\N'# T#`%R-* %e 5;"E 4nb#<+#CN%!+6g -  /'Zo%5XK(PX@bU = >@bUK >Y@'&320/,+&5'5OG +6&/&67327&#"&6?>72#54&"#>&@ bU=M>Y%,"$+32672#"547674/&76324&"'>32\N'# T#`%R-* \-'Wainb#<+#CN%!+6gWRfbV~/>K1PX6,<6,@'dd dM =M>Y@! ;942)' /!/  +"32654&!"547!2"/&54632%4632#"/&}9Ҝ5p;a; ;' 87D'L  ZQ> n_ JQ/J ?' %:  D/?{5@*b  = =M=N>Y@10! 970?1?)' /!/%'&" +327654'&#"4>3 '".2#"/&7>%2#"/&7>J^5RX.Nd5?w{!No\{5 %f 5;"E 4\}}?Zx`êjF'K{e  ,  /q/E@Bb UM =M>! -,*)&% /!/  +"32654&!"547!22#54&"#>}9Ҝ5p;a; B{k n_ JQ/JyN 5BC:m}),\@ ,&% @UM=M>Y%%'&"+327654'&#"4>3 '".4&"'>32J^5RX.Nd5?w{!No\{5f!2+alu\}}?Zx`êjF'K{eRfbV~! HXgK1PX@_U 2<@_U 2KPX@E d  dU M =  M = K =M>K(PX@E d  d U M =  M = K =M>K1PX@? d  d IU M = K =M>@C  d d  d IU M = K =M>YYYY@.JI db][RPIXJXGDA?76541.+*#" H H   +"326767&'23#"'.#2&"'7637>7654&/"?3267"/&54632%4632#"/& bNdb# q3? `D;7IXqX+244=7 ,L @+;' 77D'L  -11(E'?Mf#*RPN1d)%? '')+ !  +J?' %9  oF+;K@ A@.bb  =  =M=>Y@=<-,EC32#"'&#"#"54>7654/&76322#"/&7>%2#"/&7>DL566'!$9< N r%R%,# %f 4;"E 4H;%+H+@(7#090%!3 ,  /!q HX|@ 2KPX@J  b  UU M =  M = K =M>K(PX@J  b  U U M =  M = K =M>@D  b  U IU M = K =M>YYY@.JI VUSRONIXJXGDA?76541.+*#" H H   +"326767&'23#"'.#2&"'7637>7654&/"?3262#54&"#> bNdb# q3? `D;7IXqX+244=7 ,L @+v{j;o5-11(E'?Mf#*RPN1d)%? '')+ !  +6yN 5BC:m})+8j@ 821,@!bUM=>Y@ %.'"$#+>32#"'&#"#"54>7654/&76324&"'>32DL566'!$9< N r%R%,#re 2+`lvH;%+H+@(7#090%!3lRfbV~FVeK1PX@ ]S <<@ ]S <K1PX@'  d   d UN>@+ d d   d UN>YY@&HGb`[YPNGVHVA@?>;743'%FF +".547654&/"?327#2>7654&/"?327#"/&54632%4632#"/&s3_gL0+23@D3=8 y8R`\H u+06!3=:o9;' 78C'L  2MUAN '',+=pIf@\xRX ''.-z ?' %9  LD=M]@S $ @<   bb` =  =M=N>Y@ON?>WUN]O]GE>M?M''"('"$+32672#"54?'#"&547654#""'632326?>322#"/&7>%2#"/&7> rN'# L#}o;NZX%# J+X=H}< %V %f 4;"E 4-b#<+#CB{OQR=MXb#;+#DH;LR##7 .7Z ,  /qFV<@,   b U UM>Y@&HGTSQPMLGVHVA@?>;743'%FF +".547654&/"?327#2>7654&/"?327#2#54&"#>s3_gL0+23@D3=8 y8R`\H u+06!3=:o9r{j;o62MUAN '',+=pIf@\xRX ''.-yN 5BC:m}L)=J@JDC> $ @/b`  UM=M>Y@HFA@''"('"$ +32672#"54?'#"&547654#""'632326?>324&"'>32 rN'# L#}o;NZX%# J+X=H}< %V _f!2+`lu-b#<+#CB{OQR=MXb#;+#DH;LR##7 .7RfbV~hDHN@K32<9beM = =M>GE640/., +2&7>76&'&7>'.#"#"?!27654&'.5476$32:1c J`S E=?-ZD`;XV-X6 >-ZP@[`3 jo`PWd!>A%3q!9;NP's^d7QlB#*u*"Un( :UnA!#>y'+>J@G29b`eM=M>-,,>->! +* +.5463232654'&54632'"&'&#"#2&7>76&'&7>df%')1)+Lb}\J%5T=DRm:1b J` SD^;/B+!HPdTydj:FA);V;J7ZMuJ`PWe!?A%3?D1D@-<:89K&PX@'b`eM = >@%b`eU >Y@322D3D,+)& 11 +!2>7#.+"&#"&6?>76&+""'672&7>76&'&7>!3=$ELo5/ ';FP=@=7 @%3}(;@ @+db eM =M>Y@*));*;('%##% +7>323#3272#"&547#"547672&7>76&'&7>} !Ry3q%3H31| 1):1c J`S EofP%#7w\ <d:'\v(6)- !`PWe!?A%3D=m@ *:5@!beUM >Y@#! == +"&547>7>7654�&#"'32632$ oh\BT#/? NRF ?% D5's'%w=Z) PDwfedd1-B3-l !1+ >=@:+<<b=M=>#!  >> +"&547>767654#"7>7654&#"'732632L#}+2 uP9`s')HNL XDXa DNeI'5+ =.B## 4N=>_hbR 7k3uP)?mH;! 9] #/'7 Za:K(PX@dT = >@ddT >Y@ZYQM>:21)%  +&'7676&/&67327&#"&6?>7!&#"&6?>76&/&67327!5m)jTw{~';FP=?=8 );FQ=?=:cc );FP=@=9 &=:KPX@/b= M =M=M>@-b U=M=M>Y@;;;E;E("'&7% +%#"547654/"57672>3232672#"547674&#"&'767h T+? m|XNN'# T#M#%5eQ4e/{8]!^>!(7l?'9 1 *Rԙl'tb#<+#C1GV3-@~kNOjp#+HK1PX@  CB %<@  CB %K1PX@-b M=  L =M>@3b  ` M=  L =M>YY@GDA>;8313#7" +7654#"57276733232�'76;267654&#"32&#'74;26մ -- 'hV)--?J1P11->CL/P01/9B 3F ;''+0^`RX=''+ '=M\!K-PX@  O H 8<<;9@  O  H 8<<;9YK!PX@:bV= = M= M= N>K-PX@7bV  R= = M= M>@=b Z V  R= = M= M>YY@\ZRPKI,(#%*% +&747#"'&7476707632654#"72>7673367632#"''6654'&#"3276&#"32=uwJB5=yHH!)9 0C#9:-K(PX@dM =M>@dUM>YY@?>>O?O)('& +#"'&5476767'&54767676767654'&'2767654/DF dSc)d-|i ^ZX?R-;XRP'dX\yr=_h@FZ!SuT}b\n3P:`uPduPN)BBQ;3H-#7HJs'*?&5CBy mxTN} V0G'E[D@ OE;4.-$ :K-PX@ M>@IMAY@ GFF[G[ +#"'&'&547676767'&54767676767654'&'2767674/%  S}JNy+R% DFQL3R^#51!#  A/%#pF ^RN}ad  --'%32mQwTuk\F#C%)`uK71R`u% U! 5#)+:%3J5oF\ 9. FB|uqVNy-!!=- V7@ ,@.dbbeUM >Y@431.*)&# 77 +%2#67654'&+!"'<767!"+7733!2#x4' *;9- =g5#"s9&m'5m-@X7)7 /@;'7/5#D b !0DJ7E@B<be=M=N >1.,)&# 77 +%2#67674'&+!"&7547!"+77;3!2u9) %#8;- =gb# 9)>/l+A/P9-. 3>9'7/4"D ! b2 37Td<DV@S0<b  U   V =K >DC@?;942/+('   +!#2&#"'763>76322&#"'7637654'!">2"uVTJ7q#H^5+%8;Yq9d3g9FP.FPD"^1''>X)J!;6''= [#P::P:)1@  @6b  U=M=N=M>Y@ 10#&#&"$ +33272'"74?''"432>?&#"326>2"H#PZTǔZ5%=BZ1:FP.FPwbm!\-CV>9:;'/P;;P:m1c@$*K 0 E; : KPX@S  ZZ  b  `U  QN ==L  = K  >K(PX@T  Zb  b  `U  QN ==L  = K  >@R  Zb  b  `UU  Q=L  = K  >YYY@cba]ZXPMJGDB><$"44&&#"5+.+"32?6;#"?654&+3!2&+632#"'732654&#"'7#"'7637>7654&/"?3!272m9DgHRX+"  +'.d= z;r^qC5DkHI;89Y))5 pB<93 (/9 ?// + yfFRV4{>XZ4 " . ;h10Ld#%7/ ))+ )d @Y@V+! <,;bbUQM=>'$#$%" +4&#">2672632#"'72654&#"'7.54>32-7XBvuFPgT!@5DkHI;89X))6 hv}#Prgb`%Ll-!#=g6i4V!d1/Ld#%8/ P{Nd3=+>/4y +;J@G U U  M =M>860.%#++  +!"7>3!2462"$462""32654&!"547!2W!;N< n_ JQ/J/;=@:  UUM=M>20850;2;%'&# +462"$462"327654'&#"4>3 '".!"7>3!27N88N7N77NF^5RX.Nd5?w{!No\{5$ %&oM88M88M88M8%}}?Zx`êjF'K{ed()d/De@b4 > <= ;3 :  U U   U M =M>10B@;9750D1D,*$"#!# +32+!#"546;"32654&!"547!2267#"'&"'>32}{o}9Ҝ5p;a; h0(?y,:7!/'#P78<*5*)ZQ> n_ JQ/J1D ;239 fc?24@_@\#$.<-;  UU UM=M>75! =:5@7@20+)'% 4!4%'&" +327654'&#"4>3 '".267"'&'"'>327!"7>3!2J^5RX.Nd5?w{!No\{5W$7!/J3EA$'7/)`@AG3 %'\}}?Zx`êjF'K{ex-> 5-/4 ]Z:-()b'3@0UM =M>'&#"  +"32654&!"547!2>2"}9Ҝ5p;a; FP/FPZQ> n_ JQ/JP::P:'(@%UM=M>%'&"+327654'&#"4>3 '".>2"J^5RX.Nd5?w{!No\{5FP/FP\}}?Zx`êjF'K{eRP;;P:b/7A@> U  U M =M>7632,*$"#!# +32+!#"546;"32654&!"547!2>2"H9Ҝ5p;a; FP/FP+')ZQ> n_ JQ/JP::P:'39@6UUM=M>*(0-(3*3%'&" +327654'&#"4>3 '".>2"!"7>3!2J^5RX.Nd5?w{!No\{5FP/FPY %'\}}?Zx`êjF'K{eRP;;P:L()M)AMYK(PX@bU = >@bUK >Y@DBJGBMDM73+*"G +%&#"&6?67654'./.732736&/&67327!"7>3!2 );FP=?qH!)3sb;D1  C%B-w615' < %'/)-' Uv"T3+-#)-/-.7;()d!e8DJ@G%$<bbU=M>;9A>9D;D20%("## +!#"&463232676'&'&"&'>32>?674'&5432!"7>3!2]bR'V<#' -t<J- +)]<"%/*# O *+ _k76 $&)Z;%b)5A,+,^DS"}Bp:ALaY<"(*'1 0@*% <9KPX@"V=M =M>K!PX@ UV=M>@UVQ>YY@ <$$$!+%72654&#">32#"''7&'47654/"576725C(+\wxB+3l?% a+@ m=vFQrd1-s8k,<?'9 1 *RZD)J GA@> %"<$#9UM=M>,'*#*%$!+%72654&#">32>32#"''7.76&#" #"547654/&76325D&/_V;@ 2ssI`m?%!3CN =^N Xt%RP*+`tCVj7i`DdsgaquMPXw>!(7lV%!+Hz;- 7A@>!41<:329UM=M>((&#+327676'&"#72?6?3#67632#"''7&'4X%!1L^3 )'dd5 % }GjmAH;1cov=*#dJ+5RFP/ 1')P7j4189"#ZL\3o `o*}#@bR>Y.!$&+#"&54632326767674/&7632&%9TZJH`%7D%T@A%RL*\3/ XatRK-PX@Db` = M= M= M=N>@Ab` = M= M= M=N>YY@USKICA9755*"*$ +67632"'#7#"'&7476707632654#&?276732767654'&#" &#"327/+`eqET 75=fkF%+%wJB5=yHJ )D=U/F/]dj.1]H29:o-'}\Z%DtsZ)3N`5<+ 5L"!dbj`{=<߃+P7F193Nu}^FAd+#ygbPFJ,R#-M^d@a N10& 7 <M= M=M= N= M >[YQOKHFE"7)'#($(! +&'"327'"'&54767632763267632#"'32�76;27632767654'&#"T7{y\X#'D{dJgWbI39ÃN-LVJK<; 1jHR'' P %-5fyTm1!+VBAL3kfT?TBd-EwDXj;:\Z7+uX+!^`u?0ۍ-%-- VLfVDZ;P-+N8;CK@=<9" G /)@+de   V = M  >Y@JHCA;:8841.+4#32+ 0;20'#70#0'76;276323;2&#'76;254''3';#"r;hikh@)=i <;P9jC-/91``hI;E'uu'm`F'1q;=:''Da+ O#%+G@D!   <dbeM =M>''$!+%3267!"'#&547!2733&' &#"bэ+hd 5lf?.?_VBky%BC'' T)FHPw+ ,n@,%"$<:#9K PX@ZM=M>@bM=M>Y($#+'+ 07674&#"#"'327#"''7&5432\ L#7=#J-j#+`5!>F/+'!m1-0^B{7H;bT)#-<Z @dVK >Y@<;(H#(U5+;26&#!"&6?>7!"7>;6&/&67327!2#i(${#T#5`=@=9 m%E&K&PX@+db`eM = >@)db`eV >YY@ I"!%4 + #6&+""'673!7332>7#.+ &#"&6?>7jei7>HG##($.$% +32767672"&'.'.5463232654'&54632'"&'&#"#j2( *DY   FR%')1)+Lb}\J%5T=DRmKeE)!%\P 3$Q-/B+!HPdTydj:FA);V;J7ZMuH#9@6 < :M=M > ## +!2!2767!"70747!"'/J3#/B3hD9)F3)q37 !k3 2  hwCn@ /6@#b`UL >Y@ ?<:753*)+%2767654'.#"#"'47676 32&#76;67676mJ3t-V1'9?\b;|!sH0) W )- T`XBH ?#F!2> sIyy?Y,k]`w-+  #+JVK-PX@? % <@? % K-PX@- Z  U  UU M >@3 Z Z  U  UU M >YY@USNLJEb5$#2(%$ +!2#!32>7654&+4;26?#"=>;654&+"?3263 !"&#3267654#"#%"Aho`4 3>=3 ##)5>?!%XT+J<) !;dc5uu)6)+7R';9bw{ AK1PX@ ?> <@ ?> @&b`M=N >Y@ =;0.)' A A& +>7674"'2763232676'&7>32#"'&547654'&#"'>RR`Rbmfs5'?By 1)Bo)F-81h5`s`f1=\PT/H-J'/5+ +>V?N-o ZRX% &KPX@ <@ @+bM=M=M>Y@%#!    +%27&#"%#"76$32733273#"&bk%`+-x4'R-5@sL{- =/qZ55=])#L ==s763{{cD# &#KPX@ <KPX@ <K(PX@ <@ KPX@+bM=M=M>K(PX@!bM=M>@+bM=M=M>YYY@%#!    +"32676&>32#"'#>&#"#>32bj%`+-y'Q-5sK{- =/pZ563\#K>>753{{bC 4=@:'<bM=M=M>%&&#$&!+%3267654&#"#"&'&#"62#"'#>7672B-^)\Lux0'H)b!!1s7F)'%%p^bLfzsN;@jZ+!'%#\^PBu+HK)٠9;P7?(:@7b`M=M>$#! (( +2#".546323254'&#""&'>=F=ZN=ANe) @+V}4%ALCObVSAO"F5'%7'5!N݉B/PLn3 7~@1 <9K PX@%ZUM=M>@&bUM=M>Y@ 42+)! 7 7#! +4#"26"''67.5432#".5464.#"32-9;\-Fw's!+BlPE-TB%#" %!+)Ӆd)'slw %Q|$_XXP %3!%# -HRmH?\@Y?8* <bbQ= M=M>><75-+%#  +"327&67332>7632#"'&547#"&547>32654#"J{P5LsZ+3z-+9+ 'J7 wuCyHL!%=FDsvDT=wR3y>ZdP5 9c-V{b?8}j37JJ# =^@[. '<  b  b`M= M =M=M>=<:8'"%##%$$ +654&#"32667632#"&6&#" 3272'"54?'#".54323^J7ZZP9)FdRwT))T#PZT^NTbJ7XN;wosY7\3@3bm!\Q6}jwE?2z (;@8<bM=M>"    +2.54>#"&'"3 654&#"To E-KER/#xc9V|8DKh i\/;$T 8}Oa!{v7l5QDwm#1@.<bM=M>%#("+%32%654&#""'>3 #"54>L19V^LFtZZy A{} ;my+#u 6P` {wdѹr!1XCP\JH@E5,+$73<"!:b`M=M>B@%,$*'+32>765'&'&#"'>3267;67#"&=67#"&547>N-)T`T FbL`ZwwL%/HF $Dr 8\ud}du^fg =P#NoZBfRT9$ofjJ 3-%u +/5J!5?4Hd?vJJ{T{<K1PX@5<@5K1PX@,bIUM=M >@-bUUM=M >YY@ !##$&-#$ +%327# 54>7&547>32#"'.#"37>32#"&#"3TL 1dFsmH3 2$;v33 <V TyBRV{!5)H=/\RJ3 JAVZ-o ' bVPy8K-PX@+ 5<K1PX@+ 5<@+ 5K1PX@,bIUM=M >@-bUUM=M >YY@ %"%!$!$%" +%#"&'7327674#"#"'47632327674&#"'"&=>32`q3!1oZ,O5 7L$F>RC53'o!`?H!;.! u % ) 9=\H$7]R\@7qV}R/K-PX@ID0+  O=732:K1PX@ID0+  O=732:@ID0+  O=732:YYK-PX@/  b`UM =M >K1PX@4  b`IUM =M >@5  b`UUM =M >YY@CA:8%"%!$!$%" +%#"&'7327674#"#"'47632327674&#"'"&=>326732767#"'547`q3!1oZ,O5 7L$F>RC53'D9k!7% -D}C= 5iL!`?H!;.! u % ) 9=\H$7]} d@ ; 8!\#O1P@7q#6j@ '#@UQM>Y@.,&$!66 +2#"&5476 7654'#"=63267654!"ٶV=#͓ry 7R ;)qj^^Vjh> M9Q-<1{qKRϡ18 Y(#- ;(-r Pvk+1/wV/w9A@> <b  UM=M>9742#&$#$2"3 +654+"?307#"32+#".547632326#"7>;;> X ;#+ :*5\m^%MF@;:VIB#) +--!#*sX5) =K-D"`d H_@\ <b  b  QM=M= M >FDA?97.,)'!  +%27&#"%'"&547>327>7672#"&'&#"#"&547>323267o+^)f$q%d^B%q^bLfy/'G)c!%s^mX0 %j{/PZNN;:mxu73h)q9;P7 !'%#\^PJ[7DcC !&"!}`d 4߷ KPX@*bQ=M=M >K(PX@&bQM=M >@*bQ=M=M >YYY@20,*$"  +%27&#"%#"&547>326;#"&547>323267i+^) \,o %dFD%s^mX0 j{/VZFN74fu/7hLX7DcC !&}5C@@$<bU=M=M>&&&&3$ +4654+&?37#"&5476323#74654&#"3267)=b@ 3 # +?+67Z` 5|" #--+ ͚1) %XVϠ7-@1w:K:@7JD <QK>@>0.,)&$:: +"&5476?4'.'#"?37#"654+&?37#'3267674654/=H 4wq '% oH  !DGV#2  jD 17%r v~9+`7/ 1B!--1BT;-)">R9'1o3% +@zq/.@+.( <=N >$"  +%"&5476?&'727>3'3267674654/=H0{w F bVT3VH J7; v~9)`7/ 5>s+ 4-:?,6A̰-)1o1% +={C=G@D.<bbM=M=>("'&9%+>322"767#"547654#""'6323267 T +@ @)nYNN&# T#N#$6c? (8 $3R@&9 1!Rl'uFb#:,#D1W2, ;A@ %6@1bbM= M=M>Y@975420)'  AA +"#"5477>32#"'.#">3232672#"547674&7eN T?b98j%)L$%#d#"QZNN'# T#M#>!(7l/tD@Y@/ePT;l'tb#<+#C1GV3-# Mҵ$KPX@4bZM= M==N>@5bbM= M==N>YY@EC9731(& MM + #"5477>32#"'.#">32#"&546323267>77& yN T?b98j%)L$%#d#"Q^GS/-Js'=a\%'5   5T!O >!(7l/tD@Y@/ePT;lbÖ5L7',#  #.8'/G@D <b  U T=M>/.+*'')"$ +#32672#"54?#537654/&7632462"6N'# T#9%Q-* G +%2672#"547654'7270qN%# T!do7+jq1+#C1`>3#53#3+654+"?307#"32�'76;27sJ'H 'VH'Q 'Z5''Z7''Z5@#1 ,4<;KPX@3bU V =M =M>@1bUU V =M>Y@0/.-+(#! 55 +267#"'32672'"547&#"'>32674/"576723$7!.JEY+}VZ'7/)_A Y+@ mt"-> mB5#Z-.4 ]Z?'9 1 *R5 7@4 / 'K&PX@6b  U U =M=M>@4b  UU U =M>YY@! 7 73210.+&$  $ +.#"3!#32672'"547#"&7>32674/"57672":(Z KJGY+}VJSi m+5Z+@ m{ ,;D-5FmB5#Z-KM]:E}?'9 1 *Rb}#-@*<b`Q>"%(4+074'3232>54&#"#"54y3m ?+BVi-iY!7!+)0) 1 :'?sُomD/&AH+5bS@ < N K*PX@Eb`  b  b= M=  L =M>@Bb`  b  bQ= M=  L >YY@SPMJGD?=&#%-6 +7654#"?2767303!2#"&5476323267654&#"!"32&#'76;2 '/  #nB3-w- %} >J J9`%g;BN;H/ P/Z )'; 3 + w18T3=7'7#dHL7''skV@Se^]5OIB C <b K = M   >jgda\YRPMKHF%335&338%+326767654+"?37#"327674+"?37#"327#7#"&5"547654+"'?37#"c7!;}abG'R'Zb6"XtH' P'V!)/W!#/jVNLכbH&P'1/V;1J9ET5 7''ZX?1D;7''Z"% 9%Z[mH=_ 7 ''+%{qT@Q q<b K = M=>pnhfc[YVOMGE6&37##+##"&'#"547654'#"5732326;+27654&'#"?32326;#32>7654'#"?32326;#727Z-DVNId A!    !5!bN#!     %5# f Y5l18 B     #5!t{ =q3L7]e;=m)')' %:VZ?j!)'%:F74/'-%)')' %:  5+c@ L K-PX@Ab  b M= M=  K  =M>@<b  bI M=  K  =M>YY@$]ZXRPMGE<:8752-+$" cc +"7673>32632#"&=63232766'4#"2!7637676?654#"2"&#"#7637>7654== f-!7Tj7TFNlDL0 +%+9XoTA!f  %/ gXJyIB! %5#   <7/c5w/=J+ 34Jq+h-) +) 5B(C@-) +) '3#/ J+E @2b  bM=K = N>Y@CA<:31*'% EE +2654#"76736722"&#"#7637>7654&#"#"&547632q` = \-!Nnf%! %3!i>7V:RbHE1#bF- <4Yp=@A!) %9H)F4Hbe/B/!--#,C=@:-<bZM==M>!+($'+"+#"'.5476'4&#" #"547654/&7632>32326325$v.K@'"%^N Xt#RP*V4Y(! > 4v#'&udbo->!(7lL'!+Bfj1jQPE6CI$=o9m@ )@K= K =>Y@631.'%! 98 +72>7654'+&?3654'#"577##"';2&#763%&=> qy@= wK ;1#+1{#<yN /%P-' '` ) )''!9P;+-%'-(@%SM=M>&("+%#"'&54767632& !656!32767>5HT?9L71)TB'f6q_;$R+;`q+C^_r1LqiL*\-dDBCoFmq,<@ # @B  Zbb`  ZUN=N >Y@:8312!%!%3 +>3%3.#326?3#7654&+!27#!".54654'#";67#o!)#'H;) 3F3 /H'{`'g!ImE` aM dm1[DI#)6^?}) 7By9=N5@2JD1 <UM=M>$'((&)"'+"'#"'&'476$7632327&547676232654'.#"654"7676 3{מM9%AqQB (: R)Ak ! L/9nhiLd7LB7`X=3<+)w=!h]j76l!3CJ/N)f Ln@CH/ '@UP=>Y@GFEDBA@?;:,*&#  +%>7654';2&#'767326?.547>7654#"57273N-I>`R' _):H) P'10 9fG 5B 'D -y1 J?@Hj7g\-L@0y7''+/Ekj<39qg77; 362m%+1@.<b`=M>$'"$#+'#"&54632727>32'"547 DL555'!$9< N q%R%,$';%+G+q@ (8#/9/'!336B@?1<b`UM>337"'"$ +327#7##"&5465>3232767654'#"?37+"f!+-[)#4;H7@ @'N #DpJ;O '[oA!% 7$=z+# '=3y#%X7''%s@@ KPX@-bZR K=M>@.bbR K=M>YY@=:85/,'%  @@ +326762#"&54?'"547>3223267654'#"?37V'+ 3'&%7%FX!%LX\ B-H %-sA!T  = %{5+B'{p{b)74FT -7PsD--') j6A@>1<b`OM>337"'"$ +654#"7676733>32#"'&#";2&''76;2}-,Z- #1;G7@@'P !FnH; O 'X!% :%=y+# %?5{#% 7''s=R@O2<b`bQM>64.,*(" == +"767307>32#"54656&#"72762#"5476547;' i+)"#11:=KD+H%%-T9#=7?#@7 #+'+5I y9_q+J+ =NdZ &'V{'d@ @bM=M >Y@ #%&2"3+%;2�74;27>7672#"54&#"o:7 T9FB#bTbA;>DJK8) )''^9=945F&}{,3@0"<bM=K >229'#%+%654&#"#"&5465>32;2&#76;26G-'-. !C#BDC=B78 Y  9-"8K5#)D+ 9B>;yT1'')?q 8@ !@0ZSN=  K =M>Y@ 631/+*(&$# 8 8"$ +654&#"326267674'#"?!23#"/#32&#76!Xi1)VodQD%)4 j1[+5B fB s?Z u !7u'Xg$L+' /1 )q5AI@F=<U M= M >@><:20.,$! 54 +3237>32"#!76;267674&'#"?7#654&+3261!' 3`11tX) `P5!f4!%  mRXoV/'fB##XB) F?u'"w! #! =X m1HUi@f$ <b``   b  RM= =M>PNJHBA@>,*&%"  UU +"54?33267654'.547>32.#"#"'"&'3267>32y(,1  S3DhT}H1)<-Fu9H=DP# Zsh1fSB%A0)/F%'-'REdO1ID!!h%Nj+JP )2"%=H-Hj3LX)^9#%)Bl5J.KPX@$Z`M=M>KPX@%b`M=M>@"b`QM>YY@(&  .. +232>7>7672#"&'&" #"&547>y9R -_d^V^a0;[-_bVLBh-/C?H/ )N{D=?3)5J<KPX@.Z ZUM= N  >KPX@0b bUM= N  >@-b bU  RM>YY@65.,)'%#  << +232>7#"7>;>7672#".#" 32+"&547>y!9N [%b-_d^V^a0 ![t'C18Pni-'2&3"C?H/ )+3+{.LRZb/?3)JHl@ @"b`QM>Y@ *%-($,+32767032#"'&'&'&547654'&#"#"'&5476767632T'*5)-  >?1- ;6-1)=))& #)35DV?3  =\Q# #97/RfrN hF%  !!@1P !h\j&2S@ 1' @bQM>Y),+"+#"&'&67#"5476%67>7672326?0wO)2fX-?oy; -_d^V^a<5q!#)NhDR+#'߷R 'C?H/ ~Dw3@0<9M=M >%#$+%674#"'6323"#"?63Fv-TL}{ fe H/RX%=7{#/Rm&); )1 A@><:bM=M>    +2#"547#7276?3#326)ds3% }G1 '3<' BIXx:P91')PB-oys9E`@]2  B@;:99741-)(3$"$+#727#7#"54?#737654#"#'?37!7654#"#'?37!32767yz:!Q@1) 5#I DL'##E :R)#l/ L3DNR*7PL#7-/..P% 72- 30P')L;15'}{IjKPX@'b XM= M>@(b  `M= M>Y@ ;9!-'% +630!#4654&+2#"&547>?#"#7!232>7654.'B%%?y TZ'Ѹ %@-\!22yH.#L'u} xmRP; #B9''sNz5*#GP5R))'B} i\)^w/BT9%1A-?>=1-@*<b=N>,*$"+#"&547>7654'>32327654'&747>32!9ߖ[ +`'8?o `e%RD7'ߎt3BZ+%)9B Hh/'VgR/y1 -P5:@7.$ <=L >)&#  55 +%32�'76;>7632;2&#'76;276''Z  sK -A'#r "+  mC"= J=9)%->-)>7*))!*f`B/ !S;@8A$ S5<= L  >RQPNKH#33>33:1 +%6;276'';2&#'76;27654'';2&#'76;2763262;2�'V9 L< 4qG%;J< P!N TH!VCA r 4^Z+!,h`^?$))  f`F -))q+)-)@bUK >Y@ 45"&&34;1 +%6;676';2&#'76;267>32#"'&";2&#'f%5^#3! oG /=#?;SH#A >!#KBV V b>+%7  ''25hZk\'' )'#Xf B'{u;9@64*'#< K=K >85"+2"2"4 +%32'76;>?./"577#6754'#"?7+X$>  ;5%+#7=F \'3>)7;%# !! #=;$ !!  =;  #! -4yJyF@!+*<:K&PX@.bb  QM=M >@,bbU  QM >Y@ FD#&)!)$% +&54?!"747!"'3!672!2?673#3267632#"qR75B9a53-D% 1DBC++1A/%=) ;5  D$O=th "Pu:  4@L.Y !fNu=/ (-!1DH <K@H! <#:9UM=M ><;#:%'$$ +%4654#"326'67#"?47!"'3!2367632#)\-PFRqX'>3JD9)F3)/X=^i+K ;#K#'=kwu!k3 2  37/H16Xi3#7@3 )<2:KPX@;  bb`ZUM=N>@<  bb`bUM=N>Y@ 1/#!&&#$$ +2#"&546323267654&#"'67#"&#""'>32727㢏jm##F'%9}+\^`FI&o3HC!'x45:;dFwR568)-<7uo F)^-E^A 7{ EK1PX@& 94 <769@& 94 <769YK!PX@6b`  `M= N=M>K1PX@4b`  `  VM=M>@8b`  `  V=M=M>YY@ DB/1")&! +&'"326767674'&'"!"#733!2&'#"&547>32HNJaA`O '/m9B1+1+Z%C+yA7 \%/Tw`VmI =WZ3T7/T/;/ { '/L=i1@bUK >Y@8530" ;: +7>75>7654&#"#"&547>3232&#7635! P\mjVFCLX) XgfVXB% {P ;%1XPd\q;#Hf\> N#  {tg/PxPLoI) )-+w<OK!PX@bM=K >@bUK >Y(&-22<+32&#76;>7654&547>32#"'&4654'&#"XX P?' Q %%) G #w A!%/`L3Z7BT#3- '+-!%5#)Va'1}g-)1 !OJ3[b}+=TK&PX@bK =M>@!bUIMAY)&-22<+674.547674'#"?37+#"&547>327276VY G B% T '%(?  #u @#%3_L3\BT/';%'+-!%/Vb!7}f!)1!RJ3\P#Tw '@$ <M=M>$'&$+67632.#"3267#"&54^o+B3!BXhw1ܮ1F?kRpV`)Ѹ=B 08@5UM =M>-+%#   +"&547>32"326'4&!"547!2D)2 R-#5 Pdj/˨5>pJHF!-3#-C3%-Bwo\B whzHf/VBs >K-PX@'4 <@'4 @'ZUM=N  >Y@ >;-)&#  $% +3267654" 3267674&#'76;>7654#"5?732+'TwbTX7:}P8 3%+/ ^bq+=Ula`Bm{ \q?X@1  -aK!9z]?yAW@ 2,& @UQM>Y(#6,.!+%!"'&54767&'476762.# 6732#"'&'!267>7654S9#ɏpw#  #$CuN)jL'\^ &TQ%qP%E P3P# 81wsLPg#?;6G#/< \%+ 9-q FI%w/382uTHKPX@>b  UU = M = M = M  >K$PX@Ab   b  UU M = M = M  >@?b   b  U  SU M = M  >YY@ FEDBA?9720.+)&  HH +2#"&#"#7654&#"326?654+"?37#'"&5476323273> /B36/J7X`%}5%;b= 3 )㦼 1L= )! (55N# VRѢ7>B1j) +;9 +>'@BsVN@K$ O9<  SK= K  >TQNLIFBA>;#7$#5$#4+654&+"5?7#"!7674&+"?7#";2''76;267!;2''76;>7}'5   )5'33 '+   45' )5    +7#B\@'+   57%# -+ #=) -- #?) ++ !>1 +- #</BUE@BQI-" <b`UQ>FD>=53*)$#!' +%#"'&54654656%657>76=&"72>7673>67632"'&'432767467!q%@-\ !N+%%=/B#;9-H2!')% #%R>P@%#1 ^Fh@s d# @  -!! +RqDS{T +bK@5!   @:  bZ  UU M  =M=>Y@IG@>864320!##4 +2"767"'"5463232?5.'.'.#""'632327>32H+@ m0IRfdD%57k/% F    'Dfi=Y4?J TR?'9 1 )R>?GG+<^ ,7@ c`s*#!L C+8/`q$jK/PX@%bXK=L >@#bXUL >Y@!  $# +7>7654+"?7#"32>73!7635@; Z ;1% %@-/R! 5%5L)+'''5y9'D'!o F@= 0KPX@2b M =M=M=K>@0b  UM=M=K>YY@ CA'$229$%' +"327#".'&#"32&#76;>7#"'&547632>7672''B{fF1+)b!2%% R #7# T`K31)wL?XibNdy?̤JHX=d+?%!)3L^P5R!+-)5oB\f-H3?;P8`K@?D @'bUS  K >Y@HEC@<;76+)#! KJ +7>75#&546?37>7654&#"#"&547>323#32&#7635! ;\mjVFCLX) XgfV ;B% {P ;%1  Pd\q;#Hf\> N#  {tg/PxPLoI2) )-+wKiK!PX@( bS M=K >@& b  USK >Y@ IG&)224 +3#32&#76;>?#&546?3654&547>32#"'&4654'&#"XX <?' Q %%) ;  #w A!%/`L3Z7BT#3 - '+-!% "Va'1}g-)1 !OJ3[KY)@0F' MDC: K(PX@Eb  Z U = = M= N = N>@G  b `  Z U = M= N = N>YY@"YWPNA?=;6521,+&$ KK +!6720!27!"547#7#"&54767632654#&5727673727!"'&#"3D" /Fuh1Bm-1%w5yHL )D?U+G.T7a53-0{+{^X'qPVh8@ "H;{7H/;3oVPu:  TygbP;y5N dH@8 `T aD K*PX@Vb`  b `   b=M= M=M= N  >@Sb`  b `   b R=M= M=M>YY@dc_]WUSQKIGEA?3'"'"+%&#"37#"&54767632654#&?2767333!2#"&=6323267654&#"!"727#y'}\X)oPT}5yHL )D=U/F-u- %{@Sb`  Z U=M= M = N = N>Y@ pnge b a[YVUPMCA?='"'$#+%654#"326'67#"7567#7#"&54767632654#&?27673727!"'3!2367632#%&#"3PZ-PFRq^!<'TBo/1%}5yHL )D=U/F-!!X7+E5)/\<\k+Ky'}\X)oPT ;#K#'=\b ";H{7H3;3oHm1 2  37-H16Xiyg`HC{DO@L*< :bM=M=M >20,+)'DC +"547#7276?3#!267654.'&547>32.#"#P{ s3% }G{DTD }U+)K(PX@=b bM= M= M= M  >@:b b  QM= M= M>YY@"@@@K@JFD:81/,*$" ?? +22>?#"547#726?;7>7672#"&'&#" "&=>27#19R!w{ fd }G!)`F^V^`/>Z!(_CXL?i/)y-JT ?/js{%-kPl')/?J- (N{/=B3(P-=7 3j Zm@jU6<& :9   b  U M =M=M=M>XVOMFD><$#%*$"# +054#"3267#"''67&'"547#726?32'#27&547632#".'4.#"632?^)J?}'%w#)o7P+y{ fd }G/y-JT/4\C%##1)}qf '\T{ 'B%H{%-kPl') )P-=7?W;HZN %3#!%-̓D1@?=?b{@ BuKPX@Q  b  bM= = M =M =K = N  >K(PX@N  b  b  RM= = M =M =K >K1PX@P  b  `  b  RM= M =M =K >@M  b  `  b  RM= M =M =K >YYYY@yvtolica[YWUNLECA@?=%'%2S44+4654+!2"&#"7637>7#737>32#"&=4&#"!273632#"&547>32327674#"2"&#"7437>7'A!   %3#  %}}ADR:++?1%LZV!yXT--ΰ`3mZ3h%5D3hsqu%!   %3# %p-)+) #;P%h'DTI '-7'R\#X?+ô3Vd0)$RB/) )''5+Y@ /T K$PX@Abb  `= =M= L = N  >@Cb`b  `=M= L = N  >YY@YVSMKI$-& +7654#"?27673?3267654'.547>32.#"#"'&#"&#'76;2 '/  #nB3-++1#N5DhT}H1);-Ft:I=DP#[ri1dR'/b/Z )'; 3- 'Z+'REdO1ID!!h#Nj+JP)0$%=H-Hj3'++C@>  <; K&PX@9  b ` = M =L = L >@7  b `   U =L = L >YY@9742.-*)'&$# CC +!672!27!#&#'76;27654#"?27673307!"'D" 1Fuh1 P/Z '/  #nB3-H 7`54-h :@ 'Z )'; 37 Pu:  ) J@J-8x` iS @7  b e  U G M  AY@%|zwtqndb_\YWOLIFB@$##3:#3(1++"'.#"?37#"676&+"?37#"#"' #"'.#"&?37+"'.#"?37#"676&+"?37#"#"' #"'.#"&?377 #7  M'q 5R+T2+5!F71 #7  M'q 5R*U2+5!F71  /) ##  4  ## Jg/*#) /) ##  3  ## Jg/*#o/4@1beSGK?+#!#!#!#!u:F C  b  b ` M =M= M =>@=  b  b `c M =M= M  >Y@B@9753-+$#5# +327654+"5?307#"372"##"54754#"#".547>32 sbwH' P 'Z )D #oB3-ͪ'D+)7 9XJVL5 7''ZT1!7 3YL)NLR/5ZZ\DLZ@WF @<bbM=  K =M=M>KIEB5''"&%'"# +%72762#"54?#"54754#"#".547>32327654+"5?37#"5//1 93ͪ%N)7 ?6'"2&'76?>76&#'"&?672#qR'3 N0 0f1_9 R5}$c *|@Ph%% ] %% !A$% 5GL@IA(<bUU I K ?FC@=4$334%$$% +>32#".'&#"632;2&#'76;276&#";2&#'76;26\8C8RA -7 jj=8< /\+o/ 9 !&`+B / ^(o;Ec-2$ 'P&6ZXYQ/ ## />A,$/ ## {U +@ &K!PX@.b`XUINB@/b``UINBYY@)'%#     +2"&7>6&#'"&57672#"&76323267$*4)_ +|? =/38=-H +&,:""""{A$% 77Յq-3&3;u/e@ (/@$bUIK?Y@ 5#26"!+632#"'&'"2&#76?>76&#'"&57672kGL ;" A5% \; %#8 +|? y@5 H $$ A$% 17 )q@+bbUIMAY@ ""!$2"3 +6&+"?7+"327#7##"763232764$  %#B .~>kJL ;  @" '' !7)yy@7 H 2KPX@3bbb UQM>@9bbb UUIMAY@ 0-"6""!$"" +327672#"'.7##"7632327676&+"?7+" , %X-=#kJL ;  @4$  %#-w;(3/ry@7 H" '' !"+4@ 1@,U UU I MAY@420.&#! ++ +"&?37#"37632"#!76372676&6&+37G/i `IC\85I0/ MK  '=F.'#!w%aK4VN#o .G} J?@323267676'.#"&5?37+5I U-Y823a>7.  E%  UI'  1  #! LPX%S=4[# ZF%KPX@ e>@ d[Y)& +#"'&7676703/   %(  +\u?K1PX@IMA@bIMAY&&$+#"&767632"&76762 #% ( % /  ++3#@ :IMA  +"&7>732z71 d>W 88Z;B # W)+-!*7@9M >  +2'>76#"&7>71 d>W 887Z;B # W)+-!*'FI@  @eM >Y@   +2#".67>3%^ /94K! WF)V%*V #%3<2:\Jy5K@@QM >Y#$#"+#"'732676&#"'632yb#KV?C&0hi5p{ TCPL\T >J5K@@QM >Y#$#"+>32&#"327#"&>a% KX@C&0gaJqz TCPL\T } @bcM>"&"+>32#767>&#"#"&} y\GN TV P^J(i ! 3NUF:7:=9HBA4T6\x@KPX@ZcM>@bcM>Y'#"+#"76&#"#76'&7>32#"%>(5 6D P =? pG\Y (66*+3BAH9<;6;GTOW+"+7\V\m"+"+%771:]\V\v3@<e > + #3# \V\nN3@<e > + 3#38\~V\9nG3OX @9 >  +&''67-l1{8m#TXmkN]\cp%vJ @:[  +&'767 4e/{8]!^%~kNOjq5N-@ d[Y&% +#"'676763  "  *!x}@ "z3iN @IMA   +!"7>3!2r %&N()Db -@ d[Y$! +632#"&7673 ?(/ /' $` %K*PX@ e>@ d[Y$% +#"/7632q[ # HG)/- @ <d>#! +#"'632!#E!"- R gYN @IMA   +!"7>3!2\$%()?i` %KPX@ d>@ d[Y&! +632#"/EM &T  Ju KPX>[Y   +"767% 8!J,.G% s YL=$@!<GK? +!7]=D;@ 9[ +!;/ +@(<UM>#$#"+6&#"'632#"'7326 46 !'PXO 3S1H@ nOMm@DF9O@@UIMAY#$#"+>32&#"327#"&N(%3U 73 MYMo=H13F?l-myE @dL>Y@ " +&767376723 .----y$@! <S>" +##"'7#&767 . -=r@ KPX@dT>@deGL@YY"!+#"'7#&767376323#!  T }Z @GK?  +!&767  @;ih @ :IMA% +267#"&7|e 3+`luhRfbV~,K1PX@ M>@IMAY +>2"GP.FPP::P;< *@'UIMA    +"2676&>2#".F *[G ,V}IGC0.DE-1BdcGEeOf?(@%< :dIMA*! +"&7>732>76qSR~ng7$"IOPU"XoB`X\6@3<: 9QM >  +267#"'&#"'>32$7".J3FA$'70)`@AG3,> 5-.4 ]Y9-\u?K1PX@IMA@bIMAY&&$+#"&767632"&76762 #% ( % /  ++J@ < :d[$( +67327#"&767#21FYZ'$ \ Ta"$A#N#)9):?2CF"+'&'.'7'6767%ɓ  Е    3xw,7c@ 6(@UIMAY@1/%$" ,, +"&76?&#&'?327"?6&#?37"'32676/7: /X_ B=V< <E% E+e 89\k*!X1+"=f+'- -- I%Rm (&Dk  @dd[;2 +6&#'"&?672&'76?>7 *|@h N3}%5A$% 5V %% !-@ ,KPX@"bUQK>@(bUIUMAYY@ !-")#+332676&'.>32.#"#"&#"6- "$8)B%@L>M-a 0B()>1/.: \[*![K*%''%="C~Y O<*($&-8%:KGdXN@K+I3X@B@?+<bUIMA1/&%! >> +32&'07673>?6.'.67>32"&7>5676&#" P2 !!   }`Xy-*:/5F   %%  .! "'NYZK  &0H2,E) ;0(+)8K&PX@K = >@S >Y@  + #!7)s)AK&PX@S = >@dS >Y@  + #!7!7C)'s)AK&PX@S = >@dS >Y@  + #!7!Ds)s3R)AK&PX@S = >@dS >Y@  + #7!7!D)׍s)5):K&PX@ =K >@dK >Y@  + !7!;C)s#@ dGL@ +73!{hhZ\|h9#@ deGL@+7#3!!jk?>h^O@<d[ +33#Omu\@u!@SGK?+!7!7!7!/'/XZ1x11@. 9IMA-*11 +2&7>7>.#"&7>%2&7>764.#"&7>93 :JrT]  7%Co94 ;JrT]   7%CZ;M:I O) 4 7Z;L;I O) 3!60@<d> +3#3L3`Lu0G@<d> +#3#M4\Mu."+%7%5H |L9M."+%77A {M7L fq *@'UIMA    +"2676&>2#".F +[G ,W}JG}C0.DE-1BdcGEeV5@ d[Y@   +2#"/&7>A"E 4V /VyK(PX@ d[@dd[YYY@  +2#"/&7>%2#"/&7>6%f 4;"E 4J -  /VK(PX@IMA@bIMAYYY@  +2#"&7676'2#"&7676"# ,!  J-  / %U=@:<: 9UIMA  +267#"'&#"'>32$7!/J3EA$'7/)`@AG3-> 5-.4 ]Z:-"i@M=M>+>2">2"MY3MYMY3MYZ@@Z@&Y@@Y@ @eK>" +73##F"<7V [ "@ <eK>! +'"'#&7673D;+7# Hw C@  @dGL@Y! +6323#'<, L 7KPX@dK >@dGK?Y" +#&7673672F"; " H@deL>Y@  +%!#.'76734 @FJYmD."&+=W=3B5%6 "2`E %K*PX@ e>@ d[Y$% +#"/7632[ # HG )/bv %K*PX@ e>@ d[Y$! +632#"&767 ?(/ /' $o @9>  +&''679-l1{8m#TmkM][c\6@3<: 9QM >  +267#"'&#"'>32U$7".J3FA$'70)`@AG3,> 5-.4 ]Y9-N[ @IMA   +!"7>3!2 $&N()) 5K&PX@ M >@IMAY@    +!"7>3!2G% $( );h @ :IMA% +267#"&7e 2+`lvhRfbV~-s7@M > +462";N<32y L@9@# 1$m=J7+@3FJL("$  #%OPN *@'UIMA    +"2676&>2#"M.F +[G ,~V~IGC0.DE-1BdcGEeu?K1PX@IMA@bIMAY&&$+#"&767632"&76762 # $ ( % /  ++pDwh @:[  +&'7674e/{8]"^DkMOiptb!-@ d[Y&% +"'676762! %  *bk= $ck7TP3KPX@ K>@GK?Y+#3#3{[KZ\J\Pqou%2#"/&7>%f 4;"E 4 -  .;'*@' <UIMA%+>2"'267#"&7AM*AMf!2+aluqM88M6-RfbV~?) 4 9K&PX@ M >@IMAY% +4&"'>32f!2+aluRfbV~3"@:IMA +"&76767>3293 :JrT]  7%C[;M:I O) 3!77@9M > +2&7>764.#"&7>f93 :JrT]  7&C7Z;M:I N* 4 7B!@9M >  +2#"&"&'&7>1,98  =L l,# YB7!3 )O I;L;[;9KPX@ M >@IMAY@  +2&7>764.#"&7>P94 ;JrT^  7%B[;L;I O) 3!7?i` %KPX@ d>@ d[Y&! +632#"/EM &T 2Ju KPX>[Y   +"767%I8!J,.G% s &@#<dS>"!+#"'7#&76737632H  +  kF@ @dT>Y"!+6323##"'B    dO I@ 76&'.7>32 qT1 2+71%;+95RO[5'6"%+j9 B&@#<UM>!$""+3263#"&7>32"&#" 0A  QYU ?I3@PqRTsTB-eyE @dL>Y@ " +&767376723 / ,~-%-y$@! <S>" +##"'7#&767 . - L@ @dT>Y"!+#"'7#&767376323# " -  @GK?  +!&767  *@'ddM>  +2?0303#"&7>32u`--)'7bYVr $!*dtn@ %!9#'@$ddN>  +".7;267>32c=< 6'),-a%@1 @lG : & Bl@IMA +>2"L^3L^_??_?N@IMA+>2"$>2"fL^3L^7L^4L^_??_??_??_?`f *@'UIMA    +"2676&>2#"*.F +[G ,W}JG}C0.DE-1BdcGEeA1J@9[ +2&7>76&'&7>:1b J` SDT`PWe!?A%3fl@732>76qSR~o f7$"IOPU"XoB`\ @ <d>#! +#"'632!#E!"+ R TLH3!2#"'7! $/{%$m 3jK@  @dIMAY""&&$+#".'#"&'>732763232! 4v4$6a 3>- JTT  ROy G3^H7==b (:KPX>[Y  +&'767*n.rB]"]=f`XOiq3X @9[  +&''67%t14c#b[pITeq)V @ :M>% +267#"&7e 3+`luRfbV~1^ @ 9IMA% +4&"'>32}e 3+`lu1RfaW~l=@:<: 9UIMA  +267#"'&#"'>2$7!/J3EA%'7/)_F4-> 5-.4 ]Z:-N @IMA   +!"7>3!2]$%()_pN @IMA   +!"7>3!2=$%()Pb /@,UIMA     +!"7>3!2!"7>3!23$%83#$'*')^#=@:<: 9UIMA  +267"'&'"'>2$7!/J3EA%'7/)_F4-> 5-/4 ]Z:- @IMA   +!"7>3!2u\#%() @IMA   +!"7>3!2B>$%()aW@K!PX@ e>@ d[YY +2"&7"1=/T\D@< => +"&52*"2")& &@#<UM>!$""+6&#"#7672#"'72326u 0B  QXU ?I3@OqRTsTBTLH +37%!!b**tEBi3jI@  @eIMAY""&&$+&'>32>32&#"#"'&#"! 4w4%6a 3>- KTS  RO9y G3^G# "+''7'77xEw/yGw.R99::TN"+6&'7.676 3<3!+NB?1$'p)@;$ '=hB0] /@,UIMA     +!"7>3!2!"7>3!2X=#$9>$%'*')2`E %K*PX@ e>@ d[Y$% +#"/7632[ # HG )/bv %K*PX@ e>@ d[Y$! +632#"&767 ?(/ /' $=@:<: 9UIMA  +267"'&#"'>2$7!.J3FA$'7/)_F4b-> 5-.4 ]Z:-7@9M > +2&7>764.#"&7>f93 :JrT]  7&C7Z;M:I N* 4 7^;J2@/dbINB+>2"$>2"2"&7676VCM,CMCM,CMq .sM88M88M88M8*  %#[@ :M>%$ +73267#"&7r+!B:0'w5ORk=,^LSUH3!2#"'7!( %.{$$ >Ptb /@,UIMA     +!"7>3!2!"7>3!2>$%8=#$'*')|3 6@  @ d[Y&$+#"'672#"'672  "E!"  #E!#D R   R  z I@ @eUV >Y@ $#%#!+"#"'7&#"'>327632267#"' 6:#'7.)_@834<$G8!.J"%t(.4 ]Y"o,>  h#}@<;KPX@%UQM=M >@#UUQM>Y@! ## +"&>2"&>2267#"'&#"'>27 08 7 17 ;$7!.J3EA%'7/)_F4(8((80(8((8-> 5-.4 ]Z:-<^1( K(PX@(  U UQM  >@.  U  U UIMAYY@"/-,*%#!11  +2#"&#"#"&767232>72'"&#"#"'&?63232> O[! 8! U^ (@!* QY!"!8  V](@! mH&& hD## mH%% jE#$w;s lK!PX@dT>@deGL@YY+673!.'3#>7!#.'{mD."&& FLg}zH*>d@FJY$5!"5= ~( +77673.'1>F">%Y*+1)2+ FgnrcH !2kt"+%77 {L7L8N*@'<QM>#$#"+>32&#"327"&O 3P 24 ")O[Mm?F32G=o*@' <UIMA%+"&>24&"'>32AL+ALe 3+altM88M6-RfbV. "+''7'77y=w)y>w(2233_.h"+%7%6H |L9M<.E"+%77 {L7LF. $@!<:9d> +%77%#3# +L3\L{M6Lu7'a*@'<QM>#$#"+#"'72676&#"'632aO(%4O 33 N[Nn>H13F@nY@M> +>2"{MV2MVV??V@7}q7@ -<";KPX@"XYINBKPX@!XeINB@ deINBYY@ &'"''"!! +"&#"#"76326&7>32>32#"#".'#"&7> / &7!,E+ L3 .   #8-%3C M 3'-!AC1J (\@UIMAY@$#(("$"$$ +2676&#">32632#"'#""2676& +[G ,-.FHV)DUHV}JU)DVG.F *[G ,-FE.1ABvdBBcGEeBBB0-FE.1Au m@ KPX@eV>@deINBYY6%6"+"&?656+"76?632;2f ]! #Y     w @ :M>  + 732>7c*1gލvV7%5LI&$NC;%)%@" :IMA  + 732>7*1fލvV7%5LI&$ND:%= @IMA   +"7>3!2#-+=#/#.R @IMA   +"7>3!2#-,"/#-@XjN@ < :9KPX@QM >@UIMAY"'""+6$3232$7#"'&#"@EՅǘyV9D8¤_`hADDE Mj.DBSC @9M>  +2.#"'6$#`Z+*b⌎|G%;[[23bS;%;dK!PX@dT>@deGL@YY+!.'3#>7!'FLh}{G)=P5= ~( 54/-)'! 8 8# +32>7"5 #"&76?6&#"#"&7>3227Ed %&+    $'v7- U'1:   4+L+  ,"B=   09FL?avL<@9<SQM >  +27"'&7>32#76#"7,9JF' dAH) !a1' ?';2!O:^X>;"!% +&#"&72>?6&'&7673&"&>2" +#*   @)% %V   G%%7F $@!QM >    +2"&>6&#"32A8> a;^j";L8F8F $%$#5"+726?6&+"5?7#7'#"?6&'#"&5757p%/ :  1)1*ER)#?F 8'g *L4@1<bQM >  +2#"&76#"32>7#"&>@5$!&;  " >FC9gL%'-C:%+ BVe (\@#($@dQM >Y%"#"+7&#"323#"'&7>327&6.'7673#7y!<  .$6 W<1 @)B,A$ Z%mK2#%NKPX@( bbc =M >@%  ddbcM >YY@ :92':" +672&#7637>?6&#"2&#7637>?6#'"57672>-+3 5"J 5  9%F  2 T% dR956  )4  9FNHVn@)$@ bcM >Y@RQNLCA97,*(&#"'# +76&#"&#"&72>?6&'&7673672672&#"&76?6&#"&#"&727> .# +#* @* :(= >/'( ("+"' *# +) Zw+-   459865  w+&  L-6@3<9beM >,*'%#! +7#6326'&?67623276&#"*?  *#+ " %,6:    ##<8@5<:QM >$#$"+27#"&?#"57672?32#} #,<(/=AK?'"" M huB-N@ " @eK >Y@ %#3(34+'.#&5?7+"7676&#"?7#"&. 7P "Z '7  ! SHGz@<'B/<  :GC409K PX@ b WM >@ b cM >Y@FD@>86$'##$"$%! +632767'&#"&?27#"76#"?327#"32&'"76326'&'2&'"X  TB  sD ')R? ,!;"@,*)Pu?J Z` 7?qN% @ 9[   +2'6) #)E%'+'Js @ 9[   +%2'6D&'+D')+Hu^4 :KPX@ M>@IMAY$& +7727#"547s' -730+y4k7%Dh^LtH!'@$<M=M>($)#+?32767654'.#"'632'"&H;-odmOq/#'h3dwP/k9 7hN?AZяHT2#98MF//}{X"2;@8"<UM=M>$#,*#2$2*$)!+%#"'&747>7632.#"7267'"&547>32\Bm@U=+^e3)BC)3kfR-1 V1'8 WzVuHXL3N7:"9TAb4B=P4)/H7' 1FH!1;@8<UM=M>#"+)"1#1($)#+?32767654'.#"'632'"&"&547>32H;-odmOq/#'h3dwP/k9 7h-3 X1%8 VN?AZяHT2#98MF//}{X5'/G5)1FPB(,@) < 9UM>'(.+62'>76'#"&5476>32#"&546b7bN`!'1oL-'5 J-'5LB!Xy)S%1'+P-@4' -@4' `b@<d[  +2"547476Z' %   %#7F2@/dbINB+462"$462"2#"54767N77N7N77N!5 k oM88M88M88M8+ %#3:LZ@W? H6 '!<   d   b   V =K ><;54EC;L32u-1 X1'7 V4'/J7' /Fu?O@D <  */@C  d Z  `bX   UU=L >Y@A@JH@OAO?=5234$'4"4+4&#!"!26?6;2#"=.#!3!2!'74;27654+"577%2#"5476u7PJL N!13 -) /%2\?c5o>h\> (  +NAK+/>&5@/+ / +\ B +) #%#Wh@]=&R@5d  b  U  SN >Y@%YXcaXhYhWTQPOMJGB?<;:852.-3#53#4+%!;2�'76;27654+"?30%+"!654+"?30%+";2�'76;22#"5476ZbZb6P7oc7P6qKLb5Q5s b5P5q+  B''ZB''ZOB''Z B'')%#)9|@.$<9K(PX@' d  bM =N >@% d  b UN >Y@+*42*9+93#53#4 +;2�'76;27654+"?30%+"%2#"5476b5R8ob8P 5qe( B''Z B'') #% 0;@8dbM =M>"!+)!0"0  &" +!"547!2"32654&%2#"54765Z 56h͸%۰#/') zDE/=wd<B ^T' %#RGW@L'    B<,;K(PX@(   d  bM =K >@&   d  b UK >Y@IHRPHWIW33735,345 +%&'&+"?327+"067054'#&?327+";2&#'76;22#"5476K,-A <5 X/> 1?u+-;%jOb5R8o+  YB '% + '%+3; B'')  %#6F@; '@- d  b`M =N >Y@87A?7F8F($)(!! +%32673!"7$654&#"#!73;7.5476! 2#"7476RH59V!@)9 % ^d"M5:M ) !)  w)R!I=:G:qN! 9#w37/cB/'  %#B"*9a@^<; d  bbV= N>,+42+9,9*)&%"! +%2672#"547654'7270462"$462"2#"5476qN%# T!do7+j~8M88M7N77N!5 j q1+#C1`>;942/+('   +!#2&#"'763>76322&#"'7637654'!"uVTJ7q#H^5+%8;Yq9d3g9D"^1''>X)J!;6''= [#!- Fp@*98F@UUM >Y@ E?2,)'  (! +3 7654&" 3267674&#637>7654&/"5?3263 !"&#"'RR/Kfg@Uϲ @ZUK >Y@'$ -* +7!.#!";2&#'74;27654&+"?2 ?7P^J e5J7m)5>Zy)% {DD%%Z!%+53@0  < =N > +3#!"'&5754773!' s5 m1J@*2K(PX@9 ZbZU N  ==L >@7 ZbZ   UU=L >YY@JIHD(3T4&&#"5 +.+"32?6;#"?654&+3!2&#!"'7637>7654&/"?3!272m9DgHRX+"  +'.d= z;r^ZB<93 (/9 ?// + yfFRV4{>XZ4 " . ;))+ )TcD)T@ ('@U =N >Y7"!7@+&#!"547676#!"'673!>327!267923!;dm?:5:I4v!T )FP1f<;PTs $O  '7-O?K(PX@T = >@dT >YOHOG+6&/&67327&#"&6?>7!&#"&6?>76&/&67327!';FP=?=8 );FQ=?=:cc );FP=@=9 &)(DB<:1/(7)7 '& +26?6;#"?654&#!"+6?33"32654&!"547!25./ -'+7, - -#1ɚ9Ҝ5p;a; 1 Z9 /jF+ZQ> n_ JQ/J'-%'K(PX@ = >@ K >YOG +6&/&67327&#"&6?>7&@UV >Y@ RNED<8CG+6&/&6732767654/"?327#&#"'7654'&#"&6?>7&334$334+%;2&#'76;267632;2&#'76;254''-=%P=3}  +9P7o}#\+ ''%6/11*''@۲!-OK-PX@3(IB<@3(IBK-PX@0b  `  ` U L >@4b  `  ` U L  =>YY@NMHD@?21/-C"+%#'"'#2&#"'763767654&/"&5?327!2&#"'76?>={s61sA7w6J!$7 ' m5R }=6 %5%-';''q)) PE''HN-AV@ < @U =N>YK"K"+#"'&"&#"'76?67.'&?!327654/&?327'/)X3)4j>-? W1*7ju?1Dd1R'%q-[ 'sq>S''y3'?S/ KPX@MZ   bU K  =K = K  = L >@Nb   bU K  =K = K  = L >YY@*SRQNMLKJHEA@>=<987320-)(  '& +26?673#"?654&#!"#6?23#654&#!"#7>733!2733!2673#&#!"#5- / -'+27,- -%/T75Z^m=7!4 753BmE!7L9 XC1\:/mC @1!B3}II +BL)BD'@$M =M>  +"32654&!"547!2}9Ҝ5p;a; ZQ> n_ JQ/J#P+Hn@ 0%C@ ZU K  >Y@HEB?52AB53364 +%654#!";2&#'74;27654+"5723!0!2$3+;2&#'76;2%=-# b5P6qZ>;jb5P5q3%5 B''Z B)'X B''!; @|@*:@&IUM = K >Y@ ?>C6*!3($" +3 674#"654&/"?32632#"'2&#"'7637>Td#f'= bH,L k+lM!HzV@"ZZUK >Y@ #'+&'&'&#!!276767!727 -%%%))N.[ +$7D+3J/  2XL-''!^1?D1p@-<:K&PX@!b`M = >@b`U >Y@,+)& 11 +!2>7#.+"&#"&6?>76&+""'67!3=$ELo5/ ';FP=@=7 @bK >Y@ 73+*"G +%&#"&6?67654'./.732736&/&67327 );FP=?qH!)3sb;D1  C%B-w615' /)-' Uv"T3+-#)-/-.7;-=EO@J!K<79K PX@%Z`K =K >K(PX@&b`K =K >@$b`UK >YY@ 33>33?+%.547>?654+"?3%+";2&#'76;2?674%>\% R b5 P7o`7Q5q< ׆sV+Lsh< >B\ N B''ZNǐVB''Z)/q7#$ Pwi-l@&%\?8@)  b  `U K  >Y@`^[WLKBAC#C&'"B +?3273#327654/"?327#2&#"'7637>54'&"&#"57636767654'&'& /7 B  C+%9#!7\3+!Hj /= )% &0%  %/wyX;Y''+f0)))/.%$R )) /. ))}--Yv@JQ<$<9K(PX@!bK =L >@bUL >Y@OLIFC@52"YY +32;2&#'76;27.547674.'&54;654+"?3%+">76769+#7) lR1c7P5q/ !-@ {c5P 7o{^N!!R/ 1cI{B B''Z9FP=;J  5DPG- B''ZEq/B'dJ9c@ (@ bM =N >Y@ *%)(!!+%32673!"7$654&#"#!73;7.547>$3 !RJ59V% @)9 % \f%P5'uN!GG @UK >YOK&''"+>32#"&546%>32#"&546&/&67327&#"&6?>75B'!+@'!-@'!+@'!-&@bUK >Y@ YUMLD@K&''"+>32#"&546%>32#"&54&#"&6?67654'./.732736&/&673275B'!+@'!-@'!+@'#+ );FP=?qH!)3sb;D1  C%B-w615' '5)! '6+! %7+'8+# /)-' Uv"T3+-#)-/-.7;u'6I@E 0! K1PX@4  d  d= M=N=M>@8  d  d== M=N=M>YY@87)(B@7I8I.,(6)6'%%$ +03267#"'57# 47>322263"32754654&2#"747476\D%1#/7c=hjczFdT>; Ս׃}17& w-bUPH]q5# 5BT+1%   %#q0CKPX@?  <KPX@?  <K(PX@?  <@?  KPX@;  bb IU  =M=M>KPX@6  bb U  =M=M>K(PX@3  d  db UM=M>K-PX@8  d  db IUM=M>@9  d  db UUM=M>YYYYY@ 21<:1C2C/-+)&%"  00 +"327#"&5467&'4632#".#"7>32#"&2#"747476hsKFowofàb3%!--Nww0-BPH& TBO\'{oT\+/ZmL5%93>3dj5 %   %#s5Hh@D.<9KPX@b=M=>@ddM=>Y@76A?6H7H),'/+'66'4&#" #"547654/&7632>322#"547476nj'3L"%\N Xt%R1* V& D9#Z!E%V`9`+->!(7lV%!+7dj.%   %#w-@)<;K!PX@'bb==N>@$ddb=N>Y@&$-- +%2672#"547654'72702#"547476qN%# T!do7+jC& q1+#C1`>KPX@- d  bV==N>K(PX@) d  bV=N>@- d  bV==N>YYY@=54.54632# 74462"$462"2#"7476G%R-* @BS\^!)!6yj8M88M7N77N!6 j \5V%!+8bv6JJf5D'+'ĚQ[N88N77N88N7+ %#'6@ 0! @-== M=M=M>Y@)(.,(6)6'%%$ +03267#"'57# 47>322263"32754654&\D%1#/7c=hjczFdT>; Ս׃}17w-bUPH]q5# 5BT+5/e@* <9K/PX@M=M=M>@UM=M>Y(+#%#%+>54&#" 3254''"54632'632#"=HAV0q?$;)1%3# NCBhټT kLbtG1'P?%)}sguHNcm-F@C*)%-"<b==M>(& +205#"54?.#"7>267327yNf -I2TaPH $+R5F R!7%= fMoCi3D\eb3{k- =<f@hm#*74@13<bM=M>,++7,7-#%,+4>7'.54632#".#"#"&2>54/Rsb-REq^y7.+;?/;JDRnLuHRN+Z/Vfo7Zy<`LF'5>I>92)dTm\˲skn7d6oN0bKPX@  <KPX@  <K(PX@  <@  KPX@-b IUM=M>K(PX@(b UM=M>K-PX@-b IUM=M>@.b UUM=M>YYYY@/-+)&%"  00 +"327#"&5467&'4632#".#"7>32#"&hsKFowofàb3%!--Nww0-BPTBO\'{oT\+/ZmL5%93>3dj5 +/5@ @2bbV=M =N>Y@ )"$$#"$" +#"&5463232654# #"&=67332763232ݍLZ/#H HJy;\>P^1N:$Hk=9jj`bP1%35Zs s#k^1!=.=F-)-953NXi5%@".<9M=>),'/+'66'4&#" #"547654/&7632>32nj'3L"%\N Xt%R1* VD9#Z!E%V`9`+->!(7lV%!+7dj. $2@/SM=M>! $ +654&#"!3232#"&54`'EDLm!)'DChPAd(-@h᲌E}Z{ˍ՞JȐ%5@2<:b=N> +%2672#"547654'7270qN%# T!do7+jq1+#C1`>A?%"'!##( +654/&76322?632#"&#"32672#"&'.#"#"747E%R1*&,9hdD%59wi  @N-1<  6 TV%!+H+;o, -%5d =!(7l+3@0 < 9M= =M>$#%"+%#"&'''#"'672327)o)F4E\9)ssHL{a " +B9{=W{XF4#){:99@6<84)%:9=N>75/-(&%#$ +327#"&547"''6>7>76=327726767327i%9#ds)19+/P b+-8\J0h%+-8%@u'Z4/F?%LbU NLF1sO8ȏQ!(@% <b=>.$#+.#"7>32>7654&547>32#o+R( @'=;-!" =<81'5$5L -L@ @Ab bVIU= M = N  >Y@LJGEDC@>%!#!*"$* +7467.5467#"&=67332763232632#"&#"32#"546232654# 5451RN5@5#L?:'e{/%9-<39 N\^lj}馃1EgC=xuL6-^jAVP=.98%+;/XK.11>+!>+^e`=#-;Fq@M=M>%'&"+327654'&#"4>3 '".J^5RX.Nd5?w{!No\{5\}}?Zx`êjF'K{ebo.6@3+ <M=M>.-+"#%#++327#"&5476# #"547>7>7#"'>3)H1%Z )XhhH=/VXψ10 /PZ-#V#%Co;8wm/7\!29%EX0=#mġj %4@1$< 9M=M>!%%.$ +#"''6>76322>54" IbH#-)HI[K3)RL3N/IIDX˽{8^?%u9@$yVT}6j+/KPX@-ZZM=M =N>KPX@.bZM=M =N>@/bbM=M =N>YY@ $"$&$#$+4>2#".#"32#"&5463232654&'.\jA-)?1Lq{J^3!HF=yyd{?/%L#-#ʣhMbV#^w1#35P?:o!&@#<M=M>'$#%+4>3!#!#"&"3254&]Ӂ-F1ў3`Z<hmFR;8ZΩSEgf)h~o-@* <M=M>###%"+%4#"'>3!+327#"&\jDI8)'P6+H1נ'Pmn?KPX@==N>K(PX@=N>@==N>YYY$**)+654/&763232>54.54632# 74G%R-* @BS\^!)!6yj\5V%!+8bv6JJf5D'+'ĚQ 4+@(4 <+*)9M= >.-& +%>54#"7>32'4$47>7`oV)i;X#p 3@"b=M=M>Y#'&"+.#"'>3237327#".//7 :D-3=5XF} '+/:uC'5# T2 s-%<è fwk9!-dg^!}:D@A0/<:! 9b===>42.,$#, +%7>?67327'6.54?654#"7>32fu<^m/'fGH='!7'> :' 5ދ!E^H{L(P;#d!ʳt 8//?% ^ 7 =< \74*@'%<0/:dN>&%-#$+#"&'#"54>7327&546323267654'773`oRw ^XP+oC2/H# DNy) $!B^9 B}gpy߬{/))ZHf%'8slѴ9.'/_&2U@R<;b  U=N>('.,'2(2" && +%2672#"547654'72702#"&546%2#"&546qN%# T!do7+j(!-H'#*Es!-F)!-Fq1+#C1`>KPX@! U==N>K(PX@ U=N>@! U==N>YYY@98-,?=8C9C31,7-7$**) +654/&763232>54.54632# 742#"&546%2#"&546G%R-* @BS\^!)!6yjC!-H'#*Es!-F)!-F\5V%!+8bv6JJf5D'+'ĚQ+!/@+#-@-!-@+!/@u2f.@ ddM=M>Y@! +) 2!2%'&"+327654'&#"4>3 '".2#"547476J^5RX.Nd5?w{!No\{5'  \}}?Zx`êjF'K{e^%   %#u+>۵:KPX@#b===N>KPX@b==N>K(PX@dd=N>@ dd==N>YYYY@-,75,>->$**)+654/&763232>54.54632# 742#"747476G%R-* @BS\^!)!6yj' \5V%!+8bv6JJf5D'+'ĚQf%   %#7y4Gp@C0/%@dddN>Y@65@>5G6G&%-#$+#"&'#"54>7327&546323267654'72#"74747673`oRw ^XP+oC2/H# DNy) $!B^9&  B}gpy߬{/))ZHf%'8slѴ9.'/_%   %#o 17@4<M=M=M> &$1 1$%)+>54&# 4$32#"&254&#"3aA1-)-#P^x}ݘHjRb  fb4VE\1/8 @NDѺɃoo|p)7BCJA^@[.-<b` U M=N>>=9731,*#!  +"3654&4>322&##"54?654#"7>;32>7"&'&9[1! >LmLDju7`1!3R9Ts/NBhJ2=P)9L<`FXhe\P)bTFO5L{!% ; (H.f=BT=D8T@ @bM = >YL##)$%+%654&#"&'6727632#".#"&#"&6?>V'v@TPjyBL {\q?J9!#>H7_ );FP=?=8Õ:jR`9aJyR6V;%+%>I_++-'*8K{@G@'db`M = >Y@:9DB9K:KL##)$% +%654&#"&'6727632#".#"&#"&6?>2#"747476V'v@TPjyBL {\q?J9!#>H7_ );FP=?=8& Õ:jR`9aJyR6V;%+%>I_++-'*^%   %#=`8JZs@B9@$b UM = >Y@ WU'')L##)$% +%654&#"&'6727632#".#"&#"&6?>>32#"&546%>32#"&54V'v@TPjyBL {\q?J9!#>H7_ );FP=?=8oB&!+?'!-@'!*?'#+Õ:jR`9aJyR6V;%+%>I_++-'*'5)! '5+! %7+'7-! (55@2%4<:9=M=>*)+'6.54>767$674&'56 >`$X~x.6<^m$N/9,Wjt<:=XZ?jqX+^?%h< BZLeͬv DkNAr[5Ru6o.X79@6('<bUN>&,#%#6%+3267674&#!"&'7!2#"&'#"54>73267&54632JF% NNu1͘92 w}Ru e@B=sHC4-!'ip!fDD\x@O/,)( G@(bUQM=>Y@ *$$%/**'# +674#"/7632676763272?6?#"'&54632326?"54767#"5476768Njf\Hs} !XJT7&$  +"32654&&547!2&#'6}9Ҝ58; 6 "El'ZQ> n_ JQ/JM$rj)%@"$<9QM>*'&"+327654'&#"4>3 &#'67.J^5RX.Nd5?w{!!F_ &Ab3\}}?Zx`êjF iS:KdJHA :KPX@(ZZURM >K!PX@.ZZUUINB@/ZbUUINBYY@ &!$*!&T+>32327#"&#"6#"547623267674.'&7&Mvq#9P4 KPX@3ZM=M=M  =N>@4bM=M=M  =N>YY@1/-+&$ 66 +"&5477632327#"&#"!2#"&=>323267654^XH}r)R)?)\N<# f4!{J\1HF+{ pFR5wNLB 7*K/>3Nw1# %5qb}-+6@ #K(PX@6 ZbbU L  =K >@4 Zbb   UUK >YY@657335$!$ +#054&#!!2#7654#!;2&#'76;267674&+"?9765Zb9FD@/Js!: _;1! #9 )y`5݁ 1))'3'-5@ <5:KPX@UM=>KPX@$ZU=M=>K(PX@UM=>@$ZU=M=>YYY@ !*,!$71++"6;27#"&#""'&'47>32327^9aL75RxD3'wEfB[  BbT+)P!)XTXL3 Vi hL+'?'F #HTR -R##D@A!<USIMA  ## +"&767!676&#"'672!327BG HC# H7-3ob?L@L- O-14{X@5^L/:1)%?ZHHT+;%-#'=bD^ @K(PX@eT >@deGL@Y+ '!7!h9ZLhLbR 7-0@-<,9bcM>&+6654'#676767&'�##&%2&S))///ZDtRuR-Vi+d3-sdysQ`yue;FwuZI;!X5@ @ d[Y$ +&'763 #676'4''%&''Ǔ#>+r!;++++}Pf}h\cNGL=FLDDAP@ 4+( @UM=>Y*'**'#+674#"/7632676763272?#"54767#"5476768Njf\Hs} !XJT7Nmf\@"eUM=M >Y@ )&&-(!+%32767674'&#"#"'32#4.#"'&54>763279c`om, 1ymZ`-5/Ƽ[ +L/ )J1P/=%VBˢLZZ51P@hX`REP9HnFAVG)J j9>"'@$"<M=M>*$)!+%#"'&747>7632.#"7267\Bm@U=+^e3)BC)3kfRzVuHXL3N7:"9TAb4B=P`oj#+NKPX@ZUR>@bUR>Y.!$&+#"&54632326767674/&7632462"&%9TZJH`%7D%T@A%RL*9R99R\3/ XatR   &$ +!32654&#"!"547!2wٲ!?׬<>5Y 54j757c7/%zDE/=*?@<'&<SM=M>#! ** +#"!!32767'"'&54767>32&'&{UX4D9?mBEB3#GoT7/JCH )#53EFZ+-wEL))/FTt?@ɉ?FE++?@<('<SM=M>$" ++ +%2676?!7!654'&#"'6762#"'&'7fLb6h+B:?oBE?4!GjT7/JCH)#5J-VZuEL')/FRv?@ͅ?FE+j*4@1&$ < :9M=M>%)&+'6?6 #"'654" 67326հNAV{?+JD; 1mY%!L\6RMN@#8B-^F%y͔X3)-!\t)^LT>LF%'@$#"<M =M>(&(!+&#"2$7# '&5477632.mV7DHA}ji߅R3qb FG;RXP#F+星LNNF/1O!7@ : 9 >% +!7#"'''77ms 7q5'}qy /E%mF%'@$<M =M>(&('+'67632"'&'732766'4'&#"d 3b-h[A&%ϔy/3RO1-HN͓LM+H#PSbF%6?@<#",<UM =M>'&0.&6'6(&(!+&#"2$7# '&5477632."&5465>32mV7DHA}ji߅R3qb FG-7T1'<Q;RXP#F+星LNNF/1Or7) /I9+ /FmF%6?@<,<UM =M>'&0.&6'6(&('+'67632"'&'732766'4'&#""&5465>32d 3b-h[A&%ϔy/3R@/7 R1';PO1-HN͓LM+H#PSb9)/H9)/G bKT@+3 d ZbZU N  ==L >@< d ZbZ   UU=L >Y@OMKJIE(3T4&&$"5 +.+"326?6;#"?654&+3!2&#!"'7637>7654&/"?3!272''"'%7b9DgHQ14+"  +'.d= {;r^ZB;94 ).:@//N&7߲+ yfFR)-4{>XZ4 " . ;))+ )'\ \K]m@UL +3@A ZbZ   U   UU=L >Y@jhb`YWPNKJIE(3T4&&$"5+.+"326?6;#"?654&+3!2&#!"'7637>7654&/"?3!272%>32#"&546%>32#"&54b9DgHQ14+"  +'.d= {;r^ZB;94 ).:@//B'!*?'!-@'!*?'!-+ yfFR)-4{>XZ4 " . ;))+ )'5)! '5+! %7+'7-! bX@  R < :K(PX@0bQM =M=  K >@.bUQM=  K >Y@WTQN5)"&&%893 +654+"'673!2?.+">32"&547>3232>7674.#"32&#'76;27XVN5<565%b-8=4V71 Rsu#TLD3E7')#C1 J9Ӝ``7P5q 3Cy  !/) yC#3XbHmR=1##3ry/7STj}=''Z/7@+*<54:K(PX@% dZL =M >@# dZUM >Y@100717($! // +#"32�'76;267654&+"?;!.%"47%Ja7J5=< '5= >'H+!JA ?'')1 ) DAP!-\;D2Z@W<bb b  U  UM =M>/-,*"!#%$$# +$267632# 32.#"63232>32#"&#"H7/@Fo=?"> 1=7!9# VD8wM=3Vm\\RJs/[`LN'HBhD58@5 <bM = =M>/$/$+'.#"#"?!27654&'.5476$32?-ZD`;XV-X6 >-ZP@[`3 j!9;NP's^d7QlB#*u*"Un( :UnA!#>'-%'K(PX@ = >@ K >YOG +6&/&67327&#"&6?>7&@UK >YOK&''"+>32#"&546%>32#"&546&/&67327&#"&6?>7-B'!*?'!-@'!+?'!-&@bUM>Y@%# )) +%267654&+"?37+"#"&547>32+FI!54IO!q/yFy0%!'+6''Z2;#%/ 7J/L]K-PX@ 0G<@ 0G K-PX@*U   U M =M>@7Z  bU   U  M =M>YY@MMM]M\TRLIX"3t'"%'4 +%654+"#"&5476323276654&#"&?2;263+"3 #"&#'76;232>7654&#ZD-{^%eC=PF;#%j'37155sP} Ǯq' 7o!g/>oc5 } 9?Xi2+ T7/A>1%'Zd/7yE'q>de5*d%m+Wh@ Q;'@&  U UM >Y@XXXhXg_]VSPMJGCB@=353343Y +3 #"&#'76;27!32&#'76;27674'#"?3%+"!654'#"?3%+" 32>7654&#} gxX' 5qkj`5R6qa5R6qKCNb6P 5q^i/de5-fbY|@  S6< :K(PX@$M =M= K  >@"UM= K  >Y@XURO5'338%893 +654+"'673!2?.+">3232&#'76;267674&#"32&#'76;27XVN5<565%b-8=4V71 RmNby RJ'P '1/ OeC``7P5q 3Cy  !/) yC#3X\N{p%3Q9''+/!V^=''Z%^f@-C K-PX@<d   b  b I   UU M >@? d   b  b UU N = M >YY@`__f`f\YSRKIGE><335334 +'.'.'32&#'76;27674'#"?3%+"27>7>32#"'&#"2;2"47%q]' #:Q9cb5R6qa5R6qR;=b'hD=@"3'!G%Ld=3C+1y7+!J>>Q#&  =''X ?''ZV\R\1% %1%G-FTTJ9<'-\#VW`@B'&@$ d    UM >Y@WTQPOMJG@=:98653#73#4+32�'76;2732�'74;27654'#"?307+"7654'#"?307+"%'"'%7Z5 M 3mZ6N4j\6L4m;]5N3m'6߲ ;''Zy =''\;''Xxk;'''\o=Q@Q> *K!PX@. dZ  VK = N>K(PX@/ db  VK = N>@3 db  VU I NBYYY@ONHFB@;942.,)&#  == +%267.#"&?3%+"6754+"?37#"#"&547632#"&546322673dD5# T  P9L;XD='?a`B-J\{7!D1Vp'RVob-)'!;%)')B9+^')N^N=1*+ %H7?H+P@ K.@)e  UM =M >Y@PMJGDA533633C5 +;2&+"#654+'76;267654'#"?37+"3!267654'#"?3%+"{#= 1P-C ? 9/Z6N4m:+% \3L 3o)`:%9 =''Z 7%5=''TD<J@G0<b   V =K >;942/+('   +!#2&#"'763>76322&#"'7637654'!"uVTJ7q#H^5+%8;Yq9d3g9D"^1''>X)J!;6''= [##+6{@ +*@$ZUUM >Y@420-(&#  % +3267654&#''74;27654'#"?!5#!"3 qJ X! !y%*bפ'\ 7)  ?d+!- Fp@*98F@UUM >Y@ E?2,)'  (! +3 7654&" 3267674&#637>7654&/"5?3263 !"&#"'RR/Kfg@Uϲ @ZUM >Y@($! // +#"32�'76;267654&+"?;!.Ja7J5=< '5= >'HA ?'')1 ) DAP!+0Ds!@%Z  UO M >Y@31>;1D3D"4'337 +767654+"?63!2+"32#4.#!"#632#"3!267654T+;> ;/% '7/8)V:u8H )FNX\+k)1)'`RV //< %% 1Z_71)Pol 5)m1J@*2K(PX@9 ZbZU N  ==L >@7 ZbZ   UU=L >YY@JIHD(3T4&&#"5 +.+"32?6;#"?654&+3!2&#!"'7637>7654&/"?3!272m9DgHRX+"  +'.d= z;r^ZB<93 (/9 ?// + yfFRV4{>XZ4 " . ;))+ )/5K-PX@i ? p0<@i ? p0@D  b  U  U M = N =N >Y@%|zrqnkheb_[ZPNHFDB3(334 +'".'.#32&#'76;27"#'76;267>3&4754&#"'"&547>327654'#"?3%+"727>7>32#"'&#"2;2+B)! !7M6e`8P 5qgPkX735FZZ63u11b<}%!).'NDZnT+Sb6P 5qb7+D5^q;-$A"D\7-=%%j3%7g^P?@P%$ =''X XT^doo/'RFC/;-?%% )=tW B&:?''Z KLjD=\T %1%E/HRTL5>'y^D5"@6bbbVM = M>Y@1/*( 55 +%2676.+"76;2676&#"'2632#"'&7>32y yj# #Zz^V^6%9s\51!7}\uN-!)! ]3=}%'jd/iXMfY\o_-qNL+5)3d_#V-W{@B'&@    UM >Y@WTQPOMJG@=:98653#73#4+32�'76;2732�'74;27654'#"?307+"7654'#"?307+"Z5 M 3mZ6N4j\6L4m;]5N3m ;''Zy =''\;''Xxk;''#VoWk@kXB'&K&PX@.d V  M  =M >@,d V    UM >YY@ihb`\ZWTQPOMJG@=:98653#73#4+32�'76;2732�'74;27654'#"?307+"7654'#"?307+"#"&54632267Z5 M 3mZ6N4j\6L4m;]5N3m-J\{7!D1Vp' ;''Zy =''\;''Xxk;''CN^N=1*+ %H7%5^@-C K-PX@6   b  b I   UU M >@9   b  b UU N = M >YY@\YSRKIGE><335334 +'.'.'32&#'76;27674'#"?3%+"27>7>32#"'&#"2;2q]' #:Q9cb5R6qa5R6qR;=b'hD=@"3'!G%Ld=3C+1y7>>Q#&  =''X ?''ZV\R\1% %1%G-FTTJ9<'J/HK-PX@ . <;@ . <;YKPX@.Z  K  =N =N>K*PX@/b  K  =N =N>K-PX@-b   UN =N>@*b   UM =N>YYY@HC?="$&53#53 ++"32�'74;27654+" "&=63232676654&#"&?232$6qc5R6q`B1Lo?R5LH9'$N{)3 2+'Z ?''ZBJVs8'V71;Jt1/%!-OK-PX@3(IB<@3(IBK-PX@0b  `  ` U L >@4b  `  ` U L  =>YY@NMHD@?21/-C"+%#'"'#2&#"'763767654&/"&5?327!2&#"'76?>={s61sA7w6J!$7 ' m5R }=6 %5%-';''q)) PE''H'7-O?K(PX@T = >@dT >YOHOG+6&/&67327&#"&6?>7!&#"&6?>76&/&67327!';FP=?=8 );FQ=?=:cc );FP=@=9 &  +"32654&!"547!2}9Ҝ5p;a; ZQ> n_ JQ/J#P+Hn@ 0%C@ ZU K  >Y@HEB?52AB53364 +%654#!";2&#'74;27654+"5723!0!2$3+;2&#'76;2%=-# b5P6qZ>;jb5P5q3%5 B''Z B)'X B''!; @|@*:@&IUM = K >Y@ ?>C6*!3($" +3 674#"654&/"?32632#"'2&#"'7637>Td#f'= bH,L k+lM!HzV"! $$ +".54732!"32672x<9hl)@b`U >Y@,+)& 11 +!2>7#.+"&#"&6?>76&+""'67!3=$ELo5/ ';FP=@=7 K(PX@"bK = N>@&bU I NBYY@;942.,)&#  == +%267.#"&?3%+"6754+"?37#"#"&5476323dD5# T  P9L;XD='?a`BRVob-)'!;%)')B9+^')- ?GKPX@ V U = >KPX@% V  I U = >K(PX@ V U = >@%d V  I U >YYY@GFED?>:92.&%!   +6&/&67327&#"&6?6?$47%654&'$P}W 2[e'@)  b  `U K  >Y@`^[WLKBAC#C&'"B +?3273#327654/"?327#2&#"'7637>54'&"&#"57636767654'&'& /7 B  C+%9#!7\3+!Hj /= )% &0%  %/wyX;Y''+f0)))/.%$R )) /. ))}#+P@<;KJ<;K(PX@"   OK =M  >@ U   OM  >Y@NMIG@=:852+(! PP +%&#"'746;>7654&+"?7+"3!267654&'#"?7+32#.X83% ';   ;3# '3% '=   ;5! %5.7l# #< -- !>))5 -- %8  !+ {5+G~@9@!UU  K >Y@DB@=631/-*$"GF +%267# 547654&+"=77+"727654&+"?7+";2'763P5% ` ;!-%  %=# @ǰX);  ;5! F; 5)87>3++ #3!%` -+ 9$/ +- =Z+ez@ `C$ @#Z  U K >Y@eb_\YVPMHE367336335+;2'!'76;>7654'#"?37+"3!267654'#"?37+"3!267654'#"?37+"%;  ;1#Z6N0m)b+# \-N /o9b/%\0N3m -- #> =''Z /5 =''Z%1'==''=Z+h|@ c4NE @#  U   O M >Y@heb_\YSPMJGF&33653364+3!267654'#"?37+"3!267654'#"?37+"32#54&#!'76;267654'#"?37+"h@T5% \-N /oPK1% \0N3m'336\h- ;1!Z6N0m% 3); =''Z1);=''Z%' `- ; =''+<@98(@%Z UUM >Y@52-*'"<; % +3267654&#%+"3 #"&#'76;27654+"'67!Jg/>}5sP} Ǯq' 7oD=yE;9+{*d'Zd/7yE'Z ;lu7 #%/)Uf@8$, @'   U  UM  >Y@VVVfVe][TQNIA?=:765343#53#4+32�'76;27654'#"?30%+"674+"?30%+"3 #"&#'76;2732>7674&#T`8P5q`7P 5qXc5H8oP} Ǯp'8o d/;ob5 } ?''[ ?''\B''Zd/7yE'Z>de5)d#++<n@%@U UM >Y@,,,<,;'3X"3#3 +674+"?30%+"3 #"&#'76;2732>7674&#c5H8oP} Ǯp'8o d/;ob5 }B''Zd/7yE'Z>de5)dD2Z@W<bb bU UM = N>1/,*$!#!#!! +#"&#""7>32327#"'676$32#"'&7>3232%{q=9+++VD8JG/H9N!) uDH}uN+)" uFN'E?!Ǐ[qLV'1)3^%DL"@ =)KPX@4  S  M = K =K =M>K(PX@7  S  M = K =K =M>@2  U  S  M =K =M>YYY@IGEDB?<963.+(%"  +"32654&!"547#32&#'76;27674'#"?3%+"332wDvȒ3Z=n\b5R6qa5R6q[JHRZB wdzQE  =''X ?''Z)mXZ+<It@  6 @%Z U  UM >Y@ IG&3362]3& +"'76;26767.547>323%+"32&#'76;27654&# %3}RhY35jd  1yZ>f 7oɇ%!>?6 c5P8oAo*ZNbŪ'ʚ%q$;ig;+%5=''X '')w@  @.b=M=N=M>Y@ #&#&"$+33272'"74?''"432>?&#"326H#PZTǔZ5%=BZ1wbm!\-CV>9:;'s)1@.<=M=M>$")))" +#"&5467>736322>54&#"{Z{/5}F;#ɑ)ZFRBbIjXu;3 FN Tf\ɦRh{h (=@:<b`M=N>$"((%%" +4&#"3264.#">'2# 476VDJpa=^w} /%oFP}bwݢêH{-FhOT'%;wZ9JmAVuD캸u#'@$<M=M>*$+"+'632327#"&54?>54&#'RgHDA`G>jo#9XhswXN3-i/bH3b1),-$"+4&#"32>&2674.'&'&54;#"&547%d:qPEX?ǰ/)T[A {#Dwb1BxH{?9m%"Lh?' )>kjZ-2+'Dd (1@.<bM=M>'""+4&#">2672#"&54>32-7XBvuFPgT!{#Prgb`%Ll-!#=g6i4V!㮉P{Nd3=+>/4T%V@ NM#" @D  b  T M=N=M = N >Y@VUQOLJFDCA=;98&%$##"%+>323>32#"'.#"3267#"&547##"547'#"&546323254&#"'6323D R3?X5<%D-+#n)D:BK%F XD9o#;%#R%J+)9qBI@!'7wժ7\ մRwJ+1Rq[o>!(7l8#77>Rbx3^P\j3A@>&<bU=M=M>$*!$34$+%32654&+"546;2654&#"'672632#"&54632^85{ Ld]7g15bwB`=)Zo|1:fp;LH;D R\^VP}XH3-L=C@@$ <b`M=M>''"('"$ +32672#"54?'#"&547654#""'632326?>32 rN'# L#}o;NZX%# J+X=H}< %V -b#<+#CB{OQR=MXb#;+#DH;LR##7 .7L=R[@XR> $ < db`  UM=M>POHFB@''"('"$ +32672#"54?'#"&547654#""'632326?>32#"&54632267 rN'# L#}o;NZX%# J+X=H}< %V  -IZy7 5Vq'-b#<+#CB{OQR=MXb#;+#DH;LR##7 .7P^P=34! &G8);D@AZbN=M =>8632,*;; +"&#"2'&'.'#"547654/&7632>7632#%#?do/R j.HcA Xz%R-* $]9{XC#m  '`] >!(7lJ%!+8bb\?/>->sIB@8bb`K=M=N>Y@ "-C%&%4 +%654+"#"&547>323276654.'&#.73!732672#"54>Z?m31%F;Fx+@)#?B)$ -s;7! [ O'# L 1 )es7% !)/Hw 2- ,9':!#<+#C>VB`?@):-<0,:KPX@1bX==N =N >KPX@4bX==N= =N>KPX@1bX==N =N >KPX@2b`==N =N >K(PX@4b``=N =N >@7b``=N= =N>YYYYY@ "%%&&$ +%#"'#"&547>323276>7'&5'672732672#"54jTD>Fz)?)"=E #J#q7eN'# LZ17% !)/H{+ =1)/ub#<+#C.1;5@2)<bT=M>&")&)+7654/&7632!7>3272672#"74?!#"54z%R-* !h/ V sN%# L#+B XJ%!+8b D+7 .7 -b #1+#C#>!(7@M=M>%'&"+327654'&#"4>3 '".J^5RX.Nd5?w{!No\{5\}}?Zx`êjF'K{e)95@21 <bM=M>','("#+%32672#"5476'4&#" #"547654/&7632>32wN'# T#N"%^N Xt%RP*+Vb#<+#C1Gq->!(7lV%!+Hzj't1>N@K#7 <9dbM=N=>#&);$!+63 #"'&#'54?67"'6?>324#"3267>!yrV?Z' P&\f-3 F V GPm=[9r*8>;H˾C!& ) ' NT;+Xwy D+8 -8FVQBWb(:@7b`M=M>$#! (( +"'&5467>32#"&4654#"32672F=ZN=ANd) ?+V}3%BLCObVTAO#E6'%7'5!N݉B/QLnJV9@6<bM=M >SQ(()"(%(( +654/&763232>323272#"&547654&#"#"747674&#" #"747u%R-* wmb5H` ;U'^*$P3}N TbR TV%!+7d5+XZHN'+''BHI+!>!(7mHA-<"'7ld!e89@6%$<bb=M>20%("##+!#"&463232676'&'&"&'>32>?674'&5432]bR'V<#' -t<J- +)]<"%/*# O *+ _k7)Z;%b)5A,+,^DS"}Bp:ALaY<"{ V@5K) bZ M =M =M =N>@? bb M =M =M =N>Y@& VTNLECA?;942,*$"!     +%27&#""326'6'"&54632327>7'"&5476327>32#"'&#"632#"LH%=THLETQBT+J!#J!3%9Nlbe5R$5~7>13' 3,)C3Tndc1R=[MLwTVNms^ٖP3#!9F)%NyFOFJ\2+3 +Rku^yNRHJ<KPX@<,$& <@<,$& @7Z`  ZN=M=M>Y@ :9$&$$!$&%# +&'&"&'>327>32#"&#"327#"&/#"&5463227 +)]<"%-:fJtP'@+)5#;%o%A#V1/OBY6)M+#=y@,+,^?ThL-%#5)7LT!1A^A(%!- 'LRS@P=*#<9b`M=M>RPIG@><;97/-&$( +32672'>54&547'"54?'#"&547654#""'632326?>32' rN'# .+  dZ9<$::L#}o;NZX%# J+X=H}< %VL .7 -b#<+#;(*#)--%!5L#:5(#ER!CB{OQR=MXb#;+#DH;LR##9<8@5' <bV=M>-&")(%+326?>3272672#"54?#"&54?654/&76320.I/ V sN%# L#+VQL;$R-*8P7//X6 D+7 .7 -b #1+#C#BcBA3FJ%!+,b^[@XUT MF <  b  `M=M =M >][PNLK)''(("(% +%47'#"&5476'4#""'6323267>32326?>3272672#"54?'#"& mb7TLU$# J)K-1F V]^ #3)#DX.57C B)7wh=LZR##7 .7 -b #1+#CB{OP,o!7Obr_@\ih MF aqodbLK)''(("(% +%47'#"&5476'4#""'6323267>32326?>3272672'>54&547'"54?'#"& mb7TLU$# J)K-1F V]$;;L#7T+LI-=M92^^7T>^ #3)#DX.57C B)7wh=LZR##7 .7 -b #1+#9'+%)--%3^4:5(#DT"CB{OP,o!7O# .@&<%:KPX@+bU=M=N>@.bbUM=N>Y@-+*)$"!   +%2654#"#"&547#"&#"'>23273632fTLJ>Ķ\hr1f7H)15{f%?+Rl6ÏFN=Åh;LZyweKINk'6)ZuP )Cn@k; /<: ;>:b`U= = N= N>+*=<20.-*C+C'%  +%2654&#"3632#"&547674/&76322672#"547654'727\5'BjoB#-FVǽPjX%R-*}N%# T!do7+j-͓BJad?LcuV[Q+>V%!+<1+#C1`>'%   +%2654&#"3632#"&547674/&7632\5'BjoB#-FVǽPjX%R-*-͓BJad?LcuV[Q+>V%!+uq7@"! K1PX@6b  bU UM= M  >@=bb  bU UM= M  >YY@750.!$!#!!#$ +703267#"&#"#"7>32327654"'672632#"&54>3C:{!oD-r! )w<-f#/#j_/7 5BFiuh! 8/897 -'B%SkX`^E%- )/?,@)TM=M>&%&%")+7654/&76323>3 '".547##"543276'4'&#"z%R-* $)ڦyDbAb4? XLS0xHN(EDk:J%!+8bF'6KiM)*.>!(75}}?Za2s :_@\$5<b UM=M = M  >86431/)&#  " +&#"37#".?6767.54637+"32672#"7&O-%yHCT%=7_C'm/yFJZHŏ 3#yN'# LX rJH^nuDf ' yfPw2%b#<+#Cdb 6t@ !@'ddbM=M>Y@ '"#$%+#"/76324&#">2672#"&54>32[ # HF7-7XBvuFPgT!{#Prgb`%Ll^)/-!#=g6i4V!㮉P{Nd3=+>/48?@<#<bUM=M>'"# +462"$462"4&#">2672#"&54>328M88M7N77N5-7XBvuFPgT!{#Prgb`%LloM88M88M88M8-!#=g6i4V!㮉P{Nd3=+>/4o"@ <K=>&'!+%#"'67!654&+#"54od719!5Ae X\{!dJp>!(7uX 16@3! <b =M=M>*$+'$!+632#"&767'632327#"&54?>54&# ?(/ RgHDA`G>jo#9XhswXN3-iT/' $Z/bH3b1)K1PX@Cbb  `  `  ``M= N>@Ibb`  `  `  ``M= N>YY@5320-+*('% :: +%267632#"&5476323.#"63232632#"&#"9\@)-?o? 5N`;c93u9+u( w=+g#)$U+?6L+ TpDVlox7E{D=57 *' -3oy+:@7b`M=M>! +* +.5463232654'&54632'"&'&#"#df%')1)+Lb}\J%5T=DRm^;/B+!HPdTydj:FA);V;J7ZMu`'1@.<bU=M>,"%+462"32672#"547674/&7632;N;;N\N'# T#`%R-* fR::R9cnb#<+#CN%!+6g/5@2<bU=M>,"%+462"$462" 32672#"547674/&76327N77N7N88Nt\N'# T#`%R-* oM88M88M88M8Ynb#<+#CN%!+6g`oj#+NKPX@ZUR>@bUR>Y.!$&+#"&54632326767674/&7632462"&%9TZJH`%7D%T@A%RL*9R99R\3/ XatR@7  Zb`U N  = N>Y@DCB>960.+)%#   +%2654&#"3632#"&547#""&=632327664&#*&6'?32%727\5'Dho@ FVƾNj9-1:B/LB#76):  T?'s-͓BJad?LuV[Q+>%eہ<% J#3Hu&g %8E@@=b  UT= N>:9@>9E:E,&$!% +>323632#"&54?!#"547654/&7632!2654&#"T V@FUǾNk'B Xz%R-* !d\6'Dho@ D+7wuVZR'=>!(7lJ%!+8b/ɓBJad?LXB@? KPX@<b`` T = M =M>@:b``  U T =M>YY@BB>=<;96(("'#+!>3232672#"54?674&#"#"547#737654/"'7672EJN)N'# T#)#%==N T(+? l2H;Z'tT!#<+#C1V4-C<>!(7lH?'9 1 *R)b;IA@ @5 d  dZb N=M =>Y@EC?=8632,*;; +"&#"2'&'.'#"547654/&7632>7632632'"&767#%#?do/R j.HcA Xz%R-* $]9{XCm ?(/  #m  '`] >!(7lJ%!+8bb\?/>/' $Lb=K@$ @1 d  db`M=M>Y@KIEC''"('"$ +32672#"54?'#"&547654#""'632326?>32#"/7632 rN'# L#}o;NZX%# J+X=H}< %V [ # HG-b#<+#CB{OQR=MXb#;+#DH;LR##7 .7)/d!e8MQ@NM9%$<dbbU=M>KJCA=;20%("## +!#"&463232676'&'&"&'>32>?674'&5432#"&54632267]bR'V<#' -t<J- +)]<"%/*# O *+ _k7K-JZy8!5Vp')Z;%b)5A,+,^DS"}Bp:ALaY<"P_P>33!  'H7XLQR@O8 '<9b`M=M>MKDB;97642*("$ +32672#"54?''>54&547'"&547654#""'632326?>32 rN'# L#}L! S)''>?G;NZX%# J+X=H}< %V -b#<+#CB{OCD;-)-%5]370!V{#QR=MXb#;+#DH;LR##7 .7D`@4 b  b UM  = M   >Y@"YWSQLJHGEC;964,*%#`` +" 326?654+"?7#"3254&#"#"54632#"&'##"&5432#"&54654&RoՃqs\3 L  5m^owmZ#'T7F}`捉{% J˰5`j7A+/$$lʤɒVj;&&ZjLh+ 'DQLf`Z}]ZZ]_E3q/%T ?Q@4 b  b UM = M   >Y@"KIFD?=<;:831.,)'#! QQ +"32?654+"?37#"32654#"#"54632#"'##"&54632#"'4>&mPH%-A# D#P nfC/ --1d[yt^yyp܃B=4"1 PG?5''Z?1'wf5N83hۿ;Z>1 ! q+DS@=*)'&K-PX@) U U   U M >@/  Z U U   U M >YY@"EEESERLJB?<963/-$" DD +2?.#!32!"&#'76;267#"'673!7654'#"?37+" 3267674&#/5<1R<;B@;G323632#"&5472654&#"#"%^N Xt%R1* V)FVǿNj \6'Dho@3 ->!(7lV%!+7djZ;wuVXO+>ɓBJad?H'+D5[ @   K-PX@K  bb b  U  T  UM = =M>@R  bb  b b  U T  UM = =M>YY@TPA=20/-*(&$#%"$#+$267632#"#5332.#"63232>32#"&#"6&/&67327&#"&6?>777/@Fo1K# = 1?8!9# VD7wN>&K1PX@Gb  b  b  ` `  TM= N  >@Mbb  `  b  ` `  TM= N  >YY@YWQPIGA?<:53#!!&%")+7654/&76323>323.#"63232632#"&#"3267632#"&547467##"54z%R-* !!(7uDQ8@5NE-< = K >JG33$33:33: +>;2&#'76;2?6&';2&#'76;267672;2&#'76;276'T` ! -' L%V=P)-%07F)>-V%= N %b j:115$/-''\RA+!''69`=>y''1?T9;@86 .<=K  >993#####= +67&76';2�'763276322�'76;26'mH~^ #+d1`  #  NE-'#)%? f:  +# r($L{sI!!jB ++ X33Fd ++ '7h' FDs{@utvH. y W>%@(b  T = K  >Y@lh`_\YVSPM33:333"G+6&/&67327!672;2&#'76;276';2&#'76;2?6&';2&#'76;267!&#"&6?>7>& N%b j -' L %V=P)-$/7F)=V );FP=@=9 tT` !/)-',-d=>y''1?T/-''\RA+!''69J/)-',-:115DLS@PE I2+ G >8#<9   T= K  >DDB@=:53##" +'654/7672!6322�'76;26'#76';2&#'76327767&V VZ&&'#)%@ f:  +" s'%#+d1` #  NEϕmH^ 26* 1 go33Fd ++ '7hjB ++ X VL{sI!!)Y\@ ZR7@%S U K  >Y@$\[WTQNKHDB<9630-)'#  YY +";2&#'76;27>;&543!232;2&#'76;254'.+32&#'76;277!bNv@V)FZ>VC1%XX]N+jXYNkl\ M #mmD^%''Zcn  nZ''>c^D=''ZVoRUZ@WSK2< UM= K  >UTPMJGDA><741.+($"  RR +";2&#'76;2?>;&63!232;2&#'76;26/.+;2&#'76;277!DnPXNwF HNXqu; ;'7fs71)B K ;+//F?P@ '7yJ h=!H^iZ''Zifa)twiZ''/+i`F/+''ZN;' -@ dK0@'S V K  >Y@zvnmifc`]ZTRPMJG4&334$4(G+6&/&67327!3&543!232;2&#'76;274'.+32&#'76;27#";2&#'76;2767!&#"&6?>7 !&c^D=''ZD^%''Zcl6/)-',-Pbe`@]c YC*<9 T=M= K  >edbb^[XUROKJHEB?3%334"3+'654/7672!&63!232;2&#'76;26/.+;2&#'76;27#;2&#'76;2?67%!V VZ&& ;'7fs72)A L ;,//F?P@ '7yJ h@@  d  bbbV M ==M>Y@JJJTJTHF&3<%!) +32632#".5467>54&+"546;2>54&#"'2632%&'767d:Z/s5LkE wZ1/B i^BݹsZ`P'wRX^4';u\55m)jTw{T#Vqh!N?t  #N; FAd5 ;:'TB{fCif-Rq/iVOf\g)3JP-)Rq#;'6A@"<:9 :K!PX@9   dZU=M=M= N  >K*PX@6   dZU  R=M=M>@7   dbU  R=M=M>YY@777A7A53!)!$24" +4632654&+"76;2654&#"'672632#"2632"&&'767#^95y Ld]7b1-'bwBb?+Zf@I%7}4d.{9]"]ur9L&"F=D wRVP}nV9]@bUL >Y@OLIFC@52"YY +32;2&#'76;27.547674.'&54;654+"?3%+">76769+#7) lR1c7P5q/ !-@ {c5P 7o{^N!!R/ 1cI{B B''Z9FP=;J  5DPG- B''ZEq/B'fN0@-A9<PK=>=33?:3:+654+"?37>7>;2;2&#'767326?$44.76;2w_Y?! {AFĄ7 >/T)-  ft7?# }E"-+ 7u%RN!+vp  3%t\^8 1-!7A%5''+/A:  B"8L@I5+ <UUM =M>$#31/-#8$8&'%"! +#"'&#"32>!"547!2">32326754&=ohBD5׬'5 =U;+Ny9Y7rj<=3DBN@@ +zDR/IH8PlADDE+-I@F'<b```M=N>!%%!#%%$+4>32#"&6'4'"6323232>7#"&#"=qwq钉^ ^"AY+f#/x NV:_A)vG+u! TDžϱNI3ٖ%'6;3oFv}G>7LJ0h@ %/@ ZUM =>Y@ 364#"&!+32#"'&#""'.+"5765?3%+"7y+<:'%LZ +9P7b s`3+% %//0|1* %%2'29@6'10<ZK=N=>$44&"&"+>32#"'&#""'.+"&?37#"7DhP!A1!$%5) #s#+  mF #9  I@Z' #'H%?-)7) ))!)BmfL0AUK-PX@I87%/<@I87 %/K-PX@, d dZUM =>@0 d  d dZUM =>YY@NLEC(364#"&! +32#"'&#""'.+"5765?3%+"7632"/&76'672#"/&'476y+<:'%LZ +9P7b s`!; ' R   3+% %//0|1* %%2'BA %   "H2EYjKPX@XMK;: '10<KPX@XMK;: '10<K(PX@XMK;: '10<@XMK;: '10KPX@6  bZ = =K=N=>K(PX@2   bZ  =K=N=>@6  bZ = =K=N=>YYY@QOIG@>($44&"&" +>32#"'&#""'.+"&?37#"7672#"/&76'632#"/&54656DhP!A1!$%5) #s#+  mF #9  I@!@  #^  Z' #'H%?-)7) ))!)Bmf% )   %+@ D @Rdb   bbUU  U M = M>Y@2-,}xvtsqogeb`XVQOIGC@=:42,-%$++ +2326754&547>32#"&#""&547>" 326?654+"?7#"3254&#"#"54632#"&'##"&5432#"&54654&%=%--! a;31V+.  woՃqs\3 L  5m^owmZ#'T7F}`捉{% J˰5`j7A+/$$L{ , %+! ;\{@ ) ;qlʤɒVj;&&ZjLh+ 'DQLf`Z}]ZZ]_E3q/%T ?-!s89 K-PX@Qb   bbUU  U = M= M>@Qdb   bbUU  U M= M>YY@(#"mkhfa_^]\ZUSPNKIEC><852/*("s#s'#'!"+463232654&54632#"&"#"&"32?654+"?37#"32654#"#"54632#"'##"&54632#"'4>&dM;%+37fD1b3- PH%-A# D#P nfC/ --1d[yt^yyp܃B=4"1 =}%*#'HBx{1 % 'G?5''Z?1'wf5N83hۿ;Z>1 ! D`@4 b  b UM  = M   >Y@"YWSQLJHGEC;964,*%#`` +" 326?654+"?7#"3254&#"#"54632#"&'##"&5432#"&54654&RoՃqs\3 L  5m^owmZ#'T7F}`捉{% J˰5`j7A+/$$lʤɒVj;&&ZjLh+ 'DQLf`Z}]ZZ]_E3q/%T ?Q@4 b  b UM = M   >Y@"KIFD?=<;:831.,)'#! QQ +"32?654+"?37#"32654#"#"54632#"'##"&54632#"'4>&mPH%-A# D#P nfC/ --1d[yt^yyp܃B=4"1 PG?5''Z?1'wf5N83hۿ;Z>1 ! `!-@*deUIMA'#'!"+463232654&54632#"&"#"&ۍR=(/7;jD3f3-;{!,!'JBv{1 ) %3+ ;K-PX@ 2<@ 2K-PX@) U  S UM >@/Z U  S UM >YY@! ; ;:9741.+(%$#" # +32676&# 32!"&#'76;267!7!76&+"?37+"!s 3um=Fm!^1:95 _ )94L6j 5ye;')8E:/+''X@5  b `U = K =M >Y@10/.-,)(3T!#" +%6&+3232#"&#'76;27#736&#"5727673!!9UbC YLޥ/1-TD d Dh92+}To1,l'\F'X= 3 F!;H@+2.-,B@&IUM = K >Y@ GFC6-!3()" +327'767674#"654&/"?32632'#"'2&#"'7637>Td#fvN@G bH,L k+lM!jZ;qV#&);)!+63 ''"'&#'54?67"'6?>324#"327'767>!yr#&;gsV?Z' P&\f-3 F V GPm=[FE? 8>;H+!5BC!& ) ' NT;+Xwy D+8 -8FV=1 W!!1s@ @!dZUK >Y@+(" 1. +7!2673.#!";2&#'76;27654&+"?2Hv7P7=aJ c5H6o)5>Zy+'`=N5CD''Z!%;V'=@:"<dZbN=K >334!"5+7654&'#"?!2673#&+32&#'76;2'?  Ly8X.RJ/O 1[B! -\B| 7''#+6@(@&  Z USK >Y@0-'&%$" 63 +7!.#!"3#;2&#'74;27#734654&+"?2@7P^L ZMd5J8mNZ'5>Zy)' {CPED''ZP!%Bs2I@F*<Z  SL=M >2210%353#4 +32�'76;267#737654&+"?7!54&+3BH/ P/12 ?3&@ M{/7/HD7''+/P1 -'^iP#s+M@CB0 *)@8 Ze U  UUUK >Y@KJHEA;85335"" +%#".#"#3 6767!;2&#'74;274654&+"?237!.#!")ϾJg3 /71)=) bd5J8m'5>Zy)@7P^L HJ7 =-DHED''Z!% {Ch?FB!sI@ C @7Z  bUL=  K =N>Y@HEB?4%%)%35 +7654&+"?7!54&+2#"&5476323265%32&#'76;26&@ M{/7/HLBu\=/NL5NT<H/ P/12B1 -'^i+HRFAlH ZB9DiXH7''+/o5K-PX@6L"g<@6L"g@I bU UD M  = N  =N >Y@)|xwihec\[TRPNIG>=334%"%3+'76;267>3&54&#"'"&=>327674+"?3%+"27>7>32#"'&#"22#54'&'.'.#32&#'76;27"13r!'@G-'%%-&NA\qZ+Kc5P6qI7#;%)^98:-#?"BZ7-=% ;J-17)7;SB?  8N7Zd6P5o\RhX33hg';:J3@-C%')=yV 5!>B''ZV@:`/3\1%%1%G-FTTJ9~}zxsqpnjifc`^ZYWTQNKHDC><9842!%##3#4+7654+"?307#"3267632#"&#"232#54&#"'.#32&#'76;27"'76;2>3.'&#"#"&54763235JuH 11 8V}X^V5>?5}3=` 1!543E85 %J=BHwOZAPj3)cN)=/PDe^-VT3'')-P< 1XNhJEw}T78@7''Z9FH#HG kKX# RPy#^DF@ 8 @?bbbVM = M = M  >Y@FD?>!$25$+" +772654&'&'&7>3232676.+"76;2676&#"'2632#"!DE5HWMoVN-!)! \ky yj# #Zz^V^6%9s\51!7sYHSoVJ?)\%SNL+5)3d_=}%'jd/iXMfY\j_22NRZ#jEP@M-8?<b`U=M=N>EC!$34%+" +772654&'7.5463232654&+"546;2654&#"'672632#"!FC5HXkOV19^85{ Ld]7g15bwB`=)Z>omXJT+}n S:3-=fp;LH;D R\^VPCvD``%7W@1A K(PX@? Z  b  UCK = M =K >K1PX@= Z  b U  UC M =K >K2PX@> b  b U  UC M =K >@= Z  b U  UC M =K >YYYY@"POHFEC?=98630-*'# WW +%2#./.#;2&#'76;276&+"?3%+"276762#"&#"2))9%AJ\#q/hT^ ,>5R8o-;8P 5qJ>hu{?!bN!#1Vf7%Cm @D9 RkC>pNw^/)''X/+''Z`PN#33G@=RwP@-,WBwXg@dR <b  b  U CM=M=  K >WTQNKHDC9842!%##3#4+7654+"?307#"3267632#"&#"232#654'&'.#32&#'76;26ۇI sJ 11 7'`^iX:"CCwDs /95/Vh! /XFAGwP1/N3'')-R}< !/XJE%L^:"!!96@ 7''+%q5d@ (@  4 QP dK-PX@?   b bb I   U  UM >@B   b bb U  U N =M >YY@`^[XHFDB<:320/3363#4+%#&'32�'76;274654+"?3%+"273>7>32#"'&#";2'.'&'\C3jZd5R8ob8P 5qLh>A[8/JkB#1$#F51N-+;%3}9q_+!?''XB''ZV l#yMJ\X%1%E/94sAJ H78'Ǔ+FY}@z'2 Y<   b b  bb  UK= M =N >TRNKIHECA?:810/.#363#$+%#&#2�'76;267654+"?37"373>7>32#"'&#"232'&'.'sZ?1VBX uO '30 H'I3964Z1Jb5)7`.^8 #Auy7J  h' 1)9'''-P5'' :_M=IGE / f '309%q5f@0J"K-PX@@b  b I U   S  UM >@Cb  b U   S  U N =M >YY@daZYRPNLFD<;:9875235334 +'.'.'32&#'76;27#7374654+"?3%+"3#27>7>32#"'&#"2;2q_+=dTZd5R8ob8P 5q -;/J5kB#1$#F"Hb>)>' 3}9^RV?''XTPcB''ZcPLjF;\X%1%E/HRT!C0/'FdqU@<#" :K PX@9  Z  b ` S M = M >@: b  b ` S M = M >Y@KIEB@?<:861/3#3+%32�'76;267#73654'767!!267>32#"'&#"232'".'.'J uO '30  u)\7Boo@-3`-^7 !Auy1P  / 'F73'')1@F)$U+RT+jF!h`BAJE / f '%'R )36=7a@4 G KPX@M  Z  b  b  U K =K = N =M >K(PX@Q  Z  b  b  U K  =K  = L  =M >K1PX@F  Z  b  b  I U  U N =M >K2PX@G   b  b  b  I U  U N =M >@F  Z  b  b  I U  U N =M >YYYYY@_\VUNLJIEC<;96!!&3349 +'".'&'&'"#32&#'76;27654'#"#!33%+"27>7>32"'&"2;2)=1#(:`b5P5qTVSPMJGCB;952!%##3Q$+654'##;37#"3267632#"&#"232'".'.#32&#'76;267651H;%#^-9o10 1'^]hZ9#C@tDr 9L /D /XE@L uO '3-  /'XL')-P<!/XJE #\L?;4@5'''-% y+\@ H1 <;K(PX@0  SC  M  =M >@.    U  SCM >Y@%\\ZYVSMJGFEC@=98630/.,53#43+%0#0'76;27!32�'76;27674'#"?30%+"!654'#"?30%+";2#. 5qebd`5R6qa5R6qRT`5Q5q"#17y'Z =''Z ?''ZO ?''ZJBs[U@RT=8 I (<  SC  K  =M >YWSQNKFEB@<:73#43$3+%;2#4.#'76;267!;2''76;>7654&+"5?7#"!7674&+"?7#";##:)%ZB  +7#B\@'+   57%'5   )5'33 '+   45')?T7+ !>1 +- #<=# -+ #=) -- #?%+a@&FE \@+   Z   U  S K >Y@a^[XURKHD852.-3#53#4+%!;2�'76;27054+"?30%+"!654+"?327;23263.+";2&#'76;25\cZb5R8o`8P 5qLLc5P}?JB)7R_5b6P5o B''ZB''ZOB'{ B'';HsY]@ZV  .<  S  M =M = K >YWURMLIGDB@=63"52"74A +7232634&+";2'76;>7!32'76;>7654&'#"?7#!7654&'#"577eB)9L^ D5  +5'AJB'+  37%'3   +5'/1') q{1!+ +- %>! ++ %>;-  -+ #?! -#s+j@ ]S FE(  ZeUUU K = K  >@<  Ze  UUUU K  >Y@dca^\TROJGDA>;52334""+%#".#"#3 6765%;2&#'76;27654#!";2&#'74;27654+"5723)2$3+)ϿJf3 081)=)Dbb5P5q=-# b5P6qZ>;jJZsI D7 =-DHEq B''Z3%5 B''Z B)'Xd=`gF;!u\@; Z  bU L=  K  =N>Y@ZWUSQNIF@=;94$%%2R4+654&'#"?7!7##"&5476323265%32'76;>7654#!"32'76;>7'>  =7%9+NL5RL:D=  =7' FT F=  =5* 5 -+ $B-RnH ZD=KiLI!- +-+:7 1P)- +-+:^FLN@K,K<<9bQM =M>DB640.(&LL' +%>7654&#"26754&+ &54732#".#"&'47632'wJV^ /s` + 2 i1R;-!'(%>7/% '纞 %L1âwPfdDdsXJXPBJ % LbN58!1)b6 }b[FZeV?;Dd G/@*7FKPX@9  bb`Y  U VM>K1PX@8  bb`e  U VM>@>  bb`eU  U I NBYYY@?=1/,+&$GG% +%>7654#"2754&+ '.547632"'.#"&547>32u}Jv 7 ;sR)`ϑL3sN= #͑{ 7{R5}9PLFK9O 8DB8#) 7=1PT37?B1yf/#wD2A@>('<bM =M=M>%$($,"+772654&'&547!23.#"3267!##"FE5FbV 5mq/? B+HQnVJ?)d!-BC'-0 FHPky1LRZ+5<@9-,/<b`M=N>(&&%-#+732654&'7.547632#"&=4&#"327#"/)7+5FXios-/L#+5No&yl{mV)#V+}nwB;lJ T##7FæP?}J^e?Z6b@/.! <-,#:KPX@CM =M >@UCM >Y@ 796323+%;2#.+'76;27654&+"'7!2657.+";#58ro5o)/XR3/{3o7 3;X74) 3y'ZDw =  " wD%131@ <)#:KPX@$ZCM=M >K(PX@%bCM=M >@#bUCM >YY@ $3#233 +%;2#&'&+74;>7#"#3!2?#=4&+L=#6) (4f T5' !)=4 ,g+5;)!H- -{4A- %>"J??"+ii@Q6,)ID@? @ UM >Y@`^ZRNL4CUC +%3320#"&#"#576;7276767'&'&'#&5&?532326;+67656'&'#&?532326;+ %;  J  ;' d /   ;) p );   )/'Lh ` !!W+ #!     #! 5#)JsEB@?*#@ <M=  M   >EB?>=;63#:$#5 +&'&+"?307#"67654+"5?307#"32�'76;2F)  93w/; -P9%JG' P'Z)= )'.1 )'Z#7''+N@C)2@%  U SM >Y@NNMLHEBA@>;83453#4+32�'76;27!7!7&'&'#"&?37+"67654'#"?307+"!8b6P 7o8F !*   :3`1<-+;'`P=''ZR?Y9 )'+'))'3XR)JsLU@R.'G < M= K  = M  >LIFEDB?<876530#:$#4+'!7!&'&+"?307#"67654+"5?307#"!!32�'76;2FG)  93w/; -P9XGJG' P'Z)P= )'.1 )'ZsP7''+v@^>D g @1  b`   UC M >Y@vvsrolc`]WTQKI3t<34&3+%0'76;2'4'&;&#'76;2767654'.+"&?232623+"327676&+"?23263+"32#&'&+ 1F#5' ^f0  i} R- N%=  !^e!9% 17 #$) 6% ))m&'#))+f' ))m{% %j25ysom@jT72= ^) j<  b`C K =M >oolkfcYVSPMJDC<9#:3#5%3+!0'76;274/&#";2�'76;26?656'.+"5?7#"2?676&+"?37#";2#=43#R1hF+D5311Q - `@ 1F9#%:)'V3 '#+4 +3-'%./ '''9 ?  yV@54 @&G OM = M >Y@TQNH63#67693 +654+"'3!257.+"3!267674'#"?7+;2#.#&#"'76;>7VXR5-{33'9 1;Y71')3# F>  >3 ;# 58tod73%  9Cw @1 wC"4)5- -- %8 ) 1y#!>FY@% 54@8   bO   M =  K=M >Y@ TSPMJIC@9630-,)&  YX +"3!267654&+"?7#";2#&'&#!'74;267654&+"#3!2?#=4&#/'u'5' u'+   35%x>#5) *5je 7# }'C!)=4 ,g+5;)!H5#;!+9!# -- #?- -{4A+ ;F##J=B+Mx@ C'@%  UUCM >Y@KHFDB?'2#8%223 +%;2#.+76;267# 547654&+"5?7+"327674&+"577+"V;# 78rmR3# ^o<#-%  %=# =dcO)>  =5! % 3y- )8qE5+T++ #3%TJs -+ 9sOP@MIHC-,'><UC K =M >MKGEB?'3#8$314 +%;2#&+'74;267#"74?654&+"5?7#"327674+"5?7#" #5) !5%7{)  3' 1>v7F!  63'#+ %>/H- -+ #3L-76q-- -- %=3+O@M$K(PX@2b  bU K =  L >@0b  b UU  L >YY@JGECA>74"82#8" +#7# 547654&+"5?7+"367654&+"?7+";2'76;267\-* B!-%  %=# DT\V}X);  ;5! '; P5# g/:T++ #35Ga -+ 9)  +- ':#sW@8U(M @2b  bU K=  L >Y@ROLIFC=;#8'$#8A +%#7"#"54?654&+"5?7#";367654&+"?7#";2&#'76;267`Z%'   !1+ #2;D\BVT< '    35' F" qM !7%;Jbs9F -+ %1X'34#M) -- #?)) -+ %>P%++N@$+ @9@%U M= M  >Y@MKEB?>=;85#3#53#4+32�'76;27674'#"?30%+"$3232�'76;267654#"bb'R6qa5R6qR- RP)V52 P5?''X ?''ZO7/Q;''+/5RBHsPK@H#* A:9<  UK= M  >OMFC@?><8#$363#3 +%32�'76;267654+"?37#"63232�'76;267654&#"H!P '11 H' Q!V+ͪXa7G' P '1/ > JI^7''+/J 7''Zib9@7 ''+/ 9D2D=Q@N% <UM =M = M><:31-+)'#!  +. 3# 54767#"547>32#";6$32!#326 ;/% >H1%K ^cEA Ѯq1{g6HCL>`*<)\5 6 TVm{ B@  )(@(  U UM=M>Y@A?:832,*'%   +!27654&#"%>32#!#327"&54767"547>32#" K89XVJ}{ $_y a#դ@+.!F7)-RH/uXa Fs7D#'5yr 1H8y V'&5TDRe@b,FE  R <U  QM = M = M>QOCA:8420.*("   +. 3#"&54767 54767#"547>32#";6$32!#3267327 ;/TqL\?L >H1%K ^cEA Ѯu%Yhs9/BXqlH? w} 6HCL>`*<)\5 6 TVm`1m-HV%7JV{ Y@2LK  YQ @.U  U  QM= M>Y@XVJHB@"&%'%%0 +3!27654&#"#"&54767"&54767"547>32#">32#!327327=q K89XV}uJZ!'@.!FJ}{ $ a#7Fb{8/FU7)-RH/uPD?sy1H#,yX(4& Js7D#'5yr 9.LT %3J5=)0N@  @UK >Y"3###+4'&'#&?7#"3&#76;6767676 '>  >'+; ^=+     )' %'  ! %/{K-PX@~T  E1+<@~T  E1+K-PX@Wd  bVUM = M = N = N  >@Qd  bV UU M = N = N  >YY@+}zwtpoec][YWPOJGDB334#&$"+#"&546323267'".'.#32&#'76;27"#'76;267>3&4754&#"'"&547>327654'#"?3%+"727>7>32#"'&#"2;2-J\{8 D1+*q(8+B)! !7M6e`8P 5qgPkX735FZZ63u11b<}%!).'NDZnT+Sb6P 5qb7+D5^q;-$A"D\7-=%%j3BO]N=2(+  &I6%7g^P?@P%$ =''X XT^doo/'RFC/;-?%% )=tW B&:?''Z KLjD=\T %1%E/HRTL5>'T%k@cb87 @Qd  bU   TM=N= M  = N   >Y@kjfda_[YXVRPNMIGA@<:$##"('"+#"&54623267>323>32#"'.#"3267#"&547##"547'#"&546323254&#"'6323-I[x6!(7l8#77>Rbx3^P\%s7d@< 0^ )K(PX@M Z  be  UUU  K = N =K >K1PX@K Z  be   U  UUU N =K >K2PX@L  b  be   U  UUU N =K >@K Z  be   U  UUU N =K >YYYY@a`XVTRMKDCA>;852334%""+%#".#"#3326765!32&#'76;2754+"?3%+"27>7>32#"'&"3)Jg3 /71)=+`b5R8o`8P 5qF?;o-2?w}B -'%N-AI J7 =-DR3?''XB''ZttHPmtN #3I+TiRBF!w^@ @+@M   b  bb  U M = M =K =N>Y@^]YWVTOMJHEB36334$%&+#"&5476323267654.#32&#'76;267654+"?37#"3267632#"&#""du +۴NL3Rq#w`8L uO '3- }J'J10 3)^\hZ9#C?y 35BnH ZD=KæH?y-5'''-P 7'')-P<!/X%H`+Z@ =&K(PX@:e  SUU M  =M >@8e   U  SUUM >YY@YWRQPOLJHFB?<;:852.-3#53#4+%!;2�'76;27054+"?30%+"!654+"?30%+"#".#"#33265\cZb5R8o`8P 5qLLc5P6q)ϾJg1 /71)=ϑ B''ZB''ZOB''Z D7 =/DBmsV@<%  @1   bS R K =K >Y@USOMHFA?;95$#73#5+%!;2''74;>7654&+"5?7#"!7654&+"'?7#"#"&54767232/>\<!')  55%y)3  +3%1/&+    53%{5͉NL8Oq-  +- #<=- -+ #=- -- #?nHXB;H%`+W@ =&R@+e   U  SK >Y@WTQMLKJGB?<;:852.-3#53#4+%!;2�'76;27054+"?30%+"!654+"?30%+";#7"&#'76;25\cZb5R8o`8P 5qLLc5P6qb6 Xqy5o B''ZB''ZOB''Z B';qT@ E @*e   U  SK >Y@TSMKHFDA<;8631/,2"52!+%30##7'76;>7!32'76;>7654&'#"?7#!7654&'#"577#X nZp +5'AJB'+  37%'3   +5'/1')  55'-9+- %>! ++ %>;-  -+ #?! -+ #?!'+Mx@ C(@%e  UUM >Y@KHFDB?&2#8%324 +%;2'#"#>;267# 547654&+"5?7+"327674&+"577+"V)O  j-77# !1#\o<#-%  %=# =ɾO)>  =5! %  -y5/6 qE5+T++ #3%s -+ 9sSK@HMLG0/*A<eU K =M >QOKIFC'3#8$234 +%;2'#"#76;267#"74?654&+"5?7#"327674&+"5?7#")  9d<;)5 , /% 7{)  3' 1>v7'!  63'% ++1V!+):1H- -+ #3L-76q-  -- %=Tq<P@ P= 0@2 db  V   V =K >Y@ NMGEA?;942/+('   +!#2&#"'763>76322&#"'7637654'!"#"&54632267uVTJ7q#H^5+%8;Yq9d3g9-J\{8 D1Vq'D"^1''>X)J!;6''= [#lN^N>1)+ %H7)>@>*  @; db  U=M=N=M>Y@<;42$#&#&"$ +33272'"74?''"432>?&#"326#"&54632267H#PZTǔZ5%=BZ1#-JZy8!5Vq&wbm!\-CV>9:;'`P_P>33!  'H7Tj<HTf@c0<b    U  V =K >JI>=PNITJTDB=H>H;942/+('   +!#2&#"'763>76322&#"'7637654'!"2#"&546%2#"&546uVTJ7q#H^5+%8;Yq9d3g9f!-H'!,Es!-F)!-FD"^1''>X)J!;6''= [#+ /@-!-?- -@+!/?+9@ 0" @8  b U= M =N= M>Y@8631+)&"$ +462"$462"33272'"74?''"432>?&#"3267N77N7N77N #PZTǔZ5%=BZ1oM88M88M88M8bm!\-CV>9:;'+b@43 S `_ @I Z  b  U U S =L =K >Y@bb]ZXVRPHEA?<93;3+!6'0'763767!2&#'76?>76&/"?3!.+"326?6;2#"'7.+3!2678l6o N '7  N-732!; X; #=+!1P1/ +)-#/e Ff<9R#q)+7%Uj'  +' 'H%)) 76J"5%j)2=APN@/+H ˋ{ M\@Y,D?> <b  bUM= M  = N   >LKHFCA$"#(%)' +>54&#"72764&#"#"54632632327#"'#"&5%6sRjL5L?d-XbHPTX0)RJ;)B/Lrk q`}BbT;T^ 'D-/G %3^K(PX@F d ZbZ VU N  ==L >@D d ZbZ V   UU=L >YY@\[USOMJIHD(3T4&&#"5+.+"32?6;#"?654&+3!2&#!"'7637>7654&/"?3!272#"&54632267m9DgHRX+"  +'.d= z;r^ZB<93 (/9 ?// 0-J\{8 D1Vq'+ yfFRV4{>XZ4 " . ;))+ )N^N>1)+ %H7 (=F@C=)<dbUM=M>$,'""+4&#">2672#"&54>32#"&54632267-7XBvuFPgT!{#Prgb`%Ll-JZy8!5Vq'-!#=g6i4V!㮉P{Nd3=+>/4P_P>33!  'H7D"9@6<SM =M>! " +32$7'>32!"&547!67654&#"hD#H] u1{iX^~%Q/mjm#1@.<bM=M>%#("+%32%654&#""'>3 #"54>L19V^LFtZZy A{} ;my+#u 6P` {wdѹr!1XCP`"4DP@M,#< U SM =M>A?970.'%! " +32$7'>32!"&547!67654&#">32#"&546%>32#"&54hD#H] dB'!*?'!-@'!*?'#+u1{iX^~%Q/mjmq'5)! '5+! %7+'7-! #/;M@J<b  UM=M>10%$750;1;+)$/%/%#(" +%32%654&#""'>3 #"54>2#"&546%2#"&546L19V^LFtZZy A{} ;mY!,G'!-Fr!-E)!-Ey+#u 6P` {wdѹr!1XCP6+!/@+#-@-!-@+!/@/\K-PX@i ? p0<@i ? p0@P  b U  U  U M = N =N >Y@5|zrqnkheb_[ZPNHFDB3(334 +'".'.#32&#'76;27"#'76;267>3&4754&#"'"&547>327654'#"?3%+"727>7>32#"'&#"2;22#"&546%2#"&546+B)! !7M6e`8P 5qgPkX735FZZ63u11b<}%!).'NDZnT+Sb6P 5qb7+D5^q;-$A"D\7-=%%j3D!,G'#+Fr!-F(!-E%7g^P?@P%$ =''X XT^doo/'RFC/;-?%% )=tW B&:?''Z KLjD=\T %1%E/HRTL5>'X+!/?+#-?-!-?+!/?T% n@ fe;: @P  bU   TM=N= M  = N   >Y@2 nmigdb^\[YUSQPLJDC?=8620-+(&$#     +2#"&546%2#"&546>323>32#"'.#"3267#"&547##"547'#"&546323254&#"'63237!-H&!-Es!-F)!-F R3?X5<%D-+#n)D:BK%F XD9o#;%#R%J+)9qBI+!/@+#-@-!-@+!/@@!'7wժ7\ մRwJ+1Rq[o>!(7l8#77>Rbx3^P\y^f M:@B  bb  b  U V M = M  >Y@( IGB@643210,*&#!MM     +2#"&546%2#"&5462676.+"76;2676&#"'2632#"'&7>32Z!-H'!,Es!-F)!-Fuy yj# #Zz^V^6%9s\51!7}\uN-!)! ]d+ /@-!-?- -@+!/?͞=}%'jd/iXMfY\o_-qNL+5)3d_ Ke@b3><  b   U U= M = M  > KIEC97651/+(%"     +2#"&546%2#"&54632654&+"546;2654&#"'672632#"&54632!,G'#+Fr!-E)!-E ^85{ Ld]7g15bwB`=)Zo|1:+!/@+#-@-!-@+!/@fp;LH;D R\^VP}XH3-+PLV.r+@(b`UIMAY@(&$" .. +!2#"$547>32727654.#"!'?51= %d8/\#N* hF7d5st+D#͑-8jT )=H'/9s;^)50~@+*%<-:K*PX@%b`M=M>@"b`QM>Y@(&$"00 +!20#"&5476323267674&#"!"'+ %};J!p^%f;B7a73-q) w18T3=7^#dHPw 8 %`Wg@B&@( U    UM >Y@!ZXb_XgZgWTQPOMJG@=:98653#73#4+32�'76;27;2�'76;27654'#"?30%+"7654+"?30%+"'!"'47>3!2b6P5ob5P8ob8P 5o9c5P6q %'?''Xy B''Z?''Xxk B''(*LX I]@Z0<b` UM = M>EC<:31/.,*"   +!"7>3!232672#"54?'#"&547654#""'632326?>32 %'rN'# L#}o;NZX%# J+X=H}< %V ()-b#<+#CB{OQR=MXb#;+#DH;LR##7 .7%`PWiy@aXB&@) U    UM >Y@!vtnlec\ZWTQPOMJG@=:98653#73#4+32�'76;27;2�'76;27654'#"?30%+"7654+"?30%+">32#"&546%>32#"&54b6P5ob5P8ob8P 5o9c5P6q(A'!+@'!,?'!+@&!-?''Xy B''Z?''Xxk B'''7+! '5+  '5+ '7- L Uh@e< ) "<  b`  U M  = M> QOHF?=;:86.,%#!      +2#"&546%2#"&54632672#"54?'#"&547654#""'632326?>32v",G("+Es!-F)",FurN'# L#}o;NZX%# J+X=H}< %V + /@,"-?- -@+!/?-b#<+#CB{OQR=MXb#;+#DH;LR##7 .7`1A@@=) <UM =M>><64-+$"  +"32654&!"547!2>32#"&546%>32#"&54}9Ҝ5p;a; ,B'!*?'!-@'!*?'!-ZQ> n_ JQ/J'5)! '5+! %7+'7-!  &7>@; UM=M> 42-+$"    +2#"&546%2#"&546327654'&#"4>3 '"./!-H'#*Es!-F)!-F^5RX.Nd5?w{!No\{5+!/@-!-@-!-@+!/@j}}?Zx`êjF'K{eB"8L@I5+ <UUM =M>$#31/-#8$8&'%"! +#"'&#"32>!"547!2">32326754&=ohBD5׬'5 =U;+Ny9Y7rj<=3DBN@@ +zDR/IH8PlADDE+"3M@J<b````M=N>%'&!#!!$" +32767#"&#"#"7>323276'4'&#"4>3 '".J^5R5H2-} P=+q# .Nd5?w{!No\{5\}Q93)))}?Zx`êjF'K{ed"8JZc@`B9 5+ <    UUU M =M>$#WUOMFD=;31/-#8$8&'%"! +#"'&#"32>!"547!2">32326754&>32#"&546%>32#"&54=ohBD5׬'5 =U;+Ny9Y7rj<=3B&!+@&!-@'!*?'#+DBN@@ +zDR/IH8PlADDE+ '5)  '5+! %7+'7-!  :Kr@o<  b  ` ` ` `  U M = N  > HFA?860.-+(&%#"      +2#"&546%2#"&54632767#"&#"#"7>323276'4'&#"4>3 '".3!-H'#*Es!-F)!-F^5R5H2-} P=+q# .Nd5?w{!No\{5+!/@+#-@-!-@+!/@l}Q93)))}?Zx`êjF'K{e`2DTq@n<3 <bb b    UU UM = N>QOIG@>751/,*$!#!#!!+#"&#""7>32327#"'676$32#"'&7>3232>32#"&546%>32#"&54%{q=9+++VD8JG/H9N!) uDH}uN+)" \B&!+@&!-@&!+?'!-uFN'E?!Ǐ[qLV'1)3^y'5)! '5+! %7+'7-! u N@ 98  K1PX@B  bb U  UU M = M  >@I  bbb U  UU M = M  >YY@( NLGE?=<;640.-+(&%#"      +2#"&546%2#"&5463267#"&#"#"7>32327654"'672632#"&54>3!,G'!-Fr!-E)!-EC:{!oD-r! )w<-f#/#j_/7 5BFiuh! 8+!/@+#-@-!-@+!/@/897 -'B%SkX`^E%- y I@ 6*' K(PX@+  b UK =  N  >@/  b U U  I  N  BYY@ GE@>:852/,#  I I  +!"7>3!2267.#"&?3%+"6754+"?37#"#"&547632E %&H3dD5# T  P9L;XD='?a`B))3Vob-)'!;%)')B9+^')d!e DO@L'10%<bbU=M>><+)$"  +!"7>3!2#"&463232676'&'&"&'>32>?674'&5432 %']bR'V<#' -t<J- +)]<"%/*# O *+ _k7(*q)Z;%b)5A,+,^DS"}Bp:ALaY<"b=O_@G> *K(PX@,b    UK = N>@0b    UU I NBYY@ \ZTRKIB@;942.,)&#  == +%267.#"&?3%+"6754+"?37#"#"&547632>32#"&546%>32#"&543dD5# T  P9L;XD='?a`BB'!+@'!-?'!+@'#*RVob-)'!;%)')B9+^')'5)  '5+! %7+'7+# d!e PZ@W3=<1<bb  U =M> JH750.&$"     +2#"&546%2#"&546#"&463232676'&'&"&'>32>?674'&54327!-H&#+Es!-F)!-F]bR'V<#' -t<J- +)]<"%/*# O *+ _k7+!/@+#-@-!-@+!/@)Z;%b)5A,+,^DS"}Bp:ALaY<"=LZ@ *K(PX@/  d   dbK = N>K1PX@3  d   dbU I NB@7 d d   dbU I NBYYY@$?>XVQOGE>L?L;942.,)&#  == +%267.#"&?3%+"6754+"?37#"#"&5476322#"54?6#"54?6323dD5# T  P9L;XD='?a`BT( V DRVob-)'!;%)')B9+^')b##  @)d!eBW@ :DC8@1bb =M = =M>Y@ QO%("#%&&$ +#"&76762"&767632#"&463232676'&'&"&'>32>?674'&5432( #% ( %P]bR'V<#' -t<J- +)]<"%/*# O *+ _k7 .  ++鏘)Z;%b)5A,+,^DS"}Bp:ALaY<"5bGYi@QH 9@+    UUU  K >Y@$fd^\USLJDB@=631/-*$"GF +%267# 547654&+"=77+"727654&+"?7+";2'763>32#"&546%>32#"&54P5% ` ;!-%  %=# @ǰX);  ;5! F; LB'!+@'!-@'!+@'#+5)87>3++ #3!%` -+ 9$/ +- '5)  '5+! %7+'7+# 9 T[@X?81<  b  U  V =M> RPCA;97642)'    +2#"&546%2#"&546326?>3272672#"54?#"&54?654/&7632X!-H'#+Fs!-F)!-Fs0.I/ V sN%# L#+VQL;$R-*+!/@+#-@-!-@+!/@8P7//X6 D+7 .7 -b #1+#C#BcBA3FJ%!+,!-,K1PX@ <@ K1PX@!ZUCM >@"ZUOM >YY@*)&#,, +3#'76;267674&+"?;!#!";2#4&8;; +4;8J <# =7s'+0)A1y;s*K1PX<K1PX@+ZbeL=M >@1ZbZeL=M >YY@('$! *) +3'76;27654&+"?7!#54&+;2#&#Ֆ 1Z'?  M3+/J > 7) 'ZB! -!^e|%+#%`)Ufx@pg8$, @1U   U  UM  >Y@'VV}{trkiVfVe][TQNIA?=:765343#53#4+32�'76;27654'#"?30%+"674+"?30%+"3 #"&#'76;2732>7674&#>32#"&546%>32#"&54T`8P5q`7P 5qXc5H8oP} Ǯp'8o d/;ob5 }B'!+@'!-@'!+@'#+ ?''[ ?''\B''Zd/7yE'Z>de5)dX'5)! '5+! %7+'7-! P $A[@V S G CB UTJHFEB[C[?=0.*('&$$     +2#"&546%2#"&5462654&#"3632#"&547674/&76322672#"547654'727!-G'!-Es!-F)!,E\5'BjoB#-FVǽPjX%R-*}N%# T!do7+j+!/@+#-@-!-@+!/@=͓BJad?LcuV[Q+>V%!+<1+#C1`>32#"&547>32#"&5433533+ *  ) *  oD!1BR@ LE=4+" "+>32#"&547>32#"&54>32#"&5467>32#"&54'>32#"&546^4!4544!3 3 3333 + * ) *  + ( ) *  )*  ZD# !  "+>32#"&547>32#"&547! 4!4544!+ * ) *  %TT5D5 ,! "+>32#"&547>32#"&547!##"&5476?44!633Ps 3 5+ * ) *  'VVh  +! 5X ` "+>32#"&546 4!4!( +! `  "+>32#"&546'>32#"&543333( + ( + F#1 +" "+>32#"&547>32#"&54'>32#"&5463 344!33 + ( ) *  ) *  N"+7!NVVF "+7!##"&5476?Os 3 5VVh ( 5ZBR "+>32#"&544!3 + * BR "+>32#"&544!3 + * XD!3 -$ "+>32#"&546'>32#"&546'>32#"&5463353 43 +! ( q( +! r) *  JN "+>32#"&544!4+! +! `{"+3#JLBNjX "+7!jTTX%"+7!PVV`"+3`t_yL "+>32#"&5464!4!+! ) N "+>32#"&543 5+ +! bm" "+%.'67.'>72 %  *)1O3\U- -7TV5*?  #=H5JK#KH74+@VZ6uo  "+>7>7672!!476 JN9%ob .] -;<9AF-& 2=RJ/?^H7J+yVN"+#"&'#654'+&'47>7654&547>7'&54767367&547>? <!g+H/sZ%z R%5}^/)V?ss6 fh-{Ps1?F9^1,G5 55 1N' #\5EBBXJ%5%ZrX ?N  %#E y "+23!7!67654&#!"&547>?3Z/Fs^`sT#J\=DFLD_dBzͼQL,<3 BS \Ho-#"+"&4'#6767>7674&'&547>?32 -') )7-@E?()%F^'5;Af+y%L` H'B. Q=P !!d%o""++67>77!"547>?3!2)+!) B>Tw 3>=8THH%AB1Z DQFV /@)-T{#61*"+'676767!"&547>?3!2'67>7632=' #/ 7?D@+%j 2>m_J % 5 9^DD)?%/;1 DQ %5F19k=Q;y"+654&'.547>?'>EBG Dj''+h !/' H3 3A 5 +#1@)uLd%" "+7>767"547>?"'#'6/u<3p %3B/ M9)h;;˪j DR9 +;-)#ZwRGRa*{2'"+#'676767!/>7>7"547>;3!25 )" /;w%/!)eB'Ps}d9@(@/%JB>)!@Xe};B RXJVy:5 "+.547>?#"&#!67654#"'7632!6767:9\`#%R lj: Hf+C`R;Y7 H --FP=P1% =RJw3#V};X`ybyB^q\y"+'67654'&'47>?32 j(<-+'RAsDRh =K !+;.M^f"+#0#!"747>?7!2s}"w=\u1'mdBIFP'9 dq "+654'!"547>?3!2#!7!26w>V %#Eu}eu}AX BQ Fb=͕mG>"+3!2#"5476%>7674&#!"'476767654'&5465>?w\TFNJ?DB5fD JZɑP!67 5?<w7=F=p{B?V!0 J4uVZ\)11b q5!" )0 1k3y! "+#>7#"547>?3!2!654&#ZBw=NB^*N3DR;Lm?FHTl;|FV RTA=-"+!7!67654&#"#"747654&'&547>?6˃'h` yX! whPX]  f,)8b4Z"D՚Vd͜1-mucs!(  Z 9FB  # #g{`jo'"+6767054'&547>?{L s<r!'5t !%%% `H^# + eBOD +#}9X3%oo%"+!7!6767654'.547>? p\ T M`m>C>1=4 \-f+\%;kB+ 9C6BI--)99u0("+".54767"'47>?3!2!3267654&{3alB9^>T!!`dju-1\J7-];]JSj?HHhxFiR3H\" h767654'&547>? ?674'&54767.547>?&#71^FT%w8+DX79ZH H3?7\)+9)3%;ew$'R BN)# B9kF'B?\#DA={ D-?MF)3% b4++! HT;'"+0#"#"54767.547>?;2"6'4'&#!VsL')%3 N!!'$V3D %)%' V+7N5!"R/ 3J %3Jj5g31=hJ%7Jb<  8aq71&"+654'!"'#"#"74767&547>?3!2#!7!26 N+7N/sL' '-@T %#Eu}euoO^B 6`5$8 BQ Fq=͕VbD"+%#>7654'.547>?36%&547>?&9 #+q o^L:9#-31%4 a+5'%-1u{٦3?{+) !2-BW '-/%#?[ -H J(%^LjyO) "+67&747>?&#"!7!6/&54?654#"&547>?fyq;e'%$%# =!v+Dp!')?BL>R?42PVŏ^DL=%f1A)5'b'? %5 )B94?MB+ ;=q-52."+%'"5476%>7654#!"&547>?3!2'673D/%N9TD c=FDTd_fD7P'\N1D- 'Ec1HsA6 ;FFDn}B7%!;; TBu"+64&#!"&547>?3!2#T`;A DRB{H?4 9HJPnGe'-myl"+!>54&54767&547>;"#36&547>?3&#!667654'&547>?31Tb %>H  D= /'`9 !I ;JbH 5L>  JӘt%^=}I-A!%P5k4;})I BNB+L5 '=JT   !u-!"+654&+!73>7>7#"&547>?3!2͇#V` 'R}tZ-;BBRBLVHZ%wtPyi?4 9HJPnGe'-=y;:."+654&'.547>?'#!654&'.547>?' >G?K C+#30&hշ>EBH Cj''+h )/' H33A # 1-%L)u!/' H3 3A 5 +#1@)u\=y:8+"+'67654'&'47>?32654&'.547>?'# j(<-+'RAs >G?K C+#30&hDRh =K !+;.M^)/' H33A # 1-%L)u\ y7+"+'67654'&547>?32'67654'&'47>?32 m%<-RA@4 j(<-+'RAsFRh =K  +;.MPDRh =K !+;.M^9 "+2#"5465676V3`: aL 5m;ٰ9' "+2#"5465676#2#"54656763`: aL3`: aL 5m;ٰ 5m;ٰHTD<GQ0@5b  V  U Q =K >Y@&>=QOKJCB=G>G;942/+('   +!#2&#"'763>76322&#"'7637654'!""2676&>2#"uVTJ7q#H^5+%8;Yq9d3g9d.F +[G ,V~IGD"^1''>X)J!;6''= [#C/.EE.1AdcGEeF)4>@  K1PX@4b  U Q=M=N>@>b  U Q=M=N=M>YY@+*><870/*4+4#&#&"$ +33272'"74?''"432>?&#"326"2676&>2#"H#PZTǔZ5%=BZ1.F *[G ,V}IGwbm!\-CV>9:;'*C/.EE.1AdcGEe!P FN@*98F@&  UU UM >Y@ NMJIE?2,)'  (! +3 7654&" 3267674&#637>7654&/"5?3263 !"&#"'>2"RR/Kfg@Uϲ @*UU=M=M>Y@ )$6&& +>32'"&7654/"'76724'"32>2"Hq+)\\2Vohe+? lm)-!5= E3hGO/GP1-R%{ygLf?'9 1 *R9 !9J bP;;P:!- FN@*98F@%U U  QM >Y@ NMJIE?2,)'  (! +3 7654&" 3267674&#637>7654&/"5?3263 !"&#"'>2"RR/Kfg@Uϲ @)UQ=M=M>Y@ )$6&& +>32'"&7654/"'76724'"32>2"Hq+)\\2Vohe+? lm)-!5= E3hFP.FP1-R%{ygLf?'9 1 *R9 !9J bXO;;O;!- FR@*98F@&U U  QM >Y@IG OLGRIRE?2,)'  (! +3 7654&" 3267674&#637>7654&/"5?3263 !"&#"'!"7>3!2RR/Kfg@Uϲ @*U R=M=M>Y@31961<3<)$6&& +>32'"&7654/"'76724'"32!"7>3!2Hq+)\\2Vohe+? lm)-!5= E3h\#%1-R%{ygLf?'9 1 *R9 !9J b_()f\D<@*6 4 <;KPX@:b`b  X  QM =M>@;b`b   `  QM =M>Y@9731-+&%#! << +"'7.54732!"32672632#"'67726545* mv<9hl)K!PX@A  bb`  V  QM= M =>@?  bb`  U  V  QM=>YY@KIEC><8620*)'% AA +"'70#"'&5467>32#"&4654#"32672632#"'6732654632'"&7675* hF=ZN=ANd) ?+V}3%BLCE7CcHHH)8C ?(/  bVTAO#E6'%7'5!N݉B/QL+w6.Oc"!9/2/' $!X%7?K-PX@ %<@ %K-PX@UUM >@$bUUM >YY@ '&hc1+763767674&/"?32632#"&#"'%3 654.#">2"'@E  b  ` U  U=M=M =M >Y@CB?>8643'4%%$$ +654&#"326'#".54323674/"576723272'"547>2"^J7ZZP9N^NTbJ7XL+@ m#PZTFP.FPN;wo}jwE?2oF 9 1 *RLbm!\QP;;P:!-%7?K-PX@ %<@ %K-PX@UQM >@#bUQM >YY@ '&hc1+763767674&/"?32632#"&#"'%3 654.#">2"'@D  b  `U Q=M=M =M >Y@CB?>8643'4%%$$ +654&#"326'#".54323674/"576723272'"547>2"^J7ZZP9N^NTbJ7XL+@ m#PZT]FP/FPN;wo}jwE?2oF 9 1 *RLbm!\Q5O;;O;!-%7CK-PX@ %<@ %K-PX@UQM >@$bUQM >YY@:8@=8C:C'&hc1 +763767674&/"?32632#"&#"'%3 654.#"!"7>3!2'@E  b  `U  Q=M=M =M >Y@>G8643'4%%$$+654&#"326'#".54323674/"576723272'"547!"7>3!2^J7ZZP9N^NTbJ7XL+@ m#PZTE\$%N;wo}jwE?2oF 9 1 *RLbm!\Q<()!1-%7JK-PX@%<>9@%<>9YK(PX@eM =M >K-PX@eUM >@"beUM >YY@988J9J'&hc1+763767674&/"?32632#"&#"'%3 654.#"2&7>76&'&7>'@C  b  `   eU=M=M =M >Y@=<76&'&7>^J7ZZP9N^NTbJ7XL+@ m#PZT:2c J` SDN;wo}jwE?2oF 9 1 *RLbm!\Q`PWe!?A%3!f-%7BK-PX@%<@?=;:9@%<@?=;:9YK(PX@eM =M >K-PX@eUM >@"beUM >YY@888B8B'&hc1+763767674&/"?32632#"&#"'%3 654.#"&''67' 9KPX@E  b  `   e=M =M=M =M >@C  b  `   eU=M=M =M >Y@<<K(PX@J b  ZbZ  UU N  ==L >@H b  ZbZ  U   UU=L >YY@WU]ZU`W`TSRNKI3T4&&#";"+#"/7.+"32?6;#"?654&+3!2&#!"'7637>7654&/"?3!272'!"7>3!2+߉9DgHRX+"  +'.d= z;r^ZB<93 (/9 ?// 1 %' rQӁ yfFRV4{>XZ4 " . ;))+ )))# (4BP@M<ddb VM=M>+)B@<:1.)4+4'"" +4&#">2672#"&54>32!"7>3!2#"/7672-7XBvuFPgT!{#Prgb`%Ll %'H :9 -!#=g6i4V!㮉P{Nd3=+>/4()%*S_7@ 3;< :KPX@J b  ZZZ  UU N  ==L >K(PX@K b  ZbZ  UU N  ==L >@I b  ZbZ  U   UU=L >YY@&VT\YT_V_SRQMJH@=:51.*("  +"54?.+"32?6;#"?654&+3!2&#!"'7637>7654&/"?3!272'!"7>3!2S!g8 9DgHRX+"  +'.d= z;r^ZB<93 (/9 ?// %&Qr yfFRV4{>XZ4 " . ;))+ ))) (4BU@R:9<ddb VM=M>+)><861.)4+4'"" +4&#">2672#"&54>32!"7>3!2632#"&767-7XBvuFPgT!{#Prgb`%Ll %' 3 % -!#=g6i4V!㮉P{Nd3=+>/4()+# "fm1JU@*2 ZZZ   eU N  ==L >K(PX@? ZbZ   eU N  ==L >@= ZbZ   e   UU=L >YY@KKKUKUJIHD(3T4&&#"5 +.+"32?6;#"?654&+3!2&#!"'7637>7654&/"?3!272&''67m9DgHRX+"  +'.d= z;r^ZB<93 (/9 ?// -l1{9m#S+ yfFRV4{>XZ4 " . ;))+ )ZmkN]\cYfd (3E@B<10.,+9beM=M>)))3)3'""+4&#">2672#"&54>32&''67-7XBvuFPgT!{#Prgb`%Ll-m1{9m$T-!#=g6i4V!㮉P{Nd3=+>/4mkN]\cm1J^.@"*2NO Y K(PX@I ZbZU  U Q N  ==L >@G ZbZ   UU  U Q=L >YY@LK\[VTRPK^L^JIHD(3T4&&#"5+.+"32?6;#"?654&+3!2&#!"'7637>7654&/"?3!272267#"'&#"'>2m9DgHRX+"  +'.d= z;r^ZB<93 (/9 ?// $7!/J3EA%'7/)_G3+ yfFRV4{>XZ4 " . ;))+ )-> 5-.4 ]Z:-Vd (<]@Z,-7<69bU QM=M>*):9420.)<*<'"" +4&#">2672#"&54>32267"'&'"'>2-7XBvuFPgT!{#Prgb`%LlC$7!.J3EA%'7/)_F4-!#=g6i4V!㮉P{Nd3=+>/4_-> 5-/4 ]Z:-sqcu@$*K 0 E; : KPX@bd  ZZ  b  `UU  QN ==L  = K  >K(PX@cd  Zb  b  `UU  QN ==L  = K  >@ad  Zb  b  `UVU  Q=L  = K  >YYY@%edsrpnihdueucba]ZXPMJGDB><$"44&&#"5+.+"32?6;#"?654&+3!2&+632#"'732654&#"'7#"'7637>7654&/"?3!272%"&=332673m9DgHRX+"  +'.d= z;r^qC5DkHI;89Y))5 pB<93 (/9 ?// ux9'O8R8+ yfFRV4{>XZ4 " . ;h10Ld#%7/ ))+ )Xu\)!E8m{/ @Ml@i+! <,;MGFA :bb  UUQM=>KIDC'$#$%" +4&#">2672632#"'72654&#"'7.54>32267#"&7-7XBvuFPgT!@5DkHI;89X))6 hv}#Prgb`%LlNf!2+alu-!#=g6i4V!d1/Ld#%8/ P{Nd3=+>/4RfbV~mfIQ@- < <*;KPX@<Z  Z  U  UN = =K >K(PX@=Z  b  U  UN = =K >@;Z  b  U U  U =K >YY@"QPMLCA;9642/)('#  II +#2&#"'7637>7654&/"?3!272.+"32?6;#"?654&>2"b+21 D?5=6(/9 ?-19DgHRX+"  +'cFP.FP ''+.)J yfFRV4{>XZ4 "FP::P:_0GO!KPX@8b Z  UM=M= N>K*PX@9b  b  UM=M= N>@7b  b  UUM= N>YYY@ONKJ97$'#%#$$$ +#"5476?7>32#"'.#"32'# #"&546323267> 76>2" 1?b98j%)L$%#d#' 0"?97?<7B9B0.#  66 +23.#"327654&/"?27!"547%!"7>3!2->4 4elx=,31 w 1// ? %'D]} <>^Vc9 66 #1$@CD))! ?IU@>0)$@:b   UUM=N=M>Y@LJROJULU#&"#".,)$ +32654&'&#"'%".54%&5467&54632632#"'&#"4&#"32!"7>3!2J}|KX}T8/DAq%.^5C+^J^m#-%#  @)E@^A %'`Xe^B5L)9u#X%& = cMD3-qVV)9'#;omC(*'7XWUK(PX@UT = >@ bUT >Y@ OHOH+>2"6&/&67327&#"&6?>7!&#"&6?>76&/&67327!LFP.FPX';FP=?=8 );FQ=?=:cc );FP=@=9 &@3b U  U=M=M>Y@BA>=("'&7% +%#"547654/"57672>3232672#"547674&#">2"h T+? m|XNN'# T#M#%5e$FP.FP>!(7l?'9 1 *Rԙl'tb#<+#C1GV3-P;;P:'7-WPK(PX@TQ = >@dTQ >Y@ OHOH+>2"6&/&67327&#"&6?>7!&#"&6?>76&/&67327!FP.FP';FP=?=8 );FQ=?=:cc );FP=@=9 &@2bU Q=M=M>Y@BA>=("'&7% +%#"547654/"57672>3232672#"547674&#">2"h T+? m|XNN'# T#M#%5eIGO/GP>!(7l?'9 1 *Rԙl'tb#<+#C1GV3-P;;P:'7\!qd @"  bU  T >Y@ qpOHOK&''" +>32#"&546%>32#"&546&/&67327&#"&6?>7!&#"&6?>76&/&67327!B'!*?'!-@'!+@'!-';FP=?=8 );FQ=?=:cc );FP=@=9 &@5 bUU= M= M >Y@HF><:975&7& +462"$462"#"547654/"57672>3232672#"547674&#"6M88M5N55N T+? m|XNN'# T#M#%5emM88M66M88M6\>!(7l?'9 1 *Rԙl'tb#<+#C1GV3-7-g@U` _ <;K(PX@-b   `T Q = >@-db   `T Q >Y@ca^\XVTRJIA=.*"!gg +"'7"&6?>76&/&67327!6&/&67327&#"&6?>7!&#632#"'732654&+4 o*@=9 &@A bb`UQ= M= M >Y@RPHFDCA?&;$#$"5 +%#"#632#"'732654&#"'7&547654/"57672>3232672#"547674&#"h  55DjHJ;89X))5 p +? m|XNN'# T#M#%5e>!(T10Ld#%7/ l?'9 1 *Rԙl'tb#<+#C1GV3-'h7- \[@ @dTQ >Y@ OHOJ%+267#"&76&/&67327&#"&6?>7!&#"&6?>76&/&67327!e 3+`lun';FP=?=8 );FQ=?=:cc );FP=@=9 &@2bU Q=M=M>Y@EC>=("'&7% +%#"547654/"57672>3232672#"547674&#"267#"&7h T+? m|XNN'# T#M#%5ee 2+`lv>!(7l?'9 1 *Rԙl'tb#<+#C1GV3-RfbV~-%:h@)*4<39K(PX@UQ = >@UQK >Y@'&861/-+&:':OG +6&/&67327&#"&6?>7267#"'&#"'>32& 5-.4 ]Z:-`'<]@Z+ , 6<59bU  U Q=M>)(:831/-(<)<,"% +462"32672#"547674/&7632267#"'&#"'>32;N;;N\N'# T#`%R-* $7".J3FA$'70)`@AG3fR::R9cnb#<+#CN%!+6ge-> 5-.4 ]Z:-'!GPf@ @bUK >Y@IHHPIPOK&''"+>32#"&546%>32#"&546&/&67327&#"&6?>7"54?-B'!*?'!-@'!+?'!-&@/d  bbV=M>Y@ 97,"% +462"$462" 32672#"547674/&763262"&7677N77N7N88Nt\N'# T#`%R-*  1$ oM88M88M88M8Ynb#<+#CN%!+6gG)"  'Ybv@$F=<`_:K(PX@! dVK = >@ dUV >Y@[ZZb[bRNED<8CG +6&/&6732767654/"?327#&#"'7654'&#"&6?>7"547%&@C  bZ  b  U  U=N= M   >Y@USOMIG@>864320!##4+7654/"576722?632#"&#"3272#"&'.'&#"#"54632'"&767+@ m 0IRfdD%57j/% E!   'Cfi=Y4?J T ?(/ ?'9 1 *R?FH+;_ +7? b`r*$! B*77/' $'-Yaq@$F=@ UV  Q >Y@a`]\RNED<8CG +6&/&6732767654/"?327#&#"'7654'&#"&6?>7>2"&@AZ  bU  U Q=N= M   >Y@SRONIG@>864320!##4+7654/"576722?632#"&#"3272#"&'.'&#"#"54>2"+@ m 0IRfdD%57j/% E!   'Cfi=Y4?J TGP.GO?'9 1 *R?FH+;_ +7? b`r*$! B*7O;;O;'-Yew@$F=@!UV  Q >Y@\Zb_Ze\eRNED<8CG +6&/&6732767654/"?327#&#"'7654'&#"&6?>7!"7>3!2&@BZ  bU  U  R=N= M   >Y@NLTQLWNWIG@>864320!##4+7654/"576722?632#"&#"3272#"&'.'&#"#"54!"7>3!2+@ m 0IRfdD%57j/% E!   'Cfi=Y4?J T\#$?'9 1 *R?FH+;_ +7? b`r*$! B*7())#-+3J@dQL >YU>2"&@'bUQ=M>Y@ 7"%+>2"32672'"547654/"'7672RFP.FPX+}V+? lO;;O;ZmB5#Z-?'9 1 *R)#y+3?h@#bUQL >Y@64<94?6?U>2"!"7>3!2&@0b  UUQ =M>Y@0/.-+(!  +!"7>3!2>2"32672'"547654/"'76724 %' FP.FPX+}V+? lN()O;;O;ZmB5#Z-?'9 1 *R)#-+7S@dQL >Y@ .,41,7.7U!"7>3!2&@(bUQ=M>Y@('&%#   +!"7>3!232672'"547654/"'7672\$%X+}V+? l()ZmB5#Z-?'9 1 *R)f#-+6V@<431/.9K(PX@e =L >@deL >Y@ ,,,6,6U&''67&@&beU=M>Y@'&%$"  +&''6732672'"547654/"'7672'-l1{9m#SX+}V+? lumkN]\cZmB5#Z-?'9 1 *R!OX K-PX@3(IBK-PX@6db  `  ` U L >@:db  `  ` U L  =>YY@QPPXQXNMHD@?21/-C"+%#'"'#2&#"'763767654&/"&5?327!2&#"'76?>"547%={s61sA7w6J!$7 ' m5R }=6 %5%-+!J';''q)) PE''H\JbVd@ \[ @, d dbM=M >Y@`^ZXSQ(()"(%(( +654/&763232>323272#"&547654&#"#"747674&#" #"747632'"&767u%R-* wmb5H` ;U'^*$P3}N TbR Td ?(/ V%!+7d5+XZHN'+''BHI+!>!(7mHA-<"'7l/' $!XOWK-PX@3(IB<@3(IBK-PX@8b  `  `U U L >@<b  `  `U U L  =>YY@WVSRNMHD@?21/-C"+%#'"'#2&#"'763767654&/"&5?327!2&#"'76?>>2"={s61sA7w6J!$7 ' m5R }=6 %5%-~FP/FP';''q)) PE''HIP::P;JV^E@B<b UM=M >^]ZYSQ(()"(%(( +654/&763232>323272#"&547654&#"#"747674&#" #"747>2"u%R-* wmb5H` ;U'^*$P3}N TbR TFP/FPV%!+7d5+XZHN'+''BHI+!>!(7mHA-<"'7lO;;O;!-OWK-PX@3(IB<@3(IBK-PX@7b  `  ` UQ L >@;b  `  ` UQ L  =>YY@WVSRNMHD@?21/-C"+%#'"'#2&#"'763767654&/"&5?327!2&#"'76?>>2"={s61sA7w6J!$7 ' m5R }=6 %5%-8GP.GP';''q)) PE''HhO;;O;JV^D@A<b QM=M >^]ZYSQ(()"(%(( +654/&763232>323272#"&547654&#"#"747674&#" #"747>2"u%R-* wmb5H` ;U'^*$P3}N TbR T:FP/FPV%!+7d5+XZHN'+''BHI+!>!(7mHA-<"'7lZO;;O;NXAIi@ < @!UU =N>Y@ K"K"+#"'&"&#"'76?67.'&?!327654/&?327$>2"'/)X3)4j>-? W1*7jGO/GPu?1Dd1R'%q-[ 'sq>S''P::P;)9A?@<1 <bUM=M>','("# +%32672#"5476'4&#" #"547654/&7632>32>2"wN'# T#N"%^N Xt%RP*+VFP.FPb#<+#C1Gq->!(7lV%!+Hzj't%P;;P:N-AIg@ < @ UQ =N>Y@ K"K"+#"'&"&#"'76?67.'&?!327654/&?327>2"'/)X3)4j>-? W1*7jFP/FPu?1Dd1R'%q-[ 'sq>S''O;;O;)9A>@;1 <bRM=M>','("# +%32672#"5476'4&#" #"547654/&7632>32>2"wN'# T#N"%^N Xt%RP*+VFP/FPb#<+#C1Gq->!(7lV%!+Hzj'tO;;O;N-AMo@ < @!UQ =N>Y@DBJGBMDMK"K" +#"'&"&#"'76?67.'&?!327654/&?327!"7>3!2'/)X3)4j>-? W1*7j\$%u?1Dd1R'%q-[ 'sq>S''())9EE@B1 <b RM=M><:B?:E32!"7>3!2wN'# T#N"%^N Xt%RP*+V\$$b#<+#C1Gq->!(7lV%!+Hzj't()fN-ALq@< @eU =N>Y@BBBLBLK"K"+#"'&"&#"'76?67.'&?!327654/&?327&''67'/)X3)4j>-? W1*7jd-l1{9m#Su?1Dd1R'%q-[ 'sq>S''mkN]\cf)9DI@F1 :::D:D','("# +%32672#"5476'4&#" #"547654/&7632>32&''67wN'# T#N"%^N Xt%RP*+Vd-l1{9m#Sb#<+#C1Gq->!(7lV%!+Hzj'tGmkN]\c<m(=`@]-76<,:U  U M =M>*) ;9420.)=*=%#   +"74?"32654&!"547!2267#"'&"'>32!g99Ҝ5p;a; h0(?y,:7!/'#P78<*QrZQ> n_ JQ/J1D ;239 fc?2 -AZ@W1 2 <<;;d d  U VM=M>/.?>9753.A/A%'&'$! +632#"&767327654'&#"4>3 '".267"'&#"'>2 1$ J^5RX.Nd5?w{!No\{5$7!/J3EA%'7/)_G3*" !2}}?Zx`êjF'K{eK-> 5-.4 ]Z:-j/Dd@a4 > <= ;3:  I  U   U M =M>10B@;9750D1D,*$"+462"$462""32654&!"547!2267#"'&"'>327N77N7N77Nu9Ҝ5p;a; h0(?y,:7!/'#P78<*N77N77N77N7ZQ> n_ JQ/J1D ;239 fc?2N &7Kn@k; < F 98 IHCA?=8K9K42-+$"     +2#"&546%2#"&546327654'&#"4>3 '".267"'&#"'>2P!-H'#+Fs!,E)!-F^5RX.Nd5?w{!Nn\{6$7!.J3EA%'7/)_F4+!/@-!-@-!-@+!/@}}?Zx`êjF'K{eK-> 5-.4 ]Z:-y )5H@E<bUM =M>,* 2/*5,5&$  " +#"/7"32654&!"547!2!"7>3!2q+މ 9Ҝ5p;a; W! rQZQ> n_ JQ/J0))+9=@:ddVM=M>" 9731(% +"+%'&" +327654'&#"4>3 '".!"7>3!2#"/7632J^5RX.Nd5?w{!No\{5 %'F 77 \}}?Zx`êjF'K{ex(* $)1y(4Q@N<b UM =M>+) 1.)4+4%#   +"54?"32654&!"547!2!"7>3!2!f99Ҝ5p;a; W!QrZQ> n_ JQ/J0))'+9D@A10<ddVM=M>" 53/-(% +"+%'&" +327654'&#"4>3 '".!"7>3!2632"&767J^5RX.Nd5?w{!No\{5 %' 1$ \}}?Zx`êjF'K{ex(**"  ! @I@*:@,   dIUM = K >Y@BAAIBI?>C6*!3($" +3 674#"654&/"?32632#"'2&#"'7637>"547%Td#f'= bH,L k+lM!HzVL@ DC#7 <9K!PX@6b`b =M=N=>@1dddbM=N=>Y@ $&#&);$! +63 #"'&#'54?67"'6?>324#"3267>632'"&767!yrV?Z' P&\f-3 F V GPm=[9r*8>> ?(/ ;H˾C!& ) ' NT;+Xwy D+8 -8FVQBW}/' $!b @H@*:@.  UIUM = K >Y@HGDC?>C6*!3($" +3 674#"654&/"?32632#"'2&#"'7637>>2"Td#f'= bH,L k+lM!HzVF[@X#7 <9bbUM=N=>#&);$! +63 #"'&#'54?67"'6?>324#"3267>>2"!yrV?Z' P&\f-3 F V GPm=[9r*8>FP/FP;H˾C!& ) ' NT;+Xwy D+8 -8FVQBWP;;P:!b HPL@ 2KPX@@  UU M =  M = K =M>K(PX@@  U U M =  M = K =M>@:  U IU M = K =M>YYY@& POLKGDA?76541.+*#" H H   +"326767&'23#"'.#2&"'7637>7654&/"?326&>2" bNdb# q3? `D;7IXqX+244=7 ,L @+2FP.FP-11(E'?Mf#*RPN1d)%? '')+ !  +P::P:o+33@0<bUM=>.'"$#+>32#"'&#"#"54>7654/&7632>2"DL566'!$9< N r%R%,#dFP/FPH;%+H+@(7#090%!3P;;P:!; HPH@ 2KPX@?U Q M =  M = K =M>K(PX@? U Q M =  M = K =M>@9 IU Q M = K =M>YYY@& POLKGDA?76541.+*#" H H   +"326767&'23#"'.#2&"'7637>7654&/"?326>2" bNdb# q3? `D;7IXqX+244=7 ,L @+FP/FP-11(E'?Mf#*RPN1d)%? '')+ !  +O;;O;o+32@/<bRM=>.'"$#+>32#"'&#"#"54>7654/&7632>2"DL566'!$9< N r%R%,#qFP/FPH;%+H+@(7#090%!3]O;;O;!y HP\t@ 2KPX@HUU Q M =  M = K =M>K(PX@HU U Q M =  M = K =M>@BU IU Q M = K =M>YYY@.SQ YVQ\S\POLKGDA?76541.+*#" H H   +"326767&'23#"'.#2&"'7637>7654&/"?326>2"!"7>3!2 bNdb# q3? `D;7IXqX+244=7 ,L @+FP/FPC $&-11(E'?Mf#*RPN1d)%? '')+ !  +O;;O;m))+3?C@@<b URM=>64<94?6?.'"$# +>32#"'&#"#"54>7654/&7632>2"!"7>3!2DL566'!$9< N r%R%,#qFP/FP %'H;%+H+@(7#090%!3]O;;O;))!; HTP@ 2KPX@@U  R M =  M = K =M>K(PX@@ U  R M =  M = K =M>@: IU  R M = K =M>YYY@*KI QNITKTGDA?76541.+*#" H H   +"326767&'23#"'.#2&"'7637>7654&/"?326!"7>3!2 bNdb# q3? `D;7IXqX+244=7 ,L @+\$%-11(E'?Mf#*RPN1d)%? '')+ !  +()Yo+79@6<bRM=>.,41,7.7.'"$#+>32#"'&#"#"54>7654/&7632!"7>3!2DL566'!$9< N r%R%,#9\$%H;%+H+@(7#090%!3d()hb5=B@? <bUM = =M>/$/$+'.#"#"?!27654&'.5476$32$>2"?-ZD`;XV-X6 >-ZP@[`3 jFP/FP!9;NP's^d7QlB#*u*"Un( :UnA!#>P::P:y3D@Ab`UM=M> )'$" 3 2 +>2".5463232654'&54632'"&'&#"#GP.GOf%')1)+Lb}\J%5T=DRmiP;;P:^;/B+!HPdTydj:FA);V;J7ZMuhD5=A@> <bQM = =M>/$/$+'.#"#"?!27654&'.5476$32>2"?-ZD`;XV-X6 >-ZP@[`3 jmGO/GP!9;NP's^d7QlB#*u*"Un( :UnA!#>O;;O;y3C@@b`QM=M> )'$" 3 2 +>2".5463232654'&54632'"&'&#"#FP.FP/f%')1)+Lb}\J%5T=DRmO;;O;:^;/B+!HPdTydj:FA);V;J7ZMuhq5>FX@U<; < bbUM = =M>76FEBA6>7>/$/$ +'.#"#"?!27654&'.5476$32%"747%$462"?-ZD`;XV-X6 >-ZP@[`3 j+!K\qN;;N;ycAK!PX@8b  b `M = M=M >@6b  b `U M=M >Y@7520-+#!A@$" +>2"%632'"&767.5463232654'&54632'"&'&#"#X@G*@G ?(/ f%')1)+Lb}\J%5T=DRmH55H4/' $^;/B+!HPdTydj:FA);V;J7ZMuh%5@H@=9>;8 @3 bbUM = =M>Y@66HGDC6@6@/$/$ +'.#"#"?!27654&'.5476$32%&'767462"?-ZD`;XV-X6 >-ZP@[`3 j5m)jUw{\g)3JP-)Rq M<`@]  < bb`UM=M >42/-*( >= +>2"&'767.5463232654'&54632'"&'&#"# ?H*?HK4e.{9]!^f%')1)+Lb}\J%5T=DRmWH44H5~kMOiq^;/B+!HPdTydj:FA);V;J7ZMuh`5=EL@I <b  UQM = =M>ED/$/$ +'.#"#"?!27654&'.5476$32>2">2"?-ZD`;XV-X6 >-ZP@[`3 jmGO/GPGP.GO!9;NP's^d7QlB#*u*"Un( :UnA!#>O;;O;$P::P:y;M@J  b `UQ M=M >1/,*'%;: +>2">2".5463232654'&54632'"&'&#"#FP.FPFP.FP/f%')1)+Lb}\J%5T=DRmiP;;P:O;;O;:^;/B+!HPdTydj:FA);V;J7ZMu?^19@-@'b`UU >Y@9854,+)& 11 +!2>7#.+"&#"&6?>76&+""'67$>2"!3=$ELo5/ ';FP=@=7 @0  bb  UM =M>Y@0/,+('%##% +7>323#3272#"&547#"54767>2"} !Ry3q%3H31| 1GO/GPofP%#7w\ <d:'\v(6)- !P::P;?D19@-<:K&PX@(b`QM = >@&b`UQ >Y@9854,+)& 11 +!2>7#.+"&#"&6?>76&+""'67>2"!3=$ELo5/ ';FP=@=7 @,db  QM =M>Y@0/,+('%##% +7>323#3272#"&547#"54767>2"} !Ry3q%3H31| 1FP/FPofP%#7w\ <d:'\v(6)- !O;;O;?D1=@-<:K&PX@)b` QM = >@'b`U Q >Y@42:72=4=,+)& 11 +!2>7#.+"&#"&6?>76&+""'67!"7>3!2!3=$ELo5/ ';FP=@=7 @/db`  RM =>Y@+)1.)4+4('%##% +7>323#3272#"&547#"54767!"7>3!2} !Ry3q%3H31| 1\#%ofP%#7w\ <d:'\v(6)- !()d?D1<@-<::97549K&PX@'b`eM = >@%b`eU >Y@222<2<,+)& 11 +!2>7#.+"&#"&6?>76&+""'67&''67!3=$ELo5/ ';FP=@=7 @+db eM =M>Y@)))3)3('%##% +7>323#3272#"&547#"54767&''67} !Ry3q%3H31| 1+-l1{8m#TofP%#7w\ <d:'\v(6)- !#mkN]\c-FR^<@% U   QM>Y@*TSHGZXS^T^NLGRHRA@?>;743'%FF +".547654&/"?327#2>7654&/"?327#2#"&546%2#"&546s3_gL0+23@D3=8 y8R`\H u+06!3=:o95!,G'!-Fr!-F(!-E2MUAN '',+=pIf@\xRX ''.-w+!/@+#-@-!-@+!/@L=IU^@[$ <b`   QM=M>KJ?>QOJUKUEC>I?I''"('"$+32672#"54?'#"&547654#""'632326?>322#"&546%2#"&546 rN'# L#}o;NZX%# J+X=H}< %V ",G'",Es!-F)"+E-b#<+#CB{OQR=MXb#;+#DH;LR##7 .7, /@,"-@-!-@, /@-FZ@<J K U @* U U QM>Y@&HGXWRPNLGZHZA@?>;743'%FF +".547654&/"?327#2>7654&/"?327#267#"'&#"'>2s3_gL0+23@D3=8 y8R`\H u+06!3=:o9$7!.J3EA%'7/)_F42MUAN '',+=pIf@\xRX ''.--> 5-.4 ]Z:-rL=Ro@l$ A B L ?>PNIGEC>R?R''"('"$+32672#"54?'#"&547654#""'632326?>32267#"'&#"'>32 rN'# L#}o;NZX%# J+X=H}< %V $7!/J3EA$'7/)`@AG3-b#<+#CB{OQR=MXb#;+#DH;LR##7 .7--> 5-.4 ]Z:-d-FQ@<@  e UM >Y@ GGGQGQA@?>;743'%FF +".547654&/"?327#2>7654&/"?327#&''67s3_gL0+23@D3=8 y8R`\H u+06!3=:o9-m1{9m#T2MUAN '',+=pIf@\xRX ''.-cmkN]\cfL=HW@T$ >>>H>H''"('"$ +32672#"54?'#"&547654#""'632326?>32&''67 rN'# L#}o;NZX%# J+X=H}< %V  -m1{9m#T-b#<+#CB{OQR=MXb#;+#DH;LR##7 .7mkN]\c\Od@T ^ E!<] ;S:K(PX@6 b  U  U K =M>@4 b  U  U UM>Y@,QP b`[YWUPdQdJIHGD@=<0.&%$#  O O +"54?".547654&/"?327#2>7654&/"?327#267#"'&"'>32 !f9Z3_gL0+23@D3=8 y8R`\H u+06!3=:o9/(?y,:7!/(#P78<+Qr 2MUAN '',+=pIf@\xRX ''.-1D ;239 fc?2L==Q_@WV A B L $ ?>[YUSONIGEC>Q?Q''"('"$+32672#"54?'#"&547654#""'632326?>32267"'&'"'>2632#"&767 rN'# L#}o;NZX%# J+X=H}< %V u$7!/J3EA%'7/)_G3 1$ -b#<+#CB{OQR=MXb#;+#DH;LR##7 .7-> 5-/4 ]Z:-)"  u ]iS/@.UU   U M>Y@,`^ fc^i`iXWVURNKJ><4321.*'&]]  $ +"&54632"&54632".547654&/"?327#2>7654&/"?327#!"7>3!2N>5/%!3%//%#1133_gL0+23@D3=8 y8R`\H u+06!3=:o9X "+)'//'%)2"!31#!3.2MUAN '',+=pIf@\xRX ''.-/))L=IUap@m$ <b`   U   UM=M>WVKJ@>][VaWaQOJUKUFC>I@I''"('"$+32672#"54?'#"&547654#""'632326?>32!"7>3!22#"&546%2#"&546 rN'# L#}o;NZX%# J+X=H}< %V , %'!-H'#+Fs!,E)!-F-b#<+#CB{OQR=MXb#;+#DH;LR##7 .7(*B+!/?- -@-!-?+ /@^*>@/9<8;.:K(PX@&ddd=K =>@$dddU=>Y@,+<;6420+>,>*CE +6764'#"''&67>'#"'27267#"'&#"'>2B )= T="XA  p5-3$7!/J3EA%'7/)_G3mu)  )69/,)  )1)/0,> 5-.4 ]Y9-(*?S@P/9$"<8;.:bUU=>,+=;6420+?,?&-+ +%67>54'&5432#"'&"&'>32267"'&'"'>32p #]>\+ _k8%/.EfC+V5@5# :173$7!/J3EA$'7/)`@AG3}S@:ALaY G3GAbqK}03 Ys-> 5-/4 ]Z:--*2MK(PX@bcK =>@bcU>Y*CE+6764'#"''&67>'#"'27>2"B )= T="XA  p5-3FP.FPmu)  )69/,)  )1)/0O;;O;(*2.@+$"<bR=>&-++%67>54'&5432#"'&"&'>32>2"p #]>\+ _k8%/.EfC+V5@5# :173GP.GO}S@:ALaY G3GAbqK}03 YstO;;O;R@Io@ @#dbUM>Y@ $&(CF +.'.73277376&'.7327#"'.'&##"&''"'%7ER  Jo BTFNYBLLV  CBB V  4o  '7߲^N7)  )+0+E}78)  )w-&O a#'\b9G@  @'ddb`=>Y@GEA?1/'% 99 +"'&"&'>32676767>54'&5432#"'&'#"/7632!V5@5# :153F+:-7!+/-9J+ _k7S\`:=!2 h1[ # HG }03 YsLB9,4;MG:ALaYÂILx$,w)/R@Iv@ @$ dbUM>Y@BAAIBI&(CF +.'.73277376&'.7327#"'.'&##"&'"547%ER  Jo BTFNYBLLV  CBB V  4o  +!J^N7)  )+0+E}78)  )w-&O a#z\b9G@  @*bb`=M>Y@CA=;1/'% 99 +"'&"&'>32676767>54'&5432#"'&'632'"&767!V5@5# :153F+:-7!+/-9J+ _k7S\`:=!2 h1 ?(/ }03 YsLB9,4;MG:ALaYÂILx$,w/' $Rj@LX@*b    UUM>Y@NMBATRMXNXHFALBL&(CF+.'.73277376&'.7327#"'.'&##"&'2#"&546%2#"&546ER  Jo BTFNYBLLV  CBB V  4o   !,G'!-Fr!-F)!,E^N7)  )+0+E}78)  )w-&O a#O+ /@-!-?- -@+!/?9EQW@T <b`   U= >GF;:MKFQGQA?:E;E1/'% 99 +"'&"&'>32676767>54'&5432#"'&'2#"&546%2#"&546!V5@5# :153F+:-7!+/-9J+ _k7S\`:=!2 h1!-H'#+Fs!-F)!-F}03 YsLB9,4;MG:ALaYÂILx$,+!/@+#-@-!-@+!/@RH@Hq@&b  UUM>Y@ HG&(CF +.'.73277376&'.7327#"'.'&##"&'>2"ER  Jo BTFNYBLLV  CBB V  4o  FP.FP^N7)  )+0+E}78)  )w-&O a#P;;P:9AG@D <b`U=>A@=<1/'% 99 +"'&"&'>32676767>54'&5432#"'&'>2"!V5@5# :153F+:-7!+/-9J+ _k7S\`:=!2 h1FP.FP}03 YsLB9,4;MG:ALaYÂILx$,P;;P:R-@Ho@%bU  QM>Y@ HG&(CF +.'.73277376&'.7327#"'.'&##"&'>2"ER  Jo BTFNYBLLV  CBB V  4o  FP/FP^N7)  )+0+E}78)  )w-&O a#O;;O;9AF@C <b`Q=>A@=<1/'% 99 +"'&"&'>32676767>54'&5432#"'&'>2"!V5@5# :153F+:-7!+/-9J+ _k7S\`:=!2 h1GP.FP}03 YsLB9,4;MG:ALaYÂILx$,O;;O;^lt@&%\?8@1  b  `  UU K  >Y@tspo`^[WLKBAC#C&'"B+?3273#327654/"?327#2&#"'7637>54'&"&#"57636767654'&'&$>2" /7 B  C+%9#!7\3+!Hj /= )% &0%  %/wyX;YGO/GP''+f0)))/.%$R )) /. ))}P::P:HJDKPX@D4,' . <@D4,' . @?Z`  ZUN= M =M >Y@BA?=97$$!$&%$ +>2"&'&"&'>327>32#"&#"327#"&/#"&5463227+FP.FP +)]<"%-:fJtP'@+)5#;%o%A#V1/OBY6)M+#=iP;;P:J@,+,^?ThL-%#5)7LT!1A^A(%!- '`l~@vm &%\?8@3  b  `  UU K  >Y@zxqo`^[WLKBAC#C&'"B+?3273#327654/"?327#2&#"'7637>54'&"&#"57636767654'&'&>32#"&546%>32#"&54 /7 B  C+%9#!7\3+!Hj /= )% &0%  %/wyX;YB'!+@'!-@'!+@'#+''+f0)))/.%$R )) /. ))}'5)! '5+! %7+'7-! HJ TKPX@ TD<7' > <@ TD<7' > @CZ` ZUN= M  = M   >Y@& RQOMIGA?;95320,*$"     +2#"&546%2#"&546&'&"&'>327>32#"&#"327#"&/#"&5463227?!-G'#+Es!-F)!,EX +)]<"%-:fJtP'@+)5#;%o%A#V1/OBY6)M+#=+!/@+#-@-!-@+!/@@,+,^?ThL-%#5)7LT!1A^A(%!- 'M^AISK(PX@bU = >@bUK >Y@ IHED73+*"G +%&#"&6?67654'./.732736&/&67327>2" );FP=?qH!)3sb;D1  C%B-w615' GP.GO/)-' Uv"T3+-#)-/-.7;P::P:d!e8@E@B%$<bbU=M>@?<;20%("##+!#"&463232676'&'&"&'>32>?674'&5432>2"]bR'V<#' -t<J- +)]<"%/*# O *+ _k7FP/FP)Z;%b)5A,+,^DS"}Bp:ALaY<"wP;;P:Tcq)4o@21/-,('@dV =N >Y@***4*47"!7@+&#!"547676#!"'673!>327!267&''67923!;dm?:5:I4v!T )FP1f<;Pl=e,sIwyTs $O  hY+;DL3+Nso+4?@=<:87  ,@=  bb`   UUM=M>Y@555?5?42#!*"## +232>72#"'&#"&'&'67#"&#""'>327&''67;jD@Z&7"#1J%!FpFZVVFD u&3HC!'x496B-l1{8m#TTc#=;r3594 '-E^mkM][cTcD)1e@ ('@ UQ =N >Y@ 7"!7@+&#!"547676#!"'673!>327!267>2"923!;dm?:5:I4v!T )FP1f<;PiGP.GOTs $O  dO;;O;o4<l@i ,<:  bb``U QM=M><;8742#!*"## +232>72#"'&#"&'&'67#"&#""'>327>2";jD@Z&7"#1J%!FpFZVVFD u&3HC!'x496 FP.FPTc#=;r3594 '-E^O;;O;TcD)5m@ ('@!UQ =N >Y@,*2/*5,57"!7@ +&#!"547676#!"'673!>327!267!"7>3!2923!;dm?:5:I4v!T )FP1f<;P\#%Ts $O  k()o4@x@u ,<:  bb```b  RM=M>75=:5@7@42#!*"## +232>72#"'&#"&'&'67#"&#""'>327!"7>3!2;jD@Z&7"#1J%!FpFZVVFD u&3HC!'x496\$%Tc#=;r3594 '-E^()9:F@+@3bU  R=M=M>Y@=;C@;F=F("'&7% +%#"547654/"57672>3232672#"547674&#"!"7>3!2h T+? m|XNN'# T#M#%5e\#$>!(7l?'9 1 *Rԙl'tb#<+#C1GV3- ()m(4@ܵK$PX@6  bb  M =M =M>@4  bb    UM =M>YY@65*)<:5@6@0.)4*4('%##%+7>323#3272#"&547#"547672#"&546%2#"&546} !Ry3q%3H31| 1!-H'#+Fs!-F)!-FofP%#7w\ <d:'\v(6)- !J+!/?+#-?-!-?+!/?T9DOZ@W <b`  U M == >;:OMIG@?:D;D1/'% 99 +"'&"&'>32676767>54'&5432#"'&'"2676&>32#"!V5@5# :153F+:-7!+/-9J+ _k7S\`:=!2 h1b.F +[G ,GHV~IG}03 YsLB9,4;MG:ALaYÂILx$,6C0.EE.1BdcGEed!eR8CNX@U%$<bb  U M ==M>:9NLHF?>9C:C20%("## +!#"&463232676'&'&"&'>32>?674'&5432"2676&>32#"]bR'V<#' -t<J- +)]<"%/*# O *+ _k7.F +[G ,GHV}JG)Z;%b)5A,+,^DS"}Bp:ALaY<")C0.EE.1BdcGEe)w@  @.b=M=N=M>Y@ #&#&"$+33272'"74?''"432>?&#"326H#PZTǔZ5%=BZ1wbm!\-CV>9:;'y#2:KPX@3b` XUM=N>@4b` `UM=N>Y@:965.,%# 22 +27267>?6&/&7676767632#"&6&#" #"&546>2"D9$%!*! A*%Jg/DdRwT))Jw.0V-J5`*FP/FPB,I+ qY7\3@3@+b``M=N>Y@ ##)"$(+#"&546327267>?'?6&/&7676767632#"&6&#""F.0V-J5`+9$%!*! A*#Jg/DdRwT))J=Z@7  bb` S M=N>Y@<<;9##"$( +##"&546327267>?6&/&7676767#5367632#"&6&#" w.0V-J5`+9$%!*! A*%JgdRwT))aEO.7@7b`M = = L = N>Y@ FC35((&!$% +7>$32#"&#"6?32676.#"'676.#" ;2&#'76;2RbŨ }!"% =\ 1AyNd5T+5Z ,>Ro˨q<1 ͏# L,"%/#+3 6&'67.7>32#"'&#"#".+jdRLf#hJjLJ J)%hL p]uPo/PsibZVLmPyeBF%)(\VBP7{h\큃a)T}TD<DU@R0<b   V Q =K >DC@?;942/+('   +!#2&#"'763>76322&#"'7637654'!">2"uVTJ7q#H^5+%8;Yq9d3g9FP/FPD"^1''>X)J!;6''= [#"O;;O;)1@  @5b  Q=M=N=M>Y@ 10#&#&"$ +33272'"74?''"432>?&#"326>2"H#PZTǔZ5%=BZ1|FP/FPwbm!\-CV>9:;'O;;O;T<X@ K 0@< b  `b U  V =K >Y@"USQOGE@?;942/+('   +!#2&#"'763>76322&#"'7637654'!"#67654#"#"4632uVTJ7q#H^5+%8;Yq9d3g9AL 7:/>?'-lPN?D"^1''>X)J!;6''= [#+9J/+'5#VRA%<B)G@  @G  b `b M ==M=N=M>Y@FD@>75#&#&"$ +33272'"74?''"432>?&#"326#67676&#"#"&7>32H#PZTǔZ5%=BZ1 L@:A# 1$m=J7wbm!\-CV>9:;'+@3FJL("$  "%PQ<P`@]JEDB@?= 0HG;942/+('   +!#2&#"'763>76322&#"'7637654'!"&''6736?uVTJ7q#H^5+%8;Yq9d3g9)3+sJwyX]D"^1''>X)J!;6''= [#Y1,+;DL3+Nr Jg <)A@/. ?:97542  K1PX@. db ==M=N>@8 db ==M=N=M>YY@ =<##&#&"$ +33272'"74?''"432>?&#"326632&''67367H#PZTǔZ5%=BZ1I , 71{8m#Tfwbm!\-CV>9:;'}%2OkN]\c/2T GPj@g ;! KIFD?=:632+)%#     +&''67!#2&#"'763>76322&#"'7637654'!""/7J=e+sJwyVTJ7q#H^5+%8;Yq9d3g9'}hX+;DL3+Nr"^1''>X)J!;6''= [#wgJ &4B@  +K1PX@7 d  bb  ==M=N>@A d  bb  ==M=N=N>YY@B@<:31.,&$! +&''6733272'"74?''"432>?&#"326#"/762-l1{8m#T#PZTǔZ5%=BZ1@ 31 9mkN]\c>bm!\-CV>9:;'} !& Gc@V   ;!< ;K PX@C b `  Xb  U  V = L >@D b `  `b  U  V = L >Y@( `^\ZRPKJFD?=:632+)%#     +&''67!#2&#"'763>76322&#"'7637654'!"#67654'"#"4632J=e+sJwyVTJ7q#H^5+%8;Yq9d3g9L 79/=?'-mPN?hX+;DL3+Nr"^1''>X)J!;6''= [#`+9J.+'5#VRB%;w &4R@  +@L b  bb U  ==M=N=M>Y@ QOKIB@:931.,&$!  +&''6733272'"74?''"432>?&#"326#67676&#"#"&7>32-l1{8m#T#PZTǔZ5%=BZ1 L@:A# 1$m=J79mkN]\c>bm!\-CV>9:;'Y+?3FJL(!$   "%PQT Q\@ZYWUT E+$< ;:  b  bUU  V = L  >RRR\R\PNIGD@=<53/-*&#"  +267#"'&"'>32!#2&#"'763>76322&#"'7637654'!"&''67'=$4R8MG(+=4-jFHN8VTJ7q#H^5+%8;Yq9d3g9A=e+sIwy*; 3+,1 XU7*"^1''>X)J!;6''= [#phX+;DL3+NrVX &4I@$9 C +@Eb U U  ==M=N=M>Y@$65GE@><:5I6I31.,&$!  +&''6733272'"74?''"432>?&#"326267"'&#"'>32-l1{8m#T#PZTǔZ5%=BZ1$7".J3FA$'70)`@AG39mkN]\c>bm!\-CV>9:;',> 6-/4 ]Y9-To<DOi@fMLJHG 0<  db   V Q =L >EEEOEODC@?;942/+('   +!#2&#"'763>76322&#"'7637654'!">2"&''67uVTJ7q#H^5+%8;Yq9d3g9FP/FP%=e+sIwyD"^1''>X)J!;6''= [#"O;;O;hY+;DL3+Ns))1<@:9754  K1PX@1   db  Q=M=N>@;   db  Q=M=N=M>YY@222<2<10#&#&"$ +33272'"74?''"432>?&#"326>2"&''67H#PZTǔZ5%=BZ1|FP/FP-l1{8m#Twbm!\-CV>9:;'O;;O;wmkM][cT<NWv@s0PO>=OWPWLKIGBA=N>N;942/+('   +!#2&#"'763>76322&#"'7637654'!""&=332673'"54?uVTJ7q#H^5+%8;Yq9d3g9ux9'P7R7`!g8D"^1''>X)J!;6''= [#u\)!E8m{Qr)6D@<;60/*  @@ d  db  U=M=N=M>Y@@>:942#&#&"$ +33272'"74?''"432>?&#"326267#"&7%62"&767H#PZTǔZ5%=BZ1Ƴf!2+alu} 1$ wbm!\-CV>9:;'RfbV~)"  T<NXq@n0>=SQLKIGBA=N>N;942/+('   +!#2&#"'763>76322&#"'7637654'!""&=332673'#"/7uVTJ7q#H^5+%8;Yq9d3g9ux9'P7R7+މD"^1''>X)J!;6''= [#u\)!E8m{ rQ)6D@60/*  @@ d  db  V=M=N=M>Y@DB><42#&#&"$ +33272'"74?''"432>?&#"326267#"&?#"/7672H#PZTǔZ5%=BZ1Ƴf!2+aluF 77 wbm!\-CV>9:;'RfbV~ $)T%<l@S 0@M b  ` `b  U  U  V =K >Y@.>=jigedc][YWOMBA=l>l;942/+('   +!#2&#"'763>76322&#"'7637654'!""&=367654'"#"&632#32673uVTJ7q#H^5+%8;Yq9d3g9ux9?*:/>?'-mPN?TL 4R7D"^1''>X)J!;6''= [#u\%J/+&5#VRB%;++9E8m{)6T@60/*  @M b  `b U  U=M=N=M>Y@SQMKDB<;42#&#&"$+33272'"74?''"432>?&#"326267#"&7%#67676&#"#"&7>32H#PZTǔZ5%=BZ1Ƴf!2+alu M@9A# 1$m9:;'RfbV~u+?3FJK("$   "%PQT Qc@E+$< ;:b  bUU U  V = K  >SRa`^\WVRcScPNIGD@=<53/-*&#"  +267#"'&"'>32!#2&#"'763>76322&#"'7637654'!""&=332673'=$3R8LG)+<4-iGHN8VTJ7q#H^5+%8;Yq9d3g9ux9'P7R7*; 3+,1 XU7*"^1''>X)J!;6''= [#u\)!E8m{V)6J@#; E 60/*  @Gb U  U  U=M=N=M>Y@87HGB@><7J8J42#&#&"$+33272'"74?''"432>?&#"326267#"&7%267"'&#"'>2H#PZTǔZ5%=BZ1Ƴf!2+alu$7!/J3EA%'6/)_G3wbm!\-CV>9:;'RfbV~,> 6-/4 ]Y9-To<DVp@m0<  db  U  V Q =K >FETSQOJIEVFVDC@?;942/+('   +!#2&#"'763>76322&#"'7637654'!">2""&=332673uVTJ7q#H^5+%8;Yq9d3g9FP/FPux9'O8R8D"^1''>X)J!;6''= [#"O;;O;u\)!E8m{+)1>@  <>872 :K1PX@3b  U  Q=M=N>@=b  U  Q=M=N=M>Y@<:5410#&#&"$ +33272'"74?''"432>?&#"326>2"267#"&7H#PZTǔZ5%=BZ1|FP/FPe 2+`lvwbm!\-CV>9:;'O;;O;yRfbV~m1JR@*2K(PX@@ ZbZU Q N  ==L >@> ZbZ   UU Q=L >YY@RQNMJIHD(3T4&&#"5 +.+"32?6;#"?654&+3!2&#!"'7637>7654&/"?3!272>2"m9DgHRX+"  +'.d= z;r^ZB<93 (/9 ?// FP.FP+ yfFRV4{>XZ4 " . ;))+ )O;;O;d (0:@7<bQM=M>'""+4&#">2672#"&54>32>2"-7XBvuFPgT!{#Prgb`%LlFP.FP-!#=g6i4V!㮉P{Nd3=+>/4>O;;O;mJf@Y *2KPX@O b ` ZZZ  UU N  ==L >K(PX@P b ` ZbZ  UU N  ==L >@N b ` ZbZ  U   UU=L >YYY@ca_]USNMJIHD(3T4&&#"5+.+"32?6;#"?654&+3!2&#!"'7637>7654&/"?3!272'#67654#"#"4632m9DgHRX+"  +'.d= z;r^ZB<93 (/9 ?// L 7:/>?'-lPN?+ yfFRV4{>XZ4 " . ;))+ )+9J/+'5#VRA%<dB (FN@K<b`bM =M=M>$'&'"" +4&#">2672#"&54>32#67676&#"#"&7>32-7XBvuFPgT!{#Prgb`%Lly M@9A# 1$m/4+@3FJL("$  "%PQmHJ_2@#O Y *2K(PX@J ZbZ  U UU N  ==L >@H ZbZ  U U   UU=L >YY@LK][VTRPK_L_JIHD(3T4&&#"5+.+"32?6;#"?654&+3!2&#!"'7637>7654&/"?3!272'267#"'&#"'>32m9DgHRX+"  +'.d= z;r^ZB<93 (/9 ?// $7!/J3EA$'7/)`@AG3+ yfFRV4{>XZ4 " . ;))+ )-> 5-.4 ]Z:- (=_@\-7<6;,:bU UM=M>*);9420.)=*='"" +4&#">2672#"&54>32267"'&'"'>32-7XBvuFPgT!{#Prgb`%Llc$7!/J3EA$'7/)`@AG3-!#=g6i4V!㮉P{Nd3=+>/4-> 5-/4 ]Z:-4 U^(@ 5=<\[:KPX@D  d d  ZZZU N  ==L >K(PX@E  d d  ZbZU N  ==L >@C  d d  ZbZ   VU=L >YY@$WVV^W^UTSOLJB?<730,*$"  +&''67.+"32?6;#"?654&+3!2&#!"'7637>7654&/"?3!272'"54?=e+sIwy89DgHRX+"  +'.d= z;r^ZB<93 (/9 ?// m]4}hY+;DL3+Ns yfFRV4{>XZ4 " . ;))+ )JgU 3A@@0dbb =M=M>Y@=;75*(! +&''674&#">2672#"&54>32632#"&767-m1{9m#Tq-7XBvuFPgT!{#Prgb`%Ll2 ,! 9mkN]\c-!#=g6i4V!㮉P{Nd3=+>/4% m U^!@ 5=<\[:KPX@C  d d  ZZZU N  ==L >K(PX@D  d d  ZbZU N  ==L >@B  d d  ZbZ   VU=L >YY@ YWUTSOLJB?<730,*$"  +&''67.+"32?6;#"?654&+3!2&#!"'7637>7654&/"?3!272"/7=e+sIwy89DgHRX+"  +'.d= z;r^ZB<93 (/9 ?// '}}hY+;DL3+Ns yfFRV4{>XZ4 " . ;))+ )gJf 3A@@0dbb =M=N>Y@A?;9*(! +&''674&#">2672#"&54>32#"/762-m1{9m#Tq-7XBvuFPgT!{#Prgb`%LlI@ 31 9mkN]\c-!#=g6i4V!㮉P{Nd3=+>/4 !&fq@!lonji .F N KPX@Wb`  `  Z Z ZUU N == L >K(PX@Xb`  `  Z  b ZUU N == L >@Vb`  `  Z  b ZU  VU= L >YYY@gggqgqfed`][SPMHDA&&#"9"(%+#67674#"#"4632.+"32?6;#"?654&+3!2&#!"'7637>7654&/"?3!272&''67L 79/>?'-mPN?9DgHRX+"  +'.d= z;r^ZB<93 (/9 ?// =d+sIwy1+9J/+'5#VRB$>с yfFRV4{>XZ4 " . ;))+ )@hY+;DL3+NsH 3Qm@j<bbb  U  =M=M>PNJHA?98*(! +&''674&#">2672#"&54>32#67676&#"#"&7>32-m1{9m#Tq-7XBvuFPgT!{#Prgb`%Ll L@:A# 1$m=J79mkN]\c-!#=g6i4V!㮉P{Nd3=+>/4+?3FJL(!$   "%PQ_j@+hgecb '? G < ;:K PX@Q Z  Z Z ZUUU N == L >KPX@R  b  Z Z ZUUU N == L >K(PX@S  b  Z  b ZUUU N == L >@Q  b  Z  b ZUU  VU= L >YYY@*```j`j_^]YVTLIFA=:64.,&$!  +267#"'&"'>32.+"32?6;#"?654&+3!2&#!"'7637>7654&/"?3!272'&''67 '=$3R8LG)+<4-iGHN89DgHRX+"  +'.d= z;r^ZB<93 (/9 ?// =e+sIwy*; 3+,1 XU7*r yfFRV4{>XZ4 " . ;))+ )hX+;DL3+NrP 3Gx@u8 B54ED?=;94G5G*(! +&''674&#">2672#"&54>32267"'&#"'>2-m1{9m#Tq-7XBvuFPgT!{#Prgb`%Ll$7!/J3EA%'7/)_G39mkN]\c-!#=g6i4V!㮉P{Nd3=+>/46-> 6-/4 ]Y:-msJR]@[ZXVU *2K(PX@F d ZbZU Q N  ==L >@D d ZbZ   VU Q=L >YY@SSS]S]RQNMJIHD(3T4&&#"5+.+"32?6;#"?654&+3!2&#!"'7637>7654&/"?3!272>2"&''67m9DgHRX+"  +'.d= z;r^ZB<93 (/9 ?// FP.FP =d+sIwy~+ yfFRV4{>XZ4 " . ;))+ )O;;O;hY+;DL3+Ns+ (0;@98643@*dbQM=M>Y@111;1;'"" +4&#">2672#"&54>32>2"&''67-7XBvuFPgT!{#Prgb`%LlFP.FP-m1{9m#T-!#=g6i4V!㮉P{Nd3=+>/4>O;;O;ymkM][c'%A4K(PX@"b`U = >@"b`UK >YY"(%OG+6&/&67327&#"&6?>7#67654#"#"&632&?'-mPN?/)-',-/)-',-+9J/+'5#VRA%<B=D@A <b`bM ==M>$'&,"$+32672#"547674/&7632#67676&#"#"&7>32\N'# T#`%R-*  L@:A# 1$m=J7nb#<+#CN%!+6g&+@3FJL("$  "%PQ'-%-7K(PX@Q = >@QK >YOG+6&/&67327&#"&6?>7>2"&,"%+462"32672#"547674/&7632>2";N;;N\N'# T#`%R-* GP.GOfR::R9cnb#<+#CN%!+6gHO;;O;D'2@/QM =M>'&#"  +"32654&!"547!2>2"}9Ҝ5p;a; `FP.FPZQ> n_ JQ/JO;;O;''@$QM=M>%'&"+327654'&#"4>3 '".>2"J^5RX.Nd5?w{!No\{5oFP/FP\}}?Zx`êjF'K{eO;;O;;.@-b`UM =M>Y@8642*(#"  +"32654&!"547!2#67654#"#"4632}9Ҝ5p;a; pL 7:/=?'-lPN?ZQ> n_ JQ/J+9J/+'5#VRA%<B=;@8b`M =M=M>$'&%'&"+327654'&#"4>3 '".#67676&#"#"&7>32J^5RX.Nd5?w{!No\{5 M@:A# 1$m,+ +3,3'%   +&''67"32654&!"547!2"54?=e+sJwy-9Ҝ5p;a; ]4}hY+;DL3+NsZQ> n_ JQ/J#Jge*8@0/%('#"@(db =M=M>Y@ 42., * *%'&"+327654'&#"4>3 '".&''67%632#"&767J^5RX.Nd5?w{!No\{5E-l1{8m#T ,! \}}?Zx`êjF'K{e"mkN]\c|%  *3I@F<10:ddM =M> .,'%    +&''67"32654&!"547!2#"/7=e+sJwy-9Ҝ5p;a; #'}}hY+;DL3+NsZQ> n_ JQ/JWgJ*8}@ %('#"@(db =M=N>Y@ 8620 * *%'&"+327654'&#"4>3 '".&''67%#"/762J^5RX.Nd5?w{!No\{5E-l1{8m#T@ 31 \}}?Zx`êjF'K{e"mkN]\c| !& *F@9@5 b``U M =M>Y@ CA?=53.-'%   +&''67"32654&!"547!2#67654#"#"4632=e+sJwy-9Ҝ5p;a; UL 79/=?'-mPN?}hY+;DL3+NsZQ> n_ JQ/JD+9J/+'5#VRB$<`*HW@T%('#"<bbU  =M=M> GEA?860/ * *%'&" +327654'&#"4>3 '".&''67%#67676&#"#"&7>32J^5RX.Nd5?w{!No\{5E-l1{8m#T L@9A# 1$m@0 bU U M =M>Y@ 555?5?1/)'$$  +267#"'&"'>32"32654&!"547!2&''67'=%3R8LG)+<4-iGHM8Ϛ9Ҝ5p;a; z=e+sJwy*; 3+,1 XU7*EZQ> n_ JQ/JHhX+;DL3+Nr1Z*?b@_/9('%#"<8;.:U U  =M=M>,+ =;6420+?,? * *%'&" +327654'&#"4>3 '".&''67%267#"'&#"'>32J^5RX.Nd5?w{!No\{5E-l1{8m#T$7!.J3FA$'7/)_@AG4\}}?Zx`êjF'K{e"mkN]\c,> 5-.4 ]Y9-s'2H@E0/-+*<dQM =M>(((2(2'&#"  +"32654&!"547!2>2"&''67}9Ҝ5p;a; `FP.FP=e+sJwyZQ> n_ JQ/JO;;O;hY+;DL3+Ns-'2l@ 0/-+*@"dQM=M>Y@(((2(2%'&"+327654'&#"4>3 '".>2"&''67J^5RX.Nd5?w{!No\{5oFP/FP-m1{9m#T\}}?Zx`êjF'K{eO;;O;{mkN]\c&6>L@I<<;:d dVM =M>87('7>8>0.'6(6+"&" +!"547!2326754&'&547632"32654&'"67%5Y 5{//X!R ^1@#5ٲ#/+!KzDE/F+-H PT?=PRB fN-\b6D<;K!PX@1b`V =M=M>@,dddVM=M>YY@ $&$*"&%$ +32>5#"4632327654'&747632##".632'"&767L)C:XCm=yZ>;5j/N\-3sTBJtD+ ?(/ ^\;w;vL@PF7R^X8PfT5/' $&6?G@D<=<:ddVM =M>(':80.'6(6+"&" +!"547!2326754&'&547632"32654&7#"'%75Y 5{//X!R ^1@#5ٲ#/H'7߳zDE/F+-H PT?=PRB fN'\b6DKPX@3b` =M=M=M>K!PX@1b`V =M=M>@,dddVM=M>YY@ $*$*"&%$ +32>5#"4632327654'&747632##".#"/7632L)C:XCm=yZ>;5j/N\-3sTBJtD++[ #HG^\;w;vL@PF7R^X8PfT5)/&6R@ E @<  b  ``  UV M =M>Y@('OMKIA?:90.'6(6+"&" +!"547!2326754&'&547632"32654&'#67654#"#"46325Y 5{//X!R ^1@#5ٲ#/L 7:/>?'-mPN?zDE/F+-H PT?=PRB fN+9J/+'5#VRA%<B6TKPX@?  b`` M =M=M=M>@=  b``V M =M=M>Y@SQMK&$*"&%$ +32>5#"4632327654'&747632##".#67676&#"#"&7>32L)C:XCm=yZ>;5j/N\-3sTBJtD+ L@9A# 1$m87('HGB@><7J8J0.'6(6+"&" +!"547!2326754&'&547632"32654&7267"'&#"'>25Y 5{//X!R ^1@#5ٲ#/$7!/J3EA%'7/)_G3zDE/F+-H PT?=PRB fN,> 6-/4 ]Y9-6K@; E@6b  U UVM=M>Y@87IGB@><7K8K$*"&%$ +32>5#"4632327654'&747632##".267#"'&#"'>32L)C:XCm=yZ>;5j/N\-3sTBJtD+u 2*C.?:!#2+%V:;?.^\;w;vL@PF7R^X8PfTh1D :239 fc?2)&6>F@C<dVQ M =M>('>=:90.'6(6+"&" +!"547!2326754&'&547632"32654&>2"5Y 5{//X!R ^1@#5ٲ#/FP.FPzDE/F+-H PT?=PRB fNO;;O;}6>mKPX@+dQM=M=M>@)dVQM=M>Y@ $*"&%$ +32>5#"4632327654'&747632##".>2"L)C:XCm=yZ>;5j/N\-3sTBJtD+GP.GO^\;w;vL@PF7R^X8PfTO;;O;-FNx<@! U QM >Y@NMJIA@?>;743'%FF +".547654&/"?327#2>7654&/"?327#>2"s3_gL0+23@D3=8 y8R`\H u+06!3=:o9LFP.FP2MUAN '',+=pIf@\xRX ''.-O;;O;L=EN@K$ <b` QM=M>EDA@''"('"$ +32672#"54?'#"&547654#""'632326?>32>2" rN'# L#}o;NZX%# J+X=H}< %V .FP/FP-b#<+#CB{OQR=MXb#;+#DH;LR##7 .7O;;O;Fb@ U <K(PX@3 b  ` U K =M>@1 b  ` U UM>YY@"_][YQOJIA@?>;743'%FF +".547654&/"?327#2>7654&/"?327##67654#"#"4632s3_gL0+23@D3=8 y8R`\H u+06!3=:o9L 7:/=?'-lPN?2MUAN '',+=pIf@\xRX ''.-+9J/+'5#VRA%<LB=[d@a$ < b  `b` M =M=M>ZXTRKICB''"('"$ +32672#"54?'#"&547654#""'632326?>32#67676&#"#"&7>32 rN'# L#}o;NZX%# J+X=H}< %V  L@9@# 1%m@)   b U UM >Y@ XWW^X^PLFD?=730-$" VV +".547654+"?3%+"2>7654+"?373676&'#"547>32+""47%5doP5 {`7P7oo=Xd^E!j`7PR LB-Z eK'qdR)+!J3PXF; B''Z{>{Mf@\yRe B' @% C #1{JZZӰ[-\bBP@HG('@.d d  db==N>Y@ LJ))$)$$)" +46323267#"547#"&5476'4&#"'>3232727654.632'"&767!99-2 1/'uV-R?\^+793!Z#17^'sJ'/' ?(/ %?D7f%7F!]LBNLQi\pLz#)-FA/)9T 'f/' $V_@\<<]:K(PX@*  b U K =M >@(  b U UM >Y@ZXPLFD?=730-$" VV +".547654+"?3%+"2>7654+"?373676&'#"547>32+"'"'%75doP5 {`7P7oo=Xd^E!j`7PR LB-Z eK'qdRT'7߲3PXF; B''Z{>{Mf@\yRe B' @% C #1{JZZӰ['\bBP@ ('@. dddb==N>Y@ PN-)$)$$)" +46323267#"547#"&5476'4&#"'>3232727654.#"/7632!99-2 1/'uV-R?\^+793!Z#17^'sJ'/'[ # HF%?D7f%7F!]LBNLQi\pLz#)-FA/)9T 'f)/Vr@e < K(PX@:  b  b  U U K =M>@8  b  b  U U UM>YY@"omkia_ZYPLFD?=730-$" VV +".547654+"?3%+"2>7654+"?373676&'#"547>32+"#67654#"#"46325doP5 {`7P7oo=Xd^E!j`7PR LB-Z eK'qdRL 8:/=?'-lPN@3PXF; B''Z{>{Mf@\yRe B' @% C #1{JZZӰ[+9J/+'5#VRA%<BB`]@Z('< b ` `b M ===N>_]YWPN)$)$$)" +46323267#"547#"&5476'4&#"'>3232727654.'#67676&#"#"&7>32!99-2 1/'uV-R?\^+793!Z#17^'sJ'/' L@:A# 1$m=J7%?D7f%7F!]LBNLQi\pLz#)-FA/)9T '+@3FJL("$  "%PQ\VkwKPX@< ed<[Z:KPX@< e KPX@2  I  U  U K =M>K(PX@-   U  U K =M>@0  I  U  U UM>YYY@&XWigb`^\WkXkPLFD?=730-$" VV +".547654+"?3%+"2>7654+"?373676&'#"547>32+"267#"'&#"'>325doP5 {`7P7oo=Xd^E!j`7PR LB-Z eK'qdR$7".J3FA$'7/)_@AG33PXF; B''Z{>{Mf@\yRe B' @% C #1{JZZӰ[ ,> 5-.4 ]Y9-BVl@iG Q ('DCTSNLJHCVDV)$)$$)" +46323267#"547#"&5476'4&#"'>3232727654.'267"'&'"'>2!99-2 1/'uV-R?\^+793!Z#17^'sJ'/'$7!.J3EA%'7/)_F4%?D7f%7F!]LBNLQi\pLz#)-FA/)9T '-> 5-/4 ]Z:-PV^@ <@'U U QM >Y@^]ZYPLFD?=730-$" VV +".547654+"?3%+"2>7654+"?373676&'#"547>32+">2"5doP5 {`7P7oo=Xd^E!j`7PR LB-Z eK'qdRFP/FP3PXF; B''Z{>{Mf@\yRe B' @% C #1{JZZӰ[O;;O;}BJE@B('<db  Q==N>JI)$)$$)" +46323267#"547#"&5476'4&#"'>3232727654.>2"!99-2 1/'uV-R?\^+793!Z#17^'sJ'/')GP.FP%?D7f%7F!]LBNLQi\pLz#)-FA/)9T 'O;;O;MAJQHG:K(PX@db = >@dbK >Y@ EC73+*"G +%&#"&6?67654'./.732736&/&67327#"'%7 );FP=?qH!)3sb;D1  C%B-w615' '7߳/)-' Uv"T3+-#)-/-.7;'\d!eb8F@ %$@+ddbb=M>Y@ FD@>20%("##+!#"&463232676'&'&"&'>32>?674'&5432#"/7632]bR'V<#' -t<J- +)]<"%/*# O *+ _k7[ #HG)Z;%b)5A,+,^DS"}Bp:ALaY<"l)/M-AIQK(PX@bQ = >@bQK >Y@ IHED73+*"G +%&#"&6?67654'./.732736&/&67327>2" );FP=?qH!)3sb;D1  C%B-w615' GP.FP/)-' Uv"T3+-#)-/-.7;O;;O;d!e8@E@B%$<bbU=M>@?<;20%("##+!#"&463232676'&'&"&'>32>?674'&5432>2"]bR'V<#' -t<J- +)]<"%/*# O *+ _k7FP.FP)Z;%b)5A,+,^DS"}Bp:ALaY<"O;;O;MA]PK(PX@+b`bU = >@+b`bUK >YY@ZXVTLJED73+*"G +%&#"&6?67654'./.732736&/&67327#67654#"#"4632 );FP=?qH!)3sb;D1  C%B-w615' L 7:/=?'-lPN?/)-' Uv"T3+-#)-/-.7;+9J/+'5#VRA%<d!eB8VZ@W%$<b`bb M ==M>USOMFD>=20%("## +!#"&463232676'&'&"&'>32>?674'&5432#67676&#"#"&7>32]bR'V<#' -t<J- +)]<"%/*# O *+ _k7 L@:A# 1$m=J7)Z;%b)5A,+,^DS"}Bp:ALaY<"+@3FJL("$  "%PQM\AV@FP@%bUUK >Y@CBTRMKIGBVCV73+*"G +%&#"&6?67654'./.732736&/&67327267#"'&#"'>32 );FP=?qH!)3sb;D1  C%B-w615' $7!/J3EA$'7/)`@AG3/)-' Uv"T3+-#)-/-.7;,> 5-.4 ]Y9-d!e8Lg@d= G%$:9JIDB@>9L:L20%("## +!#"&463232676'&'&"&'>32>?674'&5432267"'&'"'>2]bR'V<#' -t<J- +)]<"%/*# O *+ _k7Q$7!/J3EA%'7/)_G3)Z;%b)5A,+,^DS"}Bp:ALaY<"-> 5-/4 ]Z:-q1B@: $@5  b M =M=M=M>Y@32@>982B3B&"$$'%% +54.# 32>?327327#"7"&7>3225>76#"76q+T?T "_C#D\7Bn-$./K+)VJ1FnDsͩ)xLs71 -}V=R\ HPtg5}?T)nL^\ao{eŇj3>[;'R\# V+%TFq1D@<  $@5  bM =M=M=M>Y@32>=862D3D&"$$'%% +54.# 32>?327327#"7"&7>322#".7>q+T?T "_C#D\7Bn-$./K+)VJ1FnDsͩ)xLs3#a-7?S VHPtg5}?T)nL^\ao{eŇj3>)T')X #7FF;[q1BQi@fJ : $< M =  M =M=M=M>32PNIG@>982B3B&"$$'%% +54.# 32>?327327#"7"&7>322'>76#"76&7>32'"'q+T?T "_C#D\7Bn-$./K+)VJ1FnDsͩ)xLs70 -}V=T ` 73#3 HPtg5}?T)nL^\ao{eŇj3>[;%T\# X)%TFR +%q1DRj@gLN<  $<M =  M =M=M=M>32QOKI>=862D3D&"$$'%% +54.# 32>?327327#"7"&7>322#".7>&7>32'"'q+T?T "_C#D\7Bn-$./K+)VJ1FnDsͩ)xLs3#_-7?R  Y4"3  HPtg5}?T)nL^\ao{eŇj39)T%+X #7FE;ZR )% q0O'K-PX@6B=<71<@6B=<71K-PX@<b =M =M = N  = M   >@Bb` =M =M = N  = M   >YY@"MKECA?;953,*%#  +2'>76#"76%2#"75654.# 32>?327327#"7"&7>3270 {=S `'! A+T?T "_C#D\7Bn-$./K+)VJ1FnDsͩ)xLsX;B# X)'TC- @)HPtg5}?T)nL^\ao{eŇj3q1DTKPX@RQ<  $<@RQ<  $KPX@8   bM =M=N=M>@>   b  `M =M=N=M>YY@FE32NLETFT>=862D3D&"$$'%%+54.# 32>?327327#"7"&7>322#".7>%2#".756q+T?T "_C#D\7Bn-$./K+)VJ1FnDsͩ)xLs3%] ,8?T U' HPtg5}?T)nL^\ao{eŇj39)T%)Z #7FE;Z - @)q1!4S@ : FA@;5" <;:  bUUM = M  = M  = M   >QOIGEC?=970.)' !!  +2'>76#"76%27#"&#"'63254.# 32>?327327#"7"&7>3270 9hB=R ` ;:+H+'=4+Nq;y+T?T "_C#D\7Bn-$./K+)VJ1FnDsͩ)xLsJZ<?J5% X)'TF\ VV\HPtg5}?T)nL^\ao{eŇj3q11AT@$5 =L  $<<;4 :K PX@E  Z  U U M =M=M=M>@F  b  U U M =M=M=M>Y@CB32NMHFBTCT@>;9862A3A&"$$'%%+54.# 32>?327327#"7"&7>3227#"&#"'6322#".7>q+T?T "_C#D\7Bn-$./K+)VJ1FnDsͩ)xLs=6-H+'=4+Lq;{w3#^-7?S VHPtg5}?T)nL^\ao{eŇj3}\ XX\'T%)X #6EF;[29J@ B5 &@.  b  U V =K >Y@;:43HFA@:J;J3949$334#334+%;2&#'76;27672;2&#'76;276'!"7!2'>76#"76;--90hD%1 <9P9\?R;oa92 ){V=R^  ''m`=>=:''8a#s" Z<%T\# X)%TF29L@ D5 &@.  b  U V =K >Y@;:43FE@>:L;L3949$334#334+%;2&#'76;27672;2&#'76;276'!"7!2#".7>;--90hD%1 <9P9\?R;oa3%_.8?T  S ''m`=>=:''8a#s" 'T%)X #6EF;[Z3:LZ@VD6 9 '@/ U   UV =K >Y@<;54YWSQJHCB;L76#"76&7>32#"'+-;3;L#%/ <;P9` =P;ma73 7gA;RZ 53%5 ''67`=>=:''8a#s" [;=L3# V+%TFR+' #3;N\@XF6 9 '@/ U   UV =K >Y@=<54[YUSHGB@&7>32'"'Z+-;3;L#%!- <;P:` =P9l`3%`/7?U R3#5  ''67`=>=:''8a#s"  z&V%)X #7FF;XO )% 2:JYpK-PX@B 8 &<5;@B 8 &<5;YK PX@= d  b   XV = M =K >KPX@> d  b   `V = M =K >K-PX@< d  b   `  UV =K >@B d  b `  `  UV =K >YYY@$LK<;43WVTRKYLYHFA@;J76#"76%2#"5436J+-<1 hD%/ m  O7^@P9o`73 }{=P b'   ''m`=>w''8a#s"  }[;B# X)'TD- @)2:M\NKPX@Q ZYE5 8 &<@Q ZYE5 8 &KPX@3   bV = M  =K >KPX@9 b  `V = M  =K >@7 b  `  UV =K >YYY@"ON<;43WUN\O\GFA?;M%2#"&756J+-<1 hD%/ m  O7^@P9o`3%[/8?T  U'    ''m`=>w''8a#s"  )T'+V #7FF;[- @)/3;L\*@(PX 6 D 9 'KPX@A  b U UV = M =K >@?  b U U UV =K >YY@&NM=<54[YVTSQM\N\JHCB76#"76%27#"&#"'632f+-;3;L#%/ <;P9` =P;ma74 +{V=S^ ;6-F+'?0+Hq;{''67`=>=:''8a#s"  Z;'R\" W+%TE] XX ]33:K^v@$> F 6V9 'KPX@D  bU  V M = = M =K >KPX@B  b UU  V = M =K >@@  b U  UU  V =K >YYY@'ML<;54XWRPL^M^JHDBA?;K322#".7>j\;3;M"%/ <;O9` ?R;ma'=6+F-%;3+'U>;{u3&\/7?T  T3''67`=>=:''8a#s" $]ZZ XT])T%+V #5FE;Zq3EK1PX@= <@= K1PX@?  bbIU M  =M=M>@@  bbUU M  =M=M>YY@54CA<;4E5E!""%$+#" +%327#".767.7>32#"&'.#"7632#"&#"2'>76#"76/VXud-+H7s 8 1%9u'229 ;T Vw7/ 9gA=R ZTu^'{4O[)=ZITZ9'%#! !Z\%) dL[;;L5# X)'TDq3FPK1PX@> <@> K-PX@:  bbU M  =M=M>K1PX@?  bbIU M  =M=M>@@  bbUU M  =M=M>YYY@54@?:84F5F!""%$+#" +%327#".767.7>32#"&'.#"7632#"&#"2#".7>/VXud-+H7s 8 1%9u'229 ;T Vw1%Z+7?S VTu^'{4O[)=ZITZ9'%#! !Z\%) dL)T')X #7FF;[3DR%K1PX@L N< <@L N< K1PX@BbIU M =  M =M=M>@CbUU M =  M =M=M>YY@54QOKIB@;:4D5D!""%$+#"+%327#".767.7>32#"&'.#"7632#"&#"2'>76#"76&7>32'"'/VXud-+H7s 8 1%9u'229 ;T Vw72 -}V=T \13%1 Tu^'{4O[)=ZITZ9'%#! !Z\%) dEZ;'R\# X)%TER )% 3FT%K1PX@N P> <@N P> K1PX@BbIU M =  M =M=M>@CbUU M =  M =M=M>YY@54SQMK@?:84F5F!""%$+#"+%327#".767.7>32#"&'.#"7632#"&#"2#".7>&7>32'"'/VXud-+H7s 8 1%9u'229 ;T Vw3#^+7?U V3#3!Tu^'{4O[)=ZITZ9'%#! !Z\%) dC)T%)X #5FE;ZR +% 3DTK1PX@H < P <@H < P KPX@D  b   `bU M  =M=M>K-PX@H  b   `bU = M =M=M>K1PX@M  b   `bIU = M =M=M>@N  b   `bUU = M =M=M>YYYY@FE54RQNLETFTB@;:4D5D!""%$+#"+%327#".767.7>32#"&'.#"7632#"&#"25>76#"76%2#"&7436/VXud-+H7s 8 1%9u'229 ;T Vw72 -}V;T \'   Tu^'{4O[)=ZITZ9'%#! !Z\%) dEZ;'R\# X)%TE -  @'3FTK1PX@R> <@R> KPX@=   bbU M  =M=M>K-PX@C b  `bU M  =M=M>K1PX@H b  `bIU M  =M=M>@I b  `bUU M  =M=M>YYYY@HG54PNGTHT@?:84F5F!""%$+#"+%327#".767.7>32#"&'.#"7632#"&#"2#".7>%2#"=6/VXud-+H7s 8 1%9u'229 ;T Vw3#`-7?U W'  Tu^'{4O[)=ZITZ9'%#! !Z\%) dE'V%)X #7FE9Z + @';M@E  '@H Z  bbZ U  UU=L >Y@=<KIDC76#"767NJHT11.#)/%1e!%yf5t>h ';;  73 7gA;R^ +NAK)1>^`@/+' -Z1++Z<=L3# V+%TFo=P@H  )(K(PX@I Z  bbX UU L  ==L >@G Z  bbX U  UU=L >YY@?>JIDB>P?P=;4243$&4"4+6&#!"!26?6;2#"=4&#!3!267!'76;276&+"?7%2#".7>o9NJL T32 - ) /%1g!%x^33s;j )9> 3%]/9?U T+NAK+/>AJ{@/+'} -Z1++'T%+V #8CH;[<M[5@U 9 WD  (K(PX@J ZbX  U   UU L ==L >@H ZbX  U U   UU=L >YY@>=ZXTRJHCB=M>M<:4233$&4"4+6&#!"!26?6;2#"=4&#!3!2!'76;276&+"577%2'>76#"7>&7>32'"'7NJJT32 -) / %1d!$yk3u=j'9> `74 ~z=Pb5 4#3 +NAK+/>AuP@/+' -Z1++[;B# X)'T)P )% =P^*@ZH  )(K(PX@J ZbX  U   UU L ==L >@H ZbX  U U   UU=L >YY@?>][WUJIDB>P?P=;4243$&4"4+6&#!"!26?6;2#"=4&#!3!267!'76;276&+"?7%2#".7>&7>32'"'7NJL T32 - ) 0%1g!%x^33s =j )9= y3# b -7 ?W R3#5 +NAK+/>AJ{@/+'} -Z1++(T%)Z #7FE;ZQ +% =N]@R [ZF  )(KPX@N Z  bbXU M  = L ==L >KPX@R d Z  bbXU M = L ==L >K(PX@P d Z  bbX  UU L ==L >@N d Z  bbX  U UU=L >YYYY@#PO?>XVO]P]LJED>N?N=;4243$&4"4+6&#!"!26?6;2#"'54&#!3!267!'76;276&+"?7%2'>76#"76%2#"&7567NJL V31 - ) /%1g!%y^33r >j )9=  }74 ,zV=S^'  +NAK+/>AJ{@/+'} -Z1++Z;'R\# V+%TE +  @'=P_XKPX@T : ]\H  )(<KPX@T : ]\H )(<K(PX@T : ]\H  )(<@T : ]\H )(KPX@N Z  bbXU M  = L ==L >KPX@T Z  b`bXU M  = L ==L >K(PX@L Z  bbX   UU L ==L >@P Z  b`bX   U UU=L >YYYY@#RQ?>ZXQ_R_JIDB>P?P=;4243$&4"4+6&#!"!26?6;2#"574&#!3!267!'76;276&+"577%2#".7>%2#"&756:MHHS31-)  /%1d%y^36u;j )9>  {3%a.8?T  S'  +NAK)1>AJ{@/+'} -Z1++)V%)Z #5FF;[-  @')!3Z@W+< 9bbcM =M>#"1/*)"3#3! &*! +3632 '&6#"'6#"767>32'>76#"763-E{??y3-)/^5; 71 :hB=S Zy%F^-;=#\ 9    X;;L5# X)'TC)!4@,< 9K PX@+ZbcM =M>@,bbcM =M>Y@#".-(&"4#4! &*! +3632 '&6#"'6#"767>32#".7>3-E{??y3-)/^5; o3#a,8?R  Wy%F^-;=#\ 9    )T')X #7FF;[)!2@b@_<)< 9bcM = M=M >#"?=970.('"2#2! &*! +3632 '&6#"'6#"767>32'>76&#"76&7>32'"'3-E{??y3-)/^5;  7/ x=R^33#5 y%F^-;=#\ 9   X;B# Z)TCP )%  )!3Af@c1=,< 9bcM = M=M >#"@>:8.-(&"3#3! &*! +3632 '&6#"'6#"767>32#".67>&7>32'"'3-E{??y3-)/^5; !3#^ -7RZ T3#5 y%F^-;=#\ 9   )T%)X "Z\;ZR )% )!2@@>=*< 9K PX@. ZbcM  =M >KPX@/ bbcM  =M >@3 bbc =M =M >YY@43#"<:3@4@0.)("2#2! &*! +3632 '&6#"'6#"767>32'>76#"76%2#"7563-E{??y3-)/^5; 90 -}V=T ` '  !y%F^-;=#\ 9   Z;%T\# X)%TE + @')!4C@, < 9K PX@;  b X  `bcM  =M >@<  b  `  `bcM  =M >Y@ 65#"A@>=5C6C.-(&"4#4! &*!+3632 '&6#"'6#"767>32#".7>%2"74363-E{??y3-)/^5; %3#`+7?U V' y%F^-;=#\ 9   'V%+V #7FE9Z+ @&)1!3C@7 ? +<> ;6 : 9bbc  U  UM  =M >54#"B@=;:84C5C1/*)"3#3! &*!+3632 '&6#"'6#"767>32'>76#"76%27#"&#"'6323-E{??y3-)/^5; 71 :hB=U\ =6+F+%?1+Jp=yy%F^-;=#\ 9   X>;L5% X)%TF\ XX \)1!2E@&%-= <,;$: 9K PX@<  ZbcU  U M =M >@=  bbcU  U M =M >Y@"43#"?>973E4E1/+)(&"2#2! &*!+3632 '&6#"'6#"767>327#"&#"'>322#".7>3-E{??y3-)/^5; =6-H+%=3+'X=;{v1%Z-7?S  Wy%F^-;=#\ 9   L\ XXXT\'T%)X #6EF;[1Oa@7"YJ@4bU  US K  >Y@QP_]XWPaQaOLIFC@<9633334333+%!;2&#'76;276&+"?3%+"!6&+"?3%+";2&#'76;22'>76#"76dbb +;7Q5q +=6P  7qTR *>5P8q *>5P8oh73 7gA;R^ /+''Z/+''ZO/+''Z/+''Z<=L3# V+%TF1Ob@7"ZJK(PX@6bUS K  = K  >@4bU  US K  >YY@QP\[VTPbQbOLIFC@<9633334333+%!;2&#'76;276&+"?3%+"!6&+"?3%+";2&#'76;22#".7>dbb +;7Q5q +=6P  7qTR *>5P8q *>5P8oh3%_/8?T  U/+''Z/+''ZO/+''Z/+'''T%)X #6EF;[Oao@7"kYJK(PX@7UUS K  = K  >@5U  UUS K  >YY@#QPnlhf_]XWPaQaOLIFC@<9633334333+%!;2&#'76;276&+"?3%+"!6&+"?3%+";2&#'76;22'>76#"76&7>32'"'Hdbd -;7Q5q -;6P  7qTR *>5P8q *>5P8o 73 7gA=R^ 73%5 /+''Z/+''ZO/+''Z/+''Z;=L3# V+%TEQ +% qObq@j7"1ZJK(PX@7UUS K  = K  >@5U  UUS K  >YY@#QPpnig\[VTPbQbOLIFC@<9633334333+%!;2&#'76;276&+"?3%+"!6&+"5?3%+";2&#'76;22#".7>&7>32'"'1edd *>5P8o *>5 P7qTT -;8P  5q +=5Q5q3%^/7?U  T3#3/+''Z/+''ZO/+''Z1)''&V%+V #7FE9ZO )%wO`o@7"mlXJKPX@;bSM= K  = L  >KPX@?dbSM= K  = L  >K(PX@=dbUS K  = L  >@;dbU  US L  >YYYY@'baQPjhaobo^\WVP`Q`OLIFC@<9633334333+%!;2&#'76;276&+"?3%+"!6&+"?3%+";2&#'76;22'>76#"76%2#"&7567edd *>5P8o *>5P8qTT -;8P 5s +=6P 5o 74 +{V=S^'  /+''Z/+''ZO/+''Z1)''Z;'R\# V+%TE - @'dObp@7"nmZJ KPX@;bSM= K  = L  >KPX@Ab`SM= K  = L  >K(PX@?b`US K  = L  >@=b`U  US L  >YYYY@'dcQPljcpdp\[VTPbQbOLIFC@<9633334333+%!;2&#'76;276&+"?3%+"!6&+"?3%+";2&#'76;22#".7>%2#"756%dce -;8P 5q +=5Q5qTR +>5P8q +=5 P7o3%^/7?U T'! /+''Z/+''ZO/+''Z/+'')T'+V #7FF;[- @)BOaq@em7"YJKPX@IbUUSM = K  = K  >K(PX@GbUUUS K  = K  >@EbUU  UUS K  >YYY@+cbQPpnkihfbqcq_]XWPaQaOLIFC@<9633334333+%!;2&#'76;276&+"?3%+"!6&+"?3%+";2&#'76;22'>76#"76%27#"&#"'632dee +>5P7o +;7 P7qTT -;8P 5q +=5P5q71 7hB=P ^;5+B-%?0+Hq;}/+''Z/+''ZO/+''Z1)''X;=L3" Y)%TC]ZZ ]VO`s@S[7"kJKPX@KbUSM=M = K  = K  >KPX@IbUUSM = K  = K  >K(PX@GbUUUS K  = K  >@EbUU  UUS K  >YYYY@+baQPmlgeasbs_]YWVTP`Q`OLIFC@<9633334333+%!;2&#'76;276&+"?3%+"!6&+"?3%+";2&#'76;227#"&#"'>322#".7>dbd +=5 P5q -<7P5qRT -;5 P5q -<7P6q2=6+B-%?/+%V=;{r1'[18?T  U1)''Z/+''ZO/+''Z/+''/] XX XR]'V%)X #7FE9Z*@"K1PX@,bbM==M>@*bbU=M>YY@(&! **%$" +327#"&76&#"767>32'>76"761Pb1K1PX@,bbM==M>@*bbU=M>YY@&% ,,%$" +327#"&76&#"767>32#".7>1Pb1751/(&! **%$" +327#"&76&#"767>325>76#"76&7>32'"'1Pb19731&% ,,%$" +327#"&76&#"767>32#".7>&7>32'"'1Pb1@:  b`b =M = =M>Y@,+8742+:,:(&! **%$" +327#"&76&#"767>32'>76#"76%2#"&74361Pb1KPX@/bbM  = =M>@5b`bM  = =M>YY@.-64-:.:&% ,,%$" +327#"&76&#"767>32#".7>%2#"=61Pb1-,:85320,;-;)'"!++%$" +327#"&76&#"767>32'>76#"76%27#"&#"'6321Pb1@=  bbU U M  = =M>Y@!+*650.*<+<(&#! ))%$" +327#"&76&#"767>327#"&#"'6322#".7>1Pb1@&b UUK >Y@'&53.-&7'7334333 +;2&#'76;276&+"?3%+"%2'>76#"76 -;7Q5q +=6P  7q74 8hA=S^ /)''X/+''Z<=K4# V+%TF%8@ 0 K(PX@(b UK =K >@&b UUK >YY@'&21,*&8'8334333 +;2&#'76;276&+"?3%+"%2#".7> -;8P 5q +=6P 5q/3%Z/7?U  T/)''X/+'''T%)X #6EF;[H%5C@ ?- K(PX@) U  UK =K >@' UU  UK >YY@'&B@<:31,+&5'5334333 +;2&#'76;276&+"?3%+"%2'>76#"76&7>32'"'u -;8P5q +=6P  5q74 |z;S\54$5 /)''X/+''X;D# X)'TCO)% %8F@@B0 < ;KPX@+  UM =K =K >K(PX@) U  UK =K >@' UU  UK >YY@'&EC?=21,*&8'8334333 +;2&#'76;276&+"?3%+"%2#".7>&7>32'"'; -=5 P7o +>5P8q3%[.8?T S3#3 /)''X/+'')T')X #7FF;[R +' #%7E@ CB/ KPX@1   d bM =K =L >K(PX@+ b UK =L >@-   d b UUL >YYY@98'&A?8E9E53.-&7'7334333 +;2&#'76;276&+"?3%+"%2'>76#"76%2#"756P -;8P 5q +=5Q5q71 7hB=R^ '  !/)''X/+''[;=L3# X)%TF -@'!%8G?@< ED0 KPX@- bM =K =L >KPX@3  b  `M =K =L >K(PX@+ b UK =L >@/  b  ` UUL >YYYY@:9'&B@9G:G21,*&8'8334333 +;2&#'76;276&+"?3%+"%2#".7>%2#"&756N -;8P 5q -;6P 5q3%]/7?U R'   /)''X/+'''T%)X %7DH;[- @'%7G@; C  / K(PX@9b U  U UK =K >@7b U  UU UK >YY@98'&FDA?><8G9G53.-&7'7334333+;2&#'76;276&+"?3%+"2'>76#"76%27#"&#"'632 +=5P5q -;8P 5qX92 7hB=R ^ =6+F+'=4+Jq;{/)''X/+'' Z;=L3# V+%TE\ XX \%5HE@ ) 1 @ <0;( :K PX@: Z  U UM  =K =K >KPX@; b  U UM  =K =K >K(PX@9 b  U U UK =K >@7 b  U U U UK >YYY@76'&BA<:6H7H42/-,*&5'5334333+;2&#'76;276&+"5?3%+"%27#"&#"'6322#".7>H +=5Q5q -;8P  5q;8+F+'?/+Hp={t3%_ /8?T  U/)''X/+''{\[[ \)T%+X #5FE;Z +#K1PX@)bM=M=M>@'bUM=M>YY@)'"!++  +"32676&632#"2'>76"76s m^Z'/f-m>'71 .|V=U\FV^VPZ;'R\# Y)%TF -%K1PX@)bM=M=M>@'bUM=M>YY@'&!--  +"32676&632#"2#".7>s m^Z'/f-m>'3%[ ,8?R  WFV^VP)T$)X #6EF;Z +:#@* UM= M=M>Y@9720)'"!++  +"32676&632#"2'>76#"76&7>32#"'s m^Z'/f-m>'71 -}V=T\53%5 FV^VPZ<'Q]# Y(%TFR (%  -<%@* UM= M=M>Y@;942'&!--  +"32676&632#"2#".7>&7>32#"'s m^Z'/f-m>'1%] ,8?R  W3%5 FV^VP)T%)X #6EF;[R (%   ,:7@ 087$KPX@0b =M = M=M>K!PX@,bM  = M=M>K(PX@*b  U M=M>@.b U = M=M>YYYY@ .-64-:.:*(#",,  +"32676&632#"2'>76#"76%2#"756s m^Z'/f-m>'71 9iA=T` '  FV^VPZ<;L3# X)%TF - ?'# -;98%KPX@,bM  = M=M>K(PX@2b`M  = M=M>@0b`  U M=M>YYY@ /.75.;/;'&!--  +"32676&632#"2#".7>%2#"756s m^Z'/f-m>'1%a,8?R U' !FV^VP)V%)X #8EF;[- ?' +F@C#<bUM =M> )'"!++  $" +! ! "32%2'>76#"76;Q9;3Nx9Ӵ'5;73 7gA;R^ zm'/5HB 1RZ<=L3# V+%TF3 ,x$@'bUM =M>Y@ &% ,,  $" +! ! "32%2#".7>3;R;;3Ny7Ѵ'6;Z3%\ /7?U Tzm'/5H@1R'T%)X #6EF;[` )8@ 1!@( UU M =M>Y@ 750.'% ))  $" +! ! "32%2'>76#"76&7>32'"'`;R9;3Ny7Ѵ'6;'74 }{=P \73#3zk)/5H@1RX;D# X+%TCO )%q *9@ 2"@( UU M =M>Y@ 861/$#**  $" +! ! "32%2#".7>&7>32'"'q=U9;3;7Ѵ)3;1%_/7?T  U3#3zk)/5B 1R&V%)X #7FE9ZO )%= )8 @ -65!KPX@0 dbM = M =M>K-PX@. db U M =M>@4 db` U M =M>YYY@+* 31*8+8'% ))  $" +! ! "32%2'>76#"76%2#"&756=;R9;3Nx9Ҵ(5;\74 |z;P \'  zm'/5H@1R[;B# X)'TD- @) ,:7$KPX@,bM  = M =M>KPX@2b`M  = M =M>@0b`  U M =M>YYY@.- 64-:.:&% ,,  $" +! ! "32%2#".7>%2#"?6=U9;3N{7Ѵ)5;r3%_/7?U T' zk)/5H@1R'V%+V #7DH;[- @'$5@-  K1PX@,bbM==N>@*bbU=N>YY@&%31,+%5&5('$" +#"&7>76''>3232676'&7>322'>76"765Ӌ%X 5a)7%# iY}#3wR 8'71 -}V=T`զ\);'93/HiuC-Z;'R\# Y)%TF$7@/  K1PX@,bbM==N>@*bbU=N>YY@&%10+)%7&7('$" +#"&7>76''>3232676'&7>322#".7>5Ӌ%X 5a)7%# iY}#3wR 8'3#_ -7?U Tզ\);'93/HiuC-)T$)X #6EF;Z$5D@=-  @-b U M==N>Y@&%CA<:31,+%5&5('$" +#"&7>76''>3232676'&7>322'>76#"76&7>32#"'5Ӌ%X 5a)7%# iY}#3wR 8'7/ -}V=T` 93#3 զ\);'93/HiuC-Z<%S]# Y(%TFR (% $7F@?/  @-b U M==N>Y@&%EC><10+)%7&7('$" +#"&7>76''>3232676'&7>322#".7>&7>32#"'5Ӌ%X 5a)7%# iY}#3wR 8'3#_ -7?U V3#3 զ\);'93/HiuC-)T%)X #6EF;[R (% $5Cz@A@-  KPX@/ bbM  ==N>KPX@3 bb =M ==N>K!PX@/ bbM  ==N>K(PX@- bb  U=N>@1 bb U ==N>YYYYY@76&%?=6C7C31,+%5&5('$" +#"&7>76''>3232676'&7>322'>76#"76%2#"7565Ӌ%X 5a)7%# iY}#3wR 8'7/ -}V=T` '  !զ\);'93/HiuC-Z<%T\# X)%TF - ?'+$7F@DC/  KPX@/ bbM  ==N>K(PX@5  b  `bM  ==N>@3  b  `b  U=N>YYY@98&%A?8F9F10+)%7&7('$" +#"&7>76''>3232676'&7>322#".7>%2#"&7565Ӌ%X 5a)7%# iY}#3wR 8'3#a -7?U T' զ\);'93/HiuC-)V%)X #8EF9]+ ?'w3$6F|@y: B .  87&%EC@>=;7F8F42-,%6&6('$"+#"&7>76''>3232676'&7>322'>76#"76%27#"&#"'6325Ӌ%X 5a)7%# iY}#3wR 8'72 9hB=U \?6+H+%?1+Jr;yզ\);'93/HiuC-Z<;L5% X)%TF\ XX\V+$4G@(0?  @=  bbU  U M  ==N>Y@65&%A@;95G6G31.,+)%4&4('$"+#"&7>76''>3232676'&7>3227#"&#"'6322#".7>5Ӌ%X 5a)7%# iY}#3wR 8'˵=6+F+%=3+Nn=yv1%a-7?R Uզ\);'93/HiuC-HZ ZZ Z)V%)X %7FF;[BU@(M  =K(PX@,  b   UK = K >@*  b   U U K >YY@DCONIGCUDUB?363393#5+%&'&'#"?307+"676&+"?37+";2&#'76;22#".7>3R + <1b!> +-;%^X -;8P5q!3%\/7?T  UY; )' /-  ))'3;/+'''T%)X #6EF;[BUc@] ( _M  =K(PX@, U   UK =K >@* U U   UK >YY@DCb`\ZONIGCUDU336339345+%&'&'#"&?37+"676&+"?37+";2&#'76;22#".7>&7>32'"'yT)  93c!; --<$`X *>5 P7o3%^/7?U  T3#3 Y; )' /-  ))'3;/+''&V%+V #7FF9ZO)% BUdxKPX@(baM  =<@(baM  =KPX@0   b M  =K =K >KPX@6 b  ` M  =K =K >K(PX@4 b  `   UK =K >@2 b  `   U UK >YYYY@WVDC_]VdWdONIGCUDU336339345+%&'&'#"&?37+"676&+"?37+";2&#'76;22#".7>%2#"&756yT)  93c!; --<$`X *>5 P7o3# b/7?U  T'  Y; )' /-  ))'3;/+'')T')X #7FF;[- @)yBSf@"F N( ^=KPX@AbU M = M =K = K >KPX@?b UU M =K = K >K(PX@=b U UUK = K >@;b U UUU K >YYYY@#UTDC`_ZXTfUfRPLJIGCSDSB?363393#5+%&'&'#"?307+"676&+"?37+";2&#'76;227#"&#"'>322#".7>R + ;1b!> +-;%^V +;7P5qK=6+F-$?0+%V=;{r1'[18?T  UY; )' /-  ))'3;/+''/] XX XR]'V%)X #7FE9Z/7H@@'&6 @(bUM =M>Y@98FD?>8H9H.$$-"" +#"'#".767327&7632326'7%6'"62'>76#"76+}JpZ!T''ZynRTZS1'"=-> # :71 .|V=U\ 'Xn#b))݁kwk^''+@CY^;F;uX>'R\# X)%TF/7J@B'&6 K1PX@(bUM =M>@&b UUM>YY@98DC><8J9J.$$-"" +#"'#".767327&7632326'7%6'"62#".7>+}JpZ!T''ZynRTZS1'"=-> # :1%_-8?R  W'Xn#b))݁kwk^''+@CY^;F;u)T$)X #6EF;Z/7HV@PR@'&6 @) UU  M =M>Y@98USOMFD?>8H9H.$$-"" +#"'#".767327&7632326'7%6'"62'>76#"76&7>32#"'+}JpZ!T''ZynRTZS1'"=-> # :7/ .|V=S ` 74#3  'Xn#b))݁kwk^''+@CY^;F;uZ<%S]# Y(%TFR (% /7JX@RTB'&6 @) UU  M =M>Y@98WUQODC><8J9J.$$-"" +#"'#".767327&7632326'7%6'"62#".7>&7>32#"'+}JpZ!T''ZynRTZS1'"=-> # :3#_-8?R  W4#3  'Xn#b))݁kwk^''+@CY^;F;u)T%)X #6EF;[R (% /7HWi@UT@'&6 KPX@+ bUM =N>KPX@/ bU  =M =N>K!PX@+ bUM =N>K(PX@) b UUN>@- b UU  =N>YYYYY@JI98RPIWJWFD?>8H9H.$$-"" +#"'#".767327&7632326'7%6'"62'>76#"76%2#"&756+}JpZ!T''ZynRTZS1'"=-> # :71 .|V=U\ ' 'Xn#b))݁kwk^''+@CY^;F;uZ<'R\# X)%TF - ?'/7IX@GVUB'& 6 K(PX@1  b  `UM =N>@/  b  ` UUN>YY@KJ98SQJXKXDC><8I9I.$$-"" +#"'#".767327&7632326'7%6'"62#".47>%2#"&756+}JpZ!T''ZynRTZS1'"=-> # :3%] -8RZ W' 'Xn#b))݁kwk^''+@CY^;F;u)V%)X #X_9]- ?''/7HX@|L T @'&6 JI98WURPOMIXJXFD?>8H9H.$$-""+#"'#".767327&7632326'7%6'"625>76#"76%27#"&#"'632+}JpZ!T''ZynRTZS1'"=-> # :7/ -}V;T ` ;:+H-}%?4+Nq;y'Xn#b))݁kwk^''+@CY^;F;uZ;%T\# X)%TE\ XX \+/7GZ@%; CR '& 6 @9  b  U  UU M =M>Y@IH98TSNLHZIZFDA?><8G9G.$$-""+#"'#".767327&7632326'7%6'"627#"&#"'6322#".7>+}JpZ!T''ZynRTZS1'"=-> # :A=8+H+}'=4+Lq;yw3#`+7?U V'Xn#b))݁kwk^''+@CY^;F;u%\ZZ \)V%)X %7FF9]\->@6  "KPX@/  bX   UM =N >@0  b`   UM =N >YY@/.<:54.>/>&"''!! +%32673!"7>76#"#!73;7&76! 2'>76#"76m!TH5>V!E!++R}#%P!57T$+%/#`92 ){V=S^ w)R!RѨb!N-w;aZ<%T\# X)%TF-@@8  "KPX@/  bX   UM =N >@0  b`   UM =N >YY@/.:942.@/@&"''!! +%32673!"7>76#"#!73;7&76! 2#".7>#RI5=V!F!+)R}#%P!39T%+#0#B1'[ 18?T  Uw)R!PѨb!N-w;a'T%)X #6EF;[->L@H6  "KPX@4 b   U M =M =N >@2 b   U   UM =N >YY@/.KIEC<:54.>/>&"''!!+%32673!"7>76#"#!73;7&76! 2'>76#"76&7>32'"'!TG5=V!F!+)R}#%P!39T%+#/#92 ){V=R ^ 73%5 w)R!PѨb!N-w;aZ;%T\# X)%TEQ +% m-@N@HJ8  "KPX@4 b   U M =M =N >@2 b   U   UM =N >YY@/.MKGE:942.@/@&"''!!+%32673!"7>76#"#!73;7&76! 2#".7>&7>32'"'{!RG5=X#F +)!R}##N!57T%+#/%3%a/8?T U4#3 w)R!RѨb!N-w;a&V%)X #7FE9ZO )% -?N@@7  "KPX@?  d   b   `X M=M =N >K(PX@:   b   ``   UM =N >@>  d   b   ``  UM =N >YYY@A@/.LKIG@NAN=;65.?/?&"''!!+%32673!"7>76#"#!73;7&76! 2'>76#"76%2#"7436+#RJ5=V% F +)!R#%P57V%+#/#V74 8hB=S^ '  w)R!PѨb!N-w;a[;=L3# V+%TF - @'#-@N?KPX@K8  "<@K8  "KPX@4   bX M =M =N >KPX@:   b  `X M =M =N >@9   b  ``   UM =N >YYY@BA/.JHANBN:942.@/@&"''!!+%32673!"7>76#"#!73;7&76! 2#".7>%2#"5761!RJ6@X% C!+)!R"%P58V%+#/#N3%^/7?U  T' w)R!RѨb!N-w;a'V%+V #7DH;[- @'-?O @C K 7  "KPX@C  b `  U  U M =M =N >@A  b `  U  U  UM =N >YY@ A@/.NLIGFD@OAO=;65.?/?&"''!!+%32673!"7>76#"#!73;7&76! 2'>76#"76%27#"&#"'632!RJ5@Y% C!+)!R#%P57V$+#/%74 7iA=P^?4-F+'?/+Hp;{w)R!RѨb!N-w;aX;=L3" Y)%TC]ZZ ]?-=Pl@1 9 H "<8 ;0 :K PX@C  ZX  U M = M =M =N >KPX@D  bX  U M = M =M =N >KPX@C  b `  U  U M =M =N >@A  b `  U  U  UM =N >YYY@ ?>/.JIDB>P?P<:7542.=/=&"''!!+%32673!"7>76#"#!73;7&76! 27#"&#"'6322#".7>N!RG3=X#F!+)!R}##N!57T%+#0%J;6+B-%?2+Jq;}w3%^-7?U Tw)R!RѨb!N-w;aA] XX ]'V%)X #7FE9Zqm1D@:9$@,ddM=M=M>Y@ ($&"$$'%% +54.# 32>?327327#"7"&7>32672#"/&76q+T?T "_C#D\7Bn-$./K+)VJ1FnDsͩ)xLs?  HPtg5}?T)nL^\ao{eŇj3   &qu1A@$@- ddM=N=M>Y@32;92A3A&"$$'%% +54.# 32>?327327#"7"&7>322#"&7676q+T?T "_C#D\7Bn-$./K+)VJ1FnDsͩ)xLs-!+  HPtg5}?T)nL^\ao{eŇj3/  %#qm3F,K1PX@<; <@<; K-PX@1 d  dbVM=M>K1PX@6 d  dbIUM=M>@7 d  dbUVM=M>YYY@A?75!""%$+#" +%327#".767.7>32#"&'.#"7632#"&#"672#"/&76/VXud-+H7s 8 1%9u'229 ;T Vw) ? Tu^'{4O[)=ZITZ9'%#! !Z\%) d   &qq3C)K1PX@? <@? K-PX@0  b   UUM=M>K1PX@5  b   UIUM=M>@6  b   UUUM=M>YYY@54=;4C5C!""%$+#" +%327#".767.7>32#"&'.#"7632#"&#"2#"&7476/VXud-+H7s 8 1%9u'229 ;T Vw+  Tu^'{4O[)=ZITZ9'%#! !Z\%) d'/  ##)w!4~@)< 9K!PX@&bbc=M>@#ddbcM>Y@/-%#! &*!+3632 '&6#"'6#"767>3632#"/&763-E{??y3-)/^5;  @  y%F^-;=#\ 9      %)s!1@-< 9KPX@'bbc=M>@$ddbcM>Y@#"+)"1#1! &*! +3632 '&6#"'6#"767>32#"&74763-E{??y3-)/^5; !+  y%F^-;=#\ 9   /  %#dq+y@! @#ddb=M>Y@&$%$" +327#"&76&#"767>3672#"/&761Pb1@$ddb=M>Y@"!((%$" +327#"&76&#"767>32"&74761Pb1@ ddM=M>Y@(&   +"32676&632#"632#"/&56s! n_Z&/d-m>% @  FޕV^ VPb   &u )iKPX@$b=M=M>@!ddM=M>Y@$"))   +"32676&632#"2#"7676s! n_Z&/d-m>%+ FޕV^ VP}/ %#b$6p@,  @#ddb=N>Y@ '"('$"+#"&7>76''>3232676'&7>32632#"/&765Ӌ%X 5a)7%# iY}#3wR 8'5=  զ\);'93/HiuC-   )u$3t@  @$ddb=N>Y@&%-,%3&3('$"+#"&7>76''>3232676'&7>322"&76765Ӌ%X 5a)7%# iY}#3wR 8'+  զ\);'93/HiuC-/  %#\/7Jo@?'&6 @ddUN>Y@ (&.$$-""+#"'#".767327&7632326'7%6'"6632#"/&76+}JpZ!T''ZynRTZS1'"=-> # :  =  'Xn#b))݁kwk^''+@CY^;F;u    )y/7Gs@'&6 @ ddUN>Y@98A?8G9G.$$-"" +#"'#".767327&7632326'7%6'"62#"&7676+}JpZ!T''ZynRTZS1'"=-> # :+  'Xn#b))݁kwk^''+@CY^;F;u/  %#q1BR@: $LKED   Z M =M=M=M= M  >@?  b M =M=M=M= M  >Y@32PNIG@>982B3B&"$$'%% +54.# 32>?327327#"7"&7>3225>76#"7673267#"&7q+T?T "_C#D\7Bn-$./K+)VJ1FnDsͩ)xLs71 -}V=R\ n)!D;/'v6NQHPtg5}?T)nL^\ao{eŇj3>[;'R\# V+%TF m>+^NVTq1DT@<  $NMGF   ZM =M=M=M= M  >@?  bM =M=M=M= M  >Y@32RPKI>=862D3D&"$$'%% +54.# 32>?327327#"7"&7>322#".7>73267#"&7q+T?T "_C#D\7Bn-$./K+)VJ1FnDsͩ)xLs3#a-7?S Vn)!D;/'v6NQHPtg5}?T)nL^\ao{eŇj3>)T')X #7FF;[ m>+^NVTq1BQa~@{J : $[ZTS < M=  M =M=M=M= M  >32_]XVPNIG@>982B3B&"$$'%%+54.# 32>?327327#"7"&7>322'>76#"76&7>32'"'73267#"&7q+T?T "_C#D\7Bn-$./K+)VJ1FnDsͩ)xLs70 -}V=T ` 73#3 n)!D;/'v6NQHPtg5}?T)nL^\ao{eŇj3>[;%T\# X)%TFR +%7 m>+^NVTq1DRb@|LN<  $\[UT <M=  M =M=M=M= M  >32`^YWQOKI>=862D3D&"$$'%%+54.# 32>?327327#"7"&7>322#".7>&7>32'"'73267#"&7q+T?T "_C#D\7Bn-$./K+)VJ1FnDsͩ)xLs3#_-7?R  Y4"3  n)!D;/'v6NQHPtg5}?T)nL^\ao{eŇj39)T%+X #7FE;ZR )% ; m>+^NVTq0O_WK-PX@6B=<71YXRQ <@6B=<71YXRQ K-PX@Fb=M=M = N  = M  = M  >@Lb`=M=M = N  = M  = M  >YY@&][VTMKECA?;953,*%#  +2'>76#"76%2#"75654.# 32>?327327#"7"&7>3273267#"&770 {=S `'! A+T?T "_C#D\7Bn-$./K+)VJ1FnDsͩ)xLsn)!D;/'v6NQX;B# X)'TC- @)HPtg5}?T)nL^\ao{eŇj3 m>+^NVTq1DTdDKPX@RQ<  $^]WV <@RQ<  $^]WV KPX@B   bM =M=N=M= M  >@H   b  `M =M=N=M= M  >YY@FE32b`[YNLETFT>=862D3D&"$$'%%+54.# 32>?327327#"7"&7>322#".7>%2#".75673267#"&7q+T?T "_C#D\7Bn-$./K+)VJ1FnDsͩ)xLs3%] ,8?T U' n)!D;/'v6NQHPtg5}?T)nL^\ao{eŇj39)T%)Z #7FE;Z - @) m>+^NVTq1!1Dc@+%-J VQPKE2  <,;$:K-PX@P  bUUM = M = M = M =M>@M  bUUQM = M = M = M  >Y@(#"a_YWUSOMIG@>970.+)(&"1#1!!%$ +73267#"&72'>76#"76%27#"&#"'63254.# 32>?327327#"7"&7>32To)#F;/'x4PO70 9hB=R ` ;:+H+'=4+Nq;y+T?T "_C#D\7Bn-$./K+)VJ1FnDsͩ)xLsj>+^NTVZ<?J5% X)'TF\ VV\HPtg5}?T)nL^\ao{eŇj3q11ATd@+5 =L  $^]WV<<;4 :K PX@O  Z  U U M =M=M=M=M>@P  b  U U M =M=M=M=M>Y@#CB32b`[YNMHFBTCT@>;9862A3A&"$$'%%+54.# 32>?327327#"7"&7>3227#"&#"'6322#".7>73267#"&7q+T?T "_C#D\7Bn-$./K+)VJ1FnDsͩ)xLs=6-H+'=4+Lq;{w3#^-7?S Vn)!D;/'v6NQHPtg5}?T)nL^\ao{eŇj3}\ XX\'T%)X #6EF;[ m>+^NVT)29JZGK-PX@B5 ML UT&<@B5 ML UT &K-PX@0  b UV = K  >K1PX@;  b U V = K  =K  >@8  b U V =K = M >YYY@;:43XWRPHFA@:J;J3949$334#334+%;2&#'76;27672;2&#'76;276'!"7!2'>76#"7673267"&7;--90hD%1 <9P9\?R;oa92 ){V=R^ 7P'+1'dG ''m`=>=:''8a#s" Z<%T\# X)%TF;#]`H17 bJVV29L[@D5 N VU &@;  b U V = K  =K  >Y@;:43YXSQFE@>:L;L3949$334#334+%;2&#'76;27672;2&#'76;276'!"7!2#".7>73267"&7;--90hD%1 <9P9\?R;oa3%_.8?T  SP!<'+1'dH ''m`=>=:''8a#s" 'T%)X #6EF;[=%]/7dHVVZ3:LZj@VD6 ]\9 ed'   UV = M =K =K >@< U   UV =K =K >Y@"<;54hgb`YWSQJHCB;L76#"76&7>32#"'73267"&7+-;3;L#%/ <;P9` =P;ma73 7gA;RZ 53%5 R!%+1'dG''67`=>=:''8a#s" [;=L3# V+%TFR+' =%]^H/7dHVV#X3;N\m@XF6 _^9 gf'   UV = M =K =K >@< U   UV =K =K >Y@"=<54kidb[YUSHGB@&7>32'"'73267#"&7Z+-;3;L#%!- <;P:` =P9l`3%`/7?U R3#5 R!%-/'dJHI ''67`=>=:''8a#s"  z&V%)X #7FF;XO )% #=%]^H/7dHVVT2:JYjK-PX@B \[8 dc&<5;@B \[8 dc&<5;YK PX@J d  b   XV = M =K =K >KPX@K d  b   `V = M =K =K >K-PX@I d  b   `  UV =K =K >@O d  b `  `  UV =K =K >YYY@(LK<;43hfa_WVTRKYLYHFA@;J76#"76%2#"543673267#"&7J+-<1 hD%/ m  O7^@P9o`73 }{=P b'  R!%,/'dJHI ''m`=>w''8a#s"  }[;B# X)'TD- @)=%]^H/7dHVVH2:M\kKPX@Q ZYE5 _^8 fe&<@Q ZYE5 _^8 fe&KPX@@   bV = M  =K =K >KPX@F b  `V = M  =K =K >@D b  `  UV =K =K >YYY@&ON<;43ihcaWUN\O\GFA?;M%2#"&75673267"&7J+-<1 hD%/ m  O7^@P9o`3%[/8?T  U'   O!;'+1'eH ''m`=>w''8a#s"  )T'+V #7FF;[- @)=%]/7dHVV/b3<M]mX@+QY 6 E _9 hg'KPX@N  b U UV = M =K =K >@L  b U U UV =K =K >YY@*ON>=54kjec\ZWUTRN]O]KIDC=M>M4<5<$334$334+%;2&#'76;267672;2&#'76;276'!"7!2'>76#"76%27#"&#"'63273267"&7d+/;3;L#%/ <;P9` =P;oa92 ){V=S^ ;6+B+'?2+Jq;{fP!%+1'dH ''67`=>=:''8a#s" Z;%T\" Y)%TE] XX ]=%]^H/7dHVV3s3:K^o@+> F 6Va`9 ih'KPX@R  bU  V M = = M =M =M >KPX@P  b UU  V = M =M =M >@N  b U  UU  V =M =M >YYY@+ML<;54mkfdXWRPL^M^JHDBA?;K322#".7>73267#"&7j\;3;M"%/ <;O9` ?R;ma'=6+F-%;3+'U>;{u3&\/7?T  TQ !%-/'eIHJ3''67`=>=:''8a#s" $]ZZ XT])T%+V #5FE;Z=%]^H/7dHVV)!3B@+<;65  <  9K!PX@9bb`M =M = M  >@6bb`  QM =M >Y@#"@>:81/*)"3#3! &*! +3632 '&6#"'6#"767>32'>76#"767327#"&73-E{??y3-)/^5; 71 :hB=S Zbp)#E54/)w3NRy%F^-;=#\ 9    X;;L5# X)'TCmi^NVT)!4C@,=<76  <  9K PX@8Zb`M =M = M  >K!PX@9bb`M =M = M  >@6bb`  QM =M >YY@#"A?;9.-(&"4#4! &*! +3632 '&6#"'6#"767>32#".7>7327#"&73-E{??y3-)/^5; o3#a,8?R  Wyp)#E54/)w3NRy%F^-;=#\ 9    )T')X #7FF;[mi^NVT)!2@O@ <)IHCB  <  9K!PX@<b  `M = M=M = M  >@9b  ` QM = M=M >Y@#"MKGE?=970.('"2#2! &*!+3632 '&6#"'6#"767>32'>76&#"76&7>32'"'7327#"&73-E{??y3-)/^5;  7/ x=R^33#5 ep)#E54/)w3NRy%F^-;=#\ 9   X;B# Z)TCP )%  `mi^NVT)!3AP@$1=,JIDC  <  9K!PX@<b  `M = M=M = M  >@9b  ` QM = M=M >Y@#"NLHF@>:8.-(&"3#3! &*!+3632 '&6#"'6#"767>32#".67>&7>32'"'7327#"&73-E{??y3-)/^5; !3#^ -7RZ T3#5 [p)#E54/)w3NRy%F^-;=#\ 9   )T%)X "Z\;ZR )% hmi^NVT)!2@OY@!>=*IHCB  <  9K PX@; Zb  `M =M = M  >KPX@< bb  `M =M = M  >K!PX@@ bb  `=M =M = M  >@= bb  ` Q=M =M >YYY@"43#"MKGE<:3@4@0.)("2#2! &*!+3632 '&6#"'6#"767>32'>76#"76%2#"7567327#"&73-E{??y3-)/^5; 90 -}V=T ` '  !op)#E54/)w3NRy%F^-;=#\ 9   Z;%T\# X)%TE + @'mi^NVT)!4CR3@, LKFE  <  9K PX@H  b X  `b  `M=M = M  >K!PX@I  b  `  `b  `M=M = M  >@F  b  `  `b  ` QM=M >YY@$65#"PNJHA@>=5C6C.-(&"4#4! &*!+3632 '&6#"'6#"767>32#".7>%2"74367327#"&73-E{??y3-)/^5; %3#`+7?U V' \p)#E54/)w3NRy%F^-;=#\ 9   'V%+V #7FE9Z+ @&mi^NVT#10BR@F N : *)#DC21QOLJIGCRDR@>981B2B0/&*!$$+7727#"&73632 '&6#"'6#"767>32'>76#"76%27#"&#"'632n)!C541)y3NR-E{??y3-)/^5; 71 :hB=U\ =6+F+%?1+Jp=ykh^LTTy%F^-;=#\ 9   X>;L5% X)%TF\ XX \)1!2ETI@0%-= NMHG  <,;$:  9K PX@I  Zb  `U U M =M= M  >K!PX@J  bb  `U U M =M= M  >@G  bb  `U U Q M =M>YY@&43#"RPLJ?>973E4E1/+)(&"2#2! &*!+3632 '&6#"'6#"767>327#"&#"'>322#".7>7327#"&73-E{??y3-)/^5; =6-H+%=3+'X=;{v1%Z-7?S  Wdp)#E54/)w3NRy%F^-;=#\ 9   L\ XXXT\'T%)X #6EF;[=mi^NVTOaq@7"YclkdJ@CbU  US K  = M>Y@#QPonig_]XWPaQaOLIFC@<9633334333+%!;2&#'76;276&+"5?3%+"!6&+"?3%+";2&#'76;22'>76#"7673267"&7ddd +=5Q5q-;6P  7oTT ,>5P8o ,<5R6qh71 7iA=P^ $R!%+1'dG1)''Z/+''ZO/+''Z/+''X>=L3# X)%TF;%\^H27 bJVVObr@7"ZdmlJK(PX@CbUS K  =K  = K  >@AbU  USK  = K  >YY@#QPpojh\[VTPbQbOLIFC@<9633334333+%!;2&#'76;276&+"?3%+"!6&+"?3%+";2&#'76;22#".7>73267"&7dbb +;7Q5q +=6P  7qTR *>5P8q *>5P8oh3%_/8?T  UP!%+1'dH/+''Z/+''ZO/+''Z/+'''T%)X #6EF;[=%]^H/7dHVV ROao1@%id7"8kYqzyrJK(PX@DUUS K  =K  = K  >@BU  UUSK  = K  >YY@'QP~|wunlhf_]XWPaQaOLIFC@<9633334333+%!;2&#'76;276&+"5?3%+"!6&+"?3%+";2&#'76;22'>76#"76&5>32'"'73267#"&7Hddf+=5Q5q-;6P  7oTT ,>5P8o ,<5R6q 73 7gA;R^ 93#3 ͋R %-/'dJHI1)''Z/+''ZO/+''Z/+''Z;=L3# V+%TEQ+% #=%]^H/7dHVV ?Obq.@"j7"1Zs|{tJK(PX@DUUS K  =K  = K  >@BU  UUSK  = K  >YY@'QP~ywpnig\[VTPbQbOLIFC@<9633334333+%!;2&#'76;276&+"?3%+"!6&+"5?3%+";2&#'76;22#".7>&7>32'"'73267#"&71edd *>5P8o *>5 P7qTT -;8P  5q +=5Q5q3%^/7?U  T3#3ՋR!%-/'dJHG/+''Z/+''ZO/+''Z1)''&V%+V #7FE9ZO )%%=%]^H/7dHVV \O`o@7"mlXqzyrJKPX@HbSM= K  =K  = L  >KPX@LdbSM= K  =K  = L  >K(PX@JdbUS K  =K  = L  >@HdbU  USK  = L  >YYYY@+baQP}|wujhaobo^\WVP`Q`OLIFC@<9633334333+%!;2&#'76;276&+"?3%+"!6&+"?3%+";2&#'76;22'>76#"76%2#"&75673267"&77edd *>5P8o *>5P8qTT -;8P 5s +=6P 5o 74 +{V=S^'  R!%+1'dG/+''Z/+''ZO/+''Z1)''Z;'R\# V+%TE - @'=%]^H/7dHVV BObp@7"nmZrzyJ KPX@HbSM= K  =K  = L  >KPX@Nb`SM= K  =K  = L  >K(PX@Lb`US K  =K  = L  >@Jb`U  USK  = L  >YYYY@+dcQP}|wuljcpdp\[VTPbQbOLIFC@<9633334333+%!;2&#'76;276&+"?3%+"!6&+"?3%+";2&#'76;22#".7>%2#"75673267"&7%dce -;8P 5q +=5Q5qTR +>5P8q +=5 P7o3%^/7?U T'! P!<'+2'eH/+''Z/+''ZO/+''Z/+'')T'+V #7FF;[- @)=%]/7dHVV /Oaq@(em7"Ys|{tJKPX@VbUUSM = K  =K  = K  >K(PX@TbUUUS K  =K  = K  >@RbUU  UUSK  = K  >YYY@/cbQP~ywpnkihfbqcq_]XWPaQaOLIFC@<9633334333+%!;2&#'76;276&+"?3%+"!6&+"?3%+";2&#'76;22'>76#"76%27#"&#"'63273267#"&7dee +>5P7o +;7 P7qTT -;8P 5q +=5P5q71 7hB=P ^;5+B-%?0+Hq;}R!%-/'dJHI/+''Z/+''ZO/+''Z1)''X;=L3" Y)%TC]ZZ ]=%]^H/7dHVV ?O_r-@(S[7"jt}|uJKPX@XbUSM=M = K  =K  = K  >KPX@VbUUSM = K  =K  = K  >K(PX@TbUUUS K  =K  = K  >@RbUU  UUSK  = K  >YYYY@/a`QPzxlkfd`rar^\YWVTP_Q_OLIFC@<9633334333+%!;2&#'76;276&+"?3%+"!6&+"?3%+";2&#'76;227#"&#"'6322#".7>73267#"&7dbd -;7P7o -<7P 5qTT -;5 P5q ->5P8o4;6+B-%?/+Hp={t1'[/8?T  S݋R!%-/'dJHG/+''Z/+''ZO/+''Z/+''/] XX ]'V%)X #7FE;X=%]^H15dHVV/7HW@@'&6 QPKJ @2bUM =M= M  >Y@98USOMFD?>8H9H.$$-"" +#"'#".767327&7632326'7%6'"62'>76#"767327#"&7+}JpZ!T''ZynRTZS1'"=-> # :71 .|V=U\ q)!F530)w3PP'Xn#b))݁kwk^''+@CY^;F;uX>'R\# X)%TF mi`LSW/7JY@B'&6 SRML K1PX@2bUM =M= M  >@0b UUM= M  >YY@98WUQODC><8J9J.$$-"" +#"'#".767327&7632326'7%6'"62#".7>7327#"&7+}JpZ!T''ZynRTZS1'"=-> # :1%_-8?R  Wq)!F530)w3PP'Xn#b))݁kwk^''+@CY^;F;u)T$)X #6EF;Z mi`LSW/7HVe@ PR@'&6 _^YX @3 UU  M =M= M  >Y@98ca][USOMFD?>8H9H.$$-""+#"'#".767327&7632326'7%6'"62'>76#"76&7>32#"'7327#"&7+}JpZ!T''ZynRTZS1'"=-> # :7/ .|V=S ` 74#3  q)!F530)w3PP'Xn#b))݁kwk^''+@CY^;F;uZ<%S]# Y(%TFR (% O mi`LSW/7JXg@ RTB'&6 a`[Z @3 UU  M =M= M  >Y@98ec_]WUQODC><8J9J.$$-""+#"'#".767327&7632326'7%6'"62#".7>&7>32#"'7327#"&7+}JpZ!T''ZynRTZS1'"=-> # :3#_-8?R  W4#3  q)!F530)w3PP'Xn#b))݁kwk^''+@CY^;F;u)T%)X #6EF;[R (% O mi`LSW/7HWf@UT@'&6 `_ZY KPX@5 bUM =N= M  >KPX@9 bU =M =N= M  >K!PX@5 bUM =N= M  >K(PX@3 b UUN= M  >@7 b UU =N= M  >YYYYY@JI98db^\RPIWJWFD?>8H9H.$$-""+#"'#".767327&7632326'7%6'"62'>76#"76%2#"&7567327#"&7+}JpZ!T''ZynRTZS1'"=-> # :71 .|V=U\ ' q)!F530)w3PP'Xn#b))݁kwk^''+@CY^;F;uZ<'R\# X)%TF - ?' mi`LSW/7IXg@!GVUB'& 6 a`[Z K(PX@;  b  `UM =N= M  >@9  b  ` UUN= M  >YY@KJ98ec_]SQJXKXDC><8I9I.$$-""+#"'#".767327&7632326'7%6'"62#".47>%2#"&7567327#"&7+}JpZ!T''ZynRTZS1'"=-> # :3%] -8RZ W' q)!F530)w3PP'Xn#b))݁kwk^''+@CY^;F;u)V%)X #X_9]- ?' mi`LSW'/7HXh@L T @'&6 ba[Z JI98fd_]WURPOMIXJXFD?>8H9H.$$-""+#"'#".767327&7632326'7%6'"625>76#"76%27#"&#"'63273267#"&7+}JpZ!T''ZynRTZS1'"=-> # :7/ -}V;T ` ;:+H-}%?4+Nq;yq)!D;/)v3NR'Xn#b))݁kwk^''+@CY^;F;uZ;%T\# X)%TE\ XX \ m>+`LVT+/7GZi@,; CR '& 6 cb]\ @C  b  U UU M =M= M>Y@"IH98gea_TSNLHZIZFDA?><8G9G.$$-""+#"'#".767327&7632326'7%6'"627#"&#"'6322#".7>7327#"&7+}JpZ!T''ZynRTZS1'"=-> # :A=8+H+}'=4+Lq;yw3#`+7?U Vq)!F530)w3PP'Xn#b))݁kwk^''+@CY^;F;u%\ZZ \)V%)X %7FF9] mi`LSWs-?P@7 BA JI"@=  b`   UM =N  = M  >Y@/.NLGE=;65.?/?&"''!!+%32673!"7>76#"#!73;7&76! 2'>76#"7673267#"&7#RJ5=V% F +)!R#%P57V%+#/#a73 7gA;R^ R %-/'eIHJw)R!PѨb!N-w;aZ<=L3# V+%TF="\^J17 dHVV\-@Q@8 CB KJ"KPX@<  bX   UM =N  = M  >@=  b`   UM =N  = M  >YY@/.OMHF:942.@/@&"''!!+%32673!"7>76#"#!73;7&76! 2#".7>73267#"&7#RI5=V% F +)!R#%P59T%+#/#B1'[ 18?T  UR!%-/'dJHIw)R!PѨb!N-w;a'T%)X #6EF;[="\^J17 bJVV->L\@H6 ON WV"KPX@A b   U M=M =N = M >@? b  U   UM =N = M >YY@/.ZYTRKIEC<:54.>/>&"''!!+%32673!"7>76#"#!73;7&76! 2'>76#"76&7>32'"'73267"&7!TG5=V!F!+)R}#%P!39T%+#/#92 ){V=R ^ 73%5 \P'+1'dHw)R!PѨb!N-w;aZ;%T\# X)%TEQ +% '="\^J17 bJVV-@N^ @HJ8 P YX"KPX@A b   U M=M =N = M >@? b  U   UM =N = M >YY@/.\[VTMKGE:942.@/@&"''!!+%32673!"7>76#"#!73;7&76! 2#".7>&7>32'"'73267"&7{!RG5=X#F +)!R}##N!57T%+#/%3%a/8?T U4#3 kP'+2'eHw)R!RѨb!N-w;a&V%)X #7FE9ZO )% )="\^J17 bJVV-?N_|@7 QP YX"KPX@L  d   b   `X M=M =N =M >K(PX@G   b   ``   UM =N =M >@K  d   b   ``  UM =N =M >YYY@"A@/.][VTLKIG@NAN=;65.?/?&"''!!+%32673!"7>76#"#!73;7&76! 2'>76#"76%2#"743673267#"&7+#RJ5=V% F +)!R#%P57V%+#/#V74 8hB=S^ '  IR %-/'dJHJw)R!PѨb!N-w;a[;=L3# V+%TF - @'="\^J17 dHVV-@N^}KPX@K8 P YX"<@K8 P YX"KPX@A   bX M =M =N = M >KPX@G   b  `X M =M =N = M >@F   b  ``   UM =N = M >YYY@ BA/.\[VTJHANBN:942.@/@&"''!!+%32673!"7>76#"#!73;7&76! 2#".7>%2#"57673267"&71!RJ6@X% C!+)!R"%P58V%+#/#N3%^/7?U  T' bP'+2'eHw)R!RѨb!N-w;a'V%+V #7DH;[- @'="\^J17 bJVV{-?O_O@#C K 7 RQ YX"KPX@P  b `  U  U M =M =N =M >@N  b `  U  U  UM =N =M >YY@$A@/.][VTNLIGFD@OAO=;65.?/?&"''!!+%32673!"7>76#"#!73;7&76! 2'>76#"76727#"&#"'63273267#"&7!TG5=V!F!+)R}#%P!39T%+#/#73 7gA=RZ =4-F+';3+Hp={R<'+/%fJHGw)R!PѨb!N-w;aX;=L3" W+%TC]ZZ ]="\17 dHVV-=P`@#1 9 H SR[Z"<8 ;0 :K PX@P  ZX  U M = M =M =N =M >KPX@Q  bX  U M = M =M =N =M >KPX@P  b `  U  U M =M =N =M >@N  b `  U  U  UM =N =M >YYY@$?>/.^]XVJIDB>P?P<:7542.=/=&"''!!+%32673!"7>76#"#!73;7&76! 27#"&#"'6322#".7>73267"&7+#RJ5=V% F +)!R#%P57V%+#/#H=4-F+'?/+Hp;}u1'Z/7?U  R`R!%+1'dGw)R!PѨb!N-w;aA] XX ]'V%)X #7FE;X="\^J17 bJVVq11?M@J$<982:UM=M=M>%%&"$$'%% +54.# 32>?327327#"7"&7>323267#"&7q+T?T "_C#D\7Bn-$./K+)VJ1FnDsͩ)xLsoh\P/+\uuHPtg5}?T)nL^\ao{eŇj3NlbX{q1=N@K$< UM=M=M>42:72=4=&"$$'%% +54.# 32>?327327#"7"&7>32!"7>3!2q+T?T "_C#D\7Bn-$./K+)VJ1FnDsͩ)xLs#  #'HPtg5}?T)nL^\ao{eŇj3''qm1DT@:9$NMGF @6ddM=M=M= M  >Y@RPKI($&"$$'%% +54.# 32>?327327#"7"&7>32672#"/&7673267#"&7q+T?T "_C#D\7Bn-$./K+)VJ1FnDsͩ)xLs?  n)!D;/'v6NQHPtg5}?T)nL^\ao{eŇj3   &! m>+^NVTq1AP@M$;:43<:M=M=M=M>%'&"$$'%% +54.# 32>?327327#"7"&7>3273267#"&7q+T?T "_C#D\7Bn-$./K+)VJ1FnDsͩ)xLsn)!D;/'v6NQHPtg5}?T)nL^\ao{eŇj3 m>+^NVTqu1AQ@$KJDC @7 ddM=N=M= M  >Y@32OMHF;92A3A&"$$'%% +54.# 32>?327327#"7"&7>322#"&767673267#"&7q+T?T "_C#D\7Bn-$./K+)VJ1FnDsͩ)xLs-!+  !n)!D;/'v6NQHPtg5}?T)nL^\ao{eŇj3/  %# m>+^NVTq#1A@ 5 =$<<;4 :KPX@5 U M =M=M=M>@3  U UM=M=M>Y@32@>;9862A3A&"$$'%% +54.# 32>?327327#"7"&7>3227#"&#"'632q+T?T "_C#D\7Bn-$./K+)VJ1FnDsͩ)xLs=;:+H+}'=4+Js;yHPtg5}?T)nL^\ao{eŇj3o\ XX \q#1AQ@'5 =$KJDC <<;4 :KPX@? U M =M=M=M= M  >@=  U UM=M=M= M  >Y@32OMHF@>;9862A3A&"$$'%%+54.# 32>?327327#"7"&7>3227#"&#"'63273267#"&7q+T?T "_C#D\7Bn-$./K+)VJ1FnDsͩ)xLs=;:+H+}'=4+Js;y)n)!D;/'v6NQHPtg5}?T)nL^\ao{eŇj3o\ XX \ m>+^NVTq29GV@S5&<  d  U V =K >::43:G:GECA@><3949$334#334+%;2&#'76;27672;2&#'76;276'!"7!#"&733267;--90hD%1 <9P9\?R;oajs 9oTP ''m`=>=:''8a#s" -h}k9DC:'29EL@I5&<   U V =K ><:43B?:E3!2;--90hD%1 <9P9\?R;oa %& ''m`=>=:''8a#s" ))29ML@I5 &<  d  b V =K >43HF=;3949$334#334 +%;2&#'76;27672;2&#'76;276'!"7!6320#"/&76;--90hD%1 <9P9\?R;oaC   ''m`=>=:''8a#s" }    %29HR@OD5 &<   d  b V =K >;:43BA:H;H3949$334#334 +%;2&#'76;27672;2&#'76;276'!"7!2"&7476;--90hD%1 <9P9\?R;oa#,  ''m`=>=:''8a#s" /   %#TD29JK-PX@<;5DC&<@<;5DC &K1PX@*  V = K  =K  >@'  V =K = M >YY@43HFA?3949$334#334 +%;2&#'76;27672;2&#'76;276'!"7!%73267#"&7;--90hD%1 <9P9\?R;oa R!%,/'dJHI ''m`=>=:''8a#s" 3;#]`H17 bJVV7D@eM >Y@   +2'>76#"7670 .}V=U` 7Z;%T\# X)%TE4 :KPX@ M>@IMAY%& +%73267#"&7#X %//)fJHEy;#]?K17 dHVV7D@eM >Y@   +2'>76#"7670 .}V=U` 7Z;%T\# X)%TE`]@ <: 9K$PX@QM >@UIMAY@  +27#"&#"'632F;9+H+}'=6+Nq;y\ XX \r9v@<;:KPX@UQM>@$UUIMAY@ +>2"$>2"27#"&#"'632j?N+BNAN+BNN;:+H+'?2+Nq;yoM88M66M88M6\ YY \)w!4C@)=<76 < 9K!PX@3bb`=M =M>@-ddb`QM >Y@A?;9/-%#! &*! +3632 '&6#"'6#"767>3632#"/&767327#"&73-E{??y3-)/^5;  @  p)#E54/)w3NRy%F^-;=#\ 9      %=mi^NVT)!0@*)$# < 9K!PX@&b`M=M>@#b`QM>Y@.,(&! &*!+3632 '&6#"'6#"767>37327#"&73-E{??y3-)/^5; p)#E54/)w3NRy%F^-;=#\ 9   mi^NVT)s!1@@-:943 < 9KPX@4bb` =M =M>K!PX@1 ddb`M =M>@. ddb`QM >YY@#"><86+)"1#1! &*! +3632 '&6#"'6#"767>32#"&74767327#"&73-E{??y3-)/^5; !+  p)#E54/)w3NRy%F^-;=#\ 9   /  %#mi^NVT)j!1@"%-<,;$: 9KPX@,bc UM =M >@*bcU UM >Y@#"0.+)(&"1#1! &*! +3632 '&6#"'6#"767>327#"&#"'6323-E{??y3-)/^5; ;9+H+%B1+Nq;yy%F^-;=#\ 9   :\ YY \)j!1@@,%-:943  <,;$:  9KPX@9b  ` UM =M = M  >@4b  `U U QM >Y@#"><860.+)(&"1#1! &*! +3632 '&6#"'6#"767>327#"&#"'6327727#"&73-E{??y3-)/^5; ;9+H+%B1+Nq;yn)#C74-'w5NPy%F^-;=#\ 9   :\ YY \ mh^NVT=N@D  )(K(PX@E d Z  bbXU L  ==L >@C d Z  bbX  UU=L >YY@IHA?=;4243$&4"4 +6&#!"!26?6;2#"=4&#!3!267!'76;276&+"?7%672"/&767NJL T32 - ) 0%1g!%x^33s =j )9= VD +NAK+/>AJ{@/+'} -Z1++   &;K@G  '@E  d Z  bbZ  UU=L >Y@=<ECh ';;  *  +NAK)1>^`@/+' -Z1++/  %#LOb@7"XJ@0db  US L  >Y@][RQOLIFC@<9633334333+%!;2&#'76;276&+"?3%+"!6&+"?3%+";2&#'76;262#"/&76 edd *>5P8o *>5P8qTT -;8P 5s +=6P 5o  D  /+''Z/+''ZO/+''Z1)''    #9O^@ 7"J@1db  US L  >Y@QPYWP^Q^OLIFC@<9633334333+%!;2&#'76;276&+"?3%+"!6&+"?3%+";2&#'76;22#"&?6bee +=6P 7o +;7Q5sRT -<7P6q +=5 P5q+  1)''Z/+''ZO/+''Z1)''/  %#%D+O_@7"QYXJD@0  USK  = K  >Y@][VTOLIFC@<9633334333+%!;2&#'76;276&+"?3%+"!6&+"?3%+";2&#'76;273267#"&79ebd ,<5R6q ,<5R6qRT -;5Q5q -;8P 5q#R<'+2)eGJH/+''Z/+''ZO/+''Z/+''F;"\17 dHVV7B5@2<M =M >  +2'>76#"76&7>32'"'7/ -}V=T`53#3  3X;%T\" Y)%TCP )% \;K-PX@e =M >@bcU >YY@  +2'>76#"76%2"?671 .|V=U\' -X;'R\" Y)%SD+ =)\7 V@S<;:eUM=M >    +25>76#"76%27#"&#"'632P7/ -}V;T` ;9+H-{'B1+Nq;yZ<%S]# X)%TF\ XX \H1'E@B%#%$" +327#"&76&#"767>33267#"&71Pb1;VJq )/`6; h\P/+\uuPb'H`cZ;! 9   NlbX{5%D@A<bU=M>"%%%$" +327#"&76&#"767>3%!"7>3!21Pb1;9/-+)&%"!%$" +327#"&76&#"767>3&>32"$>2#"632#"/&761Pb1-,53,:-:+*&%"!%$" +327#"&76&#"767>3&>32"$>2"2#"74761Pb1@*bU U=M>Y@(&#! ))%$" +327#"&76&#"767>327#"&#"'6321Pb1\ XX \!)9@- 5 <4 ;, :KPX@6b  UU M = =M>@4b  U   UU =M>Y@+*86310.*9+9)(%$! $$" +327#"&76#"767>3&>2"$>2"27#"&#"'63250Pb1@$ dUUK >Y@&&&3&3"#334333 +;2&#'76;276&+"?3%+"#"&733267m +=5 P5q -<7P5qhs :pTP/)''X/+''h}k9DC:%y-%1h@  @UUK >Y@(&.+&1(1334333 +;2&#'76;276&+"?3%+"7!"7>3!2b ,<5R6q ,<5R6q %&/)''X/+''))%9n@ / @"dbUL >Y@ )"334333+;2&#'76;276&+"?3%+"%632#"/&765 ,>5P8o *>5P8qD  /)''X/+''   %%4r@  @#dbUL >Y@'&/-&4'4334333 +;2&#'76;276&+"?3%+"%2#"&?6# -;8P 5q -;6P 5qc*  /)''X/+''/ %#47B 5@2 <M =M >  +2#".7>&7>32'"'3#a,8?R U3#3  3'V%)X "7FE;XP )% 2s; ] @bcM >Y@   +2#".7>%2"5761%] -8?R  W'  3)T%)X "5FE;Z- =)L"@ <:K PX@#YUUM >KPX@"eUUM >@(eUUIMAYY@""  +27'"&#"'6322#".7>/?6+H-}%=3+Lp;yw3&\ -7?U V\ZZ \)T%+X #7FE;Z)1$2=@:  <,+%:bU=N>%#('$"+#"&7>76''>3232676'&7>323267#"&75Ӌ%X 5a)7%# iY}#3wR 8'i\P/+\utզ\);'93/HiuC-NlbX{-$0>@;  <bU=N>'%-*%0'0('$"+#"&7>76''>3232676'&7>32'!"7>3!25Ӌ%X 5a)7%# iY}#3wR 8'{ %%զ\);'93/HiuC-))3$-6IR@O?>=  <  d  bbU=N>DB:8('$" +#"&7>76''>3232676'&7>32$>2"$>2"632#"/&765Ӌ%X 5a)7%# iY}#3wR 8'BM+ ?N?N+DN? զ\);'93/HiuC-N55''55N55''5    '7J$/:IQ@N  <   d  bbU=N><;CB;I76''>3232676'&7>32>2#"&%>32"&2"&76765Ӌ%X 5a)7%# iY}#3wR 8'BN+ B'%+PD'%+BN+* զ\);'93/HiuC- '78&%88%'78&%88/  %!`%6@.!<9K PX@'ZM=M=M>@(bM=M=M>Y@'&42-,&6'6&($*+'6>7632#"'" >73262'>76#"76հNATM8+%1oUXL\/ RM!70 -}V=T ` N@#8>$y.PkK/V/LZ<%T\# X)%TF`%8@0!<9K PX@'ZM=M=M>K1PX@(bM=M=M>@&bUM=M>YY@'&21,*&8'8&($*+'6>7632#"'" >73262#".7>հNATM8+%1oUXL\/ RM1%^-7?U TN@#8>$y.PkK/V/L)T$)X #6EF;ZF#$4@(0/  <':KPX@,b UM ==N>@*bU U=N>Y@&%31.,+)%4&4('$" +#"&7>76''>3232676'&7>3227'"&#"'6325Ӌ%X 5a)7%# iY}#3wR 8';8+H+}'=4+Lq;{զ\);'93/HiuC->\ZZ \X$-6Fk@h: B  87EC@>=;7F8F('$"+#"&7>76''>3232676'&7>32%>2"&%>2"&27#"&#"'6325Ӌ%X 5a)7%# iY}#3wR 8' BN)AN+R ?N+BM+x=6+F+$=4+Jp=yզ\);'93/HiuC-'77N55''77N55R\ XX \qBP@(=@'  d  UVK >Y@CCCPCPNLJIGE336339345+%&'&'#"&?37+"676&+"?37+";2&#'76;2#"&733267%T)  93c!; --<$`X +=5 P7ois 9qTPY; )' /-  ))'3;/+''8h}k9DC:BNw@(=@!   UUK >Y@ECKHCNEN336339345 +%&'&'#"&?37+"676&+"?37+";2&#'76;2!"7>3!2%T)  93c!; --<$`X +=5 P7oS %(Y; )' /-  ))'3;/+''()BU@(J  =K(PX@(  d  bK = K >@&  d  b U K >YY@PNFDB?363393#5 +%&'&'#"?307+"676&+"?37+";2&#'76;2672#"/&76-T + 93c!= +-;%`X +;7P5q D Y; )' /-  ))'3;/+''    &BR@F (N  =@&   d  b UK >Y@DCLJCRDR336339345 +%&'&'#"&?37+"676&+"?37+";2&#'76;22#"&7476 T!* :3` <-+;'`X +=6P5q- Y9 )'+' ))'3;1)''/   %#i,5H@ @ . 'KPX@2  bU M =M =K >@0  b   UUM =K >YY@76BA<:6H7H"#334*BR +%6#"#"5?632#"';2&#'76;23 %"%2#".7>Y @hZR9 HӅZ?X ,>5P8oq#l9>=X3%]/7?U R Z) "8ILJHO;/)'' 97'T%)X %7DH;[j9mL %2@/<dbIMA("$"+>32#"$>2"672#"/&76bA%'+D'%BN+AN @  oM88&'66M88M6   &j9J "8@5<dbIMA""$"+>32#"$>2"2#"&7476bA%'+D'%BN+ANu!+   oM88&'66M88M6/   %#jh@ <d[! +632"/&76=      )\/7JY@?'&6 SRML@)ddUN= M  >Y@ WU*(&.$$-"" +#"'#".767327&7632326'7%6'"6632#"/&767327#"&7+}JpZ!T''ZynRTZS1'"=-> # :  =  q)!F530)w3PP'Xn#b))݁kwk^''+@CY^;F;u    )/ mi`LSW/7FC@@6 @?:9<'&:UM=M>$).$$-""+#"'#".767327&7632326'7%6'"67327#"&7+}JpZ!T''ZynRTZS1'"=-> # :q)!F530)w3PP'Xn#b))݁kwk^''+@CY^;F;u mi`LSWy/7GV@'&6 POJI@* ddUN= M  >Y@98TRNLA?8G9G.$$-"" +#"'#".767327&7632326'7%6'"62#"&76767327#"&7+}JpZ!T''ZynRTZS1'"=-> # :+  3q)!F530)w3PP'Xn#b))݁kwk^''+@CY^;F;u/  %# mi`LSW/7G^@[; C'&6 98FDA?><8G9G.$$-"" +#"'#".767327&7632326'7%6'"627#"&#"'632+}JpZ!T''ZynRTZS1'"=-> # : =5+F+}'=4+Js;{'Xn#b))݁kwk^''+@CY^;F;u\ XX \/7GVs@p; C'&6 POJI 98TRNLFDA?><8G9G.$$-"" +#"'#".767327&7632326'7%6'"627#"&#"'6327327#"&7+}JpZ!T''ZynRTZS1'"=-> # : =5+F+}'=4+Js;{5q)!F530)w3PP'Xn#b))݁kwk^''+@CY^;F;u\ XX \ mi`LSWZ ,=@:"!<dbM =M> '%  $" +! ! "32%632#"/&76Z=V9;3N{7Ѵ)6; F  zm'/5H@1R   ' )E@B%<dbM =M> #!))  $" +! ! "32%2#"&7476;R;;3Ny7Ѵ)3;!+  zm'/5HB 1R/  %#f-@@5  "@,d  b`M =N >Y@ ;9&&"''!! +%32673!"7>76#"#!73;7&76! 632#"/&76u!RG3=X#F!+)!R%#N!57T%+#0%C w)R!RѨb!N-w;a    %-=@  "@- d  b`M =N >Y@/.75.=/=&"''!! +%32673!"7>76#"#!73;7&76! 2#"&7676!TH5=V!F!++R}#"M!58S%+#-%,  w)R!RӨb!N-w;a/ %#d3D-=@0/76"@-bM =N  =M  >Y@ ;9)&"''!! +%32673!"7>76#"#!73;7&76! 73267#"&7!RJ5=V% F +)!R#%P57V%+#/#R!>')/'dJHGw)R!PѨb!N-w;aV="\/9 dHVVT'5 @ d[Y@   +2"&7476+  '/   %#0'd K$PX@eM >@eIMAYY@   +2#".7>3#a-8?R  Y')V%)X #8EF9]R @IMA   +!"7>3!22-0#3%8R @IMA   +!"7>3!22-0#3%8L @IMA   +!"7>3!2y\,+L+1hL@GK? +!!ZLe'L@GK? +!!yLeL@GK? +!!Le1}@d[+#3#3TRRPR}^^/@,SGK? +!7%!7-/ZZZZ@ :[ +"&54>7Gc=-oOEd-9H;?}}t, O@ 9> +462&7>54.'&Gc=-oOE/-:H54.'&Hb>-nOF\-:H +2.546=)2 5.WP@Ssf,$%0 @(_!tTUU)@ :[ +"&54>7"&54>7Ia>={TOFHa>={TOEd.8G<-^eOt,B.8G<-^eOt,.)@ 9> +462&7>54.'&%462&7>54.'&Ha>={TOEdHa>={TOF/.9G=-^eNs-B.9G=-^eNs-)@ 9[ +7462&7>54.'&%462&7>54.'&lIa>={TOFeHa>={TOEZ.9G=-^eNs-B.9G=-^eNs-Y'@"9>'' +2.546!2.546)2 5.WP@Ssf)1 6.WOASrf,$%0 @(_!tTU,$%0 @(_!tTUuX78@56' <eV>/.&$ 77 +2'"'#4'6#"'&5476763&5476766/ ('-r%%sA/%%/f %#I 7k-PyZh@}+ :/fP/L9Rm/)#"TXbT@Q[M>0_.a* <e V= M >ZXPNFE=;31)' bb +%2#"'#"'&54767#"'&'476763654'6#"'&5476763&54767663'"'6 - ''-r '&% 5m- ()+^#sA/%%/f %#I 7k- ('-r%y= -R1X{{/'%% R- D\b!! +2#"'&'&'&47676767659*-  '%)5567)- !#)+11!!1 9315#!'-97)3"9?"+ݍ9_^ @M>   +%2#"&546)5L3'7J4'/P8'-N $@!M>      +%2#"&546!2#"&546)5L3'7J)5L3)5J4'/P8'-N4'/P8'-N #/@,M> ##    +%2#"&546!2#"&546!2#"&546;)6L3)6J)5L3)5J)5L3'7J4'/P8'-N4'/P8'-N4'/P8'-N5@ <IMA'" +>32#"&54J)%- J+#-+;-#)<-! %1JV`@]5 <8 7<4 :  U  U  UUM>32USOMHFB@;92J3J$$%$$$%"+%32654.#"4632#"&%32654.#"4632#"&27'#"'#"&5463232654&#"7#Pl%%HrknJguXb+9#Pk%%HrhoJduX`bfD)Ll^u\ItZam\+ne9%Nj9#FtHBa1;ByVd{Jcok@hNUQ PLKnlhfa_[YTRKcLcIGCA=;$$$%$$$%"+%32654.#"4632#"&%32654.#"4632#"&%32654.#"4632#"&27'#"'#"&5463232654&#"7#Pl$%HrkoJftXc_7#Pl%%HrknJguXb+9#Pk%%HrhoJduX`bfD)Ll^u\ItZam\+ne9%Nj9#FtHBa1;ByVd{  +2#"&7476F,  0   %#!V+@( <e>  +2#"74762#"&7476+ -   / ##0   %#Z!/7@4+ <e>! )' /!/  +2#"&7476#2#"&7476#2#"&7476Z-  -   ,  0   %#0   %#0   %#}=P@ <e>! +632"/&76D   %d)-'LK1PX < @bc=>Y)')!+672#"/&76%6720#"/&76 C & D      $'   $}9j';!@1 <e>''!+632"/&76'6320"/&76'632"/&76 C  F  D     )!    %  %q "+'76ÛsjqoumopPo "+3767&'7qnoɀjowPw &2L@I('2/,)10-<+*:.9UUIMA$+%>2"&>2"&%>32"&>2"%7   ' L[3 L[3 JZ5 LZ3L/-3NZ5IZ6 LZ9 +,9B+ /@B--??-??--B@1-??ZB@Z??--@'"%% )'H4<B@?,)<b  =M> <;87+*4 4  +2#"7>7>7>>2#"2"747>7>>2"f-8`LVNZ3L/-?-5 a/ 5" WNZ6LZHNHL=) FJPLJZBBZ?XNHL=bi)!`JPLJZ@@Z@D,5A4@16*;<b` =M>.(&+#"&76767>32#"7>7>7>2"67676& 7!- y<_G!"2wt!^ 1CjNZ3 LZ] %} &N##%+XN)11.,`7#c-) FGN1Z@@--@E="X2`J'-5K1PX@ K>@GK?Y@  +!7-/ZZjA~@5Sc]LG%#"  {sgB><;4-) KPX@7Xb XV  V  >KPX@7db `V  V  >KPX@8Xb `V  V  >@7db `V  V  >YYYY@#xvpnfda_WU#*&'%(,$!+%&#"505672654&5465>32>32'"#".'#"&5765>%&#"=67254&5465>32>32'"#".'#"&=>&#"=67254&5465>32>32'"#".'#"&=>L"(/5Z}"(/5Zo"(/5Z7 3L P '/wL-BS" 9gl3!-37 3L P '/yL-BS"9gj3!-37 3L O '- xM-BU#9gj3!-3!!@ d[ +3#VTTBF)5_kM@J b`M = M >a`+*ge`kak_]USHF:81/*5+5(+,"+4632"&?>54&#"#"2#"&5464632"&?>54&#"#"2#"&546BմDjmf1 Xc^;bLbs 3-E)6L3)6JմDjmf1 Xc^;bLbs 3-E)6L3)6J)\qR^JHDQZ ^jTPoOTwT3 #:4'/P8'-Nf\qR^JHDQZ ^jTPoOTwT3 #:4'/P8'-N+PV)5Gc\@YX ?6<  b  ``  =M = M>IH+*VTHcIcCA:81/*5+5(+," +4632"&?>54&#"#"2#"&546>32#"&5462#"=7654&547>+մCklg1 Yb^IH+*VTHcIcCA:81/*5+5(+," +4632"&?>54&#"#"2#"&546>32#"&5462#"757674&547>մDjmf1 Xc^;bLbs 4-E)5L3'7J J-'5L-'5P'5 #9B R%\pR_IHDQZ ^jTPnPTwT3 #93'/P8'-Ml-?3' -@4& 96;sBߔ)7JR9<ZNLJ+@(<eU>&""+7327632'47'j{?, !u22JkV93=阗 +߮'!`+PmK&PX@'M =M =M >@IUM >Y@ONLJB@864210.+PP +76767676'&'76;676767676'&'#"?327632#"'32'0+ J  l  >/  J+> FG1h=)1m-+>  X(55!%%d)!9+! %+yR\7Lw "! +7N)=c@ "@bcUM >Y@ =;31&/#+3#&'&'&'4&5476767672#"'#"'&54767632!! 1-"5)- %%''+%+#)"'%1-D''#!5 )'''!# NK1PX@QM>@UIMAY@      +"2676&"&7>32`C;|RZ!mb#!joi'%PTÜN&1h@*b`UIMAY@ #(6#"4+2�76?>7654#'"&57672'4632#"& H+$ \; %; *|@  <$&0.  $$ " . %  !2+,)X@!dVIK?Y@ B*"+37#"'476767676323#&#"&67>`' %  Z/'Rsf $<=`d> D1Bw1  m+ F15%!' w!@ K!PX@"bUQK>@(bSUIMAYY@ "$%"!+4"'!!632#"&546327276\{9B oN=%C_`uSc;v&5N.0n%F]HY@'!@E'9 "M@dUIMAY$$%!+4#"3276#"&547>7672a9F<)@#-LeOv%8fNH6K#d05JE>1?^CZdQT} '/?3/dZRQ@deIMAY@  +"'6733!#7++#)9 %dF)'H1b !-S@ " @UIMAY@)'   +">54&#"&547'&'463232654'R4B8%#J0FEMxS^A`A-lC)HX>L6q"Z/#),MP=F|h ,KTd@31R\t:.(RQ:!& R@ <9K1PX@QM>@UIMAY@   %! +327654&#"727$7#"&546a;F;)GIQ{ kRFdMd-]!E?vZe'/?\=Z- K PX@YT >K PX@XYGL@KPX@eT >KPX@deGL@KPX@eT >@deGL@YYYYY+3##7#7373T0P00P7PP%7@GK? +!77PP'//@,SGK? +!7%!7))EEFF+ "+47&+xm/8#ɑ VfsJ3=qNB "+'>7654'7Bbc#QW_mvF5y<4DS;@UIK?Y@ :2+;"+672&'76?>?654#"2&'767>7674#'"&57672pRy ;"N0 #7Q1`A* R5}% < +|? jt*   %% #"Q\  %% !. %  TL /@,UIMA      +"32676&"&7>32C;|RZ!mb#!koh'%TÛm /@,<dIK?   +"&7676&#"&67>7>.V\  m#=m= 358 ;)(-E '-!'  41 )T%C@@<b`UIK? %% +7"&7>3232>72!67>76#" mZrsm{5=# !1Z\UeN 5iPZBnDLl3++ ``}?\Lo+ .+,>6~@ 132#"&547>32327654#"&7676(R#  uGO1Ogb6}%- LQ3:! H0=A1 )\3-aCF>sTP)&"%4:RZ& (4TX)-@*<dON>B*"+37#"5476767676323#&#"&67>' %  Z/'Rsf $<=`d> D1LBw1  m+ F15%!' =^!C@@ <bSUIMA"$%"!+%4#"'!!632#"&546327276{9B nN=%C_`vSb;v&5N-1,n%F]HY@'!@EL^ "-@*<dUIMA$$%!+%4#"3276#"&547>7672a9F=)@#-LfOu%8fNH6K#d/5JE>0?^CZdQT} '/?20dW54&#"&547'&5463232654'4B9%#I0EEMxS^A_@-kB)HX=}5q#[.#),LP=F|h ,KTd@31R\t:.'QQ:"3K S@ <9KPX@UM >@UIMAY@   %! +7327654&#"727$7#"&546a;F;)GIQ{kREdMd-]"E>vZe'/?]=TB kK PX@XYL >K&PX@deL >@deGL@YY+%3##7#7373y0P01PfOOMFf5K&PX@ K >@GK?Y@  +%!7FfOONV)@&SK > +%!7%!75))?EEHHT "+347&Txm/8# WfsJ3=qNj "+%'>7674'7jbc$QV_muE6y<.J)4;@8*<bUIMA($)$#'"+74632327#"&7#"&54%76'4&#"#"327yI "  53%#>N2;I~ /#>J $%+'08Q+!L7^FD.J<@9<USIMA # +%654#"3267#"&54632!M-[(M(4N/"sxfg} RW1FG-+!wjX#4 N5LJ +@(UIMA   +2654&#"'4>32#"&"Ff6/;Y%?3HuFo]Ňch{LZt;IP"XfS8tVw?SM@J)F1S><99U I M ARNKIB@5%$D&#D&! +6726767'&#"&?327"76&#"&?327#"32&#"'76326'&'32&#"''%c# )QT24  IW $)Y2&LX; 6t)B/J")+ q   *9 dr R=Jc@!USIMAY@ # +726?4&#"'632#"547!>M-[(M'4O."sxfg} \5RV1FG-+!wjX$3 NI<=@:6<ddbcIMA:2)9"+632&#76?>?6#"2&#76?>76&#'"&57672+#qS'4 N1 /f1^: R5}$b +|?h$$ \ $$ !A$% 5L@/7  E@1dd  b U I K ?Y@LJDA=;3'&#"4 +7732�7473>7654'"767367676&#"?37"72&#'76/&#"&) J1y !{! ZKjVh 9+ TN\PAJ/L9& $$ #- #6$`  "" GV J  *-/%@dd[2 +6&#'"&?672&#""+76?>7 *|@h N2$ %fA$% 5V $$ !J`KPX@`\S<@`\S32;2&#7473>?654#"732&#7673>?674#"732&#7473>7674'"{ZLjVo&k-s > J1 :J-W@) J.9 L1\F'  N3y #K #kji%Dq/  $$ #NR $$ #)N\  $$ #B,JD3@0;<bcIMA:2+;"+%672&#76?>?654#"2&#767>7654#'"&57672ApRy ;" N1 #7Q1`A* R5~ %= +|@ jt* $$ #"Q\  $$ !. %  FJ+:KPX@ <@ @"dUVK>Y@ %'225&## +7654'"7673632#"';2&#76;>7327654&#"\Jb?DJ a;%/)  N3x#1!/ ';P- # ?=hR-#q) %'#`-)=+;@BJ->@;,<bUIUMA!-")#+372676&'.>32.#"#"&#"6- !$9)B%@L>L-b /B()>1/.: \[*![%K*%'&%="C~Y O=+'$'-8%:KGL!?@<<;:UIMA! %#$#$+327#"547#"574326?32#G)3RULVN 9:R- =###HNw1DN%Hn@k& %FEC<b  b`bUU >@><:42-,+)$" HH +"&54732327657654&#"654#"'676;3>32#"'&"67稸1G9w55wG'E-PQ##;5/)-07 w7I03!ZRZ1  hw݉i0 /3#F1%3+-#m);[@X54 < Z U   SSK >;;963/,)$#335+#3#;2&#'76;267#737#73674&+"?3!+";b5 J5=: 93)6;{7L 3P`PD'')1LP`P#!$B%C@, BK1PX@@  bb` U  SSM>@D  bb` U  SSM=>YY@&?>=<:987420.)'$#"!  CC +%2327#"'&#"#"&=676767#737#7367672#"'&#"3#3#6NN1=J \PfX8-/7 om2  -{-^5#!)/D  9w 9'R B5!%';6 bP`P͋j)' #*%-}DP;%PN!/@G rse }zwqonmigdca_OMIHEC%$#,BU+74654'"#"'?632>?32'#32?3267654&'.547>32.#"#"'&"7'"747#!"'32&#'76;232654'"٦F9  LDCq4 +R -=8+'/X=u JZ338q?++8+?e 3G;@L!ibD 1 d]mbo5%GZ/H 1eP\36$; # g/7 dc$# %+B$B#`/5GB 1N++5#\F^)BG '-!":C+ {j3Ht#!-h9##+ /+!T:G1KPX@c   " <KPX@c   "< ;K(PX@c   " <@c   "< ;YYYKPX@G  b  U  U  UM = M = M >KPX@F  b  U  U  U M =M = M >K(PX@G  b  U  U  UM = M = M >K1PX@F  b  U  U  U M =M = M >@L Z  b  U  U  U M =M = M >YYYY@~|kiedb`PNIHDB@>#!%335#%+327#"&'.#32&#'76;27654#""?62654#"3267673267654&'.547>32.#"#"'&#"Tq'N%'F^o 9K$PX@C   bb TP = =M=M>@E b   `b TP =M=M>YY@DCBA@@?>;:8765'"''#+%.#"32#327#7"&547676327!7!7654#"'727673!!^PEyXP! qMSv #H81{ )`L)%-A #oB...\=VOJ:%+=2{-OBD 3 _X4P@M  $#<U S  SM>4410.-*!'" +?36327.#"!!!!2>7&#"7#?3>713H 9?Pgr51\3+5VJ-"<k9y1L w1 !o {E $Eu)[GB++!32#?wf 6O]K1PX@YW$N<:89@Y$WN<:89YK1PX@UUM >@(UUM =M >Y@\ZUSMKCA53+)2V! +032!"&#76;277'676732>7654&+'6>7632#"'654#" 6732941\b7`dq su3RT VŪ\u1e;-{ohB!ǃ?7J5+u#T5 n)IN   mu=/ċ:!wlw9!jxg%Tn'e!8D4+! O@~F  . <USS  U  M  > OONGE@<;:9876530-,+)&#  %"+!32!!6567&#"!#3#!"'32�'76;27#737#737654'"#"572>763  3P  )B'GRk5#HX2Hf hF#  <`%I%Շ ?^5aR1+Ph9##PRP^RN; #w,N @ 2- "+ ' >76&#"32647#"&76327327#""&76$32#"&7>76&#"3267wjE- L -}#hR^@)yX >d 7#]G\d fN B) F!p/+1s)>l5c TyG FFT+7"{HT76&#"32647#"&76327327#""&#?32676&'.7>32'6&"wjE- L -}#hR^@)yX >d 7#]G#G7B+C 72;+ibB#J-V? /1s)>l5c TyG FFT+7"{L^23)?!'?+R_@DW9%?dXZeD  "+".7!2!"3267Ӡq-9%B#buWz);*Gu{)[qVDAkyوX7D(0 /+& "+ 7323"32676&#"26>2"R-9}T>F{+) 7?=] 9{\!b !g!R%)D-lޙc7^\99YYyyyw%2@ @ ;5/) "+ ' "&76$32#"&7>76&#"326732>76#">32#".wjE\g! fO A' F p--!?p5 #^X}PDW eHT:!"JO>).|PT5wY6KPw%W ;2 "+ ' "&76$32#"&7>76&#"326732676727327#"?>7#"&76?676#"'632wjE\g! fO A' F p-- d )4J$;!]H  mF- i7!aF eHT:!"JO>).|V95sTR- 7#}D  y-6O+ 7%{B 7D@KS RNIC3!"+!26?6732#"574&#!;2%'76;276&+"5?7%.+"%6&#"26>2"N31-- /%1O ,<56q#?> ='E8J 7?=] 9{\!b o+1; 0H}?/+b/+''Zu/+)D?R!)7^\99YYyyy?@/"+&'"'>3266#"''7676.7>727#"'67q}`tJL5Lt4^b5F`ypHpZ 7N5gf)LpdBVlYoZmr\_+H1>9nTkL|h1f7J?fPLPu#hT`ZooQ7%+OVVP "+%!;2&#'76;27&'#"?30%+"!6&+"?3%+";2&#'76;2%676&'bdd +=6P65O 5sRS ,<7P6q ,<5P5qXn3-1)''''ZO/+''Z/+''.& T- "+3676#"572767332327#"&767>&#" TP )C %nF1-č#f)l)u'1#i)${'); 3 7nV+%y#2+1tJh[/2T54"+732327#"&767>&#" '?676#"57276731vč#f)l)u'1#i)${! )C %nF1-t4R8_ 7nV+%y#2+1tJh[/2,R0O'); 35D="+6$32#"/&"'76232676/&76?&#".'Ps1 LB!%1%!QF%)9#7l4ٔy?'>\ J\k"ۚRJ/7/D'7d{H!!" a#TXN/FPJ/skfH+) Db" "+676&#"27#"&?'6?32 $!7HP=ob+)fJ'\VH ;ŝ=FZu!}i,F4`@yyo}+DKKE$"+#&'&";2&#'76;&+"&?!32?6&+"&?37+">7&' +3) )95PD XV   %98 H59F7B Su;)Dd13H'% !0 01L ''JI/.6#D=JV @ SMD>'"+%!!%.+"&?!327>32#"'&#" #"'&#"#"&7632322#"&7>6&#"326V#/?/ j5p)sL`/9 d73J i){hLhD?mbk{uj8B3\DB/[Z9/] 'yTAۻ<-%+P+?'6+LϴB-HZ!i;mkq{iyq`#7; 0< 81%"+6.#"3276;2&#'76;"?6$3 32676&#" RX3dRuf_ST ,<5R{uW#XZVuZ-^9' % ZwCi^M/+'''fhLV3')1-1dD).9 60+*&"+!"'327#"'.#"'>76767&! 32#"=U{fpuJ Po;!f'H4uA+3;$>7g)3=khz VLwFA9=&0F ']/`B 7L?7D\"+6$32%632327.>'.+'%'&#"'&"'7632>7>&#"'>76.!VF%%/# #>B- #'y%3+-#DXF0;+J7H }d_'#el}'!PT$/ZX-G` --?s#7; 8D @94"+6.#"327677#"&'&#;2&#'76;"?6$3 32676&#" RX3dRuf_Sd5k#7Xu T ,<5R{uW#XZ9' % ZwCi^{\ + `NM/+'''fh)1-1D91]5."+&#"#"'.#>?32676&'.7>32"'';2&#'76;27>7576#.?3673#"32&#'76;276 <u-I +9< i\)@ =F57T +19JH XRN+2B*=.  P+b%<3!{';#; Z- %  E2d/3J/Z 3BK?iI@F?(b;ݺN# Dxb   =  /N^*yW."+;2&#'76;276+"'7#!"57.+"#"'';2&#'76;27>7576#&5?373#"32&#'76;2746/  )? h g!&+L/5/ [V+B-/9  P-`% <3!!{'#< Z- %   1)%C  D$+%þ/  >;ݺN#  0   =  / %:D/"+%#!"&767676&#!"'673!63!2671y2 i7UE'#9B,N53w )=3j>;T;d;T!PAD   !D dD-'"+%32673!"7>76#"#!73;7&76! !RJ5=V% F +)!R#%P57V%+#/#w)R!PѨb!N-w;aV=6"+!"5476?#"#7!2327654'63!#54&+ -} %TJ6@D '+'#69L1=,/7=w/L d֪B=V4Ճ!=#) "+!76.#"327#"7632FE);Lq4-r@Pm$'B+H>y'+qR` BNyDGGE;"+#"&'#6'+&67>76&7>7'&767367&7>?N6 # k= E0 $^0> N% 3%t\2;S %+Ah 2 37!'-{Ps3.Y_7`N3Ht2=?1N'/]6EZ_XJV8Zg%w?N   /)$""+23!7!67>.#!"&7>?39YA+ 3sXXsN )DCHE !## LD+%J3fF@~~yHT@)A!\1o'"+".'#<6767>76&'&7>?32VG%))6)>X ;+'$NR>^ZDd-y%#'b ˉT n=P !!=V% o$ "+/67>77!"7>?3!2! ! !9 <TƑ FGJTHH!3% '?F+}DQF7N%bV %$"+>2"&;2&#'76732676#"'673Vg9Tf9A1 O 1/ j N}L3HGgGG>-+//+- LG+)RX!X @8 S ! KPX@= b  b  `  `TU  Q >@Bd d  b  `  `TU  I M AYY@RPLJHF@>42-+&$  +"'673;2&#76;267>.'4&#"#".7>32#"&547>32327654#"&7676/K }!h9 jF I-< TV(R#  uGO1Ofb6}%- LQ4:! H/=!7/D!+ ++ +!7 Bf1 )\3-aCG=sTP('#%5:RZ% (4NX:`r@ P 5KPX@F   bb``  SUQ M =  >KPX@H   b `b``  SUQ M >@N   b `b``  U  SUIMAYYY@<;[YSRONKIB@;`<`$"&*%%&+'4&#"#".7>32#"&547>32327654#"&7676"&7>3232>72!67>76"TV'R#  uGO1Ogb6}%- LQ3:! H0< lZssm{5=# !1Z\TeN Tf1 )\3.aBG=sTP('#%5:RZ% (45iPZBnDLl3++ `a}?]Lo+ -+PX!C@*-'& ! @<d d   bTS U I M AY@A?=;750.,+)(%#  +"'673;2&#76;267>.'%4#"'!!62#"&546323276/K }!h9 jF I-< TV{9B oN=%C_`uSc;v&5N.0!7/D!+ ++ +!7 Bfn$E\HY@'!ADRX.P@$7:43  @A b   bUSS U I M AY@NLJHDB=;986520(& .. +'"&547>32327!67>7674&#"4#"'!!62#"&546323276TV mPn mlFI/) ZZN8!q,{9B nN=%C__uSc;u&5N-1Tf5i@EBnD`X Zgq=]N /.< n$E\HY@'!ADRX%]'@= X  KPX@H  b  `  `b  USUQ M >@N  b  `  `b  U  USUIMAYY@WUQOMKEC97&%"$%"%+'%4#"'!!62#"&5463232764&#"#".7>32#"&547>32327654#"&7676TV{9B oM=%C_`uSc;v&5N-1'F  uGO1Ogb6}%- LQ4:! H/=Tfn$E\HY@'!AD )\3.aBG=sTP('#%5:RZ% (4RX%)O@&  K-PX@:  db  SSUQ K >@@  db   V  SSUIMAYY@NMJFDC@?>=97""$%"%+'%4#"'!!62#"&54632327637#"'476767676323#&#"&67>TV{9B nN=%C_`vSb;v&5N-1' %  Z/'Rsf $<=`d> D1Tfn$E\HY@'!ADAw1  m+ F15%!' PX!.D@9@,d dTUIMAY@?=20,*%#  +"'673;2&#76;267>.'%4#"3276#"&547>7632/K }!h9 jF I-< TVa9F=)@#-LfOu%8fNG6L#!7/D!+ ++ +!7 Bfd/5JE>0?]CZcQT}'/?30dRX&H@/2,+ @8   bS U  UUIMAY@FDB@%"'+$%% +'%4#"3276#"&547>76324#"'!!62#"&546323276TVa9F<)@$-LfOu%8fNG6L#l{9B oM=%C_`uSc;v&5N.0Tfd/5JE>0?]CZcQT}'/?30dn$E\HY@'!ADNX!,CO@D8-'!@-d dT UIMAY@#"KI?=42",#,  +"'673;2&#76;267>.'">54&#"&547'&5463232654'/K }!h9 jF I-< TV4B9%#I0EEMxS^A_@-lC)HX=!7/D!+ ++ +!7 Bf5q"[/"),MPKPX@9  b  `  `  U UQM >@?  b  `  `U  U UIMAYY@b`\ZXVPNDB=;64-+! +'">54&#"&547'&5463232654'4&#"#".7>32#"&547>32327654#"&7676TV4B9%#I0EEMxS^A_@-lC)HX=`(R"  tGO1Ogb6~%- LQ4: H/=Tf5q"[/"),MP@9   bS U  U UIMAY@QOMKGE@><;9853-+! +'">54&#"&547'&'463232654'4#"'!!62#"&546323276TV4B9$#J0FEMxS^A`A-lC)HX>{9B oM=%C_`uSc;v&5N-1Tf5q"[/"),MPK-PX@+b UUIMA@/db UUIMAYY@32@?=;982B3B-+! +'">54&#"&547'&5463232654'"'6733!#7TV4B9%#I0FEMxS^A_@-lB*HW=++")9 %dETf5q"[/"),MP@ddIL@Y@ !! +'"'673;2&#76;267>.TV/K }!h9 jF I-< Tf!7/D!+ ++ +!7 5/-@* <UM >2";###+4'&+&'77#"32�76372767676 ); >)   '>_=% 7 '% ''%!5N*@'$<UK >2C+#3+<3+4'&+"32767676%4'&+&'7!7#";&#%7637276767>X#) )' J );   ='&;}^7=% 7  '  '% } %'  5R8k9@6@+ Z<U K >daB.#3+;=+3 +6'&+"36767>76%4'&+";2767676%4'&+&'7!7#"32&#%76372767676 '% (- F *)   '' J ); ;+ +=  ^}=% 7  !  !  '' } %'%!5^M@J\O4<   UM >[VUTSPMKDA?<:8/,(& ^^ +"76&#'&?32376;"#"'.+"2&#7637>7654&+&'7670326;/Bh3; LL ' "%"e/%>^=/% %; 7  h- 6  #%  //!&6'';! '%jN.@+7.(<U >HF@841%6!+%#"''&'&#'"'&?532326;">4656'&#'#&?532326;"$ ()   P) h ;<   %)#%//-  #!  !    #! mR@OA@'/U3  \ <  b U  M  >db`]ZXMJd#$6(3 +4'&#"#"''&'&#'"'&57532326;"672436'&#'&575323!7#"3&#76;6767>76 'u+! ()  N+ h 7> j=');\>-  7 ! //- #!   #   #% }%'!pX@UJA@'/qWT3  <  b U  M  >wtgec_]ZNLt#$6(3+4'&#"#"''&'&#'"'&57532326;"672436'&#'&575323)7#"32&#!76;6767>76%&'&+"36767>7> 'u+! ()  N+ h 7> j;' '=]8>-   '')- 7 ! //- #!   #   #% !''!  }!  ^d@aka`G=<:OxuS)<b  U M  >}{oljb^\$6(=.3+&'&+"3676767>%4'&#"#"''&'&#'"'&57532326;"672436'&#'&575323)7#"3&#)76;6767>76%4'&+"36767676  )q' (r/  'u+! ()  N+ h 7> j>' '>_e\>-  &z')y- 7 }!  !  ! //- #!   #   #% }!%'!  } !5\#K-PX@#yx][ZNkfcb* 2/<@*yx][ZNb kf*  2/@)  I    UM >Y@#{wtsra^YX+2!F-K1)+.'&'&/*#"372767673#'&#!7637276767654'&+&'76723273#"67676'#&#'"'5?323273#"32#"&#"#574656372725656'&'` /#)   'q#'%i +Ejj =%  ); ՓQ PR ' %C 8  5'  1  PN#=   ) !'%!  '!   I!! !o !! %g#K-PX@d[X2qQLGC% }| <@!d[X2CqQLG% }| @&I  U M >Y@~{zkhcba\WT5U5V'+%30#"&#"#?676372767>7&'&'&/"'&?53232730#"#67674'&#'"'4?5323273#"2#"&#"#?65637272574/&'} 'G {1  1'# }  /  JR $ %C = 6)#  1 JN#%  !!   !  !! !J"  !!  n !!  f#Da@^wtNmhc_A! )(<  UM >~xspa`^[U5U.=+%36767>7654'&'" 3#"&#"#?676372767>7&'&'&/"'&?53232730#"#67674'&#'"'4?5323%7+"3&#!"#?65637272574/&'/ m+  ')#J 'G {1  1'# }  /  JR % %C  ='  *;\JN#!    o%  !!   !  !! !I"  !% '}%!  g#K1PX@wtNʭmhc_A  )'<@"wtN_ ʭmhcA   )'@*   Z   UM >Y@*xspa`^[VUTOJI:7210+%#3 +4'&'#"3676767630#"&#"#?676372767>7&'&'&/"'&?53232730#"#67674'&#'"'4?5323%!7+"3&#)"#?65637272574/&'36767>7654'&'" )'  *+  'G {1  1'# }  /  JR % %C ='  );]XJN#w m+  ')#;  '} !Z%  !!   !  !! !I"  !% '}%!  g!    o/=@B@? )4<bZUK >!36&5SF +>54'&#'#"?323273#"!2767675;#!#5763037676767 '; I >%'+'B2%hf =-  !  H 2T`#  !7@4 <bUM>  +"&732!"3267F!3$?'?w>kۉ)PZq V/fŅb˦ki{)m50@-5<ZUM >93<")"+327>76&'.'"637676767654'&/"?3!2#!#5=Vkqv#*Z?pM=+  '>Z5-{XLLbH?  !!  !Vw\VKr@J)#"@  U K =>Y@KKIF>;3*(2#'# +!#3�7637276767'"' 3&#7637276764.#'"57h8V Z (;^ >% K)<qL@' : E'Z # '' /N    ''@.'7/6@3(<UK=K >334$33+>2"6&+"?37#";2&#'76;267CR-FRh 1'J )Vq 2&Q'1/ hR:<')9--''Z/+''+/7 Uj\@Y(-O= <UK= M  >ZVe`VjZjTQNKJHGDC?:7321E5"+>32"& >2"654+"745?32?227?#";2&#""'"+'&#'76;26;267654+" D')/DR/CR-FRqJ'JAh  f: 'XpI'P:f  i@'1/1/sK Vq)::R99R:<')9dJ8 ''Z: ''+++/J;Z97 &9Bp@mT ` <9  U  K  =M >~}|{vutsolieb_[YUSRQPOKHEBATT%TS"+>32"6&+";267>32"&6&+";267>2"6&+"?32?2277276737#";2&#"#0#&#""'"'&#'76;267F')/DRk 1##Xq 2""10 C')/DQ/< 2Vq 21- CR-FRh 1'J@o n8Fz w?'Xq 1'P@x zE8n  q>'1/ hR:<')9--Z/++/)::R99u--Z/++/R:<')9--''Z/+''+/7H[Q@N60& T <U K=  K =  >YVSPMJFB?=#3;31c3+>2"6&+"?32723737#7676&+"'?37""'&+";2&#'76;267CR-FRh 1'JI{ _:"= Z5 '3 uK`B^ #_*Vq 2&Q'1/ hR:<')9--''! /B_X%)%jJ/+bZ/+''+/s0:@7 *<K=>%" 00 +#"?307""'&#"&?37#7676&D uL^A^!^ mE#; Z6 %7)%jJ/+b)'! /B_X'\W@T=B) U <U K = K =>ZWTQNKGDA><:C;34###+>2"6&#""'&#"&?37#7676&+"?32?737#";2&#'76;267CR-DQi 1]A^!^ mE#; Z6 %3 uL+Z0'Vs1'Q '1/ hR:<')9--jJ/+b)'! /B_X')'Z/+''+/#y@vdZ i; E< U  M  =K =>}|xurnkhecba`_[YXWVUQNK@=:7##4%SR+>2"6+";267>32"&6&+""'&#"&?37#7676&+"?32?22732?22737#";2&#""#0#'&#'76;267DR- CRiW XqX 1/ C')/DR.:1#^A^!^ mE#; Z6 %3 uL(? s=Aw u:'Xs1'P~}ywtsoli^[XUQOLJGDA@%TT%SS"!+>32"6&+";267>32"&6&+";267>32"6&+""'&#"&?37#7676&+"?32?22732?2277276737#";2&#"#0#&#""'"+'&#'76;267!F')/CRk 1##XqX"#1/ C')/DR.:1Xp 11- E')-FRh 1^A^!^ mE#; Z6 %3 uL!?  u7@o n8Fz w?'Xq 1'P@x zE8n  q>'1/ hR::R9--ZZ+/)::R99u--Z/++/R:<')9--jJ/+b)'! /B_X')'Z/+''+/7lul@iiP+wvvwutpoh`]ZUROLJGFB?<0-*'#! lk +"?6'&+"?37"32&#'76;26./&;2&#""'"'&#'76;2676&+"?327377>2"26?6'.+"3/X J0 ! b>7LJ 1:7 mGX8 hD/@  x='1/ q 1'J>|CR-FR)-XH 5#*Vq 270S''/J \,'' "!#@ ''+/J--'%1R:<')9 -L3/#Z/+-os_U@R\H+<b  K = K  >[YUSLJGDA>870-*'#! __ +"?6'&+"?37"32&#'76;26./."32&#'7632?6'.#"&?731X J0 `@ 7LJ /9: oEX : hDVw #91   70S''/J \,'' "!# B '#yH='%-oxz@wlX;<  bUK= K  >}zxwsrkiec\ZWTQNHG@=:7632.+($! oo +"?6'&+"?32?227?#";2&#""''&#'76;26./."32&#'7632?6'.#"&?7>2"6+";26731X J0 `@5F  n='Vs1'Q8\ "J+X : hDVw #91   CR-DQiX 7LJ /9:1/ 70S''Z/+'' "!# B '#yH='%1R:<')9Z/J \,+/-@$)mP <  bU !M=  K  >~zxqolifc]\UROLKHGDCA@?>=<852.+(%#"!  " +"?6'&+"?32?22732?22737#";2&#""'"+'&""''&#'76;26./."32&#'7632?6'.#"&?7>2"6+";267>32"6&+";26731X J0 `@3L u:=v t7'Xq 1'P=n  xv^ R'X : hDVw #91   DR- CRiW%7LJ /9:1/ 3F')/CRk 1Xs110 70S''Z/+'' "!# B '#yH='%1R:<')9Z/J \,+/R::R9--Z/++/+q!^@!b`=L >Y@ 333+76&#"'727673;2&#'76;26!- #o=6- 10P/1/X= 3 /+''+U@bM=M>Y$%$$!+%#"&7632#"&7<&#"3275')7!%+77f #{y} {V+/%%#E4ɤ\ /@+ ,@3b`b=M=M >Y@ /.$%"%" +%&#"327"&7676326&#"5727673327#f5}yZR#%u_O{%-`J= D#n=6-#F!:/yg^8{-:V? 3 `V:')/b[@X< I; \-<  b  ` M  = L >a^[XURLJGECBA?&333$337"+%6'";2&#'76;2676#";2&#'76;2676.#"7676;3>32632;2&#'76;26Z%=z\R 2&Q'1/ Z%ZbW' P'1/d +-G')fXPVˠVPR1'P'10׼DR))T/+''+/Z''+/98 9#ZXfJZ_V/+''+ +%9NK(PX@M =M >@UM >Y@97.,   +) >$3 6.#"676'&#"36sߒh3 9V:|dVXy#d#'VPbB  HZmϟ%C^u+9Vw{u7J-uه! [$511 4C{vH#+7@w@ @8K-PX@UM >@!ZZUM >YYWB56%++'3 6.#"6;2676&+"?63 #"&#67676'&'Z@vm  G#bZڝX\ ;>98 "@9'N8~Ƕ^{eM3 b'Lwo-[3wx1Q -27()kZ!?mN\i + )Fc\@ bONF*)@UM >Y@^\USB@75  +) >$7>76.'#.776?6'&#"3276?' 6.#"sߒh3 9V:F;lN %+ o3A T=d3+C ?emџ%E^wb 3Xsj3 Xy!dE]zBbB E+9Vw{u7J-Td+k)V1;!1 \3qAj+!'Ѧ  N5E{tF1o+Xb=o1Ϙf>5N ^7@4<bUN> + 732>76.# '676$325X qv@$%#$"+?32676&#"#".7>32#"LN%!enZTSŚ%%ݺXHD ) Rü  "+3!!#&'&'7676P+Z1a#%+!RBr RBk7^k/`R`/N3/\_7"+&'&'#7676737HP1LkkBh^k1dN0?=DP#;'H 5<3-R!X{Twt  "+%#6767!7!&'&'3P;J1a"-!%RHo TTX7^k?P\W+N3/^Rb=Z"+736767#&'&b=X5HjkL\^j=dN/@?N?+D#;43-R'X{Twy#"+&'&'3#67!#&'&'767673/) PVdbO h8TxV2R!Dp RN+[+fTX@fD_X/4M7jm9P1/\\Bk5`\V!# "+767673&'67#&'&'7bNqPuy-/>+P9fJ/ >;qBS'VD*=!O{u}NyZ(RfkymbR%\wTtu}P?RD/"+'6654'732+J}}C\D0'?-. )D\mT:$KHlVm;9 =2=D % B{ "+&'47'"'&'732767C 9JfLhH@fw=277HrBy#K):3!\-o#"+'676776767&'&'"+ZoLtD\+/:-/ +DTsV9L%Pdb^99-A=D!';3w#"+72&#"'676F :N``VG@hy7877HrBxL% 94!NL #"+!!+7!#&'&'767673!7Z zk%+!RBr RP+Z1a{ mkg/`R`/N3/\^Bk7^L #"+!&'&'3#6767!+7!7!7-!%RHo TP;J1aKZ z-{ m?P\W+N3/^RTX7^gkX)!$ "+!77$73&'67#.'7))?k-9:W:JTON-twddVEOb=>RdmmdR 0?PTb-TyPNL0"+!&'&'3#67!+7!#&'&'767673!7/) PVdbO h8TX Z{vV2R!Dp RN+[+f{ mX@fD_X/4M7jgm9P1/\\Bk5`3-   "+767673!3wnmR/H3NHpkT "+%!#&'&'wR1P`TP\RkuΏTP13-  "+7!&'3?R1PfNP\/kuΚIP1T  "+#67!7wnyRuRH3NPpk   "+767673!#67!79wnyR7urwPt/H3NPpkH3NPpk   "+%!#&'&'7!&'3wR1P`TP\?R1PfNP\RkuΏTP1%kuΚIP1L*"+!!!!!+7!#&'&'767673!75T;'=XZx RBr RPj=5H5TV #hm#F?/N3/\^N  ; # "+!67&')!!&53#67!+7!#&'&'767673!7VH+'7V:(>D+PVf^P o)QA)Z RHn ^PjV/;H7J4SR#]T#//P+-^;N)N1/#T^Lw/!"+!67&'!!&'3#07>7!+7!7%!7%7ZV3;'PRHn TP&Z'R+5TL )1U+N3/^R0kj "+!!!#&'&'7676735T;'' RBr RP h5H5TmF?/N3/\^JL"+ #&'#767673"jl#ALZmX)j?^T/@=q51KL5*R+TRwyyPw "+!7%67&'!7%&'3#07>{/V3;'RHn TP'1kR+5Tj1U+N3/^R/dJ+"+7367367#&'&'72^j13DbhmGL7dN/@;s-@G:'"R#X{TvoP  )'"+!67&'%!&53#67!#&'&'7676737V:(VH+'R+PVf^P o)QA RHn ^P7J4SV/;Hf]T#//P+-^;N)N1/#T^=u )""+&'67 63#&'&'7767673&33G_F"LRwr4/9.J1?7`?dN/@=o)%/?D=9F98{JPywuUw\(R!f%R+ZyP{u{O9 "+ '6654'7327"P7\J^+R_)+-4 )BXoV-+?K^TgL#JA;33>5L % :h"+#"''&'732767&''61=1<B;H1m3<7C:` K#<3 H7Ic,ZV "+76767767&'&'"'2\a?h*^/)++- )HTtT-+/LRhPNL=9J=1=D!'9FZ{"+%632&#"'676?=1-BP;;AG1{957F'9+ RL; 2#P73b/L-  "+!#3!3 !F%ۚV6{- G "+".76>3 #&#"2733R])##yy? ]^ݓ_Lۜw- )#"+"327&'2>.#"#"&7>32#"&76$sYRqBDGS@o 90q2#!&V9mNq{d)J3HˠH "+!7!!7!!7ZRFm\#\/ "+!'7#73#7!!7!73![EM(ZlWFJJFnRm\\Xw`' ' "+"&&76327#"''%3276&'d'T`cug /wg=jeV1Zj>Ug\)BP=τCc9 QbH"T4%'%yF "+ !30#!"&747Xw  J1 "+%!0&543!3D%A /ىG !"+)"3!!"!!3!^6ì7+ ş@ZZts\/ & "!"+)"''7&76;73#3!3!%!#"MEfEf"/WEJ.U.3@}bh喬7D'Ytdsr )r{{Tu"+% !3#!!;\JLȦ'/u\{}}qodn/7!7!4.+7LH]ɦ&2tZunodo/+J8o3/; L=J=;7: +<7H+=; --' }+-%%X--%'#7+-%%)/3*+/"+.#!!2>7!727-J5->1bF%+'+' 5D !P\Ec? -''!^/ "+!7 dd%!  "+#!7!3!!7!FdCcEeFbl+debe1g! "+>32#"&!!#!7!3E)'1D+-,.cDfFbHd1)<326&7>32>32#"#".'#"&7>(.RN+!'f#-B B+%D 1BS+ +## J7#?k!-%Z+w!.EY>J =3%"A{;"'5;o-  "+6&#"26%>2"&+ 7@=Z 9{\`!`^7_Z<9XZ9Tyyyy{X "+"&7>32N11 P-)7 N{?--DA0-?-  "+ '%73 kXf#%>Hybu P- ;$ "+ '%736#"#".7>32#"&7>3232676.#"7> kXfT%O 1uPP s;V|=t 'I1y 3<"%H`#%>Hybu PR%#;'h?:R9\Rh-%L\5@1 O- 5' "+ '%733#;2&'747326?!'73767632 kXf+qh! V9} 9F +#%>Hybu PHd )+fT +j} , $"+.#"326%6&#"3267#"#"&7>3232=!T^UfHQiF>w\}׌^iys/ufdq9jǕJ}hn"+3!!1eVJ#"+#3Zr[#s}# "+##33Z}T{[V#0:J#"+#3#3JZrZZr[#ss# "+##'33yTZOqZUTZ}Zb&MHtm"+ #3#BcHVcu"+%3#3eVcfs"+3#>32#>&#"eV:gsTdV^d&hH}υNjn"+3#"&4733267dT232TZ-6>%#1R@tP7:BBCXX!1'!/Dp=z7+#3H% "+>2">2"&?LZ3NZ JZ6 JZ3Z??Z@R/@@/-@@ "+>323267#"'&'Z/Z`)5-1O#9URr1hADDE dwDBRA"+>323267#"''&"'Z.TI0"5-1O#2;JD<391h<DE dw+b,RA)q  "+>327267#"'&#"!7!7)Z3X^+5-%ZT:7WX391fDBCFLj-ADTBTdddd%-" "+7>323267#"'&"'>323267#"'&")Z/\Z-5-/R#9TZ19/F'Z-_\+5-/R#5XR89fCBDEbwDBTA-fADDE dwDBQ%92"+7>327&"'>323267#"'3267#"'.#'&")Z&2NN89/'Z-_ J5-/R#',NP,5-/R#9TG$ 9/f ?RAfA DE dw:DEbwDyTA'/^   "+&''67!7!7't/5h#P}^ZqHZ_`-dddd/"+!!!'!7!7!7!/SUGJRJddl{ddB "+!7!7!7BZZZZZZB "+!!!!!'!7!7!7!7!7!BE_D9G,DuE^JZZZ+ZZZtRV  "+?!7\X``g%`!dV  "+?!%775``f`g;  "+7 7\V\Xg#b!df"c fq99  "+%77-77;fafd`gL "+%'%7%7 C)G"Luf}cDfvv\"+%'7%7'7V~pI0J`Mfwb>YgR"+?!7%7%7%!!'7 wLDh#FG8=`~`Rdag`kViR "+?377%7%7!!'7%'L%iL8 _FG8%(`Vgcf8z``kdOX}+ "+%!".7>3!! 3Kh-eoN5VNsmGV{`X8+ "+7!2#!7! 6&#h-emL{5VKsmEVyo} "+%!'7.7>;73### d}Hq<`^;/ eooIbU&5V#6RoImGV'~`oA "+7!37+'7#733 6&nJdDh_0em}Hp&U~{5V5R{SmEV'\} @  "+ 7632!6&>7!!!#%=J@@ &A 'G  /*L\}   "+ 7632%!6&#" 67#%=J: !+  ގŽ^} @  "+$ '&76 %%&$6''%9jmJvkU3b/=iVd/@ypSgeR`gf^}   "+$ '&76 $&$6'9jmJvEFw]\#HDyn[[Z n "+"&7>32R11R-)5NA-+DBZA ' " "+"&7>32!"&7>32%#"&547>3214 P.+5 L312 P-)7 NN-+1R-$4A-+DB--AA-+DB--An-A3' +D4& + "+! ! C}@BzFu"+ #3#-b!Vb+Gf5"+#!!omT%Z1"+37%2"&#" )Z+5=J1T;V1'!/Ay"+ #"&7>3232P79 ?#!DX;i=|7)%1HL<o"+'&/7672wJ#%7c5Tu%j# *NM'm"+7'&'63"'6)J"%7b6T%j[ ׸VMNS3"+!7#"&733267j!ŧ9NggyyDdgAq"+!!pqq"+!%!!p+FZLq"+%!^L%ZLq"+%! ^uL%{"+&2"+ &{2urZHo"+!Z/oZLq"+! Z/uuq}Z"+_Z"+ {_t;J'"+ ;ooo;I&"+  no nd!) &  "+$7654'&#"7632"'4632#"&z}a{yy6ˋō}zVX{zYVz}{|x{ŋTX{{XV{{o"+ fi;=!d! "+$7654'&#"7632"'z}a{yy6ˋō}{|x{ŋd! "+67632"'dˋōŋd! "+0027654'&7632"'B{ypˋōh{{|ŋd! "+%050"#"'&76$B~yx|Ǐ|}ˌd!  "+0#0! 762#"'&'476{}XVˋŋϲ}{yˋÏd!  "+030!4'&#""'&'476 }yɍyy{oǏɉd!  "+$765!"7632"'z}a{Vy6ˋō}{x{ŋd! "+$7654'&#"$7632"'z}a{yFxˋō}{|VŋdB"+0"'&76BŏCu "+02ˈP  "+%!!%uo Pck#Azif_WG"+%4.'"632676>32&747'"&#"632#"32#"'32673&547#"&%#"'3>54&'27&#"#"&' >32737%%'&'."72654&"&547632>32#"&#3>54&'&'&'&#" 32>7676767674%#63232632#"&'"'&5472674'"'&=#"/#"&54;."32>54'62&5#"'>324>7#"'654#"326767#"'&54632763262674'''3%5Xb:DhJ1+Kd3R/7DQP#)=P5?5#)baJ-2L|: 681lBYn58B;=:Dc^_{G{Hyfm{q7Re/SO.r<TruR(V>=6fXXJA%#R`3F 5%BEF !&' #3' )fd>F=R$^F=B:f1M?<;-m !''!LD~=*67' sF;ZN-'56X!-%5!5  #\w!'Pr!)I+13) \' 9J1.30-939# 4PDT 9+ '%9;0= !!?;7o!$ +/C6/? --% B+#l#'#- +!!#1^EM GF:+"+327#0#%5&54675%632&'"%#"'.'&547327>32432#t. H/-<)=Fh]5L+Dυ?ZJ^eXZL '#Th/ )r97=0)-;=BmZk$1H)$5>1X,9`d@gdbI?6.# "+"32$54'#"&%4!"#; 2654#74#"326'2654&#"#237&547#"75#"'5327'7%726323 +#"'5#".##%{!?KN5L;_'!b5{FH-J'.5m';4"#PCI7|GDa>N q7$HK\3YSb#5VH9=jJw^)V%F3fR')%^DZ1XR1Hg5#+-1h`FA)/  3gMPR}+1+L q =Pn#)6%%!?#!5HZ:+1+c Fm- ^ "+32654.#""4#"#"=27632#"'6?654&=737+";2&#'54;254/;2&#'54;2=;2&#'54;2?675&'%&+"7577-'{'NC50-gR:ہLmH1-/)-Yt"#e`)T5Nq>NGJ1N1s'g`%;`Tj- 'RR % 7ZX0 %  H J  BTTP##Pk}'  J H  f $0 @ *% "+3264&#"462"2654#"32 532/!/0 /B//B{%m<\Xa^TB//B-fA--A/ͤx:Nv:\Z}(08Y@B951/+&!"+7/232'"'#"&5''>4632#"$462"  6 5467&'73'723>7325  V! $?%F#6E))GG͛-P3)5// # 4-^XV'9F#V '#-)--)H ! w-= `D)}"*2P@I3/+)% "+%7'0#0527#"&5'&'74632#"$462"  6 5467&'6732?3675 oC)1X?%R>,!E))GGǙ/+/ 05)3R0TO4hXDF#Dn9;-)--)H #9d3=-ϲw '}0BF@ FC:4) "+264&"3264&#" 5467&'6732637367%"&'32?67' )!!) hǙ5+/05)3R0/;F ,=S%?Z1)Cq b----o #;b3=-ϲw ';9oD#FDWh8I;&"+"3267654&#7&''7&547#7367'76?373#'# ! R'R-1+uVEY}%R%V0/1oXF\͑/!wΐ-#u;Uq9sTh'Top7&5473326733##7#73"3267654&3 } fngmPV '9?a6'# #169T/b`X6-`31^^͏/#uΒ#+uf*%"+"3267654&.5476323##7#73)# # +Bè '9?a6͏/#uΒ#+u16ʔ31^^d*%"+%267654&#"#"&5476?#73733#D# ! t +ê ';B^37͒/"uϑ-!w51˓32__!1)""+#"&547632'>7'&547"3267654&Z) +AP/V+כ'X}# ! 13 ^%E-TTsZ5BFV3ϒ-#uϏ-#uV?)"+"#"&547>32!257'6'"=7>;27>7674&}LE 3'%ʉsr!Rj+y }VX  ' TT^<7++!^HM1 7 ! @% + !%ŃXXfH7"+732+>32'&7476767654&#"'#"7>;f/+-N5Ib ^)RF o G-w )w47Xf^2uă#'VRL\-#o 9P?%%"FuIYVN5 "+654+"?37373307#"32&###"&54767##'76;273267654&#"` H9bT-B Tb:qaJ:_hmRFh!i\:o0&-J+)/L ='Xd'Z='>b XbMB%m%Z '/D7#1D-=%"+#7#73?$47654#"'7654#"'&#"$27&#"3`?c8!m'BA))# UDNG3QJ!"D'^^)mBi`R%RL!'dB;^d}Z$ "+674&+?> %''%32gHg-q'3Tf$g-#LKH# J +wm)#!44"+#"'&747>3 3>>2'67674&#" hmnJw #1k !{5-]с v4fw >9L7}d11T\`T17}ʼnk!-~3!FLG@f .>6/&"+./&#7323276?673#"#"&547>2$7654&#"FH#5i:L%(:9qmOTTZ7jyBNtX /ʬ!ϧ# % !ZkRalo+++-mlaRda!w3:ߤ+=ۚ3%}٠#3}Rw "+%62#"'&'7! %$ 76)fbVuxSuvymヅ`V # +cJJc-&`NN`(;1'@P @ HA1("+2! %?!27&547>"3267654&"&547>7$! $!"'267654&#"o)59>ۜl^`NX^ o g33 5ݜt\bL\^)f-+D;N#!TZH`y^J^k%`E'F7654&#"#"&547>32&5476$32"3267654&3)u%bw1!bk# {51o3!7 'T DX\LVZB37f'iVV- {{d@`]XN'Ņm+#fR!-̒-#}ZJX{\H^oDENLF5"+%'654''654'?06?6?6?&''6767654'6674'ђ ja1%Rl7@L?uJ4PH1JaB+{ q?yY@%S3F@%FH;NGJt%^%a uS!݃K#u~PL/-\gX=C+L9\^L/)g BIH9su ="+7"505>3!2#"547>3!&7476$32!2#!7674654&#"-1-CB#X/!#'sVhR#u 0#7^3VOڞ})"ho 8f;, Vyk#d4fF! "+?6?6?32?'73274/&547654''654''674\`0'Vg-Ly') )+)/9Fhh^ZiZJo''}qN`B;1HB&4D # JZx=OZJ9c@%D1%H@%7VHPTf"+&547'''7"'772T] !gC;d{1wh$hdT5+Dh]RRgR)V%y;GE@."+'654'.'7?>32#"'+73267&547&654&#"26q7]- ,9Hl  9Pdfs yRJN8j3!!?32%>%672'&"0"/&#"0"/&#"672%672%>'&"#"/&#"#"/&#"g+ b%  xhb ^ `jd+d%yib \ ^ Ϯ  7    7  (("+&547#'#73654'73%73#!œ?F GV^MRTVMSƅ  >^:;98% ^TVL}""+"!5674&#"#"&77#".h?dh%2+iJXhdm^Jf+/u++?@>K>ouju>K>qA!"+%!5674#"#"&54>323254.5463232632#".#"o/1`#X\s)dL%FVEGTHKRs1v;eD51  L{ ++w\_yBzkg#75oG`gHp57iDhV'y'67'7P "+2'&'&5463232>Ry7HTLPqVD--FŔJ}\NuȞ767674654#"7672#"?>"67>32"547>7>32'7;/#"&#"2767#""&5476!2>7654'7#"  CfS<\%#7LJ7 +/B,'  8+?[ :'?  J=fx8+ # /  %,Z32>7"7674.'.54763"3267654A;JRBZ}J?Rj +5H )!-B /Y 5'-\ P<PpRAPf 懰Pg!M-m))X93H%@ #5 !L3 );E:Zf /] @ I7% "+67654'3267654&#""327&54767&.5476326323##7#737&'3##7#73b b [Nn! oh1 '# oj1 %I +BÜ_ '9@`5/sMuy9?a6\#+P;bw/#L?Β-!u=Ri31͏/#u?Tf16{=16ZZʔ31^^HJ^^J,Zl@ b[F/% "+&547632'>74653267674&#"%#"&'#"&547632'>767'&547"32767>7.+ )4N1V+^\ u ##))5 +AP/V+כ''X# %  !`X5BFNZ)H-b -b fhϒ-"u%>13 ^%E-TTZbj7{} df5DDT;Fϒ-#u&5d <7!"+"3267654&.547632'>7'&7473##7#73# }# +@H7V-י'X)ߪ3/`% Ȓ-%uӑ')sƉ7+Z)G-TT}sZ3DCX/D`` Pck#Azie_WG"+267#"'4654'#"'254'727#"'4326?67&'.#"'432&#"654&#672472.#654&%&#"32632 #"'.'&'.54767#57'5&54>767672>7&#"27"'47&'#"&546323&54>7#6727 &"&'.'3&="&#"#"&5463267&5432'#"&=632632&54632+27654&#""&54654'672#"'674&546323254'&#"32#"547#"'#"'546&'3%5Yb:hJ1+Ld4R/7DRP#(=Q5?6%)b`J-1L|.9 591mAX57B;=9F^^{HHyfm{q7Re/SO.s;TrtR)V=>5fYXJA%#R`3F5%A FF !''!#3' )ed>F=R%^==B9e2M?=;-m !&' LD~=*58' sF;ZN-'56X!-%5!5  #]w!'Pr!(J+14) ]' 9J0-3/-91>N-%7R#%# ?!u-/IbPC')f B1-;+\!/!;+HN}y%'B? :W5RaN)+t97-')oCF/B!2-Eh@l, QG+-J\ Be>716fh}%F7# =\"6+( 9m- 3XK~'D19D^;;4!\#d!TXD2,99kd"-+6%u'!=9# 4PDS 9+ '%9;0= " !?;7o!$ +/D5/? --% B+#l$'"- +!!#qf  "+ # !!!q\u-qjX[f "+3#%!!7!!ot\rn}p-\jX[jo"+'&/7672wJ#%7c5Tu%j# *NM'm"+7'&'63"'6)J"%7b6T%j[ ׸VMNS)#-;w @'  dSK =K >Y@;;3/U5 +!!!;26&#!"&6?>7#737#736&/&67327ZAP(${#T#5`=@=9 T8&@7b US = K  =M>Y@..*)('%""$+#3#32672'"547#737#73654/"'7672IX+}VM=+? lGoPPmB5#Z-XPP?'9 1 *R#-Ds@?%D0(@ dVUK >Y@ )U6&)I"+>326&/&67327267#"';26&#!"&6?>7&#"'ZC&@1IS  UM =  K >Y@FEDC@<960.$"!$" +#3 674#"#737654&/"?32632#"'2&#"'7637>7%4#f'= bH!!,L k+lM!HzVKPX@7 Zb  U M =M=M>KPX@1b  U M =M=M>K(PX@.b  UQ M =M>@4 Zb  UQ M =M>YYYY@PNLJ"+C,'#% +%3267632#"'&'4&547654+"?6763 ;#"&'&#%67654'&#"36-!!-9%!sC\H? !1 @5ô3[BLI5\+1Vu V1?sDvhq ^!%-YV! q/% hJj'#u3])`P`w#/Z9H9.&.7K1PX@1/.  <@1/.  K!PX@.  be== M=N>K1PX@.d  be= M=N>@8d  be= M=N=M>YYY@ 42"'&"& +23272'"74?''"'"&?&74327326767 7&#"1ou#PZTǔ`1jyZ5og91K9 =B/mbm!\-CVBgv:T 4Jۊ;ZN'}2@ %KPX@/db=M =M=>K!PX@/dbe=M =M>@/ddbeM =M>YYY@21$#%% +7>323#23272#"&547"&7#"54767 !R?13q%3H31Yh 1ofP%#7w\ </d:'\v(6%ZO !o+YK1PX@ F/<@ F/K1PX@-   U  SCM >@.   U  SOM >YY@#YYWVSPKHEDCA>;7641.-#53#43+0/76;27!32�'76;27654'#"?30%+"!654'#"?30%+";2#.5qgbga7P5q`5 P5qPPb6P 7q=!/)'Z =''Z ?''ZZ?''Z) /5%IK1PX@ 4<@ 4 K1PX@6  b  `  D = M = N >@7  b  ` O = M =N >YY@IIGFA>7532/.3#5%3+%0'76;27674#"32�'76;27654#"?27673632;2#.'V`cmtH'P&Z )D #oB2.Ӻb >! 7)i'ZR-u5''Z 1!9 3E?dQ%+7++_@2O9 [@$  UC M >Y@__]\VSKHFCA>74#53#;2+%076;27054'32�'76;27654'#"?30%+"676&+"?37+" 32#.b +-!Ra7P5q`5 P5qbH ! ^J/!-N%!1 0)o- 2u` =''Z ?''Z3 ++9z77%Z@< I2  @7  b  `D =  K = N >Y@ZZVUPMEB@=;8103#;2+%076;27654'32�'76;27654#"?27673676.+"5737#"32#4./  '13H'P&Z )D #oB2.T' H ;#/)R%! 7)%F- #Mt7''Z 1; 3-9--8T;FV9X5V/@<:K PX@YM =M >K(PX@eM =M >@eUM >YY@-,)% // +)"50567676&#!"'3!27!32#.# d6y64f-=;5#/)uT\A1H5#g@ <:K PX@YM=M >@eM=M >Y@!  ## +)"70747!"'3!2!2#.)bD7c53/#/ 9)_!Pw 8  373MC@@GEDB$#<M=K=>>=8530.,MM +">7654&'2767654'#"?37#'&5'67&547>R%D 1-' 5<3Y//$=#!3!  jD !$-;E17Zm- ^G:3!%"!5- #GJ%+mf3-) +)>-)).#3$="='%3Nn+?}@ :&@*b UU=K >Y@ ?<35334$&%! +!26?6732#"=4&#!32&#'76;27654'#"?3%+"XR!31 I 3L%1ia7P5q`5 P5qV+/= '@/+ =''Z ?'';sBL@I< <bbUM=  K >A>;84%'%"3#3 +654+"?307#"326?6732#"5754&+32&#'76;27sJ'H 'V534JL%-DH'Q 'Z5''Z+1; >^%;? )%7''Z#+6=,@eM>Y53%# +>767>7632#".546767654'"StF8 (J;35tDEJ^ }=B{{Z9f%NX^++#V\V8oSRw:977632&542674&#"2#"'32?654'.#"2772>767>54.'&'.#"#"'&'&'"327654'&'.'#"467'#"3274&5432654'.5.'#"'&"#"'&'.54654&5476727>7&'476767>7654&5322#"'& g   8!) !7&)*#J'/7% S 1'.!' MqC=7T%;T3 !u /;Bn'G>!T#O3  [+= -aQh#;,$F K4!1'5 *B@I!$J>)-'ol75(5R=;< %-'s?'L/o(Z2'-+)-*  e!J\A  71LT  >+I  f1-  - X'9) 5`!' - PBTb s75_ 3 R F-+ -)14')dP3#A=' CC# /P `BF " G ^'+]F!Hh !*32A`/ J 'B%B!=$ 71%&1)/!)%1- q 3DX +.-\!'=;;?\7G( 7- GZ 2    (E0'W! sOV1lP/:\T;^D1$0!  ?L '`mea2"+"&'".547>32#"3274>763232654&#">32#"'#"&54732654'&#"632'2654"߲-9g`9/ +1JTCoP+݋V1;e5+!+!l@Xdm{s/!?P 4''1{޼eɔMqh<`Jw/^@uS5w^5##J=qÃGZ7235PX1HHn`qn#+ 87!Vm8\PZ`8H{LN>do}wqJ"+'.7>676'&'&'&'&'&'.7>.'67>.'&767.'.67>&676'.Tq(()EB 1O3'3EjC'sk}L&M^H-.z&.>9Q<#!nZc/dž0[ /MPH5+'@$APʪsZa7EVvtk T^6U*'1-AGY/j8#6q)-  7` SI3 '+; Od.9 1Jl= !G& N_P $ ]aX#BL Z/)Ed,tQ9g ;^je_-"+".54632&'&'"32>7#"&547"'>7.4632&#"327623254&#"&54632#"''27675&#"%?jݕ}3%(4 PB#3o{jgZ=o!+#+3d32!"$#"! $#"!"$ ,32 ! ,32#"'.#"! ,#" ! %.54632 y! q "~h3!]-5!Xoi)T}J!  !7'ju+@+?IN!x8˶ s TǑf!i 3FH3JH-8-Ѱ^^GXHDTC! ooHXH:G:YVGH-5-n . }`'!XCmdeedFE-N ;2edde>!#P+:"+327#"7!''54;2654+"=73%+";26=732#'HJ$u>?)o7P5q3-ZVu/ydX9%'#8Z''Z"/7V`') ) #P Z:E[ 2>[;AZF;5 }jNH@; "+327#"7!''54;2654+"=73%+";26=732#3254&#"#"&54632#!32%4&#";2&#'54;254"'676;3632;2&#'54;2462"&;2&#'54;254#"'676;4#"'676733632#"'&#";2&#'547323274&#"'"&54632#!32%32654&#"632#"''654#"5'27674632#"&;2&#'5473254#"'67673'HJ$u>?)o7P5q3-ZVu/?)!/'=R}wy^oZmDZ#-RR6 N1 5 R-\mN76 T/y 5-#1##1#w5 T1 5!R/ L3 NAF5! /5% `/3 >)1%>Pwx^mZjFX9T\Q:J3BVT}fN1%%'='t#"##u5 T/3P/ ydX9%'#8Z''Z"/7V`') )NAHon^\% F9]5  54L!  mmd_5  "###5  51L#  R1L#  oo:1 B5  7NBHmoa\'n\|6Dl/+EgX ##""5  53J  %# "+&'&#""'>327654&54632"' #P!-L'!>=--)7'!F q- iNTRT$)D5u>522( "+&'&""'>327632654&5432#"'"&'  #P!-K'!39^95I-:y#{!# F q-^X-4uf\fDZ)FD3d9-+5!.4"+#"'&#"32676'4&#"'.#"32632F4C8A3S(R]wA)!--/>*%}m I+Ho[q:"+'Q3F/#'V?Q;T=@ (* "+%67>54'&5432#"'&"&'>32p #]>\+ _k8%/.EfC+V5@5# :173}S@:ALaY G3GAbqK}03 Ys . -# "+"&'&32>766#"''7&327%KZY" M!c9^#9:'L`76'&"'&732%KZY"D?9^#9:L_sD?%!7)D`76"&7$7252l *R sX*5/{  /{; /  / @ww3#F )V0"+>32!2676&#!>?676&#"#"&#TVR?d _5L/%#KX@{#SCbo J&fJRE<+VobsN//3.u: "+""&7>32#"&7>3232>7!"7>76&#O\ I!۽=1uj'Ѷb ?/ !MaQ3;#ML;j.NPB_V:O:.5 ?`BV\]-'"+)2+3&#"&67676?!"563w9/%/*  Vj m $o;@%ux!.## /  / /V* "+6&#"327#"'632#"&7>32326`g}qbs 9{VBiXo%\ 75#Z1N  !ݜ_;-B!   '# "+326764&#"'632#".7676% /\lN^X_-e^Hm\3X`3nuSM𴨠9m,FV@n{}mQ'F= "+"'67633!27'1'5N#V/'PL HRMO9 #. *$ "+"676&#"&76767'&7>322676/Vpz5Bdy%Jj-!֔ZR%ucI{\'yzNiX/X`ۥi97!qyil:}{'# "+676&#"32#"&7>32&76 /\lN XC_.e_Hm]3X`15 VSM𴨠'f]A-FV@nzR(G dd5*"+73632'#"!!!!32>72'"7#736767DVMVu'#F1 ==~N}Z;!#?Lq D ZBHÏZ!\/_V? \))_)"+%&#"&67>?!"?6;7!"?6;'.#"?327+"676&+"?327"32'#!2+2l 7? oI  -  - ,#!-/L +sJA 3;5  1  1{; /  / <{+ +"B;'%!#P''%4G'-+PS-&"+#"&7>32326&'&67327pTDq~)Z J )X5w M) \D/R5X=19#%{;43 =f%  "+732654.#"4632#"&7#Pl$%HrkoJfuXcHBa1;ByVd{%  "+"32676&'2#"76!!oy#avײ%'ɼ%#%1ͣѳR {{! "+%>.#"&7$72&#"&67>ZL 'sT M1m *= oI)1 $; h|i{; /  / <hs""+%26?&#!"'%>76&#"&'>32XT;4$' H1Bp݇) t;CW1 +σsq'Q< /נXPV V}yd+f: "+""&7>32#"&7>3232>7!"7>76&J#P\ L!޼>1uj'Ѹb >/  !N`R3;#K;L;j-NPB`V9P:.5?aAV\\Fw-'"+)2+3&#"&67>?!"56;y7/%%3  Vj 1B$o=?%s!-)-  /  / )!Vq+ "+%6&#"327"'632#"&7>32326?`f{qbu <{V?jXn%\ 65#Z#1N  !ޛ^<-B  ($ "+32676.#"'672#".7676% /^mN XD].d_Hl\3Xa1otSL𴨠'f\B,FV@n{}mQ'G "+"'67633!27''5N#V0&RL RNP48 #. *$ "+"676&#"&76767'&7>322676/Vpy6Ad{%Lg+!֔ZR%ucI{\'yzNiX/X`ۥi;5!qyil:}{y '# "+%676&#"3#"&7>32&76 /\lN^X^.d]Hl\3Y`3o SN𳨟9l,FV?o{}lR'G TX!)^ @ @3$" "+>32#"&546%>32#"&54!#2&#"'763>76322&#"'7637654'!"B'!+@'!-@'!+@'#+>VTJ7q#H^5+%8;Yq9d3g9'5)! '5+  %7+'7-! N"^1''>X)J!;6''= [#N!1A @ <4)" "+>32#"&546%>32#"&54"32654&!"547!2B'!*?'!-@'!+?'#+9Ҝ5p;a; '5)! '6+! %7+'8+# ZQ> n_ JQ/JN!h 5" "+>32#"&546%>32#"&54".547654&/"?327#2>7654&/"?327#A'!+@'!,?'!+@&!-)3_gL0+23@D3=8 y8R`\H u+06!3=:o9'5)! '6+! %7+'8+# 2MUAN '',+=pIf@\xRX ''.-_U%"+#"&5463232676?+ #"&546323267> 76#"5476?>3267632#"&54&'&#"32632'"&54632326767654'&&'.#";6N\a\%'88'64+"? 76#"5476?76726722?632#"&#"3272#"&'.'&#"#"54>7>0:ZQX" 0"?767#"5476?>7672#"'&#"!7>323#3272#"&547! #"&546323260%5 1%Y<+FO^)!HB5'/) !Ry3q%3H32}N"?32#"&4654#"32672+@ m0IQfdD%67j/% F!    'Cfi=Y3?J TCF=ZN=ANd) ?+V}3%BLCOÓ?'9 1 *R?FH+;_ +7? b`r*$! B*7;bVTAO#E6'%7'5!N݉B/QLn:cD;"+%#"547654/"57672>3232672#"547654&#""'&5467>32#"&4654#"32672) T+@ m|YNN'# T#N"%5d5F=ZN=ANd) ?+V}3%BLCOÓ>!(7l?'9 1 *Rԙl'tb#<+#C1GV3-bVTAO#E6'%7'5!N݉B/QLnB}F5"+3#3272#"547!27#"547#"5476?>32!7>32o1w%=FdAy'Bf 7D{ 1m !R !Ro <q'hj^+y1R'?T HW~3i !fR##7s\fR##7w c9)"+6&#"#"&4654#"32672#"'&5467>7&54>323#3272#"&547#"5476?>76"M/yX2>#,?+V}3%BLCOiF=ZN9B-yy3q%3H31} 1m 1#ER9N( (7(%7'5!N݉B/QLnbVT>M5Jv`v\ <d:'\v(6)- !f;18B ?942$ "+%27#"&'#"&76%76'&"#"7>32632!6&#"327%+!N\ys5a)-)y 8$P/ %u}!XHf%KJHVo`7/9hT/<35-)\X}gۓk55c!E7^- D,jz rkR8"+327#"'$#"'6767&547!26732672#"54?'#"&5476'4#""'632326?>32"32654&5mڔ0:'!JX68; 6y YsN'# L#}o;NZX%# J+Y=H}; %V 9Ҝ5\q%\mb(''/d%JQ/JM$!Z c-b#<+#CB{OQR=MXb#;+#DH;LR##7 .7!ZQ> n_^^"+!2672>3232672#"547654&#"#"747654#!"&#"&6?>76&+""'67;/nYNO&# T#N#%5eN T+5/ ';FP=@=7 !(7l?')#3/)-',-/!Jec =:("+276&#"'>76&#";"7"?67>?>32#&7+\b!!wN5% #=Jq '$+  dg }FhP&ryno T -Nh9Zdl1V=')!ZAǐ{wT0F;&" "+2'>54&#"'6&#"'767>qyQ a uL-1/`t !Z \-F}^E1JP^L2R9/0l= ZS"+326767#7267>32#"&5463232654.'.547>54&#""&4632? )3g%Gkq1I fnc3h^3BT#TPju5`F}VhZ>\5BThD5khO2"+'.#"#"?!27654&'.5476$32'.#"#"?!27654&'.5476$32?-ZD`;XV-X6 >-ZP@[`3 j@-ZD`;WV.X5 =,YP@[`3 j!9;NP's^d7QlB#*u*"Un( :UnA!#>!9;NP's^d7QlB#*u*"Un( :UnA!#>#ZXb\YS"+>32767654&#"463232>7&#"#"747>3232754&=62#"'#"&54&'"N5'/>-y^/2DLHqȐrDV`=48DsDZA#3D"P)`9bju; F%"M:`u+#%Z`hTVR#- 3H\{<9 < L foZ!@7q )X-2:43 "+;2�'7632763232&#'74;2705'!&%#3 H %  V; -9x *1d< #N 'k=P ++ X 33=( ++ +; ; = 8' "+327654#" 3 7674&#6;267654+"?3263 #"&#@%`)RPw2yu //) H1 >5 jFBJ٬)P\= ')11;k-#oD!"+"&547632;54&#"32673 /TX 5}u3dZ ^ޡ+:#%Z jL7DT!b[;!2.&"+76;27654+"?632#"&#'%3267654&#"F /RH/F \`FD\+FG+]' +ߪ15`+ \=#;E1"+.+"326?6;2#"'54+;267!''76;27654+"&?76F;;B)' #+# AN3Vn14Hc  /T F1˕J5DJ1#+  %i .? !i^`- B /-;C7!"+326?6;2#"'54+;2&#'76;27654+"'&?7!54&+"?)' #)# AJL) @  )X H1Ǐ89B<H#+ %g .? + ++ @)-V Fpq6 "+4654+"?7#"#"&54763232754"327mN)=  H #ż 5Z9! 8) ojL - && ?Ǥ7:'#?V=DW" "+%!;2�'74;274654+"?37+"!654+"?307+";2�'74;6qIJL+ @ )VL+ @)V=;N) >  +X L+ = +Wy\+ ++ @ - )) @" - )) @@ + ++ B) "+32�'76;27654+"&?37+"J)@  +VL)@ +X7N7 ++ L --+ f& "+2654+"5?307+"#"74762JuQK< @ X =XVw - ++ @rHT JH LX9 "+67654'"5?307#";2+7654'.'32&#'74;27654+"&?37+">?J'> : ?XNH#C   z.)BH u? +VL( ? X 1! -- !?91-7 ++ L --+ B;u,"+%"#'76;2674654+"?37+";27! !\H  -3% L+ @)V1ǁM6Do+ '- )) B8 #)E$"+ #"' 32&#'763267654&#"5?%#"32&#'76;27^1 %3 ^@=3+7 J&P)7 u= )=J) ++ #<  +'%'R) ++ D;\A("+"'&#"32�'76;267&#"&?327654+"5?37+")'ujE+  }? +-5 T  iF+ : +W7-1H? ++ 6"L'}/5D +- "+"3267674#"&547632Z-'/ /PӰR=VCǦ5>)Ӫ2; 7."+27654#"4654&#"5723263 #"';2&#'76;6Z)`/ \/F #)-V-=L) @  )Vo\/ 1w+ ++ =8 "+"3267654#"'3267#".'.#"&'6?.547632F-'/ $qV"D+!Z%w3T)!"F)d`s7 /PҰR=VCV'{! /' `)5>)Ӫ2; H*"+654&#"32654#"573263 3#".=4'#;2&#'76;2DNR\3LP}q {X%/_3F819:-?$FL @  )X)9.\/1Z N5%131%;# - ++ ZN2."+054&"#"&#"6?327654&'&547>323N5uuȇ^. 9uZ*a\w`ZfXAdFR# Xyt!FR#V u50$ "+#";2�'74;27654+"'6733!273.TN J+ = +X AiBK;3%$301 1%9.uHL+ ++ @-RwhesV>"+"&547674+"?37+"72>7654+"5?37+"3y fL%> +V\0;fNyF7 ZL+ @  %-)T-y3/ - ++ BF^-R8F/?mR= - ++ !!q. "+674+"'?307#"#"'.#"5?37+"H  = =:+101  /+ @ Vs1# ++ +-RR -- 1V "+"'&+"5?307+"676/&#"5?307+"67054+"?307#"#"'&#")u 9R<#+g V X@ /+ d 0+yC X+J/9? H)PP +) w%PB ++ -  +- HRR)#'kA"+.#"&?3273+"?676&+"&?2327#"32&#'76;276'&#";&#'76;27676'j%&-  NVb >' !1 Z5 /D1+7 Zs  &5  RH''7-+  -! -- /1+- )  +- L%C: "+%&'"&?2327+"676&+"?2327+";2&#'76;6@N --+ /   LA"96?L+ >  )Xy#?+- !G# -+ @k + ++ ??'"+"'6733!623!26?!"547676&#y\5=10HM/5('T\;'3Z#fy/GZ<c +"+!"747>3!21).3 8-d ?G A@% "+#"/7632;2&#'7632763232&#'74;2705'!&%#3}[ #HG H %  V; -9x *1d< #N 'k=`(/ ++ X 33=( ++ +; -X ?G A@%"+632#"&767;2&#'7632763232&#'74;2705'!&%#3 ?(/  H %  V; -9x *1d< #N 'k=T/' $ ++ X 33=( ++ +; -5 =E ?>#"+&''67;2�'7632763232&#'74;2705'!&%#3\-l1{8m#T H %  V; -9x *1d< #N 'k=5mkN]\c ++ X 33=( ++ +; -EM GF,!"+267"'&#"'>32;2&#'7632763232&#'74;275'!&%#3|$7!/J3EA$'7/)`@AG3; H %  V; -9x *1d< #N 'k=-> 6-/4 ]Y:- ++ X 33=( ++ +; - HP @ JI/$ "+2#"&546%2#"&546;2&#'7632763232&#'74;275'!&%#3!-H'!,Es!-F)!-F H %  V; -9x *1d< #N 'k=+!/@+#-@-!-@+!/@` ++ X 33=( ++ +; -P EM @ GF,! "+"2676&>2#";2&#'7632763232&#'74;275'!&%#3.F *[G ,W}JG H %  V; -9x *1d< #N 'k=C0.EE.1BdcGEeT ++ X 33=( ++ +; {^ljd5 "+%267!0'76;27#";2�'7632767654#"5?30!&+"32?6732#"576&+7 ;674#"Ro-1N)  NI+?  $m  B7 9 37 Jw= !% # $uL/C+F`XR- BA ++ N +H/A+ m-+b -# !'fD9. "+"'7.547632;54&#"3267632#"'677265&5) k /TX 5}u3dZ VfB7CcHHH)8D ۚ+:#%Z jL7DT![Yr6/Oc#!9/1;y S? "+#"/7632.+"326?6;2#"'54+;267!''76;27654+"&?7[ #HG6F;;B)' #+# AN3Vn14Hc  /T F1˕u)/9J5DJ1#+  %i .? !i^`- B /-;R S?"+632#"&767.+"326?6;2#"'54+;267!''76;27654+"&?79 ?(/ 6F;;B)' #+# AN3Vn14Hc  /T F1˕N/' $J5DJ1#+  %i .? !i^`- B /-;3 P< "+&''67.+"326?6;2#"'54+;267!''76;27654+"&?7?-l0{9m#T)6F;;B)' #+# AN3Vn14Hc  /T F1˕3mkN]\cJ5DJ1#+  %i .? !i^`- B /-; ] I "+2#"&546%2#"&546.+"326?6;2#"'54+;267!''76;27654+"&?71!-H'!,Es!-F)!-F6F;;B)' #+# AN3Vn14Hc  /T F1˕+!/@-!-@-!-@+!/@J5DJ1#+  %i .? !i^`- B /-BV 6- "+#"/763232&#'76;27654+"&?37+" [ # HG J)@  +VL)@ +XR)/N7 ++ L --+ BR 6-"+632#"&76732&#'76;27654+"&?37+" ?(/ \J)@  +VL)@ +XN/' $N7 ++ L --+ B5 4+"+&''6732�'76;27654+"&?37+"j-l0{9m#TJ)@  +VL)@ +X5mkN]\cN7 ++ L --+ B @ 7" "+2#"&546%2#"&546 32&#'76;27654+"&?37+"P!-H'!-Fs!,E)!-FXJ)@  +VL)@ +X+!/@-!-@-!-@+!/@EN7 ++ L --+ ;:4("+#3267654&#"6;27#73654+"?632#"&#'V<D\+FGFx /R<CH/F \`P \=#1]P9' +ߪ15`+;\AVHE("+"'&#"32�'76;267&#"&?327654+"5?37+"267"'&#"'>32)'ujE+  }? +-5 T  iF+ : +W$7".J3FA$'70)`@AG37-1H? ++ 6"L'}/5D +- -> 6-/4 ]Y:-\ , ' "+#"/7632"3267674#"&547632F[ # HG$-'/ /PӰX)/-R=VCǦ5>)Ӫ2R , '"+632#"&767"3267674#"&547632 ?(/  -'/ /PӰN/' $R=VCǦ5>)Ӫ2- ) $ "+&''67"3267674#"&547632-l1{8m#T1-'/ /PӰ-mkN]\c\R=VCǦ5>)Ӫ2"2 -%"+267"'&#"'>2"3267674#"&547632$7!/J3EA%'7/)_F40-'/ /PӰ-> 6-/4 ]Y:-R=VCǦ5>)Ӫ2 &6 @ 1) "+2#"&546%2#"&546"3267674#"&547632{!-H'!-Fs!,E)!-FB-'/ /PӰ+!/@+#-@-!-@+!/@R=VCǦ5>)Ӫ2 @<3"+%2676&#"6+"326?6;2#"'54+;27!+"&76;3L5-=N-+)5{7 ?)' #' " AN 9R3\N9/-G<5%+{1#+ )e .?/ !+ $""+"&#"''7&5476327'326767&-#KH/bw3vn /PӖlm5qbH'R=`D~_X<'d5>)Ov'zh2MVCR\ L "+#"/7632"&547674+"?37+"72>7654+"5?37+"L[ #HGy fL%> +V\0;fNyF7 ZL+ @  %-)T-X)/y3/ - ++ BF^-R8F/?mR= - ++ !!qR L"+632#"&767"&547674+"?37+"72>7654+"5?37+" ?(/ y fL%> +V\0;fNyF7 ZL+ @  %-)T-N/' $y3/ - ++ BF^-R8F/?mR= - ++ !!q+ I "+&''67"&547674+"?37+"72>7654+"5?37+"-l1{9m#Sy fL%> +V\0;fNyF7 ZL+ @  %-)T-+mkM][cy3/ - ++ BF^-R8F/?mR= - ++ !!q V ' "+2#"&546%2#"&546"&547674+"?37+"72>7654+"5?37+"!-H'#+Fs!,E)!-Fly fL%> +V\0;fNyF7 ZL+ @  %-)T-+!/@+#-@-!-@+!/@y3/ - ++ BF^-R8F/?mR= - ++ !!q^CQKE: "+%&'"&?2327+"676&+"?2327+";2&#'76;6632#"&767@N --+ /   LA"96?L+ >  )X ?(/ y#?+- !G# -+ @k + ++  /' $;8B@;,"+632#"'32"&#"#'76;>76&+"?37+"3276&#{o3`'1    1) )1yJ 1Z`1[d>9Ra)h Z+ ++ )' ++ \omCO[ UPID: "+%&'"&?2327+"676&+"?2327+";2&#'76;62#"&546%2#"&546@N --+ /   LA"96?L+ >  )X!-H'#+Fs!,E)!-Fy#?+- !G# -+ @k + ++ +!/@+#-@-!-@+!/@?f)PE6 "+32�'76;27654+"&?37+"2654+"5?307+"#"54762J) @  +VL)@ +XuRL; ? X =XVw7N7 ++ L --+ % - ++ @rHT JH Zd2d`E."+054&"#"&#"6?327654&'&547>323!54&"#"&#"6?327654&'&547>323N5uuȇ^. 9uZ*a\w`5uuɇ^- 9uZ)`\w`ZfXAdFR# Xyt!FR#V u5ZfXAdFR# Xyt!FR#V u5E"+76$!23#"&#"#6?32676.#"'76.# ;2+74;26H1 H9oĉ}% 69HBv mR 7PPP)  #+1V!qs5^\fuLN:%%+ %3 -- 5;:4("+#3267654&#"6;27#73654+"?632#"&#'V<D\+FGFx /R<CH/F \`P \=#1]P9' +ߪ15`+NH2:G C;43 "+;2�'7632763232&#'74;2705'!&%#3267#"&7 H %  V; -9x *1d< #N 'k=kf!2+`luP ++ X 33=( ++ +; RgbW~5-CKED+"+"&54670'74;275'!";2&#'7632763232&'327 #37TjpZ #N ' H %  V; -9x *1 gA5JD!n=5\RJ2+ + ++ X 33=( + P3FJDEm Dh!/)#"+"&547632;54&#"3267632#"&7673 /TX 5}u3dZ ^ ?(/ ޡ+:#%Z jL7DT!b[}/' %D?!,%""+"&547632;54&#"3267&'7673 /TX 5}u3dZ ^$4e.{9]!^ޡ+:#%Z jL7DT!b[4~kMOiq;?!2= 63.&"+76;27654+"?632#"&#'%3267654&#"7&'767F /RH/F \`FD\+FG4e.{9]!^+]' +ߪ15`+ \=#~kMOiq;5X"+"&5467#''76;27654+"&?7!.+"326?6;2#"'54+;267#"327fTjq[֔  /T F1˕6F;;B)' #+# AN3Vn14HsA6JC!5\RJ3- B /-J5DJ1#+  %i .? !i^`X3FJDE;7EPIF1"+.+"326?6;2#"'54+;267!''76;27654+"&?7'&'7676F;;B)' #+# AN3Vn14Hc  /T F1˕4e/{8]!]J5DJ1#+  %i .? !i^`- B /-`~kMOirwH6C?7 "+4654+"?7#"#"&54763232754"327267#"&7mN)=  H #ż 5Z9! 8) ojf!2+`luL - && ?Ǥ7:'#?VRgbW~;u`,:4."+%"#'76;2674654+"?37+";27!632#"&767 !\H  -3% L+ @)V1ǁM6Do1 ?(/  + '- )) B8 #\/' $;u4)"+;27!'"#'76;267'74654+"?37+"U1ǁM6Do!\H  -3% ACL+ @)V/=jl #+ '/Z=p:- )) B;\bAOIC("+"'&#"32�'76;267&#"&?327654+"5?37+"632'"&767)'ujE+  }? +-5 T  iF+ : +W ?(/ 7-1H? ++ 6"L'}/5D +- /' $;\7ALEB("+"'&#"32�'76;267&#"&?327654+"5?37+"%&'767)'ujE+  }? +-5 T  iF+ : +WU4d.{9]"^7-1H? ++ 6"L'}/5D +- ~kMOiq;uQF""+%&#";2&#'76;27.+"&?!32?6&+"&?37+""&7672326sj ++ = +T3#  r-) : +T9VP=o 31/?\7c1H %3 ++ X!?{1!#7++ Z^t=-3-<%{.= @ :4+#"+"3267674#"&547632#"&767632&767632Z-'/ /PӰ #& ( %R=VCǦ5>)Ӫ2  /  ++;` HV PJ*"+654&#"32654#"573263 3#".=4'#;2&#'76;2632#"&767DNR\3LP}q {X%/_3F819:-?$FL @  )XZ ?(/ )9.\/1Z N5%131%;# - ++ #/' $;7 HS LI*"+654&#"32654#"573263 3#".=4'#;2&#'76;2&'767DNR\3LP}q {X%/_3F819:-?$FL @  )X4e/{8]!^)9.\/1Z N5%131%;# - ++ ~kMOirZ`2@:4."+054&"#"&#"6?327654&'&547>323632#"&767N5uuȇ^. 9uZ*a\w`L ?(/ ZfXAdFR# Xyt!FR#V u5/' $ZyNI>"+"'7.#"6?327654&'&547>32;54&"632#"'732654&5'JBx . 9uZ*a\w`5uuĄ*?Q{^NLN'1J/ Xyt!FR#V u5ZfXAdFR}fC323%&'767N5uuȇ^. 9uZ*a\w`4e/{8]"^ZfXAdFR# Xyt!FR#V u5z~kMOirH<"+#";2&'632#"'72654&#"'7'74;27654+"'6733!273.TN J+ tIE5DkHI;89X()5 r +X AiBK;3%$301 1%9.uHL+ +l2/Ld#%8/ + @-RwhesV8' "+#;2�'74;27#73654+"'6733!273.+"}GJ+ = +X F<AiBK;3%$301 1%9.?TN <O+ ++ @JO-RwhesVHf>IS QLD?"+"&547674+"?37+"72>7654+"5?37+""2676&>2#"3y fL%> +V\0;fNyF7 ZL+ @  %-)T-A.F *\G ,V~IGy3/ - ++ BF^-R8F/?mR= - ++ !!qHC0.DE-1BdcGEe{>N] ZTKC"+"&547674+"?37+"72>7654+"5?37+"#"&767632&7676323y fL%> +V\0;fNyF7 ZL+ @  %-)T- #& ( %y3/ - ++ BF^-R8F/?mR= - ++ !!q> /  ++??h'5/)"+"'6733!623!26?!"547676&#632#"&767y\5=10HM/5('T\;'3Z# ?(/ fy/GZ<c /' %??'/.*"+"'6733!623!26?!"547676&#&>2"y\5=10HM/5('T\;'3Z#FP.FPfy/GZ<c O;;O;???'2+("+"'6733!623!26?!"547676&#'&'767y\5=10HM/5('T\;'3Z#4e/{8]"^fy/GZ<c ~kMOiq;"f,>3-"+%"#'76;2674654+"?37+";27!2'>76#"&7> !\H  -3% L+ @)V1ǁM6Do&71 d>X 87+ '- )) B8 #fZ;B # W)+-!*50;41$ "+#";2�'74;27654+"'6733!273.%&'767TN J+ = +X AiBK;3%$301 1%9.4e/{9]"^uHL+ ++ @-RwhesV~kMOir0C71$ "+#";2�'74;27654+"'6733!273.2&7>76&'&7>TN J+ = +X AiBK;3%$301 1%9.:1b J` SDuHL+ ++ @-RwhesV `PWe!?A%3ZND@%"+2&7>76&'&7>54&"#"&#"6?327654&'&547>323l:1b J` SD5uuȇ^. 9uZ*a\w``PWe!?A%3ZfXAdFR# Xyt!FR#V u5B)10, "+32�'76;27654+"&?37+"&462"J)@  +VL)@ +X;N;;N7N7 ++ L --+ R::R9GVNH! "+;2�'7632767654'"&?327#"32&#576;27657&+"0;274.#q=  `> L1^# NV  b +1 < R FT &CF; ! +'P+!)) / i5' ++ <D9%(  ?1 "+327"&547#"&5476?654#"#"5465>32727Th F1X3 =J-5Bw Ij ) A #  .!?1 9C+/!<1 {5))X7 % 9]+);  !- ,*# "+654'"7673632#"''67327654&#"\J@VFHI ^X'%;2 2-#;b - #3dP'/s1#)BP-)7.=B%"+2#"=4654#"327#"&547>`NT4-E5w FL`ot`a B/;)=wr-#%NX!yiV1#yV -,!"+&#"727"&547>327654'"767327#^F/32#"'&#"32#732&''76732RHT@!T;(## 1'D sJ))j0}  6R;"'7V%# "" ^1@Ud `ZFA;#"+"'&'"#"'3272>32#"54767&546567&547>3262327654'&""#&654'"32>'RD$ 9! !s-s+J'RB)={!31/L !V3>V&2/)Vd, +5#'`ih PD0B;33 Ti7+)H Nk t-Z7654'"767367676&#"?37#"72&''76/&#"&) J1y !{!ZLjVh 9+ TN\PAJ/L9 %% # - #7$`  ## GW J  *-Z`"+673672>32;2&'7473>?654#"732&'7673>?674#"732&'7473>7654'"NZLjVo&k-s > J1 :J-X@) J.9 L1]E'  N3y #K #kji%Dq/  %% #NR %% #)N\  %% #A % "+2654&#"'4>32#"&Ff6/;Y%?3IuEo]ĈchzLZs7327654&#"}\Jb@DJ a;%/)  N3y #1!/ ';P . # ?=hR-#q) %'#`-)=,;@B! 1 "+&#"32672032�76;2?"&547>32^F/ '% 87cT'"o51! "+327#"547#"574326?32#H)3RVLVN 99R- =###HNw1DN%5/ 6 "+327654+"?30727#7"54764&+"?307PI-cC) N1-`dBjTs > J/+ !LZ) #Z- !kjq)# ! #9} -+"+.#"?37+"6754"5?307#"#"A  L$o 98D+P+15 ## 4 ## Jg!"+326?2326?!"547#"'673/%35!)7}J3+%) +R!3#hE gVX_B4"+>767#"5476?>7672#"'.#"32'# #"&546323260%5 1%Y<+FEXg#'H+'/) 0"?323>3232654&#".# #";#"'&#"3276?!#"'&#"3276?327674'#,)HI8-/ ++X!bEH'#gX#jC#2kh/  /=NK''!#&#q+Lp!}/5FF%)##!'j+Lw1 pfB:A:> `-=V!680B) )L'+)fP!#Z7E%)++dL -k#+ s; B0 "+"3&' #'#";2&#'76;267"'"'767>7.'&76${3ejP-3h?Njk ;% )=8G 5=< ]Z5!Lw'7uJ7{'=}Vw`j_;%+1--''+/u) 9DR<w;+N "+%"'.+"?30%+"7'&'#"?307+"7676&+"5737+"#"'Q#1+ yM  N7O! j) R ;#hu !P V ))')--v5*-+%4˪N\+- 8)'#7}+-C -3 "+>.+"?37+""'.+"?3%+"7fQ!1> #2;P7\f% '')1/0|/,%%- "'%+HF"+&;2&#'76;276&+"?3%+"2767&'#"?37+".Xl *>5 R8q -;5Q5q\VZ{Z "A O'V1H1!B1)''Z/+''ZOJ+F1''}wEd#3%; C* "+676'&#"366&#""?6763 7.'&#;2&#'76;2V!F9dSi % @FXYT%RH;P!7XPVQ! \ *<5R6q^yMJ-1'ffw7=×) 'GfM/+''Y^-&"+#"&5463232654&'&67327B]=1o&ZCB*T D\M)ZE/hq9'V6+&7=%?"8&%%D11=82%"+67&'"&#"'>323276327#"&54?&#"'3254ZJ>%5'DE#7x4`PC3:7ZLL?B)HPv+=J/]FfR@G=L^%Am!+t7jReNN<'}f}N#/!;,!"+%6'&'7>7&'"&#"'>323276&54>32326y'A5J+Z/!'HE7v6ZV53:5wR'^Z `k%#/)NL/3:-K6p5+GZC"1#kT<1='C15B"+>32#".5463232'"&547654#""'6323267 Vq9]\s#GP37#'1+%hJ LU$# J'O  B% D+7wb{+#-#'#\X!Aob#3)#DB'/e('; 6(""+2.546%2.5462.546=)2 5.WP@Ssf)2 5.WP@Ssf)1 6.WP@Srf,$%0 @(_!tTU ,#&0 @(_!tTU,#&0 @(_!tTUO'; 1* "+462&7>54.'&%462&7>54.'&%462&7>54.'&Gc=-oOEGc=-oOEGc=-oOE/-:H.#"&7$72&#"&67>L 'rP P2m (R sY)1 $= h|i{; /  / @fq""+%26?&#!"'%>76&#"&'>32VT;4$' H1Bp݇) u;DV1 +σsq'Q< /נXPV V}yd+f: "+""&7>32#"&7>3232>7!"7>76&J#P\ L!޼>1uj'Ѹb >/  !N`R3;#K;L;j-NPB`V9P:.5?aAV\\PX)#"+)2+&#"&67676?!"56;be%/r% N Bj m%;@'ud!-D /  / /VV?+ "+%6&#"327"'632#"&7>32326yaf{qbu ;{V?jXo%\ 65#Z#1N  !ޛ^<-B  ($ "+32676.#"'672#".7676% /^mN XD].d_Hl\3Xa1otSL𴨠'f\B,FV@n{}mQ'G "+"'67633!27''6M#V/'RK RNP48 #. *$ "+"676&#"&76767'&7>322676/Vqy6Adz$Lf+"֔ZQ%ucI{\'yzNiX/X`ۥi;5!qyil:}{u '# "+%676&#"3#"&7>32&76 /\lN^X^.d]Hl\3Y`3o SN𳨟9l,FV?o{}lR'G D "+!"5463!2/11#?2IRD$-4:@D@DA>;850.(%" "+747!22#"'".7 $7>7! "&!6RTL F8m\ -Κ w !>TJm /> *ql7; %RGyNj7Z%du2:+/,*# "+654'"7673632#"''67327674&#"\J@VFHI ^X'%<1!1-#;- #3dP'/s1# )AP-)7-=B-J%"+2"=4654#"327#"&547>{NT4-F5w EL`ot`a JB/;)=vs-#%NX!yiV1#y;{ -,!"+&#"327#"&547>327654'"767327#F/P\ hT#\HdBw/P?7y;bT''o+- !{- !F8/ "+#"576;>32#"'&#"32#;2&#'76732bHT?!T;'## 1'DsI)) j/} 5};# '7V%# ""  f@Ud `YFA;#"+"'&#"#"'3272>32#"54767&546567&547>3262327654'&#""#&654#"32>^RD% :! !s-s+J'RB(=|"31/L !V3=V'1/)Vd- +6#&`ii PC1B;33 Ti7+)HNj t-Z?6#"2&#76?>76&#'"&57672+#qS'4 N1 /f1^: R5}$b +|?h$$ \ $$ !A$% 5i&1.) "+%2�7637>7654#'"&?672'4632#"&&G,$ \; &< +|@   ;#'0.  %% " . %  !2,,L +"+2"&7>6&#'"&?672#"&76723267e+3(^ *|@ =028=-H *&,:#""#{A#% 77Յq-3&3<uL "+7732�7473>7654'"767367676&#"?37"72&#'76/&#"&) J1y !{! ZKjVh 9+ TN\PAJ/L9& $$ #- #6$`  "" GV J  *-/% "+6&#'"&?672&#""+76?>7 *|@h N2$ %fA$% 5V $$ !J`"+673672>32;2&#7473>?654#"732&#7673>?674#"732&#7473>7674'"{ZLjVo&k-s > J1 :J-W@) J.9 L1\F'  N3y #K #kji%Dq/  $$ #NR $$ #)N\  $$ #B,JD"+%672&#76?>?654#"2&#767>7654#'"&57672ApRy ;" N1 #7Q1`A* R5~ %= +|@ jt* $$ #"Q\  $$ !. %  FJ+:70! "+7654'"7673632#"';2&#76;>7327654&#"\Jb?DJ a;%/)  N3x#1!/ ';P- # ?=hR-#q) %'#`-)=+;@B;FJ 1 "+&#"32672032�76;2?#"&547>32F/ '% 87bT'#o5J0"+%632#"'&#"2&#"7637>76&#'"&57672kGL ;" A5$ \;9] %#8 +|? y@5 H %% A#% 17J-&"+372676&'.>32.#"#"&#"6- !$9)B%@L>L-b /B()>1/.: \[*![%K*%'&%="C~Y O=+'$'-8%:KGL! "+327#"547#"574326?32#G)3RULVN 9:R- =###HNw1DN%b\=6 "+327654#"?30727#7"54764&+"?307!PI-cC( N1-`dAjTs = J/+;!LZ) "[- !kjq'% ! "T=-+"+.#"?37#"6754"5?307"#"A L%n 97D+P +15 "" 3 "" Ifh=J4"+#"'.#"?37"676&+"?37"#"' #"'.#"&?37S#7  M'q 5R*U2+5!G71 /) ""  3  "" If/*"?A "+"676&+"&?37"#"&7>327267676'.#"&?37P5H U-Y823a>7.  E%  UI'  1  " KPX$S<5Z"-L!"+326?2326?!"547#"'6730%35!(7}J3+%)=+R!3#hD fVX' !  "+3##5#53534632#"&3264&#"@@ɋǍ;uwwu`??ӋȌ馦'  "+!5!4632#"&3264&#"L^ɋǍ;uwwu!?!Ȍ馦:"+4'.54632#"'&'467654+"'6763!232>+ N/!+IDkX{yK`R}'R17)/!#Q)''9bH)lH{b%\%+VZ7L?5BT-##`T )O#b>#q"+#"'%7q'7߲'\"+"547%+!J\1 "+&''67L=e+sJwyhY+;DL3+Ns1 "+&'7675m)jUv{1\g)3JP-)RqZB"+"&=332673=uy9'P7R7Zu\)!E8m{L"+2#"74?6#"74?632) V D##  @)sV"+"/&54632%4632#"/&1;' 78C'L  ?' %:  ZD"+2#54&"#>{k1)+ %H7f;"+#"&54632267;-JZy8!5Vq'P^P=34! &H7#d\! "+>32#"&546%>32#"&54%B'!*?'!-@'!*?'#+'5)! '5+! %7+'7-!  V?"+#67674#"#"4632L 79/>?'-mPN?s+:J/+'5"VQA%;^ 7!L5"+7!67>7;!!!!!3!!!!!!!!!!!+>7!7!>7!7!7#73679  !  Q L  3~ QK5 L4t 3 3J C 1 2+ *&0+98/> 07'+/ $2$Q/}/0/3-8<0/g . -# "+"&'&32>766#"''7&327%LZX# M b9^#9:'M`;!=qCV>lV87)]AG<2vw"#܆\L}( ǪCS#d=3Ry #N>; <,^`! "+7>7.76'&'77676=RTP!79'+;)I%?DH=6&\@ud}o q3; 562"&7>2-5 a/ 5W<NZ6 LZ3NHJ?`i) FJPLJ-@@-/@@1F)21, "+%6'&7>32#"&7>7676?6226"&7>2 :!* yjgXJ^!35 ZGNP YLZ3 LZ!!')/dWNLA/;,HMZ\;R89iEdHZ@@-/@  "+'76''76eð`dh́qfpNh́qftN;  "+767&'%767&'V`iNsfqhNsfu{ "+'76ÙufnsultN "+?67&'7skm{sotND "+!"5463!2/11D"?1I`o*}#"+#"&54632326767674/&7632&%9TZJH`%7D%T@A%RL*\3/ XatR3!2 32672#"547674/&7632%{'\N'# T#`%R-* N()Bnb#<+#CN%!+6g . "+%2676&'"7>?&7>32372#"&#"^'!R<%dL)^'w/+N%P5'3@ VT)/3N`GN3qo"+2 5~5jP$1.' "+%7'".54323#".5463232&#"326%Ǟ=Z-q5o7igs#GP37#&1+J=BZq941Ѓ7\\/ \l{+#-#'# P\'mZuL I1"+!"7>3!232672#"54?'#"&547654#""'632326?>32 $&rN'# L#}o;NZX%# J+X=H}< %V N()-b#<+#CB{OQR=MXb#;+#DH;LR##7 .7b j""+!"7>3!247'#"&5476'4#""'6323267>32326?>3272672#"54?'#"&!$'mb7TLU$# J)K-1F V]^ #3)#DX.57C B)7wh=LZR##7 .7 -b #1+#CB{OP,o!7OVl=@ RiFK!PX@Tb`b`V M = M = M = N>@Rb`b`  UV M = M = N>YY@!lkhfa`[YUSPNKIEC<:$$"#*& +#"'&'.#"!6#"54632327667!'"54>323276>?#"'476;67632632#".'."32'(7IFs%jJ'>5j'!#")%FF 7/q#'#!''LM 6+  0h/Wz5yT#%)BZ$! 2A@f#N/d,+)%FX#f)+'LL )yH5Z:-# #VVPz -_];K*PX@Hbb bM== M= M = N  >@Fbb b UM== M = N  >YY@\ZWUQOIG><)!'&$#$%+6?#"5476?7632#"&54&'&#"3263272672#"747654'&+#"&54632326'@@+ 4{Bno=%'/ J}I% N* ]N%# T"a7!PY\a]%'87=:V !DbJ1?3+?+#%'`XD=+6gnb #1+#CD# #D&7',*"$__@^Z  @<bb M  = M =M=N>Y@]\XVSQMK*$'#$'"$ +32672'"547&'&#"32'# #"&546323267> 76#"5476?7672672[+}X>0:ZQX" 0"?K!PX@c bbb`V M =M= =M =M=N>@a bbb` UV M = =M =M=N>YY@#~|zywuljig`^XVRP%$/#$'75%+&'.#";66?+ #"&546323267> 76#"5476?>3267632#"&54&'&#"3263272672#"547654'&+"&546323268"7NH o&'64+"?K!PX@dbb  b  ` V=M=M= M=M= N  >@bbb  b  `U V=M= M=M= N  >YY@-~|zywunmigca\ZVTJHFD?=54-+)'$"#! +0#".#"!6"32'# #"54632327>6?!#"&54>32327>?#"5476?>32>3267232672'"547&'&+#'/4HFs%%{#  4?Gh'!"#)%AJ$#- O/@7q`L %!"'''+!///  +FXg7DA4G=[+}X>B-V2BfLXTB -!yd,+)%@!b3>))+'T{h%/PvcH5`J4'RZw D3#Z#3KPy#FN> @<  b  bM= M = M =N>Y@NLHFB@=<$##'"$(! +#"#"&546327267>?6&/&76767%232'#3272#"547#"5476?b^%w.0V-J5`+9$%!*! A*%JfsX5  31w%=Fd 1m{a`\ZUSPONL.$$9#*$+7654#"'"&'&#"+.5463232654'&5467&54>323#3272#"&547#"'476778}{Q}%{'1e?Q!I!!ZJL-7?KXhLf3NNXb'!#'1qJEo3=Dh_9@"+'55V<&UT&wl_<1^%'2-e1^d )Pss;r%vu7.SbA7=+Vt!V!5Ob'='O?'&)n!SX!Xp!hZEjTm$b"5_&5` C$yt+Hd|o/R/='j\=F5zF-;"7ttSZt}GGG=+VVVVVV5555='='='='V!SXXXXX7XjXX~{55555555$7++++ddVVV!9V!5555555555OOOOb'&b'&='5='5='5='5='5'`' &) &) &)Q&)&) S$S$S$^S$XXXp!p!Ep!hyhyhyhyZtHZZt++++++EjdjT|oT|oT|ody#b#(&#ObjX"~O ='%'r?sX%{pjnZdS5 dTIo+junwn}n}(fb !!f))&1KVX%5X+++++5V~{~~G%XX+h3` ! !$O3%QS$V~{XVV5555='5='5XXp!p!++hyZtb'&#nVJV55XXXXjd3(`&%^&)ZfyIH#p`d~~X7VVV``5$&&MK;Kn +  5$J~=m3p%'?d)1n5n5nn\jDd-}IJnHh3z7bbPpBuvB7z/dj r=5++-ott{ 5 \XfX'>l}lx$$m$v$Gp 5fi1D1-g& Y-11@SOV\Fp3It-~+~~~R~5|~O1A- "[HLh*2-=NptT29%#aA \7_ai]2^>| w4k8_<F772@a% FFuSqHHP`Z\SVt! +5Tb'X=''n!S/yX#!Zj\~d='j--7 + 3bG+G^  =;=K"X -O& ;P`^^9!mm5 5 h='='bJb%%^#O?V#t!=5/(y^#^#%Jn!b'X#!ZX#5b==I#v#=%|xu5TC++-QB$Cd{H+VVSCu255~uCy55`&+jt+d+Or^'=t ' 'V\-#~hhOO ?Z!!-;#7B#B/|-(yC%B%tF%F%|B%;#K;ht&9~3S( 3 C%XBtZtZM5/T%;F%|B%|; VV~{55X~X~/T(yC+h%+%+XCuddd5!Z;I#SoZ5 XjS`bGOLV(\7(A{9HVmt\\C\99Vt!t!t!V!V!V!V!V!M555555Y55V55_Ob'&b'&b'&b&b'&=5='5'''&) Z&) Z&) &) n!Cn!Cn!CS$S$S$S$XXNXX!!p!p!p!p!YhyhyhyhyhyZtZtZt*Zt++r+++EEEEEHHjdT|oT|oT|o&tddydydyBVVVVVVVVVVVV5555555555555555='5='5XXXXXXX++dddddjdjdjdjdZ#/3777777f1zIIIIIIIISSdxKKKKKKKKh/CA=&7v VS/77IIKK lZ=#9+G/X3IIIIIIII 7 $ A &  $s\n{9rt^rIIIIIhn\&%\KKKKKvn-X%(4299nnvSjj"(dX0SSE`91$$O$a$U.YudS?+ KZ&Z}d}wIbJZ!?B+M7t#ttt'tZtttt%t'+tTtmt)t,tTt=tLtt?t3t>tMtNTt.tDt5ttRtI,pL)%!G!X~+&d7  j7?%OTOTX5bf#?#?#\%SdSB)1bVGGGGGGGGGGGG;555V5l" 5##K#//?)7~777Zd7-n---+\-=/ # ^^L  b\DB; X33 d =9hZF3H3#"   Z1TV)7777#7~GSJ}IJnn777777777777EEq7777--`--`Onuzfy7SZSZSZSZZZS;S;dVdddddddddP1&1F$}}} tttV(zd"!MfRl+Ty(L7qP(FPEPPPAdVfqf5)?5!9/"%\7%XIHZ; A A Ajjsd#^2-y# 9#flVxu`+d P=f {hl+F?ZyVX__`__ "l=yh&;h;;~;O=7B^Ll;/);jz;^;Z;$b'?&&&&&&;;;;7B7B7B7Bh;;jjjjjj;Q?&ZVh;&&h;;;Ol;l;;;;j;;ZZZ???l;Z7B5G (\t+jptSZ1r5h9_V$%%YdOFf+P?uSRG+(-\;tpI,pv;LrbhTh''QLsf# ^\\jj'//=II`$P+VV___7y/yx{dddd$( ,  x ( |X\,Xtt ll P!#H$%''(*,,./</1,244505h556467898:;D=>h?@DABC\DEdEFGHI<J(KKLNOPPPQRDRDSSUdVDWXY|Y[\<\\]T^_H_`albc0d|eeftg8gh$jkn opqs(tuw<y@z|~L$,H(|h,,<T<$ l` TDT$˜ÐxŠƐǴȐhXDpptTx\Pܘݐߐtx(t@8 H@(D(4H  ( L d XP<$|DlT !$$%,'p(*+.0/01D2T35 6|78:;<> ?x@B\CE0FXGIJLMOQDRpSU8VWYXZp\]^<_X`abd\efg|h4hijl4mHnoprrsu`vwyz{|~Pp\XPtlx Dd(p|pP8XDPLh,@`X`8pƜ0ɘ(̔0Ϝ(Ҕ<Լ@׬Pߌ` |h\T,L88   l(< h!"%x&(*T+-P.x/|023<4,578;|?P@$@A,ACC\EFHHJxKLMO<P0PRShTUVWXZ[]^_aabdeg`hik lnTq rPs`uv8w8xxz{|}p}$$`lXdtpX4x hXh|`(84dhHxDDŨưTϨPPD0P(h 0@pT0\\d$l8(h` tP\<  L  p h|l<t< "<#%$&X')4*,|-/2344(6,7p9:\@B4CF\HKM,OQ`SlUX[`^abdtf8hjXln4pruwzx}40||Ll0h8pldDĈːHϤАP<רؼܜlސtPpp|,0L$lh$tPX8hLHl $    p  p8 X`ltP|d` !"l#p$,$%&P'P''(,(l)**+l,-<-./ /01$1t122H33485t6874890:;==>?@BDHxJ\KTMNOPQ QRSTUVWDWXY<Z0[H\\^^`hab|c cdDe4ef8fg8iHkmdo qTsDtv(wy{}}~TXPTtpL<HP`0@ LX0|DL(`@tl Àdp@ƸXǬT8ɐ(|P0̜p͐ʹXά$ШDlѐdҔdlP֜PהD؄$لXl\LLހ P߀ߨ00T,L<tt<@p<T0l4TDL < <   L , L  P  h   h  l X $ H  t  0 t  $ X   H x L  ! # %, & ' ( ) +4 ,< - -p - 2 3@ 4 5 6< 6 7 7 9 : >L > ?t @ @ AT A BP B C DL D E` E Fl F G H IL I J J K K LX L Mt M N O P4 Q RP T U, V W Y Z, Z \ \ ^l _ `H ` a b c d e fx f g hP i i j k4 k l m8 n n oL o p q r, r s| t u vH w w w x y z {x |t }p ~ X T L @ P P  l  H d P < $ h L | < 8 , X P @ 8  d 8  \  \ 0  $ < d d d d L h h |   $ , 0  \   \ $ Ũ  d \  Ȭ 8 , ` ˔ ̠ ̠ 4 ͨ <  l  l Ԅ X  ֠ < @ x d 4 ۨ  x ( X ݈ < ޠ L ߔ P X ( X X | @ x X (  8 t  p  ) DQh>+11b"x     8C7 8z    b ,> Dj    : :> px Linux Libertine by Philipp H. Poll, Open Font under Terms of following Free Software Licenses: GPL (General Public License) with font-exception and OFL (Open Font License). Created with FontForge (http://fontforge.sf.net) Sept 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011,2012Linux LibertineItalicFontForge 2.0 : Linux Libertine Italic : 2-7-2012Linux Libertine ItalicVersion 5.1.6 ; ttfautohint (v0.9)LinLibertineIPhilipp H. PollPhilipp H. Pollhttp://www.linuxlibertine.orghttp://www.linuxlibertine.orgGPL - General Public License AND OFL - Open Font Licensehttp://www.fsf.org/licenses/gpl.html AND http://scripts.sil.org/OFLLinux Libertine by Philipp H. Poll, Open Font under Terms of following Free Software Licenses: GPL (General Public License) with font-exception and OFL (Open Font License). Created with FontForge (http://fontforge.sf.net) Sept 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011,2012Linux LibertineItalicFontForge 2.0 : Linux Libertine Italic : 2-7-2012Linux Libertine ItalicVersion 5.1.6 ; ttfautohint (v0.9)LinLibertineIPhilipp H. PollPhilipp H. Pollhttp://www.linuxlibertine.orghttp://www.linuxlibertine.orgGPL - General Public License AND OFL - Open Font Licensehttp://www.fsf.org/licenses/gpl.html AND http://scripts.sil.org/OFLQ )  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghjikmlnoqprsutvwxzy{}|~      !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~      !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~      !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~      !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~      !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~      !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~      !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~      !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~                           ! " # $ % & ' ( ) * + , - . /uni00A0uni00AD two.superiorthree.superioruni00B5 one.superiorAmacronamacronAbreveabreveAogonekaogonek Ccircumflex ccircumflex Cdotaccent cdotaccentDcarondcaronDcroatEmacronemacronEbreveebreve Edotaccent edotaccentEogonekeogonekEcaronecaron Gcircumflex gcircumflex Gdotaccent gdotaccent Gcommaaccent gcommaaccent Hcircumflex hcircumflexHbarhbarItildeitildeImacronimacronIbreveibreveIogonekiogonekIJij Jcircumflex jcircumflex Kcommaaccent kcommaaccent kgreenlandicLacutelacute Lcommaaccent lcommaaccentLcaronlcaronLdotldotNacutenacute Ncommaaccent ncommaaccentNcaronncaron napostropheEngengOmacronomacronObreveobreve Ohungarumlaut ohungarumlautRacuteracute Rcommaaccent rcommaaccentRcaronrcaronSacutesacute Scircumflex scircumflexTcedillatcedillaTcarontcaronTbartbarUtildeutildeUmacronumacronUbreveubreveUringuring Uhungarumlaut uhungarumlautUogonekuogonek Wcircumflex wcircumflex Ycircumflex ycircumflexZacutezacute Zdotaccent zdotaccentlongsuni0180uni0181uni0182uni0183uni0184uni0185uni0186uni0187uni0188uni0189uni018Auni018Buni018Cuni018Duni018Euni018Funi0190uni0191uni0193uni0194uni0195uni0196uni0197uni0198uni0199uni019Auni019Buni019Cuni019Duni019Euni019FOhornohornuni01A2uni01A3uni01A4uni01A5uni01A6uni01A7uni01A8uni01A9uni01AAuni01ABuni01ACuni01ADuni01AEUhornuhornuni01B1uni01B2uni01B3uni01B4uni01B5uni01B6uni01B7uni01B8uni01B9uni01BAuni01BBuni01BCuni01BDuni01BEuni01BFuni01C0uni01C1uni01C2uni01C3uni01C4uni01C5uni01C6uni01C7uni01C8uni01C9uni01CAuni01CBuni01CCuni01CDuni01CEuni01CFuni01D0uni01D1uni01D2uni01D3uni01D4uni01D5uni01D6uni01D7uni01D8uni01D9uni01DAuni01DBuni01DCuni01DDuni01DEuni01DFuni01E0uni01E1uni01E2uni01E3uni01E4uni01E5Gcarongcaronuni01E8uni01E9uni01EAuni01EBuni01ECuni01EDuni01EEuni01EFuni01F0uni01F1uni01F2uni01F3uni01F4uni01F5uni01F6uni01F7uni01F8uni01F9 Aringacute aringacuteAEacuteaeacute Oslashacute oslashacuteuni0200uni0201uni0202uni0203uni0204uni0205uni0206uni0207uni0208uni0209uni020Auni020Buni020Cuni020Duni020Euni020Funi0210uni0211uni0212uni0213uni0214uni0215uni0216uni0217 Scommaaccent scommaaccent Tcommaaccent tcommaaccentuni021Cuni021Duni021Euni021Funi0220uni0221uni0222uni0223uni0224uni0225uni0226uni0227uni0228uni0229uni022Auni022Buni022Cuni022Duni022Euni022Funi0230uni0231uni0232uni0233uni0234uni0235uni0236uni0237uni0238uni0239uni023Auni023Buni023Cuni023Duni023Euni023Funi0240uni0241uni0243uni0250uni0251uni0252uni0253uni0254uni0255uni0256uni0257uni0258uni0259uni025Auni025Buni025Cuni025Duni025Euni025Funi0260uni0261uni0262 gammalatinuni0264uni0265hhookuni0267uni0268uni0269uni026Auni026Buni026Cuni026Duni026Euni026Funi0270uni0271uni0272uni0273uni0274uni0275uni0276uni0277uni0278rturneduni027A rhookturneduni027Cuni027Duni027Euni027F RsmallcapRsmallinverteduni0282uni0283uni0284uni0285uni0286uni0287uni0288uni0289uni028Auni028Buni028Cuni028Duni028Euni028Funi0290uni0291uni0292uni0293uni0294glottalstopreverseduni0296uni0297uni0298uni0299uni029Auni029Buni029Cuni029Duni029Euni029Funi02A0uni02A1uni02A2uni02A3uni02A4uni02A5uni02A6uni02A7uni02A8uni02A9uni02AAuni02ABuni02ACuni02ADuni02AEuni02AF h.superiorhhook.superior j.superior r.superiorrturned.superiorrhookturned.superiorRsmallinverted.superior w.superior y.superioruni02B9uni02BAuni02BB afii57929 afii64937uni02BEuni02BFuni02C0uni02C1uni02C2uni02C3uni02C4uni02C5uni02C8uni02C9uni02CAuni02CBuni02CCuni02CDuni02CEuni02CFuni02D0uni02D1uni02D2uni02D3uni02D4uni02D5uni02D6uni02D7uni02DEuni02DFgammalatin.superior l.superior s.superior x.superiorglottalstopreversed.superioruni02E5uni02E6uni02E7uni02E8uni02E9uni02EAuni02EBuni02ECuni02EDuni02EEuni02EFuni02F0uni02F1uni02F2uni02F3uni02F4uni02F5uni02F6uni02F7uni02F8uni02F9uni02FAuni02FBuni02FCuni02FDuni02FEuni02FF gravecomb acutecombuni0302 tildecombuni0304uni0305uni0306uni0307uni0308 hookabovecombuni030Auni030Buni030Cuni030Duni030Euni030Funi0310uni0311uni0312uni0313uni0314uni0315uni0316uni0317uni0318uni0319uni031Auni031Buni031Cuni031Duni031Euni031Funi0320uni0321uni0322 dotbelowcombuni0324uni0325uni0326uni0327uni0328uni0329uni032Auni032Buni032Cuni032Duni032Euni032Funi0330uni0331uni0332uni0333uni0334uni0335uni0336uni0337uni0338uni0339uni033Auni033Buni033Cuni033Duni033Euni033Funi0340uni0341uni0342uni0343uni0344uni0345uni0346uni0347uni0348uni0349uni034Auni034Buni034Cuni034Duni034Euni0350uni0351uni0352uni0353uni0354uni0355uni0356uni0357uni0358 acute.capcircumflex.cap caron.capuni035Chungarumlaut.capspace_uni030F.capbreveinvertedcmb.cap breve.cyrcap breve.cyr dieresis.caphookabovecomb.capuni0364uni0365uni0366uni0367uni0368uni0369uni036Auni036Buni036Cuni036Duni036Euni036Funi0374uni0375uni037Auni037Buni037Cuni037Duni037Etonos dieresistonos Alphatonos anoteleia EpsilontonosEtatonos Iotatonos Omicrontonos Upsilontonos OmegatonosiotadieresistonosAlphaBetaGammaEpsilonZetaEtaThetaIotaKappaLambdaMuNuXiOmicronPiRhoSigmaTauUpsilonPhiChiPsi IotadieresisUpsilondieresis alphatonos epsilontonosetatonos iotatonosupsilondieresistonosalphabetagammadeltaepsilonzetaetathetaiotakappalambdanuxiomicronrhosigma1sigmatauupsilonphichipsiomega iotadieresisupsilondieresis omicrontonos upsilontonos omegatonosuni03D0theta1Upsilon1uni03D3uni03D4phi1omega1uni03D7uni03D8uni03D9uni03DAuni03DBuni03DCuni03DDuni03DEuni03DFuni03E0uni03E1uni03F0uni03F1uni03F2uni03F3uni03F4uni03F5uni03F6uni03F8uni03F9uni03FBuni03FDuni03FEuni03FFuni0400 afii10023 afii10051 afii10052 afii10053 afii10054 afii10055 afii10056 afii10057 afii10058 afii10059 afii10060 afii10061uni040D afii10062 afii10145 afii10017 afii10018 afii10019 afii10020 afii10021 afii10022 afii10024 afii10025 afii10026 afii10027 afii10028 afii10029 afii10030 afii10031 afii10032 afii10033 afii10034 afii10035 afii10036 afii10037 afii10038 afii10039 afii10040 afii10041 afii10042 afii10043 afii10044 afii10045 afii10046 afii10047 afii10048 afii10049 afii10065 afii10066 afii10067 afii10068 afii10069 afii10070 afii10072 afii10073 afii10074 afii10075 afii10076 afii10077 afii10078 afii10079 afii10080 afii10081 afii10082 afii10083 afii10084 afii10085 afii10086 afii10087 afii10088 afii10089 afii10090 afii10091 afii10092 afii10093 afii10094 afii10095 afii10096 afii10097uni0450 afii10071 afii10099 afii10100 afii10101 afii10102 afii10103 afii10104 afii10105 afii10106 afii10107 afii10108 afii10109uni045D afii10110 afii10193uni0460uni0461 afii10146 afii10194uni0464uni0465uni0466uni0467uni0468uni0469uni046Auni046Buni046Cuni046Duni046Euni046Funi0470uni0471 afii10147 afii10195 afii10148 afii10196uni0476uni0477uni047Cuni047Duni047Euni047Funi0483uni048Cuni048Duni048Euni048F afii10050 afii10098uni0492uni0493uni0494uni0495uni0496uni0497uni0498uni0499uni049Auni049Buni049Cuni049Duni049Euni049Funi04A0uni04A1uni04A2uni04A3uni04A4uni04A5uni04A6uni04A7uni04A8uni04A9uni04AAuni04ABuni04ACuni04ADuni04AEuni04AFuni04B0uni04B1uni04B2uni04B3uni04B4uni04B5uni04B6uni04B7uni04B8uni04B9uni04BAuni04BBuni04BCuni04BDuni04BEuni04BFuni04C0uni04C1uni04C2uni04C3uni04C4uni04C7uni04C8uni04C9uni04CAuni04CBuni04CCuni04D0uni04D1uni04D2uni04D3uni04D4uni04D5uni04D6uni04D7uni04D8 afii10846uni04DAuni04DBuni04DCuni04DDuni04DEuni04DFuni04E0uni04E1uni04E2uni04E3uni04E4uni04E5uni04E6uni04E7uni04E8uni04E9uni04EAuni04EBuni04ECuni04EDuni04EEuni04EFuni04F0uni04F1uni04F2uni04F3uni04F4uni04F5uni04F6uni04F7uni04F8uni04F9 afii57799 afii57801 afii57800 afii57802 afii57793 afii57794 afii57795 afii57798 afii57797 afii57806uni05BA afii57796 afii57807 afii57839 afii57645 afii57841 afii57842 afii57804 afii57803 afii57658uni05C6 afii57664 afii57665 afii57666 afii57667 afii57668 afii57669 afii57670 afii57671 afii57672 afii57673 afii57674 afii57675 afii57676 afii57677 afii57678 afii57679 afii57680 afii57681 afii57682 afii57683 afii57684 afii57685 afii57686 afii57687 afii57688 afii57689 afii57690 afii57716 afii57717 afii57718uni05F3uni05F4uni1E00uni1E01uni1E02uni1E03uni1E04uni1E05uni1E06uni1E07uni1E08uni1E09uni1E0Auni1E0Buni1E0Cuni1E0Duni1E0Euni1E0Funi1E10uni1E11uni1E12uni1E13uni1E14uni1E15uni1E16uni1E17uni1E18uni1E19uni1E1Auni1E1Buni1E1Cuni1E1Duni1E1Euni1E1Funi1E20uni1E21uni1E22uni1E23uni1E24uni1E25uni1E26uni1E27uni1E28uni1E29uni1E2Auni1E2Buni1E2Cuni1E2Duni1E2Euni1E2Funi1E30uni1E31uni1E32uni1E33uni1E34uni1E35uni1E36uni1E37uni1E38uni1E39uni1E3Auni1E3Buni1E3Cuni1E3Duni1E3Euni1E3Funi1E40uni1E41uni1E42uni1E43uni1E44uni1E45uni1E46uni1E47uni1E48uni1E49uni1E4Auni1E4Buni1E4Cuni1E4Duni1E4Euni1E4Funi1E50uni1E51uni1E52uni1E53uni1E54uni1E55uni1E56uni1E57uni1E58uni1E59uni1E5Auni1E5Buni1E5Cuni1E5Duni1E5Euni1E5Funi1E60uni1E61uni1E62uni1E63uni1E64uni1E65uni1E66uni1E67uni1E68uni1E69uni1E6Auni1E6Buni1E6Cuni1E6Duni1E6Euni1E6Funi1E70uni1E71uni1E72uni1E73uni1E74uni1E75uni1E76uni1E77uni1E78uni1E79uni1E7Auni1E7Buni1E7Cuni1E7Duni1E7Euni1E7FWgravewgraveWacutewacute Wdieresis wdieresisuni1E86uni1E87uni1E88uni1E89uni1E8Auni1E8Buni1E8Cuni1E8Duni1E8Euni1E8Funi1E90uni1E91uni1E92uni1E93uni1E94uni1E95uni1E96uni1E97uni1E98uni1E99uni1E9Auni1E9Buni1E9Cuni1E9D Germandblsuni1E9Funi1EA0uni1EA1uni1EA2uni1EA3uni1EA4uni1EA5uni1EA6uni1EA7uni1EA8uni1EA9uni1EAAuni1EABuni1EACuni1EADuni1EAEuni1EAFuni1EB0uni1EB1uni1EB2uni1EB3uni1EB4uni1EB5uni1EB6uni1EB7uni1EB8uni1EB9uni1EBAuni1EBBuni1EBCuni1EBDuni1EBEuni1EBFuni1EC0uni1EC1uni1EC2uni1EC3uni1EC4uni1EC5uni1EC6uni1EC7uni1EC8uni1EC9uni1ECAuni1ECBuni1ECCuni1ECDuni1ECEuni1ECFuni1ED0uni1ED1uni1ED2uni1ED3uni1ED4uni1ED5uni1ED6uni1ED7uni1ED8uni1ED9uni1EDAuni1EDBuni1EDCuni1EDDuni1EDEuni1EDFuni1EE0uni1EE1uni1EE2uni1EE3uni1EE4uni1EE5uni1EE6uni1EE7uni1EE8uni1EE9uni1EEAuni1EEBuni1EECuni1EEDuni1EEEuni1EEFuni1EF0uni1EF1Ygraveygraveuni1EF4uni1EF5uni1EF6uni1EF7uni1EF8uni1EF9uni1F00uni1F01uni1F02uni1F03uni1F04uni1F05uni1F06uni1F07uni1F08uni1F09uni1F0Auni1F0Buni1F0Cuni1F0Duni1F0Euni1F0Funi1F10uni1F11uni1F12uni1F13uni1F14uni1F15uni1F18uni1F19uni1F1Auni1F1Buni1F1Cuni1F1Duni1F20uni1F21uni1F22uni1F23uni1F24uni1F25uni1F26uni1F27uni1F28uni1F29uni1F2Auni1F2Buni1F2Cuni1F2Duni1F2Euni1F2Funi1F30uni1F31uni1F32uni1F33uni1F34uni1F35uni1F36uni1F37uni1F38uni1F39uni1F3Auni1F3Buni1F3Cuni1F3Duni1F3Euni1F3Funi1F40uni1F41uni1F42uni1F43uni1F44uni1F45uni1F48uni1F49uni1F4Auni1F4Buni1F4Cuni1F4Duni1F50uni1F51uni1F52uni1F53uni1F54uni1F55uni1F56uni1F57uni1F59uni1F5Buni1F5Duni1F5Funi1F60uni1F61uni1F62uni1F63uni1F64uni1F65uni1F66uni1F67uni1F68uni1F69uni1F6Auni1F6Buni1F6Cuni1F6Duni1F6Euni1F6Funi1F70uni1F71uni1F72uni1F73uni1F74uni1F75uni1F76uni1F77uni1F78uni1F79uni1F7Auni1F7Buni1F7Cuni1F7Duni1F80uni1F81uni1F82uni1F83uni1F84uni1F85uni1F86uni1F87uni1F88uni1F89uni1F8Auni1F8Buni1F8Cuni1F8Duni1F8Euni1F8Funi1F90uni1F91uni1F92uni1F93uni1F94uni1F95uni1F96uni1F97uni1F98uni1F99uni1F9Auni1F9Buni1F9Cuni1F9Duni1F9Euni1F9Funi1FA0uni1FA1uni1FA2uni1FA3uni1FA4uni1FA5uni1FA6uni1FA7uni1FA8uni1FA9uni1FAAuni1FABuni1FACuni1FADuni1FAEuni1FAFuni1FB0uni1FB1uni1FB2uni1FB3uni1FB4uni1FB6uni1FB7uni1FB8uni1FB9uni1FBAuni1FBBuni1FBCuni1FBDuni1FBEuni1FBFuni1FC0uni1FC1uni1FC2uni1FC3uni1FC4uni1FC6uni1FC7uni1FC8uni1FC9uni1FCAuni1FCBuni1FCCuni1FCDuni1FCEuni1FCFuni1FD0uni1FD1uni1FD2uni1FD3uni1FD6uni1FD7uni1FD8uni1FD9uni1FDAuni1FDBuni1FDDuni1FDEuni1FDFuni1FE0uni1FE1uni1FE2uni1FE3uni1FE4uni1FE5uni1FE6uni1FE7uni1FE8uni1FE9uni1FEAuni1FEBuni1FECuni1FEDuni1FEEuni1FEFuni1FF2uni1FF3uni1FF4uni1FF6uni1FF7uni1FF8uni1FF9uni1FFAuni1FFBuni1FFCuni1FFDuni1FFEenquademquadenspaceemspacethreeperemspacefourperemspace sixperemspace figurespacepunctuationspace thinspace hairspacezerowidthspace hyphentwo hyphennobreak figuredash horizontalbaruni2016 underscoredbl quotereversed quotedblrevtrianglebulletonedotenleadertwodotenleader hyphendotuni202Funi2031minutesecond primetriple primereverseduni2036uni2037uni203B exclamdbl interrobanguni203Euni2042question_questionquestion_exclamexclam_questionuni204Auni204Buni204F zero.superior i.superior four.superior five.superior six.superiorseven.superioreight.superior nine.superior plus.superiorminus.superiorequal.superiorparenleft.superiorparenright.superior n.superior zero.inferior one.inferior two.inferiorthree.inferior four.inferior five.inferior six.inferiorseven.inferioreight.inferior nine.inferior plus.inferiorminus.inferiorequal.inferiorparenleft.inferiorparenright.inferior a.inferior e.inferior o.inferior x.inferioruni2094uni2095uni2096uni2097uni2098uni2099uni209Auni209Buni209Cuni20A2lirapesetauni20A8dongEurouni20AFuni20B1uni2100uni2101uni2102 centigrade afii61248uni2106 fahrenheituni210Cuni210Duni210Euni210FIfraktur afii61289uni2115 afii61352uni2119uni211ARfrakturuni211Duni2120uni2124uni2126uni2127 estimatedalephuni2136uni2137uni2138uni2139onethird twothirdsonefifth twofifths threefifths fourfifthsonesixth fivesixths oneeighth threeeighths fiveeighths seveneighths onenumeratorOneromanTworoman Threeroman Fourroman FiveromanSixroman Sevenroman Eightroman NineromanTenroman Elevenroman Twelveromanuni216Cuni216Duni216Euni216Foneromantworoman threeroman fourroman fiveromansixroman sevenroman eightroman nineromantenroman elevenroman twelveromanuni217Cuni217Duni217Euni217Funi2180uni2181uni2182uni2183uni2184 arrowleftarrowup arrowright arrowdown arrowboth arrowupdnuni2196uni2197uni2198uni2199uni219Auni219B arrowupdnbseuni21AEuni21BCuni21BDuni21C0uni21C1uni21CBuni21CCuni21CDuni21CEuni21CF arrowdblleft arrowdblup arrowdblright arrowdbldown arrowdblbothuni21D5NwarrowNearrowSearrowSwarrow universaluni2201 existentialuni2204emptysetuni2206gradientelement notelementuni220Asuchthatuni220Cuni220Duni2210uni2213uni2214uni2215uni2216 asteriskmathuni2218uni2219uni221Buni221C orthogonaluni2223uni2224uni2225uni2226 logicaland logicalor intersectionunionuni2236similaruni2241 congruentuni2249uni2259 equivalenceuni2262uni226Auni226Buni226Euni226Funi2270uni2271 propersubsetpropersuperset notsubsetuni2285 circleplusuni2296circlemultiplyuni2298dotmathuni22EFuni2302uni2303uni2310uni2320uni2321uni2329uni232Auni23D3 filledboxH22073triagupuni25B3uni25B6uni25B7triagdnuni25BDuni25C0uni25C1uni25C6uni25C7uni25C9circleH18533uni25D0uni25D1uni25D2uni25D3uni25D4uni25D5uni25D6uni25D7uni2605uni2619uni261Buni261Euni2627uni262Funi2639uni263Auni263Bsununi263Duni263Euni263Ffemaleuni2641maleuni2643uni2644uni2645uni2646uni2647uni2648uni2649uni264Auni264Buni264Cuni264Duni264Euni264Funi2650uni2651uni2652uni2653uni2660uni2663uni2665uni2666uni2669 musicalnotemusicalnotedbluni266Cuni2695uni2698uni26A2uni26A3uni26A4uni2767uni27E6uni27E7uni27E8uni27E9uni2C60uni2C61uni2C62uni2C63uni2C64uni2C65uni2C66uni2C67uni2C68uni2C69uni2C6Auni2C6Buni2C6Cuni2C74uni2C75uni2C76uni2C77uniA720uniA721TuxuniE001uniE002uniE003uniE004uniE005uniE006uniE007uniE008uniE009uniE00AuniE00BuniE00CuniE00DuniE00Ezero.slashfitted zero.fitted one.fitted two.fitted three.fitted four.fitted five.fitted six.fitted seven.fitted eight.fitted nine.fitted Euro.fitted Yen.fitteduniE01Cperthousandzero zero.oldstyle one.oldstyle two.oldstylethree.oldstyle four.oldstyle five.oldstyle six.oldstyleseven.oldstyleeight.oldstyle nine.oldstyle Adieresis.alt Odieresis.alt Udieresis.altf_f_jf_jf_kf_tc_kc_ht_tc_tae.altQ_uT_ht_zh.altgermandbls.altgermandbls.ss03 uni1E9C.alt ampersand.alta.scb.scc.scd.sce.scf.scg.sch.sci.scj.sck.scl.scm.scn.sco.scp.scq.scr.scs.sct.scu.scv.scw.scx.scy.scz.sc hyphen.sc agrave.sc aacute.scacircumflex.sc atilde.sc adieresis.scaring.scae.sc ccedilla.sc egrave.sc eacute.scecircumflex.sc edieresis.sc igrave.sc iacute.scicircumflex.sc idieresis.sceth.sc ntilde.sc ograve.sc oacute.scocircumflex.sc otilde.sc odieresis.scoe.sc oslash.sc ugrave.sc uacute.scucircumflex.sc udieresis.sc yacute.scthorn.sc ydieresis.scij.scgermandbls.scalt germandbls.sc dcroat.sc abreve.sc aogonek.sc cacute.sc ccaron.sc dcaron.sc eogonek.sc ecaron.sc gbreve.sc lacute.sc lslash.sc nacute.sc ncaron.sceng.scohungarumlaut.sc racute.sc rcaron.sc sacute.sc scedilla.sc scaron.sc tcedilla.sctbar.scuring.scuhungarumlaut.sc zacute.sc zdotaccent.sc zcaron.sc lcaron.sc tcaron.sctcommaaccent.scscommaaccent.sc idotaccent.sca.scalt a.superior b.superior c.superior d.superior e.superior f.superior g.superioruniE0C7uniE0C8uniE0C9 k.superior m.superioruniE0CD o.superior p.superior q.superioruniE0D1 t.superior u.superior v.superioruniE0D6uniE0D7uniE0D8 z.superiorf.short f_f.shortuniE0E8W.altV.altK.altR.altJ.altz.altuniE0F4y.altuniE0F9uniE0FBkreiszero.taboldstyleone.taboldstyletwo.taboldstylethree.taboldstylefour.taboldstylefive.taboldstylesix.taboldstyleseven.taboldstyleeight.taboldstylenine.taboldstyleuniE130uniE138uniE148 b.inferior c.inferior d.inferior f.inferior g.inferior h.inferior i.inferior j.inferior k.inferior l.inferior m.inferior n.inferior p.inferior q.inferior r.inferior s.inferior t.inferior u.inferior v.inferior w.inferior y.inferior z.inferioruniE188uniE189v.alt grave.cap acute.capcircumflex.cap caron.cap breve.caphungarumlaut.capspace_uni030F.capbreveinvertedcmb.cap breve.cyrcap breve.cyr dieresis.caphookabovecomb.capuniE420 zero.slash parenleft.sc parenright.scbracketleft.scbracketright.sc braceleft.sc braceright.sc exclamdown.scquestiondown.scguillemotleft.scguillemotright.scguilsinglleft.scguilsinglright.sc hyphen.capdotlessjafii10068.italafii10066.italafii10069.italafii10081.italuniF6C8f_ff_if_lf_f_if_f_llongs_ts_tuniFFFDuni0350DDLDD, d `f#PXeY-, d P&ZE[X!#!X PPX!@Y 8PX!8YY Ead(PX! E 0PX!0Y PX f a PX` PX! ` 6PX!6``YYY+YY#PXeYY-,#B#B#BCCQXC+C`BeY-,C E EcEb`D-,C E +#%` E#a d PX!0PX @YY#PXeY%#aDD-,EaD-,` CJPX #BY CJRX #BY-, b c#a C` ` #B#-, CUX CaB+YC%BC`B %B %B# %PXC%B #a*!#a #a*!C%B%a*!Y CG CG`b EcEb`#DC>C`B- ,ETX #B `a  BB`+g+"Y- , +- , +- , +- , +-, +-, +-, +-, +-, +-, +-,+ETX #B `a  BB`+g+"Y-,+-,+-,+-,+-,+-,+-,+-,+-,+-, +-, ` ` C#`C%%QX# <`#e!!Y- ,+*-!, G EcEb`#a8# UX G EcEb`#a8!Y-",ETX!*0"Y-#,+ETX!*0"Y-$, 5`-%,EcEb+EcEb+D>#8$*-&, < G EcEb`Ca8-',.<-(, < G EcEb`CaCc8-),% . G#B%IG#G#ab#B(*-*,%%G#G#a+e.# <8-+,%% .G#G#a #B+ `PX @QX  &YBB# C #G#G#a#F`Cb` + a C`d#CadPXCaC`Y%ba# &#Fa8#CF%CG#G#a` Cb`# +#C`+%a%b&a %`d#%`dPX!#!Y# &#Fa8Y-,, & .G#G#a#<8--, #B F#G+#a8-.,%%G#G#aTX. <#!%%G#G#a %%G#G#a%%I%aEc#bcEb`#.# <8#!Y-/, C .G#G#a ` `fb# <8-0,# .F%FRX ,#B=+-7,*+. +-C,7+-D,7+-E,7+-F,7+-8,++!# <#B#8 +C. +-O,8+-P,8+-Q,8+-R,8+-=,E# . F#a8 +-W,,+. +-X,,+0+-Y,,+1+-Z,,+2+-[,-+. +-\,-+0+-],-+1+-^,-+2+-_,.+. +-`,.+0+-a,.+1+-b,.+2+-c,/+. +-d,/+0+-e,/+1+-f,/+2+-g,+e$Px0-KRXYc #D #pE KQKSZX4(Y`f UX%aEc#b#D + ++Y(ERD +DLinLibertine_Rah.ttf000066400000000000000000030477101300200146000356420ustar00rootroot00000000000000connectome-workbench-1.2.3+git41-gc4c6c90/src/Resources/GeneralResources/Fonts/LinuxLibertine0FFTMYfN<GDEFS`YX(GPOSIGSUBWyOlMATHxh`OS/2\i`cmapn1jPcvt F F(0fpgm蕏 FXgasp F glyfK{ head"y x6hhea/3 $hmtx͡ )loca9+ )maxp [ x name|׏ ՘ypostX7 h preptH OXoB6\7yzz{}~"#01:;;<=>>?VWcd H I W X Z [ ] ^ ^ _ b c       ' ( 0 1 1 2 2 3 3 4 4 5 W X ^ _ s^2hv  (0>FT\jx "*2@NV d P  ;m   oP R  yh m !b m bL^;b?BsB;% J!!!7N !f %P~>>eg ilnouv I W [ ]% ` a( * X ^+LM &DFLT&cyrlrtbdD*2@HB:*Bz%2b3?BPEhHPH`  y$%&'()*+,-./0123456789:;<=   "$&(*,.02468:;? E F G~ .^djpv| $*06<BHNTZ`fl7 1'o\HoIo;1''}X'.%&'()*+./345679:;=EGIJKNOSTUVWYZ[]f ` % .4:@FLRX^djpv|\+\+\+\+\+\+\+&*24GHR +y & c "(.4:@FLRX^djpv| $*06<BHNTZ`flrx~ &,28>DJPV\bhntz "(.4:@FLRX^djpv| $*06<BHNTZ`flrx~ &,28>DJPV\bhntz     " ( . 4 : @ F L R X ^ d j p v |     $ * 0 6 < B H N T Z ` f l r x ~    #s?F3u5s7'u\)))))d)\rE#)+)++)+) ) ++ +)+''''mq^'''/''''''''''''?     '      ) \          9+ )\\\\/\/)\+)'o+^\+q#qsq3qd)sh:+o+3)')/)+)+o)))) )))))))))))Z))))))%)dd=\))))))B)))+)))))))))))))))))))))))))))))+)))))))))))+))))))))))))))9J9JqqXqqDq)fj`{`{)%+)))d9/q///q//qq/q//q/qDq/q1q))+ ))+3)5o)'mqq)d)b3+m+s+)`+)+D{{'{P{-{{F{`{B{Z{;{{{P{'{{P{{{'{\{+{{-{{/q'''''++qq +)f$=DDFHJLNV!X]*067@GW]deghijkpqst  uw##x,-y44{CC|FF}LL~NNVW^^``dfvv  <>npquuyzvv``ddtt !"#$%&'  (ST)+,./012 3  9 7 7: P P; _ _< b b= i > X Y Z ] ^ a V Vb== !$%$0&345 7 ( 0< 2 2E 4 4FG$*06<BHNTZ`flrx~ &,28>DJPV\bhntz5wyuT)\)sVR)?\h f\);dwyu+;H)\))d)))))) )3))))y);)s)d)m))-'\PP499RyD1=Dyqu+}0@ Q "(.4:@FLRX^djpv| $*06<BHNTZ`flrx~sfw/}NhJ9uHqH#;9{##F;-%J}/jb1FN9b1%5L%;9qh;h9%'*-/9;;==EMOUWY$[[']]()  *,,+44,ff-  .  /0''2345J  O P &,28ff`R } &,28>DJPV\bhntz "(.4:@FLRX^djpv| $*06<BHNTZ`flrx~H!V)TbZRX=H!VVVVVVT!!!15H!)RT!q!XH!H!!H!!HHH!H!H!H!H!H!H!H!H!H!H!H!H!H!H!H!H!H!H!H!H!H!H!H!H!H!H!H!H!H!H!H!H!H!H!H!H!H!H!H!H!H!H!H!H!H!H!H!H!H!H!H!H!H!H!H!H!H!H!H!H!H!H!H!H!H!H!H!'T!wb!!!!!!=!!!!L 'DDFHKLRRVX oo   $$#&&$(n%JJlUUm 7 7n N No Q Qp _ _q i ir m ms o ot q qu s uv w wy } }z { |3  $*06<BHNTZ`flrx~ &,28>DJPV\bhntz "(.4:@FLRX^djpv| $*06<BHNTZ`flrx~ &,28>DJPV\bhntz yL93-/3/w\XLVRd;;;;VV;;#{/!/RRdVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVV//6=./LV-77B5==-579-X-/H>$,.3 5=DDFFHHLLNRUX![[%]]&'(,-./01267  8##:,-;44=CC>FF?LL@[[A^^B``CdfDooGqqHI  KLM(P*n^EEKKUU   7 7 P Q i q s x z z s 4 ,28>DJPV\bhntz "(.4:@FLRX^djpv| $*06<BHNTZ`flrx~ &,28>DJPV\bhntz "(.4:@FLRX^djpv| $*06<BHNTZ`flrx~ &,28>DJPV\bhntzw%yL-%fu\LssRIDossNsd%;%;%;%;%!#+#;;###!w#!%##%%%#=N%N!!jy q9+ HZ)%%%%VV3}oo%%b +NV%!%}!%L%%#?J%-) 5o3{!s5H5=!V-?%?$,.3 5=D]23789:;<=>CDE  F##H,-I44KCCLEGMLLPNNQWWR[[S^^T``UddVffWooXY[  \n^pqccttUU    7 7 N N P Q _ _ b b i       #%)##+,567 &,28>DJPV\bhntz ".?R5F'FBBX#^qLq   F# mw9/oy%1dVsf $*06<BHNToPPRR::  * "(.wZ9 1Rq11L111o1111L\1111Lj{jjjjL111L11111d     ""##$$ ''))+,..// 1122334477889:;;<<>>BB EE HH IIJJNNRS UUYZ[[\\^^aamm}}         $$&&**,,..00224466778899::AAHHKLQQff   aabbccddeeff    "& 1 1 E E F F P P R R   U U X X    !""$$&&**--224477889:<<@@ DD EEFH JJ KKMMNOPQ RR SS TT UU VV XXYZ[[\\]] `` aammoorrtuyy{{}}       $$&&**,,..0022446677::BBIIJJ OO UUabcc   aabbccddeeff  *+ ) * 0 0 E E R S   "#$')+,./1234789:;<>BEHIJNRSUYZ[\^am}  $&*,.0246789:AHKLQfabcdef"#$%& 1 E F P R U Xj =3q.  ""&&**,,..//0011<<BBCCQQgghhrrttyy{{}} [[CCFF``ddhhtt. "&*,./01<BCQghrty{} ==)))))+::??@@CCEEFFJJLLMMQQSSUUVVWWXX\\ddggkkpprrww}}]]--//113314"CCFFMMUUVV\\]]bbddhh jk mmnnpprrstvv wwxxzz}}]]FFHHNN V V =:?@CEFJLMQSUVWX\dgkprw}]-/131234xq) H))\3%$$.. 33 77 9:;; <<$$ && :: i i l l n n s s t t w w x x y y | | } } ~             Q T  i i j j k k l l n n o o p q s t v v w w x x y y z z | | } } ~            Q T=$.379:;<$&: i l n s t w x y | } ~  Q R S T9Y9: VDFLT&cyrlbgrekhebrlatnmath  SRB B    (AZE `CRT `DEU (MOL ROM TRK `   aaltc2sccaseccmpdligfinafracfrachligligaligalnumloclloclnaltonumpnumsalt sinf&smcp,smcp4ss01LZdhl(: "&*.48>BFJNRVZ^bfjnrvz~  $(,06:>BFJNRVZ^bfjnrvz~ $(,048<@DHLPTX\`hpv| h I J *  : * H ) 9 ;{ + <t , =u - > . ? / @ 0 A 1 B 2 C 3 s z   K L i j k l m n o p c q r  s t u v w x y z { | } ~     M N O Q R P                               d e       ^                                  q              !              s f S T 4+ * : ) ; < = > ? @ B * + , - . / 1 3   K  $D\  .59:>@DEFGHIJKLMNOPQRSTUVWXYZ[\]^`cm}   !#$%')13<>@cikrstw5 ) + , - . / 0 2 : ; < = > ? A C E F G ^ c h i & '  !$%  %La h i j k l m n o p r s t u v w x y z { | } ~  q s { na DEFGHIJKMNOPQRSTUVWXYZ[\]  !#%')13<>@A `  }Lt ~ Wc tu {{ ~ WcF(> "*2:BJRZbjp]]__YYWW~cc "ZZXX $,``[[\\ "^^aabb~< {tu    rt uyzsvwx  <  DEFGHIJKLMNOPQRSTUVWXYZ[\]%(1;=CW B H\+ !"   #         $       !% " #  D]) H H*l h I J i j k l m n o p q r s t u v w x y z { | } ~  K L M N O Q R P S T l $%&'()*+,-./0123456789:;<=>@^`cm}   "$&(02:;=? E F G2  [IL YL \AL VLIAV .:L^h", " "  } `X aK $,4<DJPV\bhn MIW LIN KIM JIK \IO QW PN OM IE NK  ZO XI TW >);& $ [O WA _K ]V ]W  } $ MW LN KM JK \O "47IWrA y X  UW ^WFV&  SK RN b]FW2      : : ; < = > ? @ A B C5 ) 4 . * + , - . / 0 1 2 3 : C  4 * + , - . / 0 1 2 3 5 4 2 : ; < = > ? @ A C )gg 55 B B    H H4 H5g      ) 5 : C   H ) * h cK \ Kcikw c i RRR IILM** ( 4 X X+< ""$677889:;;<<==@@IILM_`hhlltuxx{{--//**CZ ( 4 X X !  E F G  -.5  e f    f   : _ X) I J U K L M N O 2 Q R P * + , 4 -# 3 ! S T) >@C^`cjmv}"4G LM* ( ) * ,  I X P< 3R-H{3 X fff  3RRRR\Dd ,` ' %%' 'ussuww'ssuu%%%%% ^33f R PfEd@ ''`oD , D, D~Nou~w_gEMWY[]} ' 7 > B D K O q !!! !!!!!!! !"!$!'!.!9!O!!!!!!!!" "."6"<"A"E"I"Y"b"e"k"q""""""####!#'#+#}###$#$$%%%%%%%%%&& &&&&'&/&S&`&c&f&o&&&&''''g''',l,o,w.. .W!e*0km!*08=KU^ad 8 Ptz|Cb HPY[]_  / 9 B D G O p t !!! ! !!!!!! !"!$!&!.!5!O!S!!!!!!!"""6"<"A"E"H"Y"`"d"j"n""""""#### #&#)#}###$#$`$%%%%%%%%%&& &&&&'&/&9&`&c&e&i&&&&''''g'v'',`,n,t...V e,2mp(08;IMW`X 8xvB?%8753210.-+*)("{yiZQB5 oKznlkiDB95s1ܘܗܓ baaa))))))) (((((((((((((((((&&U&S&'X b L ~NbPotu1z~38?@Tw|U[C_bg EHM#PW)YY1[[2]]3_}4S   ' / 7 9 > B B D D G K O O p q t  " / 2 4 6 7!!8!!<! ! >! !?!!C!!D!!E!!G!!I! ! K!"!"L!$!$M!&!'N!.!.P!5!9Q!O!OV!S!W!!!!!!!!!!!!!!"" ""."6"6"<"<"A"A"E"E"H"I"Y"Y"`"b"d"e"j"k"n"q""""""""""""####### #!#&#'#)#+#}#}## ## ##$#$#$`$ $$H%%%%%%%%%%%%%%%%%%&&& & &&&&&&&'&'&/&/&9&S&`&`&c&c&e&f&i&o&&&&&&&&'''''''g'g'v''''',`,l,n,o ,t,w .. .. .. VW   ! ee * ,0 E2k Jmm p   ! (* 00 88 ;= IK MU W^ `a " $ &Xd ( 5 C G88 H I V W X _89 `;> b@D fFG kJP m   !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`ardeixpkLvjsgwql|ZcnTm}b:5 Y Zyqz @ "+ !7 !!f]=]f\H \TjD@M =M>+2"'.'.54462"VA 2 )@Z??ZDJL;qA`^) F?RL1Z@@Z?h3)!9K&PX >[Y'$ +54632546325>+HC5=+HCjF%AD F%AD5KPX@'  d    TS >@1  de    TGK?Y@)  +7#%3#####737#733333;%'\'>M79N<mllmmnnm;7V :@5-,( @/bbe U=N>Y@ 98 +>54&#.'#7.#&'6&'$'46?63S{pyn2[WB `R\ 9 <75ʑ 1 G{P yrmsq ay6qZ39 &}  w׏ )u 2=X@U #<:  U UUUM><:75/-)'" 22$$%! +%32654.#"4632#"&267'#"'#"&5463232654#"wZNf!"HpqqJftXgR+AFbyZJuXdn)Png8"NiXHqb1;ATfy5:!n'+wV3H;d}XX- GR@H8" @ K-PX@2bU M =M=M>@0b  UUM=M>YY@ OM#'C)&% +3267&#"&5467&'463267>.'&473273272#".'>54&#"jkfTTLײhm?VH+/RqZR ^FFdRJ-Vb:!v<)9\-`oF=1iRw`so 0գ{qmd}`RNm6=!  11 8}vg=Dodd A>?9FSf\Nmh'9K$PX >$ +546325=+HDhF%ADZ^7 @ > +7&.Z 3 !yT1#hd+` [ +&'67 3 !yT1#Z%!bhe\s3/@, )<eU>&'#'#!+&#"54632.54632>2#"#".'#"&54>X#3!#'x%+#D%'wJ!?1]4%#//2-# 5^`L%Z)y!/H[)L?3% Ay;#'5;{TGKPX@eU>@deIMAY#"##""+4632!2#!#"5!"5463!3#i%/!!R{%#3!\!.X\@9[ +72&5>54'&'46=JcJjIZ>o\fm%7L+:RbD @IMA   +!"5463!2+F!%'7)>uN@M> +6462"u?Z@@Z+Z@@Z?X+-K(PX@ e >@ d[Y@  + #XT+\P`"*@'UM>"" +"2>76'&"'&532'ED))83#w'=zVT_렖dN|2v #P<&e#RVS #dɪC *@'<ddL >$C+%&#"&47>54"&'$72N?s -TwP)7{s {; 11 @ww3'H /jL//@,(<bUK >)(F)"+4632!2676&#!46?6'4&#"#"&}Ѡ>GHϘm7>'-^B]lFfdC!1fJTEǘ`VobsN/ %3.ZN<K@H76<b``UM>53.,#! << +""&54>32#"&5463232>5!"'>54&%MPP!%'RhO@`xsf71' !!KX:?# bL;j.P\=PAaV9O:.5 ?`BW]\9-;@8*<dbU >)'#! -- +)2+3&#"&47676=!"'63+F' +- \on^?=B!.!' 11 0V#oD+B@?< :bUUM>($$#$#"+4&#"327#"'632#"&54632326jEwf{!}ZF)Tu^/72 ^1N  !ݜ_;-B  Z^$'@$<" :UM>$"&$+32654.#"'672#".576%qPj1gHa5a`òķDzLuRN򲨠-\b@,)eѓQ'Ghd4@1<dU>  +"'7433!27'\'7@#' 5)RL 5HRM G>R^Z $03@0+<UM>&%%0&0#!   +"654&#"&54767'&'46322654/Xd7X@Y^պwG^4^`sXcI9e9'yzNiX/X`ݥi>2!qyil:LD}{fj$,@)<" 9UIMA$"&$+654&#"32"&5432&56pPk1fHb5aaòŶD{KVRN򲨠-\c?,)dѓR&G'hD>KPX@UM >@UIMAY+462"462"@Z??Z@@Z??ZZ@@Z@[??[?D(@%9eIMA   +462"2&5>54'&546@Z??Z-=JcJjJZ>Z@@Z@Yo\fm%7L+:; "+&7E "-6 E{% /@,UIMA     +!"5463!2!"5463!2!#&!#&y!.#3!.#37 "+%.7.7LwE -5 F^F)1/@,b`M =M>-%&+#"&547632"7547>54&"462" 1!3i\%R54V#!=4mq>?[??[%#!')/dUP9bT-%?(7^Z^ǃ9gGf@iZ@@Z?yPP@MNM K<'&< U  UIUIMAIG&&$$)&%%" +4&#"267632654.#"327# ! #"747'#".5432?K:#l)5/=vdyt'dmР?[H=J?;Hj113] mz9H'+߼qkPTb˚b%RTNJ^aT!`ji8TM%<1'1 yD=^KPX@UM =K >@SUK >Y@;9/.*& +!#&#"&47>727&#"&47>&'!"j 2nLk NZ%-D?63 [u!wP%D- ^  11 4XJ)l/1  11   [`- :m@ UUM >Y@ 650/,&:9  &#! +3 54&#"32654&#263 #"&#"&47>54&'&47#iZGDfך%#pR1nӒbj s??s f덁,JbQP))HsC1 <{5{;2 LD;@8 <bM =M>  + 47632'!"32672rg'hz Ǐlt_[ XiwD-.LK(PX@M =M >@UM >Y@*)$# .-(" +%32>54.#"'263 #"&#"&47>54&'&47TuT=힬X!R'}w{j s??s 73/nі}vdi;1 <{5{;2 91B@8  <6:K(PX@7 ZZ U N  =M=L >@5 ZZ   U UM=L >Y@?<543/,+&%" BB +26762"'.+;26&#!"&47>54&'&473!272'.+"?}:1  1RT3@J%"2cHss??sd 50!#'VjP71[ bOJj 70%;H9 1 <{5{;2 F{d;H1>@"41 8 @7Z  bU  U M =K >Y@:932/-*'$# >> +#&#"&47>54&'&473!272#.+"326762"'.D@r  y{s??sd 6*%9h\F-}91  2Qd{; 11 <{5{;2 v PV91R1[ jGNf 70%LTD2E@B*<b`M =N>-+! 22 +2'.#"3274&'.7327! 4$d+'# 5`kmQG` ITc 1- ;D%1Njk7N99" 5  5 +4pP-GkK(PX@%  S K  =K >@#   U  SK >Y@EDA=:96521CCC+%&#"&47>5!&#"&47>54&'&47327!4&'&47327?s TRs@C@r TV s??s lo s?@sUb s?{; 11 <{i{; 11 <{5{;2  1 ;{H{;2  1 ;{B-!AK(PX@K =K >@UK >YCC+%&#"&47>54&'&47327@r PR s??s GV s?{; 11 <{5{;2  1 ;{uw-&tKPX@ZRK >K(PX@bRK >@!bUINBYYC"$'+#"&5463232>54&'&47327%J5-f-lA99)E@FPZ b</hs9'X=19465˪{;2  1 =-Pb"@bUM >Y@ POJENCC +%&#"&47>54&'&4732767>.'.7327&#""'&'&'@r PR s??s PN s?_CR!#$  OD 5@`.HC-' HQA;o{; 11 <{5{;2  1 ;{Hh!+2  1  3/J#' 1 9UJ-'a#@ZUK >Y@ '% +!"&47>54&'&47327;2676&#1j s??s LR s?7>## !71 <{5{;2  1 ;{NA!-F.@2b`  `U  K  =>Y@A@=965G +%#'"'#&#"&47>76&'&4727%&#"&47>&M44  XN1_{F5{T< R (m )   e.=')' XD ("( X'`T 11 PbLq.2  1 6W101 11 -0-<0K(PX@%b`K = =>@&b`I =M>YY@ "J#(H+4.'.7327#"'&"&#".7>5.'&47327s 15?  /=s?/-%53 15? 75  s? Z> X HK+ 2  1 A?1DdHK+  11 BV-R2 q>LTD '@$M =M>   +"32# ! ǴVT!v: +T{h'37 3{K-PX@ "<@ "@%ZUM =K >Y@ C(c$"+32654&#"4&'&4732632#"'&#"&47>5%w?s d)'^'1hDžf@N ZR s?o" {;2 Eqh6HOy; 11 <{LTsD 4}@,('@#bUQM >Y@31$"   +"32632727#".'&#"'6767&! Ǵ]u=#uDS<]!'*#PT!v: +T,&a[NK$'#U-2)``37;Ƶ- K-PX@0I  U M =K = M  >@1U  U M =K = M  >YY@;:6410&$#   +"3265&#"&47>54&'&47326323#"'.#-bF@rTR s??s G#3yAm_/V? 9p`w+{; 11 <{5{;2 %=fRNPd)%7D8X!@bM =M>Y@ 75&$( +'.#"#"&#&'6!2654.'.54632( *'DkGY04Gjf5hXla: #@'{$.X63Mqn:u {:RQ)W1X8::X}Na7C/ ݁{:^95=[}M490l@ '@! UM =K >Y@ .+15C +%&#"&47>54&+""'6743!3 72#.+"E  OVF+6dj-) .y-5){; 11 <{dgbw y s`hi ?-:AK(PX@K =M>@UM>Y3+N&+2>54.'&47327#".54&'&4727`2/ddN- 3:C15d9-qњZX;c{B/Vo-`@\xR?D' 2  1 9u-׮Z(eϕRy=2  1 ;3-*8K(PX@b =>@dd>YH*C+.7327"'.'&4732736& 79{ .B.Xl,;Q NTg&%F C')2  1 =///0qB 0  1 =[5TT-PYK(PX@b K  =>@b  U>Y@IHEAC##K +64.'&47327'&#""'.'&47327674/&'&47327{'=;w NKd9 /HwHf  ?7+  /)JX -2  1 =;5893R78TP"2  1  2wX)-s2  1 - '-VmK(PX@(b`K  =K >@&b` UK >Y@KGDC?=9851CF +%&#"&47>'&"&#"&47>764'.'&473273276&'&47327N?NB {VRR0 #L3=| Ji1/f32  1 7B"--<HK(PX@K =K >@UK >Y@ 33C+%&#"&47>54&'.'&472776&'&4727FPV F2HF { U& $H or  JO) {; 11 <{78/U0 0  1 *B]>*2  1 3JF*?3T9/@.<:K PX@#bXM =N >K(PX@$b`M =N >@"b`UN >YY8d9@+&#!"54767654#"'673!26327%>72h7'Pq!"# ˨#k1?3PqL'%*kl @du %TFE u@eK> +! w! ?on u+ ) +NbLD; ) H@e> +#qZ)Bw@P> +!&47>54.'&47Nw! ?on- +NcLC<.B3@<e > +#3#]V]n@GK? +!&47 E B A N @e>$% +#"/7632m  +?F)/J 8H@E24<b`M=M=M>$&#(("%+32765## 546?65#"#"7463 327'"&XKHFn)op D`8)H5 %'NuF9+17[[m Vf'+ ++ /JV+1:!(L= 0j@,@#b=M=M>Y@ $"$$+32654&#"632#"'&"'654.'&7672d#NE_dXfx<!-7 i7`㷓\2!'jBA&*RL/-@*<bM=M>$$$$"+%#"&5432#"'.#"327/Hg@$FA:fri!wW lV+0J-3/¦ׇP=e,@"U=M=M>Y@ =<"%$%%+%4&'&"3276#"&5476323274.'&7672'&'&" ?g{D;ZPq!jJ5-8 i8^ N/'JfTbd{ۍ{BA&*R`P= % ?H0 LB !1@.bUM=M>#"%"# +%2'4&#"#"'&543 #%326sdF4L> #n`Z&AZVmBub-n,o#m1-)2nKPX@)ZM= M=K >@*bM= M=K >Y@ 20"#%###C +%&#"&47>5#"=4;54632#"'.#"32'#f4x  lRZ6 /iۏ{=B958//5 11 K(PX@9 b  UU M =M= M  >@? bZ  UU M =M= M  >YY@ RPMK><6431,*$"V VW%%" +4&#"3232>54&'&"#&"'&#"#"'37632#"&547&5467&54632>32f` %P=?J`}-;? g%-Z'4 XˏqI%N5=R8RZ^1)jэV%{-)5/;9TX/Lu\k2jF'5##7$!Z{+3D;: CJ\jQn{e/o7u"f917+!.7F*@' <=M= >DB84*(H +&#"&47>54.'&7672767632&#"&47>54'&#"V,9  sWZ 3-& -7 iu57/ZR^sT+%H{#JWI 1  1  'DBBA&*R3FHy9 1 1:y7m1Jy&5- (;K-PX@M == >@U= >YH$"+4632#"&&#"&47>54&'&7672F%';A)%>1b \^b2/V Z%=D&#@DV{9 11 8{V-#"f7- /K/PX@ bU=M>@bUQ>YY'$($"+4632#"&4&'&7672#"&54632325E%'LKHD=<50C +74.'&76726767>&'&47327&#""5&/&'&'&#"&47>-7 hBIJZ %)^kT^ H%L? RV-RR!!='C  ^\T Z/BA&*R6 67q $1  2L / 1 !o, y; 11 :%)@& <b=L >C+74&'&7672&#"&47>)[ i4`]V `4q1&*RP{; 11 <-?\1@.<M =L >ZYK$J)I%# +76327632&#"&47>54&#"&#"&47>54'"&#"&47>54&'&7672\Np}V+Xs`jgT+==|)PfcjgV)b{#,; {Tb{Z20V d UA{9 11 @M= >J*J!+632&#"&47>54'&#"&#"&47>54&'&7672yu57/Z{^V{T+'F}}#+R {Qf|`0/W d ݦFJy9 1 1:y7m1Jy'/y9 11 8{V-#'T%@"M=M>&# +47632'""32654&TqzǓPu=sLZ˅sւs EBT!?.@+M=M=L>C%%&'+32>54&#"'7632'"'&&#"&47>54&'&7672f! 0aRw=g+{PЗydN 7q qV`4/V d )16(+Dt{FLs{: 11 <{jV-#'H!8.@+M=M=L>C%$-&!+&#"327>54&54&'"543267632&#"&47>C^w9^a1S4 $7f\2b \Vb2KȤoT' '5L < '3{: 11 :-0)@&bM=L >J"$%+7>32#"'&#"&#"&47>54&'&7672h )yA;@;%&$!E17q iZd4/V c  Fd?#'@0dJ?{7 11 :{V-#'b4A@>3<bM=M =M>"+%*#+6232654&'.54632"'.#"#"'&#"&b , !^bBlT}{fu/bm . /@/7Y]pDjg1dV cJEDR33wm`ml =R# G'%#! ** +34.5476767232'#327#"5#"=4X^-$"" 1'^Jyy oNf RO )dY:!i )-)s::@7bM=N>87&%$! :: +"&54&'&4732723276'4&'&473272'&'&{3LF3d@\q!1N F38^ Ny`>1  =yZb3b@1  =wP= % ?F2 z s*@=>H(C +.7327"'.'.732776&  fV=R  N?"#"6L  {CH M !/1  2@P/+V6 1  2 6Iu?=N5 sJ/@,8<K=>HD<:6510-)&% +67'.'&4732767>.'&47327"' #"'.'.7327G  %0#{9R{P  <7 {VBQ  HH>!:G  jPT{ /IE:963CJ +?6&'.7327&#"&47>&/&&"&47>?6'&'&47327u}!=  jBDQ?R6 ;FL bUP{ '#w;  jR ?P8 PhjF`o Hݸ51  2)BR+ 11  //3 11 'H s 1  2%s8@=N>/,L!$!+#"&54632327676'.'&4732776&'&4727Ts)73' #F!16';?RTPP"<> {R F?!++!=;dXF{[61  2?@V:81%1  27DfL;$=@:!<b=M=N >$$%#$q+3!2>32!>?2%"547'6747Bw9)%LS)'*/3s3#!=PCV< |Kn N$7@ R>Y +3"&76'&7>5!mjsqlbG  h` A %/;jy!/v{!\ Xy9  7g%@=> +#3ZZ%qL$:@eM>Y +&675.76&#.72!.7>krqlcG   h`  %/;wTy!/w1{! X  7f5V3@0 <eUIMA"$""+>3232672#"'&""5ZBX\75w jL#BYX>7}#hADDE dwDBTA+@M=M>+"&54>7>76"&462RVB 1)?Z@@Z+JL;qB`^ ) E?QL[??[?M")?@<$"#<b`VK>($+3#"'.'327#5.5467J<$B 0# d @XJĉLZ[fP*,F&+7jW䱯!AV@S- <bU U  U M   >97421/*(%$"  AA +"'546;5467632#"'&#"32+2327#"&#"#"&547>= ]OYu1s/%+33Hb1Vw1J-B 'b\ZR/#*hT<f)Vy31'-'/ )#ZRc{3#%!-9=ڟ';} 'I@F#&$<:%9UIMA  ' '$" +32654&#""''7&547'7627'݋qoeoj]XuVVqPcj}idTwRPqNblqywy NsX\hfZVnLOvX`di^RmPTK@HbU V   U K >SROKHGDB?=<:'GF$!#!+5!"=4;5!"=46;&'&47327"36&'&47327!2'#!2+&#"&47> 1 ;i53 ;3L P+533HLV H' } 21 6>hq1 2 / +ua ' '= 11 >d dNKPX@OK >@SGK?Y@ +## ddddfbD?S8@5J@ <b`QM >0.+)&$##$ +&54632#"'.#"#"&54232654'.56654.'&'\J=<=H^ZR@# \I=<>H^ZR@# 7V))i/Ca;V))h/D`R]mGNTDE^He//51@J(}]mHNTDE^He/052?J)LY7a;N'80NX7`@2b UUUIM AY@+*! 1/*5+5&% )!)$"&" +#"'&547672#.#"327"54 "3254#N{UoJ^?X7 LPJhsVsE\\$'5NH\}Ri J`TNsqf\VQ!D-)?B/n@ , @$bUIMAY@ +)&$  / /$ +327"&'#"&54?4#"#"&54632327^\7LX#-5H1B\wn3<F *!?1TC1/!P@{59]u -5;  "+&'567&'567?y*ѩ) y)Ѫ)##Ӊ##Rm@9GK? +%!&7!T>3'RbD @IMA   +!"5463!2+F!%'7)>F/ 8BN@ @.  b UU  QM >Y@ DC:9 JHCNDN?>9B:B*& 87#! +4#"326'26327#"/&#&#".7>54&'.7"54 "3254f%-V11of]B3b#5 #&P-b B4J79J33J\\$'h)3^;9N; Ns1%%1/1%VQ!D-)y; @IMA   +!"7463!2 ;(*FF @UM>$+4&"326%462"&FHJ=;Kxwwx9[X<9TV9VyyVTxx{L`';@8dbUINB$!''#"##"" +4632!2#!#"5!"5463!!"5463!23#i%/!!R!!&)%"4!\!.i!.#3\)&@ %K1PX@"b`PM>@(b`UIL@YY@!&& +"&5463232>72!47>54#"$olZX_h% !ZN@gP5iPZBnDLl5! qP}?\Lo+ *+N11@ .KPX@&b`QM=>K1PX@(b``QM>@.b``UIMAYYY@ #"$)&&+4&"#".54>32#"&5463272654#"&76}/>A  'qRP^i=g}:+M;Z! %! 77?:R9\Rh-+#%EX}%X @e>$! +632#"&547f ?2' /' %^!7@ " <&:K!PX@-   b`= =M=>@0   b`b=M=>Y@77%!&&&! +%#"'"#"&54654&5'267326726732632#"'Z+E1'!T!N&y )''+Z/{/H';@L91DABoZ9u;@7  )+u!- 4s@ *"@bUK>Y@ -+'&! 4 1#" +3254&7 7&! '54?>5#"&54632RTTRT `5?21@5be8?/&ȽXNNNC'%--%'''+ˇou@IMA +"&462+VDDVDCVDDVf-q@<;KPX@XVIMA@dVIMAY@  +"'73632#"'4772654j5' RF4?R}^HPN)/9 ʼnF9BV#!+'G4@1<:dIK?  +"&'676&'"&7>54&Uo#!=o=33+ '/D '- 32 3!Pm} (@%UIMA   $" +4632#"&"32654&PsJMZV5ZZo}qmPwXVVby5;  "+&'67&'6'&'67&'6y*Ѫ) y)Ѫ)##Ӊ##Z(F@+ <1-:KPX@,  d b   SPM >@3  d b   SUIL@Y@*)@?=976)F*F2(" +'35#"76767676723#&"&7>"&'676&#"&7>54&uTVVp> 9I"'&s f-;==B*Uo#!=o=33+ Lv1: v(E19!'/D '- 31 3!^*H@-)<3/:KPX@6 d bX SUIL@@7 d b` SUIL@Y@,+BA?;98+H,H%# ** +'"&5463232>72!47>54""&'676&#"&7>54&BVTT$omZX_h% !ZN@gPpUo#!=o=33+ 5iPZBnDLl6! qP}?]Lo+ )+j'/D '- 31 3!J^5:Ze@26  KPX@Eb``  b U PM = M  >KPX@Cb``  b U   U PM >@Jb``  bU U   U I L @YYY@YXVSQPMLKJHF"#"$)&&+'4&"#".54>32#"&5463232654#"&7635#"76767676723#&"&7>TVV/>A  'qRP^i=g}:+M;Z  > 9I"'&s f-;==B+%  78@9R:\Rh-*#%FX}%xLw1: v(F19!^)(0/@,b`M=N>-%%+4&54632#"&54>7676=43226"&4621!3i\%R53V"!=4mp=@Z??Z'#!&)/dTP9cT-%?'7_Z^ǃ9gHf?[??[? Ho :KPX@$dUM =K >@"dSUK >Y@ FD:951('   % +#"'%6!#&#"&47>727&#"&47>&'!"F 9@1j 2nLk NZ%-D?63 [u!wP% =- ^  11 4XJ)l/1  11   [ Go:KPX@$dUM =K >@"dSUK >Y@ EC9840'&  $ +'"547!#&#"&47>727&#"&47>&'!"VX'9=j 2nLk NZ%-D?63 [u!wP%)Awe- ^  11 4XJ)l/1  11   [w Kg :KPX@UM =K >@SUK >Y@IG=<84+*# +&'&'676!#&#"&47>727&#"&47>&'!"Zy T`{T{%j 2nLk NZ%-D?63 [u!wP%wmZ! =@F7?- ^  11 4XJ)l/1  11   [f$[@ @/U VS   U K  >Y@"YWMLHD;:3/! $$ +2676#".'&#"'62!#&#"&47>727&#"&47>&'!"`#)!) #?0'''/u%)3j 2nLk NZ%-D?63 [u!wP%+? '-6 'J- ^  11 4XJ)l/1  11   [MvKPX@)U   UM = K >@'US   U K >Y@KI?>:6-,%! +462"$462"!#&#"&47>727&#"&47>&'!"HGMLGQHQDB873/"! +!#&#"&47>7.5462&#"&47>&'!""2654&j 2nLk NZ%6Gml9.?63 [u!wP%1/35Z66D- ^  11 4X@\7FhfH1U/1  11   [X>/-??-1<1]@,=:A Q <*:K(PX@?Z  Z  U SN = M= L  >@=Z  ZU  U S M= L  >Y@\[XSNKIGCB#7CK+!4'!&#"&47>76&'&47)272'.+"326762"'.+3!26&#!&47>+D /#n L\-!8di5/!"'Vk#&}9 21 RTII%#3bJes@q)+1' 11 8T/A2 F{d3'f1[ {6% 70%X 1 <LfD8d@a#&20<;b UQM =M>53/-)'"! 88 +"'7.547632'!"32672632#"'47726565( IQrg'hz Ǐl,?R}^HPN)/9 i釠_[ XiwxF9BV#!+'G9 M@C &@: d ZZ   U UM=L >Y@ JG@?>:7610-(#  M M% +#"'%626762"'.+;26&#!"&47>54&'&473!272'.+" 9@}:1  1RT3@J' 2cHss??sd 50!#'VjP7 =y1[ bOJj 70%;H9 1 <{5{;2 F{d;H9 L@B  %<@ ;:K(PX@< d ZZ U N  =M=L >@: d ZZ   U UM=L >Y@ IF?>=9650/,'" L L$ +'"54726762"'.+;26&#!"&47>54&'&473!272'.+"X'9p}:1  1RT3@J' 2cHss??sd 50!#'VjP7)Aw1[ bOJj 70%;H9 1 <{5{;2 F{d;H9w P@F )@5 ZZ   U UM=L >Y@MJCBA=:9430+&# PP +&'&'67626762"'.+;26&#!"&47>54&'&473!272'.+"Zy T`{T{%}:1  1RT3@J' 2cHss??sd 50!#'VjP7wmZ! =@F7?U1[ bOJj 70%;H9 1 <{5{;2 F{d;H9qR@H+@?  Z   Z U   UUM= L >Y@ OLEDC?<;652-(%" RR+462"$462"26762"'.+;26&#!"&47>54&'&473!272'.+"9R99RC@dUK >Y@ CC%+#"'%6&#"&47>54&'&47327 :@@r PR s??s GV s? ={; 11 <{5{;2  1 ;{B +S:K(PX@dK =K >@dUK >Y@ CC$+#"547&#"&47>54&'&47327X'9@r PR s??s GV s?)Aw{; 11 <{5{;2  1 ;{Bw /Q :K(PX@K =K >@UK >Y@-,)%"! +&'&'676&#"&47>54&'&47327RZy T`{T{%`@r PR s??s GV s?wmZ! =@F7?l{; 11 <{5{;2  1 ;{ Tq1[K(PX@!U K =K >@U UK >Y@ /.CC +462"$462"&#"&47>54&'&47327fK(PX@"U M  =M >@  UUM >Y@:9641/,+(">=#"(" +%32>54.#"32+263 #"&#"&47>5#"546;4&'&47TuT=힬X!R'}w{j s??s 73/nі}vd#i;1 <{R${;2 bZ@N K(PX@7  b  `U V K  = =>@8  b  `U V  I = M>YY@YWUTQPGC9742*& +2676#".'&#"'624.'.7327#"'&"&#".7>5.'&47327V#)!) " ?/'''/u%)3T 15?  /=s?/-%53 15? 75  s? Z> X +? '-6 '#HK+ 2  1 A?1DdHK+  11 BV-R2 q>LT $2@/ :dM =M> #!  % +#"'%6"32# ! o 9@ǴVT!v =: +T{h'3LT #2@/:dM =M> "   $ +'"547"32# ! X'9ǴVT!v)Aw: +T{h'3LTw '/@, :M =M>&$  +&'&'676"32# ! Zy T`{T{%ǴVT!vwmZ! =@F7?l: +T{h'3LTb)7Q@N<U V M =M>640.%#)) +2676#".'&#"'62"32# ! H#(!) #?0'&'/u%)4kǴVT!v+? '-6 ': +T{h'3LT)5@2UM =M>(&"  +462"$462""32# ! ;N;;N :R99R`ǴVT!v}R99R99R99R9J: +T{h'3s "+''7'77wHIIHRHHHHLTD!)6@3#"  <M =M>&$!!)'+"&'7&!272#"'"&324*jT!e)fVި_Xnɴ)3}}%~{|՜-Ҥ:  ? ES :K(PX@dK =M>@dUM>Y@ 3+N*%+#"'%62>54.'&47327#".54&'&47275 9?y2/ddN- 3:C15d9-qњZX;c{B =Vo-`@\xR?D' 2  1 9u-׮Z(eϕRy=2  1 ; ? DS:K(PX@dK =M>@dUM>Y@ 3+N*$+#"7472>54.'&47327#".54&'&4727=X'92/ddN- 3:C15d9-qњZX;c{B)AwRVo-`@\xR?D' 2  1 9u-׮Z(eϕRy=2  1 ; ?w HQ :K(PX@K =M>@UM>Y@GFC@=<53($ +&'&'6762>54.'&47327#".54&'&4727Zy T`{T{%2/ddN- 3:C15d9-qњZX;c{BwmZ! =@F7?Vo-`@\xR?D' 2  1 9u-׮Z(eϕRy=2  1 ;?8@HjK(PX@" UM =M >@  UUM >Y@HGDC@?<;32/) 88 +".54&'.732722>54&'&632327462"6462"=xdB;b ]Hf1023f{H+7 XLzb59N::N9N::NFjvLy=2 ?T^\a3fYhbN 4 1 ;{-N99N99N99N9 FY:K(PX@dK =K >@dUK >Y@ 33C$ +'"747&#"&47>54&'.'&472776&'&4727X'9FPV F2HF { U& $H or  JO) )Aw{; 11 <{78/U0 0  1 *B]>*2  1 3JF*?3 - 9t@ 9 @%UU  UK >Y@ 863CC#" +3 4&#"&#".7>54&'.7327632#"'wr+ofP?s;B  s?@r  ;Bs?%Z%1iŁd7 G{; 11 <{5{;2  1 ;{Hvs}N%O@ 43@1bM=M=K =N>Y@ K*'$.&"" +74#&632654>32#"&5463232654%5>54&#"&#"&47>i ;'TAP]ZR^?u`&9'%!5\sePPLd-%> ^Ud{ \1 :#;yb2TJRC5J}TrR/>1 XiVdL`qw; 11 <Jb F@ @B@:ddb`M=M =M >Y@ EC&#(("&$% +#"/763232765## 546?65#"#"7463 327'"&  +?FעKHFn)op D`8)H5 %'NuF9^)/{+17[[m Vf'+ ++ /JV+1:!(L=Jb F@ @B@:ddb`M=M =M >Y@ EC&#(("*$! +632'"&54732765## 546?65#"#"7463 327'"& ?2' KHFn)op D`8)H5 %'NuF9^/' %s+17[[m Vf'+ ++ /JV+1:!(L=JH FW@T@B< :b`M=M=M>EC?=7520(& +&'&'67632765## 546?65#"#"7463 327'"&Lw\sbH$KHFn)op D`8)H5 %'NuF9H{L^s7s+17[[m Vf'+ ++ /JV+1:!(L=J(Vx@uP R<  b  `U  V M = M =M >USOMGEB@86.,*)%# +2676#".'&#"'63232765## 546?65#"#"7463 327'"&D#(!) #?0'''/u%)4KKHFn)op D`8)H5 %'NuF9+? '-5 &G+17[[m Vf'+ ++ /JV+1:!(L=J LY@VFH<b  ` U M = M =M >KIEC=;(("' +462"&%462"&32765## 546?65#"#"7463 327'"&9N97R7Z9N::N9TKHFn)op D`8)H5 %'NuF9)99)'::')99)'::j+17[[m Vf'+ ++ /JV+1:!(L=JV Mo@lGI<b  ` U M = M = M =M >LJFD><97/-%#!    +"2654&462#"32765## 546?65#"#"7463 327'"&/35Z55lljJHKHFn)op D`8)H5 %'NuF9=/-@@-1;hfHFh+17[[m Vf'+ ++ /JV+1:!(L=P/9ENU@R8 :#<  b  U M=M=M>MKGFB@'%""#(! +&#"#"54632632327#".'&'"'&54676%&326767>54&#"+ZB\+%RǁPhTpi-bV1#}BdI8zgLVT1 dFd--+ )\XyDXd9{Vh9-' $9=u!HL8IFSh=1J^3\Lf/6d@a $0.<;b UQM=M>31-+'%#" 66 +"'7.5432#"'.#"327632#"'47726545' K@$FA:fri! C\,?S}_HON)/:  lV+0J-3/¦ׇn\uF9BV#!+'GLBb /zK!PX@2bbV =M=M>@/ddbVM=M>Y@ #"%"#!$% +#"/7632%2'4&#"#"'&543 #%326  +?FsdF4L> #n`Z&AZVm^)/ub-n,o#m1LBb /zK!PX@2bbV =M=M>@/ddbVM=M>Y@ #"%"#%$! +632'"&547%2'4&#"#"'&543 #%326) ?1' sdF4L> #n`Z&AZVm^/' %ub-n,o#m1LBH /8@5 :bUM=M>#"%"#.+&'&'676%2'4&#"#"'&543 #%326Lw\sbG%sdF4L> #n`Z&AZVmH{L^s7sub-n,o#m1LB 5A@>  bU  UM= M>42/-%"#" +462"&%462"&%2'4&#"#"'&543 #%3269N97R7Z9N99N9sdF4L> #n`Z&AZVm)99)'::')99)'::ub-n,o#m1#b *@K!PX@b == >@dd= >YG$%+#"/7632&#"&47>54&'&7672ˏ  ,?F1b \^b2/V Z^)/{9 11 8{V-#"f5b *@K!PX@b == >@dd= >YK$!+632'"&547&#"&47>54&'&7672N ?2' 1b \^b2/V Z^/' %{9 11 8{V-#"fH *@ := >'& +&'&'676&#"&47>54&'&7672!Lw\sbG%o1b \^b2/V ZH{L^s7s{9 11 8{V-#"f 0 @U= >H+462"&%462"&&#"&47>54&'&76729N99N9Z9N97R7!1b \^b2/V Z)99)'::')99)'::{9 11 8{V-#"fF,:@7<$#"! :M=M>+) +%2654'&#"4>32&''7'&'77#"\ NJj6XDTH?x-!BI!oj-%55tbEqwAu}g3A!D4,;WB%p-;\K@H<U VM = >XWIE;9/+! +2676#".'&#"'632632&#"&47>54'&#"&#"&47>54&'&7672#(!) #?0'''/u%)4둿u57/Z{^V{T+'F}}#+R {Qf|`0/W d +? '-5 &GFJy9 1 1:y7m1Jy'/y9 11 8{V-#'Tb )_K!PX@#b =M=M>@ ddM=M>Y@%#))&$$%+#"/763247632'""32654&ˏ  ,?FqzǓPu=sLZ^)/B˅sւs EBTTb )_K!PX@#b =M=M>@ ddM=M>Y@%#))&($!+632'"&54747632'""32654&) ?1' qzǓPu=sLZ^/' %:˅sւs EBTTH ).@+ :M=M>%#)) +&'&'67647632'""32654&%Lw\sbG%LqzǓPu=sLZH{L^s7sT˅sւs EBTT,9Q@N<U V M=M>.-53-9.9+)#! +2676#".'&#"'63247632'""32654&o#(!) #?0'''/u%)4qzǓPu=sLZ+? '-5 &˅sւs EBTT "/3@0UM=M>$#+)#/$/&% +462"&%462"&47632'""32654&9N97R7Z9N99N9qzǓPu=sLZ)99)'::')99)'::1˅sւs EBT{t,@)UQM>+462"462"!"5463!26K55K66K55K!!&K66K5K66K56!.#3N'@!  K!PX@ e=M=M>@ deM=M>YY@$"('+2'"'"&'7&547632"&32656P1U{uoh lfqzǑf$JtxMZ/iւsY~|˅Fkdb$-)b HK!PX@.b  b =M=N >@+dd  bM=N >Y@FE432/,+%#HH$% +#"/7632"&54&'&4732723276'4&'&473272'&'&  +?F {3LF3d@\q!1N F38^ N^)/y`>1  =yZb3b@1  =wP= % ?F2 z-)b HK!PX@.b  b =M=N >@+dd  bM=N >Y@FE432/,+%#HH$! +632'"&547"&54&'&4732723276'4&'&473272'&'&` ?2'  {3LF3d@\q!1N F38^ N^/' %y`>1  =yZb3b@1  =wP= % ?F2 z-)H HA@> :bM=N>FE432/,+%#HH +&'&'676"&54&'&4732723276'4&'&473272'&'&Lw\sbG%B{3LF3d@\q!1N F38^ NH{L^s7sy`>1  =yZb3b@1  =wP= % ?F2 z-) NH@E  bUM =N >LK:98521+)#"!NN +462"&%462"&"&54&'&4732723276'4&'&473272'&'&9N97R7Z9N::N9{3LF3d@\q!1N F38^ N)99)'::')99)'::yy`>1  =yZb3b@1  =wP= % ?F2 z%b FSK!PX@b ==N>@dd=N>Y@ =:L!$&$!+632'"&547#"&54632327676'.'&4732776&'&4727 ?1' HTs)73' #F!16';?RTPP"<> {R F?!+^/' %N+!=;dXF{[61  2?@V:81%1  27Df!Dw@-b=M=M=L>Y@ C%(%% +2654'&#"4.'&76727632'"'."&#".7>5J=Ryj1FB-7 h l˓wPA  7q H9 `3N{.$4٠k1wBA&*Ry!nw{: 11 <{% L+@(U=N>C@L!$# +462"&%462"&#"&54632327676'.'&4732776&'&47279N99N9Z9N97R7Ts)73' #F!16';?RTPP"<> {R F?!+)99)'::')99)'::E+!=;dXF{[61  2?@V:81%1  27DfN IxKPX@( U UM =K >@& US UK >Y@ GE;:62)(!    +!"7463!2!#&#"&47>727&#"&47>&'!"5Wj 2nLk NZ%-D?63 [u!wP%%.#0]- ^  11 4XJ)l/1  11   [J Da@^> @<b` UM=M =M >CA=;530.&$  +!"5463!232765## 546?65#"#"7463 327'"&6 MKHFn)op D`8)H5 %'NuF9%%-#/+17[[m Vf'+ ++ /JV+1:!(L=o KKPX@. dU   VM = K >@, dUS   V K >Y@IG=<84+*# "" +#"&'33267!#&#"&47>727&#"&47>&'!"ks9 }TP aj 2nLk NZ%-D?63 [u!wP%oh}k9DC:- ^  11 4XJ)l/1  11   [J- EZ@W? A< :b`UM=M =M >DB&#(("(% +267#"&'32765## 546?65#"#"7463 327'"&{}7`mǢKHFn)op D`8)H5 %'NuF9-RfbV+17[[m Vf'+ ++ /JV+1:!(L=fDGN@-b  S   UQK >Y@IHKJHNIN#M*D! +"5467"#"&47>&'!"&#"&47>727"32676!#VlY'u!wP%q2nLk NZ%-D?63 fg)7)j D(1   [  11 4XJ)l/1  1N`%)A- Jf>IZ@W?02 <b`  `bRM= M>FD)&#(("! +"547.'## 546?65#"#"7463 32732>7632765V6, )op D`8)H5 %'.HPFf1KHFno;3 Vf'+ ++ /JV+1:!(-Dc;`+17[[L (F@C'<:dbM =M> &%#! ( ($ +#"547 47632'!"32672#X'9rg'hz Ǐl)Awt_[ XiwL/b ,o@'ddbM=M>Y@ $$$$'$!+632'"&547#"&5432#"'.#"327 ?2' Hg@$FA:fri!^/' %PwW lV+0J-3/¦ׇLy ,C@@+< :bM =M>*)'% ,, +&'&'676 47632'!"32672Zy T`{T{%rg'hz ǏlymZ! =@F7?\t_[ XiwL/H ,9@6< :bM=M>*($" +&'&'676#"&5432#"'.#"327Lw\sbG$cHg@$FA:fri!H{L^s7sjwW lV+0J-3/¦ׇLq&E@B%<bUM =M> $#!& & +462" 47632'!"32672o?V@@V-rg'hz ǏlV@@V?Pt_[ XiwL// *j @%bUM=M>Y@ $$$$$$"+4632#"&#"&5432#"'.#"327oE%';A)%=Hg@$FA:fri!%=C'#@BwW lV+0J-3/¦ׇL )O@L(<:dbM =M> '&$" ) )  +&'767 47632'!"32672J!{^uffg'rg'hz Ǐl\g)5HR+)NuYt_[ XiwL/) )F@C <:dbM=M>'%!  +&'767#"&5432#"'.#"327N)HN'H+Hg@$FA:fri!jNPhpwW lV+0J-3/¦ׇD 9i:K(PX@dM =M >@dUM >Y@54/.+%98 +&'76732>54.#"'263 #"&#"&47>54&'&47TJ!{^uffgTuT=힬X!R'}w{j s??s \g)5HR+)Nu73/nі}vdi;1 <{5{;2 P!N@ =@)UM =M=M>Y@NM<;0.,*%#  +2'>54#"5464&'&"3276#"&5476323274.'&7672'&'&"7Dob ;F^/ ?g{D;ZPq!jJ5-8 i8^ N[;B # X)+N+h/'JfTbd{ۍ{BA&*R`P= % ?H0 D->fK(PX@"S M  =M >@  USM >Y@:9650/,+(">=(" +%32>54.#"3#263 #"&#"&47>5#&746734&'&47TuT=힬X!R'}w{j s??s 73/nі}vd%i;1 <{R#{;2 PN5 @5  b  T  U =M=M>Y@NM?>9843"%$%% +%4&'&"3276#"&5476323275!&7467!.'&#&76723#'&'&" ?g{D;ZPq!jJ5 '  iye8^ N/'JfTbd{ۍ{#25&*R%P= % ?H0 9\ N@D '@> ZZ  U   UUM=L >Y@$ KHA@?;8721.)$! N N   +!"7463!226762"'.+;26&#!"&47>54&'&473!272'.+"5}:1  1RT3@J' 2cHss??sd 50!#'VjP7%-#/1[ bOJj 70%;H9 1 <{5{;2 F{d;HLB -I@Fb UUM=M>,*'%#!   +!"7463!2%2'4&#"#"'&543 #%3265:sdF4L> #n`Z&AZVm%%-#/ub-n,o#m19m P@F)@Dd  Z   Z U   UUM= L >Y@%MJCBA=:9430+&# PP "" +#"&'3326726762"'.+;26&#!"&47>54&'&473!272'.+"Tks9 }TP }:1  1RT3@J' 2cHss??sd 50!#'VjP7mh}k9DC:v1[ bOJj 70%;H9 1 <{5{;2 F{d;HLB/ .B@? :bUUM=M>#"%"##% +267#"&'%2'4&#"#"'&543 #%326{}7`m@sdF4L> #n`Z&AZVm/RfbV+ub-n,o#m19qJ@@  #<> ;K(PX@? ZZ U U N  =M=L >@= ZZ U   U UM=L >Y@ GD=<;743.-*%  J J +462"26762"'.+;26&#!"&47>54&'&473!272'.+"@V??VA}:1  1RT3@J' 2cHss??sd 50!#'VjP7V@@V?G1[ bOJj 70%;H9 1 <{5{;2 F{d;HLB- -uK-PX@/bUM =M=M>@-bUUM=M>Y@ #"%"#"$" +4632#"&%2'4&#"#"'&543 #%326qE%';A)%=ssdF4L> #n`Z&AZVm%=D&#@Dub-n,o#m1f91V@"307 H  < :K(PX@FZ  Z  b  U QN = M= N  >@DZ  Z  bU  U Q M= N  >Y@VURPMJEB?=98#7CT!+"'467"#!"&47>54&'&473!272'.+"326762"'.+;26"#"32>76VlY0Hss??sd 50!#'VjP7}:1  1RT3@J' 2 fg1D(1 <{5{;2 F{d;H1[ bOJj 70%;H9 N`LfB)4R@O<bbUQ M=M>1/!'#"%$! +"'47#"'&543 #%326732>76%2'4&#"Vn`Z&AZVl<#FZiTg1sdF4L>oo#m1=[-Xk?`?ub-n9 M@ C &@;  d ZZ   U UM=L >Y@" JG@?>:7610-(#  M M  +&'76726762"'.+;26&#!"&47>54&'&473!272'.+"%J!{^uffg>}:1  1RT3@J' 2cHss??sd 50!#'VjP7\g)5HR+)NuP1[ bOJj 70%;H9 1 <{5{;2 F{d;HLB' ,L@I:dbUM=M>+)&$"  +&'767%2'4&#"#"'&543 #%326N)HN'HsdF4L> #n`Z&AZVmjNPhp@ub-n,o#m1LTw @M@J8$< :b`M =N>;9/+#!@@ +&'&'6762'.#"3274&'.7327! 4$Zy U`{T{$d+'# 5`kmQG` ITc 1- ;wmZ! =@F7?%1Njk7N99" 5  5 +4pPBF ,d@\4U;P<  :KPX@; bU M =  M =M= M  >K(PX@9 b  UU M =M= M  >@? bZ  UU M =M= M  >YY@.-`^[YLJDBA?:820-d.d,'  +&'&'6764&#"3232>54&'&"#&"'&#"#"'37632#"&547&5467&54632>32Lw\sbG%f` %P=?J`}-;? g%-Z'4 XˏqI%N5=R8RZ^1)jэV%{-)5/F{L^s7s9TX/Lu\k2jF'5##7$!Z{+3D;: CJ\jQn{e/o7u"f917+!.LTs @]@Z8$< db`UM  = N  >;9/+#!@@ "" +#"&'332672'.#"3274&'.7327! 4$js : }TP d+'# 5`kmQG` ITc 1- ;sh}k9DC:%1Njk7N99" 5  5 +4pPB' +c@[3T:O < :KPX@C  b U U M = M =  M= M  >K(PX@A  b U  U U M =  M= M  >@G  b  Z U  U U M = M= M  >YY@-,_]ZXKICA@>971/,c-cW%%%%+267#"&'4&#"3232>54&'&"#&"'&#"#"'37632#"&547&5467&54632>32|}7`mf` %P=?J`}-;? g%-Z'4 XˏqI%N5=R8RZ^1)jэV%{-)5/'RfbV-9TX/Lu\k2jF'5##7$!Z{+3D;: CJ\jQn{e/o7u"f917+!.LTq:O@L2<b`UM =N> 53)%: : +462"2'.#"3274&'.7327! 4$o?V@@VVd+'# 5`kmQG` ITc 1- ;V@@V?X%1Njk7N99" 5  5 +4pPB- *bm@Z2S9N K(PX@C  b  U UM = M =  M= M  >K-PX@I  b  Z  U UM = M = M= M  >@G  b  Z U  U U M = M= M  >YYY@,+^\YWJHB@?=860.+b,bW%%$$"+4632#"&4&#"3232>54&'&"#&"'&#"#"'37632#"&547&5467&54632>32qE%';A)%=f` %P=?J`}-;? g%-Z'4 XˏqI%N5=R8RZ^1)jэV%{-)5/%=D&#@D9TX/Lu\k2jF'5##7$!Z{+3D;: CJ\jQn{e/o7u"f917+!.L1TDDV@S<(<9b`eM =N>?=3/'% DD +2&5>54'&5462'.#"3274&'.7327! 4$9Fy`HQ?V91d+'# 5`kmQG` ITc 1- ;T`PVg!>/B%3%1Njk7N99" 5  5 +4pPB#6n.@f>_E Z$ < :KPX@E  bU  U M =M=  M= M  >K(PX@C  bUU  U M =  M= M  >@I  b  ZUU  U M = M= M  >YY@(87jhecVTNLKIDB<:7n8n61*(#! +"&5476726324&#"3232>54&'&"#&"'&#"#"'37632#"&547&5467&54632>329F/;o PM  7/7f` %P=?J`}-;? g%-Z'4 XˏqI%N5=R8RZ^1)jэV%{-)5/Z;L;J P) 3!6$9TX/Lu\k2jF'5##7$!Z{+3D;: CJ\jQn{e/o7u"f917+!.sGUtRPNLJ:K(PX@%  S K  =K >@#   U  SK >Y@EDA=:96521CCC+%&#"&47>5!&#"&47>54&'&47327!4&'&47327&'&'676?s TRs@C@r TV s??s lo s?@sUb s?%Zx T`|T{%{; 11 <{i{; 11 <{5{;2  1 ;{H{;2  1 ;{DmZ! =@F7?7 T0@-. <=M= >RPFB86-, +&'&'676&#"&47>54.'&7672767632&#"&47>54'&#"EjOdXA",9  sWZ 3-& -7 iu57/ZR^sT+%H{#PN`u9xWI 1  1  'DBBA&*R3FHy9 1 1:y7m1Jy&-KOK(PX@1 SS  K =K >@/    U SSK >Y@!ONMLKJIHFEB>;:87541-*)CC+%&#"&47>5!&#"&47>5#53.'&47327!.'&473273+!!?s TRs@C@r TV s?Dh lo hDCiUb hDuvC{; 11 <{i{; 11 <{EY.2  1 .YY.2  1 .YE7OA@>%<bT= M=  >MKA=&H +&#"&47>5#53.'&#&7672!!767632&#"&47>54'&#"V,9  sWZ 3-&  '  idu57/ZR^sT+%H{#JWI 1  1  'DBCP59&*RPFHy9 1 1:y7m1Jy&L`?@ @'U  U  VK >Y@=<9521,+($!  +2676#".'&#"'62&#"&47>54&'&47327#)!) #?0'''/u%)3 @r PR s??s GV s?+? '-6 '{; 11 <{5{;2  1 ;{;;@8<UU= >87)%  +2676#".'&#"'632&#"&47>54&'&7672y%Q>  9*"%"+h$/+1b \^b2/V Z+?_l ',6 &d{9 11 8{V-#"fB\ -bK(PX@ UK =K >@UUK >Y@+*'#   +!"7463!2&#"&47>54&'&47327 5@r PR s??s GV s?%-#/{; 11 <{5{;2  1 ;{ *%@"U= >'&    +!"&5463!2&#"&47>54&'&7672c  `1b \^b2/V Z% - /{9 11 8{V-#"fHq /oK(PX@& dU K =K >@$ dU VK >Y@-,)%"! "" +#"&'33267&#"&47>54&'&47327Hks9 }TP @r PR s??s GV s?qh}k9DC:{; 11 <{5{;2  1 ;{3 )#@ :U= >I%+267#"&'&#"&47>54&'&7672:no2Vak1b \^b2/V Z3RfbV~{9 11 8{V-#"ffB-5n @%  bUQM >Y@ 54#CCD! +"5467"#"&47>54&'&47327"&#32676+VlYR s??s GV s?@r  fg)7)D(1 <{5{;2  1 ;{{; 1N`%)5f-.:j@$bUR= >Y@ $##4! +"5467#"&47>54&'&7672"'326764632#"&VlY^b2/V Z1b ff)8)F%';A)%>D'1 8{V-#"fh{9 1N`%)%=D&#@DBq)TK(PX@UK =K >@UUK >Y@ CC+462"&#"&47>54&'&47327?V@@V@r PR s??s GV s?V@@V?^{; 11 <{5{;2  1 ;{5@= >F +%&#"&47>54&'&7672s1b \^b2/V Z{9 11 8{V-#"f-&HKPX@(ZR K  =K >K(PX@)bR K  =K >@'b  URK >YY@FEB>;:CC"$' +#"&5463232>54&'.7327&#"&47>54&'&47327$J5-f-mB98)F?E  OZbK/PX@(bU = =M>@%bUQ = >YY@ UTH$%'$($" +4632#"&4&'&7672#"&546323254632#"&&#"&47>54&'&7672E%'1b \^b2/V Z%=D&#@DV-#"gP?F/)3/%=D&#@DV{9 11 8{V-#"fuww 4 :KPX@ZRK >K(PX@bRK >@!bUINBYY@32/+('! +&'&'676#"&5463232>54&'&47327Zy T`|T{$g%J5-f-lA99)E@FPZ b<wmZ! =@F7?hs9'X=19465˪{;2  1 =75D#1O@ <.,*&:K/PX@b=M>@bQ>Y'$(+4&'&7672#"&54632325&'&'676/V VLMT%K/ )F(oLw\sbG%V-#"gP?F/)3{L^s7s1-b@ 4 <9K(PX@'  b eK =M >@%  b e UM >Y@ba\WC?10-)&%  +2&'>54'&'46&#"&47>54&'&4732767>.'.7327&#""'&'&'9Fy`HR?V:@r PR s??s PN s?_CR!#$  OD 5@`.HC-' HQA;oT`PVg!>/B%3N{; 11 <{5{;2  1 ;{Hh!+2  1  3/J#' 1 9UJ1_V@S9!H<9be=K=N >^]ZVONGB1-*) +2&'>54'&5464.'&76726767>&'&47327&#""5&/&'&'&#"&47>!9Fy`HR@V:-7 hBIJZ %)^kT^ H%L? RV-RR!!='C  ^\T Z/T`PVg!>/B%3NBA&*R6 67q $1  2L / 1 !o, y; 11 :)J1@.<b=K= >JIB>CF+%&#"&47>54&'&76726767>&'&47327&""'&/&'&'f%F 9b2/W [AEJY %) 1H%L? {ZQ J!!8y9 11 8{V-#"f 37q $1  2L / 1 +e,  1r@ -<:K(PX@#dZK =K >@!dZUK >Y@ )&!  1 /$ +#"747"&47>54&'&47327;2676&#X'9j s??s LR s?7>## !7)Aw#1 <{5{;2  1 ;{NA%&4@1 <:db=L >&C+74&'&7672&#"&47>'"547)[ i4`]V `4X'9q1&*RP{; 11 <H)Bw1-9y@ 5<9K(PX@$ZeK =K >@"ZeUK >Y@1.)(%!97 +2&5>54'&546'"&47>54&'&47327;2676&#79Fy`HQ?V9j s??s LR s?7>## !7T`PVg!>/B%3T1 <{5{;2  1 ;{NA%1.:@7 <%#9be=L >..C+74&'&7672&#"&47>2&5>54'&546)[ i4`]V `4K9Fy`HQ?V9q1&*RP{; 11 <`PVg!>/B%384@'ZUM=K  >Y@0-('$ 86  +2'>54#"546"&47>54&'&47327;2676&#37Dob ;F^/)j s??s LR s?7>## !7[;B # X)+N+j1 <{5{;2  1 ;{NA%-B@?<bM=L >,+($!   +2'>54#"5464&'&7672&#"&47>7Dob ;F^/)[ i4`]V `4[;B # X)+N+dq1&*RP{; 11 <-/s+@$ZUUK >Y@ '$ / - +462""&47>54&'&47327;2676&#P?V@@Vj s??s LR s?7>## !7V@@V?1 <{5{;2  1 ;{NA%w '3@0<bU=L >C$"+4632#"4&'&7672&#"&47>:)%;8(-)[ i4`]V `4N><)'9yq1&*RP{; 11 < -/]@/  @ZUK >YCV5+;2676&#!"&47>5'74&'&47327{%7>## !7Lj s?%?s LR s?>&NA1 <{[a?v{;2  1 ;{ $4@1!$<b=L >C+&#"&47>5'74&'&7672"4`]V `4#)[ i=g{; 11 <{V;kq1&*R F@ :<:K PX@)dZ`K = =>K(PX@*db`K = =>@+db`I =M>YY@ "J#(L$+'"7474.'.7327#"'&"&#".7>5.'&47327X'9a 15?  /=s?/-%53 15? 75  s? Z> X )Aw:HK+ 2  1 A?1DdHK+  11 BV-R2 q>-;b LRK!PX@b =M= >@ddM= >Y@ J*J&$!+632'"&547632&#"&47>54'&#"&#"&47>54&'&7672 ?1' ou57/Z{^V{T+'F}}#+R {Qf|`0/W d ^/' %sFJy9 1 1:y7m1Jy'/y9 11 8{V-#'1-N@ B<9K PX@*Z`eK = =>K(PX@+b`eK = =>@,b`eI =M>YY@MKIHED;7-+(& +2&5>54'&5464.'.7327#"'&"&#".7>5.'&473279Fy`HQ?V9 15?  /=s?/-%53 15? 75  s? Z> X T`PVg!>/B%3mHK+ 2  1 A?1DdHK+  11 BV-R2 q>-1;P2@/9eM= >LK=9/-# +2&5>54'&546632&#"&47>54'&#"&#"&47>54&'&76729FyaHR@V:qu57/Z{^V{T+'F}}#+R {Qf|`0/W d T`PVg!>/B%31FJy9 1 1:y7m1Jy'/y9 11 8{V-#' G@;<:K PX@*dZ`K = =>K(PX@+db`K = =>@,db`I =M>YY@FDBA>=40&$! +&'7674.'.7327#"'&"&#".7>5.'&47327J!{^uffgu 15?  /=s?/-%53 15? 75  s? Z> X \g)5HR+)NuHK+ 2  1 A?1DdHK+  11 BV-R2 q>-;+ I5@2:dM= >ED62(&  +&'767632&#"&47>54'&#"&#"&47>54&'&7672N)HN'H呿u57/Z{^V{T+'F}}#+R {Qf|`0/W d jNPhpצFJy9 1 1:y7m1Jy'/y9 11 8{V-#'3VO;@8<M=M= >KJ<8.,"  +2'>54#"'46632&#"&47>54'&#"&#"&47>54&'&76727Dnc ;F^/+u57/Z{^V{T+'E}}#+R {Rf{ `//V d [;B # X)+N+GFJy9 1 1:y7m1Jy'/y9 11 8{V-#'DK@ 0@6bb   UQM = L >Y@KHED?>C'%$)$ +226?32#"&5463232>5'&#"&#"&47>54&'&4732u kPP;h-mB!,)F?VF@r ;B s??s ;!-R&}uE^3X=19%5˪9~d$)h{; 11 <{5{;2 -7EZK/PX@#bM= =M>@ bQM= >Y@ J&'$(#+7632#"&546323254'&#"&#"&47>54&'&7672fu57LNT%L0  )F('F}}#)R  9`0/W b FJ?F/)3m1Jy'/y9 11 8{V-#'%LT\ %8@5UM =M> $"     +!"7463!2"32# ! 5ǴVT!v%-#/: +T{h'3T '8@5UM=M>#!''   +!"5463!247632'""32654&6aqzǓPu=sLZ%%-#/˅sւs EBTLTq '?@<dU M =N>&$  "" +#"&'33267"32# ! js : }TP ǴVT!vqh}k9DC:: +T{h'3T) (6@3 :UM=M>$"((&&%+267#"&'47632'""32654& |}7amqzǓPu=sLZ)RfbV˅sւs EBTLT*8lK1PX@"ddM =M>@*ddddM =M>Y@ 751/&$* *%&&$ +#"&54?632#"54?62"32# !  y!)+ p)3TǴVT!vV 1  -y: +T{h'3T9,9nK1PX@%b =M=M>@+b` =M=M>Y@.-53-9.9&%%&& +"&54762#"7476247632'""32654&-#/   )!-5qzǓPu=sLZ / +*˅sւs EBTV+CK&PX@,'5A<K(PX@,'5A <@,'5A K(PX@1  ZUM =M= N  >@5Z  ZUUM= N  >YY@?<:842+)$" CB  +%32654&+"$!;!.#!"326?4732#"5'.+7!2!;));HZZ7 iJJ3) -/ .1J=675?B$-/FuLAX)1= '@/+D F!.5K@H <   SM=M=M>///5/5$$$$"%&" +>32!3267'"'#"&5463232654&#"%&"F7d=ZV!B-{ly׺則\Xbw!GPr#Rfͺ,`R)7/`¸xra E@ 7 <:K(PX@0d  U M =K = M  >K-PX@5dI  U M =K = M  >@6dU  U M =K = M  >YY@ ED@>;:0.-*'&!   $ +'"547"3265&#"&47>54&'&47326323#"'.#X'9bF@rTR s??s G#3yAm_/V? 9p`w)Aw+{; 11 <{5{;2 %=fRNPd)%-b >fK!PX@(bM =M=L >@&bUM=L >Y@ J"$*$!+632'"&5477>32#"'&#"&#"&47>54&'&7672 ?2'  )yA;@;%&$!E17q iZd4/V c ^/' %s Fd?#'@0dJ?{7 11 :{V-#'17M@ ? <9K(PX@1  e  U M =K = M  >K-PX@6  eI  U M =K = M  >@7  eU  U M =K = M  >YY@"MLHFCB8652/.)(%! +2&'>54'&546"3265&#"&47>54&'&47326323#"'.#{9Fy`HR@V:!bF@rTR s??s G#3yAm_/V? 9p`wT`PVg!>/B%3F+{; 11 <{5{;2 %=fRNPd)%-1B@@=9beM=L >@?541-#! +2&5>54'&5467>32#"'&#"&#"&47>54&'&76729FyaHR@V:| )yA;@;%&$!E17q iZd4/V c T`PVg!>/B%31 Fd?#'@0dJ?{7 11 :{V-#' F@8 <:K(PX@1 d  U M =K = M  >K-PX@6 dI  U M =K = M  >@7 dU  U M =K = M  >YY@" FEA?<;1/.+('"!    +&'767"3265&#"&47>54&'&47326323#"'.#J!{^uffgbF@rTR s??s G#3yAm_/V? 9p`w\g)5HR+)Nu+{; 11 <{5{;2 %=fRNPd)%-+ ;C@@:dbM=L >98.-*&  +&'7677>32#"'&#"&#"&47>54&'&7672fN)HN'He )yA;@;%&$!E17q iZd4/V c jNPhp Fd?#'@0dJ?{7 11 :{V-#'7 Bi@ + <:K!PX@dM = =M>@"dbM =M>Y@ A?0.)('%,$ +'"547'.#"#"&#&'6!2654.'.54632BX'9 *'DkGY04Gjf5hXla: #@'{$.X63Mqn:u)Awӎz:RQ)W1X8::X}Na7C/ ݁{:^95=[}M4bb4B@3@1ddbM=M =M>Y@ $$"+%*# +6232654&'.54632"'.#"#"'&#"&632'"&547b , !^bBlT}{fu/bm . /@/7Y]pDjg1dVa ?1'  cJEDR33wm`ml =R# G@bM =M>Y@ EC42-,+) +&'&'676'.#"#"&#&'6!2654.'.54632Zy T`{T{%W *'DkGY04Gjf5hXla: #@'{$.X63Mqn:umZ! =@F7?pz:RQ)W1X8::X}Na7C/ ݁{:^95=[}M4bH4BH@E3"+%*#+6232654&'.54632"'.#"#"'&#"&&'&'676b , !^bBlT}{fu/bm . /@/7Y]pDjg1dV@Lw\sbH$ cJEDR33wm`ml =R# GJ H <;K!PX@+  U  QM = =M>@.b  U  QM =M>Y@MKGEA?=<+) PP +"'7.#&'6!2654.'.54632'.#"632#"'47726565( JJ; #@'{$.X63Mqn:u *'DkGY04Gjf5hXa,?R}^HPN)/9 + ݁{:^95=[}M4z:RQ)W1X8::X}Na7=uF9BV#!+'GbfLh@eK .C:8JHFE#$$.%*# +6232654&'.54632"'.#"632#"'4772654"'7&'&#"&b , !^bBlT}{fu/bm . /@/7Y]p;``1,?R}^HON)/9C5' JGF cJEDR33wm`ml =R# G@#dbM =M>Y@B@1/*)(&  +&'767'.#"#"&#&'6!2654.'.54632J!{^uffg *'DkGY04Gjf5hXla: #@'{$.X63Mqn:u\g)5HR+)Nuz:RQ)W1X8::X}Na7C/ ݁{:^95=[}M4b)4?U@R3<=<:87:dbM=M =M>555?5?"+%*# +6232654&'.54632"'.#"#"'&#"&&'767b , !^bBlT}{fu/bm . /@/7Y]pDjg1dVN)HN'G cJEDR33wm`ml =R# G@2 U  U  QM = K  >Y@$EC?=97530/*'%$  HH +"'7"&47>54&+""'6743!3 72#.+"&'632#"'4772656y5' RFF+6dj-) .y-5)E  G3?R}^HON)/9 1 <{dgbw y s`hi{; 1F9BV#!+'G3fhB@0< : <;K PX@0dbX  V QM>@1db`  V QM>Y@?=9731,+*(%#  BB +"'7&'#"=4;4.5476767232'#327632#"'4772654X5' Jy %^-$"" 1'^Jd-?S}_HON)/: i )Nf RO )dY:!qxF9BV#!+'G ;@'2%<:K&PX@) d M = M =K >@' d U M =K >Y@9643/.-*)($#! +&'767&#"&47>54&+""'6743!3 72#.+"7J!{^uffg>E  OVF+6dj-) .y-5)\g)5HR+)Nug{; 11 <{dgbw y s`hi3;\@Y<bbM = M =M>86420/.,)'$";;  +2'>54#"54634.5476767232'#327#"5#"=4!7Dob ;F^/b^-$"" 1'^Jyy [;B # X)+N+Nf RO )dY:!i )9@@ $/"@+ U  U M  =K >Y@@>;96310,+13##C+%&#"&47>5#"546;4&+""'6743!3 72#.+"!2#!E  OVF+6dj-) .y-5){; 11 <{#-dgbw y s`hi$'h:V@S<db  U M =M>7542/-+)'&%#  :: +34.5476767232'#32+327#"5#"746;5#"=4X^-$"" 1'^Jyyhy oNf RO )#dY:!@$ ) ?bX@ @'U V  UM>Y@WVSPMLEC84&$ +2676#".'&#"'622>54.'&47327#".54&'&4727N#)!) #?/'''/u%)4I2/ddN- 3:C15d9-qњZX;c{B+? '-6 '9Vo-`@\xR?D' 2  1 9u-׮Z(eϕRy=2  1 ;-)Xd@a<  bU V M =N>VUDCB?<;53-,+(XX +2676#".'&#"'632"&54&'&4732723276'4&'&473272'&'&#(!) #?0'''/u%)4{3LF3d@\q!1N F38^ N+? '-5 &Vy`>1  =yZb3b@1  =wP= % ?F2 z ?Z FbK(PX@ UK =M>@UUM>Y@EDA>;:31&"  +!"5463!22>54.'&47327#".54&'&472752/ddN- 3:C15d9-qњZX;c{B%-#/;Vo-`@\xR?D' 2  1 9u-׮Z(eϕRy=2  1 ;-) FK@H  b UM=N > DC210-*)#! F F  +!"5463!2"&54&'&4732723276'4&'&473272'&'&6 Í{3LF3d@\q!1N F38^ N%%-#/ǝy`>1  =yZb3b@1  =wP= % ?F2 z ?o HoK(PX@& dU K =M>@$ dU VM>Y@GFC@=<53($ "" +#"&'332672>54.'&47327#".54&'&4727js: }TP 2/ddN- 3:C15d9-qњZX;c{Boh}k9DC:Vo-`@\xR?D' 2  1 9u-׮Z(eϕRy=2  1 ;-)+ GK@H :  bUM=N > ED321.+*$" GG% +267#"&'"&54&'&4732723276'4&'&473272'&'&!{}7`mӍ{3LF3d@\q!1N F38^ N+RfbVڝy`>1  =yZb3b@1  =wP= % ?F2 z ?w OK(PX@* UM = K =M>K*PX@( U UM =M>@& UU UM>YY@NMJGDC<:/+   +"2654&462#"2>54.'&47327#".54&'&4727/46Z55mmkIH2/ddN- 3:C15d9-qњZX;c{B5=/-@@-1;igGFhVo-`@\xR?D' 2  1 9u-׮Z(eϕRy=2  1 ;-)X OY@V  bU M =M =N >ML;:9632,*$#"OO    +"2654&462#""&54&'&4732723276'4&'&473272'&'&/46Z55mmkIH{3LF3d@\q!1N F38^ N>/-@@-11  =yZb3b@1  =wP= % ?F2 z ?YK(PX@#dd K =N>K1PX@!dd UN>@)dddd UN>YY@ XW3+N(%&&$ +#"&54?632#"74?622>54.'&47327#".54&'&4727 z)+ o(2P2/ddN- 3:C15d9-qњZX;c{BV 1  -Vo-`@\xR?D' 2  1 9u-׮Z(eϕRy=2  1 ;-)9XK1PX@0b  b =M =N >@6b`  b =M =N >Y@VUDCB?<;53-,+(XX%&& +"&54762#"74762"&54&'&4732723276'4&'&473272'&'&b#/   )!-{3LF3d@\q!1N F38^ N / +*ݝy`>1  =yZb3b@1  =wP= % ?F2 z f?-Ko@ @$bURM>Y@ .N(3$! +"'47'".54&'&47272>54.'&4732732>76VANZX;c{B2/ddN- 3:C15d9^sag1s (eϕRy=2  1 ;{Vo-`@\xR?D' 2  1 9u-HlrD`-f)sJK@H <b   b QM=N>JIFD3&9"! +"5467&'&'"&54&'&4732723276'4&'&47327232>76VHI }{3LF3d@\q!1N F38^ fDGBf1E50% {y`>1  =yZb3b@1  =wP= % 'H`w ^k :K(PX@b K  =>@b  U>Y@WVSOLK@?<8540.+)&%!  +&'&'67664.'&47327'&#""'.'&47327674/&'&47327Zy T`{T{%'=;w NKd9 /HwHf  ?7+  /)JX -wmZ! =@F7?2  1 =;5893R78TP"2  1  2wX)-s2  1 -  F X6@3F< :K=>VRJHDC?>;743$  +&'&'67667'.'&4732767>.'&47327"' #"'.'.7327Lw\sbG$G  %0#{9R{P  <7 {VBQ  HH>!:G  jPT{ F{L^s7s@UK >Y@@=:90/,)&% +&'&'676&#"&47>54&'.'&472776&'&4727Zy T`{T{%^FPV F2HF { U& $H or  JO) wmZ! =@F7?l{; 11 <{78/U0 0  1 *B]>*2  1 3JF*?3%H F$@! :=N>=:L!$/+&'&'676#"&54632327676'.'&4732776&'&4727TLw\sbG%Ts)73' #F!16';?RTPP"<> {R F?!+H{L^s7sh+!=;dXF{[61  2?@V:81%1  27Dfd PcK(PX@#U K  =K >@!U  UK >Y@FC@?653C +462"&%462"&&#"&47>54&'.'&472776&'&4727f:N97R8[9N99N9FPV F2HF { U& $H or  JO) )99)'99')99)'99{; 11 <{78/U0 0  1 *B]>*2  1 3JF*?3T/9@.<20:K PX@(dbXM =N >K(PX@)db`M =N >@'db`UN >YY@ '8d9@+&#!"54767654#"'673!26327%>72'"547h7'Pq!"# ˨#k1?3PqL'%*SX'9kl @du %TFE )AwL;b 2@ /!@-ddb=M=N >Y@22%#$v$! +632'"&5473!2>32!>?2%"547'674 ?2' 7Bw9)%LS)'*/3s3#!^/' %=PCV< |Kn Tj7@6K(PX@,b`UM =N >@*b`UUN >YY@ 8d9A+462"&#!"54767654#"'673!26327%>72@V??V7'Pq!"# ˨#k1?3PqL'%*V??V?fkl @du %TFE L;- 0@ -@+bU=M=N >Y@ 0 0%#$s$" +4632#"&3!2>32!>?2%"547'674RF$'K(PX@*db`M =N >@(db`UN >YY@8730("  +&'767&#!"54767654#"'673!26327%>72=J {^ugff7'Pq!"# ˨#k1?3PqL'%*\g)5HR+)Nuikl @du %TFE L;' /V@S,<:db=M=N > / /)'"  +&'7673!2>32!>?2%"547'674N)HN'H7Bw9)%LS)'*/3s3#!jNPhp=PCV< |Kn 1-n@(bM=M=K >Y@ "%##5C+%&#"&47>54&+"'543254632#"'&#"`6l B+V/ Z /TyfD5#%N7N@}9 11 <{ )b>///TP  @@4@-b  V=M=M>Y@><97#$$"$$ +32654&#"632#"'&"'65#"546;54.'&767232+d#NE_dXfx<!}m-7 i7`㷓\2!'j$ BA&*R;# -1<G@ (@(b U UM >Y@22EC@>2<2;75-*'! 11 +"#"&546323263 #"&#"'5437>5432654&#3 54&#"/A++ .-+#oO71oӑhD=D'CgמǍ'hXLN/<%$-bP)ZHsC'3S5)_덁L->z%@%ZUUM  >Y@0.,)">; # +32>54.#".7>54&'.73!7'.#!"32#"&;XsN DbB  s?@r  ;!/%`hJdiJ$\FP19.8``63bh@V1 <{5{;2 ?d!=JXP+^-5#  +@   <9K1PX@(bM=M=M>@&bUM=M>Y@ (% + +$! +%2654&#"!&#!632#"''674&/"/691c`}TT@9h묁T 9+#A o=ᷚVqG7 /pR/ /!%LD58@5.<U =N >32-( # +32>54.#4&'&767232#"&#'54?>5;XsN DbAN!!)diJ$\FP17=29.8``63bh@ZP##\R!=JXP+^-5#/+- '7@4  <=M=M>$%$!+%32654&#"2632#"'"'654&'&76?FPc}Tf}X1BM!!T涚VRjoI8 JXZP##HD;@8 <bM =M>  + #"$'&'6!2764#""'P;Trg'hXzǏlD^Z  wiwL,T@Q* <b`UM =M>&$ ,, +2#"#'!"32672! 47632>R'#gF3'hz ǏlrgHHd 1H<= Xiwt_#vL-R@O%<bbUM=M>)'$" -- +"&#"#"'.#"327#"&5432>32H9#J@$FA:fri! HgmL(=/'%m95P+0J-3/¦ׇwW MN-!&D->fK(PX@"U M  =M >@  UUM >Y@:9641/,+(">=#"(" +%32>54.#"32+263 #"&#"&47>5#"546;4&'&47TuT=힬X!R'}w{j s??s 73/nі}vd#i;1 <{R${;2 - <W<@bUM >Y@ ;5/'" &" +%3 4.#"4?>54&#"54>323263 #"&#"'%P!'X=74C18N'L#CQa+#H'}v{D5'bOX)/6*83<!$T%VX9i;VXD 2@ <:K(PX@&ZUN =M >K-PX@$ZUUM >@*ZZUUM >YY@ /-)#!  2 2#! +%# 326#"'3!7#2&#"#"$54$!34&fDRm%5f>3&+>5DD!%2'Ϭ)pX:351+ 15Z B@=<"<;>:KPX@.=M=M=M=M>K1PX@,U=M=M=M>@*UU=M=M>YY@A?:731/-(&$#%% +%4&'&"32762'&7&"#"&5476323274&+"'!6 ?g{C;[Nr!7^L jI5Hb5\}ET/'JfTbR`P= % =H0 {ۍ{X4 F//@,<bM=M>)"$-&+%'$4&#"#"&54632254/.54$32#/RX6eRv%y2907b1 fLwRB`]mf;'%Zj{3rm}7D+:@:9 -(!@3 ZZ  UUM=L >Y@ 74$5$"4242 +!%+";2%#!73!2'!"+'64'4;23!4!"' ;92k;36h^PJ1- /- *3IJh 7)+-2\-H }C +/@s>1)BLVD5@2<SM =M>$&+'> !".'!65#" 67/&$ 44 +2.#";2+"3267#".5467.54$\R''@!t)@jD##Ɩy">#aXZy=N1D: {!EN:&)'wD!Lk! 'Dl@11A@#+'K(PX@4Z   bU  RN =M>@2Z   bUU  RM>YY@A?=;%$&$"6D +74&/&=73!272.+"!26?4;#"/.#!#"&5463232-<=C6*;'ZjJ3/---5qf^)Z5B(/) )v yfFR)-4mLFl4-!=RP=1+7%%u;KPX@0b ZM=M= N>@1b  bM=M= N>YY@ 64$&#$"$#3 +#"=4;27>32#"'&#"32'#'"&5463232>7b /Ibaoj#H5)1 /U@q(1Nb$53F' )nW5!+4h%}d )!\DP#,-%x=TLEa@^C5!<b``  U M =N>?=86,(  EE +2#"&#":'.#"3274&'.7327! 4$32>\'#g N'# 5`kmQG` ITc 1- ;Bw>d 1H kNjk7N99" 5  5 +4pP#t%- @d@50)@bUM>Y@ >'#3$+326546&/"=737##"&54>7.'&=7373T)?9!-t&N%OD?wHdLN-6=}D>P#7+;)=PR3 /# ''%6ՇjZ91lqE7&'' yfGK@H$<=M=M= =M>CA><9842/-#" GG +"&#"&47>54.'&7672767>3272654'&54632# 4&bsz#+:  8 3-& -7 hXTߖoyV-'Ɖ9y&0WI 1  1  'DBBA&*R3`FE-{(HuH)DW@ @U=N>Y$&2+4&#'"5'>72327#"5!B ?&'?J w^B 1#RodY:B-1[K(PX@! UK =K >@U UK >Y@ 1/#C##C +%&#"&47>5#"746;4&'&4732732+@r PR s??s GV s?{; 11 <{#N{;2  1 ;{$PDZC @0  b` U M =M >Y@WURQMK@?<854/.+'$#  +3&#""'&'&'&#"&47>54&'&4732767>32".#"/s  )-+ Q C:p@r PR s??s GV s?f<Z/LK+b3>)1%>V@Փ  ! 1 7WJ{; 11 <{5{;2  1 ;{Hv3Fc;!/3<3-cM!P*@1bbM=K= M >Y@NMJFCB?>73C"%" +4632#"'&#"6767>&'&47327&""'&/&'&'&#"&47>5yfD5#%N8N?BIJZ %)1 H%L? {ZR J!!=5mB+ V/>///TP 67q $1  2L / 1 +e, }9 11 <{,=@:#<b V=K >,+$##C +&#"&47>5#"546;4&'&767232#d4`]V `4)[ ii{; 11 <{#q1&*R7$-1'7@4'   <9M=M>$/%%+327#"&'''&''7&'"&'672#?x~{@=_{Xr>w4 -`_@YS>8"K. @UN>Y@ J+L*K## +%#7'"&'"&54&/&=7327327&54&/&=73273274&/&=7327!!b9/XtÅf1>773+?11>--3+=3RC}/@--=6=3L' 0'/eLe-$''%+l7g-"''%+p`^-"'''++B@ @/b`   bU  Q>Y@B@=;($334$# +7.#"757!3254&+"=737+"#&'&"#"&5463232NB 3 5:5 I778+353 1R7D^+*!%g%yJN1L''J3;)Dd3XfZ\)B"+1!-!L?9@6/*)"<bM= =>?>:+;"+3>32&#'54?>54&#"&#'54?>54&/672f`]f'3+[5';R'6W)5(_XjLy-' '' %+qb-"  '' '-H3 1 LTD 0@-SM =M>$$$ +# ! #"!32TVT!vLsH~ {h'3 (AސP8LT) ,>@;+<dbM =M>'%   +"32# !232654&'.5432ǴVT!$Q)1/^9T: +T{h'3N)-7#PqIT} -:@7<dbM=M>"!)'!-"-))"#+4763232>54'.5432'""32654&Tqzy\./:11^9JH@u=sLZ˅1 )'5#Pk^ ւs EBTJ7D 9@1 -# @(  UP M  =M>Y@65420/,*&$    +"327&&#".7>54&#"! !26723272{^}'?s ;B s?+c;rE'ZLq{; 1 2 ;{d)!Jh'-H@'bcJ! 6B@?%$ <UM =M=L>64"$$&C&! +&#"327&#"&47>54.#"#"5432326;Ty;^a1u;91c49 b20--1;HVyG?V "ȤoT'-{: 11 8{+)1K'!N  7 EP@M<Z`UM  =K > ?=31*)&" EE$" +32654&#"%32632#"'&#"&47>54.#"#"&54>%w-^'1hDžf@NZBs? '#&Ef 5&%9'Lo" 7Eqh6HOy; 11 <{5=H'/$ &007*YQ4!G~KPX@1  Z M=M=M=K>@2  b M=M=M=K>Y@ FD%%C%&&' +32>54&#"'7>32'"'&&#"&47>54632#"'.#"f! 0aRw=g\D<ЗydN 7q H9`4{>B95$+7-#76(+Dt{FӾDMs{: 11 <{,175B;%#-+<F@/*B  @, U U  UQK >Y@ECA?96334333" +3#"'.#;2&#'54;2654+"=73%+"6324&#"326]Tm#3b >3>5R7=2o7P5q+qbNsdNfu) /)''+- Z''Z4 H^{gZD0X/.@M = = =M>Y+!!,"+>3232"&#"#"&54>7$'4&#"'6oLiLqrxF=#9v..*#+""+&#"#".5467>54'"'6763232767Ll)biCTA4B;1f{by\u=d<= 7kHq6G=)T;^s3fje73cDFS;B&J+%N @UK >Y@ %% +!!2676!&67677&'&47!'.17ml#'"q1B!#3L ƒs / #!D 0wF=PN",e@"bUQM>Y@ #*#"&"+4632327672#"&'.547#"&%4&"326}^uq+.$?gP!d!//3/du`GHJ=;LZm@55;H%?'+qK!mX7XVrTT3h<@$68 @*db eM=M>Y@5431.,)'#" << +"&54>76545#"5#"=4;4.5476767232'#327P!)<^7ILy %^-$"" 1'^J")-!) !X $i )Nf RO )dY:!'!"{9;@*b` U =K >Y@430,)(! ;; +#""&54>3! 72#.+"&#"&47>54&ꛫ 5J9'KT.x-5) 4@E  OV F@3+V$ &007*XR4 rahiBE'  1  1  'EBef3.|KPX@0ZbM=M =M>@1bbM=M =M>Y@ .,""####%" +4632#"'.#"32'#327#"5#"=4;{=B:5%+7-1'^Jyy %^,175B;%t )dY:!i )90t@ '@%b UQM >Y@ .+14%#! +%326762# 4&+""'6743!3 72#.+"p/'#5=EF+6dj-) .y-5)jy'@H9!=3/}dgbw y s`hi )EMK(PX@dM =M>@dUM>Y@ (>(C(+#".54&'&473272>54.'&4732654'&746321#-qњP^;c'L{B2/jdN- 56EL)F7-m_Jb-׮Z+fӓLy=2  1 ;{Vo-f@\xRBC% 2 ! D%+@W-)}IR@O&6<%;bUM=N >GF1/,*$! II +"&54&'&4732723276'4&'&473275>54'.5432'&'&{3LF3d@\q!1N F7811^9H>=8^ Ny`>1  =yZb3b@1 #)3#PkI-Rv3wP= % ?F2 z1)1KPX@ ZM =M>K&PX@!bM =M>@bUM>YY@)' 11 +"#7323254&546;#.#"! 54>54RA 5Ŷkjv5AT^ViVXhX+PN1f%Ӂ176L/-^J\Ϗ-D:q @'bUM =M>Y@ '$$&&3#2 +4+"=730%+"324.#"#"&54632#".n8P 5q=a %\D; '+JAifjJZ''ZfhFXF=Չ1!'!"H@c (c#FA@% A-:5@'bUM =K >Y@ 336338$%$ +.#"#"&54632654&+"=737+";2&#'54;2';@V;9> eX> +-55<5P5q{hk}3+# J+9s' ))'39/+''Z)u;F@C"*<ZbUK=M>"#'#3'"$& +#"&5463272>'.#"=737">32#"'&'"Xǣ)HV3'P/+#z#13Z=X Z\/!7 %5=%HtJSm5/1)'#-!=VX;))%-a0:tPT9@@? <:K PX@.b  Z UM =N >K(PX@/b  b UM =N >@-b  bU UN >YY@ >=5#%d5#%@ +&#!"5476!"7463!7654#"'673!2632!2#!7%>72h7')߇Pq!"# ˨#k1<3PqL'%*7$6kl @# %TFE L;6K@H3<bV  =M=M >66"#%#"#$q +3!2>3232+!>?2%"54767#"546;67'6747Bw9)%WodLS)'*/zwzVs3#!=$}CV< |#n VPV)@ & K&PX@"b`QM >@(b`UIMAYY@#! )) +!2#".54327254.#"!'V9ZѬZ^-jLy{97dn 5+ )!3'-b@fH's`/Z+^PV(@ # K&PX@"b`QM >@(b`UIMAYY@ (' +267.'!&#"32762#"547&543}+5od79{wKk-Z!7)+\-/`sܡHf?c-A״ 'j9/+-&N@K$! <:b`M=M> &% +257.#!&#"327632#"&5467&'43+-B7P7A?9y^u1#H;1o$ uP_9@5^-Z'723@0)#<b`M=>6"./+#"&5467>54.#"!"'73!23VT\P1,ZfW-))V=:D.)!!/!Ru?% 312 %3'%PhB !iV/PV12`#"jL==@:6<bUUK >)%#$F##%" +463232+!2676&#!46?#"7463!>54&#"#"&}ѠajL~m7>'-^B]SSIlFfdC!1fhf#y`VobR$W`sN/ %3.-:@7 <bSUM>"$$!"+4&#"'!!72#"&54632326qm`=-o!h^)3!-HuqnwX:$9TTo!<@9 <bUK=M>#$$1"+4&#"7'!!632#"&54632326`^`Fyqo##`)/#L/`ybqw [7$=!/w)89@6%< :M=M =M>$"+#% +#"=47>=732'##"'&#"732654.'.5gT>t/9RyIof+wP 7D`3? JNFAN?p{') )c?F1tHm7#D'XE78J!">O8+#+@(9eM> +"4&/6723632'&?65q;shXS˞#R+J\#G)H5 1 J3 b@=> +#3OO@=>+#3#3PPOOV3T6@3 S=K=> +###535#5333#TOOFdFF'FD@M =M>+2"'.'.54462"VA 2 )@Z??ZDJL;qA`^) F?RL1Z@@Z? .9i@PN h <76421:K PX@Ed   b XM  =M  = N =M >K(PX@Fd   b  `M  =M  = N =M >@>d   b  `I  U N =M >YY@"//gfb_WQMLJG>:/9/9*)$# .-(" +%32>54.#"'263 #"&#"&47>54&'&47%&'767&#!"54767654#"'673!26327%>72TuT=힬X!R'}w{j s??s J!{^uffg7'Pq!"# ˧#j 1?3PqK'#*73/nі}vdi;1 <{5{;2 j\g)5HR+)Nuikl @du %TFE -.9^@76214[ M @9  b b U = M=N  >Y@&:://:^:^XVQOLKHFB;/9/9*)$# .-(" +%32>54.#"'263 #"&#"&47>54&'&47&'7673!2>32!>?2%"'47'674TuT=힬X!R'}w{j s??s N)HN'H7Bw9)%LT)'*/3s3#!73/nі}vdi;1 <{5{;2 jNPhp=PCV< |Kn P=Hm@FECA@,lj \ @Kb  bU=M = M=  N = N>Y@ II>>ImImge`^[ZWUQJ>H>H=<"%$%%+%4&'&"3276#"&5476323274.'&7672'&'&"&'7673!2>32!>?2%"547'676 ?g{D;ZPq!jJ5-8 i8^ NN(IN'H7Bv9)%LT)'+/3s3# /'JfTbd{ۍ{BA&*R`P= % ?H0 jNPhp=PCV< |Kn -'Nɵ#K(PX@0ZbR K  =K  >@.Zb  URK  >YY@MLIEBA;9751/ '% +!"&47>54&'&47327;2676&##"&5463232>54&'&473271j s??s LR s?7>## !7%J5-f-mB98)F?EOZ b;1 <{5{;2  1 ;{NA/hs9'X=19465˪{;2  1 =B7'3W#K(PX@>Z b QM =M ==K  >@:Z bU QM ==K  >YY@USLJFD<;20,* '% +!"&47>54&'&47327;2676&#4632#"&4&'&7672'"&546327251j s??s LR s?7>## !7F%';A)%>/V ULNT%L0  )F(1 <{5{;2  1 ;{NA%=C'#@BV.#"fP@F/)2%7 /LԵ:K/PX@8b  bU==  L =M>@5b  bUQ==  L >YY@KJGC'$($" +4632#"&4&'&7672#"&54632325%4&'&7672&#"&47>F%';B)%=/V ULNT%L/ (F))[ i4`]V `4%=D&#@DV-#"gP?F/)3^q1&*RP{; 11 <-<c0KPX@6b` Z  R K  = =>K(PX@7b`  b  R K  = =>@8b`  b I  R = M>YYY@ba^ZWVPN$)"J#(H +4.'.7327#"'&"&#".7>5.'&47327#"&5463232>54&'&47327s 15?  /=s?/-%53 15? 75  s? Z> X %J5-f-mB98)F?EOZ b;HK+ 2  1 A?1DdHK+  11 BV-R2 q>hs9'X=19465˪{;2  1 =7#-<HlT0K(PX@J  b  b   bK =K = = == M  >K/PX@E  b  b   bII = =M= M  >@B  b  b   bII Q = =M>YYY@jha_[YQP$$"J#(H +4.'.7327#"'&"&#".7>5.'&473274632#"&4&'&7672#"&54632325s 15?  /=s?/-%53 15? 75  s? Z> X F%';B)%=/V VKNT%L/ (F)HK+ 2  1 A?1DdHK+  11 BV-R2 q>P%=D&#@DV-#"gP?F/)3-7->JnTK/PX@-   bUM= = M>@*   bU  QM= >YY@ljca($'J*J! +632&#"&47>54'&#"&#"&47>54&'&76724632#"&4&'&7672#"&54632325yu57/Z{^V{T+'F}}#+R {Qf|`0/W d E%';A)%=/V VLNT%L0  )F(ݦFJy9 1 1:y7m1Jy'/y9 11 8{V-#' %=D&#@DV-#"gP?F/)3 Hy:KPX@%d UM =K >@#dS UK >Y@ FD:951('    +&'767!#&#"&47>727&#"&47>&'!"J!{^uffgj 2nLk NZ%-D?63 [u!wP%\g)5HR+)Nu- ^  11 4XJ)l/1  11   [J+ Cd@a= ?<: db`M=M=M>B@<:42/-%# +&'76732765## 546?65#"#"7463 327'"&N)HN'HoKHFn)op D`8)H5 %'NuF9jNPhp+17[[m Vf'+ ++ /JV+1:!(L=B ,c:K(PX@dK =K >@dUK >Y@*)&"  +&'767&#"&47>54&'&47327J!{^uffg5@r PR s??s GV s?\g)5HR+)Nug{; 11 <{5{;2  1 ;{% '(@%:d= >$#  +&'767&#"&47>54&'&7672N)HN'H61b \^b2/V ZjNPhp{9 11 8{V-#"fLT $;@8:dM =M> #!    +&'767"32# ! J!{^uffgNǴVT!v\g)5HR+)Nu: +T{h'3T) &;@8:dM=M>" &&  +&'76747632'""32654&N)HN'H7qzǓPu=sLZjNPhp˅sւs EBT ? Ec:K(PX@dK =M>@dUM>Y@DC@=:920%!  +&'7672>54.'&47327#".54&'&4727J!{^uffgl2/ddN- 3:C15d9-qњZX;c{B\g)5HR+)NuVo-`@\xR?D' 2  1 9u-׮Z(eϕRy=2  1 ;-)) EN@K: dbM=N > CB10/,)("  E E +&'767"&54&'&4732723276'4&'&473272'&'&N)HN'H}{3LF3d@\q!1N F38^ NjNPhpy`>1  =yZb3b@1  =wP= % ?F2 z?8@HTK(PX@+   U UM =M >@)   U UUM >Y@"KIQNITKTHGDC@?<;32/) 88 +".54&'.732722>54&'&632327462"6462"!"7463!2=xdB;b ]Hf1023f{H+7 XLzb59N::N9N::Nu5FjvLy=2 ?T^\a3fYhbN 4 1 ;{-N99N99N99N9%.#0-) ZKPX@6 bUM= M = N>@4 bUU M = N>Y@&! XWFEDA>=75/.-* Z!Z   +!"5463!2462"&%462"&"&54&'&4732723276'4&'&473272'&'&6 9N97R7Z9N::N9{3LF3d@\q!1N F38^ NR%-"0)99)'::')99)'::yy`>1  =yZb3b@1  =wP= % ?F2 z?8@HRKI:K(PX@*  b UM =M >@(  b UUM >Y@OMHGDC@?<;32/) 88 +".54&'.732722>54&'&632327462"6462"'"747=xdB;b ]Hf1023f{H+7 XLzb59N::N9N::N>D,FjvLy=2 ?T^\a3fYhbN 4 1 ;{-N99N99N99N9$:i-) !\K PX@9Xb bV M = N>@8db bV M = N>Y@#"ZYHGFC@?9710/,"\#\$!+632#"&547462"&%462"&"&54&'&4732723276'4&'&473272'&'& 3' 9N97R7Z9N::N9{3LF3d@\q!1N F38^ N*# ")99)'::')99)'::yy`>1  =yZb3b@1  =wP= % ?F2 z?8@HSQPNLK:K(PX@+   b UM =M >@)   b UUM >Y@ IIISISHGDC@?<;32/) 88 +".54&'.732722>54&'&632327462"6462"'&'767=xdB;b ]Hf1023f{H+7 XLzb59N::N9N::N9f_HYPNPFjvLy=2 ?T^\a3fYhbN 4 1 ;{-N99N99N99N9P[$/?H&$Dg-)/ Y`@]: d  bU M =N> WVEDC@=<64.-,)Y Y  +&'767462"&%462"&"&54&'&4732723276'4&'&473272'&'&>g!t9>s n:9N97R7Z9N::N9{3LF3d@\q!1N F38^ N'r~aEG_f)99)'::')99)'::yy`>1  =yZb3b@1  =wP= % ?F2 z?8@HSRI:K(PX@*  b UM =M >@(  b UUM >Y@PNHGDC@?<;32/) 88 +".54&'.732722>54&'&632327462"6462"#"/6=xdB;b ]Hf1023f{H+7 XLzb59N::N9N::N +0FjvLy=2 ?T^\a3fYhbN 4 1 ;{-N99N99N99N9 r5-) !\W@Tdb bV M = N>#"ZYHGFC@?9710/,"\#\$%+#"/7672462"&%462"&"&54&'&4732723276'4&'&473272'&'&s  "29 9N97R7Z9N::N9{3LF3d@\q!1N F38^ N %*)99)'::')99)'::yy`>1  =yZb3b@1  =wP= % ?F2 z+! !1@.bUM=M>#"%"# +"32>&'632# 434'&'"oeE5L>#o`'!BZVm-ub-nod#l2MYKPX@2  UU   UM = K >@0  UUS   U K >Y@ PNVSNYPYKI?>:6-,%!+462"$462"!#&#"&47>727&#"&47>&'!"!"5463!2@C  b  `U U M = M =M >Y@"WUQOIGDB:80.,+'%   +!"5463!2462"&%462"&32765## 546?65#"#"7463 327'"&6 9N97R7Z9N::N9TKHFn)op D`8)H5 %'NuF9T%-"0)99)'::')99)'::j+17[[m Vf'+ ++ /JV+1:!(L=sMUKPX@1 U  U   UM = K >@/ U  US   U K >Y@UTQPKI?>:6-,%!#!# +32+!#"546;!#&#"&47>727&#"&47>&'!"462")j 2nLk NZ%-D?63 [u!wP%?V@@V?()- ^  11 4XJ)l/1  11   [V@@V?J DP @ > @K-PX@Cb`  U M =M=M =M >@Ab`  U  UM=M =M >YY@OMIGCA=;530.&$  +!"5463!232765## 546?65#"#"7463 327'"&4632#"&6 MKHFn)op D`8)H5 %'NuF9F$'@FZ  ZU U  U S M = L >Y@&hgd_ZWUSONHGDB?<543/,+"    +!"7463!2!4'!&#"&47>76&'&47)272'.+"326762"'.+3!26&#!&47>R5D /#n L\-!8di5/!"'Vk#&}9 21 RTII'!3bJes@%-#/{)+1' 11 8T/A2 F{d3'f1[ {6% 70%X 1 <P/ EQZn@kD F/<  b U  U M=M = M >YWSRNL<:31,*('"     +!"7463!2&#"#"54632632327#".'&'"'&54676%&326767>54&#"5g+ZB\+%RǁPhTpi-bV1#}BdI8zgLVT1 dFd%%-#/--+ )\XyDXd9{Vh9-' $9=u!HL8IFSh=1J^3\LDB@ :@/b`TM  = M  >Y@=;7610)% BB +2'.#"3275#&5467354&'.73273#! 4$d+'# 5`kmQG` ITc 1-t ;D%1Njk7N#19" 5  5 +41%jpP! R_f6@JC]>K(PX@F b  UU  S M =M= M  >@L bZ  UU  S M =M= M  >YY@,SS eca`S_S_\WNLIG<;6531/.)(#!  R R%" +4&#"32"'&#"#"'376323##"&'#&7467367&5467&54632>32.'&"#"'!326f` %P='4 XˏqI%N5=R8RZDP@Ы4.&t^1)jэV%{-)5/T-9? g%-%;s>y;9TX/$!Z{+3D;: CJ\%izlw#gE/o7u"f917+!.5$2!# GWRGQQLT =Y@V5!<:db`M =N> 86,(  = = +&'7672'.#"3274&'.7327! 4$J {^ugfgd+'# 5`kmQG` ITc 1- ;\g)5HR+)NuO%1Njk7N99" 5  5 +4pPB5 )a!@Y1R8M<:KPX@A  d  bU M = M = M= M  >K(PX@?  d  b  UU M = M= M  >@E  d  b  Z  U U M = M= M  >YY@$+*][XVIGA?><75/-*a+a)$  +&'7674&#"3232>54&'&"#&"'&#"#"'37632#"&547&5467&54632>32N(IN'Hf` %P=?J`}-;? g%-Z'4 XˏqI%N5=R8RZ^1)jэV%{-)5/jNPhq+9TX/Lu\k2jF'5##7$!Z{+3D;: CJ\jQn{e/o7u"f917+!. [@- <:K(PX@' d  bK =M >@% d  b UM >Y@[ZUP<8*)&" +&'767&#"&47>54&'&4732767>.'.7327&#""'&'&'J!{^ufff@r PR s??s PN s?_CR!#$  OD 5@`.HC-' HQA;o\g)5HR+)Nug{; 11 <{5{;2  1 ;{Hh!+2  1  3/J#' 1 9UJL X\@Y2A<:bb=K=N >WVSOHG@;*&#" +&'7674.'&76726767>&'&47327&#""5&/&'&'&#"&47>JN)HN'H-7 hBIJZ %)^kT^ H%L? RV-RR!!='C  ^\T Z/'jOPipBA&*R6 67q $1  2L / 1 !o, y; 11 :LfTD+A@><bQM =M>! '% +!+*$$!+"547' ! 32>76"32BVMTT!vJkef1pǴuh'3iAvtF`: +TTf!.=@:<bQM=M>#"*(".#.+%3!+"547#"54763232>76"32654&VqzǓP=Q]Lf1=sLZo ˅sւ;Of=`8BTLfT +7W@T <b UQ M =M>-,31,7-7+*'%   +!"7463!2"547' ! 32>76"325VMTT!vJkef1pǴ%.#08uh'3iAvtF`: +TTf -:S@P <b UQ M=M>/.64.:/:-,)'   +!"7463!2"547#"54763232>76"32654&5VqzǓP=Q]Lf1=sLZ%%-#/Džo ˅sւ;Of=`8BTVP 4@1+<:KPX@+db`M =M>K&PX@(db`QM >@.db`UIMAYY@ .,*("  4 4 +&'767!2#".54327254.#"!'3J!{^uffg9ZѬZ^-jLy{97dn 5+ \g)5HR+)Nuj!3'-b@fH's`/Z+R%% 8@  8 <:K&PX@1 d  b  U=M=M>K1PX@5 d  b  U==M=M>@3 d  bU  U==M>YY@751/,*&$  +&'767!"'6737!26?22#"&5463232654&#"`N)HN'HXw553-A-ʜw!!DR7XfygjNPhpNl L9+F+P7-7#.c@ <,+)'&:K/PX@db=M>@dbQ>Y@ $$$.$.'$(+4&'&7672#"&54632325&'767/V VLMT%K/ )F(N)HN'HV-#"gP?F/)3vjNPhq 9/?^@.<:K PX@?bX M  =M  =N  = M  >K(PX@@b` M  =M  =N  = M  >@8b` I UN  = M  >YY@D@ZYTSPJ@^D](%8d9@ +&#!"54767654#"'673!26327%>72%32>54.#"'263 #"&#"&47>54&'&47 7'Pq!"# ˨#j 1?3PqL&#*TuT=힬X!R'}w{j s??s kl @du %TFE 73/nі}vdi;1 <{5{;2 -$4S@ !@0b  U =M= N  >Y@95ONIHE?5S9R31)'$$%#$q+3!2>32!>?2%"547'67432>54.#"'263 #"&#"&47>54&'&477Bw9)%LT)'*/3s3#!TuT=힬X!R'}w{j s??s =PCV< |Kn )73/nі}vdi;1 <{5{;2 P$5b@Q ! @B  b  U =M =M=N =N>Y@baPODB@>9731,*$$%#$q+3!2>32!>?2%"547'6744&'&"3276#"&5476323274.'&7672'&'&"j7Bw9)%LS)'*/3s3#!e ?g{D;ZPq!jJ5-8 i8^ N=PCV< |Kn {/'JfTbd{ۍ{BA&*R`P= % ?H0 LT <P@M4 <:db`M =N> 75+' < <$ +'"5472'.#"3274&'.7327! 4$oX'9d+'# 5`kmQG` ITc 1- ;)Aw%1Njk7N99" 5  5 +4pPBb ,do@\4U;P K!PX@F  b  b  U V = M =  M= M  >K(PX@Cd d  b  U V M =  M= M  >@Id d  b  Z  U V M = M= M  >YYY@.-`^[YLJDBA?:820-d.dW%%'$!+632'"&5474&#"3232>54&'&"#&"'&#"#"'37632#"&547&5467&54632>32D ?2' f` %P=?J`}-;? g%-Z'4 XˏqI%N5=R8RZ^1)jэV%{-)5/^/' %9TX/Lu\k2jF'5##7$!Z{+3D;: CJ\jQn{e/o7u"f917+!.#-U@943# @- U S =K = N  >Y@TROMEC9;5 +!&#'54?>54&/&=73%!4&/&'573%32654'&54632#"&d1@5R7=23<7P5=42=5P 5=3bNq}V-'׋/%'')+-$'''+O-$'''+p^^C-u1D ,6@3+&< 9eM >*)%"   +">54&>3 '&?654&#'"5'672DtwTWOoYFRyȮ)R!A cbud`jzd^ǤĎbRL3 b1^B 1(J G@ ;< :K PX@)dZ`K = =>K(PX@*db`K = =>@+db`I =M>YY@ "J#(L%+#"'%64.'.7327#"'&"&#".7>5.'&47327X 9@w 15?  /=s?/-%53 15? 75  s? Z> X  =HK+ 2  1 A?1DdHK+  11 BV-R2 q>-;b LRK!PX@b =M= >@ddM= >Y@ J*J"$%+#"/7632632&#"&47>54'&#"&#"&47>54&'&7672+  +?Fu57/Z{^V{T+'F}}#+R {Qf|`0/W d ^)/{FJy9 1 1:y7m1Jy'/y9 11 8{V-#'MXZ@W(+%<&: U UM =K >ONTSNXOXKI?>:6#! +!#&#"&47>7.546327&#"&47>&'!""2654&j 2nLk NZ%6GmG2YX'9-?63 [u!wP%1/35Z66D- ^  11 4X@\7Fh6,)Bv 0S/1  11   [X>/-??-1<J M[}@zGI< d  db  ` VM = M = M =M >WUQOLJFD><97/-%#!     +"2654&462#"32765## 546?65#"#"7463 327'"&632#"&547/35Z55lljJHKHFn)op D`8)H5 %'NuF9 3' =/-@@-1;hfHFh+17[[m Vf'+ ++ /JV+1:!(L={*# " g@6 GD K [ <4;:K(PX@DdZ  Z  U SN = M = L  >@BdZ  Z U  U S M = L  >Y@feb]XUSQMLFE#7CK$+'"547!4'!&#"&47>76&'&47)272'.+"326762"'.+3!26&#!&47>X'97D /#n L\-!8di5/!"'Vk#&}9 21 RTII'!3bJes@)Aw)+1' 11 8T/A2 F{d3'f1[ {6% 70%X 1 <P/b GS\@!F H1@=dd  b  U M=M = M >Y@[YUTPN><%""#(&$! +632'"&547&#"#"54632632327#".'&'"'&54676%&326767>54&#"B ?2' +ZB\+%RǁPhTpi-bV1#}BdI8zgLVT1 dFd^/' %--+ )\XyDXd9{Vh9-' $9=u!HL8IFSh=1J^3\LT #+3A@>-,*)#<:dM =M>%$0.$+%+)'$+'"547"&'7&!272#"'"&324#X'9*jT!e)fVި_Xnɴ)Aw)3}}%~{|՜-Ҥ: Nb &-5@&/.,+K!PX@-be ==M=N>@*dddeM=N>YY@('20'-(-('$! +632'"&5472'"'"&'7&547632"&32656P ?1' 1U{uoh lfqzǑf$JtxMZ^/' %/iւsY~|˅Fkdb$%\K1PX<K-PX@)ddS   V K >K1PX@-dddS   V K >@1ddddS   V K >YYY@ ZXNMIE<;40"!% %%&&" +4632#"/&%4632'"/&!#&#"&47>727&#"&47>&'!"+)!z  3'o j 2nLk NZ%-D?63 [u!wP%D0 %!-  - ^  11 4XJ)l/1  11   [J5 +Y@ S!UK(PX@=  b  b  U M = M =M >@A  b  b U  = M = M =M >YY@"XVRPJHEC;91/-,(&   +2#"/&'46%2#"/&54632765## 546?65#"#"7463 327'"&% +:#   +KHFn)op D`8)H5 %'NuF9) -   .+17[[m Vf'+ ++ /JV+1:!(L=o KKPX@1 bU   UM = K >@/ bUS   U K >Y@IG=<84+*# "" +>32#.#"!#&#"&47>727&#"&47>&'!"js9 ~TP j 2nLk NZ%-D?63 [u!wP%h}k9DC:- ^  11 4XJ)l/1  11   [J) E@ ? A@8b`UM=M =M >Y@ DB&#(("(% +."'>3232765## 546?65#"#"7463 327'"&|}7`m{KHFn)op D`8)H5 %'NuF9RfbV+17[[m Vf'+ ++ /JV+1:!(L=9a@W%"):K1PX@Ad d  Z   Z   VVM= L >@Edd d  Z   Z   VVM= L >YY@ ^[TSRNKJEDA<741/+*$#a a%&&"+4632#"/&%4632'"/&26762"'.+;26&#!"&47>54&'&473!272'.+"-+)!z  4)p }:1  1RT3@J' 2cHss??sd 50!#'VjP7L1 %!-  1[ bOJj 70%;H9 1 <{5{;2 F{d;HLBD +B@:b  b  U  =  =M= M>Y@ A?<:861/-,(&#!   +2#"/&546%2#"/&546%2'4&#"#"'&543 #%326% *:#   +sdF4L> #n`Z&AZVm7 ,   /ub-n,o#m19o P@F)@G  b  Z   ZU   UUM= L >Y@%MJCBA=:9430+&# PP "" +>32#.#"26762"'.+;26&#!"&47>54&'&473!272'.+"-ks9 }TP }:1  1RT3@J' 2cHss??sd 50!#'VjP7h}k9DC:\1[ bOJj 70%;H9 1 <{5{;2 F{d;HLB+ .@ @-bUUM=M>Y@ #"%"##% +."'>32%2'4&#"#"'&543 #%326{}8`msdF4L> #n`Z&AZVmRfbV+ub-n,o#m1B@K1PX<K1PX@!dd VK >@)dddd VL >YY@ >=CC%&&" +4632#"/&%4632#"/&&#"&47>54&'&47327+)z  3'p {@r PR s??s GV s?Z1 %!-  {; 11 <{5{;2  1 ;{{ =KPX@b== >K(PX@dd= >@ddd= >YY@:9+'   +2#"/&'46%2#"/&546&#"&47>54&'&7672'% +:#   +1b \^b2/V Zo -   /{9 11 8{V-#"fFm /uK(PX@) bU K =K >@' bU UK >Y@-,)%"! "" +>32#.#"&#"&47>54&'&47327js : }TP 5@r PR s??s GV s?h}k9DD9u{; 11 <{5{;2  1 ;{ ) )F@ @U= >YI%+."'>32&#"&47>54&'&7672no 2Wa1b \^b2/V ZRfbV~{9 11 8{V-#"fLT*8hK1PX@"ddM =N>@&dddM =N>Y@ 751/&$* *%&&" +4632#"/&%4632'"/&"32# ! u+)y  3'p %ǴVT!vL1 %!-  : +T{h'3TD /<@+b  = = M=M>Y@10860<1<.,&$   +2#"/&546%2#"/&54647632'""32654&% +9#   +{qzǓPu=sLZ7 ,   /`˅sւs EBTLTo 'B@?bU M =M>&$  "" +>32#.#""32# ! ks9 }TP ǴVT!vh}k9DC:: +T{h'3T) (e@ @UM=M>Y@$"((&&%+."'>3247632'""32654&|}7amTqzǓPu=sLZRfbV˅sւs EBT'ZK-PX@<d d  IU  M =L = M  >K1PX@=d d  UU M =L = M  >@Eddd d  UU M =L = M  >YYY@ ZYUSPOECB?<;652.+*$"' '%&&"+4632#"/&%4632#"/&"3265&#"&47>54&'&47326323#"'.#+)y  3'p TbF@rTR s??s G#3yAm_/V? 9p`wV1 %!-  +{; 11 <{5{;2 %=fRNPd)%-F QK&PX@/bb   =M =L >@3bb  =  =M =L >Y@ONDC@<20.,(&   +2#"/&546%2#"/&5467>32#"'&#"&#"&47>54&'&7672% *:#   + )yA;@;%&$!E17q iZd4/V c 9 ,   / Fd?#'@0dJ?{7 11 :{V-#'o I;K-PX@B  bU  IU  M =K = M  >@C  bU  UU M =K = M  >YY@%IHDB?>421.+*%$! "" +>32#.#""3265&#"&47>54&'&47326323#"'.#/ks9 }TP ŪbF@rTR s??s G#3yAm_/V? 9p`wh}k9DC:+{; 11 <{5{;2 %=fRNPd)%-) =q@ @&bUM=L >Y@ J"$(%+."'>327>32#"'&#"&#"&47>54&'&7672y{}7am )yA;@;%&$!E17q iZd4/V c RfbV Fd?#'@0dJ?{7 11 :{V-#' ?YK1PX<K1PX@!dd VM>@)dddd VN>YY@ XW3+N*%&&" +4632#"/&%4632#"/&2>54.'&47327#".54&'&4727+)z  4'p 2/ddN- 3:C15d9-qњZX;c{B71 &!,  Vo-`@\xR?D' 2  1 9u-׮Z(eϕRy=2  1 ;-)D [K&PX@2b  b   =M =N>@6b  b  =  =M =N>Y@&"!YXGFEB?>860/.+!["[   +2#"/&546%2#"/&546"&54&'&4732723276'4&'&473272'&'&% *:#   +{3LF3d@\q!1N F38^ N7 ,   /y`>1  =yZb3b@1  =wP= % ?F2 z ?o HuK(PX@) bU K =M>@' bU UM>Y@GFC@=<53($ "" +>32#.#"2>54.'&47327#".54&'&4727ks9 }TP 2/ddN- 3:C15d9-qњZX;c{Bh}k9DC:Vo-`@\xR?D' 2  1 9u-׮Z(eϕRy=2  1 ;-)) G@ @)  bUM=N >Y@ ED321.+*$" GG% +."'>32"&54&'&4732723276'4&'&473272'&'&{}7`m{3LF3d@\q!1N F38^ NRfbV؝y`>1  =yZb3b@1  =wP= % ?F2 z71D8Jp@ !@#beM =M>Y@:99J:J75&$( +'.#"#"&#&'6!2654.'.546322&5>54'&546( *'DkGY04Gjf5hXla: #@'{$.X63Mqn:u9Fy`HQ?V9 z:RQ)W1X8::X}Na7C/ ݁{:^95=[}M4`PVg!>/B%3b1FY@V(E<9beM=M =M>DB@>31,+%# +2&'>54'&5466232654&'.54632"'.#"#"'&#"&}9Fy`HR?V9 , !^bBlT}{fu/bm . /@/7Y]pDjg1dVT`PVg!>/B%3o cJEDR33wm`ml =R# G@' e U M =K >Y@@=;:65410/+*(%  +2&5>54'&546&#"&47>54&+""'6743!3 72#.+"\9Fy`HQ?V9E  OVF+6dj-) .y-5)T`PVg!>/B%3N{; 11 <{dgbw y s`hi31h<U@R <9db eM =M>975310/-*(%#<< +2&5>54'&54634.5476767232'#327#"5#"=4^9Fy`HQ?V9^-$"" 1'^Jyy T`PVg!>/B%3Nf RO )dY:!i )D,Z!@ beUM >Y!$+"&5467>54&#'2654&#"'2632-=/jզ hwNT1R^FN۰ /'D5'o=Zj]yd{}b;3-l4!F+1=@:!  <b=M=> 11 +"&5467654%"5>54&#"'72632*r2qhD`\+RVb1q@)d   U  SK >Y@HHHRHREDA=:96521CCC+%&#"&47>5!&#"&47>54&'&47327!4&'&47327&'767?s TRs@C@r TV s??s lo s?@sUb s?J {^ugff{; 11 <{i{; 11 <{5{;2  1 ;{H{;2  1 ;{d\g)5HR+)Nu7L QB@?+<:b=M= >OMC?53*)  +&'767&#"&47>54.'&7672767632&#"&47>54'&#"/N)HN'G,9  sWZ 3-& -7 iu57/ZR^sT+%H{#'jOPip#WI 1  1  'DBBA&*R3FHy9 1 1:y7m1Jy&+!DF4@1;>(# <M = =>%;;+9+&#'54?>54&#"&#'54?>54&#'"/672$329)/2P11(NcJ%12P13&!A c Df;'qK+)'''-aez-''''-^B 1(R 'PP}PFM@$L F ? 2 /<-,9KPX@9U = M = M=M= M>K!PX@6U Q = M = M=M>@4  UU Q = M=M>YY@"KJHGEB><53*(%#  +"327&264#">32#"''67.''"54>324&#'"5'>72Jj5:\Z)hRB|?I3]9#}'#'%)y3Z9Fh7Xsn6{Q!B ?&;AotBoW(\%xd{oN2:%/o'49mfgE75^B 1#R` =6@3;/ <M=M =M>7654'%# +3 54.'44#".54>7.5463">999{^VJ`bGiwV%   +% 4.'4'7#".5467.5467>ZfmRHP!=XBBMh4׽uKmL?%nH+XA@FTBVRN{} uN7DFP/->+?F%m3Hh=.. ?RD$`@ <9K&PX@ =M =N >@U =N >Y@  $$ +"'673!2!2'654&#!"5477N5>! R71-yCD)9py)4!NAD B}IVi7J7uX!F _@ <9K1PX@=M=N >@U=N >Y@    +"'6737!2!2'674&#!"7473{153-##)fw+-R`Fl 59P\J5mbsEpKPX@'U UM =K >@%US UK >Y@ CA762.%$   +462"!#&#"&47>727&#"&47>&'!"s?V@@Vj 2nLk NZ%-D?63 [u!wP%V@@V?- ^  11 4XJ)l/1  11   [J- D@ > @@8b`UM=M =M >Y@ CA&#(("'$" +4632#"&32765## 546?65#"#"7463 327'"&TF$'@IZU  U  UQ M= L  =K  >Y@&XVRPLJHE@=:843-,)'$! [[ +"'7#"&47>54&'&473!272'.+"326762"'.+;26&+632#"'4772654 5' Rss??sd 50!#'VjP7}:1  1RT3@J' 2c3?S}^HPN(/: 1 <{5{;2 F{d;H1[ bOJj 70%;H9 F9BV#!+'GLfB.9l@i(&<;b  U UQ M=M>641/+)%# .. +"'7&'&543 #%3267632#"'4772654%2'4&#"5' Jc`Z&AZVl<#,?S}_HON)/:sdF4L> uo#m1= uF9BV#!+'Gub-nLT)7F@C  UU M =M>,*30*7,7(&"  +462"$462""32# ! !"&5463!2;N;;N :R99R`ǴVT!v2z  }R99R99R99R9J: +T{h'3 . /T .;KPX@+U M= M= M>@) UU M= M>Y@0/75/;0;-+%#  +!"5463!2462"&%462"&47632'""32654&59N97R7Z9N99N9qzǓPu=sLZR%-"0)99)'::')99)'::1˅sւs EBTLTm.:H\@Y%<UU  U  M = M  >0/GEA?64/:0:(&!..#!# +32+!#"546;2676#".'&"'62"32# ! !}{o}"M: 6( "!(d#-aǴVT!v5*)?0Eiv+1; +: +T{h'3T )8E@  @1 UU V  M= M>Y@$:9 A?9E:E75/-#! ) )   +!"5463!22676#".'&#"'63247632'""32654&5d#(!) #?0'''/u%)4qzǓPu=sLZR%-"0+? '-5 &˅sւs EBTLTm!1@.UM =M>     +462""32# ! T?V@@VǴVT!vV@@V?: +T{h'3T+ 'ZK(PX@ M =M=M>@UM=M>Y@#!''&%$"+4632#"&47632'""32654&F%';B)%=qzǓPu=sLZ%=D&#@B˅sւs EBTLTm#1?@<U U  M =M>0.*(###!# +32+!#"746;462""32# ! }V?V@@VǴVT!v+')TV@@V?: +T{h'3T '3B@?U M=M=M>*(0-(3*3#!''&%$" +4632#"&47632'""32654&!"7463!2F%';A)%>qzǓPu=sLZo5w%=C'#@BV˅sւs EBT%.#0 HjK(PX@" UK  =K >@  U UK >Y@>;87.-*'$#  +!"5463!2&#"&47>54&'.'&472776&'&4727N5FPV F2HF { U& $H or  JO) %-#/X{; 11 <{78/U0 0  1 *B]>*2  1 3JF*?3% D2@/U=N>;8($    +!"5463!2#"&54632327676'.'&4732776&'&47276 Ts)73' #F!16';?RTPP"<> {R F?!+%%-#/+!=;dXF{[61  2?@V:81%1  27Df3# ,i@+'<*)9KPX@ bU=M>@bUQ>Y$+#!+%4#"326%4&'&7672>32#"''7&h9=C$3`[)[ i "5; '8P1#16^yP[glq1&*RP4CR)B=hhPJ-B'DOD@AE2B?$($+'J#+4'&#"&#"&47>54&'&76727>32>32#"''7&572654&#"'Eh~#+R {Qf|`0/W d TTu57 !)DC%?42.,&$! # +72654&##"=4;4'&>?232'#67632#"''7.BNR#5?/#Jy %^w1/N3;<=@bQ>Y'$(+4&'&7672#"&54632325/V VLMT%K/ )F(V-#"gP?F/)3FG@("G:+=M =M =M =M =M >@<U=M =M =M =M >Y@FD=;98$%6$#(! +&"32732654&#"4&#'"5'>72632#"'#5#"54>32BJj5:\Z)qI=_`8f+!B ?&mX0 %b7Xsn6{QyAotBoW(T#N﷚ -#^B 1#RbwK? idfgE7J! <Go@l,FE2 < < M= M= M=M=>>=DB=G>G;9530.+)#!  +"32654&&#'54?>5'".54327632632#"'"327&+;!Jh}'/'P'1'Nh\-縠`hmOyph@*eS  U= L >Y@ DB A A76I1'# +3'#;&'#7#"&47>7273 &#"&47>&'!#"AiW #;0<Z0hLk NZ%-Dli?63 [u!ww'%q / V] 11 4XJ)~/1  11   [ L#,H@E%#$ <be=M =M>,("!+%2672!"'#&54763273'&' &#"~lՙfkmrg|GiY=$'0]3bzƆiw[[Y _&x S4Z!*@!" K!PX@'be=M=M>@'dbeM=M>YY@ %'%#+2"'327#"'"&'7&5432 .#"1K@IOi! Hgks |IU?] =Ef/\'/+0awWgq 628¦]-7}3 @& ZU U K  >Y@/,)'$" 75 +!"&47>5#"546;4&'&47327!2#!;2676&#1j s??s LR s?H7>## !71 <{){;2  1 ;{) NA5=@ 74065@-eG  U M  =K >Y@;821-,+*A4C +%&#"&47>=#4&+""'6743!32?3672#&'5&+"E  OVFi+6dj-) fiO$. p-5){; 11 <{dgbw y ,&>hibX_@\O VTSRQP&$&;%*# +6232654&'.54632"'.#"#"'32767672#"'.'5"&#&#"&b , !^bBlT}{fu/bm . /@/7Y]pDjg1 '=) $=BH   cJEDR33wm`ml =R# G@*bR=M=N >Y@ E""#s% +3"547'67433!2>32327632#"'&/&+&{/3s3#! (+w9)%ZFPFA!"%'BR;1,P;CZKX  =P55Gq/)RmbD71@.b`M =L >$((C+%&#"&47>52>54.#"#"&54$32@s&D );5)ÓBuA/,&QT27/1@.b`M=L >$&$D+%&#"&547>=>54&#"#"&54632;j 9; j@) U  U  UM >Y@ 88 8J8IFDCA=;32/-*(%$! 76#! +3 54&#"'263 #"&#"&47>=!"546;4&'&4732654&+!2##iZGX%#pR1nӒbj s??s DfךǍf덁,P))HsC1 <{P*{;2 !XbQ) ?-?NcK(PX@#  VK = M  >@!U  V M  >Y@IGA@;9#'I3# +#"746;4&'&4727!4.'&4732732+#".5%!2>5}j;c{B 3:C15d9}h-qњZX+2/ddN- *Ay=2  1 ;{A?D' 2  1 9u*7׮Z(eϕrVo-`@\xR7B*@b = >H*C+%&#"&47>7672&#"&47>'# 79|.C.,;P NT g&% '*? 11 =///0qB / 1 >[BTU9DDHKT@+L &#K*H<3 E D <;K(PX@; Z  U = N =M= L =>@9 Z  U  U =M= L =>Y@TSPMJIC>96$C+"&'4&'&473!27272'&'>762"'.+;26&#!"'7>7#&#"3*?sd 50A(v !#P'1  1RT3@J' 2cHs8E3;4yP7{)O{;2 0O%M%5P bOJj 70%H9 F;VI9R;HZ!'04@!(&"#1 K!PX@1be  V=M=M>@1dbe  VM=M>YY@320/$'"#% +2#'3267#"'"&'7&543272'6'&#"77'1X>&-u;Vl<#Դir zGU;+ )k4L>/lW#HG1=ek?4 n]-oOt-6KPX@$ ZU  RK >K(PX@%  bU  RK >@+  bUU  I N BYY@ 31$'##C# +#"746;4&'.732732+#"&5463232>5E OZ b<%I6-f-mB98)F?){;2  1 =y)hs9'X=19465˪7;- ?K/PX@*bU V=M>@'bU VQ>YY@ ?=#'$%#$$" +4632#"&4&'&767232+#"&5463232=#"746;E%'@&bRM =M>Y@-+(&"    +"3275&# !267232>7632#".{^}~uj#A'!" NH{y1VP3L}q-h'/H <@6RK;e(ZH<h@%bRM=M>Y@ $$'%$*&!+&#"327>=4&'"54326763232>7632#".C^w9^a1S4 bs\%!$ L!$vw0VP1KȤoT' jm'5X>< '35P'"9c)[7C5K-PX@2I U  M =K = M  >K1PX@3U U  M =K = M  >@8UI U  M =K = M  >YYY@ CB><98.,+(%$!  +"3265&#"&47>5#"746;4&'&47326323#"'.#-bF@rTR s??s G#3yAm_/V? 9p`w+{; 11 <{Z#{;2 %=fRNPd)%%?8@5bUM =L >=<##G##"$% +7>32#"'&#"32+&#"&47>=#"746;54&'&7672h )yA;@;%&$!E*7q iZd4/V c  Fd?#'@0d@7#{7 11 :{#`V-#'-JPkK(PX@%  U K  =K >@#  U  UK >Y@LKFDA?85213#'C+%&#"&47>54&'#"546;'.'&4727!76&'&472732+!7FPV F92HF { U&('$H or  JO)Jf p {; 11 <{78/=)-U0 0  1 *BEJ>*2  1 3J3**?3%sHM0@- V=M>JIEC(7G#&!$! +#"&54632327676'#"546;'.'&47327!76&'&472732+#7Ts)73' #F!16/';?RTPP"71J<> {R F?!h+]+!=;dXF{^#m[61  2?@1%1  27DD?#yf:8h 8H@E42<b`M=M=M>$&#(("%+76'4&#"53763 32654'&54632# 4.#"&'632ZKHFn)op D`7)H %'NuF9+17ZZ!l!Vf'+++ /JVZ-+19!'L>H53@0<bM=M>%!&%$)&!+&#"327>54&'"54326763232632#C^w9^a1S4 )m|\#*+V-_KȤoT' '5eK< '3+1:  ++33@0<bM=M>%!&%$)$!+%32654&#"632#"'&#"74.#"#"&54>32C_whg5 (m|鴠\#%+W-_~K٨ )'6K; '4^+19! ++ 4@ !KPX@-bM=M= =M>@0bbM=M=M>YY@ #%&$%$$ +32654&#"'7632#"'&"'654632#"'.#d#NE_dlfx<!ۏ{>=+=8/7`㷓˼\2!+f-,/9!/>5+H+-@*<bM=M>$$$$"+>32#"&5463232654&#"&HHg?%FA9fsh!wXlV+/J-3/æׇL'B 3A@>,<9bUM=M>'#$-$#!+4#"3267#"'&'>7.5432#"&'&">32BH !1FD}Vf/Qstu9%1% hRJ7%H'ra)#|#=ja#141w{V+mlP4A@A6 K!PX@3bR=M =M= M>@1bUR=M= M>YY@ @>$$$)4%& +7&'5#"&5476324&#'"5'>7232>7632#"&&"327fbP!B ?') % M!$wvbL|{D9ZP+/߉{-+^B 1#R^5P'"9cyfPuP&2u@ &('@(bM=M=M>Y@ %$%#"%"+4632#"'&#"#7'#"&547632&"32wfD5#%N8!c=0fbPL|{D9ZP>///TP{#L' 0#3߉{-kyfPFBG@D<bUM=M> ! +&"73265"54632#"&'6)Jvud'ŤjG Lds}y#[\%`V !1@.bUM=M>#"%"# +"32>&'632# 434'&'"eE5L>#o`'!BZVm-ub-nod#l213@3@04$"  <:M=M>=;,*%" +.#"&'>3267767&'&7#"&54>32656L+z49`o^ 0:DK'8  ?b,)\{>ZtrTso8TH\G9Tlj8!"%%,%D53>1 E]r\\^:tYbA&0L`f6T^dZ/K(PX@ <@ K1PX@,bIUM=M>@-bUUM=M>YY@ !"""$*$" +7327'"&54675&54632#"'&#"37632#"&#"sTVds)/'+^11;=W V`RmX !{jF#=PZ9)#CX+/ `N0K-PX@ #",<K1PX@ #",<@ #",K1PX@,bIUM=M>@-bUUM=M>YY@ $)$"#"!" +%4&#"#"5432254&#"'"&54632#"'673263dTX =;4a9f6#3&u\jXo +%ZeaD%'7\R>=ozX\NsJK-PX@   '<K1PX@   '<@   'K1PX@1 dbIU M =M>@2 dbUU M =M>YY@JHDB@>;:75310.*($" +677>7&'&'#"'6732654&#"#"5432254&#"'"&54632'$-(++ '608[U\jdTX =;4a9f6#3&uˑ(+A?%%[R  #)!3kz;>=ozX\RXo +%ZeaD%'7\F,@@=!<UM=M>'% ,,  +2#"$54$2>54'#"5472654&#"ժ^mƸ=FV-uVh^JjL"j@vJb7)w+!79''pVLLti+Z[s1`KPX@#ZURK>@$bURK>Y@ #""$$#'C +4&'&473273+#"&5463232#546;9eNR b1X'T3;'uy;1  27{(J9.(3#2$J3>@3>4 ( @9bbM=M= M=M>Y@ =;#%$$$("%" +4632#"'&#"'"&54632326='".5432&#"327wfD5#%N8jb$!%%gBuNPyQTzw{od;>///TP{W oLpJ!)#%'R1Df-ƦѺ-J\$/A@>/%<bM=M=M>$##%$$$#+%'"&54632326='".54327632&#"327\jb$!%%gBuNPﻢ^Tzw{od;oLpJ!)#%'R1DfR1'3ƦѺ-F+9@6+<U=M=M>$&$(C+%4&'&47327'"54323'.#"327-[wVTJ /1'!wdD`@2  2DZZub ZĦ> bs1: @91<Q>*L-F+6&'&47327#"&54?.'&4732732654'# S fW=R )7(bqVFZ#T$6K{DHN" tH)>PM61  2.()B\H9-FU7 1  2 7HVGCL'}0<8@5;+# <M=M>53(& 00 +"&54>?&'.#"&76327>32'.#"'32654&/Jj ''*+' W0V:5q?aF2 -K8L)^1#)! RJ9#BF# L/ !*_fVGXՉ|73m#+)f':sF0@- <K=N=>DB84*(C +4.'.7327"7654'"'&54&'&473273276+:  sWZ 3-& -7 iu57/ZR^sT+%H{#%PWI 1  2  'DBBA&)REH1y91  29ym0Jy&' C:@7<7/"<bM=M= >:+:#"%"+4632#"'&#"632&#'54?>54&#"&#'54?>5אoG;)9%Z'1'R'3%EZ\'1'P'3%2-5!):^M-$''%-yN-$''%-';}F@ B)$K(PX@4bbM=M= =N>@1bbRM=M= >YY@ #"%+:*"$# +%"&5463232>4&54&#"&#'54?>54632#"'&#"632}Ln#H+5&EZ\'1'P'3%אoG;)9%Zh?2)$-!!FGsR!yN-$''%-)2-5!):^13- 8TK-PX@TM == >@UT= >Y@ H$"+4632#"&&#"&47>=#&5467354&'&76723#F$'%%$ +4'7327327#"5%?=*/DE#%y=MB qXBd)s!@K= >CG +&#"&47>54&'&47327h2b`^ b21c ]Zb2u{9 11 8{{{91  27 D4Z@W0 3&" <  bU V =L >/.*)%# 44 +2676#"'&#".7>5&#"'6324&'&7672#)!) 3` \V  `3!'''/u)Z h+@{; 11 <{ -5q1&*R%*5@ ' @-b  U V=K >Y@+++5+41/**$#C +#&#"&47>5#"&546324&'&7672#.#"3Ũ3`\V `4Py^+M)Z h/B'ZVJF{; 11 <{EL^9Ftq1&*R!+;D-51&#K!PX@bR=M >@bUR>YY7$$$+%32>7632#"&=4&#'"/>72Z)!$ M!$vwo!A ?'Z5P'"9cIuy^B 1#R^%G!K&PX@  7 32 <@  7 32 b  b U= M=  L =M>K1PX@Bb  b U== M=  L =M>@@b  b  U U==  L =M>YY@FEB>;:64$#$$5 +74&'&76727!26?22#"&5463232654&#"'!"&#"&47>)[ iA-˜w!!DR8Xfyg#XS54`]V `4q1&*R L9+F+PNa2{; 11 <)s\4@1< K=M >ZYONK$J)C%# +%&'"&'&#"&54&'&47327327&54&'&47327327654.'&47327"'&Np}V+Xs`jgT+==|)PfcjgV)b{#-; {Tb{Z1/V d UAD{91  2;ybT9F{91  29{y%1PUI 1  2;ypV-#(sa=@:QLD72 <b=N=>a`;*=*;#" +57##"&'#"&54&/&=7373267&54&/&=73732674&/&'5737#VXPf }V'/)R'1'=mI%3'P'1'3kN'2&P'1%!c=0XTdL+)'''+-?P9+''''+1EN+''''+HL' /#3B;d@_GA@9-'K(PX@.bbM = =N>@+bbRM = >YY@b`][ZYWVJ*L+"$# +%"&5463232><&54'"&#"'54?>54'"&#"'54?>54&/6723632632Lo#G+5% y+/-'-)h,--1'3%XSRj }Zh?2)$-!+1I@^@bж9T+$ '''+Ӻ+&''%-H5 1 `J;CC60K(PX@+bbM= =N>@(bbRM= >YY@ +K""$#+%"&5463232>4&54&/6723>32&#"'54?>54&#"TLn#H)5%XT\X`%1'7'1'7Nh?2)$-!!FGsR!H5 1 jLT-'''%+qb+;DK(PX@+bbM= =N>@(bbRM= >YY@A?;975+)'&$#J# +%4&#"&#"'54?>54&/6723>32327632#"'&7N%3-1'3%XT\X`%5)G#LD-oqb-" ''%-H5 1 jL[#RqGF!!-%)2@7=7s4E@B& <K= K =>1.,)$" 43 +72654&+"'534&+"=37#"#"';2&#54331? />yN ;1%)/;wP7%:G7#-\ 5)--%9R%49&+-T.@+SM=M>&$&#+47632'"%!32654'.#"TqzǓPu$@jBZt˅sւs ?skA c;7s Im@jB ED-< ZZUU M = N > A:6/+(&$! II  +%2654&# #"326?6;2#"/&'#;27&#!"#"$54323!7.B3!>Lݱ?' ) +  D@߉'9)y=+ C_ F: +F7%{+}˲/#+  +b -?1Z%3?!Fq,65@25 <UM=M>#&$$%$"$+#"'#"&54!2327&5432324.#"4'"6qDu}GLNٚtHr?3RT7=Pqq/ yZ'?bR݃iwk`mfZ7;F)T1}B!P He@=50/("  @U==>Y@ HG41 +%>54&'4&#'"5'>72&#'54?>5.54>7w->V=\3!A ?' &2&P'3%m\9f51XP7g`+B^B 1#R+''''+6Pfb+qg7s0+@(bK=M>C"$%+%4&#"&5463227654&'&47327"'& )yB;@<%&$!E17qiZ  d3/V d Fd?#'?/eJ?{71  29{pV-#(+-/@ )#" K-PX@#bZSM>@$bbSM>YY@ D"#!+"'7##"54632276'4&/&'57327'XS{ts/)/%)L'1;Y='1'GH51!^'/%k){-&''%s8@ *@.bbQ K=N>Y@520-(&$" 87 +"326762#"&5'#"&543232674&+"=37!##8#1db?JZXF[?' #%f5!T7 !}TB1?<)92F@+XX!sC7)-/ 9!/j)#@&bbN=L>Y@ D"#!+4&/6723632#"'&#"&#"'54?>XT{us0)/$)L'1<Z='1'H5 1 ^'/%j)d-'''%!%,@@-b``M=M>Y@&$! ,, +"567367632#"'&"327632#".'47)V+1!NWFV5+7% #%m7J7 ;B=Rs; =5JH53+5`v#$%222+%;2&#54;2'7632#"'.#"N-8T:F{VBX-B!)-#5*-+_"/e=C>)'D,(%)@&bM=K >226$#$+%4.#"#"&5463232&#54;26u#/' D,XDQH5F7W7-A(-D')=D=9Ŗ^+-*)s?l+*@)ZUM= =M>Y@ &#cG"!+4#"3&#"&47>54&'&47326323#"&/.+F'N 9 b21c 4+ǘj/R%9<#TJp$XA?{7 11 8{{{91 mw`{ P5oN+ `VP'31)>l@)ZV=M=N >Y@ Il%""!+%4+2326?>32"#"&#"&47>54&'&47327F\XpJT#;:%R/j+3 b21c : N'?>M>PV`!+Nn5P |`wl1 :{{{71  27bDi@f<b``   b  RM= =M>@?;97642'%  DD +"53&'6232654&'.54632"'.#"#"'&'3265462  , !^bBlT}{fu/bm . /@/7Y]pDjg1dV `%6)- 9L cJEDR33wm`ml =R# G@"b`QM>Y@$"(( +232467672#"'&#"#"&546B5q-TRThv%395-JKH}"Bj}D?Y1&;N{fo^%=N;)J8KPX@0b bUM= M  >@-b bU  QM>Y@42+)&$"  88 +232#"546;467672#"'&#"32+#"&546B5q-TRThv%395-JKH}"Bj#=D?Y1&;N{w.fo^%=N;)Jf+kKPX@#b`UM>@(b`UIMAY@%# ++ +2#"'.5#"#"&5463232>7>#}HNGT)9 %siXMT-)0  );N=D/N&1X?D/3$-2@/, <bM=M>$*-""+#"'&#"67#"&54%5467672326=%39=3?)L-9[X19V-TRThvF3Y &;Nk9A/#wV':;D?Ys'#N)D@A<beM=M >&$"! )) +!##"56=#"=4;4&#"&'6 32)^-$"" 1'^Jw;y Nf  RP )dX9! ));\+@ '<#":K!PX@ZM=N>K(PX@bM=N>@bRM>YY@ #%%$"'+%327632#"'&#"=4726=732'#P%5+H"LD-bHw2#RqGF!!-%)2@7B?p}') )-)s<FI@F  b  TM = M>CA>=<<87637"+#'&'&'"&=#5354&'&473272!54&'&473272!3276'm8^ N}{3LF31N F3od@\q!7PP= % ?F2 {yPT`>1  =\Rb@1  =\PyZb3s+8@5'! <M=M>$" ++ +%2654'7>3!.+#"&54%'#"'7!2}#!3 #:  9# 4!#/wQ%D![ّ[!D%Rw3' @&<=M>$+#+3254.'&54632#".54&'&7672^Hj/ F+`pbB/V [ F`8'<$Ӹ=wZ V-#"*@= >H(C +%&#"&47>7632&#"&47>'.XfV=SN?##"7L{CHM!B2  1 @P/+yV6 2  1 6I?>oN5J/@,8<=K >HD<:6510-)&% +%>'.&#".7>'&&#"&47>762632&#"&4LG  $1#{:Rz P  ;8 {VBR HH = !9H jPT{ B;J#$!+)22  1 .H &V"$-2  1 GH+)-)kQ12  2J8@M = >/,L!$!+672#"&#"&#"&47>'&&"&47>76mTr)73' #F 15';7(%M +.'&473276&'.727&#"&47>= B   %Q  YZ  7F 1b39 b2- ! 1  2 , M61  2+16d{9 11 8{LL99@6(<b=M=N >$q%%+.6?%"547'67433!2>32!>?2#67>;5/3s3#! 7Bw9)%LS)'* :\#C 1TiIYKn =PCV< |< *'?DB9 3>@;<9UM=M > 3 2#8$'$" +%4&#"326'67#"747!"'7!2367632#1:-G4Pb7?1H7&)7=+<##);2Lj3^-%G)#=kq{ k3 2  57-HBIXiR%-@-K1PX@/bU==M=M>@-bUU==M>YY@ $#$$1! +%!"'6737!26?22#"&5463232654&#"Xw553-A-ʜw!!DR7XfygNl L9+F+PX- 5|@-( <+*9KPX@'UM=M=M>@%UUM=M>Y@ #,7"%#!+&'"326767#"!"'3!2&'#"&4632{Jj{K`-D7/'+%% @!/`gA^'!0 `^kkZ=J%8 '/!HelR^ OF}9'?6+@( <bM = >/-)' H +%&#"'54?>5467>54&#"#"&54632%1113%>b5+%{ZN-#!3R;-''''-6Z10X9jkb<5 5/o{fldN;?70@-1+<bM =K >C'$)+%4&'.54632#"&54654&#"2&#"'5437>{@bUM>YC'$)+#"&5463232654.'.54&/"=7327#;PPR3!#-NZ{%-3b>%3111%doCFg{o/5 5;cjk9X2/Z6-&'''J;?#n @"b`QM>Y@ ## +"3273#"'&7>32#"'.XXRTP{ul'{qq-iGH?%FB?nf\gb`+}56lV+0J-3/V^D!1@.UM =M>     +"&462"32# ! VDDVDǴ~VT!v#CVDDV: +T{h'3={s 7?@<<UM=M > 32)#76  &$ +32654&#"32742632#"&#".7>54&'.7}ZZTVNC=T-)ZL^gL^  b11b  \DVA+D-alXiqVo1 8{{{91 =,@@=!<UM=M>'% ,,  +"&547.54632"632#"'32>5ժ^mƸ%=FV-uVh^JjL"k?wJb7)w+!89''qVKKui+AFo=@ 1@2Z  UU= M =M>Y@;842*&#" == +2#"'&'"7'.#"32754&'&47327'"5432>%+%!,=!wdD-[wVTJ / +!)BE#b ZĦ>M`@2  2DZZu =ssG(@%SK= >CCGG+&#"&47>=!&#".7>54&'.7327!54&'&473271b  CJ`+J+`HB  b11b  ?B`++`;G  b1u{9 11 6}}7 11 8{{{91  25}}71  27)-)5Y@ '@UQ>Y@42.,"  +26=4&'&7672>7#"&5467&54632#"&w3V{n/V UVq<\\/BVu7E%';A)%=w/B`9LV-#"gP`)77w[(==JT-8/%=D&#@D-sL8@5 <bM= =>KJGC<;40 +"565&#".76?654'.'&4727254&'&47327-7 iBIJZ %(  0 H%K@{ZR J! <&D / Z/uBA&)R 67q % 11 M T/1  +d, y;1  29/!s',@)  <Z=L >#T(H+4&/&'573273267&#!"'5437>5'1'=D'VJ=8{C`{>3+)''R}Ru'!6J! >@  82@1bM=M=M=K>Y@ #L"%$%$$! +&#"327'".543254632#"'&#"&#"'5437>T~w}oh;NPyQwfD5#%N8%3'CR;1'ƦѺ-CyDf-{>///TP{uN+%'''9'?E6@3 <bSM = >$')H+%&#"'54?>547#&'473>7>54&#"#"&546323#%1113%  DL5+%{ZN-#!3RP@< ݓ-''''-6 LpF10X9jkb<5 5/o{fF8U>N;?ED@A <b SM =K >EE'$(C +2&#"'5437>5#&5473.'.54632#"&54654&#"3%4111% <@PR3!#,MZ{$.3LC  -''''-T>V7Ff{o/5 5;cjk9X2/FpLL\N_@>K @B  bU= M = M= N = N>Y@][VTNNHF"%"#$q+3!2>32!>?2%"545'&'&"#"&5476323254.'&7672'6744&'&"32767Bw9)%LT)'*/@)jJ5-8 i/s3#! ?g{D;ZPq =PCV< |!H0 {ۍ{BA&*R`?=~+n {/'JfTbPh@,/Y U T K(PX@G   b  UU=M= M=M= M  >@G   b  UU=M= M=M= M  >YY@hgXVSQMKHFB@5"%$%%+%4&'&"3276#"&5476323274.'&76723!26?22#"&5463232654&#"'!"'&'&" ?g{D;ZPq!jJ5-8 i A.˜w! DR7Xgxg#XC08^ N/'JfTbd{ۍ{BA&*R L9+F+PN@!P= % ?H0 Pb ]n@:HG <9KPX@F  U= M= M =M= M  = M>@D  UU= M= M = M  = M>Y@ ljec ] \XVSRNK("%"'$"+%4&#"326'67#"745'&'&"#"&5476323274.'&7672!"'7!2367632#%4&'&"3276%1:-G4Pb7?1H=(jJ5-8 i*7%)8>+9##);2Lj3^{ ?g{D;ZPq!-%G)#=kq{ H0 {ۍ{BA&*R`=<k3 2  57-HBIXi/'JfTb):<@9 :bM=M=M>:$,$ +7#5276'573#!2654&'.54632.#"".s%w -Du1:?}`)DE4Fh}^jgj)oP9-')Pj#`PGTk13N5h7Nj+NR-q5DC3-J377EO@#F   @@  b ` b  Q M= M= M>Y@ OMJGA?7531-+'&"! EE +2325#"5#"=4;4.547676723467632#"'&""&546"+3255qhwy %^-$"" -TRThu%395-JLH|"'RAkPTi )Nf ROD?X1'@B d   b U M =M= M=M>Y@[YRPMKGE@>#$#"+$#!+4#"3267#"'&'>7&''"5#"=4;4.5476767232'#327&5432#"&'&">32BH !1FD}Vf/QJ3vy %^-$"" 1'\IKu9%1% hRJ7%H'ra)#|#'>~i )Nf RO )dY8wja#141w{V+ml-;z-@b>pj K(PX@L  b   bM= M  =M=K = N  >@I  b   b RM= M  =M=K >YY@&oka_TRPNJHB@;:9720-+%# zz +!2&#"'54?>5#"=4;54>32#"&'&#"!272>32"&5463232><&54'"&#"'54?>54&#5DaF'1' /iEwFdR=R3/9{=H+DPU}ZLn#H+5% %3'DF'1't/%''%- )n48)p6.KTqEotMHKJh?2)$-!+1I@^@bж-$'''-Z4%N@ ( @<bb  `=M= N  =M>Y@MLIE+%*# +74&'&7672&'6232654&'.54632"'.#"#"'0#&#"&47>)[ i$ , !^bBlT}{fu/bm - /?07X\pDjg1dV]V `4q1&*RP[I Xx cJEDR33wm`ml =R# G<<)B$q +3!2>32!>?2%&#"&47>54&'&7672'6747Bw9)%LT)'+]V `4)[ i(C3s3#!=PCV< |1 <{q1&*RPoCJn )K@ }a 2@-  d  b ` `c   >Y@{zrnfd*HJI%*C+&47327#"' #"'&'&'&47327?'&'&'&47327?6&&47327#"' #"'.'&47327?'&'&'&47327?6&^9)  ky  %^)^/WI^)^/ L`1^9)  ky  "%^)^/WI^)^/ L`+##!& |J ##+1,)-L ##+1,)1+## ' |J##+1,)-L ##+1,)1+d/4@1beSGK?+#!#!#!#!<:g<:g+-#+yB^@[ & <  bb` M =K=M=>=;64'"334! +3274&+"=737#"376"##".54&#"#".54632\'1'P&1(>A?O1;-=V.3 :}95N1 V/+''--D3d2EeQR%/5KNvD+HX@UA< 4<bbM=  K =M=M>FC@=4'%")$%"! +%327632#"5#".5&#"#".546323274&+"=737#"c 59g-H/! 59-A) \'1'P&1(TR%-5?+N;i73R%/5!MT{f/+''--h;>@;:5<ddUIL@:2$9"+632&'54?>=4'"2&'54?>54&#'"5'672\R N1 f1L R5}#+{:Ph %% ] %% !=(% 5XAO@L0<7$<bUU I K ?A>;84#334%"$# +4632#"'&#"632;2&#'54;254#";2&#'54;2^o=RL.7Xj=J/\+o/TV$/ ^)n -;Łf3#+'P'5ZXXR/ ## /+%/ ## - )@/b``UIMAY@'%#!    +2"&5464&#'"/672#"&54323265=#5#!+{:)-=-P+'+#;"#"{=(% 77Յq-3'3;t+.KPX@ZPM >K1PX@#ZUIL@@$bUIL@YY@)'#! .. +"&"&7>=4&'&76727632#".'&\#--;==B+!5m7  ;]%&$   ?9!;;# Iq--  )o@*bZUIMAY@ ""!$2"3 +4&+"=7+"327#7##"74323276% %#+y9RJL;! 1# '' !7)yy@7 H/ 1KPX@2bZb UQM>@8bZb UUIMAY@ /,"6""!#"" +327672#"'.5##"7432327654&+"=7+"h#-%+P-=-)RJL;! 1% %#-w;5'3/oy@7 H# '' !-+4@  1 @-U U  U I MAY@420.&#! ++ +"=7307#"37632"#!54372654&4&+39 H/h!`/+]7$/1@L /5=F'#!w%aK4VN#o#-HJ'@$1<de>JH%*C+&47327#"' #"'.'&47327?'&'&'&47327?6&^9)  ky  "%^)^/WI^)^/ L`## ' |J##+1,)-L ##+1,)1+)B'@$dXINB:6"$-F+6&'&47327#"&54632327654'.'&4732772^9$ #P=?)  ;    ^)^/u 1+##"3PX%.< ##-/!Z)(KPX@ e>@ d[Y!& +#"'&5476703#   %)  +ou-K1PX@ d[@dd[Y%&&+"&547632#"74762#/   )!- / ++;3#@ :IMA  +"&5467327Dob ;F^/Z;B # X)+N*B7@9M >  +2'>54#"5467Dob ;F^/7Z;B # X)+N*7FD @eM >Y@   +2#".546`^?< B[+DFFV%)X #5L<9]dJ5K@@QM >Y#$#"+#"'732654&#"'632b# LHRB%/h5qz TCPL\T ^J5K@@QM >Y#$#"+4632&#"327#"&^a%LJRD%-fJqz TCPL\T w @bcM>"&"+4632#54764&#"#"&wh\H^IJPR?+'h !! 3NVE989>9HBA3V5\w @bcM>'"!+#"'&#"#54'&54632/# h'+?RPIJ^H\h +^5V3BAH9=:7:FUPJ+"+5Po\V\J+"+%55o\V\B3@<e > +#3#]V]n?3@<e > +3#3\V\9nh3mu  "+&'&'676Lw\saH$u{L^s7sb%hJ @:[  +&'7671N)HN&G%jOPipRN7@ @IMAY&% +#"'654'63 %#v< )s5% @IMA   +!"7463!2}5%%-#/Zb %K*PX@ e>@ d[Y$! +632#"&547 ?2' /' %` %K*PX@ e>@ d[Y$% +#"/7632J  ,?F)/ @ <M>#! +#"'632&%- R N @IMA   +!"7463!2T\()J?` - @ d[Y&! +632#"'%J3N ,T \Ju KPX>[Y   +"54?y/% J,/F% s JLf=$@!<GK? +!7J=JDf;@ 9[ +!J;B/F+@(<UM>###"+4&#"'632#"'7326D5&PooP 3F1H@ om@EFFJO@@UIMAY#$#"+4632&#"327#"FnN)3FF3 Nn=H13F?P-yF @dL>Y@ " +&547356723b  -P-y%@" <S>" +##"'5#&547  R=R@  @ISMAY"!+#"'5#&547356323#H  T }FZ@GK? +!&547$+;Th @ :IMA% +267#"&'b|}7amhRfbV`- ,K-PX@ M >@IMAY$" +4632#"&E%';A)%=%=D&#@D' *@'UIMA    +"2654&462#"s/46Z55mmkIH>/-??-176V'mXf1"XoB` DdY@ @IUNBY@ +2676#".'&#"'62#)!) " ?/'''/u%)3+? '-6 'ou-K1PX@ d[@dd[Y%&&+"&547632#"74762#/   )!- / ++PJ=&@# <:IMA# +6727#"&547PuBb@A/Xt Ta`JN#)b9@3Dd}"+'&'0'7'67677˲    ! !\w,6n@# 5@UIM AY@0.%$"!  ,, +"&54?&#"=7327"?4#57307"'2654/-7D#DD=V;;oyFF+d7TL;!/ A1+#q+ jF5-VZ+5F  5R/L  %9*)KPX@"bUQK>@(bUIUMAYY@ !-(#+332654'.4632."#"&#"&9-*+9)9wTIrNo70NR5%533DNT+!`L)%''N1#BX#P;+'%$-7%9LG%bJ8@5:< UIK?EACCC +&'"&4726&'&'&'"&47>767'.'&4732776&'&47327kk'),J)BI7 R +J-I)!3es**JHI+ JT +J+I*&8!!Vf!!;w<!!uu!!56F@C&<bUIMA,*"  66 +32&'05473>=4.'.54632#"5465654&#"{ P3 !k`X'7D/57Od %% !%=J+NYZK/ %1G3FThc5)8K&PX@K = >@S >Y@  +#!5)s5)AK&PX@S = >@SK >Y@  +#!5!5D)'s5)AK&PX@S = >@SK >Y@  +#!5!D)s35)AK&PX@S = >@SK >Y@  +#5!5!D)׍s)5):K&PX@ =L >@dL >Y@  +!5!;D)sf#@ dGL@ +73!fiZ\fh9!@GSK?+7#3!!kk>h^X@<d[ +33#l\?u !@SGK?+!5!5!5!//XZh/9@6! 9IMA+)('// +2&5>54."#"&546%2&5>54."#"&5469F/;o PM  708n9F/;o PN  7/7Z;L;J P) 4 7Z;L;J P) 3!60@<d> +3#3|L4Mu0@<d> +#3#|M4Lu)."+%5%N%|L9M)."+%55w%{L7L;b *@'UIMA    +"2654&462#"/46Z55mmkIH>/-??-1@ d[Y@   +2#"/&'46#   +V  /=TV >K(PX@ d[@dd[Y@   +2#"/&'46%2#"/&546% +:#   +J -   /=TV>K(PX@ d[@dd[Y@  +2#"&5476'2#"&5476#+ ++  J-  / %)b=@:<: 9UIMA  +267#"'&#"'>2#/1%3PJ%'-1KR<-> 5-/3 \[:-\@M=M>+462"462"@Z??Z@@Z??ZZ@@Z@%Z@@Z?=h #@  <eK>" +73##=' 7V =h #@ <eK>! +'"'#&5473h'7# =h D@  @dGL@Y! +6323#'=& =h D@ @dGL@Y" +#&5473672h'  "= K@  @deL>Y@  +%!#.'5673+RFXdfqD $+=V>3B5%5#`\ %K*PX@ e>@ d[Y$% +#"/7632  +?F)/b\ %K*PX@ e>@ d[Y$! +632#"&547 ?2' /' %o  "+&'&'676Lw\sbH${L^s7sdY@ @IUNBY@ +2676#".'&#"'62#)!) #?/'''/u%)4+? '-6 'DHZ @IMA   +!"7463!25H%-#/) 5K&PX@ M >@IMAY@    +!"5463!2PH));h @ :IMA% +267#"&'{}8amhRfbV)1 @IMA$" +4632#"&)F$'/-??-1@IMAY&% +"'654'62%%#^m; )^m5sP3KPX@ K>@GK?Y+#3#3ZZ\\Pqo E@IMAY% +."'>32{}7`mRfbV3'@$ :IMA +"&5476726329F0;o PN  708[;L;J P) 3!77!@ 9M > +2&5>54."#"&5469F/;o PM  7/77Z;L;J P) 4 7B!@9M >  +2#"&"&'&54617/7  NP o;/FB8!3 )P J;L;[@ 9KPX@ M >@IMAY@  +2&'>54."#"&5469F/;o PN  708Z@ d[Y&! +632#"'%J3N ,T Ju KPX>[Y   +"54?/% J,/F% s \(@% <SM>"!+#"'5#&54735632+  \(@% <SM>"!+6323##"'\  d\ J@ 54'.54632_S22)/7124+9JRPZ5')5#%+jw XQ@ @UIMAY!$"!+3263#"&54632"&#"  RnwT ?@dL>Y@ " +&547356723Z  -H-y%@" <S>" +##"'5#&547  3,@) <SM>"!+#"'5#&'47356323#+- 3 @GK?  +!&'47y.@+:ddN>  +2=0303'"&5462`)'V-)5dn@!%!9`.@+:ddN>  +"50303265462')`%5). : %!Bl@IMA +462"@^??^_??_? }@IMA+462"$462"?^@@^+@^??^_??_??_??_?b *@'UIMA    +"2654&462#"D/46Z55mmkIH>/-??-154'&5469Fy`HQ?V9T`PVg!>/B%3fFq@<;KPX@XVIMA@dVIMAY@  +"'73632#"'47726545' RE3?S}_HON)/: ʼnF9BV#!+'G?f?(@%<:dIMA(! +"'4%32>76V'mXg1"XoB`+ @ <M>#! +#"'632+&$+ R TLK@  ""%%$+#".'#"&'6732763232% Y4-@#R!3Z /IT/ /RNyRA^H\=bb (:K$PX>[Y  +&'767+=)TN'H=d`XPhqm3uX @9[  +&''677+AR)K\qHTep)V @ :M>% +267#"&'{}7`mRfbV1^ @ 9IMA% +."'>32{}8`m1RfbV?y8@5<IUNB +2676#".'&#"'632#)!) #?0'''/u%)3+@ '-5 ' N @IMA   +!"7463!2\()N @IMA   +!"5463!2>() Pb OKPX@UM>@UIMAY@     +!"5463!2!"5463!233'*')?Ry+8@5<IUNB +2676#".'&#"'632#)!) #?0'''/u%)3+@ '-5 '{f @IMA   +!"7463!2\() @IMA   +!"5463!2>()b9KPX@ =>K!PX@ e>@ d[YY +2"&'1!/TVD@ => +"&'2/+'))%# Q@ @UIMAY!$"!+4#"#7672#"'72326  RnwT ? +35%!!tB3;j$@! <U>""%%$+&'>32>32&#"#"'&#"% Z3.?#R 3[/JT/ /RM9yRA^G# "+''7'77#:;;:R99::]N"+4&'7.476 '6 /(). RO3'$'q)@;$ '=hB0 /@,UIMA     +!"5463!2!"5463!2>>'*')`\ %K*PX@ e>@ d[Y$% +#"/7632  +?F)/b\ %K*PX@ e>@ d[Y$! +632#"&547 ?2' /' %#8@5<IUNB +2676#".'&#"'632#)!) #?0'''/u%)3h+@ '-5 '7!@ 9M > +2&5>54."#"&5469F/;o PM  7/77Z;L;J P) 4 7f9J "2@/dbINB""+462"&%462"&2#"7476f:N99N:[9N97R73!5 j )99)'::')99)'::+ %#9@ :M>%$ +73267#"&59m-A- 4i3Pck>+^LTTK@  @UIMAY@     +!"5463!2!"5463!2>>'*')3 #@  <d>+"'672"'672) %% %%D R   R  z J@ K PX@eUV >KPX@eV =M >KPX@eUV >KPX@eV =M >@eUV >YYYYY@ "##%#!+"#"'7&'"'62763232676#"'H :,'&'/u3>"%(#(!) ,t!-6-n+?'`-x@ $@$U VQM>Y@'% -- +"&462"&4622676#".'&#"'6327((7(7((7)#(!) #?0'&'/u%)4(8((80(8((8+@ '-5 '+^1@) K(PX@(  U UQM  >@.  U  U UIMAYY@"/-,*%#!11  +2#"&#"#"&567232>72'"&#"#"'&=63232> 9Z!!/ ?_!': ;X!"!1 B\': mH'% hD## mH%% jE$#; p@ K(PX@dT>@deGL@YY+673!.'3#>7!#.'fqD #/- FfoqdH !1dRFXd%5#6;!';o15V>3B5y)#@  <K> +55673.'5;!';o05)0- FfoqdH !2v"+%55%{L7L=F*@'<QM>#$#"+4632&#"327"=qP 3BB3)Nl?F31H=*@' <UIMA%+"&462."'>32Z5N55N{}8`mN88N5-RfbV". "+''7'7723322233.v"+%5%'O%|L9M.v"+%55%{L7L. '@$<:9d> +%55%#3#Nr|M3M{M6Lu\'b*@'<QM>#$#"+#"'72654&#"'632pN)3BB3 NpNn>H13F@oyN@M> +462"?V@@VV??V@b}5@ ,32#"#".'#"54>/%A-<+L3 4!4-%3A'L 3'-!AD!hJ (\@UIMAY@$#(("$"$$ +2654&#"4632632#"'#""2654&9Z::-/7kHV67UHjhJU76VHH/8:Z99-FF-1ACueBBcGFdBBC/-FF-1Aso@ KPX@eV>@deINBYY5%5+57654+"74?632;2:>!#9!     w @ :M>   + 7!2$67#D !e#7%qLVN%) %@" :IMA   + 7!2$67#D !e#7%qLVN%= @IMA   +"5463!2##"=#0#.R @IMA   +"5463!2##""0#-\XjN@ < :9KPX@QM >@UIMAY"&""+6$3232$7#"'&#"\/aՅ՘yH;DRϤR`hADDE dwDBRD @ 9M> +2!"'6$#9j<#%#3aT;%;9e K(PX@dT>@deGL@YY+!.'3#>7!/- EfoqdG !1P6;!';o15#7H .A@>, -<bQM > +)&$  . .# +32>7"'"&54?4&#"#"&5463227^% 5+=%/p4- L'w 3+L+(+#B7!   !/L?mL<@9<SQM >  +27"'&74632#74#" 7#-JF1!OB-B1?';2!O9_;% +&#"&7676=4.'&5673&"&462P%!+#)$ ='$$V    G%%DF $@!QM >    +2"&464'"32PHNKGH;L8Fe9;[VwgsmB+6@3 <+:RK >#%##4"+726754+"=77#7'"754&'#"=7/4 101%34R )#?F89g%L5@2<bQM >  +2#"'&#"32>7#"&465)!#'-!  1FDIRL$'!-D9-5 BVe'o@'#< ;KPX@ bQ=M >@ddQM >Y@ %"""+5&#"323#"'&5463254.#'673#7!;B'%5'D;1 ='-?"Z%m}#%N;c; '#h;p@ 65 @!  d dPM >Y@98432':" +672&#5437>=4&#"2&#5437>=4#'"'56723.+= 5#J  + 9%E  T! dR956  )4 9hsHPP@MA% IG><42/.(&$"'# +54&#"&#"&7676754.'&'6736726723&#"&76=4&#"&#"&7>N%%!+#($ =(/)>3/'3")#+!%#$!*) Zw+-    459865 w+&   L(@@=<9ZbN =M >""#+7##6326'&=47623254#"N'= #)#+!%   %/++:   ##'<4@1<:QM >$"$"+27#"=#"=472=732#y%;B/=>L?'D M hhB+S@" @eK >Y@ ###'33#+'.#"'577+"7674#"=07#"&Z 7P ?=!'7 `!  #yHBu@7&< :B>0,9K PX@ b WM >@ b cM >Y@A?<:42#%##$"#%! +432767'&#"=27#"76#"=327#"32&'"5432'&'02&'" DZr !5  ')?Q-!;! !4 -))Pu?J Z`!7?jN% @ 9[ +2&'>-=4w %)+'J#%1d @:[   +"&547!1v-)+'>%"+h@ :M>%$ +73267#"&5m-A- 3h4Pbk>+^LTT/H0@-<M=M>  +% !"'632#"'7dk>5}PT}5=|-o8Nȇd-L9Ph +?@<#"<UM=M>'%!   +"&54632%3267#"'&5467632.#";3CE1-JF3VP/f|?5}tC4|5?kb5s5=F/1HH1/F]qG;Pv}L3N84"9/H #D@A! <UM=M>  # #   +"&54632 !"'632#"'753DF1-JFdk>5}PT}5=|JE/1HG2/Eo8Nȇd-L9D(@%9eIMA   +462"2&5>54'&546@Z??Z-=JcJjJZ>Z@@Z@Yo\fm%7L+:XbV@d[  +2"5476!5 k + %#f9J"6@3dbINB!   +2#"7476462"&%462"&!5 j :N99N:[9N97R7J+ %#P)99)'::')99)'::=LKPX@3b U K =M =K >@1bS U K =K >Y@?>GE>L?L;9/.*& +!#&#"&47>727&#"&47>&'!"2#"5476j 2nLk NZ%-D?63 [u!wP%#!5 k D- ^  11 4XJ)l/1  11   [+ %#{ @IMA   +"&546323DF1-JFF/1HH1/FQ@G*@C  d Z  bZ   UUM=L >Y@$NKDCB>;:541,'$!QQ  +2#"547626762"'.+;26&#!"&47>54&'&473!272'.+"\!5 k }91  2RT4?I'!3bGss??sd 5/!"'VkP8+ %#1[ bOJj 70%;H9 1 <{5{;2 F{d;HoVK(PX@3 d  b  S  K =K >@1 d  b    U  SK >Y@&TSPLIHEDA@=9650/,(%$!   +2#"5476&#"&47>5!&#"&47>54&'&47327!4&'&47327w!5 j j@r TR s?D?s TVs@@smo s??s Tb s?+ %#-{; 11 <{i{; 11 <{5{;2  1 ;{H{;2  1 ;{0lK(PX@%dbK =K >@#dbUK >Y@.-*&#"  +2#"5476&#"&47>54&'&47327^!5 k @r  PRs??sGV  s?+ %#-{; 11 <{5{;2  1 ;{(=@:dbM =M>'%!  +2#"5476"32# ! !5 k wǴVT!v+ %#: +T{h'3}KtK(PX@' dbK  =K >@% db UK >Y@A>;:10-*'&  +2#"5476&#"&47>54&'.'.72776&'&4727b!5 k cEOV E2IE  { U' %H os JP) + %#-{; 11 <{78/U0 0  1 *B]>*2  1 3JF*?3fIH@E4."<dbM =M >EC<820)'   +2#"54763:>76!"5>54#"#!&'632;'&54$3 w!5 j  1 # V#{#L6R  #E+ %##c  y{RӨbu Hc;1F"8T@Q*&31<%; dbV=N>750.)'!   +2#"5476462"&%462"&4'7327327#"5!5 k 9N97R7Z9N99N9%?=*/DE#%y=F+ %#P)9:('::')9:('::$MB qXBdD=^KPX@UM =K >@SUK >Y@;9/.*& +!#&#"&47>727&#"&47>&'!"j 2nLk NZ%-D?63 [u!wP%D- ^  11 4XJ)l/1  11   [`- :m@ UUM >Y@ 650/,&:9  &#! +3 54&#"32654&#263 #"&#"&47>54&'&47#iZGDfך%#pR1nӒbj s??s f덁,JbQP))HsC1 <{5{;2 -'a@ZUK >Y@$# '' +!67'.+"&#"&47>54&'&47-P 3L7J@r  djs??s) DAPFi{; 11 <{5{;2 =D.@+b =N >  + !7#!"547fa9jg++  u/ 91B@8  <6:K(PX@7 ZZ U N  =M=L >@5 ZZ   U UM=L >Y@?<543/,+&%" BB +26762"'.+;26&#!"&47>54&'&473!272'.+"?}:1  1RT3@J' 2cHss??sd 50!#'VjP71[ bOJj 70%;H9 1 <{5{;2 F{d;HT9/@.<:K PX@#bXM =N >K(PX@$b`M =N >@"b`UN >YY8d9@+&#!"54767654#"'673!26327%>72h7'Pq!"# ˨#k1?3PqL'%*kl @du %TFE -GkK(PX@%  S K  =K >@#   U  SK >Y@EDA=:96521CCC+%&#"&47>5!&#"&47>54&'&47327!4&'&47327?s TRs@C@r TV s??s lo s?@sUb s?{; 11 <{i{; 11 <{5{;2  1 ;{H{;2  1 ;{LTD#/=X@U < UU  M =M>%$<:64+)$/%/ ## +3267>"'.+""'654'>"32# ! {{;1  1TR{{;1  2TkǴVT!v-I {1%+)#-J{1% +(#1: +T{h'3B-!AK(PX@K =K >@UK >YCC+%&#"&47>54&'&47327@r PR s??s GV s?{; 11 <{5{;2  1 ;{-Pb"@bUM >Y@ POJENCC +%&#"&47>54&'&4732767>.'.7327&#""'&'&'@r PR s??s PN s?_CR!#$  OD 5@`.HC-' HQA;o{; 11 <{5{;2  1 ;{Hh!+2  1  3/J#' 1 9UJ7B*@b = >H*C+%&#"&47>7672&#"&47>'# 79|.C.,;P NT g&% '*? 11 =///0qB / 1 >[BTU!-F.@2b`  `U  K  =>Y@A@=965G +%#'"'#&#"&47>76&'&4727%&#"&47>&M44  XN1_{F5{T< R (m )   e.=')' XD ("( X'`T 11 PbLq.2  1 6W101 11 -0-<0K(PX@%b`K = =>@&b`I =M>YY@ "J#(H+4.'.7327#"'&"&#".7>5.'&47327s 15?  /=s?/-%53 15? 75  s? Z> X HK+ 2  1 A?1DdHK+  11 BV-R2 q>\1#=W@"7, QF <:):TC 9K&PX@.U UM  = M  >@, UU U M  >Y@*@>&$VUMJBA>W@W<;30('$=&= ## +3267>"'.+""'654'>!272'.#!"'46743!""5.563!2676#&!{; 11 TR{< 11 Te5/'%AmmA%'/5/'%AmAmA%'/-I {1%+)#-J{1% +(#`;s=>r7;s==s7LTD '@$M =M>   +"32# ! ǴVT!v: +T{h'3;-9jK(PX@" ZL  =K >@ Z UK >Y@43.-*&#" 97 +27&#"&47>54#!"&#"&47>54&'&473+;s?@rnm s?^l^@rdj s??s ;) 1 ;{{; 11 <{E{; 11 <{5{;2 7 3{K-PX@ "<@ "@%ZUM =K >Y@ C(c$"+32654&#"4&'&4732632#"'&#"&47>5%w?s d)'^'1hDžf@N ZR s?o" {;2 Eqh6HOy; 11 <{&J+%N @UK >Y@ %% +!!2676!&67677&'&47!'.17ml#'"q1B!#3L ƒs / #!D 0wF=P90l@ '@! UM =K >Y@ .+15C +%&#"&47>54&+""'6743!3 72#.+"E  OVF+6dj-) .y-5){; 11 <{dgbw y s`hi-<HK(PX@K =K >@UK >Y@ 33C+%&#"&47>54&'.'&472776&'&4727FPV F2HF { U& $H or  JO) {; 11 <{78/U0 0  1 *B]>*2  1 3JF*?31-3<P@ 54$ @UK >YCC+&#"&47>7.54%.'&47327>54&;Bo @&b` UK >Y@KGDC?=9851CF +%&#"&47>'&"&#"&47>764'.'&473273276&'&47327N?NB {VRR0 #L3=| Ji1/f32  1 7B"-V-Jx@( bU  UK >Y@JI?<87C;C +%&#"&47>5".54&'&74;24&'&47327>546;2#@s 'F''*A+%3:>76!"5>54#"#!&5632;'&54$3  2 # V#{"K6Q  #Ec  y{RӨbu Hc; Tq1[K(PX@!U K =K >@U UK >Y@ /.CC +462"$462"&#"&47>54&'&47327@!U  UK >Y@B?<;213C +462"$462"&#"&47>54&'.'&472776&'&4727;N;;NA:R99R9FPV F2HF { U& $H or  JO) N<*2  1 3JF*?3Fyu6C@ 7 b  b = = M=N= M>K1PX@; dd  b = M=N= M>@? dd  b == M=N= M>YY@B@<:654210)'!  +2#"747632672#"/#"&54>3276723263'.#"32B!5 j 1)J'1!#TDq0NiTLnaFH 'bVjfmru+ %#w-`RPƇm3 N+өBZq>5K(PX@   <@   K(PX@2 ddb   UM=M>K1PX@7 ddb  I  UM=M>@8 ddb  U  UM=M>YYY@=;:86421/-+)%#  +2#"7476327'"&54675&54632#"'&#"37632#"&#"!5 j sTVds)/'+^11;=W V`q+ %#RmX !{jF#=PZ9)#CX+/ `9s;j@ /&<9KPX@b=M=>@ddM=>Y@98.,%#  +2#"54767>32'64&#"#"'54&'&7672!5 j Vg 4Ve Z 0V b s+ %#j [W7%R)E$Lk3w{ E+7pV-#'%1w$j@<;K!PX@b==N>@dd=N>Y@#!  +2#"74764'7327327#"5F!5 j m%?=*/DE#%y=w+ %#MB qXBd3F"JL@II< dbV =M>HG;953(&!   +2#"5476462"&%462"&3254.'&54632#".54&'&7672!5 k 9N97R7Z9N::N9Hj/ F+`pbB/V [F+ %#P)9:('::')9:('::# F`8'<$Ӹ=wZ V-#"Fy'4@ (@4b==M=N= M>Y@ 31$!&$#" +32672#"/#"&54>3276723263'.#"32F)J'1!#TDq0NiTLnaFH 'bVjfmrw-`RPƇm3 N+өBd7f@,+7<9K-PX@M=M=M>@UM=M>Y.,&#&!+%32654&''"&5472>54&#"'6432#"'7Ommh5.!,;B99HmTR1Nj{i+:hB3ꬫSs{# 3Jm\?$.o#APT@!55@2&%! <b==M>.(L#+32544?&.#"#&7672667327#"&-/')7B   01 ?/ S30 5P! 5>T?D;HZ`=hb 5V3}T B\?> ) aqzL$2.@+%<bM=M>)+"%&+'.54632#"'&'"#"&546732>54&'j}^`<R>J\czsEHٍ-PT1^LqmN\mLP#'#Zji-j`mRu}vﰢLwT=Z/K(PX@ <@ K1PX@,bIUM=M>@-bUUM=M>YY@ !"""$*$" +7327'"&54675&54632#"'&#"37632#"&#"sTVds)/'+^11;=W V`RmX !{jF#=PZ9)#CX+/ `N+H5@ @3bbU=N=N >Y@1/%#! 55 +"&5463232654# #"&'673327632Xr-N)K;S\@Z)L\1LjBLo>d+9'%5qZ##yg1! Zk-C#;+FBXX1f9,'@$ <9M=>'.$+7>32'64&#"#"'54&'&7672dVg 4Ve Z 0V b  [W7%R)E$Lk3w{ E+7pV-#'%P"2@/SM=M> " +&#"!326%32#".n=c9qLt8^DRVCHNuwiIX}N՜GI)1%@"<:=N>%%$ +4'7327327#"5%?=*/DE#%y=MB qXBd=ID@A1/H<bVM=M>GE?>42.,"#$ +4&'&76722?>32#"'&#"327'"&/.##"'5/V [%1No}Cj-)#-1 G'   !* \n%D5   Z V-#"f=`g`+/>X%;- 3u?V  E+7p-1/@, < 9M=M>$$*"+%#"&'''#"&'6723271h+JZ-SG3`}ND`fN?AA@>9764-+%#! +%#"'0'64654&54'32732674'32732632#"';, /93:LN% /93'%%-X-{ @R??$LiU NHBsqxP7ĞBB:;@7  ++'@e>" +./&7672754&5462'%%L 3 '5V/k!+1Z"'bHr15A/ P-3LK-PX@H<@HK-PX@Ab bUIU= M = N  >@Bb bUUV= M = N  >YY@DB?=;964%!$!("%! +#"&'67332763232632#"&#"!2#"5463232654'"&547.5456^j-:R1P?:7srH3="7)@T\=Qœ)!%N1E5SNGLbRZY;#9#qPN>'/ d{R "VϙmILuT%@"M=M>&# +47632'""32654&TqzǓPu=sLZ˅sւs EBT1Vo,=@: <bM=M>,+)"##" ++327#"&54##"5467>5#"&'>3?5gBdT-A`FP-')-5%N\osJ@}T;hZv9E1 !oKV qo -@*<9M=M>#&$*+'64>7632#"'4&#"2611++%qmYotX?$.>$s7jB#?/KPX@-ZZM=M=N>KPX@.bZM=M=N>@/bbM=M=N>YY@ $"$'$"$#+4>32#"'&#"32#"&5463272654&'.Bf嚋3)13?[oL! Xr-N)K/guN1+=3?灪uK!VD9w9'%6pcTE Lho!5@2<M=M>!!  +!#"&547>3!2654&'"f)Xr9Յ=dV`51w{QDž9H;8LT {7uoB@? <bM=M>  +!+327'"&54#""'>;#=k!BV\sXR-sH?%% +yos^ Vlw=bDpg3' @&<=M>$+#+3254.'&54632#".54&'&7672^Hj/ F+`pbB/V [ F`8'<$Ӹ=wZ V-#"P^ *@< 9K PX@M= >K PX@M=>KPX@M= >KPX@M=>KPX@M= >@M=>YYYYY#!" +4&#">'6.5467467632VZ?DF_'{%8;bJhil_E$jIٍ-+w:9qfJ+z@+%<:'9K(PX@#bM==N>@'b=M==N>Y@ $&&"+.#"&'>3237327#"&//7d1XH1-'B`?< )};B@5/!#/7`eROuo5 o-+-) bR7B -!;=;r@8+<;3/:9K&PX@b ===>@db==>Y@ 20#"# +67327'6./&+&767265327>71-/1== uV) PG@ 9eI(#>% dg#o{ P{5S\?$ VP1 sNhZk%&*#"+#"'#"&5%3267&546323265'67Ѵ@)pVvut7S1'%'1\{q^qa\ #J_pNj5S#)uyՇ9)L1%7@4 <;U=N>%%%+462"$462"4'7327327#"59N99N!9N97R%?=*/DE#%y=sR99R88R99R8XMB qXBd37.@+6<U=M>$+$+462"$462"3254.'&54632#".54&'&7672:N99N!9N97RHj/ F+`pbB/V [oR99R88R99R8[ F`8'<$Ӹ=wZ V-#"Tu*iKPX@$b=M=M>@!ddM=M>Y@&$**  +2#"547647632'""32654&+!5 k VqzǓPu=sLZu+ %#/˅sւs EBT3u6b5@dd=N>Y@43'%!  +2#"54763254.'&54632#".54&'&7672Z!5 k Hj/ F+`pbB/V [u+ %# F`8'<$Ӹ=wZ V-#"Fy;j0&@dddN>Y@31,*$"  +2#"5476#"'#"&5%3267&546323265'67!5 k +Ѵ@)pVvut7S1'%'1\{q^y+ %# qa\ #J_pNj5S#)uyՇ9)LD ,d @UM=M>Y@*(%#  " +4&#">7>2#"54326&#"/?HwI@'%q?hsusJ=LlZE;mXɤ [ L\!1R7]@Z-/<b` UM= M  >6410,+&$ " +4#"54#"'676;32>5"&'.546322&##"&)^ri-9 N% Fh3FF7wboN#f+ %-5WƔP}`X{\ ;*f;DB\hFsR JӢ bD1KPX@%bM ==K >@'b`M =K >YY@ "$$$&C +%&#"&47>5'&#"&'6723>32#"'&#"FPV FRPjf8N-TVLZ)9/\{; 11 <{lP- b9sD5/1D3@Ҷ(& KPX@2 d  b` M ==K >@4 d  b`` M =K >YY@><:842.-+)%#  +2#"7476&#"&47>5'&#"&'6723>32#"'&#"m!5 j FPV FRPjf8N-TVLZ)9/\+ %#/{; 11 <{lP- b9sD5/1D buA)' KPX@/   bU M  = =K >@1   b  `U M  =K >YY@?=;953/.$&C +462"$462"&#"&47>5'&#"&'6723>32#"'&#"9R99RD;N;;N-FPV FRPjf8N-TVLZ)9/\M<,++'6&$547&7>54.'4y) B`% FhfOtA( FW NOwB) "E~SX?$hH  N 7s{cF L5Hh[7MvM `4:Qn]3EtN >JF1:@7-#<bUM>%&*"%#2+4$#!"&'7!2#"'#"&54673267&54632326=<PNwR^͗ls3q-'%'3^}f1oנ׷̑E)uଠqhNj5S#)wwߴ1GC@@.-C<bQM=M>$$%&)(#(% +%47#"546764&#"'63267>3232?#"'&54632326?#"&jb)PBP#;'/-#PVHN#d-)Vq3TZ'FnLdR'')%-'Zd -*NDyXwP%B7=\-%h}%)-rc/VZ'o^/)!%d1qLTD !0@-<9QM >    +"32&#'67&! Ǵ#FjT!v: +T&rka3T$.@+ <9QM> $$+# +47632&#'67&"32654&TqzǓPXE^=sLZ˅sւViT3BT!H9 :KPX@(ZZURM >K!PX@.ZZUUINB@/ZbUUINBYY@ !#(!&U+4>32327#"&#"6#"&54232654.'&!K(PX@7Z  b  b  UN =K >@5Z  b  bU  UK >YY@872.,)%$ == +#&#"&47>54&'&47!272.+"3276"'.3?s ;Bs@@s  6*$ FoMJFX? 1 #Nd{; 11 <{5{;2  DN^-FR #N -/>o-F._@ <.:K(PX@UM=>@ZUM=>YW(!$71++"6;27#"&#""'&74>32327F9=_;'3J)iLnHJ\!7v-+T)XRVN3 Vi hL/CFws -d#!H@E <USIMA !! +"&547!654#"'672!327XD^8O'11dhBb1aZ/14wX@5^L36Z%?ZT+7)-#'=db^ @K(PX@eT >@deGL@Y+ '!7!Rs':qtLbR)1@.<(9bcM>%+64'#476767&'&!0##'%2&^39-qw;kywgc{/sduO`me;EyZIH!XD@ @eIMAY" +&%763 #654''%&'' %\N#LB-:B}{ŶupLD?L11-@*-,<M=N>#)(#(%+%47#"546764&#"'63267>32327#"&jb)PBP#;'/-#PVHN#d-)Vq3V\'sNDyXwP%B7=\-%h}%)-rc/V^%qF+9@6<9QM=M>%#++&#'#+#"&'32& 4>322654.#"^{sA $Qu'-EwL`s3X^4QRf͗QV{p+FHy9RjL#Ph'@$ <M=M>$'$$+3267#"'&5467632.#" 3VP/f|?5}tC4|5?kb5s5qG;Pv}L3N84"97- /K/PX@ bU=M>@bUQ>YY'$($"+4632#"&4&'&7672#"&54632325E%'$$$ +# ! #"!32TVT!vMtG~{h'31CP/L4@1 <UM=M>#&#"# +!2'!327#"54>32& d/ yf#jT@l!` )f% wm7r+DL">@;<UM=M>"!%#&#"+.#"'62#"'772>5!"'5463usj#fTAe`m%;\&%$-+'6?632#"'"67326911mje`}$`NX?"<-^F%ՔR-ϷX>LPD&@#<M =M>'($"+%# 32.#"3267=lP\I;܋5itV:ʓ5o)'4;%1-uRN##@  : 9 >$ +!'#"''7 7<6jP-w-F DE%B}D!'@$ <M =M>)%$#+'>3 !"&'732>54.#";56? 3w]-:Vuh5PP: P17|s#Nr^V%13PD +>@;+ <UM =M>)'    +"&54632# 32.#"32673DF1-JF=lP\I;܋5itV:ʓ5'F/1GG1/Fo)'4;%1-uRNB}D -?@< <UM =M>)'   +"&54632'>3 !"&'732>54.#"3DF1-IE;56? 3w]-:Vuh5PP: E/1HG2/E317|s#Nr^V%13/ M@C &< :K(PX@< d ZZ U N  =M=L >@: d ZZ   U UM=L >Y@ JG@?>:7610-(#  M M% +#"'%626762"'.+;26&#!".7>54&'.73!272'.+" 9?}: 11 RT3@I'!3bHs  s?@r  d 5/!#'VjP7 =w1[ bOJj 70%;H9 1 <{5{;2 F{d;H/qR@H+@?  Z   Z U   UUM= L >Y@ OLEDC?<;652-(%" RR+462"$462"26762"'.+;26&#!".7>54&'.73!272'.+";N;;NB9R99R}: 11 RT3@I'!3bHs  s?@r  d 5/!#'VjP7N<K&PX@9 Z  U Q M =M =K >@7 Z U  U QM =K >YY@SQGFDB><42.+15C+%&#"&47>54&+""'6743!3 72#.+"632#"&54632>/4.'&#"E  OVF+6dj-) .y-5)xUJ7kP=H9P-' )6" ..:^u{; 11 <{dgbw y s`hiτE=ߢN=9'#3RlOOfs*5^ 1r@ <:K(PX@#dZL =K >@!dZUK >Y@ .-('$  1 1$ +'"547!67'.+"&#"&47>54&'&47X'9P 3L7J@r  djs??s)Aw DAPFi{; 11 <{5{;2 FD6Z@W )<b`bbUM = N  >53#!#!'+" +!2'.#">3232?2#"&#"32762# Fdj'!7`fBTRB@bM =M>Y@ 75&$( +'.#"#"&#&'6!2654.'.54632( *'DkGY04Gjf5hXla: #@'{$.X63Mqn:u z:RQ)W1X8::X}Na7C/ ݁{:^95=[}M4B-!AK(PX@K =K >@UK >YCC+%&#"&47>54&'&47327@r PR s??s GV s?{; 11 <{5{;2  1 ;{ Tq1[K(PX@!U K =K >@U UK >Y@ /.CC +462"$462"&#"&47>54&'&47327@bUM>YC"&$+#".5463232>54&'&47327\YdDR86R!/9F PZb</w@ 5$*7==u{;2  1 = -MKPX@2Z  UK =  M =N>K(PX@3b  UK =  M =N>@1b U  U  M =N>YY@LKHB<:763.,+&$"  # +32>54.#4&+""&546322764&'&73!6732!"&#"&47>-;RoEC^)BB"cJHu6(F-B'N7hNd s?}b<'js?'T>@&    U UM >Y@%]\YURQNMJIFB?>9851.-*)&%" # +32>54.#32#"&#"&47>5!&#"&47>54&'&47327!4&'&47327sRoO!D\rwCJmV'Rs@@r  TVs??slo  s?@@sUb s?B8K&PX@.U  M =  M  =K >@,  UU  M  =K >YY@MKFCA@<;:7655C%C+%&#".7>54&#"&#".7>54&+""'6743!3 72#.+">3 X1cch  b1^J7R;b  {xj s?+6dj-) 9.yV5)\N}; 11 <} w}HNy= 11 <{dgbw y s`hi\N T@ B <:K(PX@Ad  Z  bUM = M = M  >K1PX@;d  b  b I  UU M  >@>d  b  b UU M = M  >YY@TSMKIHDC><:8&CC$+'"547&#"&47>54&'&4732727>762#"'&"2'.'.#jX'9@r PR s??s GV s?59H 'JG5%-! )!+d-ZemP#*NkX)Aw{; 11 <{5{;2  1 ;{q`HAR)!CK(PX@*d  b K  =L >@(d  b   UL >YY@YXUQNMGFCBCCC%+#"'%6&#"&47>5&#"&47>54&'&473276767.'&47327d 9??s PRs@  F\ PR s??s GV s?3" E\HV s? ={; 11 <{HK(PX@+db  `U  R >@5db`  `U  I N BYY@ NL$+GM#' +2654&54632"&5432.'&4732736&'.7327#"&546323267NbB"!LړL!"4CU J` d-'%j  G9{1D.HDt7-="#&3U-+%DLNNLD%1rA 0  1 BWdVR2  1 ;1.A0+7'-Vo}-FzK(PX@(Z eK = N  >@&Z eU N  >Y@BA=9650/,(%$ FE +!"&47>54&'&473273!2654&'&47327&+"#.#/P s??s VH s?/-?s GVs??sP/9! NA<1 <{5{;2  1 ;{o;Ai5{;2  1 ;{{; 1RXZso?D=^KPX@UM =K >@SUK >Y@;9/.*& +!#&#"&47>727&#"&47>&'!"j 2nLk NZ%-D?63 [u!wP%D- ^  11 4XJ)l/1  11   [L->z%@%ZUUM  >Y@0.,)">; # +32>54.#".7>54&'.73!7'.#!"32#"&;XsN DbB  s?@r  ;!/%`hJdiJ$\FP19.8``63bh@V1 <{5{;2 ?d!=JXP+^-5#`- :m@ UUM >Y@ 650/,&:9  &#! +3 54&#"32654&#263 #"&#"&47>54&'&47#iZGDfך%#pR1nӒbj s??s f덁,JbQP))HsC1 <{5{;2 -'a@ZUK >Y@$# '' +!67'.+"&#"&47>54&'&47-P 3L7J@r  djs??s) DAPFi{; 11 <{5{;2 F-*=[%9K(PX@ZK =M >@ZUM >Y@-+85+=-=*("J +7>754&'&73!7'.#!"'67676#"3!2654&%D3Fj.R; 8uK>! 'KJ647) `'h-#%!# P/Z yDyf %P`#5191B@8  <6:K(PX@7 ZZ U N  =M=L >@5 ZZ   U UM=L >Y@?<543/,+&%" BB +26762"'.+;26&#!"&47>54&'&473!272'.+"?}:1  1RT3@J' 2cHss??sd 50!#'VjP71[ bOJj 70%;H9 1 <{5{;2 F{d;H5tb# K1PX@F  b  b  U  M  = M  =M >@A  b  b   U  U M =M >YY@%tsmkihdc^\ZXUSMLIHEA>=:931"&&C+%&#"&47>5&7>7>35&.#"#"&46324&'&4732767>762#"'&"2'.'.'@sQR s?OaH*!PmdZ-d+ %! -%5HI' H92?s GVs@29H 'JH5%-!!) +d-ZdmP!*HbO{; 11 <{LRV˼# su B&'-+ )RAH`qA{;2  1 ;{q`HAR)!CK(PX@,bIUM =M>@-bUUM =M>YY@ +(#!"!$"" +7463232654&#"#"'43232654#"'676$32# '.Z%=5Tӓ#P;<W뢰-%'Zėuo1-%,j} 41 s >y! Çd+B-P@ @  K(PX@%  b K  =L >@#  b   UL >YY@NMJFCB<;CCC +%&#"&47>5&#"&47>54&'&473276767.'&47327?s PRs@  F\ PR s??s GV s?3" E\HV s?{; 11 <{HK(PX@3d  b U  K = L >@1d  b U   V L >YY@hgd`]\VURQNJGFA@CC#'+2654&54632"&5432&#"&47>5&#"&47>54&'&473276767.'&47327bB#!LٓL!"m?s PRs@  F\ PR s??s GV s?3" E\HV s?+%DLNNLD%{; 11 <{HK1PX@6  b   b I  U  U M  >@9  b   b U  U M = M  >YY@JICA?>:942#&CC+%&#"&47>54&'&4732727>762#"'&"2'.'.#@r PR s??s GV s?59H 'JG5%-! )!+d-ZemP#*NkX{; 11 <{5{;2  1 ;{q`HAR)!CK(PX@*b K =K =N>@(b UK =N>YY@ 87R"$%5C +%&#"&47>54&+""&546322764&'&73!67-@r PRs?)BB"cJHu6(F-B'L7hNd qA{; 11 <{7mK?uZ@J19C5G!# 1 ={!-F.@2b`  `U  K  =>Y@A@=965G +%#'"'#&#"&47>76&'&4727%&#"&47>&M44  XN1_{F5{T< R (m )   e.=')' XD ("( X'`T 11 PbLq.2  1 6W101 11 -0-GkK(PX@%  S K  =K >@#   U  SK >Y@EDA=:96521CCC+%&#"&47>5!&#"&47>54&'&47327!4&'&47327?s TRs@C@r TV s??s lo s?@sUb s?{; 11 <{i{; 11 <{5{;2  1 ;{H{;2  1 ;{LTD '@$M =M>   +"32# ! ǴVT!v: +T{h'3;-9jK(PX@" ZL  =K >@ Z UK >Y@43.-*&#" 97 +27&#"&47>54#!"&#"&47>54&'&473+;s?@rnm s?^l^@rdj s??s ;) 1 ;{{; 11 <{E{; 11 <{5{;2 7 3{K-PX@ "<@ "@%ZUM =K >Y@ C(c$"+32654&#"4&'&4732632#"'&#"&47>5%w?s d)'^'1hDžf@N ZR s?o" {;2 Eqh6HOy; 11 <{LD;@8 <bM =M>  + 47632'!"32672rg'hz Ǐlt_[ Xiw90l@ '@! UM =K >Y@ .+15C +%&#"&47>54&+""'6743!3 72#.+"E  OVF+6dj-) .y-5){; 11 <{dgbw y s`hi-66K(PX@b`R >@ dddINBYY"$+GF+.'&4732736&'.7327#"&5463232674CU J` d-'%j  G9{1D.HDt7-="#&3U-/rA 0  1 BWdVR2  1 ;1.A0+7'-VoL-*3<}@ 5 4+ @'U  UU K  >Y@32*'C +&47>5$%.'&47327&#">54&' qABm lo mBvOBp hjJsp>;mt1 @&b` UK >Y@KGDC?=9851CF +%&#"&47>'&"&#"&47>764'.'&473273276&'&47327N?NB {VRR0 #L3=| Ji1/f32  1 7B"-3-@\ 9K(PX@ ZK =M >@ZUM >Y@ C5CM +'.#!".7>54&'.73273!2654&'.7327w! !9 #!ndB s?@r dj s?3-3@r dj s?/PT y1 <{5{;2  1 ;{/75u5{;2  1 ; ->g9@ U  U K >Y@<:65CC%C +4&'.73273274&'.7327&#".7>5#"&5@r  djs?sr@r lo s??s hj s?Ѧ/{;2  1 ;{yf\\{;2  1 ;{{; 11 <{Ǧ-TK(PX@ K  = K >K-PX@  U K >@$ Z  U K >YY@"POJIFB?>9610-)&%  TS +)"&47>54&'&473273!2654&'&473273!2654&'&47327&j s??s dj s?1H?)?s dj s?/B\1?s jds@@s1 <{5{;2  1 ;{u97w5{;2  1 ;{u95y5{;2  1 ;{{; 1-Y 9K(PX@ K  = M >K-PX@  U M >@"Z  U M >YY@XWTPMLGDC5CM +'.#!"&47>54&'&473273!2654&'&473273!2654&'&47327!!!9 # oj s??s dj qA1H?)?s dj qA/B\1?s jdqB/PT y1 <{5{;2  1 ;{u97w5{;2  1 ;{u95y5{;2  1 ;5w- 6~@#@$ZUUM >Y@430*&$    +% 4&#"4&+"'673!2763 !"&#".7>5jN7C?-LV=g/ % j&j s?\L'j s??L ?Ko9mr7 1 ;{1 <{F/!,Q@ ? )@$   U  U M  >Y@#"POLFB@<;8410(&",#,CC+%&#"&47>54&'&47327 4&#"'4&'&4732763 !"&#"&47>@r PR s??s GV s?N7C@?sdj  s?\M'js?{;2  1 ;{5{; 11 <{L ?K5{;2  1 ;{1 </- /n@ @UUM >Y@.-*$   +% 4&#"'4&'&4732763 !"&#"&47>#N7C@?sdj  s?\M'js??L ?K5{;2  1 ;{1 <^D2q@n(  <  b``bbU M =N >.,+*&$ 22 +"$543232&'"#"&54263274.# '676$3 +FH-5bk9GZ((/;A!RN{Ag=# #FmP5-BH'N1-@[D <K&PX@;  S  M = K =K = M  >K(PX@7  S  M =K =K = M  >@5U  S  M =K = M  >YY@<;9731/.+*'#   +"32&#"&47>54&'&4732733 !"#A@r PR s??s GV s?JT6 7B{; 11 <{5{;2  1 ;{ob# -=@ K-PX@UU M >K1PX@)U U M =M >@)UU M =M >YYY@ ;:Ck%#! +!34&# &#'54376675.546!2327&#"&47>5Z)7˞yC5HsEs9u{%#; s??s AZ N3iq'|F 1 ;{{; 11 $&#(("%+32765## 546?65#"#"7463 327'"&XKHFn)op D`8)H5 %'NuF9+17[[m Vf'+ ++ /JV+1:!(L=F .*@'#<:M=M>-+'%$" +32654&#"'7>7>76>32#"p^tLk3^;É  ZF`37'̨٪KwmHqn=D  1)`V={s 7?@<<UM=M > 32)#76  &$ +32654&#"32742632#"&#".7>54&'.7}ZZTVNC=T-)ZL^gL^  b11b  \DVA+D-alXiqVo1 8{{{91 =1s'PK&PX<@ZL= >Y7CG+&#".7>54&'.7!7'.+"}1b `^  b11b  ^ hs-3#u{9 11 8{{{91 } mN5s:V, 9K-PX@K=M >@ZL=M >Y@)&:951 +%7!2654&+"!27'.#!"'676776&'&47J)9%7D,9b{ Z1X7 9e??! ;sB 9g {h+9=B:~ 2;y+R+ )I_9 - Dy;1 LB !1@.bUM=M>#"%"# +%2'4&#"#"'&543 #%326sdF4L> #n`Z&AZVmBub-n,o#m1po@lH < Z bV= N = M >lkd`YXTRONJIDBA?;932+'  pp +3.7>?635.'&'"#"&54632354&'&47327267>32#"&#"2'&/.'&#"&47>=ߘ(  ;1\N7>'5))!0C1P^'%qf-T ^mjcT-fq%3F+1C1!'-5'?8N\1;52Xhb-TZqo^ T-biX21 !4YIf1#9q^^`y;1  29y`^+>C#9#1gJU3! 1f?Fy; 11 :yF?fX#4K(PX@ "-,<@ "-,@-bUUM=M>Y@ )'#!"!#$" +7463232654"#"543232654"'7>32!"'&X7=D7hWC)'JX:ul%@DZnF&A-)\_ +- dR}p 5s^R^##O6=ssAK@H:$9  b   ` M =M >AA=<954C4 +%67327&#".7>54#&#".7>54&'.7327?)R?  b;;b  C^  V=$-PD  b;;b  G_ V>~4@ 2Dz{C 11 FyI&1 D{k{C1  2Fx-=s_c@`!__[ZWSPOJIFC?>C6('$+"&5463232654'&54632 67327&#".7>54#&#".7>54&'.7327N̐-#!%9-+;+%)H?)R?  b;;b  C^  V=$-PD  b;;b  G_ V>R`bP'# #53#  %%~4@ 2Dz{C 11 FyI&1 D{k{C1  2Fx-=J^@[@ <b  b UK=M= M  >GFBA<:8620*)" JJ +!&/.'&#".7>54&'.7327267>32#"'&'"2;2X!V^-RZu^  b11b  \obT+dk'3F+1C1!'5'?8N\/9fDA{9 11 8{{{91  29y^`+>C#9#1gJU3! 1s<KPX@*ZK=L =N>K&PX@+bK=L =N>K(PX@1ZbL=L =N>@1ZbL=L =N>YYY@ S"$&9C +%&#"&47>54&+"#"&5463232764&'&473!27X1gsujgT+'5\95%`LDT+%? !;%-5q {Vb{ Z1{9 /  / @?<8M$& +6&'&47!7 7&#"&47>54'.&#".7>76b H  H b41b EJ `,  'VR`^R  `7s{91 K 27{{9 11 6}-  u; 11 8{=ssG(@%SK= >CCGG+&#"&47>=!&#".7>54&'.7327!54&'&473271b  CJ`+J+`HB  b11b  ?B`++`;G  b1u{9 11 6}}7 11 8{{{91  25}}71  27T%@"M=M>&# +47632'""32654&TqzǓPu=sLZ˅sւs EBT=qs;^K&PX@K=K >@ ZL=K >Y@650/,( ;9 +27&#"&47>54&+"&#".7>54&'&473b{ Z11f stjgT+!77+R {Rwz  h71Z {bo 2;y{9 1 1C%%&'+32>54&#"'7632'"'&&#"&47>54&'&7672f! 0aRw=g+{PЗydN 7q qV`4/V d )16(+Dt{FLs{: 11 <{jV-#'L/-@*<bM=M>$$$$"+%#"&5432#"'.#"327/Hg@$FA:fri!wW lV+0J-3/¦ׇ5X{%<@9<; :bM=K >C"6!+%#"'67;27#.+&#"&47>uyT8Vw"3` GF`4)Z \{; 11 <%s8@=N>/,L!$!+#"&54632327676'.'&4732776&'&4727Ts)73' #F!16';?RTPP"<> {R F?!++!=;dXF{[61  2?@V:81%1  27DfP! Fu@rE ; <1;  b =M = M =L> DC?>:842.-*&#"     +%27&#""32632#"'&#"&47>5'"&54324&'&7672R9=DcR;5PLzɞ^;3`\V `4LyӕX@)Z h=[MLdVNmy}\;{< 11 <{RFIE:963CJ +?6&'.7327&#"&47>&/&&"&47>?6'&'&47327u}!=  jBDQ?R6 ;FL bUP{ '#w;  jR ?P8 PhjF`o Hݸ51  2)BR+ 11  //3 11 'H s 1  2=`s>:@79ZK=M >51.-('$  >> +%!2654&'&47327'#!".7>54&'&473277+R{Rb{Z1; Fb{  Z1+Xs`jgT+!P17y91  2;yNR )1 C*MH#+32754&'&47327&#"&47>=#"&=4&'.7327L/\u-R^sZb11bb^b29H;_1b ZobT+u#`t{91  27{{9 11 8{)0/wX{91  29FsT.@+ZK=L >I9I9CV +%&#!"&47>54&'&47327;2656&'.7327;2654&'&473271Z{bb{ Z1+Xs`jgT+!77*P fbjgT)'17+R{Rb{Z1y; 11 JIFB96-)  WW +)"&47>54&'&47327;2656&'.7327;2654&'&47327'Zb{ Z1+Xs`jgT+!77*P fbjgT)'17+R{Rb{Z1; F1 @%bUK=M >Y@ c'C$#!+4+3264&#"'673273 #"&#"&47>5ɑ;Lbq-)oR!Zb1w))^ b25(PfHe  27}e}1 8{=s!+N.@+UK=M >Cc##'CG +&#"&47>54&'&473274+3263 #"&#".7>54&'.73271b `^b21c]Z b1ɑ;Lbqw))^  b11b  \Z b1u{9 11 8{{{91  275(Pe}1 8{{{91  27=s ,(@%UK=M >Cc##!+4+3263 #"&#".7>54&'.7327ɑ;Lbqw))^  b11b  \Z b15(Pe}1 8{{{91  27`X4K(PX@' <@' @;b`b`UM= N  >Y@ 31($"!#"#"" +74632265&#"#"&5423276274&#"'67>32#"&`'=#1q{/#129=D}/-:C:P`{qx +9믓˨"Kd+ 9rk T#j=4@A@>S M=K= =M>65=;5@6@CG(" +3>32#"&'#&#".7>54&'.7327%"3265}ӵoZ+/\m1b `^  b11b  \Z b1Zoh/^RwPVRV{9 11 8{{{91  27{՗Zs F>@;bUM=M > B@95(" F F#" +;54&#"&47>767>76&'.54>32327&#".7>=#"#TV7>uO:(?<!?7vHx%Z b11b `^  b15Id+)Tp;8N1 !23C=Aja3LT/ 27{{9 11 8{C=LBb /zK!PX@2bbV =M=M>@/ddbVM=M>Y@ #"%"#!$% +#"/7632%2'4&#"#"'&543 #%326  +?FsdF4L> #n`Z&AZVm^)/ub-n,o#m1LB 5A@>  bU  UM= M>42/-%"#" +462"&%462"&%2'4&#"#"'&543 #%3269N97R7Z9N99N9sdF4L> #n`Z&AZVm)99)'::')99)'::ub-n,o#m1HdBN@K#. <79b T= M =K >A?1/C +&#"&47>5#&7467354&'&7672!!632&'$4'.#"4`]V `4y)[ i4= 7.$3s{; 11 <{$%q1&*RV%ߢ_qEN FT#J=1b 5K&PX-<-K&PX@ddK= >@!ddZL= >YY7CL$!+632'"&547&#".7>54&'.7!7'.+" ?2' 1b `^  b11b  ^ hs-3#^/' % {9 11 8{{{91 } mNP=3K(PX@%<@%@Cbb`  b  `UM= N  >Y@20,*""#!"$"" +432#.#"63232632#"'&#"!2762#"&P5+  rju^J7@6*-uA:61.!-o/#9 #^myϞ9 +iN$Rjb4A@>3<bM=M =M>"+%*#+6232654&'.54632"'.#"#"'&#"&b , !^bBlT}{fu/bm . /@/7Y]pDjg1dV cJEDR33wm`ml =R# G@U= >YH$"+4632#"&&#"&47>54&'&7672F%';A)%>1b \^b2/V Z%=D&#@DV{9 11 8{V-#"f 0 @U= >H+462"&%462"&&#"&47>54&'&76729N:8R7Z:M::M:1b \^b2/V Z)9:('::')9:('::{9 11 8{V-#"f7- /K/PX@ bU=M>@bUQ>YY'$($"+4632#"&4&'&7672#"&54632325E%'@3bU K= N =N>Y@ =742+&#" G F#! +4+326"#"&5463232764&'&473!673 #"&#"&47>54&#Ȉ4Ibqs95%`LDT+%? !;%-5q {VfFb2w+y&^ b2-)5(PXVNF1#3N+eJ1  27}e}1 8{{fH=sIS1@. VK= M >RP'GCGd! +32#"&#".7>=!&#".7>54&'.7327!54&'.73274+326ǿ))s]  T+d-RZu^  b11b  \obT+-R  ^pZb2ZȒ*J&H +&#"&47>5#&7467354.'&7672!!767632&#"&47>54'&#"+:  sWZ 3-& }j-7 i+u57/ZR^sT+%G{#JWI 1  1  'DB$%BA&*RV%FHy9 1 1:y7m1Jy&=b XN @Cdd   b  b UK= M= M  >Y@UTPOJHFD@>870,)(XX$! +632'"&547&/.'&#".7>54&'.7327267>32#"'&'"2f ?2' K;2X!V^-RZu^  b11b  \obT+dk'3F+1C1!'5'?8N\/9^/' %fDA{9 11 8{{{91  29y^`+>C#9#1gJU3! 1=sb O@ <29K!PX@8b  b  ` = M = M >@5dd  b  ` M = M >Y@OOKJGC@?:94C5$%+#"/763267327&#".7>54#&#".7>54&'.7327%  ,?F?)R?  b;;b  C^  V=$-PD  b;;b  G_ V>^)/~4@ 2Dz{C 11 FyI&1 D{k{C1  2Fx-%V5@2<dU=N>MJL!$#('$ +"&5463232654'&54632#"&54632327676'.'&4732776&'&4727%̐-#!%9-+;+%)}Ts)73' #F!16';?RTPP"<> {R F?!+R`bP&" #53#  $$+!=;dXF{[61  2?@V:81%1  27Df=RsC=@:9ZK=M >9851(% CB +!#"'+".7>54&'&47327;2654&'.7327&j)= % ]b{  Z1+Xs`jgT+!77+R  {Qb| Z24X |p 1 @3 bbU M  = M >Y@ZXSQJHEC<:'#'%$332+4&+"=7%#"3265#"#"'4632#"&'##".532#"&54654&#"32653<7P5qysF/?5^t/Jqw9 9wqH-u_5= /Fsw1)&&ZjV P# 8+?RwpdV]ZZ]VewR?+7 #VFU @3 bbU M = M >Y@TRLJCA=;75%$%$332+4&+"=737#"32>5#""&54632#"'##"5432#"&547654#"32'd'1'J'1'D]+%'=)VTPqBBʦTV)'%qf+^D/--''+/?VB'+!5RmӚ+R5!+' ٴBVR+@M@83)(&%K-PX@) U U   U M >@/  Z U U   U M >YY@"AAAMALFD=:741.,*#! @@ +257.#!32!"&#'54;265#"'67!54+"'573%+"32>54&#/-5 !=ATNb4;=.TA>! 5-o7P 7=2<{oP!ðf2 {D;'+6D{ 12Z''+-4N5=dc5q/9C@@+* <$#!:UK=M >#"%#3S +3 #"&#'54;265#"'67!54'767!.+4+326q)+'1'@wS7H29k<SyaYÖBL\o'-/Zm+PV+oϙP3*PD[@, 8 L @W   b  `   b b U  I  UM =K =N>Y@[ZXVSRPNKIHFCB@>=;+"CC+%&#"&47>54&'&473273!2'.#">3232?2#"&#"32762# #@r PR s??s GV s? aj'!6afBTRB;/((ZH:hb5/ў{; 11 <{5{;2  1 ;{s.t%1yLlg7@-1N'HB.ǘTmQ9)_@" 2 : O@\   b  bb`  US M =K=M =N>Y@!\ZVTRPNLJHECB@><8643.,3353#2+#32�'54;2654&+"'5737+"3>32#.#"63232632#"'&#"!2762#".<^)S)3))3)M)^5+rku^J7?6+-tB:61.!,o.#: NzI\//./9--00Z#^myϞ9 +iN$Rj0cDS+@(PE@-( < = >DA86,) + >&#'54?>=4&'&#'54?>7672&#'54?>54'-b}H'1'P'3%f` `'1;@b! !V;DP%%"`i:1151-)//)-R?8/ // 13T=>o  //?T19-@*6/*  <= >99.+#! +67&54'&#'547>7632&#'54?6'͉+1y)  ' (S$-!;Js=Z!{NysIj& 33 ?033R`  33 3-j {Dy@ v kfe-(@% U  S =K  >Y@jg^\ZYVURNKJEDA=:965,) + >&#'54?>=4&'&#'54?>7!&#"&47>54&'&47327!672&#'54?>54'b}H'1'P'3%f` a'1;@@r PR s??s GV s?+H! !V;DP$%#`i:1151-)//)-R?8/ // 13{; 11 <{5{;2  1 ;{T=>o  //?T7bg@d@;_ XS-(<S =K=K  >bbWTLJHGEB?<962/,)&#!  +67&54'&#'547>?!32&#'54;2654+"'5737+"!632&#'54?6'%+1y)  ' (SY&Q'1'X'H&Xb%- ;Js=Z!{NysIj& 33 ?0\//./9Z00Zq33R`  33 3-j V)RU@SKF3. @%S U K  >Y@$UTPMJGDA?=852/,)%#  RR +";2&#'54;27>;&3!232;2&#'54;26'.+;2&#'54;257!Po-0A+J\-=G:#F= j]>)^O-B(/mPnn#P q{D^1)''Zcn)nZ'''3c^DZ''ZV boRU]@ZSKF3. < UM= K  >UTPMJGDA?=852/,)%#! RR +";2&#'54;2?>;&63!232;2&#'54;26/.+;2&#'54;2'7!Hd?F;#GJ?D=N}X@B!;oZPDEP#?#!MJ\FVqPqH^iZ''Zi\V9a)ryiZ''/+i`FZ''ZN; -zK1PX@pkWR?:<@pkWR?: K1PX@+U   U K  >@7U   UK  =  K  >YY@%zyurolifa^\YVSPMKIDA>;851/2#CC+ !&#"&47>54&'&47327!3&3!232;2&#'54;26'.+;2&#'54;2'+";2&#'54;2767!s{@r PR s??s GV s?H9%H= k\=)^O-B)/lPoo#P!q\Pn-/B+J\-3< {; 11 <{5{;2  1 ;{)nZ'''3c^DZ''ZD^1)''Zcl67us}@=8  vq^Y*$<  U  M = M = K >{xuroljhc`]ZWTPNLIFDB?<963/,)('%" }} +!#";2&#'54;2?67!;2�'54;2654+"'5737#"!3&63!232;2&#'54;26/.+;2&#'54;25}THd@E;~#GJ?CQ4X'Q'1'X'H'X?B!;oZPDEQ#?$!NJ\FVqPq;wH^iZ''Ziz,\''-/JZ''Za)ryiZ''/+i`FZ''Zo%ZX@   < ;K*PX@EZ b  bI U  U M =N>K1PX@Fb b  bI U  U M =N>@Gb b  bU U  U M =N>YY@XXSQMKGEDB42/-+)"#$+>7737632#"'&"27672#".5467>54&#"#"&54632654&#"o)B% L5J'/2 gb|xXB1=ϞBtuE˸Djf8yDq"%dBgFnB9f-yT-!3!&@es/ !)?R=>?X4hJ{//LyNyi#31q-9qFCRjMC@    K*PX@?bZ  bXU  UR M >K-PX@@bZ  b`U  UR M >@Abb  b`U  UR M >YYY@LJGEB@?=530.-+"#$ +67>7'737632#"'&"2632# 5467654&#"#"5463254&#"RD)z D+E+++ ZqfF9F-כ))+JX1FBZ2sH5RN+3#+ kjBl-W9m37sh<#Pdw#9TVXD)/RP-T+Ry@C>J6#@bUK >Y@HEB?<930!RR +32;2&#'54;2'5.54.'&54;24&+"'573%+">5465Q5qT|F-)@3=5P5qMt9"RH1bJ{@ k/+''Zl ;q\{9 jA[/+''ZftT#FfN:@7 D8$0+<OK=>HE33<;33+%4&+"'5737>54&54732;2&#'547326=.54654'&74;2B'2&JLs\'PRX%P&1(^yC!R)\q)--%p)& [7A%Z''--;o{7Z ')LTD*N@K $<UUM =M>(&#!   +">323267# ! #"&#"328>m73|0VT!vobHW*.`%"{h'3e^7: LZ.A@>(<UUM=M>!%%!$$("+432#"%.#"6323232654''"&#"LͼhV)-Xfj~kFa)=`/w'1g";d>PvoL/!RVRX r^&'6?{qE>7ZD1@ $.K(PX@'bM =K =>@!bGM =>YY@)&#  11 +2#"&54654#""'&'#"=737+"6?i9'%+'9+ZL%#m!h P7b;o|D_=)R+#8)+N/0|^''6!'1?+0~@ @(bM=K=>Y@*($" 00 +""'.+"'5737#>32#"&54654s-T!.+mE#;w1yi=X))%37f/+7+)'! /BTX=)<)!#ZP(K1PX@C> M<@C> MK(PX@3ddb M  =  K =>K1PX@-ddb  G M  =>@5ddddb  G M  =>YYY@ HEB?<964.,&$P P%&&" +4632#"/&%4632#"/&2#"&54654#""'&'#"=737+"6+)z  4)o !?i9'%+'9+ZL%#m!h P7b;o|P1 %!-  _=)R+#8)+N/0|^''6!'1?+F Q@ 61@ K&PX@9  b  b   =  M = K=>@=  b  b  =  =  M = K=>YY@$"!KIEC;852/,(&!Q"Q   +2#"/&546%2#"/&'46""'.+"'5737#>32#"&54654% +9#   +-T!.+mE#;w1yi=X))%39 ,   /f/+7+)'! /BTX=)<)!#m?\8 K PX@RXb bbUVU M  = M >@Qdb bbUVU M  = M >YY@%^]|zsqpnjha_]^ZXSQJHEC<:'#'%$332+4&+"=7%#"3265#"#"'4632#"&'##".532#"&54654&#"3265232654&54632#"&#"#"&5463<7P5qysF/?5^t/Jqw9 9wqH-u_5= /Fsww? E'%-P=53/+!'-q1)&&ZjV P# 8+?RwpdV]ZZ]VewR?+7 #V5{3%#<-;\{'5)%;qF+#y?0+K(PX@Q  b  b  bUVU = M = M  >@Qd  b  b  bUVU M = M  >YY@.xvpngea_[YXWVTOMIHB@;952/,)& ## +232654&54632#"&#"#"&5464&+"=737#"32>5#""&54632#"'##"5432#"&547654#"32'N? E'%-P=53/+!'-qd'1'J'1'D]+%'=)VTPqBBʦTV)'%qf+^D{3%#;-;]{'5)%;q^--''+/?VB'+!5RmӚ+R5!+' ٴBVm?D\ @3 bbU M  = M >Y@ZXSQJHEC<:'#'%$332+4&+"=7%#"3265#"#"'4632#"&'##".532#"&54654&#"32653<7P5qysF/?5^t/Jqw9 9wqH-u_5= /Fsw1)&&ZjV P# 8+?RwpdV]ZZ]VewR?+7 #VFU @3 bbU M = M >Y@TRLJCA=;75%$%$332+4&+"=737#"32>5#""&54632#"'##"5432#"&547654#"32'd'1'J'1'D]+%'=)VTPqBBʦTV)'%qf+^D/--''+/?VB'+!5RmӚ+R5!+' ٴBVB#gK PX@$XeUINB@#deUINBY@ ## +232654&54632#"&#"#"&546?!F'%-P>53/+!',p{3%#;.;\{'5)%;q!d+ <K-PX@ 3.<@ 3.K-PX@) U  S UM >@/Z U  S UM >YY@! < <;:852/,)'&%$! # +32>54&#32!"&#'54;265!5!54+"'573%+"!P?uT#ͺ˪Rh5A?0u;U9w95=dc5e;'+6E:Z''X@.  UU  =K=N >Y@ 0 0-,*'3S!"" +%4&+2!!3 #"&#'54;265#534&#'"5'6767hpBR7735)"F ?X=4To\[F'-/F'R9 1 7:K-PX@)%"$#<@)%"$#@%ZUM =K >Y@ C+c("+327'7654&#"4&'&4732632''"'&#"&47>5%wL7?d?s d)'^'nv #"!<M=M=L>C*%('+327'7654&#"'7632'#"'&&#"&47>54&'&7672f! 0afD?Bg+{PЗ ;_|dN 7q qV`4/V d )16(+61dL55{: 11 <{jV-#'!-j@  <:K(PX@ZL =K >@ZUK >Y@*)$# -- +!23>76'.+"&#"&47>54&'&47- EW  3L7J@r  djs??s)^ m DAPFi{; 11 <{5{;2 =1B-dK&PX@#%<" :@#%<" :YK&PX@K= >@ZL= >Y54&'.7!23>76'.+"}1b `^  b11b  ^ >/!" hs-3#u{9 11 8{{{91 Uq m mN-/}@&  Z USK >Y@,+('&%"! // +!67'.+"3#&#"&47>5#534&'&47-P 3L7J@r  djs??s) DAPF P{; 11 <{RP{;2 +1s1pK&PX<@"ZSL= >Y@11G7C +54&'.7!7'.+"3#&#".7>=#&71b  ^ hs-3#1b `^  b1y{91 } mN\y#-{9 11 8{')sd-H8 @: Zb U  UUOK >Y@DCA>62/.C#"" +%#".#"#332654.'&#"&47>54&'&47!67'.+"2dJj7 /1@J[}@r  djs??sP 3L7Jhϑ`B7P;Dr/<{; 11 <{5{;2  DAPFmKy=!s?K&PX6<6@1ZbUL= =M>Y@ 7CG$' +#"&54232%&#".7>54&'.7!7'.+"}LmJ˰ L?{1b `^  b11b  ^ hs-3#>ctLZJ-78i {9 11 8{{{91 } mN\)5r@ b# K(PX@K Z  bb  U  M  = M  =M >K1PX@L  b  bb  U  M  = M  =M >@G  b  bb   U  U M =M >YYY@#~ljdc^\ZXUSMLIHEA>=:931"&&C+%&#"&47>5&7>7>35&.#"#"&46324&'&4732767>762#"'&"232'&'.'.'.'@sQR s?OaH*!PmdZ-d+ %! -%5HI' H92?s GVs@29H 'JH5%-!!) +d-Ch:;  Q "- Cd=!*HbO{; 11 <{LRV˼# su B&'-+ )RAH`qA{;2  1 ;{q`HAR)!Cqpkihfb`ZYRNGF$!%4G+%'&'&'&/.'&#"&47>=.7>?635.'&'"#"&54632354&'&47327267>32#"&#"2^4 /&H,)Xhb-TZqo^ T-biX16(  ;1\N7>'5))!0C1P^'%qf-T ^mjcT-fq%3F+1C1!'-5'?8N\ $K r:0,T?Fy; 11 :yF?f1 !4YIf1#9q^^`y;1  29y`^+>C#9#1gJUZ#%DKK(PX@0<;   <@0<;   K(PX@6bIUM = M = M  >@7bUUM = M = M  >YY@KIDA(#!"!$"-" +772654&'7&'.5463232654&#"#"'43232654#"'676$32#"'#"!DE5Hm`f81-%=5Tӓ#P;<W뢰-%'Zėu$KWoVJ?)h2+A0%,j} 41 s >y! Ç2PRZXF#HK(PX@2=<A<@2=<AK(PX@.bU Q M =M>@4bUU Q M =M>YY@HF75#!"!#$'# +6732654&'.'&5463232654"#"543232654"'7>32#" LK54FGBi2F7=D7hWC)'JX:ul%@DZHT`uP@+P5:&A-)\_ +- dR}p 5s^R^##"%xFfbDRK(PX@>   b  beUK = M =K >@<   b  be UU M =K >Y@MLECB@<:CC +%#.'&/.'&#"&47>54&'&473272767>32#"&#"2:963TEtQ@r PR s??s GV s?7<%FpBHC9!j@#AX5'MsZELm3'fu]T{; 11 <{5{;2  1 ;{RI\<@+#33G@?PyP@-cBwLd@a FA <Z  b  U  CM=N=  K >KHEB?<980/#!###333+74&+"=737#"3267632#"&#"22#&'&'.#;2&#'54;26%3sI1''^{D^hj/#G*\sDqNB /lh_;^F'1wP1'N/''')-PP!/XJE+96@/-''-5M@,) > MK1PX@> b  b I   UU  SM >@A b  b UU  S M =M >YY@IGED@?:8641/+*CC+%#&#&#"&47>54&'&473272736762#"'&"'.'&'}Z9c@r PR s??s GV s?h4Zb#'JG5%-! )!*`{+ZemP<,b{; 11 <{5{;2  1 ;{ mLAR)!CPNKHFEB@><86.-,+#353#"+%#&#32�'54;2654&+"=737"353>7>32#"'&#"232'".'.'RZ:OhuO'3%'1'J31ZCM"1<#79-$ !/Lr;qN/V%=-V'')-P--'' :_K12I!3)/ f'^JA1275P> K1PX@@  b  b I   U  S  UM >@C  b  b U  S  U M =M >YY@POIGED@?:8641/)('&CC+%&#"&47>5#534&'&473273#27>762#"'&"2'.'.#@r PR s?Aq GV qAǞ59H 'JG5%-! )!+d-ZemP#*NkX{; 11 <{Pu;2  1 ;uPq`HAR)!CEC@=;:7531%332+%;2&#'54;265#53&'767!!267>32#"'&#"232'".'.'m)1uO'3%19?nq+$P-7<-! /Lr=hT/V%&]j-)''+1@Fi+PV+iF!i_?GG3)/ f'^JAb\7`g@ 3 `K1PX@E  Z  `  b  I U  U M =M >K2PX@F  b  `  b  I U  U M =M >@E  Z  `  b  I U  U M =M >YYY@^[UTMKIGDB;:851!$332 +'".'&'.';2&#'54;2'4'#"#!33%+"27>7>32#"'&"2;2;XN/+ ' 49O1q5P6qV+1##%wa'5q75[$-{w9!1-%B"D\73N?!Z8/wdi 30L#.Z''Z1RRdV'ZtrNLmt^#3L(;UsO'V?>'1^Ui@f%U L <   b  b  ` `K= M= N  >OMJGEDA?=;'3a%332+%;2&#'54;2654'##;37#"2>7>32#"'&#"232'".'.''2uO'1'>5/@+#%-:C+1(Lo/1;#7:-$"(Ls@+   U  SCM >Y@UUPOLIEB?<96320-#33333+!0'54;25!;2&#'54;254+"=730%+"!4&+"'573%+";2#.5qb3>5R7oo7P5q4=5P 5q/! 9-9Z'\1+''\Z''ZO/+''Z9'!?W=-BJsPT@QIE40 ! <e  S  K  =M >NLHFC@=<973153#33!2+%;2##'54;265!32''54;>54&+"=77#"!54&+"=77#"/ + )=҇ +7]6(  5555  (56+  359()+ #>=$ +- %<=;% -+ #==% -- #?-PF @(  I   U  SK >Y@LJD=:96521CCC+%&#"&47>5!&#"&47>54&'&47327!4&'&4732763!'.#"?s TRs@C@r TV s??s lo s?@sU=hL; *- {; 11 <{i{; 11 <{5{;2  1 ;{H{;2  s%Q=sQm@*S K =K = >Y@QLIHCGJ$0 +7;03.+&#"&47>=!&#".7>54&'.7327!54&'&473:3b Y)5h^!1b  CJ`+J+`HB  b11b  ?B`++`;Nq{ GX{9 11 6}}7 11 8{{{91  25}}71 s^-ZK(PX@@  ZbUUO L = K  >@>  Zb  UUUO K  >Y@VURQNIFE@?<8540-C#""+%#".#"#332654.'&#"&47>54#!"&#"&47>54&'&473!272^Jl8/1@JZ}@rnm s?^l^@rdj s??s ;;s?h͑`B7P;Dp1<{; 11 <{E{; 11 <{5{;2  1 ;{Ky=!sU"@: ZbbL = L  =M>Y@POJIFB96-)&% US +27#"&543232%&#"&47>54&+"&#".7>54&'&473b{ Z1JfC˰MP !A/1f stjgT+!77+R {Rwz  h71Z {bo 2;yAdnLZJ#'7i{9 1 1A?/-#! 77 +2654&+ $32#"'.#"&5432>54&#"fuP+/+Λ`P%!B1G9/BDdP%m2jhBXv^e$XJs5=O-m-N=>!1Z16 }ӗ NV 3XVFHu0;@ 1%@1bUUG UK?Y@86*)! 00 +254&+ '&5432#"'.#"&'46 '>54&#"8<-u~%ГWD 7+#P=4#٩VotE_LS;V+]D8-) =1Fsw¨ {}L#D1B@?&<bM =M=M>%2%',"+772654&'&547632'!"32672!"##"FE5Fl^rg'hz Ǐl JVnVJ?)h7Y_[ Xiw2ORZL+/.B@?&(<bM=M=M>)$$$$#+732654&'.5432#"'.#"327#"/)7+5FCM@$FA:fri! emV)#V+mo  lV+0J-3/¦ׇG^eX/b@(' <%:KPX@eM =M >@eUM >Y@ 7743!2+%;2#+'54;254&+"'3!2657.+"/# 775q-8XF1D5 m 6C/GX7,72'\/#Dw =  wD%17Z5@@=<)(#":bCM=M >(3#2$2 +%;2#.+54;>5#"#3!257#.+-#-9hH T5 )4)f)5)  9,9R3/ !> !@B)-<HK(PX@K =K >@UK >Y@ 33C+%&#"&47>54&'.'&472776&'&4727FPV F2HF { U& $H or  JO) {; 11 <{78/U0 0  1 *B]>*2  1 3JF*?3)sB?@<(# /<7<K= K>A>35339$#5 +&'&+"'57307#" 654&+"'5737#";2&#'54;26+ <3 ; .N&'1'P)1%)= )'%! )'Z#/+''+-CcK(PX@#  SK  =K >@! U  SK >Y@CBA@523C +%&#"&47>=!5!4&'.'&472776&'&4727!!FPV FL2HF { U& $H or  JO) >{; 11 <{R78-U0 0  1 *B]>*2  1 3JF*?3R)sIU@R,' C> <K= K  =  K  >HEB?<96543339$#4+'!5!&'&+"'57307#" 654&+"'5737#"!!;2&#'54;26 H+ <3 ; .N&G'1'P)1%)P= )'%! )'ZsP/+''+3+p@YQ2@3  b`   UCM >Y@!pplkig^[XROLGFA><;s93$%3+%0'54;254'&";�'54;27654'.+"=72326233+"276'4+"=723263+"32#.+ 1B' <& `P<)9- k} R/%+= #/N/ kP#7 71N)6)' ))o#7.))4f+))+B{#+%m)+Pi?ske@bRM62<  b`C K =M >kkgfdaWTQNKHBA;9#93#5%3+0'54;254/&#";2�'54;26?>'.+"=77#"2?6'4&+"'5737#";2#./7y #2jD+;'!@1!/!}  !b>1?)-'#) ) 3Y' X5'#-4 +3-'%0/ '''9 B)7HX-yM@-)F < :K(PX@- OM =K = M >@&G OM = M >Y@KHE@53#55662 +4&+"'3!257.+"3!2654&'#"=77+;2#.#$!#'54;>5+7XF0B7!1!5D/HX7+1'37>  >3/#5}{53/'Cw @11 wC$2=!)57& -- %8 7.χ#!>FYs@p1&POLIFE?;630-('$! YX +"3!2654&+"=77#";2#4.#!'54;2654&+"#3!257#.#h/456+  35." + '9b>e !75A )5)d)7+  5!=;)+9!=% -- %=9*:M@-+ ;F;% !?B);+B|@!1@%e  UUM >Y@@=;974%2"5%2!2 +%;2#!#54;265#"&54&+"'57+"3274&+"=7+";/#7' P5om;# "=tu5> <550- )8qEœT7 ++ #3ygs7 -+ 7DsEM@J>:%!2 <eU K =M >CA=;85%$#4$3!2 +%;2#+'54;265#"54&+"=77#"3274&+"=77#"f-#)/ !5^5  3!7V`5!  459*+ %>7;% -+ #3woq=% -- %= -F~@ FD!@(bU S  K >Y@A@=965CC" +#5#"&=4&'.73273674&'.7327&#".7>5]@r  djs?__]v@r lo s??s hj s?Ǧ{;2  1 ;{ogZ M\{;2  1 ;{{; 11 <{XsI;@8G$"I<USK= >MJIC"+%#5#"&=4&'.73273:336754&'&47327&#"&47>=9Z%$_1b ZobT+/\ZBV-R^sZb11bb^b2^:nwX{91  29y#`K{91  27{{9 11 8{E##+Ex@ ' 83 @"U M= K >Y@DB=:34#334333 +;2&#'54;2654+"=73%+"$3 ;2&#'54;2654&#"3>'R7=2o7P5q-+3+V7'w5/)''+-Z''ZOQ/+''+/{}RB=sEG@DE' 94 <  UK= K >DC>;35$334332 +%;2&#'54;2654+"=737#"632;2&#'54;2654&"j'2P'1'X'P1(s'1'P&1'Z/+''+/JZ''--/+''+/ uWD-6P@M<UM =M = K  =M>...6.654$$"#$$&# +%#".547#"&54632#";6$32!326.#"3/ӠN @{H#)$A<2w ρ1/朕#'1{gw}TNOd`!-<)l5mٝ5)6K-PX@4b U M=M=M>@9b  I  V M=M=M>Y@31.,+*#""$#%" +%#"'&547#"54632#">3 #%3263%2'4&#"#n`o?"%!90qZ'AZVm$^dF5K=o2.V)5%[#m1 ub*hTD>Gd@a43> <U Q M =M = K  =M>???G?GFECA=;$"#$$&%!+#"&547#".547#"&54632#";6$32!3267327.#"3yXVo@KN @{H#)$A<2w ρe/qQ\]D7?J/朕#'݉ZRv} w}TNOd`!-<)l5m`1h/GX+?Jٝ5<I@ 0  @H  b   b  I  V QM=M=M>Y@FDA?>=<;86/.#""$#%3!+#"547#"'&547#"54632#">3 #%326732>723%2'4&#"Vn`o?"%!90qZ'AZVl<#LbLDg0^dF5K=bno2.V)5%[#m1=c,Ab9`- ub*hB-!AK(PX@K =K >@UK >YCC+%&#"&47>54&'&47327@r PR s??s GV s?{; 11 <{5{;2  1 ;{sB|= K1PX@Td  b  b UU M = M = M >@Od  b  b U VU M = M >YY@-~}xvtromgfcb_[XWTSMKHFDB<;76&C#'+2654&54632"&5432&#"&47>5&7>7>35&.#"#"&46324&'&4732767>762#"'&"2'.'.'=cA#!LٓK!#@sQR s?OaH*!PmdZ-d+ %! -%5HI' H92?s GVs@29H 'JH5%-!!) +d-ZdmP!*HbO+%DLMMLD%{; 11 <{LRV˼# su B&'-+ )RAH`qA{;2  1 ;{q`HAR)!C ~wvrpmlhgb`_]YWQPIE>=97310.)($# ('$+"&5463232654'&54632&47>?635.'&'"#"&54632354&'&47327267>32#"&#"2'&/.'&#"&47>=͐-#!%9-+<+%)@) ;2\N7>'5()!1C2P^'%pf-T ^mjcT-fq%3F+1D2 '-5'?7M\1; 52Xhb-TZqo^ T-bhX1R`bP&" #53#  $$=1 !4YIf1#9q^^`y;1  29y`^+>C#9#1gJU3! 1f?Fy; 11 :yF?f#s7b@94 [ &!K(PX@O Z  bb  UUO  K = M =K >K1PX@M Z  bb   U  UUO M =K >K2PX@N  b  bb   U  UUO M =K >@M Z  bb   U  UUO M =K >YYYY@a_SQOMJHA@>;852/333""+%#".#"#3 654.#;2&#'54;2654+"=73%+"27>7>32#"'&"023wJj7 /1@J`3>5R7=2o7P5q75Z%-yw9!#--%B40L+B7P;Dn-/)''+-Z''ZtrNLmt^#3L(27g?BaF!wYl@i=8 )$<  Zb  U M = N =K =M>TRQOLJGEB?35333$(+#"&54232654.#;2&#'54;2654&+"=737#"3267632#"&#"Fz~Y9괖 L?q9X{u?'1uO'3%'1'J1'$`|Abgh/#G+)p6 "GcaLZJ-78æj`>-''''-P--'')-PP!/XNh#Hw+R@72" K(PX@:   bS  UO K  =K >@8   b  US  UOK >YY@QOLKJIFDB@<9630-3#3333+%!;2&#'54;254+"=730%+"!4&+"'573%+"#".#"#3326b3>5R7oo7P5q4=5P 5qJj7 02?H/+''ZZ''ZO/+''ZB7P;DBm;sPX@U62! <   bS Q K =K >NLGEB@;9533$#53#3+%!32''54;>54&+"=77#"!54&+"=77#"#"&547232>]6(  5755  (56+  35͘NP  B/Nj+=" +- #<=;% -+ #==% -- #?уMXH!+5-M{K(PX@+e  S  K =K >@)e   U  SK >Y@KJGC@?<;8740-,CA3+%"&##7&#"&47>5!&#"&47>54&'&47327!4&'&47327?s pXqR1Rs@C@r TV s??s lo s?@sUb s?{; 11 <{i{; 11 <{5{;2  1 ;{H{;2  1 ;{=ssM1@.eSK = >HDCGA7 +"&##7&#"&47>=!&#".7>54&'.7327!54&'&473271b  nZq<'J`+J+`HB  b11b  ?B`++`;G  b1u{9 11 6}}7 11 8{{{91  25}}71  27;+Bx@ !1@%e  UUM >Y@@=;974%2"5%3"2 +%;2# #46;265#"&54&+"'57+"3274&+"=7+";5P )7 #+om;# "=tu5> <57* -+32 qEœT7 ++ #3ygs7 -+ 7DsHM@JA=($5<eU K =M >FD@>;8%$#4$3#2 +%;2#"#46;265#"54&+"=77#"3274&+"=77#"f5! Jf7- !/^5  3!7V`5!  45=& +-TL7):7;% -+ #3woq=% -- %=s W~KPX@-dU   VM = K >@+dUS   V K >Y@USIHD@76/+  #' +2654&54632"&5432!#&#"&47>727&#"&47>&'!"bB# LٔL!#Rj 2nLk NZ%-D?63 [u!wP%+%DLMMLD%D- ^  11 4XJ)l/1  11   [J(Va@^PR<db  ` U M = M =N >USOMGE(("'('$ +"&5463232654'&5463232765## 546?65#"#"7463 327'"&̐-#!%9-+;+%)LKHFn)op D`8)H5 %'NuF9R`bP'# #53#  %%+17[[m Vf'+ ++ /JV+1:!(L=sMvKPX@)U   UM = K >@'US   U K >Y@KI?>:6-,%! +462"$462"!#&#"&47>727&#"&47>&'!"9N97R#9N::Nj 2nLk NZ%-D?63 [u!wP%R::R77R::R7- ^  11 4XJ)l/1  11   [J LY@VFH<b  ` U M = M =M >KIEC=;(("' +462"&%462"&32765## 546?65#"#"7463 327'"&9N97R7Z9N::N9TKHFn)op D`8)H5 %'NuF9)99)'::')99)'::j+17[[m Vf'+ ++ /JV+1:!(L=1]@,=:A Q <*:K(PX@?Z  Z  U SN = M= L  >@=Z  ZU  U S M= L  >Y@\[XSNKIGCB#7CK+!4'!&#"&47>76&'&47)272'.+"326762"'.+3!26&#!&47>+D /#n L\-!8di5/!"'Vk#&}9 21 RTII'!3bJes@q)+1' 11 8T/A2 F{d3'f1[ {6% 70%X 1 <P/9ENU@R8 :#<  b  U M=M=M>MKGFB@'%""#(! +&#"#"54632632327#".'&'"'&54676%&326767>54&#"+ZB\+%RǁPhTpi-bV1#}BdI8zgLVT1 dFd--+ )\XyDXd9{Vh9-' $9=u!HL8IFSh=1J^3\9s\@R $5@Cd  Z   Z U   VUM= L >Y@ YVONMIFE@?<72/,*&%\\#'+2654&54632"&543226762"'.+;26&#!"&47>54&'&473!272'.+"bB# LٔL!#}:1  1RT3@J' 2cHss??sd 50!#'VjP7+%DLMMLD%1[ bOJj 70%;H9 1 <{5{;2 F{d;HLB(?K@H<d  bU  VM= M>><97%"#"('$ +"&5463232654'&54632%2'4&#"#"'&543 #%326Ô͏-#!$:-+;+%);sdF4L> #n`Z&AZVmR`bP'# #53#  %%kub-n,o#m1VD5@2<SM =M>$&+'> !".'!65#" 67/#"%"# +"32>&'632# 434'&'"oeE5L>#o`'!BZVm+ub.nod#l2Vu)/C@@<U   SM =M>***/*/$& +462"$462"'> !".'!65#" 679R99RD;N;;N/  bU  U M=M>42/-%"#" +462"&%462"&"32>&'632# 434'&'"9N97R7Z9N::N9keE5L>#o`'!BZVm)9:('::')9:('::ub.nod#l2d 6v7 K1PX@P  b  b UU M = M = M >@K  b  b U UU M = M >YY@-}|xwrpnliga`]\YURQNMGEB@><6510&C+462"&%462"&&#"&47>5&7>7>35&.#"#"&46324&'&4732767>762#"'&"2'.'.'b:M::M:Z:N97R8)@sQR s?OaH*!PmdZ-d+ %! -%5HI' H92?s GVs@29H 'JH5%-!!) +d-ZdmP!*HbO)99)'99')99)'99{; 11 <{LRV˼# su B&'-+ )RAH`qA{;2  1 ;{q`HAR)!C|{xwtslkd`YXTRONJIDBA?;932+'  pp +3.7>?635.'&'"#"&54632354&'&47327267>32#"&#"2'&/.'&#"&47>=462"$462"ߘ(  ;1\N7>'5))!0C1P^'%qf-T ^mjcT-fq%3F+1C1!'-5'?8N\1;52Xhb-TZqo^ T-biX29N97R#9N99N1 !4YIf1#9q^^`y;1  29y`^+>C#9#1gJU3! 1f?Fy; 11 :yF?fmR99R88R99R8Z%o7?GK(PX@ !-,<@ !-,K(PX@6b    UIUM =M>@7b    UUUM =M>YY@GFCB?>;:+(#!"!$"" +7463232654&#"#"'43232654#"'676$32# '.462"$462"Z%=5Tӓ#P;<W뢰-%'Zėuo1-:N97R#9N99N%,j} 41 s >y! Çd+B&R::R77R::R7X#4>HK(PX@ "-,<@ "-,@7b    UUUM=M>Y@GFBA=<87)'#!"!#$" +7463232654"#"543232654"'7>32!"'&462"&%462"&X7=D7hWC)'JX:ul%@DZnF7:N99N:Z:N97R8&A-)\_ +- dR}p 5s^R^##O6%)9:('::')9:('::VPV)@ & K&PX@"b`QM >@(b`UIMAYY@#! )) +!2#".54327254.#"!'V9ZѬZ^-jLy{97dn 5+ )!3'-b@fH's`/Z+R%-@-K1PX@/bU==M=M>@-bUU==M>YY@ $#$$1! +%!"'6737!26?22#"&5463232654&#"Xw553-A-ʜw!!DR7XfygNl L9+F+PP\@ @  K(PX@.  b  U K  =L >@,  b  U   UL >YY@SQYVQ\S\NMJFCB<;CCC+%&#"&47>5&#"&47>54&'&473276767.'&47327!"7463!2?s PRs@  F\ PR s??s GV s?3" E\HV s?5{; 11 <{HDBJGBMDMAA=<954C4+%67327&#".7>54#&#".7>54&'.7327!"7463!2?)R?  b;;b  C^  V=$-PD  b;;b  G_ V>5~4@ 2Dz{C 11 FyI&1 D{k{C1  2Fx-%-#/w`@ P! ) K(PX@/  b U  K = L >@-  b U   U L >YY@^]ZVSRLKHGD@=<76CC+462"$462"&#"&47>5&#"&47>54&'&473276767.'&47327);N<QPMLIHEDAA=<954C4+%67327&#".7>54#&#".7>54&'.7327462"$462"?)R?  b;;b  C^  V=$-PD  b;;b  G_ V>49N99N!9N:8R~4@ 2Dz{C 11 FyI&1 D{k{C1  2Fx-R99R88R99R8LTu)5@2UM =M>(&"  +462"$462""32# ! %;N;;NB9R99R˨ǴVT!vM<$#+)#/$/&% +462"&%462"&47632'""32654&9N97R7Z9N99N9qzǓPu=sLZ)99)'::')99)'::1˅sւs EBTLTD*N@K $<UUM =M>(&#!   +">323267# ! #"&#"328>m73|0VT!vobHW*.`%"{h'3e^7: T.T@Q%<bbUM=N>+)" &# +47632'"">2327.'"&#"32654TqzǓPu"t^%+3kR-! )$?kDZ˅sւs E)s@9?woCLTy,:\@Y- 4 <U  U  U M = M>86310.+)%# +462"$462"">323267# ! #"&#"329R99RD;N;;NϨ8>m73|0VT!vobHWM<?=6431.-)($#&# +47632'"">2327.462"&%462"&'"&#"32654TqzǓPu"t^%+39N99N9Z9N97R7kR-! )$?kDZ˅sւs E)[)9:('::')9:('::?@9?woC^uB@|8 0 < b ` `  bbU  U M =N>><;:64-+*(&%" BB+462"$462""$543232&'"#"&54263274.# '676$3 ;N;;NB9R99RמFH-5bk9GZ((/;A!RN{Ag=# #FM<HK(PX@' <@' @Eb`b`    UUM= N  >Y@GFBA=<8731($"!#"#""+74632265&#"#"&5423276274&#"'67>32#"&462"&%462"&`'=#1q{/#129=D}/-:C:P`{qx +9믓J9N97R7Z9N::N9"Kd+ 9rk T#j@)9:('::')9:('::6B6K(PX@&b`UR >@0b``UINBYY@97?<7B9B"$+GF +.'&4732736&'.7327#"&546323267!"5463!24CU J` d-'%j  G9{1D.HDt7-="#&3U- 5/rA 0  1 BWdVR2  1 ;1.A0+7'-Vo%.#0%8D.@+U=N>;9A>9D;D/,L!$!+#"&54632327676'.'&4732776&'&4727!"7463!2Ts)73' #F!16';?RTPP"<> {R F?!+55+!=;dXF{[61  2?@V:81%1  27Df#%-#/wFFK(PX@'b  `U  R >@1b`  `U  I N BYY@ DB$+GG +462"$462".'&4732736&'.7327#"&546323267:R99RC {R F?!+9N:8R7Z:M::M:+!=;dXF{[61  2?@V:81%1  27Df)9:('::')9:('::UUK(PX@)ddb  `  R >K1PX@,dddd d  I N B@4dddddd d  I N BYYY@ SQ$+GH%&$ +#"&54?632"54?62.'&4732736&'.7327#"&546323267 z!)+ o)34CU J` d-'%j  G9{1D.HDt7-="#&3U-\ 1  -rA 0  1 BWdVR2  1 ;1.A0+7'-Vo%B8GVdK-PX@!b ==N>@%b = ==N>Y@USNLFD>=/,L!$! +#"&54632327676'.'&4732776&'&4727"&54762#"747632Ts)73' #F!16';?RTPP"<> {R F?!+#/   )!-+!=;dXF{[61  2?@V:81%1  27Df . ++ wNI&@*U  U U  K >Y@LJFEB>;:541-%C+462"$462"4&'.73273274&'.7327&#".7>5#"&59R::RD;N<POC*MH# +32754&'&47327&#"&47>=#"&=4&'.7327462"$462"L/\u-R^sZb11bb^b29H;_1b ZobT+Y8P88P"8P88Pu#`t{91  27{{9 11 8{)0/wX{91  29P::P99P::P9#+(U@ %@eU >Y@'&(( +#'54?>54&/&=73!.#!"#ɢ:B4+EB+Bld''3%)=/)/3! 1{$3!-9Xs(y@ "@&bbeK= >Y@$#(( +3'54?>54&/"'57!#.+#.33+@.U  U  U M >Y@!32`_\VRPLKHDA@862<354&'&47327 4&#"'4&'&4732763 !"&#"&47>:Q::QCa`\[WVRQCc##'CG +&#"&47>54&'&473274+3263 #"&#".7>54&'.7327462"&%462"&1b `^b21c]Z b1ɑ;Lbqw))^  b11b  \Z b1^9N::N9Z:M:8Q8u{9 11 8{{{91  275(Pe}1 8{{{91  27)9:('::')9:('::Dy "+462"462"+:++:++:++:o9))9+9))9+^D '@ &" "+462"462"$462"6462"462"}+9++9++9++9+:++:+9++9+9++9o9))9+9))9++9))9++9))9+9))9+LD   "+5!462"462"L+9++9++9++9TT9))9+9))9+1D  "+462"462"%5!#"&54751+:++:++:++:P#+:+%o9))9+9))9+VV^'++)\ `"+462" +:++:9))9+` "+462"$462"+9++9+9++99))9++9))9+F  "+462"462"$462" +:++:J+9++9+9++9q9))9+9))9++9))9+N"+5!PVVF} "+5!#"&5475P#+:+%VV^(*+)]RP"+462"R+9++99))9+RP"+462"R+9++99))9+HD  "+462"$462"$462"+:++:+9++9+9++9o9))9+9))9+9))9+ L"+462" +:++::)):+)`u{"+3#)LLX "+7!+)X%"+5!PVV^"+3u_L"+462"+:++::)):+)L"+462"f+9++9:)):+$" "+.'&'>7.'>7TF#1JNF 97, !  605;UFf#KH^4@V38) / #=HO+%HMJZo" "+4&54672>72!!&54%gX$/ l`]A =-;:R9 >)=RL;3?g%\+=yB="+#"&'#&'+&'47>54&5467'&547367&546?2oN3-0*'@%*KNR>NF2|"T!1LQ:Pq4/Z^7`L5Hs3=?1N'/\7DZ_VLV8Zj%w?N  '%#) "+23!7!654.#!"&546?3JmI2 X/1s))PZJU3L) H;yTR;g)R?BR \+yo*""+".'#4&54767>54&'&546?32߃!,#6N\-4'%^ZF)1Nf}+ ` ˉT n=P !!%DTo "++6'4&57!"74>?3!2 RǏ%13%TFH%AD-}+?!F/@)-TD}{3( "+'654&5432%'6'4547!"&546?3!2LJ1?-uLU/7%N3%%F=Q9DD+=++R?DQ %58?19k7y "+4&'.546?oHXL`3 u5/7 5 R= \>3A ;-XJ)/9N u$# "+74&547"54>?#"'6!P  5s5F@!//h;;+?!9 =_/'#beRG5Q'*H{.'"+654547!654&5467"&546?3!2+/N''yd'R(( ;T^b3P\d;@NX'A++L?@Tb/`CP\J;DRH9y2- "+.54>?#"&#!654&#"'7632!456FF cw #"-p^ZTHpH1q5JT%CH+TH ?F-@L++ =mrV};X#XD\q7y "+'654'.546?327 \TZ+6-+mGDDhLC=K%%V`Bf"+#!"&46?7!2#!/!FK-X;!m7J KJFwT 9Fq"+4.#!"54>?3!2#!7!26+J3 RR}6ufBX^7#)?!H=͕P6."+3!2"&547>54&#!"547654'&74>?DA\H3oNG-F+L+JuѪ1BF 9P#w/p{\m/0 #'+)qN^`VPo-%1& )=3`q "+!.#67#"&546?3!2y Ru?HN-O?݊N qTA?FHFL1%"+!7!654&#"#"54&'.546?6TwP>; J5PA #+E=B#_!)' D􉢸ͪwucs`'!S79FB  )## 3eD`w  "+47654'&746?#-7&z33H (!!! `N`)3 BOHD7!v`}9Z3%!1o""+!7!&'&54654'.546?=A\ `m~-J=);<%-f+Z'?g!51LR=BI- ;+9Xu* "+!32>54&".547"&546?3!2PV/>fD5Jdc1k/^wV>m?>/SA}}N`D%?h;^td/?HLo'D}MA5"+.54>?&">7674'&546? 7%654'.'&56 EV_1%! .?3!2#!7!26+J3?83A/ JB AFw RR}6ufBX^7#T5/' D&Z))<7\^ u)?!H=͕Db;"+%#54654'.54>?36%&54>?&h)=)qUF#3B9f kNT51\i7;3J%{+'3;D)A%%/A3#Cy-H T:Mb!Hn-y:"+!7!4'%&574#"&46?67.546?&'-V#D?!=RN\+ XP<8{Ik?Q+m!'-+>3Re!7=/NyNLH1T^/VŏR3BLH 1%^#1A[X=q4$ "+'473'"54%>54.#!"&546?3!2NF; 0E.!A/`HZ3T}Fd1L=DmY= RD1DM 'Ec1#-?'Z?;FH3BpJ9^\-+?Z5{u "+4&#!"&546?3!2 aHV1P7ўX?9HL'-Dyc"+!.'.547&546?"#36&54>?3&#!67654./&54>?3bys  '4}& .?d C!})n}  #N'9RGD5  d  #   JӘ#u+Db+y%dBS % ){q-d)>"?76L5  #T-F   !)u("+4&#!!73.54>7#"&546?3!2R`n: #} .3,,HV1Q8ўy^!wnRTbFX?9HL'-/y-" "+4&'.546?4&'.546?HXL`5 u5/7 7HXL`3 u5/7 5 R= \>3A ;-XJ)/9R= \>3A ;-XJ)/97y,! "+'654'.546?324&'.546?7 \TZ+6-+mGDHXL`5 u5/7 7DhLC=K%%V`R= \>3A ;-XJ)/97y+" "+'654'.546?32'654'.546?32X \TZ+5-+mHD \TZ+6-+mGDDhLC=K%%V`/DhLC=K%%V`fy "+2"&47>!;qF,0J#7j>-T\f "+2"&47>!2"&47>y;qF,0J;qF,0J#7j>-T\#7j>-T\B{3@-@*$ <b=N>>=86,-$ +4&5432#"&5467'.54632632654&'"?1)= \G44\J A8R< + 1bhX=UXoBA7c+>5Dl! vLodV4`6Fr /( =H{T5Ph@1^?B/n@ , @$bUIMAY@ +)&$  / /$ +327"&'#"&54?4#"#"&54632327^\7LX#-5H1B\wn3<F *!?1TC1/!P@{59]u -@B4@ 1 KPX@UQ>@#bUIMAYY@ 0.(&"  4 4% +>54&#"'2>3232654&54632#".=4#"&76_+17LX$-5G2B\w?02<E*GB' (!?1$0D1/!OA{5fD:\0aEu -R&<@9 &<;UIMA####%!+#"&5476327623#".#"3267BZo~VPu?9+ 5F:O?RPDsXR3 \))/k`'\}A@  & 6KPX@0  bI U  U   Q>@=  bbI U  U  I  M  AYY@"?>97530.)'!AA +27>54'&#&""5>32>323254&5432#"'#"&5476!4&BR>7 Xe0EhJ-}3i$6%u#T';]X F;G,?q{\9L;] D-)KBF&!/2)D)#3B5PXXyD"%\f /(F@C <9ddUIMA#*$%2+4&#'"/672632#"''653274&#"h+{:PG\ocZ7!eE+J1>5=(% 5Vw3ms1 'LPH>jz1h 1@@="<;ddUIMA4$$#"+5&#"326#"&5463254&#'"5'672"'&54D?7R}!`d)^m}qT9+{9!5m6  Pwl^?/:k5^=(% 5VF;$ J7m@USQ>Y@  +272#"'&'4632%4"BmE )Fk;M}y\!/u+N'P!5B4D{}Z3F%^=#{@*bUSIMAY@  +""'>32#"&'654'&3267mE )Ek@&bUUQ>Y@/.+) 00 +";2+"327>32#"&546?&54632#&T)7?-13jtF $*;Yb@5d}Vde 6'%+9myB4 ^T5I]BDB^hs00y@&bUUQ>Y@,)&#!00 +"#'7632#"'.5463232'4'#"=4;2654&H\5!dkSzd5AdP;*" Nmj31,@;h^BICXN6RZ 4B{j;+%%P12COWK-PX@,%  3! <@, %  3! K-PX@4 ZI  U   U  Q M >K1PX@5 Z  U  U   U  QM >@: Z  U  U I   U  QM >YYY@"LJGEC?><86/-+) 22 +"'&'"#"'272632#"&47&747&746326323274'&""#&4#"72>! 9VF+I%> y95ʉTyfBEFVF.1B%)o+:7H# #u31u#?BVb*?5/7`c@BLP-BITg7)!)@fd%!r:H96u (:@7&<b`UIMA*2+"&4624&'&7273"5651##1#s-<=>B+ $ }9"2""2a:   $  ; 77uJR@O <4/.<ddbGIL@IHFB;9303 +4&#'"5'6726?>&'&'5737&''54/&#"&'"&7>s+{:J33  =. X3N'CL1N\% 7=J^>B+=(% 5V44  )) 7T 7- *-o=Pd<#@# U I L @Y@POJIGD$B#&H +&#"&7>=4&'&76727632>32&#"&7>=4#"&"&7>=4&"5=B^=B*!5q5   N`n ^0#?=T?>3s/G1=>3-b?;;# Iqi&C=?ËT;?L?#\9dKPX@#bTQM >@)bUTIMAY@ "#&&I!+4#"&#"&7>=4&'&76727632#"&5432265s=F5=B^=B* 6q5   Nl)->-P+'-#HT?;;# Iqq-3'3;w/? HKPX@QM >@UIMAY@   +2#"&5464&#"3267ywZB9J`D1J{mkyylc7@ K!PX@UQ>@!bUIMAYY@  +"&547232654'&#"'632\i35 )%7_!/L%=!'Vy}R7;/#wsX3N!ywy/?BKPX@eM >@eIMAY@ "" +.#"#>32W;9Jy}hlsvy/?%@"dIMA"$ +0#"&=332654'?ywy`A1Jmk lzcXN'2@ 2('K&PX@#UUIL@@'dUUIL@YY@ #$$'2+&"&7>54&'&76727>32#"&'532654#"-;=>B+!5m6  f+Zea!Zt-Lh'q 9!;;# I '=tiwh\oV)@@=<:UIMA%#"#"+327#"5#"574326=732#+J! FlfR7+N s#H}w1FL%w3,@)<IINB'(%&+4&'&4727232754&'.7272"'&'4#"7q/^B f7J/^A  6m7   \3jF+#h+ʼnVF+#h+;$ 5!;ZP:4@11/< :+)9UM><6)<1+&+"'654'6;2676#"+"'654'6;2654&?<;# I5;Xd=@L@1o5=B^>B+!5q4  %q9HG#@=U?>3-1!Z Pb<#@!  U I MAY@POJIGD$B#&H +6=4&'&727"'&54#"'#"754&'&727327&'54&'&7725=B^>B+!5q4  N`n _0#@=U?>3s/G2==3-b?;;# Iqi&C=?ËT;?L? w,@de>H*C+&47327#"'.'&47327?6&^9)  "%^)^/W`## '|J##+1,)1+^5,@)*#<dQ>#&#,'+&'&5476772767#"'&'#"/?727676'qqbJJbqq+'%'1?"%P{RR{P%?1'%  1]Z3++3Z\2 #BbF55FbB#bP+=@: <9UUIMA$"$$)'+'47632#"'57254&'#"543>54#"T1D}\mfP{mh%/%Df;4A331+)f;H){EdVIu/k^hF1Ll O(uX0)F@ @eIMAY+%((+#"&567''&#"&'>32>54'&5472k1F @)&!QBR'# ^/5#B59N3K)8ZR#x+X,2Hp+ AA?@b-#$9'\t"-`@ #=4&4.#&76721##1#s-<=>B+% }99"2""29!; 77+J.iK1PX@#ZUIL@@$bUIL@Y@)'#! .. +%"&"&7>=4&'&76727632#".'&\#--;==B+!5m7  ;]%&$   @9!;;$ Iq--  w?3,@)<IINB'(%&+74&'&4727232754&'.7272"'&'4#"7q/^B f7J/^A  6m7   \3F*#h+ʼnVF*#h+;$ 4!; w?+@dd[H)C+&47327#"'.'&47327?6&^9) "%^)^/W`#"!'!{J#"+1,)1+b%P+=@: <9UUIMA$"$$)'+'47672#"'573254&'#"54;>54"T1D}\mfP{mh%/%Df;4A331+)f;H3){EdVJu/k^hF1Ll N)uX0J)D@ @eIMAY+%((+#"&567''&#"&'>32>54'&5472k1F @)&!QBR'# ^/5#B59N3K)8ZR#x+X,2Hp+ AA?@b-#$9'7g@ @!bUIMAY@  +2#"'.#"32>7#"&546B\h35 )%7^!/L%=!'Vy}R8;/#wrX3N!ywy72<@9,'<beUIMA8#""%"#!+#"=4;4632#"'&#"32#&''54?>V?X?0)%3Z y5+ o0 V7"#'7%5 ""-#j@<"!:K&PX@UQ>@!bUIMAY@ ## +326?2326?2!"547#"'6?0ݘ7/ m +N   'R!3#^O g o;DD RKPX@2   U UM = K =M>KPX@0S   U U K =M>@-S   U UQ K >YY@ PNDC?;21*&   +"2654&462#"!#&#"&47>727&#"&47>&'!"/35Z55lljJHMj 2nLk NZ%-D?63 [u!wP%=/-@@-1;igGFh- ^  11 4XJ)l/1  11   [JB M@ GI@@b  ` UQ M = M =M >Y@LJFD><97/-%#!    +"2654&462#"32765## 546?65#"#"7463 327'"&/46Z55lmkIHբKHFn)op D`8)H5 %'NuF9=/-@@-1;igGFh+17[[m Vf'+ ++ /JV+1:!(L=`sB'&@'U  U UM >Y@">=874.B"A&#" +462"3 54&#"32654&#263 #"&#"&47>54&'&47?V@@V#iZGDfך%#pR1nӒbj s??s V@@V?덁,JbQP))HsC1 <{5{;2  <@8*K-PX@-b=M =M=M>@+bU=M=M>YY@ $"$&$" +4632#"&32654&#"632#"'&"'654.'&7672F%';A)%>Z#NE_dXfx<!-7 i%=D&#@DC7`㷓\2!'jBA&*RV`- !F}+*@&  U UQM >Y@&"BA<;82"F&E! &##$" +4632#"&3 54&#"32654&#263 #"&#"&47>54&'&47E%';A)%=<#iZGDfך%#pR1nӒbj s??s %=D'#?B덁,JbQP))HsC1 <{5{;2 V <z@8*@*bQ=M=M>Y@ $"$&$" +4632#"&32654&#"632#"'&"'654.'&7672fF%';A)%>#NE_dXfx<!-7 i%=D'#?BO7`㷓\2!'jBA&*R`- !F+*@'  U U QM >Y@"&"BA<;82"F&E!    +!"5463!23 54&#"32654&#263 #"&#"&47>54&'&47\y#iZGDfך%#pR1nӒbj s??s ()r덁,JbQP))HsC1 <{5{;2  <@8*@+b R=M=M>Y@76)(%$"   +!"5463!232654&#"632#"'&"'654.'&7672]#NE_dXfx<!-7 i()7`㷓\2!'jBA&*RLf Bo@l-0<  : < ;:db  U  QM =M> ?=9731,+)'"  B B$ +#"547"'7.547632'!"32672632#"'4772656DX'95( IQrg'hz Ǐl,?R}^HPN)/9)Aw^ i釠_[ XiwxF9BV#!+'GLf/b D@.2> < <;K!PX@7b  U QM=M =M>@5bU  U QM=M>Y@A?;95310+)%#DD$! +632'"&547"'7.5432#"'.#"327632#"'4772654J ?2' $5' K@$FA:fri! C\,?S}_HON)/:^/' %  lV+0J-3/¦ׇn\uF9BV#!+'GDs6^K(PX@ UM =M >@UUM >Y@21,+("65(# +462"32>54.#"'263 #"&#"&47>54&'&47?V@@VTuT=힬X!R'}w{j s??s V@@V?73/nі}vdi;1 <{5{;2 P I8K-PX@,U=M =M=M>@*UU=M=M>YY@ IH"%$%'$" +4632#"&4&'&"3276#"&5476323274.'&7672'&'&"F%';B)%= ?g{D;ZPq!jJ5-8 i8^ N%=D&#@DZ/'JfTbd{ۍ{BA&*R`P= % ?H0 VD- :\K(PX@QM =M >@UQM >Y@ 650/,&: 9($$" +4632#"&32>54.#"'263 #"&#"&47>54&'&47E%';A)%=eTuT=힬X!R'}w{j s??s %=D'#?B73/nі}vdi;1 <{5{;2 PV Iu8@)UQ=M=M>Y@ IH"%$%'$" +4632#"&4&'&"3276#"&5476323274.'&7672'&'&"{F$'@ UQM >Y@ 650/,&: 9  +!"7463!232>54.#"'263 #"&#"&47>54&'&47 \kTuT=힬X!R'}w{j s??s ()73/nі}vdi;1 <{5{;2 P I8@*U Q=M=M>Y@IH76+)'%   +!"5463!24&'&"3276#"&5476323274.'&7672'&'&"\ ?g{D;ZPq!jJ5-8 i8^ N() /'JfTbd{ۍ{BA&*R`P= % ?H0 1D-!@f9K(PX@eM =M >@eUM >Y@&"<;652,"@&?  +2&5>54'&54632>54.#"'263 #"&#"&47>54&'&479FxaHQ?V9TuT=힬X!R'}w{j s??s T`PVg!>/B%373/nі}vdi;1 <{5{;2 P1"O@ ><9KPX@*e=M=M=M>@(eU=M=M>Y@ON=<1/-+&$  +2&5>54'&5464&'&"3276#"&5476323274.'&7672'&'&"9Fy`HQ?V9 ?g{D;ZPq!jJ5-8 i8^ NT`PVg!>/B%3R/'JfTbd{ۍ{BA&*R`P= % ?H0 fD- <V 9K(PX@M =M >@UM >Y@"8721.(<"; +&'&'676'32>54.#"'263 #"&#"&47>54&'&47DLw\sbG%TuT=힬X!R'}w{j s??s X{L^s7s73/nі}vdi;1 <{5{;2 Pf Ks@ :< 9KPX@$=M=M=M>@"U=M=M>Y@KJ98-+)'"  +&'&'6764&'&"3276#"&5476323274.'&7672'&'&"Lw\sbH$ ?g{D;ZPq!jJ5-8 i8^ NX{L^s7sN/'JfTbd{ۍ{BA&*R`P= % ?H0 9 NY@D '@F  b ZZ U   UUM=L >Y@& VTKHA@?;8721.)$! N N   +!"5463!226762"'.+;26&#!"&47>54&'&473!272'.+"#"/6H5}:1  1RT3@J' 2cHss??sd 50!#'VjP7 ,0%.#0/1[ bOJj 70%;H9 1 <{5{;2 F{d;H× r5LB# $;U@Rdd  b V  UM= M>:8531/*(&%!$% +#"/7672!"7463!2%2'4&#"#"'&543 #%326r  #28  5:sdF4L> #n`Z&AZVm%*%-#/ub-n,o#m1 NX@D '@F  b ZZ U   UUM=L >Y@& USKHA@?;8721.)$! N N   +!"7463!226762"'.+;26&#!"&47>54&'&473!272'.+"'"7475}:1  1RT3@J' 2cHss??sd 50!#'VjP7D,%.#0/1[ bOJj 70%;H9 1 <{5{;2 F{d;H$:iLB $;U@Rdd  b V  UM= M>:8531/*(&%!$! +632#"&547!"7463!2%2'4&#"#"'&543 #%326 3( 5:sdF4L> #n`Z&AZVm+# "%-#/ub-n,o#m1f91 P@F )@5 ZZ   U UM=L >Y@MJCBA=:9430+&# PP +&'&'67626762"'.+;26&#!"&47>54&'&473!272'.+"TLw\sbG%}:1  1RT3@J' 2cHss??sd 50!#'VjP7X{L^s7s31[ bOJj 70%;H9 1 <{5{;2 F{d;HLfB /8@5 9bUM=M>#"%"#.+&'&'676%2'4&#"#"'&543 #%326Lw\sbG$sdF4L> #n`Z&AZVmX{L^s7sub-n,o#m191`@V$!(9 @F  Z Z  UUURM= L >Y@*]ZSRQMJIDC@;630.*)#"`` +2676#".'&#"'63226762"'.+;26&#!"&47>54&'&473!272'.+"#)!) " ?/'''/u%)3:}:1  1RT3@J' 2cHss??sd 50!#'VjP7+@ '-5 '1[ bOJj 70%;H9 1 <{5{;2 F{d;HLB(?a@^<  b  UU R M = M>><9753.,*)%#  +2676#".'&#"'632%2'4&#"#"'&543 #%326N#)!) #?/'''/u%)4sdF4L> #n`Z&AZVm+@ '-5 '.ub-n,o#m1f9q i@(+ <9 @ Q Wca<);K(PX@Zd ZU  UUQ N  = M =L =L >@Xd ZU   U  UUQ M =L =L >Y@/fd`^ZXVSNKHFBA;:752/('&"ii "" +#"&'33267"'7#"&47>54&'&473!272'.+"326762"'.+;26&+632#"'4772654}ks9 }TP 5' Rss??sd 50!#'VjP7}:1  1RT3@J' 2c3?S}^HPN(/:qh}k9DC: 1 <{5{;2 F{d;H1[ bOJj 70%;H9 F9BV#!+'GLfB/ ;F}@z)5 3 <; :bU  U  U Q M=M> CA><8620,*('%$"  ;;% +267#"&'"'7&'&543 #%3267632#"'4772654%2'4&#"|}7`m'5' Jc`Z&AZVl<#,?S}_HON)/:sdF4L>/RfbV$ uo#m1= uF9BV#!+'Gub-nsF@* <9 @ @? Z bU  U  U M =K >Y@ BA;:752/,+'&%! F F +462"#&#"&47>54&'&473!272#.+"326762"'.?V@@Vg@r  y{s??sd 6*%9h\F-}91  2QV@@V?d{; 11 <{5{;2 v PV91R1[ jGNf 70%-) >KPX@1  ZU M= M =K >@2  bU M= M =K >Y@><9753%###C$" +4632#"&&#"&47>5#"=4;54632#"'.#"32'#E%'V@S6"<b`UM  =N> 97-)! > >  +!"5463!22'.#"3274&'.7327! 4$5d+'# 5`kmQG` ITc 1- ;%-#/%1Njk7N99" 5  5 +4pPB *b$@Z2S9N K(PX@B  b U  U U M =  M= M  >@H  b  Z U  U U M = M= M  >YY@&,+^\YWJHB@?=860.+b,b*%   +!"5463!24&#"3232>54&'&"#&"'&#"#"'37632#"&547&5467&54632>325Nf` %P=?J`}-;? g%-Z'4 XˏqI%N5=R8RZ^1)jэV%{-)5/%%-#/9TX/Lu\k2jF'5##7$!Z{+3D;: CJ\jQn{e/o7u"f917+!.qOK(PX@- U  S  K =K >@+ U   U  SK >Y@MLIEBA>=:962/.CC+462"&#"&47>5!&#"&47>54&'&47327!4&'&47327?V@@V?s TRs@C@r TV s??s lo s?@sUb s?V@@V?^{; 11 <{i{; 11 <{5{;2  1 ;{H{;2  1 ;{7 R`,@U=M= >Y@ PND@64+*J$" +4632#"&&#"&47>54.'&7672767632&#"&47>54'&#"F%';A)%>,9  sWZ 3-& -7 iu57/ZR^sT+%H{#%=D&#@DWI 1  1  'DBBA&*R3FHy9 1 1:y7m1Jy&V-GS}K(PX@,  SQ K  =K >@*   U  SQK >Y@RPLJEDA=:96521CCC+%&#"&47>5!&#"&47>54&'&47327!4&'&473274632#"&?s TRs@C@r TV s??s lo s?@sUb s?E%';A)%={; 11 <{i{; 11 <{5{;2  1 ;{H{;2  1 ;{%=D'#?BT7 R3@0,<Q=M= >PND@64+*J$" +4632#"&&#"&47>54.'&7672767632&#"&47>54'&#"F%';B)%=X,9  sWZ 3-& -7 iu57/ZR^sT+%H{#%>D'#?CWI 1  1  'DBBA&*R3FHy9 1 1:y7m1Jy&qGOWK(PX@/U  S K  =K >@-U   U  SK >Y@WVSRONKJEDA=:96521CCC+%&#"&47>5!&#"&47>54&'&47327!4&'&47327462"$462"?s TRs@C@r TV s??s lo s?@sUb s?T;N;;NB9R99R{; 11 <{i{; 11 <{5{;2  1 ;{H{;2  1 ;{N<TRHD:8/.I +462"$462"&#"&47>54.'&7672767632&#"&47>54'&#"5N77N5N55N,9  sWZ 3-& -7 iu57/ZR^sT+%H{#mM88M66M88M6WI 1  1  'DBBA&*R3FHy9 1 1:y7m1Jy&h-_@ 1( &<2 ;K(PX@5S  UQ K = K  >@3   US  UQ K  >Y@]\YURQNMJIFB?>98530.$$"#C+%&#"&47>5!&#632#"'4732654"'7&47>54&'&47327!4&'&47327?s TRs@C@r A2?R|_HON)/9C5' R[ s??s lo s?@sUb s?{; 11 <{i{; 1F9BV#!+'H 1 <{5{;2  1 ;{H{;2  1 ;{f7^\@YLX V <;  U  Q=M= >[YUSOMKI><2.$"^^ +"'7&47>54.'&7672767632&#"&47>54'&#"&#632#"'47726565( RIk 3-& -7 iu57/ZR^sT+%H{#,9  eL3?R}^HPN)/9  1  'DBBA&*R3FHy9 1 1:y7m1Jy&0WI 1 F9BV#!+'Gh-GT@ TNMH@*   U  SQK >Y@RPKJEDA=:96521CCC+%&#"&47>5!&#"&47>54&'&47327!4&'&47327267#"&'?s TRs@C@r TV s??s lo s?@sUb s?|}7am{; 11 <{i{; 11 <{5{;2  1 ;{H{;2  1 ;{gRgbWT7 S:@7- <Q=M= >QOEA75,+K% +267#"&'&#"&47>54.'&7672767632&#"&47>54'&#"^{}7am/,9  sWZ 3-& -7 iu57/ZR^sT+%H{#RfbVWI 1  1  'DBBA&*R3FHy9 1 1:y7m1Jy&R-?@ @&  UU RK >Y@=<9521,+($!  +2676#".'&#"'632&#"&47>54&'&47327#)!) #?0'''/t $)3@r PR s??s GV s?+@ '-5 '{; 11 <{5{;2  1 ;{5-)F|@ @$UU R= >Y@CB40(&"  +2676#".'&#"'6324632#"&&#"&47>54&'&7672#(!) #?0'&'/u%)4F%';A)%>1b \^b2/V Z+@ '-5 '%=D&#@DV{9 11 8{V-#"f T ;s:K(PX@)b U  K =K >@'b U  UK >Y@9851C$ +'"747462"$462"&#"&47>54&'&47327D,]K PX@#XbV= >@"dbV= >Y@ H$+62"&547462"&%462"&&#"&47>54&'&7672j 1&9N99N9Z9N97R7!1b \^b2/V Z)"  {)99)'::')99)'::{9 11 8{V-#"f Zs@ , <:K(PX@&d  bK =M >@$d  b UM >Y@ZYTONCC$ +#"547&#"&47>54&'&4732767>.'.7327&#""'&'&'FX'9@r PR s??s PN s?_CR!#$  OD 5@`.HC-' HQA;o)Aw{; 11 <{5{;2  1 ;{Hh!+2  1  3/J#' 1 9UJ [@5D@2b`b=K=N >Y@ZYVRKJC>C$! +632'"&5474.'&76726767>&'&47327&#""5&/&'&'&#"&47> ?2' -7 hBIJZ %)^kT^ H%L? RV-RR!!='C  ^\T Z/^/' %BA&*R6 67q $1  2L / 1 !o, y; 11 :V- \r. @&  b UQM  >Y@\[VQNCC$" +4632#"&&#"&47>54&'&4732767>.'.7327&#""'&'&'5F%';A)%>@r PR s??s PN s?_CR!#$  OD 5@`.HC-' HQA;o%=D'#?Bb{; 11 <{5{;2  1 ;{Hh!+2  1  3/J#' 1 9UJV YK@H3B<bQ=K=N >XWTPIHA&'&47327&#""5&/&'&'&#"&47>E%';A)%=-7 hBIJZ %)^kT^ H%L? RV-RR!!='C  ^\T Z/%=D'#?BbBA&*R6 67q $1  2L / 1 !o, y; 11 :- \. @'  b U QM  >Y@\[VQ=9+*'#   +!"7463!2&#"&47>54&'&4732767>.'.7327&#""'&'&'}\@r PR s??s PN s?_CR!#$  OD 5@`.HC-' HQA;o(){; 11 <{5{;2  1 ;{Hh!+2  1  3/J#' 1 9UJ YU@R3B<b Q=K=N >XWTPIHA<+'$#  +!"7463!24.'&76726767>&'&47327&#""5&/&'&'&#"&47>\-7 hBIJZ %)^kT^ H%L? RV-RR!!='C  ^\T Z/()BA&*R6 67q $1  2L / 1 !o, y; 11 :V- 3q/@#ZUQK >Y@ +(#" 31$" +4632#"&"&47>54&'&47327;2676&#F$'## !7%=D'#?Bh1 <{5{;2  1 ;{NA%V(2@/ <bQ=L >$$C+74&'&7672&#"&47>4632#"&)[ i4`]V `4F$'@,  Z U UQ K  >Y@74/.+'$#?=  +!"5463!24632#"&"&47>54&'&47327;2676&#6OF$'## !7%.#0 %=D'#?Bh1 <{5{;2  1 ;{NAV J(4w @)b UQ=L >Y@+)1.)4+4$$C +74&'&7672&#"&47>4632#"&!"7463!2)[ i4`]V `4F$'@$ZUQK  >Y@ +(#" 31  +!"5463!2"&47>54&'&47327;2676&#\-j s??s LR s?7>## !7() 1 <{5{;2  1 ;{NA(9@6 <bQ=L >%"((C+74&'&7672&#"&47>!"5463!2)[ i4`]V `4\q1&*RP{; 11 @ZUK >Y@-*%$!53 +&'&'676%"&47>54&'&47327;2676&#bLw\sbH$j s??s LR s?7>## !7X{L^s7sP1 <{5{;2  1 ;{NA f*0@- <'%#9b=L >C+74&'&7672&#"&47>&'&'676)[ i4`]V `4nLw\sbH$q1&*RP{; 11 <{L^s7s! P@ 8<:K(PX@9db`  ` K =  K  =>@7db`  ` U  K  =>Y@KJGC@?65G$ +#"747#'"'#&#"&47>76&'&4727%&#"&47>&X'944  XN1_{F5{T< R (m )   e.=')' XD ("( )AwX'`T 11 PbLq.2  1 6W101 11 -0-?b js@%ddM = L  >Y@hg]\YU$J)I%($! +632'"&54776327632&#"&47>54&#"&#"&47>54'"&#"&47>54&'&7672= ?2' Np}V+Xs`jgT+==|)PfcjgV)b{#,; {Tb{Z20V d ^/' %sUA{9 11 @:b`  `U  U  K  =>Y@IHEA>=430/G+462"#'"'#&#"&47>76&'&4727%&#"&47>&?V@@V44  XN1_{F5{T< R (m )   e.=')' XD ("( V@@V?PX'`T 11 PbLq.2  1 6W101 11 -0-?- hn @#UM = L  >Y@fe[ZWS$J)I%%$" +4632#"&76327632&#"&47>54&#"&#"&47>54'"&#"&47>54&'&7672F%';A)%>Np}V+Xs`jgT+==|)PfcjgV)b{#,; {Tb{Z20V d %=D&#@D9UA{9 11 @9b`  `  UQ  K  =>Y@MLIEBA8743G$"+4632#"&#'"'#&#"&47>76&'&4727%&#"&47>&E%';A)%=44  XN1_{F5{T< R (m )   e.=')' XD ("( %=D'#?BVX'`T 11 PbLq.2  1 6W101 11 -0-V? h<@9 <QM = L  >fe[ZWS$J)I%%$" +4632#"&76327632&#"&47>54&#"&#"&47>54'"&#"&47>54&'&7672E%'K(PX@-b`UK = =>@.b`UI =M>YY@ "J#(I +462"4.'.7327#"'&"&#".7>5.'&47327o?V@@V 15?  /=s?/-%53 15? 75  s? Z> X V@@V?{HK+ 2  1 A?1DdHK+  11 BV-R2 q>-;- JMK-PX@M =M= >@UM= >Y@ J*J#$"+4632#"&632&#"&47>54'&#"&#"&47>54&'&7672F%';A)%>nu57/Z{^V{T+'F}}#+R {Qf|`0/W d %=D&#@D9FJy9 1 1:y7m1Jy'/y9 11 8{V-#'V- H<K(PX@,b`QK = =>@-b`IQ =M>YY@ "J#(J$" +4632#"&4.'.7327#"'&"&#".7>5.'&47327fF%';A)%> 15?  /=s?/-%53 15? 75  s? Z> X %=D'#?BHK+ 2  1 A?1DdHK+  11 BV-R2 q>-V; J%@"QM= >J*J#$"+4632#"&632&#"&47>54'&#"&#"&47>54&'&7672F%';A)%>?u57/Z{^V{T+'F}}#+R {Qf|`0/W d %=D'#?BEFJy9 1 1:y7m1Jy'/y9 11 8{V-#'- H<K(PX@-b` QK = =>@.b`I Q =M>YY@GECB?>51'%"   +!"5463!24.'.7327#"'&"&#".7>5.'&47327\ 15?  /=s?/-%53 15? 75  s? Z> X ()%HK+ 2  1 A?1DdHK+  11 BV-R2 q>-; J1@.QM= >FE73)'    +!"7463!2632&#"&47>54'&#"&#"&47>54&'&7672\u57/Z{^V{T+'F}}#+R {Qf|`0/W d ()FJy9 1 1:y7m1Jy'/y9 11 8{V-#'f- J@ >< 9K PX@$Z`K = =>K(PX@%b`K = =>@&b`I =M>YY@IGEDA@73)'$" +&'&'6764.'.7327#"'&"&#".7>5.'&47327Lw\sbG% 15?  /=s?/-%53 15? 75  s? Z> X X{L^s7siHK+ 2  1 A?1DdHK+  11 BV-R2 q>-f; L#@ 9M= >J*J/+&'&'676632&#"&47>54'&#"&#"&47>54&'&7672HLw\sbG%u57/Z{^V{T+'F}}#+R {Qf|`0/W d X{L^s7s-FJy9 1 1:y7m1Jy'/y9 11 8{V-#'LTt*8BZ@W;9<bU U M =M> ?=751/&$* *  +2676#".'&#"'632"32# ! #"5475"M: 6( "!(d#-aǴVT!vwD,0Eiv +2;  !+: +T{h'3$9iT,9G_@\< d  dU V M=M>.-CA=;53-9.9+)#! +2676#".'&#"'63247632'""32654&632#"&547o#(!) #?0'''/u%)4qzǓPu=sLZN 1&h+@ '-5 '<˅sւs EBT*" !LTt.:H\@Y%<UU  U  M = M  >0/GEA?64/:0:(&!..+462"$462"%2676#".'&#"'632"32# ! 7N77N7N77N/"M: 6( "!(d#-aǴVT!vN77N77N77N7V0Eiv +2;  !+: +T{h'3T,9AIc@`<    UU VM=M>.-IHEDA@=<53-9.9+)#! +2676#".'&#"'63247632'""32654&462"$462"o#(!) #?0'''/u%)4qzǓPu=sLZu:M::M :N97Rh+@ '-5 '<˅sւs EBTR99R88R99R8LTq '2L@I1<(:bUM =M>/-&$    +!"&5463!2"32# ! #"/6z  ǴVT!vp ,/ - .: +T{h'3 r5T (5D@AddV M=M>*)1/)5*5'%$% +#"/7632!"5463!247632'""32654&n  "06 86aqzǓPu=sLZ $)%-#/˅sւs EBTLTp '1L@I*<(:bUM =M>.,&$    +!"&5463!2"32# ! #"547]z  ǨǴVT!vD, - .: +T{h'3$9iT' (5D@AddV M=M>*)1/)5*5'%$! +632"&547!"5463!247632'""32654&/ 1&)6aqzǓPu=sLZ#*"  %-#/˅sւs EBT =K-PX@ ,<:@ ,<:YK-PX@$dUM =K >@*dZUM =K >Y@ C(c$&$ +#"54732654&#"4&'&4732632#"'&#"&47>5X&9@%w?s d)'^'1hDžf@N ZR s?)Aw" {;2 Eqh6HOy; 11 <{!b MtK!PX@.b =M =M=L>@+ddM =M=L>Y@ KJC%%&,$! +632'"&54732>54&#"'7632'"'&&#"&47>54&'&7672 ?1' ! 0aRw=g+{PЗydN 7q qV`4/V d ^/' %<)16(+Dt{FLs{: 11 <{jV-#'s;K-PX@ *<@ *@-ZUUM = K >Y@ 98C(c$# +462"32654&#"4&'&4732632#"'&#"&47>5@V??V%w?s d)'^'1hDžf@N ZR s?V@@V?" {;2 Eqh6HOy; 11 <{!- KoK-PX@+M =M =M=L>@)UM =M=L>Y@ IHC%%&)$" +4632#"&32>54&#"'7632'"'&&#"&47>54&'&7672F%';A)%>X! 0aRw=g+{PЗydN 7q qV`4/V d %=D&#@D)16(+Dt{FLs{: 11 <{jV-#'sC5 K-PX@8 UI  U  M =K = M  >@9 UU  U  M =K = M  >YY@ CB><98.,+(%$   +462""3265&#"&47>54&'&47326323#"'.#?V@@V#bF@rTR s??s G#3yAm_/V? 9p`wV@@V?+{; 11 <{5{;2 %=fRNPd)%-- <fK-PX@(bM =M=L >@&bUM=L >Y@ J"$'$"+4632#"&7>32#"'&#"&#"&47>54&'&7672BE%';A)%=& )yA;@;%&$!E17q iZd4/V c %=D&#@D9 Fd?#'@0dJ?{7 11 :{V-#'V7 Gݵ9 K-PX@7I  UQ  M =K = M  >@8U  UQ  M =K = M  >YY@ GFB@=<20/,)(#"  $" +4632#"&"3265&#"&47>54&'&47326323#"'.#E%';A)%=;bF@rTR s??s G#3yAm_/V? 9p`w%=D'#?BZ+{; 11 <{5{;2 %=fRNPd)%-V <2@/bQM=L >J"$'$"+4632#"&7>32#"'&#"&#"&47>54&'&7672E%';A)%= )yA;@;%&$!E17q iZd4/V c %=D'#?BE Fd?#'@0dJ?{7 11 :{V-#'V  SEK-PX@@ U  IUQ  M =K = M  >@A U  UUQ M =K = M  >YY@(SRNLIH><;854/.+'$#     +!"7463!24632#"&"3265&#"&47>54&'&47326323#"'.#5E%';A)%=;bF@rTR s??s G#3yAm_/V? 9p`w%.#0 %=D'#?BZ+{; 11 <{5{;2 %=fRNPd)%-V HK@Hb UQM =L >FE;:73)'%#  +!"5463!24632#"&7>32#"'&#"&#"&47>54&'&7672`6*E%';A)%= )yA;@;%&$!E17q iZd4/V c +%-"0%=D'#?BE Fd?#'@0dJ?{7 11 :{V-#'7 G9 K-PX@8I  U Q M =K = M  >@9U  U Q M =K = M  >YY@$ GFB@=<20/,)(#"     +!"5463!2"3265&#"&47>54&'&47326323#"'.#N\ͪbF@rTR s??s G#3yAm_/V? 9p`w()+{; 11 <{5{;2 %=fRNPd)%- <?@<bQM=L >:9/.+'  +!"7463!27>32#"'&#"&#"&47>54&'&7672\ )yA;@;%&$!E17q iZd4/V c () Fd?#'@0dJ?{7 11 :{V-#'7s@j) @%bUM =M>Y@ ?=.,'&%#) +462"'.#"#"&#&'6!2654.'.54632d@V??V *'DkGY04Gjf5hXla: #@'{$.X63Mqn:uV@@V?z:RQ)W1X8::X}Na7C/ ݁{:^95=[}M4b- @@" ?@/bUM=M =M>Y@ "+%*#$" +4632#"&6232654&'.54632"'.#"#"'&#"&FE%';A)%= , !^bBlT}{fu/bm . /@/7Y]pDjg1dV%=D&#@Dw cJEDR33wm`ml =R# G@$bQM =M>Y@ CA20+*)'*$" +4632#"&'.#"#"&#&'6!2654.'.54632mE%';A)%= *'DkGY04Gjf5hXla: #@'{$.X63Mqn:u%=D'#?Btz:RQ)W1X8::X}Na7C/ ݁{:^95=[}M4bV4@J@G3<bQM=M =M>$%"+%*# +6232654&'.54632"'.#"#"'&#"&4632#"&b , !^bBlT}{fu/bm . /@/7Y]pDjg1dVE%'K(PX@&bUM =M>@-bbUM =M>YY@MK<:5431,&$"+4632#"&%#"547'.#"#"&#&'6!2654.'.54632F%';A)%>X'9| *'DkGY04Gjf5hXla: #@'{$.X63Mqn:u%=D'#?D)Awюz:RQ)W1X8::X}Na7C/ ݁{:^95=[}M4bb4BN@3  bb = M =M=M =M>K!PX@<  bb  U =M=M =M>@< d  bb  UM=M =M>YY@MKGE$$"+%*# +6232654&'.54632"'.#"#"'&#"&632'"&547'4632#"&b , !^bBlT}{fu/bm . /@/7Y]pDjg1dV| ?2' >"$5;%!8 cJEDR33wm`ml =R# GK!PX@+bUM = =M>@.bbUM =M>YY@JH97210. +462"&'767'.#"#"&#&'6!2654.'.54632@V??V J!{^uffg *'DkGY04Gjf5hXla: #@'{$.X63Mqn:uV@@V?\g)5HR+)Nuz:RQ)W1X8::X}Na7C/ ݁{:^95=[}M4b4?K@=<87 : 3@:   bb M=M=M =M>Y@55JHDB5?5?"+%*# +6232654&'.54632"'.#"#"'&#"&&'7674632#"&b , !^bBlT}{fu/bm . /@/7Y]pDjg1dVN)HN'G>"$4:&!7 cJEDR33wm`ml =R# G@,bUQM =M>Y@KI:8321/*$# +462"4632#"&'.#"#"&#&'6!2654.'.54632y?V@@VKE%';A)%= *'DkGY04Gjf5hXla: #@'{$.X63Mqn:uV@@V?#%=D'#?Btz:RQ)W1X8::X}Na7C/ ݁{:^95=[}M4bV- @L@" ?@6bU QM=M =M>Y@KIEC"+%*#$" +4632#"&6232654&'.54632"'.#"#"'&#"&4632#"&E%';A)%= , !^bBlT}{fu/bm . /@/7Y]pDjg1dVE%'@)U U M  =K >Y@6310,+15C +462"&#"&47>54&+""'6743!3 72#.+" @V??VE  OVF+6dj-) .y-5)V@@V?\{; 11 <{dgbw y s`hi3h9 6Q@N<bbU M =M> 31/-+*)'$" 6 6$" +4632#"&34.5476767232'#327#"5#"=4F%';B)%=T^-$"" 1'^Jyy %=C'#?ANf RO )dY:!i )V9 <~@ (3&@( UQ M  =K >Y@:7540/15C$" +4632#"&&#"&47>54&+""'6743!3 72#.+"F$' 31/-+*)'$" 6 6$" +4632#"&34.5476767232'#327#"5#"=4F%';A)%>^-$"" 1'^Jyy %=D'#?BNf RO )dY:!i )9 <@ (3&@) U Q M  =K >Y@:7540/.+*)%$"  +!"7463!2&#"&47>54&+""'6743!3 72#.+"-\rE  OVF+6dj-) .y-5)(){; 11 <{dgbw y s`hi3h 6T@Q<db Q M =M> 31/-+*)'$" 6 6  +!"5463!234.5476767232'#327#"5#"=4\/^-$"" 1'^Jyy (){Nf RO )dY:!i )d9 >|@*5(< 9K&PX@# M =M =K >@! UM =K >Y@<976210-,+'&$! +&'&'676&#"&47>54&+""'6743!3 72#.+"Lw\sbG%TE  OVF+6dj-) .y-5)Z{L^s7sL{; 11 <{dgbw y s`hi3fh 8K@H< 9dbM=M>531/-,+)&$!88 +&'&'67634.5476767232'#327#"5#"=4Lw\sbH$^-$"" 1'^Jyy X{L^s7sNf RO )dY:!i ) ?- NYK(PX@ Q K =M>@ UQM>Y@ ML3+N( +462"&%462"&2>54.'&47327#".54&'&47279N97R7Z9N99N9j2/ddN- 3:C15d9-qњZX;c{B)99)'::')99)'::=Vo-`@\xR?D' 2  1 9u-׮Z(eϕRy=2  1 ;-)s NG@D  bRM =N >LK:98521+)#"!NN +462"&%462"&'"&54&'&4732723276'4&'&473272'&'&:N99N:Z:N97R8{3LF3d@\q!1N F38^ N)99)'::')99)'::y`>1  =yZb3b@1  =wP= % ?F2 z ?-X@ @&  UU RM>Y@WVSPMLEC84&$ +2676#".'&#"'6322>54.'&47327#".54&'&47277#)!) #?0'''/t%)3`2/ddN- 3:C15d9-qњZX;c{B+@ '-5 'Vo-`@\xR?D' 2  1 9u-׮Z(eϕRy=2  1 ;-)sXc@`<  bU R M =N>VUDCB?<;53-,+(XX +2676#".'&#"'632'"&54&'&4732723276'4&'&473272'&'&#)!) #?/'''/u%)4{3LF3d@\q!1N F38^ N+@ '-5 'ܝy`>1  =yZb3b@1  =wP= % ?F2 z d?- HP 9K(PX@K =M>@UM>Y@GFC@=<53($ +&'&'6762>54.'&47327#".54&'&4727Lw\sbG%2/ddN- 3:C15d9-qњZX;c{BZ{L^s7sVo-`@\xR?D' 2  1 9u-׮Z(eϕRy=2  1 ;-f)s HA@> 9bM=N>FE432/,+%#HH +&'&'676'"&54&'&4732723276'4&'&473272'&'&Lw\sbG$7{3LF3d@\q!1N F38^ NX{L^s7s1  =yZb3b@1  =wP= % ?F2 z ?Yc@\ @.  b U U UM>Y@`^XWTQNMFD95'%  +2676#".'&"'622>54.'&47327#".54&'&4727'"747?"M: 6( # !(c"-O2/ddN- 3:C15d9-qњZX;c{BaD,0Eiv+1; +9Vo-`@\xR?D' 2  1 9u-׮Z(eϕRy=2  1 ;$:i-)= +fp@m"<dd  bUV M = N>-,dcRQPMJICA;:96,f-f%#++$! +632#"&5472676#".'&#"'632"&54&'&4732723276'4&'&473272'&'& 1&#(!) #?0'''/u%)4{3LF3d@\q!1N F38^ N9)"  +? '-5 &Vy`>1  =yZb3b@1  =wP= % ?F2 z ?f $_K(PX@* I U  K  =M>K1PX@( I U    UM>@) U U    UM>YY@  ^]ZWTSLJ?;-+ $$  $ +"&54632"&54632%!"&5463!22>54.'&47327#".54&'&4727m>5/%!3%/0$#11{   2/ddN- 3:C15d9-qњZX;c{B+)'//'%)2"!31#!34 - .=Vo-`@\xR?D' 2  1 9u-׮Z(eϕRy=2  1 ;-)VY@V bUU M = N>TSBA@=:931+*)&VV+462"$462"!"5463!2"&54&'&4732723276'4&'&473272'&'&:N99N :N97RG6 Í{3LF3d@\q!1N F38^ NPR99R77R99R7%-#/ǝy`>1  =yZb3b@1  =wP= % ?F2 z3fH@ @(b`U V>Y@ED=91/%! +2676#".'&#"'62.7327"'.'&4732736&#(!) #?0'''/u%)4 79{ .B.Xl,;Q NTg&%F C')+? '-6 '2  1 =///0qB 0  1 =[5TT HA@><UV=>;7/-%! +2676#".'&#"'632.7327"'.'.732776&#(!) #?0'''/u%)47  fV=R  N?"#"6L  {CH M !+? '-5 &1  2@P/+V6 1  2 6Iu?=N5V3- 6HK(PX@bQ =>@ddQ>YH*E$"+4632#"&.7327"'.'&4732736&F%';B)%= 79{ .B.Xl,;Q NTg&%F C')%=D'#?BQ2  1 =///0qB 0  1 =[5TT Vs 6@Q=>H(E$"+4632#"&.7327"'.'.732776&F%';B(%>  fV=R  N?"#"6L  {CH M !%=D'#?B1  2@P/+V6 1  2 6Iu?=N5 [k :K(PX@$db K  =>@"db  U>Y@TSPLIHC##O% +#"'%664.'&47327'&#""'.'&47327674/&'&47327 9@'=;w NKd9 /HwHf  ?7+  /)JX - =2  1 =;5893R78TP"2  1  2wX)-s2  1 -  b XiF@ddK =>Y@VRJHDC?>;743$ $% +#"/763267'.'&4732767>.'&47327"' #"'.'.7327  +?FG  %0#{9R{P  <7 {VBQ  HH>!:G  jPT{ ^)/@"db  U>Y@SROKHGC##O$ +#"54764.'&47327'&#""'.'&47327674/&'&47327X'9d'=;w NKd9 /HwHf  ?7+  /)JX -)Aw2  1 =;5893R78TP"2  1  2wX)-s2  1 -  b XiF@ddK =>Y@VRJHDC?>;743$ $! +632'"&54767'.'&4732767>.'&47327"' #"'.'.7327X ?1' G  %0#{9R{P  <7 {VBQ  HH>!:G  jPT{ ^/' %@'bU  U>Y@YXUQNMBA>:76##L+462"$462"64.'&47327'&#""'.'&47327674/&'&473279N97R#9N99NR'=;w NKd9 /HwHf  ?7+  /)JX -R::R77R::R72  1 =;5893R78TP"2  1  2wX)-s2  1 -   ^=@:L <UK =   >\XPNJIEDA=:9*& +462"&%462"&67'.'&4732767>.'&47327"' #"'.'.7327:N99N:[9N97R7^G  %0#{9R{P  <7 {VBQ  HH>!:G  jPT{ )9:('::')9:('::@%bU  U>Y@QPMIFE:9C##L +462"64.'&47327'&#""'.'&47327674/&'&47327@V??V)'=;w NKd9 /HwHf  ?7+  /)JX -V@@V?2  1 =;5893R78TP"2  1  2wX)-s2  1 -  - VdD@UK =>Y@TPHFBA=<9521"$" +4632#"&67'.'&4732767>.'&47327"' #"'.'.7327F%';A)%>G  %0#{9R{P  <7 {VBQ  HH>!:G  jPT{ %=D&#@D@$b  UQ>Y@UTQMJI>=C##M$" +4632#"&64.'&47327'&#""'.'&47327674/&'&47327;F%';A)%>@'=;w NKd9 /HwHf  ?7+  /)JX -%=D'#?B2  1 =;5893R78TP"2  1  2wX)-s2  1 -  Vs V8@5D<RK =>TPHFBA=<9521"$" +4632#"&67'.'&4732767>.'&47327"' #"'.'.7327F%';A)%>G  %0#{9R{P  <7 {VBQ  HH>!:G  jPT{ %=D'#?B@.  b`U   UK >Y@SOLKGEA@=9CG +462"&#"&47>'&"&#"&47>764'.'&473273276&'&473277@V??V?NB {VRR0 #L3=| Ji1/f32  1 7B"-!- ZSK-PX@M ==L >@U=L >Y@ UQFEB?CL$"+4632#"&?6&'.7327&#"&47>&/&&"&47>?6'&'&47327E%';A)%=)}!=  jBDQ?R6 ;FL bUP{ '#w;  jR ?P8 PhjF`o H%=D&#@D951  2)BR+ 11  //3 11 'H s 1  2'ufK(PX@2  b ` U  K  =K >@0  b ` U   UK >Y@[WTSOMIHEACG+462"$462"&#"&47>'&"&#"&47>764'.'&473273276&'&47327 :R99RCf32  1 7B"-!NXb3@0 U=L >a`\[WVRQIE:963CJ +?6&'.7327&#"&47>&/&&"&47>?6'&'&47327462"&%462"&u}!=  jBDQ?R6 ;FL bUP{ '#w;  jR ?P8 PhjF`o HZ9N99N9Z9N97R7ݸ51  2)BR+ 11  //3 11 'H s 1  2)9:('::')9:('::sD[K(PX@!UK  =K >@U UK >Y@ :73C +462"&#"&47>54&'.'&472776&'&4727@V??VFPV F2HF { U& $H or  JO) V@@V?\{; 11 <{78/U0 0  1 *B]>*2  1 3JF*?3%- DNK-PX@M ==N>@U=N>Y@ ;8L!$#$"+4632#"&#"&54632327676'.'&4732776&'&4727F%';A)%>Ts)73' #F!16';?RTPP"<> {R F?!+%=D&#@D+!=;dXF{[61  2?@V:81%1  27DfTw =@"<<$ :K PX@#bXM =N >K(PX@$b`M =N >@"b`UN >YY8d9N+&'&'676&#!"54767654#"'673!26327%>72Zx T`|T{%7'Pq!"# ˨#k1?3PqL'%*wmZ! =@F7?nkl @du %TFE L;H 2D@A/!< :b=M=N >22%#$+&'&'6763!2>32!>?2%"547'674Lw\sbH$7Bw9)%LS)'*/3s3#!H{L^s7s3=PCV< |Kn TV9 ;@ :<":K PX@*bXQM =N >K(PX@+b`QM =N >@)b`UQN >YY@ 8d9B$"+4632#"&&#!"54767654#"'673!26327%>72E%' 0 0%#$s$" +4632#"&3!2>32!>?2%"547'674DE%';A)%=7Bw9)%LS)'*/3s3#!%=D'#?B=PCV< |Kn T9 ;@ :<":K PX@+bXQM =N >K(PX@,b`QM =N >@*b`UQN >YY@9841)#   +!"5463!2&#!"54767654#"'673!26327%>72?]7'Pq!"# ˨#k1?3PqL'%*()kl @du %TFE L; 0R@O-<bQ =M=N >  0 0*(#!   +!"7463!23!2>32!>?2%"547'674`\7Bw9)%LS)'*/3s3#!()=PCV< |Kn 7 R;@8,<Q=M= >PND@64+*   +!"7463!2&#"&47>54.'&7672767632&#"&47>54'&#"\c,9  sWZ 3-& -7 iu57/ZR^sT+%H{#()VWI 1  1  'DBBA&*R3FHy9 1 1:y7m1Jy&h >"@2b  bU M = M  >Y@;975321/,*'%! >> +462"&%462"&34.5476767232'#327#"5#"=4:N99N:[9N97R7^-$"" 1'^Jyy Z)99)'99')99)'99]YQOKJFEB>;:+'   +"2654&462#"67'.'&4732767>.'&47327"' #"'.'.7327/46Z55lmkIHG  %0#{9R{P  <7 {VBQ  HH>!:G  jPT{ >/-@@-1DA1-!   +"2654&462#"#"&54632327676'.'&4732776&'&4727'/35Z55lljJH=Ts)73' #F!16';?RTPP"<> {R F?!+=/-@@-1;hfHFh+!=;dXF{[61  2?@V:81%1  27DfX Nn@kHJ<b  ` UM= M = M =M >MKGE?=(("'#$#" +#"'72654&#"'63232765## 546?65#"#"7463 327'"&^pN)3AA3 NpKHFn)op D`8)H5 %'NuF9Nn>H13F@o+17[[m Vf'+ ++ /JV+1:!(L=1 9#@0  bU M=M=K >Y@ 75%##5C$" +4632#"&&#"&47>54&+"'543254632#"'&#"F%';B(%>)6l B+V/ Z /TyfD5#%N7N@%=D'#?B}9 11 <{ )b>///TP 5}@5@(bM=M=K >Y@ "%##9C+&#"&47>5'754&+"'543254632#"'&#""6l B+V/# Z /TyfD5#%N7N@=i}9 11 <{T;i; )b>///TPŸ16@3  b S M=M=K >Y@66531/"#5C +#&#"&47>54&+"'5432547#53>32#"'&#"#6l B+V/ Z /T &XfD5#%N7hO6C/}9 11 <{ )b;8O>///TP;DBS@P98(<b`M =L =N>''($&C +%&#"&47>54>$ 32#"&'&7632654.#"'654&#"@r;B s?7  P!fc\1N\ R{B{; 11 <{bq<1 Ց7f }DZE41u` &L52@/ <bM=M>20$" $ +32>54&'47.4>32#"'&#"#".@$S UQK >Y@ GE;:62)(!  $" +4632#"&!#&#"&47>727&#"&47>&'!"NE%' @<b`QM=M =M >CA&#(("'$" +4632#"&32765## 546?65#"#"7463 327'"&RF$'KPX@6b`U   UM = K >@4b`US   U K >YY@WUKJFB981-""#'% +#47654#"#"'4632!#&#"&47>727&#"&47>&'!"CD53#J 7]KVLj 2nLk NZ%-D?63 [u!wP%/+-7F3#H 7'JN - ^  11 4XJ)l/1  11   [JB'Uh@eOQ<b  `b  `M = M = M =M >TRNLFD(("'$'% +#676'4"#"&5463232765## 546?65#"#"7463 327'"&B@5:2;1^;JHKHFn)op D`8)H5 %'NuF9+@3FJL'#D  "%PR+17[[m Vf'+ ++ /JV+1:!(L= Uz@ <:KPX@$dUM =K >@"dSUK >Y@SQGFB>54-)$ +#"747'&'&'676!#&#"&47>727&#"&47>&'!"s>(Zy T`{T{%j 2nLk NZ%-D?63 [u!wP%!4_mZ! =@F7?- ^  11 4XJ)l/1  11   [J &T@NP@:ddb`M=N =M >Y@SQMKEC@>64,*('#!$! +632#"&547'&'&'67632765## 546?65#"#"7463 327'"& ," Lw\sbH$KHFn)op D`8)H5 %'NuF9% V{L^s7s+17[[m Vf'+ ++ /JV+1:!(L= Vz@ < :KPX@$dUM =K >@"dSUK >Y@TRHGC?65.*% +#"/6&'&'676!#&#"&47>727&#"&47>&'!"V (,Zy T`{T{%j 2nLk NZ%-D?63 [u!wP% g1WmZ! =@F7?- ^  11 4XJ)l/1  11   [  &T@NP@:ddb`M=M =N >Y@SQMKEC@>64,*('#!$% +#"/762&'&'67632765## 546?65#"#"7463 327'"&d  ,1 eLw\sbH$KHFn)op D`8)H5 %'NuF9 !&p{L^s7s+17[[m Vf'+ ++ /JV+1:!(L=)0g@"&$ KPX@6b`U   UM = K >@4b`US   U K >YY@+*ecYXTPGF?;-,*0+0#'% +#47654#"#"54632&'&'676!#&#"&47>727&#"&47>&'!"DD63#I 7\LVL7Zy T`{T{%j 2nLk NZ%-D?63 [u!wP%/+-7F2#H 7'JN3mZ! =@F7?- ^  11 4XJ)l/1  11   [J*5cu@r#'% ]+_<b  `b  `U M = M =M >b`\ZTROMEC;97620$'% +#676'4#"#"&54632&'&'67632765## 546?65#"#"7463 327'"&J@591;1^;JHLw\sbH$KHFn)op D`8)H5 %'NuF9+?3FJL) D #%PRn{L^s7s+17[[m Vf'+ ++ /JV+1:!(L=( Ki@Q ` @.  U US UK >Y@ MLca\ZUSLiMiIG=<84+*# +&'&'676!#&#"&47>727&#"&47>&'!"2676#".'&"'632Zy T`{T{%j 2nLk NZ%-D?63 [u!wP%&,$cL+<F3*,*4$',:BmZ! =@F7?- ^  11 4XJ)l/1  11   [);Zg $%*3 $J`+6d@|(&$  ^, `<  b  `U  V M = M =M >ca][USPNFD<:8731 +2676#".'&#"'62&'&'67632765## 546?65#"#"7463 327'"&D#(!) #?0'''/u%)44Lw\sbH$KHFn)op D`8)H5 %'NuF9+? '-6 '{L^s7s+17[[m Vf'+ ++ /JV+1:!(L=Vu  Wy :KPX@& UQM =K >@$S UQK >Y@USIHD@76/+   +&'&'6764632#"&!#&#"&47>727&#"&47>&'!"Zy T`{T{%E%'QOKICA><42*(&%! +&'&'6764632#"&32765## 546?65#"#"7463 327'"&Lw\sbG$zF$'@1 ddUS   V K  >Y@ SQGFB>54-)  "&$+'"547#"&'33267!#&#"&47>727&#"&47>&'!"TD,cks9 }TP aj 2nLk NZ%-D?63 [u!wP%!$:ih}k9DC:- ^  11 4XJ)l/1  11   [J %Sh@eMO<ddb  ` U M = M =M >RPLJDB(("(%$ +62"&547'267#"&'32765## 546?65#"#"7463 327'"& 1&{}7`mǢKHFn)op D`8)H5 %'NuF9)"  RfbV+17[[m Vf'+ ++ /JV+1:!(L= V :KPX@3 ddU   VM = K  >@1 ddUS   V K  >Y@ TRHGC?65.*  "&%+#"/6#"&'33267!#&#"&47>727&#"&47>&'!"w +0ks9 }TP aj 2nLk NZ%-D?63 [u!wP% r5h}k9DC:- ^  11 4XJ)l/1  11   [J %Sh@eMO<ddb  ` V M = M =M >RPLJDB(("(%$% +#"/7672267#"&'32765## 546?65#"#"7463 327'"&lo  !07 {}7`mǢKHFn)op D`8)H5 %'NuF9 $)RfbV+17[[m Vf'+ ++ /JV+1:!(L=%)0g K PX@Fb`XU U V M = K  >KPX@Gb``U U V M = K  >@Eb``U U  S V K  >YY@!+*ecYXTPGF?;-,*0+0))"$#'%+#47654#"#"'4632#"&'33267!#&#"&47>727&#"&47>&'!"DD53#J 7]KVLqks9 }TP aj 2nLk NZ%-D?63 [u!wP%/+-7F3#H 7'JNhh}k9DC:- ^  11 4XJ)l/1  11   [J)4by@v)#"\* ^<b`  b  `U U M = M =M >a_[YSQNLDB"(%$'%+#67654#"#"&54632267#"&'32765## 546?65#"#"7463 327'"&;?5:1;1_;JG{}7`mǢKHFn)op D`8)H5 %'NuF9+?3FJK)!D #%PRRfbV+17[[m Vf'+ ++ /JV+1:!(L=( Ki@ Q ` @@  b  U  UUS  V K >Y@)MLca\ZUSLiMiIG=<84+*# "" +#"&'33267!#&#"&47>727&#"&47>&'!"2676#".'&"'632ks9 }TP aj 2nLk NZ%-D?63 [u!wP%&-$cL+<F4*+*4$',9Hh}k9DC:- ^  11 4XJ)l/1  11   [);Zg $%*3 $J^*5c@*$#]+ _ <  b  `UV U M = M = M  >b`\ZTROMEC;97620(&!  +2676#".'&#"'62267#"&'32765## 546?65#"#"7463 327'"&3#)!) #?0'''/u%)3{}7`mǢKHFn)op D`8)H5 %'NuF9+? '-6 'RfbV+17[[m Vf'+ ++ /JV+1:!(L=Vo  WKPX@5 d U  VQ M = K  >@3 d U  S  VQ K  >Y@!USIHD@76/+   "" +#"&'332674632#"&!#&#"&47>727&#"&47>&'!"ks9 }TP E%'PNJHB@(("'$%% +267#"&'4632#"&32765## 546?65#"#"7463 327'"&{}8amF$' ZZ UQ N  =M=L >@< ZZ   U UQM=L >Y@ KHA@?;8721.)$! N N$" +4632#"&26762"'.+;26&#!"&47>54&'&473!272'.+"E%';A)%=Q}:1  1RT3@J' 2cHss??sd 50!#'VjP7%=D'#?BK1[ bOJj 70%;H9 1 <{5{;2 F{d;HLVB -:@7bUQM=M>#"%"#"$" +4632#"&%2'4&#"#"'&543 #%326mE%';A)%=osdF4L> #n`Z&AZVm%=D'#?Bub-n,o#m19^8@T"&7K(PX@Nb  `  Z   ZUU N  =M= L >@Lb  `  Z   ZU   UUM= L >YY@ [XQPOKHGBA>941.,('! ^^#'%+#47654#"#"'463226762"'.+;26&#!"&47>54&'&473!272'.+"CD53#J 7]KVL}:1  1RT3@J' 2cHss??sd 50!#'VjP7/+-7F3#H 7'JN1[ bOJj 70%;H9 1 <{5{;2 F{d;HLBB'>V@S<b`  b  UM =M= M>=;86%"#"$'% +#67654"#"&54632%2'4&#"#"'&543 #%326b?591;1_;JGsdF4L> #n`Z&AZVm+@3FJL'#D  "%PRRub-n,o#m19P`@ V$!(9 @G  Z ZU V  UUM= L >Y@*]ZSRQMJIDC@;630.*)#"`` +2676#".'&#"'63226762"'.+;26&#!"&47>54&'&473!272'.+" #)!) #?0'''/u%)3}:1  1RT3@J' 2cHss??sd 50!#'VjP7+@ '-5 '1[ bOJj 70%;H9 1 <{5{;2 F{d;HLB(?b@_<  bU  V  U M = M>><9753.,*)%#  +2676#".'&#"'632%2'4&#"#"'&543 #%326P#)!) #?/'''/u%)4sdF4L> #n`Z&AZVm+? '-5 &ub-n,o#m1O Z@%  P "3@: d ZZ   U UM=L >Y@WTMLKGDC>=:50-*($#ZZ$ +#"747'&'&'67626762"'.+;26&#!"&47>54&'&473!272'.+">(Zy T`{T{%}:1  1RT3@J' 2cHss??sd 50!#'VjP7!4_ mZ! =@F7?U1[ bOJj 70%;H9 1 <{5{;2 F{d;HL &=@ @/ddbVM=M>Y@<:7531,*('#!$! +632#"&547'&'&'676%2'4&#"#"'&543 #%326( ,# Lw\sbG%sdF4L> #n`Z&AZVm% V{L^s7sub-n,o#m19 [@%  Q #4@: d ZZ   U UM=L >Y@XUNMLHED?>;61.+)%$[[% +#"/6&'&'67626762"'.+;26&#!"&47>54&'&473!272'.+"ծ (,Zy T`{T{%}:1  1RT3@J' 2cHss??sd 50!#'VjP7 g1WmZ! =@F7?U1[ bOJj 70%;H9 1 <{5{;2 F{d;H8B &=@ @/ddbUM=M>Y@ #"%"#/$% +#"/762&'&'676%2'4&#"#"'&543 #%326d  ,1 [Lw\sbG%sdF4L> #n`Z&AZVm !&p{L^s7sub-n,o#m19 PlG@#  F )K(PX@N b  ` ZZ  UU N  =M=L >@L b  ` ZZ  U   UUM=L >YY@$kifd][VUMJCBA=:9430+&# PP +&'&'67626762"'.+;26&#!"&47>54&'&473!272'.+"#476'4#"#"'4632;Zy U`{T{$/}:1  1RT3@J' 2cHss??sd 50!#'VjP7yDD64#I 7\LVLwmZ! =@F7?U1[ bOJj 70%;H9 1 <{5{;2 F{d;H/+-7F3#H 7'JNLm*5L_@\#'%<b`  bU  UM= M>KIFDB@;97620-+$'% +#676'4#"#"&54632&'&'676%2'4&#"#"'&543 #%326m@5:2;1^;JHLw\sbG%sdF4L> #n`Z&AZVm+?3FJL) D #%PRn{L^s7sub-n,o#m19% Pn@(V e  F )@F ZZ  U  U   UUM=L >Y@(RQhfa_ZXQnRnMJCBA=:9430+&# PP +&'&'67626762"'.+;26&#!"&47>54&'&473!272'.+"2676#".'&"'62Zy T`{T{%}:1  1RT3@J' 2cHss??sd 50!#'VjP7l&-$cL+<F4*+*4$',95mY! =@F7?1[ bOJj 70%;H9 1 <{5{;2 F{d;H*;Zg $%*3 %LBX+6Mi@f(&$  <  bU  V  U M = M>LJGECA<:8731., +2676#".'&"'632&'&'676%2'4&#"#"'&543 #%326m#(!) #?0'''/u%)48Lw\sbG%sdF4L> #n`Z&AZVm+? '-5 &{L^s7sub-n,o#m1V9y \@R $5

ZZ UQ N  =M=L >@< ZZ   U UQM=L >Y@ YVONMIFE@?<72/,*&%\\ +&'&'6764632#"&26762"'.+;26&#!"&47>54&'&473!272'.+"bZy T`{Tz%IE%';A)%=Q}:1  1RT3@J' 2cHss??sd 50!#'VjP7ymZ! =@F7?1%=D'#?BK1[ bOJj 70%;H9 1 <{5{;2 F{d;HLVBH $;J@G :bUQM=M>:8531/*(&%! +&'&'6764632#"&%2'4&#"#"'&543 #%326Lw\sbG%jE%';A)%=osdF4L> #n`Z&AZVmH{L^s7sq%=D'#?Bub-n,o#m1B=K PX@-bXU K =K >K(PX@.b`U K =K >@,b`U UK >YY@ ;:CC#'% +#47654#"#"'4632&#"&47>54&'&47327DD53#J 7\LVLP@r PR s??s GV s?/+-7F3#H 7'JN{; 11 <{5{;2  1 ;{5B9/@,b`M == >H$'%+#67654"#"&54632&#"&47>54&'&7672@591<1^@UQK >Y@ CC$"+4632#"&&#"&47>54&'&47327E%'@UQ= >YH$$$"+4632#"&4632#"&&#"&47>54&'&7672F%';B)%=F%';A)%>1b \^b2/V Z%=D'#?B3%=D&#@DV{9 11 8{V-#"fLVTD %0@-QM =M> $"  $" +4632#"&"32# ! qE%';A)%=5ǴVT!v%=D'#?Bb: +T{h'3TV '.@+QM=M>#!''&%$"+4632#"&47632'""32654&F%';A)%>qzǓPu=sLZ%=D'#?B ˅sւs EBTLT'5yK PX@,bXUM =M>@-b`UM =M>Y@42.,#!''#'% +#47654#"#"54632"32# ! dCD53#J 7\LVKǴVT!vD/,-7F3#H 8'IM: +T{h'3TB+8B@?b`M =M=M>-,42,8-8&%$'% +#676'4"#"&5463247632'""32654&y@591;1^;JHqzǓPu=sLZ+@3FJL'#D  "%PR˅sւs EBTLT #1<@9 <:dM =M>0.*(##$ +#"747'&'&'676"32# ! T>(Zy T`{T{%ǴVT!v!4_*mZ! =@F7?l: +T{h'3T *7o@ @ ddM=M>Y@,+31+7,7)'!$! +632#"&547'&'&'67647632'""32654&9 ," Lw\sbG%LqzǓPu=sLZ% V{L^s7sT˅sւs EBTLT $2<@9 < :dM =M>1/+) $$% +#"/6&'&'676"32# ! D ',Zy T`{T{%ǴVT!v g1[mZ! =@F7?l: +T{h'38 *7o@ @ ddM=M>Y@,+31+7,7)'!$% +#"/762&'&'67647632'""32654&d  ,1 Lw\sbG%LqzǓPu=sLZ !&p{L^s7sT˅sւs EBTLT)5C@"&$ @-b`UM =M>Y@+*B@<:1/*5+5#'% +#47654#"#"54632&'&'676"32# ! DD53#I 7\LVLNZy T`{T{%ǴVT!v/+-7F3#H 7'JN;mZ! =@F7?l: +T{h'3T*9FN@K#'%<b`UM=M>;:B@:F;F860.$'% +#67654#"#"&54632&'&'67647632'""32654&?591<1^)(?=861/(E)E&$  +&'&'676"32# ! 2676#".'&"'62Zy T`{T{%ǴVT!v&-$dK,<F4*,*4$',9=mY! =@F7?: +T{h'3*;Zg $%*3 %Tb+:GX@U(&$ <U V M=M><;CA;G20,*!%% +&'&'6764632#"&"32# ! Zy T`{T{%TE%';A)%=5ǴVT!vymZ! =@F7?1%=D'#?Bb: +T{h'3TVJ (59@6 :QM=M>*)1/)5*5'% +&'&'6764632#"&47632'""32654&Lw\sbG%mF%';A)%>qzǓPu=sLZJ{L^s7so%=D'#?B ˅sւs EBTLT 6L@I5<:ddbM =M> 1/&$"   $ +'"547"32# !232654&'.5432-X'9wǴVT!$Q)1/^9T)Aw: +T{h'3N)-7#PqITb .;&@-dddbM=M>Y@0/75/;0;))"($! +632'"&5474763232>54'.5432'""32654&7 ?2' qzy\./:11^9JH@u=sLZ^/' %:˅1 )'5#Pk^ ւs EBTLT 7I@F6< :ddbM =M> 20'%#!  % +#"'%6"32# !232654&'.5432T 9@ǴVT!$Q)1/^9T =: +T{h'3N)-7#PqITb .;&@-dddbM=M>Y@0/75/;0;))"$$% +#"/76324763232>54'.5432'""32654&  ,?Fqzy\./:11^9JH@u=sLZ^)/B˅1 )'5#Pk^ ւs EBTLT'HG@<  b``b U M =M>Y@CA8642.,#!''#'% +#47654#"#"'4632"32# !232654&'.5432DD53#J 7\LVLۨǴVT!$Q)1/^9T/+-7F3#H 7'JN: +T{h'3N)-7#PqITB=JY@V5 <b``bM = M= M>?>FD>J?J))"%$'% +#67654"#"&546324763232>54'.5432'""32654&@591<1^EC:8640.%#)) +2676#".'&#"'62"32# !232654&'.5432=#)!) #?0'''/t %)3`ǴVT!$Q)1/^9T+? '-6 ': +T{h'3N)-7#PqIT?Lf@c7 <bbU U M= M>A@HF@LAL><31(&$"  +2676#".'&#"'6324763232>54'.5432'""32654&B$Q>  9+"$"+h $/Cqzy\./:11^9JH@u=sLZ/Fiv+1; + ˅1 )'5#Pk^ ւs EBTLVT) 8G@D7<dbQM =M> 31(&$"  $" +4632#"&"32# !232654&'.5432qE%';A)%=5ǴVT!$Q)1/^9T%=D'#?Bb: +T{h'3N)-7#PqITV} ,9C@@$<dbQM=M>.-53-9.9))"%$" +4632#"&4763232>54'.5432'""32654&F%';B(%>qzy\./:11^9JH@u=sLZ%=D'#?B ˅1 )'5#Pk^ ւs EBT V?- FRK(PX@QK =M>@UQM>Y@ 3+N($"+4632#"&2>54.'&47327#".54&'&4727XF%';B)%=2/ddN- 3:C15d9-qњZX;c{B%=D'#?BVo-`@\xR?D' 2  1 9u-׮Z(eϕRy=2  1 ;-V)s FC@@  bQM=N > DC210-*)#! F F$" +4632#"&"&54&'&4732723276'4&'&473272'&'&F%';A)%>+{3LF3d@\q!1N F38^ N%=D'#?BTy`>1  =yZb3b@1  =wP= % ?F2 z ?VK PX@-bXU K =M>K(PX@.b`U K =M>@,b`U UM>YY@ UT3+N(#'% +#47654#"#"546322>54.'&47327#".54&'&4727dCD53#J 7\LVK2/ddN- 3:C15d9-qњZX;c{B/+-7F3#H 7'JNVo-`@\xR?D' 2  1 9u-׮Z(eϕRy=2  1 ;-)BWW@Tb`  bM =M =N >UTCBA>;:42,+*'WW$'% +#67654"#"&54632"&54&'&4732723276'4&'&473272'&'&?5:1;1_;JG׍{3LF3d@\q!1N F38^ N+@3FJL'#D  "%PRy`>1  =yZb3b@1  =wP= % ?F2 z  Od@ <:K(PX@!ddM =M>@ddUM>Y@ (>(C,$+'"547#".54&'&473272>54.'&4732654'&74632BX'91#-qњP^;c'L{B2/jdN- 56EL)F7-m_)AwJb-׮Z+fӓLy=2  1 ;{Vo-f@\xRBC% 2 ! D%+@W-)b W@4D<3;K!PX@5  b  b  U =M=N >@5 d  b  b  UM=N >Y@UT?=:82/,+%#WW$! +632'"&547"&54&'&4732723276'4&'&473275>54'.5432'&'&3 ?2' ${3LF3d@\q!1N F7811^9H>=8^ N^/' %y`>1  =yZb3b@1 #)3#PkI-Rv3wP= % ?F2 z  P^ :K(PX@!ddM =M>@ddUM>Y@ (>(C,%+#"'%6#".54&'&473272>54.'&4732654'&74632N 9@1#-qњP^;c'L{B2/jdN- 56EL)F7-m_ =sJb-׮Z+fӓLy=2  1 ;{Vo-f@\xRBC% 2 ! D%+@W-)b W@4D<3;K!PX@5  b  b  U =M=N >@5 d  b  b  UM=N >Y@UT?=:82/,+%#WW$% +#"/7632"&54&'&4732723276'4&'&473275>54'.5432'&'&  +?G{3LF3d@\q!1N F7811^9H>=8^ N^)/y`>1  =yZb3b@1 #)3#PkI-Rv3wP= % ?F2 z aK PX@4  b`X UM  =M>K(PX@5  b`` UM  =M>@3  b`` U UM>YY@^\TQ(C*#'% +#47654#"#"54632#".54&'&473272>54.'&4732654'&74632hCD53#J 7\LVK1#-qњP^;c'L{B2/jdN- 56EL)F7-m_/+-7F3#H 7'JNJb-׮Z+fӓLy=2  1 ;{Vo-f@\xRBC% 2 ! D%+@W-)Bfp@mC SdcNLIGA>;:42,+*'ff$'%+#67654"#"&54632"&54&'&4732723276'4&'&473275>54'.5432'&'&?5:1;1_;JG׍{3LF3d@\q!1N F7811^9H>=8^ N+@3FJL'#D  "%PRy`>1  =yZb3b@1 #)3#PkI-Rv3wP= % ?F2 z dc@  @/  bU V  U M>Y@`^VSEC;:730/(& +2676#".'&#"'62#".54&'&473272>54.'&4732654'&74632h#)!) #?0'''/t %)31#-qњP^;c'L{B2/jdN- 56EL)F7-m_+? '-6 'Jb-׮Z+fӓLy=2  1 ;{Vo-f@\xRBC% 2 ! D%+@W-)gz@w D T edOMJHB?<;53-,+(gg +2676#".'&#"'632"&54&'&4732723276'4&'&473275>54'.5432'&'&`#)!) #?0'''/u%)3m{3LF3d@\q!1N F7811^9H>=8^ N+? '-5 &Vy`>1  =yZb3b@1 #)3#PkI-Rv3wP= % ?F2 z V) Q]K(PX@#dQM =M>@!dUQM>Y@ (>(C*$" +4632#"&#".54&'&473272>54.'&4732654'&74632HE%';A)%=1#-qњP^;c'L{B2/jdN- 56EL)F7-m_%=D'#?BGJb-׮Z+fӓLy=2  1 ;{Vo-f@\xRBC% 2 ! D%+@W-V)} U[@X2B<1;  b  UQM=N > SR=;860-*)#! U U$" +4632#"&"&54&'&4732723276'4&'&473275>54'.5432'&'&F%';A)%>1{3LF3d@\q!1N F7811^9H>=8^ N%=D'#?BTy`>1  =yZb3b@1 #)3#PkI-Rv3wP= % ?F2 z GY :K(PX@dK =K >@dUK >Y@ 33C% +#"'%6&#"&47>54&'.'&472776&'&4727 9@=FPV F2HF { U& $H or  JO)  ={; 11 <{78/U0 0  1 *B]>*2  1 3JF*?3%b FSK!PX@b ==N>@dd=N>Y@ =:L!$"$%+#"/7632#"&54632327676'.'&4732776&'&4727я  ,?FTs)73' #F!16';?RTPP"<> {R F?!+^)/V+!=;dXF{[61  2?@V:81%1  27DfV- HYK(PX@ QK  =K >@ UQK >Y@ >;3C$" +4632#"&&#"&47>54&'.'&472776&'&4727E%'*2  1 3JF*?3%s D'@$V=N>;8L!$#$"+4632#"&'#"&54632327676'.'&4732776&'&4727F%';B)%=Ts)73' #F!16';?RTPP"<> {R F?!+%=D'#?B +!=;dXF{[61  2?@V:81%1  27DfXK PX@/bXU K  =K >K(PX@0b`U K  =K >@.b`U  UK >YY@NKHG>=3C#'% +#47654#"#"54632&#"&47>54&'.'&472776&'&4727CD53#J 7\LVK\FPV F2HF { U& $H or  JO) /+-7F3#H 7'JN{; 11 <{78/U0 0  1 *B]>*2  1 3JF*?3%BU:@7b`M ==N>LIL!$#$'% +#67654"#"&54632#"&54632327676'.'&4732776&'&4727?591;1_;JGTs)73' #F!16';?RTPP"<> {R F?!++@3FJL'#D  "%PRȓ+!=;dXF{[61  2?@V:81%1  27DfdZ@ @)U  U   VK >Y@ PMJI@?<965,+($!  +2676#".'&#"'62&#"&47>54&'.'&472776&'&4727#)!) #?0'''/u%)3 FPV F2HF { U& $H or  JO) +? '-6 '{; 11 <{78/U0 0  1 *B]>*2  1 3JF*?3%VK@H<U V =N>MJ:6*('%! +2676#".'&#"'632#"&54632327676'.'&4732776&'&4727#)!) #?0'''/t $)3Ts)73' #F!16';?RTPP"<> {R F?!++? '-5 &"+!=;dXF{[61  2?@V:81%1  27Df#- 9n@.(@ZUL >Y@32-)9843 +;2654'#267%!"'54?>54&/&=73!%33>?)nqTjs%=+ V;7=25:7;5=43-/,%8Zc ˋ'+,-)'',+#/79EM@JC5 < b  T =K >BA=<9843/.+*)(CC+3#&#"&47>5!&#".7>5#5354&'&7672!54&'&76723` \V `33`\V  `3)Z hf)Z i^E{; 11 <{{; 11 <{Eq1&*RLq1&*R\ D!@ :UM>+$+2#"4%32654&# !ddRP g8mwV@ :M>+$ +#"&5%32654&'Ģq \bJEF?,$Ρq1+29`@$ 3 @ U M>Y@ 86%##83#" + &#"=730%+" 654+"=7307#"#"&5&%26%7iT N9 D R; VD4Fy3Tt>3mX!y)'!!;##)'+@òF9HX7k#s C>@;94#+<U K=M>=;38#C'#" +326?"#"&54$3654'.#"=7327"674+"=737"$%JVJL3Js #13\;LX V yH%=7H!@+ħG8`5VX;))%-5''3/)uFy8E@9 @?  bM = == M=N= M>Y@DB><876432+)#!  +2'>54#"54632672#"/#"&54>3276723263'.#"327Dob ;F^/})J'1!#TDq0NiTLnaFH 'bVjfmr[;B # X)+N+w-`RPƇm3 N+өBFy9F@ :  K1PX@Cb  bM =  = M=N= M>@Gb  bM = = = M=N= M>YY@ EC?=987543,*$"  +2#".54632672#"/#"&54>3276723263'.#"32`^?;B[+D)J'1!#TDq0NiTLnaFH 'bVjfmrFV%)X #5L;9]w-`RPƇm3 N+өBFyGT$@H.  (K1PX@Hb  b=M=  = M =N= M>@Lb  b=M=  = = M=N= M>YY@"SQMKGFECBA:820,*'&$"  +2'>54#"546&54632'"'32672#"/#"&54>3276723263'.#"32+7Dob ;F^/+!5 O )J'1!#TDq0NiTLnaFH 'bVjfmr[;B # X)+N+R )% Xw-`RPƇm3 N+өBFy HUu@ I/! )K&PX@E  b  bM=  = M  =N=M>K1PX@I  b  b=M=  = M  =N=M>@M  b  b=M=  = = M =N=M>YYY@$TRNLHGFDCB;931-+('%#  +2#".546&'4632'"'32672#"/#"&54>3276723263'.#"32B`^@;B\+D+!5 P )J'1!#TDq0NiTLnaFH 'bVjfmrEV%)X #5L;9\R )% \w-`RPƇm3 N+өBFy'4ES#@Q<; (K1PX@I  bb = M ==M=N= M>@M  bb = M ===M=N= M>YY@GF65OMFSGSB@5E6E31$!&$#"+32672#"/#"&54>3276723263'.#"322'>54#"546%2#"=6F)J'1!#TDq0NiTLnaFH 'bVjfmr7Dob ;F^/'%D w-`RPƇm3 N+өB#[;B # X)+N+ - >)FyGT@ H.  (K&PX@F  b  bM=  = M  =N=M>K1PX@Lb  `  bM=  = M  =N=M>@Pb  `  bM=  = = M =N=M>YYY@(SQMKGFECBA:820,*'&$"  +2#".546%2#"=632672#"/#"&54>3276723263'.#"32P`^@;B\+D'#D )J'1!#TDq0NiTLnaFH 'bVjfmrEV%)X #5L;9\ - >)w-`RPƇm3 N+өBFy5'4EU@"IQ <; (@Pb U U M ===M=N= M>Y@!GF65TROMLJFUGUB@5E6E31$!&$#"+32672#"/#"&54>3276723263'.#"322'>54#"'46727#"&#"'632F)J'1!#TDq0NiTLnaFH 'bVjfmr7Dnc ;F^/;%-%-';$-+o;w-`RPƇm3 N+өBZK1PX@T  bbUUM = = M  = N  = M   >@X  bbUUM = = = M = N  = M   >YY@,USOMIHGEDC<:42.,)(&$!!  +27#"&#"'6322#".54632672#"/#"&54>3276723263'.#"32j;&-%-';$-+o;\`^?<B\+C)J'1!#TDq0NiTLnaFH 'bVjfmr`^^ `FV%)X #6K<9]9w-`RPƇm3 N+өBN@ @& US UK >Y@LJ@?;7.-&"  +2'>54#"546!#&#"&47>727&#"&47>&'!"77Dnc ;F^/j 2nLk NZ%-D?63 [u!wP%ZKPX@0b U   UM =K >@.b US   UK >YY@MKA@<8/.'#  +2#".546!#&#"&47>727&#"&47>&'!"/`^?< B[+D#j 2nLk NZ%-D?63 [u!wP%FV%)X #6K<9]w- ^  11 4XJ)l/1  11   [&]@ KPX@1b U   UM = K >K(PX@/b US   U K >@9b US   UK = K >YYY@ ! [YONJF=<51#" &!&  +2'>54#"546&54632#"'!#&#"&47>727&#"&47>&'!"%7Dob ;F^/+!5 P j 2nLk NZ%-D?63 [u!wP%[;B # X)+N+R )% - ^  11 4XJ)l/1  11   [ '^@   KPX@5b  UM = M = L  >K(PX@1b  U  S  U L  >@5db  U  S  U L  >YYY@$"!\ZPOKG>=62.-$#!'"'  +2#".546&'4632'"'!#&#"&47>727&#"&47>&'!"`_?<B\+C+!5 P k 1  nLjNZ%-C@54[u !vO%FV%)X #5L;9]R )% - ^  11 4XJ)l/1  11   [%\!@ KPX@3b  U  UM = L  >K(PX@1b  US  U L  >@5 db US  U L  >YYY@& ZXNMIE<;40,+"!% %  +2'>54#"546%2#"=6!#&#".7>727&#"&47>&'!"37Dob ;F^/'%D j 1 nLj NZ$-D@53Zu !wP% [;B # X)+N+ - >)u- ^  11 4XJ)l/1  11   [&]f@  KPX@6b  UM = M = L  >KPX@4b  U  U M = L  >K&PX@2b  U  S  U L  >@8b`  U  S  U L  >YYYY@(! [YONJF=<51-,#" &!&  +2#".546%2#"=6!#&#".7>727&#"&47>&'!"T`^@;B\+D'# D j 1 nLj NZ$-D@53Zu !wP% FV%)X #5L;9]- >)y- ^  11 4XJ)l/1  11   [ '^^@ <;:K PX@= U  UM=M = M = K  >K PX@;U U  UM = M = K  >KPX@= U  UM=M = M = K  >KPX@;U U  UM = M = K  >KPX@= U  UM=M = M = K  >KPX@;U U  UM = M = K  >KPX@9U  S U  UM = K  >@7UU  S U  U K  >YYYYYYY@("!\ZPOKG>=62$#!'"'   +2'>54#"'46727#"&#"'672!#&#"&47>727&#"&47>&'!"7Dnc ;F^/;%-%-';#.+o;Pj 2 nLkNZ%-D?63[u !wP%Z;B " Y)+N*a^^ a- ^  11 4XJ)l/1  11   [!(_@  < ;:K PX@DZU  UM=M = M = K  >KPX@EbU  UM=M = M = K  >KPX@CbUU  UM = M = K  >KPX@AbU  SU  UM = K  >@?bUU  SU  U K  >YYYY@*#"][QPLH?>73%$"(#(!!  +27#"&#"'6722#".546!#&#"&47>727&#"&47>&'!"L;%-%-';#-+n;\`^@;B\+Dj 2nLk NZ%-D?63 [u!wP%fa^^ aEV%)X #5L;9\c- ^  11 4XJ)l/1  11   [Z@K(PX@!   <@!   K1PX@7b  I  UM =M=M>@8b  U  UM =M=M>YY@?=<:86431/-+'%  +2'>54#"546327'"&54675&54632#"'&#"37632#"&#"7Dob ;F^/{sTVds)/'+^11;=W V`[;B # X)+N+bRmX !{jF#=PZ9)#CX+/ `ZA[K(PX@ "!  <@ "!  K(PX@:bb    UM =M=M>K1PX@?bb  I   UM =M=M>@@b  b  U  UM =M=M>YYY@@>=;975420.,(&  +2#".546327'"&54675&54632#"'&#"37632#"&#"`^?< B[+DsTVds)/'+^11;=W V`FV%)X #5L;9]`RmX !{jF#=PZ9)#CX+/ `ZOpK(PX@0/ '% <@0/ '% K(PX@?b  b    V=M =M=M>K1PX@Db  b J   V=M =M=M>@Eb  b U  V=M =M=M>YYY@ NLKIGECB@><:64*($"  +2'>54#"546&54632'"'327'"&54675&54632#"'&#"37632#"&#"7Dob ;F^/+!5 O msTVds)/'+^11;=W V`Z;B # X)+N*R )% RmX !{jF#=PZ9)#CX+/ `Z PK(PX@ 10 (& <@ 10 (& K&PX@<b  b    VM= M=M>K(PX@@b  b    V=M= M=M>K1PX@Eb  b J   V=M= M=M>@Fb  b U  V=M= M=M>YYYY@"OMLJHFDCA?=;75+)%#  +2#".546&54632'"'327'"&54675&54632#"'&#"37632#"&#" `^?< B[+D+!5 P qsTVds)/'+^11;=W V`EV%)X #5L;9\R )% RmX !{jF#=PZ9)#CX+/ `ZNvK(PX@/. &$ <@/. &$ K(PX@=b    UM =M=M=M>K1PX@Bb I   UM =M=M=M>@Cb U  UM =M=M=M>YYY@$MKJHFDBA?=;953)'#!  +2'>54#"546%2#"=6327'"&54675&54632#"'&#"37632#"&#"7Dob ;F^/'%D sTVds)/'+^11;=W V`Z;B # X)+N* - >)\RmX !{jF#=PZ9)#CX+/ `ZOK(PX@ 0/ '% <@ 0/ '% K(PX@Hb`    UM= M=M=M>K1PX@Mb` I   UM= M=M=M>@Nb` U  UM= M=M=M>YYY@&NLKIGECB@><:64*($"  +2#".546%2"756327'"&54675&54632#"'&#"37632#"&#"`_?<B\+C'#C sTVds)/'+^11;=W V`EV%)X #5L;9\ . >)`RmX !{jF#=PZ9)#CX+/ ` S@I ,@> ZZ  U   UUM=L >Y@$PMFED@=<763.)&#!SS  +2'>54#"54626762"'.+;26&#!"&47>54&'&473!272'.+"%7Dob ;F^/}: 11 RT3@J' 2cHs s?@r d 50!#'VjP7ZK(PX@H Z  b  Z UU N  =M=L >@F Z  b  Z U   UUM=L >YY@&QNGFEA>=874/*'$"TT  +2#".54626762"'.+;26&#!".7>54&'.73!272'.+"`_?<B\+D}9 11 RT3@I'!3bHs  s?@r  d 5/!#'VjP7FV%)X #5L<9]1[ bOJj 70%;H9 1 <{5{;2 F{d;HTb=@X&#*;K(PX@I  Zb   Z UU N  =M= L >@Kd  Zb   Z U   UUM= L >YY@(! _\UTSOLKFEB=8520,+%$ b!b  +2'>54#"546&54632'"'26762"'.+;26&#!"&47>54&'&473!272'.+"7Dob ;F^/+!5 P }9 21 RT3?I'!3bHs s@@s e 5/!#'VjP7[;B # X)+N+R )% 1[ bOJj 70%;H9 1 <{5{;2 F{d;H1 c@ Y'$+< KPX@L  Zb ZUM= N =M= L >K&PX@J  Zb Z UU N =M= L >K(PX@Nd  Zb Z UU N =M= L >@Ld  Zb Z U  UUM= L >YYYY@*"!`]VUTPMLGFC>9631-,&%!c"c  +2#".546&'4632'"'26762"'.+;26&#!".7>54&'.73!272'.+"`_?<B\+C+!5 P }:1  1RT3@I'!3bHs s?@r d 5/!#'VjP7EV%)X #5L;9\Q )% 1[ bOJj 70%;H9 1 <{5{;2 F{d;H5aK&PX@W%"):K&PX@J  Zb   Z UU N  =M= L >K(PX@Nd  Zb   Z UU N  =M= L >@Ld  Zb   Z U   UUM= L >YYY@, ^[TSRNKJEDA<741/+*$#a a  +2'>54#"546%2#"75626762"'.+;26&#!"&47>54&'&473!272'.+"7Dob ;F^/'%C }:1  1RT3@J' 3bHss?@rd 50!#'VjP7Z;B # X)+N* - >)1[ bOJj 70%;H9 1 <{5{;2 F{d;Hb K&PX@ X&#*; KPX@M  Zb ZUM= N =M= L >K&PX@K  Zb Z UU N =M= L >K(PX@Q  Zb` Z UU N =M= L >@O  Zb` Z U  UUM= L >YYYY@.! _\UTSOLKFEB=8520,+%$ b!b  +2#".546%2#"=626762"'.+;26&#!"&47>54&'&473!272'.+"`^?< B[+D'# D }9 22 RT4?I'!3bGs s??s d 5/!"'VkP8FV%)X #5L;9]- >)1[ bOJj 70%;H9 1 <{5{;2 F{d;H9=C@@1(< 9M=M=>;:0.'%  +2'>54#"5467>32'64&#"#"'54&'&7672s7Dob ;F^/Vg 4Ve Z 0V b Z;B # X)+N*J [W7%R)E$Lk3w{ E+7pV-#'%9>|@ 2)@$bM=M=>Y@<;1/(&  +2#".5467>32'64&#"#"'54&'&7672R`^@;B\+DVg 4Ve Z 0V b FV%)X #5L;9]E [W7%R)E$Lk3w{ E+7pV-#'%9L@@7 @)b=M=M=>Y@JI?=64&$  +2'>54#"546&54632'"'7>32'64&#"#"'54&'&76727Dob ;F^/+!5 P Vg 4Ve Z 0V b Z;B # X)+N*R )% [W7%R)E$Lk3w{ E+7pV-#'%9 M@ A8!<0/9K PX@%ZM =M=>K&PX@&bM =M=>@*b=M =M=>YY@KJ@>75'%  +2#".546&54632'"'7>32'64&#"#"'54&'&7672`^?< B[+D+!5 P Vg 4Ve Z 0V b EV%)X "5L;9\R )% [W7%R)E$Lk3w{ E+7pV-#'%9K@?6<.-9K&PX@&bM =M=>@*b =M=M=>Y@IH><53%#  +2'>54#"'46%2#"7567>32'64&#"#"'54&'&76727Cnb ;F^0'$C SVg 4Ve Z 0V b Z;B # X)+N* - >)A [W7%R)E$Lk3w{ E+7pV-#'%9L@ @7 @-b`M  =M=>Y@JI?=64&$  +2#".546%2"7567>32'64&#"#"'54&'&7672`_?<B\+D'"C SVg 4Ve Z 0V b EV%)X "5L;9\- =)J [W7%R)E$Lk3w{ E+7pV-#'%95 Mq@nA8!<;:0/9U UM  =M =>KJ@>75'%   +2'>54#"546727#"&#"'6327>32'64&#"#"'54&'&7672u7Dob ;G^/;%-%+%;#-+n;Vg 4Ve Z 0V b HZ@5bU UM  =M =  >Y@ LKA?86(&!!  +27#"&#"'6322#".5467>32'64&#"#"'54&'&7672;%-%-';#-+n;\`^@;B\+DVg 4Ve Z 0V b `^^ `FV%)X #6K<9] [W7%R)E$Lk3w{ E+7pV-#'%fX @, U   U  SK >Y@&VURNKJGFCB?;8721.*'&#"  +2'>54#"546&#"&47>5!&#"&47>54&'&47327!4&'&47327'7Dob ;F^/?s TRs?C@sUV s??s los@@rTb s?ZK(PX@6  b U  S  K = K >@4  b U    U  S K >YY@(WVSOLKHGDC@<9832/+('$#   +2#".546&#"&47>5!&#"&47>54&'&47327!4&'&47327!`^@;B\+D?s TRs?C@sUV s??s los@@rTb s?FV%)X #6K<9]-{; 11 <{i{; 11 <{5{;2  1 ;{H{;2  1 ;{g6 K&PX@7  b US  K = K  >K(PX@;d  b US  K = K  >@9d  b U    US K  >YYY@*eda]ZYVURQNJGFA@=96521.-*&#"  +2'>54#"546&54632'"'&#"&47>5!&#"&47>54&'&47327!4&'&47327#7Dob ;F^/+!5 P N?sTR s?D?s UVs??slo s?@r Tbs?Z;B # X)+N*Q )% {; 11 <{i{; 11 <{5{;2  1 ;{H{;2  1 ;{ h@   KPX@:  bSM= K = K  >K&PX@8  b US K = K  >K(PX@<d  b US K = K  >@:d  b U   US K  >YYYY@,feb^[ZWVSROKHGBA>:7632/.+'$#  +2#".546&'4632'"'&#"&47>5!&#"&47>54&'&47327!4&'&47327`^@;B\+D+!5 P T?sTR s?D@r TVs?@rlo s??s Tbs?EV%)X #5L;9\Q )% {; 11 <{i{; 11 <{5{;2  1 ;{H{;2  1 ;{f? K&PX@8  b US  K = K  >K(PX@<d  b US  K = K  >@:d  b U    US K  >YYY@.dc`\YXUTQPMIFE@?<85410-,)%"!  +2'>54#"546%2#"=6&#"&47>5!&#"&47>54&'&47327!4&'&473277Dnc ;F^/'%D ?s TRs@C@r TV s??s lo s?@sUb s?Z;B # X)+N* - >)/{; 11 <{i{; 11 <{5{;2  1 ;{H{;2  1 ;{g  KPX@;  bSM= K = K  >K&PX@9  b US K = K  >K(PX@?  b ` US K = K  >@=  b ` U   US K  >YYYY@0eda]ZYVURQNJGFA@=96521.-*&#"  +2#".546%2#"756&#"&47>5!&#"&47>54&'&47327!4&'&47327`_?<B\+C'# C @r  TRs?D?sTV s@@s mos??sTb  s?FV%)X #5L;9]- >)-{; 11 <{i{; 11 <{5{;2  1 ;{H{;2  1 ;{y hl@<;:KPX@CU  SM=M = K = K  >KPX@AUU  SM = K = K  >K(PX@?U UU  S K = K  >@=U U  UU  S K  >YYY@2feb^[ZWVSROKHGBA>:7632/.+'$#   +2'>54#"546727#"&#"'672&#"&47>5!&#".7>54&'.7327!4&'&473277Dnc ;F^/;&-%-$;$-+o;@s URs?D?sTV  s?@r  los??sTb s@Z;B # Y)+M+a^^ a{; 11 <{i{; 11 <{5{;2  1 ;{H{;2  1 ;{!i@ < ;:K PX@JZU  SM=M =K = K  >K PX@HZUU  SM =K = K  >KPX@KbU  SM=M =K = K  >KPX@IbUU  SM =K = K  >KPX@KbU  SM=M =K = K  >KPX@IbUU  SM =K = K  >K(PX@GbUUU  SK = K  >@EbUUUU  S K  >YYYYYYY@4gfc_\[XWTSPLIHCB?;87430/,(%$!!  +27#"&#"'6722#".546&#".7>5!&#"&47>54&'&47327!4&'.7327-;%-%+%;#-+n;\`^?;B[+D\?s TR s?C@sUV s??s los@@r Tb s?ha^^ aEV%)X #5L;9\{; 11 <{i{; 11 <{5{;2  1 ;{H{;2  1 ;{1&g@!<;K1PX@M==N>@U=N>Y@%#  +2'>54#"5464'7327327#"57Dob ;F^/'%?=*/DE#%y=Z;B # Y)+M+MB qXBd1'|@ " <;K PX@"ZM==N>@#bM==N>Y@&$  +2#".5464'7327327#"5`_?<B\+D+%?=*/DE#%y=FV$)X #6L;9\MB qXBd15@'#0.<";K&PX@$bM==N>@(b=M==N>Y@42-+&$  +2'>54#"546&'4632'"'4'7327327#"5\7Dob ;F^/+!5 P %?=*/DE#%y=Z;B # X)+N*R )% hMB qXBd1 6@ ($1/<#;K PX@$ZM==N>K&PX@%bM==N>@)b=M==N>YY@53.,'%  +2#".546&54632'"'4'7327327#"5f`_?<B\+C+!5 O %?=*/DE#%y=EV%)X #5L;9\R )% lMB qXBd14@&"/-@)b=M==N>Y@31,*%#  +2'>54#"546%2#"=64'7327327#"5X7Dob ;F^/'%D %?=*/DE#%y=Z;B # X)+N* - >)MB qXBd15@ '#0.<";K PX@+Z`M ==N>@,b`M ==N>Y@42-+&$  +2#".546%2"7564'7327327#"5h`_?<B\+C'# C %?=*/DE#%y=EV%)X #5L;9\ . >)MB qXBd13 6m@j($1/<#;:U UM  ==N>53.,'%   +2'>54#"546727#"&#"'6324'7327327#"57Dob ;F^/;%-%+';#-+o;%?=*/DE#%y=FZ@4bU UM  == N  >Y@64/-(&!!  +27#"&#"'6322#".5464'7327327#"5;%.%-';#-+o;\`^?< B[+D%%?=*/DE#%y=`^^ `FV%)X #6K<9]EMB qXBd2j@UUK >Y@0/,(%$  +2'>54#"546&#"&47>54&'&473277Dob ;F^/O@sQR s??s GVs@ZK(PX@(b UK =K >@&b UUK >YY@10-)&%   +2#".546&#"&47>54&'&47327`^@;B\+D;?sPR s@@s HVs?FV%)X #6K<9]-{; 11 <{5{;2  1 ;{RAK(PX@)b U K =K >@+db U UK >YY@?>;743.-*&#"  +2'>54#"546&54632'"'&#"&47>54&'&473277Dob ;F^/+!5 P ?sPR s??s GVs?[;B # X)+N+R )% {; 11 <{5{;2  1 ;{ B KPX@,bM =  K =K >K(PX@*b U  K =K >@,db U  UK >YYY@@?<854/.+'$#  +2#".546&54632'"'&#"&47>54&'&47327`^?;B[+D+!5 P @r PR s??s GV s?FV%)X #5L;9]R )% {; 11 <{5{;2  1 ;{-@K(PX@*b  U K =K >@, db U UK >YY@>=:632-,)%"!  +2'>54#"546%2#"=6&#"&47>54&'&473277Dob ;F^/'%D H?sPR s@@s HVs?[;B # X)+N+ - >)){; 11 <{5{;2  1 ;{-A5 KPX@-bM  =  K =K >K&PX@+b   U  K =K >K(PX@1b`   U  K =K >@/b`   U  UK >YYYY@ ?>;743.-*&#"  +2#".546%2#"756&#"&47>54&'&47327`_?<B\+C'"C 3?s PRs@@sHV s?FV%)X #5L<9]- >)+{; 11 <{5{;2  1 ;{ B@ <;:KPX@3U UM =  K =K >K(PX@1U  U U  K =K >@/U  U  U UK >YY@"@?<854/.+'$#   +2'>54#"546727"&#"'632&#"&47>54&'&473277Dnc ;F^/;%-%-';$-+o;K@sQR s??s GVs@Z;B # X)+N*`__ `{; 11 <{5{;2  1 ;{)!CB@  < ;:K PX@:ZUUM  =  K = K >KPX@;bUUM  =  K = K >K(PX@9bU  UU  K = K >@7bU  U   UU K >YYY@$A@=9650/,(%$!!  +27#"&#"'6322#".546&#"&47>54&'&47327);%-%+%;#-+n;\`^@;B\+D?s PRs??sGV s?ja^^ aEV%)X #5L;9\{; 11 <{5{;2  1 ;{T,l@UM=M>Y@! (& ,!,  +2'>54#"54647632'""32654&7Dnc ;F^/qzǓPu=sLZZ;B # Y)+M+#˅sւs EBTT -~ @)bM=M=M>Y@"!)'!-"-  +2#".54647632'""32654&`^@;B\+DqzǓPu=sLZFV$)X #6L;9\!˅sւs EBTT.;K-PX@.b=M= M=M>@,bU= M=M>YY@0/75/;0;-+%#  +2'>54#"546&54632#"'47632'""32654&;7Dnc ;F^/+ 5 P qzǓPu=sLZZK(PX@+bM = M=M>K-PX@/b=M = M=M>@-b U= M=M>YYY@10860<1<.,&$  +2#".546&54632#"'47632'""32654&J`^@;B\+D+ 5 P qzǓPu=sLZFV%)X #6K<9]R (% ˅sւs EBTT-:K(PX@)b U M=M>@-bU = M=M>YY@/.64.:/:,*$"  +2'>54#"'46%2#"=647632'""32654&=7Dnc ;F^/'$D qzǓPu=sLZyZK&PX@,bM  = M=N>K(PX@2b`M  = M=N>@0b`  U M=N>YYY@ 0/75/;0;-+%#  +2#".546%2#"=647632'""32654&J`^@;B\+D'# D qzǓPu=sLZ}FV%)X #6K<9]- =)˅sւs EBT*?@<<UM =M>)'#!  +2'>54#"546"32# ! #7Dob ;F^/ƴVS!wZ@'bUM =M>Y@*($"  +2#".546"32# ! `_?<B\+C1Ǵ~VT!vFV%)X #6K<9]: +T{h'3+9K&PX@(bU M =M>@,dbU M =M>YY@! 8620'% +!+  +2'>54#"546&54632'"'%"32# ! 7Dnc ;F^/+ 5 P ƴ~VT!vZ;B # X)+N*Q )% : +T{h'3 ,: KPX@+bM = M =M>K&PX@)b U M =M>@-db U M =M>YYY@"!9731(&!,",  +2#".546&54632'"'%"32# ! `^?< B[+D+!5 P ǴVT wEV%)X #5L;9\Q )% : +T{h'3*8K(PX@)b U M =M>@- dbU M =M>YY@ 751/&$* *  +2'>54#"546%2#"756"32# ! 7Dob ;F^/'%C ШƴVT!w[;B # X)+N+ - >): +T{h'3+9 KPX@,bM  = M =M>K&PX@*b  U M =M>@0b`  U M =M>YYY@ ! 8620'% +!+  +2#".546%2#"756"32# ! `_?<B\+C'"C ƴVS!wFV%)X #5L;9]- >): +T{h'338c@ 7@U=M>Y@65)'#!  +2'>54#"5463254.'&54632#".54&'&76727Dob ;F^/mHj/ F+`pbB/V [Z;B # Y)+M+ F`8'<$Ӹ=wZ V-#"39u@ 8@$bM==M>Y@76*($"  +2#".5463254.'&54632#".54&'&7672`^?< B[+DqHj/ F+`pbB/V [FV$)X #6L;9\ F`8'<$Ӹ=wZ V-#"3G@ FK-PX@)b=M==N>@'bU==N>YY@ED8620%#  +2'>54#"546&54632#"'3254.'&54632#".54&'&7672F7Dob ;F^/+!5 P Hj/ F+`pbB/V [ZK(PX@&bM ==N>K-PX@*b=M ==N>@(b U==N>YYY@FE9731&$  +2#".546&54632#"'3254.'&54632#".54&'&7672X`^?;B[+D+!5 P Hj/ F+`pbB/V [FV%)X #6K<9]R (% y F`8'<$Ӹ=wZ V-#"3F@ EK(PX@$b U=N>@(bU ==N>YY@DC751/$"  +2'>54#"546%2#"=63254.'&54632#".54&'&7672R7Dob ;F^/'%D Hj/ F+`pbB/V [yZK&PX@'bM  ==N>K(PX@-b`M  ==N>@+b`  U=N>YYY@ED8620%#  +2#".546%2#"=63254.'&54632#".54&'&7672^`^?< B[+D'" D Hj/ F+`pbB/V [}FV%)X #6K<9]- =)  F`8'<$Ӹ=wZ V-#"37 Hj@gG<;:U UM  = =M>FE9731&$   +2'>54#"546727#"&#"'6323254.'&54632#".54&'&76727Dob ;F^/;&-%-';#-+o;Hj/ F+`pbB/V [JZ@5bU UM  = = M  >Y@ GF:842'%!!  +27#"&#"'6322#".5463254.'&54632#".54&'&7672s;%-%-';#-+n;\`^@;B\+D]Hj/ F+`pbB/V [`^^ `FV%)X #5L<9]J F`8'<$Ӹ=wZ V-#"N K(PX@*b U K  =K >@(b U  UK >YY@DA>=430-*)   +2#".546&#"&47>54&'.'.72776&'&4727!`^@;B\+DEPV E2IE  { U' %H os JP) FV%)X #6K<9]-{; 11 <{78/U0 0  1 *B]>*2  1 3JF*?3 ]@  KPX@.bM =  K  =K >K(PX@,b U  K  =K >@.db U   UK >YYY@ SPMLCB?<98/.+'$#  +2#".546&'4632'"'&#"&47>54&'.'&472776&'&4727`^@;B\+D+!5 P nF PVF2IE {U& $HorJO) FV%)X #5L;9]R )% {; 11 <{78/U0 0  1 *B]>*2  1 3JF*?3\C KPX@/bM =  K  =K >K&PX@-b U  K  =K >K(PX@3b` U  K  =K >@1b` U   UK >YYYY@$ROLKBA>;87.-*&#"  +2#".546%2#"756&#"&47>54&'.'&472776&'&4727`^?<B\+C'#C F PVF2IE {U& $HorJO) FV%)X #5L;9]- >)-{; 11 <{78/U0 0  1 *B]>*2  1 3JF*?3w!^k@  < ;:K PX@>ZUM=M =  K = K >K PX@<ZUUM =  K = K >KPX@?bUM=M =  K = K >KPX@=bUUM =  K = K >KPX@?bUM=M =  K = K >KPX@=bUUM =  K = K >K(PX@;bU UU  K = K >@9bU U   UU K >YYYYYYY@(TQNMDC@=:90/,(%$!!  +27#"&#"'6722#".546&#".7>54&'.'.72776&'&47271;%-%-%;#-+o;\`^?< B[+DFPV  E2HE { U' %H os JP) ha^^ aEV%)X #5L;9\{; 11 <{78/U0 0  1 *B]>*2  1 3JF*?3F=D@A2(<bM=M>53.,&$  +2'>54#"546#"'#"&5%3267&546323265'677Dnc ;F^/oѴ@)pVvut7S1'%'1\{q^Z@ 3)@'b`M=M>Y@64/-'%  +2#".546#"'#"&5%3267&546323265'67`^@;B\+DjѴ@)pVvut7S1'%'1\{q^FV$)X #6L;9\qa\ #J_pNj5S#)uyՇ9)LFL@ A7%K-PX@,b`=M =N>@*b` U=N>YY@DB=;53)'$"  +2'>54#"'46&54632#"'#"'#"&5%3267&546323265'677Dnc ;F^/+!5 P ZѴ@)pVvut7S1'%'1\{q^ZK(PX@)b`M = N>K-PX@-b`=M = N>@+b` U= N>YYY@EC><64*(%#  +2#".546&54632#"'#"'#"&5%3267&546323265'67`_?<B\+D+!5 O oѴ@)pVvut7S1'%'1\{q^FV%)X #6K<9]R (% jqa\ #J_pNj5S#)uyՇ9)LFK@@6$K(PX@'b`  UN>@+b` U =N>YY@CA<:42(&#!  +2'>54#"'46%2#"756#"'#"&5%3267&546323265'677Cnb ;F^0'$C Ѵ@)pVvut7S1'%'1\{q^yZK&PX@*b`M  = N>K(PX@0b``M  = N>@.b``  U N>YYY@DB=;53)'$"  +2#".546%2#"=6#"'#"&5%3267&546323265'67#`^@;B\+D'# D Ѵ@)pVvut7S1'%'1\{q^}FV%)X #6K<9]- =)qa\ #J_pNj5S#)uyՇ9)LF+ Mr@o B8& <;:  bU UM  = M>EC><64*(%#   +2'>54#"546727#"&#"'632#"'#"&5%3267&546323265'677Dnc ;F^/;&-%+$;$-+o;Ѵ@)pVvut7S1'%'1\{q^=Z;B # X)+N*`^^ `qa\ #J_pNj5S#)uyՇ9)LF+!N@ C9' < ;:K PX@7 Z  `U UM  =  M>@8  b  `U UM  =  M>Y@"FD?=75+)&$!!  +27#"&#"'6322#".546#"'#"&5%3267&546323265'67D;%-%-';$-+o;\`^@;B\+DrѴ@)pVvut7S1'%'1\{q^`^^ `FV%)X #5L<9]=qa\ #J_pNj5S#)uyՇ9)LKE@B60$<UM =M >GE>:42+)"   +2'>54#"5463:>76!"5>54#"#!&5632;'&54$3 f7Dnc ;F^/ 1 "  V#{#L5R !#FZ@(b UM =M >Y@HF?;53,*#!  +2#".5463:>76!"5>54#"#!&5632;'&54$3 {`^@;B\+D/ 2 #  V#z#L5R !#EFV%)X #6K<9]#c  y{RӨbu Hc;Z@E?3- K&PX@)b U M =M >@-db U M =M >YY@VTMICA:81/%!  +2'>54#"546&54632'"'3:>76!"5>54#"#!&5632;'&54$3 7Dnc ;F^/+ 5 P  2 #  V#z#L5R  !#EZ;B # X)+N*Q )% c  y{RӨbu Hc; [@ F@4.!KPX@,bM = M = M >K&PX@*b U M = M >@.db U M = M >YYY@WUNJDB;920&"  +2#".546&54632'"'3:>76!"5>54#"#!&5632;'&54$3 `^?;B[+D+!5 P  2 # V#{"K6Q !#EEV%)X #5L;9\Q )% c  y{RӨbu Hc;Y@D>2,K(PX@*b  U M =M >@. db U M =M >YY@USLHB@970.$   +2'>54#"546%2#"7563:>76!"5>54#"#!&5632;'&54$3 7Dob ;F^/'%C  2 # V#{"K6Q  #E[;B # X)+N+ - >)!c  y{RӨbu Hc;ZK&PX@ E?3- <@ E?3- KPX@-bM  = M = M >K&PX@+b  U M = M >@1b`  U M = M >YYY@ VTMICA:81/%!  +2#".546%2#"7563:>76!"5>54#"#!&'632;'&54$3 `^@;B\+D'#C  1 # V#{#L6R  #EFV%)X #5L;9]- >)%c  y{RӨbu Hc;{ [*@ F@4.!<;:K PX@7 UM=M  = M = M  >K PX@5U UM  = M = M  >KPX@7 UM=M  = M = M  >KPX@5U UM  = M = M  >KPX@7 UM=M  = M = M  >KPX@5U UM  = M = M  >K(PX@0U  I U M = M  >@1U U U M = M  >YYYYYYY@"WUNJDB;920&"   +2'>54#"546727#"&#"'6723:>76!"5>54#"#!&5632;'&54$3 7Dob ;G^/;%-%-';#-+n; 1 "  V#{#L5R !#FZ;B " Y)+N*a^^ ac  y{RӨbu Hc;!\ @  GA5/"< ;:K PX@:ZUUM  = M = M  >KPX@;bUUM  = M = M  >@9bU  UU M = M  >YY@$XVOKEC<:31'#!!  +27#"&#"'6322#".5463:>76!"5>54#"#!&5632;'&54$3 B;%-%-';$-+o;\`^@;B\+D 2 #  V#{"K6Q !#Eja^^ aEV%)X #5L;9\ c  y{RӨbu Hc;Fym<I@ =# K1PX@:dd  b = M=N= M>@>dd  b == M=N= M>YY@HFB@<;!&$#(*! +6720#"/&5432672#"/#"&54>3276723263'.#"32{   )J'1!#TDq0NiTLnaFH 'bVjfmrb   &Bw-`RPƇm3 N+өBFyu6C@ 7 b  b = = M=N= M>K1PX@; dd  b = M=N= M>@? dd  b == M=N= M>YY@B@<:654210)'!  +2#"747632672#"/#"&54>3276723263'.#"32B!5 j 1)J'1!#TDq0NiTLnaFH 'bVjfmru+ %#w-`RPƇm3 N+өBZmD,K(PX@ %$  <@ %$  K(PX@1ddb   UM=M>K1PX@6ddb  I  UM=M>@7ddb  U  UM=M>YYY@CA@>""$*$(*! +6720#"/&54327'"&54675&54632#"'&#"37632#"&#"F   !sTVds)/'+^11;=W V`b   &RmX !{jF#=PZ9)#CX+/ `Zq>5K(PX@   <@   K(PX@2 ddb   UM=M>K1PX@7 ddb  I  UM=M>@8 ddb  U  UM=M>YYY@=;:86421/-+)%#  +2#"7476327'"&54675&54632#"'&#"37632#"&#"!5 j sTVds)/'+^11;=W V`q+ %#RmX !{jF#=PZ9)#CX+/ `9w?a@ 3*<"!9K!PX@b=M=>@ddM=>Y'.*(!+632#"/&547>32'64&#"#"'54&'&7672   _Vg 4Ve Z 0V b m   ' [W7%R)E$Lk3w{ E+7pV-#'%9s;j@ /&<9KPX@b=M=>@ddM=>Y@98.,%#  +2#"54767>32'64&#"#"'54&'&7672!5 j Vg 4Ve Z 0V b s+ %#j [W7%R)E$Lk3w{ E+7pV-#'%^1q*b@ %#<;KPX@b==N>@dd=N>Y%%**!+6720#"/&544'7327327#"5   N%?=*/DE#%y=f   &JMB qXBd1w$j@<;K!PX@b==N>@dd=N>Y@#!  +2#"74764'7327327#"5F!5 j m%?=*/DE#%y=w+ %#MB qXBdTd#0f @ ddM=M>Y@%$,*$0%0&)*!+6320#"/&5&47632'""32654&j   qzǓPu=sLZZ   &g˅sւs EBTTu*iKPX@$b=M=M>@!ddM=M>Y@&$**  +2#"547647632'""32654&+!5 k VqzǓPu=sLZu+ %#/˅sւs EBT3`<Z@ ;@dd=N>Y$+)*!+6320#"/&543254.'&54632#".54&'&7672u   Hj/ F+`pbB/V [V   'Y F`8'<$Ӹ=wZ V-#"3u6b5@dd=N>Y@43'%!  +2#"54763254.'&54632#".54&'&7672Z!5 k Hj/ F+`pbB/V [u+ %# F`8'<$Ӹ=wZ V-#"F\Ab@ 6,@dddM>Y@ %&*#(*!+6320#"/&54#"'#"&5%3267&546323265'67   )Ѵ@)pVvut7S1'%'1\{q^R   'Nqa\ #J_pNj5S#)uyՇ9)LFy;j0&@dddN>Y@31,*$"  +2#"5476#"'#"&5%3267&546323265'67!5 k +Ѵ@)pVvut7S1'%'1\{q^y+ %# qa\ #J_pNj5S#)uyՇ9)LF=y HU@I/! ) K1PX@B  bQM=  = M =N= M>@F  bQM=  = = M=N= M>YY@ TRNLHGFDCB;931-+('%#  %$ +73267#"&52'>54#"54632672#"/#"&54>3276723263'.#"32l-B, 3i3PbZ7Dob ;F^/})J'1!#TDq0NiTLnaFH 'bVjfmrsk>+^LTT[;B # X)+N+w-`RPƇm3 N+өBF=y!IV@J0" * K$PX@M  b  bM=  = M  =N=M=M>K1PX@J  b  bQM=  = M  =N=M>@N  b  bQM=  = = M =N=M>YYY@"USOMIHGEDC<:42.,)(&$!!%$ +73267#"&52#".54632672#"/#"&54>3276723263'.#"32l-B, 3i3Pb=`^?;B[+D)J'1!#TDq0NiTLnaFH 'bVjfmrsk>+^LTTFV%)X #5L;9]w-`RPƇm3 N+өBF=y /Wd@X>08 K(PX@K  bbQM=  = M  =N =M >K1PX@O  bbQ=M=  = M  =N =M >@S  bbQ=M=  = = M =N =M >YYY@$ca][WVUSRQJHB@<:7642.,'%  %$ +73267#"&52'>54#"546&54632'"'32672#"/#"&54>3276723263'.#"32l-B, 3i3Pbo7Dob ;F^/+!5 O )J'1!#TDq0NiTLnaFH 'bVjfmrsk>+^LTT[;B # X)+N+R )% Xw-`RPƇm3 N+өBF=y!0Xe@Y?19  K$PX@O  bbM= = M  = N  = M  =M>K&PX@L  bbQM= = M  = N  = M   >K1PX@P  bbQ=M= = M  = N  = M   >@T  bbQ=M= = = M = N  = M   >YYYY@&db^\XWVTSRKICA=;8753/-(&!!%$ +73267#"&52#".546&'4632'"'32672#"/#"&54>3276723263'.#"32l-B, 3i3PbX`^@;B\+D+!5 P )J'1!#TDq0NiTLnaFH 'bVjfmrsk>+^LTTEV%)X #5L;9\R )% \w-`RPƇm3 N+өBF=y7DUc@aLK 8  K(PX@L  b  bQ M = = M=N= M>K1PX@P  b  bQ= M = = M=N= M>@T  b  bQ= M = == M=N= M>YYY@!WVFE_]VcWcRPEUFUCA=;76!&$#%%$+73267#"&532672#"/#"&54>3276723263'.#"322'>54#"546%2#"=6l-B, 3i3Pb)J'1!#TDq0NiTLnaFH 'bVjfmr7Dob ;F^/'%D sk>+^LTTw-`RPƇm3 N+өB#[;B # X)+N+ - >)F=y!/Wd@-X>08  K$PX@P  bbM= = M  = N  = M  =M>K&PX@M  bbQM= = M  = N  = M   >K1PX@Sb  `bQM= = M  = N  = M   >@Wb  `bQM= = = M = N  = M   >YYYY@*#"ca][WVUSRQJHB@<:7642+)"/#/!!%$ +73267#"&52#".546%2#"=632672#"/#"&54>3276723263'.#"32l-B, 3i3PbJ`^@;B\+D'#D )J'1!#TDq0NiTLnaFH 'bVjfmrsk>+^LTTEV%)X #5L;9\ - >)w-`RPƇm3 N+өBF-y5'4EUe@*IQ <; (_^XWV@Zb U U M ===M=N= M=M>Y@%GF65ca\ZTROMLJFUGUB@5E6E31$!&$#"+32672#"/#"&54>3276723263'.#"322'>54#"'46727#"&#"'63273267#"&5F)J'1!#TDq0NiTLnaFH 'bVjfmr7Dnc ;F^/;%-%-';$-+o;l-B- 3i3Pbw-`RPƇm3 N+өBZK$PX@^  b   bUUM == M = N  = M  =M>K1PX@[  b   bUUQM == M = N  = M   >@_  b   bUUQM === M = N  = M   >YYY@.! ec_]YXWUTSLJDB><9864+*%# 1!1%$ +73267#"&527#"&#"'6322#".54632672#"/#"&54>3276723263'.#"32l-B, 3i3Pb;&-%-';$-+o;\`^?<B\+C)J'1!#TDq0NiTLnaFH 'bVjfmrsk>+^LTT`^^ `FV%)X #6K<9]9w-`RPƇm3 N+өB '^@  @2 US   U K = M>Y@"!\ZPOKG>=62$#!'"'  % +73267"&52'>54#"546!#&#"&47>727&#"&47>&'!"!%#1VX7Dnc ;F^/j 2nLk NZ%-D?63 [u!wP%m;#\^J18 bJVV3ZKPX@<b U   UM = K  = M>@:b US   U K  = M>YY@ #"][QPLH?>73%$"(#(!!% +73267"&52#".546!#&#"&47>727&#"&47>&'!"!%" 1WX`^?< B[+D#j 2nLk NZ%-D?63 [u!wP%m;#\^J18 bJVV5FV%)X #6K<9]w- ^  11 4XJ)l/1  11   [f /6m@   KPX@;b  U  U M =M = K  >K!PX@9b  U  S  UM = K  >K(PX@7b  U  S  UU K  >@Ab  U  S  UUK  = K  >YYYY@"10ki_^ZVMLEA320616.,'%  % +73267"&52'>54#"546&54632#"'!#&#"&47>727&#"&47>&'!" %#1VX7Dob ;F^/+!5 P j 2nLk NZ%-D?63 [u!wP%;#\^J27 bJVV[;B # X)+N+R )% - ^  11 4XJ)l/1  11   [7!07nK1PX@$  <@$   KPX@BbUM= M =  L  = N>K(PX@>b U  SU  L  = N>K1PX@Bdb U  SU  L  = N>@Kdb U  S U L  =  K  = M>YYYY@&21lj`_[WNMFB>=431727/-(&!!% +73267"&52#".546&'4632'"'!#&#"&47>727&#"&47>&'!" %#1VX/`_?<B\+C+!5 P k 1  nLjNZ%-C@54[u !vO%m;#\^J18 bJVV)FV%)X #5L;9]R )% - ^  11 4XJ)l/1  11   [3 .5lK1PX@ ,  <@ ,  KPX@@b U  U M =  L  = N>K(PX@>b U  S  U  L  = N>K1PX@Bdb U  S  U  L  = N>@Kdb U  S  U L  =  K  = M>YYYY@(0/"!jh^]YULKD@<;21/505*(!.".  % +73267"&52'>54#"546%2#"=6!#&#".7>727&#"&47>&'!"!%#1VXT7Dob ;F^/'%D j 1 nLj NZ$-D@53Zu !wP% m;#\^J18 bJVV+[;B # X)+N+ - >)u- ^  11 4XJ)l/1  11   ['!/6mGK&PX@ - <K1PX@ - <@ -  KPX@CbUM= M =  L  = N>KPX@Ab UU M =  L  = N>K&PX@?b U  SU  L  = N>K1PX@Eb` U  SU  L  = N>@Nb` U  S U L  =  K  = M>YYYYY@*10#"ki_^ZVMLEA=<320616+)"/#/!!% +73267"&52#".546%2#"=6!#&#".7>727&#"&47>&'!"!%#1WX`^@;B\+D'# D j 1 nLj NZ$-D@53Zu !wP% m;#\^J18 bJVV+FV%)X #5L;9]- >)y- ^  11 4XJ)l/1  11   [B 07n@ $,  <+;#:K PX@IUUM=M = M =  K  = M>K PX@GUUUM = M =  K  = M>KPX@IUUM=M = M =  K  = M>KPX@GUUUM = M =  K  = M>KPX@IUUM=M = M =  K  = M>KPX@GUUUM = M =  K  = M>KPX@EU  SUUM =  K  = M>@CUU  SUU  K  = M>YYYYYYY@*21"!lj`_[WNMFB431727/-*('%!0"0  % +73267"&52'>54#"'46727#"&#"'672!#&#"&47>727&#"&47>&'!"!%"2WX7Dnc ;F^/;%-%-';#.+o;Pj 2 nLkNZ%-D?63[u !wP%m;#\^J18 bJVVCZ;B " Y)+N*a^^ a- ^  11 4XJ)l/1  11   [R18o@" )  <;:K PX@P ZU  UM=M = M = K  =M>KPX@Q  bU  UM=M = M = K  =M>KPX@O  bUU  UM = M = K  =M>KPX@M  bU  SU  UM = K  =M>@K  bUU  SU  U K  =M>YYYY@,32! mka`\XONGC542838+*%# 1!1% +73267"&527#"&#"'6722#".546!#&#"&47>727&#"&47>&'!"!%#1WXN;%-%-';#-+n;\`^@;B\+Dj 2nLk NZ%-D?63 [u!wP%m;#\^J18 bJVVa^^ aEV%)X #5L;9\c- ^  11 4XJ)l/1  11   [9 MZ@WA8! 0KJ@>75'%  %$ +73267#"&52'>54#"5467>32'64&#"#"'54&'&7672l-B- 3i3Pb7Dob ;F^/Vg 4Ve Z 0V b k>+^LTTZ;B # X)+N*J [W7%R)E$Lk3w{ E+7pV-#'%9!N@B9" 1<09K PX@-ZM =M==N>@.bM =M==N>Y@LKA?86(&!!%$ +73267#"&52#".5467>32'64&#"#"'54&'&7672l-B- 3i3Pb`^@;B\+DVg 4Ve Z 0V b k>+^LTTFV%)X #5L;9]E [W7%R)E$Lk3w{ E+7pV-#'%9 /\@PG0 ?<>9K&PX@/bM =M ==N>@3b=M =M ==N>Y@ZYOMFD64.,'%  %$ +73267#"&52'>54#"546&54632'"'7>32'64&#"#"'54&'&7672l-B- 3i3Pb7Dob ;F^/+!5 P Vg 4Ve Z 0V b k>+^LTTZ;B # X)+N*R )% [W7%R)E$Lk3w{ E+7pV-#'%9!0]@QH1   @K&PX@0bM =M = =N>@4b=M =M = =N>YY@[ZPNGE75/-(&!!%$ +73267#"&52#".546&54632'"'7>32'64&#"#"'54&'&7672l-B- 3i3Pb`^?< B[+D+!5 P Vg 4Ve Z 0V b k>+^LTTEV%)X "5L;9\R )% [W7%R)E$Lk3w{ E+7pV-#'%9 .[@,OF/ ><=9K&PX@0bM  =M ==N>@4b =M =M ==N>Y@"!YXNLEC53*(!.".  %$ +73267#"&52'>54#"'46%2#"7567>32'64&#"#"'54&'&7672l-B- 3i3Pb#7Cnb ;F^0'$C SVg 4Ve Z 0V b k>+^LTTZ;B # X)+N* - >)A [W7%R)E$Lk3w{ E+7pV-#'%9!/\@-PG0   ?<>9K PX@6Z`M  =M = =N>@7b`M  =M = =N>Y@#"ZYOMFD64+)"/#/!!%$ +73267#"&52#".546%2"7567>32'64&#"#"'54&'&7672l-B- 3i3Pb`_?<B\+D'"C SVg 4Ve Z 0V b k>+^LTTEV%)X "5L;9\- =)J [W7%R)E$Lk3w{ E+7pV-#'%95 M]@A8!WVPON 0 <;:/ 9U UM  =M == N  >[YTRKJ@>75'%   +2'>54#"546727#"&#"'6327>32'64&#"#"'54&'&767273267#"&5u7Dob ;G^/;%-%+%;#-+n;Vg 4Ve Z 0V b l-B- 3i3PbHZ+^LTT931^@-)RI2  A<;:@9K PX@> ZU UM = M  = =N>@?  bU UM = M  = =N>Y@"! \[QOHF86+*%# 1!1%$ +73267#"&527#"&#"'6322#".5467>32'64&#"#"'54&'&7672l-B- 3i3Pb-;%-%-';#-+n;\`^@;B\+DVg 4Ve Z 0V b k>+^LTT`^^ `FV%)X #6K<9] [W7%R)E$Lk3w{ E+7pV-#'% h@  @; U    US K  = M>Y@(feb^[ZWVSROKHGBA>:7632/.+'$#  % +73267"&52'>54#"546&#"&47>5!&#"&47>54&'&47327!4&'&47327 %#1VY7Dob ;F^/?s TRs?C@sUV s??s los@@rTb s?m;#\^J18 bJVV3ZK(PX@E  b US K = K  = M>@C  b U   US K  = M>YY@*gfc_\[XWTSPLIHCB?;87430/,(%$!!% +73267"&52#".546&#"&47>5!&#"&47>54&'&47327!4&'&47327!%"2WX`^@;B\+D?s TRs?C@sUV s??s los@@rTb s?m;#\^J18 bJVV5FV%)X #6K<9]-{; 11 <{i{; 11 <{5{;2  1 ;{H{;2  1 ;{ 1 /w@    K&PX@F  bU  S K = K  = M>K(PX@Jd  bU  S K = K  = M>@Hd  bU  U  S K  = M>YYY@,utqmjifeba^ZWVQPMIFEBA>=:632.,'%  % +73267"&52'>54#"546&54632'"'&#"&47>5!&#"&47>54&'&47327!4&'&47327!%#1VXF7Dob ;F^/+!5 P N?sTR s?D?s UVs??slo s?@r Tbs?m;#\^J18 bJVV&Z;B # X)+N*Q )% {; 11 <{i{; 11 <{5{;2  1 ;{H{;2  1 ;{ !0x@$   KPX@Ib  SM=K = K  = M>K&PX@GbU  SK = K  = M>K(PX@KdbU  SK = K  = M>@IdbUU  S K  = M>YYYY@.vurnkjgfcb_[XWRQNJGFCB?>;743/-(&!!% +73267"&52#".546&'4632'"'&#"&47>5!&#"&47>54&'&47327!4&'&47327!%" 1WXL`^@;B\+D+!5 P T?sTR s?D@r TVs?@rlo s??s Tbs?m;#\^J18 bJVV&EV%)X #5L;9\Q )% {; 11 <{i{; 11 <{5{;2  1 ;{H{;2  1 ;{ ; .v@,    K&PX@G  bU  S K = K  = M>K(PX@Kd  bU  S K = K  = M>@Id  bU  U  S K  = M>YYY@0"!tspliheda`]YVUPOLHEDA@=<9521*(!.".  % +73267"&52'>54#"546%2#"=6&#"&47>5!&#"&47>54&'&47327!4&'&47327 %#1VX)7Dnc ;F^/'%D ?s TRs@C@r TV s??s lo s?@sUb s?m;#\^J18 bJVV&Z;B # X)+N* - >)/{; 11 <{i{; 11 <{5{;2  1 ;{H{;2  1 ;{ !!/w@-   KPX@Jb  SM=K = K  = M>K&PX@HbU  SK = K  = M>K(PX@Nb`U  SK = K  = M>@Lb`UU  S K  = M>YYYY@2#"utqmjifeba^ZWVQPMIFEBA>=:632+)"/#/!!% +73267"&52#".546%2#"756&#"&47>5!&#"&47>54&'&47327!4&'&47327!%" 1WXE`_?<B\+C'# C @r  TRs?D?sTV s@@s mos??sTb  s?m;#\^J18 bJVV-FV%)X #5L;9]- >)-{; 11 <{i{; 11 <{5{;2  1 ;{H{;2  1 ;{  0x@#$,   <+;#:KPX@RU  SM=M =K =  K  = M>KPX@PUU  SM =K =  K  = M>K(PX@NUUU  SK =  K  = M>@LUUUU  S  K  = M>YYY@4"!vurnkjgfcb_[XWRQNJGFCB?>;743/-*('%!0"0  % +73267"&52'>54#"546727#"&#"'672&#"&47>5!&#".7>54&'.7327!4&'&47327 %#1VX7Dnc ;F^/;&-%-$;$-+o;@s URs?D?sTV  s?@r  los??sTb s@m;#\^J18 bJVVAZ;B # Y)+M+a^^ a{; 11 <{i{; 11 <{5{;2  1 ;{H{;2  1 ;{ 1y\@")   <;:K PX@YZU  SM=M =K =  K = M>K PX@WZUU  SM =K =  K = M>KPX@ZbU  SM=M =K =  K = M>KPX@XbUU  SM =K =  K = M>KPX@ZbU  SM=M =K =  K = M>KPX@XbUU  SM =K =  K = M>K(PX@VbUUU  SK =  K = M>@TbUUUU  S  K = M>YYYYYYY@6! wvsolkhgdc`\YXSROKHGDC@?<854+*%# 1!1% +73267"&527#"&#"'6722#".546&#".7>5!&#"&47>54&'&47327!4&'.7327!%" 1WXb;%-%+%;#-+n;\`^?;B[+D\?s TR s?C@sUV s??s los@@r Tb s?m;#\^J18 bJVVa^^ aEV%)X #5L;9\{; 11 <{i{; 11 <{5{;2  1 ;{H{;2  1 ;{F= M@B8& @'bRM =M>Y@EC><64*(%#  %$ +73267#"&52'>54#"546#"'#"&5%3267&546323265'67Nl-B- 3i3Pbl7Dnc ;F^/oѴ@)pVvut7S1'%'1\{q^sk>+^LTTZK$PX@1b`M = M=N>@.b`RM = M>YY@FD?=75+)&$!!%$ +73267#"&52#".546#"'#"&5%3267&546323265'67Nl-B- 3i3Pb}`^@;B\+DjѴ@)pVvut7S1'%'1\{q^sk>+^LTTFV$)X #6L;9\qa\ #J_pNj5S#)uyՇ9)LF= /\@QG5 K(PX@/  b  `RM = N>K-PX@3  b  `R=M = N>@1  b  ` UR= N>YYY@TRMKEC9742.,'%  %$ +73267#"&52'>54#"'46&54632#"'#"'#"&5%3267&546323265'67Nl-B- 3i3Pb:7Dnc ;F^/+!5 P ZѴ@)pVvut7S1'%'1\{q^sk>+^LTTZK$PX@3  b  `M =  N=N>K(PX@0  b  `RM =  N>K-PX@4  b  `R=M =  N>@2  b  ` UR=  N>YYYY@USNLFD:853/-(&!!%$ +73267#"&52#".546&54632#"'#"'#"&5%3267&546323265'67Nl-B- 3i3Pb@`_?<B\+D+!5 O oѴ@)pVvut7S1'%'1\{q^sk>+^LTTFV%)X #6K<9]R (% jqa\ #J_pNj5S#)uyՇ9)LF= .[@,PF4 K(PX@.  b  `  UR N>@2  b  ` UR = N>YY@"!SQLJDB8631*(!.".  %$ +73267#"&52'>54#"'46%2#"756#"'#"&5%3267&546323265'67Nl-B- 3i3Pb77Cnb ;F^0'$C Ѵ@)pVvut7S1'%'1\{q^sk>+^LTTZK$PX@4  b  `M  =  N=N>K&PX@1  b  `RM  =  N>K(PX@7b  `  `RM  =  N>@5b  `  `  UR  N>YYYY@ #"TRMKEC9742+)"/#/!!%$ +73267#"&52#".546%2#"=6#"'#"&5%3267&546323265'67Nl-B- 3i3Pb+`^@;B\+D'# D Ѵ@)pVvut7S1'%'1\{q^sk>+^LTTFV%)X #6K<9]- =)qa\ #J_pNj5S#)uyՇ9)LF=+ 0]@&$, RH6 <+;#:K$PX@;   bUUM  =  M =N>@8   bUURM  =  M >Y@""!USNLFD:853/-*('%!0"0  %$ +73267#"&52'>54#"546727#"&#"'632#"'#"&5%3267&546323265'67Nl-B- 3i3Pbh7Dnc ;F^/;&-%+$;$-+o;Ѵ@)pVvut7S1'%'1\{q^sk>+^LTTXZ;B # X)+N*`^^ `qa\ #J_pNj5S#)uyՇ9)LF=+1^$@%)SI7  <;:K PX@A Z  `UUM =  M  =N>K$PX@B  b  `UUM =  M  =N>@?  b  `UURM =  M   >YY@$! VTOMGE;964+*%# 1!1%$ +73267#"&527#"&#"'6322#".546#"'#"&5%3267&546323265'67Nl-B- 3i3Pb;%-%-';$-+o;\`^@;B\+DrѴ@)pVvut7S1'%'1\{q^sk>+^LTT`^^ `FV%)X #5L<9]=qa\ #J_pNj5S#)uyՇ9)L [V@SF@4.!  < U M =M =M>WUNJDB;920&"  % +73267"&52'>54#"5463:>76!"5>54#"#!&5632;'&54$3 +!%#1VX7Dob ;F^/ 2 # V#{"K6Q  #Em;#\^J18 bJVV3Z@2b U M = M =M>Y@XVOKEC<:31'#!!% +73267"&52#".5463:>76!"5>54#"#!&5632;'&54$3 7!%#1VX`^?;B[+D/ 2 # V#{"K6Q !#Em;#\^J18 bJVV5FV%)X #6K<9]#c  y{RӨbu Hc; /j@UOC=0  K&PX@3b U M = M  =M>@7db U M = M  =M>YY@fd]YSQJHA?51.,'%  % +73267"&52'>54#"546&54632'"'3:>76!"5>54#"#!&5632;'&54$3 V!%#1VX7Dnc ;F^/+ 5 P  2 #  V#z#L5R  !#Eo;#\^J27 bJVV$Z;B # X)+N*Q )% c  y{RӨbu Hc;!0k*@ VPD>1  KPX@6  bM = M = M  =M>K&PX@4  b  U M = M  =M>@8d  b  U M = M  =M>YYY@ge^ZTRKIB@62/-(&!!% +73267"&52#".546&54632'"'3:>76!"5>54#"#!&5632;'&54$3 J!%" 1WX`^?;B[+D+!5 P  2 # V#{"K6Q !#Em;#\^J18 bJVV&EV%)X #5L;9\Q )% c  y{RӨbu Hc; .i@,TNBK(PX@4b  U M = M  =M>@8 db U M = M  =M>YY@ "!ec\XRPIG@>40*(!.".  % +73267"&52'>54#"546%2#"7563:>76!"5>54#"#!&5632;'&54$3 -!%#1VX7Dob ;F^/'%C  2 # V#{"K6Q  #Em;#\^J18 bJVV+[;B # X)+N+ - >)!c  y{RӨbu Hc;!/jRK&PX@- UOC=0  <@- UOC=0  KPX@7  bM = M = M  =M>K&PX@5  b  U M = M  =M>@;  b `  U M = M  =M>YYY@"#"fd]YSQJHA?51+)"/#/!!% +73267"&52#".546%2#"7563:>76!"5>54#"#!&'632;'&54$3 J!%" 1WX`^@;B\+D'#C  1 # V#{#L6R  #Em;#\^J18 bJVV+FV%)X #5L;9]- >)%c  y{RӨbu Hc;{h 0k@"$, VPD>1  <+;#:K PX@AUM= M =  M =  M  =M>K PX@?UU M =  M =  M  =M>KPX@AUM= M =  M =  M  =M>KPX@?UU M =  M =  M  =M>KPX@AUM= M =  M =  M  =M>KPX@?UU M =  M =  M  =M>K(PX@:U IU  M =  M  =M>@;U UU M =  M  =M>YYYYYYY@$"!ge^ZTRKIB@62/-*('%!0"0  % +73267"&52'>54#"546727#"&#"'6723:>76!"5>54#"#!&5632;'&54$3  %#1VYg7Dob ;F^/;%-%-';#-+n; 2 #  V#{#L5R  !#Em;#\^J18 bJVVCZ;B " Y)+N*a^^ ac  y{RӨbu Hc;1l/@$)WQE?2  <;:K PX@D ZUUM = M =  M  =M>KPX@E  bUUM = M =  M  =M>@C  bU UU M =  M  =M>YY@&! hf_[USLJCA73+*%# 1!1% +73267"&527#"&#"'6322#".5463:>76!"5>54#"#!&5632;'&54$3 ; %#1VX;%-%-';#-+n;\`^@;B\+D 2 # V#{"K6Q  #Em;#\^J18 bJVVa^^ aEV%)X #5L;9\ c  y{RӨbu Hc;Fy/ 4A@5  < :K1PX@8  bU = M=N= M>@<  bU == M=N= M>Y@@>:843!&$#%% +267#"&'32672#"/#"&54>3276723263'.#"32{}7amu)J'1!#TDq0NiTLnaFH 'bVjfmr/RfbVpw-`RPƇm3 N+өBFy 3@@ 4  @=  b U == M=N= M>Y@?=97321/.-&$  +!"7463!232672#"/#"&54>3276723263'.#"325])J'1!#TDq0NiTLnaFH 'bVjfmr%%-#/bw-`RPƇm3 N+өBF=ym#KX`@L2$ , K$PX@Ddd  b  = M =N= M=M>K1PX@Add  bQ  = M =N= M>@Edd  bQ  = = M=N= M>YYY@WUQOKJIGFE&$#()$%$+73267#"&5672#"/&5432672#"/#"&54>3276723263'.#"32l-B, 3i3Pb   )J'1!#TDq0NiTLnaFH 'bVjfmrsk>+^LTT}   &Bw-`RPƇm3 N+өBF=y7D@8  K1PX@7  bQ = M=N= M>@;  bQ == M=N= M>YY@CA=;76!&$#%%$ +73267#"&532672#"/#"&54>3276723263'.#"32l-B, 3i3Pb)J'1!#TDq0NiTLnaFH 'bVjfmrsk>+^LTTw-`RPƇm3 N+өBF=yuFSk@G- ' K$PX@Edd  b  = M =N= M=M>K1PX@Bdd  bQ  = M =N= M>@Fdd  bQ  = = M=N= M>YYY@ RPLJFEDBA@971/+)&%#!%$ +73267#"&52#"747632672#"/#"&54>3276723263'.#"32l-B, 3i3Pb!5 j 1)J'1!#TDq0NiTLnaFH 'bVjfmrsk>+^LTT+ %#w-`RPƇm3 N+өBFy'7D!@ 8 < ;:K$PX@C  bUM =  = M =N= M>K1PX@A  bUU  = M =N= M>@E  bUU  = = M=N= M>YY@"CA=;765321*("   +27"&#"'63232672#"/#"&54>3276723263'.#"32h;&-%+$;$-+o;)J'1!#TDq0NiTLnaFH 'bVjfmr`__ `w-`RPƇm3 N+өBF=y'GTC@%H. ( <;:K$PX@Mb UM =  = M  =N =M =M>K1PX@HbU UQ  = M  =N =M >@LbU UQ  = = M =N =M >YY@$SQMKGFECBA:820,*'&$"%$ +73267#"&527"&#"'63232672#"/#"&54>3276723263'.#"32l-B, 3i3Pb;&-%+$;$-+o;)J'1!#TDq0NiTLnaFH 'bVjfmrsk>+^LTT`__ `w-`RPƇm3 N+өBq KKPX@. dU   VM = K >@, dUS   V K >Y@IG=<84+*# "" +#"&'33267!#&#"&47>727&#"&47>&'!"ks9 }TP cj 2nLk NZ%-D?63 [u!wP%qh}k9DC:- ^  11 4XJ)l/1  11   [ IxKPX@( U UM =K >@& US UK >Y@ GE;:62)(!    +!"7463!2!#&#"&47>727&#"&47>&'!"5_j 2nLk NZ%-D?63 [u!wP%%-#/- ^  11 4XJ)l/1  11   [R @0bS UK =K >Y@PNDC?;21*&*! +6320#"/&54!#&#"&47>727&#"&47>&'!"   j 2nLk NZ%-D?63 [u!wP%   '- ^  11 4XJ)l/1  11   [LKPX@3b U K =M =K >@1bS U K =K >Y@JH>=95,+$   +2#"5476!#&#"&47>727&#"&47>&'!"P!5 k j 2nLk NZ%-D?63 [u!wP%+ %#y- ^  11 4XJ)l/1  11   [3DM@  @)S UK =M>Y@KI?>:6-,%!% +73267"&5!#&#"&47>727&#"&47>&'!"!%#1VX<j 2nLk NZ%-D?63 [u!wP%m;#\^J18 bJVV- ^  11 4XJ)l/1  11   [B7@9M >  +2'>54#"5467Dob ;F^/7Z;B # X)+N*\@ :M>% +73267"&5!%#1VXm;#\^J18 bJVVB7@9M >  +2'>54#"5467Dob ;F^/7Z;B # X)+N*d]@ <: 9KPX@QM >@UIMAY@  +27#"&#"'6325;&-%-';#-+o;`^^ `j9!#z@ < ;:KPX@UQM>@$UUIMAY@"!  +27#"&#"'632462"&%462"&;%-%-%;#.+o;9N99N9Z9N97R7`__ `)99)'::')99)'::9w"O@C:# 2<19K!PX@(b=M==N>@%ddM==N>Y@ '.*($%$+73267#"&5632#"/&547>32'64&#"#"'54&'&7672l-B- 3i3Pb#   _Vg 4Ve Z 0V b k>+^LTT   ' [W7%R)E$Lk3w{ E+7pV-#'%9<>@;0' <9M==N>'.'%$+73267#"&57>32'64&#"#"'54&'&7672l-B- 3i3PbVg 4Ve Z 0V b k>+^LTT [W7%R)E$Lk3w{ E+7pV-#'%9sK@?6 .<-9KPX@)b=M==N>@&ddM==N>Y@IH><53%#%$ +73267#"&52#"54767>32'64&#"#"'54&'&7672l-B- 3i3Pb!5 j Vg 4Ve Z 0V b k>+^LTT+ %#j [W7%R)E$Lk3w{ E+7pV-#'%9#<@ 0'< ;:9KPX@$UM =M=>@"UUM=>Y@:9/-&$  +27#"&#"'6327>32'64&#"#"'54&'&7672;%-%-';#-+n;Vg 4Ve Z 0V b `__ ` [W7%R)E$Lk3w{ E+7pV-#'%9#L@)@7  /<;:.9KPX@. UM =M ==N>@,U UM ==N>Y@JI?=64&$%$ +73267#"&527#"&#"'6327>32'64&#"#"'54&'&7672l-B- 3i3Pb;%-%-';#-+n;Vg 4Ve Z 0V b k>+^LTT`__ ` [W7%R)E$Lk3w{ E+7pV-#'%1U@  K.K(PX@D d Z  bZ U N  =M=L >@B d Z  bZ   U UM=L >YY@ROHGFB?>9850+(%#UU(! +672#"/&5426762"'.+;26&#!".7>54&'.73!272'.+"   l}: 11 RT3@I'!3bHs  s?@r  d 5/!#'VjP7   &J1[ bOJj 70%;H9 1 <{5{;2 F{d;HQ@G*@C  d Z  bZ   UUM=L >Y@$NKDCB>;:541,'$!QQ  +2#"547626762"'.+;26&#!"&47>54&'&473!272'.+"{!5 k }:1  1RT3@J' 2cHss??sd 50!#'VjP7+ %#1[ bOJj 70%;H9 1 <{5{;2 F{d;H\  @0 d  b    U  SK >Y@ZYVRONKJGFC?<;CC*!+6320#"/&5&&#"&47>5!&#"&47>54&'&47327!4&'&47327   ?s TRs@C@r TV s??s lo s?@sUb s?   'V{; 11 <{i{; 11 <{5{;2  1 ;{H{;2  1 ;{oVK(PX@3 d  b  S  K =K >@1 d  b    U  SK >Y@&TSPLIHEDA@=9650/,(%$!   +2#"5476&#"&47>5!&#"&47>54&'&47327!4&'&47327X!5 k @r TR s?D?s TVs@@smo s??s Tb s?+ %#+{; 11 <{i{; 11 <{5{;2  1 ;{H{;2  1 ;{J-W@  @2   U  SK =M>Y@UTQMJIFEBA>:76CC%+73267"&5%&#"&47>5!&#"&47>54&'&47327!4&'&47327!%" 1WX?s TRs@C@r TV s??s lo s?@sUb s?m;#\^J18 bJVVb{; 11 <{i{; 11 <{5{;2  1 ;{H{;2  1 ;{HfBJ@e =M >Y@ %$ +&54632'"'2'>54#"546+!5 P q7Dob ;F^/ )% fZ;B " Y)+N*HN; q K1PX@e =M >@eU >YY@    +2"=62'>54#"546'%D 7Dob ;F^/;- =) Z;B " Y)+M+R7f O@L < ;:9QM=M >    +27#"&#"'6322'>54#"546;%-%+%;#-+o;b7Dob ;F^/3`^^ `Z%%'%+267#"&'4'7327327#"5{}7`m%?=*/DE#%y=/RfbV|MB qXBd1 !=@:<;U=N>    +!"7463!24'7327327#"55%?=*/DE#%y='%-#/lMB qXBd1^ '=I@F/+86<*;dbV=N>%%*)# +462"&%462"&632#"/&544'7327327#"59N99N9Z9N:8R7   %?=*/DE#%y=)99)'99')99)'99   '\MB qXBd1F"8T@Q*&31<%; dbV=N>750.)'!   +2#"5476462"&%462"&4'7327327#"5!5 k 9N97R7Z9N99N9%?=*/DE#%y=F+ %#P)9:('::')9:('::$MB qXBd1'%@   < ;:K$PX@#UM ==N>@!UU=N>Y@$"  +27"&#"'6324'7327327#"5s;%-%+';#-+n;%?=*/DE#%y=`__ `̏MB qXBd1!#9@  +'42 < &;:KPX@-UUM == N  >@+U UU= N  >Y@861/*("!  +27#"&#"'632462"&%462"&4'7327327#"5s;%-%+';#-+n;9N99N9Z9N97R7%?=*/DE#%y=`__ `)99)'::')99)':: MB qXBd#Pq /oK(PX@& dU K =K >@$ dU VK >Y@-,)%"! "" +#"&'33267&#"&47>54&'&47327Pks9 }TP @s QRs??sGV s@qh}k9DC:{; 11 <{5{;2  1 ;{B -bK(PX@ UK =K >@UUK >Y@+*'#   +!"7463!2&#"&47>54&'&473275@r PR s??s GV s?%-"0P{; 11 <{5{;2  1 ;{5l@  @"dbUK >Y@ CC)!+632#"/&'4&#"&47>54&'&47327   @r  PRs??sGV  s?   'T{; 11 <{5{;2  1 ;{0lK(PX@%dbK =K >@#dbUK >Y@.-*&#"  +2#"7476&#"&47>54&'&47327u!5 j ?s PRs@@sHV s?+ %#-{; 11 <{5{;2  1 ;{;fB jK&PX@eM >@e =M >YY@  %$ +&54632'"'2#".546+!5 P `_?< B[+D )% fEV%)X "5L;9\9b; ] @bcM >Y@    +2"=62#".546'"D `^?< B[+D;- =)EV%)X "5L;9\f{N!@ <;:K PX@#YUUM >KPX@"eUUM >@(eUUIMAYY@ !!  +2#".546727#"&#"'632m`^@;B\+D;%-%-%;$-+o;hEV%)X #5L;9\`__ `3/ 41@.3< :U=M>$+&%+267#"&'3254.'&54632#".54&'&7672#{}7`mrHj/ F+`pbB/V [/RfbV{ F`8'<$Ӹ=wZ V-#"3 35@22<U=M>10$"   +!"5463!23254.'&54632#".54&'&76725ZHj/ F+`pbB/V ['%-#/k F`8'<$Ӹ=wZ V-#"33"JB@?I<dbV =M>HG$+)(" +462"$462"632#"/&543254.'&54632#".54&'&76729N99N!9N:8R   3Hj/ F+`pbB/V [ZR99R77R99R7   ' F`8'<$Ӹ=wZ V-#"3F"JL@II< dbV =M>HG;953(&!   +2#"5476462"&%462"&3254.'&54632#".54&'&7672!5 k 9N97R7Z9N::N9Hj/ F+`pbB/V [F+ %#P)9:('::')9:('::# F`8'<$Ӹ=wZ V-#"o&1I@F-$<9M=M=M>0.+)#!  +2'>54#"546'64>7632#"'4&#"26'7Dob ;F^/ˬ1++%qmYotZ$s7jo'2@ .%<9K PX@'ZM=M=M>@(bM=M=M>Y@1/,*$"  +2#".546'64>7632#"'4&#"267`_?<B\+DϬ1++%qmYotFV$)X #6L;9\?$.>$s7j3%7@ 6< ;:K!PX@$UM ==M>@"UU=M>Y@54(&"   +27"&#"'6323254.'&54632#".54&'&7672;%-%+';#-+o;Hj/ F+`pbB/V [`__ ` F`8'<$Ӹ=wZ V-#"3Gf@c F < ;:U UM =  = M  >ED8620%#  +27#"&#"'632462"$462"3254.'&54632#".54&'&7672};%-%+%;#-+n;9N99N!9N97RHj/ F+`pbB/V [`^^ `R99R77R99R7w F`8'<$Ӹ=wZ V-#"q JwK(PX@( dU K  =K >@& dU  VK >Y@@=:90/,)&% "" +#"&'33267&#"&47>54&'.'&472776&'&4727yks9 }TP FPV F2HF { U& $H or  JO) qh}k9DC:{; 11 <{78/U0 0  1 *B]>*2  1 3JF*?3 HjK(PX@" UK  =K >@  U UK >Y@>;87.-*'$#  +!"7463!2&#"&47>54&'.'&472776&'&472735FPV F2HF { U& $H or  JO) $.#/b{; 11 <{78/U0 0  1 *B]>*2  1 3JF*?3P@  K(PX@&dbK  =K >@$db UK >YY@ FC3C)! +672#"/&'4&#".7>54&'.'&472776&'&4727   F PV E2HF {U& %HosJP)    &]{; 11 <{78/U0 0  1 *B]>*2  1 3JF*?3}KtK(PX@' dbK  =K >@% db UK >Y@A>;:10-*'&  +2#"5476&#"&47>54&'.'.72776&'&4727b!5 k cEOV E2IE  { U' %H os JP) + %#-{; 11 <{78/U0 0  1 *B]>*2  1 3JF*?3EK-PX@ 4<@ 4K-PX@0b UUM =  K >@5Z` UUM =  K >YY@CB?;8731)#   +2#".54632654&#"4&'&4732632#"'&#"&47>5;`_?<B\+C'%w@r d)'^'1hDžf@N ZR s?FV%)X #5L<9]" {;2 Eqh6HOy; 11 <{f9J(6@3< ;dbINB*!+6720#"/&'4462"&%462"&   -:N99N:[9N97R7?   &x)99)'::')99)'::f9J"6@3dbINB!   +2#"7476462"&%462"&!5 j :N99N:[9N97R7J+ %#P)99)'::')99)'::mjs@ <d[)! +632#"/&54      'F=\#P@E;) K*PX@(b`R =M>@#dddRM>YY@ %&*#()$%$ +73267#"&5632#"/&54#"'#"&5%3267&546323265'67Nl-B- 3i3PbP   )Ѵ@)pVvut7S1'%'1\{q^sk>+^LTTm   'Nqa\ #J_pNj5S#)uyՇ9)LF=<^@1' @dRM>Y@ %&*#%%$+73267#"&5#"'#"&5%3267&546323265'67Nl-B- 3i3PbѴ@)pVvut7S1'%'1\{q^sk>+^LTTqa\ #J_pNj5S#)uyՇ9)LF=yK@@6$ @$ dddQN>Y@CA<:42(&#!%$ +73267#"&52#"5476#"'#"&5%3267&546323265'67sl-B- 3i3Pb!5 k +Ѵ@)pVvut7S1'%'1\{q^sk>+^LTT+ %# qa\ #J_pNj5S#)uyՇ9)LF<Z@W 1'< ;:bU UM>42-+%#  +27"&#"'632#"'#"&5%3267&546323265'67;%-%+%;$-+o; Ѵ@)pVvut7S1'%'1\{q^`__ `qa\ #J_pNj5S#)uyՇ9)LF=L@!A7% <;:K$PX@0  bU  U M=N>@-  bU  UR M>Y@DB=;53)'$"%$ +73267#"&527"&#"'632#"'#"&5%3267&546323265'67Nl-B- 3i3Pb;%-%+%;$-+o; Ѵ@)pVvut7S1'%'1\{q^sk>+^LTT`__ `qa\ #J_pNj5S#)uyՇ9)L .@@= <dbM =M>-+'%  *! +6320#"/&'4"32# !    sǴ~VT!v   ': +T{h'3T(=@:dbM =M>'%!  +2#"5476"32# ! f!5 j mǴVT!v+ %#: +T{h'3O?@< :4("<dbM =M >'F''*G*!+6320#"/&543:>76!"5>54#"#!&5632;'&54$3 -    2 #  V#z#L5R !#E   'Lc  y{RӨbu Hc;fIH@E4."<dbM =M >EC<820)'   +2#"54763:>76!"5>54#"#!&'632;'&54$3 w!5 j  1 # V#{#L6R  #E+ %##c  y{RӨbu Hc;ZDJ=@:5/#  <M =M =M>'F''*D%+73267"&5%3:>76!"5>54#"#!&5632;'&54$3  %#1VY 2 # V#{"K6Q  #Em;#\^J18 bJVVXc  y{RӨbu Hc;PN'.K$PX@ e >@ d[Y@   +2"5476!5 j '+ %#;'d K$PX@eM >@eIMAYY@   +2#".546`_?< B[+D'FV%)X #6K<9]RbD @IMA   +!"5463!2+F!%'7)>RbD @IMA   +!"5463!2+F!%'7)>DL; @IMA   +!"7463!2\ #L+/jL @IMA   +"5463!2##;##0#.HL @IMA   +"7463!2#d"##0#.HL @IMA   +"7463!2#d"##0#.{^@GK?+#3#3PPPP^ /@,SGK? +!5%!5//ZZZZ@:[ +"&5467=JbJkJZ=o\fm%7L+:@9> +2&'>54'&546=JbJjIZ=o\fm%7L+:@9[ +%2&'>54'&546=JbJjIZ=o\fm%7L+:@ 9> +2.546/>ZJgI cJ:+K7%k!f\ob#@:[## +"&5467!"&5467+=JcJjIZ>=JbJjJZ>o\fm%7L+:o\fm%7L+:Z#!@9>## +2&'>54'&'46!2&5>54'&546=JbJjIZ>q=JbJjJZ>o\fm%7L+:o\fm%7L+:Z#@9[## +72&'>54'&'46!2&5>54'&546=JbJjIZ>q=JbJjJZ>o\fm%7L+:o\fm%7L+:e#@ 9>## +2.546!2.546,/=ZJgI cK/>ZJgI cJ:+K7%k!f\o:+K7%k!f\oXJ$9@6 <UM> $$ +2632#"'#6#"&4632&746y%5J-B@/ͅ``#^^~/=?-ɉJ5I1T>V9PyfP9V>T1IXJ@F@C=3*(& < UM =M>@>%#&#%%$&" +#"'632#"'#"&547#"&4632'6#"&4632&74632632J@/ͅ``-BB-ɉJ3'%7I+AD(σ^^~/=?-ɉJ5'%5J/V9Py}P8-+;R/LH3R;V:P qfP9V>T1II1TR@IMA +462"w "+7^uN@M> +6462"u?Z@@Z+Z@@Z?u@M>+$462"$462"?Z@@Z?Z@@Z+Z@@Z??Z@@Z?u@M>+6462"$462"$462"u?Z@@Z@Z@@Z@Z??Z+Z@@Z??Z@@Z??Z@@Z?uN@IMA +462"u?Z@@ZZ??Z@u #/JUk@h4 ;7 6<3 :  U  U  UUM>10 TROMGEA?:80J1J.,(&"   $" +#"&546322654&#"%32654.#"4632#"&267'#"'#"&5463232654#"tRgqHhRl9#Dx1ZNf!"HpqqJftXgR+AFbyZJuXdn)Png8"NiXHqPw}a{Vm^B=d=Jb1;ATfy5:!n'+wV3H;d}u  #/;Gbm|@yLSO NIH%$ ljge_]YWRPHbIbFD@>:831+)$/%/"   $" +#"&546322654&#"#"&546322654&#"%32654.#"4632#"&267'#"'#"&5463232654#" uRfnJhPn9#Dy2tRgqHhRl9#Dx1ZNf!"HpqqJftXgR+AFbyZJuXdn)Png8"NiXHqPw}a{Vm^B=d=J-w}a{Vm^B=d=Jb1;ATfy5:!n'+wV3H;d}D@e >  +2#"7476=!5 j D+ %#D$@!e >  +2#"5476#2#"7476L!5 j !5 j D+ %#+ %#D,/@,e >'%,,  +2#"5476#2#"7476#2#"7476R!5 k !5 j !5 j D+ %#+ %#+ %#qwB@ <e >)! +672#"/&54   7   &^XB&@ <e >)'(!+672#"/&54%672#"/&54      7   &   &qB&:'@$10. <e >)'(')!+672#"/&'4'672#"/&54'672#"/&54        7   &   &   &5D;  "+&'567?y (˯' #ݚ#5P;  "+&'67&'6y '˰' #ݙ#/+L@I$! +(%"*)&<#:'9UUIMA+462"462"$462"462"%7   '@Z@@ZX@Z??Z?Z@@Z\@Z??Z3e2g2u2lZ@@Z?Z??Z@BZ??Z@Z??Z@&%&& +' H5=&@#M =M>+2"'.'.54462"2"'.'.54462"uVA 1)@Z??ZVA 2 )@Z??ZHJL;qA`_) F?RL1Z@@Z?TJL;qA`^) F?RL1Z@@Z?^3D+3?7@44)9#<bM =M>32/." & +#"54767>2#"5&'.5462"67654& 1!Ti5Z =D; zT -.9i?[??[+m9N!#PZL)11/+`7##@*) ZEN1Z@@Z?DA 3Z2^J'5K1PX@ K>@GK?Y@  +!5/ZZy5l@xmMA bW6+ @Adb ` eU I M AY@#}vtpnge_]VTQOHF$'&'#'$!+%&#"&54632.54632>2#"#".'#"&54>%&#"&54632.54632>32#"#".'#"&54>&#"&54632.54632>32#"#".'#"&54>h,GG!h$ !f>y7)O,(( *&-Qy,GF!h %! !f y7)P, )' )&-Qi,GF!h %! !f y7)P+)' )&-Q% M #h)xM#A5-9gm2!/2% M #h)xM#A5-9gm2!/2% M #h) xN#A5-9gm2!.3!!@ d[ +3#VTT^F)1[c>@;b` M = M >cb_^ZY-%'-%& +#"&547632"=47>54&"462"#"&547632"7547>54&"462" 1!3i\%R54V"!=4mp=?[??[ 1!3i\%R54V#!=4mq>?[??[%#!')/dUP9bT-%?(7^Z^ǃ9gGf@iZ@@Z?%#!')/dUP9bT-%?(7^Z^ǃ9gGf@iZ@@Z?^HHPC@@bbM =M =M >PO-%' +2"'.'.54462"#"&547632"7547>54&"462"VB 1)?[??[ 1!3i\%R54V#!=4mq>?[??[HJL;qA`_) F?RL1Z@@Z?%#!')/dUP9bT-%?(7^Z^ǃ9gGf@iZ@@Z?DFNC@@bbM =M =M >NM-%% +2"'.'.54462"#"&547632"=47>54&"462"VA 2 )@Z??Z1!3h\$R63V#!=3lq>?Z@@ZDJL;qA`^) F?RL1Z@@Z?% B')/dUP9bT-%?(7^Z^ǃ9fHf@iZ@@Z@y;*@'<eU>&""+'327632'47'jx5+DhG+]193/阗 !'!\- 4K&PX@2#<K(PX@2 #<@2 #K(PX@)  ZbN  =K>@-  ZbZ UK>YY@ 0/('&%" 43#! +4"3252632#"'2&! '5437>54&/"=7RTTRTX'/?8d`5?21@5`NMNNs͇n+'''%--$'WWD)@& 9eIMA +72.546462"/=ZJfJ cJ0?Z@@Z:+K7%k!f\oZ@@Z@?5 NK1PX@QM>@UIMAY@      +"2>54&"&54632;+:=JmjoP4b`FdÜ7o'e%@)b`UIL@Y@ *2+2"&4&"&7>=4&4.#&76721##1#s-<=>B+% }9#1##19!;   77WN$X@!dUIL@Y@ 2("+35#"76767676323#&"&7>q> 9I"'&s g-<=>B+Lw1: v(F19!bw@ K!PX@"bUQK>@(bSUIMAYY@ "$$"!+4#"'!!632#"&54632726;: -N #?qxh=!5B=%FZ^+!!U\1 ,@)<:UIMA)$%!+4#"3267#"&5467672};9V5=+upm`/?H7V+0 ugNPF'/?#/>'X'Q@deIMAY@  +"'6733!#67+"+%+d\E1-)'H7 ŞOP`= !.S@ " @UIMAY@)'    +">54&#"&5467'&546323254.'FbR)1;Pd|{XgCs{TJr!OP1''LPJ-N)'-TXJ+7^?xNZ3i# 7`DTNg/=6m!'7R+ J@ <9K1PX@QM>@UIMAY$%!+727054&#"46325$7".}=9V5=+sol`1dp 3D^'-)sfNPHBuR'/?1I69= K PX@SK >K PX@GSK?KPX@SK >KPX@GSK?KPX@SK >@GSK?YYYYY+3##5#5353dPP7PPH/7@GK? +!5/7PPP'/@,SGK? +!5!5'))FFEEJ`  "+467&JubHDf)oIfsum5L  "+'>54&'7LfBGau-musfJ#:LKPX@PM >@UIL@Y@ %B'H+&#"&7>=4&'&76727>32&#"&7>=4&#"5=B^=B* 6q5   %p:HG#?=T?>3-1!Z?;;# I554&"&54632;+:=Jmjo3b`FdÛ4@1<:dIK?  +"&'676&#"&7>54&Uo#!=o=33+ )(/C '- 41 3!\)&{@ %72!47>54#"$olZX_h% !ZN@gP5iPZBnDLl5 qO}?\Lo+ ) +N11~@ .32#"&5463232654#"&76}/>A  'qRP^i=g}:+M;Z! /%! 77?:R9\Rh.*"%EX}&WN$-@*<dPM>2("+35#"76767676323#&"&7>q> 9I"'&s g-<=>B+KLw1: v(F19!bC@@ <bSUIMA"$$"!+4'"'!!632#"&54632726;: -N !Aqxh=!5B=%FZ^+!!UN# ,@)<:UIMA)$%!+4#"3267#"&5467672};:V6=+uql`/?H7V++/ ufMPF'/?#/>'X'W54&#"&5467'&746323254.'9bR)1@UIMAY$%!+7327054&#"46325$7".}=:V5=,rnm`1eq 4D^'-)sfMQHBuQ '/?1J59==B CK&PX@OK >@GSK?Y+%3##5#5353bPPfOOH/f5K&PX@ K >@GK?Y@  +%!5/fOOP')@&SK > +%!5!5'))HHEEJ`  "+7467&JubHDf`oJfsum5L  "+%'>54&'7LfBGau`mtsfJ?BJ/D@A, <bUIMA +)&$  / /$ +%327"&'#"&54?4#"#"&54632327^\7LX#-5H1B\wn3<F *5!?1TC1/!P@{59]u -F+JL@I<bUSIMA  +272#"'&54632%4#"PmE )Fk;M}y\!/u+M P!5B5D{}Z3F%_=/?J *@'UIMA   +2#"&5464&#"3267ywZB9J`D1JJ{mkyymb!^BJ8@5:< UIK?EACCC +%&#"&4726&'&'&#"&47>767'.'&472776&'&4727kk'),J)BI7 R +J-I)!2es**JHJ+ JT +J+I*&8!!Vf!!;w<!!uu!!B'J{@*bUSIMAY@  +""'>32#"&'654'&7267mF )Ek;M}y\p /u+NP!5B4D{}Z3Fۚ^=h;>@;:5<ddUIL@:2$9"+632&#54?>=4#"2&#54?>54&#'"5'672\R N1 f1L R5}#+{:h $$ \ $$ !=(% 5uJ}@ <4/.@)ddbGIL@Y@IHFB;9303 +4&#'"5'6726?>&'&'5737&#'54/&#"&#"&7>s+{:J33  =. X3N'CL1N\% 7=J^>B+L=(% 5V44  )) 7T 7- *-o=-(@% <bIK?J+4&'&#&7672&#"&47>q+ jF5-VZ+5JF  5R/L !$JP>@;<#< U I L @POJIGD$B#&H +7&#"&7>=4&'&76727632>32&#"&7>=4#"&"&7>=4&"5=B^=B*!5q5   N`n ^0#?=T?>3s/G1=>3-b?;;$ Iqi&C=?ËT;?L?#J:+@(UIL@%B'H+%&#"&7>=4&'&76727>32&#"&7>=4&#"5=B^=B* 6q5   %p:HG#?=T?>3-1!Z?;;$ I5@!dUUL>Y@ #$$'2+&"&7>54&'&76727>32#"&'532654#"-;=>B+!5m6  f+Zea!Zt-Lh'q 9!;;$ I '=tiwh\oV)9J*:@7)<bUIUMA!-(#+372654'.4632."#"&#"&9-*+9)9wTIrNo70NR5%533DNT+!`%L)%&'N1#BX#P<+'%%-7%9LG@@=<:UIMA%#"#"+327#"5#"574326=732#+J! FlfR7+N s#H}w1FL%3?@  =<:@5  bbU  UUM AY@7531-+$#"  ?? +"3232=7.#"4"'676;3>32#"'&#"67 =57 V< VK% !##!"/9'; a44##`'o4GN6%5/ /%7-/!s+8_@\* 10  < Z U  SSM >8863/+(%"! 3#2+#3#32�'54;265#535#534&+"=7;!.+"v:N8?8.C@+9obPTfT^))+4^TfT39*'F%DC@=  be U  SSUM>Y@&?>=<987631/-)'#"!  DD +%2327"'&#"#"&5476767#535#53>7632#"'&#"3#3!6=[=B=DT{d@X  b U  UUU M = M = M = M  >Y@!|yvspmjhgfdb_][YLJGFDB###,BS""+ #&4#"#"=7632>=732'#327'732654&'.54632.#"#"'&#"'#"5#!"';2&#'54;2h=  )V< RsL D5m -'<7- =7aBp\uD=IuW/^BDbAZNLb)nB`R [asb32m5L 5i +'V$ Dje174 jg'%'^T$<7LE?_39H5b5^qFK98'#'EV:Zu$6MLRV%%-`6x@'   USUM>Y@65302$# +!3#3#"'.'#&5;2>7!&5!.+&537) 82)%#+K+ 9|]-)7>R[8*)&-T71f)1!5\B1!Kq581E!%!1,O9!1AZ-!1==R9    b  TO =M=M=M>@<   b  TUO =M=M>Y@RQCB=<873210"%$%&+!!4&'&"3276#"&5476323275!&7467!.'&#&76723#'&'&"=Z ?g{D;ZPq!jJ5 '  iye8^ Nd_/'JfTbd{ۍ{#25&*R%P= % ?H0 4O@L .$"<U S  SM>440/-,*"'" +?3632'#"!!!!32>7#"'#573&547)7?$Fy'F'{7P8-*?+P5J&HJ'XLC m#5!)?f $5TK1PX@SRP <98: 9@SRP <98: 9YK1PX@UUM >@(UUM =M >Y@ 2T7&+&,% +4 6732'6>7632#"'32>54+'67632!"&#76;277'6jsLX :DIYJARK/'KhH#!s)`o1 :\ e3%b 7^p4X~:D4:!wlw7!j}Tn'!Zn&mu KNWu#T5 r)-=CJP@C L@9  U   S SUM >Y@/KKDDKPKPOMDJDJFEB@?>==;42-+*('&%3#3!+#3#!"';2�'54;27#&535#&5354#"#"=2>7632)&#"!654'27+w V+`7.m1Lj]+]+V & @f%NX-'Fd9#7 7117RV%%Vq7d7RV$cd  F\,L @ 2-"+ ' 654&#"3265#"&54327327#""&54632#"74654&#"3267Vl+#L dVT^VyV1bd -)y^Hs\f\9HFy<$f%6yjJ)>l%%5TyG FFT- 7"{HTl%%5TyG FFT- 7"{L^23BG'?+R_@BY7'?bZXgLD$"+%"$57632'!"32672zX}ϋپ# 'hhgc~lӽ'JǮ[f XFCiwZ# +5 3.(#"+ '&32'!"32674&"326%462"&ZN}d  $ZB}N𺃞g' ^FGI>;Jywwy%v +yD<{X$9[X<9TV9VyyVTxxq%3@ @ <60) "+ ' "&54632#"74654&#"326732>54#"4632#".Vlg\݃d^;F Hy%'f%7=s`''/J%^X\Ϩ{lXn!eHT:!"/ FU<+.;ALZIob%wY]bq%T ;2 "+ ' "&54632#"74654&#"326732676727327#"547#"&54?654#"'632Vlg\݃d^;F Hy%'f%7=s: ))!zf -)}ZJjqF5< /){\JeHT:!"/ FU<+.;AV/'5sTR)7#}D +y-6M)7%{B Z<GQ OJD?"+#&#".7>54&'.7!272#.+"3267>"'.4&"326%462"&?s;B  s?@r   6*%9hJ}: 11 RFGI>;Jywwy9{; 11 <{{; 2v PV9FR1Z {5%7/%9[X<9TV9VyyVTxx1\@/"+&'"'>3264#"''7654.54>727#"'67\Nvyk{wJ=->j6bsչD}^?ÅPReQIZ"`qd2uTo^Zmr\_+H1<;nTkL|h1f7J?fPLPu#hT`^koQ7f-GPLI*"+%&#"&47>5!&#"&47>54&'&47327!4&'&473276#?s ;Bs?C@s͍ s??s ͨs@@r;B s?= ={; 11 <{i{; 11 <{5{;2  1 ;{H{;2  1 ;{m T. "+3674#"572767332327#"&547654&#" T )D )i=7-ɋV)f+u%5V."r1; 3  =hZ'%y#2+/vJf0-/2T87"+>732327#"&547654&#" '5?674#"5727673((`ɋV)f+u%5V."r )D )i=7-t " R8_ =hZ'%y#2+/vJf0-/2,R1N1; 3  D="+4632#"'%&"'76232654/&54?&#".fP+9y=%'7'P=%1?#7))yN-F^Lbw>ۚRJ15/D'7d{H!!" a#TXN/FPN+skfH+) Dw *"+7674&#"27#"&='674632X6$36RTR 9DbPD[wFK#Nj3Dgh VXxo-5Rݶ9@1qwHFK-CHFD"+4.'.737#"'.&#".7>5.'&47327#u 15?  s?/')3   15? ͕  s? Z> ! HK+ 2  1 A"?5 !yHK+  11 BV-R2 w.>yFD@LXc @ `[RMFA8$"+#"&546323265.+"=7!254632#"'.#"#"'&#"!"7463!22#"&5464&#"326PNm'7! ;# R/ R!PNl'7! ;#+H# pZ %ۃywZB9J`D1JXוG1''?!b9/]'yTjזH1''?!b)'6+L)+-{mkyymb#y; /< 80%"+4.#"3276;2&#'54;"'56$3 32>54&#"7Za3dNhJD)3>5R{sV!>bR)T)'$  V}?i^M/+'''̚LX1'"3+LTsD(-8 5/*)%"+632727#".'&#"'6767&! 32#"T]u=#uDS<]!'*#PT!vLziX,&a[NK$'#U-2)``3c: +T;/qDZ"+46327632327.'.+'%'&#"'&#"'762>5#"'>54./װVN-' !)LO $)8^@#,'b14&s!#F9)F5#%1%= N-wDd/5'XB) iziͨ'(B`+/B`/%%1IӬZJ:'#o7fs3J=/ZX-G` =6#; 7D @84"+4.#"327673#"&'&#;2&#'54;"'56$3 32>54&#"7Za3dNhJD`Rm9V)3>5R{sV!s)'$  V}?i^{\+ `NM/+'''"3+-D90zY4-"+&#"#"/4.'732654&'.54632 #"'';2&#'54;2767=4#&=73673#"32&#'54;254;!v->/>H"hX4 = >X57F55-TrZ#  1f   =  / N=^/xX3"+;2�'54;274+"'7!'.57.+" #"'';2&#'54;2767=4#&=73673#"32&#'54;25= ) ?h!-'13'/"X " {R #P,a %!# )#/' Z+ " 1  1)%C    D$% /  >TrZ#  1f   =  /ND("+%#!"54764#!"'673!627!267>)'7hZ'>! R5TZ13mr#>,NwNT u F  Z9D:4"+%3:>76!"5>54#"#!&5632;'&54$3  2 # V#{"K6Q  #Ec  y{RӨbu Hc;Z95:4!"+7#*'47!23254&'4!'.#"+# 54 1 "  #{#5R Fb {xR%Өbu~ Hb;㽍M(="+!54&#"327#"5432FmqpoF%uy'- =yB="+#"&'#&'+&'47>54&5467'&547367&546?2oN3-0*'@%*KNR>NF2|"T!1LQ:Pq4/Z^7`L5Hs3=?1N'/\7DZ_VLV8Zj%w?N  '%#) "+23!7!654.#!"&546?3JmI2 X/1s))PZJU3L) H;yTR;g)R?BR \+yo*""+".'#4&54767>54&'&546?32߃!,#6N\-4'%^ZF)1Nf}+ ` ˉT n=P !!%DTo "++6'4&57!"74>?3!2 RǏ%13%TFH%AD-}+?!F/@)-TH5 '&" "+&#'54?>54&/$72462"/1P3-TLGgGGg )% //')H5 =%fgGGgG` ^ @ E# "+"654&!"654&!"654&&54632327&54>32&'#"''"'#'67&54>3232)8wb3;:P7 ;7ds6fV^DVyfjy!O8DQ_P`%ٓRT?^PQ4fAB{%bt2w1Vv2/Vhhg/^w!K_=fe3˴uJRuBpXh{>doR5S@82<>::K PX@< dbX`   SUIMA@= db``   SUIMAY@76MLJFDC6S7S#"$)&& +'4&"#".54>32#"&5463232654#"&76"&5676&#"&7>54&BVTT/=B 'qRP^i=g}:+L;Z! Vn#!=o>33+ %  68@9R9]Rh-+#%FX}%'/D '- 31 3!\N*\z@)B Y  KPX@F b  b  `  `TU QM =>KPX@H b`  b  `  `TU QM >@N b`  b  `  `UTU I M AYYY@ XVSQOMIG><64.-%# ** +'"&5463232>72!47>54"4&"#".54>32#"&5463232654#"&76BVTT$olZX_h% !ZN@gP/>A  'qRP^i=g}:+M;Z  5iPZBnDLl5! qP}?]Lo+ )+%  68@9R9]Rh-*#%FX}%}P#Al@i&  <,(: db   SSUIMA%$;:8421$A%A"$$"% +'%4#"'!!62#"&54632326"&5676&#"&7>54&BVTTך;: -N #?qxh=!5B=Vo # =o>34+ $EZ^+!!V'/D '- 31 3!^R#J@I :  KPX@B   b `b  TSUQ M >@H   b `b  U  TSUIMAYY@%$EC=<9842+)$J%J"$$"%+'%4#"'!!62#"&54632326"&5463232>72!47>54"BVTTٚ;: -N !Aqxh=!5B='$olZX_h% !ZN@gP$EZ^+!!Vq5iPZBnDLl5! qP}?]Lo+ )+LR5U*@2> A ;:  KPX@Hb``   bU  S  U QM >@Nb``   bUU  S  U I M AYY@TRPNJHDB@?&#"$)&&+'4&"#".54>32#"&5463232654#"&764#"'!!62#"&54632326BVTT/>A  'qRP^i=g}:-M;Z! ;: -N #@qxh=!5B< %  68@9R9]Rh-*#%FX}% $EZ^+!!VDR(H@1 4 .-  K-PX@:d  b T S  U  QK>@@d  bU T S  U I M  AYY@GECA=;75320/#2("+'35#"76767676723#&"&7>4#"'!!62#"&54632326BVTT> 9I"'&r f-;==B*;: -N #?qxh=!5B='Lw1: v(F19!=$EZ^+!!VP#AR@O&<,(:dSUIMA%$;:8421$A%A)$%% +'%4#"3267#"&5467632"&'676&#"&7>54&BVTT};:V6=+uql`0?I7V+Uo#!=o=33+ / ufMQF'/?"0='f'/D '- 31 3!`R#/C@  :9 <@8bSU U  U I M  AY@?=42%#"$$"% +'4#"'!!62#"&546323264"3267#"&5467632BVTT;: -N #?qxh=!5B=};:V5=,uqm`0?I7V+$EZ^+!!V / ufNPF'/?"0='}N%2PY@V5& <;7: dSUIMA43JIGCA@3P4P-+$" +'">54&#"&5467'&746323254.'"&5676&#"&7>54&BVTTvbR)134+ )PJ,N)'-TXI+7_?wNZ3h# 7`DTOg/=6m!&8'/D '- 31 3!RR5@Wd@2XOC; KPX@9b`` U  U  QM >@?b``U U  U  I M AYY@76_]VTJH6@7@#"$)&& +'4&"#".54>32#"&5463232654#"&76">54&#"&5467'&746323254.'BVTT/>A  'qRP^i=g}:+L;Z! bR)1@9bSU U  U  I M AY@%$MKDB86$.%."$$"% +'4#"'!!62#"&54632326">54&#"&5467'&546323254.'BVTT;: -N !Aqxh=!5B=jbR)0;Pd}{XfDs{TJs!PP1''$EZ^+!!VOJ-N)'-TXI+7_?wNZ3h# 7`DTOg/=6m!&8ZR6C@7."K-PX@+bU UIMA@/dbU UIMAYY@><53)'  +'"'673!#67">54&#"&5467'&546323254.'BVTTM+"+%+d\E1-ebR)1;Pd}zXgCs{TJr!OO2'&6'H8 ŞPPOJ-O('-TXI+7_?wNZ3i" 7`DTOg/=6m!&8w!9@6< :9dIK?!! +'"&5676&#"&7>54&BVTThVn"!=o>34+ '/D '- 31 3!?'@  <K >8 +%&#'54?>54&/&'5737}5@TD33DSD1/' )) *-/#  )) )+; 7*@'0*)<UK >/+J42 +%;254&+"&#!'54?>54&/&'573!7y5@Gw7@Gu3AS1D35BTD03.a1-^q/% )) *-/%  ))  )+? E.@+>87)#<UK >=9J4442+%;254&+";254&+"&#!'54?>54&/&'573!7}5@Iu7>Iu7@Gw7@Gw1CS`B51FSB23.a1-^q3.a1-^q1# )) *-/#  ))  )+?KE@BHG943<b U =>KI:3+3 +0%36&/"'5737#"'&+"&#'54?>54&/&'573R3#3A %9' !P?u1DTD31FS%)-3!)) +;4G-^q1# )) *-/! )/:@7-<b U>/.3 +0%36./"=737'./&=7R1 #!@!%7)F'7+ ')-#' ++)?34P' +VKH@EI/*<b U =  >KJ@?;8.+C +0%36./"=73!7&#'54?>54+"'./&=7R1 #!@!dD16?TD3w-7'F'7+ ')-#' +) )+q/' )) *-^+E34P' +! YU@RW("=76 <b  U K =  >NMIF<8'# YX42 +%;2'4&+"-36./"=73!7&#!'54?>54+"'./&=75@Hw7?HuBR1 #!@!/D13BT1D3w)5)F'7+ 3.a1-^')-#' +)  )+q/% )) *-^-C34P' + ' fsf@cUD5-=c<  U K =>hgnkgshra^ZWTEB?:74.+(%$ fd    +%3254&+"&#&#'54;254+"'&+"=7232$3#" 6+"=723263373737#";2&#'2'4+"3ZHw8?Hw8\UTuu`3#FN+ R3  1#sA UTSw7@TutJu5@;a1/`i3.?)a`&<34G3'#1-^''`i3.);a``i3.?ar^@[^]OI4/.<  b  ` U K  >qoeca_NJGF?>7634$+073276&/&=7372&#'5437>'&"2&#!'54?>54&/&'573367674'&'#"? '; PX-)Vtfe '7%  %% D33DSNuyZN^+u)C%) ++ 9=).m --1i-) *-/#  )`zj(~m ^!`T@Q"RM83 <  b  `U  L  >VTQNKJCB;:744$"+573073276&/&=7372&#'5437>'&"2&#'54367654'&'&7 ? &< PX-)Vufe '7%  %% yfRZ))C%) ++ 9=)'m --1i--j(~u !r_@\5/d_JD<  b  `U K  >hfc`]\UTMLIE40,*&%  +3254&'%57373276&/&=73!7&#!'5437>'&"2&#'54367654'&'&RX+)Tlw4C= ? &< D16?Ts '7%  %% yfRZ5;).m{ a/# ))C%) +)  )+q/' )-1i--j(~u !y f@cC="rmXRQ <  b  ` U  K >vtqnkjcb[ZWSB>:843,+('&#  +3254&';2'4&+"%57373276&/&=73!7&#!'5437>'&"2&#'54367654'&'&RX+)Rlw6A5@Hw7?Hu/ ? &< wD13BT '7%  %% yfRZ5;).mwa/% 3.a1-^s))C%) +)  )+q/% )-1i--j(~u =(,@) !<dZL >3C;8+4&/&=7373!2&#!'5437>53CSD1#)\1+BPC;//#  )) )+m- +5Fb ,@) <UM>   + '&32'!"3267M}d  %ZB}N𺃞f' ^%v +yD<{X).-@*,<UM >.-&# +3 4.#"'263 !"$#'54?>54&/&=7f_CkPLM98'&D33D3m7#ʓXa) *-/#  )!DW@TC <'!  4/<  b   UL =>DDA>#35%34 + 3%&#'5637>5#"'2&#'5437>74&/"=7>/#Z#/>b>3R7  EE5@yKB1M /<!)'1e7++#2G%3%++89-) -"@UK= >CI$"+4632#"&&#"&47>54&'&47327F%';B)%=2b`^ b21c ]Zb2V%=C'#?AH{9 11 8{{{91  277 4@L=@:-'<UK=K >KIEC?=97,(J31 +4+"3254&/&'573!7&#!'54?>54632#"&%4632#"&\XNVVNXk'1'J'V'/'Jj'1'{E%';A)%=jF%';B)%=ZZ\]A+)''R-,'').%=C'#?A)%=C'#?A7 4@LXdJ@G-'&<  UK=K >ca][WUQOKIEC?<96,(J31 +4+"3254&/&'573!7&#!'54?>54+"3254632#"&%4632#"&%4632#"&XNVVNX'1'J+'V'/'K'1'XNVVNX}F%';B)%=F%';B)%=E%';A)%=ZZ\]A+)''R-,'').AZZ\]%=C'#?A)%=C'#?A)%=C'#?A7HT<@9# A<;<UK= =>$+:%(3.H+4&/&'573!776&'"'5737"'.'#"&#'54?>54632#"&'1'Jm#- ##P uKd'#<3%V'/'K'1'F%';B)%=+)''1#B_X;#)% `J/+3%Z-,'').%=C'#?A s*@=>H(C +.7327"'.'.732776&  fV=R  N?"#"6L  {CH M !/1  2@P/+V6 1  2 6Iu?=N5sHT?@<.(A<; <UK= =>SQMK@=C.9%!+4+"'.'&=73776&'"=73!7&#'54?>54632#"&JX!1>#>1mE#- ##PuK8'V'/'J'1'F%';B)%=Z+5J/+3) )'1#B_X;#)'R-,'').%=C'#?A  Uam]@ZHCBQ*$<    U K=L => ljfd`^ZXGD;953)% U U31 +4+"325"=73!07&#!'54?>54+"'.'&'573776&4632#"&%4632#"&XNVVNXuL'V'/'Jj'1'X'f&#>1mF #- "#FF$'ZZ\])'R-,'').AZ \J/+3) )'1#B_X;##%=C'#?A)%=C'#?A  amyh@eTON"]60/<    UK =L =  >~|xvrpljfdSPGEA?51! aa3331+4+"3254+"325"=73!07&#!'54?>54+"'.'&'573776&4632#"&%4632#"&%4632#"&XNVVNXXNVVNX$uLn'V'/'J'1'X'f&#>1mF #- "#F%';A)%>kF$'=<b`  UK = K >}{tp_[ONB?<;0/+(- +%>?654'.'#"2?>&/&=737&#'54?>./&"&#!'54?>54&/&'573!74632#"&!#G'-H1'VVk! }  ! `@9F7+B''mE w hD'1''1'JToF%';B)%=; 53D9Z\# ''-H =< ''!'').A+)'%%=C'#?A!sN!@=L >IE:963CJ +?6&'.7327&#"&47>&/&&"&47>?6'&'&47327u}!=  jBDQ?R6 ;FL bUP{ '#w;  jR ?P8 PhjF`o Hݸ51  2)BR+ 11  //3 11 'H s 1  2)NdvZ@Wa\IDC-'&<b`  UK=K >{yusif`]ZYHE98,(L +2?>&/&=73!7&#!'54?>./&"&#'547>?654'.'&=7374+3254632#"&! }  ! `@Z'V'/'J v hD!$H)/F1d>wX';D7;D3XF%';A)%>3# ''R-,''!'# 73F; '%^Z+F T+]%=C'#?A)dwe@ba\IDC-'<b`    U K= K >|ytshf`]ZYHE98,(L +2?>&/&=73!7&#!'54?>./&"&#'547>?654'.'&=7374+3>54+"3254632#"&%4632#"&! }  ! `@'V'/'J> v hD!$H)/F1d>NX/?35D'1'XMVVMXPF%';A)%>E%'C+74&'&7672&#"&47>)[ i4`]V `4q1&*RP{; 11 <L/-@*<bM=M>$$$$"+%#"&5432#"'.#"327/Hg@$FA:fri!wW lV+0J-3/¦ׇP=e,@"U=M=M>Y@ =<"%$%%+%4&'&"3276#"&5476323274.'&7672'&'&" ?g{D;ZPq!jJ5-8 i8^ N/'JfTbd{ۍ{BA&*R`P= % ?H0 -?\1@.<M =L >ZYK$J)I%# +76327632&#"&47>54&#"&#"&47>54'"&#"&47>54&'&7672\Np}V+Xs`jgT+==|)PfcjgV)b{#,; {Tb{Z20V d UA{9 11  ?=64+)0 0  +2!"$#"# 4>$32262>54.#"'4&#"32>u?' !!#+l!AfqNGqLj>B>hRkCfX1Nqpf7#VXj+iŽX5E}9"[H2P}Ս!#?G2@/G@?>5#<UM >($ge!+7437>54&/&=73263 #"&#"'%32>54.#"'5>54'&'! ;5.%>76==O}Xj=TEJmP;}ߖRR:RJ/hBfv)%.3 +ׁT!1+fËwзl0G 9Nw'/^mq9A[c?@ XVOM+)9 9  +2!"$#"# 4>$32262>54.#"''>54'&'.576754&#"32>5u?' !!#+l!AfqNGqLj>;(fBBCmv#h%<>hRkCfX!wlB1Nqpf7#VXj+iŽX5E'U9",bl%'œ H2P}Ս)#hb,/?@<<b`UM>  + 6332#"'676$3 +%sɸ1A)' VN%!}n) o\u%!1@.<bM=M>$%&$$+76732654&#"#".54632#"% 1Lp/@ 7%DO< )% )!Py"+3!!#4.'563NH bR{bkm)/DP)=D "+&'#52>73=km)/CNH cR{cy"+%#67!5!&'3NH cR{bkm)/Cd+RF "+5367"#&djm)/DNH bR{bdf#"+&'&'3#67!#&'&'567673)L#;#P}uuSyZ+RhiymdR+TL}w{P!>%ITb6\NWX/3N5fm5N3/\XLa9\dR!# "+567673&'67#&'&'5b6\NWX03N5gm5N3/\XL`9]L";#O}uuTyZ,RhiymdR+TL|w{P!=%JdT"+''>'77LP:*"O%6FK:b!&(dX  "+&7''7$77FL:b"&(7KQ:)!Oe-T!"+'67&7&$7KQ:)!Oې7FK:b"&(e3X" "+76.'67FK:b!&'8KQ:)"O$y "+3!3!!#!#4.'563NHT;T$bR{bPkm)/Dy "+%#67!#!5!3!&'3NH_T$T~cR{bkPm)/C"+3!&'3#67!#g-cRVbTyPLgo\)/=N!. "+5>73&'670!2#!"5463!.'50)DEgm /:C?;%!D%GOyTb=>RfkmdR ,:KN]+#3!.TpPNdf+)"+&'&'3#67!#!#&'&'567673!3)L#;#P}uuSyZ+RhiTImdR+TL}w{P!>%ITTb6\NWX/3N5fm5N3/\XLa9\Py-7 "+567673!yhabPbm-J3NMrk};T "+!!#&'&'}wfiRbZoTkm֗LL5y-7 "+!5!&'37BfiRcZn-km֗LL5};T "+#67!5;h`bPbmuTJ3NMrky;  "+#67!5'567673!;h`bPbmuhabPbmTJ3NMrkJ3NMrky;  "+!5!&'3!!#&'&'7BfiRcZnFwfiRbZo-km֗LL5km֗LL5y#""+#!#.'5>73!3!!!%7![T2#)RbffυN"(hT0UUr3LH;??;Xu%/%PD`deeB;5Jy  1 !"+!73!67&'#!#.'5>73!3!&'3#673LH;UTU;HL3T#)RbffυN"(T$("NffbR)#B;5JJ5;B?;Xu%/%PD`DP%/%uX;J""+!&'3#67!#!5!7!5!3!67&'q#)RbgfЅM"'StT$TT13LH;;Xu%/%PC9edB;5Jy# "+!!!!#.'5>7373LH;/#)RbffυN"dB;5Je;Xu%/%PhJT"+#&'#5>73&eB;5Jd;Yu%/%O533LH;#)RbgfЅM!J "+!5!67&'!5!&'3#6533LH;#)RbgfЅM!9eB;5Jd;Xu%/%Ob%N"+367367#.'5dB;5Jd;Yu$/%P3LH;/#)RbffυN"y  % "+!67&'%!&'3#67!#.'5>733LH;;HL3+("NffbR)#v#)RbffυN"B;5JJ5;BdDP%/%uX;;Xu%/%Ph%T %"+&'6767#.'55>73&\B;5JJ5;BeDO%/%uX<;Yu%/%OL3LH;X;HL3h("NffbR)##)RbgfЅN"d "+ ''6'77Q/FS_ _G"9 /"c7P;Gc X_G.CZ96b!/ 7 q"+ '&7''&'7$7&Gb X_G.C[:6b!/ 7 GbS` G.": .!c7PiVr "+%76&7767&$'6GbS_ G.": .!c7Q/G X^_GC[:b"/ 6!eZv"+ 6&'6'70G X^`GCZ:b!/ 7 F0GS_ _G": .!c7Q  "+#3!3!Fn^a!{- G`!"+ ! #&#"3273m J1ߓ1HEJ"/*$"+654&#"#"&547632#"&5432&#"32>^`L3>-"rXEkyhzuneEmF*OB 1!J!L麁hےu`H "+!5!!5!!5Zm\=\/ "+!'7#53#5!!5!73 !;L3(X~m8J00}әlm\\X5w' ' "+"&&54327#"''73254&'~jpFcLFQuubC`g%iZNуGv@QbH"T4%,3xH "+ !30#!"547)\X7w ! 2J1  "+%!#0&473!2wD!ͤH  ;"+)"3!!"!!3!T3ì+=ZZts;\/ & "!"+)"''7&54;73#3#3!%!#"LIBLCT9K0*T12=~D(Ytdsr )r{;{u"+% !3#!!;\ɦ ={\{qodn1:J#nZ"+!2#!5!267!5!.#!Z3R>+!usLd7 & "!"+!27+'7#53#5!&#!!.3267R3LIBLC8L0*T12픕Dٱus *{Me;ys"+ !#53>7!5!.+5\ɦ >{\spodo1:I#o?D""+4&#!";2&#'54;254&+"'523! 726?+;2&#'54;2311u9T7w154&'&473273!254&'&47327&#+; s??s nms?^^@r djs?@r; 1 ;{5{; 11 <{E{; 11 <{{;2 1D"+.#!!2>7!527 -'Z5-`^>1V/-:')!HCR\Ec? -''!^/{5 "+!"5463!2!!&!.#3{'!"+%#"5!"5463!4632!2#!!2#!"546f3#%N/!g!L!%?%`#4G!!/!/#4{ % "+4632#"4632!2#!#"5!"5463!;)'<8+-3#i%/!!R R<2#"#".'#"&54>#3!#'x%+#D%'wJ ?1]4%#//2-#"5^L%Z)y!/H[)L?3% Ay; '5;!)  "+4&"326%462"&EHJ=;Jywwy9ZX;9UW9VxxVTyyss"+"&462/VCCVDCVDDVm-  "+ '6753N}jR#)6:BOPy PN- >) "+ '67534&"#".54>32#"&5463272654#"&76N}jR/>A  'qRP^i=g}:+M;Z! #)6:BOPy P%! 77?:R9\Rh-+#%EX}%D- 1 * "+ '675335#"76767676323#&"&7>N}jR> 9I"'&r f-;==B*#)6:BOPy PLw1: v(F19!L} ,%"+.#"326&#"327#"#"&54632;5T^fwfH3!$H/)5fc( "۽Jh{ĚsE fdqZJ9Dw=ZhnL} / '!"+.#"326%4&#"3267#"#"&54632325T^fwfH{fH/)5fc(^e}×۽Jh{ĚsEỉfdq9jǕJ9Dw=}hn"+3!!h\="+7!=EM>AZ= "+6505!&'7!!&' KTeAdTGMFfj F)R-HA{Zmh/ϝP "+6765 &'7%%6'bD)c3N\K3.:WFg"*C24FWRc=aK3ObYSeU3"+#3ZZ%  "+#'731Zy1ZP3 z16% "+#3#3 ZZZZG) "+##'7373q1ZZJ1{ZZm4E`VOJ1|Ug"+##3#lklsy"+%33#3`lgkil^s`"+3#4632#4.#"iɗh#q`tNujDz`o"+3#"&533265hɗi"q`uoMMujƲ;#"+%4&5%2#"&#"#"&546232O-@5%#=TVj1>1>I'1/RR 4%!.C/D2&-?;#G<*"+%4&5%2#"&#"#"&546232%4&5%2#"&#"#"&546232P-?5%#=TVj1>1>I'1)O-@5%#=TVj1>1>I'1/RR 4%!.C/D2&-?RR 4%!.C/D2&-?;#Gk `N<*"+%4&5%2#"&#"#"&546232%4&5%2#"&#"#"&546232%4&5%2#"&#"#"&5462323P-@6%#=TVk1>2=J'1O-@5%#=TVj1>1>I'1O-@5%#=TVj1>1>I'1/RR 4%!.C/D2&-?RR 4%!.C/D2&-?RR 4%!.C/D2&-?R;(3> 94/)"+&547!2#"&#"#"&54623267>54&'4&54֦*-?5%#=T z1>1>J#.4%!.C!72&-?ъAjHQ :o "+462"462"@Z??Z@@Z??ZZ??Z@Z@@Z?s "+>3232672#"'&""sZBW\85w jK#BZX=7~"hADDE dwDBTAs"+>3232672#"''&""sZ3232672#"'&#"!2#!"546;"!"5463!2ZBX\85w jL#BYX>7}#!%!!#&hBDDF dwCBTB#3!.F!.#3o1% "+7>3232672#"'&"""'>3232672#"'&"oZBW\85w jL"BZX>7}#ZBX\75w kK#BZX=7}hADDE dwDBTA+hADDE dwDBSo=5"+7>327&""'>3232672#"'32672#"'&''&""oZ)60V=7~"ZBX N 5w kK#%30R65w jL"BZyN%"7}#h@TAhADE dw;DE dwD{TA{%{ %  "+&'&'676!"5463!2!"5463!2/Lw\sbH$!#&!#&{{L^s7s!.#3!.#3{#""+!2#!!2#!'!"5463!7!"5463!8%P%NN!#Py!N#3#3ny!.!. "+!5!5!5))ZZZZZZ "+!!!!!'!5!7!5!7!5!,^+:mNe+,u,_NZZZ+ZZZtV "+75!&7``F #-5! F V "+75!%.7.75w``E -5 F; "+&7 &7{E #-5! FE "-6 E/; "+%.7.7-.7.7wwE -6 EE -5 F"+%'%&7%7 S_N٘NtE s|-6tR EhQ  "+-%.7'.7xٳg5NrM][l3fE p6-52 F;1"+75!7%&7%%%!!'7NuNbK{gE$INB`r-5]` FnF `GZGH ="+7537.?%.7!!'7-"}i-rNt. FN>`g. F;;cE nU-5Њ`_oLX+ "+%!".54>3!! 3NPX79X{NNɴV2O\VL+V}PX+ "+5!2#!5! 4&#PPX79X{NN}ɴV2O\VL+V}Lo!"+%!'7.54>;73### fONHY{C9X{NGN?V4dvV%PiVL+V'Rt>Po!"+5!037+'7#533 4&PGN@Sq=9X{NONGU|}V)QdVL+V's @  "+ 32!.>7!!!cA \ Hy8 Os   "+ 32%!.#" 67Ӕ#yÏs !@ ! "+ 326'4'&"%727'Fc~|aMDegyczKJg{bRTs   "+ 32"&2654'/MgeNy;ߞfT\Sޝg{"+!#3hhs\3"+5!3#37ii\-I.)"+#!5!!hC\\)"+3!!5!Ph'J'\\{"+!#3hhw\Z   "+4&#"2%62#"'!#"&4632>-+;=V5X}\+%Z{}XPoV>>)-;}ZX}wyVIZ   "+32654&"#"&54632!62#"&'f>-+;=V`5X}\+%Z{}XPoV==)-<}ZX}vyVJou"+"&462+VDDVDCVDDVhLB`  "+2"&42"&42"&4Z@@Z@@Z@@Z@@Z@@Z@`?Z@@Z!@Z@@Z#@Z??Zu  "+"&462"&462"&462oVDDVCVDDVCVDDVDCVDDVCCVDDVCCVDDVs# #"+"&&54327#"''%32654'DudGz9w9`rk9ߜuLE2ƼY1S3B읖nV+ "+! !@BzFm"+ #3#cVc+G1L5"+#!!oT%Z% "+!7>7632#"&#":@{+@-%+25)d(%/3%#-D0'R% "+#"&54632326767: @|+?-%+15*%NDCe($/3%#-D/'!Jo @  "+ 367&'! #! !X5 5Ǟ )Ѡ7ps}No3q @  "+%! #! !# 5ɘNcM5F7puqJo"+'&/7672 hG #^߁CC!^# H# (NM'm"+7'&'63"'6hH $^!CC^# H ٸTMNT;hq @  "+#3 !3# %!!'56Ͱ55kl+Ůp7u9FN+` "+#!#53!5ղ-ZZZ3) $!"+2#5#"&546;4&#"'6265#"#7-b9kP\bmJ;PRkF7XL6Z#AۗVJ)&/_SXc9D3X5MgZ+/-~-qB!}Zu%"+667#ZB%~y H@5$//m\*y#\d@{t\VZ%"+#%%Zu%"+3.'Z/%5@H y~%B/V\t{@d\#y+\3%"+#.'673/%5?G y%A//\t{@d\#y3%"+#3%%3%"+&'>733A%x G?5%/y#\d@{t\/% "+3! w!% ) +Nbf%"+3#݉%%  "+3!݉!w N%bN+ -B%  "+!#4.'&47!!wbN+ * s%"+#%%B% "+!&47>5Nw!%- ,Nb#s "+3%<^B  {%"+#5454&'5>54=3Vv93rr39vV%TuwfJf  gJgwuT$#s  "+3$ߤ_sՖ{!As"+3#ߤsTs  "+!#4.'.7T^ ;+{  w%"+5.546=3#54&546DVw93rs39w %TuwfJf  fJgwuTTs "+.7>5T ^s!{+Rs"+3#su"+!5#"&'33267 ѿ7 NggyyDdgA`#"+3!53!#FF-e) 4@ 2,@,b` UTM>Y@ 10+)%$!   + $$ "32&#"&47>54'"&'672ʴ6o8]a*J,M4$PL8o7Ga^O'  )NM! V/ e) E)@,b UUSM>Y@ DB97/+%#   + $$ "32463232676&+46?654&#"#"&ʴ6o8]a)h[?Fb$(=+o<\cmG-BB +!8o7Ga^CzfdEaCb@9G \w@v^ao{VL4  e) R@ M L @;  b  `  ` U U  UN>Y@ KIDB9731%#RR   + $$ "32""&54>32#"&5463232>54"'>54&ʴ6o8]a)V45iD^3*>NKq|B~#    29%)\u?8o7Ga^.7E4;(3*@8T% db4%#  *Z?* w8<<e) DA @1  b  U U TM>Y@" @>:84321.*%$!DD   + $$ "32;2+&#"&4727>=!"'76;ʴ6o8]ac\ 0 \5A\#!( Κ *8o7Ga^ɇ z(  z8A u|e) A@"!'( @4   b UU U  UN>Y@ @>751/+)&$    + $$ "324&#"727#"'672#"&54632326ʴ6o8]auUETV.KEN P<.n7Kl|> #! =V8o7Ga^&gc Z e~>&+~e) %;y@ :8&@$UUUM>Y@ 0.)'%#   + $$ "3232654.#&'632#".54767ʴ6o8]aq WI4E C.@">@s0uQ,QR1|o8o7Ga^62umh@-bbU UM>Y@ +*&#! ..   + $$ "32"'65433!27'6ʴ6o8]a#)"43[ a8o7Ga^ 62/7 e) ".:E@ ) @& UU UM>Y@<;$#B@;Eeʴ6o8]aG/H@PI1LT`5AkTh^Z@ > L`JdXmITbF#/o)LUȶ8o7Ga^e) %;y@ &:8@$UUUM>Y@ 0.)'%#   + $$ "32654&#"32'"&54>32&56ʴ6o8]aB VJ4E!C.?#??s/uR,PR1{o8o7Ga^53tmh=@)y_?|aA`5 Te))4EV@  @9  b b  U   U  VM>Y@!GF65+*OMFVGV=;5E6E1/*4+4$C+&#"&47>54'"&'672 $$ "32"72>76'4'&"'.54632u*J,M4$PLIʴ6o8]a,-%3M(O86?haA[3QTO'  )NM! V/ m8o7Ga^ Mu ;2SA86kVwo\,e);GR@ 93@1   b `   U T M  >Y@IHOMHRIRCB=<87$C$C+&#"&47>54'"&'672&#"&47>54'"&'672 $$ "32*J,M5$PLk)J,M4$PK8ʴ6o8]aO'  )NM! V/ 3O'  )NM! V/ m8o7Ga^e))4c@ ^\ G   b `   U  I TM =M>@<  b `U   U  I TM>Y@+*b`WUMICA971/*4+4$C+&#"&47>54'"&'672 $$ "32463232676&+46?654&#"#"&*J,M4$PL?ʴ6o8]ag[?Fb$(=+n<[cmG-BA , O'  )NM! V/ m8o7Ga^CzfdEaCb@9G \w@v^ao{VL4  e))4p@  k j @F  b  `  ` `U   U VN>Y@#65+*igb`WUQOCA;95p6p1/*4+4$C+&#"&47>54'"&'672 $$ "32"#"&54>32#"&5463232>54#"'>54&*J,M4$PL?ʴ6o8]a)U44jD^3*>NKq}B}#    28&)\u@O'  )NM! V/ m8o7Ga^.8E4;(4*?8T% cb4%#  *Z>* v8<=e))48b@5 <_;K&PX@>b  `   U  TM =M>@<b  `U   U  TM>Y@':9+*^\XVRQPOLHCB?=9b:b871/*4+4$C+&#"&47>54'"&'672 $$ "32;2+&#"&4727>=!"'76;*J,M4$PL?ʴ6o8]ac\ 0 \5A\#!( Κ *O'  )NM! V/ m8o7Ga^ɇ z(  z8A u|e))`@_:5  ; Z @@  b  bU  U  U  VN>Y@+*^\YWSQJHDB><971/*`+`$C+&#"&47>54'"&'672 $$ "3254'#"'672#"&5463232654&#"727&*J,M4$PL?ʴ6o8]aP<.n7Kl|> #! =VUETV.KEPO'  )NM! V/ m8o7GaN e~>&+~^gc  e))4CY@X D b  ` U T  U M =M>@<b  ` U U T  UM>Y@+*NLGECA;91/*4+4$C+&#"&47>54'"&'672 $$ "3232654.#&'632#".54767*J,M4$PL?ʴ6o8]a WI4E C.?#>@s0uQ,QR1|oO'  )NM! V/ m8o7Ga^62umh@7  b b  U   U TM>Y@+*KHFE=;871/*M+M$C+&#"&47>54'"&'672 $$ "3254''6#"'67433!27&g*J,M4$PLWʴ6o8]aZ a"* #+"O'  )NM! V/ m8o7Gaާ?  62/e) ".LXc@JD) @?  bb   U UT U M  >Y@(ZY$#`^YcZcTSNMIHCA=<9521#.$.!   +"654&#"&54767'&546322654/%&#"&47>54'"&'672 $$ "327Fd%sDN`rf|HCsy`Zi9bp>H=e*J,M4$PL?ʴ6o8]aG/H@RG1LT`5BjTh^Z@8 N^JdXmITbF#/q'LUO'  )NM! V/ m8o7Ga^e) 4CY@2 , D XV@6  b U   U  UTM>Y@ NLGECA;910+)%$!   + $$ "32&#"&47>54'"&'672654&#"32#"&54>32&56ʴ6o8]a~*J,M4$PL VJ4E C.?#??s/vQ,PR2|o8o7Ga^O'  )NM! V/ 53tmh=@)y`?{aA_5 Ue) HYj@" : K(PX@7b U  U I   UM>@=b  U   UU I   UM>YY@&[ZJI caZj[jQOIYJYECA<64,*&$ H H + $$ ">?654&#"#"&5463232676&+"32"72>76'4'&"'.54632ʴ6o8>XbmF-BB , g[?Fb$(=+-8Ta,-%3M(O86?haA[3QT8o7Gԥ;rZao{VL3  CzfdE`Db?8H \wdra^ Mu ;2SA86kVwo\,t`2.?*@'<ddL >$C+%&#"&47>54"&'$7247.%&'67?s-T wP)7{s  \@% \@${; 11 @ww3'H /lx\_PÚM!~PÚLh`*/@Q/@,(<bUK >)(F)"+4632!2676&#!46?654&#"#"&47.%&'67џ>HGϘm7>'-^B]lFfeD!1n \@%¹\@$fJTEǘ`VobsN/ %3.x\_PÚM!~PÚLx`8<M^K@H76<b``UM>53.,#! << +""&54>32#"&5463232>5!"'>54&47.%&'67L%NPP!$'RiN@_ysf71' !!LX9?$ a㹓 \@$ \@%L;j.P\=PAaV9O:.5 ?`BW]\x\_PÚM!~PÚL`L->O;@8*<dbU >)'#! -- +)2+3&#".7676'5!"'63%47.%&'67F' +- \o  n_?=A/ \@$ø \?%!.!' 11 0V#lx\_PÚM!~PÚLx`H+<MB@?< :bUUM>($$#$#"+4&#"327#"'632#"&5463232647.%&'67-kFwfy }ZF)Tu^/72^K \@$й \@%1N  !ݜ_;-B  9x\_PÚM!~PÚLv`8$5F'@$<" :UM>$"&$+32654.#"'672#".576%47.%&'67pPk1fHb5aaòŶD{L \@$¹ \@%uRN򲨠-\b@,)eѓQ'Gx\_PÚM!~PÚL^`o(94@1<dU>  +"'7433!27' 47.%&'67'7@#'6)RL!4 \@$ \@%HRM G>Rx\_PÚM!~PÚL`H %1BS3@0,<UM>'&&1'1$"   +"654&#"&54767'&546322654/47.%&'67jTl9g+R9-vǰoT7#X\o\{ \@$ \@%ooHmd&nJw-TL+RdӢ`J'us pj8J;w x\_PÚM!~PÚLz`L$5F,@)<" 9UIMA$"&$+654&#"32"&5432&56%47.%&'67fqPj1gGb5aañķDzLW \@$Ҹ \?%VRN򲨠-\c?,)dѓR(Gx\_PÚM!~PÚL` P"@QbN@K8<b UL =M >=<7510-)&%"" +"2>76'&"'&532&#"&47>54"&'$7247.%&'67Z'ED))83#w'=zVT_렖dN}2?s -TwP)7{r ¸ \@$˸\@$2v #P<&e#RVS #dɪC{; 11 @ww3'H /lx\_PÚM!~PÚL` U;L]2@/3< ddL >87$C$C +%&#"&47>54"&'$72&#"&47>54"&'$7247.%&'67@s-T wP)7{r -@s -TwP)7{r  \@%Ĺ\@${; 11 @ww3'H /9{; 11 @ww3'H /lx\_PÚM!~PÚL` U/M^oL@IE*(<b` UK =L >JI$C)(F)" +4632!2676&#!46?654&#"#"&&#"&47>54"&'$7247.%&'67Ѡ=HHΘm7='-^B\mEfeD!1@s -TwP)7{r  \@%Ĺ\@$fJTEǘ`VobsN/ %3.]{; 11 @ww3'H /lx\_PÚM!~PÚL` U<Zk|l@iR 76<  b```   U L =M>WVQOKJGC@?53.,#! << +""&54>32#"&5463232>5!"'>54&&#"&47>54"&'$7247.%&'67o%NPP!$&RiN@_ysf81'  !KY9?$ a/@s -TwP)7{r  \@%Ĺ\@$L;j.P\=PAaV9O:.5 ?`BW]\`{; 11 @ww3'H /lx\_PÚM!~PÚL` U-K\mO@L* C <  d d U L >HGB@<;8410)'#! -- +)2+3&#"&47676=!"'63&#"&47>54"&'$7247.%&'67F' +-\o n^?=B@s -TwP)7{r  \@%Ĺ\@$!.!' 11 0V#{; 11 @ww3'H /lx\_PÚM!~PÚL` U+IZki@f  A <  :  d  bb UU L =M>FE@>:9C($$#$#" +4&#"327#"'632#"&54632326%&#"&47>54"&'$7247.%&'67FkFwf{!}ZF)Tt^/71^@s-T wP)7{r  \@%Ĺ\@$1N  !ݜ_;-B  {; 11 @ww3'H /lx\_PÚM!~PÚL` U$BSdH@E":< :ddUL =M>$C$"&$ +32654.#"'672#".576%&#"&47>54"&'$7247.%&'67qPj1fHa6aaòŶDzL@s -TwP)7{r  \@%Ĺ\@$uRN򲨠-\b@,)eѓQ'G{; 11 @ww3'H /lx\_PÚM!~PÚL|` @5FWK(PX@3-<@3-@,ddb UL =>Y@21,*&%"  +"'7433!27' &#".7>54"&'$7247.%&'67'8?"'5)RL!5?s -T wO)7{s  \@$Ĺ \@%HRM G>R{; 11 @ww3'H /lx\_PÚM!~PÚL` U %1O`qV@SG,<db UL = M>'&LKFD@?<854&1'1$"  +"674&#"&54767'&'46322654/%&#"&47>54"&'$7247.%&'67TTm9i+R9-yƮoT7 X\o\@s -TwP)7{r  \@%Ĺ\@$ooHmd&lJw-TL+Rf}բ`J'ss pj8J;w{; 11 @ww3'H /lx\_PÚM!~PÚL` J$BSdI@F@:"< 9bUUL >$C$"&$ +6'4&#"32"&5432&56&#"&47>54"&'$7247.%&'67ZpPk1fHb5aaòŶD{K@r ,T wO(7|r  \@$ù \@%VRN򲨠-\c?,)dѓR(Gr{; 11 @ww3'H /lx\_PÚM!~PÚL` P"RctS@PMK6<b UK =M >QOFD<820'%"" +"2>76'&"'&5324632!2676&#!46?654&#"#"&47.%&'67'ED))83#w'=zVT_렖dN}-Ѡ=HHΘm7='-^B\mEfeD!1{ \?%ø\@$2v #P<&e#RVS #dɪCfJTEǘ`VobsN/ %3.x\_PÚM!~PÚLe)ES_j@& J943 @7b  `   U UU M  >Y@a`GFge`jaj[ZUTOLFSGS:;C(<+&#'5463>7654&'"757327&#'54?>/&+"";26/& $$ "32M " % P3 #( **iIl -%o *. n0 2!n**# !k" !!  2 ##)z F8o7Ga^e) 4@K.@/   UU U  U M>Y@!BA HFAKBK<;65)# 41  %#! +32654#"3254#"&47>54&'&4732632!"& $$ "32PJQ+,M.8*(Ny=|1vE13!ʴ6o8]a|DI0#'9Fbb.(bR0a'8o7Ga^e)*5 @-b UU UM>Y@,+20+5,5&%   +%"'&546323'.#"3267 $$ "32fqܤJt`sQh: ;Rʴ6o8]a^hXz Xc5D OH8o7Ga^e).:EvK&PX@( UU M =M>@& U UUM>Y@<;B@;E54&'.7 $$ "322V 'AuPiA@DsIM ;|8)(Nʴ6o8]av'"~:[fC,3f٬gQ%':Fbb.'8o7Ga^e)GS^@)<9@  <';K&PX@9U U  U S M = M  >@7   UU U  U S M  >Y@&UT[YT^U^ONIHBA;:752/&%$  GG +#;2676&+"&47>54&'&473!272'.+"326762"'. $$ "32.^#3F^]lBBv8*(Nv@S%  (@1]^L"""-hʴ6o8]aj*^T [':Fbb.'' *+-!;R#,K w8o7Ga^e)>JU@!$<;K&PX@8UUU  S M = M  >@6   UUUU  S M  >Y@LKRPKULUFE@?84%#7C+4&'.73!272'.+"326762"'.+&#".7> $$ "32N(N3S. 8F~^M!!"/&^(Ny=;|8)pʴ6o8]abb.'' RA!;BeE b.(';d8o7Ga^e)/;F@ @4b   UUU U M>Y@=@0   U US S M  >Y@YX_]XbYbSRMLEACCC+&#".7>=!&#".7>54&'.7327!54&'.7327 $$ "32X(N6>y8)(N648)(N3;|8){(N6~98)ʴ6o8]a.b.((:Eb.((:Ebb.((:Eb.((:n8o7Ga^e)#/:hK&PX@&USM =M>@$UUSM>Y@10750:1:CC +&#".7>54&'.7327 $$ "32R(NsC>y8)'N5;}8*lʴ6o8]a$b.(':Fbb.'(:x8o7Ga^e)#/: @/b   UU  U M>Y@ 10750:1:+*%$  ## +%24+"=707#"#"&5472 $$ "32`H- 1 F^;D?U1!Gʴ6o8]a33 ""3XZ;()=+8o7Ga^e)NZe5 @.  b   UUV M>Y@\[b`[e\eH=HG +4.'&47327276'&'&47327&#"54/&#&#"&47>5 $$ "32*884i,!O] &`5.d;R"&"GP.B#F!,l1;}8*ʴ6o8]a$E:'(:Ei'('( ((%)E;(':F8o7Ga^e)'3>r$@% UUSM>Y@54;94>5>:C0 +%!&47>54&'&47327;276 $$ "32 lQU!(Ny=>z8*!J0Oʴ6o8]a'*gbb.'(:EG! pT8o7Ga^e)#/Y@HB E U5 @+ U    U SM>Y@10SQLJGFDC@>970Y1Y32"C%+"'32&#"3267&#'54;265  $$ "5432654&#"=77#"326543 N1+DT酄UV4 # .ʴ6o8 1'%1 9'2m ` - #`ll`#  |8o7G! -'#H- ]e);GR@ @4  b   UIUS M>Y@IHOMHRIRCB)CG +&#"&47>5.'"757>54&'.7327#"'&" $$ "32/)8b;9cN(',w (Nb9;b8)%*!ʴ6o8]aE;('-c ' 3b.'(:E,$'98o7Ga^e) #.tK&PX@'UU M =M>@% UUUM>Y@%$+)$.%.  +"32654&#"&54632 $$ "32dwo,笥ʴ6o8]aа¯֦8o7Ga^e) 3?J@ 3@-   UUU S M>Y@A@GE@JAJ(cC"" +3254'"&#"&47>54&'&4732632#"' $$ "32-=9)'OlJKm8*'O3y!Su9>uN;,ʴ6o8]ao!*b.(':Fbb.'%?@$)SQ38o7Ga^e) ";@.#)*@-  U  UUUM>Y@751/-+(&""   +"32654& $$ "32327#"''"&54632dwoEʴ6o8]a=)96]H(-笥uа¯8o7Ga^; '\֦תxe) AMXʶ/.K1PX@/   UUU U M  >@4   UUUG U M  >YY@ONUSNXOXIHCB%#cC#"+4&#"326&#".7>54&'.7326323"/.+ $$ "32@TD(HkM =i4;|8)(N2X3%040.:p3$+Zʴ6o8]a3L?&ANb/(':Fbb.'\eMj&>{<# c%*#8o7Ga^e),8C@ @5 bb UU U M>Y@:9@>9C:C43.-,,(&!,& +'.#"#"&#"&'62'4&'&74632 $$ "32 a>?DPN*:T,mK~ (R__N3ʴ6o8]aWW LS?5/A%">O-j} 9 @I,Z|^j$8o7Ga^e)-9Dy@ "$@% UUSM>Y@;:A?:D;D77:C +&#"&47>54&+"'67;27'.+" $$ "32X(Ny<>z8* &(M6 `́ 8K+'fʴ6o8]ab.(':F/%:]VW \;'.8o7Ga^e)6BMjK&PX@'bVM =M>@%bUVM>Y@DCJHCMDM*I*F +4&'&4727#"&54&'&472732>5 $$ "32 'Ob:;b8)(Nv@88)Up-F- Nʴ6o8]ab.'(:Fb.'(:Fރ4(J*(8o7Ga^e)+7BmK&PX@(bU M =N>@&b UUN>Y@98?=8B9BI%C +76&'&4727#"&'.'.727 $$ "32BX?.H=2  *:U;HfAXʴ6o8]a?g?*((3B*'J(((!\8o7Ga^e)&2V@ML   @4   b   `U U= N>Y@SQKHDB?>:8'32C4+4&+"'57327&$ 37+">32 $$ "#"'&"#"'.+"'53254V{ " `6/SRJ$ ^rʴ6o8 P17j  ^2''e]a=2#ww! {N78o77BB!}B?[& ae)Q]hK&PX@0  b ` T  M = M>@.  b `   U T M>Y@_^ec^h_hKHCK +?6&'.7327&#"&47>&/&"&#"&47>?6/&'&47327 $$ "32j q3\9;D5G. 4:>UHFj!d q3\79F5F0 GS\;T_=Iʴ6o8]ao-'('9 I&(' )( +('$> i'(F8o7Ga^e);GRK&PX@/bU T M = M>@-b UU T M>Y@IHOMHRIRCB=<;:763/CF +6&'.7327&#"&47>576/.'&473273 $$ "32$LU@,H49#&Nv@>y8)'35_1>pD %}ʴ6o8]aK;,'(7=.Tc-('9FQ 'B/'(46d8o7Ga^e),8C@ '@5bb U U U M>Y@:9@>9C:C43.-+*$! ,, +!263237>76!"7477>&#'6743 $$ "32>T 5%7+*   ^,Qzʴ6o8]a,-71- O  z^8o7Ga^e*/;F@ , K(PX@1bU  U  M = M>@/b   UU  U M>YY@=< CAK&PX@5b`U U M = N>@3b` UU U N>YY@65<:5?6?#*$%2 +4&#'"5'672632#"''653254&#" $$ "32b+{9PG\ocZ7!eE+I2=ʴ6o8]a='$ 5Vw3ls1 'LPH=jzȶ8o7Ga^e)*5@ @/b UUM =M>Y@,+20+5,5&%   +2"'.#"32>7'"&546 $$ "32\i35 )%7^!/K%> 'Vy}+ʴ6o8]aR7;/#wsX3N!ywyb8o7Ga^e) 1=H @"<;K PX@7  b X U  M =M= N>KPX@8  b ` U  M =M= N>K&PX@6  b `U U  M = N>@4  b `   UU U N>YYY@?>EC>H?H4$$#" +5&#"326#"&5463254&#'"/672"'&5& $$ "32FD?7S}!ad(^m}pT:+{:!5m7  yʴ6o8]aPwl^?/:k5^=(% 5VE;# J/8o7Ga^e))4@8b   U S  UM= M>Y@$+*1/*4+4%$  +272#"'&'4632%4#" $$ "32mE )Fk;M}y\!/u+N5ʴ6o8]aO 5B4D{}Z3E$^>)8o7Ga^e)2>I@ ,'&@=bb  `   UUU M  >Y@@?FD?I@I:9433#""%"#!+#"=4;4632#"'&#"32#2&#'54?> $$ "32yV@X?/)%3Z y5+ o1 Eʴ6o8]ay7#"'7$5 ##8o7Ga^e)2CO[fK(PX@,%  3! <@, %  3! K(PX@F ZUI U  U   U  UM>K1PX@G ZU U  U  U   U  UM>@L ZU U  U  U I   U  UM>YYY@.]\ca\f]fWVQPLJGEC?><86/-+) 22 +"'&#"#"'3272632#"&47&747&54632672254'&#""#"4#"32> $$ "32 9VF+J%= y:5ˉTyfBFFVF/1A%)o+97H# #t32u#Vʴ6o8]a?BVc+?5/8`b?ALP-BJTf7)!)?fe% r9H 98o7Ga^e);GR@ :5 K&PX@9  b `U T M = M  >@7  b `  UU T M  >YY@IHOMHRIRCB=<9872$9"+632&#5437>=4#"2&#5437>54&#'"5'672 $$ "32\R N1 f1L R5}#+{:ʴ6o8]ah%% \ %% !='$ 58o7Ga^e)'3>K&PX@6b`U T  M = M>@4b`   UU T M>Y@54;94>5>*2 +2"&4&"&7>=4&4.#&7672 $$ "321##1#s-;==B*% }9|ʴ6o8]a'#1##19!< 78'8o7Ga^e) )5@K&PX@;b` U U  M == N>@;b``   U U U N>YY@ 76=;6@7@10+*'%#!   +2"&5464&#'"5'672"&5432265 $$ "329=#5# +{9)-=-P+'+"ʴ6o8]a9;"""{=(% 77Յq-3'3;uI8o7Ga^e)JVa@ <4/.@: b `b   UG T M>Y@XW^\WaXaRQLKIHFB;9303 +4&#'"/6726?>&'&'577&#'54/&#"&#"&7> $$ "32D+{:J33   =. X3N'CL1N\% 7=J^>B+zʴ6o8]a=(% 5V44   (( 7T 7-!)-o=d8o7Ga^e)*5 K&PX@-b``M =M>@+b``UM>YY@,+20+5,5J +4&'&#&7672&#"&47> $$ "32* jF5-VZ+5ʴ6o8]aF  5R/L!!%h8o7Ga^e)P\gƶ<#K&PX@. U  T M = M  >@,  U U  T M  >YY@^]db]g^gXWRQPOJIGD$B#&H+&#"&7>=4&'&76727632>32&#"&7>=4#"&"&7>=4&" $$ "326=B^>B+!5q5  N`n _0#@=U?>3s/G1==3-bjʴ6o8]a?;;$ Iqh&B=?ËT;?L?8o7Ga^e):FQyK&PX@, T  M =M= M>@*   U TM= M>Y@HGNLGQHQBA%B'H +&#"&7>=4&'&76727>32&#"&7>=4&#" $$ "326=B^>B+!5q5  %q9HG#@=U?>3-1!Zʴ6o8]a?<;# I5;Xd=@L@1ζ8o7Ga^e) "-xK&PX@)U M =M=M>@' UUM=M>Y@$#*(#-$-  +2#"&5464&#"326 $$ "32ywZA9J`D1I-ʴ6o8]a{mjyylb8o7Ga^e)'2>I@ 2('K(PX@.   UUU T M>@5  b   UUU T M>YY@@?FD?I@I:9#$$'2 +&"&7>54&'&76727>32#"&'52654#" $$ "32-;==B* 6m7   f+Zd`!Zt-Li'p ʴ6o8]aB9"<;# J '>uhwi\oV)Ӷ8o7Ga^e))5@@ <);K PX@. ZUV M = M>K&PX@/  bUV M = M>K(PX@-  b UUV M>@4b  b UUV M>YYY@76=;6@7@$(9""" +&"6632&#54?675#"&54632 $$ "32HD=:t % L1 3B;h{o`/lʴ6o8]a%PI!'%37so6N8o7Ga^e).:EK&PX@2Z T  M = M= M>K2PX@0Z   U T M= M>@1b   U T M= M>YY@ <;B@;E=4&'&76727632#".'& $$ "32X#--;==B* 6m7  ;]%&%   Tʴ6o8]a%@9"<;# Jq-, 8o7Ga^e)*6A)@6b   UU UM= M>Y@87><7A8A!-(# +332654'.4632."#"&#"& $$ "329-*+9)9wTIrNo70NR5%533DNT+!`{ʴ6o8]aL)%''N1#AX#P;+'%%-8$9MH38o7Ga^e))4@@' U U U M>Y@+*1/*4+4%$%#"#" +727#"5#"574326=732# $$ "32 +J!!FmfR7+N &ʴ6o8]as"H}w1FK%8o7Ga^e)3?Jt@%   U V= M>Y@A@GE@JAJ'(%& +4&'&472722754&'&47272"'&54#"7 $$ "32'/^B g7J/^B !5m7  [4ʴ6o8]aF+#h,ĉVF+#h,;# 5!;ݶ8o7Ga^e),8CKPX@+b`M ==N>K&PX@-b``M =N>@+b``UN>YY@:9@>9C:CI)C +&47327#"'&'&'.7327?6& $$ "32T^9) $^)^/V`9ʴ6o8]a)##!'!|J ##+1,)1+8o7Ga^e)HT_@ /@'b  ` U N>Y@VU\ZU_V_POJI;987H$*3 +&4727#"' #"'.'&4727?'&'&'.727?6& $$ "32^9( lx "%^)^/VI^)^/ Laʴ6o8]a##!'! !{J##+1,)-L ##+1,)1+Ѷ8o7Ga^e)JVa:@+   U SK= M  >Y@XW^\WaXaRQLKCOCC+&#"&4726&'&'&#".7>767'.'.732776&'&47327 $$ "32kk')-J)BI7 R +J-I)!2es**JHI+ IT+J*J*'ʴ6o8]a8!!Vg!!;w<!!tt!!8o7Ga^e)=IT@=87/K&PX@5b U M =K = N  >K-PX@3b   U UK = N  >@1b   U U U N  >YYY@KJQOJTKTED?><9("$&##8!+#"674+"=7307#"#"&54632327674'.#"'5737 $$ "3236 T/X 5';7+?)+ &9(ʴ6o8]aw  9 !! Nr7-#%o0e- !I8o7Ga^e)#/:@"!@/b UUM =M>Y@10750:1:+*%$ ## +326?2326?2!"547#"'657 $$ "32w0ݗ70 l +N xʴ6o8]as (R!3#^N fo;n8o7Ga^e)!-8zK&PX@(U U M =M>@& UU UM>Y@/.53.8/8)(#"!! +"72>76'4'&"'.54632 $$ "32,-%3M(O86?haB[3QTʴ6o8]a Mu ;2SA86kVwo\,޶8o7Ga^d+$@@ >"?#@# S   U M>Y@ &% <;8630.+%@&@  $ $ +  2+"6754'#"&5#6!2+"6754'#"&5#61ut6 %J{M 9!oB| %JyM 7 mE+stR#3!+ ++ +!D/7!#3!+ ++ +!D/7!d+,H@FGK(PX@0bI  U M = N>@.b UI  U N>YY@.-DC@>;863-H.H'$& +  2654&5472!67'+47>54&#"%2+"6754'#"&5#61ut%' n%H>K 13D\[Zmn %J{N :!mF+st!;K(PX@?  b  Z U   U V M =N>@=  b  Z U U   U VN>YY@;:QPMKHEC@:U;U#&)$"%)+  632#"'&#"32654&'674&#"327>322+"6754'#"&5#61utRF!!@?j1J!y=}f>h_ORs)1H'T #H{M 9!hF+stR=N 1@5\L%-iR\9R9@;>;#%R#3!+ ++ +!B17!d+0L@J KK(PX@9  b  `  I UM = M>@7  b  ` S  I U M>YY@21HGDB?<:71L2L0/#23%+  4#"#>7'!+"6354'#"&=37#2+"6754'#"&5#61ut!cj)=' L1 !ko %JyM 7 mE+st{+@f +) dH#3!+ ++ +!D/7!d+(D@!B C %$@E  Z  b  ZS   U UK=N>Y@*)@?<:742/)D*D#$$"&+  #"'&#"32654&#"7!57!62%2+"6754'#"&5#61ut>A/'>hyn?& + 5@ %J{N :!mF+st`HZ'!-^ZD#3!+ ++ +!D/7!d+(D@BCK(PX@7b  Z  U U M =N>@5b  Z U  U UN>YY@*)@?<:742/)D*D&&$% +  "&=6364.#"6%53262+"6754'#"&5#61utk5T=8r,V7L;/qc1^on+ %JyM 7 mE+stTgt'/'>/#@/)RuB#3!+ ++ +!D/7!d+4@ 23@, ZU  U =N>Y@ 0/,*'$"44   +  33!"5#>2+"6754'#"&5#61ut#̓mF\+$-# %JyM 7!mF+st%;Q 9H)-#3!+ ++ +!D/7!d+*5Q@OP+)K(PX@0Z  U U M =N>@.Z U  U UN>YY@76 MLIGDA?<6Q7Q1/$"   +  %2'&5464&#"32654/6#"&546%2+"6754'#"&5#61utwh4)Z;sHTxpDdV{}dP;\-VR7 %JyM 7 mE+st\+L1F+-\9PTDb5 %i3ZNy?^8+Jq/`w>=/i#3!+ ++ +!D/7!d+%A@?@0K$PX@2b  U M =M=N>K(PX@0bU  U M =N>@.b UU  UN>YYY@'&=<9741/,&A'A)%$% +  472#'327>54&#"%2+"6754'#"&5#61uth7U=:}s'`CJ@3ݝamn %JyN 9 mF+stgt'/6I2@/)򒏁#3!+ ++ +!D/7!d+#H87 K(PX@1   b  I U M = M>@5   b Z U  I UM>YY@"%$ DB;954-+$H%H##   +  2#"4>2654&#"2654&5472!67'+47>54&#"1utD=9+;'ffh' n%H>K/3F\[Zmo"+stdF`c39Ln:ŏ!;  b X  U S   U M =M>K*PX@?  b `  U S   U M =M>@=  b `  U  U S   UM>YY@87+*IGCB=<7A8A10*6+6$C+&'".7>54#"&'672 $$ " $5"54  3254W&Bx1uE/!HCDFѰ0f0~RUM38G#$FE N)_C~BҰ.~9Q?C4Y\.:GR]@=b  U UU S   UM>Y@IH<;ZXTSNMHRIRBA;GyY % 7&d6SYb@(;< (DFѰ0f0~RUM38V=tY82A Tj:jTXdoNC.uC~BҰ.~9Q?C4Y\;GT_j@ 65@Lb``  U  UUU  V M>Y@,VUIHgea`[ZU_V_ONHTITCB=<42-+"  ;; +"#"&54>32#"&5463232>54"'>54& $$ " $5"54  3254%M.0_=T/&7GCfp/5%/&92L! [Xr/!  &R8& j366ܽC~BҰ.~9Q?C4Y\.:GR]϶+ @C  b   U UUT U M  >Y@1IH<;ZXTSNMHRIRBA;G=#"'7673 $$ " $5"54  32544\ SM5AM$ &JDFѰ0f0~RUM38~ nn2#  iC~BҰ.~9Q?C4Y\+7DOZ@  @Eb  U  UUUU  V M>Y@FE98WUQPKJEOFO?>8D9D($$#$#"+4&#"327"'632#"&54632326 $$ " $5"54  3254L?LL(D>H| K2&g1Eaup7s    7MBDFѰ0f0~RUM384]YY Q [r7#& qC~BҰ.~9Q?C4Y\ ".;FQ@ ! @5  U  UU U  UM>Y@=<0/NLHGBAE6989rhrk(HJ-pdDFѰ0f0~RUM381-ib^%HBmV\:{Vt0 M#C~BҰ.~9Q?C4Y\#0;F@@>  b  b  U  U U  UM>Y@&21%$CA=<761;2;+*$0%0  +"'6743;27'6 $$ " $5"54  3254 %  0-R [DFѰ0f0~RUM38 0-+ d  C~BҰ.~9Q?C4Y\ !-9FQ\@ ( @7 U  U U  U  UM>Y@*HG;:#"YWSRMLGQHQA@:F;F54/."-#-    +"654&#"&54?'&'46322654/ $$ " $5"54  32541@X#f=CXrg\n?RfmVR^3[i5?8XDFѰ0f0~RUM38@+B9L=+FqLX/9cLz^VP9B FVBYNDLX=!+d#FN{C~BҰ.~9Q?C4Y\ ".;FQ@ ! @5  U  UU U  UM>Y@=<0/NLHGBAF5:99rhsk(HJ,odUDFѰ0f0~RUM380-ib^%HBnV\;zVt0 L.C~BҰ.~9Q?C4Y\.?KXcnz@ K&PX@EbUUU  U M = M  >K*PX@LbbUUU  U M = M  >@Jbb   UUUU  U M  >YYY@/ZYML0/kied_^YcZcSRLXMXGFA@86/?0?&$..$C+&'"&47>54#"&'672%"32>7674'&"'.54632 $$ " $5"54  3254%Cx1vE/!HDN((| . F$G218^X:R.IL;DFѰ0f0~RUM38G#$FE N)Fi 5-J;C21`NldR(C~BҰ.~9Q?C4d+K&PX@!M =M=M>K(PX@UM =M>@UUM>YY@    +  2#"26&#"1utD=9+fgj+stdF`c3-Tŏq"+!!pqq"+!%!!p+FZJq"+%!^J'ZLq"+% !F^{3%"+&2"+7 {3$Jtu/ZHo"+!Z1oZLq"+ %!uu/={4Z"+_Z"+ L{37E_;I&"+ ;non;I&"+  no nd!   "+4632#"&    6mzVX{zYVzsaX{{XV{{tsnao"+ i!;=d! "+    6sasnad!# @  "+   32 &546   654s?'su梠ٟіd!"+  ssd!  "+  26&#s!sab  "+  %"3{ssb  "+   65bsar߰d!  "+  4& !sys!d! "+  " 65!s!aVsnd! "+   6&#$'spa݅saVdB"+"3Bs"+2#sdj "+4632#"&732654&#"dfhsj>uOVqwPLxhmdoTtvLTuuP  "+%!!%uo   "+432#"  654462"X+=32&547'"&#"632#"32#"'32673&547#"&%#"'3>54&'27&#"#"&' >32737%%'&'."72654&"&547632>32#"&#3>54&'&'&'&#" 32>7676767654%#63232632#"&'"'&5472654'"'&=#"/#"&54;."32>54'62&7#"'>324>7#"'674#"326767#"'&54632763262654'''3%5Xb:ChJ1+Ld4R/7CRP#)=P6?5%)b`J-1L}9 590lAXo57B;=:Ed^^{H{Hyfm{q7Re/SO/s<TsuR)V=>5fXXIB%"R`3F5%AFF !''!#3' )ed>F=R%^E=B9f1N?=;-m !''!LD=)57( sE;ZN-'65X!-%6 5  #\w!'Ps!)J+13) \' 9K1-3/-91>N,%7R#%# ?w-/JbPD')f B1-;-\ !/!;+HN}y%&A? :W4RbN)+u98-)nDlsF/ B!2-Ei?m+ RH+-I\ Bf=715fh~%pF7# =\!5+)! 9m-3XK~'D09D^;ͤ \#e TXD2,99kd#-+5%u&!>9# 4PDT 9+ '%9;0= !!?;7o!$ +/C6/? --% B+#l#'#- +!!#1^0L D6%"+6432##"'.'&547327>32327+%5&54675%632&'"'ph]5L+Dυ?ZJ^eXZLt. H/-<)=F>97=0)-;=BmZk$1H)$5#Th/ )1X*.Taoy@~wqfb^V>4.,"+26323 +#"'5#".#%72654&#"#237&547#"7#"'5327'74#"3262654#4!"3 "32$54'#"&VH9=jJw^%F3fR')%^DZ1RCI7|GDa>N q7$HK\3YSb#!4"#PJ'.5m';Nb5Ru!?KN5L;_)6%%!?#!5HZ:+1+eGPR}+1+L y =Pn#3gMA)/  Vh`F1Hg5#+-1Fm- ` "+32654.#""4#"#"=27632#"'6?654&=737+";2&#'54;254/;2&#'54;2=;2&#'54;2?675&'%&+"7577-'{'NC50-gR:ہLmH1-/)-Yt"#e `)T5Nq>NGJ1N1s'g`%;`Tj- 'RR % 7ZX0 %  H J  BTTP##Pk}'  J H  ; (0 @ /+&! "+ 5322654#"332&4632#"$264&"a\{%j<\X!/!11!-B//B!\Xx:Nv:A--A/hB//B-}'/9TX@VUI:72.*$ "+ 5467&'73'7>732  6462"64632#"232'"'#"&5''>7'͜0P3(5/ / # 2G)) V! $?%G#6E)  ! t-> e=)HH%---)'9F#V '#T^X}&.8JO@LKH@61-)#"+ 5467&'673637267  6462"64632#"27#"&5'&'77'#ǘ3+/ /5)3R/G))yoC)0Y?%OB,!E9  #;b3>-v 'HH%---)hVBF#Dp9;4TO}4>F@ EA<7- "+%7"&'32?67' 5467&'67326373673264&#"264&"5 ;F ,=S%?Z1)CXǙ5+/05)3R0o  )!!)I;9oD#FDWh #;b3=-ϲw 'h----R 1 "+  654#5&''7&'#5367'7675373#'J+Rdu533##'#53+dхsdgfb>fk`sٰ`͏Β97Rmm9V69{^^wf "+%&54323##'#53  654ٰ`+^^͏Βs "+#"54?#53733# 654& uٰ`'V__͐ϑw# "+  6 5432'>7'&'+fP4=Ƈ+ZZDjwϒ^%E-TTF7d8%"+"#"&54632!257'4'"=46;27>54J?')%-mE e-y }{  POX_k7++-%^3 7 #@%+ !%ŃP0"+732+>32'&5476'4&#"'#"546;b/%+LuiD9Fu_Lb7iyy29V{wNh<hbɜDj?%%"PFu O="+%32654&#"4&+"=73737337#";2&###"&547##'54;25=1+>:1/=1>H9'B':=1nJ:BOTP9o7DD7)DD/+'Xd'+/Z'>W=XbdR%m%Z;?-4"+#'#53'5$4'"'254"' &"$27&#"3wc!9D65BD HB-7G3^^]';BiLR%RL!'dB;^d} "+%''%24&#57>/d ^4˿)Vh)Rh' J +fheH#P.."+4 #"'&546323>32'654&#" F@ncHZV-nT{qyJ #7RjV}T/kTXIi}dùd罢\j|G@d 5/"+ 654&#"./&#532276?673#"#"546DۢߢL`:-Vj:@BAFEH;B9jW-7^P{ٚqۚ٠T!\iRalq)++-mlaRda!Dޖ$ǖdZ"+%&'5 %$ 56%632#"'}up`ՒfehcSxuVL +cJJc-(`NN`( d;N ,@ @ 3-&! "+2654&#""&54676! $!""32654&'2! %'7!27&546\}X\y`i1Z%ېŰ\}]X}U'X?ېǽ'XZ}{Z^}R`E'F<`ZXy^\T+D;bPh <5"+"2654&3267#"&547>&#"#"&54632&54$32V{{{wF3?e?-uPA׏-B`Ʌ3#h}Z^{{\ZJU+;'kV#{uPurŷdo?1"+%64''4''4'?6?6?6?&''67&'4'P>\5{1}=HZ0?5X-Dy!pq++-+;{6?#Ejv 19q4@%RF@%FT^%qs;Bq1{S#^yHV=C+LJKVhu#/($ "+"5463!&74632!2#!7654&#""5463!2#!Cm鞚oX%" fh} !!%#3qaѕho#8f;jkHf#0#7Z7"+?6?6?32=7'73254/&54''4''4ZX/?3UN)e^)3 !)J343{:^^^Zww}1'41 #JHc@%TH@%7Ff"+&547'''7"'772 -V5RPhR-wpsbTmPs%RRgR)V%yZI0"+$4&#"2'4'.'7?>32#"'+532>7. '.[Is`=}XTP!#3?F{7.D;M7eyo%ZJb7+k3PZP3.  ɰ}jh3276727672'&""/&"#"/&#"^   Z    5\   Z      7     7  !!"+&5#'#5373?3#fuFX!LTTu3ƅ :^) ^L}""+"!5674&#"#"&77#".h?dh%2+iJXhdm^Jf+/u++?@>K>ouju>K>qA!"+%!5674#"#"&54>323254.5463232632#".#"o/1`#X\s)dL%FVEGTHKRs1v;eD51  L{ ++w\_yBzkg#75oG`gHp57iDhV'y'67'7P "+2'&'&5463232>Ry7HTLPqVD--FŔJ}\NuȞ54'&'#"&ZZ}9RTXX}tJFD9bkjo1-c?:b^#nAT!PTfCZP"+ #"&546327#"&54632\bkXy=R9bkXy=3@hC;m1\jhC54&#3>32'>L1%f\7^NVo#@7d)D\NkO< {b "+53%#3GG54&'7#" 67"'"&54>7>2'7;/#"&#"327"&54632#"5'>'#"54>767>54#"76727254#";&BW)#)A#BC5HK%lB͐P9N!D?\PN}r8-- , 1  #-`9dVEBQ)&1% +%6Q&-F=7oA ,5Td _? )-\$L7BXZPPiC?_5)   FJNJN?)BBEAKZB+T 1  ;J'5 C D9w   1 6&"+"32654&.54632>72#"54.'.563/HH/+LBLJbqRLtdH3k l#u+)-5h!-!uLfLG8;DmNPplTPf 扮Pg!M-m))h1N&,"@!w'f)6DL @ IE>8/*"+%&54326323##'#53'&'3##'#53"327&7&72654&#"654œvxٰ`z]_x`9ɕqZheYYq˔nYeKKKZZ^^HH^^͏>F|=?Β>~{_x{a`|xwy%4>M[@ UNKC:5+&"+%#"&'#"5432'>767'&'";&5467.>54762'67&'326&#0 P4=Ƈ+C+ZZDjxxɕ)mnM P3=6'5ߓK'o˓m^%E-TT4j F7ϒ )]t"u!t^%F zFL!*`xw.>MX @ SNEA4/% "+#"'3##'#53'&5432632'>7'&'%"327.5467&26&#">54&hZQ;C``TU`P3>LJ+ZZDjwӓɕ%"LWRI""˓! KRTN[[OO[\! __ ##^%E-USF7͐>gf?JAadE/./^]}'1,(""+&5432'>7'&'3##'#53" 654&J9<ˆ-17\Fj{a:+Z)G-TT͑C7ͅ``ӑw+ "+  6 54327'7'>7'&''+fP47)=Ƈ+ZZD)1jwϒdVVX^%E-TT[V\b7)<FA="+3##'#53'&547''7''7'7632/>7'&7%" 654&`~J@X%'΢'М@PohX'Ϙ%%V9+``SH㲝!#/T-ۋHTF:i-T/!!ӑJ "+ 6&   #yH#yJ #/ @ *%! "+327&7&#"32632#"''"6326&#"ӑSLKTppppqpqKSӑSKH&q&yGGFGkkk/*&#&JP#+ *&""+3>32#"&'##"32 6&  6& f>yy>f>xw?##ewwe'bsytbc##J 7' &" "+ 6& 32!6$32#"$'!#" 6& #5#H#y娨\#{?"+232632'"&/4632>8I'1BJd %? J#YYA )fZJ+"+22##"'".'#"&5#"&547.54>7&54632>@+- BX^9g%J/!j+  & -\% Z@RP1#)1Fiy5LR !93X#h5< =8'  m3;A )`4"+%+"&5467""&54234.54;2>32#".#wH qq- Y_ -H }- [^ c!Fw- yE/Pck#Azie_WG"+267#"54654'#"'254'727#"54326?67&'.#"5432&#"674&#672472.#654&%&#"32632 #"'.'&'.54767#57'5&'4>767672>7&#"27"547&'#"&546323&54>7#6727 &"&'.'3&75"&#"#"&5463267&5432'#"&=632632&54632+27654&#""&54654'672#"'654&546323254'&#"32#"547#"'#"'5465''3%5Xb9hJ1+Ld3R/7CRP#)=P6?5%)c`J-1L}-9 590lAX57B;=:E^^{HHyfm{q7Re/SO/s<TsuR)V>=5fXXIB%#Ra3E5%AFF !''!#3' )ed>F=R%^==B9f1N?=;-m !''!LD=)57( sE;ZM-'65X -$6 5  #\w!&Ps!)J+03) \' 9J1-3 /-91=N-$7R#%# ?!u-/IbPC')f B1-;+\!/!;+HN}y%'B? :W5RaN)+t97-')oCF/B!2-Eh@l, QG+-J\ Be>716fh}%F7# =\"6+( 9m- 3XK~'D19D^;;4!\#d!TXD2,99kd"-+6%u'!=9# 4PDS 9+ '%9;0= " !?;7o!$ +/D5/? --% B+#l$'"- +!!#d+$"+  2+"6354'#"&5#61utc %J{N 9#XV+stZ#5#( ++ )"9:5d+-'"+  2654&5472!67'+47>54&#"1ut(l%J>N1/H\[Zmp#+st!;321utPF!#@?j1H!!w?}f>f]O=e31H'R+stR=N 1@5\L%-iR\;P9@#1-;#%d+0""+  4#"#>7'!+"6354'#"&=37#1ut(bk)>' N1 !kq+st{+@f +) dHd+(""+  #"'&#"32654&#"7!57!621ut=B/'=hynB! - 1D+st`HZ'!-^ZDd+) & "+  "&5476364.#"6%53261utk5V9<r+V8L;/qb2^mp+stTgt /'>/#@/)RuBd+ "+  33!"5#>1utT̓lE\+%+#+st%;Q 9J'd+*5 @ /+" "+  %2'&5464&#"32654/6#"&5461uth4)Z;sHTxpDdV{}dP;\/VT+st\+L1F+-\9PTDb5 %i3ZNy?^8+Jq/`w>=/id+' $ "+  472#'327>54&#"1ut)i5V=:}r'^FJ?3qe1`mn+stgt'/6I2@/)RuBd+$.9 @ 3/*%"+  2+"6754'#"&5#6%2#"26&#"1ut %JyM 7 mE1D=5Pdej+stR#3!+ ++ +!D/7!Zd-Tŏ)"+3!!5!Ph'J'\\fX  "+#!!!u-qjX[f "+3#%!!5!!uu\n-jX[jJo"+'&/7672 hG #^߁CC!^# H# (NM'm"+7'&'63"'6hH $^!CC^# H ٸTMNT-7@2Z  US K  =K >Y@77430,)(%$V3+!!!;2676&#!"&47>5#535#5354&'&47327)7>## !7Lj s??s LR s?oPPNA1 <{RPP{;2  1 ;{,P@M) < bS = K  =K >,,('#"C+#3#&#"&47>5#535#534&'&76724`]V `4)[ ioPP{; 11 <{RPP q1&*R-B@ < $ . @4  b  ZU  U UL >Y@BA?=98V4$$C" +>3254&'&4732732672#"';2676&#!"&47>5&'""LZ?s LR s?5+5w jL#&27>## !7Lj s?7}#7h{;2  1 ;{!DF dwNA1 <{TB7;@ &@0Z  SUM = K >Y@;:98541-*)%#$" +#32654&#"'4&'&4732632#"'&#"&47>5#53b%w?s d)'^'1hDžf@N ZR s?P A{;2 Eqh6HOy; 11 <{P7?ҵ1 K-PX@4  bI  UQ M = M  >@5  bU  UQ M = M  >YY@?>:854*('$!   +"326532762#"&'.54&'&47326323#"'.#-bF$%-1D*1NX/B/?s G#3yAm_/V? 9p`w+;%B3!1=)9\{;2 %=fRNPd)%J6?Eb@_&#A@>07 2  <db`eM=N= M>:8$(#()" +%##"'#7&546?&"#"7463273327'"&%2765X)op>RG`TD`8)HLXT% %'NuF9Fn(b Vm|-f'++ /JVOOx+1:!(L= [ F1514e@b2+(<dbeG M =M>43.,*)'%#"! 11 +34.54767672373'#327#"'##"=47#X^-$"" 6T71'^JyTy %oNf RO`b)dY:! )m+O@?:*$K@*   U  SCM >Y@&MLJHDA>;8521/,)('%" OO +!#'54;2'!;2&#'54;254+"=730%+"!4&+"'573%+"32#.5qc3=6P5qq5P6q3=5P5q/%9 7'Z/+''ZZ''ZZ/+''Z)<)y' =@2 9$@,  UD = M =N >Y@<:874310334%2!" +%32#+54;2654&#";2&#'54;2654&#"5'27673632}+9 3;'1'EZ\'1'P'1'!D %h=0-ݜ-6+)+/yT/+''+/TA 3I#+Sz@72S&NG @  U C M  >Y@MKIHFD:9393#3332 +%;2&#'54;254+"=730%+"67654&+"'5737#2#.+'4'&'3>5R7oo7P5q6G7'+R HrFPP)9 7o1)?F/+''ZZ''Z.Ch7 ''2AJ!\)oZ.C %N^@[ 3.#D<bb  D=K= N  >LJFECA33:C" +%'&#"&#"&47>54&'&7672>7654#"=737#"2#.+5&7.4`]V `4)[ iH{\); bH)u' ?:; 7 1V:3J{; 11 <{q1&*R1 ge)'!''9) \)5JX/STV#Y @eUM >Y@ ## +!27!2#.#!"54764#!"'D1/397q'do5)5-+A @yT ^%B$B@?<M=M =M> $$ +%2#.#!"747!"'3!632# 7 3T;H7O5?1N #+P)7HZ- Pw 8  57!B-GG@:b  `  `b  UQ =>Y@EDA@>=G%$$ +%#"&5463232>.'#'"'#&#"&47>76&'&4727%3Ef-lA -( 44  XN1_{F5{T< R (m )   d/:X=19#%(TQUIX'`T 11 PbLq.2  1 6WD=KPX@"K =M=M >KPX@ UK =M >@UQK >YY@;9/.*&  +!36&>.'&4727".'&4727"!267o 1oLj NZ%/-CZ@54 [u!v%   1 2 3X)/2  1 2   P1AB@?7-+*( <M=K=>322A3A"!11 +27654'#"=37#'.''67&546">54&=IT!%7! mC !#%Fu!!!\\)-Z#3%# /9'd**mf1-) +)>-))YS=$;$mONn5G:H5!"$j+;@61 $@' UUM=K >Y@ ;833333#&4! +!26?4732#"/&#!;2&#'54;254+"=73%+"u!3) IK V3=6P5qq5P6pV+/= B@Z/+''ZZ'';s>J@G 72 <UUM=  K ><9632$'%"3#1 +4+"'57307#"326?6732#"/.+;2&#'54;265X'H'X1,  IK -2X'Q'1'Z''Z+1; '?/+Z''+/L*=@eM>Y&$% +%6765#"4>7674>32#".Nq3ZD-)nH%s`R{;NoGeXZ2+mɇRyswqIGo^  "+7%"^\ ' J  "+#"'%&5467 \Q (^ "+462"7%"/3F33F\ /F33F3\' F "+462"#"'%&5467F3E44EZ \/F33F3\Q ()'"+462"2#"&#"#"&54323254&5463F33FwHZ+B+L8\JFb3P%D8^/F33F3/-+:J'+FS;!-;A!'=Z)1"+2#"&#"#"&54323254&546HZ+B+L8\JFb3P%D8^/-+:J'+FS;!-;A!'=Z)1"+272632#"&54654#"#"546L^8D%P3bFJ\8L+B+ZZ='!B;-!;TE+'J:+-/7#  "+'67%>7'67%67NfD-L##3-fJ/J=!'R3  DC1 1V+D,4@ :53/!"+%4'&54632#"&'.5476767>5632>"&462>767 1!Th5Z >!"< zT -.9i?Z@@Z,m8##PZL)10.+`7$#@*) [EN0K(PX@-  S U K = K  >@0 U  SG U K  >YY@$a_UTPLCBA@=<9521,+($!  +!#&#".7>7!&#"&47>54&'&47327!27&#"&47>&'!"j 1nLj  NZ$~v@r PR s??s GV s?1-D@53 Zu!wP% D- ^  11 4X){; 11 <{5{;2  1 ;{)l/1  11   [7e@72  _a$<   bb  S M=  K =M=K =M>db^\VTQOGE?><9634332"$+#32765## 547!;2&#'54;2654+"'5737#"!6?65#"#"7463 327'"&LGFn)op3X'Q'1'X'H'Xh D`8)H5 %'NuF9+ ;7[[m V}Z\''-/JZ''Z+ ++ /JV+1:!(L=T"+462"2#"'.'.54T/F//FB1  7F//F/21'Ii)?s= ;'517- @:d[ +%#5%#7yۚrB`7+ @9d[ +%53%3%?Ѯud!b@!b, WT @Jb  `  `  `b=M=M=  L  >Y@`^][QPMI%'!%% +2654'&#"4.'&7672767"&#"7632'"'."&#"&47>5'&'43232?=Rzk1FB-8 i38! 4 l˔wPB  8p H9`3h94 4 N{.$4٠k1wBA&*R?$C! P!nw{: 11 <{1!F%"C %:\t7AP {miG;94$  "+#"542'4#"632723&#"0432&'&74327654&#"6274&'&654&'32>7632&542674&#"2#"'32?654'.#"2772>767>54.'&'.#"#"'&'&'"327654'&'.'#"467'#"3274&5432654'.5.'#"'&"#"'&'.54654&5476727>7&'476767>7654&5322#"'& g   8!) !7&)*#J'/7% S 1'.!' MqC=7T%;T3 !u /;Bn'G>!T#O3  [+= -aQh#;,$F K4!1'5 *B@I!$J>)-'ol75(5R=;< %-'s?'L/o(Z2'-+)-*  e!J\A  71LT  >+I  f1-  - X'9) 5`!' - PBTb s75_ 3 R F-+ -)14')dP3#A=' CC# /P `BF " G ^'+]F!Hh !*32A`/ J 'B%B!=$ 71%&1)/!)%1- q 3DX +.-\!'=;;?\7G( 7- GZ 2    (E0'W! sOV1lP/:\T;^D1$0!  ?Lq`nfa2"+"&'".547>32#"3274>763232654&#">32#"'#"&54732654'&#"632'2654&#"-9g`9/~!+1JTCoP+݋V1;e5+!+!n>Xdm{s/!?P 4&'1{޼eǕMqlhdconfD"+2#"5463232654&'#",'&#"#"&54632.'47632.#"7267&'&5464&#"6!IgAV?++B- N%=H533/1+3 L7TbTsj'Xg-ˁq)-5XuL-%'A\nL5ׇub^/%D0 $Cd:;-1V/a!\1Jm jF{ ))-3RTXb٦>5)T^w{PhNB#" 7-'i+JG\ @TjX{EoJ)Jq_lg`-"+".54632&'&'"32>7#"&547"'>7.4632&#"3276323254&#".54632#"''27654'&#"?jߖ{1''3 OB#3qzjg[=m '$+6e;1V͇+RlDTG1+!1iP-ޡy`;lJ8`ZP^7mV!78 +#npaqEH1XP5318ZGq=J##5^v6RvTk#wHd=NL{H "+!5!5!5eHRRPPPPNH "+!!!!!!N5bb5HRPP1T9"+2"#!"$#52$331ofn)BiHPH9  ## "1wD"+2"#!"$#52$3 5`R>ЍD')))y1^)N se/"+4632 3 ,323 $! ! ,32 32,32# #",#" # ,# !",!"! $%&"!"&463 $3 3 $32! $3 32$>32!"$#"! $#"!"$ ,32 ! ,32#"'.#"! ,#" ! %.54632 y! q "~h1#]-5!Xoi)T}J!  !7'lu+@+?GL !x8˶ s TǑf!i 3FH3JH-8-Ѱ^^GXHDTC! ooHXH:G:YVGH-5-n . }`'!XCmdeedFE-N ;2edde> #P+< "+327#"7!''54?>54&/&=73%;26=732#'HJ$u>B&3<7P5=43-ZVu/ydX9%' 6-)'',+'/7V`') ) f ;CV (0N;AM;/+$fLFA<  "+%327#"5!''54;2654&+"'5737#";26=732#3274&#"#"&54632#!3236722&#'54?>=4&#"2&#'54?>54/672$462"2&#'54?>54/6724/672362#"'&'"2&#'543763254&#"#"&54632!32%32654&#"'4&#'"5'672632#"'"'67462"2&#'5437>54/672'FE#u}9='193L3j1+VQo-<&r Lzsu[k;WXfH72 N1p -LN J/y 1s-D -##-N1 L1{ 3{)4w+H3 *3! \-y1;'qL{stZj7632#"&'&5432#" !))!1#  % %$ '  #{#''}%'  '  #+%  %'1 )%#';!#1#)   %9# !=)%)FT )NT +L' #% %@A # #'F/'3 -(#"+6732654&#""'&7>32#"&"54 "3254FrfmuHRS 2R?N\\$'frJNXH }NVQ!D-)F/ )=E @ B>:5 "+%"54 >54#"632#.#"67#"'&54767'327327\\jVaՀntEN?Y7 KP=, kB@{VoJr^k$ՋvvrV*)/VQ!WHw): J`TN$[$N"H\}RG}FX;WqF/?IU @ OJE@*" "+#"&'&54632#.#"327#"&'&54632#.#"327"54 "3254-X70^A_#8   01*;tS$UX70^A_#7   01*;uS$)\\$'(8$ Fba E*9(U_9(8$ Fba E*9(U_9?VQ!D-)o #/ /%"+"&'&"''7&327'32>764''FC)H'=zV;AI_O1@; N}6i)74#)2vu"#RV"#d:v ɪCӌ #P<&чo""+"2>76'&"'&532'FC))74#w'=zVT_렖dN}2v #P<&e#RVS #dɪCR"+%&#"&47>54"&'$72@s-T wP)7{r {; 11 @ww3'H /jL/"+4632!2676&#!46?6'4&#"#"&}Ѡ>GHϘm7>'-^B]lFfdC!1fJTEǘ`VobsN/ %3.ZN< "+""&54>32#"&5463232>5!"'>54&%MPP!%'RhO@`xsf71' !!KX:?# bL;j.P\=PAaV9O:.5 ?`BW]\F-'"+)2+3&#"&47676'5!"'637F' +-  \on_?=B!.!' 11 0V#f;+ "+4&#"327#"'632#"&54632326jFwf{ }ZF)Tu^072^1N  !ݜ_;-B  bf$  "+32654.#"'672#".576%qPj1gGb5aañĶD{LuRN򲨠-\b@,)eѓQ'GPL "+"'7433!27'D'8?"'5)RL!5HRM G>Rb^ $0 +%!"+"654&#"&54767'&546322654/Xd7X@Y^պwG^4^asWcI9e9'yzNiX/X`ݥi>2!qyil:LD}{`d$  "+6'4&#"32"&5432&76pPk1fHb5aaòŶD{KVRN򲨠-\c?,)dѓR&G 3("+73632'#"!!!!32672'"'!73&54753-$Z(f4ppW!0/p#ǃ0%5w 3ZB,ZmZ=\ \*(?_)"+%&#".7>=!"=4;5!"=4;'.#"=7327#" 6'4#"=7327#"!2'#!2+u?s9B s? 1 1):$%-19 1uNA %333{; 11 <{+ +"B;/- !X)!/- '3/ - + % s="+#"&54632327676'.'&4732776&'.7327Ru)7/+ #F 16*5C RTP $) <=  "!f `<+1!5;dXF{b1 1  2  #V:81% 1  2 {fJ ;, "+32765"&'##"&546?65'"#"74632327TQFHl5H)DVEq Bh1)H|=c\2!73X+37eY!I- 5!xod'+/. -HTEhhJf jT{g=8 "+%32672#"'&76'.#"#"'&465&'&/&7632>32A$iT  +$] X  %S2* bU& a#;,#C0Gp,@)79 V&!+6gj't  "+732654.#"4632#"&:"Pk%%HshmuX`H?b1;AyʺyZ  "+"32654&'2#"54)}1ͣѳR X "+%4&#"&'$72&#"&47>7'vR?s-? s?P1 &;h|i{; 11 <f`""+%26?&#!"'7>54&#"&'>32\V7%'-Fuэ)^NH;3q'Q< /מXPV V}yd^R< "+""&54>32#"&5463232>5!"'>54&%MPP!%'RhO@_ysf81'  !KX:?# b;L;j-O]=PAaV9P:.5?aAV]\9-'"+)2+3&#"&47676=!"'6;+F' +- \on^?=B!-!' 11 0V"3+ "+%4&#"327"'632#"&54632326RkFwfy YF)Tu^/71^#1N  !ޛ^<-B  bf$  "+32654.#"'672#".576%qPj1gGb5aañĶD{LuRN򲨠-\b@,)eѓQ'G\X "+"'7433!27'P'7@#'6)RK!4RN H=Rb^ $0 +%!"+"654&#"&54767'&546322654/Xd7X@Y^պwG^4^asWcI9e9'yzNiX/X`ݥi>2!qyil:LD}{TX$  "+%654&#"32#"&5432&76qPj2fHa6aaòŶDzLRN򱨟-]b?,)dєR'G-5+"+4&'.767267>32#".'&#"&#"&47>51TZ)yA;@;%E17q H=d4T/*"`B Fd?#'@dJ?{7 11 :{qM @ .! "+462"$462"!#&#"&47>727&#"&47>&'!"7:R99RC54.'&47327#".54&'&4727#9R99RD;N;;N{2/ddN- 3:C15d9-qњZX;c{BR::R99R::R9Vo-`@\xR?D' 2  1 9u-׮Z(eϕRy=2  1 ;9-Y&"+.732736&'&47327"' "'.'&473273.'&473277>&  o2/g HS. )#^ 31*>0C2:L F?^)w 68? w?Dv  >-e l#2  1 ZYIRV2  1 <1//0R/0u@ 0  1 BWnr@ 0  1 CW ?D'-Q^\V( "+7#"=4;54>326727632#"'&"'654'.#"32'#&#"&47>32654&#" /eM{D^LH-fx<""7+=H+/6lB+ X.#NF^d% )Xsn:4 R\2!+f963)Eoz ){9 11 :h7`㷓%T'"+".'&"!56672632&#'54?>54&#"&#'54?>55#".'.#"32'#&#'54?>5!&#'54?>5#"=4;467632>32-,"'!3DL5mZ&2'P)1'BGo(0&P7! )F%o 1#5'P'1'y%3'P'3% /h[hDMo1?'J/LT%;f"+".'&"!56"&5463232>54&#!2&#'54?>5!&#'54?>5#"=4;467632>32#"&'&#"!26;-,"'!3DڕLn#H)5%!&')/AP'1'y%3'P'5# /h[hDMo/^dR=U318>E+)s 55.'&#"32'#&#'54?>5!&#'54?>5#"=4;467632632673>7654#"=737#"32&#'54/.#"-,"'!%//'2&P'3%'#P7F 1%3'P'3%y#5'P'5# /h[hDMoLdD;1-AvV)<`J )w% BXfP/' 9P #P9HX\-'//%-FRCRsr )}-%//%-}-%//#/ )>'J-) V d^+%!'';' ^-++D%zS'"+#".'&#"!547#"=4;467632>32#"&#"!4'&>?232'#327 5!&#"&47>5!&#"&47> " 'X!1/ĉ /d_hDMm.BmC5#%N >N?w1'^Jwm5mB H)-L+ X-^ !N99bdLy% )>'H,KL>///\, 6RO )dY:!i{9 11 54&#"&#'54?>54'&'&#"32'#&#'54?>5#"=4;54>32673}mZ%3'P'5#AHm'1'R'3%%'X?H-/#5%P'3% /hL{DdF1%-٪V-%//%-}q-%//%- ,dCoz )}-%//%- )Xsj:0%;R9"+%"&5463232>54&#!2&#'54?>5#"=4;54>32#"&'&#"!26;Ln#H)5%!''!7DP'/) /hFwEdS=R3/9z=H+)uy?2)$-!\u?*}/%//)+ )$n28)p6.KTqCoz%G "+&#'54?>54'&'&#"32'#&#'54?>5#"=4;54632>7367654#"'5737#";2&#"'54/.#"}'1'R'7!%'X?H-/!7%P'1' /hdF7-o);bH)t% U'5B=0' -%//#/ ,dEot )}/#//%- )O0R5+%!'';' )6//+D-TP"+&#"&47>5#"=4;57>32#"'&#"!4'&>?232'#327#"7f6lML X. /iM#w1sA;'7'G5 w2'^Jy{9 11 :{% )R h-62-5!):^+#>9u#t, 6RO )dY:!iLiL("+%'"&5432#"'.#"32774.'&767267>&'&47327&#""=4/.#&#".7>w稍?#?  9+dqjuf%-7 hP  %+% S PB {-;S/3%'C  +  ;%uV)1J!)3#BA&*R< $1  2T / 1 !+D+y; 11 <Ld;&"+%'"&5432#"'.#"327&#"&47>54.'&7672767>32&#"&47>54'&#"w稍?#?  9+dqjwd%/,:  ,%-8 iVVu57/Z)%T+%Gs{#uV)1J!)3uWI 1  1 NTBA&*R3`FFHy9 11 :y7m1Jy&5B1"+#"=4;4'&>?2 34'&>?232'#327 5!327#"5y %^w+w1'^JwP'PA)J )- 6R;, 6RO )dY:!idY:=NLK "+%#"&54;&5463 32'#327#"5#"=472654&#"#"'.#"327{ \հs 1'HJ u ^V}JO-!T 9+dsj}f_u )dY: i?vLXb`<+/J!)3¦-94QM;"+>32#"&'&#"&#'54?>54&+"'5432&#"&47>54&'&7672ڏ\R=S11:r;D)%33P'9 Z /R1b\^ b2/V Z8)p6.KTqGo-'//#/l ){9 11 8{V-#"f-d"+>32#"'&#"2&#'54?>54&+"=463247#"'&#"&#'54?>54&+"'54327>7632ZfD5#$N5PD'1DP'3% X VFZ59!'1P'/) Z /P TZ?N1i>///TP-)//%-l PRh%\-+'//)+l )b8''j&08BcigGA"q=xjgdUM=952+'$"+2>4&5463232632.#"'.'&'#"&54654'"3#"&54654'327#"'.6&#"2327#"&#">7#"&#">7#"&#"#"&"#"=4'&#"#"&54732&5&#"#"&547674#"#"&54767654#"&547>7674&54632676'4&5463263243227'>54#"#4"26"5463274"326"7463274'"#"54>32'"5423254'#52"572""32>54.725432>32&54243632632>#"&/ #"47>54&5.##".547&543&546&54726262&5"'.42'&'42'&'4324&4274&54323267&'&'&5432>7.'&7472&542254&423'&54+67#"'#''54732654+"'5737#";2=732   !"  # "5432  '% !(    D3!!%dL!)#-&%RLF<  B6')#)% 3s+#"  7'+/ P %#!- +O#!-P# ! *1%1)H!L+5!0+%`g'!! !3 D+D o !k k   P -@?-lB )6--?D #H     !  ZF\@'# 1- !    ;%' s1  vV/HUZb ''F @^ !F4&546322632&'"#"'.'&'#"&5465&'";#"&54654&+2327#".6&#"32327#"&#">7#"&#">7#"&#"#"&#"#"54654'&##"&54732&'&#"#"&5476754#"#"&54767654+"&5476?654&546367654&54632632243237'>54#"#4+"26"7463274"326"5463274'"#"54>32'"5423254'#52"546""32>54.'624&5432>324&543243422?2672#"&/#"47>54&5.##".547&543&5472&547243262&5"'&54323'&542'&5432;4&4254&5723267&'./&7432>7&'&547254&572374&572'&5"54327f   1.P  $%!!)   D2#!$dM%)  ' PKG @!  B7+)"(' 3s )#" :&+/R  #!- +P"!-M!!+3#2-C !N-J;/)!$%`f'!-!/.  E -F l !k k ! O -?@-mA )5- 'aI  !H !  ZF\@&$   )1- !=  !;  u1   J  g7 /0M  )++) # 3%' + ) 1!P5H%-9Nj-!*$?BJ-'''#)XU': /#3HQ+)"#9mU # ++#'1%fJ /;IF# !)%5  -&32;  !  51 08"* /06!% &1   !  'LjiAVJ=]F`- " 0V 6   @ / :}\=/!     B qG?     ( +=5+ %   -I" "+&#'54?>54&+"'54325>32672&#'54?>55&'&#"V#5%P'5# Z /TdFD-'2/O3%#'X7A'-$''%-u )d0 I-''''-  .dG-Fho"+2#"&'&#"2&#'54?>54&+"=463247#"'&#"&#'54?>54&+"'54327>7632>&#".7>54&'&7672B\R=T317w;D)'1DP'3% X VFZ59!%3P'5# Z /P TZ?Nf;1b \^  b1/V [8)p6.KTqGo-)''%-u PRh%\$-" ''#/u )b8'wRHd{9 11 8{V-#"f-m0^VA"+>32#"'&#"&#'54?>54&+"'5432732654&'.54632.#"#"'&#"&ʋ@J1p5;#!73P'1' Z /R8:5N7hTv{_wb7y1?U:Ro}7TP ӆMFHGo/%''%-u )B;\AF^45fmb3RoZHH?D5pZ}0sPL9@I FA<:)"+%27#"'#"&546?4'&#"#"54632632!4&#"327g#/Rm?i APw93DBj +%RǁP}R|N1wHdy ߖRiHrVo%1C%/9hw)/:35-)\X}3Tsr@ ۓi55c=`^-'h"+672632&#'54?>54&#"&#'54?>55&'&#"&#'54?>54&+"'54325>3B/&2&P'3%FZ\)0/O3%#'X7A'!7%P'3% Z /Thf"H+M-$''%-yT-'''  !+  .dGo/"''%-u )dL1 D,8s lC2- "+3267#"'.#"'6767&! 67"32"&54&'&473272327654&'&473272'&'&qPb#bݠg/5 R^27T!vy~ Ǵ{3L F3d?Zs!1NF37^ M9+A8%Zk^N<*%,)h%_ 3(!Z: +Ty`>1  =yZb3b@1  =wP= % ?F2 zLq'"+%&#"&47>54&+""'6743!327>72767632&#".7>54'&#"&#"&47>54.+"E  OVF+6dj-) 6\bu57/[*% T+%H{#,: 8 3-& 3-T1c-5){; 11 <{dgbw y 0RGFHy9 1 1 :y7m1Jy&0WI 1  1  'DB^!/hi)%>'"+;'"5#"=4726=7!2>322#"&5463232654&#"'P  BbHwA+-w#F U9Lh`g#3dki?p}') F=ߚ9+F)K!H8 "+74&#'"5'>72632&764&#"&#"'54?>!B ?'}RePb%1113%^B 1#RZۍ+!}h-'//''bV"+7672#"&5463232654.'.547>54&#"2&#'5437>5#"=432Vs%#!Ry58M#}"?9RBlCJFolL9pNL+)2-W57& 1o!x+Vqz%?%1_K!+Pd@s{BL9LZBp9-H{L}f=T:PjTo-'//'- )b!,XP=$"+732654&'.54632.#"#"'&#"4%732654&'.54632.#"#"'&#"&7:5N7iTw{^wg5y1?T9R\RT`895Y9dKs{^p{b7s/;R`ZT B;\AF^45fmb3\uoZHH?D5ps:;ZCH^25fm`3hioZ?D3rs7TD8qnT5"+'.#"#"&#&'6!2654.'.54632'.#"#"&#&'6!2654.'.54632)(DjGY0=h>3Gke6hYla: #@'{%.W73Mpn:uU *'DkGY04Gjf5hXla: #@'{$.X63Mqn:u z:RQ)W1X8::X}Na7C/ ݁{:^95=[}M4z:RQ)W1X8::X}Na7C/ ݁{:^95=[}M43HD"+!2>32#"54632632654&#"'!#"'5#"=4;4'&>?2X?))+B'3Pw3+)^ \ y %^wo N Ň3D5 wt C+7p ), 6RP^XU "+ &'"4>3276'4&#"&'463232>7&#"#"54632254'&54632#"'#"& #=bl^N'-+/-cT3=3^swT'dc=##-TsrhB-%+s3F$t 5qow9 J&;/PZ7'%\`fTVP#!#!3i\{P%%%))%fq\ f)/+* "+767&#"&47>/!"&#"&47>%3M2=;%1Nw=JP#+(#RE=KJ@/2 \/ 11 0Rn WL7 11 BDB 3 " "+32654#"32'4#"&47>54&'&4732632!"&d\d87a9 b11b K<˓V>?T\!=,%1 8{{9 1{f;y1L"+"'&54323'.# 32675\"yDzdH Jt!m oz`DT bZB= , "+%32654.#"'2632#"&#"&47>54&'&47=mN{ROT\` J b11b 1,\N@ׁd/1 8{{9 1JE$ "+#;2676&#!"&47>54&'&473!272'.+"326762"'.u+@Xut RRb11bP/%"_Pt%u^+) *93(wis1 8{{9 11 FA1)Jf+7_#!# FX=4 "+74&'&473!272'.+"326762"'.+&#"&47>1b?9!FX%u`)))91u1c LJb1{9 11 fR)IRV!%{9 11 8L/ "+%#"54323'&!"32754&'.7327+?"7%JZfg)^  w`TI  $ B^!m DVn61  1IFG+"+&#"&47>=!&#"&47>54&'&47327!54&'&473271b CN b1'1cDB b11b ?Jb21b CG b1F{9 11 8{{9 11 8{{9 11 8{{9 11 8R-!"+&#"&47>54&'&473272b UNb22bCJ b2F{9 11 8{{9 11 8F!"+4&'&47327'"&54632729eNR b1X'T3;'y; 11 8{J9-)4#J-I."+4&'&47327276'&'&47327&#"54'&#&#"&47>51bEBN'bu 4 yA9~ D//+Xd:R-'X'N>Jb1{7 11 8{( 11 N1 1 1/"3{7 11 8{JF#"+)&47>54&'&47327;2765dj)1bKN b2)#\;1 6{9 11 8{(1#@ "+ "'#&#"&47>76&'&47 %&#"&47>&';T RK5S-& @+J3P') MoB=  qA 11 NT? 1#1 !<6 11 4,3B92"+%&#"&47>5&'"=7>54&'&47327"'.=2b {JHzb1!X#1b {HJzb1-3,{7 11 :{1= 1 {9 11 8{k7-!1L7 "+"32654&#"54321}y!$żۻb Fj .!"+3254#"&#"&47>54&'&4732632#"'9LH31c ]^b11b?)śJ7*{9 11 8{{9 1euLh /'"+"32654&632327#".#"'>7&54321} `C^0\Z>( C'T!$Ƴtżۻ<PN', 8z.ձ)(J @7&"+4&#"326&#"&47>54&'&47326323#"/.+PiV1Za)LBJb11b?!o?-;?>9HB-5%q^P/Ra{; 11 8{{9 1r`/NL+ {/3-d+("+'."#"&#"&'63254&'&54632xVdc5Hh7^# %1ðgvw`mm `gNC;Q/*Le7# F PZ7qw-H,"+%&#"&47>54&+"'67327'.+"o1bKN b2'11`D! uѥ $!E^51%{9 11 8{;-Gujm sI199w3"+4&'&47327#"&54&'.732732>5{1c {HJ{b2ᶿ1b PF b1jB_:"{9 11 8{A{9 11 8{ꤻ>9f>;!B* "+76&'&47327#"&'.'&47327w SoN9Z L>%%(5HjJZRN6 11 ?SbR!1\3 11 +!=M"+>.'&47327"'.#"'.'&4732776'.'&4732776 ;6sOLVHE'( )#0EjRNG  # 05FfNJP !%, 11 GIsfg  FB%eT/ 11 &/.&#"&47>?6'&'&47327#A  sGJVBZ9 ?JNjZX  )%} !?  sEHVDV; Xi sJhwL!7! 11 2G\/ 11 335! 11 .M 11 %:"+6&'.7327&#".7>576'.'&473273-_  jP7Z  AF,1`  PN b/1@Bw=N U /I8 11 FK9i{9 11 8{e(1!R< 11 ADLh*)"+!263237>76!"7476''6747;h ' #-F7!5 CF#'w5h 8N F<9c /  tRb "+!"5463!2+F!%'7)> fd)/= ;5+* "+767&#"&47>/!"&#"&47>%3#"/7632M2=;%1Nw=JP#+(#RE=KJ@/2  +?F \/ 11 0Rn WL7 11 BD(/ fX)/= 71+* "+767&#"&47>/!"&#"&47>%3632#"&547M2=;%1Nw=JP#+(#RE=KJ@/2 ?2'  \/ 11 0Rn WL7 11 BD/' % fR)/= =4+* "+767&#"&47>/!"&#"&47>%3&'&'676M2=;%1Nw=JP#+(#RE=KJ@/2Lw\sbG% \/ 11 0Rn WL7 11 BD{L^s7s f)/M 64+* "+767&#"&47>/!"&#"&47>%32676#".'&"'632M2=;%1Nw=JP#+(#RE=KJ@/2?#)!) " ?/'''/u%)3 \/ 11 0Rn WL7 11 BD+? '-5 & f)/9C @ A<72+* "+767&#"&47>/!"&#"&47>%3462"&%462"&M2=;%1Nw=JP#+(#RE=KJ@/2V9N97R7Z9N99N9 \/ 11 0Rn WL7 11 BD)9:('::')9:(':: fT)/:D @ B=50+* "+767&#"&47>/!"&#"&47>%3"2654&462#"M2=;%1Nw=JP#+(#RE=KJ@/2/46Z55lmjJH \/ 11 0Rn WL7 11 BD=/-@@-1;hfHFh  ]hfb2 "+%267!0'54?>5#"&#'54767654'&=73!7.+"326?6732#"/.+3;4#" VZ5) #/%-" & '{ X"BD 3 +F/;}'!  #  %!'}h+:#/Z%3@ /"+  +b -#3 'VLf6+ "+"'7&'&54323'.# 3267632#"'47726565( Kn\"yDzdH Hp+?R}^HPN)/9 d!m oz`DT `ZuF9BV#!+'GJyESQK$ "+#;2676&#!"&47>54&'&473!272'.+"326762"'.#"/7632u+@Xut RRb11bP/%"_Pt%u^+) *9X  +?F3(wis1 8{{9 11 FA1)Jf+7_#!# )/JRESMG$ "+#;2676&#!"&47>54&'&473!272'.+"326762"'.632#"&547u+@Xut RRb11bP/%"_Pt%u^+) *9 ?2' 3(wis1 8{{9 11 FA1)Jf+7_#!# /' %JPESSJ$ "+#;2676&#!"&47>54&'&473!272'.+"326762"'.&'&'676u+@Xut RRb11bP/%"_Pt%u^+) *9Lw\sbG%3(wis1 8{{9 11 FA1)Jf+7_#!# {L^s7sJEMU TPLH$ "+#;2676&#!"&47>54&'&473!272'.+"326762"'.462"$462"u+@Xut RRb11bP/%"_Pt%u^+) *99N97R#9N99N3(wis1 8{{9 11 FA1)Jf+7_#!# R99R88R99R8=-V!/-'"+&#"&47>54&'&47327#"/76322b UNb22bCJ b2  +?FF{9 11 8{{9 11 8#)/R-R!/)#"+&#"&47>54&'&47327632#"&5472b UNb22bCJ b2- ?2' F{9 11 8{{9 11 8/' %15R!//&"+&#"&47>54&'&47327&'&'6762b UNb22bCJ b29Lw\sbG%F{9 11 8{{9 11 8#{L^s7s!;!)1 0,($"+&#"&47>54&'&47327462"$462"2b UNb22bCJ b29N99N!9N97RF{9 11 8{{9 11 8@R99R88R99R8==4 "+#32654.#"2632#"&#"&47>=#5354&'&47h=mN{ROT\` J b11b P1,\N@ׁd/1 8{P{9 1BWP5"+2676#".'&"'632&#"&47>5&'"=7>54&'&47327"'.#)!) #?/'''/u%)3t2b {JHzb1!X#1b {HJzb1-3,+? '-5 &b{7 11 :{1= 1 {9 11 8{k7-!1L7\ % #"+"32654&#"5432#"/76321}y!$  ,?Fżۻb )/L7R % "+"32654&#"5432632#"&5471}y!$B ?1' żۻb /' %L7J % %"+"32654&#"5432&'&'6761}y!$-Lw[sbH$żۻb {L^s7sL7 5 "+"32654&#"54322676#".'&"'6321}y!$#)!) #?0'''/u%)3żۻb +? '-5 &L7 !+ @ )$"+"32654&#"5432462"&%462"&1}y!$:M:8Q8Z:N99N:żۻb )9:('::')9:('::;7 J;0"+%2654&#"#"326?6;2#"/&'#;27&#!"#"$54323!7.B3!>L?'! # % D@'3)y=+C_ Z3 +F7%+1/#+  +b -?1Z%3@ L7$ "+"&#"''7.54327%326541}R[o_;^V`!׍vQ;QJYzŮvjZb;'@vCr'rTۻ9w\3A?9"+4&'&47327#"&54&'.732732>5#"/7632{1c {HJ{b2ᶿ1b PF b1jB_:"  ,?F{9 11 8{A{9 11 8{ꤻ>9f>;)/9wR3A;5"+4&'&47327#"&54&'.732732>5632#"&547{1c {HJ{b2ᶿ1b PF b1jB_:" ?2' {9 11 8{A{9 11 8{ꤻ>9f>;/' %9wH3AA8"+4&'&47327#"&54&'.732732>5&'&'676{1c {HJ{b2ᶿ1b PF b1jB_:"Lw\sbG%{9 11 8{A{9 11 8{ꤻ>9f>;{L^s7s9w3=G E@;6"+4&'&47327#"&54&'.732732>5462"&%462"&{1c {HJ{b2ᶿ1b PF b1jB_:":M::M:Z:N97R8{9 11 8{A{9 11 8{ꤻ>9f>;)9:('::')9:('::%^ H("+632#"&5476&'.7327&#".7>576'.'&473273f ?2' -_  jP7Z  AF,1`  PN b/1@Bw=N U /Z/' %I8 11 FK9i{9 11 8{e(1!R< 11 ADF.971"+%&#"&47>54&'&47327632#"72654&#" (-3 49b11b39 Y5d1oI]"_wh\fI6;  11 8{{9 11 *R4TQ0h\vmV% N . "+462"&%462"&6&'.7327&#".7>576'.'&4732739N::N9Z:M:8R7`-_  jP7Z  AF,1`  PN b/1@Bw=N U /)9:('::')9:('::vI8 11 FK9i{9 11 8{e(1!R< 11 ADR{!C:)"+4&'.7327'"&5463272&#"&47>54&'&47327;9d  MR b2X'T3;'T2b UNb22bCJ b2y; 11 8{J9-)4#F{9 11 8{{9 11 8d+WT?("+'."#"&#"&'63254&'&54632!'."#"&#"&'63254&'&54632yVdb5Hi7^#!$1ðfwv`xVdc5Hh7^# %1ðgvw`mm `gNC;Q/*Le7# F PZ7qw-mm `gNC;Q/*Le7# F PZ7qw-!9> "+%&#"&47>=46!27#"'&7632654.#"&754&#"`'N BJ b1F>{V F=B`6vR  s1{7 11 8{ os=FjZTgtCV:#L1 D,`l fa@3 "+3267#"'.#"'6767&! 674&'&47327#"&54&'&4732732>5"32qPb#bݠg/5 R^27T!vy~ 1c{HJz  b1ᶿ1c QFb1jB`:"Ǵ9+A8%Zk^N<*%,)h%_ 3(!ZF{9 11 8{A{9 11 8{ꤻ>9f>;J: +TJ3\h b]W@"+4&'&47327! 4&'.732732>5"'>7&543263232$7#"."32654&1c {HJ{ b271b  39 b1/iNB_:# G?'S $ʶT N-zk_j@;/18z-ճ&*9 Mg0D/8//żۻ==4 "+#32654.#"2632#"&#"&47>=#5354&'&47h=mN{ROT\` J b11b P1,\N@ׁd/1 8{P{9 1 fH 6< 87"+267#"&'67&#"&47>/!"&#"&47>%3q{}7`mvM2=;%1Nw=JP#+(#RE=KJ@/2HRgcV \/ 11 0Rn WL7 11 BD 5f;A=<'"+"&5467&47>/!"&#"&47>767&'327 37TjoZ/BP#+(#RE=KJ@%M2=;%1NH%kA5JD!#25\RJ31 0Rn WL7 11 BP \/ 1 R3FJDEHDLh ,"+632#"&547"'&54323'.# 3267D ?2' \"yDzdH Jd/' &{t!m oz`DT bZL? ) "+&'767"'&54323'.# 3267N)HN'H'\"yDzdH JjNPhpt!m oz`DT bZB=? 7 # "+&'76732654.#"'2632#"&#"&47>54&'&47N)HN'H=mN{ROT\` J b11b jNPhp1,\N@ׁd/1 8{{9 1J5W"+"&5467!"&47>54&'&473!272'.+"326762"'.+;2676&'327RTkr[Rb11bP/%"_Pt%u^+) *91u+@Xut *gB5JE!5\RJ31 8{{9 11 FA1)Jf+7_#!# 3(wis P3FJDEJ7 P/"+&'767#;2676&#!"&47>54&'&473!272'.+"326762"'.N)HN'H1u+@Xut RRb11bP/%"_Pt%u^+) *9jNPhq3(wis1 8{{9 11 FA1)Jf+7_#!# LH <"+267#"&'#"54323'&!"32754&'.7327q{}7`m+?"7%JZfg)^  w`TI  $ HRgcVB^!m DVn61  1IJF` 1"+632#"&547!&47>54&'&47327;276 ?1' 5dj)1bKN b2)#\;\/' %1 6{9 11 8{(#F+ "+;276!&47>='754&'&47327%)#\;>5dj)#1bKN b2=a(1 6]=s{9 11 8{UBb G@%"+632'"&547&#"&47>5&'"=7>54&'&47327"'. ?1' 2b {JHzb1!X#1b {HJzb1-3,^/' %{7 11 :{1= 1 {9 11 8{k7-!1B7 D=""+&'767&#"&47>5&'"=7>54&'&47327"'.5N)HN'H2b {JHzb1!X#1b {HJzb1-3,jNPhq{7 11 :{1= 1 {9 11 8{k7-!1BH3"+%&#"&47>5&'"=7>54&'&47327'"&546327267&'.=2b {JHzb1!X#1b {HJzb1X'S3;(6b {7 11 :{1= 1 {9 11 8{J9-)4#x!1L7{)5 @ 2,# "+"&547632"547632"32654&#"5432}#/   )!->}y!$% / ++/żۻb J` N E4"+632#"&5474&#"326&#"&47>54&'&47326323#"/.+ ?2' SPiV1Za)LBJb11b?!o?-;?>9HB-5%q\/' %]^P/Ra{; 11 8{{9 1r`/NL+ {/3-J7 K B1 "+&'7674&#"326&#"&47>54&'&47326323#"/.+N)HN'HPiV1Za)LBJb11b?!o?-;?>9HB-5%qjNPhq^P/Ra{; 11 8{{9 1r`/NL+ {/3-d` 96!"+632#"&547'."#"&#"&'63254&'&54632 ?1' xxVdc5Hh7^# %1ðgvw`\/' %mm `gNC;Q/*Le7# F PZ7qw-dfD9"+"'7.#"&'63254&'&546323'."+632#"'47726545' I;o # %1ðgvw` xVdc5Hh7*?R}^HPN)/9  F PZ7qw-mm `gNC;Q/*Le7pF9BV#!+'Gd5 63"+&'767'."#"&#"&'63254&'&54632ZN)HN'GxVdc5Hh7^# %1ðgvw`jNPhqzmm `gNC;Q/*Le7# F PZ7qw-HfD9"+"'7"&47>54&+"'67327'.+"&'632#"'47726565( RH b2'11`D! uѥ $!E^51%1bx@3?R}^HPN)/9 1 8{;-Gujm sI19{9 1F9BV#!+'GH<$"+%&#"&47>=#"746;54&+"'67327'.+"32+o1bKN b2'11`D! uѥ $!E^51%{9 11 8{$;-Gujm sI19#9wj H ( "+"2654&462#"4&'&47327#"&54&'.732732>5N/35Z55lljJHu1c {HJ{b2ᶿ1b PF b1jB_:")=0-??-19f>;9w{Q 1$ "+"&547632"5476324&'&47327#"&54&'.732732>5#/   *!-1c {HJ{b2ᶿ1b PF b1jB_:"% / ++d{9 11 8{A{9 11 8{ꤻ>9f>;Lhh 87%"+632#"&547!263237>76!"7476''6747 ?1' 0h ' #-F7!5 CF#'w5hd/' & 8N F<9c /  tLh- 65#"+4632#"&!263237>76!"7476''6747F$'76!"7476''6747N)HN&Gh ' #-F7!5 CF#'w5hjNPhpm 8N F<9c /  tJFf4!"+2'>54#"546!&47>54&'&47327;2767Dnc ;F^/}5dj)1bKN b2)#\;fZ;B # X)+N*1 6{9 11 8{(H5 7'"+&'767&#"&47>54&+"'67327'.+"N)HN'H1bKN b2'11`D! uѥ $!E^51%jNPhq{9 11 8{;-Gujm sI19H1>."+2&5>54'&546&#"&47>54&+"'67327'.+"9FxaHR@V91bKN b2'11`D! uѥ $!E^51%T`PVg!>/B%3N{9 11 8{;-Gujm sI19d1+=2,("+'."#"&#"&'63254&'&546322&5>54'&546xVdc5Hh7^# %1ðgvw`9FxaHQ?V9mm `gNC;Q/*Le7# F PZ7qw-`PVg!>/B%3R-- -$"+4632#"&&#"&47>54&'&47327F%';B)%=2b UNb22bCJ b2%=D&#@DF{9 11 8{{9 11 8V?"+"7463!2+#"&5467&546323.#"732+"32>54&d!"!!"۾Z=ƨ/8xXd_q!#Hu )AuL``3+('æhFPZo,R Vy{>'R-RbH/o<%*DQJE "+&#'54767654&'"'57327#&#'54?>/&+";26'&N *.d@R5#3\ +- 3<> =)LV aJ#2 54+'H+ )) i=* ++ !4%X;Z) "+&#"32672&#54?6=#"&54632D=:t # L1 3B;h{o`/b%PJ!'%37so5  "+#"/7632e   ,1  ! "+632#"&547 ,# !   "+&'&'6765gT@PE m2Vr5BP'yOc."+2676#".'&#"'632xa -   !R  %z,  %  G "+!"7463!2 C !-9%"+32'#&#"&47>5#"=4;57>32#"'&#"f/6lUP X. /iM#w1sA;'7'G5 o ){9 11 :{% )R h-62-5!):^+#>9u#%cJ"+#"'&'&"!54&#"&47>5!&#"&47>5#"=4;46763267672#"'.#"32'#  5%)"5H5l  ALwH)b-LFP X- 1b_nHRo NsyE?)93+1 4R=^JNRp{9 11 '@)b2-5!):1-+#Bt )U @ 2 "+462"$462"!#&#"&47>76'&767>322&#"&472>&'!"9R99R;N;;N9{+>1n NX'%${ #B +\u ' q;}R99R99R99R9/^%-  11 6V)! >+ 11  [ FP KG$"+!#&#"&47>77&'4767>32&#"&472>&'!"'"547y kf+>1n 5%/ 11  [ )AwwFT TK$"+!#&#"&47>77&'4767>32&#"&472>&'!"&'&'676y kf+>1n 5%/ 11  [ mZ! =@F7?fFd \M$"+!#&#"&47>77&'4767>32&#"&472>&'!"2676#".'&#"'62y kf+>1n 5%/ 11  [ /+? '-6 'V @ 4 "+462"$462"!#&#"&47>77&'4767>32&#"&472>&'!"9R::R1n 5%/ 11  [ DE" "+!#&#"&47>76'&767>322&#"&472>&'!"{+>1n NX'%${ #B +\u ' q;D/^%-  11 6V)! >+ 11  [ y; C0 "+"3&' #'#";2&#'54;265"#"'7267>7.'&5476j3^\5AJiBZoj@ %n7G5=499}3%P}K)dF9%jo=}V{\j_;%-/Z''+/u)9DR<whh#+L "+%"'.+"'530%+"07'&'#"=307+"7654&+"=37+"#"'VJ+1+{N N7I+Im)R<#%9>!P V)) )--v5*-+%6˪N\+- ,%)'#7}+-C-3 "+674.+"=737+""'.+"=737+"7 !"hP16#9:9P5^ !A@#'')1/0|/,%%- '# +IG"+&;2&#'54;254+"=730%+"27654+"=7307+" .%V3>5R7oo7P5qVJ;L-BP$VmJHN1!B1+''\Z''ZOJ+H/''}wEd#3%7 D>%"+"3265&#"&47>54&'&473263223# '&#7bRb1@rTR s??s G#3{Bm^|92=,D+O,. PѭUN5+{; 11 <{5{;2 %=fHuWC- %NXf/DE"+2#"&5463232764&=/%5>7'#"&#""&5463246/'i )7jͲR !-3 Z54'.5432325 >+71R5B;9VZN3'<U"@# 0*\3HT?+Hk%`{Qr>f@3NVq:f;J:19#VAm1?) 5!"+32'67>32#"&#"'".5467&"'>2  )q];W-+7)* 12U5%76Z#?!+ T{: KRY9RZcfD-%.H|<8RPX%JLJ =<ss: "+654+"'57307"#"' #"'&#"=7307#">32+ÙZ}Lj% %% 'RV[B <)=)'}X+)-))%)##-9ys9 "+674+"'57307""' #"'.#"=7307#"636+[}Ld'A#0/V[B #(-$=)'pK+))-);')%)#\!+? "+2654'#"'57307+""'&#""'.+"=737+"6#9=5uT#)1 .1<'jN-5 3hK5''g/29312f/D'%).P\&#"+#"547.'&'63264'.5432 B37=+h38";QZ:^f>Zh/+]3G#jsJč~7N1+}c 6$VA!1."+32544?&"'>326767327#"&-/')7\!A!+ Vy@fF/ '>)= HC 5=T?D;HZ`=hb/[J =<V}TV\?> ) aqz)2-*#"+%&'&#"'>3264'&'42#"&546732654R1;: Xtz,g~'=R;K.RR9D4nO: 3VL ;<!>BG4vuAm~T1O)|N<)c-sM;"+%4'&'"&54&'&4732723276'4&'&473272#"&54623276 }{3L93d@\q!1N 93!7wtZ5% 8& 7=#){y`>1  =yZb3b@1  =77&'4767>32&#"&472>&'!"y kf+>1n 5%/ 11  [ #5 0$ "+2.546%2.5462.546!/>ZJgI cJ/>ZJgI cJ/>ZJgI cJ:+K7%k!f\o9,K7%k!f\o :+K7%k!f\o#5 *$"+2&'>54'&546!2&'>54'&546!2&'>54'&546 =JbJkJZ==JbJkJZ==JbJjIZ=o\fm%7L+:o\fm%7L+:o\fm%7L+:F/  "+%"54 "3254\\$'/VQ!D-)f+  "+     u1u ^^ut^N+  $(,048<@E'@$CA?=;97531/-+)'%#! "+!!'37!!3#7!!%!!%!!%!!%!!%!!%!!%!!%!!%!!%!!%!!!!5Bqdkk_uuuuuFHVŋ5;7777888777888888777+ '_gq@ҊnjfbA2 "+!!'337637+"'.+5"'.=>732326;#"𑸳.'#"#"'&+3264&"462#"&322;676323./77674>?#;>767>36;'"""#'6=&'&#"+"&#&+'7!!VB# 8/%   9d>+9#- RoB 3gN@ &!+-/L-k!!C1E22"!3r)- 9#7) /: 1%3% 5/+a9+7T R/11T!/ 1 3'))y)D= b {u3 3^D+" !X P??_1H+%9F 51X!!+#11E12V #N3`67 #+%1XNN-#+ 1 %+  . %= 21 L "&@E@DB;)$#!   "+35#5#!5#35#35#5#!35#5#3%#5335#535335335335!!3%!!5555dd555VEd887BB>>>>pJL %0:>X]@\ZSA<;72-( "+332654#"727'"'#"&546324632#"&732654#"#53333333333!!! !!wLJ) T'))3#$>1#+@1) -P=6J;1#-?"# 7<7;8;7>5VV}NEJ-E#+CA)qZ 7X7#1P3PK9Y7/F$+C77{Oc5pJwD% "+"32>764'&"'&532+JJ-+cG54#"&'$721L 4F H-=#C /  / DŅ9+_ -bqD/"+4632!2676&#!46?654&#"#"&yɧBLN!~9D+2dFgNjZ 3!'5qŨNXJ!B^umҘˋ{R3") ))1\D7"+"#"&54>32#"&5463232>5"'>54&'TT+)%%=XZwm< 9&F3#R^>7% lP?'J/APB-TQ>18!#HeFZ^]=D.("+)2+3&#"&47>=!"'6;V`!)#); cu -D=FTF`"0/-  11 )\B{mmD+ "+4&#"327#"'62#"&54632326sIN#qsJ-Nd3;4b5r  !b?!1G  ZD +% "+2654.#"'>32#".547>7Vs3mL}@06Ͷ0Ze9kxX54&/"654&#"&5467>7'&74632;qbbbiyjh^mBo'+V'-ưD@!<ˣ+)dR9>TCyQk)T'R7H^j֍봤D7N!jZD )# "+67%"32#"&5432&5> Xq3lLD/7Ͷ9kyX'vR?s-T  wOP1 &;h|i{; 11 @d^""+%26?&#!"'7>54&#"&'>32ZV7%'-Fuэ)^NH;3q'Q< /מXPV V}yd^R< "+""&54>32#"&5463232>5!"'>54&%MPP!%'RhO@_ysf81'  !KX:?# b;L;j-O]=PAaV9P:.5?aAV]\Dq+%"+)2+&#"&47676=!"'6;!bis1'y'TGo nr? AP!-D 11 0V\mB+ "+%4&#"327"'632#"&54632326jEwfy!}ZF)Tu^/72 ^#1N  !ޛ^<-B  bf$  "+32654.#"'672#".576%qPj1gGb5aañĶD{LuRN򲨠-\b@,)eѓQ'Gyu "+"'7433!27'm'8?"'5)RL!5RN H=R^Z $0 +%!"+"654&#"&54767'&'46322654/Xd7X@Y^պwG^4^`sXcI9e9'yzNiX/X`ݥi>2!qyil:LD}{PT$  "+%654&#"32#"&5432&56qPj2fHa6aaòķDzLRN򱨟-]b?,)dєR'G) }"+!%!!1!9#^Z) }  "+!"!4&'2!463!V}{X}ǰ}#}V1V}Z}'}) }"+2654&#!"3!"&5463!2X{{X!V}}V!}}}}XV}}VX}X}}}}RbD "+!"5463!2+F!%'7)>RD$-4:@D@DA>;850.(%" "+747!22#"'".7 $7>7! "&!6RTL F8m\ -Κ w !>TJm /> *ql7; %RGyNj7Z%du2:p^IJJJ"+.'&'367>36&'&'&6777&'.'&6'&'.7&'&>76632>5'7'&'7'7&'&/'&'67&'#".67''&''6'&'*.'6?'&&&'&'7>767>767>767&.6&6.&'67.'.'&&6.6&6$76&'667>'&67676aF * " %,# 4 (* = ),/B  B $C ) 66#4I #(D)U D: #0 , QU$1($p9<n  <2! i6#"7?+|[mZ -5]q sTL"  R&I `05..']kv26CP3847135JU Jb8Bk3*GkM3CB|*,1.5Ire*V!#WyE -60B:F 3E 'n,R -&mb!('l% H&* 1Fn4r o-# !6 b   @!&B 0  )"   7::0<,:( RH ($  =* R7%R 4- (%;'6A6o-&$U)    =  * &H{' ETkf^PDB(),/2& q6mA6ly@&a,.*9A "\q  =1>S ) *.Tu  ! LƎpZlHF%9OA^E"+&77&7>76>&&&'&6'&676&767676$&&&&6667&'>7&''.'&'.'&'.'&'"#&'67'&'6?'&&?'6467'>7&"''7'&?'&7'&'.'7/&7.'&736&67>'&'&6/&765&'7667&&'767>7>'&.7>76767&'63& kK4mF1 *&H %l'(  )bm&- &/,n'F3 G9C15- EyW#!V*frI5.1,)}BC2MjH*3kB8bJ UJ5417583PD׀63uk]'1%C0` I&R  "LTs q]4. [m[|,?6"#6i !2<  n;9p$(1$UQ , 0# ;D U)D($ I4#75  ( D% C B/,) >  *> 4 !",$   O:%FHlZpM   uT.* * S>1> q\" B8+.,a&Azl6Al6r '2/,)(BDP]gjSD!'{H% *  <    )T$%-o6A 6&;%( ,4 R$7R *=  $( HQ '9,</;:,  ")  1 B&!@ b 6! 8T y;DH\xKA$ yh]\RGE><"+6#"&'#"&'#"'#"&'#"&'#"&547#"'&'&5467&767&'467&'&'&#"6667>3?''/'''#////7'7/4'&/'''57''77'/77'7'?676'&##3'&'&'476?67676671#'&75766767576547&#&+'5071;674'0#04#"'&764?5674767>76743"#".676376767''7'?#7'7'7'7?7'>54&/7?7'7777674&546'56'&5.'&'#&'?764#'?67&'&'&'&/5&'&'&"'&67674&'&/5&'#'&'&676763&'&#/547>76'&+&".7>7676'&'&'##"&'&7676745&'#"&767676/&'&#&671622366366366./7'7/7'7'7'7'7/7'77776'./7/'7'7'7''7/777?776&/7'7'????7777?7?'#"'&#">76?>'&6./7'7'?'5?3???3???777>???7?77'.72632#/'"."&'&'''5/&'&2672732763&'&7671676326567667676274=146326717>712676#"#'77667./&'&""'&'&7##"'&'&5#"'&''&''#"'&##9'&'.'&'5&'&'#&67672&632622326632632627&?'77?27676'.'&76327>7574'&'2.%"676'&7"1>7'"67674'"67&'&"367&'./5/&'%"67#"'326'&'>'7>'&'7>&'7>"&'26 S(&+ E3C @ !'$0 :834';M..- ' ?2 bKo+ ;. 7  ! ,      *                    *  (   #*Q ,G=% 'ZL2!ff$%"    (3 = &%                   "        &,-     ;((     ,         ,     9 ! )>9o G  2, 7               &!5I>6#1)/*0 &! ( #4 1 #O1$- # 9)"*= A    $  F47"   !          0   5  ,   1    &    !$ , .  /('/)8 : +"  q  &@M!0 4?"  $  1 X  V   ;  %<    n %-!70 2.)I"1G"- ..', /3M!+ +/2 (,&#  3$3+  0"=*$,9\#Kh)@H <                $ '4.+!$ ' #               % "%  Z`EP:B".P"-u*)= !                  %+         +("     * 3 ,)9+#;?0 0))  % &!  ++)#+   #% @ 7? 5h2  (  "!0#& (('(' $#*F3#  + ')- +&)'  )'+  '         !   #?!0  lG352 )          >    :      Y5-)3# '!7!" t ' bR "/KC#2  X #      ! I 2C *BK 4-&$*1&/t2 '"10 0:";X.(* /(&! "+4&#'"/672632#"''653274&#"h+{:PG\ocZ7!eE+J1>f=(% 5Vw3ms1 ' KPH=jy7J"+2"'.#"32>7#"&546B\h35 )%7^!/L%=!'Vy}JR7;/#vsX3N!ywy1h 1, "+5&#"326#"&5463254&#'"5'672"'&54D?7R}!`d)^m}qT9+{9!5m6  Pwl^?/9j5^=(% 5VF;$ I72( "+#"=4;4632#"'&#"32#2&#'54?>V?X?0)%3Z y5+ o0 y7##'7%5 ""Ph2CO JE63-"+"'&#"#"'3272632#"&47&747&746326323274'&#""#"4#"32>! 9VF+I%> y95ʉTyfBEFVF.1B%)o+:7H# #u31u#?BVb+?5/8`b?ALP-BJTf7)!)@fd%!r:G9h;8 "+632&#54?>=4#"2&#54?>54&#'"5'672\R N1 f1L R5}#+{:h $$ \ $$ !=(% 57o9'# "+2"&4&"&7>=4&4.#&76721##1#s-<=>B+% }99"2""29!; 77L- )"+2"&5464&#'"/672#"&54723265=#5#!+{:)-=-P+'+#<"##{='% 77Յq-3'3<tuJ0 "+4&#'"5'6726?>&'&'5737&#'54/&#"&#"&7>s+{:J33  =. X3N'CL1N\% 7=J^>B+L=(% 5V44  )) 7T 7- *-o=- "+4&'&#&7672&#"&47>q+ jF5-VZ+5JF  5R/L !$JP"+7&#"&7>=4&'&76727632>32&#"&7>=4#"&"&7>=4&"5=B^=B*!5q5   N`n ^0#?=T?>3s/G1=>3-b?;;$ Iqi&C=?ËT;?L?#J:"+%&#"&7>=4&'&76727>32&#"&7>=4&#"5=B^=B* 6q5   %p:HG#?=T?>3-1!Z?;;$ I554&'&76727>32#"&'532654#"-;=>B+!5m6  f+Zea!Zt-Lh'q 9!;;$ I '=tiwh\oV);ZJ) "+&#"32672&#54?6=#"&54632D=:t % L1 3B;h{o`/m%PJ!&$37so5+J. "+%"&"&7>=4&'&76727632#".'&\#--;==B+!5m7  ;]%&$   @9!;;$ Iq--  9J*#"+372654'.4632."#"&#"&9-*+9)9wTIrNo70NR5%533DNT+!`%L)%&'N1#BX#P<+'%%-7%9LG"+327#"5#"574326=732#+J! FlfR7+N s#H}w1FL%w?3("+74&'&4727232754&'.7272"'&'4#"7q/^B f7J/^A  6m7   \3F*#h+ʼnVF*#h+;$ 4!; w?+"+&47327#"'.'&47327?6&^9) "%^)^/W`#"!'!{J#"+1,)1+?H"+&47327#"' #"'.'&47327?'&'&'&47327?6&^9) ky "%^)^/WI^)^/ L`#"!'! !{J#"+1,)-L #"+1,)1+)?B"+%6&'&47327#"&54632727654'.'&4732772^9$ #P=?)  ;    ^)^/u 1+#"#4PX$/; #"-/-N#""+326?2326?2!"547#"'6?0ݘ7/ m +N = 'R!3#^N fo;9 !  "+3264&#"4632#"&%3##5#5353utwwuɋƎu??馦ȌƮ??9   "+3264&#"4632#"&%!5!utwwuɋƎ%^馦Ȍo?Tq ? - "+%27&#""32&''6='"&543254'767632#"'T9=Fb R;5P!C9"L{ӕZ@"8D!Lzɞ^;=[MLdVNmNn)TN)q̾RFm+NR+o\;/d q<4 "+!63 '>5#"2&#'54?>5#5354'767ϓDEdi/R' )3+U)3*#7D!H3g) D%fq{y-+// +-Hm+NR+oωZ "+#"'%6D 9@ == "+#"547X'9)Aw1#  "+&'&'676Zy T`{T{%#mZ! =@F7?1 "+&'767\J!{^uffg1\g)5HR+)NuZB "+#"&'332673ks9 }TP Bh}k9DC:+1 "+#"&54?632#"54?632 y!)+ p'3 1  -+1 "+4632#"/&%4632'"/&++)z  3)p 1 %!-  ZB "+>32#.#"js: }TP Zh}k9DD9{{s "+2654&54632"&5432JbB"!LٓL!"+%DLMMLD%VF"+"&5463232654'&54632͏-#!%:-+;+%)R`bP'# #53#  %%;q "+462"$462"w9R99RD;N;;NN<7>7632!"%476"/&#". .5476762+"  #"&'&#"323327674'&'&547632#"67  Ds33# * /"V //--SO} +0%)=rN -T?B?  g) ' 5ONp?#<$5F%%0 +/,<    Q{@efP%%7 !{))'( C  5#1 cd=y)oro , s+ '  8# @ /-9.3MU /)/ TJ*.59ADP^@YQJEDB@<760/,+*) "+6#5'#5; &'7%'>4&'7%337%3"&462! "&762#!276'.%O,>m7/Ǿ1}>!+! a?}<\5L=BAy -A--AP')N('-B !!!!!!!#1}w56ٮ!3'l }P'/%' B--B-yF"!!#E+79:9:9:7   $BJMYg@aZSNMKIE,% "+"&462>?7/'&/' &'77""&/'"&462! "&762#!276'&",=))=g {89;RJ-''L=BFMC+e/ 5/s5  -)j 1/F//F9P')N('-B !!!!!!!w>++>)mJ##!3-%L>*R)_)kRp 9  +B11B1yF#!!#F+89999998F/ ) H]`n@vqga`^MI7 "+467.%'"!7&'&73632#"&'#&&.'"&463267>72"2654&'"'47&! "'&762#3264&#"'&''jb'6-rdPf V/ ,"TwwTJn! +)P-7DvyyT   %! B\\\2*  . )5R3)u U7B\]AKA4(=GL6];+@)''b xw\F 1/Z/+e>Twwv21'0 \\\B0M'H !#%#E###%#3B\^ *.F )5 @ 3-# "+'6'7''$'6777'&67''$ $$ ?AX,Rg P>ZVLBXY*[6|{ M~ʴ6m51,P-S14<3/H36&#"%2&#"#536'3#;#.=#535?>#"'&'5676&'&73"'&/3676#5&#"#3676#5&#"#%";275'2#5+"&46;54+"#'463"73275'2#5+"&46;54+"#'463%36;2+"&'#'36;2#5&#"#3#3#"!!327 &%!2#!"&546#3#3#275&#" N!1!) !'%T &282B#  --%   ' -/ 3%  7  %/ ''$"/- ')$"-) )-7F+#) )-8F+#- %!+ -- )!--d++)) %; ;$H'OM''GGerrR'+1n)J) {))3%!"+326767"&4636%##4672#"&'72654'&'6&#" 654&'67;326&#".'735!7>73  !F9 ! #AO͏g9FS=Z^?>K5XZ' `flx@{uojgcaK""+.4>./357365'3'>/"'.'>4.'.'#25#532654&#"3264&#"    = $4`D5%%''<){ V/HG  ! #3C%   /9!PP<)';7+/6@/-A?/1>+1%G) DE=9)85  ۖ!-H'- A1' 2D51'5s;es##Ff);31+:8'/??_?>31V) bhn|#@ ޷xrlifcR0 "+!54&"24&"264.'536576/3'6/"'.'>.'.'#=#=! ! 26767"&4632%##&72#"&'3254'&'6#&#"2654&'67;3264&#"&'735#!7673! 0367=#1##1'9')5)Z  : %Ko/%Z   ' (1w/FEP' `a^^Dd)f;>D``D7X7'z6/) 7 V{{}H9 '-sIV{{V),1] +8gg ))1%%1#&'''!  h  wn wvZ% P+1! !->,DJѦ K)*JNw =J/dD``a;w XJ_`D5%3V +{{{V?i%  H`}{ !a(+)/|! Z!# _ekv#@ ٱrnifc`N( "+3#4&"24&"2&'&'5357365'3'6/"'.'><.'.'#=#=% ! 2767"&4632%#7#472#"&'3254'&'6%#"&#"2654&'>7733264&#".'735#!76;!%7=V--%-!!-%5%%5G+!}'#Fj+()   !%   /q-H 3! PRsPP9V#V33r;NP9/L p/!/' 1 Jfgk>/3' d>JffJ#)N %/r1X u-""-!r6##6&1^,#& @/ p  y}T! + -  39A#; s31Ee7='W;QPwP3dqrI@NwP:-+H%iiiJ5Z=Ski L## #)iB # "0:M`hp#@ ɲokgcWNC;61,% "+32=4&#"%#3#3#5'?#'#'4632#"&5'33#57#!"3%26554'&'2#"'&547634&"24&"24./3657365'3'6/'.'>.'.'#03472=#>=  >77675p,kYZj"_""_#*"11.bba] C  +$N))!.!!.  l >\ $$  "   (c sjj U"U 5#;=!ܖ"#j'(!  = ##,D))E.!!. i   v2+  czksnK p$   _ ;2ǣljs %# "0CKchmt|#&,049>BALB@<:75420.+(&$"!~xutolieda\JF:1,% %"+32=4&#"%#3#3#5'?#'#'4632#"&52#"'&547634&"2#"'33#57#%265 3#?&'&# =457 &'3'&'5&'5&54327!"%#>77&'77&/37&'.'#037>776757'&'&''&'657365'3'6/'&'7&'3#5?575'756=p,kYZj"_""_#*"11.]+$U!.!!.RNI_bba ,*Kh u , O $ g `F .* JD C$h.&"    "#2   ̆ >F\K>*  p' *()()) (U"U 5#;=!ܖ"#j'(+#,D.!!. _! Ib!hua    h. O hG "` ( 'DCV%;"v2+. I  _ s T#3  ? <zksnK U\0L>X q -)b%)%q*)*X()  :+)*9=/ck{#GZdpsA8xqke\[QHD5|tljf;0" "+32767"&4632#77'&72#"&'2674'&'6"&#"2654&'>7;3264&#"&'?'#6763   %2#"&5463!"3%274&!2#5+"&6;54+"#'463";275%2#5+"&6;54+"#'463";275%2#5+"&6;54+"#'463";275&'54>2#742#2+532=4+536'&%?>#"'&'=676&#";2#&'73#;#.=#53553#3#%2&#536%3632#5&#72&#"#53632&#"#536%3#5+"&=46;2326=4&+".  XXXX@'`'`p<?YX@3T s) !b$PssrA5 %'/kEPssP"%-V Du^'86"+':8+45!) d %.     -%-    Z %-    -'3 T(k   !*! #  #z\NNh --    88o+-A+^?XXXT;VBXX?1=K SxsssP;aDWrs N+%% %5 uu9,0#84%)7-3'Q!Ff))P N!f))  N Pf))P  7   J 3+   'NR'3M X 1;hTX V V 5   D M#  -Yi}:B\$39>CGMScqy|A\|zxtqi\VSPJHGEA?<:740&$"l]GC=;#~pjd\5-"+?#"'#54'#5'7'+"''7&6;7&+"'7#5'?'753&5432;26=4&+"73#5+"&=46;2'2&#536#2&#536%2&#536'3#;#.=#5352?>#"'&'&7676&'&;'&'746#2+532=4+536'&7&'546#.#'";275'2#5+"&6;54+"#'463:";275'2#5+"&6;54+"#'463"#"&#"7.54632>77&'677'?>32#"'&54763'"3!2654&#57537&'?&'6767#73&'7#?&'3277#73'7&'6?&'7677674&57'&'#2367&'&'7#5#74'#7#7'7''7'?27&727&'32654'#"%32?654/67&'679  $ ,  XQ%j  YHZJ\y˸    *   H @ K  e     t)) ( $+?A   %    $7 :  >X;.+9D1 ! : @8'i! ;+$9BMA@ ?  2+ E##8 { U#`  C!#;Q*  E* "" S  / A-!E .<\ t-N>XA7D1A6%" | ?  %,  QXP@%% i YIZ'J\y  M  4   Ch Ch Ch =@e    }  : &p +    < P  == O  ZY%X>2NA,1Dh b>B ;.r!/ #,D::BN>+,i  4,3  ## ef# #:?P^  E)""g  GA/$!DN-"A1D`&6"{  d  u(<AEU@}VPFDB?>/*  "+!'7"3!26544#!"&5463&#"2654'#"747%'34737"+3264&#!7&'53#632#"&'#"&'&'"&46327>75!'7'7\e^`m+6:'y'99'D^_``+ ;'f)yL=8Y7D``7+ q\1+)V{{VJs-'P9H}{{V 7 */Pfd^`lX;VtqP`"d"# 1?-d-??--?u```Cd0 Zw1X%5D`^!)/) +)`! z|`H ^ %h@Vzz{+X7m@lffj+6:'y'99'D^_``+ ;'f)yL=8Y7D``7+ q\1+)V{{VJs-'P9H}{{V 7 */PfJgJLoOnNJfG'"d"# 1?-d-??--?u```Cd0 Zw1X%5D`^!)/) +)`! z|`H ^ %h@Vzz{+u/AQauz~!@¶}{xwhcXRHB60$ "+5353'34&#"#327#"54253362&#"#5#5354;#"3#"3!26544#!"&5463&#"2654'#"747%'34737"+3264&#!7&'53#632#"&'#"&'&'"&46327>7)))X1.^X%/)!  9/+$$+6:'y'99'D^_``+ ;'f)yL=8Y7D``7+ q\1+)V{{VJs-'P9H}{{V 7 */Pf ++{:3!im3%{% } 9' "d"# 1?-d-??--?#```Cd0 Zw1X%5D`^!)/) +)`! z}`H ^ %h@Vzzz+V%  "+  !553)'#TZ @ VUJ( "+5;#3#3+=73753#5'/#5!0;!!!3!!!!3!!5#!!!#!5!!5!5!%NNNNNN%%73%%+/%mP1P/P/{L/mm{/""N.*u$'xL11`01;2w2a/12/B={/wP` #/ &$"+"&'&"''7&32732>764'ED)G'=zV:BJ_P0@; N|s6i)83#2vu"#RV"#d:v ɪCԌ #P<&Zv7"+47.Z 6dD63 eY:=+KD+x"+&'>567 6dD6 eY;Y=0KD"+!  J[G##G\J  y*$ 0/,,.1 %A"+!&47>54.'&47N J\G""H\J % 00+4,/0 %f%"+3"&76'&7>5&63mkrqlcG   i_   &.:ng(zgj|+ oLD /Zd%"+7&675.76&#&472#&47>krqldG  i_  &.:u$ng(ygj{Գ oL 0Y= "+"&54>7>762"&462RVB 1)?Z@@ZJL;qA`_) F?RLZ@@Z?^?'/.* "+%4&4632#"&54>7676=47226"&4621!3i\%R53V"!=4mp=@Z??Z% B')/dUP9bT-%?(7^Z^ǃ9gGf@Z@@Z?bh  "+&'567&'567?y*ѩ) y)Ѫ)#"ԉ#"bh  "+&'67&'6'&'67&'6wy)Ѫ)y)Ѫ)##Ӊ##bDh  "+&'567?y (˯' #ݚ"#bh  "+&'67&'6'y '˰' #ݙ#RDb "+!"5463!2+F!%D&7(>7#"+4&'&7672#"&54632325/V VLMT%K/ )F(V-#"gP?F/)3V . "+%2654&'"546?&74632372#"&#"!ddwZ3/T 79)77yfӾ3N`GŁN3oo"+2 53m%7M`@ 2 N    b  ` M=M= M =K >@<   b  ` U M= M =K >Y@^]WUQOMKHFA?<:$###GC+%&#"&47>5!&#"&47>5#"=4;7632>32#"&'&#"32'##".'&#"!546lALw H)h-LFP X- /dDTo1Bo)%'O+B1% +X3H{9 11 EDCA><97$##C%6 +%&".7>54&#!&#"&47>5#"=4;57672#"&'&'"!2721b Z/!/1g aL X. /ehu5#3=dmA{9 11 :{PB{9 11 :{% ){^!3GFCA##G#%)F +%&#"&47>547&'.#"32'#&#"&47>5#"=4;546326723\\P{P)%_1=B)/1]OL X. /i딍F77y; 11 <{% ,/5#Nn` )y9 11 :{% )=0"R%w@ Le@B  b  `  U M ==M =L >Y@vurndcb`[YWUPNJHDB#GG&#!+#"&'&#"!5&4&#!&#"&47>5!&#"&47>5#"=4;467632>32#"'&#"!272&#"&47>  %%#\)<5U'+XHLwH)f-LJD X- /h[h?kq/^wQ5!^;D(A4\ LH P))'h#R9HRjB'{9 11 'H/=]8dk!/qGoty=-y; 11 <%jx@ pj!@7bUM= M = L  >Y@utnlhfb`][XWTPIHG#%*I# +#"'.#"!56&#"&47>576'&'.#"32'#&#"&47>5!&#"&47>5#"=4;47>32>32>72 ;X:3X3\ / P)%e3/k1+X#H)f-L+ X- /d6)amR=-%;-DXX9Rry; 11 <{ ,/5\N\ol ){9 11 @A  b M=M = M =K = M  >Y@FDA?;97532##"##8C+%&#"&47>5454.+"'54327>3 32'#327#"7#"=4732654#"Z3a 49`3 Z /Nމ31'HIu{ ^)F{; 11 <{  )b )dY:!i?TbPat@q-  L J <  bM= M=M=M = M = M >_]SQNMIGEC86$"###""+463 32'#327"5#"=472654&#""'.#"#"'&#"4'6232654&'.546;.Bհu /'HI%w ^V}R . /@/7Y]pDjg1dV , !^bBlT}{fu :-wu )dX9"m?vLXcml =R# GsRTur-F^Hl<7P=_[Qe1-)# R<EE?>EHEHGFA@>D?D*L+J +%&!"&47>7677&#"&472>&'!"7!#3 1 5)- & %/ 11  [y" $-$.9> @-Z  Z U  UN >Y@%%><;:7520%.%-)' $# +2$3 #"$#"&47>54&'&472654&#3 54&#"'#6//k#qR1oӑf!B s??s PיǍ#hXL8=E)P))HsC1 <{5{;2 !_덁-':qK(PX@&ZXM  =N >@$ZX UN >Y@ 86/-#" ' & +%4'# 2$3 #"$#"&47>54&'&473264.#"!G`hu{ s??s ` TZAmJ=Vmmqٴ;1 <{5{;2 %%jQ-7 1BN@8  @7 ZZ    U UM= L >Y@ LKED?<543/,+&%" BB +26762"'.+3!26&#"#&47>54&'&47)272'.+"5#3&5}:1  1RTJI%#3b{s@@s5/!#'Vj#'91[ {6% 70%X 1 <{5{;2 F{d3'fX! o1 H@0/ >; B @7Z Z  U  U M =L >Y@ DC=<9752,+*&#" HH +5#03&5#&#"&47>54&'&473!272.+"326762"'.?sBs@@s]6*@'VjJ}: 11 RX!d{; 11 <{5{;2 v {dFR1[ {6% 70%BhD2?@<2-, <b`M =N>%%*H'+%3274&'&47327! 4$323.#"QhH`=;{ 1-!w>-F/˜thl}c19! 5  6 +3zN]z ]-&nK(PX@K =K >K1PX@UK >@ZUK >YY@ 33+%%"&47>54&'&473%'# 3?@s s??s ʹJs@={; 11 <{5{;2  1 ;{mu'-)1f,@'Z`UIMAY@ C%$'+!"&5463232>54&'&47327$&##N1/d-lA -)E@FͲ}b<ģ /hs7)X=19#%5˪{;2  1 =9oVdof-VZK1PX" <" K1PX@#  b  U M  >@(   Z  ` U M  >YY@ZYXWVUPKC+CC +%&#"&47>54&'&4732767>.#&47327"&#""54'&'#3?@sB s??s ;s@WEX A &  5;`)L*/  7S ):j={; 11 <{5{;2  1 ;{Ih$2  1  3+R! 1 \2Jdm-'.K1PX#<#K1PX@ZUK >@$ZZUK >YY@.-)( '% +!&47>54&'&47327;2676&## 3-d s??s ;s@4-jm%#!"7i1 <{5{;2  1 ;{/7m!-GKK(PX@3b`  ` K =  K >K-PX@1b`  ` U  K >@5b`  ` U  K  =>YY@HHHKHKJIBA>:76"G"+%#'"'#&#"&47>76&'&47327%&#"&47>&3b53 XN1^ {ݺ{T< R (m >   d/=(((Y(#( YvwX'`T / 1 PbLq.2  1 6W101 11 -0*VLTD '@$ <M =M>$)$"+! ! 2#"TdRS{gZh'/Nje> 5NAu9/8B?@(IU M = K >Y@10=<:9530818!3(CB +"#"'&#".767674.+.73632"3265#264`/^jL?sͶB  /F   ͑-wAmͪb B7 h {; 1 2 A"*?!2 '=fg+ OLq7D 6v@1" @%bbM =M>Y@64/.-+!  +6'4'.'.54632.'#"&#&'6!2S=T=_ u 4!j >? a: #@'CqV=p7U '<4{ h(7/ `D<q@ /&3"@" U M =K >Y@:75495C$ +#67&#"&47>54&+""'6763!26567.+"F=F?F͞F>7TA+# !5#35R5>m{; 11 <{f3FC{   u{CH7 ->LXJ@ZUM>Y@ C-N&+2>54.'&47327'".54&'&473 7#.1/jdN- 55F!J3!Gy͍{d:;c'{AB5LfT*~e/Vo-f@\xRBC% 2  1 LT-wf@Bn{Ly=2  1 ;j]-a]B1-29T87@bS>Y@ 333939K>C+&47327"&.'.'&4732736&!7b .@-Y Y!-v/5T g&%F D'*k1Z2  1 8*+ L !"x; 0  1 =[5TTJ'-Waƶ]\ K1PX@-b  `  `  U>@2  b  `  `  I   S>YY@XXXaXaWVPOLHEDC&#$K+64.'.7327"'&#"#".'.'&473 73654/.'&4732737&/H' =X  Vl'/ 5<`X3J# ?8> ZJ )9 -'=#;9==2  1 C5/293# ZN$2  1  2a''_/G2  1 - pPN-fj@#UPO;54K(PX@<  Z  `  `K = L  = K  >@:  Z  `  ` U L  = K  >YY@gggjgjihYXTQGFC#D%2B+573 73#27>&/&'57327#2&!"'54?>&'&"'"'547>7654'&'&3/P B!7='#+ID !jdu? )+  #:& 1{)P&DBVa X9''#$' ))E %1+ )) '== )) '#J;#}uT-V\K&PX[X< K(PX@( K =  L = K >@!  I   S K >YY@WWW\W\ZYLKGC@>333 +%%"&47>5<.'.'&#&473%""67>.#&47327 qE  F 6<<  {N#    +Rim= 0! b{; / 1 <{ [* 0  1  9  21  '8>b!{L%q_<X1^ '2-X`1^ tM5;uXmbZb+\f{XRuPjZ9oZh^fff{fz^(y+LtzL`u9!LSL7I 7GTKB$"JlL PLz-BM+5-%Q-V-T&H-b3?-  !dL775M!;ffFt?XRRF-y"Ff{t\tN^uStPXGGGJz^+Ltttt```` LLLLLfLI I I V7 S%JJJJJJ~PlLLLLL+#+5++FV-TTTTTf{N?-?-?-?-JJJ+LlL+LlL+LlL+LlL9P PtLtLtLtLtLzLBzLBzLBzLBMM`+`+`+`+5`+5V5u-&)9%9%9Q%9%9  V-V-V-V39-LTLTLTV9F---7b7b7b7b33'I ?-I ?-I ?-I ?-I ?-I ?- TdLTdLTdLS1 "  %+H+LL9VZFz7&V"N1LQ)`!?-h-LLT$JJ; &#ZL&p33 ?-S1= TdLV^/Zjn-nT)(zK3M "SP$+%$-J`+LTI ?-V?-V?-V?-V?-+JJ~PzLzLBLTLTVSR- O$PzLB3#QV-J~PLNJJtLtL`+`+ LTLT--I ?-I ?-7b3FM+Fn`TRFJtLLTLTLTLT3;-/-FJ+Ll9bdLv7I Ct;-XJH%H7vHdLPQPjF`1-Z+NNF|QJJ+F/ |Q('('Z1?/)K %1 %QAB//+~=Tn;FB+59!'))bnnVj)?-=3)dLnBSRzXn9nNn3JV==F=vM-z/Jn9nNjL PP)33-5%%-d#Dtt+ 5-)!oX;fBX7d^lwlw$$$$hb Rf1Z1J&\JJBFPP1R1F+SV oPdpI9t%5~5~5~5~5~5ff~ hA)-);===)======h)D)F!\sBJ\\dwHH33` ?\m? ?{b#Bf9''y="\ybh\#mhhhyj1/P/XfA\~?=tTL`C!/\LVS&$1GnZ` F-Z99?3F"d !L-ZN99P? =?-R'hPT13oBL73P ;CF?3T3CFD`1j ;j HJ1LT!GH oKdKdjH1FPL^L^LCyPBPBttF7``  b EFtvZ !LVS+LdLGd35bx^= JF=S=M5L|X===7p==T=&lL5AP!=OFF7==`=LLlHS=Pb+5+-l=tE===mbFE^9C 7^ j |7Vo?R~-FLLbKbKmbFmbF!I-S&S=S+=-vZ`XBtF#1#|B=K=hFtH+LlL~7(F; D C#XBtE5tS5`!#;F#|Bv=; DJJ~PtL&V+&V+vZ|XVSR==LTLTLT^`3O5#9b=^L1 RRH )SJ=)+DDG7ONH9(7B97`(LAD!XD?9D-X5D)t/\7C7ffBt?t@R\` 17#ss6#t/A7t/t/Z  ^~b0\7+ ~b0A771-J+LlL P P P P PtLtLtLtLtLz-zLBMMMMM`+` +9%999 !Q-!Q-!Q-V-V-V-V-LTLTLTLTS&S&----7b7b7b7b7b3333I ?-I ?-I ?-I ?-I ?-7 7      G!G!TdLTdLTdLM XS1S S1LJJJ JJJJJJJJtLtLtLtLt8tLtLtL`+5`+5LTLTLTL8LTLTLTLTLTLTLTLTI ?-I ?- ?- ?- ?- ?- ?-=#E9^\VFFFFFFFF-Z-Z-Z-Z-Z-Z`+t9999999999999999????????h/CA=TTTTTT&733333333vCFCFCFCFCFCFCFCF VS/FF-Z-Z9999?^?TT33CFCFFFFFFFFFl=9+GX9999999999999999 7 $ A &  $CFCFCFCFCFCFCFCF\n{FFFFFFF9rBtB^j9999999999bOHHR??????n#`(;9f33333o3o33ffmCFCFCFCFCF"(ZPX;$ S5RRDbjH`H9{ $$$$bZZeuduuuIu u q^q^bJZ!|^f^yWt?7tWtbt\tXt`tRt9tHtPJ5#t?tt\tNtWtbtNtXtTtDt9tHtPJ5t?Ft/t!tBtI#p93+%p#- =~-FF+LnZ q qjZ1OTOTXf?#L/?#-\xNZZ==)+DIHxGoG\G}G^GLGDGG`G}GRGZGZw\?";??` f? !!!=F~)!/)7Q77 =  7!))%lL PQ-qZ!q/^% yP ydddddee y y =dy}y}yyy yJyhJb yhdqie`J3H3="5 ; ;; ZL;?V1f{f{f{7#7#mND\LGLSS=S=SP %)AR77s7s7{7o7o7{7{7777EE/7777-L-P-L-PssssM{M3))M{ZZhAsnz111J;+ZZZZBsB333333w317ueeeeeeeeeeeeeeeeeeeethxxv^z | eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeedddddddddd7Y;Y7Y7Y7Y7Y7Y7Y7YKYdSZSZSZSZZZS;S;dVddddbbdddddP11&1F$;}}}R);twtwtsVw(dPPz;d"PMdddPdhlZ+Fy(L7qP(F3ZEZZZTA1wdwjwV}VwA)(JJJ J{f`/dddddddddd)ffJ9?9SJ$';#%TIB!PZ;L1^1J1^1F)Z)Z)7VG7IT77? Aq A AqjjN112-y# 9fA?FFFooRjZFfbPb`" I J{+QZXf^9f3b\bT-LI "9-j%Z%$%%Q%;%%-LL5LG-x- n nX"-S--P=- LbA)-H'b753Pr BLBJFSLF|R|nJrJV1BLFLJKd;H9b!^!A-%L Rr r r r r r K LJJJJ|=|R|1|!=BLLLLLn;L9999%F%R&d~! OLJ=r r LLBJJSLrJr#BBBLJJKdKdKd;H;H99LLLrJ;H;HKd|RV v;c-%$#OXf+f?\!f-FfLL-w/mb\3=mZRhZBd^Dmby^PpppSRRpA T` A717t7I#pv;+9 )1-99GTb/++{VS X ;  `FF|HCV3ZV|Z##9CV(PbZb+A77Mz^XX#R- V%z-Q-%%|pb-pp  B^u!L7t bhddddLHt \   \ |  Dp8P X@|( l!H"$$%&(\)X*T+@,-\./11l122H22345P6l7 89:;<=>l?@x@ABCxDPE EFGHIJXKK<KLxLxLMNOPQ8RPRSTUXUUWtWX(XY[T[\]^^_h_`HacPefxgijllmnpqsDtvx@y$zz{}~t<LX @`$`4`(Pp((x<X` l4 <`<pt4(θ$\d l x|$0H lH<pLh@  X ,  < ph( "$#4$&'\(`)*,-x./12$34689d:<=h>?ABtCEF GHIJLMDNTO,OPQlRDS\TdUW0WY8YZ\D]^`aabdHef|g<hijkl`m$nhoLp`qsstuvxwxyz{} ~,@xp0LpL 0P@h4$DdpLPX$\d@  $͔$(ըָDیH߰44PhpTH\L,X H    XX8 <,H P!"$@$%&'()*,--/<0124 5,6x789:p;= =?|@B0CDEGHIJKLMxNOXP4QRSxTU VTWYZ\[L\]h^$^_`acldddeTfPgxhhjXklnpqrdrt<uvPwx<yhz\{l||}~  pt@xdH<`D|@Hd,t\(pHpL|4t0|4x(|\plƴ`8ɈLXHϰЀ dѠҰ0\ӈd հ4֌<״,ظ8$ڀx(|$ݤޤdpL00L($|88(p$p\,x$`X` @,DpTL$    T 8 L <P$h(,` !"#$%&((**,,-|/4/0123457T89x:p;=?p@ApBCDEFlGHIJHJKLdMlMOP@QQRSxTTUVX XYxZL[<\@]\^T_T`bcdeLf`ggijPklmnno`p pqlrs(stduuv vw|xLy{}x~|( $h@XLTxx0dH(pPXhTʐ`̨ͼδϘиԸ$  Xݰވ@`l440P pDPh|   PT4tx !#D&$')+P,./1\3H468D9:\?@B|CEFGTHIKLMNNP4QRTUVXYp[l\^H_<``abcexgDhjpkm8n`oHpxq\rstuvxyz| }8~L( x|x`<PDp(T8P(8tƠɔ̔,pd$ DlDX |(T@4THh<pdHL D t  0 l TT !"$<%x&'(*+-l./01p1235|7`9|;>@|BdDF8GJLXNQ TWXY []h`bPdfhjnptuvHwyLz|8}(dlH<d$4T 080<T|Tht(hä(@Ҙl <`(, Th4  $h D ht`|"$&8)*,@-/13h568;(>A4DGTJdO PRDTdVX[$\_,`b@d`fhkpo(qsttvxz| tdl\LX$\ dx`t,0DTdPŜ8ƄllTt˰̨8͈00ϔHќ 8԰(<ڴ4`ބTPtT(pLt4L$`,PlL00Tp$ ` L D  PLD L|`,<hL P !D!"$'(D*8,X.4/P024(57`8 89x:p; @ B D F H J` LL N P Q R R@ R` R R R S S< S` S S S Tp T U U U V V` V V WH W W W XL X X ]l ^\ _ a| b( cL dX eD e f\ f gl g hT h i j j k l( l mh m n od p< p q| q r s th t u u v4 vp v wD w x x` x z| {$ | }@ ~\   8 D  , L  8 8 D p D  @   t H  T X ( T $   ` H x < l $  ü x ` Ǥ < < ɜ < ʼ d l 4 Ψ T ϼ 4 | | | Ԝ ՘ @ $ D P  x ` ߸ ,  P x 8   ( x ` 8        H X l  P        8     D @ @      h < 8   0 ! " # $ %d & & 'D ' ( )| * * +H + , -L . . / 0| 1h 2 3 3 4 5 6 84 8 9 :| ; ; <\ =X >L ? ? @8 A A B C D Et F$ F G Hd I I J K LT M M Nh O( O Px Q0 R, R R S( Sh S T T U V W X Z, [D \$ \ ] ^l _@ ` ` a b8 b c d\ d e f( g g h iH i j k0 m n o pX p qT q r s s t t uH u v v w0 w xD x y y z< zp z {, {` |l h P ` \  h h   ,  P L , ( ( T  | $ T X  Ӡ l ׬ ٜ D ژ t d `  @ t ߀ 8 P ` l ` @  ` | h l ( \  $ H    t-4Ah>+*2\"k     5C& 8i   T ! D?    : : jK Linux Libertine by Philipp H. Poll, Open Font under Terms of following Free Software Licenses: GPL (General Public License) with font-exception and OFL (Open Font License). Created with FontForge (http://fontforge.sf.net) Sept 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011,2012Linux LibertineRegularFontForge 2.0 : Linux Libertine : 2-7-2012Linux LibertineVersion 5.3.0 ; ttfautohint (v0.9)LinLibertinePhilipp H. PollPhilipp H. Pollhttp://www.linuxlibertine.orghttp://www.linuxlibertine.orgGPL- General Public License AND OFL-Open Font Licensehttp://www.fsf.org/licenses/gpl.html AND http://scripts.sil.org/OFLLinux Libertine by Philipp H. Poll, Open Font under Terms of following Free Software Licenses: GPL (General Public License) with font-exception and OFL (Open Font License). Created with FontForge (http://fontforge.sf.net) Sept 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011,2012Linux LibertineRegularFontForge 2.0 : Linux Libertine : 2-7-2012Linux LibertineVersion 5.3.0 ; ttfautohint (v0.9)LinLibertinePhilipp H. PollPhilipp H. Pollhttp://www.linuxlibertine.orghttp://www.linuxlibertine.orgGPL- General Public License AND OFL-Open Font Licensehttp://www.fsf.org/licenses/gpl.html AND http://scripts.sil.org/OFLQ t  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghjikmlnoqprsutvwxzy{}|~      !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~      !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~      !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~      !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~      !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~      !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~      !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~      !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~                           ! " # $ % & ' ( ) * + , - . / 0 1 2 3 4 5 6 7 8 9 : ; < = > ? @ A B C D E F G H I J K L M N O P Q R S T U V W X Y Z [ \ ] ^ _ ` a b c d e f g h i j k l m n o p q r s t u v w x y z { | } ~                            ! " # $ % & ' ( ) * + , - . / 0 1 2 3 4 5 6 7 8 9 : ; < = > ? @ A B C D E F G H I J K L M N O P Q R S T U V W X Y Z [ \ ] ^ _ ` a b c d e f g h i j k l m n o p q r s t u v w x y z {uni00A0uni00AD two.superiorthree.superioruni00B5pilcrow one.superiorAmacronamacronAbreveabreveAogonekaogonek Ccircumflex ccircumflex Cdotaccent cdotaccentDcarondcaronDcroatEmacronemacronEbreveebreve Edotaccent edotaccentEogonekeogonekEcaronecaron Gcircumflex gcircumflex Gdotaccent gdotaccent Gcommaaccent gcommaaccent Hcircumflex hcircumflexHbarhbarItildeitildeImacronimacronIbreveibreveIogonekiogonekIJij Jcircumflex jcircumflex Kcommaaccent kcommaaccent kgreenlandicLacutelacute Lcommaaccent lcommaaccentLcaronlcaronLdotldotNacutenacute Ncommaaccent ncommaaccentNcaronncaron napostropheEngengOmacronomacronObreveobreve Ohungarumlaut ohungarumlautRacuteracute Rcommaaccent rcommaaccentRcaronrcaronSacutesacute Scircumflex scircumflexTcedillatcedillaTcarontcaronTbartbarUtildeutildeUmacronumacronUbreveubreveUringuring Uhungarumlaut uhungarumlautUogonekuogonek Wcircumflex wcircumflex Ycircumflex ycircumflexZacutezacute Zdotaccent zdotaccentlongsuni0180uni0181uni0182uni0183uni0184uni0185uni0186uni0187uni0188uni0189uni018Auni018Buni018Cuni018Duni018Euni018Funi0190uni0191uni0193uni0194uni0195uni0196uni0197uni0198uni0199uni019Auni019Buni019Cuni019Duni019Euni019FOhornohornuni01A2uni01A3uni01A4uni01A5uni01A6uni01A7uni01A8uni01A9uni01AAuni01ABuni01ACuni01ADuni01AEUhornuhornuni01B1uni01B2uni01B3uni01B4uni01B5uni01B6uni01B7uni01B8uni01B9uni01BAuni01BBuni01BCuni01BDuni01BEuni01BFuni01C0uni01C1uni01C2uni01C3uni01C4uni01C5uni01C6uni01C7uni01C8uni01C9uni01CAuni01CBuni01CCuni01CDuni01CEuni01CFuni01D0uni01D1uni01D2uni01D3uni01D4uni01D5uni01D6uni01D7uni01D8uni01D9uni01DAuni01DBuni01DCeturneduni01DEuni01DFuni01E0uni01E1uni01E2uni01E3uni01E4uni01E5Gcarongcaronuni01E8uni01E9uni01EAuni01EBuni01ECuni01EDuni01EEuni01EFuni01F0uni01F1uni01F2uni01F3uni01F4uni01F5uni01F6uni01F7uni01F8uni01F9 Aringacute aringacuteAEacuteaeacute Oslashacute oslashacuteuni0200uni0201uni0202uni0203uni0204uni0205uni0206uni0207uni0208uni0209uni020Auni020Buni020Cuni020Duni020Euni020Funi0210uni0211uni0212uni0213uni0214uni0215uni0216uni0217 Scommaaccent scommaaccent Tcommaaccent tcommaaccentuni021Cuni021Duni021Euni021Funi0220uni0221uni0222uni0223uni0224uni0225uni0226uni0227uni0228uni0229uni022Auni022Buni022Cuni022Duni022Euni022Funi0230uni0231uni0232uni0233uni0234uni0235uni0236dotlessjuni0238uni0239uni023Auni023Buni023Cuni023Duni023Euni023Funi0240uni0241uni0242uni0243uni0244uni0245uni0246uni0247uni0248uni0249uni024Auni024Buni024Cuni024Duni024Euni024Faturneduni0251uni0252uni0253cturneduni0255uni0256uni0257uni0258uni0259uni025Auni025Buni025Cuni025Duni025Euni025Funi0260uni0261uni0262 gammalatinuni0264uni0265hhookuni0267uni0268uni0269uni026Auni026Buni026Cuni026Duni026Emturneduni0270uni0271uni0272uni0273uni0274uni0275uni0276uni0277uni0278rturneduni027A rhookturneduni027Cuni027Duni027Euni027F RsmallcapRsmallinverteduni0282uni0283uni0284uni0285uni0286uni0287uni0288uni0289uni028Auni028Buni028Cuni028Duni028Euni028Funi0290uni0291uni0292uni0293uni0294glottalstopreverseduni0296uni0297uni0298uni0299uni029Auni029Buni029Cuni029Duni029Euni029Funi02A0uni02A1uni02A2uni02A3uni02A4uni02A5uni02A6uni02A7uni02A8uni02A9uni02AAuni02ABuni02ACuni02ADuni02AEuni02AF h.superiorhhook.superior j.superior r.superiorrturned.superiorrhookturned.superiorRsmallinverted.superior w.superior y.superioruni02B9uni02BAuni02BB afii57929 afii64937uni02BEuni02BFuni02C0uni02C1uni02C2uni02C3uni02C4uni02C5uni02C8uni02C9uni02CAuni02CBuni02CCuni02CDuni02CEuni02CFuni02D0uni02D1uni02D2uni02D3uni02D4uni02D5uni02D6uni02D7uni02DEuni02DFgammalatin.superior l.superior s.superior x.superiorglottalstopreversed.superioruni02E5uni02E6uni02E7uni02E8uni02E9uni02EAuni02EBuni02ECuni02EDuni02EEuni02EFuni02F0uni02F1uni02F2uni02F3uni02F4uni02F5uni02F6uni02F7uni02F8uni02F9uni02FAuni02FBuni02FCuni02FDuni02FEuni02FF gravecomb acutecombcircumflexcomb tildecomb macroncombuni0305 brevecombuni0307uni0308 hookabovecombuni030Auni030Buni030Cuni030Duni030Euni030Funi0310uni0311uni0312uni0313uni0314uni0315uni0316uni0317uni0318uni0319uni031Auni031Buni031Cuni031Duni031Euni031Funi0320uni0321uni0322 dotbelowcombuni0324uni0325uni0326uni0327uni0328uni0329uni032Auni032Buni032Cuni032Duni032Euni032Funi0330uni0331uni0332uni0333uni0334uni0335uni0336uni0337uni0338uni0339uni033Auni033Buni033Cuni033Duni033Euni033Funi0340uni0341uni0342uni0343uni0344uni0345uni0346uni0347uni0348uni0349uni034Auni034Buni034Cuni034Duni034Euni0350uni0351uni0352uni0353uni0354uni0355uni0356uni0357uni0358uni0359uni035Auni035Buni035Cuni035Duni035Euni035Funi0360uni0361uni0362uni0363uni0364uni0365uni0366uni0367uni0368uni0369uni036Auni036Buni036Cuni036Duni036Euni036Funi0374uni0375uni037Auni037Buni037Cuni037Duni037Etonos dieresistonos Alphatonos anoteleia EpsilontonosEtatonos Iotatonos Omicrontonos Upsilontonos OmegatonosiotadieresistonosAlphaBetaGammaEpsilonZetaEtaThetaIotaKappaLambdaMuNuXiOmicronPiRhoSigmaTauUpsilonPhiChiPsi IotadieresisUpsilondieresis alphatonos epsilontonosetatonos iotatonosupsilondieresistonosalphabetagammadeltaepsilonzetaetathetaiotakappalambdanuxiomicronrhosigma1sigmatauupsilonphichipsiomega iotadieresisupsilondieresis omicrontonos upsilontonos omegatonosuni03D0theta1Upsilon1uni03D3uni03D4phi1omega1uni03D7uni03D8uni03D9uni03DAuni03DBuni03DCuni03DDuni03DEuni03DFuni03E0uni03E1uni03F0uni03F1uni03F2uni03F3uni03F4uni03F5uni03F6uni03F8uni03F9uni03FBuni03FDuni03FEuni03FFuni0400 afii10023 afii10051 afii10052 afii10053 afii10054 afii10055 afii10056 afii10057 afii10058 afii10059 afii10060 afii10061uni040D afii10062 afii10145 afii10017 afii10018 afii10019 afii10020 afii10021 afii10022 afii10024 afii10025 afii10026 afii10027 afii10028 afii10029 afii10030 afii10031 afii10032 afii10033 afii10034 afii10035 afii10036 afii10037 afii10038 afii10039 afii10040 afii10041 afii10042 afii10043 afii10044 afii10045 afii10046 afii10047 afii10048 afii10049 afii10065 afii10066 afii10067 afii10068 afii10069 afii10070 afii10072 afii10073 afii10074 afii10075 afii10076 afii10077 afii10078 afii10079 afii10080 afii10081 afii10082 afii10083 afii10084 afii10085 afii10086 afii10087 afii10088 afii10089 afii10090 afii10091 afii10092 afii10093 afii10094 afii10095 afii10096 afii10097uni0450 afii10071 afii10099 afii10100 afii10101 afii10102 afii10103 afii10104 afii10105 afii10106 afii10107 afii10108 afii10109uni045D afii10110 afii10193uni0460uni0461 afii10146 afii10194uni0464uni0465uni0466uni0467uni0468uni0469uni046Auni046Buni046Cuni046Duni046Euni046Funi0470uni0471 afii10147 afii10195 afii10148 afii10196uni0476uni0477uni047Cuni047Duni047Euni047Funi0483uni048Cuni048Duni048Euni048F afii10050 afii10098uni0492uni0493uni0494uni0495uni0496uni0497uni0498uni0499uni049Auni049Buni049Cuni049Duni049Euni049Funi04A0uni04A1uni04A2uni04A3uni04A4uni04A5uni04A6uni04A7uni04A8uni04A9uni04AAuni04ABuni04ACuni04ADuni04AEuni04AFuni04B0uni04B1uni04B2uni04B3uni04B4uni04B5uni04B6uni04B7uni04B8uni04B9uni04BAuni04BBuni04BCuni04BDuni04BEuni04BFuni04C0uni04C1uni04C2uni04C3uni04C4uni04C7uni04C8uni04C9uni04CAuni04CBuni04CCuni04D0uni04D1uni04D2uni04D3uni04D4uni04D5uni04D6uni04D7uni04D8 afii10846uni04DAuni04DBuni04DCuni04DDuni04DEuni04DFuni04E0uni04E1uni04E2uni04E3uni04E4uni04E5uni04E6uni04E7uni04E8uni04E9uni04EAuni04EBuni04ECuni04EDuni04EEuni04EFuni04F0uni04F1uni04F2uni04F3uni04F4uni04F5uni04F6uni04F7uni04F8uni04F9 afii57799 afii57801 afii57800 afii57802 afii57793 afii57794 afii57795 afii57798 afii57797 afii57806uni05BA afii57796 afii57807 afii57839 afii57645 afii57841 afii57842 afii57804 afii57803 afii57658uni05C6 afii57664 afii57665 afii57666 afii57667 afii57668 afii57669 afii57670 afii57671 afii57672 afii57673 afii57674 afii57675 afii57676 afii57677 afii57678 afii57679 afii57680 afii57681 afii57682 afii57683 afii57684 afii57685 afii57686 afii57687 afii57688 afii57689 afii57690 afii57716 afii57717 afii57718uni05F3uni05F4uni1D15 a.superioraturned.superioruni1D45uni1D46 b.superior d.superior e.superioreturned.superioruni1D4Buni1D4C g.superioriturned.superior k.superior m.superior eng.superior o.superiorcturned.superioruni1D54uni1D55 p.superior t.superior u.superioruni1D59mturned.superior v.superioruni1D5Cuni1D5Duni1D5Euni1D5Funi1D62uni1D63uni1D64uni1D65uni1D66uni1D67 c.superior f.superior z.superioruni1E00uni1E01uni1E02uni1E03uni1E04uni1E05uni1E06uni1E07uni1E08uni1E09uni1E0Auni1E0Buni1E0Cuni1E0Duni1E0Euni1E0Funi1E10uni1E11uni1E12uni1E13uni1E14uni1E15uni1E16uni1E17uni1E18uni1E19uni1E1Auni1E1Buni1E1Cuni1E1Duni1E1Euni1E1Funi1E20uni1E21uni1E22uni1E23uni1E24uni1E25uni1E26uni1E27uni1E28uni1E29uni1E2Auni1E2Buni1E2Cuni1E2Duni1E2Euni1E2Funi1E30uni1E31uni1E32uni1E33uni1E34uni1E35uni1E36uni1E37uni1E38uni1E39uni1E3Auni1E3Buni1E3Cuni1E3Duni1E3Euni1E3Funi1E40uni1E41uni1E42uni1E43uni1E44uni1E45uni1E46uni1E47uni1E48uni1E49uni1E4Auni1E4Buni1E4Cuni1E4Duni1E4Euni1E4Funi1E50uni1E51uni1E52uni1E53uni1E54uni1E55uni1E56uni1E57uni1E58uni1E59uni1E5Auni1E5Buni1E5Cuni1E5Duni1E5Euni1E5Funi1E60uni1E61uni1E62uni1E63uni1E64uni1E65uni1E66uni1E67uni1E68uni1E69uni1E6Auni1E6Buni1E6Cuni1E6Duni1E6Euni1E6Funi1E70uni1E71uni1E72uni1E73uni1E74uni1E75uni1E76uni1E77uni1E78uni1E79uni1E7Auni1E7Buni1E7Cuni1E7Duni1E7Euni1E7FWgravewgraveWacutewacute Wdieresis wdieresisuni1E86uni1E87uni1E88uni1E89uni1E8Auni1E8Buni1E8Cuni1E8Duni1E8Euni1E8Funi1E90uni1E91uni1E92uni1E93uni1E94uni1E95uni1E96uni1E97uni1E98uni1E99uni1E9Auni1E9Buni1E9Cuni1E9D Germandblsuni1E9Funi1EA0uni1EA1uni1EA2uni1EA3uni1EA4uni1EA5uni1EA6uni1EA7uni1EA8uni1EA9uni1EAAuni1EABuni1EACuni1EADuni1EAEuni1EAFuni1EB0uni1EB1uni1EB2uni1EB3uni1EB4uni1EB5uni1EB6uni1EB7uni1EB8uni1EB9uni1EBAuni1EBBuni1EBCuni1EBDuni1EBEuni1EBFuni1EC0uni1EC1uni1EC2uni1EC3uni1EC4uni1EC5uni1EC6uni1EC7uni1EC8uni1EC9uni1ECAuni1ECBuni1ECCuni1ECDuni1ECEuni1ECFuni1ED0uni1ED1uni1ED2uni1ED3uni1ED4uni1ED5uni1ED6uni1ED7uni1ED8uni1ED9uni1EDAuni1EDBuni1EDCuni1EDDuni1EDEuni1EDFuni1EE0uni1EE1uni1EE2uni1EE3uni1EE4uni1EE5uni1EE6uni1EE7uni1EE8uni1EE9uni1EEAuni1EEBuni1EECuni1EEDuni1EEEuni1EEFuni1EF0uni1EF1Ygraveygraveuni1EF4uni1EF5uni1EF6uni1EF7uni1EF8uni1EF9uni1EFAuni1EFBuni1EFCuni1EFDuni1EFEuni1EFFuni1F00uni1F01uni1F02uni1F03uni1F04uni1F05uni1F06uni1F07uni1F08uni1F09uni1F0Auni1F0Buni1F0Cuni1F0Duni1F0Euni1F0Funi1F10uni1F11uni1F12uni1F13uni1F14uni1F15uni1F18uni1F19uni1F1Auni1F1Buni1F1Cuni1F1Duni1F20uni1F21uni1F22uni1F23uni1F24uni1F25uni1F26uni1F27uni1F28uni1F29uni1F2Auni1F2Buni1F2Cuni1F2Duni1F2Euni1F2Funi1F30uni1F31uni1F32uni1F33uni1F34uni1F35uni1F36uni1F37uni1F38uni1F39uni1F3Auni1F3Buni1F3Cuni1F3Duni1F3Euni1F3Funi1F40uni1F41uni1F42uni1F43uni1F44uni1F45uni1F48uni1F49uni1F4Auni1F4Buni1F4Cuni1F4Duni1F50uni1F51uni1F52uni1F53uni1F54uni1F55uni1F56uni1F57uni1F59uni1F5Buni1F5Duni1F5Funi1F60uni1F61uni1F62uni1F63uni1F64uni1F65uni1F66uni1F67uni1F68uni1F69uni1F6Auni1F6Buni1F6Cuni1F6Duni1F6Euni1F6Funi1F70uni1F71uni1F72uni1F73uni1F74uni1F75uni1F76uni1F77uni1F78uni1F79uni1F7Auni1F7Buni1F7Cuni1F7Duni1F80uni1F81uni1F82uni1F83uni1F84uni1F85uni1F86uni1F87uni1F88uni1F89uni1F8Auni1F8Buni1F8Cuni1F8Duni1F8Euni1F8Funi1F90uni1F91uni1F92uni1F93uni1F94uni1F95uni1F96uni1F97uni1F98uni1F99uni1F9Auni1F9Buni1F9Cuni1F9Duni1F9Euni1F9Funi1FA0uni1FA1uni1FA2uni1FA3uni1FA4uni1FA5uni1FA6uni1FA7uni1FA8uni1FA9uni1FAAuni1FABuni1FACuni1FADuni1FAEuni1FAFuni1FB0uni1FB1uni1FB2uni1FB3uni1FB4uni1FB6uni1FB7uni1FB8uni1FB9uni1FBAuni1FBBuni1FBCuni1FBDuni1FBEuni1FBFuni1FC0uni1FC1uni1FC2uni1FC3uni1FC4uni1FC6uni1FC7uni1FC8uni1FC9uni1FCAuni1FCBuni1FCCuni1FCDuni1FCEuni1FCFuni1FD0uni1FD1uni1FD2uni1FD3uni1FD6uni1FD7uni1FD8uni1FD9uni1FDAuni1FDBuni1FDDuni1FDEuni1FDFuni1FE0uni1FE1uni1FE2uni1FE3uni1FE4uni1FE5uni1FE6uni1FE7uni1FE8uni1FE9uni1FEAuni1FEBuni1FECuni1FEDuni1FEEuni1FEFuni1FF2uni1FF3uni1FF4uni1FF6uni1FF7uni1FF8uni1FF9uni1FFAuni1FFBuni1FFCuni1FFDuni1FFEenquademquadenspaceemspacethreeperemspacefourperemspace sixperemspace figurespacepunctuationspace thinspace hairspacezerowidthspace hyphentwo hyphennobreak figuredash horizontalbaruni2016 underscoredbl quotereversed quotedblrevtrianglebulletonedotenleadertwodotenleader hyphendotuni202Fpertenthousandminutesecond primetriple primereverseduni2036uni2037uni203B exclamdbl interrobanguni203Euni2042question_questionquestion_exclamexclam_questionuni204Auni204Buni204F zero.superior i.superior four.superior five.superior six.superiorseven.superioreight.superior nine.superior plus.superiorminus.superiorequal.superiorparenleft.superiorparenright.superior n.superior zero.inferior one.inferior two.inferiorthree.inferior four.inferior five.inferior six.inferiorseven.inferioreight.inferior nine.inferior plus.inferiorminus.inferiorequal.inferiorparenleft.inferiorparenright.inferior a.inferior e.inferior o.inferior x.inferioruni2094uni2095uni2096uni2097uni2098uni2099uni209Auni209Buni209Cuni20A2lirapesetauni20A8dongEurouni20AFpesouni2100uni2101uni2102 centigrade afii61248uni2106 fahrenheituni210Cuni210Duni210Euni210FIfraktur afii61289uni2115numerouni2119uni211ARfrakturuni211Duni2120uni2124uni2126Omegainv estimatedalephuni2136uni2137uni2138uni2139uni214Fonethird twothirdsonefifth twofifths threefifths fourfifthsonesixth fivesixths oneeighth threeeighths fiveeighths seveneighths onenumeratorOneromanTworoman Threeroman Fourroman FiveromanSixroman Sevenroman Eightroman NineromanTenroman Elevenroman Twelveromanuni216Cuni216Duni216Euni216Foneromantworoman threeroman fourroman fiveromansixroman sevenroman eightroman nineromantenroman elevenroman twelveromanuni217Cuni217Duni217Euni217Funi2180uni2181uni2182uni2183uni2184 arrowleftarrowup arrowright arrowdown arrowboth arrowupdnuni2196uni2197uni2198uni2199uni219Auni219Buni21A6 arrowupdnbseuni21AEuni21BCuni21BDuni21C0uni21C1uni21CBuni21CCuni21CDuni21CEuni21CF arrowdblleft arrowdblup arrowdblright arrowdbldown arrowdblbothuni21D5NwarrowNearrowSearrowSwarrow universaluni2201 existentialuni2204emptysetuni2206gradientelement notelementuni220Asuchthatuni220Cuni220Duni2210uni2213uni2214uni2215uni2216 asteriskmathuni2218uni2219uni221Buni221C proportionaluni221Fangleuni2221uni2222uni2223uni2224uni2225uni2226 logicaland logicalor intersectionunionuni222Cuni222Duni222Euni2236similaruni2241 congruentuni2249uni2259 equivalenceuni2262uni226Auni226Buni226Euni226Funi2270uni2271 propersubsetpropersuperset notsubsetuni2285 circleplusuni2296circlemultiplyuni2298uni22A2uni22A3uni22A4 perpendicularuni22A6uni22B6uni22B7dotmathuni22EEuni22EFuni2300uni2302uni2303uni2310 integraltopintegralbottomuni2326uni2327uni2329uni232Auni232Buni237Duni2380uni239Buni239Cuni239Duni239Euni239Funi23A0uni23A1uni23A2uni23A3uni23A4uni23A5uni23A6uni23A7uni23A8uni23A9uni23AAuni23ABuni23ACuni23ADintegralextensionuni23D3uni2423uni2460uni2461uni2462uni2463uni2464uni2465uni2466uni2467uni2468uni2469uni246Auni246Buni246Cuni246Duni246Euni246Funi2470uni2471uni2472uni2473uni2474uni2475uni2476uni2477uni2478uni2479uni247Auni247Buni247Cuni247Duni247Euni247Funi2480uni2481uni2482uni2483uni2484uni2485uni2486uni2487uni24B6uni24B7uni24B8uni24B9uni24BAuni24BBuni24BCuni24BDuni24BEuni24BFuni24C0uni24C1uni24C2uni24C3uni24C4uni24C5uni24C6uni24C7uni24C8uni24C9uni24CAuni24CBuni24CCuni24CDuni24CEuni24CFuni24D0uni24D1uni24D2uni24D3uni24D4uni24D5uni24D6uni24D7uni24D8uni24D9uni24DAuni24DBuni24DCuni24DDuni24DEuni24DFuni24E0uni24E1uni24E2uni24E3uni24E4uni24E5uni24E6uni24E7uni24E8uni24E9uni24EAuni24EBuni24ECuni24EDuni24EEuni24EFuni24F0uni24F1uni24F2uni24F3uni24F4uni24F5uni24F6uni24F7uni24F8uni24F9uni24FAuni24FBuni24FCuni24FDuni24FEuni24FF filledboxH22073triagupuni25B3uni25B6uni25B7triagdnuni25BDuni25C0uni25C1uni25C6uni25C7uni25C9 bigcircleuni25CEH18533uni25D0uni25D1uni25D2uni25D3uni25D4uni25D5uni25D6uni25D7 openbulletuni2605uni2609uni2619uni261Buni261Euni2627uni262Funi2639uni263Auni263Buni263Cuni263Duni263Euni263Ffemaleuni2641maleuni2643uni2644uni2645uni2646uni2647uni2648uni2649uni264Auni264Buni264Cuni264Duni264Euni264Funi2650uni2651uni2652uni2653uni2660uni2663uni2665uni2666uni2669 musicalnotemusicalnotedbluni266Cuni266Duni266Euni266Funi2695uni2698uni26A2uni26A3uni26A4uni26A5uni26A6uni26A7uni26ACmarrieddivorceduni26AFuni2714uni2718dieduni2767uni2776uni2777uni2778uni2779uni277Auni277Buni277Cuni277Duni277Euni277Funi27C2uni27E6uni27E7uni27E8uni27E9uni2C60uni2C61uni2C62uni2C63uni2C64uni2C65uni2C66uni2C67uni2C68uni2C69uni2C6Auni2C6Buni2C6Cuni2C6Euni2C6Funi2C74uni2C75uni2C76uni2C77uni2E02uni2E03uni2E04uni2E05uni2E08uni2E09uni2E0Auni2E17uni2E18uniA656uniA657uniA71DuniA720uniA721uniA765TuxuniE001uniE002uniE003uniE004uniE005uniE006uniE007uniE008uniE009uniE00AuniE00Bcopyleft publicdomaincreativecommonszero.slashfitted zero.fitted one.fitted two.fitted three.fitted four.fitted five.fitted six.fitted seven.fitted eight.fitted nine.fitted Euro.fitted Yen.fitteduniE01Ca.altuniE01Eperthousandzero zero.oldstyle one.oldstyle two.oldstylethree.oldstyle four.oldstyle five.oldstyle six.oldstyleseven.oldstyleeight.oldstyle nine.oldstyleuniE02A Adieresis.alt Odieresis.alt Udieresis.altW.altf_bf_f_hf_f_jf_f_kf_f_tf_hf_jf_kf_tc_kc_ht_tc_tlongs_i longs_longsuniE040uniE041uniE042longs_l longs_longs_ilongs_sae.altlongs_hQ_uT_ht_zh.altgermandbls.altgermandbls.ss03Germandbls.altuniE04F ampersand.alta.scb.scc.scd.sce.scf.scg.sch.sci.scj.sck.scl.scm.scn.sco.scp.scq.scr.scs.sct.scu.scv.scw.scx.scy.scz.scuniE06B hyphen.sc agrave.sc aacute.scacircumflex.sc atilde.sc adieresis.scaring.scae.sc ccedilla.sc egrave.sc eacute.scecircumflex.sc edieresis.sc igrave.sc iacute.scicircumflex.sc idieresis.sceth.sc ntilde.sc ograve.sc oacute.scocircumflex.sc otilde.sc odieresis.scoe.sc oslash.sc ugrave.sc uacute.scucircumflex.sc udieresis.sc yacute.scthorn.sc ydieresis.scij.scgermandbls.scalt germandbls.scQ_u.sc q.sc_u.sc dcroat.sc abreve.sc aogonek.sc cacute.sc ccaron.sc dcaron.sc eogonek.sc ecaron.sc gbreve.sc lacute.sc lslash.sc nacute.sc ncaron.sceng.scohungarumlaut.sc racute.sc rcaron.sc sacute.sc scedilla.sc scaron.sc tcedilla.sctbar.scuring.scuhungarumlaut.sc zacute.sc zdotaccent.sc zcaron.sc lcaron.sc tcaron.sctcommaaccent.scscommaaccent.sc idotaccent.sc ampersand.sca.scalt q.superiorgravecomb.supsacutecomb.supscircumflexcomb.supstildecomb.supsmacroncomb.supsf.short f_f.shortuniE0E2uniE0E3uniE0E4uniE0E5uniE0E6uniE0E7uniE0E8uniE0E9uniE0EAK.altR.altJ.altuniE0EEuniE0EFuniE0F0w.altuniE0F2uniE0F3uniE0F4uniE0F5y.altA.altuniE0F9uniE0FBkreisuniE101uniE104uniE105uniE106uniE107zero.capone.captwo.cap three.capfour.capfive.capsix.cap seven.cap eight.capnine.capzero.taboldstyleone.taboldstyletwo.taboldstylethree.taboldstylefour.taboldstylefive.taboldstylesix.taboldstyleseven.taboldstyleeight.taboldstylenine.taboldstyleuniE128uniE129uniE12AuniE130leafuniE13BuniE13CuniE13D b.inferior c.inferior d.inferior f.inferior g.inferior h.inferior i.inferior j.inferior k.inferior l.inferior m.inferior n.inferior p.inferior q.inferior r.inferior s.inferior t.inferior u.inferior v.inferior w.inferior y.inferior z.inferioruniE188uniE189 afii10086.altuniE19D grave.cap acute.capcircumflex.cap caron.cap breve.caphungarumlaut.capspace_uni030F.capbreveinvertedcmb.cap breve.cyrcap breve.cyr dieresis.caphookabovecomb.cap dotaccent.capuniE3E0uniE3E1uniE3E2uniE3E3uniE3E4uniE3E5uniE3E6uniE3E7uniE3E8uniE3E9uniE3EAuniE3EBuniE3ECuniE3EDuniE3F0uniE3F1uniE3F2uniE3F3metric zero.slash parenleft.sc parenright.scbracketleft.scbracketright.sc braceleft.sc braceright.sc exclamdown.scquestiondown.scguillemotleft.scguillemotright.scguilsinglleft.scguilsinglright.sc hyphen.capuniF6BEafii10066.italf_ff_if_lf_f_if_f_llongs_ts_tuniFFFDu1D538u1D539u1D53Bu1D53Cu1D53Du1D53Eu1D540u1D541u1D542u1D543u1D544u1D546u1D547u1D54Au1D54Bu1D54Cu1D54Du1D54Eu1D54Fu1D550HHHD!D!, d `f#PXeY-, d P&ZE[X!#!X PPX!@Y 8PX!8YY Ead(PX! E 0PX!0Y PX f a PX` PX! ` 6PX!6``YYY+YY#PXeYY-,#B#B#BCCQXC+C`BeY-,C E EcEb`D-,C E +#%` E#a d PX!0PX @YY#PXeY%#aDD-,EaD-,` CJPX #BY CJRX #BY-, b c#a C` ` #B#-, CUX CaB+YC%BC`B %B %B# %PXC%B #a*!#a #a*!C%B%a*!Y CG CG`b EcEb`#DC>C`B- ,ETX #B `a  BB`+g+"Y- , +- , +- , +- , +-, +-, +-, +-, +-, +-, +-,+ETX #B `a  BB`+g+"Y-,+-,+-,+-,+-,+-,+-,+-,+-,+-, +-, ` ` C#`C%%QX# <`#e!!Y- ,+*-!, G EcEb`#a8# UX G EcEb`#a8!Y-",ETX!*0"Y-#,+ETX!*0"Y-$, 5`-%,EcEb+EcEb+D>#8$*-&, < G EcEb`Ca8-',.<-(, < G EcEb`CaCc8-),% . G#B%IG#G#ab#B(*-*,%%G#G#a+e.# <8-+,%% .G#G#a #B+ `PX @QX  &YBB# C #G#G#a#F`Cb` + a C`d#CadPXCaC`Y%ba# &#Fa8#CF%CG#G#a` Cb`# +#C`+%a%b&a %`d#%`dPX!#!Y# &#Fa8Y-,, & .G#G#a#<8--, #B F#G+#a8-.,%%G#G#aTX. <#!%%G#G#a %%G#G#a%%I%aEc#bcEb`#.# <8#!Y-/, C .G#G#a ` `fb# <8-0,# .F%FRX ,#B=+-7,*+. +-C,7+-D,7+-E,7+-F,7+-8,++!# <#B#8 +C. +-O,8+-P,8+-Q,8+-R,8+-=,E# . F#a8 +-W,,+. +-X,,+0+-Y,,+1+-Z,,+2+-[,-+. +-\,-+0+-],-+1+-^,-+2+-_,.+. +-`,.+0+-a,.+1+-b,.+2+-c,/+. +-d,/+0+-e,/+1+-f,/+2+-g,+e$Px0-KRXYc #D #pE KQKSZX4(Y`f UX%aEc#b#D + ++Y(ERD +DOFL-1.1.txt000066400000000000000000000107141300200146000334160ustar00rootroot00000000000000connectome-workbench-1.2.3+git41-gc4c6c90/src/Resources/GeneralResources/Fonts/LinuxLibertineCopyright (c) 2003–2012, Philipp H. Poll (www.linuxlibertine.org | gillian at linuxlibertine.org), with Reserved Font Name "Linux Libertine" and "Biolinum". This Font Software is licensed under the SIL Open Font License, Version 1.1. This license is copied below, and is also available with a FAQ at: http://scripts.sil.org/OFL ----------------------------------------------------------- SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 ----------------------------------------------------------- PREAMBLE The goals of the Open Font License (OFL) are to stimulate worldwide development of collaborative font projects, to support the font creation efforts of academic and linguistic communities, and to provide a free and open framework in which fonts may be shared and improved in partnership with others. The OFL allows the licensed fonts to be used, studied, modified and redistributed freely as long as they are not sold by themselves. The fonts, including any derivative works, can be bundled, embedded, redistributed and/or sold with any software provided that any reserved names are not used by derivative works. The fonts and derivatives, however, cannot be released under any other type of license. The requirement for fonts to remain under this license does not apply to any document created using the fonts or their derivatives. DEFINITIONS "Font Software" refers to the set of files released by the Copyright Holder(s) under this license and clearly marked as such. This may include source files, build scripts and documentation. "Reserved Font Name" refers to any names specified as such after the copyright statement(s). "Original Version" refers to the collection of Font Software components as distributed by the Copyright Holder(s). "Modified Version" refers to any derivative made by adding to, deleting, or substituting -- in part or in whole -- any of the components of the Original Version, by changing formats or by porting the Font Software to a new environment. "Author" refers to any designer, engineer, programmer, technical writer or other person who contributed to the Font Software. PERMISSION & CONDITIONS Permission is hereby granted, free of charge, to any person obtaining a copy of the Font Software, to use, study, copy, merge, embed, modify, redistribute, and sell modified and unmodified copies of the Font Software, subject to the following conditions: 1) Neither the Font Software nor any of its individual components, in Original or Modified Versions, may be sold by itself. 2) Original or Modified Versions of the Font Software may be bundled, redistributed and/or sold with any software, provided that each copy contains the above copyright notice and this license. These can be included either as stand-alone text files, human-readable headers or in the appropriate machine-readable metadata fields within text or binary files as long as those fields can be easily viewed by the user. 3) No Modified Version of the Font Software may use the Reserved Font Name(s) unless explicit written permission is granted by the corresponding Copyright Holder. This restriction only applies to the primary font name as presented to the users. 4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font Software shall not be used to promote, endorse or advertise any Modified Version, except to acknowledge the contribution(s) of the Copyright Holder(s) and the Author(s) or with their explicit written permission. 5) The Font Software, modified or unmodified, in part or in whole, must be distributed entirely under this license, and must not be distributed under any other license. The requirement for fonts to remain under this license does not apply to any document created using the Font Software. TERMINATION This license becomes null and void if any of the above conditions are not met. DISCLAIMER THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL 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. connectome-workbench-1.2.3+git41-gc4c6c90/src/Resources/GeneralResources/Fonts/LinuxLibertine/README000066400000000000000000000051641300200146000327020ustar00rootroot00000000000000LOFP - Libertine Open Fonts Project 1. OPEN FONTS PROJECT'S AIMS We work on a serif and organic grotesque font-family for practical use in documents. Our project aims at creating a free alternative to the standard W*ndows Font (T*mes). But neveretheless Libertine and Biolinum are not a clone of any common font! They have been developed from scratch and go different ways in typography than the Times or Arial. Just the useability and the dimensions shall be similar, Libertine should be even better for typical office use! If you want Times- and Arial-clones go elsewhere. If you just need reliable and good typography give our fonts a chance. If you want to know more about the design of Libertine and Biolinum, have a look at our website. 2. LICENSE AND OPENSOURCE We publish our fonts under the terms of the GPL (see GPL.txt) and OFL (OFL.txt) -> see also LICENCE.txt! The OpenSource-tool Fontforge is used as font editor (see http://fontforge.sf.net). 3. FONT FORMATS TTF VS. OTF The font files are available as TTF (TrueType) and OTF (OpenType) fonts. The TTF-Family is called "Linux Libertine" and "Linux Biolinum" and the OTF "LinuxLibertine O" and "Linux Biolinum O". So that both types can be installed and used parallely. Most often TTF is the better supported format though OTF has advances in printing. Decide yourself what is better for your purpose. OpenType-features are equally available in both fonts. Note that OpenOffice doesn’t support OTFs, yet. 4. THE LINUX BIOLINUM FONT FACE Please note: The Biolinum is a very early version. While you use Libertine-Fonts without any warranty anyway, take special care with this young font face. 5. HINTING The TrueType-hinting is a complex technique and our editor FontForge doesn't support full possibilities (but it becomes alot better version by version)... Since version 2.7 also the normal TTFs are hinted. If you don't like this, send me a mail. You may also try the OpenTypes (which contain PS-Hintings which are quite good supported by FontForge). 6. DOWNLOAD AND CONTACT We publish our fonts at http://www.linuxlibertine.org/. 7. THE UNDERLINED VARIANT Please note: The underlined variant is recently not being maintained because its concept doesn’t seem to be sofware-technically reliable and because of lack of interest. The advantage of this font was that g, commas, cedillas... were not overprinted by the line anymore. For technical reasons the space was not underlined but you could use the _ instead. In this font it had the width of the space and the line was at hight of the underline. The underlined variant used an older font outline. Philipp Poll -- gillian at linuxlibertine.org connectome-workbench-1.2.3+git41-gc4c6c90/src/Resources/GeneralResources/Fonts/VeraFonts/000077500000000000000000000000001300200146000307665ustar00rootroot00000000000000COPYRIGHT.TXT000066400000000000000000000135021300200146000326610ustar00rootroot00000000000000connectome-workbench-1.2.3+git41-gc4c6c90/src/Resources/GeneralResources/Fonts/VeraFontsBitstream Vera Fonts Copyright The fonts have a generous copyright, allowing derivative works (as long as "Bitstream" or "Vera" are not in the names), and full redistribution (so long as they are not *sold* by themselves). They can be be bundled, redistributed and sold with any software. The fonts are distributed under the following copyright: Copyright ========= Copyright (c) 2003 by Bitstream, Inc. All Rights Reserved. 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 the Font Software, including without limitation the rights to use, copy, merge, publish, distribute, and/or sell copies of the Font Software, and to permit persons to whom the Font Software is furnished to do so, subject to the following conditions: The above copyright and trademark notices and this permission notice shall be included in all copies of one or more of the Font Software typefaces. The Font Software may be modified, altered, or added to, and in particular the designs of glyphs or characters in the Fonts may be modified and additional glyphs or characters may be added to the Fonts, only if the fonts are renamed to names not containing either the words "Bitstream" or the word "Vera". This License becomes null and void to the extent applicable to Fonts or Font Software that has been modified and is distributed under the "Bitstream Vera" names. The Font Software may be sold as part of a larger software package but no copy of one or more of the Font Software typefaces may be sold by itself. THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL BITSTREAM OR THE GNOME FOUNDATION BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL 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. Except as contained in this notice, the names of Gnome, the Gnome Foundation, and Bitstream Inc., shall not be used in advertising or otherwise to promote the sale, use or other dealings in this Font Software without prior written authorization from the Gnome Foundation or Bitstream Inc., respectively. For further information, contact: fonts at gnome dot org. Copyright FAQ ============= 1. I don't understand the resale restriction... What gives? Bitstream is giving away these fonts, but wishes to ensure its competitors can't just drop the fonts as is into a font sale system and sell them as is. It seems fair that if Bitstream can't make money from the Bitstream Vera fonts, their competitors should not be able to do so either. You can sell the fonts as part of any software package, however. 2. I want to package these fonts separately for distribution and sale as part of a larger software package or system. Can I do so? Yes. A RPM or Debian package is a "larger software package" to begin with, and you aren't selling them independently by themselves. See 1. above. 3. Are derivative works allowed? Yes! 4. Can I change or add to the font(s)? Yes, but you must change the name(s) of the font(s). 5. Under what terms are derivative works allowed? You must change the name(s) of the fonts. This is to ensure the quality of the fonts, both to protect Bitstream and Gnome. We want to ensure that if an application has opened a font specifically of these names, it gets what it expects (though of course, using fontconfig, substitutions could still could have occurred during font opening). You must include the Bitstream copyright. Additional copyrights can be added, as per copyright law. Happy Font Hacking! 6. If I have improvements for Bitstream Vera, is it possible they might get adopted in future versions? Yes. The contract between the Gnome Foundation and Bitstream has provisions for working with Bitstream to ensure quality additions to the Bitstream Vera font family. Please contact us if you have such additions. Note, that in general, we will want such additions for the entire family, not just a single font, and that you'll have to keep both Gnome and Jim Lyles, Vera's designer, happy! To make sense to add glyphs to the font, they must be stylistically in keeping with Vera's design. Vera cannot become a "ransom note" font. Jim Lyles will be providing a document describing the design elements used in Vera, as a guide and aid for people interested in contributing to Vera. 7. I want to sell a software package that uses these fonts: Can I do so? Sure. Bundle the fonts with your software and sell your software with the fonts. That is the intent of the copyright. 8. If applications have built the names "Bitstream Vera" into them, can I override this somehow to use fonts of my choosing? This depends on exact details of the software. Most open source systems and software (e.g., Gnome, KDE, etc.) are now converting to use fontconfig (see www.fontconfig.org) to handle font configuration, selection and substitution; it has provisions for overriding font names and subsituting alternatives. An example is provided by the supplied local.conf file, which chooses the family Bitstream Vera for "sans", "serif" and "monospace". Other software (e.g., the XFree86 core server) has other mechanisms for font substitution. connectome-workbench-1.2.3+git41-gc4c6c90/src/Resources/GeneralResources/Fonts/VeraFonts/README.TXT000066400000000000000000000005001300200146000323170ustar00rootroot00000000000000Contained herin is the Bitstream Vera font family. The Copyright information is found in the COPYRIGHT.TXT file (along with being incoporated into the fonts themselves). The releases notes are found in the file "RELEASENOTES.TXT". We hope you enjoy Vera! Bitstream, Inc. The Gnome Project RELEASENOTES.TXT000066400000000000000000000176601300200146000332130ustar00rootroot00000000000000connectome-workbench-1.2.3+git41-gc4c6c90/src/Resources/GeneralResources/Fonts/VeraFontsBitstream Vera Fonts - April 16, 2003 ===================================== The version number of these fonts is 1.10 to distinguish them from the beta test fonts. Note that the Vera copyright is incorporated in the fonts themselves. The License field in the fonts contains the copyright license as it appears below. The TrueType copyright field is not large enough to contain the full license, so the license is incorporated (as you might think if you thought about it) into the license field, which unfortunately can be obscure to find. (In pfaedit, see: Element->Font Info->TTFNames->License). Our apologies for it taking longer to complete the fonts than planned. Beta testers requested a tighter line spacing (less leading) and Jim Lyles redesigned Vera's accents to bring its line spacing to more typical of other fonts. This took additional time and effort. Our thanks to Jim for this effort above and beyond the call of duty. There are four monospace and sans faces (normal, oblique, bold, bold oblique) and two serif faces (normal and bold). Fontconfig/Xft2 (see www.fontconfig.org) can artificially oblique the serif faces for you: this loses hinting and distorts the faces slightly, but is visibly different than normal and bold, and reasonably pleasing. On systems with fontconfig 2.0 or 2.1 installed, making your sans, serif and monospace fonts default to these fonts is very easy. Just drop the file local.conf into your /etc/fonts directory. This will make the Bitstream fonts your default fonts for all applications using fontconfig (if sans, serif, or monospace names are used, as they often are as default values in many desktops). The XML in local.conf may need modification to enable subpixel decimation, if appropriate, however, the commented out phrase does so for XFree86 4.3, in the case that the server does not have sufficient information to identify the use of a flat panel. Fontconfig 2.2 adds Vera to the list of font families and will, by default use it as the default sans, serif and monospace fonts. During the testing of the final Vera fonts, we learned that screen fonts in general are only typically hinted to work correctly at integer pixel sizes. Vera is coded internally for integer sizes only. We need to investigate further to see if there are commonly used fonts that are hinted to be rounded but are not rounded to integer sizes due to oversights in their coding. Most fonts work best at 8 pixels and below if anti-aliased only, as the amount of work required to hint well at smaller and smaller sizes becomes astronomical. GASP tables are typically used to control whether hinting is used or not, but Freetype/Xft does not currently support GASP tables (which are present in Vera). To mitigate this problem, both for Vera and other fonts, there will be (very shortly) a new fontconfig 2.2 release that will, by default not apply hints if the size is below 8 pixels. if you should have a font that in fact has been hinted more agressively, you can use fontconfig to note this exception. We believe this should improve many hinted fonts in addition to Vera, though implemeting GASP support is likely the right long term solution. Font rendering in Gnome or KDE is the combination of algorithms in Xft2 and Freetype, along with hinting in the fonts themselves. It is vital to have sufficient information to disentangle problems that you may observe. Note that having your font rendering system set up correctly is vital to proper judgement of problems of the fonts: * Freetype may or may not be configured to in ways that may implement execution of possibly patented (in some parts of the world) TrueType hinting algorithms, particularly at small sizes. Best results are obtained while using these algorithms. * The freetype autohinter (used when the possibly patented algorithms are not used) continues to improve with each release. If you are using the autohinter, please ensure you are using an up to date version of freetype before reporting problems. * Please identify what version of freetype you are using in any bug reports, and how your freetype is configured. * Make sure you are not using the freetype version included in XFree86 4.3, as it has bugs that significantly degrade most fonts, including Vera. if you build XFree86 4.3 from source yourself, you may have installed this broken version without intending it (as I did). Vera was verified with the recently released Freetype 2.1.4. On many systems, 'ldd" can be used to see which freetype shared library is actually being used. * Xft/X Render does not (yet) implement gamma correction. This causes significant problems rendering white text on a black background (causing partial pixels to be insufficiently shaded) if the gamma of your monitor has not been compensated for, and minor problems with black text on a while background. The program "xgamma" can be used to set a gamma correction value in the X server's color pallette. Most monitors have a gamma near 2. * Note that the Vera family uses minimal delta hinting. Your results on other systems when not used anti-aliased may not be entirely satisfying. We are primarily interested in reports of problems on open source systems implementing Xft2/fontconfig/freetype (which implements antialiasing and hinting adjustements, and sophisticated subpixel decimation on flatpanels). Also, the algorithms used by Xft2 adjust the hints to integer widths and the results are crisper on open source systems than on Windows or MacIntosh. * Your fontconfig may (probably does) predate the release of fontconfig 2.2, and you may see artifacts not present when the font is used at very small sizes with hinting enabled. "vc-list -V" can be used to see what version you have installed. We believe and hope that these fonts will resolve the problems reported during beta test. The largest change is the reduction of leading (interline spacing), which had annoyed a number of people, and reduced Vera's utility for some applcations. The Vera monospace font should also now make '0' and 'O' and '1' and 'l' more clearly distinguishable. The version of these fonts is version 1.10. Fontconfig should be choosing the new version of the fonts if both the released fonts and beta test fonts are installed (though please discard them: they have names of form tt20[1-12]gn.ttf). Note that older versions of fontconfig sometimes did not rebuild their cache correctly when new fonts are installed: please upgrade to fontconfig 2.2. "fc-cache -f" can be used to force rebuilding fontconfig's cache files. If you note problems, please send them to fonts at gnome dot org, with exactly which face and size and unicode point you observe the problem at. The xfd utility from XFree86 CVS may be useful for this (e.g. "xfd -fa sans"). A possibly more useful program to examine fonts at a variety of sizes is the "waterfall" program found in Keith Packard's CVS. $ cvs -d :pserver:anoncvs@keithp.com:/local/src/CVS login Logging in to :pserver:anoncvs@keithp.com:2401/local/src/CVS CVS password: $ cvs -d :pserver:anoncvs@keithp.com:/local/src/CVS co waterfall $ cd waterfall $ xmkmf -a $ make # make install # make install.man Again, please make sure you are running an up-to-date freetype, and that you are only examining integer sizes. Reporting Problems ================== Please send problem reports to fonts at gnome org, with the following information: 1. Version of Freetype, Xft2 and fontconfig 2. Whether TT hinting is being used, or the autohinter 3. Application being used 4. Character/Unicode code point that has problems (if applicable) 5. Version of which operating system 6. Please include a screenshot, when possible. Please check the fonts list archives before reporting problems to cut down on duplication. connectome-workbench-1.2.3+git41-gc4c6c90/src/Resources/GeneralResources/Fonts/VeraFonts/Vera.ttf000066400000000000000000002006141300200146000324050ustar00rootroot00000000000000OS/2_cpVPCLTъ^6cmaplXcvt 9fpgm&`gaspH glyf tA&~hdmx4!Hhead݄T6hheaEoL$hmtx Ǝ0kernRՙ-loca=maxpG:, nameټȵpostZ/prep; h::_:: dM0l   p t  &   Y &  &   c . 5 `  s 0 & {Copyright (c) 2003 by Bitstream, Inc. All Rights Reserved.Bitstream Vera SansBitstreamVeraSans-RomanRelease 1.10Copyright (c) 2003 by Bitstream, Inc. All Rights Reserved. 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 the Font Software, including without limitation the rights to use, copy, merge, publish, distribute, and/or sell copies of the Font Software, and to permit persons to whom the Font Software is furnished to do so, subject to the following conditions: The above copyright and trademark notices and this permission notice shall be included in all copies of one or more of the Font Software typefaces. The Font Software may be modified, altered, or added to, and in particular the designs of glyphs or characters in the Fonts may be modified and additional glyphs or characters may be added to the Fonts, only if the fonts are renamed to names not containing either the words "Bitstream" or the word "Vera". This License becomes null and void to the extent applicable to Fonts or Font Software that has been modified and is distributed under the "Bitstream Vera" names. The Font Software may be sold as part of a larger software package but no copy of one or more of the Font Software typefaces may be sold by itself. THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL BITSTREAM OR THE GNOME FOUNDATION BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL 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. Except as contained in this notice, the names of Gnome, the Gnome Foundation, and Bitstream Inc., shall not be used in advertising or otherwise to promote the sale, use or other dealings in this Font Software without prior written authorization from the Gnome Foundation or Bitstream Inc., respectively. For further information, contact: fonts at gnome dot org.http://www.bitstream.comCopyright (c) 2003 by Bitstream, Inc. All Rights Reserved.Bitstream Vera SansBitstreamVeraSans-RomanRelease 1.10Copyright (c) 2003 by Bitstream, Inc. All Rights Reserved. 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 the Font Software, including without limitation the rights to use, copy, merge, publish, distribute, and/or sell copies of the Font Software, and to permit persons to whom the Font Software is furnished to do so, subject to the following conditions: The above copyright and trademark notices and this permission notice shall be included in all copies of one or more of the Font Software typefaces. The Font Software may be modified, altered, or added to, and in particular the designs of glyphs or characters in the Fonts may be modified and additional glyphs or characters may be added to the Fonts, only if the fonts are renamed to names not containing either the words "Bitstream" or the word "Vera". This License becomes null and void to the extent applicable to Fonts or Font Software that has been modified and is distributed under the "Bitstream Vera" names. The Font Software may be sold as part of a larger software package but no copy of one or more of the Font Software typefaces may be sold by itself. THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL BITSTREAM OR THE GNOME FOUNDATION BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL 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. Except as contained in this notice, the names of Gnome, the Gnome Foundation, and Bitstream Inc., shall not be used in advertising or otherwise to promote the sale, use or other dealings in this Font Software without prior written authorization from the Gnome Foundation or Bitstream Inc., respectively. For further information, contact: fonts at gnome dot org.http://www.bitstream.com5fqu-J3T99NR7s`s3VV9s3D{o{RoHT3fs +b-{T#\q#H99`#fy```{w``b{{Rffw;{J/}oo5jo{-{T7fD)fs@%2%%A:B2SAS//2ݖ}ٻ֊A}G}G͖2ƅ%]%]@@%d%d%A2dA  d   A(]%]@%..%A  %d%@~}}~}}|d{T{%zyxw v utsrqponl!kjBjSih}gBfedcba:`^ ][ZYX YX WW2VUTUBTSSRQJQP ONMNMLKJKJIJI IH GFEDC-CBAK@?>=>=<=<; <@; :987876765 65 43 21 21 0/ 0 / .- .- ,2+*%+d*)*%)('%(A'%&% &% $#"!! d d BBBdB-B}d       -d@--d++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++, %Id@QX Y!-,%Id@QX Y!-,  P y PXY%%# P y PXY%-,KPX EDY!-,%E`D-,KSX%%EDY!!-,ED-ff@ /10!%!!fsr)5 @@ <2991/0K TX @ 878Y P ]%3#3#5qeM@1<20KTKT[X@878Y@0 @ P ` p ]#!#o$++`@1      91/<<<<<<<2220@   ]!! !3!!!!#!#!5!!5!T%Dh$ig8R>hggh`TifaabbNm!(/@U" '&( /)/))/B" ) *!#*- ) " & 0<<<1/299990KSX99Y"K TX0@00878YK TKT[KT[X000@878Y#.'5.546753.'>54&dijfod]SS\dtzq{---@A$*.U# jXV`OnZXhq) #'3@6$%&%&'$'B .$ &($4'!%   ! + 1 49912<0KSXY"K TK T[K T[KT[KT[K T[X4@44878Y"32654&'2#"&546"32654&%3#2#"&546WccWUccUVcbWWcd1Zܻۻa ۻۼ 0@      !         B  (('+'$ .  .'.'!!199999991/9990KSX99999999Y"2]@ " ) **&:4D ^YZ UZZY0g{ "-  ' (   2'') #**(/2; 49?2J LKFO2VZ Y UY\_2j i`2uy z 2229]]3267 >73#'#"5467.54632.#"[UԠ_I{;B h]hΆ02޸SUWDi;#QX?@Yr~YW׀c?}<$$/1oX3goB@ 10KTKT[X@878Y@ @P`p]#o+{ O@  29910KTX@878YKTX@878Y#&547{>;o @ <99103#654<:=JN@,       <2<2991<22990%#'%%73%g:r:g:PrPbybcy #@   <<1/<<0!!#!5!-Ө-Ӫ--@ 1073#ӤR@d10!!d1/073#B-@B/9910KSXY"3#m #@  10"32'2#"  P3343ssyzZ K@B  1/20KSXY"KTX  @878Y]7!5%3!!JeJsHHժJ@'B   91/20KSX9Y"KTKT[KT[X@878Y@2UVVzzvtvust]]%!!567>54&#"5>32Ls3aM_xzXE[w:mIwBC12\ps({@.    #)&  )99190KTKT[X)@))878Y@ daa d!]!"&'532654&+532654&#"5>32?^jTmǹSrsY %Đ%%12wps{$& Ѳ|d @   B    <291/<290KSXY"K TK T[X@878Y@* *HYiw+&+6NO O Vfuz ]] !33##!55^%3`du@#    190KTKT[X@878YKTX@878Y!!>32!"&'532654&#",X,$^hZkʭQTժ 10$& $X@$  "% " !%190@]]"32654&.#">32# !2 LL;kPL;y$&W]ybhc@B991/0KSXY"KTX@878Y@X9Hg]]!#!3V+ #/C@% '-'0 $*$ !0991990"32654&%&&54632#"$54632654&#"HŚV г "Əُattt$X@# %!"" %190@]]7532#"543 !"&2654&#"LK:lL>$& V\s[#@<21/073#3### %@  <2103#3#ӤR#٬@^M@*B$#29190KSXY" 5Ѧ`@ #<210!!!!^O@+B$#<9190KSXY"55//m$p@+$     &%99991/9990K TX%@%%878Yy z z ]%3##546?>54&#"5>32ſ8ZZ93lOa^gHZX/'eVY5^1YnFC98ŸLVV/5<4q L@2  L4307$7CM34( (+(I+*(I,=M<9912990K TK T[KT[KT[KT[XMMM@878Y@ NN/N?N]32654&#"#"&5463253>54&'&$#"3267#"$'&5476$32|{zy!orqp ˘s'6@   0210].# !267# !2'ffjzSb_^^_HHghG.@   2 99991/0`]3 !%! )5BhPa/w.,~ .@   21/0 ]!!!!!!9>ժF# )@ 21/0 ]!!!!#ZpPժH7s9@ 43 1990%!5!# !2&&# !26uu^opkSUmnHF_`%; ,@ 8  221/<20P ]3!3#!#"d+991/0KTX@878Y@ 0@P`]3#+f M@  9 991990KTX  @878Y@ 0 @ P ` ]3+53265M?nj @(B  291/<290KSXY"]@ ((764GFCUgvw    (+*66650 A@E@@@ b`hgwp  ,]q]q3! !#3wH1j%@ :1/0@ 0P]3!!_ժ @4  B    >  91/<290KSXY"p]@V   && & 45 i|{y   #,'( 4<VY ej vy ]]! !###-}-+3 y@B6 991/<2990KSXY" ]@068HGif FIWXeiy ]]!3!#j+s #@  310"32' ! ':xyLHH[[bb:@   ? 291/0@ ?_]32654&#%!2+#8/ϒs R@*  B     39991990KSX9Y""32#'# ! '? !#y;:xLHHab[T@5  B    ?  299991/<9990KSX9Y"@]@Bz%%%&'&&& 66FFhuuw]]#.+#! 32654&#A{>ٿJx~hb؍O'~@<    B %( "-"(9999190KSX99Y")])/)O)].#"!"&'532654&/.54$32Hs_wzj{r{i76vce+ٶ0/EF~n|-&J@@@1/20K TX@878Y@  @ p ]!!#!ժ+)K@   8A1299990KTX@878Y]332653! ˮ®u\*$h@'B91/290KSXY"P]@b*GGZ} *&&))% 833<<7HEEIIGYVfiizvvyyu)]]!3 3J+D {@I      B     91/<2290KSXY"]@  ($ >>4 0 LMB @ Yjkg ` {|      !   # $ %  <:5306 9 ? 0FFJ@E@BBB@@ D M @@XVY Pfgab```d d d wv{xwtyywpx   []]3 3 3# #D:9:9+=; ]@F      B    91/<290KSXY"K TK T[KT[X  @878Y@ '' 486 KX[fkww       &()&(' ) 54<;:;4 4 8 ? H O X _ eejjhiil l xyyx}  x   @]]3 3 # #su \Y+3{@(B@@ 91/290KSXY" ]@<5000F@@@QQQe &)78@ ghxp ]]3 3#f9\ @BB 991/0KSXY"K TK T[X @ 878Y@@ )&8HGH    / 59? GJO UYfio wx ]]!!!5!sP=g՚oXS@C210K TX@878YKTKT[X@878Y!#3!XB-@B/9910KSXY"#mo<@C<10KTKT[X@878Y!53#5oXޏ@ 91290##HHu-10!5f1@ D10K TKT[X@878Y #ofv{-{ %@'   #   E&22991/9990@n0000 0!0"?'@@@@ @!@"PPPP P!P"P'p' !"'''000 0!@@@ @!PPP P!``` `!ppp p! !]]"326=7#5#"&5463!54&#"5>32߬o?`TeZ3f{bsٴ)Lfa..'' 8@  G F221/0`]4&#"326>32#"&'#3姒:{{:/Rdaadq{?@  HE210@ ].#"3267#"!2NPƳPNM]-U5++++$$>:#qZ8@G E221/0`]3#5#"3232654&#":||ǧ^daDDaq{p@$   KE9190@)?p?????,// , ooooo ]q]!3267# 32.#" ͷjbck)^Z44*,8 Cė/p@     L<<991/22990K TX@878YKTX@878Y@P]#"!!##535463cM/ѹPhc/яNqVZ{ (J@#  &#' & G E)221/990`***]4&#"326!"&'5326=#"3253aQQR9||9=,*[cb::bcd4@  N  F21/<90`]#4&#"#3>32d||Bu\edy+@F<21/0@  @ P ` p ]3#3#`Vy D@   O  F<2991990@ @P`p]3+532653#F1iL`a( @)B F 291/<90KSXY" ]@_ ')+Vfgsw    ('(++@ h` ]q]33 ##%kǹi#y"F1/0@ @P`p]3#{"Z@&   PPF#291/<<<290@0$P$p$$$$$$$ ]>32#4&#"#4&#"#3>32)Erurw?yz|v\`gb|d{6@  N  F21/<90`]#4&#"#3>32d||Bu\`edqu{ J@  QE10@#?{{   {  {]"32654&'2#"s98V{>@ GF2210@ `]%#3>32#"&4&#"326s:{{8 daaqVZ{ >@   GE2210@ `]32654&#"#"3253#/s:||:/daDDadJ{0@    F21/90P].#"#3>32JI,:.˾`fco{'@<  S  SB %( R"E(9999190KSX99Y"']@m   . , , , ; ; ; ; $( ( *//*(() )!$'      '/)?)_))))))]]q.#"#"&'532654&/.54632NZb?ĥZlfae@f?((TT@I!*##55YQKP%$78@  F<<2991/<2990]!!;#"&5#53w{KsբN`>X`6@    NF21/290`]332653#5#"&||Cua{fc=`@'B91/290KSXY"K TX@878YKTKT[X@878Y@Hj{  &&)) 55::0FFIIFH@VVYYPffiigh`ut{{uz>]]3 3#=^^\`TV5` @IU U U U   B     91/<2290KSXY"K TKT[KT[KT[K T[X  @878YK TK T[KT[X @ 878Y@" 5 IIF @ [[U P nnf yy          %%#'!%""%' $ ! # 9669 0FHF@B@@@D D D @@VVVPQRRPS T U cdejejjjn a g ouuy}x}zzxy  { v } @/   y]]333# #V`jjj;y` Z@F      B   91/<290KSXY"K TKT[KT[KT[X  @878YKTX @ 878Y@   & =1 UWX f vzvt        )&% * :9746 9 0 IFE J @ YVYYWVYVV Y P o x  /]] # # 3 dkr))`HJq=V`@C        B     9129990KSX2Y"K TKT[X@878YKTX@878Y@     # 5 I O N Z Z j        '$$  )( % $ $ ' ** 755008 6 6 8 990A@@@@@@@@B E G II@TQQUPPVUVW W U U YYPffh ii`{xx   e]]+5326?3 3N|lLT3!;^^hzHTNlX` @B 2991/0KSXY"K TK T[X @ 878YKTX  @878Y@B&GI  + 690 @@E@@CWY_ ``f``b ]]!!!5!qjL}e`ۓ%$@4 %   !  % $  C %<<29999999199999990K TX%%%@878Y&]#"&=4&+5326=46;#"3>l==k>DV[noZVtsݓXX10#$@6%   #%#C %<2<9999999199999990K TX%@%%878YKTX%%%@878Y&]326=467.=4&+532;#"+FUZooZUF?l>>l?VWstݔ1#@  1990#"'&'&'&#"56632326ian ^Xbian ^V1OD;>MSOE<>LhN'$uhm !@T   !!  ! !!!B     !  VV!"2299999991/<9990KSXY" #]@  s P#f iu {yyv v!# ]]4&#"326!.54632#!#TY?@WX??Y!X=>sr?<҈_Z?YWA?XXN)sIsrFv)su''&-k'(u3^'1usN'2'u)N'8u{-f'DR{-f'DCR{-f'DR{-'DR{-7'DR{-'DRqu{'Fqf'Hqf'HCqf'Hq'Hof'f'C\f'F'd7'Qquf'Rsquf'RCsquf'Rsqu'Rsqu7'RsXf'X{Xf'XC{Xf'X{X'X{9; '@  YW Y <<1<203!!#!5!oo\]u=  @  Z[Z10"32654&'2#"&546PnnPPnoO@v+..ooPOmmOOp1.-rB#!Q@+     "  "<<<221<9990%&&'667#&73JDFHAMf fIX⸹)**'# 32!b`@!    <<1/2<2990K TX@878Y66].#"!!!!53#535632NL=ty-=))׏/я\= >@54&.#"#"&'532654/.5467.54632{?>?>S8alӃ\]>9̭IXW:fqր][;;ȦI.Z.L-[.K''PGZsweZ54m@''TLf{xf[1,pE3!   \ 104632#"&3~|}}||};9 %@]] 91290!###&&54$yfNݸ/@0-'!  **.  !' $'$-F099991/990@@'(     ! "&  : :!MM I!I"jj  ]]4632#"&'532654&/.5467.#"#:A9`@IPAtx;e\`Wqqs`/Q*%jd_[?T>7;[gp/8L`@6EBC?2H09JC 9 $HE301BKL?gwyVpMI`3D/IC@&=>:A$104G$ 7aD=0^* D^ J21/02#"$'&5476$"32676654&'&&&&#"3267#"&54632mmllmmmmllmm^^``^^⃄^]]^\^BB@zBCFInmmmmnnmmmmng^^^傁^^__^]⃅]^^! "'F >@!    b b cbc91<<2<<903#######5Jq7rqr/B^^sRf1@ D10K TKT[X@878Y3#fF)@dd1<20K TK T[X@878YK TK T[KT[KT[X@878YKTKT[X@878Y@````pppp]3#%3#^y'>@"     <291<2<<990!!!!!'7!5!7!}/H{};fըfӪH@9  B     <291/<0KSXY"]@gww  ]!!!!!!#!59=qժF՞f +@< +,  )&  *&& &,+,* # )#3,99999999199999990@*WZWU!je!{vu! FYVjddj(|svz( ]] 324&'.#"&5!27!"&''3>_'y=_''NOy;WfNPƀ[gX@CHp@CpDfbMKYg[KKX /@- !$'!!0 $*0999919990@     $$$   $$ $ ***///***55500055 5 :::???:::EEE@@@EE E JJJOOOJJJV !"&'()]]32654&#".#"326#"&54632>32#"&1TevYR1UfvYRF^_HDa^/XZie7XXjeߦ~᧯w .@     <2<21/<<0!!#!5!!!-Ө-}} T@.B $# <2291/90KSXY" 5!!@po V@/B$ # <<291/90KSXY"55!5AǪR@F  B     fe f e<2299991/2<2<290KSXY"K TX@878Y@(' ' ')((79  ]]!#!5!5'!5!3 3!!!c`Tþ{yT9{3{JD{3V` M@%  !   NF!2912<990"`""]3326533267#"&'#"&'#% )I#ER2bf*V H<9 NPOONNh-)b@'! '!* $$*9991990K TK T[KT[KT[KT[X*@**878Y>54&#"#"&54632#"&54324&#"32IH7$$0e՘ݢe WOmVPmmWKt,>bFأ[t}t{w; ]@    91990@0QVPZ spvupz  Z pp{ t  ]]!! !!5 7AJI3!wq@gg120!#!# }/#@1 " $ #" #h#$9999991/<229990K TX$$$@878Y@V             ##(]]#3267#"&5467!##"#>3!i/7.%7vy"Pµ)6< yJ\:1fd.xo@E}/%&@  & iji&1026732#"&'&&#"#"&546327j Pd@7*8  kOeD=!0 l9TA6?&#Hn!bSA8?Ss;)_@3(%%  * "(kl"k *22999199990!!#5#"&5463354&#"56632"32655P,]uu>DIE~bRhP{@p?Dq[[""CO@Mr`d.@  klk 9910!!2#"&546"32654&PXγгi~hi}|P{ݿܾsN@@"   mm  9991/<20%!5654#"!5!&5! Dz?1/aL"a*>w؍{o{3>@C'-%= 4%:.-*1 %?47&%7& =&-7"E?<9999912<<29990@0+0,0-0.0/00@+@,@-@.@/@0P+P,P-P.P/P0+0@@@@@@@@@??? ??0,0-0.0/@,@-@.@/P,P-P.P/ooo oo`,`-`.`/p,p-p.p/,-./]q].#">32!3267#"&'#"&5463!54&#"5>32"326=DJԄ ̷hddjMI؏`TeZ߬o0Z^Z55*,ywxx..''`f{bsٴ)H +@<+,&  )&  *&& &,+,* # #Q)E,22999999199999990@p(?-YVUV jf!{    { z{ {!"#$%{&%--&YVUZ(ifej(ztvz($$]] 32654&'.#".5327#"&'')gA\*g>}66]C_56`?`!*(Ou))Hn.Mw834OMx43N $@/  !# #%" " "!& %999919990KTKT[KT[X%%%@878Y@ ttttv]33267#"&546?>7>5#537ZZ:3mN`^gIYX0&DeWX5^1YnFC98ŸLVV/5<65 b@ <2991/0K TX @ 878YKTKT[KT[X  @878Y P ]#53#3+e^@ 10!#!^=} *@    91903##'%\sB}}`s-Pb;V#@@   B   !$  $912299990KSX29Y"K TX$$$@878Y.#"!!#"&'53267#5!>32&P,`r<::d/4a/am"?$Ɨ5dzɏ!!J;?@.9*-" *19" <-<<219999990#"'&'&'&#"56632326#"'&'&'&#"56632326ian ^Xbian ^Vgian ^Xbian ^VoNE;=LTNE;=KڲOE;=LSNE;=K`8@91/90@cmpxyvn]] !3!^DC?%# @I    B   o o n<2991<2990KSXY"55%-+#-+#RRH# @I  B   o op<<991<2990KSXY"5%5+-+-#^R^  ^R^   #@   1/<<220%3#%3#%3#hk'$uh^'$us^'2'us ;@   299991/220!!!!! !# !39OAg@AժF|pm|q{'3@1 . ("%4"1 K1 Q+E499912<2290@%?5_5p55555????? ooooo ]q].#"!3267#"&'#"32>32%"32654& H ̷jbdjQGьBN5Z44*,nmnm98olkp݇y/10!!yy/10!!ym '@   1<20#53#53ӤRӤR??m '@   1<203#%3#ӤRӤRլ@@@ 10#53ӤR?@ q103#ӤR՘?o )@ r <<103#3#!!oA#u"@91990  9%-=V'\^N'<su+@B10KSXY"3#-\^R#/@I -'! - -'!0 *$0* $ $(st*(s099999999919999999907'#"&''7&&5467'766324&#"326{r%$&(r;t=:x=q%%&&s7t@?s9q(&%%s>v:@t8s'%$|pprs#G@%Bon29190KSXY"5s-+#R#I@&Bop<9190KSXY"5+-#^R^  /J@(   L<2<2991/<22990K TX@878YKTX@878Y@0P]]#!##53546;#"3#JcM`/яNPhc/J@!    L<<991/<22990K TX@878YKTX@878Y@0P ]!#!"!!##53546JcM/ѹ{Phc/яN9;>@   Y W Y <<2<<2122220%!#!5!!5!3!!!oooo\\HF103#F@ 10%3#ӤR@m '@    1<20%3#%3#ӤRfӤR@@q L #'3?K@D$%&%&'$'B@ .(F4 :&$L%IC'1+C =  1 =I 7+ ! L9912<<2220KSXY"KTK T[K T[K T[K T[KT[XL@LL878Y"32654&'2#"&5462#"&546!3#"32654&2#"&546"32654&WddWUccUt%ZVcbWWcdWccWUccܻۻۻۼܻۻhm'$um'(uhk'$uN'(uk'(uk',/u`m',/uXN',/u;k',/usk'2'usm'2'usk'2'u)k'8u)m'8u)k'8uy` F1/0@ @P`p]3#`?f7@ u91290K TKT[X@878Y3#'#fJ7c@$   VwVv99991<<99990K TK T[X@878Y'.#"#>3232673#"&9! &$}f[&@%9! &$}f[&@Z7IR!7IRb+/10K TKT[X@878Y!!V)9H W@ VV1<0K TX@878YKTKT[KT[X@878Y332673#"&v aWV` v HKKJLDf,@ d10K TX@878Y3# _@ V xV10K TK T[X@878YK TK T[K T[X@878Y4&#"3267#"&54632X@AWWA@Xzssss?XW@AWX@sss#u@  ' 1/90!#"&'532654&'T76xv.W+"J/;<+->i0Y[ 0.W=fB@991<20K TKT[X@878Y3#3#߉fxLu @   '1/90!33267#"&546w-+76 >&Dzs5=X.. W]0i?f7@ u91<90K TKT[X@878Y373xu ?@   : y<<991/900P]3%!!'79Pw^Mo;jnH ^@  z z <<991/90KTX @ 878Y@ @ P ` sz p ]37#'7Ǹ}Lɸ{JZjXjm'6uof'V\m'=uXf']@ <210##    g@    2  y<291/220@(   ]]! )#53!!3 !iP`P5~.,qu('@^%{&%#${##{#({'(#&'('%$%(('"#" ! B('&%"! ## #)&' ! (%#" QE)999999919990KSXY"?*]@v%+("/#/$)%-&-'*(6%F%X X!` `!f"u u!u"%#%$&&&''(6$6%F$E%Z Z!b b!z{     {zzv v!x"**']].#"32654&#"5432''%'3%F2X)6 ~r4*!M!ü޼z&77kc\̑oabk'<su=Vf'\^ =@   ? 2291/0@ ?_]332+#32654&#'ђV>@ GF2210@ `]%#3>32#"&4&#"326s:{{8daa-10!!ת? @M    B   <291<290KSXY" '77w55v8vL57y5yy5 ,@   |]|| 12035733! c)t'+n^J@$}}B ~9190KSX2Y"!!56754&#"56632 "?XhU4zHM98rn81^BQ##{l0b(H@'    #)~&~ )999190#"&'532654&##532654&#"56632 \e9}F4wCmxolV^^ad_(fQI7Z`mR|yOFJLl?<:=svcE`''5 d?''5db''5 dsm'* uqVZH'JP', /uu'6ou{'Vs'k'&-uqf'Fs'm'&-uqf'Fq$J@$ "    GE%<<1/<20`&&&]!5!533##5#"3232654&#"F:||ǧN}}daDDad10!!dHF103#F1@: "+ /) 2+"!)#&  , & &*!/<29999999999122<20K TK T[K T[KT[KT[KT[X222@878Y@z  1Ti lnooooiko o!o"o#n$l%i'i-  !"#$%&'()*+,-2   USjg ]].#"!!!!3267#"#734&5465#7332[f A78 ʝf[Y`(77(6bbiZȻ{.# .{ZiHH"{/ #/{"G)@ dd1<20KTKT[X@878YKTK T[KT[X@878YKTKT[X@878YKTX@878Y@````pppp]3#%3#^ys@B10KSXY"K TX@878YKTX@878Y@ %%6FVjg //]]3#7Ju@!  VV 99991<2990K TX@878YKTX@878Y ]'.#"#4632326=3#"&9 $(}gV$=09" (}gT";9! 2-ev 3)dw @B10KSXY"K TX@878YKTX@878Y@*$$5CUU//]]#ę1w@ 91<90K TX@878YKTX@878YKTX@878Y@ //- ]3#'#Ӌ1@ 91290K TK T[K T[K T[X@878YKTX@878YKTX@878Y@ "  ]373Ӌ ? @   ] <291<290KTKT[KT[KT[K T[K T[X@878YKTKT[X@878Y@T /9IFYi       "5GK S[ e]] !33##5!55bf]my9 j@ VV120K TX@878YKTX@878YKTKT[X@878Y332673#"&v cSRav 6978w{zf103#  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~>: ~1BSax~ & 0 : !""""+"H"e%  0AR^x}  0 9 !""""+"H"`%^ChVjq_8 (Bbcdefghjikmlnoqprsutvwxzy{}|~f55q=3=dd?y}s)3s\\?uLsLsyD{={\{fqqq/q999qqJ+o#7=V;=3XyysLs{{{{{{fqqqqq9999qqqqq9\3 'sLfR#hd+/s`N{H?55=ZyyLss/q%%=V^33 / /9% qyy\\\\;LsLsLs9#LF+o{\3X3 q=55^5bb3sq\+osfqsfqqds 5?+   !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~     sfthyphenperiodcenteredEuroc6459c6460c6461c6462c6463c6466c6467c6468c6469""""XO!nE~Le  R s  X : i  = z /Eu)pP@"m#{CwRw [ r !5!B!!!" ""#"0"="J"W"d"q"~"""""""""## ##'#4#A#N#[#h##$4$%3%S%&&'K''((X()_*%*\**+z+,D,,-P-..R./0A011!1P12H2z23F3p3p3}3334z44445595g55556[667C77888J999)969C9P9]9j9w99999999::{::;;^;;;<"<_<<<<<<=c>;>H>U>>>?a??@:@K@\@m@z@@@@@@@@A@AVAkBEBBC_CCDUDE*E?- x$%&')*K+-r./2934K57D9:;< =IQR&UYZ\bdg9xy&z&{&|&}&9 999 K$$$$$9$&$*$2$4$6$7a$8$9}$:$;$,,G}G  @ 2 YYdhd@%%Y   %Y%&Y]%]@%dX:t:2  Y~}|{zyxwvtvututYtsYs}rq&ponm @lkjkjj@ihihYhgYgf\ fedcdcb]ccbW%b]b@a`_.`_.^Y^]\ ] \ [Y[KZYZYXYXW%VUTSRQPO%PO%NMLKJIH H@FEFEDCDCC@BdB@A}@?>,>,=<;:94 9287265 6@5 5@43 4 3 211}0/0d//.-,+,K++**K)()(' (' &%$%2$#"#"!%!   2@   @:%dd:%:%K       @d:@  @d++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++, %Id@QX Y!-,%Id@QX Y!-,  P y PXY%%# P y PXY%-,KPX EDY!-,%E`D-,KSX%%EDY!!-,ED-ff@ Q/10!%!!fsr) Y@.   :UTS 991/0KSX9Y"!!!!iqlhE^h@VS1<20###h++=P@6 Z Z W   91/<2<<22<<220!!!3!!!!#!#!7!!7!F+`aa)5E)6````5F5`hh7 !(/v@C# ")^] ^][* [!*)#"&- !&-& - & 09999991/<29990#&&'&&54$773&&'6654&T;]1`x=ҿ+,+eX1?w93TdA7ckI-+&7=7 "%/$ DQ=07VK39q '3M@+e .e"a(ee a`d4+%   1  + %49912<0"32654&'2#"&54#3!2#"&54"32654&Vo=>Wn? ֝ k֝ Vn==Wn?hWWUWꭍ ꪍXYTX9&0@[   0'0%&$''0:0' - -iYf!gd` '*$ * 0$* 1999991/99990KSX999Y"]@`  * 9 K K'[ \'bm n'   &0' ' +'+0;';0III H H'J0ZZ\ \ \\#X'honki i l0$]] >7!!'#"$5467.54$32.#"3267m)>Z7+xJTv{ֿ.[e5WOVd54&#"6$3278{hmpe_@PMRZKN=21Ū(i@,s ^ x s ^xsw#d`)   & /999190K TK T[X)))@878Y!"&'32654&+732654&#">32q~wh9YiŖ1yUl7vp%s%%)49kYYraLP,*  @;         : uS    991/<290KSXY"K TK T[K T[K T[X@878YK]] !!3#!!Lo556`ARJ'@9:^]]sys vS `  999190KSX9Y"K TK T[K T[K T[X@878Y!!>32!"&'32654&#"j7v-,_0r}t;ol|Tt ѹ12/HD`q+-fH 'r@! ^ ]sss%d`(  (9190K TX(((@878Y@```` ` ` `````` ]"32654&.#">32!"476$32rZPqX6VQN]|pxG^ἔYc`f,,ȵ31ک!s 3@:vS991/0KSXY"!!!'f-<-F #/t@$ *ssw$sd`0- ' -!0991990K TK T[K T[KT[KT[KT[X000@878Y"32654&%.54$!2!"$546"32654&ufXwglkM&zt3f{YIf{YtSauSb-e谠-/yŰwbDTwbEST7'@ ^]s s "sd`(%% (9190K TX(((@878YKTX(@((878Y@o o o ooo$o%o&o' ]73267#"&5!2#"&2654&#"T5WQM]i|qx_rWQqX!-+ȵ31کa [b`fT`N@(:TzT91/0KSXY"!!!!-iJChL`}}` Z@.   :TpTz   9910KSX9Y"!#!!h;լiJu}=@|{29190 5<'@ o}o<210!!!!=@|{<91905511Jo!@I !:!  Y]iUTd  ! "999991/9990KSX9Y"!!!766776654&#"6632hFWJOHQLPs>ajLP9d1P}f:=j69=CB:*(wf<>M+\j L]@5IM-.* LF~*1~!=WM -.L C'7M991<<29990"32654&#"&543273654!"!267#"&'&5476$32!"#"&' jQMjP'-ZS}˅v\`Kvhm⎇bxroirg!ґTXɒV^KSBTEKnWSbbRMl>ɰH]\Yv{ @A     : uS   91/<90KSXY"]@  + / ?   + ) ]]!!!!!uLZ+?+ h@::S    !9991/90KSXY"2654&##2654&##!!! 3gtQL>ebeLbk#A h[;>s~vKHyn՜JJ@ uu d` 991990@  ))]]%# 476$32.#"3267يrroqty>`oeF12J=78IDIN+J@@!  : S   9991/0KSXY"3 4&#! !! :/""`abjq.#EQQzm\+\ T@/    :S    91/0KSXY"!!!!!!N9s6g:B7+\ O@+    :S  91/0KSXY"!!!!!N9s6g:{J9 h@6  : uu d`!  !299199990KSX9Y"%# 476$32&&# 3267#!sƳrqw;zv8p991VoGEC=87GF"+ u@A     :S    91/<<0KSXY"!!!!!!No8n{{9+y+6@:S91/0KSXY"]!!N+f g@%  : S    9991990KSX9Y"K TKT[X @ 878Y!!#3267N:N9  :S  9991/<2990KSXY"] ]!!!!NVlR + J ;@ uud`! !10KTX!!!@878Y4&#"3267> # 476$쟗s? qRn{oja*k4s?=QE'la_[+{@H    : u u S      99991/<9990KSX9Y"] ]2654&+ !!2!.#]dL3o#9ȫNh'p"a\?zvJEոXsoSR'@;    : uu%d`( " "(9999190KSX99Y"]@9 9 9 9 9 99999 9!I I I I I IIIII I!YZ Z Z Z Z ZZZZZ Z!m m m m m mkkmmmmm m!y y y y y yyyyyy y!< )```` ]].#"!"$'32654&/.54!2R>qiGoƹɑ=zJ}e.y87ZP39'265EMLlT7= '+%b`@:S991/20KSXY"K TK T[KT[X@878Y!!!!`9NTg@:     : ` S  91290KSX99Y"!3267!! $5467N ih$A 0Jba$Y;q@':S91/290KSXY"]@%5)76 ]]!!!nj5\+ % @J        : S    91/<2290KSXY"KTX @ 878Y@j       / / ? ? O O YYY      &%#' 637 0 BB UZVPP  !]]!!!!!d.n1}L2B==+o @I   :  S   91/<290KSXY" ]@4  -$ <2 LC    '$' * 79 : DHG ]] !! !!nyKa  +yx@5:S  991/290KSXY"]@  D]]!!!yy)o 9@:S   91/0KSXY"!!!7!08%:-<F@":lk9910KSXY"!!!!Nl--B5@ S9103Xƾm@@:lk9910KSXY"!7!!7!3+ +m`@ S91290##fg--10! f2@ 910K TKT[X@878Y#'fx#{ +@c        : "  g#"g&`  )#"),9991/99990KSX99999Y";"]@83!0"C!@"S!P"c!`"!"!"!"5":#:$K#K$[#[$k#k$#$#$ ]]"326?%!7#"&54$!37>54&#">32QGn}XsK8}{oa6p~ SQ=Fy)d_ BC.."Q?j @K  : ii`k    !  9991/990KSX9999Y""32654&!!6632#"&?cX_.!.huRkMH[sw\ifk+b]ρd~]L{3@ ii`  99910&&#"3267# $5476$329HCފ~TI7Y[ibmU=02u21ډgqnJ @K  : ii` k    ! 9991/990KSX9999Y"%2654&#"!!7#"&54676632uaW_ti!RkMH[s]hfkXb]ρd~]J{'H@( g$g`( ' !  (9999190!3267# $54676$32%6654&#" q7[Wg `Ri'9mlDC0/݀dx|(Yo  P^spfR@<      : igkz     991/22990KSX9Y"K TKT[KT[KT[KT[KT[X@878Y@ ``pp]#"!!!#37>3R/KE /1Ѩ2%7CO`N)Fuy+@Y+* )'&(  :& &g i z&i)#! ,99991/99990KSX99999Y"%#"&546766327!!"&'3267"32654&XViMHXv7i:h^5UY!T^Z^b\qdy\c3 !65fkhm?;@L   :  k  %#  9991/<99990KSX999Y"0]!>54&#"!!>32)q GAol.hucm VH9O@Fa^ N?X@(:zk  91/0KSXY" ]o ]!!!!h.h9`F @:     : gz k   ...9991990KSX9Y"K TK T[X@878Yo]!+73267!!h.ղ-?e]+h9`[? @A      :zk   91/<90KSXY"]@<     )EQXcwpvz  *ESdpus ]]!! !!mhug^?6@:k  91/0KSXY"]!!mh?{+@x !    "#$# !$#(')&$#%$$#:!  &$ )$z" %"!$  &$ %% $# ,99999991/<<<299990KSX9999Y"]@#     /-]>32!7654&#"!>54&#"!!>32Sr q?6gls >7clh!TgtgnM1VH|#7@HKM8?`_`w?;{@N   :  z  %#& 9991/<99990KSX999Y"0]!>54&#"!!>32)q GAolh!cm VH9O@F`a^ NJ5{ )@ii ` !)! 10p]"32654&2#"$54676$a]a@ZVf\Vd{joincvx~dtxVj{@M:ii`z !   9991990KSX9999Y"0!]%!!>32#"&"32654&s-h!RkMH[sAcX_ b]ρd~]7\ifkJVu{@N:ii`z  ! 9991990KSX9999Y"7!!#"&546766322654&#"isPkMH[saW_Lb]ρd~]]hfk?\{@6       :  z   & 91/9990KSX99Y" ]@#    @@@@@@``````].#"!!>32%Z4 fh'GՂ0!/`jq{{'@9    : gg%`(  ,*"(9999190KSX99Y"K TKT[K T[X(@((878Y@i j j jjj j!````o)vv]].#"!"&'32654&/.54$!2{5i]js@^Aٮs}6atqr@dAs=2473$ .$$9:64$( (X@>  : iz i  9991/<2990KSX9Y"K TK T[KT[KT[KT[K T[X@878Y)]!!;!"&5467#3=n3\=H3Ӣ\1>& .(@!>{s`@M    :  ` z &%9991/299990KSX999Y"K TK T[K T[K T[K T[KT[KT[KT[KT[KT[KT[X@878Y0]!3267!!7#"&546gq FAolg!\p9P?F'a`#Nm`@&:z91/290KSXY"K TKT[KT[KT[KT[KT[K T[K T[K T[X@878Y@FVffj]]!!!Jkn`j` @F     : z    91/<2290KSXY"KTKT[KT[K T[X @ 878Y@(I   %% GHF W fh xty ]]!!!!!J'R)P`%`^` @I     : z    91/<290KSXY"KTKT[KT[K T[K T[X @ 878Y@N  (%)+ 1GJ   ((')+* 65GEGFG G G kch i  ]] !! !!t#8{=#LbF`@C :  gz   /9129990KSX9Y"K TKT[KT[KT[KT[KT[K T[K T[X@878Y@   H]]!!+7326?R;wʠ%qXa&!`6ρ;J<` x@,,:izi   91/0KSXY"KTKT[KT[KT[KT[X @ 878Y@)&/ ]]!!!7!1FC1/`fZ0@g0/.-,+* $%&'() "!# : 1 #)*k110-*)&#   19999999199999990KSX9999Y"#"&546776654&##7326776633#"3+ڳ%[u@->'%+Fg!vrPJ Qtm{{<(7XHtWZMC(.0C410#Z0@l+,+0,,+ :+1''%,'%/k1'&0  /0+",%(&01999999199999990KSX9999Y"32677667&&546776654&##73233#"##Fg!wsPL !QtC+۳']t?-=%%mYZND-*2D3z{="6XHuR#@ o o1990#"'&'&'&#"56632326j`k^Xbk`k^VRPE:=MSPE:=K{k'$Du{m !@P!!    ! !:! u W " !  "99991/<9990KSXY"]@2!!!-!9!?!Y!!!! ! !(* +!!! ]]32654&#"!!!.54632!M66MN56Mh+u9vuZP6MM66MMP%M,uu0ML?Jo'&+\k'(u+m'1uuJk'2uTk'8mu#f'D#f'DC#f'D#1'D#9'D#'DLo{'FJ3f'HJf'HCJf'HJ1'H?f's?f'Cs?Pf's?R1's?;9'QJ5f'RJ5f'RCJ5f'RJ51'RJ59'R{sf'X{sf'XC{sf'X{s1'XN; [@0:S    9991220KSXY"!!!!!7!JJ!--#/dL @  -.-10"32654&'2#"&546HdcIHdeGBz0/11-0|D\dHHbcGHd3/0xDCy-03"a@8    i i #   #99199990&&##667#&&5%3f{94p9K@ YG8HU77l(77DX.рJm520h10"#"#}@E  :^] gsd s  99991/222990KSX9Y"&&#"!!!!3#737$!27;G}!u-@34?-!4.\%({ F=3?@F@1:4%  $+1d@$7! =%+.47! :=(!11.=070.(@9999999199990&&#"#"&'732654&''&&5467&&54$326654&+Z=P^19Z96T_0f=Ma0Owp42S!PUaPXr(&=22)No{:$X6++?0/%=6f{;)\5Y>=V@[8=]'` 2104676632#"&'&&'535II245633JI326J235624IJ336633; c@.  :S  9999120KSX9Y"!###&&54+Ӽ9fN?T7@^&% %&%67734257:5,& % 2gg2gk`667&%)"/ 57) ,/), "/, 7 89999999991/990KSX999Y"6$!2#"&'732654&''&&54676654&#"!*5f =TNNJ18o4Rd.M=C<ROfuZڰ0I]H7H+;[L=(A7+/e?1CFkp 4Lb@8-*+'0!5 2+A'*,$0-+$!1837$4-;6-34GM299991/29990"32676654&'&&#32654&'2#'&&###2#"$'&5476$yWWWWWVy{WWWWWXϲ##NOM+i`)Gok&: 1mmllmmmmllmm3WWWzyWVVUWWyzWXV5442wyVpP:NAD7nmmmmnnmmmmn1IH@(  2&>7,- 486 -9DJ21/990&&#"3267#"&54632'"32676654&'&&'2#"$'&5476$+9o9q~r@s.A>EyWWWWWVy{WWWWWXymmllmmmmllmmf%#rs~$#WWWzyWVVUWWyzWXVnmmmmnnmmmmn'R v@>  :   S ;: : ;:91<<2<<9990KSXY"73#######5ww㪉LqKBMPf\10K TKT[X@878YK TK T[X@878Y@ ))<<LL]!#+%Jf7;1u@99991<20K TK T[X@878Y@*    0000 ////????]]3#%3#f111 =@!   o} o  <291<2<2.990!3!!!'7#5!7!^P1}`@M  : u S  91/<90KSXY"]@ %%] !!!!!!!!!yw9s5f9A7Db^++ (6@D(76) ,&# ,'#,u#ud#`72'76) 2 2&(2 7.9999999199999990@             ! . / 0 1 2 3 4 5 6@@@@@@@@@@@@@@ @!@.@/@0@1@2@3@4@5@6PPPPPPPPPPPPPP P!P.P/P0P1P2P3P4P5P6H].#".5476$32%#"&''3267>54'&'(zR**nkaXf--oj\f{'zNs?a_\t5 /B@#  $'!-!0 $<*<099991<999032654&#"&&#"326#"&546326632#"&+vIZqgLHw+tKZqfMGzDaƯZcG_ů[1CDeOMeeCCdOMeia~q~n .@o  o = = <2<21/<<0!!#!5!!!  bb '@  o  <2291/90%!55PN '@ o  <<291/907!!55%  @S  :    S    9991/2<<<290KSXY"!!!7!7'!7!!!!!!:RRA'o%#pH%%h;`LL LLT\`&@W   &#$"% : % "` z'&%  '9999912<990KSX999Y"!3267!3267#"&'#"&'7-hVRfxh'29b-Xc>XJ]`T u(INpu# LROO/0;R)8@'! '!* $$*99919906654&#"#"&54632#"&54324&#"32;'#S0@eID`IFa~q9WzC2EqG Ur|tx)w 0@    91990!!!!5Bl_{Nw@??120!!!!)JD/@    991/<22990#!#!#"663J'7: Ddd>Dǜ3,:@ *# - *(&  @A @-9919026732#"&54&#"#"&54632jciRA@Ae '&>ÄkTF54&#"7>32/'\p>5TVBe}MLXU#bK=9F,4qXT@}LFod ,.##z3^u\ b@ d   DD 999910@,@@@OOOPPP___ JJJEEEXXX ]]2#"&54!!"32654&Ϸ*7&XcFCcGMѹLPKQ7>@    GFG F 9991/<2990!!654&#"!!&5! #~˲˄~#~zx89xz#VˤUy9yǤ#{ @J@T7@5= ,&A A5&%"g5) @G=g/)`KJAD76 ,:%&D5:@ 2:D2K9999999999912<<<999990@6; ; K K [ [ k k 2?0@B?@@R?P@b?`@?@?@?@]]"326?>32>32!3267#"&'#"&54$!37>54&#">54&#"QGnuh9_Ղ p5BuK<|xr^aQi%SQ=Fy)JIMMI-\/9mlCD0/ghif BC..  P^to$+{@@$,+%("(#(ii`,+#, %+ +" $+!! ,.99999999199999990&&#"&&54676$327#"&''3267E- ''[WdhDa))ZVemD`E/J;L~duw..o;Octz12pL!@M !!!:UT Y] i` S"  !  "9999919990KSX9Y"!3267#"&54677667%!!h VKPFPLPr=`jLS8Fh3P{f<=h69=DA*(wfK@/91/90!!!#-3mV?}' 4@     991<290772+Z%L1+Z%'qsqu' 4@     991<29077%%77%%7u1X+#L1X-#^ y@=    :T     999991/<<220KSXY"!!!!!!%hKhKBhL}}}{k'$Du{m'$DuJm'2uV ; !@8    : S   !  "91/220KSXY"]@ ## #0#`#p## ]#";!!!!!!# 476$3iLqkh9u8g9A9y7`Ѐ;/1J.zIFJo{ 3?j@:+( g:i g4i.(`@ 1 +=1=17!" @999999912<99906654&#"!3267#"&'#"$54676$326632"32654&`Ri%# p7:Z։[Ue@^ a]a  P^un9mlDC0/NRRNcuwQQQQ(]Bjoin@ q9910!! 3@ q9910!! 3X7 f@4     : S   H  991<20KSX99Y"!3!3X7'ը:'ժX``X b@2     : S H  91<20KSX99Y"!#!#T7ըT8ԩ``X9@:S910KSX9Y"!3;:'ժX`X5@:S10KSX9Y"!#wT8ԩ`V *@ on IJI <<10!!!!!!33Xˁ#uv@A:91990KSXY"  9%-F1'\yk'<ud10#3 J=#/@H ! ! $!* 00   'LKM-L K022999999122999990'7&&5467'766327'#"&72654&#"ϙљ0l=6l9ϘϚ.j?:l[\[~ Ϛ1k??l.͚Ϛ7n6?i/ϙ\\\]~}'@919071+Z%'qN'@ 9199077%%7N1X+#fb@c  : i gkz  &N&991/<22<2990KSX9Y"K TKT[KT[KT[X@878Y?]]!!!!#"!!!#37>3h9h/KE /1Ѩ2%ܐ7CO`Nfb@L   : i gk z    N&991/<22990KSX9Y"K TKT[KT[KT[X@878Y?]!!!"!!!#37>?#KE /1Ѩ2%)7CO`N;@F       : X S       .9999991<2<20KSXY"!!!!!!!7!!7!JJ!-X#/JI/!X-!<}`/@:T910KSXY"!!hL}5@:T10KSX9Y"!#T8ժ`  b@2     : T H  91<20KSX99Y"!#!#T7ըT8ժ``q 5 #/3?Kb@7 eFe:a@e2e$a0*`42dL1C=3'!    - ! 'I 7 'C =L9912<<22202#"&54"32654&!"32654&'2#"&54#3!2#"&54"32654& ۜם Vn=>Wm>$Vo=>Wn? ֝ k֝ Vn==Wn?魍WWUWWWUWꭍ ꪍXYTX{k'$Du+\k'(u{k'$Du+\k'(u+\k'(u+k',u+k',u+k',u+k',uJk'2uJk'2uJk'2uTk'8muTk'8muTk'8mu?`0@:z& 91/0KSXY"!!h`f8@ 991290K TKT[X@878Y!#'#!f9@"    91999999990K TKT[X@878Y@(         (]]'&'&#"#>3232673#"&{1&"4 ^$C%5"&4 ^"<T%E<+C>=X,9910K TX@878Y!!bw%TFo@  91<90K TX@878YK TX@878Y@   ]332673#"&546WVPQh"͜F AFGI|y;-1K@ 9910K TK T[X@878Y@  00//??]]!!11y @@  KK10K TK T[KT[X@878Y#"&546324&#"326wxxwTABTUAATxxxxATTAAUT-o#@   991/0!#"&'732654&'%"1a3(Q&BL4X*hs 9/E7%f@ 1<20K TK T[X@878YK TKT[X@878Y@-   0000@@@@]3#3#ɮbfxoF@@  991/0@IIIIYYYYiiii ]!33267#"&5463G41-%R+9Z'joJJP"& EBA~59fP@ 991<90K TKT[X@878YK TX@878Y!373x `@6    : S    .9991/90KSXY"!%!!'%eTi N7bh=iJׇ @1: k   .9991/90KSXY"KTKT[KT[KT[KT[KT[K T[K T[X @ 878Y / ]!7!'%hfdu` NˑVDˇRk'6uf'Vbk'=!uf']T@ <210##  m p@:    : S      .9999991/<20KSXY"!]3#3 4&#! )#393F;0""`adk}3/#EQQ{m\Jm,@c*+,+)(),,+&'&#$#%$$#'(&%&(#$#(( !"(:,+*)&%$#'"'i i`'k-*+,) %$'&#" + !! -99999919990KSX9Y"&&#"32654'4#"$5!2''%'!%4f4d^@A[Wel%1W$#T`R#ms (j\tdtx] l^tj^yk'<uFf'\+j@: :u uS  9991/0KSXY"!32##!32654&#N+ WSY9BP̈eb׿uOVN\zRVVj@L:ii`k  !  999991990KSX9999Y"%!!6632#"&"32654&shuRkMH[sAcX_b]ρd~]7\ifk on10!!) /@   <291<290 '7NNNN3NPPN) @0 OO:  b     9991290KSX2Y"]@   ))99JMMJJ]3?33!fۅ9 41Z\sc@!  dP99919990@ 55YY ]]!!7>54&#"7>32y!\nPNA:DG$SElxD :c*(..,mdOTRj(t@- #  #d)   PP& )99919990@        (]#"&'732654&+732654&#"7>32KNP>!:w54&#"7>32#33?33!Dv ^nQMB9DG%SFlw=mfۅ :a))0/,mdOT I 41ZZ(+6:@r*6,6)+),,6)O*)-2-+O2-4O2-3O22-: #)* .*0 9#d40,7`2*)43+5:.120/-8   /-PP& 25- ;9999991/2<299990KSXY" (]@:)+*,(.9+:,9.Y)Y+i)i+         '[)l)]]#"&'732654&+732654&#"7>32 333##7!7#3LOQ>#:w3232673#"&j1!$4 _%A#5$'5 ]&@!% @8) =:`:910K TX@878Y@ //]#hG@ 991<90K TX@878Y@/// ]!#'#\3 M@ 991290K TX@878Y@//// ]!373N3@  1290K TX@878Y332673#"&5N QOHj'Ɣ<7=6}!>@ 9910K TX@878Y@ 0000]]!!/  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~>: ~1BSax~ & 0 : !""""+"H"e%  0AR^x}  0 9 !""""+"H"`%^ChVjq_8 (Bbcdefghjikmlnoqprsutvwxzy{}|~f+7q9s/) R3 ^;JXf-T3T3\1{+J+w+w+J++3++++J+J)+ub1+ f#?LJmJ{f)??R??V??JJ?X{7d)7Z1{1{Jw++Jf#f#f#f#f#f#LmJmJmJmJ?????JJJJJ{{{{N'?'7` Z;)L'3X^'7b#LVL3}3u^1{1{J VVJ''  7VJL}LNLfLf  ' q1{w+1{w+w+++++JJJ?=Ty-%5HJ7+\RZJ)+JLJLJR3 +`3N   !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~    sfthyphenperiodcenteredEuroc6459c6460c6461c6462c6463c6466c6467c6468""""iE%JbG>lq  < _  Y F M w mEK?Hn+McIgu$NLNh W !{!"q"#,#n#{$ $-$:$G$T$a$n${$$$$$$$$$$% %%$%1%>%K%X%e%r%%%%%&.&'''(3()**w*++E+,- -D-s-..//8/[//01D12t23x334+45'5L556!6!6.6;6H67Z7v77848c8899"9/9E99::;:;;;>u>>?#?i??@6@p@A+A8AEARA_AABBBCCCCD8DE EF{G`GmGzGGGGGGGHpHHI5IqIJ J6JlJJK` R79k:;2<$&$&$&$&$&$2$7<$8$9u$:$<$F$G$W$Y$Z$\k$d$g$h$o$$$$$$k$$h$h$$$$$$$$k$$$$$%9%:%<%%&6&&'&'-Rfpgmp9)gasp glyf4h)&hdmxEHheadO$6hhea. $hmtxy,kernlocaXz[maxp} nameYͿpostx<prep|a!\::N:: R^0p   t t  &   ; 0  0   C , [ `   0 & Copyright (c) 2003 by Bitstream, Inc. All Rights Reserved.Bitstream Vera Sans BoldRelease 1.10BitstreamVeraSans-BoldCopyright (c) 2003 by Bitstream, Inc. All Rights Reserved. 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 the Font Software, including without limitation the rights to use, copy, merge, publish, distribute, and/or sell copies of the Font Software, and to permit persons to whom the Font Software is furnished to do so, subject to the following conditions: The above copyright and trademark notices and this permission notice shall be included in all copies of one or more of the Font Software typefaces. The Font Software may be modified, altered, or added to, and in particular the designs of glyphs or characters in the Fonts may be modified and additional glyphs or characters may be added to the Fonts, only if the fonts are renamed to names not containing either the words "Bitstream" or the word "Vera". This License becomes null and void to the extent applicable to Fonts or Font Software that has been modified and is distributed under the "Bitstream Vera" names. The Font Software may be sold as part of a larger software package but no copy of one or more of the Font Software typefaces may be sold by itself. THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL BITSTREAM OR THE GNOME FOUNDATION BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL 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. Except as contained in this notice, the names of Gnome, the Gnome Foundation, and Bitstream Inc., shall not be used in advertising or otherwise to promote the sale, use or other dealings in this Font Software without prior written authorization from the Gnome Foundation or Bitstream Inc., respectively. For further information, contact: fonts at gnome dot org.http://www.bitstream.comCopyright (c) 2003 by Bitstream, Inc. All Rights Reserved.Bitstream Vera Sans BoldRelease 1.10BitstreamVeraSans-BoldCopyright (c) 2003 by Bitstream, Inc. All Rights Reserved. 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 the Font Software, including without limitation the rights to use, copy, merge, publish, distribute, and/or sell copies of the Font Software, and to permit persons to whom the Font Software is furnished to do so, subject to the following conditions: The above copyright and trademark notices and this permission notice shall be included in all copies of one or more of the Font Software typefaces. The Font Software may be modified, altered, or added to, and in particular the designs of glyphs or characters in the Fonts may be modified and additional glyphs or characters may be added to the Fonts, only if the fonts are renamed to names not containing either the words "Bitstream" or the word "Vera". This License becomes null and void to the extent applicable to Fonts or Font Software that has been modified and is distributed under the "Bitstream Vera" names. The Font Software may be sold as part of a larger software package but no copy of one or more of the Font Software typefaces may be sold by itself. THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL BITSTREAM OR THE GNOME FOUNDATION BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL 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. Except as contained in this notice, the names of Gnome, the Gnome Foundation, and Bitstream Inc., shall not be used in advertising or otherwise to promote the sale, use or other dealings in this Font Software without prior written authorization from the Gnome Foundation or Bitstream Inc., respectively. For further information, contact: fonts at gnome dot org.http://www.bitstream.comf3f=ffTbfTfmf3bq%fHZfm99Xm=fuff9{{X3fLfLJ#DDf?;Pw /X#/553X sf+j-j!f#^`3B3\fy```{j\{`bXP1L`%!JJ7{'}3Xy9bsA&%$!:$#"!:"!: d}}      Y    & Y @ &  .A@}>,,G}G  @ 2 d۠d%%%   %ё%Д #&̑ɻ]ɻɀ@%]@%dĐ::2  }& @ ]%]@..@   K%%%2 ~}|{zywvwvututsr}qpo,o,nmlkjihc h2gf2ed ed d@cb c b a`a``_ ^]\\[Z[ZZYXWV@VUTSRQRQQPOPONONMLKLKJKJIJIHGFGFEDCDCBA%BAA%@?@?>?>=< =< ;d:987656%54554 4432 33@2 10100/ .-,:-,%,:+d*d)(''& %$#@+$#" "!!@  %@ K}K%%dd   2     @   @d  d++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++, %Id@QX Y!-,%Id@QX Y!-,  P y PXY%%# P y PXY%-,KPX (EDY!-,%E`D-,KSX%%EDY!!-,ED-ff&&/10!%!!fsr) @ <2991/0!!!!h33h^h@1<20###h++)K@1     91/<2<<22<<220!3!!!!#!#!5!!5!!!`aaE````HFR`PF#*1s@? %$ + ,#,, (/($ +/ 2<<991/99999990#&&''&&546773&&'6654&}osy!dede GUNWWP-.);?7* "*/(BE5;CBBDCB '3c@5  % ."( 41+  1 +%49912<0KSXY""32654&'2#"&546#3!2#"&546"32654&3GNMHHLMGֺ%պպHNNHHMNh{rs{{sr{ؽ۽ ٽڽ٨|rs}}sr|{&06@Y     ,-./+0()'%0' - -! '*$ 0$*$  *  199999991/99990KSX999Y"']@   ' 0   0%/ / %&? ? @K K K/K0ZZUZ Z U(\.\0X2_2dig`i i d&2, ' '* 9 5005@J I'I(WW\ ['ggl ]] >7!!'# 5467.54632.#"3267577oc%Xbi*([k^PMU1ABwCt2>FnkmFDےj5j:0.;6"W/wGs))@ 10#+ @ 29910!&547!י);: @   29910654'!)?C)9F@(      <2<29912290%#'%%73%JLLNLNMXX "@   <<1/<<0!!#!5!    m9@ 10!#hduo10!!ot91/0!!h}B/99103#mb/ #@  10&#"326! ! i||jj|{j@'&@mstm (@  1/20!%!!!T[nT HH5@)% 9991/2990KSX9Y"K TX@878Y@&**"""555BJFF]]!!>54&#">3 N!IFuZzz )~B~DiMLH+-zӱ(L@+  #)& )999190!"&'32654&##532654&#"663 sqlg~]^rl#!%'%%)67jcfi[]V^*) \3 C@ !  ! %    <291/<290KSXY"!!3#!!Z@jRJ=@"  " 190!!663 !"&'32654&#"v,Y00{zaSl 12/FFuv+-# $7@ "% %$%190"32654&&&#"6632! !2eeeefeev_PB[uEgჃ-+11ir E@%91/0KSXY"]@ &5F]!!!e'1} #/G@( '-0 $*& '&!$0991990"32654&%&&54$! ! $54632654&#"lttlkrr|כc\ZbbZ\cvnnuunou)ž)*ސUY``YY_`j$7@  %%" $%19073267#"54! !"&2654&#"\RDZ9$@ieffeeff!++22 "vYN`@<21/0!!!!ii`}}N` %@  <210!#!!idiu}=@29190 5<'@ <210!!!!=@<91905511J!H@'    "<299991/9990!546776654&#"6632!!Bj@95`VQfy]N^@D*i1Rb:4\.FOCB:*(ǿbY9>K-o Ml@: 40LM3 30 07$CN34L **)(I(*)4=N<991299999032654&#"#"&54632536654&'&$#"3267#"$'&5476$32!#?iZYjkZXiYثY|:;_tZked~Yk}٘~~On{KM'{zyZGOPGKɝdIz=;bɵdbg^Pag}}IJ}|b~ ' @@     %    91/<90KSXY"K TX  @878Y@ / V f  t   %* IFGH XYVWhifg` t{zu{t      /]]!!!!!F_}))}+%R P@%    !299991/90@ ""/"P"]2654&+2654&+)! [^^[tutuH|B7fPNMQsbcaay$ռmf\;@    - +21990/_]%# !2.#"3267\j}Lu}jksskR78ef87IDDI9.@  -. 99991/0P]32654&#! )=TMwiffixjq#ateeta 0@   21/0 P p ]!!!!!!rg +@ 21/0 P p ]!!!!!rgfK@%    1 3/-+19990_]%# !2.#"3267#!ʥLy}|@   221/<<0@P ` p ]!!!!!!89+y=71/0KTKT[X@878Y@P]!!+f= L@   991990KTKT[X  @878Y @ P ]!!#3265N3IO@UZfi ]]!!!!mR+ff 2@  -7-+10@ /?]"3254 ! f°±hhgddjk 1@   - 299991/0]! !#!32654&#1pzzp_mddlffb@   - 7-+999190@,  '/V S f ` w w p  Y Y YXj i x ]]# ! !"3254fgk-¾lkh\@2%       29991/<9990KSX9Y"]@66EEVVPee`]2654&+!! !.#yiiyL'O}@f7q^?ZgfX֔-XspR-'@*% %( "(999919990@Tp)9999 JJJ X ]\^^ Z!joooh o n!t t t || |!  !  !(]].#"!"$'32654&/.54$!2{hYuӎ⏏ |~[ {78LP@881/20K TKT[X@878Y@ ]!!!! `N3@   91290@p]!3265!! yy6= '@'%91/290KSXY"K TKT[X@878Y@,  GGHHEJWX]]! !! 5N+= x@J 6  6 6 6   %     91/<2290KSXY"K TK T[K T[K T[X  @878Y@  % :?:?3 0 0 @ @ @ ^^a          '('(%* /66220002 4 6 ?IFHE J ]]ZZUURRRZ U ] ooonhheh k n i o wwx v x   K]]! ! !! !=qsnDD==+o' @E    %     91/<290KSXY"K TKT[KT[X  @878Y@X  /& <3 _P      ++%$%+ :55: P ejo  ]] ! ! ! !omGF@(%:: 91/290KSXY"K TK T[KT[X  @878Y@, %%0@P` %*5:0 O o ]]! !!TTu\q w@% 991/0KSXY"K TK T[X  @878Y@ %)69? FHO V_ o ]!!!5!s8!7@210!!!!mB/99103m@210!5!!5!m`@ 91290##fg--/10!5۾^fN10K TKT[X@878YK TX@878Y] #yfxX{ %@*   # # = ;&229991/9990@L/'= =!?'M M!] ]!n n!~ ~!p' ! ! ! !20C@SPc`]]"326=%!5#"&54$!354&#">3 pq[QeiH"ӆsUst/ LJDMm)f]ˢŸUO..^ 8@ B@ 221/0O`]%2654&#">32#"&'!!syyss{{{Ju uJf稠b]]bX5{7@ B ;210_].#"3267# !25IOT@TWV/X=202177\8@ @B ;221/0O`]!!5#"322654&#"hJu tsyysryyXc\II]ɨX {C@!    D ;9190/?]!3267# ! 4&#" q}K"=w`h3f~~CD015:“f}un'`@    E <<991/22990K TKT[X@878Y@ ]#"!!!#35463L<27DN`N\Fy(K@& #& @ B;)221/990O*`*]%#"54325!!"&'3265"32654&Ju uJhic^[o|xsp||b\CA\c !655@   G 21/<9990`]!54&'.#"!!>32 H.pfQnVon#'b])@ <21/0@ P ` p ]!!!!ff`F =@    <2991990@ P`p]!+53265!!fͱ>fLf`\y @   291/<90@`;IIZ]X_ogvv{:DGJV]g`ewpv|]]!! !!fNNK- 1/0@ P`p]!!f{%t@)   #  H H &<991/<<<29990KTX&&&@878Y@'0'P'p'''']>32!>54&#"!4&#"!!>32DpFNfo@RgphBgthmVH wkHk`_`p{5@   G 21/<9990`]!54&'.#"!!>32 H.pfQnVon#'`b]X'{ -@  BLB;107?G]"32654& ! w}}wu||u!EG{88V^{;@B @ 2210O`]%!!>32#"&"32654&fJu us{{ssyy b]]7\Vy ;@  @B;2210O`]"32654&#"325!!ryyrsyyyJu uJhw+c\IG\c{C@     21/990KTX@878Y.#"!!>32/]/fE}*(/`nejb{'@@  6  6% %( SRP"M(9999190KSX99Y" ]@^ #  ,. . . . . ) 9; ; ; : : K J J J H w w  %  7 ?)_) ]].#"!"&'32654&/.54632s_fcKa?o}ktijIm?c=0035+. ###44:90/ x@    T<<991/<2990KTKT[KT[KT[X@878Y@??PPP`` ]]!!;!"&5#33q>\Ա%N7>`;@  G 291/29990`]!3265!!5#"&hG.pfQmp[.w#&)b]`@'%91/290KSXY"K TKT[X@878Y@| 0@Vf  &$+)64990FFII`x$]]! !!fgGw`H` @J 4  4 4 4   %     91/<2290KSXY"K TK T[K T[X  @878Y@ 550 G @ @ _ l        &$+)*+ $ % /554;::78 ?GIFHGH YVV[ T Y _f`b```d ` upspppt p      []]!!!! !H\+\yy` ` @F    %    91/<290KSXY"K TKT[KT[KT[X  @878Y@  / 3< CL R\ bl sz         2     $++$ 4;;4 0 DKKD o       :]] !! ! !l{{l=#LbF`A@C %    9129990KSX9Y"K TKT[KT[X@878Y@ @Pet $$$5586699EEJJEEge    9]]! !+5326?f-f)Gp[S `6:K\F` @% 2991/0KSXY"K TK T[X  @878Y@DYVifyv &)/ 9? J_ ]]!!!5!uNN`f$^@1 %   ! % $  %<<29999999199999990#"&554&##5326554633#"3l==lEUZnoYUmutWW10#$`@2%   #%# %<2<999999919999999032655467&&554&##53233#"##FUZooZUFl==lmWW͖tuR#@  1990#"'&'&'&#"56632326j`k^Xbk`k^VRPE:=MSPE:=K 'k'$u 'm!{@S!! ! !%! !  UU "9999999991/<9990KSXY"K TX"""@878Y@/!/!:!o! !! # ///  /// "+ #EKUZ` ` ` ooo``ooo`fi `#tuyz{t    D]] !!!.54632%32654&#"!}^_}vtwM66MN56MJH"K+uu/L{6MM66MMRfo\'&sk'(um'15uffk'2Nuk'8'uXf'DXf'DCXf'DX1'DX9'DX'DXo5{'FX f'HX f'HCX f'HX 1'Hf'wf'Cwf'w<1'w9'QX'f'RX'f'RCX'f'RX'1'RX'9'Rf'Xf'XCf'X1'X5; *@  WV W <<1220!!!!!5!VJ#!/dL @  XYX10"32654&'2#"&546HdcIHdeGBz0/11-0|D\dHHbcGHd3/0xDCy-03#W@.    !$   B$<<222991<9990&&##667###$4%3NMMNJAY9S: GZ,lm*902i2/  (.##}@@!   <<1/222990&&#"!!!!3#5356!2FMvqu\'&} F=3?k@8@1:4 %+1@ =!+%74:!=\.!\=[.7[(@9999991999990&&#"#"&'532654'&'&&5467&&546326654&uc9KL ҟquMKUfs9AN$ˠoqKATDC{AF''1/CO Y}u0)qI)+2(FJWh33oKL2CbBO4Cj'` ]104676632#"&'&&'535II245633JI326J235624IJ336633;d &@ ^^9120!###&&54$\fN۲h0j@4.(" !++/"!(%  a%.(a_ . 199991/990@ /2O2p22]4$! #"&'532654&/.5467.#"! 1]EtkAJ8s6HX7bFXT`[efZG NJ%94%@uH9/D7'1Zt2UYnm 4Lb@8-*+'0!5 2+A'*,$0-+$!1g3f$cX;eX3cGM299991/29990"32676654&'&&#32654&'2#'&&###2#"$'&5476$yWWWWWVy{WWWWWXϲ##NOM+i`)Gok&: 1mmllmmmmllmm3WWWzyWVVUWWyzWXV5442wyVpP:NAD7nmmmmnnmmmmn1IH@(  2&>f,X c8e XhDJ21/990&&#"3267#"&54632'"32676654&'&&'2#"$'&5476$+9o9q~r@s.A>EyWWWWWVy{WWWWWXymmllmmmmllmmf%#rs~$#WWWzyWVVUWWyzWXVnmmmmnnmmmmn'R v@>  %    ji i ji91<<2<<9990KSXY"73#######5ww㪉LqKBMmf710K TKT[X@878Y]!#f;;1\@1<20K TK T[KT[KT[X@878YK TX@878Y3#%3#1 =@!      <291<2<2.990!3!!!'7#5!7!^P1}@7%     /<291/<90KSXY"K TK T[X@878Y@&W ] !!!!!!!!!{y}sfb^- +@> +,   )*&& &,+,* # )-#7-+,99999999199999990@p- -*'&!/-976!9)?-GYVT!Y(Y)jege!j%j($'))68)KFE I)Z^SVV T!V"[(j ejlaf c!k(x ]]3254&/.#".5!27!"&''\4SM3RJJgfqMLhfqs>;Du1:9@q.dkKMscdOOq /B@#  $'!-!0 $k*k099991<999032654&#"&&#"326#"&546326632#"&+vIZqgLHw+tKZqfMGzDaƯZcG_ů[1CDeOMeeCCdOMeia~q~n .@   l l <2<21/<<0!!#!5!!!  bb '@    <2291/90%!55PN '@   <<291/907!!55%y@B  %     nm n m<2<2999991/2<<<290KSXY"]@, $+6:FI   0@ ]]!!!5!5'!5!! !!!!N9:1k$! %j1`BV3VBT` :@!  !   !2912<990!3265!3267#"&'#"&'idfgdh!'!5]-Yq#/YJhT utqqtG8 KSOO/0;R)8@'! '!* $$*99919906654&#"#"&54632#"&54324&#"32;'#S0@eID`IFa~q9WzC2EqG Ur|tx)w O@    91990@ &#)  ) ( ) 8 ]]!! !!5 Bl_{Nw@pp120!!!!)JD/@    991/<22990#!#!#"663J'7: Ddd>Dǜ3,B*# @- *(&  qr q-9919026732#"&54&#"#"&54632jciRA@Ae '&>ÄkTF32-ӅhB:Yr 7^YUWO\K=4>3:rWT@LHt8;##uu 9A  @  uu 99102#"&546!!"32654&B7T[[TS[[޾ܾM~tt||tt~7F   @  xwx w 9991/<2990!!654&#"!!&5! #~˲˄~#~zx89xz#VˤUy9yǤX{>@B8>66'&# 6-*>;0*? - 6 & 7 3;?<9999912<<<9990@N>>?@MMO@^^_@nno@@@2=0>B=@>R=P>b=`>=>=>=>=>]]4&#""326=>32>3 !3267#"$'#"&54$!354&#"w`gpq[Qe^waGMz =q}~Heߋ"ӆsUf}unLJDMm)JMOMOf~~CD01kdkdŨŸUO..N) +@> )+ *& &&++, #* #)B#LB;,999999991/9999990@@:5 ;75!8)?-IF KGD!H)[VT!U(ikfe!e(5:)EJ)U^(i em( ]].#"32654&'.5!27!"&''XK/w}HO0u|;CDG"jKmFElMpD)A+CN{8,,eP~--^!M@*   "  "<2999919990!3267#"$54677665%!!iAm@84`VQew\N^@D*ii1Q~d:3\/FPDB*(ǾcX:=L-d @ <2991/0!!!33h=^qd@ 10!#!LZ n@*      %  @  9190KSXY"3##'%`w͑%hN7V7#w@I #"!   %   !$2299990KSX92Y"&&#"!!#"&'53267#5!766327.T*Zd!)AD.U)Ycu!3*Bs_sM#;C@!.9* 1 "9*1<-<<219999990#"'&'&'&#"56632326#"'&'&'&#"56632326j`k^Xbian ^Vgj`k ^Xbk`k^V#PE:=MSNE;=KPE:=LTPE:>K  @ /91/90!!!#-3mV?j' 5  @  y y<2991<299055%$'qsq' 5  @ yy <<991<29905-5%%%!$'^ #@   1/<<220!!!!!!hhh}}} 'k'$u 'm'$uffm'2NufP@"     -+ 299991/220@ !!?!O!_!]# !3!!!!!!"# !2i iZhsf / F& 0ihX^{'3t@2"  .(%4"1 1 +B;499912<9990@/5?5O5O5_5o5o55F"]]4&#"!3267#"&'# !2>3 %"32654&w`hA q}~~HRՂG"QRLJBcw}}wu||f}unwf~~CD01QWTT88RVWQ:/10!!/10!!X +@    1<20!3!3!ddXb`Xo + @  1<20!#!#!TeTe`^X@ 10!3'dX`X9@ 10!#Td`V 0@  z{z <<10!!!!!!33Xˁ#uv@A%91990KSXY"  9%-F1'\k'<uh+@55%10KSXY"#3 J=#/@ ! ! $A !* @&00   '}|~-} |022999999122999990'7&&5467'766327'#"&72654&#"ϙљ0l=6l9ϘϚ.j?:l[\[~ Ϛ1k??l.͚Ϛ7n6?i/ϙ\\\]~'y291905%'q'y<91905%%$'+Bu@&       ET<2<<991/<2<2990KTKT[KT[KT[X@878Y@]!!#"!!!!#35463iJK:k$7DN``N'Bl@!      ET<2991/<22990KTKT[KT[KT[X@878Y@]!!!"!!!#3546{L<)7DN`N3;?@!   W VW <<<<2912<220!!!!!!!5!!5!VJ###!<}910!!h}L@ 10!#Te`F + @  1<20!#!#TeTe`^B V #/3?K|@C3 2211 003%@ *$F4 :02$L3IC1!  C=!'= I7' -L9912<<2220KSXY""32654&'2#"&546"32654&'2#"&546#32#"&546"32654& HNNHGLLGֹHNNHHMNGպֺ׺GNMHHLMh{rs{{sr{ؽ۽8|rs}}sr|ٽڽ ؽ۽٨{rs{{sr{ 'k'$uk'(u 'k'$uk'(uk'(uk',duk',du)k',du=k',duffk'2Nuffk'2Nuffk'2Nuk'8'uk'8'uk'8'u` 1/0@ P`p]!!f`yf6@ 91290K TKT[X@878Y3#'#Dzf\9@  @  999919999990K TKT[X@878Y@T              ( ]]'&'&#"#4632326=3#"&7/$&g]$I)=%$(g]$CT%>;+@9X;E10K TKT[X@878YKTX@878Y!!vPF i@  1<0K TKT[KT[X@878YK TX@878Y@]332673#"& cSSc FFJJFw;1*10K TX@878Y!!w1 C @ : 10K TKT[X@878Y32654&#"4632#"&}M67LM67Lvvvv7LM66MM6vvvo5@   991/0K TX@878Y!#"&/32654&'Z:7{0f42S!:A+->j/_[ .(R<fE@991<20K TKT[X@878Y3#3#-fxVo@   991/0!33267#"&546ō2&;1'M(7^)s{6CI'1 \V5myf6@ 91<90K TKT[X@878Y 373Dzx `@2 %    <<.9991/90KSXY"!7!!'%s۔#` j ~@-   %    T <2.991/90KSXY" ]@ut@ P ` ` tp p  ]]!7!'7ho}o XV-k'6ujbf'Vb\qk'=u\Ff']T@ <210##  !L @   -. <291/<20@X!P!`!////////OOOOOOOO________(]]3#32654&#! )#3PULxhgghyk#ateetamX'(@Y&'('%$%(('"#" ! "! 5((5(%('&%"! ## #)'& !#(%" BB;)999919990KSX92Y"KTKT[X)@))878Y@6f!/*76"?*O*oooooooooo]].#"32654&! 4!2''%'!%7l4uru| uj-.N$%3`ox#y-\8 watr`k'<uFf'\ @  - 2299991/0K TK T[KT[KT[KT[X@878Y@,0000PPPP]]!!3 !32654&#=1pzzp]mcenV^;@B @ 2210O`]%!!>32#"&"32654&fJu us{{ssyyb]]7 10!!) /@   <291<290 '7NNNN3NPPN{ 7  @   129035733!9 41Zm]@%   "@99919990KSX9Y"!!56654&#"56632r_9=4I;>TWKGeD 5P(2>-/oHyVZ(W@ #  ""#@#)& )99919990#"&'532654&##532654&#"56632P\fQDB<_hkrJTbZNP4{FAWZ`nQ$%@;@=/3--piE`d @     % @    229991/222990KSXY"333##5!5#335733!9y+ I 41Zd'@   %!   $$& !&"@ #%# (9991/299990KSX9Y"%!!56654&#"56632#335733!qt];>3J<>UXJIc 5N'2?-0oH|T I 41Zh 6:@ : 9988 77: %.1* #! #!$-*$!#917@$$8'" :!'"4  '4-";229999991/2<299990KSXY"33##5!53#"&'532654&##532654&#"56632#3'y%]fQDB<_hjsJTb[OO5zGAVZ-D7#nQ$%@;@=/3--piE`q fk'* 1u\FF'J=k', duo-'6job{'Vbf\k'&fuXuf'Ff\k'&fuXLf'F\$K%@"      @"B;%<<1/<20O&]!5!5!3#!5#"322654&#"FhJu tsyysryyrr+c\II]ɨo10!!ot910!!h}1r@;.*(1.!2*("%!) 2 +) )% 2229999999999122<2990%# '#73&&5467#736!2&&#"!!!!3267_pKXbXMep_Qc-VY2~cTR78 87NO{v$$ zzOO;@ 1<203#%3#mN810K TX@878Y@ //]!#3\#@  @! $  $999991<29990K TX$$$@878Y@\             ##+]]'&'&#"#465463232653#"&8- (kW%J';'%'kW&F#<2j'<9j810K TX@878Y@ //]#yE@ 91<90K TX@878Y@/// ]!#'#f4߲DzyK@ 91290K TX@878Y@//// ]373f߲DzP S@  120K TX@878Y@///// / ]332673#"&`LL`=<<=w*10K TX@878Y!!w  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~>: ~1BSax~ & 0 : !""""+"H"e%  0AR^x}  0 9 !""""+"H"`%^ChVjq_8 (Bbcdefghjikmlnoqprsutvwxzy{}|~f+B{s/) mRo b\}j331 fwwf3ff)u 1 =+'\^fXX\mX{'\RVX\j7dH)7\1 1 fwffXfXfXfXfXfXXmXmXmXmX<XXXXX5}''m-Z;)L'3u'7bXNVL++1 1 f VfXBB  7VhJLL+'3  B B1 w1 ww)fffwV#j\\!X7{mZHdHdHhf\jfXfX\Ro mw   !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~    sfthyphenperiodcenteredEuroc6459c6460c6461c6462c6463c6466c6467c6468%%%%Nq] 6 N{U?u<_O K G E a > *2e:\u#p|$Z<~*Z@!I"dqjw   - : G T a n { !!]!"""##$.$%!%K%%&H''n''((()6){))*<*++p,5,-8-a-}-.B..//\/////00001161U1t112 2292233v344-4L4~5*575D5Q5^5k5x5555555556 67667H7k778'8W889-9:9G9T9a9::::;O;;;<>>>>>>??????@4@T@AA@AuAAB 79k:;Y<$&$&$&$&$7a$8$9u$:$<<$Y$\$h$D$D$$<$r$r$$$$<$%9%:%<%%&/&6&&K&K&&&&'&'<6B<;:6B:S9@p8}76B6-6B543:20 /-,+-+1*))#('&'d&% %2$ $}#:#" "!!    @  @:%d%%A%:  }    :-:-  @d++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++, %Id@QX Y!-,%Id@QX Y!-,  P y PXY%%# P y PXY%-,KPX EDY!-,%E`D-,KSX%%EDY!!-,ED-ff@ D/10!%!!fsr) @-   :HF 991/0KSX9Y"K TK T[KT[KT[X @ 878Y3 #3#Z2q1qe@IF1<20#!#o$++/N@4L L    91/<<<<<<<2220!3!!!!#!#!7!!7!!!h$igP'RT$hggh%JT'Pf8T%aabbND /`@9 $ KOM+* K#M-*',+ -#$*  '09991/<2290%6654&'&&'#&&'7&&54$773Xfs_l""VOT;d;tS"\jX/d,ZgT\ZdFO.2U -/*>D$~ #/3O@,W*WRW!W*R1$Q0V40-2 '  ' - 49912<0"&54324&#"3264&#"326"&5432#ሟ躈HNMp~QKk\LMoQKk纈.ñ ij­\dkjí]dò   ` 3@T// ///:0 /& Z&%Y)Z"VQ 0/&, % 3,3  4999991/9990KSX999Y"]@X *: It      % & %&+)*%*&6 9%9&6/KIkxu/]] 326#'#"$547.5467>32.#">7Vy}^n-gjj.+ExNQ#KF|&EKjXgA[lSUڴt._6Dz2RZ$$/1d,VXOzo@ IF10#o+j @ \   99910#&5jLL\ZəE)G#N !@\ 999104'3 KL\ZKS7=JN@,  ] ] V    <2<2991<22990%#'%%73%g:r:g:PrPbybcy #@ `  <<1/<<0!!#!5!-Ө-Ӫ--@:bH10KSX9Y"K TK T[X@878Y@%%55FFVV ]KTX878Y73#!ぐ@\?@ c9910KTX@878Y@ __oo]!!}w{Q@:H91/0KSXY"K TK T[KT[X@878Y73#1jBj.@:F910KSXY"3#mf#D@d dV Q$! $10K TK T[K T[X$@$$878Y2#"547>"3267654&e[]g\\L7\murP7[ku}QOuХQOѥd T@* :ddFd   99991/20KSX2Y"7!7%3!!J#sI }HHժ@,:eY dVd 999991/990KSX9Y"K TX@878Y@( ,,eyuxy '4Ucv]] !!7>54&#"7>32{#7!qn_%|`rרBu]azDA12Щr+@+ d!KXd+KX(dVQ!g,!  "+ %,99190K TX,@,,878YK TKT[KT[X,,,@878Y@aff*a++]]>32#"&'732654&+732654&#"Dnd跡~vslR^c%^d`l &$}xL:9%%43ˢhq^g))% @;   : d F   991/<290KSXY"K TK T[KT[KT[KT[KT[X@878Y@$:  +*) :89 6 f yu ]]33##!7 !q!CD^'7f3`) @6  :dKOddFQ!   !999190KSX2Y"K TKT[KT[X!!!@878Y!>32#"&'732654&#"H._1{qPZg%\^OZը޾\@E --갂%% *@# d dk K hj(VQ+ "+9190K TX+@++878Y@`z{J J JJ*Z Z ZZ*j j jj*z z zyz* * * * * * * *-]]4&#"326.#">32#"5476$32pӁr":U>IpkfFimbg%N)ﰄ:&(UWӲ_AF@:dF991/0KSXY"K TX@878YK TKT[KT[X@878Y@ ')Zhxy]!#!'V+D /c@#$d dd*VQg0$' -'!0991990K TK T[KT[X000@878Y4&#"3264&#"326#"&5467.54$32˛gtsqxگin-qrD_ttdz+{Ӱ"$lŘZ*@("K"d kjh(dVQ+% +9190K TX+@++878YK TKT[KT[KT[KT[KT[X+++@878Y@FFFdddd ]?32#"&5467>32#"&32654&#"Z%:U>JpjgFhobhܲMDŽp҂r!&(UYӲ^BFvj #k@(:HlH91/0KSXY"K TKT[X@878Y3#3#91l2### @.   :HbHl   9910KSX9Y"]]K TK T[KT[X @ 878Y@%%55FFVV ]73#3##၏1@^M@*````:nm29190KSXY" 5Ѧ`@ ``<210!!!!^O@+````:nm<9190KSXY"55//m!@F  : K!NZHV  !"9991/9990KSX9Y"K TX"@""878Y@ t t ttt]%#7>32#7>?>54&#"11fjkyhT8NojTDm`Ph98gbTB^\{^\YFj9LVGBw4@Z@3 5"!4;or!o%5o ro%.A>  !"4811(A9912990%#"&543273654!"!267# 476$3 "32654&EW)\']RǥUѼhAiāresJLƘIPG0'}rW]rfiGr~wߜp~fy @@    :Z F   91/<90KSXY"]@$ *HIHGWhgw]]3#!# !>`]$+7 l@<:ZZFZs    !9991/90KSXY"!2!!!2654&#!2654&#Zг||TjEƃX-z՜ݨvlf>|aYVS@ ZetZVQ   99910K TKT[X  @878Yo!].#"32$7# 476$32)`}^32.#"326^NƟku)Z_Q]joIO8WUGG^cylۘ-7 }@D     :ZsF    9991/<20KSXY"3!3#!#Zwwˋ#d+97%2@:F91/0KSXY"3#Z+f! K@&  : ZvF    9991990KSX9Y"3##73267V5P!?%󪚾7 @G     :F    991/<290KSXY"]@j$%#675HXkixx  &&%'787=<IGFYX\[hfbbzxx~~$]]3! ##Z{ u7)79@:ZF9991/0KSXY"3!!X!^Ө7 @Q     : F    9991/<290KSXY"]@  )70 JC x  &)(') 2547 5 DGJCI F XYWhfw *]]!!## #Z/B7ž+7 @@  :F   999991/<2990KSXY"]@P' ?6H[j ")&5:=7IWY_ hkw ]]!3!#Ze+R#P@ ZZVQ$  $100%]KTK T[KT[KT[X$$$@878Y"32$7>54& 476$3 ^54& yl}9<^>OO]]``]!!#!y ժ+w@<     : ZQ F   9991299990KSX99Y"@!     4 4 4 4 4 444]K TX@878Y]332673!"$5467=˰ .˴; u=O\#W2@&:F91/290KSXY"]@0*(8GGXWgieyywh]]!33+)  @I      : F    91/<2290KSXY" ]@ ()& ;;3 KF     ')$)+*(- & * 6756 6 8 KGCH G VV gihg g yx 1]]333##H3J-hE  +# M@I    : F    91/<290KSXY"]@ )+) :8; IJI YZhhh y   ''()())& ( 8::665 5 8 FFGGD F H YYXT T X ffehfggfe e f zzzyu u    I]]3 3 # #s7u#h@7:F 9991/290KSXY"]@:(9IIZY{y$5Vj ]]3 3#[ʉf9 P@:ZFZ   91/0KSXY"x]@ w]]!!!7! { =w՚oR\Q@!:oo\9910KSXY"]]!#3!XB0@:F910KSXY"#XmD@ :oo\9910KSXY"!73#7X+ޏ@ F91290##HHu-o10!5fk910K TKT[X@878YK TK T[KT[KT[KT[X@878Y@ DDUgv]#JњfvT`{ +@d  #"$!)*(+:+(!!o (LK{LzQ +"! % %#% ",99991/99990KSX99999Y"0]@.<<O[[jjzz 2C@TPd`tp ]]KTX,,,@878Y@ !"#$+ ]#7#"&54$)7>54&#"7>32#"3267L}"Q9 Zd h] Ѹob$dc1Yc..''!Y~yXd״J%@M  $#$#"$# !#$#$#%$$#:! L LQz$\""#% !#$$%#$&999991/990KSX9999Y"`']KTX&&&@878Y']4&#"3267>>32#"&'#3uU7:CsW89FDpwnHgm2!/SOSmQORm\iƢvNTdc^J{K@KN LKNLzQ  " 99910KTX  @878Y.#"3267#"&547>32J%BPX6V^L]#PY}{TN500>9W}..!!Ϯ%uPR#^%@L   : L LQ z\%"&999991/990KSX9999Y"`']KTX&&&@878Y%#"&547>323#3267>54&#"XJ{vpJfl-xѹtV8;DsW5:EadǦ-wOShabSQTkRMS^{ &@$o KNL |L!zQ' $ #$#"'999910p(]KTX'''@878Y@,oooolo o o o$o%o&]]>54&#"!3267#"&547>32{65x\#cm}yNy "z&,64((ͯ-vJP.i@<      : o}\~      991/22990KSX9Y"K TK T[K T[K T[KT[KT[KT[KT[X@878Y#"!!##737>3d[/Ѿ&Oic/яNƠBV{.@X"#!-.,   :#,K L#Lz,L~ )"/99991/99990KSX99999Y"`0]KTX///@878Y!"&'7326?#"&547>3274&#"32ſ7aH"DV$MvvkGgq) \vI/LUy`+,*TX\ś)sLRi`e:4V|=H@M   :  Lz\   '%$99991/<99990KSX999Y"0]`]KTX@878Y]#>54&#"#3>32u j_ y/wFx \/HT^Ȧ^m$RH/q@*:c\~(%$9991/0KSXY"@ ]KTX@878Y3#3#w-'۸V7@A : c }~\   (...99919990KSX9Y"K TK T[X@878Y@////PPPP```` ]3+732673#)%0-vE/l[3-`GCHZ(H @H))     :~\   % $ 9991/<90KSXY"]@>6STThi ::@ XXWWiljyyy]]KTX  @878Y33 ##ww@"ju#H/U@:\%$91/0KSXY"@]`]KTX@878Y3#wѸH?{+@s    : # L& z~  #) # '#')$,99999991/<<<2990KSX9999Y"0-]@-`-p-]KTX,,,@878Y#>54&#"#>54&#"#3>32>32/dZ{dX{۸#KszT{\+>Zdɡ%?[eɡ`bgrv|!OH{@M   :  Lz~   '*$99991/<99990KSX999Y"0]`]KTX@878Y]#>54&#"#3>32u j_!{ٸ%Mw \/HT^ũ`aj$R^{ ?@LL zQ! "!10KTX!!!@878Yp"]"&5467>323254&#"O?cN?by~h@/3zVW˘CacJV{%@O  $#$$#"$# !#$#$#%$$#:! LLzQ"$~&"&% !#$$%#&999991990KSX9999Y"K TK T[KT[KT[KT[X&@&&878Y`'p'']4&#"3267>>32#"&'#3xR:9DvW59FJ{voJgw(s-TPOnRNRiad˦wOScb ^Z{"@L  :LL zQ~#%"#999991990KSX9999Y"`$]$]KTX###@878Y%#"&547>3273#3254&#"VJzwoHg|$ չ:|uwW7:Cadɢ.xNTbcRMQNRH{@4       : Lz ~   $991/990KSX99Y"O]@ T ][__]]KTX@878Y@@@@@@@@@P ].#"#3>32H)$q۸#Is:߻`ah{(@?      :  KOLKOL&zQ)  #)9991990KSX99Y"K TKT[KT[X)@))878Y@( //)X X X X XX ))99JJYY]].#"#"&'732654/.54$32#IV;{Yv$eZ? [?((cUc53pa"$46tY`;%y^b@>  : o~ }  991/<2990KSX9Y"]@ gyy]K TK T[KT[K T[X@878Y!;#"&5467#733bwMUw>=`". @:7!`>u`@L    :  LQ ~  '99991/299990KSX999Y"K TK T[K T[KT[KT[KT[X@878Y`]332673#7#"&546 h_!{ٸ%Ny c1FU]ȩlbkZ`@'++++:~91/290KSXY"]@,x%75IIgftsu]]K TK T[K T[KT[KT[KT[X@878Y0]33#ä`H` x@J++     + + : ~    91/<2290KSXY" ]@5 ED ST gdvt     ,&( ) $ 6=768 3 6 LHJJII H F [XZYZX feeddf` f v{zyv v   =]]KTK T[KT[X @ 878Y@.    ]]333##/>7T`{{`` @H++ + +  +  +++: ~   91/<290KSXY"]@*)&'%&)) ) + :865579 ; JIGEEGI J VVUVXW ffffefg f f vuvuuw v   J   &+)& 6886 FIIF Xihh wx   ]]K TKT[KT[KT[X @ 878Y ] ## 3;d'w`DNkV`2@F +  ++ +  + + :  } ~ 99129990KSX9Y"]@f& 7 F v w v &$$)9 9 98I I H Hwwwx x v v yy  *]]K TK T[KT[KT[KT[X@878YKTX@878Y+7326?33}jMlC9úhkTzk7ZX` @++:o~o   91/0KSXY"x]@xw]]K TKT[KT[KT[X @ 878Y!!!7!j!s!Kk`ۓ%4@f -.-..-    : 5'- )'.)o'oo'\5)(54.  $-5999199999990KSX9999Y"]@K      % % % %%5 5 5 55H H H HHK K!K"K#K$K%Z Z!Z"Z#Z$Z%%]#";#"&546?47654&+7326?>7>3M^3 yoIQ/UaNG- bq=> /G7.OgKB&5?9m{ D% 2-UKm{'"10#4@f -.-..-    :-5)  )'.'o)o)o\54.  5)('*(599199999990KSX9999Y"732677667&&546776654&##73233#"#N^3!xoIP/UaNJ- bq>>~5F5/OfL@&"2@9l{D' 1-VJn~&"1#@ `` 1990#"'&'&'&#"56632326ian ^Xbian ^V1OD;>MSOE<>LN'$um !@P !  !   : Z S " !"999991/<9990KSXY" ]@8 & 9MW  ( 98HGHW[YXX!gww ]]4&#"326.54632#!# !Y?@WW@?Y69rrIE>`]$Z?YWA?XX%qErrOz$Vu'&7 k'(u7^'1uRN'2TuwN'8uTf'DdT`f'DCdT`f'DdT`'DdT7'DdT`'Dd^uJ{'Fw^f'Ho^f'HCo^f'Ho^'HoH+f'Hf'CHf'H'H7'Q^f'Rs^f'RCs^f'Rs^'Rs^7'Rsuf'Xjuf'XCjuf'Xju'XjV; Y@.:L F   99991<20KSXY"3!!#!7!5Poٰp\]u=  @ V ,-,10"32654&'2#"&546PnnPPnoO@v+..ooPOmmOOp1.-rB!@[     ! : K NKNLL LLzQ"!    "912<0KSX9999Y"&&'667#&&5%3z+I>#/|YBS#AM54&#"#=& Ů*01wRL?AG|/N3aCԭqd۹q޺=%a(H%'^Pr[-NC+RoC~%NZ/8L`@6EBC?2H09JC 9 $HE301BKL?gwyVpMI`3D/IC@&=>:A$104G$ 73D=00*/D0/J21/02#"$'&5476$"32676654&'&&&&#"3267#"&54632mmllmmmmllmm^^``^^⃄^]]^\^BB@zBCFInmmmmnnmmmmng^^^傁^^__^]⃅]^^! "'F >@!   F 4 4 54591<<2<<903#######5Jq7rqr/B^^-f10K TKT[X@878YK TKT[K T[KT[KT[X@878YK TX878Y@VV]3#fqfyF@99991<20K TK T[KT[KT[KT[X@878YK TK T[X@878Y@"@@@@QQQQOOOOPPPP]]3#%3#)''>@" `  `  <291<2<<990!!!!!'7!5!7!}/H{};fըfӪ@L  : ZZZZF Zs  91/<0KSXY"]@-   GIIIGWXgfwuwwy] !%!!!!!!#ʚ!X!8i!>I7Fy 7@B78(&# )562'#Z2Z#V2Q8',(68),  &,5 (,7,  8.99999991999990@$7754709 &5&5' )765]]K TKT[KT[KT[X888@878Y@>IJ J7YZ d j gu { w) D6C7WS6S7`6`7w q6q76767]].#" 32$7>54&.5476$327#"&''-b\>A u3c[>? ''zm}9TZ((vp˽LZ<=mr-W,DCpz8]PUPMMcIbTRc /<@- !$'!!0 $*099991999032654&#"&&#"326#"&546326632#"&1TevYR1UfvYRF^_HDa^/XZie7XXjeߦ~᧯w .@`  `   <2<21/<<0!!#!5!!!-Ө-}} T@.````:m`  <2291/90KSXY" 5!!@po V@/````:m`  <<291/90KSXY"55!5AǪ\b@]  :    F    9991/2<2<290KSXY"]@85ESww  &&&::GFWUT``v vuu]]!#!7!7'!7!33!!!beXX` 6 ̾Rq 9{3{JD{3V`&@X   &#$"% : % L"Q ~'&'  %%'99912<990KSX999Y"3326733267#"&5#"&'-um  # ,P&BK8i]lV G4]d  TJPNVFh-)6@'! '!* $$*99919906654&#"#"&54632#"&54324&#"32IH7$$0e՘ݢe WOmVPmmWKt,>bFأ[t}t{w; [@    91990@.UPQVPb`g`e`tppupx V pp]]!! !!5 7AJI3!wq@77120!#!# }/#i@1 " $ #" #8#$9999991/<229990@ ]]#3267#"&5467!##"#>3!i/7.%7vy"Pµ)6< yJ\:1fd.xo@E}/%&@  & 9:9&1026732#"&'&&#"#"&546327j Pd@7*8  kOeD=!0 l9TA6?&#Hn!bSA8?SR +/r@@ " .,#" ,&V0 .- )/,0 #";);0999999199990"326?#7#"&5463356654&#"76632!!RLAsX:`kж]dFIODPPKS;F{=@pABzdFA""|x7{R 4@  V;;9999104&#"3262#"&546766!!catc\z˖&%>wPns֗mvⴚD?jv`{N@@" V  =<= < 9991/<20%!5654#"!5!&5! Dz?1/aL"a*>w؍T{ >I@T5>3*# I?>?o3o$K#NF L' K>{;L| z-'Q3JI@C54?  >3 C8*? #$ ?C0"J99999999912<<<99990@2; ; K K \ \ k k { { 3=0>D=@>T=P>c=`>t=p>=> ]]KTXJJJ@878Y@,oooolo ooooo1234?@AB"#]]>54&#">32>32!3267#"&'#"&54$)7>54&#"#"3267|6h]-V x["dm8f܄9 Zdfob$"z''Z\Z\.i<&,64((sqvn1Yc..yXd״ '1@B'2")( ,%" ,&",L"Lz"Q2/&2 )( / /% /' "29999999199999990@DJ H EGV%U&X)kkg%b&f's&w'p3I I FFU VWWY(fh%uvx%y&y'y)]]KTX222@878Y ()]].#".5467>327#"&'' 324&%]:O?bW:VO>aW9V'[;#"/)1~KzV43V7}D|V54T3%#G'&Lf!@I  : !HK!NZQF" ! $"99919990KSX9Y"KTX"""@878Y@#dddddddz y zzzz]73#"&546?>?33267d21gkkxgU9NnkTCm_Qh98gbTD^Z{^\YFj9LVGB k@/ :HF   99991/0KSX9Y"]]!#3#73yZ1p1eH^@ `10!#!^=} *@    91903##'%\sB}}`s-Pb;V#@P !    :  M ooMo!\$  $ $9912299990KSX99Y"&&#"!!#"&'73267!7!6632&P,`r<>9J{74a/am"?%ʩ.czɏ!!J;?@.9*-" *`19`"` `<-<<219999990#"'&'&'&#"56632326#"'&'&'&#"56632326ian ^Xbian ^Vgian ^Xbian ^VoNE;=LTNE;=KڲOE;=LSNE;=K`@91/90!3!^DC?q# 1@  l   991<299077'%}'%}#RRo# 1@  l   991<2990%77779'c''`% ^R^ ^R v@:    :H     999991/<<220KSXY"73#%3#%3#111k'$u^'$uR^'2Tu^ #@9    :ZZ F Zs  #  $91/220KSXY"KTK T[KT[KT[X$$$@878Y#";!!!!!"&'&5476$3xW1!X!9h!N{^+LInӵ+F#Ftba^{(2A@9 /) )o K N6L|32>32'>54&#"3254&#"x[%cm/e燿LBc6c {7h@/3264')ncihXulpq.iT"z嘗@acJy@ o9910!!yy@ o9910!!y + @1     :b F   991<20KSX99Y"@1     *** * ::: : III I YYY Y ]#73#73#၏w#၏??3 @.     : bF    91<20KSX99Y" ]@1    %%% % 666 6 FFF F UUU U ]3#%3##႐#၏լ@@ T@:bF910KSX9Y"@  **99IIYY ]#73#၏?V@:bF10KSX9Y"]@%%66FFVV ]3#!ゐլ@o )@` > <<103#3#!!oA#u"@91990  9%-V'\^hN'<um_@:QV10KSXY"]@) %86EYViexuu]3#/^R#/@I -'! - -L'L!0 *$0* $ $?@*?099999999919999999907'#"&''7&&5467'766324&#"326{r%$&(r;t=:x=q%%&&s7t@?s9q(&%%s>v:@t8s'%$|ppr#@ l91907%%}#R#@ l9190777'd# ^R!@b  : o }c\~  (%9991/<22<2990KSX9Y"K TK T[K T[KT[KT[KT[X@878Y__]3#3#'#"!!##737>3۹/-hd[/Ѿ&`Oic/яNƠ!@J   : o }\ ~    %9991/<22990KSX9Y"K TK T[K T[KT[KT[KT[X@878Y@ ````` `  ]!#!"!!##737>%ѹd[/Ѿ&{Oic/яNǟ;@I       :LL  F   .9999912<220KSXY"%!#!7!!7!3!!!jRRnkoRRpjn\\HF/@:H910KSXY"3#2F4@:bH10KSX9Y"73##ၐ@% ]@.     : bH    91<20KSX99Y"73#%3##ၐ#၏@@  #/;GKd@83W-WR'W9WBRI<QHVLHE0J*6 ?  $ !0 ? E* ! L9912<<2220"&54324&#"326"&54324&#"3264&#"326"&5432#s织HMMo~PKkn躈HNMp~QKk\LMoQKk纈. kjí]d ij­\dkjí]dò   m'$u7 m'(uk'$u7 N'(u7 k'(u7k',;u7m',;u7"N',;u7Vk',;uRk'2TuRm'2TuRk'2Tuwk'8uwm'8uwk'8uH`O@:~$91/0KSXY"@]KTX@878Y3##۸`Rfd@ 91290K TKT[X@878YK TK T[K T[KT[KT[X@878Y3#'#fP7@   91<<99990K TK T[X@878Y@  ]]K TK T[K T[KT[X@878Y@!    ]K TX878YKTKT[X@878Y'.'"#>322673#"&- '3}[%@/ &1}\&BZ7IR7GT}bd@ o9910K TK T[K T[K T[KT[KT[X@878YKTKT[X@878Y!!V)H@  91<90K TX@878YK TK T[K T[K T[KT[KT[X@878YKTKT[KT[KT[X@878Y332673#"&546rTVUnt HDCIM};D/@:9910KSXY"K TK T[K T[K T[KT[KT[X@878Y@OOPPUU]]K TX@878Y3#d' @   10K TK T[X@878Y@   ]K TK T[KT[KT[KT[X@878Y@I44400044 4 444000444OOKKKKK K O O O KKKKKKO$]K TX878Y#"&546324&#"326џtsst{WABUUBCUsstt@WUBBVVdu#&@    991/90!#"&'732654&'$"+V+'O*32''%'3%.#"3254&DEOKTRLX:(| G`P#+W,ufvdt|xet|k^ȃm_bdCDlhk'<uVf'\^7 q@= :Z ZF  999991/0KSXY"!#3!232654&#DB#3 qm|rRѯhsV%@L  $#$#"$# !#$#$#%$$#:! L LQz"$\&"&% !#$$#&999991990KSX9999Y"K TK T[KT[KT[KT[X&@&&878Y`'p'']4&#"3267>>32#"&'#3xR:9DvW59FJ{voJgw(s}TPOnRNRiad˦wOScb-`10!!ת? @M `  ````` ` ` :   <291<290KSXY" '77w55v8vL57y5yy5 @/ AA:  V     999120KSX2Y"]@  //??OO__]3?33!w摍 c)t'+nJ@/AA: V C9991990KSX9Y"]@ )),,]K TK T[KT[KT[KT[KT[KT[KT[X@878Y@#699FOOLLLLFYYjjzz] !!7>54&#"7>32;:TF7SL@{@BrnT.V(6C"${rY>q!(k@* (%V) (C "C)99190@(     ]]>32#"&'732654&+732654&#"S~7}raKP߸@@5p?{abV\fsRR8|=iVMr^Hy\R>?lH@55''5 d''5d!''5 d\m'* 'uBVH'J7`P', ;u u'6du{'VVk'&-u^f'FwVm'&-u^f'Fw^-@i'&(% !"#$  :((L LQ z\%%".999991/<2990KSX9999Y"`/]KTX...@878Y%#"&547>32!7!733##3267>54&#"XJ{vpJfl-CFtV8;DsW5:EadǦ-wOShaR{{SQTkRMS\@ c9910!!}HF/@:H910KSXY"3#2F+@> ZetZ)% # )VQ,,%$#   &$,9999999991<2220@o o ooooooo o! ].#"!!!!3267#"#73>7#7332!S`G5 7w mf'^a 7 8a@gbe^{2f7{V`34${7g1{Go@99991<20@"@@@@PPPPOOOO____]]KTKT[KT[K T[X@878YKTKT[X@878YKTKT[X@878Y3#%3#)&µ10@   ]K TK T[K T[KT[X@878Y@ ]K TX@878Y@(**)66GGWW //]]KTX@878Y@ff]3#͞Ln@ W W91<<9990KTKT[X@878YKTK T[X@878Y/&#"#>3232673#"&#+&"6}#{U#=$ &'9 }!}\79 32mn 6.mn910K TX@878Y@ //]]KTX@878YK TKT[X@878Y@ tt]#R@ 91<90K TK T[K T[K T[X@878Y@     ]KTX@878YKTKT[X@878Y3#'#Xמї c@ 91290@//// ]K TX@878YKTX@878Y#373՞ї T @*A  A  A :  V     99991<290KSXY"K TK T[K T[K T[KT[KT[KT[X@878Y@)+/--/ / 6Fy]KTKT[KT[KT[X878Y !33##7!7Zw7Rj%%`bam O@  120KTKT[X@878YKTX@878Y332673#"&w NLVlw"y=67?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~>: ~1BSax~ & 0 : !""""+"H"e%  0AR^x}  0 9 !""""+"H"`%^ChVjq_8 (Bbcdefghjikmlnoqprsutvwxzy{}|~f5D=`3=\wjfd%)DZj?wy}7V)7773\7\7\?7u777LR7LT7 Xwy{{RTJf^^^BH9H9H9HHH^^JH+#u3yyV77LRwTTTTTTf^^^^^9H9H9H9HH^^^^^uuuuV13 `'yL\#hd+/RRNT?L5=ZyyLR^9^% % V^33++% y7y77\7\7\7\7LRLRLRwww9HRP};dL# +{33\755J5!!3\B\7 +Vf^Vf^^\oLR5T1+   !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~     sfthyphenperiodcenteredEuroc6459c6460c6461c6462c6463c6466c6467c6468c6469""""zF=r8<90 9 % F  | * r 1cO-0Z3t1GZH_ V`W2 f !{!""#$%m%&&'O''(E(R(_(l(y(((((((((()))")/)<)I)V)c)p)}))))))**M*+O+,,f-8-.o.//f/0411v112:23h34 4/445o567 78i88999:$:a::::; ;;)>6>C>? ?0?R?@@AAEAB4BABNB[BhBuBBBBBBBBBBC,CpDDYDEEEF.FoFGGSG`GmGzGGHHHHI@IIJZJKIKKKKKLLL!L.L;LHLULbMM0MUMNONO"OuOPPPQv 7k9:;06fpgmQ#gaspִ glyf`h$hdmx33>lHheadٗ6hhea M[$hmtxlL8locaxmaxpg nameL- $post*(prepԱ h:: S%:%: _k0    t  0 &  X J & J &  p 8  ` :  0 & Copyright (c) 2003 by Bitstream, Inc. All Rights Reserved.Bitstream Vera Sans Mono Bold ObliqueRelease 1.10BitstreamVeraSansMono-BoldObCopyright (c) 2003 by Bitstream, Inc. All Rights Reserved. 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 the Font Software, including without limitation the rights to use, copy, merge, publish, distribute, and/or sell copies of the Font Software, and to permit persons to whom the Font Software is furnished to do so, subject to the following conditions: The above copyright and trademark notices and this permission notice shall be included in all copies of one or more of the Font Software typefaces. The Font Software may be modified, altered, or added to, and in particular the designs of glyphs or characters in the Fonts may be modified and additional glyphs or characters may be added to the Fonts, only if the fonts are renamed to names not containing either the words "Bitstream" or the word "Vera". This License becomes null and void to the extent applicable to Fonts or Font Software that has been modified and is distributed under the "Bitstream Vera" names. The Font Software may be sold as part of a larger software package but no copy of one or more of the Font Software typefaces may be sold by itself. THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL BITSTREAM OR THE GNOME FOUNDATION BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL 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. Except as contained in this notice, the names of Gnome, the Gnome Foundation, and Bitstream Inc., shall not be used in advertising or otherwise to promote the sale, use or other dealings in this Font Software without prior written authorization from the Gnome Foundation or Bitstream Inc., respectively. For further information, contact: fonts at gnome dot org.http://www.bitstream.comCopyright (c) 2003 by Bitstream, Inc. All Rights Reserved.Bitstream Vera Sans Mono Bold ObliqueRelease 1.10BitstreamVeraSansMono-BoldObCopyright (c) 2003 by Bitstream, Inc. All Rights Reserved. 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 the Font Software, including without limitation the rights to use, copy, merge, publish, distribute, and/or sell copies of the Font Software, and to permit persons to whom the Font Software is furnished to do so, subject to the following conditions: The above copyright and trademark notices and this permission notice shall be included in all copies of one or more of the Font Software typefaces. The Font Software may be modified, altered, or added to, and in particular the designs of glyphs or characters in the Fonts may be modified and additional glyphs or characters may be added to the Fonts, only if the fonts are renamed to names not containing either the words "Bitstream" or the word "Vera". This License becomes null and void to the extent applicable to Fonts or Font Software that has been modified and is distributed under the "Bitstream Vera" names. The Font Software may be sold as part of a larger software package but no copy of one or more of the Font Software typefaces may be sold by itself. THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL BITSTREAM OR THE GNOME FOUNDATION BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL 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. Except as contained in this notice, the names of Gnome, the Gnome Foundation, and Bitstream Inc., shall not be used in advertising or otherwise to promote the sale, use or other dealings in this Font Software without prior written authorization from the Gnome Foundation or Bitstream Inc., respectively. For further information, contact: fonts at gnome dot org.http://www.bitstream.com#3#N#'#\ ##3##/%%1/;sqoV#d\H```{\{`o7L'5%={D=!/s@tGG22dk  Y&Y@&~2}|{zG{{@zyAzGyAxw2xkw2vutsrqp%olkj j iihghhgg@fed:d}cbcbaba`;`2_^_^];]d\ [Z[ZY]ZZYX%Y]Y@X%WV;V}U:U2T;T'SRSdRQPdOSM;M2L:L2KJ;JdIHGF F F@ED.ED@.CBABA@]AA@=%@]@@?k>=%>=%<;4K7 7   @6 2   2X}Xd++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++, %Id@QX Y!-,%Id@QX Y!-,  P y PXY%%# P y PXY%-,KPX EDY!-,%E`D-,KSX%%EDY!!-,ED-hh@ 7/10!%!!hsr)R ]@.    49    99991/0KSX9Y"!!!# 7# f!qe@ 91<20!!!++N@3      91/<2<<22<<220#3333#3#####73#73J^^^6H6^_^_5H5^qrvuu%v'D /m@;)%%$(<2 ZD*9-.,=BZ(|',.! '3V@-(  (?? ?>.?%1"+1""499991/999904632#"&"32654&4632#"&"32654&!?9PP9:OP)˹=8ON9:QRXO::OO:9PP`^P::OP99Q&0@O$%$&%%$()'/.040'$ % -<;C<BE%%!&!0$ ' !* 199999991/99990KSX999Y"%#"&547&&54$32&&#"6673!3267VWź FA16BX^&(*0 xlsZ^^,Z.H23հi:d/#&GB"tW :][4\c910!+R7 @ G   99910#&5789RPۏͦJ"F!qV @G  999104'3q89QQ8[%3y9TJ@(   B   <2<2991<22990%#'%%73%TJLLLLKLXXB\ #@ H   <<1<<0!!#!5!RPRPo4@4I10KSX9Y"!#:6׬o9910!!+u8\o/@4I91/0KSXY"!!NFoB3@ 49/9910KSXY"3#TmZw /@ J$J-B$E0 '010@////////// / / ?????????? ? ? D@@@@@E UPPPPPU ookkkkk k o   C////////// / /  ]]4632#"&3267654&#"#"&547>32I45JJ54IjDG@k$6ABGCj$6AfUIeSJ5HI43IGqljc\rkheI+ O@(4JJ9J   991/20KSXY"!%!!!^J3F53bL J/oc@24LK JBJ 9999991/9990KSX9Y"!!7%6754&#"6632bi3K13^pcRv3me:(.RbHQ;: (*{"`(M@,M LK#M L K MBE) ) &  )999190#32654&#"6632!"&'32654&4|sjSn6hXfz6`loUFL*( Ÿ$&$.0Rc` o@<      4 J9    991/<290KSXY"!!3#!!u51>?:jBj@84JLKJ J9 E  999190KSX9Y"!!6632!"&'32654&#"T+35!H)__3F_ǎzNO  (*hy(&` '8@M M L KM%BE(  (91904&#"326&&#"6632#"&5476$32#[Mi[Kln3IH 7:_bVis ?a@4-0)? OO P,)O0P O09@? < ,- <&<3@999991999902654&#"#7#"&54326654&#"3267# 476$32hQGgP+rB3#uf&x@3#&& !"&4"##V%VUCV BE'%$#"&'991990KSX9Y"K TX'@''878Y%#"476$32.#"3267#7!FsxbQMB3QG4)E "t$?91^;@"os$$CF;9/~MbN# @?     4VS9   91/<20KSXY"P]@ PPPPP]!!!!!!'oqn'ف9+h J@&  4V9 V    991/220KSXY"!!!!! 3y3׼)43)3R@+  4 UCVV 9E  991990KSX9Y"'3267!!#"&ANgtq4!RCCqoNX`_l 7785X w@B      49   91/<290KSXY"!!!!'l3\R_- P!6@4V991/0KSXY"3!!P#'w3/  @I     4 9   991/<290KSXY"K TK T[X @ 878Y@>  /////   '//)/;8]]!!## #a1)h/q+qR @:  49  9991/<2990KSXY"(]@*5(*'%300005QWPPWPWPW]]!!! ! /=+==)P@V'VB'E*! *10@+0044553300000000 0!0"0#0$0%]4&#"3267>7>47>32#"&dPQ;d$+PQ;c%-dUMbTNxxGE'\sKxvGE,Ss ck@;    4TT 9   9991/0KSX999Y"32654&#%!2##!)Th}I:N蹤mJ[MrB[Q=-|@%VV BE." " ".99991990@A000 0 0 00000000005553$3%3&0'0(0)0*0+0,0- ]#"&547>324&#"3267>7>cTNaU#iIӛPQ;d$+PQ;c%-]Bn/xxGE'\sKxvGE,Ss@Q       4T T9    999991/<9990KSX999Y"(](]!&'&+!!232654&#=Dj5ius#ZP{\\ HWx կi|NM'@;'''4 ' UC!V U C VBE('$ $(99991990KSX99Y"   ]@    ( ]]@() ( ( 9 9 9 I I I Y Y Y h h h y y y ( ]].54!2.#"!"&'32654&'|Cg[9Dovtn|;akFEP~))>?qXTS3A451QUud>[}@4V9991/20KSXY"K TKT[KT[KT[KT[KT[X@878Y@ ])!!!j1#3-s@=   4  VE9 9991299990KSX99Y"!3267!#"&546D'\Tl'#P?L} '/RZz|R?JNƷ*d-F@#4991/290KSXY"%!!!-{dA#+)Z @A      4 9    91/<290KSXY"]@**5888]33!!!l )3 ;H+q? u@?    4 9   91/<290KSXY"!!!!!/pf#hA1N`@149 91/290KSXY"!!!%BsswwL  7@4V9V   91/0KSXY"!!!7!13/Z}#?F@!4WWG9910KSXY"!#3!D&%ZTB.@49910KSXY"#mjB@4WWG9910KSXY"!73#7!d%%9@ 991290##--W/10!5/۾s`f/910K TKT[X@878Y#fx/u{ *@O      4!  ]]!<"\]%[E_ (  !"(+99999991/9990KSX999Y"K TK T[KT[KT[KT[KT[KT[X+++@878Y@23 0!0"3#vv x  ]]"3267%!7#"&54$!37>54&#"7>32Q?v7{Cn7%faRv/q[hm?Rm}MM1 9<98'''j/ @J    4 <<E[G    999991/990KSX9999Y"K TKT[KT[KT[X  @878Y%254&#"!!>32#"&XoSJrZ)/%w?_=7Euoephw`c۽i}{W@ ` <K< K [E  99910K TKT[KT[X@878Y%#"$5!2.#"3267MSo(bL5=Nz|F[%!!=)+:8ƀ~44B @J    4 <<E[ G    999991/990KSX9999Y"K TK T[KT[KT[X  @878Y"3254&7!!7#"&547>32yoSIrZ}%B_>8Eupephw`cܾkL}&w@#`d<Kc#< [E'& '999910K TK T[X'''@878Y@ & ]%#"$547>32!3267>54&#"_nXPT  l]fYd(7**afm-vSce>;h Ubvv@=    4 ddG e    99991/22990KSX9Y"K TX@878YK TKT[KT[X@878Y!!!!7!7>;#"XX+ۮ+*+A:LNӓ7X +@\+ + + + +* +'(&)+ +4)* `<<K<&[*ef,* )+ + ,9999199990KSX99999Y"K TK T[KT[X,,,@878Y]%254&#"!"&'326?#"&547>327!sRHrP5bW6KW}6`TIFjh#% dofo --u|SUѲlgtng;`@L   4  <[G   99991/<99990KSX999Y"K TK T[KT[KT[KT[KT[X@878Y@    7 ]!>54&#"!!>32Nۅ:7Sty/#t0e (5:=\gL7 @5   4 i hded    991/20KSXY"K TK T[KT[KT[KT[KT[X@878Y!!!7!!!!5Dl++m%C`X@?   4 id dhef  .991990KSX9Y"K TX@878YK TK T[KT[KT[X@878Y]%#!73267!7!7!!!=/;-df+N)B%+.;4kTVR @D      4eG   991/<90KSXY"K TX  @878Y!! !!%r9tRZ^B `T @/ 4dGd  999991/990KSX9Y"K TKT[KT[KT[X@878Y@ ]3!!"&5467!7!q>N-ͪ +Vm!-%pF,{+@k !    $!"!#""!4&$!  <)[$e" $,"%  &!"% # % " ,.999999999991/<<<29990KSX99Y"KTK T[KT[KT[KT[X,,,@878Y@? ? ? ?????7???????@@@@@@@ @ GOOOOOOOOOO O!O"O#O$O%O&O'O(PPPPPP P __________ _!_"_#_$_%_&_'_( !"$%&'L]>32#>54&#"#>54&#"#3>32 L\l  #"09 $"/7)yEEW@Kpa/aK13=(*Zw1*B(*Wz1`tDKK;`{@L   4  <[e   9991/<99990KSX999Y"K TK T[KT[KT[KT[KT[X@878Y@ ]!>54&#"!!>32Nۅ:7Ruy% 0e (5:=`\gLXy} !@ <<[E 104&#"32!2!"&R]U{aSyM mvjy6BpV{@M4<<[Efe    999991990KSX9999Y"K TX @ 878Y%!!>32#"&7254&#"{{-%!G]>8EupoSJrZ ba۽j|rephwDV}@N4<<[Efe  999991990KSX9999Y"K TK T[KT[X  @878Y7!!#"&547>32"3254&V'%s?_>8FtooTJrZP_dܾk}sdqhw{}@5       4 <[ e   91/990KSX99Y"KTX@878Y.#"!!>320vJ(d%#>w>o,/+)``iZF{'@<    4 ``<K<K%[E( " "(9999190KSX99Y"K TK T[X(((@878Y@(-/// / / ,/. / :::JJJYYY ]].#"#"&'32654&/.54$32F2K]^nIJvhc3V]n{=Xzr]=56F;-7%%r##8;KC.4'$p@>  4 de d  9991/<2990KSX9Y"K TX@878YK TKT[KT[X@878Y!!;#"&5467!7!P>+hj`@O   4  <E e  9991/299990KSX999Y"K TK T[X@878YKTX@878Y@      ]!3267!!7#"&546{#:6Sqz#!,eT4:?wZiH`@#4e91/290KSXY"K TK T[X@878YKTK T[K T[KT[KT[KT[X@878Y@    ] !!P``nZ-` @B      4 e    91/<290KSXY"]@.&))::KL   **-<<<IM[\]]333! !`!#^`5ZN` @@    4 e   91/<290KSXY"K TX @ 878YKTKT[KT[KT[KT[KT[KT[KT[X  @878Y@ )8HYY]] !! !#ժ- `dR=X`@A     4  df e  99129990KSX9Y"K TX@878YK TK T[KT[KT[X@878Y+7326?!!'p~-uWd<')5yi%=)+${hY& }_S) DPm}880=L=nVѧWLE'.8;3k10#0@i+,+,,+ 4+1''%,%W'W/W'G1/, 1'&%(&01.991999999990KSX9999Y"32677667&&546776654&##73233#"##{gX' ~^T) BQy%ͤ)_t>%>)+UͤTOG*06<1`r#m :UFtޓXy++@ ll  19990#"'&'&&#"56632326yKOZq Mg3NJNS5dJ t]F+<73 ":?=6 7=nk'$3um !@J    !  !4 < = Q  " !  "99991/<9990KSXY"32654&#"!!!&&54632!oM66MN56MEku$*vt/P6MM66MMq$j5uu;lo'&yk'(Xum'13u=k'23u-k'89u/f'D/uf'DC/uf'D/u1'D/9'D/u'Do{'FLf'HLf'HCLf'HL1'Hf'7f'C7f'71';h9'QXf'Xyf'CXyf'Xy1'Xy9'jf'Xjf'XCjf'Xj1'X;y Z@/4; 9    9991<20KSXY"!!!!!7!yLL++L}8V@m mB102#"&546"32654&hCz//12.0zDHdbHHdc30/xDBz./3dHHbcGHdhN$R@1` `J K JK[ E%  ""%912<220&&'667#&&5476673N5-n@@@3>A77mfP{775rh5(/L.)" țjSY"j@F  4 dLK<B J  9991/222990KSX9Y"&&#"!!!!3#737$325i!P+?34@-%3J))N h=#G@N7829 %8?/;.2;+;;EB+H785 (" 5%(9B 8?"B5("B#&N:)9>&"J-D?U^=micUor-+Rb.JKV^dmPzr30G7,X&':6&'X(#@"!D?)HECAzLTC+e?!"D?Ee DHsK]2)b?` 104676632#"&'&&736HI256743IH426I235624HI447743f; `@-  49  <9991290KSX9Y"!###&&54-վEfN'3@e )*+*&'%(++*4("!%3.+%`< %<.G E))*31! (+*1!  ! 1*499999999991/99990KSX999Y"KTKT[KT[KT[K T[X444@878Y@lllllllllll ]#"&'732654/.546754&#"!6$32y`L;:B@1:k.ZhoE,*ZR]r) #dS9`L;}D ODBoG-g;%@Gfefݮ+d}N 4L\@3-*+'0!o5n2+oAn M*',$0-!1"3+"$ G3 ;/29991<99902#"&'&&546766#32654&'2#'&&'##%"32676654&'&&hZZ\[[[~}[[[\ZZb@@998(NG&7O?9)cIGHHGHccGHHHHHNZZ[~}[[[[[[}~[ZZb((+)oXZAU 81:/qGIGebJGHHGJbeGIG}N1ID@%  q po>qpo2n>n&JD  8 ,/210&&#"3267#"&546322#"&'&&546766"32676654&'&&P4[0akjb5`*7j2ʩ7iZZ\[[[~}[[[\ZZ~cIGHHGGcdFHHHHHh__g$ZZ[~}[[[[[[}~[ZZGIGebHHIIHHbeGIGf A@$   9 # # ###/91<<2<<9073#######5dYЙkKuTKBC9f-10KTK T[X@878Y!#wAFf;H1a@99991<20K TK T[X@878Y@////????]]3#%3#Z111N >@"   H H   <291<2<2.990!3!!!'7#5!7!NŮmA1}j@L    4V <V9VQS  91/<20KSXY"!!!!!!!33<3N64H}nj{sL'.l@8+'/.(+%&"V"+VB"E/ .(% '& &/999999919999999032&&54766327#"&''&&#"wM8}4 bUNi= L5Nr9K4y6b<=2^"J'BAs8a,>ObJHrN47  /7@$ s'!t s-r0 $$$*099991<2<299032654&#"&&#"3266632#"&'#"&546320P2;JC88b6J0 b )'8yaxMB3F KbPJ?(: *.t@A! '~!~ -~+ +'~}~|B/! *$-,.+/  $ $ /999999919990#7#"&5463376654&#"76632"3267!!X.{OkЇIF9S!PAzu:,Up#P@X65zh#(*(&i^ EHK,:x+ 4@~~ }~B  999910432#"&4&#"326!!3%D=WxE54&#"!3267#"&'#"&546;7>54&#"7>32>32Jbu:1CP% (,8G=UF8A/9Q^ =Yl DA4V/OERt4T~NgU3IH7:_bViR ]@. 49 99991/0KSX9Y"!!!3H8 f#+eXjy@ H 10!#!X!,1 ,@     9190'%3##q@`44{$ V#@I   4   d`K<`Ks7+P'_iP'ˠ54&#"!3267#"&'#"&547>32>32267>7>54&#" (,7G> VH9@/9R^2\@68DŽSx%0Rr4D 394D3B'40UYF54O]72((QEKK<=<;>1XZ@JvaZxHA@I*|HQz"IA@ 9910!! 1/@ 9910!! 1/ d@2     4 G    991<20KSX99Y"!3!35׬5׬~~ `@0     4 G   91<20KSX99Y"!#!#}95׬-95׬8@4G910KSX9Y"!35׬~4@4G10KSX9Y"!#95׬BV $@H  <<10!!!!!!55uKu#\u"@91990 h 9%-X1'\Nk'</uB3@ 49/9910KSXY"3#Tm^T /c@8  *( -'! ) -0)'!$ * ( $02299912299904&#"3267'#"&''7&&5467'76632;dJIeeIJd$P0'T-)S.'QIccIJffq)S,/Q$*S)/Q&1m#@ /91907m2)w#=w#@ /91907%'-B#wvB@]       4 ddGe       999991/<2<2990KSX9Y"!!!!!!#7376633#"%9Zۮɮۮ-'P+CB:-cNɝ1B@M    4 ddG e      99991/<22990KSX9Y"3#!#737663!!#"d+ۮ-'B:cNɝ311;{@J   4;;  9      999912<220KSXY"!!!!!!!7!!7!yLN-ZL+JJ+LZ-J}+}-/@4I910KSXY"!!NHo4@4I10KSX9Y"!#:6׬oo `@0     4 I   91<20KSX99Y"!#!#93լ+:3լo $0<HLp@?J%K+I"L 71= +"%>4C: L(I1KJ11F17@11.11(1/99991/<2229999904632#"&5%"32654&4632#"&%"32654&4632#"&%"32654&%wwwu2IH33JJxvvw4GH33JKޥxwvw3HH33JK'!xwxwK24KK42IxwxG45LM41J+xwxH35LM41J^\rk'$3uk'(Xuwk'$3uk'(Xuk'(Xuk',/uk',/uk',/uk',/u=k'23u=k'23u=k'23u-k'89u-k'89u-k'89u7` j@"4ded   991/20KSXY"KTKT[KT[K T[X  @878Y!!!7!!5Dl++m`PFf6@ 991290K TKT[X@878Y3#'#󷰘fyh9@     99991999999990K TK T[X@878YK TX@878Y@'           ]'&'&#"#>3232673#"&1*"4 _"=.3$&4 }e#AT%!E<$'C>XB19910K TK T[X@878Y!!w%`Fd@  1<990K TX@878YK TX@878Y@  ]332673#"&5XLQuƘF =HMER;189910K TX@878Y@ //??]!!/1 :@ 2 210K TK T[X@878Y4632#"&732654&#"ϦwvvwM76MN57Mvvvv7LM66MMo{%@    291/90!#"&'732654&'3$$4f0+R#DI0\,cv 62E7ufi@ 1<20K TKT[X@878Y@/,,,,&87663146LLJJBB]!#3#fxo#@   2 91/90!33267#"&546K04*%P-1\+ctNPJ!) MB<f6@ 991<90K TKT[X@878Y#373F򶮙x! d@8   4  V9   .9991/90KSXY"3'%!7!P`s+'_s^\w3c#%@J 4dGd  99991/990KSX9Y"KTKT[K T[X@878Y3!!"&5467'!7!%=N-Ω 5Zb+V^"gEl -%pF,Ò:ˊk'6 /uZf'V k'= /u1f']@ <210##  "}@F!" 4<V9V #" #99991/220KSX99Y"! #!#733#3267654&#;%hH^/f?-N`p+4A}WqE\PFrXXj%~R)@G!"! %&'&$#$''&4)('$#"!%< <E%G*() "#'%$! ) *999999199990KSX9Y"&&#"32654&!"&532&&''%'!%F&R,dT~8Eupephwba۽j|B H 10!!BKwXs 0@   <291<290 '7XJJGJHJHH! S@+334       99120KSXY"37733!;jŇo-+)NL@&4 3999999190KSXY"!!76754&#"76632h3[HC?MLIfD-%A[-1$#lc/(K@+ # )  &33)999190#732654&#"76632#"&'732654&omX^PF0PIBypScKE>O`oW73(.fYZo cJQF-5w{''  V{'' V/w''  Vfk'* /uXF'Jk', /uo'6ZoF{'Vk'&uf'FV4k'& uf'FVBs'@h'&% #"$      4 "  <"<E[G %      (999991/<2990KSX9999Y"!7!7!3#!7#"&5476632"3254&VB%/%%B_>8EupoSIrZTtt`cܾkqephw9910!!+u8-/@4I910KSXY"!!NH,v@@#$u& uUCJ UC JB E*-,&+*'## %+#-999999999122<290632&&#"!!!!3267#"55#73667#7]VPI@8Oi9RZRhMs?LUTT$$KMg|>Z$$ +K!;\@99991<20KTX@878Y@////????]]3#%3#P/15Ds10K TX@878YK TX@878YKTX@878Y@++//]]!#+s`@  99991<<99990K TX@878YK TX@878YKTX@878Y@%         ]'.#"#>3232673#"&1!#5 `%A#5%'4 ^%@!% @8) =:Xy} !@ <<[E 104&#"32!2!"&R]U{aSyM mvjy6BpX910#ɏX?@ 991<90!#'#L\s@ 991290!373/wo@ 9910'T%#%vu _@3 3  3 334      991<290KSXY"333##7!7Reml#${)N@  1290332673#"&5 ROGj(Ɠ<7=6}H9910!!w/  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~>: ~1BSax~ & 0 : !""""+"H"e%  0AR^x}  0 9 !""""+"H"`%^ChVjq_8 (Bbcdefghjikmlnoqprsutvwxzy{}|~hR'!RqyB\Z+`m?71XXX#/fP==-)qTj9s//BL;R;XDZjZ1X=-//////LLLL;XXXXXjjjjhhf9Njs XXXHZZRX1 XNR=Bu1=BB1===---PyRu%Z1RBw!/fZB5sXX/H#  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~      sfthyphenperiodcenteredEuroc6459c6460c6461c6462c6469c6471c6473c6478c6479c6476c6477""""ktGq_Ty ;  Z ^  Q h?#C7Av*?f(@F29u   !!!""#=##$$$+$8$E$R$_$l$y$$$$$$$$$$%%%"%/%<%I%V%c%p%}%%%&&&''(,()***o**++,,r,,--t-.Z.../M/00q1z12t223 344A4{45555+5856s6667G7v7778 8818889t9:N:t::;;;;;;;;<<<<)<69>m>>??i??@@@ @A@ABAOA\ABKBaBBC/CCCCCCCCDDDD,D9DDEEEF%FFFGG8GSGGGNH@mj^sGBGSf    JBits! mB`#cVeraSansMonoBdOb6628J00@             9_<=–jsmVeraMoBd.ttf000066400000000000000000001376341300200146000331030ustar00rootroot00000000000000connectome-workbench-1.2.3+git41-gc4c6c90/src/Resources/GeneralResources/Fonts/VeraFontsOS/2sVPCLTc6cmapXcvt IDfpgmw%gaspX glyfuÒ&vhdmx32`Hheadjd6hhea "\$hmtx ϜT4locaە maxp< name) postnprepLQ3 ::S:: Wc0y   } t  0   N :  :   V 4 n `   0 & Copyright (c) 2003 by Bitstream, Inc. All Rights Reserved.Bitstream Vera Sans Mono BoldRelease 1.10BitstreamVeraSansMono-BoldCopyright (c) 2003 by Bitstream, Inc. All Rights Reserved. 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 the Font Software, including without limitation the rights to use, copy, merge, publish, distribute, and/or sell copies of the Font Software, and to permit persons to whom the Font Software is furnished to do so, subject to the following conditions: The above copyright and trademark notices and this permission notice shall be included in all copies of one or more of the Font Software typefaces. The Font Software may be modified, altered, or added to, and in particular the designs of glyphs or characters in the Fonts may be modified and additional glyphs or characters may be added to the Fonts, only if the fonts are renamed to names not containing either the words "Bitstream" or the word "Vera". This License becomes null and void to the extent applicable to Fonts or Font Software that has been modified and is distributed under the "Bitstream Vera" names. The Font Software may be sold as part of a larger software package but no copy of one or more of the Font Software typefaces may be sold by itself. THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL BITSTREAM OR THE GNOME FOUNDATION BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL 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. Except as contained in this notice, the names of Gnome, the Gnome Foundation, and Bitstream Inc., shall not be used in advertising or otherwise to promote the sale, use or other dealings in this Font Software without prior written authorization from the Gnome Foundation or Bitstream Inc., respectively. For further information, contact: fonts at gnome dot org.http://www.bitstream.comCopyright (c) 2003 by Bitstream, Inc. All Rights Reserved.Bitstream Vera Sans Mono BoldRelease 1.10BitstreamVeraSansMono-BoldCopyright (c) 2003 by Bitstream, Inc. All Rights Reserved. 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 the Font Software, including without limitation the rights to use, copy, merge, publish, distribute, and/or sell copies of the Font Software, and to permit persons to whom the Font Software is furnished to do so, subject to the following conditions: The above copyright and trademark notices and this permission notice shall be included in all copies of one or more of the Font Software typefaces. The Font Software may be modified, altered, or added to, and in particular the designs of glyphs or characters in the Fonts may be modified and additional glyphs or characters may be added to the Fonts, only if the fonts are renamed to names not containing either the words "Bitstream" or the word "Vera". This License becomes null and void to the extent applicable to Fonts or Font Software that has been modified and is distributed under the "Bitstream Vera" names. The Font Software may be sold as part of a larger software package but no copy of one or more of the Font Software typefaces may be sold by itself. THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL BITSTREAM OR THE GNOME FOUNDATION BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL 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. Except as contained in this notice, the names of Gnome, the Gnome Foundation, and Bitstream Inc., shall not be used in advertising or otherwise to promote the sale, use or other dealings in this Font Software without prior written authorization from the Gnome Foundation or Bitstream Inc., respectively. For further information, contact: fonts at gnome dot org.http://www.bitstream.com3#N\{#'##\ #\\w#3b##\b b b%%1/#/ 9bRsq#oVd\Hf```{\{`o7L'5%={D=!/s@tGG22dk  Y&Y@&2G@AGA2k2%e}f2 ]%]@%e2d~}d|S{f{2zez2yxfxdwvts s rq.rq.pfp}onmnml]mmli%l]l@kkji%ji@%hfhdgfgdfefedcba}`^d\[\[Z2Y-YXWWV2UTS TS RQRQPQPONMdNMdLKJIJIHGF}EDE}DCA?2>=<=<; <; :9:98 998 8@76776-6545K4343212d1-10/@0D/.-.-,--@@ ,+,,@ +*++@ * **@W)K('K&$&$%$%K$$#""2! 4!>  42@ @@ @WK77222X}X       @   @   @ @  d+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++, %Id@QX Y!-,%Id@QX Y!-,  P y PXY%%# P y PXY%-,KPX EDY!-,%E`D-,KSX%%EDY!!-,ED-hh@ b/10!%!!hsr) @ d <2991/0!!!#  !#qe@ d1<20!!!++J@0g g    91/<<<<<<<2220333#3!####5!#5!#3_^aJ^^^^J^Jvuu%vD /P@+)%$(hh!/$, ( 0<21/<222906654&#&&'&&546753&&'?FE>??ˍWggU̹GP>[ٴ O=>O-C:9L-.+=BI' (0Ϭ ! '3V@-(  (kk kj.k%1"+1""499991/999904632#"&"32654&4632#"&"32654&!?9PP9:OP)˹=8ON9:QRXO::OO:9PP`^P::OP99Q%*8@Z)(*()(-,.+23456718%8+(%#1) #1p#popnr)($ +.#  .8 )$. 9999999991/99990KSX999Y"%#"5467&&54632&&#"6654&'3!32676767fKWꍋ20ID@APR=D9DI"BCr;H23ёXN<$%86$~f &b993V+wIz  d10!+ @ut  29910#&547䟚@=5R @ut  299103#6545䞛䄀=?y9TJ@(   n   <2<2991<22990%#'%%73%TJLLLLKLXXB\ #@ v    <<1<<0!!#!5!RPRPjo@ w 10!#9co-10!!-wow 1/0!!MoqB`@ d103#m{V #$@ x! xn!r$  $10@////////// / / ?????????? ? ? OOKKKKK K O TTTPPPTT T ddd```dd d    T////////// / /   $]]4632#"&"326&32#"H45HH54H|f^^fg^^5HH54HGB~{|~o $@xxdx  1/20!%!!!J5JML J/s'[@/% zy xnx    991/990KSX9Y"!!5776654&#"6632uL=KByoOkk^;H5/FVAdm?<')ݿX^D}L(G@)p zy#p z y pnr) &  )9190#32654&#"6632!"&'32654&%nyynTgg\qd^xxWOS]*( !εǢ&$/1o^s}fu B@   % xd   <291/<290KSXY"!!3#!!y5jBF<@!xzyx xd r  190!!6632!"&'32654&#"+$R.`fSXOQ  ))u''b $5@{{ z y{"nr%   %190"32654&&&#"6632# !2`ee``gg#OC/c..F싄--AAn75@%xd 991/0KSXY"!!!P #/D@% {'{-{nr'0 $*  ! 0991990"32654&%&&54632#"&54632654&#"hczzcc{zqvto|eWXeeXVf}gg~eg}}'yغx(&ĉ׊TXggXWefoN$7@{ zy{ {nr% " %19073267#"&543 !"&2654&#"OC/dF7_ee_`gg .,AAi}}'@ ww <21/0!!!!MM's' "@ww   <210!#!!MNMoXmy@29190 5y!X'y@vv<210!!!!X!!Xmy@<919055X!`a)$q@8   %$  fhn !  %<2999991/9990KSX99Y"!!!546776654&#"6632  >PZ?-\\T`beD^XD&cNY=P+CDGF 89L\VBT= s 4]@1(+$ 4| | }'$|+}|+15'(   ! .529919999904&#"326#5#"&5463254&#"3267# !2fYYeeYYf&gHȥGl"0PE\Qm`v4!qqrR511/)//77! @;      %h~d     91/<90KSXY"K TK T[X  @878Y@,0000 5::5s|]]!!!!!hi\uZq+q} >@$h hdh   !2991/9032654&#32654&#%!2!!čqvp_anF`wyjFP\\S뽼 İ9.@op op nr! 210%# !2&&#"32679FU>.UDLLLL+$$xy$$FAAFu(@p dp !  "99991/0326&#! !!P<nB? tsJ *@ppd p#  2<1/0!!!!!!!J^?{yX %@ppd# 21/0!!!!!XBuujS@!ppopnr%! 1990@]]#5!# !2.#"326hUu?-ZL>`.DTIKsy30PQH &@pd  " 221/<20!!!!!!'q'9+h% #@pd p 221/220!!!!!y))3m.@ opp dr  199073265!!!"&mVctl_JVX\t 4u a@3  %d   291/<290KSXY"!!!!u'N)Nw@ pd1/03!!'w/V{ @,  % d   % % 91/<290KSXY" ]@R )=??   & )/708?    ]]!!###V`bq+sTwX S@%d&& 991/<2990KSXY"]@  ]]!!!!w=^=+=\u #@ppnr !! '10"326&! ! hqhhqrhh    xx{,@pp d !  299991/032654&#%! !#!yuu`5nJbyyb\u<@ pp n ! !'9919990# ! "326&   ~xqhhqrhh~xL  f@6  %p pd   !  "29991/<9990KSX9Y"!&'&##!! 32654&#',A/ Ok^ yihz A^y暶 i_mm^V'p@>'' '%' o!p o pnr('$ ($"(9999190KSX99Y"&&54$32&&#"!"&'32654&'ߞge_`krSohvlmxPLU/.CFVP>Q10Bڦ541TRcYCeZw@ pd1/20!!!!!jf*@  pr d  1299990!3265!! j'reer''ppR9s@%%d91/290KSXY"K TX@878Y@  ]]%!!!h)g)+ @@      % d   /91/<<90KSXY"]@6fe   )&ghj f g wzsx]]!3!! !kT;H+ @C    % d   91/<290KSXY"K TX  @878Y@  ]]) ! ! !V11X%!S@(%d 9991/290KSXY"!!!>"#>3XwLs 8@%pdp 991/0KSXY"!!!5!Lw#$@ut10!#3!ZoB`@ d9910#Nm/+ @ut9910!53#5+޾9@ d91290##--/10!5/۾f.10K TKT[X@878Y #fx^T{ %@/   ggh g#r #  .)&22991/99990@-400 4!urr  ]"326=%!5#"&546!354&#"5>3 ZMt#5d gdikapTfLZq}JPʵĻ1GI5:(&w 6@  h hrt2 0221/9904&#"3266632#"&'!!Rl__nn__lh6Ze.$-6]]ba%}.@yh yh r 75210%# !2&&#"3267%Jb&ZS@RUB9++89*,7;9:Z;6@hhrt2 )221/990!!5#"3232654&#"$/eZl__nn__lSab40]\}{L@#   hy h r .)190@ ]%# 32!3267.#"Nfv ekspex 7**->w:?itw{q;4@ t  <<2991/22990!!!!5!54633#"XB/bNʜ0bXH} (I@(  &' hyhh#')& 2 ))221999904&#"326!"&'32655#"325!#r]\qq\]r%\]S[|v+f`+%B .,u|yPN, 8ZR/.@ h t 7  521/<99990!4&#"!!6632/ENPZ#j)yh}]f B@  s  <<1/20K TX@878Y!!!5!!!!Dlm%`XR O@  s  <2991990K TX@878Y%#!53265!5!5!!RbRN%+nTV @@! %t  .5 291/<90KSXY"!!!!%`cXd}^B `TZF %@t  991/990!5!33!"&NRbѶ`nR{"@'  h :=:=:#91/<<<299990K TK T[X###@878Y@7      /////////?? ? ? ? ?????]>32#4&#"#4&#"#3>32!fJo&22((22&nDDpGD}TV{1{VT}1`tBMQ/{0@  h 7  521/<99990!4&#"!!6632/ENO[#j)zi~`]fbo{ #@hhr . )10"32654&32#"hixxijxx==Vw{9@hhr 20221990%!!6632#"&4&#"326$.eZbl__nn__l ab]񢸸ZV;{ 5@ h hr 2)22199032654&#"#"325!!l__nn__l6Ze/$1^]04ba#{)@ h  21/990&&#"!!66320M]&%+w&ln``i+{'@=    %  yhyh%r( >7"5(99991990KSX99Y"(!]@+ $)) ) ) ) ) ),//,))))) (!$'].#"#"&'32654&/.54632QXbd Tema^gjKQ_=4598P2&##7:<98<"&o1N@  <<2991/<2990K TK T[X@878Y!!;#"&5!5!IUKA>%`0@  hr   7521/299990!3265!!5#"&%DOOY%iTyh~]fP`i@%%1/91/290KSXY"X]@U  ZUUY]] !!)``j` @@      %    /91/<<90KSXY"h ]@@     )&') 96k c wxuy   ]]333! !ywꈇ`5ZF7` @B    %  1/ 91/<290KSXY" ]@    96]] ! ! !yV`yHN;X`w@A     %    1 /9129990KSX9Y"##532677!!;vwZT+V44y=o<A)9` 8@%6/ 2991/0KSXY"!!!5!MiN``$^@0 %   !  t% $  %<<29999999199999990#"&554&##5326554633#"3k>>k{hFb~~bFhחsrݓTѥ͐Ut10#!$`@1%   #t%# %<2<999999919999999032655467&&554&##53233#"##yhHb}~aGiym>>mVͤэVחstݔXy++@  19990#"'&'&&#"56632326yKOZq Mg3NJNS5dJ t]F+<73 ":?=6 7=!k'$u!m!@M ! !%h  i ~!   "999991/<9990KSXY"]@\ !000 0!o  !!' )!;44 ;!t |! ! !]] !!!.54632%32654&#"!/\uZ,*uv*N57MN65Nsq%a=uu>_6MM66MMAo9'&uJk'(uwXm'1u\uk'2ujfk'8u^Tf'D^Tf'DC^Tf'D^T1'D^T9'D^T'Do%}'FH\}f'H'\}f'HC'\}f'H'\}1'H'f'f'Cf'1'/9'Qbo0@ hhr .)10!#"32654&32#"œixxijxx==bo0@ hhr .)10#"32654&32#"ixxijxx+t==bo8@ hhr . )912903#'#"32654&32#"ƲxixxijxxՓ==bo7@hhr . ) 12203#%3#"32654&32#"-Pixxijxxj==bo".:h@:    )h8#h2 28r;&,5.&/ /);99991999990'&'&#"#54632326553#"&"32654&32#"j7 +"'lY$G*>%$'kX#A4ixxijxx3%@9!q+>: s|==%f'X%f'XC%f'X%1'X;3 $@f d ?  <<1<20!!!!!5!LK}8V@ n@@102#"&546"32654&hCz//12.0zDHdbHHdc30/xDBz./3dHHbcGHd !F@& yh yh r" -/"<<<2212<220&&'667#&5473 8x?@w84xC=ur{~5*1L0+" :9!wb>@  zyhn x <<1/222990&&#"!!!!3#535632N9EjcPS)+N=% >y@C/0*1 06 'f&*f# f f^ C:HX D0M~JIebf}N 4L\@3-*+'0!52+A M*',$0-!1E3+E$CGB3C;B/29991<99902#"&'&&546766#32654&'2#'&&'##%"32676654&'&&hZZ\[[[~}[[[\ZZb@@998(NG&7O?9)cIGHHGHccGHHHHHNZZ[~}[[[[[[}~[ZZb((+)oXZAU 81:/qGIGebJGHHGJbeGIG}N1ID@%  >2>&JDB C8B ,/210&&#"3267#"&546322#"&'&&546766"32676654&'&&P4[0akjb5`*7j2ʩ7iZZ\[[[~}[[[\ZZ~cIGHHGGcdFHHHHHh__g$ZZ[~}[[[[[[}~[ZZGIGebHHIIHHbeGIGf A@$   d F F FFF/91<<2<<9073#######5dYЙkKuTKBC f.10KTK T[X@878Y!#f-;1I@ 1<20K TX@878YK TX@878Y3#%3#u1N >@"   v v  <291<2<2.990!3!!!'7#5!7!NŮmA1}c@3  %p hpdp~  % /<291/<20KSXY"!!!!!!#36Vfnj{ )s@> )*  '$ ($p$ pn$r*(! '!)!!!'*.99999991999999903264&'&&#"&&5!27!"&''U8rh9N4pi## g=q''j@xb9= >5 33X>=s^xCBr  /7@$ '! -0 $GG*099991<2<299032654&#"&&#"3266632#"&'#"&546320P2;JC88b6J0"#>VddVX𻗽JT` G@'  hr !    75!29912<<990!3265!3267#"&'#"&'RRQP!+J#H[%nF;ST XrsrsG> KSNP/0DF)8@'! '!* $$*99919906654&#"#"&54632#"&54324&#"32?3-,:*:}fMHeLJdcBds<+@lFWt|uy LP *@     <919905!!!!5my=/9) ӇL9@__120!!!9_^-L<@    LKJKJH1/2299990#3267#"&'&&55!!#51F",c7OrL? " VX#o{H)*@$ *'! `a!`*91026732#"&'&'&#"#"&54632mfbwL=.> b )'8yaxMB3F KbPJ?(: %)d@4 $#(&$& n*&(' #*229991999905#"326#"&5463354&#"56632#!!fmd=6Vn2yN[[R@ILP4818g96u11!"@w0@ n  9910!!4632#"&"32654&%PŦçkIUUIIUUV޾~mm}}mm~ZwF@$    OON N 9991/<299073&5323!6654&#"!Zxllx)QNpgfqPR'm8nȿm O֦M{ A@K. 4, ;5#A  ggy 451g8g,>8& rB#;-, 4T)-RB<999912222999990@'33040536G3G4G5G6YYYYW3W6 A]5#"326554&#"!3267#"&'#"&546;54&#"5>32>32IWY@=?=2??2=d_A=B +s@>+,  )*&h&h&r,+,* # )#.),9999999919999999032654&&&#"&&5327#"&''?B&hxJZ>'jy--UC./SCc:"4Im=+-yLk--w%x@<   %%  fhrd&# !  &<<99999919990KSX99Y"!!!3267#"&546776654765  >PZ=-\\SabeD^XB&ocMY;Q,CDGF89L\V@T? @ d <2991/0!!!3 !#+eXjy@ v 10!#!X!,1 ,@     9190'%3##q@`44{$ V#@I   %   yhyh!n$  $912299990KSX99Y"&&#"!!#"&'53267#5!6632)R%^g^ʠ>s7+P'_iP'ˠ@!p  pdp %  <99991/220! !!!!!"33C=.{\[|478nͲɮ{ )5o@1("!% !gy3g -%g r6*0T!*R(6<9912<22999990@ !"()]!3267#"&'#"32>32'54&#"4&#"326>c_>2:MN;;NM:ߐ;:,*JJMG8;;B%$'g^$BT%>;+?:-X@10K TX@878YK TX@878Y!!-wF `@  ]]1<0K TX@878YK TX@878Y@]332673#"& dSSc FEKJF;1M10K TX@878YK TK T[X@878Y00]!!1L R@ ] ]10K TK T[X@878YK TX@878Y4632#"&732654&#"LvwwvM67MN66Mvvvv7LM66MMoo3!@   ]1/90!#"&'532654&'97{0e5-T$:A*.>j/_[ .(R<)=fI@991<20K TKT[X@878Y55]3#3#Vfxoj@  ] 1/90!33267#"&546-5%=2&M(6_)r|7GF'1 \V5mf5@ 91<90K TKT[X@878Y 373Dzx 7@  pd    <2.9991/903'%!%!q'p}wo™%\D@%  t  <<99991/99905'!5!%33!"&duK'grSbѶӦâѠnVk'6u+f'Vsk'=u9f']@ <210##  u 9@hp dp  ! "/<29991/2203#326&#! !!#53P<nBĉr tsbf(@N (((#$%$"!"%%$%('%"! #&#h hr#t)'& !#(%" .))999919990KSX9Y"&&#"32654&#"432''%'!%+1b0izwhms udV(?7%\)B}-b2 wumt{uk'<u;Xf'\{0@hh d !  2299991/032654&#!3 !#!ytu`'n5nIbxydVw9@hhrt 20221990%!!6632#"&4&#"326$.eZbl__nn__lab]񢸸B v10!!BKwXs 0@   <291<290 '7XJJGJHJHH9 )@   ^ 12035733!9x-+)N2@ ^9190!!56754&#"56632f3Z:WK8MKB6-+KO4<#"}o~*(D@& #  )^&^ )9190#532654&#"56632#"&'532654&FooJUOFEDECibouKJCPU_b2,-3teI^p\y@8=D/w{''  V/w{'' V/w''  Vujk'* ubXHF'J%k', uoV'6o+{'VAk'&upf'Ffjk'&uGf'FfZ$F@&"   "hhrt   2)%<<1/<2990!5!5!3#!5#"3232654&#"2$/eZl__nn__l"ttab40]-10!!-ww 10!!M51@9&(  ox#o xn#r/21('/),&, 0',2229999999999122<20@]]632.#"!!!#3267#"'#73.5467#7,$SDJMkT?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~>: ~1BSax~ & 0 : !""""+"H"e%  0AR^x}  0 9 !""""+"H"`%^ChVjq_8 (Bbcdefghjikmlnoqprsutvwxzy{}|~h!%5yBj-q{s}fosXXX!}umuVw\\Zj9so/9^Z\bZRbZ#oP7;X!!w\j^^^^^^\\\\bbbbbwF-N XXXHZX1 Xw9!!\DBu;qZ  j!!\\\jjj -Lo)%sb;Bw9///ubZ-- /#   !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~      sfthyphenperiodcenteredEuroc6459c6460c6461c6468c6470c6472c6477c6478c6475c6476""""Ik>e|He{yeJx7Ya \ 0 ^  = t S  h EecNr e][ GV-g-&BO #0=JWdq~ g ^J2 ( !?!""8"o"# ##$$$R$$%<%%%&5&&'A'(P()M)t))*<**+.+f+++++, ,,,,--9-V-----.^.}../0/q///00000000001111(151B1g12+2Y223&3W3334(4s44444555556D6Z6677_7p777777777788d8z899I9t::::}::;1;Y;o ME@m GBGSf   JBits mB`#cVeraSansMonoBd6628B00@             X-_<#–UmVeraMoIt.ttf000066400000000000000000001523541300200146000331260ustar00rootroot00000000000000connectome-workbench-1.2.3+git41-gc4c6c90/src/Resources/GeneralResources/Fonts/VeraFontsOS/2ZVPCLTp(6cmapLXcvt &$fpgm%gaspԨ glyf4&\hdmx32``HheadٮԴ6hhea fF$hmtx 4locapmaxp name@Z postn]prep8 ::S : : Zf0    t  0 "  T @ " @ "  b : z ` .  0 & Copyright (c) 2003 by Bitstream, Inc. All Rights Reserved.Bitstream Vera Sans Mono ObliqueRelease 1.10BitstreamVeraSansMono-ObliqueCopyright (c) 2003 by Bitstream, Inc. All Rights Reserved. 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 the Font Software, including without limitation the rights to use, copy, merge, publish, distribute, and/or sell copies of the Font Software, and to permit persons to whom the Font Software is furnished to do so, subject to the following conditions: The above copyright and trademark notices and this permission notice shall be included in all copies of one or more of the Font Software typefaces. The Font Software may be modified, altered, or added to, and in particular the designs of glyphs or characters in the Fonts may be modified and additional glyphs or characters may be added to the Fonts, only if the fonts are renamed to names not containing either the words "Bitstream" or the word "Vera". This License becomes null and void to the extent applicable to Fonts or Font Software that has been modified and is distributed under the "Bitstream Vera" names. The Font Software may be sold as part of a larger software package but no copy of one or more of the Font Software typefaces may be sold by itself. THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL BITSTREAM OR THE GNOME FOUNDATION BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL 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. Except as contained in this notice, the names of Gnome, the Gnome Foundation, and Bitstream Inc., shall not be used in advertising or otherwise to promote the sale, use or other dealings in this Font Software without prior written authorization from the Gnome Foundation or Bitstream Inc., respectively. For further information, contact: fonts at gnome dot org.http://www.bitstream.comCopyright (c) 2003 by Bitstream, Inc. All Rights Reserved.Bitstream Vera Sans Mono ObliqueRelease 1.10BitstreamVeraSansMono-ObliqueCopyright (c) 2003 by Bitstream, Inc. All Rights Reserved. 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 the Font Software, including without limitation the rights to use, copy, merge, publish, distribute, and/or sell copies of the Font Software, and to permit persons to whom the Font Software is furnished to do so, subject to the following conditions: The above copyright and trademark notices and this permission notice shall be included in all copies of one or more of the Font Software typefaces. The Font Software may be modified, altered, or added to, and in particular the designs of glyphs or characters in the Fonts may be modified and additional glyphs or characters may be added to the Fonts, only if the fonts are renamed to names not containing either the words "Bitstream" or the word "Vera". This License becomes null and void to the extent applicable to Fonts or Font Software that has been modified and is distributed under the "Bitstream Vera" names. The Font Software may be sold as part of a larger software package but no copy of one or more of the Font Software typefaces may be sold by itself. THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL BITSTREAM OR THE GNOME FOUNDATION BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL 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. Except as contained in this notice, the names of Gnome, the Gnome Foundation, and Bitstream Inc., shall not be used in advertising or otherwise to promote the sale, use or other dealings in this Font Software without prior written authorization from the Gnome Foundation or Bitstream Inc., respectively. For further information, contact: fonts at gnome dot org.http://www.bitstream.com##X!#/{{RfoZ!7={fs +bb#3N{T\q#w!`j#fy```{w`{{RNNfffHF?{L'5oojo{-{{=foD7f}s@֚ԴG}%2і}2»}Y&Y@&//2G@GddkY }:Y :  V f   XA W%d]%]@%XA~XA~}|d{dzy@}xwvutst2srqpq(ponmlkldkjkji hgf f f@ed.ed.cXAcbY ba`a`_]``_[%_]_@^]\[%\[%ZY ZY XW%XAWVW%VUTSRSRQPONBNSMLxKJ}KJ}IHGDCB2A@BAF@?-@B?>@?->=<;:9S:98(9S8(76543210/+ /.d-,+ ,+ *)('-'&%%2$#"!-!1 d 2 @:%%:B-B   :    @$--:-  d++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++, %Id@QX Y!-,%Id@QX Y!-,  P y PXY%%# P y PXY%-,KPX EDY!-,%E`D-,KSX%%EDY!!-,ED-hh@ O/10!%!!hsr)s` Y@.   :TSQ 991/0KSX9Y"3#3#ʁX2s2qeR@UQ1<20###Ѯ++P@5 X X    91/<2<<22<<220#333!!!!###!7!!7! Siih'T'hhii'T'haabbN; 0l@@()%/0%W$ZXWZX!0 !()/ %$, 19991/29999902654&'#&&'7&&54$773&&'##uV^PwTe;__#QhZ 0d/UH AYQ LEtNUcEJ-.+J zk{;_hǼA|9#*zLq,_AGB͋fqGG֯!g4l7&&s^/Iuw96Q10#+u @hg   99910#&5;32267654&#"P77ON87PsTTߔürUUݓþP/E]bkP0F\g7PP78NOEEg`f__{ T@* :ccQc   99991/20KSX2Y"7!7%3!!9#`5!ɪuLJժf^@1: q cac99991/29990KSX9Y"%!!7754&#"76632  n!7'r\~)paGR!#]jCD12›`g*N(J@+c W q cW qc#ae) )  & )99190!"&'732654&##732654&#"76632rl^&[b {Rq%{Tس~%%45uy|d_((!? s@>      : cQ     991/<290KSXY"!33##!7JٜFE$3dT j@8  : cWbccQe!   !999190KSX9Y"!!6632#"&'732654&#"`!H/Y,i`XlH)T^OKժ|VPP!32ꮀ(&\h *;@ ccr W bc(ae+ "+9104&#"32&&#"6632#"&5476$32\tkzk $:K7DqPDNzNDb1KRqzy '%'fiǸwSa`צf5@:cQ91/0KSXY"!#! <^+)f #/?@" c* c$cae*t0 '-!09919904&#"326&&5432#"&546"32654&5vpykl(ֲ ~uf{xtŚi~&i)̳`|cr}[l?L *3@cr c( ca(e+"+91032654&#"732#"&54676632#"&Jtkxk#:K7DqPCNzNDbKqzzٺ%'fiȹvS`a֦X#%O@):nun91/0KSXY"3#3#'54&#"3267# 476$32}eX{c3QT_TX9xC-BS|n& Ә`nїap}o?D9H=+#4~}՝{=ѰM/ v@B      :cyQ    91/<90KSXY"!3#!##-+{ u@@       :c cQc z   !9991/90KSX99Y"32654&#32654&#%!2!!mkÈ{Xo}bи}<ݮkff>^T|s2@|{c|{c ae  99910%#"5476$32&&#"3267T[rkPK)@P_GhUX5)) |*(?>QS{@=j P@(   : c Qc   99991/0KSX99Y"%267654&## !!DD6ErgTGh#}s?w/5 X@0    :ccQcz   991/0KSXY"!!!!!!Xu!TV rh!wժF\ S@,    :ccQz  991/0KSXY"!!!!#o!\Vd ժH7N)a@3&))#$"%):%&"&c("c|{cae*('&%)*9991990KSX9Y"%#"54676676$32&&#"3267#7!Wu! #^8kPM)AQbD,N"#Dl%L`>? ZouC|*(?>SQ4arQ%# {@B     :czQ   9991/<20KSXY"3!3#!#v)vʋՉd+99 ?@"  : cQc   91/220KSXY"!!!!7!!Z>!:!!9ժNU@.  : {cc Qe  991990KSX9Y"'73267!7!#"&-Hg(!H8a=OSD.J j@;      :}   991/<290KSXY"33##ApyN 8@:cQ9991/0KSXY"3!!q!eժ  @D     : }   99991/<290KSXY"]@&  ]]!!# ## nݺNs +/ @<  :}  9991/<2990KSXY"]@  )]!3!#3+3R)!@c c'ae**10%267654&#"#"&4676676632U/J\inU/L\i-+C'Xڑ++C'XڑÇf_f_hun\;yo\;y3h@6    :cc Q   99991/0KSX999Y"32654&#%!2!##m|mu/ϿlgR-3@c%c ae.+"+.991990#"&4676676632267654&#" ++C'Xڑ@%6OU/J\inU/L\io\;ySotC_!\f_f_h @R       :c c Q      9999991/<9990KSX999Y"#&&###!232654&#HW6yj,mn{!)fۣ~jhybճWpi{'@:    : |qc|qc%ae(  "(9999190KSX99Y"8]@<'''))))777  (((%%%%88VVVV]].#"!"&'732654&/.54!2{'Q]byhd)bpXeJ P;<GX('2--EBRZ)!:y(5@@:cQ99991/20KSXY"!!#!t!++ժ+P~@K   : ceQ991290KSX99Y"332676673#"&546s˳mnY|--˲*_NGj=h ;\\9>h?8;%~+J@':}91/290KSXY"%3!3V1ƾ+R` @J        : }    91/<<90KSXY" ]@\  ;u      )%& 86736 FFCE ZVTU kfch e zvvy u   %]]333##ZIyF +f9 ~@H      : }    91/<290KSXY"33##D1dDf@5:} 9991/290KSXY"33#σub 9@:cQc   91/0KSXY"!!!7!1-" )՚oG@":~~g9910KSXY"!#3!mVdB0@:Q910KSXY"#3ժǪC@ :~~g9910KSXY"!73#7X+ޏH@ Q91290##Ȳu-m/10!5mPPPf2@ 910K TKT[X@878Y#ŏfxH?{ .@a! "   : %  &W%")e,% &,/99991/99990KSX99999Y"']@65$0%0&5'  ' ]]"326?#7#"&54$!37>54&#"7>32wt+DRgX&} Hn5 ~uVo#lY 3{IScԹ)L^e= U[33'' h;T%@L$#  $#"$# !#$#$#%$$#:! e$""#%!#$ $#&999991/990KSX9999Y"%2676654&#"6632#"&'#3T.19eeS30:k>fRHL|b"/ZV^l\XVo{RTVϺlsVTq{4@WZWZ e   99910%#"&5476632&&#"3267IZk[]߅YO%BUn<8?`S3')ܕbcb++:6YYRh>?w%@L  $%#: # e# &999991/990KSX9999Y""32676654&3#7#"&5476632X.16feU018msѸAdPEK|aWWZsZXWrxCTVҼ!lvxWbf} &\@" W&Y#  e'  &  '999910@h hooooo ooooo ]]>54&#"#"&547>32!3267t{,uigxjSm Xr#x++֜,mVZ"eN 498j@9     :       999991/22990KSXY"#"!!#!7!7663_`)&Sec/яN;Hy{.@[ ... ..-.)*+(,..: , W  ) -/-,. .#/999199990KSX99999Y"%2676654&#"#"&'732677#"&547663273O.17pdr5RK%IP%:nPHJoe"ZTZjwwI,.uW[׸jp|aUTH@R    :   99991/<990KSX999Y"#67654&#"#366323]W!{/tKb J G'QWab ^= h@7   :      99991/20KSXY"!!!7!!3#f׾mnm-`/BCV r@@  :     91990KSX9Y"!7!##73263#)ʵ_nJ-wZ {@:!!     :   991/<90KSXY"w]v}p]]33 ##%uͨXZFF?7]@/:    99999991/990KSX9Y"33#"&5467!7!XY[ϭ #6IHP.{+@e    $%"%#""%:!  &$ )$" $"%  &!"% # %" ,.999999999991/<<<299990KSX99Y"K TX,,,@878Y>32#>54&#"#>54&#"#3>32'}MVg}.3KT+}}/1JQ-}٨,o@K\DIm\6kw\u?8aq=:w``3B5ovD/`>}m`@Q     :  e   99991/2990KSX999Y"332673#7#"&546]U!{ڸ!Ii J%DOUyba~']`J@'!!!!:91/290KSXY"33#`D\#` @J      !!! ! :     91/<<90KSXY" ]@f   * J       *$ :4 GCD ZZ T kfh c yxy u   !]]333##\ 1j<צ`wBo` ~@H!! ! !  !  !!!:    91/<290KSXY" ##3>_4q`DT uV`~@F!  !!!!!:     99129990KSX9Y"##73267733>g6:`lQu<+ٷhkILJ]qTN^Nm` 7@!!:  91/0KSXY"!!!7!?.!u!`ۓ%4@f -.-..-    : 5'- )'.)~)~'~g5)(54.  $-5999199999990KSX9999Y"#"33#"&5467767654&##732677667663N^3!xoIP/VaNH- bq>=~2H6/OgK@&!3?9m{D% 2-UKn{'"10#4@e -.-..-    :-5)  )'.)~'~)~g54.  5)('*(5.99199999990KSX9999Y"732677667&&546776654&##73233#"#N^3!xpJP/UbMJ- bp>= /H6/OfLA&"3@9l{ D% 1-VJn|'"Xy &@ ll 1<2990#"'&'&&#"56632326yKOZq Mg3OINS5dJ t]F ;73 !;?<6 7=LN'$3um !@Q   !! !!  !:  c  [ y   " ""!"99991/<9990KSXY"4&#"326!&&54632#!#Y?@WW@?Y33srPF#Z?YWA?XX`#kErrQ {su'&h5k'(`u^'1/uRN'2+uPN'87uHjf'DH?f'DCH?f'DH?'DHZ7'DH?N'Duq{'Fwbff'Hbff'HCbff'Hbf'H=If'=f'C=f'='TV7'Quff'RuZf'RCuZf'RuZ'RuZ7'R}mf'X}mf'XC}mf'X}m'X;o \@0: Q   99991<20KSXY"3!!#!7!Roٮo\]+u @ a#$# 102#"&546"32654&hAu,-/OomOPqp1.-rBoPPlnNOp{?$V@3WY WY  e %  "" %912<990&&'667#&&5476673? 1{HAK!K?7f7̢FV8f7<}u5&,-(!úD`16"+`႙@F  :  Wqa c  9991/2<2990KSX9Y"&&#"!!!!73#737$32%6y?&+rZ 3!Z/3<+-ُ/яd7-4Bv@E$%B<;5& %, XX2aC$%5 "<%&,/B;"%/"%8%/?%)C9999919990&&#"#"&'732654/&&5467&&546326654&''-!G:gV|Ur))FS!J=p J}Pv)%GTXkPRVU~&(bHOZ ;VuEe5#Z7$'^Nb| 3UoI`?%P53o75p^34p73`S?!   & 104632#"&?}|}||{|};u g@0  :Q  99991290KSX9Y"!###&&54/эZfN7f5@[#$# $$#45512035:3*$ #0W0e445$'# 35'*-  '' '-56999999991/990KSX999Y"6632#"&'732654&''&&54676654&#"#*˰ )<3sQGA;G6U=Z8˲h\yۻqϠ!F$`&=,'UW !}l4R?/D\?$OY}N4L@I  . . :   A)5)M  -- ,G(#);(#*//29999129990KSX9Y"2#'&&####32654&2#"&'&&546766"32676654&'&&XXP:&rk1=-7ffZJJDZZ\[[[~}[[[\ZZ~jKKMMKLijLLKLKKLbeG]C;P*T6?>5VZZ[~}[[[[[[}~[ZZgJKKjhKLLLLLijKKJ}N1ID@'  &>>2J- /,(8 (8*D/210&&#"3267#"&54632'"32676654&'&&'2#"&'&&546766`:o:u8g24r=г=rjKKMMKLijLLKLKKkZZ\[[[~}[[[\ZZ/lhȬJKKjhKLLLLLijKKJgZZ[~}[[[[[[}~[ZZfh@6    :   Q  1 1010/91<<2<<90KSXY"###5!3###r}r7q^^-B?ff/10K TKT[X@878Y3#ufF-n@99991<20@@@@@PPPP]K TX@878YK TX@878Y3#%3#''X%y<@ l  l  <291<2<<990!3!!!'7#5!7!X}y}J;fժhӬo@P   :c ccQcz  91/<20KSXY"!!!!!!#!!V3!jd!K\5ժFժ 8o@;)9$863 '$*73(c$c3a$e9)9(*-7  '-6 8 -999999999199999990%3267654&'&&#"#"&''7&&54766327B^Bp=26yf[8q<2:$iSXQ6^cwhTXM6Td,2'{#)u'+l <[Z..@:V++}@) /7@$ '! -0 $22*099991<2<299032654&#"&&#"32676632#"&'#"&546329[=G[TFBi8\=G[SDCj~/[w~SNAU}^sdlkutcjmvu۠d|k֥sXy.@ l l   <2<21/<<07!!!!#!5!X!dCDLIVw? (@l  <2291/905!5w!!LK Xy? (@l  <<291/90%%5!!X#!!V/@]  :    Q    9991/2<2<290KSXY"!#!7!7'!73333!! wbbs7ln   o#o1ho#V`&@\   &#$"% :%   c"e '&'  %'99912<99999990KSX999Y"3326733267#"&5#"&''-mf! (J!CO5b\| mV H6[a  UIRLSS-)6@'! '!* $$*99919906654&#"#"&54632#"&54324&#"32JIH7$$0e՘ݢe WOmVPmmWKt,>bFأ[t}t{L= -@     <91990!!55!!LR%# Չ\P_L9@ LL120!#!L^PL?@!   544391/2299990!#3267#"&5!##P117,#J%q\TL@H? HL%&@  & MNM&1026732#"&'&&#"#"&54632j Pd@7*9  kOeD=!0 l9TA6?&#Hn!bSA8?T  +/o@?  .,#", &a0 .- )/,0 #"6)6099999919990"326?#7#"&5463347654&#"76632!!ٔI@#<<=: ;=:7 991/<20353&5323!5654#"J{n !o{1xx 7oȼ߅LI L{ OP@'O'OO'  ';:987<6'5':+ $!F@6?$O4%W$!(@W?<IC.(ePOL?965F$%+4@L'1P999999912<2<29999990KSX9999Y"@`>#>$>%0?0@Z#Z$Z%Z&ooollloooooj#j$j%j2o3o4o5oNoO{#{${%{&#$%&%9#9$9%2?2@I#I$I%I&j3 ]]7#"3267>54&#"!3267#"&'#"&546;7>54&#"7>32>321}i##N;Ze=D;SjaUF5";Hj/]uUM1L#O8Rv2PyH'!b3C'\/ '1m@:'2"1( +%&""+"e2'2& 1(.%.  . 299999999199999990324&&&54766327#"&''&&#"N4PKQߓNx.p]{ RJOJ{3w\%I49W.`0Pnwu$$M2Tmvv%%L'!@J !!!:S WY ce Q"  ! ! "99919990KSX9Y"33267#"&54677667#73PojUGodOj%lebhT=1D]]YHo8JRGB98~^lVFfVs ^@/ :SQ 99991/0KSX9Y"#73#3B1ˁX1+eXsy^@ l10!#!X!^?; /@     99190'%3##d)#Ӕ/}b%9V#@NAA  A A:   XX!$  /912299990KSX99Y"&&#"!!#"&'53267#5!6632$R,fs-/d+Ǻ9f.1d0`yu1Ɣ1c|ɏ!JX1y7K@&' 10+5  ll* l5'l.810*8<2991<2<<99990#"'&'&&#"56632326#"'&'&&#"566323326yKOZq Mg3OINS5dK t]FJKOZq Sc1NJOR` t]Dï;73 ";@<7  6<а;83 $77 7=O@*BBBB:91/90KSXY"#3 !qJR# 3@  u  CC 991<299077R+#}R+#}#RRRZ# 3@  u   CC991<299077777R+^#T+^# ^R^ ^R)/ w@<    :n    999991/<<220KSXY"3#3#3#<o;;///k'$3u^'$3uR^'2+u#H"b@7!"!"!"!""!:c c Q c"!    #91/220KSXY"%!"&5476$3!!!!"33P!B5VR!WH!k!v/1}{>᪪ђlêFlm7~j=xz{,8P@\-'@?6758'@@?  '@? '?@?:8/-59 - E5- W 5*eQB8/.- N N2'N'!Q9999912<99999990KSX99Y"o.]@oo o o jjo-i/o8 ]>32!3267#"&'#"&5467>7>32!7>54&#"267>7>54&#":Pz aUF6#;G_":Q;'8uP|4IB=Sk7<`(HCA_(J?@+TZ[nNX:2)+E@AD6NyGfkDVL8GNGB,jl MTEF5`V$QWy@ 9910!!/yy@ 9910!!/y e@3     :o g     991<20KSX99Y"#73#73& '~~y _@0     : og     91<20KSX99Y"3#%3#))9@:og910KSX9Y"#73)~5@:og10KSX9Y"3#)Xyo '@l D <<103#3#!!j!u#\u"@91990 h 9%-V'\DN'<9uB#@ :Q10KSXY"3TmLB /}@B (-  * -'! ) -0)$ !'$* EFE( $02299999999912299999999904&#"3267'#"&''7&&5467'76632d|[Z}}Z[|Z^.[20`0\^.[3.^Z{{Z\}~t]1]02[-^Z3].2]-_5m#@uC91907m+!{#R=s#@uC9190%77=+_# ^Rb@`   :        2999991/<2<2990KSX9Y"3#'#"!#!##737663/-J__۸%&Sec/яNb@K   :     99991/<22990KSX9Y"!#!"!!##73766Ѹ__)پ&{Sec/яN;;o@L       :   Q   999122220KSXY"%!#!7!!7!3!!!RPojoRRohn\\/`/@:n910KSXY"3#;`/5@:on10KSX9Y"3#)// _@0     : on     91<20KSX99Y"3#%3#''/'3?Km@<%1= 1%+\C7IF:4(:"FG4"@ "G""".G"@(/99991/<22299990'32654&#"4632#"&32654&#"4632#"&32654&#"4632#"&H%'H_EDbcCE_yxxwyLaEEacCEayyxxy aEF`bDEayyxxy7a`JGacECcaEyxyEaaECcaExxy"GaaGCcaExxy3m'$3u5m'(`uEk'$3u5N'(`u5k'(`u9k',9u9m',9u9N',9u9k',9uRk'2+uRm'2+uRk'2+uPk'87uPm'87uPk'87u=` C@":   991/20KSXY"!!!7!!f׾mnm`/BfR@91290K TKT[X@878YK TX@878Y3#'#͓ufV7@!   91<99990K TK T[X@878Y@=     ]K TK T[X@878Y@           ]'.#"#>3232673#"&/#*/}rY+D&/ )1}qY,B`1KN&/NMb+@ 9910!!V)JHn@ H H91<90K TX@878Y@   ]K TX@878Y332673#"&546xWUPjw HADJLvwDm;@ 9910K TX@878Y@ @@PP]3#))N h@ " I"10K TK T[K T[X@878YKTKT[KT[KT[X@878Y4&#"3267#"&54632X@@WW@@X{ssss;@XWA@WX?sstus&@    991/90!#"&'732654&'+%#w,^/(I CK@]$]m<6N6fe@ 1<20KTK T[X@878Y@)////2222BBBBVSUU]3#3#҇LfxuX"@    9991/90!33267#"&546w=@/-!B "G"alK:e&%& PG7xNf:@91<90K TKT[X@878Y#373wx e@8    : Qc     .99991/90KSXY"3%!!'%sr)Iqq dqJdϨ3hdXZ@K:   99991/990KSX9Y"33#"&5467'!7!%XY[ϭ ;Jss;JX%6IH#O(c$)b{m'69usNf'Vm'=9uNmf']@ <210##  jk@;  : cQc    99991/220KSXY"%267654&##!! !!#73DD6Er[ kgTGhщ{}s?+/ŕ{wj!0@L:! %%+ e1( .(!. ( 19999199990KSX9Y"#"&54676632&&''%'3%&&#"324&f{qKFMLGL0 5)h%P,sh/֗htx~io{&V:^\VȑX\ xA@eDk'<9uVf'\3 @I   :c cQ   999991/0KSX9999Y"#32!32654&#FH#3`i|m縘gbVh%@M%$#" ! :e&& &.9991990KSX9999Y"%#36632#"&72676654&#"%or=dOFKzdQ-18gfU018pRXлnvzTHYU\qZXXr{X-yl10!!X!ת;T .@     <91<2907^t^_t\t%\^u^uw^\ X@. JJ:  `     99120KSX2Y"37733!qu c)t'+n3j@5:  aK999999199990KSX99Y"!!?$54&#"76632!9J?3zST}5vrni3=%)o^(H@*  #a)   KK& )99190#"&'732654&##732654&#"76632HNЯB;Ch2pUYHHavVV*e<Gz7yr\ [Gy\NC@jP@33sd[Ot/m{''  V/y{'' V/m''  VNm'* 9u;HyH'J9P', 9uu{'6su5{'Vsk'&uf'FZsm'&uf'FZw-@|  "! #+,-+,-*: + "e+ %.999991/<2990KSX99999Y""32676654&!7!733##7#"&5476632X.16feU018m>IAdPEK|aWWZsZXWrx5yyTVҼ!lvxW/Z9910!!P !/`/@:n910KSXY"3#;`-|@C$%|{c!|{ c '+a!e.'.-,+($$ &,$.9999999991<22299032&&#"!!!!3267#"5#73667#7]LJH'FGG28 /LU'PV1 1!*(A3232673#"&"*&"6}#|U">##$(8 } ~\79 32mn 6.mn=Y910@//]]KTX@878YK TX@878Y#]@ 91<90@/// ]K TX@878YKTX@878Y3#'#՞ј5c@ 91290@//// ]K TX@878YKTX@878Y#373-՞ї /mf@ 9910'F#ll t@> JJ J  J: `   991<290KSXY"33##7!7!iuw!#~ oy}y5 3@  HH120KTX@878Y332673#"&rOKWky!x=68;qym`:@ 9910@ OO__]KTX@878Y3#'  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~>: ~1BSax~ & 0 : !""""+"H"e%  0AR^x}  0 9 !""""+"H"`%^ChVjq_8 (Bbcdefghjikmlnoqprsutvwxzy{}|~hsR!uX/bb{\)?XXXXFs5\N9NR3R PRdHH;wb;T=Z7Tubs}\NXs5RPHHHHHHbbbb====Tuuuuu}}}}+{d?}7?Xo)XVX/PJ/X;XJRR#Xu5=bb;5559999RRRPPP=wXsNw3X\3///N;9sssw/?/m+   !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~      sfthyphenperiodcenteredEuroc6459c6460c6461c6468c6470c6472c6477c6478c6475c6476""""gt]`}\ > . C =  o t!B{ j"G~kZPv"F ],$ !V!c!p!}!!!!!!!!!!" ""&"3"@"M"Z"g"t""""""""#%#`#$?$$%O%&'4'''(A()E))* *:*+K+++,?,--b-./[/0070h01_1122m2m2z2233344_445525\5i5v566@6c67W7788`9999,999F9S9`9m9z999999::=::;A;l;;0>>>?K??@@b@A!A2ACATAaAnA{AAAAAABzBBC=CCDhDDE&EAEEE QK@moJ GBGSf    JBits mB`#cVeraSansMonoOb6628I00@             [_<bomVeraMono.ttf000066400000000000000000001401101300200146000331510ustar00rootroot00000000000000connectome-workbench-1.2.3+git41-gc4c6c90/src/Resources/GeneralResources/Fonts/VeraFontsOS/2_*,VPCLT!?6cmapXcvt  0fpgmp((Tgasp glyf(thdmx32`Hheadم'6hhea ?$hmtx ε`4locamaxp name` postn]4prep:!8::h:: mR0u   y t  0   t 0  0   ~ 6 H `   0 & Copyright (c) 2003 by Bitstream, Inc. All Rights Reserved.Bitstream Vera Sans MonoBitstreamVeraSansMono-RomanRelease 1.10Copyright (c) 2003 by Bitstream, Inc. All Rights Reserved. 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 the Font Software, including without limitation the rights to use, copy, merge, publish, distribute, and/or sell copies of the Font Software, and to permit persons to whom the Font Software is furnished to do so, subject to the following conditions: The above copyright and trademark notices and this permission notice shall be included in all copies of one or more of the Font Software typefaces. The Font Software may be modified, altered, or added to, and in particular the designs of glyphs or characters in the Fonts may be modified and additional glyphs or characters may be added to the Fonts, only if the fonts are renamed to names not containing either the words "Bitstream" or the word "Vera". This License becomes null and void to the extent applicable to Fonts or Font Software that has been modified and is distributed under the "Bitstream Vera" names. The Font Software may be sold as part of a larger software package but no copy of one or more of the Font Software typefaces may be sold by itself. THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL BITSTREAM OR THE GNOME FOUNDATION BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL 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. Except as contained in this notice, the names of Gnome, the Gnome Foundation, and Bitstream Inc., shall not be used in advertising or otherwise to promote the sale, use or other dealings in this Font Software without prior written authorization from the Gnome Foundation or Bitstream Inc., respectively. For further information, contact: fonts at gnome dot org.http://www.bitstream.comCopyright (c) 2003 by Bitstream, Inc. All Rights Reserved.Bitstream Vera Sans MonoBitstreamVeraSansMono-RomanRelease 1.10Copyright (c) 2003 by Bitstream, Inc. All Rights Reserved. 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 the Font Software, including without limitation the rights to use, copy, merge, publish, distribute, and/or sell copies of the Font Software, and to permit persons to whom the Font Software is furnished to do so, subject to the following conditions: The above copyright and trademark notices and this permission notice shall be included in all copies of one or more of the Font Software typefaces. The Font Software may be modified, altered, or added to, and in particular the designs of glyphs or characters in the Fonts may be modified and additional glyphs or characters may be added to the Fonts, only if the fonts are renamed to names not containing either the words "Bitstream" or the word "Vera". This License becomes null and void to the extent applicable to Fonts or Font Software that has been modified and is distributed under the "Bitstream Vera" names. The Font Software may be sold as part of a larger software package but no copy of one or more of the Font Software typefaces may be sold by itself. THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL BITSTREAM OR THE GNOME FOUNDATION BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL 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. Except as contained in this notice, the names of Gnome, the Gnome Foundation, and Bitstream Inc., shall not be used in advertising or otherwise to promote the sale, use or other dealings in this Font Software without prior written authorization from the Gnome Foundation or Bitstream Inc., respectively. For further information, contact: fonts at gnome dot org.http://www.bitstream.comf 7X!uu9!{Xm{o{RoZ!=fs +b#1N {T\q#w`j#fy```{{w`b{!{RNNfffHF?{L'oo5jo{-{3=foD7f}s]A GA% } % 2  %%@Y}2}Y&Y@&//2G@Gddkߖږ؍ }:Ս :  ϊ̖ˋ%}Ś   ]%]@%AA dd@2(-}-d   ..A]%]@%%%A  %d%BSx~}~}}|{zwvut uu@t tss@rqponSonm(nSm(lk2ji2hgfedcbcbba`_^Z ^]d\[Z [Z YXWVUU2TSRQ}PONM-MLK(JIJ7ICIHEHGCGdFEFEDCD7CBCC@@ BABB@ A@AA@ @? @@@ ? ? ?@@d>=-=<;(:9B9d818K76-65K404K3032B21-10/-/. .-,--@ ,,,@@+*%+* *%):)('&%B%E$#""! -!} -KBBF-B-B-B@  @   @    @  @7    -:-:-d++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++, %Id@QX Y!-,%Id@QX Y!-,  P y PXY%%# P y PXY%-,KPX EDY!-,%E`D-,KSX%%EDY!!-,ED-hh/10!%!!hsr) @ <2991/03#3#qeR@1<20###Ѯ++J@0     91/<<<<<<<2220333!3!###!5!!5!#3hiiThiih)T/hTaabbNZ /d@9($)%/%$(!$, ( 0<2<1/2999906654&'#&&'5&&546753&&'n|phumdfbdcӿdOTUPDNtd]gp^Vd-.)>B+/Qš ! *9V@/7(" "7(.+  % 4  + :99991/9999032654&#"4632#"&'%32654&#"4632#"&iNMklLNi@s..2H#)iOMllMMk@u--1?NjkMMljO0./t?``OikMMkjN0--uA9*7@b  -,.+2345617B7 1 +"1"!% (! 7+!(!(! .899999991/9990KSX999Y"6654''3#'#"5467&&54632&&#"3267667#'&JKNSj抋20ǭAF;}Eap:6\[ț*\,#1h F'XmFD ̉dHG'%[M;I\ 10#+u @  29910#&547u;:\' @  299103#654\<<J+N@,       <2<2991<22990%#'%%73%+f9s9f9PsPbybcyXqy '@    <<1<<0!!#!5!CDDD/@ 103#Śc/dm10!!d 11/03#1fB7@ 103#ymL # @  $!"!$10@////////// / / ?????????? ? ? OOOO O ____ _    F////////// / / __________ _ _  $]]4632#"&"32'2#"M68PO98K7PP78NL0670xx~F &@ ## 1/20%!5%3!!:P6ȪuLJժ#Q@)%%B   "$91/20KSX92Y"%!!5676654&#"56632uu5dF[pga Yd8ժ.>zO}BC12`tA7(G@)  #)&" )9190#"&'532654&##532654&#"56632ggfbYhyI'Ǖ&$54zms{(( ۵{fo B@   B    $<291/<290KSXY"!33##!5)!3d-=@"   "190!!6632#"&'532654&#"+W,wN\aQFժ 21%%L$=@#  %"& "%190&&#"6632# !2"32654&?M0n#J݁%'dkuzl75@%%B"991/0KSXY"!#!5V+N #/C@% '-'0 $*$ "!0991990"32654&%&&54632#"$54632654&#"hʁ򑁖Myz{yŗT!Ѳ!!ȟɠbx~~xzF $;@" ""%"  &%1902654&#"532#"543 !"&T?M/nI%'!dk  os'@ <21/03#3#'9' %@  103#3#Śc /Xyw!@('29190 5yR!÷X`y@ <210!!!!X!! BXyw!@('<919055X!R^^="{@B  %%B !    ) #99991/9990KSX99Y"#546776654&#"566323#=TZ>/mNb^hC^XE&bRY;X1YnED98L\VBT=/s 4p@1(+$ 4 '$+1+5' ( + . !+ -.5<991999990@ ]4&#"326#5#"&5463254&#"!267# !2kkkk%RӡP$J6l90?{:]x<!o?DF=?z% @A%%%% % % %  % B   / 91/<90KSXY"]@    ]]!3#!#hnl#+{q =@#   21 0!29991/9032654&#32654&#%!2!!qﰖ뒃JF{f>p}qdƵϠ1.@  2 10210%# !2&&#"32671M[?[MJVXI5))pn))=@@=R(@  2 1099991/0%26&!# !!`dVDѦHKw/N )@  13 21/0!!!!!!vTrwժFX $@14 21/0!!!!#o\eժH7fP<@!   6251990%# !2&&#"3267#5!PQv@^PQ_ſCe){KMon56MI!H &@ 1 0 221/<203!3#!#)d+9 %@ 77 221/220!!!!5!!=99ժm,@    51990753265!5!#"&m[hqG`=QQD, @!% %B  0 291/<290KSXY"]@L&&6FUWX dzy{ ',+&:IGG[WXXWkzx]]33 ##wVhs@ 141/03!!dժVy @,  B    / 0 91/<290KSXY" ]@$  &)&) 6968  ]]! !###V+'F m@B10 991/<2990KSXY"]@&)&8Wdjuz &)FIWgh]]!3!#3+3u\ #@ 2 62510#"32#"32IIz~u+@  2 8 3291/032654&#%!2###ꌝL/ϔu\=@   2 625999919990"#"32#"32ȗyHdIj@8  %%B     21  0299991/<9990KSX9Y"#&&###!232654&#NnRٲM{cݑohy]ҔYJ'@=  %  %B %( &919"0(9999190KSX99Y"]@ ]].#"#"&'532654&/.54$32\^mjikshulм V;9L@)%%%%B/091/290KSXY"%3#3h_KKѪ++ @D    %%% % B    /91/<<90KSXY" ]@^ //+ ??8 ZZ  &*%*(+ % & 5:5:;: 4 6 TTZXWV[[RW X ] gh yvy v #]]333# #ŏӬ߿ʿD"+w @K % % % %%%% % B   ;/; 0 91/<290KSXY"7]@8  '()& X xyw !%+% 5UYX es]]3 3 # #VHNAu3B}%Y@.%%%%B<< 9991/290KSXY"33#%lk!mb E@%%B/0 991/0KSXY"]]!!!5!" ՚ow@=210!#3!XfB7@ 10#%mZ@=210!53#5XޏH@ 91290##Ȳu-m/10!5mPPf1@ <10K TKT[X@878Y #fx#{ )n@*  ! $   D >*22991/99990@00 0!0" ]#"326757#5#"&546;5.#"5>32=zl;^[fX=& 3qpepӺ)Ld_y64''RR2X 0@  G F221/9904&#"3266632#"&'#3,fd./xRXWS%{/@   F210%# !2&&#"3267%JR%QNI]`A9++88*,A:;>{0@G H221/9903#5#"3232654&#"Z.deCSW;7W {X{E@&    IH991990!3267# 32&&#"X㿮Xmi[ ^Z89++9 @Ţ'4@     <<2991/22990#"!!#!5!5463'cM+Qgc/яN{H{ )H@' '  $(*' G!H*221999904&#"326#"&'5326=#"3253ZLSbC,ml/9.,}^\::VZ,@  J  F21/<990#4&#"#36632jq1sJ`cD .@ L LK <<1/20!!!5!!3#mnm`/BCV 8@   <2991990!5!##53263#XZZӜ} @:  B  DE 291/<90KSXY"]@R546Ffuv ('(;;797JIYYkiiiyxyy]]33 ##Gb{ZFB?  &@   L 991/99033#"&5!5![Y饵|~mo{"@'  MNMNME#<91/<<<299990@G000000 0 0 ????????? #]K TKT[KT[X#@##878YKTX###@878Y>32#4&#"#4&#"#3>32"iJo5FP;9JI9!c?LeHEws{p{``10"32654&'2#"hڜ-.VT{3@ GF221990%#36632#"&4&#"326w.df SWWRw 3@   G>22199032654&#"#"3253#L-ed.+SY7:WSj{O@   21/990@%  0030@@C@PPPP].#"#3>32;zI.Dv6y.*`w"${'u@@    B %( OI"E(99991990KSX99Y"&&#"#"&'532654/&&54632OS}{\JSjgTzEZ9..QSKJ#}##55cY1!1@  <<2991/<2990!!33#"&5!5!f^^uϪ+|b`>^,@    JF21/2990332653#5#"&økp1qJyaddm`e@)BIE91/290KSXY"']@%]]3 3#dEFr`T` @E      B    /91/<<90KSXY" ]@      &&)&))#, 96993< EI F J VX W Y fifij e vzx| r -   ++>>< H Y jih {yz|  ]]333# #àö`wBfL` @H      B  IE 91/<290KSXY" ]@ fivy  :4 ZV ]] # # 3 ^oo)'`?HkhV`@E       B   IE9129990KSX9Y"8]@v  &&8IIY ]]+532673 3Z.Gc".\mQ\GOLGhu:NN^Nlb X@BIE 2991/0KSXY"8]@68EJWXejuz ]!!!5!-}bܖ%$f@5 %   !  % $  = %<<29999999199999990#"&554&##5326554633#"3@k>>j@FU[noZUtrݓWV10#$j@7%   #%#= %<2<999999919999999032655467&&554&##53233#"##DVZon[VD>k@@k>XXrtݔXy &@  '1<2990#"'&'&&#"56632326yKOZq Mg3OINS5dJ t]F ;73 !;?<6 7=%N'$u%m !@W % %%% %!%! %!! % !B     !  PPK/K!"2299999991/<9990KSXY"]@  ]]4&#"326!.54632#!#Y?@WX??Y:Arr@;nlZ?YWA?XXP!yIrrIv${u1'&dNk'(uF^'1uu\N'2u=N'8u#f'D#f'DC#f'D#'D#7'D#'Du%{'Fh{Xf'H{Xf'HC{Xf'H{X'HDf'Df'CDf'D'7'QHf'RHf'RCHf'RH'RH7'Rf'Xf'XCf'X'X;/ '@  RQ R <<1<203!!#!5!nn\]+u @ STS 102#"&546"32654&hAu,-/OomOPqp1.-rBoPPlnNOp%!N@*   " E"<<<2212<990&&'667#&5473%C??BI9gg9ބ5(,-("9="+` 츸X>@     <<1/2<2990&&#"!!!!53#5354632D>Cs3A,,ُ/я= 2>j@<#$93 $*0?#=><''PGZsweZ54m@''TLf{xf[1,pE-Z/L-Z/L?!   V 104632#"&?}|}||{|j; #@WW1 9120!###&&54$FfNݸ}/V@1-'!  **.  !' $'$-DF099991/9904632#"&'532654&''&&5467&&#"#7C:oEBL;lAxC\[yqyrq|d1M*%]taQG_J'8O#kr}N4L@I  ] ] B   A)5)M  \\ [G#X;#Y//29999129990KSX9Y"2#'&&####32654&2#"&'&&546766"32676654&'&&XXP:&rk1=-7ffZJJDZZ\[[[~}[[[\ZZ~jKKMMKLijLLKLKKLbeG]C;P*T6?>5VZZ[~}[[[[[[}~[ZZgJKKjhKLLLLLijKKJ}N1ID@'  &>>2J\ ^,8 8YD/210&&#"3267#"&54632'"32676654&'&&'2#"&'&&546766`:o:u8g24r=г=rjKKMMKLijLLKLKKkZZ\[[[~}[[[\ZZ/lhȬJKKjhKLLLLLijKKJgZZ[~}[[[[[[}~[ZZfh@6    B     ` `_`_/91<<2<<90KSXY"###5!3###r}r7q^^-Bf1@ <10KTK T[X@878Y3#f?F\@aa1<20K TK T[KT[X@878YK TKT[X@878Y3#%3#?X%y<@     <291<2<<990!3!!!'7#5!7!X}y}J;fժhӬg@7 % %%%B    c /<291/<20KSXY"!!!!!!#!3eex5ժFժ +k@:+)&  *&&, #* #)+262#5,999999991/9999990324&'&&#"#"&''7&5327sT sV)+y=g %s9d/NZIn-QUPeQzQQFIRPJ) /7@$ '! -0 $dd*099991<2<299032654&#"&&#"32676632#"&'#"&546329[=G[TFBi8\=G[SDCj~/[w~SNAU}^sdlkutcjmvu۠d|k֥sXy.@    <2<21/<<07!!!!#!5!X!dCDLIVw? (@( ' <2291/905!5w!!LK Xy? (@ (' <<291/90%%5!!X#!!V%@D% % %%B  < e e<<2<299991/2<2<290KSXY"333!!!#!5!5'!53%lkVoqZmo#o o#oT` L@*  !   JF!99912<9903326533267#"&'#"&'øxo ! &D">K .Y\,T H;8 OOPNLP-):@'! '! @ * $$*99919906654&#"#"&54632#"&54324&#"32JIH7$$0e՘ݢe WOmVPmmWKt,>bFأ[t}t{L= 7   @  <91990!!55!!LR%# Չ\P_L9@ 120!#!L^PL?@!   hggf91/2299990!#3267#"&5!##P117,#J%q\TL@H? HL%1  @ & &1026732#"&'&&#"#"&54632j Pd@7*9  kOeD=!0 l9TA6?&#Hn!bSA8?T %)d@6  (&&  #*& (' j kji*22999199990"326557#5#"&5463354&#"56632!!|WHi1Rwu3}CD?kdPDN@Ms=@pABtZ\#! { 3@   jkji9910"32654&'2#"&546!!hfssfeusgʫ˫\{u༻߻`{J>@#qqro prol 991/<20353&5323!5654#"J{n !o{1xx 7oȼ߅LI L){ C@I=70 6 %C "76. 3@:("D%=/.M/u MCM6+sD299912<2<2<999990@ 05060708]5#"32654&#"!3267#"&'#"&546;54&#"5>32>321xYS\JMWWLepO27Gn 'aȿuc^8>M<[|%!YHZqYa4+#"33)+RNPPXx+'#!?@=B/ +s@>+,&  )&  *&& &,+,* # )#D>,9999999919999999032654&'&&#"&&5327#"&''m1$eA H#cC')d<]*,g9\ //4o0.0tGGq.78MBz;/mNb^hC^XC&DbRY;X1YnED98L\V@T? @ <2991/0533)eqXsy^@ '10!#!X!^?; /@     99190'%3##d)#Ӕ/}b%9V#@N   B   !$  /912299990KSX99Y"&&#"!!#"&'53267#5!6632$R,fs-/d+Ǻ9f.1d0`yu1Ɣ1c|ɏ!JX1y7K@&' 10+5  * 5'.810*8<2991<2<<99990#"'&'&&#"56632326#"'&'&&#"566323326yKOZq Mg3OINS5dK t]FJKOZq Sc1NJOR` t]Dï;73 ";@<7  6<а;83 $77 7=O@*iiiiB91/90KSXY"#3 !qw# /@    v v<2991<299055L-+-+#RR\# /@   vv <<991<29905%5+-:+-#^R^  ^R^  P1 #@   1/<<2203#3#3#P3f111%k'$u%^'$uu\^'2uH;@     -299991/220%! !!!!!"33(RH=MKF{ 8i@92/ & 8   #5/)#92& MuMCM,s9299912<229999904654&#"265&#"!3267#"&'#"326632PVWMfRPhgPPcpP/;}Jb04TY/%W & +#T53+)CBDA88>A>Ay/10!!/yy/10!!/y %@   1<20#53#53Ěb5Ǚb~~ '@   1<203#%3#řb5Ěb-@ 10#53Ěb~-@ 103#1řbXyo '@ w <<103#3#!!j!u#\u"@91990 h 9%-hV'\%N'<ufB7@ 103#ymLB /@ (-  * -'! @') -0)$ !'$* xyx( $02299999999912299999999904&#"3267'#"&''7&&5467'76632d|[Z}}Z[|Z^.[20`0\^.[3.^Z{{Z\}~t]1]02[-^Z3].2]-_Z/#@ v291905/-+#Ry#@ v<91905+-#^R^  #7N@*        JEE<2<2991/<299903#'#"!#!##535463wcM%ɩQge/яN#7B@#    JE E<<991/<29990#!"!!##5354637cM%۸ɩ{Qge/яN;/<@  R Q R <<2<<212<220%!#!5!!5!3!!!/nnnn\\/`103#`/103#Śc// * @  1<203#%3#řb5Ěb/'3?Kt@%1= 1%+C@&7IF:4(:PFz4P@ PzP"P.zP@(/99991/<22299990'32654&#"4632#"&32654&#"4632#"&32654&#"4632#"&H%'H_EDbcCE_yxxwyLaEEacCEayyxxy aEF`bDEayyxxy7a`JGacECcaEyxyEaaECcaExxy"GaaGCcaExxy%m'$uNm'(u%k'$uNN'(uNk'(uk',um',uN',uk',uu\k'2uu\m'2uu\k'2u=k'8u=m'8u=k'8uD` "@LLK 1/20!!!5!!mnm`/B)f7@  91290K TKT[X@878Y3#'#f7@!   PP99991<<99990K TK T[X@878YKTX@878Y@?       ]'.#"#>3232673#"&d9!&$|f['@%9! '$}f['@Z7JQ!7JQ=b10!!=V/)H 8 @ PP1<0K TX@878Y332673#"&/w `WU`w HLJJLD6@ a10K TK T[KT[X@878Y3#V{ W @  P{P10K TK T[X@878YK TX@878Y#"&546324&#"326{tsst{X@@WW@@Xssss?XW@AWXu)'  @ |1/90!#"&'532654&'85xv-W,"K/:=,,>i0Y[ 0.W=XfZ@991<20K TKT[X@878YK TX@878Y3#3# fxu"  @  |1/90!33267#"&546w-+76 >&Dzs5=X.. W]0i)f7@  91<90K TKT[X@878Y373xs 7@   1 4<2.9991/903%!!'7;NwdPo;jnL >@!    <<2999991/999033#"&5'!5!%[Y饵P{;Pu|~$o/nJm'6uf'Vm'=uf']!<210##  N ;@!    21 0 0<291/220 !!#5326&!#!!VD}}/`ŕ{HK+H)@O B $ *'! !'D! >*999999199990KSX9Y"#"32&&''7'3%&&#"32654&Ŷ"#!H&!!#R-:/(  (-Y,\bPȑ^b n%k'<uhVf'\4 @  28  32299991/032654&#33 !##ꞝL!󄃃VT3@ GF221990%#36632#"&4&#"326w.dfSWWX-y10!!X!ת;T .@     <91<2907^t^_t\t%\^u^uw^X 9A      @  aW}a 12035733!j c)t'+nB}a@WWBA     @9991990KSX9Y"!!576654&#"56632eQdR1q?Ay;Jwrnaz3=L$$}k9wuF(\A          @#) & )99190#"&'532654&##532654&#"56632^c:r;Eq-evnmBJ]b`W,p;Eu2X`lP|yQDJLl?<8?yvcG]Z{''  VZ{'' VZ''  VfPm'* u{HH'JP', uuJ'6u{'V1k'&Zu%f'FZ1m'&Zu%f'FZ{$H@ "  @"   GH%<<1/<2990!5!533##5#"3232654&#"Z1.de5yySW;7W dm10!!d /`103#`%%3p@< 1& (# #43('1)-&- 2'-4229999999999122<2032&&#"!!!!3267#"#73&'&54767#70TJBN1Fi1OCHU,1u1!(*=Dl-.&nC>*( n -/ l?Q@ aa1<20KTX@878YKTK T[X@878Y3#%3#?Zk10K TX@878YKTX@878Y@&  //// //]]3#@   99991<<990KTKT[X@878Y@t        !      ]]'.#"#4632326=3#"&d9 #(}gU$=19#(}fT"<9! 2-ev 3)dwyi10K TX@878YKTX@878YKTX@878Y@ //]#1Ś7]@ 91<90K TX@878YKTX@878Y@ //, ]3#'# ӌ7i@ 91290KTX@878YK TX@878Y@ //*//]373 ӌ Zj@ 9910'3$ll  5   @  W <291<29033##5!5!wtt}oyc/ @ PP120332673#"&/w dRSaw 6978w{za103#  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~>: ~1BSax~ & 0 : !""""+"H"e%  0AR^x}  0 9 !""""+"H"`%^ChVjq_8 (Bbcdefghjikmlnoqprsutvwxzy{}|~hR!9\XdffXXX%fmVuu/9%fZH{{{mjdLhX%%u{{{{+?j?X)XVX%PJ)/X;XwP%%uHXuh%fZ##%%uuu)=/VX)L%hXXBFf{{d%?y77 /+   !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~      sfthyphenperiodcenteredEuroc6459c6460c6461c6468c6470c6472c6477c6478c6475c6476%%%%KmJz6b iP~Abj \ . [ ) ^ A  [ ?`m2l)*rd O TJlV|cp} &3@MZgtPJ ! !M!!"l"##3#a#$#$$$% %u%&-&'('(#(J(f()))**A*m*m*z***+k++++, ,),V,,,,-?-`--..W.o../e/r//////////0000(0O0|111S1{122C2t223"3/3<3I3V3{34S4`4m445 5E5x5696J6[6l6y666666667>7T7l78/8r99K999:$:N:d MF@Wm GBGSf   JBits@ mB`#cVeraSansMono6628R00@             䥉_<lmconnectome-workbench-1.2.3+git41-gc4c6c90/src/Resources/GeneralResources/Fonts/VeraFonts/VeraSe.ttf000066400000000000000000001655701300200146000327100ustar00rootroot00000000000000OS/2Xe\VPCLTybhմ6cmapXcvt 3fpgm%gasp4 glyf' ;&hdmx/CHhead'@6hheaEo8$hmtx1w0kernU \loca$maxp=; nameԵpostZ/prepuy ^::a:: fN0n   r t  (   _ (  (   i 0 9 `  y 0 & Copyright (c) 2003 by Bitstream, Inc. All Rights Reserved.Bitstream Vera SerifBitstreamVeraSerif-RomanRelease 1.10Copyright (c) 2003 by Bitstream, Inc. All Rights Reserved. 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 the Font Software, including without limitation the rights to use, copy, merge, publish, distribute, and/or sell copies of the Font Software, and to permit persons to whom the Font Software is furnished to do so, subject to the following conditions: The above copyright and trademark notices and this permission notice shall be included in all copies of one or more of the Font Software typefaces. The Font Software may be modified, altered, or added to, and in particular the designs of glyphs or characters in the Fonts may be modified and additional glyphs or characters may be added to the Fonts, only if the fonts are renamed to names not containing either the words "Bitstream" or the word "Vera". This License becomes null and void to the extent applicable to Fonts or Font Software that has been modified and is distributed under the "Bitstream Vera" names. The Font Software may be sold as part of a larger software package but no copy of one or more of the Font Software typefaces may be sold by itself. THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL BITSTREAM OR THE GNOME FOUNDATION BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL 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. Except as contained in this notice, the names of Gnome, the Gnome Foundation, and Bitstream Inc., shall not be used in advertising or otherwise to promote the sale, use or other dealings in this Font Software without prior written authorization from the Gnome Foundation or Bitstream Inc., respectively. For further information, contact: fonts at gnome dot org.http://www.bitstream.comCopyright (c) 2003 by Bitstream, Inc. All Rights Reserved.Bitstream Vera SerifBitstreamVeraSerif-RomanRelease 1.10Copyright (c) 2003 by Bitstream, Inc. All Rights Reserved. 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 the Font Software, including without limitation the rights to use, copy, merge, publish, distribute, and/or sell copies of the Font Software, and to permit persons to whom the Font Software is furnished to do so, subject to the following conditions: The above copyright and trademark notices and this permission notice shall be included in all copies of one or more of the Font Software typefaces. The Font Software may be modified, altered, or added to, and in particular the designs of glyphs or characters in the Fonts may be modified and additional glyphs or characters may be added to the Fonts, only if the fonts are renamed to names not containing either the words "Bitstream" or the word "Vera". This License becomes null and void to the extent applicable to Fonts or Font Software that has been modified and is distributed under the "Bitstream Vera" names. The Font Software may be sold as part of a larger software package but no copy of one or more of the Font Software typefaces may be sold by itself. THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL BITSTREAM OR THE GNOME FOUNDATION BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL 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. Except as contained in this notice, the names of Gnome, the Gnome Foundation, and Bitstream Inc., shall not be used in advertising or otherwise to promote the sale, use or other dealings in this Font Software without prior written authorization from the Gnome Foundation or Bitstream Inc., respectively. For further information, contact: fonts at gnome dot org.http://www.bitstream.com sLjq{)j/s3fbwPP;f=fRBsJ}s jjb\jjjj jj)R`fX{Hjj``'''Djb{sj\)'\jj=fHf JJ }T{3`}TjNNs@$d@$ 'd']}  22GG}  2d2dd%x %K.%x @@%0% @@   @I o} @ :]%]@%@0d0 ddl~}~2}|{|{zyx wv wvuv utltsrqp qp p@o}nm>nkm>lk llk k@jddjihihg]hhgf%g]g@f%eddeddcba`_.`_.^]\K[}ZYDXWVUSdRQ2POP}ONA@BL JdI"IH2GGFE EDCDkCBCBA BA@ A @ @@S?>->M=<=K<; <<@; :9:]98987 654543432 321 2 2@1 0/0D/.//. ..- d-,+,K+"++@* *d)(0)A(-(0'-'&:% %]$#$S#"##@"! !]     @#$0S-0 k@-B d-    @    @8k d } d2}-2- Sd+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++, %Id@QX Y!-,%Id@QX Y!-,  P y PXY%%# P y PXY%-,KPX EDY!-,%E`D-,KSX%%EDY!!-,ED-ff@ ]/10!%!!fsr)! $@a ` b  10%4632#"&!#5L97NN79LD{h8NN87NM@ b1<20#!#h++W@6 cc       991/<22<2222220!!!3!!!!#!#!5!!5!T32!53!5>54&#"pkh ou=Ŗq 9< mĖ*M@+ etee)te&ei`+  #) #  +99991906632!"&'332654&##532654&#"#u^unp _2 p,. ׫23"f~?@%d d M ss i  <<91/<<290KSXY"/ ] ]K TK T[K T[KT[X@878Y@ 8I( 68 GH ]] !53!53!!3Xtj%m ky@%xetee wb `     9190K TK T[KT[X  @878YKTKT[X @ 878Y@& //]]!>32!"&'332654&#"#T4Vgqq Z5VդT$$23"@C %c@" eely#ei`&    &190@%   ) ) NN N!]%2654&#">32#"!2#.#"DlB%O[q n¾FȽKJy^ejr@Mwb  91/90KSXY"K TX  @878YKTX @ 878Y] #!#!-Nuo1\ />@"$e ee*i`0$ ' -'! 09919904&#"3264&#"326#"$5467&&54632)|{{|lv͟ҟϴ%]@$ elyee i`& #  &190@ !"AAA ]#"5432!"&'5332"32654&CmO[p m¾KJ xRel%"ɽy @a a ` <21074632#"&4632#"&M88ML99LM89LL98Mh8NN88MM8ML99LMJy"@a  1990665534632#"&J^X?M88MM88ME%Z 8MN78MM^@29190 5Ѧf@rr<210!!!!^@<919055//y "<@ a ! ei `#! #10%4632#"&6632#6654&#"#hM97NN79MjT{~rah8NN87NMc/.ֶ3H+ʜ{o@Mm@;BAKE'N$ KE($ Ez Kzz@|$z+ |z+7N'(HA ==1N22991<9999990%#"&54632536654&'&$#"3267#"$'&5476$3254&#"326;]گ];DCjϳZVd_m+`5l}YawLNsyzrRPPR%oT&']gw}LLF]]{GF~b|i}@Qdddd  Mo ob    91/<290KSX22Y"(]@ ((( (,+]! 5333!53!3$H}}jkjjHjqd$E@'o obo ~ $ !$ !%229991/2290%!2654&#!532654&##53#5!2!+w埙jj~}jkäs8@ndn i`&' %10o]#"&'&5!2#.#"3267BapmIzq%0dap\@Aq=@ ob o  $ !299991/220@ 0O]%3 !#53#5! !#7ܺwRPjL66Hjkvtq3R@*  obo ~ $ !2221/00O]353#5!#5!!53#5!!53q{R{{{jk q7F@'  obo ~  $ !2221/20353#5!#5!!53#5!3q{>{{jk ~jsG@"  o ndni` &%1990 0 O ].# 3267!5!# !2#q]ͯiY88kMM_b;<q^@.  o bo ~ $ $$ $!2222221/<2<20@ p]353#5!#!#5!#3!53!3qGGjkkkkjj~jq 7@ obo $$! 221/220@ 0 P ` p ]%3!53#5!#GjjjkkTVm@ dn o b $ 12990K TX@878Y0]KTKT[X@878Y533265#5!##"&rXXw^qGca9kk"q(@Dd  dM  o bo $$!2<2991/<22<290KSX2Y"p]@  $ '6 F HW YYhzz    * % % & &'(: 6 6F F @ F @ F @ F@B@@@@@S X X U UPXXi i e efb```y  y  v vvyyC]]353#5!##5!# 3!3qGͪ3 HapmmpbEnpbNQJKQkdappbcVobcqC@!o ob o # $ !2299991/220_]!2654&#!53#5!2#!3wjkjs"@@nn i`#  %#99999190_$o$$] 47>3 ;.'2#"`jmpbE6mdapbcVm:C@QJKQq7$@H  Mo #ob o#   $ $ !%229999991/<2229990KSX9Y"3!&&##3!53#5!2%!2654&#!Fe(ٶCpbz'[REjۊQjjkп&)'@B#"$!   M !dn'dni'`* !)$)*9999190KSX99Y"+]@ +0+P+p++]KTKT[KT[X*@**878Y@t( ) ) ) )) )!)"8 9 : : : 99:: :!9"8#I I J I HI I!I"Y Y Y Y Y Y!Y"j l l l k kl l!l"i#{ { { { y{{ {!{"y#7++]]732654&/.54$32#.#"!"&sq֯hq|ɹ˭{HTlt7;A©-+ž{kz<7=2B{@  ob o $ $1/2<20K TX@878Y]K TK T[X@878Y@ /`]!53!#!#5!3I{.{Ij` j`bn@%   ob`$$ $$!1<299990`]KTKT[X@878Y]#5!#326#5!#! H廿jkkkkS@9dd  M o b   91/<290KSX22Y"]@"   (* * ']] #5!###5!f jzkkjkk 3@[d d    dd     M  o b 91/<<<290KSX22Y"]KTX@878Y@   %) $?< 5L FX\ Tm d~ t        &&&&)$ &546 4 7 IIEBFE G D H[[XW[X Y [_______feeehgff g g f d ehjjhusvuu v u w t y||yV]]!# ##5!# 3 #5!#y!7TZ9Bjkk9\kk @gdddd   M o bo  91/<2<290KSX2222Y"]]K TK T[KT[X@878Y@ )(%&65EFVUjicdyz        &&'& ()%%$$/6777 8997FCGGEE KIFIIUSSSSWSV V S [[WYYedeedede e d kllke``eee`xzzzz{yx x y {~||@{{}yuxxur]] 3!53 #5!# #5!# 3!53LP@Iuנjjskkkk?jjZ/@E  d d    M  o bo    $$999991/2<290KSX22Y"]]K TX@878YK TK T[KT[X@878Y@d 6 E [Zf xw       **/::7 8 ? 9 ? 9 ? ???F I V Y [ [ i h o h o `vvx x x )]]!53#5!# #5!#33+uu?jkk\kk,j\? m@#  M b     991/0KSXY"]@  O ]]35!#!!53\{^LzHZH$@ssq10!!!!/jjB*@Mb/9910KSXY"#mo@ssq9910!5!!5o/jNj@ b91290##}-m/10!5mPPsdv10K TKT[X@878YKTK T[K T[KT[KT[KT[X@878YKTX@878Y #oudxfD (~@/!  z!#&` z!-" ,' "*)22991/99990@$*o*x(*z( ]]5#"3263!5#"&5463!54&#"#5>32/퉆ts=kn_`VNvzojsJFIydb));!G@%!zqz` 5,'0"2221/990#]7#5!>32#"&'!532654&#"i6{{6ij@jmd__djuʿfD5@  `- *10o]#"5432#.#"3267'ްeekw?31/0|}f!O@& zqz` ,',5 *"<<1/990@ ####]%3!5#"5432#5!54&#"32636{{6fjjd_76_d)jifVDN@z  ` *2190@ @]]!32673#"5432.#"V碞y,}34ױJqq@& zq z - '6,0<2<91/222990]KTX@878Y``?]]#.#"!!3!53#5354632qaSOgT)CBKNqkjjRkf9D,f@3, )z) #`- -, '5&7*-22199990@ ....]#"&'53326=#"54325!4&#"32653iX`}6{{6h[&&h`ďd_76_dkJo@,  zqz =,,' :,',021/<299990/]KTX@878Y353#5!>323!534&#"3Th3l_zj@jVlnjjÏjJ` I@ z z , ',021/20]KTX@878Y4632#"&3!53#5!C/.CB//C갰hq.DD./BB(jjRk;9 i@"   z-6' , 01990/ ` p ]KTX@878Yss]4632#"&#5!#"&'533265C/.CA0/C-fëH>_UR[Wq.DD./BBzkq!!`Z{;@X    >>M z zqz   ,',0<9991/<2290KSX9Y"]@  &))899HVWggghw           ( ( ( &)-**+,)/6 6 6959?F D D DEY X X WVVVh f f gegaabef`x x x zxxvz T]])53#5!#5!# 3!533Ji‘j@j kkjj;R @@zqz,',0 21/20 ]KTX  @878Y%3!53#5!鱱ijjj@jJ^D0@A  +'z)%! z.#  = ", , '?=',?(,* '$,&01291/<<222990@ ?2_2o222]KTX111@878Y@/ / // 2 ]>323!534&#"3!534&#"3!53#5!>32%5n`o{`o{h3d|Xuwjj%jj,jjTijp{JDp@.  zz =,,' :,',021/<290/]KTX@878Y353#5!>323!534&#"3Th3l`yjRklnjjÑjfjD +@  `D *10 o]%2654&#""5432hFc32;VD #U@,  z"z ` $,5!,' 0$22212990%%]32654&#"'#5!>32#"&'3!53i6{{6Hiʵkd__dkkfVD#S@+# zz `$,',5 *$2<<12990%%]3!53#"54325!4&#"326536{{6hkkd_76_dkJD@" z z -,' ,021/2990@8/@@@@@@D@@@  ]]KTX@878Y#.#"3!53#5!>32jNKͦh6z-c)ONjjTioksD)@A#"$!>  > M !''`* !->'F$-E*9999190KSX99Y" +]@X'' '!'"'#Z Z Z Z ZZ Z!Z"X# !"# !"# !"#@++]]75332654&/.54632#.#"#"&sj|_{ֽTcjutwZg;wv]YFV1-,f,*gtRRCQ*-/o,;'qh@  z`, '/<291<2990K TX@878Y@&&/ ]#533!!32673#"&5ݢZ4FHBkJk]LU_7'u@"   z `,',:,' /<1/<2299990K TX@878Y]KTX@878Y!3!5#"&5#5!3265#X3k__z'Cjjo9k'@9    >>M z    91/<290KSX22Y" ]@H' Scv   %*** * *80HG GWXghvvwxx x x ]]!#5!# #5!#yy++wykk%kkD!'@[   >  >> > M  z  91/<<<290KSX22Y"`]@ $, (3= 9DJ IW ^di itz z          &() & # **%&:;??????:; 9 ; 8 8:9FEEE F F I H HHHHFQPPPPPPPPP R T XVSPb``dd````` b d fb`uxy}}yyyz@y v q u v vvwwwtw x]] #5!## ##5!#vęw'kkDkk->j'@g >  >>>Mz z    91/<2<290KSX2222Y"K TKT[X@878Y@ %*GIWWYXffhhyy       (),,% % ( '&&&))'9?9?HIGFGZYYYWY WUVVVVjjjgh h h gefx||||y wttttxvuuu []]#5!# 3!53 3!53 #5!#TߏLl%3kkw7jj>jjkk9'@Y  > >M z   - 9991<29990KSX229Y"]@ZYix))* * ''&XSUPUPSV V hd``dvwx~~tty x x yuuvvvv(]]7#5!# #5!##"&'53326Fsy++w2zo/c2^9<7Cñkk%kkT|[D;=R' j@" >> M z z  - - 991/0KSXY"K TK T[X@878Yif ]35!#!!53RjffBkVf#V$^@0 !   ! s ssq! %$ '%<29991/999999990#"&54&##532654633#"3>l==l>DVdbVititݓhXឈ"XG10#$[@/   ss#sq%' %<29991/9999999903265467&&54&##53233#"##FUbcUF?l>>l?W"Whtitݔ'>@    919999990#"''&'&#"56632326d]` _\Yd^` a\'XTB 9IMWQB:J\'$um )@a dd )('d&d  %"#$M %o  f'#o!$ ('& *%"   *99999991/<29990KSX22Y"]@://   / & ) "!   ///..)(%'&+/]]4&#"326! 53.546323!53!3wY?@WW@?Y#"HHKrrNH}}Z?YWA?XXj%zSrrP#jjHjsu'&Lq3k'(ud^'1us\'2Hu`b\'8uff'D9fd'DC9ff'D9f!'D9f7'D9f'D9fuD'FdfVf'H^fVd'HC^fVf'H^fV!'H^Jf'H`d'CH f'H!'HJ7'Qfjf'Rhfjd'RChfjf'Rhfj!'Rhfj7'Rh7f'XH7d'XCH7f'XH7!'XH9; 6@   b HH  <2<21<2<203%%#5#p##pFsu= @  i 10"32654&'2#"&546LhgMLhjJ@v+..fiMLfgKKk1.-rBPL"I@'  `# -" #222212<2<0%#&5473#&&'667u#dd\PjsdZt,+ .'{ i ldL@(  seis   I <2291/22990#&&#"!!!53!53#534632Ni q`ydt%UK_ekjRk'\= Ak@;39 (',o$ o?i$B3/9)'J1'*'"', 04999991/99990@D --./5``o55/0123-/0123]].#"!534632#"#"&'5332654&/.546wz 6IYwUOmofoPuVhYwZj8l]4M/7cr%#eiyhTuI6ARz$<T@TN N M  1%=1I 7" " PNPN"L7KCL+KCMOU2<99999991/2<229990KSX9Y"32654&##3#'&&##3!53#5!2"32676654&'&&'2#"$'&5476$}SSTR}*;tL#>1\TSS`׃^]__]^⃄^]]^\^ㄘmmllmmmmllmmLKJL3(DF/DDCpmS[j^^]僂^^__^]⃅]^^gnmmmmnnmmmmn2JM@+    ? 3?' REK!Q9K!M-K1/90#"&54632#&&#"32672#"$'&5476$"32676654&'&&`PWTriwyxvaqmmllmmmmllmm^]__]^⃄^]]^\^=%'mf_cnmmmmnnmmmmng^^]僂^^__^]⃅]^^(z@D  # '%!b) &S"7$S P PP TPS$T 7)2291<<22<22999903#3!53#3#53#5!!#5#3!53##^VVV+TVV}-DVVABBBB7VBBBhBBBhRfO10K TKT[X@878YK TK T[KT[X@878Y3#uf77! z@   1<20K TK T[KT[KT[X@878YK TK T[KT[X@878YK TX878Y2#"&546!2#"&546=0EB32BE/EB23BE!E03BB30EE03BB30E'<@!  r r  <291<2<29990!!!!!'7!5!7!}/H{}?f٠f٠#@Udd  ddMo!ob o~  $ "$ $<22991/<2220KSX2Y"]@ !"#0%O%o%%]!#53!3!53#5!#5!!53#5!!53dZ昤XN{P{{{MjHjjk d' +s@:, +&  ) *& nn&i`,,#* # )+#%,99999999199990_-o--].#"324&'7!"&''7&5!27A| "5@}!"{WWoeNWUC`PXVwQ`YYQJuRlSVVEiZUSG /D@$ !- $'!!0 $U*U0999919999032654&#"&&#"326#"&546326632#"&2TevYQ1UevYQG__KDa_/YYie9XXie~९{⦮u *@r  r   <2<21/<<0!!#!5!!!1Ϡ1yy &@r  <2291/90 5!!po &@ r  <<291/90%!555f$K@]dd  M s s sb"s# % #I! I%<<9999991/22<22<290KSX22Y"]@6iih     89969::9F K M MKKJ@@BBFIXVYYYYge g g gffgjhohojjhu y||yzF]]!53!5!5'!5!#5!# #5!#!!!!3hlR)WSGmjoiAikk\kkizTij;V'@3   zzz ` , ,',:, '0 2<91/<2299990!]K TX @ 878Y!]!3!5#"&'3!53#5!3265#X4Z9^'鱦^`y'Ahjo$$kkkh-)6@'! '!* $$*99919906654&#"#"&54632#"&54324&#"32IH7$$0e՘ݢe WOmVPmmWKt,>bFأ[t}t{wJ@#    <91990@  *]]!#'.#!!>?3!5 nNI =DN)u?$ HNh"%!%)/5w'=@"  V WV V WV22122<20!#3!53!3!53#56JJJJJJJJ'J@%z z =,' ,,:,',01/<2220/@]353#5!#3!53!3T!jRkkjjRj/%#@  & XX&1026732#"&'&&#"#"&546327j Pd@7*8  kOeD=!0 l9TA6?&#Hn!bSA8?S}(,q@<&+))&i-) #*#Y#Y -2299991999903#5#"&5463354&#"#566325#"326!!FP0}WtiWfLMBilea\oFTP30pr-T^FEPNQUah^d ,@ i Y Y99102654&#""&54632!!jklihmliԯP-Lװױbh}')M@( n!iw   '%*991/<22990%!53!565#"!3!&5476$32`PuuNsnccotӶF7SF-W`֗g[`_\gfD 8?@G,2*$ 29z*z2/*<'!6`@ $+-?+239**@22999912<2999990@(AoA*+,9?]]5#"326#"&5463!54&#"#5>32>32!32673#"&.#"/퉆tsmS}t_`V7Ju衟y+z\NvzoF[XIxcc))WZXY}[,L} +@?+*&  ) *& &`,,#* # )#+D#*,99999999199990@ -o-wx]].#"32654&'7#"&''7.54327H&pJ-(oJw>@^CL=>^@Lo99Hv1g=;Ly3Jv56?Ms232? "8@a ! e` i#! #10#"&54632#"$54675332673 M87NN79LjU{~q`m8NN87NM/.ֶ3+ʜ{! !@ ai   1/04632#"&53L97NN79LC{Dm8MN78NN5^@ r 10!#!^=} *@    91903##'%\sB}}`s-Pb;#P%@N%%  %!"!""!M!"se ei$&%#"!  &99912299990KSX2292Y"6632#&&#"!!#"&'533267#5!hɦ3vFbKIPW#1\˦2tGbJHPW\ oVRjVRj8l@9216/$#(!6/,(+! /(/6 6!921$#+9<2919999999999990#"'&'&'&#"56632326#"''&'&#"56632326c]\ _\Ye]` a\Xb^` _\Ye]` a\dZT?9ILZRB 9IѓYSB9ILZRA 9I3VM@)ddMwb91/90KSXY"%33^]<;A+%# :@     Z Z <21<222<22055%)+#)+#ssRssRH# :@     Z Z <<1<222<2205%5s+)N+(#^R^sXXs^R^sXX/ #&@a! `$ $1<<220%4632#"&%4632#"&%4632#"&%M87NN79LVM87NN79LVM87NN79Lh8NN87NM88NN87NM88NN87NMk'$u^'$us^'2Huwu!^@0 obo~    %"2299991/0 #0#]%# !! )#5!!53#5!!53q7#3P{Ryy{jjb_ fD ,3k@/ '--z  0*$`4'3 - !*429912<2<9990@P5 -3]]%2654&#"!32673#"&'#"5432>32.#"h碞y,IEςFIzF}ba`c32c``cױb/10!!bb/10!!b/@  i  991<290#667#667LE|ME}@?[P@?[r3@  b  991<29066553%66553NF~@LE}+?=[P??[%@ i  91990#667%ME}@?[@ b  9199066553NF~+?=[y '@a ar  <<104632#"&4632#"&!!M87NN79LM87NN79L8MN78MLU8NN87NM#u"@91990  9%-9!'\DZ\'<u+@M`i10KSXY"3#7Rh\#/o@= -'! 0 -!-'!0 *$0* $ $*099999991999999907'#"&''7&&5467'766324&#"326^+))-`8wE@}=_))*,_8xEBzQrpqq^z@Fw9^,*(pprs##@Z2105s)+#ssR##@Z<105+(#^R^sXXJ'"}@1z"q z - =,' , ,'0#<221/<222990@ $/$o$$]KTX###@878Y'.#"!3!53!3!53#5354632/^z{Ǯ갰WYVUdCjjRjjRk`Ju@. z zqz  =',, ',0<2<991/<222990/]KTX@878Y!3!53#"!!3!53#53546ף'ٮ갰Vjj@dkjjRk`9;\@1  b  H  H <222<22212<2<22<203%%%%#55#p##p##p##pFE%'BL  a  104632#"&M97NN79M8MN78MLZ@   9199066553Z_WE%ZZ`2@     991<29066553%66553Z_W;_XE%ZPE%Zq L #0<@L|@B?@=@=>?>M G$jjGj=*j1?7`A=iM$>0-'@!' :  - :4! D4 J M9912<<2290KSXY"2#"&546"32654&"32654&"32654&#'2#"&5463#2#"&546WddWVbcXcdWVbcXbdVVbaU"ZܻۻZݦ!\ܻۻ ۻۼk'$uq3k'(uk'$uq3\'(uq3k'(uqk',uWk',u_\',uqk',usk'2Husk'2Husk'2Hu`bk'8u`bk'8u`bk'8uJ`' @@zz,',0 21/20 ]KTX  @878Y%3!53#5!갰hjjjRk?f[@ 91290K TKT[X@878YK TK T[KT[KT[X@878Y3# #ttfJ7@  [[99991<299990@A            ]K TKT[KT[X@878YKTKT[KT[X@878Y'.#"#>3232673#"&9!*0`f[&@%9"+0`f[&@Z7OL!7PKb+(10K TX@878Y!!V)9H n@  [[120KTX@878YK TX@878YKTKT[KT[X@878Y332673#"&` hddh ` HOGGO7u! -  10K TK T[X @ 878Y2#"&5460EB33BE!E03BB30E \@  [[10K TK T[K T[X@878YK TK T[X@878Y#"&546324&#"326sssszX@AWWA@Xssss?XW@AWX#u"@    991/90!#"&'532654&'B@?~p*X.)O#9B,,@p1QY 5-X<f:@ 91<20K TKT[X@878Y3#3#rtfxLw&@   9991/9990!33267#"&546^WC8:$C q|<{/.8  YQ1i?fL@ 91<90K TKT[X@878YKTX@878Y33ttxV)Q@-  o bo  $ $ !<2299991/290353'7#5!#%!53{FF3F{jq\kk\-{a@% z qz, ' , 0<2<9991/290P]KTX@878Y%3!53'7#5!7ꮊ=Ǯf`VjbVk'6usf'V\?k'=uRf']@ G<210##  q M@%  obo   $!<2299991/22<200]%3 !#!!53#53#5! !#8ܼPyȾRPjL66H}1je}kvtfj-a@3-,+'$#"!( `(q.-, #"! '($+ D*.99999999199990 /o/].#"32654&#"5432.''%.'7%5-Q(/l#K4I2%%=_w\B% pاy/"5k7N:QV^DNZk'<u9f'\DqM@&oo ob o # $ !222299991/220_]!2654&#!53#5!#!2#!3wp9jkkj;V #S@+ "z zq` $,5!,' 0$2221299990%]32654&#"#5!>32#"&'3!53i6{{6Hijmd__dkk1@ r10!!ӢD /@   <291<290 '71s33r4rP13p4pq3 ?@Mh \ 12990KSXY"535733fTzj^TZk@6   M   h  \ 9999199990KSX9Y"#56632!53!5%6654&#"FBEJVJaTN^ "hzlKMzBUcLd*R@,& )&h+  #)#\\ +9999199906632#"&'532654&##532654&#"#}I;j_wyHDFb\^ffe5acQLRWFlcHdwdrzJMXR]_JJJCH@A''5 d?''5dd''5 dsm'* 3uf9H'Jq^', uu'6suD'Vsk'&5uff'F^sk'&5uff'F^f)d@2!z! 'zqz` $,',5$ **<2<91/<2990@ ++++]%3!5#"5432!5!5#5!3#54&#"32636{{6Ffjjd_76_dJjujjiZZs10!!ZsBL  a  104632#"&M97NN79M8MN78MLq4p@;-s+!s1edei`#5-+$(#,"5 .!, ",(4( 529999999999122<20#"#73&'&5467#7332#&&#"!!!!3267q+筽/--/c|jz/1w/t0h1"0h.>Eh3 Dh吏7 k@   1<20K TKT[X@878YK TK T[X@878YKTX@878Y2#"&546!2#"&546=0EC21CE/EC12CEF.2CC2.FF.2CC2.FP10@ //]K TX@878YK TX@878Y3#?uJ@!  [[999991<<99990@2         ]]K TK T[KT[X@878Y@)   ]K TK T[K T[X@878YKTKT[X@878Y'.#"'>3232653#"'0)'4`fU$>71,)/ag^CH> 9.dv 7/ir-qP10@ //]K TX@878YKTX@878Y#u?u@ 91290K TX@878Y@/// ]KTX@878YKTX@878Y3#'#tt?c@ 91<90K TX@878Y@//// ]KTX@878Y373tt5P@'  M  h  \<<91<<290KSXY"!535!533#3TNriTTR7 N@ [[1<0K TK T[K T[X@878Y@  ]332673#"&^k\\k^7667u}|u -  10K TK T[X  @878Y2#"&546/FC22CFF.2CC2.F  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~>: ~1BSax~ & 0 : !""""+"H"e%  0AR^x}  0 9 !""""+"H"`%^ChVjq_8 (Bbcdefghjikmlnoqprsutvwxzy{}|~f7q3!JZ?JJqsjqqqdsq)q5TqPq1fdsbqsq{V`9  H\f;{fffJf'JJ{;;;J'Jf;fJs7;'7!7Rsqds`ffffff{fffffJ 'Jfffff'7'7'7'79\3X;d3;#h^5BJ+/}^}fLJ7=#3s wfHVh33VJVJ9Z%Z qqqq)q)W)_)qsss```J#LZV-{s\7RuqfHhq;55Z5dddsf)q{ss{fs{ffZ55+   !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~     sfthyphenperiodcenteredEuroc6459c6460c6461c6462c6463c6466c6467c6468c6469""""Tu_Ox2A?c9^  t  \ U ~(uR%~H;f"sa4{-i4/n/J !"G""##r##$$$$$$$$$%% %%'%4%A%N%[%h%u%%%%%%%%%%&&&H&&'5''(()*%**+7+{,,--6-f-.r./=//00`0112>233H3d344455`555556;6667!7\7~778 88%8I88999:M:l::;t;;;;;;;;;;<<<<*<7/>a>>??P??????@S@@@A?AAAB$BBBC CC*C7CDCQC^CkCxCCDDD5DEEQFF9FFGGTGX7 J79k:;<$7$9$:$<$I$W$Y$Z$\$$$$$$$$%&%&&%*&%2&%<%d&%g&%&%&%&%%&%&%&%%&%&%&&&''&''9(&))))))$N)Du)H)R)bN)iu)ju)ku)lu)mu)nu)p)q)r)s)y)z){)|)})u))N)N))N)N**&**<**--a--.k.$.&.2.8.:.<.H.R.X.\}.b.d.g.h.p.q.r.s.y.z.{.|.}.~...........}...........}../7Y/8/9 /:N/<}/\/h////}////}/1}1}1122K2292;3a33a333$D383D3H3R3V3bD3h3i3j3k3l3m3n3p3q3r3s3y3z3{3|3}333D3D33D3D3333344K44&4&57595:5<5D/5\5i/5j/5k/5l/5m/5n/5/5&55555566K66666777777$77&7Da7Fa7Ha7Ra7Vk7Z7b7ia7ja7ka7la7ma7na7oa7pa7qa7ra7sa7ya7za7{a7|a7}a7a7a777a777k7k7a7a8D88D888$8-8b888899D992929$u929DD9HD9L9RD9X}9\9bu9g9iD9jD9kD9lD9mD9nD9pD9qD9rD9sD9yD9zD9{D9|D9}D9~}9}9}9}99D9D9u9u999D9K9K99u9u9999::k::N:N:$:DN:HY:L:Ru:U:X:\:b:iN:jN:kN:lN:mN:nN:pY:qY:rY:sY:yu:zu:{u:|u:}u:~::::u:u:::u:&:&::::;;$;&;2;b;d;g;;;;;;;;;;;;<<<<<<$a<&<Da<HN<L<RN<XN<ba<d<ia<ja<ka<la<ma<na<pN<qN<rN<sN<yN<zN<{N<|N<}N<~N<N<N<N<<<N<a<a<)<a<a<<==IIII&IINRUUY Y Z Z [\\b7b9b:b<bIbWbYbZb\bbbbbbbbdde&f}f}ffggKgg9g;hDhhDhhh$h-hbhhhhyz{|}&K9;79:<IWYZ\79:<IWYZ\K9;&$-/99:9;9<9b99$-/b$a&DaHNLRNXNbadiajakalamanapNqNrNsNyNzN{N|N}N~NNNN<Naa)aa79:;9<YZ79:<IWYZ\&79:<IWYZ\&&K9;K9;K9;DD$-bDD$-bDD$-b7Y89 :N<2\h22K6K$9<b$a&DaHNLRNXNbadiajakalamanapNqNrNsNyNzN{N|N}N~NNNN<Naa)aa&&<K6 UE@ ^m L GBGSf JBits@ mB'#sVeraSerif6628R00                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                     "                      "        #                       #         $                   $        &                   &        _<K LmVeraSeBd.ttf000066400000000000000000001625601300200146000330730ustar00rootroot00000000000000connectome-workbench-1.2.3+git41-gc4c6c90/src/Resources/GeneralResources/Fonts/VeraFontsOS/23TVPCLTzZϬ6cmapXcvt 먏fpgm&4gasp, glyf5&NhdmxUHhead|-86hhea0$hmtx5gh0kern4Dlocar(maxp]^ nameR postV0prepV: ::O:: S_0r   v t  (   ? 2  2   G . _ `   0 & Copyright (c) 2003 by Bitstream, Inc. All Rights Reserved.Bitstream Vera Serif BoldRelease 1.10BitstreamVeraSerif-BoldCopyright (c) 2003 by Bitstream, Inc. All Rights Reserved. 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 the Font Software, including without limitation the rights to use, copy, merge, publish, distribute, and/or sell copies of the Font Software, and to permit persons to whom the Font Software is furnished to do so, subject to the following conditions: The above copyright and trademark notices and this permission notice shall be included in all copies of one or more of the Font Software typefaces. The Font Software may be modified, altered, or added to, and in particular the designs of glyphs or characters in the Fonts may be modified and additional glyphs or characters may be added to the Fonts, only if the fonts are renamed to names not containing either the words "Bitstream" or the word "Vera". This License becomes null and void to the extent applicable to Fonts or Font Software that has been modified and is distributed under the "Bitstream Vera" names. The Font Software may be sold as part of a larger software package but no copy of one or more of the Font Software typefaces may be sold by itself. THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL BITSTREAM OR THE GNOME FOUNDATION BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL 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. Except as contained in this notice, the names of Gnome, the Gnome Foundation, and Bitstream Inc., shall not be used in advertising or otherwise to promote the sale, use or other dealings in this Font Software without prior written authorization from the Gnome Foundation or Bitstream Inc., respectively. For further information, contact: fonts at gnome dot org.http://www.bitstream.comCopyright (c) 2003 by Bitstream, Inc. All Rights Reserved.Bitstream Vera Serif BoldRelease 1.10BitstreamVeraSerif-BoldCopyright (c) 2003 by Bitstream, Inc. All Rights Reserved. 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 the Font Software, including without limitation the rights to use, copy, merge, publish, distribute, and/or sell copies of the Font Software, and to permit persons to whom the Font Software is furnished to do so, subject to the following conditions: The above copyright and trademark notices and this permission notice shall be included in all copies of one or more of the Font Software typefaces. The Font Software may be modified, altered, or added to, and in particular the designs of glyphs or characters in the Fonts may be modified and additional glyphs or characters may be added to the Fonts, only if the fonts are renamed to names not containing either the words "Bitstream" or the word "Vera". This License becomes null and void to the extent applicable to Fonts or Font Software that has been modified and is distributed under the "Bitstream Vera" names. The Font Software may be sold as part of a larger software package but no copy of one or more of the Font Software typefaces may be sold by itself. THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL BITSTREAM OR THE GNOME FOUNDATION BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL 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. Except as contained in this notice, the names of Gnome, the Gnome Foundation, and Bitstream Inc., shall not be used in advertising or otherwise to promote the sale, use or other dealings in this Font Software without prior written authorization from the Gnome Foundation or Bitstream Inc., respectively. For further information, contact: fonts at gnome dot org.http://www.bitstream.com{`jyV\byb`{{N\=VTq\f{fb--){bb??)fD)y V%hNf s{juy jdy{Hy`yqq 33Nyj``'''Dbjyyb3'b=D)fRR5fVVVJ!/TfDs@K 2ǻƻĖG}G0}  & @ 7 !7 2.+t+t ]%]@%@ @  dtd  @   2 }t} t2q  @~t~}|}|{z{zy]zzyx%y]y@x%w7vtvkututsrqrqqpo.po.nmlkKj hg g@f!e}d@ckba}`^] ][YXY-XW2V TSTSRQPQ%PPOONMN}MI MM@LL1KK2JI J}I HG H}G FE F2ED E E@D CBCBA@A}@6 @@?=)?>=)>]==)<;<;:;:9 :9 878}76 776 6@5454+ 43 32) 210 0/$)/7.! .-,-,+ ,,@+ *&*) )@('('&''@&%$)%$!$)#!#"! "! ! !  @!*@-:!}22!)k!) !-} -    @      @@d+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++, %Id@QX Y!-,%Id@QX Y!-,  P y PXY%%# P y PXY%-,KPX EDY!-,%E`D-,KSX%%EDY!!-,ED-ff@ m/10!%!!fsr) )@r p s  9910%4632#"&!#5oNNppNNowwNppNOpph@ s1<20#!#++)T@3        991/<22<2222220!!!3!!!!#!#!5!!5!F+`aaE````HFR`hh$+2h@;+2ww,v %u "p3(  /,  (%!  32<912<22907333&&54673#&&'#&&6654&'xypay| ynPUcUcyehal;#7 +'t;Ǵ, T=FV WJJ`1h '3W@,*(}}} .}"p {4+%1 412<0KSXY""32654&'2#"&546!3#2#"&546"32654&@88@A88A˿NZ;ʿ˾A89@A88e-dR.8@e,-,--,2130 ! 78/!! */,!60$ ($v&6~{-vp&03 !%$') 3/,$.)$)3 99999999991/2999990KSX999Y"0]@0 / 07 8/0178 / 07 8/0178OOf0v/u0]])'# 5467.54$32#.#">7#5!#3%326!d33J|tket +*>XHIuwwpq :@*v{  1/2990KSXY"!5!5%!!w;yyX@+   *  u{    9991/990KSX9Y"#663 !53!57$54&#"xzz?`ÉvdB%%ДJ*U@- vu)&u{p+ # ) #  +999190663 !"&'332654&##532654&#"#v!vy B4vy##̱((Hx{T=L@%* v v{ <<91/<<290KSXY"!!53!5!!!3qxCys+G@&uu s p     91990!663 !"&'332654&#"#BZ@zxLw3d34(*F?B#%6@ uu u{ p& # &1906632! !2#&&#"2654&#"A_^XZvyngXXggWWu44qffiѯدد7@*s 91/0KSXY"#!#!XE)j##/C@% 'u!-uu{p0$ *$* 0991990! $5467&&54$! 4&#"3264&#"326ȺL\]KK]]K']rs\]rq^̥Υ$Ⱦ'wwuuշo %@@# #u#uu{p&   &190"32654&#"54! !"&'3326gWWggXXxB^$2O229991<9999054&#"326#"&54632536654&'&&#"3267#"$'&5476$32!#|ecyyce|,ZڿZ,ۏnq\^aYkzg9~~GͲ$sLHLLG}'갏`NTFEjśewzDC^TT~~|KJ}~|aboL@Q     * v vs  91/<290KSX22Y"#5333!53!3!z23E{}y\yy1y#;`R$R@( v#vsv ## %(#"%2299991/2290&P&p&]353#5! !32654&+32654&+`uyyƤf}vV?@~ ~ {p % 10@ 0ou{{]! !2#.#"3267;Iz 'Ωܕ+ef@A`8@ vs v % (# "299991/220]%32+53#5! !Uoy,GE+yy`W@.  v vsv )( #"2221/0Po]353#5!#5!!53#5!!53`>yyy!`O@)  vsv )* #("2<21/20]353#5!#5!!53#5!3`3{yy!yV3D@!  v ~~{p(  % 190 P ].#"3267#5!# !2-ҿT;I6Ʊ&&xNLhi@A`3^@-  v sv - -( #(#"2222221/<2<20@ P]353#5!#!#5!#3!53!3`Ryyy yyyydy`` 9@vs v (#(" 221/220@ 0 @ P ` p ]353#5!#3`yyyyjV{@ ~ v s (# *.12990@ ///0@P]KTKT[KT[KT[X@878Y@____Y_p]533265#5!#!"&MLaMS^X+yy"`3@>   *  v sv (#("2<291/<22<290KSX2Y" ]@         , & & * *))'9H E @ E @ C CCHA@@@W S S S SSZy   =  ! &5 6B GQ Vv ]]353#5!##5!# 3!3`y-ٓgyyy!yy3yy`s =@ vs v(  (#"21/20@ 0 0 0 0 ]353#5!#!53`Lyyy-V{@D* vs v(( (# ("22991/<2<290KSXY"]@> (Sp  &&&:VPf`uwvvp]]353#5! !#3!53#3Vhg13yyByyysyZ~@-* v sv (( ( ("21/<2<22990KSXY"]@@Php&77h]]353#5!#5!#!3Z myyyy7ByV /@~ ~{ p% % 10@ o]%2#" ! {TxyS\BKLByjjdd`J@"v vsv  / * # ("2<99991/220Pp]353#5!2#!332654&+`R&ppyyy㭑V E@ ~~{p! % % !99999190@ o""""]$! ;#"&2#"u[xy5y3Lhjdt/92)BKLB`$@H  *v #vs v  # ( $( #"%22999991/<2229990KSX9Y"] ]3!.+3!53#5! %32654&+?Yz-ڙEaV=T \W`yÌMyyy!L)@?#"$!   * !~'~{'p* !1$0*9999190KSX99Y"+]@"( ( ( ( ((( (!("(#: : : : ::: :!:":#X X X X XXX X!X"X#k k k k kkk k!k"k#  !"#  !"#?0+P+++]]7332654&/.54$!2#.#"!"$ؿn۲' ĵhлђHbvrZd+78Ǹ,,gf]a4742o@  s v ( #(1/22990K TKT[KT[X@878Y@@_` ]!53!#!#5!3sgȇdyu-yFN@$   vsp(( ((#.1<299990@ ?]#5!#326#5!#! έ\yyyy&UR@9        *  vs  91/<290KSX22Y" ]@     # # @P ]]###5!# #5R}͍y\yy#y @\          *  v s 91/<<<290KSX22Y" ]@D "CFSV   && '##EFFGH@TX WP]]) !#5!# ! #5!#VĠBH#Ι?\yykyy 3)@f   * v sv  91/<2<290KSX2222Y"]@4*%   &+++))+]]K TK T[K T[KT[X@878Y@8= = ;;44?O]] 3!53 #5!# #5!# 3!53ɸ-!?:yy;yy\yy5yy@A       *  v sv    #999991/2<290KSX22Y"K TK T[KT[KT[KT[X@878Y@P       JJ[[Y Y _nnh o o xxx z  z  ppp  &]]!53#5!# #5!#3`D̦PNbyyy9yyByL y@"  * s    991/0KSXY"K TK T[K T[K T[K T[X@878YO]35!#!!53L{5hA7@vv210!#3!7xxB@ s/910#m@vv210!53#5x2x@ s91290##JJ;-/10!5۾f-10K TKT[X@878Y #fxTD(@1&&&p #35# 2)22991/99990@,*:?????9O** +//   !" ]]3!5#"&546;54&#"#5>3 5#"326hFnzjsq_q cjMORdyTP?{Wc""leedl/F!G@$!p> <58"2221/99990?#]7#5!>32#"&'!532654&#"ǘ-pp-_jmYYmj_y#xSQQSyf實TD2@  p3 >210]# !2#.#"3267&4oup~r}zkL& (**هπxTh !L@%   p55 <>2"<<1/99990?###]54&#"3263!5#"32#5!o`inYZmi`b-pp-i尦ySQ,*QSxTDe@"  p>9 ?221990@"`  ]]4&#"!32673# ! 5N^[O%|s+. Xҩwηzt#(?e@&  3 + 5A<<1/222990K TX@878Y0 0 ]#.#"!!3!53#5354632pKEPH :UN NLsyyy5yT9hD,t@., #) #p- 35 <&>2-2219999990@  ( ?.     $ ]]!"&'53326=#"325!4&#"3265jsq-pp-`inYZmi``!!ifySQ,*QSy尦?x@,   JH5 F5CA21/<299990]K TX@878Y/]353#5!>323!534&#"3F@}EUkdy#x}_Tyy5i#yF ;@  K 5 5A21/20@ /`]4632#"&3!53#5!pQOooOQpoTQooQOonuyy5yh9= L@%   3K 5 A19990 / ]4632#"&#5!#"&'53265oRNpoOQp)323!534&#"3!534&#"3!53#5!>32Js=S^h>R_h>nyjayyhyyhyy5y]VcFDy@,  JH5 F5A221/<299990]K TX@878Y/]353#5!>323!534&#"3F@}EUkdy5y_Tyy5i#yTD -@  p>; >210@`]%26&#" ! p[Zqq\\q?>Tq+)/VFD #q@+  "  p$4>32#"&'3!53)_jmYYmj_-pp-bHi實ySQQSHyyTVhD#S@*#  p$45<> 2$2<<1299990o%%]3!53#"325!4&#"3265їc-pp-`inYZmi`!yySQ,*QSy尦;LDU@%   43C 5A21/29990@ /?O_]#.#"3!53#5!>32LqPLtF4|Y3US̵\yy5yok X-D)@C  Q!  Q ! * !''p* ! 33O$O2*9999190KSX99Y"@ ]@+++) ) ) , , )6#E#Y Y Y Y Y YYY Y!Y"Y#j j j j j jjj j!j"j#  !"#  !"#  !"#<]]7332654&/.54632#.#"!"&`qlsIi^Ձq lo@SѠc)w}KF@F%)!mnEB6>%51q^@  p 8<2122990K TK T[X@878YO_]#53!!!32673#"&5˚b',:@> ۜyJyuDVZ/'i@#  pH5F 5 8<1/<299990]K TK T[X@878Y]3!5#"&5#5!3265#5?}BXkc'Ry_T+yky'}@:    LL*     91/<290KSX92Y"(]@ ,+KK[[]!#5!# #5!#ub  {yy`yyR '@[   L  LL L *    91/<<<290KSX22Y" ]@: + 7    &##**(7669 GGCCFJ WW Y ]]#5!## ##5!#Pȋ}tZ}';LyyRfyy'3@e L  LLL*    /91/<2<290KSX2222Y"]@       %+ + + )"""99?9?9 85566LIILDDZ^^ZX Wxx8$*59]]#5!# 3!53 3!53 #5!#ϼ<\ɏJwyyFyyyyyy9'@\ LL*  39991<299990KSX222Y"]@B  %%55<<<<<<FFSSVV]]#"&'5326?#5!##5!#0s1xGqCCAP(1seubMJGc%5yyuyyHF' @$ LL *   33 2991/0KSXY"K TK T[K T[K T[K T[X@878Y@ ' `]]35!#!!53Hb5wwy9+wm*\@/ + #&'  +# *& +<<29999999199999990#"&'&&554&##5326554676633#"3m2k==k2gpRcaRpy1>$npsyspm%=1x^"^10#y*^@0"+  #)+)" +<2<99999991999999903265467&&54&##53233#"##eoSbdSoe2k>>k2^"^x1=%mpsyspn$>1?-@  99199990#"'&'&'&#"56632326]e_ aM``d_eM?VR:BHIWQ9DHL'$uLm &)@d)')(" !')()&%$#*)"v' 'x$  v #'%$*!")( *999991/<29990KSX22Y"32654&#"53&&546323!53!3!N57MN65N_z:?uv=<E{}P6MM66MMzy%yJuuJv(yy1y#;Vo'&o`k'(/uZo'1uV'2}uF'8uTf'DXTf'DCXTf'DXTN'DXTV'DXT'DXToD'FTf'HTf'HCTf'HTN'HFf'f'C f'N'FV'QTf'RTf'RCTf'RTN'RTV'R/f'X/f'XC/f'X/N'X9; 8@   s TRT  <2<21<2<203%%#5)j))jJ##R##dL@  zUU102#"&546"32654&Bz0/11-0|DHdcIHde3/0xDCy-03dHHbcGHdL"S@+ u u p#   ">#<<22199990%%#$%3#&&'667cWVR y.y;vqnd[h ^kӝ%#&%~ zjL@(  vu{v    <2291/22990#&&#"!!!53!53#534$!2xcTuf` Axalyy=y?=5C@l)(*'W<=<$%#&W==<  WC6C  W66C*C=<6'  &- 3{D=@#69&  @#-'9C<#Y#WYW0@WV09WV*D99999999991990KSX9999Y"#&&#"#"&'5332654&''&&5467&&546326654&'whaQ[6ZrrSMSfvhjW]5[ssSOJr,.[n-/]pVVJC/B/Hbsh+/ZZRJE/B/Hcrg,.Y!N-=f:X#O*@"      <291<2<2.990!3!!!'7#5!7!Vb5}#@Y  *v!vvs v  $) "( #$<22991/<2220KSX2Y"KTKT[KT[X$$$@878Y!#53!3!53#5!#5!!53#5!!53KsTrPoo!;y/yyy!D! +|@=, +&  ) *& ~~&{p,,#* # )#+%%# ,99999999199990@ o----] .#"324&'!"$''7&5!27*t /-tø ZZSjZ[[x hZw{LzxNfAe+hdNNZf jMMZ /=@-$ '! !0 $`*`09999122999032654&#"&&#"326#"&546326632#"&-{N^ulQJ|.zN]vlQK~E`]XD_]1KLoYVqr$LJqXUqtE}밸u찷q)@    <2<21/<<07!!!!#!5!ff $@   <2291/90%!55yPP $@  <<291/9055%!!y'R$@\  *v v vs"v#  #!  %<2<29999991/2<2<2<290KSX22Y"]@HD, ,##3 553;??<LLEGX o o ]]!53!5!5'!5!#5!# #5!#!!!!3Fm=߉$5myZu;uyy'yy#utNuy9V'@1  p 5 H5F5 A 22<1/<2299990!]K TK T[X @ 878Y@!]3!5#"&'3!53#5!3265#57mJ;!nDXjc'Ry}SGyyynyLF)=@'! '!* $$*9919906654&#"#"&54632#"&54324&#"324,,:*:|fMHeMIecBds<+@lFWt|uy w9@$   91990!#'&&#!!26773!5ӋX\S~!v0" HX6S4+)X&G/w=@"  a ba c ba22122<20!#3!53!3!53#/XFFT^TT^TTF'K@#  HH 5F5A221/<2220@ /@]353#5!#3!53!3FPy5yyyy5y1q)*@$ *'! de!d*91026732#"&'&'&'"#"&54632VfbwL>.> c ('8z`yNB2F KbPfbN>J?(:=!,k@7"*# #*  {-$''"f'f-229999199990!!3!5#"&5463354&#"#566325#"326qs3U{^_LX `ITeHS=9?K^\96zr~ZYA>KA>CJ=*@ { f f9910!!2654&#""&54632qLCCLMBBM?Vзѷ\#K@( ~{g g %!% $991/<22990%!53!56#"!3!&5! N#Ǣ"Orr} 0 '5 5}\A<}TnD5@@E(.&>6.&7&>.+03 p&A8&;?'.3/6'?;/#2A22999912<<9999990@@:,?-?.?/?0?1PBB+-/.//$%&'789:]]4&#">3 !32673#"&'#"&546!354&#"#5>325#"326N^[OMo %|r+NY|{grq_qmjOYV^XҩPNNηyuMONN?tWa""MSnqq`p3#'|@>('%" &" "p((&%'?;?2(999999999199999990@)`)] .#"326=!"&''7.5!27ZMn]\Mm\ BCoPTAB?tHVZXYYHv12VKu)21V\+ "7@!r  p {#! #10#"&54632#"$54$75332673ZpNOnoNNp|izxmw x1NnnNOpq""+ &@r {  991/0#"&54632!53pNOnoNNpu1NnnNOpqJ@ 190!#!HD *@    91903##'%ugdɌKZ3qtkP-%@ML!"!L""!L%%  L%*! "vu u{$&%#"!  &99912299990KSX99Y"6632#4&#"!!#"&'533267#5!1CYy49 ?/2529912<2<99990@6   ]]%26&#"4&#">3 32673#"&'# !2p[Zqq\\O]ZNKv %zs'фHFԇ?TҩPPL϶ztMOMO.)M/10!!/10!!#2@   { i i991<22990!67!67#`Y`XhC7aC7`b4@   s ii 991<<29906655!%6655!3`V`WC7aC7aR@ { i 9190!67R`XhC7`b'@s i 91906655!b`WC7a{ '@   <<104632#"&4632#"&!!X=>XX>=XX=>XX>=X=XX=>XXZ>XX>=XX!#u"@91990  9%-9N'\T'<u)@*p{10KSXY"3#)ZL= /u@> )0- *( -!'-0  )'!$* $ ( $ 02299999919999999902654&#"'7&&5467'766327'#"&[\[~|їљ0l=7m9͘ќ.j?:l\\\]~Ϛ.k@?l.͞Ӛ8o6?i/Ӛs-@ h291905s+--@ h<91905+-AB?"l@4"  JH3 5 F5A#<21/<2229990@ $$/$$$]#.#"!3!53!3!53#5354$!2p xi˘avVZVRyy5yy5yL?a@/    J H5 F5A<29991/<222990@ /]4$)3!53#"3#3!53#53$2ӘXseyy+Vyyy5y9;a@5  s T R T <222<2221222<22<203%%%%#55)j))j))j))jJ####I#### #    104632#"&pNOppONpNppNNoo=?@  i 91906655!=`WC7`=?3@    ii 991<<29906655!%6655! `Y`WC7`C7`1  #/3?Kk@723030121*@ }}*}$F}4:2p0${L C= I17!'73-L12<<2220KSXY"2#"&546"32654&"32654&'2#"&546!3#2#"&546"32654& Z˾˾@77@A88@88@A88A˿NZ;ʿ˾A89@A88dߋe-dLk'$u`k'(/uLk'$u`'(/u`k'(/u``k',u``k',u``',u``k',uVk'2}uVk'2}uVk'2}uFk'8uFk'8uFk'8uF' 1@55A 21/20@  / ` ]%3!53#5!?oyyy5yyf<@ 91290K TKT[X@878YXW]3#'#դffVr@      9999199990@,               ]]5463232653#"&/&#"jf%G.G,$)1njf%G.H-$)1/F=/F=P; (10K TX@878Y!!v V5 6@  120K TK T[X@878Y332673#"&}pccp}5INNIm)N A  _ 10K TX @ 878YK TX  @878Y4632#"&mW<;y0f4-U$AJ)- \T4myf4@ 91<90K TKT[X@878Y 373դxO@,  v sv  ( ( #"<2299991/290353'%#5!#%!53mFTFfLyuyyNu`@# 5  5 A<2<9991/290@  /Z`]]%3!53'7#5!7Do>ٗ@yyy`ixaiLk'6uX-f'VBLk'=uHFf']F@ <210##  X L@&  vsv  % (#"<2299991/22<20]%32+!!53#53#5! !LWpy+HF*7yP yT-j@7" " (p .+% +% +>;%> 2.9999999199990@/`/]! 5432.''%.'7%.#"32654&?*V,A/24d[J//K o^^oo\9m%. ?n4d&>nI7g꾼kk'<u9f'\T`W@)v vvsv / *# ("2<<999991/220Pp]353#5!#!2#!332654&+`/&ppyyyy/VF #q@+ " p $4>32#"&'3!53)_jmYYmj_-pp-bHi實xSQQSHyy10!!/ /@    <291<290 '7HHHG9HHHH ?@*{ k 1290KSXY"535733^eei}^V@@   { k 9991990#56632!53!576654&#"^HPmdHdSI?WrRZ`T{]T_SZ/*N@) )&{+ # )#kk +9991906632#"&'5332654&##532654&#"oX@ifx|PP`VOX]b[)QWNHBP`idH^o]z{POVRPWTD@?FI@'' d''dZ'' dV3k'* juT9h5'J``', uoL'6Xo-D'VBVk'&yuTf'FVk'&yuTf'FT} )a@1 & ( "$ p#l!5'% <>2*<<91/<299990?+++]54&#"3263!5#"32!5!5#5!3#o`inYZmi`b-pp-Di尦ySQ,*QS'x]xxo10!!ot #    104632#"&pNOppONpNppNNoo14u@?-v1uu"v+${p5-+%($,#5  "., #,(4( 529999999991<2220#"#734'&54765#7332#&&#"!!!!32671&45y54Olv(t6YR5p$ w58 w $>Cķw,16wܭs |@  _ _1<20K TKT[X@878YK TK T[KT[X@878YKTKT[KT[X@878Y4632#"&%4632#"&X<;VU<?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~>: ~1BSax~ & 0 : !""""+"H"e%  0AR^x}  0 9 !""""+"H"`%^ChVjq_8 (Bbcdefghjikmlnoqprsutvwxzy{}|~f+19Rsh/-Ro`Tjo55`^V```V``j``VPZV`V`F55 L/T/TTTq?T? FhF FwFFVT/T7;X1/H%%55^V`PZVF/T/T/T/T/T/TTTTTT F   FVTVTVTVTVT/////9/?'FZFD'9FL/F1==\TV3\BH)55V qV9TbbVL33??/9== 15`5``````VVVFFF Fm=XLHXVT`/VZXXXZVT`X^VT^VTTRoZ1m'   !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~     sfthyphenperiodcenteredEuroc6459c6460c6461c6462c6463c6466c6467c6468c6469""""Vwb#wVNJ|G} r  8 ~  p [  F 3|)qz6XloxUT6 \#x19  !r!!!!!!!!!!""""("5"B"O"\"i"v""""""""""#0#m#$+$%!%U%&'>''(+(n)))*,*Y*+=+, ,T,,-.--.K/ //00/0^01e1122Y2Y2f2s2233334*4N4r444455556G6777B78"8/8<8I8V8c8p8}8888888899A999:2:::;;B;;;;<<<5<===,==> >E>z>?)?:?K?\?i?v???????@A@W@uAAeAB5B`BBCCUCt 79k:;k<$7$9k$:$<$I$W$Y$Z$\$ $ $$$$$$%&%&&%*&%2&%<%d&%g&%&%&%&%%&%&%&%%&%&%&&&''&''9(&)2))2)))$)D)H)R)U)X)b)i)j)k)l)m)n)p)q)r)s)y)z){)|)})~))))))))))**&**<**--a--.k.$.&.2.8.:.<.H.R.X.\.b.d.g.h.p.q.r.s.y.z.{.|.}.~......................../7Y/8/9 /:u/<}/\/h//////}////}/1}1}1122K2292;333333$k383D3H3R3V3bk3h3i3j3k3l3m3n3p3q3r3s3y3z3{3|3}333k3k3333k3k3333344K44457595:5<5D/5\5i/5j/5k/5l/5m/5n/5/5&55555566K66666777777$777D<7F7H7R7Uk7VD7XD7Z7\7b7i<7j<7k<7l<7m<7n<7o7p7q7r7s7y7z7{7|7}7~D7D7D7D7a7a7k7k777a77k7k7k7k777D77D778D88D888$8-8b888899k992929$a929DD9HD9L9RD9X}9\9ba9g9iD9jD9kD9lD9mD9nD9pD9qD9rD9sD9yD9zD9{D9|D9}D9~}9}9}9}99D9D9a9a999D9&9&9999a9a9999::::u:u:$}:DN:HY:L:RY:U:X:\:b}:iN:jN:kN:lN:mN:nN:pY:qY:rY:sY:yY:zY:{Y:|Y:}Y:~::::u:Y:}:}:Y:&:&::::}:}:;;$;&;2;b;d;g;;;;;;;;;;;;<<2<<<<$}<&<Da<HN<L<RN<XN<b}<d<ia<ja<ka<la<ma<na<pN<qN<rN<sN<yN<zN<{N<|N<}N<~N<N<N<N<a<N<}<}<N<}<}<<==IIIIAIINRUkUDUKUKUKUKYYY2YKYKYKYKZYZ2ZKZKZKZK[\<\\K\K\K\Kb7b9kb:b<bIbWbYbZb\b b bbbbbbdde&f}f}ffggKgg9g;hDhhDhhh$h-hbhhhhyz{|}&K9;-7k-7k79k:<IWYZ\ 79k:<IWYZ\ K9;&$-99:9;9<9b99$-b<KKKK2$}&DaHNLRNXNb}diajakalamanapNqNrNsNyNzN{N|N}N~NNNNaN}}N}}-7k-7k79:; serif Bitstream Vera Serif sans-serif Bitstream Vera Sans monospace Bitstream Vera Sans Mono connectome-workbench-1.2.3+git41-gc4c6c90/src/Resources/GeneralResources/LayersPanel/000077500000000000000000000000001300200146000302055ustar00rootroot00000000000000connectome-workbench-1.2.3+git41-gc4c6c90/src/Resources/GeneralResources/LayersPanel/colorbar.png000066400000000000000000000011371300200146000325200ustar00rootroot00000000000000PNG  IHDR szzsRGB pHYs  YiTXtXML:com.adobe.xmp 1 L'YIDATX c?@dX0 ?$PTXn10<2oM }1Tb`x+T`B2 __2> ޗ )0´(     `"R=io{;CuIENDB`construction.png000066400000000000000000000042251300200146000333710ustar00rootroot00000000000000connectome-workbench-1.2.3+git41-gc4c6c90/src/Resources/GeneralResources/LayersPanelPNG  IHDR szzsRGB pHYs  YiTXtXML:com.adobe.xmp 1 L'YIDATX W[lTUhy4TPڊKAQ"/+jE%ʏ_?hlDR@)Ҕvx`%"SZfNs=ٯs+DB! %u/YFѨb͕2!^rb\ȡ4² =z`P]KM 误 $efo44H,V 75b3f8N*{i߀95ihnve8;w `|/zMjWOcx<8/vHArL/M?UV%5?7CJ둯u׮F[Cjd~%\0ޛA]P,Wo}ʊ2]k=ur~cv]c Km6iԏD@F"am+:^E(\Pn_?dKzN܅yY[z,nB67xM6M̃H# -&lX %f.zTn`ƧO[7qη_!<͓W}MM>`!6ɽ'6of֟~/kV[cBZKg٥wCFKkFR/zrF;V 4\!o#' oneGn0]Fg0Cz#VC@IENDB`connectome-workbench-1.2.3+git41-gc4c6c90/src/Resources/GeneralResources/LayersPanel/wrench.png000066400000000000000000000042731300200146000322070ustar00rootroot00000000000000PNG  IHDR szzsRGB, pHYs  YiTXtXML:com.adobe.xmp 1 L'YIDATX W[LTfa *r)j}j9>4mbV4'&5pbM^1hՂ"*[Pܘ`5ua#7.\fG5 ȑ#Xjw340l:YpcT Q nx===|cb݃;w2"22 \.Eqq10Wt09s{5Yka4P]UK.fjŮ]cҎ:Ұr eܾ};7hmiQ*uCed޴i7o<ٸqcY`-͆h t;(-- dUBP#h RRS4@)(:jC}}!'-̘d QP50*,YDe˖-`n(5/XT?3@}}\Bs0^0Y~]`4…(}>5%Bf`Oǭ+RK6T5@L#CEE]2߽{wb* ;o؀6C#h4_*++oooG__ZK[$33?\DmOFoNkpi-mZKk֡:(/EIUDhLJZ\\v9#2d5N?? oJ%9@$qj;W:[ƾ}h"FQ2AdEl"1`hű~͛76wڹ8HDr;wF1>L{7ץL8uLL XDZ0pw YPN8Edf|R0@-KII1߿/I_xRk&`[k/Yh{jWEDd2߁[n)+W$UnpB /dᓓU\2xG*pb{Ӱr岊oJykE&=vO:XOnVOi{!qBneehnV ؽvr*W4 [VFdө-ѩp#uww1?K8z/ixvqNLP[[;k%&0KZ! g`èex)F>HC`8޶PD:]SyS#@R]/ɧ_pn3AddfCrxyU䦳r`y?|XC"MYc܉>sDzA<_>(wW^z222r{PfрH`xy6-_1qlkmFEy93,bGϰ@3ҴN[L_$1sq3ϰdYŸsf<`yƑ 7gE()JK$@g~o :h&N$ZIgt$Hӧ4U74R(FCO[ @ p?9ywxD"bx#7o`6V {[;G OroM]Ce}t䈗͕J e4.0fgXp du-`{pA 8.ˠwC CA8 MH2,!{ yBP( CP $P*ʠJh?T.@W>>4Bo0 &jlρ7GËx9  >7n,_("(}5EP( j UA5P][(%hk;:梗נKЕft'z=`17 Y)Ĕcj100w0CX,5ź`T*l v7 ێbq8& qpYB.yM# x~=_?OT7B8GXI(%$DU)уML%#VoI$ɕ4$$*HHWHOd*قK^D7o) EIdQ6S()O(hJ6Jl%Z*fJ Ks˕O(_W~BP1QUᨬQR9ү2JSS WP-QW:BQMTzz:HC i4.m mH fVKU+V;֫6NUwTUQR?.&t6=^J?NK|>mCSc+4: lm g밹:XKrHTHePPIh[-}OtiiLGrغq~qeqs^/oM%&&/_c"E.6]%K.U^Yz"XΩ'Ƹܝ<o;o/'{$%xlKx /JԠԽMǥ7e33N4Q2e9ĖBl$!L(sqfkRHͤ?H=?]q"G5Gӳb妕ù?B[7{5К5k ?.momח!nC[NA~?4*J 7o#Gᏽ6WtضK Ov?U49ysos---wzm=\Z[6-l[vv,qܱ|NNNYEhE.][v}Tީj֮Ta7o=={uO5&5<;{guZŵ_Gs׮/m Gqhku&zS1pLz/=rDIէhc-Yk|kmm~3Ugϖ#+87y>x兔 K;^\pvK!\|˻+g]=}y۹ǩoNumr덶}nzݼpws^~=޽_?~00QcOn{YvvoiӇdeYp݈șр>z!~1O?_:믞cC%'ߔ|{㻎'3O|('槮q'V|}j[ȷGb3U NN!(n@Twt0w5|!v|NDfAU2" I[Fęrrerk-R?t-wcLΕv>ϟzoJl#< pHYs  IDAT8ݒ 0 ED|;qB]w'L* Tq(imP)wu&ݣmӗ XUAP]{\ֲ,1ù`6wa`]Wz b6Ǒ<ϱ( MQ4M12k&ض ('IBhȎ5(Ry eY4ʾB t60Zyx(>n1IܰIENDB`connectome-workbench-1.2.3+git41-gc4c6c90/src/Resources/GeneralResources/SpecFileDialog/000077500000000000000000000000001300200146000306005ustar00rootroot00000000000000delete_icon.png000066400000000000000000000040271300200146000335040ustar00rootroot00000000000000connectome-workbench-1.2.3+git41-gc4c6c90/src/Resources/GeneralResources/SpecFileDialogPNG  IHDR szz$iCCPICC Profile8UoT>oR? XGůUS[IJ*$:7鶪O{7@Hkk?<kktq݋m6nƶد-mR;`zv x#=\% oYRڱ#&?>ҹЪn_;j;$}*}+(}'}/LtY"$].9⦅%{_a݊]hk5'SN{<_ t jM{-4%TńtY۟R6#v\喊x:'HO3^&0::m,L%3:qVE t]~Iv6Wٯ) |ʸ2]G4(6w‹$"AEv m[D;Vh[}چN|3HS:KtxU'D;77;_"e?Yqxl+ pHYs  IDATX KlLQgPxVHCY4մ$Db!$ HHDb'6D%e!bceAbCHX( V(E zOs;;${=gwd3Ōl6r $VI>@7[)FEKɶf5^a|pܽSMbX{bx~(嗽PZY1()W%bC0%Z6WК1'JADNp~A,^gi/bP J$7v%׬B$L5ƺX_6,kf} [9<9}#lOkv6p`ܢ?9LL;s7Nt`¹L&uՔnMvqph?n1a1q9[Cx 3gJvW͹Y z s_ ^[`f:_~ zEIENDB`load_icon.png000066400000000000000000000026441300200146000331640ustar00rootroot00000000000000connectome-workbench-1.2.3+git41-gc4c6c90/src/Resources/GeneralResources/SpecFileDialogPNG  IHDR s pHYs  iCCPPhotoshop ICC profilexc``$PPTR~!11 !/?/020|pYɕ4\PTp(%8 CzyIA c HRvA cHvH3c OIjE s~AeQfzFcJ~RBpeqIjng^r~QA~QbIj ^<#U*( >1H.-*%CC"C= o]KW0cc btY9y!K[z,٦}cg͡3#nM4}2?~ 4] cHRMz%u0`:o_FIDATxڴ@3X]  JPm$!c+i xZ`(%5/@9> x,3=9k,ߕSTXb)9N]^AxX,: & 9 #'g"WQdhr*m ʀ$@F%s()9!Kߑdh<0ؿuow8 !a!*W- (^|Eszv H(ŗ;֎vt1uB:4XN_54iBAg2,,haJylu?ջ9.] z\R߱蜿.+Z?jy.# a X2ߠ @'sܱzIENDB`options_icon.png000066400000000000000000000041471300200146000337400ustar00rootroot00000000000000connectome-workbench-1.2.3+git41-gc4c6c90/src/Resources/GeneralResources/SpecFileDialogPNG  IHDR s pHYs11(RiCCPPhotoshop ICC profilexc``$PPTR~!11 !/?/020|pYɕ4\PTp(%8 CzyIA c HRvA cHvH3c OIjE s~AeQfzFcJ~RBpeqIjng^r~QA~QbIj ^<#U*( >1H.-*%CC"C= o]KW0cc btY9y!K[z,٦}cg͡3#nM4}2?~ 4] cHRMz%u0`:o_FIDATx|ohU?`@3?Dhև*U량X׀-PQ@?#@ۥ;p[ŠB/5T\ԍpSwLET|[*DoۓoW$+_g,F M*2 _Vu0'/A ! 򦽓h;@YZt)r3Q[U#/m6D~qFϺqI`3wmzO^/+c@} SfWi( ' ޡ_۶Dz?Zs{>;-tVoH'7;\)<.ZEҙiCvwMQϪǑ-J+jn.¯UŠ:Zm閣ќk/t ^.p<QEU"hhMiEOy"D UTQVm  {]ɩ}aᯮyċ(pvǣ}&|0Df0,ɜ4$O Ƴ%\.G.fc8Fa[34abLb1F"Uggp?ȏ'i \n%qCiO#C7QLJjŦ+i5٠PD<( iPRG)Δ d-mD!chRs7'yaǦ+{W&g2 &G[e%JT"Zr83 C Q@JT7G|hJߡjzd| |9p:BZsޟvh7JLk!TsNUop.O V*ޅЌ.ӫNoT9"7b*W~ڬRm*V|U(}2rQ-93R'*IENDB`reload_icon.png000066400000000000000000000044041300200146000335070ustar00rootroot00000000000000connectome-workbench-1.2.3+git41-gc4c6c90/src/Resources/GeneralResources/SpecFileDialogPNG  IHDR szzsRGB cHRMz&u0`:pQ< pHYs^YiTXtXML:com.adobe.xmp 1 L'YIDATX YhTW3Lbc1ƨYĈ{QS[[-+hVT"v)QC>}1bVJ* QcX6VM5.>?;1ڤ;˷o;w,9[v\eG:)C4ݢE offfٳ#իk%+^d,\0(>_MLLƌsyFj=Rp3P9eee5@W:R#FlCB^P\09~Ğײ͏m!3}'օK,Y~yY/q>s 4 I;vw9$)7$!#f^R8pFFF{JJ'G@k<<'ƏW8LN7|O)ߊQFaE!`ՙ@CTX#dhH9s.l8B"މ@v]{qIyygCs_(Ta\nrrr5kȜ)ѲzX7 A\Nm볂˞4[dI^JOt[sNzҥKO~} :  WҤ8#|hsWAWNV\߰vnɓ'$߿Y&Lqኰ&L|ʃ޼y3e=@Us{d47nx?h~^8?9'nPEC <% X%cP7Jx#,R1߄Qnݺo 0" +䆤TC;`/`/ H3Q5 B.@ $pd!s#~<<+"x M0B\t8K@zB@F&S`cbP-`'{[! eDh;VEX0fK9-0IWfH  0Q){`##xFW<+*x<$9E[-qWW.(I+6aa@.y24x6_-"bbϫp@t~,/;m%h^ uf@Wp~<5j>{-]cK'Xto(hw?G%fIq^D$.Tʳ?D*A, `6B$BB dr`)B(Ͱ*`/@4Qhp.U=pa( Aa!ڈbX#!H$ ɈQ"K5H1RT UH=r9\F;2G1Q= C7F dt1r=6Ыhڏ>C03l0.B8, c˱" VcϱwE 6wB aAHXLXNH $4 7 Q'"K&b21XH,#/{C7$C2'ITFnR#,4H#dk9, +ȅ3![ b@qS(RjJ4e2AURݨT5ZBRQ4u9̓IKhhitݕNWGw Ljg(gwLӋT071oUX**| J&*/Tު UUT^S}FU3S ԖUPSSg;goT?~YYLOCQ_ cx,!k u5&|v*=9C3J3WRf?qtN (~))4L1e\kXHQG6EYAJ'\'GgSSݧ M=:.kDwn^Loy}/TmG X $ <5qo</QC]@Caaᄑ.ȽJtq]zۯ6iܟ4)Y3sCQ? 0k߬~OCOg#/c/Wװwa>>r><72Y_7ȷOo_C#dz%gA[z|!?:eAAA!h쐭!ΑiP~aa~ 'W?pX15wCsDDDޛg1O9-J5*>.j<74?.fYXXIlK9.*6nl {/]py.,:@LN8A*%w% yg"/6шC\*NH*Mz쑼5y$3,幄'L Lݛ:v m2=:1qB!Mggfvˬen/kY- BTZ(*geWf͉9+̳ې7ᒶKW-X潬j9(xoʿܔĹdff-[n ڴ VE/(ۻCɾUUMfeI?m]Nmq#׹=TR+Gw- 6 U#pDy  :v{vg/jBFS[b[O>zG499?rCd&ˮ/~јѡ򗓿m|x31^VwwO| (hSЧc3- cHRMz%u0`:o_F3IDATxwmUv>/'$=%Bla䌡p vvٴMlaWc`,I I _N7'_ǺAWOc(qǾ^kセG @d .8 c1f1??lsD=GĢ(9SeUU9=mweY )ZNZo]`9\ !sNwgY$i 9_ѝ\08H9кL&ڔDdW_{e}m*!ZѿukK~ʕK?x3iuzSvwRxso;o9( e){yEXDdGp" iGׯ]kƉ~uc菒$(* DD)%#8"E'cn_xᣳfy|?Md{OIlUNÍ76FWЇv4goܸqx8lrn:􋻖FѾ}l"4 "g1+keLL&g}~i~Ky>ַ:}Z׫$8Aj G}haa4ee;|ЗGyß|w~G~g1*,CMMƏȿ7fZN/2ʲGIa}B(,Kdjj*˲˗/_vm8a8L0I(8eYN~O9ks<# 9פ1z^$ٹux<:7sg\WU3VD2FnwO߿955#ƩS^N<\zM c 1p3Ӛ v;Z:V#oIEmn;rJkh68$N0ED" v9css3 sa LFI"gs4vv^~UG=8)rKȱlB8}vmmٳkkk}t߁W^`"xe*$qF#}ꉷ v-.q\[8@(+rst hTUՠ!7ϟ?г`]^YYY(0A%1sH8GcL)#:5sNDZO(I(MsLeoG|zjlןR 9>TRJݙ#A\4`ɫRjc, yA c"I82!yrUU)eS2Ƽ{q~0qv{;]kmfJ@{}yXɘ 9G8e !F;n3Vk]i\i=b܍ؾ}]'ΙPQW I2 =0ivBN8Nn,ju{o@lY `sm]JۭF>I,qeP7'[ pav0yj<}ol'\|ڵgi^xs+JDB.\^~=ąPE+:ϖozg|2"g3s3)8rk "c[gq8NTj=r~4qyLsg˽^7xⱭ{ \!.A*5:B2P*b<7P3.92/Js{L5-` !q:㪪(h6EQXk^J[MDA!9W)DT%VGA T1B N'"c 9/9c@kEVUUJ*+[("/ zl?>ę^NFa?,TkL31ҘS'_:ۆYgsSanooW`ټ<33{O+=9~לs%eQ˲\^^ZE|ȑ .LOO:u;9ww߽^X `ρ=vIiβ1vƍ{>o?ܻw >|Ç@wJȪ((D䩨{yo_?}jw {e, z7$bLTUs.Pa* )!GDx"!wTC2&c(1)%!2h=! qƍ2 ̃0tז,#qz4cb2 xo$U}/}Y[Vo~H&QE>Fr\deUM/tzv{r͙K._ I# uBF:P{Е7sF{R:7$rL'?_~vBkio5Ħ>q߃opʵk?ѿ^};053/o8Z_d'O\vZ+h4iFQTeEZkP1???L8{677q09㱵+UUyg_֗ڻjf}}Gz<|T???`PYƳ3>ҫׯ>g>ิ}0B9yg9M2SuFpFCyYyKZqNuv@#{cFu$;7w{Ag>8V3.NLq)읾usfi? i[kOJz٬Kι1;'byqcff_z饬ȳ,[]]=~xuSJ=snt:nWqf4elQSJhtMƍY7{ѕ0 8OOϔe*ۛ+Gʖ*Fg%cKFPT @)A^jj7 UJ)99:s֗ܛVvr'gGCNk6Zőg*RȐ"x!tq[}Y,i%(JfEBJaq+$IRJ)G>c"35 TX8qo3֓;gsκ"ƛ,w-u]8gjF#F,J4"Q_FQq\|2N˲, mTJqe#1fyyy~~իo\n5QVp<K)k<7I@Dyw?h4k2B80`,(<vlJ;KF(J8y6&(]bfqL66V2D5խsQ6 U䀢^?׮ M^|ERPҹ3޸6H>83ifyJ3!'y155Et2 "b5o4eWWW8T?W_w`hΜz_{u[>wO4謌>x$`v6Ξ>rke|ǘrxvlf߮KhqmNg+nʣׯ,_ o6I'舩G}h4:b a#zl7:60 $L&~ի=>|PJ}W^z4Mv4'~^oƍƸnD&#Dcg73=ީ<˲:֞:yf77&#WRr 2`xO1i"yȨ4 FȈGD8Y끌 '=_D{F䔪NH6DLI!s%0R<sgÉ5{!P0IQvyDy?7]]Oc׾6/.qεwZ*ΞS?ͫΝtwj&yeو owݻ{|ҋϿtJ{j55@+/./Z_MMI@Voo+g/;߉g_=yסHtʕW^Ɩ+UU}3W~/%q%o޺?/◾͵A8͢(GNGցay֟ +]reuuCzo;tکg^7lК qP{NJ[DyQܸyG$aiVۋIvˀ0@@@bYIAH@O c2aa4c  Qĝ ioՐ#ki^5hnf̫,n.;M((j&Q\wse^/\?X| r{8"//S^_P*nwQѝ3"S(8Qcgff=WWW(nmmf9Tݩk׮?Rt굥ݻ͆1-//C~߼u֑ONo^wnz“QN;00,&r:k` R9xB1 ǭRdyURqSų{2)u|/ѦD{4}[+oo |+_w`R(.\x큻|wl_ztR%[3?O+ !&CWF߉r'B0ƼښPE5x'@風!Ci6ڎcDnkZu2(0ƔeY?Iyuc*\[ػw˗hzvnei2fq9m"xN# $3c> xUt2G;tUz9M;o"QMHsb:tC{vە1tꍫ=]zz2Iqr̙鹞&{qu ^4<^[[k6Es= /q'Ok^r89brdxjaO9s̮Gj$Z ~u|RڵG__?6ϾF؈JzGGgzGO˯>Ү]O?vܸxᏯpqljμqNzyN#CX{}.?w\~eq !:th}?ol߶]qӯ|G/>ׯҫiiju)(H<73g\tQk?lsss0hh8,*X_ TfzTapUAj'K%XֻpkekAHZ&6M3` H"(nrq:ޒ1ڹ#BWQI!L)T1@U:.ȃ‰x1b#eI rƪdIʲLlRZk=zB.K`ȹ􄥮cfl&NBEW-!؞{^]?TJ'E+.4〽vgkks@ 3դ,:rq LOO> /033#%m7===55%*:-̹yU9; sNnu\ v{?sf1a]zy=n۷ϟ{[7DtނhBH"JM&#A ɘ`?ɇ*__G?Onq =ݜz}s>H/'J(0hƋ{v_t#V.;ӧ^?rHS'|??jn,__xŋ?ۘo{*+k]ӿ,Mǟz3'&S :v}人tj 囍N ~ F|ƲS?7??KKKSFUU'Od ǚ?.8_V19B&5q9uԉ'677GR*@;!>y$\ :6jceU ZqWʖޑ`9I"ȫ*ҾTFaU0I4h WTއDPBZ"BQK%*r^"J(ƹ{_&r9ET7ߪo=xl6ZKX܌aa>{_h<7vSnZIcyey[_[_oZιMTѨ# 0˲(Z#:_B[[[MFbH@)']B`fI6$dec%O3x(gtYIPR58 lUUiqFZ[c59ϳ, C"SUUE?p%O|ƘӧO$I^|knF#Gɤfggo߾-ƞ=(UUYՏ~GѸhۃ`<3ƒ$qΗCGʲ (+QȘve^q̻Ra 8b\[U py:s@D=!T\R8Tc d}BBFjNd#b= 9cH2.!>ͽ U5d azk8?_ p =O_w=or_|G/ol$Q ǟg>ϿxW_|~бH&A"ш?7~{e>a:y';W_p+ѹS[^{W^?3?'(|3<?<>)K͸͛+++o/޳gzg~굫WGQQx;?=~ӧ`85h4WV'd2\9n=ݎ-+c¼5sY'A #c*tRe(@Ƙ&")2 80D=x$o/GLDq8RY91^釱RToogjNxd=F5FckSpC&M+68 bػޝӧO^zjo&MY|>񉹹Yį:x׿_WJT'׹^cc}[\صIDgϞ}ocgjL/M-̽k8~O;W^|7oHɵY:~Gojiג *w [Rk\bx,ҌZ|k'qvK$q"$JJ);j{ZaڛDӥko9unѣv{yVjfzk{cnvsg=rLz?aȦYfCIJ(BI.P"Zm#Y7o.izw/-./ŋWמz;8ǍJ몪8vO;t#r!9eYrEOhmeXP^7XoLHr ,xIJوP}_$I|em3iZvh98`UdRvhMM̈́A<84ZVU{q%d74v==ַػ? UYd7nΡcr wvuuu2@QS K37:j$|)=pWo_V7Vw^owfg:҄,D80Z|Bt9O(pyQqwZuk ! ^?<(*j_F"x 9ګ2 $xoeXO1D5fZvRk @Bx6I9cBlS{k6i6jZQ2 I^Go~yn4ѻ$P+[{Dy-*["ϓ$t4zg{.߼TUMfϿrI"xksڥ8Ksss֣qDPKd~ |CRZeg8z$G1@IeBcV̘G$_ӯ6T03ƫԖ,K1,e΀CF]0$GL06&: ZL%8q{IJ2JC%'#S܇8`q"B$2Z::8@:͖-GFj="r`vea`+ *< uo:A{6B"K U?S>[[~sшoݸy#1&83^5( ,uQTɚ1FW{l6SS]HѡRJ90Rֽ^o޽uŋ}avv333@wvvWVff秧nݸ.9g{]ݺu#˲4ٳkmmȁQܸpyq?lyZUE#N^z]x@Dm 4M9kh7 6k.A`Ev{:rZs΅`H*m͗'W|p8t./+M<87^UU ԙV݉\Wι4M}NEQӹm#* ʼӲ 1rF!dTҖn^g̶{Ox<RI@J﬒=lRmpzq#`0LTX 1fu%hqUNfQU`K]͗~U9oPMBfiGw`'p\`Uӫ+ˁ#!sPsϱH01L<υUY0Ӭxv:@;t2;=B9g, Ð!zoJO֔bLs)e ˫^Rm1LGeaj] D K;ɝ'ZctECv c0i,s¡=pBL %`֤! 1VSxksS3{3_zs̥驹Kq}M4Pbĭvlĭ#*o/_Ra` ڊE!}DIG`I*P"sF$K(2rP쳡H k`QF3<33;UU%I2 &4M禧$uQ޽;ӧh+"4͉\:F&|eOYak.c`.,&[(~skf>ooy׷_yy5uhiSg?7>I?hLlYu-| $V~޽f.vZy 6SSSVUQ߻{=ғX(VEDZ*ʺϽfAmE7n\O3gWمcǎ=uWR-3nZ!g?|;;8K<3+5$e`L&IH)?j]Ǐ[m6׮q~wlZ=g{y̥'^x]^ҎpUk-0tJ[a$q:gfff/_4k=Ssф:< kZz4ݻ7+rc6 Ü`11K@IT eC@x0D$Vp##y$ \F(ϻ?Jb3 {(`mnoč9ϊnTQkR2UUbBCd9g2+xk+VK Aw8A0:sG}cgΟ3p! X?t:IOJ4͉2!@rʵ5ʴZ2!ǃT;|0N'ns>#"\\#k\a4Gfd Xex[GI+K)E*0I=7hB4r)eM=c"2=QUJMu(*uh4,JB_yA2.Q0.\8:+fguUEl$ZDx~qa{{j72`kkksssaaa߾}??x~b$"&E-+˲.Ṷ֭[VUu_җr֭Nw~~|(w]gΜ9qǎpፍMԉ'կJ}|[߼qZ眥گ3]U?6t+yN"į^|$"1YWT%G;y}{#,bB(]UAWb c0.yhEyI Ƈ\^PpBd#gdǣD!2'@^WׁTഩ!`)eFAD(xLrcI0R8 vvѣjR:$A85țBLJc"h5sɈ 8%Na]՘%kl+&yg G⦅1>̋̄JOw1Ɣ1مZI0Bi RxrٙZKaLUy$C9.?kιukijk'{|{{kU@z'ޥ] _skΞy/,f}=wa."w}q UD̓#b{u*&9";kGFYaa#or=ܑUA0;=3&E(b2q@u#9 yR0F̺#3TE7UQIUxkv~ 1Ģ*C2dxVY~cys噅V5JS8m8J2u͛7z4ρ<2djuD< -$CEHrVVâ=>{>ڡA>?i8y|Ͼ vܕWc.s_ɯ?ykko?oվǿ{>go/~d?O~/iu+WR%Qj{s贌s ({X1@֒mc Dq,ٞG_ZXZX9JZOcLN!$.s~3ʺg8i:m,ͯ~ƖA,JSđw0 'y%q9h{gX[j4yF4#~'_;q7ΥXȱ}{@1ƥB)O@C(ET6~gRk/ܡcGf9A0rӌqD F\pUC6o!`M3xFnMByeLݝ vmE"H;I[FӎJॡT<կx'(Fn33 n[W1gC2*^;ɰ 8@ %9IltI33Y ʡGPU.+V>NE{ȍJDJ yVP8 282-yTW$Su:gYGpdu B08_D$q 'Vڂ`j~ƙbFX$jh[;)ھqWVPOt{稒J.P s9c1Ȫ "+`{.;#c߱*ӌlE2clU4'—繩4 ! Br3B'uXUtX%x1\7z!TFFv٥مՍPLФ|gaf$\(v\rv.-_y{uҮ/O+o\Rn53A DD30Zolo).v}zz:G7:̣f;;WjDyT5ZA=_@wzBZ BewqL{{g_~47msss$gΝ,Vj׸ـPaPUP2-&o^ By[+ˬ`<?h\@͙q{BBXѓ,K5zμqʥ͛/xՓQ.sw{vJ˾o~l eJsV{@FCJɑEIXV#Gn\~E:΅*Y퍵P1$w8ډxq>K Oz}&\zqg2,ӓ4LQF*R0$OKhCTB:*L1!X75f^ggkt:$KwYQ705,v /xY߱R(%g?0(R a<Gd3=YGАgAhhpzBX,0\ޚݽKvRʥ]sOzHQIjkF# ?7MHdi3gu" 2(+X|QRa+VW;Ott.+ }*%JKU]sю%_ڒ VT9Wt?;V0J͠tgIat C-'扻JOȋ0?tϮlEDz  ;.h<Otnz>+` x:Ej.G/}2+)q$ȑ7q5$ t >Ί,MPZܣƘtSI<uA:ђ*jlٷsgI63pccu?v_V +nv$LMu;=Dą jrLeMNn#+RͼԎ&"NunƛRW ސw@LwtW`]']Nƃv{3.\^YYnGGa>eF $WAv{{>{{, ntfgsY߱R+()pڍ_/^ UR޾;zX\IdXV0OZ6 9XZ*0JvdmYLe!07_ aC㸱v{iǀ "T",- d4Zv>83;֭n8IUUz]IP"86sݞz3u_/t{q?3Y6[cgq"7NqU g,1+rR)kmgSVqJ#c`ъfk{s\ڵ&3+#,ٷH( 8$N򛿱D?#׮\=qyQiș!x߳|6#cLpfqF2"rl(333Qwo7]u=;æ"*SWߊ"k̷\[˂c;Cyb[k^P!G"=(Zk 0z(Zy'L$s)A*i9Oy:d3v=$`F9Qrw;2B IƈU|1Vйk~jo0J9팳O4ڲ:Ckݝ6U }G @$1 ڲYPX6-cfHlH8yeqذIJDJ(J" Rh @ߺuf5sSD Sd-`qsvsߞ7[wo~W_PFyr~y4M1̌W )kزmyٿ{]q}>tۗ R^X$FRoY_W]8tmfTS2#Z@DD&@d%.xqQh1瘻x7}" 3{ Vkyy,UJh:>kN'K u!Bv.4~3!V(xuKs3}kq!v@(P憗 $$&U^<X@ T" #{!߭<ɭg֟'Ow 'Y_|SS:7h|~0>G~_xY;`Ӣ1PZHd˳ 6wKNQjZܸ_՟3cǎҩgX3gIab#ImJko}O>p9Dgx޻vmRMJECw/X4(_/jum 6vf!Hݔ:,>73GB^m?m8:d۟i=9r;rZRʲU5ABcL-Y"5{ .VJy3]1 #DA} E4/MF&2Ep DaDBFᘙg&z)Ofzɤ,2ZDW(x]|1L3no. U+""jK@^<_t,ejR5Ryaǟ}࣏_ O.x1YE4_|*QǟzRyal'Oy'QS^@yat'OFxⓟfzqU׵dp4eY$M/ܹsMUvhզUy䉃n;2-ޡG?5e~yAi'Cf" :"PUw7;coQPk H0P$0D)k{Ξ`> zR, ۻ;ZԔDP&IQ~Y^]sn:4%EHXh3)"70efgfNld ́C}DM&a`"Rg׉sֆ$ˁEw9\n=>xig_g>usOrO_x~+>On '0F͹OF8oOM?uuw5Mu :QjR! iakeֺj$ՄvQu.to߭&%@C'(*md4j5!T ɣ,{G 8y9W+ӯX՚T. 1dcRY+M䇂B@Bplc'"<M$k`!xFB JLN'/7-tR7z[;{"z'o'EpZЊQ! jpVɇUoNR&,Bɼ^^@%@Qt61sDO\Hs.xXk=B"PT7MeR4CE&/\RDIJOuLĈ4|8Uy xUQ)3wJ!*Lю}`!R)Jj<)p-ˋ_P,./,ƕ#VƋX^[ӧ~t]xhwE'ZTFi筭'h<ymQ.+Mj<[V1{%uSh@ !ڄ D8[dy &Mq8su45E A$N @0i&Dv084Uw R DmF Q@1J_|GfZ~Ge0>{ololŏ/|^j2!e%ipuŲPѻ{ ۿ yp^ǧPk!"!$`B"RZiEC`ODJi"!"m$F @dBgu]whvoS:ݮX1HKTU'(8|mtؚD8J)Bq<Zеux`Q#$a iR ^F$  Nw<Ts`ۛۻǎNƷn߬ytϼ^|c)Vn=~]vawkxqf/(Ji#Gi-ZuUERF׾.b4Jw>ˋogԋ+5aFYR]B =2u;W "(r쀘0*$ aBEq4Hv7;rpciN6/ͤ~⽏j<n{rҕ/}Ճνq($GvZk샍֛yVvh"1v9'?ǎKz kc㝟E [1:Mu*η0 !} PE˗/7.^]Fㄓ_Ttgzt@G,A` EϲuJ `IB9'Y*F"˗^~X.WXa|s1H<đXmun^Ќk5zw;` ֚P@ =Ddo*kƻ[Fs-SEh}UסQG)Jɜ_gڰ@D 5 5`@P@~MPw\"΋7^?~˧^?,"VϾ?v4-Gr ^ ;y_iSP:i6y""F<}ގNƎ;Nޢ}C1W6vg yx"g"q@]ĺgSGjsc 1AI_~ժ"(Nw ⇝3/IGRM (9?BaVae ɞ %*@'0~@(EvHT5vw{sλ~ _sihYG?yk><^)j!]?W*NJQ"D$OTv롇^gI=nWtٯ~+}r2%,l49x<$Y% 賀#-hA@$AE Y= K`/@$E#J}+/¸nl.^}<οq|o}]{[ۓյL`~5nnǴ2I[nA!Dy\f&@fY(nGv&DK"Z$",(3onn_|x[lsZ[UU6ƸxqfZiE'y$&Y]Y?fxڤLUTt:6.|ikӌ{Kd<>n{hPF-r;BNv?믾l,3_2ܯ]6T@'I*M/|A]",jrMRlH$D|y2*;)"IxAOǓ4MiiWWRt;]TgEQĀ,vRRO(kfֆm+ _qF5!;Nmy(Ctۏ=viuꦙ9//q~rv;~]ǎ޾]V܈ֹyYNivuΉJ64Z|;ˮnq]6t5i;Y_nZ~Vo_s?R`08;{yzvo~J/^ ̊4HODmp.˒TdK] @b1F Q(QiE2ƠWGy8 EJY%غJӴMkuK+Y^^6x4M$IZVvEyСpvPk]YYiFa"ZcsLc*V!Cj L@Q@(o;#g?ԧ>cOo=ܹ~[pw7'ǯCEAj"J88ѴtEIեM:heisH'|`Joy~۷#Y砈c;E0&I$Jh\82i53vV1(D`.Xk:_4 v)>pyqmҥK[#/? HW.I^vIߵeD3m_dÇ_ 'fD$@`," ΋L,*`RP,k4j 2depFf hcm-"h(I*1i:aQCj'A'q6o(jDzmʉ؇I2"Y2 *lIUE"z}HY'0u6Rb4zu! Qd1WIфāه`䉢{~;=2wutx)\rr^~ɓ7wR.01 š t9@,ppz2nLLĻ}+[ۗ7Ͽ5& _j["6J7M"±wiy{K]uJ$51i,YQ,[Y*J"esg^y78v`;]7^>yggKKK++Nulpփ&7:@TbξynX Nιy`4MSc )YKņS:*RŧiJDi!$H 55LHZ88D2:R,K!"mHkD#XDD !l>. Мش?d#>t`ABZE%$D$ADLT/D"I3@ U׹<,BPucRZ' lXy: ڡ _{~{??'S/_uuryݨgOy;rYY*r56 JYvPOSr׉^ۗmnʲĝ'\9wkK\c$ĘNe)&y-J3(h爨$+%~E\xwjIN4t:>xNJ9#ԩ lxo*ڭV+ Cu2I=hPE!fDYi$[VQzeUU4Mu4A##+E!&B(\!!Fr2/w.>PJi:'$B@I֝^|JH!$KUy8S!0)Bi5}N"JQj*0-beL7."GvPPZX߼w흸˯>q~ޤŭ>~s yQ/0~>ǟ;,99+;u_W[7VRi;+gVş?}˗/^\YYki[\K/+w8GsqTjD$KjxJ)/HCiba2Iju1.:#ҌFS PD$Iξyf:{g{8B GNG͠nv 53XRIt]=jc8LSDv\^^EaqUUeU!b]U^ع:,OAM0Q*U,iV?zY>n ^ڹNZ푟y[| ,pܨB7',"wQbjEYSW% )%,7G?P{q9}w}SE')&ӑ6p. P5 B9?i^s45qND$Q7ϲ,45hm GHiᰣPF2#H!aLG6[~2tڽi:4hKiOgGq8ԽC̵ܑ1ढ़OKszya"qMw5 w1{ksTfXBYYZ^8x鯮ln_W;UDݰ5@i#Zc[FQjf]4MÞ`'؏Ԥ9WHi/zc4)E{ Ip:gk<J44M)4X+FRhpcQ4U56B <˘ꀪRQ 䏵v6D2c^2YWT\Y}.(I<~oon?x7c|s߅p(fcI4C"r[M'{Y nVms B݈2:KR:ktc(jR h4꺪a m,&5Zͳl޻vnfh{kiJ\|sNiXhY++<oͪls"5(9̓_D cKc9=,0bdT'%_;u0DP3wcaZQ$h"D08HT/ cPnwgo_٭z[W]׋li.9ϳN'M7/Ku#GdiPN 3N@j~tLҤyκ{CF)3 il(|لcVT(Be}sbD "4WAD*$(TynL\`>8dy@NRͳ|T1, j s2ڛvcټ98{2`hJu-Y^I49W$u˿r֕NyYSWA@Jn@KWE@EfGD0k̍#^WFߞp}3ad?>-!],Qn[[!R|z2=pp3~cΞ WN2sԍRfALn@jj1VP! A }6,kw;BYġt1Zu *zd 0TVHD|7Ӭ8.> nɮl^.nɋ"2m3K9(1Y0MӄKYo"y(1fnl0Ax9B 0 O!2鹡#c!(I 7F#wV8۝ ^bdk5 3WK}8%$( Ӳn4Lѭz֏?OEɀ%4X$."g~랻z6._>{ #8 8Ik-W=~{Ld4?v#nO[JSJt` 8v udKg/]9s*hMz<[ߴ{H45Jhk1H 5I",OE ޵yB@)Cn?~b DـpMSGS3.IE" hfH˯6e+/|Eu"f3YHy^W%ysi2hRA Pc1RhP&@0" Q CD٣38>8E>ukɸstދ Vf^xv4ສ&(cT^[[퍍_>EQfZ't~Rj]Y*'Of*OWaS\jxV< Nv^:yzp8|oM]UF A!4 TF`&3WAe4}5(AEB ss!ȁkA%(\kg$`WbV y>B*Yh4E!kml23PWmx<8>E j,%0Q*rӬȋI豵jZu}}V&M<+;{jk媪,mmEDIru|r>S#ԕs+˫+H#8$UOY+ HJf^6IT D! A5 .?uo'",Οc$̇?qGn|k33]y$N[W #ۦ4J7.^`" P0F1i.W{UUZ3ζZ$Ii^Y~k֕X҆%Xya>fWMu)*_o+ME3<lJ"" #$i:=|d\zҊ76~;wvɒ Rvλ|U':ʗ|[I~"KVZbst|N^>}[ʹ.GU$T#+Q #VZi@(%r}L Y(>21^^ټ<TRf("cv̲՞{9~'zgowwo̜d< jfZ罈ZG&9_W!03B(l;SZ뺮E)y[7 I@ cA3QYf+utn13B&KkgE qd`0ΟkE4ԭ4U eYjN6YynLIZ2{Hp#+{'I4v'*_'s=ZuO= u}0Ck̋EKw{\Ga::Eof:|3UK1KYQUͲ1Xb 4E# ]@jN|l*'-mUQeS{˵o@+T.cCEo鯮!\OGAyrei^B*`cv^H#;ip!QB`"#! (+04MReYw'< ' h ,Z}GUU{{{,˙0&{kG5uHt8^r{?A [}PJ1>̾kDzmA1(>s;LfBZ.\(J!Ht 3inqދHE4˚"t2ʭ]f`AR $I;vQifv]EjLVصImԶ췣}2^ǟ/OwG<9c]X-/gc (bkd039mQr_`H`47gao&B1&"F:=g<̌GT$Q2` ΦY |MU;cveZ'Y$I Q `&>XjcTDouϲҊֺj:톕sL^_V;xi{/Oo3x꾛of$m @te1Q{~iw4)DbqN9o\WR jUS[/"4z׌j+zV]5ٽ-GjT ;֤<2QA1A  BVfmU,"'Tko,"˯`KD 2<>}h_ ,--CPfxcRD$p|yޚBsWXQ7㯱0fde nDo(Mt1&8UkkFfR,fi\R{ @EI,AUwu4t8\|%Io7c'O~3Yß̘ןm#O9=6Yle1J7j^YsPzya4ރs4_|g>I7jǿxßP#V^n-t9Ϸ|]H;0~࣏>^*啥^w0ܭ{7,I:8/IYiUY<3lBR(EPޱu?=~)[DZDB@:as3`oӧ˛W<$f{VH*uoL;!s|gɓn00~ß?2CM""ATKj5 @)]T#-P*+LFdiwLե ZeJZY{,?TB@+&DÝa(g_;kgTI?V{",S6>iaq2|itӈ_k,y3^w |pXuųk\z;x! cE(%H۝*>C=riiCW\9p?XxaW?)AJ7oP%;g(-xc A$/xLAvcTk}JӂBX{pyk'^z.KS "qgL TU9 CFH9x}bb{?;i!GhE bf$E ^zߋ5ukHysVj9Y=fPP&DxO[Vk:yʝ*N_?4lm圈م@ySq5ClEQa@UK&z9"F j<<ȌĎ<>:[NƆb#IIIgIYr&よR UWY(H (Bd*<3x { /#Z_E\.s;ޟU"RE٪6h"YB<\]W///ϼ7O'Iɳb_^^UӪ.`oϲOT⃭(#GXYͲl;N&"=ollm vy}L? e0I}oqjf0!mՕp#KbCSpQLi`P=#110123/ADD1jA`Q>06oCMp8p$". Pf>iB6 0R@1ƍUѠ 3s Xw?"pG:'ca24Թcx|!XD Fd`mE@k3UvԨE;'J+0y^@kmכLF )O3Ԏ_;}g?BMFւ5yzlj?ًهB5CDOʪz;$TUXٺs#|hyݕ/C_x᭸2؊m >uy=?p5?yfnF_}׿,bM[ ـڤXB=ed?~IcͭƖۗ& N?~UUu+kkkHW~[_[WzRF446Zijv'z%ZOjǙV (D8tɕ;o7no>9ΛrAj2;Cn@i2UPiW0kJ0PqLB@h%4113p@dILob&_ 2ƯD"vq)799@Gݍb2lܬJX|FE,M  k3sU"J+kd")DWN<gFBzɤi>ɴR QFz[Ϭ\H !{D>OǔGn^ܨy-&57ƶǟo~ouZ7B?ų[_ktS03 z?c>?^9+˩+ YNJ޼|ec=ϒv,/ɓg/o !#VP"1NBҕ^pQL+Z:_͎\QV格0x߅~>oCa&&"b Jl:!U4#ߠ㜝N+EhF"5p. D[EQ bUUEQd3sdq'%/џ`$]F8;ɻūVz緯m{ո߸#Ooy<QR)=B{S8t=r;NۏSNC>DTx:!w_]^\V"}FʹLe:-na\1¶q[//ϒdIYWR凵1XIQ4 ]$)X  !(E@!(+@ !k Jl4fق B huuI@C7RUvтu3i鋍ņiuuwxvv`) \#%HP6( -cBEBt:MN֔Iub<Za6x툨7˒$"NvEQL&o먱Uhyz~GNziq['>ǟ=}C&)-vPX<~}~wvp'UP/ubIw.>6In߰8)ʊ'zff@Ԥʲ_w=?~ӧ{1.}g] ,1ll˫i3ݻrykss&ʳ^5x2m7 oouWM]NPu \j^Դ,1@8®uY#$Djp-!+ 5H!_,J8H8 B@JѤ.o\R $bRV.@>ziE8~ߴ4M5-dN "s>wh("xl^Pd/0fb1 c4<[GWiiRf̍>v_if0 Zk4MT{3 |oOK {}/`w~;;x<>s# ݶND1&My`www}}}V4&7b^pQFn&"F^#9)ލ(~20s.,$@L@.F,Ļ#KT#⁵Cvk"A2!$ys,CiE(WXT 59X[h@B `n[m];wpwϞ=|KΟX?G[Ի|{eո0_0]ٸ|k]iE(`_7.ghs :\NW&u٢U54 l9_2qphU`Ab$x|bK!1BR@@",,Oi%lKKj<ڸpV@iY9vQ#坈H4MeY//ujNjจpBܵ)<ܴQ^].ưbt\pS0U *dN?J7YZ|'@#˫@4MP5Y\`mc]iu1c(3yrU3NZE[k)xF^D[;r*_}Cɇ*ٳ~[ݽ/_ި7OZ$ɺ{17z"VCc ,LQ;-MUW:TҕT*ISeUNDV(r$vҌ/nMA}톓M'5J%DeHBd$AD`f\ " L!eHk RlGêHD(&T LEP0i3g湬ᡵu7m؊n~u- VXykIE@E@B[1~ֺ:˲~l\=x)//}?~ĉ'+ѬBʲ"xIo2Fyw~7wfʙgv_n+^c0tlzx/XG8ΒUز.Ӧ>U;K\,MU"ZFM~׊>DK qRRbp.`, w @SB7':Egow"KݴV)RHf>Fi~<"2453,B.F0__ܼv9ؒ+`c՛󠒃Pf˕RQrqAjvdǖA2-8DjWW֮r @B@ @00GK,H$ؐib](&v2à$ FXEh־vE865MZ) 5*mcyגoUSCs9*}(}i~; he .hF8e'u]+u:ݦi%@HDc$I;XޞsPuQy̲LiZt+붱E6Ze*?,b|kZ]e}`u8 ͳh@q/h8W #}!L\! ̀X4Xt-C-/y'U):Vhy?1($4ͽ.g_Jjg[rRƆt]2&ED Gn[[&fgDj;&>\pS;vp~oy*h;dQ;؝j7Ycc"$|W>J{SYm\lSolyvrX=z\"mot:\cn=c: qtk 5 - kt-,@qF e_$=X Dyx_)hiH7\ Jk)XlJFQ)7v~;/25EN%Hu]+$9"p"IQ&3Fq>Zu'Ib)2o3[x!HA}p4D Bu)T *ދ\%UU$i{DLcPMkbK24\I iS Ku{er|pg0m:.\\ZY{3_ҏ\[]˒.u;n[[.V+ޯZzƤҥK;/|aGϞůy!;,;}k윟j[~FgϪfOjƈe`QJ[ \ 8 HKe%*H ΡrծW>ݻ'q}|hlVZ{T*McRV5Edi4Tb $fk"_ZZ&JH)"H l 3 Bc!jc9" QF@>R-Q.Pj  f̟lMm/2}!djjjEX׃S9%15* X!gL+KZk)Vi 4B5S9צZ"DBR+c @|4a܁V:\L!B(,1Q9ncV AL"`R.wةkY #kt;Ҫ(>0UUZ-'4h-델 rF= 2UUBw~_eeY_. kUU * 4M, NMOÍfl)(1Cd{[-A5eRka5J*$ѾFC m%l!3 t{_h힛߷d<"pm*hhJ F $<+ȰΐΑ.`nkA/]kFSLZ!AfLubL`ઃ}/>V#Q`cʦF"D;&6qj5 B\YdTFZF.""b <_kVTeYJ[k-A0 /QG$Hb1"Щ;s :![_c D⪪ 1Xe,w]WzP7r.^Xdy2 3peD?ݡ SU)1y2e!Ϲ6r~W6ԬnffF1&Fc8r/p]ι(FQYZ# zݙƄP=l)"Z&6%nkMoDRli]9YbJM5H6I |nkvnmLv۽+2J龥"/OMY\Rip0I*6o@`h1!![5hiVe ܼԨZ,5P h!\T>rzXl@+!Z}vv!rL kcygJ_98k,D 0#`N$˲aPT%8#9 cV}y1\2ԀC)PJI);a5:b&9ёV;`0#JifU*)FeՈC`(jk$ c Zf2@4u#7ed-`"< .E{^#\ @06 ,JsYnBWfڴYUU6LByCw6۔rF , (}aw/xyZyBF4Jb!H; TJji6*)dqRk!d 4`EF~1(PTBXhZ=iW={&RE5@7`04'ZƀMcLۥ.,,,,,pW Ͻpl!/~ٟ7^wñ^(5aPU^G^PVPkbެu@*" 3 a^(M[(4ϋBhuLC&ǵFTY6 _ZxzjjճWuZ#ϋ,wEѨ=SgDKU!0qS!@Z! sv0" [,-˲**dvS cMLW?p=pM7_W]uՉ677߿K$a͆b/\'?? |ƛѫ]>ss JI!2RT9 +rcXw|~Uj-TTERjM^TeYZ*E%J0&Zpm=I D/K B 0aF…@1hh2r 0 ÔJ D,LҚ7,"cB($RJd42N*k\obȕRy( C?|WUaQeVT@q FJpЪdUC}{Rp1ߜyEQhm)ft\#}*eԗZҁnRJB{w{dQNM67׿/~(X[ۨaiZBnҮ0wۻW[2A*:J?QV42@W.lo|+ pa"A^%M7W7V aWOת7'Z!&{~# TT!BbNCJ|s0yBvgHEƅ*/|[IV׍Q")1E0ə_|yg{n8|WVennn~k__]]]Xg>>w^]fy, AcGgkBHI=t5@`)U9//˲7tK^V;m/^iE~M)ղJnSL7O+bb Q袪 H(Bq~55EѮb4kk{?OOO QZ=]*0a2躣7 JYHjqcrzj~곿{7[O>) =SJ[kzg<80d5v5B?# ! 1F*-(<_X8lvhY ZgFy^l_X=0OW^|]Y&-i _̋Ͽwv{|bu Z{~ZJTLskI( aYxS]w' J!claaq},/1jȹB`1|W^9:8瘒R%V:\4[8_Zv [H0F*c 4FFK!aRJa2 1f ?!$l'Ł~/SMוJAXZ)" PRW Z=MSBD!h#2R))n]GS̈d8RXBqб؆cLU`{=BAhAyVI)76V8 arCN<{B܈k,CLq@12aC垏R$y#B}̇;I9KDv;72Ag"c+7|Giލ"0BQB*] `,)esJZ=qX E[DJ5RZjRQ'Z:E6RU`(‚vy%jZT(4m4Zk)DV8nXZZ|駅>mnG˵Z(*9cDkM)s.t >v`1ɽ>я&iBXJC) \32iKƔoDTZ-om'yZE sqV$Y-JjEP*ePs!TJpYv-..XO_?0 R~|3/tlޥ"d11<;~DYV8 l4qb-DM':Āp VTlZk>#T%PH@9Z_sv}l ~Ȟ:sO/?O({vG_ ۝`tv&ڍG:fUw}#( QB1F^ m$*aI/_ RB@<[3( {U+^=^\F,-*l#,! #Y@FWZ` b,G8+b5JUE'$V1Q`DQFf>H Yoy4I (4˲lv~hPo4w:JFiƔTRe<(A@eY>,yŏ<q137yWw;r5F o<He1.A,2JcQm $0Y0B驉c-$mMP1}~Cxo}f;I2dd1ۼA/<{ę̹(Rh"M2PfYCAd.zyY *V{¾}wLܕr=ұng`!akS## JVRPpEw ORTMӔB,+ݖk`N#I~䚫zTEqjc3Vk"x`G"1Ffͭ흞DŽPA<[Y_#TU5;5]lU.=+! 2ZcpH? (}ߟ ZcHAe(=|4MxsOZ-7k !FKq1@kmv%>؏BYUz*iJJk "-"L e",bnjƵf#- DhXɲ\+a.b>!15?S!6Ea 䘻X%~gϞ_|(/pۭwϜ>qrhi~};L1Fa;LR1)e*K]BYvmmQnCAogcvgBxݞnN,_Nᵇ>|:v-:Ō8fffzO\$VTmأa&S$Ld)f  V3_UZ b5Hp4|@{4Yz<2T%,5_`UUh2Zm1 $0"}_RY: J)!5Qm֌!L)6Di-K={LOJ =JqJ))JkA$NzPƘ0/*y[; +e^ zAȢ(@l9*l4]{2b*tssvypxWk}GR9s cyaOȏ:s!32!,(Vjk,4  k!004䙭 Ea/O Jd W($y\' ƌtƕ!z.=F)&im͉߃꯾S̉n4Mn(o?@爳NWo8ˆX8URygR .K)%2bZ|^xI0v-d*-rmeE+hϯ0ivq-X[aej1 Uc|nN=񤨚~d4"7>IJtW\AR9llN.qӭ{'Ǐkk$)kp^d B5@6ka} Н˚qB !J)%ˡ{Y(-V 0 xvvvcc c0sRˍ𼨴J &qB.t)|P" mZyf¸b޽ªy)`I;BeiR6 bΎ201%@D(Ӭͭ-I>,N^F1}^cS"[=wg{/cP=Bȼ"Xh Ž:,FC >[R<ȋ'ڻO"Ƨ(r1ͳZ2Ae I* +RRRԧM}i4jw}Bh03%Ed$Ij04)+{^u~}_RcZ7BmbRBLk#yǟzIBXgF Y7BmnƘ 8B?q׍Z*LZ]tmmbg4[YY܋&,`Izuuu9ܶ{n fo d\Q {nS~v"@ؗ *v AHt{8SV:UenP ժ2 x@11PRJJ v]\z JSF1cڨ4όRjk fZ4_^^q㨨pXkyQFl1;;;Rg ™Oj)B`[$U{qVHR:C=u,!jwi?֨(8#m?'[+Y*<wyL2ȲB8j @"[ch B.X+0(VViAVc,~{ߌj4/f7w9c ~}7|贷iRgy i,kη,?N=nI Kqs'(  ТTJ)6J)A'AUj@ `UaXk6v)jZ011qܹJ 1ō"StU !d8ij9 .CkECaLE 3i C4 2<"c5h`ItaJ%(ukM~@g;; cadڄa  8og|ރy-uppVg'<j!ܳ4A翐eY[Op??3?v)4CL~#:l`B}ߟ(!z8V}BB)~1! !X_X:s&)r~ӽ?ٹYj PVNrY)YJn/?/]9 ,\[[[\%VRF8g a>gkQzܹsEUmM0dI,fgZfވt0J3=ߓ*o4U%sҲʶz/7ddŬ,^ywɩ;CK;}SIUH|)ºJc>EZC)6/[[Y 0Qq^ fWBaeUa(bFDE}Sày^,+7.2 d / JJi2$95H @066Bk<`Yɗ_<(c@k.Ejb^ t2Jv!mӯ@,,˲;zݯp|caRIhB''WVVζsKc,ˬ,F"" _~ojJ ǬB+/Y2 t` B dAokC1|3/ka~~[ns;4z" $ISX+ʹ+9jjUR*6ӼHBK[av-Ve1Fc0e !~TcʒRT)B0 ֲ8^x}[[[vMjhCKñ6v*+)AgJQJ!&0kqRk BItw1)ep( qU )RV<b0Lp-F*l~aWP 0VUUBJkE%EQloloSL^B/ .IJ)5]O v< Y y)*|n]^B|#}ߵ^M )W~W7o~z{~a$vI)@ !e- `!(h5L}O8kϮnΠrWH@YFP[nc(>!4W['O:{lL<7u̹};mΜ_WOk$Knhi{ 'f|Fq2S/2`1|8H1B[[ZS'@w|_z!HiYVV,˼*Y1hNLLߵwkusucCm$ @{^Z~( 5ĔSSz0.l3RiGrX`X"Hh#I11٨MdTyrp v[3iRJÇ?y}ϵTeYB*!\R.=S?S?{,8#:7~}OW$]p.-ڸ&114mJ~WZKߏ 3FݛTUBkkkV 0=yӧA2L3 `  *S8GkZcc-s7|KwtdR~s|9˲_~7a+X\ivw9*B1vrrrkPa# ccf$SS3s_?iQ^?13^ڽ +Rpbt41|hv/rjΆ5O֠i ˒lcmlll.^4lYkӲY!Q 4Y lN;ZQUs3YFmnn"[MFyzK[ ,eH*_󚻔н~WVW7633W_sufq)D;ݭ.TJR )4@=B8e2 (Ȍ&i "E6{v UmktDY28lNOO75! .xXd~H)}}e4=&Ӈh: I/՚<&+ة$a Xe]ڬ*bƘH)5JCD;Bh.EXk1F!/.ԋ("cv R/oj(֍Fcrr}@7ߕyt:&fJd` L ׻$//j`0|+#{煄aa=ٳg!kn Z0_IRΥW^y7ٰ^p +J)Y+mZk㔸q 鴍TGyzp/6~~Sz J1#fZY ܨck׮ζ#~''?+VB!/ſ$}jj i2 'M0 r~beB?MH{͘/L.,.n4kӭRy׿}S,/AFFӜl)4LL5U7Z!EY XT7Z-h2*[kBn{? "XK(rBTkpY?CDf疖RQu$Z-^ 9SoȴT(-qd%`δV)bDn)Pk4ڀ31%%WRv75>ػϧ\k=ٚNMM-,,[^zi}}HlXiQYkp4kMoAZxufJֺS1rX!ɜ(s}<BUfgg/>^&=ynmUU/?^prresqAw<]w]^眻~Ш[^w!B3RehjoPJe ް_m>qx "T; fc.i:ڢƛo7V7y3|S7Bdj@RhW(ƕTJM ڟ?jkrJm3+w<(*h6 ޠ/de cǞѭ +7v=[#RN2ofa[o+k=dNOO9rdff.IBT}U  a{>v'^=199EOaUUVKNֲު%^yjm0 yB`ֶhbN:53SEQH-h{Aݱ0D0@@(G|Vk~#`&h!a(fY1bF]7^w`0xgágϞ>sRiDA10&y,0FAkN1QVzkmW_yJ9Aaq?zKɱIah󢄄B0fY0xlqVkq@ZL5!L)U@%b GZ0q)yKQ Dj5y䑋+/?1w]߳$I4}K!Qj󤬌8٩{7/=%WMWhFץNj\}L'ddol B=.JIz9@PXzړ'_-˒+gN[R:(4MwlT؜!qHZєzs,K(r}7!^٢(\x0 ǬIgb2X]+owc2'=- wm<%krmp jn78 Ơ!D J{]n{ܹ4 Y#9eWYwsye7dKMBy^Q^$<`sgO4SO=3Ʒ_xk2Z]yO5<-WZS7Mu-A/ɀ805%Be}F)a%󄐥,O1qg!4j)BJD@ , B[N0*eq4A7bs.w|i Φڌ%B qKF>774Ke)jQ1γrXe,+w( (z.JC R#ծz8u%"BxVTN3ӥǸ_O}9RByg á+RkW9wN)t8:!dee%zjyJogdy&ܐpiWca=\w -a(FǵK/>x*W\Mt<:PEmrJ[*I)Qcy?Ӌ Yyw p8E4%kB( yLD Tzׂ7̏~S|ށy^Ds6z(IH (?#}'կ~뭩f #UFU Z/lm:}FVzfvk+xǶw"dP5tEKUER eŒ*%O>㏯}Pe-R\s<ըOZ(kaͫL@s|I-xލ!ao} cԛ[[~iJ /UUQHKCBPEQPNGB/+8AIUUnl6tԔ  qꘈc"ws=Ч[#ac R'sT8q\(R=yGA877WǵzdY|yQʬ,SnobE0}3) le!aCB}$rc:Bd,[zq)%{/cG܉@`t@"cyQ D)WRx$ c}q/ƾk{Ύ["S!>g\N].#&`JiYeY...lnm|Ba\}լ;srl'!ΠR@teZJTa,P%F6MGkP+_ys2"-ExL3Xc!Fgr;wP++ֳ" VJynR!1DuH`$18 3=5 fOFBU1`gWKgBeqpsLЕp#z;Ye-oZ=Bc J Bw4qt<qUUIFo]Q-̜8q Rb4uVw6{eycE5:q~y6(!z!!2~z?3UUB*[80ѕKvJ_ҍ=˲]+v̙3va*"IAoqyl4a|*F9F!v}ԩV}K[Mt #h &H 2@dEG/{IJyUWٻŗ^W|k?fErN"tib[c|fiҕՋ v :;Ѳ*,?|P^dlL.Cd ŝN{: BW!0YX&@bA` < CGaW*eh#VJhIUZG}ĉӃAokk[o/B#RBHc˲$E: [\\C_?sdZ s4M9~G"۝PrO ;sK_{u>{Z_ڻkZm&ȏ|믿`zqYw^qeEQāϾtp Ӳ,گ|ӟK_F֠k iRʷvjZ 'Np8& Ùٳg`J c[bqSyOpK7^^玽ַu;0B)i^ƚ8 +`zjr=1QUGm$ !8"XjA0^k4z;]OVyRFCh R Y"|g򼚘X߸[߿K/_{JB+y,À!Ƶ'~O8_T)+h5A7~'9xgg.;?Yo;c/˪Z[0/sSSSy~}zbjnbf`3T2㤳֢:LgeYveYƵxᆬI{Y^T $LJScBJ!(rWL;K͊(mڐљ) ݵZ*i$ ?!@(i!1xa c hw4*74Z c"WZZcXk(ZcKBSJh{{{յ(Fo}[ !UU63JԴ*5j 4J(zRJq5#L\ZUUdЍE:tYBURhy G4!H(H091hfqsu pc:8jxܖ$CkHm2InJ3\t%yjԔ1]W^\\qUU_^{MOe,0B E%[SӝNgc !l4UU9bÉǟ|Z֬Ja[o]R+tKފ6/#3'^^}W-2A7z&ik=ԜOQOҙ%Fqj0rX0u1@UB%* ?B{D45~3I8 P"$뜅JQ\tセjm`AU)!S!5-yƨ4puڍf3|06Un<;nJ"l8|jFW_}ɓn YO{߶ra(+HSIĭՕVq]qBdq~ev{P3R5=SSBGaZ,,Bk/ 13}V2D1UQyN߮)B)rЕRI".0UoQ`ԛ57/Ue5JAd9QRAJ%c:0"MJ?D! c_/aGZkկEшb)%( ׾{|AR"x)MbGzog}fUW]ɓZ*"xPW c<11DZ[.p +++nq#n qEUL]48HǏF+>aV0RBHcFYDDw0c(rM%y9`a<ݏ{vclccI$'&&ƶ -] ð^qvI{455E#kQJx̙8=7VUUֹLe//?'&on ??=w6˲/fY(bjjj{{" Fve|?˭1HJJz+AՙḬ"ى(31'? s ^вra%!HK[X%vEB[kR f&ϲ "ad4ae{l}}"nwԍDLj\\_YY}!l e+U$4k( y晵՛0h5?癐5Ѻq$VZ =|gG,GC`=Ыj9jv _$<!t]ZFTڗ nwyǹ,w''s>*lB!A `]. aG0Jk-ZqF1]Z!#MBTAo\XXW|2ϭB e8dݥ"_]]ufeי !D*OKKK&0 R?&Q?vЮ:tI*vk}D%aͮ}nzf*5}y^jڸ cݎEQNdgj.RuCw7 ׷:VQA"y$YF#Yx_|' h/}kc__^햛d%?ST%᧗ s/"xvzVTt}= Rr=gz}U^k#JvR b%iZ~+_9ﮥ Ud2D9ˢ`EE 'kxU@Ra=G>/R1U00DcF#Gz諏RS;Z7ڭv{t=K@u+ `,hYsǞ#}h3y`n,bﭷ_t2J7OtbzjFySS™9Ngϟ/t[]gzܯW}O[=OhxAufg 0D5 Uml!əɩ ZkqwshmRk60Fh<`a#b/Y QBP L%92x&\ZmqL n30ŊY[Z I(NX "-8w/r·y>!`cҢB2h4<h15@*-+\jk-%$SNw?i0P[m:$DN[dEak 0ZZ (ouzzGCB^}[o @ jCkn-;7;Е(RJ#T":ʊV[W (ϜJ)7q8i\Q\qEQEt{0 ݏvB̺o}o喵6iCyۭw_Y4  0]'ͩɩFU7fu"/Z#H]R*ϊ>p' fgg76ɲ,!vRղ"8"bh`P2O| ~Jjte^z3Lzb;fI*!,K84K;`ƒ_~;W{0å,dU4nu7wv[KZ1&MSJ1͋eR$u/>{lgg{vzJY1ZAQ馔Bi !bB Ph-0a !Pk „y Y%EZh<߇(-:"eX+2"̥Ԍ1)M)JDECL<-4AUV^è(f'>4EQ8;s۝>oa* /@670h[YHƐX cqy"ZF[_|q}y2rBdf(2Jq>LjG[fkt:ZkgF`lk~_P|brE(9<>??-h Q=NXHFL=z'6<:,_>yҧ<SJ8sQA(P" f=|o>ᕕ*;Rkj {<ĢR"%N{ X;7;+zRVk2+?Пmoǘ0Fcq>Yg#Y5YBSF[\.)]. gdѷ5D9"kPRҨŵz!!PkY3R66<(,+8.FBj]T% BX`!7iq}\x*iRF@JkﱝK?=#H$ 0@F83`ŋP?i:a@0\___\\uͲkY81ƀv{sfffhZYV;$ٟɟ4<ϵ>eY:X2>oӷ%d@pDbxnnc.}fMLL)j;\tD9z 8n6'NpueElnnZk3RWVVZiьQs+/iniow֮ݵ0B zv)< T%7VWΞ@F00aiN80]kvcB)@"fXBZբY1%fGk#W^y%/KG>򑲬YlZkK8ǼqeDqC!b-2@L@bh@jDGV-N_aDPA @kӁHأ0Mey_X\۵y֣8²,ZFSe7SHY 뫫S3UQ^ GZ \UZk(z׻j~ܹsRʽ{:tuc|Mcx!G':D;yP=KKKnW[s0t;~_5W.@r3Ɔzfff*I!DΤ_,^AP˲\]]viٳ'MS7ݏV?RI|曝-"d…秦|?C{oo Z tNy*SRƌ$IZkFJR2I 67SSS++˓SB_A~h4kFn_Z{0 +yoooWUF^>trrҍ$@Ca^BƮ]?˘{ಔ$#G/8p`nn."bwsssccc}}#e渱uqeIW]ucΏ<)^w~l0A}1&<`ѕ>FfZkt62P!MՊDRۛi &z`{^)%d-D{1q<AB.ɮ>`N`%1RUU)Z6rOSO~xֺﯮZISslp[[[r Mw׃o.rӀE޽{RnYVn?Ѩ,K"z=a؃ùv oY t'O\^^Q$IRάȿu߿?2~JۛK,͊Pnv| R'Y-dQo7;EIԪ+׮9'ɉ\Ղ6;AZ[c #58sm$8N9>B UY(*R[b1 ڢ(G(};/ /3?JU~{q4*U纥.xV|6 CEP1767,F7oO7wf;|F$/sꝵ[ۗ^~>b28l5 1j;߳0i֚ڬ(WWL@<p.AeZt8 a˲le^L~@3J$d2?˂140v}ӧό'p1dy;R#{6c1HkeLX)EEQ9v?~kD۷`Fs?sJUo-}Jk-C i\*I|2gYV؛SJƀsιEOD04Ms{J~;ץ|g_~zs>60++w$.2M3bяnnnOWW$EA玘L&@|Dpr '<| r*SUMjT ͛7VVqN:u'O9rxa9T;JpxU>Od[{n~~y(ߗ.]2l3~imTAmW1#ʘHlƳXxO9}խNw x<:},J8McR V 1xO+W8aJ2YYcX+X$(CHBVV\O"k44M"ge}k?x3˒Gaj׶VϜ9sȱ-GtQ#G`kk+, F|ryO3#Ksz#̲ըGl2lM'h@892A 2/&f2=q}YXK_Ɔj[KK ]O=CzD[sԩt.( dýฎ0(BRC`Jv!T&gmr_\nܜ' ι# E; 5ؚCxiE1Ve)V,sTIAH(Nv5%sA9?obLY¹1N< (Nٟ|g`0;Cl R)겹Lʍ 9q[Aظ|&_RJ!w,%wYOg"Q <|\YHpJTY?vɓP*Oꆬ%4@ Bb ~ Ix$I~mXgCf`!n=}ÇIyE<r ({w2ܾ}ĉn裏~n6i}eIT~@qL^]`O.'@u<|<̴"Vzqł;{0lllc`x/|7~O(GV+2MSo$io `l;ιBsRBUnq,sYA4 0ѶNw`B4/ l2 &(eXF:,!1&"zLhGÅD.4R,tqoFlm:R$QΐUa_[clj7_޳haPkM <|?zƍ?9.VR掠\Ӎ(c˼̇k7۝^|2ܹte8i c-:0&1LRHi. Ei˼;=Y^NyIKFmyaF1 c-["t0p, E`O\nuuuss!Tqz^q<RvlZ-AIyttI'3g۷nAVĻx|ʕSNϟ={ƍRK.---JL4 6w[A۽=Zfڧ V8IFQeZ ~78q}>ǯ1d izڵhe*\e1Hoָ,zYv3G+m5"‘YNכ8JA"MD8ǖj)A+j"KOpߙV=UB,T .)$Iղ&ya褔jcC 2‚C\2A 0(8277wG>9΋x}~깏>,VY%d06kMRʢE>",ʳ…#( ώ8r}rBhzfoV}OL9#XDQy晇zHJG*(K)&,gf ,; F*e9&ķBkT::v-Q(cGOڑ,ZHTj;a+ڢ8Ͷ,K676<DŽ=Gyw}., bV*P:Q%FP zݮj@ e_v}jsa7_o*dK)+y8Ze)sFS㹾{tj:kysA'N6܅?񧦳 #cN]A &ڍ\*dq:}DDFM&ZfڂQCEyYN&x<fJ)d= up?g~Ym4JͦwϿͦVk4M+w֐V{O:z}sfIqńsO pQ*BH-+c61%~zUQj=>믽)9~{|K_8ۡZL(ht Va=?ˋxmkt4kI2#2}( nQ)ƺդR+`hK,%(m*AHk8l;1RZYXku8.e]GpR:㖌BB]KHGYy9VIDݻG+w={_km06K eYֹ8-˲leiieE9SJ,cZFIK{;oJ)eY(Bx ( 5Hж]R0^fr{y lU׺.{fA|c[\\tL*Z_NB}ZJ`XZY _޽{:L+gU+ןϞ= -MjPׁ=DN0v{jx҄QQ6ВCr 8^0iٳnW.ŠVkZq{VCߺ3ʐRnߴ֞}}뭷[l4y^3&:ힵhkkorQi:՚Y:tcdDb׭1ʒhRyQ<>eQ2cJ V6fII5,u\n_z;ʭ- kz=tclll@&˯xR:@)&aVo(#Y6[4I# #BZŜ9W\k.^¦jY)S@,%,H#A LmsY*JxPc6I.[W6[Ņ>alyy ~O)ElJpcH͎R3JQ2QDUw+( ASlQdj&ՇRJF(!P2BT+\ hBdLYX#-! gyA BnYa)51B)KP88C_ "c[̤ cG._z里5)e~m:#;{9M,@9opΓ8BcA=ܞ Pl(JiX-8g AV$Ab6>'jڍ786C6 n D(&TRH*AǏ((U| _³,L&@p*~^8|h4 6X k+&fHnC˂dHN5q՚L&чaXgw}:1O>T` @}LBZiHIi>3w߾{ =ܥKVWbQ)Z)<12Fm N-ϯ//}¡Q6x41SQ:+U9wW%*pkkiz~j(dQzxh8^Z#e}Ȳ̧뵐 (4 TYj#)#q<#6ZJ"]t6:^ C(cd-AK'+ҢdyQaaުosV**._~8'ܝ9S *л{VԭH?l65IPjʃ}=a& +W,,, ,gvxypx @WjMNVRݳʃ}.#4 j$q*#RKf0FQJ9[U*lEQFУjUn 6k({z晏,{ZӜVeXGQJJXa1]y˯rw'?WiMXZ\E ,.ZCC=w~g7߼9 {~{nr ݼ}/g?2A4!Lz'bSr)KgR^eF#BH^! okmSa:(Zd F=ܸEj5$/v]Zd)!eSfv+Dc9.!uRF˭NZ뺘qK(Y2Ra1i0L(ZK(Fan}; d-6P&`4iF,"BPl,(&aBf8Z8"KMgF'eIB>w#daPFC0$MrF?$Tv:,j͛7$J%{XN ;tcLET1???77t*|W^]6(B=+76Hvl6`v#GTAXȏyWNՌ2ƅ>xn|ZBTLr1WT@UIgoݸ~ki#YM)kZAZKMES\RimyY֟0F0Aɚ23Eʪ2+A;827a;?6VL|cO<5^|V6V"[FLj빡R"bCRKw0s ~C޸y1F8*]'t_|Ͽp3E^2nu|.c')%"53,By$%Dln=-U.۩ȔD5(!RV<k>ane©p1X]*MqKI-QRZŵ S*-PJʬ,< 3YcKU*3#L)&YPN(334Fbg"x[xcR ˷jl;eYOrmw|߿qFH9X,sˌҔ<͌QVkँJVX_x';R,Ҽ`9, +LӜ G8$fb!'|0}BEIJ O.-/ !$Rs>#'WGկ phscl rE__EDh_[Y-riyy^DD00>q~IcLXp?-ݿ q[wn cLJńk1R ?J)k˼2-10&@]mж[dְI&cMrX q@k-U Yۮ;srt2r85šr6Y]?s.cvB"6N2zsY27V0-#Z]1t7.HJ)#"l1Ž`*vR.;ޡJd9\zS'N8p'85cwPju5NXl P0&bkEL0+ D,Xbp1EG_0 }R#]]XɘPv):j52+#WbU$I m6e=~=4ͤVFIi)Gy{N<_xşO>oo^߾iFZ=TJk&G}ڵk^j]4No߾]!(zXp_<2`bN ZVϟ**'NZ-x7Fm`A³>vҥS0l6p8fЃaQ| b`2 v*뺭V{z*[qʕJ6#Ç[r7{s !nZ8 1FRRnmG! J15eY2͊<7֛)g++ܗ,F`bAFfBxp>zhe0 'il~打ǃ5NgSA@# k*u]lH1pð1ͺݎ8y dEZ+k &A6W..%rk5(\-DBT06Q &e )4aeiGK|ߍ^a[dTa1cERmq 0ۊb`BpF9`]-iRj "L!4 9APL`@.!Sb<!r]w6`}tO?quu5FiPeIV7[+g)C m\/t|l6[]+?X>xpk4Nj&$QJnw m!u(#-jc8<D3JXb6]?J^ʷϽrgEC&6MSqa^Ǐj5((#GaE^,ˍltA9_(Zbfdo~wr1ƿ+|Ðq^$я~Ƭ"-$tp]<~sss$5`q(fYa81~Bф~p<8p MSSdTR½ ;/[_Y\"wz'&J)Dr#EsƈҥwY]ۺ}vɲB)9Hd=ykm qRV W{}H,ڳgߟ7={ֶ݆^ondiÐ2 'F8ʦiJĈ}/J'iϔ)ZG}c't:M}'T(UZkRJV!A0 2j5ەbmA R#l#RA BUZ[c cs\-eYp\Ďǣ0 ^om};y"˥-)Y POÊ"߼q5+Rh(GV<q=0Ƙm 2>9ўt :sĕ뗧qV)N@ DO>r~~c\vɶwlvy `4A >oBak1^]]ru


|0,aJsҥVvkk?Zk("4ԡ²eYCz~f7(WP8 .tM-a RJdXV ΃F#,RҔ Yϝ{͛ӧ('n'Mb0r<aLwQ׽v:KiPC0F#BPs֘e,)B@(B.RJpP>^z֛o;~gg_JspccX|Z%V)YVW5ιH[#"L0 !%ņ@k5Dw(B"KlN#GnΝ[kwo{xÏ-8e:(]"c6ZL!XH#1<)Rrdtm(㱖*յ78kZwy1u,s2a-'& kmZ+wnؿյ5o'kqAF5[A`8WRKiM&.uFbQQy6EIecB-j4cDJw}eX؟C iL(JcZJXkСCpj8{*&t LZ q ?a**u^F91 ={ܺukccsO...F#[]G>]0C}yy 0u@/="JJ5ڵkΝB>ĉ`q!B4s0a{y8<%X߹sԩSp'wNJ m;#֥KǏ9o|ʕkeY AbJ!h0T;͎:ϒ /ݹh ޾}(fS/u:6.@ +p :!$- 1!?t/_~{O_;#| 3!g_nQfCje%'>kexUFZcBx^z?zY_ڍZ[&8!,K--k%&ֺ;q$(8 a!8qE WX+nmlf3ϝ%InpqG)Iku-t=pϽ&qyd89s֭dj5777gyTk0KcΝRkMP`D֘2LhcŘ` 6`I* tkk cLd({Q?8#Anly.$Xxƨh6b!\8,V,j8 8ՔP.23!XZa1b8jl`}A˲$W8Ry&0|++Ї{1~^qsݵ7x#R{,vZӗR"fj`Fkk މ1"w~f[-h:6 :lYHс}ɂ[w,\P&UY;2ԙ v\̋^z3.fg!N^OW_{SAĎǓ(hmYI(5<#jlwkszaì{<# |?oKDȥ$I* g[rC;wg>@r {?+;}ԅQ /^?>VVV`#)CM@)P`%2+%j"/n۷oyy͛pSJ!]J{=՝Pct:7h #fYF3+ (R+&x!s)8RJW_{g~)J`$ 9SH^ }LHوf3?Pnf'򯹞hhbP4`jaͦZK'}VE ﭭofZn2C4ZYrJ0e,e,nvC|$PJ+<-tmmm%Im4W0&1!R a.\+Lfy!)QLɸ8V`ʕ,=7X˼:l/?_yW_8={8EE2vedkma<%amS%BL~>2F !8RJ;GI uFyEt0jAlp8Ty#TBaS7'3JRFQλYaz]ȳs7&>cL^9yd2a3Mh/p)42s}1|)eEQqj-BL0jY c6ai$T"p<\HY ̔.Dcj8#(|cm=ZǃVQN/VDh)qq B[B" <:g%\rH!! $MFݦ\}%,}Hǐ: 7sv@M,@[1qTm*(e2Is/h;g)n$ts}֭[6Lm1Xܽ{w8:t虧z/] w\^qYo\!nݼtZ~nܺuw.]c9*ϨѲ,40ƺZ.s4]⻎k0kוRҢ֐'JLbmm흫wؚQ2*qrvX«k\"RYܱ1ZK)u]\&zd8anpq׿{Ş={|C?GyPU2|J鷾ڠ؝1 N} B媢e{5S:o}K_n#!O؀7+Y)NeY**$ * p;O_X/o77~&&swDp G@Jܹ370^l鴢xZ\ 'CS0aNu4[SO??lfE)z-V㸮zQk0EqÇߙ5u߯!ua^QLʲ$a0/W7gL%lB%ER;C?~)wUkfS*!chE!Eh=͂EsEYEB &2JYk0BTt 2>S`?nʾ>ti€udEQLJJÀK{pͪ~`9G|߇u9L+nZշVm@TW#YDsED*NxaUjʧ,+L$p`#zFo߾(Ν;wY0Zan4Ӳ;DE0(f'c$$Jf+++I =SnR,R!VZSʘ$IÇ(ܸq |AJ!E)ur$#ǎ%T+&BF0uP1MU04.4c󛄇HCL]muY`՚+Ry E}R#e,ǡ1D`E>^b5To)s q&A^=yx|ر0$CEQ+{BDp)!la7 ٜo|{nqCcMY@8]i{v[aXuw0POqݻw\*Rt:UJZ-0(1L&:̅ĵݹUNl7oF#uq$j4sss_~GwA/deZ?pٿOՌ j5˹.dr-kM$>}O<n&lQV ͍Z(KeJˋs=s[8w܎[^JiZI`YZou+W7Z-k_}CnO*`ަ.[cAc1چZϦbDԥWN; [&)ښ4N4R$66Q;eW͖n I[k^|Wz}sn"Hu]7KҫW%-5cL-(hsSJ)%00c #&1:4MЄfq!Bq ~"^}n$E.[k-w8xE)fav=p IRq tEӡ\r:qsVu^~ů*„ >Eq4-RUhŮbeI--WB ˸eb<{9e4-V$ xl\rW9"Jc-TZw`O.1vF[Ji:'N!o"V$Pcg^<Ͽ/?@ee%@2> P8*jYZXICdX)%&bcW < 믃t 4fFL?sY:"'Z֔ڏ~'WW~W9B)Ef_)!1R kƨZ c̕ײy}?wyXɨLW̐Ѯy O;[[OeFbb:t(%ߡ#FJB!)KHhck2eYigK/6u lmO^{x\B?4M뵀 "]PCE|8_oJj:,F"K(e,1n QJ1%91iwa?ܨC$p8<|pEN\C6a P! 𪲉|Fa@叨w*<8 z!XT;n8WY<Ф`2L,kQ2RƯ/rQz r8XbmQFm`mmm<C$Ok;_p0ܹ3LT i)PAQI837!@k-1&zeRݩeR-ghEi1!vvW1NkAH0ǎç|w4JnO&#!\T[SR0gf F)B(\;Ѡ ##0ȀX-c5SBB(jw;3088+Z"Qh'DhB:QgQ[QݻWk,[X: QVynRYw9+/1x[X˂s+L \&N5 \ emj7a;rBܶ BN/0ƮL(2)#H8{Ng4>Ctĉe:}p) "N˗ ڍq BxIZ( NQ߽| 6رcYV7z~'|qq.o\Zqb *l%^)wQbrJQ[+\kùc .4t^Xc)ak ˜QvTRL8F ]bޥ OV˲l:BѣGoݺu̙/BN(`.e2dZl&*i?;!K/FeYjv'w])?0^р dmWiP`,;:h -..V|e=,6m1`v1?pWWWſ1; נ.[kvTy/y^U.;|ګvh=޹1 .6˸VY)i2e&p0q[,<SR,Vi(6Leg0)أaRF)b^PJ Fvy)$Y]]edYfw-p8MdM4֢VkE~ c shTBxh̉AB# VSw)&0o/}^}3?={n\K6ybP#K5jkM-fpGxn8.c>vky WױQ4X~1'ϊVɲb<{Ra=f,*d:t}7o\;vZP,}]ݵ,JwU CJ wv]WJSn#,0%I=#;Y|-lѥ 8%JG0d)TL # Idy6EQ.--}G^{\fBl Y"dK)ZAYl]v{WRƒzC8^d0͛pD*ZQﺵ7ΝK_Ҧ~sև:.U\z!!gc,$WeV,cV&+ AaN13`bA"AC-V }yQJ1խbl*(c+w*d`` &_ Bضr(uYJ, ! ![l9gS,/.Y^"SኣWFlnn`T-+4Z*o=zLXq<Ν;7b¢ VY_|q߾}UQ?Sm cmmĉP++CՃkޝ P~w~ѣ=,' ۖh|'[dWhS*5 Nk?8#W*RJ(Еpǀ79;Pkm竫>`2aS:8N[1T8ޕuZmmY4Ay?\]]L?|vz/>Sc̻^xfPf,c}? Z4i"c"Y*(KKy˲$2q<BU,2\fsk̼ZFad]k1ު܃F$vKlw.P4ZJ ab'!*r4ԃyeN&\y[7>O?rp2<`yK,a cjӧOwQN0 ;x4EISNM j˷[Gk/.-T`D BQ%B ~`dA"be1ak2Z{vLB{Rݍ5?,ҞA`FRJFyg֦IY%ҔpR%,rFW kpժh,.CQz2 Ra>t:Oqge;,ᶩr[Y A`c9%XH@\7ϽḞ#BywnC1V0Ƴ,K ?6kޝ@Y !=PRVb f@ ktBy 1PgVqZ4͵iYkay֭'O}O… v굫Ơ#G役ߺyZT$Vj52]Ji=tG>°9uMJ$׋0La8j++w1Ʈӧ0/W2bkwq4`j~`FS "+RJFKK=b.ÜRF)ZAt!Ī=CV_}1mIQ1q!g0j͊bo6v;/:r <`?>A0 붯|+=#<=_HH(%2ǁ[.'S}cp TL!//O<b{G|}}ի?3?Jx @=p޽{FO=Л*J W>+LJifuK!e>OS&ǹooo^$/ ^|vh0}j /f KI(e׮|߾}^G>:̒4Os_tz{ð۝Y?08لRRyB @0O|:$IAXZ TiJ1Ƙ εbmS8eޝ;wN9s<2IpX\ fFrfYPD\KӼhoAkt JYH0 12[ `zP#ksaRNxyqo넺Qi2}`3W&yL̋TaB.S2υðz@={绞rUQ䱄4RIl <5^^ƺƱ1s:IlmϦ~-h6ZFk Q)s , %K- `0`lbL%6+ LX + !6HZ y}m&1J/î{ 珶n9h(P2D*i Ž0J-iz#GP s69+15oEugy<\6 ؘCx<Fׯ__XXt:fs8~Sˊ*GU>O@;j#SQ`ҽVdr ^Ղ`C}_Wa\ g%NU,gY~Ȟ6W;QH씐VY^$4P[JIy~zussmvge;湷]7Sʿ=ޝc%QٓR"ˎ>aM쁳cjAO%_!~~g aIPPN ]HNO4LF !$$9ԉݝjOu+^z3I^op-\0o7OTk .}ǟs\p"(e0[-AY^dRfl֣(sGpR ;;;x7r}/uU2%$)tna3'N(B,ZRkz9wH$֖zU=''{{1*P2L8*˩`U3p]-S/A%tT 9X>qʱ k {JBVx~II;E !YgtպzrzRkqiD@  PwG2:T c'R< 0ω'4?^*ǎ55 ƕf2~GNa!6G\ Ƅ* qfw=W~e8F~}Ci4v)^ JY.pB/k7%;<Y=#K^tVU¢NYRKD|~D4Q)wo4(~PՎ=׾M@+Sfeh x(%_Xs8ݸ}uBFϯq D$1`GL)Ǔh|p+Ɠ=ڑcoV{nngFY-,,M$. ͙+\VX!QԂX+U[vZ *Qm|i(+r4*qSOVW/>p#n`0n묨գF5'j8͚RxX9dHɲR g $Bp NF Q7|pYyZkg! ~i$A;eSG.#.wᑇ.__=cju63*J3u&K) #Z'+w$FñܖAsQ]}ߏ2|Fa<굶1| eR̝E=I)]^eyR.ڋA_0ZTRVudG~˫$ }ўK'zůAp ~)r*az< JnC.WnooogYVfTXk[h%8w\R)7'r…'NfN AQ'O8LaQ}B[[G*3ffeK6OXP_R&njcx-~`R۹s/؏XYyeD_$ |rqUL9EKvvv"W:(EM]טmzkW͵>...HGPJ@t#fl*g.s5jتxMJTT*VΒ2ͲޠnomnP_ͽnۗp/6G$ a앗^tJ񗏾}ϟ-1VLitZM-q=EWFi +cyĆ ùØB 1q >A~ס@(T*E->mL JBl>8 kyRTa(Kb=5qè6R;8RF'2#+4֛*c_Zј[YYZY]j6G;y.b#{(9\o UEKΝ9{x9Y2:h1uQs2 3. S jJSEX J'KSR)ŹdRxrj{`3//,#f X PkA {wwБO9oq\t+d2''~'?yWO)cGO|ʵDz,sl8Kj++pvZeyB20+yIHS'vʌ EHyv1c!>4}۲,پ7t)P.8N9eDZ`aƜ 4sYkolP@A BZU܉+rTa50 Bjk(R4#Ho߾|v֨޾}+p~n#~3)IAY<)\eC)ʩg0˲Jfyl5-C@)vBIs%s؟W u]C,Kr΀Q1QT<ﻭVc08/C83a`f:},M,OQIRpXKb`.yittj**rUXcl5cL=x_oo.GPGzaHǩV+Ci7VK0͛F>>p+jA/h"V\|Rk(Nh4Ξ=e_iyΞ=]pƲ$%%O0 !Py|׉4:W&TeD&,c11 % RD`K좌 Pɟ'> <\??+(Vv/^UYb1h,--n[y AP1u"qns.7 Tй 2cl~ӜIe?_ n?.\ ܸ}ɓ_9+++R2)vz}¦~Eׯѣ;{~zGV^~qZT[oͭm+wD(eq3sΉBZ,O&cFZP)ZH)u4uclnu$Xk=PoU+8AonVYnt:o HKq}H&_ 8C!oom[+[89{.) ja =(8v鹕4bxj6O=7(K&(IFt:;aTŎ)ݭaԮ5Jjrx~A"gEe9R:2F(ƁR¦ xh H*@4ɸ`IÄ:~[<)1a^~.c*2 0I j)B-!`,00Tr9ϲdOգq: %<]1ڂ+'c؅sǣO}Ci:]]/~}c_ënܸqkw GjPJ0V-hWQZh[\+Kk(//9 (:v0dN1=ɓ'*b$٤P^i1(/su(GR8YF~?rlyy=5f DիWϞ={e/‡ŨK,..ie$;;;I2koGΫ5s-gumO_x-]PZC'Jų֌J qN 1~;zyBŖ$˲~w4M25qڢ#YYP TFr+ 1RbAgDZ/Rc-q68P~%lZB{{{i⎹뺃˯jZ{'|֭[.]ԧ> /;LΜ9Ez/tB x%TUlcEcf(x{{9W zTBs]|i^*d'e4!DHI12kQ8NZչEe\{Sѣ`oog~~lՖw<7(Ο=)HO*_\]Ph0a}룍v#I•F1jI*(s`6nE6GN γno} VAgWo6:{^~x;[ʋ,ꨧ)WQQX+KV 5R,YΔOQ.#Z%$GAq.WB7N#v x8pOvtt26Im5v^U(JFҨ5 QkAFBqL%iO]ƀHp$'\^8$jX9~UomוJ4M8ET=(Gbfܙ?T~G=W[jͭY̲(lq~tRkdq.T+)bMbi2q*d}Ꮿ\T|gO7aaa( f8Yղhm\Y !kmǣXvumsZT41ENCE3"p)#!vy{&ɫH`0v^/-\ $fёHA}vMrYsrxssI:u<+ (N(X__GQ-*=NUZn{2I//u7n\tl~q=\QOnZb\YYIx<8|!uƒo=#Z-2΅t#,R2Ό6J(eTHrJН|:*OOt Gۻ۷vww,_4ַy, y~T` x)FQA`PNm|xkVxB8Q$0Fv)q8fBVs.Ak@)J QB p?SBuRqIgXaKd{)uNqCw qa/ cl}7m&hAi[o>s 9X-ʎςoɓcAԗEeEI[aA6y{~~nW޼yƍVWWW\YYvړO> /<|}qRbS>cb};绎`@4BPgLIZu R֔XF0Ji*.\0L Bpܵ#+Phjespomn]v=p³~ۂ%¨RSJ%r0QHpr)?nAJEu\1Fcj$N`DѢ8Ngu81 R@ :1BI^[nͶW}x(OyP+D~iWrhc^Fһ21"lZH=9//^xa>칰<B4[˛7ompqP:kX3Йq?_FejhyyE?v]|tNCjៀ3|߯V(KQk3dSpGRє)1kZT^XKBOZk SAh+$*qBjm$60 Bq4%It<6H1U°l6 כay.&F 7h[h)!* \ID2){y,hЊMjaDl+[n=׮]SNUp.1rV^b/www.q-If2zPCjG>gϾ+.] (b0wTV>C?zov;z(;}1*I GnoouɧYl lsaJlZ- 0J%L hʭwY.V[1R:!DX+cjƎ=[)<Ű7N΍7^VBE㸌ݴH UL&y^=b*7W_|[{{; ?~?B0N=/~VPMTg$s)EmЕbq~~2 Y' `Y’(:8C5Z[ @iL߹zjF|7;v~/_m4F P 4Ro \0!Ӽpu}qhN(3'x!XB0PƵ$x9]L ǫTjYQ˜p=Α#kF3,ˇal yq,>[9rP< / RRJRkj黎#X%upyv]na., S8,-εZQ5+W j6:05JVkMX8R*׾#}=#6VS+Q8lnnGY]][n---;v ;8,ػ_?#85 zSr2!+8onnk_ן|2YV@I@a?;QЃ5o??8.v-]>N:㏗u-Y~!jF@MLq8"E^ڿP YB1#b,0IXF &*pKLܦ*EXiSͦ:1ѭV 5XFU $IؼY UNgww;HѸvvR\z8%\ 4EJvytu9` ;IckmTk5C pARH.v7oܸ12㜢d(R:l*5rt61UjJi!yss핕$V[o T*pj9r|I]J)`s*Uc@=(E> KOr.)#YFQR5#L0 @:2ifY哢PBB]\kZaTLSc=GVh'~28!O<뺮>J)Lav_yV!8SN16FK) > ޳~H!k^ĨZ[++kajAԙ!˒<ϋ"k6QO:/%uk`h%\G”ĕFFkm҆ %2=W}?YNK/mm&`0H e1RT.w/>}sjX{dppvtڭ{sKsdi#`Kk+cRXa VF)cL4.NqJը^Y0 7oz^rq\S (0Nb0 Bp PO~an>}E&{jtN8>F~s=+++a>Ch2R6ĭ2jYfa\|P-T5WUQ-a_[[Î]0vvvKh4v~?2Ѓ2Isέ}@+1fuuĉ/_W++#g_aC {a)*yg3֧BT}s?3?&&oooonnJ)zZwg8HLz<y:}R5FS)%Fw !PXLJ)c؇2kR9AQA xk~Q}s %ґb:5\cՇQn㇂[` @&uнvڭׇg+aSkh tPD1fQKBIs6uBs e$q\]p0EQBZkjJDRdqlTUcRI:хI *c \cp.熮C=%/\oqqu])۷w1PVx;;{q2MTk=ejU2I(ԓB|1Ji|B(UIJ)[9Y ߏzZ޾z7{^%qZ{{… mjifRJHQ;Fà]\jYv;F4r;9i2_Zl/,i]}rs"DNJSX]h } <)2hǔ^`ٸ\yȑ# tQ"鈨RDa(qi>8=@&Vu8d$Ir 'Wܴ-?~r+$չ1r6J R/U-/,,|sɟIpNG4/Hx@l!2N3B,eP.!302- 0!6&-pN#!Z جJ`9LAPJ~%RC$Bh0JﻌSBm$Qs)E<`QZzn}z%: 2ɨs/%(VAjzt} m͖?X{o_~.0p7^GC4"&_ ;z^zAШR`3˽˰5_fa|#2X\(6ҋH={^8qի؏=Q23fzˉ1ԩSv_79_㖦ԥPYl^9Ia9rCVr #9q7%xTJVQ*9iNQJ,™HN9~P'(*(%P4bjd eEwEF8:eZb-`L+C9* JN(ų)| !ˊ2D5 s*t X;qGgkiE|ωH@X2 W=J%R8H5מ:J'p8Iɰqff<҂+y$Yz \0I`Y ʥt ,7 /U$o^8N^բ#k ѹjyISPkVШ$Mo<8ܯV"^_e (V*{. #\u]Nj8Y{׋YLRf7*BZ˅]1)p[[[Cyt gHk?V:xfp[,elJ)7GRuTBwc+rX^^~WVV.\ݥĵ$K,uݼyl.{x+3,<ǁpQMn~@PVUlJ|ReZO]ddC0P L+P q@,%1C `%B0 Z"`( 8|J [%ZXJ+X q)NL9Q 5p:0!  ɨd}p:|N":QV= 47,mllܾE) TQF$P"W==Ȅ`љgӕ,JY4g0z5;;;A,--amBv4(PfafY =6筯ӟg>?"|%ex;%Lƣ`NR1;LBq 0ktܩ17;J3FQփd#XjckO`@x~?x"w0lmm(#V.,.ַxr?ggI;䓇 )Q"y\A8q/jK<~oV]P|.u9r,Uz)!@QZChsssdV2x 6~~GW_}uee"t~VePJ ;r]ޥN)eSϬެ:I]TZC1!"ѩW Uˊ;ӟA)^( k)QJN,PJ7\0ve+UjͯnO8ҭ??حW8"8yK5PhcPǕm?H's&qnͭ* - Uo6>aH $8P҉(z!hxgQT @,a94@pΕs=J3ˣE Nd|,aGΘqnY 7tE֋0( [k- w@ 1XCSr:E}T6 ׎GQ'SQ(KDI!T*lf]m\2Y2=~`{{[)Ux Խۄe7*rlٷۄVk{{{wwr"bmBV椗ey-+欶L:vثh9wvg( en>8S@`%]\^9`px[ \U4\z޻&+в #d eӞQeOy^::Mloo9s&"t).7ʳ,zޢ&t6{r1/-"7e-=;pRXڂU,e,d6ŧҜ$ Y6 77e%dQBHH)|x` \ROIX=RH'^NZ/0$ww8@`oHHx( ?#N (HD#D%s=GHP?OR'Y:YZַz{>V yoktH'斚S˸$MtG~FްbO8n]2 )̑*yso=j໓dF!h'Eo2 ֫{?QFeq)yE(A(J)1(c @)q7;$YYeBJ)=ZkBZJ_Mp; 2N(%}R[4pF=[Y 1za3~.R5>g2|Zu`% ! (!DJnUJ !Zs^ҥK[[[/_Fq<#3%@:rnc| jq?#Jl\) rTlG Kúq߿uRIeLM5jIvc#+W [h[^h\T.!q)uvV"SfE%GG/^̲ X|L1խn\㔷|Aگ?L??vؗ}c%KVu`g|h4F=a5$i5Fim2Rim-<ϻ}6X\=;}wau-.=2B]Lw})HrxT˟S&>_RO!*ɳ P'jPYZCw;csL`5GR8QǁcZ6$ϾnUTABs`X(Cs=9/[7\0 z-xNX .7jqO)A-,,eZJNYg2@7#D\?ß&2I9{ճ4F nJK]|4M;6hJ iK^Ԝڢ!#<2[KU~ ]S2X-&E&JٳQY)yOjR*/L0uՈJT6ZF8Z75#वպMqW0 n^wvѣ'3ƶQ w<,ć7wUrS-p2!H*=l:0 B-%T +ҢH.gP_a(O݃Le'OJ,b"xr2E>!D2yckaPbQ1Ix♧_qӛ/?:0)cWOO+/ׄ'$wOΝ\]_]^"W:N_p0p+R8B2 Z l~x#Q$2CZCYj),ʹ!T 5CtB0 bE+O-+ChJJ`jHpRo9uc~'ChFQtU ιB0I1ڨRBܼy^{W6za0ƢF)ϕr&1Y^p. s!m$hg#Gp0G)MSԓϪP;a lN]B꣏>z-av.Ѱ<6z*iP`)虆lOw͞x͚irn߾U@sq3YS`ڹs|?j8F,4KSkP(&&IRvM]k@UjK sZz߉BY\#b-{^ҕݲn3RJn\*{C*k̵6 UW `wj&h= ˴p;1Zq'Ă[q"&*{}ދYl.p)530tmJRJ )R)PclQ9'M;2ZS;/ L CL.Ɓ(TZcQ;g RoD$VVPdK$d C)^ N74ƓZn <8ܯV);w_{卷zwq/zcxJhB 1ZkA߀cP*_QSy\ާ~^Ƭ//`zW%2X̰?]> c}퓟$c-ip$K՗Nv,CPyaEt%XzjOFI0|:1=}qD.[ygy+ s~gWZ{@`KSʄt$kmp P΄8ƭW޹Np&)\[О4z8? |6FC%b> RJ (YѻIÑ}cX.B4K(̚6r&p ,PM+4M~5/+VvP) ű\bFZڵkOF6h[Wbj%GlCYgͧɤⴎ̥N.ϨJrĉ7o^t… F# ógo//LhK)VYoiC`)F%B[nY)%GהΕ\ &6`bg?p<`9E^diznjЖ(t^0 *Q5ZtUa5$ԜS B 0e8SP w00ƒ{%SȖb3dIZkP kLK18F TMq(RS!l7i%Ʀ @)ӭvNBW^|ۛn7Wrf5(4Lx F^ G+M)cDxTkb)RbG8)H 8eq) ӟoJqZ#knD6.DzRpu'Iht766ZѣGK тqpxCDKg"qr/Y\۝O}1W۷ow:LȲ #ggvchzqbTA=c4њ 11bA{,.;* \w\pAB)CU Ż8Ns{g wwwo|ckf.cx].KZٵ -jۤ3QJ-,LlRQ O^עjZ;r* \۹8@Ѥv6F =;W=3?Tg[@+-N YYZM'zNy#!Ph'[jtm  Q 0w< C[b9 \\^YZB ) áRƀC*B!YXZ`ȍRZN?$ ,b?8݃aAKe-(S!%1LQk X (9  +w,<ir&)k%؄Q >3 x7^}"-˵"i&24'JyVqaUn1W mdZ_嚄.TvVw7Ā\(,XIKKK7aiSTJ1*]3{zHk&IvfLKo6oc |8^\\jZlcΈ0K>Hwń*)/L%zx.~ y4Mwww?B $I,6F28e|:0ɤ+U:&;09f^Y(Ѷ0Eg:(+jZIm`0qJ-ᔓr\:xz"YPBjʹ@a3e-= J ӖQb)Sk9pPq3(x{>gk^/Vdܼvd$.lp(sٗ?qpեU4Q9&O\twxw^ugE٧>;<~1awkt4c4 Z\BS!4N8a`p\M$s9L0c 0#\r¨EbR0@0qJb &97 S}`0 F-gK8AYeT9wwҔ}3FVZ0LP F[ gSckIJFs'qV']S?S~M&VիRr3Jw,!2K)ƨpЧ s,mI'!/`1K(Tȭ\2E=)gXXͩehk ik18e48Ӫ9cFx`UQo~_{[/N?/?/>}SqAպ~~Ʃt /'>^hpIڭȭC:[oy^qc sދp։G2 t:Ņc'5h*+M TPKi`52+Rߎ*յ#G{݁ 3B9$!9#p-F(%Θ QH)2F 8[O5npTw<)bv$\8‘X7olu]E텅N~_8zd?ZhT {{{ V$f r'f֘W^xۮE76n~}=&{_k6޸!t}S3NVU^eh%Xk똴0E^#xf3uw0BXDs Gx\@s `q1SY"@Cb,৩[+}KhQoR΁24bCk!`*\ϖ  ^])J,+Z{{I3@,dŪRTVL(ff"&fZ"uDϨG*u[%T"$@2f>@bzb"Z707󺳾eߟ(8 OG0T g愈Y^~]8wݷ\U+r)g ʚA(9q4k9ʱuUA1E@ 8䝟/ & _|3-[p~֭OɄ{k׮mnnzJQ? ^\\%"wf9=(]CwީSR> V!8\,fHY/ajާ4S<]&̉sr릦ܻȿ=\1DH( sRDĄDgTf#l^[\\GZkIBk-CاYggnFg-{~Ǟ;_] h ZѐP6p|pV3Qt{*oD #]Z;SJ"VZ`PTSHIYf{w`;z8HjF}곩0Y8]5\r$Cßɟ?__OQ3w7{vQ0 A̒1uM"KOѓ٨sgypùSA^$cEeÇ}O.'O|?Ghr_o߾-|MOxʩS}`G0z=g=T)󹹹vʓ1&Ij/_CcݍFlzM}3?:q"=Ov^ZVtgu@y$]0r"0nolq(:w~7[vQPįVJ"E/:C!}3ipɞ>}z…NMM /X&jp8orOdd39w/H)l1 szcp1,u6*SUƫ7f FN1&}* ?F 9zZ<۪tB8ꓟq{\[F$Lm*0v8ҰׂA5kS]٪v/2˲w;[ۅ+ jjjVMzZkMMMKAЪ(vQQ(hbA*'[Q쬶Qt"wKWj`7$QK굸N_y??6tؙRʛ|xN<~䩩Y`4 h}"eZQ> UbZsa" QNȂ(!#( f,'B\YxH4ۍgν+Ϝ=HllQeY4`q\z"߃O$>-Ypб VD'6엀JW`_Q@q,tƕ5N; j @7R 4ʆA g$qȲ_굫UU<_{ֶ=HӴ՞8IIG3nJY,eE ^I+,? AOe?677ϼRLO>[[[㭭۷o?kJ6? 7Ikm[[[ީՎl6~W\vDF|~k뾣l~xT4'pwpp?4}x(eB#APTyg63)6\:ýao>VD&C2#@@8@"fsȌÛ>xGc'[o}RLZ!*h֛RQ :gBQT%C E&Zk` w~6;F[)f9;;{Z\(j|P WZ D ,u"وO>[[YTǎ-ٲRPZks3&/3/DHX7!_Ĺ ˡX]5_~eeo_Ο?ĉW^9455? %}qG}t}?z+Wx0?{{{^:߫moo{8y^ϗwzK`0{%heUUnۍ>ܹsjfff|}^ +++nO3N ;d e@A0 $ef(@xeџw}Xk/~Ӌ[+Fh 8  4pueJSRUUHFJ)@@8 ;tnߊf@hSLJϺݮԴdU m(eqȨdJE̍fnV? #uٹ_mnl#8DQn+@E!~{/ |8F#k~;N//͇AȎF,PA" SD,*S[YOw Q֯LǏ$D"(ʳ2  fyi$ѹq' vuk/_=qK|eUڔְCVR}m&IJ&<c ЂHH/T҃:89 @!cA`RB2Ca#[N#kL*B P !o#찲p8̲l=x{Y0 #9v:ls0#3h Dv~;&%hjjJ1ֈMMM}_]XXF1!]O&\Ǐ?y*|{MdWkFQN^ٳg9.9w߁,nesssz_{^l!'kFe~_ŕC?`0ںyfeGk}ii:9C/n8L7c""c[>tٲ J ueY(W_}/FUUFi?BuZF#:*#`_R8( %5Fk[[7?X}^g髯 Rގ@^ΪIȪ;vl Z B`JkM%F%f:ӞW v55;dnrqq>\Vtemokek;k:Ӯܬxh0555=3UStpyiѨ2ЖPlvq:]d0N›7o8qe!n..,lmmFCh!RZyFVkpA{GIHĵd0--J`D"PTK眱,IH@=_~W^vԩ8 <`a礐PEC) a!q΂`-"REd^F23cXA(2h`{*@rgJDt"AI5 skt !GP4V/_REUUO>}~f0ljLj4veU9 Vj"S8T($ Ik'b|l6333SSSam&oiZssseYzb}s0vH;s~Çϟ?z_!3gx;׿i_ y_zO$>(Onmm"z͢(k0=!~JOAcmr v1;@Udyjʯ"/MS_Gװ,KoGaǪȟ4t:I,,,g<I e_W=x."cښ'k@tcǎ `ppp5}tSNy[S_NU~" ֚Cn9` e@8ʇI-bE10iQV4:ϟ?ASfyђHB(|XƜsR@TzjjWI_&|k^NJ:z$7?Dq?}FD_$zo~7/_h4o+QfffͦLOO>.c୷ު9 j 8vT7%op߿qF 109!WgϞ}߸t?L"'Yln#\ܿ~|(*fG YUٲ2SlM5GYS~|… TLa$D` b}خ}!if#FA$IҲ |:ʵa`mmr߼aQZXW,[c5 E%2ef(HRuNDq2SAV` 82GN=Ydcg8rad*7EEoY:TlKg M+Hj6\03ʙ鹶"MQ\;HX ʂE]ExI20|Ǘg-/`٨}[?,Bol\~}ӱ ]V7v:kI5,o?1:2B'Tz 䤊1 , ˎ9?^EF%se%@0A꣧78iOM-,,LzcfvتePS#˺tlv4IZ"1FFz G*Ag~Έ@\H86Xrd [(B(*EYoRzZmme5~Dli_^||`0Ʀ6"oPi8 *3=~o*'z榯^Ϗ~m_F|4=}h\phF#;̻/^Uۇ{@]F>mffG xo4swϟ?~7Ye.333W\y_֏ &>zZ5q>IюRdAZh0FW^Wzs3kkI~?=ਖ%[WZ jד chٝ^_z򵓕ص!c"2HXp-pյ"Tj0f(("EE$"k:+)jF~ҩL#Xgfnz`CQktI-b $8YWHqy?NTłu:h$qROuWjF$#ftB:Dkpwvkcmߩ5PRg<{;MO:s{w>*DT[^^㭍_$e2y>38f?6`F 2"hmeH?~7.9m¸" ݙ$fYvE/tJpL#0;;[Ύ:aff>?R!( f`:}&n7^pA A% TA&BBçr,,\ʣKUYpC"I {(+}goղdkqWϺQV;"QiGdFԦ& 5A7ˇ33iKmFIAVz wᝏMQUY8mFyɷ32:5j3Sr'bcϔa !+]H0q"E/?YU#1u)H1[;Ϟ=#Z=Z|)!(ˌ8  zsO.S'PlavYdrlsƓ[PE;a?zuK`{ j/_ d||iHqv7W勞?R9bcpG%uyK+0e $t@ޟ`<{ԉΞ>K/_6 v˪\y>s7aa b`Հtǖo4}z4gW 6{!Jdag6 e㴵бp5B&)(UĉZW)tyUq$Z,(P cT83;|şƏWCX1;m3ZS"쮭?{?vT~zRf;;bl%3"(NF ZPa)q xhsѺ5"Kk9/MY:F1ʇ{>a6IXT @ :UAEsII,_ܞj '% XUzU@QVeYVuBJzc`g33 QK[;3m46ZJI3' AɭN'wwwFYykuE٪n}`iii=ݨ5|') @y>(3һaL=  wD%A#slI&yA( d% $;^[ߢ"NΞ>ڵki{JaEEQW TCD1[=QIYrxzZO'e$I `,A DFrD (e B,iR3՘sn-v }\FQ֘(zCzY)ܙ3/WUwww8Mt$^-:ZsDFqjL4 9iáb\]]W?</;{W/EQK/G?^$ʯx5=q?ķαa%&egtUА2WHB*)4 QK}G3D%1;@1x ѹ9~177w̙o}[ſݼ(}-?xQM}wvӮ2ږ H aU i͙pvvV9ͺe>+ZRZmq\k)ʪ4'zR2/%@ ;޾ᬌkI°;wdR*JpUZͩ֫׮J<{߻q^\(u7$,a,"7zII^a%l[xlOjD!?m5WEQhSj7U>xB)fg/\p˗. ۪*sgugUY:;1ARٿG❐+ǡ"Q4 Pk]* C$4BZtLD 3h BlCf'"pOS$Κ* y%55i|R*Pz,GyAB %Xtaɟ{{?F5ٙ T'xv _boF@p~zph=~'iEQI7Y D?죻IА?RM?#<3z/܋`JZ[ʰeTtJvlou4 $p6"BHD6$@)g-*ct2 * ;ihDҁPI<߽{oml=>~r/S=1bHjj\B4~!:GWVֲQ%H(\]/Z؅ݝ@4i Au97\V&ee4C0S !2 _^[<: Ʌ;cf~ݧO֞mKRQ՞eOG Ћ'El%pngUȖ;?~ޝK%/FsfaXQNI|'._YyKiTZU5hga3LT^/0z\r9>˿>u\vIȇ]*qf~4K! Za oyss.Ymbкҥ=4ت (*"I H I%'%1zAsNz[qƈM@  8LjZf-q/FR a]9v>2C-^tbgg~>_v{cccnn28y_ze?~ĉZ?󵵵(Y>}&km߯jnn+}땕}\,()T ͘\zx^8,I-q)VoTʊ7momVYoNt'7?gNRa6hkQVT;Q-d?#)bRk>^HlN&:/ƘOZ!xQ>yb?2p耜v;J$s~&hґ.ā~~}0Hs( ֝hxeejIt׮]}رcQϵR[G$T)IOh+Ҙ**Jc3&d0NRze~Rc"8ivDc$aly^r3*>Y o'B LC$' o-;v3zIcFo~ _;{;{Q=ydjjj4x֯ t( @{c/dCO0?? >ѣG ϟT:_^y[n?~h/?ソnG?Nce/ۏZ o'@^9 -StRiG__lS/*2Ơe! cQ|4{q=v4(!=+E [һͤ1?w͝ϟz_wÔַV3c5s`tu>zcg7=z_ןolλ,mf-N/UJ1Z٬7NY4O6VnlwFY(^`G؎V46HWQ,f0ZXx핗gkkLePAJzV tVӧBz0<(|䩩&46w1ay.H,QWEWW<~ YʹaGUVg5#tֺvZsGt6Ew`yk9s P*+kA"kID : b9Z[#3o/b?􏎄"8E)D8S@RIdtU奔43=353u҅K.8q,2+|GC$RT9 ,\UE 6T q}/f+=i$!EQ 1X!8E<)c~-p0!UBa%C&"̞LxWkmӀ_0s=ExmmE/˿oO&dyU|p3ycc#IǏx<55uٓ'Oϟ߼yɓ'NvܜE]_x;sY\r&H?Vl M1777;;['q_<w] 7/*O׊Qn\e*WTZ*Unx7o|㭅Ff+A@ )Մ}=[4_DQ[Tҡlp֌E}ۘ W\<uڳ0aa)Pq%Ϟ-׾I2zO?n16$ H*%\Ob%f;GVV?|\(e5N@3lS/'`[&q7Ww _W ߉bFb0j@xo֙r3)677}^iuӱ6[ GĎua ~W}|l~e ,E :`t ;WHRK(dvi);0: H'l7[E8Ύa3ss .}./,У>8yM11% e! UIٜ_x믾vF-FN(sI"$0L\FJRkonӂ_!XIlyfƕ0aNG2/"LL9t RY(@4>_ ":f̲l}}ɓdi˓Z-v1;;;=== =VnX[[sݻw/\|[ Wz/~4,//Z-NO k'}R.yVhgpGo+Bp8IUUQRګ+rҥ8(2O>|ꞵ0^2Xr_ L>.KklT$ [|odwos+_^<{y!&1pΌ6@S JbY7v:.\8sޠxO64fC*ꪖ(! @`A,>=_ڷǟ*: Ud:6;oA7 KbPd{;;;Z¥Q* ͶTjiinESδZ3IpwL6,fgW6ڵƠ;v;RQi KvXdrpꥴ]8KZҹL5?[ՎC@ S03_A7'wY;i>}_zf{*.2 :d ءр9DDGe|vJcY2;,C‹wg+]1hG{:HHHB s6R(;b=L΁!N73333nl{{,˹9B/Z'Iݐh8z@gE>9畞z}nn߸,yɝ;w^y3gVݗ‰F?gggݻw}ǟWL_m$>OtWzYt<Ib?J7~~K_N( 猱ny>=T?ۍ|e` :mP$4]k֍~8KSd$t:0%]]yh-p ӹ=s.+OoGYsKsA\(ITR@ &c.܌ɫ ֟olw;|ig;;,UXB[` +gƩ0vve0 7vd*ٯ+U sjAB}{sg_{lTCYV_>vٛ7>lpNtѹ Rt"7qyQlz;AGʓcfگn{qeaES6XߏH tޚnJKmN& ID a&TBk2+Ulag>Ti+_7c܋z(ڭVQ9{+rsw1se1~'pv蓼2P\D%"b͗>u- %v-8N5c2@DPRJ0{,{|~nKEv]dfgoӞޫh4v$ڝ޸q7޸|r^M_V2s|w/ռ(~^mb".7wmo-7w?dѓ/(3NyN ~? N?w5 UC"F'k>}ȬSn|,lFXaieH pe'߻'7x/\7Fz-MS"bKW6liXߘVfdIiTaiV76⹳gN?u؍?-km$Tn}euAYvfլ/-ǑW?Ӭ[ӭfa ݓZS{_>S4vG&ڣ838q:wW$Yee$ ,XO3)m{nܹ{$3W\yΟ?ߎ(p8b. |x/:w+5if=q(@>?رN88[oD "3!E@-xBKmPuɓ'"|;D< $?|OMMyo<}n_k׮yQs/|u^zm7cQ937{MzOfNF~zƍ>Ɇ9Rg㱮L΂ d4 {~}_ի/x JDIiHأ<3/Q "BV e06>'wݽ17_[3Nmz2 jbSGOn?P+W۳?O~kI$a܌rFSs5](Uiu5vkgu+_3c Ѩ,5v 0IY5F;cf333g/ϟ8qz2X,7$(՝TjT Y;Lc࠻OO-S- ֞jo<{kKl}u -jf[ff3k}׿t{wQݼ{67wfARkkQUiv˚13fkg2tHΥMi[ >o[:¹-lꓽw~Q5&D<ԃϘzd48;'eG'Gx#rNܜ/";M%D$LRD^?|…cKΟn6l0e`dfi7IPh R$AeHCDg,3+RRJ,(^|y|cD7<>"Ya?xJ?TZ1>F *')fn?L,^潿tZ֥K<>cSl޽iyO}_E8$%|Rv4[0Ga^fqZZ˽5<2GDI"|҅|;o|VU兔$q/^à^' &DIwaeQ*ʼR"غ(J0n=OW|1NA>v9 ~h¨֜[}gjn1H0VBALزHd*kiqr8t:ܿtjZk.?|gYf^Vik D&0Ldmn첦d$e\TE >;$z4ΟtR[<13O[W}@5(=__D}vF7$n9M^yJHE؜v\`@ʧ;WwbΪ.fnUgZ-NOoY!~7~3goZfUeFEjfzk_dNٹ9Mߤ\zE~\QP}?V>1YvA<3t:$,ϐCxuD Ua %P80J^WRi -1 ݥDoFEOgc$Rֺq[k=rM+7D)AGr,sFs$Ǣ(fyL9v}:iG7ZytW> <|aav1Ǘxs~g0 ٘BiSe[9fDSyUϧ I- CG't)(υNVӶ"̲a^USv҅7|K_<aS˥0^>'T|'sNSD It&OG+`bV11TIZ* a3mQlg{mS]ɏWN^|{˲'Ϥ 0(uRU(F* 0Q,2+W>{镫Op*wn?x'0~ֈ5\s1(**1ֺ* _~ً̩^zpg_ϭTЈ` %͟Ջ::a?F(ɸ(l@tb8 Al^΁@cva$TԴI* tXTCw:H"Z_]⨞$!Qjavu Q2n|q~\.޵ˣhOo ~jɐ4q?};I`𢩜M}WRBqs֒aFUΞ?} f3~ͳoG~q1e{#0 8g]ya,K򤉪2ry(tRDr0+ZZKB@Voi}}DQj=\yRKg"-9gq"|././zBRF28QxyiJQ4n.8s[}y+!;2_fT1X|]u:d^$@ZAG$ǵAUݸs{_˨L>}ŋϞ[\oTgPҧ# ֺt(d7ȝZkf4{"v[`r>ȳgrOGiex|"F䃋uD`0z]GD蜳qHlⰵ $ 3ChC eH0O:vWT^{{w}}uuu}}}}{{{oYIRK#Y:>o,k㵆sZkcIHERԄUe?mXF Ev믿ou<8^P>VB}1d@sl@( 4t5'?|tOYY/_7 DAhPH$W6Z%ցtkOt޿|wS|&.kQS B2F;ejste$s;(ʌ*.*=w<,Jde;EIleYG"(STRDB'O/+kA[3fo T#:^뀱EͥV^O-//Wy]Y䝭bnfvν'>ham\|jyr|zfQU$ $ v$N61‚SQ4"kY`m\;N7a:_A:7'Xh̓A1`8zeL;SC?f2}n]qO׻O-g͟OwHPaTUDJZ;kξ|奅9`βQ~@HVWUE'ωY4^tGCOD<`S'F|,no`MuG9%(HBAL(Hg^m:_f`BwFK$I" DrbǏ>{y K )Qch4&TJYUEƸ f<Ϝ׾/Z4I|HZDpKzĈ$*/-;lgdcgAQl(6ֳ[} 33Oy۟ {'Μ^8dEf!s%l2CAI urEY m$9$ YHyVTA UDJ h͉i: h@Oa/.?yB6ٝO#U#:[_҅ ODc5EV(q3$%Q\2e](޸~}޲L6D0,r"#Ljǎ,{?F`JSiVݿw޿ENW[ۻ[EU:4VU_+>==ɍVoݵ]]\v_Unˆ噟G0q ia>, ?\>/q^dj,;gZ+NJ72Z vG'o||gBX3SgOy˗.\^Zk7g۲,lX 35{X_'_(}[K(| [BL KLGw^˂ql|(KH٢xXR )vnc ]9m9"I1@GcEA-,~xܹ/~[`۫;{$IjIZjm *āRQ>(t|;o|8^yI:β(& >G_!" "bƓ'A]{?|_}/_Uw7T(JHg3?(vf믽vKipdcy(h!j؍Ze :P*  3]ic+2=XWJkZ8\.I8єŰߩ4A 4MM,KDe) v7(Sg'εkW_Wp(DmzhABkY^s?*r#S" .z3”A{v:sv8K4eeNg{mӕg{fGeDFs4u:" z婩ZDA0P 5M!h00/C鰵6u8"}*(G03au^ox?8uNJJCUU& *7Ɖ?{|sug~v܋뭋^^\}ѓW4a`(M&eT*lQ)?`h* +ّ v9˪ Nkm-F0 TJg i*.]m7(&\b(LES΄퓣v=b~dvؙtfG~ꂠ?R+$IF0H*\?|sR\TPUD%5gg~￿3gEk$QU"F?^{)>b0Hs[%9Ls/ͼ(̌%Ҍ 0ξxI_(_ϽDk ϕ?\a|R4rI;n\}33YCcHoX 0TBI@A΀\ BPHQDSS3gNK/.WryakڪQm2:s}JV$Pb/?j'E6΂s&O>:~x|6d7Cxi_@?$qQRTCYG|*-y>!H|[Sf UYzDRJê*Phx9613X @ݟ$ " !s$P+O>yxoWo'Y10 CDZ3h7fL߾q7cAمKWO_yɽk׮d~z: eY G~Q90Q-ԅ( ?ɏw;d%eT`KWCQX%XgL*$ [;_70 D$(\V2LSɫav՗Z8 K/]Ji]5e9;v:~ǣ@)jScDB0+r (J_=|pssW[_Y?]ol*5Uĕvc`ΨY5iS6ڵF1)Ս\@KSkϭatP$";ٷ5RJ % Ƙhc51u_G[k}&O۵5`3?{݉~z1~D"'H= Na1@ $$p333_ B:lY3SJ@H)}q9k؋X : I @DrOU~,`_U1x=$|B)+T@4 >h!BNtCB Dqɕ*," ~QY<_ ݢsp|AV!IDRW,Hk  A^5(R U qJK\8WV6v?gwo~ęo}xo*RHpHA<]mʪP˗k^G?u0tbiLmܘl]I4"DA'"l(?ɻEY F5]*DP̠- uf lDj"Țoibw81y(Bd 18]|G) 'k·[@vOn?xp3 qflLq=T- eU^N%aƣQ/ 1$!_/{f`%tg}ՑR1Ԝi@8dl]5[un++kFgm#?P$0Vf_R1-'^ZkwZVs:Bft?mi T>}1'Nh~/p,X08 8B)/^xΟ8(TUUU1YM˓4T19:;Q>D057cH㵕Fֵ$;sԕ_(ʬ;(v:QRSf+C✜(NBwģ-°51[*ɩV!P m͆bf) \b>ߨvE^JnjI=?Ucg7wY [) sc!@zispYr'%!3Bw҄?(hO0 9\8MXV,1h~ Ѷh IJB0F[IA*njza!;~3+OVkKgO947[} s\YUFdz|:oHs:>˫g&;O'qрQ)>}TDc 2{v$!2!"BIqNKDD0jȘ@*ػlx4>HZO@g, D()Ӈnݼ3=S7N^ʞŢުY2AܖU5S':XZ>1e҉NHko|tМn ˪ B!KoP F>9,Zٺ(Lc*7>e-SYViHI*t+6\#fVvkvv5yAfRyhsQr(A@Q`0,HaJC+(*Q12( M%[HJ2T΅3!!BOs2z2`41V-!eA(I0seoxQ?*4M$~qJ~DSc.]rʱaI˪pY֖'dFOD dY5,x8K 0cz$)oi1';PA - r9 D(hk ((  ADY^Y #0$aJI7 :tBH:`fDT 5T$\i;}v~ \w5a-`tlQ֎p4ua%EDJwGEqN{Ν^z5IBT,8@…7R%eΆQC2 gW֜p a?~FDEkc 4 ZZ%R8MPPUBWBz 3.7 ;+e ]5-rl$t%.J" *\YRC^dC8BS7?` JCӤ0 ]lhq]wYZ &k#Tq\@ְ軼?ҨR?+JѠgMǔ#"sF vFv0 Ceό~Z,y_O|-89/gp_AH~Fy1/H WGtJ)`9WFA qP3Wv0ƍr7byyϟ?qıťz΂!I## JD^X0XJ_,ٍ'TI1L 9FU;jb; XC}!PJ g- 1eX[9 me ;*3眶 Fs|SBD)H"`J[ 1} b|&DHj f A c`U*EPg}|gϟ}t3| `!c 7۞##6ֵZ ԥOڭ|x<;s񥷾>xƍ i4 .aܨ7jsn8̇!7|jz֭GyQhS:g@Rʫ.\`h Z[tL(uQFI&6fzبZG@EMY:afԖKCh$9"Da%!vEpb9vnd Y-[2dV "z}Ў@R頨tG*߻|Xvs|~nnqCp@sZ֢8P0vZirmgV`Py@}2N 6e1눀 2&q*tpX%YZxO!^')9,rq})ৃI@Ry E1 (+!H4N"B>dٹSO=}ر/| Q0MYiB% :DI41lЧ2;\ <`z grM|K::= J!  ` "&\2B(;f 0CvKF&cfW ΆR! k*,u+ܻɓg^zܷF[ϓ(f( REV8Mx\i7>кzl9=}̅N~ލ,e?AHUQF=5dUǗ9 pﯬi̙3=](MS)(Pss3{sa5IW)4UQm9DAeQ{:Y\6iMIaGQ;' p쁊DXmLRW^V}e" U(Xa*kGjM€6FYR ʂsankWwwjRWS ?a0G68C?0F#F8܎ys۔QEADTLE)%v(+W: Bg~XoRܹ.\pǏgZ|..!dlxe⓺hg1:+m^h\n!>S }! !A$9`gcY(a!3p@8FvTڲ,1D zRI"!9 9}&p`;RxӕB!HGO=}7k{#d2TDJ[y(Gfkihz@F.\8~hݹƭWקff6Jh4 +8 &%4uf}aQNfA9uPWD{nz" D@|fB[)plSfʲ# tUm)9 4eimBc00HA$DFE'.2*A.0N\e WBB$9–J" Id( <[]y$x~ *0d\neSSE1҄ Y!Y@Kl!E1F @J$!ѱs rsX 8Td>iB@>(( FbIDJID PJ >9qW\"?ߝ/1qBOw?\TݿJ H1tuzv -C<Oav{vvjN8|l1#BVUeR DզeD֕H$Qieֺ(ӔBz$(N+Җ ˌ̈$pEƸʎB@ '~ $ 1a=E+%#WUV1(BEBvZ+T2f#D"eFK)YǍ1FRn߽ykYaklw7\FϪʬiI qD"z$^Az 2"Hd rvsZ䨻LVto{#Ԩg(k!Ȍq9{{?ڿ_><ECE*ȂxOr4y>-ƭn|wG?z'ը~}8>F"r+ bsctztJh e┑{NN[7/FJ7YQ+lem֕p|;n;_׍"yVj5=hwnpekw#r' Z Ka/pYhQ2ngw*luXNf"6M7=%S%sAe|= 6Ō*WEǸj$JH8r{J1 5Q>N؝[, # 6E.8PyF@bnE$жzJZyBdS). si^|pT3I_/>)-)r`~4 I&wD"@,YP@# T&X&(2e8w{êomm]ܵ[nm]wm׶u:@δ)l:LgUλ">AK!YfY]`>\SOH" @(WXJΝ} #D% !) BnU@D$CBsV[M"g}{w?xǏ\濮rg>yIC8pieVMw?ZeYN{W^>{F;ۨ)uiƶ[FWVY:vhؐ)Jyp  Dj[X,ڶMy{sQz!QAn6T A:׺tx1+E^,7+W*%~7Ɵ믿nx?>|0,e'XJb0*Ve2bKs 5h + X2 QaAEVi I wQ!x^P ))JPA4 ltT I)$Q%6˴w |NgzB/CcZ.uD,"޻^ c }'SzO4oYһ&P 8pr !sL )U#R.i{YFBIv@FYux6=-އ@\ׯ_߿//ll xs7yE.,ˀNY:@)!B uK 9ed$ $=g B 1#Q1#jeA$6tGO5 j$Y2ƣч~p|{/_淿>k팃2k  +^M}Ýݍv߸uzh{ㅗ^2˲w% D c z88s{ha,ȌkUlXx'[yw:Y.7X a9KòYIl  `3m3]s" "!jjU5>ԫvAz#}w~x|t]|c}ڵ?/3ғy{{"U,=ke2DЪ i\tֹLBLA>8ULJY i4Q.v5eIqN3$y ,P }M)Q tK"<;{V!,ZkLy祵^۶ [u]׬{ةd"")s矱]?¶9~ߒ(55K!DM"*B)!g$9W«B *r{e1_MG,`{kׯ\qck{e"ZfBkwN v>0kmU,!79&bL{NP!cLFO3!DJ&9E=cI@¹) js+y)@TeYZkD%)yVmS,+]W7wÃO'?_[o6U9*3Aylˢ H[W ??}g0~{QhRV3F!T6ͺ'Ӳ6;e7zE= 6|8u]2xgyj5\-g,PZsQRpK`sɵ]@JXdQĮlnQ]/Bll@gC|o7ww.iiIŪ d콻7[|W'=>;w{eesMPwnj'vY.Xz.[xdʒFdQ " @8JWC8[*5 R[P$&)#3OAi DADk̲L33bXE6b"$m>DŽEUUڶ=n6iC. E*B9+9C@( NP03D"NT$rMSD\L!*1Cfn^VQUU ëoݺuK.CjChV8'=dI! G>P ?PAD(VFZ\w3ȂIȌq.rS"d0څcضk#K"kQaRw{띟4]_W~vчw^\`Bg9 y'=NVbS㭫ׯ-o_WƘi/%X [9>\vm6?u=k$kTcΨUUE.Dm>-@Yu ֙A1TBB @da˞ޘ zﻮ*ZiTbŖ].`co 7_z&BDtf0n㪞 ^ǦFijTz~ʷ_}NOaVh,|MuѶfԑQŨ#("È:Fmac*4X+4R M:"֧kr 1&֭`vq&Iړ>?swjBgƔ"˳ OCe~.ODTJ"|J鿢4# 8yFAvy'MX)J*Ҧ\늨Hu!(lRƪ,WDd'~7Z,&҆系~GUzxپy/tލ7]J.: G*)r9"Im~:$4Ep=M'(R@:[ ;% nk^լb>}r6=!Gaõ2 HPZkgGγdxnsg{oӷ~a7ػtekkfAӵ[; t5^A4`Z׭cK%F s\en=jѠCmYٝK+ŪWeI"~QYa{2HN'Lsg׾}lYtyz:Y->tž 1YWkC]BԴH!EC(Jf2v6Nz^UXHCܹ ZH[N (022DxhLp\D NH9Dv4Ve*G!ƘJg|9DAD"KzAYz=m۔|qP&rk/FO3sϠ.&.RȌ~g,dWJVm*" JkR b&OSCF%c"H@.d?5 ŕ+_y_|qwww8(D|>1"  $w6I 5qՅZT@`@>nJt<$"=H("޻`-i(J?zܯ-WϪtĩL69QGڸ]XrWܹ{Wѕ/\6mG^.2V(Q5<?|pXe%t`I[z8\I`DAmܶί 1;HxZ#wM2UJI!VEkm# pqy //t֋M.= c1jWe}7ikB:ǝ"V rcu57-mw@ jxOܽ`r2=9zڗ(H z#*BAAT+ccD!JX|1Zc6]Z,Lu:"Z({ZPN3j:\ 2Ƙo|b1mS75~!|XcHq *5&y_ի.\DTCF1Wͬj$g۴]QT -.u݂GὦزFD9!D$h01Vghrr4~zfsvT])s# ' D+zVg^rz}7_{B&uݔekBb6nǣMRVee8:.{};j0iA00.fjU (B1)zUӤoT<$6i MׅqTpSyzJL''GO{x>N|޶m6(rc-vF竬@CF6SdXeԑ2hCjY®*zEQE[[r\C6WJR& hڦ}Be cdkm(BDD>kT@=(Y?ݮO|HH87?%3>K:0%Q|N6* #va\g"bBI7Iǃ5c! !t'g~ۙ5077W\yyK{4],F1ȩ1bK,U$m =30M$~h}\#~6:헼(3aWKDݯzª^6Ya>퟽^jM?xf_u;GyI`5]㬪VK O=!` ;7(֖y!^߷23YQ(0όr53DZ̻;w5[\e^eT#jM>J횼*S= (@*D|@ ZU~^f.ޠ/!V_W_Ɵ_ }'4mnk{g-f>:֟dY&"H˛U1똙͋1e1c$K8Ƥ]Ka(munX6M3ͼZո_v/̩8[OOOzjޣ.yϒCEdZu]Y6,M]5!@IkeHL9+ qppp|rw\]l۶뼈(JRd1 (41kYUfHO|O Kc籃jRƿ$H系s'^ϝP:I}J~i$@#gE80\l}o{޸?ꕯK׮]ۿ70k C.!+Q" s[c)ԀQ!\=A(eں^Ϸݵk[GG_DW/WjfVn-Ib8[ᣟ ,/]nwdUwW_sm, ˒Dsdd0oXSpz j0㣃3Ds6g'՜4 ~H!FYR*D !zmg4g]].&!d2p6njr o5˽r8[,'󙶪7mՏ~,ˮ_n4]u]t !e,ͦ,]hQH$6RkCƪ,,C("E$u]t ֪֓iu1ܟ>y5֖^ػq?e:޿s>>:8lzXF{&Yڶ }i3AAYn$mdʞefZ@Z$>wꫯkɃܻjYcwKɲb0,| pNBK*q#?b+V{5QQ4i!CD5`r"]grY׿ggpRҗ(zEI)S 6]GD .BzQHe~iwŗ_x饗/ݸv wMJCcih^ BH4VLkG5fQw1BZr?g67ۗ6ng'mojFǏʢo`tn>~66||e P2h4%=z,f~>{Q{pݸvc{c 9WLO|]Q UiB伌L0DQZ`2mB1v~5WWnmln^Qd93h1jKB`spo|׺?ݻwLSK?RZkTh [ _y㫃^gbz2}ѣWyk p$+U\5  k2/j<3Vm" U_y_zy2=|RE"rqcDI(i<1 $DO4ع?w\?SD$q:0-b#v- oY_=. ~-6=j胐J>2NGN7&@S6l[[;>ݝn=w677aYfEQDBu!=E1Hytd%PV sdhe1(K'/_z!y3gYl[On/wiw/wk''?&PeSm;M&"֎j|>p\[@r9'ߍ?;;ۮ&擃{]W:畵a8QfH9N EA}Lu빛/( VDQG(D;|ݪ^ҫ~ō`܏Wm/xOw~g0yY^kj0jG{VYKZ `XiclZ"(E.軮.Hy\vMB@67նm[TCwvrZBmNS53ig}tc+ܿ}g ]YdSUoڮ#QFuADBT!txޥK[,>1I,/ +e Le2]?1!M/fO>,8aJ}@Fx`R D xZ4ߔ^E!Aua&<8=ccs˭ X1B:5RGĠ@y?.NX'oYmml^tiscml^~<Ͻrնu\ՇNɐIp!n1d6:#W~_k_ye2<:y{yjUF9IG]@"}}xvo^0 NAغ.x6I ݽA j ácL3;Ct6|1f:2R^]-#wJAo|[;nbp>tn@@*;юT =;}vHd@i*NNή߼/6ƭ΢ÇG~t`Kk[9 VjպNkm\D LgZkv.-_)Uu2%U {)D|?<,h t_J)k gY#ϥ\ BP*ў X82`紫NLu?l8SOH izv]Qy 2r!aDTD4 G"=(J+.U7?.n{?x* ^|ekk^x+Wj0V(żkjC[?Ǐ=⍿/+]h&3,{1Dٲv?|;{kWpLe|} B]E^i27od-˲̓wm[$Lk- t]3ww޷R[kM.TwwXM~?#׹FZ`>'/kWoټ<;;;>:ɝ릧Ӻ+ڵk)Ct:_0n꺆5Ѥ߄\Ӷm罵*"4кijZ+L+eZR@Fy,s@BJC]JD6psA,r;CFbz<n߸Q7U=]R7OV]HgUY6¯"!Hs2/Eis$qc+Wmd6L&buZkҔY,VJEE'>ܡ%|.t?TE1B׎h(̌RH0 i Qym2ey Bh]H:)ARyZ"@ Q"$4go$a@RkVi HE! a(@@: Z2ŀɡ|΅s˶;->y>eUUIJ?O<~ޕ[\2SG8z1?-?|7_NsetiKmUk"3-˶m6ۿ|>_.{{{63IbXJ"YV:MT/"xׯo|jM|@hB>H[#!6ݼt%R7,u,uTX̧hᝣ\WO=Ɋlsc#ۗ^{7oiYSy4ۻrUږUt:sEuejۺi{OH!Dhk5YfBY+#,mEMjE"RJ)׵o8҈6|6΍,X#GmMܶQƭZRafm5z5YT0Xzm5DkS 6}H 2xiB?~<THB,,88S\k IK$%pr\4$(QR z>EM+p6?~8WW]7|膣뷾ԶD.#ر=kccw<)7z 2tu@k[U}aBD;Y|[oOz/g_ƥkFJ"{%ʫ/Zarzv|tzF#@n6+t%1fp3t?PȄ ل2mM4M=R 'gg{kE4&"El RWES?:=MFQTyU/~MDGC'la?+ D,fn{@kjN_ZY?.jM!pCl-sr\KSKxno Ŭ#uM9S&%pt97SDD( oIjʵPHY>QZ& y-#|ogojwQoEl潼 o|1cʢt!BrhV6p8̲;Í1yp1P ,^G!B]e4;9:^K?a;9V1ƮH)1Jo7,cHie6hQ%%"EDZk]J$zՋSV:ƠPց#"1lq7b{{"k/!Lj.Z<όyn,E\ɻw?fE=C\׮ QD֨E,htڵmclfڶ1QjEZBpmwNFBZV\.QQͪa:׽ 0s׹]"EDEgXcUZkms>ᣮm77{j\u"dzz:oV4-׵7ڵQ2Zd0+]"jT  x+V?h|]lVzZۢf$Y_z N:yZV|9a~r.|Q>Z_W66ΎQJmmm.VC $` &~ /n$ED1Aj{L55ٓP>WZ .Kױզwr}89=Z.wv;!+p4lTYʢQ\t⤞u]]=Yu3=D8;~e,(Ɔ@QM'eշYC2bQE^ͷ>࣮k_~_hP@ZGDHTȨQRZ,QD!DsKK&sbۼᆱ眿ڗ_(^ܭШ uB]pXsمBTQ0˗/G!-Y8s!f!4}mJN3i4W(!֭Ƶ6)"&]um;ri ͪ~b>eg3mA_LP~pjch"Gtje(i4l3F QaqhՓ'~[7O+"/-V&"L/Bށ3g˖ܳ/s3ٮ5[lc<ee;ۛs6nVA2y .n7^ݭv;wVuSK/xP}߹ޠO .( .SyQW&co>Oyz buY 2}Jr\a]~Lz(E8E ('J7-˲כՠWVV9ZqyPVI]΅v]íƆL.ʢ*HŪbv#F 3dwR4fujAPl0O&Bӓ"A [w{4dX}/!rѤ9ffRjMsk~8UE(-MQ~ z۳vsxNMG.]ٺz˯7suFαAb􈢴9]۝LbDe!JkdX={\bs9d6IIЮIKTG$"V<7yi"0^1/JđYeQ\yY!Mwxp09=ͭĵ9<9hf#9P ]hCMn3 w]Uey^*g3[VW/WOYV679ZY-2dN?6,(=y.6zfz^t]lsqE=4?3,Gzmgk{ONz^Y(rR,{u-|ooX%9ѥK{EyOU:,B8<<fM*Kf^V`hXf`-|Z~Ƌz ` H "!ui:v?\daˆ(QLb#O&wyn{K1!rj5FQ>Fi)o;*EUeȲ?(qD&ׁѷa. O| 7oܺkQ+R4X{'RںVZl pP@#CHFZҲ3Vs$zPUHd^&Ӄ{gIZh#7~+:nk!NbGDLM1$4]|:&1Fp8(ʇ.0-r4M۶L w{2k 1:߶m\=z,pP#2Gx"Q(!fEy ]-3M6ۤ'uB$+s&m[wO;9X]xnWE_}HmYc/J=TPV+ݶz>=#ڿ9U,G"1[f`0z4pLBtb$+H\ H|uQu3i1?>Oa'l& uk45y6޺|wk׮Txާ0-@sg "gr՟JXR7 ƶy3ũHzE3k%>B5oU.:9xo@gYF'G/q㍼vǗ&x8>8> uܮwMieI JF}پ w>wztzrpo[67VB7l6+ŠmҠ].Jz &0kUm?8x`-P5k0h*Ҁ  L9ll٪@٬gTk/**=7|kGJ'庹1u"i$mBhn(Efs7o޼~*g?wXd2٤q5'jh[;$6kUL&GGuÍ-l6`jeq;ZVV `t 0rxw:6/Vur1;==ۺ߯z~T@hgSZsD:7n=_ǷǛY>ysx/ONOɪi"{_zSpx!8*Hfγ\5 @b@-0(EIxRDD}L J@zWRۆ)-XDi):]1us"h(&z1r\jMLIy.teVUUWV5ƾm}H3;WR@ *fJ[e̊Ao_s/Ұ+ͲZIAYBZ,\wnAdq<΁1p8>=0Vl6?\~>/M'bYrU|5A<3e^[dC-)*Η>uvzUU R{Ŕ^Fl:_L'y]/EW_C>ѣl~mS{=.ezzFa?ptrxUWF;d5٠Jb輆eQ!e*EҰJ::BԆ$|!ܼq>y49ǫM,M׬uAF 4BugR Yb!:*ܥ.%K)۵u H*%H͗^zUaOs 2D1F}4]J@MabM'ZHSÝ7%0i}x?:9z~;ӳ^^xܬ("+L^Κbwآ*ƨuYM1PeH%!yukmM67$3BeYeyڕd>ͳtz๑,˴y%ᦔ(btC۬s2cZ$DC_/":ضr޴k" -(!V\>GmN3ҔgE11]'x!7 H)繈Ĉ"F}~oQJծHU:!bL(c b$P2#F9gS2J%zs**sg @LͭөZd,*o$v'wnt٬i>믿nlλϏ'bjڍۇee=?qUfۋB+sK|׮_ G}K_W~g1_YZ+"^<8>ZD|`IKD=~NkM]k>C,10;VMKA( ‘'r8xma\mS j׵mY(! [0FSRkB .'\C/Zk)mǵsJ)qM*df*2M*%D )"D[s9,LbQ%NP6i& )OZ{?QfGm4*ˬ,휋c yV֚Qig)ޏfMӹ7_|tzx{ϖW^}w'b~iݷ7/m泇7?A otO>{{^[mm=Z=z@LJd.!ݕRD8*$DA=kc39,+x06ȹ "z(O*Y13 tq;@[k$yufLYa,9I~RlM^f=[5M{|[[=UmY,LfȲXm"C+!CeMտe&bW0 {]sLbB ֡D̐Z[ZT_jx|pwi QbtqnJcLe>"K""!.$YMfsd U/SJBh.s x<."ݹ1ƛ7o.߿\.!RR9$eO,H&mcU/58L&'ԥݝl6NEuaZ*geX7v4], d=y/w"10~<3!a F>PVBʕ#B1F BR zF{#h֚/pFCP *RkmLiK;5P \W qs~? GY7W(}?=ˀ__9~l)Woo޸vߺKmo(v#Kw!HuUUM`Sk}i׵ ,BY(P$7y24Hnm`]\@.)j 35Ɏn=OO2*} A{%D$”@|QkBDZKmHC,z`4 FDhUsv*{=ѝ dYW-lnzj7_6 ;2D~bT H :' /lFԐc l)|Ct"q9O>*zW_@Ѓ^O)oU!eY2DfrŴsmݬP$BD瑈!h2 ]Hwe%% ㍍dr|r7apЯFDeE_{aFZ)Dݹ.46p%߹yv69xX!GC߹'u릳t:.5༯f5.$埳(o>E6LBB*>1Q/ {͝s{{{{8]4 VSڮ&&q 8HBxf:g-moc>Ƨɀk7(J%C(1F=>z2B4ыTbATYu.z;G7OrsTްq"lOOfGW͍EgM|^<99Y NӼWzYm۶-"/sh9mH)%ZEa`׼4g @ ffIQ}?BL(0⧒R'LbL&9JdpBW{RHZ Cc#cʔ2[(C+c-Є9]VHPcoohc0mahg]7ru3"E@$("!hm 7G`rQ^YQVY 6+.˲( !%6  Isd$`%iĴ!d*%Q,bn,ҴrJȬ͋b\4˕PYj׭iBÄMS5bM}ރ`tɩ\rݾd2އr9LRQ!.UӺUӹSU 1g}|EE9^/ou+%LVEje9<: '3!TwJNcoR(@u,_K I80s1}))' ^SԷ=BCu~Umn [׹eB^$ǓiUZֶo\[Szn]W^zjN]7w南_۟h4gk|ɿo7kv9~ooF|[>^m|o0Fm[:נs95$L MOoX8m IkH 8id!J"$[c<0*QߒQ"߀7E$덐"MD 8qFAH)]֖sۦmYN?|,Fxcʕ+Ҷ aX$!Wê6MN*CyJr۫Vun|tH85 ޅH@J@:Tm}Q%16˲4m2201 *BE:.KpiCc11.˲6]l:WO?L?(mWU9(W/B$c6~jyYXk]QtDc )PR1D8uء$:S1\1 2׼Ѣ'I{ш4"LP^ƠlF=c+lWv6Ia\ Pi}-FcȈpں1S?1.:!̚7og~||z'V9cMX-E`<OmQ^?< NO1Q+r3_LR(BZkvHFc$]jC5[Vk2$̜NO`aaMJ2B>ic)WbtPJ( iKm@!#Ȍ*eIU-((Φs|HYMJb"( R&=ʚpPp {EEґe`mM56F;fcdY=|݃?=c7K1Ht\$pO.&lJ5ܻtiQ%1&X.c h?`Tc,!2K iO+RPEA=1&Ƶ,gZ%`:7{F I!V"'Ja^B\LgggeYm︺}Y6U=_!MU?>/]M+"zk?ۉASg"B]%R@m}<9;kV ˗߿j: Etە"1%jMi !蝏!*q-Y{5~` Rrg"~A .!Qg|#mw%Qbb]+S(.*/e6FM]Yվ[ @*mlV>vYg&/R[?_|ժ*ML=ȫrwz{ks({ch$߿swM!2B4S  DįxD5YU^{eʊ|kwK2ZsBHP1" {f՗Gް!b]GSnWu߬j٬ꮭ"(ui5͋7F*v5L^Kc\AiKDg %49x1~9D3iZp$.φQ@!Eatoz:xr0ǃr8|p> `IF[676_]4Cq),Q#&[Tꜛ/z5 d(eu)VbHMzuynr Ҫ26:uXsK]CQVk.2+aB>\4\QIk RjR`/R>eDPA JL!TDJ8M (chZQfDo/K .,EjX1ޣ 7I5eYUJ7roW.\rm\V*ˌ.tzrRǛg̬ڇWeAku̽^(P@OYeXKY$u3Rˆ"Lgzxz~M ֔Y$*i)u19%ŐҨ K-I4Pa`@dШc~ 3Tf\W5FcUUVUUUVT`467y1qI60 *d~)E9 ʫ7%h:Z6UӺnY7M7fsx(n+{xccc17QUUy'޻̊4E妤Bu.(YN.- G\Yמ ESOON`ss37b|o;G 5uNx\H畢f1,$9ze {z2zX2Į5^oX=~kkM׆tA/ϲ^ۅYu%K@n48P")M=W.㭟ÑgLkT\/_SC^&4pjϙQtQ$BDho eBV)}/*y$%d  Qlg'˺f#!jmFch0^SÃիE,2"of:^rGF~\0Xk"\fI[YtZ1sp!BX38bE4JƳλQ(z!˚\'OiMZTǵ /-XozS&@bPp*:EPp̊AJPP!!J-* E 7mp'"!Ey^&SYfѠdYVyr2;,Y"xv|LQ^FIdfE&AnE/Bm3]U3wȵ2U[*IQcbUkZC}y i+Y `uE1-MCy׶;;6g'?^.z`FR!ֹ;MӴKߤkAJgfNZu΅إHk}&It%͸RZ+2ZٮGGGwU&CktOR"B@Z)t2ܿ:y`ҎxYF18U31,LCalJOGb:["Q&~J=; Nǥ:ħ>_4eb\`@-)ŀ( Dkc0;B<cNaT$`ږ2E`E[AV{[C;7O/;ϑvvױ7?7/DU=SJ!WX^ "ks{ODh%i+5TZᠵm[ ! ;f4TR Bh@a#)粳L$.f&.7 E@%09SnT2}F`.)#m^x J<1ADܪBzB`%"( &E`8t+e%A)a_'Y-1*m*Yc)D :7^f\!gynmidVoP4^o ͥ"̮ ]ۺ.NNzGOۨT,x^W*ibW7iC|q^ TJ39r"*Z{n>|x||4MZ/QeFi(sMb Fa *"kb5q)SݴRB̌$XX$v]J۶]ׁ`Y z(e>yrx|zUU- kjYw] R"#xv7u>̗S痶ƣ^uv:[ xq.iօ &7"6${y͛7S9 ӨDunwS/z=>;9=Y!fEgSG1U0O]\|t7߿e?ƬY[NN1N9"X9$(gEP(9 03 {G)齮|:@byHIc׹My`Nmw/]V5WW/_rx\p{5SCC4p KCcFO$bb1pH)9wb ) b.8Q b`0'UBq㷿_~'U6E<^zU}kǻ]]WU f`}]~MfK5z !sX(ZonnlU]3OuUMy_jv1S&bpQAf,LS):6#YaD 1  )"B*<&`6flL4T(+JFy8AGNCb}D s|5tdbɈT;#,.`Jْ&khgx`0Dc03`+ sw9_`f GbRn+fF4_Rhxv\5 yiw)[%d|}]yyu>==;>>e欚bXz{tZ˻Yx@v/^_F"IMd ppݮa=)/Й*379CSSMaJJ T׆ETM9\m6ru۶9IB|_]]nw؛"iSF瀩SFe&#ڍngw3|ØMGv8 eE09в!uRV( £D@AC@),@0Ԉ Ηڽۜ33Ź)-F`4(i=|%PgdO%$@2e4=sI {PTJgDb\#SMd<#ɢ9;^cA9d&Iq1۱լffxX¹PUjjKbl7oGzc'bI@|U)nRJYS)eГ6 vѭ3rJb#*99~DR)usΐi2t>0Ulg^Fmq9D%own#i\ۘBA׭W*B > 9\2jqݮOB૛1$<9/_3\; ݰE) "ڬ,)"r^˳Z< c c"$J-{ġH)fO_|6LqFu9*GtU4gC*I2(Y8\;5Ȣ|yÈ{4j}D4Eډ$A =x}OgCE-ڦi,ͬ>TgT$FB;$`Z׫#is ~JzfeDT5 nvTQ[>0*oPUiot~7;Xm]=:>mQ/=y*~GϞ>կO?>?;Ґн"0;% JsTtLUMÖ@f @T i ~:: Ǹ\eL)3jʔs9* +%sOBC@T(<7&  du1(CFä|'=Y􃋕k㷂f&sN[JfvIC篯(bAH޹p$KĻ:TeYInyv&m1|w4 $eM68oNY0bd %Lt?z`ͿN.C8oT>B*'C̐P"4PBD$٤|~C, A]WѡCc@"p.x5RT0jZV.b νQUUb:iJY$M)n]~~~}JRլ 8&/dYxA|ӷn>فafHJ 0S!jm?{//nnnΞE,\"2NcuȀFO!4m{^CNf!xލ'ub\Dwz.~7!HʲnRJWWm2?o=.#F)}n!Zj5+`FS08㦭1Sd4]?/=:7oU&f1/+U,p}e랽j*z|zrz|lj#CÜr)O9TO.Ɯ-Yd5&LSEӢa1-/A3Uc4D( i9aJ("Tȫ??\Eͮ3BcbJ5?R1T]9bV#m#DEӴmUUFs G*UUWWf2pa!(v(j"Y἟"fz_l31<NC]|/x LP7NGq:= !*G5 x Af?@*̊3A6@5UP2Yd<zb‰ &>QlRQѯ׶u] $M:m.EG%9\ŸaEkw[8MtCD<:7CkdZX{r??󨯮_bxqۛ陁TX(iFD`Ί_.r_qVǧO(QXDnA0bRpC??]-'GGm $d9%e0y Ә~\.f$v)VOCf ;PY,ѾR'*lVyWo|J]Qh E?@?lf~s'Gڗo(~BU=ekr9R-3C.,$PU #;5V3Բ/`B *fT{Rf'bD+U 2eQRID,) Bؓ Ԁ2u%2Φ!iL5Bp-F0vvZY1 1Sc1),ND]^PfE%gfE`3L{{_5)}//on6ol=>9-Hs3bɦn[9gY9THXsbƿ7o}pUO?CPT0B3jBwʟ:MysE+ 3VMQ@Ӥy<9>?h6Si1;BfU43MTm`NWI~ _DMJt7nR:=YwL%1I1bdfO/^ߏ۾n!|{vVCYׄTdڻnj #(9!y[ ߿~ƪmqR)=hZ$~x[V3Ǫi)=dnr[.4 {$CLj ۻ`V}30U`}6*Te>6ar})šos&r!#|@f ]Ɩxw&9GKTa@߻*"A+QH4â(:, .q%;ǎ-땬$ԣCˀ;@f ɰ )4a}yT4\4 b \~t ,D,J w礵~|~l@D?=z3"gI~jzB{<'|%e! 5I'HzzIr %H|SүgA?9H)HցhrX R Ё 6SZ!/>!1g--y]̞_e&De]-4Wd[z-37 rJ)pBJ:A yBP@AY6*ʠ#P- t݄z 4( ê.]a?8 ^L8 U ߆`1 @ ECiLPv(wT *6Q%*TՉ^>h*6A;}h6zz3]>nBw Fcq011/Jt%O$JJOʆ)_S~X~bŧ?RU UBT6U2*P=zUMWژ:UYWKݕL/w5T4|4DG44445s45jjkkk/Ү~CбIٯө3]YwTOQWDNJ A!lhm`Xnx6141󍫌L&&&&4Sf7KD/ٽsw3kdjr9, --)^[,[,ZYqY=Z/nnFh`3fmc[a;`'odWhwcfg498&99.[]ZtIӉtILwq>,vpaT%&<_X6cmH'sXoݸOX JmIG;"}tO#֟ɐg`adžL̟777gidmfhs-Z[r g{gJܚ"fC}T0o`?~a[f%_ م~2q;lxlqfОe{o;%V%Eť-:,ܭBbGACC _?8}J(h՝?\[\SPxZ:z^T?vb'[L4 NSS/3vgꜭ8G=5mhoNhD=zW_]иP~Qb%Kӗ3/O ^_2ԾWwwt]vo8ݸp[vonc}oֿjk{۾giϥ^+<]ϼ/?΃ч>J48 IS%TUn{F|qc珇CHp ʋQ c^c/W~%x5:O?+9;LJ N+|A>|gϝ_"LZ[wOS,!kv@!(YfYs3p EE"ĩȫ a [ZJgi1EjFFȌ3TG}g 0tE6wZ pHYs   IDATxyW}5;^ْ,d!C$BMF`t $1 //18a2S0cm,۲Yӹgc}t' 6iҹTթSRJ99'JsHs8'd99' 9$qN&9srN69@sI✜MrlG}?J9p|gQ]!OxV؇42;W |=nrh߾}'?x'XP8p#S+x}׼o<'ߟWƾ} ',fcOJJź]w}|ݿ]ϸ@5ݶ~@矺ޞc\sM_;@~}[7w2|lP%A=2@%,P9H7Bw  1^]7v, zQ d$j&HQ < t34P>B X@ŀxݙ>'>Ɯ" u3l2{jgՈ1Roʋ_.Y@lޫ)?q.. Np 2,rVk(TP T_3)6s>fD3R{o%;OP7:]fYcSah6OI۫tYYyL֗O4991J$)H8}&P#x(E(_aAyp҆](=/(0gۀ(F(P94d%ah8y/OɕWO?q,Ǭ<q}kHyk\O fU2 S0ER P L(j JA3 p`ՇH91 p唷P>9QnΙT[> $H3Qyt[ȱ(]z~+n~l~U*s(-)@=Vv]oaAAH=7LSkPJn1D5 uRgR&  "CD5(a@Qџ%`XCA9 DDY}tNSO'Sw8ow_L?Gפ̡i>_WQa:ӛ5i4ghs9[mxPlIu0ӟr#)<1 GF CYcbeP6?kFL㡔\ 6BxfVݴMmNo 0(fiĀ2ki}$)SV/q8{uRO `¥ǿ6Sxcm_1$F)qA JQzۂRH(@, N6L(@o>le}}Jd $̩>Z8B1̏p08ܟ&BMiTE_ӟ=A2 :ҍ G[16rd^Hƻ92U &B #k33b 8l#"@hn2w x){ΩẄ<&.7^p_|W?6 [/zʒ⢃_5No2PE) n VDXCPӾI2ZPFF(N 鰮-"\2T_ٌSn|5n+234mTb*Ԩ2&aI!B3 0b]D]g{~z*@8<yg.ߩ -xVHJEqj4(%vkq~_ |fT 8d$\ LDHLE ()*`:mA˜PU}(UO$De>d۩2A-LbS'%'&'fv jjp? %D~/l=Gj&QlB1e(@oh" 9k$+g~P섗 fzј&6Tȩ"%#ã !ty%E4[W銗 *~?%kwlvn'a{;#óݗ?ٲ:_גm "E6 fletkSywFS!9*T\h4&`jym2y/oL՗KAGĄX&O2viΞ&ZXĝi >GY9&zN>h)W9.w<xG^?~YO_9s.hv۾ (ˆQAe,䆒PQmlb/ṗcs7o[gBrHfa݂B{j6<'\׺ba|å2  _JPZa%R5F*0`H 6Fz FJ 1tG *⹐4 q3Ds!-}wқaw,܉goDh$#9]V}c*yow)nnU.pae\K^/я>,rYz ad*"s0[*;C8Hyݟ]߼6x M¢(! X.ݥoeK`\n(%7qX)ฮGN s 2rbV7Q9)!vrg 1CIfw()j,P \3&b>[T$!>=7pzK++xIS@6 # ɉw߾gaP"@8<.ċ UF%:@<&a k6ɭsv1:% P)x*ZwCn+3G\CM:U_,!}̹zgb87PyiQʬ:%Gdx(@چp4q*rjp:"h/C~%$3 ]EnNY)L' !v8' nl{/_!YG >wQ2@UlbĦ=fa>Ð \HT&MƒNf+|B_V}` }w3)1pSiU&( 3'@)I}׿Ar c2 %8|>m 02=}9}X)'¢V 3.w-{b9681w19t {Ug*)7"8ATjOv)^ťm7ΝۻYҽ?œ%y_{tn8N)[BJ6ZT#'@6#E HKC]_@X̌gq͈n34.B+Sv3&ɂwq`Mђ)D2$+B'r )MZi+Pcz V M)zO^5dvXeXnFHFnbG0EyX\,odG7k9y6u\ .~HfMAJ@AFv-i%66 l̠pyL[W62}Sd&pxjl(P0+Q ??'bzUvMb`m.yf XJխ߳1R%Z,R#%$'DhU2=4-nVY^s7g?k27ݯՇD<"Lf6Æ2b!M q|Q3GفS78.Sǝ=kC<ݥ^!:̞^e 'ӣ<\ǰѪ B3SͨZg(GJNN#sjZbur,ڠ݂SH=ضOx׫OCP,?r@!/yp8d ijtM^-jDͣ lzGͮPL-'*3ATqSL,OrrJhL@juɵI`jP#5-07w6 IUA+&+9D%L7+@؆q[' A~ .; |!QW!Nvq&eڰ{Aj qdEރ*594ϐØBG,2|llS= 51E vd '?ͻ?o?><ˏoz.mˍ)by2x41C[V3=; [x"K fv!xGi7,G-KV4H>DxD#s&P&)0qZzSƌ @=}(rߥԦ` >, DjyiC6ɥC5)F C갊TE)y'/=@ku +o2^YGVg 9P#Ci1gJ$5@Z'TfCOΒnᆵy]7=ʏ<]-v {!!)(4a6E(09Ic=3\oBs(fb8gq #C#UeGyCi'C0i2VP&L>t |!pRnmCĤc% |uS@ y pyt_^{ 0IYdxQ!T811#w:Y͇&śF+2fDpN/قdDp1K hDa'H*9Gnb=Upuⳟ+^wvMŏ/tU 柶3Of*E\qw!{u>vN4z# VrQ#}z R"AW6 ITjj3N1on\ ͽIPiL; J|KV- IDATUN8faz @m5GSbhY`&B616C Tu%A iAndk> {,]"y,jB CrX,1BȘjYi:a{d0Z'оZ?ݻܫ@Y: ~rϞ_垼=MP㬁E5k {ʙ܈!YZPA$"ċ )<4Fadjv5pm`im9o:R@o}XW"2 $–PͿ~ZVhV c@T8;rOb wQ& I +F`eLYu QupӑйM8dX졔޴i1#(375ue6&5F N @f>Xc(HmHĚDM"1$貂B8|{H8B:e5Bb!Xg]$!VA`l3"$M-9>VR~xAܵC?<o!*?T@G,4rp'(c@e Zҷ֘MQY'~ ȇ&6m)O=,ѣugL^E<>R IMa(E55ڛޭDPfURcC:pFܶK o) 9c VY#H1dDEJQRIhGߵ9c\LbMጷ0!l\ ݂!o܉w4튵1[_򑳰fYBvϞPUb.pgON^cSEpd|6 ΀q*+b d@65d.S.NN@0 $%nE-;RoF= %ȄTMc2Y@`U׹ZSj̻J`IH5>"J}]c ? )UP_dIH,Հ!<#[W6_,85&^fcbǼj gV`!h*r6c3P4 (MbI͑,d'Ok4jG|N0k#5/e>}+F 8`zӞLlhe:âC)l|"bxT UemQcǜj ?HBR{1`!)Ǒz]lcJWҥT5&Gm4 25V %jD_[|8\o:m hX*T*$ rM!.Կ +K0[t#\|Cb2 +Pўg(m<AB-0E @hpC,:aJ\ )X}Ѓ@dpH:B<C=E9+^|!0:0)A.`2*Gb"`wV7ѧҔ#/r*s 랩1 ]̺f Sj\)KzLz-abra7ŸYɫ2iJul8$:jQ%9s2#cK3He[^q!wú5m"5ԄV hr:DRT/Q#!49j/[07ZmeABZ+lY:B!S  wJJLBPeFF| 8vn%FYTpt}D>6K,c錏CHW#FKUBl3kAMy"]v` :)FV} "p*x s*HlrڦEJL@OAvʱZ666.2|R$UԨgC I74Mk/OSZc:* =NKh !W塎+!MaB] q=]E=魰! 5 Q*_H q(e1dQrTMyF6)-C TJoX@ьMIAIZ* #?ޖL {eOuH h& Cnm,ٗo(m*LTH~G $/+QNEses6J%HL49y0" .u\uO#T]J)bgD (2 g, AFRTI*ʱN>!)n~mHʡ3 q}pvmqFʍnٳ7D&2a"3(Qleg>ۢM1L1OAJ=e.ҭP Ѱ]:O_s5|~[b [87b"Urshf2E5tjC3A!Iqh`1AI謳.>7ır<˥Y `WbF1͛-) msŸ|GzN B#"&S#褧`%ǀܺDL]vR76L#ۈ6DÌdͣk=n+-5}QEIauL()&\ɥ$sBDdDIu1ƌ`MYmYi‹^!|EU LH"U(Q͡bV1?ԌXtٸ16crz, 8Jn/ӯSF=FC3t݄eёMfESG r–mT~haEJƮѳWH<\CȀ2$=]$c|$sYPͬw7O)sDtjBl3pP\3&I1f^Sr86 64V1}K(*5s6׹9(`:p%1C"l )NR2t>= /a5Rݫ^狈< -+ sL0!K@u/ˑ06G0&å*9G7.|?w|s*UTRUH`\ mJtuxmYKH7 ”Ĥn"r&5}ӧ1Vfu΀0a9t$` ͦ`NNs2wFVD+w&Ǡ`_j7M) Xq8jnk;X:'?|+`.6#x\L@ àػϠJmJ^NUk 5"KL( E>+ѕٺI'HHe @<TB=4'0Gvs:)jdǝv>t2m~ {(jJdhz2qz]J#%ϟG+hyާ{@ک6EI*%8&'@ ,TO.SB4<,"6&  ^҃(%dK:#DglZd]X6Fu5]DQE3aD!Uͺ DĕpP9w@GI: Q8 r07+Pֺs-hBc( c2acSq^̈́ \֧"ӟX ;4 nGQ2#DMSS!l\ys쐦(*R_0B42`'R+> y`Ɔ.TJ] .r:Q`)Q.ci'?b3HX[FcmADŽ^$BTR6.DĸqWgɬ]Go&T0r5^'w\(, K MBWj䒻 +rbcfwe'%ٽiXePzq"E/þ:ц(ym5o`uEr"\ߢCA + IDATJtAav )Yۛn¦J !xfp1<.4#g}4f Dj9 Qۏ#_ VXd۶ _8R|$AAqo{0&%aJ S'G0AХ:12OH`MQs6UB$>{B,Q,bsN\8.%n.x׉뮻w'?rJtn$0ѶjC6ɃK*0[w@"$={0؄\lp*kR4ց,7]pPz]x0x\JY k,.˅!w VWt 9hh0{'^^1] zB1]sB4! `1Q-`#+8JJ1R ^J#3HRS󍐓C,Gg!<0>RSEKL9_` V2\GN,Z6Dd94_'STz5]ꀨ+&QpҡΕvwΑK9) XC,72ん4-1ֺ (q9&|Wit/C>_cǦ1#e>ٵKu%.`׌vX/ Hp C p1xp` kC83}}20C~+B (8pzI" hwaI4׽,{0 SG'gE_l1sa%ֺ!Y'qO7ծmQ|0h_x >+r @[!7wxJ 8dA]þc[Uk_o('F6L(~]$mjL%!K/!iaVvXt ɒm i^mkDq='GL-&Ϥ )uL?f+ BvH)Wd[JYԠOY?AkH\u@(_g4T6(1zJ70,+`W [%|Tt4An}}upRS4!A: +ǰ{?Gx)?ity *PT`EXgESfeI@؅0_O ;a𑆲@{ПI[MM+PԲ[u|s{o1``8 _o) `7493'ȕ Dɮ}Ic:iG1s@1e& EAc0/I,#ȶŢԆ;aL'^&ׁ?>ၷ˓*"> 2֡ZFj2ߓBBeyzrp&Hd8MoERrybx.$JU0&O,i0(BF0wXfH~H߆"&=\tX `/T݁@1(1uo' 󬮫 vG-VqhTBEKLOM!|8{%Pn޾=[,;ۂv[( xyV7l](066=l^ n  05 O ϫg r>%{Pm&`Ǡ"{r~heVI^KfW?/ oFi#-5Cns!y/J r*(ZqdLb'Ev[n0 !֓θޔ`'%<ƈ'p1;YG[%2xٟ'yլ l fEMʉ|& "[1y "m]xtO%A#6D]Uo,NY3SD0*iT*c  VʢP4]FP/Hj?j/=.F ̅!" +uv! UXj0NF]}t/8s D?^_U@|?_$qӥ  v q ˯ڂr'+_s nA p  Pmp@N1–>rɣ)`%f22K Y!y&Q>1=Z$#t( ^*#q;(tuF4_ Bx LMg:'7[&,4!_ɷ+PHbhZ@:"oKdLQGz{ &I#ARdBMM =>Je8a4@ 56sޛlA& rkvL{v!+EwrUĄeCJ 'ᶘ^yeIaFC}5I}0[[й$7O#,[yhi.lr*S' 1T.1yBY}'m{ ughx3(j+]$ 3hXph_/=` p`iev6"ȱ7Cρmn@\7pl ̷Ek#(%'cii8=4\Yp 'JYPCuqzKxAXσHm2{^Ayx_{.7!-cڗבhiF-h^&idz $߮719=J^HJ84ZnO!w3QN:Y:Al [""KaP.u7<"Pې,X{ ,"X kUa 8߇_@ G?vuNᳱ3ZI7QF||TȡMu,3i0|8$\I4xZ<2L]#7c#*ǫ ,l;_{oʯ" :DQ<ŋM}ҟ`pe*D.BlIE}1TD842ؑ}N\Nj&E wy=pc:eŰ7ion^y[cdY?bZM -_?}_&q0['a(*K:ʠ=3<d!SN~j›sxK}9y$qt]Lǐ+-n $m@%l; 0v+ic@qF7F bvz晟1&IAmRlUd9'9װ29 MqbC6BŐkȜkHkJG ;5¶m7H+)S(̩Mԁ̀ӧ!jhFUy !<#f%HHe<ϯ_/l}R](Bjbr%јܹƧNJ[|72YDEJT&VꄃRJed72B #&OH'VW :%4ű[[Rf@ιDR&%/3)Šfg8NL0Xb$U`2w/Bo`"V X҂_},0ZKP;_-p+H ;Ek:Z7J.r p.ï®_qqE a_({#8Yl o+B0hd+`eq0SFc,N@`*:x0b(|fS4M6bjYM;/%eWF%#7P =z3c§k487MSM%l]W/3SZ$g:7&O dWoTmt@͂UK(hۂ8q\._ ڰAuF0C!Ahro$qx)xPjxw؁uD\1^9CO&eDWKK\)Pgtg|iy&|R}_]؂u؜zhKfkBy 6GgD! vdd$ׯ[P%o; "XXvaCWYhJcHx %FbEfV\R@Iv%kRѝ t )d`V5vMcmTq7%1̘򟘞D]ʟ}vYD *e΄Q`a#K3]dMM/i{¯q'9Vt[P=*䟄ӎLӉG$a( ⴆ0|k^dq4": gXYE&cɎѥ56:X{VgE++ ]^@gw vi)e(n ɯu{ A$DXscg9:ik-y.dVWۃs*{ DfoyҨs |c> !?6y涅?"-HE%d A:)f߹=m»1㒯t&>.^gQۘW S_۸w5$g`-THPD =g؜BۅO34ag1֑rnyWe,e`xG7!@ԇ8}*D#tpa"m~m7H-K͉lC W9dEj ]9aL Sw!,%ä>ibwi joUﺆ` /B9l\Tn? hc$4r,edݿي0ksnv;&$.+YxaxÒ׿mq(2̅qD /.OF HOG24)xƤ0Y 6 \Nl#r8p$2*Uog>(|~").=],z(|\?MkLffpt y4%4ԧM(9©;N .e Č8TRݽlhC^'}q5ߡ@{ʋ7#\kHz?6AҌ#) Dv:W4~_ż@I~e%JsL شqpQVA̍%hd,]:2%cX lÓ`+{#GP]$w#D.A0 ≫}]1āl dJܓ+IDAT]5593"2 "`\~q(R *TQ$OGMH .dmXfFf cf" 'ff$HB""2XX(J&D*R`W4߯RYuoM߽4ID p.#\Ī(_:B(y裃F}*G  Y(#EkmڊZsCB=GQ9RconnM?Z;A9\8p0b:%2(DRb2@IV# n4\KQ#LZP 5`TzV`2<̖aц]Y8ex@ß΢WEfQ8(z_w\s.|"i4ja<vW׻IQ1 b}ߵ{ d2E;N zO}]z{|>47ى8 ,;Fn'M9dTbɃ0 腍$RP|lP;ǏU(ҧDqr{(U#UȀ"rX [}{^7&X4'r1#q12lY<@_QLVeFgkM.g:Ϟ n`Y|ON 1\JnҡP#F.t*p2읇p' -L0@ -nBAQ cED;㌟K:{rg5N32N)4LAnzKi"ؑ\8 *ddLJX6yͅa64S_:СC~SoYAf2#X`|UC4 ̊-c%iKD$䡬NK,ۂIճBv"sC}V#]ehRK.Jfve1d#qb޹9(2D 0bH&b̡3fxDl4yۙu~iާCdi>/%`MLv0,a nil65p7PIZx!y}XX`Z?{ ھH4e~p3cfxbO]`0iń3 EqǤ^]RĪfbHleȷj6|ǮeB.(SG<{`fMS<7V$hR'd-^l|p'Kjڜи-mGUA] WLN6xz7;q) y}4 H5RD:alɽ%Y `HLY0KMRF}79 { ߽_X{8JF܃U <;n õNQiIɲ$ԴB`9h4 ~"yhƔp11D#B0"lj$G g2q&:{_lQxE(%=u24qh <. LP`N*0_VzMuG] *bV-@Cn<-@˩D)z*ĄlMfF[(E~ؤv…˸^DpTcGX,7.<[{DZ4^_e #L1c xE,l/M&1]bPēWu{yx O¨kJ˞ҥxò9KWM`#qqqn+ozt{@(?VҢOŐhNu("^&=ax~H'׳8?b !hj%DSMAg.zj9 h!~ ^Jn3H26^D1H6 +ò0)VMtar%b\ w} MQY1`fA}/0),1r-LKc8@%ŽYAf8xL~ׁͯq.O+\'c&>yCL[ +-K8As"7^߃!V&]JRJ7FY˅NtDzխJi1$De,&¹,Bn!Bd%_IΐâE|ΐcLeg(;F|2Nmb"lr: i#|FxֳXIC7 +ǃ43 Y!s>!vkVԲq[A'8FL%*A!s䱨29$G5xra=06 k0X",īVcy<jdp8NZixȗ-z'5Ԓi|;}Pc-?q37t*yl:M&l2a39L~״(<!]XeU:ƷpG & t __9\~. n q@PKضdhQI571Hn 8Gi2Za16d8@^vAU&|;E'Ҹ9V ,XC|Bl!\*nXfτ 1=7X(wZS>X#xoxy=WE[nԥm{>6Is klH0day.\Ƅ=Ms gT& izqPM 3ca:}drI'Z 2w<Ȟ/xr32؅,,ѧט9l/A u$(i\D}$<,bas<'b<,Pe~\ YAYb;$RX3]b<1g9(F8 \) ?|/x^a\h\GF=~\o& c,t 3_3T2'FHO\B=<: 8L,{Sc˜igAo~u:.^ܱ>Am,L@LṮu2HFgX0K{LI\B\u}*QdRƶ&Nv|{bimN݉=?'2ja.8/.27`vxS8faTI,˧3,.Z\!@sFn&pd7Z'PL"Pb#1tmQZr-s7| B2G,!.N L"L8ʀ45CkŚVD1'K[0w+^sx[Sz@ԋ,-QُON2̜ۍ{L1&'bDĀ]TPHX^d ]2Y׃-2;̀.$Kܜ_c^|)IIBUbFGy| %"GHO>0 `H(S%>!C"z0J6 p_?Cp-B\եԑ#wٓz5c^Qe#\VhRLg3 ғƤS#\"nJgI.\@ob"vT< Xeq#T(T J܏KҴiL]04qeH׼qKaEE1k\c*8{9sl֭\J/4ljx fEP#BM5`# j0H/*jS XTje"N23g̜9s^?sfD̗~yyQgI#My>$Dqg__ N_Qxzx(KjmXa#mUbwYZ.nOa-9ɬ8+/,I'%XjOu 3]{pGxSLs"YsjPQsnΟ ^Mc@T 60tjoM?<܀ }" $E0ER$B"E4tZ acPƠ"!JF```r.C /˯Ha/fҳɡcltL;+2fm|㲓XI昆;I{[&4CyB)N39ؽ^)Nru΂AJ"łb.]_:׺^!ec-mw<`qִMtW0W1#!L*88T!7>gKya$zѣ B -`Ϟ{ah%L+ < }luu,o{hefsNi9{*IIƌ)t_ EJ$|%(s=ĩ5X'@gnzB!WA -dd'"OB- =Ʋ$6$K# W99,Ȭ*s%LgU4ߪK19 &0ȠQ/I]0I#12x𲹼oD bu?ai%hy_Ld1\FT#^^0vW2u.Fft9$CYlN{[8O2.( S! g$0[ lAoV;רpߝRaAX9#@IR$!aZq2Q -5q)cK"nIi F00.G1 "h 7*PB'*Sm Q x[1Gܻ|}t"*O4eZJ:9(#Xf0Ʒ /0*x/8POv, bI&Jė zL?e/VqD[z5C Bq_7oZ{4>׋I-sc tFb ETh*s5a>PK7cd2i_wbɢxbi0riD}N!Ŭl9A̓wYqxIy7V`S؄rUAV%ą"F`K5h^+*`Bl}:9^j 1D$H +8Z]aX]ÆjS8= XQ).sZ0/-/2TNօF0*=w 8@6"Rˇec:~ٸlh m{Ʌ0ja8xwWhi {l5b=Nb9w"<ό˰[ 40z1d<1!neBYj DN)1%l 1-+EV[\V+La (" 2ᅛ@!i'0AacuSk>lU "։R!ÿ}_Z:fD!CJn`[qC1s\Xy&98-At&8=[Lg "E*҅@_ch ` y?Lc@0؉  n  d67$3e@,p4⹩Lxx R/0*#]J bմ?(j76h@:MLW?>qnY"!"RFj ,Z0j(̀ 27%)ڬPiBx<^8}÷9</UL;v7@6sӃ.];G0G s/aaVoOLc:>=T :þ=tLHHb@4M= Ah) G1  Jp0oAwEv>j%ӱ4k񛵸q,| yj.d>UJU6WY:53O>Sy\_f
ؓTSK IM[t|#Z1ln]ޯZ3 I @POƪhkУ8Wz{x#!)y5L?DV&3g>mh`m{*d>ff5@WI`]5;V! A'@{N\..ƅG_!7suV$BlW=}i; F)A<.&Xax,38S(b׵*%31l #O-zQvaXOP5o6Zz&⻏^wkI/#&\%x/@{7S މjPh͔&mry>k7=ߪB#@fs_{덱п0-LZ~%Gpˈ{YXf^+_s-T>D@קƸ-9!r[2]3BcnCs?>*ԮeD|%4` :X2 pQSLPRaeyqĘ י5Fit )CdL$o$rpӶb>4+̹F_{Яki+x.+B.{L<۩ =UHcnf<>F ^e||pyv%b:iX'%8Iߔ? rw[vITVQN^dpYI"|#\cz[2M^S0[zIJ/HHȟ- IcV>ܼgy s8ͅH |>4dž?Vˮs/aibb{+Рڽ\Ej ?|+W:rȱK.o}^^ުi7ni{ier:;/Ƃ\.5\ǕP((>6z׳_NHpfϚHii Up'PZ.thkiM5O8ڰaڼy9uVUUQ;v<t:EqL{>PMMͺ@`Etw ܼm {1 R?TLJ>]А>(ض}`n+|҄qB7VǓ6Rٶ}CӴ[BCPm۶&4M ĸRRQJRŬ s ɓ5xv>vml:OùFTwl#..z.\hgto/C>MWW99 ))قmK*+O˲eK(.~zJKK^8֬Y`?ODmmali>]|V `ɒ\Auu]\rsshzzIMKfۛ`0X$XUVC?4DJhkk3o^:XEKK####dffO8`(2p3@|<+ ˍCu&gRƝoid1˚5Wڹ|௉XIwt]wO!iJRʊJY7޺ϒ,`IENDB`features_toolbox.png000066400000000000000000000017531300200146000333510ustar00rootroot00000000000000connectome-workbench-1.2.3+git41-gc4c6c90/src/Resources/GeneralResources/ToolBarPNG  IHDR szzsRGB cHRMz&u0`:pQ< pHYs  YiTXtXML:com.adobe.xmp 1 L'YIDATX V1k@5kPs@\ .t8 nDDKN:Al' ~zI.W\L{޻.!4M)p#ad2I1 ahz{,|LԍY1Ikwg^8_94o:h44M;ȝBОA~BX׷F㓈D(r< / 4ʾ#JE8CRIrlfsJ>fCN]qUU-l6 3,bGϰ@3ҴN[L_$1sq3ϰdYŸsf<`yƑ 7gE()JK$@g~o :h&N$ZIgt$Hӧ4U74R(FCO[ @ p?9ywxD"bx#7o`6V {[;G OroM]Ce}t䈗͕J e4.0fgXp du-`{pA 8.ˠwC CA8 MH2,!{ yBP( CP $P*ʠJh?T.@W>>4Bo0 &jlρ7GËx9  >7n,_("(}5EP( j UA5P][(%hk;:梗נKЕft'z=`17 Y)Ĕcj100w0CX,5ź`T*l v7 ێbq8& qpYB.yM# x~=_?OT7B8GXI(%$DU)уML%#VoI$ɕ4$$*HHWHOd*قK^D7o) EIdQ6S()O(hJ6Jl%Z*fJ Ks˕O(_W~BP1QUᨬQR9ү2JSS WP-QW:BQMTzz:HC i4.m mH fVKU+V;֫6NUwTUQR?.&t6=^J?NK|>mCSc+4: lm g밹:XKrHTHePPIh[-}OtiiLGrغq~qeqs^/oM%&&/_c"E.6]%K.U^Yz"XΩ'Ƹܝ<o;o/'{$%xlKx /JԠԽMǥ7e33N4Q2e9ĖBl$!L(sqfkRHͤ?H=?]q"G5Gӳb妕ù?B[7{5К5k ?.momח!nC[NA~?4*J 7o#Gᏽ6WtضK Ov?U49ysos---wzm=\Z[6-l[vv,qܱ|NNNYEhE.][v}Tީj֮Ta7o=={uO5&5<;{guZŵ_Gs׮/m Gqhku&zS1pLz/=rDIէhc-Yk|kmm~3Ugϖ#+87y>x兔 K;^\pvK!\|˻+g]=}y۹ǩoNumr덶}nzݼpws^~=޽_?~00QcOn{YvvoiӇdeYp݈șр>z!~1O?_:믞cC%'ߔ|{㻎'3O|('槮q'V|}j[ȷGb3U NN!(n@Twt0w5|!v|NDfAU2" I[Fęrrerk-R?t-wcLΕv>ϟzoJl#< pHYs  IDATX OW3c`xy.jee F7TL4ݘ1t?aј.&PY$b$P!y oϝ9Gk/w={ܔ'u_]FFFU80ߌn7{ֺn߾ݢ7PWWW~pϲ&K˲!@@8@͛7Ptiio=V^hGuuu Y' 嗝- =sssׯ_vS"i8qݻ} Et475K$cp {{.,y%>D Lx)G~^+# Q~$_4w2Fp~)Ç]iKYV9|RPP9tP CItww28,ׯ_s fn[CCܹsjc8)..J~U)-=EƍRRRR0#_eH*T.sAgF+H={xGW{d 7rh@O }w|#1N8gЮKW%/7/7g48 }@ɡLERyF]RD`œd3,Ϭz l V*8(񐚥w>:LФ?È| 5-f5p 2G-0KDFHtL|ᭂ b,aƒDUs[3A96{|.xMNNr=SrWSDt9iď ¯(Foo+R&1"rS.*BIuOs3Aty ,rXX2!\[4zV=p~@jn'@ >uz޽peKKKпc|ݿq^i .^i } -Hf0X˳gsIz&Ā8dU]Lr!cf֟?./&^$J|6Lk @EEɓ'fV-2dz0~N" i&1`iJKJ]~[1c2_Z2.-@{DPI3YYRTXD#NpR:qѯ43"d_Y tիW284N@p'O}2ȣ`lg31-6ݻ 8|>qdLpe+FLrTEOik+2VkCX6Nw{Ug"NjY_v}N;މt1i*'Pπ֔>oPiIENDB`connectome-workbench-1.2.3+git41-gc4c6c90/src/Resources/GeneralResources/ToolBar/identify.png000066400000000000000000000045211300200146000316530ustar00rootroot00000000000000PNG  IHDR szz$iCCPICC Profile8UoT>oR? XGůUS[IJ*$:7鶪O{7@Hkk?<kktq݋m6nƶد-mR;`zv x#=\% oYRڱ#&?>ҹЪn_;j;$}*}+(}'}/LtY"$].9⦅%{_a݊]hk5'SN{<_ t jM{-4%TńtY۟R6#v\喊x:'HO3^&0::m,L%3:qVE t]~Iv6Wٯ) |ʸ2]G4(6w‹$"AEv m[D;Vh[}چN|3HS:KtxU'D;77;_"e?Yqxl+ pHYs  IDATX [UgvÌJ00-lR I,|!zB=D !]0#"2$0"肁S >t]![߷g3']^{Z{{3='*38g':޵eҙ '`9̅$CdX9u)]q2noXG^&Fy+ dFD<0!8w8L,iR\BWy],dcF`SӶ (?=VOs&CS 9GOva{FxvU r }jwXmӁv?G_  ^ lJ{)NoSNet͍o{~`a;삿MxDFK.O{UY$g6u0y#y[RG0<,lIY<|h\蟎~>#?Jμ@1BWAEvSL~cGqLx69 d7K_{XJө]@ c'wkWvAQw,+&hw=zJJ]/;Lrr. A) )z*\I2c>,Ioѷ(܍*LnN} ó`ޖlDb&y`ú>z@ 6W`d D\y^5̴ @L#r {yBҶ歯~0cN[}اVROSSJ7DZnүe![?,v-IhWdS,!Э|;~M:~?չmL9@oB-u!:"yI_['j~uE ;qo1M⏑2{)ń:t,nNQzxj#0G{4P < =/JUz?Wo͂s9;w'w8lREulyeΪo '0Z\30X'ce<_Y=0xcG7\%~o!˃!l}*|1giXt[>NxanႃNxbax~P1āfQR,i9Ṙ 39U1 M͎~#Nnmg082cn_jO,U$]lYr9ǹ_c-x0<+}|w)M9zD(1cS_h,Z:i-/pXOErOuԴڈFfVO014J3N9|6\IicTtL6lΗU[:9v~2Nd^QhkK^`ե!Ma*&]£b*b;Ư$%\M6NqKNOox}u%![8G=Z7ZnGW ]hz;NNQSh @J9"jPQx~|}u&d3>i[xڡә={//^ ::l&mhȘ&H'N~0̑ɹO:/c.?Y[ݳZ69rB0#FHВ 2?o/xZWv)M$T ET$ a S ?z9#4=f8v)MOtOr|=>u m1L,kC 7?s˷ pbҨ*j_j74j6y5g?m&׎t[iFo[3/|_ R&j(iQъw'D':'ǦSͲ훫T7ﲀ]YlYXrdUtg i{`JX~#&)#U&Ez;QP") IKi,Q{Q]h)ʎ :;+v]`fdޝ1 2J<]W |M[[$-w4>֭Noϥ^8DBOa7#etX05ʬ\ЧOK5}Y6\PM @t8Tޠv<4h9 r13 AЛz0 d0 DLb^g"^C0#!r$"$~$7H:6# ]N2~H)r-* 6pJs>*ZEj!49(vT6-'m>$jN3tS;w/5X[&I)ifO:K^#VZ98:98*rcWyxf:%+(,D'4''R-%"$a|XQ1eޟ 7)?HPPWe?%&a]Ӣ;Gk ihj==gWMLpECtV >6ea}.b~mvy|J."tz)B,B#"F)DyǏ$j$^qIMIKNicw]FgZ;ECwmJf|+*S h@5lnlôt=&t+=P{E2 }6^=%6S=/ԵfQ?`ȍw'r/%mF{7eOGaNE枪E>kמbл M{9!][|ɿ~v9_oؾ6lWCv|?3+?;w% ߼z'ߛQcÜkoMi?y"`vt^󙗁鯤 o(px;C(px =lրwc:8z9XYIC+K 6%< ϵ K©6},򮭋IDATX VIL$enmpDxb 7ILPa4%0b22Aà aaH06TWWTwW/|K_/Ekk`;%p 'v͊7apyVV g)ʲ#| .,zFOGGGt>Rfl6t?>>xDIdp8|d2# 6ݽ{u+999$$e  A_тDzg2ƾ"Dp?wP&R<}wNc 2SHCo\~ZW_/:iN걸b1744hoozST,ꪩ^;( DX Sp}`$G8nhhc Rͥ0l#c-q W~y-2(QSRYx9sss=cD4v89"'d$wRWWN30mWH{zzެ޷aVVtC_1}soCOHaJ\F0Eg ."EpP xt^vKiq077Sr hv9Y`sآ70N3ن:;T$HGU5@WI`]5;V! A'@{N\..ƅG_!7suV$BlW=}i; F)A<.&Xax,38S(b׵*%31l #O-zQvaXOP5o6Zz&⻏^wkI/#&\%x/@{7S މjPh͔&mry>k7=ߪB#@fs_{덱п0-LZ~%Gpˈ{YXf^+_s-T>D@קƸ-9!r[2]3BcnCs?>*ԮeD|%4` :X2 pQSLPRaeyqĘ י5Fit )CdL$o$rpӶb>4+̹F_{Яki+x.+B.{L<۩ =UHcnf<>F ^e||pyv%b:iX'%8Iߔ? rw[vITVQN^dpYI"|#\cz[2M^S0[zIJ/HHȟ- Icv|8bksϷ P,Wkk4{xOv_.+A)if}3S3/G"CbJ$Jt:,676i$eFqq|rȑ_ɯqbbf+}"/  JbB{zfB{axtnnTT8ZI?kki'ϛJׯx+}oɷ`XZ\D.v;>AۡjQ,A)q( XY ;% ˖mrgPm2j(> OGGkKwb؏VVB[[0eÁNfERiejST*jق$\֟KBuՉ BAa ?zuptϜ&"V "R<$l6L&B^K%[5e,kСC;###}L!D+At%8466B@&E*F^ʃSP $Q(u*Q,  b}}ͦGXw_cmIugϞ- !"IZTThqæGA!2=de:Jb w&'?.\PܼywDQj٩J"|>!\V:pHRtJ)j%xRJe@X)llûP(Tv8`9[\Kdlfs f )d{ըChP(H$F&&n!He^Ŵn)-2 ÕJ%Dۘ@0Fww7[ d\N*a2iJ%rIdYdYE$A, R J)aY>vY_oUS)2<<(J$qDۈvL&Ԩl eHw ss! CtD"NOJ]e¿.۱e'), C__RSa0 0!~?`7ZI:Nm^e@ F<;NloGN01qx P(XDb1\8~%h<$~ 559 F\SW)f:"@BOO/ m띇Ϸ`0A0;"Ess<$Iz@455C| ܹbQjɓ'qQb1:99IF/~Z֙3ǝ?Ꮵ|/~N?tz, YWBQ[[ 6& x~1gfޅԩWp!bZQUUJ)pk|uZ`Yp8X PK-?o@̒9w\ ե!nn&c,5띧K]4(XxGu 6 vK=A@PFGGi*}i٩}nk}7IlmD Hvw[qƥ0{p…+(|!{EܨtWyV_ιs-.t ) s~d\VNNMH.2t0,A&>>xOs` ] *S 4>s@@z&@ڌ;C nD nXNV53 u9@' uV#MJ@?XJjxz7 fDF/9u&g򇚞"Β8xdI?Q_2`9R{OWǏlա|3k.ų0F8GFG2JM!/yo($GYm9H~l_ٯ/f=̂9Q@aZlsߥ M\k01ib1vr/S[D>fPbG Q &[ڽv=jlZ[-n%+$Fŀ؁{^[ SNLq!r[%~ksWsʂ=Oߌɩitnvk[`֚Ŷ]oA[Kps[7Mk˺'WFrmoA^YZRBiT̼*l]V_m4>]i4\p[Eiݎ cHRMms{n3'IIDATxڔW}Lg~/X]X eQZ15wzQ.]4,9v^FjKb. 3zgzXZ k6Y^._ "[a?7@d>0c|nhhPV9\yy9DVQx<` ***89}Q284Ax!A!nb5uuu2i(ؾ};RRRz%RWWGE׮D2 N^_#99RUUJpqԩLxd?r1U2Jx^MZZgΜuQSctt{QJw^%'')-8QnE1D"<^xtLreرwh4k׮U\.W PJ #d}>߂HHH`Vs\EI~6??=z4f Eq|Hpwwqq1Եezdgg[GyJBf {<K/.]DbI|%&&UYNNnGrqrrk>2$IlhVTTp>/$~g 333(Yjj*=11V(p8Fٳ_uy^x&ێiffFvOV}f͚ .ݺuQPP:p;w ~>wA]< "’$ӬZJn:5qwi:%%-[1׻룏>ʶf͚͵W"7o%K?Q #lOV1:uݽ{71by>!!l{sA@ooĉR? "d|I)^/tV|8PCˁ?%(yTAJ΢ZW\zc>/@ؤsFu.Vdwls]TD N J\¾VWW_hܸ9ؿbH555UUU hii >bt>-CKMMMF]GGqTIf H(cF4<ӴeFQbW?44dXS$lQH) 2{8Šuݔo )3 [R*RtVTdpбmTb_M@`8+.pXxep;@^an!%l<+gYő~:̅`b+6*++0}]]]^TBkk<ʻE cyo?JA+˹zA2::z]6z,ҝC56*-U@ii:}R#2j%˩|3Ŗ,N,LFlo?a:9yxPNBw?sGpos`Q9>gUʰ9Clun/)D#گ٬0;SZzIENDB`connectome-workbench-1.2.3+git41-gc4c6c90/src/Resources/GeneralResources/ToolBar/view-dorsal.png000066400000000000000000000016111300200146000322710ustar00rootroot00000000000000PNG  IHDR szztEXtSoftwareAdobe ImageReadyqe<+IDATxWSZAT"_ q2`!%N;9TQLf6f&) @!4Un{k3;pox]5:Zg=cm[fXjvN[]M17l6O'X,=???Yёi6qzZ֝sDfݏAc\i2|̌ ߀l...BssCH$!l|||L}FAW{CN/ niaay8kfḁQ\.G`IZDdJG@ @NjC~Uˡ'{i~\Q2mllP&UvSN*R/b{!<#G'''Ol@|KVVVmx>%CEfS?%b c%NSTRϸɐ߬P~zxcqqdC=w(96geCs62驰,')]|=ߎDlMw~rO)Ӱ+]yIENDB`view-left-lateral.png000066400000000000000000000014651300200146000333110ustar00rootroot00000000000000connectome-workbench-1.2.3+git41-gc4c6c90/src/Resources/GeneralResources/ToolBarPNG  IHDR szztEXtSoftwareAdobe ImageReadyqe<IDATxW=Q+NHb $Ҙ͝Bu Oa4R@ g]l,{ Z$|opm׶ ^t _U6"t@xPՄdr \NZL|U=LEj2G Z0 bTzwej<֓Fz~xΕJl6ۅEjU|b`~aN5BphŢ榸!Z^%'0Q TTj!.-&9Rt<!N\.@  ϯ% h=ǺəÉ &ZðR2"gt`݌r8m4u`báT6i,d2 R8d2`R[(`4itl6ρee֌cb5 n`,[πFȖةaDd5f]ݖQ) OI6 =imxv^V >¯Z8Ӊ r)@X,pvOfvKr+C{t~gٲm:xBl6a@뚊)XFڢTF쓧Qxf޾:qz 8vsx D}v_>:<[W]:"*L.3;,Ox͛aFgIENDB`view-left-medial.png000066400000000000000000000021771300200146000331210ustar00rootroot00000000000000connectome-workbench-1.2.3+git41-gc4c6c90/src/Resources/GeneralResources/ToolBarPNG  IHDR szztEXtSoftwareAdobe ImageReadyqe<!IDATxWKHTQ}`(ꌍ64ڨ3.zC*aP qHE4JPe$V#JQ2F(8`%w8zgIMïs ю;L3u0 {v7 uL2dj@ff&PVVbӻ[{i ~wݛΙ?! wtthRLNNV&uK||ЧQS֬PoOpq#PՄdr \NZL|U=LEj2G Z0 bTzwej<֓Fz~xΕJl6ۅEjU|b`~aN5BphŢ榸!Z^%'0Q TTj!.-&9Rt<!N\.@  ϯ% h=ǺəÉ &ZðR2"gt`݌r8m4u`báT6i,d2 R8d2`R[(`4itl6ρee֌cb5 n`,[πFȖةaDd5f]ݖQ) OI6 =imxv^V >¯Z8Ӊ r)@X,pvOfvKr+C{t~gٲm:xBl6a@뚊)XFڢTF쓧Qxf޾:qz 8vsx D}v_>:<[W]:"*L.3;,Ox͛aFgIENDB`view-plane-axial.png000066400000000000000000000020671300200146000331270ustar00rootroot00000000000000connectome-workbench-1.2.3+git41-gc4c6c90/src/Resources/GeneralResources/ToolBarPNG  IHDR szztEXtSoftwareAdobe ImageReadyqe<IDATxWOQDZlKh EbH%xSo/DQED/ cb!R%JG( Ej ԙǾ,mqv;o~3flQ[Q*ؘ *v) -:HlsfUqױN:@|%^&)U[\8e@}G@p 60t^ ]fZ`0{)+ v>u݋rr ¯ZJH2P-*.bGL=VVV@K <@ju NĐUS(QǤ6 Rb>ٻo?IORa䅚TVVo%!1aXcjN'n\kWVdA@'$"`YN2=F 3'OHtw9G<dlhIHv9tdv,(Tv`aGvl,'Ԅ&Q?e$>~ʩ9F R.l=m8yx0 [1Å8sT⻢ED]OөGqͅ(^%B\ [5oR - P}>}G˗.PiXTusLY/'TTmm{X4ZxdljT?}ͨT#_ Eц#Qf/'Nv: \C Z\!Js`.{l -@-ۜr|4 . ۍM@tdianM&C7S>L1[ K97H݂!sm(4~CNdSܟGXPTS{JT<8 C]A0wyP6,(GR6&mˣGcmi۷n,2Tk T; *}U.!;ikϜqV y5US#uwvcvv6 W*tEMh}p%ܫ& f\ߖhB 5F!X>:ZǬ0QWi HnӬ^FRTؿM?^f(.IENDB`view-plane-parasagittal.png000066400000000000000000000017171300200146000345060ustar00rootroot00000000000000connectome-workbench-1.2.3+git41-gc4c6c90/src/Resources/GeneralResources/ToolBarPNG  IHDR szztEXtSoftwareAdobe ImageReadyqe<qIDATxWMLQb9-) jPc!<8zØQ &&$ћ?pBL["c1 8@-v ݒA'ݷoͼYߛVj/07L@ \`0P%-ha~1%=([>oSd4~_&'l*@e?S3 I;NFHp+MQ;-Z`T8Gfmuq&hV.cF#]\MqV+]yFh}}=n'r^J eR\TDmhv6䐍jnj iMH$f>40;;;أwzz~OryG8zIܧ7oͷh,@ܑqܕqg_f%Wy.bh"4UQRg)شPzkw33{Ñf!eTFz'$"kHΘ{e8|=}Qi*Qay&^Ũ#2 eVBxma^:Ea ± ,"Dcjx mwܜ݈kݤe~gJ@0 VVy)x^7хXFΒhJ>9v"@ J, `,D:&4o5Za|xhP,TP!O~w6ٞ0VVUU.Ў@<@qbΦ3g#v+~:VIago%9##IENDB`connectome-workbench-1.2.3+git41-gc4c6c90/src/Resources/GeneralResources/ToolBar/view-posterior.png000066400000000000000000000017511300200146000330400ustar00rootroot00000000000000PNG  IHDR szztEXtSoftwareAdobe ImageReadyqe<IDATxWMLSA $pm" -xi8' $⑟$z7=a5A)xQB@jBƀX$^(M!Iń $ufݭ{1q췳/KNvVD#yzue 9A f9nؓԮb"'6NH$}:bpL)+ sccc"# r4Ҿ^/ vvve]=Ĺ X,ZvQ; pZ1R]] )3+++SR#"N3jjjX28p@ `Sѡi/P "G>BI#455LzzzLtV RKfى!K(`xxAHY ST{јxji xO5"]˂@j3i2pa߻S!: iv$t`1,-  jooD~?q{T烁qD1kM߶6YD馋J1-/`;fVV=jDž.Ĩr7̷cFl`z-/.`#ќu'M򭲎gi6$;~/ j dvX>T~>B쨭6'-p4IB?t=`@\Qs;d7gl_.T:} UAnY|N b0zz9,Ԟ9X^`#ߏʇl\+.&)^b:v7"fץ߀f\N0o#X&IENDB`view-right-lateral.png000066400000000000000000000015051300200146000334670ustar00rootroot00000000000000connectome-workbench-1.2.3+git41-gc4c6c90/src/Resources/GeneralResources/ToolBarPNG  IHDR szztEXtSoftwareAdobe ImageReadyqe<IDATxW=oQ>E&>BW?RtX:@:TƄ8 1&aĄ`TӲXӏ. 81/"`bzx{sss9s;3"CG[.67 ,xt:p"efTT0Tgxb5]zc3$O2[ jA~D~~iH@e A% M=B] $Ǵw/ ~ ŴbeHPA$^ܺ J~iHb(\h4J,3pq):َLjq  )w~ 0NIENDB`view-right-medial.png000066400000000000000000000021741300200146000333010ustar00rootroot00000000000000connectome-workbench-1.2.3+git41-gc4c6c90/src/Resources/GeneralResources/ToolBarPNG  IHDR szztEXtSoftwareAdobe ImageReadyqe<IDATxWMLA~XVkڂh#JIĠ^ENM/EPiAPFT/&$1PZEHZ~#yL]+'K>/%~x" ٺ5%ĩ2>??JJJ@^?u^- v=2<< B1E113x(rWk)V+!74Hjj*joI,~0`2`bbN&s~/󣪪 q`H+ѨkjjYVsgzH]2 l tww+2D+FFF4TH7zV10zhiiQU@<˦>UVV*aسmuRf6<ѡb{ <^𽞆ٹB  qX?h?Z76ՒV+My45bbM#jkk̜]ٮ$hVV^>Q*=yΣ`rzZV'|P*mmm5~U9EDX)'Od.-c z<kHD~WFLn'aBiRj IENDB`connectome-workbench-1.2.3+git41-gc4c6c90/src/Resources/GeneralResources/ToolBar/view-right.png000066400000000000000000000016341300200146000321270ustar00rootroot00000000000000PNG  IHDR szztEXtSoftwareAdobe ImageReadyqe<>IDATxW=OSQ~iiMY ZC;bbˀFBuI['M P#M 99{{{m] 'yq>8f]e(Ign(M Ši.1g!qQ ivQP uA@&HW"[hivlK3 *Px7f'9;{w~D:ҒbJLL;ɚ?66V1X=~]<߮rlR5̗hL\)SoX=?2ւdX" T4XSC[̅ꕂHjÀ B??^OW @7n@+A@X`<5xb:mo5假D"߆-?߼^:hI$:r0!dQ,J?f4ԣ >5Np5-'''(Ei/=]_` ߠcK ^{92 CG2Izn rw&91:nbmbzxW=Ӱ5_#*./ ýU^Y Ѱt%|[Kii)9965 ^[?V3"UUUR],rzFMϸnLS -80xsǿ{>+Gnc}]O0*u\xdԫ(8 Wh4!aIENDB`volume-crosshair-pointer.png000066400000000000000000000040141300200146000347360ustar00rootroot00000000000000connectome-workbench-1.2.3+git41-gc4c6c90/src/Resources/GeneralResources/ToolBarPNG  IHDR szz$iCCPICC Profile8UoT>oR? XGůUS[IJ*$:7鶪O{7@Hkk?<kktq݋m6nƶد-mR;`zv x#=\% oYRڱ#&?>ҹЪn_;j;$}*}+(}'}/LtY"$].9⦅%{_a݊]hk5'SN{<_ t jM{-4%TńtY۟R6#v\喊x:'HO3^&0::m,L%3:qVE t]~Iv6Wٯ) |ʸ2]G4(6w‹$"AEv m[D;Vh[}چN|3HS:KtxU'D;77;_"e?Yqxl+ pHYs  IDATX ;KA/FMbRXV n'vZkg)X@,AML&,Fͬamd̜9Gi uj+gMNNJgű*|zzBII PTTʆWV pww'/¾k N?  cFFF VB @yy9?cq R9!?+V\888FD0<>.y?44:|h @+++ ׇd* x`+{OiUUU0m%jsss֖8/a8ߗ 5&z%Szzz^ÿ4/S! #iN˱1~pxR~ @#I x~~{{{əV7d[e@ tii XYYy5 dK6dC 3][[Ύ 4[fTB&#OV 1PbzIENDB`connectome-workbench-1.2.3+git41-gc4c6c90/src/Resources/GeneralResources/general_resources.qrc000066400000000000000000000050571300200146000322130ustar00rootroot00000000000000 ./About/hcp-logo.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 ./SpecFileDialog/delete_icon.png ./SpecFileDialog/load_icon.png ./SpecFileDialog/options_icon.png ./SpecFileDialog/reload_icon.png ./Splash/hcp.png ./Splash/startup_image.png ./Splash/twitter.png ./ToolBar/clapboard.png ./ToolBar/features_toolbox.png ./ToolBar/help.png ./ToolBar/identify.png ./ToolBar/info.png ./ToolBar/overlay_toolbox.png ./ToolBar/toolbar.png ./ToolBar/view-anterior.png ./ToolBar/view-dorsal.png ./ToolBar/view-left-lateral.png ./ToolBar/view-left-medial.png ./ToolBar/view-left.png ./ToolBar/view-plane-axial.png ./ToolBar/view-plane-coronal.png ./ToolBar/view-plane-parasagittal.png ./ToolBar/view-posterior.png ./ToolBar/view-right-lateral.png ./ToolBar/view-right-medial.png ./ToolBar/view-right.png ./ToolBar/view-ventral.png ./ToolBar/volume-crosshair-pointer.png connectome-workbench-1.2.3+git41-gc4c6c90/src/Resources/GuiResources/000077500000000000000000000000001300200146000251355ustar00rootroot00000000000000connectome-workbench-1.2.3+git41-gc4c6c90/src/Resources/GuiResources/HelpFiles/000077500000000000000000000000001300200146000270105ustar00rootroot00000000000000connectome-workbench-1.2.3+git41-gc4c6c90/src/Resources/GuiResources/HelpFiles/Annotations/000077500000000000000000000000001300200146000313055ustar00rootroot00000000000000Annotations.fld/000077500000000000000000000000001300200146000342675ustar00rootroot00000000000000connectome-workbench-1.2.3+git41-gc4c6c90/src/Resources/GuiResources/HelpFiles/Annotationsimage001.png000066400000000000000000000706571300200146000363170ustar00rootroot00000000000000connectome-workbench-1.2.3+git41-gc4c6c90/src/Resources/GuiResources/HelpFiles/Annotations/Annotations.fldPNG  IHDRLmFiCCPICC Profile8UoT?o\?US[IB*unS6mUo xB ISA$=t@hpS]Ƹ9w>5@WI`]5;V! A'@{N\..ƅG_!7suV$BlW=}i; F)A<.&Xax,38S(b׵*%31l #O-zQvaXOP5o6Zz&⻏^wkI/#&\%x/@{7S މjPh͔&mry>k7=ߪB#@fs_{덱п0-LZ~%Gpˈ{YXf^+_s-T>D@קƸ-9!r[2]3BcnCs?>*ԮeD|%4` :X2 pQSLPRaeyqĘ י5Fit )CdL$o$rpӶb>4+̹F_{Яki+x.+B.{L<۩ =UHcnf<>F ^e||pyv%b:iX'%8Iߔ? rw[vITVQN^dpYI"|#\cz[2M^S0[zIJ/HHȟ- Ic<;~Dޥr/IOz1y y}?N>O3=<yB..[Z˿;G}Gݐ0H .;*|G|}~w.X]q,A؏û3$:..ș??3Ux' =yϙht Lx노  (Yûu)z2\ЅY|;}coznzQMy@{ K`E]L pq>3>_g Ox™ Te `[7| /zы گ~qr@u8aav^C w>s?Akh7Rk@$%2r.9չr.Lw ņogz<'`O|Crq?#?r)%c}oS?S'<+x r/UD%/yᵯ}Dm\&[Uzx_}=>S><ܝk(!CWcr/{ӞːDEBtZʼnǩ.4灀(9rq\&~9s 3!֣6&^댔DrFs:)4f4]AL8IA)?ti?>ioN%K'/K8L 6_rp_fop6&h!koJ.3 -yƨg"PGOwlXt&GA"D+S@v$H$o{)2_&X >x6̀ƥS$aI}wbjhJui MNKK yAM]:fo| 4}0f&u4_z}rG]wQ\ҹY)]'9]r9??_oSlWW;qH6t݆ͅM6m`ئIL6sBdՏ'@ J%tcxK$>:Q):x?9y[r?O_FŲX?˾x+>6IÍE7]Rl fR@uMܦ&Ӯ$#ئ@\V7NNvYi㳹ga{逵6.3e⫏y"qQ$Npys#乄r]x(ޛӄd36J]M6t ꏎ&RMn΄и6rي܀l|ݏƯ 5^;x3aewe/'}? ~~?W|W쟂> gQ8\6(놴ylfq*s׸ɋFmp|6\"Æ.ٍ*C7dKl>ڟHĠ}B7[փ^ɔwY?\&׼5ѯ؆/qt c Rn,|B|ڠMl!Ձ!N cd#ͩN}JDrv&9_'K*4(;m|envAS{*O}!M87 j97}'4o'ۓvG]6d/IRp%!_+?7>DMWzrK=@ 8% Rͤ_2lW 1 6107UmظUhm" w:M?>Գ5ۓtѧ̱dڼH"7,)ws/A}N.m!neKxpL >Q$籞ُ$xKvM؏'Ѹ6l8۳-tm8$r!OG~eڙaYH_a}-el(؏n//\&]ܧc=yn06ڬG+P.1J hT-{ D :n +̾7%~v tkJ6%'5`J*dd:1.G):q</ Xԕ^mmBr8"@@۔c~^},N:$OjJOg6-xK<K#cͼ< $K~`WĽ,G~Wza@=\JA|=<X}< j*m-6Ou^/ NXaؤEohl4<90ƬtEoo|E_Ds93贉lr&vucI a+ed'ZA2)|eSp=2v_ś?0H ?/>e 6 BAS;^QЭ8)OO\׷'SeǏ$`~>H쳅-g[Sxh'+W `1^= n)e,D O:\!+z|ꍭ=ԏAW6)mU wmړѲoWv?Ӯ& g2ɋNW;, tCVqY#m2+IC7xKK~`)8JL F1`*%pcklIN6;Sx5I8|\ 6]Ϟl6 tPhN<}ҎK&&$F_6N>3ޣo?̒C@|1Jp yXxK L_Ҧ8 8PfX!0a}'>&KzWMѥME㦾/m*I)O0 2&IW|ddzs[Qsv0ߧ<){쾙"xcqTFի^{5'l!$ AU~" hMD5wlO 'p3Nx7yvW JCRB=L:iomX?<1ߗdM~4TCu"9GXc]n|tQ-=lޒ'Q^Kf`ƒ!S<'E ;}W}e(w}ВιO2E] =iOOEYߏ3#XK)`bu|n7Fl1e ͻ~/1ݗ8t+19/h`/i6ҕS&$>حY<(Xp4$&̻ Xvq,ŕKW$r B|~ Y@ \pD͑++˟N~F-@oF4uyNV]T=~z:Ս)L99&Wo'W[|&37qNS$ݒ`IDD #Y85ܐ<3tA` >l>SA'`9fӗD46O4E+:q :,@#!t?'Nosy ~fi3Í#e^MGz@_ғuXSlW=A%Bey)2z|dP`HJ"m6F`ϺI6y AK>nVY$XMtcxb-ZXXZkd`CV:2̻b\IEBL`>1mYAKBӧDe%yN։O,<՛[Sz [02Vr5ex{s=N鼃w{ (6L-h`'Ԃ\^c f3xЦi<9xౠGDVyaQ{kd%Ans7N !*ë i)lSHɁCvo{_C'++o^IN `ڤ. W5oNl,J'g pmp;h&^@Mc:yF Xd|k3{#77/xEӚ{:@őPd &~qH$%+ai7NP6 *dHTMN[+tymm9|w}׃HSuD2(_ѫO9i&o^!օ>#އʕ+MS\m`XN_>1L:Z >a=yk^3~*a;g;LKvn:qMܟst:~ŋc\^HT?[q3\z\1ڒ.Yκ%_%MbC`E ;R:qQG EtqVv ^W?f]Sм6w8>}%$>CO?)mNYN$߳X~_am $aok&K9QՇOy{&Nw%C(i![x-c3`F^ZAƃ.T"e~+%>)I%t5:v'*X뾧=y _xil>쫟 ͱv ' 㬫.iJ"x69lw210~C) m &(>GiYb痿'(?zOb}@ h bzts^iaY6 |E}d;i3nDFsh#Iz{[}i ~x nlz|Vۓ-OO~>זXuՆ%֞$wc\/}KOy&hJ}Dnnb r M':m%ɇ˿6C?C rnhCBc 뒜g>lI){YW,Oa>U|H.2wY/g4{E7.%{ss]< mF<$'>l)A$% Hs/XAA ݵT۸0^f98ƴl2y kcγ׽n?O{N'f{Mlΰ6d _`ؗ0u .ys9]qӊ" C6$~gh>]΍S92 h5q'/Y>69+IȜͽc[PMF .93OpAnإ D3oԁ1 "Y3clOvci<;ߧ1So ٴ"Q:bkd\\$\n)!TPTrDJZtYk d\<$$ş<~56̷54֜ h&Y྇}`hlvl%yӸZd֣Mܢ.P8iΦ+O^a7YAv2`P6_coç60_HNnёtA/}p6Xyh62Fu3O=]u5f:L9J"SgT%Ons&ʟ}s$BE/HzSz]$b!-(g ??9F]XȸMg|vw>OnL`h Ec 31c:YA"u`S2#[-h+4|ͫ͸l\/{iSO$lw ܏ı:K7\pT%Fx.4=2o l1ȾitDKmO䯘dĔ,<8<:Ѝo^pN?Ktl$YWalNYf9ۜM6ZEF x[3=) ]+ua&9Nؼ<,qpb^i+ޱ?h76Z4SƓQA;Nߔ7ۂ=|_KMkqO͞L]:6OܜS߱jWī60 'g#{%˽5y|-0?zI@ -g[}cW}h+$Oa6l|>ї<}w!O}+&S"d_p3Fmb@:d)(G01p0هgLJ&W1d' }|~ޣr'&S9E956to Z[rLmV}u3 WWI6ol D̿ ^es'&6~m0i; b<=`(I yYis]x&4VȚ|;ӵ?)~5mXl—m黦bM6z=&hbv!n 4Qp`Άe~a̵!(-OcŞ^}dg+ &=,]@NZЦx騝.%G,Jɖ)S p4=?a}cg#zlΎh_|[uhGk>hQB$/<(px?SSHI-tt2V2+)y^KvSڔxŴ>ui~2A||'I["q`Q봢2 ArvmW@֟ uZ՞kMz@N8/՞z\xx[k"xE{xhW=&cʥO1͊/0+zI$;amg:9xOkGp4EI-JN%w罏q9"( YOo6Hؘ%I/: S/`wflI) 6"'W%ȯNdVʟm.>|F8tF굍v'Wr}.ۚ@@8ZNΩyȴNnvP`r7xoėw!G10uO>&K䡸d_(W=KB$f րk>zS1Zdɿx9/oP鋖zzk>| CoJW\ٿ{t|&~DI8:}H%w+8]0iÃ>K/yG ''߸Q/TmdcꟲNh7&߬Z tz' :yO}'o1 CpvLG}ǟdY=ܺ#L^+֍[ O]Zh+Y~w? ~;_.m DZ9 7N>@rӓXwO :HX-4S֬' 3H^ ;^|4|$0Gm̤%=7z6n}ɚdQǟՆ{''ֵ+$_t[덙Ƒ3 zufN$O|ԉܽoܖO'p'N҅c@fQ1q@8ctc]g^&o4>y7& 4sP6*5vnOl=40}J2S/г;WiLDˆƭJUsiX)=dʘuzHhְ+t/DyX%o<g)ևy?Ua9kl I1lh \}Vz֏+Nu+1@TgI{[Sܸ06ldw3'<T?]Uї];l_ SƑ?+pB"cS.|x[N''+Wܳ8pI_{.Nu2õʵ zUds<~xڅ<#āErM$H sJ~0z}oxUez>є |@>зɏtPCK%0y L$%Ýxv4Aǵ!;Gmxgwscϱ'[_p}7!ѥɉG]aotBG+h֩NuC>+W9\{9{%Gchk+-f[2"߭Dk}zU-kpO2 ]h@X>x9_y V [7f͛Г׿7e'FO qXѽ#[(hL̾+h/iLbS͠ ң>Pxd.Iַ}{3>!ƧK`vCnTȜ:M4¶6dϱ\Kf1}!+])?\i)aLsUO|؏IFbvLT'wO*e ^8UL:q1Z_ qٗ+<] %W]X|ßFޑĂ_O%.w`'Y$!7JFCN:%lQȝɄ>D~&zTE+NoX ''x67q<#,:9቎2ă$?>@{L93s !8፛8֍?wJUp-vSZ6o>U 7e06{*G"5^$LB_3Q2鲦 Qg)p]ƃmpn j dSԃxcm3= qOrӅ~BA@)6ςM\e#[̂Ϲ>NN=pҕ+WS֧ DP9&vӞc)c[XW2.x̉RWң-HëhWOD Ľ/I(mfKꊱ ԛ۲ǩN4_ (.d&'N֏NtjzavV?ZHOdhl&@lmAђ_VWְ=a]Ȳ6Jݘ"A5^qZ)Ǡ8w1x*-V2ۨ% < S`uK :^gvJDJԕ|Mn:`@vlSvqo:HdJ"~K~As _7~7|`4Y֗Lxͦh_~k?X]eq4O!k׿N?I$%5~BCs3VO@%[OzŖ ,sk}Ǵyη~cՏw+OëĈ 9<uqy@L?vܱmuS[[5kƑ3 zÍW 7qcaFWN!6[dr.8qc?J &ۢ2;-Joq[H~O}dMiWLH_xlTF zh@$;2|ks*I$9ӆ9aΕ?}ox);%d40e֧-޵/+&oV~m4&<o`[W|%~7 9&OO-ҴLY8P{ ^iܔʏZx8hlskS;Yd *70t'1ez qSoiģ^??| O Gzc7z~'kGv2= ':!In_ `v^cѤUǫ4Nݏ]?c??q3]L2]gi8[g=cXǺ@rգ YUA fL;O|n DP[o&&ds6|-б>Sѣ+{zA_Gd@J"alVdG̒HN#NlhNXC57/zыdWr2='x+[6v,s7e۟9V_2?טIK~ȯ3鴞J47<)Mz::4b90ݫ=i9 [IN љӭ3^ WܠtLO[Y>m!&MBEwmƔWEӆA~>I$tk31e{kMoz~9RLU LF'Mu/Kh]8k,Czgi9sCM_%>c=m;Os͞U/ɘ [6E9o4c pj `hUO3;eE)ߧ@_1v2ٺI>%7o?7{*d$g(XǫήF+l/g~g~Gt?y7(ه/I1[Ꮗ= 0zNevS6^ѮsϞtg1ISOϬrl}^7aIN}9X?J_ۧeuԗɌGolvf~ .`x TymOĭAY:i/8-o_:sӦ9%o՛:X /yKysD œ'Ya8gTrQfҘ>xt)GFOod{c7q<ᩧ:]5>b^"kp6\N^rJ*x'-`-o~3oIN~k \ cS7pcR2Q''O|$/ykػ?4ć5Oe苾h#?#7o'# čV WؑOɩMw:+2ȣ#?-i1kç _?\??EQN1ӳk36o}[6Fݳ~t Ck={O޵Ϝx=Wh>$:-ټar+|E ~@._ѕ 9ɼ&xMFp19g6v3:odӍ-p 1j⏞#я25J VXZdP)D`Gexm6MUUzYzB& xzSN4A fp \AKo6N P`qC36\p*KKӈM}_]&'?9sWkkO6p/s cYC;$` :?%E)!h~}Gd,y["=汖삧oW0vL7 [ :o8NDrڟsT֏~ Vrƭ)WmbAnq P| ^*ЧtКHԧL"mD4O\n892@holK7] N%._b͟o% 63}n#\ LID*]Js糒nj_E; L<%'Y~up ɠ'OE`6fd%̾)Z,r$bѓX)4W!fSl%78m׾8/ݏdg\`~Coğavv "=oL<% QX-I ez<ƲE=WDɦYjͽ> FSo^t7Ϲ$&N\Zl E'~Sea1y+l7fn6i4m n;x"рOQIĦƯ@g=a؀G!đpqeǬ?4/SQF4 Yxl +/[2%V'2> l~Α񍱒BdKv(vM/hO*\n;bl`b&cr>O[IֵDW^jI^xKOx?'?I&hRB@Iטpr7@£ \Ѷ)|mCK|=!?͕t g]uӋ<5|Qr-IdH)D%W iz%Ɵ.qu+\IyUp5y8Po!sjsR;w$pȝ5Kv}Do}ws?sv;9ƳLcaͅOf1wH"%Άpv㉯zߴuEA8RZkH_(-xv}y6嘋?~mίë)Lf}<&FW|)XӝLx9ۜ&VŸS賆Fl?qZVf=p6</LÓe4/x0!+,QmӠK&>y%h|d6yX=n߸ǞNV3s5g,|gSsv2Dfl%_iJԝZnɫM n܂pέ_ QolNN-eDOV=]O%k_yq$ 3nIx$eeKzҞ\f's:y*uoӐܴɉ⵾3-=K|dF_ӟ3ND׍N!8UW7ީ[L(®Sp~?ihR>1>7N mbuU{/I[ M<65$" |KF2ë>c$0 &lS \vT >i6\Yئ5WISw/ļ̒3}G)ۦ| ޒOg0a %}9p~0&"a9 7LNFcÓzO0ѦcW G^mp/5*+W'4) ]x?0A6ѡdleW=}s7~r7}x~cYnm\tѮpE6I6:N>UvE%n#\3ynQxrruX)Xg=Sؤ.~cx'N]W\_ç-:6O?7F$e<)|O1K>/=/D{,l4/';~ڐk _bvT|Kܽ) ØUN&:IAX<#*-1[ qF V_u:~=O{⵾w?V[>V7_.vۜ^4%cٙfk>ɮ5^6L*Sݞu|cS?&oux֧-6gH1.ٜ@̝ӜSUKӯH%h/yܢr dR@_Xxg1l'ѯ׃dNkj;yM)q $2O#e-< NFS< 6Yn~j)*sU+6fRA"/a@~i,t̾tS#$DJ bEp $LAl,'Ó6o7r\Su2Zp hxn}ۘ>lH[PvIs#lwmGtOd#rV|(rk[x/6Sߵ{朲cyzd#X678]ΰ;:'X?OOW&/{K 6輄Qo-$Y/P /}U?D@37vf-\;r*`M>5Fx|G }/ rV޵<w;yk{bE]۫wmA}ii/Oܒam[4O{A i[=!ms5z֦ok Lܘc~>FKfS{b}^96TW1+&MrJ~4pKuۏ%&j!Zh?lbů7naW<ǩdOg4m1iwK [@JGvxoco?sSp6<ש7%eKG᫿^vcvGm'}WO.C~~/nIaéKNng?{?:?1CʧO,k\qKru&g{s/iF1Cv-I &憏gM'7hpBh) N+Y@ WJnz CR;OWN /~we_;ߌȳᑜx/d=}wO]7mm[vx52 (7?m;1y݉ vӏ7$Y9юɟ4E  ُ@6Xƫ%DV&/}qbnW^4I78> sѢg`~%)--\_:V6:J<g}xG78l&`?G??>|Kq᷶K'9Yom'wlo>D޼]Έ/N7zֿCSYlRtЦ`ⴭ3aT'C},YL,Ic]x"&u`7Vl))e;ta ֢zc f`ٔ Xɘ-oum,[p>a?Y~F'EO܂כw[S7VmTޭ\)ؚM9FiO43 y\zv$K-O|,єpJ0N?S?ؕަ?)ۛ;^:ݍ)ASbEVM:miWbLxK&ơ5^=y{ۗZ>Iu~cƬN|/ՍuĿwid_xZ/Y5֯: o`LSEP\?l Zzu%.v¡n6k:8-**s6lGC?UZ)dÍI{b“_c9K$yǁG/޳]_+[wmvsF7N"oJk:x/\rov+䴩xYvOb>+żig\Vk p\On'ױSI>)`.X$(@&V=/9:tSQOa>~;op8wğ_=|`{ 1;?nMϟ< 6E/zMhZ؇GOw.!MmpnW.]g{s;]-݆4Lquy#K0M{K"Pf .,5vb duoIIۧrxß3W^a{3mvԷ}˷197A> 3l͠ۄ!}F^iYt>&5./V Ӿ9/=2Lg{vυess8|_W,mUfȨږxGn~ٶh䗉YMM?ڈ.a{!$uKsMGAGcɞG'eML`rJfOqw gn.q0]#wep]d۾N%6M\B9soh>3K&ޓږH6m7>v۠O[_)}}pp26-Ce-JsƮDۧ;pVm#~o6/<6߶)ƫ)S~s}n8\M۝=aF/dm7e4ڂ#!al3<`A$2OF 7d#ƹ:/fr2%D"%~z{&m칡[yb1Bk[~c Ԇ^ &ۧ㶍5-Fm=b GH %IE[xկo7[|T|}ۻ)%n &][RM$bD g>MWVx7ATu-9$&׮X%h-1?O_[o@\N3y8‌G*. = qyl7LܒWS2J(mhŐx.|. TY pdl2U6[u-lJ:|߃+W#۰pug#¾֎SɃJ"7voGA/@BE`7zzdB 6asuEa@.ЮFkf}hOKSW [ΏHV>=m%<3خkw^1r;u+;J SG놖_Ost u[w \&u'לh6R $s%k6ނ$5H.} `n:Idg_s47I`}씘%|C7a>o:x†mpE;5KɥGISGzȘs\ Iʍn`OVrr=/ |7ad"M nmN$p#s-f/p1Yͱyas4\:iOwI֊!L}M)y65^@7]-LЙ%:ؑu]I KaqFgg\Ξ[oXا'z*M77E* 6^P@x X8=m8+`g=+4/C)Vsu'g: OmOd<86+Lg>~|$BRd\'78InYCEe_k+{ l`׆K@-XkSWq+>8dgW gۏmdLln~NtlPt?G_Oio8><~ iCs`צ@<.2/s 蹡Fi@7qI:禟6SsnyHf2֜O|ۻv$ $M5 ! zz^u(j[rEߎSe8<KiRX(x On@P77vڰ4vxxt?0?ȱOϜ5lמBLW51T_)Z4?gY|kmt}6]g[.Rl[ /?RQ}v|oBJ7KsKR6̧No5FLE@ņwR >'L0JѿzՇt1%]cZkncj7xHO+vÇ{w|t A7EL]>)y2)EydFTڵvڈBiXt]Top 0j֗<њx'N! QN(Jފ`͈,[Vx!cjO&k?Y?cc ˖uDZR <[ٗ.Q(]8ua'Oo2A\~h@/WOO7j 32~> GZ7Y&Gtu>u~HĶLԶP|mҌ*~t*&^@S*?is-VZoYh f,I`oao(ENXKLbP T@U'KAMmMU@9el?ƣ8oͭ <;,eQ=?g,@@xA$T?PޤǬSoķ} lY"0%(^L8c:,˭Q+/֦ uK(ӗjxƼjuا0d,#Q0@gb#x 4mRH-o3V籼Yn6iXWs%-. @∾"GwMI%@>D^KOhieg̼C40|Ȋ~^l_DptE(xI[_ ".SxL"NhǤưa6iG*NW^],NVly ? '|Z)r`1}#}Ƿof_#=כf= `S޽liX"nv,v<eByrOX#o߾5@WI`]5;V! A'@{N\..ƅG_!7suV$BlW=}i; F)A<.&Xax,38S(b׵*%31l #O-zQvaXOP5o6Zz&⻏^wkI/#&\%x/@{7S މjPh͔&mry>k7=ߪB#@fs_{덱п0-LZ~%Gpˈ{YXf^+_s-T>D@קƸ-9!r[2]3BcnCs?>*ԮeD|%4` :X2 pQSLPRaeyqĘ י5Fit )CdL$o$rpӶb>4+̹F_{Яki+x.+B.{L<۩ =UHcnf<>F ^e||pyv%b:iX'%8Iߔ? rw[vITVQN^dpYI"|#\cz[2M^S0[zIJ/HHȟ- Icl7vea齫 b5\M3kM)skIL4+vAQQRmlcY}ۼ3gΜ9̙3FF00 `3>00ӳ풾psF@F00u PVjǍ...Fuww%f9=w6yly# |7~u>>|,=ԔGaPP`]tSR9p纔O<)$r)d#1V~Uy6@_6fˁ/|<\iZl`e9n2:7'W:M%Lʽ9=V?6튱iPoTPȚ \Apᚴ+gyTYh̏w^n6K3&vD}9džC\:iz%c5$6Ll::'2$42F0a`- Rh󃻨5`o^w9\jmk/uN}pK ;*GBƝ¥x7q5^xRȉ2uc <CuH呎\1?݄~43ƛƘkc7<=4$qa&8#ijlȅּNdqI>ٔ1̑ |YOBүhVAΤi ]A\`R]9G#[c<9rL64d3K"}`}r::`w3r> #|&;KnVg\GF00V9Xs6yn#Ȇ3I׶Z4^Ryb|bgG00€g)wla\?%e7Ϣ>L~2T~-\_8V׀xc_? 8λ,t޽A5:m$օ=ii-TGm IkrjrOP_n> Am:s5""N4gOW'y_n} HmIL4jS;1OQ/NЅh/p;?I.Ղ],ApNBf7rpc+ݴ]>tnhcTzrIY\\-[HIj 0{-PS%;i655Vd>G7pͷnoVvnA~E#(D2@)яϞW-EaAU5riZ*~ ~!? X Lc6Vpck%= EGm9zC}XϘ(1Qo icK%ap--Rd/#%%el{4^{콡G΅XW|YoPB}I>݂cbmZ7<>\d:^Z*;D{OyNӹTQQaLTwժUff\">: id\!y5V!%D&cu3i <͎؁Oً]h?D,.]]7C\e1.jv fAho>,9\%v[0P{鶛?% G J1燐GlEN]'|v/ǂ /EiU m@89T+weחs 2KoDKvƅafD0۷oǵ^k`Ijas{Φ`ՈI,9G̘%ŘLlؕKcB(Ք¨Ԭ-QuP4e?Ư~+C§sR^h̹-<5ExoO.xmln-GUC;Fw㕗_.`-doÓ MߌmaKZ}5_L@c"E#?zA:+Z kiǘbԎn iXXuH/ƋaæMزj<01 W-{^(j g( ΆYg[ ksb{hay㣱7! |1x)e?7xnۧ6YqW"K2 Mp 'co4BGR4Py[?FfY^~Qo h6tҤؓ~ؽ~QӐ=w.l@IF&.F+돠)ʦ}p CRX'֭ByiBQo|׿ .4tjCQy$.A"c?()vLvK63&\*0IqJͫqWZZ|A#nv3IIHH0υCSTAH3aT&̬KJYZl7#-}輼3M6Пۤ._ujUUUW^x;:ᐧ6iy= Zd_xm' 2 %4,{x!<*yfR:\.F dVd?kֿ4̥ꁍƿU~f&ޏѓ0yq8FE(4!7 vU6g-WڍK`L̤vo:gc8Trf1@Af={6~ ==<Фwl #L[m+qUiN"tS@mMS]ȡAa!r(.][`tt4^y睸 A Gf[5̽;5v>Q9 Si΁'͍c+gHn{ q.( |0#-n%WtA%%G8sЬ|dњWQAFlhFܶ"D A?K5T0/ph #ŸA2Vc(vdauZVqcRٲo~Y ^8wnſ=g2ݽc¶;0{ؿm7&M&gWd=q3.A'm=h,؋4|^<:"4>+=Ydń%Gy!޶eyuR8k :;e͞׿C7r+a"5Ӕ 6dr=4ϜLjCr0:E[tT~$ɟҜ'/<*_?n3C)0&&h\u51ߡ uwwI6Y8O:)'al~Yd$88yy9(uȓ9r-f%>R^^I\ *D؁,htF:m@&ںZJǍIDGs-FK6^G?<^>&/܀77nA%pp<#p߿ZL[<6&[o5P'Ä0 =6A_w;FNddVaLr::Fu%TMۑ.)m kj3땆Ctm٬ 18IR+B{,9嬪y<p0S&m*mD_mM皔[/jIJ{O2w]8wiPzlI-S33g "=[4N`͇*h_?+rK׆Uo ߘxhᇺ*1Thbv9=򫯡Hn,I Q Mmx)ɴ.=qt| gH72jLFyaQu,{I**}x[}bC1~ 4¥ 삩ñ~_ϙ,(bxG&"^y~G˽\sg g/{ e)xV4?\jV~^ hRͨ$2 s@5[EJZ~-_C)//^l~gQll<DD$2irml̨U7o6eCVcJF@=wGfd k:ef69/ʅ^hfx\3>ko zeqA"*k֋rvM \:] c @}UHW]m2xTERl" 5U!(*MEEQ>F >Ī'؟Y^{=وNH1ڊO>)\}N52XAWS:z(;%qõ˞| 3,B_{# *rP[[jUGwH|]9 MpUSQJe4QHp~dEw0$Lٳ?F)?.]z#|DΝg'gmuxwpgyGßl{jhiK}U<8} Uh#tYIC'JgO?l?y7ڑVѱu-VkR 6gE+碉z˘64#.)hn}鱤?~gDy7!itqP=Y3g1?hF& sNʯ'[h|p@p: 6˦vr4v\ UdES$W(C.ǖ{$3 #ɭu\ټgcEp>v Y:TG!ؕ&,II(MXS p~FÛ>JMs=c{zS\Ɠ9ُuSP'r%$@>5cYڒi N=Ӎ4S.ʹcS1f1rUµ8xqέRrM~D("nt-| \ߨepn`%oDd4͵'^Uߎ8Zk cz>[Pg\iUhK~III'хګ|btuW[߀xr &2(ǽ4C4A)xeQ{e>QP_.:e& m,8[m`.#QIL% Xq%:(td`s}ZzMaB;A3q![1[}U; pmrğT{(vЄbvTLi>~vi]VAI4(=-z>=NW ,9 Iv^吡vjhl|ٟdƚV4$!wU‘ĽƳ׵=F 6iʈ 7UV/o7y C3Xv   0ѳAF嫭zOysz$8UL P4`8NL^{8Xڮs\ Foh\jMC "LE$!Y^r>ʣejFu]g#`e ~ K[_&Tu{IۍvGv~#G[,'pWA0 !Υ*qƧi{z|ZIx $\Nq`aS>1;Ϗ !fS(|?88NiO ;,]Z2;sqC9;yΧsKbRf옲dXŇ_9G{Y/Aߠ\zdйIX38=] MI?R/ħ혳|V{f{_GmGRlkn;d!~c>PVnRwwr͏)16Yxl=N|6t7\opZb>EȂ_) j.̏<>Ԭ|jn>SR{$ $X,)iI=Y"ΔT8vuw_$dTΕd<]R>g'Fg |2)tV'ˠxT0M7&p:`?jgS8#Ɩu6e(O:^g?ϥ>xkO_0`۩IӝxE8}:ݽw)ωv~UË!'XF2Xb+*ɹϩێ?bPZeY8Tj1Sڠ1$o.cXڦ!L&~yq>Mh=VWͤ\){ɘw+ΰI iar+t|aǛêş[nkp(un‹O>=<+)Q.X\~D> -C/>HIHhYZs_ ^x9WY4]^Wtxl[a]w3.y4.S[- sty,^2ΰ U滊^Fۛ{I|OhgM_*E )%Il:ZZHKjv$~OעMf8AǜqQܲofs6vv'Z/j}nW3[|+$ƄMC{~e#cG lR߭ S 7]4GaQ:k1nRyb_tn\mks15ӢzMXdfʢFkVo܅cIa"6".ٶ3#¥X+Pxmc"9;̇q6 G5`u رغz\0D+֙й㧲:t{\{1s k{ihu7D;z{g#7fŮ:̻zA`[;šڍ1eѕ/&ČyL,D6\kmڽ4rn-r־6/5B"] VI9m&~VfRDolf~a?\|y00~O?N@~ oaw/.? "m::߸@HxbCg%,>e2BhV&[︶|jtR[M4] )/a꜉p)8noQ(+CY6`F%#w&tpv֝Ybcx?M{q96)hD`dzRwо}6ƙe΁i-"+}v WzNIDX0OT?=gr-0InbeW_I!p,<bjI]#B񊈥 2K MIXB/L$Q>KĸPN}X ETx445!ko3*~"?At:s_71`0o"]ti̪PA6X`r"od:I?kKOOGAx@~c )~!9o&Zz03V<߸ ǐH~ڕmi~l'&̝_H5,azgL Ha Pı:V0NG’ےR%CQ;Psze]9 G ?=RQ\߬Ęьh3!j(c H XGU3Igia Gl,-G ̞vM /c? IcV8946gb/{u"}O&zy cqtGFNƤM\iFc}\ l#Ꮨ;1OU,ܧ;رc>ԀڭiqW?i&)Qaiե%Ç;⼃ze?'z@;#!:>A}Q~M[OK%8S~P5f,ߐ/r5&ftTΪ?u#ƇVs|poWу_YZhƇzbq_Fpc=zT[QD FD7Z:ߓNRn\3b8/"#~ N 6wM[tT_j L|m8Z/$<޳Ggi W}oBQ3{9Ֆ{)LW!B,B еɼc=ġh:LF0Ϙs # 2̮xrEU8~^$ST{"6YdghR?WKQ!X1Dpq>~N+0IX ƿS<4vَ9V ^+K3IČJ"qv /R|X=2Dw)Y*,4q~*۪<Č+_le\.);+Ȅnl:Eʹ&(Fuq~TImVp6Z--J9Nkhka])UH]8.!UV0 BqA.?܃&WEE ZDW\WW03Y:^{ >礆(fe*lంww^4'S b2_KI`YbꞘ]s}Ʌ~(ZIzOx^5^4˰8X;6 =.yi0d28Q}=\ EfKaI@¥tw7ӭ_e'_[6@_&>kIbjm|BTCa.{GbM{7ϾA/LHtkXOn/vd?<؉/!l "ZuXL2'#WhVi>#X,YE]})MF$&LbEǃ XlPt"@N_nxq͙6wp̗䨬i@rr7FꭎR r)xjpƋM=5cEx^6kىش Vzn ~t*O=6>/ǂi)5oo5\n | PkWm;iFEzA,eh  3)@5>z\ǣ:\$jo؂0 Q"MhL]QϏqFAQ LuEj 0*1t3͝\)gxBN57ޅj]J1)I|>%v>;c|LC nM:Ez&z6"$9twgE7`5=E vɿ#H£FKxU3^wNA)23{)ls4jB<01u E;V?i %.*lףď@%ŶwCUV6,vN„hi X`fs5\q^9Zч bYm: ""^:{R>>9``44ʎO-r'nwe , LL6LcўۼQ0!0(y]s hpݴ|+(9{`K8YX_z_?Q?߾x.]dHWrG sշOm2ē$iQ^M]30Ũdrԗ@u.֦D0Djݮ\ 䬎^X*Z̾r6p=߿a~t:!6 p5Z֥мP4u%w!c?ޱn:exW+++ <'x}mmm{9Z/=Ҍ=Qx 6?" ރ0}Ih(~| Kضw7Ϛ'1'mj146/xl:kQ/sIƪa*LE(bQ-Pz7zz:I=u'6=`Og,$XӮ9܆FsD"檯`6 6|:Mnz 4`0IHBF{d!\m |@f NwcjW4mNzAgŸMnT=8v̲ld{NYj(P BPrQmd.4^~zIaQ" m_ rR\`mTwgTnn^VI}pIg+)#2H`x)kcQ>u]^wz ^E}!L{>ip-|Kk׊(: ?I+U^բ7~>q}= N(Y‘)saiҖ Ҡ]>c#. U.!)-Nm8^{RDpm5ZLOh7aE YYؗ1zhntKn>ʆg˶GoSTTdBTlBk@tF,!? pu;O C>ܸ3L)WMS 2W,$Ƭܕԟ 9+xQR[ڍ4 ҵ=<ٱg&Θ *fDsuyhMp)Y݆)XА$̋sگ9BKǢMʫm\1&5 ~$8ojʧp"-GHǦMB(OЈҺI@J]vX4}^3LӒl(> Qc QWC^=& 鎪2%dXE 9u`yt"X1k>Q:9igQPvL7ڼZW~pJqg{ޭa'uism^J$¯|v2w?BWQ2"fi\QdJ'9)((58hL`.}LN'ן‘GD#:.ZS6i4Pvvrp 241* PdkXxxyd oڪ/c5|?(Bih#Lۉc͢5 ['㯼lmA^{=$AX[sCCM&r~)c<~4f%R`Ekevm͠IuEhc9.7:L``mK(l߾ jQj*2Ͽj5 MF`¹Abnjls/ljuNr"bW^ƅ_IBXj86].3ygfSt}ٲ\L$6}k5ngUYԞIKԕx>^חe߮e^m+-Y #u^\ҞX1`m:O~]~p[ؖ6ш6fc\Dxqm~F1 ?S|a\jG,ܾ dw3q 2[ pmEaP_įH@VfϛC W3'qVڇ0¢fnR0Dc1~X\(5mt$ >8][~pL<p3,xgA|kbʧqѬ7Jb\2:~i>ZFz )c7:>#IYlw ^-!Zm;#"u"=9 0}vN)ǤxMUʿ3|ΰK91:ťsglM' 2=ӠOb lflFz2-R{0dKFR}Mdf$6nXpH##"TKƦY~N#XΔiCswY8%04g4Q;Ywf_#1X( Ǚun>{*`+Z&x/ޅcƴ8k6Wr6ˑhu> xQS Azz>|譵f2ԷQiؽU2 gcҘ4?nzdN9Z0}u F гo"%M޻ؖn3 ?uvg8`oTYOޔPY%`զBpavlÙ~mw0~G0- DO{`9t*b 랻 ObH7}(헝0qs-3%Fc|ZS!4g , fѸ&>ӽ DGU6ݢozMWw(+*FG/B#NDpeSY^?BzS% ̏a9c;t307ol\OkC+Q.cLWs(ť(E=Yh@Cq}z$DOB<- ~;_C^A'7ҘI˯K.S(ebc[0&a&F%#,a@O}1,dh_9*k,G}|FJ3ыv ԇ0yTS e':|@/G"<0w׮[XMzW=tX$UuSdChdCQ$MEEEeS;t~YOdBFGu6iK MKq}^$AW#.U[_ZDrq_Omd6ZfaN"Qh\B8Zp3r P}(ql(ˣ+͋ut ׃QT>LR<3% q3B^4GAqXmޱId~ɰH7g]p^7K2|/jK.=x=#{P^PLDzp)'z"6 { )uq )(+PQۄW8xmĿ{Fa&.-uf I@*{Z!y,/V(,Z8!QXY>`SJ0z2'NCU!(\|\~z9eEvլ~"$3+WG͎z9虒.ख़87~"J.ZX৽jS0vk+<فMc;Ÿzb4?є߀8,"C 58oOW2j } vn:)iɯuhx8IxgKst+*4 iYMۉ@3w0+Q\p,.A{#.)LzH05.nX9-$W's5zMHu7߆Rx1;踙h<sci]ϵx~a<(ns>.ko(u! ڍ.1֣8RŏVQh+–Hf>.*| 1y@iw[iK /Y6eL5K.J8]%W^Mޓk.O`S7@\Ght=_#=Ԃ: xmv\sfFOo-?G0S!H2.9¿m M` 'v 05id|ET .&aܘ8=#M^\FNϖÍW8vij>J%.ɹ I2R}.rSGC)۴fsThZj+4i"_[6 2n:ڨP@+d622%=b|.:T_XgM:/%v}P}uT.;e5($* 3y~1s&cBO, λJ*FsSjL>ٯ?N޿/A v=4$>c|RaHiOۛxy, efQYc*9.i;[GȬ@-d4DDԴIHFyz2n{bK{ɢ^[mʎK5g=q,ch}ȯ,ڃLNFupnCX*Ñ;jl<`@vQ@es\(w'~_C!Q}͗KL&kDLkY̢dpyV^h?{ vP8+g>F?>!kaYI:br$qlI@!X .:W1Z9'A ШXۚ[@!'Wt.dyt#IZ/ꑔh\_MNDĄQDXH̔[e]bmc =ݹ#<6 cTRH(5S}zC1N-jGV?Ǵ&u-H;@縁ZA>eՎ֙tt߾|U93|t剌N"(9>7uo_¦p̜2^\*K{UR 3Q-fm#|cH{WsShE Rkps;'cO;4q%aO8kjimxcJ۴ӅKIODX:f՟ږtPE{Qi{چx؜<]8({}ݴC ([ `=ṬB&Ax( $i%x暎vN$gf/s> ZX[OmA1-n ŭAl,XCMW]-U(}H;j&֋1P'8x-,UUe9irN%X nR::>^6M##Мsvgu-_"FfQƽ8#!&66Ko uxM$$2kl/gS>{&pkǺG1'=WY4<~׭4~ރcGAd- eR|SHw3PA = ]է/7,XOx- TF8}=~m??B >܁{HPOKOOc|:;8Ӑc>+$μÝN74bϖWq™x{p6L`$|gm^4$l~1lzgzV_7.c.|K`ol| "4.LF\v΃$#GLTYsZ6J(iG(O>ny8D[Xx> x1g#9 D|$\ Z.pwlN+\[1HoYv bQX@d?6ʬk'έcG3Ǯ]ir%p 4ϽR8 m6j6$Z8M<8݈g~Ko`{6:i+:PB{]h E }Zk[/pGĹ} a.]8Av>&j*1Ɵ O!_?KnmXMlC3kp 2s~sYfqmkG%2lp~ʃ{?̙썭;rQTB0L\y"6HF%зm[mĭu4!| ȼ uN-ڹ0C's5~cI̸p!|3Θ>'go׽72D ]g=?L*~v޻l&G@ӍT9E"&mm'Ƒ*t6FdT5c#&hV>+j/zbRٶm=զC>=V^es| k~z\GN>Ș8*yE?9XO!u緄ARF)3Dmhsƃ1;n^~-tןD`"[bHC( ])e^Y]vQc%Zj7\0㏥㩿AA[6(u GtH?ZCRzI]-%ֳ'vKZE'Pt7ң(ڥdKWk7jW7w< '2E;'^z`V ĆJ,]4#&wxflqL:yxs=dɽ :0t։4Ħ-QƔ UBB^z7hGg݂NڇrӐ$q~w"d.eqz|o}%q"&+@BOcm@XvI(^B="413 ѺW߰DsmڳQTut$c5AX -ZW][執(Q{. fp|NCu dg9㩧-K;! ʸD1a8v.i_ ;~_1z$3">ȩΦCII)8]JN54esLcchm9/EwDSᨙZKqW<TVU2ܹh**j H^FtgcƉgoۧz50ttM#~^K2F2Š"vC+aR$ zC<=A?%-^n]B%rI3wC (^@ 0E?UQ֛'?=4F3㪮7oWm!Et1HD V?žZKJ83M܏?wI$5XO}1C? M5)(EES/y2Mm$uKqlVLkHGˡmhAo4QNh?|On itOU 7)ͺLݦP6I^ҷڢ<OΘG|zqz ݓ Wo? Kmʣ5_OCiMbmoi+M}ڪNsWgtt6nš}G)j:*ϣhx`S0d&S I!:62w-8~8}yܣ3p 9لRLԧHKNf`㨰I:P:-* C=P!Zy;2MC/vLIchq~^\KXM~wIAEZwS1s@ڸmq)IBs{iSD^H.Px'x+.Hy = $!رe/}hkj8kvmoW Ifr6͚O &%bѷA6B' wߜ!C_P xŇ<+5vZ چGKwN}+ع} jx;""~lFi-)x['_Ū;wvcn/=oMvχ~f Ċ4صu/<yfQ ɋ;!-dU;sw%1Axdo_==<ϘUT格qɳRy.*ҟ8ݯi 9W]鏳N,7Z ;g 9o NmQmCt I7BX?[t*n$~Js&\!"y2sf?_/i+y-^N'N=W;oq~;9Z'|QM1d6gXFq,4>XTH;0:fts;-o"OW0j7"gN](jAl|47Iƨnݎθq+ύ.X%$RYNv~$Xg~Džt7BF%J\{H2LFWC9z8fL3"_tO-Yj:YZZCS-iJ! 5 t`9yP]ZSh,du>{cnNX!X˨QSa$/l~KoYS o1)Lmoǝ.{z#АuUQfۀ2B1gە' PA}X$Zhw&98$C;X嗸*)qF|Ô5Qy P.͟si;x![߹m8ц=s"͒5?}ۙJCR'*LxY| ˱?'7ɝbnow 756XIz: b;jmfzb GGDREgXBF5亏Qd!0kê{pO?@dكvXWm>㙃4Ԧ~,3.l{o' ?<tO.Џ\8_CCtH R2;{}j|}ֆ^g5&_W;&W",/Fp+,`Inj]I 1d56pg )fDSVX|_i:u:C{PΗusG%E+ %r7 =gj6~=sB'+i箽&;k胮Ft,E^IUW|jIPX׽c$GjتN>9ØrLgGs9Esfu!Ď*_}ͧNqc nd!`O\c;*[s W{xMVD[~NtU;.F$|׶^Uz֌yo "(G{F/Ov6)[>M7]y'*>ӈIrf[2fKj̅uM->e!B7GYrɱغw/ۑ9g *zp6Oq'}>R9_&aw}F2_ځ!B??iF21B/~ؓ_D`w9:^'i| e<'ioCY/Qؘ3iZvHL (4qZ8"dFDOLr)%&&矸# (ţš9"{r-68L!d͡GkVsu592 ڀ ʼ&CEw TTAxPZmz'$Bfn[6ɶ{~n[c+B5kّ.T%QIDF-$b!) tUz3ojNFLd(]V\|F kQ[q M !DQ7RcBqq'Y0x?AXsOЍO#qrH_z>Jn32LBGڝXhyp:eԠ=1Hݜq"#E,x"DIs{WIX⟫3#!ҷ!31cQGʮ,gTP!!O+nZeEoHKYCx|1{뵿kDHݞKʊrǩ6Ђ sշQԎbzQ<@v5IXLaܚ:dvd n5 _A~ d9.LGO?gSl)VqebD񍲇ّn9KQމ{fkzwhnUu3g,⩺e0?;ޅP>U~i89}6<BTVYeG۴K ;7~-X>'k&Tu#vRלB@IDATn5Iե4:[e/:$C(ɞN ׸@)oh2DH8bYZc$|y9̊ʜ!hDD،XMȳ:kqO6i^(WKhK/b?y䳀6v{z}EERID*w.Ay!j:iȍoEqxLc[Wt7(V7ϟA5V-cZ4ۈϽ[^`<@Et'L˓!ˆ1Ffhu50(ю̘\N?#dڕ t8SܻR{98DT$f@d6^ $%6DWãZOAҙa8sĕn\R/n~wZ%¥lܶՕ(,!7J6b2i;J@NnCu-D(p~,ns . *΃dX߯ySUSOG@GA3)R{Lowvd4!׉G@eYĚ$_֗'!sz 'k2x %ڍ)i s>L׍7̦h nO³9i* &)mGN'=_j!r4Z ;|Ҕ m.=tpϛ `U>B19%I * tG,̋\h*mxy\u'J_ u?-!"x`#|0 GRQK׶1==RTFMmq.҃5~xNupiI[m>Z$Zlq2!]U^FW!JDa 0J5# U="WyZDG kar+HH W5VuXL(h ڡ OZwu6kڇ(O 'N!*`q1(Xj̔z83vZsDϾ ][sATuԢel!,fhÚ.SYVGm4Ie$.2gU`P&b4cJ֨-p`<7Yic;[|Ȳ5μ)pog%EtboQ-NMl(w,Y<.~p:}- 3gF:t0i#Zh.(.8Tԍ̙hTk6u!"r%5v)加 5R֕Ϋ5^w.*HP\ r"]\rL}H؞N;q2! 5.nCb0HOb:_-CQF-pC]ѵ2@jðlFrm_I`.\QS}9b/Xؗs5ڭ_Q }/T~v'Rgӷ2&O\0ѬMqA.~,>?\]BۦQ}RAPƾ:V1z?Xf2zzxcv׫k}vڷ{r28u_~N51p6F*|A>>xOF+(i4[]Gδ:bGt>&M#';O\Q17ʕ~ xn,l0hxjt&8%y\B>>DQ͙2Tedn_4ǖK'N*܉ՑwΣo7dy\ʋ[??zeV+@{cn'B&5:i}1hp☍; Č˱=rgdk/RyۮE#\Mpކ͇F]l|}:[g_ L$Zrٱ?hŶD^g"]3lcxc&: 77mn$2䍰3q {ᇿ{!)y^/ӌS݋r~OTy%/t?CBqܕ ű[ퟆߵIqرcهEsI}.!g]0}ItZz, 9G"c,ҩ6܋J:-D|C d f_"!hAsg/=+pO|-!x^vA:q \=9yط0(6b"$uBٜwat;"9y֖"ܽo߱?p2P+ O!mۍjxDUF_\_Mc2(-*/-/*j'ЃG< '2qd.*[{[H 7/!s [=ކBn~Wءp E7wCA=r MU:S=X"Tԡ4jz1%pv5 f+Ep`^ٽQi@-I;f`p˔] E,!S܅#f统/^ID_>$!B@@@f̜]ondƵYF3D@)QD‚PVztA"1)쭣m= 5or˱U ʰ HmtENGnLK~ .51oüe7bNF<$2R2>=zƂg?? LEMEbb7>Z6s>Eٌ!8]JQ w] m܉5R<1AoN/HaB1PH!ֳ]@ 5$Rч;3vT/22g0JA4B$KGjr,;>g , k1:ygTL7 E{UԥuMDU#?6$XcE>?\P"b\>d:{#c, ul+u̳p*H4ѓ19Ezvvw~vz|Y!yc0}rL+Ur?kG/|;$25N.ŭ%} ρnD`z y+I0 JIJG܅\Ya(.?p\ &""@3BNUS9BHبNp@x# /E.UvxK>A@ؑsH62ܵw3ܻT3vyN59c5y7E@}-p EHFNXG{p*w}Ar ,SrȝBGw։^,\93cw V` nذѓ͛΀]oj!wv!,$Qd&A \*9-g4pD"u<|^߆iKnÍsW^P@,ZLr f sjlO2//*ZbB<{ʆht֛o ( (=u dzN!u pi*NJbb?(\ơ2oߋةa;c++ZԸǗS'&ѫDPD POIHU3VyAhǺ8uU%JCKKNpwVc[z P R8ŕNKMTMojjI=,SjwU!qDi)=iMF_yCIQéH 륏JH3XLdX%("eU#x]1@`p">FXl_?7*(Z|~Z e )&.\h"\eTGə҄$Uhu-xW~N~G؎:[ c%"8w3pER@> ^\ !ؼeYAwY s†zKsk4 a<J:E<6v^<ϜV9vNͅF yIx;55U !ӵHhB"lg&O7W#뽺uMGȢ n^ޔ@>A C R14TQɞo)px H[gBBՆAZ#JI@ʯ60f/Bb*KmRYp3=wDtS5:ݐh6gz;,Ҕ4T1G/CoͻrC@:p¡TgTl2hD$`Nلj]S'ňBsUi$#\T?紷Tre*!QQUIITaH-X%quccӦmwRڑRxTpwh] [ʧ f-%η`IIJFEŸf{RO/ׄU?axgD G9fEW&C{vԠ8: ́++1 /t:]sR?S:-KyU*@> iY~JXY֞A鰑R&iEcN\'Er4vybZHۂC9TMH@ĊhWEO!jbQSETx6o|Gq xE#&< R'w C}hc `.%ĹW9К Xy"҂}T4>5NBB6ϯoQ%|ghakv=wu@ᑭWw܅PVGF+Mkote-hVTw-)3svGmM%{Y/.H'>fNm]g^ݏyOlzshC F+t P^K ]zy0cLTl|9px$g11Ჿ e% g!--@$Mb(F͘t-ߛ&B6IC?l߈X աTv;l0a[Z] بf KXUW7lѬ _K[}",$Jvxۈ <Ϳ[5忿DChMZ \,HI#ʶ[CvDMmKo2} =nϼ`j;cҤXT.7vh(;cBF|G;B\"b UAM(spDyyJOS\HNw$ni\ŏD7 эBiCdfH@~luo'PsM%mC=hNNCdd$-> c+ן”v#==E(ۺկ}bU(Tq6#`ڥļӶ%z\_'_Hށ4}R9VY7uCCV.$6$9*+.}ط$V/_oC*]ugiA:ptapGR\:"샃(xzaط^c1)%e%OJDyM-&zd-)"ET# p 7NG =ÀJ)$b>ށM:|Bϐ+<]N쉼lt9޻ -d^ }ȽƠ9jޮ] q͵t%wR/ >m.[p<GN!c6U9-^?CĺGv|zelFtS1+ ڏz-$4zG3̨OIcC-ROt2s3"tU:a/?AN F$>A2,* | GJP_Jf2R&TV0AұX{$ ӷv6N|DYp ZB#w4wB X}RfH"!3w%v)'ONK?nD~s'D;$\HtA6m1̜lCEHJIB̙EنFO[6 ]nWt,I2\#<}Ggd|̿^yhpR=b< BCC=EΏ/z&yVc=]]ut!&UuV `a7fY:̟5?@w\ꗿǨ#w5%5Uݦj6/pk@F8U b;]oëq %#։5/5G77.x'ücQD6; )i.g  Fz/KK{^ُHM ka&wus4xy?ͤ1m—8<پ5A=]V:8VtSxÑcqˊ%fBĨ<[46aU8EѥOAy)~scQՅv&ID*x6ʹܰO?]$aO SQqwFD8(îd:=oǑts)p p@2&^9q-lܓvٷoΥoOd6xRbq^IN_Ir4eJ22Y1iS!#:jL)wG Ϯ[w>4{'aA|NU7b TRγ~xz#\=mdď@:#a |)Xx {tVPp2COK])("#u\ kK޸ըE:oԞ Aq Gai92Zך՗g|yKDm9e䜅v-h^.I2\Lh1Ew7r 'zq+LUp)A%iF/Zڂ5h+|d&DUe5:x?WRmv\cnoQ;U[%''=#_z֡J&\ f<\NR&bo*|cE,<&pL6M}kl}-H̼+3JQdoK~䣏al9xٍ{ޓwbA<|]N;:jme2^3͇>FiE&)C(Lyyl ^;ń. qșwyg~)\(i>[9U%oB뙥1o-ޑ9#3U[}cϴ|۽u,@66}qB(m}X|$I!$.aY[Z<Ӥ S22}đOD-oi2vAbziξjoCs+|*566ZZPi\mZK ;illY:SWo1<ی1.4f$%%g `{86mYQI-R̈M7j mBWP77M c+-늲P>A0=|e&Y Xq:įG]:{{/|,1xV[)Z6AvYPֳ WMQ%U^ bOq޷z2Hl5!IHyD;IEʼ ;kJ'Mԫ;e Os{k ʜ\M01p%ι /<}D?FWep447;-+Aܺ|zT7C|[z;üR 71ƪߴ[yڼv_C~yI[iǓkޙz@R>zmJD|5Jj֘m__sZOsn[7/ Dx9?o~oO0pɪi. w9ؗZkEI 86pd>s\)6z+c{;L@!_8}T4b>!pNmO1q GbJzG[Gplw, "";s<؆ӧ t̔0tRf_,=<ݮn8١CM?}o4}?Z:w*w[xPAne5su0zFW곐k>B-t`JϞkօʱ6U>Jos;@\?{q ?JDaHQ]l˒lY*۱qrq/Ν[HU^({ A{y? C!Tž[fgwgvggg4P{|!6448ǜD2䩲* Ϫ^ Hbfdb==B"z2 ?;Wƭ-zC{,Ÿ;X>.qf8vS9J*ψw:1bAlR[dOem1T]yAqdA׿<Ǟ vj/pzW*&FNwbJ;U\*)dAU?^:52Y}3׉ZڣvT$C {wAʧ3zc]6.y YhS?٩qТ+8i@n<"3g~ l;OSus122׏!#i.CPpJPCbhFVZ&p =5X@ CO֌|A% dë/>̥+O 1Atv|>W:~#DOm3]8Ъ^+77wB¨X-a2&β ]<LBKm|;9!wC(h[P{*ng1TYII 222y>E(4YgZ)-""|6]?9w1nij *B(/ܳ9W|!°`fv?kHZ-U=vqo>8$$ڰa3;[MTpK .Rɬ*zӞ5kjʨ-Dj0 .sB}Q^Gy9w +SڻxOd7Qh,wC\l~eDcTիhW< ; k ޡoA0o{xa6jspDӊg$!7wlW :ecdE/.M#պJlM jkhl&XY/b؇F"~Teֶyau;L0ۧhyCDĉXtw2GtފzN}GvnmQ8όOXzTABd7N@L2f8ü?TЌkgS6unmw,r߀vo?8ѵ(ףDMܸAEJVӝQLZx_w-M8)!]~1Ey@O&]><6;E -2t+ f&5k8iu ۂϧʕ8=]v 3*qmq*?RE27^y 7!W%$ ,os7݂e%&eJ.HcP#E s?s1KgԽB^v"DQßlw,B#۷;1((+V, /4k[cgixZ_JBf*B<2}̢^@b?*W9[_$؃=7wұeHO,) /Gv6r`cS~JOA[@l=z|>YMVS_`b=PZZ vZg.|Uk+}sx~%ac蹰cP9 O&6mm:僝a닯,%!>m}&(7Upd q>EzN^D5P3|qB-\3Ŏ6*҆Fn'Kh&%Gz%gqH#>!Zm*dĹ)5IЈ9;.i9eREyl n巠2pa2-_ >BEYFzlP,_OQ?u ]MvDKk7/܎o?".g)n[<{M5х1kp/p<`܄.zOokpX"(AљtRT̴&o'4\1@IDATQs q+o:80ZJ5GZSm{xz^ԮO}44$Ζf t\9i3+D 3?J>:թuWE8G۷;.4 _gV m۶9KicJADCxvzTJێ[6aT02[}{ Qdљ?JӏsOrS_|\\ZA&:ሓm[HlƛLꕁxW㓏=N9XVRvGQXZV?/G@gpG~,BBm5$KY)!x䷏r7ԓFxqxb{Vl,?|ɸg8φOG-dOoډH%뭻cՍ8cԁ٫G030Cy54O|I:rJ V&??9;zJcS%P%=0c^R1f!U4іy;IJnYseܤ";5}h!f48ϛ4R;oee5h+GC[dfΡ^'.5fG&&x1' #bppÅVbhuX!c4QL[`ޜBÙ#{c=wQI jnQz7XCs}ƈy;dBJ'BhJr,͊G[S5~@X[G?MsX8O]c78_ԃ8S݉%6Qj ;a&Z(]hygd /틞?7+;NE pox *`E -P2?-"Nsv?q9_QvyK.{Cӗ"'wzJ܄5f/s2ӹXDЕ(|g7G{Xo!/ o|=Dzge˖92w&$WvT54VeMGC_7"?EJ20;(z eS;1tD.yYC=Ex;k>f)i gl-g92|w`q>=ԑ4cL ddf;'%ɫJ:(ep11qzcʬ5'+𫘡 "LUV B:wP~ ɀ߭x""# Mф립G|l"Nh,> o@ʘG'x1>C;# U,h}|])֦h?suvϤTrF'qER˾ܪwc UKsG3G g}xw>rC8qz~% =wSͿƁsk NLCR:_p@) Ķq7sNx1.qv~/C//Ǝ'vv'W$[5~[gmB T?Kݟc5AbP[X"*:7sQ,7w\]wxZmiȊ|*cMJ]T5U^NN3u'cfB΢`L={872h0E<#G-Fr7}P?^d] ]O'=n&01Ey-ebfIOŁ6 kVѪhx&<ė w KO>vGn@͈8d,gQVc'yNS'.o|g{C1+]GLGdBǕ D֭s0BG{bi!WH< bF<>D(lcP>Gֵ7`p5E|5.g Jy̼1өLءM YE\bqs| EsMJMnw9bW>}m-PyhNW *)dxĵ#xs]GTOS/v,~r/_Á~nZ~&>f3VShv*[gxq;Ҩ6ne{<Lz=<w,D^A}"Z4N:o=j~k9{h%䏿NZ`&V /S\fP{QSJE ]/i}(+<CՇ yf&SC+4 LX8twջӸ߭=[u\uNM7;o}x/O1Zr *sCa{Oa=BjlŊVNFr,rHS ;}x _C8v$|_Qi0+L<x' z=o&[09riƜ>ڨ$g*3Sj5`dO8|:e8U<\Vzʋ',94 {[yed}ZJPKlR* srU gJz<(1Kҥrxe28.^e)v j}s^X6H7=Lwܕk^s Wi0wܵz7$lnt\sOAtQ t6kMy+,I~5#T:SO[>o}/3:ZeyQ=4LekKРP}jޟz)GY[Si(^I0?̮d0X5x ***rC/Koeh((N jKpЯO/jBpFī 0\kg0JEuּET&TYqdo&"!*F_b"AQP?˿%QB}y N3s7ukG7b/JD:j̡X.x~qǬգ&tTB.iѲmcX98 Pg{{d[;xv|}i#+^{sʰ 牅ts8 c8_scpP)߉tzZDi-fĎn7qbHd[D/& Jqp9VաD?u$By#Ƶ$^W,i0(n*:/穲-L>H.x>D.y(D.,;y*hn\#u:vtGQ@﹏ly7݊}?LTU5TyJSµu!U@)^*(V5u~'ŒǏ;bj7_(l|+y~O7&N-_Z4f>'TFtiCan-u&+-K Δa_ǻ\bjhD5nE,,/]/ө,D\oJ2,[n{z|>.Nͱ5OI¯;81ӇC (K󥧐ʢ84waŪ9gʥHm<ʺ>_ FZ=5)%V.LډsCl¨ x}|'ٝ(w=݌rS˄Mxi#O<9 'R5Dڠv _WDVVֈԡr;xNGjJWܚZI ~1톴ԄcKڵm۶Q=TԘpAqۍQzwg)X{*{)~A@L;W٥E5wj"8 VtTֹquťe2j1T)6nލ)pikk#dž7O5e/Jrkۨ|i3Ai46lg9&z|\qIwұ@|x_U(#SC-edx7 G5bfÊ4)UV q_B+y|uv\]5YNvc Zm]w5\:V*ҳ+ˉ̠)ʰ6<םOm Xb.LN;݃!԰($#hdh OA Kxa"wii7v1MR[iom[cSAOc4c1!ʋ6&,YzA }Op,*ӟDT2)B4Ay@GƍEeg<^pu6Rڨ/.'؂r{S)_5AwM~1:1Qw2/O4Q|VL۾aT i?ͤɶKU D<ݫL9)| wkOwu&߭?Zt4|B 1[٘78ϲzNǀ %bn*VjxA߭+?d>.*t*b9QNLL+b"YHNBT's jnխJmeV?`!-LQDq´bW_񱲆 p@8-ƛ[`il8&"ʧ FT]s(&U-(aqroGOJ}ߵ Omxyi "ҾܠX]Aƭuy]4߿xau*w*_LO gJxe'7Rj7kp:5vVsBa5l1oy.mܹ/nåҾ߭hOƢTG7!.J Ĥf81LLLaNo*(DE*K=PTTD%J&=2j0 A2T "~v'񫈷&њe [~'˛hq܏( bz)e"nTpBWg+Qup W?(rA pښ[h̦\W= m޼!rNt٦;817ӟDя~|O+woqd-{eǛҩZgZ񴶨w~.O|m ecqp!9nE1/^!bm(xֶ+y^Mj斵KeقhP cƛW waqWV\n6dbAk]&4b3޷;8wgEZfm@j;MVuڮ@ܽNDR$}PӒmmKT(;~~9k՚k}C~* .g"[^}W=g=UaOs-^]cM6,VǘȲӟ£pٻ9oJݑ«ʆǑ_noO@11:4TE+ >13gΌL}8x̌W1lmy?=G8W*Sw)B:νjqg~V f)5u 5Xs 'ѯc[rџ4/cKo"#p&ȤAXD͞nڞ]8]ُ +tQvC4.gכ57`Y9c9u`:ȗԯg;{¹}5+Gc|oOVl/VX~K"FdLLխ5F4VPcv4_>寢p'^zy,cN\^0¡cb*=ڧOtsZ7ݎ,2jn6m"XG}1M2feؑ(^)UXѻt$.6(~Vc:$NԸgt}4|kFy|JqfA*XډOf*Z7z7 cvdjV$+7`dmS( Ҿ&O}b'Nda"J<{4.JU{m겼+aVEtީDPK;TT1䱳Q)xL5I[R2 F.}jiMOѩ02 xإ:mtFU+(o*m\R+1ai*>Dx5€4f5Iĸǘ_}eL զaTD‡DrDW?/cd֦N&ٕҹʻkI&-`8SsFfļE{ < +Q"l]L(//wA#Q(zm8zp/.6;>yʂŋ㥧?}7 ӒUm>?'՗3IWCNd_w7s = ߊ{@ C T{GbB">ozvO}ŧ7Ox 6 eM>l2!iBjn nlZA: B]t巿FiP]x 3{?l-(–^sDxĸrhxS_k&&k9VϑqmDmS ֋GĠ=[Ȍ<}z]_7A^f ~1:RC{k;jwU;0-{ZxY_{2 8T523~%3nk;bNC۶#$O͏,^1 2ʩH|bۧDYhvB\TEAqMy ~eS勑 O[n[o1:aWxxAaԎL- ud/w<x5d-E(hDVJUV$<-\ ,Gj,]s%ͫh}(n?fdƹo~Fl9iC"I0Pp8*9";9M-=8uQ&A C/ˎ#F1[%k57.r~>Vـ!m\T'_~ ^ !ꗿdٹ d{} ϡerUȢ⮻rE&ʝ#2wJ7ވ@D}bAtnmn@$- W AhnÑ])Mb߯~x/]F#iumO'>z53@0OܴMOOkks}0;'"H 1T[xW3Db#}ɵ{ld₊%#konD|t&֭K2:2/[ D'"-5e&w#<5LpUVa֬Yc9.,=WӲr1?/{o ibp֩'ځ}?la' ad:VdM=nAR{1k|$Ƨ")>Q:C1t8+^x ?rb<?J(> /C8!k/ w += ͭX=qE#$۶K[[e>|ڝyrG#';;>޵m\-7+dekRq<K ܖ[b4w یήNA,y{[{&wrF`EGoW8x;;Jg vkhA}9 Enu"O>@vc۶xeAwՓ o—N{,)pD!8 2"2vπ 죿2fbFvGpx6z'T nIv7YcSmIHL2D!wsGvb)~ؚ5{sc{p.GTR k/у(*d©$F!Yt]PY 4E|J*ғPSsdvcp;箝9*NQ rWRRDa|V8X sF]$#5%ӟ  X8E{qG?v$G\ [w`qB:sIo䳏ٝ&v=Zы ,JLOILGJb,(œ32'T1/_@#;g.v[nB_/?/]̱4%W :|rA9bG#c0}ByT\"D輑:Yb3t9_o ͘tJO#2}>VBSCqr3D̟ؗ' Wm32K)fJX,knIFک3f"6&|;ш'sAғw_6$4/$jS Uq͗1Mc]g<3?<] Ho94w۔&EuQV2yZF2|? K2f!9Y:t4|x8v){@HyAMḃjAf=HG[nFJ_rGC)X8yOHφgmɼ 71Xi:t>{#jEzJAt||代r'b(szƕeSISgdR0dM84>$Ң!4" ~wU@:qÆ #SxSk܊aXJ6_D/unoeOy=pCQHL|o=-t\?QyHjwnj.S}x,b̜Ai:rQ h Z8h2M6Ѥv$pFٻM*H!UH,z}9jaܵ\z 6=ϻ=y7;M\bhZLi _0I;L4Q$tyv>r|"ȓr F_`CP&Bq} \w\,FwmtF~033W!-~xߢ}ٰkrF;(-.6YUCk=v7ҏp胺7woF&By8yJO\o?DY{ξJ\J[}R}W1TO] 젆f$p(^z#Ds@j~nG.vtzCZӪn ?ލ ;CHO@w8 kgGw'?w5);>I:ûӌ]EZQȄD]ݝ4RB}㱪BُغmEF\@Wri)~+\8Y%H@D'AHkoAa46I襆@ŇD͏z;J_B^: ccޕn=͎qWs`D1~m?`@Qu[PU߂ v#*2VrȈܕ|yE|*G} ^Eۮy4GG;hʋ6}j}:zMըՎh$!ofSLw/;7bXƼdH*UW'Ͼ(8]!%uۍܾy)Asq\+ov'x/ENBdH4JO:OU؁G4@&$FxvyX;/wLc OvksP5m&n͝o006z; (,!' ~yOD_|{/`_NG: ^Lm9ߦA2BQwӀ֪6o.v(8RB y4&MK4.giFv=;NٚZWTTӫqcHbE)xoZyy:XZL |Ԕs`7@IDAT {>=tm*h*2,,^ L3g}|YQf&fD,E܅l:=mۏ޴3elZ.# $*/jKcCyޢ+PUO"MLǀ~--xhm`p :M!*4^a(f4!IH +)Ν.NYZQ\XVӠ5ڃQ4{@f"y<Ǹ ,Djj0(v2ٔ26-wq5fdW e\O!<['y8'-/}Rp +ЙJoG>*B:hW㹺,"#+.-v33LqAPeTCO W=1;bHG٢-DD?2R73xJv,xNu)ӌj7ZEХG-">[H4O:(XDn>9a$]];pQTlyE糒0=9Av9*#@{$|:QSK|>OW/tiDS}@7۟_ΛrvC)]hR&CHOwgR1scJxZJ5 е=f|+نQ{0G`L@Tg.vt^bfhC'3YO9E )=.TD3c6%)i莪tL lBko7:.y2i,dg!ỸM닁iFvXFݎ8)#ks⣣*Fb#&_^rK݉m8f;Sޘ:((9"fspqnNAj y΋d ̬~74ѝЎScY[38q\v j Cs@HX@225-Qn/ieG&ӅS%P|UuHHG ݕwK,R;ުJ>B'Qy2$w/(\QbDn;c)^Ă0j1h43wfF\r&+8*b)z'LmIB\|5F$ݳX|nF:xBxiFvQ~zCEIKPQHJ 碦 #B(s2GTuxDRC_ We :'Wj,{%ё-X닼~zjLʍT\ؐF7tS-Xۃ6_T[ߢ2w{~Q b`VBx` wmmر}?rd"99Q~?JN3Zmeb!'m ̓~ڻvb͍SY) i)81UHCTp"b禡iE^;F@7 1XE@Ochj+\و*UqnvK__;*ApA?8(1sbP9r.w7 cN~E6r!PGzBCB΍JDŒDE%P,څ][v؀jGaTDõ4# LQᙳ()*A[Iyʋ@0y8L}:T`@8P䇢%p*MFΩ5k\ݑw"15}_G&Fb/n^aɪErL hEhdd{ ִ>A\^:*=-FXM<W/qqf!b{@9=WDdk2N <*1 sg'χP}> X-Gk)ݝI)G4Tn}ǥ](}]T8كXwڏT*>'2Mq&š!]Ovr!dAQYTr΍p:}Z9k_| ^{u 3) @?ϸRsM &u!db=h G@3W%C}ytd7RށX^ FG'y,09̨7sȸ6PL/ϹSG5cg| ڋ@Yl}ĉj7&6YL4~C}Kb{57}\mF.++U_ʺD7;wH xpv!r4#ԈwSaib*Op5ŨI+D4n T>TwFnō(< ^W9EH*w"5g| ȈrL^W/5>HᕖWxWM.]UB#ա[$=Y 32:5ԫسYqҪLWiFvx3E Clyu˫6xHJ#]&Մ[ys D#V5:)Xxy9ry8Cy7ûf\uso[D"`tѝN0J:DoV?urue%(NHpUDJBY6tr)Bpm$Zցpe|,#rŞFwK3XOf_mbf$4h~!U׎V#y$3Flj5~d\%~aHVُY}ki|0w8b24\ c -sǡ;*-u-xEGG 9%s BK#$h _1>.%,sOhrGG<"GZ^̤耶x1S%^Jrb`>.%Z*4D2ew¯]B('q Αq"*4S멋DXkk<8*DT:|XHxPz`kx\!-CJħDaFt3 ==;ÝB-ŋ;ڗ5Y؉8ʕ:1^#Dgiɨ3ƺfҺ`jjyӨnx~A3;b. yVEj 6"XTtG!\7?xGMghkaQt0/enGDqjJѡAp&̢^.10|M=+s̋{?{{nziЏҏ:H y h!%cr.M3xY3wHT?;^gPWp2$\myݎ^-D #|{my]˻ާt$HEB%[,9DYN8NVVNeEI9J)V!%$ `v_{1d-Qg^;>n]rP aL]jTp+%q?ziv^<@I/^T[*k?Ԛ<ͣ~̚_%- TzxSim.ljwL*ߣXHzOs>zFԁ1#RWh˫r'U ~fɭj̣hgY#>P΄B ŽdQD{n][k>ؓ )2[4jU/PQ8.*/Ŷ+"<,C|uC,Bt;d%$G[6tz3GeY{ٷSwclʆV.uڷ,|>-N1V琢]c ^2WsSgaE4sii*> KY4)۪xi7rb^z@óuz-?]ZLiV2@,AR2~0%f3x'Pa}1wiM 4w9puSAr8#9ru~;;>#c鏨-jaaUoׇzw:qw_Rӧu]|}}tTno멍] N=e"+oMrzd,WVzs:]}Ymw]zI+AP|f8n #8ƾU:Yu3ai͊ODC'JU2{}r\vcD qLOrKZPmxHj3.tٔ D,F[z1\BЌh>\'og^Ui˿_~MПf|^?ht /WWGhX}OH(ETe80;h6TjuǰOYsB^ƾjU :g̰y'"R lz/VU}-S]s?t1 pɉ9B]P X_ƺj.N "yrrM0V=d =OcضAЪ#rz~lKM #.v: 2;'ՓPhQx'+r^y{Ùy'24'|wח̺w37ݟ"t㻺|喂w ]vjE#~fe*DCIt6ę%|)ʊ6U9ʶJ+TUERMФT:{t\m)JɕR߆n5I7Bmʡ‘{(K~U4]^*,Hԣ3rj'yyv' 5Z@ #j* wkwcQJrѣYE` $qgKQګV?2{k w90"J6RiOaicjHu ƀMv蟹h-8`9͒ĎGU CF!Q?6׀O_4H;I jqsA'z/{=?r7,s/;˛*~X|F=ֺhEhgEzCz[CU.ڤ~kT=Uv< rPcu-OلqLj0(\3@K#kș5j =Z31lm2}63J@6ԸзY3(cz[?8T(gG(ԁ@K'iFjtߔԭ-+r#;m<{-1~ޏG(w9߉1>8O}'=MiBԹ.PjoL{~B q=[[ ۻ70ґqokm>S2=z(<ۥvU# 2%ԉhF<)9կuc[ǗL䟧 93^!r}1厩1dW)Y7 o mY\;7f@*8vrP:-")>윎 N?Ⱦu7.^+kPt)gTtV4zFAuݴ!,h< BHz +nl\ic~4#N Ï z&Khh= L$b#b:¥^~%Kj͊=]BoXk*4~՝+l =X4:˓+ʿO6<(kE-fo(sլK7-OA򺂣 z]rQiw /;;!5zzV *y/]ߠW#ss|n{iՐj0q;eemuA0%9XS*9 PR#[VU?uӊm )+RQ꒜@1]a_ƋqdA2zρVfO_[qUB$q|hxLtH"T "Hv YgCi178e eJL("VyrMh:OcZ\$!o>tabs~]-ZE/sc-J`ax!/W=Q&@ǵÈVm7|!$lXm5,r4֏^k*&|h;1\U$OVEc* hMNzz` (eGp4)=qə'pxa*nb4ܮЍ]w.a|fw{Ehͼ{lk00+n 馜B;3^/=n1Y}*pB(pwr]{{L8S+ W}*75Z>#heԜ 1یA t ňi=Yӹͣ^ @r!u102G?N󖘒#>~1 \ I(B, q#9X/MШJCYl A(deCL_b1)W5֮ޛP-pWI-m\Y-K'!"7-G;+6C/𸰄AM,l >iԴz; j 7`G:~*"%?x2a-v<6#:-0U}xȲ=VH%ɾ3IoA9KuPVc7}$-~bUF^Ñ)VE8Sqy=GSxf޵̲ Su5"D%ރTr[(sBßN_(_N0 Kմ2MK.ST-]TWudP+LE|B3S-/D9%qs,"l]馚4⊟>_D0(M 1[:  ~(d74o&d ѯkАze'h=~r {ыzr3rVCPg\~\6ۋԏĨtau0p6 8Y{՛g'A'bq֫b85N MJc5wė&:y»)XP>0kKVqꉍ %.i3e\fxcS'{i* HX"{uIiɈ9W%Vp)䥙ZmWn0!F+5 bg[Ǒ!QC;)P>?^ _pwgWvC` U \Qn}bx9{zrZ.~~܂SO ߦ;0C,ٔn|ӷX`1WԘ;4}tZiaon^a}&7be"fxt90+}zhC[ȏ7ˆ)‘ _L)Lϫ_az42^LKC~R2^ NNuis^IS0^ː 94ihuqˠ6 `;ɐ 0t䫰Bw {A1NT/^zmk{-ڰ`I\a<-{Ha.zT>yϪGSYRC]T6$hYEKv|MSSݏ6J99=3x -(CWIf*Z9BxHeX@ ۉIUosvZ%FW{Q ][/@,͒뗯h# wP_EA`.qG<$6K]3ޘKb)i>΂Zg~]r~_8ցÙ'33Ȱ.3^ŏRbS :j-O%SaBf12Y>.gT `~ =2ms;3 Vl!6oW`Lthcs]Q .iAy:R˂W VNybLB,]sӉqAQ7_Fy/}~޷l˽r 6,Ưk7rKO.`9ꁒ]e]w1c0p_ $TmGɸrӌ/}0<~lQ<|X'  bP/ Bc.k]9FaOh(P:0yX0'x&Q bVtVAº;Jɰ"sS`]8ɫ3Y%%;ֵ'3C(i U. kV||[nm#l7f#UTYHlgߏA2AFj8@OigY](=ԆcZ55Re>=ES17Љ>Q\>tN˚keOCxzƧ!,k`ZA ĩ-b%RZP.a_[YsQKlj-`6rCYIfi&7 3дUHXփ+嵟H"jޮ?8nDwW$foK=ZzAc I} m&kƺ4C&O<#N-=(Հv{C-^kd0R't/hKTAClOHeAqqSYA1l,4AlE_K_=}ǿ.g dV`/!^b`djLz&ar\\nn<(s^;!uƧ~Lo|çO|4~Ն6mVd#aDG1/W|izUT\h%a^~'qdY;{W}`P7RɄ6+[ȓ2ߐUisQJЈ5s@=L{#Ύz lw16 Zl) ) SCK21i:U"Nߖg1x5h7hMFﯸfE;bjFQpUXWe)[,6@SMn,ڲLH yK0H6كTݝ{M7qR=: dCǴ~xHAq N7?SfIJHĤU,hI|dm?D\{[ƾ"={^em_Ƕ*E.| ЯcFY6>b`aUoyDj5P&p-5ӌvb $I 8NCa#q7mRd$A.m뫿}3o;{nZ^WkVe3ymӪ%m<>{40tkND%EؠCM~`ޭ=7C-]??8pJT?sB[GާohΩ8w =%U"yT1MƖ )ZuFi8`+Eeܭr2U V}Y DUg 4vp슍#u9ЋSGz~KpGjH?I6TQĝn;ހE=5I@ |g ˷җZvk*2/_2+?+p*gLY`,DV8b`T EocL@ k)Z:S&ZO!.3¡Qby*/&<ՕŴt*-]JhrwY6_9xw5FQ$Tm: lU5 X֋ܦSYֳ26* ><1߮\޷Љ!lbYUu&XET_SȾރ0̘cfu,=M1=u-ִUfF/ԹvkGd "+Kt?6,aln~Oܙ:M(y%^[3a<Σ ?L4lE#* `Uk" ƹ#@]CW6r{XlEO ARɈO9|C]ocB!A\F%Uiـ\լ9x.8$3R}-ckMpJrF: }q.zzM\;l6#40>`!`*oZN4%@]JnB\i񚛰 l vT]y&R;lW1*lnvċ_d}'z.}7]cpmAa2NꆰD/[ $NHDƶ y} NK۶s]qY ss;9袣 Ew4d$~XV^4Kr,M;E9΂oNv 0& b^9Y;`캡CeP (]^-q15˦=;.\]-' t29n?2kL`/ФT9^A-?܃`= Jjz0*OC
Dߑ^>i(K/tA﹛[]g'0JU?D9&#i/'}{+IđH֩Œa[O^|^y}c~Q^&V-"u-[{ִAEO,2C&"07N67m[>o}YݙG,9C5U'-Gġiշm=OgXiLV܁OWhLR*4Ue. UWe xkLi `G;$ /o) 2pQ^-[¦T~CfuCo$xV=8Dp0ri<}PL>kPQuFGT7Bc)vD, ImzaAQ V&D$"Zv0"pI:6HǁdA c $Cs*S>i9Ne;Z E,3`miY$qC%on7}Mۙ9t$lk(?Oe50@Wq@y'>^;yTu2ɨk:ărUUCz\ 7!X=hC7vY7xW%EC,<б^3`h6nQYԀ' ުvHE{-L&׵IlGJZCmOUuA8ш$F:.ݼAHXTvs|LE0#ul1y3a y*5@eÂVb@_6^$([&Hcus&p!I10[}w hIq2hzp1HbxhnLM:6[ :i#h42|L6skyLSG`vH m9<֒]Ȗb{gCg_C@_Hw^M<޺fo`F?ZǛO?sߑT6l'#9O4Mb"J*G"9:t*+}hRxI?M?V)0A^1X8JQmyHF [va?㐃X]]C#¤*5g4x(&9RNz@y ~v@Xw{ *L?xHi9`6@IDAT'}=mmbTЏfuښ(% aРzFp%34?ń@ZRM@o } V02b}} 0Zh&S'(n(o Cxx2lvE%8XW^ۋ/+=LLPvoa,+k#C7+'?4 39sj:7JG:7nhB|gX-U&BWe_7Qh:PXq2BIHkXj@6_]ѶGc3¸?yK;yCz324oۊn1~cDCcMDjLL;4\LJU޾QWa~UA:0ZCMĽicA.]~X^ G T(Ρo$ #:7{'nQi0uKsh;\UybK$=\Lt=`6sA^a: 7noG.UkD0 =ܓѠ*H"・…XOjp 0h8Mig0]HlH{@G_Vm2"AնЈB*0Мm״u۩ 6,U c)O2 F`2o hat5\]3i$xsa]cSo}f[0sqZ#7:fqAd:= jvƂ"av0G6GBc#iLc#Y @C>է/9ߧ]_byX\wa]xaKh`v7F3%K5Mэٴeimh׏fe!%Z۰ y.TzS8ۣ#T}C,Qc{*a4!GƎ X=,Wfk) uE [ 6]yCZB\w801~VЉceJƴq NVt&j_r1Vu OnPjt0ymZ%v {-x^?۽~}[u"-5B8W8pkweF28ge m:x:}Y" , l>BSA@ϒmJT2141K\/lQ7hr;uМ&M?["h'긄+yo; eЃdd< S<8d$8?V67Ռ27XT (p_0s sf޲xT2O]ј/'zRS_ n}E%%5fǏ҂AyX +[ӼǀRytz 8d&yk6#<9 K$ |f(4^`n|NЂ*Кw>4θ+v`BȌMz$wa=@[}׀priW1g@M*;YOe@jOᒑڧ-IJ$t@5;3 fVv=eu;40$mPP>>Ƕq/)o퀔Mÿ,&~xF!x(R)HJ%WJEXTXk7yɩo,fǭ TC !xMv/8SVz플l;uv<݌X\'aFU-F-Сv2y75}suEfX*ҹ^T%S->fpP%؈: OGChZF1KGMd<dndy-%W4<#W Ī*Z/K1 {s ʘ& ձ{מ&5Ot^]vF4DPgqA.2cTУ4M\Su>~OCVwK?`bNIt@/MX\M$:7d@)f5YL^82π*1zѕxh>G82ʽi8 );pGXf=mޏKC]2z$yx/g?G93]`՘2X _#| %p!F6zIV0#hG6q|}T :6NFN6?mq!iޡ%OWP-|J@\@ۥQkF| @޿r]uxm @N4ɸ$o>3 L!>ހ6О8k87 t \PmL8>AEHҠ1 H}l \ufeR}" mwliѹm 14a ]f/bl!W5dyB>JGܒ*{Dyj=[/V%&qfPf0J@h8hYmG˱+l p⋩5{7WǜUho+pEU|߅^؇6kj#gh16%u=\ׅc 4Uy,27wtJ5T+4t/-^C$tWq:GN#m ,Zfŏ9ThJʰ9Pnلu=ǁ@9ܷZ Ov&cdxKpc ڊ9 sl;Mkap2roh+]\M"Yśr 5IV ,}7>^U{?/4?80_^2.үu@wu Hzs th`5VgfR5<>wjzԩԛfZr#CiOa7A]Ulk' S[[Tn9j'AsM~mE&?UqE~_vUPu4gUMJ:`@o1D~cXL1mfi0Ehp]"Z`ʈ+@Pmk4,|W&o!wJu{% 1O ư>c7M\@:PcgXttj=[}r2Ɗ|<@[-:KP6;)fɁN3Р _ s]2#+og5sB bÂnߕJm{u+u&d r+QhOb<".ӺozBK?[7~^_9^zZq8jl0ecgu~&yylP?i,`6ЃmlJ)mg*|8)^DzK,@ sp$ckc*muGhz|&HGD_`ɾvp.B&L1Ԡgl V!g.,P Nr? ?A2a_DZrj<9ݚ{Leѭ?-g.nB?zVXt%۽loWvullK6~~rO.+5#.~F,h=wQ[Uy4/!@ziY$l +Tg|8aL^A  &PORzi4{K1s W0|_ :6VaOW `ɉ7z.#z"~[ҭ &$8+۟J="KvdX8w ꇱv7zMًWӍK:5arH޼9O_A z"Y\eu@u_W,PQ[տu=&Xn%a|0}!v׎<kj.!_%^IH[7uڑ}5P_Go}:e$093O5\`BW",_"'c JB\36X[COt1!W67r-# 1w`E'=6( 2>ͮϷ$@Ϸi__pK÷d4zOߩБ'~*J;.>*O1}w h/2q<ɺNIpJ;8BGI8cT!=Y&:xSLu<!,=ׁhʩJrh;lLkW''b @6 jmlݲL & `у횠? L.8\0{J A-k/ 847Mo+(L^y:BR VŐ*c󐴠oID9򌤁 ×>CEM, Y p`#$P$YVl1Y~Y3˚߯Udm C)<Ҫish2<}Pc3F6MVW멭u#Xn2>ǺF pY/@E6Ɵ> |=.Av3 髇*񌣲 uik:U#pzQYJZ0Ψ&ǞPP~ܖ֎|U?#c01,褒k`]DMm< dj >u貄szؓWذ#@)T?CStWjDZE >Weޖo b{x%Lj~O+^?v*#9ٌ?J{̠:g;q `λ4r)/S tɒ~PS|x|1$ܧI`G }')If8 q [:BP;HSI@I(e]0ms9AJDUG}mB^焚($P\c~8C3I GNibK{S>M<[G{j<HDN{#jQ/S*p'}'x:o3նaZ|22zư_<JjzK$<^CZg#`[C`ȴ2PG_k.Nϡ5m†lA2(`/2}Zׁ>*.Wd2F0d/ Kb+CEE fxD ӑ"2z']ouhu4SPH؂QS5~ B؄Ȣɜ@tFu~/a#6[+Tih:A6`ʣl5NLhm?yNoBi^vvU n"~441RfcTy7{zZ ӓyT,ͱlB$ΐA?8u GoeK~jH?s:pE'_ JI~y;0I0Ѝ%[ةo_Œ%Q ;/R=|* ?~$$k?CO vf椏q8lSW$yGH)0%ȞnZZ@dq!NR{y &Lt(4S[<Z,(UT C!|յYkUipM;ў>UՑi$*Ǥ>-gKSu1$i=#QXe6eM?6/6qm/;OG'4( N4ocB[P8}ܨ밓e.G_۹NS}_2#QdzYLa=#{{R Kej4O4/t3" -83GBFUEd[Jь~2xq `I9],te~z6E%ymP?Wn0(̥mAeͅ^a6>tO~R~ÿ3g~7f-ۯUv!`Њ?EF?U'7B׳\g^yf:l:dr ިQrnGnG$0cZ-!~ևZGy({@| l73:f.8Dzj[ f5?mI*~3_ދ![=c٬~U{+~._ Y%OR4Yen93Z@ƌkCc~JP_qq-ĸb'BujdnZ?r ˦hmh$@m0$6 wcїgAVHWZr| Qfg[^ze%]0%Xv֝TD0|ȝ_#,ۅ3Ÿ:gT d6cSM |xG-R5PaYk{bb j{pam'#Fj%TceZ"0Lܚ_q{ \w4yIOK43i}䕗on2&].ポSb5o7q1go[bUexfkٞ R,ˤ@7ކMc&lDyEvδ}Zt?bcILko-9'C4oPܖހ? hioI4Eo] k~yX_%9Y߂+CnC QqoS陵5= n liPЩT6֯TT%+͟DuŻM,K%&ӽG~P߫O.ET]?#aAAԦ?X0ss[`,Z6?uXǀcAUy#-yufnJ+1f2O)ZE62P2R+>U{f'HBwShvQ=SKզ;W<`ً(Rngs!\4Ϟrq~k^j0q|lAfy1 ai&{Z53zp1eТIڸ=H*!'?wuܷƶ$|s_Tj>MOj0NEFѯA;fhx=@VbfE؄ G֥-i 92mϷ& B+C1:d:ȢWyNhaȺ'bm/J1J/hcwY/_ ?'7TV寝R1Ĺ^{Me6(ش\-ښ/Ep98p\hb"#U An՞aeF[Uϫz0l0Ud@uy6[!ǥC^sX:I!][h(<>Xt+^_2OSB{ Oϝˣ#S!a%vq ў_C_K;KwMx܃nOX~ -[6GY"<ĉ Iu0@j`t`19p >:5؆8gN4f@pf@ڠLa[kjvOk0qL;STiЗli8&=rp+ ΊbRҕ`s{4ި֖ATv͕ כ6u B"h{equLhS)ˤI3ď,C ~h_FDhv-`Fsr<=(&XV%~'(޿N&]p!ՄEep7&bجNcUU6t eF: fCc=,;egvg{MZU@HH 6ƉَKi|I;ϕvqO\0c:juj:;ef  £mSyy9}&a_,bFͦ 6/j8PٟfD)TY&)$$vX3qjҁ($gZ7;Ny&kxԲF xi)Bm΄i=z8umwWcLAIBNH5A{M(.|zrpI\ϭ CWȣ˂wl& ^rizZ"O5нZG+ XwsBZ z4Z\G쉱VeSXTu6egcQ,m{=V<]o+j,C=rYk&bchA#YhM^}&oBrܓH~+p"pa&?XƧ`BGt8\aP kM"!GHa4;22G*"0Ԣ*iRqԲ c4)>{K p,'o>&89VZZҐ)Alj55vm"zD3C 6L~zi2{xQ2K"vę4PPP?Aʻ~:)]> $53\"p"D`\u:Y_ HAJ&: ]%]// nBjq+lfg,}\~GP#_Yy&1<̶X!?0cYSR#@of_A.4ł|16`sdfT.I B`W6{n9d98[J7RFɸa:V!8 DdD:m>xi-@@g~ve$~mrRRB4 q\+1+ p_cF[dC3T3#wQڲĠLDN͏U^VH,`=vPHM^݋O=$zlvShZOۃD`=ZAtY9u eBʧpL4 9${8{q ܚy Y;K # oo>D؞! +!H,7lZ:72XPr(@ĈhC\ qn *–*ܑr1\OMRB_yuCE__gLeH*F,UXhw:mh?h_mkZ8ion 0~)/BXW'05gYg"AG.aݷRï0ȵDyXEJ%Ӟ R^mТ3@IGZa"`a:(^0K$y{ʅٟUԉ9*=Abyv,F_.sŕlƛJb;2ҳ#`LJ.!SnjUEBg4ٵdä7Q͕9TFQ,eISj<Ân$b-pf0sQuIyE!3{>dE@5h?`*ȵm) ~ Fsm* X5}\bQN %WYafѷ&l%4gjU2/bR#rc㸢 y[qɏiʳ껪m'v#JLD# ۋ/P(_{7Fl+D ͈xg춾')Vr8ѼC^'\`缧~llZA"FXN؃q=D~7IF.yGN#김0cU>C^\6RP^Y;:Dx0¦t JL28, 1|'a(gb.l J+'(`Oњ%s$4}8OW łP%JJ s-kZzv.G"E Ƀ H[P 4(T(I#X1%C!aT1d2ģ '$Fs|0G902gP.LPbΑY*Zk&qm,9 8C;xUCʪ(r䆦2IJDH#4uL%0!S҅|0C4fSgEFf|z),c3#AE\S xɍA"1N#MT9ذy,D rXubhIdxw1p[̯A_Y*Tx"IikXhx7ն@^_ -h`/|zѤdUڿ7^l߸!G~b8{Y )gVdb`dr|j-x5)YONt*l*L#&m\D)yh4S7)ˤ;t=g X uj{un[yϜ-e6^CK]nnmjldcEI։<#I*@e> #F9&,yyIߴ)J /hadCp.ݢ?@ّKHܜ7!=4jbŅ@kb,D~HYљ= (|}XD=m<_ɱyV*k?1ˇ`r'Aóq*E]8M<[XoM:4qm^h~n&UQNH,vI="D]c3$cQ& >_lg`G VyH")Nd]#;9)(OMyyEXЋQ-<02d8!d<HSǡkJC-D&ZpŽb9 q-檮4sE9EMڋ*Cu46x~-5p4py 8P~g7p̤,mD-tj 5QK<)\x8Q`E>%yzy:~qťV̖}w]Nֽͭ&`]}{qMe+1fl6Gr#^_XgR;1"@v=N, %@UY~EI ""1,3Lg ?G- h0myZgi-H'1$QsE k(7O9L:ڥ}3M6S"GĈDY*'k3g!Ga7UyaƘ =A?F $HJϾ0aףEnI|F^8$ؙFA7;dE5l'B8sO19wHƄNCD]EDP֐'8dȔ+c5w@FtW!ӍSĴ~Åv`A<M$Q:!3H';S1Xc '3"yL 3/iBB5 Czm@IDAT$IbLaQ+ekiBDG1=H ')\ DaZk"C&5p/r=E:B{DyDY:X4xZF%K$a%ĨOX5XǰMQC␳CMɕH嬄}9ř6gAX&fx 3,vou[,Ovkt2f-;-w38 !B<Ʉr(nhYMzrCx|v sX󼦊P+t|}f7)E[jXo߯U_Ze=Xm6 PXϣb-o6}6GDZ)k |1gQ2kVUQc=#x"UHCu]Ǫ(H{!Kn$%]B,˜)F$dOBiW0sDchH ,TT:,ǁ{25.*$q}92MѤK?bV48! ]I` el4w 8meblA wr o +Ւ/ PLij~tt=rM7D89i21"%wFn&ߙi`_lyM[Z\L,'?$;W%>rai?C>j 0va<Mpg5J =}R ɨfv<aD 21M[1m~h%Gi'ăY^cdFɪffND-c#ԡJ2- F ΂@$2b$0f`W߷*[Dw u,5DuĠx~l@V,@sD\6:O2ZrN#,^e˳d&]VNli/ Z?#CX[~ZZ7oFrtlMq|QQ>>]WXꉈP D8K/ q5!u@Bt4ɤ魎A>|Z{YKLZV-Z:B^Z uW@@ %lrb4^SrsRQc4B VG.I;\i : #:ZH/ړ{\3?2Xġqwn.~ߩl1[j[o]X5fCcYr4?CԸ %miJ0SSV81 e7Y*}B&XI ydZh\3q6^ II }J@FOL8 C;?Fxs&O(#`ᦁGh&1E2G3 wy?bq .6 *'6gHj'!Θ) A D IOҴ0< +nZK?t^ؽr_cnb\zbH.JTQ^rY0ֹӕDaa̖zx{攓ҍuq+"q->G-8ƌeΛ' --uvK~Ϯ˜w9nK,av5A*FӰC0QQz|p))˞?`R(rP8ta+6 1bQ' 4i 0 BP ^EŊ=ҁ ,mwDIk wL \nC4h0l$lF&Z+T0o%<YL ?I+R-3X|  ы&~Q ^S@-Qռ̲` y$% dͣ(SU d1@Q#g0LYZ`%xjkX3)$ЫQOb ԋrvMȐrs]StFLZP>O~Ie?;=?[grsOӒ}:m݋ltb6CFuhzuG*AV ;ʅsY0k( 2gt/Ns*B=M6L-šJgѱBʲ!ȱsbA15c6<:5 PphŰ>p>xdOTh!;hYLayAam9)&E{,Q`R>8x즪]}7d2bUSeV[tNeNce^j?W>8صo~v zIJ 1 Dȕ~g8V,mv%Ap|7xaI3ɽM^,˂c%{E%0apbRq:`[R<+EbLVDJLڑsHV sV0TEFAdkf09h Q,d^*Q&km.䍴,, Ix<\sȰx(r Y^LcGox#E;,-vSdu"GJK֮uD_30M.R{Xy)@`:3LEjxGɨwMon $ wIxǏu|t)13y ,ј6(姦Qjrr?}8Q Y9PiE4LhLBYp ;Dn!C}3&Xi>EN$ȑ~E jǀ̶ j `嵢k7=jْ4MgȐ$CoNm jIH(@RwZ[[\ۖuD"aTJ}]nɐ?X4DːIc$<^LP Vl=6;L"ABb̉nȰ?`LP&m ?{L\յEeBW ,%0DKK,]5 `贈,$,KO󖆱=:?jtZxØ"XƦ8 KjluW "245Ay#?}h6~)-*~ޟ_\I a/^p=VR"{)u vs ]NgԘKrƫ[v_LmvCC/>t\x &N%֮lѺd"ٔSm,ub/ > L_ NC~unzST=o%ԫQGqs*ZA tAY&un>`8ڎ>. ؔ `-Ӛf4DNb3 o)^\)'q7*NԀښ,(zH3D'wbQ.$dh'E]k{62mmIDPBE ̟кڭz#A{ǜΥcr4dТۈ* K]raa鯤NKsLeVW_cmmD!Fn$1xL3dlKI?.ZE4&tvBI:pw*?A!W #GibruJ7Km4^^ra[:lC+AT2c$, VqrJ$IY*j#1"W?Laz-$4d2P̣ 3/d֯ƕˈ=`ϡGܗ6+((믿_|DhvB["\'iyU9g z/y)CO=e5y>g&X+'"tIUWa5wΠ jST(M^+턳vƩKp'}( P$W 0<& }U咦슠bFS(kh]0*ﹱe>Zjmks^f{a[vFHܻAt?|0EKFlS_<^sEOg$D隴5_ZCIMZURH+P76jN Wڞ~ 20\\T kL:129gaj=c"}QbQ D+b|GmO.1LB \E]6l:À z@/,vۿnŝs%ҊCT]r*JjA ajJEm;3{_g]51뜢ߚo؎!hmAp!5 4~hAhMfS֒z|NW_m]]]XjH]c`@i9PCJCQ4Aʏ8-_ ~a-5X4L:Xm^M` a/jbGa=b=:-0F,ҲVmK { i"U-PN%VM{E#%P^8^^p,NU@!z׻3=uPE9gS]ʯ\T Y-0yKqR_Js6qLTeuE%Onuo>lh<"c h)wV-VCNx+m *p9!~6hC#QŒ$8o)DPC:JLw9jÆyK*#h>p6]pAqJ7Y*B(ì(L:ojjr{G[ \IGgpGIX(s5 -."h&v載^EfRAD=Ys =yFHc<٨ ۊi"y0|C4-7^nhI3DƱ78fK:n^A̼^cC{1`9q? ]S6 %7Eԑ7Yh-r+X90 ^|Xv?qlU jB&'lk=;7j X/y.幽P=UNLe؄}t5 Z3ҏ`"'qPR% ':'x¥3V4&c6I4'E]WvLpʱK>sr5sh ^aEAW}f&k =j i$ysM!FkݩdHlGr$CH]xВ#]'ʰv*?Gܳ!ABb(k/\jeC#n5iڼRaG]e ;ﴽ5bgxhLQV&MsO(D&eNA_ДD)TTV[6ɞGOITLJ-j7 HCv<-NԶT:{>wEܸХ8/Oin#z#Sb/:Kl];l5Tn #t&u]6L6Z6iDT{qVR= l>s #-7l=s4%ǖMMgm*[j@~N"T$R{nNnhzҕS|g>\VTEH .d$^޳=_ )-tT ]}[޺Q;a?u2RfCR.SK&b0?nr+++;kF]qCLw1d W>Ռt 8Cfd3:] 0yfk}&rnj1[Ywz!/^(uz?1qc3C08PBNo E[mS¾sw\U Mn[Z><5`Yhh c׻Hm~=,(/z쯄'GT0W^i~?^E@,DHGHꜜNT?R)N#E컊mQ#vZTEh>ROum@ [YWP$=zEYh++W:=6Fl]F-@˻ϊ1ӶpX+ H@M )WZb֮ێQF/[ʮZsEL(O= )fɩe0]xQƐQt%CYƟ3.e<>[{'z =cйcUMӌm =fov*_}u~v7X{7c?E7fSJN,!JS -vT?*Eh˅+kk[4.J; RBX(:puhJvLt|~+uox"%1`2b9ůt~{ٗd>S3{Ge[ebg2Ed U6;-^WW- tH^bv<{mS>nJv9Sr=ly)CJ.E6"N E<bȠp)c!8ی݌0dd(?qƛw2| #gdNvhy:¸G?|_k]nߵ,yu:ޓǩ 9BIx珧B#׀kX?!a 8曻rB[o#3Ec+q is #u7[MC3^g$T+٧/w2d6!J}As`1cQ-;3,mȖ>j+.^`622"ň?$hn EV`P4UC?A2x`0dx,{Mw9 "1 Ȑc~zbeGn}y\pgh9:["k)WS~`@f'=gkg-kqIa94Oo6Yfk-Z@}s1кƀR> [~vwXZk\\ZDFH> $ΉY$݋Ѣ3ت3T= Uijq7dg*=h9CC ˢQ a9H[gL7&zL ͉e̯eX^j1ʩg1u{<=vш!P%3`(\Z2lhJHS߃u:1JzK'o翓W ~ޓ'ӝBA:)ǵvWJ8樫W*㳯67ʠ(~J5DDe'H,/( AB[2RT  :Mij}\Dd%3NUY)ٗ7CWz]g,U_]H-P[=pvYlXG^`qM.,yCx%Wymol-'zA .,3biwOˬ›-76(0Cȿ Ӽ1 37!e u#Qϸ[ 9kcXCirc5?e𽛑GRݯj~uvG5b/ΜwCPxಊԠ)ӑ#v\&LZsiz7. v珧"9@!W*kf:68rܠU=jKrVZ2ZUD hڍwj֗k7J'\Tɲ3Yz EX'fNeu_(;Mj/K[ޑ[G=\\<Ÿ[m#6I>b{V[dՅ-|G?J?E=RtY{?{7dwu׺:vfi-{okj]6AT6j)@*<ۇB Ǣ`nS4}4׭CV֟k-m}|]Ԏwt9w35H0~NAQ;}:3~CtC^0' `ۛnR+NUWo_2ǣGOz-\@ЪCo?VzК!~m{bTw~f {-4}Y5CjnDss֕c\5mPl 1`1b|dSˉ!GgSG![Y U`  QȊ jQs(*nu0* ©VP6oG'S;> \ %R-Ҩk@J N8 Ri1ڳ{6lf&i6چkJd/żdZcc)^-4}_8_i XVU4cNJ꯺ $  ]j-/x͔ˈsuf!aNt:{ `;M?~=Σ 2GjྍqTڐ,{+lದۛ\.H{ 3_ ؎ssLY<+^b+ܱگo׷9RAK1rgXl1Zy688 0ӕ=C^HGLk*Vedm<߶Nؕ":ax#>}me=Vc_O4lo*M IYՙS&,Q RVl|)tؑoy(hۡj{_ύ]dc(FL!;SË}Wꡧvby; eYFu(F[)"-bJDiƒaa]CRn^{؜.!#%i\DLSc捧cЂ1d' ;jʭm֯4#['C&&12tl&pˈ)ZYǽ^TLluu+O|Q7SSv7XrGA,#ozd&rF&=?ʽbI8>xC#x흵If;\ve-|%)4ZV~hF93E%^jDivIΜ sYcn(7wXWbklĴ7/gGԦ O<U,+&+\g>X s!>goS3ys=R|f _kyaY*0X]G306;B#),E#%flh#3Wi5r" ؛STI7dNr",:n1lOr*g:w}Gm4 7MڼpJ:x*0$At\0N8z=Mf?bYy(q>Ď7)~hhhpM>[ZhB{* r@׷뎚jD__Mm0EJIeKAj8s>+@Cŵ}~a+yj^czɬ/We|f|t0yd0SC6Z +rI'z#OYl!_-*AlМ^wO9!4i4 jʥid fM66c]1QΫ=QH|3*O&v4ad9/3!;y tKjD'9bir7-vc6G:XeֽVdnHCDa5D[S2%iArdq+x$lׯxm?Ʈ90o^5F c ֗s&ѻl6g)EA^'6΂Rw%ÐoGGVXik,e M P)b"` l SCw@;]~G=Fy2Ep¡$*NJ+wȐXK50-c~WȢ ELT}YFs@ gx_GRg؁z#6^ooxf{bnD@kIOfL_zaX,oG&,Y0k#X#Ӷw > cVe;P}m d۸/Z($BYO%#a?6P6|\mc0.}Ci05_Ճ,(1"{+m:kX=nd4:K}7dqE,v~w&W<{hxd%1`DB`6.Fۓ0V 21HR#Y1;A-׼-,JR^Ίdغ}tQ]%t`F'fGlZ/p8BmNM$ 3SP) XNkZn>q::"ߴ2/ɲ"l42gaFCv-#kE^ˢf[@E3Fm&Xwe31`v!P,6^b9+A6_y4np:'qdj2oO^C;:̤y6EC6/|9xj !A"2T = /tڛWCK7d6M\.{}ڈpA[Pei|A+RR^"!;ڣOPh9`Q%oV!y!AUK{_ð0(;>/^`E v_`H$ Juf7#9b@08ⴐ?-6>̱#v4b"ĺ[~3h'e<W*ܿwm2(%$%a5]?s'"Hrկ.<^IkO"=3/:gvO ؎/x~\/d$ۄINec!HHܾh).DUF96M~.=Ta?XS]1Y ro}!b#ޘE!=Ź(aBl|NdXYP!g}Y5-f7`pP/.HX8;9vuZ`14 Tdļ(O2W%wvfNF\aJ,Ky8})z0dB!ێ , j;@IDAT)\`z{Gn)̷-LJaIuF_ ;kN9Q{[-@@0,ǂ' ' 7WQ͢,/ γ!GV[w \ۜA#_+ֶ1<%(J%G bPaXU鴦`p^,"0DW!<۵.ףv W :Kq] @{EOͤ"`w:41koGMoڸ$,-{3z #.uk׷^*w(b;JIa&쫻Z D9R6i6OF<!c:䷩yn*ګ#Zf`)Mlhvƪv\#];RaQYAxL (oT0|!XSu, E',{ 1+h2q )y4b60+Z=eAÌ\Z38+8TX{N;\+%,p 4DK^)z<^ii{ku5|,76^My5#;'s٠}~  nW̱:>3({o \.۲JlbYՒ;v8ɓ8'8qYG'YV1)b]%f0A|%%Ž'Ѿ`*oht&cٔE( 1>'FnQ6kiaG.:L7o}$py)kQKŸ]ܿhmg9=&;0/5$Ӂ|Smpg@F,\x(*Pnؠ,RgM]WX͵m+hgꢶZ;kq w/Wgӏʔ^e?#{{0:E 'B.,:ðArG{jdWmd5ָN_i_9ƫU Y:h!>rI92 rJі"r Lo7#m5D뙊jm7 |3}CE76r5#$=|5Yrc.9M:PjLhym%$(|޾/ ?pHGZ) ,PK;NdGBS7qkZN2`/yWY =ZstNΑN'z (5Tz>r/ {탍C&qϯAՇHd7 bMZq'vPg.QXH-0Tdȗtot%5is6EfӓVMդW,]lpa.t[-;s-={:þG}Vi !^qvv]C<6orڸTW;5zH*hsz!{3,<@r*#G aeWw* #EF#5VA5n.0aNVX'S0B "NSLغ-ϭҟQR91J4{*,i5P}yJ mȲ=_c6\qEH'L|F+ApFSro%쯄G)CL\ héU8pL}8 Brt~AXEukUV9r0h2fgɘEY?KFLvXfN2t0VN=o6B{G>5_$T67;чZAs7נ^VN20ĺm_fŰ9 "u qKK |8<|u-c#WY1,Dvĝ֖l!Wxq:r` C/Hb h9hRՌY} fm 53 THg3iɐp2)b[m݋W( ֜c+>fɑZ'@ŵ_D=1#0;z %O< Ǭ,E.$RN۸i,)& ]+t!1FLh^=,!ּ } xl&\BC0Yڣ?Ca S@_ZeӶ;e^kXLPE1x n&j[gKmpg/. x; c*zhCq47mLr|o̚vXpm,Ê "T.KEڤ!h1h'Yb5-Xla֖'-^dYVvSB1#O?sD%#"(&MxƦ{NmiڨTCoVf-RU GTĄ再5h?oS䥦$׀AΣx/*!Ӭ  cs=֒iU-fT:!@MǕNAp?̖HxX(d-,Yx"y9f&d8 'Hb>@LM('gU6o-&^*@R D297)}P;EOd:X->y$9p'\),䖴~gqk8`|1`v^,#FҲ^?& /[L\=\~s6z.n F-v[ȯ&ՎB^ f 1.Ee^ ucXQY@PXgaE.P2dV`O%h5T ,+ԃ7 7PZ֞;lbb|`.@_ǜ$uЇOyQ$CWd>'KЂuXR 'JJ!^;C۶mۈj16lZP|d-0{_Cソn+6RM"5&-4hc|/c0c,0lV.$H9"F)mډQ+865$\*%3 8r5Zs+lc\]uUhsu˲:#(VeM9_J2v7PcUSp&+S--',c2ɥd2z@ 򑼣21}}s:ZΤRiy~_9G-~D]d9 yƏHOHER4%Q=VC //|F%3\c|e lac' UOFZcqUСVc5ʐA̘ui: sIV'Y:2W*#S$Y,;)K 9.+\aOj%K6\om_3a"jRo&C@H% 'ɉ_oX|Gd`/JFTmS_C)RF?̑]4t\;ʛBϞ$(^E*DhmDK[h]=v -\ ccշhIYVfe0$,%㍪>GV T++, <@.o5&4 DKldI~')%AVLumԮ1[%_jA##Fs;=*Sd%b6:A/r":Àd*j֤.Rjo1-Rs殛`2hۿ:he{ ؎;1#ϝyr"@kX*ҳVȣ9-Ajuc*W&0OǞփYoɁ2Hb5vb+()|cR.(P$,"+' 3% yk9ɥEH QDn0,KN)Ia )J*"&? 0ppr •wҦ'8R= *ӍAb"tf2p 7.cOA }'{q" @ԡY~;"Nǘ ؠ^\f1H _F=Vİ ٖSGyrצa$iᩓ@nBžglڭ,XUi,rRn$rcm6_7`1?]gjq:ĂOc :{fm Lo)FR| #Ő.Զpi h+DR ~K -Df1W?m| EEEVTS*T)M?7hzyZ!!BB~")q+Q)V"fWH2\ꐁđ[/v~36[{"\'=RӓHh4┻8E[9OtXlOi .'48(Ih6b?4d-6Lx"h]BGLJw$6nxՋgH_b;N:򮦖$[VmsڢnڬLe(`^CYmL||w]"G؄LD B- k$}!+79{jܼ h:mRA,!j ٣|ZeO4 Oʇ0_6] !o'0|`4KCn,8JV^BlM>OC-v/%EN\@M|i2 FTV#v"1)O*ȑNk<#ch뗓1QEWGm_Svi% 4/9 _SVF3(Ep)nDWe辚)lg1WV֨ 'Xsƾ?oIK:1Ĭ朕hp+B0G ]p@"I:hM!qrcRM؈~X=AV9DSSNMy'c@?c=cYVn v!-?(! pE=VuM;;m{!p|MV]ck]cvr˻qQ zs +'X6N=hK_C!4NZZnBd:8=G^ZOڭYZUN_s\OJ+(drЉIYbImM^&FB@:{`7dOâSIvI f P!p CklcuV21  xBPJ:; )Q+AT'̕El~QZ”k婼2=l4000]b3Uܖc?-i23PGy_ x2Mg>#*@\%r3dÁI RP,d]wٶ@Wk>J4F)aykD9R/gSyC`\qDr҉q1ϩs?eO4o'Yi٠ş{[Spآ]Ij5Os/ eP\^#a~G)`7N$6a0EǘAV A䋀C|Cr}] }ca(phFozNVvjOtZF}/' iqS>L,2BV`5CԧYo2օN:_.?k-1fJlYYďIKZcDї$k_8I.KɐmM.W]ޟ-ܠm.孥kM6m,fg?}5H;ZKӚf859<<)?ٲ b$Y_uIe(8$v cC'Y6"-_.`\Ay>:|PHx9d73D|Y ,0װ&-G\L*T{H F2|AaLQun@FIIBZ5? Ӓh1,$y%HwVّK^[@ȓ1ЏCBTiw'ۚGkk+\~.\(A}}.Ԝ^a(Zb2?5e8RMƣqUrGVLǎ:,ٕS'0 !r@y~@P{ @p|8xYn~F`/"Iu`tHCL7%Ӕ6)$nD Dn"/Bέтrj_j1d2y ]0Q`9wZҿ~sY)tU=e>TEH9lT!P2Nl:7_?)) sV#.v"$I Y.C{ɉXxҰ=M]' :+FZՇq~zKbC&}|t%Oo! vc ׵[yi~;c4 TVWO9/jϑGķ W8x)LV ]L`cNXK5t7RFy&ZaD(ze49@~!()02*d)%Y 4e\´{)GNXf 2ycW1y1r><EI#4? E&It ?cv}7]UP{SW@!8!lxhϚBɨ)BWK0d]wQrf1{iA-YmÎ8lxWO 3uBW cЅA#WoqF 31dxP꩷InL^9t>7\AI?kw>a#)K|(m_ Yh^ˁMvQ! \׈94DE*nClFvd6zՖ"% HzK;aW< ]#BWKc>kЛC/lyUc =nԐtf1ϖ'!as--~a|g6{!ٝDʃ5Svs{a v\)\;}!Cۦg8g[̻um j}69`3Ӑy"syU$m59@&9/*=B9OGi^Y2I&E[>h#6}/j΄jzƒEν6Wqz"ﱦݶT{!Y1PNK."sȉݡ9q8$=,>W㱁0eb [.+fx1T⒣)ؤ }FEk>&:Q[Ud*V~h8W%#Z< ci5a.V0_x9Ŏ::h C=PEbP흂?Ӳ~Ή(~_Bq`)Jt~hJ}Y=,CágƧm5"F:}+MH)'r2S8bAc/Md-ʕɴUPך7km5J)JEReq'15(P(8HA_9_臔3EKlkfF[ՕgY윯ږyhK}[yb VjQ>?g#!u|nJ-ouYM!x`b4hvUբz;oH]D۬Yp$CgOyʑ{Mt"s(, k5+[e2)uAI{3>p>@ {7aWM悵fv ?}!-YfOr-6A4xld+?4mu;v!gT'ҩ<"=+{)r<k%g 0`U7PsYmˌGK%lѹAD9 I`=gp.y.`*" D(-)_@|z|gr~:lˏ;RevW-USge5{oh\?;leEO91fog^+,Ѵjwhw|\%j3d@.}n~&PN1KG,' bC?cED9|,pu \fQ* ,Ɨ՛ ĸ%?NquK^ ɀtan;`>b#q0i;5xmb%dӉu8QȻmkXobvցœVJ5^M\Zs/ tݺvLN4bd x0^!rk:L h$̗ kV u"qk2r: V3tɑbGi=(s%z%,.2{u|}o8]ߣsfX;ϖr fʝ[?QVRPR9NiPHK8>b~c<d9Ǡ8c/=kd:_ ;"2Bqͥ2, l[߻bqO]*چg`kS\G)"Fl70*^_^X$*?%18SDYj*׭ib EŐ5Ek:7lSMs/&<[8 P!r;`8ܾ'g5{(6DM1`^#E0aհ$+lsOy7.EHq6ȧ<1|i^w/r+AK]Ɔ?2iogȰvfIqY׼6Kkl>PIBzw^:x30@FXx]'7v0DN+5 a~0Z*SvCpͮ'1嘯ؠl8+{b&f/$u2aWmccVe82`rhe7IV]EqME9)+rwov{~ Vf9kI 3111њ! `ʑĀ# 0PPY Y.NjX /QXx^yJoDKӂO` .[ɽIĕ/MVP?'gu"?RMI[mmI1wdDW~sn۷3gkNytx3@g|;=s:ؒ %6Pl.!&l1ʏ b06SV S#^@j.I[i sn]Wl#L]g~TWCODcN΄X#N~wQb{0+gxo/e`*-=;b yր#h7ٗ6YSҏ@d&[X<~N5+MYy.sVi֟?ƻ;(5QʀHyG-voS>Չ8Bcc6h?P Si׋#ֆwBG~5*kmoj6΀;Oh^yfM@cU6Y  rMHJE , Yq9 i-Ѽo\ r::"^I5ϫa@#HM.-< TRk ;RQ5^Zx51]#2MTwʐɠ&-@iD7]uAtٳ61H+0ňHE +:cϥ)`:zXt*Q wGԂ(݋d )+~9A@$21 X҂͎OZޝ +lr .?ԂI0XK9/s"l;ߋ(M_ |Wb;>Uۻa+}_{󠵬m'O3p6ްB;V½΂Tw ŧYAsɛ9dWDQWՐ.[n' ָ|wFLŝ#:2"hnȨIXFMDyS=\.1hXҬyPyt/zsI$ޔ v?k,uaTD1w7Cp{πV&z]As XhȚI/CDu®"]Fi"շr=K5H^7kUޡl4uShRm?X j),WWE;NlTچ r"3>F(_,/̴XSm+x1yDP5#|ۂmUnysZ%Ď6ARzn~` c k-r׀JVINL,d4aV"tLL&Nf GoƝ Ȍ#,R&|,T+=$C6.b+֘>R4[&3\~y"2 #)+|ϟ:gr>rG%i`6S%Eu'<0@A2?NtKrgpjk!2! GNE4\:yMۘ*㧽vZSY˜gY/nkf K&\V9k,I2Z& fm ÷٩igF[mGy]OFG jjN{\#ĖM<R>TjOة/=f/#a2-񟝁M:0JmԜD4 ,IKgg(;_'oWT}\P_AX54ʀibKeм#UKXMHE3[/cȰ:cz'x}$[oJ_vJ"`!%X!M2i/ZxIZu1{9{d\æ(he~k og$UVŘmQծΝ'>38 <$o`x^I3k |Z)Tb&}Γp F.~Kk]7i2|Ý/O2Wڏr= Skaߛ#\R O?W`q"1a{Ph#I;>j|mA ǼN2ߙC54d̄dh\5Zw92p6R5WLO \ZxhF}ovQ~C}e0I;5#/S6V{I{y֎AQ4?t-XClHDǢ>`kn+[I"k3'8W5,쀭DAxpz/c&ii)v?mCr ƀeۤ~JS[q`%M>okn,R;P ;etF wv 35Q˂ M#w?ZDsM;괍_8`,Bը ~`2Av;JW<I}[4wvkb#k.jNuhjmj6{Y>۶Z;[ԡN-ۨPrmb.xaE'z\CXKcՅ2{UtVDv9ǝд%QknDNP;=4kG8 Ė5xm^0ؽO)mO2dĢMA呑ZEetτmŬ/~a tMW HgLՓ]2566:7Eb2botӜ쮛 'ʱs.5wMK'PFN?s}WXz͑l5r\*!VpΒ㚴Xʲ~1~cOa>s+"PȻb'{\XTm`~$Q ÷೵aYzn ~ieVruZd n8`},jƮTƪl %ʑ@h-JZfeWmۡM Y+0Qr24xl/udUcۙ~Q[^!Z鄷ú&(gԮ&5곺k->qg[}OZs{Gl V?cU5ME&N80yq.6PcMɽ>߯g74MH-7vwu]+Y'#2'?X1I]-mB/ړ׋g+j?O/ҋ[ē19O.DP?}J[H7xۃxiQ rxM2rNrfv,op}u#7qU/JPvdQD`eD;>j]eT+y|O'V}q[,]H k5&ïHΟ k͝ow+7^Mk? 7w;2dֵL F1@G V l \ ^kAdzcTȣ 8^+ v~RyIe!EgsQ| ~M'r'boVz7+c&gB} XN$"ڀ;JɽPW؎4=0(]k/tЉ#QkO_oq{5>ca`ȧƬ IԊ(zJ'a [v';݆#_6f\νȚbE;v!h&oO^&;qGӦP}^Ͻq Jqn~;1d/ukly'lyjȜ`<0Tr&%bH⪠)j#7lɥzRwм}6}˲%p stI ?+ǫ1F7wsyzݝ r\i.(a 7[ ur߯9ɕ춲gڦ[a6ц:IVA'coiD)4>o63+4PFn \=Ѿ8~6-V%  hdž/ 57tZ۶k}YrB3娨Ѧi Znm8q֎9_<,~0 {#>duUeL6pJōwڶ"hUaFDj- u&Ec-63|=6kƚɺ<yAcv@^(uqnLsi:tW03Mw v{u@b{}]5dsڷu1) S,KE61YqJ(X$x e nn6ֶDVS3VWC4xyc_ӹ>OB_w(";ܛn*HeоoGjD>;L^9(U'$azˈeɉI͞o!`S3E?қXz1l% ځ[ێr4nID#D2`:7^ֱu_=v79.^^IN!)ak xT-mQN ƉVh/T q$Scavbg/En 9F !:S/@47_0&/ Ym ga&ۏN%B n 2^-+o>'Kx)z:4u~ےIǠ]ʂwuDaMfRݰk9{zN\M3xPBͩ81N[Lv]B# Id\`ڱi79F$"P0ҪP3`خ^d`KbɢEQRL,&w*Ԥ5Atsj {}{zn뾯g _ި噿5wT^&TMcnO _:.5:<|rs6~A ?9,j:WBFN1!-j>kT֡}­4 ᴡ)ѐsN446oWk/r&ɐ`p8^#}A2Mф]G5KO Z=ܳ( q\Sit0%@PzǚX?e08=E}mя.GPOg'_\/# qflJOىJd,5gӠ5U2/P1v}xؚP zEiuzUAI,zoʶ͡Ǔ\ \Ec.#;jCWmd!Kr9 !=d=K`YM-їɥ k W3,r-X+Ѭ4ats s'ݛ:q}km[ߧm8VaK +#"4'4Fp!",_A+uatg3j̨{2?9؝zR[CfߎpI k4 B ww XkĨ\HƩvn?˛aߕ̕+C@CLѷhk [{lz#=O/ ݐX1$?AT W/7籼(WGEAcu&.G>_|nu=ƽ EfA> D?S.rDE@Q`TVY3ee5kG((ݯ>B}]dłqۑs/a3߁XC,h6q%{ {A{GW?^{ߚ`?.7h 0g3+q#_ɈlW*icuo6gb>?ᝑQ~?JBEmJZ%(DAĠm:΢}$R̚MT[G-Ngc9hxi K1m#/}xWxlVDD+R;e'cèms~&{q=BM'N@w2SwnyM^}E7[k[v{m3bIUf)l 5b9 lWefr'5-$nR=s2 ym'_6נ,u~{K=Ðn'%{g\jΫΝCU|庩]0uP-@hjyvHVn.;4:έow79} |t?KkdlXlVI]}Z뤃.rSY֖Sԇb]ZO53^DKZ DZ scڠw,塬jɮmMv>eE;X2FtxqIdz>a96?^X1A=vۻҐE,etON@Zm7Υlo訛0 Sڋw,5%:r~h?=@ (4 BBmDJ?ZaKF}#|{X|UJj~bGv-TZڰ}KvbblyO4q\]CJyz͝lr:Hd$t߯gkX7wzWU]]F2,NhB- Z&IZ,@FՖFzgK>+r@IQ9f{O;jKiu7.3i.]  3/H=j:'Ƀ)O,hjm/fYJ(2J],V$o_WT\cWO)n9{M}/~pKG}+Wue'*dSEDS'Y>TaCGa>UYVY;':{; '+N1مo`wY(x<Xoto;jOl׵yG V}Φd蕡2:g-E붵 ?f1_D&cTQm@ɃI_\΋w!./gZ( xbU6yDv;7~*w2O}V[pC Va6-,9+rvg fˎo8Z"o''}la]]T{2fnN:oٶ>5;q ښ)F|wq: 1V[N-V$:GrvŇ :@baNaǟ1p)>͢v3ELDjΜ8,oɯw]{}/'{rHbyfR)u05R,jf(LesֈK"[fj@x&CmqǣƧnq?y[?S{Ӿz^Vu.u/``9oԮl#pEО\l*-~jdͶ_En_ue/#=Rd A{r۟m#8Gs8U5P5 cmgByy{yS 'np8y$"J|iй}+w%kCRp0a֏K#X Ls}vޑ{pCal /R ,)az50xݛŸaW׏uDgR+h]۷}պ-IXg `z`>KŰ5SXezrШhf,"Oűz,Bs'IzMW1G\=.^GNhuݻKj6fcϠ'"ѳƻ {B9(cfܠcte#Կ,Wذ4 )P]:Kd)::I9om`#ϙگ|-ZL7--dP WL"dA؆88y6h_H,_@hRPQ}F#"I@@z*XrǴ΍;ݱ^uq6\w[Ph) 9$E9Afm觃v6KWncS(lYltnڨt5{vz|%0}Po͑ێ})k'Y+;ؤo/5B<7 &3|U2 SD[6 ah/^,:[}]|Zb]׋^[6#6x#5wmozy8@#iku]AOd IB z wd=˚rv=غn.d e `jʈib oL:63ݳ  @(\.[OTKTٕʇU]()P )H v̂5sK !QEJ%=ݾg{s=qWL[ jfҰR°:ĆCN$:&_A1NɅIȥ ®ls4_`tu9|zK JTkla>>d1`u`~w*ÇY\̩*d@bWQS,5uL/U"sB|oٝՌqЌ)_DGo&(B0w:>=Si6sO~mY{(5Ji2S=U_.Dt6 g>ZANl XC2XSXC'_[QuJ9 bQD2a՜W ٔ޸_X;/ӻ'wVfuRsUY:5MS;uKp[m) g)2WM\v{"9{ksؔ &fﱛ;DŽ 9S77]d!yPQh[2'௪;5&gjh\bp{Ӛj#s5׿p6ww:WvS||00zP^d@ȇn>ceXXvv6{]99gxm9%*j dXw V([6w]>gcsq>~cqmaܧƇixˍݼI Mj%F$n%/]UדC,w5J`_"<[ & 9SE|LSvM e+=m+!q7md>hWĐlȝ"P{iH͂XQŦ͏}yVbBG&m3A0^ ڸaIFNO&OW25!3P#vl\iڟNޘx6J]SFzJq>xcT5QZ>Z$49D-‰'`H)fr>7/:vr+ijK7r[,d,3cìHWLl}9{~Gݭx Bnsvo 20˧;,HQRxqlpa^/YY6k>,q)Cr?YJ@ȡ'~8 qk;qZ0%fq߸66lbT*lx>փ˺ɛh=FL~?XN5>%>O)69$ĸA>{i3[sםcw 6r}q}ֆ)O Z]K{l޸=%7O?zO:w,BT!2u]O4@zqMP:sD˸=HC1UM,,/L觾88s3SN7@jf3O)KFFg`譊K&~~tIz)ľ 4oycmh5mޘvE(Jܞtț5_rDC2iB_KZ_x]O9xGV4b4!$*[N=xgRYHV;\|yRoS8Ϋ ?e!ǝxsJZ~Z?=v̡?ڵk㍙5heBj7w@ٸ[ Jdfǰyc13wik7 4q4s Zc}O6*Sx]J+H>E;@3E3YC2P^G]r!ٳ.`B b\[2\z%M"銴iߥS/3~ -TخWv];+I&}R1B'/ŘeB#xd' 5P;z*wV#TVM26dk:Cu&$iS;Fkuҧ`HO es׾z"y YHn{}ַu[XywiW>o{L) $AZpipRK1-S+SO."0C)lvsQו d"=ro1F cG 6ǫĤarϦjʮ L"H3x3$IeUJH%//C+Bp 穣O+4D.ŬA˕hRW~p ^986 ؘ[bj$˃$2tMNcqQVyT+a Bmo 耻8$y%f" lEP1;J95ͳS`֮2455gԱp 6_2[n"a8#AX @CK>RRźA\@YƏ26x+.2z!F!xɽI>f 5u[> #0_b*2ڰyw+3vL-GY -Gmy}(91/&=%Pk"F贠y (Yjc[`%X eutYYP}|ZS`KDڜgS.bేGS}SѱCyQt)C,&2@n_}yQ7i=D@31{b[nr P,Vu3|ij+A  /$rSlDBƚv^FRp˔ү+mu ՇbdIk0_yaW /8NB ]AܛfIc7h7  XBj`F;'7 lRV:NgmrҰؓ B *kgK[M.jX EGn 4`oW3Z $p}d459$j5ۼ5e*ê@, ,XfC{tR zC4DXux#_ %!PiB+7W{mI7!}֯aV_@ [ʄކP&^..*vPbYW\?@A~,tjJ#{tI '05fGc$Yuo-!a61A`UT+!Rkk,C42<Gb 5f ,ms*L)]/ )=RDN})0SbXDWT% y="RGol\W1I}r-F6dn1اi I& ^ G|O~XM] & %7 >¹@BG,ZOBjhK`ZK ˻b64f,v3a4g#Iho06lؠxh ݺˆG+YJ$H ƚG*[Pb*6ùdhH+2|Wb,3eɦ *F(fܘ3`ч`s_nPy@@yLFLٽ{ebn.0${PbS'qDAi,)U ?oBsnv F_sz["á.-!ux ?\4yU@pNЪ7SAnA_&TOfT]eQ͒G7˺ԸApVjT\ V"9-oТ)qq+<{Os/ {ΕԩkA_{꿶uhsg5raSWF=*O6 \|?me ANb}.OURۖ-ܴmQKZ0l]sx/%ׯBz 搯塳z[ Vb˔ mq g`j *|mgG_:Wʨc=I0B܀DsK8 -X>ADtW YN|Gvd«QXFo Пn> rj) K ;p]@jX3`M}=&D$LLЙ8Y"`MHz.@j*xV-J-x QM^Me(„oei }ƨʍ 2Ŗ{'FF1x8ǒȭ9 }][Gz!OJ͔Jؾ><J.N*Ur;C%fUhuؼ^M @$[7 *ڡkz^ڛT]t;bs MygLLY1{6:g]Sz>GkO6+wd r-&M5fhy T?p|$0`PV[[,PETJ~6L!z*쇯􎑷ҋt ns^.f7Źn42OъoPSf%,FGx 놟ͶɱjVb;o瞲1y%zc:կ8I !:#bCY5CB2 BKq9^L[(0%qddX#2%ν{:3^ _hxbZ'ΝU'mf=xyȉ?Odb!5Tba@ γh7-8܉^,@ki_ǖb0ȧm.4+7 ̛=uGvmac*CLR7~k of\A$[S?C533z=D<_ zҦn &3~Oaݤev4ձ58!R,pNM 7`D k̀hYЈ C{Efj45$[׫E UjkoqB: ЫlӱnW"BQP"UZl vx#fA`8N?e :!!Z 9XMH{0J.$b!-D6{ߟ),4%"_;._PӞZ-m[?]>ݡTaSzvNewi@ u9Tə2M ]'N*!ޝ=*Շjkh1pèy$_ 0xgYm"]hK500͗Sǂ4!Iŏ"Nd\ңE=Gk4dܦѩMJ'4]N7D=q$&$1(yM h괒yg cSC[9r}>Z?ww"e*ޗA'2sjNT6mr ~AšЊ]UT`ڎlޙC't݋jRE`U^Ð6˺uvJ9r 4Muw2~lCգ'F `EW6VF3ɠﳐ<9[|sH$4_[GU].*R2lYtrA5LRd۵S۷o-KDr+@|&htݠ"L';%dro- FXk]Z/3tjhYVmmU8\ץ@S|j|h.ְG`Dq iqqGC{L8ziOViss숊s^0ŗf!C{1ۛյ}l゚f}#=qg-L< + r2+Y>,FЋ|ߛ#T׫c{|LvשFO܆ƻQݣN-d !' Lk+|ͱiRm>G+2ר⑄^OD &]I0 -= * "{^gW|mO>1efdp: =qJgN3h^ hH](%h&ǀ 2 b?Fr=2C9yQЈVT'0xL隇E[Gq&!6V_dAGq>noRoԍ!;yڬ< дP)@$3&/n\ĈF.Ѭ`fZPa56:UZU]Ѝ?WVZi0t. RIGbM+4~u;xd(ۑ@Q}>sW|) S i%yxFug4ƴ&R;AP A5W=6jiTz6dg<ۥ̞=,hG, R7>ggV>W8jL(Nl֘cqwGl ,l3ck/ O<$, Ec|"RJ-p@}zhTZyܾKZi+}"5'HZȁ2@*=Dzs$5u!;99[4\{WQ(byz:F%͉t7t#c ,o")=諸I#S ʝN&e /65V%}O8 ~NͬJ9.2[!DP~FNP❫W&Nz6]%FX.}8j<fk s" u W}};CSű:gVuP$^}1uoֿC[PR1HAX!'e}’4Y!^K)L /]s+muql Q6P9:1G %@J~)^v+#E/Jȋnb .i6Q-i5}w;`)z^wM{ȕ.0f9pskߐRQZ\@%~Lpΐ_JDɄ+~-QFmY P啡_K̀Wq׷BE#˔F`Z8D~ ^GBָ:N{@FQIs^VI.-B\[gѽڰ OX3PV5Z8?Ɨ" Rr$ʪjRe@Me2w葇p({>3`l 7 Ƥrƚq!.,їR ,f GÌbe8E68D6J^z ]= -=?R.JcJl?Vpy: Y aG|\ G$n~P  >鏞}AyG (AcsQڶfrD%LP|]SGu4Z| %]O״V%ϢShp-[4 ;'Wemeʔn )2hļraCSFGh킧gϙwVX3`ǣO>IVMdk8x`9(jʥ.4ͳ )ɑBV/>޽;S`L"űfj߾]q'QY (}"l)07b4fQÇfY83g֑#Go> ب._j)UWE7@ <5hD jj?dd X{3Ew36#~OÔ 5zgt #2[793̙^OYo2뷴R&*8kq| 2CًZXS?@6<*qEPZ+lQd/b+i+*30'-z`kO8x,D$ rk@s䖋O<ĸ#ԁ/}K'S g83a `IENDB`image003.png000066400000000000000000000141721300200146000363070ustar00rootroot00000000000000connectome-workbench-1.2.3+git41-gc4c6c90/src/Resources/GuiResources/HelpFiles/Annotations/Annotations.fldPNG  IHDRE`gAMABO iCCPDisplayxTǿI/@ޑ^B't *! JbCE\ED]Rd-(E@,*Xxy{y;w7r-2 D!nwT؜Lkp?p@[&ӳᅥ, F9IC( DֵVf ey*iN3?˽3=a!(f uF6'CAW \e&Μ$6\ҧ(2'fKfى]L0hܙN1'=fl91[4/?6Ǣ|^gd>/ѐ(ks"88Ǚ)~=H"ќ 1-s^=!+)g^[D) %,7Laj-gfJB?9Nf p| <؀|YUYӂӅEĤ,+zbx cj̰03`;wwgD'rR`^Eq}tW~#e0?X@BϵIAJ:A yBP ACkP!T C:4t Aah z }ê.]a?8 ^'p*(_@BFb!H$ "d=R"UH#Ҏt#!0  p0rL-ӅƌccXFa+Rl5{ ;~pt烋%p>n+N <şGd:E!z9B?a(C!:\jbx8J$ɒHN0R2i#HDzHzG&5%d>9\F>NB&Q )XRCܣRT&5EA^>~IJR*Z^Ku]KHJ)J(+.ÖY/S!sZfPfB&k.$&]^s9W.OEBӢ8ʹ#KQyYP|B* CtKgSE;/ T.-ضqA <&/J %O]JJ1ʆKW*PjBDž O,Q9rCeBUM[UO+5S-YDژ:MY^~^CHe1*>bC=z᚛44iJ:Ƶյj7h!$UUK/GA>UE?CJ `A!lhmdXax6173 M(&&& &ætSMi/YkQfffG˙o2o7kahmI`fȊgu5:zu7[M͘mm]v+X{7 g?;8d9p1űbżG8i:9 93rrpaT1%(&e*52)vZ 'HtJ C{2E~L(sYf[R[Uzw8#GZ[f6ζ^n7={X[I&zSqp\|/q9wS:*i-P֤֡趾Ӿ;ۛ5ƙ gΑ坛:s~CB⅑.F]ݵߥ+._v>ʙWO_zz ͿYcrf[}o{s.nyܺ|u@@ߝ;wcr>zrb/{T'={ F/9.}l5*+OAa:5A;9K>w! 4dhZ_7-;3:M <4ڭntK<sЪ^xzx@2츁 g}_HY=i^x{W?=3g8u]jyC,vnoa+_N!xX!,;ppV?F HRr8SHrqJ%R$i"I%o2Ii,BRF9:4r|6̱V&Kj<@A}ďrw g{1Z!KL4g;N44Hu9:-oR^Pp' ±8I_m6R?Ŭ2 a+QVL/av5 9~ 8)Ĩ)EC9}i5py`lLzu`2bk>}(:!vͪ{V@ `$ $3/{iC~ _@<C5$ |v(Mrl۴ 0kͦ$#Ǚvr|H&w"G79.Ǟ/bH[850 {&Ļ=2bkֺȕ hcD< fPkъxٿ0a^! >bue>ڥmDduqAص W#Bg^.c9MyiU~"o~Hk0^&?%Tcf|ˈ=K1BhNvF˪,Xp32cIk=QTu[зw"ߑ$`rwx6F>i2$Bx,EŴxHi8vh>2 F)v&oGʎY"p~~gHEcPR ӓLMy. 7@QTAҡ[Wu($AR20p /MOhRCt7F3'D/B4PB zk3"TNϊWT'2"h#O >HƤ~s073L8މeM _=1;xVܥ$8?#s/Ik#G#Y!P؜wDl&G#$r(`r}|K]]Cϕ :IT)IC==ihhSi [PjaMͲT ݸqq!ߠ!, 0 a^Y!,~/]h 0as;ߵrPL>ݮWUU *>H9Z[[ mىuqq108=diM|g刎TPPVVӃnj͍9<<<c,>.Ǹ. * >>>ŰaØR.tttΝ;d||3g~sPA#F0H r17~7"pN85YcUz\9+6Q ctDԒPˢ[mVl*j>_<3y5Řu)ԝX*G݋1Yo=$.̖ǧ-YLu^Y!~ŋfrU[X ;' G~~t/\aمd>>ᆆrFEEq8q"F娼ہrTe-ܿo){7odcԩ2e jkkk???5D^>mkZZy.CWwUt"{M,rf2qPV?<|p̟?TA :lCa>m2O)lim^K˳ o)[yg~{#b|oFQQ3gz}sl3׫t-22۸vc50r~,Ͷ>G>uT4k Gb֬Y ՗3. .,kyր7R{oT!B*?`yP\\l?o<:!ʏvVf7[ӹn!H_08Ӊ-[}{J~x7>t@ө^ Xk5bQRRbP0?ӾtWss3y""ݿP(Š[٦g}+W޽{\[^" {. LQ7B|U S$$$0|ĉ^"W^u4V3g>JZ C+ΏRBPf!(C+]gmaGk3v\IENDB`image004.png000066400000000000000000000075151300200146000363130ustar00rootroot00000000000000connectome-workbench-1.2.3+git41-gc4c6c90/src/Resources/GuiResources/HelpFiles/Annotations/Annotations.fldPNG  IHDR!FBgAMABO iCCPDisplayxTǿI/@ޑ^B't *! JbCE\ED]Rd-(E@,*Xxy{y;w7r-2 D!nwT؜Lkp?p@[&ӳᅥ, F9IC( DֵVf ey*iN3?˽3=a!(f uF6'CAW \e&Μ$6\ҧ(2'fKfى]L0hܙN1'=fl91[4/?6Ǣ|^gd>/ѐ(ks"88Ǚ)~=H"ќ 1-s^=!+)g^[D) %,7Laj-gfJB?9Nf p| <؀|YUYӂӅEĤ,+zbx cj̰03`;wwgD'rR`^Eq}tW~#e0?X@BϵIAJ:A yBP ACkP!T C:4t Aah z }ê.]a?8 ^'p*(_@BFb!H$ "d=R"UH#Ҏt#!0  p0rL-ӅƌccXFa+Rl5{ ;~pt烋%p>n+N <şGd:E!z9B?a(C!:\jbx8J$ɒHN0R2i#HDzHzG&5%d>9\F>NB&Q )XRCܣRT&5EA^>~IJR*Z^Ku]KHJ)J(+.ÖY/S!sZfPfB&k.$&]^s9W.OEBӢ8ʹ#KQyYP|B* CtKgSE;/ T.-ضqA <&/J %O]JJ1ʆKW*PjBDž O,Q9rCeBUM[UO+5S-YDژ:MY^~^CHe1*>bC=z᚛44iJ:Ƶյj7h!$UUK/GA>UE?CJ `A!lhmdXax6173 M(&&& &ætSMi/YkQfffG˙o2o7kahmI`fȊgu5:zu7[M͘mm]v+X{7 g?;8d9p1űbżG8i:9 93rrpaT1%(&e*52)vZ 'HtJ C{2E~L(sYf[R[Uzw8#GZ[f6ζ^n7={X[I&zSqp\|/q9wS:*i-P֤֡趾Ӿ;ۛ5ƙ gΑ坛:s~CB⅑.F]ݵߥ+._v>ʙWO_zz ͿYcrf[}o{s.nyܺ|u@@ߝ;wcr>zrb}CZHY4+݊)9F)Z\ݝyww<V+ u[>ӌCJXd ^qJ tM0"Oo&YГFrjm }92"'WKtz3̿>qpbAXjtĈx O8H)Bf8jJf5x44sėgbhp[xԆ'qIWb%ߺ ;(f `Ip]Ǭ.ۙ g$Y &o>ԁF}zrއ}gq^*qM___ױ1f?K.ǡhJ% Bk0aAD$! J#DbNv $hd< * ځ%1SSSlZ^^Nm3>A4t:'SCA@z$2n=pe)搬~Qu9!I d I_Y)٭h$6?iwYIAM{{2B} [mx<ʒ0͛N *cH$!Bǘy|oh.4!b̎*$nkkIoMFV M2 ZPSQQFzRGڶ,ƤXDm'ߺHibۗeٗi}Ee{g!&@.ԷmӠxY\\g]Uh"OKbxxX\D}}"$8!΢@?7Gx+J&JIENDB`image005.png000066400000000000000000000145401300200146000363100ustar00rootroot00000000000000connectome-workbench-1.2.3+git41-gc4c6c90/src/Resources/GuiResources/HelpFiles/Annotations/Annotations.fldPNG  IHDRvBL"gAMABO iCCPDisplayxTǿI/@ޑ^B't *! JbCE\ED]Rd-(E@,*Xxy{y;w7r-2 D!nwT؜Lkp?p@[&ӳᅥ, F9IC( DֵVf ey*iN3?˽3=a!(f uF6'CAW \e&Μ$6\ҧ(2'fKfى]L0hܙN1'=fl91[4/?6Ǣ|^gd>/ѐ(ks"88Ǚ)~=H"ќ 1-s^=!+)g^[D) %,7Laj-gfJB?9Nf p| <؀|YUYӂӅEĤ,+zbx cj̰03`;wwgD'rR`^Eq}tW~#e0?X@BϵIAJ:A yBP ACkP!T C:4t Aah z }ê.]a?8 ^'p*(_@BFb!H$ "d=R"UH#Ҏt#!0  p0rL-ӅƌccXFa+Rl5{ ;~pt烋%p>n+N <şGd:E!z9B?a(C!:\jbx8J$ɒHN0R2i#HDzHzG&5%d>9\F>NB&Q )XRCܣRT&5EA^>~IJR*Z^Ku]KHJ)J(+.ÖY/S!sZfPfB&k.$&]^s9W.OEBӢ8ʹ#KQyYP|B* CtKgSE;/ T.-ضqA <&/J %O]JJ1ʆKW*PjBDž O,Q9rCeBUM[UO+5S-YDژ:MY^~^CHe1*>bC=z᚛44iJ:Ƶյj7h!$UUK/GA>UE?CJ `A!lhmdXax6173 M(&&& &ætSMi/YkQfffG˙o2o7kahmI`fȊgu5:zu7[M͘mm]v+X{7 g?;8d9p1űbżG8i:9 93rrpaT1%(&e*52)vZ 'HtJ C{2E~L(sYf[R[Uzw8#GZ[f6ζ^n7={X[I&zSqp\|/q9wS:*i-P֤֡趾Ӿ;ۛ5ƙ gΑ坛:s~CB⅑.F]ݵߥ+._v>ʙWO_zz ͿYcrf[}o{s.nyܺ|u@@ߝ;wcr>zrb,F- ;!|켂7翆U𖳕{h-{N— ud"ĵģӠRg)6є cݡFggg=VLRZ`oԛ؛u '׊J{pՎq_)b xh6㻤p[ Y|(>҈}8aXeZm77~d7|N&)x?_6AC|YĿg#HKVp\A9b匌ypX!hW FsiA‰rp~'9@NX '"s{*dBfCt_2r~֌ʴdJpY|U B2'" gp4:mp2;ݹRdBAKh ?Ax܄M K#_/yᕕnX2!|V|gM¯Cbm1wwgbю|"i;R˗/k3 :Q_O24$!&``lnm100\7N4zXLw\r޽^z"W|ڴiiQXX+cuzbu&mgk k~1.uF%Z%Enb,|cp(K) Ģ#ϱ_3yY7"mtFōWr-*W .pA1]#. v/(l?W?NBlfvLU.؍$ȅq$V9Z _ lAQ](솰33}ĵ(D!ݓ#ˮ##0 o RDzF^N~58 ǩ MMjd\pvdEbH eEq84N'5qq(B2=qH-<@AD 9M(<[ODP-((` 4;HN^,,"N2ω&.'Vd.=~I Ǘ:ylEA&@eC,Z9n,+I`ʇx]BRْ65…= -Af]Xi""Nh1jb ,#ٌH8-! GTi9j 7ah-FK>8Uۿ>(Xs(-]:iRpT]*z*agkh߆mR#8 W-(37,O ۬aFK{|#¤WhڏJ)X;7h3Lb)Wd{Y0f'y$@`}S~Zs. J-\B-ҧ^_"̝ڝZp ~D#xV8֝Yfz$ [__Mi vM5 j-,z)#G}}YMxk:2ЖLbdv6vk#k3o(((6O:vU =ځ6O2eʘ)@(Ԛ&[&kJ\XPhJ'Mk=E0,k)Lj jKlii%I/;œxXBr)nvn 8^V9N^8C6f,l@f&r1I,EmA, NB2i }NA0ғޓ>v / l<YQ=9!y8|-Ϩ5`{EV\y5但Њja%69 Qw4;\pTiw*dl~Ĉ'WpN+y!^vE ̅ur wI͝i /*'}iӆރa~Ek~`l h;j5v[rf yNjmڥQ[3bsss\zUSh5Zsq5x9ϴ0gf?r(**ݻw5V#K0#P&`ZW+8k\X###EsO͓*d5-x޾fk.p ;,%iIVwh``CCQjdKa~?M# I,y/NKK#˜/b%Jxn-e RYkߑ*$O(rC!%Oyqfg9I1Ly)pBJ C yBP(AAP1TUAF4t Cah z }Q0`=x l~p8N3\Wµ _`1 D ECiLPv(wT*6PZT ՋQX4MG>4ހ.AWm0zCb1&&b*0SK(aXl 6[݇mva#I39p,\www7'5x/|,WS.DJ QQOt"%#;E'x)-}|VDm;rn  u::t vI{t{u?Emk{od@1p105k53L1gx66J26i /,__\xȄljml2lJ377m7}DgI쒝Kz|76K5;d\<߼ۢ%re+c+~{Tllm6-6c:q5Cvvv%vW1nvqr8𗣉cc㋥KK-qrb9t;ӝrh\j]2F=㹫knfnBSn׻wy<=<<<#<\ّ%"psvuȜeWWXcfۚ^?Ee^nӺn6oި`hw^&⦔M@ `dBBaVǭ~@o嶽۾qW-a\'l+)ݿcpΆ2ٲܲ]Ew}ªqe^;~JvnQVqg-{Va<ύBzmTJfQؑGn8brZ| {K/w;rRd)ꩢ6mMD{R#NSz gK>{~K5~!H/ we{]{_qr쮵_vƩ߬;gvf-[Kv}ǝwwF Z>$ǹ~7L={yTXq'*4ȫ?2:Z/Όyz+?eym_nLDO~[NV''|H0ҧv{D}y>~MMO XB֬@! '$0o&Jـ<,9=sBH$+@8gd&XZ"#֤bz q|jV}@ׇ9_>XĿiйҞy?|OPiTXtXML:com.adobe.xmp 152 86 J IDATx]\RQ0A hlFK$%bEأ-h`K؍h b "*&ivoCQotٙ7o~ys`(rJ 65`isdVkm`f lXmW]y|PoX w,޽{fUvgQ:FFF?~'O //PszB-it69QMۗF}L嘎:ge#` M6Eʕ3ǟQĄ?p@%eɳ4$Ezɨ 0\˗G˖-eM7GuQ $ QPp*mT(OM$˕22meI*1|RQiő7Z{ah*O2$l 1]r}=s"@` MGX*DpyVդrwS9f p•C E#|X+lR,vltz|tl5B4sx=X*LRgj8 c-&ALt`o6UnQc(ve2c:].`di(}[HnjW9ø` &Klmd!i)QIJAc쵲—QF1i)]V!̘o0L?XSm7S30cU,tM@(qPj[͡9&"D!Qa@p"P%(8X||ƶmKЌV%#')K|^;{I_sN #@ӤF_iE 4I|x4v $8q)TJ\|@̏;0 qR<~~g1pb"`tTm V6q4T3آTv>_Dۻ~ P'$|)4xÿ́pmu][a8w.}m/Glٹt[ `W0'<ιBQ KyM&z, ;NNE_0|N!Bvd{\EA=m-Lb3~ } a5۠mrE;ꍱ0\S&¼h>=Qј_G/kYөiCi,ۇ.nhdlpE/W 0)eJkM6s+ Ll4R\=L 0 6Xlm_UE=<{#eZ7ԭA *R٪8Y MiA$yixl{|gP~|]%;-.׹} %+uɕ4Ìad 3RGXЇ\sL.H-- u< wiFǶ`aAdldСv9]?Հpz7??'AMe'L\ !ҙb*N2 &B\ 'sjlp?quW&r$ᘟo ȏ9eusm|İ,Xo:2qJLbRqdt<}i/s_0{$HcN? mj#mc~&މ=ۢ1~M 5Z joUe__vdȤ/ꄾSuՓx~EhT TB)X~ "5SZ4O-|̋C-2O'{l0P@TĴ4[iE4'J ,^aAG_ }<*2(F fתF(i,9Mn#99?3OϨH#OV8WNp-C^M &#Md!!v+sh=bjt-|̯Rnb}{5=9+j2~9".<Т\RLp-6]3K'] Ė[%גs 22'kA2ͱ\yHq%g7xè*kcic1`KI\$FŖw$BE]ő"p ~BpAk<,~ݫ$VP7y+-IJh֩+zv.$QLrZeFU`_BO\,%E*UQٳ',uè`"ybۑ Nٟ-K2MVyv0+ksPZ Ǻ6NjЄ 9 d^dɘ<sMQEgޏGo?Q}]'%#Z Oi>1jK1S[Xi.b/x{|E$2?f>`<1)# >旇'Lu.ob4Ee %7[C|^NVUԈerA%ա֡9ԯ]aEQݻ 4FB$L(0MOD>1Ӱio ROmQ#mx(R &LꎦV4jO7Ť Guj;ڃCriӔXp,%Y%و Ę1c0x`r ކJxpqqQlF…_MtW Ja<{FL> qz^\C|=y" 1qyޓ+ZTژy,MÞ`LD޼?Ac^B(v% se:Plfw Q1eҝ0`AȒ驥qr6ew$aܔ=\*12Dw.Wr; Xj7n,4A֢E ԩSGornȧ_9L"e:Os{4|;_ Ij(92!NSd ) 9Y8r)x"BG#'mO.o EW}'wxW`JN׻VP{lײa| ǑjQ|Ըz&u< KP;]uGg.HTJ<'׫cYJ]wy{Q_bTV O%Gٺl[Tژ`%{'/H6MEj>*֒=&Mۧ:7'O,MgϞƾM+nb7eU]iHMMš>xr BZ4i)G2A[׉|Cd,”!qd4xD*ZRLRiV5v쿪մ$|;|.VMn> {Ĕ;J>çB./od\c~ ^p^ (.'z5ت,&g^L6y*^TFKwCO=|HtCpYk$`Wnd^EܗOLN+LV"]i{qilY-'@++YZVaP#~%^r ]D;VQ#gzHBe%)׳\eOyyOD1[b\ `Up!Cq m0w"=C"Qڿp ޯYezJ3&wqJv?d7[%o@Bgɻs= =bk VO=o%t};PzMA9qt>`L˞7 8_as.w|kz594t/f]}&`^*i|, <,Ν;'ͳt2镶kptA^f +MiD L 8PЉ1nau!20ަ8$mS;k9oNju?Śxpй:n邐#̶St]viYWIWS*6l Lyk*ݫ۩lW?H1O^Z3d iL%qri2v=*9&->. 'GMe!4ѣ5)֮] >i C3ycK@ L@ O)sRX9D#p|>t+},\ѣT'((6Cn e>/zj $ yؐ1.E/HPܹcT_`3zSXs>1Js? ?:x=#m27֭[}E_ʝaA+3?L X|z|^2KYk1_)&N e1k%3O{̃ ^7n(W[^=*UFV3=ЪU+u$"<(u0P*VZLCWy4 t0e`aEyjf*}-|mfO̔O 0|SSjYk:J L(S7<6Cׯ1l]f{Ƕ"J]|>.,S>5i>.f%>`3X +S+[O5M0}k /C,¼1{9j!6[&< _vuL̫;qVX} Ƹ+bVHmǡj_hSwfCEf<:ǜh)!p;|IDCKtZd0w} 9Jh b$C5X $p:Bn=ڠ Oy`6ґr:kskfs)2C/D.?ahd_ G[# 8yNTňAyVT\񳐢P#mDym%yJ4rƦnRmPS1lU &{hYD2OaIn.ԑqAc!̩@ȥݣ7.J٩4hH.(wH_# n"nrr'[N{hvӪc"/Lۀ4qXRӵ[RDE FNt9ˆv!}sԭ"3ed%a8h zoYqEy;SPU>vҰȍ]wn؎:1)K\u~^ِF_ͣ'"**h:/O`HX:W|X5TjKCՌi5WA<%A!2U~H?'amZcѲÝyχ{pB 3p4,!h^S{o7Dw#(?h+:*f4 յ)z5B෻`A1 8XЃ!{D4Z*iDž/QRnx(OlR9 g-8UpfK~Ae0cZ)'W#^iw0wCmAtS (1)'E>`~?r^6AU#Ա#Rgǿ\@c(,AfG43R") VDjI`Iqk}0hܙN1'=fl91[4/?6Ǣ|^gd>/ѐ(ks"88Ǚ)~=H"ќ 1-s^=!+)g^[D) %,7Laj-gfJB?9Nf p| <؀|YUYӂӅEĤ,+zbx cj̰03`;wwgD'rR`^Eq}tW~#e0?X@BϵIAJ:A yBP ACkP!T C:4t Aah z }ê.]a?8 ^'p*(_@BFb!H$ "d=R"UH#Ҏt#!0  p0rL-ӅƌccXFa+Rl5{ ;~pt烋%p>n+N <şGd:E!z9B?a(C!:\jbx8J$ɒHN0R2i#HDzHzG&5%d>9\F>NB&Q )XRCܣRT&5EA^>~IJR*Z^Ku]KHJ)J(+.ÖY/S!sZfPfB&k.$&]^s9W.OEBӢ8ʹ#KQyYP|B* CtKgSE;/ T.-ضqA <&/J %O]JJ1ʆKW*PjBDž O,Q9rCeBUM[UO+5S-YDژ:MY^~^CHe1*>bC=z᚛44iJ:Ƶյj7h!$UUK/GA>UE?CJ `A!lhmdXax6173 M(&&& &ætSMi/YkQfffG˙o2o7kahmI`fȊgu5:zu7[M͘mm]v+X{7 g?;8d9p1űbżG8i:9 93rrpaT1%(&e*52)vZ 'HtJ C{2E~L(sYf[R[Uzw8#GZ[f6ζ^n7={X[I&zSqp\|/q9wS:*i-P֤֡趾Ӿ;ۛ5ƙ gΑ坛:s~CB⅑.F]ݵߥ+._v>ʙWO_zz ͿYcrf[}o{s.nyܺ|u@@ߝ;wcr>zrb__-9J |_WW^];CsS?K 8t?C)RS^.oz=xQ&ۍ=⿯t*yxGɝqa,))o!., L)eH-&yțb͍03/PMr R $V+oDݼt! 躑CǏRC%ր}q#-S,a(F+CFAtIaLyXƽ[C>;-eIZ\UdH ԡ7jq8V8^mftPDu(k,~GNyPM$+\9ڻ I{&X9#'?FjZ&r.AKU0G#(#`!;rҐXlEzo I3YR->Xh =Eţzdjⱥn2i`RY դ,bm SYrL),xey2by A\[I^I0mrR0$*[?W&+:Sg-BpҽK1Uk4$ AERZ׆zZl]q*?B$+mƭå/WVaX&g.B *܌QfT5JB j ~iDбtN\'jX yL8oi:n$3&Y7[0-d7'y AKɕ6Ri*ɹұ#"q{b~lyTN O[/:Hj]*gэU:- Q#ZiQgˮw,dt²(5BBBJSxRҒPl[ rEPRN7k](bkE'.WSS#6KG Jˆ1!bo?F3T _1hH0w͌n1uv=Bl]OȃΝ;y񁫾#?[?{Q쒟~ \쬱yf2uX(2rOY؂bo/Qr+gQT $>_Sx$< "7dۏz)|w8… *_n)rmyKo3Fy^XY{·<8Jj;cCs7GqI~ ߹ጉy->3zo)vEj~ 1Js7wӷ06hĎ?^?؂bB~[Kb9ņG|b#7vRSHr䶜bǏJO)G]w!v0:b=v\%7^E_O߽]ҟA_Ev܍Ϳ}vI)<{:%>G񠪪 ӦM8N𫬬M|$_oA<nIENDB`image008.png000066400000000000000000000132201300200146000363050ustar00rootroot00000000000000connectome-workbench-1.2.3+git41-gc4c6c90/src/Resources/GuiResources/HelpFiles/Annotations/Annotations.fldPNG  IHDRL@UPgAMABO iCCPDisplayxTǿI/@ޑ^B't *! JbCE\ED]Rd-(E@,*Xxy{y;w7r-2 D!nwT؜Lkp?p@[&ӳᅥ, F9IC( DֵVf ey*iN3?˽3=a!(f uF6'CAW \e&Μ$6\ҧ(2'fKfى]L0hܙN1'=fl91[4/?6Ǣ|^gd>/ѐ(ks"88Ǚ)~=H"ќ 1-s^=!+)g^[D) %,7Laj-gfJB?9Nf p| <؀|YUYӂӅEĤ,+zbx cj̰03`;wwgD'rR`^Eq}tW~#e0?X@BϵIAJ:A yBP ACkP!T C:4t Aah z }ê.]a?8 ^'p*(_@BFb!H$ "d=R"UH#Ҏt#!0  p0rL-ӅƌccXFa+Rl5{ ;~pt烋%p>n+N <şGd:E!z9B?a(C!:\jbx8J$ɒHN0R2i#HDzHzG&5%d>9\F>NB&Q )XRCܣRT&5EA^>~IJR*Z^Ku]KHJ)J(+.ÖY/S!sZfPfB&k.$&]^s9W.OEBӢ8ʹ#KQyYP|B* CtKgSE;/ T.-ضqA <&/J %O]JJ1ʆKW*PjBDž O,Q9rCeBUM[UO+5S-YDژ:MY^~^CHe1*>bC=z᚛44iJ:Ƶյj7h!$UUK/GA>UE?CJ `A!lhmdXax6173 M(&&& &ætSMi/YkQfffG˙o2o7kahmI`fȊgu5:zu7[M͘mm]v+X{7 g?;8d9p1űbżG8i:9 93rrpaT1%(&e*52)vZ 'HtJ C{2E~L(sYf[R[Uzw8#GZ[f6ζ^n7={X[I&zSqp\|/q9wS:*i-P֤֡趾Ӿ;ۛ5ƙ gΑ坛:s~CB⅑.F]ݵߥ+._v>ʙWO_zz ͿYcrf[}o{s.nyܺ|u@@ߝ;wcr>zrbʠRjG|`b*UT D{I!7sɽ{/6nfcPK0VXbУGѡhw> '},W;:ZQx`wو̋;Z/c{klGàʲ1ur!y&e!j D/3J)Q|QH 7&%L U}9g%^\ꍂ#AR *΍#>H=;. zH =>siÊRluفk?皒d8vb.s ' z=Lv5d1H&'q!A^^BE"-E?qq EMFoAW:;$yyH1$(6y '2wHW2d$,c!F)ہkD23ѩ!#|JYDw ~~(Ȧ# Ltv`eZf@sC9yɔ>=Nd`aSr@FYDm;#*WjjAÈ2ɌYܔU8Uf2ٻ #;>1i$d1"u Lwd!=3CL?ш'wU1:P?L zN \7x0c\|/qc1sAi|'ÖLe Q#P[ I-xRr^2|'o*l"'饤 ugEf|oaEF=}͔=Dkbnd 'ebw| (1>%h<q]2D)LI3z)zp S{HA&$O@pxvd4O|yzՔ}8ShQ Bd&PglP^O)W l0oCyw/sB_Kr҇UWN`3 hiOIjm)ëۻj7!6̏ [*Z8ukI =Z]Zl#:{3XFN;'&0Q}%6CV#ڿ iHٻ~EO@6\頱wT!,<)b+T=0j`(+3 vb{g!\86؞xg|VC*[>Y6\|z2B:G+ r9=mSQLspЯMf9<F ziχ@ߚ`!<-n<~W\a\+F`|0ЛVXa(Q[[ۭHv=|i݂P(ԜG=ǐp_?t>4a t]?նAxmЅUӞJc$7hrg MMMFWVV>^n>k/aJu$N{ uB S+Q/ׄ3":m2ΗBI 8&!u ̀U p8:t\lLDJ$XrEcMp'-ϢW"-[7вDs-ČqNo$RgMWAY6C̓f$bhx+B㠘/f0f6za8{c-תARԚzo*DLcv&sr]+JsV@ Jaӛ*a Ÿ{<$7hoJ_\}Cb?6`Wซq, ' q-HZostmʛtV򄪩}WiPEw"-u 9'na<\G5iW cWՅun &2}s"o7;1;P,~̼Z7\\Y7pqEőY,cX3B2z5BV=q[k P_腥WIc 63}+!pBoj$-JT G~GW/6J^gz.3J5iypmģ$ `?%Q+swxaҍyp#1cZNfdIP"53ԁ>}b|_޸52 GCº=P ni |]PЫ[`λᛛ|s]!2d1׎4YZtU՗e}%^2}nk|j $:] HʳKÜ,}$y~ &}ʙb`, 4p,7-tO\ޯΙ97'U>qbXks{WAn/ݞV7i âߎAauZ d#^?S=/ ҘSH\NTEؐ8B<' IO4-4Nx}nS[Т6h=A֤Gm@Āaq5VPGyyye G>~1B /5^LEJKX c +a,a%%Vc +a,a%%.$bIENDB`image009.png000066400000000000000000000171511300200146000363150ustar00rootroot00000000000000connectome-workbench-1.2.3+git41-gc4c6c90/src/Resources/GuiResources/HelpFiles/Annotations/Annotations.fldPNG  IHDR/%2o iCCPICC ProfileHTSiǿ-U tBFHB G`Du)  EdPQ }v={Ϲyss}{;.K HeHg C1t@@ ]bg \ƇA;&3ᅥ, F8NC$l0 kd fay!"N33?Ƿf{C<&@@lv"29<> I,y/NKK#˜/b%Jxn-e RYkߑ*$O(rC!%Oyqfg9I1Ly)pBJ C yBP(AAP1TUAF4t Cah z }Q0`=x l~p8N3\Wµ _`1 D ECiLPv(wT*6PZT ՋQX4MG>4ހ.AWm0zCb1&&b*0SK(aXl 6[݇mva#I39p,\www7'5x/|,WS.DJ QQOt"%#;E'x)-}|VDm;rn  u::t vI{t{u?Emk{od@1p105k53L1gx66J26i /,__\xȄljml2lJ377m7}DgI쒝Kz|76K5;d\<߼ۢ%re+c+~{Tllm6-6c:q5Cvvv%vW1nvqr8𗣉cc㋥KK-qrb9t;ӝrh\j]2F=㹫knfnBSn׻wy<=<<<#<\ّ%"psvuȜeWWXcfۚ^?Ee^nӺn6oި`hw^&⦔M@ `dBBaVǭ~@o嶽۾qW-a\'l+)ݿcpΆ2ٲܲ]Ew}ªqe^;~JvnQVqg-{Va<ύBzmTJfQؑGn8brZ| {K/w;rRd)ꩢ6mMD{R#NSz gK>{~K5~!H/ we{]{_qr쮵_vƩ߬;gvf-[Kv}ǝwwF Z>$ǹ~7L={yTXq'*4ȫ?2:Z/Όyz+?eym_nLDO~[NV''|H0ҧv{D}y>~MMO XB֬@! '$0o&Jـ<,9=sBH$+@8gd&XZ"#֤bz q|jV}@ׇ9_>XĿiйҞy?|OPiTXtXML:com.adobe.xmp 303 37 fMIDATx] TTGTĴ-4mŸԬCωHNN{|@u$/<.K qFi$( 耡m74)L+wNX<]>{nDM2wf=81)ɻ}$J:5~\C o F,͸Sa#Ѡl&3q6QJǼ()@Q&IAh"M򈶳q9*[ri HIFDP^aѩZ5}];:Pg0Z>S?<]+,B!#Fy_^ܪ b0[ݙ3gv;FMyŽ2m691>>)CLͣ1!in!9Ydc26g6rK- _ƥZ Y}2ֶv =$nَ&%iP7iY$@?*L`x78$B#kRAXVmmӾk\bҟBϑ/`+pHxaT]$8ZN8T;qq.`wB?4'4Vap6cjhAl!r@ֽ46H_t$I,IۇT+ٳl`(~d7;vxMX|Q7ӱpiLiUxs~jp_q~G7~; ~w!t$nͦm5rL1soG]3 oWc,euXA]h\ch+U&giYe |v1 |]252oXqY'1$\" ҉ m5i+Rcވ| }_ 4p**UGFV<F(ڰ,էF,U?aJ%/_nBC9K7oGeBC6ݜPxz^(d8S?KxHDLgeꍘzկđt1׺K0˒L K/i~&.B~"!,;_, (·r{&mGlez T8PD\]F*&`U8,%:&Kʐ[?[p/%[u̙JSF!'1ťc!S^W^:{1zqu:bgdLlEQob 7n\ |:J)~PNGhQא.bEd=foȶIURv4rd2_l7|J6iٌD:s H.pߢbcl?q .:I|yܹp>cDFF^31zlCcmnolE `ц+-}cن6le/OVR+**$cن6le/\yي|7cDIII%tF?3&Lm&rۄ\%jӼc ]U.ک`oFe}f_0{z%g6.T~;Ú=3wx]+KG?}?eǧ;3Ν;`տ)= 6 6 Uُ~ ;VTy4m4:;pڑqZG#]jb;G#t̫+*yr8=]:Gʫ8nA+nt6rΫ+q8Ȳ>}IENDB`image010.png000066400000000000000000000242571300200146000363120ustar00rootroot00000000000000connectome-workbench-1.2.3+git41-gc4c6c90/src/Resources/GuiResources/HelpFiles/Annotations/Annotations.fldPNG  IHDRz#V iCCPICC ProfileHTSiǿ-U tBFHB G`Du)  EdPQ }v={Ϲyss}{;.K HeHg C1t@@ ]bg \ƇA;&3ᅥ, F8NC$l0 kd fay!"N33?Ƿf{C<&@@lv"29<> I,y/NKK#˜/b%Jxn-e RYkߑ*$O(rC!%Oyqfg9I1Ly)pBJ C yBP(AAP1TUAF4t Cah z }Q0`=x l~p8N3\Wµ _`1 D ECiLPv(wT*6PZT ՋQX4MG>4ހ.AWm0zCb1&&b*0SK(aXl 6[݇mva#I39p,\www7'5x/|,WS.DJ QQOt"%#;E'x)-}|VDm;rn  u::t vI{t{u?Emk{od@1p105k53L1gx66J26i /,__\xȄljml2lJ377m7}DgI쒝Kz|76K5;d\<߼ۢ%re+c+~{Tllm6-6c:q5Cvvv%vW1nvqr8𗣉cc㋥KK-qrb9t;ӝrh\j]2F=㹫knfnBSn׻wy<=<<<#<\ّ%"psvuȜeWWXcfۚ^?Ee^nӺn6oި`hw^&⦔M@ `dBBaVǭ~@o嶽۾qW-a\'l+)ݿcpΆ2ٲܲ]Ew}ªqe^;~JvnQVqg-{Va<ύBzmTJfQؑGn8brZ| {K/w;rRd)ꩢ6mMD{R#NSz gK>{~K5~!H/ we{]{_qr쮵_vƩ߬;gvf-[Kv}ǝwwF Z>$ǹ~7L={yTXq'*4ȫ?2:Z/Όyz+?eym_nLDO~[NV''|H0ҧv{D}y>~MMO XB֬@! '$0o&Jـ<,9=sBH$+@8gd&XZ"#֤bz q|jV}@ׇ9_>XĿiйҞy?|OPiTXtXML:com.adobe.xmp 378 35 iFIDATx]XTU @J_ Ai,d)&iQ$,@meML WjC̘RЄ=a?c<ϝ}{~=93cPPP [nYZZRB$I~ :*I@$IwJF/'{y}hJ$ I@@/$ YT$Iw$&,I@$^HU{nOn$e$ BEEEh}7uuuO[б.}/u2ɩ r_o߆4aynVVn171`U Waaab7j}>B_~E 333 :FFAcCұ(.ƨds% nnnd08/6q_ E`c5-(k0ōk}Xv{Tkkkոq] dϱ!X%N}#(X%oeeG}TFZhn%$o!dgNyBT*gYʛ[Jz&8 ̒g ?vX899 OÇא!CwaРA*^cCq;UtGoی&}dZKok\ӬU@gTg"&dw+ehH))OAL\bb(V^q)8S,J^-1H/-[њ 47P0[.X,gmh}P2xQg;;nq6 v96$RGob@\C[t~5.Q-dK[QjH%Z/fd*.|vjz/&~wߴ-`kkm[05ǁ{@jqRy5$҄1 yD񈏍ENNJGlC}3 W#IKH=z Z7R@[| 9:E33ڳ̴We8SP 9d"==)4jUTdz:33QPZW2Ǭ#GkڋAcNX|jΜ$#T4z!2M:mHGo.Y[b! 7ƃ׎a}x;KK>Ɗ`9 d9ub25%f(HvV]R=OOmPM, 7O9MWNJwKBP3PjYoR!N: Q_"~E"fibuKCK;Fp7YЕ?Cl WZ>-wxx|жT!1d\}@u0^w]Jԯ<yiqtL !yݳ {F=|g9s&XX;^ qדpbL:0 |IAr3mq^bFz!|\hL=!IzǀY nl=4z7xCɈ^:X܎ ޚ 7#cxUzXv7d F$0w݄9h 2^;<&ZmrVtRH=9OB~25b9c_bA b[Dw $M@rȋjWM[|D1a{?ֵ/F<Ķ,m)+3_/O?E||<ƍO>DX{Ndž.BBSgձ~bzBaRX8Szhlg 6Q>+ c{I,#jmB!jc9+\7Fs'F̒xn_ªjq/=2ZS~w@龩Mg|p#]D&`Tt"w#<`#I-uȧSfx>Seʟjp=?!!2_^3q+k^؃kG":?ӧ"Jjڬ-w(*ʂǩj(ll? \-"j8d({/ _r 2g"*ߌIL_<8a?E~V`K֤aOYa[+@hƧENu 9FTD7n.o5!#h8'Dnîsjs?N,Fŷ#|5U>"њo" sѰexFkҚ(nyцdYn+p;" ?KV6pO3|9qEY;.۞w_j7wẃN?45ܼado.2i`^ HHH… }voڴ %%%bL= U2dS vf|_;/tT/%FShuς47N}Ӥ|ֹ~(y%<5NozSEc% ǰ9 )-\}0 ;hLq1™ K ;`.\y JxF!W'Ȓ0)v~s)X4u˰-4i]"w_O.0z6r SVo d"кP=pw reot~dlcs݁% AAޞ9z{x#0B&)+j Ppa~O1'ԈI8<3 Aa{`h8B baerj7  1t$\( y3FO" )t14*>E8\Vc"i/ iX܋` |&|p89[06E3QVQ$X}elM%Ok\<k}6l&|'u:؋s dWSgysY2,HD`">ԢF0BT#E#+M9e8x1mv߱}}ud~yaÎIu<,gV iמuեyw DG}6(L>zsKqMFǃȃ놗fOܿ_S1E#Qy5240팑'cCMNg <8R3/a X2D Fj<~"R) u]EU[-~&~G"xI B z5, C$Y$sЈ ,KG0@F8! 8?y;ȍP87ǟl ofln13ShS4[GD@s?ch/]¤9Z_ Qc te0 xD eV-EQL ޜ9mN\"b0ǣz7Q7ҋ20xt6 Af y #*Ў,} \==%!>y~*[0# M|2 Wܟւ̢^昳x]= #XMԶj_^L~:kXqrJf3Z*r'h`?ԟ]=},]-;qsAٳ_"b1)+ݎ 5Bz1c)O@um- UZ;Ɯw ZȎf@m:\`#`oחӽwƳ lx$em;;ۛ}ax%;B)1݊/ d[[8دKoALAZ9G$id{]UMZ?zKBgggDGG/!ɰj*\;c3kZqkWFgJfvց 8HsRuO~3TB+Ex^Fju[hsO  h'[v3O[Xz5|IxPO(!kAGoe~N5{V=9YJx;y+ǐn\oQF:k/} IǢh[~ŋ,x (́dȳZ27`TK쟣zts֎>džcQQ^C_u***pI\~]VNZ+O4|AYf:d3J@/W^EqqX5fk/} IǢ(n4jL_faXYY 3=> 2RVh*^ґYZezy殹t._ݗ1KFOcCұԇMmNٳ79;c~ ^|5 ry 6]oxNW8e dcpf=1U-jlW77ɢ&S{ խNIŒJ 0wpptҩz|:DD$IW\HI$ I@'^'bH$ I`J@/@7o\UII$ Io O:oH$ H$ I@Js($ I@$~I?11IENDB`image011.png000066400000000000000000000125701300200146000363060ustar00rootroot00000000000000connectome-workbench-1.2.3+git41-gc4c6c90/src/Resources/GuiResources/HelpFiles/Annotations/Annotations.fldPNG  IHDR}MgAMABO iCCPDisplayxTSiǿ-U tBFHB G`Du)  EdPQ }v={Ϲyss}{;.K HeHg C1t@@ ]bg \ƇA;&3ᅥ, F8NC$l0 kd fay!"N33?Ƿf{C<&@@lv"29<> I,y/NKK#˜/b%Jxn-e RYkߑ*$O(rC!%Oyqfg9I1Ly)pBJ C yBP(AAP1TUAF4t Cah z }Q0`=x l~p8N3\Wµ _`1 D ECiLPv(wT*6PZT ՋQX4MG>4ހ.AWm0zCb1&&b*0SK(aXl 6[݇mva#I39p,\www7'5x/|,WS.DJ QQOt"%#;E'x)-}|VDm;rn  u::t vI{t{u?Emk{od@1p105k53L1gx66J26i /,__\xȄljml2lJ377m7}DgI쒝Kz|76K5;d\<߼ۢ%re+c+~{Tllm6-6c:q5Cvvv%vW1nvqr8𗣉cc㋥KK-qrb9t;ӝrh\j]2F=㹫knfnBSn׻wy<=<<<#<\ّ%"psvuȜeWWXcfۚ^?Ee^nӺn6oި`hw^&⦔M@ `dBBaVǭ~@o嶽۾qW-a\'l+)ݿcpΆ2ٲܲ]Ew}ªqe^;~JvnQVqg-{Va<ύBzmTJfQؑGn8brZ| {K/w;rRd)ꩢ6mMD{R#NSz gK>{~K5~!H/ we{]{_qr쮵_vƩ߬;gvf-[Kv}ǝwwF Z>$ǹ~7L={yTXq'*4ȫ?2:Z/Όyz+?eym_nLDO~[NV''|H0ҧv{D}y>~MMO XB֬@! '$0o&Jـ<,9=sBH$+@8gd&XZ"#֤bz q|jV}@ׇ9_>XĿiйҞy?|sd pHYs  $tEXtSoftwareQuickTime 7.7.3 (Mac OS X)_7StIME%2G $IDATx[{LTWDPAEʬfv%(jIb_ifJ Ak ӭRX<kyUfyY|@eKs.0f/{||\`1ɮ 3C^]]m' ]1r; j!!![EJJʨrLtuur98̙33̀\7Nk9zzzrѢE5LMMߏV<O>,,,&F5~=A{˖-9cٳgǘ1c[Ź[{DY[XveݵFVE3m36>T*Z#zT__iH%LE?n-|@Jў9:&].@&@J䲮Z!bi0{!I LܼyEhj.I,Dvf&R). ?igg.գߜ*ri}>zԫt$/yf%<8W2~|IZ^bH҂0GUuHkU9{aT][ OQYޗ[CPP|=d+-<G󃯧;p,{1Zqd&llma5{%. G: TfXEܽ{>>>LG5a&j9B* eI1yȹrkj|\ Y0,[ ry1G}Z+-KűݫD'q1sH%NxEB; G]^Hkg^ ~|uQ3DG<#W4&|#ꦵE__SΝ;+Q=ܙj`U?-1׊uL/V~b]j0RU8IR<Aj/PZ'~3}G/v!ris# hɆOg/RRa⌫Q'ǰ&&&+!n"b#IFHɨSxN>D"Att4ك&AMMͨc 9L#-77 hʑv5!vbģ*sQt!C?Ɩ#h)+RXKމu] bF @iiNM@(梿7"tGnvxX X@.f'קּ%pMX#g~FAZ;H@{˗3;wbժUXt8K?mYj\= ޜ(W3$ģ k X9W3~ŮJKb߬`+B1@yZJa*BilgFE47F9E%VZ8ƝGn9|u z'X9JM/,[*F[|7`46PROu?AЍq*>;ݖ*y34H gsSP=<?Jl]3r'oM'6&kF8GԴAI+(at4wV%vXicY: g4)C`ij!MY#7#֓N(fVD ȣ2sQYu]!n &n sJt:LxDŽ5 *_"t(\,е JQFAeh@wo_?COƒ8$'QPda88m^G{CPE[̴887)))1pXݻw&4"KHwjjTdĮiH-8xMa7jۻw/uHMM˗/y㜮+8Xb=I3Ko: \\\ę߿HGFǣ;9D֭[ΝCdd$6o,ץzT~( ИW8K,\8+60g9 9kt[H> 0QƯ'\.CgyGY臉>kSuHp ***ՄtحE]]mr&d<5yى1?OP H=YgZYLKPg}~񳘖A,X`{~%"BIENDB`image012.png000066400000000000000000000101601300200146000363000ustar00rootroot00000000000000connectome-workbench-1.2.3+git41-gc4c6c90/src/Resources/GuiResources/HelpFiles/Annotations/Annotations.fldPNG  IHDRO#WgAMABO iCCPDisplayxTǿI/@ޑ^B't *! JbCE\ED]Rd-(E@,*Xxy{y;w7r-2 D!nwT؜Lkp?p@[&ӳᅥ, F9IC( DֵVf ey*iN3?˽3=a!(f uF6'CAW \e&Μ$6\ҧ(2'fKfى]L0hܙN1'=fl91[4/?6Ǣ|^gd>/ѐ(ks"88Ǚ)~=H"ќ 1-s^=!+)g^[D) %,7Laj-gfJB?9Nf p| <؀|YUYӂӅEĤ,+zbx cj̰03`;wwgD'rR`^Eq}tW~#e0?X@BϵIAJ:A yBP ACkP!T C:4t Aah z }ê.]a?8 ^'p*(_@BFb!H$ "d=R"UH#Ҏt#!0  p0rL-ӅƌccXFa+Rl5{ ;~pt烋%p>n+N <şGd:E!z9B?a(C!:\jbx8J$ɒHN0R2i#HDzHzG&5%d>9\F>NB&Q )XRCܣRT&5EA^>~IJR*Z^Ku]KHJ)J(+.ÖY/S!sZfPfB&k.$&]^s9W.OEBӢ8ʹ#KQyYP|B* CtKgSE;/ T.-ضqA <&/J %O]JJ1ʆKW*PjBDž O,Q9rCeBUM[UO+5S-YDژ:MY^~^CHe1*>bC=z᚛44iJ:Ƶյj7h!$UUK/GA>UE?CJ `A!lhmdXax6173 M(&&& &ætSMi/YkQfffG˙o2o7kahmI`fȊgu5:zu7[M͘mm]v+X{7 g?;8d9p1űbżG8i:9 93rrpaT1%(&e*52)vZ 'HtJ C{2E~L(sYf[R[Uzw8#GZ[f6ζ^n7={X[I&zSqp\|/q9wS:*i-P֤֡趾Ӿ;ۛ5ƙ gΑ坛:s~CB⅑.F]ݵߥ+._v>ʙWO_zz ͿYcrf[}o{s.nyܺ|u@@ߝ;wcr>zrbiio)-#/s{8sܧ]xtnsqn-syAkN)>șDSSzVejoo8UFE!::!!!T*T*PۏˉG3KNNFRRI Ɗ?;ySM׸R$7Tccc̈́ /--avvmx[Rudff`Q!x7~v]"YaRK`d=.**&[7T. 64akN_Glй"ɑW~-~ }*[tvv"??!;c8o,Y/F0٬QFfož>ꡮf|f&^'x\wuT*EJJ ;Ȏ8rq͋S~ee{r 98z.~9eSv$0[o@cuLQ%%%aǪ*lȞO#+֏a8BYٖ\ÐCvIqT*ǥAiڊ[u(.-PWW4v^XX $&&te9֮.6|n7(Us8uiOC.ʣCz9 " ).3 kCatd?ApM>-ˎd8 . GzTs'rt8쿿bk1w'VfC{yPn.թn)׾l2++|N -鷂v ެ3)+[_wlz2qfg{vf'hELIᮇ*c3~Ojf G7g9rdy̻vwЬQg6B@ii)9|||ܦ].1`X,Fyy9z{{X2H$w9<<Ņ ,79??ϒ -ձ1q)EoQ̓2.caaa%HͶp9 =$L|ckvx9JUIENDB`image013.png000066400000000000000000000105371300200146000363110ustar00rootroot00000000000000connectome-workbench-1.2.3+git41-gc4c6c90/src/Resources/GuiResources/HelpFiles/Annotations/Annotations.fldPNG  IHDRhst G iCCPICC ProfileHTSiǿ-U tBFHB G`Du)  EdPQ }v={Ϲyss}{;.K HeHg C1t@@ ]bg \ƇA;&3ᅥ, F8NC$l0 kd fay!"N33?Ƿf{C<&@@lv"29<> I,y/NKK#˜/b%Jxn-e RYkߑ*$O(rC!%Oyqfg9I1Ly)pBJ C yBP(AAP1TUAF4t Cah z }Q0`=x l~p8N3\Wµ _`1 D ECiLPv(wT*6PZT ՋQX4MG>4ހ.AWm0zCb1&&b*0SK(aXl 6[݇mva#I39p,\www7'5x/|,WS.DJ QQOt"%#;E'x)-}|VDm;rn  u::t vI{t{u?Emk{od@1p105k53L1gx66J26i /,__\xȄljml2lJ377m7}DgI쒝Kz|76K5;d\<߼ۢ%re+c+~{Tllm6-6c:q5Cvvv%vW1nvqr8𗣉cc㋥KK-qrb9t;ӝrh\j]2F=㹫knfnBSn׻wy<=<<<#<\ّ%"psvuȜeWWXcfۚ^?Ee^nӺn6oި`hw^&⦔M@ `dBBaVǭ~@o嶽۾qW-a\'l+)ݿcpΆ2ٲܲ]Ew}ªqe^;~JvnQVqg-{Va<ύBzmTJfQؑGn8brZ| {K/w;rRd)ꩢ6mMD{R#NSz gK>{~K5~!H/ we{]{_qr쮵_vƩ߬;gvf-[Kv}ǝwwF Z>$ǹ~7L={yTXq'*4ȫ?2:Z/Όyz+?eym_nLDO~[NV''|H0ҧv{D}y>~MMO XB֬@! '$0o&Jـ<,9=sBH$+@8gd&XZ"#֤bz q|jV}@ׇ9_>XĿiйҞy?|OPiTXtXML:com.adobe.xmp 104 115 hQIDATxAN0C}+P7zNWG7k\svk9+Z+6mިF&K7 9c{̐˿7X*~!;.#D@\I7$54(`AUh O͐ @ZZ50zεj@:MuZ~WCZ=0c===-Wo #q@NGP СR'J@U\rĊ)?&X\ ͎c$oF+3(<;Vi=`|аU>_:`r?99yeGhz,v6|{yhz,^ 3g#br5e O2/s_{{{9RHgr4YVrf@"b W["&c$rtYH[VqZo#XfРXݻSMgg'jOEw}σvwqqGGG|X"Aݝ%JooW.rlPuTk0 Tk0 Tk0 Tk0 Tk0 Tk0 Tk0 Tk0 Tk0 Tk0 Lww{ki$WAO@PbIO@PbIO@PbIO@PbIozd|gɶ8< <1<rA *0ϣ*y\ J@. <1<rA *0ϣ*y\ J@. <1CҩO0 0V6 惖?i 꾖4xd]VM°>)l9\# ))r(`P<B@)WFJ \# ))r(`P<l>gIENDB`image014.png000066400000000000000000000276131300200146000363150ustar00rootroot00000000000000connectome-workbench-1.2.3+git41-gc4c6c90/src/Resources/GuiResources/HelpFiles/Annotations/Annotations.fldPNG  IHDR| iCCPICC ProfileHTSiǿ-U tBFHB G`Du)  EdPQ }v={Ϲyss}{;.K HeHg C1t@@ ]bg \ƇA;&3ᅥ, F8NC$l0 kd fay!"N33?Ƿf{C<&@@lv"29<> I,y/NKK#˜/b%Jxn-e RYkߑ*$O(rC!%Oyqfg9I1Ly)pBJ C yBP(AAP1TUAF4t Cah z }Q0`=x l~p8N3\Wµ _`1 D ECiLPv(wT*6PZT ՋQX4MG>4ހ.AWm0zCb1&&b*0SK(aXl 6[݇mva#I39p,\www7'5x/|,WS.DJ QQOt"%#;E'x)-}|VDm;rn  u::t vI{t{u?Emk{od@1p105k53L1gx66J26i /,__\xȄljml2lJ377m7}DgI쒝Kz|76K5;d\<߼ۢ%re+c+~{Tllm6-6c:q5Cvvv%vW1nvqr8𗣉cc㋥KK-qrb9t;ӝrh\j]2F=㹫knfnBSn׻wy<=<<<#<\ّ%"psvuȜeWWXcfۚ^?Ee^nӺn6oި`hw^&⦔M@ `dBBaVǭ~@o嶽۾qW-a\'l+)ݿcpΆ2ٲܲ]Ew}ªqe^;~JvnQVqg-{Va<ύBzmTJfQؑGn8brZ| {K/w;rRd)ꩢ6mMD{R#NSz gK>{~K5~!H/ we{]{_qr쮵_vƩ߬;gvf-[Kv}ǝwwF Z>$ǹ~7L={yTXq'*4ȫ?2:Z/Όyz+?eym_nLDO~[NV''|H0ҧv{D}y>~MMO XB֬@! '$0o&Jـ<,9=sBH$+@8gd&XZ"#֤bz q|jV}@ׇ9_>XĿiйҞy?|OPiTXtXML:com.adobe.xmp 132 124 "IDATxyl\WǏg<},N$mIZB[%-k) BB?UP,b)[)ktIIn6'nόϝw'ϓ=ߌy}{jiRbUNT9@anT *fp b;?b`* * f *fp ]q,R=}55×K<[FL,f@ѣ.c&`b9\ tuG_3Ig,1:jɉ KNMY]:m๴m"dRluuP @`eK= ]L 6J[rd$zmu%uObl>kڳ{i86k;lr:XkkmJNh?Q_o64H]kiR jEmIk~kk%ÇQ'i=uj ӄ~L46.n겔?Lة5k-luåe7b*BJ!#qm[_1,hI|6)0,)͑|u\s7m[zjmq,W|$l{Fj}`6 :%|B`&&$jT{I L#MM6bV9u_5;g$3 3I8au0ݻ-98hu!tP3vL$0LyƜ-'|@~޿u9^tt뮳ɶ6?&%sy9'TBDĤD@3Z/ШIZw[ b:0vǎJkLH  9s1Svi_‰7 NYcCz >ۄtG.lF]3"N+FiNmkn׬YS'1 &qTX@dTB4–=OZ׃Y׎ XlhGJpqjTBJ+%NvR'.ͱZfk^x$x8O~:gYfjeK4#lQG t@;3ˆZ  8/T^WU^'U izq0Bmb_*;(m1 `8*W^Cyr!`=2 gkӟr@=]8@G5BYP#1FٳG;4*r^kc2o1 +jZLzSt0 pAz˭Tg+Nc0چloY!sZ{ GST(`.0-sS[oA5GLI= "##P\B-jtis=.Z^J{H8ysE"ַg?`J~/~1Co=ة.Ꚉ R+fV$k4j4ܮ&ߘbJK|0lҗ\tg6ARLֺO%!a`cPq8:ՏѠZbI1xDr恶r(T"qEbgOȖ$!%*5"|1 Y [^FZ#j~ڷρM!32$1,,(+ fD" ^LVPPÇNt GT(at~e_Saχ?l{_ pxA!mqNQC"uv=j n mB܎fY=Wp1:Nky3Q>AӄuXlq% AS a 6hgfԔˏ*0~b/] `yڸ5ʁWPr<@J ԯjY}+OwYg*'\C)-_OZ~C/5Ps StH ǕRէ*$P1%W J8s֡|˯H8nqtP&!{1"0T!tTIۤ#2j?Yr0cR餍x>,F5{i  €2:#9=J\" s[+շFHhP!e(4E0h w{W] 1tMO|^PU{ zG٢i(&t7%%!SR-8Ycy뮷!`ӟ ovcFYe/o~~_jd|  \}Sg'? bM W@ N] 3 ~ӛgYBPhqX6aܹ GWHm_v_l$0:#1g;-NzOIC4Y o;^e.veOi(2TSߝ+ 4Z-sZL6%RQCLpF*w<7]~kf8 mPNg'pMM|φ߰$@eMW3/M<~0nmAff9i=< qP&ׅsMo۶>f5Bz@9_g?YkfjR/|s ujY&HKDP& BNNdz=-`T$QMl(Ηt`J~ڧ>)RmQY*R|B1>9r[LKFpд x[cpB Mz.) H*???4`X*0;y&5ܮ]vNۤ!y9iEg2vP,ۨAРLF;pQg3b0uj.~;>W1`θ*x1^MNJL(* d.L6mV4~v;0ZyKp@ זL# d}zP&i5Lkk%Ee ܈#`cSi,Y1%/0ybn>Ai"hjŕgZ QS$@;EKQ'׃PW#4I䥵$Dk:e9gѩG~r K |e9rg",KtP"x1%~R]Ɏ"p6`T:8"k +ޤZ(HA*ߤq5FiPc12{y6+=9{9 Ϳ~y 3h[,') S,ItcꆆC݅KD oq g_J™%މc9F7YF}AqMIԠX< TiI1u+I'T@3njjI)2alz{IHij:}*Ex$&2:u2M-!5Z^>Ι,4EfV JξIOe{?q^b3Ϙ4'Qq*B:-] Y 09Wm6u# c6Lu!i{Gs!i ib|C({q~w_La: MthрD}es&'[C0^z-PvR#;&9֧M93_gJ8,=6!֭w٢Abp02u{Ӝ70dYv8HИԾ$]si· _5!61Sø).Y> e,6gȚXĈ4oFs1Me "$%JgOX,R T.g o>_ &<7yQ (=ϩB0`T׵()AZOpqP쮅qF)4QC0E0#o"ߢ ^}8o-kiht[e)Ef+} k(wCBZLa(V6kN| +,x{̓isZխ3P:b===.ѧv>^c׬6&l=<|}L"O%R# ָ,Άcَ  vU3`I=(U+` @8k>lIy^+JSΙ3,`>5u+1]M \û Nq<+i`aS80x11)-|OKZGTѼrޞHgJDCqFK $5ɸR:gZbc _(& LGMTƐS`/O-߫!qk^;h5 9SvASj5%P_MH6یW^ȱƋ ̴+M JgsJ| ꂖTT,y?9CH8 4/w?dឹhEC4)Wq?GrGg :nbJM0/D@o6|r:'E?⸘O@G1 letо9hZj+CeeA ފ& bq-$Q)5D63yV16Zl6g \hN' Asp!Ppo"y QL)e!Z@y/އ+PXq C5J  >~/Jݙ M ՉLC˶P!L&aaq&f}=zZx syǔQ^)!JJAie)5b,U~TsS 8$_D Z hxIṶ2Y,mkZCsGq !ޓLJ*@}癝^\a]b揋Kiaw{C܏Ѿ]6  iJFMe " 07Ok8 $I pioJY_ a^ؾu]Rb^bFj>fӬ5!ZÒo: DH?:@(h D; j)Npz'(DË 7 ]v2?7cPZ\p6<-yPlGISIMZ%v/O %:@(1G4~kC`0!OK1|\A1a L?DF{.xR#{A|Hp٦5of9IMA8;G Ā^Zakմ>׌.gL| f`Nᗄ D^y#:GJo5VK )D{TDwZB`>}BKhUQN|ZnCs.;|\;s#_rwrVK#9/E%a{B`yit /3rzFҬV+0-j(z@(xƌVⰴZ"8J1n(/J2[Tyǁ;A:|/%ZIk uZP>wQQi9T+{ղ uFɀї.PJLK獨_)E[`"N9>w5{L6B &-,v>x:+Ai.6vJ[i1mi-V7m&|9H{X "ôvj:MG8F: T>@59PpZx~_R$}LmZr 6CzL$5`u J (Sh jǤz4W=\<<1{Vw]b@ZޤpAA=sM١$bbo0>H>4(*qdsHi(]eZԡӢ4PquM/W;ԅQ K D5bԠ@1)A,Ѫ]Ƙ/9AT;B tYW*J]-+Bs=GoefX- (y)fYQr..u˞=-5|呼_Z} Lk~0m~z]sծܯO%t=ݤUۮwQ+AdnTegZ[Z 6.άۗ@U>|˺fT )'QZ/Ekb6FU  *ڮoTQG"\{yO.>&/%0r@ƤVS:`|F6#~\ڣ+3 PRqVWLhoO#wvT R?cu!^7F9_@4(h9_<*ߩv,#{Hy=$0OySYNreftl6OvlÏX~r4HcySq=,mH9q`YF@Р^[4Z/~a4T3o4/4MJA._Rσ0p1hj`:ViKJ{xV+턾:.O>;GT5!j6{wiq5 if|S SfboHaHRj*i>z0u$ gXR1HsT.c0& Icmiה2)LqiD_ q_{ &%SB NuAfʿillupNb~}aB ʍ` ׊he̵C &uE[Є兓 ۱ӆ/nwؤ Lүz5yN6gl Fx<м ,l%[>p܃YuKNjUx,6۴5Z;gg;kΠ=o]DjLxcyZhqa&r/2vGkKc0^i5+lrҏGRړPE΁ES Ԥ4JJ5\S/#wR-4wUbarK͉A;8&@c BI#uRIyêElBH-_H8—F/S=q-s9>{iOX}SQKPuț 4A A[ZV j9i K37µL6 Gtߥr_"PXp(wʴHGsE~\J 1B Ygs+HR!S+}?[}eŖ +0_V* V  |YgX/\* eŞbŊp(̗{ +0_V* V *IENDB`image015.png000066400000000000000000000044651300200146000363160ustar00rootroot00000000000000connectome-workbench-1.2.3+git41-gc4c6c90/src/Resources/GuiResources/HelpFiles/Annotations/Annotations.fldPNG  IHDR.IBHsRGB pHYs  IDAThZ{PT]bCV"DԙWNBuh3ԉIciXAQQhbEqDADD`e]v. $pfs;ejkkAhldV*₿Zmc (?}!Excؕh%~~FRomr=اH{o?v:q|?* [Z B^Jn{ aa3\Í<~(J9tk44QuHJJBҜ(:u6xp?_&`3DAԉTIx4|U .(baӮR.DV];@n 8[žLl}=_q [7DH\Ev"ů؅LywHפ8j `֦ H`Gcd@KSB[p7coJq_31ZT9{Bh6 Og/Eoq@pF;0fzںa <6Col~(||Q׋QWc:ABFfBkT0)f;9k*8<+fktb$("aI&b/tӕ^ -=0.M${aX!OǢp[%'3Q˺V q ]D3A-AdRCK7*t?|vWPc~z|y|\ejoBe=>tU1r~8B)z9'+"z֟>'q8";/ atX%Q^eaS9x=W׽eЋ^rUA<-5>bXͽ2"DScc#|.Y$4Rjgm)#FLI$H}7tV+ .X<=qy]Xƭ=U<'2nB7f-ܿ:gxH@>q ܡ@>V(Ev.GII.=ə᭿Or"{:eAzH;σpA2 |4 arnixR-%'z<'Գx]58^t-zB9;߭}pcĜ &%fdxyx"6J.6_Ò j컆5@hr|ipl v VM:PPtd>lu7z'x,L-ڡ%IL }:Ճ~sۿ]e!2: " D\E)v:UF=G5^YrjyQ<='8zr h'.=="uvt+|VlE#ǻ0H.TK\3G/`W=b6;ל!Ls3eɊ.cTPIc `$z"\#'L<==] GODFFz:jW!UTj'DO ]׸"WqUr'xn9cw#[__'tWAF\c*r. .mO&k->h6GIENDB`image016.png000066400000000000000000000275351300200146000363220ustar00rootroot00000000000000connectome-workbench-1.2.3+git41-gc4c6c90/src/Resources/GuiResources/HelpFiles/Annotations/Annotations.fldPNG  IHDRT* iCCPICC ProfileHTSiǿ-U tBFHB G`Du)  EdPQ }v={Ϲyss}{;.K HeHg C1t@@ ]bg \ƇA;&3ᅥ, F8NC$l0 kd fay!"N33?Ƿf{C<&@@lv"29<> I,y/NKK#˜/b%Jxn-e RYkߑ*$O(rC!%Oyqfg9I1Ly)pBJ C yBP(AAP1TUAF4t Cah z }Q0`=x l~p8N3\Wµ _`1 D ECiLPv(wT*6PZT ՋQX4MG>4ހ.AWm0zCb1&&b*0SK(aXl 6[݇mva#I39p,\www7'5x/|,WS.DJ QQOt"%#;E'x)-}|VDm;rn  u::t vI{t{u?Emk{od@1p105k53L1gx66J26i /,__\xȄljml2lJ377m7}DgI쒝Kz|76K5;d\<߼ۢ%re+c+~{Tllm6-6c:q5Cvvv%vW1nvqr8𗣉cc㋥KK-qrb9t;ӝrh\j]2F=㹫knfnBSn׻wy<=<<<#<\ّ%"psvuȜeWWXcfۚ^?Ee^nӺn6oި`hw^&⦔M@ `dBBaVǭ~@o嶽۾qW-a\'l+)ݿcpΆ2ٲܲ]Ew}ªqe^;~JvnQVqg-{Va<ύBzmTJfQؑGn8brZ| {K/w;rRd)ꩢ6mMD{R#NSz gK>{~K5~!H/ we{]{_qr쮵_vƩ߬;gvf-[Kv}ǝwwF Z>$ǹ~7L={yTXq'*4ȫ?2:Z/Όyz+?eym_nLDO~[NV''|H0ҧv{D}y>~MMO XB֬@! '$0o&Jـ<,9=sBH$+@8gd&XZ"#֤bz q|jV}@ׇ9_>XĿiйҞy?|OPiTXtXML:com.adobe.xmp 250 84 ڝb"IDATx]\G#( ؕƂ% Q-#jlE%h X"%#KPDz~{ޛMbHAҀ7ZI4 iрdҋ i h@2*"J0*.k iM׀',Oрdk i h@2t=<3K9.SXg"rIBwEAp>7/A;eRS݌@':㜛 W$ ,|Ĉ]2'? vlb.IUYVclbn0+elX,e7#QY|Ԁdz}l8a]+Z8!t RqC%qxEFsW\Bb07@dxu3?Kv0T旟!8%gmF@`i2!p!-BJњ5c; ;W'i#>\ty/X|;Ok8JPH'LZy+ OB%XF3d$IuDŽC(p/ꚗ/+xxѐ98/)3ȩ_5Mڅ&y^"J$݈gQ3+KGy*M KvV@ƿ'Y׈\ ©Ç8?s;BA | oC[b{LVpD͛ãIAOVZPcV:&3 a+*ygceN"7q(0[9~Rn5 5tr~^Mx&Ĵ-0}:C{e&`r]46#9!0xO=q 4uWa8:~sG:J` ~]0(a3#EtE]TQ2*3wan\=&c̊H*5?&C\L7D hD\6}qXo-fiA|w#X `wajd~k 2;Hf!WSFÒ yG5 G V0¯1;*G3Y]?ړnmO:ٳT5`&je`HޞיF:Hlm{΂|<+$-|'Z0 A ]ږxIГezRVҀ.5 uR/IzҀdzRVҀ.5Pr7iB_ɴo9CWblg܍ߎm%q$@9 {˥i#Ņ!+ӌvY}jzTbq6Q$N폶O b8B=zbK۷תΞ=[ٕ{!a:cN \'RlwUmK UIu]J [Z{[jC2OnŬELƧnnp#Q#9+_OJ!hױ6y Ώ_mkexH}\"κ=^ MZ#{&y:uߠ0lD,+&Ax%l5?!geUTT#vUͭa#K,м}289 EgVVl:KZldTGY [&'6?;;;~p>P}u{9RHBmyH BOBY aH:m._O'CoTC2Oϳa/poVta0:+7M(,,d,е2g_דĕZݱihOH67[V-6 2ԫ4MP^ǬOWcd tj7CPpj[L}6" ·Y[h^ZZc)8>IDZ%t8ˤqx#3~6BhTPEUir9WwlՌPOw,YGߡkhaqK8GI1Vߑge CN^.M./qE/ =;KG#; 72r}+ [> 3\9>SSd؊-;GL)Y?޵d\=nt׊GP);N1iL&gs{ǰh22\ZWEjc׉a׫waQg0|SE"@ehIHrұ9M}T+ĝ˧ߥ;b%ુ.%[>=#XfM="(??7ܼZx`͗cIh"zb(ĮvGC?l1jhX ֯Ø+ 3=cс<"O6v)Rou\L㈦ӬaV^RQz='CmLmvd|rڷH&&T:4hC>LMaD/M8~BCⷓDNq`ܧsweǷ,[xɦ%`Gwqf0/ P]o0ܫ- ?!Y D=1oDG TG +#G5ԴA͚5aU&a\侦5A7\Z!Ynۣ,~AB$S11'7co6藳ط LЊ-KIQX&k/s0r^zt9-,"BH|꣎έ&c 5?+ a c|(+1e?86DR#1]3f)*0τK"89.Eܑјl ϊ.h4,HWOAuH«L^Bѱes8ۯCP?v4o>n0=̪r8x A_YmO>\U~7ӌ0bI¯5MTȕRq\5smƵq[]fC_}4/hBvu ')^XNFN @c|x*M~>^hgr8 ?GGw|I8r.j5u.Ϩ\[ү2DlMoJLGiGC_飧?V|{Nܼm>ƶdz5qê='ީoZϫM=$jy*kucfHn_:ѥyxZD>wq4kV gV\&Ͱ#[// kiX% O26t_ yQfac.l)xðN&ad[ Sa#Y: y3a{!Q@qy`>}FX>@ &'`ټ9XiRf9y e'YU S|x<\VagѸ>è~?rDkhA!%Q e8RE L_3CO 8=ŭofĊypBOv"z,EpQ~YCIY?.|M1t^2,HShX=+Dݶ_­@c9g=bxi1tEUR/er /A݇̔}V-C|Bf.Ezdg0L^p!q<#:ڂN2dIo>i<9r `JL{,GJ6}T"հ w76pN69ֵj㝮hդ'x"u%)~]=V? \Kbp"z,?6. ^-4YON[ʚO\ '.FO<$nK)dVKD&\!VnbЁ,f]`@BКO_]Kۗ~XT#K[p,-˼fV0+d']͠o~=-+_~~~>\|̃T/~'thdRqhfpWxft%fh=IhflÍ|KFܹJg lY 2r e2H^y L++@ zE?&2 {4Dj^$f;a'>V1J_$gA #:sd(̧#x!Aג+%Z"|%r)X۬%֡^UV'PnU/冮%p98#зl~u,Oky85xtգ 8pO݀D*2c`g ЋPIM {tKTTSƏ_^m!) y1tC>BC=xoXWX~ϻ3mtj2钇γ(x5Zcf{ؖp4 OY $d3w>i*(#!{/:-(Yꬍ|(25eHfV6ɲ92OzF b #C{$zoiLR/dw+ٻ!ut: /ѣlrIV1wXbq:ՓO3fjQQɣbyN9ݔͲ hS:F>B|a%4kꀣN(=܀,BM05ڑFnL O?ZYmD&_@րŋ^=zk)3aeM&jrC=" +[AVI=}yK믪^‚CJIЯ&W4;P0M\6SEJ $ T g诨VXC ¡CI٧O&{qKA@UՀkƨ}p,݅ ve%_` P4PE4 oByl23)4M OEːiF'^nlW3E-;vѣuVl"o!K[qtzW>H㙚Ҩ.p$ӧ>u$$*t^Q B0m*ӬvuOН4[ 5 -ďuOG}jȑ#@z:B={T' 䘘Ucdd3L5_T<|dlLN% ҳ5jTv޼deOMŽ58nHVܼxAwD'l^TSp%]~@%[ IIbUW1"9IIIs/WFN-B-KdГv؁nݺK.rb`?աxiAA9?yIA=#:)ѵ~+HRt39[ .M1[~Տ]^?SpO5.>?."q1zbp1ԓS#1bʫZdj0͛7/u#lxQF̬R444Cf%^8v@>1ٲZc"Y%Cg5~MsSPG{27֫('WGKzr&FOرc2cryDDD`„ A@dӛK8tVn|g/A^ 1ToRb\dԣ !c}YQ"'OOOgq℠x ex~3~Dp/3DDNpˏ^S`QTWNLlك%JۓNm KEqlbciMX~qw8gm@@υHSHd,԰?."q.-18m6PO닀tޝO̽=1xb%7BLJӎ7&4/>>̳Ϗ'GLLu<<*IW^A2R"W-{BncJORũہA#i,XC{&̙^S JL `ܒ$~qyaԻGdMKKc:TsIWِe4\D ؽ\&rb參 0tx3' nVX@LwILgX4Fo`t<$M'odI֧fP*%T)%FO NcUDH]U\ULJMI̓|zl:{=Ÿ1=o'wA{;q+GJ^{1r eeAq653gINvxa8:_pRK;^c}eXGW16g,Bd1)wo/`@tbyW;_vԈKm{\ap3s+͍A)_`DMEiVJW=>nݺhg{<~1fZN6 p5pj贽N׬YgeeUlZ7:F@{0#2xɹCG'^j>{=\gÚvsZoQ"e<ҭ ۶ Q(/yDN%Ca] mFzC_.kkkLJ{Z}뭷&Ə\Gv1$~հ) i錚.^įV)ƵilIENDB`image017.png000066400000000000000000000600641300200146000363150ustar00rootroot00000000000000connectome-workbench-1.2.3+git41-gc4c6c90/src/Resources/GuiResources/HelpFiles/Annotations/Annotations.fldPNG  IHDR iCCPICC ProfileHTSiǿ-U tBFHB G`Du)  EdPQ }v={Ϲyss}{;.K HeHg C1t@@ ]bg \ƇA;&3ᅥ, F8NC$l0 kd fay!"N33?Ƿf{C<&@@lv"29<> I,y/NKK#˜/b%Jxn-e RYkߑ*$O(rC!%Oyqfg9I1Ly)pBJ C yBP(AAP1TUAF4t Cah z }Q0`=x l~p8N3\Wµ _`1 D ECiLPv(wT*6PZT ՋQX4MG>4ހ.AWm0zCb1&&b*0SK(aXl 6[݇mva#I39p,\www7'5x/|,WS.DJ QQOt"%#;E'x)-}|VDm;rn  u::t vI{t{u?Emk{od@1p105k53L1gx66J26i /,__\xȄljml2lJ377m7}DgI쒝Kz|76K5;d\<߼ۢ%re+c+~{Tllm6-6c:q5Cvvv%vW1nvqr8𗣉cc㋥KK-qrb9t;ӝrh\j]2F=㹫knfnBSn׻wy<=<<<#<\ّ%"psvuȜeWWXcfۚ^?Ee^nӺn6oި`hw^&⦔M@ `dBBaVǭ~@o嶽۾qW-a\'l+)ݿcpΆ2ٲܲ]Ew}ªqe^;~JvnQVqg-{Va<ύBzmTJfQؑGn8brZ| {K/w;rRd)ꩢ6mMD{R#NSz gK>{~K5~!H/ we{]{_qr쮵_vƩ߬;gvf-[Kv}ǝwwF Z>$ǹ~7L={yTXq'*4ȫ?2:Z/Όyz+?eym_nLDO~[NV''|H0ҧv{D}y>~MMO XB֬@! '$0o&Jـ<,9=sBH$+@8gd&XZ"#֤bz q|jV}@ׇ9_>XĿiйҞy?|OPiTXtXML:com.adobe.xmp 251 155 6@IDATx}@UEAEk?ibZJ} L%fDIZ [2Ct1S+QUXQ#DEEs}]w3Ι;wfáCnAwt %_Ct HpuKK }d3NysW<[v#\};!5Fkr`l F&5hW2˟NRJ{+=Fpɣ*M%zXWh 7\g~!h* 8رy^o&遞O8Rč8tYOksPbzMJK :b?gqDzG )KQ< @pJ􄺕2V֦V s&noB΋#tR)@Ϙ; eR|CҤdP{4þ(SL^5G?DUsBχ\zLUxtnRUΏ{ {uɟZߊ<>#Ylq8}A.=x q ~ЙnOx/x): GPo t=w! x5*vx(^ RSi|nEa[Q=л 1 /q-‡BvsP3Δڀѯ%zwalpZtSxF(4;u%`E?_L)wa;MFwg{zKggD! o.NM-87"#ֿ)gCҞ.ՙFZzl mŝ(NC8{%Mչ!M;VA 5=A)~H3-VݿpO`k8}*IlZ`E3͛W= Z pIo=*Օte+Itt f G\o+ tu%i.,]o+ dJ?1gZg.̼\cvbݡN|n0N4DgH4VGj}$,ila5e߾r%"mE.>~b8JR_CᇤmU!۷]2oHaa ʺ :Ԛ364D/:߃V 4hPoauMVW#fQV[v_r|G&$E__oxd ' S˸6ii)ZOo7؃}z4Bwzb'R㌅h?|R:wX<~)\]/ogUkh֖NڍŒeii%S+صzBÑNiMyo г~j qKYNN5{h%2MOݺu+]#Gl*V;׬uTj2>гJ)p$rh52g_)[0ͭ嘡$}yǃ!xz9 N ̍#uuuE_H|B/jWiKmii)nܸ!h-&JpX8YAOv{=hB$UWHLVfEH~J0̋tO? K>0YhDV41o.}˯XV3Z}g8MmE}\AZ{Y,de~%pwۥVװu0L'EoX\xL듂pq+*U kCUdl,~rr[׎h7,&];UaKE{!>>0f&HƐ@koFVOM{9x=Ut& nb2:"zt| YL޷TS0|&|B0|ҫ3^@`8:9ìA%ҩw, "z}1Vӳ^!\[g~>wk֪)vs [_~ gtӯo9j|)Pk#{M ^^Yѹ4GNGŘ55 AЯ8$|{\Q b8:vI>K"2O3Xb2#u]}ۏW(<+"Ǣ{|.䤓'Q|}Q9k_;q,<<bgρ}Kjc];V27Puu}Z_/>odǧ=loۜSkosC*]t%FM13P/9x{op =^O"v#գg0dJv[8+>W<"_َ;sp5oj0Bσ0P9gJ#iK.ý' \dg}oD᭗`ŜHTwڦgi$|[k᳦~-^6H;}=[÷g m};گ1k ;9Ko{IPkx[^в}w z˾Rˍ-ȧ<6ķCzk<>nҾ.Qr71Gⷍ-F+X7Gh7@ LN(+ƏttʆZ^y!a2Nnyͩ}CqA[ iί=zZY!KG0I,/̣}ڸVW ̟K#dXx6t%㺠9|snroKK !5٧qe˗EшWbR^Bty|_S==y}Ţf]P|N\F DB7yz[i[$/.Ś1c?x?9]O`^pW!=iWv-*_!ĥVS2k_i]>v(>{w 0Cgfcwr7y|ko]unǼ1g?RfÛ\rHKS0;0V} },}U83<Q3!wKHC蚶gGdTxJ!H1i7; {| >!Q.7K"ͺ 3蕡ta# vs srWWLs,徱ӻ@Zh,OWjB1ou'cl`cMĻ~ v0`Z+;SoY[{.»-ӫ}`́qtſ;Z6O_`Shdv6 <?1h^ pO7*WBФ0>GS*.4W;:FT>uA9YVwȃށ#^B齥\Ez7˯4!}yEoܺ;^[O}W)e"l _4C$}C޳9s>H{M$ +UBnmhk|eˊN@:5ieS&& Qw4\͖"P瀦nJ 즸Xbb%ztt2n Ұ'm|i άj}V96пEZ/egZ0ܺK7%|%|-η7>5~[lӛu̕EZne*n2(U9ax텯2)Zua׮vOY/ڤWմTO%,E-|J'[;96%~3ApG3y2-vªi|zΪYz)j ~[Hѽ/Ɔw ?y]aHCڃ5p]]g>ZvSgk]˫k[j^IFщ'V58gKq7f;#{Dh|+>23D[?'ԦMUTtֆb~P-8K@ɇOٟK{<08FyҾ mԘpq?|K?7n׺b++_|oNF"5S<} .Ci?1/ꗇPb*(Y5W-ufaK?=u}k;nN'?~!].p XZ| a5o{7]GFsDepF:OÓF!&cɌ_p.¹AnT6Wu[kVꕝ؎؍exa 1}J\Gzgwd ĕSdtDnfMW=@pJPӑdm'1WŻ"䯾NPVqL6*^:TSӛs(ܸ)7Go e8u<:;~Ũ>8ӧxr)J o;wӧ輭bH"BWRNex*ץK:aOIgqVCU"̀ ZxꕩTtZhn31<^|OЉnxbpcd0J 1¥mՄ㑙Y <<\JgxeJz.;K@،^AeVX)R\yN8|65O bHJ+â]Ӕq{@'+ 0at% +jL'O{'yt8yy%]Ν%":Z(n׮]%DFN _JT12(14/Ɯ_%ۜɇ̊[bĈR\we:ׯV%?Ig ~*7QiaEu\[ Ln ]~\i WW2O邃<<_j J'Ug{sNz*Sg\za%2,(4WpeL… RO.Wm!Rװ̻ :7, =;oMOY?rYVEPNa%g}ccqlSDHK.a)G} D|<(%iذ0VfwmDyz߿31[kl`֖W8V\UJfhԒ+|rZi*ل$trfEYKՠ*')%KYW]Ȁ ZpևtՕbQWiZ'xdxoHpt.Į1k_pcǯ&%ڒ %kR.i<͒RyU?%e顫V6Vu9.'"lEZKO>pvv;$cGYG ~vdРJ0 6aY-)'{ELGQYv'7ng3Xnb7zjÓ${ M@Ap:)`|,2qiyŒ<.Hx,5V(+\MgjK}=UՓa6E9Q+~Kև1wxix#|K۰T"߀ت}Y+9SYƤBxkb 4ٿc(.>@]h^/"Xϑm4W%o4—[<1CFϋPߓrXܢi2wA Ž+ DSS:Jet8,ߎ ]p쇻ZlFw43rkܕ+Wr obt]y&"'#3;E%h\}pMGFv2H$ tj$Fj yS؟98:h>V^ ~tHHAp8m9Ñ3+aۄ~X鑀`?K7E M AȘGH偘Ir52xF=1~5I1.pw|Gc}($|W"btoB ߨ zdzڅVV HnV#)1#&S-dtfj~,NY;Qw`QF$.Pt OdZz|`kgbV^8T8*uz<^D2W(V|:~ٷyYGo<^xc)~ݵ?˾rTI{?iPs2ROqqR;Wdd5 3Ⱦdz B)cmX9M*˭~a!?D$!`6l)<'۽d2miHo20NJFi]<9wC0ND,II V)lFc0.9iK t^ Mq{#bT%hs>}2m8bT]r;b0$ĂId}H6kpܑiw I[qzo"}q*JbĤq9H"gR(4Br0kq |;&>(ʼnf`uz,%Gq&bRM4Yۮ,-D[,|.|!7>H&2g7`%gL 2*BT~F ?@:D_~MNI?^}Cx ctD'aή$Zo{SD.h(덮qu*MU}~ی#fcEѤ)]w^i^|p!$UW_xsIen HZ!9M:/44g!Ӥwuh %nqC*!ЍZV4hvX44V$MḟnJ +tNیy8xHJe~FWQ+3qgO-,KWi-uAsQ#ZtL};B"GʿT%*ūĮYP"edPE"{8ؘ'GX勄s]ycw%e$ie˰vZDŽyݪU$x[m߾4:qR'x[hG`•(>p*m(Uι]Rvq3_ɫp4pxT]/@Cc@({̟MQt%8b2B#UMΡU鈦F'WeN>hć6]wp 7iI_є D`h8.O"~hJ? U<3Rҧ*s܌mI CV| &{N(daqSSd9:tNw܍&K6R~pCȑC;v,֭[-[Þ8q-05EXOͻg8~\ F @r :wBa "_l͜. X;o6 ƐLc$ޞ;4r8|z+:BEvR؝!Fع1U7M<ЙVO1ѕo8lx84s#F 7R[KE;Y<;g9˟:u8nx=ipq_+Da= Go%*ə >Sqú],n'n _j.>]0"OE3~-WEj\\cÆ `yDgyI᥈?nɋ\#"nS&󐛽 O 'pP#Js>oK8zE)ďLZ+EBc_edc ''pEL^H[X5aZ~[ Eg}(Xk2AnFzN|q^;4{Vk Ii.y?}9 fI[MmL(zm6IRWWL[g& @cAD%o7ʣq<'g1C>@/%Nˆ:|5>2.)g|FG>&/gD|#Jw Cph(,ʍZ !󨳚(-r$|"ˡoJA,l; shU<& ~"Eѧ6VC1_&G8t󝀌4$pyS9LJ%>e0ZCH6~ݤE_^1(f/6-4c%u`:5qj|j\)))XCC3111OnUx띁F\n Oiֺ5*u-(,HSTxzyzU84Kit:w^h*iidVՅ,*RzmqB{/s ZsJhT@2+-ņp5R ]Ѹ4ڍgiQߡ}kL>77 D!͐fТf nLx TRPznjzBx[#{yA7bx"+f/jWg?ԩV݂ ʁm 8u{iT1_^)"-Q:Wh_fUd͋:Fe+dΔ07&-Ў~#vpʠdÜ/SgX3tV:jޅ'hZ\VdOhlԺΣ jHOuڒ˝:S٥Ooխ^N.I@5b]iidWOK+C%PS Q]O|Alu;lE%`̥qsaAZeW;W>Ⱥ`_g_)]W*< n+ʮ%PhժZl)]'ʮTxbR*2l-˖XI%K^K@Wzxtt OxS1%P_$`x[t [ ޱrL#B꼳;<@<a;]NJ^[kÇ:y$^ku]$֮]; Pz⋑]ʰ8uS|m֖t rssؒQ% K@] zN}{z859m(7?߰A0S* 8 kIp'k lhK8!yt/; ȣp~gHNQ$P#e7'ׂǁCԖ&N^=%S[G/slNz+6G\M*u@h2MEVJ=5x.9fhq8 _[P<7XW*e/ 4 ٹ)PY6n=EdeN0f&JpVIrY1;M[{<p*n2@q}c%`*Z"vRTJp6([.&+KÔQS)D6FX&+*[ZIO7hF+tV~&]"4{vm˹R ߋITNS6]R,K64S \WZYECa#]+fMwP 7c0os6˛Rd=fն#p[{%&|3T"p*;xd1dMa'͐ qt&aolW3KF%nčOjM#u̅($_YuXw.T9mԩQ]$Ѐon+hkvI'!?`;ʻJ W :X7̝ 3W"+l$ỒwF_})ݼ 'ȾgaFtWAnӏ7ppiT^6n `'9xl9 G3\<3brG l{gWҟ&.HrRnn%& uK}bQǝt TN砲)!+;IL?,j04D^̬9FDRK& 4`EfELf5G _IM7z>>#$d'|bnD<+zs %η_eu+ ʸ2`k]H@3+J_۫2}ݭ} 9 Vxf@ˉN\TEfԖN9&З]D>esrnd͙:tH$an4|mѯ8=еڷ6]Ӫc_ 1x@*/~}QMWk׮FiX(oU#e{Sʉ知F*hAhay#pD{6&u Td&bdӸ3$ Z`r٥YŃQǭ$/7uo 85m3BoYO% &WFp&a~P&PD{ޡ0J(:SRk]uY[U]IeWEg>XM4[tVBIJ&]Nju„ND@x-4:(ui[GM%mRv2^iVgsJTE.]}[@ˋvUs rˀ`A}\\],'@WIieSc)7NW1gh%ޱ-@5 $vz#VZfx56"~">; rjb5A^eSn4~l`On5eƟăQk_/Kp>}0179eJ({"c7vZF+ Hjvٺ@}+v}y:$P;BL6oxNͶw OhNHDc)!ZK\ZUOO<3~a#1nFl.7-o5%`c;&׿bJJc K.U$]."+z*ZA7nkjU@{Oee 2pykiZ*)ߑҍ49i #09q|*BŽ|EK#!ҘQS"@V09I4w'v ~+>*{-yt83|d 2aU7 t?S tvoG&]=~T!P%UΧi d7O3!.9Ӭx<9 i9i0Ks~rZ$_*^M!{}BBiӏF˄,ߔrai<{ SLW} Hr_0b<ՋF"aq8^$5{D9L,&u՘PLY+^3y&." 8v(>/b@6=6Zv1}B$#f"~a)pw|G#kR6"/ü4㩒OCy"&;`a.U\={ Tqr,b#'u1g&Y%ى@VHJJA=97RM1 -NXL 0D/ƱlG ~NJOkXYAZ>?vRYMWճH'asu8JO^ѹUlvV^+`u:tf#b> 6 ,gɎH2]k>Q.RR D "m7Ʊ) N j~a{~&$MLڞ8w ڼ嗟N v$|i/ad!hf~>rͤC£+,K#FU|u+y<)L3Bcյ1>tԻҽPf]ӣmi/ٻ|cEqeTEH&_9{#ַRؐ*hVN:Lw5_X l$l;TQUˌm'v޹̇g7H A]^>rlSgl|UȮY5W_sc8hZ6xa|.;*Tn0 VFG3ڭv GWH0+"RةP,"Ly_Pi,ٙ\ \5;IJ!}HRt9 #Ciֿ Ǹ U Wwt!@y^\y ӆV2J.OkҔﰗ-і' t9 ||!",},wW@ww*? c ѣ"c2T!sz'NU.`vꨝU^]Kq>'ቀX4-Cr`bqMV9&=Zy {Q kGYU1ZEI)g h@{DÖՋ0E,]1p^:qkVfSg\S4MT/!C=gOEҳgJQ_N^f$Jˊrإ"MZ~u*1WO~r/nOd~Ohұ XfܐrCɠJ`kOuސLyQFHOiHKNдNs~T䈠೔mֽSi:2 4M0B •Tzۺ k؏eeu N(!3yEDY!Tlי/%N@ĖovGγ \VvfjMw&"")[W¨NAAA8yn55qgW-~A^nk)7* !=tAx Ċ-mwwxiKy]1BUְ !X8݆t@ޖXL_'W ]:yŊGqIo S]gyԅJ$ c[Ad:\?gc3 ? 7z hAӶ7kO?Ad`̮s1W~*|M)޼9 WܷWMw64wc3ǝ:ܔ,oo*ѱ*}͞w-LC}J%eOU_͌EXn]1YÇq"#`6251Sf@(8++T(p86E+M}+ X.ĥу$PKx TߍXi*Pde:J452_pL㕏G$ F^wƴXYRRVLr+Ӕypiדlݧ;h.h{W>fںdC!v`G 悖]Pk6lJB _(ZY^2\X|%t2_ݕ`DWm({kJw`o0}cvpd2ԟx'9 n{3E4'ppi)i8M,J/K٧:S)lϻKq>>>Y&)dx)o;1Ƈ=}ZJRqg[$ԊEK qu+1e2]_(2'67І<8N?i<;7F$}>ZHdWH:8,͟򷮠"@T03kxmi"`o)mځSL OC~E(ǥAt:=bkg^B'K3 HN'3 霸nYv)N>3GMy^٭9ﮖ᪯H 뗋t.8o&_BV"45~.|AS|?oweJ^^8IWm;"񩩈YWءgLOZ[<} G(u%NH_(tF>:,HKH ܊.|;޶|3fg7O'Ґt6oGNvӱQ*W*:".,aKF8ejynj4/: WZRvaI9{'N`7_D!NE8sRЊ_*X^O[cǎѣ9NҌa9@EzBRt.va;K)@^Jh.%h!>SĊ.9Yw>fYv7 7q"1e( Nv+Z:Ϯr7HB)8T@cv=YxcBaE\k4?.qV|1+}%SpOL4:}bSJyF?9XqXDn|wugq3ccpB̝;=(rSm*dJr6(EF'ʘtvUSvGWak< 34#]R Tlk O29: ee7yjL+v|& iʿvIB'JSa1<[i7tNzϖʿGR4 Ͼ: &#y­coZ9m!N/pÍ"AJ.0>4t*C|B4fKF~Q/B+ #M pNM]gr|е>yZEWa`:QKA2t]wwrl [2+2J28Vo˳¶S{00ri)#S_WR BQQNUUs;s} #ʨq;p<vbg<mUZRsX| T{+.)*[a290 =SCi^ o-(k50oUU5~eybts=-:N[_tK9_ϑ25I]A]T3{NX!Fڬh ^)nјu\ ڶgNɻۊ[7ǟ)42_/){m'f\aU&-g++i+Q75~mWgM6xW4T7v>cEO7'1}7oKzu K#VsiU5nse,+qr: ϲ."n>糌rϠQFt^{5L0AB'm+ +~Fܰ R68S7X!m(ZD~u|A}[^(X> l0w/kI#{UATiૣskh:aՆ8 U*YwBv+@괘Ν;M8z(+_}UT0B!ky6)qtBwO=8 RC|k,sYkռiE]XBaDR"}Ge^*߲eK GsСuӧKi6lPQӷP>}e:|]p?qe>SWkč^X+mpqq?sVмysds^YY 2rIk׮I?,*7b͝<_&)lqdXJȸ<4'U+He۷o4] ?f 0**׏3:˕ +e-pG#[Y`>,Y\ִgypd,XKBaa3af@+!~&~ú&N޽{KqX,{!qC0FÍ27>V a D:Ⰸ3.Vsgqr&n9(7T.pB8+1Ki;0=v?;y1ߌয়~cǎ1,2nDs]89a I,y/NKK#˜/b%Jxn-e RYkߑ*$O(rC!%Oyqfg9I1Ly)pBJ C yBP(AAP1TUAF4t Cah z }Q0`=x l~p8N3\Wµ _`1 D ECiLPv(wT*6PZT ՋQX4MG>4ހ.AWm0zCb1&&b*0SK(aXl 6[݇mva#I39p,\www7'5x/|,WS.DJ QQOt"%#;E'x)-}|VDm;rn  u::t vI{t{u?Emk{od@1p105k53L1gx66J26i /,__\xȄljml2lJ377m7}DgI쒝Kz|76K5;d\<߼ۢ%re+c+~{Tllm6-6c:q5Cvvv%vW1nvqr8𗣉cc㋥KK-qrb9t;ӝrh\j]2F=㹫knfnBSn׻wy<=<<<#<\ّ%"psvuȜeWWXcfۚ^?Ee^nӺn6oި`hw^&⦔M@ `dBBaVǭ~@o嶽۾qW-a\'l+)ݿcpΆ2ٲܲ]Ew}ªqe^;~JvnQVqg-{Va<ύBzmTJfQؑGn8brZ| {K/w;rRd)ꩢ6mMD{R#NSz gK>{~K5~!H/ we{]{_qr쮵_vƩ߬;gvf-[Kv}ǝwwF Z>$ǹ~7L={yTXq'*4ȫ?2:Z/Όyz+?eym_nLDO~[NV''|H0ҧv{D}y>~MMO XB֬@! '$0o&Jـ<,9=sBH$+@8gd&XZ"#֤bz q|jV}@ׇ9_>XĿiйҞy?|OPiTXtXML:com.adobe.xmp 40 75 <IDAThYoh[UZc~`ŦҎkѵa[80: 2J~hC CAU,cXP+80uC26]cY>ɽyn^=G.w=ws=ܴ < s-`2+Kb`\6g[2*߄Dp~\[&dI)YΒY\xmڏzT.|Ò&jjứ e3 w^]%E`68^Ǘ/V RXcRx)%3D!|s9lB(iD:9Hj{<%T+9.t4:v:n/u7PЇ={:ȏjh{6[Gk+/,aEFt=]hae Y1]q ;ḿm;Q7a@iɱ1  y ?#;M 2#ؽŊgl#b/EFCÓ܂6!|Cǖ*q{Y/fW`f^;;3M0 Ǿ7%hƿYIӐC&$8?3st6k1BBWWLlD"p`r_dkًo|S>D%"O ~|>< jTmggMˋ8ֵsLi4#Om'k7NcdU֍S:R }YuڝWQ>x*T1Wa*vCLWj܂2caU X=샊7Y@_qwBI4?ҤQ˧EBXY5$7#+XE`64̲pP׭\c,NqKAngjށ$x^9`a1-,((~&6ck:("j"])YЬFSVxIn7QQQ!5:tvvJמp0& cnnԉTqKgDfROOOX__&gޢAYMLMMtRI"U|ss3Ǟ7@g^cW2_-N.I ۊ>(c655>GLVާ8zXbNLXѨsTG6C8.~ecʤ)Tb#fŕHޚ`Le *2# Y+3!;k#xGPu+("\GeG0*o>ņ\To~IENDB`image019.png000066400000000000000000001003111300200146000363050ustar00rootroot00000000000000connectome-workbench-1.2.3+git41-gc4c6c90/src/Resources/GuiResources/HelpFiles/Annotations/Annotations.fldPNG  IHDR* iCCPICC ProfileHTSiǿ-U tBFHB G`Du)  EdPQ }v={Ϲyss}{;.K HeHg C1t@@ ]bg \ƇA;&3ᅥ, F8NC$l0 kd fay!"N33?Ƿf{C<&@@lv"29<> I,y/NKK#˜/b%Jxn-e RYkߑ*$O(rC!%Oyqfg9I1Ly)pBJ C yBP(AAP1TUAF4t Cah z }Q0`=x l~p8N3\Wµ _`1 D ECiLPv(wT*6PZT ՋQX4MG>4ހ.AWm0zCb1&&b*0SK(aXl 6[݇mva#I39p,\www7'5x/|,WS.DJ QQOt"%#;E'x)-}|VDm;rn  u::t vI{t{u?Emk{od@1p105k53L1gx66J26i /,__\xȄljml2lJ377m7}DgI쒝Kz|76K5;d\<߼ۢ%re+c+~{Tllm6-6c:q5Cvvv%vW1nvqr8𗣉cc㋥KK-qrb9t;ӝrh\j]2F=㹫knfnBSn׻wy<=<<<#<\ّ%"psvuȜeWWXcfۚ^?Ee^nӺn6oި`hw^&⦔M@ `dBBaVǭ~@o嶽۾qW-a\'l+)ݿcpΆ2ٲܲ]Ew}ªqe^;~JvnQVqg-{Va<ύBzmTJfQؑGn8brZ| {K/w;rRd)ꩢ6mMD{R#NSz gK>{~K5~!H/ we{]{_qr쮵_vƩ߬;gvf-[Kv}ǝwwF Z>$ǹ~7L={yTXq'*4ȫ?2:Z/Όyz+?eym_nLDO~[NV''|H0ҧv{D}y>~MMO XB֬@! '$0o&Jـ<,9=sBH$+@8gd&XZ"#֤bz q|jV}@ׇ9_>XĿiйҞy?|OPiTXtXML:com.adobe.xmp 203 298 m@IDATx} @UUET i*c>JS+PGR2r5"dF c%Sg5f:iHa.(>PIT|` ^>{^/k:{k-ބ-݋@x{{]4iRqx8%M ~Wx XW95|nW hrY]MmpjoK h3mZF9sӜmPe ~;L[Nm+Hr9 h3KKޑaiAZB=0+xyՁ:*UC[ :T#dr־{zrՙۥKwt =ٿKcש+Ԩ߰vGK9YXNTz$5 KEރH/˴O+CΛObXi7q*&lN:^{:Vi;0uڡmkav-{ؓ^` E~m=WpF< Y0eh\d+^}VB&^+dcӳ0~HꅿҬevI{x/VU"DGЙˑmocr[~zQxeK#gyXq#ަ7}{b<^ r)~aĜgҎ_=1m"XӺenٚZûzã-g/7<:E)eKD쁮`Ӂ/_*]. 4ka矣u+R=8Q& t]w pY1Q `zrݘ>@g!2r 2?oaH›خ)J[3fsͩXV,'Ot ?(}}tNƆo7RZ?7; #h8gKT"/Lj;tǁhfBD TR%f. Epp,Yj Nj#'_\]Cн{\ŏ{JË}(OG#ZaLaz`ZQ>ߠ߁V,^fAqbP>` %}&=k0IzwBԺ]%kݑ t (FvץÖ˧Ѥ,ET=C zlx_ [Gz{aĿ#{,\Z wVhP%lk8K$kۊv: tO`Q'wO4o #=RNsi9ilu지af?)P\\lBWvʌ}И>zYn߾!Uӆ:jQ"_ׯ'WϞ=m^-]^N$C,\Jhi-3=j@.1K &f1Owj^%[Ỷ/B쎲mQ Krl[vw=oPPyF/ 1.m e60yP]9U @Mqҝbÿ7a.xvzyJIcZjQּ|HcJ:mjǗ[m֭ uv(?U6,] ka ƍ@=K&VEѭ %N0ˍ<|Z g0rS4W%fv2?=9K]ٮ -YܠpT4ĺ=~v~]#&cbpS[.>qtY/wosؾ?^IXO9|t-ھ<ګ.gRo > Mt~COJG'**ܝ!!NXN#_.+K5}RPގS~XxOі: w3@N(E/<}<m.=t̘6:2T߹Y4өqrׯJuP -DE>m.Lc^qz3UmY~X,}>&I_Ew}<$ gOgjf$JW+CKx*H%/hTAOPr|.8|Ut+2={hB~<|YxGE'tk_uhnq ܰʰ4<0۾AR7  ƣdMhʛl8s9^_ؙ0 \Ѽ >]*<ە 3Ҕ"۷5Rg6v$,|-<FTmdLPc"Ks?p| AD>xk_ lG!K?a쎨1}rf:((g)``3xsQ 9H\=ZJWtFȟ*,KEy|*ݒ!D8:yHিb;h27 u(wL~ cXxq W~*/ 5a0c u̼Wi<pChC<4<K+ (fZ=e)aaWwJ7YJRƓ6QNz/zu?![s1"[ȣYIK'I#ß1_C))%>t!hzXc֯񏊒z-ѢGx~jRd$pE _Y 4x 0_$'8p~!haeC6=WpT4oٜhNWق836LjG 䭚0OӚ\e)᲌A\|ٮ1| ϝ;!C0ӌwu!U=GE6mT履~^$}ɫ~ jҺޜP7HJgE '$?t)˻f]S% yp:8%ͼVǙ|TY]ꯤ[z2uV?vE n)gKՙKS9^CաX>th^cRu;RRRgϞ*U0`bccE<ëx8%O-Mi:+ NtWw\GszqX;kP[uN?tՇMHNi@mUg/9KJi#JToakԫXBNtR?U> %NaKq_.ϒ8%͖R&ra%?qZepP!槒YP4%SLo)ixqYԝj z_y=:)T6qpuXW;OOW┰Y)}9R_4, qFrfӾ*t%}V5 ȑ#i&5J ?1OKS꭮*G)[8?x~+a%R려WWꪆUy*-=9NgbfQO&>]./` %]󳡺 d? y ׮]CY_9/߬Y3C{P6qW8]UJ:Y+q\>{"#Qp(a[|_i)m1af&URdwE8(n'ᶙ;>>~y?LŊة s)4G)fQ:ɜY8NQ:OvNSHd+ONyZWTW~N Rid!>Y(W\M!246(o h-[q Wf@G!tطy{=c"*<~ZjZ~ W 394Ԇi(h ('0&̢$8@ FJeV8 czvqtÎ9{V$MC\Nӄeo!:[q7I]㩩7oKd5k鎡,&3 HG;[g*aˊ#3Yمf֣/x=x %ȦXí0u\)3V y:JoԀAFӐyD0:@q-駟肍0*2ao!_H)(Q1_D=KU*Wk`xixuPtMC_-gRh6 ^_<)BO?n`|U[nL feְ\|*W94ݽBPa19ȍ{jizYiϏ{~g8>%g uzj`1a%4J_me0*f-,2[kr%\2Im8X9Qy)2aLNb-ZAj2ϴKAP)sA(/@d&OK ^?ݗxہ30).tDL_ىwDSxb&IN߫JXK1NDƧObL Q1X#)) DbJ38QH8dNWVD )7/_~E$B6UK3[ûYGِOa0Y N pE9Gىnpnl6'GݝD~$aB Q )T7#*cf|awBfsБp녴8+{˰19%qN鄗L/}/=ޘ-U)JNJ_GW&MGe6:ڪsZxW\QQY@PsFaR:9Vb+ǙC4+4هƒgR$pKZ ׈ < f1zk͚xja)&ZFl<9#&|XjL vfQ+ ~= ڵk[,CgR|\D@΢(^j/W.-`'I1U!BiOFj)FʚDM^xCO΍Pb531 R½4јG&W9矷/,%Caо%@53#;[.Ce9t@IѾ pCJu?/ et)o4ޘmPx.: a $)(EM+ #ab02d{٣GACIIm]loa`UsOŝ_Gd֞5ˑ$|$7=LŬNDN~OYHYv2/ay;]H`pZ9PCfqNniZ ^ Zqvp$ b3#fxȤIK; eo)h[ xzRH;Z(!'%.'3p'a&BD`]P( n.\_Y?{,~i6Sf͚%ں %شiLHrEJCHou(M6uj?2(VVRDHd9ԗ҆>54RW^۱6$С|||<ӑ~G]g1QH ڏ[E^canm9[汖:_yshuI7 Ca۷o_ǎ _61q\,"š3s hb4FLY~7$f|vȹ\u#WO{4 8Y3gK9eGE1oAQ>O2[p}oqd3?q GYw -0,SْV`n*Y|izʖcј”HwHQĄ,+қ( _R 0jR,ft x'13c! |+~ 6_s2)s#^p=zҷAoLkۡ Y\A__X M؈y5$W.v*eXhA ˲],?u de,-EuWraةX_J~=r v ;!.{dn[`Θ};zoCfW/):.^2`L[xWJ+BLYːݎBY].%""*< 4+-QBV= N =2x-KĥԹ4{J4')B;LkV)P/0=Ejr."*{U;vLQRhxoq9Y+Q෹~~.`_JKQD33-i&67I9BK!A}QLDr$YA'v"bAbf/`51 QΩri۷ϤLk3}lٹ ~.yؒ[!CKq"?n.܅h&`A L; UI '*l`ABmC~fv56R,ʱq]ޠeF&Te^2 I//Evk٫?ωC?ar0;@?yv0! {2Obވ+/gSF0eZl( 7 VdHߚ>< =2coE0^ODK<u;-Nh_OGgӬᓓ[T\nd͆WqMO#W#h)3zOJH TjX&%ZH0,5kAoaoW1f.bFeg.ۯG#wbAsebFa}B?Y,9}*jSbG]㘱b+FaG":euc06jya>[aXhK+7A%f9H):+=oNւ$ҤxM^-[d[,'ϖY„ r7-]7cؙ/X^/лaz%++*=mZ,k_VBy5Cnykd9/df:bR٫fQ2P}U wfٽ{Qw潡uί0RLv?o]jg"c tUƶWc UmVG[#9afBn=:]1T2wj}[*B"A.Y[?u^ւiTƀhVmB_ VXHQsl`(dOla7h %Y:VRM䡚+9XgcEJcX÷ ,bYEe]://ղX23-nVRgh/:w:!3Lg^ya*PESIxյ`<@J`Nd@$R= I)dV8n (̫̯y9=:<5|S<* =w.r3:\,X9w&ήt^1bfq)F*uQbXFmq#<ۘQ9c6TA紛3oXK0:/x*i\#)kTi8Fr@*gXQ/U>37:2, ZSZSG;I^[w Pɓg0TTTD2ɠFo pe_ǯ S4Јb\:Ow!|/gyJ]~MԩSbTp`qO%K4d9p(9 ]NNdV^wd^t|wĺw"' 7μR@acfȥe҆,Ai54cR[ rs66  "3չz#v1vFMY;շOI e*I>z :M,mZLN&{gne<~AѼF, ~lddd`|RVlb9G@+1$h#B΄|+{kxcd45~#C1sRH۔Mz]Lqq?d DA q^,rBSpڸIJ,jYӋPK7'DA1X/9mUh2iB_JM."j YAFSYmu΂|8!!LO'!D2A0|f3kGNQ-JE$xOۈNZyo k0]JfqꈋbAYK'?Kf &٨E%Od(|+إ}j"}*TE*S >b{a<򉠮+ٌN$?8~ nK>Ko$_6>)LΛ"`e'K`X"?uO͛7岼ňvh|7a-+z-E_?f5;Hr(c~(:cX1$w`;4I -2$/ !bK$ip叄BbHU/e};W X1.Gh4H_ é/nHu,Ve%'K=TKJ^v՞ݻkoλ (Weo E;EzCI;-aT6RSŤ0PXePv75$iFL&wJ c_L %N&r|aaM/8e"& ƽWa$ؐP`Y#d1攙HyHNi|u{1qwVx oS9?$kJI1:SE]oP:#_˷-:CnVɬR$@9HYȰ1C@fZP  w(ii#u6("&~DO3-}q}/ UY033jPǯ/V,EXd%oiΧ_|L_y#Ne+R|m mB)yV1g~V,Rn;cz^լhJf( )C`;bdfUѭY,H8 [:괉,hXbR)I^$3:)e5N|<PH}:J/w.]f@ǤI{a1 -*FE"n]ȯ?wF\oيuesfJ,\&2t-: `W}0oqje!hQ+R.RILny K)U2Ƈx>=^vw/'O!9*޷ԒTE6Z4YScO[d_\A%ͅ Ja|9h Lmշ8YeH%2dI~L2򵋑uҩ >P#!b-i"O:"`~F!P~)B|H۶TDE{{k^q9:v5*=ߘK_9C=J!a)K#s[(fcĤ*@Y\OEG6E?aɦHQec#麎OJ0jzS7k [ml^N7_`96Q)AI͹\ffa#aaa_E%dپ;7:Sb)+{QAWѾ a"I _s~L9OS+[IQh(4JIral(O-ɯʮy.3 G 7kd4%kb)0wTܹuP'/[ӆuoBKl8f&F`QL>5L[<#jp0\YiWM2I,?Z|I?];dd:ǧ,?_Ԁ?BJXNx Pm@0jʡe[1ɠOy\nB_1YbbbZ5> ?y Y?"ooq1˄;X-%VFuFaEx ?~5/wFj3֢/O {KWhEǎڧ[{u;K]cᙤFzufMto*.4T$u5NF8/O/'$Os Na9H[9R@ǖXoٴ io[5. Fn\ܤuO5)\ ~VF@Y,yj-_ä@ßYX?3Yمfo@ؑ_Ld^@J_=Yd~d۪&(+ʥ4~zQ`sY$,[\FM*vgv9aTf/j%DD[7+"dGc=_aQX-DiTi5գ7\g ^&ao%PEFd8DsNS-279escM/^:> #JI ,"4>lU`hN,y7D2zrs,\Y^y 8KMd𹎥 ^OUouI70\$ b}kbMq*Jjw1g'KW $W8 l-NEڝ8$Uyꑛp,OKK*[)2r9?~++3-ǕdprwcǗsITr&fp$@L%NLCtя#CzxǓo2<"nJIXv٩D6_ ^eq)u.MZΩJ]vMhk9}T\ʕ+8x+Վa;viB<> b ^ ߲XAKEO-iC9J9' wtIcBQtXh dxf|] >Q*\"qi$fHn| `2,滛cM?;sFbHB};Pdq4WQZ$IG( iI7?uTtT6MsLJ6g)쌰'H,]\{~΢#IjCQF(>S9Jl@_ܿ(\^tHNzBUacU`'E|w` mM, 5FsSu +ٳ'f͚%3L~Qu-ee+̛vv5Bw5 nqM[ %<~A)$J*@G`AB1Kٴ8(2{2Qgi,,l"-d;B\FөXß}Rk$Cttlckc D'g۰Hż4A#=Ix8ME?0SIz|Š8)^OHۋ Ypnt%c>=u~G6PUlc4MQ _S>IJ^_JE⊖TM=pp +:d4#?kůY3~n@||CV<eXY>_\Peau< X]EBմ(צ@cz.!B 8YЈFi2\2GǍZ4fiHoKkR@cz%VxCS,w,o>}F-Gɮ14D4 8YJ2E-̢dQn 2[PԺӵ-8>᷈,}P}h 鵭. 4e3~B'@ l%S' 9Cd\V!f7^|kh2|G+tF\|G6aelKKǦwx\/BZ6U H4:0 ~.,*2ђK'xC$-EZE_8=%gXUI$oK a܆cb]tUp L[ ,ib hEV/*N, b}obVv{me0uToϞ=5ٕdĐ)pR!ԥGk0z2݂Qu= -!qf*o^XMVXC6 Laoa,.(_Qxcp ړlۉ~ҺAK9OK?yNG~JPk@9 Y\MkXz5VX%ydЋDf䈅p3z7+Ŵ atfJXV?)d>®X╨gW5n8 q231]7m4#L 35j25HGˆ a#|,L.8[?y^캻026)x.OI; z O) S'm<1WtajSek G|})-q:V #N)ځ/4cVi,{ntA|?sªYYYkZ;PfA븓ϚIZY}kHxQ)RKiUp<5|rhͭZr4eaK<5VWe0y7nRߩzsƍd֤ITͪfbzTČi>5sL28veҶ[ Դ% 1`fK]&0h%Xo07͛73mj@̚sIՐ; aVMb$f&4Դ\QgsM?1$-$7(č4rSyEv"`]Gc|N(͖&\iV[Mt(k5ɣ4aRi4$Juo1\#ӺQK<ԙEG&ٝM"N[TAL`/S5qΰd MV:efP4$IDATZP:)Cfjz3ݸCJ3xyĐ4W#^H+iͨI83N%3p*JY "fIڏܤ/1 0l>d1 q'8~r_x>aFйKCO&q}7*٘a9EC̩Z$50,Fij(F$k/: IكͬKRWV7h,܅y%dQ|ɩRêRĄ#T?5:7Q`~WC߸X۳ SF&z8/äuI:wbёyFn3=TY1'PܪHRjb1xT fg2\$-㟳ƗHM @P;zD,<<,!yv`%aȧl9ge!8K4>0|“R̔n R=R*)% *R@˛^ӏt E;apߒ@p7hX ".y#/C!oySmJ~:-c*T9tK*ic͖:od\C0J\[22pj|?r4Cћ3R|EE=L§^ENZ~q`.'G7^:sP a\HS+~>5$ Q<zSvŒ)my{QEj`XXY},J2lL52.QR3+X8Sr0\YR.KߌV:麋)@1ZtbyJiCt/}"71Ō`Y%g$bB."F+',e^ѷPS-S&&i0%@լ% - 0GmM*VZkeQQP@cFF:NY%~;Y$yzR ]r׋H^F]*ӂ*_#&7}SКioF+_D8S̙3X~=[X Č ŌUd[Éaⱑf?OETL08E}HxY>MÊu3&f/tM\%zy;I Ob=TK?d+^#,yl21n%(de*-Z%F-ߒ-UY:0ɘh5vd,mH=}kbMD*ߜ9ERdΑmGh?ۋY0cȞLhFZSliA[ef Ԡ5k58.9S9#kI&_'Q?Ne^/^(&w'&!.@yQ6R{I %by2얆)aAXWXFZ G'! ?"6*)B;rӑ᳣5c3&,C)Oɩ"(# _7֮^mKÙ߮ãYG eptu<4SeHYIde:UGhMCAA>h X$u\Tg]H[8hI(U<$!Eiq6%)HvGለKAԂeX~i 1r a{)(Fo E'?Sq%U7dOeD]qam@τ8!aHP\M6ab%2XsX2?7 ӷRyƃ]!H<]9:XocNkȧظq /=?@TW  q_9CFcϗs)4CCx֯W˰hR/Y┬gP{BNZ̄_N*c#чFҾn{p݄SpQ,C/ӇN$,f^H`v~{<0hq͘f[sW-:^n=xBhqg < *N/&JIf(#&69>eR0wXޟnBnq.x| M Zԙי%$$/cS(kkk3iJBlD)HFXFbvVIo9iI^" P_a&w~JyNZ9#^l&mL"S{R~0UQ~[יMK!ual٧:\MVZb Uc:X `Cb\*#YڴiG}E39dHo &{&RbWgR\/=@¹c'bⱃAUuaeY|Qʰ$F$}a8u,bŏ p1~'uc;O&:XD̶eGGSE31 U&\ K6a9OiQlœhr[VHKߵ ƾA5b(b fx\Ƨ3FκvtTӠdBkO=!{_.U"6t)Zw"1mdfbz[Jq*(Y;Rq1J$( D:i[NǬA < s|!|>QS72C,Lg=ݯgj,[¡dSI҆7)XKLo1 ܅h'-CzSBD`]%!FaPR(Ul 7fCc;Z1Q!7$Z0g<[^ezInnL/൜'Qx6ekF0/=آBlp #!CcHNj {{ñTON\gTLŇX#&[|N1xqQ!JƉOK)}jb4xP\t 4/EqIt>pF_VJc7|c,edU輚 -o5dm=޽=4XuB,/_nr|,efaGn!,R9MrX ~|2YeY}1df"+2u4JfC6[aLTDWT~\<)bNf05h8[ey2"#0jPZY|N< 'A_ :*FC2֊5`5Gaԡ!f2H'"= x; }ǹ51n2jjWP̳9.$.\/]Ѹd;♔I|21 Kbv-fj{f]FjmB-3[),<̞=oɏ8VeF=A_"S [7:cPΡG.+&%e _&IE7CB!(O ֿAg"ga u.&6#I_t9udv¶ㅾ}EUP-Zo?zY$8Wv,;"a ~࠱$ _I)` 0Jsw khCcl|RYq|S2}t_"XxRuO/ &3 %9liaHv"BH. $ +:tTʣbt/%NbB O@m| 烅PRnB#O!p>S_D+ph[@bsph[DCשwtn|a9^xqeZiLzcff)f)ca,Ԯ5X<==@bDL`lQPI&FiV{ ?݊=.=DQ΄1w@}U®a4kYQhQ 5@ Xvk393Ν7*sye$Vq/1Dn`ŕ٥{c+5q[zw!s5z15EzeZR%B?H"!VdfEMhXR*k0ӭ-\(HT-)TfQVLI_ [[SާcZdnũ^AZiI`1g٤p%q8^T^I{/.?cQ sX& l˭G %㥸ґh&<& 58C]wUu`iIf̞׹q+ rO)݈:zQ=;oz*I^@@qQ(@ +l P0k35;~u4[_v~֊, Z)p# A1BOBL &dw}@~ݷ>}s/IvƗɾxaL-Uwl_".Z6S(  1nM%J +@/W; (۽ byrT*)Ҕ$L^'k[%MZ7>'WZ. ^ y'/WZ#3ANxC5U5=e:FV<]R/Zqxh`ZTRpsq0>h4,AIê))E`V6 Ifi*|%nmG! *4xy7BGO+\Er0/]ҟNiR(cJܩWKEfA:ޖol?^ Qu]mXIcĤɅ %B;|bY>xH *f #_pϙ%|r5qݾL7 9J'Kme?%R{#FSij``jLBɨW@֊kdz96|%a>*)E1?-/ɫ1'ʆ*EQvZaq\9bs-$Y^+',Yi) Ȕq) [70Y (NkWRI6:ƀ 3GRR\3xoxƧQ+zIJ=*|R/ V{ZdL! & vKx"u Bj s3C_ב<4u|kK',Wu'3'M|N`-i?cRW,H hNX}nj!^ @aMAT6([8P7 Ƃu%R2unJ | X-.Xh! ,&Vse0!2 PdMR vCv̢cW@F&h>n^ߜ d O_+  fItb?qgr-_c5vIF$͉JCq* RőbVn!x&֍0"1ڢc Dk er?)o(/AX~2Y邥@q+(Su y>U'dg%?ʶ`qRҒ#wJ1Y$9 'm~R#֕2.bD2~<(򬇓0mRa,eѿ!_ ^juЧڈr翕5d%~Кu.ct's/`4n]Ңod$DʷȉLE|#P;.̥4x?x}NɮOnY\zKx2~F2ȒO>>HN]'T\ܪ᫜!˿P6씞s@>"Kfqqʕ΃NܱolGncUTX4C:GYwfy$i%]mPK%d&lL2zō䵵P" h2ٳg/ ;w?̒ojd3Xlr* KdVYuIzn;Yֿq/Im#W͂ZhЍ9]+Vŏߓ};Zd {:D M#=]4ޔ\,.>aN[\@E37koy Iְ|,ej:ٷ_c3v!\5jeY^[-~/g|&A?ĻM2:uStvj",Q$x+U^A)>U$,WcǎW==ź v0*܆[E{d%dVvpW@LS/{.a D6yqD _3p>򰌀>#l$7*I_A 3 2.;+i;YNroO-Ǩ*AnG0PnZ(jgϞ-K,ˬYN6MhJo~w>evYr[+iWX5[ Vh 4Fae`R\Q3 -(bvW\sVUx F.݁h"yy6CWS'5z[#MҺ nTRXY97xZyw`n],5«;S)H>K06&؁dEc_ g, !ifyTdMugX/]3e^SꯤRgU1kU߳M9b,])6o(o]~]捖Voaw"^re8~Z?,E oUV 嘼8=l ]dԴ6V"=nS7*5.M*o!A`klg,% 3a.aa0wX9#nmǤK=3D*tSQ:~" N. 3Ҵk]T5*kư=Vߣ}Ȭxxd( Sw~|HG!0=y'WN)Ɵ#)x5@`2{ @^'w0~R8fQփ޻X.9 WNrq¥I KrAMg&ٞ z&8)˒Q1_A&z),H"|dB,#fS&ٚJm=G#llaƛYƝspK˱OBrՀ<,ta̫;iS|Ɔr! m68-Oif&QCUY[s.|5R~7n&. Y0EB*_bkZV`Y}S%h%✆pe_W+<C5j<4=| );dNq*D4/q>э6|qG?w)."8yNRYNpK^z%9r䈶Z6nhha,> Y:/oh9?FDV/a_)!q!*gq۽6 ݧ*-Й [׷l{θG. lre(Wd(U>a\o}!s}> =6!0oK▗E\A~bٍ7ިWy-}:u7&/|wg@ʡBkS/b@lkbcHQ3|h i.gMCej湳N.-of@~࿶S:+f&9qA!o `U0JW-mGnYY*P~s^\c}Nȩ @%~h떅+VȂr:5%/g|ΓeT>%.> .w5 '3G1['sO>~t/:z:9v:q#8qdax_zg9N=34i`J5b];-gO~EjZuA(!"D0q1GxD`c~GBeӜK2T\8[BUa" ((|on"2 #d/TFD^ǠOꢓnNv|B<^ZS\o: ~qE؅@"~ t⭬˚legtl})oG}>~t?? 硃ɦKDՎ|nэaNШ p{،((lG,VӿGeۦ R5ky;ri12G7*AX:twz:ӡ,_Ҵ:{dcd8q(!:|U뮻AJֱ ɤ'|hb!`#KlA+~k@5to'57.g* ;~\m6o (A#秕# t(FHUXxN'ه9Ȇ0?aÐ[N5a$D32%nY?,ƏuiFM3ƚ\lεÍ,9TfqE@ /"p0| Uj}frBP.vPh}dǐp&(L;ÇaˆI;LQ&`g|x]$;k/|fY2,m\6%yӮ '=o"sˢPi~ {tX v 0<?rx?QȳXFtâhϖ&o(`['YQhʔ)r ˘ 9əRǯqAed0mZcG^IW^}$` l}N~v,3a'jkku܃'ybfqf_oi,.e\0oD9`~eex,Y,9lD"NQGF<{O`Ih>=,kx_` ՄGNؤvm^e\˰`8!di-Πfti®3nTX0>tI0n2ddIENDB`image020.png000066400000000000000000001254441300200146000363130ustar00rootroot00000000000000connectome-workbench-1.2.3+git41-gc4c6c90/src/Resources/GuiResources/HelpFiles/Annotations/Annotations.fldPNG  IHDRyZnsRGB pHYs  @IDATx]\9/EEEEE{v=?ޝݳ~xbbŎbCPAPPP:,9=owf䟙<8y$#ʓ' dY0޽Lyj*i J1O5?# G¨Sj m{zE&wW^7rPL?9tG?،f&aXGDu]"k9t^N*GX")G y*___>}Lm۶w"##\27D(YRʕK#ݗ/_ROcfF 1@u0lnD}|P$B4.o`JɓȾ=.]jsPkם+"#+x{  c˙#`{NFQ$8GsPQs>,6m`РAIpD0c!E 81c⡣h`m OS1]Gg 8B%ag +B Q\^7]Q .ƃ#Y"gȟ??׭ļF,)Ts~υkE1\<1}R}ۥ]6hZ6722%/ uamm-,4FhYm1e~r}ƍ _fG#9~yyv@ 9= /@́Ψ NC5bIv_XW$- TiU[aW6Lv7\qc&"P8 QcZCZe}{ zT5xR bڣ/p aA<[l/`߳^0Af;Pf!n 2=0]::kkvx4?[e&#F`ݺu(R@/DeyyQvvvB;ׯ.X짋e#hCFfq1r,%FU#5_G X*+[ ڦ/aoIm:` \đ9}Q*k.c~c}"-@[izlĥ583ϡf!f! ۢ,9>I9|}Ԫ:9,adz׀=pc(;^ P%^(;{G___ #ruh,--tvS2Z_22?&맜 QH\]T2l@IUomJI5ZJ/&CcU<-W ݎHfi|~՗{=CxJ`D2j3MqeqbIb'CBQA|H%-NsU r5PKġ^m].=] __O_JЧkG-˦ *$ܜ` `蟽~l&XQΣG4+O+8,Qwx勡@pXGюMѣg#_yZo;CJ`Dr`[:  [ 9 7+`ں Ɋ3psb ͩ2cf+f0 W[ _xiT {z5K"k8:^0(9K&z i{g`s8\!:PUO7޼y{ GDDΝF tahڴ ,z[[[Qߊ+k" *'Gwz-yrQYWrI ^qvnfƲ1зdW13ډ2!7f('l.&OK )t7n<`o(+W߿,{E0>cS9вeKSΞ<%)Gp8@P11rS0vBBBY1]| N5E⧩ p8)C@`,fz AAP|`WK +mg4?mxG#ЍԳ7~ص|hY\FVp8(FL3?:ګS8G -H$Ŏ l^ * 9a>1g{7uK}*U6މi.X fzfΜ9j* {}_p8Y@|~yK` {K8GD}dt2H%lCW#pP@0YO8jvj6SBfM#.o}toDJy0~̩mH}$TD(vO,}jK1_l}<ɑ'EFlT)0?tHO ȗ9pTL)SJ ƒ=odž``FQ+}sd:VB0jȡ{`FR=SF;iVrl vL4W&-0{t S QO] xLmJF/aaֶT./c}\q,K#i+\9 aRX*;cqRJľ낉8lllMlKq HaٳtSH '!$R+lY1aoJM-?>{o%=RInh +qp~yQQRЧ89S{;z@mrt@ǺIi rG[];fh%t_֮ȔmpNk^fIxwa;U\{Ze:<v&-ި7z9ɰ yca&XEhxp.<u1r8 o6UhA{Y>ztn(> 7]juAyXC(υ):l^͒Ю\˞RӖeU YvTJO+<6e`ִG)z\px;'#G' ȏ땍;05f@o&: cZ:w#Lc4fl܍?RTZwWq~o4]\E6xi&4+ZoifC~G^bgpe&gw8U&_b=}[ aJ%0XcߍxtqDQZ]x58^y1K,yHJ%^?nz{/ e!,&Ln$)7b[  mcpu|Y#qe*~"Aп ybBP^9C7JÆFW*CѾj<: ^++B]?St?]@2K[UB{%΍Qc=/.aOYE Qw;\9t ;Ktp-_ŠM+1^x! .$ H} =D 2tq yn'9t8t.cgz''אF.W QlBq`.aC\whWoǁT)HIf՞/ۓ_9 #n]A" ǀ֛Pl y`FY  w"lWx-h;¢Fk.,GLc:12ipe&Jioľs5g0jOohu /zJ]5N_ڔY)r?z r^GW)wPZ<\YPlA\tmmmጻ{&fKw-=7 s| ZO#l[GŃeNA> ~q4r^6$|R6j'-R%6䈣cLpn]UkC`9nxG3 x6^̒kMr:^ۗV*)#&zKs"Kl>ֆB|ۀc YL>to>ap U/$$UsM)EV]gݧ9]sx>GGZ@5(aO:LbƆ׈)V s!]ǯ@B$ BF\'o0ckϿb*5mO&עDhc"R\s^"JlгC$x ħgkq6ڔmҤjM/DlXE0> `ؾɅX,`CCP2|NY7CN{:>2gD*F E-*U p;e$LP&|#STn<8hzO♦vIwhkKLk(GCSd, l#V(7LOD:-.,"E[YQy6/ p6ƌkMhMDH%Ua*۝,29ȃW)i,,ʠ(,䮎a]"pE(ĦEnEa%fp$o>Bަ~W juƝ ċObLH#/̤9WgƤ }TIvDqs)(9+41)4cvgmIH@.=Q,B,3 f'5<".I 1)" X^*fUӘT R4=mg:W.aH([8Z[ere_XH{/^\Cq0&2-?&:V>,IA(nD*)|Ł4H qxs.hdg`2UP7XlYBk-'G:"FŤ8 _’&9U?>^ ʭ>QPh](B_Jckfr5!͠XZnI]6d!!9eFzЫ{2|Ta _lr"g^f8>^yvRVHP@ܺzHCC^4 /,6R-[" Kg rÚ{dv%1I<},Ere9hf-.=Qt`؉WR *a8h 7 G0o+[x. ߿ &KR>``G9f m=ݘW3퐒χ-U;ыx뀚SZ(P0 1_d؏g1Jrʞ|eI 5̼KSkpsI# FhfbZ l@}NdH5ZҟLYyWDGME˲Ľ>{y'ބFn@MdTFA-?; Fp5Bv0(P5m/^p?}Qwqu7@Z51"3Ed|;9Ml[&(=`;8DTfƅb4ji4>鹝@>'Z1c0z憝hp7 Yh|֮v4(З,3kMMk{B>| l#?GxNa Ǒ0\=b¾'.pu⸋+iVP*׃^3{h_`ش(ϝ4s G2.Nऴ47WfKii2NF Tf'`&  HHF) ;cź/fulWG[x~)&\IlСL;wɩ20egʚI2m{^ߊ(ή"JA ["%kT!]xv S~qƯ;A*<F S[[KLЩOr9O&=o"fu2Rc*7JM+}'Hp`7ly/B 2t˝ݱgչ~A(ˬV1s6&z"1-k}q4@mWLޫl׮/FJ)LYzX@-ʹ~'zfޮq4bw}=Ʌ( 9sEʄy$Ӟi Odu;)1Z)'XxCۏQmu|w41KA9=4RS)O7$ڠ">^lI}# #r ˡUM;Yϱj8 办%cF_yؤ?Sw#VdkRtJ;%tQȔnFY _ ,,H~9 ~"/v4 ~)Qy=څ;{XIY+e<2*=w{53jLy' Bw̧"ɕD#fYX>fi?Tr"ziuX{=g2-mON` ;_s8Z_廎w;,*6Da]:jGǏ)qJ033NΝ;Zp G# Gڱ6:ЏG`:%A-20/3lL 9zp8Yի/&SR5`_1l+GpJ iRjcT^5gG\ m/!{߄6d2 ) R \YJNIᜆ#p=ReKp8F+Fp8? \$ūp22QΏ#$h]>tO^MG#H)]tQjUQp8ύ͛7UM@*p G#} 5o)G#PA+8G#>p}#  #d>}[pT @~p\d-p8*pp8O_r8nB‹(#00߿י+G<1O6e\KcPIWte+KXKKenidfќ7G# B枍Y LJ'crpzy-9p`Btqxt$${">ֵ2\<36rJвb8f(I6N16Wњj8Ûx dD$|z[tgCW>56#O ˗/^Nqqqpy慩)nݺ`چ]XHc3+\~ss {87,2ߙ+!R" Iu ǚyeיoC3䄸 BML* `$/*r8*.gVӄ$&J-5,ϡ ɰ)? M7Ha}_~Kp7$^m?Slj_| V*\DR{cZ"Nй@46#`oU6o/^R냓0r3/Wsra}| # .{]o Ah g9 Uzz1/~EW$vo.x5w݄M_;.oVAF=~@l7B] 7!2*ptVحG=);섏\nDHAMǖڵиs-`A܊Nn?nS8x([Icax NY6ז㈒bm:H)t ^n+z솄" 0ja_cwYvkG+VD}`] :W'A1tAqȏ]ka֕ 誷^cLl;u=[W)5|`vi3FFFFEXXD-MPrM@B߿p89„c8w%C;-*bZw8];bpSi\^e/_1\D܄ *' 3Y8q3g8j[N"c벗(FQ'jY+=7а8!äg#TmSS["쉻0XCڽzl"k Dݺ40iBbZv⟋ɡJQ8uѽ`f[NU/#9􀃍8j)j,誷_TAQqy4[ď@*gB LFTb#pI<2m5ˇXbR(ic'ᪿ̰Cq _^@EitKT;nl"VְG.Tr QϦ!fyЇ}޳HϷb?{3Z m,4FHߵC$5$V^2 Q'$FT)ggH3ϙO8g;OXP_,b +k+BV^b@;x{JBebȄE3-N0- k~-H*Pm `O8LSLس}L RϐSF&>?/Z5DfU| G{ܿ~Z'`ƀIE{vxI^+(U!noh-R%Zۍ; &s#g1POׯ:Lo@ٚԊPN&蜅!,ļA j5X8aY $s?\EÁ`4|-X$T\±xHY). /le HKY#>[p2g:cwJ)G##") 5ӧO`3%`@o0?{3O<)jp߿۷o¡U 8YZGhXg"pɲTFln1. >6ׯ 7.7ә9r[qO9?gF@ G#J,tlcʁ^<\\\{̴DU{ }?V}PDg|x8&y^XDJa'Z67yZqo#Pf's<֞~J< :Doc$]edN艖MjckfmU* |/?dS;H{Xx Sj1Q]M bo`/<\):;Ui+%H':W% cv@s2"'`ě9N#>PrŘcQnrt!!2ljF_r GȠf7(&FlvUCI=Ԓ(ҍ& ձ~;vFuβd?Ṱ` ( 'R2vʼnDZ{UzFY~3 _bX9j0ns_m/@\1QHSʹ'q:|k# ڵѣT;g#ƍ f##JTVM=: q +Vg.O6@%1cK+bA0 3RZ=>"&0_bW[54T6MY$QaXSAk+5(D_^װ@D,cDQhz@ &V(hjSUF} EnZGTNs _4B 566=30E¶z`dcls”˓1=EVZQ6c,S$mzFH[+ŸPjb k-•)*޴leJsa#k K[0"afT. h(WP@>*^FL3ڔ hdH ^$IߗPx?`-o *I8@*|!88w>FL<,x{<>\xvG#c! Ypac5׆#pȴ@ G#c!6G  ˠq8 ~p8Y@.gKHIl l[S7"}kj~⌮OǎX5fۥՓFRO @Zޖmj;[1w A2w쫡[ |${H {E9Ч'{j@z\itn?E_WAǡԵ17-N/nZ9X`xf(41_yCtW4o|i0 ?9OR,8f @%d۶m/?VF }$˓4DrA=/A70c-u{#G۩%eA1F_6bco#~ы~2.(ٙp9D;䐯gv:N]̡͸Vmeb5UIHXFU [UEz r,uڈSȽ狌N2W9rE^- D`ªX3UZCFQ\'i9 .~O™Sp;Py|\Ļ(M U(&?UHK[FcǑ/b.JSk"/~Z2tLQ.!}م6Vp:F 7@Ѧ5"e>>FA&Ӏ(pm.{[ (],=t]9МvIh3`Y¬)9OQU7[鸂Dj` `.]`ȄKu2Χi&FiK+Ϗ6l˕.ܼ N>A㾥5'-N;,7/\Ji d?zl;Rn>T.e'@Ww9ԙ U+v-R3s ;ďĤωhXEM%mg a(x#}}Õ>0FGz0oo"ۺX?:&v+rw"yk!uq GWLZ &o+ '@HH&LΝja;mK,Ys;&C43>:-$.KN; VGif۶i !Y}ijȽ&9,6ד˺: ;NmnSNuZscB1e3Yr-*0*L AN%C+ 댚 sևݦ;ɹCev&3(hD C>S 5/!SZCF%φn_),q#3 zo”С yq\ 7)<~W0%}} I`m)ODLjYh]w_ST={a`Aգ!a1~qYQJ]٬Apx0>z Kk` C,i1D^ێƍ#pcbxȝd`Z Y>r7`9Y PVGn hq_Om?8qƤn#ppzȹ9sj4.7괢L}fruZT/^ɕUK6lM+h6OO]jLknK ׆E(7.c~OsF(,FNoAܸtM?,@cDvo] =_հfdkVLgrC̵%gf(*GS)J-1On.aoOSLP >!wذy -+߻ KMc\=Fp4`Ϡrӌ"Kji_^ LOoq^Ks6kE.̴X˷L[GږL;'L7x05gPoÝCPZ-Mr'mSPFɺt: &A2E>9}73)nɺۦJSԿbx4tcҀX5y:rH:M qiz@ƢЭtߴV8/?J(rtMKא 5C-YuKѯX;G] wi2p;ӽ|5B we/Ȕp}JV81\:8yXYXxL+9 1.? C; o|UL+P ~Z]BmBSqr^ .|%_;sF?b8mpk! & 7#7,8KlC~o/,`h-g/ 5AG1BZ) SlJ+n/B7DW;ȟqvwaul6cI`ooQW4,@/MU Ut7_ea6qUt}%k ma:e:]O6bՈ7>ʴr0>SWtUf\Kӱ-%ś` +#~/̮pm&P')fMhECa&UD_rNz@Ћ.-i[|RYU~) ĺ?JWE:EfA=}ɇFv* ~.y9 3bLё,Vzo0.7}̖[%%-02M%d(_`2`ԧmU oU 2oӣ%iۮ-MCK{'`ҸqX[a~W42rQĝ>ShN^:sUD-O^9 Ք?Ⱥۑ|S8-ȑ7@2Z G1QA.>t|?zkEi圯 T&MXV#F&>S)x˵HU#ڵJ?,7ޮb&OJbEKqemJO<5LrE )2y׭PlV~Lh8`yw6,Ų ^)ȁ14S@zS000^'vm`Ak2F3;ŔKFMh3F@p<LQ,P$r&}clssFѲ+?~+puBpf4aQѐb۶) QmuVZg=~jмy ٔm4؈<ω&G'?hFOrt#!wya)mlT%Ѧ.FţVJNo'ah)/ ڲJn/Ll>`9:o.ד-XLksөDlB2s$>jNעRW5}J9c&O| <a!_Uu\(VQ0=2aaxMMfBJR5j._fa`o2T"V=e~YP<[Çf 7MHY(Ӻ/ݨ0oC,^*/Z0]đ?{QM, Ws^`]d 4kS6}ۿeLQu _ /aGf&71ht+sɣ*&h":}md\~#BuZͳWTmHt;ܲƛQ[t܏U-n˃MeL)̵jrVEB2w +`?PHT:RЯ.SnlsOѤ=َ[hV)ĺ떚^?h2F]}f⻞]%},7d-zRj,Ά/1HOګb=Ɂ}r3!io]E۪ܧ4O帢f< :OꊽS_cQD =c6v]ُ N:J%!غ O8 ",j=EڥlfG;qJxbdFFgKԀDKW N@IDATPچ)>l^XvȮ1f_+& ke,+ õ;eL*ߍ}=d24FGu#N0+9Ne5ʣ4"_"cI[X,sHΙ*r?D#yGc*z yڑ0C417n7tL\k$d`mNd纙=߄S+&8HΊ#0w qCX=4Ns&a?C?LO҄\ ʇLkT 2WX<M]O_c%%M*r9:g*&9Z4 ]#Xl7kαuo' Fa.Btl aB47D}x(a,ُR{my GD@jXy銥G.?gKy3 U?K,㗰߿Q:/@*r[vp8-d6r8Wٻy9lWٸy9Wٻy9lWٸy9Wٻy9lWٸy9Wٻy9l@~l %o:G#Zʗ*P {Ҳ9G#~z ~WҘ]Ȗ aÆ p8Y@*G#9?G?Zr8 G+ 3p8?\kp22Rΐ#ps%G#ppH9CG#s OG  ! 9ρW?G?Zr8 G+ 3p8?\kp22Rΐ#dfp?Gk)J|111`q<\rA"$2 all ##ȩ9 A [+&UhQΝ;C@nLRdh7!/WglȟD˕+n ;̜,DEEY@vx2e -=7`zL#w~>^GG D}+`dtwK//b1jA|%WT #HX0M?>& ߅ t۳VƽL.P{qD.`kk vyx~p U1g02e 4S ,F@E1\> [a/ -Fd: d,Finsws%•B8vNm8'~QD] iG=cb`Q1Sb1ݽ%Xx7 Ün#Hަ'Ê6G.*?1 dy+GOK ׶P;-s 9r _|›VȮuJ!sjιr7_#;q,33Q,xz4.m#-xEPf`6 x\v "bܺuD&p9DfPT$'BM 1yq821Q xxs*SƆ#гh|Z؄cy2 N.w#ni q#oǏ>;@54C3r2 UE*;- F q7=s Kq<W2Q9 2Q$|5@~y|Eit"Ee.m7_[|P{___8@# @?gz`ѱEwW{bB#F8aW#rSN.9}WCwOau:}=K+Y=ĬbMaU(ʇ^ڙWIq'DɖE,imY<ԝ׊6!ܯjQDv| K6"7OS2]܆ *u])+v΂8ӔHtvG#d0, cOKH힃ʃ6imYUF ,yE$ LaI& &ȓ xNm2EaʣZHڋdy`e=t__a;?L,$`8*&-,,Ppa,*TH޽-"NGG ybTI}sKX/5AJF[ZO2I.:/bS9eDL Q~ $ ՖǔޓO%Q,2'mJU[RHFlτ;OF,vg)q2LS\O,za$1!g遘f@gL?;3#@!.5NQh6_fk'ev!ȟ tu.^3%J RbD4#d[@Y[fbb"H?'AcB^Lcm9@"MřIvя_lJYP?8q&LM08E [+J"ۧƍ– ;ɫ>;g٘/)3{)lOI@DDr sss1F,! Hr L+׍FPүP0|ඥQx8g>.Ħș3$< B '.mE |^/a:8t[,xDu3كyyN&)^3t ϷQ} Mˏb.JqGtxN괷ˆAfbqb~&o]ߝ^NXu]\GM0jlX"VP Ś ؍5Xbk4vb5jTT@,hXЈQ.HN8nwʛ7ٛ7mSjP)hM{ox\: Te~Tg&ÎS85m0Q7>K',=/s!JT u>Є[~BprɐԶqqZc0 Ֆ]GJX)ZYᄡ.cN\H82+?]i z`Hj*uYnǣa\6d> A'g/Ʈ ϯf2JY9(C9"ϪUO ϟ??'NBCCDY+-[Y|vѣGfMa®](1 KzCB9A…Sc[kk,#^7F_CEE CpN~Q-=Ÿi||G|.|I|Eg=N8pOsy߄ Bjڐk+8 ^тOGqDϬ%O>UwD9헜 'uю^BI֜QC>>tO2?\Ij2(3QnK*?Lvr(j?c!J|>J@av #MRoyhaGgMJ0g׉c?]"x%ԛ#\ 9r}-+ aoxۉa>gKS,FYm{K&vT?{ߨ>1K/g] _ur{ ߿XCm}߾}+|>WdzXCxyyeB`9!eW&a֭ÇSs<ԑ%F ڗڙ){qRY)o*YUt$鯓:0y'Su wCX.vTwb)X!-afaiEyHNR,?!VGE” )ga ô5QqLXg@,'",L Dk$)')R<\*".V6JcHRcD%*oXo\U߻0D|O4$)R3sNOFeC%“L1H pU(SgOԊ=YWe,T zo @g_-UǮ޲cṥc4L([x2(|L! YZ=p+EsΨVNX/3#HG=3PJ.M`B'ٿY8¨Jy>F2˟q"a0dƅ`Qfg6Ai\Uej96oD$@OlPyV7|i]:,i!29i\+|9܀-9}cpk/0XTq؅ pJǸJS@ٴ͢vE??xz5gO xߦ?=QAMN{ гa _aZ.2POh*Ŋzm91J pl #]P={ Ͼew|b>qT7i {4c,o9ם1kR4]h61"]tf<9"XN+xŊQ|y|ׯ/o޼ [n ܔE[ƥ`Cϱw?>R Vlca)_F{,H a茦v%WOz[ |$iK ǽv0u+%JQ/xf$<؀;1R,EɡV ۊp *< waKZ?hiWjC,KmW*i)3|l8ĺ#+Y(mK;T1@ݮݻy/hӴğb4xcIZbZjAO$Q8;fؕ-{=&4)&M]H;FĶ562a@Fd`t'|f|m>E*cMt'1Y>o**mꊳ2 Zytt(iS1d ZPq%,㎮[f1`uz*+!+12)|0gVE:IxR\ĽNXjք-֦M.v|,3j;Z)+3Ҍ߂{9^3fIȧXc%" f^k"A{VaƳiIܳ}X!'H,Zz/cLޠ tC{Qr~;sUk!!!ɕ8RJd3QadnV4LC[[de8$#e6ea̎ʈI44%( u2&<fVҲJ%ƛ$۠rʩ2^LF׽IYQJU}!<$IToT (u{Tn҅m 8V.JVzPT ‘UړQMݺ܍W+84Q}vLf3ϺrE:@?D[F S^ vL0vXZ'd[[Ȝ:sKiaԪllɃ"u.mj8]Y٢lfrua*b:=wS FVr.1EMLAK;f^O''غ?>8^lpUΓ#\A#q9@~ @~yr8=@+=h$."G#>S@y {WkT9?GC!g@CfJF95 vƐkT|lŐkp :Ht"Y@d}`c&6<#!\8C6XuM.%vMdxCI㋱i](Zn?=GآdʋMؘC#.\d1̖Ƈ1\B r=n]>"ne}$;-ɱ_#Kxn[nEֱ΍# wV5DK$߬ϯ!+an7:\|Ɔ_dxPZHZ,jALITNG?>H)G1 ^,2 KN زQik}JA>gAO(VRnC>^Xk9 ?p )q8'09I=/} ?x@4͜B䵏a&Y8]]ף8i@Ӟ֣Q窦MBbf;>loYnNq2.&͌i \›awM% c`W^dq8'5`h뫏aIvJL\1*Sta Yp(6@t#2m;KBX~|#yj@.&g_ֳCP+>vD0Mi=-LљX`룏ay@=NM*IA_V0ͺ"\>VVb믱{-)dzAD)_cm&,ΌfkzJiW)zZ6v抨'ϯ8kp^ď|Z3H).Ÿh$Ҷ6GT#pq>jZ[M>hũe9}@ PODai . G#}>f9T(G#m8=l4.2G#  9G#pEp8y 9Tά^PBlF?IQm2S_s? )f)>-\ C{cGyY&}AҾB2>j`*޹ϬSpS͡D=Uq5 x[g~hYcV}i >ocG*an}лʜ1c.T'a8*mmbn!L!{VmŌVX<yS,\,]߃ ֊:.ԁƬG*lUᢦfWpL٭1g^7OQ<9`=ܟEGf=ЫLa;h;bܹ i{'&#r[o =o"|\2D|08^0?L."!GƷ#6`JZ8ciScQ3%0q5&kR8̦qC1<7c.+kc:^hd[w9^oK_S&o\I}Zk(,㊆TYE5(9GT)f"ܨ\\d}yǘ VSH3!u:Kӎ5b@1$}[[ְ&sA'Syn_֯cхG4*-)ռ*|Oz+fN;>gi,X6"(Etw֝дxsL^;R|(q]bZ V`/sxν} }!:؍Uhԧ- 1hF{ B f:VƜ'n~<_4౬֐#Ə9VA|2fb|r #֟}B88)T}?4]HYz+xe^*Op2}6W\W""c贁[ &b]={oh0l>mA6G6cl]Z󮴂b[TJm[`ZuDS5Ec$4b2f +KѸ3M9eZ1c:}EgBB"vlV*i|D >Ll1 yɦk^Hx7["'>~QwpP!dVcpr"`,&4㘲} .<\ɚFzg(FoHz|3baaa\-v¦hѢ}hN1ĪX,Ycat;њW<u89G|3s7P{Eg%~ Tj+g;ޅ?e a7JHх@xL~=wT餍hJq[g銾Eܷ6><߭á{x(@HEK%,%wCUrJXgry{ ֣{kã1U8sD(gۼ8J3CD$EĹG\T@&x+ꝅx=F+j7̴~XǘUb#hFloV5FR3nӟxa7 Cio 7Zv/Vv6}#搇O6\ԇA@?n7?cluP,˰ lVSza0m־$ ߅.mhCJN)M.YL6."E`'~->2JFsCk}rP%mPƓ<(Ij4Oz,\Sƥ-Т[G<3F[mA)LXVJXzUwa:ug{M0l̓BZgqfCp,i@rW5jPҷ3ޞGHt#B~ Jl%8RVbS+%h7~Lm#43: % D MJ]\'af6h~;XX.3I g%]& z}H#hۀ9v"'=䨇Ѱ!p[)QU;t\$Zr9aoWR﫣r~WQ(Y>Y0"PF:SN/ɓ'TRnj% u*/$mIDM`EF!8Q*eh3 ,O-,Ӌ|MS$g8qlE1s#6Vd7"%h'7)aԹ[\"% $<iaJmNz2囖 ;baYY zg!O""!_Hu+W~|)05rFH,y'4AYh"sZjKt4kh# (|rJׯ8ܼyS_Æ QXvWrqt6ޒN>2~(e-Uԝg_aL3kH ?篺!-|d6(7%JL2QI3M';ǀ3&`! \n~[% -1/fXJ5|&+/*mJ6Y8\~6"a\6yyc[ "ީV^# 4!S&4U577GвeKHҲ6z?PSǁ0/?hQӽD&܇GKǦ8m7ޞk>(ʐzu@ ㅵ_cL[<]k.G@| H2UkAPgiY]$+̿k}v$!mf”N7+ɀZK͆វ!PrHͥ&|#ğ#*z2Y9?p,=CQ4Ů_z8Yw72'%hXGܾ_z-K~UС)Bf^&aΟEr3qX퀪V!4֥a958 Br#kZ L6! j@bp7!RU?Ȥ"P$ }" 1JePB6Yn*~eСa̺'|,dn2{v}B[Ӕޯ5Fk- ýxZb` . uղ8{7m 'q+:l{/RuM*ҝ<q>=b)|9DW2 mdB0V4< OYf-j4'A׆atz*gХ!> pq)q0tFS;]!N)^+2Kqt >еa>|E3ɊX7jq)h6Mk-wSPβ0&\݁ZH;ᬚN'_pP 9M,ĺ[o-4$rc /t,5?2QJe$W] |֭[.G3OgZ\VG# Yeed#uƤ7+[f!~ SRR"##E?Y5QhQ79q8"{" ֙WX1S#os++Lv^aNpIapϯ0ȐWPF>A02/Uk¶HF˩Jz~ +vOPS:n@R"^w]#~d .WoċnVXjy1;i%}Ȩd=,k~~M)=N \PKƊ._3?zH@iĕ@9QYvM `^êetuH>^I^b!OK @)wB]l>t W eåػsr>)X5 ;U3(܂yU!uA{6iJ xTB 85vτo;Ѡ'ymB4+zD+jỶիx\]]Ŵ,O^ *l6K\ߕVeZM?bZS .5ǣ~fDEH$Kq8:(bRkL I ?# Z8)*KUķ\ e%Y &ie%a: zXYT[<\,-^^ f8HvĒsF?#ыqaxy04fsɓ<^^е{b1]e#1a?7׏`F4~;uG WWa2$!&uڧx&6 2 GEI2!ϝut c _TdW\)6847Ǐ"0oa/ ^H,Z.}s{xuv%m>[źJJMEK:C^1uЄ{G/@R4n=?<:w$R{}Ξdڠ|zLŞsp1lW+bյP1>2  bןIk/RonZU:wWf)Swܰ62XpA4 iu8 Z*ʶyNMl>RU0Yg7+ߏ:bp3눿0cF^5oteP֭$sDݒ퓖m*6QwAxx87n,.8Ͻ0{ìoV%~;k ns1˧)ftm^5Y#ZGMP.)lL %.}l$c@IK,\mܱ)"> )?ťd tQeYXz$&n vbx%cJU}*te1}łbLyk1LqM?Ǚo]Б/ dFXj#*6ONibaY*h l[p&AڥUY'1?vjۑpmC7XNЊ-2_6E̼du1^f-[V$r>$ˬ][brk/*IDATy":a)f?EdKREbJ)96%H2S/i^]v#qdxwdS k\Q%)#!Y<ÖnD@#h3fJZI-eFF 0i=Eɇ$ }'MF2R[͚y @;= @GRqez8uTvub%'Uy`o3Ur#$2T`]+- [b~Jbmw[h?+ LmԬ#hqr1dONٵc*d=@(y]2n%dthyہ8c7G~| xgߞl,*L#9el`&<Đ=h3a"J/)59A #ɧ2)[b2De -%m^%-R'YE!Ԃ'/9#G7oOԭ[Ŋ˘0CnOL! ))1YXr4hv鋱쟺/9G.`fkF7?ly_f,\ Z=yb6YM,)q6jeX[ݾMid_9$c.N(L-jIH#sjgl'aneݻ/‘# ! &[xKw5. ٻW8rЁwj,ж  ;G)N〱t7M+q12 Ą!v$Q%SeTݻwbZG66-2DB;BV<7 Msq-&)OBM)uZ4ҠPXey2-'(8(83laa!cz~N$lSu,'G# jǐ̺'7mEp#7P~Wubr8z?' p8y@jժu8@ ^'3}j-.+G#C |uᬲ@%O60 "s8W:^gXX8>?L,4;-A*[ ՜p8UPţ@gv{|F? ,=oH/i+K9xfA bOkyRQe LZF:vڳsGZDN-UQOC-:t+++.333hXZG(97D@\t)!1xsri\؂.h}걚XG+=|iXZGHn\X &r=pdWB(Di*1}k*7Nb Y2N< }4 gu[7$d}%ժ?"F<ƽFJ ~}pz뮓=0.$Oh'cL}Q~E<\=Ǿ>E#Of]0] tȐ'~fuol+ooo  j4,'+[=?qgxժ3~RFѿj3@W7 ̟ⷛa7X+h [@C'1V[BQE>imGlXF 0XAFVOe%1<u}أ1{ h%x~𞫶h<N!NO,4cwcP?18)2=l E$4@Se}sAVNH*G0Da6ap݆3&5YjӘ)#c@ O`F 6wy%{1W>xE8 hVo G`E4p wtJe-- aqxcP~((ؒ&)O^s׈?} lIG@X8Thr7z)[qѻ^zzK=NcmDh G8L S>\/e1_:t(K3WD!lm J15TEH^ q`d^zʫsHd҇Jp ̾[u Y|8u -`/h}XWq-)RQQQ`6ٹmFgyrEO*;/\P۱ufݓ}oU"UX@#@7@Ao_^Pr9Fqp8O>Vup8W1p8(|mxY‰'2\P밳'5##q:>+W mf,ycyt=0s ɡLj.5J\ 8SGۂB[bmؽeJ{G&ey\a֮xG <1jϤ+j G!t9ȪO`Us>@i:8ݓZQ2 ݳ{;lR sF70Gț.el8DdsPIMP:YE/`?ѾE߼ONE4-kb@mz턟0r NX;)ofvӷa" uhݢ9^=Y!%|p'-/>^WgD\z*_c[T͘{1v_a[GL_g}lTٞQL$BirЧosIq538Mp g'IWT}.2- ╁d0ef@S7EGx5?ꎋ7erJ#^yxnu6~mbo(T6 8}ō8Yۍ @tuП޻ CH!)ai"q2j6Al*XACg/}o-+<==SqUPCg'7/4H.9 ۤu֎> mU~YV7*zt,kaX:iY1`eT~U"lb<} iyG# != O` &Bȕx:V3r(mq3+5§QJ|YeYY1Õ+.azOYDYZd+o@_䧇ո |g -lP>9b6P^·?+dfe[i8t(:T. G#a ⭵xMLfj(y&\x4G#PpPd? estN4#|x9N N4#|x\7JtɥdG#pC}PcpQ8\P#+fok#dyJ,-ָjOYGAظ`6J>ۼBZP]E>g"7E oQA G#D+jT֡碫G3LA̍'G8X}j}!*#G f33Y(,fvZg?=cwiG@@{/i`3̪O`www0ajj lDcObǎ8|0ww1,=:g@]oO'f&9q8YC+AR} >U2L18mWe:?'2a~#6\ 827&r~4%侉(OXjqs"t+WH5!p4"4B[Vwƞl؃i|1Fjw!mmZÚ!]LA-U =t0{`麙xe~ϠR0|DY}D5Al_A 7eF{tm)%in߾-^<1mTYy]S^9+''ͧ1(lTi\|nƘ)cn-*cm!%9% \$ ) O>/ȗ>? 84.J;:!ԿfaaTqǢ/V)_3?1ɛ8Ӭwi ;_q8Z @+<>SnU:W#M VPRl&'d8vNN=QJtOFV0N-lKG 3 g>˖-7oG=֭b2.xZe{/ytr+z:ǡYzp}o8q!85< Ԯo {!=vǶ4OGULK@췿&&vah:y#Pph;έ-CI>,G}R [ vnaмz)=_ͣ6]z@0q lί执},e:(ݲ-NBL?CC#]-ZT|YS$,+n? -^6ze1$8caS,UBrNԴ+[b!8<qNjT7GGG#)I3౸& /V DJ E * ϟ?Gdd("[e8)P(PU /ϴRq8E)UfSN4qP-T ; wF fJKG Wp+7s>s~ 4op\8!5# | (op\Y=?Yp|<b6z{+*FMaS>:uk/ui=̝]~{ER jwqEmOhlJ5KA+O?T5#ƪKh8`W ӳ+HZqD+UOMxE0sDp,i^m,Ƃ 3l B/7yR ٲ6C|KfD]5WP0H.5\'C}_B {X[ Fz{MtEԚdOep[ vv[zDQh^YvGTkM.{Z=368J#[3px <HK2N Q:pRpb:8UպdȔIYaEf$'%.G*K^BN.5iq3u-LkwTmXUB-ФIӰX(j3рMb' r-#+T+6 HLeRLc@άe<߉PBN7osq1-01]z)O'."Z./}0+O-s^h?b _b :-~n;Zړm f'Gnݠ@Ybm)]cVaI<~MVKeI6o9֜/|bT+2:NWcSr=,ZH֫}a>] eIO 40b(\zu_FE@oא1>i#C%$b>>xcέ[^^G[?=hr]/]oQJ7f ;Yh];t8hޫƣ%-X1h'&펠}PGӛm;;צM {48T XXXh3D:995sϬ Z[[kN]15$U-lA]eQ'"Ȭ :BxeG09%e αX1jrqecwQAGt=1\W Qy{`}T fVI8rWos 8 )KӤѱ'55=hb+[.*m2&8`QB)z%Cl\<&K@yd^ Grr23ʕ+k4,-ˣ%?Bb,bIE< ?':@2q#(L_pC؞l)k%`ec+2dWF(vTxT^?1a$!G~eeaX4Y?9SJ_Lk^i($"66I/qQ8u.>wQmR"Q2!9Pb H%αxӳbxIڳQb(ӣJh%W:flm *KҲqCb(9-M13J GXnZ%!dd֒z躕|V}N_}E9h4ʕ+ϭQ(Sß=UCtt|ϥp s $;oܸ???z Dlk7b)ջJRdYIJaټdҌdW΃=i%"TgNRl2n[ )C$ C,8A@?VS$-ۀ͛75lP\f/!IRNn C桼BhZb琴J.\^j FgtqJt;Cb|;99q(S , K"E$Ye5t5cJ!RD pǙaZ2 "9[u.+`W$ycUrB'J [RBF h.1o<.S#6_.!| 5Q (bKvv{^Oԫ7^_:x%6u2? SH"{Fl:GI4Vo`KJ_ۣ0 *ʍ)Jd.+`rv5ҥ4Afv[Bifi&+=Mhc8yWy gufݓ}؛`n2Ypn@_J4c$20K'?RA7|´LB̭&՚D#1ujRNMdƚfv^`.@Lk|={Ir%cICr|@[bxn>Y9y XV,* o,z=b"9ö)uB pmZ L mE,]t8GC"}HyYUp9+]V7J(0Aֹ7Q>8@E@W D7-}8q PPr9@#@7/#\\G#PpP p8W!p8 @0\>2GLfV> f )Y޾}ŋgJZ|>aTۼI_G.OϽᡯa/Ѡ=22[u9+| HZuVVV7333h0Cp,-ˣS3WJm} ^M:dwǭ|킎;ۦ5Qa.f~sùp t-hnW]LҲ<:EZP oZޟivjK??X;5@WI`]5;V! A'@{N\..ƅG_!7suV$BlW=}i; F)A<.&Xax,38S(b׵*%31l #O-zQvaXOP5o6Zz&⻏^wkI/#&\%x/@{7S މjPh͔&mry>k7=ߪB#@fs_{덱п0-LZ~%Gpˈ{YXf^+_s-T>D@קƸ-9!r[2]3BcnCs?>*ԮeD|%4` :X2 pQSLPRaeyqĘ י5Fit )CdL$o$rpӶb>4+̹F_{Яki+x.+B.{L<۩ =UHcnf<>F ^e||pyv%b:iX'%8Iߔ? rw[vITVQN^dpYI"|#\cz[2M^S0[zIJ/HHȟ- IcDQQ^~e466*dmĜ{=ڵ dZ-xʕ{ k׿(`-[ݯʵkFŏJA)3s 0_a$aGC>3̴0I M Ɗdfgklj"Ynhhb_^q1feeACEhaϞ=XݙHKAH+](Lh쀃/BCሸpo !зVa,XG?cQیrh][>C@P\EMc'"&\'%l8GqէۼxCĦMեpm)D V΋&c&h<m_ H߻XVqO%c#zb )X}ppf_9ē#r)1$h@/vuBsN(+w@wg-@8i1u9{o+6 \kHIIeb ~j^ 1ԠLaĤ$DFF iaĜ#88t箹ֈx#'Zs5ެ瘔@e~ȮvX7&1qk%Ǭzq_ko6/ٰߛ%WrLFŠV&&.E (Vb;q5۾R㦰 y0#م_t°tY?b5q?hDHxP/R,yAl]j1KEQ52V](&{ z+SHo8':Px9+v N p K\.A&B M#Jg  L!/ϷOemHU&vb7R@4t##0Pr.=JsSP?m\+0%Jr"T^w)- Ts`F|S)3 NEecR1Ӈɓ䄥I [ w+j.f0^y4Bcſn}UThɩe3|?}֥Ŵ[P㘾`:Q0Ӕ[ }8]܈Yӑwހ !új{]y>:ư|n:;bG@윰hRxj{W4VU1x;E;?⵭OD۱+"D&2&Ȯjkœ]N3a41Pָk5 ,Lԏ!ȟHBсv C0/Žɾfxj0c;jYd㭼fn@os{\%Nf%5 ;oCwc=bUUPch(CcG/Ccw9v-#>ٟaov [\{JxR`~6o܆]8-8CTUzٷJ;x<<?FYyO!>Ɲkb͝+qj!L^n[ZNcz44uZGz}8{"56)0 vw!(-HΠT <ėʝlGS Zyce1zY k,rN !Em=IO;_8yHNC'khθ`"n.9l'S@5N~GS5ny6 6A6<Ȥ^Y0в2zoH~ =LBz=rkXoNKnCĈ=؇)'x;A-CQ, <  3N<N nHMF5'T__ )FQ\Z wZx>2Y8@ yCSYV.i kknAl$Ŋp9 Onp-8gLÖw? 1OQG[GUC,YK뇓{`ر~ڄw!XNj'lFkM'޲QbUTG{#Sk:+P\Rx_D0TTT~~!4!, GX;%~=4 #PЉH8ySQh܃֎AњNEua1:'v#(>VsXmKt(+EkBۊWBdb-JoPOcGX;(h9rt֯Gy>ݎD:PRgGF"bą0mi`G M Y8Sκ2y~D̨uF5nFTT*;ΤN^t`G]c3]f.Sc3 ܤчSpv򂫛#[İy?RQ;TJow|J8u8{P ܼr)xQ=hͬC߿w=p?|H @K[#츦Nԍ:b΢=NQ4RtÁZD> 1 \ʮ Y憐`ȡpӣh@aauFgwH17A$a:7W88:;C_Ҳ!kn6ARy5P?|߲] kZ"~N :*KB<vN9d{E5,ûNZAu3o]Q3΁m=g+R,7e >H8z!.: }}Lw0]Zh? J~4Uv\rbݣ6) 37,{$DKmY>2sRQ]S՘:z~ycgw/dS˗qvcUl*Dۨ+ӹEHMV_N",/~~o#Q xE: Nbt8 c8~Dv&B,z ?0Z;ܽv:]gE[ Zej&1hvEK3P]D+:A8 kQQP2??M] keP4@(Q۩+G kw*W -M<.nIqG*1嘌{v}z$$“u YGKua&:0sRMk/KҢEYYd_B?ReXf[Q*(XJ=G0hV&ltX8}Ȧ@8ZRӿz)3>l?I FX9]*2c[pۿ+N;Z'>{g!}rxu`OF."p7N1m( ݲz#S }b-""}؟i3!I~v}';82lGvè>!&K9( I +NkݰAtڇaEg&E!#Q[ƒ!էB^M+x۵CLF~I׵{=U0Eء.Cx,8.bȄ=Ts gAѰ{tBbi}nn4#31XEE!t _z0(-^W~٨lM/KGks'q`4 !&f]Nv[q3OK>D1 ٭:GlCdlxعe#iŒT ؝HYfcvucW~=/[F4DEPwAoBkam:4 {SJCw +(LWַҲ>ާH4%b<ٖa;$1TITpp9pyЫ@C (\C覱YuEJ?Ol^*NJI&xpru'Dxd<]/\1*1;5N(,8>\p"~ݼ EIpu{.F[Phb<9; My'Nr#=" JIiHlwŝȤyM_t#8p3jfsð ^%GYKrP\ӂNMDCp AZT0h!93ցLG7;Q;\Qԅ`R3jچ0{<8]?U?Cpת/MWL̝?n6ܞL!L3Eft^So='aDpm I?4DDŽcq !NgsfL1{IbNpRq[LaGHTwmKp<̟ɉG/Y2H UA&F`X|Ābsׁ !He%ZMmfv6Cػk;X4!/'<]"Vl%/s03H<Ne{8ORyO"}o3ÒUAg'0r%eOڲh.Ix+g]׈{J"$ ˑ%|h;yNƲdkJ_wlyNp3v1Q$"rq \D=BAYLٕirW꺐JQ?/<`y'Edjp\t݌2*epi(J\lmQFQ@Ғ$:J~~ [9r2"*0$2$$^\3VP/չR>#yp۬o)/ӗ,Ҧe"+icK K>vF-0:xgZ֫WeNު&87Ʒcb>T>EI_W? WPF&31h*.=4}o!@)4]LL"M\I'="L'RDqE`)n(jD ڹ%E/X0b-rL=ZqM}|3iÅ-6ZW/mG;~zg"JX]ݣ[HMrZzŝ$/Z(]:}ُ҂L|6w!jCXmH&?G>@Sjx?#}ˢQD=u\𣼃›ҷrYǷ[I긖>ʧĥJJ-*sOw,.lLJ,YO3̙}To2Z.碘2pwn{S}CPWH"*j6#Քfe ~hº:;;&/´ԭPSHIHS:8Z]8r,['aJr w@F] e9"vQ>\2vusGd/L%_PrLX_7MhaP&n6u~1=7f{y{n s&=}EeN ^pGkZǽX׬ƞ/ WV5b熔vS熝Z[`!&&zs3b$ً_ { ҡպ ]u)*FdQ;gnbfdIxFbRRB;y I꽹YЅ'HHK={>'':Z[~۾ʌfS{&S7ډ}c;O (R /Rr֟1*0Gf+TBecQd'm5o N_=ZoB rhs{(fȗS 3wF:w!Xza߹r?nbOkꛙG1 SSw֖ !݌:h@p$,фй0n6uԁVdqD GQ:W?1ys2ЬElHi<^-?,Ce''9~?d< _i̸[3Ǹ$ʌpK Kh!n.x6p8QEq=fE*L@-" XaZg P_hfrSV>E((#.%<)Tg¡`*8Ezp6)ubɽ2O1g~v@k["Z4+ϓvЛ3m\] x00Xnh]іIU^K|r/&l|-^jL.ơ.g}ĈodRp8 lGw׈9zq54^0wjB37ſ ,qL^QىcUy 5bZmMصyNd@^El}aH T \t =a17tEvZ4]9(ȯىQ`Ш&0gǹGIabD_]SՂOG<Әr5~:(müYS8@- Оק/"S?r=KHA-*NCfq/r1 s4;[Ou8 &;*FbgF~S?RSS/ {]b7Q^=pie?iۻjE[^Vwc?sCRu[p7j{Pp|Qq0]k eTzn[:&6,H!aqM{dj 6vnZg?EyL^{ܨdzOGҰb|oдy;H!*u#3bIyr`~;kM:m~;G .r5'C?;Y_ )'UJ?)M6g1[ڍ|9%'#2? fxA}QV zRx"L3fO0{" d0 {?z0\ST͉Ϝ3DnhjuwpGAnxӍe+јjspRL?#۴bN^i 4݁vc8q8L h WVY#-9(N\c~qLÞ9UDROKFF=;ͻr8P]S(̇ߖxblTOKn‹OvXGP?wĝ$6 3;?w,a?JZtp`:Gxt|-& =%zz{H; )2Öh8IFWOM&)v ˌmD11eB#MS)6|p5 ȯƆ+q)?(z{U,4eBu$#zR8R`ej+'uD 6oPOo5yg¨[5q~ڙhA97q~Se#F0-erCs)rNW~ؾ0sM|hgAb Mq<u 2^Ia>\cg឵36DH믆GP&Ÿ-z8w:5Dh@&i9DC_?'NZt=R|p0Mǣ?탓g8.ϯLS"iL95'IX|wھtQ&OFp% [0g J C}~*S"?771( p^QOwDHxosoELPanMtatjˠщ/|4$Z+Χ 僴n TPh(xz P5{Y;,]¡b;.Ja͚54bR3/,Y˯Iqvı8TvPQ^~ܜLS5er䄦|.De}Q"ʛd#ߩO(1z Lv&߻աB:O7 Ã(#>weg>{bh;3aˮM(nJeo@}kt+#3]E7ԠK,tX0Gub\qmmEy1 (A^ba>>9!1 b#XdF#ZdQN"dD#D"jNN;xZe⊓!E!&͑{N5Vh=Lq& p-EMc\ >JIZNd*g9;oGyXQ;_v%#=>ڑ8G;#?aoӿ y4v} 7?3$94bIKz 5 M܉IxX>iS.h:KӋdݽ|7L Y W(OINtb;EҺӨloKOt6ۏ'p S,@ES \"A^{>fGe@y8]vsљMFw=}j`':x#! | D% #ljEqqc;b:Q:C1oڈt#)%8m9^|w(olBU#2nF͠ߘ7.D4'Cy^x8&_Y0T?F9u)|hϕ-,_ O-K;?}U*VmR4 wFenvE)q:Q rrxI9?1 dBoEFD;⣐I8l^⛬KWMT.\CGOҶ51z*xRWR;!T:Kƞv<<[?E7VagHWϮ6MEbD{ 0ۑSВOޙK_n~F86 N#ݨ~{c8嵌!d>aĶtb0I~؋kk1'F_ExND&۱qeLxAK$N Ǩ7AQ~¯3VKYOw~UVj4眩pb+A!c¯l"k#ǎGMMDx牅بWVV*Us2qGpx~?ƲV$ G^ 7_d,;ѷ"(-6cH~3~%QU%nM=H~u;ZAB7nGU;#r455)E s8qB=o0XEm2{GhwjpG^'XV ǤDi9,i9l/w\ƒ/3B2eaIgq]lxt'  nts $xArJ։U׻E֘wd}L28 h)K$i+%HӉV1ᨗ@deڴh?SLV).QwzJtڤ$Ğd%:41G󐳇7 e-^*ds}?¾C \E!ZmD ǒfO}+|DEdbҹLg= 5UcJDh wHv:hx^ƹ\}˄:0na.q][g KuLJd @)OP&D8F,N`h)A,&Pe@K@{ᅬw͐'@Q[*^dG&AF^SaTS(xc珜PzI L휨o;&G}g-6\g3ł ؽ{-RWW0Z3i&̈mgUD3qAD:իXh?XoA)礁ITF܎gssuƨ"9kQU/ e947/Su4LyJ xW#L( )kժUJu"H>i]VeQ@@7D,i eLq*d&aRIRfuR%0 ,cY4Yk^Bx[T5O[H DH(E(QbFć>Xٸb2cA+ Xa Ȫg}0ྐ(X,C"wa yu v4+DkC#AkvwIC\#r(ϗs8Р0kQ`b YꋥL"2ưx3@}k+rxHFUH~䎉({ H$ JU^#m1 pX,n!#!.w!aA&b%G4`d`KBr/<_0t"L&.3O2 @&)Z$j\>}OT ڃ%`E=& T2DEʘ-b4)JnZ#g[xTSyD,6][QR 21Qg]'.L2_)kWzZXIE7q_s%<=ؕiFE\Zy"L!/W+k*c;ox \  <X(I|"`h亨(&07Y+]KOQqɚ-)c^KJuq^'E$>O_Tmtqo.GetqLI(Iꘖ:U~E]FԶY׭g/Bm\xԕ>W&\#S{WK% $ULb^ԕA-DT0kͨe *-bQPkRdc/^i- P#>|TRȡ2Z1Ks/N%ϖHU"廬RXC917Ep#:[$_Ƒ!yg93/JB$-^-yt2 rV}% Sm*`U!,{ICN?s.E[ȸ8rܿrm ʽ*VVӹ.EHEq 6R(Ru| *+`Xʵ֠.?>)%M%lrs Lڪ5G+2O9NYq.WTxTLR׹ YRKTM`IZ?/5ګA&Zd06#ygؘl ;[IO ǎ@\w} èZr='ٞ*PঁABt"ғ[:zeru" v~2ޖgeS  +|l"L*(BTrcT? *圬Y{7Y`n~!J< EN7 rMA~\d܌O"T:,]81)yT&3Uh&ư4~XQd ˘ějAC訫@}0L0E-:E X>M!%P/y+QNQDR4 XQṕs]|I.ؿcj v4p3F.؋۩J3@}6L_k0@'Na0suHOĿ RS̄%b 0qH[w5)(#b{}"rT tϤE27 ’>Wxf)5lZ3"ʣ>vzb~|ӎj2vԅGg5>0UṁdTtع&d ӗ\qhͿ/Y- kXdXĄ68yPL -e|Qf%fHp  xiO"䏔B߇S::]@=EfMIUg6P1( {oH ƾl}0%“E@x4ݍس 1 /b,' *|'N"::X:`p[l!*!zu Aަ dd#( -Յ8uL'1F!s^x 䧿x kSfQ Kޯ#nE ʙpPU~;{0E3-wa _= 9Z^=;͕]&V k<y  c$7{7<an0򲏡/L*\p,3 N^AD*rVV$.]|6t"(%yD P'^'LKoxkSvud6`chV˅LIPTL2wRxq׈jy0g&$%D+V'cSlza(LIxT8}'>#튔`R{ُ Lk}o#O⺻'~h?P4o&=\eÓK0' ϼH`2;HTD\ ,ރ 8u&W^ǴK zř&QYX ׯǒ 1vfGػ8wh$؃=&ŗ>̀?mɫ3Fwa`' pv4':#v%>ƫ;JEHKa߶Dvٙ'jc y`~BΥ ^|ct;eQ; ;{ Q1:^ɇ T "%{uFwHcbQS<\`n^lm%ow-Z _t4R5#DB d63-cFr/ qX[n3W݂S߆@ű87R+(6R[++a% 7 $a&OL~&~5i[ 7#xq&_ "l1!̍#(>Ρ'e6DFI>cLaB.\󢱩y|+?S9|Cc55n;`y}rsM&OGAkb1/ůOOXs~j jeo3RD8m ͇0ej8\`ݪP NEƌnżidT_|vf"֒u+fO=Όt*F|;JEH s~ E(̮CyB#2wGp>]h͝sbhTyl;@ %ڜaiɽ'qYNx/59 D+·#ԖDI!z&Цwe,a ^-슌NpIDK۶uYIx?#q^1CwR QkʛuRӅDlp33=H4q`b]SiZSQKw};0o~:zXQU;2 Y m8xBQY(w׭8~axe߾ҕZO=d,tsϼ"̝nQ"P%I F+;Lyh="Na1HK"1--2Z+#{ADU)2>KKKZZ8RUVʦsJԅO/G^WZRYX` y;%߈GFb"CҍA MT39'olك3Z5]\omB16J+`û1wvt=%FiU3JK==Gm], ESqtJB Tǩhyj{{ᅥӥuw2₺eƈ"B;1POџc(ܱ}U9b$bԔq,GPl"QMؘm'SyqH$,\;((T`u|spT8}Js7"0>mEYks*h{*|f"hGGHIeՐ876)OtFk*WaRؔQl# &#KFu2$GArr"P^9YܘD L #-& ZZaTN={gRO?âQx@xitmzf,eMnʟ00D/9(x{+# $#zA2q 3de|rJ7-;΋EZg̜KQO#AGz7 d }]5J=%<gOk.'מVNDQwȽ"R`jJ0|C[BLCXH@]ַ(rrFSBJxY<Z k73 $`q a&:C\LJ-WeWc΂t\GEԋG∘>\|5/[Fƅ%7 "Z&BGuΞA|Ͽsf#:m&r bnVdV(ᵆz/ﳎV#Au8+O%be!e=WIUR"͙~blj^1r*L#z+̓"OLD-Sܕʚ)݊+qBδzՇF_j=ñoO z٪&!!ӦMS\j}Ra\F[sOsh\\!]?|r0Pux{ VQ6BmnWc&|Iw}2 jLu7COv’-rOd$,YF@*Up3"=я=vcg u-^:̘إeT2p:at;~2*JUI^!BK~>o#H:Մ5kQLS߷ qqﭫh|v;|Hn\VrR r7 Z}׭Y9ܩ(O_7_?nG:-:Mx"M=aܸ)׶`>i2wd4=Lp %?nnADw@!DHaY< 5T4 Ɏt1gjcj/PV!.ZwFCr^EcؒkƆfT,12'$&=bJ5B<=tcؙ\)\hs,UP֊d񵣣Hϕ\jFn)3^0ĝ!N[Fz k')I5s ŵ(_3yOrsO 1N[;AA{M%XBkѲ@g=[ia\ņ'[K 0JkQ+7oimZѓD2k}%iiʄM{1>y<]cnQ`-력fOe 93ɉo+g{>̢%DTַbې`v# &,pJ:}ܹcP6;QNatCsJsJܾN@+ NܿAk^ mé eywy} '`J+9݈d>)  /#6歺AZw75L3AH,@8{ %~w>zRICSK/S㕾pxOT7Sý nz|u/&!=>xp p]F_eB"S1f*Rۋ5wNXAlK$hiBn}B6bSoX@F4ҮIxb6 [e`ş(S`Q17nB+X5H9 *@YXwĄ`.gΘJ녉I Ba2,E)xlkQ?؃Z9GJ .)$C%EiDFL14Ԃk="G3{:~=xr;e^$qtr8O9?|'HCp݀ĄxFPZ9^c'G|ȞHN {1Sلw>q1=)^ؤ8 w B@ iG /#s؍ "1-|1mXZLwut~&2OP1z834ʹW:ڧy-vtѴPȌiܹw^xFnD@甂lkhЎ(ӂ+W1B@.%脛/}Jnÿqc\̿ڌ[(H+ v%cgefBJ;+/c !T2utLCyhJ&4X );E F{ A1&&UY\ΒjL'@ q9>-A:ua~XOה`3c< O`vU7AGu\đPM(V[oxi03ZL9eO:ґO'otIUƮQ_"ۜ" sNRetT~A[ f7͆ZZR[ud"=rR:/SztOyP)kMB!ݛUvP"HMJA I2Yh+¬==70Y#ü՞O)ɚyi-Y|#OZe>=p1A]W;ņ,tFl삌n%lZ*HY;0+P~7)db( ΒYC -[g̬2b&8OXpzVgeڷA39&&a$kdgH.g?Ƹ~6ݕrIV޴ܤ}U=5 &@OͤA_k43"ˎ.AfzU|%$%eZ9}777olC& Iw-IIRW54Xqu]yzۼv-k5rZznGe,靖zB _W޾Y:*5vxoeŚ:( k{]F=9hC>S}$3:7ۦzݓjCS:hڧhZۭO%W<5ਜ9nVfGR<72|yMU9:Uk^ygy|-߃b>\h7 "Gki[@4Tϵ]|$dAɃP2E%=&~27Pw7 %➑@$SR*#3kjބ)@{]7Jnf3y-{1)MӢf.)s <%B Ș",״N`DQΗp}>Bh`W|P}vdy`l^nyPAS4wS.i3?x+:J@Nv}o |~YYQ%\`ge_sV]G6]kg*E5z*rAe_ G 5Cfn yW;j]X MU\ԳiJ<=QFK7~^ @At|/+.5`F+0t,fnhRWj8 cR)y@|K^]/e;Jʣ[ /m#ꨎăK&D{+j*>Ƙvh͉yxA⤉<0?EwѰ@ެcmmS n'<>&Z^Ao16= M3A{7_8w`}<\tER'}6@+F귈ZWm߁۶G+ ȴ XK *kMb]H  yϡ s/3Qo*A6trF)aFb [nO u|ܨ(>85j@=K! g) wiSQ{\ "/|cڵHK][Hzo5 Ƶ҂l_ޚMXzb/CjOmW}Xq"[ڿ (9M}Z[8f^yw!Juhl$V1D9Ͻj|2P>kI\)7wq}W0Mg?h%|ؐn!}vkc=!Tu Om~-ر~QR傄^g!f釯⣥_ *#=Vqc ljrk|1wV/[bkF>X2=43*>o''Koo;PC)_3;HvcVs퉞Z DnMSdݎ}";'Q(:"A2_,MwŏpDDzhn[5)r%'iQ)}r2GtoW-xo/ivjbe""L07+_澍Kb˦ 8x3r| 8gޏGg,4h2AH'v;}{q"0`!ɶ cG nmvGz|4=w,/"\=^#0e8/9lbnjP }B=F|E|i)Đq|!9@5x틽rK}o@`y4E3@܄[|j|y 1d5(8Y ׯgK6aKa9o϶㉍p^_n9ȍщX"T-W'^wpoc3oMÞ, !vv;^0>. lT|èPEsmM Edp!y k#Xڀ˚L*+&龟2M07zTlo/6 hn4JWeUwee=rE\r 1Y'9>%<%lK2Gkwl8e>it[@/6=.QijoyGE_|~z'+,Y lAbo6Yp( ;O{oؿn)^Y* 3 \c.w q?On,۵}˱2u7:gqO!`ZCm8L:"Osŧ+#w'lXARB)9w ~A;fs4l^.1LlDd_}c0}:'$ &O>xspw!j6*Cߋ7^ g8P| wށ[uKaҘq…CxRetqVOG~B(52j޵ucBsdZ"r-Ɖ[06 W?ͩi8Sp;1[TYu{Z$xL7k:6G3y 8:}58tהazކ>CVq}`,? \w;_4`߶XF v a5Kp$v+Lj;iKqVsrj+ܢFŃX87UNcႏID B<𘀼LGVa#oNd {ZR"{!(a] >[]%h(SFl#gC݆_|k7CeCPwY.c8CYAlIm4 \#>S/q-P\ݚVbm01ĭ\-_L0\zo}$0z&̙&fPL3AƠmk N^&qh6' PDF8\慇AŊ^oTw4aY|+8ZN&ìciSTv+֧3O,'E[=<s' ua̙=yDnj.<ȷV牑7yTGaĚ,ycg=7%&bHq#(Yz3Нߙ֢Ukh"^`CzDӅDB#'Qm8~;Knm i n [=֯KKj$ç Nm%ݫ7!Ct"+Vr2`G.OwoĢf_}>r8S^E2T1m2,xܺr^1l,܋t`îhZLuњqX~~(;/,1H[VÓ)vT i_~6 %H5xg&y{9f2W"9akc1cG Żp`TcOHhmdC[!ՈNS4)n &&oA^x4r(cr'UW7#0118($ZٓH L{`PI߈F~H WuG $&"M=hl&>G)TGjBk/J bQS o3zAd kk^\b}9ck$~!Acc>ohj?KAIreܽz Hh9E鏪PLܷs+وKTfoUeT[K3|»K-;65xp Ȣ6x{S9U=|*#p;d~_}]akxi$&!"̮df&{PA2u2/pZd \ep6)~P"kdcȄ)@M A2r,O` #B"q%h0IʊG WeI>C]C2a:"sߚ%IC"0%W;txA<*aykJѣ_caђ%o㬍&G6VqH>)ccE?x+Ǹ1ݟ[Ӧ 9oџ RKo X r)c@D Ny ԝ E(2Bɢ<*ޮ>kœ{ߎfOEC|i gVh@`( 4rD;2vcO{׺qmЄtF?rEdhǡ ^;wa\J|80+֚z{j6pybЀ>or?0Gphw*vC!ȇZ`JJF+m-<ˆI:7d1 Q5"9%^rܡ'S[BYEY^2_x٤!ˀ~۶nFk7{`FiAf#ܼDNC96h}$. Y>VȸF\ל1mj Hb~ m Of#<k͆m >: AiBQq5@؎K~t;2]ary'=<}HQA`sʛ8\wc07_3: 7Geb'g :Z<dq`_PpTi(O`$0lA 8dԅ8N3Z&CQQr]#;gDN'1\|&"sPNtAbA'&}gr9y2֢JWPM-": zd/60trnj6ʔpTZ'zL奧>m!pES'Gû3B7ҔRS_դ w&LMM)ep4 "1!z3\u Á]1Ed&U؍ "~FqĀpH$85N{[Oo5 %֢QYǸ{1r^DIII6Lqf2b6&4)|X3.3ҕscFoeJ3 S᧩qN`뉉 4/{%fZZSPI&*kTt-?P!|.7:i{CgG5~@ndGC;(4鶭P"6YJb qM~YhErP񢄘u :9݋GZpZ'ן,)u~#?C1vnDm挪%]O჈>Jw?r\ _rIQ =A͍u.4|7Q8Fzo4iIg-_SID ◘h 51N*c$pxb,=R(c2Y^1<#:c-KFeTV*ck۰%v3*iˀ\U D{' H_cߑ%0V|T#'k@i%It$b EURUNL"SR39{zz@d<'Ӽ ?x}`t#%pkVod0FWLejӀ.,yEZ/TFTb/ՒHG=Zڤ2jTu=Gz39{z{;(JC2(HWD7"nDr'>_Icg^s=p-=hѻ HY!4hs.cuE]M"z亚 ]Kl#ך2:1ŬJff}}5Alu=WӁR&Im.^Y)w,'8絮􀔹kM5j Z$ڮj{:wY]{p=@vΚ=g8{7 Af>!:+q=g8{p=g8{p=g8{p=g8{Zz L*IENDB`image022.png000066400000000000000000002156121300200146000363120ustar00rootroot00000000000000connectome-workbench-1.2.3+git41-gc4c6c90/src/Resources/GuiResources/HelpFiles/Annotations/Annotations.fldPNG  IHDRC_iCCPICC Profile8UoT?o\?US[IB*unS6mUo xB ISA$=t@hpS]Ƹ9w>5@WI`]5;V! A'@{N\..ƅG_!7suV$BlW=}i; F)A<.&Xax,38S(b׵*%31l #O-zQvaXOP5o6Zz&⻏^wkI/#&\%x/@{7S މjPh͔&mry>k7=ߪB#@fs_{덱п0-LZ~%Gpˈ{YXf^+_s-T>D@קƸ-9!r[2]3BcnCs?>*ԮeD|%4` :X2 pQSLPRaeyqĘ י5Fit )CdL$o$rpӶb>4+̹F_{Яki+x.+B.{L<۩ =UHcnf<>F ^e||pyv%b:iX'%8Iߔ? rw[vITVQN^dpYI"|#\cz[2M^S0[zIJ/HHȟ- Icޔ&v罍z.l|UUUl']r..xQgl7#;]{S@ފ@t U׷ ]mI4]x=`a@ES湚a ) $3-jnP+O7(ZvQ^YZh!!Y9 .>2<9X8)u92*.H컏ޱpZiNhUK7hnj*2R\w/#Hd@%(톆ϯNxɵX]Jwci\fBWáhQ셫Ց"+RΤ)Ai|^>+Oyx0_B6_әbl׎ ?:y_)H?scCFW*jܱhmۊ^#'QOVbՊՈ@\Qp!v9^f,Cu6Y/˫0iH4 '**57?.zVb奝1JV Ty@MP|9 'űb=Z <@BuY1؍=lկ.Z~gpVUE}joq5myJmẁZ-}u׉'b0Hw\?SeGcs q]Q"߮>ԵPR3mZTF)gEc6nOIAPA.aL!2oQTvhmpX NVVV*J9Q]¬L9R g`cdx_FO2HirI@^mat7Mtݭ=gŞ x01٥6cM( wMGdz[DđX۷4Ry6WWhʮ0J*aC=>zz_ 5v=TvZ5v*)-CIYA!&͇/^JWjB׭w䡢:/*BjX) + Dpc E%  99C#`*6d'3d"#7U50ᨩ >h1ъ+2`Cdd8z,VpSpنGoDX ⛟v̄J)V⩴F]EA|<( s&&֙8a{e J*M"}EcGQ^!J+БNW =*B^ Q_- S|">Xُ黮+ҋN J={^JL?Tյ~򆚑+//G-.gJufk Ww? rU@?րbu)rk#q7)}RnW'֞rǃ0TL%*,qJ'Di/Ayjpu#0Cp&ׂ1׌BXf#EdF\!=`KaG^yBo e3tV0r0ɓF`i׎/l<-+nѝ[e8xp#qU.>C[e i#YN+ס`-Ņhݢu-JdBDN/6.ڹ i0 +>Ypdlڽva2֠Q>rN?LDD d'OstBܮ2ԉtjy¢[zxkP^7\" vwPߏs'#?j0Ī/a~D|f$u8e*2_L|-9v((@PR'!)17žgӫcЦe1_ŶD++SP{\@߶6Ґ 7%gοԼ57/.9JKDŽBfǀ+"ބs #w#gPBbB HzzA Dw2rJ?&S0 w`ɏ˱ma)X4عq5aͷYB_ER~hWBв}WtopՃT6eH;`)Lɴ0q|V.~]b^(/̓"?nHA#MsZ )YWXdb@J&70T^ؕ 5.Awr;$vY5-Ǣ)Y=+VY-<=4NM+JXEMs҃- е&2,= )ah6 5ƴCع;WSbf )ʉ]wf J+p9@yYC+Fat5ڐ)NUCc-*/,r&t2Ź<P*vۻfQ1a j 7}p93!Aj%_ N,Vn?BQн]k8Xș!,H" 5kqeīqJa[\>u❃Q 68lLƈA@ =zǎǐKc#Y>+MW{~Rd^85 ¨TUKUyYY)qq+0ϛ11(C#*Dj}:3"BBP]eBi~& lA3s7x+`%8 Hf-NaP[VwӵXKdug%#?v o`6xA){YwKxI L+WwDIV7Hi,T$dl5Wysn(UP #so]՝w,yl8|"ڵ-0\]KnՒ/oZ#dCTccN*Z$q-.4ՠe@"2̏1sa#G)d3bي`kCzL)0 o=t~i\R$I I2dzJK$維GJ֦:p0!Bw^/YQCʈ(?Sȕ=N Y,_~;5?#5ЊVuډʒt,^]BrOQ)M;W+h&c[uRqͤ1hL@ NeѺcGLn5ۭad3Oo ˕ ېҾ?AڵjozV^d8)6Kf:䮽+FMa&¼f?R\.[7Q4 [( T!/Ѯ}OMkZdy#LʤȱȱǛTZKE2yX[A BƎe=KSt%n?i7[X3]P;*<[mZ8Z`b/2 8* JOQХ0۷lAxj'X FEa, ƿ,hե#mI O="#qnN; 5#B: K+⛼gabp&]Xbp4Vaɂq6풲<"dUHyoР Xq-D}p<'F~+D6Tg]Y-jJMqة!UPlv%D{z+-qYEB "?>rH+X,`m\bcrgsPa@TS`UZ9e( qqhMK=ز}_*7b[%$`k.:,Fm<^ VfKG|D56a3Q<0uz;mH/4`AD5gOƮ)8p.0fxgZpi@MEm߂QP`Ǒ]*عUkI[vIaJ];v?y~1ovzF y"2XʱfjrM׶]ب ʄeʇ~<ɓL_9UPqT8TF#͚Hd5Ge"D5ooo0㐑W)nBŵK@ΫlG9ʷ#͘JX},:uCogur}7R^ mS/<9.a -U*ۜOj'1 iyNRs>K/+o vyQ֣I=Բy0sOaޚҲ,x>z^w+v{޽{kR CzҾXR䷸21vI|k? W$k$gA8"-LJvnb,?ccHU*gܚRBjEJ.ܻB1Q! 3QRd,E%&FZnATnTFQw_[8췅2F_E(Wܟ$h"aBE:VN`R3D>f21hUcyg}5)J*YO֠gWuAͿM/Fᤑ_ҁ2S9窲5_fCVڣzd5u9g4蚢1::ܹWRW[/P;78|רZz:qݣ^9/+E g?] ] g[;_דּ?Wkfdkc 9ˍ*N(Y v^xkX :A溩Aē$~ uŕR.~hrj7=Nb7cs SKLj`ht'@zO8dLwS(X?Gons3܋;]PXMطk|Տ8oBQSq9.5[ 9GH۳?n91[Z}}ʑ܇ JvE1 ˱o \c;e#{wmiWNŀh=rKP:)-]MSĠSu- |ms__)XC!p~ct#j-]1O2|_7 JŢ[qP̙4ThB/9yK57r}FMJԓnj[n3 #R +ߟ{ ;+7p )9^q% o޻jN̋y'Tkfa|3O\G#viϚm] s[G3ڄ O|efF8]PЌBWTvk"p>_OWbW2koXu?Q~=bnC|9ҖRUP3z9znƚQŗƊ/7Nq#T~#ϸ<ze{TL}̬3㥻muMxW7זL^XU/Ū)׬Ix@CXU I8k)>}C~ 9Νҷ}vx[xx33qǷ9w݃Y+adƉMcCҎOe)8qw|}qoy],|=sH+!oq(qS t(9oy.s[Y s@e̼f:6^[ {M@mU8m-V.ɣbǹF Nڨo.:VӝVe~v/ [2Mݱns^x?焦M\&?QMyۃx70OQ+[oϼS_͔wo<< wy/͹iT~ ZhVݺ"IyKJa 09z noZ:peUІ%{R7Zܳ,R&*jкSĆrZ B$-۰ٓpeb(/cP:w[_cosh٪pin=v#>*PL(i"?Y֌sR`Q݆Q;3`y޽0~J/4MYc6# _ų?ŝc:0O\;>O3V,_b@ηqppRm36ǭg-FaNՙ0cܝp4۵U}U)KO[cTRI/BK Si=MI$tB\^Pgcp(qsGOTc[C9" c2?5_ D1 x^$tc<\i'O¥24CVi\F~%;7iWЫk3\8V.=[!փОôMhٶE*&X6I\>:LbsV aԹPiܹ7GOcz/ |bq~!\ |BDZW6p%3) TI@˷%2~ϲ1կqխG/pdxm=66V]]kæ,fdN7Ec½Bh7lySyQ%䔔#$7?F^7m"|P.|Sė6.TD;'o-{e'oƾWS:Qy%|!Fb<~E*C%y(Z:Eʨ8 S}co 0Uy3؎5h^30@h又.V~57oTϘ(P쯴G͘&7o U)T([5& 펫cLknDvZ,rEIr >J6>90?XGKe_&mR"%Vc| ;q "TWZıXӈىY7܏WػS Ԡj/Ps۲QaF.<2#ɋKQTM *g.@^|n7]=gV w=ROzKGe6 |7f0Mô_}l _L 4(]A۪ p`zTL}>eG| #Ǐā}Y>|QNi،8ܰYZ cqtzm:dՒ"f+r]Uxqxb]h0f`;cb Sl?2C½1 2K0fP<܌NCd'?8[J^\_tlBʛETȶ=h?qcE2,#qr0g.[}GSOu@Fܶ]v۽PZˀØ}_n>C^nOX೦>ze,9XP_xPq#r㛥0vHW]K0pS2enŋO)}.<sDmPM]Qdz#$8E Ph{ m6n}G9CfCWASK}"n^Y[;O9qO'Z4P%}t7/ x৯WbŸw#O(+șc\|rsV<2c$N-˃qW"l^y4/Տ`l$Ztx|Eܵ<&el7Bwu~6Ξǵɹ@ L } rN஢Pځ5r&a5q.D|YxE(;l!}* g ]Yjb*~58spg7os~:*y GyA1#]l"MFf7b[cܳm'&Hw.lZaˮ8Dyphg$cl7FC W a0Bŕ7M7p7Jl 0N_[V"^x70\>M@ CKkAP>z|8p8=.^D,:Gi´b;'w ϵWƄf"-$;k,꾝*\ʮ[`\'.;Lrv;USot;! Y Ƌ|?/GRWx,MËw 0qDL6*%øKh8Nh/Neضe# _B덉cz#̗q ,"4V@B7b&d*Z$O 32 Jce"kO04~q1=1R>!xnË7|gOb!=~:VZמ@NQ^'~۶afw 3XdqQc| ū`ӒK/܆+ÝpQvn)}z/)~Z3ޝ;>{3Tq 1>ۃv>֑w;gQ叡o<~A9\2Vphx(95<i2PCA/?t7t7`3#$ fމ1]#; h/b#rc71eֽn:52`aĉ2y"M1z8^6*~ qaKrL0 n\Y&Ӣ* a3S0q0nӱ^dyuGC`"2FryxŦсRSQ !2<̾sΰ 1KdxoaG0oa6=Tx>lrtckV{ڜZCjf?gwތInŏ1fbgͭ~'bHM!1O< l~pԌKTn{'lXO dž_pQb!&FmGj)/ 7DYo׋Y]Ohp?mc/t*ٕ7֢x񖗰at06V9'JTK&q7$:dC(/`&"c7!ֈ?B&/u<# 3:ط9V?:m#;@^A,U6da4T}+}H?OOAҎ?-p3N3Q;g^g0i@";#/vEZ9dXԯl7!ڡ/jYWwWH=FL8X!㷩:2 ķ FmiX,Rٜ绱ꋠ&LM@TAqNV`B];,X}&LE䠋ظ+d%z} 4F"A~p:: w> Qp+@ґ ZgnLJ\;\Z$-60`лc}Ec僎Cno6`*L»މo6mEԀŜmP.V5U ;T@ W۸1g9.wIFhjɦiD<`g 5W#zCs J/ -0us^5C#}bsh㊥)-Bf`@rx`ڪ|i X--YlV!Xt.G7w0:4 Pbsm)Eo}&"$B pc>0ntb б#u8ﹴ&LTRRL 2bx1yy_B<}}3ȂLT DB'*j|p͚A6_97e]r"V#C09@ueҙuMj/L(4h9޾m0iH4*% /լ]1G0w"-| U"coPU1)B6^,GsUُ|n:.!26nVx 1zNk1EGMDy fI+1ZJ^[Ui! Jru D}ҲdY)GPy%^u`v]+*i*nf1ۈCO/]ڔII aE6-T0d(|̽%rm|8uB [jߨk012 jMa/CXpd2p> zq"u9x왹XZ$eV% P7.OiJ}9f*Y,Y|#zSf妯m_zL{KDwUŸ82) w܁)mqѡYc^g&e͵3S)P=xY=( c]*zk`ƣЖy&4 Bu"e;:88cDdlUf^R^s}tvLE-:WGcrbiUr8h[L((ՐP]十#nk(},eR#'R&@XىA KWle* NW~ OH⟖^2WQ]]Y`9co`i1:'!2B-ZT1K]h^uzZi ,/Z$yV^@tftw MЊX,MC\rd'{0zjq[7t !9XEV4k1Av|IU( 5U+*xr t`qB2ma+NL*43*mENH?G-PXrv|!/@IW˶%[Oў#0 r<xn4b%يUXGbZwF>}зo/b?5fxΥ#3^zp^^\L HNhpoMH78GTf0Fj62^a:Dk4лwElFeENz ˏhEoRIn%2ebӘߘ1rMxt7V.`6*}IYق͛7+ )JjeFq.؈[xmzl=LnҒL :{OQ3nb̘kpͷ!=}]lIdpv^FڒV4]: }0MLf=m$t Ć?"Kq_ >_1e,{kj (WY\Ha+96~l+J\@IDAT'wAV[:iuBP/*o~#gqhF,]Q}e u`ޓUw~38k}Ѭ]80 a6w>NQAVm/Z$Ѝd3V!|TY)\U6~T`X̬sXK G8V}"Eh=6H~Ɲ?Iw恝- 'O~تOrLZ^S0"w-2mZ%p gE1g}cCN}1lDw}F|CSǡ7F3/{#8i0f'BH9_$$6ލ!4>-؝Lhfpt0n?/ixwUt0 rL`R(Duw?>w ע}> Mv9R FTkmd|kbk*AkfpvK9q1{6~̸n5}/<ƗLBE؋{Zoo@7h"uDsl{-Ǽo|دӷmf?3A^=V"4*CnV`^[p. gaWy]\E}ன !IJ @?o>xfJ}F,n#1e8aЪ]'[`K ^\<-Xq E>BZ0&B^͑>xvzwYkh.}!ٱk=} .Ǽ7>ţ7@]@xtkXY B0א̈X5[F7_ F|+v"Z%WG40ZJA||,2xlu$n}s A]=b-MBbPAxc։jE'=<=KLcw0[{ 떬<7nGkrtjyۢ9s&[^u/^}Œ?~߾@Pd4}.Zik3w芊ГkC5NѪ&vޙ?Ɠi ?h쓸)8ףŠړ\U0WS@;HO1 y2ܾe!y1B$'qٸ8=WDۤà}־{֣39#):gV$i_B9V2=e_es5 -w8ĥ/¸iw"l<}uQèvPKyExet10q pHњ%ia!SY\4ʚ-E, L(ASlNl5@#ӘMJ^VF=7…ax b*3r-?g ҈Lb]K:n͐]4-3\O5ryqQDgaV'>Ge់UՄAGX-Ζ=u dZYhHQ"uIs]{2Ц/̗ߝ Ԍ4jG`bЍ F$]}@s逗7B#L+qEUpVTaYCLT1CK+Eq2PtxdZ2Ϯ`?cJ*Ѡ9Τ&U(8Mk50h2r/"O*L'| \ KXBTmډG38:f榡cdo#tdjZdfp{OELKȀ/- iV|HJ3"/˄Ek%ʙ875cq#j>Nk>R֓i LXIfU:/0v@ }BM "%A͸TxLFLͅq8a!e]H[zqD ^ʜ,DؘOXi^˚8-7Zh1˩mI7ïc4.OtYGrRC<ؠtuv.X3 M=@͌:C3 H/.ڇ%nm 3å5s^Qh>짱wn@fqmѺ?w"f\%;xsΨ_'wIj2gSJt%UHױA\o...d# kvALkUC }@.rqvSQ!k^TbO4WۇF=v4eΧgpLQ!K?|ZA(IfЍ-:=[SBHcql'KmH B\Z˫**^d̗oK0 MT|D7Yq|:'ZG rlO.P;EI3v;1"n%s\RN,!OA#A~}墫oaKnx%鐰-dlI6j?Zd3;@޾AWq5ڱ@%VʎvDuWwHbXک5GW,-P/}T U~Q4br;2'V8]"\b+L+̦(N s=AB~ \w6,糒jW[, 453ەs VTpw5XG |l[BB;B -96MW/ g7 ;lqŹ#F}Q3DU FٷڙJz0uE 76c@75/Olw\5eiXZ[øa;GS}!1q֛׮>v]s~\ VATޥ9ʱh׽۵ q E&c#>vkY|W Gc=0vzm0Pʹ.gw[Ա?&폽HWDf`'W _- NnCLz>jLR,7lKCuFU4J.=!!{]:o R H[H{..)UEjmݷ3; ?CGq0 T4ؾ7,^=[K,H܏EC}Q3[ 2}BA*>(Ԯ$Chx>h]E{-x[#B`ShԺ*nq>cߪeu/kS5EHZvR싇{6C֒Msظ+1ފ*2ܬ:TNX_~FfMWB_(%`8g4POZI(*ABjCUaKĂc}tBv1ɇP;*_Esqb2M:qbh^ m gIǻ P/*)n,nb6k+hTP)S@ffj*Q#GbqɊ$c1yzdjJ˟ "]% :!l1L}s[V.υ"'u #)+m`"iwsG~aiw`tF~ZQ;#f| f܈{4rNnǎłh"=1{^<-ן#J:Ĵ shf? iv vFz> &J.W S~Og=> M1363|o^y] O3Y Ϊ/apk/xxcN8 n0m{I}2Y{mv! LS/Ÿ'i0eL:3f-©T\(@9-XzDxF$#pp:l43&#TۼH9>i;`("{bԟAx8cӺx.y>ĥ9L"֣2ԤR@_G?}Q8Sr6vAe7cU5()y)jO `bY+Os2yhBy\2.!U&GGff./wLiM3GZTS䨈D2>ݽKB@ZZِ 3wY#煐! yK(R JI$&:)č,[y+,.iPzYi!eub\,|p.F>*A{,fyzg"+۟Qh@\?DŽa=AƠ KjN ҘcX)鉪r5IOӇv| .GQ$&#‹[;ij0#D>ֆE1+ _ӰXD~ydrm "35R2䑝XyчVq8q4WnB˥\^VkSг/Fav<0.Od\w{m,>|' 4gjDiq00lwGJQT|Xw~ӒKMtKa/87 :k8aR<޶"%Z+lWou5wJzϏqFk]].}~Oz_W]25p#Mkb~`}`T 'SF&=41v[>/-mHG]p0V G0 #GKa_0kE3p w'JMa/uJku:๚iCsF%k3΋HokX¼Q{;uWv; Z=0aMRa-Kӄ/ Fevؖ_1a alF}H`}Z6J$(,_t)ĜڃL\[M }4=Zj#jͻY% хR"@5i%y>Q oE' x}6en ʔG:s2Z6jv&D߁$xk@7QIIG|EɈ. FaYW^M|n6cݥBX0M`eVa|pC ii&Vj9ŗ2bBƹ$`$ҏF)*KoO4y%f9gZYg3EZXwDah̫xMeUI] [~Qs͙ Č[zǗwbT68M=0&ˡuCޣ=A}VJ[xNQ/G(ϚE1S<o}ǻ7^l 6CǞ|1$^y)|= &Ԕ ;a/rgǼ?^bq3$I:0WMTCAl>Tq+1d l< O DnfJϚ!%S?\TVBz+:']ilEYēYIz*HPτ'[v_:g\/sS20X'7i-i>e9!ArQ=;bу_6{RPX&A_jĭLmAIMB(I'GOÎD5zYyXiMk?7J6i跦7z`ϋ˘R -*SOnKxm!/Y8EvwqSd7p%TW&hiR'Y[60aڧ8,*-*m3y¾ٙZXe%*ĿJ?"a&:W)] ND́3U3n\Y@c/f@ oko7˖@W^A: ó85P)RO(rѢġ}B63T;CIgZѴIKu"3D|\D2=Юu9'aȒ^i02` ? >,UIZӷo`E+Я a*#*ky]PT"zt>b"#Ֆ-#>ێ"sp[l=w$\ߑĴ݉Re!a..Q:З :<=&Q`̭DE~-{ʥ _.fz{PoVo'qQ24ɰ-Xy ,D{Xc {C,Uu5]DO?&Qs bQJ^abK ߕTjv3QJNEg2ba}B @ 2H>VY|Z X+NjDL&'}CQAm ӜOH4Ec^=qpj q4DpD&n#6m+ٻ$m3!t SL9o#P4Q^TmݏC{ ^Sk*T I19/Y Ukao+oP48=b'~e\>gYCP' \<۾q6,:q}4gۆWD Z(q2 3vo9's,V:c& Q9՚wsvRTNہ"W+dJыcK.WVR)RRZ~rd/WBf֬$;G8#.%A;cLawQOm_ЪWk@ep { f`Bq&l%zmUBiAjIvmX^0:BU.Еj]`.SHH O ,BƌaG0-"E&2) U nĞOCu, ˵x ;j/cˑT CP :7y^69򯏁0kIE2LExq(F|HO~$&xb ^ C#F'vSÓMBu4b}zu*Zbv~D)" +,Ixd^oǿ?ؙRCiP{H_|{LusΛZifW:nXvO>n/%o:e3pc}h*㦂} Cn?H]E' e\ކ\ FAYU(7"P(E} J6D)`,)ϛV!i5A=Vlo^|ߒs>kZedvk)dۓ,ΗO-I=8M4FLi%'">̅r5lJ$so/.C"[.i pQSv3/LDТj HE?e5KqiVKJRM]]nW)qg9YD:ڠSЇqMR« .C[ʦBhJ}}+H-y%K+uh/c">v`eN++F&ʹl^ iq1 N3lz ?%USKlƴ.(l3"_ \<֗T k);}q9y n%Qbq讒ns"9z]I^^ t m8-z_R@Hdvi/풃;r+gcAbosBSe &3ԤR@J?5dQu0?zz?R͡R@J*nW021^>SJ"_/GrEK=WRྡL0 7n2;R-7$Pz# RF3-⿲ v-ӃnDCr?]mW+F2{6/<Ebb 8CZm+Y@պ$q(b Ke*b$mts.ДYkR%hsu #v\Q@,nt¡ Npu țĕFkIac5+Wqe/Ÿsfè ŊqW۞O1afvwZ?TeZed4C6q y(^LkJ"i\OihGLo.S:onU/ؿwD/ ɘK?UFv?,Y:@PLbP-V!Nde'?gMs7..֡uQB,b&Z.ʧ1&\<5LfD+N]1Х'+H|2C3ʧf72Z8aEI^G=gºy(*"ě˴PT\NNgFTcȸw8-ͥlDECbv/e3ܱ3%aw< YER5V;rǏνg~ΜNAwJYctL?s !KsfTfznAN G,QrEļq/Cp6/ RnE,,ؐE6:'o(dKmHiP)I)G)5% 䎘1fE1 %lk,-+P}=mVȱdB^J![;#?ssl<2`3GCjx#faXj 5ƥWc.,Y;ʢ%bI xsѹ&l쳇L"RruKXܭ {ό>{0o.mPbٜR)*GOhZǖ YOwwv^1qddزx%ʢpNƚEˑ]'Nã߶܈Yk#E}tm@nQ3ȉAhO"ɥ4> Ή{0e&ks8CV:M q)ȷGWD3e1lUGHZ6-vDsQ닔ȷКqh|t§=zcЇepfKx!, >c]RRك8h; 8<mJr|?)S[rkFf4 cU澟(`HgdTHH%CS VLmC#|2jh6IJ||g-Zh@֒pX3EE gKcK]PغɊ(ZOi=xAJ,C\kEs5a! C$P)q*S. b:Oj:u熔 hҩ/zJu8 "yhl\y{I5UlT,L<d|6IB1Ԫ=/KˏcĔgb:bcOó> e:C*őgYZ^X|)L?SbWz{4c_ tMYkA5  Aeرp1#wEGe4^Ƥ8 RNEK6c:NXbhԤ6|trVX]f<){"ɔiO5;8nWKNjNvܧ5ܜYz!)?G>)F3o6׺#w3A9o e9R[433ahʟdjp'+zSzNsz^LCOٰIcSBǓ<:HF5YMvɞ{@Vxd[IeEyh1iߒ4@3~< {=%9JidOb%Q 93<"Zsc/\ 4f{;x%t*lPbWT$Dc׆C<؎3|G7_f> 0axN|8->tG6mQ&{96߿j@͏f#(s:d22mOA1Mps KxC3rAzY,(id\'Q|)x]zsӺj8un!Nj*xA)Qz,XCq?fL>MJU WF۾3e3gpB)ݹJp+^ -+Kn#앁CPDET-遝b5hS+`ģ0 g ֓(Ή% R#n1(/7~'xE܌1}<nA_{᫧[}v6t {, j4/~x{V{~^e"~x*28w-y0~4\Y9f&Dx >be8-k@IDAT՞js#FV`NdJ>B1P9n*3]Ճd^bd*": D/}+ex'<.jrޏ4WD!j[&W+a\_5өz^x y&qo4tP9ﯻ~^܄~_I>V^]5R01Iĵ%X{7lU&*PUءp25g T"WV)g2}*T /(2bFP)R@(2QFR@JTFv_ H*T TFv#ʨU P)RྠaRR@J7nDJ*T ~d M@+C4n&BNHp; f!^FSatջYџ%!3l+.LkƤ*mb*1ƀTE\Cpc8< ҧg:1]aj@ C@$]nQJw1j-s27뀲Ŝi5#3+O_xXb1oQ _2Ӽ18qg<.K!Rz.ag%~Ckuܠ5(/og`a/WS:ZyYT7DTw,C[TRYa!ռUU)R@% e"5ɘqEp5Qs>`UG~z=ރ07d~7F_ 2?>+G,pd30`: ahugUZ"Gʐ뎲/_zFv`օ.LՍy8cyƔre۽y{k~D g1bwbd83Zd~j c"u9ѓ~Ipwa\m5{6ϟ-w+c H6_Wz@{a$m0sL /^| bEKgS7G2Qn03d|s-loѲ=^|{r5Lؿh4ڷonCỉcd.m6PR)R@]=`d׶P #k65V  3Od D<<|$z,b9p S>ײ Xq4 UDѳ@6\igXӇaB|2vz18iÅ0f ul0RNSCօmdy~4ƾKƯ#:CdU7,i9qxz \Sc]>"i~ vNFƑ-K<'~ȉَ7^ułGb38cG1[0ðeg^94DH$gIGgؖ/agO9|~wizo4Vԙ &Ap܉e;k"Hs>Fn;mT(_;*T s(pȊv̱Smb {t٥|r.ނx[4Sê&!ѺgOCؒ|QR*QWDjtyyVX͹v0-kl^q:< VBˆA0:sKkfhW=xlٵg1U> MTADF#p-{% ZDPգ.T)#<1Т2x<msxy8}֩'bIUVE1 RWMT,Y hAE*`J7\sD[{Dv dV9Vn{)d;hע!U]q9RT P)ϧݗȸX*/3 NewN&gdB s3嚠َƔ}aɋ?¢CYhԩ'y:4#DXа. -:Asraѳ<Uȴ~rH1O0sqt' $ci/4sZɜ\mÜC5^$[9_Ǽٳ0gDXkX0ZpEbg ?Nlw0(g[rΗ2 塒0)9&t]΅EPVf"r,Vr5 a1C$+*c2b.gLDS?U P)mIdfG% Ll\0^75Gwc,7Fa9j.ނy] [0kzp֝إIB(%:K/D.4Cw՟_}Hްۢz-΂f벌[hzṇq!,(qC9wFܙL |#%M(0uУѷ0y"ja9!( IrCj\w4yb80=l(@&LtVNuФX|1zTcúh'(ٔBN , =r1/P$3pGLh& `!e7k^ *QT)p?R@٪Jh+ 3->IE;S+7@ERwز!L˰bk ru^Ӧ+ZV$9h\PdžsñfxFҹ;[ أ;t:I\ԪQ xdv,zARŜq F@W$DFlu[׆b_m\ڽ#BE6%kBFq/$G`撵btک q+BKCoLC=v&$|99*4.'"b*3;L̔O2/o,:P@BKhyggg% U  ᖛf6s&&ݺ􂛷|V3)eh T%Rg>a2\\ݜK:M[hq 3TzjDo)`O-U&M#lBSsvGj'[9M5H.n(-4'M4/w<|6=q_R-CpHbN)*?}H͠R@SSvcdr]M͙8F+I ElFTDJ+c[ɟh)T FvKgdVТM5IU(_zN 94n_T %FVVd3!CJ.LIϒz3JIp?vjrq/Gԟ*T C #PK㙝9T쿶a^f %Jtu]tf{իU (pgL%_ BnX0 EV NXȂ'Zf߾JUG# F=$J+''y6K+/].Ŋ~/B UNȈc4T5)^R@-Rirq`l,_)ށhU ̈́S{vA_!uA'o'ɹev6PRmNjJK*D 2aAiUKyӷ̌{gEкB H܌k-/ ի::)uM-{oPBգh 2:r@bn3$?@T {(p&|i:|=ZOh^إGuv|L[|B=kOƛ΂Q熼Sx͗0bx0 \#&bRSpyMs N--w %b75P)wQ$2.֜sI4~{ٳliuؓZ<ܬ2}͗ݙBspHoX 37Y!Uxnݾ\Pjcs Rs2M ֢ǣOyiwݳW)Z:`PIO,e-pWd#,j8ez.\he1f]P^Yo~͇"ѲJIе^[YKHUZ7zl{ GE[pCYRn8b N|R&8uc7Y&W"n Cf1)mތ4&iȅw/`c%IFNd/Ec Ƌc[8_03anjR)R鹿}ռ6-AkZj(bGӾ@6жCҀ;ĤG8g){dnhX;e_Yu0Q4|f(>~cW={DZZ&ly1oh׮-:w1N0s?8}hۮ8bw#' oϤO% )\Pli,U ՛6E@$,T h׾cl9JI}p)TExbvd٨>b"LD xnJu|]vkkЧ1 hΘxo8̞NU"K{ӔTZv$=o=-Schÿ}{Ӗ%̚998k!Yɳ\Fn}D( z)R⑘\kՀ,Dp)Y uoO˜ithgC 'Aۜ1Ͻ0hfoƮ0&a<ޘ$&r,eKb{aQAOş!\:KͳK)H/Jdd3g|.%Ϝxle2oFKz-ar4>50k['q4>G|PD;~6Ipʹ8:c-q<㘎9xsL q f_eL}_ wOħSba'/pNލE7PŠ3?_GbK'aq{E]2$h jhH}=&bXзdfF΂>#ym٘cz*6 ܞjq^[:/IJk1$=*]^goU !^RS`3a=4WZ`<|%؃   oT,yQź>RȊCM$щ粴V3{3f3ahV U| 6g6dUS lVOB%O*<>F@jT)y2"١wbZ4Dc \ʗjPD6>ot!>%M%vDG "EM+CA FUO[J"1$O4ovaW52/]f :#NDvʾrCan֪b/оjd<;"vns?Fe>:?>I{1xӲGo,Q +q(V"SUGN$lڣym2'[5DIرن>+ݙPȼu|$ K'^Nȏ9Y?.A厴Ӈ**Gqm"3!v#n^+×B m?Wc<ѲOp9;ñ|^|+}Ig8=[ֽ2O%r?4{&B&5++6ҝ|H1f AsПDV5GtlBǯr;LN倢 '^Ɂ<\s&2Z* xV"2 @"Pk7.Ukӭs>MD+B#&/L('"r|U}eTyeJYcR #t,'1<'"@""|1#55ԵL)θ1q8xAe`FgȷuZ+Fah2mNC@ū(, |oĉN9mFʶ2TGd=:N::U!( `EPPkXRZ'yYq`6ck[**5x(xZ "Ɔ 4NQx|'>|-2as%&LNL~oj"]bKsԙ&`w@qo qߋy݋%zt6Ca#qw NO f&F w<%~N'A1BX8u,WBf 0;mЌu gaĈ>a*%SIvòOGϮ=O~" #'͊'~_#PhaZs"us<qXHP]9RP%3ԶXIJp^z/,K3wk׮vyweiC0ͧѯ[w|Ѥ!?x( ?uSnd)}ʤXJ:mW8cU&R,y Sz)C Zj|A쟬Q, (O EΛX] t#7+ %N`qx5E>vo/gϡ{ɷSg@z)bE:@B3&l9& f܇$,{y|v%Jv|Vp_| +, ~abo`H&=Tæ>|֛C0ɉIuqSa>8aQR \=qPJl¹ Yu"1DVDbQbKjD56'!M{A |y5cqj\ˣ}J'1oqۨfH_l۵ Y4bԄ;0H& Sk~p:`A*H[&~ wnGeعclQO܌Xg62SWf RR=Ḣ- yQ:#0z*^`q ~!#r4te 5 RЂ(>}[B% 'G1O^*4 J"M톙>.2?&>D[r]cdJ_ } T $u"/eYd}C VIɵ.62XA|Ύ cqT daRO\\ѬT뇑~7ǷD z ]0|1G:E{(5hLzQ-ciC"CX%%g7U<*&U IV,1-)h[+]뤴*\Yh#IЋņ V+qd -"?7rJFFXTh֖`D||a/HŶ:u á?Ca5칫l"lN*JdI= T/B5-G"K۳кS,*"?(80T]g+I\Kmۈ|X [9?;oT\5y[Cn:As, zw!u0>4m7p!_mXM֫Іl$+ a$N+ u%@~ dcN߄{f/(xf|(-P7p"*F^'9'=yBDYFF(2z\-nEA%2dm'a#8L}l2i:n$(Z^WFW/I [\_q_QFb'|oߏy #NK*M4nwƄ.Pg1S_QNh~L֌TS-0'A"I) :vS  t> &N܅dxs<M1IsTGi ,GnЫk$B۠_W2O7 !ܭ=f9 P H*߂DHpvStXcJܲ ]F7vɃBM Xʳ~:7@D~C:QN۳&z> rk$mPǀhC[^m¡$ƴca G\ ;ѮkK؋mu pqph6JMk? ØqWW޸LOѳgO8+g !!JoG](ES YXg6—+,ǐn!48fGᆳ$` iNq!ܷl>MDԺbB{h=~_&$׳rB߮?h (:Gz\ɔl*IrK/ڱ#2@YlH/`c"2MUޤ t\}Mq8?σ/LDlfWvtBlj`v'&2=ʥhvG5ᇷD*7?h %Kݙp2➧iBFx9́t2 u@{rY  "ueE`ԭaPSdʔ:*fa,ʷlrK@3+f'??ub  bO uB+\NC.J P&tPc0R)"uZ#k?PtaߧOWQm& ʵ(KO'xkgmhZJѳIR\  zűSMVK{*$/т΍xmExdI;glb>oGX=hE@DV^ yKf'BMpy,IpY mMR8@# 19 )]`{kޝɄ@!pU+z|]6;G #xKd(GY 2^{dCDUrHyw 2%5|X߆~_^ZԊȸrUJ@1DC\ ZZFB| DVl@@ T P@ t@@ TyU랜aˢrVGc6ڏkZ!*k *l%g!‰۱'S};')gdjۣ_3}@vxhɇiGi[0oVjJh~^Lmy|*;o*+Kq|4jڿn Ji~]ތkv*ͧD > E҉sRȑU z/HbTCuC$xC+t_+s7=UQ =8F;I.I^"Hi#DXr,F7\ODP G #R(K^BqGKd2* ߧc+xcwhf9c}/\,+T&uG x:^#!yu]  ؗ_bw~Cн],|evsH-Æu[FdH'JNzLw}m ]gɈQVԣY>ERXan', z ɖ-&"'i;s#4+{4*8z. ͈&nG|;&8HI*3N\7+_G!h5hü=qu=*X5;7S \`o@IDATԽDF\¤#0^Y 1M,XlF!1{786;!תa)L>v0qkHF[<:6t3ױ|gBrZ겑rZR8Mc$G\uF'Ici=g^6in$}RndeYaS9>PZkk v?>>RaCy N@@  %ݹV# gR\v08Gs' ԈV7aݎf(WIB G42ONl۰؛{6Hda#)nEPma{I> GϋcSO6+&:Ycn;*{`c? ֱEe8m'ؐɐ\-> hK/<FM-c[]CL!r"<Gy!.DD?8+~A7LƯ7U@8"DŽ;/,[0t4,P3ΈŒCD ݈-ږ2 Qy8LNc"U%}Ƴ<Ma&E n< z$lG5bo!C*Gš1JhBO` +"zކ{Ebg[Sp @:fW?H̎xоuf/_]Dv{\:k7"eO nv$NͿ :\4+|I\`?cF?(&16q*s??HJaɇݳ҆h&:Li33_TxV"<-ݧ=bF~ZjBRQ4g&)Pe=CZfzTh"S"3{FdJ-DF,3]kĞ:`F,YH+I8 oTCTL9u ^()-=)뷤/3Nj@f\lS"cbc"s]xv)©{@&k4AI$v|}}0@-`"jUnUS]UXM=_F .ZO$vZY-@@ uybQ@@ xAdDW-^G@!7DMtE@@ uybQ@@ xAdDW-^GV^QWJy'>"@Gػ^9WpO&Vt"@@ p";]JZ]/Btg%R*Y:Lu<kYt z@@Y=|c% ^Uj_-qUdW=: M|*0(>GOYkQƍ@tȰNNQ>%b^ :垼22i뾂j ˧+x{a6 0>UV 9wW\!M^\'kʯkm9ͮBrm߬H# x{(U*^s?}3v!v$ sODz9pWgX h-c[s'H8K.]D^CR\+XOcApp0X;+g1|&n r̄d:j }cǮ<)ܑ|μOvθukc'x="$}hMDb\2Gr5G@8\x   oN[p0_r 2"d%_|0f-3'1y9m "SuY/a0#L3`o֜H؟  ̋>!׃|ҰEH,4ʧd~>%7!Ձ_۟Čqݱ{1#DőͻOZN{>| nt3Gst*;ϮGEE/jQӢ$,ɉ$Y1QH ]M.Kk|Ɍ|DР>d @j"Z16<|M[`,ڏsfʾ_SYH(CBDLsD7謼|gg b|4#x:J-  2LO3tvIDGÇj?k#@A)o;|yq$9v ~Q1 Ij$ТIyN":99(|C4>rGy_OssjљX0/\G{bcM3g,D)8MH?BMBHU#_sE),0EN4oP+͉ `3אAI27O܀9Ю!ޯrXZ5E˸2 /MYJ@hd8TrҜB᧩E^.m"*fv::ȱ .8 6"@򫫸4x@f<-yޅwߚَ-z`4K&LڰFpߣ_hx f#|p|,<:mDb*5=(sjOc  ]?|Snyz.L}Ww)I[2Nm8.4)"vےT%utRLHd"$jtԜ$ؤ)vRd됣_{?|40Ţ}xU/( i3щ3V ~^㤚 COSnQ ؿo#zM!S<,U _oGaF<hv7*J EhGxNF#TS6ҙcF Ғ$*C35&OGލ:s?ƽ|(Ɵw`]`H&hpoðD +7 Cpnzǒ?DHú{^x8IU\h? q_~$Ijysy0ڈE8k\"iiЉci(/<1AT75@nA#SPQŠsm~΀N3+iD|H$v):ֿ9nxz,xc%~1 #6S}yOf7ӎ2,āL;Jd""TVW !2CN%u%%ډQfAS3\7f⯥HF^>CFҦdq N#}7g&уAOiS1txDL [8aZ# HQ)w D*$eCZA9'!<njA|y(1ҀSuH.OR,i5_ +}0#}waoj_AP2v22~w(k,ƪrbb' 2-h|l5 : 4šb׎! SY o\rGK7 4pG7o~$;_[ r2aw;.'Ǥ'@}%i' ؾp- t5w*8,;>r"#·nis{sвWl%{d~O_̖ F$ ;Z$rA >ޘȞRkBpxj*KkFAᡩqCP!//W3 m~y;|8y|J4m>#bϱuxo oͣ$4贾ƀ܆4@UA[߅GnO!Hq+}짰]<탐fw^2? #k: (<z<$F{W`: G`<_m] y*YtE<_I0<{~g|Y$HЎdH)i-U?Y*=ׄb@hށ}GkɊ͛𿫕# h3R N}-maU" ϗ62=GmuDf%g9*]4c9I@=*5j8$uzQIjIӌb3tz?N,v:CDx|h9h݌"HNqJ a%7 j2pzKyYFAPQd>ImY(3Yitd;^BjU]JMMENAY9SwӞ۱dNt<}@mHgE1h[Eٹ.im4k~sL{23 U'>#:#GɁe[6Lr^a\L0[k[z1U<4R5k&;u쥆HkNgɛFK䤥89O{N~bV/ЪI0 cVAEK|<0iIyv_P+sz &29UjiMe 1wkBYa,_`hto(ot_HkBPhd0R󺝈IOۗgf|5J17\'$kx}*NQB!պWE!P/DVfS\/F9%ٿV;:1ayZ;zc -ᓕ_b=)^l&&߬xe&_?eɘO^aQ*Y,$ ll?lq"ܽ/=i բ׻F DYeE#.EVV-\2q=1%+Tv\xP6%.5]'3:i%1Rt=xDK{ɾu%x:}ʬ,2x9iU腢.,%$26% 8M>%[C0vs|Kiow  )*(꤁VQ~)ȗ|*OI:XrW]G:mA'T&Lx $c|\g rtRn˙*3L&Z΀DN:Pؐ}׷#!%z`,CI~uRɤi3`툦zϼs]ue֝P4spZ#B3jvę^`???I";-?y/ hٱB.'g2&{9_O3Vartk~AD6ڭm ' #;b=?eeUh;酢А3i)_†\}Q7%"5s f*tn}څD^VױF\:YH<:Hd6cÙo>90 DNy9ʉmڪ]TNQ>O08`m"` 敡d)՘te;r)µ}{B}ذe_hF?OZ7b#w0zǎCYE֭ڀtd+mݏr(0hhNj\K*po.7mڄ}"&&Øk%h@!P/v " -c°u*&Yِ]ҤJĔ\eعn3`~8Xƍ߅(! O(kǞ~ b%ؗVش" 2$Š} ̘2۱`6*` ( /+CyE18 00mXv-`2:\ndcԨQW`97ixߝ4$ Ä{3SX!Vn YӞ%UpAT|\ r4e@{ Ek!;(/(cIֹ֮[{8rջ L01JGLiao^WtCHh ~tXy{iV-i:v(YC åH @#D^TUeڛn@ФB?6E(x.7AJFNĐjva:C5i=jbMdTbMዞETR)/%iƥ1LTf@ݣ?6^l w<8kdN;JswB`[u^?FyI:E1z^FL$o_k#S77 &'eZ IJuG0T: 8A5础4Cޣoj^?NdIxIuLե+{K6TX%6ի_s;b0`t"@Id9 mc>x/y(=8ibq3T>4h(I"ҨHz9݁"b0qT=dNOw'@T"(3$uhfK'sd`2at,C(.Z*q(g Ѻu.%z4 ϙW~'z'R(I9@w v,6;+J@yN6DЗgAfm{@1p` h!#s.(Nv1 6 22ؼy6ltj YŘ`j=h"5GYf4Q&:\;x 34$'r'x/۝(d*1HL*Q3URE.@u:T.{<;&2^ "3yv"B"$.\x4|kg" RqaT.BZ.a2cIK9  =`YV/rDv掿c &0=3@" 4O` @;GTK 8oyC' @C@@YCQ@@ 8o7t"@@ 45^uF@yC' @C@@YCQ@@ 8oyC}d_c{ U}yա$ S(ObWdb=< L =-OEA̜1eLZBqC  ȼkʹcǎDpk0jm6OrbDzL`nrהɍ%^>A=Qp?w"*ݢ+䰙\\nJ')O9y}VBAbJn;ΘL$N:u\;\C&c_c8Iϖ;mu 4s\HKe>Ƙ]HFTIp@[]o 2{~h4jGl`fgO@~:)hΝH$vfrhչ:4ցv5kJDls`oZt0z,JFRe'oæTF\A"?.I'6LʀNG984mmFZ\)G΍F]h;`IՆ,m1O&3&-=vc0xU_/!u"zW| n;J EIXll{zu0V }ӸT3fC)Mp@ٯ{7S/ #ӬM>Pd|6{l=ޭ!X@YQ1 ;* k*BH*1 f]p}S"k}Vf1'J2| Mt%zO_IhPÇ-+GX૕S*`sԝ2P~s\9;S( E$5:)=Izh0]p4m>{Ulȡ,(.uje ČYk.-N߾}%o|_J/XD3 z!2n)4sUЀW0 fƱB4S%`6Itk]pC V~7[+h}Ȃ.cqđqagq@aH$VdX:̡P+޳ L'WM7aaw:'2c.!+^R7Wc[!53m# .iLy<}ʰy)!SRg#ᦩذG:_s]P5%&Cmpa4<$\ӊ"qQ0N&7cUX0,*cpI62G>[F:*{R9F0{%>\y(%.~X2CBByfIZ 2 Fd'A%IFJg[}A#_ȤuzlĻ#-] IqqT"KCgaCVh,1qЧs+h%uǯ,-{ Zy^Ux<6.YpC/--ն ?mBG<,f%n8ny(p4Y)P7{{}0uthR㻕+0dP;T'5w>bҧ?kH%(Jʿ7!vq}Xl[7'g>$b l`\=3:E(i}Cc0b(|=LdjFvPZPP"22۷oG^^DjjF@@ P?\"s5 aq1{0@+ABa3H:=a C[ġGo&Ý܇II-DEa}V;BƁ7MCHDTaIJblߒEkWP#|JSPbAqŠ\懔eĔJĨ>nT8,Cn퀀PoJ*IDDϰgJZ;syUO@Zi݋mR-)9e(CFaуp>A5ƒ"fLBdE+Nַ;;L8wL>3!?WZsuP:t6yὛgMn@Rӣ1 ( ?G'L e*D!5G eRiBzQZWH–5!";AHް鹾h?@j^roW?#*w&QUY՚TU* Uڢ=kFƿ:mj" ($"D-,C Ծ9HJrPozw=\*DZ|ԒS.6t-X~*l۸˷$8g%8x;B?^pz> 롭*G Q75n*52{tSTD>-[6u&an:5SѦ3IÊPǤQ{VnKC9K%S_"Iɋ{Øxǿ\7Z-Zn):f0|Xt郌g_S.#G-Ϟ?^1 0{Z6(zwxH{&)ߌ)$NN|3~UP㎛'#!-T?|s7v} c8Cib3IғǙA_! ԧI.B1Eێ^up (5o៯>C^jD-W CiDEz=y*WSk~(9i$oL9ƌ~9_@_RW- R>$)`*܉x+C𒀍HsM*IbV4y] [1l?#4 ;r569AVM@y)R- / oh4 58Wc'vN$#<< ZVmC f#5CFnkR2uuUf6DB}S=4B֚PSlGQzL\0O飶 n&smvs3S]u-5` 7(rte밊2SG{=Ul6Av2hC>ڪj8(o14-EcƎQ7-R9qai'yI%n~ Wk<68nӧ`tkypt{{C5$ !viiiN!byj"!!$u$f+'|GD:>뮁Icn]sT(LY;r@3|eVSXX޽{/==]&/XΕU! :@cЎ\9MΆU_æwRkd9ouZBO 7)rYnb}edd(-{BɁB3wliqӟSǏM2J#G`sRꖚZ"dA{k`BX8" q :`qRC { uh#UXF.N,f "}g,b<'1$@XWbhtT'XӹvE8:YS&:YGY4],PCTco  SƟ\"d[ #{-_$ns".:Ypt#"XfJUqQ*!Dz ! nqB@K@{B[!Q*!Dz ! qmGhrW{' B肔E^ɟ<$?Ş+r$RqOB>@!\޿l6 $x8 G?.ECX$! :@b/۫6. z8ogS|YmWC10pT.,V<} OL9bQ{-Aɲx"d*5؝vXpz(qCCGsA !ۭjB.\-/is7g567z4-?넮"d?{e#rE2i,!S)9vf֭/D/:g/UKd \=BRitpgy0nOEkaa}5q9hT$ݘ2j 7A|YGKVΫI'`B?Eʄ>)Q󥽶+gM1wEՋ^TdO΃ƒ;h` F֖*n8r ! 2 aԜ-]PnS!"7=Ӫ1yٕ8?<ⲐlO=alrXce M,`| L:~@''@<#Od;yXxZpo.Y``ðF"_/x;@Ґ $Fpb | .7߮]+A ˰#3Pc{o=\:5wnRg"WXC'? }0D?n@>&|p Rc%9D=”ts|c Lùw9^XTڄM{;0UKpH1=iZT`@K-U#N]o-G9 }IWSRvbӒX1_~MO7LLaej>}Z(q!7aݺu È#[ll~23kobkHB^|':"R^;|EFgA =>qT=RNd\9yx\l3 !mSfd4ֹ7|WsSbmSá-Hbj0`b}q EBBBF! :@  un W\8$E1|yujY4OE}_Ts rjƩWkiy?oWeu46Ȑ;>YBcf"gq`Qi!pS[W3-@/yV$MݳuW\\oViCbmJD"K_^s HƒMq[6!)~*RM$AQ! #b&Smp/`W 77UP!zI`LHKKbEfZ*OCƣHd}#56oCݱv'7c&jD@@d#%@n kP@hS%Ag;oH2xOFn{V~o(O# 5D#L2!f lګPXZ+D o.dۘ& d ғaTw N;{\;}qq|zIqPDTֺફ>4W8/:> G`ǎ|SVZZ*_wcpTj1!JG֝)&f!,' olh c;I(\=u2GP\AzPdf qg۱jhD:Pe5 chɝDd v6uĄ* .YYVqHN&@N .Xjy+KYeYN':{nRdz3[^ON$ u ^ʔ^XZ-F~){n_K^&;*x `UAG+< jC7LE밳DVM}QDMZ*,0m'e'ω#kBăU4hFP.'E͘ZrR4AB&>g!n ֐%{%SSA"vA}Ƙ1c|!yXi7z3F_>FAIyyyhK{)([nU\y Ν;Fc:tHޚp_Cm'FOH$zTKwAޅ s)BH"'W~ e3mSXhO!ӕXkҘH 3Gg$˃)UI )'($4qjl,;4^YDOgchICKpG*>D ޢ5ڼy2يIY;)Y%wwgoVL^Ҧ͜/G mɳs"bmᮼh9c+eފ'OѣB8<)!@+ Y M$V ;jbʑ̝qGB@Zd_@7uYXX8Mx`4Gu6 D7NcEcnًJd,d^ZZ E}dZc,df3͜ IN# Bi aKCRS[,Tl}Pss"'8jdkd2IbS&:Y# h{(,q?Ca,v!"~rD~6®AC:Jʢco?R! u{,5KvB@x-.E@,F! I@p! ."du?4B@!N]s;6KSkNo'gRMQu4A/pRkQPaQWJdBZҚܰ[.ipS@TQ$}ߐTe O 7q+8B0MyvAeB!FEE2ELk^<'22p|vK\#\%`Y=?wQ|VK2RSV!|hXj ׼NlTFoW:9RGX&2Ĩw#1lǿ~>yGAOY7ԃ/$~:kk!z Ss9UNq]@ QQQ8p`tIÙ##$ ОB,Ae0ݘZ {gLC?1F[kPj$& $4AˆZjf7=$nӝ2?Ρy= [TN{=Ys!q}e uu o1Q_Z$'?nSh$&5a6F"iMF1=-65Z3φ#uR$6|o$#qՙ _o,ʊ_MBʬΚSysFO} 6قV۱ n'Qw<+Sdx4Ku׃BѱCf)ᡇ_C΁ůw(DxozIB歇'xJ:1{ZB 4um)6,Zu}x}:TXݨ+؏WAt QۂXxO nvDYgz+/x YJ"k*KB@IC-2F;xPp7gV|ކ-++3"\mET-x ưQcQ׋AmQȢJ-6OMw`݉#bF̨'jj !<Β)'֠5 sn}S̈s.ze"js7@k|$Js"wb7tՏcӡBclQYG49SWl?#>}o/ j:Q"=nP!k zAT`8Uӕ0(։oW,~=<,bu-B@ 'Сϭ\/u{z!]?\C>|8\HN8tD=Dtt^Ar||=skpɀqfuR+禢fTDBtВףn{Zw% l^ qpkv:<,=tc' ,?Muv7dT!1H#{BZr舥,6j&tӰh\4.n .Li#0g9:[O@Ew݁O =F}{_GKkYq~p KFsy=p#YXr*]y9_mBM\&£=*۶ G\~HgĮ<ӆ_sf~/9`gԫk`z9t7}lZ@-4i93Q2XEEduՕƶU_o:*m22 Ly S=?Uo([Zw&.–e$ҴD_u)?@7-7 TXC|ܴ5Hzv .!̜5CN WZ*衘tM )f4N;-5OgRy ~ &cx8r<{2 )7Yy~S3ѣ4>iii;ZYsss1g܀熵+$B\l$9er3Ws$w _iՠt)"26 f=BtB 1@Oy8)R˫B^Xxgqn^W"}ptj$WwBUe),N"e6ij1-cmN5 :)lT%֦ Z*NrfԢC"mF]U %uYm.Mףu=VjrXexٵ-,7D .FRG Y]]~ߏZ@f#??M<}ǐ5'a/78 m<ε 㫇޼(*TNxIYj'vÅ`!EIbرc0-Zs555x#mx>mr \?O9|y~! @0P!'(7͒YJf0!nkdB@4'СB 54ow4dkEVB:Exn{T[cvK-#f!G/] 9\;x|Ӹj2–w[EƱ9J>-rȡD:\[h1ž[J ! @PhsbP^ %=Y ! @& Bֵ^! B@tm"d]I酀=Y ! @& Bֵ^! B@tm"d]I酀=Y ! @& Bֵ^! B@! B@! B@! B@! $x|EIENDB`Annotations.html000066400000000000000000003124631300200146000344220ustar00rootroot00000000000000connectome-workbench-1.2.3+git41-gc4c6c90/src/Resources/GuiResources/HelpFiles/Annotations

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 vertexs 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 tabs drawing region and one is at the right side of the tabs drawing region.  For the Y-coordinate, zero is at the bottom of the tabs drawing region and one is at the top of the tabs drawing region.  Since the coordinate is normalized, the annotation will maintain its relative position as the tabs 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 tabs margin.  If one desires annotations in a tabs 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 boxs 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 ToolBoxs 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 lines or the box or ovals 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 lines start and the bottom arrow button adds an arrow to the lines 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 annotations X-coordinate is on the left side of the text.  With Center, the annotations X-coordinate is in the horizontal center of the text.  With Right, the annotations X-coordinate is on the right side of the text.

 

Vertical Text Alignment selections include Bottom, Middle, and Top.  With Bottom, the annotations Y-coordinate is located at the bottom of the text.  With Middle, the annotations Y-coordinate is located in the vertical center of the text.  With Top, the annotations 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 vertexs 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 images 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 toolbars 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 annotations 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 Windows 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 annotations 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 annotations 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 characters 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 Toolbars 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 Toolbars 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 Toolbars 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 tabs 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 annotations 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.

 

 

 

 

 

connectome-workbench-1.2.3+git41-gc4c6c90/src/Resources/GuiResources/HelpFiles/Features_Toolbox/000077500000000000000000000000001300200146000322745ustar00rootroot00000000000000Borders/000077500000000000000000000000001300200146000336155ustar00rootroot00000000000000connectome-workbench-1.2.3+git41-gc4c6c90/src/Resources/GuiResources/HelpFiles/Features_ToolboxBorders.html000066400000000000000000000102071300200146000361030ustar00rootroot00000000000000connectome-workbench-1.2.3+git41-gc4c6c90/src/Resources/GuiResources/HelpFiles/Features_Toolbox/Borders 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.


Borders_Attributes.png000066400000000000000000001404751300200146000401440ustar00rootroot00000000000000connectome-workbench-1.2.3+git41-gc4c6c90/src/Resources/GuiResources/HelpFiles/Features_Toolbox/BordersPNG  IHDRw^- iCCPICC ProfileHTSiǿ-U tBFHB G`Du)  EdPQ }v={Ϲyss}{;.K HeHg C1t@@ ]bg \ƇA;&3ᅥ, F8NC$l0 kd fay!"N33?Ƿf{C<&@@lv"29<> I,y/NKK#˜/b%Jxn-e RYkߑ*$O(rC!%Oyqfg9I1Ly)pBJ C yBP(AAP1TUAF4t Cah z }Q0`=x l~p8N3\Wµ _`1 D ECiLPv(wT*6PZT ՋQX4MG>4ހ.AWm0zCb1&&b*0SK(aXl 6[݇mva#I39p,\www7'5x/|,WS.DJ QQOt"%#;E'x)-}|VDm;rn  u::t vI{t{u?Emk{od@1p105k53L1gx66J26i /,__\xȄljml2lJ377m7}DgI쒝Kz|76K5;d\<߼ۢ%re+c+~{Tllm6-6c:q5Cvvv%vW1nvqr8𗣉cc㋥KK-qrb9t;ӝrh\j]2F=㹫knfnBSn׻wy<=<<<#<\ّ%"psvuȜeWWXcfۚ^?Ee^nӺn6oި`hw^&⦔M@ `dBBaVǭ~@o嶽۾qW-a\'l+)ݿcpΆ2ٲܲ]Ew}ªqe^;~JvnQVqg-{Va<ύBzmTJfQؑGn8brZ| {K/w;rRd)ꩢ6mMD{R#NSz gK>{~K5~!H/ we{]{_qr쮵_vƩ߬;gvf-[Kv}ǝwwF Z>$ǹ~7L={yTXq'*4ȫ?2:Z/Όyz+?eym_nLDO~[NV''|H0ҧv{D}y>~MMO XB֬@! '$0o&Jـ<,9=sBH$+@8gd&XZ"#֤bz q|jV}@ׇ9_>XĿiйҞy?|OPiTXtXML:com.adobe.xmp 480 375 1 @IDATx] @eSPpR Q!e.=MS,ry4{5K='ZOP3 117T %P3sr"3sws7ߜ9WaF`j Bi7`FK.&33`Ffhz}5k7`FɝF`!vW^&1#`r?uۇH@hիڵkW.*[_ gaF` եK,fl߾b믿Z-"hܸ1|Ii+ ^,e #07| ƍ%Lԩ#FεZDYYYذaK'(Wba#p3q= kю .XĕHB|i ҄+f@3?eݼme5`>GO*"ncдST>yP(BgΜO?A5kSO=?R\]]#`۶my$۪U+ij]UׯIYoS{^1p'cB+N;եKcNNKTh,y'B1X=] Fm99DLZW {Bz݈؅ƹ;Yԗy 𩙅_rD7xڻ^. q$yOפM֣a 5 hתACt\p:ي:7Y/4^ӟ߿0E׽\g'r /^ˆ7!nVcOGQSx2/܅?]8/ݏu!P.f888HdICJV-\ :ERnIdjso#g'!'MnX/S<Q,֮m.h@)Ap";]"ĵk[2㱃}f {gg,^R(YpS@MҡN E-=Tfz6"oGNSAI"ֽ)K@roҤ ӡ H|ܹh֬Yy|/^ 1cF\8" \-E-Y>[:8 (kɼYjg4s]0{<-yj/vk(~Y/Mgk2_mSB[RDDYVuбz| $:!/rk)xK]\> ",٫'ŏolS}eONoΉ;YH=SW9Rx-(iݺ5h;ѹsg؅瞓+VD/"c-cEeA$} uϙØi1Ga2xG[)v1hw}u/|' @!k5ȱ! W*I.V kӉ1\)ۼl\+?'|:>lڋjԱ`ic}фr=>3W-lj89X_ \>ocG;nP'Os믿Ν;>fr%bna?hٲU-[sW!]a.>%7qm8׬΅8ܠ[pZ55KpC-ucPįQ&(vQkq54(A{]σKeVg[[,W}ĉ&rMÎ;p-p[z)) ^/T 5j`Ȑ!ll}xF`.sяypUE2C^z"[+2Z0#P- VrWSSSsMiUE 4W+/n/}̡Ȗe_R&0#`oTGJ`ݻ-2bրd&fؚ m4AqUR0Hs܀j d#P){UY@|/>qBBH7PV-~>P{#h2X!ՐVJ%qSe>|xE5?PŔ_ƃC* {ʕ+\,(8Z`BÜrT5}obtsN -S[Ű(S^b{0&~HIpXޡCktN/L 2)Vk{fts}4[T#Zl)VYeалzY )-ScF18{Kvvx-SzA-Sg;Т`i~  i2ɖ(hs{h BOWX rtW!tG_3ԋ[NEPNg n£˳"b/MNj5`XA SiÍdx=b^wH /F*|K6IA;,_^":~m[f9LKј oW3I|-KGBwWJmM/LU[ىqB&@j6ܷ\?يo0jt,5*VMUBYu ^-G.% *ZrAE(+`/qvV1T@AE r\Ii{hܚ"ky jLw5@\Ɖ0lt%IKRz׾nkqd 6xͯZr:_Af7Ck*(]VM;]GO{AFZHV._Np6XST XI֪̒Bw/fcѹ H9b5+Е.\" y%Xܦ'U,MF@a~\>*3t~ fQ訑- YD=Z0|-ޝ1ĵiZTK2E[鎩L-yW,Q/ubrׂg/*; w(Z!Z=ؐkA egFeə}>b8m{j: -3b,?wi9 ҏY@~2Z^hH>u_C S*Q6atԳm_+ᓺ#tFwDga٨u*#!i`t8I V㼵'l4ku/”-xaP&g'Fb?`.ꋹ+bhSȵx+H+Aܣ3[o: #Bs'mw6 Lws\7ݚG9Fwwx%qqm1v@4K`k`'obrpاQ| $As#b|_E&򕑗 0^ 7u(*>|4g -khqOŕwRNl{a{ag u~;;b1_p򐓓L~c# qش+U˥0#bw0m|8MI E ,wR839rW{3נ?\8{HPQ݁D\kd Kjv@-Juɍ8?pM]ߝǍ$.1JCa~61] ?juVDjmmb2گթ">3E,q Q;S<?#5WFN+ڊkaT_E|VuZ"o{߾} :b)ڽ&܀e}^ҎDe(SddE"jp%[ߘ sp[@'bo)O&Xz7}~2w㉖D Wɓر? ŇX`9$DŖœrbrwv* 3bGkTXX!b-δ?$耩Bx$4zqG6 J6͞tsn(F3zt!$%⟣%khW n~ ~],uG TX U:SY>ي Q)<"u<mtS:GAXʔ^N7bi 7 r{*87V-yq!__G[>GK}h 5}e/zي\=*V/27yY{k|z۵m)v47yEd/ZGRj$$Y'..)F !ϙ6=\r2pS Ah*jJTVY27:޲])k'Z'(=K87p.aD)?e[ Z/}F #1@ {:4~{]ȼ"CQt=v(( =zddA~>F/I(2詨O^m 7 5,4oo^-Mt#:Z}-& .nñ|)xafܫXc}L46x.\EGrR7d,*cLp2!FU#]L?'몗ZdDȂ8>azh %69x:QW\,-döwClWm㉸БIA'. q×!D'$^+ƵZl23 !ɸj K3fĀAa/]rF&[Zꩤ/I Q?JJ^̴M;9`iVD(-ldF\3R%y7 y”tAhfVmFƬف] =0d CUqH&i;`Op47\#k\"S"wc9& &2ǓFjs,w#ӚLwme܍Mҹ'Ĩ1 $10=((uS_*L#_9$& d;3T5 C)Zmz줴1]kƘj,+_ cb~l.Sڱ h:ji.| iI+1`Yh27Vm@ĚDzQ8K`k_7ڈŝvFЀү;9!CYZ(Zb.Ưkt͔ /&IqHqO" _N䖡6r,rZyRuz>BKN ֭6sb9f Q0=y:F$giAi]n'َa ;*w {!@)Ba,2 QչSWcN _K}Ҙy86$E/=ާİrr)+q^҉sSz"[`~f\nȂ4@l88k,ug wL ¤WA)vON\iyeFȠEB# ^%h%VjhL8e5"REZL𲚨y׀Jы{?]E:vLKF]N"uUi޳LJkW˥wEp^zkR^Oˀ*F|z?vଦ{{ərӐ ?3">iUQ ^!;Wer7n%H6B\ihZn:$oY{{ hБgc)i}*ݸePsW$"d?.&(3şҊHŕtZ>yrfr%6q;Q!iSɺlf[wϲJe/>pjۧx|Tgthc&={=N2QM/E}C4s üHp!ڬ ^҃ 鶥VҸbh._ hG5 :@[+6'j^[Myfo†&/bJx;t gкOb>%lђs V<ڒ 5k\5Վ q9Y/&L[ "$`s{xH8T=W,]PGJJ ك֭[WFdVǽ$45JrUFb%Ȥ$ic-,]|$~~L{ >HMuݸqE]czh1~msuh-oX/.8ZN|T/U)RO򼚼5WeWs#P?ͬ`F!~ϠF:ܫ[0=CA3#PuyN p`Fx5jTJ!wNAϞ= #0@# co,JSrBx)b}Y!wT`Fg_^./\%_|$滗KCFܺu Ǐ~aI)>33SZiG<%ڞ_ZCF!v:}#J L!#WKJS$x0}sy֪TR" {F`Lը3)#(0+H`j{5Ln #0 L gF!PᏘp nj/Y ԢG -䣎X…(Nh7`F2 !?t&(Tis!z5VoHd~ѲT>JH)Srg9K^ĝڤFN.DdhPu4!#dEk!jR,i?-q?. =0hI8RZ^Ꝥ2ɖIH{Jϟ?D#{`v2(M(75 胆9`#܅HZ% :J5 9jԒ?g l:\L1vgs Aj;qRXrQU,w aX0MmG7wo",0@ \6 +4b`#{!+ .ka( ٛrCd ݡP^=i \ȏ0t+B獐!D"PGLr&`#yr`ވ ?!.%xX}3[p`BS]MX r;.Dc6j#TϜ3X=[{شcPG)*"?m5ou$D'?F(6F63@Dn.2lø8} 򘄔v-x*rSBu-٫䇽8<kHMKٓQc2xvpLD‚Fg_3hS$*iެQ VX3F_ȅMXfM,®>3wchlmL f(Y'6K1gM$Ĝ% HE莥6b l?xλյl :"CI Sܳsz^=Gn(|8(C)K"wifVD V5ZIHU$0,O̞,݀5C)~*ll702a6QڕIGSc H~-M1A)5{P)L׈ qD%j\I#6o)$3@D] vt*"v nlԮczX"­r79r@4d7bg è!i2i4k;yS Y݂cd2VN&k*W,p[ /:֭O7ʙHfp!K z]GoK)v*:v-C|ryBhԧP[rC߈<-؊ibt#k} It$QCq :T@iŔY>d@QsE]x1@&p::p,;FW kڬf-^(Ex|a0#4+Й 4cڜHCiDTnx,,ɠR,<Cq5b"fwbR`&˝wgj?qbߜLkI,`hE#77=]-Ã1xtWK8CgH' S_Ð#=BAg*"@5RU*_6e{7.^YTm9w"e舼t6X8ڴ"JQ6p0U@X9goTɩo/AXCbzFUր*EݺuK͖Kry,W-;Q1RuT@&OƲ`Y>0, iD-e X\1';e,ߞ\fr/)2@%XY v{ _qS!\,Rlȼ͸ZU20/K X3]{'l>oLä W)WpTU+Mc6hnՖW\ ̬qZs!5vBjZ|Ţ7ї#Pf*Lygv"8fq+,|6Q~. B4fOX@G3X={ѕS>ڊZYEօ0o+CޅHL𑔘nE{)v?'B&"4ǎiP8@߱G*#7-KgOFDؿ? oGPDW#P[9¥ 5::QtaU"?O}Gcvؑkphp֥u F%ݺMxII* |nkڳ DޏOkc2Lj/:Ю SAi=:}S5Y4|^9U\MV-&?vxpT 9͚m5GXuB; ` mzw?BYOK)B)oy%*n~AfmG5P6GGGi[~M+dcqJێ9GIF`V3 Q>M&F)BV4;+/p3l25k# Ƅ%Km蛕Q92+zJ>{X#\%(]F:&@>:;q8jX%"EBvU_J)$?`l, @=%#gҔ zgqK1:#b R.^Fʁ FJb_hgަjb&|imBO@FcQ'/SvZą~%Lu?~sb<*BUD-[2iS/XNScES)N̤z咨r}"ParԪCȝG^#CAQ-sS$"sCA( RФV"&Y'\6Ц"*xK3LE<İqQ+7iM ym)!'thjMQܼI6fmJO1Sݛd̽dQJ8y$❾a;ICW!_ѹVP`,|eYӱ$oh`* Ѵ`vh4.de!޸=&YKV]1#-O 1O! t$CX(ĜB΅&DS+'cn"CUDXdrw׵3Jb"wǴHDTD֣Czpk#hPF\G1OЙ$qOؙh}J)kҸ“2K t%&hrimj'M4{Z@,'tIطoiI-ۋ񎅪Eh<Јr"Fl!}=+Ck˻EŸр`2\%IX&+q7in+["2'Hh@6-6ŌKv‚jtiF z)^-Ö{Ql_Ӱj3,wz3YG Ε[E}.{ oK?{0Zd੐SFrx +r8(5wʲz Qe+_qD= 9e/&Մ^O/a90@!P2UW"^5G}܀fH{a8s&-/ĹH1fܸBȽf͚HII[Vrah o'O|N$.`"xINѱB#u9&#TvGkזֽFUnu+9Y&8cAM"zjfE#TϽ"h899ᡇBzz:-vHZ C$D: :>o;0Uݑx*22_Z2D^i4i_,0F]AHX#0kձ&F` &wnn,# LNF+#`/0KOs;F`r2 n/=dBݮ04` &wnn,# LNF+#`/0KOs;F`r2 n/=dBݮ04` &wnn,# LNF+#`/0KOs;F`r2 n/=dBݮ04` &wnn,# LNF+#`/0KOs;F`r2 n/=dB@S=sEC4H쀬\7lU* |n?;&("(cZ '''ԨQnnnpttpS8ٌ@4r?sYNiBcWdN'rAxA={Veff… E%$լTO5\dtz3]@D:0Cb;vrrrPNb|04r?q5T.\ 2eS`72r@IDATp(37ˌ 2@l4hu И4^Kk>N)]KHiJ S7"S5:jX+#P4rkЉhH{ۀj$muԁh;~ r.\t9S\jPQ7)q+. ŗ=hCf.CO7#6>?폰pAQƑ-p %rZ6&zϹ]geW_5TF0G2z"|$W ~NjWa@k~?eGSt\#]nS|NǼ]Z2F5&'Oj]fyH\R+XVvxUE\L&b>r^pGm{8dzj5.>.4 yyZ2Q 1Ja(c6bYG%#P1*r2:։Z|CV;?pvT(71 &>D>z5>2dcXG|OVy @}OOtĿw@CESP@OX Փ"I3)uwYwV ZCA oTSZ_a{rt\ ӋXܹiRv#x]HnYHbr'+< g H_ ^-]NEi[ipU֔<$n$W[&lYTV\1BN]Y*BFuYyN`R0(Yp),3)hm )n.fHm ԘfE{FS*r>wZ\3ʱqO<ք/M6&.fF3r3rqުsJ=Z.-/tn- ӵdYLX"z>8PUZyyunG˴jܩǮC' x J\#KzrBRGye G{nuk -\&:Ž>B,L8Gv9! [5MOF ߲=Y0"xk?=ɸ(rrt훚1YNy0CF*s^ҍ]q*t&݃{`{5hK6#v# 9Hd;Wt#w}ѓTodryw"|gr-A~qq<{YH1cSyZQ_x BޤTLbaR Y4FAJ qr.F/0w_,ߺg/\󳇰j?ș<7ރbL ވW V%_Fw<\p8AbZ'_$exz'q*NZFo3 =@W'-ǡ)H1ßun7ՊFr< Lf<ՁhgHƼƈ XQ:x@1m<АLWsI1cfR"nڍ{FbGPA/W @.՘0S"g>1/0B/fS T`Z!tQzT79VE͏L>ވIfaޘi+0[8H7ƣ5E7e%ν֋Lo){A  s13I;.!8]s7,#^!Ad8`xFM鍴MaA'[\KߠbCQV(ec0*99 XNW*Ry0Qw&$ez4(Jm4IPYJ+khAm#B?K⡕p]'n)c;FL9SIQi-Ŏp/m:5- QwXRZ&p-3-yEy[1ؽ{7|||b^h {hf .}cǎҹ{VB]"[Fq⸇܎=(X )+/YV.uuE%84DA@iҬ(,]h0I$!Ѷ\׿=J%ocÊρACOt=>1|*0;@j͋XoKsv$q2⁼4D90!`wpM DX]M$> YCFg (<8KG"ig^V-3\Y1 /L0,%i  p\Lm:چd=I>86 3Fx(oraB'aH(_ⓘ+2i t"7oP<1(.5Z Xp1bDjj*f)Fu]<@h?Sϓ[A"?Čo?qTѺW_=#9E =OCx $=i!Ņzz3!B/\q(I8\9{?{uQP`*MĨQg@1Q mSJ#jl,k&Bj%1&'F RE(U4 P|k332s~augo'LYCo[>AV>OVgŒ`\vpd#k1Կ{:V @G ޹|Inv>PoVb=EikN^7 z(҄/$i&F>{CzR(}2ש!4mv2sr/@i0`)lUڶ)mѤANXmy2TAmԷvp@k!`ƃ "mS^ _E,ؙ=qㆴ¤; vN#H ,R6ltw@_-s7P ^@@ `9n9}%j*:J0 A@w+QS@@ a,rB @~5j$di-R3Z}W@ =_b\bH>sL[TZ <,,R[H:U#Ą nݰSE7 !BwNMB߀@@  ;U4I h[<"ExJ: 5550A؞tg+ASh衵շVD oMWioʅ{ڇ lYxq СCiv5I܊StG3h寐2>*.`|=]u RNzRKϼ<UpPX6#m/Dl scѠu꨾D^ d.[:l*!#`yy$.#q/\I慣u}UW4vc=&I#z9] xb5w ;x*\,JQP ML$R22 \)ǡ$\]]_rh 3y0#!1-/̈EJC963i;(0 {ԛOtyH7xj ^hwgܻ/cHb&k>qWϒ^ɭ"5RLqVx1ǰ1p>$fHqEbz|&'/wVT Iw#q+MaE<:6\uqí@IEI xid½)O@HdDw$Bg:#_$vhp#웈GdhN_5 Gql)8{ Fw]!fN ޲;[o1P}ԇ3QX;u}Í2_57a#+ W6` %4Ba?Qɘ?U$/[xgu9:}NڍcXwg`ߛvMl:5\`ϊa)OaSW~th݋x<*DЃD9bjv{Xa.{kY'Ǻ.~5YΫ{ wzv}z2YOkvbjfqlߑ3c'i׹c19%8WNH#K@{L$?dStwH*{.;v8?79@Pg!@Z#ھ?^#\П4M? $GzLzXEW9U4$@g1$Ȏ[ǎ++ a& $)PJ8y\c"cO+s)b -/Poc7Q|$昼mKA@,Y˶ d]_QGڹǥz5Hc#:!gXu8Фjd%bH d>$|tAn $e5%y9 |ةÉ=;c%Ӌ2QQHO[(?؁-PNTfCiNsHm/! '=e.ֽVkjnsYqLy6ܤ5Ìjf3V5e5 )Iɏ A4S`+:s4:0Jl {[k@-K5z .r,w(lG5=O9CM:]Z6 JRUGM|Ô4(v0a$ѿB e+aOfJ1tSbyFs!T<5&>H;_?eKQV 2ħWh`vz ܭ._л73ٳg1uT۷aH4OWYRzesp- :Nokڴ8"U(.({;'+nC^^|||:j\-j7pe[8'>7 k)^~`߯PޞF6q9ao4?PtZm tvnfi3[!^= k3„'_{!l=#ķ7^gW~K껑PUԀwm L4x`lV̉nxYf|q'/A4oM7L,# [f\\NvsDgh8(;8;7 y[Tdh4@ ,xŋ95T^6>z& ip1%kWW>"0xlbXl;DG֪v5E+64K~Cj=sv&Y6mRIwKXo+6}+D'DIPd($NjzT2 #^v3){dByS0~I'p23!G*8q=ڶ4?B zbE 'Ybd?lv` $%>#7k^WaϑXO&Q"06uYYY())`*֓9i 8$%7 w% zC8WwW yu*T̙ iiZzŦFALfjMbNђZxOYbh$e[5ف3ms=k%H `pOz4]7իi>& BwNUjSWaVhUf!O@@22IR B\Y XbɁe%!ۜ҉FMsTSS# p!=Yi ]IeibcZr,pЎf^ZZ i2dHOA.[mÂ[{7s$O]}_\T% :2&*]y󰧟~kWTԮ P;@֓4\>WUUЁy?hIXp/J@ħy(, MmYu_Օ]@R ebK~TWWcСmgSwQ?h'#ա09>y"_OB^,)\Gypʖx)yY/JtHXEe..Xr:\ Ui>tC߻8[ \t Lk0 &am 3=A]h`a]+ rYu^]O,> id6? I0a p3Hl4ۄjPH|dERl8B-> o7,*%Ds)Yņ4WpwEdn$D"$(B\B\@('j1oSشbYcH YYVR|T:1nڏF+unZagu\*X|pՒ/6ˌn`/J*Bs/=-,R9AxŜ &]S"#J [ Q9ؑ~{6ܖPLܠm>#s4޼ueH{ xdIGD0B?==P"bȡE[DP f'Z U|2/Yob}j:U ZC@j3KLV6[ tݮL0l~as{+pb=~uK/i-]51}EjʼuH83ܸnص*&.܁sgӷggS"4܇!{kxoi0If{UDI G!#|Y.lMʅ4; ]SWh@<>?žzC|#޷Gc(|YJzmfF'd!D~K#,,RƢ4-^rb7v~g8r.\kl@G)E`6dm`plƐ1!?G@ 2p+{E=ރ l92ÇR!TyD&P{itarS)1Sm ̀LQs\dJp1{2g!߀oVj@;#!h_m@!=@%_4yMJ,΅k Y=kP\4b`%[CMcI8\2NRs$:Z0=()˰,_=ɋ#%eUU!i&pSkS?gTW{Kz&,CMVGVeec>[h6vґHNg#!Vp8]2/=- Gݚ+"m y]KP\Z\$Oj*tFN ^ڒa!.Ǔ&+]l;EktEh@bF+KV.Mh-IҸP"טN'O: pĀ>ZA)uw`9n Zz/l~ 󜐠ΰ쑅* oy9$=G '݌x |TGҤsl ԛ\)I+eikN6Z9̶->f!q`Q 69i:h`_iX$ 6.Oe;.LocdyvuuRVyb|wٜ1J{E޷l=7tgk_Ο9JPA]O1,kM˕}vʨ^7^Q)ނ5w˗/7MΞ=SJn߾-7Qͧ$4sV99nBrV@ś bގ$iY^xfx"]>iBu&+"%R=ݸ~4~h/.*nTޮHTUZ=ӧ1VYECEߓ`0#+Lx 5Rc[z=B|{qV}緍VwUE H~f9tL3Yg!)lt dTzc|a+_VFJڳZ-Z{?OfY-d>IKan'Ivw~iDvZ@3Y1SS8~Ig?i -%Pf Xm[[[t{Dti,٠`얟HسfuVy7cﱒLNVd8p ***ÃG( lW鉿ihMA&;/yT`PBtpvmXcÞ~]QQ.@YO\;M:G~c8{oCoUhq|,ciT+b6w] 2T/kEH7;6[Nf$"<6?BL>ncHd"NW)>U֢-2M1...H\ͮ4XqXiL^ݣnHx]*=Ώ$]:!\>WUUЁy?j` PᮅfBB\4,L%QS(!SDB9w-h54^9+-]فO"7!.: iQX5Gt$E5 h`eСCnn"-* QGQTTs!*],Iu~q/b]z]??Ms%iB_OB^,)\ypA/1/e+-,S-v$qRK?Ktz?7XˏU=Ra:"aI-VLIx+3[Y}ǽlAZp#&Uyk߰S^uϳy O#I8r=!$cam0e҈^x9>>CN!s1P7!)R25}bձwYN#((|T#n&c9zN%41HTAn!k-N$}yr5P9iM&BFP[|!1PCIae>"e3Ox qB$*b F%*\|jpޔE-i5e]٪~ؕ玧Л7l]?M+fI+4x`YcM87>p#]ouӍ5+EN޻^UKn,3n(h@0 xs/=-,SsW؀-QJOIp>͇"%шd u'0vZL[OB7aJ%ǜDz1?z:Qڼoּ~V?^{Fq>.%شd-0WҲkJjGб]Co?|SvGrZ<]@*ӜHTFԅ۩}3&TxŜ %ԠQݔkbM>6PnCT`_v;3QG+9NO<9h 楐`?{Է/Agz26*?nql/b!U;|eFC= ij/fJEQ1ϵ6soTܮ~<ē`|06,|NJw2 _տApV dzS ,9J6F==l-i"ev<"FAJ6Wg $>CKr0L_B@GĢ|~VZcȘvpGBf>Bd128 ۀCz#LTW(lES Hփ{MX_"]J'q$ Z-S-XWuc 2où祕{9Q& N)9=1/&Q%I->rtjq3H3& {0ԁ0y:xd&4~5̙{x=X)t'Z#r#nt'ЭPa7Ҫ%ғ0z<E[ӘY(W%"Z2@lL;?J´?yE#UY ޒ&EkncG3y~4w埐UX]e!CbaokYEʢZzk0u (0f7J:iVk|S *w/T)P\Z\$O \7m)2O횆I H!u!\!:u ǎ‘cP ]c | 0h{$QI-Z#E7Pqs"DeH&8::beC 5X (qgte,u}BKkY5{Im7xasg1 |ތpZE5f1[tFI47_dO B9%YvyIWbD֫L oI>@ |dc\=-iuC$E#* xa~ADd ==>\ll#,"N;(=I>?ۚ_,׍-ɘJy\ ŧޤrAnQH3oř_bd(iPA*O,q4%IDATW1J{E޷ x1%c1}>'U8}fʃmwv!~1dy̦Xpu 5&>H;_?eKQVdyc{8]?Zo˗z_9Ϟ=SIE,t޾}[:oMNԢ l6ӢR ҴUII9؄0&c[ZIIRf[*FGowz[l!3/(fjt_: rrkyi-bnZQSwмngiA!nPYIf?kxRB9Mšĭfu2K&<bL7͚7ZL75e$ܦ;P]$%P7 <OiO#>~ V.Ct6SG#yNU&Zȩ-0꫹8r@LXj6 kc~A= t-S_av ¥K0\7mJڵ BѥlD/n ,]m}{Rzg-yӨAn} r-r鈊LKK3|/g!{]Y-2iaoZ֮=jި@|ƚvyޚ=}ID,,:;HO@I;ЀQ+Ir*`gz& !Bf~2BCa;¨N[ ^ _c9壅&r"#nRc*^*&p>׵ݞޑ,.!_}7 P⭄J T"pj|&-f!q1O\---cWB R7b[(-XSG)p쟵Z}}h7]_PPhغuo̔(n'GQjoqB= 3H3ޭ&'hQczzGOS"xAij;ٿp<>jX: xDJ o"zCy  Ѱ۱^NڍuNx٧k.͝+hǚإZc5b,ۀ3Gl92\9qX$<uUHV"((gF* E/Y|u0+LOCѻ/FœӑopʃQV*GAs@Xt9?#\,s]\nX6c!,ٺbݟn{iۀw*q&(?!ŐNLFFk`o*i9)MՂdD:r3/xe) nAqqݙ Fu/&$:eľ>+ A_C%8p-q87[UCa)L|i'#'|Ҭ~HXpkmJiB;|r_\ᬫ9GP2p\?s0|HJ/4 qj|4o<4A6&Mtd>"bXkwJ9`,ax#1b6w}L8;Zi~E XY=^DK&wVq¶#v{#KZ$aHX nz,/ @\VO) (Z68*/ Ykpq}z0'GAP醍4\@o3h`ȿ>/F+P&Q}W|EMa"bKc}֘+Pѧٶn̸b{[ɌwF1Vz4u88 &WTz-&|l5Mj6jl|%%dNP4 u"*u r[my yw@47ȭK7pƼ32/;L쨐fMˉ>jI9g[xŋfvsabˁNƾ+gouzٳgbԩwU_ ڐ f="[Ϯ7ֶqv}kp񮐦[G*B2Ȑi̧~m۶I;cʠoƌ17Je<_zkfu3lK[xǏǸq$]]4ej͚,Gzٻ<(Zj^<7Aw v Bw "S@"khf___n7oFdd$^xcϠ# {bq/wCX uC8o zguh@;;;恭,pY-ZMiԶ;2 YLnZ3EXmyˁ{ .\@yy9\\\hɭdg{ii)%Okv~"{]%]NO;O^v(N\Syb_~Λ ̣G_D  n!nPܻu =!{jϋv !ܻu =!{jϋv !ܻu =!{jϋv !ܻu =!{jϋv S8ޯܔz\^½=D|D~Nb jR{防{|OUUjjjIç ʘփBM^hZ=dx__?̐!C`cÇ Iޓz[ ;;>:;;+W0l0=+u%lW%(xP:0tarWHiq(q+JbO_~r 0=z8|)1Vg x}CTcj!<bL7m jtcoߝNmRoBa I;' vll6m=W%w6:|E\u94s& 2}=h]6NwMưa&i-vݔkkך|/g!`{>@jOpC;k0w`!?JcoH \@x w'ՕGʡJQ>AiH]1.f MJ4$7I׎D+uA5 YTg-|}A'v#܇ːˉUP ML$5Đ%}$>r>>!H**ވd:[=$/-2$j_Rny; `iwآ9׬%Wh13k[݌͝0Pzz:!= Q YXpW)JpU"4N >m4~O}oVжS_3 ǽRX`>D(\VB H\ i),$?VVx*`FĭG#8^ZSKtmAAob֭>@~~a !Xpw*7qa(ɊBhBѤu'*$M2^|VXJmȬ c4l|&MūJu1u*~:iݰQ\fxe@?96wR-f nï]nߩv Hp;u>f<(;0$#XgaCyҀlyMwPo|S$ѣYJni6x c^<)1َn8Y3U+9|ĺL Sgjo'JQWUdu,r> IgHXN~0I˯*> G"ه?G: s&OGa+FaG(C#F@||<.]*OHA=ܲЖD(W.,qK>o $"q8=\IdNcztGG”+E1LIgR_8iSr5$;F@qqYHuv\giLS4ho*i9)`؄^j~=^(=G瀛?F;/MVcZ0slاZ5f⫘f .SSH]ZD*sy\\K~*iЗ[%fDr6,Gu&> lx^δ|O>iVJSY,Rs/9GLDޘDX.:,&c3Zɤ*n:5sgf~C[ phez襛Fzjq6쌚Ids' mSk @C̅fMX_"]JL(dWߡ<V{Ea8|.^ ۋܳ;ߞ8@YuאqxYVAIȿws6lQXl:GW}+fQNed0Zy0qC9;9o}!xXpw3D?%'AN/ N[I(l9JyܣX{ J:Ӥx (x&FL|-MVf}{I|hXr, _^L& uPY ޒ4К[mkV_ 's#M>^Oΰq7l9'NRyeh*N /D ke0ј<5 Xl64Ctm7]rff+/-ؑcK GƘ-H&^Ɣ{ɼPL-O[q!;U$,܉$%۵_5J LÚ{x>B b!SiA%z^ ~ rҦ~C]釤2ﴰ].yGhw~ryciJ˜\RmG`+~j5V$j2$o1~q /"H4Fnhj9i!Be wBib*RTr`n`[T`6EEڎYF~zדR2ؙaZ$J+tP8PsR[,U\ 1eVG۩Zt%T 8 2tc5Y `RFIr*omEcpg-Du҆.>pkk=Zu7ϤG┬'TrL@ZG5;8y TV@{ ;*aryhOᣆ0`>25\/sH6S;s0zh@Bߥs|raٳg1V gІwyymxH>×fXg#ey+䍫E(8¾ew47//>>>Ry)$/ V|ضmt( 3Fr[Dw,D5]-t}yBڵkFtu777aG|-=ރ= {z xaso!,!-D@*4"B X.B[n߉ V½UhD@@ \pܾ5" {Ј@@ `n}'j.ZAJEp7@+72״ׅ=!$$sSlP;2KǔxPSS#{>ǀľ2w/ vhZ=dx__?̐!C`c' )]?-{Olk|l8.W\aÄoRjqmQmme VתB>2yS(hXL~FWޖC)E eS]#txTGM9KPn{u#v_Up7SJ>n()YcCSmk+_Vx9MK4@-#1O嶖L M}{&E8_D}[盗h/#JhgYI@Dx+C0~̟?^CwC.M?M_e!O=4/9#Ӟ`_|G\b^NsoT ML$jĆJ# yI>p( E"|_:ERl8"hKr#Ci [L hq"E%шRY[-:*ȑ0PHc:UR4$}yG Qw,!@M"16x0CaV BpWsJ Y E}WQ˓̙8y3x&n.Wh13k[~)]m|P陖ƿ>Y,njg}V-Hޚ@+܆a~xIVOkϛr%nzg iCyr%J^ہLl'}B/"a3/T%e ĄXC{*-7/s%5K#bt$(m6/-,Hš525-P$>䈵Nh$`폇XMDG]G 4; DUyIym6 }m@i^)f>"ziQ*6r*C1$.;ϕPC.ŕj_Ldg [7AQSu"œ|<×'5MZX`>D(\VB 1%#+\ 7YH W#U)ӂԍXb#q #l /gǩ^~k 7|[n|E[#Эֹ`K_Z$݄hwVJw! Rm`7>+b}d:yO\s6nȓ8p+BML*ـPM"2" \*IR.5a>T>P{ƺr[s*@h2wPNu NO$$f?ε{1MtRh*0/]rMBR0do OC:Tɩ4`T`B<,ں[K1EASlo4VTQ.,,Ajf6NJyc̓@f.p6"7JC1ձt`_aH3#(L CWHuP/O@a: Rpp_l!au$߈#'|R*aҥ0a:D% `]Q[wy}KO"<zYU[} hER,S)>fd ;Vƭ"GD! Z%"fv7 8g^(=Gӄܤ:i<#P˃ '-tQLKĪ EmƲmFN 9 ]9lOCt m>GC1Sb!:m}szmm"ME'K~[X\ɤdvSGCRzT'|&$'}1^;(:?&H$ 02@K)v mX/QRj *NLG*6-BF((JNU@pIp gr\B}ޙwϾ!{ yk_ s`(xy;r.u%ZK})PA_} ܠpW7؜`Uݲ!xX Gϵ` WP'8tZ@ɜ>ERδ XQ#kX%?a_G٩zkPu`r 4r'Pw~"A씖f|\TKþKܪyGNK{h9~/ Oz)1RA ~)+p 4{sv?6 ;*)$wٿ诶/ۆ꥓Tr[}ؿ \?sm©b^b=}UIi4'GI7 ?1"U'KI/՚AouR΁s.h l ŋhK- Cm/r=ҟ|cB D|؊4<ו2{7<T87Wn FfQ;ș;U uP;Zғ|ԟ8mKm!ܲjDP{Č|l9x!zߔ;0nG) QI͞ +f9<˅;O *jjK5Cx>***pGlG0ap^EƷUx qj\7Y#1l# ѭZ:2Q)~uINYSP&0 f/IݨegG&[_E żn#\inm Z2г 1jK_KP<UYe66]{(E3;Ԯ8"rS6EBR|cCP<&OǴRkC;PVyTviv#f϶ێoO#wmzJ蚮t ut1m2-XU1^-oi Emy1Zf]4VH_W2p_b$wwgKYr䦵 /~!i?r/j5\i7W@'O^qT:t'NTO9_׎C1zxI5V4|z/<uf~ 2Ao 2|Ժȅ`&4w,Jt|^74̕td{_rU൵ o MTf|CMA_Wj|j+e9ٸآe5 XmQª)4*AJj̸VdzSx?X>Of\\w{pmenX&f JĥW{Nh؁*Kdywy!4X`}Ge,oa kY_Uԋ" yQ̚5j^Ms K b`3Fcp>m2 5,TH5 rѢEP3en݊7Z?2om:WY+fω$~ Ğ=%**eRxwz\zgWz4PZո5l0\jnߊ+{\Av; w0U0 vgn'I&PutիW3g \·W2~wI ܝ*#CY^ 곘E@5XH@ZZΟx@UOg1,VjVEuuuvUOg1@\˘e"7E#Ū:'W8p %S]]cɴZwZS+w0ܻƉn!oúᇢӮn;OYqIC=׫~CzCa_p%lA#uwH$p7v&Н]wHH w#mh  ᮷TG$`(S6 R Z?fi=U L]gHL)w2ʹI&3᮵G$`,Ζ1z 'Й]gwHX wcp  0uvH%p7z 'Й]gwHX wcp  0uvH%p7z 'Й]gwHX wcp  0uvH%p7z 'Й]gwHX wcp  0uvH%p7z 'Й]gwHX wcp  0uvH%p7z 'Й]gwHX wcp  0uvH%p7z 'Й]gwHX wcp  0uvH%p7z 'Й]gwHX wcp  0uvH%p7z 'Й]gwHX wcp  0uvH%p7z 'Й]gwHX wcp  0uvH%p7z 'Й]gwHX wcp  0uvH%p7z 'Й]gwHX wcp  0uvH%p7z 'Й]gwHX wcp  0uvH%p7z 'Й]gwHX wcp  0uvH%p7z 'Й]gwHX wcp  0uvH%p7z 'Й@b=,}oǣ^j# - r ,/RplIENDB`Borders_Selection.png000066400000000000000000001455441300200146000377450ustar00rootroot00000000000000connectome-workbench-1.2.3+git41-gc4c6c90/src/Resources/GuiResources/HelpFiles/Features_Toolbox/BordersPNG  IHDRt,Q iCCPICC ProfileHTSiǿ-U tBFHB G`Du)  EdPQ }v={Ϲyss}{;.K HeHg C1t@@ ]bg \ƇA;&3ᅥ, F8NC$l0 kd fay!"N33?Ƿf{C<&@@lv"29<> I,y/NKK#˜/b%Jxn-e RYkߑ*$O(rC!%Oyqfg9I1Ly)pBJ C yBP(AAP1TUAF4t Cah z }Q0`=x l~p8N3\Wµ _`1 D ECiLPv(wT*6PZT ՋQX4MG>4ހ.AWm0zCb1&&b*0SK(aXl 6[݇mva#I39p,\www7'5x/|,WS.DJ QQOt"%#;E'x)-}|VDm;rn  u::t vI{t{u?Emk{od@1p105k53L1gx66J26i /,__\xȄljml2lJ377m7}DgI쒝Kz|76K5;d\<߼ۢ%re+c+~{Tllm6-6c:q5Cvvv%vW1nvqr8𗣉cc㋥KK-qrb9t;ӝrh\j]2F=㹫knfnBSn׻wy<=<<<#<\ّ%"psvuȜeWWXcfۚ^?Ee^nӺn6oި`hw^&⦔M@ `dBBaVǭ~@o嶽۾qW-a\'l+)ݿcpΆ2ٲܲ]Ew}ªqe^;~JvnQVqg-{Va<ύBzmTJfQؑGn8brZ| {K/w;rRd)ꩢ6mMD{R#NSz gK>{~K5~!H/ we{]{_qr쮵_vƩ߬;gvf-[Kv}ǝwwF Z>$ǹ~7L={yTXq'*4ȫ?2:Z/Όyz+?eym_nLDO~[NV''|H0ҧv{D}y>~MMO XB֬@! '$0o&Jـ<,9=sBH$+@8gd&XZ"#֤bz q|jV}@ׇ9_>XĿiйҞy?|OPiTXtXML:com.adobe.xmp 476 372 1 p@IDATx} @UU= n`.)p+,sY.N)XN:VjfS.5.ZLTR 2JF ( [sﻏ(G/޳|~ν{yk׮`F#^ V`F`B竀```KKV`/C!%%DڵC`` .]l5-*̌#82&7|pvvZw}x'*մyFpDT.\0~m۶I8IhsV+zaat>a<61iy6+zekB\e4$ej0p,s57_* `b_zbԍOc!M&T&W?LZ!`r={}'? ?سgnݺ%С:udQ5kf1ߍ7$balPIẂhBVN{fb.OfFQzr E7?o6W6M8)S<sjY_Pb;z(hND#F̟?^^^&EH/2J{sy be61pTE^y|.[8]ڢ+iGƂ0:th'_ciX7k7n1K1ʼnaD6g]N–c޿Ri4DE}FeםȅJ7bO۸;qY[>^h[~|C:&c0mMm^>d1I\"=2wi͛u7>@R?`ߋ{tO(~t"F1q[+'V7HwE߇;`; c^X?Of / }}' GAd,NNN!YA+ ܚn.O?_FB"Rז2A" л hrE'%}]5DPqU:ǦG_;a ,6m.h)Aj:]"ĵk u8߇Gɇp9{~{$^(YrK:mH-C'[]]@+{yC  `!]1= 2^** 5k6DL˗/CAo __2yX"XhYNXqqqRB-ydp|A@"_nsԍІ=zѾq'P[D[Ap3-?;ubAmQ H(\n(IKn[@s:1^ yus./B[菷xW \9vYC?b L_벅NnM%_ =-VBoWy^|ސ0\:vbId."=?Om"~Ek.sy.S $IOGL|FL+W g|}K<t:KCǏG_{Qܟ0{K0 ~]STDmPB5Q%.-Ah L czum|  :`D߸x'~5ACcI }{ƢNnxm ~g/R#J%QF@u9<>sϗ^۷h#' :T"sV>[\իM>t%,9U#pݺFި4 Fzz۩ѡ:1Mr7nJq"w{T5hoĵEpmMl-rZ5LTO]{&ܖ^T)"KQRq>|8BBB4kjZz8`Fj[u駟-T@Rfl"WIWԔvڪRdz|nk]TRFF^|)ӴE ͛hڴ)uVZ_/wdl@+z6R%t=GezYNL<t)WJnphAC ]dŏX45}bqsvoHVI@ҿU*˙F^! l\,mnޞ2.w8+J_FwaB@E+,gCmVNڃ7]+Ui9"5z>^7vKDW<מhtyquJOP 蠷Z: ÇkyѣץXT U5uMUrY\i@Pr*Z€1͘^n^Ө t)B`T6Y2h(t;M+Dӣ۔_Y(0Wļ, q9k--oK&+en}N.l_XNm*Gf7C[^O i~x9N"j2(ϴKr?λKR^Ja^ Gk+-pC'2`: +ZiAbJ,]N]5Rƒ\ĒIbvJ͏-N|~08# $YQ7$+4uFC!x*nS}ʓrKeߙT C-k ]<@glIX29MX:s͵`ZTti%0hLpӕ2/SqyK?u9Hr;,zuäa>:Bj`gR.7MIφ.ȐNv$^blt䝋Ê? E o1߼,|y8Vxs*9c0=vaیQ87fN$ޕ o͟$uBFw?%9l"Ƈb/q|?q>N lJ7=|;3<;9}\^W<3v(ܥ~`kbyQ1e1.||L)8j)6|&WFQA,j+ oYCxot,RQon "\LHy u\}m :~JoH!LGO_8EoL6 A^5sϥ4]Ӊo Bϔڑ-D݇bHڹ;94xį< Fwf8|Bp5&A5f>`zYpě(iҡDiL{R hAR焦!t[1s~9I5:ȼĀ/c,m$nẖK$>;L-Ԙ;Ɗ<75fk~'V t SG˺ɤ3թ{89"o-rs44]>>yHsVǾC"sZV\ cW&yi:JId~ }86cK1;m>YNrkZ?&&FSQoأ)8#BHJd$I]"bF?&Rwv?S]:=z99'i + ;@.8}v.A_TqW<3m:M ˧c?]ȼmh ȺtRmq!6r'NI&ЏlJr՟%2O¦)HK;uvajߑa֠>݅b@wj)x=yu8z yfaLyq4D.W8 ɗ!D"i]p[M^h㥰NzP|zhcΝ_$tdZRЉ AU2*eA8Ňm 2#[L1HPH+WnpqdeKP;tU0)%3I t2ƛ5ٵmO'*ց,_&VYj׳$0%kbd.7bv0gnx.b輍h)xOo/$ %Op~وX낋 i ȣ |~cmL&fLDW"拍.6:YP:]*~W+Fb@e_0-A"@St0GqcRU GمTVLpufͤsBHFZ) ϼ4ZGVtDmI?Eq!W P+ tfpJc+ ]E7oEuQD]N޻ׂh/2SKŚJƴ05vPjH%+],%a5+L1)^'?ٴH*6m>Mt8z mp]L /㚶-.Bd/ j_G̷rݙЁuqOyW΃еmOA:qbAb cLgƯ{4<5.i| Ndy+0ӹKSO)rxYBN&} m__liиe2iNA+󎽋}-]\={o\PKy+&쯞CbA;zHeFy8{oz|6/4ƙȔƁb.zx(`J)? x E@h򻠿׈IN@ ,&P$%|-45~;Lw:eˈ}?I4-*uQoLL- }腣>q*m4"w{4e*]GTcJOnj'9C._5^ aAFg(cp'D DwjzpkCGS}!-4э:+I>B E,EX'_wb[+  IWp.sxN7D7teQAXM>tK4Sv]G k4Bp5|6%Bש8xgCR&t{YԜ~r0~)y=3߬5v,epy9~k${fy;K/}GBzi-,x6=lq+FYLVýۺ X"޾:C^VRN2|l+̻̣AWD!"eܗ6`Ӭ!rt6'!;2 =ɜa}Fad \ȸX=(G?66>-@;d5_RQK_gTsNr~~Jq&!%#Élp)@$TDr%6Zw z)"aHQ1 roxXxlB!I8Y+AkFm酉8l);8}:Γ3Ҡӳ1ҝ9'~bذa +I^(Cw:OLЩ`RRg[~Ubē(=uY Q+D y$f ,jn^W-i\L5 + - Zy0rrAw[%d'Z2%[!i&+\Q{:(5TiImH@}WL\𿄽8r³ r+Z~H":1ʖTo`AJt.fޭaY9hMhZkQMhĵ*K L;,[6]sII4_н3NG.)jj>iu*a!xJyh4T^x+$ۚkquBk\r[tZe*V`N H_k]vs4 a#L¡cYZg)( c/c?K (Uݑ@e^r$m]gS;]RܰZG'`ơ-R(C-222?cǎ f)]1ǻ,9Yy-A[.krFbZZtnv̓Le]|m$~K􉉉RA4MДX->S}/m۶ 7W롂ƈsCwX8 ,rSp9?#0@A1zi`F>ÍK1#P`Bs] bF>ÍK1#P0rEr80#?Ǝ+5Db]zi-fF# 3sG+q(kz12io"tS 0#s/ɉ=oܐ/>(ѭ&tkp<#0:uJ%" ׯ_Vp֭ sJ_nj#hY2d4IfDn gF!Y,Uo+ b0=D@`+ j0@=C u7`k0[CFg0׳2#` &tkp<#0 ?,p34tK#<%ůykѯúG\-+ .2d,7f.B܇CY 16`X ivEc߶Ulw({n$ |pاI0 `.TWhИDu4Ge,U8|X*#a{q( hqj*Z*^o~IZV)&voHMM=F2f"z}`PYFNgFB3VtUjQK8?yt|͸F6F>/=)Ax%h5v7b,fy@1oK)]t|ja,۩VGjTee8##4lбm22:xg"(c5ȶŔN's'ZE,ye˖:=$gGz{Xy76/-F t*sTF LLƂmɥe`dDuW\Eb ZE8m-'/CYϢ qK"tِL? s嶇F,{JوpIւ 16& '}ދk#EF!`7l`a oZ nr*cʤxt8A2Oj=2nh{3+NįXF$l<@; ;O#M,Ƥ)HR(lxzz,+[q=(;g~F?DžX_^la?KIIq`}C% 6[^nLl޻ Kui[XU UЀ{n&-u_"88H͑+LƁ`&6Lfd._uµ\Ѯwe,ڍuZN|'@I <ycD`tP"|`$Zѫkc>{\D:WBWM m~>zl> 4*3X2 joz`Jz*KB+qZӧoi-ƙYGIGGjl42Vv)ތP*1җe`_!|K6@{Xhl:T0@Cn ]AZc^y'й l F-QUo5lsDŽoKx|2姀yE?8peggDc%S!J+4FG:yc\ [pkIl ڛT9\ZمjLrxKn`JVƢ0FhT7CT().FfZL iofonhV5KmlQ,φVsɅKQ>O.X{w|'ZXe f"d<׬ XΓl}=,ϝڊmx<Ȃyn*adQ:IG-:5+P4:c:(BW Mht26 ٗ. N;Yx(la=#4<MAxW"2fC7z6n?!998zQh4 "k..i۩;zP YׂW]2 kUk BJA~-˃]'Rjl&w3B6Dcʧa>HDd$mËs)v{lIKͿ-BBЯҞRizSix{.i}z7"ĈFw~S(v"=&cҤc(&Ff%`eK;tS7+D l*՟Ω9j3j6윕+S$o5#zGc.AdlnAsE%b҈~<|'M œEsЍ湏;0uN7;KL nTq'ڽyl2e9e[0R(FSrtÚ@U/>Yy9e Q05X|8 MBbmbҦC<;dtRlb$࿌#Pѯ_9q;IJzyײ\k\q+wUkT&;!j.p'd^ʖ\+!KF+9.ճ%<]-YldwӔAQ\-ҸØ?nAK<>PsMVfn˗S\\.nԫDE.jg˖y-cTG#PW(#щf#R#=z-]Q# PaW> Nd W@EIhқ+kH(*Xx{+s1gxxHS .ćk7 屏WE]*u쮤- ż0  ]mjעgg}2/! _GSiqd^ %בr &l ^5\`FnB mPŹ̕25"?Uعwv4cF!`7b]Kk;ufF`s`Fh07^dF 2`F @:`F F`L #Y F``F @:`FN81#]v)bj(EF`¸'F`j˗/׀jX99950#P3Kњ0#p`B] `Ff`BY #0&{F`j&0#p`B] `Ff`BY #0&{F`j&0#p`B] `Ff`BY #0&{F`jj-[3M;R JJJCߝy-*ʤOB KGhٲ%mg*!`7݇ [0GieW/ y.aeِ ņnxo70yn(΄ 1mލ eC#sU!T:+drƎ06\x=6hw\fR,m"5GNX1Qah\5t.z̉6T,Z}nR,Xuse'c g%ǁčWYVrNtR:+{` .\)y^K{#P;MڂkԢ<32mQhk■qf&6]%xl:LJc~0^ӑ("IcKP{Q>zRRj4l< bQhL z0%CN֡رГt8;cyx䗏wY`oJ؈EtAOTݍtD/\_5~iiG! #AKs=c䧆MRb d VAzmdtQڊ{Vvr3Y<#M?$Y-—6oooiېX0},Ò`p7t0_~=ؼ'Ev !:/§ F?!%{j%laAf:%whL9Z#yX6s/}y'iGx8?Q'Vf:EbС,x|i%#`53Aad# z:jP&rB/Agic[td}x'JyhZG{ztKa, `7=8eP1=z˓qd*Ǯ3V$dښp=RJ ϔAIq-EK%#Cg/)1Dϡ@1={1 Vl%1 4>f:yWSqq.z~02TpĞ2֧Ht2- O{NT{Aβ.8~%8Ȝ45<ś *W.Zo)W8visB4>ڙlD-Bn#Y" gHT%SMٿP%HBڂ<%d9>ar@^<9pC F1nh?V(I?l]4}u*A^JϽ"t 6[7l~pN.x)tp=?ё5sgΜ}"opt-)$mD̲O\bGJZIAċҞ&@ʪg#P}&tWш,{q 8mM[oezc8 ƥ"-^u“=Tn=%3T(7L1|0W OOOӴ ()Rz^^:dZҤFC o(Y{Z+CאPT@x `BjRf|h@dg [Yr_$<Fbꋈb>F Л5k&3+W~jІ.S"p M|)aKǂЍD"69}k(Ġ~T# ʇ^\\\p˴1i-M;G$uLBdÒi`oެ 0a=;F KQ׋MQ1 j 寂/U1(+6F#0gf;Ba]AEX9#0 K MGև`@ !dG@ zud@ !dG@ zud@ !dG@ zud@ !dG@ zud@ !dG@ zud@ !dG@ zud@ !dG@ zud@ !dG@ zud@ !dG@ zud@ !dG@ zud@ !dG@ zud@ !dG@ zud@ !dG@ zud@ !dG@ zud@ !dG@ zud@@S]- JJJCWWl/R*B4GCM:բS+çUM̯^ A`~~~Z'd\нveؘ'Pt5Ngky zFL&|@ ]Xj=zQ ' &^2ҸbW40WgFG2uDTj[pW.ϣӛ3L䱒n2 pqtS>aT ѢE 5p Y肨̃.̓P:јDŽl'՞TuQj4Fѩ]BFu"de\,aj y͊sp|W^_U%ʧ%?^w@IDAT ׋8Tұ0.Tq[6 ]笗)M{.\׺ז<z(ӿ+pB m/m-i&62Fzˆ#T jR?®b?QO}O`dml?+g(tQQPb-s%UUI%6ImmO=A}*d hR?;B{coY}51 >:nHES{ C$ӪtavT cX"ӗ+ٻt6Lf@`3)cs'Ұ|95ϙMXETQVKfw ZQB$.消!PZAn)'SXzdsƸj3_D#I(H@̣\wDWLDS'o.xl JV7To6*{F@-t[•aFᏯi*Mgk. %nw 9b?0g[*RapD^4=rH6AF\.||쌠o5KFARKd!z`Ivމd*8;Ywwd?c4CLTc&}L,(npH:0LǥVbkUprT/Y9z%7Vc3xmSԦ'I (d -u;5ޙH.eՠSIh0 $ ٭}˶a%=At2Y]9 GSD;6*m+؈#T @ױkj`4t#V#_8 KQcgcޙm(#RD{LRƸQ\{$)Ie\1/ s]k!}d ) Y]&PC:=s{ ƼQxUj:]H;żq}qhaVKCaW؈*)nxdۏ9abw5km*Tw<}"V :ts@r8O=>?/E*<6T'Szs"ANhCr$9_2 KqAzbɼ`r(Tc IaO-h?I4Ղ(CnIvG҅>.|8FO2F*:kb=/$maߏҹW(:y?N|؋X WKi ߘ2'zdc5v'<(i!TQ #io7a6Qz +ωw6E54`l{èA$:K'+Kka4ZJc7YgQN9vtUA}.1:=4i4ɀ LygP5?Dn%lc/@4)6 >SVfK~WA/‚6PYцϿGeOPlB)KolmzD51zX-Je6DȔDH2Mj5l,/ rn܍'@PsR0Ub:ڕGk! <|h( *ˣ/n"!dI Y)'*@)g5U8lŻ&O)i;]au" 2߆VpoiK@=05fWz)Nk.1.F ]PVB+:pQc㸇'ѭh'/5N꥽"BHWޅ ]9N--{T>E(_82/,e e"חƨLY>awvMQ cҽx"Ο?/3oRMNm4wƙv')*/9tȓ,aR0H,vTbخb+/Y%,bCM{Yr*N# cRIX7"7ńGZ//H߯.=ĻfDE{aO 8 "A<\\՛iY1EzD]#;!5ӱ,ʹxiwIig1&R8{4]ҩ@{%=OcFJWځ(j O+k||iDSFMcy -W2vՌcx!7G:ywg뷐Y3Rļc(W_^Sql7d| BqȠi"nK>bz%A6Tep3~?N."^YJAu :UՉNR@'ރeyKVpRc T:H^4q"JrudDg._SzPX`@F.'dDuENja'oWNJ ^sV>&f͚!''IIIxк5M<`Pr TѐĦ(́z:aP?`@ E˗/رcoZ$u-@fRz/#OAڄ.|֡C>5J!Ob2wg}@~XTqiF`;A N⼌#ar}3/..57Ugn#ajjڷ݄.֭[PhܸxqFhME􉽆~ٸDP#0Cn(#0@@nFVȜ-ё F`:F`zn #0v#n7t\`zn #0v#n7t\`zn #0v#n7t\`zn #0v#n7t\`zn #0v#n7t\`zn #0v#n7t\`zn #0v#n7t\`zn #0v#n7t\`zn #0v#n7t\`zn #0v#n7t\`zn #0v#4 ())[PPPbT*\\\иqc"ΞzۃaLv.<''G"Ν;yvH{E]L-ZmO펪=XqF^ n2oԨ(}GlS =XqF^ P|G⇴[eX?W9ekDYڶm[=,l[!? DbpzRmmm7- bA Ȟw#8'kyuV.}7ƨ)ֵscsÅO~(^'hٲ" Sw[L\a3`֍?:_E~ѶJ6ާbQTd@!ty&kPG0T{"ym]rѼYe}K^6$֓^Ƽ,Zy'ȽH݊wf4kX%Ώⅇ{猵p(D!I^ʓhS{]@!)qHޝRn=`4eoZf3ޥԢYRM.N<(=%\rc X6 HΪt3S<ڍ|fƠ^h=^<'N)7!PO]F.7Ѯ™v&{h}+iɔ>~iΤ'zfu((nމҳTQNrQ\٣XR[8D!..;Qš#kRnO~3][PLi3;лjf9'$[ &;cǎL"#}1.*/vEꔽ׊k}%νr%mfv{m/^\0ہt;J"͢G^#p`BM"???#._|0;"͢G^#pY.v .Hn1uuyV1^^^R"m%E{lB+Q^DVClثbQN ?{oVU(@g0%k G*_^U4p96#Е:T4L|L]+xMa)>*ȉO4rHNfセzZ^9B Ah=“rCy!G`}1%p89A 9pf.gc^G#@y!G`}1%p89A 9pf.gc^G#@y!G`}1%p89A 9pf95DE[t\uK~s8oeT{:Rr<τ#p̙ʅ̹p8C`f袚E:ujp3VximoF1ԋ_s8ERTsrږp.h^ G#mejt\c6Aw4+jqϭ pR*CBb1a -.\b@2X>UPآ^Oi4]5HAfi,0 1ij?©8wGC!bkJWjBRBL<4u qL/4JZL>44$ktT!pظc|4I cDjo@EEjg|MՂÇ0tRvTRev ̣9YD0~)L#FCZN!ڍ7]Q]#45HIC5ӯSZ@g3s;Z,i 64קґYDOFa N"+?+0W'%SQo|د\!ܔa= LdҳQq8qxSbᄼѯa ߿cQ~A#~ǸWWY(jL\0nk x,CB| US<|=|;Se)T%a4n '#wL~_*)Bv8N5FMvK ކ W}L &:,W;?=b~A\n9|n߿$K>{044&Ϡ@N1ڪA]q:"DFRVᱸ" 4ĞH,EOc.)³E8rSZֶbL_::t3CHsl,_LL9/l+O7-^"\j3I Ĩ(lT>A58޶rR@E(KwhPO3e@~2꣯9[ 䒌eoOyy$>bs[6`PK0H@VݲZ1[-ry'~m/`Suf- ]A >;{ѐDi \ŝIIU66H:d]&Mz| @?5' I ̖5hK03 MuFDKea+jC ^ δ*vUweMgٚ.YwnM@ac Kwe!g:6 j&^.x4PwDΩ?kL~nG qP=pG\7F8Zt0Յ=T »m̷pjGubĦIQΞR6މÂkTĩu?[xmAa0B ]&'>xǡ+B^VRA9˦pvdRuI Pc߶TDM9_vgv1]FRaMUةol TU_WC[su&e744b:tTVNؕI6r !Ą c|aGzyGiv>3H v#(qPvչH^Ck m+A['ߍ nI|RxPC,4R؛5!l Է|#bV@K ʾ]V,P}FP+,K/*]CF# MԿٳe!*nDen<O+,Zm+)f3ŞV Fd/v \G젷T z& pYعɐdc-]߄%z]b@= ϐPwndѹzx-M![n}:걥#n^УO徿[5Ptq[}^yp=p {|n n/cM^/t>B x(*˫@0Y*SXv^L-XeըM$Վ2R둼mmNsmOȠ\(sFd/"60l1nَk9-o@FOBZ+x%f\шZ'? tc¼h <ƞb.pw}te)7؏Hgn^ST#ؘVH"|@9y6zkL`1G&|ђ<&<|ڌ"_T4u!pI\J*%5-Z+Yğ@`fHϴNQ[1z )HDs`M2s܈.>8Ei3 'MH}cTڨ,%Nl1Eco z3Ȇ(W>ݥ䑊Hמ"?S'##ǐ;peyfDž(ȥ1> `>V#1 GL h00{p<+"UF CJbVPͤziGJ98a%4h2-w :sDs۰}0 u7t7aWlX_.\vWH+b7HrZFl$kbR NeTG'dU@_ELmSe-%$xPxjC|a DOo7Y ťAoػ@ 3[dau8S4S$>(j1D+RAaP+I_Š!yES\wvѱ>Kb{&"7~9poy4BJ NŬVs\Y}"pafud8ZD5 <¨ӱis{Wz0^mqGgr={l^u s[\\Aaٲf="s1O2!$QM&i? bN\쟚tI*Dd~ Q&ݐLhLژRéz^ +_?gi 1׉;pSoԗd 822ĝwGGYjWzŢJFwww,7沧ky‹VncPKo-RlM뾦۪4)Hy u֠"aBRΡTDy"3 C}"^j$ ;m}➄ R3 ʥۀ=hfy2nΦI<\WWWZeKq#ګ#*8mR0cyIoߘ$_Nf^bd kڞG&VT)OOO|Bؙ15K{fI0 ,\f1UX:DƧXvY]ץK.:cK Ċ³#d*;Zs7HCT&ZԄ 7_YU $#+i/^CTL/6UHؙD1445A !F{~1шnw2B m3y N(԰EQbeH<7u}={?jugqȪP.s;8:yB}1K2b\TO "~+y|Ccf$ؒنk#2BsA$sEVBX^tc"Ƽ5=Q٭wG" O/b'J:hY9ڟJWZUDuPcZgqpIT(*S^U4ֹ\KwW9K5!)/7Ժ;!19Vw!q-{ 3ơJjA=d'^.^X! )'+[W*]I\ =,2^^bk빍55>.q%;'xE8P%,To[%)WID b"vI}ῒE_ =}l2 Y6$v &0-J._f[ok;tCSZgۄ#:sxUDF5}_0So, 9ڞ;8ZHm}zdɓ'aÆIs`` N-qQL2=d-T*a6銙/@I nET8+niT[kdlKxE@ n"i#(}#5 @tq$\](70#-cOoekhŋ!J}tuǤV)qvKՅT1h<6V]ٹwQ/=2˿a Y6 g-視AEHH0\&ȰZe/Um%J]GD̅Њ`\IGuJ[FjbkirLIoYqSߣ}SB5ݕj}ƘJ-!cQǑY[d 6hld/1%5{"~!֭['cc::f_kQ[RS-o:&̇Hp4slmA4m* $q%e;:Amzv[JZ|،{%efK/Gf5;&Dm܏rz/҄5<#R:,l]n,?ByAkFRnDՙKfr EN{i {] &C&cnTQ̣y/-3ITsM!>~735¯4@$?xQŘf Afy fjb &]_/#F`z{G#p~p,Bp1\_s8K!p8kUr4Ɯ҈8K 4Psيe(e);M- pz|5]Mx)痑 x,a76/T$-HߣgFˡt31GE gLjThnCXO&MV #b`lQ !0 3gu)G~M'NƊ 3 8uSN6RbÏy?))FAF o&:S⁌C EE&q9m21pluZIa!Jlig5B*zYY-rVwpAB;Fsɰ11q:O_eH CL S/Edm3XlT}r jt= ɂ*%O1_Ĉ3Â5<[/Ryr=n`'r#e\Y5?q8sUo \R3E9SZl)U(Nr8/3j$^!(!˝+JF-}*u~E 5^7,}$yzت>L&WI 0[_+Fk2{wZZd  K_gr4m3 r$!u < l}2[a$wFGeq Л(С=z55xua#aS2Ԁ_p8B`sZA~mhz3r}]撣T_d)27fJuhnK{$QudTLyG`,V):ѧ+tsQ:\&O:NL\H<3SZS,pLp*zO4;V!4KiT'pb#@FX\[G;p.Э#r8ց)9+@rF#sNiƓp8ӀxN9EQI pf"{ъ"@Dkb۵x6Z6 tf$yMY;=MvA{S#>W};dGIk} 6X0 j0 x.K)f"~$cV̌x8I,H4.D %(דHJL.'#P:Nm2ue29"5c6Aܦm #+]H=0ˮM엫j$r|,ڢ1x G`&L fJ{]sZJyv3r&Q(|Gfd&5(yjh:!P\ѹQ)xhBɳX5V!tW09bZ̗6Tw_!ˋHI&\FR]cg酨  q|ܗ#Vrs:uJctv8E/]bb_ٱZ40XF)ܷՠ4\ӕwt'/l>"2[Iý8[GJ 'RN@yx>.|Q,d( eQp,t=^#=3{WFƁR<tz*p{S;n6Hܗ^خ_:=+~4]n_'z?~pfU\t=qTCСtIrLz{zӋ6FH"XbrE9{|t?JլAw?]Re#3G#`V_3km&p'rRWՅl{wS~JFLK_GMt`9:-1e}D"݄"$Tq8BT.ST.[o1zN`B~( W.mWODԚ֨.KN^egҡa/yMQȻSD<hWK,^ FnST~=sNAebG'Qb.\#C:,%a#}B^~p`bI;Fx7 a%>:Ui$w +ʥZ\$h…X.ct},_2DHwHίŶ_z3Wq)I]BB8 y|F 2.ؙb[3CXm0;8 0HL!Y]kXN]R6{6+hE<Q+%~ ^̤H 6]@$n[a7ba3:^:2d4Anspnk$. WPXbkfN%N_RlY"8)!`u]q1B5)ӠC;PM|Dgg7|ys&UiϡDhchIOCH)λ‘%1FzHOT )SKQJQ)oٔI3y`[Ƅz,QաEcG/"ըit~LS4$$b FzN0NQG ):T-eA[O)28i$3g2| br*СC_M286#jy >d:j_}o92RQnZ~Li*5s0@h gJj~<p1 [ $.qٷf3Mxh,1rtz9ˊ9G`X@jNQJpzn{v<2>fyEG'lwC_EA-Y.d=2RͰRwW.G83BhQ/1\:uJHOoT rI9nl)jiDy~%}(rNiƓp8ruDyG!5p,Fp\_#yGpniDy~G!wLx): xG`}_W haR,Z|/ZF}XwسcɨAع#A,y>LIDATp_׵bp_?8 >34'} ,r\Se x.8E#S/Ni_.(z E'>|T6`<=Lv##ZKCp\.\h鈫$Ӽ8܅{ Ed`&Wh* l{6 ?G`XdPw_0;q\4MO mlRs[Q[[+d2UN$ue < CȠ8' #L^ #.{Z8[;E n͜}Se=Y"4^r|$H_Zwƻr$z}.-±3̧ˌ?b l=l:#` F=uӓ9NKXe$@_񁲜ˎբ.[2w:?[(•KJI |"tS"h\StI 6͋_d Jb#pEJԥ Jг1N셣haQM KM3 9St*|L-MZ(,\Xnmwhgg'z͋ _u+\e0bJI>3դr) ǫ-}?):U>ЫW(()s/jp5f?⣨iWܟ@[Ww7]}/mx|T=^Z ;sn"):y>P#wt$:½"sP)V"V ݩpJX ߕѹ6yJV)*tI,.lE.jeD5碓':eW/푱ma3<9G#C*:[{|7.LHlG#0X@4pS҈8K 4P䜢'pfcp~poc 44LGǏ?6OX \[ICp~].hc): xG`}_o%ơB:␓x1r- 3l ev"CBKW=[D> =Eu `taW"um0/8"`2|'g t4;bA= q w߲:<$9_(n~}'OEu.?sijtjog*S*8N)bN%m/Ս2τ[4Opt~>V!-^ür.?,Zp!8r i7ȊnG3IZsBFUjݏɘ{p8SB*:|p$fKh?$.RjxEے _V{r3B[M8q8@ i9EO:Vep8kx^,G#4\[QG#Fp~r8K#q8k2 94@I8YGЧSZԁ$,y,5sJT$ @Gg55 S , M9v@^RA9A4aaIyAP 4up>"=*M@G&ppSb6Js#QAkpCxIq^,dBgrWMı7D]H^o ߪeQ5ǑzM/$SB[5^p=A"Y=] 'sB{IړoMo$(1!74 (h: ~գh&z$W~LV)JAaRzF?+TG8'/@>Sd) *メErH)SXQs#7a&vlW?nh3G#0u棨r9g/-">֭ǖdn;@+d2HeSwO!FU5, ]:5*5hL ,Di/ol{!#^ 8!`5]ql< wvv_(TtfSPQ$#-G%^XEiKgfD#BG#Pkg)..ƅ Oʏq+{`n7 StdL;5}P߄}ϡU^iFjD8=e\Q%&ƍ{;)&*t)~>7e{C$g[=)s뛥 ~} E`%{C 3yC p tFz6mh=ڌStlHۚp>8+OJѤܶEamJBuL ܃v@F4sIO%f<[}1E:Mj\a|P!iuk xVՍ&)*q뭷θB~(J'R^Yyٿev;w?KXp0\& +Ob:2Eݟ@Hw7]}/mBEӆ7Λ4윙b-xtG V#ЭSTݍo͊Nʶ? #Eau6+q툠D#{/L; "sP)$Zݝ w]ot6(  !`%] G#\<#'@Opf3'p8 9@Supf zP;[t"0_  <Ijצl'B,v 1$~s Ϧ4ٵx&yY1qj< ?{t&; Br&I&G_؝ݚq>r`ϢD`ތ1LTgTi(~]Uyxj` }C0nO;U{>g.&UD{"8m8طh|}Pq)$IP|<d~xgs^{ϱ% -s.Z b؟i:*—H|yl+5^ 6NH&GxIQbMֹr, *JGf5 5 5 l}EtEuYqP8Q suR"?e;X؟+rvb/GvA2)]" &tT?t:^Xy8!I58m3CKչH_;ķ.ԁՃ)jXR5'kp?<ȨZ˕^n +۷  =Q[lcVD|,{5nEli~7GsP(YF6H]GQԱ@W͂M6^EL cF#v`6Y\u˙ .L} q/_*ښqCpq$k,)#LԠ8S¦*<WD6A쉌ľR4R"<[D3vǑZ֢gʅcѡ5NY"tȡJ}4CK#4 x9df%"'/;TVvhĀI\RQd2opmS?R%L EeeT^Xr}% ݋`ds߀C+=2TNX2QfRMk!=Ln7x0]1CZ_=XTDYKgnWA^4d=QC$3l3 y9IC;I c;E<66H08 $Qx0[֠!/44D)Q6!my5lF>C #j8vU LASYl&K1{֝[PXx:B;TL*<+ձk P OGVJlK$ksgT-@_G.]7F8Zt0e`p܂7S) ﶉv&_ G&#68LrUq)PQ{zᖉg{&ՠܤx( +>9Nc }݂vpD&^-#T.D [?Up" &HNُt5]5I}1w[ТwNy3 Ș p`on x#ضvδWĶƤ!'m'orU6ǘPS'wǓq9p'KBhXxn tfQoUlt? c|aGzyGiv>3H v#(qPvչH } 5m%h뤰⻑Ba-9ؐ 6!w(ԒF5c}Q {&#ƟP.GQ ʾS\Y.D+>gE?I EcJyCٳe!*nDen< ,qpDgg3XTMPE".*HԐ YCh.'.ߍ %04  wQy\4eWxؗZh[#&M^ay4n;ģ:"%G?닰75fϙ\$˓jPu4[*$,C'I=AJM^dJSZ{ m ৮Nr@=X`H/ލh̥>>y ArV-)u{6$EPqC{MhAuadeIPJn4u5_3h/saAv ^܂( wvݝz~PUqiupp.Xe2Z,ca y$*}]wᣏ>S \A/A`a1ʃV# ,Xt#FSIzwMlyI:㡨,/FcNayj&MvxAZO6 4` hy"olv(ӲnZT+x%f\шZ'? tc¼H*z"AǧgDžK$ܝmFe/*e j:nn#Դ j Yğe86B(aXʢ\uԛHH5H"k DwPPl ݈iLj7'MH}T;ڪ,%N)t1Ecoxqi%֛@e+ӎ i=Ӌ:+OTן+۰D?g>#jaT>o:x_¿W1dBs n///CaDg65e f6u^o*s*԰#zR5aG:ަBtVzo'HY;E<'ymR ~Ǝ7Qߌf3ńe_BLِHW ^X쮶͒ j A$3F,&͚<9PUM}œ[/PW [J'n$w3BǵU@'zzQ2 ij3H sb&M^nB,%+ևK먫H-*^5'øR3=\Nh8voNdeRsj_yLgIOWCj\1'~.31B O8$0]" >HAZr:G =CJ}2eI?u):$`3*8&B1׉;pRoԗd 822kK g _腯#.U0ē.KefZ"ŕ 9P|mL<|v-5#z էpHe[x>&g}M^x z&Iê8ܝ[^— /7"L0~d$.:= n K3ы'dY.F("ʏ03CM`C&}O#a{&?`Ḏ3cj4G %@ˌsT-bԳ#]!Ncr]d=s\]87us[X1=>gG>-xf{Rt\ڂ!+5!xЃ /&,{!D21tIh$GCчa >IuoAmb:.|UX$_JE[).'I:m+"9^vq}VvA"JwMhijBSS #o,2m&{qBu7Imz9ߞ FZUUz1^<2l;BTtH"ӌtK}M8Io!RP4Pu4&}(,~+ MZM/7dEx}cőpx{ĢA^;ג/ ḹ1O{M T'i2|ghDM&:r@u  cON0<Xb@/Gͧ b -M^P@3OW2Īqlu7jMaSZ<[Rk ZkS uXVc@[Ay  նgxz=.b(nqcm-RK_WCQ Yl&pj͖ke>4-fqumi+{e1<0o7IeNr6m?6¤S\i,;߈8(ji[j3LvR6?LmK.i{˗X .[/=J9TȃQfMmX 鈥=q{*`GjWFI>Zs8c0E c:Vf'x '.[C3],^ ɘRP)4ūA*%_xL]]HӁJ7`cؕ+|7x0˿a 15"$>T`|a JFjKRm?~*!- PsAQڼGX-u#5|mO>KX*U$FjKiBx1SO:=ќr1Qe:Ǚ{n]Nhi ْb-lY; bi2>hUjFIWbX"/e l.*iG8q2]zv'``҄CGkJc<:Y8BJl8[GWP0TQu R3tDYnOxC{i,~Ihb$u7b6GGKǓ"Vd6 KЅ!>~7GyK\༯班A:'m7}*UxnFf\5/#^p8yx%8}8@ y G`p>s yG`^ `\,4(4 産x-\ۃFjNՄɎz~yvc2q']{uFˡt3yCUd @G=֓L6j%>h0/3l^\2 産xezq:u )'BCbDxG¼(BQ~"[ \6 Yqo~+:%lJA~V H)*p9_~S*e򎁞luZIa!祟Jlig5B*zYY-r;VwpAB;Fs "fXu⟂w}l+^z6g8m w|$pR'jt= "5J PbD@[ xE*OK'rRY\G#0X@7466efkeff(5[dn'!;3u_eF +%vs\@}*u~E LqLdkG%V2IsXMdk^al֦W;ݗMkvp a?~\2?n68JY=ٗAMM Ex "C(FS&Hvo<-gwxPa|XO#!h8A9z)]'kEƝ`&nE!P Bbx `bqLniLT00 CG)WO4BASir]z;͠#S"-FJ Z! H6L3TOIQf߿kn I,OZK :dפ{Êuwb H[ȸ)wDjfAfbA+sTlbomm2xAf&H9h`q5B^ ϳN`ei$sbaDPwyu5L WDU)!ҵ *OjRoGA>>?s8s tFkA\q„ vL$nFQJ, ;DH͸ ;_eWz$t7 &p+RD7HiPOH}y( O; x!'oc pfK3$$?0RmG3Աx1[PU0X?,˩X}*(jx f&>j<@q"B1xl=!ǽK8-[!<3S:< $@M bi(̧Wp378%5) %Z5lذhgT.#1dK@V?uYoǟA;s%ޮ%f1ެiJBd Whn.ud#L{epZ,m_n:xxL`1ڴ`Qoґ =gȂy8||*m|6:+|[h76Bݾ+f*둖MuhUKOR=_ x:ƔCZRR:s$Ɯ;g5Ĥ9U81MJڳBA*yu RO}k`\ ׎zpY|伅V~)9癀 ؠQ 2lM"^am6ڤM}L6>+vTA"x$[$R",Ėf##OQ=0{tM˭6{l()6_BQEikoBSҡ6l[.\#%Q"}O{X49@(F0It;NiMQPG\mE*=pjTW{WWBIJ(CB״d*nF~^A=ɘ\³y;)wyU:M h& =)-uwiC- ].E^^TәB!R} \=X/OKL @@3.ES6r/ :?MY#MPƩ'o݊ǣr"}~ 4n ?E$]ӎ*EuVi P&).U>*m6k?/I\-)8ITJ4c^SQST9`":! 72<*mA|#:8Yj!ҏk~X,9[]=ߦ|-Ha^9Q 0PhTwlNɇ>2$H^dCec\mmhksq ޏs1OiV3@9>U=IYhAo7pGvbc*y 4kЧhhW[ܑd$%#h^J;\2r[z:tM; $:+̚׽Eћ_H< '.DGNL E@s.)*|- ):߫k=#a'ow<ހ&MA5"zu؟>dPҁ kkE{Ā4e ^;4>8B!L B@3}*krAbCH:C3gSz߻e^d&·0GU<v!3m)vIe4%~S!NniPkr<5Ex(S I{k \5FwD{O - OeMё UIgTm] ޑheMQ5ir_L E z$YS4h܄ 01'0b`.63cL@cؠk2&p`~^[>3&4F .8.`.ĵeM q&Ɯ O)MQ=Hx_ݬ?q%4`+銞+:~/4؍8x} 3L omB9 *ja>MW3ϻyo~3I`E=Lf#Pb&0겦zIg0Bz Bܔ 0& 5E&1&6APdM q&Ɯf9Q`L`A [&ؠMcL L6wPtrL 0 "\ϚA@&L 96A Rݻ8s&<1o-ՂTR0ڎSZ-,C㥯 h0# JA\8nqlЃpͅH"Wn)&m C艎&y_i'@c$ 9@>it]Z(/Uϧ](F!m%IuFyfI%7jjZ>+)4oi }-XRCmض܈Vlowez@4CWCݍ _ D/ƅh31izlΣx2 P]]--u2F"5y^e#(WPO H cblFP\\`"P& 5EwTGEh+קHg3 ?BQW 3ЙB7z o?󋣡3Xa!zbjЌEmhhpX,dtTN4Eg͎Py/uwuz聊OWԓڭD2ǣ>Aʣ@h_VDI W"(c4&z{z\P-k-`q É E@3]h . {TTRZ23l M5EKN MIbF1_$*Y*)'M ߔwMX9%?7Iɣ[]0%,%a )Zپq 0h⏐V4Elɇ?`(ʭ9phAڣhtcϚŢXȸ\Iغh"Ky&~sݧ>gf V4EW, LF;j"Q3 f$"͚H':=,+%w,om1&"͹\p,Z(Dt=cHj&i f2$Y4 *ߗ!F׃+G?D=AE}elx&^^Kr\Ch#cq>c,ѻ{$c6M/E3PhƠkWStzGn$n/x 3q>HB#fQV|񤼊KȰ蕯fL D0n遖rn@42!mG \=@ IE{ޒJzhmq"mxss&<4aYSTڡ끎&ZrU $ - 0jcMQrL AFYi2IENDB`Borders_Attributes.png000066400000000000000000001404751300200146000365440ustar00rootroot00000000000000connectome-workbench-1.2.3+git41-gc4c6c90/src/Resources/GuiResources/HelpFiles/Features_ToolboxPNG  IHDRw^- iCCPICC ProfileHTSiǿ-U tBFHB G`Du)  EdPQ }v={Ϲyss}{;.K HeHg C1t@@ ]bg \ƇA;&3ᅥ, F8NC$l0 kd fay!"N33?Ƿf{C<&@@lv"29<> I,y/NKK#˜/b%Jxn-e RYkߑ*$O(rC!%Oyqfg9I1Ly)pBJ C yBP(AAP1TUAF4t Cah z }Q0`=x l~p8N3\Wµ _`1 D ECiLPv(wT*6PZT ՋQX4MG>4ހ.AWm0zCb1&&b*0SK(aXl 6[݇mva#I39p,\www7'5x/|,WS.DJ QQOt"%#;E'x)-}|VDm;rn  u::t vI{t{u?Emk{od@1p105k53L1gx66J26i /,__\xȄljml2lJ377m7}DgI쒝Kz|76K5;d\<߼ۢ%re+c+~{Tllm6-6c:q5Cvvv%vW1nvqr8𗣉cc㋥KK-qrb9t;ӝrh\j]2F=㹫knfnBSn׻wy<=<<<#<\ّ%"psvuȜeWWXcfۚ^?Ee^nӺn6oި`hw^&⦔M@ `dBBaVǭ~@o嶽۾qW-a\'l+)ݿcpΆ2ٲܲ]Ew}ªqe^;~JvnQVqg-{Va<ύBzmTJfQؑGn8brZ| {K/w;rRd)ꩢ6mMD{R#NSz gK>{~K5~!H/ we{]{_qr쮵_vƩ߬;gvf-[Kv}ǝwwF Z>$ǹ~7L={yTXq'*4ȫ?2:Z/Όyz+?eym_nLDO~[NV''|H0ҧv{D}y>~MMO XB֬@! '$0o&Jـ<,9=sBH$+@8gd&XZ"#֤bz q|jV}@ׇ9_>XĿiйҞy?|OPiTXtXML:com.adobe.xmp 480 375 1 @IDATx] @eSPpR Q!e.=MS,ry4{5K='ZOP3 117T %P3sr"3sws7ߜ9WaF`j Bi7`FK.&33`Ffhz}5k7`FɝF`!vW^&1#`r?uۇH@hիڵkW.*[_ gaF` եK,fl߾b믿Z-"hܸ1|Ii+ ^,e #07| ƍ%Lԩ#FεZDYYYذaK'(Wba#p3q= kю .XĕHB|i ҄+f@3?eݼme5`>GO*"ncдST>yP(BgΜO?A5kSO=?R\]]#`۶my$۪U+ij]UׯIYoS{^1p'cB+N;եKcNNKTh,y'B1X=] Fm99DLZW {Bz݈؅ƹ;Yԗy 𩙅_rD7xڻ^. q$yOפM֣a 5 hתACt\p:ي:7Y/4^ӟ߿0E׽\g'r /^ˆ7!nVcOGQSx2/܅?]8/ݏu!P.f888HdICJV-\ :ERnIdjso#g'!'MnX/S<Q,֮m.h@)Ap";]"ĵk[2㱃}f {gg,^R(YpS@MҡN E-=Tfz6"oGNSAI"ֽ)K@roҤ ӡ H|ܹh֬Yy|/^ 1cF\8" \-E-Y>[:8 (kɼYjg4s]0{<-yj/vk(~Y/Mgk2_mSB[RDDYVuбz| $:!/rk)xK]\> ",٫'ŏolS}eONoΉ;YH=SW9Rx-(iݺ5h;ѹsg؅瞓+VD/"c-cEeA$} uϙØi1Ga2xG[)v1hw}u/|' @!k5ȱ! W*I.V kӉ1\)ۼl\+?'|:>lڋjԱ`ic}фr=>3W-lj89X_ \>ocG;nP'Os믿Ν;>fr%bna?hٲU-[sW!]a.>%7qm8׬΅8ܠ[pZ55KpC-ucPįQ&(vQkq54(A{]σKeVg[[,W}ĉ&rMÎ;p-p[z)) ^/T 5j`Ȑ!ll}xF`.sяypUE2C^z"[+2Z0#P- VrWSSSsMiUE 4W+/n/}̡Ȗe_R&0#`oTGJ`ݻ-2bրd&fؚ m4AqUR0Hs܀j d#P){UY@|/>qBBH7PV-~>P{#h2X!ՐVJ%qSe>|xE5?PŔ_ƃC* {ʕ+\,(8Z`BÜrT5}obtsN -S[Ű(S^b{0&~HIpXޡCktN/L 2)Vk{fts}4[T#Zl)VYeалzY )-ScF18{Kvvx-SzA-Sg;Т`i~  i2ɖ(hs{h BOWX rtW!tG_3ԋ[NEPNg n£˳"b/MNj5`XA SiÍdx=b^wH /F*|K6IA;,_^":~m[f9LKј oW3I|-KGBwWJmM/LU[ىqB&@j6ܷ\?يo0jt,5*VMUBYu ^-G.% *ZrAE(+`/qvV1T@AE r\Ii{hܚ"ky jLw5@\Ɖ0lt%IKRz׾nkqd 6xͯZr:_Af7Ck*(]VM;]GO{AFZHV._Np6XST XI֪̒Bw/fcѹ H9b5+Е.\" y%Xܦ'U,MF@a~\>*3t~ fQ訑- YD=Z0|-ޝ1ĵiZTK2E[鎩L-yW,Q/ubrׂg/*; w(Z!Z=ؐkA egFeə}>b8m{j: -3b,?wi9 ҏY@~2Z^hH>u_C S*Q6atԳm_+ᓺ#tFwDga٨u*#!i`t8I V㼵'l4ku/”-xaP&g'Fb?`.ꋹ+bhSȵx+H+Aܣ3[o: #Bs'mw6 Lws\7ݚG9Fwwx%qqm1v@4K`k`'obrpاQ| $As#b|_E&򕑗 0^ 7u(*>|4g -khqOŕwRNl{a{ag u~;;b1_p򐓓L~c# qش+U˥0#bw0m|8MI E ,wR839rW{3נ?\8{HPQ݁D\kd Kjv@-Juɍ8?pM]ߝǍ$.1JCa~61] ?juVDjmmb2گթ">3E,q Q;S<?#5WFN+ڊkaT_E|VuZ"o{߾} :b)ڽ&܀e}^ҎDe(SddE"jp%[ߘ sp[@'bo)O&Xz7}~2w㉖D Wɓر? ŇX`9$DŖœrbrwv* 3bGkTXX!b-δ?$耩Bx$4zqG6 J6͞tsn(F3zt!$%⟣%khW n~ ~],uG TX U:SY>ي Q)<"u<mtS:GAXʔ^N7bi 7 r{*87V-yq!__G[>GK}h 5}e/zي\=*V/27yY{k|z۵m)v47yEd/ZGRj$$Y'..)F !ϙ6=\r2pS Ah*jJTVY27:޲])k'Z'(=K87p.aD)?e[ Z/}F #1@ {:4~{]ȼ"CQt=v(( =zddA~>F/I(2詨O^m 7 5,4oo^-Mt#:Z}-& .nñ|)xafܫXc}L46x.\EGrR7d,*cLp2!FU#]L?'몗ZdDȂ8>azh %69x:QW\,-döwClWm㉸БIA'. q×!D'$^+ƵZl23 !ɸj K3fĀAa/]rF&[Zꩤ/I Q?JJ^̴M;9`iVD(-ldF\3R%y7 y”tAhfVmFƬف] =0d CUqH&i;`Op47\#k\"S"wc9& &2ǓFjs,w#ӚLwme܍Mҹ'Ĩ1 $10=((uS_*L#_9$& d;3T5 C)Zmz줴1]kƘj,+_ cb~l.Sڱ h:ji.| iI+1`Yh27Vm@ĚDzQ8K`k_7ڈŝvFЀү;9!CYZ(Zb.Ưkt͔ /&IqHqO" _N䖡6r,rZyRuz>BKN ֭6sb9f Q0=y:F$giAi]n'َa ;*w {!@)Ba,2 QչSWcN _K}Ҙy86$E/=ާİrr)+q^҉sSz"[`~f\nȂ4@l88k,ug wL ¤WA)vON\iyeFȠEB# ^%h%VjhL8e5"REZL𲚨y׀Jы{?]E:vLKF]N"uUi޳LJkW˥wEp^zkR^Oˀ*F|z?vଦ{{ərӐ ?3">iUQ ^!;Wer7n%H6B\ihZn:$oY{{ hБgc)i}*ݸePsW$"d?.&(3şҊHŕtZ>yrfr%6q;Q!iSɺlf[wϲJe/>pjۧx|Tgthc&={=N2QM/E}C4s üHp!ڬ ^҃ 鶥VҸbh._ hG5 :@[+6'j^[Myfo†&/bJx;t gкOb>%lђs V<ڒ 5k\5Վ q9Y/&L[ "$`s{xH8T=W,]PGJJ ك֭[WFdVǽ$45JrUFb%Ȥ$ic-,]|$~~L{ >HMuݸqE]czh1~msuh-oX/.8ZN|T/U)RO򼚼5WeWs#P?ͬ`F!~ϠF:ܫ[0=CA3#PuyN p`Fx5jTJ!wNAϞ= #0@# co,JSrBx)b}Y!wT`Fg_^./\%_|$滗KCFܺu Ǐ~aI)>33SZiG<%ڞ_ZCF!v:}#J L!#WKJS$x0}sy֪TR" {F`Lը3)#(0+H`j{5Ln #0 L gF!PᏘp nj/Y ԢG -䣎X…(Nh7`F2 !?t&(Tis!z5VoHd~ѲT>JH)Srg9K^ĝڤFN.DdhPu4!#dEk!jR,i?-q?. =0hI8RZ^Ꝥ2ɖIH{Jϟ?D#{`v2(M(75 胆9`#܅HZ% :J5 9jԒ?g l:\L1vgs Aj;qRXrQU,w aX0MmG7wo",0@ \6 +4b`#{!+ .ka( ٛrCd ݡP^=i \ȏ0t+B獐!D"PGLr&`#yr`ވ ?!.%xX}3[p`BS]MX r;.Dc6j#TϜ3X=[{شcPG)*"?m5ou$D'?F(6F63@Dn.2lø8} 򘄔v-x*rSBu-٫䇽8<kHMKٓQc2xvpLD‚Fg_3hS$*iެQ VX3F_ȅMXfM,®>3wchlmL f(Y'6K1gM$Ĝ% HE莥6b l?xλյl :"CI Sܳsz^=Gn(|8(C)K"wifVD V5ZIHU$0,O̞,݀5C)~*ll702a6QڕIGSc H~-M1A)5{P)L׈ qD%j\I#6o)$3@D] vt*"v nlԮczX"­r79r@4d7bg è!i2i4k;yS Y݂cd2VN&k*W,p[ /:֭O7ʙHfp!K z]GoK)v*:v-C|ryBhԧP[rC߈<-؊ibt#k} It$QCq :T@iŔY>d@QsE]x1@&p::p,;FW kڬf-^(Ex|a0#4+Й 4cڜHCiDTnx,,ɠR,<Cq5b"fwbR`&˝wgj?qbߜLkI,`hE#77=]-Ã1xtWK8CgH' S_Ð#=BAg*"@5RU*_6e{7.^YTm9w"e舼t6X8ڴ"JQ6p0U@X9goTɩo/AXCbzFUր*EݺuK͖Kry,W-;Q1RuT@&OƲ`Y>0, iD-e X\1';e,ߞ\fr/)2@%XY v{ _qS!\,Rlȼ͸ZU20/K X3]{'l>oLä W)WpTU+Mc6hnՖW\ ̬qZs!5vBjZ|Ţ7ї#Pf*Lygv"8fq+,|6Q~. B4fOX@G3X={ѕS>ڊZYEօ0o+CޅHL𑔘nE{)v?'B&"4ǎiP8@߱G*#7-KgOFDؿ? oGPDW#P[9¥ 5::QtaU"?O}Gcvؑkphp֥u F%ݺMxII* |nkڳ DޏOkc2Lj/:Ю SAi=:}S5Y4|^9U\MV-&?vxpT 9͚m5GXuB; ` mzw?BYOK)B)oy%*n~AfmG5P6GGGi[~M+dcqJێ9GIF`V3 Q>M&F)BV4;+/p3l25k# Ƅ%Km蛕Q92+zJ>{X#\%(]F:&@>:;q8jX%"EBvU_J)$?`l, @=%#gҔ zgqK1:#b R.^Fʁ FJb_hgަjb&|imBO@FcQ'/SvZą~%Lu?~sb<*BUD-[2iS/XNScES)N̤z咨r}"ParԪCȝG^#CAQ-sS$"sCA( RФV"&Y'\6Ц"*xK3LE<İqQ+7iM ym)!'thjMQܼI6fmJO1Sݛd̽dQJ8y$❾a;ICW!_ѹVP`,|eYӱ$oh`* Ѵ`vh4.de!޸=&YKV]1#-O 1O! t$CX(ĜB΅&DS+'cn"CUDXdrw׵3Jb"wǴHDTD֣Czpk#hPF\G1OЙ$qOؙh}J)kҸ“2K t%&hrimj'M4{Z@,'tIطoiI-ۋ񎅪Eh<Јr"Fl!}=+Ck˻EŸр`2\%IX&+q7in+["2'Hh@6-6ŌKv‚jtiF z)^-Ö{Ql_Ӱj3,wz3YG Ε[E}.{ oK?{0Zd੐SFrx +r8(5wʲz Qe+_qD= 9e/&Մ^O/a90@!P2UW"^5G}܀fH{a8s&-/ĹH1fܸBȽf͚HII[Vrah o'O|N$.`"xINѱB#u9&#TvGkזֽFUnu+9Y&8cAM"zjfE#TϽ"h899ᡇBzz:-vHZ C$D: :>o;0Uݑx*22_Z2D^i4i_,0F]AHX#0kձ&F` &wnn,# LNF+#`/0KOs;F`r2 n/=dBݮ04` &wnn,# LNF+#`/0KOs;F`r2 n/=dBݮ04` &wnn,# LNF+#`/0KOs;F`r2 n/=dBݮ04` &wnn,# LNF+#`/0KOs;F`r2 n/=dB@S=sEC4H쀬\7lU* |n?;&("(cZ '''ԨQnnnpttpS8ٌ@4r?sYNiBcWdN'rAxA={Veff… E%$լTO5\dtz3]@D:0Cb;vrrrPNb|04r?q5T.\ 2eS`72r@IDATp(37ˌ 2@l4hu И4^Kk>N)]KHiJ S7"S5:jX+#P4rkЉhH{ۀj$muԁh;~ r.\t9S\jPQ7)q+. ŗ=hCf.CO7#6>?폰pAQƑ-p %rZ6&zϹ]geW_5TF0G2z"|$W ~NjWa@k~?eGSt\#]nS|NǼ]Z2F5&'Oj]fyH\R+XVvxUE\L&b>r^pGm{8dzj5.>.4 yyZ2Q 1Ja(c6bYG%#P1*r2:։Z|CV;?pvT(71 &>D>z5>2dcXG|OVy @}OOtĿw@CESP@OX Փ"I3)uwYwV ZCA oTSZ_a{rt\ ӋXܹiRv#x]HnYHbr'+< g H_ ^-]NEi[ipU֔<$n$W[&lYTV\1BN]Y*BFuYyN`R0(Yp),3)hm )n.fHm ԘfE{FS*r>wZ\3ʱqO<ք/M6&.fF3r3rqުsJ=Z.-/tn- ӵdYLX"z>8PUZyyunG˴jܩǮC' x J\#KzrBRGye G{nuk -\&:Ž>B,L8Gv9! [5MOF ߲=Y0"xk?=ɸ(rrt훚1YNy0CF*s^ҍ]q*t&݃{`{5hK6#v# 9Hd;Wt#w}ѓTodryw"|gr-A~qq<{YH1cSyZQ_x BޤTLbaR Y4FAJ qr.F/0w_,ߺg/\󳇰j?ș<7ރbL ވW V%_Fw<\p8AbZ'_$exz'q*NZFo3 =@W'-ǡ)H1ßun7ՊFr< Lf<ՁhgHƼƈ XQ:x@1m<АLWsI1cfR"nڍ{FbGPA/W @.՘0S"g>1/0B/fS T`Z!tQzT79VE͏L>ވIfaޘi+0[8H7ƣ5E7e%ν֋Lo){A  s13I;.!8]s7,#^!Ad8`xFM鍴MaA'[\KߠbCQV(ec0*99 XNW*Ry0Qw&$ez4(Jm4IPYJ+khAm#B?K⡕p]'n)c;FL9SIQi-Ŏp/m:5- QwXRZ&p-3-yEy[1ؽ{7|||b^h {hf .}cǎҹ{VB]"[Fq⸇܎=(X )+/YV.uuE%84DA@iҬ(,]h0I$!Ѷ\׿=J%ocÊρACOt=>1|*0;@j͋XoKsv$q2⁼4D90!`wpM DX]M$> YCFg (<8KG"ig^V-3\Y1 /L0,%i  p\Lm:چd=I>86 3Fx(oraB'aH(_ⓘ+2i t"7oP<1(.5Z Xp1bDjj*f)Fu]<@h?Sϓ[A"?Čo?qTѺW_=#9E =OCx $=i!Ņzz3!B/\q(I8\9{?{uQP`*MĨQg@1Q mSJ#jl,k&Bj%1&'F RE(U4 P|k332s~augo'LYCo[>AV>OVgŒ`\vpd#k1Կ{:V @G ޹|Inv>PoVb=EikN^7 z(҄/$i&F>{CzR(}2ש!4mv2sr/@i0`)lUڶ)mѤANXmy2TAmԷvp@k!`ƃ "mS^ _E,ؙ=qㆴ¤; vN#H ,R6ltw@_-s7P ^@@ `9n9}%j*:J0 A@w+QS@@ a,rB @~5j$di-R3Z}W@ =_b\bH>sL[TZ <,,R[H:U#Ą nݰSE7 !BwNMB߀@@  ;U4I h[<"ExJ: 5550A؞tg+ASh衵շVD oMWioʅ{ڇ lYxq СCiv5I܊StG3h寐2>*.`|=]u RNzRKϼ<UpPX6#m/Dl scѠu꨾D^ d.[:l*!#`yy$.#q/\I慣u}UW4vc=&I#z9] xb5w ;x*\,JQP ML$R22 \)ǡ$\]]_rh 3y0#!1-/̈EJC963i;(0 {ԛOtyH7xj ^hwgܻ/cHb&k>qWϒ^ɭ"5RLqVx1ǰ1p>$fHqEbz|&'/wVT Iw#q+MaE<:6\uqí@IEI xid½)O@HdDw$Bg:#_$vhp#웈GdhN_5 Gql)8{ Fw]!fN ޲;[o1P}ԇ3QX;u}Í2_57a#+ W6` %4Ba?Qɘ?U$/[xgu9:}NڍcXwg`ߛvMl:5\`ϊa)OaSW~th݋x<*DЃD9bjv{Xa.{kY'Ǻ.~5YΫ{ wzv}z2YOkvbjfqlߑ3c'i׹c19%8WNH#K@{L$?dStwH*{.;v8?79@Pg!@Z#ھ?^#\П4M? $GzLzXEW9U4$@g1$Ȏ[ǎ++ a& $)PJ8y\c"cO+s)b -/Poc7Q|$昼mKA@,Y˶ d]_QGڹǥz5Hc#:!gXu8Фjd%bH d>$|tAn $e5%y9 |ةÉ=;c%Ӌ2QQHO[(?؁-PNTfCiNsHm/! '=e.ֽVkjnsYqLy6ܤ5Ìjf3V5e5 )Iɏ A4S`+:s4:0Jl {[k@-K5z .r,w(lG5=O9CM:]Z6 JRUGM|Ô4(v0a$ѿB e+aOfJ1tSbyFs!T<5&>H;_?eKQV 2ħWh`vz ܭ._л73ٳg1uT۷aH4OWYRzesp- :Nokڴ8"U(.({;'+nC^^|||:j\-j7pe[8'>7 k)^~`߯PޞF6q9ao4?PtZm tvnfi3[!^= k3„'_{!l=#ķ7^gW~K껑PUԀwm L4x`lV̉nxYf|q'/A4oM7L,# [f\\NvsDgh8(;8;7 y[Tdh4@ ,xŋ95T^6>z& ip1%kWW>"0xlbXl;DG֪v5E+64K~Cj=sv&Y6mRIwKXo+6}+D'DIPd($NjzT2 #^v3){dByS0~I'p23!G*8q=ڶ4?B zbE 'Ybd?lv` $%>#7k^WaϑXO&Q"06uYYY())`*֓9i 8$%7 w% zC8WwW yu*T̙ iiZzŦFALfjMbNђZxOYbh$e[5ف3ms=k%H `pOz4]7իi>& BwNUjSWaVhUf!O@@22IR B\Y XbɁe%!ۜ҉FMsTSS# p!=Yi ]IeibcZr,pЎf^ZZ i2dHOA.[mÂ[{7s$O]}_\T% :2&*]y󰧟~kWTԮ P;@֓4\>WUUЁy?hIXp/J@ħy(, MmYu_Օ]@R ebK~TWWcСmgSwQ?h'#ա09>y"_OB^,)\Gypʖx)yY/JtHXEe..Xr:\ Ui>tC߻8[ \t Lk0 &am 3=A]h`a]+ rYu^]O,> id6? I0a p3Hl4ۄjPH|dERl8B-> o7,*%Ds)Yņ4WpwEdn$D"$(B\B\@('j1oSشbYcH YYVR|T:1nڏF+unZagu\*X|pՒ/6ˌn`/J*Bs/=-,R9AxŜ &]S"#J [ Q9ؑ~{6ܖPLܠm>#s4޼ueH{ xdIGD0B?==P"bȡE[DP f'Z U|2/Yob}j:U ZC@j3KLV6[ tݮL0l~as{+pb=~uK/i-]51}EjʼuH83ܸnص*&.܁sgӷggS"4܇!{kxoi0If{UDI G!#|Y.lMʅ4; ]SWh@<>?žzC|#޷Gc(|YJzmfF'd!D~K#,,RƢ4-^rb7v~g8r.\kl@G)E`6dm`plƐ1!?G@ 2p+{E=ރ l92ÇR!TyD&P{itarS)1Sm ̀LQs\dJp1{2g!߀oVj@;#!h_m@!=@%_4yMJ,΅k Y=kP\4b`%[CMcI8\2NRs$:Z0=()˰,_=ɋ#%eUU!i&pSkS?gTW{Kz&,CMVGVeec>[h6vґHNg#!Vp8]2/=- Gݚ+"m y]KP\Z\$Oj*tFN ^ڒa!.Ǔ&+]l;EktEh@bF+KV.Mh-IҸP"טN'O: pĀ>ZA)uw`9n Zz/l~ 󜐠ΰ쑅* oy9$=G '݌x |TGҤsl ԛ\)I+eikN6Z9̶->f!q`Q 69i:h`_iX$ 6.Oe;.LocdyvuuRVyb|wٜ1J{E޷l=7tgk_Ο9JPA]O1,kM˕}vʨ^7^Q)ނ5w˗/7MΞ=SJn߾-7Qͧ$4sV99nBrV@ś bގ$iY^xfx"]>iBu&+"%R=ݸ~4~h/.*nTޮHTUZ=ӧ1VYECEߓ`0#+Lx 5Rc[z=B|{qV}緍VwUE H~f9tL3Yg!)lt dTzc|a+_VFJڳZ-Z{?OfY-d>IKan'Ivw~iDvZ@3Y1SS8~Ig?i -%Pf Xm[[[t{Dti,٠`얟HسfuVy7cﱒLNVd8p ***ÃG( lW鉿ihMA&;/yT`PBtpvmXcÞ~]QQ.@YO\;M:G~c8{oCoUhq|,ciT+b6w] 2T/kEH7;6[Nf$"<6?BL>ncHd"NW)>U֢-2M1...H\ͮ4XqXiL^ݣnHx]*=Ώ$]:!\>WUUЁy?j` PᮅfBB\4,L%QS(!SDB9w-h54^9+-]فO"7!.: iQX5Gt$E5 h`eСCnn"-* QGQTTs!*],Iu~q/b]z]??Ms%iB_OB^,)\ypA/1/e+-,S-v$qRK?Ktz?7XˏU=Ra:"aI-VLIx+3[Y}ǽlAZp#&Uyk߰S^uϳy O#I8r=!$cam0e҈^x9>>CN!s1P7!)R25}bձwYN#((|T#n&c9zN%41HTAn!k-N$}yr5P9iM&BFP[|!1PCIae>"e3Ox qB$*b F%*\|jpޔE-i5e]٪~ؕ玧Л7l]?M+fI+4x`YcM87>p#]ouӍ5+EN޻^UKn,3n(h@0 xs/=-,SsW؀-QJOIp>͇"%шd u'0vZL[OB7aJ%ǜDz1?z:Qڼoּ~V?^{Fq>.%شd-0WҲkJjGб]Co?|SvGrZ<]@*ӜHTFԅ۩}3&TxŜ %ԠQݔkbM>6PnCT`_v;3QG+9NO<9h 楐`?{Է/Agz26*?nql/b!U;|eFC= ij/fJEQ1ϵ6soTܮ~<ē`|06,|NJw2 _տApV dzS ,9J6F==l-i"ev<"FAJ6Wg $>CKr0L_B@GĢ|~VZcȘvpGBf>Bd128 ۀCz#LTW(lES Hփ{MX_"]J'q$ Z-S-XWuc 2où祕{9Q& N)9=1/&Q%I->rtjq3H3& {0ԁ0y:xd&4~5̙{x=X)t'Z#r#nt'ЭPa7Ҫ%ғ0z<E[ӘY(W%"Z2@lL;?J´?yE#UY ޒ&EkncG3y~4w埐UX]e!CbaokYEʢZzk0u (0f7J:iVk|S *w/T)P\Z\$O \7m)2O횆I H!u!\!:u ǎ‘cP ]c | 0h{$QI-Z#E7Pqs"DeH&8::beC 5X (qgte,u}BKkY5{Im7xasg1 |ތpZE5f1[tFI47_dO B9%YvyIWbD֫L oI>@ |dc\=-iuC$E#* xa~ADd ==>\ll#,"N;(=I>?ۚ_,׍-ɘJy\ ŧޤrAnQH3oř_bd(iPA*O,q4%IDATW1J{E޷ x1%c1}>'U8}fʃmwv!~1dy̦Xpu 5&>H;_?eKQVdyc{8]?Zo˗z_9Ϟ=SIE,t޾}[:oMNԢ l6ӢR ҴUII9؄0&c[ZIIRf[*FGowz[l!3/(fjt_: rrkyi-bnZQSwмngiA!nPYIf?kxRB9Mšĭfu2K&<bL7͚7ZL75e$ܦ;P]$%P7 <OiO#>~ V.Ct6SG#yNU&Zȩ-0꫹8r@LXj6 kc~A= t-S_av ¥K0\7mJڵ BѥlD/n ,]m}{Rzg-yӨAn} r-r鈊LKK3|/g!{]Y-2iaoZ֮=jި@|ƚvyޚ=}ID,,:;HO@I;ЀQ+Ir*`gz& !Bf~2BCa;¨N[ ^ _c9壅&r"#nRc*^*&p>׵ݞޑ,.!_}7 P⭄J T"pj|&-f!q1O\---cWB R7b[(-XSG)p쟵Z}}h7]_PPhغuo̔(n'GQjoqB= 3H3ޭ&'hQczzGOS"xAij;ٿp<>jX: xDJ o"zCy  Ѱ۱^NڍuNx٧k.͝+hǚإZc5b,ۀ3Gl92\9qX$<uUHV"((gF* E/Y|u0+LOCѻ/FœӑopʃQV*GAs@Xt9?#\,s]\nX6c!,ٺbݟn{iۀw*q&(?!ŐNLFFk`o*i9)MՂdD:r3/xe) nAqqݙ Fu/&$:eľ>+ A_C%8p-q87[UCa)L|i'#'|Ҭ~HXpkmJiB;|r_\ᬫ9GP2p\?s0|HJ/4 qj|4o<4A6&Mtd>"bXkwJ9`,ax#1b6w}L8;Zi~E XY=^DK&wVq¶#v{#KZ$aHX nz,/ @\VO) (Z68*/ Ykpq}z0'GAP醍4\@o3h`ȿ>/F+P&Q}W|EMa"bKc}֘+Pѧٶn̸b{[ɌwF1Vz4u88 &WTz-&|l5Mj6jl|%%dNP4 u"*u r[my yw@47ȭK7pƼ32/;L쨐fMˉ>jI9g[xŋfvsabˁNƾ+gouzٳgbԩwU_ ڐ f="[Ϯ7ֶqv}kp񮐦[G*B2Ȑi̧~m۶I;cʠoƌ17Je<_zkfu3lK[xǏǸq$]]4ej͚,Gzٻ<(Zj^<7Aw v Bw "S@"khf___n7oFdd$^xcϠ# {bq/wCX uC8o zguh@;;;恭,pY-ZMiԶ;2 YLnZ3EXmyˁ{ .\@yy9\\\hɭdg{ii)%Okv~"{]%]NO;O^v(N\Syb_~Λ ̣G_D  n!nPܻu =!{jϋv !ܻu =!{jϋv !ܻu =!{jϋv !ܻu =!{jϋv S8ޯܔz\^½=D|D~Nb jR{防{|OUUjjjIç ʘփBM^hZ=dx__?̐!C`cÇ Iޓz[ ;;>:;;+W0l0=+u%lW%(xP:0tarWHiq(q+JbO_~r 0=z8|)1Vg x}CTcj!<bL7m jtcoߝNmRoBa I;' vll6m=W%w6:|E\u94s& 2}=h]6NwMưa&i-vݔkkך|/g!`{>@jOpC;k0w`!?JcoH \@x w'ՕGʡJQ>AiH]1.f MJ4$7I׎D+uA5 YTg-|}A'v#܇ːˉUP ML$5Đ%}$>r>>!H**ވd:[=$/-2$j_Rny; `iwآ9׬%Wh13k[݌͝0Pzz:!= Q YXpW)JpU"4N >m4~O}oVжS_3 ǽRX`>D(\VB H\ i),$?VVx*`FĭG#8^ZSKtmAAob֭>@~~a !Xpw*7qa(ɊBhBѤu'*$M2^|VXJmȬ c4l|&MūJu1u*~:iݰQ\fxe@?96wR-f nï]nߩv Hp;u>f<(;0$#XgaCyҀlyMwPo|S$ѣYJni6x c^<)1َn8Y3U+9|ĺL Sgjo'JQWUdu,r> IgHXN~0I˯*> G"ه?G: s&OGa+FaG(C#F@||<.]*OHA=ܲЖD(W.,qK>o $"q8=\IdNcztGG”+E1LIgR_8iSr5$;F@qqYHuv\giLS4ho*i9)`؄^j~=^(=G瀛?F;/MVcZ0slاZ5f⫘f .SSH]ZD*sy\\K~*iЗ[%fDr6,Gu&> lx^δ|O>iVJSY,Rs/9GLDޘDX.:,&c3Zɤ*n:5sgf~C[ phez襛Fzjq6쌚Ids' mSk @C̅fMX_"]JL(dWߡ<V{Ea8|.^ ۋܳ;ߞ8@YuאqxYVAIȿws6lQXl:GW}+fQNed0Zy0qC9;9o}!xXpw3D?%'AN/ N[I(l9JyܣX{ J:Ӥx (x&FL|-MVf}{I|hXr, _^L& uPY ޒ4К[mkV_ 's#M>^Oΰq7l9'NRyeh*N /D ke0ј<5 Xl64Ctm7]rff+/-ؑcK GƘ-H&^Ɣ{ɼPL-O[q!;U$,܉$%۵_5J LÚ{x>B b!SiA%z^ ~ rҦ~C]釤2ﴰ].yGhw~ryciJ˜\RmG`+~j5V$j2$o1~q /"H4Fnhj9i!Be wBib*RTr`n`[T`6EEڎYF~zדR2ؙaZ$J+tP8PsR[,U\ 1eVG۩Zt%T 8 2tc5Y `RFIr*omEcpg-Du҆.>pkk=Zu7ϤG┬'TrL@ZG5;8y TV@{ ;*aryhOᣆ0`>25\/sH6S;s0zh@Bߥs|raٳg1V gІwyymxH>×fXg#ey+䍫E(8¾ew47//>>>Ry)$/ V|ضmt( 3Fr[Dw,D5]-t}yBڵkFtu777aG|-=ރ= {z xaso!,!-D@*4"B X.B[n߉ V½UhD@@ \pܾ5" {Ј@@ `n}'j.ZAJEp7@+72״ׅ=!$$sSlP;2KǔxPSS#{>ǀľ2w/ vhZ=dx__?̐!C`c' )]?-{Olk|l8.W\aÄoRjqmQmme VתB>2yS(hXL~FWޖC)E eS]#txTGM9KPn{u#v_Up7SJ>n()YcCSmk+_Vx9MK4@-#1O嶖L M}{&E8_D}[盗h/#JhgYI@Dx+C0~̟?^CwC.M?M_e!O=4/9#Ӟ`_|G\b^NsoT ML$jĆJ# yI>p( E"|_:ERl8"hKr#Ci [L hq"E%шRY[-:*ȑ0PHc:UR4$}yG Qw,!@M"16x0CaV BpWsJ Y E}WQ˓̙8y3x&n.Wh13k[~)]m|P陖ƿ>Y,njg}V-Hޚ@+܆a~xIVOkϛr%nzg iCyr%J^ہLl'}B/"a3/T%e ĄXC{*-7/s%5K#bt$(m6/-,Hš525-P$>䈵Nh$`폇XMDG]G 4; DUyIym6 }m@i^)f>"ziQ*6r*C1$.;ϕPC.ŕj_Ldg [7AQSu"œ|<×'5MZX`>D(\VB 1%#+\ 7YH W#U)ӂԍXb#q #l /gǩ^~k 7|[n|E[#Эֹ`K_Z$݄hwVJw! Rm`7>+b}d:yO\s6nȓ8p+BML*ـPM"2" \*IR.5a>T>P{ƺr[s*@h2wPNu NO$$f?ε{1MtRh*0/]rMBR0do OC:Tɩ4`T`B<,ں[K1EASlo4VTQ.,,Ajf6NJyc̓@f.p6"7JC1ձt`_aH3#(L CWHuP/O@a: Rpp_l!au$߈#'|R*aҥ0a:D% `]Q[wy}KO"<zYU[} hER,S)>fd ;Vƭ"GD! Z%"fv7 8g^(=Gӄܤ:i<#P˃ '-tQLKĪ EmƲmFN 9 ]9lOCt m>GC1Sb!:m}szmm"ME'K~[X\ɤdvSGCRzT'|&$'}1^;(:?&H$ 02@K)v mX/QRj *NLG*6-BF((JNU@pIp gr\B}ޙwϾ!{ yk_ s`(xy;r.u%ZK})PA_} ܠpW7؜`Uݲ!xX Gϵ` WP'8tZ@ɜ>ERδ XQ#kX%?a_G٩zkPu`r 4r'Pw~"A씖f|\TKþKܪyGNK{h9~/ Oz)1RA ~)+p 4{sv?6 ;*)$wٿ诶/ۆ꥓Tr[}ؿ \?sm©b^b=}UIi4'GI7 ?1"U'KI/՚AouR΁s.h l ŋhK- Cm/r=ҟ|cB D|؊4<ו2{7<T87Wn FfQ;ș;U uP;Zғ|ԟ8mKm!ܲjDP{Č|l9x!zߔ;0nG) QI͞ +f9<˅;O *jjK5Cx>***pGlG0ap^EƷUx qj\7Y#1l# ѭZ:2Q)~uINYSP&0 f/IݨegG&[_E żn#\inm Z2г 1jK_KP<UYe66]{(E3;Ԯ8"rS6EBR|cCP<&OǴRkC;PVyTviv#f϶ێoO#wmzJ蚮t ut1m2-XU1^-oi Emy1Zf]4VH_W2p_b$wwgKYr䦵 /~!i?r/j5\i7W@'O^qT:t'NTO9_׎C1zxI5V4|z/<uf~ 2Ao 2|Ժȅ`&4w,Jt|^74̕td{_rU൵ o MTf|CMA_Wj|j+e9ٸآe5 XmQª)4*AJj̸VdzSx?X>Of\\w{pmenX&f JĥW{Nh؁*Kdywy!4X`}Ge,oa kY_Uԋ" yQ̚5j^Ms K b`3Fcp>m2 5,TH5 rѢEP3en݊7Z?2om:WY+fω$~ Ğ=%**eRxwz\zgWz4PZո5l0\jnߊ+{\Av; w0U0 vgn'I&PutիW3g \·W2~wI ܝ*#CY^ 곘E@5XH@ZZΟx@UOg1,VjVEuuuvUOg1@\˘e"7E#Ū:'W8p %S]]cɴZwZS+w0ܻƉn!oúᇢӮn;OYqIC=׫~CzCa_p%lA#uwH$p7v&Н]wHH w#mh  ᮷TG$`(S6 R Z?fi=U L]gHL)w2ʹI&3᮵G$`,Ζ1z 'Й]gwHX wcp  0uvH%p7z 'Й]gwHX wcp  0uvH%p7z 'Й]gwHX wcp  0uvH%p7z 'Й]gwHX wcp  0uvH%p7z 'Й]gwHX wcp  0uvH%p7z 'Й]gwHX wcp  0uvH%p7z 'Й]gwHX wcp  0uvH%p7z 'Й]gwHX wcp  0uvH%p7z 'Й]gwHX wcp  0uvH%p7z 'Й]gwHX wcp  0uvH%p7z 'Й]gwHX wcp  0uvH%p7z 'Й]gwHX wcp  0uvH%p7z 'Й]gwHX wcp  0uvH%p7z 'Й]gwHX wcp  0uvH%p7z 'Й]gwHX wcp  0uvH%p7z 'Й@b=,}oǣ^j# - r ,/RplIENDB`Features_TB_icon.png000066400000000000000000000142061300200146000361010ustar00rootroot00000000000000connectome-workbench-1.2.3+git41-gc4c6c90/src/Resources/GuiResources/HelpFiles/Features_ToolboxPNG  IHDRc7iCCPICC ProfileXPTAmfs9sX.9IVDT@H ( * ATQD0ܪ]Ulfz`)!`FG񸸺 +7*\!y6H/?Ȇ>~Q!dB5h` dNf8yH O&抟/`O!0B$}'֗H[E =Z?X s$BB~r8E|)_bI 0AQ$BqBs z %YC~?Nuá>V8 hb?o7N83?cb [oddM^`{Ao b`4.9%xLC}$xddϟW@ ?|^\m &96~7&2IZX"@\J@C`p @82@g@U Y02xW A V!9H҂ ! r!" @IQ( *ʠZj FYhM5FgKѭ[i2a0nPLӌƬ`v)h))()()r)(z(&))vtXa&=-aOq8Ng ƕ.p/p۔b19M})?SQQ QPQESPZF@-EmJGJ]NE=MFFƓ&f=->-6vvNΚ..nnM/DoHGN_O?Hggge80̰̈bf4e fb8DϤt"›I\|;!~Yٟ$s4 ;?K&EYVVCփWY!lت؆޳3g?qqq$rs4 ,|υ *[;[&]O / o o-.0#_EgX~B5nK$'*ł[BBBDž  '?E/S ++WOI %T%B%$$)%u%c%[$_H,ҤJ}vΗ!(Ci5Mݐ+7O& PHAR➒RReeo 9Fl1Ujj궚ZZ'uI K|ZE--oEm^mvK~?F]Q`Vݏz2zzz[j}pcL CzCG2#>#Qњqq $dΔ隙Yِ9yK 1HKi˧VVVWig666mQ6嶯dF:9:;8888y8wr6p.p^tvIvz n~}C#{yl|쑃Gɤ}9|'3p1c-sՏW@:1qRd~dgf>%{~N@DRnU*/4avӖ y 3 :3^PT]-)^,(V*PW,l\bGɊJ*jꝚGƵ]uBuE N gUΞodkjk mZTybq_T˲ X%]o|QG]t}NùO6cy[[._#xĉY.r,s, O/+(,}PTQ UVךyj(88ܷfV .ma/^nm1qY燮װ,=BzuoX H@Їܨؘ;w{8>biC9GVx@z"eR嫖#+V|x#'5u}I޴*u{]RA5\'?[ܦѢ%Uҏ12,SlHv50j{ܛ|.$x#‡EDIbf*R8-E۲d(-d7UTj8i*h1jmj]ѿhjxΨ޸ڤ̴yEe*A? |`9.|Q[1q4$$%%H)L<\s:hAc)Qlj'NZefg b~(zz L\BRJڠƢ֩Χ>!lnceS˹ͳ-/[?^عDwClrUkF={nG?4p|tuodztym&,'OLG>};(qž/FH|yl)UrQ{v>>~bY\g`eus%rd}!S ? @C'9!0@䵃*T,dLO @ X/@/R=e`|p9BP6V`0q5,V }&wtv 72"ч@: (T ݀c1)(*Xl".҅>1U/y>JZvr:.zzqN=)F:Sn3OdE6ePXZ.y[g̷*#&8+T l+ 2+ZJf%&)=qbY<-JA*.j&FƚZ:DXw8%,4-ll;88i;v-x Hz:{{w}Yl3FAB8B݌`$EĦǭ[$t$q%g>Y O3cޝy?@Rx4\b*FvW[ڴ/\~ՙ}Э|wf__@iu] J3NC KͯoD߭}e#GGV$*0D40 ^] /(* epmr _F 67GEGa1-XlK T{_h"hi#iR1޴ьq)Ooe6b~NI9-לw?H 9 Td%HvʗʒtY=,0GU]5YuQ !MQ-muS]W=Q kFsۦfVM}6Ol*NG[]ܕ<y{}!8L :$z#!?>1y Up]h1(#nʧsN8[FV]iH*Z o$]hۿ4ܞsŵKf^$ƆJGB nsٽ7?ysAmo-i-Z}!|bqdNٞٿB_ҟ"@8 A7^mt!(*ڡ)hS`Ͱ8 "1#(KT j mn0!) Uz,6 #^QzRQYQ SkP_imצ1,^ ?̂bi`5`]a;ŮȾȑéŹUȃ% qlr,^K7K:*(#& >/TVtVRQX zMn-im}7h|&֦f5+VkQK(rQM'!g7$r`K0HƨgFq EIR4p1\F ֓UY<ٵ9<5|MRg<( (߭,Ki:ϓtt:/YaRJ9r`a."6GWAyÁ92QASؐkr)ۍMTAHi$yy@V^*|_,="czHUxLc&$Lj:Y}q,r}Rl6k4nRײbf5ji)>%q\F#A}LܙV/nZARض>5&n`^w] QhX$ڶ ~>w]P(u!g@X.ŴaFTk'ĈNh4V5B( CV,+%ur9ciHl6cu}avCRQBp>B`/YDV"Q~?3p 'OUuaWҔ |O N_-=±q\IENDB`Features_Toolbox.html000066400000000000000000000072471300200146000364010ustar00rootroot00000000000000connectome-workbench-1.2.3+git41-gc4c6c90/src/Resources/GuiResources/HelpFiles/Features_Toolbox 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.


Fibers/000077500000000000000000000000001300200146000334275ustar00rootroot00000000000000connectome-workbench-1.2.3+git41-gc4c6c90/src/Resources/GuiResources/HelpFiles/Features_ToolboxFibers.html000066400000000000000000000016021300200146000355260ustar00rootroot00000000000000connectome-workbench-1.2.3+git41-gc4c6c90/src/Resources/GuiResources/HelpFiles/Features_Toolbox/Fibers Fibers Fibers
The Fibers Tab in the Features Toolbox contains options for display, selection, and samples of white matter fibers derived from diffusion imaging analysis. Currently, HCP has not released fiber data that can be viewed using options in this Tab. Stay tuned to the hcp-users@humanconnectome.org email list (sign up here) for future release information of this type of data.
Foci/000077500000000000000000000000001300200146000330755ustar00rootroot00000000000000connectome-workbench-1.2.3+git41-gc4c6c90/src/Resources/GuiResources/HelpFiles/Features_ToolboxFoci.html000066400000000000000000000056341300200146000346530ustar00rootroot00000000000000connectome-workbench-1.2.3+git41-gc4c6c90/src/Resources/GuiResources/HelpFiles/Features_Toolbox/Foci Foci Foci
The Foci Tab in the Features Toolbox contains options for display and selection of loaded and newly created foci on brain surfaces and in volumes.
  • Display Foci toggles foci display on and off. When Foci Mode is turned on, Display Foci is NOT turned on by default.
  • Group: identifies the Foci group for the Active Tab. Tabs assigned to the same Group will display the same foci with the same attributes.
  • Foci Attributes contains options for viewing foci.
    • Contralateral check box toggles on display of foci that are currently being displayed on one hemisphere to be displayed on the contralateral hemisphere (on top of that hemisphere's own displayed foci, if any).
    • Paste Onto Surface moves the foci from their coordinate space to the nearest point directly on the surface displayed.
    • Coloring sets the Class, Name or Standard Color as the foci color source.
    • Standard Color selects the color when Coloring is set to Standard Color.
    • Draw As sets the shape of the foci to Spheres or Squares.
    • Symbol Diameter sets the size of the foci symbols.


  • Foci Selection controls group and individual display selection for foci. If a higher level group is selected on or off, all foci below that level will be similarly selected on/off. The All On/Off buttons allow for quick toggling of all foci on or off.

Foci_Attributes.png000066400000000000000000001221221300200146000366710ustar00rootroot00000000000000connectome-workbench-1.2.3+git41-gc4c6c90/src/Resources/GuiResources/HelpFiles/Features_Toolbox/FociPNG  IHDRrkŠ iCCPICC ProfileHTSiǿ-U tBFHB G`Du)  EdPQ }v={Ϲyss}{;.K HeHg C1t@@ ]bg \ƇA;&3ᅥ, F8NC$l0 kd fay!"N33?Ƿf{C<&@@lv"29<> I,y/NKK#˜/b%Jxn-e RYkߑ*$O(rC!%Oyqfg9I1Ly)pBJ C yBP(AAP1TUAF4t Cah z }Q0`=x l~p8N3\Wµ _`1 D ECiLPv(wT*6PZT ՋQX4MG>4ހ.AWm0zCb1&&b*0SK(aXl 6[݇mva#I39p,\www7'5x/|,WS.DJ QQOt"%#;E'x)-}|VDm;rn  u::t vI{t{u?Emk{od@1p105k53L1gx66J26i /,__\xȄljml2lJ377m7}DgI쒝Kz|76K5;d\<߼ۢ%re+c+~{Tllm6-6c:q5Cvvv%vW1nvqr8𗣉cc㋥KK-qrb9t;ӝrh\j]2F=㹫knfnBSn׻wy<=<<<#<\ّ%"psvuȜeWWXcfۚ^?Ee^nӺn6oި`hw^&⦔M@ `dBBaVǭ~@o嶽۾qW-a\'l+)ݿcpΆ2ٲܲ]Ew}ªqe^;~JvnQVqg-{Va<ύBzmTJfQؑGn8brZ| {K/w;rRd)ꩢ6mMD{R#NSz gK>{~K5~!H/ we{]{_qr쮵_vƩ߬;gvf-[Kv}ǝwwF Z>$ǹ~7L={yTXq'*4ȫ?2:Z/Όyz+?eym_nLDO~[NV''|H0ҧv{D}y>~MMO XB֬@! '$0o&Jـ<,9=sBH$+@8gd&XZ"#֤bz q|jV}@ׇ9_>XĿiйҞy?|OPiTXtXML:com.adobe.xmp 474 370 1 x3@IDATx] \UTF'T %'@s q4SS"=#ˁ2W`rH["N "*V]]\\S' ]i㢾6X-:Vd$CMVs,+~ v|%gŇ{ʼn! uCx}JR^? 6ιXY,|g F|1ӛ~/*OJ)#>|Tyqh/R `׮ZPl3DU7:Si $n;4F ѴiShg>ڠ-,&Dxd_?3ׇCvWۤ88Iauɓ2dŋK'Wfͤ"MY4kI]I$:[ANi~DqmcwX.LEsBu)/BjlWys w 8|~^Bh?L w0xZ"ƶxo Ǐ |]hQ|mcq}ֿ7)suD/qƺJtB^SXXIXc؍w%^sث5~O(oO}odĆbQ?D۟]%7}dxcms6Z4&[qOc#ŹĈl~~t:7]:WqwZPiK wwjWԟCqlBwPiLp Žvڧ.OlDjyGtb˭/wN_ji#~hx ,yto(V3,r1;N:R5ߠA)]BVXYԩ]D-K_|P-|` ڏeW1Ţ֭vm3ۇ c_$#)Af9"<z|-Z x]>S`G1fQGcIl1´(v ߈֖C ]CVy9; MA f] _](F-Z@jj* {ɒ%hٲeyWƪU!$"Oi'zXXTStp|A@"ȧsDYMaCǖv]0{Sd_^~,nB/oMGmd\<&!#R?QȗVɆxfR>AEb0o)^74^?d|3dևvAJz"'wP/]G'z/\ϗwx/85Xh֒\+m۶+G^^:w,pRJv1|E\+JNL1O"01PE,ٽ }& Nj ,0kfRIg^Xך?wa\8}OQF J0BVM}l':) })=A g|׉}T;gB݆< e}薃r|=6)sf< &u5çc@\:2&>+4^5CVttt^ǏWZOŠ:${zC)ed߿lrW[4l QA`V)չ5?Uj]vE"""paJo}#T})U9sݓBQTiܸ$Uk۪dO[0@MF-U"WH( أCG.[}ӥLaF& PZ&40@u@kE<~p$JpF0jժUFx ^@)D(6PD~!իGGG4iDDW Vy#Pp.gEO֔sݻ[,4(8Z,"B \LF`a-~?UK3TbxiŸpMF\L24F`тU/ԃXkE ;тXi=g lh*,R ^Q!V6pzeN@/b'bvՏnc{ЇhU7SzE b?.>L0gAi%AZdY ;v&9V`{ lZD[Ϥ|tCUx_Qyعf7Zx W;CB+#९cCdyr{ć?n:&&WCW3E0|3Ա:@fy\Nv ǻ **6mޅ^>x 4؏}7^0c=BnG"C/faY^MCrZݒċl(,tZ]1c;[F- d5H'7nG`D2ɑHܸ`x@dV- /c$gG׋j(8`ƅ at["Kz NVh:;d#?7["Oņbz\jhZKI؊gbt["q`O\9 C["4x[xM/DVqȻ_~ .\P~H922o܈C#~R/D"y$fz )H>iVq.w%cv`] ("H<]]V7l%Ysgadw \>qEyUү"i+~)ƴ~D;${.Ep:$"}+D͖ϧ}6}4vDI ]_UT.ZoΘYޅ1kw~ܔMCЦ]=#&Ϙa;)M@ m1pYyHNyHeq¶+z̚+qAxufuq׃#^_o$b/{2Kjk"q?\a?7xvγ7ezblU1p֭?!ލ8O\ܿ]֣Ƶ5㡸;(14•]*8 {n (O8`hC_k| %nTyڈ^k6D'̗G=mPC}"Dx{dwIśo#szDWhR{ʚیh|ʻ xxE;Ѥ rQJ@vzi{ND)jE`eD#:zBnlO h#8B;97ң)= HAe߻me錷:k@bw1x]n7zNRA]t>m{ު)E隍kH9Xu@f m n"CfBZ7GW+dv<.H!ۣV"is [ch }I0 O=@)z̭ܤ6/ܾ}'Nl7..Sʑjd#Abb,XV +Ȁ"38|}Erș^+-JHMAl"6*.nWʼn W{[+I&/J|B}tԇ+IKFw*iO1thkX?ΑڀWw3]%[Xؽd~QtlӦZLtj*pP 1IA4C_ȲZAwB5Rl'0 fw5*}A~xQⳔȮ m9D}^#~qɦWW5j^щ3N!2t-*ݝXka#Ys5 P\yt5@ξkS'FN?.QTdɍVf"j404lt-n_l% }kaCcTVxE;QVQx6Su(ݺv+?I~p0O9B]aDG[egId0.z2ʼnx)/0:E)^~7 ? k )?$;`Clg֕6e=ͪ*V?(EDt)R&*2ԟРL:Û)8Ɇ9$<59/}`R?.c`w)Zn?%Uih gB!|_OKLΐѺEdR] ^w?uo ;[陒KH|a&]0TLwp *w.e:aUhmK0yF,vp\\ω;@n\Wyw\959Toi h~:#{i$A羽p#q%!Yʽ:uD#Yjvadf ~\#{g&xiG!>݃,v*`K1:PKi5 I۩n-`$诧)Ҩ(ǟ(9Ut8q>=_%/"aPGH,yx%6)!_?L`r%΍HMgxs@ Py9mm6K}/ :[WORaZ{G-°y[Ť\+tݩ]fTnqlEj!PsW腮AwaZD&q#!14{e, "=o 9ƽ9'W3h=vɬ*|#ca_>m5"n ˈ ]έG#vJܕ׸*g!~Q/IVy ²+{pyEp^\XxTM-n:j> 1!C<53 'x4ܼyGծFIńRr23ጭ`e]ZI贶bڗ"'}9"3ױ"UKQ9—Co _V!G&VDӃRWzEA 󔡠jW?|y Cj,t:Oh%= hi:07VM4!_q˨{m977M-cFo(u iC^HfN>ͭh89vHV73E8sFգ5d/:qSI* ԅǎtTBj}[-}]:]ϾfZJԕUY͞iftr*%j[#ҧ ey"-M(M\+[WY4i,ޫUZ (pQRX),IS_N^L{W;>RyeH{ܧNFt}H~ڃ&'iĖZWdVD.ݿs6mQ2#ƬƑ8w:nޤu:bKhG򱂣Um%na"<֬Qp荱P\ U;X4VUx0#PN,8Zp*x)fNۉyt?UتVc]g0@ G|F0| #P`"'7` &rCn#pk gG[05&~pF`"7>0@ G+B|A?DJJ =zTuYfx/]h Xb&0h85yeFlF䢘4i~ s1%QŔÆ |§A e5_paA0 ]§ŔKňL1BvɁɚ&I3 h66oQ'Sނ_52"H_ TFKd/ _3À$O̸Z&b1  nq9Y8 6mzkERd}Uj^60m a#&999e+("K$KE'+Yʭ7~|4+k*ƹkEP{.߿/Sʪ7iDX,1#@-r1VV$K1#N?gX#0n0K1#"Dv W``" 'bFE#0!DN,0ss5\1F`tC\7X`J6+0!"TjWEi I*F`#ҷj4j̱#0J*F`~a%3##+aFCÞKfF@/0FV0߇߇=0^`" `&{.` DY #0L\2#0zA\/0F`>>dF`^`d%#}TzѬ+ɓ'GVV=z<8C~USS]6ajj Ҍ#P*5߽{W"!;;;X[[ '@BBB鉥Ζ$Kr"P\X2|饗 k"P;2rM@ zR.`ҩhܸ1\0u pժll޼N$Gc%V-gA:}Bɧc!`Lֳ(X*[}*NdT[n.-#bg B>sx :&{f׾`@3VZrrrШQjʊ l'-X [V$,skTU+e3>Xhp4 R,~"[Uũ%!:.((PE`*, @#NJy0eR?tU̮c\ANu+p% ާ؏]}V䉡X̸2Q9PR,* [EުZn Uụ̃-#TJqxGӶ}Q,jby>&zO+ .~z9Ⱥ(11'W;Si 9SB _2!HAXeɉ9,=0AMN ԗ/PrNcbLƋԦl3Z<wmc)' ].UXʼSq0.E)8yvmlr)U-ZjuE&MAb+5ı.P55g@CD.tPnAxո\}GBD^9s>~oM[>yg܈k3HSNy>%JIug ?[e"dd EަMn.pp6<ǖk1. [:XD܆O@ Gg|ւq>!=J+NuEl (q8d;})KeVw-kվ MÇHLLXeT*"gj &_OXV0VAun-[%5k&^Vx,5vwkhv`^6\4c95,{Θ9ո\)]QybQ6_ĊS`r^Qݩ LGO-_A@٦BT!G[_^1mtÇ;PZ_8$p&Q .n~շŬ;wHfffF:lX# 4۽<$ Ge:[WoWc[eUb܆L0Qe&RJ ˄J4VZ:`LUL=R[x2-;#4) Iwp!P[4-Ԡ$6ei (,$W 4:YYj#GV W8#0Jʷ. DZ믿 I[ħ͛D t#*MFRMq3ț5F~¯2_L?K@(Hj%exRBDFú,Nb.*՘I ;ص oUKSn!_mBFVIM4aRC~B]X:֦8 ^nW(#-Q{3Ѵ=O\(W'r/VJFȩ>F<&r31)b0@ƽ8szRj -tp BL1}H_`'^"WP@S&p`Bf`)">=mވÔЧ]6q9~!HLOGDm_SC{chH[1_.3љ$|IGVb!Wp`BP Em#8(,z'_#h3 4QmdTe5 \/WjZF!2x+p#E@x"T9b}HZ:069D,D,c]Fs(栣N3J4>m4wBڥsĞ= ]pkHڌں8|>F-sHmA7FcSS썭4dPخ1\<UFuMIchv_0-Ɂhi@=ѿm3ă|[S4!U Z%Šɽ" /_uYbbb$7*^Ue@GV\\ܓ:u7W^EϞ=+hyVe!V!?52r/ rӌbQY4,Ju >Jϲ#l(2]__ Jǝ125V!?5#n&-(OZ/&skXƁX81ans>g( pT`kk +-ZH$$`Q Y񆭽*#P F Ќ~ DZ퍵 !{DX?h,W: YZ[ؕM 4ĝ%`ն\mQXX$-\L9{%HAE#,耀^\"Ra5 :zC s>A,A k\le.,qA"8#P5$ۻü " flm^Vv+ĬIΪcAXEB-re*@gT V-z"nZ&žتpg@#rửGm< BVݨoE[eZ!#PnjpbHUr# OeqOBX\F#L^xWq(ba ŪΈ#GL,--%"TY] ]2r<#G*xyF0tj;0&L䚈1#0uW`M5cF` &r0.#00k"nj#La\]F`4`"DF00 ø#h"D3#``0XquFD\>fF`"72#&"|0!Dn`eF@&rMD`C:0&L䚈1#0uW`M5cF` &r0.#00k"nj#La\]F`4`"DF00 ø#h"D3#``0XquFD\>fF`"72#&"|0!Dn`eF@#͈?y£GǏWT]WV-#pԮ]&&&[.,--all25  Td24lM4I8V-ZP)==oFNNt> b0%ЛkEX₳,Eq #P>$Y0gggKb88͛7880vF^ 2@QF^%TpVFZ#7"/(( (Y!̘0Ŗ=HERxR W܍= ^ 5yi̛O/OWMKGƅLNCZ:c;\8D1<,0;d$$f Z;YS5;_US:keoFζGΜYAHfzv D!g_CמθX]uVމ3ncFtKy%s=9¼]1|zm؃س <`nt.GJ7Ry=Y4z~U0@F:=; nyM*l) *euїQEܶ I{B$Y V+)C&\-RZFyr;"%U㫮KNbЫ_[?e -̌KTN&+Fua0m&ԋꠒ[FDyaܹx#,\^1 ?6 Y>LCGw`-fA> VV;୫1ضڴj3R2Dz)qk;; }sX=ZA+hzfP=l|}(=Bؑ=ğBϟ4SMbɒxAɪSS\Ø\/R.n17ٝ"VhnE.-a$6:\Zcp AQsd SC-*7ag:ւX|0! B ' .*&aq_fǙڷ[,QN(gL75#tZohyxX6{Csvt;u Ā"4&}3֮%Ãss65YFjA\v&~̥q\ +f㻢QBJud}Ly1t?E!VmSx Vbt4k3>?Ѣ:$x+'y=KXoѱ -r j}9<u˸yOn;.A+8[Fl'?3[L:#qhDX*UF<3"G``NRB3+oFm0ȵZBanXFaڶ$'-} >LE?U\E&r@[𽴝r|׊8k̦Dy1P =qp(Y7Aq3bqz/rLB*238B1BǐFpW/Ɔ8`:vFz0 _K,D~<lo x}yHAd&B>_Ev,|삩>dec?vH@ލBm|rh>D=d 60 R twS3emyOPދW­'׸& ),u/@*ŸwoFΧy'I-6@IDATdGOQ(>yY\AXXӾFM0s_Y9 @ndAJgAd6׼~I}=cw[Ut;g&8vu|:(.|>gw KmWJ7> ǒ2Zb:L/&OwcN$jxVJR:3j"R Nanc@qq%)DsbzZ(`tBXX8q%/%{&WxlZMc͈B#8/d#Sd('GcPU3$%!IۧB$=pɍ,`^VJ[zfV->{6FDƦb\xZE;WBtOl #KB ј$%E&_|zXMH$O,\AoP&zҐusMqHz i*[3quvp+rߺ- !PG8 C{0{'nݽK0ə|?+ׅZ7L_5^]~j2=`v6IǸ =1yz}{nJ64 G/p wrPގ7ҭĜǤAˀoŠ# BiQ#]Lm53sdz¹w+q`y dQ:8TeqF02i&'0s9Te]Ģ?.qCŞiP]7ǽ~2}=WY]af䌃kڢ ]qRMvRi > 'K'/4n(%g!~Q/SH0k&CynRF.s=S{6 '="^Ls;Ҁ11Vl jj /FFF!)r;<{OGE-=}4e HRg 0DV\\ܓ:uWgOOkЦx@mSS "jrY6r=cAib:0:uLaV"lYPtmdf&-Y$6Ce䓨 Lu\=YFƤ*eĩSXZ235oEx֊&+J{6"ү_HQmde&oJ=#"b^qtC1O{4Ҵ(t>w痪be#7#BxUn%880vFMe*@BBB9`(2ވ~'gg@φwA$-&:%/#9wgZn-}-11ׯ_ovjǜcu@@|Ӝc |wꐍEވ\<jJVn4#$ZIq[Fx`"z0@Ћk%77Wb^^\0bx'ɘ*V*S+MéuJ օ1#x151c""Wڵ"IW{ dF-xL!JF0d >3PiV80ܥ3CC50@uC(`jL5˹#P`"n=ayrn0#T7ȫ[r{F!D^㺜0 &֣Fq0׸.3@uC(`jL5˹#P`"n=ayrn0#T7ȫ[r{F!D^㺜0 &֣Fq0׸.3@uC(`jL5˹#P`"n=ayrn0#T7ȫ[r{F!D^㺜0 &֣Fqbsss_ݻde6 M4AajjUA݌҇yzcSBLyn9<ⴔ!~mLtj%FyA <%%uE޽aeeZjiɓ'̔H_iժU ~J 5,:/Y#+ hmV"QqQGBr oŶCN(Ln?0ǩUA1\X;0H"BNРARI\%q9UTVEUixt}+~"r+j(SAFư"Q+)C*˟Hx421.>0?~X'H k]ȣd"}ą IFȎ6ɰ~ B\J!_ocLEd_D=dt[BBg01ْlcb~Q?o)k7b(sPzϡ8BKNCpT[ğLe]'/XK S/2gJ$ >MvVG0ߌAy寶361@*Ƹ'E8zaAkpm\Dx`oİ3_ae])uV쒷¯1׵bc68'aՋP?*x(K}$3]"g_v hzŘ^"\FksLq#6#I|e?SkG#"`|e-O~Hi1DZ:_pƮd<~-ud:, D v*rlЫW'Ja]i@PX}]=0ow^i;v!I$ZWPkRr]}u\n ܌<^5a,">~Јg:Dn>eA@Z6AnO%7m1HfEPYk"吼i L%,|LFQzj98y<ޚEeJ!'ϧ(Bdw1\P ($ٷRH=z01A!P~A5ڴBgݡ T|O.wxd?x b@Y2dN6U8DD-*)FNrfh*4Gq}oZ(4Bl#|v7HDGVUeMF `b3 -) Z5.d#_H!K/Sp$wA2Ӑ 8jx |^ 9a,9s֟䳷AEtwcClx}[S"碡G-\KMʒD<րY# 5Ʉ(|=ۖLEr7 nj4/ۢ+VG/}1GAas؊)M! :=H>$ |\8"\ƣ>IN!}ߥ?Vych g1VJmgb,ahGw]?|JW3@'u(^=txF_0K~dE*f[77 ąÁ`ꈀA5ӥ&u<F$ rnڴmbF<Y+偊eFx>`">k00  2#|"D| ׊`0ȇn__ĉOTIr/G+j547Ԩ,nl ;Ż´*}c~&?]K0 ΄?;~-''G0}*yM;s^0v#7+{rG|z8STc3s LO`}6c2'K=>}Gk5S{XV` QxQQcswC}s\[wxx>7;a?So:]u<G0u.\TMŒ~Td1?(H<$4.=%?{ 86G6dnE? j i5>=2hVHF$LcF #קTZ媿Td~cƩh[gb}g}g9{YX5q!O jev帰ߟz+uU!&8K+6!?6ɤ3I.7.UʑL_nbqs6,#o?ZV?9XUvyˆMhn I@C \g۞ueȖHJF \=^oHa]g8KߞgqUX9wǢyaҺ.`Ѫkiu Zb.X_ɱCŧӱ [FwFP0}3tCZPo10fWX9gOcQWghD`/wbLåx<+w dș_P>{n&}ڻꟳ{o+< ?w7>h&|4 G7W1(zzaڸu?R : XT׹VTBDJ@b㵨)bb<ʡ5R|x*k Á+bbL/5/EҊGѠ" &h {ff\3^{w_9)ݿrv@5m۝v^RazxS523LvYkՋ]ye 懢~rƏ@qjO~&WMJٳ#X46i<7;~q"q(,Bxݩ.ͣIw$K"BK6qdUbXs. =Fbeb]B]%hŨI3ܿCM羛 0q[s)S11[OkkPvD(!R{xjMy9Tyh_x7Mi<{c wA4tLGYqxeϩT .v@2eӽnܦvmsZ n%uL0M)Es i\L 컷;xȉ@ !7€#!?JʊRĦ"Z7=-v:Q֎ءn:6t?2T:t lzb%K!ҵȣ׾O#MV/B2N$ 6!"sW)>qL;x)x@u Z,!iqӢYڜ"0ĔxTŷ c ej㢰ը(Fn͡8hFNt4P -%ߺ{717[ ="5cn-E]3,?lUn|F]w-u.h1n5J){ w7нYEa|}Q&MWg@ep_{x!޵'s6O6c3x:Xp &&Wtxl]1GqD;Ӏ{c'-F[hd7a\\khʶyeȡ0Bqӿ-鋰$eULC:(!9mJEoVN Xn/Oijq*Rȹ._.w녀?,&إO4R ! l] m$MB B@!?)-D{H# B=~RZ!p .! G@{B;5i@ |7hllDM{wMܺu '1o+_9w fw6e$؟ag+oJ-~7$5 NũY&Ǚ0{6-؇}IIg&ۦ ̄ . B i.̌یsЯ)+&}ސިi?}ӑ $+_ y)8!#CCה y]WٗLqOc>UZ,;$ֈ-իW"___(q\$ ̲Y6T݆{:j>Vy<ࣔha/14W%=M@vyÀJu[ ܢs%u`{DeF]?ġ^^B  kFN+$|1{X*̡xϡ9L#!飈HVv mjȚKJJ_6W_}>Edz{EB إ>6,?[X< VF͵MIrQnf%ΐ{[X6[2ߑK 0/PftrN@Pn!l$AI .k^n7~fo&=tr!>ѸK:iZt^=v).3)ر|]lcZdx/^;ywRu47zH[C5**katq-|"V_>sätsd^)J;NㆇAU׮ }*}Y[U ׁk{Ǖ+tK* 8sL4n///i7n(b>dE˕;X_Am%s~{ -zjGj\%LptK":@+.5';?ӗ#v@]4&7v\}9{w-aR6YY>}A"<4.#vQZ`>r,O=B^žMk~$:]4N&!d&z>"bK7 ohG,m5vi6cDUZ"8|6+VG_RiaW-qX|[P8eD$dD}RC!d$;&3/-?l*j:{E{vR% 8b]r<%ǩ#sH>3: (kB^P*M⑵\y3G544(Cpjm3)<ؽRXXt:MϮe+ws;ϲ%Q|驌^N2!捅\uMm"694G[" >r[ iB DȻM! l-E!"]&ED@ܖzC"k ФH ͛uV~Er*H~(X#w2uDi"g%9@ERy{Ĉ4H&F-reƢo[!oD"zYO>dOV+u;j*?n4Edeȍ؛9 ?1 JZLe%e7 -) %y:WUҽYO۫xEH]0Gm\u-Q~ YKgNgSvTY+V`E*ecڏ]/ҥ`̈́"Kt5<-,ON܀c0?9qL_Ks|+Ue$F^NmEv>ߏy9ysEABD#c7.k;'F"G ;Yi]ǃ>xbo`wJ RvFYYx:0oj Sdv~η(fk;\߁5P(kXs{<ڶ[צɺӵRQH*VGh<<7[ "")d+osk=ߤsS(m'7`yw?4ύ4)D,xn>lwPOo;kw&%J9l3_T_#~!ֽL>{ҍW*xC3]tgy,>>>vr[kZF]jdz9"GߧbDb?5 %zDMIØb݆pGif7LR#m9}N*ժ 8lvo,@SjrCIbSAC$-H^pJ Jdrǧ⃽4n`()@6b֕M"{t Y+fw'6wz,۩ZSKGu wȪ L3^;&+4kf\UPҦU8r`\g&O2XV})"|AŜ˫e uMхlLMx$| 6 Į,GG12r9–e"0w>g1/O %#1@R#b8!qWs+#rD'F%w*>uvl ũO(ELuɿkXL Ugq$}6둺;kY:PH|d<g!m[[tT~_mW*v*H.ǨRW%!iF,~ph(ǖM[x+ QjC#LmG &wūk+`,H(ÒBj1"bl*twQ49 sXGsIā 9~45OxSmK"Lm[I##u8x3Rw)N/YsW&4(**RL*;_whbRڇ( ښFpX beDzǾ9=}~dEOk܎Ֆ,3___(P\g]hOڧQa|E4"4sn0|&M>Ӫ0fܶU?J|tE4 S#:lNQMɴbb:CY5y߻4k +Fy-+E3?, mamCow֑1\N| &qvKxé#obo3:H:5V}NnfDSڢ,%Eզ_D_Z!ga풒89R>h"*} }c5ݑ!$a 5[$bqMl/.&Qeo;4p)e h`M:?*wGm_֔ՊrD)Efgc@_#f?eq n#f!1CeUr7t6R WYx"J}l)j<\u"TwPD|_xrDNiAP@0Ws(|Pn䋄nL7,& 1>Q{VO7KP+Z ];3 s'Mnt "$ D._if_cr`@Q$dOV+u;j*?nE"d] ynj*hak?MA76E30>p{S&_c}|SB,}e L yy$*UեB%׮kxr '`;gd 8y>%'#'`N^j_ ؽܱ]쫾>Z?LG6Lsҽ i^,Y"T(ݛyTcpjv:ZGǛ_j.FvҜ%e8W?j<= L2 W]:ǡ@lU(.l_YKVB}NDZemFtiثdzM ԗ}EVw"'x^ε酥;)UEH_4)mڏ%9XV})"|AŜzvrK A7"?WD$Ο;iJ-y8x0kec,T?(nA1? ;]IݶXi8l2Ǝʽ~\'Ӑ?X=[ '% (!0n |_#xń4{ ]bsha]v%?1N kEoS0o^ߐ}}`L@c8/gbҝԛFJ E(,SVsb!ηjɖʔ%o2o·jֵҶ2 ڜl+]2^vYM[^ųqNFH ohu~gH0{wg͕תO#}{`fRSũU:߀)ڛ3 pOi~(d6v&"g1flI455v?cr0i1|i%K0hYIfZ Yg Ů!S0<wɾ9[,z J.}xil{o9;(?GQ9eJwYDg-!{kkkwѳ555 .qzci~j9~f>4CMsGA4#C[CI^$.#Q?!W-pn 8ݘ7Wqt鈱|H}K}F)tolATz&0goF;v*,U_+G/VS(9느r v1krPӎ>}EQxSJos>^ y&aLxKIIG"GI)4yAvaak>>oqdt/M풒|MoꫯgxvH>>\@J#t`/jOl58Xt|W}YS ,[>N+D G6MG~~>-XF'9[^/B2\<{{\]5M&ʕ78 GNn\ImL؇~CAnQ)f+kyvk'l9n6&)M%n45%t ]v4>'N> O8·Vh=pzCuQFW59nr \K |7P1j޲b'JIE*iCX3);>)P,ϑXHk }߱G0sQ[=rA3ߣb7<<q {܈kW <::IDATat /kpuJ tr--3Ӳ/+UCi>ݨQE 6)?`-ȟ_v^hRiYG9su,^^t-7nPĜ'扖˕;X/狠n_xxW.tK]ڟ~ԖJnpÐgew@! ںe|ӍWnO!47Zf?%EG<gf[C+H8ۢh}]0#%@0K΂zs$W`! 3@h<28;D{Ԩ!kzdm 5GJNRm󪪪&*8U[]+3[J+ [~ DPZk5y];w\:2{i>R;,Yv,b߾}9JB7 wt6?]PP3K]Gq/jhhP.ڥ6hX8Zsxw/{^(f04j;hE+oY[ ' B}RC4Sy?$90=4o,+Ɓͽg3ν#7 =#{lf! =! B~O0Nwc+5 !  {Yv"{D[Y!pO̲! # =R@QQfayotHMr!Ae"wTL?anJѨ&M;)XwwwebMtvHׯ_I0|&76Ūqbg)dm@BHٙ #4)O=:3Ag(/ȠLʾWy~|[Z/W/ʴr|d5;kŀc0D7.kxiϝUybmيk+/>Ƃ˭ ޮIOɏ>q#{R9d$VRDGxV wigEZ7GTP>v܉#l}f%O! [r|lHAn6: gK:cy\p煵}.DawwMߢxŋ$uE=c۫nڨh3Ē]H(cVB脩9s&BDjl!Vm1c R8 :C>wxu>6 00@+i􊣹3'Edlۖpz8ɴ42NJjJKvWeq2z4NkѶG@82.(6+_/wT~tyOP$d!FԜE]YlH7J+KS~imKZ /4m3X$ wm rA&^wHOX6kH[հg ((HY޽[MRq~ B3S]\G֥BGQR^%ؘ~C1n~73:ؘ^C|YBHGu-۷cMŪZS ;bKlY3#<OMřu/<-ɾHA3]`TL?f>qQgkSXXM%9HE9[5=;R#"r#e2ڎ{,''Ni;N@J<DҌဋ?^̥9tE7E(ݾ׫dd}te-UuݫW(\Qpa*Z +MiV !5mO^wkN{ p֬Y_~6lPoJJ%!`B|'š ё2n Fh:@)9kH3Pƒʿa# v- Uv6Q?Vd&ǎOc^l1akQ2&cU$IG-oM~~Y~4V*򫦡c(cT?lٴE O lhd(Ѕ$=c!,POM[U/cl` cVb sRG;1{atW=; MQµYfX[;iKECe_ogS$p$6Uɕ[MΣ hICb-uҥo[N>tϚO)3y (D=By wKa0pS!I|Ѣg}+PYk7ݭ0ΊjGx} &/BgF5]x=- +9tZö_UͳHȟVEwؠnWᝥ1Osǔ!.{нB?ıxt(w7gBwF͎,~o:j>YԼMPzO~iW[ ϓK?s FX޽{S͑z:AK:զعϒX9pb ܣ},}!!UWp47\#! (w;ظq 8nW!wXyviO=dZ_ÇGbb"/K"~Z&{G+K.] lsG|+rڴi[III~Tg9IES +vʦ]jcXs>/At]~$5ꫯ:,%! %#z ͖Uo|_ qt9,PzΝCuu5'^I+vEH쑟ST"B|O}[sZ狝W^EYY2=ܔQ88K*q"kf&"-Bm]hsu4H!`rxIn! !. ! # Bn/-9"6% ! uDȭ%6G@D$u$B\H!B@riB:"B@# Bns]" B@XG@:^[!`sDmKAB@[Kr ! lu4H!`rxIn! !. ! # Bn/-9"6% ! uDȭ%6G@D$u$Bt[{Ku欔 ! !.{հn -|7 h! }`c=V(vA_Y89p/ w}UJ߾}y7`JQ! 9v[zvv[yj|7Iq! Mk&F! w.c!w~+pb"NbA@1QB ;qB@8rGB'& Bĝ/ !D ! w.c!w~+pb"NbA@1QB ;qB@8rGB'& Bĝ/ !D ! w.c!w~+pb"NbA@1QB ;qB@8rGB'& Bĝ/ !D ! w.c!w~+pb"NbA@1QB ;qB@8rGB'& Bĝ/ !D ! w.c!w~+pb"NbA@1QB ;qB@8rGB'& Bĝ/ !D ! w.c!w~+pb"NbA@1QB ;qB@8rGB'& Bĝ/ !D ! w.c!w~+pb"NbA@1QB ;qB@8rGB'& Bĝ/ !D ! w.c!w~+pb"NbA@1QB ;qB@8rGB'& Bĝ/ !D ! w.c!w~+pb"NbA@1QB ;qB@8rGB'& Bĝ/ !D ! w.c!w~+pb"NbA@1QB ;qB@8rGB'& Bĝ/ !D ! w.c!w~+pb"NbA@1QB ӓۭra5IENDB`Foci_Selection.png000066400000000000000000001215401300200146000364730ustar00rootroot00000000000000connectome-workbench-1.2.3+git41-gc4c6c90/src/Resources/GuiResources/HelpFiles/Features_Toolbox/FociPNG  IHDRt iCCPICC ProfileHTSiǿ-U tBFHB G`Du)  EdPQ }v={Ϲyss}{;.K HeHg C1t@@ ]bg \ƇA;&3ᅥ, F8NC$l0 kd fay!"N33?Ƿf{C<&@@lv"29<> I,y/NKK#˜/b%Jxn-e RYkߑ*$O(rC!%Oyqfg9I1Ly)pBJ C yBP(AAP1TUAF4t Cah z }Q0`=x l~p8N3\Wµ _`1 D ECiLPv(wT*6PZT ՋQX4MG>4ހ.AWm0zCb1&&b*0SK(aXl 6[݇mva#I39p,\www7'5x/|,WS.DJ QQOt"%#;E'x)-}|VDm;rn  u::t vI{t{u?Emk{od@1p105k53L1gx66J26i /,__\xȄljml2lJ377m7}DgI쒝Kz|76K5;d\<߼ۢ%re+c+~{Tllm6-6c:q5Cvvv%vW1nvqr8𗣉cc㋥KK-qrb9t;ӝrh\j]2F=㹫knfnBSn׻wy<=<<<#<\ّ%"psvuȜeWWXcfۚ^?Ee^nӺn6oި`hw^&⦔M@ `dBBaVǭ~@o嶽۾qW-a\'l+)ݿcpΆ2ٲܲ]Ew}ªqe^;~JvnQVqg-{Va<ύBzmTJfQؑGn8brZ| {K/w;rRd)ꩢ6mMD{R#NSz gK>{~K5~!H/ we{]{_qr쮵_vƩ߬;gvf-[Kv}ǝwwF Z>$ǹ~7L={yTXq'*4ȫ?2:Z/Όyz+?eym_nLDO~[NV''|H0ҧv{D}y>~MMO XB֬@! '$0o&Jـ<,9=sBH$+@8gd&XZ"#֤bz q|jV}@ׇ9_>XĿiйҞy?|OPiTXtXML:com.adobe.xmp 478 372 1 #@IDATx} @UUA@ BC Q\R)VXߥqe&Ś2IgJrri&i\&F\*)I)p\(uXRrCP{wx"}w=9ޫyf)80#4z:+ 0#L|0#`%KKb%}j0#`Bq ##C} D~еk{r0 *goIP_{II z!<3R,-&urF`lեK 7`ji_H^XX(O4 O?tYZ^qbGN':Jz1Z)m$e.s7mE?Wu^yrŖ*ԿLDV\0\Ο?o]\\⧟~B}Qݻwܑv;w6sEy͛77֭[5g0GZ:]e|}h"ȀHکQXګa/apgz!pF٭xVyS҄bɣ8eCa>Wڰƌ)*ǏKd>rHǂ !-b[ĉ4A"(6ypp0,YRnGMUrYā~M{2бc{˿ s> >=_Oǂ'ŮS7P5~ IŅ]t )  hNL1\ȧe])} ojwtx-&<5X2\iC5WH][ChӦ ;%+t0U(}?ʣAxo:NX'X6B7.Ca7Bϟ/d^^^ R&򈼢9X$~ "/ւŕAuu"s~ކUkP.YmCGu>zÈwakŅz }B {}7NL/aB<6_3\ %K&]F~[Gy:~5/, {ObRL?, +uBV^ꄕp;pz?ҾXgdjDR*qx;@Xl=|ՌU""A:vҙ4UVaxg.n /_&MI+ʈT0'HWʶm6if wQXg.%K֭+p4iFsb~]rq@{;\Yq%|uɇ;tL۳YgS;N-jݥ풻5|w6]Y_s'`R{oǽ~d:' /$j{cz?tmJwC h{ #p?Mi x-v &XQi!$Vl.fKd1W]J+sskϿ^G,vrPո=8B)"^"R9:@~SϠW.v9QA!t1X8w~xߝzĵ =Fc+{9":H~|eZ7߮GRo\.:uIGqq1$2n^xA6iHX1r1g@A":@R<)#X9Dd5bZm`ڱ& 7w̚LE6q">x܃׶ !/9"BD L; mó9Nc:HqOoH9#72aޑZ,ݑ0o__K*P_V^qhho|QX?ńw!eY|?LoY1sINzz:>Sϗn8p@oF6lD%#wСIxYCW2Bc\yJ9^0w杻h.MpM3Фw훠 mjo&|f.$Lwn^rghQMMmܼUm̨*9u":{DB۷4tRL)A(OM6ň#0p@)܏噫F`*mq.|?#nܸ!ݬT@Rfj "WI7VXJ:8`F@F@E/2X (HMM?\*Jl ߎMzB[դN0!JNND05 `(w\E,bY,֠<`{|KoJ%7Yg -m\pkks }TxSU i"A۷ѬY3u7}1%SsP[%q)z uyd=}U**k1R&ѣG?@l1|u .^h(8Z|PB3YDF 5"1<qcvJm8K" l V^mn\G9BϻwnیU! BTLū>xkwеDn}CCU˥!ku"% ]EEC9]C]{ג\jhIfn-MԜĻ\j⫏?Ea}z(5/fey"0g z2(u;%l~ D^)HؾoQYb%wwBzD 3 >x4Cgaxlq.3BOztv*M%?8RpjUOV*ZkPmq&)/#~b/G1dMVfLً7Y7հ {:qjgŠ6~ړnv?/(]ʡ 0zP?|3 _x\d#nR!h'Ḁ$Ľi %%7ppw ۱UT6U{ХO4WGnzN ę-%H0n94Tg\8o̰:T[HN [Dj2.n[^h+)@*w, &^H3kA%I8Ugz)M\(6a1F]{zeY Գli_+8kl2 )7d2hLj93_ LQGC.^u=!JfZk4] g@֩ 8r~mKSA-[&\),1EX^~[;iQh>8UX(պe9UR+^}B|&|(@]N6q,7CSɘԀNc8 QNPٲ.W;[R_p||=Yogow!.!0)řX#={8 !&v1c0 -ҒczKnjGhOom 4Q+7d&T:[$)n򥍴2yOCr7+b7ysĄԭb`hy$ݎC=ؔBg!bx?Q=,D EoEeM=eJ^a^5XJjCfJ'}q>^eQ1v&>M|(T6 _3W].!21D:2q î\<>~]I׆B] Q;Wn^Gv,+/C:⋟w{;hoՖ}E_kqN;Nz2b~(}N/dm*Qɇ. F,N_紇$u+ oFFKdL -N_U|v{/3tnt4=d}\QGx~0xާ ~hicMrrhlG>[}7>`t^M~$Pq9!#}ٲUQ> "`[[vtH[9d?L>ósc|B aZzc_3_?\J!??Bic`ԎHl#2w> MFH i&< 綏b԰ >F#FD(nZb}v}[]1yL .8/A?Ùnx~,̜< .W`RY "2o;p dUbd]MUq!r'Ϛ#qA/ ڪer>Fd-Rpql3 #+)};w/Bowj)Ca<݁s;} |t ;|ݨö)(o2_HGJ^9v⭊S\Շl'z'?PAN y}B1{Z(ZR^O#!9 iU0Nbto"z)u~JJVWgYЬ@3=D́::W2ICd).^E4Zbb ^^+Cq1J~jg!7*~Ņi<j2H ']w[` <\ZvoYpnh(G~kb?];W&FzOVAҁd=H_/a[B22<19|: rG!?;w~|r t@|肝%~)/8%BWoPA&F=s~XJk=^@g)>m;t+g- 2tQVU)K ڒ+Է-Z9NlڎƞE(97YHZ<D' o_cΠU6åv7;cfOp2E|MyT-&ʼnWW~Tdv@Ēeً|u8J$MgJ&􀧇rbhD6˧ i?OKhgt쁄J#6@wGp /YL2U2nfzQ\Co>xDY:[`G'RoZΒ+GwJiK Djz:[<;IZ&}S)]/k7?r~1({y \(y lF<6t4\RH_ȵ+)㛘xDmJ }$A_ʕ|STDvwF-kɲjE%cYsί ;"&бrKDe>JtumW=q:>~Utt5 " ez)n`ǚnS0δ9Jz9:q:Ю8eo Z7t6̞/Ag!6witTC+Ga@ɢ7Dh &!hsl"}!YژRr;[KatnrTÞ͛K :rVEJbsFiO 5"7cXAh0XAnt߾eIeusߥQ'iZ^ބ$?p_<3y0wJ(":S._)NR&s4Jf}Sb@'7\2@Vƕb&SbIјر-4 Gj0Ef LR/ԟ^poV$ M;k1HrUԴ Q[%cwT "t3ǝQiiS+@kORcG:n+g?=25|uT!~H{Lh^Wf ]jcVܯbg毎{L<߇--z'dy+0gӾc3w)t/X6/;؆Q@^ X߉\&SZt0{h؍e\lNXi".Bz 2HpT KnKpL\ on '> MqdO ٫P,egQei?~-ӓDϣKDLЬ s n̓I\%XĤ?Ρ;Txم&GskSYq=/exFkm:[1q"_+F' YqƐE %k`rY1z(̴Qࡽ1wc2rT&ŖNeB-]Ъ+ S1QHK/={59~3_Fnv 11q\|'D¿cȣ/Qp.jkCZw}&wGU|NU!l=&ᙾcD^p(ƵHHEiUv CѡZe3T1M]fHjv6yѵ|n?T|)Ji@WǷq(:}YץdDSmɴ:/=qy0b;9yH|vG1wTݵ< ,%9'ދCɕ:O'I+1cv\wd؃C1|;AT͔BvKEM=qdpQקB=Oěæcd^a()A⣑3,r|=\Nr N;Rkڏ-Զ9W8%>@M˲jʟ~hF&t$e5^?+7vS|I)as =d?xՋ?3UD}}T!|:aM\Dp#_kr"l9~ŵOw8xc#I)Ck~ h% ]P \-,-a\Z!˭:,Cx SG dmH.J_NJ__Ei,j jAiXom2;ӌV.㎦& z0,h0># UvT2li"M/" D$}X9" "9ޚE H!-:w; FTBBdtMڀIy՟(e'n'w<`WNykYjWO(GQÇW+d 'y4<{iG棊 4>:Cd:wQ^n*АBr9װ2 KY= V|@*5nj^# 2@U"+ QCz[!k!v鮔Z[LYXkytK؇+.<Qh=L">iԉ^,r s%y%ppvy4@݌Ooz>tGdj8ש&#ϯRJL_RA'k՞*)+B<':Hp"h&y&|?&TLx_D_C# 8ZpP-222?SN*]Sq\FT,GOܨD=7.kmx Woޤ]&LDV&h\FפҥkM[<)}KR T۷WjϦkB5w"Ѧ>?'.700##@϶q`FЭYF`&t> FЭ#Y F` \%9F`ƍm t^{i-fF# g.=lnjo<S/ V BW"x0#/ɉ=oݒ_$_Їs(m7iΏ-{kfƈ@]7Np )M?*[ruSȯ ի 87 a_:;C ,]^&B/+w/[ƥO[vFr5#N{ں}fsEyO ̌#( \]-=$erPIּ)Bϣ<+6|YAs [#&'1=\MvƳl"fYLhМ+JXCX#4*n| &ËPRTnb1bH 1yKօhժO}7#-},ߺ[Ӄs6La+qdА/zT,ܞlȉd,3gEݕCH}̍{ogVc)|Ylc8 H;aǔ-F`j@]UEDh11gmCΪ2)]1^L&3WoD- r}7bW8ʈP͔(gbWә8z4GcJt$Pѯ`‚X#VʷF\v^'8/۴~_R!))I$#]8;{OŎ^X@#E-W ;% غ`ޏXD,`cF0)YE s{; ^∛%m.TQ\DӅz.)$FfJXB+}e} b;bK$ӺuˈBt&?=zM%,'}8a#0=}܍7%`dqĤ6Sd8X>IjKYkD4ZRsߐC:S߮")OW!6#Xu8۩4r-+O]<tǯk |3J|M] #|~ fԮF#U@IqWC14&>/bPѮ:tWR/n4x,41bjP~Cq 12 N2 ADYL"0ƠWVQaF^ #†PB4` Mk.E$2߿ 9Drņg|,I!='LdBS=!ޘ^pBD 7Jw,(ld+A&s} c2<H…FiEH>DsbڻB]"fd p@=(?o2#Ps,B袺Og#FUqPмX}qG$/رcOIl𵔅a9IWh׹;(kaB;z3pO֯)g[rӒBtF!ϓK1bRF1CTПFgYK#\3f g~$,ƾ+E#NSup#0 JZbJhzQڷC ]-Uj7ot[)wi !fGebDY'?v틥zYᑉ⥦46\ȧ ¼OGNߊ=1c?fvKvqנW劋;%ЖqğР/|xhF,6 oPZVj'YFT 'N>}"VYx ݺU޺M!rz٬Jj+7$׊ڕ/ʬi c +prow'&qi9Zv>V>TS&3Ҽ| NoUޭDFn80!P#щ@0Ht#=zM3]%Q# Pa /W?*;).{Ut+7PT@qta|~"r^t\ΉpigvyU0ID,q~iQ>Ɋo'dG6<,tXP34p] fVq3.UpYF`,@]hK\)ciEyek'QdרZb#4 u"tqQԻYF`EF`&tkEց`B F`&t+HV`&t>FЭ#Y F``FJ`Bd5FӃENbF`* P!։E32.ߟr-#4Mii=$$A͕2#TFNPPe58`Fo10 n%j0##0Vt$0:#X LVґ#0L| 0#`%0[IG#010 n%j0##0Vt$0@^+--EII QPPbtƦFWR@}gAgqwiй5c^%n"tAׯ_Ͷ=t]&yq+`p70&0ÛE)BzalU[G .0%^oCp5.;9p4SGNF(]9NEVh ˱%6ܹzF ޼5RblBD'e֨Ȕux Qߏա%ZmhшtZ+"큳YÁ`,@-0 LjūнelX3F /7 Ѳejn[R"#W7X4ĻZ!ыfbM< .*H,eT0b+NsZ~vQX6%?<DŽ^Re,@-\lC?6'c+1iBJ?p'nXfd!2syXff)y)xbʨ X̍ױ" 뷾҅^bduKV9f#|$\]_FWnX)3@ԉЋ`mٵ2Wb+FKq?u9?D0e6%i߃YDڞ嘺pgBѥM}_JLy˷օa9KjA2 SId9{gSވ WaԦ }{$su)4*L]z.F/y 3beR/]alT=ĒcA) p(q3#eb!"T5ҾHS򊵔Fԉ57E_C4EE(Հ9[ DLlݷaaKԊ 6>= vŽCG: ?AI*r_rދ~(gbWә8z4GcJt$YR@{G Ўe:Fk'G餽V`p<6 }0wcWKs6#yxc4]#vfD/U{+ J{Š9/…cQᰩKE󀥡)aKTX%RtmS#e ~vdtQZkFvs3Y<#`sԉ3:LA-kvxyyI˦dVŦcqvK!*غEv !~RR ㅣWА vTNIմ) -뎗X: WF)`煿#4#@졥@@+YI#"c8to~HCJ<0q6XC}~11"yިlD]&(5~Ha[pNeA֏nkg@x<#*ׂe+]&j¿JQ,9mivX47ĉ0TVR-Df-Ǯd=jNhEW+dd19C*o*Aj)$ ;L&OlgS {w1T9>nESb 䥄kҦA'`(s[&꧎锆ogH9`)Cs8w4D\|ekH!i!b}*8WJJJ%Kq9/i[oz0uCN;4e5rs{o_37mk^A+qH1ݑ)A ?KɟM rHAFD[(a@g.s CYv郰 1ALmYNㅳtp@0ۑE,b_S^D$}88<7\Lڑ# FAE:b>x(al˙h"[<gtWa(Fp-W#tDzSOK\^8pPCH zBY\`u:<9C?L_):8ŸF i~⏝cBмᑉUGUdQƳobCbԜ8GIXPr;ޡ % YdqM4dhf\(d)RO3n%fQUm14jHOK"vy9P#s()Qhy;L>ƅz!5 _oiNJR/Ch7;<Dб.PeffJӧcZ&?7rNp' jCQ.ȇxQ\vrkYJ1tKtv?D.w<{, TUri\I3<"Py3LһdH'gxt|`:|YYKV[}0W*ۣNdz=TN'3T(C% 1:W<'&&VDyA~=`)=//G1bvu-ڙ+CV&7PT@ո `|CjQfi@dc [_$<\i_I3LSKn,iО XNa ?,.s|4%:ʁ`,@fX9+Izzb7Aģ:"Bb_1'7'E/ivg+Wc2 "tddd/lAҥ4EXE'[DR>Fbꋈb>F46E͛73k~, -ȓ,R"mAE"wȜ|jWZ}F8·^ի hIʅ)@ߢy1`:vv#`ilMQWWW׋Ʉ^CԬ>(+Z-0gf#`S"E9#0քdkҏuaFf`BfEF`BfFf`BfEF`BfFf`BfEF`BfFf`BfEF`BfFf`BfEF`BfFf`BfEF`BfFf`BfEF`BfFf`BfEF`BfFf`BfEF`BfFf`BfEF`BfFf`BfEF`BfFf`BfEF`BfFf`BfEF`BfFf`BfEF`BfFf`BfEF`BfFfP[BR ??(..NF-CRUjO9:ѢW; :c<D".q$UlsAە5G@і 52oct]tiä#P!ta١Gg/i\+6pHcZ"bǃ`C"~<鄀Vg2Ja(ŁгY 0fe˖fhcd 2*rT2NҲƼyL6xʵ@"\Zm.JƘ!C :R,dd!+:4F17SaȣMVSEstj*|JpmV%m8 X3Cѕz|BxFyBerVc3hYeQl4  #aā`G".f$ﷱAodT(p#С?adͅ8x^Px4"&\jkR_5"jJ,Z֓V mW<þcZ*EVvc=\)+4U֪*먪~%O5fN4uQXb-Bu<߫%=)k0~t"6ǽPBn\yRpn߽۾TEK(wQOV9t`܀a߆!_ǡ0܎؍ om[F Zo BU)NwGk$kHy^\cwl3;G]gf=HN?bw_5fy-?05iJ-Ó|Ar>;S o{,fsuStH'ZMʉ[(2Ꮁ/EL{SKђHMWZ-銢;6dw"]꣢‡n#ltq%Á`Cb,ҵAR{N냈ܗ*{t/m? }1hr1Z'~^7gcH'\-eam>(~Fae{ yTE+wjߨx|[z& }M7a9/[f`p5–C'b}doqiL,Gk!Ri"+8=;k=|)hBWiHKN.d}$>N*%o3FN!qTef ]eJ)bvIh,BdSr a"dq]"U$Wο#`K W?H\4)L撻a t*V7!`<󸷼iw4Ѣ!!dIVR(OT,MRg 5u(.{'w0)inu" rφ˦Lg:ád9gklvxm`)I7U7㌅;Qo Jةڑ6xj՝V 䓗IKZ}!S+…l'斕=wM*Fɢ/K_Vl nrA2Jƨ\Yalgu}QM1x>\vH|ǣ^#y8N58+Z /d-gۍ^\dFwK=H O"BPsh"҉,\K?G^J,Ғ۸@b?WVylJYd˖Ljb )&=a{R~ASuBBX`~(,qΐOAVގ*uaЂdOWMKe۟(bLprY+SQ])r>qA?#P N܉HjK/ wkoo빙B3&͈Oy0-Wxš7':{ͫ 8H Ĭ{sSļ}Pߓ^ "&L @Qz qȠi"MK>bz5AqTep3T\~;I."n~Y!,/Õ:Kiwt+1 $;^!;!jnߓO~Bz?*+Y3ʙى XFeKE O ؗ hǽQ6l=&RDٿ* YEXWF:1p`jPeffS Oc=VmSΞ=AJj"Vi(DnVA^_TaP"/p^^4ѩRiCKtf"QeQn*ѽ‚RdB)K:RMċ?2\Ȏ$Y S!t@T+B94ϒVeˢ LTFW!0FJА]? ZJ*F΀tMG1"CUR/y|[S; b]EFk"\w-rDb>yitC Di1&Rj DŽU5|tØ54U#TE݉Suk1_CUhiZ< -*gKiB)U-º2#`BwqqAFFzxӬ"raJބʣ5aY2F:v쬟d@7oHJJ#<6mh1TE,dtTG>vEܗ-t[<X{G"(}ի8q;$u-@Zat/@alyJKǎmګRKL۬k<.0#PSkcFG.wJ>bz߼z0FCtvvFFDܹ;;;4mTZ7r<#`C)Mc"4vR˥Wgb0@cB@/c{M`ƌ1kj]'6V\<΁`Ɔ1w)|t0no,tcA0#а07,\;#0C bP F`&şkgFb0[ J0@"ްs#X &tAɂFhX`!n1(Y#0 zϵ3#`1-% bFa`BoXvF`,ŠdA#4,L ?0`B,`E aFL1#а07,\;#0C bP F`&şkgFb0[ J0@"ްs#X $٘RΝ;(((@qq1D܃T*ѴiS"6V V\-vvvt&4s 2 2ΖK.hѢE5%6͛̔ܲeKݵi] #P[7oE`͕x a7i={|\4X 8͢ wmr@mZ]q"\h~;dA_(OLߌ%']8mN: 7K۶m&G$pTpC LuJ)-,^p.:eScpv"{ C`<ʯ]wjR@ݻ"-D `>6GX9n^W\8-.}r),,DVj/0 {2{)6GmƂ1X1gZ ~ڋhh{ml", /7]ܾv-޵K1G@'-b*255:B\N Є -Aޮ_V['ߟעRpp"̘4˿,@l > ?I ץu)+TS]܁X6T27ǹ*ͪNk۫Ω@#=OkDբXt{rѬl˙uѢIEx`N`~g yXJQ ^oDz%C8^GL]^'a]4D!эtO; MFN4.Bפc]_z=;c^u]"8U^uKdFšlpzcKJ?Ə>y2rFCblį@WQ^?Ū\Souyx<)~$y³.i='3>oa3abhwĨRnU di}`ԢY@Ga&}KaP2 ևYbԪz;?O0ғcãJGn #А4"BџJX4ug L{.Rk)}0|D)P١Bk^%Ȗ.,u5}GWxjpPh I1;UVaOqbqx,oJ)bFz7R ٌ@"h==a]1ept :uu/y~ KpYyMS)Z{}Dsf2NBN~26C_ޛ=$6u[qA5]GwEc_Frq~hSMѣMQ:Ȉ]]ܭ-Z@>#T??ϱ*@WYZvpn#`IT~ROc=vO`oogYjj*~Ox̬GVU>&Y2r iz+.Ӹ)QAԸ&(/3:,8ȑ#CsL!w?Ԯ~JlUzn"h\.#0A 8*?,ޠmPƎo0ײkh]V WLYV-^\0m埴Znb͢ wmrD h7k pj/e5+&(*,^`z/.OxK-.􂰢"]J.Sm=9nиwŠDFF 5e"\|~-݂܊sۈWȥM@W`uexT?B=]~;6ߊ5_%BnF3 '_T<` ߰D"$WI I@]^H|HIԁZht7.x4cw\VtIm>ɠ:x|MIזx:& eu/:&˓1-RoAGGBmǖ"K/O79rBJΜiGTr}횬TJJmpsx[ sֆcXltLˉCXoKyzc =e7g Ͷ{V g@aF9<,+vǚ_ТJpgl:R8x)PYHZ;-gbXHL{:r>=JGjt0i|6nl)SL/w`v;RxZMOCq>#gw}!h1;A>%+kmpJ,[imGzr{ q-QfG@}5 'g/|Q>ZI8:O\2v 6RH!L&[p4lo̦YCqpĞZ"gi6"f>@Q}>29q>1hHʇ^ą݇5D\SSX}!  Z'JwVe9Ky9*ȏ%'dI"4sx-AYI:v1~i&HZ{w#̢Ypad&ʹR K0o{W H |uٚt6\@d ǛJ:># ɪnO-*$] .-bc4/Ԣ~ӭbi42. ,U(x(B5RA<j6_ۆ?gA^S5' w}GXAopy;RZV{M?UONkJn( ǩ\$?¢V ۇ?#I"6_)a k_3S Mb_$Sz"! w+'*P9 kSi؄&;wbup_<+<5q艝фk)XHk9.n_|HE"=xx9s }9; ^K!1p ".GD&L34cU!P||m67E%-4g3k&Zx96C>ْLϜ7P%5=~$fVcwqG:yܴgiqCFH|fξE;_` ,L9֙ЮmK)[e`W_ˢpNyн:s(7n WeS&cM UKnϩ%7m ͚So UUrnD=wn{n~vrcw%}ixh5@d6}I -N'phV)q}xګ77{ϯί`-dYdw/+w [[SRz^wyXmO:j47uc7a65]]]t?w"5Gb']'DW"DkMX|זzz3'#f1kQw,w힧pU3]MNF{qWƜUJV_>6ʇ߶ޫ(E3͖% =sCWQֿ_5g;?⡳aw'(bUԌCqi\r*">5 C b-6pFBJ6B,N3IL.VM m&4`}mLOg`cGر喙`JΘ`cGر喙`JΘ`cG`<2Ytr{L ;t' F9@iҏ]_ij3YHΣ7%mƒ*!Vix̸ F6f"ф'pK-q,Wt EP tykSء;άQڻf>HbA0iҏy~ }ܷ8-Sb'#wmAPCz~!⒓Q]f=֨h!DLmEl2Z >Nۣ6>i^;P #D}YJFw*FF7 p }LFCk=z˕DZ>9k-Jνhnߍ˧ :hD*5> DkL OґڛMZ[vU9N4uԁI"AP^SG+3vDX.t?īWhf J" W/P^ҳ* stYׅ/{:D(܂h c2b+~aL S¡[5J in(^.fB9!4J_< k#/.$f |*5 EI5b?/&)Jbt=fai xq-/Z4*B,MgQ^DYjn@vr$6`e+.~gN]quR's͙Џ AgJFZO}ZҌ\Ԅ*ITG\ at>Byy$na o3IL9Zذ*kSqpi:=L`22}2h0nDAB̍>D!HGu w =] !{ˎSQ@+ 4$سv_t,EJ9|]3񟠶0+b"Pd(ե@R¨) I \BStjâ2:/HC#5rD O6dG:4>]i3&Q o %5Er\MTnG S8ҕ$H u4:6$0B.ec(-`E$YIp|`cF`<8fa&BCW!xL 0`>R|<`L@!ء+< &H C)A> 0&B  1 0v 1iZzIze% ҽ۞ 84MgW{We\plg |,WCMSk鯡4V氦-75dCaU$aw&rء+dQ!j'uBGR] d[n I]m8R23͚:NrT2Ltk$TKXJZ ,-/8~zx+AXEryy=+Bj[gcVZfmI=$!GEEフtPj>ǫG vMVOb~ճHIӴ6hߞہI Ogb/L`R`p3 g>}Z'ZKֆiJXg/\B~$vIz_ mNL&[pTiv=MӾ9&ܟ؊cCK\*{wE4\;tYP8pW(m%qlbm-Y:)|Rk֜GU.K ,7̚5]:#tr(u!zLYfyEPd2ش'.ЫA.d']a&"O뉄p7tPN1 'ϫ0Ce o~ vsChr%W6MMhjj¹sށ Rjmn&Mǩf&ꯩXhQ8 CW f}fW?<+%1ㅤLwy6<HSp xz Q@D^ҶA5Me'O}m@{sC.CBLpN7.^q'Xiӧc%!$ױ*Ttllޡݒ?4m8QeG,Gp@0i;~}\5v#6K`{AN ]μҠuQ9h MӖOQZH rO`&bء+`Q! 2)OB.(U!xL 8݅ϘءORi1&0Cz63fL``>I ˧#}ٜϘ 0IJrQaYST!a0&]!cMQ`5E;PUUPE`$}ƪ:k zq(HN{KNFAvʚXgݘ~vh'3DxZ)_%Qk%,Q,o2!Au59s-Jνhnߍ˧ {+;)j:}Pr:Z֙9w'.g%]!qʚW5&뤕 W݇t\}}r=&0QءO!˚.`h1](;"VXcs8_I/fx +X :HmA]Iܸ[?Ŵ h)4EGh𛒦h-/JEȡy8dZPS*z֖wk4ƚC#KGm ŒŚ݁KM.΁2tJkvjc3GJ>+G`͇(;tXB6uGu Hג]P75@PdzgA܁O"~j\ (p*lo8VMyj):_: ?4E=USDh'i1BS!l[^ [(Co _aۢ. ғ/OBlĚ)MX=qb;tX5E͆`MQ\< $]!fcMQdž`MQ| rB `.L@M0@:`L@)ء+<& C!@> 0&Еb `L`ء P ~E!`MQ\;t5E'BSԈo5-{=VOp;t縍Q):Κ^yb$WLMG t%CWXSt|5E[k$gTX˨-L xV\< &0|ЇlL`M1N6c123 N;Xd;%H*iMgX+p)0)EB=Ӂ6:=8z(/B Q!Lat̀'3#Sxt0)CBL=ᚢ%(cX^k;n{K{g-'a}OzjmEmcpnB̝(MٖDxZ]lgSnz{3񨽩*^AU\tt\ùW ̆M(ExSm6)᳢CD25oETdjA\}X^8Z{Jj?A$w8g""O.ƟՍ' ݞxzsXaS(L``0[p[3e:LL=k-TPnURQ;dP^bP Rpڧ^D˗yhd[ d2tqI#b<Sǁ yZq+Pۃ-E.)"I&!4E?bz鯻$rxE@(n)HFԾV _R$.^30\@KbX/9YA/ˣ|Uuhj&Wɉ?Oc'x[%YL`2]' M~ :t|eBn`AឭvvkBENԜXr M+fл H,eGdEInZ!~I@_ڄ5Z207Y6UQcB/u"qtZ>ٝXk*V"З/r$'W l=V)a77q7=ѦǮDMB25.-&^p -~MsS b?vyl1d0Cf7g6%/9y$V\9,>kvFo̮ tT}J->HIDAT5\âΎ|qL48}mi2hjT؂毀9~N_f {bEH ϶Ǥ6}q<1ΦNw6H~5 bRZ'jU>o@JL$*ŝfPQ0Oeڽ ?hn DE v68&ngi 5{f;t5Ebpa `.L 0&݅CgL Cm& `3&)9 fM ]3IBB ɚ HS\(֬):!iJe*HV&0 ]!Ff<!}O(pb<4cJN(4Eh-7ƎYːIOG=w;hMы}H6f:#Ī5g1zsKL ]!r䚅~]^™ʕ%9:Zg.)jgVBHhh,$3d\ >v =MQfHXV*Gyy)|ԼyYS)Gfh\GBS\=J,VZ=S鍒hOj4EkhӇh)Z__٤)4 C8h! X7Ӆ/ {:HVac`@1E pEagMQAiB%A{; 1Ȯǥ`1HYF;g:hO^obP_yΚhBSt7LQwhbI4E- ZO M0DFȱD]]^ê)ZOS:R9Zj+8<[x_15-6:F}3k/ns#g.;c'kJ]vgKk[5E̚=uHh@Uc ^mt<]ֆMISt) Q<M-C[c3 ;ES;tڪ)2S#t YE$MѾE#K'jPSsUHfMQs}4E+hLMCES44EW' 8:v&S Jw M x̥o)!i+*2rtaR[7b.r&U{UKyvn6NIrQ٭Ri.@13oYFj=_2sYMQߟ0+b"Pd8ե@Rχ`{2y Bl˚2C USdU<Ʃ<;1tMo@D9)k!kƞioS(ܬ)C0 `㱦B `.LrqaЙ`r4x 0&ءxL 09vrpa]xs {b-w&0CW9͚np]!dD xy8&V'e -}hZGisf6 6m"Nrvg^Cvft:`.VVGLK _*X.^!+bQ!j9LhZQXJÓ{a)%C&8 }/= Lus&:ϢIkks*X!hΡ^*[rOI|+=6 O\1T+vyzRjnڐ@WAH[Z}7'5&v 19h.Y!:j>=^#=m,HMooAdb%Q(CoS?`px02']!&\8E6}:f{ ;XhQQңLTFsmQt' 6~E3= #>NTp~4Mgw2qsd ]!VOMQGkjhJmjW/$R]Ap? 2~ءC21SMS/L ;aʬ):vle&0UcS|L Lz'*ءOKy2&0 C&dL``>U,'O(Ĭ)C0 `㱦cCc>\v XSԺ}}.8.1۬):|3g C|jOc I {KJ6 ݱ\~iֺwȉ (\n 5y2$בdE̮{a1MkEyE(A_`_Fe"*Ǐ{bxf-SWCWAXSԑA,&gzk)^MA2~e0͹ 2ֳsPh5gѡh;tG2`#ȇr}5JwQ.|uǚXC]嶑Ys`;g#1tř`Mс2Mkר\݉\6X3 <v 3USBST%*d]4E<+&hin-EcK+ZO ʅ)^#D`0Xai83/&D$ (\bk:24E!( }R#ur݀=!m4RFeL@ %_rI\rXcjootҿj51f.&pNz?GSTlgG8߫)wv.4EuKwd/걐4:):ZtFRGm3o)' f47uq//1^y^H |ヒ˗Kebk\IT&]!gMQ\k>6yL 0"}rL 0q&}swL 0"}rL 0q&}swL 0"Oa˚ՙGz?$c>\v XSYST!*C8 `MAq1`MQ\*Fu!z g[vGLĉ>}CtDSC,p^ !BȒae8!/$dXFّG`jp[4F7,8'L@A8 c kRq^S<$g! e!<&b2[/aK\;tٌ5EyM-$(FrYHN#>&)Ė ZZQ1i:2&xf"ud5E!) aK-,5+; ω 4)@@F(5)DfZ+3W!3tYʪ):O-4EK{3IS5E#IStlBecŸi*<&0&ء 7jbMQGHar-PB,`M&Hbs{4ENlo}})4E1b+k\p DC{&NCW}U0ʴ;.gJ$]!VaMqUyL`,C Nɚ)eÇ0mpLkN{ Ld$`S;) 0&& vŒ|L LyЧ%,ءOKy0&0 S. XST!a0&]!cMQdž`MQ| r(k*Ra0+x):!XSt@\XST1׀#}PiMQjx{Vͪ!q-e`GnmƱVYǼOC. kyMf}Qߏێ㞰3 2ӠK9߮@do*hx (;te) ky3?@ ۇހRYx {jx⥚FZhP\CbLak:2Fr@n^jo?3JwZ{%g.e^S_Oo*F):QF)zM(6C75KuЫP/{,,!u,^$_g$~q f&'KT* i y:WLQR4n^8n+i~^S'L>, nRUE4Uހډ"^hŻ"w#sꝻ*ƚ2')ss'tsa~HyZ/AeŧVq42.cJ ] V1h cf9G) B9 vtNu{ Q _A{ط*_ kVrW|59z\;t،5E'VS{&d>㢐GxYm3Cj8`vlRMK5P.O`L`C01`L`t C]`L`C01`L`t C]`L`S.޶c{L ;3#XS1Vu̇K ]!kV{awD-ۃbu]C]NƚtysB$]!veM!B9ZܝB]5]u~6URly L rQYS@j.at{ T81F,ʚ ⼦W:/7ƚGppYf:ud5E#JM ):NL`rʚe&~gfO</$MQ$MWK_~S׎) L |m4nZ5_ y8@Q8MP(@SC︇L⏮]81EBɚc)I\n9" 0(`>J G kJ^|r CWmXStb5Er0C;5EG%*n777MU|L 0&jݍ}lIENDB`Images/000077500000000000000000000000001300200146000334225ustar00rootroot00000000000000connectome-workbench-1.2.3+git41-gc4c6c90/src/Resources/GuiResources/HelpFiles/Features_ToolboxImages.html000066400000000000000000000024611300200146000355200ustar00rootroot00000000000000connectome-workbench-1.2.3+git41-gc4c6c90/src/Resources/GuiResources/HelpFiles/Features_Toolbox/Images 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.

Images_Selection.png000066400000000000000000000572001300200146000373460ustar00rootroot00000000000000connectome-workbench-1.2.3+git41-gc4c6c90/src/Resources/GuiResources/HelpFiles/Features_Toolbox/ImagesPNG  IHDRt㝲 iCCPICC ProfileHTSiǿ-U tBFHB G`Du)  EdPQ }v={Ϲyss}{;.K HeHg C1t@@ ]bg \ƇA;&3ᅥ, F8NC$l0 kd fay!"N33?Ƿf{C<&@@lv"29<> I,y/NKK#˜/b%Jxn-e RYkߑ*$O(rC!%Oyqfg9I1Ly)pBJ C yBP(AAP1TUAF4t Cah z }Q0`=x l~p8N3\Wµ _`1 D ECiLPv(wT*6PZT ՋQX4MG>4ހ.AWm0zCb1&&b*0SK(aXl 6[݇mva#I39p,\www7'5x/|,WS.DJ QQOt"%#;E'x)-}|VDm;rn  u::t vI{t{u?Emk{od@1p105k53L1gx66J26i /,__\xȄljml2lJ377m7}DgI쒝Kz|76K5;d\<߼ۢ%re+c+~{Tllm6-6c:q5Cvvv%vW1nvqr8𗣉cc㋥KK-qrb9t;ӝrh\j]2F=㹫knfnBSn׻wy<=<<<#<\ّ%"psvuȜeWWXcfۚ^?Ee^nӺn6oި`hw^&⦔M@ `dBBaVǭ~@o嶽۾qW-a\'l+)ݿcpΆ2ٲܲ]Ew}ªqe^;~JvnQVqg-{Va<ύBzmTJfQؑGn8brZ| {K/w;rRd)ꩢ6mMD{R#NSz gK>{~K5~!H/ we{]{_qr쮵_vƩ߬;gvf-[Kv}ǝwwF Z>$ǹ~7L={yTXq'*4ȫ?2:Z/Όyz+?eym_nLDO~[NV''|H0ҧv{D}y>~MMO XB֬@! '$0o&Jـ<,9=sBH$+@8gd&XZ"#֤bz q|jV}@ׇ9_>XĿiйҞy?|OPiTXtXML:com.adobe.xmp 224 372 1 Ǚ@IDATx] @UU+H">0 _ڌb)MJh9?Sf M34S3rS C  : k}YgK NA#)..n QFA9 PFCqQ|?rzMdW."MfzoUpo GIA~oo&\fߊ?;Mě݈yM/аYO?x1e|P ޽{q-EO>۷1SOJJJhƍϘ@<-bvᙁO ;Mۮ ;8'p3xapuv03qqmkM8PԵm1սй xX-1+0`m5'p5OcŊԩr8 e9 L5w槒^}2\f˝@CЃ߰Kzh3 X%0_P8]֕x~{,goūBcw,Jyy<^#3[Fye(c>RMg@}իztAa]"Z|c.[D(s!WعuI!QHm+)1sj3AGuᙇ ڲuU0kPs-ԴZ iqx&tAPPk {P|b2p} ^k$};Ƕ`PH KKN %wˮcGixkF={ }D﫵 ܣ-[, H|~; O{?ۀO;0ƩkKZR5YYY͜_IæMp*8OJX6m`kkr99]]]{O9ɝ䣲̕!ay.SA6 9)_|hKƧoo>KI zm:: 8;º$ OLFFQس) $3Xe=|"^]csÚ}?GpYGvm#!cxv$8n!JJ=o~'nѐ7#Qh!A| ?^g+(_Knhx6KVNј7^盌eY{dSON%vT˭,2 P G=6~g'SB/PIٗƒ`Vh (-J k++=i$DzH oC2FWÇ+Su(WEʬXPL4ٷ1_RrX: V ЙqTu:x~>ՄSW߽||xɝL4~7,-~{/fkV.ӿD`&,Vw>^_6jrnX8p?rrr=x2O>0j'*dZ:_~ߢwfa9<֯_oª +ZU,ܢVw4mйT;7qhFHGh#{? ܺy.ڡZ 4l[̅hڣ[gͤ/MIpJuM_iJNN.;n߾<(/EѮ];L4 ޕ60A@PPry-TQg888TY5Y̕!a e ={?#.]:vskW p|Q6yW_I -$#l( 4m,6γݜsgXբ 4.([h0_iƃ۩~_NS .ol;]vUh\yqeh46E'~\wᕡVV̷~(۴iӚac6ʕ+Y8dfWY g oj!NNtO@ 4nwgVn_ϥmٱoF\.u4K<}w[;> `@Q1J>l/Lͯ[Իwo;t/άEQWA p7"PDgj*jXs>Ǡ d\m~S[Εwhb%8z -5ֳk,Wnޑk"g#k_GW ~q<;g JƺUB{v;&)[^ƺe='hE@zk^ZZ--t㞝^68-MXluZ{DD ȥ]8Tw3 OF"t~Y5fg{<8]8zxtW** /O H!aNohjC{+&ݍ95%.b^?VZ9 Cg։C_D۫1'}=Z 4"gxu LUjv&7}_kA^EBݡ,EڱmX>w=3bg(,O^{z i+pp/tOGyF J]m]N3d.H f8)[PPɤ%9 R*\MڍmTMଶVBgzKg(8jJYF_ e,\g&^HhOZѡƮjuq L\Q  )8}}~@||2&ΡՐ̸"mYm*KAZi;Gd7?.`#Z“Tێ:z'tZz3aKTho1.?TM)m'&.zUIڞִ ; gӶ%+ =mQl ՠ`I3<( ZFEP\jd[&&}uk5Nzy9"VLj|5u;#)ʤN"VcWo^QjAuL`o&'66R AI8>W,7)늂Cذ#CF|JvX`"($]oq)5}|@~A mͫpnk!gboQRw+yJ=7o$xũ g#"Sĝ1!0 7c$! 2ɦtrz%R 6m@=f.?ca ?!gteS *zA.le=],OBi*]E;pKe~cX^8H<1\wrA}4>ڕ~6 awߊP;uEW}¹g'a|5/mKbi87c+^u*MM ~ vgN?! +A5IE(a]L-«+V58\ kn:֗7sR;%ȳBY5HOMj/CcӾ"$ϗ£5 0i[ЕAͼtBuWoƴ[ɇLw1= ߄3F񏔜60^{ C;/Nh^<HV|BIv':b}pu|uRL9<~_c׳~8$P\VwL#2<{ްw1!8s][QUy?Nl0|FgB\p8+tFcyg^;inD7_B̉([OCEQN,@d~ oo{]X9 L*^vrqN?=́Nd6͹bFd~8vO,=ebwL dD?~\$4:r rT4 ]:!~X)x*u$DDf(ظU?h>Ed~ ǎGDl!2/gox{$܆i%koA9b!:&W}{sfL FDn:yb AX!T4sљ_q[A1ޏ)9a=p|G.[+҆KEVxяȜ68F&Hk%l"?agE8 ](NdޡG1@kḥ|)F{[<^`d 6n|]Ի5$RSO`n,y&IK)̉ z.Y_?ɽ7ę38y p?V 7FEs?z86eIbGHqOh|ؽ=l0u3iv}%Tte!N~H(n.S̍$;xÑx}?v/ޮD+%|qgЍ؅pEtV o?Dq/4 qODǶFW?o"!wz$LC41<҉DoS#.}I9d#w51t럍ʍD5쟓[C|?uUş?|ڗ8w ahe+ R|)iꔗʌD~Jh#/.aMoEm6[x}uz#ŸZh׉'A Gyeh^gZЦ{+ ©bHa3m* Owk"wV-ngbD -V6Pc&wp |G&#w?I% F:avcKiؓ J fÕ/ɿz<;f!c՟1SնXb6آ%qy8SW Ҿ)TvDȫK tax+$MbU'8N*)7 m䒝-RVb{,ipGc1cᛈx$2ܤJG_&sstu'z8~ܤxZ{lh :ExS\ue\<[6}kԇC5z?yaio.~1utǴ6x`X'$ % Ct8Bc>.C.Ȯ?~3?mIPd휇cN6#>vҢ/Tk.cf抎_:+@VXE!(Vv%D ""UpFi?BwtVJ?^(m]&=lsp+rEX>z_'?f"ory2=Fx%ͩEf_0F- ƾx 2!ԁ5L⏏ =LL {}=M1c*xpo#B? 4dZ ߇R=l:⛦A9n0) l1JU\GN4x`Mb'g# k0Sr:ՑJJá(i|Bw8mgi:ׯr `[d"MMBwM/t8w 4mÀY{>>aHEv+"ŶYH ?IbA]~,rr"1zyqSȹ٧O [DچGӽ{ǸMk3RdKQx|v8Ð Ru#dxFˎe .٤״Zs0(k[GZzk<=QiZ?~}bZcx?dn 6H-m@nSZk֘<hsJA4C?IE'E:qH0mqz.Anoh=\ W@"pT:<3LjYa~x V:=xb/_ys5w\u;tAG3u1h%JOUN<"M\ dV-9QmՊkkJ87,k|n>z)^k& ^ۮhCQ8}.if}B I>erBo荶RD> wR\C6cO l'&'w2] dL=ϼX7vHؚ/zj\ ) ih5`#-'qXH-a۰+|!7G̦WS>q}HЋ8zB|v uRDܯ} @&4nnG|wJoH )R*&[a ^UpELGlBaTĉh5sJBJrMAtN˜Ja4;Zii5`gzAZ|[Bj#|\g ǬŐ[dyJp\GNm/Mf-e$RʺkXt oN [$FGo$O˿v6$m'bg6M-܌ȶnv+'?BYd.#Xj|5d^mB?lE=d$W桷8O&kh>*+g--iCb:Uyi-}OQLk0/ɡ:/l[KsjX\Jafϧlg-ZpZjgȫ }Ps\}@~z-{uHLVٶ||ty:]w ū*,t%M 8iCiŰDΊZ!"co[m/z.l&j&&g gc"{[QZ&k+PWi+ϫҤ Y}@ީjŊN݄e,K8 ot)?4Cn|OJ1ޤ&@V E3کZg`&!}$hJ}Uْ &;*Zߺ>qy6K6Og֍&"zۨA-\3jÍY]5>b3#Ȥ.$mz.;pRPf6]Dž 4K۷GZZzw KnFNy zbUWq@Tʒn TTSJ5HhjSgyst|psstM p /6]TzTaaȆ3+,R&G[6m9rDIXL,(o_-q%lgS-"o^ ْSN Gn? 7;˥"  p#PfIA@hy> @3E@v4KZB-ϥł LDEE5ӦIA@hYhx#GVKkA@hɥv4IZ&B-ߥՂ Bo*Mzwi 4CЛaJAe" 2]Z-!fة$A@h~V @3D@v4IZ&B-ߥՂ Bo*Mzwi 4CЛaJAe"`vo[XX<ܾ}(.-Ur]ޅݰ+}bw+0gv̫[ "ʕ+ K1.Դ/жzJ ]55ȸf з <A謙[[[cРA ^Kˬ[礩L옫ΗtmkԶƱb"t @ P'686:|0/({g6HE[н{je1Y:C歬`FJD%dT+mc]<KN{5Fqe("fR/*tt0q=rTc63*ik+ i}LUU9+?{@!ELw6 ƲEϠ:X!n8qt4VZt.j%f,2)?c/xMa(l^jH;d,N1+w$ΑyɆ Qԣ|X9 OR^ (Y8A@h8jEרFxWctZ-t8~A \Glw7߬`jSэ۵s.ǔa\<x>?Lřp$n+з/~]1QhȵF"w}\ݾ-<; {'S<t!Wbף2n`пJ睅t.R&mQ`8"N ZzƷBZVqwv3NNNʱ%Iճu1rL|coa+z84:?WN'o!%{%l1&Y{6%`XvhGG֭cÉ6m:%s >J}}ĭCXiҦ>}!>6OR-q?'BƒЛQ?^=fz JX L+{E[)V2t]D"!E@c(t䫪 ||:"1f(5!&{)euĔQ k|ࠆڹc7p8%K u8JUU1ґ~2ҏo3Fňm&mWզ ~{P@̙ԓT.tPL;CsNj3^K5W!ލ!2'>bx -Vo LjE蚎 d}W5[88o-#Ys{*wSo_PJ۹9'n4@xѡFxZPq !ڣz´cNy `R]^eklMQw޺)ci&mJç){e#[)LC38s `X~AAĬ[Z+,,Q4t&"olJZtDtA̜dg`X3>0?,<-`?«}|  /0ٳtA4Ȩl:LF 8@uv\-3{h:ˮg#ps42_+ݺna4,|y;.# cg 1LGxZ6A_ ^ÞK0I{V2Fm(@,b-4d~>8iҕoĢGcjz:}014n351߰L6$hyx.ðm |1i5q/iOcJU `7@ƮX4t=Wh _2rZgAvdrabisq4e)Ylޮ4&itz%B%mi|ee&''c̘1]kdѴE'201W\jS[4ekOkPvB'60A؀/śh۩:-rQ`OV OgxV2 /)Fb Fg%@"PgÉ9"k'gKi`fXs T /$kTr5vЃ9",!{|W3Â*iÎmSbAgu%8q W1.Opliˢ'W@}#PgB YʯcYf#t]߲"O^S=˓%xX!W)\Jdb9DR_ .ZtMZf Ly/6H iٗD _&oȮ2qJmE8A@h8j5˥09*7L-'W?/,ʙ?kckN;SzMZ*4-۷o´ٖ$]B\LV57y"uES_8(_}q PB{ufp98i%TD؊ p(w'|A @!dluA{/.]Dl},%{³3*&"MCx{ 0mL|C] 4-{rBջL_(UD)NˇFQ5J,‚ PcZ0ʉA@h{Xn6  Bo], zKii 4{Л}KA RzZ)!f@A@h)v @G@w4PZ B- Bo], @>j駟ho,ܾYvڵ-㜔űx]."8ڠj"v_Q<|]kM[fl Ǘ).DbaZϺ&iA@;$:55UYp{СŅx\tpssPy8 ?x/߁Mѡ7nFbpKVȶLo0=ad|za\D8 |V {%I+@!P+ ƍ1b-ZC^m۶8vTKWk6]? EPl|ybzъ}um(GGP6d%@ 9׍W8dYNcjK4Θ.($hG@%! X1; .:C) bJ>위@F| j:e _`!ҿ=f ,\@͙afil2f(Ńδ䡋B{7[ WWO, ocL5R9w|;ͧcȤQa GDVpps0?3/u1k*Uظl&:h eI ů#½ʯKCxP&^3cPD.7ɻzn~Yx߅o`e:+z'\x&d N~u&npfT[X 5dPkoDxSX6uqxл|DPd45>XCŊOOAsAT51MnOo<ԕ;)6| _SpprFo7rT:MCή)TDippWK(Wq -޺TNѦ*d5Ä$M|6f'K 6O!_DҮ-Rre ѡ&]f#i|oI=&B! _ɞYci7+LX H̢3Q&tF?Sm<0-űldefҔL/r@L. XzWHJ:Ǐ#6j#@K?ZJ], `w$Uh' *ENMHFUuvfMrll7B|<7d؈ŠdX7Á"hOA`bVu4dv-7b :qZ[{8"!edKRfd%~~ؒx]? 0 aKHk6̥P;HܡsF,AQFEcI ~2nQ> #`;kVFzcu(G {|K=@5w^_S]:M, +qUO&͋<?B1c>V-4EsT,{'`5<%+^{.D+ @}HlW!A#PX~Gr?00|z\lEE;=C"sDl`ok~e#e;uyyjlag]2&zENvȴ%;2p}5.3tzcma%  Boy}.-fz3Xi <[^KA" L;V%-!bA@h7ӎf @C@X)BʹcY Boy}.-fz3Xi <[^KA" L;V%-!bA@h7ӎf @C@X)BʹcY ՎE[RbA@~!|j\+Bwwwȑ#U  trÃؕsssqQuU1T  DBo"%A*ЫBHA@h"7j  PBU!$ 4ЛHGI5A@ !xA@BM TzUI M!&QRMA@B@*$^&@rk3Z!' R[VJv=)C/oI(CIDh V 4ednh xaL{1O BlV3rNk%vc=OG `X1R' zFVy g.Xe3Ӆ3"uf,JB.A;1v ^;x gSPC^r4V'ľ_K@C7/ݼ!f蹷ɍ48Cc8wsggټ9h-EfzaL Ikbl'xaLzOolT@EQg}\u[tIDATu/[3$?XQ~d +08mEW@A0s 6 چȱ%'IT^x]v3AģzuzG_; g!Ƴ54PZÎ]](k3p8G'ry2u>=miԷI1FG$&4p穉y1#e5vb S0i.&戬@R2c.qS#-yUuV{y42GNm}eIY7۹zmg8oNdeg!33 YOcgx$F6ɨkTJ4p9,ɻ_ \A@h4 C" 3V?Z2"0,'I灏K/4sFLl nQp@s2*x߹;!D\(d %z-|CJ: 䵜-KaÑ0#ZweJDnj7ڬ$u '8e@sFצ?f$szNF@ cFBw3Wtk||&&MSiݥaּwpwz/KtڻƯD W&3aѫWYr.w צ~* ?z^ &1|i;4z6B>8@|,ݎu`h=ܚ=/9aQa^Vbx؀G`&v~VyOpҹI-uvs0c *'9FQ44d:͛.sh]K}҉SR|5uΦivpr0F}}=I.Rt`L*|"(Y>4>[{8ؓDEA쮯4v2< v}*E 9dr4vjiI<c~/*v{'T4̘&bIӃD߂޾s~<+W>Z"#2gM-\^jx kGVoQWs~ ma⾒k7>4ށ\u#pZN{R1Ƥ٠ynE.8ɤN[3Yr#0QK>K܆@2,H\ڣG.Tm"՘̦U4rO&EX<A@KhBi>_|wLW+0e| v .6lߐdx\“4de+'jPM_ud'm2'{Ć}\m`EpXR"6!| BEkv`ҧUDAnCQL.>WQ|JY24,^fwAAA=//=lllΝ;U+^E?ɞ õkw)IX8A@6˅5cъvFU2gXaL,òƒN2n02&HkiQSU"F ZKP&$Lhs- $LXp8o%o߈&e7I6(S\ PC^ֶYe9 -W"}ÆEXǛ8HaG1ٷ!˟dV\~IvXanuYF(V\Ǿ#źM,VEu K| Ѵ"Z ." F1wF{yuɶuN/NEo\ܸq\.۫l"~-w妹}ӯN5l@k6\n.BJ6߂-DDasG}i°sJ60S  PCECubJaݔ4K H~`<ȋOKqʮ9U&G3oki6 OUҠ@\օыOB{է[t=,h}J7(\  Pܹ&(VӖwu_MХ!"x-]M:0jWoו !3_l1=qF\ӊG#(γYj̥0f M)ji)BD!#y,BuoJ318Yv}UKNs__17Z73a;V3jZ>PAvX4*.;ڵk98Ti8my]he _;"4z Ġ@}2[7Ӱ^8ؙnx g&c}7"rv6㍙}i9FWWt!Sg\(A@@|ү_U?2bR珌9m^Hl͡9󠣵ȝh+Q]9y[ыhs9k${uYB: &@chAީ?O8,V =l\i 'g{ ʕIVtܦaŽ60W|qE60G"(6N:m۶ʇBWؾ6R=(EJ_F%tLB*rA~EinpA/juà^p-EJhTBW_x6 .t8v&fě@c׵]L @L[\7B߾}"9f>]m7ЦP{i$gg#q:x ?>'y  P'ЛyW/ʇD^A;EN9ys=X9]O RF9RF!]ǰs߿0Tk8ֈ)#4(رr|$d"F)PzGQla1Nz3G%Z]/ \9txS4bzbL ꥏ 08_A@_ECo\h)Jdp.ʹ‚\xQ\st2N2H KA@'ЛyI _vXYxrA "=.t "`L.AhB7IEuo.mcopgkqq:p<9S!2`ؐm&*Ӧ:|cg}cqPA@m5K_-馾ctŌ ew͇ꁏfj#y& T\>X6( \ @ .F M27m1n5@YDlpqnpQōt⵼Y5dDdc% `FB\"\70iXǾjpNU0_j@5lL r]eB!`T+4V;ABA@йE}t!y**rAhW]IAB薐pA@BMä XB@2.@C@uTWK[BFA@hb7  ` !tKH M !&aR]A@,! n  A! :L+%-!#ႀ 41ЛXIuA@%d$\&z0 B  Bob&AB薐pA@BMä XB@2.@C@uTWK[BFA@hb7  ` !tKH M !&aR]A@,! n  A! :L+%-!#ႀ 41ЛXIuA@%d$\&z0 B  Bob&AB薐pA@BMä XB@2.@CtIN$IENDB`Labels/000077500000000000000000000000001300200146000334175ustar00rootroot00000000000000connectome-workbench-1.2.3+git41-gc4c6c90/src/Resources/GuiResources/HelpFiles/Features_ToolboxLabels.html000066400000000000000000000022351300200146000355110ustar00rootroot00000000000000connectome-workbench-1.2.3+git41-gc4c6c90/src/Resources/GuiResources/HelpFiles/Features_Toolbox/Labels Labels Labels
The Labels Tab in the Features Toolbox contains options for display and selection of loaded labels (named colored regions) on brain surfaces and in volumes.  When labels are displayed, labelled regions appear painted on the brain surface/volume. 
  • Group: identifies the Label group for the Active Tab. Tabs assigned to the same Group will display the same labels with the same attributes.